Git Interactive Staging: Crafting Commits With Surgical Precision¶
How git add -p transformed my commits from "dump everything" to carefully curated changesets that tell a story.
The Problem With git add .¶
I had a habit. I'd work for hours, changing multiple things — fixing a bug, refactoring a function, adding a feature, updating documentation. Then I'd git add . && git commit -m "various changes". Every commit was a grab bag. Code review was painful. Reverting one change meant reverting everything. Bisecting was imprecise because each commit mixed unrelated changes.
The solution was always there: interactive staging. I just needed to slow down and use it.
The Basics of git add -p¶
The -p flag (short for --patch) shows each change in your working directory as a "hunk" and asks what to do with it:
Git presents each hunk with this prompt:
@@ -45,7 +45,8 @@ def validate_user(user_data):
if not user_data.get("email"):
- return False
+ raise ValidationError("email is required")
+ if not is_valid_email(user_data["email"]):
+ raise ValidationError("email format is invalid")
return True
Stage this hunk [y,n,q,a,d,s,e,?]?
The options: - y — stage this hunk - n — skip this hunk - s — split into smaller hunks (if possible) - e — manually edit the hunk - q — quit (don't stage remaining hunks) - a — stage this and all remaining hunks in this file - d — don't stage this or any remaining hunks in this file
Splitting Hunks: When Changes Are Adjacent¶
Sometimes two unrelated changes are close together in a file, and git groups them as one hunk. The s (split) command breaks them apart:
@@ -10,8 +10,9 @@
-DEFAULT_TIMEOUT = 5
+DEFAULT_TIMEOUT = 30
+MAX_RETRIES = 3
-logger = logging.getLogger("app")
+logger = logging.getLogger(__name__)
Stage this hunk [y,n,q,a,d,s,e,?]? s
After splitting, git shows each change separately. The timeout change goes in one commit ("config: increase default timeout"), the logger fix goes in another ("fix: use module-specific logger name").
Editing Hunks: The Ultimate Precision Tool¶
When s can't split finely enough — two changes on adjacent lines — I use e (edit). This opens the hunk in my editor where I can manually choose which lines to stage:
# In the editor, lines starting with:
# + → added lines (remove the line to NOT stage it)
# - → removed lines (change - to space to NOT stage the removal)
# → context lines (leave unchanged)
This is how I stage a single line from a multi-line change. It requires understanding diff format, but once mastered, it gives complete control.
My Interactive Staging Workflow¶
When I finish a work session, I never commit directly. I review my changes interactively:
# First, see the full picture
git diff --stat
# Then stage methodically, hunk by hunk
git add -p
# Review what's staged vs. what's left
git diff --cached # staged changes (next commit)
git diff # unstaged changes (not yet committed)
# Commit the curated changeset
git commit -m "feat(auth): add email format validation"
# Repeat for the next logical group
git add -p
git commit -m "config: increase default timeout to 30s"
Staging by File: A Middle Ground¶
Sometimes I don't need hunk-level precision — whole files belong together:
# Stage specific files
git add src/auth/validator.py src/auth/tests/test_validator.py
# Stage all files in a directory
git add src/auth/
# Interactive mode at file level (not hunk level)
git add -i
The -i flag gives a menu-driven interface for selecting files, which I find useful when dealing with many changed files across multiple directories.
Unstaging With Precision¶
Made a mistake staging? Interactive unstaging works the same way:
# Unstage hunks interactively
git reset -p
# This shows each staged hunk and asks: "Unstage this hunk?"
# Same y/n/s/e interface as git add -p
Stashing Selectively¶
The patch interface also works with stash:
# Stash only specific hunks
git stash push -p -m "WIP: experimental cache layer"
# Keep some changes in the working directory, stash others
This is powerful when I need to isolate one concern for a quick commit while keeping other in-progress work available.
The Mindset Shift¶
Interactive staging changed how I think about work. Instead of "I'm done, let me commit everything," I think "what are the distinct logical changes in my working directory?" Each one becomes its own commit.
The benefits compound: - Code review is easier because each commit is focused - Bisect is more precise because commits don't mix concerns - Revert is safe because reverting one commit only undoes one thing - Cherry-pick works because changes are self-contained - History tells a story that future developers can follow
Practical Tips¶
-
Commit small, commit often. I aim for commits that a reviewer can understand in 30 seconds.
-
Read every hunk. The review during
add -poften catches bugs — "wait, that's not right." It's a free code review by yourself. -
Don't be afraid of many small commits. You can always squash later during rebase. You can never split a large commit after it's pushed.
-
Use the
--statpreview. Before starting interactive staging,git diff --statgives you the map of what changed where.
Key Takeaway¶
git add -p is the difference between using git as a backup tool and using it as a communication tool. Every commit becomes an intentional message to future readers — including future you. The two extra minutes spent curating each commit saves hours of confusion later. Precision in staging creates precision in history.
Tags: git, staging, workflow, patch, commit-hygiene