Skip to main content

git_psect/
repo.rs

1use std::{env, path::PathBuf, process::Command};
2
3use git2::Repository;
4
5use crate::error::Error;
6
7pub struct RepoContext {
8    pub repo: Repository,
9    pub state_dir: PathBuf,
10}
11
12impl RepoContext {
13    /// Resolve any revspec git understands (including `@`, `@{upstream}`, etc.)
14    /// to a full 40-character SHA by delegating to `git rev-parse`.
15    pub fn resolve_rev(&self, refspec: &str) -> Result<String, Error> {
16        let work_dir = self
17            .repo
18            .workdir()
19            .unwrap_or_else(|| self.repo.path())
20            .to_path_buf();
21        let out = Command::new("git")
22            .args(["rev-parse", "--verify", refspec])
23            .current_dir(&work_dir)
24            .output()?;
25        if !out.status.success() {
26            let msg = String::from_utf8_lossy(&out.stderr).trim().to_string();
27            return Err(Error::Validation(msg));
28        }
29        Ok(String::from_utf8_lossy(&out.stdout).trim().to_string())
30    }
31}
32
33pub fn open() -> Result<RepoContext, Error> {
34    let (repo, git_dir) = if let Ok(val) = env::var("GIT_DIR") {
35        let git_dir = PathBuf::from(&val);
36        if !git_dir.join("HEAD").exists() {
37            return Err(Error::Validation(format!(
38                "GIT_DIR '{val}' does not look like a git directory"
39            )));
40        }
41        let repo = Repository::open(&git_dir)?;
42        (repo, git_dir)
43    } else {
44        let cwd = env::current_dir()?;
45        let repo = Repository::discover(&cwd)?;
46        let git_dir = repo.path().to_path_buf();
47        (repo, git_dir)
48    };
49
50    Ok(RepoContext {
51        state_dir: git_dir.join("psect"),
52        repo,
53    })
54}