omni_dev/git/
remote.rs

1//! Git remote operations
2
3use anyhow::{Context, Result};
4use git2::{BranchType, Repository};
5use serde::{Deserialize, Serialize};
6
7/// Remote repository information
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct RemoteInfo {
10    /// Name of the remote (e.g., "origin", "upstream")
11    pub name: String,
12    /// URL of the remote repository
13    pub url: String,
14    /// Detected main branch name for this remote
15    pub main_branch: String,
16}
17
18impl RemoteInfo {
19    /// Get all remotes for a repository
20    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    /// Detect the main branch for a remote
41    fn detect_main_branch(repo: &Repository, remote_name: &str) -> Result<String> {
42        // First try to get the remote HEAD reference
43        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                // Extract branch name from refs/remotes/origin/main
47                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        // Fallback to checking common branch names
56        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        // If no common branch found, try to find any branch
66        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        // If still no branch found, return "unknown"
80        Ok("unknown".to_string())
81    }
82}