Git
Cheat-sheet for daily work and incident recovery — from staging through rebase, reflog, worktrees, and bisect.
Setup
One-time config, aliases, and credential helpers.
User & Editor
Global identity
git config --global user.name "Jane Dev" Global email
git config --global user.email jane@example.com Default editor
git config --global core.editor "code --wait" Default branch on init
git config --global init.defaultBranch main Sensible pull behaviour
git config --global pull.rebase true Auto-stash during rebase
git config --global rebase.autostash true Aliases worth having
Short status
git config --global alias.s "status -sb" Pretty graph log
git config --global alias.lg "log --graph --oneline --decorate --all" Amend with previous message
git config --global alias.amend "commit --amend --no-edit" Unstage a file
git config --global alias.unstage "reset HEAD --" Credentials & SSH
Credential helper (macOS)
git config --global credential.helper osxkeychain Credential helper (Windows)
git config --global credential.helper manager Credential cache (Linux)
git config --global credential.helper "cache --timeout=7200" Test SSH to GitHub
ssh -T git@github.com Sign commits (GPG)
git config --global commit.gpgsign true Inspect config
Show all with sources
git config --list --show-origin Per-repo override
git config --local user.email jane@work.com Unset a key
git config --global --unset alias.s Basics
Starting a repo, staging, committing, inspecting.
Start a repo
Init (modern default branch)
git init --initial-branch=main Clone
git clone https://github.com/owner/repo.git Shallow clone (CI-fast)
git clone --depth 1 <url> Single branch only
git clone --single-branch --branch main <url> Blobless partial clone
git clone --filter=blob:none <url> Stage & Commit
Short status with branch
git status -sb Stage a specific file
git add path/to/file Interactive hunk staging
git add -p Stage all tracked + new
git add -A Commit with subject
git commit -m "feat: add login" Amend keeping message
git commit --amend --no-edit Empty commit (trigger CI)
git commit --allow-empty -m "chore: rebuild" Inspect changes
Unstaged diff
git diff Staged diff
git diff --cached Branch vs main (since divergence)
git diff main...HEAD Just changed file names
git diff --name-only main...HEAD Word-level diff
git diff --word-diff Branches
Create, switch, track, and delete branches.
List & Create
List local
git branch List all (incl. remotes)
git branch -a Last commit per branch
git branch -v Create + switch
git switch -c feature/login Branch from a tag / SHA
git switch -c hotfix v1.2.3 Rename current
git branch -m new-name Switch & Track
Switch branch
git switch main Jump back to previous
git switch - Detach HEAD at a SHA
git switch --detach <sha> Set upstream on first push
git push -u origin HEAD Track an existing remote branch
git switch --track origin/feature-x Delete
Delete (only if merged)
git branch -d feature/login Force delete local
git branch -D feature/login Delete remote branch
git push origin --delete feature/login Prune deleted remote refs
git fetch --prune Remotes
Fetch, pull, and push safely.
Remotes
List with URLs
git remote -v Add an upstream
git remote add upstream https://github.com/upstream/repo.git Rename
git remote rename old new Change URL
git remote set-url origin git@github.com:owner/repo.git Remove
git remote remove upstream Sync
Fetch one remote
git fetch origin Fetch all + prune
git fetch --all --prune Pull with rebase
git pull --rebase Push + set upstream
git push -u origin HEAD Push all branches
git push --all Push tags
git push --tags Force push (safely)
Always prefer --force-with-lease over --force — it aborts if the remote moved since your last fetch.
Safer force push
git push --force-with-lease With explicit expected SHA
git push --force-with-lease=main:<expected-sha> Merge & Rebase
Integrate branches; rewrite local history cleanly.
Merge
Merge a branch
git merge feature/login Preserve topology (no fast-forward)
git merge --no-ff feature/login Squash-merge into one commit
git merge --squash feature/login Abort a conflicted merge
git merge --abort Rebase
Rebase onto main
git rebase main Interactive (last 5 commits)
git rebase -i HEAD~5 Auto-squash fixup! commits
git rebase -i --autosquash main Auto-stash dirty tree first
git rebase --autostash main Continue / abort / skip
git rebase --continue|--abort|--skip Cherry-pick
Pick one commit
git cherry-pick <sha> Pick a range
git cherry-pick A^..B Leave staged, don't commit
git cherry-pick -n <sha> Resolve + continue
git cherry-pick --continue History
Read the past — logs, blame, and reflog.
Log
One-line with refs
git log --oneline --decorate All-branches graph
git log --oneline --graph --all By author
git log --author="Jane" Since date
git log --since="2 weeks ago" Patches touching a file
git log -p -- path/to/file Pickaxe: commits that added/removed a string
git log -S "functionName" -p Blame & Show
Blame a file
git blame path/to/file Blame with move/copy detection
git blame -C -M path/to/file Show a commit
git show <sha> Show file at a revision
git show HEAD~3:path/to/file Diff & Reflog
Diff two branches (tip vs tip)
git diff main..feature Diff since branch point
git diff main...feature HEAD history (lifeline for lost commits)
git reflog Reflog for a specific branch
git reflog show feature/login Undo
Walk back changes, the index, or history — safely.
Working tree & index
git restore is the modern replacement for checkout -- / reset HEAD.
Discard unstaged edits to a file
git restore path/to/file Unstage a file (keep edits)
git restore --staged path/to/file Discard ALL unstaged edits
git restore . Dry-run clean untracked
git clean -nd Remove untracked files + dirs
git clean -fd Rewind HEAD
Rewinds the current branch. Only use on local/unpushed history.
Move HEAD back, keep changes staged
git reset --soft HEAD~1 Move HEAD back, unstage, keep edits
git reset --mixed HEAD~1 Move HEAD back, DISCARD changes
git reset --hard HEAD~1 Snap branch to remote
git reset --hard origin/main Public-safe undo
revert creates a new inverse commit — safe to push on shared branches.
Revert a commit
git revert <sha> Revert a merge commit (first-parent)
git revert -m 1 <merge-sha> Stage the revert, don't commit yet
git revert --no-commit <sha> Stash
Park work-in-progress safely.
Stash
Stash tracked changes with message
git stash push -m "wip: login form" Include untracked files
git stash push -u -m "wip" Stash only some paths
git stash push -m "wip css" -- src/styles List stashes
git stash list Apply most recent (keep entry)
git stash apply Apply + drop most recent
git stash pop Apply a specific one
git stash apply stash@{2} Show contents as a patch
git stash show -p stash@{0} Drop / clear all
git stash drop stash@{0} # git stash clear Advanced
Tags, worktrees, submodules, bisect, maintenance.
Tags
Lightweight tag
git tag v1.0.0 Annotated tag (recommended)
git tag -a v1.0.0 -m "Release 1.0.0" Push a single tag
git push origin v1.0.0 Push all tags
git push --tags Delete local tag
git tag -d v1.0.0 Delete remote tag
git push origin --delete v1.0.0 Worktrees
Multiple checkouts from one repo — review a PR without stashing, or keep hotfix + feature work open simultaneously.
Add worktree on a branch
git worktree add ../repo-hotfix hotfix/1.2.1 Add detached at a SHA
git worktree add --detach ../repo-inspect <sha> List
git worktree list Remove
git worktree remove ../repo-hotfix Prune stale entries
git worktree prune Submodules
Add a submodule
git submodule add https://github.com/owner/lib lib/ Init + fetch after a clone
git submodule update --init --recursive Clone including submodules
git clone --recurse-submodules <url> Update to remote tip
git submodule update --remote --merge Deinit a submodule
git submodule deinit -f lib/ Bisect
Binary-search for the commit that broke something.
Start
git bisect start Mark current as bad
git bisect bad Mark a known-good tag
git bisect good v1.0.0 Run script to auto-bisect
git bisect run ./scripts/test.sh Finish
git bisect reset Maintenance & Recovery
Recover a "lost" commit via reflog
git reflog # then: git branch recovery <sha> Garbage collect
git gc --aggressive Background maintenance
git maintenance start Verify repo integrity
git fsck --full CI/CD Essentials
Provider-agnostic concepts. The YAML syntax changes every release — the patterns don't.
Matrix builds
Run the same pipeline across multiple inputs in parallel.
Define an axis (e.g., Node versions, OS, database versions) — the runner expands the pipeline into one job per combination. Keeps configuration tiny and compatibility coverage high. Flip the matrix to a single entry when you need a quick smoke run.
Cache keys are hashes, not branches
Key caches on the lockfile hash, not on the branch name.
A cache keyed on `main` pollutes across unrelated PRs. A cache keyed on `hash(package-lock.json)` stays valid exactly as long as the dependency tree is stable, and invalidates automatically when dependencies change. Falls back through a hierarchy of progressively more permissive keys.
Short-lived cloud credentials beat long-lived secrets
Federate your CI to the cloud (OIDC-style) instead of storing static keys.
Cloud IAM providers let a CI job assume a role by exchanging a signed identity token for short-lived credentials (minutes). No long-lived access key lives in the CI vault, so there is nothing to rotate and nothing to leak. The concept is provider-agnostic — the names differ (OIDC, workload identity, workflow trust) but the shape is the same.
Branch protection + required checks
Merges to main require specific CI jobs to pass.
The gate is not "CI ran" but "these named checks succeeded". A check marked required but never reporting blocks merges silently — keep the set small and intentional. Pair with a "CODEOWNERS-style" review requirement to catch logic the pipeline cannot.
Artifact promotion: build once, deploy many
Build a single immutable artifact; promote the same one through environments.
Anti-pattern: rebuild for staging, rebuild for prod. You ship untested bytes. Correct: build once, tag by git SHA, push to an artifact registry, deploy the identical bytes to dev → staging → prod. Configuration differs per environment — artifacts do not.
Fail-fast vs continue-on-error
Two strategies for how the pipeline handles a failing step.
Fail-fast: one step dies, the rest stop. Best for unit tests where a type error in one file makes the rest meaningless. Continue-on-error: run everything, collect all failures. Best for linters and matrix jobs — you want the full set of failures in one run, not one at a time over six retries.
Tips & Pitfalls
Things that save careers during an incident.
--force-with-lease > --force
--force-with-lease aborts the push if someone else pushed since your last fetch. Plain --force silently overwrites teammates' commits.
Never rebase shared history
Rebasing rewrites SHAs. If the commits are already on a shared branch, everyone else has to reset — and may lose work. Rebase only local/unpushed commits.
reflog is your lifeline
Even "deleted" commits survive for ~90 days in the reflog. git reflog → find the SHA → git branch recovery . Resets and force-pushes are rarely truly fatal.
Prefer git restore / git switch
The modern split: restore for files (--staged to unstage), switch for branches. Reserve checkout for older muscle memory — the new commands are clearer.
pull.rebase = true
Default pull merges, producing ugly "Merge branch main" commits. git config --global pull.rebase true keeps history linear without thinking.
fixup! + --autosquash
Commit WIP as git commit --fixup , then git rebase -i --autosquash main folds them into the right commit automatically. Cleaner than squash.
Large binaries → Git LFS
Committing big binaries bloats history forever — every clone pays. Use git lfs track "*.psd" (or model/video extensions) and commit .gitattributes.
Sign your commits
git config --global commit.gpgsign true (GPG) or gpg.format=ssh for SSH signing. Most orgs' branch protection rules require it.
.git/info/exclude for personal ignores
IDE/OS files you don't want to PR into .gitignore go in .git/info/exclude — per-repo, uncommitted, personal.
Detached HEAD isn't broken
HEAD at a SHA without a branch is fine for inspection. To save work, git switch -c new-branch before committing anything.
Revert merges with -m
git revert fails unless you pick a parent: git revert -m 1 keeps the first-parent line and undoes the side branch.
Worktrees beat stashing
Need to hotfix while mid-feature? git worktree add ../repo-hotfix hotfix/x gives you a second working directory — no stash juggling, no branch switching.