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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
use std::path::Path;
use anyhow::Result;
use crate::git;
pub(super) struct DiffTracker<'a> {
path: &'a Path,
baseline: DiffBaseline,
}
enum DiffBaseline {
Clean,
Unknown,
Snapshot(Vec<u8>),
}
impl<'a> DiffTracker<'a> {
pub(super) fn clean_baseline(path: &'a Path) -> Self {
Self {
path,
baseline: DiffBaseline::Clean,
}
}
pub(super) fn unknown_baseline(path: &'a Path) -> Self {
Self {
path,
baseline: DiffBaseline::Unknown,
}
}
pub(super) async fn prepare_for_group(&mut self, may_modify_files: bool) -> Result<()> {
if may_modify_files && let DiffBaseline::Unknown = self.baseline {
self.baseline = DiffBaseline::Snapshot(git::get_diff(self.path).await?);
}
Ok(())
}
pub(super) async fn changed_after_group(
&mut self,
may_modify_files: bool,
all_skipped: bool,
) -> Result<bool> {
// Read-only groups and fully skipped groups cannot change files, so avoid
// asking git about the working tree.
if !may_modify_files || all_skipped {
return Ok(false);
}
match &mut self.baseline {
DiffBaseline::Clean => {
// `WorkTreeKeeper` already removed unstaged changes. A quiet
// worktree check is enough unless the hook actually wrote files.
if !git::has_worktree_diff(self.path).await? {
return Ok(false);
}
// Capture the dirty state after this group so later groups can
// compare against the exact diff left by previous hooks.
self.baseline = DiffBaseline::Snapshot(git::get_diff(self.path).await?);
Ok(true)
}
DiffBaseline::Snapshot(prev_diff) => {
// Unknown initial state, `--all-files`, and later dirty groups
// need a full before/after diff comparison to avoid confusing
// pre-existing user changes with hook changes.
let curr_diff = git::get_diff(self.path).await?;
let modified = curr_diff != *prev_diff;
*prev_diff = curr_diff;
Ok(modified)
}
DiffBaseline::Unknown => {
unreachable!("diff baseline must be captured before hooks can modify files")
}
}
}
}