Patchwork [2,of,2] util: use tuple accessor to get accurate st_mtime value (issue4836)

login
register
mail settings
Submitter Yuya Nishihara
Date Oct. 6, 2015, 2:44 p.m.
Message ID <026a9c645639a1b642e4.1444142667@mimosa>
Download mbox | patch
Permalink /patch/10831/
State Accepted
Headers show

Comments

Yuya Nishihara - Oct. 6, 2015, 2:44 p.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1443965736 -32400
#      Sun Oct 04 22:35:36 2015 +0900
# Node ID 026a9c645639a1b642e42b0dafeda6461f6ebc73
# Parent  869c9d9c777435f94a84d7284d6ba5c6a39e5ded
util: use tuple accessor to get accurate st_mtime value (issue4836)

Because st.st_mtime is computed as 'sec + 1e-9 * nsec' and double is too narrow
to represent nanoseconds, int(st.st_mtime) can be 'sec + 1'. Therefore, that
value could be different from the one got by osutils.listdir().

This patch fixes the problem by accessing to raw st_mtime by tuple index.

It catches TypeError to fall back to st.st_mtime because our osutil.stat does
not support tuple index. In dirstate.normal(), 'st' is always a Python stat,
but in dirstate.status(), it can be either a Python stat or an osutil.stat.

Thanks to vgatien-baron@janestreet.com for finding the root cause of this
subtle problem.
Pierre-Yves David - Oct. 6, 2015, 9:56 p.m.
On 10/06/2015 07:44 AM, Yuya Nishihara wrote:
> # HG changeset patch
> # User Yuya Nishihara <yuya@tcha.org>
> # Date 1443965736 -32400
> #      Sun Oct 04 22:35:36 2015 +0900
> # Node ID 026a9c645639a1b642e42b0dafeda6461f6ebc73
> # Parent  869c9d9c777435f94a84d7284d6ba5c6a39e5ded
> util: use tuple accessor to get accurate st_mtime value (issue4836)

Pretty nice, pushed to the clowncopter.

Patch

diff --git a/mercurial/util.py b/mercurial/util.py
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -19,6 +19,7 @@  import error, osutil, encoding, parsers
 import errno, shutil, sys, tempfile, traceback
 import re as remod
 import os, time, datetime, calendar, textwrap, signal, collections
+import stat
 import imp, socket, urllib
 import gc
 import bz2
@@ -953,7 +954,18 @@  def fstat(fp):
         return os.stat(fp.name)
 
 def statmtimesec(st):
-    return int(st.st_mtime)
+    """Get mtime as integer of seconds
+
+    'int(st.st_mtime)' cannot be used because st.st_mtime is computed as
+    'sec + 1e-9 * nsec' and double-precision floating-point type is too narrow
+    to represent nanoseconds. If 'nsec' is close to 1 sec, 'int(st.st_mtime)'
+    can be 'sec + 1'. (issue4836)
+    """
+    try:
+        return st[stat.ST_MTIME]
+    except TypeError:
+        # osutil.stat doesn't allow index access and its st_mtime is int
+        return st.st_mtime
 
 # File system features