From 5bc4bc6152ba5433333b70da59c80a09c9892ea2 Mon Sep 17 00:00:00 2001 From: Shawn Hartsock Date: Sun, 31 May 2026 11:25:40 -0400 Subject: [PATCH] feat(repo): implement is_git_repo via gix::discover (M1 R1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WHAT: src/repo/is_git_repo.rs — `pub fn is_git_repo(path: &Path) -> bool` backed by `gix::discover` (true iff `path` is inside a git working tree, including from a subdirectory), with parity tests vs `git rev-parse --git-dir` on repo / subdir / non-repo fixtures. Registered in repo/mod.rs; the PyO3 `is_git_repo` wrapper is wired to call it. WHY: First read primitive of the M1 git-tend port (docs/ROADMAP.md M1, step 1). PROVENANCE: the implementation and its tests were written by the local model qwen2.5-coder:32b, driven headlessly through `newt worker` (the newt-agent ACP worker) by the pilot. The pilot applied a mechanical clippy fixup (needless_borrows_for_generic_args) and the module/PyO3 wiring. Co-Authored-By: qwen2.5-coder:32b Model: qwen2.5-coder:32b Piloted-by: newt-agent Co-Authored-By: Claude Opus 4.8 (1M context) --- src/python.rs | 4 +- src/repo/is_git_repo.rs | 86 +++++++++++++++++++++++++++++++++++++++++ src/repo/mod.rs | 3 ++ 3 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 src/repo/is_git_repo.rs diff --git a/src/python.rs b/src/python.rs index 9065088..35756be 100644 --- a/src/python.rs +++ b/src/python.rs @@ -47,8 +47,8 @@ pub struct RepoStatus { // with a call into it. The pure-Rust core is what carries the gix logic + tests. #[pyfunction] -fn is_git_repo(_path: String) -> PyResult { - todo!("repo::is_git_repo (gix::discover)") +fn is_git_repo(path: String) -> PyResult { + Ok(crate::repo::is_git_repo(std::path::Path::new(&path))) } #[pyfunction] diff --git a/src/repo/is_git_repo.rs b/src/repo/is_git_repo.rs new file mode 100644 index 0000000..8ed90fb --- /dev/null +++ b/src/repo/is_git_repo.rs @@ -0,0 +1,86 @@ +use gix::discover; +use std::path::Path; + +/// Returns true iff `path` is inside a git working tree (mirrors +/// `git rev-parse --git-dir` exit==0). +pub fn is_git_repo(path: &Path) -> bool { + discover(path).is_ok() +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::repo::fixtures; + use std::process::Command; + + #[test] + fn repo_root_and_subdir() { + let td = fixtures::repo(); + let root_path = td.path(); + + // Test at the repo root + assert!(is_git_repo(root_path)); + + // Create a subdirectory and test there + let subdir_path = root_path.join("subdir"); + std::fs::create_dir(&subdir_path).expect("mkdir"); + assert!(is_git_repo(&subdir_path)); + } + + #[test] + fn non_repo() { + let td = tempfile::tempdir().expect("tempdir"); + let non_repo_path = td.path(); + assert!(!is_git_repo(non_repo_path)); + } + + #[test] + fn parity_with_git_cli() { + let repo_td = fixtures::repo(); + let repo_path = repo_td.path(); + + // Check the repo path + assert_eq!( + is_git_repo(repo_path), + Command::new("git") + .args(["-C", &repo_path.to_string_lossy(), "rev-parse", "--git-dir"]) + .status() + .expect("spawn git") + .success() + ); + + // Check a subdirectory within the repo + let subdir_path = repo_path.join("subdir"); + std::fs::create_dir(&subdir_path).expect("mkdir"); + assert_eq!( + is_git_repo(&subdir_path), + Command::new("git") + .args([ + "-C", + &subdir_path.to_string_lossy(), + "rev-parse", + "--git-dir" + ]) + .status() + .expect("spawn git") + .success() + ); + + // Check a non-repo path + let non_repo_td = tempfile::tempdir().expect("tempdir"); + let non_repo_path = non_repo_td.path(); + assert_eq!( + is_git_repo(non_repo_path), + Command::new("git") + .args([ + "-C", + &non_repo_path.to_string_lossy(), + "rev-parse", + "--git-dir" + ]) + .status() + .expect("spawn git") + .success() + ); + } +} diff --git a/src/repo/mod.rs b/src/repo/mod.rs index a8b0680..c8edf7d 100644 --- a/src/repo/mod.rs +++ b/src/repo/mod.rs @@ -20,6 +20,9 @@ pub use crate::error::{GitxtendError, Result}; // ---- method registrations (one block per implemented method) ------------- // (methods land here as M1 progresses — see docs/ROADMAP.md M1 ordering) +mod is_git_repo; +pub use is_git_repo::is_git_repo; + /// Temp-dir git fixtures shared by the per-method parity tests. /// /// Fixtures are built with the real `git` CLI, so each parity test asserts