History

refactor

Sebastian Thiel committed 3 years ago

Commit 06774061 parent ed63ec04 files · +136 −126Browse files

Unified · Split

4 files
  1. gix-attributes/src/match_group.rs +6 −2
  2. gix-attributes/tests/match_group/ignore.rs +116 −0
  3. gix-attributes/tests/match_group/mod.rs +1 −118
  4. gix/src/worktree/mod.rs +13 −6
gix-attributes/src/match_group.rs+6−2
@@ -61,6 +61,10 @@ pub enum Value { · 60 unmodified lines
6161 #[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Default)]
6262 pub struct Attributes;
6363
64+fn macro_mode() -> gix_glob::pattern::Mode {
65+ gix_glob::pattern::Mode::all()
66+}
67+
6468 impl Pattern for Attributes {
6569 type Value = Value;
6670
@@ -72,7 +76,7 @@ impl Pattern for Attributes { · 5 unmodified lines
7276 crate::parse::Kind::Macro(macro_name) => (
7377 gix_glob::Pattern {
7478 text: macro_name.as_str().into(),
75− mode: gix_glob::pattern::Mode::all(),
79+ mode: macro_mode(),
7680 first_wildcard_pos: None,
7781 },
7882 Value::MacroAttributes(into_owned_assignments(assignments).ok()?),
@@ -93,7 +97,7 @@ impl Pattern for Attributes { · 14 unmodified lines
9397 }
9498
9599 fn may_use_glob_pattern(pattern: &gix_glob::Pattern) -> bool {
96− pattern.mode != gix_glob::pattern::Mode::all()
100+ pattern.mode != macro_mode()
97101 }
98102 }
99103
gix-attributes/tests/match_group/ignore.rs+116−0
@@ -0,0 +1,116 @@
1+use std::io::Read;
2+
3+use bstr::{BStr, ByteSlice};
4+use gix_attributes::{Ignore, Match, MatchGroup};
5+use gix_glob::pattern::Case;
6+
7+struct Expectations<'a> {
8+ lines: bstr::Lines<'a>,
9+}
10+
11+impl<'a> Iterator for Expectations<'a> {
12+ type Item = (&'a BStr, Option<(&'a BStr, usize, &'a BStr)>);
13+
14+ fn next(&mut self) -> Option<Self::Item> {
15+ let line = self.lines.next()?;
16+ let (left, value) = line.split_at(line.find_byte(b'\t').unwrap());
17+ let value = value[1..].as_bstr();
18+
19+ let source_and_line = if left == b"::" {
20+ None
21+ } else {
22+ let mut tokens = left.split(|b| *b == b':');
23+ let source = tokens.next().unwrap().as_bstr();
24+ let line_number: usize = tokens.next().unwrap().to_str_lossy().parse().ok().unwrap();
25+ let pattern = tokens.next().unwrap().as_bstr();
26+ Some((source, line_number, pattern))
27+ };
28+ Some((value, source_and_line))
29+ }
30+}
31+
32+#[test]
33+fn from_git_dir() -> crate::Result {
34+ let dir = gix_testtools::scripted_fixture_read_only("make_global_and_external_and_dir_ignores.sh")?;
35+ let repo_dir = dir.join("repo");
36+ let git_dir = repo_dir.join(".git");
37+ let baseline = std::fs::read(git_dir.parent().unwrap().join("git-check-ignore.baseline"))?;
38+ let mut buf = Vec::new();
39+ let mut group = MatchGroup::from_git_dir(git_dir, Some(dir.join("user.exclude")), &mut buf)?;
40+
41+ assert!(
42+ !group.add_patterns_file("not-a-file", false, None, &mut buf)?,
43+ "missing files are no problem and cause a negative response"
44+ );
45+ assert!(
46+ group.add_patterns_file(repo_dir.join(".gitignore"), true, repo_dir.as_path().into(), &mut buf)?,
47+ "existing files return true"
48+ );
49+
50+ buf.clear();
51+ let ignore_file = repo_dir.join("dir-with-ignore").join(".gitignore");
52+ std::fs::File::open(&ignore_file)?.read_to_end(&mut buf)?;
53+ group.add_patterns_buffer(&buf, ignore_file, repo_dir.as_path().into());
54+
55+ for (path, source_and_line) in (Expectations {
56+ lines: baseline.lines(),
57+ }) {
58+ let actual = group.pattern_matching_relative_path(
59+ path,
60+ repo_dir
61+ .join(path.to_str_lossy().as_ref())
62+ .metadata()
63+ .ok()
64+ .map(|m| m.is_dir()),
65+ Case::Sensitive,
66+ );
67+ match (actual, source_and_line) {
68+ (
69+ Some(Match {
70+ sequence_number,
71+ pattern: _,
72+ source,
73+ value: _,
74+ }),
75+ Some((expected_source, line, _expected_pattern)),
76+ ) => {
77+ assert_eq!(sequence_number, line, "our counting should match the one used in git");
78+ assert_eq!(
79+ source.map(|p| p.canonicalize().unwrap()),
80+ Some(repo_dir.join(expected_source.to_str_lossy().as_ref()).canonicalize()?)
81+ );
82+ }
83+ (None, None) => {}
84+ (actual, expected) => panic!("actual {actual:?} should match {expected:?} with path '{path}'"),
85+ }
86+ }
87+ Ok(())
88+}
89+
90+#[test]
91+fn from_overrides() {
92+ let input = ["simple", "pattern/"];
93+ let group = gix_attributes::MatchGroup::<Ignore>::from_overrides(input);
94+ assert_eq!(
95+ group.pattern_matching_relative_path("Simple", None, gix_glob::pattern::Case::Fold),
96+ Some(pattern_to_match(&gix_glob::parse("simple").unwrap(), 0))
97+ );
98+ assert_eq!(
99+ group.pattern_matching_relative_path("pattern", Some(true), gix_glob::pattern::Case::Sensitive),
100+ Some(pattern_to_match(&gix_glob::parse("pattern/").unwrap(), 1))
101+ );
102+ assert_eq!(group.patterns.len(), 1);
103+ assert_eq!(
104+ gix_attributes::PatternList::<Ignore>::from_overrides(input),
105+ group.patterns.into_iter().next().unwrap()
106+ );
107+}
108+
109+fn pattern_to_match(pattern: &gix_glob::Pattern, sequence_number: usize) -> Match<'_, ()> {
110+ Match {
111+ pattern,
112+ value: &(),
113+ source: None,
114+ sequence_number,
115+ }
116+}
gix-attributes/tests/match_group/mod.rs+1−118
@@ -1,118 +1 @@
1−mod ignore {
2− use std::io::Read;
3−
4− use bstr::{BStr, ByteSlice};
5− use gix_attributes::{Ignore, Match, MatchGroup};
6− use gix_glob::pattern::Case;
7−
8− struct Expectations<'a> {
9− lines: bstr::Lines<'a>,
10− }
11−
12− impl<'a> Iterator for Expectations<'a> {
13− type Item = (&'a BStr, Option<(&'a BStr, usize, &'a BStr)>);
14−
15− fn next(&mut self) -> Option<Self::Item> {
16− let line = self.lines.next()?;
17− let (left, value) = line.split_at(line.find_byte(b'\t').unwrap());
18− let value = value[1..].as_bstr();
19−
20− let source_and_line = if left == b"::" {
21− None
22− } else {
23− let mut tokens = left.split(|b| *b == b':');
24− let source = tokens.next().unwrap().as_bstr();
25− let line_number: usize = tokens.next().unwrap().to_str_lossy().parse().ok().unwrap();
26− let pattern = tokens.next().unwrap().as_bstr();
27− Some((source, line_number, pattern))
28− };
29− Some((value, source_and_line))
30− }
31− }
32−
33− #[test]
34− fn from_git_dir() -> crate::Result {
35− let dir = gix_testtools::scripted_fixture_read_only("make_global_and_external_and_dir_ignores.sh")?;
36− let repo_dir = dir.join("repo");
37− let git_dir = repo_dir.join(".git");
38− let baseline = std::fs::read(git_dir.parent().unwrap().join("git-check-ignore.baseline"))?;
39− let mut buf = Vec::new();
40− let mut group = MatchGroup::from_git_dir(git_dir, Some(dir.join("user.exclude")), &mut buf)?;
41−
42− assert!(
43− !group.add_patterns_file("not-a-file", false, None, &mut buf)?,
44− "missing files are no problem and cause a negative response"
45− );
46− assert!(
47− group.add_patterns_file(repo_dir.join(".gitignore"), true, repo_dir.as_path().into(), &mut buf)?,
48− "existing files return true"
49− );
50−
51− buf.clear();
52− let ignore_file = repo_dir.join("dir-with-ignore").join(".gitignore");
53− std::fs::File::open(&ignore_file)?.read_to_end(&mut buf)?;
54− group.add_patterns_buffer(&buf, ignore_file, repo_dir.as_path().into());
55−
56− for (path, source_and_line) in (Expectations {
57− lines: baseline.lines(),
58− }) {
59− let actual = group.pattern_matching_relative_path(
60− path,
61− repo_dir
62− .join(path.to_str_lossy().as_ref())
63− .metadata()
64− .ok()
65− .map(|m| m.is_dir()),
66− Case::Sensitive,
67− );
68− match (actual, source_and_line) {
69− (
70− Some(Match {
71− sequence_number,
72− pattern: _,
73− source,
74− value: _,
75− }),
76− Some((expected_source, line, _expected_pattern)),
77− ) => {
78− assert_eq!(sequence_number, line, "our counting should match the one used in git");
79− assert_eq!(
80− source.map(|p| p.canonicalize().unwrap()),
81− Some(repo_dir.join(expected_source.to_str_lossy().as_ref()).canonicalize()?)
82− );
83− }
84− (None, None) => {}
85− (actual, expected) => panic!("actual {actual:?} should match {expected:?} with path '{path}'"),
86− }
87− }
88− Ok(())
89− }
90−
91− #[test]
92− fn from_overrides() {
93− let input = ["simple", "pattern/"];
94− let group = gix_attributes::MatchGroup::<Ignore>::from_overrides(input);
95− assert_eq!(
96− group.pattern_matching_relative_path("Simple", None, gix_glob::pattern::Case::Fold),
97− Some(pattern_to_match(&gix_glob::parse("simple").unwrap(), 0))
98− );
99− assert_eq!(
100− group.pattern_matching_relative_path("pattern", Some(true), gix_glob::pattern::Case::Sensitive),
101− Some(pattern_to_match(&gix_glob::parse("pattern/").unwrap(), 1))
102− );
103− assert_eq!(group.patterns.len(), 1);
104− assert_eq!(
105− gix_attributes::PatternList::<Ignore>::from_overrides(input),
106− group.patterns.into_iter().next().unwrap()
107− );
108− }
109−
110− fn pattern_to_match(pattern: &gix_glob::Pattern, sequence_number: usize) -> Match<'_, ()> {
111− Match {
112− pattern,
113− value: &(),
114− source: None,
115− sequence_number,
116− }
117− }
118−}
1+mod ignore;
gix/src/worktree/mod.rs+13−6
@@ -120,18 +120,25 @@ pub mod excludes { · 119 unmodified lines
120120 /// Configure a file-system cache checking if files below the repository are excluded.
121121 ///
122122 /// This takes into consideration all the usual repository configuration.
123− // TODO: test, provide higher-level interface that is much easier to use and doesn't panic.
123+ ///
124+ /// `index` may be used to obtain `.gitignore` files directly from the index under certain conditions.
125+ // TODO: test, provide higher-level interface that is much easier to use and doesn't panic when accessing entries
126+ // by non-relative path.
127+ // TODO: `index` might be so special (given the conditions we are talking about) that it's better obtained internally
128+ // so the caller won't have to care.
129+ // TODO: global files like `~/.gitignore` seem to be missing here, but we need a way to control if these should be loaded.
130+ // probably that needs another permission in the repo options or a custom config variable. The latter is easiest to manage.
124131 pub fn excludes(
125132 &self,
126133 index: &gix_index::State,
127134 overrides: Option<gix_attributes::MatchGroup<gix_attributes::Ignore>>,
128135 ) -> Result<gix_worktree::fs::Cache, Error> {
129136 let repo = self.parent;
130− let case = repo
131− .config
132− .ignore_case
133− .then_some(gix_glob::pattern::Case::Fold)
134− .unwrap_or_default();
137+ let case = if repo.config.ignore_case {
138+ gix_glob::pattern::Case::Fold
139+ } else {
140+ gix_glob::pattern::Case::Sensitive
141+ };
135142 let mut buf = Vec::with_capacity(512);
136143 let excludes_file = match repo.config.excludes_file().transpose()? {
137144 Some(user_path) => Some(user_path),