Mistakes are inevitable. You will commit the wrong files, stage debug code, or realize halfway through that your approach is wrong. The good news is that Git makes almost everything reversible. git restore and git reset are the two tools you need to undo almost any mistake at any stage of the workflow.
The key to using them confidently is understanding where each one operates. Git manages your files across three areas, and each undo command targets a different combination:
Rule of thumb: use git restore to undo changes to files (working tree and staging area). Use git reset to undo commits (move the branch pointer back).
git restore: Discarding working tree changes
git restore replaces file contents in your working tree or staging area with a version from another source (usually the last commit). It does not move the branch pointer or change commit history.
Discard unstaged changes
The most common use: you modified a file, decided the changes are wrong, and want to go back to the committed version:
Terminal
$ git restore src/App.tsx
Hover over each part to see what it does
To discard all unstaged changes at once:
Terminal
# Discard ALL unstaged changes in the working tree
$ git restore .
$ git status
nothing to commit, working tree clean
Warning:git restore permanently discards uncommitted changes. There is no undo for this operation. If you are not sure, stash the changes first with git stash so you can recover them later.
Unstage files
You staged a file by accident with git add and want to move it back to the unstaged section. Use the --staged flag:
Terminal
$ git restore --staged src/debug.ts
Hover over each part to see what it does
The file contents on disk are not changed. The file simply moves from "Changes to be committed" back to the working tree.
Restore from a specific commit
You can restore a file to its state in any previous commit using the --source flag. This is perfect for recovering deleted files or rolling back a single file without affecting anything else:
Terminal
# Restore a file to its state in a specific commit
While git restore works on individual files, git reset works on commits. It moves the branch pointer to a different commit, effectively "undoing" one or more commits. What happens to the files from those commits depends on the mode you choose.
Here is the starting point — a commit that you want to undo:
Komitly - Commit Graph (before reset)
e7b8c9dAdd broken feature (oops)main
d6a7b8cUpdate navigation styles
c5d6e7fAdd user authentication
b4c5d6eInitial commit
--soft: Keep everything staged
git reset --soft moves the branch pointer back but keeps all changes from the undone commit in the staging area. This is the gentlest form of reset — nothing is lost:
Terminal
$ git reset --soft HEAD~1
Hover over each part to see what it does
After the soft reset, the branch pointer has moved back but your changes are still staged and ready to be re-committed:
Komitly - After soft reset
d6a7b8cUpdate navigation stylesmain
c5d6e7fAdd user authentication
b4c5d6eInitial commit
Komitly - Changes still staged
Staged (2)
Asrc/features/broken.tsx
Msrc/App.tsx
Changes (0)
Working tree clean
--mixed: Keep changes unstaged (default)
git reset --mixed (or simply git reset with no flag) moves the branch pointer back and clears the staging area, but keeps the changes in your working tree. You can review, edit, and selectively re-stage:
Terminal
# Undo the last commit, keep changes unstaged (default)
$ git reset HEAD~1
$ git status
Changes not staged for commit:
modified: src/App.tsx
Untracked files:
src/features/broken.tsx
--hard: Discard everything
git reset --hard moves the branch pointer back and discards all changes — staged and unstaged. This is the nuclear option:
Terminal
# ⚠ Undo the last commit AND discard all changes
$ git reset --hard HEAD~1
HEAD is now at d6a7b8c Update navigation styles
$ git status
nothing to commit, working tree clean
Warning:git reset --hard is the only reset mode that can permanently destroy uncommitted work. Use it only when you are certain you do not need the changes. If you are unsure, start with --soft — you can always discard changes later.
Soft, mixed, and hard explained
Here is the complete comparison. All three modes move the branch pointer — the difference is what happens to the staging area and working tree:
Reset mode comparison
Mode Branch pointer Staging area Working tree
────────── ──────────────── ────────────── ─────────────
--soft ✓ Moves back ✗ Untouched ✗ Untouched
--mixed ✓ Moves back ✓ Cleared ✗ Untouched
--hard ✓ Moves back ✓ Cleared ✓ Cleared
⚠ DESTRUCTIVE
--soft — Use when you want to re-do a commit. The changes stay staged so you can adjust and commit again immediately.
--mixed — Use when you want to reorganize changes. The files are unstaged but still on disk, so you can selectively re-stage different parts into new commits.
--hard — Use when you want a clean slate. Everything is discarded and you are back to the exact state of the target commit.
The reflog: your ultimate safety net
Even after a --hard reset, the "lost" commits are not actually deleted — they still exist in Git's object database. The reflog records every movement of HEAD, so you can find and recover any commit:
Terminal
# Oops — git reset --hard went too far
# The reflog records every HEAD movement
$ git reflog
d6a7b8c (HEAD -> main) HEAD@{0}: reset: moving to HEAD~1
The reflog keeps entries for at least 90 days by default. As long as you act within that window, almost nothing is truly permanent in Git.
Common undo recipes
Here are the most common "I need to undo this" scenarios and the exact command for each one:
Terminal
$ git reset --soft HEAD~1
# Changes are staged — edit, re-stage, and commit again
Terminal
$ git reset HEAD
# All files are unstaged but still modified on disk
Terminal
$ git reset --hard HEAD~1
HEAD is now at d6a7b8c Update navigation styles
Terminal
$ git restore src/App.tsx
# src/App.tsx is back to its last committed state
Terminal
$ git restore --source HEAD src/config.json
# File is back on disk exactly as it was in the last commit
Terminal
$ git revert HEAD
[main a1b2c3d] Revert "Add broken feature"
2 files changed, 0 insertions(+), 45 deletions(-)
# Safe to push — history is preserved, change is undone
$ git push
Reset vs. revert:git reset rewrites history — only use it on commits that have not been pushed. git revert creates a new commit that undoes a previous one — safe to use on shared branches because it preserves history.
Undoing in Komitly
Komitly provides several visual tools that make undoing mistakes fast and safe — without memorizing flags or worrying about destructive operations.
The Undo panel
Every Git operation you perform in Komitly is recorded in the Undo panel (accessible from the Activity Rail). It shows a chronological list of your recent operations — commits, resets, rebases, merges, cherry-picks, and stashes — each with a one-click Undo button. Immediately after an operation, a toast notification appears with a quick undo link.
Context menu on commits
Right-click any commit in the graph and you will see options to Reset (soft, mixed, or hard), Revert, and Cherry-pick. The reset option lets you choose the mode from a submenu, so you always know which one you are using.
Discard changes per file
In the staging panel, right-click any modified file and select Discard Changes. Komitly asks for confirmation before discarding — the visual equivalent of git restore <file>, but with a safety prompt.
Discard by hunk or line
Open the diff viewer for a file and you can discard changes at the hunk or individual line level. This is far more precise than discarding the entire file — you can keep the good changes and remove just the parts you do not want.
The Reflog panel
Komitly gives you visual access to the Reflog from the Activity Rail. Instead of parsing terminal output, you see a clean list of every HEAD movement with timestamps and descriptions. Click any entry to navigate to that state, making recovery from accidental resets straightforward.
In Komitly, the Undo panel catches you before you need the Reflog. Every operation has a one-click undo, and destructive actions always show a confirmation dialog first. Combined with hunk-level discard in the diff viewer, you can undo precisely what you need without affecting the rest of your work.
Summary
Undoing mistakes is a core skill in Git. With git restore and git reset, you can reverse almost any change at any stage. Here is a quick recap:
git restore <file> — Discard unstaged changes to a file (destructive).
git restore --staged <file> — Unstage a file without modifying its contents.
git restore --source <commit> <file> — Restore a file to its state in a specific commit.
git reset --hard HEAD~1 — Undo a commit and discard all changes (destructive).
git revert HEAD — Create a new commit that undoes the last one. Safe for shared branches.
git reflog — Find and recover commits after an accidental reset.
In Komitly, the Undo panel provides one-click undo for every operation, the commit graph context menu offers visual reset and revert, and the diff viewer supports hunk-level and line-level discard.
With these tools, mistakes are never permanent. Next, learn how to stage with surgical precision using line-level staging.