Skip to main content
RunBook Academy

Git, CI/CD & GitOpsXII · Merge vs RebaseFoundations

When to rebase — local branches, cleanup, and pre-merge replay

Advanced⏱ ~20 mingit

What you'll learn

  • Identify the four scenarios where rebase is the right verb for an infrastructure repository
  • Apply `git rebase <upstream>` to replay local commits onto a new upstream tip
  • Use `git rebase -i` to clean up a feature branch before review (squash, fixup, reorder)
  • Recognise the boundaries that make each rebase scenario safe (local branch, no pushed OIDs, no signed tags)
  • Configure `git pull --rebase` and `branch.autosetuprebase` for the rebase-on-pull policy

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

Not yet marked complete on this device.

Rebase is the right verb when the engineer wants the branch’s commits to sit on top of a new upstream tip — a linear history — and when the OIDs of those commits are not held by anyone outside the local repository. The four scenarios in an infrastructure repository are: (1) a local branch that has never been pushed, (2) a feature branch being cleaned up before code review, (3) a feature branch being replayed onto the trunk tip just before merging, and (4) any branch on which git pull should fetch-and-rebase instead of fetch-and-merge. Each scenario is bounded by the same rule: no downstream consumer may hold the OIDs that rebase would rewrite.

Local unpushed branches

The simplest rebase scenario: a branch exists only on the engineer’s laptop. No one has fetched it, no CI runner has built against it, no artifact has been pinned to it, no signed tag points into it. The engineer wants to bring the branch up to date with a new commit on main.

git checkout feature/iam-rotation
git fetch origin
git rebase origin/main
# Successfully rebased and updated refs/heads/feature/iam-rotation.
flowchart LR
    subgraph BEFORE["before rebase"]
        C["merge base"] --> M["main tip (M1)"]
        C --> F1["F1 (feature)"]
        F1 --> F2["F2"]
        F2 --> F3["F3 (feature tip)"]
    end
    subgraph AFTER["after rebase"]
        C2["merge base"] --> M2["new main tip (M2)"]
        M2 --> R1["R1 (was F1)"]
        R1 --> R2["R2 (was F2)"]
        R2 --> R3["R3 (was F3)"]
    end

The branch’s three commits are replayed onto the new main tip M2. They receive new OIDs (R1, R2, R3) because the parent chain changed. The original commits (F1, F2, F3) are still in the local object store, reachable from the reflog; they are unreachable from the branch but recoverable within the reflog-retention window (90 days by default locally).

The boundary that makes this safe: no one outside the local clone has ever observed F1, F2, or F3. There is nothing to break, nothing to coordinate, nothing to recover from a remote reflog. The rebase is contained.

Feature branches before review

The second scenario: the engineer has pushed a feature branch for review, but the rebase is performed before the review consumes the branch. The team has agreed that the contributor owns the branch until the first reviewer comment, and rebases during this window are safe with notification.

git checkout feature/iam-rotation
git fetch origin
git rebase -i origin/main
# interactive rebase opens in $EDITOR
# pick   R1 Add IAM role
# squash R2 Fix typo in role name
# pick   R3 Update trust policy

The interactive rebase (-i) is the cleanup tool: it lets the engineer reorder commits, squash fixups into the commits they fix, reword commit messages, and drop commits entirely. The resulting branch has a clean history — one commit per logical change — that is easier to review and easier to revert if a single change needs to be backed out.

The boundary that makes this safe: the rebase happens before the reviewer has fetched the branch and started work. Once the reviewer has the branch’s OIDs locally, a force-push of the rebased branch is a divergence event for the reviewer. The discipline is to communicate in the PR (“I’m about to rebase, please refetch”) or to wait for the reviewer to fetch first.

History cleanup with autosquash

A variant of the pre-review cleanup: the engineer has accumulated a sequence of small fixup commits (fixup!, squash! prefixes) that they want autosquash to fold into the commits they fix. git rebase -i --autosquash recognises the fixup! and squash! prefixes and arranges the todo list automatically.

# Make a fixup commit that fixes an earlier commit
TARGET_SHA=R1
git commit --fixup=$TARGET_SHA
# 9a8b7c6 fixup! Add IAM role

# Autosquash rebase onto the upstream
git rebase -i --autosquash origin/main
# pick   R1 Add IAM role
# fixup  9a8b7c6 fixup! Add IAM role     (auto-arranged)
# pick   R3 Update trust policy

The branch’s history is cleaned up in one operation: the fixup is folded into the commit it fixes, the target commit gets a fresh OID, and the resulting history is one logical commit per change. The boundary is the same as the pre-review scenario: no reviewer has fetched, no downstream consumer holds the OIDs.

Pre-merge replay onto the trunk tip

The third scenario: the feature branch has been reviewed, approved, and is about to be merged into main. Before the merge, the engineer rebases the branch onto the latest main tip so that the merge can be a fast-forward (no merge commit) and the resulting main history is a single straight line.

git checkout feature/iam-rotation
git fetch origin
git rebase origin/main
# Successfully rebased and updated refs/heads/feature/iam-rotation.

# Now the merge is a fast-forward
git checkout main
git merge feature/iam-rotation
# Updating M2..R3
# Fast-forward
flowchart LR
    subgraph REPLAY["after rebase + fast-forward merge"]
        M3["main tip (M2)"] --> R1b["R1"]
        R1b --> R2b["R2"]
        R2b --> R3b["R3 (also new main tip)"]
    end

The trunk history is now a single straight line: M2, R1, R2, R3. There is no merge commit, no merge bubble in git log --graph. The branch event is gone from the graph — it survives only in the PR system and the contributor’s memory.

The boundary that makes this safe: the rebase happens at the moment of merge, when the contributor is the only consumer of the branch. After the merge, the rebased commits are on main and have new OIDs; no one holds the old OIDs because the old OIDs were never on main. The force-push of the feature branch after the rebase is contained — the branch is about to be deleted anyway.

Rebase-on-pull policy

The fourth scenario is the everyday one: the engineer runs git pull on a feature branch to fetch the latest from the remote, and wants the local unpushed commits to sit on top of the remote’s new commits rather than be merged with them. The default git pull is fetch-and-merge; the rebase-on-pull policy changes the default to fetch-and-rebase.

# Per-branch: this branch always pulls with rebase
git config branch.feature/iam-rotation.rebase true

# Global default: every branch pulls with rebase unless overridden
git config --global pull.rebase true

# New branches automatically set up with rebase-on-pull
git config --global branch.autosetuprebase always

The branch.autosetuprebase = always setting applies to every branch created by git branch <name> <upstream> or git checkout -b <name> <upstream>: each new branch inherits the rebase-on-pull policy without an explicit git config branch.<name>.rebase true. The setting is the production-grade default for teams that have decided rebase is the right verb for feature branches.

Production discipline

  1. The rebase window closes at the moment of merge. A feature branch is safe to rebase from creation until the merge completes. After the merge, the branch’s original OIDs are unreachable from main and any further rebase would clobber nothing — but the branch itself should be deleted, not rebased further.
  2. Communicate before rebasing a branch anyone has fetched. A rebase of a branch a reviewer has already fetched is a divergence event for the reviewer. A note in the PR (“rebasing, please refetch”) is the minimum coordination.
  3. Configure branch.<name>.rebase per branch, not pull.rebase globally. The global setting affects trunks and is rarely what is wanted.
  4. Use git rebase -i --autosquash for cleanup, not manual squashing. Autosquash recognises the fixup! and squash! prefixes and arranges the todo list automatically, eliminating the manual-edit step that is the most common source of rebase mistakes.
  5. Never rebase a commit referenced by a signed tag. A signed tag commits to a specific OID; rewriting the OID invalidates the referent even though the signature itself remains cryptographically valid.

Cross-course references

  • GitOps with Argo CD - Part VI (MergeStrategies) maps the pre-merge rebase pattern onto GitOps: a feature branch is rebased onto the trunk tip just before merge so the eventual GitOps sync is a fast-forward, and the controller reads a linear history without merge bubbles.
  • CI/CD Pipeline Patterns - Part V (MergeQueues) uses the pre-merge rebase pattern automatically: each pull request is rebased onto the trunk tip in the merge queue’s temporary branch before the CI build, so the contributor’s branch OIDs are never rewritten.
  • Terraform for Production Sysadmins - Part XI (PRWorkflows) recommends git rebase origin/main on a Terraform plan branch just before merge, so the eventual merge into main is a fast-forward and the state file references remain consistent.

Quiz

Knowledge check · 4 questions

  1. Q1. An engineer is about to run `git rebase origin/main` on a feature branch that has been open for review for two days. Two reviewers have already commented on specific commits. What is the right course of action?

  2. Q2. Setting `git config --global branch.autosetuprebase always` is not a safe default for every repository, including those with long-lived trunk branches.

  3. Q3. List the four scenarios where rebase is the right verb for an infrastructure branch and name the boundary that keeps each scenario safe.

  4. Q4. Choose the right rebase scenario for three branches in flight and justify each choice using the local-vs-shared rule.

    An infrastructure engineer has three branches in flight. The first is `feature/iam-rotation`, a local branch that has never been pushed; it has fallen three commits behind `main`. The second is `feature/s3-policy`, a feature branch that was pushed yesterday for review; one reviewer has fetched it and left three comments on specific commits. The third is `release/2026-q3-prep`, a long-lived release branch shared with three teammates; it has fallen two commits behind `main` and is being prepared for a fast-forward merge into `main` later today.

Passing score: 75%. Answers are checked in this browser.