Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .github/scripts/setup-branch-protection.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/usr/bin/env bash
# sets up branch protection for the main branch.
# prerequisites: gh CLI authenticated, run from repo root.
# usage: bash .github/scripts/setup-branch-protection.sh [branch]

set -euo pipefail

GREEN='\033[0;32m'
CYAN='\033[0;36m'
RESET='\033[0m'

BRANCH="${1:-main}"
REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner)

echo "Configuring branch protection for '$BRANCH' in $REPO..."

gh api \
--method PUT \
"repos/$REPO/branches/$BRANCH/protection" \
--input - <<EOF
{
"required_status_checks": {
"strict": true,
"contexts": [
"backend-lint",
"backend-unit-tests",
"backend-integration-tests",
"frontend-precommit"
]
},
"enforce_admins": false,
"required_pull_request_reviews": {
"required_approving_review_count": 1,
"dismiss_stale_reviews": true
},
"restrictions": null,
"allow_force_pushes": false,
"allow_deletions": false
}
EOF

echo ""
echo -e "${GREEN}Branch protection applied.${RESET}"
echo ""
echo -e "${CYAN}Required status checks:${RESET} backend-lint, backend-unit-tests, backend-integration-tests, frontend-precommit"
echo -e "${CYAN}Required approvals:${RESET} 1 (stale reviews dismissed)"
echo -e "${CYAN}Force push to $BRANCH:${RESET} blocked"
78 changes: 78 additions & 0 deletions dev-docs/stacking-workflow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Stacking Workflow

We use [Git Town](https://www.git-town.com/) for stacked PRs. Stack visualization is posted automatically as a comment on every PR via `git-town/action`.

## Creating a stack

```bash
git town sync # sync main first
git town hack feat/my-base # branch 1 off main

# make changes, commit
git town append feat/my-feature # branch 2 on top of branch 1

# make changes, commit
git town propose # open PR targeting feat/my-base (git town sets target automatically)
```

Repeat `git town append` + `git town propose` for each branch in the stack.

## Syncing when main moves

```bash
# from any branch in the stack:
git town sync --stack # rebases entire stack from main down, force-pushes all remote branches
```

## When a parent PR merges

Once a parent branch is merged, GitHub auto-retargets the next PR to main. Locally:

```bash
git town sync --stack # cleans up merged parent, rebases children onto main
```

## Shipping

Merge bottom-up: always merge the oldest (lowest) PR in the stack first, then work upward.

On each PR, once it's approved and CI is green: click **"Squash and merge"**.

After each merge:
```bash
git town sync --stack # clean up locally before merging the next PR up
```

## Resolving conflicts

If a PR has conflicts after a parent merges:

```bash
git town sync --stack # rebase against latest main
# resolve conflicts, then:
git add .
git rebase --continue
git town sync # force-push updated branch
```

## Command reference

| Command | When to use |
|---|---|
| `git town hack <branch>` | start new branch off main |
| `git town append <branch>` | add branch on top of current branch |
| `git town prepend <branch>` | insert branch below current branch |
| `git town sync` | sync current branch only |
| `git town sync --stack` | sync entire stack |
| `git town propose` | open or update PR with correct target |
| `git town switch` | interactive branch switcher |
| `git town compress` | squash all commits on current branch into one |
| `git town kill` | delete current branch and its PR |
| `git town diff` | show diff for current branch only (not parent) |
| `git town branch` | show current branch and position in stack |

## Rules

- Stack only branches that depend on each other. Independent work goes on separate top-level branches.
- Always ship oldest-first (bottom of stack before top).
- Never force-push to `main` or other perennial branches.
Loading