merge: start using the per-side copy dicts
The point of this patch is mostly to clarify `manifestmerge()`. I find
it much easier to reason about now.
Differential Revision: https://phab.mercurial-scm.org/D7990
--- a/mercurial/copies.py Wed Jan 22 14:35:30 2020 -0800
+++ b/mercurial/copies.py Thu Jan 23 15:44:30 2020 -0800
@@ -463,19 +463,23 @@
"""
# avoid silly behavior for update from empty dir
if not c1 or not c2 or c1 == c2:
- return branch_copies(), {}
+ return branch_copies(), branch_copies(), {}
narrowmatch = c1.repo().narrowmatch()
# avoid silly behavior for parent -> working dir
if c2.node() is None and c1.node() == repo.dirstate.p1():
- return branch_copies(_dirstatecopies(repo, narrowmatch)), {}
+ return (
+ branch_copies(_dirstatecopies(repo, narrowmatch)),
+ branch_copies(),
+ {},
+ )
copytracing = repo.ui.config(b'experimental', b'copytrace')
if stringutil.parsebool(copytracing) is False:
# stringutil.parsebool() returns None when it is unable to parse the
# value, so we should rely on making sure copytracing is on such cases
- return branch_copies(), {}
+ return branch_copies(), branch_copies(), {}
if usechangesetcentricalgo(repo):
# The heuristics don't make sense when we need changeset-centric algos
@@ -578,7 +582,7 @@
copies2 = pathcopies(base, c2)
if not (copies1 or copies2):
- return branch_copies(), {}
+ return branch_copies(), branch_copies(), {}
inversecopies1 = {}
inversecopies2 = {}
@@ -681,12 +685,10 @@
dirmove1, movewithdir2 = _dir_renames(repo, c1, copy1, copies1, u2)
dirmove2, movewithdir1 = _dir_renames(repo, c2, copy2, copies2, u1)
- copy1.update(copy2)
- renamedelete1.update(renamedelete2)
- movewithdir1.update(movewithdir2)
- dirmove1.update(dirmove2)
+ branch_copies1 = branch_copies(copy1, renamedelete1, dirmove1, movewithdir1)
+ branch_copies2 = branch_copies(copy2, renamedelete2, dirmove2, movewithdir2)
- return branch_copies(copy1, renamedelete1, dirmove1, movewithdir1), diverge
+ return branch_copies1, branch_copies2, diverge
def _dir_renames(repo, ctx, copy, fullcopy, addedfiles):
@@ -784,8 +786,6 @@
if c2.rev() is None:
c2 = c2.p1()
- copies = {}
-
changedfiles = set()
m1 = c1.manifest()
if not repo.revs(b'%d::%d', base.rev(), c2.rev()):
@@ -805,10 +805,11 @@
changedfiles.update(ctx.files())
ctx = ctx.p1()
+ copies2 = {}
cp = _forwardcopies(base, c2)
for dst, src in pycompat.iteritems(cp):
if src in m1:
- copies[dst] = src
+ copies2[dst] = src
# file is missing if it isn't present in the destination, but is present in
# the base and present in the source.
@@ -817,6 +818,7 @@
filt = lambda f: f not in m1 and f in base and f in c2
missingfiles = [f for f in changedfiles if filt(f)]
+ copies1 = {}
if missingfiles:
basenametofilename = collections.defaultdict(list)
dirnametofilename = collections.defaultdict(list)
@@ -858,9 +860,9 @@
# if there are a few related copies then we'll merge
# changes into all of them. This matches the behaviour
# of upstream copytracing
- copies[candidate] = f
+ copies1[candidate] = f
- return branch_copies(copies), {}
+ return branch_copies(copies1), branch_copies(copies2), {}
def _related(f1, f2):
--- a/mercurial/merge.py Wed Jan 22 14:35:30 2020 -0800
+++ b/mercurial/merge.py Thu Jan 23 15:44:30 2020 -0800
@@ -1262,13 +1262,13 @@
for x in sorted(wctx.parents() + [p2, pa], key=scmutil.intrev)
]
- copy, movewithdir, diverge, renamedelete, dirmove = {}, {}, {}, {}, {}
+ branch_copies1 = copies.branch_copies()
+ branch_copies2 = copies.branch_copies()
+ diverge = {}
if followcopies:
- branch_copies, diverge = copies.mergecopies(repo, wctx, p2, pa)
- copy = branch_copies.copy
- renamedelete = branch_copies.renamedelete
- dirmove = branch_copies.dirmove
- movewithdir = branch_copies.movewithdir
+ branch_copies1, branch_copies2, diverge = copies.mergecopies(
+ repo, wctx, p2, pa
+ )
boolbm = pycompat.bytestr(bool(branchmerge))
boolf = pycompat.bytestr(bool(force))
@@ -1280,8 +1280,10 @@
repo.ui.debug(b" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
- copied = set(copy.values())
- copied.update(movewithdir.values())
+ copied1 = set(branch_copies1.copy.values())
+ copied1.update(branch_copies1.movewithdir.values())
+ copied2 = set(branch_copies2.copy.values())
+ copied2.update(branch_copies2.movewithdir.values())
if b'.hgsubstate' in m1 and wctx.rev() is None:
# Check whether sub state is modified, and overwrite the manifest
@@ -1301,10 +1303,10 @@
relevantfiles = set(ma.diff(m2).keys())
# For copied and moved files, we need to add the source file too.
- for copykey, copyvalue in pycompat.iteritems(copy):
+ for copykey, copyvalue in pycompat.iteritems(branch_copies1.copy):
if copyvalue in relevantfiles:
relevantfiles.add(copykey)
- for movedirkey in movewithdir:
+ for movedirkey in branch_copies1.movewithdir:
relevantfiles.add(movedirkey)
filesmatcher = scmutil.matchfiles(repo, relevantfiles)
matcher = matchmod.intersectmatchers(matcher, filesmatcher)
@@ -1315,7 +1317,10 @@
for f, ((n1, fl1), (n2, fl2)) in pycompat.iteritems(diff):
if n1 and n2: # file exists on both local and remote side
if f not in ma:
- fa = copy.get(f, None)
+ # TODO: what if they're renamed from different sources?
+ fa = branch_copies1.copy.get(
+ f, None
+ ) or branch_copies2.copy.get(f, None)
if fa is not None:
actions[f] = (
ACTION_MERGE,
@@ -1358,10 +1363,12 @@
b'versions differ',
)
elif n1: # file exists only on local side
- if f in copied:
+ if f in copied2:
pass # we'll deal with it on m2 side
- elif f in movewithdir: # directory rename, move local
- f2 = movewithdir[f]
+ elif (
+ f in branch_copies1.movewithdir
+ ): # directory rename, move local
+ f2 = branch_copies1.movewithdir[f]
if f2 in m2:
actions[f2] = (
ACTION_MERGE,
@@ -1374,8 +1381,8 @@
(f, fl1),
b'remote directory rename - move from %s' % f,
)
- elif f in copy:
- f2 = copy[f]
+ elif f in branch_copies1.copy:
+ f2 = branch_copies1.copy[f]
actions[f] = (
ACTION_MERGE,
(f, f2, f2, False, pa.node()),
@@ -1399,10 +1406,10 @@
else:
actions[f] = (ACTION_REMOVE, None, b'other deleted')
elif n2: # file exists only on remote side
- if f in copied:
+ if f in copied1:
pass # we'll deal with it on m1 side
- elif f in movewithdir:
- f2 = movewithdir[f]
+ elif f in branch_copies2.movewithdir:
+ f2 = branch_copies2.movewithdir[f]
if f2 in m1:
actions[f2] = (
ACTION_MERGE,
@@ -1415,8 +1422,8 @@
(f, fl2),
b'local directory rename - get from %s' % f,
)
- elif f in copy:
- f2 = copy[f]
+ elif f in branch_copies2.copy:
+ f2 = branch_copies2.copy[f]
if f2 in m2:
actions[f] = (
ACTION_MERGE,
@@ -1453,10 +1460,10 @@
)
elif n2 != ma[f]:
df = None
- for d in dirmove:
+ for d in branch_copies1.dirmove:
if f.startswith(d):
# new file added in a directory that was moved
- df = dirmove[d] + f[len(d) :]
+ df = branch_copies1.dirmove[d] + f[len(d) :]
break
if df is not None and df in m1:
actions[df] = (
@@ -1483,6 +1490,9 @@
# Updates "actions" in place
_filternarrowactions(narrowmatch, branchmerge, actions)
+ renamedelete = branch_copies1.renamedelete
+ renamedelete.update(branch_copies2.renamedelete)
+
return actions, diverge, renamedelete