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.
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:
git statusto 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).
git showto show the commit you are working on.
This is especially important when using these steps when doing a
git rebase -iwith 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.
git reset HEAD^to 'undo' the change.
This will set the pseudo-tag
ORIG_HEADto the value of
HEADbefore this command.
git diffand ensure that the diff matches the diff part of the
git showyou did earlier.
git add -ipto 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.
git commit -c ORIG_HEADto commit the change.
This commits the change and copies the commit message fro the pseudo-tag
More often than not, when you are splitting changes, you will want a subset of the original message, or to edit it somehow.
git diffto see what’s left to commit.
Repeat the last two steps until there’s no more changes left.
git commitdoes not move
ORIG_HEADso 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