The Git Rebase Introduction I Wish I'd Had

Max Antonucci on September 24, 2017

One of the most important (and confusing) git features in my new job was rebasing. Looking back now, the worst part was not finding a clear begin... [Read Full]

Very nice explanation. I will definitely use this one next time I have to explain rebase ;-).

However, what I was missing is a word on the trouble you can cause when rebasing already pushed commits.

The rebase itself technically removes your old commits and makes new commits identical to them, rewriting the repo's commit history. That means pushing the rebase to the remote repo will need some extra juice.

This has its reason. If there are several people working on the same branch, you are making changes that have not been pushed yet incompatible with the upstream branch for everyone else working on the branch. This means once you do this, everyone else has to pull with --force and reintegrate their changes (in the best case by rebasing themselves, in the worst case with much more complicated and tedius measures or having to recover "lost" commits from reflog if someone pulls with --force without care).

In the given example that clearly aims towards pull requests and single user WIP branches you might not get struck by this very often, but this can be a real problem.

Thus, my recommendation is always to use rebase as much as possible as long as you are not breaking the history for anyone else. This normally means that you shouldn't rebase commits you have already pushed or - if you do nevertheless - know exactly what the consequences are in your case and get consent of other people working on the branch if needed.

Very this. A simpler rule: just never use git push --force* except you're the one and only working on the branch and you're ready for consequences. I prefer periodical merges with master if the branch is already pushed, the history doesn't look as good as with rebase but OTOH it won't be messed up. Any usage of --force (both push and pull) can unexpectedly remove commits you don't want to be removed so just don't do it. Fortunately, it's relatively easy to restore them, read about git reflog.

That's pretty much the essence of what I wanted to say. Just wanted to point out that it is not the end of the world if you have to push with --force, you just need to know about the consequences and if it gets you into trouble.

But in general, I agree. In collaboration branches, you should not need --force in your all da work. I also prefer merging master (or an other parent branch) regularly to the topic branch and merging when ready with --squash and a proper message to remove WIP commits, but this is a topic that involves a lot of personal taste and philosophy, so discussion is difficult about this.

Thanks for the extra perspective! I work on many solo branch PRs myself so this kind of issue with rebases hasn't hit me that often. But there have been one or two moments where pushing rebased commits have caused issues with co-workers, and better communication with them was always how they would've been avoided.

That image halfway down was a game changer. Thanks Max!

Glad you liked it! Thinking up that cartoon actually led me to write this entire post haha

Great article and I love your drawings (and cupcakes :) )

I've had many arguments before on the concept of rebase from master vs merge into master as a first step, mainly due to the commit timeline continuity. I'm 100% rebase then merge.

I insist that my team (data scientists who are git newbies) rebase from (an immediately re-pulled) master - this way, any conflicts are kept in their own branches and not master, which is kept pure. Only when their branch has been rebased, conflicts resolved, tested and checked for a rebase again do we merge their code into master.

After a few time consuming sessions de-conflicting master they're starting to see the benefit in doing it the way I impose :D

Autosquash is my "one thing I've learned today" - thanks :)

Thank you! Glad you liked the drawings, and honored I could be the daily thing something learned today. I can't ask for much more than that :D (well I suppose I could ask for my laptop battery replacement to be covered by my warranty, but I try to stay realistic haha)

Another incredibly practical use of rebase is the following:

You have to start a new feature "CupcakePictures" that is based on another feature "CupcakeColoring" that your colleague is still working on (or that hangs in a pull request), i.e. this is about working in parallel.

Just branch off from that non-merged incomplete feature branch "CupcakeColoring" of your colleague and get working. Whenever he produces more stuff that you need, you just rebase onto his latest commit.

When his feature has been merged to master, you rebase on master too, and the history will - once you're done - wonderfully show how "CupcakePictures" was started after "CupcakeColoring" as the dependency actually required it (the parallel work will not show up and no cluttering will take place, like you would have by constantly merging his stuff).

This is also another good example of rebasing to prevent conflict! Thank you for sharing this. I haven't done any branch work off another person's branch work yet, so I appreciate the extra perspective. Also love how you kept with the cupcake theme :)

Holy mao, using rebase for merge and wasting time to refix the same conflicts, and teaching other devs to do the same, that's a no go for me.

If you have to write 2 pages on a simple task maybe something is wrong, git merge is for merges, rebase and push force is for playing with fire and rescue the lost kittens :))

It's common sense if you reached - - force you screwed something.

Note that if you don’t want to learn all the syntax required for autosquashing, or lookup and copy/paste the commit IDs, you can just commit the fix normally, and when you rebase interactively, manually move the line next to the commit to fix and change it to squash. It’s not necessarily more work.

That is very true. I didn't go into too much detail about what someone can do about interactive rebasing, but since I went into detail about autosquashing, I probably should've mentioned it's basically a shortcut for what can also be done in a normal rebase. But since I've found it to be simpler and require less VIM knowledge I wanted to highlight it above the other options. Thank you for pointing this out though!

I also agree about about trying some different "Explain X with Cupcakes" posts. Maybe I can also try it with other pastries, like pies and brownies!

I forgot to mention that I do all my interactive git stuff with nano instead of VIM. I find it sufficient for this kind of work.

Git has a built in tool to help ease the git churn during merge conflict resolution. Anytime a command pauses because of a conflict you can run git mergetool and it will open each conflicting file in the default conflict resolution application. See git-mergetool man page for more info.

Also the default editor that git uses is determined by your environment variable $VISUAL. It does not need to be Vim. For non-technical editing you could use nano or even your favorite GUI editor (millage may vary). For more information on this check out ${VISUAL}ize the future.

How can one apply these concepts to a Git Flow workflow? I'm a huge fan of Git Flow and we use it a ton at my office but usually when a feature is being finished, it's just a simple merge with the no-ff flag, so we have to do the checkout, pull, and deal with merge conflicts (when they exist) then anyway. I definitely see the utility of this command, but I don't know if it's super applicable in our context.

Also be warned: all this is done through a VIM interface, which is very useful and even more difficult to learn.

Not entirely true, just uses your system default visual editor. Which is $VISUAL || $EDITOR || vi IIRC.

Another small note to the past me from college: use Jekyll for your personal website. Don't waste your meager college budget on hosting a WordPress site...

medium.com is good too, for college guys

Also, I think it’s time to stop with these “Explain X like I’m five” posts, they’re out of fashion, and switch to “Explain X with cupcakes.”

I worked on a repo where one branch was used per project and all commits for all features were made to that branch, the process here was you commit your changes and then run a:

git pull --rebase

I have a basic understanding that this does the same thing as mentioned in the above article, except instead of the latest commit of another branch, this applies your commits to the latest commit on your branch, is this accurate or is there much more going on under the hood that I am not aware of?

I think it would be amiss to omit git mergetool from any rebate merge conflict workflow. Installing kdiff3 or p4merge will typically add their necessary lines to your global gitconfig.

Love the drawings. The most confusing part when I was learning was the "rebase from" ... there are a lot of assumptions about this on other explanations.

One thing I never did that you have in your steps is the conflict resolution workflow. When I rebase and have conflicts, I never use commit as its not needed. Here is my workflow:

git rebase master
[ ... fix conflicts ]
git add [specifically what I need to add]
git rebase --continue

boom

Now I'm wondering if I've caused myself any issues by never using commit before I continue...

I'm actually not too sure about this myself. I think I've done a mix of conflict resolutions with and without that specific commit step, but the end result was basically the same. The one difference was I got an extra chance to rename the commit before continuing. I wrote it as resolving it like a normal merge conflict since in the Github documentation I read, that's how they emphasized it, so I borrowed their phrasing a bit.

I don't believe it's a cause for any serious concerns though. If anyone else can clarify this though please share! I did some research here too but couldn't get many clear answers.

Hi Max, very nice explanation. I may be wrong but didn't you miss a step? Merging back to master to push the changes? Because the rebase that you do happens in your local personal branch. Your commits still need to go in the master and then merge and push upstream?

That is true, but I felt it went a little outside the scope of the guide. I wanted to focus more on the rebase itself. I felt that merging a branch back to master was a separate kind of tast - one closely connected to rebasing, yes, but still something separate.

Any comments about -onto option of the rebase command?

You made my day with autosquasing, super nice!!

This is wonderful! I was currently struggling to figure out what rebasing was all about. But now I think I got a pretty good grasp on it. Thanks a lot!

I’ve been rebasing and squashing for years with joy, and I just learned of autosquashing today. Thank you Max.

got commit —amend also much convenient

Thank you, this article has some really useful explanation. Plus I learnt about --fixup that I'm sure I'll use a lot!

This is the clearest explanation of rebasing I've come across yet --- thanks a million!

I once held discussion on this topic with fellow contributor to the repo. He told me about practice where instead of rebasing on top of dev branch with feature branch one should merge dev into feature branch and then merge feature into dev.

Any ideas on this approach and rebase approach?

Hey ! Can you please draw the git rebase -i --autosquash master command ? I don't really get it.. Thanks !

Exactly the git resabe intro I wish I would had! nice article!

Where have you been all my life??? This article was pretty helpful to me.

Bookmarked for later reference. Thank you!

Great write up. I love your pictorials. Nice take. Gonna steal your idea 😜😜

Thanks for writing this article, it's a important part of git!

80% of the verbiage need to be cut.

code of conduct - report abuse