Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

Commit

Repositories can be forked into a workspace, and pull requests merge by merge commit, squash, rebase or fast-forward, with every repository setting for pull requests, commits, pushes and issues. Forks: a fork is an ordinary repository with parent_id (repos 0019; fork_of still means only a pull request's hidden working copy) and forked_from, which is never cleared. Forking (web Fork button and page, POST /repos/{owner}/{name}/forks, MCP repository.fork, g1t repo fork) copies the code (the default branch only unless asked) and description, nothing else; Actions (actions 0023, Settings → Actions → Run workflows) and Issues (work has_issues) start off in a fork and stay off once it is detached unless someone turns them on. Public repositories can always be forked, private ones only with Allow forking, and the fork stays private; an hourly sweep deletes (restorably) a private fork whose maker can no longer read its parent (identity can_read_repo). A fork counts at its full size toward its workspace's storage, and a free workspace's private cap is checked first. Artifacts forks in the source's namespace, or the code is copied when the workspace keeps its data in the EU. A fork cannot change its visibility; a parent going public or private detaches its forks (repo.detached), and a purged parent's forks move to its parent. Sync fork (POST merge-upstream, g1t repo sync) fast-forwards, merges line by line, or names the clashing files, under the fork's own rules. Pull requests from a fork's branch (head_repo, work 0041) are reviewed, checked and merged on the parent; their runs are untrusted (no secrets, read-only token) whoever opened them; a reader of the parent sees a private fork's pull request diff only through commits the parent already holds; forks list, detach and the new-pull-request head picker complete it. A token for chosen repositories can no longer create a repository by forking or creating one. Merging: repos land makes merge commits, squashes (author the pull request's, Co-authored-by for the others, Signed-off-by where required) and rebases with line-level three-way merges, refusing with the clashing files, and fast-forward lands the checked commit as before; limits are 1,000 files, 100 line merges and 250 rebased commits. Update branch merges or rebases (PUT update-branch, g1t pr update-branch --rebase), honouring expected_head. Work (0042) adds the settings: allowed methods and the default, default messages for merge and squash with custom templates ({title} {number} {body} {branch} {head_repo} {base} {author} {commits} {issues} {co_authors}), always suggest updating branches, allow auto-merge, delete head branches on merge, require sign-off on web commits, allow commit comments, auto-close issues; merge_choices tells the merge box what may be used and the default messages. Rules' allowed_merge_methods are enforced and linear history refuses merge commits. A behind pull request, a fork's included, now merges directly unless it must be up to date. Auto-merge per pull request (PUT/DELETE auto-merge, g1t pr merge --auto) merges as whoever turned it on, checked against their access now and then, and turns itself off with a note and a notification when it no longer can, or when someone else pushes. Closing keywords (Fixes #12, closes owner/repo#3) close every issue named; head branches are deleted and restored from the pull request. Commit comments (work 0043) on a commit or a line, with notifications and commit_comment.* events; a per-push branch and tag limit (repos 0020, max_ref_updates); Include Git LFS objects in archives is shown, off and marked coming soon. The site's settings, merge box (methods, messages, auto-merge, update, delete and restore branch), commit page comments, fork pages and sync bar; the API (fork, forks, sync, detach, actions permissions, merge options, update branch, auto-merge, head branch, commit comments), MCP and CLI; docs: forking guide, forks concept page, pull requests, managing repositories, rules, Actions, webhooks, limitations, MCP reference and llms.txt. Deploy migrations first: repos 0019 and 0020, work 0041 to 0043, actions 0023; then identity, repos, work, actions, security, events, the API and the site together. Not yet: the merge queue lands the combination it tested as it is, whatever method was asked; Actions reads its settings more often than it needs; rebase force-moves branches in the git store, to be confirmed live. Checked: cargo test --workspace (repos 333, contracts 309, api 202, work 184, identity 174, actions 111, security 89, rules 74, events 57, the CLI's 105 and 51, all others passing), wasm32 checks of repos, work, actions, api, identity, security, events and search, the site's typecheck, tests (926) and production build, the contracts' and mobile app's typechecks, and the docs build; the site's new pages rendered with fixtures.

syntaqxcommitted Parentfade4d1Browse files
141 files+1864−101125 files not counted0/141 viewed
+40−2
132132 Op::ArchiveRepo,
133133 Op::UnarchiveRepo,
134134 Op::TransferRepo,
135+ Op::ForkRepo,
136+ Op::ListForks,
137+ Op::SyncFork,
138+ Op::DetachFork,
135139 Op::DeleteRepo,
136140 Op::ListDeletedRepos,
137141 Op::RestoreRepo,
144148 ],
145149 ),
146150 (
151+ "Commit comments",
152+ "Comments on a commit, as a whole or on one line of a file it changed. Anyone who can read a repository reads them, and may comment unless the repository turned commit comments off.",
153+ &[
154+ Op::ListRepoCommitComments,
155+ Op::ListCommitComments,
156+ Op::CreateCommitComment,
157+ Op::UpdateCommitComment,
158+ Op::DeleteCommitComment,
159+ ],
160+ ),
161+ (
147162 "Repository insights",
148163 "What a repository's default branch says about it, read in the background and kept by commit: the languages it is written in, who made it, and its license.",
149164 &[Op::About(AboutOp::GetLanguages), Op::About(AboutOp::ListContributors), Op::About(AboutOp::GetLicense)],
372387 ),
373388 (
374389 "Pull requests",
375− "A proposed change in its own fork or on a branch. Several can be made for one issue; the one merged resolves it.",
390+ "A proposed change in its own fork or on a branch. Several can be made for one issue; the one merged resolves it. It merges by a merge commit, a squash, a rebase or a fast-forward, now or with auto-merge once everything it needs is met.",
376391 &[
377392 Op::ListPullRequests,
378393 Op::CreatePullRequest,
384399 Op::RequestReviewers,
385400 Op::RemoveRequestedReviewers,
386401 Op::ReviewPullRequest,
402+ Op::GetPullRequestMergeOptions,
387403 Op::MergePullRequest,
404+ Op::UpdatePullRequestBranch,
405+ Op::EnablePullRequestAutoMerge,
406+ Op::DisablePullRequestAutoMerge,
407+ Op::DeletePullRequestHeadBranch,
408+ Op::RestorePullRequestHeadBranch,
388409 Op::ClosePullRequest,
389410 Op::ReopenPullRequest,
390411 Op::GetMergeQueue,
519540 Op::Protection(ProtectionOp::SetForkPrApproval),
520541 Op::Protection(ProtectionOp::GetActionsAccess),
521542 Op::Protection(ProtectionOp::SetActionsAccess),
543+ Op::Protection(ProtectionOp::GetActionsPermissions),
544+ Op::Protection(ProtectionOp::SetActionsPermissions),
522545 Op::Protection(ProtectionOp::CreateRepositoryDispatch),
523546 Op::Protection(ProtectionOp::GetWorkspaceWorkflowPermissions),
524547 Op::Protection(ProtectionOp::SetWorkspaceWorkflowPermissions),
643666 Op::DeclineInvitation => "Decline a workspace invitation",
644667 Op::RevokeWorkspaceInvite => "Revoke a workspace's invite",
645668 Op::TransferRepo => "Transfer a repository",
669+ Op::ForkRepo => "Fork a repository",
670+ Op::ListForks => "List forks",
671+ Op::SyncFork => "Sync a fork",
672+ Op::DetachFork => "Detach a fork",
646673 Op::RenameRepo => "Rename a repository",
647674 Op::RenameBranch => "Rename a branch",
648675 Op::CreateBranch => "Create a branch",
702729 Op::ConvertPullRequestToDraft => "Convert a pull request to a draft",
703730 Op::GetPullRequestChanges => "Get a pull request's changes",
704731 Op::MergePullRequest => "Merge a pull request",
732+ Op::GetPullRequestMergeOptions => "Get a pull request's merge options",
733+ Op::UpdatePullRequestBranch => "Update a pull request's branch",
734+ Op::EnablePullRequestAutoMerge => "Enable auto-merge",
735+ Op::DisablePullRequestAutoMerge => "Disable auto-merge",
736+ Op::DeletePullRequestHeadBranch => "Delete a pull request's branch",
737+ Op::RestorePullRequestHeadBranch => "Restore a pull request's branch",
738+ Op::ListRepoCommitComments => "List a repository's commit comments",
739+ Op::ListCommitComments => "List a commit's comments",
740+ Op::CreateCommitComment => "Comment on a commit",
741+ Op::UpdateCommitComment => "Edit a commit comment",
742+ Op::DeleteCommitComment => "Delete a commit comment",
705743 Op::ListEvents => "List repository events",
706744 Op::ListIntegrations => "List integrations",
707745 Op::ConnectIntegration => "Connect an integration",
825863 /// free workspace's private storage has no room for it.
826864 fn may_need_payment(op: Op) -> Option<&'static str> {
827865 match op {
828− Op::UpdateRepo | Op::SetRepoVisibility | Op::TransferRepo => Some(
866+ Op::UpdateRepo | Op::SetRepoVisibility | Op::TransferRepo | Op::ForkRepo => Some(
829867 "A free workspace's private storage has no room for this private repository.",
830868 ),
831869 _ => None,
+605−10
1313 use g1t_contracts::identity::AgentScope;
1414 use g1t_contracts::events::Event;
1515 use g1t_contracts::identity::{CreateWorkspaceArgs, UpdateWorkspaceArgs, Workspace};
16−use g1t_contracts::repos::{CreateArgs, GetArgs, ListArgs as ListReposArgs, Repo, RepoPath};
16+use g1t_contracts::repos::{
17+ CreateArgs, DetachForkArgs, ForkRepoArgs, ForksArgs, GetArgs, ListArgs as ListReposArgs, Repo, RepoPath, SyncForkArgs,
18+};
1719 use g1t_contracts::teams::{
1820 CreateTeamArgs, DeleteTeamArgs, ListTeamsArgs, RemoveTeamMemberArgs, RemoveTeamRepoArgs, ReviewAlgorithm,
1921 ReviewAssignment, SetTeamCreationArgs, SetTeamMemberArgs, SetTeamRepoArgs, Team, TeamArgs, TeamCreation, TeamRole,
4244 use crate::rules::RulesOp;
4345 use crate::security::SecurityOp;
4446 use g1t_contracts::inbox::{Reason, Severity, WATCH_EVENTS, WatchLevel};
47+use g1t_contracts::repos::UpdateMethod;
48+use g1t_contracts::rules::MergeMethod;
4549 use g1t_contracts::work::*;
4650 use g1t_contracts::{FailureCode, Outcome, Viewer};
4751 use serde::Serialize;
142146 CreateRepo,
143147 UpdateRepo,
144148 TransferRepo,
149+ ForkRepo,
150+ ListForks,
151+ SyncFork,
152+ DetachFork,
145153 RenameRepo,
146154 RenameBranch,
147155 CreateBranch,
197205 ReopenPullRequest,
198206 GetPullRequestChanges,
199207 MergePullRequest,
208+ GetPullRequestMergeOptions,
209+ UpdatePullRequestBranch,
210+ EnablePullRequestAutoMerge,
211+ DisablePullRequestAutoMerge,
212+ DeletePullRequestHeadBranch,
213+ RestorePullRequestHeadBranch,
214+ ListRepoCommitComments,
215+ ListCommitComments,
216+ CreateCommitComment,
217+ UpdateCommitComment,
218+ DeleteCommitComment,
200219 ListEvents,
201220 ListIntegrations,
202221 ConnectIntegration,
475494 })
476495 }
477496
497+fn commit_sha_schema() -> Value {
498+ json!({ "type": "string", "description": "The commit's hash, as list_commits gives it." })
499+}
500+
501+fn commit_comment_id_schema() -> Value {
502+ json!({
503+ "type": "string",
504+ "description": "The commit comment's id, as list_commit_comments and list_repo_commit_comments give it.",
505+ })
506+}
507+
478508 fn workspace_schema() -> Value {
479509 json!({ "type": "string", "description": "The workspace's slug, e.g. \"flagon-io\"." })
480510 }
705735 properties
706736 }
707737
738+/// How a pull request merges.
739+fn merge_method_schema(description: &str) -> Value {
740+ json!({
741+ "type": "string",
742+ "enum": MergeMethod::ALL.map(MergeMethod::as_str),
743+ "description": description,
744+ })
745+}
746+
747+/// A merge method given by name, if one was given: `Err` names the ones
748+/// there are.
749+fn merge_method(input: &Value, key: &str) -> std::result::Result<Option<MergeMethod>, String> {
750+ match input[key].as_str().map(str::trim).filter(|given| !given.is_empty()) {
751+ None => Ok(None),
752+ Some(given) => MergeMethod::ALL
753+ .into_iter()
754+ .find(|method| method.as_str().eq_ignore_ascii_case(given))
755+ .map(Some)
756+ .ok_or_else(|| format!("{key} must be merge, squash, rebase or fast_forward, not {given}.")),
757+ }
758+}
759+
760+/// What a merge or squash commit says by default.
761+const COMMIT_MESSAGE_DEFAULTS: [(&str, CommitMessageDefault); 5] = [
762+ ("default", CommitMessageDefault::Default),
763+ ("pull_title", CommitMessageDefault::PullTitle),
764+ ("pull_title_and_description", CommitMessageDefault::PullTitleAndDescription),
765+ ("pull_title_and_commits", CommitMessageDefault::PullTitleAndCommits),
766+ ("custom", CommitMessageDefault::Custom),
767+];
768+
769+fn commit_message_default(input: &Value, key: &str) -> std::result::Result<Option<CommitMessageDefault>, String> {
770+ match input[key].as_str().map(str::trim).filter(|given| !given.is_empty()) {
771+ None => Ok(None),
772+ Some(given) => COMMIT_MESSAGE_DEFAULTS
773+ .iter()
774+ .find(|(name, _)| name.eq_ignore_ascii_case(given))
775+ .map(|(_, value)| Some(*value))
776+ .ok_or_else(|| {
777+ format!("{key} must be default, pull_title, pull_title_and_description, pull_title_and_commits or custom, not {given}.")
778+ }),
779+ }
780+}
781+
708782 fn alert_id_schema() -> Value {
709783 json!({
710784 "type": "string",
713787 }
714788
715789 impl Op {
716− pub const ALL: [Op; 378] = [
790+ pub const ALL: [Op; 395] = [
717791 Op::Whoami,
718792 Op::GetWorkspace,
719793 Op::CreateWorkspace,
744818 Op::CreateRepo,
745819 Op::UpdateRepo,
746820 Op::TransferRepo,
821+ Op::ForkRepo,
822+ Op::ListForks,
823+ Op::SyncFork,
824+ Op::DetachFork,
747825 Op::RenameRepo,
748826 Op::RenameBranch,
749827 Op::CreateBranch,
799877 Op::ReopenPullRequest,
800878 Op::GetPullRequestChanges,
801879 Op::MergePullRequest,
880+ Op::GetPullRequestMergeOptions,
881+ Op::UpdatePullRequestBranch,
882+ Op::EnablePullRequestAutoMerge,
883+ Op::DisablePullRequestAutoMerge,
884+ Op::DeletePullRequestHeadBranch,
885+ Op::RestorePullRequestHeadBranch,
886+ Op::ListRepoCommitComments,
887+ Op::ListCommitComments,
888+ Op::CreateCommitComment,
889+ Op::UpdateCommitComment,
890+ Op::DeleteCommitComment,
802891 Op::ListEvents,
803892 Op::ListIntegrations,
804893 Op::ConnectIntegration,
10331122 Op::Protection(ProtectionOp::SetForkPrApproval),
10341123 Op::Protection(ProtectionOp::GetActionsAccess),
10351124 Op::Protection(ProtectionOp::SetActionsAccess),
1125+ Op::Protection(ProtectionOp::GetActionsPermissions),
1126+ Op::Protection(ProtectionOp::SetActionsPermissions),
10361127 Op::Protection(ProtectionOp::CreateRepositoryDispatch),
10371128 Op::Protection(ProtectionOp::GetWorkspaceWorkflowPermissions),
10381129 Op::Protection(ProtectionOp::SetWorkspaceWorkflowPermissions),
11311222 Op::CreateRepo => "create_repo",
11321223 Op::UpdateRepo => "update_repo",
11331224 Op::TransferRepo => "transfer_repo",
1225+ Op::ForkRepo => "fork_repo",
1226+ Op::ListForks => "list_forks",
1227+ Op::SyncFork => "sync_fork",
1228+ Op::DetachFork => "detach_fork",
11341229 Op::RenameRepo => "rename_repo",
11351230 Op::RenameBranch => "rename_branch",
11361231 Op::CreateBranch => "create_branch",
11861281 Op::ConvertPullRequestToDraft => "convert_pull_request_to_draft",
11871282 Op::GetPullRequestChanges => "get_pull_request_changes",
11881283 Op::MergePullRequest => "merge_pull_request",
1284+ Op::GetPullRequestMergeOptions => "get_pull_request_merge_options",
1285+ Op::UpdatePullRequestBranch => "update_pull_request_branch",
1286+ Op::EnablePullRequestAutoMerge => "enable_pull_request_auto_merge",
1287+ Op::DisablePullRequestAutoMerge => "disable_pull_request_auto_merge",
1288+ Op::DeletePullRequestHeadBranch => "delete_pull_request_head_branch",
1289+ Op::RestorePullRequestHeadBranch => "restore_pull_request_head_branch",
1290+ Op::ListRepoCommitComments => "list_repo_commit_comments",
1291+ Op::ListCommitComments => "list_commit_comments",
1292+ Op::CreateCommitComment => "create_commit_comment",
1293+ Op::UpdateCommitComment => "update_commit_comment",
1294+ Op::DeleteCommitComment => "delete_commit_comment",
11891295 Op::ListEvents => "list_events",
11901296 Op::ListIntegrations => "list_integrations",
11911297 Op::ConnectIntegration => "connect_integration",
13821488 Op::ListRepos => "Repositories you can see, optionally filtered by a search query.",
13831489 Op::GetRepo => "One repository's details.",
13841490 Op::UpdateRepo => {
1385− "Change a repository's description, website, topics and default branch, whether its default branch is protected, and whether it is private. Only the fields given are changed. Its description, website and topics need the Maintain role or higher; protecting its default branch, making it public or private and changing its default branch need the Admin role (and making it public or private, the workspace's member privileges to allow it, unless you are an owner), and a free workspace takes a private repository only while its private storage has room. A protected branch refuses pushes and changes only by merging a pull request. A new default branch must already exist; open pull requests then merge into it."
1491+ "Change a repository's description, website, topics and default branch, whether its default branch is protected, whether it is private, whether a private repository may be forked, and the most branches and tags one push may change (max_ref_updates). Only the fields given are changed. Its description, website and topics need the Maintain role or higher; protecting its default branch, making it public or private, allowing forking, max_ref_updates and changing its default branch need the Admin role (and making it public or private, the workspace's member privileges to allow it, unless you are an owner), and a free workspace takes a private repository only while its private storage has room. A protected branch refuses pushes and changes only by merging a pull request. A new default branch must already exist; open pull requests then merge into it. A fork cannot be made public or private: it is as visible as its parent until it is detached (detach_fork)."
13861492 }
13871493 Op::RenameRepo => {
13881494 "Give a repository a new name in its workspace. Needs the Admin role. Everything stays with it: git data, issues, pull requests, workflow runs, deployments, secrets and webhooks. Its old address keeps working: web pages, git remotes and API calls redirect to the new one until a repository is made at the old address. The new name must be free in the workspace, including names held by recently deleted repositories."
14201526 Op::TransferRepo => {
14211527 "Move a repository to another workspace, keeping its name. You must own both workspaces, and the destination must not already have a repository of that name; a free destination takes a private repository only if its private storage has room. Everything moves with it: git data, issues, pull requests, comments, labels, workflow runs, deployments, its project, and its own secrets, variables and webhooks. Its old address keeps working: web pages, git remotes and API calls redirect to the new one until a repository is made at the old address. Usage from now on is charged to the new workspace."
14221528 }
1529+ Op::ForkRepo => {
1530+ "Fork a repository: copy it into a workspace you can create repositories in, as a new repository that remembers the one it came from (its parent). Anyone who can read a public repository may fork it; a private one only when its admins allow forking (allow_forking on update_repo). A fork is exactly as visible as its parent: a public repository's fork is public, a private one's private. Only its code and description come with it: the default branch, or every branch and tag with default_branch_only false. Its issues, pull requests, settings, rulesets, secrets, variables, webhooks, environments and deployments stay behind; issues are off in the fork, and Actions is off until an admin of the fork turns it on. Its full size counts toward the destination workspace's storage, and a free workspace takes a private fork only while its private storage has room. workspace is the destination's slug: your personal workspace when left out. name is the parent's when left out. Returns the fork, with parent naming the repository it came from."
1531+ }
1532+ Op::ListForks => {
1533+ "A repository's forks that you can see, newest first, each with parent naming the repository. limit is at most 100, 30 when left out. Forks of its forks are listed on them."
1534+ }
1535+ Op::SyncFork => {
1536+ "Bring a fork's branch up to date with its parent: branch (the fork's default branch when left out) takes in the parent's branch of the same name, or the parent's default branch for the fork's default branch. Needs the Write role on the fork and read access to its parent. A branch that is only behind moves forward (fast_forwarded); one with commits of its own gets a merge commit when the two sides' changes do not clash, edits to the same file merging line by line (merged); when both changed the same lines (or one deleted a file the other changed, or both changed a binary file) nothing is changed (conflicted, with the files in conflicts, at most 20): merge the parent's branch in a clone and push, or open a pull request. Returns status, branch, head (the branch's commit afterwards), behind and ahead (the commits each side had that the other lacked)."
1537+ }
1538+ Op::DetachFork => {
1539+ "Detach a fork from its parent: it becomes an ordinary repository with no parent, and can then be made public or private. Needs the Admin role on the fork. Its code, settings, and the pull requests it already opened into the parent stay as they are; it can no longer be synced or open new pull requests into the parent. It cannot be attached again."
1540+ }
14231541 Op::GetRepoSettings => {
1424− "How a repository handles pull requests: whether they merge by themselves, and its default branch's protection as the rules of its rulesets stack there: the checks that must pass (required_checks), the approvals a merge needs, whether its code owners must approve (`require_code_owner_review`), whether required checks can be bypassed, whether a pull request must be up to date, and the merge queue. The same rules hold for every pull request, whoever opened it. list_repo_rulesets and get_branch_rules show every rule."
1542+ "How a repository handles pull requests, commits and issues: the merge methods it allows and the one offered first, what merge and squash commits say by default (merge_commit_message, squash_commit_message, and the templates for custom), whether pull requests may turn on auto-merge, whether their branches are deleted once merged, whether to suggest updating branches, whether web commits are signed off, whether commit comments are allowed, whether merged pull requests close the issues they name, and whether issues can be opened (issues_enabled); also whether they merge by themselves, and its default branch's protection as the rules of its rulesets stack there: the checks that must pass (required_checks), the approvals a merge needs, whether its code owners must approve (`require_code_owner_review`), whether required checks can be bypassed, whether a pull request must be up to date, and the merge queue. The same rules hold for every pull request, whoever opened it. list_repo_rulesets and get_branch_rules show every rule."
14251543 }
14261544 Op::UpdateRepoSettings => {
1427− "Change how a repository handles pull requests. Only the fields given are changed; required_checks replaces the whole list. The branch protection fields (required_checks, require_up_to_date, required_approvals, count_agent_approvals, allow_ignoring_checks, merge_queue, require_code_owner_review) are written to the repository's \"Default branch protection\" ruleset, made when it has none; rules only rulesets have stay as they are. A required check is named as list_check_names gives it: a workflow's name, such as CI, or another status's context, such as g1t / deploy. Needs the Maintain role or higher, and the Admin role to change a branch protection field."
1545+ "Change how a repository handles pull requests, commits and issues. Only the fields given are changed; required_checks replaces the whole list. At least one merge method must stay allowed, and default_merge_method must be one of them. A custom commit message (merge_commit_message or squash_commit_message custom) uses its template, whose {title}, {number}, {body}, {branch}, {head_repo}, {base}, {author}, {commits}, {issues} and {co_authors} are filled in; pull_title_and_commits is for squash commits only. The branch protection fields (required_checks, require_up_to_date, required_approvals, count_agent_approvals, allow_ignoring_checks, merge_queue, require_code_owner_review) are written to the repository's \"Default branch protection\" ruleset, made when it has none; rules only rulesets have stay as they are. A required check is named as list_check_names gives it: a workflow's name, such as CI, or another status's context, such as g1t / deploy. Needs the Maintain role or higher, and the Admin role to change a branch protection field."
14281546 }
14291547 Op::ListCheckNames => {
14301548 "The check names reported on a repository's commits in the last 30 days, most recent first, with the events each was reported for: the names update_repo_settings takes in required_checks. A workflow's runs report a check named after the workflow; a check required on the default branch must be reported on a pull request's head (pull_request events) and, with the merge queue on, on its queued state (merge_group events)."
15411659 "What a pull request changes: the files it touches and their line-by-line diff against the commit it started from. Use it to review a pull request or to compare several made for the same issue. A large one lists every file (up to 300; truncated says when there were more) but has the lines of only the first, about 20,000 lines read: the files after that come with omitted true and no lines or counts. Ask again with paths (up to 50 at a time) to have those files' lines."
15421660 }
15431661 Op::MergePullRequest => {
1544− "Land a pull request on its base, the branch it merges into (the default branch unless it names another). Merging needs the Write role or higher, and only once it is marked ready and it meets every rule that holds for its base (see rules and required_checks on get_pull_request: approvals, checks, deployments, merge windows and the rest, from the repository's and its workspace's rulesets); the refusal names the first rule not met. With ignore_checks, someone who may merge can bypass required checks where the rule allows it; with bypass_rules, someone a ruleset lists as a bypass actor merges past its rules, and it is recorded. Merging into the default branch resolves the issue it was made for: the issue closes recording this pull request, and the other pull requests still in progress for that issue close as superseded; merging into another branch leaves the issue open. Where the repository has a merge queue, a pull request into the default branch joins the queue instead of landing at once. If its base has moved since the pull request was opened, it is brought up to date first and lands when that is done; a repository that requires pull requests into its default branch to be up to date refuses instead, so pull the base into its fork or branch, push, and merge again. Check status in the result to see whether it has landed."
1662+ "Land a pull request on its base, the branch it merges into (the default branch unless it names another). merge_method says how: merge (a merge commit joins its branch to the base, its commits included), squash (one new commit holds everything it changed), rebase (each of its commits is made again on top of the base, in order) or fast_forward (the base moves to its head, exactly the commit that was checked; only when its branch contains the base's head). Left out, the repository's default_merge_method. A method the repository does not allow (allow_merge_commit, allow_squash_merge, allow_rebase_merge, allow_fast_forward on update_repo_settings), or that a rule for the base does not (allowed_merge_methods, and required linear history, which refuses merge), is refused. commit_title and commit_message are a merge or squash commit's message: its first line and the rest; left out, the repository's merge_commit_message or squash_commit_message decides. With sha, the merge is refused (409) if the pull request's head is no longer that commit. delete_branch deletes its branch once it has merged (left out, the repository's delete_branch_on_merge); restore_pull_request_head_branch brings it back. Merging needs the Write role or higher, and only once it is marked ready and it meets every rule that holds for its base (see rules and required_checks on get_pull_request: approvals, checks, deployments, merge windows and the rest, from the repository's and its workspace's rulesets); the refusal names the first rule not met. With ignore_checks, someone who may merge can bypass required checks where the rule allows it; with bypass_rules, someone a ruleset lists as a bypass actor merges past its rules, and it is recorded. Merging into the default branch closes the issues in its closes: the issue it was made for and those its title and description name after a closing keyword (Fixes #12), where the repository closes issues (auto_close_issues); the other pull requests still in progress for its issue close as superseded. Merging into another branch closes nothing. Where the repository has a merge queue, a pull request into the default branch joins the queue instead of landing at once. If its base has moved since the pull request was opened, it is brought up to date first and lands when that is done; a repository that requires pull requests into its default branch to be up to date refuses instead: update_pull_request_branch, then merge again. Check status in the result to see whether it has landed, and merge_method and merge_commit for how."
1663+ }
1664+ Op::GetPullRequestMergeOptions => {
1665+ "How a pull request may merge now, as the merge box offers it to you: the methods it may merge by (the repository's, as the rules for its base allow them) and the one offered first (default_method); each method's default commit title and message, and why one cannot be used now (unavailable); whether it is behind its base, can fast-forward as it is, should be offered an update (suggest_update) or must be up to date (require_up_to_date); whether it goes through the merge queue; whether you may turn on auto-merge; whether deleting its branch starts ticked and is possible; and whether commits made merging it are signed off."
1666+ }
1667+ Op::UpdatePullRequestBranch => {
1668+ "Bring a pull request's branch up to date with its base: update_method merge (the default) makes a merge commit on its branch that takes in the base's new commits; rebase makes each of its commits again on top of the base's head and moves its branch there, rewriting its history. Pushed as you, so you must be able to push to its branch: the Write role for a branch of the repository, its owner for a working copy, and push access to the fork for a branch of a fork. With expected_head_sha, refused (409) if its head is no longer that commit. Edits to the same file merge line by line; a rebase drops merge commits and commits the base already has, keeps authors, dates and messages, and moves its branch only if its head is still where it was. Returns outcome: updated (with commit, its head now, and previous), up_to_date, or needs_agent when both sides changed the same lines (with reason, detail and the paths): nothing was pushed, so merge or rebase in a clone and push."
1669+ }
1670+ Op::EnablePullRequestAutoMerge => {
1671+ "Turn on auto-merge for a pull request: it merges on its own, as you, once its required checks, reviews and rules allow, with merge_method (the repository's default when left out) and, for merge and squash, commit_title and commit_message. Needs the Write role, since it merges as you, and a repository that allows auto-merge (allow_auto_merge). A method the repository or a rule for its base does not allow is refused. A pull request that may merge already merges at once instead. Auto-merge is turned off when it merges or closes, when someone turns it off, and when someone other than whoever turned it on pushes to it. Publishes pull.auto_merge_enabled. Returns the pull request, with auto_merge saying how and who asked."
1672+ }
1673+ Op::DisablePullRequestAutoMerge => {
1674+ "Turn off auto-merge for a pull request: it waits for someone to merge it. Needs the Write role, or to be its author. Publishes pull.auto_merge_disabled. Returns the pull request, with auto_merge null."
1675+ }
1676+ Op::DeletePullRequestHeadBranch => {
1677+ "Delete a merged or closed pull request's branch: a branch of the repository, or of a fork you can push to. Needs the Write role (push access to the fork, for a fork's branch). Refused while the pull request is open, for the default branch, for a branch an open pull request still uses, and where rules protect it. head_branch_deleted is then true, and restore_pull_request_head_branch makes it again at its head. Publishes pull.head_branch_deleted. Returns the pull request."
1678+ }
1679+ Op::RestorePullRequestHeadBranch => {
1680+ "Make a pull request's deleted branch again, at the pull request's head commit (head_commit). Needs the Write role (push access to the fork, for a fork's branch). Refused when a branch of that name exists again. Publishes pull.head_branch_restored. Returns the pull request."
1681+ }
1682+ Op::ListRepoCommitComments => {
1683+ "A repository's commit comments, newest first, 30 a page (page from 1). Each names its commit; one on a line of a file has path and line. Anyone who can read the repository can read them."
1684+ }
1685+ Op::ListCommitComments => {
1686+ "A commit's comments, oldest first: on the whole commit, or on one line of a file it changed (path and line). sha is the commit's hash. Anyone who can read the repository can read them."
1687+ }
1688+ Op::CreateCommitComment => {
1689+ "Comment on a commit, as a whole or, with path and line, on one line of a file the commit changed (a line of the file as the commit left it). Anyone who can read the repository may, unless it has turned commit comments off (allow_commit_comments on update_repo_settings). The commit's author and anyone mentioned are notified. Publishes commit_comment.created."
1690+ }
1691+ Op::UpdateCommitComment => {
1692+ "Change a commit comment's text, named by comment_id. Its author only. Publishes commit_comment.edited."
15451693 }
1694+ Op::DeleteCommitComment => {
1695+ "Delete a commit comment, named by comment_id. Its author may, and so may anyone with the Maintain role or higher. This cannot be undone. Publishes commit_comment.deleted."
1696+ }
15461697 Op::ListEvents => {
15471698 "The timeline of a repository: pushes, issues, pull requests, comments, sessions, claims and integration results, newest first; page back with before. To follow it, give after (an event id): the events written after that one come oldest first, and the last one's id is the next call's after. With after and wait (seconds, at most 25), a call that finds nothing holds on until a matching event is written or the wait ends, then returns what it has, perhaps nothing. Narrow it with types, numbers (issues and pull requests, matching events about them and comments on them), since and limit (50 by default, at most 200)."
15481699 }
20522203 }),
20532204 &["repo", "to"],
20542205 ),
2206+ Op::ForkRepo => object(
2207+ json!({
2208+ "repo": repo_schema(),
2209+ "workspace": {
2210+ "type": "string",
2211+ "description": "The slug of the workspace to fork it into, e.g. \"flagon-io\". Your personal workspace when left out.",
2212+ },
2213+ "name": { "type": "string", "description": "The fork's name; the repository's own when left out." },
2214+ "description": { "type": "string", "description": "The fork's description; the repository's own when left out, none when empty." },
2215+ "default_branch_only": {
2216+ "type": "boolean",
2217+ "description": "Copy only the default branch (the default). false copies every branch and tag.",
2218+ },
2219+ }),
2220+ &["repo"],
2221+ ),
2222+ Op::ListForks => object(
2223+ json!({
2224+ "repo": repo_schema(),
2225+ "limit": { "type": "integer", "minimum": 1, "maximum": 100, "description": "At most this many; 30 when left out." },
2226+ }),
2227+ &["repo"],
2228+ ),
2229+ Op::SyncFork => object(
2230+ json!({
2231+ "repo": repo_schema(),
2232+ "branch": { "type": "string", "description": "The fork's branch to sync; its default branch when left out." },
2233+ }),
2234+ &["repo"],
2235+ ),
2236+ Op::DetachFork => repo_only(),
20552237 Op::GetRepo | Op::ListLabels | Op::AddDefaultLabels => repo_only(),
20562238 Op::CreateLabel => object(
20572239 json!({
21392321 "type": "string",
21402322 "description": "Make this existing branch the default: the one clones check out and pull requests merge into.",
21412323 },
2324+ "allow_forking": {
2325+ "type": "boolean",
2326+ "description": "Let people who can read this private repository fork it; their forks stay private. Public repositories can always be forked.",
2327+ },
2328+ "max_ref_updates": {
2329+ "type": "integer",
2330+ "minimum": 0,
2331+ "maximum": 1000,
2332+ "description": "The most branches and tags one push may change, 1 to 1,000; a push that would change more is declined whole. 0 for no limit.",
2333+ },
21422334 }),
21432335 &["repo"],
21442336 ),
22682460 "type": "boolean",
22692461 "description": "Refuse to merge until the code owners of every file a pull request changes, as the CODEOWNERS file of the branch it merges into names them, have approved it, as many as each section asks. Only people's approvals count, and g1t's only where the file names @g1t.",
22702462 },
2463+ "allow_merge_commit": {
2464+ "type": "boolean",
2465+ "description": "Let pull requests merge with a merge commit (merge). At least one method stays allowed.",
2466+ },
2467+ "allow_squash_merge": {
2468+ "type": "boolean",
2469+ "description": "Let pull requests merge as one squashed commit (squash).",
2470+ },
2471+ "allow_rebase_merge": {
2472+ "type": "boolean",
2473+ "description": "Let pull requests merge by making each of their commits again on the base (rebase).",
2474+ },
2475+ "allow_fast_forward": {
2476+ "type": "boolean",
2477+ "description": "Let pull requests merge by moving the base to their head, with no new commit (fast_forward). Off unless turned on.",
2478+ },
2479+ "default_merge_method": merge_method_schema("The method offered first, and used when a merge names none. Must be allowed."),
2480+ "merge_commit_message": {
2481+ "type": "string",
2482+ "enum": ["default", "pull_title", "pull_title_and_description", "custom"],
2483+ "description": "What a merge commit says unless its merger writes it: default (\"Merge pull request #12 from owner/branch\" and its title), pull_title (its title, with its number), pull_title_and_description (and its description below), or custom (merge_commit_template).",
2484+ },
2485+ "squash_commit_message": {
2486+ "type": "string",
2487+ "enum": ["default", "pull_title", "pull_title_and_description", "pull_title_and_commits", "custom"],
2488+ "description": "What a squash commit says unless its merger writes it: default (its title for one commit, else its title and the commits' messages), pull_title, pull_title_and_description, pull_title_and_commits (its title, and each commit's message below), or custom (squash_commit_template).",
2489+ },
2490+ "merge_commit_template": {
2491+ "type": "string",
2492+ "description": "With merge_commit_message custom: the message, its first line the title. {title}, {number}, {body}, {branch}, {head_repo}, {base}, {author}, {commits}, {issues} and {co_authors} are filled in. An empty string clears it.",
2493+ },
2494+ "squash_commit_template": {
2495+ "type": "string",
2496+ "description": "With squash_commit_message custom: the message, with the same placeholders as merge_commit_template. An empty string clears it.",
2497+ },
2498+ "suggest_updating_branches": {
2499+ "type": "boolean",
2500+ "description": "Offer to update a pull request's branch whenever its base has new commits, not only when it must be up to date to merge.",
2501+ },
2502+ "allow_auto_merge": {
2503+ "type": "boolean",
2504+ "description": "Let people turn on auto-merge for a pull request, so it merges on its own once everything it needs is met.",
2505+ },
2506+ "delete_branch_on_merge": {
2507+ "type": "boolean",
2508+ "description": "Delete a pull request's branch once it merges. It can be restored from the pull request.",
2509+ },
2510+ "require_web_commit_sign_off": {
2511+ "type": "boolean",
2512+ "description": "Commits made on g1t (web edits, merge and squash commits) carry a Signed-off-by: trailer for whoever made them.",
2513+ },
2514+ "allow_commit_comments": {
2515+ "type": "boolean",
2516+ "description": "Let anyone who can read the repository comment on its commits. Existing comments stay readable either way.",
2517+ },
2518+ "auto_close_issues": {
2519+ "type": "boolean",
2520+ "description": "Close the issues a pull request names (its issue, and Fixes #12 in its title or description) when it merges into the default branch.",
2521+ },
2522+ "has_issues": {
2523+ "type": "boolean",
2524+ "description": "Whether issues can be opened in it. null goes back to the default: on for a repository, off for a fork. Existing issues stay readable either way.",
2525+ },
22712526 }),
22722527 &["repo"],
22732528 ),
24602715 },
24612716 "branch": {
24622717 "type": "string",
2463− "description": "A branch already pushed to the repository that holds the change. Leave out to get a fork.",
2718+ "description": "A branch already pushed to the repository (or to head_repo) that holds the change. Leave out to get a fork.",
24642719 },
2720+ "head_repo": {
2721+ "type": "string",
2722+ "description": "A fork of the repository, \"owner/name\", whose branch holds the change: to propose a change from your fork. Needs branch.",
2723+ },
24652724 "body": {
24662725 "type": "string",
24672726 "description": "Markdown: what changed and why. Mainly for pull requests from a branch.",
25472806 "type": "boolean",
25482807 "description": "Merge although rules are not met, where a ruleset lists you as one who may bypass it. Recorded as a bypass in its evaluations.",
25492808 },
2809+ "merge_method": merge_method_schema("How to merge: merge, squash, rebase or fast_forward. The repository's default_merge_method when left out."),
2810+ "commit_title": {
2811+ "type": "string",
2812+ "description": "For merge and squash: the commit message's first line. The repository's default when left out.",
2813+ },
2814+ "commit_message": {
2815+ "type": "string",
2816+ "description": "For merge and squash: the rest of the commit message. The repository's default when left out.",
2817+ },
2818+ "sha": {
2819+ "type": "string",
2820+ "description": "The head commit you reviewed: refused (409) if the pull request's head has moved since.",
2821+ },
2822+ "delete_branch": {
2823+ "type": "boolean",
2824+ "description": "Delete its branch once merged. The repository's delete_branch_on_merge when left out.",
2825+ },
25502826 })),
25512827 &["repo", "number"],
25522828 ),
2829+ Op::UpdatePullRequestBranch => object(
2830+ numbered(json!({
2831+ "expected_head_sha": {
2832+ "type": "string",
2833+ "description": "The head commit you saw: refused (409) if the pull request's head has moved since.",
2834+ },
2835+ "update_method": {
2836+ "type": "string",
2837+ "enum": ["merge", "rebase"],
2838+ "description": "merge (the default): a merge commit on its branch takes in the base's new commits. rebase: its commits are made again on the base's head, rewriting its branch.",
2839+ },
2840+ })),
2841+ &["repo", "number"],
2842+ ),
2843+ Op::EnablePullRequestAutoMerge => object(
2844+ numbered(json!({
2845+ "merge_method": merge_method_schema("How it will merge: merge, squash, rebase or fast_forward. The repository's default_merge_method when left out."),
2846+ "commit_title": {
2847+ "type": "string",
2848+ "description": "For merge and squash: the commit message's first line. The repository's default when left out.",
2849+ },
2850+ "commit_message": {
2851+ "type": "string",
2852+ "description": "For merge and squash: the rest of the commit message. The repository's default when left out.",
2853+ },
2854+ })),
2855+ &["repo", "number"],
2856+ ),
2857+ Op::GetPullRequestMergeOptions
2858+ | Op::DisablePullRequestAutoMerge
2859+ | Op::DeletePullRequestHeadBranch
2860+ | Op::RestorePullRequestHeadBranch => {
2861+ just_numbered()
2862+ }
2863+ Op::ListRepoCommitComments => object(
2864+ json!({
2865+ "repo": repo_schema(),
2866+ "page": { "type": "integer", "minimum": 1, "description": "From 1; 30 comments a page." },
2867+ }),
2868+ &["repo"],
2869+ ),
2870+ Op::ListCommitComments => object(
2871+ json!({ "repo": repo_schema(), "sha": commit_sha_schema() }),
2872+ &["repo", "sha"],
2873+ ),
2874+ Op::CreateCommitComment => object(
2875+ json!({
2876+ "repo": repo_schema(),
2877+ "sha": commit_sha_schema(),
2878+ "body": { "type": "string", "description": "Markdown." },
2879+ "path": {
2880+ "type": "string",
2881+ "description": "A file the commit changed, from the repository's root, to comment on one of its lines.",
2882+ },
2883+ "line": {
2884+ "type": "integer",
2885+ "minimum": 1,
2886+ "description": "With path: the line, as the commit left the file, counted from 1.",
2887+ },
2888+ }),
2889+ &["repo", "sha", "body"],
2890+ ),
2891+ Op::UpdateCommitComment => object(
2892+ json!({
2893+ "repo": repo_schema(),
2894+ "comment_id": commit_comment_id_schema(),
2895+ "body": { "type": "string", "description": "The new text, in Markdown." },
2896+ }),
2897+ &["repo", "comment_id", "body"],
2898+ ),
2899+ Op::DeleteCommitComment => object(
2900+ json!({ "repo": repo_schema(), "comment_id": commit_comment_id_schema() }),
2901+ &["repo", "comment_id"],
2902+ ),
25532903 Op::ListEvents => object(
25542904 json!({
25552905 "repo": repo_schema(),
32773627 Op::ListRepos
32783628 | Op::Search
32793629 | Op::GetRepo
3630+ | Op::ListForks
32803631 | Op::ListIssues
32813632 | Op::GetIssue
32823633 | Op::ListLabels
32893640 | Op::ListDecisions
32903641 | Op::Why
32913642 | Op::GetPullRequestChanges
3643+ | Op::GetPullRequestMergeOptions
3644+ | Op::ListRepoCommitComments
3645+ | Op::ListCommitComments
32923646 | Op::ListEvents
32933647 | Op::GetRepoSettings
32943648 | Op::ListCheckNames
33093663 | ProtectionOp::GetWorkflowPermissions
33103664 | ProtectionOp::GetForkPrApproval
33113665 | ProtectionOp::GetActionsAccess
3666+ | ProtectionOp::GetActionsPermissions
33123667 )
33133668 )
33143669 }
35963951 keep_issue_open: input["keep_issue_open"].as_bool() == Some(true),
35973952 ignore_checks: input["ignore_checks"].as_bool() == Some(true),
35983953 bypass_rules: input["bypass_rules"].as_bool() == Some(true),
3954+ merge_method: None,
3955+ commit_title: None,
3956+ commit_message: None,
3957+ expected_head: None,
3958+ delete_branch: None,
3959+ update_method: None,
35993960 };
36003961 let Services {
36013962 identity,
39884349 )
39894350 .await
39904351 }
4352+ Op::ForkRepo => {
4353+ let owner = actor();
4354+ // Your personal workspace (your username's), or the only
4355+ // one you belong to, when none is named.
4356+ let namespace = optional_text(input, "workspace").map(|slug| slug.to_lowercase()).unwrap_or_else(|| {
4357+ if owner.workspaces.iter().any(|w| w.slug == owner.username) {
4358+ owner.username.clone()
4359+ } else {
4360+ match owner.workspaces.as_slice() {
4361+ [only] => only.slug.clone(),
4362+ _ => String::new(),
4363+ }
4364+ }
4365+ });
4366+ pass(
4367+ repos,
4368+ "fork",
4369+ &ForkRepoArgs {
4370+ actor: owner,
4371+ path: repo,
4372+ namespace,
4373+ name: optional_text(input, "name"),
4374+ description: input["description"].as_str().map(str::to_owned),
4375+ default_branch_only: input["default_branch_only"].as_bool() != Some(false),
4376+ surface: Some(services.audit.surface),
4377+ },
4378+ )
4379+ .await
4380+ }
4381+ Op::ListForks => {
4382+ pass(
4383+ repos,
4384+ "forks",
4385+ &ForksArgs {
4386+ viewer: viewer.clone(),
4387+ path: repo,
4388+ limit: integer(input, "limit"),
4389+ },
4390+ )
4391+ .await
4392+ }
4393+ Op::SyncFork => {
4394+ pass(
4395+ repos,
4396+ "sync_fork",
4397+ &SyncForkArgs {
4398+ actor: actor(),
4399+ path: repo,
4400+ branch: optional_text(input, "branch"),
4401+ surface: Some(services.audit.surface),
4402+ },
4403+ )
4404+ .await
4405+ }
4406+ Op::DetachFork => {
4407+ pass(
4408+ repos,
4409+ "detach_fork",
4410+ &DetachForkArgs {
4411+ actor: actor(),
4412+ path: repo,
4413+ surface: Some(services.audit.surface),
4414+ },
4415+ )
4416+ .await
4417+ }
39914418 Op::ListRepos => {
39924419 let found: Vec<Repo> = g1t_kit::call(
39934420 repos,
40144441 .await
40154442 }
40164443 Op::UpdateRepo => {
4444+ let max_ref_updates = integer(input, "max_ref_updates");
4445+ if input.get("max_ref_updates").is_some_and(|given| !given.is_null()) && max_ref_updates.is_none() {
4446+ return failed(FailureCode::Invalid, "max_ref_updates must be a whole number, 0 for no limit.");
4447+ }
40174448 let updated = pass(
40184449 repos,
40194450 "update",
40254456 "protected": input["protected"].as_bool(),
40264457 "topics": strings(input, "topics"),
40274458 "website": input["website"].as_str(),
4459+ "allowForking": input["allow_forking"].as_bool(),
4460+ "maxRefUpdates": max_ref_updates,
40284461 "surface": services.audit.surface,
40294462 }),
40304463 )
42184651 Outcome::Fail(failure) => return Ok(Outcome::Fail(failure)),
42194652 };
42204653 let flag = |key: &str, now: bool| input[key].as_bool().unwrap_or(now);
4654+ let default_merge_method = match merge_method(input, "default_merge_method") {
4655+ Ok(method) => method.unwrap_or(current.default_merge_method),
4656+ Err(message) => return failed(FailureCode::Invalid, &message),
4657+ };
4658+ let merge_commit_message = match commit_message_default(input, "merge_commit_message") {
4659+ Ok(Some(CommitMessageDefault::PullTitleAndCommits)) => {
4660+ return failed(FailureCode::Invalid, "pull_title_and_commits is for squash commits: use it in squash_commit_message.");
4661+ }
4662+ Ok(given) => given.unwrap_or(current.merge_commit_message),
4663+ Err(message) => return failed(FailureCode::Invalid, &message),
4664+ };
4665+ let squash_commit_message = match commit_message_default(input, "squash_commit_message") {
4666+ Ok(given) => given.unwrap_or(current.squash_commit_message),
4667+ Err(message) => return failed(FailureCode::Invalid, &message),
4668+ };
4669+ // A template given empty clears it; one left out stays.
4670+ let template = |key: &str, now: &Option<String>| match input.get(key) {
4671+ None => now.clone(),
4672+ Some(given) => given.as_str().map(str::to_owned).filter(|text| !text.trim().is_empty()),
4673+ };
4674+ let has_issues = match input.get("has_issues") {
4675+ None => current.has_issues,
4676+ Some(Value::Null) => None,
4677+ Some(given) => match given.as_bool() {
4678+ Some(on) => Some(on),
4679+ None => return failed(FailureCode::Invalid, "has_issues must be true, false or null."),
4680+ },
4681+ };
42214682 let settings = RepoSettings {
4683+ allow_merge_commit: flag("allow_merge_commit", current.allow_merge_commit),
4684+ allow_squash_merge: flag("allow_squash_merge", current.allow_squash_merge),
4685+ allow_rebase_merge: flag("allow_rebase_merge", current.allow_rebase_merge),
4686+ allow_fast_forward: flag("allow_fast_forward", current.allow_fast_forward),
4687+ default_merge_method,
4688+ merge_commit_message,
4689+ squash_commit_message,
4690+ merge_commit_template: template("merge_commit_template", &current.merge_commit_template),
4691+ squash_commit_template: template("squash_commit_template", &current.squash_commit_template),
4692+ suggest_updating_branches: flag("suggest_updating_branches", current.suggest_updating_branches),
4693+ allow_auto_merge: flag("allow_auto_merge", current.allow_auto_merge),
4694+ delete_branch_on_merge: flag("delete_branch_on_merge", current.delete_branch_on_merge),
4695+ require_web_commit_sign_off: flag("require_web_commit_sign_off", current.require_web_commit_sign_off),
4696+ allow_commit_comments: flag("allow_commit_comments", current.allow_commit_comments),
4697+ auto_close_issues: flag("auto_close_issues", current.auto_close_issues),
4698+ has_issues,
42224699 auto_merge: flag("auto_merge", current.auto_merge),
42234700 required_checks: strings(input, "required_checks").unwrap_or(current.required_checks.clone()),
42244701 require_up_to_date: flag("require_up_to_date", current.require_up_to_date),
45335010 Op::GetPullRequest => pass(work, "get_pull", &view()).await,
45345011 Op::CreatePullRequest => {
45355012 let user = actor();
5013+ // A fork named but not as `owner/name` is refused, never
5014+ // read as the repository's own branch of that name.
5015+ let head_repo = match optional_text(input, "head_repo") {
5016+ Some(_) => match repo_path(&json!({ "repo": input["head_repo"] })) {
5017+ Some(path) => Some(path),
5018+ None => return failed(FailureCode::Invalid, "Give head_repo as owner/name."),
5019+ },
5020+ None => None,
5021+ };
45365022 let opened: Outcome<Pull> = call(
45375023 work,
45385024 "open_pull",
45435029 title: text(input, "title"),
45445030 body: text(input, "body"),
45455031 branch: optional_text(input, "branch"),
5032+ head_repo,
45465033 // Unnamed, the change is its author's, unless an agent's token opened it.
45475034 agent: optional_text(input, "agent")
45485035 .unwrap_or_else(|| if g1t_contracts::rules::is_agent(&user) { "agent".into() } else { user.username.clone() }),
45575044 Outcome::Fail(failure) => return Ok(Outcome::Fail(failure)),
45585045 };
45595046 // Where to push. A pull request from a branch has no fork:
4560− // push to that branch of the repository.
4561− let source = pull.fork.as_ref().unwrap_or(&repo);
5047+ // push to that branch of the repository, or of the fork
5048+ // it was opened from.
5049+ let source = pull.fork.as_ref().or(pull.head_repo.as_ref()).unwrap_or(&repo);
45625050 let remote = services.addresses.git_remote(&source.namespace, &source.name);
45635051 ok(&json!({
45645052 "pull": pull,
46405128 let method = if self == Op::EditComment { "edit_comment" } else { "delete_comment" };
46415129 pass(work, method, &asked).await
46425130 }
4643− Op::MergePullRequest => pass(work, "merge_pull", &pull_action()).await,
5131+ Op::MergePullRequest => {
5132+ let method = match merge_method(input, "merge_method") {
5133+ Ok(method) => method,
5134+ Err(message) => return failed(FailureCode::Invalid, &message),
5135+ };
5136+ let asked = PullActionArgs {
5137+ merge_method: method,
5138+ commit_title: optional_text(input, "commit_title"),
5139+ commit_message: input["commit_message"].as_str().map(str::to_owned),
5140+ expected_head: optional_text(input, "sha"),
5141+ delete_branch: input["delete_branch"].as_bool(),
5142+ ..pull_action()
5143+ };
5144+ pass(work, "merge_pull", &asked).await
5145+ }
5146+ Op::GetPullRequestMergeOptions => pass(work, "merge_choices", &view()).await,
5147+ Op::UpdatePullRequestBranch => {
5148+ let update_method = match input["update_method"].as_str().map(str::trim).filter(|given| !given.is_empty()) {
5149+ None => None,
5150+ Some(given) if given.eq_ignore_ascii_case("merge") => Some(UpdateMethod::Merge),
5151+ Some(given) if given.eq_ignore_ascii_case("rebase") => Some(UpdateMethod::Rebase),
5152+ Some(given) => return failed(FailureCode::Invalid, &format!("update_method must be merge or rebase, not {given}.")),
5153+ };
5154+ let asked = PullActionArgs {
5155+ expected_head: optional_text(input, "expected_head_sha"),
5156+ update_method,
5157+ ..pull_action()
5158+ };
5159+ pass(work, "catch_up_pull", &asked).await
5160+ }
5161+ Op::EnablePullRequestAutoMerge | Op::DisablePullRequestAutoMerge => {
5162+ let enabled = self == Op::EnablePullRequestAutoMerge;
5163+ let method = match merge_method(input, "merge_method") {
5164+ Ok(method) => method.filter(|_| enabled),
5165+ Err(message) if enabled => return failed(FailureCode::Invalid, &message),
5166+ Err(_) => None,
5167+ };
5168+ pass(
5169+ work,
5170+ "set_auto_merge",
5171+ &SetAutoMergeArgs {
5172+ actor: actor(),
5173+ repo,
5174+ number,
5175+ enabled,
5176+ merge_method: method,
5177+ commit_title: optional_text(input, "commit_title").filter(|_| enabled),
5178+ commit_message: input["commit_message"].as_str().map(str::to_owned).filter(|_| enabled),
5179+ },
5180+ )
5181+ .await
5182+ }
5183+ Op::DeletePullRequestHeadBranch => pass(work, "delete_head_branch", &pull_action()).await,
5184+ Op::RestorePullRequestHeadBranch => pass(work, "restore_head_branch", &pull_action()).await,
5185+ Op::ListRepoCommitComments | Op::ListCommitComments => {
5186+ let commit = if self == Op::ListCommitComments {
5187+ match optional_text(input, "sha") {
5188+ Some(sha) => Some(sha),
5189+ None => return failed(FailureCode::Invalid, "Give sha: the commit's hash."),
5190+ }
5191+ } else {
5192+ None
5193+ };
5194+ pass(
5195+ work,
5196+ "commit_comments",
5197+ &CommitCommentsArgs {
5198+ viewer: viewer.clone(),
5199+ repo,
5200+ commit,
5201+ page: integer(input, "page").filter(|page| *page > 0),
5202+ },
5203+ )
5204+ .await
5205+ }
5206+ Op::CreateCommitComment => {
5207+ let body = text(input, "body");
5208+ if body.trim().is_empty() {
5209+ return failed(FailureCode::Invalid, "Give body: the comment, in Markdown.");
5210+ }
5211+ let Some(commit) = optional_text(input, "sha") else {
5212+ return failed(FailureCode::Invalid, "Give sha: the commit's hash.");
5213+ };
5214+ let path = optional_text(input, "path");
5215+ let line = integer(input, "line").filter(|line| *line > 0);
5216+ if input.get("line").is_some_and(|given| !given.is_null()) && line.is_none() {
5217+ return failed(FailureCode::Invalid, "line is counted from 1.");
5218+ }
5219+ if line.is_some() && path.is_none() {
5220+ return failed(FailureCode::Invalid, "Give path with line: the file the line is in.");
5221+ }
5222+ pass(
5223+ work,
5224+ "add_commit_comment",
5225+ &AddCommitCommentArgs { actor: actor(), repo, commit, body, path, line },
5226+ )
5227+ .await
5228+ }
5229+ Op::UpdateCommitComment | Op::DeleteCommitComment => {
5230+ let editing = self == Op::UpdateCommitComment;
5231+ let body = text(input, "body");
5232+ if editing && body.trim().is_empty() {
5233+ return failed(FailureCode::Invalid, "Give body: the new text, in Markdown.");
5234+ }
5235+ let asked = CommitCommentArgs { actor: actor(), repo, id: text(input, "comment_id"), body };
5236+ let method = if editing { "edit_commit_comment" } else { "delete_commit_comment" };
5237+ pass(work, method, &asked).await
5238+ }
46445239 Op::GetPullRequestChanges => {
46455240 let found: Outcome<PullDetail> = call(work, "get_pull", &view()).await?;
46465241 match found {
+40−2
2626 SetForkPrApproval,
2727 GetActionsAccess,
2828 SetActionsAccess,
29+ GetActionsPermissions,
30+ SetActionsPermissions,
2931 CreateRepositoryDispatch,
3032 GetWorkspaceWorkflowPermissions,
3133 SetWorkspaceWorkflowPermissions,
3941 /// Every one: `Op::ALL` lists each as `Op::Protection(…)`, which a test
4042 /// checks against this.
4143 #[cfg(test)]
42− pub const ALL: [ProtectionOp; 18] = [
44+ pub const ALL: [ProtectionOp; 20] = [
4345 ProtectionOp::UpdateEnvironment,
4446 ProtectionOp::DeleteEnvironment,
4547 ProtectionOp::GetPendingDeployments,
5153 ProtectionOp::SetForkPrApproval,
5254 ProtectionOp::GetActionsAccess,
5355 ProtectionOp::SetActionsAccess,
56+ ProtectionOp::GetActionsPermissions,
57+ ProtectionOp::SetActionsPermissions,
5458 ProtectionOp::CreateRepositoryDispatch,
5559 ProtectionOp::GetWorkspaceWorkflowPermissions,
5660 ProtectionOp::SetWorkspaceWorkflowPermissions,
7377 ProtectionOp::SetForkPrApproval => "set_fork_pr_approval",
7478 ProtectionOp::GetActionsAccess => "get_actions_access",
7579 ProtectionOp::SetActionsAccess => "set_actions_access",
80+ ProtectionOp::GetActionsPermissions => "get_actions_permissions",
81+ ProtectionOp::SetActionsPermissions => "set_actions_permissions",
7682 ProtectionOp::CreateRepositoryDispatch => "create_repository_dispatch",
7783 ProtectionOp::GetWorkspaceWorkflowPermissions => "get_workspace_workflow_permissions",
7884 ProtectionOp::SetWorkspaceWorkflowPermissions => "set_workspace_workflow_permissions",
113119 ProtectionOp::SetForkPrApproval => "Set the approval policy for outside pull requests",
114120 ProtectionOp::GetActionsAccess => "Get who may use a repository's actions and workflows",
115121 ProtectionOp::SetActionsAccess => "Set who may use a repository's actions and workflows",
122+ ProtectionOp::GetActionsPermissions => "Get whether a repository's workflows run",
123+ ProtectionOp::SetActionsPermissions => "Turn a repository's workflows on or off",
116124 ProtectionOp::CreateRepositoryDispatch => "Create a repository dispatch event",
117125 ProtectionOp::GetWorkspaceWorkflowPermissions => "Get a workspace's default workflow permissions",
118126 ProtectionOp::SetWorkspaceWorkflowPermissions => "Set a workspace's default workflow permissions",
136144 ProtectionOp::SetForkPrApproval => "Set approval_policy: first_time_contributors, outside_contributors or all_external_contributors. Needs the Admin role.",
137145 ProtectionOp::GetActionsAccess => "Which other repositories' workflows may use this private repository's actions (uses: owner/repo@ref) and reusable workflows (jobs.<id>.uses: owner/repo/.g1t/workflows/build.yml@ref): access_level is none (the default: only this repository) or organization (private repositories in the same workspace). A public repository's actions and workflows are anyone's, whatever this says, and a public repository's workflows never use a private one's. Needs the Read role.",
138146 ProtectionOp::SetActionsAccess => "Set access_level: none, or organization to let the workspace's other private repositories use this repository's actions and reusable workflows (user is read as organization). Needs the Admin role.",
147+ ProtectionOp::GetActionsPermissions => "Whether a repository's workflows run at all: enabled is on for a repository and off for a fork until someone chooses (enabled_chosen says whether anyone has), so a fork never runs the workflows it copied until they are turned on. While it is off, no push, pull request, schedule, dispatch, repository_dispatch, merge queue or re-run starts a run; its workflows stay listed. allowed_actions is always all. Needs the Read role.",
148+ ProtectionOp::SetActionsPermissions => "Turn a repository's workflows on or off (enabled). Off, nothing starts a run, and a dispatch or re-run is refused with \"Actions is turned off for owner/name\"; runs already going finish. Turning it on runs nothing by itself: the next event does. Needs the Admin role; a workflow job's own token cannot.",
139149 ProtectionOp::GetWorkspaceWorkflowPermissions => "A workspace's policy for its repositories' job tokens: default_workflow_permissions (read, the default, or write) is what a repository made from now on gets until it chooses; max_workflow_permissions (write, the default, or read) is the most any repository's default may be, so read holds every repository to read-only; can_approve_pull_request_reviews (off by default) lets its repositories allow jobs to open and approve pull requests. Members only.",
140150 ProtectionOp::SetWorkspaceWorkflowPermissions => "Change a workspace's default_workflow_permissions, max_workflow_permissions and can_approve_pull_request_reviews; fields left out stay as they are. A maximum of read makes the default read too. Owners only.",
141151 ProtectionOp::ListWorkspaceManagedWorkflows => "The workflows a workspace's owners added to its repositories, g1t's required workflows: each runs in every repository it targets as that repository's own workflow (its token, its secrets, its checks), under the path managed/<name>.yml, from a copy of its source file taken when it was saved. Each has name, path, workflow_name (its `name:`), source (owner/repo/path@ref), source_sha (the commit it was copied at), source_private, mode, repositories, visibility, content (the copy that runs) and when and by whom it was last saved. Owners only.",
154164 | ProtectionOp::GetWorkflowPermissions
155165 | ProtectionOp::GetForkPrApproval
156166 | ProtectionOp::GetActionsAccess
167+ | ProtectionOp::GetActionsPermissions
157168 | ProtectionOp::GetWorkspaceWorkflowPermissions
158169 | ProtectionOp::ListWorkspaceManagedWorkflows
159170 )
217228 }),
218229 &["repo", "id", "state"],
219230 ),
220− ProtectionOp::GetWorkflowPermissions | ProtectionOp::GetForkPrApproval | ProtectionOp::GetActionsAccess => (json!({ "repo": repo }), &["repo"]),
231+ ProtectionOp::GetWorkflowPermissions
232+ | ProtectionOp::GetForkPrApproval
233+ | ProtectionOp::GetActionsAccess
234+ | ProtectionOp::GetActionsPermissions => (json!({ "repo": repo }), &["repo"]),
235+ ProtectionOp::SetActionsPermissions => (
236+ json!({
237+ "repo": repo,
238+ "enabled": { "type": "boolean", "description": "Whether its workflows run." },
239+ }),
240+ &["repo", "enabled"],
241+ ),
221242 ProtectionOp::SetActionsAccess => (
222243 json!({
223244 "repo": repo,
714735 };
715736 map(settings, |s| json!({ "access_level": s["accessLevel"] }))
716737 }
738+ ProtectionOp::GetActionsPermissions | ProtectionOp::SetActionsPermissions => {
739+ let settings: Outcome<Value> = if op == ProtectionOp::GetActionsPermissions {
740+ g1t_kit::call(actions, "actions_settings", &json!({ "viewer": viewer, "repo": repo })).await?
741+ } else {
742+ let Some(enabled) = flag(input, "enabled") else {
743+ return Ok(Outcome::fail(FailureCode::Invalid, "enabled is true or false."));
744+ };
745+ g1t_kit::call(actions, "set_actions_settings", &json!({ "actor": actor(), "repo": repo, "enabled": enabled })).await?
746+ };
747+ map(settings, |s| {
748+ json!({
749+ "enabled": s.get("enabled").cloned().unwrap_or(Value::Bool(true)),
750+ "enabled_chosen": s.get("enabledChosen").cloned().unwrap_or(Value::Bool(false)),
751+ "allowed_actions": "all",
752+ })
753+ })
754+ }
717755 ProtectionOp::CreateRepositoryDispatch => {
718756 let started: Outcome<u32> = g1t_kit::call(
719757 actions,
+551−5
707707 "website": "https://hello.g1t.page",
708708 "archived_at": null
709709 },
710− "notes": "Only the fields given change. `website` gets `https://` when no scheme is given; an empty string clears it. `default_branch` must name a branch that exists (`422` otherwise); it is changed after the other fields. `description`, `website`, `topics` and `protected` need the Maintain role or higher, and `private` and `default_branch` the Admin role (`403` otherwise, saying which role). Changing `private` here needs no confirmation; `POST /repos/{owner}/{name}/visibility` asks for one. Making it private is refused with `402` when a free workspace's private storage has no room for it. See [Managing a repository](/guides/managing-repositories/) and [Access and roles](/guides/access-and-roles/)."
710+ "notes": "Only the fields given change. `website` gets `https://` when no scheme is given; an empty string clears it. `default_branch` must name a branch that exists (`422` otherwise); it is changed after the other fields. `description`, `website`, `topics` and `protected` need the Maintain role or higher, and `private`, `allow_forking`, `max_ref_updates` and `default_branch` the Admin role (`403` otherwise, saying which role). Changing `private` here needs no confirmation; `POST /repos/{owner}/{name}/visibility` asks for one. Making it private is refused with `402` when a free workspace's private storage has no room for it. A fork cannot change `private` (`422`): detach it first. See [Managing a repository](/guides/managing-repositories/) and [Access and roles](/guides/access-and-roles/)."
711711 },
712712 "transfer_repo": {
713713 "request": {
729729 },
730730 "notes": "The repository at its new address. Refused with `409` when the destination already has a repository of that name, `403` unless you own both workspaces, and `402` when a free destination has no room for a private repository. The old address redirects. See [Transferring a repository](/guides/transferring-repositories/)."
731731 },
732+ "fork_repo": {
733+ "request": {
734+ "workspace": "ada",
735+ "default_branch_only": true
736+ },
737+ "response": {
738+ "id": "rep_01m4b7k2v9c3tq8x5n6d0fr1hz",
739+ "namespace": "ada",
740+ "name": "hello",
741+ "description": "A tiny service that says hello.",
742+ "is_private": false,
743+ "owner_id": "usr_01kq3r8w2m6vd5e9t0hy7jx4cb",
744+ "default_branch": "main",
745+ "fork_of": null,
746+ "parent_id": "rep_01m3m5q6p0e2qaw6mmjahk0qrr",
747+ "parent": {
748+ "namespace": "flagon-io",
749+ "name": "hello"
750+ },
751+ "allow_forking": false,
752+ "protected": false,
753+ "created_at": "2026-10-14T09:30:12.418Z",
754+ "website": null,
755+ "archived_at": null
756+ },
757+ "notes": "The new fork, with `parent` naming the repository it came from. Refused with `403` when the repository is private and its admins have not allowed forking, or when you cannot create repositories in the workspace; `409` when the workspace already has a repository of that name (give another `name`); `402` when a free workspace's private storage has no room for a private fork. Its code and description are copied; nothing else is. See [Forking a repository](/guides/forking-repositories/) and [Forks](/concepts/forks/)."
758+ },
759+ "list_forks": {
760+ "query": {
761+ "limit": "30"
762+ },
763+ "response": [
764+ {
765+ "id": "rep_01m4b7k2v9c3tq8x5n6d0fr1hz",
766+ "namespace": "ada",
767+ "name": "hello",
768+ "description": "A tiny service that says hello.",
769+ "is_private": false,
770+ "owner_id": "usr_01kq3r8w2m6vd5e9t0hy7jx4cb",
771+ "default_branch": "main",
772+ "fork_of": null,
773+ "parent_id": "rep_01m3m5q6p0e2qaw6mmjahk0qrr",
774+ "parent": {
775+ "namespace": "flagon-io",
776+ "name": "hello"
777+ },
778+ "allow_forking": false,
779+ "protected": false,
780+ "created_at": "2026-10-14T09:30:12.418Z",
781+ "website": null,
782+ "archived_at": null
783+ }
784+ ],
785+ "notes": "Only the forks you can read, newest first: a private repository's forks are listed for people who can read them. Forks of a fork are listed on that fork. See [Forks](/concepts/forks/)."
786+ },
787+ "sync_fork": {
788+ "request": {
789+ "branch": "main"
790+ },
791+ "response": {
792+ "status": "fast_forwarded",
793+ "branch": "main",
794+ "head": "9c4e1f0b7a2d3e5f6a8b9c0d1e2f3a4b5c6d7e8f",
795+ "behind": 3,
796+ "ahead": 0,
797+ "conflicts": []
798+ },
799+ "notes": "`status` is `up_to_date`, `fast_forwarded`, `merged` (a merge commit joins the fork's own commits and the parent's) or `conflicted`: both sides changed the files in `conflicts`, and nothing was changed. Merge the parent's branch in a clone and push. Refused with `422` when the repository is not a fork, `403` without the Write role on it, and `404` when its parent is gone or you can no longer read it. See [Sync a fork](/guides/forking-repositories/#sync-a-fork)."
800+ },
801+ "detach_fork": {
802+ "response": {
803+ "id": "rep_01m4b7k2v9c3tq8x5n6d0fr1hz",
804+ "namespace": "ada",
805+ "name": "hello",
806+ "description": "A tiny service that says hello.",
807+ "is_private": false,
808+ "owner_id": "usr_01kq3r8w2m6vd5e9t0hy7jx4cb",
809+ "default_branch": "main",
810+ "fork_of": null,
811+ "parent_id": null,
812+ "parent": null,
813+ "allow_forking": false,
814+ "protected": false,
815+ "created_at": "2026-10-14T09:30:12.418Z",
816+ "website": null,
817+ "archived_at": null
818+ },
819+ "notes": "The repository, now with no parent. Needs the Admin role (`403` otherwise); `422` when it is not a fork. It cannot be attached again. See [Detach a fork](/guides/forking-repositories/#detach-a-fork)."
820+ },
732821 "rename_repo": {
733822 "request": {
734823 "name": "greeter"
9461035 "allow_ignoring_checks": true,
9471036 "merge_queue": false,
9481037 "require_code_owner_review": false,
1038+ "has_issues": null,
1039+ "issues_enabled": true,
1040+ "allow_merge_commit": true,
1041+ "allow_squash_merge": true,
1042+ "allow_rebase_merge": true,
1043+ "allow_fast_forward": false,
1044+ "default_merge_method": "merge",
1045+ "merge_commit_message": "default",
1046+ "squash_commit_message": "default",
1047+ "merge_commit_template": null,
1048+ "squash_commit_template": null,
1049+ "suggest_updating_branches": true,
1050+ "allow_auto_merge": true,
1051+ "delete_branch_on_merge": false,
1052+ "require_web_commit_sign_off": false,
1053+ "allow_commit_comments": true,
1054+ "auto_close_issues": true,
9491055 "updated_by": null,
9501056 "updated_at": null
9511057 }
9591065 "required_approvals": 1,
9601066 "count_agent_approvals": false,
9611067 "merge_queue": true,
962− "require_code_owner_review": true
1068+ "require_code_owner_review": true,
1069+ "allow_merge_commit": false,
1070+ "default_merge_method": "squash",
1071+ "squash_commit_message": "pull_title_and_description",
1072+ "delete_branch_on_merge": true
9631073 },
9641074 "response": {
9651075 "auto_merge": false,
9731083 "allow_ignoring_checks": true,
9741084 "merge_queue": true,
9751085 "require_code_owner_review": true,
1086+ "has_issues": null,
1087+ "issues_enabled": true,
1088+ "allow_merge_commit": false,
1089+ "allow_squash_merge": true,
1090+ "allow_rebase_merge": true,
1091+ "allow_fast_forward": false,
1092+ "default_merge_method": "squash",
1093+ "merge_commit_message": "default",
1094+ "squash_commit_message": "pull_title_and_description",
1095+ "merge_commit_template": null,
1096+ "squash_commit_template": null,
1097+ "suggest_updating_branches": true,
1098+ "allow_auto_merge": true,
1099+ "delete_branch_on_merge": true,
1100+ "require_web_commit_sign_off": false,
1101+ "allow_commit_comments": true,
1102+ "auto_close_issues": true,
9761103 "updated_by": "syntaqx",
9771104 "updated_at": "2026-10-04T16:20:37.508Z"
9781105 },
979− "notes": "`required_checks` replaces the whole list: at most 20 names, each a workflow's name or another status's context, as [`list_check_names`](/reference/api/repositories/list-check-names/) gives them. A pull request merges only once each passes on its head; one nothing has reported holds it too. With the merge queue on, each must also pass on the queued state, so the workflows behind them need `merge_group` in their `on:`. `required_approvals` is at most 6. See [what a repository can ask for](/guides/working-with-g1t/#what-a-repository-can-ask-for). With `require_code_owner_review`, a pull request into the default branch merges only once the code owners of every file it changes have approved it, as many as each section of its CODEOWNERS file asks: see [Code owners](/guides/codeowners/)."
1106+ "notes": "`required_checks` replaces the whole list: at most 20 names, each a workflow's name or another status's context, as [`list_check_names`](/reference/api/repositories/list-check-names/) gives them. A pull request merges only once each passes on its head; one nothing has reported holds it too. With the merge queue on, each must also pass on the queued state, so the workflows behind them need `merge_group` in their `on:`. `required_approvals` is at most 6. See [what a repository can ask for](/guides/working-with-g1t/#what-a-repository-can-ask-for). With `require_code_owner_review`, a pull request into the default branch merges only once the code owners of every file it changes have approved it, as many as each section of its CODEOWNERS file asks: see [Code owners](/guides/codeowners/). At least one of `allow_merge_commit`, `allow_squash_merge`, `allow_rebase_merge` and `allow_fast_forward` stays on, and `default_merge_method` must be one of them (`422` otherwise). `merge_commit_template` and `squash_commit_template` are used with `custom`: see [Commit messages](/guides/pull-requests/#commit-messages) for their placeholders. `has_issues` `null` goes back to the default: on for a repository, off for a fork. See [Repository settings](/guides/managing-repositories/#settings)."
9801107 },
9811108 "list_check_names": {
9821109 "response": [
20502177 },
20512178 "notes": "Takes them off who is asked; anyone not asked is ignored. Reviews they already gave stay, as do the people a team's review assignment picked: remove them by username."
20522179 },
2180+ "get_pull_request_merge_options": {
2181+ "response": {
2182+ "methods": [
2183+ "merge",
2184+ "squash",
2185+ "rebase"
2186+ ],
2187+ "default_method": "squash",
2188+ "options": [
2189+ {
2190+ "method": "merge",
2191+ "commit_title": "Merge pull request #14 from flagon-io/greet-caller",
2192+ "commit_message": "Greeting should name the caller",
2193+ "unavailable": null
2194+ },
2195+ {
2196+ "method": "squash",
2197+ "commit_title": "Greeting should name the caller (#14)",
2198+ "commit_message": "* Read the name from the first argument\n* Fall back to \"world\"",
2199+ "unavailable": null
2200+ },
2201+ {
2202+ "method": "rebase",
2203+ "commit_title": null,
2204+ "commit_message": null,
2205+ "unavailable": null
2206+ }
2207+ ],
2208+ "behind": false,
2209+ "can_fast_forward": false,
2210+ "suggest_update": false,
2211+ "require_up_to_date": false,
2212+ "merge_queue": false,
2213+ "auto_merge_allowed": true,
2214+ "delete_branch_default": true,
2215+ "can_delete_branch": true,
2216+ "sign_off": false
2217+ },
2218+ "notes": "`methods` are the repository's allowed methods, narrowed by the rules for the branch it merges into: required linear history leaves out `merge`. `fast_forward` lists `unavailable` until the branch contains its base's head. `merge` and `squash` carry the commit message a merge would use if you wrote none. See [Merging a pull request](/guides/pull-requests/#merging-a-pull-request)."
2219+ },
20532220 "merge_pull_request": {
20542221 "request": {
2055− "keep_issue_open": false
2222+ "merge_method": "squash",
2223+ "commit_title": "Greeting names the caller (#14)",
2224+ "sha": "9f2c4e1a7b3d5f60812a4c6e8b0d2f4a6c8e0b13",
2225+ "delete_branch": true
20562226 },
20572227 "response": {
20582228 "id": "pr_01m43smh3vexsr5pmp60qwv0vs",
20742244 "merge_base": "3a7e9c1b5d2f4a6c8e0b2d4f6a8c0e2b4d6f8a1c",
20752245 "merged_by": "syntaqx",
20762246 "merged_at": "2026-10-01T18:52:17.093Z",
2247+ "merge_method": "squash",
2248+ "merge_commit": "c41d7e2a9b0f3e5d7c9a1b3d5f7e9c0a2b4d6f81",
2249+ "closes": [
2250+ 12
2251+ ],
20772252 "superseded_by": null,
20782253 "check_status": "passed",
20792254 "files": [
21022277 "created_at": "2026-10-01T18:20:02.117Z",
21032278 "updated_at": "2026-10-01T18:52:17.093Z"
21042279 },
2105− "notes": "A draft, or one whose checks have not passed, answers `409`. A pull request still `open` in the response has not landed yet: `landing` is true on it while it is brought up to date, and [get the merge queue](/reference/api/pull-requests/get-merge-queue/) shows it waiting in the [merge queue](/guides/merge-queue/). Merging records the pull request in the issue's `resolved_by` and sets `superseded_by` on the pull requests it closes."
2280+ "notes": "A draft, or one whose checks have not passed, answers `409`. A pull request still `open` in the response has not landed yet: `landing` is true on it while it is brought up to date, and [get the merge queue](/reference/api/pull-requests/get-merge-queue/) shows it waiting in the [merge queue](/guides/merge-queue/). Merging records the pull request in the issue's `resolved_by`, closes the issues in `closes` (into the default branch, where `auto_close_issues` is on) and sets `superseded_by` on the pull requests it closes. A `merge_method` the repository or a rule for the base does not allow answers `422`, and a head that moved since `sha`, `409`. `merge_commit` is the commit the base is at afterwards: the merge or squash commit, the last rebased one, or the head for a fast-forward. See [Merging a pull request](/guides/pull-requests/#merging-a-pull-request)."
2281+ },
2282+ "update_pull_request_branch": {
2283+ "request": {
2284+ "expected_head_sha": "9f2c4e1a7b3d5f60812a4c6e8b0d2f4a6c8e0b13",
2285+ "update_method": "merge"
2286+ },
2287+ "response": {
2288+ "outcome": "updated",
2289+ "commit": "5b8d0f2a4c6e8a0c2e4a6c8e0a2c4e6a8c0e2a4c",
2290+ "previous": "9f2c4e1a7b3d5f60812a4c6e8b0d2f4a6c8e0b13"
2291+ },
2292+ "notes": "`outcome` is `updated` (its branch moved from `previous` to `commit`), `up_to_date` (it already holds its base's head; `commit` only) or `needs_agent` (nothing was pushed: `reason` is `overlap`, `conflicting` or `unsupported`, with `detail` and the `paths` concerned). `update_method` `rebase` rewrites its branch, so pull it again before pushing more. A head that moved since `expected_head_sha` answers `409`. See [Keeping a pull request up to date](/guides/pull-requests/#keeping-it-up-to-date)."
2293+ },
2294+ "enable_pull_request_auto_merge": {
2295+ "request": {
2296+ "merge_method": "squash",
2297+ "commit_title": "Greeting names the caller (#14)"
2298+ },
2299+ "response": {
2300+ "id": "pr_01m43smh3vexsr5pmp60qwv0vs",
2301+ "repo_id": "rep_01m3m5q6p0e2qaw6mmjahk0qrr",
2302+ "number": 14,
2303+ "issue": 12,
2304+ "title": "Greeting should name the caller",
2305+ "body": "Reads a name from the first argument. `hello ana` prints \"Hello, ana!\".",
2306+ "agent": "claude-code",
2307+ "runtime": "external",
2308+ "status": "open",
2309+ "fork": {
2310+ "namespace": "pulls",
2311+ "name": "pr_01m43smh3vexsr5pmp60qwv0vs"
2312+ },
2313+ "fork_repo_id": "rep_01m43smh5xd7aw2kq9tv0b4c8e",
2314+ "branch": null,
2315+ "head_commit": "9f2c4e1a7b3d5f60812a4c6e8b0d2f4a6c8e0b13",
2316+ "merge_base": "3a7e9c1b5d2f4a6c8e0b2d4f6a8c0e2b4d6f8a1c",
2317+ "merged_by": null,
2318+ "merged_at": null,
2319+ "superseded_by": null,
2320+ "check_status": "running",
2321+ "files": [
2322+ {
2323+ "path": "src/main.rs",
2324+ "additions": 6,
2325+ "deletions": 2
2326+ }
2327+ ],
2328+ "assignees": [],
2329+ "reviewers": [
2330+ "ana"
2331+ ],
2332+ "labels": [],
2333+ "milestone": null,
2334+ "base": "main",
2335+ "author": {
2336+ "id": "usr_01kkntcg1eeb98j62xjm7eh09p",
2337+ "username": "syntaqx",
2338+ "display_username": "syntaqx",
2339+ "kind": "user",
2340+ "verified": false,
2341+ "workspaces": []
2342+ },
2343+ "requested_by": null,
2344+ "created_at": "2026-10-01T18:20:02.117Z",
2345+ "updated_at": "2026-10-01T18:44:09.310Z",
2346+ "auto_merge": {
2347+ "method": "squash",
2348+ "commit_title": "Greeting names the caller (#14)",
2349+ "commit_message": null,
2350+ "enabled_by": "syntaqx",
2351+ "enabled_at": "2026-10-01T18:44:09.310Z"
2352+ },
2353+ "closes": [
2354+ 12
2355+ ]
2356+ },
2357+ "notes": "A repository with `allow_auto_merge` off answers `422`, as does a method it or a rule for the base does not allow. A pull request that may already merge merges at once: check `status`. `auto_merge` is cleared when it merges or closes, when it is turned off, and when someone other than `enabled_by` pushes to it. See [Auto-merge](/guides/pull-requests/#auto-merge)."
2358+ },
2359+ "disable_pull_request_auto_merge": {
2360+ "response": {
2361+ "id": "pr_01m43smh3vexsr5pmp60qwv0vs",
2362+ "repo_id": "rep_01m3m5q6p0e2qaw6mmjahk0qrr",
2363+ "number": 14,
2364+ "issue": 12,
2365+ "title": "Greeting should name the caller",
2366+ "body": "Reads a name from the first argument. `hello ana` prints \"Hello, ana!\".",
2367+ "agent": "claude-code",
2368+ "runtime": "external",
2369+ "status": "open",
2370+ "fork": {
2371+ "namespace": "pulls",
2372+ "name": "pr_01m43smh3vexsr5pmp60qwv0vs"
2373+ },
2374+ "fork_repo_id": "rep_01m43smh5xd7aw2kq9tv0b4c8e",
2375+ "branch": null,
2376+ "head_commit": "9f2c4e1a7b3d5f60812a4c6e8b0d2f4a6c8e0b13",
2377+ "merge_base": "3a7e9c1b5d2f4a6c8e0b2d4f6a8c0e2b4d6f8a1c",
2378+ "merged_by": null,
2379+ "merged_at": null,
2380+ "superseded_by": null,
2381+ "check_status": "running",
2382+ "files": [
2383+ {
2384+ "path": "src/main.rs",
2385+ "additions": 6,
2386+ "deletions": 2
2387+ }
2388+ ],
2389+ "assignees": [],
2390+ "reviewers": [
2391+ "ana"
2392+ ],
2393+ "labels": [],
2394+ "milestone": null,
2395+ "base": "main",
2396+ "author": {
2397+ "id": "usr_01kkntcg1eeb98j62xjm7eh09p",
2398+ "username": "syntaqx",
2399+ "display_username": "syntaqx",
2400+ "kind": "user",
2401+ "verified": false,
2402+ "workspaces": []
2403+ },
2404+ "requested_by": null,
2405+ "created_at": "2026-10-01T18:20:02.117Z",
2406+ "updated_at": "2026-10-01T18:44:09.310Z",
2407+ "auto_merge": null,
2408+ "closes": [
2409+ 12
2410+ ]
2411+ }
2412+ },
2413+ "delete_pull_request_head_branch": {
2414+ "response": {
2415+ "id": "pr_01m43smh3vexsr5pmp60qwv0vs",
2416+ "repo_id": "rep_01m3m5q6p0e2qaw6mmjahk0qrr",
2417+ "number": 14,
2418+ "issue": 12,
2419+ "title": "Greeting should name the caller",
2420+ "body": "Reads a name from the first argument. `hello ana` prints \"Hello, ana!\".",
2421+ "agent": "claude-code",
2422+ "runtime": "external",
2423+ "status": "merged",
2424+ "fork": null,
2425+ "fork_repo_id": null,
2426+ "branch": "greet-caller",
2427+ "head_commit": "9f2c4e1a7b3d5f60812a4c6e8b0d2f4a6c8e0b13",
2428+ "merge_base": "3a7e9c1b5d2f4a6c8e0b2d4f6a8c0e2b4d6f8a1c",
2429+ "merged_by": "syntaqx",
2430+ "merged_at": "2026-10-01T18:52:17.093Z",
2431+ "superseded_by": null,
2432+ "check_status": "passed",
2433+ "files": [
2434+ {
2435+ "path": "src/main.rs",
2436+ "additions": 6,
2437+ "deletions": 2
2438+ }
2439+ ],
2440+ "assignees": [],
2441+ "reviewers": [
2442+ "ana"
2443+ ],
2444+ "labels": [],
2445+ "milestone": null,
2446+ "base": "main",
2447+ "author": {
2448+ "id": "usr_01kkntcg1eeb98j62xjm7eh09p",
2449+ "username": "syntaqx",
2450+ "display_username": "syntaqx",
2451+ "kind": "user",
2452+ "verified": false,
2453+ "workspaces": []
2454+ },
2455+ "requested_by": null,
2456+ "created_at": "2026-10-01T18:20:02.117Z",
2457+ "updated_at": "2026-10-01T18:53:02.415Z",
2458+ "merge_method": "squash",
2459+ "merge_commit": "c41d7e2a9b0f3e5d7c9a1b3d5f7e9c0a2b4d6f81",
2460+ "closes": [
2461+ 12
2462+ ],
2463+ "head_branch_deleted": true
2464+ },
2465+ "notes": "An open pull request answers `409`: close or merge it first. `head_branch_deleted` is then `true`; [restore it](/reference/api/pull-requests/restore-pull-request-head-branch/) at `head_commit`."
2466+ },
2467+ "restore_pull_request_head_branch": {
2468+ "response": {
2469+ "id": "pr_01m43smh3vexsr5pmp60qwv0vs",
2470+ "repo_id": "rep_01m3m5q6p0e2qaw6mmjahk0qrr",
2471+ "number": 14,
2472+ "issue": 12,
2473+ "title": "Greeting should name the caller",
2474+ "body": "Reads a name from the first argument. `hello ana` prints \"Hello, ana!\".",
2475+ "agent": "claude-code",
2476+ "runtime": "external",
2477+ "status": "merged",
2478+ "fork": null,
2479+ "fork_repo_id": null,
2480+ "branch": "greet-caller",
2481+ "head_commit": "9f2c4e1a7b3d5f60812a4c6e8b0d2f4a6c8e0b13",
2482+ "merge_base": "3a7e9c1b5d2f4a6c8e0b2d4f6a8c0e2b4d6f8a1c",
2483+ "merged_by": "syntaqx",
2484+ "merged_at": "2026-10-01T18:52:17.093Z",
2485+ "superseded_by": null,
2486+ "check_status": "passed",
2487+ "files": [
2488+ {
2489+ "path": "src/main.rs",
2490+ "additions": 6,
2491+ "deletions": 2
2492+ }
2493+ ],
2494+ "assignees": [],
2495+ "reviewers": [
2496+ "ana"
2497+ ],
2498+ "labels": [],
2499+ "milestone": null,
2500+ "base": "main",
2501+ "author": {
2502+ "id": "usr_01kkntcg1eeb98j62xjm7eh09p",
2503+ "username": "syntaqx",
2504+ "display_username": "syntaqx",
2505+ "kind": "user",
2506+ "verified": false,
2507+ "workspaces": []
2508+ },
2509+ "requested_by": null,
2510+ "created_at": "2026-10-01T18:20:02.117Z",
2511+ "updated_at": "2026-10-02T09:10:44.902Z",
2512+ "merge_method": "squash",
2513+ "merge_commit": "c41d7e2a9b0f3e5d7c9a1b3d5f7e9c0a2b4d6f81",
2514+ "closes": [
2515+ 12
2516+ ],
2517+ "head_branch_deleted": false
2518+ },
2519+ "notes": "The branch is made again at `head_commit`. A branch of that name that exists again answers `409`."
2520+ },
2521+ "list_repo_commit_comments": {
2522+ "query": {
2523+ "page": 1
2524+ },
2525+ "response": [
2526+ {
2527+ "id": "ccm_01m4a2s1b5c7e9g3k6m0p4s8v2",
2528+ "repo_id": "rep_01m3m5q6p0e2qaw6mmjahk0qrr",
2529+ "commit": "9f2c4e1a7b3d5f60812a4c6e8b0d2f4a6c8e0b13",
2530+ "path": null,
2531+ "line": null,
2532+ "author": "syntaqx",
2533+ "body": "Released as v0.4.0.",
2534+ "created_at": "2026-10-02T08:30:57.664Z",
2535+ "updated_at": null
2536+ },
2537+ {
2538+ "id": "ccm_01m4a2r7k3d9f1h5j8n2q6t0w4",
2539+ "repo_id": "rep_01m3m5q6p0e2qaw6mmjahk0qrr",
2540+ "commit": "9f2c4e1a7b3d5f60812a4c6e8b0d2f4a6c8e0b13",
2541+ "path": "src/main.rs",
2542+ "line": 12,
2543+ "author": "ana",
2544+ "body": "Should an empty name fall back to \"world\"?",
2545+ "created_at": "2026-10-01T19:02:11.208Z",
2546+ "updated_at": null
2547+ }
2548+ ],
2549+ "notes": "Newest first, 30 a page. A comment with `path` and `line` is on that line of the file as its `commit` left it; one without is on the whole commit."
2550+ },
2551+ "list_commit_comments": {
2552+ "params": {
2553+ "owner": "flagon-io",
2554+ "name": "hello",
2555+ "sha": "9f2c4e1a7b3d5f60812a4c6e8b0d2f4a6c8e0b13"
2556+ },
2557+ "response": [
2558+ {
2559+ "id": "ccm_01m4a2r7k3d9f1h5j8n2q6t0w4",
2560+ "repo_id": "rep_01m3m5q6p0e2qaw6mmjahk0qrr",
2561+ "commit": "9f2c4e1a7b3d5f60812a4c6e8b0d2f4a6c8e0b13",
2562+ "path": "src/main.rs",
2563+ "line": 12,
2564+ "author": "ana",
2565+ "body": "Should an empty name fall back to \"world\"?",
2566+ "created_at": "2026-10-01T19:02:11.208Z",
2567+ "updated_at": null
2568+ }
2569+ ],
2570+ "notes": "Oldest first."
21062571 },
2572+ "create_commit_comment": {
2573+ "params": {
2574+ "owner": "flagon-io",
2575+ "name": "hello",
2576+ "sha": "9f2c4e1a7b3d5f60812a4c6e8b0d2f4a6c8e0b13"
2577+ },
2578+ "request": {
2579+ "body": "Should an empty name fall back to \"world\"?",
2580+ "path": "src/main.rs",
2581+ "line": 12
2582+ },
2583+ "response": {
2584+ "id": "ccm_01m4a2r7k3d9f1h5j8n2q6t0w4",
2585+ "repo_id": "rep_01m3m5q6p0e2qaw6mmjahk0qrr",
2586+ "commit": "9f2c4e1a7b3d5f60812a4c6e8b0d2f4a6c8e0b13",
2587+ "path": "src/main.rs",
2588+ "line": 12,
2589+ "author": "ana",
2590+ "body": "Should an empty name fall back to \"world\"?",
2591+ "created_at": "2026-10-01T19:02:11.208Z",
2592+ "updated_at": null
2593+ },
2594+ "notes": "`line` needs `path`, a file the commit changed, and counts lines of that file as the commit left it. A repository with `allow_commit_comments` off answers `403`; a commit it does not have, `404`. See [Commit comments](/guides/pull-requests/#commit-comments)."
2595+ },
2596+ "update_commit_comment": {
2597+ "params": {
2598+ "owner": "flagon-io",
2599+ "name": "hello",
2600+ "comment_id": "ccm_01m4a2r7k3d9f1h5j8n2q6t0w4"
2601+ },
2602+ "request": {
2603+ "body": "Should an empty name fall back to \"world\"? The README says it does."
2604+ },
2605+ "response": {
2606+ "id": "ccm_01m4a2r7k3d9f1h5j8n2q6t0w4",
2607+ "repo_id": "rep_01m3m5q6p0e2qaw6mmjahk0qrr",
2608+ "commit": "9f2c4e1a7b3d5f60812a4c6e8b0d2f4a6c8e0b13",
2609+ "path": "src/main.rs",
2610+ "line": 12,
2611+ "author": "ana",
2612+ "body": "Should an empty name fall back to \"world\"? The README says it does.",
2613+ "created_at": "2026-10-01T19:02:11.208Z",
2614+ "updated_at": "2026-10-01T19:05:40.117Z"
2615+ },
2616+ "notes": "Its author only."
2617+ },
2618+ "delete_commit_comment": {
2619+ "params": {
2620+ "owner": "flagon-io",
2621+ "name": "hello",
2622+ "comment_id": "ccm_01m4a2r7k3d9f1h5j8n2q6t0w4"
2623+ },
2624+ "notes": "Answers `204` with no body. Its author, or someone with the Maintain role or higher."
2625+ },
21072626 "close_pull_request": {
21082627 "response": {
21092628 "id": "pr_01m43smh3vexsr5pmp60qwv0vs",
1123911758 "access_level": "none"
1124011759 }
1124111760 },
11761+ "get_actions_permissions": {
11762+ "params": {
11763+ "owner": "ada",
11764+ "name": "hello"
11765+ },
11766+ "response": {
11767+ "enabled": false,
11768+ "enabled_chosen": false,
11769+ "allowed_actions": "all"
11770+ },
11771+ "notes": "A fork's workflows are off until someone turns them on: here `ada/hello` is a fork nobody has chosen for yet. See [Forks](/concepts/forks/)."
11772+ },
11773+ "set_actions_permissions": {
11774+ "params": {
11775+ "owner": "ada",
11776+ "name": "hello"
11777+ },
11778+ "request": {
11779+ "enabled": true
11780+ },
11781+ "response": {
11782+ "enabled": true,
11783+ "enabled_chosen": true,
11784+ "allowed_actions": "all"
11785+ },
11786+ "notes": "Needs the Admin role (`403` otherwise); a workflow job's own token is refused. Turning workflows on starts nothing by itself. See [Turn workflows off](/guides/actions/#turn-workflows-on-or-off)."
11787+ },
1124211788 "set_actions_access": {
1124311789 "params": {
1124411790 "owner": "flagon-io",
+12−1
152152 }
153153 match op {
154154 Op::GetWorkspace | Op::CreateWorkspace | Op::UpdateWorkspace => through::<g1t_contracts::identity::Workspace>(op, sent),
155− Op::ListRepos => through::<Vec<repos::Repo>>(op, sent),
155+ Op::ListRepos | Op::ListForks => through::<Vec<repos::Repo>>(op, sent),
156+ Op::SyncFork => through::<repos::SyncedFork>(op, sent),
156157 Op::Search => through::<search::SearchResults>(op, sent),
157158 Op::GetRepo
158159 | Op::CreateRepo
159160 | Op::UpdateRepo
160161 | Op::TransferRepo
162+ | Op::ForkRepo
163+ | Op::DetachFork
161164 | Op::RenameRepo
162165 | Op::RenameBranch
163166 | Op::ArchiveRepo
191194 | Op::ReopenPullRequest
192195 | Op::ConvertPullRequestToDraft
193196 | Op::MergePullRequest
197+ | Op::EnablePullRequestAutoMerge
198+ | Op::DisablePullRequestAutoMerge
199+ | Op::DeletePullRequestHeadBranch
200+ | Op::RestorePullRequestHeadBranch
194201 | Op::RequestReviewers
195202 | Op::RemoveRequestedReviewers => {
196203 through::<work::Pull>(op, sent)
197204 }
205+ Op::UpdatePullRequestBranch => through::<repos::PullBranchUpdate>(op, sent),
206+ Op::GetPullRequestMergeOptions => through::<work::MergeChoices>(op, sent),
207+ Op::ListRepoCommitComments | Op::ListCommitComments => through::<Vec<work::CommitComment>>(op, sent),
208+ Op::CreateCommitComment | Op::UpdateCommitComment => through::<work::CommitComment>(op, sent),
198209 Op::ListWorkflows => through::<Vec<actions::Workflow>>(op, sent),
199210 Op::GetWorkflow => through::<actions::WorkflowDetail>(op, sent),
200211 Op::ListWorkflowRuns => through::<Vec<actions::WorkflowRun>>(op, sent),
+47−1
364364 route("GET", "/repos/:owner/:name/zipball/:ref", Op::Archives(ArchiveOp::DownloadZipball), &[]),
365365 route("PATCH", "/repos/:owner/:name", Op::UpdateRepo, &[]),
366366 route("POST", "/repos/:owner/:name/transfer", Op::TransferRepo, &[]),
367+ route("POST", "/repos/:owner/:name/forks", Op::ForkRepo, &[]),
368+ route("GET", "/repos/:owner/:name/forks", Op::ListForks, &[("limit", "limit")]),
369+ route("POST", "/repos/:owner/:name/merge-upstream", Op::SyncFork, &[]),
370+ route("POST", "/repos/:owner/:name/detach", Op::DetachFork, &[]),
367371 route("DELETE", "/repos/:owner/:name", Op::DeleteRepo, &[]),
368372 route(
369373 "GET",
498502 ),
499503 route("GET", "/repos/:owner/:name/actions/permissions/access", Op::Protection(ProtectionOp::GetActionsAccess), &[]),
500504 route("PUT", "/repos/:owner/:name/actions/permissions/access", Op::Protection(ProtectionOp::SetActionsAccess), &[]),
505+ route("GET", "/repos/:owner/:name/actions/permissions", Op::Protection(ProtectionOp::GetActionsPermissions), &[]),
506+ route("PUT", "/repos/:owner/:name/actions/permissions", Op::Protection(ProtectionOp::SetActionsPermissions), &[]),
501507 route("POST", "/repos/:owner/:name/dispatches", Op::Protection(ProtectionOp::CreateRepositoryDispatch), &[]),
502508 route(
503509 "GET",
10791085 Op::MergePullRequest,
10801086 &[],
10811087 ),
1088+ route("GET", "/repos/:owner/:name/pulls/:number/merge-options", Op::GetPullRequestMergeOptions, &[]),
1089+ route("PUT", "/repos/:owner/:name/pulls/:number/update-branch", Op::UpdatePullRequestBranch, &[]),
1090+ route("PUT", "/repos/:owner/:name/pulls/:number/auto-merge", Op::EnablePullRequestAutoMerge, &[]),
1091+ route("DELETE", "/repos/:owner/:name/pulls/:number/auto-merge", Op::DisablePullRequestAutoMerge, &[]),
1092+ route("DELETE", "/repos/:owner/:name/pulls/:number/head-branch", Op::DeletePullRequestHeadBranch, &[]),
1093+ route("POST", "/repos/:owner/:name/pulls/:number/head-branch/restore", Op::RestorePullRequestHeadBranch, &[]),
1094+ // Commit comments: GitHub's addresses.
1095+ route("GET", "/repos/:owner/:name/comments", Op::ListRepoCommitComments, &[("page", "page")]),
1096+ route("PATCH", "/repos/:owner/:name/comments/:comment_id", Op::UpdateCommitComment, &[]),
1097+ route("DELETE", "/repos/:owner/:name/comments/:comment_id", Op::DeleteCommitComment, &[]),
1098+ route("GET", "/repos/:owner/:name/commits/:sha/comments", Op::ListCommitComments, &[]),
1099+ route("POST", "/repos/:owner/:name/commits/:sha/comments", Op::CreateCommitComment, &[]),
10821100 // Artifacts mode's docs, slides, designs and dashboards. Search comes
10831101 // before an artifact by id, which it would otherwise match.
10841102 route("GET", "/workspaces/:workspace/artifacts", Op::Folios(FoliosOp::List), &[("tab", "tab"), ("kind", "kind"), ("space", "space"), ("project", "project"), ("q", "q"), ("state", "state"), ("cursor", "cursor"), ("limit", "limit")]),
11061124 pub fn no_content(&self) -> bool {
11071125 matches!(
11081126 self.op,
1109− Op::DeleteComment | Op::Caches(CachesOp::DeleteCache)
1127+ Op::DeleteComment | Op::DeleteCommitComment | Op::Caches(CachesOp::DeleteCache)
11101128 )
11111129 }
11121130
14681486 }
14691487
14701488 #[test]
1489+ fn merging_settings_and_commit_comments_have_their_addresses() {
1490+ let op = |method: &str, path: &str| resolve(method, path, &[], Value::Null).unwrap().0.op;
1491+ assert_eq!(op("GET", "/repos/acme/web/pulls/9/merge-options"), Op::GetPullRequestMergeOptions);
1492+ assert_eq!(op("PUT", "/repos/acme/web/pulls/9/update-branch"), Op::UpdatePullRequestBranch);
1493+ assert_eq!(op("PUT", "/repos/acme/web/pulls/9/auto-merge"), Op::EnablePullRequestAutoMerge);
1494+ assert_eq!(op("DELETE", "/repos/acme/web/pulls/9/auto-merge"), Op::DisablePullRequestAutoMerge);
1495+ assert_eq!(op("DELETE", "/repos/acme/web/pulls/9/head-branch"), Op::DeletePullRequestHeadBranch);
1496+ assert_eq!(op("POST", "/repos/acme/web/pulls/9/head-branch/restore"), Op::RestorePullRequestHeadBranch);
1497+ let (route, input) = resolve("GET", "/repos/acme/web/comments", &[("page".to_owned(), "2".to_owned())], Value::Null).unwrap();
1498+ assert_eq!(route.op, Op::ListRepoCommitComments);
1499+ assert_eq!(input, json!({ "page": "2", "repo": "acme/web" }));
1500+ let (route, input) =
1501+ resolve("POST", "/repos/acme/web/commits/9f2c4e1/comments", &[], json!({ "body": "Why?", "path": "a.rs", "line": 3 })).unwrap();
1502+ assert_eq!(route.op, Op::CreateCommitComment);
1503+ assert_eq!(input, json!({ "body": "Why?", "path": "a.rs", "line": 3, "sha": "9f2c4e1", "repo": "acme/web" }));
1504+ assert_eq!(op("GET", "/repos/acme/web/commits/9f2c4e1/comments"), Op::ListCommitComments);
1505+ // Still a commit's statuses, beside its comments.
1506+ assert_eq!(op("GET", "/repos/acme/web/commits/main/statuses"), Op::Checks(ChecksOp::ListCommitStatuses));
1507+ let (route, input) = resolve("PATCH", "/repos/acme/web/comments/cc_1", &[], json!({ "body": "Better" })).unwrap();
1508+ assert_eq!(route.op, Op::UpdateCommitComment);
1509+ assert_eq!(input, json!({ "body": "Better", "comment_id": "cc_1", "repo": "acme/web" }));
1510+ let (route, _) = resolve("DELETE", "/repos/acme/web/comments/cc_1", &[], Value::Null).unwrap();
1511+ assert!(route.op == Op::DeleteCommitComment && route.no_content());
1512+ // An issue's comments keep theirs.
1513+ assert_eq!(op("PATCH", "/repos/acme/web/issues/comments/cmt_1"), Op::EditComment);
1514+ }
1515+
1516+ #[test]
14711517 fn pull_requests_reopen_and_turn_draft_and_comments_are_named_by_id() {
14721518 let op = |method: &str, path: &str| resolve(method, path, &[], Value::Null).unwrap().0.op;
14731519 assert_eq!(op("POST", "/repos/acme/web/pulls/9/reopen"), Op::ReopenPullRequest);
+22−4
7070 Tool {
7171 name: "repository",
7272 title: "Repositories",
73− description: "Repositories: find, read and create them, change their settings and rulesets (what may happen to branches and tags, and what a pull request needs to merge), check their CODEOWNERS file, manage their labels and milestones, see and dismiss their security alerts (secrets and vulnerable dependencies), read what their default branch says (languages, contributors, license), star them and publish releases. Name one as \"owner/name\". Deleting, transferring and changing visibility need `confirm`.",
73+ description: "Repositories: find, read and create them, change their settings and rulesets (what may happen to branches and tags, and what a pull request needs to merge), check their CODEOWNERS file, manage their labels and milestones, see and dismiss their security alerts (secrets and vulnerable dependencies), read what their default branch says (languages, contributors, license), star them, publish releases and comment on their commits. Name one as \"owner/name\". Deleting, transferring and changing visibility need `confirm`.",
7474 default_action: None,
7575 actions: &[
7676 a("list", Op::ListRepos, "Repositories you can see"),
7777 a("get", Op::GetRepo, "One repository"),
7878 a("commits", Op::ListCommits, "A branch's commits with their messages (why each was made); with path, the commit that last changed it"),
79+ a("commit_comments", Op::ListCommitComments, "A commit's comments, on it or on lines of its files"),
80+ a("list_commit_comments", Op::ListRepoCommitComments, "All its commit comments, newest first"),
81+ a("comment_on_commit", Op::CreateCommitComment, "Comment on a commit; path and line for one line of a file it changed"),
82+ a("edit_commit_comment", Op::UpdateCommitComment, "Change your commit comment's text"),
83+ a("delete_commit_comment", Op::DeleteCommitComment, "Delete a commit comment: your own, or any with the Maintain role"),
7984 a("contents", Op::GetContents, "A file's text, or a directory's entries and README, at a branch, tag or commit"),
8085 a("tarball", Op::Archives(ArchiveOp::DownloadTarball), "A 10-minute link to a branch, tag or commit as a .tar.gz"),
8186 a("zipball", Op::Archives(ArchiveOp::DownloadZipball), "A 10-minute link to a branch, tag or commit as a .zip"),
8287 a("create", Op::CreateRepo, "Create one, empty or copied from a public git URL"),
83− a("update", Op::UpdateRepo, "Change description, website, topics, default branch, protection"),
88+ a("update", Op::UpdateRepo, "Change description, website, topics, default branch, protection, forking"),
8489 a("get_settings", Op::GetRepoSettings, "How pull requests merge, and the default branch's protection as its rules stack"),
8590 a("update_settings", Op::UpdateRepoSettings, "Change how pull requests merge and the default branch protection ruleset"),
8691 a("check_names", Op::ListCheckNames, "Check names reported lately, to require in a ruleset"),
123128 a("delete_branch", Op::DeleteBranch, "Delete a branch; make it again at its commit to restore it"),
124129 a("rename", Op::RenameRepo, "Rename it; old addresses redirect"),
125130 a("transfer", Op::TransferRepo, "Move it to another workspace you own"),
131+ a("fork", Op::ForkRepo, "Fork it into a workspace: its code and description, linked to it"),
132+ a("forks", Op::ListForks, "Its forks you can see"),
133+ a("sync_fork", Op::SyncFork, "Bring a fork's branch up to date with its parent"),
134+ a("detach_fork", Op::DetachFork, "Make a fork an ordinary repository, no longer linked to its parent"),
126135 a("archive", Op::ArchiveRepo, "Make it read-only"),
127136 a("unarchive", Op::UnarchiveRepo, "Make it writable again"),
128137 a("set_visibility", Op::SetRepoVisibility, "Make it public or private"),
166175 Tool {
167176 name: "pull_request",
168177 title: "Pull requests",
169− description: "Pull requests: start a change for an issue, record your session, mark it ready, ask people and teams to review, review and merge. Read `overlaps` and `behind` on `get` before going far, and `code_owners` for whose approval it needs.",
178+ description: "Pull requests: start a change for an issue, record your session, mark it ready, ask people and teams to review, review, update its branch, and merge (by merge, squash, rebase or fast_forward) now or with auto-merge. Read `overlaps` and `behind` on `get` before going far, and `code_owners` for whose approval it needs.",
170179 default_action: None,
171180 actions: &[
172181 a("list", Op::ListPullRequests, "Pull requests on a repository, newest first"),
185194 a("review", Op::ReviewPullRequest, "Approve or request changes"),
186195 a("close", Op::ClosePullRequest, "Close without merging"),
187196 a("reopen", Op::ReopenPullRequest, "Reopen a closed one"),
188− a("merge", Op::MergePullRequest, "Land it, or join the merge queue"),
197+ a("merge", Op::MergePullRequest, "Land it (merge, squash, rebase or fast_forward), or join the merge queue"),
198+ a("merge_options", Op::GetPullRequestMergeOptions, "How it may merge now: methods, default messages, whether it is behind"),
199+ a("update_branch", Op::UpdatePullRequestBranch, "Bring its branch up to date with its base, by merge or rebase"),
200+ a("enable_auto_merge", Op::EnablePullRequestAutoMerge, "Merge it on its own once its checks, reviews and rules allow"),
201+ a("disable_auto_merge", Op::DisablePullRequestAutoMerge, "Turn its auto-merge off"),
202+ a("delete_branch", Op::DeletePullRequestHeadBranch, "Delete a merged or closed one's branch; restorable"),
203+ a("restore_branch", Op::RestorePullRequestHeadBranch, "Make its deleted branch again at its head"),
189204 a("merge_queue", Op::GetMergeQueue, "The repository's merge queue"),
190205 ],
191206 },
261276 a("set_approval_policy", Op::Protection(ProtectionOp::SetForkPrApproval), "Set which pull requests' runs wait for approval"),
262277 a("get_access", Op::Protection(ProtectionOp::GetActionsAccess), "Which repositories may use this one's actions and workflows"),
263278 a("set_access", Op::Protection(ProtectionOp::SetActionsAccess), "Let the workspace's private repositories use them, or not"),
279+ a("get_enabled", Op::Protection(ProtectionOp::GetActionsPermissions), "Whether its workflows run at all; off for a fork until turned on"),
280+ a("set_enabled", Op::Protection(ProtectionOp::SetActionsPermissions), "Turn its workflows on or off"),
264281 a("repository_dispatch", Op::Protection(ProtectionOp::CreateRepositoryDispatch), "Start repository_dispatch workflows with an event"),
265282 a("get_workspace_permissions", Op::Protection(ProtectionOp::GetWorkspaceWorkflowPermissions), "A workspace's default and maximum token permissions"),
266283 a("set_workspace_permissions", Op::Protection(ProtectionOp::SetWorkspaceWorkflowPermissions), "Set them, and whether jobs may open pull requests"),
617634 | Op::DeleteTeam
618635 | Op::RemoveTeamRepo
619636 | Op::MergePullRequest
637+ | Op::DeleteCommitComment
620638 | Op::RemoveRunner
621639 | Op::DeleteRunnerGroup
622640 | Op::UpdateRunnerSettings
+2−1
181181 { label: 'The merge queue', slug: 'guides/merge-queue' },
182182 { label: 'Rules', slug: 'guides/rules' },
183183 { label: 'CODEOWNERS', slug: 'guides/codeowners' },
184− { label: 'Forks and branches', slug: 'concepts/forks' },
184+ { label: 'Forks and working copies', slug: 'concepts/forks' },
185185 ],
186186 },
187187 {
196196 { label: 'People and teams', slug: 'guides/people-and-teams' },
197197 { label: 'Managing a repository', slug: 'guides/managing-repositories' },
198198 { label: 'Releases', slug: 'guides/releases' },
199+ { label: 'Forking a repository', slug: 'guides/forking-repositories' },
199200 { label: 'Transferring a repository', slug: 'guides/transferring-repositories' },
200201 { label: 'Audit log', slug: 'guides/audit-log' },
201202 { label: 'Usage and billing', slug: 'guides/usage-and-billing' },
+29−18
130130
131131 ## Pull requests and forks
132132
133−### Forks are only for pull requests
133+### A fork's storage counts in full
134134
135−You can't fork a repository into your own workspace. On g1t, a fork is a
136−pull request's own working copy, made when the pull request is opened.
135+A fork counts toward its workspace's private repository storage at its
136+whole size, even where it is the same as the repository it came from.
137137
138−- **Why.** We built forks to isolate each pull request's work, so many
139− people and agents can work at once.
140− See [Forks and branches](/concepts/forks/).
141−- **Instead.** To contribute, open a pull request: it gets its own fork. To
142− start a copy of your own, clone the repository and push it to a new one
143− in your workspace; pushing creates it.
144−- **Status.** Not scheduled.
138+- **Why.** Cloudflare has not documented whether a fork shares stored
139+ objects with its source or copies them, so g1t never assumes a fork is
140+ cheaper than a copy.
141+- **Instead.** Fork with **Copy the default branch only** (the default)
142+ unless you need every branch and tag, and delete forks you no longer use.
143+- **Status.** Depends on Cloudflare. See
144+ [What a fork costs](/concepts/forks/#what-a-fork-costs).
145145
146−### Pull request forks are removed a day after they close
146+### Pull requests' working copies are removed a day after they close
147147
148−A day after a pull request merges or closes, its fork's git data is
149−removed. The pull request's changes stay readable: its head is kept in the
150−repository as `refs/pull/<pull request id>/head`. Pushing to the fork, or
151−reopening the pull request, makes the fork again from there.
148+A day after a pull request merges or closes, its working copy's git data
149+is removed. The pull request's changes stay readable: its head is kept in the
150+repository as `refs/pull/<pull request id>/head`. Pushing to the working
151+copy, or reopening the pull request, makes it again from there.
152152
153153 - **Why.** Cloudflare has not documented whether a fork shares stored
154− objects with its source or copies them, so forks are not kept longer
155− than they are useful.
154+ objects with its source or copies them, so working copies are not
155+ kept longer than they are useful.
156156 - **Instead.** Nothing you need to do. To keep working on a closed pull
157157 request's change, fetch `refs/pull/<pull request id>/head` and push it
158158 to a branch.
159−- **Status.** Clear fork storage rules depend on Cloudflare.
159+- **Status.** Clear storage rules for copies depend on Cloudflare.
160160
161161 ### Some dependency update options are not applied yet
162162
197197 See [Conflicts](/guides/pull-requests/#conflicts).
198198 - **Status.** Planned.
199199
200+### Large merges, squashes and rebases
201+
202+g1t merges, squashes, rebases and updates a pull request's branch by itself
203+up to 1,000 changed files, 100 files merged line by line (each up to 1 MiB)
204+and 250 commits in a rebase. Beyond them it refuses, and nothing changes.
205+
206+- **Instead.** Squash or merge instead of rebasing, or merge the base into
207+ the branch in a clone and push. See
208+ [Merge methods](/guides/pull-requests/#merge-methods).
209+- **Status.** Not scheduled.
210+
200211 ## Actions and runners
201212
202213 ### Docker shares the job's network
+5−3
143143 ## Pull request forks
144144
145145 A pull request made for an issue gets a fork of its own
146−([Forks and branches](/concepts/forks/)). `fork_for_pull` makes it in the
146+([Forks and working copies](/concepts/forks/)). `fork_for_pull` makes it in the
147147 same namespace as its source, as `pulls--<pull request id>`, with
148148 Artifacts' own fork of the default branch, and it is served at
149149 `g1t.sh/pulls/<id>.git`. Anyone who can read the source can read the fork.
163163 `repos` also builds the test merge for every pull request,
164164 `refs/pull/<n>/merge`, without a sandbox, using a line-level merge on
165165 files up to 1 MiB (`test_merge.rs`). When a pull request is behind and the
166−two sides changed different files, **Catch up with main** is done the same
167−way (`catch_up.rs`). Testing several pull requests together is the merge
166+two sides' changes do not clash, updating its branch is done the same way
167+(`catch_up.rs`), by a merge or a rebase; so are merge, squash and rebase
168+merges, up to 1,000 changed files, 100 files merged line by line and 250
169+commits in a rebase. Testing several pull requests together is the merge
168170 queue's job ([Work and the merge queue](/architecture/work/#the-merge-queue)).
169171
170172 ## Backups
+160−45
Loading the diff…
+7−6
Loading the diff…
+37−0
Loading the diff…
+2−2
Loading the diff…
+231−0
Loading the diff…
+74−1
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…

Comments

Sign in to comment.