Git Reset: Undo Commits With --soft and --hard
Table of Contents
If you’ve ever made a commit and immediately thought “noooope”, then git reset is what you need. It’s one of the most useful Git commands for undoing work, but it’s also one of the easiest to misuse. The --soft version is usually safe and predictable. The --hard version is powerful and can absolutely nuke your local changes.
All the content from our Boot.dev courses are available for free here on the blog. This one is the “Reset” chapter of Learn Git. If you want to try the far more immersive version of the course, do check it out!
What Does Git Reset Actually Change?
git reset can affect three things:
HEAD(what commit your branch points to)- The index (your staged changes)
- The working tree (your files on disk)
Different flags change different layers.
If you want broader branch-history context first, start with Git Branching and Git Rebase.
How Do You Undo a Commit but Keep Your Changes?
Use the --soft flag.
git reset --soft COMMITISH
This moves your branch pointer back to COMMITISH (which can be a commit hash, branch tip, tag, etc), but your reverted commit’s file changes stay staged.
That’s perfect when your last commit message is bad, or you want to squash/rework the commit before pushing. The typical flow is:
git log --oneline
git reset --soft <target-commit>
git status
After the reset, git log no longer shows the removed commit, and git status shows those changes as staged and ready to recommit.
What Does Git Reset --hard Do?
The --hard flag is much more heavy-handed:
git reset --hard COMMITISH
It resets all three layers (HEAD, index, and working tree) to the target commit. If local changes are only in your working tree or staging area, they’re gone.
This is useful when your branch is a mess and you intentionally want a clean rollback.
It’s not useful when you might want your work back later.
Why Is Git Reset --hard Considered Dangerous?
Because it can permanently discard local changes.
If a file change was never committed anywhere, and you wipe it with --hard, Git usually can’t help you recover it.
[!warning]
git reset --hardis fast, clean, and brutal. Run it only when you are certain you don’t need the discarded work.
When in doubt, make a backup branch first:
git switch -c backup-before-reset
Then go do destructive stuff on the original branch.
When Should You Use --soft vs --hard?
Use --soft when you want to rewrite recent commits but keep the code changes.
Use --hard when you want your branch and files to exactly match an older commit and you intentionally want to discard local work.
- If your goal is to combine branch histories without merge commits, that’s not reset territory — use Git Rebase.
- If your goal is to combine completed branch work, use Git Merge.
- If your goal is to create a new commit that is the inverse of a previous commit use
git revert.
How Do You Reset to a Specific Commit?
Find the target hash in git log, then reset to it:
git log --oneline
git reset --hard a1b2c3d
You can use full hashes or short hashes as long as the short one is unambiguous.
If you’re working solo on your feature branch, this is usually straightforward. If you’re rewriting commits that teammates already pulled, coordinate first.
If you want to learn about advanced undo workflows (including options after mistakes), our Learn Git 2 course goes even deeper.
Frequently Asked Questions
What does git reset do?
git reset moves your current branch pointer and can also update the staging area and working directory depending on the flags you use.
What is git reset --soft used for?
git reset --soft moves HEAD to an older commit but keeps your changes staged so you can recommit them.
What is git reset --hard used for?
git reset --hard moves HEAD and also forces your index and working directory to match the target commit, discarding local changes.
Is git reset --hard dangerous?
Yes. It can permanently delete uncommitted work, so you should use it carefully.
Should I use git reset on shared branches?
Avoid rewriting shared branch history unless your team explicitly agrees on that workflow.
Related Articles
Git Rebase: Keep History Linear Without Merge Commits
Apr 01, 2026 by ThePrimeagen - Ex-Netflix engineer, NeoVim ricer, and Git rebaser
If git merge feels noisy, that’s because it can be. Lots of noisy merge commits make history harder to scan, especially when you’re just trying to understand what actually changed. git rebase takes a different approach: instead of creating a merge commit, it replays your branch commits on top of the latest base commit so your history stays clean and linear.
Git Remote: Add Origin, Fetch, and Merge Remote Branches
Apr 01, 2026 by ThePrimeagen - Ex-Netflix engineer, NeoVim ricer, and Git rebaser
Git is distributed, which means your repo and my repo can both be valid “sources of truth”. In practice, teams usually pick one remote as the shared source (often GitHub) and sync local work against it… but that’s just a convention. Let’s dig into how syncing Git repositories to GitHub works, and how that’s just one way to work with Git.
GitHub + Git: Push, Pull, and Pull Requests
Apr 01, 2026 by ThePrimeagen - Ex-Netflix engineer, NeoVim ricer, and Git rebaser
Git is the open-source command-line version control tool, and GitHub is basically just a collaboration on top of it. You can absolutely use Git without GitHub, but for most teams, GitHub (or something similar) is where a lot of the collaboration happens. Linus Torvalds created Git, and… well we don’t talk about who owns GitHub these days…
Gitignore Patterns: What to Ignore and Why
Apr 01, 2026 by ThePrimeagen - Ex-Netflix engineer, NeoVim ricer, and Git rebaser
Many devs adopt the git add . (yolo, just yeet it all into the commit) workflow. It is fast, simple, and usually correct. But eventually you’ll have files in your repo directory that should never be committed: secrets, generated artifacts, dependency folders, and random local junk. That’s what .gitignore is for.