use std::path::{Path, PathBuf};
use crate::shell::{exec, exec_silent};
pub fn parse_repo_name_from_url(url: &str) -> Option<String> {
let normalized = if url.contains(':') && !url.contains("://") {
url.replace(':', "/")
} else {
url.to_string()
};
normalized
.rsplit('/')
.next()
.map(|segment| segment.trim_end_matches(".git").to_string())
.filter(|s| !s.is_empty())
}
pub fn is_git_repository() -> bool {
exec_silent("git", &["rev-parse", "--git-dir"], None).is_ok()
}
pub fn is_git_repository_at(path: &Path) -> bool {
exec_silent("git", &["rev-parse", "--git-dir"], Some(path)).is_ok()
}
pub fn get_repository_name() -> String {
if let Ok(remote_url) = exec("git", &["remote", "get-url", "origin"], None) {
if let Some(name) = parse_repo_name_from_url(remote_url.trim()) {
return name;
}
}
std::env::current_dir()
.ok()
.and_then(|p| p.file_name().map(|s| s.to_string_lossy().to_string()))
.unwrap_or_else(|| "unknown".to_string())
}
pub fn get_repo_root() -> Option<PathBuf> {
exec("git", &["rev-parse", "--show-toplevel"], None)
.ok()
.map(|s| PathBuf::from(s.trim()))
}
pub fn get_repo_root_at(path: &Path) -> Option<PathBuf> {
exec("git", &["rev-parse", "--show-toplevel"], Some(path))
.ok()
.map(|s| PathBuf::from(s.trim()))
}
pub fn local_branch_exists(branch: &str) -> bool {
let ref_path = format!("refs/heads/{}", branch);
exec_silent("git", &["show-ref", "--verify", "--quiet", &ref_path], None).is_ok()
}
#[cfg(test)]
mod tests {
use super::*;
use std::process::Command;
use tempfile::TempDir;
fn create_test_repo() -> TempDir {
let temp = TempDir::new().unwrap();
Command::new("git")
.args(["init"])
.current_dir(temp.path())
.output()
.unwrap();
temp
}
#[test]
fn test_is_git_repository_in_repo() {
let temp = create_test_repo();
assert!(is_git_repository_at(temp.path()));
}
#[test]
fn test_is_git_repository_not_repo() {
let temp = TempDir::new().unwrap();
assert!(!is_git_repository_at(temp.path()));
}
#[test]
fn test_get_repo_root_at_in_repo() {
let temp = create_test_repo();
let root = get_repo_root_at(temp.path());
assert!(root.is_some());
assert_eq!(
root.unwrap().canonicalize().unwrap(),
temp.path().canonicalize().unwrap()
);
}
#[test]
fn test_get_repo_root_at_not_repo() {
let temp = TempDir::new().unwrap();
let root = get_repo_root_at(temp.path());
assert!(root.is_none());
}
#[test]
fn test_parse_repo_name_from_url_https() {
let url = "https://github.com/user/repo.git";
assert_eq!(parse_repo_name_from_url(url), Some("repo".to_string()));
}
#[test]
fn test_parse_repo_name_from_url_ssh() {
let url = "git@github.com:user/repo.git";
assert_eq!(parse_repo_name_from_url(url), Some("repo".to_string()));
}
#[test]
fn test_parse_repo_name_from_url_without_git_suffix() {
let url = "https://github.com/user/repo";
assert_eq!(parse_repo_name_from_url(url), Some("repo".to_string()));
}
#[test]
fn test_parse_repo_name_from_url_empty() {
assert_eq!(parse_repo_name_from_url(""), None);
}
#[test]
fn test_parse_repo_name_from_url_only_git_suffix() {
assert_eq!(parse_repo_name_from_url("https://example.com/.git"), None);
}
}