/user/kayd @ devops :~$ cat git.md

Git Cheatsheet Git Cheatsheet

QR Code linking to: Git Cheatsheet
Karandeep Singh
Karandeep Singh
• 9 minutes

Summary

The git commands I actually reach for, built around one table of how to undo things, plus reflog recovery, scripted bisect, and the rules for rewriting history that is already shared.

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.

tip
The rule that prevents most git disasters: rewrite history only on commits nobody else has pulled. Your own unpushed branch, rebase freely. A shared branch, use 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 toCommand
Unstage a file, keep the editgit restore --staged file
Throw away edits to a filegit restore file
Throw away all uncommitted editsgit restore . then git clean -fd
Fix the last commit messagegit commit --amend
Add a forgotten file to the last commitgit add f && git commit --amend --no-edit
Undo the last commit, keep changes stagedgit reset --soft HEAD~1
Undo the last commit, keep changes unstagedgit reset HEAD~1
Undo the last commit and the changesgit reset --hard HEAD~1
Undo a commit that is already pushedgit revert <sha>
Recover something you just destroyedgit reflog then git reset --hard <sha>
Move the last commit to another branchgit reset --hard HEAD~1 after cherry-picking it across
Abandon a merge in progressgit merge --abort
Abandon a rebase in progressgit rebase --abort
caution
--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

ModeBranchIndexWorking tree
--softmoveduntoucheduntouched
--mixed (default)movedresetuntouched
--hardmovedresetoverwritten
the same reset, one mode at a time
$ 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:

destroying two commits and getting them back
$ 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 1

Entries 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
git diff — what reverting looks like in the lognothing is rewritten
@@ 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

Get new articles by email

One DevOps article a week, plus the 18-cheatsheet PDF pack. No spam, one click to leave.

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
warning
Rebase gives every replayed commit a new SHA. Doing it to a branch someone else has pulled means their history and yours disagree, and the next 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:

finding a regression in twelve commits, untouched by hand
$ 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 reset

Twelve 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
note
A stash is a commit, so 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

0

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
warning
--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 checkout doing two jobs. It switches branches and discards file changes. git switch and git restore split those apart; use them.
  • git pull making merge commits. Set pull.rebase true and 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 -fd to “tidy up”. It deletes untracked files with no reflog and no recovery. git clean -nd first, which only lists them.
  • A detached HEAD you commit onto. The commits are real but no branch points at them; git switch -c keep before leaving, or find them in the reflog afterwards.
Question

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

2

Similar Articles

More from devops

Atuin Cheatsheet

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

tmux Cheatsheet

Quick-reference tmux cheatsheet: sessions, windows, panes, copy mode, synchronize-panes, .tmux.conf …

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.

Get new articles by email

One DevOps article a week, plus the 18-cheatsheet PDF pack. No spam, one click to leave.

🍪 We use cookies for analytics. Read the privacy policy