//! `pr-commits [repo]` — list a PR's commits as JSON //! (sha, message, author date, author login), paginated. //! //! The forge stores a PR's commits against its branch ref, so this //! returns them even for a PR whose shas were rewritten by a rebase //! merge. The commit message and author date survive a rebase //! unchanged, so a caller can match these rows against the linear //! `main` history to recover the merged-final commits when the PR's //! recorded shas no longer resolve. use anyhow::Result; use clap::Args as ClapArgs; use forgejo_api::structs::{Commit, RepoGetPullRequestCommitsQuery}; use serde_json::json; use crate::client::{Client, index}; use crate::verbs::{print_json, rfc3339}; /// Page cap for the commit list. Forgejo serves up to 50 commits per /// page; 40 pages (2000 commits) is far beyond any real PR. const MAX_PAGES: u32 = 40; /// Page size on the commit list endpoint (Forgejo's cap). const PAGE_SIZE: u32 = 50; #[derive(ClapArgs)] pub struct Args { /// PR number. number: u64, } pub fn run(client: &Client, args: Args) -> Result<()> { let (owner, name) = client.owner_repo()?; let idx = index(args.number)?; let mut commits: Vec = Vec::new(); for page in 1..=MAX_PAGES { let (_, batch) = client .api() .repo_get_pull_request_commits( owner, name, idx, RepoGetPullRequestCommitsQuery::default(), ) .page(page) .page_size(PAGE_SIZE) .send()?; let short = batch.len() < PAGE_SIZE as usize; commits.extend(batch); if short { break; } } let trimmed: Vec<_> = commits .iter() .map(|c| { json!({ "sha": c.sha, "message": c.commit.as_ref().and_then(|x| x.message.as_deref()), "author_date": rfc3339(c.commit.as_ref().and_then(|x| x.author.as_ref()).and_then(|a| a.date)), "author": c.author.as_ref().and_then(|u| u.login.as_deref()), }) }) .collect(); print_json(&json!(trimmed)) }