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