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
--- 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 ..