Linear History

Linear History and Rebase-Only Policies

A linear main, one commit after another with no merge commits, reads like a changelog, bisects without following side branches, and reverts cleanly. On your own machine, git config set merge.ff only makes git merge refuse anything but a fast-forward, and pull.ff only (set globally in First-Time Setup) does the same for git pull. A policy, however, belongs on the server, so Sam extends the update hook of Requiring Signatures, then writes the workflow of Choosing a Workflow into CONTRIBUTING.md and tries to land it with a merge commit:

Refusing merge commits on main, then landing a branch by fast-forwardShell
cat >> ../server/booknest.git/hooks/update <<'HOOK'
# ...and only a linear history: merge commits are refused on main
git rev-list --merges "$old..$new" | grep -q . &&
  { echo "error: main takes no merge commits; rebase and fast-forward instead"; exit 1; }
exit 0
HOOK
git switch -qc docs/workflow
# Create CONTRIBUTING.md: the branching workflow and commit conventions
git add CONTRIBUTING.md && git commit -qm "docs: describe the branching workflow"
git switch -q main
git merge -q --no-ff docs/workflow -m "Merge branch 'docs/workflow'"
git push -q; echo "exit status $?"
git reset -q --hard origin/main && git merge --ff-only docs/workflow
git push -q && git branch -d docs/workflow
Output
...
remote: error: main takes no merge commits; rebase and fast-forward instead
remote: error: hook declined to update refs/heads/main
...
 ! [remote rejected] main -> main (hook declined)
...
exit status 1
Updating 92f2964..c1085fd
Fast-forward
 CONTRIBUTING.md | 11 +++++++++++
...
Deleted branch docs/workflow (was c1085fd).

--merges lists only commits with more than one parent, and the final exit 0 matters: without it, the grep that found nothing would become the hook's failing exit status. Resetting to origin/main threw away the refused merge commit, and the fast-forward needed no rebase because main had not moved since the branch was cut; when it has, rebase the branch first (Rebasing onto Main) and re-test. Hosts offer the same rule as "require linear history" (GitHub).