Thuta Learning
IntermediateDevOps & Toolsintermediate

Revert vs Reset: Undoing Safely

What you'll walk away with

  • Explain the core ideas behind Revert vs Reset: Undoing Safely
  • Read the diagram and trace how state or data flows through the Git/GitHub workflow
  • Decide how this applies to your own project or team

Build the mental model

`git reset` and `git revert` both undo changes, but take opposite approaches to history — reset moves the branch pointer backward and shortens history, while revert never touches existing commits and instead adds a new one.

ToolWhat happens to history
resetMoves branch pointer backward; later commits become detached, history shortens
revertAdds a new commit undoing another; nothing existing is rewritten, history grows

If a bad commit is private and unpushed, reset is fine. Once pushed and pulled by a teammate, reset plus force-push rewrites their history out from under them — revert is the safe choice instead.

  • `--soft` — leaves changes staged, ready to recommit
  • `--mixed` (default) — unstages them but leaves them in your working directory
  • `--hard` — discards everything completely, no undo button
Reset
Moves the current branch pointer to a different commit, optionally changing the staging area and working directory too — rewrites local history.
Revert
Creates a new commit whose changes undo an existing commit, without altering any commit that already exists — safe for shared history.
text
RESET (SHORTENS HISTORY) VS REVERT (ADDS COMMIT)
------------------------------------------------
RESET (SHORTENS HISTORY) VS REVERT (ADDS COMMIT)
----------------------------------------------------
Before:      C1 --- C2 --- C3   <- main

git reset --hard C1:
             C1   <- main        (C2, C3 detached, shorter)

git revert C3:
             C1 --- C2 --- C3 --- C4  <- main
                                  (C4 undoes C3, longer)

Connect it to a real scenario

For a bad commit that's already shared, `git revert <hash>` (with `--no-edit`) creates a new commit reversing its changes.

Comparing `git log --oneline` before and after shows the original commit still present, with the new revert commit on top undoing its effect.

ModeWhere changes end up
--softStaged, ready to recommit ('Changes to be committed')
HEAD (mixed)Unstaged but still in your files ('Changes not staged for commit')

git reset --hard destroys uncommitted work

`--hard` discards staged AND unstaged changes permanently — there's no straightforward way to get them back. Always run `git status` before a `--hard` reset to see exactly what you're about to lose.

Try the working example

bash
git init -q -b main
git config user.name "Thuta Learner"
git config user.email "learner@example.com"

# --- Part 1: revert (safe on shared history) ---
mkdir part1 && cd part1
git init -q -b main
git config user.name "Thuta Learner"
git config user.email "learner@example.com"

export GIT_AUTHOR_NAME="Thuta Learner"
export GIT_AUTHOR_EMAIL="learner@example.com"
export GIT_COMMITTER_NAME="Thuta Learner"
export GIT_COMMITTER_EMAIL="learner@example.com"

echo "body { color: black; }" > style.css
git add style.css
GIT_AUTHOR_DATE="2026-01-04T09:00:00" GIT_COMMITTER_DATE="2026-01-04T09:00:00"   git commit -m "Add base styles"

echo "body { color: red; }" >> style.css
git add style.css
GIT_AUTHOR_DATE="2026-01-04T10:00:00" GIT_COMMITTER_DATE="2026-01-04T10:00:00"   git commit -m "Change text to red (bad idea)"
BAD_COMMIT=$(git rev-parse HEAD)

git log --oneline
GIT_AUTHOR_DATE="2026-01-04T11:00:00" GIT_COMMITTER_DATE="2026-01-04T11:00:00"   git revert --no-edit $BAD_COMMIT
git log --oneline
cat style.css
cd ..

# --- Part 2: reset --soft / mixed (separate fresh repo) ---
mkdir part2 && cd part2
git init -q -b main
git config user.name "Thuta Learner"
git config user.email "learner@example.com"

echo "console.log('start');" > app.js
git add app.js
GIT_AUTHOR_DATE="2026-01-05T09:00:00" GIT_COMMITTER_DATE="2026-01-05T09:00:00"   git commit -m "Initial commit"

echo "console.log('oops typo comit');" >> app.js
git add app.js
GIT_AUTHOR_DATE="2026-01-05T10:00:00" GIT_COMMITTER_DATE="2026-01-05T10:00:00"   git commit -m "Bad commit with typo"

git log --oneline
git reset --soft HEAD~1
git status

git reset HEAD
git status
You should see
$ git log --oneline
23ae617 Change text to red (bad idea)
a43964a Add base styles

$ git revert --no-edit 23ae617
[main b1d22c5] Revert "Change text to red (bad idea)"
 Date: Sun Jan 4 11:00:00 2026 +0900
 1 file changed, 1 deletion(-)

$ git log --oneline
b1d22c5 Revert "Change text to red (bad idea)"
23ae617 Change text to red (bad idea)
a43964a Add base styles

$ cat style.css
body { color: black; }

--- Part 2: reset ---

$ git log --oneline
e379b53 Bad commit with typo
1e07e9f Initial commit

$ git reset --soft HEAD~1

$ git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   app.js

$ git reset HEAD
Unstaged changes after reset:
M	app.js

$ git status
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   app.js

no changes added to commit (use "git add" and/or "git commit -a")

5-minute try-it

Make two commits in a scratch repo. Use `git revert` to undo the second one and confirm with `git log --oneline` that history grew rather than shrank. Then, in a separate scratch repo, make a commit you regret, and try all three reset modes in sequence (`--soft`, then unstage with plain `git reset HEAD`) — after each, run `git status` and note exactly what changed.

One important caution

Using `git reset --hard` (or force-pushing after any reset) on commits that have already been pushed and pulled by others rewrites shared history and breaks their clones.

`git reset --hard` deletes uncommitted staged and unstaged changes with no confirmation and no easy recovery — always check `git status` first.

Quick Check: Undoing a Shared Commit

You already pushed a bad commit and teammates have pulled it. What should you use to undo it?

Git Documentation: git-revertGit & GitHub

Easy traps

  • Using `git reset --hard` (or force-pushing after any reset) on commits that have already been pushed and pulled by others rewrites shared history and breaks their clones.
  • `git reset --hard` deletes uncommitted staged and unstaged changes with no confirmation and no easy recovery — always check `git status` first.
  • Always run `git status` before any destructive or history-rewriting command, to know exactly what state you're in.

Exercise

Make two commits in a scratch repo. Use `git revert` to undo the second one and confirm with `git log --oneline` that history grew rather than shrank. Then, in a separate scratch repo, make a commit you regret, and try all three reset modes in sequence (`--soft`, then unstage with plain `git reset HEAD`) — after each, run `git status` and note exactly what changed.

You'll know it worked when: $ git log --oneline 23ae617 Change text to red (bad idea) a43964a Add base styles $ git revert --no-edit 23ae617 [main b1d22c5] Revert "Change text to red (bad idea)" Date: Sun Jan 4 11:00:00 2026 +0900 1 file changed, 1 deletion(-) $ git log --oneline b1d22c5 Revert "Change text to red (bad idea)" 23ae617 Change text to red (bad idea) a43964a Add base styles $ cat style.css body { color: black; } --- Part 2: reset --- $ git log --oneline e379b53 Bad commit with typo 1e07e9f Initial commit $ git reset --soft HEAD~1 $ git status On branch main Changes to be committed: (use "git restore --staged <file>..." to unstage) modified: app.js $ git reset HEAD Unstaged changes after reset: M app.js $ git status On branch main Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: app.js no changes added to commit (use "git add" and/or "git commit -a")

Revert vs Reset: Undoing Safely | Thuta Learning