Back to all articles
Git Time Travel: reset, reflog, bisect and saving doomed branches

Git Time Travel: reset, reflog, bisect and saving doomed branches

Learn how to time travel with Git: reflog, reset, revert, bisect and disaster-recovery techniques without taking the team down.

Human-architected research synthesized with the assistance of AI personas.
9 min read

TL;DR / Executive Summary

Learn how to time travel with Git: reflog, reset, revert, bisect and disaster-recovery techniques without taking the team down.

💡 TL;DR (Too Long; Didn't Read)

Git lets you time travel and fix mistakes. git reflog recovers anything still in the system. git reset rewrites local history. git revert undoes commits without rewriting (safe for shared history). git bisect automatically finds which commit broke everything. Use these tools confidently — Git was built for this.

If you use Git long enough, you eventually do the classics: wrong git push, deleted branch, commit on the wrong branch, that bug that "came out of nowhere" after a merge.

The difference between a constantly-suffering dev and a battle-hardened one is simple: the latter knows how to time travel with Git.

In this article we'll go beyond the basics (git log, git reset --hard and other dangerous toys) and into what really separates adults from kids in Git:

  • How to actually see history (not just default git log)
  • How to use reflog to recover almost anything
  • When to use reset vs revert (and how not to become the team villain)
  • How to hunt bugs with git bisect like a pro
  • A survival kit for production disasters

By the end, you'll be able to break things with confidence — because you know how to go back.


1. Understanding time in Git (no cheap philosophy)

Before you start throwing reset --hard around, you need a decent mental model of what Git is doing.

1.1. Commits, refs and this HEAD thing

In a very simplified way:

  • Each commit is a snapshot of the project.
  • Branches (main, develop, etc.) are pointers to commits.
  • HEAD is "where you are" right now — usually pointing to a branch.

See it in practice:

bash
git log --oneline --graph --decorate --all

This shows:

  • The commit graph (--graph)
  • All refs (branches, tags, HEAD) (--decorate --all)

Use this every day. Devs who only use plain git log are playing on easy mode.

1.2. What it really means to "lose" a commit

Most of the time you didn't lose anything. You just removed the reference to a commit.

As long as Git still remembers that commit somewhere (spoiler: reflog), you can bring it back.


2. git reflog: the secret history that saves your career

git log shows the history of commits.

git reflog shows the history of HEAD movements.

That includes everything you do:

  • Branch checkouts
  • Resets
  • Merges
  • Rebases
  • Commits
  • Hopping between branches like crazy
bash
git reflog

Typical output (simplified):

bash
1a2b3c4 (HEAD -> main) HEAD@{0}: commit: Fix error logging 9f8e7d6 HEAD@{1}: reset: moving to HEAD~1 abcd123 HEAD@{2}: commit: Remove legacy code ...

Each line is a frame in your time-travel movie.

2.1. Recovering a commit you "deleted"

Classic scenario:

bash
# You got emotional git reset --hard HEAD~3

Now what?

  1. Check the reflog:
bash
git reflog
  1. Find the entry before the reset — something like:
bash
abcd123 HEAD@{3}: commit: Feature you just wiped
  1. Go back to that commit by creating a rescue branch:
bash
git checkout -b saving-my-life abcd123

Boom. Your "deleted" commit is back.

2.2. Recovering a deleted branch

You nuked a remote and local branch:

bash
git branch -D feature-top

If you worked on it recently, the last commit will still show up in the reflog.

  1. Look for something with the branch name or commit hash:
bash
git reflog --all | grep feature-top || git reflog --all | head -n 30
  1. Once you find the commit, recreate the branch on top of it:
bash
git checkout -b feature-top-v2 <commit-hash>

Reflog is basically Git's extended Ctrl+Z. Use it freely — it's read-only.

Spicy take: if you don't know how to use git reflog, you're not ready to use git reset --hard.


3. git reset vs git revert: when to erase, when to undo

These two confuse even senior devs. Let's make it clear.

3.1. git reset

reset changes pointers and, depending on the mode, your working directory.

Three main modes:

  • --soft: only moves the branch pointer; keeps changes staged.
  • --mixed (default): moves the pointer and unstages files, but keeps them in the working directory.
  • --hard: moves the pointer and discards everything — stage + working directory.

Examples:

bash
# Go back 1 commit, keeping changes staged git reset --soft HEAD~1 # Go back 1 commit, keeping changes as local modifications (unstaged) git reset HEAD~1 # Go back 1 commit, throwing code away for real git reset --hard HEAD~1

Golden rule:

  • Use reset to rewrite local history.
  • Avoid reset on branches that are already shared (pushed).

3.2. git revert

revert does not delete a commit. It creates a new commit that undoes the changes made by another commit.

bash
# Undo the last commit by creating a new one git revert HEAD # Undo a specific commit git revert a1b2c3d

Perfect for:

  • Fixing mistakes already pushed to remote
  • Undoing a bad merge on main/master
bash
# Revert a specific merge commit git revert -m 1 <merge-commit-hash>

-m 1 indicates which parent is considered the mainline (usually the target branch, like main).

3.3. Quick guide: which one to use?

  • Working locally, before pushing:
    • Use reset freely (but know your reflog).
  • After pushing:
    • Prefer revert.
    • Only use reset if EVERYONE agrees to rewrite history (git push --force-with-lease).
bash
# Shared history? Be an adult. # Instead of rewriting everything: git revert HEAD

Spicy take: git push --force is not evil. Using it without understanding what you're rewriting is.


4. Hunting bugs with git bisect (sniper mode)

You know that bug that "always worked" and broke at some point in the past?

You could guess commits… or you can use git bisect and find the culprit in O(log n) steps.

4.1. Concept

You need two things:

  • A good commit (where the bug didn't exist)
  • A bad commit (where the bug exists)

Git will test the middle until it finds the first bad commit.

4.2. Manual mode

bash
# Start bisect git bisect start # Mark current commit as bad git bisect bad # Mark an older commit as good git bisect good <hash-or-tag>

Git will place you on an intermediate commit.

You test your system:

  • If the bug is present:
bash
git bisect bad
  • If it's not:
bash
git bisect good

Repeat until Git tells you which commit introduced the bug.

When you're done:

bash
git bisect reset

4.3. Automatic mode (for smart lazy devs)

If you can automate the test (script that returns 0 for good and 1 for bad), Git will do the whole search for you.

bash
# Example of a simple test script cat << 'EOF' > test-bug.sh #!/usr/bin/env bash # run tests, lints, or anything that proves the bug exists npm test > /dev/null 2>&1 EOF chmod +x test-bug.sh # Now run automated bisect git bisect start git bisect bad HEAD git bisect good v1.0.0 git bisect run ./test-bug.sh

Git will jump across commits on its own.

This sounds like overkill… until you hit a weird bug in production.


5. Disaster survival checklist

When things go wrong, instead of panicking, follow this sequence:

  1. Don't freak out. Seriously.

  2. Run:

    bash
    git status git log --oneline --graph --decorate --all -n 20 git reflog -n 20
  3. Identify:

    • What do you want to go back to (commit, branch, merge)?
    • Has this history already been pushed?
  4. If it's local only:

    • Use git reset (soft/mixed/hard as needed).
    • If you reset too much, recover with git reflog.
  5. If it's already on remote:

    • Prefer git revert.
    • Talk to the team before using git push --force-with-lease.
  6. If you need to find which commit introduced a bug:

    • Use git bisect.

6. Time-travel aliases for power users

Add this to your ~/.gitconfig to live a better life:

ini
[alias] lg = log --oneline --graph --decorate --all undo-last = reset --soft HEAD~1 undo-hard = reset --hard HEAD~1 hist = reflog who-broke = bisect

Typical usage:

bash
# see a pretty history git lg # regret the last commit (but keep the code) git undo-last # post-disaster inspection git hist

7. Cheat sheet to pin on your wall

  • git log --graph --decorate --all to understand history.
  • git reflog to recover "lost" commits and branches.
  • git reset to rewrite local history.
  • git revert to undo things in shared history.
  • git bisect to find the commit that introduced a bug.

Use Git as a time machine, not as Russian roulette.


If you enjoyed this deeper dive into Git internals, share it with that friend who's still afraid of git reset.

Find me on X/Twitter: @gss_62

#git #gittips

Receive new articles

Subscribe to receive notifications about new articles directly to your email

We won't send spam. You can unsubscribe at any time.