use anyhow::Result; use crate::git; use crate::spinner::Spinner; use crate::vm; use super::helpers::{require_session, resolve_conflicts}; const MAX_REBASE_ROUNDS: usize = 10; pub async fn action(branch: &str) -> Result<()> { let (root, session) = require_session(branch).await; let worktree = &session.worktree; if git::is_dirty(worktree).await { crate::fmt::die(&format!( "Branch \"{branch}\" has unsaved changes. Run \"sandlot save {branch}\" first." )); } let main = git::main_branch(Some(&root)).await?; let fetch_spin = Spinner::new("Fetching origin", Some(branch)); // Fetch origin main let _ = tokio::process::Command::new("git") .args(["-C", &root, "fetch", "origin", &main]) .stdout(std::process::Stdio::null()) .stderr(std::process::Stdio::null()) .output() .await; fetch_spin.set_text(&format!("Rebasing onto origin/{main}")); let onto = format!("origin/{main}"); let mut conflicts = match git::rebase(&onto, worktree).await { Ok(c) => c, Err(e) => { fetch_spin.fail(&e.to_string()); std::process::exit(1); } }; if conflicts.is_empty() { fetch_spin.succeed(&format!("Rebased {branch} onto {main}")); return Ok(()); } fetch_spin.stop(); println!( "\u{25C6} Rebase conflicts in {} file(s). Resolving with Claude...", conflicts.len() ); let resolve_spin = Spinner::new("Starting container", Some(branch)); let result: Result<()> = async { vm::ensure(&|msg| resolve_spin.set_text(msg)).await?; vm::set_activity(worktree, branch).await; let mut round = 1usize; while !conflicts.is_empty() { if round > MAX_REBASE_ROUNDS { anyhow::bail!( "Exceeded {MAX_REBASE_ROUNDS} conflict resolution rounds \u{2014} aborting rebase" ); } resolve_conflicts(&conflicts, worktree, &|file, i, total| { if total > 1 { resolve_spin .set_text(&format!("({i}/{total}) Resolving {file} (round {round})")); } else { resolve_spin.set_text(&format!("Resolving {file} (round {round})")); } }) .await?; conflicts = git::rebase_continue(worktree).await?; round += 1; } resolve_spin.succeed(&format!( "Rebased {branch} onto {main} (resolved {} conflict round(s))", round - 1 )); Ok(()) } .await; if let Err(e) = result { resolve_spin.fail(&e.to_string()); git::rebase_abort(worktree).await; std::process::exit(1); } vm::clear_activity(worktree, branch).await; Ok(()) }