bctx_forge/awareness/
git.rs1use super::{AwarenessMap, EcosystemAwareness};
2use once_cell::sync::Lazy;
3use regex::Regex;
4
5static STATS_RE: Lazy<Regex> = Lazy::new(|| {
6 Regex::new(r"(\d+) files? changed(?:, (\d+) insertions?\(\+\))?(?:, (\d+) deletions?\(-\))?")
7 .unwrap()
8});
9static BRANCH_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"On branch (.+)").unwrap());
10static AHEAD_BEHIND_RE: Lazy<Regex> =
11 Lazy::new(|| Regex::new(r"Your branch is (ahead|behind).+by (\d+) commit").unwrap());
12static COMMIT_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"^([0-9a-f]{7,40})\s+(.+)$").unwrap());
13
14pub struct GitAwareness;
15
16impl EcosystemAwareness for GitAwareness {
17 fn matches(&self, program: &str, _args: &[String]) -> bool {
18 matches!(
19 program.split('/').next_back().unwrap_or(program),
20 "git" | "gh" | "jj"
21 )
22 }
23
24 fn extract(&self, stdout: &str, stderr: &str, exit_code: i32) -> AwarenessMap {
25 let mut map = AwarenessMap::new("vcs", "git");
26 map.insert("exit_code", exit_code.to_string());
27
28 let combined = format!("{stdout}\n{stderr}");
29
30 if let Some(cap) = BRANCH_RE.captures(&combined) {
31 map.insert("branch", cap[1].trim().to_string());
32 }
33 if let Some(cap) = STATS_RE.captures(&combined) {
34 map.insert("files_changed", cap[1].to_string());
35 if let Some(ins) = cap.get(2) {
36 map.insert("insertions", ins.as_str().to_string());
37 }
38 if let Some(del) = cap.get(3) {
39 map.insert("deletions", del.as_str().to_string());
40 }
41 }
42 if let Some(cap) = AHEAD_BEHIND_RE.captures(&combined) {
43 map.insert("ahead_behind", cap[1].to_string());
44 map.insert("ahead_behind_count", cap[2].to_string());
45 }
46
47 let commit_count = COMMIT_RE.captures_iter(stdout).count();
48 if commit_count > 0 {
49 map.insert("commit_count", commit_count.to_string());
50 }
51
52 map
53 }
54}