Git fork
1#!/bin/sh
2
3test_description='object name disambiguation
4
5Create blobs, trees, commits and a tag that all share the same
6prefix, and make sure "git rev-parse" can take advantage of
7type information to disambiguate short object names that are
8not necessarily unique.
9
10The final history used in the test has five commits, with the bottom
11one tagged as v1.0.0. They all have one regular file each.
12
13 +-------------------------------------------+
14 | |
15 | .-------b3wettvi---- ad2uee |
16 | / / |
17 | a2onsxbvj---czy8f73t--ioiley5o |
18 | |
19 +-------------------------------------------+
20
21'
22
23GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
24export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
25
26. ./test-lib.sh
27. "$TEST_DIRECTORY/lib-loose.sh"
28
29test_cmp_failed_rev_parse () {
30 dir=$1
31 rev=$2
32
33 cat >expect &&
34 test_must_fail git -C "$dir" rev-parse "$rev" 2>actual.raw &&
35 sed "s/\($rev\)[0-9a-f]*/\1.../" <actual.raw >actual &&
36 test_cmp expect actual
37}
38
39test_expect_success 'ambiguous blob output' '
40 git init --bare blob.prefix &&
41 (
42 cd blob.prefix &&
43
44 # Both start with "dead..", under both SHA-1 and SHA-256
45 echo brocdnra | git hash-object -w --stdin &&
46 echo brigddsv | git hash-object -w --stdin &&
47
48 # Both start with "beef.."
49 echo 1agllotbh | git hash-object -w --stdin &&
50 echo 1bbfctrkc | git hash-object -w --stdin
51 ) &&
52
53 test_must_fail git -C blob.prefix rev-parse dead &&
54 test_cmp_failed_rev_parse blob.prefix beef <<-\EOF
55 error: short object ID beef... is ambiguous
56 hint: The candidates are:
57 hint: beef... blob
58 hint: beef... blob
59 fatal: ambiguous argument '\''beef...'\'': unknown revision or path not in the working tree.
60 Use '\''--'\'' to separate paths from revisions, like this:
61 '\''git <command> [<revision>...] -- [<file>...]'\''
62 EOF
63'
64
65test_expect_success 'ambiguous loose bad object parsed as OBJ_BAD' '
66 git init --bare blob.bad &&
67 (
68 cd blob.bad &&
69
70 # Both have the prefix "bad0"
71 echo xyzfaowcoh | loose_obj objects bad &&
72 echo xyzhjpyvwl | loose_obj objects bad
73 ) &&
74
75 test_cmp_failed_rev_parse blob.bad bad0 <<-\EOF
76 error: short object ID bad0... is ambiguous
77 fatal: invalid object type
78 EOF
79'
80
81test_expect_success POSIXPERM 'ambigous zlib corrupt loose blob' '
82 git init --bare blob.corrupt &&
83 (
84 cd blob.corrupt &&
85
86 # Both have the prefix "cafe"
87 echo bnkxmdwz | git hash-object -w --stdin &&
88 oid=$(echo bmwsjxzi | git hash-object -w --stdin) &&
89
90 oidf=objects/$(test_oid_to_path "$oid") &&
91 chmod 755 $oidf &&
92 echo broken >$oidf
93 ) &&
94
95 test_cmp_failed_rev_parse blob.corrupt cafe <<-\EOF
96 error: short object ID cafe... is ambiguous
97 error: inflate: data stream error (incorrect header check)
98 error: unable to unpack cafe... header
99 error: inflate: data stream error (incorrect header check)
100 error: unable to unpack cafe... header
101 hint: The candidates are:
102 hint: cafe... [bad object]
103 hint: cafe... blob
104 fatal: ambiguous argument '\''cafe...'\'': unknown revision or path not in the working tree.
105 Use '\''--'\'' to separate paths from revisions, like this:
106 '\''git <command> [<revision>...] -- [<file>...]'\''
107 EOF
108'
109
110if ! test_have_prereq SHA1
111then
112 skip_all='not using SHA-1 for objects'
113 test_done
114fi
115
116test_expect_success 'blob and tree' '
117 test_tick &&
118 (
119 test_write_lines 0 1 2 3 4 5 6 7 8 9 &&
120 echo &&
121 echo b1rwzyc3
122 ) >a0blgqsjc &&
123
124 # create one blob 0000000000b36
125 git add a0blgqsjc &&
126
127 # create one tree 0000000000cdc
128 git write-tree
129'
130
131test_expect_success 'warn ambiguity when no candidate matches type hint' '
132 test_must_fail git rev-parse --verify 000000000^{commit} 2>actual &&
133 test_grep "short object ID 000000000 is ambiguous" actual
134'
135
136test_expect_success 'disambiguate tree-ish' '
137 # feed tree-ish in an unambiguous way
138 git rev-parse --verify 0000000000cdc:a0blgqsjc &&
139
140 # ambiguous at the object name level, but there is only one
141 # such tree-ish (the other is a blob)
142 git rev-parse --verify 000000000:a0blgqsjc
143'
144
145test_expect_success 'disambiguate blob' '
146 sed -e "s/|$//" >patch <<-EOF &&
147 diff --git a/frotz b/frotz
148 index 000000000..ffffff 100644
149 --- a/frotz
150 +++ b/frotz
151 @@ -10,3 +10,4 @@
152 9
153 |
154 b1rwzyc3
155 +irwry
156 EOF
157 (
158 GIT_INDEX_FILE=frotz &&
159 export GIT_INDEX_FILE &&
160 git apply --build-fake-ancestor frotz patch &&
161 git cat-file blob :frotz >actual
162 ) &&
163 test_cmp a0blgqsjc actual
164'
165
166test_expect_success 'disambiguate tree' '
167 commit=$(echo "d7xm" | git commit-tree 000000000) &&
168 # this commit is fffff2e and not ambiguous with the 00000* objects
169 test $(git rev-parse $commit^{tree}) = $(git rev-parse 0000000000cdc)
170'
171
172test_expect_success 'first commit' '
173 # create one commit 0000000000e4f
174 git commit -m a2onsxbvj
175'
176
177test_expect_success 'disambiguate commit-ish' '
178 # feed commit-ish in an unambiguous way
179 git rev-parse --verify 0000000000e4f^{commit} &&
180
181 # ambiguous at the object name level, but there is only one
182 # such commit (the others are tree and blob)
183 git rev-parse --verify 000000000^{commit} &&
184
185 # likewise
186 git rev-parse --verify 000000000^0
187'
188
189test_expect_success 'disambiguate commit' '
190 commit=$(echo "hoaxj" | git commit-tree 0000000000cdc -p 000000000) &&
191 # this commit is ffffffd8 and not ambiguous with the 00000* objects
192 test $(git rev-parse $commit^) = $(git rev-parse 0000000000e4f)
193'
194
195test_expect_success 'log name1..name2 takes only commit-ishes on both ends' '
196 # These are underspecified from the prefix-length point of view
197 # to disambiguate the commit with other objects, but there is only
198 # one commit that has 00000* prefix at this point.
199 git log 000000000..000000000 &&
200 git log ..000000000 &&
201 git log 000000000.. &&
202 git log 000000000...000000000 &&
203 git log ...000000000 &&
204 git log 000000000...
205'
206
207test_expect_success 'rev-parse name1..name2 takes only commit-ishes on both ends' '
208 # Likewise.
209 git rev-parse 000000000..000000000 &&
210 git rev-parse ..000000000 &&
211 git rev-parse 000000000..
212'
213
214test_expect_success 'git log takes only commit-ish' '
215 # Likewise.
216 git log 000000000
217'
218
219test_expect_success 'git reset takes only commit-ish' '
220 # Likewise.
221 git reset 000000000
222'
223
224test_expect_success 'first tag' '
225 # create one tag 0000000000f8f
226 git tag -a -m j7cp83um v1.0.0
227'
228
229test_expect_failure 'two semi-ambiguous commit-ish' '
230 # At this point, we have a tag 0000000000f8f that points
231 # at a commit 0000000000e4f, and a tree and a blob that
232 # share 0000000000 prefix with these tag and commit.
233 #
234 # Once the parser becomes ultra-smart, it could notice that
235 # 0000000000 before ^{commit} name many different objects, but
236 # that only two (HEAD and v1.0.0 tag) can be peeled to commit,
237 # and that peeling them down to commit yield the same commit
238 # without ambiguity.
239 git rev-parse --verify 0000000000^{commit} &&
240
241 # likewise
242 git log 0000000000..0000000000 &&
243 git log ..0000000000 &&
244 git log 0000000000.. &&
245 git log 0000000000...0000000000 &&
246 git log ...0000000000 &&
247 git log 0000000000...
248'
249
250test_expect_failure 'three semi-ambiguous tree-ish' '
251 # Likewise for tree-ish. HEAD, v1.0.0 and HEAD^{tree} share
252 # the prefix but peeling them to tree yields the same thing
253 git rev-parse --verify 0000000000^{tree}
254'
255
256test_expect_success 'parse describe name' '
257 # feed an unambiguous describe name
258 git rev-parse --verify v1.0.0-0-g0000000000e4f &&
259
260 # ambiguous at the object name level, but there is only one
261 # such commit (others are blob, tree and tag)
262 git rev-parse --verify v1.0.0-0-g000000000
263'
264
265test_expect_success 'more history' '
266 # commit 0000000000043
267 git mv a0blgqsjc d12cr3h8t &&
268 echo h62xsjeu >>d12cr3h8t &&
269 git add d12cr3h8t &&
270
271 test_tick &&
272 git commit -m czy8f73t &&
273
274 # commit 00000000008ec
275 git mv d12cr3h8t j000jmpzn &&
276 echo j08bekfvt >>j000jmpzn &&
277 git add j000jmpzn &&
278
279 test_tick &&
280 git commit -m ioiley5o &&
281
282 # commit 0000000005b0
283 git checkout v1.0.0^0 &&
284 git mv a0blgqsjc f5518nwu &&
285
286 test_write_lines h62xsjeu j08bekfvt kg7xflhm >>f5518nwu &&
287 git add f5518nwu &&
288
289 test_tick &&
290 git commit -m b3wettvi &&
291 side=$(git rev-parse HEAD) &&
292
293 # commit 000000000066
294 git checkout main &&
295
296 # If you use recursive, merge will fail and you will need to
297 # clean up a0blgqsjc as well. If you use resolve, merge will
298 # succeed.
299 test_might_fail git merge --no-commit -s recursive $side &&
300 git rm -f f5518nwu j000jmpzn &&
301
302 test_might_fail git rm -f a0blgqsjc &&
303 (
304 git cat-file blob $side:f5518nwu &&
305 echo j3l0i9s6
306 ) >ab2gs879 &&
307 git add ab2gs879 &&
308
309 test_tick &&
310 git commit -m ad2uee
311
312'
313
314test_expect_failure 'parse describe name taking advantage of generation' '
315 # ambiguous at the object name level, but there is only one
316 # such commit at generation 0
317 git rev-parse --verify v1.0.0-0-g000000000 &&
318
319 # likewise for generation 2 and 4
320 git rev-parse --verify v1.0.0-2-g000000000 &&
321 git rev-parse --verify v1.0.0-4-g000000000
322'
323
324# Note: because rev-parse does not even try to disambiguate based on
325# the generation number, this test currently succeeds for a wrong
326# reason. When it learns to use the generation number, the previous
327# test should succeed, and also this test should fail because the
328# describe name used in the test with generation number can name two
329# commits. Make sure that such a future enhancement does not randomly
330# pick one.
331test_expect_success 'parse describe name not ignoring ambiguity' '
332 # ambiguous at the object name level, and there are two such
333 # commits at generation 1
334 test_must_fail git rev-parse --verify v1.0.0-1-g000000000
335'
336
337test_expect_success 'ambiguous commit-ish' '
338 # Now there are many commits that begin with the
339 # common prefix, none of these should pick one at
340 # random. They all should result in ambiguity errors.
341 test_must_fail git rev-parse --verify 00000000^{commit} &&
342
343 # likewise
344 test_must_fail git log 000000000..000000000 &&
345 test_must_fail git log ..000000000 &&
346 test_must_fail git log 000000000.. &&
347 test_must_fail git log 000000000...000000000 &&
348 test_must_fail git log ...000000000 &&
349 test_must_fail git log 000000000...
350'
351
352# There are three objects with this prefix: a blob, a tree, and a tag. We know
353# the blob will not pass as a treeish, but the tree and tag should (and thus
354# cause an error).
355test_expect_success 'ambiguous tags peel to treeish' '
356 test_must_fail git rev-parse 0000000000f^{tree}
357'
358
359test_expect_success 'rev-parse --disambiguate' '
360 # The test creates 16 objects that share the prefix and two
361 # commits created by commit-tree in earlier tests share a
362 # different prefix.
363 git rev-parse --disambiguate=000000000 >actual &&
364 test_line_count = 16 actual &&
365 test "$(sed -e "s/^\(.........\).*/\1/" actual | sort -u)" = 000000000
366'
367
368test_expect_success 'rev-parse --disambiguate drops duplicates' '
369 git rev-parse --disambiguate=000000000 >expect &&
370 git pack-objects .git/objects/pack/pack <expect &&
371 git rev-parse --disambiguate=000000000 >actual &&
372 test_cmp expect actual
373'
374
375test_expect_success 'ambiguous 40-hex ref' '
376 TREE=$(git mktree </dev/null) &&
377 REF=$(git rev-parse HEAD) &&
378 VAL=$(git commit-tree $TREE </dev/null) &&
379 git update-ref refs/heads/$REF $VAL &&
380 test $(git rev-parse $REF 2>err) = $REF &&
381 grep "refname.*${REF}.*ambiguous" err
382'
383
384test_expect_success 'ambiguous short sha1 ref' '
385 TREE=$(git mktree </dev/null) &&
386 REF=$(git rev-parse --short HEAD) &&
387 VAL=$(git commit-tree $TREE </dev/null) &&
388 git update-ref refs/heads/$REF $VAL &&
389 test $(git rev-parse $REF 2>err) = $VAL &&
390 grep "refname.*${REF}.*ambiguous" err
391'
392
393test_expect_success 'ambiguity errors are not repeated (raw)' '
394 test_must_fail git rev-parse 00000 2>stderr &&
395 grep "is ambiguous" stderr >errors &&
396 test_line_count = 1 errors
397'
398
399test_expect_success 'ambiguity errors are not repeated (treeish)' '
400 test_must_fail git rev-parse 00000:foo 2>stderr &&
401 grep "is ambiguous" stderr >errors &&
402 test_line_count = 1 errors
403'
404
405test_expect_success 'ambiguity errors are not repeated (peel)' '
406 test_must_fail git rev-parse 00000^{commit} 2>stderr &&
407 grep "is ambiguous" stderr >errors &&
408 test_line_count = 1 errors
409'
410
411test_expect_success 'ambiguity hints' '
412 test_must_fail git rev-parse 000000000 2>stderr &&
413 grep ^hint: stderr >hints &&
414 # 16 candidates, plus one intro line
415 test_line_count = 17 hints
416'
417
418test_expect_success 'ambiguity hints respect type' '
419 test_must_fail git rev-parse 000000000^{commit} 2>stderr &&
420 grep ^hint: stderr >hints &&
421 # 5 commits, 1 tag (which is a committish), plus intro line
422 test_line_count = 7 hints
423'
424
425test_expect_success 'failed type-selector still shows hint' '
426 # these two blobs share the same prefix "ee3d", but neither
427 # will pass for a commit
428 echo 851 | git hash-object --stdin -w &&
429 echo 872 | git hash-object --stdin -w &&
430 test_must_fail git rev-parse ee3d^{commit} 2>stderr &&
431 grep ^hint: stderr >hints &&
432 test_line_count = 3 hints
433'
434
435test_expect_success 'core.disambiguate config can prefer types' '
436 # ambiguous between tree and tag
437 sha1=0000000000f &&
438 test_must_fail git rev-parse $sha1 &&
439 git rev-parse $sha1^{commit} &&
440 git -c core.disambiguate=committish rev-parse $sha1
441'
442
443test_expect_success 'core.disambiguate does not override context' '
444 # treeish ambiguous between tag and tree
445 test_must_fail \
446 git -c core.disambiguate=committish rev-parse $sha1^{tree}
447'
448
449test_expect_success 'ambiguous commits are printed by type first, then hash order' '
450 test_must_fail git rev-parse 0000 2>stderr &&
451 grep ^hint: stderr >hints &&
452 grep 0000 hints >objects &&
453 cat >expected <<-\EOF &&
454 tag
455 commit
456 tree
457 blob
458 EOF
459 awk "{print \$3}" <objects >objects.types &&
460 uniq <objects.types >objects.types.uniq &&
461 test_cmp expected objects.types.uniq &&
462 for type in tag commit tree blob
463 do
464 grep $type objects >$type.objects &&
465 sort $type.objects >$type.objects.sorted &&
466 test_cmp $type.objects.sorted $type.objects || return 1
467 done
468'
469
470test_expect_success 'cat-file --batch and --batch-check show ambiguous' '
471 echo "0000 ambiguous" >expect &&
472 echo 0000 | git cat-file --batch-check >actual 2>err &&
473 test_cmp expect actual &&
474 test_grep hint: err &&
475 echo 0000 | git cat-file --batch >actual 2>err &&
476 test_cmp expect actual &&
477 test_grep hint: err
478'
479
480test_done