If you haven’t already, take a look at my first
git rebase / Pikmin blog post about how to learn and use
git rebase. That post covers the basics, while this one will conceptualize how to use
git rebase when things go bad: conflicts, force pushing, and working with a lot of development churn.
git rebase is a real treat when there are no conflicts, but that’s often not the case when you’re working on a fast-moving development team or contributing to an open source project. There’s a good chance you will be working on the same files, at the same time or your work lags behind what the open source project is doing.
This increases the likelihood for conflicts. Throw in different branches, different merge strategies, and the different complexities of coding in general. But hopefully, these adorable Pikmin can help you traverse the minefield.
Let’s start a small example so you can see how conflicts can occur and then how to fix them. In this example, I want to bump the version of a file in the
tccutil repo because of a change I’ve made.
It’s a one line change so this is a perfect example to show how merge conflicts can happen and how to fix them. I add and commit my change.
I now have a unique commit that does a unique thing (this is the Pikmin in the analogy as you can see in the image below).
This is a friendly way to help conceptualize commits; it's why I wrote the first blog post--to help conceptualize that:
- each commit is unique and does a unique thing
- each pikmin is unique and does a unique thing
- you can modify each commit to do something else if you need
- you can modify each pikmin to do something else if you need
- you can rearrange commits to any order you need
- you can rearrange pikmin to any order you need
Now back to the example: I haven’t
pushed my branch yet, and everything is local to my machine right now. But it’s been a few weeks since I pulled in any upstream changes, so now I want to
rebase my changes into the latest code produced in the
In other words, before I make a pull request, I want to make sure I have the latest code produced by someone else.
You can do a
git pull and
git merge here, but many repos will have you rebase onto a branch before accepting a PR. And if you have to do several merges over time, it's just kind of messy. I have found it much easier to visualize each commit as a consumable piece of code developed by someone. Rebasing puts your commits right on top of someone else's up-to-date commits. Because of this, I pretty much always use
git rebase when developing.
Since my local repo is out of date with the upstream repo, I:
- switch to (my local)
masterbranch (this is the branch I want to target my PR against)
git pullin the latest changes
- switch back to my local development branch
rebasemy branch onto
git checkout master git fetch && git pull git checkout more-verbose-error git rebase -i master
I'm then thrown into
vim, which shows my single commit,
7783be6. It also shows the latest commit that was pulled in.
I only have one commit and I want to keep it, so I just
:wq my changes in and then the rebase process continues.
But uh-oh! A conflict!
My yellow pikmin (commit
7783be6) could not be applied on top of
master (blue pikmin). These conflicts happen when the line(s) you edit are the same line(s) as those edited by someone else.
In other words, this conflict means someone upstream edited the same line in the same file on the branch I’m trying to rebase onto. Since
git doesn't know which piece of code is correct, the
rebase halts and asks for human intervention.
Here is that error message broken down a bit:
gitis attempting to auto-merge the delta of changes
CONFLICT (content): Merge conflict in tccutil.py: There's a conflict in the content of this file (you can have conflicts where someone removed the file that you are editing, which is not the case here)
error: could not apply 7783be6...update error and bump version: this shows my commit (the yellow pikmin) and my commit message
Resolve all conflicts manually. Mark them as resolved with git add/rm...: this tells you go look in the file(s) in that commit, fix them,
git addthem, and the
git rebase --continue. Essentially, you can't do anything here until the conflicts are resolved.
git can’t determine which code is correct, it’s up to us humans to figure it out. To do this, you need to edit the file in question.
git puts in some markers so you know where you need to make changes.
So I open
tccutil.py and I start looking for something like this:
<<<<<<<< HEAD some code from someone else ======== my code >>>>>>>>
This sometimes-confusing looking chunk of code is what you need to fix. You should see:
<<<<<<<< HEADindicates the start of the upstream code (their changes)
========indicates that our changes are below
>>>>>>>>closes out the beginning marker (nothing else past this marker needs to be modified (unless there is another start marker
I opened my file in
vim and found the offending lines (116-120).
Line 117 is the upstream change (what's currently in
master). Line 119 is my change. It's now up to you, the human, to determine the best course of action.
This conflict is a little easier to see if you have a modern text editor with built-in
git functionality, as they often come in with fancy buttons and colors to help you fix the conflict a bit easier:
If you were logged into Github (and had pushed your branch), you would see a Resolve conflicts button,
which shows the same thing you see in your text editor of choice:
Now as I mentioned, it's up to you the user to fix this conflict. Determining a simple
Whatever method you decide to use (
vim, fancy text editor, Github, etc.) you need to make sure to remove all three markers:
and save your change before
adding your file.
I tend to use the editor at this point because I like how the fancy colors help distinguish what I need to be looking at. In this instance, I have decided that I liked parts of both pieces of code, so I make a completely new sentence.
I could have just easily chosen to use their code, or my existing code (either by clicking "Use this" or editing it in
vim and deleting everything except my code.
I have saved my file with the change above. I then enter
git add tccutil.py git rebase --continue
If there were other files to address, it would move on to the next, but since we just had the one, the rebase process completes.
Now the interesting part is that you have a brand new commit. You would have a brand commit even if you had left chosen to keep your existing code (i.e. not modifying the message like I did). This is because you have an entirely new set of commits then what you previously had. So no matter what, you'll always end up with a new commit. So even though I used the same code, it still shows up as a new commit (
This becomes problematic when it comes time to push your code.
If you attempt a regular push, it will reject it (with a deceptive message). Despite it saying you need to do a
git pull, don't. That will complicate your life considerably. Instead, you need to do a
git push --force, or
git push --force-with-lease. The latter is less likely to cause problems if you are working with other people and other teams. But in many cases, you'll be on your own branch and you just need to update it before making a PR. So pushing with
--force is fine.
Not only fine, force pushing is the only way push your code after a
This part can be quite scary and turns a lot of people off to the
rebase command. And with good reason: you are essentially re-writing the history of commits. If you mess up and change or delete some commits and then force push it, those previous commits are just gone (unless you had a backup or someone else's local branch still had them).
But when you successfully rebase onto a new branch, you can easily test your commit(s) on new code that someone else is pushing. This is often useful if you are working on a feature, new code is added to development, and you want to keep up-to-date with it.
There are a million and one different strategies for working with
git and one workflow doesn't fit all. This isn't the only way to use
rebase, but hopefully you have learned a few tips to help you use it in a way that works for you.
Feel free to leave comments or questions.