use super::types::{SyncAction, SyncPlan};
use crate::error::Result;
use crate::stack::{Stack, StackBranch};
pub fn create_sync_plan(
repo: &impl rung_git::GitOps,
stack: &Stack,
base_branch: &str,
) -> Result<SyncPlan> {
let mut actions = Vec::new();
let mut needs_rebase: std::collections::HashSet<String> = std::collections::HashSet::new();
let sorted_branches = topological_sort(&stack.branches, base_branch);
for branch in sorted_branches {
if !repo.branch_exists(&branch.name) {
continue;
}
let parent_name = branch.parent.as_deref().unwrap_or(base_branch);
if !repo.branch_exists(parent_name) && branch.parent.is_none() {
return Err(crate::error::Error::BranchNotFound(parent_name.to_string()));
}
if branch.parent.is_some() && !repo.branch_exists(parent_name) {
continue;
}
let branch_commit = repo.branch_commit(&branch.name)?;
let parent_commit = repo.branch_commit(parent_name)?;
let merge_base = repo.merge_base(branch_commit, parent_commit)?;
let needs_direct_rebase = merge_base != parent_commit;
let needs_cascade_rebase = needs_rebase.contains(branch.name.as_str());
if needs_direct_rebase || needs_cascade_rebase {
actions.push(SyncAction {
branch: branch.name.to_string(),
old_base: merge_base.to_string(),
new_base: parent_commit.to_string(),
parent_branch: parent_name.to_string(),
});
for descendant in stack.descendants(&branch.name) {
needs_rebase.insert(descendant.name.to_string());
}
}
}
Ok(SyncPlan { branches: actions })
}
fn topological_sort<'a>(branches: &'a [StackBranch], base_branch: &str) -> Vec<&'a StackBranch> {
let mut result = Vec::with_capacity(branches.len());
let mut remaining: Vec<&StackBranch> = branches.iter().collect();
let mut processed: std::collections::HashSet<&str> = std::collections::HashSet::new();
processed.insert(base_branch);
while !remaining.is_empty() {
let prev_len = remaining.len();
remaining.retain(|branch| {
let parent = branch.parent.as_deref().unwrap_or(base_branch);
if processed.contains(parent) {
processed.insert(&branch.name);
result.push(*branch);
false } else {
true }
});
if remaining.len() == prev_len {
result.append(&mut remaining);
break;
}
}
result
}