Git Cheat Sheet — Commands by Task
Git commands organised by what you want to do: undo the last commit, rename a branch, delete a remote branch. Modern switch/restore with classic equivalents and danger badges.
Last verified: 2026-10-18 against the git-scm.com reference for Git 2.56 and the GitHub CLI manual. Sources: Git reference (git-scm.com/docs), git restore, git switch, git reset, git config, GitHub CLI manual.
Setup (once per machine)
Settings go to ~/.gitconfig with --global. Since Git 2.46 the documented form is git config set / get / list; the classic form still works.
git config set --global user.name "Ana Lee" git config set --global user.email "ana@example.com"
Classic: git config --global user.name "Ana Lee"
Recorded in every commit you make. Use the email your Git host knows so commits link to your account.
git config set --global init.defaultBranch main
Classic: git config --global init.defaultBranch main
Without it, git init still names the first branch master (and prints a hint).
git config set --global core.editor "code --wait"
Classic: git config --global core.editor "code --wait"
Any command that blocks until the file is closed works: "nano", "vim", "subl -n -w".
git config set --global core.autocrlf true # Windows git config set --global core.autocrlf input # macOS, Linux
Classic: git config --global core.autocrlf true
Commits LF, checks out CRLF on Windows. For a whole team, a .gitattributes file with "* text=auto" is more reliable than per-person settings.
git --version
git switch and git restore need 2.23+, git config subcommands 2.46+.
Start a repository
git init
git init -b main # name the first branchCreates a .git directory. Add a .gitignore before the first commit so build output and secrets never get in.
git clone https://github.com/owner/repo.git git clone git@github.com:owner/repo.git my-folder
Downloads the full history and sets the remote "origin". The second form uses SSH and a custom folder name.
git clone --depth 1 https://github.com/owner/repo.git
Skips history; good for CI and big repositories. git fetch --unshallow gets the rest later.
git remote add origin git@github.com:owner/repo.git git push -u origin main
Create the empty repository on the host first (or use gh repo create, below). -u remembers origin/main as the upstream.
Stage and commit
git status
git status -s # short formLists staged, unstaged and untracked files, and suggests the next command.
git add file.txt
git add . # everything in this folder and belowgit add -A stages deletions everywhere in the repository too.
git add -p file.txt
Walks through each change ("hunk"): y to stage, n to skip, s to split, e to edit.
git commit -m "Add login form"
git commit -am "Fix typo" # stage tracked files and commit-a picks up modified and deleted tracked files, never new ones.
git mv old.txt new.txt git rm file.txt
Both stage the change for you. Git detects renames by content, so a plain mv plus git add works too.
git rm --cached .env echo ".env" >> .gitignore
Removes it from the repository from the next commit on. It stays in earlier commits: if it held a secret, rotate the secret.
Branches
git branch git branch -a # include remote branches git branch -vv # show upstream and ahead/behind
The current branch is marked with *.
git switch -c feature/login
Classic: git checkout -b feature/login
Starts from the current commit; add a start point to branch from elsewhere: git switch -c hotfix origin/main.
git switch main
git switch - # back to the previous branchClassic: git checkout main
If only origin/feature exists, git switch feature creates a local tracking branch automatically.
git branch -m new-name # current branch
git branch -m old-name new-nameRenames locally only. To rename on the remote too, see the next entry.
git branch -m old-name new-name git push -u origin new-name git push origin --delete old-name
There is no remote rename: push the new name and delete the old one. Open pull requests on the old name may close; GitHub's rename button in the web UI retargets them.
git branch -d feature/login
git branch -D feature/login # even if unmerged-d refuses if the branch has commits not merged into its upstream or HEAD; -D deletes anyway.
Recovery: Unmerged commits are still in the reflog: git reflog, find the tip, then git branch feature/login <sha>.
git push origin --delete feature/login
Classic: git push origin :feature/login
Teammates still see it until they run git fetch --prune.
Recovery: Anyone with the branch locally (or you, via git reflog) can push it back.
git branch -u origin/main
git push -u origin HEAD # push current branch and track itClassic: git branch --set-upstream-to=origin/main
The upstream is what plain git pull and git push use.
git switch --detach a1b2c3d
git switch -c rescue # keep work made thereClassic: git checkout a1b2c3d
Commits made while detached belong to no branch; create one before switching away or they are only reachable via the reflog.
Merge, rebase and cherry-pick
git switch main
git merge feature/login
git merge --no-ff feature/login # always create a merge commitFast-forwards when possible; --no-ff keeps the branch visible in history.
git merge --squash feature/login git commit -m "Add login"
Stages the combined changes without recording a merge; the branch is not marked as merged afterwards.
git status # list conflicted files # edit the files, remove <<<<<<< ======= >>>>>>> markers git add file.txt git commit # or: git rebase --continue
Take one side wholesale with git restore --ours file.txt or --theirs. During a rebase "ours" is the branch being rebased onto, the reverse of a merge.
git merge --abort
Returns to the state before the merge started.
Recovery: Conflict resolutions you made are lost. If you had uncommitted changes when the merge started, --abort may not be able to restore them, so commit or stash before merging.
git switch feature/login git rebase main
Replays your commits on top of main for a linear history. Resolve conflicts, git add, then git rebase --continue; --abort gives up.
Recovery: Right after: git reset --hard ORIG_HEAD. Later: find the old tip in git reflog.
git rebase -i HEAD~3
Opens a list: change pick to squash/fixup to combine, reword to edit a message, drop to delete, or reorder lines.
Recovery: git reflog shows the commit before the rebase; git reset --hard <sha> restores it.
git commit --fixup=a1b2c3d git rebase -i --autosquash a1b2c3d~1
The fixup commit is folded into a1b2c3d automatically when you rebase.
Recovery: Same as interactive rebase: git reflog.
git pull --rebase
git config set --global pull.rebase true # make it the defaultRebases your local commits on top of what was fetched.
git cherry-pick a1b2c3d
git cherry-pick -x a1b2c3d # note the source in the messageCreates a new commit with the same changes. A range: git cherry-pick A..B (excludes A).
Remotes: fetch, pull, push
git remote -v git remote set-url origin git@github.com:owner/repo.git
set-url switches a clone from HTTPS to SSH (or to a renamed repository).
git fetch
git fetch --prune # also drop deleted remote branchesUpdates origin/* only; your branches are untouched.
git pull
git fetch plus merge (or rebase, if pull.rebase is set).
git push
git push -u origin HEAD # first push of a new branch
git config set --global push.autoSetupRemote trueWith push.autoSetupRemote (Git 2.37+), a plain git push on a new branch sets the upstream itself.
git push --force-with-lease
Refuses if the remote has commits you have not fetched, unlike --force. Add --force-if-includes (Git 2.30+) for extra safety with background fetches.
Recovery: Overwritten remote commits survive in the reflog of whoever had them, and GitHub keeps them reachable by SHA for a while.
git remote add upstream https://github.com/original/repo.git git fetch upstream git switch main git merge upstream/main
Or with GitHub CLI: gh repo sync.
Undo things
Pick by where the mistake is: in the working tree, in the staging area, in a local commit, or in a commit already pushed. Anything that was ever committed can usually be found again with git reflog; uncommitted edits that you discard cannot.
git reset --soft HEAD~1
The commit disappears; its changes stay staged, ready to recommit. Use git reset HEAD~1 (no --soft) to keep them unstaged instead.
Recovery: git reset ORIG_HEAD brings the commit back. If it was already pushed, use git revert instead.
git reset --hard HEAD~1
Moves the branch back one commit and resets every tracked file to match.
Recovery: The commit is in git reflog (unreachable entries expire after 30 days by default): git reset --hard HEAD@{1}. Uncommitted changes in tracked files are gone for good.
git revert a1b2c3d
git revert HEAD # the latest commitAdds a new commit that reverses the old one, so nobody else's history changes. This is the safe undo for shared branches.
git revert -m 1 <merge-sha>
-m 1 keeps the first parent (usually main). Re-merging the branch later needs a revert of this revert first.
git commit --amend -m "Better message"
Replaces the last commit with a new one. Fine before pushing; after pushing it needs --force-with-lease.
Recovery: git reset --soft HEAD@{1} returns to the commit before the amend.
git add forgotten.txt git commit --amend --no-edit
Keeps the message, adds the file.
Recovery: git reset --soft HEAD@{1}.
git restore --staged file.txt
git restore --staged . # everythingClassic: git reset HEAD file.txt
Changes stay in the working tree; only the staging is undone.
git restore file.txt
Classic: git checkout -- file.txt
Resets the file to the staged version (or the last commit if nothing is staged).
Recovery: Not recoverable from Git: the edits were never committed. Your editor's local history may still have them.
git restore --staged --worktree .
git clean -fd # also delete untracked filesClassic: git reset --hard
git clean -fd
Back to the last commit. Run git clean -n first to see what would be deleted.
Recovery: Not recoverable: uncommitted edits and untracked files are deleted. git stash -u is the reversible alternative.
git restore file.txt # deletion not committed git restore --source=a1b2c3d~1 file.txt # deleted in commit a1b2c3d
Classic: git checkout a1b2c3d~1 -- file.txt
Find the commit that deleted it with git log --diff-filter=D -- file.txt.
git restore --source=main -- config.yml
Classic: git checkout main -- config.yml
Overwrites the working copy only; add --staged --worktree to stage it too. To just look: git show main:config.yml.
Recovery: Uncommitted edits to that file are overwritten.
git reflog git branch rescued a1b2c3d
The reflog records every place HEAD has been. Find the SHA from before the mistake and put a branch on it.
git reset --hard ORIG_HEAD
ORIG_HEAD is set by merge, rebase and reset to where you were before. It is overwritten by the next such command.
Recovery: git reflog still lists the merge commit.
git reflog # find "rebase (start)"; the entry below it is the old tip
git reset --hard HEAD@{5}Straight after the rebase, git reset --hard ORIG_HEAD does the same.
Recovery: Uncommitted changes are discarded by --hard; stash them first.
git reset --hard HEAD@{1}HEAD@{1} is where HEAD was before the last move. Works for committed work only.
git filter-repo --invert-paths --path secrets.env
git filter-repo is a separate tool (pip install git-filter-repo), recommended by the git-filter-branch docs. Every clone must re-clone afterwards. Rotate the secret regardless: it was public from the moment it was pushed.
Recovery: filter-repo works on a fresh clone; keep the old clone until you are sure.
Stash
git stash
git stash push -m "half-done login"
git stash -u # include untracked filesSaves tracked changes and cleans the working tree so you can switch branches.
git stash pop # apply and drop git stash apply stash@{1} # apply, keep the stash
Conflicts are possible; with pop, the stash is kept if applying fails.
git stash list
git stash show -p stash@{0}Newest first; stash@{0} is the latest.
git stash push -- src/login.ts
git stash push --staged # only what is staged (2.35+)Path-limited stashes leave everything else in place.
git stash branch feature/login stash@{0}Creates the branch at the commit where you stashed, applies the stash and drops it; avoids conflicts.
git stash drop stash@{0}
git stash clearclear removes every stash.
Recovery: A dropped stash is an unreachable commit until garbage collection: git fsck --unreachable | grep commit, then git stash apply <sha>.
History and diffs
git log --oneline --graph --all
One line per commit with branch structure. Add --decorate on very old Git to see branch names.
git log --follow -p -- path/file.txt
--follow continues across renames; -p shows each change.
git log -S "functionName" --oneline git log -G "regex" --oneline
-S counts occurrences changing; -G matches any changed line against a regex.
git log --author="Ana" --since="2 weeks ago" --oneline
Also --until, --grep="fix" for messages, and main..feature for commits on feature but not main.
git show a1b2c3d git show a1b2c3d:path/file.txt
The second form prints the file as it was in that commit without touching your copy.
git diff # unstaged git diff --staged # staged, i.e. what will be committed
--stat summarises per file; --word-diff is better for prose.
git diff main...feature # changes on feature since it branched git diff main feature # the two tips directly
Three dots compare against the merge base, which is what a pull request shows.
git blame -L 10,20 path/file.txt
Add -w to ignore whitespace and -C to follow lines moved from other files.
git bisect start git bisect bad # current commit is broken git bisect good v1.4.0 # this one worked # test, then mark each step good or bad git bisect reset
Binary search: about log2(n) steps. git bisect run ./test.sh automates it.
git grep -n "TODO"
Faster than grep on large repositories and skips ignored files. Add a commit to search an old version.
Tags and releases
git tag -a v1.2.0 -m "Release 1.2.0"
git tag -a v1.1.1 a1b2c3d -m "Hotfix" # tag an older commitAnnotated tags (-a) store author, date and message; prefer them for releases. git tag v1.2.0 alone makes a lightweight tag.
git tag -d v1.2.0 git push origin --delete v1.2.0
Avoid re-using a deleted release tag name: other clones keep the old tag.
Recovery: The tagged commit is unaffected; re-create the tag on it.
Submodules and worktrees
git submodule add https://github.com/owner/lib.git vendor/lib git commit -m "Add lib submodule"
Records the URL in .gitmodules and pins an exact commit of the other repository.
git clone --recurse-submodules <url>
git submodule update --init --recursive # already clonedWithout this, submodule folders are empty.
git submodule update --remote git commit -am "Bump submodules"
Fetches each submodule's tracked branch; commit the new pins in the parent.
git worktree add ../repo-hotfix hotfix git worktree add -b review ../repo-review origin/main
A second working folder sharing the same repository; no stash or second clone needed.
git worktree list
git worktree remove ../repo-hotfix
git worktree prune # forget folders deleted by handremove refuses if the worktree has uncommitted changes (use --force to override).
Cleaning up
git clean -n -d
Always run this before git clean -f.
git clean -fd # untracked files and folders git clean -fdx # also ignored files (build output, .env!)
-x removes ignored files too, which includes local config such as .env.
Recovery: Not recoverable: these files were never in Git.
git branch --merged main git branch -d branch-a branch-b
Review the list first; -d refuses unmerged branches, so it is safe to try.
git fetch --prune git config set --global fetch.prune true
Drops origin/* refs whose branches were deleted on the server.
git maintenance start git gc
maintenance start schedules background prefetch, commit-graph and repack jobs; gc packs objects and prunes unreachable ones now.
Config and aliases
git config list --show-origin
Classic: git config --list --show-origin
Shows system, global and repository values with the file each one is in.
git config set --global alias.st "status -s" git config set --global alias.lg "log --oneline --graph --all"
Classic: git config --global alias.st "status -s"
Then git st and git lg. Prefix with ! to run a shell command: alias.root "!pwd".
git config set --global pull.rebase true git config set --global push.autoSetupRemote true git config set --global rerere.enabled true git config set --global merge.conflictStyle zdiff3 git config set --global diff.algorithm histogram
Classic: git config --global pull.rebase true
Rebase on pull; auto upstream on first push (2.37+); remember conflict resolutions; show the common ancestor in conflicts (2.35+); clearer diffs.
git config set --global core.excludesFile ~/.gitignore_global
Classic: git config --global core.excludesFile ~/.gitignore_global
For OS and editor clutter (.DS_Store, .idea/). Project-specific rules belong in the repository's .gitignore.
GitHub CLI (gh) basics
gh is GitHub's own command-line tool, installed separately from Git.
gh auth login
gh auth setup-git # let git use gh for HTTPS credentialsOpens a browser or takes a token; works for github.com and GitHub Enterprise.
gh repo create my-app --private --source=. --push
Creates the repository, adds it as origin and pushes the current branch.
gh repo clone owner/repo
For a fork, also adds the parent repository as the upstream remote.
gh pr create --fill gh pr create --draft --title "WIP: login" --body "…"
--fill uses the commit messages for the title and body.
gh pr checkout 123
Fetches the PR branch (forks included) and switches to it.
gh pr list gh pr view 123 --web gh pr merge 123 --squash --delete-branch
gh pr status shows PRs relevant to you.
gh issue create --title "Bug: …" --body "…" gh issue list --assignee @me
gh issue view 42 --comments reads a thread in the terminal.
gh run list gh run watch
gh run view --log-failed prints only the failing steps.
Need a starting .gitignore? Use the .gitignore generator. Comparing two versions of a file outside Git? The diff checker shows the changes side by side.
A static reference: search runs in your browser and nothing you type is sent anywhere. Browse all free tools.
Found a problem, or something that could be better? We read every message and we fix things quickly.
This git cheat sheet is organised by task, not by command, because nobody searches for “git reset --soft”; they search for “undo last commit”. Find what you are trying to do, copy the command, and read the one-line explanation of what it changes. Search ranks tasks first, so “delete remote branch” or “rename branch” goes straight to the right entry.
Commands that rewrite history or throw work away are marked, with a note on what can be recovered: almost anything that was ever committed can be found again with git reflog, while uncommitted edits you discard with restore, reset --hard or clean are gone. Where Git now has a clearer command (git switch and git restore since 2.23, git config set and list since 2.46), that is shown first with the classic form alongside, so the sheet works whether your team learned Git last year or in 2010. Checked against the git-scm.com reference for Git 2.56.
How to use
- Type what you want to do in the search box, such as “undo last commit”, “unstage” or “squash commits”, or click one of the common tasks.
- Read the badges before you run anything: amber means the command rewrites history, red means it can discard work. The recovery note says how to get back.
- Click Copy command. The copy leaves out the # comments so it pastes cleanly into a terminal; replace placeholders such as a1b2c3d or feature/login with your own.
- Share a specific answer with the # link next to each task, or print the whole sheet as a two-column PDF.
Frequently asked questions
How do I undo the last commit in git?
If you have not pushed it, git reset --soft HEAD~1 removes the commit and keeps its changes staged, and git reset HEAD~1 keeps them unstaged. git reset --hard HEAD~1 removes the commit and its changes. If the commit is already pushed to a shared branch, use git revert HEAD instead, which adds a new commit that undoes it without rewriting anyone's history.
What is the difference between git reset, git restore and git revert?
git restore changes files: it discards edits in the working tree or unstages them, and never moves a branch. git reset moves the current branch to another commit (and with --hard also overwrites your files), so it rewrites history. git revert creates a new commit that applies the opposite of an earlier one; history only grows, which makes it the safe choice on branches other people use.
How do I rename a git branch, locally and on the remote?
git branch -m new-name renames the branch you are on (git branch -m old new for another one). Remotes have no rename operation, so push the new name with git push -u origin new-name and delete the old one with git push origin --delete old-name. On GitHub, renaming the branch in the web interface also retargets open pull requests.
Should I use git switch or git checkout?
Either works. git checkout does two unrelated jobs, switching branches and restoring files, which is why Git 2.23 split them into git switch and git restore. The new commands are clearer and harder to misuse (git checkout -- file silently discards edits), so this sheet shows them first. git checkout is not deprecated and every command here lists its classic equivalent.
Can I recover a commit after git reset --hard?
Usually, yes. Run git reflog, find the line from before the reset, and run git reset --hard HEAD@{n} or create a branch on that SHA. Commits stay reachable through the reflog for 30 days by default once nothing else points to them. What cannot be recovered is uncommitted work: changes that were never committed or stashed are overwritten for good.
What does git push --force-with-lease do?
It overwrites the remote branch only if the remote still points where your last fetch saw it. If someone pushed in the meantime, the push is refused instead of silently deleting their commits, which is what plain --force does. Use it after a rebase or amend on a branch you have already pushed.
Which undo command? Decide by where the change is
• Edited a file and want the edit gone: git restore file. Not recoverable, because it was never committed.
• Staged something by mistake: git restore --staged file. Nothing is lost; the edit stays in the file.
• Committed but not pushed: git reset --soft HEAD~1 to redo the commit, or git commit --amend to fix it in place.
• Committed and pushed to a shared branch: git revert <sha>. Do not reset a branch other people have pulled.
• Lost a commit, a branch or the result of a bad rebase: git reflog, then git branch rescued <sha>.
The rule underneath: anything that rewrites commits is fine while the commits are only yours, and becomes everyone's problem once they are shared.
Merge or rebase?
Both bring the changes from one branch into another. A merge records that two histories joined, with a merge commit that has two parents; nothing is rewritten, so it is always safe on shared branches. A rebase replays your commits on top of the other branch, producing a straight line of history as if you had started from the latest commit, but it creates new commit IDs, so anyone who already has the old ones must reconcile.
A common team convention: rebase your own feature branch onto main to stay current (git pull --rebase, or git rebase main), and merge the finished branch into main through a pull request. Squash-merging gives one commit per feature on main; plain merges keep every commit and the branch shape. Pick one per repository and set it in the host's settings, so history stays consistent.
Git settings worth changing from the defaults
• pull.rebase true: git pull rebases instead of creating merge commits for every sync.
• push.autoSetupRemote true (Git 2.37+): the first git push of a new branch sets its upstream, no -u needed.
• merge.conflictStyle zdiff3 (Git 2.35+): conflict markers include the common ancestor, which makes most conflicts obvious.
• rerere.enabled true: Git remembers how you resolved a conflict and reapplies it when the same conflict comes back during a rebase.
• fetch.prune true: remote branches deleted on the server disappear locally on the next fetch.
• init.defaultBranch main: new repositories start on main.
Related free tools
- .gitignore Generator — Combine .gitignore templates for languages, frameworks, editors and operating systems into one clean file.
- Diff Checker — Compare two versions of a text, code or JSON file and see exactly which lines, words and characters changed.
- Markdown Cheat Sheet — Every Markdown element with source and rendered preview, extended syntax (tables, task lists, footnotes, math, Mermaid, alerts), a 9-platform compatibility table and a live playground.
- Regex Cheat Sheet — Every regular expression token with a working example, support in JavaScript, PCRE/PHP, Python, Go, Java and .NET, and a Try it link that opens the regex tester pre-filled.