sandlot/rust-sandlot/src/commands/rebase.rs
2026-04-10 11:13:00 -07:00

99 lines
2.8 KiB
Rust

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(())
}