Git, CI/CD & GitOpsFinal · Final AssessmentFinal Review
Git internals and recovery — recap
What you'll learn
- Name the four Git object types and what each one is for
- Trace a commit hash from working tree through tree to blob and explain why the store is tamper-evident
- Distinguish revert from reset and pick the right one for shared versus local history
- Recover a deleted branch, a dropped commit, or a forced-overwritten ref using the reflog
- Plan a filter-repo history rewrite after a secret leak and the force-push protocol that must follow
Prerequisites
- The three object types — blob, tree, commit, tag
- Object IDs and hashing — what the SHA covers and why collisions matter
- Blob objects — content only, no filename, deduplicated by hash
- Tree objects — directory entries, mode bits, and recursive sub-trees
- Commit objects — parents, tree pointers, author versus committer, signatures
- Tag objects — annotated tags, lightweight tags, and signed tags
- The commit DAG — what Git is really a graph of
- Graph topology and merges — what merge commits actually encode
- Refs and the refs namespace — refs/heads, refs/tags, refs/remotes
- Packed refs and the reflog — when refs are packed, and how the reflog records every ref change
- What revert does — producing a new commit that undoes a change
- Reset modes explained — --soft, --mixed, --hard, --merge, --keep
- What the reflog records — every ref update, every time, with a reason
- Recovering from a hard reset — the precise reflog recipe and the boundaries of recovery
- Recovering a deleted branch — the reflog is the recovery path
- git filter-repo — the modern replacement for git filter-branch
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
The first part of the course established that Git is a content-addressed store; the second part established that recovery flows from the reflog and from history rewriting. This recap compresses both into the working knowledge the final assessments will test: the four object types, the shape of the commit DAG, the difference between revert and reset, and the four operations every on-call engineer must be able to execute under pressure.
The four object types and the tamper-evident store
A Git repository stores four kinds of objects, each identified by the SHA of its contents:
flowchart LR
A["working tree file"] -->|git hash-object| B["blob"]
B --> C["tree"]
C --> D["commit"]
D --> E["annotated tag"]
D -->|points to| F["parent commit"]
F --> D
- Blob. The bytes of a single file. A blob has no name and no path; two files with identical contents in different directories share one blob.
- Tree. A directory listing: a set of
(mode, name, blob-or-tree-SHA)entries. Trees are how Git records paths. - Commit. A root tree SHA, zero or more parent commit SHAs, an author and committer, and a message. A commit is the unit of history.
- Tag (annotated). A name, a target object SHA, a tagger, and a message. Annotated tags are first-class objects; lightweight tags are raw refs that name a commit directly without an intermediate object.
Refs, HEAD, and the reflog
Refs are the layer humans and tools actually navigate:
branches, tags, and HEAD. They live under .git/refs
(with packed refs in .git/packed-refs for efficiency).
A branch is a movable pointer to a commit; HEAD is the
pointer to the currently checked-out ref.
The reflog records every movement of HEAD and every
update of branch tips. It is local to each clone,
stored in .git/logs/, and retained for 90 days by
default. The reflog is the safety net under every other
recovery operation:
flowchart LR
A["git reset --hard HEAD~3"] --> B["HEAD moves back 3 commits"]
B --> C["reflog records old HEAD@{0}"]
C --> D["git checkout HEAD@{0}"]
D --> E["working tree restored"]
E --> F["old commits still reachable"]
F -->|"until 90 days elapse"| G["git gc prunes unreachable objects"]
A git reset --hard wipes the working tree, but the prior
commit is still reachable from the reflog for up to 90
days. Recovery is a git checkout of the reflog entry;
re-attachment is git branch rescue-branch <sha>. A
git gc is what actually destroys history, and a reflog
expiry is what causes a git gc to consider an entry
unreachable.
Revert versus reset — the shared-history rule
The two ways to undo a change have opposite effects on shared history:
git revert <sha>creates a new commit that inverts the target commit’s changes. History grows; the bad SHA remains in the DAG. Safe for any branch that other people have based work on.git reset --hard <sha>moves the current branch tip backwards. The intermediate commits become unreachable; they remain in the reflog briefly and are then garbage collected. Never safe on a branch that other people have based work on.
The four recovery operations
The four operations every infrastructure engineer must be able to perform under pressure:
- Navigate the reflog.
git reflogshows the reflog;git checkout <sha-from-reflog>restores the working tree to that state;git branch rescue <sha>re-attaches a branch name to the recovered commit. - Revert a shared commit.
git revert <sha>produces a new commit that undoes the bad change; the history is preserved and the audit trail is intact. - Reset a local branch.
git reset --hard <sha>moves the branch tip back; the old commits are still in the reflog for recovery within 90 days. - Rewrite history with
git filter-repo. The modern replacement forgit filter-branch. Used after a committed secret leak to strip the secret from every commit in the history. After rewriting, every clone must re-clone, the old refs must be deleted on the remote, and any tag pointing at the old history must be re-cut.
Production discipline
The four things the on-call engineer must be able to do without thinking:
- Read a reflog. A reflog entry of the form
<sha> HEAD@{N}: <action>: <message>is the first thing you reach for when “the branch is gone” or “the tree is empty” or “the last commit was wrong”. - Revert before reset on shared history. The choice is not about preference; it is about whether anyone else has a clone. If yes, revert. If no, reset is faster and the reflog still has your back.
--force-with-lease, never--force. A bare--forceoverwrites the remote unconditionally and is the proximate cause of most overwritten-deploy incidents.--force-with-leaserefuses to push if the remote tip has moved since you last fetched.- Treat a secret leak as a forensic operation. A filter-repo rewrite without coordinating every clone, every tag, every signed commit, and every registry reference is not a fix; it is a partial fix that leaves the secret recoverable from anywhere a clone was not refreshed. Rotate the secret first, then rewrite history, then invalidate every reference that could still carry the old SHA.
Cross-course references
- Linux for Production Sysadmins — Parts covering
filesystem forensics and recovery: the same shape
applies to a Git repo. The object store is recoverable
until it is not; act on the assumption that
unreachable objects will be reclaimed on the next
gc. - Terraform for Production Sysadmins — State recovery
has the same shape: state is recoverable until the
bucket is purged, and a
terraform state rmis recoverable only if you still have the old state file.
Quiz
Knowledge check · 4 questions
Q1. An engineer accidentally runs `git reset --hard HEAD~3` on a feature branch three other engineers have cloned. What is the correct recovery path?
Q2. A `git filter-repo` history rewrite changes the SHA of every rewritten commit, which is why every clone must re-clone and every tag must be re-signed after such a rewrite.
Q3. Name the four Git object types and the command that recovers a deleted branch from the reflog.
Q4. A secret AWS access key was committed to the main branch two weeks ago and has since been deployed. The security team wants the secret removed from Git history. Walk through the operations, in order, that produce a defensible result.
An engineer notices a personal AWS access key in `infra/terraform/main.tf` on the `main` branch. The commit is two weeks old, has been merged, has been deployed via a Terraform pipeline, and is referenced by a signed tag `v3.4.0`. The key has been used from a developer laptop, not from the CI runner, and CloudTrail shows accesses from outside the corporate IP range in the last 48 hours.
Passing score: 75%. Answers are checked in this browser.