plumbing & porcelain

Git commands, arranged by where your work is sitting.

  working tree  ──add──▶  index  ──commit──▶  repository  ──push──▶  remote
       ▲                    │                     │                    │
       └────restore─────────┘                     │                    │
       └───────────────reset────────────────────  ┘                    │
       └──────────────────────────fetch / pull ────────────────────────┘

Most git confusion is not about syntax. It is about not knowing which of these four places a command reads from and which one it writes to. Everything below is grouped by that, so you can find a command by describing where your work is now and where you want it to end up.

Looking around Read-only. Nothing here changes any of the four states.

git status --short --branch
The compact form. Left column is the index, right column is the working tree, so MM means you staged a change and then edited the file again.
git diff
Working tree against the index — what you have not staged yet.
git diff --staged
Index against the last commit — what you are about to commit.
git log --oneline --graph --decorate
History with the branch topology drawn in. Add --all to see branches you are not currently on.
git log -p -- path/to/file
Every commit that touched one file, with the diffs. The bare -- separates paths from revisions when a file and a branch share a name.
git show HEAD~3
One commit in full. ~3 counts back three first parents; ^2 picks the second parent of a merge.
git blame -L 40,60 file.py
Who last touched each line in a range. -w ignores whitespace-only changes, which usually cuts the noise in half.

Working tree to index Choosing what goes into the next commit.

git add -p
Stage hunk by hunk. Answer s to split a hunk further and e to hand-edit one. This is the single habit that most improves commit quality.
git add -u
Stage modifications and deletions of files git already tracks, but no new files.
git restore --staged file.txt
Take a file back out of the index and leave your edits alone.
git restore file.txt
Throw away working-tree edits and take the file from the index. This one loses work with no recovery.
git stash push -m "wip" -- src/
Park changes, optionally only under some paths. git stash pop brings them back and drops the entry; apply keeps it.
git clean -nd
List untracked files and directories that -fd would delete. Run the dry form first, always.

Index to repository Committing, and editing commits you have not shared.

git commit -v
Puts the staged diff in the editor below the message, so you write the message while looking at what you actually staged.
git commit --amend --no-edit
Fold the staged changes into the previous commit and keep its message. Rewrites that commit, so only for work you have not pushed.
git reset --soft HEAD~1
Undo the last commit and leave everything staged. Useful for splitting one commit into two.
git reset HEAD~1
The default, --mixed: undo the commit and unstage, but keep the files as they are.
git reset --hard HEAD~1
Undo the commit and wipe the changes. The one command here that genuinely destroys work.
git rebase -i HEAD~5
Reorder, squash, reword or drop the last five commits. Only for a branch nobody else has pulled.
git revert <sha>
Make a new commit that undoes an old one. This is the safe undo for anything already published — history stays intact.

Branches Moving HEAD around inside the local repository.

git switch -c topic
Create a branch and move to it. switch and restore split the old overloaded checkout into two clearer commands.
git switch -
Back to the branch you were on before. Same idea as cd -.
git branch --merged main
Branches whose work is already contained in main — the safe list to delete with git branch -d.
git merge --no-ff topic
Always record a merge commit, even when a fast-forward was possible, so the branch stays visible in the graph.
git rebase main
Replay your commits on top of the current main. Produces a linear history and new commit hashes.
git cherry-pick <sha>
Copy one commit onto the current branch. -x records where it came from in the message.

Repository and remotes The only commands here that touch the network.

git fetch --prune
Update your view of the remote and delete tracking refs for branches that no longer exist there. Changes nothing in your working tree.
git pull --rebase
Fetch, then replay your local commits on top instead of merging. Keeps the history free of merge commits that say nothing.
git push -u origin topic
Push and set the upstream, so later a bare git push works on this branch.
git push --force-with-lease
Force-push, but refuse if the remote moved since your last fetch. Use this instead of --force every time.
git remote -v
Fetch and push URLs for each remote. They can differ, which is how read-only mirrors are set up.

Getting out of trouble Where to look when a state has gone somewhere unexpected.

git reflog
Every position HEAD has held, including commits no branch points at any more. A bad reset or rebase is nearly always recoverable from here with git reset --hard HEAD@{2}.
git bisect start / bad / good <sha>
Binary search through history for the commit that introduced a problem. With git bisect run ./test.sh it finishes on its own.
git rebase --abort
Put everything back the way it was before the rebase started. The same flag exists for merge and cherry-pick.
git fsck --lost-found
Find dangling objects when even the reflog has been expired.

The reflog is local and expires — ninety days for reachable entries, thirty for unreachable ones by default. It will not save you from a bad git clean, because untracked files were never objects in the first place.

Three commands here can lose work that no other command can bring back: git restore without --staged, git reset --hard, and git clean -fd. Everything else writes a new object before it moves a pointer.