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():