I Lost 3 Weeks of Work Because I Didn’t Know Git Rebase

2:47 AM. Merge conflict. I panicked. Typed git reset –hard. 127 commits vanished in 0.3 seconds.

I’m going to tell you about the worst night of my engineering career.

Not the night our production database crashed.

Not the deploy that took down the API.

This was worse.

Because I deleted my own work. Three weeks of it. And it was entirely my fault.

The Setup

It was mid-November. I’d been working on a major feature for our payment system.

Three weeks of:

  • 47 commits
  • 2,847 lines of code
  • Stripe integration
  • Error handling
  • Tests

Everything worked. Ready for review.

Just needed to sync with main before creating the PR.

Should’ve been 5 minutes.

It took 3 weeks to recover.

2:43 AM: Where It Started Going Wrong

I was tired. Should’ve been asleep.

But I wanted to finish. Get that PR up. Look productive in the morning standup.

git checkout main
git pull
git checkout feature/payment-system
git merge main
```
Expected: clean merge.
Reality:
```
CONFLICT (content): Merge conflict in src/payment/processor.py
CONFLICT (content): Merge conflict in src/models/transaction.py
CONFLICT (content): Merge conflict in tests/test_payments.py
Automatic merge failed; fix conflicts and then commit the result.

47 conflicts.

My brain: “What the fuck.”

2:44 AM: The Panic Begins

I opened the first file.

<<<<<<< HEAD
def process_payment(amount, currency, customer_id):
# My beautiful implementation
# 3 weeks of work
# Stripe integration
# Error handling
=======
def process_payment(amount, currency):
# Someone else's version
# Completely different approach
# No customer_id parameter
>>>>>>> main
```
Every file looked like this.
Their code vs. my code. Everywhere.
I tried fixing them manually. Got confused. Made it worse.
```
<<<<<
=======
>>>>>>>
<<<<<

Conflict markers everywhere. Code not even valid Python anymore.

2:47 AM: The Mistake

I Googled: “how to undo git merge”

First result: “Use git reset –hard HEAD”

I thought: “Okay, reset to before the merge. Then I’ll figure out a better approach.”

Typed it:

git reset --hard HEAD

Terminal: nothing.

Opened my code editor.

Every file: back to main branch state.

My 3 weeks of work: gone.

My brain: “Wait… what?”

Checked git log:

git log --oneline

My 47 commits: not there.

All I saw: other people’s commits on main.

My feature branch: exactly the same as main.

2:48 AM: Full Panic

Hands shaking.

Opened every file. Nothing. All main branch code.

git status
```
```
On branch feature/payment-system
nothing to commit, working tree clean

“Working tree clean.”

CLEAN?

I just had 2,847 lines of code!

Tried:

git log --all --grep="payment"

Nothing.

git branch -a

My branch existed. But it was empty. Same as main.

2:52 AM: The Acceptance

I sat there staring at the screen.

3 weeks.

Gone.

I’d have to:

  • Rebuild Stripe integration
  • Rewrite error handling
  • Recreate all the tests
  • Remember all the edge cases I’d solved

And explain to my team tomorrow: “Hey, so I deleted everything.”

Started crying. Not gonna lie.

3:17 AM: The Google Search That Saved Me

One last desperate search: “git recover deleted commits”

Found a StackOverflow answer mentioning git reflog.

Never heard of it.

Tried it:

git reflog
```
Output:
```
a4f8d92 HEAD@{0}: reset: moving to HEAD
f7e3c41 HEAD@{1}: merge main: Merge made by the 'recursive' strategy
b2d9a83 HEAD@{2}: commit: Add error handling for failed payments
8c4e1f5 HEAD@{3}: commit: Implement Stripe webhook handlers
...

Holy shit.

There they were. All my commits. Still existed.

HEAD@{1} – right before the reset.

3:19 AM: The Recovery

Hands still shaking, but different now. Hope, not panic.

git reset --hard HEAD@{1}

Opened my editor.

Files started appearing.

My code. My 3 weeks. Back.

Checked git log:

git log --oneline

All 47 commits. There.

I actually laughed. Alone. At 3:20 AM.

What I Learned (The Hard Way)

1. git reset –hard is dangerous

It doesn’t just undo the merge.

It resets your branch to match whatever you point it at.

HEAD was pointing at the merge commit on main.

So my branch became main.

All my commits: orphaned.

2. Git never really deletes anything

Those 127 commits didn’t vanish.

They became “dangling” — no branch pointing to them.

Git keeps them for ~30 days (configurable).

git reflog shows every HEAD movement. Ever.

That’s how I found them.

3. What I should’ve done instead

Option 1: Abort the merge

git merge --abort

Clean slate. Try again.

Option 2: Use rebase (the right way)

git rebase main

Replays my commits on top of main.

Conflicts? Fix them one commit at a time.

Way cleaner than merge conflicts.

Option 3: Create a backup branch first

git branch backup-payment-system

Then experiment. Fuck up. Switch back to backup.

4. Reflog is a time machine

Every time you:

  • Commit
  • Merge
  • Rebase
  • Reset
  • Checkout

Git records it in reflog.

You can go back to ANY point.

git reflog               # See history
git reset --hard HEAD@{5} # Go back 5 moves

It’s saved me 7 times since that night.

The Commands I Wish I Knew

Safe merge conflict resolution:

git merge main
# Conflicts appear
git merge --abort # Nope, try again

Rebase instead of merge:

git rebase main
# Conflicts on first commit
# Fix it
git add .
git rebase --continue
# Next conflict
# Fix it
git add .
git rebase --continue
# Repeat until done

Create safety checkpoints:

git branch backup-$(date +%Y%m%d)
# Now experiment freely
# Fucked up?
git checkout backup-20241127

Check what you’re about to delete:

git log HEAD..feature/payment-system
# Shows commits you'd lose

Undo almost anything:

git reflog
# Find the commit before you fucked up
git reset --hard HEAD@{X}

What Actually Happened

That night taught me more about Git than 2 years of using it.

Because I felt the pain.

The panic of losing work.

The shame of almost having to admit it.

The relief of discovering reflog.

I made a checklist after that. Run it before every risky Git operation:

Pre-Merge Checklist:

  • Create backup branch
  • Check current commits: git log –oneline
  • Know the abort command: git merge –abort
  • Have reflog open in another terminal

Pre-Rebase Checklist:

  • Backup branch created
  • On correct branch: git branch
  • Know the abort: git rebase –abort
  • Conflicts expected? Plan time to fix them

Pre-Reset Checklist:

  • Double-check what you’re resetting to
  • Use –soft or –mixed unless you’re SURE
  • Never –hard without a backup

The Brutal Truth

Git is unforgiving.

One command. Years of work: gone.

But it’s also forgiving.

Because of reflog. Because commits stick around.

The trick is knowing the escape hatches BEFORE you need them.

I learned mine at 3 AM on a Wednesday.

You can learn yours now.

Everything I Wish Someone Told Me About Git

I turned that night into a reference guide.

Every Git command I actually use. Every footgun I’ve stepped on. Every recovery technique that saved me.

Not 500 pages. Just the stuff that matters.

Master Git in Minutes

35 essential commands. Real-world workflows. The reflog tricks that saved my ass.

It’s the guide I wish I had at 2:47 AM.

More production disasters and lessons learned:
Subscribe on Substack

Dealing with other production nightmares?
Try ProdRescue AI — Get instant RCA analysis and recovery runbooks

Have you lost work to a Git mistake?

Drop your story in the comments. The most painful one gets featured next week.

And remember: git reflog is your friend. Learn it before you need it.

Because you will need it.


I Lost 3 Weeks of Work Because I Didn’t Know Git Rebase was originally published in Javarevisited on Medium, where people are continuing the conversation by highlighting and responding to this story.

This post first appeared on Read More