[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