Today’s tip is pretty clearly spelled out in the manpage for git-rebase. But I find it so useful, I thought I would highlight it here.
From time to time, you end up with a patchset in your history that contains more than it should. It may be that it contains files (or chunks) that have been added incorrectly, or that a review comment has suggested would be better split into multiple changes. Git’s all purpose rewriter of history - git rebase - can help with doing this.
git rebase is a fantastic tool which can let you completely change the history of your project at the drop of a hat. I’ll be writing more about it in tips to come. As with all powerful tools, you need to use its power wisely. In effect, rebasing doesn’t rewrite your history, but creates an entirely new timeline, starting at some common point in the past. If you are working on a personal repository, then this isn’t an issue. If you are sharing your repository, you should think very carefully about rebasing it as your collaborators will have based their work on the old tip of your tree. When you rebase, you create a new branch, with a new tip, of which your collaborators will be completely unaware.
How to split a change is described in detail in the git rebase manpage, so we’ll just recap it here.
- Find the SHA1 hash of the change you wish to split – this is probably easiest done by running
git log –oneline
- Start up an interactive rebase, with the start point being the parent of the commit you wish to split. The git notation HASH^ - for exampledeadbeef^ will give you the parent of a commit. So, run:
git rebase -i HASH^
- In the editor window that appears replace the word pick that appears beside the change you are picking with edit. This instructs git to stop the rebase operation at this point, and to allow you to modify this change.
- Save the file, and exit your editor. Git will now start the rebase operation.
- When it reaches the change you are modifying, git will pause the rebase, and return control. You can now modify this
change – make a note of the SHA1 id it has stopped at, as this will come in handy later!
- git reset HEAD^ will reset the index to the state of the parent commit. This gives you a working tree that contains the contents of the change you are splitting.
- Use git add, and git commit to create as many commits as you wish from this tree. If you want your original commit message, then using git commit -C HASH will commit the current index, using the same message as that in HASH.
- When you’re done, and have committed all of the fragments, use git rebase –continue to resume the rebasing process
Providing all goes well, you’ll end up with a modified history, and a change that has been split into multiple parts. You’ll note that every change after the split now has a new SHA1 – to git, these are completely new changes, as they have a different history, and this is the whole reason why rebases can be dangerous.