What is the difference between git reset and git revert?
Quick Answer
git reset moves the branch pointer backward, effectively erasing commits from history (dangerous on shared branches). git revert creates a new commit that undoes a previous commit's changes while preserving the original history (safe for shared branches).
Detailed Answer
Imagine you wrote several pages in a book and realized page 5 has an error. git reset is like tearing out pages 5 through the end and pretending they never existed. git revert is like adding a new page at the end that says 'Correction: disregard what page 5 said.' Both fix the problem, but reset rewrites history while revert preserves it. The rule of thumb: use revert for anything others have seen, use reset only for local, unpushed work.
git reset moves the current branch's HEAD pointer to a specified commit, optionally modifying the staging area and working directory. It has three modes: --soft moves HEAD but keeps the staging area and working directory unchanged (your changes become staged), --mixed (the default) moves HEAD and resets the staging area but keeps working directory changes (your changes become unstaged), and --hard moves HEAD and resets both the staging area and working directory (all changes are lost). git revert, on the other hand, computes the inverse of a specified commit's changes and applies them as a new commit, preserving the full history.
Internally, git reset simply updates the reference file for the current branch (e.g., .git/refs/heads/main) to point to a different commit. The 'erased' commits still exist in Git's object database and can be recovered via git reflog for a default period of 90 days. However, they become unreachable from any branch and will eventually be garbage collected. git revert works differently: it reads the diff introduced by the target commit, generates the inverse diff, applies it to the current state, and creates a new commit. If the inverse changes conflict with subsequent modifications, Git will report a merge conflict that you must resolve manually.
In production, this distinction is critical for shared branches. If you reset a commit that other developers have already pulled, their repositories will diverge from the remote, causing confusion and potential data loss. A force push after reset on a shared branch can overwrite teammates' work. git revert is safe because it only adds new commits, so all developers can simply pull and stay in sync. Most teams have branch protection rules that prevent force pushes to main or release branches, making revert the only viable option for undoing changes in those branches.
A crucial gotcha: git reset --hard is one of the few Git commands that can permanently lose uncommitted work. Always check git status before running it. Another trap: reverting a merge commit requires specifying which parent to use with the -m flag (e.g., git revert -m 1 <merge-commit>), which confuses many developers. Also, if you revert a commit and later want to re-apply those changes, simply cherry-picking the original commit won't work because Git sees it as already applied. You'd need to revert the revert commit instead, which is counterintuitive but correct.