datarekha

Staging & commits

How to stage changes, write good commit messages, and build a history that makes future debugging sane.

8 min read Beginner Git Lesson 4 of 15

What you'll learn

  • What a commit really is: a snapshot, a parent pointer, and a SHA hash
  • How to stage exactly what you want with git add, git add ., and git add -p
  • Writing atomic commits and imperative messages that teammates (and future-you) will thank you for

Before you start

What a commit actually is

A commit is a complete snapshot of every tracked file at a point in time, plus three pieces of metadata that turn it into a node in a graph:

  • A SHA-1 hash (usually shown as the first 7 characters, like a1b2c3f) — a unique address derived from the content itself. Change anything and the hash changes.
  • A parent pointer — the SHA of the commit that came before this one. That chain of pointers is your project history.
  • Author, timestamp, and message — who made the change, when, and why.

Here is a three-commit chain. Each box is a commit. The arrow means “my parent is”:

a1b2c3fAdd login pageC3 (HEAD)9f3e1a2Add auth middlewareC27c4d8b0Init projectC1
C3 → C2 → C1. Each commit knows its parent; Git reads this chain as history.

The staging area: why it exists

Before a commit, files pass through the staging area (also called the index). Think of it as a draft tray: you choose exactly which changes go in this commit, even if you have edits across a dozen files.

git status
On branch main
Changes not staged for commit:
  modified:   src/auth.py
  modified:   src/models.py
Untracked files:
  notes.txt

You decide only auth.py belongs in this commit. You stage it:

git add src/auth.py
git status
Changes to be committed:
  modified:   src/auth.py

Changes not staged for commit:
  modified:   src/models.py
Untracked files:
  notes.txt

models.py and notes.txt are untouched — they will not be in this commit.

Staging commands

Stage a single file — the most surgical choice:

git add src/auth.py

Stage everything in the working tree — fast but broad:

git add .

Stage selected hunks inside a file (-p for patch mode) — the most precise option:

git add -p src/auth.py

Git shows each hunk (a contiguous block of changes) one at a time and asks what to do:

@@ -12,6 +12,10 @@ def authenticate(user, password):
+    if not user:
+        raise ValueError("user required")
     return check_hash(password, user.password_hash)

Stage this hunk [y,n,q,a,d,s,e,?]?

Type y to stage it, n to skip it, s to split it into smaller hunks, q to quit. This lets one file’s changes land in two separate, focused commits — invaluable when you fix a bug and refactor in the same session.

Inspect before you commit

Making the commit

Short message — fine for a small, obvious change:

git commit -m "Raise ValueError when user is missing in authenticate"

Long message — the preferred form for anything non-trivial. Running git commit without -m opens your editor. The convention is a subject line under 72 characters, a blank line, then a body:

Add input validation to authenticate()

Previously the function would call check_hash with a None user,
producing a cryptic AttributeError deep in the hash library.
Now it raises ValueError immediately with a clear message.

Fixes #214.

Writing good messages

Use imperative mood for the subject: “Add”, “Fix”, “Remove”, “Refactor” — not “Added”, “Fixing”, “Removes”. Git itself uses this convention (Merge branch 'main', Revert "Add login page"), so your messages read naturally alongside it.

The subject says what. The body says why — the context that will not be obvious from the diff alone. What problem did this solve? What alternative did you consider and reject? That reasoning is exactly what reviewers and future debuggers need.

Atomic commits

An atomic commit captures one logical change. Not one file, not one function — one idea.

Why it matters in practice:

  • Reverting is clean. git revert <sha> undoes exactly one thing without collateral damage.
  • Bisecting works. git bisect steps through commits to find which one introduced a bug. If each commit is one change, bisect narrows it down fast.
  • Code review is faster. A reviewer can read a 30-line atomic commit in two minutes. A 600-line “weekly dump” commit takes an afternoon.

If you find yourself writing “and” in the subject line (“Fix login bug and update styles”), split it into two commits.

The -am shortcut

git commit -am "Fix null check in authenticate"

The -a flag automatically stages every already-tracked file before committing, skipping git add. It is a handy shortcut when you are iterating on existing files and you want to stage everything.

Its limit: -a does not add brand-new untracked files. Those still require an explicit git add. If you have a new file that must go with this commit and you use -am, the new file will be silently left out.

Putting it together

A typical workflow for a focused change:

git status                    # see what changed
git add -p src/auth.py        # stage only the relevant hunks
git diff --staged             # confirm what's about to be committed
git commit -m "Raise ValueError when user is missing in authenticate"
git status                    # working tree should be clean (or show remaining work)
On branch main
nothing to commit, working tree clean

Quick check

Quick check

0/3
Q1What does a commit's SHA-1 hash represent?
Q2You edited three files but only want two of them in this commit. Which command is most precise?
Q3Your teammate bisects a regression to a commit titled 'misc fixes and refactors (2 weeks of work)'. What does this reveal about the commit strategy — and what should have been done instead?

Sign in to track your progress

Completed lessons, your XP, level, and streak save to your account — it's free and takes a few seconds.

Explore further

Glossary terms
Cheat sheets

Related lessons

Skip to content