0

I'm trying to undo a commit in the middle while keeping the following ones unchanged. I have thought it might be impossible before finding these posts: Post 1 & Post 2. (The ultimate goal might be duplicated but I indeed didn't have it work. I apologise for that.)

Unfortunately, I'm still stuck in how to use git rebase -i properly. I created a sample repo as following:

commit cc2576365a9716bb9f96e195e599190d3c70fba0
Author: zzy
Date:   Sun Jul 17 14:30:53 2016 -0400
commit #3

commit 11d9f8fdc69be8b4530e4b70c9661b4e34287afe
Author: zzy
Date:   Sun Jul 17 14:30:36 2016 -0400
commit #2

commit 4770e08eb54d0d582bd4b391f2361a328b417cdf
Author: zzy
Date:   Sun Jul 17 14:29:58 2016 -0400
commit #1

Each commit adds a new line at a file called a (see a's content below):

After commit #1

1

After commit #2

1
2

After commit #3

1
2
3

Now I intend to remove commit #2, and the a should look like:

1
3

I tried the command as git rebase -i 11d9f8fdc69be8^ and get the text file as:

pick 11d9f8f commit #2
pick cc25763 commit #3

I deleted the first line, only keeping commit #3 and exited the file. But I get the following error:

error: could not apply cc25763... commit #3

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".
Could not apply cc2576365a9716bb9f96e195e599190d3c70fba0... commit #3

And the file a becomes:

1
<<<<<<< HEAD
=======
2
3
>>>>>>> cc25763... commit #3

I'm confused why 2 is still here and why there are conflicts. Could anyone help me to get the expected result? Thanks in advance!

Community
  • 1
  • 1
zzy
  • 751
  • 1
  • 13
  • 25
  • 4
    The changes you introduce with your commits are too close one to another. They do not overlap but, in order to be sure nothing is lost, `Git` and any other `diff` program includes at least two lines of context (one before and one after) in the diff. In this perspective, the changes introduced by your commits overlap and this is why a conflict happens. Try again with a larger file and take care to make the changes several lines apart and it will work as expected. – axiac Jul 19 '16 at 21:07
  • 2
    Besides [what axiac mentioned](http://stackoverflow.com/questions/38468446/use-git-rebase-to-remove-a-commit-in-the-middle#comment64341138_38468446), you may want to set `merge.conflictstyle` to `diff3`, so that Git will show you not just the two different versions, but also the common base part. It's not really useful for this particular trivial example, but in general, I find it easier to read a merge conflict with `diff3` mode set. – torek Jul 19 '16 at 21:10
  • @axiac Thank you soooooooo much!!!! Yes I changed to a larger example with the exactly same commands and it works! – zzy Jul 19 '16 at 21:22
  • @torek May I ask how to enable it? I did a quick survey and find this command: `git config --global merge.conflictstyle diff3`. Is it what you mentioned here? – zzy Jul 19 '16 at 21:24
  • Instead of a rebase you can undo a commit with git revert. It's worth to mention it.. even if you explicitly ask for rebasing solution. $git revert SHA_commit2.. Doing this you keep trace of the change – xaa Jul 19 '16 at 21:25
  • @zzy: yes, that's the right way to set `merge.conflictstyle`. – torek Jul 19 '16 at 21:26
  • @xaa So will `git revert` remove all following commits (in this example, will it maintain commit #3)? If it can, you are right, I feel like `git revert` might be the more proper command to use here and this is only one "side effect" of `git rebase`. The main purpose of `git rebase` seems to be for merging branches if I understand correctly. – zzy Jul 19 '16 at 21:32
  • @torek OK I got it. Thank you! – zzy Jul 19 '16 at 21:32
  • @zzy:yes indeed. `git revert ` will undo the commit and will let the others untouched. `git revert` will generate a new commit (so a fourth commit in your case) that undoes all changes introduced by . I would say: use `revert` to keep the "undo" history. Use `rebase` if you are working locally (commits not pushed yet) and want a clean history without useless commits. Read more on https://www.atlassian.com/git/tutorials/undoing-changes/git-revert – xaa Jul 20 '16 at 09:36
  • @xaa I see. So `git revert` generates a new "reverse" commit to undo a certain one while `git rebase` applied all following commits to a previous version to skip certain commits. Am I correct here? Also, may I ask will such "reverse" commit given by `git revert` cause conflict sometimes? (I guess yes, when the following commits touch the same text of the undone commit.) Thanks! – zzy Jul 20 '16 at 14:31

1 Answers1

2

Basically, since both commit #2 and commit #3 changed lines that were very close to each other in the same file, git doesn't know quite how to "replay" the changes in commit #3 now that commit #2 is no longer present, so you're getting a conflict.

Within the file that contains the conflicts, you see this:

<<<<<<< HEAD
=======
2
3
>>>>>>> cc25763... commit #3

The space between <<<<<<< HEAD and ======= shows what the file contained in that location at commit #1, before commit #2 or commit #3 were created. The space between ======= and >>>>>>> cc25763... commit #3 contains the contents of that section of the file in commit #3, after commit #2 and commit #3 were added. Git expects you to use this information to decide manually what content the new version of commit #3 should contain now that you're trying to replay it without commit #2.

Replace that whole section of the file with what you want the file to contain after commit #3 is replayed. In this case, you'd replace:

<<<<<<< HEAD
=======
2
3
>>>>>>> cc25763... commit #3

With:

3

Then, run git add a to stage these changes, and git rebase --continue to continue the rebase.

Note that you only have to do this during a rebase when conflicts are encountered. For commits which change unrelated things (e.g. if commits #2 and #3 were changing entirely different files, or completely different sections of the same file), git rebase can usually determine what changes to make automatically, and you won't encounter conflicts.

Ajedi32
  • 45,670
  • 22
  • 127
  • 172