git_hosting_providers: Extract Bitbucket pull request number (#34584)

git: Extract Bitbucket pull request number

Release Notes:

- git: Extract Bitbucket pull request number

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
This commit is contained in:
Neo Nie 2025-08-08 02:39:32 +01:00 committed by GitHub
parent cdfb3348ea
commit 00701b5e99
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,12 +1,22 @@
use std::str::FromStr;
use std::sync::LazyLock;
use regex::Regex;
use url::Url;
use git::{
BuildCommitPermalinkParams, BuildPermalinkParams, GitHostingProvider, ParsedGitRemote,
RemoteUrl,
PullRequest, RemoteUrl,
};
fn pull_request_regex() -> &'static Regex {
static PULL_REQUEST_REGEX: LazyLock<Regex> = LazyLock::new(|| {
// This matches Bitbucket PR reference pattern: (pull request #xxx)
Regex::new(r"\(pull request #(\d+)\)").unwrap()
});
&PULL_REQUEST_REGEX
}
pub struct Bitbucket {
name: String,
base_url: Url,
@ -96,6 +106,22 @@ impl GitHostingProvider for Bitbucket {
);
permalink
}
fn extract_pull_request(&self, remote: &ParsedGitRemote, message: &str) -> Option<PullRequest> {
// Check first line of commit message for PR references
let first_line = message.lines().next()?;
// Try to match against our PR patterns
let capture = pull_request_regex().captures(first_line)?;
let number = capture.get(1)?.as_str().parse::<u32>().ok()?;
// Construct the PR URL in Bitbucket format
let mut url = self.base_url();
let path = format!("/{}/{}/pull-requests/{}", remote.owner, remote.repo, number);
url.set_path(&path);
Some(PullRequest { number, url })
}
}
#[cfg(test)]
@ -203,4 +229,34 @@ mod tests {
"https://bitbucket.org/zed-industries/zed/src/f00b4r/main.rs#lines-24:48";
assert_eq!(permalink.to_string(), expected_url.to_string())
}
#[test]
fn test_bitbucket_pull_requests() {
use indoc::indoc;
let remote = ParsedGitRemote {
owner: "zed-industries".into(),
repo: "zed".into(),
};
let bitbucket = Bitbucket::public_instance();
// Test message without PR reference
let message = "This does not contain a pull request";
assert!(bitbucket.extract_pull_request(&remote, message).is_none());
// Pull request number at end of first line
let message = indoc! {r#"
Merged in feature-branch (pull request #123)
Some detailed description of the changes.
"#};
let pr = bitbucket.extract_pull_request(&remote, message).unwrap();
assert_eq!(pr.number, 123);
assert_eq!(
pr.url.as_str(),
"https://bitbucket.org/zed-industries/zed/pull-requests/123"
);
}
}