Checkpoint Lab — Undoing Changes, Reset, Restore, Revert, Reflog, and Lost-Work Recovery
Checkpoint recovery across separate disposable clones: restore an unstaged file, unstage safely, undo a private local commit, revert a published bad commit, recover a deleted branch, dry-run clean, preserve evidence, and exercise fsck discovery.
Learning objectives
- Choose the narrowest recovery tool for five distinct Git failure states.
- Predict ref/index/working-tree effects before each recovery command.
- Preserve published history by reverting instead of rewriting.
- Recover deleted/lost tips from reflog/object evidence and immediately create safety refs.
- Use clean dry-run and exact OID reporting as mandatory operational safeguards.
1. Checkpoint scenario — five failures in independent disposable clones
One recovery repository will act as the source of truth, with a local bare remote and multiple clones. Separating scenarios prevents one recovery action from hiding another and models the reality that reflogs are clone-local.
2. Build the source repository and bare remote
Git Bash, Bash, or zsh
mkdir git-recovery-checkpoint
cd git-recovery-checkpoint
git init --bare origin.git
git clone origin.git seed
cd seed
git switch -c trunk
git config user.name "Checkpoint Maintainer"
git config user.email "checkpoint-maintainer@example.invalid"
printf "mode=stable\nlimit=10\n" > app.conf
printf "# Recovery Checkpoint\n" > README.md
git add app.conf README.md
git commit -m "Create checkpoint baseline"
git push -u origin trunk
cd ..
git --git-dir=origin.git symbolic-ref HEAD refs/heads/trunk
git clone origin.git worktree-case
git clone origin.git staged-case
git clone origin.git local-commit-case
git clone origin.git published-case
git clone origin.git deleted-branch-case
Preflight: all remotes are local paths, identities are fake, and every clone is disposable.
3. Use this decision record for every scenario
1. Symptom:
2. Affected layer:
3. Desired source state:
4. Is the mistake published/shared?
5. Evidence captured:
6. Narrowest correction:
7. Verification:
8. Recovery fallback / safety OID:
Do not write “used reset” as the rationale. Record why that exact destination layer and source were selected.
4. Scenario A — recover an unstaged file
cd worktree-case
git config user.name "Case A"
git config user.email "case-a@example.invalid"
# Change mode=stable to mode=broken
git status --short
git diff -- app.conf
git show :app.conf
# Prediction: only working tree changes.
git restore -- app.conf
git status --short
git diff -- app.conf
cd ..
Decision: working tree was wrong; index held the desired version; nothing was published.
5. Scenario B — unstage the wrong file without discarding it
cd staged-case
git config user.name "Case B"
git config user.email "case-b@example.invalid"
printf "local experimental note\n" >> README.md
git add README.md
git status --short
git diff --staged -- README.md
# Prediction: index returns to HEAD; working edit remains.
git restore --staged -- README.md
git status --short
git diff -- README.md
git diff --staged -- README.md
cd ..
6. Scenario C — undo one unwanted private local commit
cd local-commit-case
git config user.name "Case C"
git config user.email "case-c@example.invalid"
printf "temporary=true\n" >> app.conf
git add app.conf
git commit -m "Temporary private experiment"
UNWANTED=$(git rev-parse HEAD)
PARENT=$(git rev-parse HEAD^)
git status --short
git log --oneline -3
git reflog -5
# Prediction: HEAD moves to PARENT, changes remain unstaged.
git reset --mixed HEAD^
git rev-parse HEAD
git status --short
git diff -- app.conf
# Preserve evidence of the old commit before cleaning up local file state.
git branch evidence-unwanted "$UNWANTED"
git restore -- app.conf
git status --short
cd ..
Decision: the commit had never been pushed; mixed reset changed local history while preserving its content long enough to inspect it, and a safety ref preserves the old commit.
7. Scenario D — correct a published bad commit with revert
cd published-case
git config user.name "Case D"
git config user.email "case-d@example.invalid"
printf "dangerous=true\n" >> app.conf
git add app.conf
git commit -m "Enable dangerous production mode"
BAD=$(git rev-parse HEAD)
git push origin trunk
git ls-remote origin refs/heads/trunk
git show --stat "$BAD"
# Prediction: BAD remains in history and a new inverse commit is added.
GIT_EDITOR=true git revert "$BAD"
REVERT=$(git rev-parse HEAD)
git log --oneline -4
git push origin trunk
git ls-remote origin refs/heads/trunk
cd ..
Decision: the bad OID was already shared through the remote, so additive history preserved provenance.
8. Scenario E — recover a deleted branch tip
cd deleted-branch-case
git config user.name "Case E"
git config user.email "case-e@example.invalid"
git fetch origin
git switch -c experiment origin/trunk
printf "valuable branch work\n" > experiment.txt
git add experiment.txt
git commit -m "Create valuable branch work"
LOST=$(git rev-parse HEAD)
git switch trunk
git branch -D experiment
git log --oneline --all --decorate --max-count=10
git reflog --all --date=local | head -30
git show "$LOST"
# Prediction: creating a new ref makes the commit reachable again.
git branch recovered-experiment "$LOST"
git merge-base --is-ancestor "$LOST" recovered-experiment
git log --oneline -2 recovered-experiment
cd ..
Decision: branch ref was deleted, but the commit still existed and local reflog/known OID evidence identified it. Recovery created a ref; no object reconstruction was necessary.
9. Mandatory clean dry-run demonstration
Use a separate clone so deleting an untracked file cannot affect the recovery scenarios.
git clone origin.git clean-case
cd clean-case
printf "local scratch only\n" > scratch.tmp
mkdir scratch-dir
printf "nested scratch\n" > scratch-dir/nested.tmp
git status --short
git clean -n
git clean -nd
# Stop here unless you verified that scratch.tmp is disposable.
git clean -f -- scratch.tmp
git status --short
test -f scratch-dir/nested.tmp
cd ..
-d changes scope. Do not
substitute broad git clean -fdx.
10. Preserve a compact evidence report
Git Bash / Bash / zsh
{
echo "published_bad=$BAD"
echo "published_revert=$REVERT"
echo "deleted_branch_tip=$LOST"
echo "local_unwanted=$UNWANTED"
} > recovery-evidence.txt
cat recovery-evidence.txt
This report is intentionally outside the repositories. In real operations, pair exact OIDs with incident/ticket/deployment identifiers in the system of record.
11. Optional lost-object inspection without pruning
In local-commit-case, temporarily remove the safety
branch only after copying its OID into the evidence report, then ask
fsck to ignore reflog roots:
cd local-commit-case
git branch -D evidence-unwanted
git fsck --no-reflogs --unreachable
git fsck --no-reflogs --lost-found
git show "$UNWANTED"
git branch recovered-unwanted "$UNWANTED"
cd ..
12. Verification checklist
- Scenario A ends clean with the working-tree file restored from the index.
- Scenario B keeps the README working edit but removes it from the index.
- Scenario C moves local HEAD back one commit, proves content became unstaged, and preserves the old OID under a safety/recovery branch.
- Scenario D leaves the bad published commit in ancestry and adds/pushes a distinct revert commit.
- Scenario E recreates a branch pointing at the deleted branch tip.
-
The clean demonstration ran
git clean -n/-ndbefore a narrow forced deletion. - Evidence report records exact old/revert/lost OIDs.
- No reflog expiry, prune-now, broad hard reset, or force push was used.
13. Knowledge check
Question 1. Why did Scenario B use restore --staged instead of reset --hard?
Question 2. Why was mixed reset appropriate in Scenario C but not Scenario D?
Question 3. What made the deleted-branch commit recoverable?
Question 4. Why did the clean lab delete only
scratch.tmp?
Question 5. If fsck reports a missing blob rather than an unreachable commit, what changes?
14. What Chapter 10 adds to a production Git operating model
You now have a recovery sequence that begins with evidence and layer identification, distinguishes local rewrite from shared correction, preserves old OIDs before invasive operations, and treats reflog/fsck/clean/GC as tools with specific boundaries. This lowers the chance that the recovery attempt destroys more information than the original mistake.
15. Chapter checkpoint summary
The safest Git recovery is not the most powerful command. It is the narrowest state transition that restores the intended layer while preserving unrelated work and shared provenance.
Authoritative references
git-restore
git-reset
git-revert
git-reflog
git-clean
git-fsck
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.