From 43c96036be9dda2bcb1d6e106c94864e090fef33 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Tue, 14 May 2013 09:27:05 +0200 Subject: [PATCH 1/2] Add a function get_tree(). --- git-imerge | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/git-imerge b/git-imerge index 6dac6d4..64ee967 100755 --- a/git-imerge +++ b/git-imerge @@ -326,6 +326,10 @@ def get_type(arg): return check_output(['git', 'cat-file', '-t', arg]).strip() +def get_tree(arg): + return rev_parse('%s^{tree}' % (arg,)) + + BRANCH_PREFIX = 'refs/heads/' def checkout(refname): @@ -1915,7 +1919,7 @@ class MergeState(Block): commit = self[i1, 0].sha1 for i2 in range(1, self.len2): orig = self[0, i2].sha1 - tree = rev_parse('%s^{tree}' % (self[i1, i2].sha1,)) + tree = get_tree(self[i1, i2].sha1) # Create a commit, copying the old log message: commit = check_output([ @@ -1938,7 +1942,7 @@ class MergeState(Block): commit = self[i1, 0].sha1 for i2 in range(1, self.len2): orig = self[0, i2].sha1 - tree = rev_parse('%s^{tree}' % (self[i1, i2].sha1,)) + tree = get_tree(self[i1, i2].sha1) # Create a commit, copying the old log message: commit = check_output([ @@ -1955,7 +1959,7 @@ class MergeState(Block): 'Cannot simplify to merge because merge %d-%d is not yet done' % (self.len1 - 1, self.len2 - 1) ) - tree = rev_parse('%s^{tree}' % (self[-1, -1].sha1,)) + tree = get_tree(self[-1, -1].sha1) parents = [self[-1,0].sha1, self[0,-1].sha1] # Create a preliminary commit with a generic commit message: From 8d283e9658bb930b44fb71048410f85841f9e434 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Tue, 14 May 2013 10:07:01 +0200 Subject: [PATCH 2/2] 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)