comparison mercurial/copies.py @ 41392:b80af0707066

copies: consider nullrev a common ancestor I've seen many bugs in the git codebase that were caused by it not having a null revision and being forced to treat root commits differently. Mercurial has a null revision and I think it's generally a bug to treat it differently from other commits in graph algorithms. This effectively undoes 83cfa1baf8ad (copies: don't report copies with unrelated branch, 2010-01-01). The test cases that that commit added still passes. I suspect some other fix after that commit made it unnecessary. Differential Revision: https://phab.mercurial-scm.org/D5594
author Martin von Zweigbergk <martinvonz@google.com>
date Tue, 15 Jan 2019 11:16:42 -0800
parents f3f5bfbf7e04
children dc50121126ae
comparison
equal deleted inserted replaced
41391:bc843e251134 41392:b80af0707066
29 Find the last revision that needs to be checked to ensure that a full 29 Find the last revision that needs to be checked to ensure that a full
30 transitive closure for file copies can be properly calculated. 30 transitive closure for file copies can be properly calculated.
31 Generally, this means finding the earliest revision number that's an 31 Generally, this means finding the earliest revision number that's an
32 ancestor of a or b but not both, except when a or b is a direct descendent 32 ancestor of a or b but not both, except when a or b is a direct descendent
33 of the other, in which case we can return the minimum revnum of a and b. 33 of the other, in which case we can return the minimum revnum of a and b.
34 None if no such revision exists.
35 """ 34 """
36 35
37 # basic idea: 36 # basic idea:
38 # - mark a and b with different sides 37 # - mark a and b with different sides
39 # - if a parent's children are all on the same side, the parent is 38 # - if a parent's children are all on the same side, the parent is
53 52
54 side = {a: -1, b: 1} 53 side = {a: -1, b: 1}
55 visit = [-a, -b] 54 visit = [-a, -b]
56 heapq.heapify(visit) 55 heapq.heapify(visit)
57 interesting = len(visit) 56 interesting = len(visit)
58 hascommonancestor = False
59 limit = node.wdirrev 57 limit = node.wdirrev
60 58
61 while interesting: 59 while interesting:
62 r = -heapq.heappop(visit) 60 r = -heapq.heappop(visit)
63 if r == node.wdirrev: 61 if r == node.wdirrev:
64 parents = [cl.rev(p) for p in repo.dirstate.parents()] 62 parents = [cl.rev(p) for p in repo.dirstate.parents()]
65 else: 63 else:
66 parents = cl.parentrevs(r) 64 parents = cl.parentrevs(r)
65 if parents[1] == node.nullrev:
66 parents = parents[:1]
67 for p in parents: 67 for p in parents:
68 if p < 0:
69 continue
70 if p not in side: 68 if p not in side:
71 # first time we see p; add it to visit 69 # first time we see p; add it to visit
72 side[p] = side[r] 70 side[p] = side[r]
73 if side[p]: 71 if side[p]:
74 interesting += 1 72 interesting += 1
75 heapq.heappush(visit, -p) 73 heapq.heappush(visit, -p)
76 elif side[p] and side[p] != side[r]: 74 elif side[p] and side[p] != side[r]:
77 # p was interesting but now we know better 75 # p was interesting but now we know better
78 side[p] = 0 76 side[p] = 0
79 interesting -= 1 77 interesting -= 1
80 hascommonancestor = True
81 if side[r]: 78 if side[r]:
82 limit = r # lowest rev visited 79 limit = r # lowest rev visited
83 interesting -= 1 80 interesting -= 1
84
85 if not hascommonancestor:
86 return None
87 81
88 # Consider the following flow (see test-commit-amend.t under issue4405): 82 # Consider the following flow (see test-commit-amend.t under issue4405):
89 # 1/ File 'a0' committed 83 # 1/ File 'a0' committed
90 # 2/ File renamed from 'a0' to 'a1' in a new commit (call it 'a1') 84 # 2/ File renamed from 'a0' to 'a1' in a new commit (call it 'a1')
91 # 3/ Move back to first commit 85 # 3/ Move back to first commit
167 dbg = repo.ui.debug 161 dbg = repo.ui.debug
168 if debug: 162 if debug:
169 dbg('debug.copies: looking into rename from %s to %s\n' 163 dbg('debug.copies: looking into rename from %s to %s\n'
170 % (a, b)) 164 % (a, b))
171 limit = _findlimit(repo, a.rev(), b.rev()) 165 limit = _findlimit(repo, a.rev(), b.rev())
172 if limit is None:
173 limit = node.nullrev
174 if debug: 166 if debug:
175 dbg('debug.copies: search limit: %d\n' % limit) 167 dbg('debug.copies: search limit: %d\n' % limit)
176 am = a.manifest() 168 am = a.manifest()
177 169
178 # find where new files came from 170 # find where new files came from
463 tca = base 455 tca = base
464 if graft: 456 if graft:
465 tca = _c1.ancestor(_c2) 457 tca = _c1.ancestor(_c2)
466 458
467 limit = _findlimit(repo, c1.rev(), c2.rev()) 459 limit = _findlimit(repo, c1.rev(), c2.rev())
468 if limit is None:
469 # no common ancestor, no copies
470 return {}, {}, {}, {}, {}
471 repo.ui.debug(" searching for copies back to rev %d\n" % limit) 460 repo.ui.debug(" searching for copies back to rev %d\n" % limit)
472 461
473 m1 = c1.manifest() 462 m1 = c1.manifest()
474 m2 = c2.manifest() 463 m2 = c2.manifest()
475 mb = base.manifest() 464 mb = base.manifest()