1

Is there a good way to undo(or remove) a merge from history?

The original state:

* F main
| 
* E
|   
* D
|\  Merge: C 4
| | 
| * 4 dev
| | 
| * 3
| |
* | C
| | 
| * 2
| | 
| * 1
|/ 
* B
| 
* A

After a undo(or remove) the merge, it would looks like:

* F main
| 
* E
|   
| * 4 dev
| | 
| * 3
| |
* | C
| | 
| * 2
| | 
| * 1
|/ 
* B
| 
* A

Or looks like:

* C E F main
|   
| * 4 dev
| | 
| * 3
| | 
| * 2
| | 
| * 1
|/ 
* B
| 
* A

I have tried git rebase --onto C D main, but there was a error:

error: Your local changes to the following files would be overwritten by checkout:
    file1
    file2
    ...
Please commit your changes or stash them before you switch branches.
Aborting
error: could not detach HEAD

I gave up this method since I don't know the dangers of advanced rebase applications. Finally, I reset the branch of main hard to the commit of C, and add the E,F commits manually. I wonder if is there a good way to achieve this.

Stoull
  • 1,098
  • 8
  • 13
  • I'm guessing that you understand the potential problems with "rewriting history", which is what you are doing. If you share your code with other devs or between multiple local repos, then you should understand this. If you only have your one local repo, this isn't an issue. – CryptoFool Jan 05 '23 at 03:23

1 Answers1

3

I believe your command:

git rebase --onto C D main

was correct for achieving your goal. In fact, it would have had the same effect as what you ended up doing:

Finally, I reset the branch of main hard to the commit of C, and add the E,F commits manually.

(I assume by "add" you mean "cherry-picked", and if not, you could have done that as well.)

The rebase command would have done all of that for you if your status had been clean at the time, but the error you received was because file1 and file2 were currently modified. The rebase command was aborting because you needed to do something with those files first, such as committing or stashing, or just undoing them. By using reset --hard you effectively undid those changes yourself. If you had undone the pending changes first, the rebase command would have worked as desired.

Side Notes:

  1. After the rebase, it would be more accurate to draw your graph denoting the rewritten commits as E' and F' because they are in fact different commits with different states from E and F, even if the diffs between them and their new parents are the same as the previous diffs.
  2. When rewriting a branch such as main, if the branch was already pushed and is shared by other users, you may not wish to rewrite the branch. Another way to achieve the same ending state without rewriting the branch is by reverting the merge. We can't know which is best in your situation, but it's important to know the option exists.
TTT
  • 22,611
  • 8
  • 63
  • 69