Removing a specific commit in git history that includes merges

I want to remove a specific commit, C, in git history, as well as some single-commit-merges.

The history looks like this. In this example, I want to remove the single-commit-merge m6 and a problematic commit C.

  • Getting merged code “right” with Git
  • Adding a commit from a fork to a topic branch
  • node module gitignores its own node_modules directory, breaking Heroku deploy
  • Git diff in summary?
  • error: could not apply ---> tried it in proper way by seeing instructions but still errors for git squash
  • Using git for plugin development
  • ---m0-------------------m1---m2---m3-------------m4---m5----m6---m7---(...)
         \                 /            \           /       \  /
          b0---b1---b2---b3              b4--(C)--b5         b6

    I want to wind up with this (changes highlighted):

        \                 /            \vvvvvvv/          ^^
         b0---b1---b2---b3              b4---b5

    If I use “git rebase -i” to excise C and b6/m6, this works, but I get:


    and the history is flattened. I want to preserve the entire structure. Conversely, if I run with the --preserve-merges flag, then I get:

    error: Commit <sha-1 corresponding to m6> is a merge but no -m option was given.
    fatal: cherry-pick failed
    Could not pick <sha-1 corresponding to m6>

    and I can’t remove b6/m6 (although I can remove C).

    What’s the right way to wind up with the desired result? I’m using git 1.8.4, if that matters.

    (Note that the other questions on StackOverflow that I perused don’t seem to account for this very specific case, so as nearly as I can tell, this is not a duplicate question.)

  • Find files that changed specifically in a branch
  • git cherry-pick to another branch
  • How to organize git in projects with overlapping dependencies?
  • Git: Is there a quick way to see when was the last time that git merge master was done on the current working branch?
  • How do I manage active development on git submodules
  • How to get list of latest tags in remote git?
  • 2 Solutions collect form web for “Removing a specific commit in git history that includes merges”

    Looks like it may be a small bug in git-rebase--interactive:

                case "$new_parents" in
                ' '*' '*)
                        test "a$1" = a-n && die "Refusing to squash a merge: $sha1"
                ... snip ...
                        output eval git cherry-pick "$strategy_args" "$@" ||
                                die_with_patch $sha1 "Could not pick $sha1"

    When this hits m6, the new set of parents is singular, so it tries to use cherry-pick, but m6 is a merge so cherry-pick requires a hint about which parent to use.

    I’m not sure why it’s trying to take m6 at all, though.

    Solution/workaround: (for me, anyway) This seems to be a bug in git rebase. I reverted to git 1.7.9 and my problem was solved; git rebase --interactive --preserve-merges worked exactly as I wanted and produces the graph I indicated in my post.

    Git Baby is a git and github fan, let's start git clone.