use crate::commit_pretty::format_reference_line;
use crate::error::Result;
use crate::objects::{CommitData, ObjectId};
use crate::repo::Repository;
use crate::rev_list::{resolve_revision_specs_to_commits, rev_list, OrderingMode, RevListOptions};
use crate::rev_parse::resolve_revision_as_commit;
use crate::revwalk::parse_rev_walk_tokens;
pub fn commits_to_revert(repo: &Repository, specs: &[String]) -> Result<Vec<ObjectId>> {
let tokens: Vec<&str> = specs.iter().map(String::as_str).collect();
let parsed = parse_rev_walk_tokens(repo, &tokens)?;
let include = resolve_revision_specs_to_commits(repo, &parsed.positive)?;
let exclude = resolve_revision_specs_to_commits(repo, &parsed.negative)?;
let range_syntax = tokens
.iter()
.any(|t| t.contains("..") || t.starts_with('^'))
|| !exclude.is_empty()
|| parsed.symmetric_left.is_some();
if range_syntax {
return revision_range_apply_order(repo, &include, &exclude);
}
tokens
.iter()
.map(|spec| resolve_revision_as_commit(repo, spec))
.collect()
}
pub fn revision_range_apply_order(
repo: &Repository,
include: &[ObjectId],
exclude: &[ObjectId],
) -> Result<Vec<ObjectId>> {
let positive: Vec<String> = include.iter().map(ObjectId::to_hex).collect();
let negative: Vec<String> = exclude.iter().map(ObjectId::to_hex).collect();
let opts = RevListOptions {
ordering: OrderingMode::Topo,
..RevListOptions::default()
};
Ok(rev_list(repo, &positive, &negative, &opts)?.commits)
}
#[must_use]
pub fn merge_commit_message_for_revert(
commit: &CommitData,
commit_oid: ObjectId,
use_reference: bool,
comment_char: char,
mainline_parent: Option<ObjectId>,
) -> (String, String) {
let subject_line = commit.message.lines().next().unwrap_or("");
let oid_full = commit_oid.to_hex();
if use_reference {
let title = format!("{comment_char} *** SAY WHY WE ARE REVERTING ON THE TITLE LINE ***");
let ref_line = format_reference_line(&commit_oid, subject_line, &commit.committer, 7);
let body = format!("This reverts commit {ref_line}.\n\n");
return (title, body);
}
let body = if let Some(parent) = mainline_parent {
format!(
"This reverts merge commit {oid_full} (mainline parent {parent}).\n",
parent = parent.to_hex()
)
} else {
format!("This reverts commit {oid_full}.\n")
};
if let Some(rest) = subject_line.strip_prefix("Revert \"") {
if let Some(orig) = rest.strip_suffix('"') {
if !orig.starts_with("Revert \"") {
let title = format!("Reapply \"{orig}\"\n");
return (title, body);
}
}
}
let title = format!("Revert \"{subject_line}\"\n");
(title, body)
}
#[must_use]
pub fn revert_mainline_extra_body(_reverted: ObjectId, mainline_parent: ObjectId) -> String {
format!(
"Mainline parent {} was used when building the revert tree.\n",
mainline_parent.to_hex()
)
}
#[must_use]
pub fn merge_mainline_revert_note(source_oid: ObjectId, mainline_parent: ObjectId) -> String {
format!(
"Reverted merge {} using mainline parent {}.\n\n",
source_oid.to_hex(),
mainline_parent.to_hex()
)
}