Atuin cheatsheet: Ctrl-R search keys, filter and search modes, non-interactive flags, sync and …
Git Cheatsheet Git Cheatsheet

Summary
Nearly every git question I get asked is really the same question: how do I undo this? So that is what this sheet is built around — one table for undoing things, then the few commands worth knowing properly. The rest of git you can look up; these are the ones where guessing costs you work. Everything shown running below was run.
revert. That single line decides between almost every pair of
commands here.The Undo Table
Docs: Reset, restore and revert
Expand your knowledge with Go (Golang) Cheatsheet
| I want to | Command |
|---|---|
| Unstage a file, keep the edit | git restore --staged file |
| Throw away edits to a file | git restore file |
| Throw away all uncommitted edits | git restore . then git clean -fd |
| Fix the last commit message | git commit --amend |
| Add a forgotten file to the last commit | git add f && git commit --amend --no-edit |
| Undo the last commit, keep changes staged | git reset --soft HEAD~1 |
| Undo the last commit, keep changes unstaged | git reset HEAD~1 |
| Undo the last commit and the changes | git reset --hard HEAD~1 |
| Undo a commit that is already pushed | git revert <sha> |
| Recover something you just destroyed | git reflog then git reset --hard <sha> |
| Move the last commit to another branch | git reset --hard HEAD~1 after cherry-picking it across |
| Abandon a merge in progress | git merge --abort |
| Abandon a rebase in progress | git rebase --abort |
--hard and git clean are the two commands on this sheet that destroy work
the reflog cannot return, because uncommitted changes were never in a commit
to begin with. git stash first if you are not certain.reset: What Each Mode Touches
Three things move, and the mode decides how many: the branch pointer, the index (staging area) and the working tree.
Deepen your understanding in tmux Cheatsheet
| Mode | Branch | Index | Working tree |
|---|---|---|---|
--soft | moved | untouched | untouched |
--mixed (default) | moved | reset | untouched |
--hard | moved | reset | overwritten |
$ git log --oneline | head -37b8d969 commit 3b126a69 commit 27c1ee69 commit 1$ git status --short M app.txtA new.txt$ git reset --soft HEAD~1$ git status --shortMM app.txtA new.txt# commit 3's changes are now staged alongside what was already there,# and new.txt is still staged. Nothing was lost.$ git reset --mixed HEAD~1$ git status --short M app.txt?? new.txt# the index is cleared: app.txt is modified-but-unstaged and new.txt is# back to being untracked. Both files still have all their content.reflog: The Undo Button Behind the Undo Button
Docs: git reflog
A commit is never deleted by reset, only left unreferenced. The reflog
records every position HEAD has held, so it is still reachable:
$ git log --oneline | head -3ca5e030 commit 5bc458bb commit 47c1ee69 commit 1$ git reset --hard HEAD~2$ git log --oneline | head -27c1ee69 commit 1# two commits gone from the branch$ git reflog7c1ee69 HEAD@{0}: reset: moving to HEAD~2ca5e030 HEAD@{1}: commit: commit 5bc458bb HEAD@{2}: commit: commit 47c1ee69 HEAD@{3}: reset: moving to HEAD$ git reset --hard ca5e030$ git log --oneline | head -3ca5e030 commit 5bc458bb commit 47c1ee69 commit 1Entries expire after 90 days (30 for unreachable commits), and the reflog is local — it is not pushed, and a fresh clone has none.
Explore this further in Kubernetes Cheatsheet
git reflog # HEAD's history
git reflog show main # one branch's history
git reset --hard HEAD@{2} # or address it by position
git fsck --lost-found # when even the reflog has no entry
revert: Undo Without Rewriting
git revert <sha> # a new commit that undoes that one
git revert --no-commit <sha> # stage the undo, commit it yourself
git revert -m 1 <merge-sha> # undo a merge, keeping parent 1's line
git revert <old>..<new> # a range, newest first
@@ history @@ a1b2c3d Add rate limiting d4e5f6a Break pagination <- the bad commit, still there+9f8e7d6 Revert "Break pagination"Use revert on anything already pushed. Use reset only on commits that live
nowhere but your machine.
Discover related concepts in Go Security Cheatsheet
rebase vs merge
Docs: git rebase
Uncover more details in Low-Cost Cloud Stack Cheatsheet
git merge main # a merge commit; history shows the branching
git rebase main # replays your commits on top; linear history
git rebase --continue # after fixing a conflict
git rebase --abort # back to where you started
git pull --rebase # the one to put in your config
git config --global pull.rebase true # no more accidental merge commits
git config --global rebase.autoStash true # stash and restore around a rebase
pull
either conflicts or quietly duplicates the commits. If you must force-push a
shared branch, --force-with-lease refuses when the remote moved since you
last fetched; plain --force does not check.Interactive rebase
git rebase -i HEAD~5 # edit the last five
git rebase -i --root # all of them
pick a1b2c3d Add parser
squash d4e5f6a Fix typo in parser # fold into the one above
reword 9f8e7d6 Add tests # change the message
edit 1a2b3c4 Add validation # stop here to amend
drop 5e6f7a8 Debug printf # remove entirely
bisect: Find the Commit That Broke It
Docs: git bisect
Binary search over history. With run it is fully automatic — your script’s
exit status answers the question, zero for good and non-zero for bad:
$ cat test.sh#!/bin/sh[ "$(./calc.sh)" = "4" ]$ git bisect start HEAD <first-commit>$ git bisect run ./test.shrunning './test.sh'Bisecting: 2 revisions left to test after this (roughly 2 steps)[<sha>] change 7running './test.sh'Bisecting: 0 revisions left to test after this (roughly 1 step)[<sha>] refactor arithmeticrunning './test.sh'<sha> is the first bad commitcommit <sha> refactor arithmetic calc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)bisect found first bad commit$ git bisect resetTwelve commits took four tests; a thousand would take about ten. The manual form is the same search with you answering:
Journey deeper into this topic with Python Logging Cheatsheet
git bisect start
git bisect bad # current commit is broken
git bisect good v1.4.0 # this tag was fine
# git checks out the midpoint; test it, then:
git bisect good # or: git bisect bad # or: git bisect skip
git bisect reset # always, when you are done
Branches
git switch -c feature/x # create and switch (clearer than checkout)
git switch - # back to the previous branch
git branch -vv # local branches + their upstreams
git branch --merged main # safe to delete
git branch -d old # delete if merged; -D to force
git push origin --delete old # delete it on the remote too
git fetch --prune # drop local refs to branches deleted upstream
Stash
git stash push -m "wip: parser" # -m, so it is findable later
git stash push -u # include untracked files
git stash push path/to/file # only part of the tree
git stash list
git stash show -p stash@{1} # what is actually in it
git stash pop # apply and drop
git stash apply stash@{1} # apply and keep
git stash branch fix stash@{0} # new branch from where it was stashed
git stash drop by mistake is also recoverable:
git fsck --unreachable | grep commit lists the orphans, and
git stash apply <sha> takes one back.cherry-pick
git cherry-pick <sha> # that commit, onto the current branch
git cherry-pick <a>^..<b> # an inclusive range
git cherry-pick -x <sha> # record the source sha in the message
git cherry-pick -n <sha> # stage it without committing
git cherry-pick --abort
-x is the one to use when backporting to a release branch — six months on,
the message says where the commit came from.
Master this concept through Go Concurrency Cheatsheet
Inspecting
git log --oneline --graph --decorate --all # the shape of everything
git log -p path/to/file # every change to one file
git log -S "functionName" # commits that added or removed a string
git log --since="2 weeks" --author="Karan"
git show <sha> # one commit in full
git blame -L 40,60 file # who last touched these lines
git diff main...feature # what the branch adds (merge-base)
git diff --staged # what is about to be committed
[alias]
s = status --short --branch
lg = log --oneline --graph --decorate -20
last = log -1 --stat
unstage = restore --staged
amend = commit --amend --no-edit
# branches by how recently you touched them
recent = branch --sort=-committerdate --format='%(committerdate:relative)%09%(refname:short)'
# what am I about to push?
out = log @{u}.. --oneline
[core]
excludesfile = ~/.gitignore_global
[push]
default = current # push the current branch, no argument needed
autoSetupRemote = true # and set upstream on first push
[diff]
algorithm = histogram # better diffs on reordered code
[rerere]
enabled = true # remember conflict resolutions, replay them
rerere earns its keep on a long-lived branch: resolve a conflict once and
git replays the same resolution every rebase after.
Worktrees
One repository, several checked-out branches, no stashing to switch:
git worktree add ../hotfix hotfix/urgent # existing branch
git worktree add -b fix/x ../fix main # new branch from main
git worktree list
git worktree remove ../hotfix
git worktree prune # clean up deleted ones
Far better than cloning again: the worktrees share one object store, so a second checkout of a large repo costs almost nothing.
Deepen your understanding in tmux Cheatsheet
Fixing a Commit You Already Made
git commit --amend # message and/or staged changes
git commit --amend --no-edit # just fold in what is staged
git commit --fixup <sha> # mark it as fixing an older commit
git rebase -i --autosquash HEAD~5 # and fold all fixups in automatically
--amend makes a new commit with a new SHA — it is a rewrite like any other.
Amending something you have pushed needs a force-push, with the same caveat as
rebase.Common Pitfalls
git checkoutdoing two jobs. It switches branches and discards file changes.git switchandgit restoresplit those apart; use them.git pullmaking merge commits. Setpull.rebase trueand the noise goes away.- Force-pushing with
--force. Use--force-with-lease, which refuses if the remote moved since your last fetch. - Committing a secret. Removing it in a later commit does not remove it
from history — it must be rewritten (
git filter-repo) and the credential rotated, because it was pushed. git clean -fdto “tidy up”. It deletes untracked files with no reflog and no recovery.git clean -ndfirst, which only lists them.- A detached HEAD you commit onto. The commits are real but no branch
points at them;
git switch -c keepbefore leaving, or find them in the reflog afterwards.
When did you last check that you can recover a commit you deleted? `git reflog` on your own repo right now — can you find the SHA of something you reset away this month?
Deepen your understanding in tmux Cheatsheet
Related Cheatsheets
- Jenkins Cheatsheet — where these branches get built
- Bash Basics Cheatsheet — scripting around git, including for
bisect run - Bash CLI Cheatsheet — the find, grep and jq that pair with
git log -S
Similar Articles
Related Content
More from devops
A cost cheatsheet for running a product cheaply: free-tier hosting, serverless databases, auth, and …
Quick-reference tmux cheatsheet: sessions, windows, panes, copy mode, synchronize-panes, .tmux.conf …
You Might Also Like
No related topic suggestions found.
Knowledge Quiz
Test your general knowledge with this quick quiz!
A set of multiple-choice questions to test your knowledge.
Take as much time as you need.
Your score will be shown at the end.
Question 1 of 5
Quiz Complete!
Your score: 0 out of 5
Loading next question...
Contents

