Lesson 5 - Guided Project: Polish a Branch and Recover a Lost Commit
Welcome to the Guided Project
This project ties the whole module together by walking you through the cycle that defines confident, everyday Git use: clean up, then oops, then rescue. You will do it in two parts, each a self-contained scenario you can run on your own machine.
In Part 1, you’ll inherit a messy feature branch — the kind every developer produces while actually working: a string of wip commits and a last-minute typo fix. You’ll use interactive rebase to squash that noise into a single, clearly-named commit that’s ready to share. In Part 2, you’ll engineer a small disaster. You’ll git reset --hard away two good commits as if you’d fat-fingered the wrong command, watch them seemingly vanish, and then use git reflog to find them again and bring them back. Together these two skills are the safety net under all the history-rewriting you learned this module.
By the end of this project, you will be able to:
- Squash several messy
wipcommits into one clean commit with interactive rebase - Write a meaningful combined commit message during a squash
- Recognize that
git reset --harddoesn’t truly delete your commits right away - Use
git reflogto find and recover commits you thought were lost
A quick note before you begin: the commit hashes you see below are real output from a verified run, but your hashes will be different — Git derives them from your content, author, and timestamp. Match on the messages and the shape of the output, not the exact characters. Let’s build.
Part 1 — Inspect the Messy Branch
Picture a feature branch where you’ve been hacking on a small JSON parser. You committed early and often, which is good practice — but the names are throwaway:
$ git log --oneline
6028cb2 oops fix typo
0a3a32a more wip
5416912 wip
34e6232 Add parserThe bottom commit, Add parser, is a reasonable starting point. The three above it — wip, more wip, and oops fix typo — are the breadcrumbs of work-in-progress. Nobody reviewing your code needs to see “oops fix typo” as a separate moment in history. Those three commits really represent one logical change: building the parser. Your goal is to fold them into a single commit with a name that explains the what and why, not the when.
You want to reshape the top three commits, so you’ll rebase the last three interactively.
Part 1 — Squash Into One Clean Commit
Start the interactive rebase across the three commits you want to combine:
$ git rebase -i HEAD~3Git opens an editor listing those three commits, oldest at the top, each prefixed with the word pick:
pick 5416912 wip
pick 0a3a32a more wip
pick 6028cb2 oops fix typoTo merge them, you keep the first as pick and change the other two to squash — that tells Git to fold each one into the commit above it:
pick 5416912 wip
squash 0a3a32a more wip
squash 6028cb2 oops fix typoSave and close the file. Git now opens a second editor so you can write the message for the single combined commit. Clear out the old wip text and write something a reviewer would thank you for:
feat: add JSON parserSave that, and the three commits collapse into one:
$ git log --oneline
dfa8239 feat: add JSON parser
34e6232 Add parserThe history is now clean: a clear feat: add JSON parser commit sitting on top of your starting point, with all the messy intermediate steps gone. This is exactly the branch you’d want to open a pull request from. Notice the new commit has a brand-new hash (dfa8239 here) — squashing is a form of rewriting history, so it produces a fresh commit. That’s perfectly safe because you only did it to local, un-pushed work.
Part 2 — Engineer the Disaster
Now switch scenarios. Imagine a different small repo where you’ve done three commits of real, valuable work:
Add work(the starting point)Important analysisMore important work
You meant to run a harmless command, but instead you typed git reset --hard HEAD~2 — moving your branch back two commits and discarding everything since. Here’s the moment it happens:
$ git reset --hard HEAD~2
HEAD is now at 8b36465 Add work
$ git log --oneline
8b36465 Add workYour stomach drops. git log shows only Add work. The Important analysis and More important work commits are gone from the log entirely — as if hours of effort just evaporated. This is the exact disaster that makes people fear git reset --hard.
Here’s the good news: those commits are not actually deleted. reset only moved your branch pointer; the commits themselves are still sitting in the repository, unreferenced, waiting to be found.
Part 2 — Recover With Reflog
The tool that saves you is git reflog. While the ordinary log only shows commits reachable from where your branch currently points, the reflog records every place HEAD has been — including the commits you just reset away. Run it:
$ git reflog
8b36465 HEAD@{0}: reset: moving to HEAD~2
db03b58 HEAD@{1}: commit: More important work
e672b64 HEAD@{2}: commit: Important analysis
8b36465 HEAD@{3}: commit (initial): Add workThere they are. HEAD@{0} is the reset you just did. But HEAD@{1} — commit db03b58, “More important work” — is the exact tip your branch had before the reset. That’s the commit you want back, and recovering it brings Important analysis along too, since it’s that commit’s parent.
To recover, point your branch back at the lost commit with another reset --hard — this time using the hash from the reflog:
$ git reset --hard db03b58
HEAD is now at db03b58 More important work
$ git log --oneline
db03b58 More important work
e672b64 Important analysis
8b36465 Add workAll three commits are back exactly as they were. The “disaster” is undone. Note that db03b58 is the hash from this verified run — in your own repo you’d copy whatever hash the reflog shows on the HEAD@{1} line.
The everyday rhythm of confident Git
These two skills together are the heartbeat of fearless Git use: shape your history deliberately, and know you can always recover. Interactive rebase lets you polish messy work into something clear before you share it, and the reflog means a wrong reset or a botched rebase is almost never fatal — Git remembers where you’ve been. Two reminders, though. Only rebase local, un-pushed commits, since rewriting shared history breaks everyone who built on it. And the reflog is a local safety net that expires over time (entries are pruned after about 90 days by default), so it rescues recent mistakes, not ancient ones.
Extend the Project
Try these variations to deepen the two skills. Each comes with a hint if you get stuck.
Exercise 1: Reword instead of squash
Recreate a small branch with two or three commits. This time, instead of squashing them, use interactive rebase to reword just one commit’s message — fixing a typo or making it clearer — without combining anything.
Hint
Run git rebase -i HEAD~3 and change pick to reword (or its shorthand r) on the one line you want to fix. Leave the others as pick. Save, and Git will open the message editor for just that commit, letting you rewrite it while the rest stay untouched.
Exercise 2: Drop a commit entirely
Make a branch where one commit is a genuine mistake — say, a debug print you never want in history. Use interactive rebase to remove that commit completely while keeping the others.
Hint
In the interactive rebase editor, change that commit’s line from pick to drop (or its shorthand d) — or simply delete the entire line. Save, and Git replays the remaining commits as if the dropped one never existed. (If deleting the line, make sure you remove the whole line, not just the word.)
Exercise 3: Recover from a bad rebase
Do an interactive rebase that goes wrong on purpose — for example, drop a commit you actually needed. Then use the reflog to undo the entire rebase and get your original branch back.
Hint
Run git reflog and look for the entry just before the rebase started — it’s often labelled something like rebase (start) or shows your branch tip from before. Reset back to that hash with git reset --hard <hash>, exactly as you did in Part 2. The reflog records pre-rebase state too, so a bad rebase is just as recoverable as a bad reset.
Summary
In this project you ran the full clean-up-and-recover cycle on your own. Part 1 took a messy feature branch — wip, more wip, oops fix typo — and used git rebase -i HEAD~3 with the squash action to fold three throwaway commits into a single, well-named feat: add JSON parser commit ready to share. Part 2 simulated the classic disaster: a git reset --hard HEAD~2 that appeared to destroy two good commits, followed by a rescue with git reflog — which revealed that HEAD@{1} still pointed at the lost work, letting you git reset --hard straight back to it. Polish, oops, rescue: that’s the loop you’ll run for the rest of your career.
Key Concepts
- Interactive rebase (
git rebase -i) — reshape your recent local commits;squashfolds several into one. - Combined commit message — during a squash, Git lets you write one clear message for the merged commit.
git reset --hard— moves your branch pointer and discards changes, but doesn’t immediately delete the underlying commits.git reflog— a local record of everywhereHEADhas been; the tool for finding and recovering “lost” commits.
Why This Matters
Messy local history and the occasional catastrophic command are facts of real development — not signs you’re doing it wrong. What separates a confident developer from an anxious one is knowing that both are fixable: you can always polish your commits before sharing, and you can almost always recover work you thought was gone. Once reflog is in your toolkit, git reset --hard and interactive rebase stop being scary and become exactly what they are — powerful, routine tools. That confidence is the real outcome of this module.
Next Steps
Continue to Module 8 - Automating with GitHub
Issues, GitHub Actions CI, branch protection, and Pages
Back to Module Overview
Return to the Rewriting and Recovering History module overview
Continue Building Your Skills
You’ve now done the complete cycle hands-on: you can take a tangle of wip commits and turn it into history a reviewer will appreciate, and you can rescue good work from an accidental reset --hard using the reflog. With deliberate rewriting and reliable recovery both under your belt, you’re ready to move beyond your own machine and let GitHub do work for you — automating checks, protecting branches, and publishing — in Module 8.