Tricks & Tips: Git – committing specific sections of a file

(If you are an SVN user, see the end of this post.)

Using git add -p

This command has been a life saver for me on several occasions when writing my dissertation project, something I look forward to talking about on here later. It allows you to stage specific lines within a file using the -p switch on the add function. Lets say you have this file:

# A demo for `git add -p filename`

def foo(x, y):

 if x > y:
     print x
 else:
     print y

and you modify it which generates the following diff

jake@lenovo-jake:~/gitaddp_eg$ git diff
diff --git a/example.py b/example.py
index 6958139..9523d96 100644
--- a/example.py
+++ b/example.py
@@ -4,5 +4,9 @@ def foo(x, y):
 
 if x > y:
     print x
+ elif x == y:
+     print x + y
 else:
     print y
+
+ print "Complete"

If you want to commit the print before you add the elif, you can call

git add -p example.py

This will give you the following output:

jake@lenovo-jake:~/gitaddp_eg$ git add -p example.py 
diff --git a/example.py b/example.py
index 6958139..9523d96 100644
--- a/example.py
+++ b/example.py
@@ -4,5 +4,9 @@ def foo(x, y):
 
 if x > y:
     print x
+ elif x == y:
+     print x + y
 else:
     print y
+
+ print "Complete"
Stage this hunk [y,n,q,a,d,/,s,e,?]?

Here’s what those options mean:

  • 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 hunk and all later hunks in the file
  • d – do not stage this hunk nor any of the later 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

Some of these don’t show because they aren’t valid at this point, but they will show up later in this post.

So if you just want to stage just the print statement at the end, you would hit s which would give you the following output:

Stage this hunk [y,n,q,a,d,/,s,e,?]? s
Split into 2 hunks.
@@ -4,5 +4,7 @@
 
 if x > y:
     print x
+ elif x == y:
+     print x + y
 else:
     print y
Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]?

This has now split the hunk into 2 smaller hunks.
This is the section that we don’t want to commit yet, so we will skip this hunk using n

Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]? n
@@ -7,2 +9,4 @@
 else:
     print y
+
+ print "Complete"
Stage this hunk [y,n,q,a,d,/,K,g,e,?]?

Since this is the hunk we want to commit first, we will use y to stage it. If there were more hunks, this would then continue to iterate through them all but since it is the last hunk, it will return back to the normal UNIX prompt.

jake@lenovo-jake:~/gitaddp_eg$ git status
On branch master
Changes to be committed:
 (use "git reset HEAD ..." to unstage)

 modified: example.py

Changes not staged for commit:
 (use "git add ..." to update what will be committed)
 (use "git checkout -- ..." to discard changes in working directory)

 modified: example.py

As you can see the file is both staged and not staged as only some of the changes are staged. Now we can commit the first hunk and see what is left over

jake@lenovo-jake:~/gitaddp_eg$ git commit -m 'Committing the print statement'
[master 8475327] Committing the print statement
 1 file changed, 2 insertions(+)
jake@lenovo-jake:~/gitaddp_eg$ git diff
diff --git a/example.py b/example.py
index fee8902..9523d96 100644
--- a/example.py
+++ b/example.py
@@ -4,6 +4,8 @@ def foo(x, y):
 
 if x > y:
     print x
+ elif x == y:
+     print x + y
 else:
     print y

Now we can commit this final piece

jake@lenovo-jake:~/gitaddp_eg$ git add example.py 
jake@lenovo-jake:~/gitaddp_eg$ git commit -m 'Committing the elif statement'
[master 54f4623] Committing the elif statement
 1 file changed, 2 insertions(+)

As you can see from the show commands below, we have two separate commits

jake@lenovo-jake:~/gitaddp_eg$ git log --pretty=oneline
54f4623a119995f8fe3fff54a2a8c59b1a5b32f3 Committing the elif statement
8475327e327023806931fd7047ccfd8f5956b175 Committing the print statement
3851ca685822670f68feee6520ee29ca394ec78e Added foo
902101ee8e1560f2e8440e34f3d8bd112509a5eb Initial commit
jake@lenovo-jake:~/gitaddp_eg$ git show 8475327e327023806931fd7047ccfd8f5956b175
commit 8475327e327023806931fd7047ccfd8f5956b175
Author: Jake Cowton <rockjake28@hotmail.co.uk>
Date: Fri Jan 30 02:32:36 2015 +0000

 Committing the print statement

diff --git a/example.py b/example.py
index 6958139..fee8902 100644
--- a/example.py
+++ b/example.py
@@ -6,3 +6,5 @@ def foo(x, y):
     print x
 else:
     print y
+
+ print "Complete"
jake@lenovo-jake:~/gitaddp_eg$ git show 54f4623a119995f8fe3fff54a2a8c59b1a5b32f3
commit 54f4623a119995f8fe3fff54a2a8c59b1a5b32f3
Author: Jake Cowton <rockjake28@hotmail.co.uk>
Date: Fri Jan 30 02:33:22 2015 +0000

 Committing the elif statement

diff --git a/example.py b/example.py
index fee8902..9523d96 100644
--- a/example.py
+++ b/example.py
@@ -4,6 +4,8 @@ def foo(x, y):
 
 if x > y:
     print x
+ elif x == y:
+     print x + y
 else:
     print y

So there you go, I hope this helps you as much as it has helped me.

It should be noted that splits are only possible if there are unchanged lines between sections of changed lines.

Credit to Mechanical Snail of StackOverflow for being the one to show this to me

My favourite Octocat variant and current wallpaper

My favourite Octocat variant and current wallpaper

SVN Users

Why? Stop doing this to yourselves! I at least hope you’re using the git svn to save yourself at least a little bit of hell.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s