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...
For further actions, you may consider blocking this person and/or reporting abuse
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)
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.
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 aboutgit 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.
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.
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 :)
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.
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
.I export
EDITOR=nano
(never heard ofVISUAL
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. :-DHoly 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.
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.
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.
Not entirely true, just uses your system default visual editor. Which is $VISUAL || $EDITOR ||
vi
IIRC.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, 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.”
Thanks so much for this awesome explanation! That rebase-cupcake cartoon is the best! :D
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.
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...
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.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:
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 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.
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?
medium.com is good too, for college guys
got commit —amend also much convenient
I'll tattoo this post in my front.
This is the clearest explanation of rebasing I've come across yet --- thanks a million!
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!
Any comments about
-onto
option of the rebase command?Thank you, this article has some really useful explanation. Plus I learnt about
--fixup
that I'm sure I'll use a lot!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?
You made my day with autosquasing, super nice!!
I’ve been rebasing and squashing for years with joy, and I just learned of autosquashing today. Thank you Max.
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.
Nice post! Just wanted to point out that VIM isn't mandatory, you can set Git to use your preferred editor.
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.
I love this one, spares me the trouble of explaining the same thing to everyone I come accross
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!
Hey ! Can you please draw the git rebase -i --autosquash master command ? I don't really get it.. Thanks !
This is a great post!
Hey Max, great post on git rebase.
Hope to read more interactive posts of your like this one 👍
Really helpful. Quite clear. Thanks
Where have you been all my life??? This article was pretty helpful to me.
Exactly the git resabe intro I wish I would had! nice article!
Bookmarked for later reference. Thank you!
I am an experienced developer, I use git everyday and this was the explanation I needed. Thanks for sharing!.
Thanks for writing this article, it's a important part of git!
80% of the verbiage need to be cut.
Great write up. I love your pictorials. Nice take. Gonna steal your idea 😜😜