Git, CI/CD & GitOpsXI · RebasingFoundations
What rebase does — replaying commits on a new base
What you'll learn
- Explain what `git rebase <upstream>` does to the branch DAG in terms of replaying commits
- Identify why each replayed commit receives a new OID even when its diff is unchanged
- Distinguish a replay (cherry-pick loop) from a merge (three-way combine)
- Predict the topology before and after a rebase from the upstream and branch tips alone
- Recognise the local-vs-shared boundary that determines when rebase is safe
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
git rebase is the operation that turns a diverged branch into a
linear extension of its upstream. It does not combine two tips the
way a merge does; it replays each commit from the current branch
on top of a new base, one commit at a time, as if each commit had
been authored against the new tip from the start. The cost is that
every replayed commit gets a new OID. The benefit is a history that
reads as a straight line. Understanding what rebase does — and what
it does not do — is the prerequisite for everything else in this
part.
What rebase does in one sentence
git rebase <upstream> finds the merge base of the current branch
and <upstream>, then replays each commit from the current branch
that is not on <upstream> onto the tip of <upstream>, in order,
one at a time. After the rebase, the current branch points at the
last replayed commit, which has <upstream>’s tip as its parent.
git checkout feature/iam-rotation
git rebase main
# Successfully rebased and updated refs/heads/feature/iam-rotation.
The verb is replay, not combine. A merge takes two tips and produces a single merge commit whose tree is a three-way combination. A rebase takes one branch and rewrites its commits so that they sit on a different parent. The merge creates a node; the rebase moves commits.
Topology before and after
The two diagrams below show the same repository before and after
git checkout feature/iam-rotation && git rebase main. The commits
on feature/iam-rotation (F1, F2, F3) move from sitting on
top of C (the old base) to sitting on top of M2 (the new base).
flowchart LR
subgraph BEFORE["before rebase"]
C["C (merge base)"] --> M1["M1 (main)"]
C --> F1["F1 (feature)"]
F1 --> F2["F2 (feature)"]
F2 --> F3["F3 (feature tip)"]
end
subgraph AFTER["after git rebase main"]
C2["C (gone from branch view)"] --> M1b["M1"]
M1b --> M2["M2 (new main tip)"]
M2 --> R1["R1 (was F1)"]
R1 --> R2["R2 (was F2)"]
R2 --> R3["R3 (was F3, new tip)"]
end
Two things changed:
- The parent chain of the feature commits now ends at
M2instead ofC. The first parent ofR1isM2, notC. - The OIDs of
R1,R2,R3are different fromF1,F2,F3even though their diffs are identical, because a commit’s OID is a hash of (tree, parents, author, committer, message).
The original commits F1, F2, F3 still exist in the object
store — nothing in a rebase deletes objects — but they are no
longer reachable from the branch. The reflog and git reflog feature/iam-rotation retain a record of the previous tip until the
reflog entries expire (default 90 days).
Replay is a cherry-pick loop
The mechanism by which rebase moves a commit is a sequence of
git cherry-pick operations. For each commit on the original
branch that is not on the upstream, Git runs the equivalent of:
git cherry-pick $ORIGINAL_COMMIT_OID
A cherry-pick is itself a three-way merge: given the cherry-picked commit, its parent, and HEAD, Git computes the merge base (the cherry-picked commit’s parent), takes the diff from base to cherry-picked commit, and applies that diff to HEAD. The resulting commit has HEAD as its parent and the cherry-picked diff as its tree. That is why a replayed commit’s parent changes — the merge base of the cherry-pick is still the original parent, but the new commit’s parent is HEAD at replay time.
# Equivalent to what rebase does internally, one commit at a time:
git checkout main
git cherry-pick F1 # creates R1 with parent = M2
git cherry-pick F2 # creates R2 with parent = R1
git cherry-pick F3 # creates R3 with parent = R2
git branch -f feature/iam-rotation
A conflict during a rebase is a conflict during the corresponding
cherry-pick: the diff from base to original-commit cannot be applied
cleanly to the new base, so the working tree is left with conflict
markers and the rebase pauses. The conflict resolution path is the
same as for a cherry-pick: edit, git add, then git rebase --continue.
The local-versus-shared boundary
Rebase is safe when the commits being rewritten are not yet visible to anyone else. A feature branch that exists only in the local repository can be rebased freely; the reflog retains the previous tips until the entries expire, and the rewritten OIDs replace the old OIDs from the perspective of that one local ref.
Rebase is dangerous when the commits being rewritten are already on
a remote, or have been pulled by a teammate, or are referenced by
a signed tag, or are pinned by an external system. Once another
repository holds the old OIDs, replacing them with new OIDs in
the upstream repository breaks every reference that points at the
old OIDs. The mechanism that pushes the new OIDs is git push --force (or, safely, --force-with-lease), covered in XI-06.
The rule that recurs throughout this part:
Never rebase commits that have been pushed to a shared branch and might have downstream consumers.
The corollary:
Rebase freely while a feature branch is local. Push the rebased branch once, with
--force-with-lease, only after confirming no downstream consumer holds the old OIDs.
When rebase is the right verb
The cases where rebase is the canonical operation:
- A feature branch has fallen behind
mainand the engineer wants the feature’s commits to sit on top of the currentmaintip rather than an old one, so the eventual merge intomainis a fast-forward. - An engineer wants to clean up local commits (squash, fixup, reword, drop) before opening a pull request.
- A pull request has had review feedback and the engineer wants to fold review-fixup commits into the original commits before merging (autosquash workflow, XI-05).
git pull --rebaseis configured so a local branch’s pending commits are replayed on top of the fetched remote tip instead of being merged.
The cases where rebase is the wrong verb:
- The branch has been pushed and is used by teammates. Rebase + force-push rewrites OIDs the teammates already hold.
- The branch is
mainor another trunk branch. Rebasing a trunk rewrites every commit reachable from it. - The repository has signed tags pointing at commits that would be rewritten. Signed tags commit to a specific OID; rewriting the OID invalidates the signature’s referent.
Production discipline
- Rebase only local, unpushed commits. The first rule of rebasing. The reflog is your safety net for local rebases; it is not a safety net once commits are pushed.
- Confirm with
--force-with-lease, not--force, when pushing a rebased branch.--force-with-leaserefuses the push if the remote’s branch tip has moved since your last fetch, which is the guard rail that catches “I am about to clobber a teammate’s commit”.--forcedoes not have that guard. - Treat the replayed branch as a new branch for downstream
consumers. After
git rebase main+git push --force-with-lease, any tag, comment, or external pin that referenced the old OIDs is stale and must be updated. - Never rebase commits referenced by signed tags. A signed tag’s signature commits to a specific OID; rewriting the OID invalidates the referent. The signature remains cryptographically valid against the new OID, but the meaning of the tag — “this object” — is lost.
Cross-course references
- GitOps with Argo CD - Part VI (MergeStrategies) maps the rebase-versus-merge trade-off onto GitOps sync policies: a sync policy that prefers rebase produces a linear cluster history, while a sync policy that prefers merge preserves the topology of in-cluster changes.
- CI/CD Pipeline Patterns - Part V (MergeQueues) describes merge queues that rebase each pull request onto the current trunk tip before building, so the build runs against the same parent the eventual merge will use.
- Terraform for Production Sysadmins - Part XI (PRWorkflows)
uses rebase as the standard operation before merging a Terraform
plan branch, so the merge into
mainis a fast-forward and the state file remains consistent with the new tip.
Quiz
Knowledge check · 4 questions
Q1. What is the operation that `git rebase <upstream>` performs on each commit of the current branch?
Q2. A rebase is not safe to run on commits that have already been pushed to a shared branch, as long as the engineer remembers to run `git push --force` afterwards.
Q3. Why does each commit replayed by `git rebase` receive a new OID, even when its tree and message are unchanged?
Q4. Decide whether rebase is the right operation, and if so, identify the exact commands to run in order.
An engineer has a local feature branch `feature/iam-rotation` that is three commits ahead of `main`. The branch has never been pushed. Meanwhile, two new commits have landed on `main` since the branch was cut. The engineer wants to merge the feature into `main` and have the merge be a fast-forward so the audit log shows the feature's commits as a clean linear extension of `main`.
Passing score: 75%. Answers are checked in this browser.