gnostr_asyncgit/sync/
reword.rs1use git2::{Oid, RebaseOptions, Repository};
2
3use super::{
4 commit::signature_allow_undefined_name,
5 repo,
6 utils::{bytes2string, get_head_refname, get_head_repo},
7 CommitId, RepoPath,
8};
9use crate::error::{Error, Result};
10
11pub fn reword(repo_path: &RepoPath, commit: CommitId, message: &str) -> Result<CommitId> {
14 let repo = repo(repo_path)?;
15 let config = repo.config()?;
16
17 if config.get_bool("commit.gpgsign").unwrap_or(false) {
18 use crate::sync::utils::undo_last_commit;
20
21 let head = get_head_repo(&repo)?;
22 if head == commit {
23 let parent = repo.find_commit(head.into())?;
25 let tree = parent.tree()?;
26 if repo
27 .diff_tree_to_index(Some(&tree), None, None)?
28 .deltas()
29 .len()
30 == 0
31 {
32 undo_last_commit(repo_path)?;
33 return super::commit(repo_path, message);
34 }
35
36 return Err(Error::SignRewordLastCommitStaged);
37 }
38
39 return Err(Error::SignRewordNonLastCommit);
40 }
41
42 let cur_branch_ref = get_head_refname(&repo)?;
43
44 match reword_internal(&repo, commit.get_oid(), message) {
45 Ok(id) => Ok(id.into()),
46 Err(e) => {
49 if let Ok(mut rebase) = repo.open_rebase(None) {
50 rebase.abort()?;
51 repo.set_head(&cur_branch_ref)?;
52 repo.checkout_head(None)?;
53 }
54 Err(e)
55 }
56 }
57}
58
59fn get_current_branch(repo: &Repository) -> Result<Option<git2::Branch<'_>>> {
63 for b in repo.branches(None)? {
64 let branch = b?.0;
65 if branch.is_head() {
66 return Ok(Some(branch));
67 }
68 }
69 Ok(None)
70}
71
72fn reword_internal(repo: &Repository, commit: Oid, message: &str) -> Result<Oid> {
82 let sig = signature_allow_undefined_name(repo)?;
83
84 let parent_commit_oid = repo
85 .find_commit(commit)?
86 .parent(0)
87 .map_or(None, |parent_commit| Some(parent_commit.id()));
88
89 let commit_to_change = if let Some(pc_oid) = parent_commit_oid {
90 repo.find_annotated_commit(pc_oid)?
94 } else {
95 return Err(Error::NoParent);
96 };
97
98 if let Ok(Some(branch)) = get_current_branch(repo) {
100 let cur_branch_ref = bytes2string(branch.get().name_bytes())?;
101 let cur_branch_name = bytes2string(branch.name_bytes()?)?;
102 let top_branch_commit = repo.find_annotated_commit(branch.get().peel_to_commit()?.id())?;
103
104 let mut rebase = repo.rebase(
105 Some(&top_branch_commit),
106 Some(&commit_to_change),
107 None,
108 Some(&mut RebaseOptions::default()),
109 )?;
110
111 let mut target;
112
113 rebase.next();
114 if parent_commit_oid.is_none() {
115 return Err(Error::NoParent);
116 }
117 target = rebase.commit(None, &sig, Some(message))?;
118 let reworded_commit = target;
119
120 while rebase.next().is_some() {
123 target = rebase.commit(None, &sig, None)?;
124 }
125 rebase.finish(None)?;
126
127 repo.branch(&cur_branch_name, &repo.find_commit(target)?, true)?;
129
130 repo.set_head(&cur_branch_ref)?;
132 repo.checkout_head(None)?;
133 return Ok(reworded_commit);
134 }
135 Err(Error::NoBranch)
137}
138
139#[cfg(test)]
140mod tests {
141 use pretty_assertions::assert_eq;
142
143 use super::*;
144 use crate::sync::{
145 get_commit_info,
146 tests::{repo_init_empty, write_commit_file},
147 };
148
149 #[test]
150 fn test_reword() {
151 let (_td, repo) = repo_init_empty().unwrap();
152 let root = repo.path().parent().unwrap();
153
154 let repo_path: &RepoPath = &root.as_os_str().to_str().unwrap().into();
155
156 write_commit_file(&repo, "foo", "a", "commit1");
157
158 let oid2 = write_commit_file(&repo, "foo", "ab", "commit2");
159
160 let branch = repo.branches(None).unwrap().next().unwrap().unwrap().0;
161 let branch_ref = branch.get();
162 let commit_ref = branch_ref.peel_to_commit().unwrap();
163 let message = commit_ref.message().unwrap();
164
165 assert_eq!(message, "commit2");
166
167 let reworded = reword(repo_path, oid2.into(), "NewCommitMessage").unwrap();
168
169 let branch = repo.branches(None).unwrap().next().unwrap().unwrap().0;
171 let branch_ref = branch.get();
172 let commit_ref_new = branch_ref.peel_to_commit().unwrap();
173 let message_new = commit_ref_new.message().unwrap();
174 assert_eq!(message_new, "NewCommitMessage");
175
176 assert_eq!(
177 message_new,
178 get_commit_info(repo_path, &reworded).unwrap().message
179 );
180 }
181}