git_editor/rewrite/
rewrite_all.rs

1use crate::utils::types::Result;
2use crate::{args::Args, utils::commit_history::get_commit_history};
3use chrono::NaiveDateTime;
4use colored::Colorize;
5use git2::{Repository, Signature, Sort, Time};
6use std::collections::HashMap;
7
8pub fn rewrite_all_commits(args: &Args, timestamps: Vec<NaiveDateTime>) -> Result<()> {
9    let repo = Repository::open(args.repo_path.as_ref().unwrap())?;
10    let head_ref = repo.head()?;
11    let branch_name = head_ref
12        .shorthand()
13        .ok_or("Detached HEAD or invalid branch")?;
14    let full_ref = format!("refs/heads/{branch_name}");
15
16    let mut revwalk = repo.revwalk()?;
17    revwalk.push_head()?;
18    revwalk.set_sorting(Sort::TOPOLOGICAL | Sort::TIME)?;
19    let mut orig_oids: Vec<_> = revwalk.filter_map(|id| id.ok()).collect();
20    orig_oids.reverse();
21
22    let mut new_map: HashMap<git2::Oid, git2::Oid> = HashMap::new();
23    let mut last_new_oid = None;
24
25    for (i, &oid) in orig_oids.iter().enumerate() {
26        let orig = repo.find_commit(oid)?;
27        let tree = orig.tree()?;
28
29        let new_parents: Result<Vec<_>> = orig
30            .parent_ids()
31            .map(|pid| {
32                let new_pid = *new_map.get(&pid).unwrap_or(&pid);
33                repo.find_commit(new_pid).map_err(|e| e.into())
34            })
35            .collect();
36
37        let timestamp: i64 = timestamps[i].and_utc().timestamp();
38        let sig = Signature::new(
39            args.name.as_ref().unwrap(),
40            args.email.as_ref().unwrap(),
41            &Time::new(timestamp, 0),
42        )?;
43
44        let new_oid = repo.commit(
45            None,
46            &sig,
47            &sig,
48            orig.message().unwrap_or_default(),
49            &tree,
50            &new_parents?.iter().collect::<Vec<_>>(),
51        )?;
52
53        new_map.insert(oid, new_oid);
54        last_new_oid = Some(new_oid);
55    }
56
57    if let Some(new_head) = last_new_oid {
58        repo.reference(&full_ref, new_head, true, "history rewritten")?;
59        println!(
60            "{} '{}' -> {}",
61            "Rewritten branch".green(),
62            branch_name.cyan(),
63            new_head.to_string().cyan()
64        );
65        if args.show_history {
66            get_commit_history(args, true)?;
67        }
68    }
69
70    Ok(())
71}