copies: remove commit graph walk

Summary:
Copy tracing had a manually implemented graph walk that was very
expensive with lazy changelog, since it walked the parent graph one-by-one. It
was created before revsets, so let's just replace it with a revset.

I'm not 100% certain the algorithm is identical, but it seems pretty close.

Reviewed By: quark-zju

Differential Revision: D31777360

fbshipit-source-id: a7b1209ddee823faa3d4adb0fcc920feadb0afa4
This commit is contained in:
Durham Goode 2021-10-21 11:44:11 -07:00 committed by Facebook GitHub Bot
parent 4407137752
commit 119081a243
3 changed files with 9 additions and 80 deletions

View File

@ -22,88 +22,17 @@ from .i18n import _
def _findlimit(repo, a, b):
"""
Find the last revision that needs to be checked to ensure that a full
transitive closure for file copies can be properly calculated.
Generally, this means finding the earliest revision number that's an
ancestor of a or b but not both, except when a or b is a direct descendent
of the other, in which case we can return the minimum revnum of a and b.
None if no such revision exists.
Find the earliest revision that's an ancestor of a or b but not both, except
in the case where a or b is an ancestor of the other.
"""
# basic idea:
# - mark a and b with different sides
# - if a parent's children are all on the same side, the parent is
# on that side, otherwise it is on no side
# - walk the graph in topological order with the help of a heap;
# - add unseen parents to side map
# - clear side of any parent that has children on different sides
# - track number of interesting revs that might still be on a side
# - track the lowest interesting rev seen
# - quit when interesting revs is zero
cl = repo.changelog
working = 1 << 63 # pseudo rev for the working directory
if a is None:
a = working
a = repo.revs("p1()").first()
if b is None:
b = working
side = {a: -1, b: 1}
visit = [-a, -b]
heapq.heapify(visit)
interesting = len(visit)
hascommonancestor = False
limit = working
while interesting > 0:
r = -(heapq.heappop(visit))
if r == working:
parents = [cl.rev(p) for p in repo.dirstate.parents()]
else:
parents = cl.parentrevs(r)
for p in parents:
if p < 0:
continue
if p not in side:
# first time we see p; add it to visit
side[p] = side[r]
if side[p]:
interesting += 1
heapq.heappush(visit, -p)
elif side[p] and side[p] != side[r]:
# p was interesting but now we know better
side[p] = 0
interesting -= 1
hascommonancestor = True
if side[r]:
limit = r # lowest rev visited
interesting -= 1
if not hascommonancestor:
b = repo.revs("p1()").first()
if a is None or b is None or not repo.revs("ancestor(%d, %d)", a, b):
return None
# Consider the following flow (see test-commit-amend.t under issue4405):
# 1/ File 'a0' committed
# 2/ File renamed from 'a0' to 'a1' in a new commit (call it 'a1')
# 3/ Move back to first commit
# 4/ Create a new commit via revert to contents of 'a1' (call it 'a1-amend')
# 5/ Rename file from 'a1' to 'a2' and commit --amend 'a1-msg'
#
# During the amend in step five, we will be in this state:
#
# @ 3 temporary amend commit for a1-amend
# |
# o 2 a1-amend
# |
# | o 1 a1
# |/
# o 0 a0
#
# When _findlimit is called, a and b are revs 3 and 0, so limit will be 2,
# yet the filelog has the copy information in rev 1 and we will not look
# back far enough unless we also look at the a and b as candidates.
# This only occurs when a is a descendent of b or visa-versa.
return min(limit, a, b)
return repo.revs("only(%d, %d) + only(%d, %d) + %d + %d", a, b, b, a, a, b).min()
def _chain(src, dst, a, b):

View File

@ -172,7 +172,7 @@ Update to link with local change should cause a merge prompt (issue3200):
$ hg up -Cq 'desc(add)'
$ echo data > a
$ HGMERGE= hg up -y --debug
searching for copies back to 521a1e40188f
searching for copies back to c334dc3be0da
resolving manifests
branchmerge: False, force: False, partial: False
ancestor: c334dc3be0da, local: c334dc3be0da+, remote: 521a1e40188f

View File

@ -43,7 +43,7 @@
summary: 1
$ hg --debug up
searching for copies back to 1e71731e6fbb
searching for copies back to c19d34741b0a
unmatched files in other:
b
resolving manifests
@ -99,7 +99,7 @@
summary: 1
$ hg --debug up
searching for copies back to 1e71731e6fbb
searching for copies back to c19d34741b0a
unmatched files in other:
b
resolving manifests