Reverting the revert

As a reminder to myself and maybe to help others too, try to avoid reverting a merge if you plan on merging it some later times !

Let's consider an example in order to understand why this could be a problem. First create a repository:

mkdir repo; cd repo; git init
echo 'master-file-1' > master-file-1
git add *
git commit -a -m 'master-file-1'

# master --o--H

Now a new branch branch is created and two files are added to it

git checkout -b branch
echo 'branch-file-1' > branch-file-1
echo 'branch-file-2' > branch-file-2
git add *
git commit -a -m 'branch-file-*'

# master --o--H
#           \
# branch     o--H

At this time the branch branch is (supposingly) ready to be merged with master

# now comes the merge (M)
git checkout master
git merge branch

# master --o------M--H
#           \    /
# branch     o--o

However for some reason the merge needs to be reverted (issue with master, unstable, not the right time, etc)

# now comes the revert (W)
git revert <commit>

# master --o------M--W--H
#           \    /
# branch     o--o

This is not directly an error, but it could become an issue if you don't realize what was exactly done here. A revert will actually create a new commit that is the opposite of what was done by the merge, i.e. removing the two files that were committed in the branch branch. Therefore the master branch's history now contains a commit that says that the two files branch-file-1 and branch-file-2 have been removed. This commit is more recent that the one that actually added the files in the first place. Thus if those file are not changed, they will never be added in a future merge.

Let's see that with an example. One of the two files is updated in the branch branch

# update branch (C)
git checkout branch
echo 'some change' > branch-file-2
git commit -a -m 'update'

# master --o------M--W--H
#           \    /
# branch     o--o-------C--H

And the new version of the branch is merged

# and re-merge (Y)
git checkout master
git merge branch

# master --o------M--W---Y--H
#           \    /      /
# branch     o--o------C--H

Now two things are happening, first a conflict arises between the two versions of the branch-file-2, the one in master that was deleted and the one from branch branch that was modified. That is expected and needs to be fixed. The second issue, which is more problematic, is that the file branch-file-1 does not at all (re-)appear in the master branch. It makes sense since for the master branch it has been deleted in a commit that is more recent than the one that actually created it but it might not be directly expected without having understood exactly what the first revert did.

How to fix this then and actually have all changes from branch branch being merged into master ? Well the revert (W) needs to be reverted first and then the whole branch branch re-merged in Master.

# revert the revert (X) and merge (N)
git checkout master
git revert <revert-commit>
git merge branch

# master --o------M--W--X---N--H
#           \    /         /
# branch     o--o---------C--H

That's it, now all changes/files brought by branch branch are in master.

References: