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.