Skip to content

Commit 461b374

Browse files
committed
List direct files only for ref name prefix
Previously, `refs/heads/foo/bar` would be listed when running `repo.references()?.prefixed("refs/heads/b")`. The code identified that the last component was not a directory and started to match it as a filename prefix for all files in all recursive directories, effectively matching `refs/heads/**/b*`. This commit makes sure to only list direct files and not walk the directory tree recursively. Fixes GitoxideLabs#1934.
1 parent 1612c73 commit 461b374

File tree

3 files changed

+18
-5
lines changed

3 files changed

+18
-5
lines changed

gix-ref/src/store/file/loose/iter.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,22 @@ pub(in crate::store_impl::file) struct SortedLoosePaths {
1414

1515
impl SortedLoosePaths {
1616
pub fn at(path: &Path, base: PathBuf, filename_prefix: Option<BString>, precompose_unicode: bool) -> Self {
17+
let has_filename_prefix = filename_prefix.is_some();
1718
SortedLoosePaths {
1819
base,
1920
filename_prefix,
2021
file_walk: path.is_dir().then(|| {
2122
// serial iteration as we expect most refs in packed-refs anyway.
22-
gix_features::fs::walkdir_sorted_new(
23+
let mut walkdir = gix_features::fs::walkdir_sorted_new(
2324
path,
2425
gix_features::fs::walkdir::Parallelism::Serial,
2526
precompose_unicode,
26-
)
27-
.into_iter()
27+
);
28+
// With a filename prefix, only files within the base are of interest.
29+
if has_filename_prefix {
30+
walkdir = walkdir.max_depth(1);
31+
}
32+
walkdir.into_iter()
2833
}),
2934
}
3035
}

gix-ref/tests/fixtures/make_ref_repository.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ echo "ref: refs/tags/multi-link-target2" > .git/refs/heads/multi-link-target1
2222
echo "ref: refs/remotes/origin/multi-link-target3" > .git/refs/tags/multi-link-target2
2323
git rev-parse HEAD > .git/refs/remotes/origin/multi-link-target3
2424

25+
# Regression test for issue #1934 where prefix refs/m matched refs/heads/main.
26+
# Testing both single subdirectory with 'sub-dir'.
27+
mkdir .git/refs/heads/sub-dir
28+
echo "ref: refs/remotes/origin/main" > .git/refs/heads/sub-dir/d2
29+
echo "ref: refs/remotes/origin/main" > .git/refs/remotes/origin/heads
2530

2631
echo "ref: refs/loop-b" > .git/refs/loop-a
2732
echo "ref: refs/loop-a" > .git/refs/loop-b

gix-ref/tests/refs/file/store/iter.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ fn loose_iter_with_broken_refs() -> crate::Result {
262262
let store = store()?;
263263

264264
let mut actual: Vec<_> = store.loose_iter()?.collect();
265-
assert_eq!(actual.len(), 16);
265+
assert_eq!(actual.len(), 18);
266266
actual.sort_by_key(Result::is_err);
267267
let first_error = actual
268268
.iter()
@@ -271,7 +271,7 @@ fn loose_iter_with_broken_refs() -> crate::Result {
271271
.expect("there is an error");
272272

273273
assert_eq!(
274-
first_error, 15,
274+
first_error, 17,
275275
"there is exactly one invalid item, and it didn't abort the iterator most importantly"
276276
);
277277
#[cfg(not(windows))]
@@ -296,10 +296,12 @@ fn loose_iter_with_broken_refs() -> crate::Result {
296296
"heads/dt1",
297297
"heads/main",
298298
"heads/multi-link-target1",
299+
"heads/sub-dir/d2",
299300
"loop-a",
300301
"loop-b",
301302
"multi-link",
302303
"remotes/origin/HEAD",
304+
"remotes/origin/heads",
303305
"remotes/origin/main",
304306
"remotes/origin/multi-link-target3",
305307
"tags/dt1",
@@ -349,6 +351,7 @@ fn loose_iter_with_prefix() -> crate::Result {
349351
"refs/heads/dt1",
350352
"refs/heads/main",
351353
"refs/heads/multi-link-target1",
354+
"refs/heads/sub-dir/d2",
352355
]
353356
.into_iter()
354357
.map(String::from)

0 commit comments

Comments
 (0)