Programming and IT

git reset

git reset moves the current branch pointer to another commit. How much your work suffers depends on one flag — `--soft` touches nothing but history, `--mixed` clears the index, `--hard` rewrites the files on disk. The third mode is the one Git command that destroys unsaved work for good.

Updated
In this article

The three areas the command moves#

To stop mixing up the modes, keep three layers in mind.

  1. History — where the branch pointer is, and HEAD with it.
  2. Index — the draft of the next commit, whatever went through git add.
  3. Working copy — the files in the folder that are open in your editor.

The command always moves the first layer. The rest depends on the flag:

Flag History Index Files on disk
--soft moves untouched untouched
--mixed (default) moves reset untouched
--hard moves reset overwritten

The first two modes are safe: your changes stay in the files; only the way Git sees them changes. The third wipes edits that are not in any commit. Nothing can bring them back — not the reflog, not the trash.

Syntax#

git reset --soft HEAD~1      # remove the last commit, keep changes staged
git reset HEAD~1             # same, but changes stay only in the files
git reset --hard HEAD~1      # remove the commit and erase its changes entirely
git reset report.md          # unstage one file (edits are safe)
git reset --hard origin/main # make the branch match the server branch
git reset --merge            # abort a merge in progress, keeping other edits

HEAD~1 means "the parent of the current commit", HEAD~3 means "three commits back". You can put any hash in its place.

Example: redo the last commit#

You committed too early: forgot a file, wrote a poor message, left a debug line in. Remove the commit while keeping all the work staged:

$ git reset --soft HEAD~1
$ git status -sb
## main
M  parser.py
A  tests/test_parser.py
$ git add utils/dates.py
$ git commit -m "Parse dates, with a test"

History is one commit shorter, and nothing was lost. When you only need to add a file or fix the message, there is a shorter route — git commit --amend, covered among the ways to undo a commit.

Example: unstage something you did not mean to add#

$ git add .
$ git status -s
A  .env
M  app.py
$ git reset .env
$ git status -s
M  app.py
?? .env

The file left the commit draft and became untracked again; its contents on disk did not change. The modern synonym for this action is git restore --staged .env; it reads more clearly and cannot accidentally move a branch. To keep such files out for good, add them to .gitignore.

The dangerous mode: --hard#

$ git status -s
 M invoice.py
$ git reset --hard HEAD
HEAD is now at 7c4e1a2 Add ledger report
$ git status -s
$

The edit in invoice.py is destroyed. Not "moved", not "stashed" — it no longer exists anywhere, because it never made it into Git's object database. The same happens to every unsaved change in every file if you run the command without a path.

Three habits that remove nearly all the risk:

  • before --hard, always look at git status -s — if there are lines, stash them first (git stash push -m "just in case", see git stash) or commit them;
  • to roll back a single file, use git restore <file> rather than resetting the whole branch;
  • do not paste --hard from the internet without looking: most of the time where people recommend it, --soft or --mixed would do.

What to do if commits have disappeared#

Here is the good news: commits, unlike unsaved edits, can almost always be brought back. They were not deleted; nothing points to them any more.

$ git reflog
7c4e1a2 HEAD@{0}: reset: moving to HEAD~2
91b0d3f HEAD@{1}: commit: Export to accounting
5ee8a41 HEAD@{2}: commit: Reconcile stock
$ git reset --hard 91b0d3f
HEAD is now at 91b0d3f Export to accounting

The reflog keeps every position of HEAD — several weeks by default, and entries for commits that became unreachable live for at least thirty days. Find the line from before the unlucky reset and move the branch back to it. If you would rather not move the current branch, create a new one at the commit you found: git switch -c rescue 91b0d3f.

reset or revert#

The difference is who will see the undo.

git reset removes a commit from the branch: it is no longer in the history. That is fine while the commit lives only on your machine. After it has been pushed, such a reset needs a forced push, and git push --force wipes whatever teammates have pushed in the meantime — if you cannot avoid it, use git push --force-with-lease, which refuses to work when something unexpected has appeared on the server.

git revert does not touch existing history; it adds a new commit with the opposite changes. It goes out with an ordinary git push, gets in nobody's way and is therefore the only right way to undo something that has already reached a shared branch. Remember the pair: private — reset, shared — revert. The same line runs through rebasing.

Step-by-step plan

  1. See what is unsavedgit status -s before any reset: the lines in the output are what can disappear.
  2. Choose the mode--soft to redo a commit, --mixed to rebuild the index, --hard only with a clean status.
  3. Pick the targetHEAD~1, a commit hash or origin/main; check your choice with git log --oneline.
  4. Run it and checkAfter the reset, git status -s and git log --oneline again: the expected tip and the expected files.
  5. If you missedgit reflog, find the line before the reset and go back — commits are recoverable, unsaved edits are not.

Start learning this in your own space

The plan goes into your repository: tick off stages, keep notes — the change history shows how far you have come.

Start the plan

Check yourself

1.Which git reset mode destroys uncommitted edits in your files?

2.A commit has already been pushed to a shared branch and must be undone. What do you choose?

3.What does git reset --soft HEAD~1 do?

4.You reset the branch two commits back by mistake and need those commits. Where do you start?

Sources

Was this helpful?

More articles

Programming and IT git checkout Historically `git checkout` does several unrelated things at once: it moves your working copy to another branch, creates a branch and pulls a single file out of a commit. Because it was so overloaded, Git 2.23 added two separate commands — `switch` and `restore`. Here are both sides: the old way and its modern replacement. Programming and IT How to undo a commit The question "how do I undo a commit" actually hides four different ones: fix the last commit, take it back and redo it, erase several, or cancel an old commit in the middle of history. The answer depends above all on one thing — whether anyone besides you has seen that commit. Programming and IT The .gitignore file Every project has files that do not belong in the repository — caches, build folders, editor settings, local passwords. You keep the list of such files in .gitignore, and Git stops offering to add them. Programming and IT Python: reading a file Working with a file takes three steps: open it, read or write, close it. You are better off not closing it by hand — that is what the `with` statement is for. And the parameter people forget most is the encoding: without it, the same code reads a file differently on different machines. Programming and IT git commit A commit is a saved state of your project with an author, a timestamp and a message. git commit takes what you selected beforehand with git add and writes it into history as one indivisible piece. Programming and IT git merge Merging brings work from one branch into another. git merge takes the branch you name and joins its history with the current one — sometimes by just moving a pointer, sometimes by creating a separate commit, and sometimes by stopping and asking for help.

More solutions