create src

This commit is contained in:
awfixer
2026-03-11 02:04:19 -07:00
commit 52f7a22bf2
2595 changed files with 402870 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
lints.workspace = true
[package]
name = "src-worktree-tests"
version = "0.0.0"
repository = "https://github.com/GitoxideLabs/gitoxide"
license = "MIT OR Apache-2.0"
description = "A crate for testing the src-worktree crate with feature toggles"
authors = ["Sebastian Thiel <sebastian.thiel@icloud.com>"]
edition = "2021"
rust-version = "1.82"
publish = false
[[test]]
name = "integrate"
path = "integrate.rs"
[features]
src-features-parallel = ["src-features/parallel"]
[dev-dependencies]
src-worktree = { path = "..", features = ["attributes"] }
src-index = { path = "../../src-index" }
src-fs = { path = "../../src-fs" }
src-hash = { path = "../../src-hash", features = ["sha256"] }
src-object = { path = "../../src-object" }
src-glob = { path = "../../src-glob" }
src-path = { path = "../../src-path" }
src-attributes = { path = "../../src-attributes" }
src-ignore = { path = "../../src-ignore" }
src-features = { path = "../../src-features" }
src-discover = { path = "../../src-discover" }
bstr = { version = "1.12.0", default-features = false }
src-testtools = { path = "../../tests/tools" }
src-odb = { path = "../../src-odb" }
symlink = "0.1.0"

View File

@@ -0,0 +1,3 @@
make_ignore_and_attributes_setup.tar
make_attributes_baseline.tar
symlink_stack.tar

View File

@@ -0,0 +1,108 @@
#!/usr/bin/env bash
set -eu -o pipefail
mkdir basics;
function baseline() {
{
echo "$1"
GIT_ATTR_NOSYSTEM=1 git -c core.attributesFile=$PWD/user.attributes check-attr -a "$1"
echo
} >> baseline
{
echo "$1"
GIT_ATTR_NOSYSTEM=1 git -c core.attributesFile=$PWD/user.attributes check-attr info test -- "$1"
echo
} >> baseline.selected
}
(cd basics
git init
# based on https://github.com/git/git/blob/140b9478dad5d19543c1cb4fd293ccec228f1240/t/t0003-attributes.sh#L45
mkdir -p a/b/d a/c b
(
echo "[attr]notest !test"
echo "\" d \" test=d"
echo " e test=e"
echo " e\" test=e"
echo "f test=f"
echo "a/i test=a/i"
echo "onoff test -test"
echo "offon -test test"
echo "no notest"
echo "A/e/F test=A/e/F"
echo "\!escaped test-escaped"
echo "**/recursive test-double-star-slash"
echo "a**f test-double-star-no-slash"
echo "dir-slash/ never"
echo "dir/** always"
) > .gitattributes
(
echo "g test=a/g"
echo "b/g test=a/b/g"
) > a/.gitattributes
(
echo "h test=a/b/h"
echo "d/* test=a/b/d/*"
echo "d/yes notest"
) > a/b/.gitattributes
(
echo "global test=global"
echo "z/x/a global-no-wildcard-case-test"
echo "z/x/* global-wildcard-case-test"
) > user.attributes
git add . && git commit -qm c1
(
echo "global test=global"
echo "* info=attributes"
) > .git/info/attributes
baseline z/x/a
baseline Z/x/a
baseline z/x/A
baseline Z/X/a
baseline Z/x/a
baseline " d "
baseline e
baseline f
baseline dir-slash
baseline dir-slash/a
baseline dir
baseline dir/a
baseline recursive
baseline a/recursive
baseline a/b/recursive
baseline a/b/c/recursive
baseline "!escaped"
baseline af
baseline axf
baseline a/b/d/no
baseline a/e/f
baseline a/f
baseline a/b/d/g
baseline a/B/D/g
baseline b/g
baseline a/c/f
baseline "e\""
baseline a/i
baseline A/b/h
baseline A/B/D/NO
baseline subdir/a/i
baseline onoff
baseline offon
baseline no
baseline A/e/F
baseline a/e/F
baseline a/e/f
baseline a/g
baseline a/b/g
baseline a/b/h
baseline a/b/d/ANY
baseline a/b/d/yes
baseline global
)

View File

@@ -0,0 +1,132 @@
#!/usr/bin/env bash
set -eu -o pipefail
cat <<EOF >user.exclude
# a custom exclude configured per user
user-file-anywhere
/user-file-from-top
user-Dir-anywhere/
/user-dir-from-top
user-subdir/file
**/user-subdir-anywhere/file
a/b/*
z/x
EOF
mkdir repo;
(cd repo
git init -q
git config core.excludesFile ../user.exclude
cat <<EOF >.git/info/exclude
# a sample .git/info/exclude
file-anywhere
/file-from-top
dir-anywhere/
/dir-from-top
subdir/file
**/subdir-anywhere/file
EOF
cat <<EOF >.gitignore
# a sample .gitignore
top-level-local-file-anywhere
d/e/*
e/f
EOF
mkdir dir-with-ignore
cat <<EOF >dir-with-ignore/.gitignore
# a sample .gitignore
sub-level-local-file-anywhere
sub-Level-dir-anywhere/
!/negated
/negated-dir/
!/negated-dir/
EOF
git add .gitignore dir-with-ignore
git commit --allow-empty -m "init"
# just add this git-ignore file, so it's a new file that doesn't exist on disk.
mkdir other-dir-with-ignore
skip_worktree_ignore=other-dir-with-ignore/.gitignore
cat <<EOF >"$skip_worktree_ignore"
# a sample .gitignore
other-sub-level-local-file-anywhere
other-sub-level-dir-anywhere/
EOF
git add $skip_worktree_ignore && git update-index --skip-worktree $skip_worktree_ignore && rm $skip_worktree_ignore
mkdir user-dir-anywhere user-dir-from-top dir-anywhere dir-from-top
mkdir -p dir/user-dir-anywhere dir/dir-anywhere
git check-ignore -vn --stdin 2>&1 <<EOF >git-check-ignore.baseline || :
dir-with-ignore/sub-level-dir-anywhere/
dir-with-ignore/foo/Sub-level-dir-anywhere/
dir-with-ignore/Sub-level-dir-anywhere
user-file-anywhere
dir/user-file-anywhere
user-file-from-top
no-match/user-file-from-top
USER-dir-anywhere
user-dir-from-top
no-match/user-dir-from-top
user-subdir/file
subdir/user-subdir-anywhere/file
user-dir-anywhere/hello
dir/user-dir-anywhere/hello
file-anywhere
dir/file-anywhere
file-from-top
no-match/file-from-top
dir-anywhere
dir/dir-anywhere
dir-from-top
no-match/dir-from-top
subdir/file
subdir/subdir-anywhere/file
top-level-local-file-anywhere
dir/top-level-local-file-anywhere
no-match/sub-level-local-file-anywhere
dir-with-ignore/sub-level-local-file-anywhere
dir-with-ignore/sub-dir/sub-level-local-file-anywhere
other-dir-with-ignore/other-sub-level-local-file-anywhere
other-dir-with-ignore/sub-level-local-file-anywhere
other-dir-with-ignore/sub-dir/other-sub-level-local-file-anywhere
other-dir-with-ignore/no-match/sub-level-local-file-anywhere
non-existing/dir-anywhere
dir-anywhere/hello
dir/dir-anywhere/hello
no-match/sub-level-dir-anywhere/hello
no-match/other-sub-level-dir-anywhere/hello
dir-with-ignore/sub-level-dir-anywhere/hello
dir-with-ignore/sub-level-dir-anywhere/
other-dir-with-ignore/sub-level-dir-anywhere/hello
other-dir-with-ignore/other-sub-level-dir-anywhere/hello
other-dir-with-ignore/other-sub-level-dir-anywhere/
dir-with-ignore/negated
dir-with-ignore/negated-dir/hello
User-file-ANYWHERE
User-Dir-ANYWHERE
a/b/C
a/B/c
A/B/C
z/x
Z/x
z/X
Z/X
d/e/F
d/e/f
D/e/F
D/E/F
e/f
e/F
E/f
E/F
EOF
)

View File

@@ -0,0 +1,28 @@
#!/usr/bin/env bash
set -eu -o pipefail
git init -q
mkdir -p tld tld/sd
cat <<EOF >.gitignore
# directory exclude
tld/
!tld/file
EOF
cat <<EOF >tld/.gitignore
sd/
!sd/
!file
EOF
git check-ignore -vn --stdin 2>&1 <<EOF >git-check-ignore.baseline || :
tld
tld/
tld/file
tld/sd
tld/sd/
EOF

View File

@@ -0,0 +1,26 @@
#!/usr/bin/env bash
set -eu -o pipefail
git init base;
(cd base
touch file
mkdir dir
touch dir/file-in-dir
(cd dir
ln -s file-in-dir filelink
mkdir subdir
ln -s subdir dirlink
)
ln -s file root-filelink
ln -s dir root-dirlink
cat <<EOF > .gitattributes
/file file-attr
/dir/file-in-dir dir-file-attr
EOF
git add . && git commit -m "init"
)
ln -s base symlink-base

View File

@@ -0,0 +1,2 @@
mod worktree;
use worktree::*;

View File

@@ -0,0 +1,9 @@
use gix_hash::ObjectId;
mod stack;
pub use gix_testtools::Result;
pub fn hex_to_id(hex: &str) -> ObjectId {
ObjectId::from_hex(hex.as_bytes()).expect("40 bytes hex")
}

View File

@@ -0,0 +1,139 @@
use bstr::ByteSlice;
use gix_attributes::search::Outcome;
use gix_worktree::stack::state;
use crate::worktree::stack::probe_case;
#[test]
fn baseline() -> crate::Result {
// Due to the way our setup differs from gits dynamic stack (which involves trying to read files from disk
// by path) we can only test one case baseline, so we require multiple platforms (or filesystems) to run this.
let case = probe_case()?;
let dir = gix_testtools::scripted_fixture_read_only_standalone("make_attributes_baseline.sh")?;
let base = dir.join("basics");
let git_dir = base.join(".git");
let mut buf = Vec::new();
let mut collection = gix_attributes::search::MetadataCollection::default();
let state = gix_worktree::stack::State::for_checkout(
false,
gix_worktree::validate::path::component::Options {
protect_windows: false,
protect_ntfs: false,
..Default::default()
},
state::Attributes::new(
gix_attributes::Search::new_globals([base.join("user.attributes")], &mut buf, &mut collection)?,
Some(git_dir.join("info").join("attributes")),
gix_worktree::stack::state::attributes::Source::WorktreeThenIdMapping,
collection,
),
);
let mut cache = gix_worktree::Stack::new(&base, state, case, buf, vec![]);
let mut actual = cache.attribute_matches();
let input = std::fs::read(base.join("baseline"))?;
for (rela_path, expected) in (baseline::Expectations { lines: input.lines() }) {
let entry = cache.at_entry(rela_path, None, &gix_object::find::Never)?;
let has_match = entry.matching_attributes(&mut actual);
assert_eq!(
has_match,
!expected.is_empty(),
"matches are reported when git reports them, too"
);
assert_references(&actual);
assert_eq!(actual.iter_selected().count(), 0, "no selection made yet");
let actual: Vec<_> = actual
.iter()
.filter_map(|m| (!m.assignment.state.is_unspecified()).then_some(m.assignment))
.collect();
assert_eq!(actual, expected, "we have the same matches: {rela_path:?}");
assert_eq!(has_match, !actual.is_empty());
}
let mut actual = cache.selected_attribute_matches(["info", "test"]);
let input = std::fs::read(base.join("baseline.selected"))?;
for (rela_path, expected) in (baseline::Expectations { lines: input.lines() }) {
let entry = cache.at_entry(rela_path, None, &gix_object::find::Never)?;
let has_match = entry.matching_attributes(&mut actual);
assert_eq!(
has_match,
!expected.is_empty(),
"matches are reported when git reports them, too"
);
assert_references(&actual);
let actual: Vec<_> = actual.iter_selected().map(|m| m.assignment).collect();
assert_eq!(actual, expected, "we have the same matches: {rela_path:?}");
assert_eq!(has_match, !actual.is_empty());
}
Ok(())
}
fn assert_references(out: &Outcome) {
for m in out.iter() {
if let Some(source) = m.kind.source_id() {
let sm = out
.match_by_id(source)
.expect("sources are always available in the outcome");
assert_ne!(
sm.assignment.name, m.assignment.name,
"it's impossible to resolve to ourselves"
);
}
}
}
mod baseline {
use bstr::{BStr, ByteSlice};
use gix_attributes::{AssignmentRef, StateRef};
pub struct Expectations<'a> {
pub lines: bstr::Lines<'a>,
}
impl<'a> Iterator for Expectations<'a> {
type Item = (
&'a BStr,
// Names might refer to attributes or macros
Vec<AssignmentRef<'a>>,
);
fn next(&mut self) -> Option<Self::Item> {
let path = self.lines.next()?;
let mut assignments = Vec::new();
loop {
let line = self.lines.next()?;
if line.is_empty() {
return Some((path.as_bstr(), assignments));
}
let mut prev = None;
let mut tokens = line.splitn(3, |b| {
let is_match = *b == b' ' && prev.take() == Some(b':');
prev = Some(*b);
is_match
});
if let Some(((_path, attr), info)) = tokens.next().zip(tokens.next()).zip(tokens.next()) {
let state = match info {
b"set" => StateRef::Set,
b"unset" => StateRef::Unset,
b"unspecified" => StateRef::Unspecified,
_ => StateRef::from_bytes(info),
};
let attr = attr.trim_end_with(|b| b == ':');
assignments.push(AssignmentRef {
name: gix_attributes::NameRef::try_from(attr.as_bstr()).expect("valid attributes"),
state,
});
} else {
unreachable!("invalid line format: {line:?}", line = line.as_bstr())
}
}
}
}
}

View File

@@ -0,0 +1,139 @@
use std::path::Path;
use gix_testtools::tempfile::{tempdir, TempDir};
use gix_worktree::{stack, Stack};
const IS_FILE: Option<gix_index::entry::Mode> = Some(gix_index::entry::Mode::FILE);
const IS_DIR: Option<gix_index::entry::Mode> = Some(gix_index::entry::Mode::DIR);
#[test]
fn root_is_assumed_to_exist_and_files_in_root_do_not_create_directory() -> crate::Result {
let dir = tempdir()?;
let mut cache = Stack::new(
dir.path().join("non-existing-root"),
stack::State::for_checkout(false, Default::default(), Default::default()),
Default::default(),
Vec::new(),
Default::default(),
);
assert_eq!(cache.statistics().delegate.num_mkdir_calls, 0);
let path = cache.at_path("hello", IS_FILE, &gix_object::find::Never)?.path();
assert!(!path.parent().unwrap().exists(), "prefix itself is never created");
assert_eq!(cache.statistics().delegate.num_mkdir_calls, 0);
Ok(())
}
#[test]
fn directory_paths_are_created_in_full() {
let (mut cache, _tmp) = new_cache();
for (name, mode) in [
("dir", IS_DIR),
("submodule", IS_DIR),
("file", IS_FILE),
("exe", IS_FILE),
("link", None),
] {
let path = cache
.at_path(Path::new("dir").join(name), mode, &gix_object::find::Never)
.unwrap()
.path();
assert!(path.parent().unwrap().is_dir(), "dir exists");
}
assert_eq!(cache.statistics().delegate.num_mkdir_calls, 3);
}
#[test]
fn existing_directories_are_fine() -> crate::Result {
let (mut cache, tmp) = new_cache();
std::fs::create_dir(tmp.path().join("dir"))?;
let path = cache.at_path("dir/file", IS_FILE, &gix_object::find::Never)?.path();
assert!(path.parent().unwrap().is_dir(), "directory is still present");
assert!(!path.exists(), "it won't create the file");
assert_eq!(cache.statistics().delegate.num_mkdir_calls, 1);
Ok(())
}
#[test]
fn validation_to_each_component() -> crate::Result {
let (mut cache, tmp) = new_cache();
let err = cache
.at_path("valid/.gIt", IS_FILE, &gix_object::find::Never)
.unwrap_err();
assert_eq!(
cache.statistics().delegate.num_mkdir_calls,
1,
"the valid directory was created"
);
assert!(tmp.path().join("valid").is_dir(), "it was actually created");
assert_eq!(err.to_string(), "The .git name may never be used");
Ok(())
}
#[test]
fn symlinks_or_files_in_path_are_forbidden_or_unlinked_when_forced() -> crate::Result {
let (mut cache, tmp) = new_cache();
let forbidden = tmp.path().join("forbidden");
std::fs::create_dir(&forbidden)?;
symlink::symlink_dir(&forbidden, tmp.path().join("link-to-dir"))?;
std::fs::write(tmp.path().join("file-in-dir"), [])?;
for dirname in &["file-in-dir", "link-to-dir"] {
if let stack::State::CreateDirectoryAndAttributesStack {
unlink_on_collision, ..
} = cache.state_mut()
{
*unlink_on_collision = false;
}
let relative_path = format!("{dirname}/file");
assert_eq!(
cache
.at_path(&*relative_path, IS_FILE, &gix_object::find::Never)
.unwrap_err()
.kind(),
std::io::ErrorKind::AlreadyExists
);
}
assert_eq!(
cache.statistics().delegate.num_mkdir_calls,
2,
"it tries to create each directory once, but it's a file"
);
cache.take_statistics();
for dirname in &["link-to-dir", "file-in-dir"] {
if let stack::State::CreateDirectoryAndAttributesStack {
unlink_on_collision, ..
} = cache.state_mut()
{
*unlink_on_collision = true;
}
let relative_path = format!("{dirname}/file");
let path = cache
.at_path(&*relative_path, IS_FILE, &gix_object::find::Never)?
.path();
assert!(path.parent().unwrap().is_dir(), "directory was forcefully created");
assert!(!path.exists());
}
assert_eq!(
cache.statistics().delegate.num_mkdir_calls,
4,
"like before, but it unlinks what's there and tries again"
);
Ok(())
}
fn new_cache() -> (Stack, TempDir) {
let dir = tempdir().unwrap();
let cache = Stack::new(
dir.path(),
stack::State::for_checkout(false, Default::default(), Default::default()),
Default::default(),
Vec::new(),
Default::default(),
);
(cache, dir)
}

View File

@@ -0,0 +1,186 @@
use std::fs::Metadata;
use bstr::{BStr, ByteSlice};
use gix_fs::stack::ToNormalPathComponents;
use gix_index::entry::Mode;
use gix_worktree::{stack::state::ignore::Source, Stack};
use crate::{hex_to_id, worktree::stack::probe_case};
struct IgnoreExpectations<'a> {
lines: bstr::Lines<'a>,
}
impl<'a> Iterator for IgnoreExpectations<'a> {
type Item = (&'a BStr, Option<(&'a BStr, usize, &'a BStr)>);
fn next(&mut self) -> Option<Self::Item> {
let line = self.lines.next()?;
let (left, value) = line.split_at(line.find_byte(b'\t').unwrap());
let value = value[1..].as_bstr();
let source_and_line = if left == b"::" {
None
} else {
let mut tokens = left.split(|b| *b == b':');
let source = tokens.next().unwrap().as_bstr();
let line_number: usize = tokens.next().unwrap().to_str_lossy().parse().ok().unwrap();
let pattern = tokens.next().unwrap().as_bstr();
Some((source, line_number, pattern))
};
Some((value, source_and_line))
}
}
#[test]
fn exclude_by_dir_is_handled_just_like_git() {
let dir = gix_testtools::scripted_fixture_read_only_standalone("make_special_exclude_case.sh").unwrap();
let git_dir = dir.join(".git");
let mut buf = Vec::new();
let case = gix_glob::pattern::Case::Sensitive;
let state = gix_worktree::stack::State::for_add(
Default::default(),
gix_worktree::stack::state::Ignore::new(
Default::default(),
gix_ignore::Search::from_git_dir(&git_dir, None, &mut buf, Default::default()).unwrap(),
None,
Source::WorktreeThenIdMappingIfNotSkipped,
Default::default(),
),
);
let mut cache = Stack::new(&dir, state, case, buf, Default::default());
let baseline = std::fs::read(git_dir.parent().unwrap().join("git-check-ignore.baseline")).unwrap();
let expectations = IgnoreExpectations {
lines: baseline.lines(),
};
struct FindError;
impl gix_object::Find for FindError {
fn try_find<'a>(
&self,
_id: &gix_hash::oid,
_buffer: &'a mut Vec<u8>,
) -> Result<Option<gix_object::Data<'a>>, gix_object::find::Error> {
Err(std::io::Error::other("unreachable").into())
}
}
for (relative_entry, source_and_line) in expectations {
let (source, line, expected_pattern) = source_and_line.expect("every value is matched");
let relative_path = gix_path::from_byte_slice(relative_entry);
let is_dir = dir.join(relative_path).metadata().ok().map(metadata_to_mode);
let platform = cache.at_entry(relative_entry, is_dir, &FindError).unwrap();
let match_ = platform.matching_exclude_pattern().expect("match all values");
let _is_excluded = platform.is_excluded();
assert_eq!(
match_.pattern.to_string(),
expected_pattern,
"we perfectly agree with git"
);
assert_eq!(
expected_pattern, "tld/",
"each entry matches on the main directory exclude, ignoring negations entirely"
);
// TODO: adjust baseline to also include precious files.
assert_eq!(
match_.kind,
gix_ignore::Kind::Expendable,
"for now all patterns are expendable until precious files are supported by git"
);
assert_eq!(line, 2);
assert_eq!(source, ".gitignore");
}
}
fn metadata_to_mode(meta: Metadata) -> Mode {
if meta.is_dir() {
gix_index::entry::Mode::DIR
} else {
gix_index::entry::Mode::FILE
}
}
#[test]
fn check_against_baseline() -> crate::Result {
let dir = gix_testtools::scripted_fixture_read_only_standalone("make_ignore_and_attributes_setup.sh")?;
let worktree_dir = dir.join("repo");
let git_dir = worktree_dir.join(".git");
let mut buf = Vec::new();
let user_exclude_path = dir.join("user.exclude");
assert!(user_exclude_path.is_file());
// Due to the way our setup differs from gits dynamic stack (which involves trying to read files from disk
// by path) we can only test one case baseline, so we require multiple platforms (or filesystems) to run this.
let case = probe_case()?;
let mut index = gix_index::File::at(git_dir.join("index"), gix_hash::Kind::Sha1, false, Default::default())?;
let odb = gix_odb::at(git_dir.join("objects"))?;
let parse_ignore = gix_ignore::search::Ignore::default();
let state = gix_worktree::stack::State::for_add(
Default::default(),
gix_worktree::stack::state::Ignore::new(
gix_ignore::Search::from_overrides(["!force-include"], parse_ignore),
gix_ignore::Search::from_git_dir(&git_dir, Some(user_exclude_path), &mut buf, parse_ignore)?,
None,
Source::WorktreeThenIdMappingIfNotSkipped,
parse_ignore,
),
);
let paths_storage = index.take_path_backing();
let attribute_files_in_index = state.id_mappings_from_index(&index, &paths_storage, case);
assert_eq!(
attribute_files_in_index,
vec![(
"other-dir-with-ignore/.gitignore".into(),
hex_to_id("5c7e0ed672d3d31d83a3df61f13cc8f7b22d5bfd")
)]
);
let mut cache = Stack::new(&worktree_dir, state, case, buf, attribute_files_in_index);
let baseline = std::fs::read(git_dir.parent().unwrap().join("git-check-ignore.baseline"))?;
let expectations = IgnoreExpectations {
lines: baseline.lines(),
};
for (relative_entry, source_and_line) in expectations {
let relative_path = gix_path::from_byte_slice(relative_entry);
let is_dir = worktree_dir.join(relative_path).metadata().ok().map(metadata_to_mode);
let platform = cache.at_entry(relative_entry, is_dir, &odb)?;
let match_ = platform.matching_exclude_pattern();
let is_excluded = platform.is_excluded();
match (match_, source_and_line) {
(None, None) => {
assert!(!is_excluded);
}
(Some(m), Some((source_file, line, pattern))) => {
assert_eq!(m.pattern.to_string(), pattern);
assert_eq!(m.sequence_number, line);
// TODO: adjust baseline to also include precious files.
if !m.pattern.is_negative() {
assert_eq!(
m.kind,
platform.excluded_kind().expect("it matches"),
"both values agree, no matter which method is used"
);
}
// Paths read from the index are relative to the repo, and they don't exist locally due tot skip-worktree
if m.source.is_some_and(std::path::Path::exists) {
assert_eq!(
m.source.map(|p| p.canonicalize().unwrap()),
Some(worktree_dir.join(source_file.to_str_lossy().as_ref()).canonicalize()?)
);
}
}
(Some(actual), None) if actual.pattern.is_negative() => {
// OK: we provide negative patterns that matched on paths if there was no other match, while git doesn't.
}
(actual, expected) => {
panic!(
"actual {actual:?} didn't match {expected:?} at '{relative_entry}': {components:?}",
components = relative_entry.to_normal_path_components().collect::<Vec<_>>()
);
}
}
}
Ok(())
}

View File

@@ -0,0 +1,23 @@
use gix_glob::pattern::Case;
mod create_directory;
mod attributes;
mod ignore;
fn probe_case() -> crate::Result<Case> {
Ok(
if gix_fs::Capabilities::probe(
&gix_discover::upwards(".".as_ref())?
.0
.into_repository_and_work_tree_directories()
.0,
)
.ignore_case
{
Case::Fold
} else {
Case::Sensitive
},
)
}