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_revert: bool,
skip: bool,
no_commit: bool,
ui: &UI,
) -> Result<()> {
let desc = if abort {
"abort revert".to_string()
} else if continue_revert {
"continue revert".to_string()
} else if skip {
"skip revert".to_string()
} else {
format!("revert {}", commit_ref)
};
oplog::with_oplog(path, "revert", &desc, || {
execute_inner(
path,
commit_ref,
abort,
continue_revert,
skip,
no_commit,
ui,
)
})
}
fn execute_inner(
path: &Path,
commit_ref: &str,
abort: bool,
continue_revert: bool,
skip: bool,
no_commit: bool,
ui: &UI,
) -> Result<()> {
let repo = crate::ops::open_repo(path)?;
if abort {
if repo.state() != git2::RepositoryState::RevertSequence
&& repo.state() != git2::RepositoryState::Revert
{
bail!("No revert 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("Revert aborted");
return Ok(());
}
if skip {
if repo.state() != git2::RepositoryState::RevertSequence
&& repo.state() != git2::RepositoryState::Revert
{
bail!("No revert 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("Revert skipped");
return Ok(());
}
if continue_revert {
if repo.state() != git2::RepositoryState::RevertSequence
&& repo.state() != git2::RepositoryState::Revert
{
bail!("No revert 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,
"revert (continued)",
&tree,
&[&head_commit],
)?;
repo.cleanup_state()?;
ui.success("Revert continued and committed");
return Ok(());
}
let obj = repo.revparse_single(commit_ref)?;
let commit = obj.peel_to_commit()?;
repo.revert(&commit, None)?;
let index = repo.index()?;
if index.has_conflicts() {
let hash = short_oid(&commit.id());
let _ = crate::ops::conflicts::record_conflicts(path, &hash, "revert", commit_ref);
ui.warning(format!("Revert of {} had conflicts", hash));
ui.info("Resolve conflicts, then run: securegit revert --continue");
ui.info("Or abort with: securegit revert --abort");
return Ok(());
}
if no_commit {
repo.cleanup_state()?;
let hash = short_oid(&commit.id());
ui.success(format!(
"Revert of {} applied to index (not committed)",
hash
));
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 = format!(
"Revert \"{}\"\n\nThis reverts commit {}.",
commit.summary().unwrap_or(""),
commit.id()
);
match repo.commit(Some("HEAD"), &sig, &sig, &msg, &tree, &[&head_commit]) {
Ok(_) => {
repo.cleanup_state()?;
let hash = short_oid(&commit.id());
ui.success(format!(
"Reverted [{}] \"{}\"",
hash,
commit.summary().unwrap_or("")
));
}
Err(e) => {
let _ = repo.cleanup_state();
bail!("Revert commit failed: {}", e);
}
}
Ok(())
}