Git and GitHub / Basic: the local Git model / Lesson 2
Working tree, index, and deliberate commits
Control exactly which content enters the next snapshot and inspect both staged and unstaged changes.
On this page
Lesson goals
- Explain the working tree, index, and HEAD snapshot.
- Stage, inspect, and unstage without discarding working changes.
Keep three views of a tracked file
The working tree contains the files you edit. The index, often called the staging area, holds content selected for the next commit. HEAD normally identifies the current commit through the current branch. These views can differ simultaneously. Staging records the file content at that moment; it does not establish a live subscription to future edits. A later edit remains unstaged until added again. This is why reviewing only the visible editor buffer cannot tell you exactly what the next commit will contain. Inspect the index explicitly before committing.
Observe staging as a snapshot
In a lab that already contains the initial notes.txt commit, replace the file, stage it, and edit it again. The index preserves the middle version while the working tree contains the latest version. git show with the colon path syntax reads the indexed content. This makes the distinction concrete without relying on color conventions from a particular terminal. The example changes only a synthetic lab file. On an actual project, inspect the current status and protect unrelated work before any operation that updates the index or working tree.
printf 'draft one\n' > notes.txt
git add -- notes.txt
printf 'draft two\n' > notes.txt
git show :notes.txt
draft one
After the stated lab setup, the indexed content is draft one even though the working file says draft two. A commit at this point would include draft one for this file. The output is content, not a prediction of generated commit metadata.
Choose the comparison you intend
git diff compares working-tree content with the index by default. git diff --cached compares the index with HEAD. git diff HEAD compares current working content with the current commit for tracked paths. These answer different questions. Use -- before a path to separate path arguments from revisions and options. Empty unstaged diff output does not prove the whole repository is clean because staged changes and untracked files can remain. Combine status with both relevant comparisons. For a coherent commit, review the complete staged patch, including deletions, generated files, and accidental private content.
Unstage without erasing the edit
For a tracked path with an existing HEAD commit, git restore --staged moves the indexed version back to the chosen source, which defaults to HEAD for this mode. It leaves the working file intact. This differs from restoring the working-tree file, which can discard local edits and requires a separate decision. The example returns notes.txt to an unstaged-only modification. Avoid treating every restore or reset form as interchangeable undo. Read the selected destination and source, and distinguish index changes from working-tree changes before executing a command.
git restore --staged -- notes.txt
git diff --cached --name-only
git diff --name-only
Following the previous example, the cached name list is empty and the unstaged list contains notes.txt. The working file still contains draft two. This command is not used for a repository with no first commit, where HEAD is absent.
Build a commit as an explanation
A useful commit groups one understandable change with a message explaining intent and relevant constraints. It need not match an arbitrary number of files. Partial staging can separate independent changes, but the staged snapshot must remain coherent and testable; tests run against the working tree can include unstaged fixes that are missing from the commit. Verify the selected snapshot through an isolated checkout or a suitable workflow when that risk matters. Ignore rules prevent ordinary discovery of matching untracked paths, but they do not remove already tracked files or cleanse history.
Common errors and fixes
A commit omits the latest edit
- Why it happens
- The file was edited after staging.
- How to fix it
- Review the unstaged diff and stage the intended final content.
- Prevent it next time
- Inspect git diff --cached immediately before committing.
A blank diff is mistaken for a clean repository
- Why it happens
- Only working-tree versus index was compared.
- How to fix it
- Inspect status, staged changes, and untracked files.
- Prevent it next time
- Name the two states being compared.
An ignore rule does not stop tracking a file
- Why it happens
- The file is already present in the index.
- How to fix it
- Plan an explicit tracking change and review data exposure separately.
- Prevent it next time
- Add appropriate ignore rules before first staging.
Check your understanding
1. A file is staged, edited again, and committed. Which version enters the commit?
Show worked solution
The indexed version enters unless another option or staging operation changes that selection. The later working-tree edit remains outside the ordinary commit. Compare both diffs to verify rather than assuming commit captures every editor buffer.
2. Tests pass with an unstaged fix, but the staged change is incomplete. How should review improve?
Show worked solution
Review and test the actual selected snapshot in an isolated lab checkout or equivalent process. Stage the required fix if it belongs to the same change, or separate the changes coherently. Working-tree success alone does not establish the commit is complete.
Key takeaways
- Staging records content at a specific moment.
- Status and explicit diff pairs reveal different kinds of work.
- Unstaging and discarding edits are separate operations.