0

I have a development branch that I accidentally merged into the Master branch before it was ready.

As I did this in error, I decided to revert the changes in Master so that none of my changes where there. This process created a Revert Commit that removed my changes from Master.

All was good I thought...

A week has now passed and I'm nearly ready to merge my branch back into Master. As there are other developers working on other branches, I decided to update my Development branch by merging any new changes from Master back into mine.

This process seems to delete all of my work.

Reviewing the history of what has just come from Master, it contains the accidental commit and the revert commit. This means that whenever I update my branch from Master, the revert commit is deleting the majority of my work.

How can I merge the changes in master back into my branch but without including the revert commit?

The only 2 possibilities I can think of are:

  • Go through every item in the Merge and attempt to manually remove the revert items (this I think would be impossible to do correctly and a complete nightmare to get right).
  • Copy all of the files I have made changes to locally, merge from Master and then attempt to copy/paste all the changes back into my branch (this would probably also be a nightmare to get right).

I'm hoping there is a better automated way to do this (I'm hoping for some sort of Merge but ignore certain commits process).

halfer
  • 19,824
  • 17
  • 99
  • 186
plingingo
  • 121
  • 2
  • 13

2 Answers2

6

So, your situation is this (time flows left-to-right):

--o--o--o---M--R--o   <- master
   \       /
    A--B--C--D--E     <- devel

You have merged commits A, B, and C into master, then reverted the changes in R, which is the inverse of A+B+C.

When you merge two branches (it does not matter which one you merge into which one), you will get everything from both sides since the last common ancestor.

In this case, the common ancestor is C, so you get R, D, and E. From devel's point of view, this looks like A, B, and C had been reverted, but from master's point of view it is as if only D and E had happened.

Solution 1: Revert the revert

You can revert R on master or an auxiliary branch that you derive from master. Then you merge them:

--o--o--o---M--R--o   <- master
   \       /    \
    A--B--C      R'   <- aux (git revert R)
           \      \
            D--E---M' <- devel

Now you can merge devel into master.

Solution 2: Rebase your branch

You rebuild you devel branch with new commits using git rebase --force-rebase:

--o--o--o---M--R--o   <- master
  |\       /
  | A--B--C--D--E     <- (abandoned)
   \        
    A'-B'-C'-D'-E'    <- devel

Now you can merge devel into master.

Solution 3: Hide the unwanted common ancestor (experts only)

You build this history temporarily:

--o--o--o---M--R--o   <- master
   \        
    A--B--C--D--E     <- devel

Do that by installing a graft:

git replace --graft M M^

This tells Git that commit M has only a single parent, M^. Now you can merge devel into master.

Finally, you restore the actual history:

git replace --delete M

[EDIT: use git replace instead of the deprecated grafts file.]

j6t
  • 9,150
  • 1
  • 15
  • 35
  • These are interesting solutions, but you have missed a part of my issue. After R on Master, there have been many other checkins from different branches. If I revert Master at the point that the Revert Commit was entered, I will lose all of the other changes that have been made. By having the Revert Commit included in my checkin, I lose all of my work. – plingingo Jan 22 '19 at 13:17
  • @plingingo Read and look carefully: I said "revert", not "reset". You won't lose any of these additional commits in any of the given solutions. (I've even drawn one of them after R as a representative.) Furthermore, when you branch off the auxiliary branch directly on top of R and create `R^` with `git revert R`, you get a new branch (`aux`), which you should merge into master or devel before you do the final merge. – j6t Jan 22 '19 at 13:43
  • I've tried what you have suggested and it isn't working. After R in Master there has been many checkins from other branches so if I revert R on Master then everything that was checked in after that is lost. What you have said makes sense if there is only 2 branches (Master and my Dev), so in your example, how do I add commits after R (from Master) into the AUX branch? – plingingo Jan 22 '19 at 13:57
  • @plingingo `git checkout -b aux R && git revert HEAD` (insert your commit id for `R`). Of course, branch `aux` does not have any of the additional commits now. But that is very intentional. Now you follow up with `git checkout devel && git merge aux`, and then either `git merge master` or `git checkout master && git merge devel`. In both cases you should see everything you did in devel and the additional commits from master. – j6t Jan 22 '19 at 14:18
0

Could you try git reset --soft to keep all local changes and then push it to master?

dunajski
  • 381
  • 3
  • 15
  • I can't push my code into Master without 1st updating my branch as otherwise merge conflicts would occur. This is why I want to update my local branch from Master and then I can check in my code into Master. – plingingo Jan 22 '19 at 12:15
  • Why do you want to merge changes from master before merge dev to master? Commit and push changes on dev then merge dev to master and checkout new dev from new (with your changes from dev) master. Do I misunderstand you or something? @plingingo – dunajski Jan 22 '19 at 12:43
  • You ask why I want to merge Master into my branch, this is probably just the way my company works as if there are any merge conflicts we want to fix them in our local branch before merging it all back into Master. Maybe I need to break policy here just for this issue and fix the conflicts in Master directly. – plingingo Jan 22 '19 at 13:19