Skip to content

Commit cbdbb8a

Browse files
authored
Merge pull request #1645 from dvtkrlbs/refloglookup-date
feat: handle ReflogLookup::Date
2 parents ca54b8c + 9662bc1 commit cbdbb8a

File tree

15 files changed

+1305
-64
lines changed

15 files changed

+1305
-64
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ impl Platform<'_, '_> {
9292
/// Return a forward iterator over all log-lines, most recent to oldest.
9393
pub fn rev(&mut self) -> std::io::Result<Option<log::iter::Reverse<'_, std::fs::File>>> {
9494
self.buf.clear();
95-
self.buf.resize(512, 0);
95+
self.buf.resize(1024 * 4, 0);
9696
self.store
9797
.reflog_iter_rev(self.name, &mut self.buf)
9898
.map_err(must_be_io_err)
@@ -219,7 +219,7 @@ where
219219
if npos == last_read_pos {
220220
return Some(Err(std::io::Error::new(
221221
std::io::ErrorKind::Other,
222-
"buffer too small for line size",
222+
format!("buffer too small for line size, got until {:?}", self.buf.as_bstr()),
223223
)
224224
.into()));
225225
}
Binary file not shown.

gix-ref/tests/fixtures/make_repo_for_reflog.sh

Lines changed: 585 additions & 0 deletions
Large diffs are not rendered by default.

gix-ref/tests/refs/file/log.rs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ mod iter {
8181
.source()
8282
.expect("source")
8383
.to_string(),
84-
"buffer too small for line size"
84+
"buffer too small for line size, got until \"0000000000000000 134385f6d781b7e97062102c6a483440bfda2a03 committer <[email protected]> 946771200 +0000\\tcommit (initial): c1\""
8585
);
8686
assert!(iter.next().is_none(), "iterator depleted");
8787
}
@@ -90,9 +90,9 @@ mod iter {
9090
}
9191

9292
mod with_buffer_big_enough_for_largest_line {
93-
use gix_ref::log::Line;
94-
93+
use crate::file::log::iter::reflog;
9594
use crate::hex_to_id;
95+
use gix_ref::log::Line;
9696

9797
#[test]
9898
fn single_line() -> crate::Result {
@@ -157,6 +157,24 @@ mod iter {
157157
}
158158
Ok(())
159159
}
160+
161+
#[test]
162+
fn realistic_logs_can_be_read_completely() -> crate::Result {
163+
let log = reflog("refs/heads/old")?;
164+
let mut buf = Vec::with_capacity(16 * 1024);
165+
for size in [2048, 3000, 4096, 8192, 16384] {
166+
buf.resize(size, 0);
167+
let read = std::io::Cursor::new(&*log);
168+
let count = gix_ref::file::log::iter::reverse(read, &mut buf)?
169+
.filter_map(Result::ok)
170+
.count();
171+
assert_eq!(
172+
count, 581,
173+
"All entries must be readable as long as the buffer can fit a whole line"
174+
);
175+
}
176+
Ok(())
177+
}
160178
}
161179
}
162180
mod forward {
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

gix/src/revision/spec/parse/delegate/revision.rs

Lines changed: 70 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -105,59 +105,81 @@ impl delegate::Revision for Delegate<'_> {
105105

106106
fn reflog(&mut self, query: ReflogLookup) -> Option<()> {
107107
self.unset_disambiguate_call();
108-
match query {
109-
ReflogLookup::Date(_date) => {
110-
// TODO: actually do this - this should be possible now despite incomplete date parsing
111-
self.err.push(Error::Planned {
112-
dependency: "remote handling and ref-specs are fleshed out more",
113-
});
114-
None
115-
}
116-
ReflogLookup::Entry(no) => {
117-
let r = match &mut self.refs[self.idx] {
118-
Some(r) => r.clone().attach(self.repo),
119-
val @ None => match self.repo.head().map(crate::Head::try_into_referent) {
120-
Ok(Some(r)) => {
121-
*val = Some(r.clone().detach());
122-
r
123-
}
124-
Ok(None) => {
125-
self.err.push(Error::UnbornHeadsHaveNoRefLog);
126-
return None;
127-
}
128-
Err(err) => {
129-
self.err.push(err.into());
130-
return None;
131-
}
132-
},
133-
};
134-
let mut platform = r.log_iter();
135-
match platform.rev().ok().flatten() {
136-
Some(mut it) => match it.nth(no).and_then(Result::ok) {
137-
Some(line) => {
138-
self.objs[self.idx]
139-
.get_or_insert_with(HashSet::default)
140-
.insert(line.new_oid);
141-
Some(())
142-
}
143-
None => {
144-
let available = platform.rev().ok().flatten().map_or(0, Iterator::count);
145-
self.err.push(Error::RefLogEntryOutOfRange {
146-
reference: r.detach(),
147-
desired: no,
148-
available,
108+
let r = match &mut self.refs[self.idx] {
109+
Some(r) => r.clone().attach(self.repo),
110+
val @ None => match self.repo.head().map(crate::Head::try_into_referent) {
111+
Ok(Some(r)) => {
112+
*val = Some(r.clone().detach());
113+
r
114+
}
115+
Ok(None) => {
116+
self.err.push(Error::UnbornHeadsHaveNoRefLog);
117+
return None;
118+
}
119+
Err(err) => {
120+
self.err.push(err.into());
121+
return None;
122+
}
123+
},
124+
};
125+
126+
let mut platform = r.log_iter();
127+
match platform.rev().ok().flatten() {
128+
Some(mut it) => match query {
129+
ReflogLookup::Date(date) => {
130+
let mut last = None;
131+
let id_to_insert = match it
132+
.filter_map(Result::ok)
133+
.inspect(|d| {
134+
last = Some(if d.previous_oid.is_null() {
135+
d.new_oid
136+
} else {
137+
d.previous_oid
149138
});
150-
None
151-
}
152-
},
139+
})
140+
.find(|l| l.signature.time.seconds <= date.seconds)
141+
{
142+
Some(closest_line) => closest_line.new_oid,
143+
None => match last {
144+
None => {
145+
self.err.push(Error::EmptyReflog);
146+
return None;
147+
}
148+
Some(id) => id,
149+
},
150+
};
151+
self.objs[self.idx]
152+
.get_or_insert_with(HashSet::default)
153+
.insert(id_to_insert);
154+
Some(())
155+
}
156+
ReflogLookup::Entry(no) => match it.nth(no).and_then(Result::ok) {
157+
Some(line) => {
158+
self.objs[self.idx]
159+
.get_or_insert_with(HashSet::default)
160+
.insert(line.new_oid);
161+
Some(())
162+
}
153163
None => {
154-
self.err.push(Error::MissingRefLog {
155-
reference: r.name().as_bstr().into(),
156-
action: "lookup entry",
164+
let available = platform.rev().ok().flatten().map_or(0, Iterator::count);
165+
self.err.push(Error::RefLogEntryOutOfRange {
166+
reference: r.detach(),
167+
desired: no,
168+
available,
157169
});
158170
None
159171
}
160-
}
172+
},
173+
},
174+
None => {
175+
self.err.push(Error::MissingRefLog {
176+
reference: r.name().as_bstr().into(),
177+
action: match query {
178+
ReflogLookup::Entry(_) => "lookup reflog entry by index",
179+
ReflogLookup::Date(_) => "lookup reflog entry by date",
180+
},
181+
});
182+
None
161183
}
162184
}
163185
}

gix/src/revision/spec/parse/types.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,6 @@ pub enum Error {
7777
direction: remote::Direction,
7878
source: Box<dyn std::error::Error + Send + Sync + 'static>,
7979
},
80-
#[error("This feature will be implemented once {dependency}")]
81-
Planned { dependency: &'static str },
8280
#[error("Reference {reference:?} does not have a reference log, cannot {action}")]
8381
MissingRefLog { reference: BString, action: &'static str },
8482
#[error("HEAD has {available} prior checkouts and checkout number {desired} is out of range")]
@@ -194,4 +192,6 @@ pub enum Error {
194192
Walk(#[from] crate::revision::walk::Error),
195193
#[error("Spec does not contain a single object id")]
196194
SingleNotFound,
195+
#[error("Reflog does not contain any entries")]
196+
EmptyReflog,
197197
}
Binary file not shown.

0 commit comments

Comments
 (0)