30

The developer was commiting small changes to two files. But during this commit, he had a merge conflict which deleted a lot of stuff (probably didn't have the last up to date version). Then it was pushed to the shared repo and some other developers did some other commits.

Now, we noticed, that the merge deleted the important files, and we want to revert it back.
How can I do this without losing the changes from the next commits?

I was trying to git revert commitsha, but it didn't bring the changes back. Do I need to revert back the mergesha? How can I determine it?

Sridhar Sarnobat
  • 25,183
  • 12
  • 93
  • 106
Sfisioza
  • 3,830
  • 6
  • 42
  • 57

4 Answers4

44

git revert --mainline

Usually:

git revert --mainline 1 dd8cbe3e4

Where:

  • dd8cbe3e4 is the bad merge commit you want to undo, and
  • --mainline tells you which of the multiple previous commits is the one to restore (remember, a merge commit has multiple parent commits and you can only keep one of them).
    • I can't find a good explanation of what the 1 means, but my guess is that 1,2,3... corresponds to a list of mappings to the commits immediately before dd8cbe3e4, sorted by ascending chronological order (oldest first - which is usually what you want to revert to).

Source:

http://thezencoder.com/2013/09/05/how-to-correctly-revert-a-bad-merge-in-git/

Sridhar Sarnobat
  • 25,183
  • 12
  • 93
  • 106
  • 4
    I believe your statement about mainline is correct — docs: https://git-scm.com/docs/git-revert#git-revert--mparent-number – Mark Fox Aug 07 '17 at 20:49
  • --mainline works for me but -m does not. Gives me `fatal: bad revision '1'` – Noah Gary Dec 13 '19 at 18:07
  • Pretty sad that we have to guess what the 1 means. Regarding your guess: do you think chronological order is in terms of CommitDate, or in terms of AuthorDate? – Don Hatch Sep 29 '20 at 19:53
10

In short, WARNING: there is no real safe way to undo a merge except to actually reset the branch to the commit before the merge.

Let me explain and browse for an existing reference for now.

Quoting the linked answer from How do you revert a faulty git merge commit

Basically, reverting a merge will undo the data changes, but not the history (graph) changes. Therefore it is expected that reverting your faulty merge does nothing.

Certainly, resetting the branch would be the most simple approach, but it has drawbacks if the result of the merge has already been pushed to a shared repo (because you're effectively rewriting published history).

Here is the breakdown

  • git merge <someref> to merge (optionally commit after resolving conflicts)
  • If you find out right away that you want to reset the branch to before the merge:

    git reset HEAD@{1} 
         # optionally pass --hard to reset the working tree too
    
  • if you found out only later,

    • per-use the reflog to find the point before the merge. (HEAD@{1} is short for the previous value of the current head reference, but the reflog tracks a limited history of values for the head reference)

      git reflog
      
    • reset the branch

      git reset HEAD@{n} # substitute reflog entry index
      
    • optionally rebase/cherry-pick the commits done after the merge

      git cherry-pick HEAD@{1} # just an example. interactive tools will make this easier
      
Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Thanks sehe this was close enough! Of course it doesn't work due to some other errors. I am hoping, one day some team will create a version control system that doesn't need a separate master degree and developers can concentrate on development instead of spending days on figuring out a system that is made safe but as confusing as it can be. I dislike git since the first day I was using it and that didn't change since, it evolves though, will be good one day :( – Yatko Aug 08 '17 at 16:26
  • 2
    The problem with reset rather than revert is that if you’ve already pushed the bad merge and others have pulled it, it’s going to complicate life for them. Once you push, you should generally create compensatory new commits rather than removing commits. – Sridhar Sarnobat Nov 19 '17 at 21:39
  • That's the same issue as ever with changing published history. It's not a problem with reset, it's a problem with published history :) – sehe Nov 19 '17 at 21:41
  • Right, but what do you do after you’ve reset to the last good commit? It’s analogous to syncing clocks in distributed systems. You should never go backwards in time. – Sridhar Sarnobat Nov 25 '17 at 07:51
  • There is no place for "but" in your last message: it's just restating what you already said. That same point was in my answer ever since [Nov 30 '11](https://stackoverflow.com/revisions/8323171/2). I guess you can use this answer as a reason to (a) avoid merge, favour `pull --rebase` (b) never edit merge conflicts in the merge commit (c) don't bork merges (d) enable `git-rerere` – sehe Nov 25 '17 at 09:24
  • 1
    Yes you’re right, I didn’t read your full original answer (and others seeing it may not either). – Sridhar Sarnobat Jan 15 '18 at 09:47
6

Another (more safe) approach is to create a diff between the last good and the current version of the file and then restore the lost parts by copy&paste.

This always works, doesn't need any odd command line options, and it doesn't tamper with things that you should leave alone :-)

Eclipse, for example, has good tools to cherry pick each individual difference and copy it to either version. Just use the "Compare" menu to open both versions side by side.

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
1

In short, you can do a git reset --soft <commit> where commit can be HEAD^ (previous), HEAD~2 (current-2), a SHA, etc.

With --soft all the changes will be ready to commit, so you can actually change the commit. With --hard the changes will all be lost.

After you altered the commit you have to force push the changes to the shared repo with git push --force.

Note that you will need to tell the other developers that they should rebase their repos onto the shared repo. (use git pull --rebase). They could get some merge conflicts though... Please keep that in mind.

Hiery Nomus
  • 17,429
  • 2
  • 41
  • 37