Master git add: Selective Staging, Patch Mode, and Staging Strategies
Master git add including selective staging, interactive mode, patch mode, and staging strategies for clean atomic commits in version control.
Introduction
git add is the command that moves changes from your working directory into the staging area, preparing them for the next commit. It looks simple on the surface — git add file.txt — but beneath that simplicity lies a powerful system for controlling exactly what becomes part of your commit history. Mastering git add means mastering the art of atomic commits.
Most developers learn git add . early and never look deeper. That single command stages everything, turning your commit history into a chaotic dump of unrelated changes, debug code, and half-finished experiments. The real power of git add lies in its selective staging capabilities: interactive mode, patch mode, pathspec matching, and intelligent file selection. These features let you craft commits that tell a clear story about your development process.
This guide covers every important form of git add, from basic usage to advanced selective staging patterns. Understanding these techniques transforms you from someone who commits changes into someone who curates history. For the conceptual foundation, see The Three States.
When to Use / When Not to Use
Use selective staging when:
- A single file contains multiple unrelated changes that should be separate commits
- You need to separate bug fixes from feature additions in the same working session
- Preparing a pull request and want clean, reviewable commits
- You accidentally modified files that should not be committed (editor backups, debug output)
- You want to commit some changes now and save others for later
Simple git add . is acceptable when:
- All changes in the working directory form a single logical unit
- You are working on a solo project with no code review
- You are making a quick documentation fix or typo correction
- You have already reviewed changes with
git diffand are confident
Core Concepts
The staging area (index) is a file (.git/index) that stores a snapshot of what the next commit will contain. git add copies the current content of specified files into this snapshot. Importantly, it copies the current content, not the change itself — if you modify a file after staging it, the new modifications are not staged.
graph LR
A[Working Directory<br/>Current file content] -->|git add| B[Staging Area<br/>Snapshot for next commit]
B -->|git commit| C[Repository<br/>Permanent history]
Key Insight: Staging Copies Content, Not Changes
When you run git add file.txt, Git takes the current content of file.txt and stores it in the staging area. If you then edit file.txt again, those new edits are not staged. The staging area holds the version that existed at the moment you ran git add. This is why you can stage part of a file, keep editing, and stage the new changes separately.
Architecture or Flow Diagram
git add Command Variants and Their Effects
graph TD
A[Changes in Working Directory] --> B{What to stage?}
B -->|Specific file| C[git add file.txt]
B -->|All changes| D[git add .]
B -->|Interactive| E[git add -i]
B -->|Patch by patch| F[git add -p]
B -->|By pattern| G[git add *.py]
B -->|Force ignored| H[git add -f file.log]
B -->|Update tracked| I[git add -u]
C --> J[Staging Area]
D --> J
E --> J
F --> J
G --> J
H --> J
I --> J
J -->|git commit| K[Repository]
The Staging Pipeline
sequenceDiagram
participant WD as Working Directory
participant SA as Staging Area
participant Repo as Repository
Note over WD: You edit files
WD->>WD: Modify file.txt (v1 to v2)
WD->>SA: git add file.txt (copies v2)
WD->>WD: Modify file.txt (v2 to v3)
Note over SA: Still holds v2
Note over WD: Working directory has v3
WD->>SA: git add file.txt (copies v3)
Note over SA: Now holds v3
SA->>Repo: git commit (records v3)
Step-by-Step Guide / Deep Dive
Basic Usage
# Stage a specific file
git add src/app.py
# Stage multiple files
git add src/app.py src/utils.py README.md
# Stage all changes in current directory and subdirectories
git add .
# Stage all changes in the entire repository (from any directory)
git add -A
# Stage only modifications and deletions (not new files)
git add -u
# Stage new files and modifications (not deletions)
git add -N
Interactive Mode: git add -i
The interactive mode presents a menu-driven interface for staging changes:
git add -i
Output:
staged unstaged path
1: unchanged +5/-0 src/app.py
2: unchanged +3/-2 src/utils.py
3: unchanged +1/-0 README.md
*** Commands ***
1: status 2: update 3: revert 4: add untracked
5: patch 6: diff 7: quit 8: help
What now>
Type 2 (update) to stage specific files by number, 5 (patch) for hunk-by-hunk staging, or 4 (add untracked) to stage new files only.
Patch Mode: git add -p
This is the most powerful staging tool in Git. It breaks your changes into hunks and lets you decide for each one:
git add -p src/app.py
Git presents each hunk with interactive prompts:
diff --git a/src/app.py b/src/app.py
index abc1234..def5678 100644
--- a/src/app.py
+++ b/src/app.py
@@ -10,6 +10,8 @@ def process_data(data):
result = validate(data)
+ log.debug(f"Processing {len(data)} items")
return transform(result)
def transform(data):
Stage this hunk [y,n,q,a,d,s,e,?]?
Response options:
| Key | Action |
|---|---|
y | Stage this hunk |
n | Skip this hunk |
q | Quit — do not stage this hunk or any remaining |
a | Stage this hunk and all remaining hunks in this file |
d | Skip this hunk and all remaining hunks in this file |
s | Split this hunk into smaller hunks |
e | Manually edit the hunk |
? | Show help |
Pathspec Patterns
Git supports glob patterns for selective staging:
# Stage all Python files
git add "*.py"
# Stage all files in the src directory
git add src/
# Stage all test files anywhere in the project
git add "**/test_*.py"
# Stage everything except a specific directory
git add . ':!vendor/'
# Stage only files matching multiple patterns
git add "*.py" "*.js"
# Stage files modified in a specific directory
git add src/components/
Force-Staging Ignored Files
Sometimes you need to commit a file that is in .gitignore:
# Force-add an ignored file
git add -f build/output.jar
# This bypasses .gitignore rules for this specific file
Use this sparingly. If you consistently need to commit a file, remove it from .gitignore instead.
Dry Run: Preview Before Staging
# See what would be staged without actually staging
git add -n .
# Output shows files that would be staged
# add 'src/app.py'
# add 'src/utils.py'
# add 'README.md'
Production Failure Scenarios + Mitigations
| Scenario | Impact | Mitigation |
|---|---|---|
git add . stages debug code or console.log statements | Broken production, noisy logs | Use git add -p to review each hunk; add pre-commit hooks |
| Staging generated files (build artifacts, node_modules) | Bloated repository, slow clones | Maintain accurate .gitignore; use git status to verify before staging |
| Forgetting to stage a new file | Broken build on remote, missing dependencies | Always run git status before committing to catch untracked files |
| Staging credentials or API keys accidentally | Security breach, rotated keys required | Use pre-commit secret scanning (git-secrets, gitleaks); never commit .env files |
| Partial staging leaves inconsistent state | Code that does not compile or fails tests | Verify with git diff --staged that staged changes form a complete, testable unit |
| Staging binary files that should not be tracked | Repository bloat, merge conflicts impossible to resolve | Add binary patterns to .gitignore; use Git LFS for large files |
Trade-offs
| Approach | Advantages | Disadvantages | When to Use |
|---|---|---|---|
git add . | Fast, simple, stages everything | No selectivity, risks committing unwanted files | Solo work, all changes are related, after thorough review |
git add -p | Granular control, clean atomic commits | Slower, requires understanding of hunks | Production code, PRs, multi-purpose changes |
git add -i | Menu-driven, good for beginners | More steps than direct commands | Learning Git, complex staging scenarios |
git add -u | Stages only tracked files | Misses new files | Updating existing files without adding new ones |
git add -A | Stages everything including deletions | Same risks as git add . | Full snapshot of working directory |
| GUI staging | Visual, intuitive | Abstracts the model, tool-dependent | Complex merges, visual thinkers |
Implementation Snippets
The Careful Staging Workflow
# 1. See what changed
git status
# 2. Review all changes
git diff
# 3. Stage files selectively
git add src/feature.py
git add -p src/refactored.py # Only stage specific hunks
# 4. Verify staged content
git diff --staged
# 5. Commit
git commit -m "feat: add user authentication module"
# 6. Verify commit
git log -1 --stat
Staging Only New Files
# Stage only untracked files (not modifications)
git ls-files --others --exclude-standard | xargs git add
# Or use git add with pathspec
git add $(git ls-files --others --exclude-standard)
Staging Only Modified Files (Not New)
# Stage modifications and deletions of tracked files only
git add -u
# Equivalent to:
git add --update
Interactive Hunk Editing
# Stage a file in patch mode
git add -p config.py
# When prompted, type 'e' to edit the hunk
# This opens your editor with the hunk in diff format
# Remove lines starting with '+' to NOT stage those additions
# Remove lines starting with '-' to NOT stage those deletions
# Save and close to stage the edited hunk
Staging with Multiple Operations
# Scenario: You have changes for two different features in the same files
# Stage feature A changes interactively
git add -p src/app.py # Select only feature A hunks
git add src/feature_a.py
git commit -m "feat: implement feature A"
# Stage feature B changes
git add -p src/app.py # Select remaining feature B hunks
git add src/feature_b.py
git commit -m "feat: implement feature B"
# Result: Two clean commits from one working session
Observability Checklist
- Logs: Use
git statusafter everygit addto verify what was staged - Metrics: Track the number of files per commit — commits touching many files often need splitting
- Traces: Use
git diff --stagedto trace exactly what content will be committed - Alerts: Pre-commit hooks should block staging of files matching secret patterns, large binaries, or common debug markers
- Audit: Run
git log --statafter committing to verify each commit’s file scope - Health: Periodically review
git statusto ensure no significant uncommitted work is accumulating - Validation: Before committing, run your test suite against staged changes only:
git stash --keep-index && npm test && git stash pop
Security/Compliance Notes
- The staging area is not encrypted: Files staged for commit are stored in
.git/objectsas blobs. Anyone with filesystem access can read them - Secrets in staging are still secrets: Even if you unstage a file containing a secret, the blob may remain in Git’s object database until garbage collection runs. Use
git reflog expireandgit gcafter accidental staging - Audit compliance: Atomic commits with selective staging create better audit trails. Each commit should represent a single logical change that can be independently reviewed and, if necessary, reverted
- Signed commits: After staging, use
git commit -Sto cryptographically sign the commit, proving authorship of the staged content - Pre-commit scanning: Tools like
gitleaks,git-secrets, andtrufflehogshould run as pre-commit hooks to scan staged content for secrets before they enter the repository
Common Pitfalls / Anti-Patterns
git add .as default habit: This is the most common Git anti-pattern. It stages everything without review, leading to commits that mix unrelated changes, debug code, and accidental modifications- Staging then modifying: If you stage a file and then continue editing it, the new edits are not staged. This leads to confusion when the committed version differs from what you see on screen. Always run
git statusto check for “Changes not staged for commit” after staging - Using
git add -Awithout understanding the difference fromgit add .: In Git 2.x they behave identically from the repository root, butgit add .only stages the current directory whilegit add -Astages the entire repository regardless of your current directory - Not using
git diff --staged: Skipping this review step means you commit blindly. This is the #1 cause of accidental commits containing wrong content - Staging binary files without Git LFS: Large binary files bloat the repository permanently. Every clone downloads the full history of every binary version. Use Git LFS for files over 50MB
- Forgetting that
git addstages content, not files: If you delete a file after staging it, the staged version still exists. The deletion is a separate change that needs its owngit add
Quick Recap Checklist
-
git add <file>stages specific files -
git add .stages all changes in current directory -
git add -Astages all changes in entire repository -
git add -ustages only modifications and deletions of tracked files -
git add -penables interactive hunk-by-hunk staging -
git add -iopens the interactive staging menu -
git add -nshows what would be staged without staging (dry run) -
git add -fforces staging of ignored files -
git diff --stagedreviews what will be committed - Staging copies content, not changes — subsequent edits are not staged
- Always verify staged content before committing
- Use pathspec patterns (
*.py,src/,**/test_*.py) for bulk selective staging
Interview Q&A
git add . stages all changes (new, modified, deleted) in the current directory and subdirectories. git add -A stages all changes in the entire repository regardless of your current directory. git add -u stages only modifications and deletions of already-tracked files, ignoring new untracked files. In Git 2.x, when run from the repository root, git add . and git add -A behave identically, but they differ when run from a subdirectory.
git add -p divides a file's changes into hunks — contiguous blocks of added, removed, or modified lines. If a hunk contains multiple unrelated changes, you can press s to split it into smaller hunks, or e to manually edit the hunk in your editor. This is useful when a single file contains changes for multiple logical commits — for example, fixing a bug and adding a feature in the same file. It enables atomic commits from messy working sessions.
Only the version that was staged gets committed. The subsequent modifications remain in the working directory as unstaged changes. This is because git add copies the current content of the file into the staging area at that moment. Later edits create a new working directory version that is separate from the staged snapshot. This behavior is what enables partial staging — you can stage part of your work, keep working, and stage the new changes as a separate commit.
Running git add on a file that has been deleted from the working directory stages the deletion. The file will be removed from the repository in the next commit. This is equivalent to running git rm on the file. Note that git add -u automatically stages deletions of tracked files, while git add . only stages deletions if run from the directory containing the deleted file.
How the Index Stores File Snapshots
The staging area (.git/index) is not a simple list of filenames. It is a binary index file that stores blob references — SHA-1 hashes pointing to compressed file objects in .git/objects/. When you run git add, Git:
- Computes the SHA-1 hash of the file’s content
- Compresses the content with zlib
- Stores it as a blob object in
.git/objects/ - Records the hash, file path, and metadata in the index
graph LR
A[file.txt content] -->|SHA-1 hash| B[blob object]
B -->|zlib compress| C[.git/objects/ab/cdef1234]
C -->|hash reference| D[.git/index entry]
D -->|path + mode + hash| E[Next commit tree]
This means the same file content staged twice produces the same blob — Git deduplicates automatically. Different content, even one character changed, creates a new blob object.
Production Failure: Binary File Bloat
A developer accidentally stages a 200MB compiled binary (build/app.jar) with git add .. The commit pushes to the shared repository. Consequences:
- Every clone downloads 200MB — even developers who never touch that file
- Repository size grows permanently — Git stores every version of the binary forever
- Merge conflicts become impossible — binaries cannot be diffed or merged textually
- CI/CD pipelines slow down — checkout times increase for all workflows
Mitigation:
# Prevent this before it happens
echo "*.jar" >> .gitignore
echo "*.exe" >> .gitignore
echo "*.dll" >> .gitignore
# For binaries that must be tracked, use Git LFS
git lfs install
git lfs track "*.jar"
# If already committed, remove from history
git filter-repo --invert-paths --path build/app.jar
Always run git status after git add . to verify no unexpected files were staged.
Implementation: Interactive Staging for Code Review Prep
Before submitting a PR, use git add -p to craft commits that are easy to review:
# Start interactive staging on the entire working directory
git add -p
# Or target specific files
git add -p src/auth.py src/config.py
Code review prep workflow:
# 1. See the full scope of changes
git diff --stat
# 2. Stage logical units separately
git add -p src/auth.py # Stage authentication changes only
git commit -m "feat(auth): add JWT token validation"
git add -p src/auth.py # Stage remaining logging changes
git commit -m "chore(auth): add debug logging to auth flow"
# 3. Verify each commit is independently reviewable
git log --oneline -2
git show HEAD --stat
git show HEAD~1 --stat
# 4. Final check — nothing unintended staged
git diff --staged
Each commit should answer: “Can a reviewer understand this change in isolation?” If not, split it further with git add -p.
Quick Recap: Staging Best Practices
- Run
git statusbefore everygit addto know what has changed - Use
git add -pfor files containing multiple unrelated changes - Verify staged content with
git diff --stagedbefore every commit - Never
git add .without reviewing the output ofgit statusfirst - Keep
.gitignoreupdated to prevent accidental staging of build artifacts - Use Git LFS for binary files that must be tracked
- Stage logical units — each commit should represent one complete thought
- Run
git add -n(dry run) when unsure what a pattern will match - Remember: staging copies content at that moment, later edits are not staged
- Use
git restore --staged <file>to unstage something added by mistake
Resources
- Pro Git — Recording Changes — Official guide to git add
- Git Add Documentation — Complete reference
- Interactive Staging — Deep dive into -i and -p modes
- Git Pathspec Documentation — Pattern matching for staging
- The Three States — Foundational concepts
Category
Related Posts
Git Branch Basics: Creating, Switching, Listing, and Deleting Branches
Master the fundamentals of Git branching — creating, switching, listing, and deleting branches. Learn the core commands that enable parallel development workflows.
Git Cherry-Pick: Selectively Applying Commits
Master git cherry-pick to selectively apply commits between branches. Learn use cases, pitfalls, and best practices for targeted commit transplantation.
Rebase vs Merge: When to Use Each in Git
Decision framework for choosing between git rebase and git merge. Understand trade-offs, team conventions, history implications, and production best practices.