From 8d283e9658bb930b44fb71048410f85841f9e434 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Tue, 14 May 2013 10:07:01 +0200 Subject: [PATCH] Handle vertex merges more carefully. When autofilling the vertex of a block, it is possible to do the merge three ways: 1. Using the commits directly above and to the left as parents 2. Using the commits at [i1,0] and [i1-1,i2] as parents (i.e., as a continuation of the bottom edge) 3. Using the commits at [i1,i2-1] and [0,i2] as parents (i.e., as a continuation of the right edge) We want the final history to be *as if* the parents were as in (1). Formerly we were doing (1) and simply using the result. This is incorrect because the neighboring commits do not have correct histories. Therefore, the merge base for this merge is [0,0], and the merge can have unnecessary conflicts. Change to trying both (2) and (3). If both of those commits succeed *and* if they give identical trees, then create a synthetic commit with the parents as in (1) and use that; otherwise, fail. --- git-imerge | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/git-imerge b/git-imerge index 64ee967..ca030f4 100755 --- a/git-imerge +++ b/git-imerge @@ -1241,19 +1241,18 @@ class Block(object): # of them permanently. merges = [] - def do_merge(i1, commit1, i2, commit2): + def do_merge(i1, commit1, i2, commit2, msg='Autofilling %d-%d...', record=True): if (i1, i2) in self: return self[i1,i2].sha1 try: - sys.stderr.write( - 'Autofilling %d-%d...' % self.get_original_indexes(i1, i2) - ) + sys.stderr.write(msg % self.get_original_indexes(i1, i2)) merge = automerge(commit1, commit2) sys.stderr.write('success.\n') except AutomaticMergeFailed, e: sys.stderr.write('unexpected conflict. Backtracking...\n') raise UnexpectedMergeFailure(str(e), i1, i2) - merges.append((i1, i2, merge)) + if record: + merges.append((i1, i2, merge)) return merge i2 = self.len2 - 1 @@ -1266,10 +1265,39 @@ class Block(object): for i2 in range(1, self.len2 - 1): above = do_merge(i1, above, i2, self[0,i2].sha1) + # We will compare two ways of doing the final "vertex" merge: + # as a continuation of the bottom edge, or as a continuation + # of the right edge. We only accept it if both approaches + # succeed and give identical trees. i1, i2 = self.len1 - 1, self.len2 - 1 - do_merge(i1, above, i2, left) + vertex_v1 = do_merge( + i1, self[i1,0].sha1, i2, left, + msg='Autofilling %d-%d (first way)...', + record=False, + ) + vertex_v2 = do_merge( + i1, above, i2, self[0,i2].sha1, + msg='Autofilling %d-%d (second way)...', + record=False, + ) + if get_tree(vertex_v1) == get_tree(vertex_v2): + sys.stderr.write( + 'The two ways of autofilling %d-%d agree.\n' + % self.get_original_indexes(i1, i2) + ) + else: + sys.stderr.write( + 'The two ways of autofilling %d-%d do not agree. Backtracking...\n' + % self.get_original_indexes(i1, i2) + ) + raise UnexpectedMergeFailure('Inconsistent vertex merges', i1, i2) - # Success! Now we can record the results: + # Everything is OK. Now reparent the actual vertex merge to + # have above and left as its parents: + merges.append((i1, i2, reparent(vertex_v1, [above, left]))) + + # Done! Now we can record the results: + sys.stderr.write('Recording autofilled block %s.\n' % (self,)) for (i1, i2, merge) in merges: self[i1, i2].record_merge(merge, MergeRecord.NEW_AUTO)