Replace Git subprocesses with native APIs

This commit is contained in:
Georg Bauer
2026-07-28 06:55:05 +02:00
parent 90a445cafd
commit ed2226bc60
5 changed files with 457 additions and 254 deletions

View File

@@ -7,7 +7,6 @@ use sha2::{Digest, Sha256};
use std::collections::{BTreeSet, HashMap, HashSet};
use std::fs;
use std::path::{Component, Path, PathBuf};
use std::process::Command;
use std::sync::RwLock;
use std::time::{SystemTime, UNIX_EPOCH};
use time::OffsetDateTime;
@@ -1113,35 +1112,34 @@ fn git_revision_matches(root: &Path, revision: &str, current: &str) -> bool {
{
return false;
}
Command::new("git")
.args(["-C"])
.arg(root)
.args(["rev-parse", "--verify"])
.arg(format!("{revision}^{{commit}}"))
.output()
git2::Repository::discover(root)
.ok()
.filter(|output| output.status.success())
.is_some_and(|output| String::from_utf8_lossy(&output.stdout).trim() == current)
.is_some_and(|repository| {
repository
.revparse_single(revision)
.ok()
.and_then(|object| object.peel_to_commit().ok())
.is_some_and(|commit| commit.id().to_string() == current)
})
}
fn git_state(root: &Path) -> Option<(String, bool)> {
let revision = Command::new("git")
.args(["-C"])
.arg(root)
.args(["rev-parse", "HEAD"])
.output()
.ok()
.filter(|output| output.status.success())
.map(|output| String::from_utf8_lossy(&output.stdout).trim().to_owned())?;
let status = Command::new("git")
.args(["-C"])
.arg(root)
.args(["status", "--porcelain"])
.output()
.ok()?;
let repository = git2::Repository::discover(root).ok()?;
let revision = repository
.head()
.ok()?
.peel_to_commit()
.ok()?
.id()
.to_string();
let mut options = git2::StatusOptions::new();
options
.include_untracked(true)
.recurse_untracked_dirs(true)
.include_ignored(false);
Some((
revision,
status.status.success() && status.stdout.is_empty(),
repository.statuses(Some(&mut options)).ok()?.is_empty(),
))
}
@@ -1586,29 +1584,18 @@ mod tests {
#[test]
fn clean_full_and_short_git_revisions_become_stale_when_the_worktree_changes() {
let fixture = Fixture::new();
for arguments in [
vec!["init"],
vec!["add", "source.rs"],
vec![
"-c",
"user.name=DS4Server",
"-c",
"user.email=ds4@example.invalid",
"commit",
"-m",
"Initial",
],
] {
assert!(
Command::new("git")
.arg("-C")
.arg(&fixture.project)
.args(arguments)
.status()
.unwrap()
.success()
);
}
let repository = git2::Repository::init(&fixture.project).unwrap();
let mut index = repository.index().unwrap();
index.add_path(Path::new("source.rs")).unwrap();
let tree_id = index.write_tree().unwrap();
index.write().unwrap();
let tree = repository.find_tree(tree_id).unwrap();
let signature = git2::Signature::now("DS4Server", "ds4@example.invalid").unwrap();
repository
.commit(Some("HEAD"), &signature, &signature, "Initial", &tree, &[])
.unwrap();
drop(tree);
drop(repository);
let revision = git_state(&fixture.project).unwrap().0;
assert!(git_revision_matches(
&fixture.project,