graft: support grafting changes to new file in renamed directory (
issue5436)
--- a/mercurial/copies.py Mon Nov 28 05:45:22 2016 +0000
+++ b/mercurial/copies.py Mon Dec 05 17:40:01 2016 +0100
@@ -310,8 +310,8 @@
Find moves and copies between context c1 and c2 that are relevant
for merging. 'base' will be used as the merge base.
- Returns four dicts: "copy", "movewithdir", "diverge", and
- "renamedelete".
+ Returns five dicts: "copy", "movewithdir", "diverge", "renamedelete" and
+ "dirmove".
"copy" is a mapping from destination name -> source name,
where source is in c1 and destination is in c2 or vice-versa.
@@ -326,20 +326,24 @@
"renamedelete" is a mapping of source name -> list of destination
names for files deleted in c1 that were renamed in c2 or vice-versa.
+
+ "dirmove" is a mapping of detected source dir -> destination dir renames.
+ This is needed for handling changes to new files previously grafted into
+ renamed directories.
"""
# 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(), {}, {}, {}, {}
# Copy trace disabling is explicitly below the node == p1 logic above
# because the logic above is required for a simple copy to be kept across a
# rebase.
if repo.ui.configbool('experimental', 'disablecopytrace'):
- return {}, {}, {}, {}
+ return {}, {}, {}, {}, {}
# In certain scenarios (e.g. graft, update or rebase), base can be
# overridden We still need to know a real common ancestor in this case We
@@ -365,7 +369,7 @@
limit = _findlimit(repo, c1.rev(), c2.rev())
if limit is None:
# no common ancestor, no copies
- return {}, {}, {}, {}
+ return {}, {}, {}, {}, {}
repo.ui.debug(" searching for copies back to rev %d\n" % limit)
m1 = c1.manifest()
@@ -503,7 +507,7 @@
del divergeset
if not fullcopy:
- return copy, {}, diverge, renamedelete
+ return copy, {}, diverge, renamedelete, {}
repo.ui.debug(" checking for directory renames\n")
@@ -541,7 +545,7 @@
del d1, d2, invalid
if not dirmove:
- return copy, {}, diverge, renamedelete
+ return copy, {}, diverge, renamedelete, {}
for d in dirmove:
repo.ui.debug(" discovered dir src: '%s' -> dst: '%s'\n" %
@@ -561,7 +565,7 @@
"dst: '%s'\n") % (f, df))
break
- return copy, movewithdir, diverge, renamedelete
+ return copy, movewithdir, diverge, renamedelete, dirmove
def _related(f1, f2, limit):
"""return True if f1 and f2 filectx have a common ancestor
--- a/mercurial/merge.py Mon Nov 28 05:45:22 2016 +0000
+++ b/mercurial/merge.py Mon Dec 05 17:40:01 2016 +0100
@@ -794,7 +794,7 @@
if matcher is not None and matcher.always():
matcher = None
- copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
+ copy, movewithdir, diverge, renamedelete, dirmove = {}, {}, {}, {}, {}
# manifests fetched in order are going to be faster, so prime the caches
[x.manifest() for x in
@@ -802,7 +802,7 @@
if followcopies:
ret = copies.mergecopies(repo, wctx, p2, pa)
- copy, movewithdir, diverge, renamedelete = ret
+ copy, movewithdir, diverge, renamedelete, dirmove = ret
repo.ui.note(_("resolving manifests\n"))
repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
@@ -921,7 +921,16 @@
actions[f] = ('cm', (fl2, pa.node()),
"remote created, get or merge")
elif n2 != ma[f]:
- if acceptremote:
+ df = None
+ for d in dirmove:
+ if f.startswith(d):
+ # new file added in a directory that was moved
+ df = dirmove[d] + f[len(d):]
+ break
+ if df in m1:
+ actions[df] = ('m', (df, f, f, False, pa.node()),
+ "local directory rename - respect move from " + f)
+ elif acceptremote:
actions[f] = ('c', (fl2,), "remote recreating")
else:
actions[f] = ('dc', (None, f, f, False, pa.node()),
--- a/tests/test-graft.t Mon Nov 28 05:45:22 2016 +0000
+++ b/tests/test-graft.t Mon Dec 05 17:40:01 2016 +0100
@@ -1286,3 +1286,28 @@
$ hg ci -qAmc
$ hg up -q .~2
$ hg graft tip -qt:fail
+
+ $ cd ..
+
+Graft a change into a new file previously grafted into a renamed directory
+
+ $ hg init dirmovenewfile
+ $ cd dirmovenewfile
+ $ mkdir a
+ $ echo a > a/a
+ $ hg ci -qAma
+ $ echo x > a/x
+ $ hg ci -qAmx
+ $ hg up -q 0
+ $ hg mv -q a b
+ $ hg ci -qAmb
+ $ hg graft -q 1 # a/x grafted as b/x, but no copy information recorded
+ $ hg up -q 1
+ $ echo y > a/x
+ $ hg ci -qAmy
+ $ hg up -q 3
+ $ hg graft -q 4
+ $ hg status --change .
+ M b/x
+
+ $ cd ..