shipper_core/ops/git/mod.rs
1//! Git repository operations (cleanliness, context capture).
2//!
3//! Absorbed into `crate::ops::git` from the standalone `shipper-git` microcrate.
4//! The facade is re-exported at `shipper::git` (see `lib.rs`) so external
5//! consumers keep using the historical `shipper::git::*` public API.
6//!
7//! Split:
8//!
9//! - [`cleanliness`] — `is_git_clean` + `ensure_git_clean`, routing through
10//! [`bin_override`] when `SHIPPER_GIT_BIN` is set.
11//! - [`context`] — commit/branch/tag/changed-files/remote queries + the
12//! [`collect_git_context`] aggregator.
13//! - [`bin_override`] — parallel helpers that honor `SHIPPER_GIT_BIN`.
14//!
15//! See `CLAUDE.md` in this folder for architectural rules.
16
17use std::env;
18use std::path::Path;
19
20pub(crate) mod bin_override;
21pub(crate) mod cleanliness;
22pub(crate) mod context;
23
24// Public-to-crate API re-exports; reachable via `shipper::git::*` through the
25// façade module in `lib.rs`.
26pub use cleanliness::{ensure_git_clean, is_git_clean};
27
28use crate::types::GitContext;
29
30/// Collect a [`GitContext`] for the current working directory.
31///
32/// Returns `None` when the CWD is not inside a git repository.
33///
34/// When `SHIPPER_GIT_BIN` is set, every sub-query is routed through the
35/// `bin_override` helpers — there is no silent fallback to the default
36/// `git` binary. Without the override, queries go through `context`.
37pub fn collect_git_context() -> Option<GitContext> {
38 let repo_root = std::env::current_dir().ok()?;
39 collect_git_context_at(&repo_root)
40}
41
42/// Collect a [`GitContext`] for a specific workspace or repository path.
43///
44/// Returns `None` when `repo_root` is not inside a git repository.
45pub fn collect_git_context_at(repo_root: &Path) -> Option<GitContext> {
46 let git_program = bin_override::git_program();
47 if !bin_override::is_repo_root(repo_root, &git_program) {
48 return None;
49 }
50
51 if env::var("SHIPPER_GIT_BIN").is_ok() {
52 let commit = bin_override::get_git_commit(repo_root, &git_program);
53 let branch = bin_override::get_git_branch(repo_root, &git_program);
54 let tag = bin_override::get_git_tag(repo_root, &git_program);
55 let dirty = bin_override::get_git_dirty_status(repo_root, &git_program);
56 return Some(GitContext {
57 commit,
58 branch,
59 tag,
60 dirty,
61 });
62 }
63
64 if !context::is_git_repo(repo_root) {
65 return None;
66 }
67
68 Some(context::get_git_context(repo_root))
69}