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 = (&str, 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, status));
55 }
56 })
57}
58
59#[cfg(test)]
60mod tests {
61
62 use super::*;
63
64 #[test]
65 fn test_parse_git_diff_name_status() {
66 let input = concat!(
67 "M\x00Cargo.lock\x00",
68 "M\x00crates/project/Cargo.toml\x00",
69 "M\x00crates/project/src/buffer_store.rs\x00",
70 "D\x00crates/project/src/git.rs\x00",
71 "A\x00crates/project/src/git_store.rs\x00",
72 "A\x00crates/project/src/git_store/git_traversal.rs\x00",
73 "M\x00crates/project/src/project.rs\x00",
74 "M\x00crates/project/src/worktree_store.rs\x00",
75 "M\x00crates/project_panel/src/project_panel.rs\x00",
76 );
77
78 let output = parse_git_diff_name_status(input).collect::<Vec<_>>();
79 assert_eq!(
80 output,
81 &[
82 ("Cargo.lock", StatusCode::Modified),
83 ("crates/project/Cargo.toml", StatusCode::Modified),
84 ("crates/project/src/buffer_store.rs", StatusCode::Modified),
85 ("crates/project/src/git.rs", StatusCode::Deleted),
86 ("crates/project/src/git_store.rs", StatusCode::Added),
87 (
88 "crates/project/src/git_store/git_traversal.rs",
89 StatusCode::Added,
90 ),
91 ("crates/project/src/project.rs", StatusCode::Modified),
92 ("crates/project/src/worktree_store.rs", StatusCode::Modified),
93 (
94 "crates/project_panel/src/project_panel.rs",
95 StatusCode::Modified
96 ),
97 ]
98 );
99 }
100}