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,
message: &str,
allow_empty: bool,
amend: bool,
ui: &UI,
) -> Result<git2::Oid> {
let desc = if amend {
format!("amend '{}'", message.lines().next().unwrap_or(""))
} else {
format!("'{}'", message.lines().next().unwrap_or(""))
};
oplog::with_oplog(path, "commit", &desc, || {
execute_inner(path, message, allow_empty, amend, ui)
})
}
fn execute_inner(
path: &Path,
message: &str,
allow_empty: bool,
amend: bool,
ui: &UI,
) -> Result<git2::Oid> {
let repo = crate::ops::open_repo(path)?;
let mut index = repo.index()?;
if amend {
let head_commit = repo
.head()
.ok()
.and_then(|h| h.peel_to_commit().ok())
.ok_or_else(|| anyhow::anyhow!("No commit to amend (empty history)"))?;
let tree_oid = index.write_tree()?;
let tree = repo.find_tree(tree_oid)?;
let sig = repo.signature()?;
let final_msg = if message.is_empty() {
head_commit.message().unwrap_or("(no message)").to_string()
} else {
message.to_string()
};
let parents: Vec<git2::Commit> = head_commit.parents().collect();
let parent_refs: Vec<&git2::Commit> = parents.iter().collect();
let oid = repo.commit(Some("HEAD"), &sig, &sig, &final_msg, &tree, &parent_refs)?;
let sid = short_oid(&oid);
let branch = repo
.head()
.ok()
.and_then(|h| h.shorthand().map(|s| s.to_string()))
.unwrap_or_else(|| "(detached)".to_string());
ui.success(format!(
"[{} {}] {}",
branch,
sid,
final_msg.lines().next().unwrap_or("")
));
return Ok(oid);
}
let head_tree = repo.head().ok().and_then(|h| h.peel_to_tree().ok());
let diff = repo.diff_tree_to_index(head_tree.as_ref(), Some(&index), None)?;
if diff.deltas().count() == 0 && !allow_empty {
bail!("nothing to commit (use --allow-empty to override)");
}
let tree_oid = index.write_tree()?;
let tree = repo.find_tree(tree_oid)?;
let sig = repo.signature()?;
let parent_commit = repo.head().ok().and_then(|h| h.peel_to_commit().ok());
let parents: Vec<&git2::Commit> = parent_commit.iter().collect();
let oid = repo.commit(Some("HEAD"), &sig, &sig, message, &tree, &parents)?;
let sid = short_oid(&oid);
let branch = repo
.head()
.ok()
.and_then(|h| h.shorthand().map(|s| s.to_string()))
.unwrap_or_else(|| "(detached)".to_string());
ui.success(format!(
"[{} {}] {}",
branch,
sid,
message.lines().next().unwrap_or("")
));
let stats = diff.stats()?;
let count = stats.files_changed();
ui.field(
"Changed",
format!("{} file{}", count, if count == 1 { "" } else { "s" }),
);
Ok(oid)
}