[PATCH 2 of 3 STABLE] opener: break hardlinks for open files on Windows (issue2524)

Adrian Buehlmann adrian at cadifra.com
Wed Dec 1 06:53:53 CST 2010


# HG changeset patch
# User Adrian Buehlmann <adrian at cadifra.com>
# Date 1291199694 -3600
# Branch stable
# Node ID 52f97fe43a6be964691d78a0147f6650855fc303
# Parent  923338ecd57c11b2131ef9e90491fc8447d1ac60
opener: break hardlinks for open files on Windows (issue2524)

using new function unlinkopened

diff --git a/mercurial/util.py b/mercurial/util.py
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -880,34 +880,50 @@ class opener(object):
 
         nlink = -1
         st_mode = None
+        fd = None
         dirname, basename = os.path.split(f)
         # If basename is empty, then the path is malformed because it points
         # to a directory. Let the posixfile() call below raise IOError.
         if basename and mode not in ('r', 'rb'):
+
             if atomictemp:
                 if not os.path.isdir(dirname):
                     makedirs(dirname, self.createmode)
                 return atomictempfile(f, mode, self.createmode)
-            try:
-                if 'w' in mode:
+
+            # break up hardlinks
+            if 'w' in mode:
+                # 'w'rite modes truncate the file, so we can unlink it to
+                # break hardlinks (avoiding a gratuitous file copy)
+                try:
                     st_mode = os.lstat(f).st_mode & 0777
-                    os.unlink(f)
+                except OSError, err:
+                    if err.errno != errno.ENOENT:
+                        raise
                     nlink = 0
-                else:
+                if nlink != 0:
+                    unlinkopened(f)
+                    nlink = 0
+            else:
+                try:
                     # nlinks() may behave differently for files on Windows
                     # shares if the file is open.
                     fd = open(f)
+                except IOError, err:
+                    if err.errno != errno.ENOENT:
+                        raise
+                    nlink = 0
+                if nlink != 0:
                     nlink = nlinks(f)
                     fd.close()
-            except (OSError, IOError):
-                nlink = 0
-                if not os.path.isdir(dirname):
-                    makedirs(dirname, self.createmode)
-            if nlink > 0:
-                if self._trustnlink is None:
-                    self._trustnlink = nlink > 1 or checknlink(f)
-                if nlink > 1 or not self._trustnlink:
-                    rename(mktempcopy(f), f)
+                    if self._trustnlink is None:
+                        self._trustnlink = nlink > 1 or checknlink(f)
+                    if nlink > 1 or not self._trustnlink:
+                        rename(mktempcopy(f), f)
+
+            if st_mode is None and fd is None and not os.path.isdir(dirname):
+                makedirs(dirname, self.createmode)
+
         fp = posixfile(f, mode)
         if nlink == 0:
             if st_mode is None:


More information about the Mercurial-devel mailing list