chronicle/hooks/
post_rewrite.rs1use crate::annotate::squash::{migrate_amend_annotation, AmendMigrationContext};
2use crate::error::chronicle_error::{GitSnafu, JsonSnafu};
3use crate::error::Result;
4use crate::git::GitOps;
5use crate::schema::Annotation;
6use snafu::ResultExt;
7
8#[derive(Debug, Clone)]
10pub struct RewriteMapping {
11 pub old_sha: String,
12 pub new_sha: String,
13}
14
15pub fn handle_post_rewrite(
22 git_ops: &dyn GitOps,
23 rewrite_type: &str,
24 mappings: &[RewriteMapping],
25) -> Result<()> {
26 if rewrite_type != "amend" {
27 tracing::info!(
28 "post-rewrite: {} rewrites not yet supported, skipping {} mappings",
29 rewrite_type,
30 mappings.len()
31 );
32 return Ok(());
33 }
34
35 for mapping in mappings {
36 if let Err(e) = handle_single_amend(git_ops, &mapping.old_sha, &mapping.new_sha) {
37 tracing::warn!(
38 "Failed to migrate annotation from {} to {}: {}",
39 mapping.old_sha,
40 mapping.new_sha,
41 e
42 );
43 }
45 }
46
47 Ok(())
48}
49
50fn handle_single_amend(git_ops: &dyn GitOps, old_sha: &str, new_sha: &str) -> Result<()> {
52 let old_note = git_ops.note_read(old_sha).context(GitSnafu)?;
54 let old_json = match old_note {
55 Some(json) => json,
56 None => {
57 tracing::debug!("No annotation for old commit {old_sha}, skipping amend migration");
58 return Ok(());
59 }
60 };
61
62 let old_annotation: Annotation = serde_json::from_str(&old_json).context(JsonSnafu)?;
63
64 let new_info = git_ops.commit_info(new_sha).context(GitSnafu)?;
66
67 let new_diffs = git_ops.diff(new_sha).context(GitSnafu)?;
70 let old_diffs = git_ops.diff(old_sha).context(GitSnafu)?;
71
72 let new_diff_text = format!("{:?}", new_diffs);
74 let old_diff_text = format!("{:?}", old_diffs);
75 let diff_for_migration = if new_diff_text == old_diff_text {
76 String::new() } else {
78 new_diff_text
79 };
80
81 let ctx = AmendMigrationContext {
82 new_commit: new_sha.to_string(),
83 new_diff: diff_for_migration,
84 old_annotation,
85 new_message: new_info.message,
86 };
87
88 let new_annotation = migrate_amend_annotation(&ctx);
89
90 let json = serde_json::to_string_pretty(&new_annotation).context(JsonSnafu)?;
91 git_ops.note_write(new_sha, &json).context(GitSnafu)?;
92
93 tracing::info!("Migrated annotation from {old_sha} to {new_sha}");
94 Ok(())
95}
96
97pub fn parse_rewrite_mappings(input: &str) -> Vec<RewriteMapping> {
101 input
102 .lines()
103 .filter_map(|line| {
104 let parts: Vec<&str> = line.split_whitespace().collect();
105 if parts.len() >= 2 {
106 Some(RewriteMapping {
107 old_sha: parts[0].to_string(),
108 new_sha: parts[1].to_string(),
109 })
110 } else {
111 None
112 }
113 })
114 .collect()
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120
121 #[test]
122 fn test_parse_rewrite_mappings_single() {
123 let input = "abc123 def456\n";
124 let mappings = parse_rewrite_mappings(input);
125 assert_eq!(mappings.len(), 1);
126 assert_eq!(mappings[0].old_sha, "abc123");
127 assert_eq!(mappings[0].new_sha, "def456");
128 }
129
130 #[test]
131 fn test_parse_rewrite_mappings_multiple() {
132 let input = "abc123 def456\nghi789 jkl012\nmno345 pqr678\n";
133 let mappings = parse_rewrite_mappings(input);
134 assert_eq!(mappings.len(), 3);
135 assert_eq!(mappings[0].old_sha, "abc123");
136 assert_eq!(mappings[0].new_sha, "def456");
137 assert_eq!(mappings[1].old_sha, "ghi789");
138 assert_eq!(mappings[1].new_sha, "jkl012");
139 assert_eq!(mappings[2].old_sha, "mno345");
140 assert_eq!(mappings[2].new_sha, "pqr678");
141 }
142
143 #[test]
144 fn test_parse_rewrite_mappings_empty() {
145 let input = "";
146 let mappings = parse_rewrite_mappings(input);
147 assert!(mappings.is_empty());
148 }
149
150 #[test]
151 fn test_parse_rewrite_mappings_blank_lines() {
152 let input = "abc123 def456\n\nghi789 jkl012\n";
153 let mappings = parse_rewrite_mappings(input);
154 assert_eq!(mappings.len(), 2);
155 }
156
157 #[test]
158 fn test_parse_rewrite_mappings_extra_fields() {
159 let input = "abc123 def456 extra info\n";
161 let mappings = parse_rewrite_mappings(input);
162 assert_eq!(mappings.len(), 1);
163 assert_eq!(mappings[0].old_sha, "abc123");
164 assert_eq!(mappings[0].new_sha, "def456");
165 }
166
167 #[test]
168 fn test_parse_rewrite_mappings_malformed_line() {
169 let input = "only_one_sha\nabc123 def456\n";
170 let mappings = parse_rewrite_mappings(input);
171 assert_eq!(mappings.len(), 1);
172 assert_eq!(mappings[0].old_sha, "abc123");
173 }
174}