binocular/search/sources/git/
preview.rs1use super::scope::if_empty_then;
2use anyhow::{bail, Context};
3use std::path::Path;
4use std::process::Command;
5
6pub fn read_history_blob(
7 repo_root: &Path,
8 commit: &str,
9 file_path: &Path,
10) -> anyhow::Result<Vec<u8>> {
11 let object = format!("{}:{}", commit, file_path.to_string_lossy());
12 let output = Command::new("git")
13 .arg("show")
14 .arg(&object)
15 .current_dir(repo_root)
16 .output()
17 .with_context(|| format!("failed to read historical blob for {object}"))?;
18
19 if !output.status.success() {
20 let stderr = String::from_utf8_lossy(&output.stderr);
21 bail!(
22 "git show failed for {object}: {}",
23 if_empty_then(stderr.trim(), "unknown git error")
24 );
25 }
26
27 Ok(output.stdout)
28}
29
30pub fn read_branch_preview(repo_root: &Path, branch: &str) -> anyhow::Result<String> {
31 run_git_text(
32 repo_root,
33 &[
34 "log",
35 "--color=always",
36 "--decorate",
37 "--stat",
38 "--max-count=25",
39 branch,
40 ],
41 &format!("failed to read branch preview for {branch}"),
42 )
43}
44
45pub fn read_commit_preview(repo_root: &Path, commit: &str) -> anyhow::Result<String> {
46 run_git_text(
47 repo_root,
48 &[
49 "show",
50 "--color=always",
51 "--stat",
52 "--patch",
53 "--decorate",
54 commit,
55 ],
56 &format!("failed to read commit preview for {commit}"),
57 )
58}
59
60fn run_git_text(repo_root: &Path, args: &[&str], context: &str) -> anyhow::Result<String> {
61 let output = Command::new("git")
62 .args(args)
63 .current_dir(repo_root)
64 .output()
65 .context(context.to_string())?;
66
67 if !output.status.success() {
68 let stderr = String::from_utf8_lossy(&output.stderr);
69 bail!(
70 "{}: {}",
71 context,
72 if_empty_then(stderr.trim(), "unknown git error")
73 );
74 }
75
76 let mut text = String::from_utf8_lossy(&output.stdout).into_owned();
77 if text.trim().is_empty() {
78 text = "No output".to_string();
79 }
80 Ok(text)
81}