use crate::cli::UI;
use crate::ops::oplog;
use crate::ops::utils::short_oid;
use anyhow::Result;
use std::path::Path;
pub fn execute(
path: &Path,
upstream: Option<&str>,
abort: bool,
continue_rebase: bool,
skip: bool,
onto: Option<&str>,
ui: &UI,
) -> Result<()> {
let desc = if abort {
"abort rebase".to_string()
} else if continue_rebase {
"continue rebase".to_string()
} else if skip {
"skip rebase step".to_string()
} else {
format!("rebase onto '{}'", upstream.unwrap_or("?"))
};
oplog::with_oplog(path, "rebase", &desc, || {
execute_inner(path, upstream, abort, continue_rebase, skip, onto, ui)
})
}
fn execute_inner(
path: &Path,
upstream: Option<&str>,
abort: bool,
continue_rebase: bool,
skip: bool,
onto: Option<&str>,
ui: &UI,
) -> Result<()> {
let repo = crate::ops::open_repo(path)?;
if abort {
let mut rebase = repo.open_rebase(None)?;
rebase.abort()?;
ui.success("Rebase aborted");
return Ok(());
}
if skip {
let sig = repo.signature()?;
let mut rebase = repo.open_rebase(None)?;
while let Some(_) = rebase.next() {
let index = repo.index()?;
if index.has_conflicts() {
let _ = crate::ops::conflicts::record_conflicts(
path,
"rebase-step",
"rebase",
"continue",
);
ui.warning("Conflict during rebase. Resolve and run: securegit rebase --continue");
return Ok(());
}
rebase.commit(None, &sig, None)?;
}
rebase.finish(None)?;
ui.success("Successfully rebased and updated HEAD");
return Ok(());
}
if continue_rebase {
let sig = repo.signature()?;
let mut rebase = repo.open_rebase(None)?;
rebase.commit(None, &sig, None)?;
while rebase.next().is_some() {
let index = repo.index()?;
if index.has_conflicts() {
let _ = crate::ops::conflicts::record_conflicts(
path,
"rebase-step",
"rebase",
"continue",
);
ui.warning("Conflict during rebase. Resolve and run: securegit rebase --continue");
return Ok(());
}
rebase.commit(None, &sig, None)?;
}
rebase.finish(None)?;
ui.success("Successfully rebased and updated HEAD");
return Ok(());
}
let upstream_name = upstream.ok_or_else(|| {
anyhow::anyhow!("Usage: securegit rebase <upstream>\n e.g. securegit rebase main")
})?;
let upstream_obj = repo.revparse_single(upstream_name)?;
let upstream_commit = upstream_obj.peel_to_commit()?;
let upstream_annotated = repo.find_annotated_commit(upstream_commit.id())?;
let onto_annotated = if let Some(onto_ref) = onto {
let onto_obj = repo.revparse_single(onto_ref)?;
let onto_commit = onto_obj.peel_to_commit()?;
Some(repo.find_annotated_commit(onto_commit.id())?)
} else {
None
};
let head = repo.head()?;
let head_name = head.shorthand().unwrap_or("HEAD");
let target_id = onto_annotated
.as_ref()
.map(|a| a.id())
.unwrap_or(upstream_commit.id());
if let Ok(merge_base) = repo.merge_base(target_id, head.target().unwrap_or(target_id)) {
if merge_base == head.target().unwrap_or(target_id) {
ui.success(format!("Current branch {} is up to date", head_name));
return Ok(());
}
}
let mut rebase = repo.rebase(
None,
Some(&upstream_annotated),
onto_annotated.as_ref(),
None,
)?;
let sig = repo.signature()?;
let mut count = 0;
while rebase.next().is_some() {
let index = repo.index()?;
if index.has_conflicts() {
let short_id = short_oid(&upstream_commit.id());
let _ =
crate::ops::conflicts::record_conflicts(path, &short_id, "rebase", upstream_name);
ui.warning("Conflict during rebase. Resolve and run: securegit rebase --continue");
ui.info("Or abort with: securegit rebase --abort");
return Ok(());
}
rebase.commit(None, &sig, None)?;
count += 1;
}
rebase.finish(None)?;
if count == 0 {
ui.success(format!("Current branch {} is up to date", head_name));
} else {
let target_name = onto.unwrap_or(upstream_name);
ui.success(format!(
"Successfully rebased {} commit{} onto '{}'",
count,
if count == 1 { "" } else { "s" },
target_name
));
}
Ok(())
}