Git Command Cheat Sheet
The Git commands that come up in real work, including the ones for undoing things safely and the difference between the dangerous options and the safe ones.
Most Git use is a dozen commands. The rest of it is undoing something, which is where the manual gets least helpful and the stakes are highest. This covers both, and is explicit about which commands rewrite history.
Looking at what you have#
git status # what is staged, changed, untracked
git status -sb # the same, in two compact lines
git diff # unstaged changes
git diff --staged # what a commit would contain right now
git log --oneline --graph --decorate --all
git log -p path/to/file # every change to one file, with diffs
git log -S "someString" # commits that added or removed that string
git blame path/to/file # who last touched each line, and when
git show abc1234 # one commit in fullBranching#
git switch main # change branch (clearer than checkout)
git switch -c feature/thing # create and change to it
git switch - # back to the previous branch
git branch -vv # local branches and their upstreams
git branch -d old-branch # delete, refusing if unmerged
git branch -D old-branch # delete regardless
git branch -m new-name # rename the current branchswitch and restore were added because checkout did too many unrelated jobs. Prefer them.
Staging and committing#
git add path/to/file
git add -p # stage selected hunks, reviewing as you go
git commit -m "message"
git commit --amend # fold changes into the last commit
git commit --amend --no-edit # ...without editing the messagegit add -p is the single habit most worth acquiring: it forces you to read your own diff before committing it.
Undoing, from safest to most destructive#
| Command | What it does | Safe? |
|---|---|---|
git restore file | Discards unstaged changes to that file | Loses uncommitted work only |
git restore --staged file | Unstages, keeping the changes | Yes |
git revert abc1234 | New commit undoing an old one | Yes — history is preserved |
git reset --soft HEAD~1 | Undoes the commit, keeps everything staged | Yes |
git reset HEAD~1 | Undoes the commit, keeps changes unstaged | Yes |
git reset --hard HEAD~1 | Undoes the commit and discards the changes | Destroys uncommitted work |
git clean -fd | Deletes untracked files and directories | No undo. Run -nd first to preview |
On a shared branch, use revert, never reset. Revert adds a commit; reset rewrites history that other people already have.
Stashing#
git stash push -m "half-done thing"
git stash list
git stash show -p stash@{0} # what is actually in it
git stash pop # apply and remove
git stash apply # apply and keep
git stash push -u # include untracked files
git stash drop stash@{0}-u matters more than it looks: without it a stash silently leaves new files behind, and switching branches carries them with you.
Remotes#
git fetch --all --prune # update refs, drop deleted remote branches
git pull --rebase # rebase local commits on top, no merge commit
git push -u origin feature/thing # push and set upstream
git push --force-with-lease # safe force pushUse --force-with-lease, not --force. Plain force overwrites the remote whatever state it is in, including a colleague's push you have not seen. --force-with-lease refuses if the remote moved since your last fetch.
Rebase and merge#
git rebase main # replay your commits on top of main
git rebase --continue # after resolving a conflict
git rebase --abort # give up, leaving nothing changed
git merge --no-ff feature/thing # merge keeping an explicit merge commit
git cherry-pick abc1234 # take one commit from elsewhereRebasing produces a linear history that is far easier to read. The rule is simply: never rebase anything you have already pushed to a branch someone else is using.
When something has gone badly wrong#
git reflog # every position HEAD has held
git reset --hard HEAD@{3} # go back to one of themreflog is the reason most Git accidents are recoverable. A commit dropped by a bad reset or rebase is still there, and reflog knows where. It keeps entries for 90 days by default — so a "lost" commit almost never is.
git bisect start
git bisect bad # current commit is broken
git bisect good v1.2.0 # this one was fine
# test, then mark each step good or bad
git bisect resetBisect finds the commit that introduced a bug by binary search. Over a thousand commits that is ten tests, not a thousand.
Configuration worth setting once#
git config --global pull.rebase true
git config --global push.default current
git config --global rerere.enabled true
git config --global init.defaultBranch mainrerere records how you resolved a conflict and replays that resolution when the same one reappears. During a long rebase it saves the same work being done repeatedly.