Skip to main content

fluidattacks_core_git/
rebase.rs

1use anyhow::Result;
2use std::path::Path;
3
4use super::types::RebaseResult;
5use crate::cli;
6
7pub async fn rebase(
8    repo: &Path,
9    file: &Path,
10    line: usize,
11    rev_a: &str,
12    rev_b: &str,
13) -> Result<Option<RebaseResult>> {
14    let Some(blame) = cli::blame_reverse(repo, file, line, rev_a, rev_b, true).await? else {
15        return Ok(None);
16    };
17
18    let path = if blame.path.as_os_str().is_empty() {
19        file.to_path_buf()
20    } else {
21        blame.path
22    };
23
24    Ok(Some(RebaseResult {
25        path,
26        line: blame.line,
27        rev: blame.rev,
28    }))
29}
30
31#[cfg(test)]
32mod tests {
33    use super::rebase;
34    use std::path::{Path, PathBuf};
35
36    fn run(repo: &Path, args: &[&str]) -> String {
37        let output = std::process::Command::new("git")
38            .arg("-C")
39            .arg(repo)
40            .arg("-c")
41            .arg("user.email=dev@fluidattacks.com")
42            .arg("-c")
43            .arg("user.name=dev")
44            .args(args)
45            .output()
46            .unwrap();
47        assert!(output.status.success(), "git {args:?} failed");
48        String::from_utf8(output.stdout).unwrap().trim().to_owned()
49    }
50
51    #[tokio::test]
52    async fn a_line_that_moved_to_a_renamed_file_is_reported_at_its_new_home() {
53        let dir = tempfile::tempdir().unwrap();
54        std::env::set_var("GIT_CONFIG_GLOBAL", dir.path().join("gitconfig"));
55
56        let repo = dir.path().join("repo");
57        std::fs::create_dir_all(&repo).unwrap();
58        run(&repo, &["init", "-q"]);
59        std::fs::write(repo.join("old.txt"), b"first\nsecond\nthird\n").unwrap();
60        run(&repo, &["add", "-A"]);
61        run(&repo, &["commit", "-q", "-m", "add"]);
62        let rev_a = run(&repo, &["rev-parse", "HEAD"]);
63
64        run(&repo, &["mv", "old.txt", "new.txt"]);
65        run(&repo, &["commit", "-q", "-m", "rename"]);
66        let head = run(&repo, &["rev-parse", "HEAD"]);
67
68        let result = rebase(&repo, Path::new("old.txt"), 2, &rev_a, "HEAD")
69            .await
70            .unwrap()
71            .unwrap();
72
73        assert_eq!(result.path, PathBuf::from("new.txt"));
74        assert_eq!(result.line, 2);
75        assert_eq!(result.rev, head);
76    }
77}