Partial Commits with Git

Every once in a while I’m working on a feature, only to discover that I need to extend another part of the code first. If I was disciplined, I would create another branch at that point. But I’m not. I end up with both the extended utility class and the actual feature as pending changes. With git it is simple to make two separate commits while ensuring that every commit compiles.

I’m working on my new big thing; the command line calculator. I’ve already done addition and am quite happy with that and I’m now implementing subtraction. Half way through the subtraction implementation I discover that I need to make some changes to the console output formatter class. It has the + sign hard coded and now needs to take that as a parameter. I do that and end up with a working solution.

Doing a git status however shows a mess.

C:\git\spikes\gitpartial [master +1 ~2 -0 !]> git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
 
        modified:   ConsoleFormatter.cs
        modified:   Program.cs
 
Untracked files:
  (use "git add <file>..." to include in what will be committed)
 
        Subtraction.cs
 
no changes added to commit (use "git add" and/or "git commit -a")
C:\git\spikes\gitpartial [master +1 ~2 -0 !]>

I’ve got both the updated ConsoleFormatter.cs, the updated Program.cs and the new Subtraction.cs. The first one contains the updated console formatting features that are independent of the added functionality. I want to commit the ConsoleFormmatter.cs separately. And not only commit it. I want to compile and test the exact code I’m going to commit, by hiding the other files from view. With git this can be done with just a few commands. With subversion, I’ve never quite figured out how to do it in a simple enough way. I usually end up with one big commit on svn. If anyone knows how to do this as simple in svn, please leave a comment.

Staging the Wanted Changes

The first step is to stage the changes I want in the first commit.

C:\git\spikes\gitpartial [master +1 ~2 -0 !]> git add ConsoleFormatter.cs
C:\git\spikes\gitpartial [master +0 ~1 -0 | +1 ~1 -0 !]>

Doing a git status shows that the ConsoleFormatter.cs file is now ready to be committed.

C:\git\spikes\gitpartial [master +0 ~1 -0 | +1 ~1 -0 !]> git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
 
        modified:   ConsoleFormatter.cs
 
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
 
        modified:   Program.cs
 
Untracked files:
  (use "git add <file>..." to include in what will be committed)
 
        Subtraction.cs
 
C:\git\spikes\gitpartial [master +0 ~1 -0 | +1 ~1 -0 !]>

Hiding the Other Changes

Now it’s time to hide the other changes from view with git stash.

C:\git\spikes\gitpartial [master +0 ~1 -0 | +1 ~1 -0 !]> git stash -u -k
Saved working directory and index state WIP on master: 0b093c1 Implemented addition.
HEAD is now at 0b093c1 Implemented addition.
C:\git\spikes\gitpartial [master +0 ~1 -0]>

The -k switch tells stash to keep the files that are staged intact. The -u switch tells stash to include untracked files (those that are new and not yet added to git).

Doing a git status again shows only the ConsoleFormatter.cs file I want to commit in the first step. This is not just a git status. This is the actual contents of the working directory. At this point I can compile and test the code I’m going to check in, to see that it indeed works without the updated Program.cs file.

C:\git\spikes\gitpartial [master +0 ~1 -0]> git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
 
        modified:   ConsoleFormatter.cs
 
C:\git\spikes\gitpartial [master +0 ~1 -0]>

Making Two Commits

My tests show that the staged changes indeed compile and work without the updated Program.cs so the first changes are ready to be commited.

C:\git\spikes\gitpartial [master +0 ~1 -0]> git commit -m "Improved ConsoleFormatter."
[master d69baee] Improved ConsoleFormatter.
 1 file changed, 0 insertions(+), 0 deletions(-)
C:\git\spikes\gitpartial [master]>

Time to get the other changes back.

C:\git\spikes\gitpartial [master]> git stash pop
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
 
        modified:   Program.cs
 
Untracked files:
  (use "git add <file>..." to include in what will be committed)
 
        Subtraction.cs
 
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (1bd32daf6787280201ccdacb90bc76e1ca45e7ce)
C:\git\spikes\gitpartial [master +1 ~1 -0 !]>

And commit them.

C:\git\spikes\gitpartial [master +1 ~1 -0 !]> git add .
C:\git\spikes\gitpartial [master +1 ~1 -0]> git commit -m "Implemented Subtraction."
[master 46f2e66] Implemented Subtraction.
 2 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 Subtraction.cs
C:\git\spikes\gitpartial [master]>

The Result

The result is a nice commit history, where the separate changes are in separate commits.

 
C:\git\spikes\gitpartial [master]> git log --stat
commit 46f2e66f69dac06ce111f3ab13c6ec68d9b9fae2 (HEAD, master)
Author: Anders Abel <anders@abel.nu>
Date:   Thu Oct 16 22:18:02 2014 +0200
 
    Implemented Subtraction.
 
 Program.cs     | Bin 12 -> 22 bytes
 Subtraction.cs | Bin 0 -> 12 bytes
 2 files changed, 0 insertions(+), 0 deletions(-)
 
commit d69baeeb4b753779bdc3706730e9162244b6e355
Author: Anders Abel <anders@abel.nu>
Date:   Thu Oct 16 22:17:47 2014 +0200
 
    Improved ConsoleFormatter.
 
 ConsoleFormatter.cs | Bin 12 -> 22 bytes
 1 file changed, 0 insertions(+), 0 deletions(-)
 
commit 0b093c1dcb7a536a40648e2977fb73edef9200d5
Author: Anders Abel <anders@abel.nu>
Date:   Thu Oct 16 22:03:53 2014 +0200
 
    Implemented addition.
 
 Addition.cs         | Bin 0 -> 12 bytes
 ConsoleFormatter.cs | Bin 0 -> 12 bytes
 Program.cs          | Bin 0 -> 12 bytes
 3 files changed, 0 insertions(+), 0 deletions(-)
C:\git\spikes\gitpartial [master]>

(And the byte count is correct. I’ve cheated, it’s not actual code in those files.)

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.