[PATCH 5 of 8] merge: don't overwrite untracked file at directory rename target

Martin von Zweigbergk martinvonz at google.com
Mon Dec 22 18:33:39 CST 2014


# HG changeset patch
# User Martin von Zweigbergk <martinvonz at google.com>
# Date 1418455116 28800
#      Fri Dec 12 23:18:36 2014 -0800
# Node ID d32c1035354d1317dced8a8263673ad590aaa6aa
# Parent  c51c4c470a9e7e9817d336d896f972fbac041558
merge: don't overwrite untracked file at directory rename target

When a directory was renamed and a new untracked file was added in the
new directory and the remote directory added a file by the same name
in the old directory, the local untracked file gets overwritten, as
demonstrated by the broken test case in test-rename-dir-merge.

Fix by checking for unknown files for 'dg' actions too. Since
_checkunknownfile() currently expects the same filename in both
contexts, we need to add a new parameter for the remote filename to
it.

diff --git a/hgext/largefiles/overrides.py b/hgext/largefiles/overrides.py
--- a/hgext/largefiles/overrides.py
+++ b/hgext/largefiles/overrides.py
@@ -405,10 +405,10 @@
 # The overridden function filters the unknown files by removing any
 # largefiles. This makes the merge proceed and we can then handle this
 # case further in the overridden calculateupdates function below.
-def overridecheckunknownfile(origfn, repo, wctx, mctx, f):
+def overridecheckunknownfile(origfn, repo, wctx, mctx, f, f2=None):
     if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
         return False
-    return origfn(repo, wctx, mctx, f)
+    return origfn(repo, wctx, mctx, f, f2)
 
 # The manifest merge handles conflicts on the manifest level. We want
 # to handle changes in largefile-ness of files at this level too.
diff --git a/mercurial/merge.py b/mercurial/merge.py
--- a/mercurial/merge.py
+++ b/mercurial/merge.py
@@ -297,11 +297,13 @@
             self.mark(dfile, 'r')
         return r
 
-def _checkunknownfile(repo, wctx, mctx, f):
+def _checkunknownfile(repo, wctx, mctx, f, f2=None):
+    if f2 is None:
+        f2 = f
     return (os.path.isfile(repo.wjoin(f))
         and repo.wopener.audit.check(f)
         and repo.dirstate.normalize(f) not in repo.dirstate
-        and mctx[f].cmp(wctx[f]))
+        and mctx[f2].cmp(wctx[f]))
 
 def _forgetremoved(wctx, mctx, branchmerge):
     """
@@ -517,6 +519,9 @@
             if m in ('c', 'dc'):
                 if _checkunknownfile(repo, wctx, p2, f):
                     aborts.append(f)
+            elif m == 'dg':
+                if _checkunknownfile(repo, wctx, p2, f, args[0]):
+                    aborts.append(f)
 
     for f in sorted(aborts):
         repo.ui.warn(_("%s: untracked file differs\n") % f)
diff --git a/tests/test-rename-dir-merge.t b/tests/test-rename-dir-merge.t
--- a/tests/test-rename-dir-merge.t
+++ b/tests/test-rename-dir-merge.t
@@ -108,21 +108,23 @@
 Local directory rename with conflicting file added in remote source directory
 and untracked in local target directory.
 
-BROKEN: the uncommitted file is overwritten; we should abort
-
   $ hg co -qC 1
   $ echo target > b/c
   $ hg merge 2
+  b/c: untracked file differs
+  abort: untracked files in working directory differ from files in requested revision
+  [255]
+  $ cat b/c
+  target
+but it should succeed if the content matches
+  $ hg cat -r 2 a/c > b/c
+  $ hg merge 2
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
-  $ hg st -A
+  $ hg st -C
   A b/c
     a/c
   ? a/d
-  C b/a
-  C b/b
-  $ cat b/c
-  baz
 
 Local directory rename with conflicting file added in remote source directory
 and committed in local target directory.


More information about the Mercurial-devel mailing list