Git fork
1#!/bin/sh
2#
3# Copyright (c) 2020 Google LLC
4#
5
6test_description='reftable basics'
7
8GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
9export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
10GIT_TEST_DEFAULT_REF_FORMAT=reftable
11export GIT_TEST_DEFAULT_REF_FORMAT
12
13. ./test-lib.sh
14
15INVALID_OID=$(test_oid 001)
16
17test_expect_success 'pack-refs does not crash with -h' '
18 test_expect_code 129 git pack-refs -h >usage &&
19 test_grep "[Uu]sage: git pack-refs " usage &&
20 test_expect_code 129 nongit git pack-refs -h >usage &&
21 test_grep "[Uu]sage: git pack-refs " usage
22'
23
24test_expect_success 'init: creates basic reftable structures' '
25 test_when_finished "rm -rf repo" &&
26 git init repo &&
27 test_path_is_dir repo/.git/reftable &&
28 test_path_is_file repo/.git/reftable/tables.list &&
29 echo reftable >expect &&
30 git -C repo rev-parse --show-ref-format >actual &&
31 test_cmp expect actual
32'
33
34test_expect_success 'init: sha256 object format via environment variable' '
35 test_when_finished "rm -rf repo" &&
36 GIT_DEFAULT_HASH=sha256 git init repo &&
37 cat >expect <<-EOF &&
38 sha256
39 reftable
40 EOF
41 git -C repo rev-parse --show-object-format --show-ref-format >actual &&
42 test_cmp expect actual
43'
44
45test_expect_success 'init: sha256 object format via option' '
46 test_when_finished "rm -rf repo" &&
47 git init --object-format=sha256 repo &&
48 cat >expect <<-EOF &&
49 sha256
50 reftable
51 EOF
52 git -C repo rev-parse --show-object-format --show-ref-format >actual &&
53 test_cmp expect actual
54'
55
56test_expect_success 'init: reinitializing reftable backend succeeds' '
57 test_when_finished "rm -rf repo" &&
58 git init repo &&
59 test_commit -C repo A &&
60
61 git -C repo for-each-ref >expect &&
62 git init --ref-format=reftable repo &&
63 git -C repo for-each-ref >actual &&
64 test_cmp expect actual
65'
66
67test_expect_success 'init: reinitializing files with reftable backend fails' '
68 test_when_finished "rm -rf repo" &&
69 git init --ref-format=files repo &&
70 test_commit -C repo file &&
71
72 cp repo/.git/HEAD expect &&
73 test_must_fail git init --ref-format=reftable repo &&
74 test_cmp expect repo/.git/HEAD
75'
76
77test_expect_success 'init: reinitializing reftable with files backend fails' '
78 test_when_finished "rm -rf repo" &&
79 git init --ref-format=reftable repo &&
80 test_commit -C repo file &&
81
82 cp repo/.git/HEAD expect &&
83 test_must_fail git init --ref-format=files repo &&
84 test_cmp expect repo/.git/HEAD
85'
86
87test_expect_perms () {
88 local perms="$1" &&
89 local file="$2" &&
90 local actual="$(ls -l "$file")" &&
91
92 case "$actual" in
93 $perms*)
94 : happy
95 ;;
96 *)
97 echo "$(basename $2) is not $perms but $actual"
98 false
99 ;;
100 esac
101}
102
103test_expect_reftable_perms () {
104 local umask="$1"
105 local shared="$2"
106 local expect="$3"
107
108 test_expect_success POSIXPERM "init: honors --shared=$shared with umask $umask" '
109 test_when_finished "rm -rf repo" &&
110 (
111 umask $umask &&
112 git init --shared=$shared repo
113 ) &&
114 test_expect_perms "$expect" repo/.git/reftable/tables.list &&
115 for table in repo/.git/reftable/*.ref
116 do
117 test_expect_perms "$expect" "$table" ||
118 return 1
119 done
120 '
121
122 test_expect_success POSIXPERM "pack-refs: honors --shared=$shared with umask $umask" '
123 test_when_finished "rm -rf repo" &&
124 (
125 umask $umask &&
126 git init --shared=$shared repo &&
127 test_commit -C repo A &&
128 test_line_count = 2 repo/.git/reftable/tables.list &&
129 git -C repo pack-refs
130 ) &&
131 test_expect_perms "$expect" repo/.git/reftable/tables.list &&
132 for table in repo/.git/reftable/*.ref
133 do
134 test_expect_perms "$expect" "$table" ||
135 return 1
136 done
137 '
138}
139
140test_expect_reftable_perms 002 umask "-rw-rw-r--"
141test_expect_reftable_perms 022 umask "-rw-r--r--"
142test_expect_reftable_perms 027 umask "-rw-r-----"
143
144test_expect_reftable_perms 002 group "-rw-rw-r--"
145test_expect_reftable_perms 022 group "-rw-rw-r--"
146test_expect_reftable_perms 027 group "-rw-rw----"
147
148test_expect_reftable_perms 002 world "-rw-rw-r--"
149test_expect_reftable_perms 022 world "-rw-rw-r--"
150test_expect_reftable_perms 027 world "-rw-rw-r--"
151
152test_expect_success 'clone: can clone reftable repository' '
153 test_when_finished "rm -rf repo clone" &&
154 git init repo &&
155 test_commit -C repo message1 file1 &&
156
157 git clone repo cloned &&
158 echo reftable >expect &&
159 git -C cloned rev-parse --show-ref-format >actual &&
160 test_cmp expect actual &&
161 test_path_is_file cloned/file1
162'
163
164test_expect_success 'clone: can clone reffiles into reftable repository' '
165 test_when_finished "rm -rf reffiles reftable" &&
166 git init --ref-format=files reffiles &&
167 test_commit -C reffiles A &&
168 git clone --ref-format=reftable ./reffiles reftable &&
169
170 git -C reffiles rev-parse HEAD >expect &&
171 git -C reftable rev-parse HEAD >actual &&
172 test_cmp expect actual &&
173
174 git -C reftable rev-parse --show-ref-format >actual &&
175 echo reftable >expect &&
176 test_cmp expect actual &&
177
178 git -C reffiles rev-parse --show-ref-format >actual &&
179 echo files >expect &&
180 test_cmp expect actual
181'
182
183test_expect_success 'clone: can clone reftable into reffiles repository' '
184 test_when_finished "rm -rf reffiles reftable" &&
185 git init --ref-format=reftable reftable &&
186 test_commit -C reftable A &&
187 git clone --ref-format=files ./reftable reffiles &&
188
189 git -C reftable rev-parse HEAD >expect &&
190 git -C reffiles rev-parse HEAD >actual &&
191 test_cmp expect actual &&
192
193 git -C reftable rev-parse --show-ref-format >actual &&
194 echo reftable >expect &&
195 test_cmp expect actual &&
196
197 git -C reffiles rev-parse --show-ref-format >actual &&
198 echo files >expect &&
199 test_cmp expect actual
200'
201
202test_expect_success 'ref transaction: corrupted tables cause failure' '
203 test_when_finished "rm -rf repo" &&
204 git init repo &&
205 (
206 cd repo &&
207 test_commit file1 &&
208 for f in .git/reftable/*.ref
209 do
210 : >"$f" || return 1
211 done &&
212 test_must_fail git update-ref refs/heads/main HEAD
213 )
214'
215
216test_expect_success 'ref transaction: corrupted tables.list cause failure' '
217 test_when_finished "rm -rf repo" &&
218 git init repo &&
219 (
220 cd repo &&
221 test_commit file1 &&
222 echo garbage >.git/reftable/tables.list &&
223 test_must_fail git update-ref refs/heads/main HEAD
224 )
225'
226
227test_expect_success 'ref transaction: refuses to write ref causing F/D conflict' '
228 test_when_finished "rm -rf repo" &&
229 git init repo &&
230 test_commit -C repo file &&
231 test_must_fail git -C repo update-ref refs/heads/main/forbidden
232'
233
234test_expect_success 'ref transaction: deleting ref with invalid name fails' '
235 test_when_finished "rm -rf repo" &&
236 git init repo &&
237 test_commit -C repo file &&
238 test_must_fail git -C repo update-ref -d ../../my-private-file
239'
240
241test_expect_success 'ref transaction: can skip object ID verification' '
242 test_when_finished "rm -rf repo" &&
243 git init repo &&
244 test_must_fail test-tool -C repo ref-store main update-ref msg refs/heads/branch $INVALID_OID $ZERO_OID 0 &&
245 test-tool -C repo ref-store main update-ref msg refs/heads/branch $INVALID_OID $ZERO_OID REF_SKIP_OID_VERIFICATION
246'
247
248test_expect_success 'ref transaction: updating same ref multiple times fails' '
249 test_when_finished "rm -rf repo" &&
250 git init repo &&
251 test_commit -C repo A &&
252 cat >updates <<-EOF &&
253 update refs/heads/main $A
254 update refs/heads/main $A
255 EOF
256 cat >expect <<-EOF &&
257 fatal: multiple updates for ref ${SQ}refs/heads/main${SQ} not allowed
258 EOF
259 test_must_fail git -C repo update-ref --stdin <updates 2>err &&
260 test_cmp expect err
261'
262
263test_expect_success 'ref transaction: can delete symbolic self-reference with git-symbolic-ref(1)' '
264 test_when_finished "rm -rf repo" &&
265 git init repo &&
266 git -C repo symbolic-ref refs/heads/self refs/heads/self &&
267 git -C repo symbolic-ref -d refs/heads/self
268'
269
270test_expect_success 'ref transaction: deleting symbolic self-reference without --no-deref fails' '
271 test_when_finished "rm -rf repo" &&
272 git init repo &&
273 git -C repo symbolic-ref refs/heads/self refs/heads/self &&
274 cat >expect <<-EOF &&
275 error: multiple updates for ${SQ}refs/heads/self${SQ} (including one via symref ${SQ}refs/heads/self${SQ}) are not allowed
276 EOF
277 test_must_fail git -C repo update-ref -d refs/heads/self 2>err &&
278 test_cmp expect err
279'
280
281test_expect_success 'ref transaction: deleting symbolic self-reference with --no-deref succeeds' '
282 test_when_finished "rm -rf repo" &&
283 git init repo &&
284 git -C repo symbolic-ref refs/heads/self refs/heads/self &&
285 git -C repo update-ref -d --no-deref refs/heads/self
286'
287
288test_expect_success 'ref transaction: creating symbolic ref fails with F/D conflict' '
289 test_when_finished "rm -rf repo" &&
290 git init repo &&
291 test_commit -C repo A &&
292 cat >expect <<-EOF &&
293 error: ${SQ}refs/heads/main${SQ} exists; cannot create ${SQ}refs/heads${SQ}
294 EOF
295 test_must_fail git -C repo symbolic-ref refs/heads refs/heads/foo 2>err &&
296 test_cmp expect err
297'
298
299test_expect_success 'ref transaction: ref deletion' '
300 test_when_finished "rm -rf repo" &&
301 git init repo &&
302 (
303 cd repo &&
304 test_commit file &&
305 HEAD_OID=$(git show-ref -s --verify HEAD) &&
306 cat >expect <<-EOF &&
307 $HEAD_OID refs/heads/main
308 $HEAD_OID refs/tags/file
309 EOF
310 git show-ref >actual &&
311 test_cmp expect actual &&
312
313 test_must_fail git update-ref -d refs/tags/file $INVALID_OID &&
314 git show-ref >actual &&
315 test_cmp expect actual &&
316
317 git update-ref -d refs/tags/file $HEAD_OID &&
318 echo "$HEAD_OID refs/heads/main" >expect &&
319 git show-ref >actual &&
320 test_cmp expect actual
321 )
322'
323
324test_expect_success 'ref transaction: writes cause auto-compaction' '
325 test_when_finished "rm -rf repo" &&
326
327 git init repo &&
328 test_line_count = 1 repo/.git/reftable/tables.list &&
329
330 test_commit -C repo --no-tag A &&
331 test_line_count = 1 repo/.git/reftable/tables.list &&
332
333 test_commit -C repo --no-tag B &&
334 test_line_count = 1 repo/.git/reftable/tables.list
335'
336
337test_expect_success 'ref transaction: env var disables compaction' '
338 test_when_finished "rm -rf repo" &&
339
340 git init repo &&
341 test_commit -C repo A &&
342
343 start=$(wc -l <repo/.git/reftable/tables.list) &&
344 iterations=5 &&
345 expected=$((start + iterations)) &&
346
347 for i in $(test_seq $iterations)
348 do
349 GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
350 git -C repo update-ref branch-$i HEAD || return 1
351 done &&
352 test_line_count = $expected repo/.git/reftable/tables.list &&
353
354 git -C repo update-ref foo HEAD &&
355 test_line_count -lt $expected repo/.git/reftable/tables.list
356'
357
358test_expect_success 'ref transaction: alternating table sizes are compacted' '
359 test_when_finished "rm -rf repo" &&
360
361 git init repo &&
362 test_commit -C repo A &&
363 for i in $(test_seq 5)
364 do
365 git -C repo branch -f foo &&
366 git -C repo branch -d foo || return 1
367 done &&
368 test_line_count = 2 repo/.git/reftable/tables.list
369'
370
371check_fsync_events () {
372 local trace="$1" &&
373 shift &&
374
375 cat >expect &&
376 sed -n \
377 -e '/^{"event":"counter",.*"category":"fsync",/ {
378 s/.*"category":"fsync",//;
379 s/}$//;
380 p;
381 }' \
382 <"$trace" >actual &&
383 test_cmp expect actual
384}
385
386test_expect_success 'ref transaction: writes are synced' '
387 test_when_finished "rm -rf repo" &&
388 git init repo &&
389 test_commit -C repo initial &&
390
391 GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
392 GIT_TEST_FSYNC=true \
393 git -C repo -c core.fsync=reference \
394 -c core.fsyncMethod=fsync update-ref refs/heads/branch HEAD &&
395 check_fsync_events trace2.txt <<-EOF
396 "name":"hardware-flush","count":4
397 EOF
398'
399
400test_expect_success 'ref transaction: empty transaction in empty repo' '
401 test_when_finished "rm -rf repo" &&
402 git init repo &&
403 test_commit -C repo --no-tag A &&
404 git -C repo update-ref -d refs/heads/main &&
405 test-tool -C repo ref-store main delete-refs REF_NO_DEREF msg HEAD &&
406 git -C repo update-ref --stdin <<-EOF
407 prepare
408 commit
409 EOF
410'
411
412test_expect_success 'ref transaction: fails gracefully when auto compaction fails' '
413 test_when_finished "rm -rf repo" &&
414 git init repo &&
415 (
416 cd repo &&
417
418 test_commit A &&
419 for i in $(test_seq 10)
420 do
421 git branch branch-$i &&
422 for table in .git/reftable/*.ref
423 do
424 touch "$table.lock" || exit 1
425 done ||
426 exit 1
427 done &&
428 test_line_count = 10 .git/reftable/tables.list
429 )
430'
431
432test_expect_success 'ref transaction: timeout acquiring tables.list lock' '
433 test_when_finished "rm -rf repo" &&
434 git init repo &&
435 (
436 cd repo &&
437 test_commit initial &&
438 >.git/reftable/tables.list.lock &&
439 test_must_fail git update-ref refs/heads/branch HEAD 2>err &&
440 test_grep "cannot lock references" err
441 )
442'
443
444test_expect_success 'ref transaction: retry acquiring tables.list lock' '
445 test_when_finished "rm -rf repo" &&
446 git init repo &&
447 (
448 cd repo &&
449 test_commit initial &&
450 LOCK=.git/reftable/tables.list.lock &&
451 >$LOCK &&
452 {
453 ( sleep 1 && rm -f $LOCK ) &
454 } &&
455 git -c reftable.lockTimeout=5000 update-ref refs/heads/branch HEAD
456 )
457'
458
459# This test fails most of the time on Cygwin systems. The root cause is
460# that Windows does not allow us to rename the "tables.list.lock" file into
461# place when "tables.list" is open for reading by a concurrent process. We have
462# worked around that in our MinGW-based rename emulation, but the Cygwin
463# emulation seems to be insufficient.
464test_expect_success !CYGWIN 'ref transaction: many concurrent writers' '
465 test_when_finished "rm -rf repo" &&
466 git init repo &&
467 (
468 cd repo &&
469 # Set a high timeout. While a couple of seconds should be
470 # plenty, using the address sanitizer will significantly slow
471 # us down here. So we are aiming way higher than you would ever
472 # think is necessary just to keep us from flaking. We could
473 # also lock indefinitely by passing -1, but that could
474 # potentially block CI jobs indefinitely if there was a bug
475 # here.
476 git config set reftable.lockTimeout 300000 &&
477 test_commit --no-tag initial &&
478
479 head=$(git rev-parse HEAD) &&
480 test_seq -f "$head commit\trefs/heads/branch-%d" 100 >expect &&
481 printf "%s commit\trefs/heads/main\n" "$head" >>expect &&
482
483 for i in $(test_seq 100)
484 do
485 { git update-ref refs/heads/branch-$i HEAD& } ||
486 return 1
487 done &&
488
489 wait &&
490 git for-each-ref --sort=v:refname >actual &&
491 test_cmp expect actual
492 )
493'
494
495test_expect_success 'pack-refs: compacts tables' '
496 test_when_finished "rm -rf repo" &&
497 git init repo &&
498
499 test_commit -C repo A &&
500 ls -1 repo/.git/reftable >table-files &&
501 test_line_count = 3 table-files &&
502 test_line_count = 2 repo/.git/reftable/tables.list &&
503
504 git -C repo pack-refs &&
505 ls -1 repo/.git/reftable >table-files &&
506 test_line_count = 2 table-files &&
507 test_line_count = 1 repo/.git/reftable/tables.list
508'
509
510test_expect_success 'pack-refs: compaction raises locking errors' '
511 test_when_finished "rm -rf repo" &&
512 git init repo &&
513 test_commit -C repo A &&
514 touch repo/.git/reftable/tables.list.lock &&
515 cat >expect <<-EOF &&
516 error: unable to compact stack: data is locked
517 EOF
518 test_must_fail git -C repo pack-refs 2>err &&
519 test_cmp expect err
520'
521
522for command in pack-refs gc "maintenance run --task=pack-refs"
523do
524test_expect_success "$command: auto compaction" '
525 test_when_finished "rm -rf repo" &&
526 git init repo &&
527 (
528 cd repo &&
529
530 test_commit A &&
531
532 # We need a bit of setup to ensure that git-gc(1) actually
533 # triggers, and that it does not write anything to the refdb.
534 git config gc.auto 1 &&
535 git config gc.autoDetach 0 &&
536 git config gc.reflogExpire never &&
537 git config gc.reflogExpireUnreachable never &&
538 test_oid blob17_1 | git hash-object -w --stdin &&
539
540 # The tables should have been auto-compacted, and thus auto
541 # compaction should not have to do anything.
542 ls -1 .git/reftable >tables-expect &&
543 test_line_count = 3 tables-expect &&
544 git $command --auto &&
545 ls -1 .git/reftable >tables-actual &&
546 test_cmp tables-expect tables-actual &&
547
548 test_oid blob17_2 | git hash-object -w --stdin &&
549
550 # Lock all tables, write some refs. Auto-compaction will be
551 # unable to compact tables and thus fails gracefully,
552 # compacting only those tables which are not locked.
553 ls .git/reftable/*.ref | sort |
554 while read table
555 do
556 touch "$table.lock" &&
557 basename "$table" >>tables.expect || exit 1
558 done &&
559 test_line_count = 2 .git/reftable/tables.list &&
560 git branch B &&
561 git branch C &&
562
563 # The new tables are auto-compacted, but the locked tables are
564 # left intact.
565 test_line_count = 3 .git/reftable/tables.list &&
566 head -n 2 .git/reftable/tables.list >tables.head &&
567 test_cmp tables.expect tables.head &&
568
569 rm .git/reftable/*.lock &&
570 git $command --auto &&
571 test_line_count = 1 .git/reftable/tables.list
572 )
573'
574done
575
576test_expect_success 'pack-refs: prunes stale tables' '
577 test_when_finished "rm -rf repo" &&
578 git init repo &&
579 touch repo/.git/reftable/stale-table.ref &&
580 git -C repo pack-refs &&
581 test_path_is_missing repo/.git/reftable/stable-ref.ref
582'
583
584test_expect_success 'pack-refs: does not prune non-table files' '
585 test_when_finished "rm -rf repo" &&
586 git init repo &&
587 touch repo/.git/reftable/garbage &&
588 git -C repo pack-refs &&
589 test_path_is_file repo/.git/reftable/garbage
590'
591
592test_expect_success 'packed-refs: writes are synced' '
593 test_when_finished "rm -rf repo" &&
594 git init repo &&
595 test_commit -C repo initial &&
596 test_line_count = 2 table-files &&
597
598 : >trace2.txt &&
599 GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
600 GIT_TEST_FSYNC=true \
601 git -C repo -c core.fsync=reference \
602 -c core.fsyncMethod=fsync pack-refs &&
603 check_fsync_events trace2.txt <<-EOF
604 "name":"hardware-flush","count":2
605 EOF
606'
607
608test_expect_success 'ref iterator: bogus names are flagged' '
609 test_when_finished "rm -rf repo" &&
610 git init repo &&
611 (
612 cd repo &&
613 test_commit --no-tag file &&
614 test-tool ref-store main update-ref msg "refs/heads/bogus..name" $(git rev-parse HEAD) $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
615
616 cat >expect <<-EOF &&
617 $ZERO_OID refs/heads/bogus..name 0xc
618 $(git rev-parse HEAD) refs/heads/main 0x0
619 EOF
620 test-tool ref-store main for-each-ref "" >actual &&
621 test_cmp expect actual
622 )
623'
624
625test_expect_success 'ref iterator: missing object IDs are not flagged' '
626 test_when_finished "rm -rf repo" &&
627 git init repo &&
628 (
629 cd repo &&
630 test-tool ref-store main update-ref msg "refs/heads/broken-hash" $INVALID_OID $ZERO_OID REF_SKIP_OID_VERIFICATION &&
631
632 cat >expect <<-EOF &&
633 $INVALID_OID refs/heads/broken-hash 0x0
634 EOF
635 test-tool ref-store main for-each-ref "" >actual &&
636 test_cmp expect actual
637 )
638'
639
640test_expect_success 'basic: commit and list refs' '
641 test_when_finished "rm -rf repo" &&
642 git init repo &&
643 test_commit -C repo file &&
644 test_write_lines refs/heads/main refs/tags/file >expect &&
645 git -C repo for-each-ref --format="%(refname)" >actual &&
646 test_cmp actual expect
647'
648
649test_expect_success 'basic: can write large commit message' '
650 test_when_finished "rm -rf repo" &&
651 git init repo &&
652
653 awk "BEGIN { for (i = 0; i < 50000; i++) printf \"%s\", \"this is a long commit message\" }" >commit-msg &&
654 git -C repo commit --allow-empty --file=../commit-msg
655'
656
657test_expect_success 'basic: show-ref fails with empty repository' '
658 test_when_finished "rm -rf repo" &&
659 git init repo &&
660 test_must_fail git -C repo show-ref >actual &&
661 test_must_be_empty actual
662'
663
664test_expect_success 'basic: can check out unborn branch' '
665 test_when_finished "rm -rf repo" &&
666 git init repo &&
667 git -C repo checkout -b main
668'
669
670test_expect_success 'basic: peeled tags are stored' '
671 test_when_finished "rm -rf repo" &&
672 git init repo &&
673 test_commit -C repo file &&
674 git -C repo tag -m "annotated tag" test_tag HEAD &&
675 for ref in refs/heads/main refs/tags/file refs/tags/test_tag refs/tags/test_tag^{}
676 do
677 echo "$(git -C repo rev-parse "$ref") $ref" || return 1
678 done >expect &&
679 git -C repo show-ref -d >actual &&
680 test_cmp expect actual
681'
682
683test_expect_success 'basic: for-each-ref can print symrefs' '
684 test_when_finished "rm -rf repo" &&
685 git init repo &&
686 (
687 cd repo &&
688 test_commit file &&
689 git branch &&
690 git symbolic-ref refs/heads/sym refs/heads/main &&
691 cat >expected <<-EOF &&
692 refs/heads/main
693 EOF
694 git for-each-ref --format="%(symref)" refs/heads/sym >actual &&
695 test_cmp expected actual
696 )
697'
698
699test_expect_success 'basic: notes' '
700 test_when_finished "rm -rf repo" &&
701 git init repo &&
702 (
703 write_script fake_editor <<-\EOF &&
704 echo "$MSG" >"$1"
705 echo "$MSG" >&2
706 EOF
707
708 test_commit 1st &&
709 test_commit 2nd &&
710 GIT_EDITOR=./fake_editor MSG=b4 git notes add &&
711 GIT_EDITOR=./fake_editor MSG=b3 git notes edit &&
712 echo b4 >expect &&
713 git notes --ref commits@{1} show >actual &&
714 test_cmp expect actual
715 )
716'
717
718test_expect_success 'basic: stash' '
719 test_when_finished "rm -rf repo" &&
720 git init repo &&
721 (
722 cd repo &&
723 test_commit file &&
724 git stash list >expect &&
725 test_line_count = 0 expect &&
726
727 echo hoi >>file.t &&
728 git stash push -m stashed &&
729 git stash list >expect &&
730 test_line_count = 1 expect &&
731
732 git stash clear &&
733 git stash list >expect &&
734 test_line_count = 0 expect
735 )
736'
737
738test_expect_success 'basic: cherry-pick' '
739 test_when_finished "rm -rf repo" &&
740 git init repo &&
741 (
742 cd repo &&
743 test_commit message1 file1 &&
744 test_commit message2 file2 &&
745 git branch source &&
746 git checkout HEAD^ &&
747 test_commit message3 file3 &&
748 git cherry-pick source &&
749 test_path_is_file file2
750 )
751'
752
753test_expect_success 'basic: rebase' '
754 test_when_finished "rm -rf repo" &&
755 git init repo &&
756 (
757 cd repo &&
758 test_commit message1 file1 &&
759 test_commit message2 file2 &&
760 git branch source &&
761 git checkout HEAD^ &&
762 test_commit message3 file3 &&
763 git rebase source &&
764 test_path_is_file file2
765 )
766'
767
768test_expect_success 'reflog: can delete separate reflog entries' '
769 test_when_finished "rm -rf repo" &&
770 git init repo &&
771 (
772 cd repo &&
773
774 test_commit file &&
775 test_commit file2 &&
776 test_commit file3 &&
777 test_commit file4 &&
778 git reflog >actual &&
779 grep file3 actual &&
780
781 git reflog delete HEAD@{1} &&
782 git reflog >actual &&
783 ! grep file3 actual
784 )
785'
786
787test_expect_success 'reflog: can switch to previous branch' '
788 test_when_finished "rm -rf repo" &&
789 git init repo &&
790 (
791 cd repo &&
792 test_commit file1 &&
793 git checkout -b branch1 &&
794 test_commit file2 &&
795 git checkout -b branch2 &&
796 git switch - &&
797 git rev-parse --symbolic-full-name HEAD >actual &&
798 echo refs/heads/branch1 >expect &&
799 test_cmp actual expect
800 )
801'
802
803test_expect_success 'reflog: copying branch writes reflog entry' '
804 test_when_finished "rm -rf repo" &&
805 git init repo &&
806 (
807 cd repo &&
808 test_commit file1 &&
809 test_commit file2 &&
810 oid=$(git rev-parse --short HEAD) &&
811 git branch src &&
812 cat >expect <<-EOF &&
813 ${oid} dst@{0}: Branch: copied refs/heads/src to refs/heads/dst
814 ${oid} dst@{1}: branch: Created from main
815 EOF
816 git branch -c src dst &&
817 git reflog dst >actual &&
818 test_cmp expect actual
819 )
820'
821
822test_expect_success 'reflog: renaming branch writes reflog entry' '
823 test_when_finished "rm -rf repo" &&
824 git init repo &&
825 (
826 cd repo &&
827 git symbolic-ref HEAD refs/heads/before &&
828 test_commit file &&
829 git show-ref >expected.refs &&
830 sed s/before/after/g <expected.refs >expected &&
831 git branch -M after &&
832 git show-ref >actual &&
833 test_cmp expected actual &&
834 echo refs/heads/after >expected &&
835 git symbolic-ref HEAD >actual &&
836 test_cmp expected actual
837 )
838'
839
840test_expect_success 'reflog: can store empty logs' '
841 test_when_finished "rm -rf repo" &&
842 git init repo &&
843 (
844 cd repo &&
845
846 test_must_fail test-tool ref-store main reflog-exists refs/heads/branch &&
847 test-tool ref-store main create-reflog refs/heads/branch &&
848 test-tool ref-store main reflog-exists refs/heads/branch &&
849 test-tool ref-store main for-each-reflog-ent-reverse refs/heads/branch >actual &&
850 test_must_be_empty actual
851 )
852'
853
854test_expect_success 'reflog: expiry empties reflog' '
855 test_when_finished "rm -rf repo" &&
856 git init repo &&
857 (
858 cd repo &&
859
860 test_commit initial &&
861 git checkout -b branch &&
862 test_commit fileA &&
863 test_commit fileB &&
864
865 cat >expect <<-EOF &&
866 commit: fileB
867 commit: fileA
868 branch: Created from HEAD
869 EOF
870 git reflog show --format="%gs" refs/heads/branch >actual &&
871 test_cmp expect actual &&
872
873 git reflog expire branch --expire=all &&
874 git reflog show --format="%gs" refs/heads/branch >actual &&
875 test_must_be_empty actual &&
876 test-tool ref-store main reflog-exists refs/heads/branch
877 )
878'
879
880test_expect_success 'reflog: can be deleted' '
881 test_when_finished "rm -rf repo" &&
882 git init repo &&
883 (
884 cd repo &&
885 test_commit initial &&
886 test-tool ref-store main reflog-exists refs/heads/main &&
887 test-tool ref-store main delete-reflog refs/heads/main &&
888 test_must_fail test-tool ref-store main reflog-exists refs/heads/main
889 )
890'
891
892test_expect_success 'reflog: garbage collection deletes reflog entries' '
893 test_when_finished "rm -rf repo" &&
894 git init repo &&
895 (
896 cd repo &&
897
898 for count in $(test_seq 1 10)
899 do
900 test_commit "number $count" file.t $count number-$count ||
901 return 1
902 done &&
903 git reflog refs/heads/main >actual &&
904 test_line_count = 10 actual &&
905 grep "commit (initial): number 1" actual &&
906 grep "commit: number 10" actual &&
907
908 git gc &&
909 git reflog refs/heads/main >actual &&
910 test_line_count = 0 actual
911 )
912'
913
914test_expect_success 'reflog: updates via HEAD update HEAD reflog' '
915 test_when_finished "rm -rf repo" &&
916 git init repo &&
917 (
918 cd repo &&
919 test_commit main-one &&
920 git checkout -b new-branch &&
921 test_commit new-one &&
922 test_commit new-two &&
923
924 echo new-one >expect &&
925 git log -1 --format=%s HEAD@{1} >actual &&
926 test_cmp expect actual
927 )
928'
929
930test_expect_success 'branch: copying branch with D/F conflict' '
931 test_when_finished "rm -rf repo" &&
932 git init repo &&
933 (
934 cd repo &&
935 test_commit A &&
936 git branch branch &&
937 cat >expect <<-EOF &&
938 error: ${SQ}refs/heads/branch${SQ} exists; cannot create ${SQ}refs/heads/branch/moved${SQ}
939 fatal: branch copy failed
940 EOF
941 test_must_fail git branch -c branch branch/moved 2>err &&
942 test_cmp expect err
943 )
944'
945
946test_expect_success 'branch: moving branch with D/F conflict' '
947 test_when_finished "rm -rf repo" &&
948 git init repo &&
949 (
950 cd repo &&
951 test_commit A &&
952 git branch branch &&
953 git branch conflict &&
954 cat >expect <<-EOF &&
955 error: ${SQ}refs/heads/conflict${SQ} exists; cannot create ${SQ}refs/heads/conflict/moved${SQ}
956 fatal: branch rename failed
957 EOF
958 test_must_fail git branch -m branch conflict/moved 2>err &&
959 test_cmp expect err
960 )
961'
962
963test_expect_success 'worktree: adding worktree creates separate stack' '
964 test_when_finished "rm -rf repo worktree" &&
965 git init repo &&
966 test_commit -C repo A &&
967
968 git -C repo worktree add ../worktree &&
969 test_path_is_file repo/.git/worktrees/worktree/refs/heads &&
970 echo "ref: refs/heads/.invalid" >expect &&
971 test_cmp expect repo/.git/worktrees/worktree/HEAD &&
972 test_path_is_dir repo/.git/worktrees/worktree/reftable &&
973 test_path_is_file repo/.git/worktrees/worktree/reftable/tables.list
974'
975
976test_expect_success 'worktree: pack-refs in main repo packs main refs' '
977 test_when_finished "rm -rf repo worktree" &&
978 git init repo &&
979 test_commit -C repo A &&
980
981 GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
982 git -C repo worktree add ../worktree &&
983 GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
984 git -C worktree update-ref refs/worktree/per-worktree HEAD &&
985
986 test_line_count = 4 repo/.git/worktrees/worktree/reftable/tables.list &&
987 test_line_count = 3 repo/.git/reftable/tables.list &&
988 git -C repo pack-refs &&
989 test_line_count = 4 repo/.git/worktrees/worktree/reftable/tables.list &&
990 test_line_count = 1 repo/.git/reftable/tables.list
991'
992
993test_expect_success 'worktree: pack-refs in worktree packs worktree refs' '
994 test_when_finished "rm -rf repo worktree" &&
995 git init repo &&
996 test_commit -C repo A &&
997
998 GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
999 git -C repo worktree add ../worktree &&
1000 GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
1001 git -C worktree update-ref refs/worktree/per-worktree HEAD &&
1002
1003 test_line_count = 4 repo/.git/worktrees/worktree/reftable/tables.list &&
1004 test_line_count = 3 repo/.git/reftable/tables.list &&
1005 git -C worktree pack-refs &&
1006 test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
1007 test_line_count = 3 repo/.git/reftable/tables.list
1008'
1009
1010test_expect_success 'worktree: creating shared ref updates main stack' '
1011 test_when_finished "rm -rf repo worktree" &&
1012 git init repo &&
1013 test_commit -C repo A &&
1014
1015 git -C repo worktree add ../worktree &&
1016 git -C repo pack-refs &&
1017 git -C worktree pack-refs &&
1018 test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
1019 test_line_count = 1 repo/.git/reftable/tables.list &&
1020
1021 GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
1022 git -C worktree update-ref refs/heads/shared HEAD &&
1023 test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
1024 test_line_count = 2 repo/.git/reftable/tables.list
1025'
1026
1027test_expect_success 'worktree: creating per-worktree ref updates worktree stack' '
1028 test_when_finished "rm -rf repo worktree" &&
1029 git init repo &&
1030 test_commit -C repo A &&
1031
1032 git -C repo worktree add ../worktree &&
1033 git -C repo pack-refs &&
1034 git -C worktree pack-refs &&
1035 test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
1036 test_line_count = 1 repo/.git/reftable/tables.list &&
1037
1038 git -C worktree update-ref refs/bisect/per-worktree HEAD &&
1039 test_line_count = 2 repo/.git/worktrees/worktree/reftable/tables.list &&
1040 test_line_count = 1 repo/.git/reftable/tables.list
1041'
1042
1043test_expect_success 'worktree: creating per-worktree ref from main repo' '
1044 test_when_finished "rm -rf repo worktree" &&
1045 git init repo &&
1046 test_commit -C repo A &&
1047
1048 git -C repo worktree add ../worktree &&
1049 git -C repo pack-refs &&
1050 git -C worktree pack-refs &&
1051 test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
1052 test_line_count = 1 repo/.git/reftable/tables.list &&
1053
1054 git -C repo update-ref worktrees/worktree/refs/bisect/per-worktree HEAD &&
1055 test_line_count = 2 repo/.git/worktrees/worktree/reftable/tables.list &&
1056 test_line_count = 1 repo/.git/reftable/tables.list
1057'
1058
1059test_expect_success 'worktree: creating per-worktree ref from second worktree' '
1060 test_when_finished "rm -rf repo wt1 wt2" &&
1061 git init repo &&
1062 test_commit -C repo A &&
1063
1064 git -C repo worktree add ../wt1 &&
1065 git -C repo worktree add ../wt2 &&
1066 git -C repo pack-refs &&
1067 git -C wt1 pack-refs &&
1068 git -C wt2 pack-refs &&
1069 test_line_count = 1 repo/.git/worktrees/wt1/reftable/tables.list &&
1070 test_line_count = 1 repo/.git/worktrees/wt2/reftable/tables.list &&
1071 test_line_count = 1 repo/.git/reftable/tables.list &&
1072
1073 git -C wt1 update-ref worktrees/wt2/refs/bisect/per-worktree HEAD &&
1074 test_line_count = 1 repo/.git/worktrees/wt1/reftable/tables.list &&
1075 test_line_count = 2 repo/.git/worktrees/wt2/reftable/tables.list &&
1076 test_line_count = 1 repo/.git/reftable/tables.list
1077'
1078
1079test_expect_success 'worktree: can create shared and per-worktree ref in one transaction' '
1080 test_when_finished "rm -rf repo worktree" &&
1081 git init repo &&
1082 test_commit -C repo A &&
1083
1084 git -C repo worktree add ../worktree &&
1085 git -C repo pack-refs &&
1086 git -C worktree pack-refs &&
1087 test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
1088 test_line_count = 1 repo/.git/reftable/tables.list &&
1089
1090 cat >stdin <<-EOF &&
1091 create worktrees/worktree/refs/bisect/per-worktree HEAD
1092 create refs/branches/shared HEAD
1093 EOF
1094 git -C repo update-ref --stdin <stdin &&
1095 test_line_count = 2 repo/.git/worktrees/worktree/reftable/tables.list &&
1096 test_line_count = 2 repo/.git/reftable/tables.list
1097'
1098
1099test_expect_success 'worktree: can access common refs' '
1100 test_when_finished "rm -rf repo worktree" &&
1101 git init repo &&
1102 test_commit -C repo file1 &&
1103 git -C repo branch branch1 &&
1104 git -C repo worktree add ../worktree &&
1105
1106 echo refs/heads/worktree >expect &&
1107 git -C worktree symbolic-ref HEAD >actual &&
1108 test_cmp expect actual &&
1109 git -C worktree checkout branch1
1110'
1111
1112test_expect_success 'worktree: adds worktree with detached HEAD' '
1113 test_when_finished "rm -rf repo worktree" &&
1114
1115 git init repo &&
1116 test_commit -C repo A &&
1117 git -C repo rev-parse main >expect &&
1118
1119 git -C repo worktree add --detach ../worktree main &&
1120 git -C worktree rev-parse HEAD >actual &&
1121 test_cmp expect actual
1122'
1123
1124test_expect_success 'fetch: accessing FETCH_HEAD special ref works' '
1125 test_when_finished "rm -rf repo sub" &&
1126
1127 git init sub &&
1128 test_commit -C sub two &&
1129 git -C sub rev-parse HEAD >expect &&
1130
1131 git init repo &&
1132 test_commit -C repo one &&
1133 git -C repo fetch ../sub &&
1134 git -C repo rev-parse FETCH_HEAD >actual &&
1135 test_cmp expect actual
1136'
1137
1138test_done