Version control cheat sheetGit logo

Git cheat sheet

Every Git command worth keeping close, grouped by what you are trying to do: commit, branch, merge, undo a mistake, and dig through history.

Last updated

Git tracks the history of a project as a series of snapshots, and almost every command you will run is either recording a new snapshot, moving between existing ones, or comparing two of them. The reference below is grouped by what you are trying to achieve rather than alphabetically, and the filter box searches all of it at once — type rebase and every rebase command on the page comes to you.

Anything in angle brackets is a placeholder you replace. A few commands need Git 2.23 or newer, and those are called out where they appear — check with git --version.

The three places your changes live

Most confusing Git behaviour makes sense once this is clear. A change moves left to right, and nearly every command below is really just moving something between these.

WhereAlso calledWhat it holdsHow things get there
Working treeYour files on diskEdits you have made and not recordedYou edit a file
Staging areaThe index, the cacheThe exact contents of your next commitgit add
RepositoryHistory, the object databaseEvery commit ever recordedgit commit

HEAD is a fourth thing worth naming: it is a pointer to the commit you currently have checked out. HEAD~1 is its parent, HEAD~2 the one before that. That is why the undo commands are written relative to HEAD.

The everyday loop

Nine commands out of ten, this is all you need.

TaskCommand
See what has changedgit status
See the actual edits, unstagedgit diff
See the edits you have stagedgit diff --staged
Stage one filegit add <file>
Stage everything, including deletionsgit add -A
Stage only parts of a file, interactivelygit add -p
Commit what is stagedgit commit -m "<message>"
Stage every tracked file and commit in one gogit commit -am "<message>"
Download new commits without touching your filesgit fetch
Download and integrate themgit pull
Send your commits to the remotegit push
Push a new branch and set it to trackgit push -u origin <branch>

git add -p is the one most people never learn and then use daily. It walks through each block of changes and asks whether to stage it, which is how you keep a tidy history without keeping a tidy working tree.

Start a repository

TaskCommand
Create a repository in the current foldergit init
Create one with a named default branchgit init -b main
Copy an existing repositorygit clone <url>
Clone into a specific foldergit clone <url> <folder>
Clone only the latest commitgit clone --depth 1 <url>
Clone one branch onlygit clone --single-branch --branch <branch> <url>

A shallow clone with --depth 1 is much faster on a large repository and is the usual choice in CI, where the history is not needed. It does limit what you can do afterwards: no git log beyond that commit, and no diffing against old versions until you run git fetch --unshallow.

Branching and switching

git switch and git restore arrived in Git 2.23 to split the two unrelated jobs git checkout used to do. Both forms still work, but the newer ones are harder to get wrong: git checkout followed by a name either changes branch or destroys your changes, depending on whether that name happens to be a branch.

TaskCommandOlder equivalent
List local branchesgit branch
List every branch, remotes includedgit branch -a
Create a branch and switch to itgit switch -c <branch>git checkout -b <branch>
Switch to an existing branchgit switch <branch>git checkout <branch>
Go back to the previous branchgit switch -git checkout -
Create a branch from a specific commitgit switch -c <branch> <commit>git checkout -b <branch> <commit>
Rename the branch you are ongit branch -m <new-name>
Delete a merged branchgit branch -d <branch>
Delete a branch regardlessgit branch -D <branch>
Delete a branch on the remotegit push origin --delete <branch>
See which branches are merged into this onegit branch --merged

The lowercase -d refuses to delete a branch holding commits that are not merged anywhere, which is a useful safety net. -D skips the check, so reach for it when you mean to abandon the work.

Merging and rebasing

TaskCommand
Merge another branch into yoursgit merge <branch>
Merge but always create a merge commitgit merge --no-ff <branch>
Combine a branch into one set of staged changesgit merge --squash <branch>
Abandon a merge that hit conflictsgit merge --abort
Replay your commits on top of another branchgit rebase <branch>
Edit, squash or reorder your last three commitsgit rebase -i HEAD~3
Continue after resolving a conflictgit rebase --continue
Abandon a rebasegit rebase --abort
Pull, rebasing instead of merginggit pull --rebase
Copy a single commit onto the current branchgit cherry-pick <commit>

The rule that keeps you out of trouble: rebase your own unpushed commits as much as you like, and merge anything anyone else might already have. There is a fuller comparison further down the page.

Undoing things

The block people come back for. The right command depends on two things: whether the change is committed yet, and whether you have pushed it.

I want toCommandLoses work?
Unstage a file, keep the editsgit restore --staged <file>No
Throw away uncommitted edits to a filegit restore <file>Yes, permanently
Throw away all uncommitted editsgit restore .Yes, permanently
Delete untracked files and foldersgit clean -fdYes, permanently
Fix the last commit messagegit commit --amendNo
Add a forgotten file to the last commitgit commit --amend --no-editNo
Undo the last commit, keep changes stagedgit reset --soft HEAD~1No
Undo the last commit, keep changes unstagedgit reset HEAD~1No
Undo the last commit and its changesgit reset --hard HEAD~1Yes
Undo a commit that is already pushedgit revert <commit>No
Undo a merge that is already pushedgit revert -m 1 <merge-commit>No
Stop tracking a file but keep it on diskgit rm --cached <file>No

Two things worth internalising. git restore and git clean are the only genuinely dangerous commands here, because uncommitted work exists nowhere else, whereas everything else can be walked back through the reflog. And revert is the answer for anything public: it adds a commit rather than rewriting one, so nobody else's clone breaks. Note also that git reset with no flag is --mixed, so git reset HEAD~1 and git reset --mixed HEAD~1 are the same command.

Inspecting history

TaskCommand
Compact log, one line per commitgit log --oneline
The whole graph, all branchesgit log --oneline --graph --decorate --all
Last five commitsgit log -5
Commits by one persongit log --author="<name>"
Commits touching one filegit log --follow -- <file>
Commits whose diff mentions some textgit log -S"<text>"
Show one commit in fullgit show <commit>
Who last changed each linegit blame <file>
Compare two branchesgit diff <branch-a>..<branch-b>
What is on your branch and not on maingit log main..HEAD --oneline
Search the working treegit grep "<text>"
Commit count per contributorgit shortlog -sn

git log -S is the pickaxe. It finds commits that changed the number of occurrences of a string, which is how you find where a function was deleted rather than only where it is mentioned.

Working with remotes

TaskCommand
List remotes and their URLsgit remote -v
Add a remotegit remote add <name> <url>
Change a remote's URLgit remote set-url origin <url>
Fetch and delete stale remote-tracking branchesgit fetch --prune
Fetch every remotegit fetch --all
Push a rewritten branch, safelygit push --force-with-lease
See what tracks whatgit branch -vv
Set the upstream for the current branchgit push -u origin <branch>

Prefer --force-with-lease to --force, always. Plain --force overwrites whatever is on the remote, while --force-with-lease first checks the remote is where you last saw it, so a colleague's push turns into an error instead of a deletion.

Stashing

For when you need a clean tree right now and are not ready to commit.

TaskCommand
Stash tracked changesgit stash push -m "<message>"
Include untracked filesgit stash push -u -m "<message>"
List what you have stashedgit stash list
Apply the most recent and remove itgit stash pop
Apply it and keep it in the listgit stash apply
Apply a specific onegit stash apply stash@{2}
See what is in a stashgit stash show -p stash@{0}
Delete onegit stash drop stash@{0}
Delete all of themgit stash clear

git stash push replaced git stash save, which still works but is deprecated. Note that a plain stash leaves untracked files behind, which is a common way to lose a new file. The -u flag includes them.

Tags and releases

TaskCommand
List tagsgit tag
Create an annotated taggit tag -a v1.0.0 -m "<message>"
Tag an older commitgit tag -a v1.0.0 <commit> -m "<message>"
Push one taggit push origin v1.0.0
Push every taggit push origin --tags
Delete a tag locallygit tag -d v1.0.0
Delete a tag on the remotegit push origin --delete v1.0.0

Use annotated tags, the -a flag, for anything you release. They carry an author, a date and a message, and unlike lightweight tags they are real objects in the repository, which is what release tooling expects.

When it has gone properly wrong

SituationWay out
Reset away a commit you neededgit reflog, then git reset --hard <hash>
Lost a branch you deletedgit reflog, then git switch -c <branch> <hash>
Need to find which commit broke somethinggit bisect start, then git bisect bad and git bisect good <commit>
Finished bisectinggit bisect reset
Detached HEAD and made commitsgit switch -c <branch>
Committed to the wrong branchgit cherry-pick <commit> onto the right one, then reset this one

git reflog is the single most useful recovery command in Git. It records every move HEAD has made, including the ones that left commits unreachable, and those commits stay on disk for 90 days by default. If you committed it, you can almost certainly get it back. The one thing this does not rescue is a committed secret: rotate the credential first and assume it is compromised, then rewrite the history.

Merge or rebase

Both bring another branch's work into yours, and the choice only really matters for commits you have already shared.

MergeRebase
History shapeBranches visibly joinOne straight line
Commit hashesUnchangedRewritten
Records what really happenedYesNo, it is tidied
Safe on shared branchesYesNo
Conflicts to resolveOncePotentially once per commit
Good forBringing a finished feature into mainCleaning up your own work before a pull request

Resolving a conflict

  1. git status lists the conflicted files.
  2. Open each one and edit it. Git marks the two versions with <<<<<<<, ======= and >>>>>>>; delete the markers and leave the content you want.
  3. git add <file> on each resolved file to mark it done.
  4. git commit to finish a merge, or git rebase --continue to finish a rebase.

If it gets away from you, git merge --abort or git rebase --abort puts everything back exactly as it was.

Config worth setting once

git config --global user.name "Your Name"
git config --global user.email "you@example.com"
git config --global init.defaultBranch main
git config --global pull.rebase false
git config --global push.autoSetupRemote true
git config --global core.editor "code --wait"
git config --global diff.colorMoved zebra

push.autoSetupRemote (Git 2.37+) means a plain git push works on a new branch without -u origin <branch>. pull.rebase has no default, and Git nags on every pull until you pick one — false merges, true rebases.

To see where a setting came from:

git config --list --show-origin

Common questions

What is the difference between git fetch and git pull?

git fetch downloads new commits from the remote and updates your remote-tracking branches, but leaves your working branch alone. git pull does a fetch and then immediately merges or rebases those commits into your current branch. Fetch is always safe; pull changes your files.

How do I undo the last commit in Git?

If it is not pushed yet, git reset --soft HEAD~1 removes the commit and leaves your changes staged, ready to recommit. Use --mixed, the default, to leave them unstaged, or --hard to throw the changes away entirely. If the commit is already pushed, use git revert instead: it records a new commit that undoes the old one, so nobody else's history breaks.

What is the difference between git merge and git rebase?

Both bring another branch's commits into yours. Merge creates one new commit joining the two histories, so the record of what happened is preserved exactly. Rebase replays your commits on top of the other branch, producing a straight line but new commit hashes. Merge for anything already shared; rebase to tidy your own local work before sharing it.

How do I discard local changes to a file?

git restore followed by the filename throws away uncommitted changes in the working tree, and it cannot be undone because that work was never recorded anywhere. To unstage a file but keep the edits, use git restore --staged instead. On Git older than 2.23 the equivalents are git checkout -- and git reset HEAD.

How do I recover a commit I deleted with git reset --hard?

Run git reflog, which lists every position HEAD has been in for the last 90 days, find the hash you want, then git reset --hard onto it. Commits are not deleted the moment they become unreachable, so a reset you regret is almost always recoverable. This does not help with uncommitted changes, which were never recorded in the first place.

Why should I use --force-with-lease instead of --force?

git push --force overwrites the remote branch regardless of what is on it, so it silently destroys commits a colleague pushed while you were rebasing. --force-with-lease refuses the push if the remote has moved since you last fetched, which turns a data-loss bug into an error message.

How do I change the message of a commit I already pushed?

git commit --amend rewrites the message, but that produces a different commit hash, so the pushed version and your local version have diverged. You then need git push --force-with-lease. That is fine on a branch only you work on and disruptive on a shared one, because everybody else has to reset to the new history.

What does the staging area actually do?

It lets you choose what goes into the next commit, rather than committing everything you have touched. That is what makes it possible to fix a typo and add a feature in the same working session and still record them as two clean, separate commits, using git add -p to stage them one block at a time.

More cheat sheets

Want this explained by a cat?

The videos cover the same ground in sixty seconds. If there is a tool you want a cheat sheet for next, ask.