Undoing Changes
Three commands cover almost all "undo" in Git, and they are not interchangeable. git restore discards working-tree or staged changes, git reset moves the branch pointer and optionally the index and working tree with it, and git revert creates a new commit that inverts an old one.
Picking the wrong one has real costs: the wrong restore or reset destroys uncommitted work the reflog cannot recover, and a reset on shared history breaks everyone who already pulled it. The deciding question is almost always whether the thing you are undoing has been pushed.
Restoring Files
git restore <file> overwrites the working-tree copy with the staged (or HEAD) version, discarding your uncommitted edits to it. git restore --staged <file> does the opposite kind of undo — it unstages the file but leaves your changes on disk. And git restore --source=<commit> <file> pulls an older version of the file out of a specific commit. The first one loses edits, the second keeps them; choosing the wrong flag is how people lose work here.
Resetting the Branch
git reset moves HEAD to a chosen commit, and its flag decides how many of the three trees follow. --soft moves HEAD only, leaving the index and working tree intact (your changes become staged again). --mixed, the default, also resets the index but keeps the working tree. --hard moves all three — it overwrites the working tree, and any uncommitted changes it discards are gone, because they were never committed for the reflog to track.
Reverting Commits
git revert <commit> does not remove the commit. It computes the inverse of that commit's diff and records it as a brand-new commit on top. History stays intact and linear-forward, which is exactly why it is the safe way to undo something on a branch others have already pulled — no one's existing commits change.
Recovering From Mistakes
A bad reset feels catastrophic but usually is not. Every movement of HEAD is recorded in git reflog, retained about 90 days by default, so the "lost" commit is almost always sitting at an entry like HEAD@{1}. Find it there and git reset --hard <hash> puts you back. The one thing the reflog cannot save is work that was never committed — uncommitted working-tree changes a --hard discarded leave no ref behind.
Choosing the Right Tool
The clean rule: reset for local, unpushed cleanup, revert for anything already shared. Using revert on a purely local mistake just leaves a noisy "Revert" commit in history; using reset --hard on a pushed commit breaks every collaborator who pulled it. Match the tool to whether the history is yours alone or shared.
git reset — rewrites history by moving the branch pointer backward and (with --hard) discarding work. It is clean and safe only on commits you have not pushed, because it makes the branch diverge from what others have.
git revert — keeps history and appends a new commit that undoes the change. It is the only safe undo on a shared branch, since it adds to history rather than rewriting it, at the cost of one extra "Revert" commit in the log.
- Running
git reset --hardto undo a commit on a branch already pushed and shared — collaborators who pulled it now hold commits you discarded, producing diverging-history conflicts. - Using
git reset --hardwhile uncommitted working-tree changes you meant to keep are present — those changes were never committed, so the reflog cannot bring them back. - Reaching for
git reverton a local-only mistake whereresetwould be cleaner, leaving a pointless "Revert" commit in history. - Assuming a bad
resetis unrecoverable and re-doing the lost work, instead of checkinggit reflogwhere the commit still sits. - Running
git reverton a merge commit without-mto pick the mainline parent, which fails or reverts the wrong side.
- Use
git revert <commit>for anything already pushed to a shared branch, so no one else's history breaks. - Reserve
git reset --hardfor local, unpushed commits, and rungit statusfirst to check for uncommitted work. - Use
git restore --staged <file>to unstage andgit restore <file>to discard, instead of the older overloadedgit checkout. - Check
git reflogbefore assuming any commit is gone; HEAD movements are kept about 90 days by default. - When reverting a merge, pass
-m 1to name the mainline parent explicitly.
hg revert (restore files), hg backout (Git's revert), hg strip (history removal)Subversion svn revert to discard local changes; a reverse-merge to undo a committed revisionPerforce p4 revertFossil fossil revert; history rewriting discouraged by designKnowledge Check
You need to undo a commit that has already been pushed to a shared branch. Which command is safe?
git revert— it appends an inverse commit, leaving everyone's existing history intactgit reset --hard— it removes the commit cleanly so that collaborators no longer see it at allgit restore— it rolls the file back to its prior state without touching shared history- Either of them works equally well here; the choice between them is purely stylistic
What does git reset --hard touch that git reset --soft leaves alone?
- The index and the working tree —
--softmoves only HEAD, while--hardoverwrites all three trees - Only the commit message text recorded on the current HEAD commit, and nothing else in the working tree
- The tracking remote branch on the server in addition to the local branch pointer
- Nothing different at all between them; the three reset flags are simply aliases
You ran git reset --hard with uncommitted edits in the working tree that you wanted. Can git reflog recover them?
- No — the reflog tracks committed HEAD positions, and those edits were never committed, so there is no ref to recover
- Yes — the reflog quietly takes a full snapshot of the entire working tree on disk for every single
resetcommand you run - Yes, but only if you act within the first 30 minutes immediately following the reset command
- Only if the files happened to be previously staged with
git addbefore the reset ran
When is accepting a "Revert" commit in history the right call rather than a smell?
- When the commit being undone is already shared, since a clean rewrite would break collaborators' history
- Always — a Revert commit is the single preferred undo for any mistake at all, whether local or shared
- Only for merge commits with multiple parents, and never for ordinary single-parent ones
- Never — a Revert commit appearing always indicates the wrong tool was reached for
You got correct