Skip to content

Commit a252ed8

Browse files
committed
fix: cache::Ignore assures that case-sensitivity is handled similarly to git.
Previously directory excludes like `dir/` could yield different results compared to git.
1 parent bf68b00 commit a252ed8

File tree

5 files changed

+58
-22
lines changed

5 files changed

+58
-22
lines changed

gix-worktree/src/cache/state/ignore.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -149,13 +149,23 @@ impl Ignore {
149149
Find: for<'b> FnMut(&oid, &'b mut Vec<u8>) -> Result<gix_object::BlobRef<'b>, E>,
150150
E: std::error::Error + Send + Sync + 'static,
151151
{
152-
gix_glob::search::pattern::strip_base_handle_recompute_basename_pos()
153-
let rela_dir = dir.strip_prefix(root).expect("dir in root");
152+
let dir_bstr = gix_path::into_bstr(dir);
153+
let mut rela_dir = gix_glob::search::pattern::strip_base_handle_recompute_basename_pos(
154+
gix_path::into_bstr(root).as_ref(),
155+
dir_bstr.as_ref(),
156+
None,
157+
self.case,
158+
)
159+
.expect("dir in root")
160+
.0;
161+
if rela_dir.starts_with(b"/") {
162+
rela_dir = &rela_dir[1..];
163+
}
154164
self.matched_directory_patterns_stack
155-
.push(self.matching_exclude_pattern_no_dir(gix_path::into_bstr(rela_dir).as_ref(), Some(true), self.case));
165+
.push(self.matching_exclude_pattern_no_dir(rela_dir, Some(true), self.case));
156166

157-
let ignore_path_relative = rela_dir.join(".gitignore");
158-
let ignore_path_relative = gix_path::to_unix_separators_on_windows(gix_path::into_bstr(ignore_path_relative));
167+
let ignore_path_relative =
168+
gix_path::to_unix_separators_on_windows(gix_path::join_bstr_unix_pathsep(rela_dir, ".gitignore"));
159169
let ignore_file_in_index =
160170
attribute_files_in_index.binary_search_by(|t| t.0.as_bstr().cmp(ignore_path_relative.as_ref()));
161171
let follow_symlinks = ignore_file_in_index.is_err();
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
make_ignore_and_attributes_setup.tar.xz
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
version https://git-lfs.github.com/spec/v1
2-
oid sha256:ae297f2c067e2cc7b0c8eabfff721ff775a7d8266ffef979bde2458de2ab03c9
3-
size 10944
2+
oid sha256:38df6900cdb0ad620e158a3cc19325053658b9cfcb8987779af634813d957f6c
3+
size 11088

gix-worktree/tests/fixtures/make_ignore_and_attributes_setup.sh

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ cat <<EOF >user.exclude
66
user-file-anywhere
77
/user-file-from-top
88
9-
user-dir-anywhere/
9+
user-Dir-anywhere/
1010
/user-dir-from-top
1111
1212
user-subdir/file
1313
**/user-subdir-anywhere/file
14+
a/b/*
15+
z/x
1416
EOF
1517

1618
mkdir repo;
@@ -33,13 +35,15 @@ EOF
3335
cat <<EOF >.gitignore
3436
# a sample .gitignore
3537
top-level-local-file-anywhere
38+
d/e/*
39+
e/f
3640
EOF
3741

3842
mkdir dir-with-ignore
3943
cat <<EOF >dir-with-ignore/.gitignore
4044
# a sample .gitignore
4145
sub-level-local-file-anywhere
42-
sub-level-dir-anywhere/
46+
sub-Level-dir-anywhere/
4347
!/negated
4448
/negated-dir/
4549
!/negated-dir/
@@ -63,13 +67,13 @@ EOF
6367

6468
git check-ignore -vn --stdin 2>&1 <<EOF >git-check-ignore.baseline || :
6569
dir-with-ignore/sub-level-dir-anywhere/
66-
dir-with-ignore/foo/sub-level-dir-anywhere/
67-
dir-with-ignore/sub-level-dir-anywhere
70+
dir-with-ignore/foo/Sub-level-dir-anywhere/
71+
dir-with-ignore/Sub-level-dir-anywhere
6872
user-file-anywhere
6973
dir/user-file-anywhere
7074
user-file-from-top
7175
no-match/user-file-from-top
72-
user-dir-anywhere
76+
USER-dir-anywhere
7377
user-dir-from-top
7478
no-match/user-dir-from-top
7579
user-subdir/file
@@ -107,6 +111,20 @@ other-dir-with-ignore/other-sub-level-dir-anywhere/hello
107111
other-dir-with-ignore/other-sub-level-dir-anywhere/
108112
dir-with-ignore/negated
109113
dir-with-ignore/negated-dir/hello
114+
a/b/C
115+
a/B/c
116+
A/B/C
117+
z/x
118+
Z/x
119+
z/X
120+
Z/X
121+
d/e/F
122+
d/e/f
123+
D/e/F
124+
D/E/F
125+
e/f
126+
e/F
127+
E/f
128+
E/F
110129
EOF
111-
112130
)

gix-worktree/tests/worktree/cache/ignore.rs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
1-
use std::path::Path;
2-
31
use bstr::{BStr, ByteSlice};
42
use gix_glob::pattern::Case;
5-
use gix_index::entry::Mode;
6-
use gix_odb::{pack::bundle::write::Options, FindExt};
3+
use gix_odb::FindExt;
74
use gix_worktree::Cache;
8-
use tempfile::{tempdir, TempDir};
95

106
use crate::hex_to_id;
117

@@ -68,12 +64,12 @@ fn special_exclude_cases_we_handle_differently() {
6864
let is_dir = dir.join(&relative_path).metadata().ok().map(|m| m.is_dir());
6965

7066
let platform = cache
71-
.at_entry(relative_entry, is_dir, |oid, buf| {
67+
.at_entry(relative_entry, is_dir, |_oid, _buf| {
7268
Err(std::io::Error::new(std::io::ErrorKind::Other, "unreachable"))
7369
})
7470
.unwrap();
7571
let match_ = platform.matching_exclude_pattern().expect("match all values");
76-
let is_excluded = platform.is_excluded();
72+
let _is_excluded = platform.is_excluded();
7773

7874
match relative_entry.as_bytes() {
7975
b"tld" | b"tld/" | b"tld/file" | b"tld/sd" | b"tld/sd/" => {
@@ -93,11 +89,17 @@ fn check_against_baseline() -> crate::Result {
9389
let user_exclude_path = dir.join("user.exclude");
9490
assert!(user_exclude_path.is_file());
9591

92+
// Due to the way our setup differs from gits dynamic stack (which involves trying to read files from disk
93+
// by path) we can only test one case baseline, so we require multiple platforms (or filesystems) to run this.
94+
let case = if gix_fs::Capabilities::probe("../.git").ignore_case {
95+
Case::Fold
96+
} else {
97+
Case::Sensitive
98+
};
9699
let mut index = gix_index::File::at(git_dir.join("index"), gix_hash::Kind::Sha1, Default::default())?;
97100
let odb = gix_odb::at(git_dir.join("objects"))?;
98-
let case = gix_glob::pattern::Case::Sensitive;
99101
let state = gix_worktree::cache::State::for_add(
100-
Default::default(), // TODO: attribute tests
102+
Default::default(),
101103
gix_worktree::cache::state::Ignore::new(
102104
gix_ignore::Search::from_overrides(vec!["!force-include"]),
103105
gix_ignore::Search::from_git_dir(&git_dir, Some(user_exclude_path), &mut buf)?,
@@ -159,5 +161,10 @@ fn check_against_baseline() -> crate::Result {
159161
let platform = cache.at_entry("User-file-ANYWHERE", Some(false), |oid, buf| odb.find_blob(oid, buf))?;
160162
let m = platform.matching_exclude_pattern().expect("match");
161163
assert_eq!(m.pattern.text, "user-file-anywhere");
164+
165+
cache.set_case(Case::Fold);
166+
let platform = cache.at_entry("User-Dir-ANYWHERE", Some(true), |oid, buf| odb.find_blob(oid, buf))?;
167+
let m = platform.matching_exclude_pattern().expect("match");
168+
assert_eq!(m.pattern.text, "user-Dir-anywhere");
162169
Ok(())
163170
}

0 commit comments

Comments
 (0)