CHAPTER 15

Branch protection & CODEOWNERS

Tripathi almost erased two days of Anvesha's work with one panicked command. The only reason it didn't work is a setting, not a habit.

A near miss, the week after Ishaan's first PR

Tripathi's fixing something on his own branch, gets tangled in a rebase, and — following the first StackOverflow answer he finds — decides the fastest way out is to just force his local branch's state onto main directly:

git push origin main --force

His local main is two days stale. If this succeeded, it would silently overwrite everything Anvesha has pushed since, including work Ishaan's PR already built on top of. Nobody would even get a warning — force-push, by definition, means "trust me, don't check."

GitHub refuses the push outright:

remote: error: GH006: Protected branch update failed for refs/heads/main.
remote: error: Cannot force-push to a protected branch

Not because Tripathi is careless — he isn't, usually. Because two days of someone else's real work were never actually one panicked command away from disappearing. That protection exists as a setting on the repository, sitting quietly in the background the entire time, completely independent of whether anyone remembered to be careful in the moment.

What "branch protection" is, mechanically

It's a set of rules GitHub enforces on a specific branch — almost always main — regardless of who's pushing. Crucially, it doesn't remove anyone's permissions; Tripathi still has push access to the repo. It just narrows which kinds of pushes are allowed to land on that one branch. Everything still has to come in through the front door: a pull request that satisfies the rules.

The rules actually turned on for `.github`, `website`, and `resources`

RuleWhat it stopped, in this story
Require a PR before mergingNo one — not even you, back in Chapter 2 — can push straight to main anymore once this is on. Everything goes through review, the way Ishaan's typo fix did.
Require 1 approvalIshaan's PR couldn't merge until Vidha approved it. One person's opinion, even the author's own, isn't enough by itself.
Dismiss stale reviews on new commitsIf Ishaan had pushed another change after Vidha's approval, her approval would reset — she has to look again at what's actually there now, not what she saw an hour ago.
Block force-pushesExactly what stopped Tripathi just now.
Block branch deletionNobody, including an Owner, can delete main by accident or otherwise.
"INCLUDE ADMINISTRATORS" — CURRENTLY OFF, ON PURPOSE, TEMPORARILY
During this early setup period, repo Owners can still push directly, bypassing review — a deliberate, logged exception so early bootstrap work isn't blocked by needing a second approver when the club is still tiny. Everyone else, Tripathi included, is fully covered by every rule above. This gets switched on for Owners too once the club's steady-state review flow is running.

CODEOWNERS — making sure the right person gets asked

Branch protection stops the wrong kind of push. It doesn't, by itself, make sure the right person reviews a given change — Vidha happened to review Ishaan's PR because she runs the beginner track and was already in the room. A file named CODEOWNERS automates that instead of leaving it to chance:

# .github/CODEOWNERS

# Fallback — anything not matched below
*                       @accelerate-muj/executive-board

# Constitution and governance — the people actually accountable for it
CONSTITUTION.md         @accelerate-muj/executive-board
GOVERNANCE.md           @shashwat-deep @TheQMLGuy

# Website source — Core Committee's territory
/src/                   @accelerate-muj/core-committee

# CI and deploy workflows — don't let these change quietly
/.github/workflows/     @TheQMLGuy @sahil-handle

# Beginner Git-track docs — Vidha owns the content
/git-guide/             @vidha-handle

With this in place and combined with branch protection's "Require review from Code Owners" rule, a PR touching /git-guide/ automatically requests Vidha specifically, the moment it's opened — nobody has to remember to tag her.

Status checks — a machine's approval, not just a human's

Once Chapter 17 wires up automated checks, branch protection can require them to pass too, not just a human approval. A PR that fails a required check simply can't be merged — the button stays disabled — regardless of how many humans approved it. Typical required checks for a project like this: lint, test, build.

Rulesets — the newer, more flexible version

GitHub's newer "Rulesets" feature covers everything classic branch protection does, plus wildcard branch patterns (release/*, not just main), multiple composable rulesets, and a dry-run "evaluate" mode that reports what would be blocked before actually enforcing anything. For an org this size, classic branch protection is enough — rulesets earn their complexity once there are many branch patterns to manage at once.

CHECK
Tripathi still has push access to the website repo. Why did his force-push get rejected?
Branch protection rules apply to the branch, not to a person's permission level — having write access doesn't override a "block force-pushes" rule. That's the entire point: the safeguard doesn't depend on any individual remembering to be careful.

Every rule in this chapter has been about the code itself — who can push it, who has to review it, what has to pass first. But a huge amount of the club's actual coordination — what needs doing, who's doing it, what's blocked on what — has been happening entirely in a group chat this whole time, with no record anywhere in GitHub at all. That's about to change.