Mercurial > hg
comparison mercurial/copies.py @ 23071:652ab726ba93 stable
amend: fix amending rename commit with diverged topologies (issue4405)
This addresses the bug described in issue4405: when obsolescence markers are
enabled, amending a commit with a file move can lead to the copy information
being lost.
However, the bug is more general and can be reproduced without obsmarkers as
well, as demonstracted by Pierre-Yves and put into the updated test.
Specifically, graph topology divergences between the filelogs and the changelog
can cause copy information to be lost during amends.
author | Ryan McElroy <rmcelroy@fb.com> |
---|---|
date | Thu, 16 Oct 2014 06:35:06 -0700 |
parents | 722117c8e023 |
children | e53f6b72a0e4 |
comparison
equal
deleted
inserted
replaced
23070:c289fb3624b8 | 23071:652ab726ba93 |
---|---|
17 if s == -1: | 17 if s == -1: |
18 return "" | 18 return "" |
19 return f[:s] | 19 return f[:s] |
20 | 20 |
21 def _findlimit(repo, a, b): | 21 def _findlimit(repo, a, b): |
22 """Find the earliest revision that's an ancestor of a or b but not both, | 22 """ |
23 Find the last revision that needs to be checked to ensure that a full | |
24 transitive closure for file copies can be properly calculated. | |
25 Generally, this means finding the earliest revision number that's an | |
26 ancestor of a or b but not both, except when a or b is a direct descendent | |
27 of the other, in which case we can return the minimum revnum of a and b. | |
23 None if no such revision exists. | 28 None if no such revision exists. |
24 """ | 29 """ |
30 | |
25 # basic idea: | 31 # basic idea: |
26 # - mark a and b with different sides | 32 # - mark a and b with different sides |
27 # - if a parent's children are all on the same side, the parent is | 33 # - if a parent's children are all on the same side, the parent is |
28 # on that side, otherwise it is on no side | 34 # on that side, otherwise it is on no side |
29 # - walk the graph in topological order with the help of a heap; | 35 # - walk the graph in topological order with the help of a heap; |
71 limit = r # lowest rev visited | 77 limit = r # lowest rev visited |
72 interesting -= 1 | 78 interesting -= 1 |
73 | 79 |
74 if not hascommonancestor: | 80 if not hascommonancestor: |
75 return None | 81 return None |
76 return limit | 82 |
83 # Consider the following flow (see test-commit-amend.t under issue4405): | |
84 # 1/ File 'a0' committed | |
85 # 2/ File renamed from 'a0' to 'a1' in a new commit (call it 'a1') | |
86 # 3/ Move back to first commit | |
87 # 4/ Create a new commit via revert to contents of 'a1' (call it 'a1-amend') | |
88 # 5/ Rename file from 'a1' to 'a2' and commit --amend 'a1-msg' | |
89 # | |
90 # During the amend in step five, we will be in this state: | |
91 # | |
92 # @ 3 temporary amend commit for a1-amend | |
93 # | | |
94 # o 2 a1-amend | |
95 # | | |
96 # | o 1 a1 | |
97 # |/ | |
98 # o 0 a0 | |
99 # | |
100 # When findlimit is called, a and b are revs 3 and 0, so limit will be 2, | |
101 # yet the filelog has the copy information in rev 1 and we will not look | |
102 # back far enough unless we also look at the a and b as candidates. | |
103 # This only occurs when a is a descendent of b or visa-versa. | |
104 return min(limit, a, b) | |
77 | 105 |
78 def _chain(src, dst, a, b): | 106 def _chain(src, dst, a, b): |
79 '''chain two sets of copies a->b''' | 107 '''chain two sets of copies a->b''' |
80 t = a.copy() | 108 t = a.copy() |
81 for k, v in b.iteritems(): | 109 for k, v in b.iteritems(): |