Files
oak-gpui/crates/git/src/commit.rs
T
Kirill Bulatov 16366cf9f2 Use anyhow more idiomatically (#31052)
https://github.com/zed-industries/zed/issues/30972 brought up another
case where our context is not enough to track the actual source of the
issue: we get a general top-level error without inner error.

The reason for this was `.ok_or_else(|| anyhow!("failed to read HEAD
SHA"))?; ` on the top level.

The PR finally reworks the way we use anyhow to reduce such issues (or
at least make it simpler to bubble them up later in a fix).
On top of that, uses a few more anyhow methods for better readability.

* `.ok_or_else(|| anyhow!("..."))`, `map_err` and other similar error
conversion/option reporting cases are replaced with `context` and
`with_context` calls
* in addition to that, various `anyhow!("failed to do ...")` are
stripped with `.context("Doing ...")` messages instead to remove the
parasitic `failed to` text
* `anyhow::ensure!` is used instead of `if ... { return Err(...); }`
calls
* `anyhow::bail!` is used instead of `return Err(anyhow!(...));`

Release Notes:

- N/A
2025-05-20 23:06:07 +00:00

112 lines
3.5 KiB
Rust

use crate::{Oid, status::StatusCode};
use anyhow::{Context as _, Result};
use collections::HashMap;
use std::path::Path;
pub async fn get_messages(working_directory: &Path, shas: &[Oid]) -> Result<HashMap<Oid, String>> {
if shas.is_empty() {
return Ok(HashMap::default());
}
const MARKER: &str = "<MARKER>";
let output = util::command::new_smol_command("git")
.current_dir(working_directory)
.arg("show")
.arg("-s")
.arg(format!("--format=%B{}", MARKER))
.args(shas.iter().map(ToString::to_string))
.output()
.await
.context("starting git blame process")?;
anyhow::ensure!(
output.status.success(),
"'git show' failed with error {:?}",
output.status
);
Ok(shas
.iter()
.cloned()
.zip(
String::from_utf8_lossy(&output.stdout)
.trim()
.split_terminator(MARKER)
.map(|str| str.trim().replace("<", "&lt;").replace(">", "&gt;")),
)
.collect::<HashMap<Oid, String>>())
}
/// Parse the output of `git diff --name-status -z`
pub fn parse_git_diff_name_status(content: &str) -> impl Iterator<Item = (&Path, StatusCode)> {
let mut parts = content.split('\0');
std::iter::from_fn(move || {
loop {
let status_str = parts.next()?;
let path = parts.next()?;
let status = match status_str {
"M" => StatusCode::Modified,
"A" => StatusCode::Added,
"D" => StatusCode::Deleted,
_ => continue,
};
return Some((Path::new(path), status));
}
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_git_diff_name_status() {
let input = concat!(
"MCargo.lock",
"Mcrates/project/Cargo.toml",
"Mcrates/project/src/buffer_store.rs",
"Dcrates/project/src/git.rs",
"Acrates/project/src/git_store.rs",
"Acrates/project/src/git_store/git_traversal.rs",
"Mcrates/project/src/project.rs",
"Mcrates/project/src/worktree_store.rs",
"Mcrates/project_panel/src/project_panel.rs",
);
let output = parse_git_diff_name_status(input).collect::<Vec<_>>();
assert_eq!(
output,
&[
(Path::new("Cargo.lock"), StatusCode::Modified),
(Path::new("crates/project/Cargo.toml"), StatusCode::Modified),
(
Path::new("crates/project/src/buffer_store.rs"),
StatusCode::Modified
),
(Path::new("crates/project/src/git.rs"), StatusCode::Deleted),
(
Path::new("crates/project/src/git_store.rs"),
StatusCode::Added
),
(
Path::new("crates/project/src/git_store/git_traversal.rs"),
StatusCode::Added,
),
(
Path::new("crates/project/src/project.rs"),
StatusCode::Modified
),
(
Path::new("crates/project/src/worktree_store.rs"),
StatusCode::Modified
),
(
Path::new("crates/project_panel/src/project_panel.rs"),
StatusCode::Modified
),
]
);
}
}