feat!: add branch guessing on pr creation

This commit is contained in:
Cyborus 2024-07-20 13:06:55 -04:00
parent 4a9c9b7301
commit af40eb32ba
No known key found for this signature in database

View file

@ -45,9 +45,11 @@ pub enum PrSubcommand {
/// Create a new pull request /// Create a new pull request
Create { Create {
/// The branch to merge onto. /// The branch to merge onto.
base: String, #[clap(long)]
base: Option<String>,
/// The branch to pull changes from. /// The branch to pull changes from.
head: String, #[clap(long)]
head: Option<String>,
/// What to name the new pull request. /// What to name the new pull request.
/// ///
/// Prefix with "WIP: " to mark this PR as a draft. /// Prefix with "WIP: " to mark this PR as a draft.
@ -788,33 +790,115 @@ async fn create_pr(
repo: &RepoName, repo: &RepoName,
api: &Forgejo, api: &Forgejo,
title: String, title: String,
base: String, base: Option<String>,
head: String, head: Option<String>,
body: Option<String>, body: Option<String>,
) -> eyre::Result<()> { ) -> eyre::Result<()> {
let (repo_owner, repo_name, base, head) = match base.strip_prefix("^") {
Some(parent_base) => {
let mut repo_data = api.repo_get(repo.owner(), repo.name()).await?; let mut repo_data = api.repo_get(repo.owner(), repo.name()).await?;
let parent = *repo_data
let head = match head {
Some(head) => head,
None => {
let local_repo = git2::Repository::open(".")?;
let head = local_repo.head()?;
eyre::ensure!(
head.is_branch(),
"HEAD is not on branch, can't guess head branch"
);
let branch_ref = head
.name()
.ok_or_eyre("current branch does not have utf8 name")?;
let upstream_remote = local_repo.branch_upstream_remote(branch_ref)?;
let remote_name = upstream_remote
.as_str()
.ok_or_eyre("remote does not have utf8 name")?;
let remote = local_repo.find_remote(remote_name)?;
let remote_url_s = remote.url().ok_or_eyre("remote does not have utf8 url")?;
let remote_url = url::Url::parse(remote_url_s)?;
let clone_url = repo_data
.clone_url
.as_ref()
.ok_or_eyre("repo does not have git url")?;
let html_url = repo_data
.html_url
.as_ref()
.ok_or_eyre("repo does not have html url")?;
let ssh_url = repo_data
.ssh_url
.as_ref()
.ok_or_eyre("repo does not have ssh url")?;
eyre::ensure!(
&remote_url == clone_url || &remote_url == html_url || &remote_url == ssh_url,
"branch does not track that repo"
);
let upstream_branch = local_repo.branch_upstream_name(branch_ref)?;
let upstream_branch = upstream_branch
.as_str()
.ok_or_eyre("remote branch does not have utf8 name")?;
upstream_branch
.rsplit_once("/")
.map(|(_, b)| b)
.unwrap_or(upstream_branch)
.to_owned()
}
};
let (base, base_is_parent) = match base {
Some(base) => match base.strip_prefix("^") {
Some(stripped) if stripped.is_empty() => (None, true),
Some(stripped) => (Some(stripped.to_owned()), true),
None => (Some(base), false),
},
None => (None, false),
};
let (repo_owner, repo_name, base_repo, head) = if base_is_parent {
let parent_repo = *repo_data
.parent .parent
.take() .take()
.ok_or_eyre("cannot create pull request upstream, there is no upstream")?; .ok_or_eyre("cannot create pull request upstream, there is no upstream")?;
let parent_owner = parent let parent_owner = parent_repo
.owner .owner
.as_ref()
.ok_or_eyre("parent has no owner")? .ok_or_eyre("parent has no owner")?
.login .login
.ok_or_eyre("parent owner has no login")?; .as_deref()
let parent_name = parent.name.ok_or_eyre("parent has no name")?; .ok_or_eyre("parent owner has no login")?
.to_owned();
let parent_name = parent_repo
.name
.as_deref()
.ok_or_eyre("parent has no name")?
.to_owned();
( (
parent_owner, parent_owner,
parent_name, parent_name,
parent_base.to_owned(), parent_repo,
format!("{}:{}", repo.owner(), head), format!("{}:{}", repo.owner(), head),
) )
} } else {
None => (repo.owner().to_owned(), repo.name().to_owned(), base, head), (
repo.owner().to_owned(),
repo.name().to_owned(),
repo_data,
head,
)
}; };
let base = match base {
Some(base) => base,
None => base_repo
.default_branch
.as_deref()
.ok_or_eyre("repo does not have default branch")?
.to_owned(),
};
let body = match body { let body = match body {
Some(body) => body, Some(body) => body,
None => { None => {