Fix the last commit
You forgot a file or made a typo in the message. Run git reset --soft HEAD^, make the fix, then commit again with git commit -a -c ORIG_HEAD.
Git · Command
git reset either moves the current branch tip (HEAD) to another commit, or changes what is staged for chosen paths. You use it to undo commits, merges and pulls, or to unstage files.
Last updated
When you give it a commit, reset moves the branch that HEAD points to onto that commit. Before doing so it saves the old tip in ORIG_HEAD. The mode decides how much else changes. --soft moves only the branch. --mixed, the default, also resets the index. --hard also overwrites the working tree. --merge and --keep update the working tree but refuse to run when local edits could be lost. When you give it paths or -p, HEAD stays where it is. Only the index entries for those paths are copied from the given tree-ish, which defaults to HEAD.
git reset [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]
git reset [-q] [<tree-ish>] [--] <pathspec>...
git reset [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]
git reset (--patch | -p) [<tree-ish>] [--] [<pathspec>...]| Name | Type | Description |
|---|---|---|
<commit> | commit-ish | Commit the current branch is moved to. Defaults to HEAD. |
--soft | --mixed | --hard | --merge | --keep | mode flag | Sets how much of the index and working tree is updated. The default is --mixed. |
-N | flag | Only with --mixed. Paths that the reset removes from the index are marked as intent-to-add. |
<tree-ish> <pathspec>... | tree-ish, paths | Copies the staged version of the matching paths from the given tree. HEAD and the working tree are left alone. |
-p, --patch | flag | Lets you choose hunks interactively to unstage, comparing the index with the tree-ish. |
-q, --quiet | flag | Prints errors only. |
--[no-]refresh | flag | Refreshes the index after a mixed reset. This is on by default. |
--pathspec-from-file=<file>, --pathspec-file-nul | file path, flag | Reads pathspecs from a file, or from stdin if the file is '-'. With --pathspec-file-nul, entries are separated by NUL. |
--[no-]recurse-submodules | flag | Also resets the working trees of active submodules. |
-U<n>, --inter-hunk-context=<n> | integer | Diff context settings for patch mode. |
git reset is therefore a quick way to empty the staging area.git reset --soft HEAD~N followed by git commit an easy way to squash the last N commits into one.git reset --hard ORIG_HEAD, or reuse the old commit message with git commit -c ORIG_HEAD.git am -3.git reset -p is the opposite of git add -p. It lets you unstage individual hunks instead of whole files.#!/usr/bin/env bash
set -euo pipefail
cd "$(mktemp -d)" && git init -q demo && cd demo
git config user.email you@example.com && git config user.name You
echo one > a.txt && git add a.txt && git commit -qm "first"
echo two >> a.txt && git commit -qam "second"
# Undo the commit but keep its changes staged
git reset --soft HEAD^
git status --short # M a.txt (staged)
# Unstage, keeping edits on disk (default --mixed)
git reset
git status --short # M a.txt (unstaged)
# Throw away the edits entirely
git reset --hard
cat a.txt # one
# Recover the dropped commit via the reflog
git reset --hard 'HEAD@{2}'
git log --onelineThe script undoes one commit at three depths, first keeping the change staged, then unstaged, then discarding it, and finally gets the dropped commit back from the reflog.
You forgot a file or made a typo in the message. Run git reset --soft HEAD^, make the fix, then commit again with git commit -a -c ORIG_HEAD.
You committed to master too early. Create topic/wip at the current HEAD, run git reset --hard HEAD~3 on master, then switch to topic/wip.
Run git reset -N HEAD^ to remove the commit while keeping its changes, including new files. Then use git add -p and commit several times.
Run git reset -- frotz.c so that file stays out of the commit while the rest of the staged changes go in.
All three move the current branch to the target commit. --soft stops there, so your staged and unstaged changes stay as they are. --mixed, the default, also makes the index match the target, so nothing is staged afterwards. --hard also overwrites the working tree, so any uncommitted tracked changes are lost.
Follow-up: How would you get back commits you lost with --hard? Find the old tip in ORIG_HEAD or in the reflog (for example HEAD@{1}) and run git reset --hard to that commit.
Reset rewrites the branch by moving its tip, so it suits local commits nobody else has. The docs warn against resetting away commits you have already given to others. Revert adds a new commit that cancels the old one, so it is safe on shared branches.
Follow-up: What happens if you reset a branch you already pushed? Pushing again needs a force push, and anyone who built on the old commits has to recover, much like after an upstream rebase.
It copies the given paths from the tree-ish (HEAD by default) into the index. It does not move HEAD or change the files on disk. That makes it the opposite of git add <path>, and the same as git restore --staged <path>.
Follow-up: Why can't you combine --hard with a path? A path reset only touches the index. To change the files on disk for specific paths, you use git restore (or checkout) instead.
git restoregit revertgit addgit commitgit refloggit stashgit switchgit rebase