1use crate::core::{
2 find_repo_root, get_current_branch, read_head, write_ref, Commit, Object, ObjectHash,
3};
4use crate::response::CommitResponse;
5use crate::storage::ObjectStore;
6
7pub fn execute(
9 count: usize,
10 message: Option<String>,
11) -> Result<CommitResponse, crate::errors::LitError> {
12 if count < 2 {
13 return Err("Squash requires at least 2 commits".into());
14 }
15
16 let repo_root = find_repo_root()?;
17 let store = ObjectStore::new(&repo_root);
18
19 let head_hash = read_head(&repo_root)?;
20
21 let mut current_hash = head_hash.clone();
23 let mut messages: Vec<String> = Vec::new();
24 let mut final_tree = String::new();
25 let mut final_author = String::new();
26
27 for i in 0..count {
28 let obj = store.read(&ObjectHash::from_hex(current_hash.clone()))?;
29 match obj {
30 Object::Commit(c) => {
31 if i == 0 {
32 final_tree = c.tree.to_string();
33 final_author = c.author.clone();
34 }
35 messages.push(c.message.clone());
36 if let Some(parent) = c.parents.first() {
37 current_hash = parent.to_string();
38 } else if i < count - 1 {
39 return Err(format!(
40 "Only {} commits available, cannot squash {}",
41 i + 1,
42 count
43 )
44 .into());
45 }
46 }
47 _ => return Err("Unexpected non-commit object in history".into()),
48 }
49 }
50
51 messages.reverse();
52
53 let base_obj = store.read(&ObjectHash::from_hex(current_hash.clone()))?;
55 let parents = match base_obj {
56 Object::Commit(_) => vec![crate::core::ObjectHash::from_hex(current_hash.clone())],
57 _ => vec![],
58 };
59 let parent_str = Some(current_hash);
60
61 let squash_message = message.unwrap_or_else(|| {
62 let mut msg = String::from("Squashed commits:\n\n");
63 for m in &messages {
64 msg.push_str(&format!("* {}\n", m));
65 }
66 msg
67 });
68
69 let tree_hash = crate::core::ObjectHash::from_hex(final_tree.clone());
70 let commit = Commit::new(
71 tree_hash,
72 parents,
73 final_author.clone(),
74 squash_message.clone(),
75 );
76 let timestamp = commit.timestamp;
77 let commit_object = Object::Commit(commit);
78 let commit_hash = store.write(&commit_object)?;
79
80 let branch = get_current_branch(&repo_root).unwrap_or_else(|_| "main".to_string());
81 write_ref(
82 &repo_root,
83 &format!("heads/{}", branch),
84 commit_hash.as_str(),
85 )?;
86
87 Ok(CommitResponse {
88 hash: commit_hash.to_string(),
89 short_hash: commit_hash.short().to_string(),
90 tree: final_tree,
91 parent: parent_str,
92 author: final_author,
93 message: squash_message,
94 timestamp,
95 })
96}