Skip to content
1,363 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Rust repos service with shipping; pull requests kept in the model1//! The repos service: repository metadata, contents, forks and git access.
2//!
3//! Each `*Args` struct is the argument of the method of the same name,
4//! served at `POST /rpc/<method>`.
5
6use serde::{Deserialize, Serialize};
7
8use crate::{User, Viewer};
9
10#[derive(Clone, Debug, Serialize, Deserialize)]
11#[serde(rename_all = "camelCase")]
12pub struct Repo {
13 pub id: String,
Workspaces own repositories14 /// The slug of the workspace that owns it: the first URL segment.
Rust repos service with shipping; pull requests kept in the model15 pub namespace: String,
16 pub name: String,
17 pub description: Option<String>,
18 pub is_private: bool,
19 pub owner_id: String,
20 pub default_branch: String,
Issues and pull requests replace intents and attempts21 /// Set when this repo is a pull request's working copy of another repo.
Rust repos service with shipping; pull requests kept in the model22 pub fork_of: Option<String>,
Agents as a team: lifecycle, merge queue, billing and a new shell23 /// Whether the default branch is protected: it changes only by merging
24 /// a pull request, and pushes to it are refused.
25 #[serde(default)]
26 pub protected: bool,
RFC 3339 timestamps in identity and repos27 /// RFC 3339.
28 pub created_at: String,
Search across all of g1t, Explore, and a command palette29 /// Words that say what it is about, for search and Explore: lowercase
30 /// letters, digits and hyphens. See [`clean_topics`].
31 #[serde(default)]
32 pub topics: Vec<String>,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look33 /// Its home page, an http(s) address, shown beside its description.
34 /// See [`clean_website`].
35 #[serde(default)]
36 pub website: Option<String>,
37 /// RFC 3339: when it was archived, made read-only. Null when it is not.
38 #[serde(default)]
39 pub archived_at: Option<String>,
40}
41
42impl Repo {
43 pub fn archived(&self) -> bool {
44 self.archived_at.is_some()
45 }
46}
47
Merge branch 'worktree-agent-a2013627e5ea4ab13'48/// `storage_options` (no arguments, `{}`): what a workspace may choose
49/// about where its repositories are kept. `eu_available`: an EU namespace
50/// is configured and takes new repositories, so a workspace may keep its
51/// data in the EU (`set_workspace_residency` on identity). Returns
52/// `StorageOptions`.
53#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
54#[serde(rename_all = "camelCase")]
55pub struct StorageOptions {
56 pub eu_available: bool,
57}
58
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look59/// How long a deleted repository can be restored before it is purged.
60pub const RESTORE_DAYS: u64 = 30;
61
62/// A deleted repository, as its workspace's Recently deleted list shows
63/// it: restorable until `purge_after`.
64#[derive(Clone, Debug, Serialize, Deserialize)]
65#[serde(rename_all = "camelCase")]
66pub struct DeletedRepo {
67 pub id: String,
68 pub namespace: String,
69 pub name: String,
70 pub description: Option<String>,
71 pub is_private: bool,
72 /// RFC 3339.
73 pub deleted_at: String,
74 /// The username of who deleted it.
75 pub deleted_by: String,
76 /// RFC 3339: when it is purged, unless restored first.
77 pub purge_after: String,
Search across all of g1t, Explore, and a command palette78}
79
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look80/// The longest website address a repository keeps.
81pub const MAX_WEBSITE_CHARS: usize = 255;
82
83/// A website as it is kept: an http(s) address, `https://` added when no
84/// scheme is given; empty clears it. Anything else is refused.
85pub fn clean_website(text: &str) -> Result<Option<String>, String> {
86 let text = text.trim();
87 if text.is_empty() {
88 return Ok(None);
89 }
90 let url = if text.starts_with("https://") || text.starts_with("http://") {
91 text.to_owned()
92 } else if text.contains("://") {
93 return Err("A website is an http or https address.".into());
94 } else {
95 format!("https://{text}")
96 };
97 let host = url
98 .split("://")
99 .nth(1)
100 .unwrap_or("")
101 .split(['/', '?', '#'])
102 .next()
103 .unwrap_or("");
104 if url.chars().count() > MAX_WEBSITE_CHARS
105 || host.is_empty()
106 || !host.contains('.')
107 || url.chars().any(char::is_whitespace)
108 {
109 return Err("That is not a website address, such as https://example.com.".into());
110 }
111 Ok(Some(url))
112}
113
114/// Whether `name` can be a branch people name: what `git check-ref-format
115/// --branch` accepts, less the names g1t keeps for itself
116/// ([`G1T_BRANCH_PREFIX`]).
117pub fn is_valid_branch_name(name: &str) -> bool {
118 !name.is_empty()
119 && name.len() <= 200
120 && !name.starts_with('-')
121 && !name.starts_with('/')
122 && !name.ends_with('/')
123 && !name.ends_with('.')
124 && !name.ends_with(".lock")
125 && !name.contains("..")
126 && !name.contains("//")
127 && !name.contains("@{")
128 && name != "@"
129 && !name.starts_with(G1T_BRANCH_PREFIX)
130 && !name.split('/').any(|part| part.starts_with('.'))
131 && name
132 .chars()
133 .all(|c| !c.is_control() && !matches!(c, ' ' | '~' | '^' | ':' | '?' | '*' | '[' | '\\'))
134}
135
Search across all of g1t, Explore, and a command palette136/// The most topics a repository has.
137pub const MAX_TOPICS: usize = 20;
138/// The longest topic.
139pub const MAX_TOPIC_CHARS: usize = 35;
140
141/// Topics as they are kept: lowercase, spaces and underscores made
142/// hyphens, each of letters, digits and hyphens, starting with a letter or
143/// digit, without repeats, at most [`MAX_TOPICS`]. Anything else is the
144/// first topic that could not be read.
145pub fn clean_topics(topics: &[String]) -> Result<Vec<String>, String> {
146 let mut kept: Vec<String> = Vec::new();
147 for topic in topics {
148 let topic: String = topic
149 .trim()
150 .to_lowercase()
151 .chars()
152 .map(|c| if c == ' ' || c == '_' { '-' } else { c })
153 .collect();
154 if topic.is_empty() {
155 continue;
156 }
157 let valid = topic.chars().count() <= MAX_TOPIC_CHARS
158 && topic.chars().all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-')
159 && topic.chars().next().is_some_and(|c| c.is_ascii_alphanumeric());
160 if !valid {
161 return Err(format!(
162 "\"{topic}\" is not a topic: use letters, digits and hyphens, at most {MAX_TOPIC_CHARS} characters."
163 ));
164 }
165 if !kept.contains(&topic) {
166 kept.push(topic);
167 }
168 }
169 if kept.len() > MAX_TOPICS {
170 return Err(format!("A repository has at most {MAX_TOPICS} topics."));
171 }
172 Ok(kept)
Rust repos service with shipping; pull requests kept in the model173}
174
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API175#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
Rust repos service with shipping; pull requests kept in the model176pub struct RepoPath {
177 pub namespace: String,
178 pub name: String,
179}
180
181#[derive(Clone, Debug, Serialize, Deserialize)]
182pub struct Signature {
183 pub name: String,
184 pub email: String,
185}
186
187#[derive(Clone, Debug, Serialize, Deserialize)]
188#[serde(rename_all = "camelCase")]
189pub struct Commit {
190 pub hash: String,
191 pub tree_hash: String,
192 pub message: String,
193 pub author: Signature,
194 pub parents: Vec<String>,
RFC 3339 timestamps in identity and repos195 /// RFC 3339.
196 pub authored_at: String,
Rust repos service with shipping; pull requests kept in the model197}
198
199#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
200#[serde(rename_all = "lowercase")]
201pub enum EntryKind {
202 Tree,
203 Blob,
204 Symlink,
205 Gitlink,
206 Exec,
207}
208
209#[derive(Clone, Debug, Serialize, Deserialize)]
210pub struct TreeEntry {
211 pub name: String,
212 pub hash: String,
213 pub kind: EntryKind,
214}
215
216#[derive(Clone, Debug, Serialize, Deserialize)]
217pub struct Readme {
218 pub name: String,
219 /// Null when the file is binary or too large to show.
220 pub text: Option<String>,
221}
222
223#[derive(Clone, Debug, Serialize, Deserialize)]
224pub struct TreeView {
225 pub repo: Repo,
226 #[serde(rename = "ref")]
227 pub git_ref: String,
228 pub path: String,
229 /// Null when the repo has no commits yet.
230 pub head: Option<Commit>,
231 pub entries: Vec<TreeEntry>,
232 pub readme: Option<Readme>,
233}
234
235#[derive(Clone, Debug, Serialize, Deserialize)]
236pub struct BlobView {
237 pub repo: Repo,
238 #[serde(rename = "ref")]
239 pub git_ref: String,
240 pub path: String,
241 pub size: u64,
242 /// Null when the file is binary or too large to show.
243 pub text: Option<String>,
244}
245
246/// A git remote and a short-lived credential for it.
247#[derive(Clone, Debug, Serialize, Deserialize)]
248pub struct GitAccess {
249 pub remote: String,
250 pub token: String,
251}
252
253#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
254pub enum GitService {
255 #[serde(rename = "git-upload-pack")]
256 UploadPack,
257 #[serde(rename = "git-receive-pack")]
258 ReceivePack,
259}
260
Issues and pull requests replace intents and attempts261/// The result of landing a pull request.
Rust repos service with shipping; pull requests kept in the model262#[derive(Clone, Debug, Serialize, Deserialize)]
263pub struct Landed {
Diffs on attempts; hosted agent presented as the g1t agent264 /// The commit the branch points to now.
Rust repos service with shipping; pull requests kept in the model265 pub commit: String,
Diffs on attempts; hosted agent presented as the g1t agent266 /// The commit it pointed to before, if it had one. Comparing against
Issues and pull requests replace intents and attempts267 /// this shows what the pull request changed.
Diffs on attempts; hosted agent presented as the g1t agent268 pub previous: Option<String>,
Rust repos service with shipping; pull requests kept in the model269}
270
271/// `get`. Returns `Outcome<Repo>`.
272#[derive(Debug, Serialize, Deserialize)]
273pub struct GetArgs {
274 pub path: RepoPath,
275 pub viewer: Viewer,
276}
277
Automations: rules in .g1t/automations that act when something happens278/// `path_by_id`: where a repository is, whoever may see it. For g1t's own
279/// services, which hold a repository's id from an event and act for its
280/// workspace; nothing outside reaches it. Returns `Option<RepoPath>`, null
281/// for a fork or an unknown id.
282#[derive(Debug, Serialize, Deserialize)]
283pub struct PathByIdArgs {
284 pub id: String,
285}
286
Rust repos service with shipping; pull requests kept in the model287/// `get_by_id`. Returns `Outcome<Repo>`.
288#[derive(Debug, Serialize, Deserialize)]
289pub struct GetByIdArgs {
290 pub id: String,
291 pub viewer: Viewer,
292}
293
294/// `list`: repos the viewer may see, newest first. Returns `Vec<Repo>`.
295#[derive(Debug, Default, Serialize, Deserialize)]
Workspaces own repositories296#[serde(rename_all = "camelCase")]
Rust repos service with shipping; pull requests kept in the model297pub struct ListArgs {
298 pub viewer: Viewer,
299 #[serde(default)]
300 pub query: Option<String>,
Workspaces own repositories301 /// Only repos in this workspace.
Rust repos service with shipping; pull requests kept in the model302 #[serde(default)]
303 pub namespace: Option<String>,
Workspaces own repositories304 /// Only repos in workspaces the viewer belongs to.
305 #[serde(default)]
306 pub member_only: bool,
Rust repos service with shipping; pull requests kept in the model307}
308
309/// `create`. Returns `Outcome<Repo>`.
310#[derive(Debug, Serialize, Deserialize)]
311#[serde(rename_all = "camelCase")]
312pub struct CreateArgs {
Workspaces own repositories313 /// Who is creating it; they must belong to the workspace.
Rust repos service with shipping; pull requests kept in the model314 pub owner: User,
Workspaces own repositories315 /// The workspace it is created in.
316 pub namespace: String,
Rust repos service with shipping; pull requests kept in the model317 pub name: String,
318 #[serde(default)]
319 pub description: Option<String>,
320 #[serde(default)]
321 pub is_private: bool,
Agents as a team: lifecycle, merge queue, billing and a new shell322 /// The https address of a public git repository to copy the default
323 /// branch of, such as `https://github.com/owner/repo`.
324 #[serde(default)]
325 pub import_url: Option<String>,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look326 /// With `import_url`: a GitHub installation access token that opens it,
327 /// for a private repository. Every branch and tag is then copied, not
328 /// only the default branch. Set only by the integrations service.
329 #[serde(default, skip_serializing_if = "Option::is_none")]
330 pub import_token: Option<String>,
Rust repos service with shipping; pull requests kept in the model331}
332
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look333/// `mirror`: makes a repository's branches and tags match another git
334/// host's, or pushes its own out to one. Services only. Returns
335/// `Outcome<Mirrored>`.
336#[derive(Debug, Serialize, Deserialize)]
337#[serde(rename_all = "camelCase")]
338pub struct MirrorArgs {
339 pub repo_id: String,
340 /// The other host's https address, such as
341 /// `https://github.com/owner/repo.git`.
342 pub url: String,
343 /// A GitHub installation access token for it. Opaque: any length.
344 pub token: String,
345 pub direction: MirrorDirection,
346}
347
348#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
349#[serde(rename_all = "snake_case")]
350pub enum MirrorDirection {
351 /// The repository on g1t follows the other host: its refs are moved,
352 /// and removed, to match.
353 Pull,
354 /// The other host follows g1t: refs g1t has are pushed there; refs only
355 /// the other host has are left alone.
356 Push,
357}
358
359/// What a `mirror` changed.
360#[derive(Clone, Debug, Default, Serialize, Deserialize)]
361#[serde(rename_all = "camelCase")]
362pub struct Mirrored {
363 /// Full ref names created or moved.
364 pub updated: Vec<String>,
365 pub deleted: Vec<String>,
366}
367
Agents as a team: lifecycle, merge queue, billing and a new shell368/// `update`: changes whichever of a repository's details are given.
369/// Members of its workspace only. Returns `Outcome<Repo>`.
370#[derive(Debug, Serialize, Deserialize)]
371#[serde(rename_all = "camelCase")]
372pub struct UpdateArgs {
373 pub actor: User,
374 pub path: RepoPath,
375 /// An empty description clears it.
376 #[serde(default)]
377 pub description: Option<String>,
378 #[serde(default)]
379 pub is_private: Option<bool>,
380 #[serde(default)]
381 pub protected: Option<bool>,
Search across all of g1t, Explore, and a command palette382 /// Replaces its topics; an empty list clears them.
383 #[serde(default)]
384 pub topics: Option<Vec<String>>,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look385 /// Its home page; an empty string clears it.
386 #[serde(default)]
387 pub website: Option<String>,
388 /// Where the request came in, for the audit log; g1t.sh when absent.
389 #[serde(default)]
390 pub surface: Option<crate::audit::Surface>,
Agents as a team: lifecycle, merge queue, billing and a new shell391}
392
Rust repos service with shipping; pull requests kept in the model393/// `tree`. Returns `Outcome<TreeView>`.
394#[derive(Debug, Serialize, Deserialize)]
395#[serde(rename_all = "camelCase")]
396pub struct TreeArgs {
397 pub path: RepoPath,
398 pub viewer: Viewer,
399 /// The default branch when absent.
400 #[serde(default, rename = "ref")]
401 pub git_ref: Option<String>,
402 #[serde(default)]
403 pub tree_path: String,
404}
405
406/// `blob`. Returns `Outcome<BlobView>`.
407#[derive(Debug, Serialize, Deserialize)]
408#[serde(rename_all = "camelCase")]
409pub struct BlobArgs {
410 pub path: RepoPath,
411 pub viewer: Viewer,
412 #[serde(rename = "ref")]
413 pub git_ref: String,
414 pub file_path: String,
415}
416
417/// `log`. Returns `Outcome<Vec<Commit>>`.
418#[derive(Debug, Serialize, Deserialize)]
419pub struct LogArgs {
420 pub path: RepoPath,
421 pub viewer: Viewer,
422 #[serde(default, rename = "ref")]
423 pub git_ref: Option<String>,
424 pub limit: u32,
425}
426
Issues and pull requests replace intents and attempts427/// `fork_for_pull`: a copy-on-write copy of the source repo, hidden from
428/// listings, for one pull request to be made in. Returns `Outcome<Repo>`.
Rust repos service with shipping; pull requests kept in the model429#[derive(Debug, Serialize, Deserialize)]
430#[serde(rename_all = "camelCase")]
431pub struct ForkArgs {
432 pub source_id: String,
Issues and pull requests replace intents and attempts433 pub pull_id: String,
Rust repos service with shipping; pull requests kept in the model434 pub actor: User,
435}
436
437/// `git_access`: authorizes a git operation and says where to send it.
438/// Pushing to a repo that does not exist creates it in the pusher's own
439/// namespace. Returns `Outcome<GitAccess>`.
440#[derive(Debug, Serialize, Deserialize)]
441pub struct GitAccessArgs {
442 pub path: RepoPath,
443 pub viewer: Viewer,
444 pub service: GitService,
445}
446
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar447/// `land`: moves the branch a pull request merges into (the repository's
448/// default branch unless `target_branch` names another) to the head of
449/// its source. Refused with `conflict` when the source is behind, since
450/// that would discard commits. Returns `Outcome<Landed>`.
Rust repos service with shipping; pull requests kept in the model451#[derive(Debug, Serialize, Deserialize)]
452#[serde(rename_all = "camelCase")]
453pub struct LandArgs {
Pull requests from branches454 /// The repository holding the commits: a pull request's fork, or the
455 /// target itself when landing one of its own branches.
456 pub source_id: String,
457 /// The branch of the source to land. Required when the source is the
458 /// target; a fork lands its default branch.
459 #[serde(default)]
460 pub branch: Option<String>,
Rust repos service with shipping; pull requests kept in the model461 pub actor: User,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar462 /// The branch of the target to land on; its default branch when absent.
463 #[serde(default)]
464 pub target_branch: Option<String>,
Rust repos service with shipping; pull requests kept in the model465}
Diffs on attempts; hosted agent presented as the g1t agent466
467#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
468#[serde(rename_all = "lowercase")]
469pub enum FileStatus {
470 Added,
471 Modified,
472 Deleted,
473}
474
475#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
476#[serde(rename_all = "lowercase")]
477pub enum LineKind {
478 /// Unchanged, shown for context.
479 Context,
480 Add,
481 Delete,
482}
483
484#[derive(Clone, Debug, Serialize, Deserialize)]
485pub struct DiffLine {
486 pub kind: LineKind,
487 /// Line number in the old file; absent for added lines.
488 pub old: Option<u32>,
489 /// Line number in the new file; absent for deleted lines.
490 pub new: Option<u32>,
491 pub text: String,
492}
493
494/// A run of changed lines with their surrounding context.
495#[derive(Clone, Debug, Serialize, Deserialize)]
496pub struct Hunk {
497 pub lines: Vec<DiffLine>,
498}
499
500#[derive(Clone, Debug, Serialize, Deserialize)]
501pub struct FileDiff {
502 pub path: String,
503 pub status: FileStatus,
504 pub additions: u32,
505 pub deletions: u32,
506 /// True when the file is binary or too large, so no lines are shown.
507 pub binary: bool,
508 pub hunks: Vec<Hunk>,
509}
510
511/// What changed between two commits.
512#[derive(Clone, Debug, Serialize, Deserialize)]
513pub struct Comparison {
514 /// Null when the head has no earlier commit to compare against.
515 pub base: Option<String>,
516 pub head: String,
517 pub files: Vec<FileDiff>,
518 /// True when the change was too large to return in full.
519 pub truncated: bool,
520}
521
Pull requests from branches522/// `compare`: what `head` changes relative to `base`.
Diffs on attempts; hosted agent presented as the g1t agent523///
Pull requests from branches524/// `head` is a branch or a commit, and defaults to the default branch.
525/// With no `base`, a fork is compared against the point where it and the
526/// repository it came from last agreed; a branch against the point where it
527/// left the default branch; and the default branch against its head's
528/// parent. Returns `Outcome<Comparison>`.
Diffs on attempts; hosted agent presented as the g1t agent529#[derive(Debug, Serialize, Deserialize)]
530#[serde(rename_all = "camelCase")]
531pub struct CompareArgs {
532 pub repo_id: String,
533 pub viewer: Viewer,
534 #[serde(default)]
535 pub base: Option<String>,
Pull requests from branches536 #[serde(default)]
537 pub head: Option<String>,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar538 /// With no `base`: the branch whose shared point with the head it is
539 /// compared from, instead of the default branch. A pull request into
540 /// another branch is compared this way.
541 #[serde(default)]
542 pub base_branch: Option<String>,
Pull requests from branches543}
544
Agents as a team: lifecycle, merge queue, billing and a new shell545/// Lines `start` to `end` of a file, inclusive and counted from 1, last
546/// changed by `commit`.
547#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
548pub struct BlameRange {
549 pub start: u32,
550 pub end: u32,
551 pub commit: String,
552}
553
554/// Who last changed each line of a file.
555#[derive(Clone, Debug, Serialize, Deserialize)]
556pub struct Blame {
557 /// The commit the file was read at.
558 pub head: String,
559 /// Every line, in order, in runs that share a commit.
560 pub ranges: Vec<BlameRange>,
561 /// The commits the ranges name, each once.
562 pub commits: Vec<Commit>,
563 /// True when the history was too long to read in full, so the oldest
564 /// lines are given to the oldest commit read.
565 pub partial: bool,
566}
567
568/// `blame`: who last changed each line of `path` as of `ref` (the default
569/// branch if absent). Returns `Outcome<Blame>`; not found when the file is
570/// missing or is not text.
571#[derive(Debug, Serialize, Deserialize)]
572pub struct BlameArgs {
573 pub path: RepoPath,
574 pub viewer: Viewer,
575 #[serde(default, rename = "ref")]
576 pub git_ref: Option<String>,
577 #[serde(rename = "filePath")]
578 pub file_path: String,
579}
580
Pull requests from branches581/// A branch and the commit it points to.
582#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
583pub struct Branch {
584 pub name: String,
585 pub hash: String,
586}
587
Branches and Tags pages, each file's last commit, and the branch menu on files588/// `last_commits`: which commit last changed each entry of a directory at
589/// `ref` (the default branch when absent). Returns `Outcome<LastCommits>`.
590#[derive(Debug, Serialize, Deserialize)]
591#[serde(rename_all = "camelCase")]
592pub struct LastCommitsArgs {
593 pub path: RepoPath,
594 pub viewer: Viewer,
595 #[serde(default, rename = "ref")]
596 pub git_ref: Option<String>,
597 #[serde(default)]
598 pub tree_path: String,
Last commits walk the whole history: progress kept per ref and path, a push reads only its own commits599 /// Answer within this many milliseconds with what was found; absent,
600 /// within 20 seconds. A walk that stops short keeps its progress, and
601 /// the next call goes on from it.
A last-commits walk runs to the end unless asked for a budget; the page bounds its own wait602 #[serde(default)]
603 pub budget_ms: Option<u64>,
Branches and Tags pages, each file's last commit, and the branch menu on files604}
605
606/// An entry of a directory and the commit that last changed it.
607#[derive(Clone, Debug, Serialize, Deserialize)]
608pub struct LastCommit {
609 pub name: String,
610 pub commit: Commit,
611}
612
Last commits walk the whole history: progress kept per ref and path, a push reads only its own commits613/// The entries' last commits. `complete` is false when the walk stopped
614/// (or the history ran out) before every entry was placed; those entries
615/// are left out, and a later call goes on placing them.
Branches and Tags pages, each file's last commit, and the branch menu on files616#[derive(Clone, Debug, Serialize, Deserialize)]
617pub struct LastCommits {
618 pub entries: Vec<LastCommit>,
619 pub complete: bool,
620}
621
Merge project overview: one branch_drift call, spliced histories, cached tags, 6 repos calls instead of 25622/// `branch_drift`: how far each of `heads` (branch head commits) has moved
623/// from `base` (the default branch's head commit), and each one's head
624/// commit, in one call. Every answer is kept by the pair of hashes: neither
625/// history can change, so neither can it. Returns `Outcome<BranchDrifts>`.
626#[derive(Debug, Serialize, Deserialize)]
627#[serde(rename_all = "camelCase")]
628pub struct BranchDriftArgs {
629 pub path: RepoPath,
630 pub viewer: Viewer,
631 pub base: String,
632 pub heads: Vec<String>,
633}
634
635/// Commits a branch has that the default branch does not (`ahead`), and
636/// the other way round (`behind`), as `git rev-list --left-right --count`.
637#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
638pub struct Drift {
639 pub ahead: u32,
640 pub behind: u32,
641}
642
643/// One branch head's commit and drift. `drift` is absent when the two
644/// histories do not meet within what is read (or could not be read).
645#[derive(Clone, Debug, Serialize, Deserialize)]
646pub struct BranchDrift {
647 pub head: String,
648 pub commit: Option<Commit>,
649 pub drift: Option<Drift>,
650}
651
652/// `base`'s own commit, and each head's answer in the order asked.
653#[derive(Clone, Debug, Serialize, Deserialize)]
654pub struct BranchDrifts {
655 pub base: Option<Commit>,
656 pub branches: Vec<BranchDrift>,
657}
658
Branches and Tags pages, each file's last commit, and the branch menu on files659/// `tags`: the repository's tags, newest commit first, each with the
660/// commit it names. Returns `Outcome<Vec<Tag>>`.
661#[derive(Debug, Serialize, Deserialize)]
662pub struct TagsArgs {
663 pub path: RepoPath,
664 pub viewer: Viewer,
665}
666
667/// A tag, and its commit when it could be read.
668#[derive(Clone, Debug, Serialize, Deserialize)]
669pub struct Tag {
670 pub name: String,
671 pub commit: Option<Commit>,
672}
673
Pull requests from branches674/// `branches`: the repository's branches, default branch first.
675/// Returns `Outcome<Vec<Branch>>`.
676#[derive(Debug, Serialize, Deserialize)]
677pub struct BranchesArgs {
678 pub path: RepoPath,
679 pub viewer: Viewer,
680}
681
Agents as a team: lifecycle, merge queue, billing and a new shell682/// `behind`: whether the default branch of the repository a pull request
683/// would merge into has commits its source does not. For services that
684/// have already decided the caller may see the pull request; it reveals
685/// one bit. Returns `bool`.
686#[derive(Debug, Serialize, Deserialize)]
687#[serde(rename_all = "camelCase")]
688pub struct BehindArgs {
689 /// The pull request's fork, or the repository itself for a branch.
690 pub source_id: String,
691 /// The branch of the source. A fork is compared on its default branch.
692 #[serde(default)]
693 pub branch: Option<String>,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar694 /// The branch of the target it would merge into; the default branch
695 /// when absent.
696 #[serde(default)]
697 pub target_branch: Option<String>,
Agents as a team: lifecycle, merge queue, billing and a new shell698}
699
Agents and memory, checks and conflicts, profiles, slug renames, custom domains700/// `divergence`: how a pull request's source and the default branch it
701/// would merge into have moved apart since they last agreed: the files each
702/// side changed. Takes `BehindArgs`. For services that have already decided
703/// the caller may see the pull request; it reveals paths, not contents.
704/// Returns `Option<Divergence>`, null when either side has no commits.
705#[derive(Clone, Debug, Default, Serialize, Deserialize)]
706#[serde(rename_all = "camelCase")]
707pub struct Divergence {
708 /// The source's commit.
709 pub head: String,
710 /// The default branch's commit.
711 pub base: String,
712 /// Where they last agreed, if that could be found.
713 pub merge_base: Option<String>,
714 /// Whether the default branch has commits the source does not.
715 pub behind: bool,
716 /// The files the source changed since the merge base.
717 pub ours: Vec<String>,
718 /// The files the default branch changed since the merge base. Empty
719 /// when it is not behind.
720 pub theirs: Vec<String>,
721 /// Whether either list was cut short.
722 pub truncated: bool,
723}
724
Catching up with main takes seconds when the two sides touched different files725/// `update_pull_branch`: brings a pull request's source up to date with the
726/// default branch it would merge into, without a sandbox, when that can be
727/// done safely: merges the default branch's head into the source's head and
728/// pushes the merge commit to the source's branch, as `actor`, only if the
729/// branch has not moved meanwhile. It applies only when the two sides
730/// changed different files since they last agreed; otherwise the answer is
731/// [`PullBranchUpdate::NeedsAgent`] and nothing is pushed. Refused unless
732/// `actor` may push to the source. Returns `Outcome<PullBranchUpdate>`.
733#[derive(Debug, Serialize, Deserialize)]
734#[serde(rename_all = "camelCase")]
735pub struct UpdatePullBranchArgs {
736 /// The pull request's fork, or the repository itself for a branch.
737 pub source_id: String,
738 /// The branch of the source. A fork is updated on its default branch.
739 #[serde(default)]
740 pub branch: Option<String>,
741 /// The pull request's number, to name it in the merge commit's message
742 /// when its branch has the same name as the default branch.
743 pub number: u32,
744 /// Who asked: the merge commit's author and committer, and the pusher.
745 pub actor: User,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar746 /// The branch of the target to merge in; its default branch when absent.
747 #[serde(default)]
748 pub target_branch: Option<String>,
Catching up with main takes seconds when the two sides touched different files749}
750
751/// Why an update has to be left to a sandbox.
752#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
753#[serde(rename_all = "snake_case")]
754pub enum NeedsAgentReason {
755 /// Both sides changed some of the same files; merging them needs a
756 /// real merge, which may or may not conflict.
757 Overlap,
758 /// Merging is known to conflict.
759 Conflicting,
760 /// The update could not be worked out here, such as when the two sides
761 /// share no history g1t can see, or the change is too large to list.
762 Unsupported,
763}
764
765/// What came of `update_pull_branch` (or the work service's `catch_up_pull`).
766#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
767#[serde(tag = "outcome", rename_all = "snake_case")]
768pub enum PullBranchUpdate {
769 /// The merge commit was pushed: the branch moved from `previous` to
770 /// `commit`.
771 Updated { commit: String, previous: String },
772 /// The source already holds the default branch's head.
773 UpToDate { commit: String },
774 /// Nothing was pushed; a sandbox has to merge it. `paths` are the
775 /// files both sides changed, or that conflict, when known.
776 NeedsAgent {
777 reason: NeedsAgentReason,
778 detail: String,
779 paths: Vec<String>,
780 },
781}
782
Pull requests from branches783/// `head`: the commit a branch points to, or null. For services reacting
784/// to a push, which have no viewer; it reveals nothing but a commit hash.
785/// Returns `Option<String>`.
786#[derive(Debug, Serialize, Deserialize)]
787#[serde(rename_all = "camelCase")]
788pub struct HeadArgs {
789 pub repo_id: String,
Workflows run when an agent's pull request is marked ready790 /// Empty for the repository's default branch.
Pull requests from branches791 pub branch: String,
Diffs on attempts; hosted agent presented as the g1t agent792}
Merge queue: tested states are deleted once their entry leaves793
794/// Where g1t keeps branches of its own in a repository, such as the merge
795/// queue's tested states. Only these can be removed with `delete_branch`.
796pub const G1T_BRANCH_PREFIX: &str = "g1t-";
797
798/// `delete_branch`: removes a branch g1t made for itself once it is done
799/// with it, never one of people's: the name must start with
800/// [`G1T_BRANCH_PREFIX`]. For services, which have no viewer. Returns
801/// `Outcome<bool>`: whether there was such a branch.
802#[derive(Debug, Serialize, Deserialize)]
803#[serde(rename_all = "camelCase")]
804pub struct DeleteBranchArgs {
805 pub repo_id: String,
806 pub branch: String,
807}
Agents and memory, checks and conflicts, profiles, slug renames, custom domains808
Fast pages, required checks on the branch, self-hosted runners, honest incidents809/// `commit_file`: writes one file on a new branch made from the default
810/// branch's head, as one commit by `actor`, without a sandbox. For a change
811/// g1t proposes on someone's behalf, such as a starter workflow, which then
812/// becomes a pull request. Refused unless `actor` may push, when the branch
813/// already exists, or when the file is already there. Returns
814/// `Outcome<CommittedFile>`.
815#[derive(Debug, Serialize, Deserialize)]
816#[serde(rename_all = "camelCase")]
817pub struct CommitFileArgs {
818 pub repo: RepoPath,
819 pub actor: User,
820 /// The new branch, which must not exist yet.
821 pub branch: String,
822 /// Where the file goes, such as `.g1t/workflows/ci.yml`.
823 pub path: String,
824 pub content: String,
825 pub message: String,
826}
827
828/// The commit `commit_file` made.
829#[derive(Clone, Debug, Serialize, Deserialize)]
830#[serde(rename_all = "camelCase")]
831pub struct CommittedFile {
832 pub branch: String,
833 pub commit: String,
834}
835
Agents and memory, checks and conflicts, profiles, slug renames, custom domains836/// `readable`: of these repository ids, the repositories the viewer may
837/// read, as `get_by_id` decides; forks and unknown ids are left out. For
838/// services that hold ids and must show only what the viewer could open.
839/// At most [`MAX_READABLE`] ids are looked at. Returns `Vec<Repo>`.
840#[derive(Debug, Serialize, Deserialize)]
841pub struct ReadableArgs {
842 pub ids: Vec<String>,
843 pub viewer: Viewer,
844}
845
846/// The most ids one `readable` call looks at.
847pub const MAX_READABLE: usize = 500;
848
849/// `public_namespaces`: the workspaces in which this account made a public
850/// repository, and so a public project, which anyone can see on its page.
851/// Returns `Vec<String>` of workspace slugs.
852#[derive(Debug, Serialize, Deserialize)]
853#[serde(rename_all = "camelCase")]
854pub struct PublicNamespacesArgs {
855 pub owner_id: String,
856}
Search across all of g1t, Explore, and a command palette857
858/// One file on a branch, or one a change touched: its path and blob.
859#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
860pub struct FileEntry {
861 pub path: String,
862 /// The blob it holds now; null when the change deleted it.
863 pub hash: Option<String>,
864}
865
866/// Files, and whether there were more than were listed.
867#[derive(Clone, Debug, Default, Serialize, Deserialize)]
868#[serde(rename_all = "camelCase")]
869pub struct FileList {
870 /// The commit the files were read at; null for an empty repository.
871 pub commit: Option<String>,
872 pub files: Vec<FileEntry>,
873 pub truncated: bool,
874}
875
876/// `list_files`: every file on a branch (the default branch when absent),
877/// path order by level, never descending into a directory named in
878/// `skip_dirs`. For services that index a repository; no viewer, since it
879/// is only reached by g1t's own services. Returns `FileList`.
880#[derive(Debug, Default, Serialize, Deserialize)]
881#[serde(rename_all = "camelCase")]
882pub struct ListFilesArgs {
883 pub repo_id: String,
884 #[serde(default, rename = "ref")]
885 pub git_ref: Option<String>,
886 #[serde(default)]
887 pub skip_dirs: Vec<String>,
888 /// At most this many files; capped at [`MAX_LISTED_FILES`].
889 pub limit: u32,
890}
891
892/// `changed_files`: the files that differ between two commits, as
893/// `list_files` reads them. With no `base`, every file at `head`. Returns
894/// `FileList`.
895#[derive(Debug, Default, Serialize, Deserialize)]
896#[serde(rename_all = "camelCase")]
897pub struct ChangedFilesArgs {
898 pub repo_id: String,
899 #[serde(default)]
900 pub base: Option<String>,
901 pub head: String,
902 #[serde(default)]
903 pub skip_dirs: Vec<String>,
904 pub limit: u32,
905}
906
907/// The most files one `list_files` or `changed_files` call lists.
908pub const MAX_LISTED_FILES: u32 = 10_000;
909
910/// `read_blobs`: the text of these blobs of a repository, for services
911/// that index it. A blob larger than `max_bytes`, or binary, comes back
912/// with no text. Returns `Vec<BlobText>`, in the order asked.
913#[derive(Debug, Default, Serialize, Deserialize)]
914#[serde(rename_all = "camelCase")]
915pub struct ReadBlobsArgs {
916 pub repo_id: String,
917 pub hashes: Vec<String>,
918 pub max_bytes: u32,
919}
920
921/// The most blobs one `read_blobs` call reads.
922pub const MAX_READ_BLOBS: usize = 100;
923
Composer from the workspace's own repositories, and go get from g1t.sh924/// `refs`: a repository's branches and tags with the commit each points to
925/// (annotated tags peeled), for services that follow them, such as the
926/// packages service's Composer registry. No viewer: g1t's own services
927/// only. Returns `Option<RepoRefs>`, null for a fork or an unknown id.
928#[derive(Debug, Default, Serialize, Deserialize)]
929#[serde(rename_all = "camelCase")]
930pub struct RefsArgs {
931 pub repo_id: String,
932}
933
934#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
935pub struct GitRefEntry {
936 /// The full ref: `refs/heads/main`, `refs/tags/v1.0.0`.
937 pub name: String,
938 pub commit: String,
939}
940
941#[derive(Clone, Debug, Serialize, Deserialize)]
942pub struct RepoRefs {
943 pub repo: Repo,
944 pub refs: Vec<GitRefEntry>,
945}
946
947/// `raw_file`: one file's bytes at a ref or commit, base64, for g1t's own
948/// services (no viewer). Returns `Option<RawFile>`: null when the file is
949/// missing or larger than `max_bytes`.
950#[derive(Debug, Default, Serialize, Deserialize)]
951#[serde(rename_all = "camelCase")]
952pub struct RawFileArgs {
953 pub repo_id: String,
954 #[serde(rename = "ref")]
955 pub git_ref: String,
956 pub path: String,
957 pub max_bytes: u32,
958}
959
960#[derive(Clone, Debug, Serialize, Deserialize)]
961pub struct RawFile {
962 pub size: u64,
963 /// Standard base64.
964 pub data: String,
965}
966
967/// `raw_blobs`: blobs' bytes, base64, in the order asked, at most
968/// [`MAX_READ_BLOBS`]; `data` is null for one missing or larger than
969/// `max_bytes`. For g1t's own services. Returns `Vec<RawBlob>`.
970#[derive(Debug, Default, Serialize, Deserialize)]
971#[serde(rename_all = "camelCase")]
972pub struct RawBlobsArgs {
973 pub repo_id: String,
974 pub hashes: Vec<String>,
975 pub max_bytes: u32,
976}
977
978#[derive(Clone, Debug, Serialize, Deserialize)]
979pub struct RawBlob {
980 pub hash: String,
981 pub size: u64,
982 pub data: Option<String>,
983}
984
Search across all of g1t, Explore, and a command palette985#[derive(Clone, Debug, Serialize, Deserialize)]
986pub struct BlobText {
987 pub hash: String,
988 pub size: u64,
989 /// Null when the blob is missing, binary or larger than asked.
990 pub text: Option<String>,
991}
992
993/// `all_ids`: every repository that is not a fork, by id, a page at a
994/// time, for services that index all of them. Returns `IdPage`.
995#[derive(Debug, Default, Serialize, Deserialize)]
996pub struct AllIdsArgs {
997 /// Ids after this one.
998 #[serde(default)]
999 pub after: Option<String>,
1000 pub limit: u32,
1001}
1002
1003#[derive(Clone, Debug, Default, Serialize, Deserialize)]
1004pub struct IdPage {
1005 pub ids: Vec<String>,
1006 /// Where the next page starts; null on the last.
1007 pub next: Option<String>,
1008}
1009
Merge main (membership, two-factor, GitHub repo roles) into tokens1010/// `repo_creators` takes [`AllIdsArgs`]: every repository that is not a
1011/// fork, with the account that created it, a page at a time, for identity
1012/// giving creators the Admin role. Returns [`CreatorPage`].
1013#[derive(Clone, Debug, Default, Serialize, Deserialize)]
1014pub struct CreatorPage {
1015 pub repos: Vec<RepoCreator>,
1016 /// Where the next page starts; null on the last.
1017 pub next: Option<String>,
1018}
1019
1020#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
1021pub struct RepoCreator {
1022 pub id: String,
1023 /// Its workspace's slug.
1024 pub namespace: String,
1025 pub name: String,
1026 /// The account that created it.
1027 pub owner_id: String,
1028}
1029
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1030/// `visibility`: which of these repositories (`namespace/name`) are
1031/// private, for billing, which pays for work on public ones from g1t's
1032/// open-source pool. A pull request's working copy answers as the
1033/// repository it is a copy of. Unknown paths are left out. Returns
1034/// `Vec<RepoVisibility>`.
1035#[derive(Debug, Default, Serialize, Deserialize)]
1036pub struct VisibilityArgs {
1037 pub paths: Vec<String>,
1038}
1039
1040#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1041pub struct RepoVisibility {
1042 pub path: String,
1043 pub is_private: bool,
1044}
1045
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1046/// `git_operations`: how many git operations (clones, fetches and pushes
1047/// through g1t's git endpoints) each workspace's repositories had in a
1048/// month, for billing's git meter. Cloudflare Artifacts charges per
1049/// operation from 2026-10-14. Pushes from agents' sandboxes go to the
1050/// store directly and are not counted here. Returns
1051/// `Vec<WorkspaceGitOperations>`.
1052#[derive(Debug, Serialize, Deserialize)]
1053pub struct GitOperationsArgs {
1054 /// YYYY-MM.
1055 pub month: String,
1056 /// Count only from this hour on, `YYYY-MM-DDTHH`, such as the day the
1057 /// provider starts charging.
1058 #[serde(default)]
1059 pub since: Option<String>,
1060 /// One workspace only; every workspace with any when absent.
1061 #[serde(default)]
1062 pub namespace: Option<String>,
1063}
1064
1065#[derive(Clone, Debug, Serialize, Deserialize)]
1066pub struct WorkspaceGitOperations {
1067 pub namespace: String,
1068 pub operations: u64,
1069}
1070
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1071/// `storage`: what each workspace's private repositories hold, as far as
1072/// g1t can measure it, for billing's daily storage meter. Returns
1073/// `Vec<WorkspaceStorage>`.
1074///
1075/// The git store does not report a repository's size. What is counted is
1076/// the bytes of every pack pushed through g1t's git endpoints to the
1077/// repository or to its pull requests' working copies. Pushes made from
1078/// agents' sandboxes, which go to the store directly, and imports are not
1079/// counted, so it is a lower bound on what is stored.
1080#[derive(Debug, Default, Serialize, Deserialize)]
1081pub struct StorageArgs {}
1082
1083#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1084pub struct WorkspaceStorage {
1085 pub namespace: String,
1086 pub private_bytes: i64,
1087 pub public_bytes: i64,
1088}
1089
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1090/// `transfer`: moves a repository to another workspace, keeping its name,
1091/// its id and everything kept under it. The actor must own both
1092/// workspaces. The old path keeps working as a redirect (see
1093/// `resolve_path`) until a repository is made there. Publishes
1094/// `repo.transferred`. Returns `Outcome<Repo>`, the repository at its new
1095/// path.
1096#[derive(Debug, Serialize, Deserialize)]
1097#[serde(rename_all = "camelCase")]
1098pub struct TransferArgs {
1099 pub actor: User,
1100 pub path: RepoPath,
1101 /// The destination workspace's slug.
1102 pub to: String,
1103 /// Where the request came in, for the audit log; g1t.sh when absent.
1104 #[serde(default)]
1105 pub surface: Option<crate::audit::Surface>,
1106}
1107
1108/// `resolve_path`: where a repository that was transferred away from
1109/// `path` is now, while nothing else is there. Returns `Option<RepoPath>`:
1110/// null when `path` is a repository, or never was one that moved. Callers
1111/// check the viewer may see the repository at its new path, as for any
1112/// other.
1113#[derive(Debug, Serialize, Deserialize)]
1114pub struct ResolvePathArgs {
1115 pub path: RepoPath,
1116}
1117
1118/// `namespace_count`: how many repositories (not pull request working
1119/// copies) a workspace holds, private or not, for deciding whether it can
1120/// be deleted. Returns `u32`.
1121#[derive(Debug, Serialize, Deserialize)]
1122pub struct NamespaceCountArgs {
1123 pub namespace: String,
1124}
1125
1126/// `delete`: deletes a repository. Owners of its workspace only, who type
1127/// its full name (`namespace/name`) as `confirm`. It is hidden at once,
1128/// git refuses it, and nothing runs for it; it can be restored for
1129/// [`RESTORE_DAYS`] days, then it is purged, its git data with it. Its
1130/// name stays taken until then, or until it is purged sooner from the
1131/// workspace's Recently deleted list. Publishes `repo.deleted`. Returns
1132/// `Outcome<DeletedRepo>`.
1133#[derive(Debug, Serialize, Deserialize)]
1134#[serde(rename_all = "camelCase")]
1135pub struct DeleteArgs {
1136 pub actor: User,
1137 pub path: RepoPath,
1138 #[serde(default)]
1139 pub confirm: String,
1140 #[serde(default)]
1141 pub surface: Option<crate::audit::Surface>,
1142}
1143
1144/// `deleted`: a workspace's recently deleted repositories, newest first.
1145/// Owners only; empty for anyone else. Returns `Vec<DeletedRepo>`.
1146#[derive(Debug, Serialize, Deserialize)]
1147pub struct DeletedArgs {
1148 pub viewer: Viewer,
1149 pub namespace: String,
1150}
1151
1152/// `restore` and `purge`: a deleted repository, by the path it had.
1153/// `restore` brings it back as it was, at that path (`repo.restored`).
1154/// `purge` removes it for good now, its git data with it, and frees its
1155/// name (`repo.purged`); it takes the full name typed as `confirm`.
1156/// Owners only. Return `Outcome<Repo>` and `Outcome<bool>`.
1157#[derive(Debug, Serialize, Deserialize)]
1158#[serde(rename_all = "camelCase")]
1159pub struct DeletedRepoArgs {
1160 pub actor: User,
1161 pub path: RepoPath,
1162 #[serde(default)]
1163 pub confirm: Option<String>,
1164 #[serde(default)]
1165 pub surface: Option<crate::audit::Surface>,
1166}
1167
1168/// `purge_due`: purges deleted repositories whose time has passed, at
1169/// most `limit` (25 when absent). The service's own schedule runs it.
1170/// Returns `u32`, how many were purged.
1171#[derive(Debug, Default, Serialize, Deserialize)]
1172pub struct PurgeDueArgs {
1173 #[serde(default)]
1174 pub limit: Option<u32>,
1175}
1176
1177/// `rename`: gives a repository a new name in its workspace, keeping its
1178/// id, its git data and everything kept under it. Owners only. The old
1179/// path keeps redirecting, as after a transfer, until a repository is made
1180/// there. Publishes `repo.renamed`. Returns `Outcome<Repo>`.
1181#[derive(Debug, Serialize, Deserialize)]
1182#[serde(rename_all = "camelCase")]
1183pub struct RenameArgs {
1184 pub actor: User,
1185 pub path: RepoPath,
1186 pub name: String,
1187 #[serde(default)]
1188 pub surface: Option<crate::audit::Surface>,
1189}
1190
1191/// `archive`: makes a repository read-only (`archived: true`), or writable
1192/// again. Owners only. While archived, pushes and merges are refused,
1193/// issues and pull requests are locked, and agents and workflows do not
1194/// run; deployments keep serving. Publishes `repo.archived` or
1195/// `repo.unarchived`. Returns `Outcome<Repo>`.
1196#[derive(Debug, Serialize, Deserialize)]
1197#[serde(rename_all = "camelCase")]
1198pub struct ArchiveArgs {
1199 pub actor: User,
1200 pub path: RepoPath,
1201 pub archived: bool,
1202 #[serde(default)]
1203 pub surface: Option<crate::audit::Surface>,
1204}
1205
1206/// `set_visibility`: makes a repository public or private. Owners only,
1207/// who type its full name as `confirm`. A free workspace takes a private
1208/// repository only while its private storage has room. Publishes
1209/// `repo.updated` and `repo.visibility_changed`. Returns `Outcome<Repo>`.
1210#[derive(Debug, Serialize, Deserialize)]
1211#[serde(rename_all = "camelCase")]
1212pub struct SetVisibilityArgs {
1213 pub actor: User,
1214 pub path: RepoPath,
1215 pub is_private: bool,
1216 #[serde(default)]
1217 pub confirm: String,
1218 #[serde(default)]
1219 pub surface: Option<crate::audit::Surface>,
1220}
1221
1222/// `set_default_branch`: makes another existing branch the one everything
1223/// lands on. Members of its workspace. Open pull requests then merge into
1224/// it. Publishes `repo.default_branch_changed`. Returns `Outcome<Repo>`.
1225#[derive(Debug, Serialize, Deserialize)]
1226#[serde(rename_all = "camelCase")]
1227pub struct SetDefaultBranchArgs {
1228 pub actor: User,
1229 pub path: RepoPath,
1230 pub branch: String,
1231 #[serde(default)]
1232 pub surface: Option<crate::audit::Surface>,
1233}
1234
1235/// `rename_branch`: renames a branch. Members of its workspace; only an
1236/// owner renames the default branch, which stays the default. Pull
1237/// requests from it follow, and web addresses naming the old branch
1238/// redirect until a branch of that name is made again. Publishes
1239/// `branch.renamed` (and `repo.default_branch_changed` for the default).
1240/// Returns `Outcome<Repo>`.
1241#[derive(Debug, Serialize, Deserialize)]
1242#[serde(rename_all = "camelCase")]
1243pub struct RenameBranchArgs {
1244 pub actor: User,
1245 pub path: RepoPath,
1246 pub from: String,
1247 pub to: String,
1248 #[serde(default)]
1249 pub surface: Option<crate::audit::Surface>,
1250}
1251
1252/// `resolve_branch`: what a branch renamed away from `branch` is called
1253/// now, for web addresses that name the old one; null when `branch` was
1254/// never renamed or exists again. Returns `Option<String>`.
1255#[derive(Debug, Serialize, Deserialize)]
1256#[serde(rename_all = "camelCase")]
1257pub struct ResolveBranchArgs {
1258 pub repo_id: String,
1259 pub branch: String,
1260}
1261
1262/// `status_by_id`: whether a repository is archived or deleted, for g1t's
1263/// own services deciding whether to act on it. An unknown id answers as
1264/// deleted. Returns `RepoStatus`.
1265#[derive(Debug, Serialize, Deserialize)]
1266pub struct StatusByIdArgs {
1267 pub id: String,
1268}
1269
1270#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
1271pub struct RepoStatus {
1272 pub archived: bool,
1273 pub deleted: bool,
1274}
1275
1276impl RepoStatus {
1277 /// Whether work may start on it: neither archived nor deleted.
1278 pub fn active(&self) -> bool {
1279 !self.archived && !self.deleted
1280 }
1281}
1282
1283/// What a person is told when something would change an archived
1284/// repository.
1285pub fn archived_message(namespace: &str, name: &str) -> String {
1286 format!(
1287 "{namespace}/{name} is archived, so it is read-only. An owner can unarchive it in its settings."
1288 )
1289}
1290
1291/// The path a repository was transferred from, and when, as `transfer`
1292/// keeps it so old addresses redirect.
1293#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
1294#[serde(rename_all = "camelCase")]
1295pub struct RepoRedirect {
1296 pub from: RepoPath,
1297 pub repo_id: String,
1298 /// RFC 3339.
1299 pub created_at: String,
1300}
1301
Search across all of g1t, Explore, and a command palette1302#[cfg(test)]
1303mod topic_tests {
1304 use super::*;
1305
1306 fn topics(list: &[&str]) -> Result<Vec<String>, String> {
1307 clean_topics(&list.iter().map(|t| t.to_string()).collect::<Vec<_>>())
1308 }
1309
1310 #[test]
1311 fn topics_are_tidied() {
1312 assert_eq!(topics(&["Rust", " web_server ", "rust", ""]).unwrap(), vec!["rust", "web-server"]);
1313 }
1314
1315 #[test]
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1316 fn websites_are_tidied() {
1317 assert_eq!(clean_website(" example.com ").unwrap().as_deref(), Some("https://example.com"));
1318 assert_eq!(clean_website("http://a.io/x").unwrap().as_deref(), Some("http://a.io/x"));
1319 assert_eq!(clean_website("").unwrap(), None);
1320 assert!(clean_website("ftp://a.io").is_err());
1321 assert!(clean_website("localhost").is_err());
1322 assert!(clean_website("https://a b.io").is_err());
1323 }
1324
1325 #[test]
1326 fn branch_names_follow_git() {
1327 for good in ["main", "trunk", "release/1.2", "feat-x_y"] {
1328 assert!(is_valid_branch_name(good), "{good}");
1329 }
1330 for bad in ["", "-x", "a..b", "a b", "x.lock", "a/", ".hidden", "a/.b", "g1t-queue", "a~1", "a:b", "@"] {
1331 assert!(!is_valid_branch_name(bad), "{bad}");
1332 }
1333 }
1334
1335 #[test]
Search across all of g1t, Explore, and a command palette1336 fn odd_topics_are_refused() {
1337 assert!(topics(&["c++"]).is_err());
1338 assert!(topics(&["-lead"]).is_err());
1339 assert!(topics(&[&"a".repeat(36)]).is_err());
1340 let many: Vec<String> = (0..21).map(|i| format!("t{i}")).collect();
1341 assert!(clean_topics(&many).is_err());
1342 }
1343}
Catching up with main takes seconds when the two sides touched different files1344
1345#[cfg(test)]
1346mod tests {
1347 use super::*;
1348
1349 #[test]
1350 fn a_pull_branch_update_reads_as_the_web_expects() {
1351 let update = PullBranchUpdate::NeedsAgent {
1352 reason: NeedsAgentReason::Overlap,
1353 detail: "both".into(),
1354 paths: vec!["a.rs".into()],
1355 };
1356 assert_eq!(
1357 serde_json::to_value(&update).unwrap(),
1358 serde_json::json!({ "outcome": "needs_agent", "reason": "overlap", "detail": "both", "paths": ["a.rs"] })
1359 );
1360 let done = PullBranchUpdate::UpToDate { commit: "c".into() };
1361 assert_eq!(serde_json::to_value(&done).unwrap(), serde_json::json!({ "outcome": "up_to_date", "commit": "c" }));
1362 }
1363}

This file's history is long; its oldest lines are credited to the oldest commit read.