1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//! Best-effort git working-tree status (1.2.20+ Phase G).
//!
//! Inkhaven has no git dependency — a project is plain
//! files the user version-controls however they like.
//! For the quit-time "uncommitted changes" warning we
//! simply shell out to the user's own `git`, parse
//! `status --porcelain`, and treat any failure (not a
//! repo, git not installed, the command erroring) as
//! "nothing to warn about". Never errors; never blocks
//! the user from quitting.
use std::path::Path;
use std::process::Command;
/// Number of changed entries in the working tree at
/// `root` per `git status --porcelain` (modified, staged,
/// and untracked paths all count). Returns `None` when
/// `root` isn't a git repo, git is unavailable, or the
/// command fails — callers treat `None` as "don't warn".
pub fn uncommitted_count(root: &Path) -> Option<usize> {
let output = Command::new("git")
.arg("-C")
.arg(root)
.arg("status")
.arg("--porcelain")
.output()
.ok()?;
if !output.status.success() {
// Not a repository, or git refused — nothing to say.
return None;
}
let count = output
.stdout
.split(|&b| b == b'\n')
.filter(|line| !line.is_empty())
.count();
Some(count)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn non_repo_dir_is_none() {
// A fresh temp dir is not a git repo (and if git is
// absent the command simply fails) — either way the
// result is None, so the quit warning stays silent.
let tmp = tempfile::tempdir().expect("tempdir");
assert_eq!(uncommitted_count(tmp.path()), None);
}
}