Skip to content

Keep a linear history: block merge, fixup and squash commits

This is a principle

A reusable technical claim: something I would want true in any of my work.

Claim. Enforce a rebase-only, linear history by actively blocking merge commits and un-squashed fixup!/squash! commits from landing on the main branch.

Why. A linear history is dramatically easier to read, bisect, revert and reason about than one braided with merge commits. fixup!/squash! commits are scaffolding for interactive rebase - if they reach the base branch, the author forgot to autosquash, leaving noise in permanent history. Rather than hoping contributors self-police, a CI guard makes the policy mechanical: the PR simply cannot merge until the history is clean.

Snippet.

# CI: fail if any commit in the PR is a merge commit
for commit in pr_commits:
    if len(commit["parents"]) > 1:
        raise RuntimeError("Merge commits are not allowed; please rebase.")
# (a sibling check greps commit messages for 'fixup!' / 'squash!')

How enforced. Two inline-Python steps in the governance workflow inspect the PR's commits via the GitHub API. A domain-specific instance of Guard invariants at commit-time, not review-time, enforced in CI where it can't be bypassed.

The following pages link to this page: