use crate::core::{
find_repo_root, get_current_branch, read_head, write_ref, Commit, Object, ObjectHash, Tree,
};
use crate::response::CommitResponse;
use crate::storage::{Index, ObjectStore};
use std::collections::HashMap;
pub fn execute(
message: Option<String>,
author: Option<String>,
) -> Result<CommitResponse, crate::errors::LitError> {
let repo_root = find_repo_root()?;
let store = ObjectStore::new(&repo_root);
let index = Index::load(&repo_root)?;
let head_hash = read_head(&repo_root)?;
let head_obj = store.read(&ObjectHash::from_hex(head_hash.clone()))?;
let old_commit = match head_obj {
Object::Commit(c) => c,
_ => return Err("HEAD is not a commit".into()),
};
let tree_hash = if index.entries.is_empty() {
old_commit.tree.clone()
} else {
build_tree_from_index(&index, &store)?
};
let msg = message.unwrap_or_else(|| old_commit.message.clone());
let author_name = author.unwrap_or_else(|| old_commit.author.clone());
let parents = old_commit.parents.clone();
let parent_str = parents.first().map(|p| p.to_string());
let commit = Commit::new(tree_hash.clone(), parents, author_name.clone(), msg.clone());
let timestamp = commit.timestamp;
let commit_object = Object::Commit(commit);
let commit_hash = store.write(&commit_object)?;
let branch = get_current_branch(&repo_root).unwrap_or_else(|_| "main".to_string());
write_ref(
&repo_root,
&format!("heads/{}", branch),
commit_hash.as_str(),
)?;
Ok(CommitResponse {
hash: commit_hash.to_string(),
short_hash: commit_hash.short().to_string(),
tree: tree_hash.to_string(),
parent: parent_str,
author: author_name,
message: msg,
timestamp,
})
}
fn build_tree_from_index(
index: &Index,
store: &ObjectStore,
) -> Result<ObjectHash, crate::errors::LitError> {
let mut tree_map: HashMap<String, Vec<(String, String, String)>> = HashMap::new();
for entry in index.sorted_entries() {
let parts: Vec<&str> = entry.path.split('/').collect();
if parts.len() == 1 {
tree_map.entry("".to_string()).or_default().push((
parts[0].to_string(),
entry.hash.clone(),
entry.mode.clone(),
));
} else {
let dir = parts[0].to_string();
tree_map.entry(dir).or_default().push((
parts[1..].join("/"),
entry.hash.clone(),
entry.mode.clone(),
));
}
}
let mut root_tree = Tree::new();
for (dir, files) in &tree_map {
if dir.is_empty() {
for (name, hash, mode) in files {
root_tree.add_entry(
mode.clone(),
name.clone(),
ObjectHash::from_hex(hash.clone()),
"blob".to_string(),
);
}
} else {
let mut sub_tree = Tree::new();
for (name, hash, mode) in files {
sub_tree.add_entry(
mode.clone(),
name.clone(),
ObjectHash::from_hex(hash.clone()),
"blob".to_string(),
);
}
let sub_hash = store.write(&Object::Tree(sub_tree))?;
root_tree.add_entry(
"040000".to_string(),
dir.clone(),
sub_hash,
"tree".to_string(),
);
}
}
Ok(store.write(&Object::Tree(root_tree))?)
}