Skip to content

Commit 3912ee6

Browse files
committed
A slightly ugly way of not adjusting input patterns too much (#301)
Absolute patterns should stay absolute, otherwise one cannot do normal matches anymore without getting the leading slash back, or in other words, matches against absolute paths won't work then.
1 parent 16a0973 commit 3912ee6

File tree

4 files changed

+37
-24
lines changed

4 files changed

+37
-24
lines changed

git-glob/src/parse.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,23 @@ pub fn pattern(mut pat: &[u8]) -> Option<(BString, pattern::Mode, Option<usize>)
2626
}
2727
if pat.first() == Some(&b'/') {
2828
mode |= Mode::ABSOLUTE;
29-
pat = &pat[1..];
3029
}
31-
let mut line = truncate_non_escaped_trailing_spaces(pat);
32-
if line.last() == Some(&b'/') {
30+
let mut pat = truncate_non_escaped_trailing_spaces(pat);
31+
if pat.last() == Some(&b'/') {
3332
mode |= Mode::MUST_BE_DIR;
34-
line.pop();
33+
pat.pop();
3534
}
36-
if !line.contains(&b'/') {
35+
36+
let relative_pattern = mode.contains(Mode::ABSOLUTE).then(|| &pat[1..]).unwrap_or(&pat);
37+
if !relative_pattern.contains(&b'/') {
3738
mode |= Mode::NO_SUB_DIR;
3839
}
39-
let pos_of_first_wildcard = first_wildcard_pos(&line);
40-
if line.first() == Some(&b'*') && first_wildcard_pos(&line[1..]).is_none() {
40+
if relative_pattern.first() == Some(&b'*') && first_wildcard_pos(&relative_pattern[1..]).is_none() {
4141
mode |= Mode::ENDS_WITH;
4242
}
43-
Some((line, mode, pos_of_first_wildcard))
43+
44+
let pos_of_first_wildcard = first_wildcard_pos(&pat);
45+
Some((pat, mode, pos_of_first_wildcard))
4446
}
4547

4648
fn first_wildcard_pos(pat: &[u8]) -> Option<usize> {

git-glob/src/pattern.rs

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -88,15 +88,17 @@ impl Pattern {
8888
base_path.map_or(true, |p| p.ends_with(b"/")),
8989
"base must end with a trailing slash"
9090
);
91-
debug_assert!(
92-
base_path.map_or(true, |p| !p.starts_with(b"/")),
93-
"base must be relative"
94-
);
91+
debug_assert!(!path.starts_with(b"/"), "input path must be relative");
9592
debug_assert!(
9693
base_path.map(|base| path.starts_with(base)).unwrap_or(true),
9794
"repo-relative paths must be pre-filtered to match our base."
9895
);
9996

97+
let (text, first_wildcard_pos) = self
98+
.mode
99+
.contains(pattern::Mode::ABSOLUTE)
100+
.then(|| (self.text[1..].as_bstr(), self.first_wildcard_pos.map(|p| p - 1)))
101+
.unwrap_or((self.text.as_bstr(), self.first_wildcard_pos));
100102
if self.mode.contains(pattern::Mode::NO_SUB_DIR) {
101103
let basename = if self.mode.contains(pattern::Mode::ABSOLUTE) {
102104
base_path
@@ -105,7 +107,7 @@ impl Pattern {
105107
} else {
106108
&path[basename_start_pos.unwrap_or_default()..]
107109
};
108-
self.matches(basename, flags)
110+
self.matches_inner(text, first_wildcard_pos, basename, flags)
109111
} else {
110112
let path = match base_path {
111113
Some(base) => match path.strip_prefix(base.as_ref()) {
@@ -114,7 +116,7 @@ impl Pattern {
114116
},
115117
None => path,
116118
};
117-
self.matches(path, flags)
119+
self.matches_inner(text, first_wildcard_pos, path, flags)
118120
}
119121
}
120122

@@ -125,11 +127,21 @@ impl Pattern {
125127
///
126128
/// Note that this method uses some shortcuts to accelerate simple patterns.
127129
pub fn matches<'a>(&self, value: impl Into<&'a BStr>, mode: wildmatch::Mode) -> bool {
130+
self.matches_inner(self.text.as_bstr(), self.first_wildcard_pos, value, mode)
131+
}
132+
133+
fn matches_inner<'a>(
134+
&self,
135+
text: &BStr,
136+
first_wildcard_pos: Option<usize>,
137+
value: impl Into<&'a BStr>,
138+
mode: wildmatch::Mode,
139+
) -> bool {
128140
let value = value.into();
129-
match self.first_wildcard_pos {
141+
match first_wildcard_pos {
130142
// "*literal" case, overrides starts-with
131143
Some(pos) if self.mode.contains(pattern::Mode::ENDS_WITH) && !value.contains(&b'/') => {
132-
let text = &self.text[pos + 1..];
144+
let text = &text[pos + 1..];
133145
if mode.contains(wildmatch::Mode::IGNORE_CASE) {
134146
value
135147
.len()
@@ -144,20 +156,20 @@ impl Pattern {
144156
if mode.contains(wildmatch::Mode::IGNORE_CASE) {
145157
if !value
146158
.get(..pos)
147-
.map_or(false, |value| value.eq_ignore_ascii_case(&self.text[..pos]))
159+
.map_or(false, |value| value.eq_ignore_ascii_case(&text[..pos]))
148160
{
149161
return false;
150162
}
151-
} else if !value.starts_with(&self.text[..pos]) {
163+
} else if !value.starts_with(&text[..pos]) {
152164
return false;
153165
}
154-
crate::wildmatch(self.text.as_bstr(), value, mode)
166+
crate::wildmatch(text.as_bstr(), value, mode)
155167
}
156168
None => {
157169
if mode.contains(wildmatch::Mode::IGNORE_CASE) {
158-
self.text.eq_ignore_ascii_case(value)
170+
text.eq_ignore_ascii_case(value)
159171
} else {
160-
self.text == value
172+
text == value
161173
}
162174
}
163175
}

git-glob/tests/matching/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,6 @@ fn non_dirs_for_must_be_dir_patterns_are_ignored() {
122122
}
123123

124124
#[test]
125-
#[ignore]
126125
fn matches_of_absolute_paths_work() {
127126
let input = "/hello/git";
128127
let pat = pat(input);

git-glob/tests/parse/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,12 @@ fn leading_exclamation_marks_can_be_escaped_with_backslash() {
7777
fn leading_slashes_mark_patterns_as_absolute() {
7878
assert_eq!(
7979
git_glob::parse(br"/absolute"),
80-
pat("absolute", Mode::NO_SUB_DIR | Mode::ABSOLUTE, None)
80+
pat("/absolute", Mode::NO_SUB_DIR | Mode::ABSOLUTE, None)
8181
);
8282

8383
assert_eq!(
8484
git_glob::parse(br"/absolute/path"),
85-
pat("absolute/path", Mode::ABSOLUTE, None)
85+
pat("/absolute/path", Mode::ABSOLUTE, None)
8686
);
8787
}
8888

0 commit comments

Comments
 (0)