1use anyhow::{Context, Result};
4use git2::{BranchType, Repository};
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct RemoteInfo {
10 pub name: String,
12 pub url: String,
14 pub main_branch: String,
16}
17
18impl RemoteInfo {
19 pub fn get_all_remotes(repo: &Repository) -> Result<Vec<Self>> {
21 let mut remotes = Vec::new();
22 let remote_names = repo.remotes().context("Failed to get remote names")?;
23
24 for name in remote_names.iter().flatten() {
25 if let Ok(remote) = repo.find_remote(name) {
26 let url = remote.url().unwrap_or("").to_string();
27 let main_branch = Self::detect_main_branch(repo, name)?;
28
29 remotes.push(RemoteInfo {
30 name: name.to_string(),
31 url,
32 main_branch,
33 });
34 }
35 }
36
37 Ok(remotes)
38 }
39
40 fn detect_main_branch(repo: &Repository, remote_name: &str) -> Result<String> {
42 let head_ref_name = format!("refs/remotes/{}/HEAD", remote_name);
44 if let Ok(head_ref) = repo.find_reference(&head_ref_name) {
45 if let Some(target) = head_ref.symbolic_target() {
46 if let Some(branch_name) =
48 target.strip_prefix(&format!("refs/remotes/{}/", remote_name))
49 {
50 return Ok(branch_name.to_string());
51 }
52 }
53 }
54
55 let common_branches = ["main", "master", "develop"];
57
58 for branch_name in &common_branches {
59 let reference_name = format!("refs/remotes/{}/{}", remote_name, branch_name);
60 if repo.find_reference(&reference_name).is_ok() {
61 return Ok(branch_name.to_string());
62 }
63 }
64
65 let branch_iter = repo.branches(Some(BranchType::Remote))?;
67 for branch_result in branch_iter {
68 let (branch, _) = branch_result?;
69 if let Some(name) = branch.name()? {
70 if name.starts_with(&format!("{}/", remote_name)) {
71 let branch_name = name
72 .strip_prefix(&format!("{}/", remote_name))
73 .unwrap_or(name);
74 return Ok(branch_name.to_string());
75 }
76 }
77 }
78
79 Ok("unknown".to_string())
81 }
82}