Home > Back-end >  Git Rebase from a branch with Squashed merges
Git Rebase from a branch with Squashed merges

Time:10-30

So say I have commits my dev branch,

a
b
c

I do a pull/merge request to the main branch, with squash merge option turned on.

So the main branch now looks like,

merge from 'dev' to 'main'
squash: a, b, c

But my source branch dev remains to be three seperated commits. And that could be a problem when I do git rebase main on dev branch, especially when the main branch is stuffed with other developers' squashed merges.

Normally, I would cherry-pick my ahead commits onto a dev_bak branch. delete my current branch dev and republishes it by doing

git switch [any-branch]
git branch -d dev   // delete dev branch
git checkout -b dev // re-create dev branch
git rebase main     // do rebase
git push --force    // force push to remote to overwrite

And cherry-picks my ahead commits back onto the dev branch.

So I was wondering if there is a quick way of doing this? Perhaps git rebase --force ?

Thanks!!

CodePudding user response:

First, let's visualize your scenario:

o---o---o-----S (main)
     \
      A---B---C (dev)

Commit S is the result of squashing A, B and C into a single commit and applying it to the target branch (a.k.a a "squash merge").

Now, the problem you're referring to is due to the fact that S is unrelated to the original commits A, B and C even though it contains the same changes. Rebasing dev on top of main would result in merge conflicts as Git tries to reapply the same changes on top of the squashed commit.

The command you're looking for is reset with the --hard option:

git switch dev
git reset --hard main

This will move the dev branch to point to the same commit as main:

o---o---o-----S (main, dev)
     \
      A---B---C

Commits A, B and C will now become unreachable and will eventually be deleted.

Note that the --hard option will also synchronize the files in your working directory to match the commit you're resetting to. This means that any uncommitted changes in your working directory will be discarded when you do this. Make sure to stash them or commit them to a temporary branch before doing git reset --hard.

CodePudding user response:

Your best option is don't use squash merges for a branch you're not deleting. A squash merge throws away the history on a branch; it only makes sense when you're about to delete that branch anyway. For long-running branches, it is better to create a merge commit, so that the relationship between branches is recorded.

Because a squash merge doesn't record which commits were merged, you will need to track that manually if you carry on using the branch.

If you visualise the commit graph you have and what you are trying to create, then you can work out what rebase command to use - it's not about "forcing" the rebase, it's about telling git what commits you want to keep.

Let's say we start with this:

... <--A <--B <--C <--D <--E <--F
                 ^              ^
               main            dev

Then you squash-merge dev to main (commit DEF), and carry on committing to dev:

... <--A <--B <--C <--D <--E <--F <--G <--H
                  \                       ^
                   <-- DEF               dev
                        ^
                      main

What you want to end up with is this (commits GR and HR recreating the changes of G and H, respectively):

... <--A <--B <--C
                  \                      
                   <-- DEF <--GR <--HR
                        ^            ^
                      main          dev

git rebase has a three-argument form which I read as "rebase commits from old_base to tip onto new_base": git rebase old_base tip --onto new_base (some prefer to put the --onto new_base first, it really doesn't matter).

So in this case, we want to "rebase commits from F to H onto DEF" (because F is the last commit that we squashed):

git rebase F H --onto DEF

Since we have some branch pointers, we only need to look up the commit hash for F, and can write:

git rebase F dev --onto main
  • Related