Introduction
We've all been there: you're deep in a feature branch, making progress on the task at hand, when suddenly you spot a typo in an unrelated file or realize you need to refactor a utility function that has nothing to do with your current work. You fix both things in the same session, then face a dilemma at commit time. Do you bundle everything together and muddy the commit history, or do you stash your changes and commit them separately? The answer lies in mastering one of Git's most underrated commands: git add -p. This interactive staging tool lets you review changes hunk by hunk, giving you surgical precision over what goes into each commit. Understanding how to use git add -p is what every Git & GitHub developer should know if they want to maintain clean, reviewable, and maintainable commit histories.
Why hunk-by-hunk staging matters in professional teams
In production environments—whether you're working on e-commerce checkouts, ride-hailing backends, or financial trading systems—commit clarity directly impacts code review velocity and debugging speed. When you commit unrelated changes together, reviewers have to mentally parse multiple concerns in a single diff. Six months later, when a bug surfaces and you're hunting through git blame, a poorly structured commit tells a story that's hard to follow.
Hunk-by-hunk staging transforms your workflow by letting you:
- Separate logical concerns into distinct commits
- Review your own changes before proposing them to teammates
- Catch mistakes or incomplete thoughts before they hit the PR
- Build a narrative through commit messages that actually reflects the code changes
This is especially valuable in high-velocity teams where multiple people touch the same files daily. A well-structured commit makes git revert safer and git bisect more effective.
Review changes hunk by hunk
A hunk is a contiguous block of changes in a file. Git's diff algorithm automatically groups related lines together, and git add -p presents each hunk one at a time, asking you what you want to do with it.
Let's walk through a real scenario. You're working on a NestJS backend and you've made several changes to an auth service:
$ git add -pGit will walk you through each hunk in your working directory. For each one, you'll see a diff preview and a prompt:
Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?The core options you need:
- y — stage this hunk
- n — skip this hunk for now
- q — quit; don't stage anything else
- a — stage this hunk and all remaining hunks in this file
- d — skip this hunk and all remaining hunks in this file
Let's say you have a file with two distinct changes: one that adds a new validation function and another that refactors error logging. You'd stage the validation first by pressing y, skip the logging refactor with n, then commit. Your staged changes now contain only the validation work.
Split unrelated changes into separate commits
The power of git add -p becomes obvious when you need to split unrelated work into logical commits. In a real project, you might have touched the same file for multiple reasons during a single session.
Consider a PostgreSQL schema file where you:
- Fixed a missing index on a high-traffic query column
- Added a new table for a feature you're building
- Corrected a column name typo in an existing table
These are three distinct, reviewable pieces of work. With git add -p, you can stage the index fix, commit it with a clear message, then stage the new table, commit that, and finally stage the typo fix. Each commit tells its own story and can be reviewed, tested, and potentially reverted independently.
$ git add -p
# Stage only the index addition, commit it
$ git commit -m "perf: add index to users.email for login queries"
# Back to add -p for the new table
$ git add -p
$ git commit -m "feat: create notifications table with user_id foreign key"
# And again for the typo
$ git add -p
$ git commit -m "fix: rename column created_on to created_at for consistency"This is a far cleaner history than a single commit that says "database updates" and forces reviewers to untangle three unrelated concerns.
Using s to split hunks and e to edit them
Sometimes Git's automatic hunk boundaries aren't granular enough. If a hunk contains multiple logical changes and you want to stage only part of it, you can split the hunk further or even edit it manually.
The s option: split hunks
When you're presented with a hunk and you press s, Git attempts to break the hunk into smaller pieces. This is useful when Git grouped lines together that you'd rather separate.
Example: you've edited a React component and made two changes within a few lines of each other—maybe you added a new state variable and also rewrote a hook. Git might present both as one hunk. Press s to split it:
Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,s,?]?After pressing s, Git will re-examine the hunk and present smaller chunks. Now you can stage only the state variable addition and skip the hook refactor for a separate commit.
The e option: edit hunks directly
For even finer control, press e to open the hunk in your editor. You'll see the unified diff format with a legend showing which lines will be staged (prefixed with + or unchanged) and which will be left unstaged (prefixed with -). You can manually delete lines from the hunk to refine exactly what gets staged.
This is powerful but requires care: if you accidentally break the diff format, Git will reject it and you'll have to try again. Most of the time, s is sufficient, but e is there when you need surgical precision.
Example scenario: you're working on a Kafka consumer in NestJS and you've modified a message handler. The hunk includes a bug fix in the retry logic and a logging enhancement. You want to stage only the bug fix:
Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,s,?]? e
# Your editor opens; you delete the lines related to logging
# Save and close; only the retry logic fix is stagedCleaner commits, easier reviews
The downstream benefit of disciplined staging is dramatic. Code reviews become focused conversations. When a PR contains five commits, each addressing one concern, reviewers can approve changes commit by commit. They can understand the intent behind each change without mental gymnastics.
In e-commerce or SaaS teams, this translates to faster merge cycles. A reviewer can quickly verify that commit one is a refactor with no behavior change, commit two is a bug fix, and commit three is the new feature. If something goes wrong in production, you can confidently revert commit three without worrying about collateral damage from commit one.
Over time, your git log becomes a readable narrative of your project's evolution. New team members joining the project can use git log --oneline or git log -p to understand the reasoning behind changes, because each commit is complete, purposeful, and bounded.
Common mistakes
Staging everything with -A when you meant to be selective
It's easy to default to git add . or git add -A out of habit. This defeats the purpose of careful staging. Keep your muscle memory sharp: when you want precision, reach for git add -p.
Editing hunks without understanding diff format
If you press e, you're directly editing unified diff format. Lines starting with (space) are context and shouldn't be deleted. Lines starting with + are additions; lines starting with - are deletions. Accidentally removing a context line will cause Git to reject the edit. When in doubt, stick with s to split instead of e to edit.
Losing work by skipping too aggressively
If you press q (quit) during git add -p, the changes you didn't stage remain in your working directory. This is safe, but don't confuse it with committing. Always verify your staging area with git status or git diff --staged before committing.
Mixing refactoring and feature work
A subtle mistake: staging related-but-distinct types of work in a single commit. For example, extracting a utility function and then using it in a new feature should be two commits. The extraction is purely refactoring and should compile and pass tests on its own. The feature that uses it is additive. Splitting them makes code review and git blame clearer.
Key takeaways
git add -plets you stage changes interactively, hunk by hunk, giving you precise control over what goes into each commit.- Use y, n, and q to accept, reject, or stop reviewing hunks; use a and d to stage or skip entire files.
- Press s to split a hunk into smaller pieces when Git's automatic grouping isn't granular enough.
- Press e to edit a hunk directly in your editor for surgical control, but understand unified diff format first.
- Clean, focused commits speed up code review, make debugging easier with
git blame, and create a maintainable project history. - Separate logical concerns—bug fixes, refactors, new features—into distinct commits, even if they touch the same files.
- Always verify your staging area with
git statusorgit diff --stagedbefore committing.






Comments
Loading comments…