Your feature branch was current last week. Then main moved. Stay Off Main already taught git merge origin/main to catch up — safe, and it adds a merge commit. If you want a straight line of feature commits sitting on the new main instead, you replay those commits on the updated base. That is rebase: powerful, but easy to misuse on a branch others already pulled.

Day-one snapshots live in Git Without the Panic. Branch create, switch, and -u live in Stay Off Main. Conflict markers live in When Push Fails. This page is the replay.

Warm-up: two-clone lab

You need a fake remote, Alice to advance main, and Bob with unique feature commits — the drift rebase is built for. Create a bare repository and two clones:

mkdir -p git-rebase-lab
cd git-rebase-lab
git init --bare remote.git

git clone remote.git alice
git clone remote.git bob

Seed main from Alice and publish it:

cd alice
echo 'App v1' > README.md
git add README.md
git commit -m "Initial README"
git branch -M main
git push -u origin main

On Bob, update main, start a feature branch, and commit work that exists only there:

cd ../bob
git pull origin main
git switch -c feature/docs
echo 'Docs outline' > docs.txt
git add docs.txt
git commit -m "Add docs outline"

Alice then moves main and pushes — a change Bob does not have yet:

cd ../alice
echo 'App v2' >> README.md
git add README.md
git commit -m "Bump README to v2"
git push

Bob’s feature/docs still sits on the old README commit. Alice’s new commit is only on origin/main. Use alice/ and bob/ for the scenarios below. Delete git-rebase-lab when you finish.

Note: A bare repo is only for this lab. On GitHub or GitLab the remote already exists; origin points there the same way. Prefer git switch for changing branches.

Mental model: replay, not a new kind of commit

A branch name is still a pointer to a commit. Rebase does not invent a parallel universe. It copies the commits that are unique to your feature branch and reapplies their patches on a new base — here, the tip of origin/main. Your branch label then moves to the last replayed commit. The old copies are left behind.

Two ways to catch up with main:

  • Merge (git merge origin/main) — keeps both lines and adds a merge commit. Safe when others already have your feature branch.
  • Rebase (git rebase origin/main) — looks like you started from the new main all along. Linear history; the replayed commits get new hashes.

git fetch downloads Alice’s commit without touching Bob’s files. Then rebase applies Bob’s unique work on top. Stay on the feature branch: git rebase origin/main means “replay this branch onto origin/main.”

Note: Run rebase on feature/docs, not on main. Replaying main itself rewrites the shared default — do not do that.

Hero use case: fetch, then rebase onto origin/main

Bob wants feature/docs current with main without a merge commit. Stay in bob/ on feature/docs.

See the fork

Download remote commits and look at the graph before you rewrite anything:

cd ../bob
git fetch origin
git log --oneline --decorate --graph -n 10

You should see two tips: Bob’s feature commit, and origin/main with Alice’s README bump:

* a1b2c3d (HEAD -> feature/docs) Add docs outline
| * e4f5g6h (origin/main, origin/HEAD) Bump README to v2
|/
* 9aa0001 (main) Initial README

Replay onto origin/main

Rebase the current branch onto the fetched main:

git rebase origin/main

Git reapplies “Add docs outline” on top of Alice’s commit. A clean replay prints something like:

Successfully rebased and updated refs/heads/feature/docs.

Confirm the line is straight — feature commit above origin/main, no merge commit:

git log --oneline --decorate --graph -n 10
* c7d8e9f (HEAD -> feature/docs) Add docs outline
* e4f5g6h (origin/main, origin/HEAD) Bump README to v2
* 9aa0001 (main) Initial README

The docs commit has a new hash. Same message and same file change; different parent. That is the replay.

Note: If Git refuses because you have uncommitted edits that would be overwritten, park them with Git Stash and retry — rebase wants a clean tree. Do not start a rebase with leftover edits you still need.

If this feature branch was never pushed, you are done locally. Publish later with the same git push -u loop from Stay Off Main. If you already pushed the old feature commits, a normal git push will be rejected — rewrite only a private branch, and never main.

Conflicts: continue or abort

Rebase pauses the same way a merge does when the same lines changed. Markers are the same as in When Push Fails; this page does not re-lecture how to pick the final text.

Recreate an overlap. On Alice, edit README.md and push:

cd ../alice
echo 'Alice: release notes' >> README.md
git add README.md
git commit -m "Add Alice release notes"
git push

On Bob, edit the same file on feature/docs without fetching first:

cd ../bob
echo 'Bob: docs banner' >> README.md
git add README.md
git commit -m "Add Bob docs banner"

Fetch and rebase again:

git fetch origin
git rebase origin/main

Git stops in README.md. Open the file. One extra gotcha versus merge: HEAD is the new base (origin/main, Alice), and the other side is the feature commit being applied (Bob). Delete the marker lines after you choose the result.

Example of a finished file:

App v1
App v2
Alice: release notes
Bob: docs banner

Stage the file and continue the rebase — Git keeps the original commit message unless you change it:

git add README.md
git rebase --continue

Instead of continuing, abort returns you to the pre-rebase feature tip. Your local commits remain; only the in-progress replay is canceled:

git rebase --abort

Note: Finish with --continue after git add, or walk away with --abort. Do not git commit to “finish” a rebase the way you finish a merge — the replay is still in progress until --continue completes.

When to merge instead

Rebase is for a private feature branch you have not shared — or that nobody else has pulled. The moment teammates built on your old feature commits, those hashes are part of their history.

Do not rebase a branch others already pulled, and never rebase origin/main (or local main you share). Rewriting their base forces everyone to recover. Catch up the shared way you already know:

git switch feature/docs
git fetch origin
git merge origin/main

That merge commit is the honest record that two published lines met. Prefer it on a shared feature branch.

If you already pushed feature/docs, then rebased it locally, a normal push is rejected because the remote still has the old copies. That rejection is Git protecting rewritten history. Do not force-push main or anyone else’s branch. Republish your private rewritten feature only when you are sure nobody else has it:

git push --force-with-lease origin feature/docs

--force-with-lease still rewrites the remote feature branch. It refuses if the remote moved in a way you have not seen — slightly safer than --force, not a license to rebase shared work.

Note: Force-pushing over teammates’ commits causes more pain than a merge commit ever did. If they already pulled the branch, merge origin/main instead of rebasing.

Optional: interactive rebase is not this lab

git rebase -i origin/main opens an editor listing the commits about to be replayed so you can reorder or drop them before they land. That is a later skill, and it rewrites hashes the same way — still unsafe on a branch others pulled. This page’s drills stay on plain git rebase origin/main. Do not start combining commits until the fetch-and-replay loop is routine.

Quick reference card

GoalCommand
Download remote commitsgit fetch origin
Replay current branch onto updated maingit rebase origin/main
See branch tipsgit log --oneline --decorate --graph -n 10
Continue after resolving filesgit add <file> then git rebase --continue
Abort a conflicted rebasegit rebase --abort
Catch up without rewriting (shared branch)git merge origin/main
Republish a private rewritten featuregit push --force-with-lease origin feature/name
Park dirty work that blocks rebaseSee Git Stash

Practice drills

Stay inside git-rebase-lab (recreate the bare remote and clones if needed). Try these without peeking.

  1. Recreate the warm-up: Alice advances main and pushes; Bob has unique commits on feature/docs. On Bob, fetch and git rebase origin/main.
  2. Confirm with the graph that feature/docs sits on top of origin/main with no merge commit, and that the feature commit hash changed.
  3. Make Alice and Bob both edit README.md, fetch, rebase, resolve markers so both lines remain, then --continue.
  4. Start a conflicting rebase on Bob, abort with git rebase --abort, and confirm git status is clean of rebase state.
  5. Treat feature/docs as already pulled by a teammate: fetch and git merge origin/main instead of rebasing.

Solid answers — clear and portable, not the only ones:

# 1–2 (from bob/, on feature/docs, after alice pushed)
git fetch origin
git rebase origin/main
git log --oneline --decorate --graph -n 10

# 3 (from bob/, after both edited README.md)
git fetch origin
git rebase origin/main
# edit README.md: remove markers, keep both lines
git add README.md
git rebase --continue

# 4
git fetch origin
git rebase origin/main
git rebase --abort
git status

# 5 (shared feature branch — do not rewrite)
git fetch origin
git merge origin/main

If those five feel routine, you already practice the team habit: rebase a private feature onto updated main for a linear history, continue or abort through the same markers you know, and merge instead when the branch is shared. Snapshot basics remain in Git Without the Panic. The branch loop remains in Stay Off Main. When the same lines collide, markers are unchanged from When Push Fails. Next is review on the host before main moves.

Next optional step Open a pull request on the host so someone else reviews before main moves. Review Before Merge: Pull Requests on the Host Without the Guesswork