From 9fafd2688e9bffb4e88ba8d5e0fe04646b3e4a00 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Wed, 30 Oct 2013 09:21:52 +0100 Subject: [PATCH] Add a new goal, "manual". Add a new "manual" goal that is like "merge" but also keeps a permanent record of any manual commits that were needed to resolve conflicts, plus some automatic commits needed to make sense of the manual commits. --- README.rst | 11 ++++++-- TODO.rst | 3 -- git-imerge | 69 +++++++++++++++++++++++++++++++++++++++++++++ t/test-conflicted | 3 +- t/test-unconflicted | 5 ++-- 5 files changed, 83 insertions(+), 8 deletions(-) diff --git a/README.rst b/README.rst index fd2bdf5..4664a7c 100644 --- a/README.rst +++ b/README.rst @@ -89,8 +89,8 @@ history. discard the intermediate merge commits and create a simpler history to record permanently in your project repository using either the ``finish`` or -``simplify`` command. The "goal" of the incremental merge can be one -of the following: +``simplify`` command. The incremental merge can be simplified in one +of four ways: ``merge`` keep only a simple merge of the second branch into the first @@ -100,6 +100,13 @@ of the following: git checkout BRANCH1 git merge BRANCH2 +``manual`` + like ``merge``, but also keep any merges that had to be done + manually plus enough automatic merges to connect the manual merges + together meaningfully. If the result is later discovered to be + buggy, the additional information might be helpful in isolating + the problem. + ``rebase`` keep the versions of the commits from the second branch rebased onto the first branch. The end result is similar to what you diff --git a/TODO.rst b/TODO.rst index 9ec9e40..a7a3d26 100644 --- a/TODO.rst +++ b/TODO.rst @@ -52,9 +52,6 @@ Convenience features New merge goals and styles ========================== -* Add a ``--goal=sparse`` option that retains all of the conflicted - merges and enough intermediate history to connect them. - * Add an option that allows the user to resolve conflicts in larger chunks; for example, add a rebase-with-history-type merge where each branch commit is merged directly to its final location on the last diff --git a/git-imerge b/git-imerge index 2a9a3cf..a5cb92f 100755 --- a/git-imerge +++ b/git-imerge @@ -80,6 +80,13 @@ where git checkout BRANCH1 git merge BRANCH2 + * manual + like merge, but also keep any merges that had to be done + manually plus enough automatic merges to connect the + manual merges together meaningfully. If the merge is + later discovered to be buggy, the additional information + might be helpful in isolating the problem. + * rebase keep the versions of the commits from the second branch rebased onto the first branch. The end result is similar @@ -198,6 +205,7 @@ ZEROS = '0' * 40 ALLOWED_GOALS = [ 'full', + 'manual', 'rebase-with-history', 'rebase', 'merge', @@ -2308,6 +2316,65 @@ class MergeState(Block): self._set_refname(refname, self[-1, -1].sha1, force=force) + def simplify_to_manual(self, refname, force=False): + i1s = set() + i2s = set() + for i2 in range(1, self.len2): + for i1 in range(1, self.len1): + record = self[i1,i2] + if record.is_known() and record.is_manual(): + i1s.add(i1) + i2s.add(i2) + if not i1s: + # No manual merges were needed; simplify to merge instead. + self.simplify_to_merge(refname, force=force) + return + + i1s = sorted(i1s.union([0, self.len1 - 1])) + i2s = sorted(i2s.union([0, self.len2 - 1])) + + for i2 in i2s: + for i1 in i1s: + if not (i1, i2) in self: + raise Failure( + 'Cannot simplify to "manual" because ' + 'merge %d-%d is not yet done' + % (i1, i2) + ) + + # A map {(i1, i2) : sha1} for the grid commits that will be + # retained: + keepers = {} + keepers.update(((i1,0), self[i1,0].sha1) for i1 in i1s) + keepers.update(((0,i2), self[0,i2].sha1) for i2 in i2s) + + for index1 in range(1, len(i1s)): + i1 = i1s[index1] + for index2 in range(1, len(i2s)): + i2 = i2s[index2] + tree = get_tree(self[i1, i2].sha1) + parents = [ + keepers[i1, i2s[index2 - 1]], + keepers[i1s[index1 - 1], i2], + ] + + if index1 == len(i1s) - 1 and index2 == len(i2s) - 1: + # Handle the last commit specially. First create + # a preliminary commit with a generic commit + # message: + sha1 = commit_tree( + tree, parents, + msg='Merge %s into %s (using imerge)' % (self.tip2, self.tip1), + ) + + self._set_refname(refname, sha1, force=force) + + # Now let the user edit the commit log message: + check_call(['git', 'commit', '--amend']) + else: + msg = get_log_message(self[i1, i2].sha1) + keepers[i1, i2] = commit_tree(tree, parents, msg=msg) + def simplify_to_rebase_with_history(self, refname, force=False): i1 = self.len1 - 1 for i2 in range(1, self.len2): @@ -2400,6 +2467,8 @@ class MergeState(Block): if self.goal == 'full': self.simplify_to_full(refname, force=force) + elif self.goal == 'manual': + self.simplify_to_manual(refname, force=force) elif self.goal == 'rebase-with-history': self.simplify_to_rebase_with_history(refname, force=force) elif self.goal == 'rebase': diff --git a/t/test-conflicted b/t/test-conflicted index 6a35755..be6c288 100755 --- a/t/test-conflicted +++ b/t/test-conflicted @@ -14,7 +14,7 @@ cd "$TMP" # Clean up detritus from possible previous runs of this test: git checkout master "$GIT_IMERGE" remove --name=c-d || true -for b in c-d-merge c-d-rebase c-d-rebase-with-history c-d-full +for b in c-d-merge c-d-manual c-d-rebase c-d-rebase-with-history c-d-full do git branch -D $b || true done @@ -33,6 +33,7 @@ git add conflict.txt GIT_EDITOR=cat "$GIT_IMERGE" simplify --goal=merge --branch=c-d-merge "$GIT_IMERGE" simplify --goal=rebase --branch=c-d-rebase "$GIT_IMERGE" simplify --goal=rebase-with-history --branch=c-d-rebase-with-history +GIT_EDITOR=cat "$GIT_IMERGE" simplify --goal=manual --branch=c-d-manual "$GIT_IMERGE" remove git checkout c diff --git a/t/test-unconflicted b/t/test-unconflicted index cc40a89..ed5ec2d 100755 --- a/t/test-unconflicted +++ b/t/test-unconflicted @@ -14,7 +14,7 @@ cd "$TMP" # Clean up detritus from possible previous runs: git checkout master "$GIT_IMERGE" remove --name=a-b || true -for b in a-b-merge a-b-rebase a-b-rebase-with-history a-b-full +for b in a-b-merge a-b-manual a-b-rebase a-b-rebase-with-history a-b-full do git branch -D $b || true done @@ -32,5 +32,6 @@ git checkout a "$GIT_IMERGE" start --goal=full --first-parent --name=a-b b "$GIT_IMERGE" list "$GIT_IMERGE" diagram --commits --frontier --html=imerge4.html -"$GIT_IMERGE" finish --branch=a-b-full +"$GIT_IMERGE" simplify --branch=a-b-full +GIT_EDITOR=cat "$GIT_IMERGE" finish --goal=manual --branch=a-b-manual