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