Splitting work into focused commits
You fixed a bug and refactored code in the same file. You use git add -p to stage only the bug-fix hunks, commit them, and then stage and commit the refactor separately.
Git · Concept
The staging area, which Git also calls the index, is one of Git's four core kinds of data. It holds the exact file snapshot your next commit will record, so you can choose what goes into a commit separately from what is in your working directory.
Last updated
Each index entry has four fields: a file type (regular, executable, symlink or gitlink), the ID of the blob holding the file's contents, a stage number and a path. Running git add stores the file's current content as a blob and points the entry at it. When you commit, Git turns this flat list of paths into nested tree objects and records that tree in the new commit. That means a commit holds whatever is staged, not whatever is on disk. During a merge conflict, one path can have several entries with stage numbers 1 to 3.
git add [<options>] [--] [<pathspec>…]set -e
mkdir demo && cd demo && git init -q
echo 'print("hi")' > hello.py
echo 'notes' > TODO.txt
# Stage only hello.py
git add hello.py
git status --short # A hello.py / ?? TODO.txt
# Look at the raw index entries: mode, blob id, stage, path
git ls-files --stage
# Edit after staging: the new edit is NOT staged
echo 'print("bye")' >> hello.py
git diff # working tree vs index
git diff --cached # index vs HEAD (what will be committed)
# Commit records only the staged snapshot
git commit -qm 'Add hello.py'
git status --short # ' M hello.py' and '?? TODO.txt' remainThe script stages one file, lists the index entries, then shows that an edit made after staging stays out of the commit until you run git add on that file again.
You fixed a bug and refactored code in the same file. You use git add -p to stage only the bug-fix hunks, commit them, and then stage and commit the refactor separately.
After a conflicted merge, the index holds several stage entries for each conflicted path. Once you have edited a file to remove the conflict markers, you stage it to mark it as resolved, then you commit.
Before you commit, git diff --cached shows the difference between HEAD and the index, so you can check that only the intended changes go in.
The index, also called the staging area, is a flat list of entries. Each entry has a file type, a blob ID for the contents, a stage number and a path. git add writes the file's contents as a blob and updates the entry, and git commit converts the list into tree objects for the new commit.
Follow-up: Why is the index a flat list instead of a tree? A flat list of full paths is easy to update one file at a time, and Git only builds the nested trees when it creates a commit.
The version you staged gets committed. git add records the content at the moment you run it, and git commit with no arguments commits only what is staged. The later edit stays unstaged until you run git add on the file again.
Follow-up: How would you see both sets of changes? git diff --cached shows staged changes (index vs HEAD), and plain git diff shows unstaged edits (working tree vs index).
Each entry has a stage number. Normally it is 0, but when a merge conflicts, the same path can have several entries with stages 1, 2 and 3 for the different versions. Running git add on the resolved file replaces them with a single entry.
Follow-up: What does git add --resolved do differently? It stages only unmerged paths that no longer contain conflict markers, and if any matching path still has markers, it refuses to stage anything.
git-addgit-commitgit-statusgit-diffgit-restoregit-resetgit-rmgit-ls-files