Skip to content

g1t/apps/docs/src/content/docs/guides/rules.md

390 lines22,233 bytesCodeBlame
1---
2title: Rules
3description: Rulesets decide what may happen to a repository's branches and tags, and what a pull request needs before it merges, for people and agents alike.
4---
5
6A **ruleset** says what may happen to some of a repository's branches or
7tags, and what a pull request into them needs before it merges. Every push,
8every merge, and every branch renamed or commit made through g1t is checked
9against the rulesets that cover it. Agents, g1t's own included, follow the
10same rules as people. Only people, teams, tokens or g1t that a ruleset
11lists as able to bypass it are let through.
12
13A ruleset belongs to a repository, or to a workspace. A workspace's ruleset
14holds in every repository it selects. When several rulesets cover a branch,
15all of their rules hold, so the strictest one wins.
16
17## What a ruleset has
18
19| Part | What it is |
20| --- | --- |
21| Name | What people call it, up to 100 characters. |
22| Enforcement | `active`: its rules hold. `evaluate`: nothing is refused, and every push or merge it would have refused is recorded (see [Insights](#insights)). `disabled`: kept, but not checked. |
23| Target | `branch` or `tag`. |
24| Branches or tags | `include` and `exclude` patterns. A name is covered when it matches an include pattern and no exclude pattern. |
25| Repositories | A workspace's ruleset only: which of its repositories it holds in. |
26| Bypass list | Who these rules do not hold for. Nobody is on it unless listed. |
27| Rules | What holds, each with its parameters, and whose changes it holds for. |
28
29### Patterns
30
31Branch, tag and repository names are matched with fnmatch patterns:
32
33| Pattern | Matches |
34| --- | --- |
35| `~DEFAULT_BRANCH` | The repository's default branch, whatever it is called now. |
36| `~ALL` | Every branch, tag or repository. |
37| `release/*` | `release/1.x`, but not `release/1.x/hotfix`: `*` stays within one segment. |
38| `release/**` | Everything under `release/`. |
39| `v[0-9]*` | `v1`, `v2.0.1`. `[...]` matches one character of a set; `[!...]` one not in it. |
40
41A pattern written as a full ref, such as `refs/heads/main`, works too. An
42empty include list covers nothing.
43
44File path patterns in rules work the same way, with one addition: a
45pattern without a `/`, such as `*.exe` or `CODEOWNERS`, matches a file of
46that name in any directory. A pattern ending in `/` matches everything
47under it.
48
49### Repositories a workspace ruleset holds in
50
51| Condition | What it does |
52| --- | --- |
53| `include`, `exclude` | Repository name patterns, ignoring case. `~ALL` is every repository. |
54| `visibility` | `any`, `public` or `private`. |
55| `topics` | Only repositories with at least one of these [topics](/guides/managing-repositories/). Empty means any. |
56
57## Create a ruleset
58
59You need the Maintain [role](/guides/access-and-roles/) or higher for a
60repository's ruleset. For a workspace's, you need to be an owner.
61
621. Open the repository's **Settings → Rules**, or the workspace's
63 **Settings → Rules**.
642. Choose **New ruleset**.
653. Name it, choose **Branches** or **Tags**, and set which names it covers.
66 **Default branch** and **All branches** are a click away.
674. Choose its enforcement. To try it without refusing anything, choose
68 **Evaluate** and watch Insights.
695. Add people, teams, roles, tokens or g1t who may bypass it, if anyone.
706. Choose **Add a rule** for each rule. Set its parameters, and whether it
71 holds for **Everyone**, **Agents' changes** or **People's changes**.
727. Choose **Create ruleset**.
73
74**Export JSON** downloads a ruleset. **Import JSON** fills the form from a
75ruleset file, so one ruleset can be copied to other repositories or
76workspaces. The file is the ruleset as the [API](#from-the-api) shows it.
77
78## The rules that hold for a branch
79
80Under **Settings → Rules**, **What holds for a branch** lists every rule of
81every ruleset that covers a branch, the repository's and its workspace's,
82active ones first. For each ruleset it also shows who may bypass it. To see
83a tag's rules, put `?tag=v1.0` in the address.
84
85The **Rules** box on a pull request lists each rule of its base branch it
86does not meet yet. For each one it shows which ruleset it comes from and
87what to do about it.
88
89## Who may bypass
90
91| Kind | `value` | Who |
92| --- | --- | --- |
93| `role` | `write`, `maintain`, `admin` (that role or higher), or `owner` | People with that role on the repository, or the workspace's owners. |
94| `team` | `team` or `workspace/team` | The team's people, child teams included. |
95| `user` | A username | One person. |
96| `token` | A token's id, or `workspace` | That access token, or any of the workspace's own tokens. |
97| `g1t` | None | g1t's agents, and g1t acting on its own, such as the merge queue and security updates. |
98
99Each entry has a `mode`:
100
101- `always`: pushes and merges both go through.
102- `pull_requests`: only merges go through. Their pushes follow the rules.
103
104An agent never gets its person's role. An agent acting for an admin still
105follows a ruleset that lets admins bypass it. Only a `g1t` or `token` entry
106lets an agent through.
107
108When a person who may bypass merges a pull request that does not meet the
109rules, the merge box offers **Bypass the rules**. Through the API, pass
110`bypass_rules`. A push from someone on the bypass list goes through by
111itself, since git has no box to tick. Every bypass is recorded in
112[Insights](#insights).
113
114## Rules
115
116Each rule has a `type` and `parameters`. A parameter you leave out takes its
117default. `applies_to` is `everyone` (the default), `agents` or `people`.
118
119A change counts as an agent's when it is pushed with an agent's token, or
120when its pull request was made by g1t, opened with an agent's token, or opened
121naming an agent (`agent`) through the API or MCP. A pull request a person
122opens on the site, or through the API without naming an agent, is a person's.
123
124### Branches and tags
125
126| Rule | `type` | What it does | Parameters |
127| --- | --- | --- | --- |
128| Restrict creations | `creation` | Only people on the bypass list create matching branches or tags. | None |
129| Restrict updates | `update` | Only people on the bypass list push to matching branches or tags. This covers merges too. | None |
130| Restrict deletions | `deletion` | Only people on the bypass list delete them. | None |
131| Block force pushes | `non_fast_forward` | A push must add to the branch's history, never rewrite it. | None |
132| Branch name pattern | `branch_name_pattern` | New branches must be named as it says. | [Pattern](#pattern-rules) |
133| Tag name pattern | `tag_name_pattern` | New tags must be named as it says. | [Pattern](#pattern-rules) |
134
135### Pull requests and checks
136
137| Rule | `type` | What it does |
138| --- | --- | --- |
139| Require a pull request before merging | `pull_request` | Pushes straight to the branch are refused. A pull request into it needs what the parameters say. |
140| Require status checks to pass | `required_status_checks` | These checks must pass on a pull request's head before it merges. |
141| Require the merge queue | `merge_queue` | Merging into the default branch adds the pull request to the [merge queue](/guides/merge-queue/), with these settings. On other branches it merges directly. |
142| Require deployments to succeed | `required_deployments` | A pull request's head must have deployed successfully to these environments. |
143
144`pull_request` parameters:
145
146| Parameter | Default | What it does |
147| --- | --- | --- |
148| `required_approvals` | `0` | Approving reviews needed, 0 to 10. A reviewer who has since asked for changes blocks the merge. Nobody approves their own pull request, or one g1t made for them. |
149| `count_agent_approvals` | `true` | Whether an agent's approval counts toward `required_approvals`. |
150| `dismiss_stale_reviews_on_push` | `false` | Approvals given before the latest push no longer count. |
151| `require_code_owner_review` | `false` | The [code owners](/guides/codeowners/) of every file it changes must approve. |
152| `require_last_push_approval` | `false` | Someone other than whoever pushed last must approve after that push. |
153| `allowed_merge_methods` | All | `merge`, `squash` or `rebase`. g1t merges by landing the branch as it is, which counts as `merge`. Squash and rebase merging are planned. |
154| `allow_direct_pushes` | `false` | Pull requests need what this rule says, but pushes straight to the branch are still allowed. Only the ruleset made from branch protection that did not require pull requests has this on. |
155
156`required_status_checks` parameters:
157
158| Parameter | Default | What it does |
159| --- | --- | --- |
160| `checks` | None | Each check has a `context`, such as `CI` (a workflow's name) or `g1t / deploy`, and an optional `integration`: `actions`, `deployments`, `security` or `g1t`. A check with an `integration` counts only when that integration reported it, so a workflow cannot stand in for a deployment. |
161| `strict` | `false` | The pull request must contain the branch's latest commits, so what merges is exactly what was checked. |
162| `paths` | Always | The checks are required only when the pull request changes a file matching one of these patterns. |
163| `allow_bypass_on_merge` | `false` | Someone who may merge can merge past checks that have not passed by ticking **Bypass the required checks**. |
164
165`merge_queue` parameters: `max_entries_to_build` (pull requests tested at
166once, 1 to 20, default 4), `min_entries_to_merge` and
167`min_entries_wait_minutes` (the smallest batch to start, and how long the
168oldest entry waits for it to fill; default 1 and 0),
169`check_response_timeout_minutes` (how long a batch's checks may take before
170it is tested again, 5 to 360, default 45) and `merge_method`. When several
171rulesets set a queue, the queue uses the smallest batch size and timeout
172and the longest wait among them.
173
174`required_deployments` takes `environments`. `preview` is a pull request's
175[preview deployment](/guides/deployments/). A project's slug is that
176project's deployment, when a repository has several.
177
178### Commits
179
180| Rule | `type` | What it does |
181| --- | --- | --- |
182| Require linear history | `required_linear_history` | No merge commits. Rebase instead of merging the branch in. |
183| Require signed commits | `required_signatures` | Every commit carries a signature g1t verifies. |
184| Commit message pattern | `commit_message_pattern` | Every commit message must match, or must not. |
185| Commit author email pattern | `commit_author_email_pattern` | Every author address must match, or must not. |
186| Committer email pattern | `committer_email_pattern` | Every committer address must match, or must not. |
187
188These hold for the commits a push adds and for the commits a pull request
189would land when it merges.
190
191**Signed commits.** g1t verifies SSH signatures made with an ed25519 key:
192
193```sh
194git config gpg.format ssh
195git config user.signingkey ~/.ssh/id_ed25519.pub
196git commit -S -m "Add rules"
197```
198
199A signature counts as verified when it is valid over the commit and the key
200is one of the [SSH keys](/guides/authentication/) on the g1t account that
201owns the committer's verified email address. GPG signatures, and SSH
202signatures made with RSA or ECDSA keys, are reported as not verified yet.
203Commits g1t makes itself, such as catching a pull request up and web edits,
204are not signed. So with this rule on a branch, bring pull requests up to
205date with a signed rebase of your own.
206
207### Pattern rules
208
209| Parameter | What it does |
210| --- | --- |
211| `operator` | `starts_with`, `ends_with`, `contains` or `regex`. |
212| `pattern` | The text, or the regular expression. |
213| `negate` | The text must not match. |
214| `name` | What the rule is called in refusals, such as `Conventional commits`. |
215
216Regular expressions run on a linear-time engine. A pattern can never make a
217push slow, and look-around and back-references are not supported. For
218example, conventional commits:
219
220```json
221{ "type": "commit_message_pattern", "parameters": { "name": "Conventional commits", "operator": "regex", "pattern": "^(feat|fix|docs|chore)(\\(.+\\))?: " } }
222```
223
224### Files
225
226| Rule | `type` | What it does | Parameters |
227| --- | --- | --- | --- |
228| Restrict file paths | `file_path_restriction` | Changes to matching paths are refused, deletions included. | `restricted_file_paths` |
229| Restrict file extensions | `file_extension_restriction` | Files with these extensions may not be added or changed. | `restricted_file_extensions`, such as `.exe` |
230| Restrict file size | `max_file_size` | No file larger than this. | `max_file_size_mb`, 1 to 100 |
231| Restrict file path length | `max_file_path_length` | No path longer than this. | `max_file_path_length` |
232| Restrict files changed | `max_files_changed` | A commit may change at most this many files. | `max_files` |
233| Block pushes that add secrets | `secret_scanning` | Every push to the branch is scanned by [push protection](/guides/security/secret-protection/). A push too large to scan is refused rather than let through unscanned. | None |
234
235### Agents, review and timing
236
237These rules exist for teams where agents write much of the code.
238
239| Rule | `type` | What it does | Parameters |
240| --- | --- | --- | --- |
241| Confidence threshold | `confidence_threshold` | An agent's change that g1t [rates](/guides/working-with-g1t/#how-sure-the-agent-is) below `minimum` needs approvals from people before it merges. A change not rated yet counts as below. | `minimum` (`low`, `medium`, `high`), `required_approvals` |
242| Cost cap | `cost_cap` | Once agents have spent more than `max_usd` on a pull request, it does not merge, and its agent is not sent back to revise, until a person approves it. | `max_usd` |
243| Review for sensitive paths | `path_review` | A pull request that changes a matching file needs approvals from people since its latest push, from `team` when one is named. | `paths`, `required_approvals`, `team` |
244| Merge window | `merge_window` | When pull requests may merge into the branch. | See below |
245| Agent auto-merge | `agent_auto_merge` | Whether g1t lands an agent's ready pull request into the branch without a person, and how sure g1t must be first. The repository's **Merge automatically when ready** setting must be on too. | `allowed`, `minimum_confidence` |
246
247Rules that hold only for agents' changes cover the rest of what an
248agent-first team needs. For example:
249
250- Agents' pull requests need one approval from a person: `pull_request`
251 with `required_approvals` `1`, `count_agent_approvals` `false`, and
252 `applies_to` `agents`.
253- Agents may not change workflows or code owners: `file_path_restriction`
254 with `.g1t/workflows/**` and `CODEOWNERS`, and `applies_to` `agents`.
255
256`merge_window` parameters:
257
258| Parameter | What it does |
259| --- | --- |
260| `time_zone` | A fixed offset from UTC, such as `+02:00` or `-05:00`, or `UTC`. Daylight saving time is not applied. |
261| `windows` | Weekly hours when merging is open: each has `days` (`mon` to `sun`), `start` and `end` as `HH:MM`. An `end` before its `start` runs past midnight. With none, merging is open whenever no freeze covers the moment. |
262| `freezes` | Periods when merging waits, each with `start`, `end` (RFC 3339) and a `reason`. A freeze without an `end` lasts until you remove it. Use one during an incident. **Freeze now, until lifted** adds one. |
263| `exceptions` | Periods when merging is open whatever the windows and freezes say, such as a hotfix. |
264
265A refusal outside the window says when it next opens.
266
267## Evaluate mode
268
269A ruleset in `evaluate` refuses nothing. Every push and merge it would have
270refused is recorded as **Would block** in Insights. On a pull request, the
271merge box lists what it would refuse under **Rulesets in evaluate would
272refuse it**. Turn a ruleset to **Active** once Insights shows it refusing
273only what it should.
274
275## Insights
276
277**Settings → Rules → Insights** lists how the rules judged each push, merge,
278branch creation, deletion, rename and commit made on the site, newest first.
279Each entry shows the ruleset, the ref, who made the change, whether they are
280a person or an agent, and every rule broken with the reason. Totals cover
281the last 30 days: **Passed**, **Blocked** (refused by an active ruleset),
282**Would block** and **Bypassed**, with the rules broken most. A workspace's
283Insights covers all of its repositories. Evaluations are kept for 90 days.
284
285Changing a ruleset is recorded in the workspace's
286[audit log](/guides/audit-log/) and sent to webhooks as `ruleset.created`,
287`ruleset.updated` or `ruleset.deleted`. A workspace's ruleset events go to
288the workspace's webhooks.
289
290## Where rules are checked
291
292| Change | What happens |
293| --- | --- |
294| `git push` | The rules of every branch and tag the push changes are checked before anything is stored. A refused push changes nothing, and git prints why. |
295| Merging a pull request | The merge button, the API, MCP, auto-merge, g1t's own merges and the merge queue all check the same rules. |
296| Renaming a branch | Covered as deleting the old name and creating the new one. |
297| A file committed on the site | Covered as a push of that commit to its new branch. |
298| Bringing a pull request up to date | Covered as a push of the merge commit to its branch. |
299
300A refused push looks like this:
301
302```text
303remote:
304remote: error: rules for refs/heads/main declined this push:
305remote: - Changes to main must be made through a pull request. [ruleset "Protect main", pull_request]
306remote: Push a branch, open a pull request into main, and merge it.
307remote: See the rules that hold for it: https://g1t.sh/acme/web/settings/rules?branch=main
308remote:
309 ! [remote rejected] main -> main (declined by ruleset "Protect main" (pull_request))
310```
311
312A pull request's fork, where g1t's agents work, has no rules of its own.
313What it brings is checked against the base branch's rules when it merges.
314
315## Rulesets and branch protection
316
317Before rulesets, a repository had one set of protection settings for its
318default branch. Each repository's settings became a ruleset named
319**Default branch protection**, targeting `~DEFAULT_BRANCH` and holding
320exactly what they held:
321
322| Setting | Becomes |
323| --- | --- |
324| Require a pull request to change the default branch | `pull_request` |
325| Required approvals, g1t's approval counts, require review from code owners | `pull_request`'s `required_approvals`, `count_agent_approvals`, `require_code_owner_review` |
326| Required status checks, require branches to be up to date, allow bypassing required checks | `required_status_checks`'s `checks`, `strict`, `allow_bypass_on_merge` |
327| Merge through a queue | `merge_queue` |
328
329Where approvals or code owners were required but pushes were not refused,
330the `pull_request` rule has `allow_direct_pushes` on, so pushes still go
331through as before.
332
333Pull requests into other branches used to need nothing. Now they need what
334the rulesets covering their base ask for. With only the migrated ruleset,
335that is still nothing.
336
337[`update_repo_settings`](/reference/api/repositories/update-repo-settings/)
338still takes the old fields and writes them to the **Default branch
339protection** ruleset, creating it when needed. Rules only rulesets have
340stay as they are. [`get_repo_settings`](/reference/api/repositories/get-repo-settings/)
341returns the default branch's protection as all of its rulesets stack. The
342`protected` field of [`update_repo`](/reference/api/repositories/update-repo/)
343turns the ruleset's pull request requirement on or off.
344
345## From the API
346
347| Route | MCP | What it does |
348| --- | --- | --- |
349| `GET /repos/{owner}/{name}/rulesets` | `repository` `list_rulesets` | A repository's rulesets; `include_parents` adds the workspace's that hold in it. |
350| `POST /repos/{owner}/{name}/rulesets` | `repository` `create_ruleset` | Create one. |
351| `GET /repos/{owner}/{name}/rulesets/{id}` | `repository` `get_ruleset` | One ruleset. |
352| `PUT /repos/{owner}/{name}/rulesets/{id}` | `repository` `update_ruleset` | Change one. Fields you leave out stay as they are. |
353| `DELETE /repos/{owner}/{name}/rulesets/{id}` | `repository` `delete_ruleset` | Delete one. |
354| `GET /repos/{owner}/{name}/rules/branches/{branch}` | `repository` `branch_rules` | Every rule that holds for a branch (`?target=tag` for a tag). Encode slashes: `release%2F1.x`. |
355| `GET /repos/{owner}/{name}/rules/evaluations` | `repository` `rule_evaluations` | Evaluations and insights; `ruleset_id`, `verdict`, `problems_only`, `before`, `limit`. |
356| `/workspaces/{workspace}/rulesets` and `/workspaces/{workspace}/rules/evaluations` | `workspace` `list_rulesets` and the rest | The same for a workspace. |
357
358The body of a create or update is the ruleset. Under a repository's
359address, `name` already names the repository, so the ruleset's name is
360`ruleset_name`. A file exported from the site, which says `name`, is read as
361it is.
362
363```sh
364curl -X POST https://api.g1t.sh/repos/acme/web/rulesets \
365 -H "Authorization: Bearer $G1T_TOKEN" \
366 -H "Content-Type: application/json" \
367 -d '{
368 "ruleset_name": "Protect main",
369 "conditions": { "ref_name": { "include": ["~DEFAULT_BRANCH"] } },
370 "bypass_actors": [{ "kind": "role", "value": "admin", "mode": "pull_requests" }],
371 "rules": [
372 { "type": "deletion" },
373 { "type": "non_fast_forward" },
374 { "type": "pull_request", "parameters": { "required_approvals": 1, "dismiss_stale_reviews_on_push": true } },
375 { "type": "required_status_checks", "parameters": { "checks": [{ "context": "CI", "integration": "actions" }], "strict": true } },
376 { "type": "pull_request", "parameters": { "required_approvals": 1, "count_agent_approvals": false }, "applies_to": "agents" },
377 { "type": "file_path_restriction", "parameters": { "restricted_file_paths": [".g1t/workflows/**"] }, "applies_to": "agents" }
378 ]
379 }'
380```
381
382Reading rulesets takes the `repo:read` or `workspace:read`
383[scope](/guides/authentication/). Changing them takes `repo:admin` or
384`workspace:admin`, since it changes what everyone, agents included, may do.
385[`merge_pull_request`](/reference/api/pull-requests/merge-pull-request/)
386takes `bypass_rules`. [`get_pull_request`](/reference/api/pull-requests/get-pull-request/)
387returns `rules`: what is `unmet`, what you may bypass (`bypassable`),
388and what rulesets in evaluate would refuse (`evaluate`).
389
390Every operation is in the [API reference](/reference/api/rules/list-repo-rulesets/).