Üllar Maasik

'git add' has an editing mode

July 14, 2026

Take one look at how I work with Git and you’ll see that I’m a bit of pedant when it comes to committing. So much so that I actually forked a tool for creating conventional commits and supercharged it to fit how I work with Git on a daily basis1. However, before I reach the actual commit phase of my work, I need to figure out what changes I’m going to tie together into a single commit.

My usual process for achieving this boils down to using either:

The latter will bring up an interactive mode where I can decide which hunks I want staged and which not. This will work just fine for most cases, and I’ve been a happy customer for years, but it has sometimes been the cause of some frustration to which I haven’t had a solution. Consider the following situation (using my previous TIL as an example):

--- a/src/pages/tils/easy-reproductions-with-txtar-and-testscript.md
+++ b/src/pages/tils/easy-reproductions-with-txtar-and-testscript.md
@@ -18,7 +18,7 @@ go: downloading golang.org/x/tools v0.26.0

-* creating any necessary directory structures along with their files;
+* creating any directory structures along with their files;
 * invoking any commands that assumes the existence of said directories;
 * comparing the standard output to something expected.

@@ -153,7 +153,7 @@ goversion=1.24
 PASS

-Go forth and ~reproduce~ produce reproduction cases!
+Go forth and produce reproduction cases!

These, of course, could be considered a single wording change, but humor me. Running git add -p will plop me into the aforementioned interactive mode:

$ git add -p
...

-* creating any necessary directory structures along with their files;
+* creating any directory structures along with their files;
 * invoking any commands that assumes the existence of said directories;
 * comparing the standard output to something expected.

(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?

Here I would just either press y to stage or n to not and continue on to the next hunk, and that would basically be the workflow. Now, where would the frustration come from then? Check out this scenario:

-* creating any necessary directory structures along with their files;
-* invoking any commands that assumes the existence of said directories;
+* creating any directory structures along with their files;
+* invoking any commands that assume the existence of said directories;
 * comparing the standard output to something expected.

I would, again, intuitively use git add -p, but I would want to separate each change out into its own commit:

$ git add -p
...

-* creating any necessary directory structures along with their files;
-* invoking any commands that assumes the existence of said directories;
+* creating any directory structures along with their files;
+* invoking any commands that assume the existence of said directories;
 * comparing the standard output to something expected.

(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?

Normally, when changes in a single hunk are on disparate lines it’s only a matter of pressing s to split the current hunk into smaller hunks2, but in this case I would be greeted with an error:

(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? s
Sorry, cannot split this hunk

What I’ve usually done, and I can’t believe I’m even saying this out loud, is I’ve gone into the file, manually undone one of the changes, copied it to a scratch file (or buffer in NeoVim), gone through this dance to get my commit done, and then added it back. Luckily this hasn’t happened that often, but something pushed me over the edge today and I couldn’t live like this any longer!

Turns out that in the myriad of options presented in the interactive mode there is e, which stands for “manually edit the current hunk”. Choosing that opens the current hunk in whatever your default $EDITOR is3:

# Manual hunk edit mode -- see bottom for a quick guide.
@@ -18,8 +18,8 @@ go: downloading golang.org/x/tools v0.26.0

...
 
-* creating any necessary directory structures along with their files;
-* invoking any commands that assumes the existence of said directories;
+* creating any directory structures along with their files;
+* invoking any commands that assume the existence of said directories;
 * comparing the standard output to something expected.
 
# ---
# To remove '-' lines, make them ' ' lines (context).
# To remove '+' lines, delete them.
# Lines starting with # will be removed.
# If the patch applies cleanly, the edited hunk will immediately be marked for staging.
# If it does not apply cleanly, you will be given an opportunity to
# edit again.  If all lines of the hunk are removed, then the edit is
# aborted and the hunk is left unchanged.

Through this I can just remove any related changes by replacing that change’s - with a space and removing the + line entirely. This basically undoes that change in the context of this hunk:

# Manual hunk edit mode -- see bottom for a quick guide.
@@ -18,8 +18,8 @@ go: downloading golang.org/x/tools v0.26.0
 
...
 
-* creating any necessary directory structures along with their files;
 * invoking any commands that assumes the existence of said directories;
+* creating any directory structures along with their files;
 * comparing the standard output to something expected.
 
# ---
# To remove '-' lines, make them ' ' lines (context).
# To remove '+' lines, delete them.
# Lines starting with # will be removed.
# If the patch applies cleanly, the edited hunk will immediately be marked for staging.
# If it does not apply cleanly, you will be given an opportunity to
# edit again.  If all lines of the hunk are removed, then the edit is
# aborted and the hunk is left unchanged.

Now just the line I was interested in is its own staged change when viewed through git diff --cached:

-* creating any necessary directory structures along with their files;
 * invoking any commands that assumes the existence of said directories;
+* creating any directory structures along with their files;
 * comparing the standard output to something expected.

And following up the commit above with a normal git add -p will allow me to stage the other change just nicely:

-* invoking any commands that assumes the existence of said directories;
 * creating any directory structures along with their files;
+* invoking any commands that assume the existence of said directories;
 * comparing the standard output to something expected.

Some things are truer than others, but one thing will forever remain true: always read the friendly manual.

Footnotes

  1. To those who are interested, it’s called Cometary. I’d love it if more people used it and gave feedback on what to improve <3

  2. The more knowledgeable among you will already guess where this is going.

  3. This is the same option you can use directly with git add, i.e. git add -e, which will just drop you in your editor with all of the hunks at once.

« Previous