Module 2: Git from Scratch for Automators
7. Rollback: going back to a version that worked
Description
By the end of this lesson you will be able to do what gives the whole module its meaning: when a change breaks your workflow, go back to a version that worked, cleanly, on the record, and without drama. You'll know how to use git log to find the good commit, you'll understand the difference between git revert —undoing a change while leaving a record, the safe path— and git reset —moving the history backward, powerful and dangerous— with the criterion for when to use each, and you'll recover a single file's previous version with git checkout <commit> -- file. In one sentence: you're going to turn all the history you built into a real safety net.
This matters because it's lesson 1's promise made real. Remember Cumbre's "bad Tuesday": someone pointed the CRM query at the test URL, wholesale orders stopped routing, and the difference between solving it in ten minutes or at midnight through guesswork was having —or not having— a history you could walk back through. Everything you learned up to here —clean commits, readable diffs, branches, backup on GitHub— existed to get to this moment: the moment something goes wrong and you can go back with surgical precision instead of an old JSON and a prayer.
Connection to the module: this lesson closes the change lifecycle that lesson 1 opened —save, compare, experiment, share, and now, revert. It uses everything before it: git log (lesson 3) to find the commit to go back to, git diff (lesson 4) to confirm what the suspect commit changed, and the backup on GitHub (lesson 6) to understand why some ways of "going back" are safe on a shared repository and others aren't. After this lesson, lesson 8's project just pulls together everything you already know into a deliverable.
The real value, versus "I had an old JSON lying around"
Before the commands, it's worth naming precisely what Git gives you that manual copies don't. Because you could already "go back to a previous version" before Git: you had order-triage-backup.json saved in some folder. What changes?
Three things change, and all three matter on incident day.
Precision. With a manual backup, you go back to "the version from when you made the backup" —which could be from three weeks ago and carry the loss of everything good you did since then. With Git, you go back to exactly the commit you want, and you can undo only the change that broke things, keeping everything else. It's the difference between amputating and operating with a scalpel.
Traceability. A manual backup leaves no trace of why you went back or what you undid. Git does: reverting a change is, in itself, an event recorded in the history, with its author, its date, and its note. A month from now, anyone can read in git log that this change got undone and why. No mystery.
Confidence. With manual backups there's always a lingering doubt: "was this the good one? did I overwrite it by accident?" With Git, every commit is immutable (lesson 3): the version that worked on Tuesday is, with absolute certainty, the one git log points to. No possible ambiguity.
That's the leap. It's not "being able to go back" —you already had that. It's going back with precision, traceability, and confidence. And only a real history gives you that.
Step zero: finding the good commit
Any rollback starts the same way: identifying where you want to go back to. The tool is git log, which you already know. When something breaks, your first move is to read the history to locate the suspect commit and the last commit that worked.
git log --oneline
Let's go back to Cumbre's "bad Tuesday" scenario. The history looks like this:
7b2e105 (HEAD -> main) Point CRM lookup at the staging URL
a91c3f8 Add wholesale category to the order classifier
3e5d720 Widen CRM lookup timeout to 15 seconds
c08b4a2 Add order-triage workflow
The workflow broke and you suspect the top commit, 7b2e105 Point CRM lookup at the staging URL. Before undoing anything, confirm it with a diff, so you don't revert blind:
# Compare the suspect commit with the one before it.
git diff a91c3f8 7b2e105
Reading that diff with lesson 4's rules, you see the only logic that changed was the Lookup Customer in CRM node's url, which went from the production CRM to the staging one. There's the cause, confirmed. Now you know exactly what to undo. The question is how, and there are two ways with opposite philosophies.
git revert: undoing while leaving a record (the safe path)
The first way, and the one you're going to use 90% of the time, is git revert.
git revert <commit> creates a new commit that undoes that commit's changes. It doesn't delete anything from the history: on the contrary, it adds a new photo that is, in effect, "the opposite" of the commit you pointed at. If that one changed the URL from production to staging, the revert changes it from staging back to production, and saves it as one more commit, at the very top.
The analogy is a ledger book. Imagine you accidentally recorded an extra charge. In a serious ledger, you don't erase the wrong line —that would raise suspicion and lose the trail. What you do is write a new line recording the reverse adjustment, with a note saying "correction of the previous entry." The mistake stays visible, and so does its correction. The final total is correct, and anyone can audit what happened. git revert is that correction line: the mistake stays in the history, but so does, right below it, its recorded amendment.
That's why git revert is the safe path, especially when your work is already shared on GitHub. Since it doesn't alter the past history —it only adds something new— it doesn't conflict with the copies others already have. You undo the mistake without rewriting the past your team shares.
Worked example: reverting the change that broke order-triage
Step 1 — Start from a clean tree and confirm where you are.
git status
It should say On branch main and nothing to commit, working tree clean.
Step 2 — Revert the guilty commit. Since the bad commit is the last one (HEAD), you can refer to it by its ID or simply as HEAD:
git revert HEAD
What to expect. Git is going to open your text editor with a commit message already written for you, something like:
Revert "Point CRM lookup at the staging URL"
This reverts commit 7b2e105...
That automatic message is good as is —it describes exactly what the commit does. Save it and close the editor to confirm. (If you got stuck with Vim as your editor and don't know how to exit: press Esc, then type :wq and Enter, which means "save and quit." And if you'd rather avoid the editor entirely, there's git revert --no-edit HEAD, which uses the automatic message without opening anything.)
After confirming, Git answers:
[main d4f9a1c] Revert "Point CRM lookup at the staging URL"
1 file changed, 1 insertion(+), 1 deletion(-)
Step 3 — Read the history and check the cleanliness.
git log --oneline
d4f9a1c (HEAD -> main) Revert "Point CRM lookup at the staging URL"
7b2e105 Point CRM lookup at the staging URL
a91c3f8 Add wholesale category to the order classifier
3e5d720 Widen CRM lookup timeout to 15 seconds
c08b4a2 Add order-triage workflow
Look at what happened, because it's exactly the block we opened lesson 1 with. The bad commit 7b2e105 is still there —the history wasn't falsified— and above it shows up d4f9a1c Revert "...", the recorded correction. Your order-triage.json in the folder is already back to pointing the CRM at production; the workflow works again. And the history tells the whole truth: a mistake was made, and it was fixed, with a date and an author. That's closing an incident with traceability.
Step 4 — Share the fix. Since your team works off the remote (lesson 6), push the revert so everyone has the fix:
git push
And that's it. Cumbre's "bad Tuesday," solved in five commands and without touching a single manual backup.
Close the loop with n8n. One detail worth not skipping: reverting in Git fixes the file, but the workflow running on your n8n instance is the one you imported, not the repository's file. For the fix to reach production, you have to import the corrected order-triage.json back into n8n and publish it. Git is the source of truth for the history; n8n is where the workflow actually runs. The rollback in Git is the first step —recovering the good version, auditably; bringing it to n8n is the second. How to promote a workflow from one side to the other in an orderly way is a topic for Modules 4 through 6; for now hold on to this: a clean revert in Git isn't the end, it's the good file ready to go back to the platform.
Recovering a single file from a previous version
Sometimes you don't want to revert an entire commit, but recover how one specific file looked at some point in the past. For example: you explored several uncommitted changes and want to bring your order-triage.json back to how it was in the first commit, without touching anything else. That's what git checkout in its file form is for:
# Brings back the version of order-triage.json from commit c08b4a2.
git checkout c08b4a2 -- order-triage.json
What to expect. The silence of success. But underneath, Git took the version of order-triage.json that existed in commit c08b4a2 and put it into your folder and your staging area, replacing whatever you had. The double dash -- before the name matters: it tells Git "what follows is a file name, not a branch" (it avoids an ambiguity if you had a branch with the same name as a file).
Notice what this command did not do: it didn't move HEAD, you didn't switch branches, it didn't touch any other file. It just brought one file from an old photo into the present. Confirm it:
git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: order-triage.json
Git sees the recovered file as an already-staged modification —because checkout left it in the working tree and in the staging area at once. Now that file is ready for you to review and, if it convinces you, save with a normal commit:
git commit -m "Restore order-triage to its first-commit version"
That way it stays recorded that you recovered that version, with its note and its date —the traceability we talked about at the beginning. Recovering the file is half the work; saving the recovery is the other half.
A note on names: modern Git also offers git restore --source=c08b4a2 order-triage.json, which does the same thing more explicitly and is the "newer" command (part of the same 2019 reform that brought git switch). Both work; checkout is the one you're going to see most in existing material, restore is the clearer one. Either one recovers a file from a previous version.
Undoing before committing: discarding loose changes
Not every rollback is about already-saved commits. A very common case is more modest: you were editing order-triage, made some changes to the file, and realized it was a bad path —you want to go back to how the file was in your last commit, tossing what you have. You haven't committed anything yet, so there's no commit to revert. What you want is to discard loose changes from the working tree.
The tool is git restore:
# Discards the unsaved changes to order-triage.json,
# returning it to how it was in the last commit.
git restore order-triage.json
What to expect. The silence of success, and your file back to how it was in the last commit. Watch out, because this is a serious warning: git restore on a file discards your unsaved changes and there's no trash can. What you hadn't committed or staged gets lost. That's why Git reminds you every time you run git status and see a modified file: among the suggestions shows up (use "git restore <file>..." to discard changes in working tree), with the word discard right there.
There's a sibling variant for when you already did git add but regret having staged the file —not the changes, just having put them in the frame:
# Takes order-triage.json out of the staging area, WITHOUT losing the changes.
git restore --staged order-triage.json
This last one is gentle and reversible: it just puts the file from the staging area back into the working tree, without touching your changes. It's the "undo" of git add. The difference between the two forms matters and is worth memorizing: git restore --staged file takes it out of the frame but keeps your changes; git restore file (without --staged) discards your unsaved changes. One is reversible, the other isn't. When in doubt about the second one, look first with git diff at what you're about to lose.
With this you have the complete map of "undoing" depending on which zone what you want to undo lives in, which is, again, lesson 2's three-zone model: loose changes in the working tree get discarded with git restore file; staged changes get taken out of the frame with git restore --staged file; and already-saved commits get undone with git revert (safe) or git reset (local, with care).
git reset: moving the history backward (powerful and dangerous)
We've arrived at the tool that has to be treated with respect. git revert adds a correction; git reset does something different and more drastic: it moves your branch's pointer backward, as if the later commits had never existed.
Remember from lesson 5 that a branch is a movable pointer to a commit. git reset <commit> grabs that pointer and moves it to the commit you tell it, leaving the commits that came after "orphaned." That is, it rewrites your branch's history: it removes them from the line.
Go back to the ledger book analogy. If revert was writing a correction line, reset is tearing out the page with the mistake. The account can end up right, but you destroyed the record of what happened —and if someone else had a copy of that page, now your book and theirs don't match.
reset has three modes, depending on what it does with your changes when it moves the pointer:
git reset --soft <commit>: moves the pointer, but keeps all your changes in the staging area, ready to redo a commit. The gentlest one.git reset --mixed <commit>(the default mode): moves the pointer and keeps your changes in the working tree, but unstaged. The files stay touched, you decide what to do.git reset --hard <commit>: moves the pointer and discards all later changes, staged and in the working tree. It throws them away. It's the most powerful and the most dangerous: work it discards isn't easy to recover.
So when is reset used? In one specific, local case: when you made a commit that you haven't shared with anyone yet and you want to undo it as if it never happened —for example, a commit with an embarrassing message or with something that shouldn't have gone in. Since nobody else has that commit, rewriting your local history breaks nothing of anyone else's.
The golden rule, the one you must not break: never use git reset on commits you already pushed to a shared remote. If you already did git push on a commit and then delete it with reset, your local history and the remote's —and your teammates'— stop matching, and fixing it is a mess that can cost the team lost work. To undo something already shared, always git revert, which adds instead of rewriting. The distinction is simple and saves you a lot of trouble: revert for what's shared, reset only for what's still yours and nobody else's.
Since this guide teaches you the Git of a workflow's lifecycle, and that lifecycle almost always goes through a shared remote, the practice you take away is: use revert by default. reset is here so you understand and recognize it, not to be your daily tool.
The emergency net: git reflog
It's worth knowing one last tool, not for daily use but for the day of the scare: git reflog. I mention it because knowing it exists can save you from believing you lost work forever.
Every time HEAD moves —every commit, every branch switch, every reset, every merge— Git secretly notes where it's been, in a local record called the reflog (for reference log). That record isn't your commit history; it's a private log of your movements, even the ones that "erased" things.
git reflog
What to expect. A list of HEAD's recent movements, each with its ID:
d4f9a1c (HEAD -> main) HEAD@{0}: revert: Revert "Point CRM lookup at the staging URL"
7b2e105 HEAD@{1}: commit: Point CRM lookup at the staging URL
a91c3f8 HEAD@{2}: commit: Add wholesale category to the order classifier
3e5d720 HEAD@{3}: commit: Widen CRM lookup timeout to 15 seconds
What's this good for in a rollback? Imagine you did a git reset --hard too aggressively and "lost" a commit you actually wanted. That commit doesn't show up in git log —you took it out of the line— but it's still in the reflog, with its ID. With that ID you can go back to it (for example, git checkout <ID> to inspect it, or create a new branch that rescues it with git switch -c rescue <ID>). That's why I said reset --hard discards work in a way that's "almost" unrecoverable, not completely: the reflog is that net underneath.
Two honest limits so you don't lean on it too much. First, the reflog is local: it lives only on your machine, it doesn't get shared or cloned; if the problem is on another copy, its reflog is the one that matters. Second, the reflog expires: Git cleans up old entries after a while (by default, weeks or months depending on the type). It isn't an eternal file. That's why it's still better not to get into the jam in the first place —use revert instead of reset --hard— than to count on rescuing yourself afterward. But if the jam already happened, breathe: git reflog is the first thing to look at before considering something lost.
Common mistakes
Using git reset --hard on work you didn't want to lose (practical and serious). What happens: to "clean up" changes, git reset --hard gets run and commits or modifications that actually mattered disappear, with no obvious trash can to pull them from. Why it happens: --hard sounds like "reset and done," and it isn't obvious that it discards work in an almost unrecoverable way. How to spot it: if after a reset --hard you're missing something, this is it —and the scare is real. How to fix it: before any reset --hard, ask yourself if there's something there you want to keep; if in doubt, don't use it. There's a little-known emergency net —git reflog, a record of where HEAD has been, that sometimes lets you recover commits you thought were lost— but don't count on it: the best defense is not using --hard lightly. To undo without destroying, use revert.
Confusing revert and reset (conceptual). What happens: you want to undo an already-shared change and use reset, rewriting history others already had and causing divergence between the team's copies. Or the opposite: you want to clean up a private local commit and use revert, leaving two commits (the mistake and its correction) when deleting one would have been enough. Why it happens: both "undo," but in opposite ways —one adds, the other rewrites— and it's easy to mix them up. How to spot it: ask yourself "have I already pushed this commit to the remote?" If yes, reset is dangerous. How to fix it: memorize the rule —revert for shared work (adds a correction, safe), reset only for local, private work (rewrites, risky). When in doubt, revert never gets you into a diverging-history problem.
Reverting without first confirming what the commit did (practical). What happens: something breaks, the last commit gets blamed and reverted immediately, but the reverted commit wasn't the cause —or it also carried a good change that now got undone too. Why it happens: incident urgency pushes you to act before diagnosing. How to spot it: if after reverting the problem persists, or a new one shows up, you might have reverted the wrong commit. How to fix it: before reverting, confirm with git diff <previous> <suspect> that this commit contains the change that broke things, and only that. Diagnose with the diff, then act. And if a commit mixes a good change with a bad one, that's exactly why lesson 3 insisted on one commit per idea: atomic commits get reverted without collateral damage.
Recovering an old file and believing you "already went back" without committing (practical). What happens: a previous version of order-triage.json gets recovered with git checkout <commit> -- file, it gets tested, it works, and the matter is considered closed —but the commit never got made, so the history doesn't record the recovery and the remote doesn't have it. Why it happens: since the file is already right in the folder, it feels finished. How to spot it: git status shows the recovered file under "Changes to be committed" or "not staged"; if it's still there, you didn't save it. How to fix it: after recovering and verifying, do the normal cycle —git add, git commit with a clear note ("Restore order-triage to the pre-staging-url version"), and git push. Recovering the file is half; recording the recovery in the history is the other half, and it's the one that gives it traceability.
Exercises
Exercise 1 — Revert a change from start to finish. In your repository, make a change to order-triage you know is "bad" (for example, change the webhook's path to something incorrect), export, and save it with a commit Change webhook path to order-intake. Then, imagine it broke order intake: confirm it with a git diff against the previous commit, revert the bad commit with git revert, and verify with git log --oneline that both commits are there (the bad one and its revert) and your file is back to the correct path.
See solution
The sequence:
# (you make the bad change in n8n and replace the file)
git add order-triage.json
git commit -m "Change webhook path to order-intake"
# diagnosis: confirm what the bad commit changed
git diff HEAD~1 HEAD
# the fix
git revert HEAD # save the automatic message, or use --no-edit
git log --oneline # you see the bad commit AND its revert on top
At the end, git log --oneline shows on top the commit Revert "Change webhook path to order-intake", below it the bad commit (which is still there, it didn't get deleted), and your order-triage.json back with the correct path.
Why it works: you did the real cycle of an incident —break, diagnose with a diff, revert, verify. The pedagogical point is seeing the bad commit remains in the history: you didn't falsify the past, you corrected it in plain sight. That's the traceability that separates a professional rollback from "I deleted the bad file and moved on."
Exercise 2 — Choose the right tool. For each situation, say whether you'd use git revert, git reset, or git checkout <commit> -- file, and why.
(a) A commit you already pushed to GitHub, and that your coworker already pulled, turns out to be wrong; it needs to be undone.
(b) You just made a local commit, haven't pushed it anywhere yet, and realized the message has a serious mistake and you'd rather redo it from scratch.
(c) You want to recover how only order-triage.json looked three commits ago, without touching the rest of the project or undoing commits.
See solution
(a) git revert. The commit is already shared —you pushed it and she pulled it— so rewriting history with reset would cause divergence between the copies. revert adds a correction without touching the shared past: safe.
(b) git reset (for example git reset --soft HEAD~1, which undoes the commit but keeps your changes ready to redo it). The commit is local and private, nobody else has it, so rewriting your own history breaks nothing of anyone else's. This is reset's legitimate case.
(c) git checkout <commit> -- order-triage.json (or its equivalent git restore --source=<commit> order-triage.json). You don't want to undo commits or touch the rest; you just want to bring in a file from an earlier version. That's exactly what this form does.
Why it works: the three situations map to the three tools by their essential property —is it shared? (revert vs reset) and do I want to undo commits or just recover a file? (reset vs checkout). Once that double question is clear, choosing stops being memory and becomes deduction.
Exercise 3 — Reason through the danger of reset on a remote. Explain in your own words, in three or four sentences, why using git reset --hard on a commit you already pushed to GitHub, and that a coworker already pulled, is a bad idea, and what you should use instead.
See solution
A complete answer touches these points: git reset rewrites your branch's history by removing commits from the line. If those commits were already on the remote and your coworker already pulled them, then your local history (without them) and the remote's/your coworker's (with them) stop matching. When you try to push your rewritten history, Git will reject it as diverged, and forcing it can overwrite or discard the work your coworker built on top of those commits. The right tool for undoing something already shared is git revert, which adds a correction commit without touching the history the team already has, keeping all copies in sync.
Why it works: this exercise forces you to articulate the golden rule in your own words, which is how it really gets internalized. The danger of reset on shared work isn't abstract: it's a coworker's real work that can evaporate. Understanding it deeply is what makes the reflex "have I already pushed this?" become automatic before any reset.
Summary and next step
In this lesson you turned your history into a real safety net. You saw that Git's value over "I had an old JSON lying around" isn't "being able to go back" —you already had that— but going back with precision (to the exact commit, undoing only what broke things), traceability (the reversion gets recorded with author and date), and confidence (every commit is immutable, no ambiguity about which one was the good one). You learned to find the commit to go back to with git log and to confirm the cause with git diff before acting. You mastered the two undoing philosophies: git revert, which adds a correction commit without touching the past —the adjustment line in the ledger book, safe for shared work— and git reset, which moves the branch's pointer backward, rewriting history —tearing out the page, powerful and reserved only for local commits nobody else has— with the golden rule: revert for shared work, reset only for private work. And you recovered a single file from a previous version with git checkout <commit> -- file. You closed Cumbre's "bad Tuesday" in five commands.
Before moving on to lesson 8 you should be able to: revert a commit and explain why the bad commit stays in the history; say when reset is safe and when it's dangerous; and recover a file's previous version without touching the rest of the project.
With this you have the complete cycle. You know how to install Git, create a repository, make clean commits, read diffs through the JSON's noise, experiment on branches, back up and share on GitHub, and go back when something breaks. Lesson 8 doesn't introduce anything new: it's the project that pulls it all together. You're going to take order-triage, put it under version control end to end, iterate across three different branches, review its diffs, merge into main, revert a specific change, and connect it to a remote on GitHub. The deliverable —a repository with a readable history and one cleanly reverted change— is exactly what the market asks for when it says "version-controlled JSON," and it's the proof, defensible in an interview, that you crossed the border from workflow builder to system owner.
Resources
- Undoing Things — Git Docs — the official book's chapter on undoing changes, including file recovery and
reset's traps. - git revert — Git Docs — the reference for the command that undoes a commit by adding another, the safe path for shared work.
- git reset — Git Docs — the reference for the command that moves the branch's pointer, with the explanation of the
--soft,--mixed, and--hardmodes. Read it with the golden rule in mind. - Reset Demystified — Git Docs — a chapter dedicated to understanding
resetin depth, with diagrams of what each mode touches. Useful for when you want to stop fearing it by understanding it. - git checkout — Git Docs — the command's reference, including the
git checkout <commit> -- <file>form for recovering a file from a previous version.