Patchwork [v3] chmod: create a new file when flags are set on a hardlinked file

login
register
mail settings
Submitter Koen Van Hoof
Date June 7, 2017, 7:20 p.m.
Message ID <ed4818954e563f50068a.1496863258@devws223>
Download mbox | patch
Permalink /patch/21236/
State Accepted
Headers show

Comments

Koen Van Hoof - June 7, 2017, 7:20 p.m.
# HG changeset patch
# User Koen Van Hoof <koen.van_hoof@nokia.com>
# Date 1493215522 -7200
#      Wed Apr 26 16:05:22 2017 +0200
# Node ID ed4818954e563f50068a3f948b826395bc342bff
# Parent  3ef319e9505f376775c91b2ab7d89ac9ac4343e9
chmod: create a new file when flags are set on a hardlinked file

For performance reasons we have several repositories where the files in the working
directory of 1 repo are hardlinks to the files of the other repo
When an update in one repo results in a chmod of a such a file, the hardlink
has to be deleted and replaced by a regular file to make sure that the change
does not happen in the other repo
Augie Fackler - June 8, 2017, 3:21 p.m.
On Wed, Jun 07, 2017 at 09:20:58PM +0200, Koen Van Hoof wrote:
> # HG changeset patch
> # User Koen Van Hoof <koen.van_hoof@nokia.com>
> # Date 1493215522 -7200
> #      Wed Apr 26 16:05:22 2017 +0200
> # Node ID ed4818954e563f50068a3f948b826395bc342bff
> # Parent  3ef319e9505f376775c91b2ab7d89ac9ac4343e9
> chmod: create a new file when flags are set on a hardlinked file

queued, many thanks

(made a few minor style tweaks that check-code caught on the way in)

Patch

diff --git a/mercurial/posix.py b/mercurial/posix.py
--- a/mercurial/posix.py
+++ b/mercurial/posix.py
@@ -98,7 +98,8 @@  def isexec(f):
     return (os.lstat(f).st_mode & 0o100 != 0)
 
 def setflags(f, l, x):
-    s = os.lstat(f).st_mode
+    st = os.lstat(f)
+    s = st.st_mode
     if l:
         if not stat.S_ISLNK(s):
             # switch file to link
@@ -125,6 +126,13 @@  def setflags(f, l, x):
         s = 0o666 & ~umask # avoid restatting for chmod
 
     sx = s & 0o100
+    if st.st_nlink > 1 and bool(x) != bool(sx): # the file is a hardlink, break it
+        with open(f,"r") as fp:
+            data=fp.read();
+        unlink(f)
+        with open(f, "w") as fp:
+            fp.write(data)
+
     if x and not sx:
         # Turn on +x for every +r bit when making a file executable
         # and obey umask.
diff --git a/tests/test-hardlinks.t b/tests/test-hardlinks.t
--- a/tests/test-hardlinks.t
+++ b/tests/test-hardlinks.t
@@ -203,10 +203,18 @@  Committing a change to f1 in r1 must bre
   2 r2/.hg/store/fncache
 #endif
 
+Create a file which exec permissions we will change
+  $ cd r3
+  $ echo "echo hello world" > f3
+  $ hg add f3
+  $ hg ci -mf3
+  $ cd ..
+
   $ cd r3
   $ hg tip --template '{rev}:{node|short}\n'
-  11:a6451b6bc41f
+  12:d3b77733a28a
   $ echo bla > f1
+  $ chmod +x f3
   $ hg ci -m1
   $ cd ..
 
@@ -241,6 +249,7 @@  r4 has hardlinks in the working dir (not
   2 r4/.hg/store/data/d1/f2.d
   2 r4/.hg/store/data/d1/f2.i
   2 r4/.hg/store/data/f1.i
+  2 r4/.hg/store/data/f3.i
   2 r4/.hg/store/fncache
   2 r4/.hg/store/phaseroots
   2 r4/.hg/store/undo
@@ -256,17 +265,18 @@  r4 has hardlinks in the working dir (not
   2 r4/d1/data1
   2 r4/d1/f2
   2 r4/f1
+  2 r4/f3
 
+Update back to revision 12 in r4 should break hardlink of file f1 and f3:
 #if hardlink-whitelisted
   $ nlinksdir r4/.hg/undo.backup.dirstate r4/.hg/undo.dirstate
   4 r4/.hg/undo.backup.dirstate
   4 r4/.hg/undo.dirstate
 #endif
 
-Update back to revision 11 in r4 should break hardlink of file f1:
 
-  $ hg -R r4 up 11
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg -R r4 up 12
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
   $ nlinksdir r4
   2 r4/.hg/00changelog.i
@@ -287,6 +297,7 @@  Update back to revision 11 in r4 should 
   2 r4/.hg/store/data/d1/f2.d
   2 r4/.hg/store/data/d1/f2.i
   2 r4/.hg/store/data/f1.i
+  2 r4/.hg/store/data/f3.i
   2 r4/.hg/store/fncache
   2 r4/.hg/store/phaseroots
   2 r4/.hg/store/undo
@@ -302,6 +313,7 @@  Update back to revision 11 in r4 should 
   2 r4/d1/data1
   2 r4/d1/f2
   1 r4/f1
+  1 r4/f3
 
 #if hardlink-whitelisted
   $ nlinksdir r4/.hg/undo.backup.dirstate r4/.hg/undo.dirstate