Skip to main content

lattice_host/vcs/
baseline.rs

1use std::path::PathBuf;
2
3use lattice_diff::subsystem::DiffParticipantSource;
4use ropey::Rope;
5
6/// A [`DiffParticipantSource`] that reads file content from a specific
7/// git revision (e.g., `"HEAD"`, `"main~3"`) via `git show <rev>:<path>`.
8///
9/// `snapshot()` is called inside `spawn_blocking` by the diff subsystem,
10/// so the blocking I/O of the `git show` child process is acceptable.
11///
12/// Stores the git workdir path rather than a `gix::Repository` handle
13/// because `gix::Repository` is `Send` but not `Sync` (contains `RefCell`s
14/// internally), and `DiffParticipantSource` requires `Send + Sync`.
15#[derive(Clone, Debug)]
16pub struct GitBaseline {
17    workdir: PathBuf,
18    rev: String,
19    rel_path: PathBuf,
20}
21
22impl GitBaseline {
23    /// `workdir` — the repository working tree root.
24    /// `rev` — the git revision (e.g., `"HEAD"`, `"main~3"`).
25    /// `rel_path` — file path relative to `workdir`.
26    pub fn new(
27        workdir: impl Into<PathBuf>,
28        rev: impl Into<String>,
29        rel_path: impl Into<PathBuf>,
30    ) -> Self {
31        Self {
32            workdir: workdir.into(),
33            rev: rev.into(),
34            rel_path: rel_path.into(),
35        }
36    }
37}
38
39impl DiffParticipantSource for GitBaseline {
40    fn snapshot(&self) -> Rope {
41        let spec = format!("{}:{}", self.rev, self.rel_path.display());
42        let result = std::process::Command::new("git")
43            .args(["show", &spec])
44            .current_dir(&self.workdir)
45            .output();
46
47        match result {
48            Ok(output) if output.status.success() => match String::from_utf8(output.stdout) {
49                Ok(text) => Rope::from(text),
50                Err(_) => Rope::new(),
51            },
52            _ => {
53                tracing::debug!(
54                    target: "lattice_host::vcs",
55                    "GitBaseline::snapshot failed: {}:{}",
56                    self.rev,
57                    self.rel_path.display(),
58                );
59                Rope::new()
60            }
61        }
62    }
63}