Skip to content

Lab: Repair a Broken Interactive Rebase

Lesson 3 of 4Intermediate3 min readHands-On Git & GitHub Labs · Workflow Labs
Time20 minutes
LevelIntermediate
You needLocal Git only — no GitHub account needed

You are squashing three messy commits before opening a pull request. The rebase stops on a conflict. You resolve it, continue, and it stops again on the next commit.

Then you lose track of where you are, and the branch looks wrong.

This is the situation where people force-push something destructive out of confusion. It is entirely recoverable, and this lab is about making that recovery familiar.

Drive an interactive rebase through multiple conflicts, use --continue, --skip and --abort deliberately, and recover the original branch from the reflog after a rebase you decide was a mistake.

Terminal window
mkdir -p /tmp/lab-rebase && cd /tmp/lab-rebase
git init -q .
git config user.email "lab@example.com"
git config user.name "Lab User"
echo "line 1" > app.txt
git add . && git commit -q -m "Initial commit"
git switch -qc feature/cleanup
echo "line 2 draft" >> app.txt && git commit -qam "WIP: first attempt"
sed -i 's/line 2 draft/line 2 revised/' app.txt && git commit -qam "WIP: fix attempt"
sed -i 's/line 2 revised/line 2 final/' app.txt && git commit -qam "WIP: actually final"
git switch -q main
echo "line 2 from main" >> app.txt && git commit -qam "Main edits the same line"
git switch -q feature/cleanup
git log --oneline --all --graph

Three throwaway commits on the branch, all touching the same line, and main has changed that line too. Rebasing onto main will conflict repeatedly.

Record where you started — you will need it:

Terminal window
git rev-parse HEAD
  1. Start the rebase. To keep it scriptable, set a non-interactive editor that squashes the second and third commits into the first:

    Terminal window
    GIT_SEQUENCE_EDITOR="sed -i '2,3s/^pick/squash/'" git rebase -i main

    It will stop on a conflict almost immediately.

  2. Read where you are. git status tells you which commit is being applied and what to do next. Run it — the message is more useful than people expect.

  3. Resolve the conflict. Edit app.txt to a sensible final state with no markers, then:

    Terminal window
    git add app.txt
    git rebase --continue
  4. Handle the next stop. It may conflict again, or open an editor for the squashed message. If an editor opens, save and close it.

  5. Deliberately break it. Once the rebase completes, decide it was wrong. Look at the log, then recover the pre-rebase branch:

    Terminal window
    git reflog
    git reset --hard <the-sha-you-recorded>
    git log --oneline

    Confirm all three original commits are back.

  6. Do it again, then abort mid-flight:

    Terminal window
    GIT_SEQUENCE_EDITOR="sed -i '2,3s/^pick/squash/'" git rebase -i main
    # stop at the first conflict, then:
    git rebase --abort
    git log --oneline

    Confirm you are exactly where you began.

“I don’t know if a rebase is in progress.”

Terminal window
git status
ls .git/rebase-merge .git/rebase-apply 2>/dev/null

Either directory existing means a rebase is unfinished.

--continue says there are no changes. Your resolution matched what was already there, so the commit would be empty. Either git rebase --skip to drop it, or check you actually saved.

Which side is which during a rebase? Rebase replays your commits onto the base, so the sides are reversed relative to a merge: HEAD is the base you are replaying onto, and the incoming side is your commit. This trips up almost everyone.

Terminal window
ORIG=$(git rev-parse HEAD)
GIT_SEQUENCE_EDITOR="sed -i '2,3s/^pick/squash/'" git rebase -i main
# CONFLICT (content): Merge conflict in app.txt
printf 'line 1\nline 2 final\n' > app.txt
git add app.txt
git rebase --continue
# repeat resolve/continue if it stops again
git log --oneline # squashed result
# Recover the original branch:
git reset --hard "$ORIG"
git log --oneline # three commits back
# Or, without having recorded it:
git reflog
git reset --hard HEAD@{n} # the entry before "rebase (start)"

A rebase is a sequence of cherry-picks. Each original commit is replayed onto the new base in turn. That is why conflicts arrive one commit at a time — each replay is its own three-way merge — and why a five-commit rebase can conflict five times on the same line.

--continue, --skip, --abort are the three exits. Continue after resolving; skip to drop the commit being applied; abort to restore the original branch entirely.

--abort is completely safe while a rebase is running. It restores the branch to the commit recorded in ORIG_HEAD, exactly as it was.

After a rebase finishes, the reflog is the escape. The original commits are unreferenced but still present. git reset --hard to the pre-rebase SHA restores the branch — this is the same mechanism as Lab 01.

Squashing rewrites SHAs. Every replayed commit is a new object. This is exactly why the rule is rebase before you share.

Terminal window
cd /tmp && rm -rf lab-rebase

Debug a failing GitHub Actions workflow — move from local Git to CI.

Choose a learning pathA sequenced route through the curriculum for wherever you are now.