use crate::cli::UI;
use crate::ops::oplog;
use crate::ops::utils::short_oid;
use anyhow::{bail, Result};
use std::path::Path;
pub fn execute(
path: &Path,
commit_ref: &str,
abort: bool,
continue_pick: bool,
skip: bool,
no_commit: bool,
ui: &UI,
) -> Result<()> {
let desc = if abort {
"abort cherry-pick".to_string()
} else if continue_pick {
"continue cherry-pick".to_string()
} else if skip {
"skip cherry-pick".to_string()
} else {
format!("cherry-pick {}", commit_ref)
};
oplog::with_oplog(path, "cherry-pick", &desc, || {
execute_inner(path, commit_ref, abort, continue_pick, skip, no_commit, ui)
})
}
fn execute_inner(
path: &Path,
commit_ref: &str,
abort: bool,
continue_pick: bool,
skip: bool,
no_commit: bool,
ui: &UI,
) -> Result<()> {
let repo = crate::ops::open_repo(path)?;
if abort {
if repo.state() != git2::RepositoryState::CherryPickSequence
&& repo.state() != git2::RepositoryState::CherryPick
{
bail!("No cherry-pick in progress to abort.");
}
repo.cleanup_state()?;
let head = repo.head()?.peel_to_commit()?;
repo.reset(head.as_object(), git2::ResetType::Hard, None)?;
ui.success("Cherry-pick aborted");
return Ok(());
}
if skip {
if repo.state() != git2::RepositoryState::CherryPickSequence
&& repo.state() != git2::RepositoryState::CherryPick
{
bail!("No cherry-pick in progress to skip.");
}
let head = repo.head()?.peel_to_commit()?;
repo.reset(head.as_object(), git2::ResetType::Hard, None)?;
repo.cleanup_state()?;
ui.success("Cherry-pick skipped");
return Ok(());
}
if continue_pick {
if repo.state() != git2::RepositoryState::CherryPickSequence
&& repo.state() != git2::RepositoryState::CherryPick
{
bail!("No cherry-pick in progress to continue.");
}
let index = repo.index()?;
if index.has_conflicts() {
bail!("Conflicts still exist. Resolve them before continuing.");
}
let mut index = repo.index()?;
let tree_oid = index.write_tree()?;
let tree = repo.find_tree(tree_oid)?;
let sig = repo.signature().map_err(|_| {
anyhow::anyhow!(
"Author identity not configured. Run:\n \
git config user.name \"Your Name\"\n \
git config user.email \"you@example.com\""
)
})?;
let head_commit = repo.head()?.peel_to_commit()?;
repo.commit(
Some("HEAD"),
&sig,
&sig,
"cherry-pick (continued)",
&tree,
&[&head_commit],
)?;
repo.cleanup_state()?;
ui.success("Cherry-pick continued and committed");
return Ok(());
}
let obj = repo.revparse_single(commit_ref)?;
let commit = obj.peel_to_commit()?;
repo.cherrypick(&commit, None)?;
let index = repo.index()?;
if index.has_conflicts() {
let short_id = short_oid(&commit.id());
let _ = crate::ops::conflicts::record_conflicts(path, &short_id, "cherry-pick", commit_ref);
ui.warning(format!("Cherry-pick of {} had conflicts", short_id));
ui.info("Resolve conflicts, then run: securegit cherry-pick --continue");
ui.info("Or abort with: securegit cherry-pick --abort");
return Ok(());
}
if no_commit {
repo.cleanup_state()?;
let short_id = short_oid(&commit.id());
ui.success(format!(
"Cherry-pick {} applied to index (not committed)",
short_id
));
return Ok(());
}
let mut index = repo.index()?;
let tree_oid = index.write_tree()?;
let tree = repo.find_tree(tree_oid)?;
let sig = repo.signature().map_err(|_| {
anyhow::anyhow!(
"Author identity not configured. Run:\n \
git config user.name \"Your Name\"\n \
git config user.email \"you@example.com\""
)
})?;
let head_commit = repo.head()?.peel_to_commit()?;
let msg = commit.message().unwrap_or("cherry-pick");
match repo.commit(Some("HEAD"), &sig, &sig, msg, &tree, &[&head_commit]) {
Ok(_) => {
repo.cleanup_state()?;
let short_id = short_oid(&commit.id());
ui.success(format!(
"[cherry-pick {}] {}",
short_id,
commit.summary().unwrap_or("")
));
}
Err(e) => {
let _ = repo.cleanup_state();
bail!("Cherry-pick commit failed: {}", e);
}
}
Ok(())
}