14

I have a dev branch which already merged with branches featureA and featureB. I merged that dev branch to master and pushed to remote. Later I identified featureB is not yet ready to merge with master as there is a faulty commit. So I revert that merge of dev --> master.

git revert -m 1 <merge-commit-hash>

Then I am trying to merge featureA to master. But I cannot merge. It will say Already up to date.

I know the reason as Linus Torvalds said,

Linus explains the situation:

Reverting a regular commit just effectively undoes what that commit did, and is fairly straightforward. But reverting a merge commit also undoes the data that the commit changed, but it does absolutely nothing to the effects on history that the merge had.

So the merge will still exist, and it will still be seen as joining the two branches together, and future merges will see that merge as the last shared state - and the revert that reverted the merge brought in will not affect that at all.

So a "revert" undoes the data changes, but it's very much not an "undo" in the sense that it doesn't undo the effects of a commit on the repository history.

So if you think of "revert" as "undo", then you're going to always miss this part of reverts. Yes, it undoes the data, but no, it doesn't undo history.

In this situation only solution to merge that dev again to master is revert the revert commit. But I only want to merge the one branch included in dev branch, that is featureA

How can I merge the branch (featureA) to mainline branch (master), that included in reverted merge branch (dev) ?

Mohammed Safeer
  • 20,751
  • 8
  • 75
  • 78

1 Answers1

1

You should not do a revert of the whole develop to master merge here, but do a revert of the 1 specific branch instead. In your case it should be a revert of featureB to develop merge.

If your revert cannot be undone, simplest solution is to revert it back and do a new revert of featureB to develop merge only. Otherwise, I would recommend to get rid of the initial revert and follow the explanation below.

Let's draw what happens here according to the description:

  master   a------------------M3
            \                /    
     dev     b-------M1-----M2
             |\     /      /
featureA     | c---d      /
              \          /
featureB       e--------f

here M3 is merge commit you're trying to revert by:

git checkout master
git revert -m 1 <M3-commit-hash>

If you did exactly what you said, by the command above you doing revert of the whole dev to master merge commit M3, which will produce revert commit and will put it after M3 in timeline:

  master   a------------------M3---rvM3
            \                /    
     dev     b-------M1-----M2
             |\     /      /
featureA     | c---d      /
              \          /
featureB       e--------f

where rvM3 is the revert commit of the whole dev branch merge.

Then you're trying to execute:

git checkout master
git merge featureA

Now let's imagine the picture of expected result:

  master   a------------------M3---rvM3---(M4)
            \                /            /
     dev     b-------M1-----M2           /
             |\     /      /            /
featureA     | c---d----- / -----------         
              \          /          
featureB       e--------f

If take a look carefully on the last view we can found that commits c and d from the featureA are already a part of the master branch after M3 merge and they are reachable from M3 by the parent links.

You may expect that M4 with commits from featureA will be created, but it does not happen, since to Git there is nothing new to add from the featureA branch to master (from the commit perspective) as it was already merged before. It doesn't matter that in fact code changes from the featureA are not presented in master anymore as Git cares about commits only, not the changes itself and does not care what happens to them in the future commits.

Having that in mind we can come to a simple solution — what you really need is not a revert of whole M3 merge, but instead a revert of M2 only and picking the branch with M1 as revert destination for merge commit (assuming it has index 1 in the command):

git checkout master
git revert -m 1 <M2-commit-hash>

Which will do exactly what you wanted and you will end up with something like that:

  master   a------------------M3---rvM2
            \                /    
     dev     b-------M1-----M2
             |\     /      /
featureA     | c---d      /
              \          /          
featureB       e--------f

where rvM2 holds changes from the M2 discarding ones that came from e and f commits.

Generally, every time we want to revert something it's better to be as specific as possible. I.e. if we want to revert 1 commit, we need to execute revert only for it, not the whole branch holding it, and if we want to revert 1 specific branch — we should not revert composite merge of several branches, like you did in M3 initially.

mmelnik
  • 583
  • 5
  • 12