4

We maintain a branch structure with a merge workflow. As a result our master branch commit hierarchy looks like this.

Expected hierarchy

However, since we use Gitlab community edition which doesn't squash and rebase merges, sometimes developers forget to do one or the other and merge commits which look like this.

Bad merges

The usual way I follow to recover from this is to reset master to the last good commit before these merges and then replay every merge (after rebasing and squashing correctly) to merge them.

A better way may be to use the git rebase --onto command to replay the good merges after the bad merges have been fixed. However, I have been unable to figure out the correct way to do this. What I do is:

  1. Reset master branch to the commit for the last good merge.
  2. Rebase bad commit (not squashed-rebased) on top of the rebased branch.
  3. Merge the squashed-rebased commit on the rebased branch.
  4. Repeat the process for any other bad commits.
  5. Finally replay all good merges in the same way as in steps 2-3.

I tried using git rebase -ip --onto <last_good_commit> to replay my good merges in step 5 but that seems to exchange the order of parents for the commit. Anyone have a good way of improving my workflow so that I don't have to replay every single merge manually?

proteus
  • 467
  • 4
  • 21
  • I would probably do something similar to what you already suggest; taking your second image as an example I would reset to where the green branch is merged in, then checkout the first red branch and rebase that, then merge it again, and so on for the following branches. The reason I would do it the "slow" way is the risk of conflicts (semantic or syntactic) -- I would want to inspect the result of each step either way. – jsageryd Mar 31 '18 at 19:48
  • 1
    Have you considered simply *not* doing any of that stuff? Git is able to handle this situation just fine and as long as you at least *try* to be disciplined so that it doesn't get out of hand, do you really need to fix anything at all? The reason I ask is that if you are strict on the rebase scheme, why do you have merges at all, and not just a linear history? – Lasse V. Karlsen Apr 04 '18 at 07:09

2 Answers2

1

git rebase -ip --onto <last_good_commit> seems incomplete.

It should be:

git rebase -imp --onto <last_good_commit> <first> <yourBranchToReplay>

If you have checked out "yourBranchToReplay", you can omit that last parameter.
But you cannot omit <first>, which is the first commit of the branch to replay you want to put on top of <last_good_commit>

As noted by the OP, -m (--merge) is important.
I also documented the -p --preserve-merge option of Git 1.8.5 back in 2013

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • Without the -m flag, it wouldn't preserve the merges and would flatten the commits which is not something I want. – proteus Apr 04 '18 at 02:06
0

The answer by @VonC was almost correct. I had to modify the rebase command as git rebase -ipm --onto <last_good_commit> <first> <yourBranchToReplay> and it rebased all remaining merges correctly on the fixed merges.

proteus
  • 467
  • 4
  • 21