use crate::fleet::error::{Result, SuperGitError};
use crate::fleet::git::{GitOps, SubmoduleOps, SubmoduleTree};
use crate::fleet::output::Formatter;
use crate::git_backend;
use crate::prelude::*;
use rayon::prelude::*;
pub fn execute<P: AsRef<Path> + Sync>(
repo_root: P,
all: bool,
force_all: bool,
dry_run: bool,
verbose: bool,
) -> Result<()> {
let tree = SubmoduleTree::discover(&repo_root)?;
Formatter::print_info(&format!(
"Fetching updates for superproject + {} submodule(s){}",
tree.len(),
if all { " (all remotes)" } else { "" }
));
if dry_run {
Formatter::print_warning("DRY RUN: No changes will be made");
}
let results: Vec<_> = tree
.top_down()
.par_iter()
.map(|node| {
let label = node.display_path.clone();
if !node.initialized {
return (label, Err("not initialized".to_string()));
}
let repo = match GitOps::open_repo(&node.abs_path) {
Ok(repo) => repo,
Err(e) => return (label, Err(e.to_string())),
};
if !force_all {
match git_backend::has_fetchable_updates(&repo, all) {
Ok(true) => {}
Ok(false) => return (label, Ok(false)),
Err(e) => return (label, Err(e.to_string())),
}
}
if dry_run {
return (label, Ok(true));
}
match SubmoduleOps::fetch(node, all) {
Ok(_) => (label, Ok(true)),
Err(e) => (label, Err(e.to_string())),
}
})
.collect();
let mut successful = 0;
let mut failed = 0;
let mut skipped = 0;
if verbose {
Formatter::print_submodule_header("[SUPERPROJECT]");
}
match fetch_repository(repo_root.as_ref(), all, force_all, dry_run) {
Ok(true) => {
if dry_run {
Formatter::print_info("Would fetch superproject");
} else {
Formatter::print_success("Fetched superproject");
}
successful += 1;
}
Ok(false) => {
if verbose {
Formatter::print_info("No fetchable remote updates in superproject");
}
skipped += 1;
}
Err(e) => {
Formatter::print_error(&format!("Failed to fetch superproject: {}", e));
failed += 1;
}
}
for (name, result) in results {
match result {
Ok(true) => {
if verbose {
if dry_run {
Formatter::print_info(&format!("Would fetch '{}'", name));
} else {
Formatter::print_success(&format!("Fetched '{}'", name));
}
}
successful += 1;
}
Ok(false) => {
if verbose {
Formatter::print_info(&format!("No fetchable remote updates in '{}'", name));
}
skipped += 1;
}
Err(e) => {
if e == "not initialized" {
if verbose {
Formatter::print_warning(&format!("'{}' not initialized, skipping", name));
}
skipped += 1;
} else {
Formatter::print_error(&format!("Failed to fetch '{}': {}", name, e));
failed += 1;
}
}
}
}
Formatter::print_summary(successful, failed, skipped);
if failed > 0 {
return Err(SuperGitError::Other(format!(
"{} repository/submodule(s) failed to fetch",
failed
)));
}
Ok(())
}
fn fetch_repository(
repo_path: &Path,
all: bool,
force_all: bool,
dry_run: bool,
) -> std::result::Result<bool, String> {
let repo = git_backend::open_repo(repo_path).map_err(|err| err.to_string())?;
if !force_all {
match git_backend::has_fetchable_updates(&repo, all) {
Ok(true) => {}
Ok(false) => return Ok(false),
Err(e) => return Err(e.to_string()),
}
}
if dry_run {
return Ok(true);
}
if all {
git_backend::fetch_all(&repo).map_err(|err| err.to_string())?;
} else {
git_backend::fetch(&repo, Some("origin")).map_err(|err| err.to_string())?;
}
Ok(true)
}