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
61#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Default)]61#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Default)]
62pub struct Attributes;62pub struct Attributes;
63 63
64fn macro_mode() -> gix_glob::pattern::Mode {
65 gix_glob::pattern::Mode::all()
66}
67
64impl Pattern for Attributes {68impl Pattern for Attributes {
65 type Value = Value;69 type Value = Value;
66 70
@@ -72,7 +76,7 @@ impl Pattern for Attributes { · 5 unmodified lines
72 crate::parse::Kind::Macro(macro_name) => (76 crate::parse::Kind::Macro(macro_name) => (
73 gix_glob::Pattern {77 gix_glob::Pattern {
74 text: macro_name.as_str().into(),78 text: macro_name.as_str().into(),
75 mode: gix_glob::pattern::Mode::all(),79 mode: macro_mode(),
76 first_wildcard_pos: None,80 first_wildcard_pos: None,
77 },81 },
78 Value::MacroAttributes(into_owned_assignments(assignments).ok()?),82 Value::MacroAttributes(into_owned_assignments(assignments).ok()?),
@@ -93,7 +97,7 @@ impl Pattern for Attributes { · 14 unmodified lines
93 }97 }
94 98
95 fn may_use_glob_pattern(pattern: &gix_glob::Pattern) -> bool {99 fn may_use_glob_pattern(pattern: &gix_glob::Pattern) -> bool {
96 pattern.mode != gix_glob::pattern::Mode::all()100 pattern.mode != macro_mode()
97 }101 }
98}102}
99 103
gix-attributes/tests/match_group/ignore.rs+116−0
@@ -0,0 +1,116 @@
1use std::io::Read;
2
3use bstr::{BStr, ByteSlice};
4use gix_attributes::{Ignore, Match, MatchGroup};
5use gix_glob::pattern::Case;
6
7struct Expectations<'a> {
8 lines: bstr::Lines<'a>,
9}
10
11impl<'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]
33fn 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]
91fn 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
109fn 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 @@
1mod ignore {1mod 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}
gix/src/worktree/mod.rs+13−6
@@ -120,18 +120,25 @@ pub mod excludes { · 119 unmodified lines
120 /// Configure a file-system cache checking if files below the repository are excluded.120 /// Configure a file-system cache checking if files below the repository are excluded.
121 ///121 ///
122 /// This takes into consideration all the usual repository configuration.122 /// 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.
124 pub fn excludes(131 pub fn excludes(
125 &self,132 &self,
126 index: &gix_index::State,133 index: &gix_index::State,
127 overrides: Option<gix_attributes::MatchGroup<gix_attributes::Ignore>>,134 overrides: Option<gix_attributes::MatchGroup<gix_attributes::Ignore>>,
128 ) -> Result<gix_worktree::fs::Cache, Error> {135 ) -> Result<gix_worktree::fs::Cache, Error> {
129 let repo = self.parent;136 let repo = self.parent;
130 let case = repo137 let case = if repo.config.ignore_case {
131 .config138 gix_glob::pattern::Case::Fold
132 .ignore_case139 } else {
133 .then_some(gix_glob::pattern::Case::Fold)140 gix_glob::pattern::Case::Sensitive
134 .unwrap_or_default();141 };
135 let mut buf = Vec::with_capacity(512);142 let mut buf = Vec::with_capacity(512);
136 let excludes_file = match repo.config.excludes_file().transpose()? {143 let excludes_file = match repo.config.excludes_file().transpose()? {
137 Some(user_path) => Some(user_path),144 Some(user_path) => Some(user_path),