Skip to content
masterclass Buddy

Git · Command

git reset

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

How it works

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.

Syntax

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>...]

Parameters

NameTypeDescription
<commit>commit-ishCommit the current branch is moved to. Defaults to HEAD.
--soft | --mixed | --hard | --merge | --keepmode flagSets how much of the index and working tree is updated. The default is --mixed.
-NflagOnly with --mixed. Paths that the reset removes from the index are marked as intent-to-add.
<tree-ish> <pathspec>...tree-ish, pathsCopies the staged version of the matching paths from the given tree. HEAD and the working tree are left alone.
-p, --patchflagLets you choose hunks interactively to unstage, comparing the index with the tree-ish.
-q, --quietflagPrints errors only.
--[no-]refreshflagRefreshes the index after a mixed reset. This is on by default.
--pathspec-from-file=<file>, --pathspec-file-nulfile path, flagReads pathspecs from a file, or from stdin if the file is '-'. With --pathspec-file-nul, entries are separated by NUL.
--[no-]recurse-submodulesflagAlso resets the working trees of active submodules.
-U<n>, --inter-hunk-context=<n>integerDiff context settings for patch mode.

Key points

  • The default mode, --mixed, unstages everything but keeps all your edits on disk. A bare git reset is therefore a quick way to empty the staging area.
  • --soft leaves both staged and unstaged work alone. That makes git reset --soft HEAD~N followed by git commit an easy way to squash the last N commits into one.
  • The previous branch tip is saved in ORIG_HEAD. So you can undo a reset, merge or pull with git reset --hard ORIG_HEAD, or reuse the old commit message with git commit -c ORIG_HEAD.
  • --keep is the safe way to drop recent commits while keeping uncommitted edits. It refuses to run if those edits touch files that the reset would change.
  • --merge is designed to back out of a merge that stopped with conflicts. It works on unmerged index entries, for example after git am -3.
  • git reset -p is the opposite of git add -p. It lets you unstage individual hunks instead of whole files.
  • The path form never touches the files on disk. If you also want to throw away the edits in the working tree, use git restore.
  • With --recurse-submodules, the submodules are reset as well. Each one ends up on a detached HEAD at the commit the superproject records.

Common mistakes

  • --hard throws away tracked changes. It can also overwrite untracked files that get in the way.
  • Resetting changes history. Don't use it to remove commits you have already shared, because the people who have them will run into trouble.
  • If you undo a merge with --hard while you have unrelated local edits, those edits are lost too. Use --merge in that case.
  • Running --soft while you have unstaged edits is allowed, but it can easily mix up what ends up in the next commit.

Example: Comparing soft, mixed and hard resets in a throwaway repo

#!/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 --oneline

The 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.

When to use it

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.

Move commits to a topic branch

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.

Split a commit

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.

Unstage one file before committing

Run git reset -- frotz.c so that file stays out of the commit while the rest of the staged changes go in.

Interview questions

QWhat is the difference between git reset --soft, --mixed and --hard?

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.

QWhen should you use git reset rather than git revert?

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.

QWhat does git reset <path> do, and how is it related to git add?

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 restore
  • git revert
  • git add
  • git commit
  • git reflog
  • git stash
  • git switch
  • git rebase

Source: Git - git-reset Documentation. Checked 2026-11-02.