1use std::path::{Path, PathBuf};
4
5pub mod amendment;
6pub mod commit;
7pub mod diff_split;
8pub mod lint;
9pub mod main_branches;
10pub mod remote;
11pub mod repository;
12pub mod worktree_batch;
13pub mod worktree_push;
14pub mod worktree_rebase;
15
16pub use amendment::AmendmentHandler;
17pub use commit::{
18 refine_message_scope, resolve_scope, CommitAnalysis, CommitAnalysisForAI, CommitInfo,
19 CommitInfoForAI, FileDiffRef,
20};
21pub use diff_split::{split_by_file, split_file_by_hunk, FileDiff, HunkDiff};
22pub use lint::{
23 lint_message, parse_subject, passes as lint_passes, suggest_scope_fix, ParsedSubject,
24};
25pub use main_branches::{branches_containing, detect_main_branch_tips, MainBranchTip};
26pub use remote::RemoteInfo;
27pub use repository::GitRepository;
28
29pub const SHORT_HASH_LEN: usize = 8;
31
32pub const FULL_HASH_LEN: usize = 40;
34
35const GIT_BIN_ENV: &str = "OMNI_DEV_GIT_BIN";
40
41const GIT_BINARY_CANDIDATES: &[&str] = &[
52 "/opt/homebrew/bin/git",
53 "/usr/local/bin/git",
54 "/home/linuxbrew/.linuxbrew/bin/git",
55 "/usr/bin/git",
56];
57
58#[must_use]
71pub fn resolve_git_binary() -> PathBuf {
72 resolve_git_binary_from(std::env::var_os(GIT_BIN_ENV), GIT_BINARY_CANDIDATES)
73}
74
75fn resolve_git_binary_from(
78 env_override: Option<std::ffi::OsString>,
79 candidates: &[&str],
80) -> PathBuf {
81 if let Some(path) = env_override.filter(|p| !p.is_empty()) {
82 return PathBuf::from(path);
83 }
84 for candidate in candidates {
85 let path = Path::new(candidate);
86 if path.exists() {
87 return path.to_path_buf();
88 }
89 }
90 PathBuf::from("git")
91}
92
93#[cfg(test)]
94#[allow(clippy::unwrap_used)]
95mod tests {
96 use super::*;
97
98 #[test]
99 fn resolve_git_binary_from_prefers_env_then_candidate_then_fallback() {
100 assert_eq!(
101 resolve_git_binary_from(Some("/custom/git".into()), &["/usr/bin/git"]),
102 PathBuf::from("/custom/git"),
103 "an explicit override wins over every candidate"
104 );
105 let existing = std::env::current_exe().unwrap();
107 let existing = existing.to_str().unwrap();
108 assert_eq!(
109 resolve_git_binary_from(None, &["/no/such/git/xyzzy", existing]),
110 PathBuf::from(existing),
111 "the first *existing* candidate wins, not merely the first"
112 );
113 assert_eq!(
114 resolve_git_binary_from(None, &["/no/such/git/xyzzy"]),
115 PathBuf::from("git"),
116 "with nothing found, fall back to a bare PATH lookup"
117 );
118 assert_eq!(
119 resolve_git_binary_from(Some(String::new().into()), &["/no/such/git/xyzzy"]),
120 PathBuf::from("git"),
121 "an empty override is ignored rather than spawning \"\""
122 );
123 }
124
125 #[test]
126 fn resolve_git_binary_reads_the_real_environment() {
127 let _ = resolve_git_binary();
129 }
130}