DEV Community

Cover image for The Git Rebase Introduction I Wish I'd Had

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 beginne...
Collapse
 
vincecampanale profile image
Vince Campanale

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

Collapse
 
maxwell_dev profile image
Max Antonucci

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

Collapse
 
yssybyl profile image
Dr Janet Bastiman

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

Collapse
 
maxwell_dev profile image
Max Antonucci

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)

Collapse
 
oxygen0211 profile image
Julian Engelhardt • Edited

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.

Collapse
 
rkfg profile image
rkfg • Edited

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.

Collapse
 
oxygen0211 profile image
Julian Engelhardt

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.

Collapse
 
maxwell_dev profile image
Max Antonucci

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.

Collapse
 
jessekphillips profile image
Jesse Phillips

A caviot to this, as long as commits aren't edited, both parties can rebase a shared branch and git will identify the existing commits.

Collapse
 
cheppsn profile image
Kaspar von Gunten • Edited

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

Collapse
 
maxwell_dev profile image
Max Antonucci

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

Collapse
 
oscherler profile image
Olivier “Ölbaum” Scherler

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.

Collapse
 
maxwell_dev profile image
Max Antonucci

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!

Collapse
 
oscherler profile image
Olivier “Ölbaum” Scherler

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.

Thread Thread
 
unixnut profile image
Alastair Irvine

This can be as easy as putting export VISUAL=nano in your .profile .

There's probably also a setting you can use with git config.

Thread Thread
 
oscherler profile image
Olivier “Ölbaum” Scherler

I export EDITOR=nano (never heard of VISUAL before,) and in my .zshrc, but yes.

Since then I’ve switched to vim, though. I learned to do just what I did in nano (navigate with the arrow keys, type, cut and paste entire lines, search, save, and quit,) and the rest I’m learning as the needs occur. The first one being :! sudo tee % to save when you forgot to open the file as root. :-D

Collapse
 
bgadrian profile image
Adrian B.G.

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.

Collapse
 
jmccabe profile image
John McCabe

Personally, I think rebase is an exceptionally useful tool however it's potential for misuse is great.

From my point of view, rebase should only be used on stuff that hasn't been pushed to a [shared] remote. There is a useful article here - daolf.com/posts/git-series-part-2/ - about that.

Consider the case where multiple developers are working on a single feature. The feature is in branch feature/j207, which was taken from branch develop. Potentially each developer could have their own branch off feature/j207, do some work, then do merges etc, but they might actually just all work directly from feature/j207, which is where rebase comes in. Let's just consider two developers for the moment, Bob and Jim.

1) Bob and Jim both clone the repo and checkout -b feature/j207
2) Bob makes a change and commits locally
3) Jim makes a change and commits locally
4) Jim makes a second change and commits locally
5) Bob makes a second change and commits locally
6) Bob pushes to the remote branch
7) Jim makes a third change and commits locally
8) Jim tries to push to the remote branch, but it's rejected due to Bob's changes having gone in

At this point Jim has two main options:

1) merge from the remote, e.g. use git pull, auto-merge, sort out conflicts across multiple commits, commit and push
2) rebase from the remote, e.g. use git pull --rebase, sort out conflicts on a commit-by-commit basis with git rebase --continue then, once complete, push

My preference is for that latter.

First of all, you avoid a merge commit that says (assuming your developers are what would appear typical in my experience) "Merge branch feature/j207 on blah into feature/j207". How useful is is it to know that you merged from a branch into another branch, in some unknown place, with the same name?

Secondly, you make the history of the branch linear, rather than having multiple branches, with the same name, being displayed on the graphical representation. As other people have mentioned, doing it this way also makes git bisect work better (I believe) when trying to track down bugs that may have been introduced during this process.

In this way you're only rewriting your local history that affects no one, other than yourself.

What's not so acceptable is rebasing feature/j207 from the current state of develop then pushing it. There's no reason why this should NEVER be done, but it's got to be done carefully, and not while multiple people are working on feature/j207.

So, in summary, there are circumstances where rebase is appropriate, and others where you need to think carefully about it.

Collapse
 
olesteban profile image
olesteban

Well, rebase is a nice tool to use, specially if you haven't pushed your branch.
About conflicts, in the rare cases where they would be recurrent, git rerere can help a lot.

Collapse
 
dividedmind profile image
Rafał Rzepecki

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.

Collapse
 
sukima profile image
Devin Weaver • Edited

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.

Collapse
 
glutnix profile image
Brett Taylor

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.

Collapse
 
devdevcharlie profile image
Charlie Gerard

Thanks so much for this awesome explanation! That rebase-cupcake cartoon is the best! :D

Collapse
 
dstalor profile image
Daniel Tal Or

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.

Collapse
 
seokjeon profile image
Se-ok Jeon

Thx for this! This is really what I wanted. Helped A LOT.
Can I translate in Korean this article? If you don't mind, I wanna share this awesome information in Korean. Surely, There will be a link directing to this original one.

Collapse
 
maxwell_dev profile image
Max Antonucci

Sure thing! Go right ahead as long as you credit the original to the link on my personal site here: maxwellantonucci.com/posts/2017/09...

Collapse
 
oscherler profile image
Olivier “Ölbaum” Scherler

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

Collapse
 
shayd16 profile image
Shayne Darren • Edited

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?

Collapse
 
unixnut profile image
Alastair Irvine

I think this works like it "hides" all your commits since the last common one, does the pull, then reapplies all your commits (with different IDs) onto the last pulled commit.

Collapse
 
sunflower profile image
sunflowerseed

are you sure we need git push --force? Because I have done quite some rebase and I didn't need to do a --force, and usually a --force is considered fairly ... forceful and best to be avoided.

Another question is... you mentioned all things your coworkers did and you don't want to lose those and therefore you want to rebase... but you could very well merge as well.... did you mention something about why not just do a merge but rebase instead?

Collapse
 
marcussk profile image
Marek Beňo

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?

Collapse
 
itsjzt profile image
Saurabh Sharma • Edited

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

Collapse
 
decadef20 profile image
Decade

got commit —amend also much convenient

Collapse
 
fagianijunior profile image
Carlos Fagiani Junior • Edited

I'll tattoo this post in my front.

Collapse
 
zacharyputhoff profile image
Zachary Puthoff

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

Collapse
 
joespinelli7 profile image
Joseph Spinelli

Had to create an account just to thank you for this post!! I've had a hard time grasping the rebase workflow and this article really helped me put it all together. Just rebased and pushed by myself for the first time working as an entry level dev, thanks to you!

Collapse
 
shackra profile image
Jorge Araya

Any comments about -onto option of the rebase command?

Collapse
 
sandordargo profile image
Sandor Dargo

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

Collapse
 
mgtitimoli profile image
Mauro Gabriel Titimoli

You made my day with autosquasing, super nice!!

Collapse
 
heyharold profile image
Harold Nyikal

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

Collapse
 
whitetire67 profile image
Paul Dailey • Edited

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

Collapse
 
maxwell_dev profile image
Max Antonucci

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.

Collapse
 
mohisham profile image
Hisham Mohammed • Edited

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?

Collapse
 
maxwell_dev profile image
Max Antonucci

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.

Collapse
 
ddonprogramming profile image
Decebal Dobrica

I love this one, spares me the trouble of explaining the same thing to everyone I come accross

Collapse
 
fervic profile image
fervic

Nice post! Just wanted to point out that VIM isn't mandatory, you can set Git to use your preferred editor.

Collapse
 
sebastiannielsen profile image
Sebastian-Nielsen

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!

Collapse
 
mercier_remi profile image
Rémi Mercier

This is a great post!

Collapse
 
mohit_knock profile image
《MohitChauhan /》

Hey Max, great post on git rebase.

Hope to read more interactive posts of your like this one 👍

Collapse
 
lmuzquiz profile image
lmuzquiz

Really helpful. Quite clear. Thanks

Collapse
 
vasilvestre profile image
Valentin Silvestre

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

Collapse
 
gokatz profile image
Gokul Kathirvel

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

Collapse
 
samxeshun profile image
Kwaku Eshun

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

Collapse
 
rossijonas profile image
Jonas B. R.

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

Collapse
 
dimyme profile image
s3goat

80% of the verbiage need to be cut.

Collapse
 
stereodealer profile image
P̾̔̅̊͂̏̚aͬͪ̄v̋̒lo͛̎

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

Collapse
 
proco profile image
Eduardo Ortega

I am an experienced developer, I use git everyday and this was the explanation I needed. Thanks for sharing!.

Collapse
 
codemouse92 profile image
Jason C. McDonald

Bookmarked for later reference. Thank you!