Writing a team merge policy Jump to heading

Every team eventually has the merge-versus-rebase argument, usually in a pull request thread at the worst possible moment. Without a written policy, each person follows their own habits: one squash-merges, another rebase-merges, a third merges with commits, someone force-pushes a shared branch. History becomes an inconsistent mix that serves nobody, and the argument repeats with every new team member. A merge policy settles it: a page that states how branches stay current, how pull requests land on main, when history may be rewritten, and how each rule is enforced. It does not need to be long β€” the useful ones fit on one screen β€” but it needs to be explicit and backed by settings, so it is followed by default. This page walks through writing one, within merge vs rebase decision matrix.

When to use this approach Jump to heading

  • Merge methods on main are inconsistent, or the team keeps re-debating them.
  • New team members ask how to update their branch or how to merge, and get different answers.
  • A force-push or a rebase of a shared branch has caused lost work.
  • You have weighed the options, for example in the real cost of requiring linear history, and need to write the outcome down.

Step 1 β€” Answer the four questions the policy must cover Jump to heading

A merge policy answers four questions. Leave out the rest.

The four questions of a merge policyHow do pull requests land on main: merge commit, squash or rebase? How do branches stay current with main: merge or rebase? When may history be rewritten and force-pushed? How is each rule enforced: settings, checks or convention?Landingmerge / squash /rebaseStaying currentmerge main inor rebaseRewritingwhen force-pushis allowedEnforcementsettings, checksanything outside these four belongs in the contributing guide, not the policy

Step 2 β€” Draft it in a few lines Jump to heading

Write the policy as a short document in the repository, next to the contributing guide, with each rule followed by a one-sentence reason. Reasons keep the policy from being re-litigated, because the next person can see what was weighed.

# Merge policy

**Landing on main:** squash merge only. *Why:* one commit per change keeps main readable and bisectable; we do not rely on per-commit history inside pull requests.

**Staying current:** your branch, your choice β€” rebase onto main or merge main in. Shared branches: merge main in only. *Why:* rebasing shared branches forces collaborators to reset.

**Rewriting history:** allowed on branches only you use, with `git push --force-with-lease`. Never on main, release branches or shared branches. *Why:* rewrites of shared history lose other people's work.

**Enforcement:** squash-only and no force-push on main are repository settings; everything else is convention, checked in review.

Step 3 β€” Enforce what can be enforced in settings Jump to heading

Policies followed by default are policies followed. Encode every rule you can as a repository setting or ruleset, so the wrong action is unavailable rather than merely discouraged.

# Squash merge only, with the PR title as the commit message
gh api -X PATCH "repos/$OWNER/$REPO" \
  -F allow_squash_merge=true -F allow_merge_commit=false -F allow_rebase_merge=false \
  -f squash_merge_commit_title=PR_TITLE -f squash_merge_commit_message=PR_BODY

# No force-push or deletion on main and release branches (ruleset rules)
gh api "repos/$OWNER/$REPO/rulesets" --jq '.[] | {name, enforcement}'
From policy text to enforced behaviourThe written policy states the rules and reasons. Repository settings make disallowed merge methods unavailable. Rulesets block force-pushes and deletions on protected branches. Shared Git configuration sets safe defaults on every machine. Review catches what remains.the more layers a rule reaches, the less it depends on memoryWritten policyrules + reasonsRepository settingsonly allowed merge methodsRulesetsno force-push on protectedShared git configpull.rebase, push aliasesReviewwhat nothing else can checka rule that exists only in the document will be broken within a month

Step 4 β€” Set safe local defaults for everyone Jump to heading

Some rules live on developer machines: how git pull integrates, how force-pushes are done. Ship those in the team’s shared Git configuration.

# team.gitconfig
[pull]
    rebase = true                 # rebase local unpushed work on pull; never creates merge commits
[rebase]
    autoStash = true
    updateRefs = true
[push]
    default = simple
[alias]
    pushf = push --force-with-lease --force-if-includes

Distribution is covered in shipping a team gitconfig with includeIf, and the pull setting in configuring git pull --rebase safely for a team.

Step 5 β€” Roll it out and listen Jump to heading

Propose the policy as a pull request, so the team can comment on it like code. Once merged, apply the settings, announce it with a link, and revisit after a few weeks: are people working around a rule? That usually means the rule or its reason needs updating.

gh pr create --title "Add merge policy" --body-file docs/merge-policy.md
Introducing a merge policyThe draft is opened as a pull request and discussed. Once merged, repository settings and rulesets are applied the same day. The team is told where the policy lives. After a month, the team reviews whether any rule is being worked around and adjusts it.Policy PRdiscussed like codeday 1Mergedsettings appliedday 3Announcedlink in channelday 3Reviewworkarounds?day 30Adjustupdate + reasonsday 31the review at day thirty is where the policy becomes the team's rather than its author's

Step 6 β€” Keep it current Jump to heading

When the team or the codebase changes β€” a monorepo migration, signed-commit requirements, a merge queue β€” revisit the policy. A merge policy that describes a workflow the team no longer uses is worse than none, because new members follow it.

Validation checklist Jump to heading

Frequently Asked Questions Jump to heading

Should every repository in an organisation share one policy? Jump to heading

A shared default policy with per-repository exceptions works well. Libraries that preserve per-commit history and services that squash-merge may legitimately differ; record the difference where people will see it.

What if a team member strongly disagrees with the policy? Jump to heading

The pull request is the place for it. A policy with reasons can be argued with on its merits, and changed if the argument holds; that is better than silent non-compliance.

Does the policy need to cover commit messages? Jump to heading

Only where the merge method affects them β€” for example, that squash commit messages come from the pull request title. Message conventions themselves belong in their own guide, such as enforcing subject line length and format.