localrepo: handle rename with hardlinks properly
authorJun Wu <quark@fb.com>
Thu, 02 Mar 2017 21:49:30 -0800
changeset 31209 dd2364f5180a
parent 31208 fc57a8b95f1b
child 31210 e1d035905b2e
localrepo: handle rename with hardlinks properly In "aftertrans", we rename "journal.*" to "undo.*". We expect "journal.*" files to disappear after renaming. However, if "journal.foo" and "undo.foo" refer to a same file (hardlink), rename may be a no-op, leaving both files on disk, according to Linux manpage [1]: If oldpath and newpath are existing hard links referring to the same file, then rename() does nothing, and returns a suc‐ cess status. The POSIX specification [2] is not very clear about what to do. To be safe, remove "undo.*" before the rename so "journal.*" cannot be left on disk. [1]: http://man7.org/linux/man-pages/man2/rename.2.html [2]: http://pubs.opengroup.org/onlinepubs/9699919799/
mercurial/localrepo.py
--- a/mercurial/localrepo.py	Wed Mar 01 18:21:06 2017 -0800
+++ b/mercurial/localrepo.py	Thu Mar 02 21:49:30 2017 -0800
@@ -2012,6 +2012,14 @@
     def a():
         for vfs, src, dest in renamefiles:
             try:
+                # if src and dest refer to a same file, vfs.rename is a no-op,
+                # leaving both src and dest on disk. delete dest to make sure
+                # the rename couldn't be such a no-op.
+                vfs.unlink(dest)
+            except OSError as ex:
+                if ex.errno != errno.ENOENT:
+                    raise
+            try:
                 vfs.rename(src, dest)
             except OSError: # journal file does not yet exist
                 pass