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.

published: reading time: 17 min read updated: March 31, 2026

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 diff and 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:

KeyAction
yStage this hunk
nSkip this hunk
qQuit — do not stage this hunk or any remaining
aStage this hunk and all remaining hunks in this file
dSkip this hunk and all remaining hunks in this file
sSplit this hunk into smaller hunks
eManually 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

ScenarioImpactMitigation
git add . stages debug code or console.log statementsBroken production, noisy logsUse git add -p to review each hunk; add pre-commit hooks
Staging generated files (build artifacts, node_modules)Bloated repository, slow clonesMaintain accurate .gitignore; use git status to verify before staging
Forgetting to stage a new fileBroken build on remote, missing dependenciesAlways run git status before committing to catch untracked files
Staging credentials or API keys accidentallySecurity breach, rotated keys requiredUse pre-commit secret scanning (git-secrets, gitleaks); never commit .env files
Partial staging leaves inconsistent stateCode that does not compile or fails testsVerify with git diff --staged that staged changes form a complete, testable unit
Staging binary files that should not be trackedRepository bloat, merge conflicts impossible to resolveAdd binary patterns to .gitignore; use Git LFS for large files

Trade-offs

ApproachAdvantagesDisadvantagesWhen to Use
git add .Fast, simple, stages everythingNo selectivity, risks committing unwanted filesSolo work, all changes are related, after thorough review
git add -pGranular control, clean atomic commitsSlower, requires understanding of hunksProduction code, PRs, multi-purpose changes
git add -iMenu-driven, good for beginnersMore steps than direct commandsLearning Git, complex staging scenarios
git add -uStages only tracked filesMisses new filesUpdating existing files without adding new ones
git add -AStages everything including deletionsSame risks as git add .Full snapshot of working directory
GUI stagingVisual, intuitiveAbstracts the model, tool-dependentComplex 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 status after every git add to verify what was staged
  • Metrics: Track the number of files per commit — commits touching many files often need splitting
  • Traces: Use git diff --staged to 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 --stat after committing to verify each commit’s file scope
  • Health: Periodically review git status to 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/objects as 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 expire and git gc after 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 -S to cryptographically sign the commit, proving authorship of the staged content
  • Pre-commit scanning: Tools like gitleaks, git-secrets, and trufflehog should 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 status to check for “Changes not staged for commit” after staging
  • Using git add -A without understanding the difference from git add .: In Git 2.x they behave identically from the repository root, but git add . only stages the current directory while git add -A stages 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 add stages 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 own git add

Quick Recap Checklist

  • git add <file> stages specific files
  • git add . stages all changes in current directory
  • git add -A stages all changes in entire repository
  • git add -u stages only modifications and deletions of tracked files
  • git add -p enables interactive hunk-by-hunk staging
  • git add -i opens the interactive staging menu
  • git add -n shows what would be staged without staging (dry run)
  • git add -f forces staging of ignored files
  • git diff --staged reviews 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

What is the difference between `git add .`, `git add -A`, and `git add -u`?

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.

How does `git add -p` split hunks and why is it useful?

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.

If you stage a file and then modify it again, what gets committed?

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.

What happens when you run `git add` on a deleted file?

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:

  1. Computes the SHA-1 hash of the file’s content
  2. Compresses the content with zlib
  3. Stores it as a blob object in .git/objects/
  4. 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 status before every git add to know what has changed
  • Use git add -p for files containing multiple unrelated changes
  • Verify staged content with git diff --staged before every commit
  • Never git add . without reviewing the output of git status first
  • Keep .gitignore updated 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

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 #version-control #branching

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.

#git #version-control #cherry-pick

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.

#git #version-control #rebase