git.guide

Undoing almost anything

Find the row that matches what you did. The commands are in order of how much they destroy, and anything that cannot be recovered is marked. Nothing on this page is lost forever except where it says so.

Read this first

Almost nothing that is committed is ever really gone.

Once git has recorded a commit, it stays in the repository even after you move a branch away from it. It becomes unreachable rather than deleted, and git keeps a log of everywhere your branch has pointed, which is how you find it again.

git reflog

That prints a numbered list of every position your current branch has been in, most recent first, with the command that moved it. Almost every disaster on this page is recoverable because that list exists.

The exception, and it is the one to remember: changes you never committed are not in there. Git has no record of a file you edited and did not stage. Anything that discards uncommitted work is the genuinely dangerous kind, and it is called out below.

Unreachable commits are not kept forever. Git eventually clears them out, by default after thirty days for anything unreachable. In practice that is a month to notice, which is plenty, but it is not indefinite.

The table

What did you do?

You want toCommandSafe?
Unstage a file you added by mistake git restore --staged file.txt Yes. The edit stays in your working copy
Throw away your edits to one file git restore file.txt No. Uncommitted work, gone for good
Fix the message on the commit you just made git commit --amend Yes, if you have not pushed it
Add a forgotten file to the commit you just made git add file.txt then git commit --amend --no-edit Yes, if you have not pushed it
Undo the last commit but keep the changes staged git reset --soft HEAD~1 Yes. Nothing is lost
Undo the last commit and keep the changes unstaged git reset HEAD~1 Yes. Nothing is lost
Undo the last commit and throw the changes away git reset --hard HEAD~1 Careful. The commit is recoverable, uncommitted work is not
Undo a commit that other people already have git revert <commit> Yes. This is the right answer for a shared branch
Get back a commit you reset away git reflog then git reset --hard <commit> Yes, within about a month
Abandon a merge that is going badly git merge --abort Yes. Puts you back exactly as you were
Abandon a rebase that is going badly git rebase --abort Yes. Puts you back exactly as you were
Get back a branch you deleted git reflog then git branch name <commit> Yes, within about a month
Put aside what you are doing without committing git stash, later git stash pop Yes, and see the warning below

git restore and git switch arrived in git 2.23, in 2019, to split apart the jobs that git checkout used to do all at once. Older instructions say git checkout -- file.txt, which still works and does the same thing. The newer pair are worth the habit because their names say which one throws work away.

The distinction everything else rests on

Reset moves the branch. Revert adds a commit.

These two get confused constantly and the difference decides whether you are about to cause a problem for somebody else.

Reset moves your branch pointer backwards, so the commits after it are no longer on the branch. The history now looks as though they never happened. That is fine while the work is only on your machine, and it is a problem the moment somebody else has those commits, because their history and yours no longer agree.

Revert leaves everything where it is and adds a new commit that undoes the change. The history grows rather than being rearranged, so everybody else is unaffected. It is honest, it is boring, and it is the correct answer on any branch that other people or a deploy pipeline use.

# On a shared branch, always this
git revert a1b2c3d

# Reverting a merge needs to know which side to keep,
# and -m 1 means the branch you merged into
git revert -m 1 <merge-commit>

The rule of thumb: if the commit has left your machine, revert it. If it has not, reset is fine and tidier.

The three flavours of reset

Soft, mixed and hard, in one paragraph each.

Soft moves the branch and leaves everything else alone, so your changes are sitting staged and ready to commit again. This is what you want when you committed too early or want to combine the last few commits into one.

Mixed is the default when you type no flag at all. It moves the branch and unstages the changes, leaving them in your working copy. This is what you want when you committed the wrong set of files and want to pick again.

Hard moves the branch and makes your files match, which means it throws away everything you had not committed. This is the only one that destroys work irrecoverably, and it is also the one most often copied off the internet by somebody in a hurry.

# Check what you would lose before running a hard reset
git status
git stash list

If you are unsure, commit first. A commit you do not want is a trivial problem. Uncommitted work that has been overwritten is not a problem at all, because there is nothing left to have a problem with.

Recovering something you think you have lost

Working through the reflog

  1. Look at where the branch has been

    git reflog prints entries like HEAD@{4} alongside the commit and what moved it. Find the point just before the thing you regret.

  2. Check it is what you think it is

    git show HEAD@{4} shows you the commit before you act on it. This step takes seconds and stops you fixing the wrong mistake.

  3. Get to it without moving anything yet

    git switch --detach HEAD@{4} puts your files in that state and leaves every branch alone, so you can look around safely.

  4. Keep it

    git branch recovered HEAD@{4} makes a branch at that point. Now it is reachable again, it will not be cleared out, and you can decide what to do with it in your own time.

The reflog is local to your machine and to your clone. It is not pushed and it is not shared, so it cannot help you with something that only ever existed on somebody else's computer, and somebody else's reflog may save you when yours cannot.

The one that catches people

Stash is not a filing cabinet.

Stashing is genuinely useful for putting work aside for ten minutes. It becomes a trap when it is used as storage, for three reasons.

  • It does not include untracked files by default. A new file you have not added is left behind, and if you then switch branches and clean up, it goes. Use git stash -u to include them.
  • The list is unlabelled and grows. Six entries called "WIP on main" three weeks later tell you nothing. Use git stash push -m "what this is".
  • Nothing reminds you it is there. A branch is visible in every listing. A stash is visible only when you go looking, and work has been lost that way more than once.

For anything you might still want tomorrow, make a branch instead. It costs nothing, it has a name, and it shows up.

The one thing on this page that is not recoverable

Uncommitted work. Git has no record of it, so no command here can bring it back.

The habit that makes everything else on this page safe is committing early and often on your own branch, even badly, even with a message you would not want anybody to read. A messy history can be tidied later with the tools on the branches page. Work that was never committed cannot be tidied, because it is not there.

Short answers

Questions people actually ask

What is the difference between reset and revert?

Reset moves your branch backwards, so the history looks as though the commits never happened. Revert leaves everything in place and adds a new commit that undoes the change. The rule of thumb: if the commit has left your machine, revert it. If it has not, reset is fine and tidier.

How do I undo a commit I have already pushed?

git revert, naming the commit. It adds a commit rather than rearranging what other people already have, so nobody else gets a conflict in code they never touched. Reverting a merge needs -m 1 to say which side to keep.

I ran git reset --hard. Can I get my work back?

The commits, almost certainly. git reflog lists everywhere your branch has pointed, and you can make a branch at the old position. Anything you had not committed is gone, because git never had a record of it. That is the one genuinely unrecoverable case.

I deleted a branch by mistake. Is it gone?

No. Deleting a branch removes a pointer and leaves the commits unreachable rather than deleted. Find the tip in git reflog and make a new branch at it.

How long do I have to recover something?

About thirty days for anything unreachable, by default, before git clears it out. In practice that is a month to notice, which is usually plenty. It is not indefinite, and the reflog is local to your clone, so it cannot help with something that only ever existed on another machine.

How do I fix the message on my last commit?

git commit --amend. Safe while the commit is only on your machine. Once it has been pushed, amending replaces it, which is the same problem as rebasing shared history, so do it before you push or leave it alone.

Is there any way to recover uncommitted changes?

Not through git, because git has no record of them. This is why committing early and often on your own branch, even badly, is what makes everything else on the undo page safe. A messy history can be tidied later. Work that was never committed cannot.

Every question on this site, in one list

Why your ignore file is not working

Because it does nothing about a file git has already been told to track.

A repository that has got too big

What one committed database dump costs, and how long it keeps costing it.

Branches, merging and rebasing

What a branch actually is, and the one rule you must not break.

All nine guides