[ell-i-developers] Git best practises #1: How to make good, small commits

From: Pekka Nikander <pekka.nikander@xxxxxx>

To: "ell-i-developers@xxxxxxxxxxxxx" <ell-i-developers@xxxxxxxxxxxxx>

Date: Fri, 24 Jan 2014 09:54:35 +0200

During the last three years that I've used git, I've learned to mostly make
relatively small commits. In the typical case, once I've completed a largish
change in my local repo, I sit down for another hour or more, and split it into
a set of small commits. This message briefly describes how I do that.
1. Split changes among multiple commits
=======================================
Let's assume that I have a largish number of changes in my local repo, after
the latest commit. Some of these changes are typically refactorings needed to
implement a new feature, others are simple fixes on things that I've noticed
and fixed on the fly, and some changes are parts of the new feature.
I find it a prudent practise to create separate commits from the small changes,
from the refactorings (often several of those), and the actual new feature.
Typically the first commits are relative small, preparing for the big change,
and the last one adding some new functionality is then a larger one.
The first thing is to commit changes selectively. For this, I use "git commit
-p", which asks interactively whether I want to stage each change. In that way
I can stage some changes from a while while leaving others still unstaged. It
is often necessary to split (s) the changes to separate those changes that I
want to stage and those I don't. Sometimes I also slightly edit some changes
before staging them, in order to stage only a part of the change. Note that
such editing doesn't change the file in the working area, so none of the
existing changes are lost. "git status" and "git diff" and "git diff --staged"
are also very useful at this phase.
Once I think I have the desired changes staged, I review them once more with
"git diff --staged", selectively fixing and repeating git add -p <file> if
needed. Once I'm happy, I simply commit the change, usually from the command
line with "git commit -m 'Message'".
I have adopted the Linux convention of writing the commit messages in present
tense style, explaining what the commit does. So the idea is that the message
would continue the sentence "This commit ...", like "... 'Refactors the foobar
interface to facilitate zap' where the actual commit message starts with the
predicate (verb) of the sentence. Please review my commits to learn the style.
Feel free to use "Adds" instead of "Add"; I'd actually prefer the present
tense, but forget it myself too often.
Of course, this same process is repeated a few times, until all of the wanted
changes are committed, in a series of commits. However, especially in the
beginning when you are not super-confident of yourself (as I am :-), it is a
good idea to test each commit separately.
2. (optional) Test a commit while there are additional changes
==============================================================
Consider now a situation where you have just create a commit of some changes,
while leaving a number of changes still there at your working area. It is a
good idea to test now the new commit, at least to test that it compiles (i.e.
has no syntax or other similar errors.) The way to do is to _stash_ the
remaining changes from the working area, with "git stash". That saves all the
changes in the working area into a temporary commit called stash, into a stash
list. After that you can then test the latest commit, and revise it if needed.
Once you are happy with the commit, use "git stash pop" to redo your
previously saved changes to your working area.
Please note that if there are any conflicts between your new local changes
after saving into the stash and those saved into the stash, there will be
conflicts when you pop the stash. So far I haven't found any very good way of
dealing with that, as it happens so rarely. However, if you notice that after
"git stash pop" you have some changes already staged, you most probably are in
such a situation.
I think this stackoverflow explains the situation and resolution pretty well:
http://stackoverflow.com/questions/7751555/how-to-resolve-git-stash-conflict-without-commit
3. Amending a commit if you notice that you missed something on the latest
commit
=================================================================================
While testing your commit, you may notice that it does not compile, or that
there is some other problem with it. In that case the best approach is to fix
those things, and then *amend* the commit. That is, you add your changes to
the staging area as if you were doing a new commit. Then, instead of giving a
"git commit -m ..." command, you give "git commit --amend". That *adds*
(amends) the new changes from the staging area to the latest commit on the top
of your branch.
4. Amending a commit when it is not on the top of the branch
============================================================
Sometimes it also happens that you notice only later, once you have already
done a few more commits, that you should have added some changes to a commit
that is already somewhat deeper in the stack. That is a slightly trickier
situation. I'm not sure what is the best way to handle it, but currently I
stash my changes, roll back to the commit that I want to amend with "git reset
<sha1>", then amend the commit, and then cherry pick the other commits.
However, let's return to this only once you are more confident with the simpler
processes above.
----------
Questions, comments and suggestions for improvement very welcome!
--Pekka

Other related posts:

» [ell-i-developers] Git best practises #1: How to make good, small commits - Pekka Nikander