# How to Undo the Last Git Commit: Reset, Revert or Amend

Which command you need depends on two questions: **have you pushed the commit**, and **do you want to keep the changes**?

| Situation | Command |
|---|---|
| Not pushed — undo the commit, keep the changes staged | `git reset --soft HEAD~1` |
| Not pushed — undo the commit, keep the changes unstaged | `git reset HEAD~1` |
| Not pushed — throw the commit and its changes away | `git reset --hard HEAD~1` |
| Fix the last commit's message or add a forgotten file | `git commit --amend` |
| Already pushed to a shared branch | `git revert HEAD` |

`HEAD~1` means "the commit before the current one". Use `HEAD~3` to go back three commits.

## Undo a commit but keep the work

```bash
git reset --soft HEAD~1
```

The commit disappears and its changes are left **staged**, ready to commit again — useful when you committed too early or want to split the work differently.

```bash
git reset HEAD~1
```

The default (`--mixed`) does the same but leaves the changes **unstaged** in your working directory, so you can choose again what goes into each commit.

## Throw the commit away completely

```bash
git reset --hard HEAD~1
```

This deletes the commit **and** discards its changes from your files. Uncommitted work in your working directory is discarded too, so check `git status` first. (The commit itself is still recoverable for a while — see reflog below.)

## Fix the last commit instead

To change the message:

```bash
git commit --amend -m "Better message"
```

To add a file you forgot:

```bash
git add forgotten-file.js
git commit --amend --no-edit
```

Amending replaces the commit with a new one. That's fine locally, but if you've already pushed it, the next push needs force (see below).

## Undo a commit that's already pushed

On a branch other people use, don't rewrite history. Create a new commit that reverses the old one:

```bash
git revert HEAD
git push
```

`git revert` works on any commit, not just the last: `git revert a1b2c3d`. To revert a **merge** commit, tell Git which parent to keep — usually the first, the branch you merged into:

```bash
git revert -m 1 <merge-commit>
```

## Pushed, but only to your own branch

If nobody else has pulled the branch — say, your own feature branch — you can rewrite it and force-push:

```bash
git reset --hard HEAD~1
git push --force-with-lease
```

`--force-with-lease` refuses to push if the remote has commits you haven't seen, so you can't silently wipe out someone else's work. Prefer it to plain `--force`.

## Recover from a reset you regret

Git keeps a log of every position `HEAD` has been at, including commits you reset away:

```bash
git reflog
```

```text
e4f5a6b HEAD@{0}: reset: moving to HEAD~1
9c8d7e1 HEAD@{1}: commit: Add search endpoint
```

Jump back to the commit before the reset:

```bash
git reset --hard HEAD@{1}
```

The same trick undoes an amend: `git reset --soft HEAD@{1}` puts back the original commit and leaves the amended changes staged. Reflog entries are kept for 90 days by default, and only in your local repository.

## A few related undos

```bash
git restore --staged file.js    # unstage a file, keep the edits
git restore file.js             # discard uncommitted edits to a file
git update-ref -d HEAD          # undo the very first commit in a repo
```

The last one is needed because the first commit has no parent, so `HEAD~1` doesn't exist.
