Chapter 18Lesson 04~135 minutes

Plumbing Commands, Index Internals, cat-file, hash-object, and commit-tree: Diagnostics, Failure Modes, Security, and Performance

Diagnose unreachable objects, invalid index tuples, accidental live-index replacement, wrong-repository environment overrides, unsafe filename parsing, alternate-object failures, and unstable debug-output parsing without destroying evidence.

DiagnosticsUnreachable objectsIndex recoveryPath safety

Learning objectives

  • Separate object existence from reachability and retention.
  • Interpret and repair invalid manual path/mode/index operations in disposable state.
  • Recover a deliberately emptied live index from HEAD without hard reset.
  • Detect repository targeting errors caused by inherited Git environment variables.
  • Use NUL-safe path interfaces and avoid treating debug/internal formats as stable APIs.

1. Diagnostic sequence — preserve the object/index/ref evidence first

  1. Preserve evidence: Git version, cwd, repository identity, environment overrides, status, index entries, refs, and candidate object IDs.
  2. Inspect read-only: rev-parse, status --porcelain=v2, ls-files --stage, cat-file, show-ref.
  3. Identify the affected layer: object database, index, working tree, ref, environment/repository discovery, or parser.
  4. Choose the least destructive correction.
  5. Verify state from an independent interface.

2. Capture a low-level evidence bundle

git --version
pwd
git rev-parse --show-toplevel
git rev-parse --absolute-git-dir
git rev-parse --git-path index
git rev-parse --show-object-format
git status --porcelain=v2 --branch
git ls-files --stage
git show-ref
git rev-parse --local-env-vars

If repository identity is already suspect, do not run low-level mutating commands until these paths make sense.

3. Failure mode — writing an object and forgetting that no history reaches it

In a disposable repository:

printf "orphan evidence\n" > orphan.txt
ORPHAN=$(git hash-object -w orphan.txt)

git cat-file -t "$ORPHAN"
git cat-file -p "$ORPHAN"
git rev-list --objects --all | grep "$ORPHAN" || echo "not reachable from refs"
git fsck --unreachable --no-reflogs

The object exists, so cat-file succeeds. It is absent from normal reachable history because no tree/commit/ref path includes it. The correction is not to edit the object file; either construct the intended reachable tree/commit/ref chain or accept that it was temporary.

4. Unreachable does not mean permanently recoverable

Do not promise that an unreachable object will remain forever. Garbage collection and pruning can eventually remove unreachable objects according to repository policy and timing. During investigation, do not run aggressive pruning/reflog-expiry commands.

5. Intentionally broken example — a manual index entry uses an invalid repository path

BLOB=$(printf "demo\n" | git hash-object -w --stdin)
git update-index --add --cacheinfo "100644,$BLOB,../outside.txt"

A current Git error is conceptually:

error: Invalid path '../outside.txt'
fatal: git update-index: --cacheinfo cannot add ../outside.txt

The path attempts to escape the repository worktree namespace. Git rejects it. The repair is to supply a valid repository-relative path, for example:

git update-index --add --cacheinfo "100644,$BLOB,inside.txt"
git ls-files --stage -- inside.txt

6. Manual mode mistakes can make index/tree semantics differ from the filesystem

A mode is part of the index/tree entry, not inferred later from file contents. If automation registers a script blob as 100644 when the intended repository mode is executable 100755, Unix-like checkouts may not be executable. On filesystems where executable bits are unreliable, core.filemode affects change detection. Use git ls-files --stage to verify the recorded mode.

7. Intentionally broken example — emptying the live index makes every tracked path look staged for deletion

Disposable repository only. read-tree --empty directly replaces the selected index. Do not run this in valuable work.
git status --short
git ls-files --stage

git read-tree --empty
git status --short
git ls-files --stage

With a previously committed file still present in the working tree, status can show a staged deletion plus an untracked working copy. Git has not deleted the file; the live index was replaced with an empty snapshot.

Repair from the committed tree

git read-tree HEAD
git status --short
git ls-files --stage

This restores the index from HEAD without needing a hard reset. The working-tree file was left untouched throughout.

8. Prevention — redirect dangerous index experiments before they start

ALT_INDEX="$PWD/recovery-lab.index"
GIT_INDEX_FILE="$ALT_INDEX" git read-tree --empty
GIT_INDEX_FILE="$ALT_INDEX" git ls-files --stage
git ls-files --stage

The normal index remains intact. Isolation is cheaper than recovery.

9. Failure mode — environment variables make Git operate on an unintended repository

Imagine a process inherited GIT_DIR=/srv/repo-a/.git and then changed directory into repo B. Its Git commands can still resolve repo A's metadata.

env | grep '^GIT_' || true
git rev-parse --absolute-git-dir
git rev-parse --show-toplevel
git rev-parse --local-env-vars

The safe correction is to clear or deliberately set the relevant environment before mutation. Do not infer the target repository only from pwd.

10. Cross-repository tooling should sanitize inherited repository environment

Hooks and parent Git processes may export repository-specific variables. A tool that invokes Git against a second repository should use an explicit process environment and repository path rather than inheriting every GIT_* override accidentally.

11. Failure mode — parsing filenames with whitespace splitting

This common shell pattern is unsafe:

for path in $(git ls-files); do
  echo "$path"
done

Spaces, tabs, or newlines can split one pathname into several shell words. Use a NUL-delimited interface and a language/runtime API that reads NUL records. In Bash, a demonstration looks like:

git ls-files -z |
while IFS= read -r -d '' path
do
  printf 'path=%q\n' "$path"
done

PowerShell and other runtimes should use their own byte/record-safe handling rather than copying Bash syntax.

12. Failure mode — treating internal/debug output as a timeless compatibility guarantee

A script may parse git ls-files --debug because it exposes cache details. Current documentation explicitly warns that the exact debug format may change. The repair is to identify the actual needed fields and use documented --stage, --format, porcelain v2, or another stable interface.

13. Failure mode — an alternate object store disappears

A repository configured to borrow objects can work until the alternate directory is moved, unmounted, or cleaned. Object lookup then fails even though local refs still name commits that conceptually belong to the repository.

git rev-parse --git-path objects/info/alternates
git fsck --full

Operational repair depends on why alternates were used: restore the required object store, repopulate local objects from an authoritative source, or migrate away from the dependency. Do not delete refs merely because objects are temporarily unavailable.

14. Failure mode — one environment has replacement refs and another does not

git replace -l
git show <object>
git --no-replace-objects show <object>

If these views differ, record that replacement refs affect interpretation. Replacement refs are advanced local/history tooling and should not silently contaminate forensic conclusions.

15. Security relevance — environment and path handling can redirect trusted automation

Low-level tooling often runs with broad filesystem access in CI agents or developer tools. An attacker-controlled path, inherited GIT_DIR/GIT_WORK_TREE, or unsafe filename parser can make automation inspect or mutate unintended data. Validate repository roots, use -- before path arguments, keep paths NUL-safe, and avoid shell eval around untrusted repository content.

16. Performance relevance — batch object queries and index design matter at scale

Repeatedly spawning git cat-file for hundreds of thousands of objects is much slower than documented batch modes. Large indexes may benefit from current index features, but changing index format/extensions should follow measurement and ecosystem compatibility testing. Do not optimize by bypassing Git's documented interfaces and parsing internal files yourself.

17. Red-zone operations that reduce recovery options

During low-level diagnosis, do not expire reflogs, prune unreachable objects, force-update valuable refs, rewrite history, or delete unknown repository metadata. Those operations can erase the evidence needed to understand how the object/index/ref state became inconsistent.

18. Symptom → affected layer → verification

Symptom Likely layer Verification
Object exists but not in history Reachability/ref graph cat-file + rev-list --objects --all
All files appear staged deleted Index replaced/emptied ls-files --stage + compare HEAD
Git reports unexpected project Discovery/environment rev-parse --absolute-git-dir --show-toplevel
Script breaks on odd filename Parser framing Switch to -z/NUL records
Refs exist but objects missing Alternate object dependency Inspect alternates + fsck

19. Knowledge check

Question 1. Why is an object written by hash-object -w not automatically protected by reachability?

Question 2. After read-tree --empty, why can a tracked file appear both staged deleted and untracked?

Question 3. Why is pwd insufficient to prove which repository a Git command will use?

Question 4. Why is git ls-files --debug a bad long-term parsing contract?

Question 5. What should happen before any pruning during an unreachable-object investigation?

20. Summary

Most plumbing failures become understandable once you classify the wrong layer: unreachable object, malformed index tuple, unintended live-index mutation, wrong repository environment, unsafe path parser, or external object dependency. The safest corrections preserve evidence and repair the narrowest layer rather than applying destructive repository-wide commands.

Next

Checkpoint the full blob → index → tree → commit → ref chain

Lesson 5 starts from an empty repository, predicts each state transition, verifies it through porcelain, and repeats index work with a separate index file.

Authoritative references

 git-update-index
 git-read-tree
 git-ls-files
 git-fsck
 gitrepository-layout
 git-replace

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.