Git undo cheat sheet: restore, reset, revert and reflog
How to undo almost anything in Git: discard edits, unstage, amend, reset or revert commits, recover deleted branches with the reflog and force-push safely.
Updated 7 min read

In this article
Almost every Git mistake can be undone, as long as you pick the right command and know whether the work was ever committed or pushed. The trouble is that Git offers five ways to "go back", and the wrong one can throw away uncommitted work for good.
This Git undo cheat sheet groups commands by situation. It explains git restore vs reset vs revert, how to handle git undo local changes safely, fixing the last commit, recovering lost commits with reflog, parking work with stash and force-pushing with --force-with-lease on shared branches.
This cheat sheet groups the undo commands by situation, from discarding an edit to rescuing a deleted branch, and marks which ones are safe on shared branches. Every command was run in a practice repository with Git 2.54 on 9 October 2026 (Git 2.56.0 is the newest release listed on git-scm.com), and the behaviour notes come from the official Git documentation and the Pro Git book.

The short answer: git restore discards or unstages changes to files; git reset moves your branch back and rewrites history, so use it only before pushing; and git revert adds a new commit that undoes an old one, which is safe on shared branches.
Pick the right command in ten seconds
| I want to... | Command | Safe after pushing? |
|---|---|---|
| Throw away edits to a file | git restore <file> | Yes (local only), but the edits are gone |
| Unstage a file but keep the edits | git restore --staged <file> | Yes |
| Fix the last commit's message or add a forgotten file | git commit --amend | No: rewrites history |
| Undo the last commit, keep the changes staged | git reset --soft HEAD~1 | No |
| Undo the last commit, keep the changes unstaged | git reset HEAD~1 | No |
| Undo a pushed commit | git revert <commit> | Yes: adds a new commit |
| Get a file back as it was in an old commit | git restore --source=<commit> <file> | Yes |
| Park unfinished work | git stash push -u -m "note" | Yes (local only) |
| Recover a lost commit or deleted branch | git reflog, then git branch <name> <sha> | Yes (local only) |
| Overwrite a remote branch after rewriting it | git push --force-with-lease | Only on your own branch |
The rule behind the third column comes straight from Pro Git: only amend commits that are still local, because amending previously pushed commits and force-pushing "will cause problems for your collaborators". The same applies to reset on any commit others may have pulled.
Undo changes you have not committed
Discard edits in the working tree
git restore app.txt # one file
git restore . # everything under the current directoryPro Git is blunt about this one: git restore <file> is "a dangerous command", because any local changes to the file are gone. Committed work can nearly always be recovered; work that was never committed usually cannot. If in doubt, stash instead.
Unstage without losing anything
git restore --staged app.txt # was: git reset HEAD app.txtgit restore and git switch arrived in Git 2.23 to split the many jobs of git checkout into clearer commands. git status prints these hints for you, so read its output before reaching for a command from memory.
Remove untracked files carefully
git clean -n # dry run: lists what would be deleted
git clean -f # actually delete untracked files
git clean -fd # ...and untracked directoriesAlways run -n first. In our practice repository it printed Would remove tmp.log and touched nothing.
Fix the last commit
git commit --amend -m "Better message" # change the message
git add forgotten.txt
git commit --amend --no-edit # add a file, keep the messageAmending replaces the commit with a new one; as Pro Git puts it, it is as if the previous commit never happened. Fine on a local branch, a problem on a shared one.
Undo commits: reset versus revert
git reset moves the branch pointer
| Mode | Commits after the target | Staging area | Working tree |
|---|---|---|---|
--soft | Removed from the branch | Changes kept, staged | Unchanged |
--mixed (default) | Removed from the branch | Changes kept, unstaged | Unchanged |
--hard | Removed from the branch | Reset | Reset: uncommitted edits are lost |
git reset --soft HEAD~1 is the cleanest way to "uncommit" and recommit differently, for example to split a commit in two. Use --hard only after checking git status; the commits it removes can be recovered through the reflog, but uncommitted edits cannot.
git revert adds an opposite commit
git revert a1b2c3d # opens an editor for the message
git revert --no-edit a1b2c3d # use the default "Revert ..." message
git revert -m 1 <merge-commit> # revert a merge, keeping parent 1 as mainlineThe Git documentation describes revert as recording new commits that reverse the effect of earlier ones, which is why it is the right tool on main or any branch others use. Reverting a merge needs -m to say which parent is the mainline, and the documentation warns that it declares you will never want that merge's changes: merging the same branch again later will not bring them back unless you revert the revert.

Rescue work you thought was lost
The reflog: Git's undo history
The reflog records when the tips of branches and other references were updated in your local repository, including every commit, reset, rebase and branch switch. Entries are kept for 90 days by default (gc.reflogExpire), or 30 days for entries no longer reachable from the branch (gc.reflogExpireUnreachable). It exists only on your machine; a fresh clone has none of it.
git reflog -5
# 73de51a HEAD@{0}: reset: moving to HEAD~2
# 3d2ff30 HEAD@{1}: commit: third again
# ...
git reset --hard 'HEAD@{1}' # put the branch back where it was before the resetThis is the exact sequence we ran: after a git reset --hard HEAD~2 removed two commits, git reset --hard 'HEAD@{1}' brought both back. Quote HEAD@{1} so shells such as zsh do not interpret the braces.
Restore a deleted branch
git branch -D experiment
# Deleted branch experiment (was 20e41f8).
git branch experiment 20e41f8 # recreate it from the printed hash
# or find the hash later with: git reflog | grep experimentGet one file from the past
git restore --source=HEAD~2 app.txt # working tree only
git log --oneline -- app.txt # find the commit you want firstTo find the commit that added or removed a piece of text, git log -S "text" --oneline searches the history of changes rather than commit messages.
Park work and switch context
git stash push -u -m "wip: pricing page" # -u includes untracked files
git stash list
git stash pop # re-apply and drop the stash
git stash apply stash@{1} # re-apply, keep the stashFor longer interruptions, such as an urgent fix while a feature is half done, a worktree is often better than a stash. It checks out a second branch in a separate folder that shares the same repository:
git worktree add ../myapp-hotfix -b hotfix
# fix, commit and push from ../myapp-hotfix, then:
git worktree remove ../myapp-hotfixForce-pushing safely
If you rewrote your own feature branch with amend, reset or rebase, the remote will reject a normal push. Use --force-with-lease rather than --force: the git-push documentation explains that it refuses the update unless the remote branch still points where your remote-tracking branch says it does, so you cannot silently overwrite a colleague's new commits.
git push --force-with-lease --force-if-includes origin feature/loginThe documentation adds a caveat: if something runs git fetch in the background, such as an editor, the lease check can pass without you ever having seen the new commits. --force-if-includes closes that gap by also checking that the remote tip appears in your local reflog. You can make it the default with git config --global push.useForceIfIncludes true. Never force-push to main; protect it on GitHub or GitLab so nobody can.
How to choose, in one paragraph
Ask two questions. Was it committed? If not, restore and stash are your tools, and anything you discard is gone. Was it pushed to a shared branch? If yes, use revert; if no, amend and reset are fine. And whenever something seems lost, run git reflog before you panic.
Practise before you need it
Create a throwaway repository, make three commits and try each command in this guide. Five minutes of practice is much cheaper than learning reset modes during an incident. For the commit that broke your build, pair this sheet with git bisect; for the full reference, the free Pro Git book and the Git reference manual are maintained on git-scm.com.
Frequently asked questions
What is the difference between git reset and git revert?
resetmoves your branch back and removes commits from it, rewriting history.revertadds a new commit that reverses an earlier one, so it is safe on shared branches.How do I undo the last commit but keep my changes?
git reset --soft HEAD~1keeps them staged;git reset HEAD~1keeps them unstaged. Only do this if the commit has not been pushed to a shared branch.Can I recover a branch I deleted?
Usually, yes. Use the hash printed by
git branch -D, or find it withgit reflog, then rungit branch <name> <hash>.How long does Git keep reflog entries?
By default 90 days, or 30 days for entries no longer reachable from the branch tip, controlled by
gc.reflogExpireandgc.reflogExpireUnreachable.Can I recover changes discarded with git restore?
Generally no. Uncommitted edits discarded with
git restoreorgit reset --hardare not in the reflog. Stash or commit work you might want back.Why use --force-with-lease instead of --force?
It refuses to overwrite the remote branch if someone else has pushed since you last fetched. Add
--force-if-includesto protect against background fetches too.
Your next customer could start right here.
Introduce your tool, product or business to readers exploring technology.
Explore placements and request a private quote
Sources
- Pro Git: Undoing Things — git-scm.com
- git-revert documentation — git-scm.com
- git-reflog documentation — git-scm.com
- git-push documentation — git-scm.com
- git-restore documentation — git-scm.com
Comments
No comments yet. Be the first to join the conversation.
Leave a comment
Every comment is read by a moderator before it appears. Your email address is never published; it is used only to contact you about this comment.
More from Cloudsoft Zone

TutorialDevOps
Laravel log management: rotation, retention and safe production debugging
Set up Laravel log management with deliberate channels, retention, file ownership, rotation responsibility and useful incident context without recording secrets.
By Cloudsoft Zone

TutorialDevOps
Laravel backup restore: recover the application, database and uploaded files together
Plan Laravel backup restore around a recoverable set of code, database, uploaded objects and protected keys, then verify the complete application in isolation.
By Cloudsoft Zone

TutorialDevOps
Laravel S3 compatible uploads: private files, MinIO endpoints and safe delivery
Build Laravel S3 compatible uploads with a private disk, scoped credentials, validated files, unique object keys and authorized downloads across MinIO or S3.
By Cloudsoft Zone
