diff --git a/Cargo.lock b/Cargo.lock index 33bdb05..04c349a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -100,7 +100,7 @@ checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "git-link" -version = "0.1.2" +version = "0.1.3" dependencies = [ "clap", ] diff --git a/Cargo.toml b/Cargo.toml index f775264..97d4014 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "git-link" -version = "0.1.2" +version = "0.1.3" edition = "2024" description = "`git link` alias to fetch a link to a remote git repo" license = "Unlicense" diff --git a/src/main.rs b/src/main.rs index 56b53ed..c5df567 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,8 +16,10 @@ struct Cli { #[derive(Subcommand)] enum Commands { - /// Link to a pull request for the current branch + /// Link to a pull request / merge request for the current branch Pr, + /// Link to a merge request / pull request for the current branch + Mr, } fn run(cmd: &str, args: &[&str]) -> String { @@ -54,10 +56,31 @@ pub fn normalize_remote(remote: &str) -> String { panic!("Unrecognized remote URL format: {}", remote); } +fn is_gitlab_repo(repo_url: &str) -> bool { + repo_url.contains("gitlab") +} + +/// Create a Pull Request url, assuming the remote is GitHub, or a URL-compatible site. pub fn pr_url(repo_url: &str, branch: &str) -> String { format!("{}/pull/new/{}", repo_url, branch) } +/// Create a Merge Request url, assuming the remote is GitLab, or a URL-compatible site. +pub fn mr_url(repo_url: &str, branch: &str) -> String { + format!( + "{}/-/merge_requests/new?merge_request[source_branch]={}", + repo_url, branch + ) +} + +fn link_for_command(repo_url: &str, branch: &str) -> String { + if is_gitlab_repo(repo_url) { + mr_url(repo_url, branch) + } else { + pr_url(repo_url, branch) + } +} + fn open_in_browser(url: &str) { let opener = if cfg!(target_os = "macos") { "open" @@ -80,21 +103,21 @@ fn main() { let repo_url = normalize_remote(&remote); let final_url = match cli.command { - Some(Commands::Pr) => { + Some(Commands::Pr | Commands::Mr) => { let branch = run("git", &["symbolic-ref", "--short", "HEAD"]); if branch.is_empty() { eprintln!("Not on a branch"); exit(1); } - pr_url(&repo_url, &branch) + link_for_command(&repo_url, &branch) } None => repo_url, }; - // Always print + // Always print (whether or not opening in a browser). println!("{}", final_url); - // Optionally open + // Optionally open in web browser. if cli.open { open_in_browser(&final_url); } @@ -145,4 +168,28 @@ mod tests { fn invalid_remote_panics() { normalize_remote("ssh://example.com/org/project.git"); } + + #[test] + fn github_pr_url() { + let repo = "https://github.com/org/project"; + let branch = "feature-x"; + let expected = "https://github.com/org/project/pull/new/feature-x"; + assert_eq!(link_for_command(repo, branch), expected); + } + + #[test] + fn gitlab_mr_url() { + let repo = "https://gitlab.com/org/project"; + let branch = "feature-x"; + let expected = "https://gitlab.com/org/project/-/merge_requests/new?merge_request[source_branch]=feature-x"; + assert_eq!(link_for_command(repo, branch), expected); + } + + #[test] + fn self_hosted_gitlab_mr_url() { + let repo = "https://gitlab.example.com/org/project"; + let branch = "dev"; + let expected = "https://gitlab.example.com/org/project/-/merge_requests/new?merge_request[source_branch]=dev"; + assert_eq!(link_for_command(repo, branch), expected); + } }