| Tested on | RHEL 10.2 (Coughlan) |
|---|---|
| Package | git 2.52.0 |
| Applies to | Any host with Git installed |
| Privilege | Normal user |
| Scope | Go back to an earlier commit or undo its effect — git revert on shared branches, git reset locally, detached inspection, single-file restore, merge revert, and reflog recovery pointers. Not a full reset, revert, or reflog tutorial. |
| Related guides | Git revert examples Git reset examples Git reflog tutorial Git discard changes Git remove commit |
Going back to a previous commit can mean four different things:
- undo a bad commit while keeping history;
- move your local branch back;
- temporarily inspect an older version;
- restore only one file from an older commit.
Git uses a different command for each goal — git revert, git reset, detached git switch, or git restore --source.
HEAD is the commit your current branch points to. HEAD~1 is one commit before it, and HEAD~2 is two commits before it. When you know which of the four goals you have, the decision table below sends you to the right section.
Choose the Right Way to Go Back
| Your goal | Safe on shared branches? | Command |
|---|---|---|
| Undo a commit that others may have pulled | Yes | git revert <commit> or git revert HEAD |
| Move your local branch back N commits | Local only | git reset --hard HEAD~N — see Git reset examples |
| Undo last commit but keep edits staged | Local only | git reset --soft HEAD~1 |
| Inspect an old snapshot without moving the branch | Yes (read-only) | git switch --detach <commit> |
| Restore one file from an older commit | Yes | git restore --source=<commit> -- file |
| Undo a merge commit on main | Usually yes | git revert -m 1 <merge-commit> |
| Recover a commit removed by reset | Local recovery | git reflog then git reset --hard <hash> |
If the problem is uncommitted edits in your working tree — not an old commit — see Git discard changes. For every git revert flag and range syntax, see Git revert examples.
Revert vs Reset in Plain Terms
Both commands can make your project look like an earlier commit, but they treat history differently:
git revertadds a new commit that reverses an older one. The bad commit stays in the log; a follow-up commit cancels its changes. Use this when the branch is shared or already pushed.git resetmakes an older commit become the latest commit on your local branch. Commits after that point drop off your branch (until reflog recovery). Use this mainly for local work you have not shared. In Git terms, it moves the branch pointer backward.
Example history before any fix:
A → B → C (HEAD, main)After git revert C on a shared branch:
A → B → C → D (HEAD)Commit C remains; D undoes its file changes.
After git reset --hard B locally:
A → B (HEAD)Commit C is no longer on the branch — recoverable via reflog for a while.
Undo a Commit That Was Already Pushed
Scenario: the latest commit on main introduced a bad change and teammates may have pulled it. Revert adds a safe undo commit instead of rewriting history.
Start from a branch with three commits; the latest is the one you want to cancel:
git log --oneline -3Sample output:
aa5020a Third commit - bad change
9dcc7e5 Second commit
c46b413 Initial commitRevert the tip commit. Git opens an editor for the revert message unless you pass --no-edit:
git revert HEAD --no-editGit creates a new commit that reverses the changes from HEAD:
[main 10e0380] Revert "Third commit - bad change"
1 file changed, 1 insertion(+), 1 deletion(-)The log now shows both the original commit and the revert on top:
git log --oneline -410e0380 Revert "Third commit - bad change"
aa5020a Third commit - bad change
9dcc7e5 Second commit
c46b413 Initial commitThe working tree matches the state before the bad commit — here app.txt again holds v2 from the second commit — but history stays intact for anyone who already pulled aa5020a.
To revert an older commit (not HEAD), name its hash from the log above — here, undo Second commit:
git revert 9dcc7e5 --no-editThat removes only the changes from that commit and leaves later commits in place. For range reverts, conflict handling, and --no-commit, see Git revert examples.
Move Your Branch Back Locally
Scenario: you have not pushed the last commit (or the branch is yours alone) and you want an earlier commit to become the latest commit on this branch.
Drop the last commit but keep changes staged
When to use this: you committed too early and will edit the message or files before committing again.
git reset --soft HEAD~1The commit disappears from the branch, but Git keeps its changes staged:
git status -sb## main
A b.txtgit log --oneline now shows the previous commit as the branch tip; the removed commit’s changes remain staged in b.txt.
Drop the last commit and discard its changes
When to use this: the commit and its edits should both go away on a local branch.
--hard does not only remove the last commit’s changes. Any other staged or unstaged changes in tracked files can also be overwritten, so check git status first:
git status -sbWhen the tree is clean (or you accept losing uncommitted tracked edits), reset back one commit:
git reset --hard HEAD~1Git moves HEAD back one commit and resets tracked files to match:
HEAD is now at 0dfe067 v2Confirm the branch tip no longer lists the removed commit:
git log --oneline0dfe067 v2
de2bad3 v1The former tip commit (v3) is off the branch. --hard deletes those file changes from disk — not recoverable with git restore, though reflog may still list the old tip for a time.
For --mixed, path-only reset, matching origin/main after fetch, and reset-versus-revert detail, see Git reset examples.
Inspect an Old Commit Without Changing the Branch
Scenario: you want to read code or run tests at an older snapshot without moving main.
Check out that commit in detached HEAD mode:
git switch --detach HEAD~1Git prints where you landed:
HEAD is now at de2bad3 v1git status -sb confirms you are not on a branch:
git status -sb## HEAD (no branch)Commits you make here are not on main until you attach them to a branch. If you decide to keep work created while detached, create a branch before switching away:
git switch -c test-old-versionThe new branch starts at the commit you are currently viewing. Any commits you make from that point will then belong to test-old-version. To return to normal development on main without keeping detached work:
git switch mainOlder Git versions use git checkout <commit> for the same detached state; git switch --detach is the modern spelling.
Restore One File From an Older Commit
Scenario: only one file should match an older commit; the rest of the branch stays as-is.
With uncommitted edits in app.txt, replace your current file with the version stored one commit earlier:
git restore --source=HEAD~1 app.txtThe file on disk now matches that older commit; other files and commits are unchanged:
cat app.txtv2Because the branch tip still has the newer content, status should show the file as modified:
git status -sb## main
M app.txtGit shows app.txt as modified because your branch tip still has the newer content — you have a local edit ready to commit or discard. git restore --source changes the file in your working tree; it does not move the branch or remove any commits. To copy from a specific hash, replace HEAD~1 with that commit id.
Revert a Merge Commit
Scenario: a merge to main should be undone without rewriting history.
After a non-fast-forward merge, find the merge commit hash:
git log --oneline --graph -5* a39da0e Merge branch 'feature'
|\
| * f22cbb0 feature work
* | d96cddb main moves forward
|/
* 543e74a main initRevert the merge, keeping the first parent (usually main before the merge):
git revert -m 1 a39da0e --no-edit[main f8e252c] Revert "Merge branch 'feature'"
1 file changed, 1 deletion(-)
delete mode 100644 feat.txtAfter the revert, feat.txt from the feature branch is gone but the merge commit remains:
git log --oneline --graph -5* f8e252c Revert "Merge branch 'feature'"
* a39da0e Merge branch 'feature'
|\
| * f22cbb0 feature work
* | d96cddb main moves forward
|/
* 543e74a main initIn a typical feature → main merge, -m 1 means keep the main side and undo the changes that the merge brought in from feature. Git calls that first parent the mainline. Unusual parent choices and merge-revert side effects — including how later merges treat reverted changes — are covered in Git revert examples.
Recover a Commit After Reset
Scenario: you ran git reset --hard and need the old tip back.
Reflog records where HEAD has been, including commits no longer on the branch:
git reflog -50dfe067 HEAD@{0}: reset: moving to HEAD~1
8c861a7 HEAD@{1}: commit: v3
0dfe067 HEAD@{2}: commit: v2
de2bad3 HEAD@{3}: commit: v1You are now at 0dfe067 — the same tip shown after git reset --hard HEAD~1 above. In this example, 8c861a7 is the commit recorded just before the reset and is the old branch tip you want to recover. If that hash is the one you need, move your current branch there:
git reset --hard 8c861a7Reflog entries expire eventually; on shared branches, prefer git revert so teammates are not surprised by a force push. Full walkthrough: Git reflog tutorial.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
error: commit … is a merge but no -m option given |
Reverting a merge without naming a parent | Run git revert -m 1 <merge-commit> (usually parent 1 is main) |
| Revert stops with conflict markers | The reverse patch does not apply cleanly | Resolve files, git add them, then git revert --continue |
git reset --hard removed work you still need |
Reset moved the branch and cleared files | Find the old tip in git reflog, then git reset --hard <hash> |
| Detached HEAD warning after old commit | You checked out a commit, not a branch | git switch main (or your branch name) before new commits |
| Teammates reject your push after reset | Reset rewrote history they already pulled | Use git revert instead, or coordinate a force push only when the branch is truly yours |
| Only one file should roll back | Whole-commit revert is too broad | git restore --source=<commit> -- path |
References
- Git revert documentation
- Git reset documentation
- Git restore documentation
- Git switch documentation
- Git reflog documentation
Summary
Going back to a previous commit starts with what you need: cancel a change on a shared branch, move a local branch back, inspect history, or fix one file. The decision table maps each goal to git revert, git reset, detached git switch, or git restore --source.
On branches others pull from, git revert is the default safe path — it adds an undo commit and leaves the original in the log. Local-only mistakes on commits you have not shared are where git reset is appropriate; choose --soft, --mixed, or --hard depending on whether you want to keep the changes. If you reset too far, check git reflog before you panic.
Detached checkout lets you read or test an old tree without moving main. Create a branch with git switch -c if you want to keep commits made while detached. Single-file rollback uses git restore --source so the rest of the branch stays put. Merge undos on a typical feature → main flow use git revert -m 1 to keep the main side.
When you outgrow this overview, the linked guides cover revert ranges, every reset mode, and full reflog recovery — but the split to remember is revert for shared history, reset when you want a local branch to go back to an earlier commit, and restore when only one path should change.

