Repositories, Working Tree, Index, HEAD, and the File Lifecycle: Guided Hands-On Workflow and Core Operations
Move paths deliberately through Git's file lifecycle with status, add, partial staging, diff, restore, rm, mv, ls-files, and ignore diagnostics inside a disposable repository.
Learning objectives
- Compare working tree, index, and HEAD before and after each state-changing command.
- Stage whole files and selected hunks while preserving unstaged work.
- Unstage with modern git restore syntax and understand the legacy reset compatibility form.
- Track deletion and rename operations while distinguishing explicit file movement from inferred rename reporting.
- Inspect ignored and indexed paths with check-ignore and ls-files.
1. Create a disposable lifecycle lab
Git Bash, Bash, or zsh
mkdir git-lifecycle-lab
cd git-lifecycle-lab
git init -b trunk
git config user.name "Lifecycle Lab"
git config user.email "lifecycle@example.invalid"
printf "alpha\nbeta\ngamma\ndelta\n" > app.txt
printf "temporary.log\n" > .gitignore
git status --short
git add app.txt .gitignore
git diff --staged
git commit -m "Create lifecycle baseline"
git status --short --branch
PowerShell file-creation alternative
New-Item -ItemType Directory git-lifecycle-lab | Out-Null
Set-Location git-lifecycle-lab
git init -b trunk
git config user.name "Lifecycle Lab"
git config user.email "lifecycle@example.invalid"
@('alpha','beta','gamma','delta') | Set-Content app.txt
Set-Content .gitignore 'temporary.log'
git status --short
git add app.txt .gitignore
git diff --staged
git commit -m "Create lifecycle baseline"
git status --short --branch
2. Prove all three views agree at baseline
git status --short
git diff
git diff --staged
git ls-files --stage
git show HEAD:app.txt
Both diffs should be empty. The index and HEAD contain
the same snapshot, and the working-tree copy matches the index.
3. Stage a whole-file change
Change one line, inspect first, then copy that working-tree version into the index.
Git Bash / Bash / zsh
printf "alpha\nbeta changed\ngamma\ndelta\n" > app.txt
git status --short
git diff -- app.txt
git add -- app.txt
git status --short
git diff -- app.txt
git diff --staged -- app.txt
After git add, ordinary git diff for the
path becomes empty because working tree and index agree;
git diff --staged shows the proposed next snapshot
relative to HEAD.
4. Modify after staging — one path, three versions
Git Bash / Bash / zsh
printf "alpha\nbeta changed\ngamma\ndelta working-only\n" > app.txt
git status --short
git diff -- app.txt
git diff --staged -- app.txt
git show HEAD:app.txt
Short status should show changes in both columns for
app.txt. HEAD still has the original file; the index
has the staged beta change; the working tree has that
plus the new delta change.
5. Unstage safely with modern restore syntax
git status --short
git restore --staged -- app.txt
git status --short
git diff -- app.txt
git diff --staged -- app.txt
With --staged and no explicit source, Git restores the
index entry from HEAD. It leaves the working-tree file
alone. You should now see only a working-tree modification.
git reset HEAD -- app.txt to unstage. That form still
exists, but this course uses
git restore --staged because it names the destination
layer explicitly. Chapter 10 studies reset/recovery semantics in
depth.
6. Restore a working-tree path from the index
git diff -- app.txt
git restore -- app.txt
git status --short
git diff -- app.txt
Because the index currently matches HEAD, restoring the working tree
from the index returns app.txt to the committed
baseline. This does not search old history for “whatever version you
meant”; the default source is the index.
7. Partial staging with git add -p
Create two separated edits so Git offers separate hunks. Use a real text editor or the platform-specific example below, then inspect:
Git Bash / Bash / zsh
python - <<'PY'
from pathlib import Path
p = Path('app.txt')
text = p.read_text()
text = text.replace('alpha', 'alpha staged-candidate')
text = text.replace('delta', 'delta working-candidate')
p.write_text(text)
PY
git diff -- app.txt
git add -p -- app.txt
At the interactive prompt, choose y for the first hunk
and n for the second. If Git combines the edits into
one hunk, use s (split) when available or make the
edits farther apart. Then:
git status --short
git diff --staged -- app.txt
git diff -- app.txt
The staged diff should contain only the hunk you accepted. The ordinary diff should contain the working-tree hunk you declined.
8. Commit only the staged hunk
git diff --staged --check
git commit -m "Stage selected app change"
git status --short
git diff -- app.txt
The commit records exactly the index, not every working-tree change. The declined hunk remains as an uncommitted working-tree modification.
9. Discard only the remaining intended local edit
git restore -- app.txt will replace the working-tree
copy with the index version, which now matches the new HEAD after
the commit.
git diff -- app.txt
git restore -- app.txt
git status --short
10. Rename with git mv and inspect what changed
git status --short
git mv app.txt service.txt
git status --short
git diff --staged --summary
git ls-files
git commit -m "Rename app to service"
git mv moves the working-tree path and updates the
index. The eventual commit still stores snapshots; rename reporting
is inferred when comparing old and new trees.
11. Remove a tracked path with git rm
printf "remove me\n" > obsolete.txt
git add obsolete.txt
git commit -m "Add obsolete file"
git status --short
git rm -- obsolete.txt
git status --short
git diff --staged -- obsolete.txt
git commit -m "Remove obsolete file"
git rm removes the working-tree file and stages its
deletion. If you need to stop tracking but keep the working-tree
file, git rm --cached is a different policy choice—use
it deliberately rather than as a generic cleanup command.
12. Inspect ignored files instead of guessing
printf "local runtime output\n" > temporary.log
git status --short
git check-ignore -v -- temporary.log
git ls-files --others --ignored --exclude-standard
check-ignore -v shows which exclude source and pattern
matched. The file is absent from ordinary status because it is
intentionally ignored.
13. Challenge — choose the destination layer
For each requirement, choose a command before looking back:
- Keep a working-tree edit but remove it from the proposed next commit.
- Discard a working-tree edit and restore the index version.
- Stage only one hunk from a two-hunk file.
- Show every path currently in the index.
- Find which ignore rule hides
temporary.log.
14. Cleanup
Git Bash / Bash / zsh
git status --short --branch
cd ..
pwd
rm -rf git-lifecycle-lab
PowerShell
git status --short --branch
Set-Location ..
Get-Location
Remove-Item -Recurse -Force git-lifecycle-lab
15. Knowledge check
Question 1. After staging a file and editing it again, what
does git commit record by default?
Question 2. What exactly did
git restore --staged -- app.txt change in this
lab?
app.txt, restoring it from HEAD
by default. The working-tree copy remained untouched.
Question 3. Does git mv create a permanent rename
object?
Question 4. Why did temporary.log disappear from
ordinary git status?
git check-ignore -v identifies the rule.
16. Summary
You moved content among working tree, index, and commit history; staged only selected hunks; unstaged without discarding work; restored only the working-tree layer; and tracked rename/deletion/ignore behavior with inspection commands before and after every mutation.
Authoritative references
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.