Git makes it hard to truly lose work, but it is easy to panic when something goes wrong. This guide covers the most common "how do I undo" situations, from safest to most powerful.

Rule of thumb: if the commits are already pushed and shared, prefer commands that add new history (revert) over commands that rewrite it (reset, rebase).

Discard uncommitted changes to a file

bash
git restore path/to/file.ts          # discard working changes
git restore --staged path/to/file.ts # unstage, keep changes

Fix the last commit message or add a forgotten file

bash
git add forgotten-file.ts
git commit --amend -m "Better message"

Only amend commits that have not been pushed, or you will need to force-push.

Undo the last commit but keep the changes

bash
git reset --soft HEAD~1   # changes stay staged
git reset HEAD~1          # changes stay, unstaged

Throw away the last commit completely

bash
git reset --hard HEAD~1

This discards the changes. If you regret it, see the reflog section below.

Undo a commit that is already pushed

Create a new commit that reverses it. History stays intact for everyone.

bash
git revert <commit-sha>
git push

Committed to the wrong branch

Move the commit to a new branch and remove it from the current one:

bash
git branch feature/my-work        # new branch pointing at your commit
git reset --hard HEAD~1           # remove it from the current branch
git switch feature/my-work

To move it to an existing branch instead, use git cherry-pick <sha> on that branch.

Recover "lost" commits with the reflog

The reflog records where HEAD has been, including commits you reset away.

bash
git reflog
# a1b2c3d HEAD@{0}: reset: moving to HEAD~1
# 9f8e7d6 HEAD@{1}: commit: Add payment retry logic

git reset --hard 9f8e7d6    # or: git branch recovered 9f8e7d6

Unreachable commits are kept for a while before garbage collection, so recovery is usually possible for weeks.

Undo a bad merge or rebase

Right after a merge or rebase, ORIG_HEAD points to where you were before:

bash
git reset --hard ORIG_HEAD

For a merge commit that is already pushed, revert it with the parent you want to keep: git revert -m 1 <merge-sha>.

Force-pushing safely

If you must rewrite pushed history on your own branch, use:

bash
git push --force-with-lease

It refuses to overwrite commits someone else pushed in the meantime.

Key takeaways

  • restore for files, reset for local commits, revert for shared commits.
  • The reflog can recover almost any commit you reset away.
  • Avoid rewriting shared history; when necessary, use --force-with-lease.