Remove a merge commit, keeping current changes

23,180

Solution 1

This is a job for git rebase -i. Run:

git rebase -i F

You'll be presented with a list of commits in ${EDITOR}, like so:

pick 334ad92 D
pick fb54c42 E
pick 6901e51 B
pick 6c61a52 A

# Rebase eea2847..6c61a52 onto eea2847
#
# (more instructions here)

Delete the pick fb54c42 E line to remove that commit. (Also, if you want A and B to be combined into a single commit, you can change the pick command to squash -- squash 6c61a52 A). Save and close the file and your branches will be in the state you wish.

Note that this will change history. This means that you'll need to do a git push -f if you've already pushed the branch anywhere, and it'll mess with anyone else who's been collaborating on this branch. If that's an issue, you can git revert the merge commit instead:

git revert -m 1 C

The -m 1 argument tells git to revert against the first parent of the commit, which is the side that was merged into (D, in your diagram).

Solution 2

I fail to understand what direction the timeline has in your examples (which commits are made before which) so I'll explain in broad terms:

  1. Fork a new branch off the pre-merge commit, and check it out.
  2. git cherry-pick A and B there to bring in the changes made by the deveoper in a merge commit.
  3. Cherry-pick whatever commits came after the merge commit.

By now your new branch contains the history as it supposed to look at so you might now git reset --hard your original branch to this state. Be aware that force-pushing it to a shared repository and fetching it by the other devs will have obvious repercussions for them: everyone will have to git rebase whatever work they have based on the old state of that branch to its new state.

You might also want to "refork" the feature branch off the "fixed" branch. As I've said, I failed to see whether it was forked before the offending commit of after. If it was done before, you won't need fixing up the feature branch.

Solution 3

Use git revert C to revert the C merge commit (you may have to pass -m 1 so that Git knows which side of the merge to keep). See How to revert a faulty merge for details and an elaboration of what happens if you continue work on the feature branch and later on decide to merge it again.

The revert approach doesn't give you exactly what you're asking for; the commits brought in by the merge will still be part of the project's history, but their changes will be gone. Getting rid of the commits and the merge altogether will require a rebase (and described in Ash Wilson's response), which requires careful consideration and usually isn't worth the effort unless there are legal restrictions or technical issues (like gigantic binaries) with the commits that arrived through the merge commit.

Share:
23,180

Related videos on Youtube

khose
Author by

khose

iOS/Android/Palm/WP7 Fancy developer

Updated on July 09, 2022

Comments

  • khose
    khose almost 2 years

    We have had a small problem in our team. One dev had to include some changes in our development branch. Before doing so, he accidentally merged a feature branch (which shouldn't be merged then) and kept on working over those changes, generating a couple of commits after the merge.

    Now we want to keep those changes, but apply them to the commit before the merge was done. To keep it clear:

    A (+b, +a)
    |
    B (+a)
    |
    C (merge commit)
    |\
    D \
    |  E (feature branch)
    | /
    |/
    F
    

    What we want to have his changes (+a,+b) applied over commit D . The equivalent to:

    C (+a,+b)
    |
    D 
    |  E (feature branch)
    | /
    |/
    F
    

    How can we commit a change to dismiss a previous merge keeping local changes?

  • kostix
    kostix over 10 years
    Removing picking of commit E will make the changes it represent disappear from the history, and this is not what the OP wants.
  • khose
    khose over 10 years
    Thanks for the tip!!! @kostix Thanks for pointing it down. Although I understand what Ash wanted to say with it.
  • Ralf Marmorglatt
    Ralf Marmorglatt over 10 years
    @kostix E will still be reachable from the feature branch, though (assuming it wasn't deleted).
  • khose
    khose over 10 years
    BTW @kostix Really nice solution, elegant. I forgot to tell you this yesterday :)
  • Rayjax
    Rayjax over 8 years
    what if the feature branch has a lot of commits, and it was merged without -no-ff so the history is now mixed up ? should I remove the "pick" lines one by one for every commit from the feature branch or is there any better solution ?
  • Swaps
    Swaps almost 8 years
    what if that merge is necessary? will it be fine if we revert the merge commit? won't it remove all changes?
  • Magnus Bäck
    Magnus Bäck almost 8 years
    The old commits will remain in the history of the branch but the changes introduced by those commits will be gone.
  • Danny
    Danny almost 7 years
    If you have a merge commit you want to remove, and it's your last commit in your git log, do a git rebase -i HEAD~2, and the merge commit seems to not even show. Saving and exiting the editor now leaves history intact without the merge commit
  • Oleg Valter is with Ukraine
    Oleg Valter is with Ukraine about 3 years
    FYI, for anyone who stumbles upon the Q&A - the direction in the question is, unusually, F -> A (which is important since git rebase -i A would be noop as A is HEAD in this history).