Skip to content

fix 1914 #1916

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions gix-revision/tests/revision/spec/parse/navigate/caret_symbol.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use gix_revision::{spec, spec::parse::delegate::Traversal};

use crate::spec::parse::{parse, try_parse, PeelToOwned as PeelTo};
use gix_revision::{spec, spec::parse::delegate::Traversal};

#[test]
fn single_is_first_parent() {
Expand Down Expand Up @@ -190,6 +189,23 @@ fn invalid_object_type() {
);
}

#[test]
fn invalid_caret_without_previous_refname() {
let rec = parse(r"^^");
assert_eq!(rec.calls, 2);
assert_eq!(rec.kind, Some(gix_revision::spec::Kind::ExcludeReachable));
assert_eq!(
rec.traversal,
[Traversal::NthParent(1)],
"This can trip off an implementation as it's actually invalid, but looks valid"
);

for revspec in ["^^^HEAD", "^^HEAD"] {
let err = try_parse(revspec).unwrap_err();
assert!(matches!(err, spec::parse::Error::UnconsumedInput {input} if input == "HEAD"));
}
}

#[test]
fn incomplete_escaped_braces_in_regex_are_invalid() {
let err = try_parse(r"@^{/a\{1}}").unwrap_err();
Expand Down
8 changes: 7 additions & 1 deletion gix/src/revision/spec/parse/delegate/navigate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@ impl delegate::Navigate for Delegate<'_> {

let mut replacements = Replacements::default();
let mut errors = Vec::new();
let objs = self.objs[self.idx].as_mut()?;
let objs = match self.objs[self.idx].as_mut() {
Some(objs) => objs,
None => {
self.err.push(Error::TraversalWithoutStartObject);
return None;
}
};
let repo = self.repo;

for obj in objs.iter() {
Expand Down
1 change: 0 additions & 1 deletion gix/src/revision/spec/parse/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ impl Error {
}

pub(crate) fn from_errors(errors: Vec<Self>) -> Self {
assert!(!errors.is_empty());
match errors.len() {
0 => unreachable!(
"BUG: cannot create something from nothing, must have recorded some errors to call from_errors()"
Expand Down
2 changes: 2 additions & 0 deletions gix/src/revision/spec/parse/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ pub enum Error {
},
#[error(transparent)]
Traverse(#[from] crate::revision::walk::iter::Error),
#[error("Tried to navigate the commit-graph without providing an anchor first")]
TraversalWithoutStartObject,
#[error(transparent)]
Walk(#[from] crate::revision::walk::Error),
#[error("Spec does not contain a single object id")]
Expand Down
Binary file not shown.
5 changes: 5 additions & 0 deletions gix/tests/fixtures/make_rev_spec_parse_repos.sh
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ EOF
baseline "@^{/!-B}" # negation from branch
baseline ":file" # index lookup, default stage 0
baseline ":1:file" # stage 1
baseline ":5:file" # invalid stage
baseline ":foo" # not found
# parents
baseline "a"
Expand All @@ -381,6 +382,10 @@ EOF
baseline "b^3^2"
baseline "a^^3^2"

# invalid
baseline "^^"
baseline "^^HEAD"

baseline "@{-1}"
baseline "@{-2}"
baseline "@{-3}"
Expand Down
6 changes: 6 additions & 0 deletions gix/tests/gix/revision/spec/from_bytes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ mod index {
"Path \"file\" did not exist in index at stage 1. It does exist at stage 0. It exists on disk",
);

assert_eq!(
parse_spec(":5:file", &repo).unwrap_err().to_string(),
"Path \"5:file\" did not exist in index at stage 0. It does not exist on disk",
"invalid stage ids are interpreted as part of the filename"
);

assert_eq!(
parse_spec(":foo", &repo).unwrap_err().to_string(),
"Path \"foo\" did not exist in index at stage 0. It does not exist on disk",
Expand Down
12 changes: 10 additions & 2 deletions gix/tests/gix/revision/spec/from_bytes/traverse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,16 @@ fn complex() -> crate::Result {
#[test]
fn freestanding_negation_yields_descriptive_error() -> crate::Result {
let repo = repo("complex_graph")?;
let expected = "The rev-spec is malformed and misses a ref name";
assert_eq!(parse_spec("^", &repo).unwrap_err().to_string(), expected);
for revspec in ["^^", "^^HEAD"] {
assert_eq!(
parse_spec(revspec, &repo).unwrap_err().to_string(),
"Tried to navigate the commit-graph without providing an anchor first"
);
}
assert_eq!(
parse_spec("^", &repo).unwrap_err().to_string(),
"The rev-spec is malformed and misses a ref name"
);
assert_eq!(
parse_spec("^!", &repo).unwrap_err().to_string(),
"The ref partially named \"!\" could not be found"
Expand Down