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