From ac2105f2e3dfdf2b1775e6479594daf0503b27cc Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 2 Aug 2022 13:59:13 +0800 Subject: [PATCH] support for ancestor traversal (#427) --- .../src/revision/spec/parse/delegate.rs | 29 ++++++++++++++++++- .../src/revision/spec/parse/types.rs | 8 +++++ .../make_rev_spec_parse_repos.tar.xz | 4 +-- .../fixtures/make_rev_spec_parse_repos.sh | 4 +++ .../tests/revision/spec/from_bytes/mod.rs | 18 ++++++++++++ 5 files changed, 60 insertions(+), 3 deletions(-) diff --git a/git-repository/src/revision/spec/parse/delegate.rs b/git-repository/src/revision/spec/parse/delegate.rs index 7eb1dba99f..5094bac5f8 100644 --- a/git-repository/src/revision/spec/parse/delegate.rs +++ b/git-repository/src/revision/spec/parse/delegate.rs @@ -324,7 +324,34 @@ impl<'repo> delegate::Navigate for Delegate<'repo> { Err(err) => errors.push((*obj, err)), } } - Traversal::NthAncestor(_num) => todo!("ancestor"), + Traversal::NthAncestor(num) => { + let id = obj.attach(repo); + match id + .ancestors() + .first_parent_only() + .all() + .expect("cannot fail without sorting") + .skip(num) + .filter_map(Result::ok) + .next() + { + Some(id) => replacements.push((*obj, id.detach())), + None => errors.push(( + *obj, + Error::AncestorOutOfRange { + oid: id.shorten_or_id(), + desired: num, + available: id + .ancestors() + .first_parent_only() + .all() + .expect("cannot fail without sorting") + .skip(1) + .count(), + }, + )), + } + } } } diff --git a/git-repository/src/revision/spec/parse/types.rs b/git-repository/src/revision/spec/parse/types.rs index 70b4871b85..60f3e2ba6c 100644 --- a/git-repository/src/revision/spec/parse/types.rs +++ b/git-repository/src/revision/spec/parse/types.rs @@ -61,6 +61,14 @@ pub struct Options { #[derive(Debug, thiserror::Error)] #[allow(missing_docs)] pub enum Error { + #[error( + "Commit {oid} has {available} ancestors along the first parent and ancestor number {desired} is out of range" + )] + AncestorOutOfRange { + oid: git_hash::Prefix, + desired: usize, + available: usize, + }, #[error("Commit {oid} has {available} parents and parent number {desired} is out of range")] ParentOutOfRange { oid: git_hash::Prefix, 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 c1310f8e22..cec8beee10 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:a6db0e8ed85ec2e7a3457d411a65a3508683de5156f45baf551c8d019b26e2e3 -size 28012 +oid sha256:15daf80cdca30bfed1844c71a4a2e88becb4e2e08972f4a6ea42eefc23f43000 +size 27980 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 46949e4574..9cdcf3fdbf 100644 --- a/git-repository/tests/fixtures/make_rev_spec_parse_repos.sh +++ b/git-repository/tests/fixtures/make_rev_spec_parse_repos.sh @@ -350,6 +350,10 @@ git init complex_graph # parents baseline "a" baseline "a^1" + baseline "a~1" baseline "a^0" + baseline "a~0" baseline "a^42" + baseline "a~42" + baseline "a~3" ) diff --git a/git-repository/tests/revision/spec/from_bytes/mod.rs b/git-repository/tests/revision/spec/from_bytes/mod.rs index 390617cee9..d00249365f 100644 --- a/git-repository/tests/revision/spec/from_bytes/mod.rs +++ b/git-repository/tests/revision/spec/from_bytes/mod.rs @@ -27,6 +27,24 @@ mod traverse { "Commit 55e825e has 2 parents and parent number 42 is out of range" ); } + + #[test] + fn ancestors() { + let repo = repo("complex_graph").unwrap(); + assert_eq!( + parse_spec("a~1", &repo).unwrap(), + Spec::from_id(hex_to_id("5b3f9e24965d0b28780b7ce5daf2b5b7f7e0459f").attach(&repo)) + ); + assert_eq!(parse_spec("a", &repo).unwrap(), parse_spec("a~0", &repo).unwrap(),); + assert_eq!( + parse_spec("a~3", &repo).unwrap(), + Spec::from_id(hex_to_id("9f9eac6bd1cd4b4cc6a494f044b28c985a22972b").attach(&repo)) + ); + assert_eq!( + parse_spec("a~42", &repo).unwrap_err().to_string(), + "Commit 55e825e has 3 ancestors along the first parent and ancestor number 42 is out of range" + ); + } } mod index {