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