Select Hunks to Stage in Git


How to pick which parts of what files you want to stage in git.

Stage everything

Often, we want to stage and then commit all our changes in git. To change everything in the working directory,

git add .

Stage selectively

To stage selectively, git allows us an interactive interface for staging. Add the -i flag:

git add -i .

Our changes are tracked in hunks, or parts of the files that we change. One file can have many hunks, depending on the spread and space between the changes. Git makes some calculation on the hunk splits, but I'm not sure how it does it.

Let's try it out with a test project. My git --no-pager diff shows:

diff --git a/test.js b/test.js
index f7cc1c0..76e7ad9 100644
--- a/test.js
+++ b/test.js
@@ -1,9 +1,9 @@
 function addMe() {
-  doThis();
+  // doThis();
 }

 function doThis() {
-  console.log("asdf");
+  console.log("changed");
 }

 function main() {
@@ -11,3 +11,7 @@ function main() {
 }

 main();
+
+function newThing() {
+  return 1 + 1;
+}

Let's say that I want to skip the addMe change, stage the doThis change and stage the newThing change.

Once I type git add -i . and enter the interactive stage, I see a menu of options:

           staged     unstaged path
  1:    unchanged        +6/-2 test.js

*** Commands ***
  1: status	  2: update	  3: revert	  4: add untracked
  5: patch	  6: diff	  7: quit	  8: help
What now> 5
           staged     unstaged path
  1:    unchanged        +6/-2 test.js
Patch update>> 1
           staged     unstaged path
* 1:    unchanged        +6/-2 test.js
Patch update>>

What I want to do is patch for the stage. I select p and then 1 for the test.js file, then enter. Now I'm brought to a prompt with the first hunk:

diff --git a/test.js b/test.js
index f7cc1c0..76e7ad9 100644
--- a/test.js
+++ b/test.js
@@ -1,9 +1,9 @@
  1 ⋮  1 │ function addMe() {
  2 ⋮    │-  doThis();
    ⋮  2 │+  // doThis();
  3 ⋮  3 │ }
  4 ⋮  4 │
  5 ⋮  5 │ function doThis() {
  6 ⋮    │-  console.log("asdf");
    ⋮  6 │+  console.log("changed");
  7 ⋮  7 │ }
  8 ⋮  8 │
  9 ⋮  9 │ function main() {
(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,s,e,?]?

Two of my changes have been put into one hunk. I can split them with s. Then I'm re-asked the same question, now on each hunk. The first looks like:

Split into 2 hunks.
@@ -1,5 +1,5 @@
  1 ⋮  1 │ function addMe() {
  2 ⋮    │-  doThis();
    ⋮  2 │+  // doThis();
  3 ⋮  3 │ }
  4 ⋮  4 │
  5 ⋮  5 │ function doThis() {
(1/3) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]?

In this case, we decide that no, I don't want to stage this change, so I press n. The next hunk is presented:

@@ -3,7 +3,7 @@
  3 ⋮  3 │ }
  4 ⋮  4 │
  5 ⋮  5 │ function doThis() {
  6 ⋮    │-  console.log("asdf");
    ⋮  6 │+  console.log("changed");
  7 ⋮  7 │ }
  8 ⋮  8 │
  9 ⋮  9 │ function main() {
(2/3) Stage this hunk [y,n,q,a,d,K,j,J,g,/,e,?]?

I want that staged, so I press y. So comes the last hunk:

@@ -11,3 +11,7 @@ function main() {
 11 ⋮ 11 │ }
 12 ⋮ 12 │
 13 ⋮ 13 │ main();
    ⋮ 14 │+
    ⋮ 15 │+function newThing() {
    ⋮ 16 │+  return 1 + 1;
    ⋮ 17 │+}
(3/3) Stage this hunk [y,n,q,a,d,K,g,/,e,?]?

y, stage that one too. As that is the last hunk, I'm brought back to the first command menu, where I q for quit.

Key bindings

Here are a few explainers on keys used in the interactive staging:

  • j/k next and previous hunks, a la vim
  • g shows a goto index for hunks to jump to, by line number and first line
  • / allows you to search by regex for code in the hunk
  • e edit the hunk manually. A step up from split. You get full control of the hunk boundaries here.

Finally, if you use the interactive staging for patching, you can jump directly to that and bypass the command menu with:

git add --patch -i .

Now commit

Don't be a Horace Wimp. Commit! Staging is just playing around. Let's make it real. Reflog or it didn't happen.

Check what you've staged with git diff --staged.

See what you're leaving uncommitted with git diff.

Finally, commit with: git commit -m "gone done it"