Git

What Does Git Reset Do? Discover Its Use

What Does Git Reset Do? Discover Its Use

Most people meet git reset right after a commit they wish they hadn’t made. The command moves the current branch to a commit you pick, and depending on the mode it also rewrites the index and the working directory to match.

People use it to undo commits they haven’t pushed and to unstage files. Some also use it to rewind a local branch to a state they trust. Run it with no flag and you get a mixed reset.

One line in the docs is easy to skim past. According to the git-scm manual (Git 2.56.0 documentation, 2026), a hard reset may overwrite untracked files, not only tracked ones.

What does git reset do?

YouTube player

The git-scm manual describes the command at two levels. At the commit level it changes which commit HEAD points to, and that is how you undo a commit, merge, rebase or pull. Hand it files or directories and it only updates their staged version.

Neither level deletes a commit. The abandoned commits stay in the repository and in the reflog until they expire, which is why most resets can be reversed.

The three trees reset works on

Pro Git describes Git as a content manager for three trees, and every reset mode is defined by how far it reaches into them.

HEAD is the snapshot of your last commit, and it becomes the parent of the next one. The index is the proposed next commit. The working directory is the sandbox, meaning the real files you edit.

The index is what most people call the staging area, which explains why reset is the command that unstages things.

How reset differs from checkout

Reset moves the branch that HEAD points to. Checkout moves HEAD itself and leaves the branch where it was.

Say you are on develop. Running git reset master drags develop to master’s commit, while git checkout master leaves develop alone and switches HEAD to master.

Why is GitHub the heart of open source?

Uncover GitHub statistics: developer community growth, repository trends, collaboration patterns, and the platform that powers modern software development.

Explore GitHub Data →

Checkout is also working-directory safe, because it checks that it is not overwriting files with changes and tries a trivial merge in the working directory instead. git reset --hard replaces everything without asking.

How do soft, mixed and hard resets differ?

YouTube player

Every reset with a commit starts by moving the branch. Soft stops right there. Mixed goes a step further and resets the index. Hard keeps going and overwrites the working directory too.

Mixed is the default when no flag is given. The git-reset manual (last revised in Git 2.54.0, April 2026, with no changes through 2.56.0) lists two more modes, merge and keep.

ModeIndexWorking directoryAborts when
–softUnchangedUnchangedThe index has unmerged entries
–mixedMatches new HEADUnchangedNever
–hardMatches new HEADOverwrittenNever
–mergeMatches new HEADUpdated, except files with unstaged editsA file differing between the commit and the index has unstaged edits
–keepMatches new HEADUpdated where the commit differs from HEADA file differing between the commit and HEAD has local edits, or the index has unmerged entries

HEAD moves in every row, so the table leaves that column out.

Soft reset

The manual’s own example is git reset --soft HEAD~5 followed by git commit, which combines the last 5 commits into 1.

Looking to sharpen your Git skills? Branching, merging, rebasing, and everything else you need - including git rebase, git stash, and commit workflows - is on one page in the Git Cheat Sheet.

The branch moves while the index and the files stay exactly as they were. Everything from the undone commits sits staged and ready to commit again.

The manual adds a warning. It works with changes already in the working tree, but that usage can lead to confusion.

Mixed reset

Mixed is what you get by typing nothing extra. The branch moves and the index is reset to the new HEAD, but every file in the working directory is left alone.

The net effect rolls back both git commit and git add. Your edits survive, but nothing is staged.

Hard reset

Pro Git singles out --hard as the one option that destroys data. Every other form can be undone fairly easily.

▶ GIT EXPLAINED

If you commit, branch, or merge code every day, it helps to know how Git came to be. This video covers how Linus Torvalds created Git in just 10 days after a license dispute and explains how its data model actually works.

Linus Torvalds Built Git in Days Out of Spite
Built in 10 Days Out of Spite. Git and its data model explained in plain language.

Tracked files are overwritten with the version from the commit, and removed if the commit lacks them. Untracked files may be overwritten too, according to the git-scm manual. Committed work is still reachable through the reflog. Uncommitted edits are gone for good.

Stage or stash first when in doubt.

Merge and keep

Both exist for narrower jobs, and both refuse to run when they could destroy local edits.

With –merge, the index is reset and files that differ between the commit and HEAD are updated, but files with unstaged edits are kept. The manual says it mainly exists to reset unmerged index entries, such as those left behind by git am -3 or git switch -m in certain situations, and it is also the safe way to back out of a merge or pull in a dirty working tree. In the manual’s example, git reset --merge ORIG_HEAD undoes a pull into a dirty working tree, where --hard would have thrown the local changes away.

–keep removes recent commits while preserving working tree changes, and it aborts if a file touched by those commits also has local changes. In the manual’s example, git reset --keep start drops an unwanted commit after you moved to a new branch.

How to write the command: syntax and commit references

The form is git reset [mode] [commit], and the commit defaults to HEAD when you leave it out.

That makes plain git reset a mixed reset to HEAD. Nothing moves, and the index is reset to match the last commit.

Commit references reset accepts

  • HEAD~N is the Nth-generation ancestor, following first parents only, so HEAD~3 equals HEAD^^^
  • HEAD^ is the first parent, and HEAD^2 picks the second parent of a merge commit
  • A hash can be the full object name or any unique leading substring
  • A branch name works too, whether local or a remote-tracking ref such as origin/main
  • HEAD@{n} reads the n-th prior value of HEAD from the reflog

On a linear history, HEAD~1 and HEAD^ name the same commit. They only diverge on merge commits, where ^2 reaches the second parent and ~ never leaves the first.

A commit hash is the steadier reference once history has been rewritten, because relative names like HEAD~2 shift after every reset.

ORIG\_HEAD and checking the result

Before reset runs, Git writes the previous branch tip to ORIG\_HEAD. The gitrevisions manual lists reset, merge, rebase and am as the commands that set it.

That gives you a one-step way back. The git-reset manual uses git reset --hard ORIG_HEAD to undo a pull.

After any reset, run git status to see what is staged and what is not. git diff --cached then compares the index against HEAD.

What does git reset do to a single file?

YouTube player

Give it a path and git reset updates only the index. HEAD, the branch and the working directory stay where they are.

Pro Git explains the logic. HEAD is a single pointer and cannot point to part of one commit and part of another, so reset skips the branch move and limits the rest of its work to the named files.

git reset file.txt is shorthand for git reset --mixed HEAD file.txt. It copies the file from HEAD into the index.

That makes it the exact opposite of git add. Add copies a file into the index, and reset copies it back out.

In practice this is how you unstage a file without touching your edits. Modern Git has a dedicated command for the same job. The git-reset manual calls git reset <pathspec> equivalent to git restore --staged <pathspec>, and git restore --staged is what git status suggests today.

Commit everything except one file

  1. Run git reset -- frotz.c to remove the file from the index while keeping it on disk
  2. Run git commit -m "Commit files in index" to commit everything else
  3. Run git add frotz.c to stage the file again

The -- separator marks everything after it as a path, so a file name is never read as a commit.

Staging an older version of a file

A commit argument changes the source. git reset eb43bf file.txt stages the version of the file from that commit.

Commit now and the history records the file reverting, although your working copy never changed.

Add --patch and reset works hunk by hunk, the opposite of git add -p.

What path-level reset cannot do

It cannot touch working tree files. Pro Git notes that reset will not run a hard reset on a path.

To change file contents, use git restore, or git checkout [commit] [paths]. Checkout overwrites the working copy as well, and it is not working-directory safe.

Which reset command fits which task?

Pick the smallest mode that reaches the goal. Soft before mixed, mixed before hard.

Hard is the only mode that can cost you work, so make it a deliberate choice and never a reflex.

  • To undo the last commit and keep its changes staged, run git reset --soft HEAD~1
  • If you want the edits kept but unstaged, git reset HEAD~1 does it
  • Dropping the commit and its changes entirely takes git reset --hard HEAD~1
  • git reset on its own unstages everything
  • git reset --hard throws away all uncommitted work in tracked files
  • Backing out of a merge or pull means targeting ORIG_HEAD, with --merge when local edits exist

Undo a commit and redo it

YouTube player

This is the manual’s own recipe for a commit made too early or with a typo in the message.

  1. Run git reset --soft HEAD^. The working tree stays as it was.
  2. Edit the files that need correcting.
  3. Run git commit -a -c ORIG_HEAD to commit again, starting from the old message.

Swap -c for -C when the message needs no edits.

Squash recent commits

Pro Git’s example runs git reset --soft HEAD~2 and then git commit, which folds the last two commits into one.

It is the quickest way to squash several commits into one, but it only suits commits you have not pushed.

Match a branch to the remote

YouTube player

Two commands do it. git fetch origin first, then git reset --hard origin/main.

The reset target is the remote-tracking branch, which is only as current as your last fetch. Skip the first command and you reset to stale history.

Local commits and edits on that branch are discarded. Commit or stash them first if any of it matters.

Git reset, git revert or git restore: which one should you use?

YouTube player

Use reset on unpublished local history, revert on commits others may have pulled, and restore for file contents. Only reset rewrites the branch.

Attributegit resetgit revertgit restore
What it changesBranch pointer, index, optionally the working directoryAdds a commit that reverses an earlier oneFiles in the working tree, the index, or both
Effect on historyCommits drop off the branchHistory stays, one commit is appendedNone
Safe after pushingNoYesYes, it only touches local files
Typical useClean up unpublished commitsUndo a published commitDiscard edits or unstage

The git-revert manual points readers to the other two commands for different jobs. It sends you to reset --hard to throw away uncommitted changes and to restore --source to pull files from another commit. It adds that both discard uncommitted changes in the working directory.

How git revert works

The git-revert manual (last revised in Git 2.54.0, April 2026) describes it as recording new commits that reverse earlier ones, and it requires a clean working tree.

Because git revert adds a commit instead of moving the branch, teammates pull the undo like any other change.

Merge commits need extra care. Revert refuses them unless you pass -m with the number of the parent that counts as the mainline, starting from 1.

The manual attaches a failure mode to that. Reverting a merge declares you never want its tree changes, so later merges bring in only commits that are not ancestors of the reverted merge.

On a shared branch, revert wins even when reset looks quicker.

How git restore differs

Restore targets the working tree by default. Pass --staged and it targets the index, and pass both flags to hit both. The source defaults to the index, or to HEAD once --staged is given, and --source accepts any commit, branch or tag.

Tracked files missing from the source are removed, because no-overlay mode is the default.

Restore has no commit-level form at all. Its manual was last revised in Git 2.55.0 (June 2026), and it never moves a branch.

Can you undo a git reset?

YouTube player

Committed work, yes. Uncommitted edits wiped by a hard reset, no.

Tutorials sound contradictory on this. O’Reilly’s Version Control with Git warns that destroyed changes may be unrecoverable even if you keep backups, while Pro Git shows the committed version coming back through the reflog.

Both are right. The dividing line is whether the work was ever committed.

Two ways back to the old tip

ORIG\_HEAD is the quick route, but it holds one position only, so the next command that moves HEAD in a drastic way replaces it.

The reflog records every move of HEAD in your local repository, so it reaches back past the most recent reset.

Recover a branch after a bad reset

  1. Run git reflog and find the entry from just before the reset.
  2. Note its HEAD@{n} position or its hash.
  3. Run git branch rescue [hash] to keep a safe pointer, or git reset --hard HEAD@{n} to move the branch back.

Step 3 in its hard form overwrites the working directory again. Stash or commit current edits first.

How long recovery stays possible

Reflog entries expire, usually through git gc rather than by hand. As of the Git 2.56.0 documentation, the git-reflog manual sets two limits. Reachable entries last 90 days, controlled by gc.reflogExpire. Entries unreachable from the current tip last 30 days, controlled by gc.reflogExpireUnreachable.

A reset leaves the abandoned commits unreachable from the branch tip, so in the usual case the 30-day limit is the one that applies to them. Treat a month as the window.

When should you not use git reset?

Skip it for any commit that others may already have pulled. Reset rewrites the branch locally, and the remote still holds the old history.

The git-reset manual says the same in its own example. Do not drop commits with a hard reset once you have given them to somebody else.

What happens when you push a reset branch

The remote rejects it. git push accepts only fast-forward updates to a branch by default, and a reset branch is not a descendant of the old tip.

Forcing the push makes the remote lose history. The git-push manual notes that the abandoned commits become unreachable there, and a later git gc on that repository removes them.

The same trade-off applies to a rebase, which also rewrites commits you have already published.

Force versus force-with-lease

Plain --force disables the fast-forward check, the other push safety checks and the lease checks. It can leave the remote without commits a teammate just pushed.

--force-with-lease updates the remote ref only if it still holds the value you expect, so a teammate’s new commit makes your push fail instead of vanishing.

The lease has a documented weakness. Without an explicit expected value, it compares against your remote-tracking branch, and a background git fetch from an editor plugin or a cron job refreshes that branch and defeats the check.

The git-push manual (last revised in Git 2.55.0, June 2026) offers ways around it. You can name the expected commit with --force-with-lease=[ref]:[expect], or add --force-if-includes, which rejects the push unless the remote tip is already integrated locally. A separate push-only remote also works, since background fetches of origin then do not touch the branch you push to.

Other limits

Reset never contacts the remote, so teammates see nothing until a forced push.

Submodules are left alone too, unless you pass --recurse-submodules. Only then does a reset rewind active submodules to the commit the superproject records, leaving each submodule HEAD detached.

Treat the forced push as the step that turns a private cleanup into everyone’s problem.

Git Reset FAQ

Does git reset delete untracked files?

Not by design. Untracked files stay in place through soft, mixed and hard resets, although the git-reset manual says a hard reset may overwrite them.

To remove them, use git clean, which refuses to delete anything without -f because clean.requireForce defaults to true.

What happens if you run git reset in a detached HEAD state?

HEAD itself moves to the target commit, since there is no branch to carry it. The manual describes HEAD as the tip of the current branch only when you are on one, so a detached HEAD simply points at the new commit directly.

Commits left behind stay reachable through the reflog only.

How do you split one commit into several with git reset?

Rewind the commit while keeping its changes, then stage and commit them in pieces. The manual’s recipe is git reset -N HEAD^, then git add -p, then git diff --cached to check, and finally git commit -c HEAD@{1} to reuse the original message.

The -N flag keeps files the commit added visible to git add -p. Repeat until nothing remains.

Can git reset move a branch forward?

Yes. The target can be any commit, including one ahead of the current tip, such as a remote-tracking branch after a fetch or an earlier reflog position like HEAD@{1}.

Reset simply points the branch at the commit you name, in either direction.

What to Check Before Running git reset

YouTube player

Look at the working state, pin the current tip, then choose the mode. In that order, a wrong git reset is harmless.

Start with git status to see what is staged, unstaged and untracked. Then run git branch backup to pin the current tip, and only after that pick the smallest mode that reaches the goal.

The branch protects more than the reflog does. A branch keeps the old commits reachable, so garbage collection cannot remove them, whatever happens to the reflog entries after 30 days.

The cost is one extra branch to delete later. The backup covers committed work only, so commit or stash edits before any hard reset.

The sequence holds as of the Git 2.56.0 documentation (September 2026), and only a change to the reflog expiry defaults would alter the timing.

A git cheat sheet keeps the mode flags and reference forms within reach for the next reset.

Bogdan Sandu

Stay sharp. Ship better code.

Every week: one curated article, one tool worth knowing, one tip you can use tomorrow. No noise, no padding.