use crate::fleet::config::GitModules;
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::*;
pub fn execute<P: AsRef<Path>>(
repo_root: P,
branch: &str,
from_branch: Option<&str>,
push: bool,
force_all: bool,
dry_run: bool,
verbose: bool,
) -> Result<()> {
let tree = SubmoduleTree::discover(&repo_root)?;
Formatter::print_info(&format!(
"Creating branch '{}' in superproject + {} submodule(s){}",
branch,
tree.len(),
if let Some(from) = from_branch {
format!(" from '{}'", from)
} else {
String::new()
}
));
if dry_run {
Formatter::print_warning("DRY RUN: No changes will be made");
}
let mut successful = 0;
let mut failed = 0;
let mut skipped = 0;
let mut branched: Vec<(PathBuf, PathBuf)> = Vec::new();
if verbose {
Formatter::print_submodule_header("[SUPERPROJECT]");
}
let super_repo = GitOps::open_repo(&repo_root)?;
let super_is_clean = GitOps::is_clean(&super_repo)?;
let super_branch_exists = GitOps::branch_exists(&super_repo, branch)?;
if super_is_clean && !force_all {
Formatter::print_info("No changes in superproject, skipping branch creation");
skipped += 1;
} else if super_branch_exists {
Formatter::print_warning(&format!(
"Branch '{}' already exists in superproject, skipping creation",
branch
));
skipped += 1;
} else {
if !dry_run {
if let Err(err) = git_backend::create_branch(&super_repo, branch, from_branch) {
Formatter::print_error(&format!(
"Failed to create branch '{}' in superproject: {}",
branch, err
));
return Err(SuperGitError::Other(
"Superproject branch creation failed".to_string(),
));
}
Formatter::print_success(&format!("Created branch '{}' in superproject", branch));
successful += 1;
if push {
match git_backend::push_current_branch(&super_repo, Some("origin"), true, false) {
Ok(_) => {
Formatter::print_success(&format!(
"Pushed branch '{}' in superproject",
branch
));
}
Err(err) => {
Formatter::print_error(&format!(
"Failed to push '{}' in superproject: {}",
branch, err
));
failed += 1;
}
}
}
} else {
Formatter::print_info(&format!("Would create branch '{}'", branch));
successful += 1;
}
}
for node in tree.top_down() {
if verbose {
Formatter::print_submodule_header(node.label());
}
if !node.initialized {
Formatter::print_warning(&format!(
"Submodule '{}' not initialized, skipping",
node.label()
));
skipped += 1;
continue;
}
let repo = match GitOps::open_repo(&node.abs_path) {
Ok(r) => r,
Err(e) => {
Formatter::print_error(&format!("Failed to open '{}': {}", node.label(), e));
failed += 1;
continue;
}
};
let is_clean = match GitOps::is_clean(&repo) {
Ok(c) => c,
Err(e) => {
Formatter::print_error(&format!(
"Failed to check status of '{}': {}",
node.label(),
e
));
failed += 1;
continue;
}
};
if is_clean && !force_all {
if verbose {
Formatter::print_info(&format!("No changes in '{}'", node.label()));
}
skipped += 1;
continue;
}
let branch_exists = match GitOps::branch_exists(&repo, branch) {
Ok(exists) => exists,
Err(e) => {
Formatter::print_error(&format!(
"Failed to check branch in '{}': {}",
node.label(),
e
));
failed += 1;
continue;
}
};
if branch_exists {
Formatter::print_warning(&format!(
"Branch '{}' already exists in '{}', skipping",
branch,
node.label()
));
skipped += 1;
continue;
}
if dry_run {
Formatter::print_info(&format!("Would create branch '{}'", branch));
successful += 1;
continue;
}
match SubmoduleOps::create_branch(node, branch, from_branch) {
Ok(_) => {
Formatter::print_success(&format!(
"Created branch '{}' in '{}'",
branch,
node.label()
));
successful += 1;
branched.push((node.parent_path.clone(), node.rel_path.clone()));
if push {
match SubmoduleOps::push(node, false) {
Ok(_) => {
Formatter::print_success(&format!(
"Pushed '{}' in '{}'",
branch,
node.label()
));
}
Err(e) => {
Formatter::print_error(&format!(
"Failed to push '{}' in '{}': {}",
branch,
node.label(),
e
));
failed += 1;
}
}
}
}
Err(e) => {
Formatter::print_error(&format!(
"Failed to create branch '{}' in '{}': {}",
branch,
node.label(),
e
));
failed += 1;
}
}
}
if !dry_run {
Formatter::print_summary(successful, failed, skipped);
if !branched.is_empty() {
Formatter::print_info("Updating .gitmodules branch fields...");
for (parent_path, rel_path) in &branched {
let rel_path = rel_path.to_str().ok_or_else(|| {
SuperGitError::Other(format!(
"Submodule path '{}' is not valid UTF-8 and cannot be written to .gitmodules",
rel_path.display()
))
})?;
GitModules::update_branch(parent_path, rel_path, branch)?;
}
Formatter::print_success("Updated .gitmodules");
}
}
if failed > 0 {
return Err(SuperGitError::Other(format!(
"{} submodule(s) failed to create branch",
failed
)));
}
Ok(())
}