Git Bible

GIT BIBLE

Production-Grade Internals, Operations, Recovery, and Release Engineering

Operating principle: every Git problem is CURRENT STATE → DESIRED STATE → STATE TRANSFORMATION → VERIFICATION.

Audience: Senior / Staff / Principal Engineers, SREs, DevOps, Release Engineers, Infrastructure Engineers, Engineering Managers, and teams operating production repositories.

Validation baseline: command workflows in this artifact were experimentally exercised on Git 2.47.3 in disposable repositories. Authoritative Git/GitHub/GitLab documentation was also reviewed during authorship; where current documentation may be newer than the local test binary, that distinction is explicit.


How to use this manual

This is not a command catalog. It is a state-model reference. Start by identifying what Git state actually exists; do not mutate the repository because someone described the situation informally. The supplied specification explicitly requires a current-state/desired-state model and a diagnosis-before-mutation workflow. Source specification: state-first command teaching.

For incident pressure, read only the first layer of the relevant procedure. For learning, follow the cross-references into the state model, DAG, refs, index, reflog, and plumbing sections.

Four levels of every important operation

this manual deliberately moves through four levels:

  1. Human intention — what the engineer is trying to accomplish.
  2. Git abstraction — move a ref, replay a patch, restore a tree into the index, create a merge commit, etc.
  3. State transition — exact effects on HEAD, branch refs, index, working tree, DAG and remotes.
  4. Implementation / plumbing — objects, object IDs, refs, reflogs, index stages, and low-level operations.

Incident operating sequence

STOP
↓
INSPECT
↓
IDENTIFY LAST KNOWN GOOD STATE
↓
SAVE CURRENT STATE IF NECESSARY
↓
CHECK REFLOG / OBJECTS
↓
MODEL CURRENT DAG
↓
DEFINE DESIRED DAG
↓
SELECT THE LEAST DESTRUCTIVE SUITABLE TRANSFORMATION
↓
EXECUTE
↓
VERIFY

Never let “least destructive” mean “always preserve every historical shape.” The correct operation is the one that reaches the desired state while satisfying the collaboration, audit, deployment, and repository-policy constraints.

Part I — Git Mental Model

1. Git is an object database plus references

Git stores content in a content-addressed object database and names useful entry points with references. A commit refers to a tree and one or more parent commits. A tree refers to blobs and subtrees. Branch names are refs that typically point to commits. Tags are refs with different semantics, and annotated tags themselves are tag objects that can point to another object.

refs / names
                      │
                      ▼
               ┌────────────┐
               │   commit   │
               │ tree +     │
               │ parents    │
               └─────┬──────┘
                     │
                     ▼
                  ┌─────┐
                  │tree │
                  └──┬──┘
                     │
             ┌───────┴────────┐
             ▼                ▼
          ┌──────┐         ┌──────┐
          │ blob │         │ tree │
          └──────┘         └──────┘

Git does not store a branch as “a folder containing copied files.” A branch is a movable reference. Creating a branch normally creates or updates a ref; it does not duplicate the commit graph.

2. The three working-state views

Use these as three snapshots, not three synonyms:

View Conceptual meaning Typical inspection Can differ from the others?
HEAD The commit checked out, or the symbolic branch ref followed by HEAD git rev-parse HEAD, git symbolic-ref HEAD Yes
Index The next snapshot Git will commit; during conflicts it can hold multiple stages git diff --cached, git ls-files -u Yes
Working tree Files currently checked out on disk git diff, git status Yes
HEAD commit        A---B---C
                           │
                           │ tree(C)
                           ▼
Index              staged snapshot
                           │
                           ▼
Working tree        staged + unstaged filesystem changes

The canonical inspection triad

git status
git diff
git diff --cached
git diff HEAD
git rev-parse HEAD

Interpretation:

3. Formal notation used throughout

Let:

Symbol Meaning
W working tree state
I index state
H commit named by HEAD
R current local branch ref, when HEAD is attached
G commit DAG / reachability graph
O object database
RT local remote-tracking refs such as refs/remotes/origin/main
RR remote refs on the remote repository

This is descriptive, not a formal proof calculus. Use it to predict effects before executing commands.

4. State-transition matrix

Current state Desired state Primary transformation Core state that changes What remains conceptually intact Reversibility / risk
untracked file tracked+staged git add index working-tree bytes easy
modified tracked file staged git add index working tree easy
staged change unstaged change git restore --staged / git reset <path> index working tree easy
staged snapshot committed git commit object DB + current ref working tree contents normal
bad local commit, preserve changes previous commit as tip git reset --soft / mixed ref + maybe index objects generally retained local rewrite
bad local commit, discard changes previous commit as tip and content git reset --hard ref + index + working tree old objects may remain via reflog high
shared bad commit history preserved git revert new commit old DAG low/moderate
change exists on A, needs on B patch replayed on B git cherry-pick new commit + ref source commit remains depends on conflicts
two divergent lines need combined history one DAG with both parents git merge new merge commit or fast-forward topology if true merge normal if intentional
feature should appear as if based on newer base rewritten feature commits git rebase new commits + ref old objects temporarily history rewrite
deleted/rewound branch prior tip recovered reflog + new ref ref underlying object if retained time-sensitive
local refs stale vs remote local remote-tracking state updated git fetch object DB + refs/remotes/* + FETCH_HEAD working tree normally safe
remote branch needs fast-forward remote ref advanced git push remote ref local history normal
remote branch must be rewritten remote ref moved backward/sideways force push with policy/lease remote ref remote object DB may retain old commits high

Rule: the same command can be a very different operational choice depending on whether the affected history is private, private-but-pushed, shared, protected, or release/public history.

5. CURRENT STATE → DESIRED STATE worksheet

Use this before any history rewrite:

  1. What is the exact current HEAD?
  2. What local branch ref does it follow?
  3. What does the index contain?
  4. What differs in the working tree?
  5. What are the relevant remote-tracking refs?
  6. What commits are actually reachable from each important ref?
  7. Which commits are already shared?
  8. Which commit(s) must exist at the end?
  9. Which historical relationships must remain?
  10. What does the deployment/release system consider authoritative?
git status
git branch --show-current
git log --graph --decorate --oneline --all --reflog
git branch -vv
git remote -v
git reflog --all -20

Part II — Git Object Model

6. Objects: blob, tree, commit, tag

Git’s core loose object types are blob, tree, commit, and tag. Object identity is based on the object’s bytes under the repository’s hash algorithm. A repository may use SHA-1 or SHA-256 object formats; the important operational property is that changing object content changes its object ID.

Commit object

tree <tree-id>
parent <parent-id>          # zero or more lines
author ...
committer ...

commit message

Because the parent IDs, tree ID, author/committer metadata, and message contribute to the commit object, changing a parent relationship normally changes the commit ID. This is the mechanical reason a rebase creates different commits.

Tree object

mode type object-id path
100644 blob  <oid>  src/main.c
040000 tree  <oid>  src/lib

Trees describe a directory snapshot. A commit points to exactly one top-level tree.

Blob object

A blob is file content without the pathname. The pathname is represented by the tree entry that points to the blob.

Annotated tag object

An annotated tag is a first-class tag object that can carry a tagger identity, message, and optional signature, and points at another object. A lightweight tag is simply a ref directly naming an object.

7. Object inspection

git cat-file -t <oid>
git cat-file -p <oid>
git ls-tree <commit-or-tree>
git rev-parse <name>
git hash-object <file>
git fsck --full

Experimental observation

The validation harness constructed a tree with git write-tree, inspected it using git ls-tree, inspected object types using git cat-file -t, and constructed a commit with git commit-tree. It then moved a branch ref with git update-ref. This verified the conceptual porcelain→plumbing path described later.

8. Reachability: the hidden variable behind “deleted” commits

An object can stop being reachable from a visible branch while still existing in the object database. Reflogs, the index, remote-tracking refs and other references can keep objects alive. Git maintenance eventually prunes objects that are no longer protected by the repository’s reachability/retention mechanisms.

Do not equate “no branch points to it” with “object immediately deleted.” This distinction explains why reflog-based recovery often works.


Part III — HEAD / Index / Working Tree

9. HEAD semantics

Attached HEAD is normally a symbolic ref:

git symbolic-ref HEAD
# e.g. refs/heads/main
git rev-parse HEAD
# e.g. <commit-id>

Detached HEAD instead names a commit directly. That is why git switch --detach <commit> can create useful temporary states without moving any branch ref.

State HEAD Current branch ref moves when you commit? Operational consequence
attached ref: refs/heads/main yes normal branch workflow
detached direct commit ID no commits are reachable only through some other ref until you create one

10. Index: the staging snapshot and conflict database

The index is a binary file that records the next tree Git can write. In the normal state, each path has one stage. During an unmerged conflict, a path can have multiple stages: stage 1 common ancestor, stage 2 “ours” (HEAD) and stage 3 “theirs” (MERGE_HEAD) during a normal merge. Inspect with git ls-files -u.

git ls-files --stage
git ls-files -u

For merge conflict education, remember: the index is where unresolved structure lives; the working tree is where the human resolves it.

11. Partial staging

Use the index deliberately when one file contains multiple logical changes.

git add -p
git diff --cached
git diff
git restore --staged path/to/file
git reset -p

Pattern: split one file into reviewable commits

working tree
├── security fix
├── debug logging
├── formatting noise
└── unrelated feature
      │
      ├── git add -p → index contains only security fix
      └── commit → clean, surgical commit

Use git add --patch to select hunks into the index. Use git diff --cached as the pre-commit proof that the commit contains exactly what you think it contains.


Part IV — Commits, DAGs, and Revision Algebra

12. Git is a DAG, not a timeline

A---B---C---D main
     \
      E---F feature

Each commit points backward to its parent(s). A normal commit has one parent; a root commit has zero; a true merge commit has two or more parents. This makes Git history a directed acyclic graph (DAG).

Fast-forward

before
A---B main
     \
      C feature

after git merge feature
A---B---C main,feature

A fast-forward does not create a merge commit. The receiving branch ref simply moves to the descendant commit.

True merge

before
A---B---C main
     \
      D---E feature

after git merge feature
A---B---C---M main
     \\   /
      D---E  
M has parents C and E

A true merge creates a commit with both branch tips as parents and a tree representing the merged result. The merge operation therefore preserves the fact that two histories were integrated.

13. Ancestry and reachability queries

git merge-base A B
git merge-base --is-ancestor A B
git rev-list A
git rev-list --ancestry-path A..B
git branch --contains <commit>
git branch --merged
git branch --no-merged

merge-base gives a common ancestor useful for three-way reasoning. --is-ancestor A B asks whether A is reachable from B. This is the primitive behind many fast-forward / divergence decisions.

14. Revision syntax without magic

Syntax Meaning Key trap
HEAD commit named by HEAD attached vs detached both resolve to a commit
HEAD~1 first-parent ancestor, one generation follows first parent only
HEAD~2 first-parent ancestor, two generations not “parent #2”
HEAD^ first parent of commit on merge commit this is parent 1
HEAD^2 second parent of a merge commit invalid for a normal one-parent commit
A..B commits reachable from B but not A in set terms, not simply textual “between”
A...B symmetric difference of reachability sets; often used to describe commits on either side of a merge base context depends on consuming command
A^..B includes A itself plus descendants reachable from B but not before A under the usual revision-set semantics useful for cherry-pick/log ranges
@{-1} previous checkout/switch location stored in reflog-ish checkout state
branch@{yesterday} branch reflog entry at the specified time needs reflog data
HEAD@{1} previous HEAD reflog entry reflog is local state, not remote history

Authoritative revision syntax is documented by Git’s gitrevisions manual.

Why A..B ≠ A...B

For reachability sets:

A..B  = Reach(B) \ Reach(A)
A...B = (Reach(A) \ Reach(B)) ∪ (Reach(B) \ Reach(A))

In merge-base terms, A...B commonly describes the commits on either side of the divergence. Do not treat it as a synonym for “the diff”. Commands such as git diff A...B have their own defined semantics: compare the merge base of A and B to B. Always check the command’s revision interpretation when scripting around ranges.


Part V — Refs, Branches, Tags, and Remote-Tracking State

15. Reference namespaces

Ref Meaning Stored where Typical role
refs/heads/main local branch ref local repo movable branch pointer
refs/tags/v1.2.3 tag ref local repo release name
refs/remotes/origin/main local remote-tracking ref local repo last-observed remote branch tip
HEAD symbolic/direct pseudoref repo metadata current checkout target
ORIG_HEAD safety pseudoref used by drastic movements repo metadata pre-operation pointer
FETCH_HEAD fetched remote refs from most recent fetch repo metadata fetch result / scripts
MERGE_HEAD commit(s) being merged while merge is in progress repo metadata merge state
CHERRY_PICK_HEAD commit currently being cherry-picked on conflict repo metadata sequencer state
REBASE_HEAD commit currently associated with stopped rebase repo metadata rebase state

Branch creation is cheap

git branch feature-a main
git show-ref --heads
git rev-parse refs/heads/feature-a

The new branch initially names the same commit as the source. No commit objects are copied. The next commit made while feature-a is checked out creates a new commit and advances only that branch ref.

16. Refs and precise mutation

git show-ref
git for-each-ref
git update-ref refs/heads/experiment <new-oid> <expected-old-oid>

git update-ref is the controlled plumbing interface for changing refs. Its old-OID argument supports compare-and-swap-style safety: move the ref only if it still equals what you expected. This is the same class of primitive you want in automation that manipulates history precisely.


Part VI — Core Commands as State Transformations

17. git add

Human intention: put selected working-tree content into the next commit.

State transformation: W → I for selected paths/hunks. HEAD does not move. The working tree remains as it was.

git add path/to/file
git add -p

Verification: git diff --cached.

18. git commit

Human intention: make the staged snapshot durable in history.

State transformation: write tree from the index → create commit object → advance current branch ref → attached HEAD follows the branch. The working tree is generally not rewritten by the core commit operation.

git diff --cached
git commit -m "Describe the change"
git rev-parse HEAD
git show --stat --oneline HEAD

HEAD moves because it follows the branch ref; it does not move as a separate independent branch pointer.

19. git restore vs git reset vs git revert

Command Primary purpose Moves branch tip? Index effect Working tree effect History effect
restore restore paths from a source snapshot No optional yes none
reset (commit form) move current branch to a target Yes mode-dependent mode-dependent rewrites current ref
revert record inverse change as new commit Yes, by adding a new commit through commit through commit preserves original history

Use git restore for file-state restoration, git reset for pointer/index/worktree manipulation, and git revert when the desired public/shared-history operation is “make a new commit that undoes the effect of an old commit.” This distinction is part of Git’s own command overview.

20. Reset: soft / mixed / hard

Mode Branch/HEAD Index Working tree
--soft moves to target unchanged unchanged
--mixed moves to target reset to target unchanged
--hard moves to target reset to target updated to target, discarding tracked changes that block the reset

Git also supports --merge, --keep, path-limited reset and patch mode. Do not flatten these into the three common modes when working on complex merges.

Reset state example

BEFORE
HEAD -> C
branch main -> C
INDEX = tree(C) + staged S
WORKTREE = tree(C) + staged S + unstaged U

git reset --soft HEAD~1

AFTER
HEAD -> B
branch main -> B
INDEX = tree(C) + staged S
WORKTREE = tree(C) + staged S + unstaged U
git reset --soft HEAD~1
git status
git diff --cached
git diff

Reset is not object deletion

Changing a branch ref makes the old tip less reachable from that ref. The commit object may still be reachable via reflogs or other refs, and Git’s maintenance rules determine when unreachable data becomes eligible for pruning.

21. Revert: history-preserving undo

A---B---C---D
          │
          └── revert C,D → E,F

A---B---C---D---E---F

git revert creates new commits that reverse the selected patches. This is normally the right conceptual tool when shared history must remain intact. It does not make the original commits cease to exist.

git revert <commit>
git log --graph --decorate --oneline -6

Merge revert is special

Because a merge has multiple parents, reverting a merge requires choosing a mainline parent with -m <parent-number>. This is not “undo both parents”; it means produce the inverse of the merge relative to the selected mainline.


Part VII — Merge / Rebase / Cherry-Pick

22. Merge

Decision model

Use merge when the desired result is to integrate histories and preserve the fact that they were independently developed. Whether that creates a new merge commit depends on fast-forward rules and flags.

Mode Possible topology New commit? Use when
default --ff fast-forward if possible, otherwise true merge sometimes normal integration where fast-forward is acceptable
--no-ff always records merge node yes topology should record branch integration even if FF possible
--ff-only fast-forward only no policy requires no merge commit
--squash applies merge result to index/worktree, no merge commit metadata not by the merge command itself want one resulting commit without recording merge ancestry
git fetch origin
git merge --ff-only origin/main

Conflict state

During a true merge conflict, HEAD remains the current side, MERGE_HEAD names the incoming side, and the index can carry stage 1/2/3 entries. The working tree contains the attempted merged content, including conflict markers for textual conflicts.

git status
git ls-files -u
git diff --ours
git diff --theirs
# resolve files
git add <resolved-files>
git merge --continue
# or
git merge --abort

23. Rebase

Mental model

git rebase <upstream> identifies commits reachable from the current branch but not the selected upstream, then replays them on the new base (with caveats for merge-preserving modes and special options). It creates new commit objects for the replayed commits because their parent relationship and/or metadata differ.

BEFORE
A---B---C main
     \
      D---E feature

rebase feature onto main
A---B---C main
         \
          D'---E' feature

D != D'
E != E'
git switch feature
git rebase main
git log --graph --decorate --oneline --all

Why commit IDs change

A commit includes parent IDs. Rebasing changes the parent of replayed commits, which changes the commit object’s bytes and therefore its ID. The old commits may remain in the repository for a time if reachable from reflogs or other refs.

Rebase conflict control

git status
git diff
# resolve and stage
git add <paths>
git rebase --continue
# skip the current patch if it is intentionally unnecessary
git rebase --skip
# abandon
git rebase --abort

Ours/theirs trap: during rebase, Git’s internal “ours”/“theirs” perspective is counter-intuitive compared with a normal merge. Git’s documentation explicitly warns that the labels can appear swapped because the “current” side is the rebased series being applied onto the new base. Do not make a policy around --ours/--theirs without understanding the active operation.

24. git rebase --onto: surgical graph surgery

Derivation form

For git rebase --onto NEWBASE OLDBASE BRANCH, think:

Replay = Reach(BRANCH) \ Reach(OLDBASE)
Destination = NEWBASE
Operation = replay Replay, in ancestry order, onto NEWBASE

Example:

A---B
     \
      E---F topic
           \
            G---H feature

Goal: keep G,H but move them directly onto B.

Action:
git rebase --onto B F feature

Result:
A---B------G'---H' feature
     \
      E---F topic
git rebase --onto B F feature

This is one of the highest-value expert commands because it lets you express “remove this ancestor region and replay what remains” directly.

25. Interactive rebase

Interactive rebase is a history-editor, not merely a conflict tool.

Action Effect Typical use
pick replay commit keep
reword replay but change message clarify
edit stop to modify commit split, amend, fix
squash combine with previous, keep/edit messages collapse related work
fixup combine with previous, discard this commit message fold cleanup
drop omit commit remove accidental history
exec run shell command during sequence per-commit tests/formatters
break pause the sequence deliberate inspection

Example transformation:

A(feature)---B(typo)---C(debug)---D(feature)---E(logging)

interactive rebase can produce:
A(feature)-------------------B(logically complete)

Before rewriting pushed history, classify the branch as private, private-but-pushed, shared, protected, release/public, or deployed. The mechanics may work even when the collaboration policy should forbid them.

26. Cherry-pick

Mental model

Cherry-pick means: take the patch introduced by an existing commit and apply it to the current HEAD; normally record the result as a new commit. It is therefore a change-transfer operation, not a branch merge.

source history
A---B---X---Y
         
         X = bug fix

target history
A---B---M
         \
          X' = replayed bug fix
git switch release/1.0
git cherry-pick X

Use -x when the environment benefits from recording the source commit ID in the message. Use --no-commit to apply the patch to index/worktree without making a commit immediately. During a conflict, use git cherry-pick --continue, --skip, or --abort as appropriate.

Ranges

git cherry-pick A..B selects commits reachable from B but not A under the revision-set semantics; git cherry-pick A^..B includes A. This difference is subtle and central to correct backport ranges. Inspect with git rev-list --reverse before mutating when the set matters.

git rev-list --reverse A..B
git rev-list --reverse A^..B

Merge commits and -m

A merge commit has multiple parents and therefore does not have a single unambiguous patch to replay. git cherry-pick -m <parent-number> <merge> tells Git which parent should be treated as the mainline when computing the inverse/forward difference.


Part VIII — Remote Git Internals

27. origin/main is local state

origin/main normally resolves to a local remote-tracking ref, such as refs/remotes/origin/main. It is not a live network pointer. A fetch updates that local ref according to the remote’s refspec.

Remote repository
refs/heads/main -> R5
          │
          │ git fetch
          ▼
Local repository
refs/remotes/origin/main -> R5

After another developer pushes R6, your origin/main still names R5 until you fetch. This is why a local lease based on remote-tracking state can be stale.

28. Fetch

Conceptually:

remote refs + required objects
          │
          ▼
local object database
          +
refs/remotes/<remote>/*
          +
FETCH_HEAD
git fetch origin
git show-ref --heads --remotes
cat .git/FETCH_HEAD

Git’s fetch documentation states that fetch downloads refs and objects needed to complete their histories, updates remote-tracking branches according to refspecs, and writes fetched refs/object names to .git/FETCH_HEAD by default.

Pruning stale remote-tracking refs

git fetch --prune origin
git remote prune origin

Pruning removes stale local remote-tracking refs; it does not magically delete remote branch data everywhere.

29. Pull

git pull is a convenience workflow combining fetch with an integration choice.

Mode Concept
default/configured fetch, then configured integration behavior
git pull --rebase fetch then rebase local work on fetched upstream
git pull --no-rebase fetch then merge
git pull --ff-only fetch then refuse unless a fast-forward is possible

For production branches, explicitly selecting the integration policy is usually easier to reason about than relying on user/global configuration.

30. Push as a remote ref update

Conceptually:

local source ref
refs/heads/main -> M
       │
       │ negotiate/send objects
       ▼
remote ref
refs/heads/main -> M
git push origin main
git push -u origin feature/payment-refactor

Without force semantics, a remote branch update that would discard commits can be rejected as non-fast-forward. This is a server-side ref policy check layered over the transport and object transfer protocol.

31. Force vs force-with-lease

Option Concept Main risk
--force override normal non-fast-forward safety checks can overwrite remote commits unexpectedly
--force-with-lease require a remote-ref expectation (“lease”) before forced update still unsafe if your expectation is stale/incorrect or policy is inappropriate
--force-if-includes additional protection tied to whether remote-tracking tip was integrated more robust, but must understand configured refs/reflogs

Git’s push documentation explicitly describes --force as capable of losing remote commits and --force-with-lease as a safety check against an unexpected remote state.

High-stakes rewrite procedure

git fetch origin
git log --graph --decorate --oneline --all
# inspect the exact remote-tracking tip
git rev-parse origin/feature-x
# rewrite local feature branch
git rebase ...
# then, if a rewrite is actually intended:
git push --force-with-lease origin feature-x

Never assume “lease” means “safe.” It is safer than blind force because it constrains the remote state you are willing to overwrite. It is not a substitute for coordination or branch protection.


Part IX — Reflog, Reachability, and Garbage Collection

32. Reflog: local movement history

A reflog records updates to a ref locally. It is not a second commit graph and it is not a universal server-side undo log. It is a record of where refs and HEAD have pointed over time, which frequently makes previously visible commits recoverable.

git reflog
git reflog show HEAD
git reflog show main
git reflog --all

Common disaster:

A---B---C---D feature
          
user runs git reset --hard B

visible branch:
A---B

HEAD reflog still records a prior position at D
git reflog
git branch recovery HEAD@{1}
# or use the exact OID
git show <lost-commit>

The safe recovery sequence is: inspect the reflog, identify the exact desired OID, create a recovery ref before experimenting further, then decide how to reintegrate it.

33. ORIG_HEAD and operation safety nets

Git uses pseudorefs such as ORIG_HEAD during operations that move HEAD drastically. These are useful evidence during recovery, but reflogs are often more informative because multiple transitions are recorded.

git show ORIG_HEAD
git rev-parse ORIG_HEAD

34. Garbage collection and pruning

Git maintenance can repack objects, update ancillary indexes, and eventually remove objects no longer protected by repository reachability/retention rules. Current Git documentation describes default expiry values for reflogs and prune grace periods and notes that reachable objects referenced from refs, reflogs, the index, and other namespaces are protected.

Term Operational meaning
reachable can be reached from relevant protected references/object roots
unreachable not currently reachable from those roots
dangling a valid object not referenced by another reachable object in the manner fsck reports
pruned no longer retained in the object database after cleanup rules permit deletion
git fsck --full --no-reflogs
git gc
git count-objects -v

Recovery window is finite

Do not build an operational policy that assumes reflog recovery works forever. Reflog entries can expire, unreachable-object grace periods can elapse, repositories can be repacked/pruned, and alternate copies may or may not exist. Recovery gets progressively more difficult as references and retention evidence disappear.


Part X — Conflicts, Stash, and Partial Work

35. Conflict resolution as index-state repair

A conflict is not “Git has two text files and is confused.” It is a state where Git could not automatically produce one index entry for a path. For a normal merge, the index stores base/ours/theirs stages. Your task is to choose the intended resulting content, write that content, and collapse the path to stage 0 by staging it.

Normal merge

git status
git ls-files -u
git diff --ours
git diff --theirs
# edit
git add file
git merge --continue

Ours/theirs table

Operation “ours” means “theirs” means Key nuance
normal merge current HEAD side incoming MERGE_HEAD side intuitive case
rebase current rebased branch/upstream side in the implementation’s operation perspective commit being replayed labels can feel swapped; follow the rebase docs/state
cherry-pick conflict current HEAD side the picked commit’s side sequencer state uses CHERRY_PICK_HEAD

36. Stash

git stash stores a working-state snapshot so you can temporarily restore a clean working tree. The exact stash structure is richer than “a zip of my changes”; depending on options and Git version it can represent tracked changes, staged state, and optionally untracked/ignored files.

git stash push -m "incident checkpoint"
git stash list
git stash show -p stash@{0}
git stash apply stash@{0}
# or apply and remove if successful
git stash pop

Apply vs pop

apply re-applies the stash but retains the stash entry. pop attempts to apply it and then drops it if application succeeds according to Git’s stash semantics. During incident recovery, apply is often easier to reason about because it keeps the original stash as a recovery artifact until you verify the result.

Untracked and ignored content

git stash push -u
# includes untracked files
git stash push -a
# includes ignored files as well

Be explicit about what you are protecting. If the thing you need to recover is not in the stash snapshot, it cannot be recreated by popping the stash.


Part XI — Advanced Inspection / Git Detective Toolkit

37. The baseline incident toolbox

git status
git branch --show-current
git log --graph --decorate --oneline --all --reflog
git branch -vv
git show-ref
git for-each-ref
git rev-parse HEAD
git reflog --all -50
git diff
git diff --cached
git remote -v
git fsck --full

What each question answers

Question Command family
What am I on? git status, git branch --show-current, git symbolic-ref HEAD
What is the DAG? git log --graph --decorate --oneline --all --reflog
Where does commit X exist? git branch --contains X, git tag --contains X, git rev-list --all --contains X
What does X change? git show X, git diff X^ X
What is staged? git diff --cached
What is local vs remote? git branch -vv, git show-ref, git ls-remote origin
What ref moved? git reflog show <ref>
Does A contain B? git merge-base --is-ancestor B A
What is the common base? git merge-base A B
Are there dangling objects? git fsck --full

38. git rev-list: set algebra over history

git rev-list is one of the best tools for answering “what commits are in this set?”

git rev-list main
git rev-list origin/main..feature
git rev-list --left-right --count origin/main...feature
git rev-list --ancestry-path A..B

Use it to preflight a cherry-pick range, quantify divergence, determine candidate commits for backporting, or prove that one ref contains another.

39. git range-diff

git range-diff compares two versions of a patch series and helps reviewers understand whether a rebase/rewrite materially changed the series.

git range-diff old-base..old-tip new-base..new-tip

It is particularly valuable when a developer force-pushes an amended series and the reviewer needs to compare version N-1 to version N instead of re-reading the entire history.

40. git blame → git show → git bisect

Use blame as a locator, not as a verdict about responsibility:

symptom
  ↓
suspicious line
  ↓
git blame
  ↓
commit X
  ↓
git show X
  ↓
parent / surrounding commits
  ↓
git bisect if the regression boundary is uncertain
git blame -L <start>,<end> -- path/to/file
git show <commit>
git bisect start
git bisect bad
git bisect good <known-good>
# test each checkout
git bisect reset

For N candidate commits, bisection typically takes O(log2 N) test iterations, assuming a deterministic good/bad predicate over the chosen history.

41. Worktrees

git worktree lets one repository have multiple working directories tied to different branches/commits.

git worktree add ../hotfix hotfix/2026.09
git worktree add ../release release/2026.09
git worktree list
git worktree remove ../hotfix
git worktree prune

Operationally, worktrees are useful when an engineer must inspect or patch another line while retaining an in-progress working tree. They avoid the cognitive and state-management cost of repeated stash/switch cycles.

42. rerere

rerere records conflict resolutions so that recurring identical conflict shapes can be reused, particularly valuable for long-running branches rebased repeatedly.

git config rerere.enabled true
git rerere status
git rerere diff

Git’s documentation notes that rerere records preimage/resolution data and can reuse it; enabling rerere does not mean you should blindly accept every automatic resolution. Inspect the result and stage it deliberately.


Part XII — Patch Transfer, Tags, Hooks, and Plumbing

43. Patch-oriented workflows

git format-patch serializes commits into mail-oriented patch files; git am applies such patches as commits. This remains useful for kernel-style workflows, offline transfer, selective backporting, and cross-repository change movement.

git format-patch -1 <commit>
git am 0001-some-change.patch

Patch transfer differs from cherry-pick in operational emphasis: patch files are portable artifacts, while cherry-pick operates directly on commit objects available in the repository graph.

44. Tags and releases

Tag type Representation Metadata Typical role
lightweight ref → target object none beyond ref name private/simple labels
annotated ref → tag object → target tagger/message/signature possible releases / auditable milestones
signed annotated tag with cryptographic signature signer evidence stronger authenticity / release processes
git tag v1.2.3
git tag -a v1.2.3 -m "Release 1.2.3"
git show v1.2.3
git push origin v1.2.3
# or all tags, deliberately
git push origin --tags

Be careful deleting or moving release tags. Tag names are human-facing release identifiers and are often consumed by CI/CD, package managers, and deployment tooling.

45. Hooks and enforcement boundaries

Client-side hooks include pre-commit, commit-msg, pre-push, post-merge, and post-checkout. Hooks are powerful for local quality gates but are not a substitute for server-side or CI enforcement because local hooks can be absent or bypassed depending on configuration and workflow.

Good hook responsibilities:

Keep slow, authoritative, or security-critical enforcement in CI/hosting policy as well.

46. Plumbing: from index to commit

One useful conceptual construction is:

working files
   │
   ├─ git add ────────────────┐
   ▼                          │
index                         │
   │                          │
   └─ git write-tree ───────► tree OID
                                │
                                └─ git commit-tree <tree> -p <parent>
                                             │
                                             ▼
                                          commit OID
                                             │
                                             └─ git update-ref refs/heads/main <commit>
git add <files>
TREE=$(git write-tree)
COMMIT=$(printf "message\n" | git commit-tree "$TREE" -p "$(git rev-parse HEAD)")
git update-ref refs/heads/main "$COMMIT"
git rev-parse HEAD
git cat-file -p "$COMMIT"

This is not a replacement for git commit. It is a mental bridge between porcelain commands and the underlying object/ref machinery.


Part XIII — Security and Sensitive History

47. Secret leak: four distinct problems

Never collapse these into one action:

Problem Required treatment
secret exists in working tree only remove/rotate as appropriate; do not commit
secret exists in latest local commit revoke/rotate; remove before sharing; rewrite local history if needed
secret exists in shared/public history revoke/rotate first; coordinate history remediation and affected copies
secret exists in remote caches/forks/PRs/artifacts use hosting/security-specific cleanup processes in addition to Git rewrite

GitHub’s current sensitive-data guidance explicitly says revocation/rotation is the first priority for real secrets and explains that history rewriting has side effects, including changed commit hashes, coordination requirements, and residual copies in clones/forks/caches/PRs. See the source in the bibliography.

48. Removing a sensitive file from history

For modern GitHub-oriented remediation, git filter-repo is the recommended external tool; the older git filter-branch command is explicitly discouraged by its own documentation.

git filter-repo --sensitive-data-removal --invert-paths --path path/to/secret.txt
# or replace sensitive text patterns
git filter-repo --sensitive-data-removal --replace-text ../replacements.txt

Production sequence

1. Revoke/rotate secret
2. Freeze / coordinate affected pushes if necessary
3. Clone a clean mirror / working copy
4. Rewrite history
5. Inspect changed refs and affected commits
6. Force-update the central repository according to the incident plan
7. Coordinate clone cleanup / fork cleanup
8. Clean hosting caches/PR references when supported
9. Verify the secret is absent from relevant refs and artifacts
10. Prevent recurrence

Why a new deletion commit is not enough

A commit that deletes the file creates a new tree state, but the earlier commit containing the secret remains reachable from history. Anyone who can access that commit can still inspect the old content. Therefore “delete it in the next commit” is not history erasure.


Part XIV — Monorepos, LFS, Submodules, and Performance

49. Monorepo strategies

As repositories scale, the constraints shift from “can Git model this?” to “how much data must each developer materialize and how much history must common commands traverse?”

Important mechanisms:

Technique Problem it addresses
sparse checkout checkout only selected paths
sparse index reduce index traversal for sparse working trees
partial clone / promisor objects avoid downloading all object content up front
shallow clone limit history depth when full history is unnecessary
Git LFS store large binary payloads outside normal Git blob storage
submodules compose repositories while recording exact submodule commits
subtree vendor/merge another project’s history into one repository

Do not use shallow history for workflows that require complete ancestry (for example, some bisect/release/security investigations) without a plan to deepen the clone.

50. Git LFS

Git LFS changes the representation seen in normal Git history: Git tracks pointer files while LFS storage holds the large object payloads separately. Clone/fetch/checkout workflows may therefore involve Git objects and LFS objects as two related but distinct transfer/storage systems.

The 2 GB binary incident

If someone commits a huge binary directly as a normal Git blob, the blob becomes part of Git history. Deleting it later does not remove the historical object; every clone that fetched that history may already have it. Moving to LFS later often requires a history migration and careful coordination.

51. Submodules

A superproject records a gitlink identifying the exact commit of a submodule. This is why the superproject can pin a dependency to a specific submodule commit without containing the submodule’s full file history in its own tree.

git submodule add <url> path/to/submodule
git submodule init
git submodule update
git submodule sync
git submodule status

Common production failure modes include: forgetting to push the referenced submodule commit, checking out a superproject commit that points to a submodule commit the developer has not fetched, and accidentally modifying a detached submodule checkout without updating the superproject gitlink.

52. Performance internals

Important repository performance mechanisms include packfiles, delta compression, commit-graph, multi-pack-index, bitmaps, fsmonitor, sparse checkout/index, partial clone, and maintenance tasks.

Diagnostic loop

git count-objects -vH
git maintenance run --auto
git repack -ad
git commit-graph write --reachable
git multi-pack-index write
git status

Do not run expensive repack/aggressive maintenance blindly on a production repository under concurrent load. Git’s maintenance documentation distinguishes automatic maintenance from explicit, potentially expensive optimization steps.


Part XV — Organization, Hosting Platforms, and CI/CD

53. Git vs GitHub/GitLab/Bitbucket

Git defines objects, refs, transport protocols, local history manipulation, and core merge/rebase semantics. Hosting platforms add policy and collaboration layers such as pull/merge requests, protected branches, review requirements, CODEOWNERS integration, merge queues/trains, status checks, and UI/API workflows.

GitHub examples

GitHub protected branches can enforce review requirements, status checks, linear history, signed commits, merge queues, deployment checks, force-push restrictions, and deletion restrictions. These are repository-hosting policies, not primitives of local Git itself.

GitLab examples

GitLab protected branches similarly control who can push/merge/force-push/delete, and GitLab supports merged-results pipelines and merge trains for validating changes against integration state.

Always name the layer when documenting an operational rule: Git, host policy, CI, deployment controller, or artifact registry.

54. Git history is not deployment state

Keep these four objects conceptually separate:

Git commit/ref
      ↓
Build artifact (container, binary, package)
      ↓
Deployment record / environment state
      ↓
Actual production behavior

A successful Git revert does not necessarily roll back a production environment. A deployment can be built from an immutable artifact that no longer maps 1:1 to a branch tip. Rollbacks should therefore be expressed as artifact/deployment operations when the deployment system is the source of truth, with Git history used as the source of code lineage and desired-state intent.

55. Branching strategies without pretending one is universal

Strategy Typical shape Strength Cost / risk
trunk-based short-lived branches → main low divergence demands strong CI/review/release discipline
GitHub Flow-like short-lived feature branches → main simple mental model release coordination may need another layer
release branches main + supported release lines explicit stabilization/backport lane divergence and cherry-pick management
GitFlow-like develop/release/hotfix branches explicit long release choreography more refs and merge complexity
monorepo + ownership shared trunk + path governance unified dependency changes scale requires tooling and policy

Choose topology based on release cadence, compatibility windows, audit requirements, team count, and deployment architecture—not on folklore.

56. Merge queues / trains and CI

A merge queue addresses the problem that “PR passed against branch state X” does not prove that multiple PRs will compose successfully in the eventual target branch. GitHub and GitLab implement different hosting-level mechanisms, including GitHub merge queue and GitLab merge trains/merged-results pipelines.

From a Git mental-model perspective, the important point is that CI may test a synthetic merge state that is not yet a durable branch ref. Treat the tested commit / merge result as a build input, then separately identify the resulting integrated ref and deployment artifact.


Part XVI — Release Engineering / Hotfixes / Backports

57. Release model

main
│
├── release/2026.09
│      │
│      └── hotfix/2026.09.1
│
└── feature branches

Common release flow:

feature → main
main → release branch
hotfix → release branch
hotfix → main (forward-port)
release tag → artifact → deployment

The exact branch policy varies. The invariant is to make the propagation direction explicit so that a hotfix does not disappear from the forward-moving branch and a backport does not accidentally import unrelated feature history.

58. Backport derivation

main:       A---B---C---D
release/1.0: A---B

D = fix

Desired:
release/1.0: A---B---D'
git switch release/1.0
git cherry-pick D
# resolve/verify
git diff HEAD^ HEAD
git show --stat --oneline HEAD

Before cherry-picking, inspect the patch’s dependencies. A fix that relies on changes introduced after the release base may not apply cleanly or may compile but be semantically invalid.

59. The wrong-branch + hotfix problem — canonical deep dive

Scenario

main
│
A---B                     main
     \
      C---D---E feature/payment-refactor
               \
                H production hotfix accidentally committed here

Assume C and D are feature work and H is the one-line production fix. The required end state is:

main
│
A---B---H' main / hotfix lineage
     \
      C---D---E feature

First question: is H’s patch independent of C/D/E? A commit’s diff is relative to its parent, so applying H to B may conflict if its patch context depends on feature changes. The DAG alone does not tell you whether the patch is semantically portable.

Solution A — branch at H, then move feature back

git branch hotfix H
git switch feature/payment-refactor
git reset --hard E
git switch main
git cherry-pick hotfix

Use this when H is the tip, feature history is private or otherwise safe to rewrite, and you want to retain H exactly as the source commit for a clean transfer. Verify the feature branch ref moved to E and the hotfix branch still points to H.

Solution B — cherry-pick H without rewriting feature history

git switch main
git cherry-pick H

Use this when the feature branch is shared or there is no need to remove H from it immediately. If H should not remain on the feature branch, a later cleanup may be considered, but the collaboration impact must be assessed first.

Solution C — interactive rebase to reorder/extract the fix

git switch feature/payment-refactor
git rebase -i <base-before-C>
# reorder/drop/edit so feature work and H are separated
# then update main/hotfix appropriately

Use only when history is private enough to rewrite and the sequence is easier to transform than to cherry-pick. This can be the most surgical approach when H is not the tip or when feature and fix are entangled across adjacent commits.

Solution D — rebase --onto

When the hotfix is separated by an identifiable ancestry boundary, rebase --onto can rebase the feature suffix while keeping the hotfix commit referenced elsewhere. Derive the exact old-base by computing which commits should be replayed, not by copying a memorized incantation.

Solution E — patch extraction

git show --format=email --patch H > /tmp/hotfix.patch
git switch main
git apply --index /tmp/hotfix.patch
git diff --cached
git commit -m "Hotfix: ..."

Patch extraction is useful when commit identity/topology is less important than preserving exactly the change. If H’s patch contains feature-only context, expect manual adjustment.

Variants by sharing level

State of feature branch Operational consequence Typical posture
local only history rewrite is cheap branch at H, restore feature tip, transfer fix
pushed private branch rewrite may require force-with-lease coordinate with yourself/automation; preserve backup ref before rewrite
shared team branch history rewrite affects collaborators prefer add-only change transfer; avoid rewriting the shared ref
already merged downstream graph already contains the fix decide whether it is logically a duplicate; avoid unnecessary surgery
already deployed Git correction does not equal production rollback use deployment rollback/forward-fix policy plus Git lineage cleanup

Variant: fix is uncommitted

When the hotfix is still mixed into working-tree state, capture it as a patch or isolate it with partial staging before switching context.

git diff > /tmp/hotfix-working-tree.patch
git status
git switch main
git apply --index /tmp/hotfix-working-tree.patch
git diff --cached
git commit -m "Hotfix: ..."

This assumes the patch is contextually applicable. If feature and hotfix edits are intermixed in the same hunk, use git add -p, git restore -p, manual separation, or a temporary worktree to isolate the exact content.

Variant: feature commits already pushed to protected main accidentally

Do not try to “make main look right” with an uncoordinated local reset. A protected/shared branch should usually be repaired through a history-preserving revert or an approved incident-specific rewrite procedure. Hosting policy may reject force pushes regardless of local Git state.


Part XVII — Git Emergency Room

Emergency default: stop mutating refs until you have a backup ref, a known OID, or a saved patch/stash when that is practical.

1. I deleted a branch

Symptoms

Branch ref is gone locally.

Diagnosis / immediate commands

git reflog --all -50
git reflog show HEAD
git branch recovery <oid>

Recovery posture

Once you identify the last correct tip, recreate the intended branch name with git branch <name> <oid>.

Do not do

Do not run aggressive pruning first.

2. I ran git reset --hard

Symptoms

The branch now points earlier and working tree/index match the target.

Diagnosis / immediate commands

git reflog show HEAD
git show <candidate-oid>
git branch recovery <candidate-oid>

Recovery posture

Recover via the reflog or another ref before continuing work.

Do not do

Do not assume the data is gone merely because the branch moved.

3. I rebased the wrong branch

Symptoms

Your branch now contains rewritten commit IDs.

Diagnosis / immediate commands

git reflog --all
git log --graph --decorate --oneline --all --reflog

Recovery posture

Create a recovery ref at the pre-rebase tip; then decide whether to reset back or keep the rewrite.

Do not do

Do not immediately force-push the rewritten branch.

4. I committed on the wrong branch

Symptoms

A change exists under the wrong branch tip.

Diagnosis / immediate commands

git show HEAD
git branch --contains HEAD
git log --graph --oneline --all

Recovery posture

If local/private, create the correct branch at the commit then move the wrong branch back; otherwise transfer the patch with cherry-pick.

Do not do

Do not delete the only ref until the commit is safely named elsewhere.

5. I pushed to the wrong branch

Symptoms

A remote ref changed unexpectedly.

Diagnosis / immediate commands

git fetch origin
git show-ref --remote
git reflog --all

Recovery posture

If shared, use a preserving correction (often revert) and coordinate. If a private branch was rewritten, use an appropriate lease.

Do not do

Do not force the intended branch just because the wrong branch was updated.

6. I force-pushed

Symptoms

Remote history changed unexpectedly.

Diagnosis / immediate commands

git fetch origin
git reflog --all
git fsck --full

Recovery posture

Identify previous remote tip from another clone, CI checkout, teammate reflog, or server evidence; create a recovery ref before deciding on restoration.

Do not do

Do not blindly force-push a different guess.

7. Someone force-pushed over my work

Symptoms

Your local and remote refs diverged unexpectedly.

Diagnosis / immediate commands

git fetch origin
git log --graph --decorate --oneline --all --reflog

Recovery posture

Name your local work with a recovery branch, inspect remote history, then coordinate a reconciliation strategy.

Do not do

Do not delete your local branch or hard reset before creating a recovery ref.

8. I merged the wrong branch

Symptoms

A merge commit or fast-forward incorporated unintended history.

Diagnosis / immediate commands

git show --summary HEAD
git log --graph --decorate --oneline --all
git reflog

Recovery posture

If private, reset/rebuild the merge. If shared, consider reverting the merge with the correct mainline semantics.

Do not do

Do not revert a merge without understanding -m.

9. I committed a secret

Symptoms

A credential/token/key is in Git history.

Diagnosis / immediate commands

git show --stat HEAD
git log --all -- <path>
# then immediately revoke/rotate the secret

Recovery posture

Revoke/rotate first; then clean history and hosting copies according to the security incident plan.

Do not do

Do not rely on a later delete commit as remediation.

10. I committed generated files

Symptoms

Large/generated content entered tracked history.

Diagnosis / immediate commands

git show --stat HEAD
git status
git log --oneline -- <generated-path>

Recovery posture

If unshared, amend/reset and add ignore rules. If shared, decide whether the content should remain and whether history rewrite is worth the coordination cost.

Do not do

Do not delete history without establishing policy and ownership.

11. I need to undo a pushed commit

Symptoms

A public branch contains a bad commit.

Diagnosis / immediate commands

git show <bad-commit>
git branch -r --contains <bad-commit>
git log --graph --decorate --oneline --all

Recovery posture

Prefer git revert <bad-commit> for shared/public history unless an approved rewrite is required.

Do not do

Do not reset shared history as the default undo mechanism.

12. Rebase conflict is everywhere

Symptoms

The rebase has stopped with many conflicts.

Diagnosis / immediate commands

git status
git rebase --show-current-patch
git diff --name-only
git ls-files -u

Recovery posture

Resolve the current patch as a state problem; use --continue, --skip, or --abort deliberately.

Do not do

Do not mix merge-specific assumptions into a rebase without checking the active sequencer state.

13. Cherry-pick conflict

Symptoms

The target branch cannot apply the source patch cleanly.

Diagnosis / immediate commands

git status
git cherry-pick --show-current-patch
git diff --ours
git diff --theirs

Recovery posture

Resolve, stage, git cherry-pick --continue; skip only when the patch is intentionally unnecessary.

Do not do

Do not use --theirs blindly across files.

14. I detached HEAD and made commits

Symptoms

Commits exist without a branch ref pointing to the tip.

Diagnosis / immediate commands

git log --oneline --decorate -10
git reflog -20
git branch rescue <oid>

Recovery posture

Create a branch at the desired detached tip, then continue normally.

Do not do

Do not switch away repeatedly before naming the commit if it is important.

15. I lost a stash

Symptoms

A stash was popped/dropped or disappeared.

Diagnosis / immediate commands

git reflog --all
git fsck --full --no-reflogs

Recovery posture

Search dangling commits/objects and identify stash parent structures; recovery becomes more difficult as objects age out.

Do not do

Do not immediately run pruning or aggressive maintenance.

16. I deleted a tag

Symptoms

A release tag ref was removed.

Diagnosis / immediate commands

git reflog --all
git fsck --full
git show <candidate-oid>

Recovery posture

Recreate the tag at the exact prior target; confirm annotated-tag object vs target commit.

Do not do

Do not create a new tag at “the current release” without proving equivalence.

17. Local branch says ahead and behind

Symptoms

Local and upstream refs have diverged.

Diagnosis / immediate commands

git fetch origin
git rev-list --left-right --count HEAD...origin/$(git branch --show-current)
git log --graph --decorate --oneline --all

Recovery posture

Choose merge/rebase/reset based on sharing and intended topology.

Do not do

Do not use git pull blindly when the branch has an unclear history policy.

18. Git says non-fast-forward

Symptoms

Push or merge attempted a non-descendant update.

Diagnosis / immediate commands

git fetch origin
git merge-base --is-ancestor origin/main main; echo $?
git log --graph --decorate --oneline --all

Recovery posture

Determine whether remote work should be integrated, or whether a history rewrite is intentional and permitted.

Do not do

Do not add --force merely to silence the error.

19. Git says unrelated histories

Symptoms

The two tips have no common ancestor in the expected graph.

Diagnosis / immediate commands

git merge-base A B; echo $?
git log --graph --decorate --oneline --all --boundary

Recovery posture

Validate whether two repositories were accidentally combined, then choose an explicit migration/import strategy. --allow-unrelated-histories is a deliberate exception, not a repair button.

Do not do

Do not merge unrelated histories just because Git offered an option.

20. Repository corruption suspected

Symptoms

Commands report missing/broken objects or invalid refs.

Diagnosis / immediate commands

git fsck --full
git show-ref
git count-objects -vH
git verify-pack -v .git/objects/pack/*.idx 2>/dev/null | head -50

Recovery posture

Freeze destructive maintenance, capture a filesystem backup, locate alternate clones/object sources, and repair refs/object stores methodically.

Do not do

Do not run git gc / git prune first in a repository where recovery evidence may still be needed.

21. Release tag points to the wrong commit

Symptoms

The human-facing release name has incorrect target state.

Diagnosis / immediate commands

git show-ref --tags
git rev-parse <tag>
git show <tag>

Recovery posture

Determine whether the tag is immutable by policy. For a public release, publish a corrected tag/version rather than silently moving the existing name unless policy explicitly permits retagging.

Do not do

Do not force-move a release tag without understanding downstream consumers.

22. Need to split one bad commit

Symptoms

One commit contains unrelated logical changes.

Diagnosis / immediate commands

git show --stat <commit>
git show <commit>
git switch -c split-work <commit>^

Recovery posture

Use interactive rebase/edit + mixed reset or patch-based staging to construct multiple clean commits.

Do not do

Do not manipulate the index without verifying each resulting diff.

23. Need one change from a large commit

Symptoms

The desired fix is mixed with unrelated changes in the same commit.

Diagnosis / immediate commands

git show <commit>
git diff <commit>^ <commit>
git merge-base HEAD <commit>

Recovery posture

Use cherry-pick if the whole patch is acceptable; otherwise extract selected hunks with git show/git apply/git add -p.

Do not do

Do not assume a commit is an indivisible unit of useful change.

24. Working tree has unknown local changes

Symptoms

You inherited or reopened a messy checkout.

Diagnosis / immediate commands

git status --short
git diff
git diff --cached
git stash push -u -m "checkpoint"

Recovery posture

Checkpoint before mutation; classify changes before cleaning or switching branches.

Do not do

Do not use git clean -fdx as housekeeping without enumerating what it will remove.


Part XVIII — Production Incident Case Studies

Each case uses the required pattern: Context → Current DAG → Mistake → Diagnosis → Procedure → Intermediate State → Final DAG → Verification → Alternatives → Lessons.

Case 1 — Wrong branch, fix is the last local commit

Context

A developer is on a private feature branch and accidentally adds a production fix.

Initial DAG / state

main A---B; feature C---D---H

Investigation

Create a hotfix ref at H; move feature back to D; transfer H to main.

Procedure

git branch hotfix H
git reset --hard D
git switch main
git cherry-pick hotfix

Resulting state

main A---B---H' ; feature C---D ; hotfix -> H

Verification

git branch -vv; git show H; git show main; git diff D..feature

Alternatives

A patch-only extraction is preferable when H depends on feature context.

Lessons

Private history enables surgical rewrite; once shared, transfer-first is usually easier.

Case 2 — Wrong branch, fix is uncommitted and mixed

Context

Working tree contains feature edits plus a one-line production fix.

Initial DAG / state

main A---B; feature C---D; W = feature + hotfix mixed

Investigation

Capture or isolate the hotfix before changing refs.

Procedure

git diff > /tmp/hotfix.patch
git switch main
git apply --index /tmp/hotfix.patch
git diff --cached
git commit -m "Hotfix"

Resulting state

main A---B---H; feature still has original working state to clean up

Verification

git status; git diff --cached; git show HEAD

Alternatives

Partial staging is better than a broad patch when hunks can be separated.

Lessons

When content is mixed inside one hunk, the real problem is content separation, not branch movement.

Case 3 — Lost commit after hard reset

Context

A feature branch was reset two commits back.

Initial DAG / state

before A---B---C---D feature; after A---B feature

Investigation

Use reflog to identify C/D and name the intended tip.

Procedure

git reflog show feature
git branch rescue <oid>

Resulting state

A---B---C---D rescue; feature remains B until chosen

Verification

git log --graph --decorate --oneline --all --reflog; git show rescue

Alternatives

Recovery may use ORIG_HEAD when available, but reflog is the broader record.

Lessons

A ref disappearing does not imply immediate object deletion.

Case 4 — Shared branch needs an undo

Context

A bad commit is already in main.

Initial DAG / state

A---B---C bad---D main

Investigation

Create an inverse commit rather than moving main backward.

Procedure

git revert C
git show --stat HEAD

Resulting state

A---B---C---D---E main, E reverses C

Verification

git diff C^ C; git show E; CI/deployment checks

Alternatives

If the incident requires restoring an exact prior artifact, deploy the prior artifact; Git revert is source-history correction.

Lessons

Production rollback and source rollback are related but not identical.

Case 5 — Force-with-lease prevents stale overwrite

Context

Engineer rewrites a private branch while another clone pushes new work.

Initial DAG / state

remote R2; local remote-tracking R2; other clone pushes R3; local rewrite proposes R2→R4

Investigation

Use fetch/lease semantics so the stale remote state is detected.

Procedure

git fetch origin
git push --force-with-lease origin feature

Resulting state

The stale lease rejects the overwrite until the engineer reconciles the remote.

Verification

Inspect rejection; fetch; compare DAG; replay or rebase; retry with correct lease.

Alternatives

Blind force would not provide the same guard.

Lessons

A lease is only as good as the expected remote state and the operator’s discipline.

Case 6 — Rebase changed every feature commit ID

Context

A feature branch was rebased onto a new main.

Initial DAG / state

A---B---C main; B---D---E feature

Investigation

Treat D/E as rewritten copies D’/E’.

Procedure

git switch feature
git rebase main

Resulting state

A---B---C---D'---E' feature

Verification

git log --graph --decorate --oneline --all; compare old/new with `git range-diff`

Alternatives

Merge may avoid identity rewriting but preserves the original topology.

Lessons

Commit identity is a graph/object property, not a stable “change ID”.

Case 7 — Backport fix to an older release

Context

Fix exists on main but must be applied to release/1.0.

Initial DAG / state

main A---B---C---D fix; release A---B

Investigation

Replay D onto the release line.

Procedure

git switch release/1.0
git cherry-pick D

Resulting state

release A---B---D'

Verification

run targeted tests; `git diff B D'`; verify artifact

Alternatives

Patch extraction can be used when the commit contains unrelated change.

Lessons

Backport correctness is semantic, not just conflict-free application.

Case 8 — Rebase went wrong; recover pre-rebase

Context

Interactive rebase reordered/dropped the wrong commits.

Initial DAG / state

pre-rebase A---B---C---D; post-rebase A---B---C'---D'

Investigation

Find pre-rebase tip in HEAD reflog, create recovery ref, then decide.

Procedure

git reflog show HEAD
git branch pre-rebase <oid>

Resulting state

Both old and new lines are named; no evidence is destroyed.

Verification

git log --graph --all --decorate --reflog; compare with range-diff

Alternatives

If collaboration already occurred, recovery may mean preserving both lines until coordination is complete.

Lessons

Recovery starts by naming states, not by guessing the reset target.

Case 9 — Merge conflict with three index stages

Context

Main and side edit the same file differently.

Initial DAG / state

A---B main; A---C side

Investigation

Merge and inspect index stages.

Procedure

git merge side
git ls-files -u

Resulting state

index stages 1/2/3; resolve → `git add` → merge commit

Verification

git status; git ls-files -u should become empty; inspect merge commit parents

Alternatives

Abort the merge if the base is wrong.

Lessons

The index is the authoritative unresolved merge state.

Case 10 — Detached HEAD with valuable work

Context

Engineer checked out a historical commit, built a fix, and committed twice.

Initial DAG / state

A---B---C; detached tip D---E

Investigation

Create a branch at E.

Procedure

git branch rescue-detached E
git switch rescue-detached

Resulting state

A---B---C; rescue-detached E

Verification

git show rescue-detached; git status; git branch --contains E

Alternatives

If E has to be integrated elsewhere, cherry-pick or rebase based on intent.

Lessons

Detached does not mean “uncommitted”; it means no branch ref advances automatically.

Case 11 — Large commit contains one needed security fix

Context

A vendor commit includes hundreds of unrelated updates and one critical line.

Initial DAG / state

A---V big change

Investigation

Extract only the desired patch rather than cherry-picking the entire commit.

Procedure

git show V > /tmp/vendor.patch
# apply selected hunks manually or with git apply
git add -p
git commit

Resulting state

target branch gains a small surgical commit

Verification

git diff --cached; tests; `git show HEAD`

Alternatives

A dedicated upstream patch or filtered cherry-pick can be cleaner if maintainable.

Lessons

Commit boundaries are useful organizational units, but they do not dictate the unit of transfer.

Case 12 — Secret was committed and pushed

Context

A credential appears in a public repository.

Initial DAG / state

A---B(secret)---C main

Investigation

Revoke/rotate first, then coordinate history cleanup.

Procedure

revoke/rotate secret
# clean a fresh clone with git-filter-repo
# force-update refs only after coordination

Resulting state

new rewritten graph without the secret in relevant refs

Verification

search all refs; inspect changed refs; clean forks/caches/PRs as required

Alternatives

If the secret is already revoked and compliance impact is low, full rewrite may be unnecessary depending on policy.

Lessons

History cleanup cannot retroactively un-expose copies already made.

Case 13 — Release branch diverged for months

Context

Many fixes exist on both main and release with different ancestry.

Initial DAG / state

main A-B-C-D-E; release A-B-F-G

Investigation

Model common base and patch sets before choosing merge vs cherry-pick.

Procedure

git merge-base main release
git rev-list --left-right --count release...main
git log --graph --all

Resulting state

Explicit propagation plan

Verification

verify release artifact and forward-port hotfixes

Alternatives

A merge may be appropriate for topology; cherry-picks can preserve independent release lines.

Lessons

Divergence is an organizational state, not merely a Git error.

Case 14 — Someone force-pushed the wrong branch

Context

Remote feature branch was overwritten with unrelated history.

Initial DAG / state

remote old X; remote new Y; local still X

Investigation

Recover old X from a clone/reflog/CI checkout, name it, then coordinate.

Procedure

git branch recovered-X X
# compare Y vs X before any remote rewrite

Resulting state

recovery ref X preserved

Verification

inspect hosting audit/PR refs and all known clones

Alternatives

Do not immediately push X back over Y until all relevant work is captured.

Lessons

The first goal is evidence preservation.

Case 15 — Commit series rewritten and review must understand delta

Context

A developer rebases and force-pushes a 12-commit series.

Initial DAG / state

series v1 and series v2 differ only in the rewrite

Investigation

Use range-diff to correlate commits.

Procedure

git range-diff base..oldtip base..newtip

Resulting state

reviewer sees patch correspondence and changed hunks

Verification

run CI on v2 and verify target ref

Alternatives

A plain git log comparison may obscure which commits correspond.

Lessons

For complex rebases, patch-series comparison is a first-class review operation.


Part XIX — Expert / Plumbing Reference

60. Porcelain vs plumbing

Layer Examples Why experts care
porcelain status, add, commit, switch, restore, merge, rebase, cherry-pick human workflow
inspection plumbing rev-parse, show-ref, cat-file, ls-tree, rev-list precise introspection
ref plumbing update-ref, symbolic-ref controlled reference mutation
object plumbing hash-object, write-tree, commit-tree object-model experiments / automation

61. rev-parse: resolve names to exact values

git rev-parse HEAD
git rev-parse refs/heads/main
git rev-parse --symbolic-full-name @{upstream}
git rev-parse --show-toplevel

Use rev-parse when scripts need canonical values and names without depending on display-oriented command output.

62. for-each-ref: inspect the ref namespace systematically

git for-each-ref --format="%(refname:short) %(objectname)" refs/heads refs/remotes refs/tags

This is superior to parsing human-oriented git branch output when building tooling.

63. update-ref: compare-and-swap for refs

old=$(git rev-parse refs/heads/main)
new=<new-oid>
git update-ref refs/heads/main "$new" "$old"

The expected-old parameter makes the operation fail rather than silently overwrite an unexpected current ref value. This is the right mental primitive for race-aware ref automation.

64. hash-object: content addressing in the open

oid=$(git hash-object path/to/file)
echo "$oid"
git cat-file -t "$oid"

With -w, hash-object writes the object into the repository object database. Without -w, it computes the identifier without storing the object.

65. fsck and pack inspection

git fsck --full
git count-objects -vH
git verify-pack -v .git/objects/pack/*.idx

Use these for repository forensics, not as casual daily commands. Pack internals can reveal where objects physically live and which objects remain even when refs moved.

66. Replace refs, grafts, alternates, and other edge cases

Advanced repositories may have refs/replace/*, graft files, alternates, shallow boundaries, and promisor/partial-clone metadata. These features can change what “history” means for a given command. Forensic scripts should inspect unusual namespaces before declaring a commit truly unreachable or a repository truly self-contained.


Part XX — Quick Reference and Operator Handbook

A. One-page Git mental model

CONTENT
  ↓
blob/tree objects
  ↓
commit object (tree + parents + metadata)
  ↓
refs name commits
  ↓
HEAD selects current branch/ref or detached commit

WORKING TREE ↔ INDEX ↔ HEAD
         add      commit

REMOTE
  ↓ fetch
remote-tracking refs + objects + FETCH_HEAD
  ↑ push

Recovery evidence:
refs + reflogs + other refs + object database

B. One-page command cheat sheet

Goal Command
Inspect state git status
Show topology git log --graph --decorate --oneline --all --reflog
See unstaged git diff
See staged git diff --cached
Stage selected git add -p
Unstage git restore --staged <path>
Discard file changes git restore <path>
Create branch git switch -c <name>
Move branch pointer git reset --soft\|--mixed\|--hard <target>
Public undo git revert <commit>
Integrate history git merge
Rewrite private series git rebase / git rebase -i
Surgical base change git rebase --onto
Transfer one commit git cherry-pick <commit>
Update remote view git fetch
Normal push git push
Guarded rewrite git push --force-with-lease
Recover prior ref git reflog
Find object git fsck --full

C. One-page undo matrix

Situation Preserve history? Typical transformation
Uncommitted file changes, keep yes leave working tree or stash
Uncommitted file changes, discard no git restore
Staged but not committed, unstage yes git restore --staged
Local commit, keep changes no history at tip git reset --soft HEAD~1
Local commit, keep worktree but restage no history at tip git reset --mixed HEAD~1
Local commit, discard content no git reset --hard with explicit target
Shared bad commit yes git revert
Need change from another branch not necessarily git cherry-pick / patch
Lost branch/commit recover reflog + new ref

D. One-page branch management matrix

Task Typical operation Shared-history note
new feature branch git switch -c feature safe
update from main merge or rebase depends on branch policy
backport cherry-pick / patch verify dependencies
hotfix branch from release/prod line; forward-port preserve topology policy
clean private feature history interactive rebase do not rewrite shared branch
recover deleted branch reflog → branch name recovery ref first

E. One-page emergency recovery guide

1. STOP
2. git status
3. git log --graph --decorate --oneline --all --reflog
4. git reflog --all
5. git branch recovery/<timestamp> <known-good-oid>
6. Save any important work with stash / patch / worktree
7. Model CURRENT vs DESIRED DAG
8. Choose reset / revert / rebase / cherry-pick / merge based on sharing
9. Execute one meaningful mutation at a time
10. Verify refs + DAG + index + worktree + remote state

F. One-page release / hotfix guide

BUG DISCOVERED
   ↓
Identify deployed artifact + corresponding Git ref/tag
   ↓
Branch from the actual release/prod line
   ↓
Implement smallest corrective change
   ↓
CI + tests + review
   ↓
Tag/build/deploy
   ↓
Forward-port to main
   ↓
Verify release and main contain the intended lineage

G. One-page advanced command reference

Command High-value use
git merge-base common-base / ancestry reasoning
git rev-list history set algebra
git range-diff compare rewritten patch series
git worktree parallel branch work
git rerere reuse recurring conflict resolutions
git bisect regression localization
git update-ref precise ref mutation
git for-each-ref scriptable ref inventory
git cat-file object inspection
git write-tree / commit-tree understand commit construction
git fsck object/repository forensics
git filter-repo history rewrite / sensitive-data remediation (external tool)

H. Git decision tree

NEED TO CHANGE GIT STATE?
        │
        ├─ Is the problem only file content?
        │      ├─ working tree → git restore
        │      └─ index → git restore --staged / reset <path>
        │
        ├─ Is the branch tip wrong?
        │      ├─ private → reset/rebase as derived from desired DAG
        │      └─ shared → preserve history with revert/forward correction
        │
        ├─ Need a change from elsewhere?
        │      ├─ whole branch topology → merge
        │      ├─ one patch → cherry-pick
        │      └─ portable patch artifact → format-patch/am
        │
        ├─ Need newer base?
        │      ├─ preserve topology → merge
        │      └─ rewrite private series → rebase / rebase --onto
        │
        └─ Something disappeared?
               └─ reflog → identify OID → create recovery ref → proceed

I. Production incident checklist

Before mutation:

After mutation:


Command comparison encyclopedia

reset vs revert

Semantic difference: reset changes where the current branch points and may change index/worktree. revert records a new commit that inverses an earlier change.

Operational selection: Use reset for appropriate local/private history surgery; revert for shared/public history preservation.

DAG/state implication: Reset can alter the branch DAG view; revert adds a new node.

restore vs checkout

Semantic difference: restore is file/index-oriented; checkout is a historical multipurpose command that also moves HEAD.

Operational selection: Prefer restore for file content and switch for branch movement in new workflows; know checkout because legacy scripts use it.

DAG/state implication: Checkout remains valid and is not “broken”; its role is broader and easier to misuse.

switch vs checkout

Semantic difference: switch is branch-oriented and more explicit; checkout is older/multipurpose.

Operational selection: Use switch for branch navigation; checkout for legacy compatibility or operations not covered by switch.

DAG/state implication: Both can participate in detached HEAD workflows.

merge vs rebase

Semantic difference: Merge integrates histories and can create a merge node; rebase rewrites the copied/replayed side onto another base.

Operational selection: Merge for topology preservation/shared histories; rebase for suitable private-history cleanup.

DAG/state implication: Rebase changes commit IDs; merge can preserve original commits.

merge vs cherry-pick

Semantic difference: Merge connects branch histories; cherry-pick transfers selected patch effects into a new commit.

Operational selection: Use merge when integration of the histories is desired; cherry-pick for selective transfer.

DAG/state implication: Cherry-picked commit is not the same object as source commit in the normal case.

cherry-pick vs patch

Semantic difference: Cherry-pick transfers a commit from Git object history; patch workflows serialize changes into portable patch artifacts.

Operational selection: Use patch when repository boundaries or offline/email workflows matter.

DAG/state implication: Patch application may produce a different commit identity and metadata.

fetch vs pull

Semantic difference: Fetch updates objects and remote-tracking refs; pull fetches and then integrates.

Operational selection: Use fetch for explicit control and diagnosis.

DAG/state implication: Pull has integration side effects; fetch does not merge/rebase the current branch by itself.

stash apply vs pop

Semantic difference: Both attempt to restore stash content; apply retains the stash; pop removes it after successful application under stash semantics.

Operational selection: During recovery, apply often preserves evidence longer.

DAG/state implication: Conflicts can affect whether a pop drops the entry.

force vs force-with-lease

Semantic difference: Force bypasses normal ref-update safety; lease requires an expected remote state.

Operational selection: Use lease when a rewrite is intended and permitted.

DAG/state implication: Lease is not a collaboration substitute; stale/incorrect expectations can still produce bad outcomes.

branch vs tag

Semantic difference: Branch is normally a movable ref for ongoing work; tag is a named release/milestone ref.

Operational selection: Use branch for evolving work; tag for stable names.

DAG/state implication: Both are refs, but their workflows and expectations differ.

annotated vs lightweight tag

Semantic difference: Annotated tag points to a tag object with metadata; lightweight tag names an object directly.

Operational selection: Annotated tags are usually more appropriate for releases where provenance matters.

DAG/state implication: Do not retarget release tags silently.

clone vs fetch

Semantic difference: Clone initializes a new repository and obtains history; fetch adds/updates objects and refs in an existing repository.

Operational selection: Use clone for new copies; fetch for refreshing existing copies.

DAG/state implication: Clone also sets up remote/tracking configuration by default.

soft vs mixed vs hard reset

Semantic difference: All move the current branch tip; they differ in index/worktree synchronization.

Operational selection: Soft for recommit workflows; mixed for unstaging/rebuilding; hard for deliberate content reset.

DAG/state implication: Hard can discard uncommitted tracked work and therefore requires explicit verification.


Safety rules — the ten commandments

  1. Inspect before mutating.
  2. Name important states with refs before experimenting.
  3. Understand whether the history is private or shared.
  4. Never use --force to silence a non-fast-forward error.
  5. Prefer --force-with-lease for permitted rewrites.
  6. Treat reflog as recovery evidence, not an infinite backup.
  7. Separate source history, build artifacts, and deployment state.
  8. Rotate/revoke leaked credentials before history cleanup.
  9. Verify the final DAG, refs, index, working tree, and remote state.
  10. Prefer a command you can derive from the state model over a command you can recite from memory.

Part XXI — Organization Scale, Advanced Porcelain, and Edge Cases

67. Git architecture at 20, 100, and 500 engineers

Repository topology is an organizational system, not merely a Git preference. The useful question is not “Which branching model is best?” but “What failure modes does this topology make easy or hard, and what automation/policy compensates for them?”

20 engineers: keep the state model obvious

A small team can often operate with a single long-lived integration branch plus short-lived feature branches:

feature/a ─┐
feature/b ─┼──> main ───> build ───> deploy
feature/c ─┘

The operational focus is review quality, fast CI, clear ownership, and short branch lifetimes. The main failure modes are usually accidental commits, stale branches, merge conflicts, and ad-hoc release procedures.

Useful controls:

git branch -vv
git log --graph --decorate --oneline --all
git fetch --prune
git merge --ff-only

100 engineers: policy becomes part of the Git system

At roughly this scale, the branch itself is no longer a sufficient coordination mechanism. Teams need explicit ownership and integration policy:

developer branch
      │
      ▼
code review + ownership checks
      │
      ▼
CI / integration validation
      │
      ▼
merge queue / controlled integration
      │
      ▼
main

Typical additions include path ownership, required checks, CODEOWNERS, protected branches/rulesets, merge queues, release automation, and observability around failed integrations. The Git objects and refs remain the same; the hosting/CI system constrains which ref transitions are permitted.

500 engineers: optimize for system throughput and contention

At larger scale, contention is a first-class capacity problem. Common architectural responses include:

                       ┌── ownership / policy
                       ├── merge queues or trains
                       ├── incremental / targeted CI
main ────────────────► ├── release branches where needed
                       ├── monorepo scale tooling
                       └── reproducible artifacts

Questions become operational:

The repository design should follow these constraints. Git itself does not provide a universal “large organization mode.”

68. Branching models: trunk-based, GitHub Flow, GitLab Flow, GitFlow, release branches

These labels describe workflows layered over Git’s common primitives. They are not distinct Git implementations.

Model Characteristic topology Typical operational concern
trunk-based short-lived branches converge quickly CI/review throughput
GitHub Flow-like feature branch → pull request → main release sequencing may need extra automation
GitLab Flow-like branch/release/environment relationships vary by policy multiple integration/release lines
GitFlow-like long-lived develop/release/hotfix branches merge and backport complexity
release-branch model main plus supported release lines propagation of fixes across versions

Use the state model to reason about any of them. A release/* branch is still a ref. A pull request is still a host-level object referencing commits/branches. A merge queue is still a policy/control mechanism around integration; its synthetic test state may not be a durable branch ref.

69. Advanced command toolbox: purpose before syntax

git notes — attach metadata without changing the commit ID

A note is separate Git data associated with an object. Adding a note does not alter the commit object and therefore does not create a new commit ID merely because metadata was attached.

git notes add -m 'incident=INC-1234' <commit>
git notes show <commit>
git notes list

Conceptually:

commit X  ────────────────► unchanged commit object
   │
   └────────► refs/notes/commits ─► note object/tree

Notes are useful for review metadata, incident annotations, test evidence, migration markers, and other information that should not become part of the commit payload. Because notes use refs, they need explicit transport policy when shared across repositories; ordinary commit fetch/push should not be assumed to carry every notes namespace.

Reference: https://git-scm.com/docs/git-notes

git bundle — move a repository graph as a file

A bundle packages Git objects and references into a file that can be verified and transported without a live Git server. This is useful for air-gapped transfer, offline handoff, archival snapshots, and controlled repository migration.

git bundle create repo.bundle main
git bundle verify repo.bundle
git clone -b main repo.bundle repo-copy

A bundle is not merely a tarball of .git/; it is a Git transport representation containing the commits/objects needed by its advertised refs and their prerequisite relationships. Incremental bundles can be constructed with explicit prerequisite revisions when a base repository already has part of the graph.

Reference: https://git-scm.com/docs/git-bundle

git archive — export a tree, not a repository

git archive --format=tar.gz --prefix=project-1.4.0/ v1.4.0 > project-1.4.0.tar.gz

The output is a source snapshot of the selected tree. It deliberately does not contain the Git database, reflogs, refs, or object history. This makes it appropriate for source distributions but inappropriate as a repository backup or recovery mechanism.

Reference: https://git-scm.com/docs/git-archive

git replace — local graph substitution

git replace <object-to-replace> <replacement-object>
git replace -l
git replace -d <object-to-replace>

Replace refs let compatible Git commands temporarily treat one object as another. This can be useful for experiments, history analysis, or debugging a repository whose stored graph should not be permanently rewritten. Because the substitution changes how local commands interpret the graph, it is an easy way to create “I see a different history” confusion. Inspect refs/replace/ and use --no-replace-objects when you need to bypass replacements.

Reference: https://git-scm.com/docs/git-replace

70. Power-user aliases without hiding dangerous operations

Aliases should shorten inspection and repetitive safe operations; they should not turn destructive history rewriting into muscle memory.

Useful examples:

git config --global alias.lg 'log --graph --decorate --oneline --all'
git config --global alias.last 'log -1 --stat'
git config --global alias.root 'rev-parse --show-toplevel'
git config --global alias.unstage 'restore --staged --'
git config --global alias.review 'diff --check'
git config --global alias.refs 'for-each-ref --format="%(refname:short) %(objectname:short)"'

Avoid aliases whose names imply a harmless “undo” while silently executing reset --hard, push --force, clean -fdx, or similar operations. The goal of an alias is reduced typing, not reduced awareness.

A useful rule is:

alias = faster inspection / faster repetition
not
alias = conceal the state transition

71. Monorepo scale: sparse checkout, sparse index, partial clone

These are related but solve different dimensions of repository scale.

                 Repository graph / objects
                          │
                partial clone controls
                what objects are downloaded
                          │
                          ▼
                    local object DB
                          │
                sparse-checkout controls
                 what is populated locally
                          │
                          ▼
                    working tree

Sparse checkout

Sparse checkout keeps tracked files in the repository graph while limiting the populated working tree. The implementation uses SKIP_WORKTREE information in the index; sparse-index can further reduce index expansion for large sparse workspaces.

git sparse-checkout init --cone
git sparse-checkout set services/payment libs/common
git sparse-checkout reapply

The important mental model is not “unselected files are untracked.” They are still tracked; they are simply omitted from the populated sparse working tree under the sparse-checkout specification.

References:

Partial clone

Partial clone controls which reachable objects are transferred initially.

git clone --filter=blob:none --sparse <url> repo

A blobless partial clone can hold commits and trees while deferring blob downloads until operations require those blobs. Newer Git releases also provide git backfill to proactively populate missing objects in supported workflows.

References:

Sparse versus partial

Do not confuse the dimensions:

Feature Primary concern
sparse-checkout working-tree population
sparse-index index scalability under sparse checkout
partial clone object transfer/storage
shallow clone history depth
LFS large-file payload storage

They can be combined, but each introduces different failure and tooling considerations. A sparse checkout does not make a repository smaller on the network; a partial clone does not by itself define which files are present in the working tree.

72. Git security: signatures, identities, and secrets

Commit identity versus commit signature

A commit contains author and committer identity fields. A cryptographic signature, when present, is additional commit payload that can be verified against a key. These are different concepts.

git commit -S -m 'Release fix'
git verify-commit HEAD
git show --show-signature HEAD

A verified signature means the signed payload validated against the relevant key/signing policy. It does not, by itself, prove that the change was reviewed, bug-free, authorized for a specific production system, or semantically correct.

Git supports multiple signing backends via gpg.format, including OpenPGP, X.509, and SSH. Trust and key-ownership policy are external to the cryptographic syntax itself.

References:

Signed tags for release identity

Annotated signed tags are especially useful when a release process wants an independently verifiable object naming a release point.

git tag -s v2.7.0 -m 'Release 2.7.0'
git tag -v v2.7.0
git rev-parse v2.7.0^{commit}

A signed tag authenticates the tag object’s signed payload. Release systems still need their own policy for which signing keys are trusted and which artifacts correspond to a release.

Secret incident: rotation precedes history cleanup

When a credential is committed, treat the credential as compromised first. Removing the file from Git history does not make a previously exposed credential valid again.

1. revoke / rotate credential
2. identify blast radius
3. preserve forensic evidence as needed
4. rewrite history only when required
5. coordinate remote / fork / clone cleanup
6. invalidate CI caches / artifacts where relevant
7. verify the new credential path

GitHub’s sensitive-data-removal guidance explicitly warns that history rewriting changes commit IDs and can affect clones, forks, pull requests, and cached copies. History cleanup is a separate operation from credential rotation.

Reference: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/removing-sensitive-data-from-a-repository

73. Git and CI/CD: make the tested state immutable

A robust deployment pipeline should distinguish between mutable names and immutable commit IDs.

branch / tag name
      │
      ▼
exact commit OID
      │
      ▼
source checkout
      │
      ▼
reproducible build
      │
      ▼
immutable artifact digest
      │
      ▼
deployment record

CI should record the source OID

A build should be able to answer:

Which exact commit did we build?
Which exact tree did we build?
Which exact dependencies were resolved?
Which exact artifact digest was deployed?

Use git rev-parse HEAD, the CI provider’s immutable source identifier, and artifact digests. Do not make a mutable branch name the sole provenance identifier.

Merge queues and synthetic states

A merge queue can test a change against a temporary integration state created from several refs/commits. That state may not become a durable branch ref. Therefore:

reviewed branch tip ≠ necessarily tested integration tip ≠ deployed artifact

The pipeline must preserve the identity of the tested input and the identity of the produced artifact.

GitHub’s protected-branch features and merge queue, and GitLab’s merge trains/merged-results pipelines, are hosting-level controls around these Git states rather than replacements for Git’s object/ref model.

References:

Rollback versus revert

A Git revert changes code history by adding an inverse commit. A deployment rollback changes runtime state to an earlier artifact. They often happen together, but neither is a substitute for the other.

Git change:             A---B---C---R
runtime artifact:      artifact(C) → artifact(B)

Use the deployment system’s rollback primitive when runtime state is the incident target; use Git history operations when source lineage or future integration state is the target.

74. Edge cases that break simplistic Git explanations

Unrelated histories

If two tips have no common ancestor, normal merge cannot construct a three-way merge base. --allow-unrelated-histories is an explicit request to combine such histories; it should not be used merely to silence a confusing error.

git merge --allow-unrelated-histories other-history

First determine why the repositories are unrelated. Common causes include independently initialized repositories, incorrect remotes, or an accidental repository boundary.

Shallow clones

A shallow clone deliberately omits historical ancestry. Commands whose reasoning depends on older ancestors can produce different results or fail because required history is absent.

git rev-parse --is-shallow-repository
git fetch --unshallow
# or deepen incrementally
git fetch --deepen=500

Do not diagnose a history problem as “Git lost the commit” until you have checked whether the local clone simply does not contain the relevant ancestry.

Rebase and ours / theirs

Conflict terminology is operation-relative. During ordinary merge, “ours” means the current side and “theirs” the merged side. During rebase, the conceptual roles are reversed in parts of the conflict machinery because the branch being replayed is treated as “theirs” while the temporary base is “ours.” Always inspect the conflict context instead of relying on intuition.

References:

Alternates and shared object stores

Git can be configured to borrow objects from another object database using alternates. This can be useful for storage efficiency and controlled infrastructure, but it creates a dependency outside the repository’s obvious .git/objects path.

For incident work, inspect the environment if object lookup behaves unexpectedly:

git rev-parse --git-dir
git count-objects -v
git cat-file -t <oid>

Replace refs and “different history on my machine”

Replace refs alter local object interpretation without changing the stored commit IDs. They are therefore useful for experiments but dangerous as an implicit collaboration mechanism. Before comparing histories across machines, inspect:

git replace -l
git --no-replace-objects log --graph --oneline --all

Remote-tracking reflogs are not magical server backups

A local origin/main ref is a local record of fetched remote state. Its reflog, when retained, records local movements of that ref. Neither should be assumed to be the authoritative server-side historical record for an incident. Hosting platforms may have additional server-side retention, but that is platform-specific policy.

75. Performance engineering: know which subsystem is slow

Git performance problems are easier to diagnose when decomposed:

network transfer
      │
      ├── pack negotiation / object filtering
      ▼
object storage
      │
      ├── packfiles / deltas / multi-pack-index / bitmaps
      ▼
index
      │
      ├── sparse index / fsmonitor
      ▼
working tree
      │
      └── file-system traversal / checkout

Useful inspection and maintenance commands include:

git count-objects -v
git maintenance run --auto
git commit-graph write --reachable
git multi-pack-index write
git rev-list --objects --all > /tmp/reachable-objects.txt

Interpret evidence instead of blindly repacking. For a monorepo, reducing the amount of populated working-tree state may matter more than shaving a small percentage from pack size. For a CI runner repeatedly cloning, protocol/object transfer may dominate. For a developer repeatedly scanning millions of files, index/worktree traversal may dominate.

Object-store scale model

Git’s packfiles delta-compress related objects. Commit-graphs accelerate graph traversals. Multi-pack-index lets Git efficiently address objects across multiple packfiles. Bitmaps can accelerate reachability enumeration for suitable operations. None of these changes the conceptual fact that refs name objects and commits form a DAG; they are storage/traversal optimizations around that model.

References:

76. Verification cookbook: prove each layer separately

When an incident report says “the branch is fixed,” make that statement testable.

Verify the ref

git rev-parse refs/heads/main
git show-ref refs/heads/main
git symbolic-ref HEAD

Verify the ancestry

git merge-base --is-ancestor <expected-base> <expected-tip>
git rev-list --left-right --count <expected-tip>...<other-tip>
git branch --contains <commit>

Verify the commit payload and tree

git show --no-ext-diff --stat <commit>
git cat-file -p <commit>
git ls-tree -r <commit>

Verify the index and working tree

git status --short
git diff
git diff --cached
git diff HEAD
git ls-files --stage
git ls-files -u

Verify recovery evidence

git reflog --date=iso --all
git fsck --full --no-reflogs --unreachable

The point is not to run every command every time. Choose the smallest evidence set that proves the desired postcondition. But during history surgery, always prove both the ref position and the content state; a visually plausible log can still hide staged or working-tree changes.

The final incident invariant

A Git operation is complete only when:

1. the intended refs point where they should,
2. the intended commit graph exists,
3. the index contains the intended snapshot,
4. the working tree is in the intended state,
5. remote state is known where relevant,
6. deployment state is separately verified where relevant,
7. recovery evidence has been preserved if the operation was destructive.

Validation and experimental evidence

Test environment

git --version

Observed validation environment: Git 2.47.3.

Current Git documentation was consulted separately because the installed test binary can lag current manuals. The artifact therefore distinguishes experimentally validated behavior from documentation-current behavior.

Executed workflows

the reproducible test suite covers:

Expected evidence highlights

Assertion Observed result
soft reset branch/HEAD moved; index and working tree retained changes
mixed reset branch/HEAD moved; index reset toward target; working tree retained changes
hard reset branch/HEAD moved; index and tracked working tree synchronized to target
rebase feature commit IDs changed after replay onto a new base
merge --no-ff result commit had two parent IDs
B..Y excluded B, included later descendants
B^..Y included B and later descendants
cherry-pick source and resulting target commit IDs differed in divergent-target test
rebase –onto only the selected suffix was replayed with new IDs
reflog recovery pre-reset commit was recoverable and re-named with a branch
force-with-lease worked when lease matched; rejected a stale remote state
merge conflict index contained three stages for the conflicted path
plumbing tree/commit object types and refs matched the expected model

Source specification alignment

This manual was shaped directly by the supplied authoring specification: it requires commands to be taught through current state → command → state transition → resulting state; it requires current-state vs desired-state reasoning, shared/private history classification, progressive depth, decision trees, emergency procedures, and verification. See the supplied specification around the state-first teaching rule, recovery framework, and quality test. fileciteturn0file0L128-L183

The expanded supplied outline also requires the three-tree model, object model, refs, DAGs, remote internals, reset/revert/rebase/cherry-pick, reflog/GC, conflicts, stash, monorepos, LFS, submodules, security, CI/CD, platform boundaries, and a final operator handbook. fileciteturn0file1L142-L193 fileciteturn0file1L868-L928 fileciteturn0file1L1507-L1584

Authoritative references

Git core

Hosting / security

Versioning notes

Final operator test

The document is successful when an engineer can answer all of the following without cargo-culting a command:

The engineer should finish this manual thinking: “I can derive the Git operation from the state I have, the state I want, and the collaboration constraints around it.”

Glossary

Blob: Git object containing file content.
Tree: Git object describing directory entries and modes.
Commit: Git object containing a tree, parent IDs, metadata and message.
Ref: named pointer to an object ID, often a commit.
Branch: normally a movable ref under refs/heads/.
Remote-tracking ref: local ref recording the last fetched state of a remote ref.
HEAD: current checkout target; symbolic when attached to a branch, direct when detached.
Index: staging snapshot / conflict-state database.
Reflog: local log of ref movements.
Reachable: discoverable from protected reference/object roots.
Fast-forward: move a ref to a descendant without creating a merge commit.
Three-way merge: merge using a common ancestor plus two tips.
Rebase: replay selected commits onto another base, normally generating new commit objects.
Cherry-pick: apply one or more commits’ patches onto the current branch, normally creating new commits.
Revert: create new commits that reverse the effect of existing commits.
Remote: configured location used for network operations.
FETCH_HEAD: file recording fetched refs from a fetch operation.
ORIG_HEAD: pseudoref used to retain a pre-operation HEAD in certain drastic operations.
Merge base: common ancestor used for integration reasoning.
Refspec: mapping between source refs and destination refs during fetch/push.