Remove git merge commit without conflict

68 views Asked by At

Below is my git commit history:

HEAD
... +500 commits
A   4140759b
A   c5cf69a2
A   3e5282cb  ⎯┐   << Merge branch B
|   94936548    B
|   02a550ce    B   
A   5d09daa9  ⎯┤   << Pull request
|   afbe397b    B   << Merge branch A into B
|   74fe4f3c    B
|   86cdc993    B
|   223e1740    B
|   a7ea596a    B
|   0d4c36cf    B
A   2740d295    |
A   24437962    |
A   73725f04    |
A   90be4300    |
A   6b1720d6  ⎯┘
A   d65bf4bd
...

I want to remove B branch history and merge commit, and leave A branch history only.

But, they are very old commits, with hundreds of commits between the latest commits,
so git rebase -i command makes a lot of conflicts that I couldn't solve.

It doesn't matter if a few commits, between d65bf4bd and c5cf69a2, dropped.
However, recent commits, after c5cf69a2, should not be affected.

How can I completely remove B branch history, from history?

My expected result looks like this:

HEAD
... +500 commits
pick    A   4140759b
pick    A   c5cf69a2
[drop]  A   3e5282cb  ⎯┐   << Merge branch B
[drop]  |   94936548    B
[drop]  |   02a550ce    B   
[drop]  A   5d09daa9  ⎯┤   << Pull request
[drop]  |   afbe397b    B   << Merge branch A into B
[drop]  |   74fe4f3c    B
[drop]  |   86cdc993    B
[drop]  |   223e1740    B
[drop]  |   a7ea596a    B
[drop]  |   0d4c36cf    B
pick    A   2740d295    |
pick    A   24437962    |
pick    A   73725f04    |
pick    A   90be4300    |
pick    A   6b1720d6  ⎯┘
pick    A   d65bf4bd
...
2

There are 2 answers

1
LeGEC On BEST ANSWER

Here is one way to squash together the history of branch B:

  1. use git replace to create a commit, with the same end content but only one parent, and a replacement rule
git replace --graft [target commit] [the parent you want]
# with the hashes you provided:
git replace --graft 3e5282cb 2740d295

on your machine, git log --oneline --graph should already display a simplified history

  1. to make this replacement rule permanent and actually rewrite the history, use git filter-repo or git filter-branch
git filter-repo --replace-refs delete-no-add

# or
git filter-branch --env-filter true

a note about git filter-branch: making the replacement permanent is actually a side effect of git filter-branch [any command], you can use any subcommand (--index-filter, --env-filter, --tree-filter ...) and any scripted action as long as it makes git filter-branch go through. --env-filter true happens to be convenient, because git will not read or write any file on disk, and true has no side effects.

1
eftshift0 On

It sounds like it will be painful if you go with rebase interactive... actually, why are you trying interactive? If the 2 merges of branch B are one after the other, then you should be able to pull it off like this:

git rebase --rebase-merges --onto 2740d295 3e5282cb HEAD

That should remove the commits from B and the 2 merge commits. It's not like you will not get conflicts.... you will still get conflicts but given that you are using --rebase-merges it will include the merges (though you might still have to solve conflicts in the merges) and other conflicts could be related to the changes you are not including from B.