Rebase and Merge Don't Mix
Tags: git, rebase
Published: 2021-08-04 04:20:44

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
```

```
Local
  M---R---Q (foo)
 /
A---B---C (origin/main)
```

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)
```

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)
```

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)
```

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)
```

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)
```

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