Skip to content

GitHub controls reference

Golden-path controls validated on WU repos (e.g. wiresuncrossed/hoen-assessment). Use with diagnostic-prompt.md.

Concepts

Mechanism Purpose
Rulesets Modern replacement for branch protection; org or repo scoped
Branch protection (legacy API) May 404 when rulesets-only — use rulesets API
Repo merge settings Which merge buttons appear on PRs
Merge method ruleset rule Restrict PR merges to rebase / squash / merge commit
Push rulesets Block pushes by file size, path, etc.

Stacking: org rulesets + repo rulesets both apply. Repo settings must allow any merge method the ruleset requires.

Ghost branches and squash

Squash merge creates one new commit on main. The PR branch tip is not an ancestor of that commit → GitHub shows "1 commit ahead" on stale branches. Rebase merge replays commits onto main so branch tips become ancestors (when branch is updated). Auto-delete head branches removes stale refs.

Scan commands

Install gh first if needed — see gh-cli-install.md (Windows, Git Bash, macOS, Linux/WSL).

Single repo

OWNER=wiresuncrossed
REPO=hoen-assessment

gh repo view "$OWNER/$REPO" \
  --json name,defaultBranchRef,mergeCommitAllowed,squashMergeAllowed,rebaseMergeAllowed,deleteBranchOnMerge

gh api "repos/$OWNER/$REPO/rulesets" \
  --jq '.[] | {id, name, source_type, enforcement, target}'

# Detail for each ruleset ID
gh api "repos/$OWNER/$REPO/rulesets/16769475" \
  --jq '{name, enforcement, conditions, rules}'

All org repos (summary loop)

ORG=wiresuncrossed

gh repo list "$ORG" --limit 200 --json name,isArchived \
  --jq '.[] | select(.isArchived == false) | .name' | while read -r repo; do
  echo "## $repo"
  gh repo view "$ORG/$repo" \
    --json mergeCommitAllowed,squashMergeAllowed,rebaseMergeAllowed,deleteBranchOnMerge \
    --jq .
  gh api "repos/$ORG/$repo/rulesets" --jq '.[].name' 2>/dev/null || echo "(none)"
  echo
done

Org rulesets (requires org admin scope)

gh api "orgs/$ORG/rulesets" 2>&1
# May require: gh auth refresh -h github.com -s admin:org

Golden-path — repo settings only

Pilot on one repo before org rollout:

gh repo edit "$OWNER/$REPO" \
  --enable-merge-commit=false \
  --enable-squash-merge=false \
  --enable-rebase-merge=true \
  --delete-branch-on-merge=true \
  --allow-update-branch

Verify:

gh repo view "$OWNER/$REPO" \
  --json mergeCommitAllowed,squashMergeAllowed,rebaseMergeAllowed,deleteBranchOnMerge

Golden-path — rulesets (UI)

Create at Settings → Rules → Rulesets → New branch ruleset (repo) or Organization settings → Rules → Rulesets (org).

Protect Main (branch ruleset)

Rule Setting
Target Default branch / refs/heads/main
Restrict deletions On
Block force pushes On (non_fast_forward)
Require linear history On
Require status checks to pass On — see below (do not skip)

Required status checks (merge gate)

Gap to catch: ruleset protects main (no force-push / linear history / PR required) but does not list required checks → PRs can merge while CI is pending or red. Workflows existing under .github/workflows/ is not enough.

Step Action
1 Confirm rule type REQUIRED_STATUS_CHECKS on the Protect Main ruleset (or legacy branch protection)
2 Discover exact check contexts from recent PRs + workflow job ids (GitHub’s reported name is usually the job id, not the workflow name:)
3 Require verify jobs (validate, test, lint, commitlint). Prefer not requiring deploy/preview jobs that need third-party secrets
4 Enable “require branch up to date” / strict checks when the team accepts rebase-before-merge friction
5 Ask the user to verify the proposed context list before applying — names vary by repo and rename when job ids change

Discovery helpers:

# Job ids from workflow YAML (candidates for required contexts)
rg -n '^jobs:' -A 20 .github/workflows/*.yml

# Names GitHub actually reported on recent merged PRs — see diagnostic-prompt Phase 3b

Example (hoen-library): require validate and commitlint; leave build-deploy optional (Cloudflare Pages deploy).

When recommending remediation, present the exact strings to add in the UI (Settings → Rules → Rulesets → Protect Main → Require status checks to pass) and wait for user confirmation.

Rebase merge only (branch ruleset)

Add rule: Pull request merge method → allow Rebase only.

Requires repo: allow_rebase_merge=true, squash and merge commit disabled.

No large files (push ruleset)

Setting Example
Target Push
Max file size 3 MB
Enforcement Active

Org-level example name: No large files (>3mb).

UI audit mode (no gh)

Ask the user for screenshots or values from:

  1. Settings → Rules → Rulesets — list names, enforcement, targets, rule types
  2. Open Protect Main (or equivalent) — confirm Require status checks to pass and list the named checks (empty list = gap)
  3. Settings → General → Pull Requests — merge commit / squash / rebase toggles; auto-delete head branches
  4. Settings → General — default branch name
  5. Branches page — behind/ahead on stale feature branches (ghost-branch check)
  6. A recent PR’s Checks tab — compare displayed job names to the ruleset list

Remediation — large file already in git

  1. Move artifact to R2 / release asset / LFS
  2. Update docs to link or download instructions (not a raw blob in repo)
  3. Remove from tree; add path to .gitignore
  4. Rewrite feature branch history:
# Prefer git-filter-repo (install separately)
git filter-repo --path "path/to/large.zip" --invert-paths --force

# Or filter-branch (built-in, slower)
FILTER_BRANCH_SQUELCH_WARNING=1 git filter-branch -f \
  --index-filter 'git rm --cached --ignore-unmatch "path/to/large.zip"' \
  -- BRANCH_NAME
  1. git push --force-with-lease origin BRANCH_NAME — user approval required
  2. Rewriting main requires org admin and team coordination

Docs