changeset 16794:98687cdddcb1

merge: warn about file deleted in one branch and renamed in other (issue3074) For divergent renames the following message is printed during merge: note: possible conflict - file was renamed multiple times to: newfile file2 When a file is renamed in one branch and deleted in the other, the file still exists after a merge. With this change a similar message is printed for mv+rm: note: possible conflict - file was deleted and renamed to: newfile
author Thomas Arendsen Hein <thomas@intevation.de>
date Wed, 23 May 2012 20:50:16 +0200
parents 9cbc44a6600e
children e9ae770eff1c
files mercurial/copies.py mercurial/merge.py tests/test-rename-merge1.t
diffstat 3 files changed, 46 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/copies.py	Wed May 23 17:33:19 2012 +0200
+++ b/mercurial/copies.py	Wed May 23 20:50:16 2012 +0200
@@ -177,19 +177,22 @@
 
     "diverge" is a mapping of source name -> list of destination names
     for divergent renames.
+
+    "renamedelete" is a mapping of source name -> list of destination
+    names for files deleted in c1 that were renamed in c2 or vice-versa.
     """
     # avoid silly behavior for update from empty dir
     if not c1 or not c2 or c1 == c2:
-        return {}, {}
+        return {}, {}, {}
 
     # avoid silly behavior for parent -> working dir
     if c2.node() is None and c1.node() == repo.dirstate.p1():
-        return repo.dirstate.copies(), {}
+        return repo.dirstate.copies(), {}, {}
 
     limit = _findlimit(repo, c1.rev(), c2.rev())
     if limit is None:
         # no common ancestor, no copies
-        return {}, {}
+        return {}, {}, {}
     m1 = c1.manifest()
     m2 = c2.manifest()
     ma = ca.manifest()
@@ -283,10 +286,15 @@
     for f in u2:
         checkcopies(f, m2, m1)
 
+    renamedelete = {}
     diverge2 = set()
     for of, fl in diverge.items():
         if len(fl) == 1 or of in c1 or of in c2:
             del diverge[of] # not actually divergent, or not a rename
+            if of not in c1 and of not in c2:
+                # renamed on one side, deleted on the other side, but filter
+                # out files that have been renamed and then deleted
+                renamedelete[of] = [f for f in fl if f in c1 or f in c2]
         else:
             diverge2.update(fl) # reverse map for below
 
@@ -302,7 +310,7 @@
     del diverge2
 
     if not fullcopy:
-        return copy, diverge
+        return copy, diverge, renamedelete
 
     repo.ui.debug("  checking for directory renames\n")
 
@@ -337,7 +345,7 @@
     del d1, d2, invalid
 
     if not dirmove:
-        return copy, diverge
+        return copy, diverge, renamedelete
 
     for d in dirmove:
         repo.ui.debug("  dir %s -> %s\n" % (d, dirmove[d]))
@@ -354,4 +362,4 @@
                         repo.ui.debug("  file %s -> %s\n" % (f, copy[f]))
                     break
 
-    return copy, diverge
+    return copy, diverge, renamedelete
--- a/mercurial/merge.py	Wed May 23 17:33:19 2012 +0200
+++ b/mercurial/merge.py	Wed May 23 20:50:16 2012 +0200
@@ -198,9 +198,11 @@
     elif pa == p2: # backwards
         pa = p1.p1()
     elif pa and repo.ui.configbool("merge", "followcopies", True):
-        copy, diverge = copies.mergecopies(repo, p1, p2, pa)
+        copy, diverge, renamedelete = copies.mergecopies(repo, p1, p2, pa)
         for of, fl in diverge.iteritems():
             act("divergent renames", "dr", of, fl)
+        for of, fl in renamedelete.iteritems():
+            act("rename and delete", "rd", of, fl)
 
     repo.ui.note(_("resolving manifests\n"))
     repo.ui.debug(" overwrite: %s, partial: %s\n"
@@ -409,6 +411,12 @@
                            "multiple times to:\n") % f)
             for nf in fl:
                 repo.ui.warn(" %s\n" % nf)
+        elif m == "rd": # rename and delete
+            fl = a[2]
+            repo.ui.warn(_("note: possible conflict - %s was deleted "
+                           "and renamed to:\n") % f)
+            for nf in fl:
+                repo.ui.warn(" %s\n" % nf)
         elif m == "e": # exec
             flags = a[2]
             repo.wopener.audit(f)
--- a/tests/test-rename-merge1.t	Wed May 23 17:33:19 2012 +0200
+++ b/tests/test-rename-merge1.t	Wed May 23 20:50:16 2012 +0200
@@ -156,3 +156,26 @@
   c2
 
   $ cd ..
+
+Check for issue3074
+
+  $ hg init repo3074
+  $ cd repo3074
+  $ echo foo > file
+  $ hg add file
+  $ hg commit -m "added file"
+  $ hg mv file newfile
+  $ hg commit -m "renamed file"
+  $ hg update 0
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg rm file
+  $ hg commit -m "deleted file"
+  created new head
+  $ hg merge
+  note: possible conflict - file was deleted and renamed to:
+   newfile
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ hg status
+  M newfile
+  $ cd ..