Git Add -p

I learned a new tool that has quickly become an essential part of my workflow. git add -p lets you add small hunks of modified code to the index from your working tree in order to prepare your next commit.

The best refactors are small atomic changes, and while I may not always work incrementally, I still want my git history to reflect the ideal. I use git add -p before every commit to ensure that each code change is intentional.

An example from my latest test project.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ git add -p
diff --git a/send.rb b/send.rb
index 5e89eaf..a0f25b6 100644
--- a/send.rb
+++ b/send.rb
@@ -17,5 +17,5 @@ queue = channel.queue("hello")
 channel.default_exchange.publish("Hello world!", :routing_key => q.name)
 puts " [x] Sent 'Hello World!'"

-# close the connection
-conn.close
\ No newline at end of file
+#close the connection
+connection.close
Stage this hunk [y,n,q,a,d,/,e,?]?

Woah — that is a lot of information… lets go through by the numbers:

  1. the initial command
  2. tells me that it is showing the changes made in the send.rb file. ‘a’ is the version already in the commit history, and ‘b’ is the copy in the working directory.
  3. shows the commit SHA range for this branch
  4. tells me that lines marked with a ‘–’ are from the committed version of send.rb
  5. tells me that lines marked with a ‘+’ are from the working directory version of send.rb
  6. @@ -17,5 +17,5 @@ indicates that the hunk under scrutiny begins at line 17 of the committed version (‘-17’) and affects 5 lines. It corresponds to the working directory version starting at line 17 (‘+17’), also affecting 5 lines.
  7. the remaining lines contain the details of the changes in the hunk.

Know your options

Now that you have a hunk, what can you do with it?

Type ‘?’ to learn what you can do with your hunk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
...
Stage this hunk [y,n,q,a,d,/,e,?] ?
y - stage this hunk
n - do not stage this hunk
q - quit, do not stage this hunk nor any of the remaining ones
a - stage this and all the remaining hunks in the file
d - do not stage this hunk nor any of the remaining hunks in the file
g - select a hunk to go to
/ - search for a hunk matching the given regex
j - leave this hunk undecided, see next undecided hunk
J - leave this hunk undecided, see next hunk
k - leave this hunk undecided, see previous undecided hunk
K - leave this hunk undecided, see previous hunk
s - split the current hunk into smaller hunks
e - manually edit the current hunk
? - print help

The first few options are obvious choices — y (“Yes, please!”), and n (“What was I thinking there?”), as well as q (“Hey, Jane! Get me off this crazy thing!”).

I haven’t quite got a handle on the middle options, but I have skimmed down and used s to split the hunk into smaller pieces. Split will only divide a hunk if there is a logical place for separation, like a gap between changes. For real index-finesse and line-by-line control you have to… edit the hunk (gasp).

Seasoned veterans may blanch at the thought of editing a hunk without a GUI, but I have discovered the secret to command-line-hunk-editing-prowess. The secret is:

Read the instructions

Revisiting the earlier example, I choose e to edit my hunk. I think the original comment on line 10 is better than the new one on line 13 (spacy == better). Selecting e opens your editor of choice with the now familiar hunk, along with…

Lo, and behold! Instructions after the hunk!

Simple enough:

  • -# close the connection becomes # close the connection — mind the space!
  • remove +#close the connection entirely
  • save and close.

Success? If you have done everything right, the hunk will be staged and git will move on to the next hunk in your working directory. If you have made an error you will see the following message:

Type ‘?’ to learn what you can do with your hunk
1
2
Your edited hunk does not apply.
Edit again (saying "no" discards!) [y/n]?

Aw nuts. y will let you amend your transgressions (did you remember to save? did you forget to leave a space in place of the ‘–’?), and n will abandon your changes and take you back to the drawing board, where you are free to transgress anew.

Awesome.