From ae3866065c9c3c6d01709f8dde1cea1ae1345779 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 19 Sep 2022 20:35:54 +0800 Subject: [PATCH] fix: rev-spec parsing can now handle the empty tree as full hex hash. (#470) Even though the empty-tree object can be found when searched via `Repository::find_object()`, previously it was not locatable when used during rev-spec parsing. --- .../revision/spec/parse/delegate/revision.rs | 11 +++++++- .../generated-archives/make_diff_repo.tar.xz | 3 +++ .../make_rev_spec_parse_repos.tar.xz | 4 +-- .../tests/fixtures/make_diff_repo.sh | 18 +++++++++++++ .../fixtures/make_rev_spec_parse_repos.sh | 1 + git-repository/tests/object/tree.rs | 27 +++++++++++++------ .../tests/revision/spec/from_bytes/mod.rs | 9 +++++++ 7 files changed, 62 insertions(+), 11 deletions(-) create mode 100644 git-repository/tests/fixtures/generated-archives/make_diff_repo.tar.xz create mode 100644 git-repository/tests/fixtures/make_diff_repo.sh diff --git a/git-repository/src/revision/spec/parse/delegate/revision.rs b/git-repository/src/revision/spec/parse/delegate/revision.rs index 851686b3dd..ead9887e14 100644 --- a/git-repository/src/revision/spec/parse/delegate/revision.rs +++ b/git-repository/src/revision/spec/parse/delegate/revision.rs @@ -40,7 +40,16 @@ impl<'repo> delegate::Revision for Delegate<'repo> { self.last_call_was_disambiguate_prefix[self.idx] = true; let mut candidates = Some(HashSet::default()); self.prefix[self.idx] = Some(prefix); - match self.repo.objects.lookup_prefix(prefix, candidates.as_mut()) { + + let empty_tree_id = git_hash::ObjectId::empty_tree(prefix.as_oid().kind()); + let res = if prefix.as_oid() == empty_tree_id { + candidates.as_mut().expect("set").insert(empty_tree_id); + Ok(Some(Err(()))) + } else { + self.repo.objects.lookup_prefix(prefix, candidates.as_mut()) + }; + + match res { Err(err) => { self.err.push(object::find::existing::Error::Find(err).into()); None diff --git a/git-repository/tests/fixtures/generated-archives/make_diff_repo.tar.xz b/git-repository/tests/fixtures/generated-archives/make_diff_repo.tar.xz new file mode 100644 index 0000000000..9f9fe38849 --- /dev/null +++ b/git-repository/tests/fixtures/generated-archives/make_diff_repo.tar.xz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2123aa9e3fd85166d35c02c59ce8f0e8aaa3631c3dd4e43468f98d26cddf049c +size 11128 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 e2d3029e8d..699d49f7e7 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:4de4199add061d1ef825b392162898fcf4da67f69c5d588012fe4d71465c768f -size 28372 +oid sha256:0edf5366df74aaeb3c58ad070f9f9a522d0fae049f0905b7372ff28d0b3628bf +size 28716 diff --git a/git-repository/tests/fixtures/make_diff_repo.sh b/git-repository/tests/fixtures/make_diff_repo.sh new file mode 100644 index 0000000000..431c02138a --- /dev/null +++ b/git-repository/tests/fixtures/make_diff_repo.sh @@ -0,0 +1,18 @@ +#!/bin/bash +set -eu -o pipefail + +git init -q + +git checkout -b main +mkdir dir +touch a b dir/c +git add . +git commit -q -m c1 + +echo a >> a +echo b >> b +echo dir/c >> dir/c +git commit -q -am c2 + +echo a1 >> a +git commit -q -am c3 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 a0bad26d05..1d02c182d0 100644 --- a/git-repository/tests/fixtures/make_rev_spec_parse_repos.sh +++ b/git-repository/tests/fixtures/make_rev_spec_parse_repos.sh @@ -389,6 +389,7 @@ git init complex_graph baseline "@^{tree}" baseline "@:" + baseline "4b825dc642cb6eb9a060e54bf8d69288fbee4904" ) git init new diff --git a/git-repository/tests/object/tree.rs b/git-repository/tests/object/tree.rs index 1a0300ec41..d120350666 100644 --- a/git-repository/tests/object/tree.rs +++ b/git-repository/tests/object/tree.rs @@ -1,5 +1,5 @@ mod diff { - use crate::remote; + use crate::named_repo; use git_object::bstr::ByteSlice; use git_object::tree::EntryMode; use git_repository as git; @@ -8,9 +8,9 @@ mod diff { #[test] fn changes_against_tree_modified() { - let repo = remote::repo("base"); - let from = tree_named(&repo, "g"); - let to = tree_named(&repo, "h"); + let repo = named_repo("make_diff_repo.sh").unwrap(); + let from = tree_named(&repo, "@^{/c3}~1"); + let to = tree_named(&repo, ":/c3"); from.changes() .for_each_to_obtain_tree(&to, |change| -> Result<_, Infallible> { assert_eq!(change.location, "", "without configuration the location field is empty"); @@ -23,22 +23,33 @@ mod diff { } => { assert_eq!(previous_entry_mode, EntryMode::Blob); assert_eq!(entry_mode, EntryMode::Blob); - assert_eq!(previous_id.object().unwrap().data.as_bstr(), "g\n"); - assert_eq!(id.object().unwrap().data.as_bstr(), "h\n"); + assert_eq!(previous_id.object().unwrap().data.as_bstr(), "a\n"); + assert_eq!(id.object().unwrap().data.as_bstr(), "a\na1\n"); Ok(Default::default()) } Event::Deletion { .. } | Event::Addition { .. } => unreachable!("only modification is expected"), } }) .unwrap(); + } + #[test] + fn changes_against_tree_with_filename_tracking() { + let repo = named_repo("make_diff_repo.sh").unwrap(); + let from = tree_named( + &repo, + &git::hash::ObjectId::empty_tree(git::hash::Kind::Sha1).to_string(), + ); + let to = tree_named(&repo, ":/c1"); + let mut expected = vec!["a", "b", "c"]; from.changes() .track_filename() .for_each_to_obtain_tree(&to, |change| -> Result<_, Infallible> { - assert_eq!(change.location, "file"); - Ok(git::object::tree::diff::Action::Continue) + expected.retain(|name| name != change.location); + Ok(Default::default()) }) .unwrap(); + assert_eq!(expected, Vec::<&str>::new(), "all paths should have been seen") } fn tree_named<'repo>(repo: &'repo git::Repository, rev_spec: &str) -> git::Tree<'repo> { diff --git a/git-repository/tests/revision/spec/from_bytes/mod.rs b/git-repository/tests/revision/spec/from_bytes/mod.rs index a26b257ea1..b341badfa9 100644 --- a/git-repository/tests/revision/spec/from_bytes/mod.rs +++ b/git-repository/tests/revision/spec/from_bytes/mod.rs @@ -123,3 +123,12 @@ fn access_blob_through_tree() { "Could not find path \"missing\" in tree 0000000000c of parent object 0000000000c" ); } + +#[test] +fn empty_tree_as_full_name() { + let repo = repo("complex_graph").unwrap(); + assert_eq!( + parse_spec("4b825dc642cb6eb9a060e54bf8d69288fbee4904", &repo).unwrap(), + Spec::from_id(hex_to_id("4b825dc642cb6eb9a060e54bf8d69288fbee4904").attach(&repo)) + ); +}