diff --git a/mercurial/copies.py b/mercurial/copies.py --- a/mercurial/copies.py +++ b/mercurial/copies.py @@ -306,7 +306,6 @@ def _changesetforwardcopies(a, b, match): if a.rev() in (node.nullrev, b.rev()): return {} - repo = a.repo() children = {} revinfo = _revinfogetter(repo) @@ -314,6 +313,8 @@ cl = repo.changelog missingrevs = cl.findmissingrevs(common=[a.rev()], heads=[b.rev()]) + mrset = set(missingrevs) + roots = set() for r in missingrevs: for p in parents(r): if p == node.nullrev: @@ -322,17 +323,23 @@ children[p] = [r] else: children[p].append(r) + if p not in mrset: + roots.add(p) + if not roots: + # no common revision to track copies from + return {} + min_root = min(roots) - roots = set(children) - set(missingrevs) - work = [r for r in roots] + from_head = set(cl.reachableroots(min_root, [b.rev()], list(roots), includepath=True)) + + iterrevs = set(from_head) + iterrevs &= mrset + iterrevs.update(roots) + iterrevs.remove(b.rev()) all_copies = dict((r, {}) for r in roots) - heapq.heapify(work) alwaysmatch = match.always() - while work: - r = heapq.heappop(work) + for r in sorted(iterrevs): copies = all_copies.pop(r) - if r == b.rev(): - return copies for i, c in enumerate(children[r]): p1, p2, p1copies, p2copies, removed = revinfo(c) if r == p1: @@ -358,7 +365,6 @@ del newcopies[f] othercopies = all_copies.get(c) if othercopies is None: - heapq.heappush(work, c) all_copies[c] = newcopies else: # we are the second parent to work on c, we need to merge our @@ -378,7 +384,7 @@ else: newcopies.update(othercopies) all_copies[c] = newcopies - assert False + return all_copies[b.rev()] def _forwardcopies(a, b, base=None, match=None): diff --git a/tests/test-copies.t b/tests/test-copies.t --- a/tests/test-copies.t +++ b/tests/test-copies.t @@ -236,7 +236,7 @@ $ hg debugpathcopies 1 2 y -> x $ hg debugpathcopies 1 3 - $ hg debugpathcopies 2 3 + $ FOO=1 hg debugpathcopies 2 3 x -> y Copy file from either side in a merge