[PATCH 5 of 5 V2] util: restore copyfile's hardlink support for some filesystems

Jun Wu quark at fb.com
Fri Mar 3 01:13:31 EST 2017


# HG changeset patch
# User Jun Wu <quark at fb.com>
# Date 1488521537 28800
#      Thu Mar 02 22:12:17 2017 -0800
# Node ID 76c7d930d73ee58b7f38c1a6ce74e264e0613b0f
# Parent  d79c818940ff7e29c76ff5e985b920885aa4e7c1
# Available At https://bitbucket.org/quark-zju/hg-draft
#              hg pull https://bitbucket.org/quark-zju/hg-draft -r 76c7d930d73e
util: restore copyfile's hardlink support for some filesystems

We know Windows CIFS server implementation has issues with hardlinks. But
local filesystems like ext4 should be safe. So re-enable hardlinks if we
detect such safe filesystems.

This patch removes the global variable "allowhardlinks" added by "util: add
allowhardlinks module variable". Third party extensions wanting to enable
hardlink support unconditionally can wrap the "_ishardlinksupportgenuine"
function instead.

A new hghave check "hardlink-genuine" was added and a
"test-hardlinks-genuine" was copied from "test-hardlinks" to test the real
hardlink support.

diff --git a/mercurial/util.py b/mercurial/util.py
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -1058,8 +1058,23 @@ def checksignature(func):
     return check
 
-# Hardlinks are problematic on CIFS, do not allow hardlinks
-# until we find a way to work around it cleanly (issue4546).
-# This is a variable so extensions can opt-in to using them.
-allowhardlinks = False
+# a whilelist of known filesystems that hardlink works reliably
+_genuinehardlinkfslist = set([
+    'btrfs',
+    'ext3',
+    'ext4',
+    'tmpfs',
+    'xfs',
+])
+
+def _ishardlinksupportgenuine(path):
+    '''
+    return True if path lives on a filesystem with good hardlink support
+    return False if unsure
+
+    some filesystems report they support hardlink, but using hardlinks on them
+    is problematic. see issue4546 for an example where Windows CIFS
+    implementation is broken.
+    '''
+    return getfstype(path) in _genuinehardlinkfslist
 
 def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False):
@@ -1079,5 +1094,5 @@ def copyfile(src, dest, hardlink=False, 
             oldstat = checkambig and filestat(dest)
         unlink(dest)
-    if allowhardlinks and hardlink:
+    if hardlink and _ishardlinksupportgenuine(dest):
         try:
             oslink(src, dest)
diff --git a/tests/hghave.py b/tests/hghave.py
--- a/tests/hghave.py
+++ b/tests/hghave.py
@@ -347,4 +347,9 @@ def has_hardlink():
         os.unlink(fn)
 
+ at check("hardlink-genuine", "hardlinks known to be safe to use")
+def has_hardlink_genuine():
+    from mercurial import util
+    return util._ishardlinksupportgenuine('.')
+
 @check("rmcwd", "can remove current working directory")
 def has_rmcwd():
diff --git a/tests/test-hardlinks.t b/tests/test-hardlinks-genuine.t
copy from tests/test-hardlinks.t
copy to tests/test-hardlinks-genuine.t
--- a/tests/test-hardlinks.t
+++ b/tests/test-hardlinks-genuine.t
@@ -1,3 +1,9 @@
 #require hardlink
+#require hardlink-genuine
+
+This test is similar to test-hardlinks.t, but will only run on some filesystems
+that we are sure to have known good hardlink supports (see issue4546 for an
+example where the filesystem claims hardlink support but is actually
+problematic).
 
   $ cat > nlinks.py <<EOF
@@ -167,5 +173,5 @@ Push to repo r1 should break up most har
   1 r2/.hg/store/data/d1/f2.i
   2 r2/.hg/store/data/f1.i
-  1 r2/.hg/store/fncache
+  2 r2/.hg/store/fncache
 
   $ hg -R r2 verify
@@ -192,5 +198,5 @@ Committing a change to f1 in r1 must bre
   1 r2/.hg/store/data/d1/f2.i
   1 r2/.hg/store/data/f1.i
-  1 r2/.hg/store/fncache
+  2 r2/.hg/store/fncache
 
 
@@ -234,9 +240,9 @@ r4 has hardlinks in the working dir (not
   2 r4/.hg/store/undo.backupfiles
   2 r4/.hg/store/undo.phaseroots
-  2 r4/.hg/undo.backup.dirstate
+  4 r4/.hg/undo.backup.dirstate
   2 r4/.hg/undo.bookmarks
   2 r4/.hg/undo.branch
   2 r4/.hg/undo.desc
-  2 r4/.hg/undo.dirstate
+  4 r4/.hg/undo.dirstate
   2 r4/d1/data1
   2 r4/d1/f2
@@ -273,9 +279,9 @@ Update back to revision 11 in r4 should 
   2 r4/.hg/store/undo.backupfiles
   2 r4/.hg/store/undo.phaseroots
-  2 r4/.hg/undo.backup.dirstate
+  4 r4/.hg/undo.backup.dirstate
   2 r4/.hg/undo.bookmarks
   2 r4/.hg/undo.branch
   2 r4/.hg/undo.desc
-  2 r4/.hg/undo.dirstate
+  4 r4/.hg/undo.dirstate
   2 r4/d1/data1
   2 r4/d1/f2
diff --git a/tests/test-hardlinks.t b/tests/test-hardlinks.t
--- a/tests/test-hardlinks.t
+++ b/tests/test-hardlinks.t
@@ -167,5 +167,5 @@ Push to repo r1 should break up most har
   1 r2/.hg/store/data/d1/f2.i
   2 r2/.hg/store/data/f1.i
-  1 r2/.hg/store/fncache
+  [12] r2/\.hg/store/fncache (re)
 
   $ hg -R r2 verify
@@ -192,5 +192,5 @@ Committing a change to f1 in r1 must bre
   1 r2/.hg/store/data/d1/f2.i
   1 r2/.hg/store/data/f1.i
-  1 r2/.hg/store/fncache
+  [12] r2/\.hg/store/fncache (re)
 
 
@@ -234,9 +234,9 @@ r4 has hardlinks in the working dir (not
   2 r4/.hg/store/undo.backupfiles
   2 r4/.hg/store/undo.phaseroots
-  2 r4/.hg/undo.backup.dirstate
+  [24] r4/\.hg/undo\.backup\.dirstate (re)
   2 r4/.hg/undo.bookmarks
   2 r4/.hg/undo.branch
   2 r4/.hg/undo.desc
-  2 r4/.hg/undo.dirstate
+  [24] r4/\.hg/undo\.dirstate (re)
   2 r4/d1/data1
   2 r4/d1/f2
@@ -273,9 +273,9 @@ Update back to revision 11 in r4 should 
   2 r4/.hg/store/undo.backupfiles
   2 r4/.hg/store/undo.phaseroots
-  2 r4/.hg/undo.backup.dirstate
+  [24] r4/\.hg/undo\.backup\.dirstate (re)
   2 r4/.hg/undo.bookmarks
   2 r4/.hg/undo.branch
   2 r4/.hg/undo.desc
-  2 r4/.hg/undo.dirstate
+  [24] r4/\.hg/undo\.dirstate (re)
   2 r4/d1/data1
   2 r4/d1/f2


More information about the Mercurial-devel mailing list