Improve your Git commits using patch mode

November 11th, 2012 22:56 – by Torsten Trautwein

When modifying existing code, it's often the case the I stumble upon some other part nearby that seems like it could use some improvement. To keep the commit history clean and more accurate, I could keep that part in mind, finish the changes I originally wanted to do, commit, make the refinements I came across by chance and then commit those.

But let's face it, I don't. Mostly, I just add a commit message the reads like this:

changed x, improved y

And sometimes not even that. So how to improve this without switching between the editor and the console all the time?

Introducing the patch mode

Luckily, Git has a switch ready for exactly that case.

Let's assume we have the following piece of code:

#!/usr/bin/ruby
presidents = ["Jimmy Carter", "Ronald Reagan", "George H. W. Bush", "Bill Clinton", "George W. Bush", "Barack Obama"]
presidents.each do |president|
  puts president
end

vice_presidents = ["Walter Mondale", "George H. W. Bush", "Dan Quayle", "Al Gore", "Dick Cheney", "Joe Biden"]
vice_presidents.each do |vice_president|
  puts vice_president
end

We now want to change George H. W. Bush to George Bush Sr. and George W. Bush to George Bush Jr.. Easy enough.

#!/usr/bin/ruby

presidents = ["Jimmy Carter", "Ronald Reagan", "George Bush Sr.", "Bill Clinton", "George Bush Jr.", "Barack Obama"]
presidents.each do |president|
  puts president
end

vice_presidents = ["Walter Mondale", "George H. W. Bush", "Dan Quayle", "Al Gore", "Dick Cheney", "Joe Biden"]
vice_presidents.each do |vice_president|
  puts vice_president
end

While doing so, we think it would be a nice addition to also have an output of all the presidents that are still alive. So we add the following lines to the same file.

living_presidents = ["Jimmy Carter", "George Bush Sr.", "Bill Clinton", "George Bush Jr.", "Barack Obama"]
living_presidents.each do |living_president|
  puts living_president
end

So in the end we changed some of the file, but also added a part that hasn't got so much to do with that change.

We end up with this:

#!/usr/bin/ruby

presidents = ["Jimmy Carter", "Ronald Reagan", "George Bush Sr.", "Bill Clinton", "George Bush Jr.", "Barack Obama"]
presidents.each do |president|
  puts president
end

vice_presidents = ["Walter Mondale", "George H. W. Bush", "Dan Quayle", "Al Gore", "Dick Cheney", "Joe Biden"]
vice_presidents.each do |vice_president|
  puts vice_president
end

living_presidents = ["Jimmy Carter", "George Bush Sr.", "Bill Clinton", "George Bush Jr.", "Barack Obama"]
living_presidents.each do |living_president|
  puts living_president
end

We could just type git commit -m"changed the presidents names for better perceptibility and added an output for the currently living presidents" but now we know better.

So let's use Git's patch mode by using git commit -p which seperates the changes in hunks and lets us choose which are tied together logically.

% git commit -p 
diff --git a/example.rb b/example.rb
index 1ade9f3..b5f56a2 100755
--- a/example.rb
+++ b/example.rb
@@ -1,6 +1,6 @@
 #!/usr/bin/ruby

-presidents = ["Jimmy Carter", "Ronald Reagan", "George H. W. Bush", "Bill Clinton", "George W. Bush", "Barack Obama"]
+presidents = ["Jimmy Carter", "Ronald Reagan", "George Bush Sr.", "Bill Clinton", "George Bush Jr.", "Barack Obama"]
 presidents.each do |president|
   puts president
 end
Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]?

This is the first part and we'd really like to stage that for commit. So we type y.

Next, Git asks us if we'd also like to stage the following part for commit.

@@ -9,3 +9,8 @@ vice_presidents = ["Walter Mondale", "George H. W. Bush", "Dan Quayle", "Al Gore
 vice_presidents.each do |vice_president|
   puts vice_president
 end
+
+living_presidents = ["Jimmy Carter", "George Bush Sr.", "Bill Clinton", "George Bush Jr.", "Barack Obama"]
+living_presidents.each do |living_president|
+  puts living_president
+end
Stage this hunk [y,n,q,a,d,/,K,g,e,?]?

But since we don't want this in the same commit, we type n this time. We then get asked for the commit message as usual.

Now, we type git commit -p again and are asked if we want to stage the following part for commit, once again:

% git commit -p                                              
diff --git a/example.rb b/example.rb
index 5854cd9..b5f56a2 100755
--- a/example.rb
+++ b/example.rb
@@ -9,3 +9,8 @@ vice_presidents = ["Walter Mondale", "George H. W. Bush", "Dan Quayle", "Al Gore
 vice_presidents.each do |vice_president|
   puts vice_president
 end
+
  +living_presidents = ["Jimmy Carter", "George Bush Sr.", "Bill Clinton", "George Bush Jr.", "Barack Obama"]
+living_presidents.each do |living_president|
+  puts living_president
+end
Stage this hunk [y,n,q,a,d,/,e,?]?

This time, we do want it staged. So we type y, type in the commit message and we're good to go.

Once someone else (or we ourselves) come across that code again, we're have better chances to find the change we're looking for in the file's history without searching around the file(s) and wondering what those changes have to do with the commit message.

% git log
commit 16584466b7a2a6098602bf3014af80d94e13fe6f
Author: Torsten Trautwein <mail@example.com>
Date:   Sun Nov 11 16:31:08 2012 +0100

    output of the presidents currently alive

commit 7f9d163374d9cb30903d56b2cdb52a549d82ac52
Author: Torsten Trautwein <mail@example.com>
Date:   Sun Nov 11 16:27:16 2012 +0100

    changed the presidents names for better perceptibility

As you can see, the commit log is uncluttered and well structured and we didn't really have to do much for it.

If you are looking for more information on Git, have a look at Version Control with Git by Jon Loeliger and Matthew McCullough.

Update

As mentioned by languagehacker in the comments over at hn, you can also use patch mode to add hunks (think git add -p) which allows you to review them before committing.

Back

Get In Touch

The usual channels

twitter @codenapper
Google+ +neowork
github neowork
500px neowork
E-Mail mail@neowork.com

Or send a message right away

captcha

Imprint

Torsten Trautwein
Pfannmuellerstr. 28
60488 Frankfurt am Main, Germany

mail@neowork.com
+49 (0)7000 NEOWORK
+49 (0)7000 63 69 675
max. 0.12 €/min on German landlines