Some Hints For Splitting Commits
Sometimes in a code review, the commentators suggest that commits be broken up into smaller pieces. Here are a few of the collected tricks I have learned over the years. They are presented as 'editing the current tip of the tree' for simplicity. However, you can apply them to rebasing as well using the 'edit' action. The last section offers a strategy when you need to recombine the commits.
Splitting up
Sometimes you have a commit that you need to split up. This section will
show you how to do that. Briefly, you will reset the HEAD
of the tree to remove the commit from the current branch (the original
hash remains in the repo), and then add and commit it piecemeal.
Before we get started on doing the change, there’s two steps to do:
-
Run
git status
to ensure the tree is clean.
If the tree is unclean, then you can lose changes in the following steps (or have them accidentally merged in). -
Run
git show
to show the commit you are working on.
This is especially important when using these steps when doing agit rebase -i
with and edit step.
It also helps to know what the whole change looks like.
Now, to 'undo' the change from the repository, make sure the changes are what we think, then redo the changes.
-
Run
git reset HEAD^
to 'undo' the change.
This will set the pseudo-tagORIG_HEAD
to the value ofHEAD
before this command. -
Run
git diff
and ensure that the diff matches the diff part of thegit show
you did earlier. -
Run
git add -ip
to interactively select the subset of the change you want.
There are instructions for how to edit the context diff at the end of the diff, so I won’t go over them here. -
Run
git commit -c ORIG_HEAD
to commit the change.
This commits the change and copies the commit message fro the pseudo-tagORIG_HEAD
.
More often than not, when you are splitting changes, you will want a subset of the original message, or to edit it somehow. -
Run
git diff
to see what’s left to commit.
Repeat the last two steps until there’s no more changes left.
git commit
does not moveORIG_HEAD
so you will start with the right commit message each time.
If you are really operating on the last change in your patch series, you
are now done: the patch is split up. If you are doing this inside a git rebase -i
with an edit
action for the step, do not forget to now run
git rebase --continue
to complete the process. I usually
just edit one at a time to keep things simple (I often forget to do the
git rebase --continue
if I have too many things to edit).
Rearranging the commits.
Sometimes, you have 2 commits that should be 2 different commits, or some
similar rearrangement. Each of the new commits have parts of the original
commits. What I like to do here is take the original two commits and
break them down into the basic parts using the previous section. So I
break the first commit down into the commit into 2 new commits. I repeat
the process for the second commit. There are now 4 commits in my branch.
I usually keep the original commit messages intact and do not edit them
at this stage. I then use git rebase -i
to recombine them
using the 'squash' action. This gives me the new commits with copies
of the original two commits' commit messages so I can edit each one down
appropriately. I find that doing this in multiple steps makes it easier
to keep track of everything. This allows easy backing out half way through
if you realize you have done something in error. Doing only one or a
few things at a time makes it easier to do that.
git reflog
can also help if you made a mistake several steps
ago.