1use crate::core::validation::Validate;
2use crate::{GitXError, Result};
3use std::process::Command;
4
5pub fn run(commit_hash: String, rebase: bool) -> Result<()> {
6 Validate::commit_hash(&commit_hash)?;
8
9 validate_commit_hash(&commit_hash).map_err(|e| GitXError::GitCommand(e.to_string()))?;
11
12 let has_changes = check_for_changes().map_err(|e| GitXError::GitCommand(e.to_string()))?;
14
15 if !has_changes {
16 return Err(GitXError::GitCommand(
17 "No staged changes found. Please stage your changes first with 'git add'".to_string(),
18 ));
19 }
20
21 let short_hash =
23 get_short_commit_hash(&commit_hash).map_err(|e| GitXError::GitCommand(e.to_string()))?;
24
25 println!("{}", &short_hash);
26
27 create_fixup_commit(&commit_hash).map_err(|e| GitXError::GitCommand(e.to_string()))?;
29
30 let short_hash2 = &short_hash;
31 println!("{short_hash2}");
32
33 if rebase {
35 println!("🔄 Starting interactive rebase with autosquash...");
36 if let Err(msg) = run_autosquash_rebase(&commit_hash) {
37 let msg1 = &msg.to_string();
38 eprintln!("{msg1}");
39 let commit_hash1 = &commit_hash;
40 eprintln!("{commit_hash1}");
41 return Ok(()); }
43 println!("✅ Interactive rebase completed successfully");
44 } else {
45 let commit_hash1 = &commit_hash;
46 println!("{commit_hash1}");
47 }
48 Ok(())
49}
50
51fn validate_commit_hash(commit_hash: &str) -> Result<()> {
52 let output = Command::new("git")
53 .args([
54 "rev-parse",
55 "--verify",
56 &format!("{commit_hash}^{{commit}}"),
57 ])
58 .output()
59 .map_err(GitXError::Io)?;
60
61 if !output.status.success() {
62 return Err(GitXError::GitCommand(
63 "Commit hash does not exist".to_string(),
64 ));
65 }
66
67 Ok(())
68}
69
70fn check_for_changes() -> Result<bool> {
71 let output = Command::new("git")
72 .args(["diff", "--cached", "--quiet"])
73 .status()
74 .map_err(GitXError::Io)?;
75
76 if !output.success() {
78 return Ok(true);
79 }
80
81 let output = Command::new("git")
83 .args(["diff", "--quiet"])
84 .status()
85 .map_err(GitXError::Io)?;
86
87 if !output.success() {
89 return Err(GitXError::GitCommand(
90 "You have unstaged changes. Please stage them first with 'git add'".to_string(),
91 ));
92 }
93
94 Ok(false)
95}
96
97fn get_short_commit_hash(commit_hash: &str) -> Result<String> {
98 let output = Command::new("git")
99 .args(["rev-parse", "--short", commit_hash])
100 .output()
101 .map_err(GitXError::Io)?;
102
103 if !output.status.success() {
104 return Err(GitXError::GitCommand(
105 "Failed to resolve commit hash".to_string(),
106 ));
107 }
108
109 Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
110}
111
112fn create_fixup_commit(commit_hash: &str) -> Result<()> {
113 let status = Command::new("git")
114 .args(["commit", &format!("--fixup={commit_hash}")])
115 .status()
116 .map_err(GitXError::Io)?;
117
118 if !status.success() {
119 return Err(GitXError::GitCommand(
120 "Failed to create fixup commit".to_string(),
121 ));
122 }
123
124 Ok(())
125}
126
127fn run_autosquash_rebase(commit_hash: &str) -> Result<()> {
128 let output = Command::new("git")
130 .args(["rev-parse", &format!("{commit_hash}^")])
131 .output()
132 .map_err(GitXError::Io)?;
133
134 if !output.status.success() {
135 return Err(GitXError::GitCommand(
136 "Cannot rebase - commit has no parent".to_string(),
137 ));
138 }
139
140 let parent_hash_string = String::from_utf8_lossy(&output.stdout);
141 let parent_hash = parent_hash_string.trim();
142
143 let status = Command::new("git")
144 .args(["rebase", "-i", "--autosquash", parent_hash])
145 .status()
146 .map_err(GitXError::Io)?;
147
148 if !status.success() {
149 return Err(GitXError::GitCommand(
150 "Interactive rebase failed".to_string(),
151 ));
152 }
153
154 Ok(())
155}