17

The problem: A branch has good commits interleaved with undesired ones.

Attempted solution:

git revert hash5 hash8 hash9 hash23

What I thought this would do is that it'd apply all specified commits, and then let me sort out any conflicts.

What I now think that happens:

  • git applies commit hash5, in the process introducing large conflicts.
  • I attemt a merge, editing code what I want it to look like, setting the stage for more conflicts (see next point)
  • git applies commit hash8, which conflict with the edits done in the merge
  • I attempt a merge... etc etc

Question: How do I get git to apply all the reverts in a row before presenting any possible conflicts to me?

1 Answer 1

12

Sanity check

First, note that git revert reverts your patches in the order you list their hashes; you need to list the hashes from newest to oldest, since you want to proceed backwards in time. So, I'm going to call your hashes

<hash1> ... <hashN>

where <hash1> is older than <hash2> ... is older than <hashN>. So, make sure you were doing

git revert <hashN> ... <hash1>

in the first place!

Easy solution

Second, assuming you had been reverting them in the right order, try the --no-commit option:

git revert --no-commit <hashN> ... <hash1>

More involved solution

Third, If the easy solution doesn't work well, but the commits you want to revert really do make sense as a single commit (if not I don't see much hope), then try this: build one big commit out of the four you want to revert, and then revert the big commit.

  1. Build the big commit:

    Create a branch at the parent of the oldest commit:

    git checkout -b big-commit <hash1>~
    

    Copy the commits on your new branch and collapse them:

    git cherry-pick --no-commit <hash1> ... <hashN>
    git commit -m "Big commit"
    

    You should now have one big commit on your branch big-commit.

  2. Apply the big commit in reverse to the branch you're trying to revert:

    git checkout <branch you wanted to revert on>
    git revert big-commit
    

Another relatively easy solution

Use selective rebasing to rebuild the branch in question as if it never contained the unwanted commits:

  1. Create a new rebuild branch to work in:

    git checkout -b rebuild <branch you want to revert>
    
  2. Interactively rebase, dropping the commits you don't want:

    git rebase -i <hash1>~
    

    In the interactive rebase editor, delete the lines for <hash1> ... <hashN>.

Now your rebuild branch will contain <branch you want to revert>, as though <hash1> ... <hashN> never existed. If you run into conflicts here it would seem they're unavoidable.

If you need your work to be on <branch you want to revert>, and you can't just git reset it to point to your new rebuild branch:

git checkout <branch you want to revert>
git reset --hard rebuild

(e.g. because you've already pushed it publicly), then you can instead apply the differences to <branch you want to revert> as a patch:

git co <branch you want to revert>    
git diff <branch you want to revert> rebuild | patch
4
  • 1
    I'm curious - wouldn't using the cherry-pick command in this situation still generate merge conflicts that need to be manually resolved as each commit is applied? I thought the OP is trying to avoid doing procedural merging tasks.
    – miqh
    Commented Nov 27, 2013 at 1:07
  • @miqid: good question! I was assuming the commits themselves were locally compatible / could be applied in order from some starting point without conflict. If that's not the case, I see no hope of avoiding the conflicts.
    – ntc2
    Commented Nov 27, 2013 at 1:10
  • In fact before the merge I tried a cherry-pick too. The result was roughly the same -- I found myself resolving conflicts for no apparent reason. Commented Nov 27, 2013 at 2:26
  • @BenjaminBurkhart: did you try the --no-commit flag with git revert or git cherry-pick? I have another idea; I'll add it above ...
    – ntc2
    Commented Nov 27, 2013 at 5:09

Not the answer you're looking for? Browse other questions tagged or ask your own question.