Patchwork [4,of,5] vfs: make rename avoid ambiguity of file stat if needed

login
register
mail settings
Submitter Katsunori FUJIWARA
Date May 18, 2016, 3:26 p.m.
Message ID <a74b90602bdeb11bab9c.1463585190@feefifofum>
Download mbox | patch
Permalink /patch/15170/
State Accepted
Headers show

Comments

Katsunori FUJIWARA - May 18, 2016, 3:26 p.m.
# HG changeset patch
# User FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
# Date 1463584838 -32400
#      Thu May 19 00:20:38 2016 +0900
# Node ID a74b90602bdeb11bab9cef36283f5119c96c6656
# Parent  8b45962710be55e598df3282752396fd1275fc84
vfs: make rename avoid ambiguity of file stat if needed

In some cases below, renaming from backup is used to restore original
contents of a file. If renaming 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 (only from '.hg/journal.dirstate')
  - rollback of previous transaction (from '.hg/undo.*')
  - failure in dirstateguard scope (from '.hg/dirstate.SUFFIX')

To avoid such problem, this patch makes vfs.rename() avoid ambiguity
of file stat, if needed.

Ambiguity check is executed, only if:

  - checkambig=True is specified (not all renaming needs ambiguity check), and
  - destination file exists before renaming

This patch is a part of preparation for "Exact Cache Validation Plan":

    https://www.mercurial-scm.org/wiki/ExactCacheValidationPlan

Patch

diff --git a/mercurial/scmutil.py b/mercurial/scmutil.py
--- a/mercurial/scmutil.py
+++ b/mercurial/scmutil.py
@@ -377,8 +377,18 @@  class abstractvfs(object):
     def readlock(self, path):
         return util.readlock(self.join(path))
 
-    def rename(self, src, dst):
-        return util.rename(self.join(src), self.join(dst))
+    def rename(self, src, dst, checkambig=False):
+        dstpath = self.join(dst)
+        oldstat = checkambig and util.filestat(dstpath)
+        if oldstat and oldstat.stat:
+            ret = util.rename(self.join(src), dstpath)
+            newstat = util.filestat(dstpath)
+            if newstat.isambig(oldstat):
+                # stat of renamed file is ambiguous to original one
+                advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
+                os.utime(dstpath, (advanced, advanced))
+            return ret
+        return util.rename(self.join(src), dstpath)
 
     def readlink(self, path):
         return os.readlink(self.join(path))