Keep a topic branch current
Your PR branch has fallen behind main. Rebasing it onto origin/main gives a linear history with no merge commit when it is merged.
Git · Command
git rebase takes the commits that are unique to a branch and replays them on a different base commit, creating new commits. It can also rewrite a branch in place by reordering, editing, squashing or dropping commits in interactive mode.
Last updated
First, rebase lists the commits on your branch that are not already in <upstream>. Commits whose changes are already upstream are left out. Next, it detaches HEAD at the new base, which is <upstream> or the --onto target. It then replays each listed commit in order, much like running cherry-pick on each one. Finally, it moves the branch ref to the last replayed commit. If you don't give an upstream, it uses the branch's configured upstream and assumes --fork-point. The default backend is merge, which uses the ort strategy. The older apply backend, based on git-am, is still available with --apply.
git rebase [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase> | --keep-base] [<upstream> [<branch>]]
git rebase [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>] --root [<branch>]
git rebase (--continue|--skip|--abort|--quit|--edit-todo|--show-current-patch)| Name | Type | Description |
|---|---|---|
<upstream> | commit-ish | The branch or commit to compare against. Any valid commit works. If omitted, the branch's configured upstream is used. |
<branch> | branch name | The branch to rebase. Defaults to HEAD. If you give it, rebase checks it out first. |
--onto <newbase> | commit-ish | Where the new commits will be created, if different from <upstream>. 'A...B' is shorthand for the merge base of A and B. |
--keep-base | flag | Replays the commits onto the merge base of <upstream> and <branch>, so the branch's base stays the same. Implies --reapply-cherry-picks and --no-fork-point. |
-i, --interactive | flag | Opens the todo list of commits in an editor so you can change it before the rebase runs. |
--continue | --skip | --abort | --quit | mode flag | Control a rebase that has stopped. --continue resumes after you resolve conflicts, --skip drops the current patch, --abort restores the original branch, and --quit stops without resetting HEAD. |
--autostash | flag | Stashes uncommitted changes before the rebase and reapplies them afterwards. |
On success, <branch> is the current branch and points at the rewritten commits. If a conflict, an 'edit' step or a failed exec stops the rebase, it pauses and waits for --continue, --skip or --abort.
#!/usr/bin/env bash
set -euo pipefail
# Bring the latest main into this repo
git fetch origin
# Work on the feature branch
git switch feature
# Record a fix aimed at an earlier commit
git commit --fixup=HEAD~2 -am ""
# Replay feature onto origin/main, squash fixups,
# and run the tests after every rewritten commit
git rebase -i --autosquash --autostash \
--exec "npm test" origin/main
# If it stops on a conflict:
# git status # see the conflicted files
# git add <file> # mark each one resolved
# git rebase --continue # or --skip / --abort
# Compare the old and new history
git range-diff @{1}...HEAD
# Publish the rewritten branch safely
git push --force-with-leaseThe script moves a feature branch onto the updated origin/main, folds a fixup commit into its target, runs the tests at each step, and compares the result with the old tip from the reflog before force-pushing.
Your PR branch has fallen behind main. Rebasing it onto origin/main gives a linear history with no merge commit when it is merged.
Use an interactive rebase to reword unclear messages, squash work-in-progress commits and drop debugging commits before you open a pull request.
A feature was started on 'next' but now needs to ship from 'master'. 'git rebase --onto master next topic' moves only the feature's own commits.
To cut two broken commits out of the middle of a branch, rebase the commits after them onto the commit just before them with --onto.
Rebase finds your branch's commits that are missing from the upstream, then replays them one by one on the new base, like a series of cherry-picks. The results are new commits with new IDs, and the branch ref is moved to them. A merge keeps the existing commits and adds a merge commit. Rebase rewrites history to make it linear, which is why the docs warn against rebasing branches that others build on.
Follow-up: Why can an unchanged rebase still create new commits? With -f/--force-rebase (implied by options like --committer-date-is-author-date), rebase replays every commit instead of fast-forwarding over the ones that are unchanged, so every commit gets a new ID.
First, C is checked out. The commits in B..C, meaning those reachable from C but not from B, are chosen for replay. They are then replayed on top of A. Here B decides which commits move and A decides where they land. This lets you detach a topic from an intermediate branch, or delete a range with something like 'git rebase --onto X~5 X~3 X'.
Follow-up: How is --keep-base different from --fork-point? Both use a merge base, but --keep-base uses it as the place where new commits are created, while --fork-point uses the reflog to decide which commits get rebased.
While the rebase is paused, 'git rebase --show-current-patch' shows the commit being applied (it is the same as git show REBASE_HEAD). Remember that 'ours' is the upstream side here, not your branch. If things go badly, --abort resets HEAD to the original branch. After a finished rebase, the old tip is still in the branch reflog at @{1}.
Follow-up: How can you avoid resolving the same conflicts again on later rebases? Turn on git rerere so that recorded resolutions are reused, and use --no-rerere-autoupdate to check the reused resolution before it is staged.
git cherry-pickgit mergegit commit --fixupgit refloggit range-diffgit merge-basegit pull --rebasegit rerere