I work in a git repo where we maintain a master and a develop branch. Changes are made in develop and merged into master before releases. Today I accidentally merged master into develop and pushed the result to develop. A git log
in develop now shows the merge develop into master
commits. I need to fix this somehow. I found the most recent good commit using git reflog
. What is the correct approach to revert to it?
Since I have already pushed, I would like to avoid rewriting history. However, I'm not sure that I can use git revert
. I'm not convinced that the "revert a faulty merge" howto and other SO questions apply since the commits that I want to revert are merges themselves (https://www.kernel.org/pub/software/scm/git/docs/howto/revert-a-faulty-merge.txt).
simply run git reset --hard to revert all those changes.
You can use the Git reset command to undo a merge. Firstly, you need to check for the commit hash (or id) so you can use it to go back to the previous commit. To check for the hash, run git log or git reflog . git reflog is a better option because things are more readable with it.
How do I cancel a git merge? Use git-reset or git merge --abort to cancel a merge that had conflicts. Please note that all the changes will be reset, and this operation cannot be reverted, so make sure to commit or git-stash all your changes before you start a merge.
You can undo a Git merge using the git reset –merge command. This command changes all files that are different between your current repository and a particular commit. There is no “git undo merge” command but the git reset command works well to undo a merge.
You can use git revert
to revert the merge. Just be aware that when you do that, you make a future "re-merge" more difficult. Read that message you linked closely—it says a lot about how to make the future "re-merge" work—but be aware that it's talking about merging develop
into master
, and you said you did the opposite, merged master
into develop
:
A -- B -- C -- D -- E <-- master
\ \
F - G - H - M <-- develop
In this case (having merged master
into develop
instead of the other way around) you can choose a number of different options, all with advantages and drawbacks...
(0) Do nothing. If merging in C
and D
doesn't break the develop
branch, just leave it that way. Later, someone will git checkout master
and git merge --no-ff develop
and get this (let's say one more commit I
got added to develop
first):
A -- B -- C -- D -- E -- M2 <-- master
\ \ /
F - G - H - M - I <-- develop
Here merge
finds what's been done in develop
since it split off from master
, which was at B
. So it puts in F
, G
, and H
, skips any parts of M
that came off master
(probably all of it), and finally puts in I
and makes merge-commit M2
(because of commit E
; but if E
were not there, it would be because I used --no-ff
too).
(1) Just "rewrite history": erase commit M
from branch develop
. Alert everyone else who uses the branch that this is about to happen: that commit M
is going away and they should take whatever measures they must, to handle this.
(2) Stop using the old name develop
, make a new branch name develop1
or whatever:
A -- B -- C -- D -- E <-- master
| \
| M <-- develop
\ /
F - G - H <-- develop1
This is the same as option (1) except that the commit M
is still there, along with the branch label on it, and you have a new/different branch label pointing to commit H
. You still need to alert everyone, but it might make their jobs easier. You can delete develop
later, when everyone is good with that.
(3) Revert the merge. As in the linked article, let's use W
to represent the new revert commit:
A -- B -- C -- D -- E <-- master
\ \
F - G - H - M - W <-- develop
What's in W
? Whatever it takes to "erase the effect" of the merge. In this case, that means, changes that un-do whatever was done in C
and D
. So far so good, develop
has just the changes from F
, G
, and H
, again. The problem occurs later, if/when someone does:
git checkout master
git merge develop
The second command goes rooting around to find where master
and develop
were split, i.e., commit B
, so it picks up all the changes since then. Which at this point include those in W
, which will un-do C
and D
, not at all what you wanted. It can be fixed-up afterward, but whoever does the merge has to know about this "delete C
and D
" time-bomb waiting for them.
Note that this is the same merge as in the "do nothing" option (0). The issue here is the contents of W
. In case (0), you want the contents of commit I
, but here you don't want the contents of W
.
This is arguably the worst option of all, given what's shown above, but I'll list it anyway:
(4) make an all-new branch with copies of the commits you want to keep, and name it develop
. (Or name it something else, but then we're back to the develop1
-like situation above, and you should probably just use that one.) Whether you keep the old-develop
branch, or just abandon it (by not labeling it), is up to you, but I will draw it in:
F' - G' - H' <-- develop
/
A -- B -- C -- D -- E <-- master
\ \
F - G - H - M <-- old-develop
This is described in the link you included. It's only really useful in more complex cases.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With