Ztyx
Ztyx

Reputation: 14959

rebase reverted merged branch

Background: I recently merged a rather large topic branch into master. A couple of days later I discovered this topic branch contained bugs. So I git revert -m 1 <merge-commit>ed it.

Problem: Now I'd like to check out the topic branch and rebase it against current master so that I can 1) fix the bugs and 2) (again) merge the fixed up topic branch with master. Creating the new branch, fixedtopic is the easy part, but every time I do

git checkout fixedtopic
git rebase master

git decides that it's not willing to replay the old commits since they are already merged into master. Instead it simply does a fast-forward rebase.

Question: How can force replay of the commits onto fixedtopic using rebase? Can I? I'd rather not use cherry-pick since it's a bit more cumbersome.

Additional:

Upvotes: 53

Views: 15941

Answers (7)

amin_nejad
amin_nejad

Reputation: 1090

What worked best for me stashing my feature branch's changes and applying them to a new branch off main and resolving the conflicts there (if any).

git checkout my-feature-branch
git reset --soft <commit-on-main-that-the-feature-branch-was-branched-off>
git stash
git checkout main
git checkout -b my-feature-branch-2
git stash pop

This feels like a safer method with much lower likelihood of messing up any existing branches. This also allows you to resolve conflicts in the new branch if other PRs have been merged into main since the feature branch was reverted.

Upvotes: 1

Louis Caron
Louis Caron

Reputation: 1350

With git 2.22, the solution suggested by jurglic does not seem to work anymore.

Let's start with the status, to make sure that we are talking about the same thing:

P---o---o---M---o---o---o---o---o---W---o---o---master
 \         /     \                /
  A---B---C       D (revert A-B-C)

I had created a dev branch with A-B-C commits and it was merged in M, but immediately we noticed there was an error so it was reverted in W. I would like to rebase A-B-C on master and fix the issue before merging it again.

Here is what I did:

git checkout C          (detached HEAD) 
git checkout -b redo
git rebase -i master

But at this point, git offered me only the following:

noop

# Rebase A..C onto X (1 command)

This meant that it figured out that I was trying to reapply the exact same commits and they had already been merged.

To workaround this problem, jurglic had suggested to modify A's the commit message but it seems that git 2.22 is clever enough to figure it out and was still offering me the exact same noop. I tried other solutions, using --force-rebase and/or --no-ff, but I was all the time back to noop.

Finally, I used an easy short-cut by entering manually in the rebase -i selection editor and I replaced noop with:

pick A
pick B
pick C

or, if you want to save even more keystrokes:

p A
p B
p C

This worked finally like a charm.

Upvotes: 20

Alexander Pozdneev
Alexander Pozdneev

Reputation: 1389

For git merge (i.e. not for git rebase as was originally asked), as per 7.8 Git Tools - Advanced Merging (see the "Reverse the commit" section):

The best way around this is to un-revert the original merge, since now you want to bring in the changes that were reverted out, then create a new merge commit:

$ git revert ^M

<...>

$ git merge topic

History after re-merging a reverted merge Figure 142. History after re-merging a reverted merge

Upvotes: 0

Peter Hartley
Peter Hartley

Reputation: 301

The documentation (git help rebase) suggests that "git rebase --force-rebase" does just this -- but (Git 1.9.1) it doesn't. The documentation also suggests that "git rebase -i --no-ff" is equivalent to that command -- but it isn't: it does work.

Having cloned your gist, the commands:

  git checkout topic
  git rebase -i --no-ff --onto master 7b3af topic

produce the desired result, with new versions of the "third" and "fourth" commits on top of master, and topic pointing at the new version of "fourth". In the second command, the SHA 7b3af is the "second" commit, the point where topic was branched from.

Upvotes: 30

Steve Jorgensen
Steve Jorgensen

Reputation: 12401

What seems to work best is to check out a new branch, and then re-instate the previous working branch by reverting revert.

Everything else that I've tried is overly complicated and/or doesn't work.

Upvotes: 1

jurglic
jurglic

Reputation: 3737

One way to achieve this is to interactively rebase the topic branch and reword the first commit after branching out of master (e.g. git rebase -i HEAD~10 if you have 10 commits in the branch). This will rewrite sha's of all the commits inside the topic branch. Therefore you will be able to rebase the usual way with git rebase master.

Upvotes: 16

CB Bailey
CB Bailey

Reputation: 793269

You need to use --onto to prevent Git form trying to determine the appropriate unmerged commits on its own.

E.g. (with topic branch checked out):

git rebase --onto master <id-of-branch-point>

For <id-of-branch-point> you want the git merge-base of your topic branch and the commit on master before the merge that you reverted.

Edit

Re-reading your situation again, it might be a better if you fast-forward the topic branch to the point where you reverted the merge, then revert the reversion and fix the topic branch from that point. This way you won't get a repetition of all the commits in the original topic branch but with new ids in the final history of master. Whatever you do, you're going to end up with a history involving "do, undo, redo", but this way might be considered a cleaner history.

Upvotes: 6

Related Questions