From 6c77b541b476656827ee0542a650b9731ba549cf Mon Sep 17 00:00:00 2001 From: Colin Nelson Date: Tue, 24 Jun 2025 15:20:31 -0700 Subject: [PATCH 1/5] feat!: walkdir_sorted_new adds max_depth parameter max_depth parameter determines the maximum depth the WalkDir will recurse into. Example values: * 0 -> Returns only the root path with no children. * 1 -> Returns the root path, with children. * 2..n -> Returns the root path, children and {n}-grandchildren --- gix-features/src/fs.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/gix-features/src/fs.rs b/gix-features/src/fs.rs index 1838dcdbd87..a1769cd0b21 100644 --- a/gix-features/src/fs.rs +++ b/gix-features/src/fs.rs @@ -207,9 +207,14 @@ pub mod walkdir { /// Instantiate a new directory iterator which will not skip hidden files and is sorted, with the given level of `parallelism`. /// /// Use `precompose_unicode` to represent the `core.precomposeUnicode` configuration option. - pub fn walkdir_sorted_new(root: &Path, _: Parallelism, precompose_unicode: bool) -> WalkDir { + /// Use `max_depth` to limit the depth of the recursive walk. + /// * 0 -> Returns only the root path with no children + /// * 1 -> Root directory and children. + /// * 2..n -> Root directory, children and {n}-grandchildren + pub fn walkdir_sorted_new(root: &Path, _: Parallelism, max_depth: usize, precompose_unicode: bool) -> WalkDir { WalkDir { inner: WalkDirImpl::new(root) + .max_depth(max_depth) .sort_by(|a, b| { let storage_a; let storage_b; From a2741da85fe04907f8773a99813e3802333b402d Mon Sep 17 00:00:00 2001 From: Colin Nelson Date: Tue, 24 Jun 2025 15:20:31 -0700 Subject: [PATCH 2/5] adapt to changes in gix_features::walkdir_sorted_new --- gix-ref/src/store/file/loose/iter.rs | 1 + gix-submodule/tests/file/baseline.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/gix-ref/src/store/file/loose/iter.rs b/gix-ref/src/store/file/loose/iter.rs index 31d91a4b094..2b8be91da4b 100644 --- a/gix-ref/src/store/file/loose/iter.rs +++ b/gix-ref/src/store/file/loose/iter.rs @@ -24,6 +24,7 @@ impl SortedLoosePaths { gix_features::fs::walkdir_sorted_new( path, gix_features::fs::walkdir::Parallelism::Serial, + usize::MAX, precompose_unicode, ) .into_iter() diff --git a/gix-submodule/tests/file/baseline.rs b/gix-submodule/tests/file/baseline.rs index 6971cdb33a7..513b5051af0 100644 --- a/gix-submodule/tests/file/baseline.rs +++ b/gix-submodule/tests/file/baseline.rs @@ -61,7 +61,7 @@ fn common_values_and_names_by_path() -> crate::Result { fn module_files() -> impl Iterator { let dir = gix_testtools::scripted_fixture_read_only("basic.sh").expect("valid fixture"); - gix_features::fs::walkdir_sorted_new(&dir, Parallelism::Serial, false) + gix_features::fs::walkdir_sorted_new(&dir, Parallelism::Serial, usize::MAX, false) .follow_links(false) .into_iter() .filter_map(move |entry| { From fdf5153d9fe5e7c059b5a9687b7041e16ba54683 Mon Sep 17 00:00:00 2001 From: Colin Nelson Date: Tue, 24 Jun 2025 15:20:31 -0700 Subject: [PATCH 3/5] feat: refs support pseudo refs --- gix-ref/src/store/file/loose/iter.rs | 20 +++- gix-ref/src/store/file/overlay_iter.rs | 111 +++++++++++++----- .../make_pref_repository.tar | Bin 0 -> 81920 bytes .../tests/fixtures/make_pref_repository.sh | 41 +++++++ gix-ref/tests/refs/main.rs | 1 + gix-ref/tests/refs/pseudo_refs.rs | 21 ++++ 6 files changed, 160 insertions(+), 34 deletions(-) create mode 100644 gix-ref/tests/fixtures/generated-archives/make_pref_repository.tar create mode 100755 gix-ref/tests/fixtures/make_pref_repository.sh create mode 100644 gix-ref/tests/refs/pseudo_refs.rs diff --git a/gix-ref/src/store/file/loose/iter.rs b/gix-ref/src/store/file/loose/iter.rs index 2b8be91da4b..a44ff92b7b8 100644 --- a/gix-ref/src/store/file/loose/iter.rs +++ b/gix-ref/src/store/file/loose/iter.rs @@ -11,20 +11,31 @@ pub(in crate::store_impl::file) struct SortedLoosePaths { pub(crate) base: PathBuf, /// An prefix like `refs/heads/foo/` or `refs/heads/prefix` that a returned reference must match against.. prefix: Option, + /// A suffix like `HEAD` that a returned reference must match against.. + suffix: Option, file_walk: Option, } impl SortedLoosePaths { - pub fn at(path: &Path, base: PathBuf, prefix: Option, precompose_unicode: bool) -> Self { + pub fn at( + path: &Path, + base: PathBuf, + prefix: Option, + suffix: Option, + root_only: bool, + precompose_unicode: bool, + ) -> Self { + let depth = if root_only { 1 } else { usize::MAX }; SortedLoosePaths { base, prefix, + suffix, file_walk: path.is_dir().then(|| { // serial iteration as we expect most refs in packed-refs anyway. gix_features::fs::walkdir_sorted_new( path, gix_features::fs::walkdir::Parallelism::Serial, - usize::MAX, + depth, precompose_unicode, ) .into_iter() @@ -57,6 +68,11 @@ impl Iterator for SortedLoosePaths { continue; } } + if let Some(suffix) = &self.suffix { + if !full_name.ends_with(suffix) { + continue; + } + } if gix_validate::reference::name_partial(full_name.as_bstr()).is_ok() { let name = FullName(full_name); return Some(Ok((full_path, name))); diff --git a/gix-ref/src/store/file/overlay_iter.rs b/gix-ref/src/store/file/overlay_iter.rs index d6c3b5ae8c8..6850fba2137 100644 --- a/gix-ref/src/store/file/overlay_iter.rs +++ b/gix-ref/src/store/file/overlay_iter.rs @@ -10,7 +10,7 @@ use gix_object::bstr::ByteSlice; use gix_path::RelativePath; use crate::{ - file::{loose, loose::iter::SortedLoosePaths}, + file::loose::{self, iter::SortedLoosePaths}, store_impl::{file, packed}, BStr, FullName, Namespace, Reference, }; @@ -85,36 +85,48 @@ impl<'p> LooseThenPacked<'p, '_> { } fn convert_loose(&mut self, res: std::io::Result<(PathBuf, FullName)>) -> Result { - let (refpath, name) = res.map_err(Error::Traversal)?; - std::fs::File::open(&refpath) - .and_then(|mut f| { - self.buf.clear(); - f.read_to_end(&mut self.buf) - }) - .map_err(|err| Error::ReadFileContents { - source: err, - path: refpath.to_owned(), - })?; - loose::Reference::try_from_path(name, &self.buf) - .map_err(|err| { - let relative_path = refpath - .strip_prefix(self.git_dir) - .ok() - .or_else(|| { - self.common_dir - .and_then(|common_dir| refpath.strip_prefix(common_dir).ok()) - }) - .expect("one of our bases contains the path"); - Error::ReferenceCreation { - source: err, - relative_path: relative_path.into(), - } - }) - .map(Into::into) - .map(|r| self.strip_namespace(r)) + convert_loose(&mut self.buf, self.git_dir, self.common_dir, self.namespace, res) } } +pub(crate) fn convert_loose( + buf: &mut Vec, + git_dir: &Path, + common_dir: Option<&Path>, + namespace: Option<&Namespace>, + res: std::io::Result<(PathBuf, FullName)>, +) -> Result { + let (refpath, name) = res.map_err(Error::Traversal)?; + std::fs::File::open(&refpath) + .and_then(|mut f| { + buf.clear(); + f.read_to_end(buf) + }) + .map_err(|err| Error::ReadFileContents { + source: err, + path: refpath.to_owned(), + })?; + loose::Reference::try_from_path(name, buf) + .map_err(|err| { + let relative_path = refpath + .strip_prefix(git_dir) + .ok() + .or_else(|| common_dir.and_then(|common_dir| refpath.strip_prefix(common_dir).ok())) + .expect("one of our bases contains the path"); + Error::ReferenceCreation { + source: err, + relative_path: relative_path.into(), + } + }) + .map(Into::into) + .map(|mut r: Reference| { + if let Some(namespace) = namespace { + r.strip_namespace(namespace); + } + r + }) +} + impl Iterator for LooseThenPacked<'_, '_> { type Item = Result; @@ -210,6 +222,11 @@ impl Platform<'_> { self.store .iter_prefixed_packed(prefix, self.packed.as_ref().map(|b| &***b)) } + + /// Return an iterator over the pseudo references + pub fn psuedo_refs(&self) -> std::io::Result> { + self.store.iter_pseudo_refs() + } } impl file::Store { @@ -254,6 +271,10 @@ pub(crate) enum IterInfo<'a> { /// If `true`, we will convert decomposed into precomposed unicode. precompose_unicode: bool, }, + PseudoRefs { + base: &'a Path, + precompose_unicode: bool, + }, } impl<'a> IterInfo<'a> { @@ -263,6 +284,7 @@ impl<'a> IterInfo<'a> { IterInfo::PrefixAndBase { prefix, .. } => Some(gix_path::into_bstr(*prefix)), IterInfo::BaseAndIterRoot { prefix, .. } => Some(gix_path::into_bstr(prefix.clone())), IterInfo::ComputedIterationRoot { prefix, .. } => Some(prefix.clone()), + IterInfo::PseudoRefs { .. } => None, } } @@ -271,24 +293,35 @@ impl<'a> IterInfo<'a> { IterInfo::Base { base, precompose_unicode, - } => SortedLoosePaths::at(&base.join("refs"), base.into(), None, precompose_unicode), + } => SortedLoosePaths::at(&base.join("refs"), base.into(), None, None, false, precompose_unicode), IterInfo::BaseAndIterRoot { base, iter_root, prefix: _, precompose_unicode, - } => SortedLoosePaths::at(&iter_root, base.into(), None, precompose_unicode), + } => SortedLoosePaths::at(&iter_root, base.into(), None, None, false, precompose_unicode), IterInfo::PrefixAndBase { base, prefix, precompose_unicode, - } => SortedLoosePaths::at(&base.join(prefix), base.into(), None, precompose_unicode), + } => SortedLoosePaths::at(&base.join(prefix), base.into(), None, None, false, precompose_unicode), IterInfo::ComputedIterationRoot { iter_root, base, prefix, precompose_unicode, - } => SortedLoosePaths::at(&iter_root, base.into(), Some(prefix.into_owned()), precompose_unicode), + } => SortedLoosePaths::at( + &iter_root, + base.into(), + Some(prefix.into_owned()), + None, + false, + precompose_unicode, + ), + IterInfo::PseudoRefs { + base, + precompose_unicode, + } => SortedLoosePaths::at(base, base.into(), None, Some("HEAD".into()), true, precompose_unicode), } .peekable() } @@ -354,6 +387,20 @@ impl file::Store { } } + /// Return an iterator over all pseudo references, loose or `packed`, sorted by their name. + /// + /// Errors are returned similarly to what would happen when loose and packed refs where iterated by themselves. + pub fn iter_pseudo_refs<'p>(&'_ self) -> std::io::Result> { + self.iter_from_info( + IterInfo::PseudoRefs { + base: self.git_dir(), + precompose_unicode: self.precompose_unicode, + }, + None, + None, + ) + } + /// As [`iter(…)`](file::Store::iter()), but filters by `prefix`, i.e. `refs/heads/` or /// `refs/heads/feature-`. /// Note that if a prefix isn't using a trailing `/`, like in `refs/heads/foo`, it will effectively diff --git a/gix-ref/tests/fixtures/generated-archives/make_pref_repository.tar b/gix-ref/tests/fixtures/generated-archives/make_pref_repository.tar new file mode 100644 index 0000000000000000000000000000000000000000..e8942611672e0792c012edba1bf008c46dc0f280 GIT binary patch literal 81920 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 literal 0 HcmV?d00001 diff --git a/gix-ref/tests/fixtures/make_pref_repository.sh b/gix-ref/tests/fixtures/make_pref_repository.sh new file mode 100755 index 00000000000..df3faa2eb82 --- /dev/null +++ b/gix-ref/tests/fixtures/make_pref_repository.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +set -eu -o pipefail + +git init -q + +git checkout -q -b main +git commit -q --allow-empty -m c1 +git branch dt1 +git branch d1 +git branch A + +mkdir -p .git/refs/remotes/origin +mkdir -p .git/refs/prefix/feature/sub/dir + +cp .git/refs/heads/main .git/refs/remotes/origin/ +cp .git/refs/heads/main .git/refs/d1 +cp .git/refs/heads/main .git/refs/prefix/feature-suffix +cp .git/refs/heads/main .git/refs/prefix/feature/sub/dir/algo + +echo "ref: refs/remotes/origin/main" > .git/refs/remotes/origin/HEAD +echo "notahexsha" > .git/refs/broken + +git rev-parse HEAD > .git/JIRI_HEAD +touch .git/SOME_ALL_CAPS_FILE +touch .git/refs/SHOULD_BE_EXCLUDED_HEAD + +cat <> .git/FETCH_HEAD +9064ea31fae4dc59a56bdd3a06c0ddc990ee689e branch 'main' of https://github.com/Byron/gitoxide +1b8d9e6a408e480ae1912e919c37a26e5c46639d not-for-merge branch 'faster-discovery' of https://github.com/Byron/gitoxide +43f695a9607f1f85f859f2ef944b785b5b6dd238 not-for-merge branch 'fix-823' of https://github.com/Byron/gitoxide +96267708958ead2646aae8766a50fa060739003c not-for-merge branch 'fix-bare-with-index' of https://github.com/Byron/gitoxide +1397e19375bb98522f951b8a452b08c1b35ffbac not-for-merge branch 'gix-archive' of https://github.com/Byron/gitoxide +db71ec8b7c7f2730c47dde3bb662ab56ae89ae7d not-for-merge branch 'index-from-files' of https://github.com/Byron/gitoxide +9f0c71917e57653d2e7121eae65d9385a188a8df not-for-merge branch 'moonwalk' of https://github.com/Byron/gitoxide +44d2b67de5639d4ea3d08ab030ecfe4bdfc8cbfb not-for-merge branch 'release-gix' of https://github.com/Byron/gitoxide +37c3d073b15dafcb52b2040e4b92a413c69a726d not-for-merge branch 'smart-release-without-git2' of https://github.com/Byron/gitoxide +af3608ad397784795c3758a1ac99ec6a367de9be not-for-merge branch 'walk-with-commitgraph' of https://github.com/Byron/gitoxide +EOF + +git tag t1 +git tag -m "tag object" dt1 diff --git a/gix-ref/tests/refs/main.rs b/gix-ref/tests/refs/main.rs index ddd06716f88..05123b5fdf5 100644 --- a/gix-ref/tests/refs/main.rs +++ b/gix-ref/tests/refs/main.rs @@ -39,6 +39,7 @@ mod partialname { } mod namespace; mod packed; +mod pseudo_refs; mod reference; mod store; mod transaction; diff --git a/gix-ref/tests/refs/pseudo_refs.rs b/gix-ref/tests/refs/pseudo_refs.rs new file mode 100644 index 00000000000..097ca217e85 --- /dev/null +++ b/gix-ref/tests/refs/pseudo_refs.rs @@ -0,0 +1,21 @@ +use crate::file::store_at; + +#[test] +fn pseudo_refs_iterate_valid_pseudorefs() -> crate::Result { + let store = store_at("make_pref_repository.sh")?; + + let prefs = store + .iter_pseudo_refs()? + .map(Result::unwrap) + .map(|r: gix_ref::Reference| r.name) + .collect::>(); + + let expected_prefs = vec!["FETCH_HEAD", "HEAD", "JIRI_HEAD"]; + + assert_eq!( + prefs.iter().map(gix_ref::FullName::as_bstr).collect::>(), + expected_prefs + ); + + Ok(()) +} From 2affbab7491d6b4667572d4d17db864c5b703c7a Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 11 Jul 2025 06:54:02 +0200 Subject: [PATCH 4/5] feat: add `repo.references().pseudo()` for traversing refs like `HEAD` and `FETCH_HEAD`. --- gix/src/reference/iter.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/gix/src/reference/iter.rs b/gix/src/reference/iter.rs index a18ecbeda91..62fcbe8d02e 100644 --- a/gix/src/reference/iter.rs +++ b/gix/src/reference/iter.rs @@ -32,7 +32,8 @@ impl<'r> Iter<'r> { } impl Platform<'_> { - /// Return an iterator over all references in the repository. + /// Return an iterator over all references in the repository, excluding + /// pseudo references. /// /// Even broken or otherwise unparsable or inaccessible references are returned and have to be handled by the caller on a /// case by case basis. @@ -69,6 +70,12 @@ impl Platform<'_> { )) } + // TODO: tests + /// Return an iterator over all local pseudo references. + pub fn pseudo_refs(&self) -> Result, init::Error> { + Ok(Iter::new(self.repo, self.platform.psuedo_refs()?)) + } + // TODO: tests /// Return an iterator over all remote branches. /// From 43f92b5285af6696cd21f0e94f3bec568aef8468 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 11 Jul 2025 06:56:24 +0200 Subject: [PATCH 5/5] refactor - fix typo - move and simplify test; minimize its fixture - adjust docs --- gix-features/src/fs.rs | 9 +- gix-ref/src/lib.rs | 2 +- gix-ref/src/store/file/loose/iter.rs | 3 +- gix-ref/src/store/file/overlay_iter.rs | 110 ++++++++---------- ...ory.tar => make_pseudo_ref_repository.tar} | Bin 81920 -> 58880 bytes .../tests/fixtures/make_pref_repository.sh | 41 ------- .../fixtures/make_pseudo_ref_repository.sh | 14 +++ gix-ref/tests/refs/file/store/iter.rs | 17 ++- gix-ref/tests/refs/main.rs | 1 - gix-ref/tests/refs/pseudo_refs.rs | 21 ---- gix/src/reference/iter.rs | 4 +- 11 files changed, 88 insertions(+), 134 deletions(-) rename gix-ref/tests/fixtures/generated-archives/{make_pref_repository.tar => make_pseudo_ref_repository.tar} (57%) delete mode 100755 gix-ref/tests/fixtures/make_pref_repository.sh create mode 100755 gix-ref/tests/fixtures/make_pseudo_ref_repository.sh delete mode 100644 gix-ref/tests/refs/pseudo_refs.rs diff --git a/gix-features/src/fs.rs b/gix-features/src/fs.rs index a1769cd0b21..164d6913483 100644 --- a/gix-features/src/fs.rs +++ b/gix-features/src/fs.rs @@ -208,9 +208,12 @@ pub mod walkdir { /// /// Use `precompose_unicode` to represent the `core.precomposeUnicode` configuration option. /// Use `max_depth` to limit the depth of the recursive walk. - /// * 0 -> Returns only the root path with no children - /// * 1 -> Root directory and children. - /// * 2..n -> Root directory, children and {n}-grandchildren + /// * `0` + /// - Returns only the root path with no children + /// * `1` + /// - Root directory and children. + /// * `1..n` + /// - Root directory, children and {n}-grandchildren pub fn walkdir_sorted_new(root: &Path, _: Parallelism, max_depth: usize, precompose_unicode: bool) -> WalkDir { WalkDir { inner: WalkDirImpl::new(root) diff --git a/gix-ref/src/lib.rs b/gix-ref/src/lib.rs index a564d0f6488..1d9eded44cc 100644 --- a/gix-ref/src/lib.rs +++ b/gix-ref/src/lib.rs @@ -166,7 +166,7 @@ pub enum Category<'a> { RemoteBranch, /// A tag in `refs/notes` Note, - /// Something outside of `ref/` in the current worktree, typically `HEAD`. + /// Something outside `ref/` in the current worktree, typically `HEAD`. PseudoRef, /// A `PseudoRef`, but referenced so that it will always refer to the main worktree by /// prefixing it with `main-worktree/`. diff --git a/gix-ref/src/store/file/loose/iter.rs b/gix-ref/src/store/file/loose/iter.rs index a44ff92b7b8..c0c7b7f3a8b 100644 --- a/gix-ref/src/store/file/loose/iter.rs +++ b/gix-ref/src/store/file/loose/iter.rs @@ -22,10 +22,9 @@ impl SortedLoosePaths { base: PathBuf, prefix: Option, suffix: Option, - root_only: bool, precompose_unicode: bool, ) -> Self { - let depth = if root_only { 1 } else { usize::MAX }; + let depth = if suffix.is_some() { 1 } else { usize::MAX }; SortedLoosePaths { base, prefix, diff --git a/gix-ref/src/store/file/overlay_iter.rs b/gix-ref/src/store/file/overlay_iter.rs index 6850fba2137..8c4b2c36809 100644 --- a/gix-ref/src/store/file/overlay_iter.rs +++ b/gix-ref/src/store/file/overlay_iter.rs @@ -1,3 +1,5 @@ +use gix_object::bstr::ByteSlice; +use gix_path::RelativePath; use std::{ borrow::Cow, cmp::Ordering, @@ -6,9 +8,6 @@ use std::{ path::{Path, PathBuf}, }; -use gix_object::bstr::ByteSlice; -use gix_path::RelativePath; - use crate::{ file::loose::{self, iter::SortedLoosePaths}, store_impl::{file, packed}, @@ -85,46 +84,34 @@ impl<'p> LooseThenPacked<'p, '_> { } fn convert_loose(&mut self, res: std::io::Result<(PathBuf, FullName)>) -> Result { - convert_loose(&mut self.buf, self.git_dir, self.common_dir, self.namespace, res) - } -} - -pub(crate) fn convert_loose( - buf: &mut Vec, - git_dir: &Path, - common_dir: Option<&Path>, - namespace: Option<&Namespace>, - res: std::io::Result<(PathBuf, FullName)>, -) -> Result { - let (refpath, name) = res.map_err(Error::Traversal)?; - std::fs::File::open(&refpath) - .and_then(|mut f| { - buf.clear(); - f.read_to_end(buf) - }) - .map_err(|err| Error::ReadFileContents { - source: err, - path: refpath.to_owned(), - })?; - loose::Reference::try_from_path(name, buf) - .map_err(|err| { - let relative_path = refpath - .strip_prefix(git_dir) - .ok() - .or_else(|| common_dir.and_then(|common_dir| refpath.strip_prefix(common_dir).ok())) - .expect("one of our bases contains the path"); - Error::ReferenceCreation { + let buf = &mut self.buf; + let git_dir = self.git_dir; + let common_dir = self.common_dir; + let (refpath, name) = res.map_err(Error::Traversal)?; + std::fs::File::open(&refpath) + .and_then(|mut f| { + buf.clear(); + f.read_to_end(buf) + }) + .map_err(|err| Error::ReadFileContents { source: err, - relative_path: relative_path.into(), - } - }) - .map(Into::into) - .map(|mut r: Reference| { - if let Some(namespace) = namespace { - r.strip_namespace(namespace); - } - r - }) + path: refpath.to_owned(), + })?; + loose::Reference::try_from_path(name, buf) + .map_err(|err| { + let relative_path = refpath + .strip_prefix(git_dir) + .ok() + .or_else(|| common_dir.and_then(|common_dir| refpath.strip_prefix(common_dir).ok())) + .expect("one of our bases contains the path"); + Error::ReferenceCreation { + source: err, + relative_path: relative_path.into(), + } + }) + .map(Into::into) + .map(|r| self.strip_namespace(r)) + } } impl Iterator for LooseThenPacked<'_, '_> { @@ -203,9 +190,9 @@ impl Iterator for LooseThenPacked<'_, '_> { } impl Platform<'_> { - /// Return an iterator over all references, loose or `packed`, sorted by their name. + /// Return an iterator over all references, loose or packed, sorted by their name. /// - /// Errors are returned similarly to what would happen when loose and packed refs where iterated by themselves. + /// Errors are returned similarly to what would happen when loose and packed refs were iterated by themselves. pub fn all(&self) -> std::io::Result> { self.store.iter_packed(self.packed.as_ref().map(|b| &***b)) } @@ -223,16 +210,17 @@ impl Platform<'_> { .iter_prefixed_packed(prefix, self.packed.as_ref().map(|b| &***b)) } - /// Return an iterator over the pseudo references - pub fn psuedo_refs(&self) -> std::io::Result> { - self.store.iter_pseudo_refs() + /// Return an iterator over the pseudo references, like `HEAD` or `FETCH_HEAD`, or anything else suffixed with `HEAD` + /// in the root of the `.git` directory, sorted by name. + pub fn pseudo(&self) -> std::io::Result> { + self.store.iter_pseudo() } } impl file::Store { /// Return a platform to obtain iterator over all references, or prefixed ones, loose or packed, sorted by their name. /// - /// Errors are returned similarly to what would happen when loose and packed refs where iterated by themselves. + /// Errors are returned similarly to what would happen when loose and packed refs were iterated by themselves. /// /// Note that since packed-refs are storing refs as precomposed unicode if [`Self::precompose_unicode`] is true, for consistency /// we also return loose references as precomposed unicode. @@ -271,7 +259,7 @@ pub(crate) enum IterInfo<'a> { /// If `true`, we will convert decomposed into precomposed unicode. precompose_unicode: bool, }, - PseudoRefs { + Pseudo { base: &'a Path, precompose_unicode: bool, }, @@ -284,7 +272,7 @@ impl<'a> IterInfo<'a> { IterInfo::PrefixAndBase { prefix, .. } => Some(gix_path::into_bstr(*prefix)), IterInfo::BaseAndIterRoot { prefix, .. } => Some(gix_path::into_bstr(prefix.clone())), IterInfo::ComputedIterationRoot { prefix, .. } => Some(prefix.clone()), - IterInfo::PseudoRefs { .. } => None, + IterInfo::Pseudo { .. } => None, } } @@ -293,18 +281,18 @@ impl<'a> IterInfo<'a> { IterInfo::Base { base, precompose_unicode, - } => SortedLoosePaths::at(&base.join("refs"), base.into(), None, None, false, precompose_unicode), + } => SortedLoosePaths::at(&base.join("refs"), base.into(), None, None, precompose_unicode), IterInfo::BaseAndIterRoot { base, iter_root, prefix: _, precompose_unicode, - } => SortedLoosePaths::at(&iter_root, base.into(), None, None, false, precompose_unicode), + } => SortedLoosePaths::at(&iter_root, base.into(), None, None, precompose_unicode), IterInfo::PrefixAndBase { base, prefix, precompose_unicode, - } => SortedLoosePaths::at(&base.join(prefix), base.into(), None, None, false, precompose_unicode), + } => SortedLoosePaths::at(&base.join(prefix), base.into(), None, None, precompose_unicode), IterInfo::ComputedIterationRoot { iter_root, base, @@ -315,13 +303,12 @@ impl<'a> IterInfo<'a> { base.into(), Some(prefix.into_owned()), None, - false, precompose_unicode, ), - IterInfo::PseudoRefs { + IterInfo::Pseudo { base, precompose_unicode, - } => SortedLoosePaths::at(base, base.into(), None, Some("HEAD".into()), true, precompose_unicode), + } => SortedLoosePaths::at(base, base.into(), None, Some("HEAD".into()), precompose_unicode), } .peekable() } @@ -354,7 +341,7 @@ impl<'a> IterInfo<'a> { impl file::Store { /// Return an iterator over all references, loose or `packed`, sorted by their name. /// - /// Errors are returned similarly to what would happen when loose and packed refs where iterated by themselves. + /// Errors are returned similarly to what would happen when loose and packed refs were iterated by themselves. pub fn iter_packed<'s, 'p>( &'s self, packed: Option<&'p packed::Buffer>, @@ -387,12 +374,13 @@ impl file::Store { } } - /// Return an iterator over all pseudo references, loose or `packed`, sorted by their name. + /// Return an iterator over the pseudo references, like `HEAD` or `FETCH_HEAD`, or anything else suffixed with `HEAD` + /// in the root of the `.git` directory, sorted by name. /// - /// Errors are returned similarly to what would happen when loose and packed refs where iterated by themselves. - pub fn iter_pseudo_refs<'p>(&'_ self) -> std::io::Result> { + /// Errors are returned similarly to what would happen when loose refs were iterated by themselves. + pub fn iter_pseudo<'p>(&'_ self) -> std::io::Result> { self.iter_from_info( - IterInfo::PseudoRefs { + IterInfo::Pseudo { base: self.git_dir(), precompose_unicode: self.precompose_unicode, }, diff --git a/gix-ref/tests/fixtures/generated-archives/make_pref_repository.tar b/gix-ref/tests/fixtures/generated-archives/make_pseudo_ref_repository.tar similarity index 57% rename from gix-ref/tests/fixtures/generated-archives/make_pref_repository.tar rename to gix-ref/tests/fixtures/generated-archives/make_pseudo_ref_repository.tar index e8942611672e0792c012edba1bf008c46dc0f280..68efa91bb9c1692a30f40a20e40e8d9e9491ffb4 100644 GIT binary patch delta 971 zcmZo@U~M?Vyn&TvG3yMb$*c?NqzsIWj2R3KOw0@oj0}v74Hygz3=NIVOc)F%C$gt( zW?XoQadXu&UB*dM+Qd!K6&eARLKIGBWc<&PnU`6z+0ke}<3t79P0v_aHnYBCWm?W9 z#t0-d7@0P6{a|C-F2Kh4fl(tZF)hV1#lRxb!q_s!(lRB<%pl3g$S66*)FRa=)gTEd zZ|CJO3-4LBu-nPkjO4dH3) zG%I8MAp-#y+vnQf^iG)NZI{+~v|i<<81pd?kAvsU7hGvBt~`7oIq9UOsl<<(j8IXH z4%z7^Qj!yR&nYkN6A6pF5joLx#Uy8=y6o*i54(RJUcIlT?%r1s?|lu*X^M_5%HL+# zeOtdwv@ZAF>-pBc&39ub%dMPgKCj!1hXD#UTRx~}+;oMHWi#Ko@2rzqAJsD%8E$U- zP{lauNsJC>W_n(JQEGBxajJr?LP=3+Di>z~ke{DhkYAiynwOcJp8}Hsrql|?Wk!rF z{D!9Hrk17#CPpT@KqVEM72n(k<_Mu_7L2VDCK&0%#M~SjAln%&8F?8+j4ky{6*L?R e3UX2v+%rpb4NXln!P#K5!J7+=U}ACsdp!UT`!cft delta 5558 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 .git/refs/remotes/origin/HEAD -echo "notahexsha" > .git/refs/broken - -git rev-parse HEAD > .git/JIRI_HEAD -touch .git/SOME_ALL_CAPS_FILE -touch .git/refs/SHOULD_BE_EXCLUDED_HEAD - -cat <> .git/FETCH_HEAD -9064ea31fae4dc59a56bdd3a06c0ddc990ee689e branch 'main' of https://github.com/Byron/gitoxide -1b8d9e6a408e480ae1912e919c37a26e5c46639d not-for-merge branch 'faster-discovery' of https://github.com/Byron/gitoxide -43f695a9607f1f85f859f2ef944b785b5b6dd238 not-for-merge branch 'fix-823' of https://github.com/Byron/gitoxide -96267708958ead2646aae8766a50fa060739003c not-for-merge branch 'fix-bare-with-index' of https://github.com/Byron/gitoxide -1397e19375bb98522f951b8a452b08c1b35ffbac not-for-merge branch 'gix-archive' of https://github.com/Byron/gitoxide -db71ec8b7c7f2730c47dde3bb662ab56ae89ae7d not-for-merge branch 'index-from-files' of https://github.com/Byron/gitoxide -9f0c71917e57653d2e7121eae65d9385a188a8df not-for-merge branch 'moonwalk' of https://github.com/Byron/gitoxide -44d2b67de5639d4ea3d08ab030ecfe4bdfc8cbfb not-for-merge branch 'release-gix' of https://github.com/Byron/gitoxide -37c3d073b15dafcb52b2040e4b92a413c69a726d not-for-merge branch 'smart-release-without-git2' of https://github.com/Byron/gitoxide -af3608ad397784795c3758a1ac99ec6a367de9be not-for-merge branch 'walk-with-commitgraph' of https://github.com/Byron/gitoxide -EOF - -git tag t1 -git tag -m "tag object" dt1 diff --git a/gix-ref/tests/fixtures/make_pseudo_ref_repository.sh b/gix-ref/tests/fixtures/make_pseudo_ref_repository.sh new file mode 100755 index 00000000000..ef4b3476dae --- /dev/null +++ b/gix-ref/tests/fixtures/make_pseudo_ref_repository.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -eu -o pipefail + +git init -q +git commit -m "init" --allow-empty + +git rev-parse HEAD > .git/JIRI_HEAD +touch .git/SOME_ALL_CAPS_FILE +touch .git/refs/SHOULD_BE_EXCLUDED_HEAD + +cat <> .git/FETCH_HEAD +9064ea31fae4dc59a56bdd3a06c0ddc990ee689e branch 'main' of https://github.com/Byron/gitoxide +1b8d9e6a408e480ae1912e919c37a26e5c46639d not-for-merge branch 'faster-discovery' of https://github.com/Byron/gitoxide +EOF \ No newline at end of file diff --git a/gix-ref/tests/refs/file/store/iter.rs b/gix-ref/tests/refs/file/store/iter.rs index 68259c0b1b9..91639672e6e 100644 --- a/gix-ref/tests/refs/file/store/iter.rs +++ b/gix-ref/tests/refs/file/store/iter.rs @@ -1,9 +1,8 @@ -use gix_object::bstr::ByteSlice; - use crate::{ file::{store, store_at, store_with_packed_refs}, hex_to_id, }; +use gix_object::bstr::ByteSlice; mod with_namespace { use gix_object::bstr::{BString, ByteSlice}; @@ -257,6 +256,20 @@ fn packed_file_iter() -> crate::Result { Ok(()) } +#[test] +fn pseudo_refs_iter() -> crate::Result { + let store = store_at("make_pseudo_ref_repository.sh")?; + + let actual = store + .iter_pseudo()? + .map(Result::unwrap) + .map(|r: gix_ref::Reference| r.name.as_bstr().to_string()) + .collect::>(); + + assert_eq!(actual, ["FETCH_HEAD", "HEAD", "JIRI_HEAD"]); + Ok(()) +} + #[test] fn loose_iter_with_broken_refs() -> crate::Result { let store = store()?; diff --git a/gix-ref/tests/refs/main.rs b/gix-ref/tests/refs/main.rs index 05123b5fdf5..ddd06716f88 100644 --- a/gix-ref/tests/refs/main.rs +++ b/gix-ref/tests/refs/main.rs @@ -39,7 +39,6 @@ mod partialname { } mod namespace; mod packed; -mod pseudo_refs; mod reference; mod store; mod transaction; diff --git a/gix-ref/tests/refs/pseudo_refs.rs b/gix-ref/tests/refs/pseudo_refs.rs deleted file mode 100644 index 097ca217e85..00000000000 --- a/gix-ref/tests/refs/pseudo_refs.rs +++ /dev/null @@ -1,21 +0,0 @@ -use crate::file::store_at; - -#[test] -fn pseudo_refs_iterate_valid_pseudorefs() -> crate::Result { - let store = store_at("make_pref_repository.sh")?; - - let prefs = store - .iter_pseudo_refs()? - .map(Result::unwrap) - .map(|r: gix_ref::Reference| r.name) - .collect::>(); - - let expected_prefs = vec!["FETCH_HEAD", "HEAD", "JIRI_HEAD"]; - - assert_eq!( - prefs.iter().map(gix_ref::FullName::as_bstr).collect::>(), - expected_prefs - ); - - Ok(()) -} diff --git a/gix/src/reference/iter.rs b/gix/src/reference/iter.rs index 62fcbe8d02e..66952577b64 100644 --- a/gix/src/reference/iter.rs +++ b/gix/src/reference/iter.rs @@ -72,8 +72,8 @@ impl Platform<'_> { // TODO: tests /// Return an iterator over all local pseudo references. - pub fn pseudo_refs(&self) -> Result, init::Error> { - Ok(Iter::new(self.repo, self.platform.psuedo_refs()?)) + pub fn pseudo(&self) -> Result, init::Error> { + Ok(Iter::new(self.repo, self.platform.pseudo()?)) } // TODO: tests