Skip to main content

core_lib/git/
remote.rs

1#![allow(dead_code)]
2use std::path::PathBuf;
3
4use dirs::home_dir;
5use git2::{BranchType, Cred, Error, Remote, RemoteCallbacks, Repository};
6
7pub fn find_ssh_key() -> Result<PathBuf, Error> {
8    let keys_name = vec![String::from("id_ed25519"), String::from("id_rsa")];
9    for k in keys_name {
10        let ssh_key_path = home_dir()
11            .map(|h| h.join(".ssh/").join(k))
12            .expect("Failed to find HOME directory");
13        if ssh_key_path.exists() {
14            return Ok(ssh_key_path);
15        }
16    }
17    Err(git2::Error::from_str(
18        "Failed to find ssh_key on your machine :/",
19    ))
20}
21
22pub fn get_remote_branch_hash(url: &str, branch: &str) -> Result<String, Error> {
23    tracing::trace!(%url, %branch, "fetching remote branch hash");
24    let mut callbacks = RemoteCallbacks::new();
25
26    callbacks.credentials(|_url, username_from_url, allowed_types| {
27        let username = username_from_url.unwrap_or("git");
28        let ssh_key_path = find_ssh_key()?;
29
30        if allowed_types.contains(git2::CredentialType::SSH_KEY)
31            && let Ok(cred) = Cred::ssh_key(username, None, &ssh_key_path, None)
32        {
33            return Ok(cred);
34        }
35
36        // Try default credentials
37        if allowed_types.contains(git2::CredentialType::DEFAULT)
38            && let Ok(cred) = Cred::default()
39        {
40            return Ok(cred);
41        }
42
43        // Try ssh-agent
44        if allowed_types.contains(git2::CredentialType::SSH_KEY)
45            && let Ok(cred) = Cred::ssh_key_from_agent(username)
46        {
47            return Ok(cred);
48        }
49
50        Err(git2::Error::from_str("No authentication methods available"))
51    });
52
53    let mut remote = Remote::create_detached(url)?;
54    remote.connect_auth(git2::Direction::Fetch, Some(callbacks), None)?;
55    tracing::debug!(%url, %branch, "remote connected");
56
57    let refs = remote.list()?;
58    tracing::debug!(%url, %branch, refs = refs.len(), "listing remote refs");
59
60    let branch_name = if let Some(end) = branch.strip_prefix("origin/") {
61        end
62    } else {
63        branch
64    };
65
66    for r in refs {
67        let name = r.name();
68        if name.ends_with(branch_name) {
69            return Ok(r.oid().to_string());
70        }
71    }
72
73    Err(git2::Error::from_str(&format!("Branch {branch} not found")))
74}
75
76/// search in current repository all remotes branches and store it in Vec<String>
77pub fn branch_wildcard() -> anyhow::Result<Vec<String>> {
78    let repo = Repository::open(".")?;
79    branch_wildcard_from_repo(&repo)
80}
81
82// search in the repo provided in argument all remotes branches and store it in Vec<String>
83pub fn branch_wildcard_from_repo(repo: &Repository) -> anyhow::Result<Vec<String>> {
84    let branches = repo.branches(Some(BranchType::Remote))?;
85
86    let names: Vec<_> = branches
87        .into_iter()
88        .map(|b| {
89            let (branche, _) = b?;
90            let name = branche.name()?;
91            Ok(name.unwrap_or("").to_string())
92        })
93        .collect::<Result<Vec<_>, git2::Error>>()?;
94
95    Ok(names)
96}