Repositories, Working Tree, Index, HEAD, and the File Lifecycle: Diagnostics, Failure Modes, Security, and Performance
Diagnose file-lifecycle failures without destructive guesses: wrong repository, overly broad staging, restore misconceptions, ignored files, case/file-mode surprises, path ambiguity, and detached HEAD.
Learning objectives
- Apply an evidence-first sequence to repository, index, pathspec, ignore, and HEAD problems.
- Recover from unintended broad staging without discarding working-tree content.
- Explain what git restore can and cannot recover by default.
- Diagnose ignored files and case/file-mode surprises with narrow read-only commands.
- Recognize detached HEAD as valid state and preserve useful unnamed commits by creating a ref.
1. Diagnostic sequence — identify the layer before changing it
- Preserve evidence: current directory, exact command, status output, diff output.
-
Locate the repository:
git rev-parse --show-toplevelandgit rev-parse --git-dir. -
Inspect HEAD/refs/history:
status --branch,symbolic-ref,log. -
Inspect index/working tree: ordinary and staged
diff,
ls-files. - Inspect config/ignore sources only when the symptom points there.
- Choose the least destructive correction, then repeat the inspection.
2. Failure mode — “nothing to commit” in the wrong repository
Imagine editing service-a/config.yml while your
terminal is in a different checkout. Git may truthfully report a
clean repository.
pwd
git rev-parse --show-toplevel
git status --short --branch
git ls-files -- config.yml
The fix is navigation/repository selection, not
git add -A or reset. In automation, prefer explicit
repository paths (for example
git -C /path/to/repo status) rather than relying on an
inherited working directory.
3. Failure mode — a broad pathspec staged too much
git add . stages matching changes beneath the current
directory. git add -A can stage
additions/modifications/deletions across the repository according to
its pathspec scope. After any broad add:
git status --short
git diff --staged --stat
git diff --staged
If one path should not be staged, remove only that path from the index proposal:
git restore --staged -- path/to/unwanted.txt
git status --short
git diff --staged
The working-tree copy remains. This is safer than discarding files or resetting the whole repository.
4. Failure mode — treating git restore as a time
machine
By default, git restore -- path copies from the index
into the working tree. It does not automatically search old commits
for a lost version. If you explicitly provide
--source=<tree>, then you are selecting a
historical source.
git diff -- path first.
git diff -- config.yml
git diff --staged -- config.yml
git restore -- config.yml
Recovery from committed/reflog history is Chapter 10. Do not teach “restore” as generic recovery from any past state.
5. Failure mode — “Git cannot see my file” because it is ignored
git status --short
git check-ignore -v -- path/to/file
git status --ignored --short
git config --show-origin --get core.excludesFile
The verbose ignore command identifies the matching rule and source.
If the file is already tracked, ordinary ignore rules do not hide it
from tracking; diagnose with git ls-files -- path.
6. Failure mode — case-only rename or file-mode noise
On case-insensitive filesystems,
Readme.md → README.md can be ambiguous to the OS/Git
interaction. Use an intermediate rename when needed and inspect the
staged summary. For executable-bit noise, inspect configuration and
mode-only diffs instead of disabling file-mode tracking globally.
git config --show-origin --get core.ignoreCase
git config --show-origin --get core.fileMode
git diff --summary
7. Failure mode — detached HEAD after checking out an object directly
Modern Git makes this explicit:
git status --short --branch
git switch --detach HEAD~1
git status --short --branch
git symbolic-ref -q HEAD
git rev-parse HEAD
In detached state, symbolic-ref exits non-zero because
HEAD is no longer attached to a local branch. Inspection is safe. If
you create useful commits, attach a branch before leaving:
git switch -c keep-this-work
If you made no commits and only inspected an old commit, return to
the previous branch with git switch -.
8. Intentionally broken example — a path/revision ambiguity
In a disposable repository, create both a branch and a file named
release. A command such as:
git diff release
can be interpreted as a revision argument rather than “diff only the path named release.” The error or unexpected output is evidence of ambiguous grammar, not corrupted state. Repair it by declaring the boundary:
git diff -- release
git diff HEAD -- release
Line by line, the first form compares working tree/index only for
the path; the second names HEAD as the revision
baseline and then release as the path.
9. Security failures relevant to this layer
- Broad pathspecs can stage local credentials, environment files, or build outputs. Review staged content before commit.
- Ignore rules are prevention; they are not secret revocation or history erasure.
- Shell expansion can make a pathspec touch more files than the runbook author intended. Quote Git patterns and prefer dry-run/inspection where supported.
- Detached-HEAD builds are normal in CI, but a pipeline must capture the exact commit OID rather than infer a branch name that may not exist locally.
10. Performance issues that actually belong here
Large working trees with many untracked files can make
git status slower because Git must discover filesystem
state. Do not solve that by hiding meaningful source files. Use
correct ignore rules for generated/cache directories and later
large-repository features where appropriate. Machine-readable
automation should prefer git status --porcelain rather
than parsing localized/human-oriented long status output.
11. Red-zone commands that do not belong in first-response diagnostics
git reset --hard,
git clean -fdx, force pushes, reflog expiry, object
pruning, or history-rewrite tools to fix a staging/path
problem.
They can destroy uncommitted or recoverable data. The failures in
this lesson are repaired with navigation, path-specific
unstaging/restoring, ignore/pathspec diagnosis, or
creating/switching refs.
12. Symptom → first evidence
| Symptom | First evidence | Likely layer |
|---|---|---|
| “Nothing to commit” | rev-parse --show-toplevel + status |
Repository/path |
| Too many staged files | diff --staged --stat |
Index/pathspec |
| File missing from status | check-ignore -v, ls-files |
Ignore/index |
| Case/mode-only diff | diff --summary + config |
Filesystem/config |
| “Not on any branch” |
status --branch, symbolic-ref
|
HEAD/ref |
13. Knowledge check
Question 1. Git says “nothing to commit,” but your editor shows unsaved-looking changes. What do you verify before staging anything?
git rev-parse --show-toplevel, the actual path, and
git status. You may be in another repository or
directory.
Question 2. You staged one secret file accidentally with a broad pathspec. What is the narrow first correction before any commit?
git restore --staged -- path/to/secret removes that
path from the index proposal while keeping the working-tree file.
Then fix ignore/secret handling policy.
Question 3. Does detached HEAD mean the repository is damaged?
Question 4. Why can git diff release be misleading
when both a branch and file are named release?
-- to
make the path boundary explicit.
14. Summary
File-lifecycle failures are usually location, pathspec, index, ignore, filesystem, or HEAD/ref problems—not reasons to run destructive recovery commands. Preserve evidence, identify the affected layer, make the narrowest correction, and verify.
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.