use anyhow::{Context, Result};
use std::path::Path;
use tokio::process::Command;
pub async fn clone(url: &str, target: &Path) -> Result<()> {
log::info!("Cloning git repository: {}", url);
log::debug!("Target path: {}", target.display());
if target.exists() {
log::warn!("Target directory already exists: {}", target.display());
return Ok(());
}
if let Some(parent) = target.parent() {
tokio::fs::create_dir_all(parent)
.await
.context("Failed to create parent directory")?;
}
let output = Command::new("git")
.arg("clone")
.arg(url)
.arg(target)
.output()
.await
.context("Failed to execute git clone command")?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
anyhow::bail!("Git clone failed: {}", stderr);
}
log::info!("✓ Repository cloned successfully");
let gitmodules_path = target.join(".gitmodules");
if gitmodules_path.exists() {
log::info!("Found .gitmodules file, initializing submodules...");
init_submodules(target).await?;
}
Ok(())
}
async fn init_submodules(repo_path: &Path) -> Result<()> {
log::debug!("Initializing submodules in: {}", repo_path.display());
let init_output = Command::new("git")
.arg("submodule")
.arg("init")
.current_dir(repo_path)
.output()
.await
.context("Failed to execute git submodule init")?;
if !init_output.status.success() {
let stderr = String::from_utf8_lossy(&init_output.stderr);
anyhow::bail!("Git submodule init failed: {}", stderr);
}
let update_output = Command::new("git")
.arg("submodule")
.arg("update")
.arg("--recursive")
.current_dir(repo_path)
.output()
.await
.context("Failed to execute git submodule update")?;
if !update_output.status.success() {
let stderr = String::from_utf8_lossy(&update_output.stderr);
anyhow::bail!("Git submodule update failed: {}", stderr);
}
log::info!("✓ Submodules initialized and updated");
Ok(())
}
pub async fn pull(repo_path: &Path) -> Result<()> {
log::info!("Pulling latest changes: {}", repo_path.display());
if !repo_path.exists() {
anyhow::bail!("Repository does not exist: {}", repo_path.display());
}
let output = Command::new("git")
.arg("pull")
.current_dir(repo_path)
.output()
.await
.context("Failed to execute git pull")?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
anyhow::bail!("Git pull failed: {}", stderr);
}
log::info!("✓ Repository updated successfully");
let gitmodules_path = repo_path.join(".gitmodules");
if gitmodules_path.exists() {
log::info!("Updating submodules...");
update_submodules(repo_path).await?;
}
Ok(())
}
async fn update_submodules(repo_path: &Path) -> Result<()> {
let output = Command::new("git")
.arg("submodule")
.arg("update")
.arg("--recursive")
.arg("--remote")
.current_dir(repo_path)
.output()
.await
.context("Failed to execute git submodule update")?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
anyhow::bail!("Git submodule update failed: {}", stderr);
}
log::info!("✓ Submodules updated");
Ok(())
}
pub async fn check_git_available() -> Result<()> {
let output = Command::new("git")
.arg("--version")
.output()
.await
.context("Failed to check git version. Is git installed?")?;
if !output.status.success() {
anyhow::bail!("Git command failed. Please ensure git is installed.");
}
let version = String::from_utf8_lossy(&output.stdout);
log::debug!("Git version: {}", version.trim());
Ok(())
}
#[allow(dead_code)]
pub async fn status(repo_path: &Path) -> Result<String> {
let output = Command::new("git")
.arg("status")
.arg("--short")
.current_dir(repo_path)
.output()
.await
.context("Failed to execute git status")?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
anyhow::bail!("Git status failed: {}", stderr);
}
let status = String::from_utf8_lossy(&output.stdout).to_string();
Ok(status)
}