(Updated Feb, 2017.)
Some developers regard Git’s rebase feature as mysterious – even dangerous! While inexpert rebasing can indeed cause annoying problems for your teammates, rebase done right is perfectly safe. It’s also a handy way to clean things up before pushing to origin or merging back into master.
But ‘mysterious”? No need for that.
In this post, I’ll provide a brief overview of rebase, and it’s cousin, interactive rebase. Then we’ll dive into interactive rebase using SourceTree (a free UI tool for working with Git commands) and the operations you can perform during it: squash, edit, delete, reword, and reorder.
Don’t have SourceTree yet? Take a tour and download it free at sourcetreeapp.com
Rebase vs. interactive rebase
You may have heard terms like “rewriting history” or “replaying your commits” in the context of rebasing, which can be confusing if you’re new to Git. Essentially, rebase is a way of changing your commit history.
Why call the operation “rebase”, though? Because rebase lets you choose a new base commit to serve as the starting point for your feature branch. You can also rebase against a commit on your current branch, then reapply (or “replay”) subsequent commits on top of that. Like so:
Basic rebase is a good idea when you’re about to merge into master, and master has progressed since you branched off of it. By reapplying all your branch commits onto a different base commit, you flush out merge conflicts before you go to perform the actual merge. And if you kick off a test run against your rebased branch (which you would totally do, because you’re conscientious – right?), you’re can discover integration issues before pushing your changes to origin.
Interactive rebase is a variation that lets you tidy up your commit history before merging or pushing to origin. Changing the commit history lets you apply 20/20 hindsight to describing the steps you took to get to this change. Git has a special file called git-rebase-todo which is simply a text file containing a list of the commits you’re working with. Interactive rebase offers a chance to edit this file and gives you some control over exactly what happens when you replay those commits.
Warning: Even though it looks like you’re just shifting your commits to a different point in your repo’s history, under the covers, Git actually creates new commits – each with a new sha. So don’t rebase commits you’ve already pushed to origin unless you coordinate closely with your teammates. They’ll need to bring their local copy of the repo up to date afterwards.
Interactive rebase using SourceTree
If you’re doing an interactive rebase from the command line, Git will open an editor where you an issue the commands that result in edits to git-rebase-todo – which is sort of like a runbook Git will use when executing the rebase. Similarly, SourceTree taps into git-rebase-todo and edits it. The difference is the user experience: you get to interact with the rebase through a point-n-click UI instead of having to memorize commands and their syntax.
Learn more about the internal mechanisms of interactive rebase on our free Git tutorials site.
There are two ways to start an interactive rebase in SourceTree. The first is to right-click (or context-click) on a commit and choose Rebase children of <sha> interactively… The second is to pull down the Repository menu and select Interactive rebase.
From there, you’ll have the chance to rewrite your repository’s history with the help of a few operations. Let’s walk through them one by one.
Squashing is a nice way to tidy up after a series of “panic commits” – when you write a line of code, then are overcome by fear of a sudden widespread power shortage resulting in the loss of your work. Panic commits tend to be small and, taken in isolation, rather trivial. Squashing lets you combine tiny-yet-related commits into a single, meaningful commit.
To use the squashing feature in SourceTree, just drag and drop rows on top of one another. Or, you can use the squash with previous option by right-clicking or using the button at the bottom of the dialog.
Editing commit contents
Some commits just aren’t quite right. But the git commit –amend command lets you do things like change the commit message or add staged changes to a previous commit.
To use this feature, check the “Amend commit?” checkbox on the commit you’d like to edit. When rebasing continues, it will drop back out to SourceTree allowing you to do whatever the heck you want before continuing on.
Rewording commit messages
When you’re in the middle of solving a problem, you don’t know how the whole story will read. But now that you’ve solved it, you do. Rewording commit messages lets you tell the story in a way your colleagues (and your future self) will be able to make sense of.
Double-click on the “Description” column in the interactive rebase screen. A window will pop into view where you can tweak or completely replace the commit message.
Rewording commit messages is also useful after you’ve added staged changes to an existing commit. In fact, you really ought to update the commit message in such cases so it’s clear to everyone what the commit now contains.
This is one to use with caution. But if you absolutely must delete a commit (and you’re absolutely certain doing so won’t wreak havoc on your repo), you can do it as part of an interactive rebase in SourceTree.
As with squashing and rewording, double-click on the the description of the commit you want to nuke, and select “Delete commit”. And *poof* – it’s gone.
Once upon a time, I got into trouble because the commit history of a repository clearly showed I was working on feature Y before feature X, contrary to the project plan. I’ve also seen commit histories where commits for feature X where scattered around and interleaved with commits for feature Y, making it hard to get a sense where feature X is at in its development.
Whether to save yourself from reprimand, or to group related commits together for ease-of-grokking, reordering commits is easy. Just drag and drop them in SourceTree’s “Reorder and amend” window.
Avoiding confusion during interactive rebase
“mine” vs. “theirs”
Let’s say you’ve deleted a commit, and Git is now replaying subsequent commits. Let’s also say one of those subsequent commits affects a file that was changed as part of the commit you deleted. Git won’t know which version of that file to replay the other commits on top of, so it has to ask you: mine or theirs?
In this case, mine refers to the changeset just prior to the commit you deleted, and theirs refers to the changeset just after the commit you deleted. If you choose mine, you’ll loose all the changes made to that file after the deleted commit. Proceed thoughtfully in this situation, and don’t rush through it. But even if you do make a mistake here, you can still reset your repository as long as you don’t push the changes.
Rebasing upstream commits
I said it above, but it bears repeating: never rebase commits that have already been pushed. This can lead to dangerous and confusing situations.
If you rebase pushed commits, SourceTree will tell you that you’ve got changes to pull down after you’ve finished the rebase. This is really confusing if you know you’ve pulled all the changes already. Here again, you can save your team from experiencing this confusion by resetting your repo as long as you don’t push these changes.
Rebase with confidence
Interactive rebase can be really useful, especially if you tend to commit locally all day long and push your changes on your way out the door. Up to the point of pushing, you can condense, delete, or just edit the message on your commits to make the history easier for everyone to understand.
Give it a try next time you’re iterating quickly on a feature or fix. And tweet us up @sourcetree if you run into issues or have any feedback for us. Happy coding!
For more on rebasing, workflows, and other Git commands, check our our free tutorials site. You’ll find advanced tips for experts, as well as getting-started help for newbies.