Skip to content

Commit 04a18f3

Browse files
authored
Merge pull request #2105 from jalil-salame/fix-2103
fix(gix): extend lifetime of iterators
2 parents 202bc6d + d4130c3 commit 04a18f3

File tree

3 files changed

+33
-19
lines changed

3 files changed

+33
-19
lines changed

gix-ref/src/store/file/overlay_iter.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,11 +189,11 @@ impl Iterator for LooseThenPacked<'_, '_> {
189189
}
190190
}
191191

192-
impl Platform<'_> {
192+
impl<'repo> Platform<'repo> {
193193
/// Return an iterator over all references, loose or packed, sorted by their name.
194194
///
195195
/// Errors are returned similarly to what would happen when loose and packed refs were iterated by themselves.
196-
pub fn all(&self) -> std::io::Result<LooseThenPacked<'_, '_>> {
196+
pub fn all<'p>(&'p self) -> std::io::Result<LooseThenPacked<'p, 'repo>> {
197197
self.store.iter_packed(self.packed.as_ref().map(|b| &***b))
198198
}
199199

@@ -205,14 +205,14 @@ impl Platform<'_> {
205205
/// starts with `foo`, like `refs/heads/foo` and `refs/heads/foobar`.
206206
///
207207
/// Prefixes are relative paths with slash-separated components.
208-
pub fn prefixed(&self, prefix: &RelativePath) -> std::io::Result<LooseThenPacked<'_, '_>> {
208+
pub fn prefixed<'p>(&'p self, prefix: &RelativePath) -> std::io::Result<LooseThenPacked<'p, 'repo>> {
209209
self.store
210210
.iter_prefixed_packed(prefix, self.packed.as_ref().map(|b| &***b))
211211
}
212212

213213
/// Return an iterator over the pseudo references, like `HEAD` or `FETCH_HEAD`, or anything else suffixed with `HEAD`
214214
/// in the root of the `.git` directory, sorted by name.
215-
pub fn pseudo(&self) -> std::io::Result<LooseThenPacked<'_, '_>> {
215+
pub fn pseudo<'p>(&'p self) -> std::io::Result<LooseThenPacked<'p, 'repo>> {
216216
self.store.iter_pseudo()
217217
}
218218
}

gix/src/reference/iter.rs

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@ pub struct Platform<'r> {
1313
}
1414

1515
/// An iterator over references, with or without filter.
16-
pub struct Iter<'r> {
17-
inner: gix_ref::file::iter::LooseThenPacked<'r, 'r>,
16+
pub struct Iter<'packed, 'repo> {
17+
inner: gix_ref::file::iter::LooseThenPacked<'packed, 'repo>,
1818
peel_with_packed: Option<gix_ref::file::packed::SharedBufferSnapshot>,
1919
peel: bool,
20-
repo: &'r crate::Repository,
20+
repo: &'repo crate::Repository,
2121
}
2222

23-
impl<'r> Iter<'r> {
24-
fn new(repo: &'r crate::Repository, platform: gix_ref::file::iter::LooseThenPacked<'r, 'r>) -> Self {
23+
impl<'packed, 'repo> Iter<'packed, 'repo> {
24+
fn new(repo: &'repo crate::Repository, platform: gix_ref::file::iter::LooseThenPacked<'packed, 'repo>) -> Self {
2525
Iter {
2626
inner: platform,
2727
peel_with_packed: None,
@@ -31,13 +31,13 @@ impl<'r> Iter<'r> {
3131
}
3232
}
3333

34-
impl Platform<'_> {
34+
impl<'repo> Platform<'repo> {
3535
/// Return an iterator over all references in the repository, excluding
3636
/// pseudo references.
3737
///
3838
/// Even broken or otherwise unparsable or inaccessible references are returned and have to be handled by the caller on a
3939
/// case by case basis.
40-
pub fn all(&self) -> Result<Iter<'_>, init::Error> {
40+
pub fn all(&self) -> Result<Iter<'_, 'repo>, init::Error> {
4141
Ok(Iter::new(self.repo, self.platform.all()?))
4242
}
4343

@@ -47,23 +47,22 @@ impl Platform<'_> {
4747
pub fn prefixed<'a>(
4848
&self,
4949
prefix: impl TryInto<&'a RelativePath, Error = gix_path::relative_path::Error>,
50-
) -> Result<Iter<'_>, init::Error> {
50+
) -> Result<Iter<'_, 'repo>, init::Error> {
5151
Ok(Iter::new(self.repo, self.platform.prefixed(prefix.try_into()?)?))
5252
}
5353

54-
// TODO: tests
5554
/// Return an iterator over all references that are tags.
5655
///
5756
/// They are all prefixed with `refs/tags`.
58-
pub fn tags(&self) -> Result<Iter<'_>, init::Error> {
57+
pub fn tags(&self) -> Result<Iter<'_, 'repo>, init::Error> {
5958
Ok(Iter::new(self.repo, self.platform.prefixed(b"refs/tags/".try_into()?)?))
6059
}
6160

6261
// TODO: tests
6362
/// Return an iterator over all local branches.
6463
///
6564
/// They are all prefixed with `refs/heads`.
66-
pub fn local_branches(&self) -> Result<Iter<'_>, init::Error> {
65+
pub fn local_branches(&self) -> Result<Iter<'_, 'repo>, init::Error> {
6766
Ok(Iter::new(
6867
self.repo,
6968
self.platform.prefixed(b"refs/heads/".try_into()?)?,
@@ -72,23 +71,23 @@ impl Platform<'_> {
7271

7372
// TODO: tests
7473
/// Return an iterator over all local pseudo references.
75-
pub fn pseudo(&self) -> Result<Iter<'_>, init::Error> {
74+
pub fn pseudo(&self) -> Result<Iter<'_, 'repo>, init::Error> {
7675
Ok(Iter::new(self.repo, self.platform.pseudo()?))
7776
}
7877

7978
// TODO: tests
8079
/// Return an iterator over all remote branches.
8180
///
8281
/// They are all prefixed with `refs/remotes`.
83-
pub fn remote_branches(&self) -> Result<Iter<'_>, init::Error> {
82+
pub fn remote_branches(&self) -> Result<Iter<'_, 'repo>, init::Error> {
8483
Ok(Iter::new(
8584
self.repo,
8685
self.platform.prefixed(b"refs/remotes/".try_into()?)?,
8786
))
8887
}
8988
}
9089

91-
impl Iter<'_> {
90+
impl Iter<'_, '_> {
9291
/// Automatically peel references before yielding them during iteration.
9392
///
9493
/// This has the same effect as using `iter.map(|r| {r.peel_to_id_in_place(); r})`.
@@ -104,7 +103,7 @@ impl Iter<'_> {
104103
}
105104
}
106105

107-
impl<'r> Iterator for Iter<'r> {
106+
impl<'r> Iterator for Iter<'_, 'r> {
108107
type Item = Result<crate::Reference<'r>, Box<dyn std::error::Error + Send + Sync + 'static>>;
109108

110109
fn next(&mut self) -> Option<Self::Item> {

gix/tests/gix/repository/reference.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,21 @@ mod iter_references {
183183
);
184184
Ok(())
185185
}
186+
187+
/// Regression test for https://github.com/GitoxideLabs/gitoxide/issues/2103
188+
/// This only ensures we can return a reference, not that the code below is correct
189+
#[test]
190+
fn tags() -> crate::Result {
191+
let repo = repo()?;
192+
let actual = repo
193+
.references()?
194+
.tags()?
195+
.filter_map(Result::ok)
196+
.max_by_key(|tag| tag.name().shorten().to_owned())
197+
.ok_or(std::io::Error::other("latest tag not found"))?;
198+
assert_eq!(actual.name().as_bstr(), "refs/tags/t1");
199+
Ok(())
200+
}
186201
}
187202

188203
mod head {

0 commit comments

Comments
 (0)