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?

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

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.
| Mode | Index | Working directory | Aborts when |
|---|---|---|---|
| –soft | Unchanged | Unchanged | The index has unmerged entries |
| –mixed | Matches new HEAD | Unchanged | Never |
| –hard | Matches new HEAD | Overwritten | Never |
| –merge | Matches new HEAD | Updated, except files with unstaged edits | A file differing between the commit and the index has unstaged edits |
| –keep | Matches new HEAD | Updated where the commit differs from HEAD | A 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.
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.
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~Nis the Nth-generation ancestor, following first parents only, soHEAD~3equalsHEAD^^^HEAD^is the first parent, andHEAD^2picks 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?

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
- Run
git reset -- frotz.cto remove the file from the index while keeping it on disk - Run
git commit -m "Commit files in index"to commit everything else - Run
git add frotz.cto 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~1does it - Dropping the commit and its changes entirely takes
git reset --hard HEAD~1 git reseton its own unstages everythinggit reset --hardthrows away all uncommitted work in tracked files- Backing out of a merge or pull means targeting
ORIG_HEAD, with--mergewhen local edits exist
Undo a commit and redo it

This is the manual’s own recipe for a commit made too early or with a typo in the message.
- Run
git reset --soft HEAD^. The working tree stays as it was. - Edit the files that need correcting.
- Run
git commit -a -c ORIG_HEADto 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

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?

Use reset on unpublished local history, revert on commits others may have pulled, and restore for file contents. Only reset rewrites the branch.
| Attribute | git reset | git revert | git restore |
|---|---|---|---|
| What it changes | Branch pointer, index, optionally the working directory | Adds a commit that reverses an earlier one | Files in the working tree, the index, or both |
| Effect on history | Commits drop off the branch | History stays, one commit is appended | None |
| Safe after pushing | No | Yes | Yes, it only touches local files |
| Typical use | Clean up unpublished commits | Undo a published commit | Discard 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?

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
- Run
git reflogand find the entry from just before the reset. - Note its
HEAD@{n}position or its hash. - Run
git branch rescue [hash]to keep a safe pointer, orgit 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

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.
- Notepad++ Android Options: Apps That Come Close - October 21, 2026
- Data Management Services: A Practical Guide for Growing Companies - October 21, 2026
- MongoDB Cheat Sheet - October 20, 2026




