Git Reset: Soft, Mixed, and Hard Examples

Tested on RHEL 10.2 (Coughlan)
Package git 2.52.0
Applies to Any host with Git installed
Privilege Normal user
Scope git reset modes and common scenarios — undo local commits, unstage paths, discard tracked edits with --hard HEAD, and align with a remote after fetch. Brief reset-versus-revert and reflog pointers — not a full history-rewrite or reflog tutorial.
Related guides Git discard changes
Git revert examples
Git unstage files
Git reflog tutorial
Git remove commit

git reset lets you move back to an earlier commit. Depending on the mode you choose, Git can keep your changes staged, keep them only in your files, or discard them completely. It can also unstage a file after git add without moving your branch at all.

In Git terms, the staging area is where changes sit after git add (ready to commit). The working tree is your actual files on disk. Reset always affects one or both of those — plus your branch position when you reset to an older commit.

HEAD means the commit your branch currently points to. HEAD~1 means one commit before it, and HEAD~2 means two commits before it.

Each section below stands alone. Read the decision table first, pick the row that matches what you want, then jump to that section.


Choose the Right Reset Mode

Your goal Mode Command
Undo last commit; keep changes staged --soft git reset --soft HEAD~1
Undo last commit; keep edits unstaged on disk --mixed (default) git reset HEAD~1
Undo last commit; drop those changes entirely --hard git reset --hard HEAD~1
Unstage one file; keep the same commits --mixed at current commit git reset HEAD file
Unstage everything; keep the same commits --mixed at current commit git reset
Throw away all uncommitted edits; keep the same commits --hard at current commit git reset --hard HEAD
Move branch back N commits any mode git reset [--soft|--mixed|--hard] HEAD~N
Match remote after fetch --hard git fetch origin then git reset --hard origin/main
Undo on a shared branch without rewriting history use revert git revert <commit> — see Git revert examples

On modern Git, unstage-only work can also use git restore --staged — see Git unstage files. To discard current local edits without moving commits, see Git discard changes.


What git reset Changes

Think of --soft, --mixed, and --hard as three levels of undo when you move your branch to an earlier commit:

  • --soft — undo the commit, but keep everything ready to commit again (still staged after git add).
  • --mixed — undo the commit and unstage the changes, but keep your edited files on disk.
  • --hard — undo the commit and throw away the changes in your files too.

When you specify a filename, Git uses the file-only form of reset, which only changes the staging area. It does not move your branch to another commit:

bash
git reset HEAD app.txt

--soft and --hard are available only when resetting the repository to a commit — not when you pass a filename.

Technical mapping — if you already know Git’s internal names:

Mode Branch (HEAD) Staging area (index) Working tree (files on disk)
--soft moves to target commit unchanged unchanged
--mixed moves to target commit reset to match target unchanged
--hard moves to target commit reset to match target reset to match target

For --mixed, Git removes the commit and takes its changes out of the staging area, but the edits remain in your files. Technically, Git resets the index to the target commit while leaving the working tree unchanged.

When the target is your current commit — git reset --hard HEAD — the branch does not move. You stay on the same commit; Git only clears uncommitted staged and on-disk changes in tracked files.


Undo the Last Commit

Scenario: you committed too early. Your latest commit is 5a7c04b Third commit and you want it off the branch — but you may still need the file changes. Each example below starts from that state; run one mode, not all three in a row.

Keep changes staged (--soft)

When to use this: you will fix the commit message or tweak the files and commit again right away.

bash
git reset --soft HEAD~1

The last commit disappears from your branch, but Git keeps the changes staged (ready for git commit):

bash
git status -sb
output
## main
M  app.txt

git log --oneline now shows Second commit as the latest — not Third commit.

Keep changes in your files but unstaged (--mixed)

When to use this: you want the edits in your files, but not staged — this is the default when you omit --soft or --hard. Git removes the commit and takes its changes out of the staging area, but the edits stay in your files.

bash
git reset HEAD~1

Git may print that it unstaged files after removing the commit:

output
Unstaged changes after reset:
M	app.txt

In git status -sb, the first column should be blank — the M moves to the second column, meaning the edit is only in your file, not staged:

bash
git status -sb
output
## main
 M app.txt

Drop the commit and the edits (--hard)

When to use this: the last commit and everything in it should be gone.

bash
git reset --hard HEAD~1
output
HEAD is now at 4d23291 Second commit

Your files should now match Second commit with nothing left over from Third commit:

bash
git status -sb
output
## main

Unstage a File Without Removing Commits

Scenario: you ran git add and want one file out of the staging area before you commit. Status shows M in the first column:

text
## main
M  app.txt

When you give git reset a filename, Git does not move your branch to another commit. It only removes that file from the staging area:

bash
git reset HEAD app.txt
output
Unstaged changes after reset:
M	app.txt

The edit is still in the file; it is just no longer staged:

bash
git status -sb
output
## main
 M app.txt

To unstage every staged file at once:

bash
git reset

That clears the staging area back to the current commit. It does not delete any commits.


Discard All Uncommitted Edits

Scenario: you have local changes (staged, unstaged, or both) and want your tracked files to match the last commit. You are not trying to remove commits from history.

bash
git reset --hard HEAD
output
HEAD is now at 4d23291 Second commit

You should still be on the same commit — only your uncommitted work is gone:

bash
git status -sb
output
## main

git reset --hard HEAD usually leaves unrelated untracked files (?? in status) alone. Use git clean when you want those removed on purpose. Git may delete an untracked file if it would block updating a tracked file. To drop changes in one file without touching the rest of the repo, see Git discard changes.


Move the Branch Back Further

Scenario: you need to remove more than one recent commit, or jump back to a specific commit hash from git log.

To drop the last two commits but keep the file edits (unstaged):

bash
git reset HEAD~2

To jump back to one exact commit and delete local edits — check the hash with Git show command first if you are unsure:

bash
git reset --hard c86447a
output
HEAD is now at c86447a Initial commit

Same three modes as undoing one commit (--soft, --mixed, --hard) — only the commit you target changes.


Match the Remote Branch After Fetch

Scenario: the copy on the server is correct and your local branch is wrong — extra commits, local edits, or both.

Download the latest remote state without merging:

bash
git fetch origin

Use the next command when you want to throw away your local version of the branch and make it exactly match the remote branch. Replace main with your branch name:

bash
git reset --hard origin/main
output
HEAD is now at 91b8fc5 Remote moved forward

This makes your current branch and tracked files match origin/main. Local-only commits and tracked staged or unstaged edits are discarded. Untracked files may remain — run git status and use git clean if you also need ?? paths removed. Commits you lost may still show up in git reflog for a while — see Git reflog tutorial if you need to recover one.

Only do this on a branch you control. If you already pushed the commits you are removing, teammates may be affected — Git revert examples is often safer on shared branches.


Shared Branches: Reset vs Revert

Scenario: the bad commit is already on main (or another branch your team pulls from).

If the commit has already been pushed and other people may be using it, normally use git revert instead of reset. Revert creates a new undo commit, while reset moves your branch backward and can require a force push to publish the change.

Use reset on a local feature branch you have not shared. Use revert on branches the whole team uses — see Git revert examples.


Recover After a Mistaken Reset

Scenario: you ran git reset --hard to the wrong place and want your previous latest commit back.

A reset can remove a commit from the normal branch history, but Git often remembers the old branch position temporarily in the reflog. List those recent positions:

bash
git reflog
output
4d23291 HEAD@{0}: reset: moving to HEAD~1
5a7c04b HEAD@{1}: commit: Third commit
4d23291 HEAD@{2}: commit: Second commit

Read it from the top: HEAD@{0} is where you are now (after the mistaken reset, on Second commit). HEAD@{1} is where you were one step ago — still on Third commit at 5a7c04b.

Find the reflog entry that contains the commit you want, then reset to that entry. In this example, it is HEAD@{1}:

bash
git reset --hard HEAD@{1}

That puts Third commit back at the tip of your branch. Reflog entries do not last forever — see Git reflog tutorial for a full recovery walkthrough.


Troubleshooting

Symptom Likely cause Fix
fatal: Cannot do hard reset with paths --hard with a file path Use git reset --hard without paths, or git restore --staged --worktree file for one file
fatal: Cannot do soft reset with paths --soft with a file path Use git reset --soft HEAD~1 at commit level, or git reset HEAD file to unstage
Reset removed the commit but changes are still staged You used --soft Expected — use --mixed or --hard if you wanted unstaged or deleted edits
git reset --hard HEAD but unrelated ?? files remain Reset usually does not remove arbitrary untracked files Run git clean -fd after git clean -n when you want them gone
Teammates broken after you reset a pushed branch Shared history was rewritten Prefer git revert on team branches

References

Git documentation: git-reset
Git documentation: git-revert
Git documentation: git-reflog
Git documentation: git-restore
Git documentation: git-fetch


Summary

git reset answers three common questions: how do I undo a commit, how do I unstage after git add, and how do I throw away uncommitted edits? The --soft, --mixed, and --hard flags control what survives when you move your branch to an older commit — changes remain staged (--soft), changes remain in your files but become unstaged (--mixed), or tracked changes are discarded (--hard).

git reset HEAD file and plain git reset only unstage; they do not remove commits. git reset --hard HEAD clears uncommitted work while staying on the same commit. After git fetch, git reset --hard origin/main makes your branch and tracked files match the server and drops local-only commits and edits; untracked files may remain.

On branches your team shares, prefer git revert over reset. If you reset to the wrong commit by mistake, git reflog lists recent branch positions — find the entry with the commit you want, then reset to that entry (for example HEAD@{1} in the walkthrough above).


Frequently Asked Questions

1. What is git reset?

git reset lets you move back to an earlier commit, or unstage a file without moving the branch. Depending on the mode, Git keeps changes staged, keeps them only in your files, or discards them completely.

2. What is the difference between git reset soft, mixed, and hard?

--soft removes the commit but keeps changes staged after git add. --mixed removes the commit and unstages those changes, but the edits stay in your files. --hard removes the commit and deletes those edits from your files too.

3. What does git reset --soft do?

git reset --soft HEAD~1 removes the last commit from your branch and leaves all of its changes still staged, ready to edit and commit again.

4. What does git reset --hard do?

git reset --hard makes your branch, staged files, and on-disk files match the commit you name. Local edits not in that commit are deleted. With git reset --hard HEAD the branch does not move — only your uncommitted work is cleared. Git may remove an untracked file if it blocks updating a tracked file.

5. What does git reset --mixed do?

git reset --mixed is the default when you omit a mode. It removes the commit and takes its changes out of the staging area, but the edits remain in your files as unstaged changes.

6. Is git reset safe to use?

Reset is safe on local-only branches when you pick the right mode. --hard discards tracked uncommitted changes. If you reset to an older commit such as HEAD~1, it also moves your branch backward and rewrites its local history. On shared branches, prefer git revert instead of reset followed by force push.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)