We have a master branch into which we've merged about 10 feature branches, one at a time.
So the recent history looks like this:
merged feat/10 (HEAD of master)
merged feat/9
merged feat/8
merged feat/7
merged feat/6
merged feat/5
...
Now we found out that feat/7
was bad and we want to take it out of master. Reverting that merge commit isn't enough because we don't want that broken commit to exist in our history at all. We can't really use interactive rebase because that will flatten out the history to make it look as if it was all done on a single branch, and we want to preserve all that good merge history.
Is there a way to zap out a particular merge commit from a branch's history?
I'll note that the real history is much more complex than what you see in the example above, so manually re-doing all the merges since feat/7 wouldn't be a good option.
Edit
To clarify for those who vote to close this as a dup: this isn't the FAQ about how to take out a commit with rebase, which of course has been answered many times. The question here is about taking out a commit without flattening the merge history.
In the Conceptual Overview section, we saw how a feature branch can incorporate upstream changes from main using either git merge or git rebase . Merging is a safe option that preserves the entire history of your repository, while rebasing creates a linear history by moving your feature branch onto the tip of main .
There are many ways to rewrite history with git. Use git commit --amend to change your latest log message. Use git commit --amend to make modifications to the most recent commit. Use git rebase to combine commits and modify history of a branch.
No history is rewritten. From the high-level commands, only git rebase and git commit --amend perform history rewrites.
Interactive rebase is one of those tools that "rewrite" Git history – and you shouldn't do this on commits that have already been shared with others. With this little warning message out of the way, let's look at some practical examples!
If your history currently looks like that and you didnt delete the branches yet you can simply git reset --hard HEAD~4
this will reset your code back to state before you merged in 7 then you can simply git merge
the good ones back in. This is the simplest way I can think of off my head.
EDIT : You can use -p switch on rebase to preserve merges but using this switch with -i might have consequances. Check man git-rebase page and see the BUGS part to see current bugs.
EDIT2 : I don't take any responsibility if you don't take proper precautions before using this command. Don't use it before reading the manpage too.
You can use git filter-branch --parent-filter
to rewrite the feat/8
commit so that its parent points to the feat/6
commit. Leaving the parents of all other commits (9-10) as they are, which should preserve merge commits in history as they were.
Only problem with this is what will happen to conflicts that result in the removed code changed ... there is no real way of knowing, and it might be the culprit.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With