Git & GitHub Mastery Β· Chapter 33
Cheat Sheets
41 min read Β· Volume 4 of 4: Scale, professional practice, production, and reference
Contents
Source file: cheatsheets/git-cheat-sheet.md
Git cheat sheet
Baseline. Git 2.55.0. π’ SAFE reads state or only adds objects Β· π‘ CAUTION moves refs or rewrites local history, recoverable through the reflog Β· π΄ DANGEROUS can destroy uncommitted work, remote history, or the safety net itself (Chapter 1, section 1.8). Every command is taught in the textbook; the number in brackets is the section. Full forms: Git command reference. The five answers for every π‘ and π΄ command: command safety. When something is already lost: emergency recovery.
Before anything else: look
| Command | Purpose | Example | Risk | Common mistake | Recovery |
|---|---|---|---|---|---|
git status -sb | Branch, upstream, ahead and behind, changed paths (4.4) | git status -sb | π’ | Reading "clean" as "equal to the commit": ignored files are not shown | git status --ignored |
git log --oneline --graph --decorate --all | Every ref as a graph (14A.9) | git log --oneline --graph --decorate --all -20 | π’ | Forgetting --all: the walk starts at HEAD only | Not needed |
git diff, git diff --cached | Index against working tree; HEAD against index (5.4) | git diff --cached | π’ | Committing without reading the staged diff | Not needed |
git reflog | Every commit HEAD was on, and the command that moved it (13.3) | git reflog -10 | π’ | Expecting one in a fresh clone or on CI | The clone where the work was done |
git remote -v, git branch -vv | Where pushes go; which upstream each branch has (12.2, 12.5) | git branch -vv | π’ | Trusting origin/<branch> without fetching: it is a cache | git fetch |
Daily use
| Command | Purpose | Example | Risk | Common mistake | Recovery |
|---|---|---|---|---|---|
git init | Creates .git (1.9) | git init shop | π’ | Running it in the wrong directory | Remove the stray .git if it holds nothing |
git clone | New repository with origin, remote-tracking refs and one checked-out branch (12.3) | git clone <url> | π’ | --depth 1 where history is needed | git fetch --unshallow |
git add <path> | Stores content as a blob and points the index entry at it (5.3) | git add src/router.py | π’ | Editing after git add: the commit lacks the edit | git add again |
git add -p | Stages hunk by hunk (5.5) | git add -p | π’ | Following it with git commit -a | git reset HEAD~1 π‘ while private, stage again |
git commit -m | New commit from the index; moves the current branch (6.3) | git commit -m "Add rate limits" | π’ | A missing file: it was never staged | A further commit |
git commit --amend | Replaces the tip commit with a new one (6.7) | git commit --amend --no-edit | π‘ | Amending a commit that is already pushed | git reset --soft 'HEAD@{1}' |
git restore --staged <path> | Unstages: index entry copied from HEAD (5.9) | git restore --staged notes.txt | π‘ | Using git rm --cached, which stages a deletion | git add <path> |
git restore <path> | Overwrites the file from the index (4.7) | git restore -p src/router.py | π΄ | Not reading git diff -- <path> first | None for content never staged |
git rm --cached <path> | Stops tracking; the file stays on disk (4.6) | git rm --cached .env | π‘ | Forgetting that the commit deletes the file for everyone who pulls | git restore --staged <path> before committing |
git mv <a> <b> | Renames the file and its index entry (4.8) | git mv old.py new.py | π’ | Renaming and editing in one commit: history of the file stops at the rename | Rename and edit in separate commits |
git stash push -u | Parks index and working tree as commits under refs/stash (11.11) | git stash push -u -m "wip retry" | π‘ | Leaving long-lived work in the stash | git stash pop --index |
git stash pop | Applies the newest entry and drops it (11.11) | git stash pop --index | π‘ | Popping onto local edits: conflict, entry kept | git reset --merge, then git stash branch <name> |
git switch <branch> | Moves HEAD, index and working tree; refuses to lose edits (7.6) | git switch -c feature/streaming | π’ | Committing on a detached HEAD and leaving | git branch <name> <id> from git reflog |
git config get, list | Reads configuration with its origin (14B.3; Git 2.46 or later) | git config list --show-origin --show-scope | π’ | Looking in one scope only | Not needed |
git config set | Writes one configuration file (14B.3; Git 2.46 or later) | git config set --global pull.ff only | π‘ | A mistyped key is stored and ignored | git config unset |
History
| Command | Purpose | Example | Risk | Common mistake | Recovery |
|---|---|---|---|---|---|
git log <A>..<B> | Commits reachable from B and not from A (14A.8) | git log --oneline origin/main..HEAD | π’ | Confusing it with git diff A..B, which compares two tips | Not needed |
git log <A>...<B> --left-right | Commits on either side and not both (14A.14) | git log --oneline --left-right main...feature/x | π’ | Reading three dots in log like three dots in diff | Not needed |
git diff <A>...<B> | Merge base against B: what a branch adds (14A.2) | git diff --stat main...feature/x | π’ | Two dots for review | Not needed |
git show <commit> | One commit with its diff; a file at a revision (3.5) | git show HEAD~1:config/limits.yaml | π’ | Reading a merge's combined diff as the whole merge | git show --remerge-diff <merge> |
git log -S<string> | Commits that changed the number of occurrences (14A.11) | git log -S'PASS_MARK' --oneline | π’ | Taking the hit for the cause without reading the patch | Check with -G and git show |
git log -L | History of a range of lines or a function (14A.12) | git log -L :score:scoring/metrics.py | π’ | Anchoring on line numbers | A /regex/ or :funcname range |
git log --follow -- <path> | History of one file across renames (14A.10) | git log --follow --oneline -- src/app.py | π’ | Expecting it to survive a rename plus rewrite | Read on under the old path |
git blame | The commit that last changed each line (14A.16) | git blame -w -C -L 10,30 <file> | π’ | Stopping at a formatting commit | --ignore-rev, --ignore-revs-file |
git shortlog -sn <rev> | Commit counts per author (14A.15) | git shortlog -sn --no-merges HEAD | π’ | Omitting the revision in a script: it reads standard input | Pass the revision |
git describe | Version string from the nearest tag (14B.12) | git describe --tags --dirty | π’ | Shallow clone without tags | git fetch --unshallow --tags |
git range-diff | Compares two versions of a commit series (9.14) | git range-diff main ORIG_HEAD HEAD | π’ | Pushing a rebase without it | Not needed |
Branches and integration
| Command | Purpose | Example | Risk | Common mistake | Recovery |
|---|---|---|---|---|---|
git branch <name> [<start>] | Adds one ref (7.5) | git branch rescue/before-rebase | π’ | Branching on an unborn branch | Make the first commit |
git branch -d | Deletes a merged branch and its reflog (7.5) | git branch -d feature/x | π‘ | Reading the refusal after a squash merge as a bug | git branch <name> <id> from the was <id> line |
git branch -D | Deletes with no merge check (7.5) | git branch -D feature/x | π΄ | Deleting the only name of unmerged commits | HEAD reflog, while the objects exist |
git merge <other> | Fast-forward or three-way merge into the current branch (8.3, 8.4) | git merge --no-ff feature/x | π‘ | Whole-file --ours or --theirs in a conflict | git reset --merge ORIG_HEAD; pushed: git revert -m 1 |
git merge --abort | Back to the state before the merge (8.10) | git merge --abort | π΄ | Losing resolutions and edits staged during the merge | git fsck for staged content |
git merge-tree --write-tree | A test merge without touching anything (8.17) | git merge-tree --write-tree --name-only HEAD feature/x | π’ | The deprecated three-argument form | Not needed |
git rebase <upstream> | Re-creates your commits on top of the upstream; new IDs (9.2) | git rebase origin/main | π‘ | Rebasing a branch others push to | git reset --hard <branch>@{1} π΄ |
git rebase -i | Reorder, squash, edit, drop commits (9.6) | git rebase -i --autosquash origin/main | π‘ | Deleting a line: the commit is dropped silently | Same reset; rebase.missingCommitsCheck=error |
git rebase --onto <new> <old> | Replays only the commits after <old> (9.5) | git rebase --onto main feature/base | π‘ | The wrong boundary: conflicts in files you never changed | git rebase --abort |
git rebase --continue, --abort | Go on after resolving; or give up (9.11) | git rebase --continue | π‘ | --amend at a conflict stop; forgetting that "ours" is the new base | Reflog of the branch |
git cherry-pick -x <commit> | Copies one change as a new commit (10.2) | git cherry-pick -x <id> | π‘ | No -x on a backport | git cherry-pick --abort; pushed: git revert |
git tag -a <name> | Annotated tag: a tag object plus a ref (14B.8) | git tag -a v1.0.0 -m "1.0.0" | π’ | Moving it after publishing | git tag -d <name> while unpublished |
git worktree add | A second working tree on the same repository (25.2) | git worktree add ../hotfix release/1.2 | π’ | Removing it with rm -rf | git worktree prune |
Remotes
| Command | Purpose | Example | Risk | Common mistake | Recovery |
|---|---|---|---|---|---|
git fetch | Updates remote-tracking refs; never your branches (12.4) | git fetch origin | π’ | Believing it changed your branch | Not needed |
git fetch --prune | Also deletes stale remote-tracking refs and their reflogs (12.11) | git fetch --prune | π‘ | Pruning the last name of commits you still need | git fsck --lost-found, then git branch <name> <id> |
git pull --ff-only | Fetch, then fast-forward or stop (12.6) | git pull --ff-only | π‘ | Plain git pull on diverged branches without a setting: fatal | git reset --hard ORIG_HEAD π΄, at once |
git pull --rebase | Fetch, then re-create your unpushed commits (9.13) | git pull --rebase | π‘ | After someone's forced push it can drop commits | git reset --hard <branch>@{1} π΄ |
git push -u origin <branch> | Publishes the branch and sets its upstream (12.5) | git push -u origin feature/x | π‘ | Everything up-to-date while the commit is on another branch | Push the branch that holds it |
git push --dry-run | Shows <source> -> <destination> without sending (12.7) | git push --dry-run | π’ | Skipping it before a push that matters | Not needed |
git push --force-with-lease --force-if-includes | Replaces the server's branch only if it is what you last integrated (12.8) | git push --force-with-lease --force-if-includes | π΄ | Using it on a branch others push to | The old tip from your reflog, pushed back |
git push --force | Replaces the server's branch unconditionally (12.8) | none: use the guarded form | π΄ | Answering "non-fast-forward" with it | A clone that still has the old tip |
git push origin --delete <branch> | Deletes a ref on the server (12.7) | git push origin --delete feature/x | π΄ | Deleting a branch with unmerged commits | git push origin <id>:refs/heads/<name> |
git push origin <tag> | Publishes one tag (14B.10) | git push origin v1.0.0 | π‘ | --tags, which publishes every local tag | None that is clean |
git ls-remote | What the server has now (12.4) | git ls-remote --tags origin | π’ | Deciding from stale remote-tracking refs | Not needed |
git remote set-url | Changes where a remote points (12.2) | git remote set-url origin <url> | π‘ | A token inside the URL | Set the previous URL; revoke the token |
git submodule update --init --recursive | Checks out the recorded submodule commits (23.3) | git submodule update --init --recursive | π‘ | Work on the detached HEAD vanishes at the next update | git -C <path> branch <name> <id> |
Undo
| Situation | Command | Example | Risk | Common mistake | Recovery |
|---|---|---|---|---|---|
| Unstage a file | git restore --staged <path> (5.9) | git restore --staged notes.txt | π‘ | git reset --hard instead | git add <path> |
| Discard edits to a file | git restore <path> (4.7) | git restore -p config.yaml | π΄ | No git diff first | None unless staged once |
| Get one file from another commit | git restore --source=<commit> <path> (11.3) | git restore --source=HEAD~2 -- config.yaml | π΄ | A . as the path: deletes files the commit lacks | git restore . for the index versions |
| Fix the last commit, unpushed | git commit --amend (11.7) | git commit --amend | π‘ | Unrelated staged changes are swallowed | git reset --soft 'HEAD@{1}' |
| Take back commits, keep the changes | git reset --soft <commit> or git reset <commit> (11.4) | git reset --soft HEAD~1 | π‘ | Doing it to pushed commits | git reset --soft ORIG_HEAD, at once |
| Move the branch, keep local edits safe | git reset --keep <commit> (11.6) | git reset --keep origin/main | π‘ | Reaching for --hard | ORIG_HEAD, the reflog |
| Throw away commits and edits | git reset --hard <commit> (11.5) | git reset --hard origin/main | π΄ | Uncommitted work in the tree | Commits: reflog. Staged: git fsck --lost-found. Unstaged: none |
| Undo a pushed commit | git revert <commit> (11.8) | git revert --no-edit <id> | π‘ | Resetting and force-pushing instead | Revert the revert |
| Undo a pushed merge | git revert -m 1 <merge> (11.9) | git revert -m 1 <id> | π‘ | Merging the same branch again later: its changes are missing | Revert the revert, then merge |
| Remove untracked files | git clean -n, then git clean -f (4.14) | git clean -n -d | π΄ | -x in a repository with ignored data | None through Git |
| Leave a merge, rebase or pick | --abort (29.6) | git rebase --abort | π‘ (merge: π΄) | Aborting before anchoring new commits | HEAD reflog |
Recovery
| Situation | Command | Example | Risk | Common mistake | Recovery |
|---|---|---|---|---|---|
| Name a state before touching anything | git branch rescue/<what> <id> (13.7) | git branch rescue/before-fix | π’ | A second fix before the anchor | git branch -D when done |
| Find where a branch was | git reflog show <branch> (13.3) | git reflog show main | π’ | Using ORIG_HEAD later than the next command | The branch reflog |
| Commits lost by reset, amend or rebase | git reset --keep <id> or git cherry-pick <range> (13.8) | git reset --keep 'main@{1}' | π‘ | --hard during recovery | ORIG_HEAD |
| A deleted branch | git branch <name> <id> (13.8) | git branch feature/x <id> | π’ | Looking for the deleted branch's reflog | HEAD reflog; git fsck --no-reflogs |
| Staged content that was overwritten | git fsck --lost-found (13.9) | git fsck --lost-found | π’ | Running git gc --prune=now first | git cat-file -p <id> > <path> |
| A dropped stash | git stash store -m <message> <id> (13.9) | git fsck --unreachable to find the ID | π’ | git stash clear | While the objects exist |
| A teammate force-pushed | git reflog show origin/<branch> (13.10) | git branch rescue/server-before origin/main@{1} | π’ | Pulling first | Push the old tip back, with a lease |
| A backup that can travel | git bundle create <file> --all (13.14) | git bundle verify repo.bundle | π’ | Expecting reflogs or uncommitted files in it | cp -Rp of the repository for those |
| The point of no return | git reflog expire --expire=now --all, git gc --prune=now (13.13) | none: only at the end of a secret clean-up | π΄ | Running them as "maintenance" | None |
Debugging
| Command | Purpose | Example | Risk | Common mistake | Recovery |
|---|---|---|---|---|---|
git bisect start, good, bad | Binary search for the commit that changed a property (14A.20) | git bisect start HEAD v1.2.0 | π‘ | A dirty tree; a verdict on a commit that does not build | git bisect reset |
git bisect run <command> | Lets a script give the verdicts; exit 125 skips (14A.21) | git bisect run ./test.sh | π‘ | A script that fails for other reasons than the bug | git bisect log, edit, git bisect replay |
git check-ignore -v | The pattern that ignores a path (4.5) | git check-ignore -v build/out.log | π’ | Asking about a tracked file | --no-index |
git ls-files --stage | The index, entry by entry; stages 1 to 3 in a conflict (5.11) | git ls-files -u | π’ | Missing the lower-case tag of -v for assume-unchanged | git update-index --no-assume-unchanged |
git rev-parse | A name as an object ID; facts about the repository (3.6) | git rev-parse --abbrev-ref HEAD | π’ | Reading .git files in scripts | Not needed |
git cat-file -p, -t | Content and type of any object (3.5) | git cat-file -p HEAD | π’ | Assuming 40-digit IDs | Not needed |
git config list --show-origin --show-scope | Every setting and the file it comes from (14B.2) | git config get --show-origin user.email | π’ | Forgetting includes and environment variables | git var GIT_AUTHOR_IDENT |
GIT_TRACE=1 git <command> | What Git actually ran (14B.7) | GIT_TRACE=1 git st | π’ | Pasting a trace that holds credentials | Not needed |
git diff --check | Conflict markers and whitespace errors (14A.6) | git diff --cached --check | π’ | Committing a merge without it | A new commit that removes the markers |
git fsck | Integrity of the object database (3.8) | git fsck | π’ | Reading dangling as damage | Not needed |
Advanced
| Command | Purpose | Example | Risk | Common mistake | Recovery |
|---|---|---|---|---|---|
git commit --fixup=<commit> with git rebase --autosquash | A correction that finds its target at the next rebase (9.7) | git commit --fixup=<id> | π’, then π‘ | Forgetting the rebase before merging | Fold it in, or drop it |
git rebase --update-refs | Moves every local branch in the range: stacks stay stacked (9.9; Git 2.38 or later) | git rebase --update-refs main | π‘ | It moves branches you did not name | Each branch from its reflog |
git rerere | Replays recorded conflict resolutions (14C.3) | git rerere diff | π’, π‘ for forget | A wrong resolution is replayed too | git rerere forget <path>, git restore --merge <path> π΄ |
git add --renormalize . | Restages every file under the current attributes (14C.5) | git add --renormalize . | π‘ | Mixing it with other changes | git restore --staged . |
git hook list <event> | Which hooks would run, and from which scope (14C.11) | git hook list --show-scope pre-commit | π’ | Treating hooks as enforcement | Enforce on the server |
git sparse-checkout set | Limits the working tree to a cone of directories (24.4) | git sparse-checkout set services/gateway | π‘ | Ignored files in directories that leave the cone are deleted | None for those files |
git clone --filter=blob:none | A blobless partial clone: full history, blobs on demand (26.12) | git clone --filter=blob:none <url> | π’ | Going offline without git backfill | Reconnect |
git maintenance run | Packs, commit-graph, multi-pack-index (26.3) | git maintenance run --task=commit-graph | π‘ | Running git gc beside it | None for expired reflog entries |
git gc --prune=now | Deletes all unreachable objects at once (13.13) | none: see Recovery | π΄ | "It fixes a slow repository" | None in this repository |
git worktree remove | Deletes a clean working tree and its HEAD reflog (25.6) | git worktree remove ../hotfix | π‘ (π΄ with --force) | Detached commits made there lose their name | git fsck --lost-found |
git lfs track, git lfs migrate info | Which paths become pointers; what a migration would rewrite (22.4, 22.9) | git lfs migrate info --above=1mb | π’ | Tracking after the file is in history | git lfs migrate import on unpushed commits π‘ |
git history reword | Rewrites one commit and moves descendant branches (14D.6; experimental, Git 2.54 or later) | git history reword --dry-run HEAD~2 | π‘ | Using it on published commits | Each branch from its reflog |
git update-ref <ref> <new> [<old>] | Writes one ref and nothing else (7.2) | git update-ref refs/backup/main <id> | π‘ (π΄ with -d) | Moving the current branch with it | git update-ref <ref> "<ref>@{1}" |
Emergency recovery, one page
Source file: cheatsheets/emergency-recovery-one-page.md
Emergency recovery: one page
Git 2.55.0. π’ reads or only adds Β· π‘ moves refs, recoverable through the reflog Β· π΄ can destroy work or the safety net. Every command was run in this book's labs. Details: disaster-recovery playbook.
Stop. No git gc, git prune, git clean, git stash clear, git fetch --prune, re-clone or second fix until the lost state has a name.
Look (always first)
| Command | Shows | |
|---|---|---|
git status -sb | π’ | branch, upstream, ahead and behind, operation in progress |
git reflog show <branch> | π’ | every value the branch had, and the command that set it |
git reflog | π’ | every commit HEAD was on (survives branch deletion) |
git reflog show origin/<branch> | π’ | the server's values as this clone saw them; @{1} is the one before the last change |
git ls-remote origin | π’ | what the server holds now |
git fsck --lost-found | π’ | dangling commits and blobs; copies in .git/lost-found/ |
Anchor (before anything moves)
| Command | ||
|---|---|---|
git branch rescue/<what> <ID> | π’ | a named commit cannot expire |
Recover
| Disaster | Command | |
|---|---|---|
| Reset, amend or rebase took commits away | git reset --keep rescue/<what> | π‘ |
| The same, but new commits were made since | git cherry-pick <branch>..rescue/<what> | π‘ |
Branch deleted (Deleted branch x (was <ID>)) | git branch <name> <ID> | π’ |
| Rebase in progress, going wrong | git rebase --abort | π‘ |
| Merge in progress, going wrong | git merge --abort (discards the resolutions made so far) | π΄ |
| Bad merge committed, not pushed | git reset --keep ORIG_HEAD | π‘ |
| Bad commit on a shared branch | git revert --no-edit <ID> | π‘ |
| Bad merge on a shared branch | git revert -m 1 <merge> (a later re-merge then brings nothing) | π‘ |
| Staged file overwritten | git cat-file -p <blob ID> > <path> | π‘ |
| Stash dropped | git stash store -m 'recovered' <ID> | π’ |
| Server branch force-pushed: keep the newcomers | git push origin origin/<branch>:refs/heads/<new-name> | π’ |
| Server branch force-pushed: restore | git push --force-with-lease=<branch>:<bad ID> origin rescue/<what>:<branch> | π΄ |
| Server branch deleted | git push origin <name> | π’ |
| My clone after the server was restored | git cherry -v origin/<branch> <branch>, then git reset --keep origin/<branch> | π’ π‘ |
| Corrupt index | mv .git/index .git/index.corrupt && git reset | π‘ |
| Missing or corrupt objects | git fetch --refetch origin | π‘ |
| Secret committed | Rotate the credential first. Then the playbook, page 11 |
Verify
| Command | Proves | |
|---|---|---|
git range-diff <base> <old> <new> | π’ | = on every line: the same commits came back |
git diff --stat <old> <new> | π’ | no output: identical content |
git merge-base --is-ancestor <ID> <branch> | π’ | exit 0: the commit is on the branch |
git ls-remote origin <branch> | π’ | the server has the ID you intended |
Never in an emergency
| Command | Why | |
|---|---|---|
git reset --hard | π΄ | destroys uncommitted changes; use --keep |
git push --force | π΄ | no condition; use --force-with-lease=<branch>:<ID> |
git reflog expire --expire=now --all, git gc --prune=now | π΄ | deletes the safety net itself |
git clean -f -d | π΄ | deletes untracked files; no undo |
Command safety
Source file: reference/command-safety.md
Command safety: every π‘ and π΄ command of the book
Baseline. Git 2.55.0 on macOS; GitHub CLI 2.88.1; GitHub facts as of 1 October 2026. This file is compiled from the "Command safety" tables and the inline risk labels of Chapters 1 to 30. It adds nothing to them. The section number in the last column is where the textbook answers the questions in full.
1. How to read this file
The three labels are defined in Chapter 1, section 1.8, and they describe the worst case:
| Label | Meaning | Examples from 1.8 |
|---|---|---|
| π’ SAFE | Reads state or only adds objects. Nothing is lost | git status, git log, git fetch, git reflog, git commit |
| π‘ CAUTION | Moves refs or rewrites local history. Recoverable through the reflog if you know how | git reset --soft, git rebase, git commit --amend |
| π΄ DANGEROUS | Can destroy uncommitted work, remote history, or the safety net itself | git reset --hard, git clean -fd, git push --force and its conditional forms --force-with-lease and --force-if-includes, git reflog expire --expire=now --all, git gc --prune=now |
Before any π΄ command the book answers five questions: what it changes, what it can destroy, how to preview it, how to recover, when it is appropriate. The tables below give those five answers for every π΄ and every π‘ command. π’ commands are not listed here; they are in the Git command reference.
Three rules follow from the tables and are worth knowing before you read them:
- The preview is always a π’ command. Run it, read every line, then decide.
- "Reflog" in the Recover column means a local reflog. A fresh clone and a CI runner have none (Chapter 13, section 13.16), tags and remote-tracking refs have none by default (Chapter 7, section 7.11), and the lab configuration switches reflog expiry off while real repositories expire entries after 90 days, or 30 days when unreachable (Chapter 13, section 13.4).
- Content that was never staged and never committed is in no object. No reflog and no
git fsckreturns it (Chapter 13, section 13.12). That is the common reason for a π΄ label.
Earlier drafts of the chapters labelled a few commands in two ways. One label per command has since been chosen, and section 12 records every decision and its reason.
2. Working tree and index
| Command | Label | What it changes | What it can destroy | Preview | Recover | When appropriate | Section |
|---|---|---|---|---|---|---|---|
git restore <path>, git restore -p <path>, git checkout -- <path> | π΄ | Working tree files, from the index | Every unstaged change in the named paths, without confirmation | git diff -- <path> | None for content that was never staged; git fsck for content that was | After you have read the diff and decided the changes are worthless; -p to discard hunk by hunk | 4.7, 11.3 |
git restore --source=<commit> [--staged] [--worktree] <path> | π΄ | Files (and index entries) from a commit; deletes tracked paths the commit lacks | Unstaged edits in those paths | git status --short -- <path>; git diff <commit> -- <path> | None for unstaged edits | Bringing back a deleted file, where no file of that name exists; name the paths you mean | 4.7, 11.3, 14A.13 |
git restore <path> on a file with the assume-unchanged bit | π΄ | Overwrites the hidden edit without warning | The edit the bit was hiding | Clear the bit, then git diff | None | Never as a way to keep local edits: the bits are not an ignore mechanism | 5.12 |
git restore <notebook>, git switch across a notebook change, with a clean filter that strips outputs | π΄ for local outputs | Rewrites the file from the stripped blob | Notebook outputs, which were never in Git | git status; copy the file first | None; rerun the notebook | When the working copy holds nothing that exists only there | 28.18 |
git clean -f [-d] [-X or -x] | π΄ | Deletes untracked files; with -d untracked directories; with -x or -X ignored files | Files Git has no copy of: .env, downloaded datasets, model checkpoints, virtual environments | The same options with -n in place of -f | None through Git | To prove a build from a pristine tree or reset a CI workspace; prefer -X to -x, pass a pathspec, protect files with -e | 4.14, 11.10 |
git switch --discard-changes, git checkout -f | π΄ | Index and working tree, to the target | Uncommitted edits in the files it touches | git status, git diff | None for content that was never staged | Only when you have decided to throw the edits away | 7.15 |
git switch <branch> when an ignored file is tracked on that branch | π΄ | Overwrites the ignored file | The ignored file's content | git ls-tree -r --name-only <branch> compared with git status --ignored | None; prevent with --no-overwrite-ignore | Not a choice: know the case before switching | 4.17 |
git rm -f <path> | π΄ | As git rm, without the check | Uncommitted changes in the file | git diff HEAD -- <path> | None for unstaged content | Only after reading that diff | 4.8 |
git rm <path> | π‘ | Deletes the file and its index entry; refuses if the content is not in HEAD | Nothing: the content is in HEAD | git rm -n <path> | git restore --staged --worktree <path> | Removing a tracked file from the project | 4.8 |
git rm --cached <path> | π‘ | Removes the index entry; the file on disk stays | The next commit deletes the path for everyone who pulls | git rm --cached -n <path>; git status | git restore --staged <path> before committing | A file that was tracked by mistake; announce the commit with the restore command | 4.6, 5.9, 21B.10, 28.9 |
git restore --staged <path>, git reset [--] <path> | π‘ | Index entries, copied from HEAD | The name of a staged version that differed from the file | git diff --cached -- <path> | git add <path>; git fsck --lost-found for a staged version that differed from the file | Unstaging | 5.9, 11.3 |
git update-index --add --cacheinfo <mode>,<id>,<path> | π‘ | One index entry | Nothing | git ls-files --stage | git restore --staged <path> | Building a commit by hand to learn the plumbing | 2.6 |
git update-index --assume-unchanged, --skip-worktree | π‘ | A flag bit that hides later edits from git status | Nothing at once; sets up the π΄ row above | git ls-files -v | The --no- forms | Not as an ignore mechanism | 5.12 |
git add --renormalize . | π‘ | Stages every tracked file whose stored form changes under the current attributes and filters | Nothing | git ls-files --eol; git status before committing | git restore --staged . before committing | One renormalizing commit when attributes or filters change | 14C.5, 20B.18, 28.18 |
git apply <patch> | π‘ | Working tree files (and the index with --index) | Nothing that the reverse patch does not restore | git apply --check, git apply --stat | git apply -R | Applying a diff that is not a commit | 14D.11, 28.18 |
rm .git/index.lock | π‘ | Removes a lock; unsafe while a Git process is writing | A write in progress | pgrep -fl git | None needed for a stale lock | Only when no Git process is running | 5.15 |
rm .git/index, then git reset | π΄ | Discards staging state, flag bits and conflict stages; outside Git's locking | What was staged and not committed loses its index entry | git diff --cached; move the file aside instead of deleting it | git fsck for blobs that were staged; move the file back | "index file corrupt", "index file smaller than expected" | 5.15, 13.18 |
3. Stash
| Command | Label | What it changes | What it can destroy | Preview | Recover | When appropriate | Section |
|---|---|---|---|---|---|---|---|
git stash push [-u] | π‘ | Records index and working tree as commits that only refs/stash reaches, then resets both to HEAD; with -u also removes untracked files | Nothing while the entry exists | git status -s | git stash pop --index | Parking work for minutes; a branch is better for work that must survive | 11.11, 14C.2 |
git stash apply, git stash pop, git stash branch <name> | π‘ | Merges an entry into the files; can conflict | Nothing: the entry is kept after a conflict | git stash show -p | git reset --merge backs out; then git stash branch <name> | Pop soon; clear local edits before popping | 11.11 |
git stash drop, git stash clear | π΄ | Deletes one line, or all lines, of the log of refs/stash | The stashed changes, once the unreachable commits are pruned | git stash show -p 'stash@{n}' | git stash store <id> while the objects exist; without the ID, the git fsck --unreachable recipe of Lab 8.6 | drop after git stash show -p; clear is almost never appropriate | 11.11, 14C.15 |
4. Commits, branches and refs
| Command | Label | What it changes | What it can destroy | Preview | Recover | When appropriate | Section |
|---|---|---|---|---|---|---|---|
git commit --amend (with or without --no-edit, --only, --reset-author) | π‘ | Replaces the tip commit with a new one that has a new ID | Nothing locally; a published commit that others have is not replaced for them | git diff --cached, git log -1 | git reset --soft 'HEAD@{1}' | Unpublished commits only | 6.7, 11.7 |
git commit --no-verify | π‘ | As git commit, without the pre-commit and commit-msg hooks | Nothing; the checks did not run | Run the hook's checks by hand | Amend, or a further commit; the server may still refuse | When a hook is wrong and you have run its checks yourself | 6.15, 14C.11, 28.18 |
git branch -m <name> | π‘ | Renames the ref, its reflog and its configuration section | Nothing | git branch -vv | Rename it back | Before the first push | 7.5 |
git branch -f <name> <commit>, git switch -C | π‘ | Moves a ref; the old value stays in the reflog | Nothing while the reflog entry lives | git branch -vv, git rev-parse <ref> | git branch -f <ref> <ref>@{1} | Putting a branch where you have decided it belongs | 7.5 |
git branch -d <name> | π‘ | Deletes the ref and its reflog after a merge check against the upstream or HEAD | The branch reflog | git branch --merged, git branch -vv | git branch <name> <id> from the was <id> output or from git reflog | Merged branches | 7.5, 27.21 |
git branch -D <name> | π΄ | Deletes the ref and its reflog with no merge check | The only name of unmerged commits | git log --oneline main..<branch>; git diff main <branch> | git branch <name> <id> while the objects exist; the HEAD reflog if the commits were checked out | After the diff against the target is empty (a squash-merged branch) | 7.5, 13.8, 17.9, 30.23 |
git tag -f <name>, git tag -d <name> | π‘ | Moves or deletes a local tag; tags have no reflog by default | The only record of where the tag pointed | git rev-parse <tag>, and write the ID down | git tag <name> <old-id> from the was <id> output; git fsck lists a dangling tag object | Unpublished tags | 7.11, 14B.21 |
git update-ref <ref> <new> [<old>] | π‘ | One ref and its reflog, and nothing else: not the index, not the working tree | Nothing while the reflog entry lives | git rev-parse <ref>; pass <old> so that a surprise stops the command | git update-ref <ref> "<ref>@{1}" | Scripts; full names that begin with refs/ | 2.6, 3.17, 7.2 |
git update-ref -d <ref> | π΄ | Deletes the ref and its reflog, even for the current branch | The only name that leads to those commits | git rev-parse <ref>; write the ID down | git update-ref <ref> <id>; without the ID, git fsck --no-reflogs lists the commits as dangling while they exist | Scripts that remove refs they created themselves | 2.14 |
git symbolic-ref HEAD <ref> | π‘ | Which branch is current, without touching the index or the working tree | Nothing | git symbolic-ref HEAD | git symbolic-ref HEAD <previous>; git reflog | Repairs and demonstrations; porcelain otherwise | 3.17, 7.3 |
Writing a file under .git/refs/ by hand | π΄ | A ref without validation, locking or reflog | A typo produces a broken ref that git log --all refuses to read | Use git update-ref instead | git update-ref after deleting the broken file (Lab 4.1) | Never | 7.15 |
rm of a file under .git/refs | π΄ | Removes a ref outside Git's locking | The ref's value | Move the file aside instead of deleting it | Move it back | An empty or unreadable ref file that git update-ref -d cannot delete | 13.11, 13.18 |
Deleting or editing files under .git/objects | π΄ | The object database | One object can be part of every snapshot in the history | None | Write the same content back, or copy the object from another clone (Lab 1.2) | Never | 2.14 |
rm -rf .git | π΄ | Deletes the repository and leaves the working tree files | Every commit that was never pushed, every local branch, stash and reflog | git log --oneline --all, git branch -vv | None through Git; another clone or a disk backup | A repository created by mistake that holds no commit you need | 1.14 |
5. Reset
| Command | Label | What it changes | What it can destroy | Preview | Recover | When appropriate | Section |
|---|---|---|---|---|---|---|---|
git reset --soft <commit> | π‘ | The branch ref | Nothing | git log --oneline <commit>..HEAD | The same mode with ORIG_HEAD, or the reflog | Regrouping private commits; git reset --soft @{u} after a rejected push of an amend | 11.4, 21B.23 |
git reset [--mixed] <commit> | π‘ | The branch ref and the index | Nothing in files; staged versions lose their index entry | As above, and git diff --cached | As above | While the commits are private | 11.4 |
git reset --keep <commit> | π‘ | The branch ref, the index, files that differ between the two commits; refuses when it would overwrite a local change | Nothing | git log --oneline <commit>..HEAD | ORIG_HEAD, the branch reflog | The reset to use during recovery, in place of --hard | 11.6, 13.7, 30.23 |
git reset --merge [<commit>] | π΄ | As --keep for unstaged changes; discards staged changes | Staged changes | git diff --cached | Commits: reflog. Staged content: git fsck --lost-found | Backing out of a merge, a pop or a pick that left the index unmerged | 8.21, 11.6 |
git reset --hard <commit>, git reset --hard ORIG_HEAD | π΄ | The branch ref, the index, and every tracked file that differs from the target | Every uncommitted change to tracked files, and untracked files that are in the target's way | git status -s, git diff HEAD --stat, git log --oneline <commit>..HEAD | Commits: reflog. Staged content: git fsck --lost-found. Unstaged: none | When the working tree is known to be disposable; git stash push -u first when unsure; ORIG_HEAD only as the very next command | 9.16, 11.5, 13.7 |
6. Merge, rebase, cherry-pick, revert, bisect
| Command | Label | What it changes | What it can destroy | Preview | Recover | When appropriate | Section |
|---|---|---|---|---|---|---|---|
git merge <other> (fast-forward or true merge), --no-ff, --ff-only | π‘ | Moves the current branch; rewrites index and working tree | Nothing permanently | git merge-tree --write-tree --name-only HEAD <other>; git diff HEAD...<other>; git log HEAD..<other> | Before pushing: git reset --merge ORIG_HEAD or git reset --keep ORIG_HEAD (check it first, or take the commit from the reflog). After pushing: git revert -m 1 | From a clean tree | 8.3, 8.4, 8.12, 27.21 |
git merge -X ours, -X theirs, -X ignore-space-change | π‘ | As above, and silently drops one side of every conflicting hunk | The dropped side's changes, in the result | Merge without the option first | As above; audit with --remerge-diff | Only when one side's hunks are the decision | 8.6 |
git merge -s ours <other> | π‘ | Records <other> as merged and takes none of its content | Nothing in objects; the branch counts as merged while its changes are missing | git diff HEAD...<other> shows what is discarded | As for git merge | Deliberately superseding a branch | 8.6 |
git merge --squash <other>, git merge --no-ff --no-commit <other> | π‘ | Index and working tree; no commit | Nothing | As for git merge | git reset --merge | Reviewing or editing the result before committing | 8.12 |
git merge --continue, git commit during a merge | π‘ | Creates the merge commit and moves the branch | Nothing | git diff --cached --check; run the tests | git reset --merge ORIG_HEAD before pushing | After every conflict is resolved and checked | 8.10 |
git merge --quit | π‘ | Deletes the merge state only | Nothing in files; no command resumes the merge | git status | git reset --merge | Rarely | 8.10 |
git merge --abort, git reset --merge | π΄ | Resets index and working tree to HEAD; deletes the merge state | Every resolution made so far, and any uncommitted edit that was staged during the merge | git status, git diff --cached --stat | Staged content survives as unreferenced objects that git fsck lists (Lab 6.7); unstaged resolution edits are gone | The merge was a mistake or needs a different approach | 8.10, 8.21 |
git restore --ours|--theirs|--merge <path>, and the git checkout forms | π΄ | Overwrites the working tree file with one side, or with a fresh marker version | Your hand edits to that file, which exist nowhere else until you git add them | git diff --ours, git diff --theirs, git diff <path> | git restore --merge <path> brings back the starting point, not your edits | The whole file from one side is the decision; a resolution, yours or rerere's, is wrong | 8.10, 8.21, 14C.15 |
git rebase <upstream>, --onto, -i, --autosquash, --keep-base, --rebase-merges | π‘ | New commits; the branch ref, at the end | Nothing permanently | git log --oneline <upstream>..HEAD lists what will be replayed | git reset --hard <branch>@{1}, or git reset --keep ORIG_HEAD; a backup ref | Your own unpublished commits; ask before rebasing a branch others push to | 9.2, 9.5, 9.6 |
git rebase --update-refs | π‘ | Also every local branch inside the range | Nothing permanently | With -i, the update-ref lines | Each branch from its own reflog | Stacked branches | 9.9 |
git rebase --root | π‘ | Every commit of the branch | Nothing locally | git log --oneline | Reflog; for a published repository there is no clean recovery | Unpublished repositories | 9.23 |
git rebase --continue | π‘ | Commits the index as the copy, runs on | Nothing | git status, git diff --cached | Reflog, after the rebase | After git add of the resolution; never --amend at a conflict stop | 9.11 |
git rebase --skip | π‘ | Omits the stopped commit | Nothing: the original is in the old history | git rebase --show-current-patch | Redo | A commit that is already upstream | 9.11 |
git rebase --abort | π‘ | Index and working tree back to the old tip | Resolution work in progress; commits made inside the rebase leave the branch | git status, git diff, git log <branch>..HEAD | Commits: the HEAD reflog or a rescue ref. Uncommitted edits: none | After anchoring new commits with a branch | 9.11, 29.3 |
git rebase --quit | π‘ | Removes the state directory; HEAD stays detached | Nothing | git status | git switch <branch> | Rarely; leave with --abort | 9.11 |
git pull --rebase | π‘ | Fetches; re-creates your unpushed commits on the fetched upstream | Nothing permanently; after someone's forced push it can drop commits | git fetch, then git log --oneline @{u}..HEAD | git reset --hard <branch>@{1} | Unpushed local commits; look before integrating after a forced update | 9.13, 12.6 |
git pull --ff-only, git pull --no-rebase | π‘ | Fetch, then moves the branch forward or creates a merge commit; working tree | Nothing permanently | git fetch; git log ..@{u}; git log --oneline --graph HEAD @{u} | git reset --hard ORIG_HEAD (itself π΄); the reflog | Not in automation: a job that pulls can create merge commits nobody reviewed | 12.6, 12.16 |
git cherry-pick <commit>, with or without -x, -e, -m | π‘ | Adds commits to the current branch; updates index and working tree; refuses to overwrite local changes | Nothing | git show <commit>; git merge-tree --write-tree --merge-base=<commit>^ HEAD <commit>; git cherry -v | git reset --keep ORIG_HEAD or the branch reflog; git revert if already pushed | Backports (-x always), and recovery of single commits | 10.2, 10.4, 13.7 |
git cherry-pick -n <commit> | π‘ | Index and working tree; no commit, no in-progress state | Nothing | git show <commit> | git restore --staged --worktree <paths> | Combining several changes into one commit | 10.4 |
git cherry-pick --skip, --abort, --quit | π‘ | --skip discards the stopped pick; --abort returns branch, index and working tree to the state before the sequence; --quit removes the sequencer state only | Resolution work in progress | git status | Completed copies are in the reflog; resolution work is gone | An interrupted sequence | 10.6 |
git revert <commit>, git revert -m 1 <merge> | π‘ | Adds one commit that applies the inverse change | Nothing now; after -m 1 a later merge of the same branch is silently empty | git show <commit>; git show --stat <merge> | Revert the revert; before pushing, git reset --keep HEAD~1 | The undo for published history; -m 1 only when the merged branch will never be merged again as it is, or with the re-merge procedure in the message | 10.11, 11.8, 11.9, 30.23 |
git revert -n <commit> | π‘ | Index and files | Nothing | git show <commit> | git revert --abort | Several reverts in one commit | 11.16 |
git merge --abort, git rebase --abort, git cherry-pick --abort, git revert --abort, git bisect reset as "leave" commands | π‘ (merge: π΄, above) | End the operation; reset index and working tree to the starting commit | Uncommitted edits made during the operation | git status, git diff, git log <branch>..HEAD | Commits: the HEAD reflog or a rescue ref. Uncommitted edits: none | After the PRESERVE step of the troubleshooting method | 29.6, 29.14 |
git bisect start, good, bad, skip, run, replay | π‘ | Detaches HEAD and checks out other commits; writes refs/bisect/* and BISECT_* | Nothing | git status must be clean; git rev-list --count <good>..<bad> | git bisect reset | Finding the commit that changed a property | 14A.20 |
git bisect reset | π‘ | Checks out the starting branch; deletes the bisect state | The session, unless you saved it | git bisect log > file to keep the session | git bisect replay file | At the end of a session | 14A.22 |
git history reword, fixup, split (experimental; reword and split since Git 2.54, fixup since 2.55) | π‘ | New commits; every descendant local branch moves | Nothing locally | --dry-run; git branch -r --contains <commit> first | Each branch's reflog | Local cleanup only | 14D.6 |
git replay --onto, --advance, --revert (experimental; since Git 2.44, updates refs itself since 2.53, --revert since 2.54) | π‘ | New commits; refs move in one transaction | Nothing where a reflog exists | --ref-action=print | Reflog, where one exists; none by default in a bare repository | Rebase and cherry-pick without a working tree | 14D.7 |
git am | π‘ | New commits on the current branch | Nothing | git apply --check | git am --abort while it is stopped; afterwards the reflog of the branch | The patch-based workflow | 14D.11 |
7. Remotes, pushes and tags
Git, not GitHub. The rows in this section describe what Git does against any server. On GitHub, rulesets and branch protection can reject a push that Git would perform (Chapter 18), and a default bare Git server keeps no reflog (Chapter 12, section 12.8).
| Command | Label | What it changes | What it can destroy | Preview | Recover | When appropriate | Section |
|---|---|---|---|---|---|---|---|
git push, git push -u, git push --follow-tags | π‘ | Refs on the server (fast-forward or new), your remote-tracking refs; starts whatever listens for pushes | Nothing; published commits are not taken back quietly once others have fetched | git push --dry-run; git log @{u}.. | A revert commit and another push; moving the ref back needs a force | After reading <source> -> <destination> in the dry run | 1.12, 12.7 |
git push origin <tag>, git push --tags | π‘ | Creates tags on the server; on GitHub starts every workflow with a matching tag filter | Nothing in Git; a published name cannot be taken back cleanly | git push --dry-run; git ls-remote --tags origin | None that is clean: others may have fetched; deleting the tag does not unpublish what a workflow built | Push single tags or --follow-tags, not --tags | 14B.10, 20A.18 |
git push <remote> --delete <ref>, git push <remote> :<ref> | π΄ | Deletes a ref on the server, and your remote-tracking ref | The only name the branch's commits have there; a bare server keeps no record of the deletion | git ls-remote <remote>; git log --oneline origin/<branch> --not origin/main | From any clone that has the commit: git push <remote> <id>:refs/heads/<name> | Branches that are merged, or abandoned by agreement | 12.7, 27.21 |
git push --force, git push -f, git push <remote> +<ref> | π΄ | Sets the server's ref with no check, and your remote-tracking ref | Every commit on the server's branch that is not in your history, including commits you have never seen | git fetch, then git log --oneline HEAD..origin/<branch>; git push --dry-run --force | The old tip from any clone that had it (a teammate's branch, the reflog of anybody's remote-tracking ref), or the server's unreachable object until it is collected; push it back | None for the plain form: use a guarded form. Not in an incident | 9.17, 12.8, 30.23 |
git push --force-with-lease alone | π΄ | The server's ref, if it equals your remote-tracking ref; the check passes wrongly after a background fetch | As --force, when the remote-tracking ref was updated without your looking | --dry-run; git log HEAD..origin/<branch> | Only from a clone that has the commits | None: add --force-if-includes or an explicit value | 9.17, 9.23 |
git push --force-with-lease --force-if-includes, git push --force-with-lease=<ref>:<expect> | π΄ | The server's ref, only if it still has the value you rebased from or named | Commits on the server that only the server and stale clones have; with these forms everything removed is something your clone has | --dry-run; git log <local>..<expect> lists what will leave the branch | The old tip is in your own reflogs; a clone's reflog of origin/<ref>; on GitHub the Activity view (Chapter 13, section 13.15) | A branch that only you use, such as your own pull request branch after a rebase; restoring a known good value in an incident; tell teammates before the push | 9.17, 12.8, 27.3, 30.23 |
git push --mirror, git push --prune, git push --force --mirror origin | π΄ | Creates, forces and deletes server refs to match yours; from an ordinary clone --mirror would also publish your remote-tracking refs | Branches and tags that exist only on the remote | git push --dry-run --force --mirror origin | From other clones, ref by ref; on GitHub, the instruments of Chapter 13 | Once, after a freeze, at the end of a verified rewrite, from a --mirror clone | 12.16, 21B.17 |
git push --force origin <tag> | π΄ | Replaces a published tag | The meaning of a release name for everyone who fetches later; a workflow that names the tag runs different code | git ls-remote --tags origin | Force the old tag object back from a clone that has it | Putting a tag back at its published position; a name that is meant to move should be a branch | 14B.11, 21A.7 |
git push origin --delete <tag> | π΄ | Deletes a published tag | The only published name of a release commit, and the tag under a GitHub release | git ls-remote --tags origin | Push it again from a clone that kept it | A tag pushed by mistake minutes ago, announced | 14B.10 |
git fetch --prune, git remote prune <remote> | π‘ | Deletes stale remote-tracking refs and their reflogs | The reflog that recorded the server's earlier values | git remote prune --dry-run <remote> | git fsck --lost-found, then git branch <name> <id>, before garbage collection | Routine; not while a lost remote state still has no name | 7.13, 12.11, 13.18 |
git fetch --tags --force | π‘ | Replaces local tags with the server's | Your local value of a moved tag | git ls-remote --tags; git fetch --tags --dry-run; note git rev-parse <tag> first | Re-create from that ID; the old tag objects stay, dangling, until pruned | After confirming the server is right | 12.4, 14B.11 |
git fetch --prune --prune-tags | π΄ | Deletes every local tag the remote lacks | Private lightweight tags, which have no reflog and no object to find them by | Add --dry-run | Annotated tags: git fsck; lightweight: only the IDs in the command's output | A mirror or a CI cache that must equal the server | 14B.10 |
git remote set-url, set-head, set-branches, rename; git branch -u; git config set push.default | π‘ | Where later fetches, pulls and pushes go | Nothing | git remote -v; git config list --local | Set the previous value | Deliberate changes of topology | 12.2, 12.5, 12.10 |
git remote remove <name> | π‘ | The remote, its remote-tracking refs and reflogs, upstream settings | The reflogs of the remote-tracking refs | git branch -r, git branch -vv | Add and fetch again; the reflogs are lost | A remote you no longer use | 12.10 |
8. Reflogs, the object database and maintenance
| Command | Label | What it changes | What it can destroy | Preview | Recover | When appropriate | Section |
|---|---|---|---|---|---|---|---|
git gc, git maintenance run --task=gc | π‘ | Packs objects and refs; expires reflog entries by the configured periods; deletes unreachable objects older than two weeks | What was past the retention periods | git fsck --unreachable; git reflog expire --dry-run --all; git count-objects -v; git prune -n | Within the periods nothing is lost; beyond them, another clone or a backup | Rarely by hand: maintenance is automatic (geometric strategy by default since Git 2.54) | 3.17, 13.13, 26.3 |
git maintenance run | π‘ | Packs, packed-refs, commit-graph, multi-pack-index; expires reflog entries by the configured periods | Expired reflog entries | git maintenance is-needed (Git 2.53 or later); GIT_TRACE=1 | None for expired reflog entries | Hundreds of packs or thousands of loose objects | 26.3 |
git maintenance start, git maintenance register | π‘ | Global configuration and the system scheduler | Nothing | git help maintenance | git maintenance stop, git maintenance unregister | A deliberate choice on your own machine; never in the labs | 26.3 |
git gc --prune=now, git prune | π΄ | Deletes unreachable objects at once, with no grace period | Everything that only the grace period was keeping: dropped stashes, staged blobs, commits whose reflog entries are gone | git fsck --unreachable; git prune -n | None in this repository; another clone or a backup | The purge after a deliberate history rewrite, with no other process using the repository | 3.17, 13.13, 26.5 |
git repack -a -d | π΄ | Rewrites packs; drops the unreachable objects of the packs it replaces | Unreachable objects in those packs | git fsck --unreachable; git count-objects -v | Another clone or a backup only | After a deliberate history rewrite in a repository you have copied first | 3.17 |
git repack --geometric=2 -d | π‘ | Rewrites packs | Nothing reachable | git count-objects -v | Not needed for reachable objects | What automatic maintenance does for you | 26.4 |
git reflog expire, git reflog delete <ref>@{<n>}, git reflog drop <ref> (drop: Git 2.50 or later) | π΄ | Remove reflog entries | The record of where a ref used to point | --dry-run --verbose | git fsck, until a prune | Almost never by hand | 13.4, 13.18 |
git reflog expire --expire=now --all, usually followed by git gc --prune=now | π΄ | Empties every reflog, the stash list included; then deletes every unreachable object | Every recovery path in that clone: every commit, stash and staged blob that only a reflog, or nothing at all, was keeping | git reflog expire --dry-run --expire=now --all; git fsck --unreachable --no-reflogs lists what would go | None locally | The final step of a secret clean-up, in a cleanup clone, after rotation; in a stale clone after its owner has saved their work | 3.17, 13.13, 21B.17, 30.23 |
git refs migrate --ref-format=<format> (Git 2.46 or later) | π‘ | The ref storage format; values are kept | Nothing | --dry-run | Migrate back, or restore the copy of .git you made first | A clone that hits a case-insensitive ref conflict; throwaway repositories | 3.13, 14D.5 |
git filter-repo ..., git filter-branch ... | π΄ | Every affected commit and all descendants, on every rewritten ref | Signatures; with a wrong path argument, files you meant to keep | git-filter-repo --dry-run; run in a fresh clone | The untouched server and other clones, until you push | Data that stays harmful after rotation; for an unpushed mistake on a private branch a rebase is the smaller tool | 21B.17 |
9. Configuration, hooks and recorded resolutions
| Command | Label | What it changes | What it can destroy | Preview | Recover | When appropriate | Section |
|---|---|---|---|---|---|---|---|
git config set, unset, edit (Git 2.46 or later), in any scope | π‘ | One configuration file, which has no history | The previous value | git config get --show-origin <key>; git config list --show-origin --show-scope | Set the old value again; keep the global file under version control | Deliberate settings; the scope that should own the key | 14B.3 |
git config set --global init.default*, git config set --global safe.bareRepository explicit | π‘ | The behavior of later commands: new repositories; Git commands inside bare repositories then need --git-dir or GIT_DIR | Nothing | git config list --show-origin | Unset the keys | init.defaultBranch and safe.bareRepository now; the object and ref formats per repository, not globally | 14D.4, 21B.23 |
git config set --global --append safe.directory <dir> | π‘ | Makes Git trust that directory's configuration and hooks | Nothing; removes a protection | Read <dir>/.git/config and <dir>/.git/hooks first | git config unset --global --value=<dir> safe.directory | A directory whose contents you have read | 21B.23 |
git config set core.hooksPath, hook.<name>.*, filter.*, diff.*, merge.* | π‘ | Which programs Git runs on your machine | Nothing | git hook list --show-scope <event>; git config list --show-origin | Unset the keys | Programs you have read | 14C.11, 14C.12, 28.18 |
git hook run <event>; --no-verify | π‘ | Runs the local checks; skips them | Nothing | git hook list <event> | The server may still refuse | Testing a hook; a hook that is wrong | 14C.11 |
git rerere forget <path>, git rerere clear, git rerere gc | π‘ | Deletes recorded resolutions | The recorded resolution | git rerere diff; ls .git/rr-cache | Resolve again | A recorded resolution that is wrong | 14C.3 |
git lfs install without --local | π‘ | The filter keys go into your global configuration; hooks in the current repository | Nothing | git config list --show-origin | git lfs uninstall | In this book it is always run with --local (π’) | 22.3 |
10. Submodules, subtrees, LFS, sparse-checkout, worktrees
| Command | Label | What it changes | What it can destroy | Preview | Recover | When appropriate | Section |
|---|---|---|---|---|---|---|---|
git submodule update (default checkout) | π‘ | Moves the submodule's HEAD (detached) and its files; refuses to overwrite local edits to files it must change | Nothing; commits made on the detached HEAD lose their name | git submodule status; git -C <path> status | The submodule's reflog: git -C <path> reflog; git -C <path> branch <name> <id> | After every pull that moved a gitlink | 23.3, 23.5 |
git submodule update --remote | π‘ | As above, to the tip of the remote-tracking branch; the superproject then shows a modified gitlink | Nothing | git -C <path> fetch && git -C <path> log --oneline HEAD..origin/HEAD | git submodule update returns to the recorded commit | Moving the pointer on purpose, in a dedicated branch | 23.6 |
git submodule update --force, git submodule deinit --force | π΄ | Overwrites modified tracked files in the submodule | Uncommitted changes inside the submodule | git submodule foreach 'git status --short' | None for uncommitted edits | To get back to exactly the recorded state | 23.9, 23.16 |
git submodule deinit <path> | π‘ | Empties the working directory; removes the section from .git/config; refuses if there are local modifications unless --force | Nothing | git -C <path> status | git submodule update --init | A submodule you do not need locally | 23.11 |
git rm <submodule path> | π‘ | Removes the gitlink, the .gitmodules section and the working directory | Nothing | git status | git restore --staged --worktree .gitmodules <path>, then git submodule update --init | Removing the submodule from the project | 23.11 |
Deleting .git/modules/<name> | π΄ | Deletes a repository | Commits in the submodule that were never pushed | git -C <path> status; git -C <path> reflog | None | Removing a submodule completely from your clone, after checking for unpushed commits | 23.11, 23.16 |
git push --recurse-submodules=on-demand | π‘ | Pushes in the submodules, then the superproject | As for any push | git submodule foreach 'git status --short --branch' | As for any push | When the superproject commit needs submodule commits that are not published | 23.8 |
git subtree add, git subtree pull | π‘ | New commits on the current branch (a merge); needs a clean working tree | Nothing | git ls-remote <repository> <ref> | git reset --hard ORIG_HEAD if not pushed (π΄ for uncommitted work); git revert -m 1 if pushed | Vendoring a dependency's files into your own history | 23.13 |
git subtree push | π‘ | Creates or updates a branch in the library's repository | Nothing in yours | git subtree split first and inspect the result | Delete or reset that branch in the library's repository | Sending changes back upstream | 23.13 |
git lfs push, and git push with the pre-push hook | π‘ | Uploads LFS objects; on GitHub they are billed and cannot be deleted by you | Nothing | git lfs push --dry-run <remote> <ref>; git lfs status | None on GitHub short of deleting the repository (22.11) | After deciding what belongs in LFS | 22.6 |
git lfs prune | π‘ | Deletes local LFS objects that are pushed and not needed | Nothing while the remote has the objects | --dry-run --verbose; --verify-remote | git lfs fetch, if the remote has them | Reclaiming disk space | 22.8 |
git lfs migrate import, export (default scope or named branches) | π‘ | Rewrites unpushed commits; the working tree is left with pointers after import | Nothing locally | git lfs migrate info with the same options | Branch reflogs: git reset --hard <branch>@{1} | Unpushed commits that contain large files | 22.9 |
git lfs migrate import --everything, then a forced push | π΄ | Rewrites published history | Nothing locally; breaks every other clone | git lfs migrate info --everything; rehearse in a fresh clone | Before the push: reflogs. After: the old history from another clone | A coordinated rewrite | 22.9, 22.13 |
git sparse-checkout set, add, init --cone (init is deprecated: set does everything) | π‘ | Working tree, skip-worktree bits, pattern file, config.worktree; deletes ignored files in directories that leave the cone | Ignored files in directories that leave the cone, such as a local .env or a downloaded evaluation set | git status --short --ignored | set again or disable; ignored files are gone | After checking for ignored files you cannot regenerate | 24.4, 24.5 |
git sparse-checkout reapply, disable | π‘ | Removes, or writes, tracked files | Nothing tracked | git ls-files -t | set or add | After conflicts outside the cone | 24.4 |
git sparse-checkout set --sparse-index | π‘ | Rewrites the index in a form some tools cannot read | Nothing | Test the tools first | --no-sparse-index | Very large repositories | 24.6 |
git sparse-checkout clean -f (Git 2.52 or later) | π΄ | Deletes directories outside the cone that hold untracked files | Untracked files there, which were never in Git | --dry-run, --verbose | None | After reading the preview and moving away what you need | 24.5 |
scalar clone | π‘ | A clone, and also global configuration, a scheduler unless --no-maintenance, a monitor daemon | Nothing | git help scalar | scalar unregister or scalar delete; git fsmonitor--daemon stop | A deliberate choice on your own machine | 24.7 |
git worktree move | π‘ | The directory and both pointers | Nothing | git worktree list | Move it back | Reorganising directories | 25.6 |
git worktree remove | π‘ | Deletes a clean working tree and its entry, including its HEAD reflog | The HEAD reflog of that worktree; the name of detached commits made there | git -C <path> status --short; git -C <path> log --oneline -3 | git worktree add again; detached commits via git fsck --lost-found | A finished task; create a branch before removing | 25.6 |
git worktree remove --force | π΄ | As above, and deletes uncommitted and untracked files | Files that were never committed | git -C <path> status --short --ignored | None for files that were never committed | Only after looking | 25.6, 25.9 |
git worktree prune | π‘ | Deletes entries whose working tree is missing, including their HEAD reflogs | Those HEAD reflogs | git worktree prune --dry-run --verbose | git worktree add again; a moved tree: git worktree repair before pruning | After a working tree was deleted with rm -rf | 25.6 |
git worktree add --force for a branch that is already checked out | π΄ | Two indexes on one ref | The consistency of the stale worktree: staged changes nobody made | None; do not use | git reset --hard in the stale worktree after saving its work | Never | 25.4, 25.9 |
python3 tools/dataref.py checkout (and dvc checkout) | π΄ for unrecorded data edits | Overwrites data files with the versions the pointers name | Unrecorded edits to data files | python3 tools/dataref.py verify | None for unrecorded edits; record them first with add | After verify reports "ok" | 28.18 |
11. GitHub: the GitHub CLI, authentication, rules and Actions
GitHub, not Git. Every π΄ command here changes GitHub objects that no clone contains (Chapter 15, section 15.22). The preview is always a read with the same tool. Flags were checked with
gh <command> --helpof version 2.88.1; nothing was run against GitHub. The full command tables are in the GitHub CLI cheat sheet.
| Command | Label | What it changes | What it can destroy | Preview | Recover | When appropriate | Section |
|---|---|---|---|---|---|---|---|
gh repo create --source=. --push | π‘ | A repository on GitHub, a remote, a push | Nothing; what reached a public repository is published | git log, git status; check --public or --private | gh repo delete | After reading what the push will publish | 15.22 |
gh issue create, gh pr create, gh label create, gh release create --verify-tag --draft | π‘ | GitHub objects that notify people; gh pr create may push the branch | Nothing; notifications are not recalled | gh pr create --dry-run; --draft | Close or delete; gh pr close | Ordinary collaboration | 15.22, 17.21 |
gh repo edit (features, merge methods), gh repo fork, gh repo sync | π‘ | Settings; a new repository; a fast-forward | Nothing | gh repo view --json; git log HEAD..upstream/main | Set the previous value; delete the fork | Deliberate changes | 15.22 |
gh repo sync --force | π΄ | Hard reset of the destination branch | Commits on the destination branch that the source lacks | git log upstream/main..origin/main after a fetch; git rev-list --left-right --count upstream/main...origin/main | Push the old commits back from a clone that still has them | A fork branch that holds nothing of its own | 15.7, 17.21, 27.21 |
gh api with POST, PATCH, PUT, DELETE | π΄ | Whatever the endpoint says, with all your permissions | Depends on the endpoint | The GET first | Depends on the endpoint; often none | After the GET, with the saved JSON at hand | 15.22 |
gh release create TAG without --verify-tag | π΄ | May create a tag on the default branch and start tag workflows | Nothing existing; publishes a version name | git ls-remote --tags origin TAG | Delete release and tag; consumers may have fetched it | Use --verify-tag | 15.22 |
gh release delete --cleanup-tag, git push origin --delete TAG | π΄ | A published version's name | The release and its tag | gh release view TAG | Re-create from the recorded commit ID | A release published by mistake | 15.22 |
gh repo edit --visibility, gh repo delete, gh repo rename, gh repo archive | π΄ | Visibility with its side effects; the repository, its URL, its writability | The side effects of a visibility change cannot be undone | Section 15.5; ask who depends on the URL | Visibility can be changed back, its side effects cannot; restore within 90 days unless the fork network is not empty | Planned, announced changes | 15.5, 15.22 |
gh pr update-branch, gh pr edit --base, gh pr merge | π‘ | Move the head branch, change the base, or write to the base branch; can dismiss approvals | Nothing | gh pr view, gh pr checks --required | Change the base back; gh pr revert | The normal end of a reviewed pull request | 17.21 |
gh pr merge --admin, a bypass merge | π΄ | Writes to a protected branch without the required reviews or checks | Nothing in Git; the control | gh pr checks --required | gh pr revert; record why | A planned migration or a declared emergency, by a named person, with the reason written down where the audit will find it | 17.16, 18.21 |
gh api --method POST repos/ORG/REPO/rulesets --input file.json | π΄ | Creates a ruleset; if Active, it binds everyone at once, you included | Nothing | Create it with "enforcement": "disabled", then gh ruleset check | Set enforcement to disabled, or delete the ruleset | Rules as reviewed JSON | 18.21 |
gh api --method PUT .../rulesets/ID | π΄ | Replaces settings of a ruleset | The previous settings | gh api .../rulesets/ID first and keep the JSON | PUT the saved JSON back | Changing a rule | 18.21 |
gh api --method DELETE .../rulesets/ID | π΄ | Removes the protection for every ref it targeted, immediately | Nothing directly; it makes force pushes and deletions possible | Save the JSON first | Re-create from the saved JSON; ruleset history exists only on Enterprise | A planned migration or a declared emergency | 18.21 |
git push --force to a shared base branch, or to a branch whose protection was just removed | π΄ | Removes commits from the remote branch; corrupts open pull requests | Commits others pushed | git log <branch>..origin/<branch> and the reverse | Chapter 13; Chapter 30 | Not appropriate | 17.21, 18.21 |
Editing CODEOWNERS in a pull request | π‘ | Who is requested, and under a rule who can block, from the next merge on | Nothing | The errors endpoint with ref | Revert the commit | With review by the current owners | 19.14 |
Removing the owner of /.github/ | π΄ | Lets any writer change reviewers and workflows without owner review | No data; removes a control | Review the diff of the file | Restore the line; audit what merged in between | Never without a replacement owner | 19.14 |
gh workflow run <file> --ref <branch> | π‘ | Starts a run, which uses minutes and may deploy if the workflow deploys | Nothing in Git | Read the workflow file at that ref: gh workflow view --yaml | gh run cancel <run-id>; deploy the previous commit | A workflow_dispatch workflow you have read | 20A.18, 20B.18 |
gh run rerun <run-id>, --failed | π‘ | Starts a new attempt on the original commit | Nothing | gh run view RUN_ID --json headSha and compare with main | Cancel the attempt; start a new run from the current commit | A flaky failure, not a stale commit | 20A.18, 20B.18 |
gh run cancel | π‘ | Stops a run, possibly mid-deployment | The consistency of a half-finished deployment | gh run view | Re-run; check the target's state by hand | A run that should not finish | 20B.18 |
gh secret set, gh variable set | π‘ | Overwrites the stored value | The old secret value, which cannot be read back | gh secret list --env NAME | Set the previous value again from your secret store | Rotation | 20B.18 |
gh api -X PUT .../environments/NAME | π΄ | Replaces the environment's protection settings with the body sent | The previous settings | gh api .../environments/NAME | Send the previous configuration again | Environment rules as code | 20B.18 |
gh cache delete --all | π‘ | Removes every cache of the repository | Nothing permanent | gh cache list | Caches are rebuilt by the next runs, slowly | A poisoned or oversized cache | 20B.18 |
git tag -f -a v1, then git push --force origin v1 | π‘, then π΄ | Moves a local tag; replaces a published tag that workflows may name | What uses: ...@v1 meant yesterday | git rev-parse 'v1^{commit}'; git ls-remote --tags origin v1 | Re-create the tag at the old commit; push the old tag object back, if you still have it | The reason to pin actions by full commit ID: see the action pins | 21A.7 |
Pushing a change under .github/workflows/ | π‘ | Git: an ordinary commit. GitHub Actions: the next matching event runs the new file with the repository's token and secrets | Nothing in Git | Review the diff with section 21A.19 | Revert the commit; rotate any secret the changed workflow could read | After review by the owner of /.github/ | 21A.22 |
ssh-keygen -t ed25519 -f NEW | π‘ | Creates two files; overwrites an existing key only after asking | An overwritten private key | ls ~/.ssh | None for an overwritten private key | A new key under a new file name | 16.23 |
ssh-add, ssh-add --apple-use-keychain | π‘ | The agent's key list; a keychain entry | Nothing | ssh-add -l | ssh-add -d | Loading the key you mean | 16.23 |
ssh-keygen -R HOST | π‘ | Removes a host's lines from known_hosts, keeping a .old copy | Nothing | ssh-keygen -F HOST | The .old file | After comparing the published fingerprint | 16.11, 16.23 |
StrictHostKeyChecking no, or deleting known_hosts wholesale | π΄ | Removes the host key check | The protection against a wrong host | None | Install the published lines instead | Never; on a CI runner install the published lines | 16.11 |
git config set --global credential..., gh auth setup-git, gh auth login, gh auth switch | π‘ | Which credential answers, for every repository | Nothing | git config get --show-origin --all credential.helper | Set the previous value; git config unset | Deliberate changes of identity | 16.23 |
git credential reject, git credential-osxkeychain erase | π‘ | Deletes a stored credential | The stored credential | git credential fill shows what is stored | Log in again | A stale or wrong stored credential | 16.23 |
gh auth token, gh auth status --show-token | π΄ | Prints a live token to the terminal and its scrollback | The secrecy of the token | Ask whether you need the value at all | Revoke the token | Rarely; never in a shared or recorded terminal | 16.23 |
gh ssh-key delete, deleting a token or a deploy key in the web interface | π΄ | Every machine and job that used it stops | Access for those machines and jobs | List where it is used | Create and distribute a new one | Rotation, with the list of users at hand | 16.23 |
| Writing a token into a URL, a file or a command line | π΄ | Publishes the secret to logs, history and backups | The secrecy of the token | None | Revoke it; cleaning is not enough | Never | 16.23 |
For the response to a leaked secret, the order of the steps matters more than any single command: see the security cheat sheet and Chapter 21B. For recovery after any π΄ command, see the emergency recovery page and the disaster-recovery playbook.
12. Where chapters label the same command differently
Earlier drafts of the chapters labelled fifteen commands in two ways, because each chapter judged the worst case in its own context. The editorial pass of 6 October 2026 chose one label per command from the definitions in section 1, and the chapters, this file, the command reference, the cheat sheets and the playbooks now agree. The table records each decision. Git behavior named in the last column was checked on Git 2.55.0; the gh rows were checked against gh <command> --help of 2.88.1 and not run against GitHub.
One test settled most rows: a command is π’ only if no ref moves and nothing is lost. A command that adds commits to the current branch through the merge machinery moves that branch and rewrites index and working tree, which is the reason git merge is π‘ in 8.21; a command that overwrites a setting loses the previous value, because a configuration file keeps no history.
| Command | Label now | Sections changed (label before) | Reason |
|---|---|---|---|
git cherry-pick <commit>, git cherry-pick --continue | π‘ | 2.10, 2.15, 10.2, 10.14, 27.9, 27.21 (π’) | It moves the current branch and rewrites index and working tree, as git merge does; it adds commits and removes none |
git revert <commit>, git revert -m 1 <merge> | π‘ | 10.11, 10.14, 11.8, 11.16, 27.15, 27.21 (π’) | As above; and after -m 1 a later merge of the same branch is silently empty |
git merge --abort | π΄ | 29.14, the two playbooks, the emergency page (π‘) | It discards every resolution made so far and every edit staged during the merge, and the manual warns that uncommitted changes present when the merge started cannot always be reconstructed. git rebase --abort, git cherry-pick --abort and git revert --abort stay π‘ in their own rows |
git push --force-with-lease --force-if-includes | π΄ | 1.8 (π‘, as an example) | Every forced push replaces history on a remote, where you have no reflog (9.17, 12.8). The conditions make it the right form of a π΄ command, not a π‘ one |
git push --force-with-lease | π΄ | 17.21 (π‘) | As above; alone, the check passes wrongly after a background fetch |
git update-ref -d <ref> | π΄ | 3.17 (π‘) | It deletes the ref together with its reflog, which is the safety net for that ref |
git repack -a -d | π΄ | 26.18 (π‘) | It drops the unreachable objects of the packs it replaces, at once and without the two-week grace period. git repack --geometric=2 -d stays π‘ |
rm .git/index, then git reset | π΄ | 5.18 (π‘) | It removes the index outside Git's locking and discards the record of what was staged, the flag bits and the conflict stages; no reflog holds them. Moving the file aside first (mv .git/index .git/index.corrupt, 13.11) is π‘, because the file can be moved back |
git config set <key>, unset, edit, in any scope | π‘ | 14A.17, 14A.27 (blame.ignoreRevsFile, diff.algorithm, blame.markIgnoredLines), 14D.4 (init.default*), 21B.23 (safe.bareRepository) (π’) | It rewrites a file that keeps no history, and it changes the behavior of later commands. This is the label of 1.15 and 14B.21 |
git lfs checkout | π’ | 22.7 (π‘) | It replaces unmodified pointer files with their content from the local store and never overwrites a modified file: no ref moves and nothing is lost. git lfs pull is fetch plus checkout and carries the same label (22.14). The row this file had for the command is removed, because π’ commands are not listed here |
git subtree add | π‘ | 23.13 (π’) | It creates a merge on the current branch |
gh pr create | π‘ | 17.2, 17.21 (π’) | It may push the branch, and it creates a GitHub object that notifies people |
gh api with POST, PATCH, PUT or DELETE, including the ruleset calls of 18.21 and the environment call of 20B.18 | π΄ | 18.21 (POST and PUT on rulesets), 20B.18 (PUT on an environment) (π‘) | The call does whatever the endpoint and the body say, with all your permissions (15.22). A PUT replaces settings that exist nowhere else unless you saved the JSON first; the POST that creates a ruleset destroys nothing and keeps the label of the command, with that stated in its row |
Three labels of the GitHub CLI cheat sheet that no chapter table carried were confirmed in the same pass:
| Command | Label | Reason |
|---|---|---|
gh ssh-key add | π‘ | It attaches a credential to your account that can act as you until it is deleted; gh ssh-key delete undoes it (16.8) |
gh repo set-default <repository> | π‘ | It records a setting in the clone that decides which repository later gh commands act on; --view is π’ and --unset undoes it (15.16) |
gh api graphql | π’ with a query, π΄ with a mutation | A GraphQL query only reads, although the request is sent as a POST; a mutation writes, and falls under the rule of 15.22 |
13. Chapter files
The section numbers in the tables point into these files.
| Sections | File |
|---|---|
| 1.x | Chapter 1: Fundamentals |
| 2.x | Chapter 2: The Mental Model |
| 3.x | Chapter 3: Git Internals |
| 4.x | Chapter 4: The Working Tree |
| 5.x | Chapter 5: The Index |
| 6.x | Chapter 6: Commits |
| 7.x | Chapter 7: Branches |
| 8.x | Chapter 8: Merge |
| 9.x | Chapter 9: Rebase |
| 10.x | Chapter 10: Cherry-pick |
| 11.x | Chapter 11: Reset, Revert, Restore |
| 12.x | Chapter 12: Remote Operations |
| 13.x | Chapter 13: Recovery |
| 14A.x | Chapter 14A: History investigation |
| 14B.x | Chapter 14B: Configuration, Aliases, Tags and Signing |
| 14C.x | Chapter 14C: Stash Internals, Rerere, Attributes, Hooks |
| 14D.x | Chapter 14D: The Frontier |
| 15.x | Chapter 15: GitHub |
| 16.x | Chapter 16: Authentication |
| 17.x | Chapter 17: Pull Requests |
| 18.x | Chapter 18: Branch Protection and Rulesets |
| 19.x | Chapter 19: CODEOWNERS |
| 20A.x | Chapter 20A: GitHub Actions Fundamentals |
| 20B.x | Chapter 20B: GitHub Actions: delivery, runners, cost and debugging |
| 21A.x | Chapter 21A: GitHub Actions security |
| 21B.x | Chapter 21B: Repository security, identity, the Git client, and secret-leak response |
| 22.x | Chapter 22: Git LFS |
| 23.x | Chapter 23: Submodules and Subtrees |
| 24.x | Chapter 24: Monorepos |
| 25.x | Chapter 25: Worktrees |
| 26.x | Chapter 26: Performance |
| 27.x | Chapter 27: Open source and team workflows |
| 28.x | Chapter 28: AI/ML workflows |
| 29.x | Chapter 29: Production Troubleshooting |
| 30.x | Chapter 30: Incident Response |
Written against Git 2.55.0 on macOS; GitHub facts as of 1 October 2026. Git transcripts are output from the bookβs lab scripts; GitHub behaviour is described from GitHubβs documentation. Found a mistake? Tell us through the enquiry form.