| name | git |
| description | Advanced Git CLI mastery, recovery, and troubleshooting (git 2.54+). Use whenever a git task goes beyond the basics — recovering lost commits/branches/stashes (reflog, fsck), undoing a bad reset/merge/rebase/amend, rewriting history (interactive rebase, --autosquash, --update-refs, filter-repo), resolving merge conflicts or using rerere, working with worktrees, bisect, cherry-pick, revert, or stash, understanding refspecs/remotes/push behavior and --force-with-lease, configuring .gitattributes/.gitignore/hooks, exploring git internals (object model, plumbing like cat-file/rev-parse), or diagnosing confusing errors (detached HEAD, dubious ownership, non-fast-forward, CRLF). Trigger even when the user doesn't name the exact command but is clearly stuck or doing something non-trivial in git. For commit-message wording use conventional-commits; for stacked/dependent branches use git-chain; for auto-folding fixups use git-absorb; for Jujutsu use jj. |
Git — Advanced CLI Mastery & Recovery
Overview
This skill is the power-user and recovery reference for the Git command line (targeting git 2.54+). It is not a beginner tutorial — it assumes the everyday add/commit/push loop is already understood and instead focuses on the parts of Git that are easy to get wrong, easy to forget, or costly to mess up:
- Getting back work that seems lost (reflog,
fsck, dangling objects).
- Rewriting history safely and understanding what each operation actually moves.
- The mental models (object graph, HEAD/index/worktree, refspecs) that make confusing behavior obvious.
- Power features Claude tends to underuse: worktrees, bisect, rerere, sparse-checkout, partial clone, notes, bundles.
- Diagnosing cryptic error messages.
Core principle: In Git, commits are rarely truly lost until garbage collection runs (default ~90 days for reachable-from-reflog, ~30 for unreachable). Almost every "oh no" is recoverable via the reflog or fsck. Stay calm; diagnose before acting; avoid --hard/--force/clean until you understand the current state.
When to Use This Skill
- Recovering lost commits, branches, or stashes; undoing a reset/merge/rebase/amend.
- Interactive rebase, history surgery, squashing/splitting/reordering commits, removing a large file or secret from history.
- Merge strategy choices, conflict resolution,
rerere, cherry-pick, revert (including reverting a merge).
- Inspecting history:
log formats, pickaxe (-S/-G), line-log (-L), blame, range-diff, git grep.
- Remotes, refspecs, fetch/pull/push semantics, upstream tracking, safe force-pushing.
- Worktrees and stash management.
- Git internals / plumbing (
cat-file, rev-parse, rev-list, object model, packfiles, gc).
git config, .gitattributes, .gitignore, hooks, aliases.
- Submodules, subtree, sparse-checkout, partial/shallow clone, bundles, notes, bisect, signing.
- Any confusing git error or unexpected state.
When NOT to Use This Skill (defer to the specialist)
| Task | Use instead |
|---|
| Writing/formatting a commit message (Conventional Commits) | conventional-commits skill |
| Managing/rebasing chains of stacked dependent branches/PRs | git-chain skill |
| Auto-folding uncommitted changes into the right earlier commits | git-absorb skill |
Jujutsu (jj) workflows | jj skill |
This skill covers core git itself; the workflows above have dedicated skills. Cross-reference them rather than reinventing.
Mental Models You Must Get Right
The object model
Git stores four object types, addressed by the hash of their content:
- blob — file contents (no name).
- tree — a directory listing: names → blobs/trees + modes.
- commit — a snapshot: one root tree + parent commit(s) + author/committer + message.
- tag (annotated) — a named, signed pointer to an object.
Branches and tags are just refs — files under .git/refs/ (or packed-refs) containing a commit hash. HEAD is a ref that usually points at a branch (ref: refs/heads/main); when it points directly at a commit you are in detached HEAD. A commit is "reachable" if you can walk to it from a ref; unreachable commits await gc. Deep dive: internals-plumbing.md.
HEAD, the index, and the working tree
Three states a file moves between:
working tree --(git add)--> index/staging --(git commit)--> HEAD (history)
(edits) (staged) (committed)
git status shows differences between all three. git diff = worktree vs index; git diff --staged = index vs HEAD.
git restore <path> (worktree) / git restore --staged <path> (unstage) are the modern, scoped undo verbs; git switch changes branches. They replace the overloaded git checkout.
The three git reset modes (memorize this)
git reset [--soft|--mixed|--hard] <commit> moves the current branch to <commit>, differing only in what else it touches:
| Mode | Moves HEAD/branch | Index | Working tree | Net effect |
|---|
--soft | ✅ | unchanged | unchanged | Prior changes left staged. Great for squashing: git reset --soft HEAD~5 && git commit. |
--mixed (default) | ✅ | reset to commit | unchanged | Prior changes left unstaged (un-commits and un-stages). |
--hard | ✅ | reset to commit | reset to commit | ⚠️ Discards uncommitted changes; may remove untracked files in the tree. |
Two specialist modes: --merge and --keep reset while preserving local edits (and abort on conflict) — used to back out of a failed merge without losing work. Full treatment: recovery.md.
reset moves a branch pointer; revert makes a new commit that undoes another (safe on shared history); restore/checkout change file contents, not the branch.
Refspecs in one line
A refspec is [+]<src>:<dst> mapping refs between repos. git push origin main ≈ refs/heads/main:refs/heads/main; a leading + forces (non-fast-forward); an empty <src> deletes (git push origin :old-branch). Fetch config like +refs/heads/*:refs/remotes/origin/* is why remote branches land under origin/. Full treatment: refspecs-remotes.md.
"Oh No" — Recovery Quick Reference
Start here when something went wrong. Each row links to the deep guide. Diagnose with git status, git reflog, and git log --oneline --all --graph before destructive action.
| Situation | First move | Depth |
|---|
| Committed to the wrong branch | git log note the hash → git reset --hard HEAD~1 on wrong branch, git cherry-pick <hash> on right one | recovery.md |
| "Lost" a commit after reset/rebase | git reflog → find hash → git branch rescue <hash> or git reset --hard <hash> | recovery.md |
| Deleted a branch by mistake | git reflog (or reflog show <branch>@{...}) → git branch <name> <hash> | recovery.md |
| Dropped/lost a stash | git fsck --no-reflogs --unreachable | grep commit, inspect, git stash apply <hash> | recovery.md |
| Bad merge just now | git reset --merge ORIG_HEAD (keeps local edits) | recovery.md |
| Botched rebase mid-flight | git rebase --abort; if finished, git reset --hard ORIG_HEAD | recovery.md |
| Amended and lost the old commit | git reflog → the pre-amend entry is still there | recovery.md |
| Undo a pushed commit safely | git revert <hash> (new inverse commit; never rewrite shared history) | branching-merging.md |
| Unstage / discard a file | git restore --staged <f> / git restore <f> | recovery.md |
| Detached HEAD with new commits | git branch <name> before switching away, or git switch -c <name> | troubleshooting.md |
Safety Rules (footguns)
- Never
--force push a shared branch. Use git push --force-with-lease (refuses if the remote moved under you). See refspecs-remotes.md.
- Don't rewrite published history others have pulled. Rewrite only local/unshared commits; otherwise
revert.
git reset --hard and git clean -fd destroy uncommitted/untracked work with no reflog. Run git stash or git clean -nd (dry run) first.
- Prefer
git switch/git restore over git checkout — narrower, less surprising.
git filter-repo rewrites every commit hash (invalidates clones); git filter-branch is discouraged by upstream. See history-rewriting.md.
- After any rewrite, collaborators must re-clone or hard-reset to the new history — communicate it.
Where to Look — Reference Map
Read the focused file for the task at hand (progressive disclosure — don't load them all):
| Reference | Covers |
|---|
| recovery.md | reflog, fsck --lost-found, undoing reset/merge/rebase/amend, recovering commits/branches/stashes, the reset modes in depth, ORIG_HEAD/MERGE_HEAD |
| history-rewriting.md | interactive rebase (all todo commands), --autosquash/--rebase-merges/--update-refs/--onto/--root, amend, splitting commits, filter-repo, why not filter-branch |
| branching-merging.md | merge strategies (ort/octopus/ours/subtree), -X options, ff control, rebase-vs-merge, cherry-pick, revert (incl. merges), rerere |
| inspection.md | log (formats, --graph, -S/-G, -L, --follow), diff (.. vs ..., --word-diff), show, blame, shortlog, range-diff, describe, git grep |
| refspecs-remotes.md | remotes, fetch/pull/push semantics, refspecs, upstream tracking, push.default, --force-with-lease, tags, multiple remotes |
| worktrees-stash.md | git worktree (add/list/remove/prune/move/lock), git stash (push -p/-k/-u, apply vs pop, branch, create/store, internals) |
| internals-plumbing.md | object model, refs/packed-refs, the index, cat-file/hash-object/rev-parse/rev-list/ls-tree/ls-files/update-ref, packfiles, gc, gitrevisions syntax |
| config-attributes-hooks.md | git config (levels, includeIf), .gitattributes (eol/filters/diff/merge drivers), .gitignore, hooks, aliases |
| advanced-features.md | submodules, subtree, sparse-checkout, partial/shallow clone, bundles, notes, replace, bisect, backfill, commit/tag signing |
| troubleshooting.md | error→cause→fix lookup: detached HEAD, unrelated histories, non-fast-forward, CRLF, dubious ownership, large files, .gitignore not working, wrong author |
Common Workflows (compact)
Undo the last commit, keep the changes
git reset --soft HEAD~1
git reset HEAD~1
git revert HEAD
Clean up local commits before pushing
git rebase -i @{upstream}
git commit --fixup=<hash> && git rebase -i --autosquash @{upstream}
Full todo-command reference and --update-refs (keeps stacked branches aligned): history-rewriting.md. For dedicated stacked-branch tooling use the git-chain skill; for auto-fixup use git-absorb.
Find the commit that introduced a bug (bisect)
git bisect start
git bisect bad
git bisect good v1.2.0
git bisect run ./test.sh
git bisect reset
Details, skip, and custom terms: advanced-features.md.
Work on two branches at once (no stashing)
git worktree add ../hotfix main
git worktree remove ../hotfix
Gotchas (a branch checks out in only one worktree, shared object store): worktrees-stash.md.
Stash, switch context, come back
git stash push -u -m "wip: parser"
git switch other-branch
git switch -
git stash pop
Partial stashing (-p), keeping the index (-k), recovering a dropped stash: worktrees-stash.md.
Bring one fix to another branch
git switch release-1.x
git cherry-pick -x <hash>
git cherry-pick --continue
Ranges, picking a merge (-m), and rerere to auto-reuse resolutions: branching-merging.md.
Resolve a merge conflict
git merge feature
git status
git diff
git add <resolved> && git merge --continue
git merge --abort
-X ours/theirs, choosing a strategy, and reusing resolutions with rerere: branching-merging.md. Backing out a committed merge: recovery.md.
Search history for where/when something changed
git log -S "functionName" --oneline
git log -L :funcName:file.c
git log --oneline --follow -- path
git blame -w -C file.c
More in inspection.md.
Git Version Awareness
This skill is documented against git 2.54, but most of it works on far older git. Features that need a specific minimum version are tagged inline as (git X.Y+); default changes and removals are noted where they matter. When no tag is present, assume the feature is long-standing. For a consolidated lookup, see version-features.md.
High-impact cutoffs worth remembering:
| Feature | Minimum version |
|---|
git switch / git restore | git 2.23+ |
ort merge strategy as default | git 2.34 |
git rebase --update-refs | git 2.38+ |
git push --force-if-includes | git 2.30+ |
git config get/set/unset/list subcommands | git 2.46+ |
git backfill (blobless partial clones) | git 2.49+ |
Always confirm against the installed git: git version, then git <cmd> --help.
Conventions & Notes
- Verify exact flags against the installed git (
git <cmd> --help); flag behavior evolves across releases (see Git Version Awareness).
- Prefer non-destructive diagnosis first (
status, reflog, log --graph, --dry-run, --no- variants) before any command that rewrites or deletes.
- Quote/escape revisions and pathspecs in shells (
'HEAD^', -- to separate paths from revs).
- When in doubt about a destructive op, create a throwaway branch or tag at the current commit first:
git branch backup-$(date +%s).