use crate::error::Error; use crate::executor::RunType; use crate::terminal::print_separator; use crate::utils::which; use log::{debug, error}; use std::collections::HashSet; use std::io; use std::path::{Path, PathBuf}; use std::process::Command; pub struct Git { git: Option, } pub struct Repositories<'a> { git: &'a Git, repositories: HashSet, } impl Git { pub fn new() -> Self { Self { git: which("git") } } pub fn get_repo_root>(&self, path: P) -> Option { match path.as_ref().canonicalize() { Ok(mut path) => { debug_assert!(path.exists()); if path.is_file() { debug!("{} is a file. Checking {}", path.display(), path.parent()?.display()); path = path.parent()?.to_path_buf(); } debug!("Checking if {} is a git repository", path.display()); if let Some(git) = &self.git { let output = Command::new(&git) .args(&["rev-parse", "--show-toplevel"]) .current_dir(path) .output(); if let Ok(output) = output { if !output.status.success() { return None; } return Some(String::from_utf8_lossy(&output.stdout).trim().to_string()); } } } Err(e) => match e.kind() { io::ErrorKind::NotFound => debug!("{} does not exists", path.as_ref().display()), _ => error!("Error looking for {}: {}", path.as_ref().display(), e), }, } None } pub fn pull>(&self, path: P, run_type: RunType) -> Option<(String, bool)> { let path = path.as_ref(); print_separator(format!("Pulling {}", path.display())); let git = self.git.as_ref().unwrap(); let success = || -> Result<(), Error> { run_type .execute(git) .args(&["pull", "--rebase", "--autostash"]) .current_dir(&path) .check_run()?; run_type .execute(git) .args(&["submodule", "update", "--init", "--recursive"]) .current_dir(&path) .check_run()?; Ok(()) }() .is_ok(); Some((format!("git: {}", path.display()), success)) } } impl<'a> Repositories<'a> { pub fn new(git: &'a Git) -> Self { Self { git, repositories: HashSet::new(), } } pub fn insert>(&mut self, path: P) { if let Some(repo) = self.git.get_repo_root(path) { self.repositories.insert(repo); } } pub fn repositories(&self) -> &HashSet { &self.repositories } }