Something went wrong. Try again.
atproto git client
Something went wrong. Try again.
Rust
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297//! Pull requests: everything `atgc pr` does except `diff` and `checkout`.//!//! This is a directory rather than a file because the file had reached 3,141//! lines: half again the size of the next largest module in the tree, and//! still growing, and because the two jobs inside it barely speak to each//! other. The split is a move and nothing else: not one function was renamed,//! no signature changed, no behaviour altered. The only edits that are not a//! cut and a paste are the module documentation you are reading, one//! `pub(super)`, and two intra-doc links repointed at paths that still exist.//!//! The seam is between reading pull requests and writing them, and it is a//! real one rather than a tidy one://!//! - [`mod@read`] answers "what pull requests are there": `pr list`,//! `status pr` and `pr view`. Every request it makes is public, so it needs//! no token and no session and works for accounts nobody here has ever//! logged in to. Its whole difficulty is that the answer comes from two//! sources that disagree: an account's own PDS, which is complete and//! immediate but can only speak for one account, and Bobbin, which is the//! only thing that can speak for a *repo* and can be hours behind. Merging//! those and reporting where they differ is most of what the half contains,//! and is why it is a directory of its own now, split on that line rather//! than on the verbs — see its own module documentation.//! - [`mod@write`] changes them: `pr create`, `pr resubmit`, `pr edit`,//! `pr close`, `pr reopen` and `pr comment`. All of it selects an account,//! announces which one, honours `--dry-run` and lands a record in that//! account's own PDS. Its difficulties are the other kind entirely: which//! pull the user meant, whether a status record this account writes would//! be honoured by anybody, and not clobbering a record something else wrote//! between the read and the write.//!//! The two halves share exactly one function: [`read::target_repo_did`],//! which the write path calls only to print a `view:` URL once a write has//! landed, so there is no third submodule for shared helpers. There would be//! nothing to put in it. `round_count`, `owner_and_name` and `newest` all turn//! out to have no caller outside the read half, and the `Key` alias, the//! status-page cap and `repo_did_of` none outside the write half; each sits//! with its only user rather than in a `common` module that would exist to//! collect six unrelated things.//!//! Two names left that list rather than joining it. `ellipsize` and `day` were//! here on the same reasoning and it expired: `issue list`, `repo list` and//! `search` all came to want them, the first two reaching in as//! `crate::cmd::pr::read::ellipsize` and `day` ending up copied five times//! over. Neither is about pull requests, so both are//! [`crate::term::column`] now: a column is a printing concern, and that is//! the module for printing concerns.//!//! `pr comment` went in with the writes rather than into a third submodule of//! its own. It is a write by every property that makes the write half a half,//! and its two helpers are the twins of `pr edit`'s: `comment_body` is//! `new_body` without the clear-it case and says so in its own documentation,//! and `scope_advice` is `swap_advice` aimed at a different failure. Giving//! one command its own file would have been splitting by command, which is//! the thing the read/write line is deliberately not doing.//!//! `pr diff` and `pr checkout` are not here at all. They are//! [`crate::cmd::pr::review`], which was moved out for the same kind of reason and//! argues it in its own module documentation.
// `read` is crate-visible only so that [`crate::clients::tangled::bobbin`]'s module// documentation can go on linking [`read::Source`] by a path that exists; a// private module inside a private module is not nameable from a sibling of// its parent, and a re-export of one item nobody calls is an unused import.// `write` needs no such thing and does not get it.// Crate-visible because `Pull::blobs`'s generated doc comment documents itself by// pointing here; only the write half calls it.pub(crate) mod read;pub(crate) mod review;pub(crate) mod write;
// The paths `main.rs` calls, unchanged by the split: every one of these was// `pr::<name>` before and still is.
/// The `atgc pr` verbs.#[derive(clap::Subcommand, Debug)]pub(crate) enum Command { /// Open a pull request from the current branch /// /// The branch is pushed to the target's knot first, and the patch comes /// back from the knot's own compare, which is what the `source` recorded /// on the pull claims: Tangled tells a branch-based pull from a /// patch-based one by that field alone and never checks it. --patch-only /// skips the push, sends the local patch, and records no source — for a /// target you cannot push to, or a branch not worth publishing. /// /// A local image path in the description: : /// is uploaded to your PDS and embedded in the pull; https:// URLs and /// blob+at:// URIs pass through untouched. The file need not be /// committed anywhere: paths resolve against the working directory, /// then the repo root. A path that names no file, a non-image, or a /// file over 1 MB is refused before anything is sent. /// /// The pull this opens is updated with `pr resubmit` after the branch /// changes: pushing alone never updates it. Create(write::CreateArgs), /// List pull requests: this repo's, or --all for yours everywhere /// /// Two scopes, one verb. Bare, this is the repo you are standing in, /// whoever filed them. `--all` is your own wherever you filed them, /// however many repos that spans, and needs no checkout at all: the /// records come from your PDS, so that listing is immediate and no index /// can be behind on it. It is complete as well whenever `--state` is /// filtering — that reads the whole collection, since a pull's state is /// not in its pull record — while `--state all` stops at `--limit`. /// `--author` asks either of those about somebody else: bare it narrows /// this repo to that account, and with `--all` it is their pulls /// everywhere. It reads their PDS rather than yours, which needs no /// token and no permission on anything. /// /// `--json` prints an array of objects: the same state, number, round /// count and resolved author handle the table shows, not the raw /// `sh.tangled.repo.pull` record (fetch that yourself with /// `com.atproto.repo.getRecord` off `uri` if you want it). With `--all` /// each object carries the repo it targets as well. Field names are /// stable within a minor version, same as every other flag; a field can /// be added in a `feat`, but removing or renaming one is a breaking `!` /// per CONTRIBUTING.md's versioning rules. See docs/output.md for the /// full contract. /// /// Examples: /// atgc pr list --json | jq '.[] | select(.state == null)' /// atgc pr list --state all --json | jq length /// atgc pr list --all --json | jq -r '.[] | "\(.repo) #\(.number)"' #[command(verbatim_doc_comment)] List(read::ListArgs), /// View a pull request (defaults to the current branch's) /// /// With no argument, shows the pull opened from the currently /// checked-out branch, and, when that pull is stacked, the chain it /// belongs to. Naming a pull works from anywhere and needs no branch: /// the same four spellings `pr diff` takes. A named pull the index has /// not caught up with yet is read live from its author's PDS and shown /// by itself. /// /// `--comments` adds the discussion. A comment is a record in its /// commenter's PDS and nothing enumerates who has commented, so the /// thread can only come from the appview index: pass `--source bobbin` /// with it. `issue view` takes the same pair. /// /// `--json` prints one object with the same fields the human view /// prints, state, rounds, the stack chain when there is one, plus /// each round's raw timestamp and byte size in place of the day-only /// dates the human view shows. Same stability rule as `pr list --json`; /// see docs/output.md. /// /// Examples: /// atgc pr view --json | jq .state /// atgc pr view 23 --json | jq '.rounds | length' #[command(verbatim_doc_comment)] View(read::ViewArgs), /// Print a pull request's patch, or the interdiff between two rounds /// /// Reads the round's patch blob out of the author's PDS. Both that and /// the record are public, so this needs no session and works on anyone's /// pull request, logged out. /// /// The patch goes through the pager git itself would use, and /// --interdiff hands the comparison to `git range-diff` with your own /// colour and diff settings. When stdout is not a terminal the patch is /// printed bare, so `atgc pr diff 23 | git am` is a patch and nothing /// else. /// /// Examples: /// atgc pr diff 23 /// atgc pr diff 3msg7wllcqc2b --round 1 /// atgc pr diff 23 --interdiff /// atgc pr diff at://did:plc:xyz/sh.tangled.repo.pull/3msg7wllcqc2b #[command(verbatim_doc_comment)] Diff(review::DiffArgs), /// Check a pull request out onto a local branch /// /// Applies the round's patch with `git am` onto a new branch off the /// target branch, or fetches the source branch when the pull request is /// branch-based. Refuses to run on a dirty working tree, and refuses to /// overwrite a branch that already exists. /// /// `--worktree <path>` creates a `git worktree` there and checks the /// pull out in it instead, leaving this checkout on the branch it is on /// and its uncommitted work where it is, so there is nothing to stash /// and no dirty-tree refusal. The worktree shares this repo's objects /// and its `.git/config`, so it costs no clone and commits in it are /// attributed to the same account. /// /// Examples: /// atgc pr checkout 23 /// atgc pr checkout 23 --worktree ../review-23 /// atgc pr checkout 23 --worktree ../review-23 --json | jq -r .worktree #[command(verbatim_doc_comment)] Checkout(review::CheckoutArgs), /// Merge a pull request into its target branch /// /// The web's merge button: the knot checks the latest round applies /// cleanly, merges it, and the pull is marked merged. Takes the push /// access the knot takes, so the repo's owner and its collaborators can /// both land a pull. A stacked pull is refused: Tangled merges a stack /// member together with everything beneath it, which is /// `atgc stack merge`. /// /// Examples: /// atgc pr merge 23 --dry-run /// atgc pr merge 23 #[command(verbatim_doc_comment)] Merge(write::MergeArgs), /// Append a round to one of your pull requests /// /// Pushing a branch never updates its pull: the pull is a record /// carrying patches, and this command is what refreshes it. It reads /// the currently checked-out branch, so check the branch out first /// (from elsewhere it refuses, seeing no commits), and appends a new /// round; earlier rounds are kept, so reviewers can compare them with /// `pr diff --interdiff`. The title and body are left alone: those /// are `pr edit`'s. Do not close a pull and open a new one to update it. /// /// The round follows whichever shape the pull already has, read off its /// record: one that carries a source gets that branch pushed to the knot /// again and its patch from the knot's compare, and one opened /// --patch-only is still formatted locally and still records no source. /// There is no --patch-only here — the shape was chosen when the pull was /// opened. Check out the branch the pull names; a round from another /// branch, and a pull whose source is a fork, are both refused. /// /// That push is leased against the head the pull's last round recorded, /// so the rewritten branch a round is made of lands without a manual /// force-push, and a branch something else moved refuses untouched. /// /// Examples: /// atgc pr resubmit 23 --dry-run /// atgc pr resubmit 23 /// atgc pr resubmit 3msg7wllcqc2b #[command(verbatim_doc_comment)] Resubmit(write::ResubmitArgs), /// Close a pull request /// /// Examples: /// atgc pr close 23 --dry-run /// atgc pr close 23 #[command(verbatim_doc_comment)] Close(write::StateArgs), /// Reopen a closed pull request /// /// Examples: /// atgc pr reopen 23 --dry-run /// atgc pr reopen 23 #[command(verbatim_doc_comment)] Reopen(write::StateArgs), /// Change the title or body of one of your pull requests /// /// A local image path in the new body: : is /// uploaded to your PDS and embedded, exactly as `pr create` does it; /// images the pull already embeds stay retained. https:// URLs and /// blob+at:// URIs pass through untouched, and a path that names no /// local file is refused before anything is written. Edit(write::EditArgs), /// Comment on a pull request /// /// The comment goes in your own PDS as a `sh.tangled.feed.comment` /// record, so it needs no access to the repo and no permission from /// anyone: unlike `pr close`, Tangled accepts a comment from any account. /// /// Comments attach to a *round*, not to the pull as a whole, and the /// default is the latest one. Rounds are numbered from 1 here, as they are /// for `pr diff --round`; Tangled's own URLs number them from 0, so /// `--round 1` is the round its page calls `/round/0`. /// /// There is no editor. Use `--body-file -` and a heredoc for a long one. /// /// A local image path in the body: : is /// uploaded to your PDS and embedded, as `pr create` does it; a path /// that names no local file is refused before anything is written. /// /// Examples: /// atgc pr comment 23 --body 'lgtm' /// atgc pr comment 23 --round 1 --body 'this round broke the build' /// atgc pr comment 3msg7wllcqc2b --body-file review.md /// atgc pr comment 23 --body-file - <<'EOF' #[command(verbatim_doc_comment)] Comment(write::CommentArgs),}
/// Run whichever `pr` verb was parsed.pub(crate) async fn run(command: Command) -> anyhow::Result<()> { match command { Command::Create(args) => write::create(args).await, Command::List(args) => read::list(args).await, Command::View(args) => read::view(args).await, Command::Diff(args) => review::diff(args).await, Command::Checkout(args) => review::checkout(args).await, Command::Merge(args) => write::merge(args).await, Command::Resubmit(args) => write::resubmit(args).await, Command::Close(args) => write::close(args).await, Command::Reopen(args) => write::reopen(args).await, Command::Edit(args) => write::edit(args).await, Command::Comment(args) => write::comment(args).await, }}