DEV Community

loading...

Rebase and Merge Don't Mix

Jesse Phillips
Senior Quality Assurance (SDET) ¶ Avid hobby D programmer ¶ Telling people what to do because I am right.
・2 min read

When working with git it is possible to utilize both rebase and merge functionality. These two can live harmoniously within the same repository history, but things get messing if you try to mix them together.

This is a mistake made when working with remote repositories and local repositories while attempting to follow a rebase type workflow.

Remote
  M---R---Q
 /
A---B---C
Enter fullscreen mode Exit fullscreen mode
Local
  M---R---Q (foo)
 /
A---B---C (origin/main)
Enter fullscreen mode Exit fullscreen mode

Here I show that Local and Remote are in the same state. If you try to do any sync related to the remote and local git won't take any action, "Already up to date."

git switch foo
git rebase origin/main

Local
  M---R---Q (origin/foo)
 /        M'---R'---Q' (foo)
/        /
A---B---C (origin/main)
Enter fullscreen mode Exit fullscreen mode

Now I'm going to work off the local repository since that is an accurate depiction of what git works with.

In this case I have rebased the branch foo onto the main branch, new commits are created, but the remote still has the same old commits.

git pull origin

Local
     M-----R----Q (origin/foo)
   /             \
  /               \
 /        M'-R'-Q'-Merge (foo)
/        /
A---B---C (origin/main)
Enter fullscreen mode Exit fullscreen mode

Unless you have changed your configurations with

git config --global pull.rebase true

The use of pull will actually perform a merge for the remote branch foo. This is by no means desired. It results in merge conflicts and messy history with commits which duplicate each other, it is excessively bad when you've attempted additional commits on the local branch and you just want to get them out to the remote.

git push --force-with-lease origin foo

This is the most correct way to work in a rebase workflow. You own the branch and you can rewrite history and force push the changes out.


In general you should only be using rebase within a branch you own. However that does not mean that you can't collaborate within a rebase branch. Most optimally you would want to transfer ownership and only when you own the branch do you do any work on it.

When you take ownership of a branch:

git fetch origin foo
git reset --hard origin/foo

However git can handle commit divergence if the commit content has not been modified (--fixup, --squash).

Local
  M---R---Z (origin/foo)
 /        M'-R'-Q (foo)
/        /
A---B---C (origin/main)
Enter fullscreen mode Exit fullscreen mode

Going back a little bit, lets say the remote had Z added and locally Q was created. You've rebased to main but don't have the Z commit. Sure we could:

git cherry-pick origin/foo

And there are probably even more fancy commands to handle multiple commits desired. But here is just one simple way to get the commits you want and still end up on main

git switch foo
git rebase origin/foo

Local
   M---R---Z (origin/foo)
  /         \
 /           Q' (foo)
/
A---B---C (origin/main)
Enter fullscreen mode Exit fullscreen mode

After moving onto the remote updates, bring it back to main.

git rebase origin/main

Local
  M---R---Z (origin/foo)
 /        M'-R'-Z'-Q' (foo)
/        /
A---B---C (origin/main)
Enter fullscreen mode Exit fullscreen mode

Now when the branch is force pushed, the changes from both sides still remain within the history and everything is linear.

Discussion (0)