1use crate::{Oid, status::StatusCode};
2use anyhow::{Context as _, Result};
3use collections::HashMap;
4use std::path::Path;
5
6pub async fn get_messages(working_directory: &Path, shas: &[Oid]) -> Result<HashMap<Oid, String>> {
7 if shas.is_empty() {
8 return Ok(HashMap::default());
9 }
10
11 const MARKER: &str = "<MARKER>";
12
13 let output = util::command::new_smol_command("git")
14 .current_dir(working_directory)
15 .arg("show")
16 .arg("-s")
17 .arg(format!("--format=%B{}", MARKER))
18 .args(shas.iter().map(ToString::to_string))
19 .output()
20 .await
21 .context("starting git blame process")?;
22
23 anyhow::ensure!(
24 output.status.success(),
25 "'git show' failed with error {:?}",
26 output.status
27 );
28
29 Ok(shas
30 .iter()
31 .cloned()
32 .zip(
33 String::from_utf8_lossy(&output.stdout)
34 .trim()
35 .split_terminator(MARKER)
36 .map(|str| str.trim().replace("<", "<").replace(">", ">")),
37 )
38 .collect::<HashMap<Oid, String>>())
39}
40
41/// Parse the output of `git diff --name-status -z`
42pub fn parse_git_diff_name_status(content: &str) -> impl Iterator<Item = (&Path, StatusCode)> {
43 let mut parts = content.split('\0');
44 std::iter::from_fn(move || {
45 loop {
46 let status_str = parts.next()?;
47 let path = parts.next()?;
48 let status = match status_str {
49 "M" => StatusCode::Modified,
50 "A" => StatusCode::Added,
51 "D" => StatusCode::Deleted,
52 _ => continue,
53 };
54 return Some((Path::new(path), status));
55 }
56 })
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62
63 #[test]
64 fn test_parse_git_diff_name_status() {
65 let input = concat!(
66 "M\x00Cargo.lock\x00",
67 "M\x00crates/project/Cargo.toml\x00",
68 "M\x00crates/project/src/buffer_store.rs\x00",
69 "D\x00crates/project/src/git.rs\x00",
70 "A\x00crates/project/src/git_store.rs\x00",
71 "A\x00crates/project/src/git_store/git_traversal.rs\x00",
72 "M\x00crates/project/src/project.rs\x00",
73 "M\x00crates/project/src/worktree_store.rs\x00",
74 "M\x00crates/project_panel/src/project_panel.rs\x00",
75 );
76
77 let output = parse_git_diff_name_status(input).collect::<Vec<_>>();
78 assert_eq!(
79 output,
80 &[
81 (Path::new("Cargo.lock"), StatusCode::Modified),
82 (Path::new("crates/project/Cargo.toml"), StatusCode::Modified),
83 (
84 Path::new("crates/project/src/buffer_store.rs"),
85 StatusCode::Modified
86 ),
87 (Path::new("crates/project/src/git.rs"), StatusCode::Deleted),
88 (
89 Path::new("crates/project/src/git_store.rs"),
90 StatusCode::Added
91 ),
92 (
93 Path::new("crates/project/src/git_store/git_traversal.rs"),
94 StatusCode::Added,
95 ),
96 (
97 Path::new("crates/project/src/project.rs"),
98 StatusCode::Modified
99 ),
100 (
101 Path::new("crates/project/src/worktree_store.rs"),
102 StatusCode::Modified
103 ),
104 (
105 Path::new("crates/project_panel/src/project_panel.rs"),
106 StatusCode::Modified
107 ),
108 ]
109 );
110 }
111}