From 48be4e80dd99c974f3d50638f62b8df2ec029507 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 25 Jun 2018 10:35:18 +0200 Subject: [PATCH] Add three new goals that involve keeping the border commits Add three new goals: * `border` * `border-with-history` * `border-with-history2` These keep all the commits along both edges of the border of the full merge graph, but not interior merge commits. They differ in whether to also preserve history information across the "gap", like `rebase` vs. `rebase-with-history`. See the README for more information. --- README.rst | 98 +++++++++++++++++++++++++++++++++++++++++++++ git-imerge | 97 ++++++++++++++++++++++++++++++++++++++++++-- t/test-conflicted | 8 +++- t/test-unconflicted | 8 +++- 4 files changed, 206 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index cc3aa71..93857ec 100644 --- a/README.rst +++ b/README.rst @@ -195,8 +195,106 @@ history by using either the ``finish`` or ``simplify`` command. The approach. See [2]_ for more information. ``full`` + don't simplify the incremental merge at all: do all of the intermediate merges and retain them all in the permanent history. + In other words, it transforms this:: + + o---o---1---2---3 BRANCH1 + \ + A---B---C---D BRANCH2 + + into this:: + + o---o---1---2---3 + \ \ \ \ + A---A1--A2--A3 + \ \ \ \ + B---B1--B2--B3 + \ \ \ \ + C---C1--C2--C3 + \ \ \ \ + D---D1--D2--D3 NEW_BRANCH + + This approach retains the complete history and ancestry + information, which gives the maximum flexibility for conducting + future merges. On the other hand, it clutters up the permanent Git + history considerably. + +``border`` + this experimental goal retains the rebase of ``BRANCH2`` onto + ``BRANCH1`` and also the rebase of ``BRANCH1`` onto ``BRANCH2``, + plus a merge commit that includes both branches. In other words, + it transforms this:: + + o---o---1---2---3 BRANCH1 + \ + A---B---C---D BRANCH2 + + into this:: + + o---o---1---2---3 + \ \ + A A2 + \ \ + B B2 + \ \ + C C2 + \ \ + D---D1--D2--D3 NEW_BRANCH + + This approach leaves more history than a simple merge or rebase, + possibly making future merges easier. + +``border-with-history`` + this experimental goal retains the rebase-with-history of + ``BRANCH2`` onto ``BRANCH1`` and also the rebase (without history) + of ``BRANCH1`` onto ``BRANCH2``, plus a merge commit that includes + both branches. In other words, it transforms this:: + + o---o---1---2---3 BRANCH1 + \ + A---B---C---D BRANCH2 + + into this:: + + o---o---1---2---3 + \ \ + A-----------A3 + \ \ + B-----------B3 + \ \ + C-----------C3 + \ \ + D---D1--D2--D3 NEW_BRANCH + + This approach leaves more history and ancestry information than a + simple merge or rebase, possibly making future merges easier. + +``border-with-history2`` + this experimental goal retains the rebase-with-history of + ``BRANCH1`` onto ``BRANCH2`` and also the rebase-with-history of + ``BRANCH2`` onto ``BRANCH1``, plus a merge commit that includes + both branches. In other words, it transforms this:: + + o---o---1---2---3 BRANCH1 + \ + A---B---C---D BRANCH2 + + into this:: + + o---o---1---2---3 + \ \ \ \ + A--- --- ---A3 + \ \ \ \ + B--- --- ---B3 + \ \ \ \ + C--- --- ---C3 + \ \ \ \ + D---D1--D2--D3 NEW_BRANCH + + This approach leaves more history and ancestry information than a + simple merge or rebase, possibly making future merges easier. Technical notes diff --git a/git-imerge b/git-imerge index 87eac6b..b7020e4 100755 --- a/git-imerge +++ b/git-imerge @@ -122,8 +122,11 @@ ZEROS = '0' * 40 ALLOWED_GOALS = [ 'full', - 'rebase-with-history', 'rebase', + 'rebase-with-history', + 'border', + 'border-with-history', + 'border-with-history2', 'merge', 'drop', 'revert', @@ -2864,6 +2867,86 @@ class MergeState(Block): self._set_refname(refname, commit, force=force) + def simplify_to_border( + self, refname, + with_history1=False, with_history2=False, force=False, + ): + i1 = self.len1 - 1 + for i2 in range(1, self.len2): + if not (i1, i2) in self: + raise Failure( + 'Cannot simplify to border because ' + 'merge %d-%d is not yet done' + % (i1, i2) + ) + + i2 = self.len2 - 1 + for i1 in range(1, self.len1): + if not (i1, i2) in self: + raise Failure( + 'Cannot simplify to border because ' + 'merge %d-%d is not yet done' + % (i1, i2) + ) + + i1 = self.len1 - 1 + commit = self[i1, 0].sha1 + for i2 in range(1, self.len2 - 1): + orig = self[0, i2].sha1 + tree = self.git.get_tree(self[i1, i2].sha1) + + # Create a commit, copying the old log message: + if with_history2: + parents = [commit, orig] + msg = ( + self.git.get_log_message(orig).rstrip('\n') + + '\n\n(rebased-with-history from commit %s)\n' % (orig,) + ) + else: + parents = [commit] + msg = ( + self.git.get_log_message(orig).rstrip('\n') + + '\n\n(rebased from commit %s)\n' % (orig,) + ) + + commit = self.git.commit_tree(tree, parents, msg=msg) + commit1 = commit + + i2 = self.len2 - 1 + commit = self[0, i2].sha1 + for i1 in range(1, self.len1 - 1): + orig = self[i1, 0].sha1 + tree = self.git.get_tree(self[i1, i2].sha1) + + # Create a commit, copying the old log message: + if with_history1: + parents = [orig, commit] + msg = ( + self.git.get_log_message(orig).rstrip('\n') + + '\n\n(rebased-with-history from commit %s)\n' % (orig,) + ) + else: + parents = [commit] + msg = ( + self.git.get_log_message(orig).rstrip('\n') + + '\n\n(rebased from commit %s)\n' % (orig,) + ) + + commit = self.git.commit_tree(tree, parents, msg=msg) + commit2 = commit + + # Construct the apex commit: + tree = self.git.get_tree(self[-1, -1].sha1) + msg = ( + 'Merge %s into %s (using imerge border)' + % (self.tip2, self.tip1) + ) + + commit = self.git.commit_tree(tree, [commit1, commit2], msg=msg) + + # Update the reference: + self._set_refname(refname, commit, force=force) + def _simplify_to_path(self, refname, base, path, force=False): """Simplify based on path and set refname to the result. @@ -2976,10 +3059,18 @@ class MergeState(Block): if self.goal == 'full': self.simplify_to_full(refname, force=force) - elif self.goal == 'rebase-with-history': - self.simplify_to_rebase_with_history(refname, force=force) elif self.goal == 'rebase': self.simplify_to_rebase(refname, force=force) + elif self.goal == 'rebase-with-history': + self.simplify_to_rebase_with_history(refname, force=force) + elif self.goal == 'border': + self.simplify_to_border(refname, force=force) + elif self.goal == 'border-with-history': + self.simplify_to_border(refname, with_history2=True, force=force) + elif self.goal == 'border-with-history2': + self.simplify_to_border( + refname, with_history1=True, with_history2=True, force=force, + ) elif self.goal == 'drop': self.simplify_to_drop(refname, force=force) elif self.goal == 'revert': diff --git a/t/test-conflicted b/t/test-conflicted index 262aa03..16d1f4d 100755 --- a/t/test-conflicted +++ b/t/test-conflicted @@ -14,7 +14,10 @@ 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-rebase c-d-rebase-with-history \ + c-d-border c-d-border-with-history c-d-border-with-history2 \ + c-d-full do git branch -D $b || true done @@ -37,6 +40,9 @@ 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_IMERGE" simplify --goal=border --branch=c-d-border +"$GIT_IMERGE" simplify --goal=border-with-history --branch=c-d-border-with-history +"$GIT_IMERGE" simplify --goal=border-with-history2 --branch=c-d-border-with-history2 "$GIT_IMERGE" remove git checkout c diff --git a/t/test-unconflicted b/t/test-unconflicted index 3615fa9..e89ed5a 100755 --- a/t/test-unconflicted +++ b/t/test-unconflicted @@ -14,7 +14,10 @@ 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-rebase a-b-rebase-with-history \ + a-b-border a-b-border-with-history a-b-border-with-history2 \ + a-b-full do git branch -D $b || true done @@ -26,6 +29,9 @@ git checkout a GIT_EDITOR=cat "$GIT_IMERGE" simplify --goal=merge --branch=a-b-merge "$GIT_IMERGE" simplify --goal=rebase --branch=a-b-rebase "$GIT_IMERGE" simplify --goal=rebase-with-history --branch=a-b-rebase-with-history +"$GIT_IMERGE" simplify --goal=border --branch=a-b-border +"$GIT_IMERGE" simplify --goal=border-with-history --branch=a-b-border-with-history +"$GIT_IMERGE" simplify --goal=border-with-history2 --branch=a-b-border-with-history2 "$GIT_IMERGE" remove git checkout a