DevOps Foundations · Module 4: Git and team workflow
Revert, Reset and Recovery
Mistakes in version control are inevitable; damage from undoing them is optional. This lesson picks the undo that matches the audience: revert for the world, reset for your desk, reflog for disasters.
11 min reading
Objectives
- Undo a published change with revert, never with rewritten history
- Choose reset modes by what must survive: index, worktree or nothing
- Recover lost commits with the reflog before doing anything else
- Explain why force-push to shared branches is a team-level event
Why this matters
A bad change reaches main and deploys start failing. One engineer reverts in two minutes; the service recovers and the change returns later as a proper fix. Another resets main and force-pushes; every teammate's checkout now diverges, the CI builds ghosts, and the incident doubles into a history repair affecting ten people. Same mistake, opposite recoveries. The undo must fit who already saw the change.
Concepts
Revert adds a new commit that undoes an old one. History stays append-only: everyone's checkout advances normally, the bad change and its reversal are both visible, and a later re-landing is just another commit. This is the only safe undo for anything pushed, shared, or deployed. Reverting a merge needs -m to name which parent was the mainline; get it backwards and the revert re-applies the feature instead of removing it.
Reset moves your branch pointer and optionally the index and worktree. --soft keeps everything staged (re-commit differently), --mixed keeps the work but unstages (the default, re-stage deliberately), --hard discards all local changes permanently. That permanence is the entire warning: --hard on work you have not pushed anywhere else destroys it. Reset is a desk tool for private branches; the moment another human or machine holds the commits, revert is the answer and reset is off the table.
The reflog is Git's flight recorder: every move your pointers made, including the bad reset, kept for about 90 days. git reflog shows HEAD@{n} entries with actions; git reset --hard HEAD@{n} or a new branch at the hash restores whatever was lost. The recovery order is fixed: stop, reflog, new branch at the rescued commit, verify contents, then decide. Never run gc, never clone fresh hoping the objects appear, and never reset again to fix the reset.
Worked example
A bad commit is on main and teammates have pulled. The safe undo:
$ git log --oneline -3 a1b2c3d (main) Raise default timeout to 120s e4f5a6b Healthcheck uses /ready 9d8e7f6 Release 1.4.0 $ git revert a1b2c3d --no-edit [main 7f6e5d4] Revert "Raise default timeout to 120s" $ git log --oneline -2 7f6e5d4 Revert "Raise default timeout to 120s" a1b2c3d Raise default timeout to 120s
Expected reading: history grew by one commit instead of shrinking; every teammate pulls normally with no divergence; the revert message names what it undoes so the future re-landing references it. Contrast with the forbidden alternative: reset --hard HEAD~1 plus push --force, which orphans every existing checkout and turns one bad commit into a team-wide repair. Lab L11 replays this: revert the published mistake, keep history intact, verify.
The common wrong move
Force-pushing to shared branches to keep history pretty. Pretty history on main is worth less than every teammate's working checkout, and the rewrite hides the mistake instead of recording the learning. Rewrite private branches freely (rebase, squash, reorder); treat anything pulled by others as published, and published history only ever moves forward.
Lab and next step
Lab L11 publishes a bad change to a shared scratch remote and requires the revert path with verification from a second clone. Next, lesson 4 makes history reviewable: pull requests and traceability.
Quick check
An optional 4-question self-check. Answers never leave your device, are not stored, and never count toward any assessment.
Lesson feedback
No published feedback yet.
Log in and complete the lesson to leave feedback.
Exercise
In a scratch repo with a second clone simulating a teammate: publish a bad commit, pull it in the second clone, revert on main, and show both clones converging with normal pull. Then demonstrate the divergence a reset+force would have caused (in scratch only).
Pass criteria
Revert path shown with both clones converging cleanly; reset+force divergence demonstrated as the contrast; no shared real repo touched.