util: make copyfile avoid ambiguity of file stat if needed
authorFUJIWARA Katsunori <foozy@lares.dti.ne.jp>
Thu, 19 May 2016 00:20:38 +0900
changeset 29204 ce2d81aafbae
parent 29203 731ced087a4b
child 29205 a0939666b836
util: make copyfile avoid ambiguity of file stat if needed In some cases below, copying from backup is used to restore original contents of a file. If copying keeps ctime, mtime and size of a file, restoring is overlooked, and old contents cached before restoring isn't invalidated as expected. - failure of transaction before closing (from '.hg/journal.backup.*') - rollback of previous transaction (from '.hg/undo.backup.*') To avoid such problem, this patch makes copyfile() avoid ambiguity of file stat, if needed. Ambiguity check is executed, only if: - checkambig=True is specified (not all copying needs ambiguity check), and - destination file exists before copying This patch also adds 'not (copystat and checkambig)' assertion, because combination of copystat and checkambig is meaningless. This patch is a part of preparation for "Exact Cache Validation Plan": https://www.mercurial-scm.org/wiki/ExactCacheValidationPlan
mercurial/util.py
--- a/mercurial/util.py	Thu May 19 00:20:38 2016 +0900
+++ b/mercurial/util.py	Thu May 19 00:20:38 2016 +0900
@@ -1010,10 +1010,14 @@
 
     return check
 
-def copyfile(src, dest, hardlink=False, copystat=False):
+def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False):
     '''copy a file, preserving mode and optionally other stat info like
     atime/mtime'''
+    assert not (copystat and checkambig)
+    oldstat = None
     if os.path.lexists(dest):
+        if checkambig:
+            oldstat = checkambig and filestat(dest)
         unlink(dest)
     # hardlinks are problematic on CIFS, quietly ignore this flag
     # until we find a way to work around it cleanly (issue4546)
@@ -1035,6 +1039,12 @@
                 shutil.copystat(src, dest)
             else:
                 shutil.copymode(src, dest)
+                if oldstat and oldstat.stat:
+                    newstat = filestat(dest)
+                    if newstat.isambig(oldstat):
+                        # stat of copied file is ambiguous to original one
+                        advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
+                        os.utime(dest, (advanced, advanced))
         except shutil.Error as inst:
             raise Abort(str(inst))