Interactive rebase in SourceTree

Over the last year we’ve introduced interactive rebase support in SourceTree on both Mac and Windows to help developers rewrite their commit history easier than ever. Often we’ve found this feature to be regarded as both mysterious and dangerous by many DVCS users, so we thought we’d help you learn what this feature does, how it can be a great asset in your workflow, and how to use it safely.

What is “rebasing interactively?”

In short, rebasing is a way of changing your commit history. You’ll often hear terms such as “replaying your commits” and “rewriting your commit history,” which can come across as confusing if you’re new to Git concepts. Why do we call it rebasing, though? Imagine your commit history looks like this:

If I were to rebase from commit a4d3a then what I’m really saying is “base all future commits on this one,” hence the term rebase. Now, imagine each commit is like a frame on a reel of film. When we say “rewind your commits” it literally means, like a reel of film, to rewind to a specific frame in time. So the term replay means just what you think, to go through each “frame” on that reel starting from the point you rewound your commits to.

Now you’ve learned about rewinding and rebasing! So how does interactive rebasing fit into this? Well, Git allows you to personally get involved in the process of replaying your commits to give you more control in changing your repository’s history. Git has a special file called git-rebase-todo which is simply a text file containing a list of the commits you’re about to get involved in. By editing this text file you can control what happens when you replay your commits.

Great, now you know all that we do! Now come work for us.

Seriously though, this is the secret of the black box. Of course Git does other magical wonders in making sure all of this goes smoothly.

SourceTree’s involvement

So how does SourceTree fit into this? SourceTree is the middleman between you and Git, making it really easy to change your commit history through a simple user interface. You can drag and drop commits to reorder them or squash them, you can delete commits, reword the commit messages, edit them, or just leave them be.

There’s two ways to rebase interactively: The first is to right-click (or context-click) on a commit and hit Rebase children of <sha> interactively… and the second is to hit the Repository menu and go to Interactive rebase.

Let’s dive into what’s available to you.

Squashing

If you’re like me, you panic commit. What’s panic committing? It’s where you write an awesome line of code and fear that the sun will throw out a huge mass of electrically charged particles causing widespread power shortage resulting in the loss of your work. So you commit things like NSLog(“test”).

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

Some commits just aren’t quite right. You have included too much or too little, perhaps. To counter this you, can quite literally edit a particular commit. To use this feature just check the amend commit checkbox on that particular commit. When rebasing continues, it will drop back out to SourceTree allowing you to do whatever the heck you want before continuing on.

Rewording

If you’re like me, you write in a manner that’s equivocal of the elusiveness of your average Vulpes vulpes. By that I mean that I make no sense at all.

To help repair the sorry state of your commit messages, you can simply double-click on the message column in the interactive rebase screen, allowing you to change your commit message, saving your colleagues from having to translate whatever the heck it was you were saying in your commit.

Deleting

Like any good programmer some of my commits have been known to be laughable. Fortunately, I can hide them away never to see the light of day again by using interactive rebase and deleting that sucker. You’re welcome.

Reordering

Once upon a time many moons ago, 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. If only I could reorder my commits back then. Just drag and drop those commits around and save getting yourself fired.

In practice

There are lots of unusual cases that can lead to commits being lost. Sometimes deleting a commit isn’t the dangerous part but instead it could be when subsequent replays lead to Git not knowing which changeset to choose. In this scenario Git will ask you to choose mine or theirs. It’s important to understand that in this case, mine will be changes from the prior commit affecting that file, and theirs will be changes from the commit after the commit you deleted. If you choose mine then you can lose subsequent changes.

This is a confusing case that can happen quite easily, and dealing with merge conflicts can already be difficult, let alone dealing with merge conflicts that can lead to a loss of an entire commit which you think you’ve included during the rebase process. The good news is that even if you do make a mistake, as long as you don’t push those changes, you can still reset your repository into a state that won’t affect other developers.

Rebasing upstream commits

One word of caution we always see necessary to mention in practice: Never rebase commits that have already been pushed. This can lead to dangerous and confusing situations. If you have done so then SourceTree will actually tell you that you’ve got changes to pull after you’ve finished the rebase which can be even more confusing if you know you’ve pulled all changes already. As mentioned previously, even at this point you can reset your commits and ensure you don’t push the changes leading to all other developers to end up in a confusing state.

Summary

You can see that interactive rebase can be hugely useful, especially if you tend to push your changes at the end of the day. Up until the point of pushing you can do whatever you want with your unpushed commits if you’re iterating quickly on some feature or fix.

Comments (8)

Mercurial does have an equivalent called ‘histedit’ (http://mercurial.selenic.com/wiki/HisteditExtension) which is an extension of Mercurial. We have always had plans on supporting it, however the Mercurial community haven’t shown as much interest compared to the Git community which is why we targeted Git first. So yeah, I will implement this for Mercurial some time in the future, but it’s going to be likely that it’ll be further down the line as interests pick up on it.

Cheers

By Kieran Senior on June 17, 2014

This is great! I wrote a guide a few months ago on some of these features too (including how to save yourself if things go horribly wrong!)

That’s a great, comprehensive write up on the feature. Thanks for sharing!

Cheers

By Kieran Senior on June 17, 2014

I’ve always found this concept of rebasing in conflict with some of the main tenants of the way that the Atlassian tools work.

Delaying pushing commits is nice, but when we have JIRA & Bamboo (amongst other tools) looking at the commits and remote developers using all of this information on which to make decisions…how does one “delay” pushing commits?

Maybe I’m just doing something wrong?

By David Hunt on June 16, 2014

Hey David,

You would (and should) use interactive rebase on commits you haven’t pushed; this is a general rule of thumb.

As to how one delays pushing commits, you simply don’t push when you commit. My workflow sometimes means I iterate on a feature making frequent commits at each small milestone until I have a whole bunch of commits that are related to one overall feature in the product. But that’s a lot of commits for perhaps a small feature, and they might all be commits containing changesets for the same file.

In this case, before you push, you can then squash all of those commits together to make a single changeset for a single file. Your remote systems (JIRA/Bamboo) will be hooked into your ‘remote’ repository so will only see commits when they’re pushed, by which time you’d have squashed them so there’d be less/cleaner commits.