How to Undo Git Commits with reset, revert, and rebase
In my previous posts “GIT in 10 minutes” and Collaboration in GitHub, I have covered the basics of Git setup, a few workflow commands to get started using Git (version control system), and collaborative work, including operations with forks and pull requests. As promised, I will go into more detail about working with Git repositories. Herein I will focus on reverting your changes. Sometimes it’s good to step back and think about something different, right?
Git Fork-and-Pull-Request Workflow Setup
I assume here the following setup:
- We work on a GitHub project with other team members working together on an “upstream” repository.
- We have forked this upstream repository and named our fork “origin.”
- We have a local copy of the origin repository (which we sync regularly with the upstream repository)
- This local copy of the origin repository (forked one) is changed with the next code contributions.
- We commit our local changes to the origin forked repository.
- In the forked repository, we create a Pull Request to the upstream repository.
The workflow can be described with the following example.
Cloning your forked repo locally
git clone <tocken>@github.com/<user/<repo>
cd repo
Add upstream
git remote add upstream https://githubcom/<upstream_user>/<repo>
Working on your branch
git checkout master
git checkout -b your_branch_name
git fetch upstream
Make changes
git add .
git commit -m' message'
# use -u for your first push
git push -u origin your_branch_name
Possibly, we revert our changes
We might reconsider the changes at this step and try to revert the commit.
Pull Request
Next, we will go to your GitHub forked repository and click the “Compare & Review” green button beside the branch button. Or, on your fork page, press the “Pull Request” button.
We might want to do the changes after PR
At this stage, we cannot just revert commit so quickly. We will need to proceed with a new PR.
Update Frequently with git pull upstream master
It’s important to mention that when working with the remote repository, pulling the most recent changes from the master is essential. Before that, ensure that the upstream remote is set up (with git remote -v).
git pull upstream master
git reset vs git revert: When to Use Each
Sometimes we do not want to keep changes that are already committed. Let’s consider the following cases of when and how we revert or disregard these changes.
git reset is a Git command that moves the current branch tip to an earlier commit, discarding the commits after it (history-rewriting). git revert is a Git command that creates a new commit which undoes the changes of a previous commit while preserving history (non-destructive). The two main ways to undo committed changes:
| Command | What it does | History | Safe on shared branches? |
|---|---|---|---|
git reset [hash] |
Moves the branch tip back, dropping later commits | Rewrites history | No — only for local, unpushed commits |
git revert [hash] |
Adds a new commit that cancels a previous one | Preserves history | Yes — preferred for pushed commits |
Reverting a Commit with git revert [hash]
GitHub Docs “Reverting a commit” state that reverting a commit is a commit, and it is good practice reverting commits “order from newest to oldest.” Use “git log” to check the commit history and hashes of commits.
git log
This output will include the hash strings you can use to revert a commit. The identifier string is located to the right of the word “commit.”
commit ec427b9199866d88dd67db744012264c3bfb4b62
Author: Elena Daehnhardt my@mail.com
Date: Mon Jul 11 13:28:22 2022 +0200
+_posts/2022-07-11-tf-nlp.md
commit 3fb88a4c6aa122f9fdd0a38adec3bb46fd9f2d67
Author: Elena Daehnhardt my@mail.com
Date: Mon Jul 11 13:27:52 2022 +0200
+forks update, fetch upstream
A hash code identifies each commit and can be used to manage or see the related commit. With git checkout, we can analyze the commit with the defined hash.
git checkout [hash]
With git revert, we revert to the previous commit with the defined hash code.
git revert [hash]
Resetting a Commit with git reset [hash]
git reset sets a repository back to a specific commit.
git reset [hash]
Interestingly, we can also undo the reset of a commit when we know the commit hash of the reset.
It is essential to mention that we should avoid changing the commit history when working on remote repositories, especially when collaborating with others. When the commit was not yet uploaded into a remote server, we can reset to a previous commit before any changes are made using the following options.
Option 1. cleans up all your changes which are lost. This option will altogether remove any changes from the local repository.
git reset --hard [hash]
To update the remote repository after the hard reset, you can do a force push. Herein “origin” is the name of remote, and “main” is its branch name.
git push -f origin main
Option 2. resets to the commit, and your changes are preserved with stashes (first saved, then hard reset, and finally with pop).
git stash
git reset --hard [hash]
git stash pop
Option 3. resets the commit history but preserves your files.
git reset --soft [hash]
Option 4. default option unstaging the changes. This option will reset back to the commit with the defined hash number.
git reset --mixed [hash]
Option 5. resetting to the latest commit just before the HEAD.
git reset --mixed HEAD~
Option 6. When things go totally wrong, we can use the hard reset option.
git fetch origin
git checkout main
git reset --hard origin/main
git clean -d --force
Reverting Pushed Changes on a Remote Repository
Prefer git revert when undoing changes already pushed to a remote repository. git revert keeps the history intact for other people working on the repository, whereas git reset followed by a force push rewrites shared history and can break their clones.
Rewriting History with git rebase –interactive
git rebase is a Git command that reapplies commits onto a new base, letting you edit, delete, reorder, or squash commits to rewrite branch history. Use git rebase with caution, because it rewrites history.
The interactive form lets you work with the latest five commits in your current branch.
git rebase --interactive HEAD~5
Resolving “Updates were rejected because the tip of your current branch is behind”
After a git reset or git revert, a normal git push can fail with:
! [rejected] main -> main (non-fast-forward)
error: failed to push some refs
Updates were rejected because the tip of your current branch is behind its remote counterpart.
Cause: your local history no longer fast-forwards onto the remote, because you rewrote commits locally. Fix it by force-pushing your rewritten branch (only on branches you own):
git push -f origin main
For a full walkthrough of this error, see Fix “Updates were rejected” non-fast-forward push errors.
Conclusion: reset vs revert vs rebase for Undoing Commits
In this post, I have covered fixing commit mistakes in Git.
git revert is a non-destructive command that undoes a commit by adding a new one and is safer than git reset, which rewrites history. We have learned how to check the commit history with git log and get commit hashes.
I also used git stash to keep changes while doing the reset. git rebase is another option to interactively cherry-pick, join, reorder, and delete commits. In my next post, I go into more detail about GitHub merge conflicts.
Git Reverting and Resetting Commits FAQ
What is the difference between git reset and git revert?
git reset moves the branch tip to an earlier commit and discards the later commits, rewriting history — use it only on local, unpushed commits. git revert creates a new commit that undoes a previous one while preserving history, so it is the safe choice for commits already pushed to a shared remote.
How do I undo a commit that I already pushed to a remote repository?
Use git revert [hash] to add a new commit that cancels the pushed commit while keeping history intact for collaborators. Avoid git reset --hard plus a force push on shared branches, because rewriting public history can break other people’s clones.
What is the difference between git reset –soft, –mixed, and –hard?
git reset --soft [hash] moves the branch tip but keeps your changes staged. git reset --mixed [hash] (the default) keeps the changes in your working directory but unstages them. git reset --hard [hash] discards all changes after the target commit permanently.
How do I keep my changes while resetting a commit?
Stash first, then reset, then restore: run git stash, git reset --hard [hash], and git stash pop. Alternatively, use git reset --soft [hash] to move the tip while leaving your changes staged.
How do I find the commit hash to revert or reset to?
Run git log to list the commit history. Each entry shows a 40-character hash to the right of the word commit; copy that hash and pass it to git revert [hash] or git reset [hash].
References
Did you like this post? Please let me know if you have any comments or suggestions.
Git posts that might be interesting for you
Stork 'Teens' and Me
Enjoyed this? Get more like it.
Weekly notes on AI tools, Python, and what I'm actually building — plus a free copy of Fantastic AI: The 2026 Toolkit.