Skip to content

Commit ae38660

Browse files
committed
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.
1 parent 9d01fb4 commit ae38660

File tree

7 files changed

+62
-11
lines changed

7 files changed

+62
-11
lines changed

git-repository/src/revision/spec/parse/delegate/revision.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,16 @@ impl<'repo> delegate::Revision for Delegate<'repo> {
4040
self.last_call_was_disambiguate_prefix[self.idx] = true;
4141
let mut candidates = Some(HashSet::default());
4242
self.prefix[self.idx] = Some(prefix);
43-
match self.repo.objects.lookup_prefix(prefix, candidates.as_mut()) {
43+
44+
let empty_tree_id = git_hash::ObjectId::empty_tree(prefix.as_oid().kind());
45+
let res = if prefix.as_oid() == empty_tree_id {
46+
candidates.as_mut().expect("set").insert(empty_tree_id);
47+
Ok(Some(Err(())))
48+
} else {
49+
self.repo.objects.lookup_prefix(prefix, candidates.as_mut())
50+
};
51+
52+
match res {
4453
Err(err) => {
4554
self.err.push(object::find::existing::Error::Find(err).into());
4655
None
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:2123aa9e3fd85166d35c02c59ce8f0e8aaa3631c3dd4e43468f98d26cddf049c
3+
size 11128
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
version https://git-lfs.github.com/spec/v1
2-
oid sha256:4de4199add061d1ef825b392162898fcf4da67f69c5d588012fe4d71465c768f
3-
size 28372
2+
oid sha256:0edf5366df74aaeb3c58ad070f9f9a522d0fae049f0905b7372ff28d0b3628bf
3+
size 28716
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!/bin/bash
2+
set -eu -o pipefail
3+
4+
git init -q
5+
6+
git checkout -b main
7+
mkdir dir
8+
touch a b dir/c
9+
git add .
10+
git commit -q -m c1
11+
12+
echo a >> a
13+
echo b >> b
14+
echo dir/c >> dir/c
15+
git commit -q -am c2
16+
17+
echo a1 >> a
18+
git commit -q -am c3

git-repository/tests/fixtures/make_rev_spec_parse_repos.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ git init complex_graph
389389

390390
baseline "@^{tree}"
391391
baseline "@:"
392+
baseline "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
392393
)
393394

394395
git init new

git-repository/tests/object/tree.rs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
mod diff {
2-
use crate::remote;
2+
use crate::named_repo;
33
use git_object::bstr::ByteSlice;
44
use git_object::tree::EntryMode;
55
use git_repository as git;
@@ -8,9 +8,9 @@ mod diff {
88

99
#[test]
1010
fn changes_against_tree_modified() {
11-
let repo = remote::repo("base");
12-
let from = tree_named(&repo, "g");
13-
let to = tree_named(&repo, "h");
11+
let repo = named_repo("make_diff_repo.sh").unwrap();
12+
let from = tree_named(&repo, "@^{/c3}~1");
13+
let to = tree_named(&repo, ":/c3");
1414
from.changes()
1515
.for_each_to_obtain_tree(&to, |change| -> Result<_, Infallible> {
1616
assert_eq!(change.location, "", "without configuration the location field is empty");
@@ -23,22 +23,33 @@ mod diff {
2323
} => {
2424
assert_eq!(previous_entry_mode, EntryMode::Blob);
2525
assert_eq!(entry_mode, EntryMode::Blob);
26-
assert_eq!(previous_id.object().unwrap().data.as_bstr(), "g\n");
27-
assert_eq!(id.object().unwrap().data.as_bstr(), "h\n");
26+
assert_eq!(previous_id.object().unwrap().data.as_bstr(), "a\n");
27+
assert_eq!(id.object().unwrap().data.as_bstr(), "a\na1\n");
2828
Ok(Default::default())
2929
}
3030
Event::Deletion { .. } | Event::Addition { .. } => unreachable!("only modification is expected"),
3131
}
3232
})
3333
.unwrap();
34+
}
35+
#[test]
36+
fn changes_against_tree_with_filename_tracking() {
37+
let repo = named_repo("make_diff_repo.sh").unwrap();
38+
let from = tree_named(
39+
&repo,
40+
&git::hash::ObjectId::empty_tree(git::hash::Kind::Sha1).to_string(),
41+
);
42+
let to = tree_named(&repo, ":/c1");
3443

44+
let mut expected = vec!["a", "b", "c"];
3545
from.changes()
3646
.track_filename()
3747
.for_each_to_obtain_tree(&to, |change| -> Result<_, Infallible> {
38-
assert_eq!(change.location, "file");
39-
Ok(git::object::tree::diff::Action::Continue)
48+
expected.retain(|name| name != change.location);
49+
Ok(Default::default())
4050
})
4151
.unwrap();
52+
assert_eq!(expected, Vec::<&str>::new(), "all paths should have been seen")
4253
}
4354

4455
fn tree_named<'repo>(repo: &'repo git::Repository, rev_spec: &str) -> git::Tree<'repo> {

git-repository/tests/revision/spec/from_bytes/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,12 @@ fn access_blob_through_tree() {
123123
"Could not find path \"missing\" in tree 0000000000c of parent object 0000000000c"
124124
);
125125
}
126+
127+
#[test]
128+
fn empty_tree_as_full_name() {
129+
let repo = repo("complex_graph").unwrap();
130+
assert_eq!(
131+
parse_spec("4b825dc642cb6eb9a060e54bf8d69288fbee4904", &repo).unwrap(),
132+
Spec::from_id(hex_to_id("4b825dc642cb6eb9a060e54bf8d69288fbee4904").attach(&repo))
133+
);
134+
}

0 commit comments

Comments
 (0)