user3186301
user3186301

Reputation: 101

Combine two merges into one (or, can I change a commit's parents?)

Can you please consider the following scenario:

Assume we have the following topic branches: A, B, C, D, E, F and that we want to merge them all together.

Also assume, due to multiple conflicts, it is NOT possible to merge them all together in one octopus merge:

# git checkout A
# git --no-ff B C D E F
ERROR: no octopus strategy allowed

However, it was possible to merge them into turns:

# git checkout A
# git merge --no-ff B C
OK with conflicts.
(resolve conflicts)
# git commit

# git checkout D
# git merge --no-ff E F
OK with conflicts.
(resolve conflicts)
# git commit

Now we have two merge commits on the HEADs of A and D, respectively. And we can merge those two.

# git checkout A
# git merge --no-ff D
OK: no conlicts

Now the history looks a bit like this:

* A
|\
|*- D
||\\
|||* E
||* F
|*
*-
|\\
||* B
|* C
*

As you can now see the commit at the HEAD of A, has two parents each of which is a merge commit. Is it possible rewrite the last commit (with all the changes it contains) so that it will have as parents the commits pointed by the HEADs of the original six branches?

If there is no git command that can do this, is it possible to find the object in the git database, change it, calculate a new SHA-1 and store it?

Thank you very much in advance.

Upvotes: 2

Views: 94

Answers (1)

torek
torek

Reputation: 488013

You cannot change any part of any existing commit.

You can, as RomainValeri noted in a comment, build any new commit you like, including a custom merge commit made with git commit-tree. You can then modify the hash IDs stored in any particular branch name(s) you like to refer to this new merge commit that is in effect an octopus merge with conflict resolution:

          ________________
         /                \
...--o--o   <-- name1      \
         \   _______________\
          \ /                \
           X                  \
          / \                  \
 ...--o--o---M1   <-- name2     @   <-- branch
               >--M3 <--name5  /
 ...--o--o---M2   <-- name3   /
          \ /                /
           X________________/
          /                /
 ...--o--o   <-- name4    /
          \______________/

where @ is your hand-crafted octopus merge, for instance. Change the names around as needed (you can remove and re-create names, or force-update names, or rename branch names, at will) so that one particular name you find particularly interesting, selects commit @, which you made with git commit-tree using the same tree as merge M3 which merges merge commits M1 and M2.

(The multiple names above are mostly optional, but help you find each commit.)

I would advise against doing this because it's not a typical work-flow. If you do do it, put something in the unusual merge's commit message about how you made it and why. Some future code archaeologist—possibly even yourself in a year—may be looking at historical commits, trying to understand how some bug came about for instance, and may encounter this unusual, hand-crafted merge commit. Without guidance, or at least a reminder, whoever encounters this octopus merge could look at it and think: aha, an octopus merge, there must not have been any conflicts and hence assume that this merge had no conflicts.

If you have two ordinary merges in a row, whoever encounters them can't assume a lack of conflicts, because ordinary merges often involve conflict resolution.

Upvotes: 2

Related Questions