# HG changeset patch # User Martin von Zweigbergk # Date 1579823070 28800 # Node ID fa9ad1da2e774decc37bf50135c3e1c32ed3566a # Parent 7f8bdee0034e5001186db03b693858e390a1a561 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 diff -r 7f8bdee0034e -r fa9ad1da2e77 mercurial/copies.py --- 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): diff -r 7f8bdee0034e -r fa9ad1da2e77 mercurial/merge.py --- 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