Skip to main content

omnifuse_git/
tracking.rs

1//! Git tracking rules.
2
3use std::path::{Path, PathBuf};
4
5use crate::filter::GitignoreFilter;
6
7/// Rules deciding whether a path should be tracked by the Git backend.
8#[derive(Debug)]
9pub struct GitTrackingRules {
10  repo_path: PathBuf,
11  filter: GitignoreFilter
12}
13
14impl GitTrackingRules {
15  /// Create tracking rules for a repository.
16  #[must_use]
17  pub fn new(repo_path: &Path) -> Self {
18    Self {
19      repo_path: repo_path.to_path_buf(),
20      filter: GitignoreFilter::new(repo_path)
21    }
22  }
23
24  /// Return whether a path should be tracked.
25  #[must_use]
26  pub fn accepts(&self, path: &Path) -> bool {
27    if Self::contains_git_directory(path) {
28      return false;
29    }
30
31    let path = if path.is_absolute() {
32      path.to_path_buf()
33    } else {
34      self.repo_path.join(path)
35    };
36
37    !self.filter.is_ignored(&path)
38  }
39
40  pub(crate) fn contains_git_directory(path: &Path) -> bool {
41    path.components().any(|component| component.as_os_str() == ".git")
42  }
43}
44
45#[cfg(test)]
46#[allow(clippy::expect_used)]
47mod tests {
48  use std::path::Path;
49
50  use super::GitTrackingRules;
51
52  #[test]
53  fn tracking_rejects_git_directory() {
54    let tmp = tempfile::tempdir().expect("tempdir");
55    let rules = GitTrackingRules::new(tmp.path());
56
57    assert!(!rules.accepts(Path::new(".git/config")));
58    assert!(!rules.accepts(&tmp.path().join(".git/config")));
59  }
60
61  #[test]
62  fn tracking_uses_gitignore_after_init() {
63    let tmp = tempfile::tempdir().expect("tempdir");
64    std::fs::write(tmp.path().join(".gitignore"), "*.log\nbuild/\n").expect("gitignore");
65    let rules = GitTrackingRules::new(tmp.path());
66
67    assert!(!rules.accepts(&tmp.path().join("debug.log")));
68    assert!(!rules.accepts(&tmp.path().join("build/output.bin")));
69    assert!(rules.accepts(&tmp.path().join("src/lib.rs")));
70  }
71}