From 5cd06cf668bcefe81c6feba8e9475d97d6debf43 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 3 Aug 2022 09:52:36 +0800 Subject: [PATCH] access to reflog entries (#427) --- .../src/revision/spec/parse/delegate.rs | 38 ++++++++++++++++- .../src/revision/spec/parse/types.rs | 6 +++ .../make_rev_spec_parse_repos.tar.xz | 4 +- .../fixtures/make_rev_spec_parse_repos.sh | 5 +++ .../tests/revision/spec/from_bytes/reflog.rs | 41 +++++++++++++++++++ 5 files changed, 91 insertions(+), 3 deletions(-) diff --git a/git-repository/src/revision/spec/parse/delegate.rs b/git-repository/src/revision/spec/parse/delegate.rs index e8c26d91f5..a5e2eda64a 100644 --- a/git-repository/src/revision/spec/parse/delegate.rs +++ b/git-repository/src/revision/spec/parse/delegate.rs @@ -280,7 +280,43 @@ impl<'repo> delegate::Revision for Delegate<'repo> { }); None } - ReflogLookup::Entry(_no) => todo!("entry lookup"), + ReflogLookup::Entry(no) => { + let head_ref = match self.repo.head().map(|head| head.into_referent()) { + Ok(r) => r.detach(), + Err(err) => { + self.err.push(err.into()); + return None; + } + }; + let r = self.refs[self.idx].get_or_insert(head_ref).clone().attach(self.repo); + let mut platform = r.log_iter(); + match platform.rev().ok().flatten() { + Some(mut it) => match it.nth(no).map(Result::ok).flatten() { + Some(line) => { + self.objs[self.idx] + .get_or_insert_with(HashSet::default) + .insert(line.new_oid); + Some(()) + } + None => { + let available = platform.rev().ok().flatten().map_or(0, |it| it.count()); + self.err.push(Error::RefLogEntryOutOfRange { + reference: r.detach(), + desired: no, + available, + }); + None + } + }, + None => { + self.err.push(Error::MissingRefLog { + reference: r.name().as_bstr().into(), + action: "lookup entry", + }); + None + } + } + } } } diff --git a/git-repository/src/revision/spec/parse/types.rs b/git-repository/src/revision/spec/parse/types.rs index f3b1a99bb4..557fa0849a 100644 --- a/git-repository/src/revision/spec/parse/types.rs +++ b/git-repository/src/revision/spec/parse/types.rs @@ -67,6 +67,12 @@ pub enum Error { MissingRefLog { reference: BString, action: &'static str }, #[error("HEAD has {available} prior checkouts and checkout number {desired} is out of range")] PriorCheckoutOutOfRange { desired: usize, available: usize }, + #[error("Reference {:?} has {available} ref-log entries and entry number {desired} is out of range", reference.name.as_bstr())] + RefLogEntryOutOfRange { + reference: git_ref::Reference, + desired: usize, + available: usize, + }, #[error( "Commit {oid} has {available} ancestors along the first parent and ancestor number {desired} is out of range" )] diff --git a/git-repository/tests/fixtures/generated-archives/make_rev_spec_parse_repos.tar.xz b/git-repository/tests/fixtures/generated-archives/make_rev_spec_parse_repos.tar.xz index 858a00433e..b297f0c0d7 100644 --- a/git-repository/tests/fixtures/generated-archives/make_rev_spec_parse_repos.tar.xz +++ b/git-repository/tests/fixtures/generated-archives/make_rev_spec_parse_repos.tar.xz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4684b243803678e18130357413321361a4f6cb38076590404bf81788b6f2a1c6 -size 28116 +oid sha256:41adc1d79626602c2ab31f92e33598b322e92e9c58d6f015dd6d81c39e9129d6 +size 28104 diff --git a/git-repository/tests/fixtures/make_rev_spec_parse_repos.sh b/git-repository/tests/fixtures/make_rev_spec_parse_repos.sh index eaf1913869..c14f11853a 100644 --- a/git-repository/tests/fixtures/make_rev_spec_parse_repos.sh +++ b/git-repository/tests/fixtures/make_rev_spec_parse_repos.sh @@ -377,4 +377,9 @@ git init complex_graph baseline "@{-4}" baseline "@{-5}" baseline "@{-6}" + + baseline "@{0}" + baseline "@{3}" + baseline "HEAD@{5}" + baseline "main@{12345}" ) diff --git a/git-repository/tests/revision/spec/from_bytes/reflog.rs b/git-repository/tests/revision/spec/from_bytes/reflog.rs index e26cd9ec35..60fabed12f 100644 --- a/git-repository/tests/revision/spec/from_bytes/reflog.rs +++ b/git-repository/tests/revision/spec/from_bytes/reflog.rs @@ -1,5 +1,8 @@ use crate::revision::spec::from_bytes::{parse_spec, parse_spec_no_baseline, repo}; +use git_repository::prelude::ObjectIdExt; use git_repository::revision::spec::parse::Error; +use git_repository::revision::Spec; +use git_testtools::hex_to_id; #[test] fn nth_prior_checkout() { @@ -14,6 +17,7 @@ fn nth_prior_checkout() { ] { let parsed = parse_spec(spec, &repo).unwrap_or_else(|_| panic!("{} to be parsed successfully", spec)); assert_eq!(parsed.first_reference().expect("present").name.as_bstr(), prior_branch); + assert_eq!(parsed.second_reference(), None); } assert_eq!( @@ -22,6 +26,43 @@ fn nth_prior_checkout() { ); } +#[test] +fn by_index() { + let repo = &repo("complex_graph").unwrap(); + { + let spec = parse_spec("@{0}", repo).unwrap(); + assert_eq!( + spec, + Spec::from_id(hex_to_id("55e825ebe8fd2ff78cad3826afb696b96b576a7e").attach(repo)) + ); + assert_eq!( + spec.first_reference().expect("set").name.as_bstr(), + "refs/heads/main", + "it sets the reference name even if it is implied" + ); + assert_eq!(spec.second_reference(), None); + } + + { + let spec = parse_spec("HEAD@{5}", repo).unwrap(); + assert_eq!( + spec, + Spec::from_id(hex_to_id("5b3f9e24965d0b28780b7ce5daf2b5b7f7e0459f").attach(repo)) + ); + assert_eq!( + spec.first_reference().map(|r| r.name.to_string()), + Some("HEAD".into()), + "explicit references are picked up as usual" + ); + assert_eq!(spec.second_reference(), None); + } + + assert_eq!( + parse_spec("main@{12345}", repo).unwrap_err().to_string(), + "Reference \"refs/heads/main\" has 4 ref-log entries and entry number 12345 is out of range" + ); +} + #[test] fn by_date() { let repo = repo("complex_graph").unwrap();