summaryrefslogtreecommitdiff
path: root/t
diff options
context:
space:
mode:
Diffstat (limited to 't')
-rw-r--r--t/.gitignore1
-rw-r--r--t/README155
-rw-r--r--t/annotate-tests.sh8
-rwxr-xr-xt/check-non-portable-shell.pl14
-rw-r--r--t/gitweb-lib.sh7
-rw-r--r--t/helper/.gitignore9
-rw-r--r--t/helper/test-advise.c22
-rw-r--r--t/helper/test-bloom.c93
-rw-r--r--t/helper/test-config.c18
-rw-r--r--t/helper/test-date.c54
-rw-r--r--t/helper/test-dir-iterator.c65
-rw-r--r--t/helper/test-dump-fsmonitor.c6
-rw-r--r--t/helper/test-dump-split-index.c2
-rw-r--r--t/helper/test-dump-untracked-cache.c1
-rw-r--r--t/helper/test-example-decorate.c6
-rw-r--r--t/helper/test-genzeros.c21
-rw-r--r--t/helper/test-hash-speed.c61
-rw-r--r--t/helper/test-hash.c58
-rw-r--r--t/helper/test-hashmap.c59
-rw-r--r--t/helper/test-lazy-init-name-hash.c12
-rw-r--r--t/helper/test-match-trees.c2
-rw-r--r--t/helper/test-oid-array.c (renamed from t/helper/test-sha1-array.c)8
-rw-r--r--t/helper/test-oidmap.c112
-rw-r--r--t/helper/test-parse-options.c10
-rw-r--r--t/helper/test-parse-pathspec-file.c33
-rw-r--r--t/helper/test-path-utils.c69
-rw-r--r--t/helper/test-pkt-line.c2
-rw-r--r--t/helper/test-prio-queue.c2
-rw-r--r--t/helper/test-progress.c74
-rw-r--r--t/helper/test-read-cache.c21
-rw-r--r--t/helper/test-read-graph.c46
-rw-r--r--t/helper/test-ref-store.c2
-rw-r--r--t/helper/test-repository.c12
-rw-r--r--t/helper/test-run-command.c197
-rw-r--r--t/helper/test-serve-v2.c31
-rw-r--r--t/helper/test-sha1.c52
-rw-r--r--t/helper/test-sha256.c7
-rw-r--r--t/helper/test-sigchain.c3
-rw-r--r--t/helper/test-submodule-nested-repo-config.c8
-rw-r--r--t/helper/test-tool.c38
-rw-r--r--t/helper/test-tool.h18
-rw-r--r--t/helper/test-trace2.c273
-rw-r--r--t/helper/test-windows-named-pipe.c2
-rw-r--r--t/helper/test-xml-encode.c80
-rwxr-xr-xt/interop/i5500-git-daemon.sh1
-rw-r--r--t/lib-bash.sh6
-rw-r--r--[-rwxr-xr-x]t/lib-credential.sh3
-rw-r--r--t/lib-git-daemon.sh43
-rw-r--r--t/lib-git-p4.sh76
-rw-r--r--t/lib-git-svn.sh18
-rw-r--r--[-rwxr-xr-x]t/lib-gpg.sh111
-rw-r--r--t/lib-httpd.sh27
-rw-r--r--t/lib-httpd/apache.conf13
-rw-r--r--t/lib-httpd/apply-one-time-perl.sh27
-rw-r--r--t/lib-httpd/apply-one-time-sed.sh22
-rw-r--r--t/lib-httpd/error-smart-http.sh3
-rw-r--r--t/lib-log-graph.sh28
-rw-r--r--t/lib-pack.sh35
-rw-r--r--t/lib-patch-mode.sh12
-rw-r--r--t/lib-rebase.sh37
-rw-r--r--[-rwxr-xr-x]t/lib-submodule-update.sh76
-rw-r--r--t/oid-info/hash-info9
-rw-r--r--t/perf/README2
-rwxr-xr-xt/perf/aggregate.perl42
-rwxr-xr-xt/perf/bisect_regression2
-rwxr-xr-xt/perf/p0001-rev-list.sh18
-rwxr-xr-xt/perf/p5302-pack-index.sh31
-rwxr-xr-xt/perf/p5303-many-packs.sh19
-rwxr-xr-xt/perf/p5304-prune.sh35
-rwxr-xr-xt/perf/p5310-pack-bitmaps.sh29
-rwxr-xr-xt/perf/p5311-pack-bitmaps-fetch.sh1
-rwxr-xr-xt/perf/p5600-partial-clone.sh26
-rwxr-xr-xt/perf/p5601-clone-reference.sh27
-rwxr-xr-xt/perf/p9300-fast-import-export.sh23
-rw-r--r--t/perf/perf-lib.sh42
-rwxr-xr-xt/perf/run43
-rwxr-xr-xt/t0000-basic.sh177
-rwxr-xr-xt/t0001-init.sh49
-rwxr-xr-xt/t0003-attributes.sh67
-rwxr-xr-xt/t0006-date.sh28
-rwxr-xr-xt/t0007-git-var.sh2
-rwxr-xr-xt/t0008-ignores.sh39
-rwxr-xr-xt/t0011-hashmap.sh67
-rwxr-xr-xt/t0014-alias.sh7
-rwxr-xr-xt/t0015-hash.sh55
-rwxr-xr-xt/t0016-oidmap.sh110
-rwxr-xr-xt/t0017-env-helper.sh99
-rwxr-xr-xt/t0018-advice.sh32
-rwxr-xr-xt/t0020-crlf.sh18
-rwxr-xr-xt/t0021-conversion.sh208
-rw-r--r--t/t0021/rot13-filter.pl6
-rwxr-xr-xt/t0025-crlf-renormalize.sh9
-rwxr-xr-xt/t0027-auto-crlf.sh12
-rwxr-xr-xt/t0028-working-tree-encoding.sh93
-rwxr-xr-xt/t0030-stripspace.sh12
-rwxr-xr-xt/t0040-parse-options.sh64
-rwxr-xr-xt/t0050-filesystem.sh20
-rwxr-xr-xt/t0060-path-utils.sh19
-rwxr-xr-xt/t0061-run-command.sh46
-rwxr-xr-xt/t0064-sha1-array.sh16
-rwxr-xr-xt/t0066-dir-iterator.sh148
-rwxr-xr-xt/t0067-parse_pathspec_file.sh108
-rwxr-xr-xt/t0090-cache-tree.sh9
-rwxr-xr-xt/t0091-bugreport.sh61
-rwxr-xr-xt/t0095-bloom.sh117
-rwxr-xr-xt/t0205-gettext-poison.sh7
-rwxr-xr-xt/t0210-trace2-normal.sh189
-rw-r--r--t/t0210/scrub_normal.perl48
-rwxr-xr-xt/t0211-trace2-perf.sh174
-rw-r--r--t/t0211/scrub_perf.perl76
-rwxr-xr-xt/t0212-trace2-event.sh324
-rw-r--r--t/t0212/parse_events.perl251
-rwxr-xr-xt/t0300-credentials.sh247
-rwxr-xr-xt/t0301-credential-cache.sh7
-rwxr-xr-xt/t0302-credential-store.sh91
-rwxr-xr-xt/t0410-partial-clone.sh110
-rwxr-xr-xt/t0500-progress-display.sh286
-rwxr-xr-xt/t1007-hash-object.sh62
-rwxr-xr-xt/t1011-read-tree-sparse-checkout.sh14
-rwxr-xr-xt/t1050-large.sh18
-rwxr-xr-xt/t1060-object-corruption.sh10
-rwxr-xr-xt/t1090-sparse-checkout-scope.sh14
-rwxr-xr-xt/t1091-sparse-checkout-builtin.sh609
-rwxr-xr-xt/t1300-config.sh256
-rwxr-xr-xt/t1301-shared-repo.sh6
-rwxr-xr-xt/t1305-config-include.sh75
-rwxr-xr-xt/t1306-xdg-files.sh8
-rwxr-xr-xt/t1307-config-blob.sh2
-rwxr-xr-xt/t1308-config-set.sh26
-rwxr-xr-xt/t1309-early-config.sh12
-rwxr-xr-xt/t1400-update-ref.sh202
-rwxr-xr-xt/t1404-update-ref-errors.sh80
-rwxr-xr-xt/t1406-submodule-ref-store.sh2
-rwxr-xr-xt/t1409-avoid-packing-refs.sh16
-rwxr-xr-xt/t1410-reflog.sh51
-rwxr-xr-xt/t1414-reflog-walk.sh3
-rwxr-xr-xt/t1415-worktree-refs.sh35
-rwxr-xr-xt/t1450-fsck.sh165
-rwxr-xr-xt/t1500-rev-parse.sh25
-rwxr-xr-xt/t1501-work-tree.sh2
-rwxr-xr-xt/t1506-rev-parse-diagnosis.sh79
-rwxr-xr-xt/t1507-rev-parse-upstream.sh116
-rwxr-xr-xt/t1512-rev-parse-disambiguation.sh14
-rwxr-xr-xt/t1600-index.sh35
-rwxr-xr-xt/t1700-split-index.sh69
-rwxr-xr-xt/t2014-checkout-switch.sh (renamed from t/t2014-switch.sh)0
-rwxr-xr-xt/t2018-checkout-branch.sh162
-rwxr-xr-xt/t2020-checkout-detach.sh28
-rwxr-xr-xt/t2022-checkout-paths.sh11
-rwxr-xr-xt/t2023-checkout-m.sh24
-rwxr-xr-xt/t2024-checkout-dwim.sh59
-rwxr-xr-xt/t2025-checkout-no-overlay.sh47
-rwxr-xr-xt/t2026-checkout-pathspec-file.sh163
-rwxr-xr-xt/t2060-switch.sh96
-rwxr-xr-xt/t2070-restore.sh126
-rwxr-xr-xt/t2071-restore-patch.sh110
-rwxr-xr-xt/t2072-restore-pathspec-file.sh164
-rwxr-xr-xt/t2107-update-index-basic.sh1
-rwxr-xr-xt/t2203-add-intent.sh6
-rwxr-xr-xt/t2400-worktree-add.sh (renamed from t/t2025-worktree-add.sh)54
-rwxr-xr-xt/t2401-worktree-prune.sh (renamed from t/t2026-worktree-prune.sh)0
-rwxr-xr-xt/t2402-worktree-list.sh (renamed from t/t2027-worktree-list.sh)6
-rwxr-xr-xt/t2403-worktree-move.sh (renamed from t/t2028-worktree-move.sh)37
-rwxr-xr-xt/t2404-worktree-config.sh (renamed from t/t2029-worktree-config.sh)0
-rwxr-xr-xt/t2405-worktree-submodule.sh90
-rwxr-xr-xt/t3000-ls-files-others.sh123
-rwxr-xr-xt/t3005-ls-files-relative.sh12
-rwxr-xr-xt/t3007-ls-files-recurse-submodules.sh1
-rwxr-xr-xt/t3008-ls-files-lazy-init-name-hash.sh2
-rwxr-xr-xt/t3009-ls-files-others-nonsubmodule.sh50
-rwxr-xr-xt/t3011-common-prefixes-and-directory-traversal.sh209
-rwxr-xr-xt/t3030-merge-recursive.sh39
-rwxr-xr-xt/t3033-merge-toplevel.sh22
-rwxr-xr-xt/t3035-merge-sparse.sh4
-rwxr-xr-xt/t3060-ls-files-with-tree.sh4
-rwxr-xr-xt/t3200-branch.sh54
-rwxr-xr-xt/t3201-branch-contains.sh8
-rwxr-xr-xt/t3203-branch-output.sh84
-rwxr-xr-xt/t3206-range-diff.sh647
-rw-r--r--t/t3206/history.export115
-rwxr-xr-xt/t3210-pack-refs.sh2
-rwxr-xr-xt/t3301-notes.sh403
-rwxr-xr-xt/t3305-notes-fanout.sh117
-rwxr-xr-xt/t3306-notes-prune.sh45
-rwxr-xr-xt/t3308-notes-merge.sh83
-rwxr-xr-xt/t3309-notes-merge-auto-resolve.sh228
-rwxr-xr-xt/t3310-notes-merge-manual-resolve.sh106
-rwxr-xr-xt/t3311-notes-merge-fanout.sh70
-rwxr-xr-xt/t3400-rebase.sh124
-rwxr-xr-xt/t3401-rebase-and-am-rename.sh10
-rwxr-xr-xt/t3402-rebase-merge.sh77
-rwxr-xr-xt/t3403-rebase-skip.sh111
-rwxr-xr-xt/t3404-rebase-interactive.sh986
-rwxr-xr-xt/t3406-rebase-message.sh32
-rwxr-xr-xt/t3407-rebase-abort.sh6
-rwxr-xr-xt/t3415-rebase-autosquash.sh155
-rwxr-xr-xt/t3416-rebase-onto-threedots.sh57
-rwxr-xr-xt/t3417-rebase-whitespace-fix.sh4
-rwxr-xr-xt/t3418-rebase-continue.sh33
-rwxr-xr-xt/t3419-rebase-patch-id.sh113
-rwxr-xr-xt/t3420-rebase-autostash.sh135
-rwxr-xr-xt/t3421-rebase-topology-linear.sh95
-rwxr-xr-xt/t3422-rebase-incompatible-options.sh15
-rwxr-xr-xt/t3424-rebase-empty.sh170
-rwxr-xr-xt/t3425-rebase-topology-merges.sh17
-rwxr-xr-xt/t3427-rebase-subtree.sh155
-rwxr-xr-xt/t3429-rebase-edit-todo.sh73
-rwxr-xr-xt/t3430-rebase-merges.sh118
-rwxr-xr-xt/t3431-rebase-fork-point.sh77
-rwxr-xr-xt/t3432-rebase-fast-forward.sh137
-rwxr-xr-xt/t3433-rebase-across-mode-change.sh48
-rwxr-xr-xt/t3434-rebase-i18n.sh84
-rw-r--r--t/t3434/ISO8859-1.txt3
-rw-r--r--t/t3434/eucJP.txt4
-rwxr-xr-xt/t3435-rebase-gpg-sign.sh71
-rwxr-xr-xt/t3501-revert-cherry-pick.sh4
-rwxr-xr-xt/t3502-cherry-pick-merge.sh12
-rwxr-xr-xt/t3504-cherry-pick-rerere.sh6
-rwxr-xr-xt/t3506-cherry-pick-ff.sh14
-rwxr-xr-xt/t3507-cherry-pick-conflict.sh214
-rwxr-xr-xt/t3508-cherry-pick-many-commits.sh2
-rwxr-xr-xt/t3510-cherry-pick-sequence.sh131
-rwxr-xr-xt/t3511-cherry-pick-x.sh20
-rwxr-xr-xt/t3514-cherry-pick-revert-gpg.sh86
-rwxr-xr-xt/t3600-rm.sh385
-rwxr-xr-xt/t3601-rm-pathspec-file.sh79
-rwxr-xr-xt/t3700-add.sh25
-rwxr-xr-xt/t3701-add-interactive.sh189
-rwxr-xr-xt/t3704-add-pathspec-file.sh159
-rwxr-xr-xt/t3800-mktag.sh49
-rwxr-xr-xt/t3900-i18n-commit.sh37
-rwxr-xr-xt/t3903-stash.sh360
-rwxr-xr-xt/t3904-stash-patch.sh6
-rwxr-xr-xt/t3905-stash-include-untracked.sh10
-rwxr-xr-xt/t3906-stash-submodule.sh42
-rwxr-xr-xt/t3907-stash-show-config.sh83
-rwxr-xr-xt/t3908-stash-in-worktree.sh27
-rwxr-xr-xt/t3909-stash-pathspec-file.sh100
-rwxr-xr-xt/t4000-diff-format.sh2
-rwxr-xr-xt/t4002-diff-basic.sh367
-rwxr-xr-xt/t4006-diff-mode.sh55
-rwxr-xr-xt/t4009-diff-rename-4.sh19
-rwxr-xr-xt/t4010-diff-pathspec.sh20
-rwxr-xr-xt/t4011-diff-symlink.sh40
-rwxr-xr-xt/t4013-diff-various.sh70
-rw-r--r--t/t4013/diff.diff-tree_--cc_--shortstat_master4
-rw-r--r--t/t4013/diff.diff-tree_--cc_--summary_REVERSE6
-rw-r--r--t/t4013/diff.diff-tree_--format=%N_note6
-rw-r--r--t/t4013/diff.diff-tree_--pretty_--notes_note12
-rw-r--r--t/t4013/diff.diff-tree_--pretty_note9
-rw-r--r--t/t4013/diff.diff_--dirstat_--cc_master~1_master3
-rw-r--r--t/t4013/diff.diff_-U1_initial..side29
-rw-r--r--t/t4013/diff.diff_-U2_initial..side31
-rw-r--r--t/t4013/diff.diff_-U_initial..side32
-rw-r--r--t/t4013/diff.log_--decorate=full_--all15
-rw-r--r--t/t4013/diff.log_--decorate_--all15
-rwxr-xr-xt/t4014-format-patch.sh1210
-rwxr-xr-xt/t4015-diff-whitespace.sh342
-rwxr-xr-xt/t4018-diff-funcname.sh5
-rw-r--r--t/t4018/dts-labels9
-rw-r--r--t/t4018/dts-node-unitless8
-rw-r--r--t/t4018/dts-nodes8
-rw-r--r--t/t4018/dts-nodes-boolean-prop9
-rw-r--r--t/t4018/dts-nodes-comment18
-rw-r--r--t/t4018/dts-nodes-comment28
-rw-r--r--t/t4018/dts-nodes-multiline-prop13
-rw-r--r--t/t4018/dts-reference9
-rw-r--r--t/t4018/dts-root5
-rw-r--r--t/t4018/dts-root-comment8
-rw-r--r--t/t4018/elixir-do-not-pick-end5
-rw-r--r--t/t4018/elixir-ex-unit-test6
-rw-r--r--t/t4018/elixir-function5
-rw-r--r--t/t4018/elixir-macro5
-rw-r--r--t/t4018/elixir-module9
-rw-r--r--t/t4018/elixir-module-func8
-rw-r--r--t/t4018/elixir-nested-module9
-rw-r--r--t/t4018/elixir-private-function5
-rw-r--r--t/t4018/elixir-protocol6
-rw-r--r--t/t4018/elixir-protocol-implementation5
-rw-r--r--t/t4018/markdown-heading-indented6
-rw-r--r--t/t4018/markdown-heading-non-headings17
-rw-r--r--t/t4018/matlab-class-definition5
-rw-r--r--t/t4018/matlab-function4
-rw-r--r--t/t4018/matlab-octave-section-13
-rw-r--r--t/t4018/matlab-octave-section-23
-rw-r--r--t/t4018/matlab-section3
-rw-r--r--t/t4018/python-async-def4
-rw-r--r--t/t4018/python-class4
-rw-r--r--t/t4018/python-def4
-rw-r--r--t/t4018/python-indented-async-def7
-rw-r--r--t/t4018/python-indented-class5
-rw-r--r--t/t4018/python-indented-def7
-rw-r--r--t/t4018/rust-fn5
-rw-r--r--t/t4018/rust-impl5
-rw-r--r--t/t4018/rust-struct5
-rw-r--r--t/t4018/rust-trait5
-rwxr-xr-xt/t4026-color.sh12
-rwxr-xr-xt/t4027-diff-submodule.sh16
-rwxr-xr-xt/t4034-diff-words.sh94
-rw-r--r--t/t4034/dts/expect37
-rw-r--r--t/t4034/dts/post32
-rw-r--r--t/t4034/dts/pre32
-rwxr-xr-xt/t4038-diff-combined.sh97
-rwxr-xr-xt/t4039-diff-assume-unchanged.sh3
-rwxr-xr-xt/t4041-diff-submodule-option.sh20
-rwxr-xr-xt/t4044-diff-index-unique-abbrev.sh46
-rwxr-xr-xt/t4045-diff-relative.sh2
-rwxr-xr-xt/t4048-diff-combined-binary.sh58
-rwxr-xr-xt/t4053-diff-no-index.sh11
-rwxr-xr-xt/t4054-diff-bogus-tree.sh3
-rwxr-xr-xt/t4057-diff-combined-paths.sh4
-rwxr-xr-xt/t4060-diff-submodule-option-diff-format.sh126
-rwxr-xr-xt/t4061-diff-indent.sh2
-rwxr-xr-xt/t4066-diff-emit-delay.sh79
-rwxr-xr-xt/t4067-diff-partial-clone.sh182
-rw-r--r--t/t4100/t-apply-1.patch4
-rw-r--r--t/t4100/t-apply-3.patch2
-rw-r--r--t/t4100/t-apply-5.patch4
-rw-r--r--t/t4100/t-apply-7.patch2
-rwxr-xr-xt/t4108-apply-threeway.sh55
-rwxr-xr-xt/t4117-apply-reject.sh6
-rwxr-xr-xt/t4124-apply-ws-rule.sh17
-rwxr-xr-xt/t4134-apply-submodule.sh5
-rwxr-xr-xt/t4138-apply-ws-expansion.sh16
-rwxr-xr-xt/t4150-am.sh78
-rwxr-xr-xt/t4200-rerere.sh3
-rwxr-xr-xt/t4202-log.sh402
-rwxr-xr-xt/t4203-mailmap.sh147
-rwxr-xr-xt/t4204-patch-id.sh2
-rwxr-xr-xt/t4205-log-pretty-formats.sh246
-rwxr-xr-xt/t4208-log-magic-pathspec.sh4
-rwxr-xr-xt/t4209-log-pickaxe.sh35
-rwxr-xr-xt/t4210-log-i18n.sh41
-rwxr-xr-xt/t4211-line-log.sh104
-rw-r--r--t/t4211/sha1/expect.beginning-of-file (renamed from t/t4211/expect.beginning-of-file)0
-rw-r--r--t/t4211/sha1/expect.end-of-file (renamed from t/t4211/expect.end-of-file)0
-rw-r--r--t/t4211/sha1/expect.move-support-f (renamed from t/t4211/expect.move-support-f)0
-rw-r--r--t/t4211/sha1/expect.multiple (renamed from t/t4211/expect.multiple)0
-rw-r--r--t/t4211/sha1/expect.multiple-overlapping (renamed from t/t4211/expect.multiple-overlapping)0
-rw-r--r--t/t4211/sha1/expect.multiple-superset (renamed from t/t4211/expect.multiple-superset)0
-rw-r--r--t/t4211/sha1/expect.parallel-change-f-to-main (renamed from t/t4211/expect.parallel-change-f-to-main)0
-rw-r--r--t/t4211/sha1/expect.simple-f (renamed from t/t4211/expect.simple-f)0
-rw-r--r--t/t4211/sha1/expect.simple-f-to-main (renamed from t/t4211/expect.simple-f-to-main)0
-rw-r--r--t/t4211/sha1/expect.simple-main (renamed from t/t4211/expect.simple-main)0
-rw-r--r--t/t4211/sha1/expect.simple-main-to-end (renamed from t/t4211/expect.simple-main-to-end)0
-rw-r--r--t/t4211/sha1/expect.two-ranges (renamed from t/t4211/expect.two-ranges)0
-rw-r--r--t/t4211/sha1/expect.vanishes-early (renamed from t/t4211/expect.vanishes-early)0
-rw-r--r--t/t4211/sha256/expect.beginning-of-file43
-rw-r--r--t/t4211/sha256/expect.end-of-file62
-rw-r--r--t/t4211/sha256/expect.move-support-f80
-rw-r--r--t/t4211/sha256/expect.multiple104
-rw-r--r--t/t4211/sha256/expect.multiple-overlapping187
-rw-r--r--t/t4211/sha256/expect.multiple-superset187
-rw-r--r--t/t4211/sha256/expect.parallel-change-f-to-main160
-rw-r--r--t/t4211/sha256/expect.simple-f59
-rw-r--r--t/t4211/sha256/expect.simple-f-to-main100
-rw-r--r--t/t4211/sha256/expect.simple-main68
-rw-r--r--t/t4211/sha256/expect.simple-main-to-end70
-rw-r--r--t/t4211/sha256/expect.two-ranges102
-rw-r--r--t/t4211/sha256/expect.vanishes-early39
-rwxr-xr-xt/t4213-log-tabexpand.sh2
-rwxr-xr-xt/t4214-log-graph-octopus.sh339
-rwxr-xr-xt/t4215-log-skewed-merges.sh373
-rwxr-xr-xt/t4216-log-bloom.sh155
-rwxr-xr-xt/t4253-am-keep-cr-dos.sh6
-rwxr-xr-xt/t4254-am-corrupt.sh53
-rwxr-xr-xt/t4256-am-format-flowed.sh2
-rwxr-xr-xt/t4257-am-interactive.sh52
-rwxr-xr-xt/t4300-merge-tree.sh188
-rwxr-xr-xt/t5000-tar-tree.sh16
-rw-r--r--t/t5000/huge-object (renamed from t/t5000/19f9c8273ec45a8938e6999cb59b3ff66739902a)bin2048 -> 2048 bytes
-rwxr-xr-xt/t5003-archive-zip.sh24
-rwxr-xr-xt/t5004-archive-corner-cases.sh36
-rwxr-xr-xt/t5100-mailinfo.sh15
-rwxr-xr-xt/t5150-request-pull.sh61
-rwxr-xr-xt/t5200-update-server-info.sh41
-rwxr-xr-xt/t5302-pack-index.sh21
-rwxr-xr-xt/t5304-prune.sh36
-rwxr-xr-xt/t5307-pack-missing-commit.sh4
-rwxr-xr-xt/t5309-pack-delta-cycles.sh18
-rwxr-xr-xt/t5310-pack-bitmaps.sh56
-rwxr-xr-xt/t5313-pack-bounds-checks.sh19
-rwxr-xr-xt/t5314-pack-cycle-detection.sh2
-rwxr-xr-xt/t5315-pack-objects-compression.sh2
-rwxr-xr-xt/t5317-pack-objects-filter-objects.sh143
-rwxr-xr-xt/t5318-commit-graph.sh190
-rwxr-xr-xt/t5319-multi-pack-index.sh321
-rw-r--r--t/t5319/no-objects.midxbin0 -> 1116 bytes
-rwxr-xr-xt/t5321-pack-large-objects.sh4
-rwxr-xr-xt/t5322-pack-objects-sparse.sh138
-rwxr-xr-xt/t5323-pack-redundant.sh467
-rwxr-xr-xt/t5324-split-commit-graph.sh428
-rwxr-xr-xt/t5400-send-pack.sh2
-rwxr-xr-xt/t5403-post-checkout-hook.sh96
-rwxr-xr-xt/t5407-post-rewrite-hook.sh46
-rwxr-xr-xt/t5409-colorize-remote-messages.sh5
-rwxr-xr-xt/t5500-fetch-pack.sh148
-rwxr-xr-xt/t5503-tagfollow.sh2
-rwxr-xr-xt/t5504-fetch-receive-strict.sh32
-rwxr-xr-xt/t5505-remote.sh143
-rwxr-xr-xt/t5509-fetch-push-namespaces.sh41
-rwxr-xr-xt/t5510-fetch.sh89
-rwxr-xr-xt/t5512-ls-remote.sh126
-rwxr-xr-xt/t5514-fetch-multiple.sh18
-rwxr-xr-xt/t5515-fetch-merge-logic.sh65
-rwxr-xr-xt/t5516-fetch-push.sh50
-rwxr-xr-xt/t5517-push-mirror.sh10
-rwxr-xr-xt/t5520-pull.sh423
-rwxr-xr-xt/t5521-pull-options.sh28
-rwxr-xr-xt/t5526-fetch-submodules.sh119
-rwxr-xr-xt/t5528-push-default.sh2
-rwxr-xr-xt/t5530-upload-pack-error.sh40
-rwxr-xr-xt/t5531-deep-submodule-push.sh2
-rwxr-xr-xt/t5535-fetch-push-symref.sh2
-rwxr-xr-xt/t5537-fetch-shallow.sh121
-rwxr-xr-xt/t5539-fetch-http-shallow.sh6
-rwxr-xr-xt/t5540-http-push-webdav.sh8
-rwxr-xr-xt/t5541-http-push-smart.sh108
-rwxr-xr-xt/t5542-push-http-shallow.sh1
-rwxr-xr-xt/t5543-atomic-push.sh89
-rwxr-xr-xt/t5545-push-options.sh5
-rwxr-xr-xt/t5548-push-porcelain.sh279
-rwxr-xr-xt/t5550-http-fetch-dumb.sh21
-rwxr-xr-xt/t5551-http-fetch-smart.sh72
-rwxr-xr-xt/t5552-skipping-fetch-negotiator.sh44
-rwxr-xr-xt/t5553-set-upstream.sh178
-rwxr-xr-xt/t5561-http-backend.sh1
-rwxr-xr-xt/t5562-http-backend-content-length.sh29
-rwxr-xr-xt/t5570-git-daemon.sh15
-rwxr-xr-xt/t5573-pull-verify-signatures.sh64
-rwxr-xr-xt/t5580-unc-paths.sh (renamed from t/t5580-clone-push-unc.sh)31
-rwxr-xr-xt/t5581-http-curl-verbose.sh26
-rwxr-xr-xt/t5601-clone.sh49
-rwxr-xr-xt/t5604-clone-reference.sh136
-rwxr-xr-xt/t5607-clone-bundle.sh19
-rwxr-xr-xt/t5608-clone-2gb.sh2
-rwxr-xr-xt/t5611-clone-config.sh58
-rwxr-xr-xt/t5612-clone-refspec.sh26
-rwxr-xr-xt/t5616-partial-clone.sh323
-rwxr-xr-xt/t5617-clone-submodules-remote.sh65
-rwxr-xr-xt/t5618-alternate-refs.sh60
-rwxr-xr-xt/t5700-protocol-v1.sh7
-rwxr-xr-xt/t5701-git-serve.sh32
-rwxr-xr-xt/t5702-protocol-v2.sh211
-rwxr-xr-xt/t5703-upload-pack-ref-in-want.sh275
-rwxr-xr-xt/t5704-protocol-violations.sh33
-rwxr-xr-xt/t5801-remote-helpers.sh31
-rwxr-xr-xt/t5801/git-remote-testgit151
-rwxr-xr-xt/t5812-proto-disable-http.sh1
-rwxr-xr-xt/t6000-rev-list-misc.sh53
-rwxr-xr-xt/t6006-rev-list-format.sh41
-rwxr-xr-xt/t6011-rev-list-with-bad-commit.sh2
-rwxr-xr-xt/t6012-rev-list-simplify.sh120
-rwxr-xr-xt/t6016-rev-list-graph-simplify-history.sh30
-rwxr-xr-xt/t6019-rev-list-ancestry-path.sh4
-rwxr-xr-xt/t6020-merge-df.sh55
-rwxr-xr-xt/t6021-merge-criss-cross.sh137
-rwxr-xr-xt/t6022-merge-rename.sh393
-rwxr-xr-xt/t6023-merge-file.sh568
-rwxr-xr-xt/t6024-recursive-merge.sh135
-rwxr-xr-xt/t6025-merge-symlinks.sh95
-rwxr-xr-xt/t6026-merge-attr.sh46
-rwxr-xr-xt/t6030-bisect-porcelain.sh49
-rwxr-xr-xt/t6034-merge-rename-nocruft.sh122
-rwxr-xr-xt/t6035-merge-dir-to-symlink.sh28
-rwxr-xr-xt/t6036-recursive-corner-cases.sh459
-rwxr-xr-xt/t6040-tracking-info.sh37
-rwxr-xr-xt/t6042-merge-rename-corner-cases.sh540
-rwxr-xr-xt/t6043-merge-rename-directories.sh1211
-rwxr-xr-xt/t6046-merge-skip-unneeded-updates.sh225
-rwxr-xr-xt/t6047-diff3-conflict-markers.sh211
-rwxr-xr-xt/t6050-replace.sh35
-rwxr-xr-xt/t6102-rev-list-unexpected-objects.sh127
-rwxr-xr-xt/t6112-rev-list-filters-objects.sh358
-rwxr-xr-xt/t6113-rev-list-bitmap-filters.sh56
-rwxr-xr-xt/t6120-describe.sh197
-rwxr-xr-xt/t6135-pathspec-with-attrs.sh58
-rwxr-xr-xt/t6136-pathspec-in-bare.sh38
-rwxr-xr-xt/t6200-fmt-merge-msg.sh30
-rwxr-xr-xt/t6300-for-each-ref.sh77
-rwxr-xr-xt/t6302-for-each-ref-filter.sh13
-rwxr-xr-xt/t6500-gc.sh37
-rwxr-xr-xt/t6501-freshen-objects.sh6
-rwxr-xr-xt/t6600-test-reach.sh2
-rwxr-xr-xt/t7004-tag.sh42
-rwxr-xr-xt/t7008-filter-branch-null-sha1.sh (renamed from t/t7009-filter-branch-null-sha1.sh)0
-rwxr-xr-xt/t7012-skip-worktree-writing.sh15
-rwxr-xr-xt/t7030-verify-tag.sh38
-rwxr-xr-xt/t7060-wtstatus.sh5
-rwxr-xr-xt/t7061-wtstatus-ignore.sh9
-rwxr-xr-xt/t7063-status-untracked-cache.sh54
-rwxr-xr-xt/t7064-wtstatus-pv2.sh8
-rwxr-xr-xt/t7105-reset-patch.sh21
-rwxr-xr-xt/t7107-reset-pathspec-file.sh173
-rwxr-xr-xt/t7112-reset-submodule.sh1
-rwxr-xr-xt/t7113-post-index-change-hook.sh144
-rwxr-xr-xt/t7201-co.sh11
-rwxr-xr-xt/t7300-clean.sh85
-rwxr-xr-xt/t7400-submodule-basic.sh84
-rwxr-xr-xt/t7405-submodule-merge.sh2
-rwxr-xr-xt/t7406-submodule-update.sh8
-rwxr-xr-xt/t7407-submodule-foreach.sh17
-rwxr-xr-xt/t7408-submodule-reference.sh8
-rwxr-xr-xt/t7410-submodule-checkout-to.sh77
-rwxr-xr-xt/t7411-submodule-config.sh17
-rwxr-xr-xt/t7412-submodule-absorbgitdirs.sh7
-rwxr-xr-xt/t7415-submodule-names.sh15
-rwxr-xr-xt/t7419-submodule-set-branch.sh93
-rwxr-xr-xt/t7420-submodule-set-url.sh55
-rwxr-xr-xt/t7500-commit-template-squash-signoff.sh9
-rwxr-xr-xt/t7501-commit-basic-functionality.sh83
-rwxr-xr-xt/t7502-commit-porcelain.sh95
-rwxr-xr-xt/t7503-pre-commit-and-pre-merge-commit-hooks.sh281
-rwxr-xr-xt/t7503-pre-commit-hook.sh139
-rwxr-xr-xt/t7505-prepare-commit-msg-hook.sh16
-rw-r--r--t/t7505/expected-rebase-i3
-rwxr-xr-xt/t7508-status.sh148
-rwxr-xr-xt/t7510-signed-commit.sh76
-rwxr-xr-xt/t7512-status-help.sh104
-rwxr-xr-xt/t7513-interpret-trailers.sh73
-rwxr-xr-xt/t7517-per-repo-email.sh74
-rwxr-xr-xt/t7519-status-fsmonitor.sh45
-rwxr-xr-xt/t7519/fsmonitor-all1
-rwxr-xr-xt/t7519/fsmonitor-all-v221
-rwxr-xr-xt/t7519/fsmonitor-env24
-rwxr-xr-xt/t7519/fsmonitor-watchman16
-rwxr-xr-xt/t7519/fsmonitor-watchman-v2173
-rwxr-xr-xt/t7525-status-rename.sh8
-rwxr-xr-xt/t7526-commit-pathspec-file.sh164
-rwxr-xr-xt/t7600-merge.sh279
-rwxr-xr-xt/t7601-merge-pull-config.sh38
-rwxr-xr-xt/t7604-merge-custom-message.sh75
-rwxr-xr-xt/t7609-merge-co-error-msgs.sh2
-rwxr-xr-xt/t7610-mergetool.sh326
-rwxr-xr-xt/t7612-merge-verify-signatures.sh22
-rwxr-xr-xt/t7700-repack.sh212
-rwxr-xr-xt/t7800-difftool.sh43
-rwxr-xr-xt/t7810-grep.sh69
-rwxr-xr-xt/t7811-grep-open.sh1
-rwxr-xr-xt/t7812-grep-icase-non-ascii.sh31
-rwxr-xr-xt/t7814-grep-recurse-submodules.sh56
-rwxr-xr-xt/t7815-grep-binary.sh (renamed from t/t7008-grep-binary.sh)101
-rwxr-xr-xt/t7816-grep-binary-pattern.sh127
-rwxr-xr-xt/t8003-blame-corner-cases.sh37
-rwxr-xr-xt/t8013-blame-ignore-revs.sh274
-rwxr-xr-xt/t8014-blame-ignore-fuzzy.sh437
-rwxr-xr-xt/t9001-send-email.sh122
-rwxr-xr-xt/t9010-svn-fe.sh6
-rwxr-xr-xt/t9106-git-svn-commit-diff-clobber.sh3
-rwxr-xr-xt/t9115-git-svn-dcommit-funky-renames.sh2
-rwxr-xr-xt/t9116-git-svn-log.sh12
-rwxr-xr-xt/t9118-git-svn-funky-branch-names.sh2
-rwxr-xr-xt/t9120-git-svn-clone-with-percent-escapes.sh2
-rwxr-xr-xt/t9141-git-svn-multiple-branches.sh8
-rwxr-xr-xt/t9142-git-svn-shallow-clone.sh2
-rwxr-xr-xt/t9160-git-svn-preserve-empty-dirs.sh4
-rwxr-xr-xt/t9164-git-svn-dcommit-concurrent.sh4
-rwxr-xr-xt/t9300-fast-import.sh240
-rwxr-xr-xt/t9301-fast-import-notes.sh2
-rwxr-xr-xt/t9303-fast-import-compression.sh2
-rwxr-xr-xt/t9350-fast-export.sh226
-rw-r--r--t/t9350/broken-iso-8859-7-commit-message.txt1
-rw-r--r--t/t9350/simple-iso-8859-7-commit-message.txt1
-rwxr-xr-xt/t9500-gitweb-standalone-no-errors.sh70
-rwxr-xr-xt/t9502-gitweb-standalone-parse-output.sh7
-rwxr-xr-xt/t9800-git-p4-basic.sh5
-rwxr-xr-xt/t9801-git-p4-branch.sh136
-rwxr-xr-xt/t9802-git-p4-filetype.sh4
-rwxr-xr-xt/t9803-git-p4-shell-metachars.sh4
-rwxr-xr-xt/t9804-git-p4-label.sh4
-rwxr-xr-xt/t9805-git-p4-skip-submit-edit.sh4
-rwxr-xr-xt/t9806-git-p4-options.sh5
-rwxr-xr-xt/t9807-git-p4-submit.sh57
-rwxr-xr-xt/t9808-git-p4-chdir.sh4
-rwxr-xr-xt/t9809-git-p4-client-view.sh6
-rwxr-xr-xt/t9810-git-p4-rcs.sh5
-rwxr-xr-xt/t9811-git-p4-label-import.sh7
-rwxr-xr-xt/t9812-git-p4-wildcards.sh4
-rwxr-xr-xt/t9813-git-p4-preserve-users.sh4
-rwxr-xr-xt/t9814-git-p4-rename.sh4
-rwxr-xr-xt/t9815-git-p4-submit-fail.sh4
-rwxr-xr-xt/t9816-git-p4-locked.sh4
-rwxr-xr-xt/t9817-git-p4-exclude.sh51
-rwxr-xr-xt/t9818-git-p4-block.sh4
-rwxr-xr-xt/t9819-git-p4-case-folding.sh6
-rwxr-xr-xt/t9820-git-p4-editor-handling.sh4
-rwxr-xr-xt/t9821-git-p4-path-variations.sh4
-rwxr-xr-xt/t9822-git-p4-path-encoding.sh11
-rwxr-xr-xt/t9823-git-p4-mock-lfs.sh4
-rwxr-xr-xt/t9824-git-p4-git-lfs.sh4
-rwxr-xr-xt/t9825-git-p4-handle-utf16-without-bom.sh4
-rwxr-xr-xt/t9826-git-p4-keep-empty-commits.sh4
-rwxr-xr-xt/t9827-git-p4-change-filetype.sh4
-rwxr-xr-xt/t9828-git-p4-map-user.sh4
-rwxr-xr-xt/t9829-git-p4-jobs.sh4
-rwxr-xr-xt/t9830-git-p4-symlink-dir.sh4
-rwxr-xr-xt/t9831-git-p4-triggers.sh6
-rwxr-xr-xt/t9832-unshelve.sh11
-rwxr-xr-xt/t9833-errors.sh32
-rwxr-xr-xt/t9902-completion.sh172
-rwxr-xr-xt/t9903-bash-prompt.sh28
-rw-r--r--t/test-lib-functions.sh416
-rw-r--r--t/test-lib.sh729
603 files changed, 34841 insertions, 7746 deletions
diff --git a/t/.gitignore b/t/.gitignore
index 348715f..91cf577 100644
--- a/t/.gitignore
+++ b/t/.gitignore
@@ -2,3 +2,4 @@
/test-results
/.prove
/chainlinttmp
+/out/
diff --git a/t/README b/t/README
index 28711cc..cf86383 100644
--- a/t/README
+++ b/t/README
@@ -69,7 +69,8 @@ You can also run each test individually from command line, like this:
You can pass --verbose (or -v), --debug (or -d), and --immediate
(or -i) command line argument to the test, or by setting GIT_TEST_OPTS
-appropriately before running "make".
+appropriately before running "make". Short options can be bundled, i.e.
+'-d -v' is the same as '-dv'.
-v::
--verbose::
@@ -170,6 +171,15 @@ appropriately before running "make".
implied by other options like --valgrind and
GIT_TEST_INSTALLED.
+--no-bin-wrappers::
+ By default, the test suite uses the wrappers in
+ `../bin-wrappers/` to execute `git` and friends. With this option,
+ `../git` and friends are run directly. This is not recommended
+ in general, as the wrappers contain safeguards to ensure that no
+ files from an installed Git are used, but can speed up test runs
+ especially on platforms where running shell scripts is expensive
+ (most notably, Windows).
+
--root=<directory>::
Create "trash" directories used to store all temporary data during
testing under <directory>, instead of the t/ directory.
@@ -186,6 +196,29 @@ appropriately before running "make".
this feature by setting the GIT_TEST_CHAIN_LINT environment
variable to "1" or "0", respectively.
+--stress::
+ Run the test script repeatedly in multiple parallel jobs until
+ one of them fails. Useful for reproducing rare failures in
+ flaky tests. The number of parallel jobs is, in order of
+ precedence: the value of the GIT_TEST_STRESS_LOAD
+ environment variable, or twice the number of available
+ processors (as shown by the 'getconf' utility), or 8.
+ Implies `--verbose -x --immediate` to get the most information
+ about the failure. Note that the verbose output of each test
+ job is saved to 't/test-results/$TEST_NAME.stress-<nr>.out',
+ and only the output of the failed test job is shown on the
+ terminal. The names of the trash directories get a
+ '.stress-<nr>' suffix, and the trash directory of the failed
+ test job is renamed to end with a '.stress-failed' suffix.
+
+--stress-jobs=<N>::
+ Override the number of parallel jobs. Implies `--stress`.
+
+--stress-limit=<N>::
+ When combined with --stress run the test script repeatedly
+ this many times in each of the parallel jobs or until one of
+ them fails, whichever comes first. Implies `--stress`.
+
You can also set the GIT_TEST_INSTALLED environment variable to
the bindir of an existing git installation to test that installation.
You still need to have built this git sandbox, from which various
@@ -302,15 +335,27 @@ that cannot be easily covered by a few specific test cases. These
could be enabled by running the test suite with correct GIT_TEST_
environment set.
-GIT_TEST_GETTEXT_POISON=<non-empty?> turns all strings marked for
-translation into gibberish if non-empty (think "test -n"). Used for
-spotting those tests that need to be marked with a C_LOCALE_OUTPUT
-prerequisite when adding more strings for translation. See "Testing
-marked strings" in po/README for details.
+GIT_TEST_FAIL_PREREQS=<boolean> fails all prerequisites. This is
+useful for discovering issues with the tests where say a later test
+implicitly depends on an optional earlier test.
+
+There's a "FAIL_PREREQS" prerequisite that can be used to test for
+whether this mode is active, and e.g. skip some tests that are hard to
+refactor to deal with it. The "SYMLINKS" prerequisite is currently
+excluded as so much relies on it, but this might change in the future.
+
+GIT_TEST_GETTEXT_POISON=<boolean> turns all strings marked for
+translation into gibberish if true. Used for spotting those tests that
+need to be marked with a C_LOCALE_OUTPUT prerequisite when adding more
+strings for translation. See "Testing marked strings" in po/README for
+details.
GIT_TEST_SPLIT_INDEX=<boolean> forces split-index mode on the whole
test suite. Accept any boolean values that are accepted by git-config.
+GIT_TEST_PROTOCOL_VERSION=<n>, when set, makes 'protocol.version'
+default to n.
+
GIT_TEST_FULL_IN_PACK_ARRAY=<boolean> exercises the uncommon
pack-objects code path where there are more than 1024 packs even if
the actual number of packs in repository is below this limit. Accept
@@ -334,6 +379,11 @@ GIT_TEST_COMMIT_GRAPH=<boolean>, when true, forces the commit-graph to
be written after every 'git commit' command, and overrides the
'core.commitGraph' setting to true.
+GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=<boolean>, when true, forces
+commit-graph write to compute and write changed path Bloom filters for
+every 'git commit-graph write', as if the `--changed-paths` option was
+passed in.
+
GIT_TEST_FSMONITOR=$PWD/t7519/fsmonitor-all exercises the fsmonitor
code path for utilizing a file system monitor to speed up detecting
new or changed files.
@@ -342,11 +392,15 @@ GIT_TEST_INDEX_VERSION=<n> exercises the index read/write code path
for the index version specified. Can be set to any valid version
(currently 2, 3, or 4).
+GIT_TEST_PACK_SPARSE=<boolean> if disabled will default the pack-objects
+builtin to use the non-sparse object walk. This can still be overridden by
+the --sparse command-line argument.
+
GIT_TEST_PRELOAD_INDEX=<boolean> exercises the preload-index code path
by overriding the minimum number of cache entries required per thread.
-GIT_TEST_REBASE_USE_BUILTIN=<boolean>, when false, disables the
-builtin version of git-rebase. See 'rebase.useBuiltin' in
+GIT_TEST_ADD_I_USE_BUILTIN=<boolean>, when true, enables the
+built-in version of git add -i. See 'add.interactive.useBuiltin' in
git-config(1).
GIT_TEST_INDEX_THREADS=<n> enables exercising the multi-threaded loading
@@ -358,6 +412,15 @@ GIT_TEST_MULTI_PACK_INDEX=<boolean>, when true, forces the multi-pack-
index to be written after every 'git repack' command, and overrides the
'core.multiPackIndex' setting to true.
+GIT_TEST_SIDEBAND_ALL=<boolean>, when true, overrides the
+'uploadpack.allowSidebandAll' setting to true, and when false, forces
+fetch-pack to not request sideband-all (even if the server advertises
+sideband-all).
+
+GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=<boolean>, when true (which is
+the default when running tests), errors out when an abbreviated option
+is used.
+
Naming Tests
------------
@@ -425,7 +488,8 @@ This test harness library does the following things:
- Creates an empty test directory with an empty .git/objects database
and chdir(2) into it. This directory is 't/trash
directory.$test_name_without_dotsh', with t/ subject to change by
- the --root option documented above.
+ the --root option documented above, and a '.stress-<N>' suffix
+ appended by the --stress option.
- Defines standard test helper functions for your scripts to
use. These functions are designed to make all scripts behave
@@ -488,6 +552,41 @@ Here are the "do's:"
reports "ok" or "not ok" to the end user running the tests. Under
--verbose, they are shown to help debug the tests.
+ - Be careful when you loop
+
+ You may need to verify multiple things in a loop, but the
+ following does not work correctly:
+
+ test_expect_success 'test three things' '
+ for i in one two three
+ do
+ test_something "$i"
+ done &&
+ test_something_else
+ '
+
+ Because the status of the loop itself is the exit status of the
+ test_something in the last round, the loop does not fail when
+ "test_something" for "one" or "two" fails. This is not what you
+ want.
+
+ Instead, you can break out of the loop immediately when you see a
+ failure. Because all test_expect_* snippets are executed inside
+ a function, "return 1" can be used to fail the test immediately
+ upon a failure:
+
+ test_expect_success 'test three things' '
+ for i in one two three
+ do
+ test_something "$i" || return 1
+ done &&
+ test_something_else
+ '
+
+ Note that we still &&-chain the loop to propagate failures from
+ earlier commands.
+
+
And here are the "don'ts:"
- Don't exit() within a <script> part.
@@ -822,6 +921,26 @@ library for your script to use.
...
'
+ - test_atexit <script>
+
+ Prepend <script> to a list of commands to run unconditionally to
+ clean up before the test script exits, e.g. to stop a daemon:
+
+ test_expect_success 'test git daemon' '
+ git daemon &
+ daemon_pid=$! &&
+ test_atexit 'kill $daemon_pid' &&
+ hello world
+ '
+
+ The commands will be executed before the trash directory is removed,
+ i.e. the atexit commands will still be able to access any pidfiles or
+ socket files.
+
+ Note that these commands will be run even when a test script run
+ with '--immediate' fails. Be careful with your atexit commands to
+ minimize any changes to the failed state.
+
- test_write_lines <lines>
Write <lines> on standard output, one line per argument.
@@ -892,6 +1011,24 @@ library for your script to use.
test_oid_init or test_oid_cache. Providing an unknown key is an
error.
+ - yes [<string>]
+
+ This is often seen in modern UNIX but some platforms lack it, so
+ the test harness overrides the platform implementation with a
+ more limited one. Use this only when feeding a handful lines of
+ output to the downstream---unlike the real version, it generates
+ only up to 99 lines.
+
+ - test_bool_env <env-variable-name> <default-value>
+
+ Given the name of an environment variable with a bool value,
+ normalize its value to a 0 (true) or 1 (false or empty string)
+ return code. Return with code corresponding to the given default
+ value if the variable is unset.
+ Abort the test script if either the value of the variable or the
+ default are not valid bool values.
+
+
Prerequisites
-------------
diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh
index 6da48a2..d933af5 100644
--- a/t/annotate-tests.sh
+++ b/t/annotate-tests.sh
@@ -68,6 +68,14 @@ test_expect_success 'blame 1 author' '
check_count A 2
'
+test_expect_success 'blame in a bare repo without starting commit' '
+ git clone --bare . bare.git &&
+ (
+ cd bare.git &&
+ check_count A 2
+ )
+'
+
test_expect_success 'blame by tag objects' '
git tag -m "test tag" testTag &&
git tag -m "test tag #2" testTag2 testTag &&
diff --git a/t/check-non-portable-shell.pl b/t/check-non-portable-shell.pl
index b45bdac..fd33035 100755
--- a/t/check-non-portable-shell.pl
+++ b/t/check-non-portable-shell.pl
@@ -27,15 +27,16 @@ for my $i (@ARGV) {
close $f;
}
+my $line = '';
while (<>) {
chomp;
+ $line .= $_;
# stitch together incomplete lines (those ending with "\")
- while (s/\\$//) {
- $_ .= readline;
- chomp;
- }
+ next if $line =~ s/\\$//;
- /\bsed\s+-i/ and err 'sed -i is not portable';
+ $_ = $line;
+ /\bcp\s+-a/ and err 'cp -a is not portable';
+ /\bsed\s+-[^efn]\s+/ and err 'sed option not portable (use only -n, -e, -f)';
/\becho\s+-[neE]/ and err 'echo with option is not portable (use printf)';
/^\s*declare\s+/ and err 'arrays/declare not portable';
/^\s*[^#]\s*which\s/ and err 'which is not portable (use type)';
@@ -45,8 +46,9 @@ while (<>) {
/(?:\$\(seq|^\s*seq\b)/ and err 'seq is not portable (use test_seq)';
/\bgrep\b.*--file\b/ and err 'grep --file FILE is not portable (use grep -f FILE)';
/\bexport\s+[A-Za-z0-9_]*=/ and err '"export FOO=bar" is not portable (use FOO=bar && export FOO)';
- /^\s*([A-Z0-9_]+=(\w+|(["']).*?\3)\s+)+(\w+)/ and exists($func{$4}) and
+ /^\s*([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and exists($func{$4}) and
err '"FOO=bar shell_func" assignment extends beyond "shell_func"';
+ $line = '';
# this resets our $. for each file
close ARGV if eof;
}
diff --git a/t/gitweb-lib.sh b/t/gitweb-lib.sh
index 006d2a8..1f32ca6 100644
--- a/t/gitweb-lib.sh
+++ b/t/gitweb-lib.sh
@@ -58,10 +58,11 @@ gitweb_run () {
GATEWAY_INTERFACE='CGI/1.1'
HTTP_ACCEPT='*/*'
REQUEST_METHOD='GET'
- QUERY_STRING=""$1""
- PATH_INFO=""$2""
+ QUERY_STRING=$1
+ PATH_INFO=$2
+ REQUEST_URI=/gitweb.cgi$PATH_INFO
export GATEWAY_INTERFACE HTTP_ACCEPT REQUEST_METHOD \
- QUERY_STRING PATH_INFO
+ QUERY_STRING PATH_INFO REQUEST_URI
GITWEB_CONFIG=$(pwd)/gitweb_config.perl
export GITWEB_CONFIG
diff --git a/t/helper/.gitignore b/t/helper/.gitignore
index 2bad28a..48c7bb0 100644
--- a/t/helper/.gitignore
+++ b/t/helper/.gitignore
@@ -1,5 +1,4 @@
-*
-!*.sh
-!*.[ch]
-!*.gitignore
-
+/test-tool
+/test-fake-ssh
+/test-line-buffer
+/test-svn-fe
diff --git a/t/helper/test-advise.c b/t/helper/test-advise.c
new file mode 100644
index 0000000..38cdc28
--- /dev/null
+++ b/t/helper/test-advise.c
@@ -0,0 +1,22 @@
+#include "test-tool.h"
+#include "cache.h"
+#include "advice.h"
+#include "config.h"
+
+int cmd__advise_if_enabled(int argc, const char **argv)
+{
+ if (!argv[1])
+ die("usage: %s <advice>", argv[0]);
+
+ setup_git_directory();
+ git_config(git_default_config, NULL);
+
+ /*
+ * Any advice type can be used for testing, but NESTED_TAG was
+ * selected here and in t0018 where this command is being
+ * executed.
+ */
+ advise_if_enabled(ADVICE_NESTED_TAG, argv[1]);
+
+ return 0;
+}
diff --git a/t/helper/test-bloom.c b/t/helper/test-bloom.c
new file mode 100644
index 0000000..77eb27a
--- /dev/null
+++ b/t/helper/test-bloom.c
@@ -0,0 +1,93 @@
+#include "git-compat-util.h"
+#include "bloom.h"
+#include "test-tool.h"
+#include "commit.h"
+
+struct bloom_filter_settings settings = DEFAULT_BLOOM_FILTER_SETTINGS;
+
+static void add_string_to_filter(const char *data, struct bloom_filter *filter) {
+ struct bloom_key key;
+ int i;
+
+ fill_bloom_key(data, strlen(data), &key, &settings);
+ printf("Hashes:");
+ for (i = 0; i < settings.num_hashes; i++){
+ printf("0x%08x|", key.hashes[i]);
+ }
+ printf("\n");
+ add_key_to_filter(&key, filter, &settings);
+}
+
+static void print_bloom_filter(struct bloom_filter *filter) {
+ int i;
+
+ if (!filter) {
+ printf("No filter.\n");
+ return;
+ }
+ printf("Filter_Length:%d\n", (int)filter->len);
+ printf("Filter_Data:");
+ for (i = 0; i < filter->len; i++) {
+ printf("%02x|", filter->data[i]);
+ }
+ printf("\n");
+}
+
+static void get_bloom_filter_for_commit(const struct object_id *commit_oid)
+{
+ struct commit *c;
+ struct bloom_filter *filter;
+ setup_git_directory();
+ c = lookup_commit(the_repository, commit_oid);
+ filter = get_bloom_filter(the_repository, c, 1);
+ print_bloom_filter(filter);
+}
+
+static const char *bloom_usage = "\n"
+" test-tool bloom get_murmer3 <string>\n"
+" test-tool bloom generate_filter <string> [<string>...]\n"
+" test-tool get_filter_for_commit <commit-hex>\n";
+
+int cmd__bloom(int argc, const char **argv)
+{
+ if (argc < 2)
+ usage(bloom_usage);
+
+ if (!strcmp(argv[1], "get_murmur3")) {
+ uint32_t hashed;
+ if (argc < 3)
+ usage(bloom_usage);
+ hashed = murmur3_seeded(0, argv[2], strlen(argv[2]));
+ printf("Murmur3 Hash with seed=0:0x%08x\n", hashed);
+ }
+
+ if (!strcmp(argv[1], "generate_filter")) {
+ struct bloom_filter filter;
+ int i = 2;
+ filter.len = (settings.bits_per_entry + BITS_PER_WORD - 1) / BITS_PER_WORD;
+ filter.data = xcalloc(filter.len, sizeof(unsigned char));
+
+ if (argc - 1 < i)
+ usage(bloom_usage);
+
+ while (argv[i]) {
+ add_string_to_filter(argv[i], &filter);
+ i++;
+ }
+
+ print_bloom_filter(&filter);
+ }
+
+ if (!strcmp(argv[1], "get_filter_for_commit")) {
+ struct object_id oid;
+ const char *end;
+ if (argc < 3)
+ usage(bloom_usage);
+ if (parse_oid_hex(argv[2], &oid, &end))
+ die("cannot parse oid '%s'", argv[2]);
+ init_bloom_filters();
+ get_bloom_filter_for_commit(&oid);
+ }
+
+ return 0;
+}
diff --git a/t/helper/test-config.c b/t/helper/test-config.c
index 214003d..234c722 100644
--- a/t/helper/test-config.c
+++ b/t/helper/test-config.c
@@ -37,21 +37,6 @@
*
*/
-static const char *scope_name(enum config_scope scope)
-{
- switch (scope) {
- case CONFIG_SCOPE_SYSTEM:
- return "system";
- case CONFIG_SCOPE_GLOBAL:
- return "global";
- case CONFIG_SCOPE_REPO:
- return "repo";
- case CONFIG_SCOPE_CMDLINE:
- return "cmdline";
- default:
- return "unknown";
- }
-}
static int iterate_cb(const char *var, const char *value, void *data)
{
static int nr;
@@ -63,7 +48,8 @@ static int iterate_cb(const char *var, const char *value, void *data)
printf("value=%s\n", value ? value : "(null)");
printf("origin=%s\n", current_config_origin_type());
printf("name=%s\n", current_config_name());
- printf("scope=%s\n", scope_name(current_config_scope()));
+ printf("lno=%d\n", current_config_line());
+ printf("scope=%s\n", config_scope_name(current_config_scope()));
return 0;
}
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index a083737..099eff4 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -3,25 +3,35 @@
static const char *usage_msg = "\n"
" test-tool date relative [time_t]...\n"
+" test-tool date human [time_t]...\n"
" test-tool date show:<format> [time_t]...\n"
" test-tool date parse [date]...\n"
" test-tool date approxidate [date]...\n"
" test-tool date timestamp [date]...\n"
+" test-tool date getnanos [start-nanos]\n"
" test-tool date is64bit\n"
" test-tool date time_t-is64bit\n";
-static void show_relative_dates(const char **argv, struct timeval *now)
+static void show_relative_dates(const char **argv)
{
struct strbuf buf = STRBUF_INIT;
for (; *argv; argv++) {
time_t t = atoi(*argv);
- show_date_relative(t, 0, now, &buf);
+ show_date_relative(t, &buf);
printf("%s -> %s\n", *argv, buf.buf);
}
strbuf_release(&buf);
}
+static void show_human_dates(const char **argv)
+{
+ for (; *argv; argv++) {
+ time_t t = atoi(*argv);
+ printf("%s -> %s\n", *argv, show_date(t, 0, DATE_MODE(HUMAN)));
+ }
+}
+
static void show_dates(const char **argv, const char *format)
{
struct date_mode mode;
@@ -45,7 +55,7 @@ static void show_dates(const char **argv, const char *format)
}
}
-static void parse_dates(const char **argv, struct timeval *now)
+static void parse_dates(const char **argv)
{
struct strbuf result = STRBUF_INIT;
@@ -64,50 +74,54 @@ static void parse_dates(const char **argv, struct timeval *now)
strbuf_release(&result);
}
-static void parse_approxidate(const char **argv, struct timeval *now)
+static void parse_approxidate(const char **argv)
{
for (; *argv; argv++) {
timestamp_t t;
- t = approxidate_relative(*argv, now);
+ t = approxidate_relative(*argv);
printf("%s -> %s\n", *argv, show_date(t, 0, DATE_MODE(ISO8601)));
}
}
-static void parse_approx_timestamp(const char **argv, struct timeval *now)
+static void parse_approx_timestamp(const char **argv)
{
for (; *argv; argv++) {
timestamp_t t;
- t = approxidate_relative(*argv, now);
+ t = approxidate_relative(*argv);
printf("%s -> %"PRItime"\n", *argv, t);
}
}
+static void getnanos(const char **argv)
+{
+ double seconds = getnanotime() / 1.0e9;
+
+ if (*argv)
+ seconds -= strtod(*argv, NULL);
+ printf("%lf\n", seconds);
+}
+
int cmd__date(int argc, const char **argv)
{
- struct timeval now;
const char *x;
- x = getenv("TEST_DATE_NOW");
- if (x) {
- now.tv_sec = atoi(x);
- now.tv_usec = 0;
- }
- else
- gettimeofday(&now, NULL);
-
argv++;
if (!*argv)
usage(usage_msg);
if (!strcmp(*argv, "relative"))
- show_relative_dates(argv+1, &now);
+ show_relative_dates(argv+1);
+ else if (!strcmp(*argv, "human"))
+ show_human_dates(argv+1);
else if (skip_prefix(*argv, "show:", &x))
show_dates(argv+1, x);
else if (!strcmp(*argv, "parse"))
- parse_dates(argv+1, &now);
+ parse_dates(argv+1);
else if (!strcmp(*argv, "approxidate"))
- parse_approxidate(argv+1, &now);
+ parse_approxidate(argv+1);
else if (!strcmp(*argv, "timestamp"))
- parse_approx_timestamp(argv+1, &now);
+ parse_approx_timestamp(argv+1);
+ else if (!strcmp(*argv, "getnanos"))
+ getnanos(argv+1);
else if (!strcmp(*argv, "is64bit"))
return sizeof(timestamp_t) == 8 ? 0 : 1;
else if (!strcmp(*argv, "time_t-is64bit"))
diff --git a/t/helper/test-dir-iterator.c b/t/helper/test-dir-iterator.c
new file mode 100644
index 0000000..659b6bf
--- /dev/null
+++ b/t/helper/test-dir-iterator.c
@@ -0,0 +1,65 @@
+#include "test-tool.h"
+#include "git-compat-util.h"
+#include "strbuf.h"
+#include "iterator.h"
+#include "dir-iterator.h"
+
+static const char *error_name(int error_number)
+{
+ switch (error_number) {
+ case ENOENT: return "ENOENT";
+ case ENOTDIR: return "ENOTDIR";
+ default: return "ESOMETHINGELSE";
+ }
+}
+
+/*
+ * usage:
+ * tool-test dir-iterator [--follow-symlinks] [--pedantic] directory_path
+ */
+int cmd__dir_iterator(int argc, const char **argv)
+{
+ struct dir_iterator *diter;
+ unsigned int flags = 0;
+ int iter_status;
+
+ for (++argv, --argc; *argv && starts_with(*argv, "--"); ++argv, --argc) {
+ if (strcmp(*argv, "--follow-symlinks") == 0)
+ flags |= DIR_ITERATOR_FOLLOW_SYMLINKS;
+ else if (strcmp(*argv, "--pedantic") == 0)
+ flags |= DIR_ITERATOR_PEDANTIC;
+ else
+ die("invalid option '%s'", *argv);
+ }
+
+ if (!*argv || argc != 1)
+ die("dir-iterator needs exactly one non-option argument");
+
+ diter = dir_iterator_begin(*argv, flags);
+
+ if (!diter) {
+ printf("dir_iterator_begin failure: %s\n", error_name(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ while ((iter_status = dir_iterator_advance(diter)) == ITER_OK) {
+ if (S_ISDIR(diter->st.st_mode))
+ printf("[d] ");
+ else if (S_ISREG(diter->st.st_mode))
+ printf("[f] ");
+ else if (S_ISLNK(diter->st.st_mode))
+ printf("[s] ");
+ else
+ printf("[?] ");
+
+ printf("(%s) [%s] %s\n", diter->relative_path, diter->basename,
+ diter->path.buf);
+ }
+
+ if (iter_status != ITER_DONE) {
+ printf("dir_iterator_advance failure\n");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/t/helper/test-dump-fsmonitor.c b/t/helper/test-dump-fsmonitor.c
index 08e3684..975f0ac 100644
--- a/t/helper/test-dump-fsmonitor.c
+++ b/t/helper/test-dump-fsmonitor.c
@@ -3,17 +3,17 @@
int cmd__dump_fsmonitor(int ac, const char **av)
{
- struct index_state *istate = &the_index;
+ struct index_state *istate = the_repository->index;
int i;
setup_git_directory();
- if (do_read_index(istate, get_index_file(), 0) < 0)
+ if (do_read_index(istate, the_repository->index_file, 0) < 0)
die("unable to read index file");
if (!istate->fsmonitor_last_update) {
printf("no fsmonitor\n");
return 0;
}
- printf("fsmonitor last update %"PRIuMAX"\n", (uintmax_t)istate->fsmonitor_last_update);
+ printf("fsmonitor last update %s\n", istate->fsmonitor_last_update);
for (i = 0; i < istate->cache_nr; i++)
printf((istate->cache[i]->ce_flags & CE_FSMONITOR_VALID) ? "+" : "-");
diff --git a/t/helper/test-dump-split-index.c b/t/helper/test-dump-split-index.c
index 63c689d..a209880 100644
--- a/t/helper/test-dump-split-index.c
+++ b/t/helper/test-dump-split-index.c
@@ -13,6 +13,8 @@ int cmd__dump_split_index(int ac, const char **av)
struct split_index *si;
int i;
+ setup_git_directory();
+
do_read_index(&the_index, av[1], 1);
printf("own %s\n", oid_to_hex(&the_index.oid));
si = the_index.split_index;
diff --git a/t/helper/test-dump-untracked-cache.c b/t/helper/test-dump-untracked-cache.c
index 52870eb..cf0f2c7 100644
--- a/t/helper/test-dump-untracked-cache.c
+++ b/t/helper/test-dump-untracked-cache.c
@@ -1,3 +1,4 @@
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
#include "test-tool.h"
#include "cache.h"
#include "dir.h"
diff --git a/t/helper/test-example-decorate.c b/t/helper/test-example-decorate.c
index a20a616..c8a1cde 100644
--- a/t/helper/test-example-decorate.c
+++ b/t/helper/test-example-decorate.c
@@ -26,8 +26,8 @@ int cmd__example_decorate(int argc, const char **argv)
* Add 2 objects, one with a non-NULL decoration and one with a NULL
* decoration.
*/
- one = lookup_unknown_object(one_oid.hash);
- two = lookup_unknown_object(two_oid.hash);
+ one = lookup_unknown_object(&one_oid);
+ two = lookup_unknown_object(&two_oid);
ret = add_decoration(&n, one, &decoration_a);
if (ret)
BUG("when adding a brand-new object, NULL should be returned");
@@ -56,7 +56,7 @@ int cmd__example_decorate(int argc, const char **argv)
ret = lookup_decoration(&n, two);
if (ret != &decoration_b)
BUG("lookup should return added declaration");
- three = lookup_unknown_object(three_oid.hash);
+ three = lookup_unknown_object(&three_oid);
ret = lookup_decoration(&n, three);
if (ret)
BUG("lookup for unknown object should return NULL");
diff --git a/t/helper/test-genzeros.c b/t/helper/test-genzeros.c
new file mode 100644
index 0000000..9532f5b
--- /dev/null
+++ b/t/helper/test-genzeros.c
@@ -0,0 +1,21 @@
+#include "test-tool.h"
+#include "git-compat-util.h"
+
+int cmd__genzeros(int argc, const char **argv)
+{
+ long count;
+
+ if (argc > 2) {
+ fprintf(stderr, "usage: %s [<count>]\n", argv[0]);
+ return 1;
+ }
+
+ count = argc > 1 ? strtol(argv[1], NULL, 0) : -1L;
+
+ while (count < 0 || count--) {
+ if (putchar(0) == EOF)
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/t/helper/test-hash-speed.c b/t/helper/test-hash-speed.c
new file mode 100644
index 0000000..432233c
--- /dev/null
+++ b/t/helper/test-hash-speed.c
@@ -0,0 +1,61 @@
+#include "test-tool.h"
+#include "cache.h"
+
+#define NUM_SECONDS 3
+
+static inline void compute_hash(const struct git_hash_algo *algo, git_hash_ctx *ctx, uint8_t *final, const void *p, size_t len)
+{
+ algo->init_fn(ctx);
+ algo->update_fn(ctx, p, len);
+ algo->final_fn(final, ctx);
+}
+
+int cmd__hash_speed(int ac, const char **av)
+{
+ git_hash_ctx ctx;
+ unsigned char hash[GIT_MAX_RAWSZ];
+ clock_t initial, start, end;
+ unsigned bufsizes[] = { 64, 256, 1024, 8192, 16384 };
+ int i;
+ void *p;
+ const struct git_hash_algo *algo = NULL;
+
+ if (ac == 2) {
+ for (i = 1; i < GIT_HASH_NALGOS; i++) {
+ if (!strcmp(av[1], hash_algos[i].name)) {
+ algo = &hash_algos[i];
+ break;
+ }
+ }
+ }
+ if (!algo)
+ die("usage: test-tool hash-speed algo_name");
+
+ /* Use this as an offset to make overflow less likely. */
+ initial = clock();
+
+ printf("algo: %s\n", algo->name);
+
+ for (i = 0; i < ARRAY_SIZE(bufsizes); i++) {
+ unsigned long j, kb;
+ double kb_per_sec;
+ p = xcalloc(1, bufsizes[i]);
+ start = end = clock() - initial;
+ for (j = 0; ((end - start) / CLOCKS_PER_SEC) < NUM_SECONDS; j++) {
+ compute_hash(algo, &ctx, hash, p, bufsizes[i]);
+
+ /*
+ * Only check elapsed time every 128 iterations to avoid
+ * dominating the runtime with system calls.
+ */
+ if (!(j & 127))
+ end = clock() - initial;
+ }
+ kb = j * bufsizes[i];
+ kb_per_sec = kb / (1024 * ((double)end - start) / CLOCKS_PER_SEC);
+ printf("size %u: %lu iters; %lu KiB; %0.2f KiB/s\n", bufsizes[i], j, kb, kb_per_sec);
+ free(p);
+ }
+
+ exit(0);
+}
diff --git a/t/helper/test-hash.c b/t/helper/test-hash.c
new file mode 100644
index 0000000..0a31de6
--- /dev/null
+++ b/t/helper/test-hash.c
@@ -0,0 +1,58 @@
+#include "test-tool.h"
+#include "cache.h"
+
+int cmd_hash_impl(int ac, const char **av, int algo)
+{
+ git_hash_ctx ctx;
+ unsigned char hash[GIT_MAX_HEXSZ];
+ unsigned bufsz = 8192;
+ int binary = 0;
+ char *buffer;
+ const struct git_hash_algo *algop = &hash_algos[algo];
+
+ if (ac == 2) {
+ if (!strcmp(av[1], "-b"))
+ binary = 1;
+ else
+ bufsz = strtoul(av[1], NULL, 10) * 1024 * 1024;
+ }
+
+ if (!bufsz)
+ bufsz = 8192;
+
+ while ((buffer = malloc(bufsz)) == NULL) {
+ fprintf(stderr, "bufsz %u is too big, halving...\n", bufsz);
+ bufsz /= 2;
+ if (bufsz < 1024)
+ die("OOPS");
+ }
+
+ algop->init_fn(&ctx);
+
+ while (1) {
+ ssize_t sz, this_sz;
+ char *cp = buffer;
+ unsigned room = bufsz;
+ this_sz = 0;
+ while (room) {
+ sz = xread(0, cp, room);
+ if (sz == 0)
+ break;
+ if (sz < 0)
+ die_errno("test-hash");
+ this_sz += sz;
+ cp += sz;
+ room -= sz;
+ }
+ if (this_sz == 0)
+ break;
+ algop->update_fn(&ctx, buffer, this_sz);
+ }
+ algop->final_fn(hash, &ctx);
+
+ if (binary)
+ fwrite(hash, 1, algop->rawsz, stdout);
+ else
+ puts(hash_to_hex_algop(hash, algop));
+ exit(0);
+}
diff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c
index 23d2b17..f387062 100644
--- a/t/helper/test-hashmap.c
+++ b/t/helper/test-hashmap.c
@@ -5,6 +5,7 @@
struct test_entry
{
+ int padding; /* hashmap entry no longer needs to be the first member */
struct hashmap_entry ent;
/* key and value as two \0-terminated strings */
char key[FLEX_ARRAY];
@@ -16,15 +17,17 @@ static const char *get_value(const struct test_entry *e)
}
static int test_entry_cmp(const void *cmp_data,
- const void *entry,
- const void *entry_or_key,
+ const struct hashmap_entry *eptr,
+ const struct hashmap_entry *entry_or_key,
const void *keydata)
{
const int ignore_case = cmp_data ? *((int *)cmp_data) : 0;
- const struct test_entry *e1 = entry;
- const struct test_entry *e2 = entry_or_key;
+ const struct test_entry *e1, *e2;
const char *key = keydata;
+ e1 = container_of(eptr, const struct test_entry, ent);
+ e2 = container_of(entry_or_key, const struct test_entry, ent);
+
if (ignore_case)
return strcasecmp(e1->key, key ? key : e2->key);
else
@@ -37,7 +40,7 @@ static struct test_entry *alloc_test_entry(unsigned int hash,
size_t klen = strlen(key);
size_t vlen = strlen(value);
struct test_entry *entry = xmalloc(st_add4(sizeof(*entry), klen, vlen, 2));
- hashmap_entry_init(entry, hash);
+ hashmap_entry_init(&entry->ent, hash);
memcpy(entry->key, key, klen + 1);
memcpy(entry->key + klen + 1, value, vlen + 1);
return entry;
@@ -103,11 +106,11 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)
/* add entries */
for (i = 0; i < TEST_SIZE; i++) {
- hashmap_entry_init(entries[i], hashes[i]);
- hashmap_add(&map, entries[i]);
+ hashmap_entry_init(&entries[i]->ent, hashes[i]);
+ hashmap_add(&map, &entries[i]->ent);
}
- hashmap_free(&map, 0);
+ hashmap_free(&map);
}
} else {
/* test map lookups */
@@ -116,8 +119,8 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)
/* fill the map (sparsely if specified) */
j = (method & TEST_SPARSE) ? TEST_SIZE / 10 : TEST_SIZE;
for (i = 0; i < j; i++) {
- hashmap_entry_init(entries[i], hashes[i]);
- hashmap_add(&map, entries[i]);
+ hashmap_entry_init(&entries[i]->ent, hashes[i]);
+ hashmap_add(&map, &entries[i]->ent);
}
for (j = 0; j < rounds; j++) {
@@ -127,7 +130,7 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)
}
}
- hashmap_free(&map, 0);
+ hashmap_free(&map);
}
}
@@ -173,20 +176,13 @@ int cmd__hashmap(int argc, const char **argv)
p2 = strtok(NULL, DELIM);
}
- if (!strcmp("hash", cmd) && p1) {
-
- /* print results of different hash functions */
- printf("%u %u %u %u\n",
- strhash(p1), memhash(p1, strlen(p1)),
- strihash(p1), memihash(p1, strlen(p1)));
-
- } else if (!strcmp("add", cmd) && p1 && p2) {
+ if (!strcmp("add", cmd) && p1 && p2) {
/* create entry with key = p1, value = p2 */
entry = alloc_test_entry(hash, p1, p2);
/* add to hashmap */
- hashmap_add(&map, entry);
+ hashmap_add(&map, &entry->ent);
} else if (!strcmp("put", cmd) && p1 && p2) {
@@ -194,43 +190,44 @@ int cmd__hashmap(int argc, const char **argv)
entry = alloc_test_entry(hash, p1, p2);
/* add / replace entry */
- entry = hashmap_put(&map, entry);
+ entry = hashmap_put_entry(&map, entry, ent);
/* print and free replaced entry, if any */
puts(entry ? get_value(entry) : "NULL");
free(entry);
} else if (!strcmp("get", cmd) && p1) {
-
/* lookup entry in hashmap */
- entry = hashmap_get_from_hash(&map, hash, p1);
+ entry = hashmap_get_entry_from_hash(&map, hash, p1,
+ struct test_entry, ent);
/* print result */
if (!entry)
puts("NULL");
- while (entry) {
+ hashmap_for_each_entry_from(&map, entry, ent)
puts(get_value(entry));
- entry = hashmap_get_next(&map, entry);
- }
} else if (!strcmp("remove", cmd) && p1) {
/* setup static key */
struct hashmap_entry key;
+ struct hashmap_entry *rm;
hashmap_entry_init(&key, hash);
/* remove entry from hashmap */
- entry = hashmap_remove(&map, &key, p1);
+ rm = hashmap_remove(&map, &key, p1);
+ entry = rm ? container_of(rm, struct test_entry, ent)
+ : NULL;
/* print result and free entry*/
puts(entry ? get_value(entry) : "NULL");
free(entry);
} else if (!strcmp("iterate", cmd)) {
-
struct hashmap_iter iter;
- hashmap_iter_init(&map, &iter);
- while ((entry = hashmap_iter_next(&iter)))
+
+ hashmap_for_each_entry(&map, &iter, entry,
+ ent /* member name */)
printf("%s %s\n", entry->key, get_value(entry));
} else if (!strcmp("size", cmd)) {
@@ -265,6 +262,6 @@ int cmd__hashmap(int argc, const char **argv)
}
strbuf_release(&line);
- hashmap_free(&map, 1);
+ hashmap_free_entries(&map, struct test_entry, ent);
return 0;
}
diff --git a/t/helper/test-lazy-init-name-hash.c b/t/helper/test-lazy-init-name-hash.c
index b99a370..cd1b4c9 100644
--- a/t/helper/test-lazy-init-name-hash.c
+++ b/t/helper/test-lazy-init-name-hash.c
@@ -41,17 +41,13 @@ static void dump_run(void)
die("non-threaded code path used");
}
- dir = hashmap_iter_first(&the_index.dir_hash, &iter_dir);
- while (dir) {
+ hashmap_for_each_entry(&the_index.dir_hash, &iter_dir, dir,
+ ent /* member name */)
printf("dir %08x %7d %s\n", dir->ent.hash, dir->nr, dir->name);
- dir = hashmap_iter_next(&iter_dir);
- }
- ce = hashmap_iter_first(&the_index.name_hash, &iter_cache);
- while (ce) {
+ hashmap_for_each_entry(&the_index.name_hash, &iter_cache, ce,
+ ent /* member name */)
printf("name %08x %s\n", ce->ent.hash, ce->name);
- ce = hashmap_iter_next(&iter_cache);
- }
discard_cache();
}
diff --git a/t/helper/test-match-trees.c b/t/helper/test-match-trees.c
index 96857f2..b9fd427 100644
--- a/t/helper/test-match-trees.c
+++ b/t/helper/test-match-trees.c
@@ -20,7 +20,7 @@ int cmd__match_trees(int ac, const char **av)
if (!two)
die("not a tree-ish %s", av[2]);
- shift_tree(&one->object.oid, &two->object.oid, &shifted, -1);
+ shift_tree(the_repository, &one->object.oid, &two->object.oid, &shifted, -1);
printf("shifted: %s\n", oid_to_hex(&shifted));
exit(0);
diff --git a/t/helper/test-sha1-array.c b/t/helper/test-oid-array.c
index ad5e69f..ce9fd5f 100644
--- a/t/helper/test-sha1-array.c
+++ b/t/helper/test-oid-array.c
@@ -1,6 +1,6 @@
#include "test-tool.h"
#include "cache.h"
-#include "sha1-array.h"
+#include "oid-array.h"
static int print_oid(const struct object_id *oid, void *data)
{
@@ -8,7 +8,7 @@ static int print_oid(const struct object_id *oid, void *data)
return 0;
}
-int cmd__sha1_array(int argc, const char **argv)
+int cmd__oid_array(int argc, const char **argv)
{
struct oid_array array = OID_ARRAY_INIT;
struct strbuf line = STRBUF_INIT;
@@ -19,11 +19,11 @@ int cmd__sha1_array(int argc, const char **argv)
if (skip_prefix(line.buf, "append ", &arg)) {
if (get_oid_hex(arg, &oid))
- die("not a hexadecimal SHA1: %s", arg);
+ die("not a hexadecimal oid: %s", arg);
oid_array_append(&array, &oid);
} else if (skip_prefix(line.buf, "lookup ", &arg)) {
if (get_oid_hex(arg, &oid))
- die("not a hexadecimal SHA1: %s", arg);
+ die("not a hexadecimal oid: %s", arg);
printf("%d\n", oid_array_lookup(&array, &oid));
} else if (!strcmp(line.buf, "clear"))
oid_array_clear(&array);
diff --git a/t/helper/test-oidmap.c b/t/helper/test-oidmap.c
new file mode 100644
index 0000000..0acf999
--- /dev/null
+++ b/t/helper/test-oidmap.c
@@ -0,0 +1,112 @@
+#include "test-tool.h"
+#include "cache.h"
+#include "oidmap.h"
+#include "strbuf.h"
+
+/* key is an oid and value is a name (could be a refname for example) */
+struct test_entry {
+ struct oidmap_entry entry;
+ char name[FLEX_ARRAY];
+};
+
+#define DELIM " \t\r\n"
+
+/*
+ * Read stdin line by line and print result of commands to stdout:
+ *
+ * hash oidkey -> sha1hash(oidkey)
+ * put oidkey namevalue -> NULL / old namevalue
+ * get oidkey -> NULL / namevalue
+ * remove oidkey -> NULL / old namevalue
+ * iterate -> oidkey1 namevalue1\noidkey2 namevalue2\n...
+ *
+ */
+int cmd__oidmap(int argc, const char **argv)
+{
+ struct strbuf line = STRBUF_INIT;
+ struct oidmap map = OIDMAP_INIT;
+
+ setup_git_directory();
+
+ /* init oidmap */
+ oidmap_init(&map, 0);
+
+ /* process commands from stdin */
+ while (strbuf_getline(&line, stdin) != EOF) {
+ char *cmd, *p1 = NULL, *p2 = NULL;
+ struct test_entry *entry;
+ struct object_id oid;
+
+ /* break line into command and up to two parameters */
+ cmd = strtok(line.buf, DELIM);
+ /* ignore empty lines */
+ if (!cmd || *cmd == '#')
+ continue;
+
+ p1 = strtok(NULL, DELIM);
+ if (p1)
+ p2 = strtok(NULL, DELIM);
+
+ if (!strcmp("put", cmd) && p1 && p2) {
+
+ if (get_oid(p1, &oid)) {
+ printf("Unknown oid: %s\n", p1);
+ continue;
+ }
+
+ /* create entry with oid_key = p1, name_value = p2 */
+ FLEX_ALLOC_STR(entry, name, p2);
+ oidcpy(&entry->entry.oid, &oid);
+
+ /* add / replace entry */
+ entry = oidmap_put(&map, entry);
+
+ /* print and free replaced entry, if any */
+ puts(entry ? entry->name : "NULL");
+ free(entry);
+
+ } else if (!strcmp("get", cmd) && p1) {
+
+ if (get_oid(p1, &oid)) {
+ printf("Unknown oid: %s\n", p1);
+ continue;
+ }
+
+ /* lookup entry in oidmap */
+ entry = oidmap_get(&map, &oid);
+
+ /* print result */
+ puts(entry ? entry->name : "NULL");
+
+ } else if (!strcmp("remove", cmd) && p1) {
+
+ if (get_oid(p1, &oid)) {
+ printf("Unknown oid: %s\n", p1);
+ continue;
+ }
+
+ /* remove entry from oidmap */
+ entry = oidmap_remove(&map, &oid);
+
+ /* print result and free entry*/
+ puts(entry ? entry->name : "NULL");
+ free(entry);
+
+ } else if (!strcmp("iterate", cmd)) {
+
+ struct oidmap_iter iter;
+ oidmap_iter_init(&map, &iter);
+ while ((entry = oidmap_iter_next(&iter)))
+ printf("%s %s\n", oid_to_hex(&entry->entry.oid), entry->name);
+
+ } else {
+
+ printf("Unknown command %s\n", cmd);
+
+ }
+ }
+
+ strbuf_release(&line);
+ oidmap_free(&map, 1);
+ return 0;
+}
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
index 47fee66..2051ce5 100644
--- a/t/helper/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -2,6 +2,7 @@
#include "cache.h"
#include "parse-options.h"
#include "string-list.h"
+#include "trace2.h"
static int boolean = 0;
static int integer = 0;
@@ -120,6 +121,8 @@ int cmd__parse_options(int argc, const char **argv)
OPT_INTEGER('j', NULL, &integer, "get a integer, too"),
OPT_MAGNITUDE('m', "magnitude", &magnitude, "get a magnitude"),
OPT_SET_INT(0, "set23", &integer, "set integer to 23", 23),
+ OPT_CMDMODE(0, "mode1", &integer, "set integer to 1 (cmdmode option)", 1),
+ OPT_CMDMODE(0, "mode2", &integer, "set integer to 2 (cmdmode option)", 2),
OPT_CALLBACK('L', "length", &integer, "str",
"get length of <str>", length_callback),
OPT_FILENAME('F', "file", &file, "set file to <file>"),
@@ -131,7 +134,7 @@ int cmd__parse_options(int argc, const char **argv)
OPT_NOOP_NOARG(0, "obsolete"),
OPT_STRING_LIST(0, "list", &list, "str", "add str to list"),
OPT_GROUP("Magic arguments"),
- OPT_ARGUMENT("quux", "means --quux"),
+ OPT_ARGUMENT("quux", NULL, "means --quux"),
OPT_NUMBER_CALLBACK(&integer, "set integer to NUM",
number_callback),
{ OPTION_COUNTUP, '+', NULL, &boolean, NULL, "same as -b",
@@ -148,11 +151,16 @@ int cmd__parse_options(int argc, const char **argv)
OPT_CALLBACK(0, "expect", &expect, "string",
"expected output in the variable dump",
collect_expect),
+ OPT_GROUP("Alias"),
+ OPT_STRING('A', "alias-source", &string, "string", "get a string"),
+ OPT_ALIAS('Z', "alias-target", "alias-source"),
OPT_END(),
};
int i;
int ret = 0;
+ trace2_cmd_name("_parse_");
+
argc = parse_options(argc, (const char **)argv, prefix, options, usage, 0);
if (length_cb.called) {
diff --git a/t/helper/test-parse-pathspec-file.c b/t/helper/test-parse-pathspec-file.c
new file mode 100644
index 0000000..b3e08ce
--- /dev/null
+++ b/t/helper/test-parse-pathspec-file.c
@@ -0,0 +1,33 @@
+#include "test-tool.h"
+#include "parse-options.h"
+#include "pathspec.h"
+#include "gettext.h"
+
+int cmd__parse_pathspec_file(int argc, const char **argv)
+{
+ struct pathspec pathspec;
+ const char *pathspec_from_file = NULL;
+ int pathspec_file_nul = 0, i;
+
+ static const char *const usage[] = {
+ "test-tool parse-pathspec-file --pathspec-from-file [--pathspec-file-nul]",
+ NULL
+ };
+
+ struct option options[] = {
+ OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
+ OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
+ OPT_END()
+ };
+
+ parse_options(argc, argv, NULL, options, usage, 0);
+
+ parse_pathspec_file(&pathspec, 0, 0, NULL, pathspec_from_file,
+ pathspec_file_nul);
+
+ for (i = 0; i < pathspec.nr; i++)
+ printf("%s\n", pathspec.items[i].original);
+
+ clear_pathspec(&pathspec);
+ return 0;
+}
diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c
index e737a94..313a153 100644
--- a/t/helper/test-path-utils.c
+++ b/t/helper/test-path-utils.c
@@ -177,6 +177,14 @@ static int is_dotgitmodules(const char *path)
return is_hfs_dotgitmodules(path) || is_ntfs_dotgitmodules(path);
}
+static int cmp_by_st_size(const void *a, const void *b)
+{
+ intptr_t x = (intptr_t)((struct string_list_item *)a)->util;
+ intptr_t y = (intptr_t)((struct string_list_item *)b)->util;
+
+ return x > y ? -1 : (x < y ? +1 : 0);
+}
+
/*
* A very simple, reproducible pseudo-random generator. Copied from
* `test-genrandom.c`.
@@ -282,11 +290,14 @@ int cmd__path_utils(int argc, const char **argv)
}
if (argc >= 2 && !strcmp(argv[1], "real_path")) {
+ struct strbuf realpath = STRBUF_INIT;
while (argc > 2) {
- puts(real_path(argv[2]));
+ strbuf_realpath(&realpath, argv[2], 1);
+ puts(realpath.buf);
argc--;
argv++;
}
+ strbuf_release(&realpath);
return 0;
}
@@ -384,6 +395,62 @@ int cmd__path_utils(int argc, const char **argv)
return !!res;
}
+ if (argc > 2 && !strcmp(argv[1], "file-size")) {
+ int res = 0, i;
+ struct stat st;
+
+ for (i = 2; i < argc; i++)
+ if (stat(argv[i], &st))
+ res = error_errno("Cannot stat '%s'", argv[i]);
+ else
+ printf("%"PRIuMAX"\n", (uintmax_t)st.st_size);
+ return !!res;
+ }
+
+ if (argc == 4 && !strcmp(argv[1], "skip-n-bytes")) {
+ int fd = open(argv[2], O_RDONLY), offset = atoi(argv[3]);
+ char buffer[65536];
+
+ if (fd < 0)
+ die_errno("could not open '%s'", argv[2]);
+ if (lseek(fd, offset, SEEK_SET) < 0)
+ die_errno("could not skip %d bytes", offset);
+ for (;;) {
+ ssize_t count = read(fd, buffer, sizeof(buffer));
+ if (count < 0)
+ die_errno("could not read '%s'", argv[2]);
+ if (!count)
+ break;
+ if (write(1, buffer, count) < 0)
+ die_errno("could not write to stdout");
+ }
+ close(fd);
+ return 0;
+ }
+
+ if (argc > 5 && !strcmp(argv[1], "slice-tests")) {
+ int res = 0;
+ long offset, stride, i;
+ struct string_list list = STRING_LIST_INIT_NODUP;
+ struct stat st;
+
+ offset = strtol(argv[2], NULL, 10);
+ stride = strtol(argv[3], NULL, 10);
+ if (stride < 1)
+ stride = 1;
+ for (i = 4; i < argc; i++)
+ if (stat(argv[i], &st))
+ res = error_errno("Cannot stat '%s'", argv[i]);
+ else
+ string_list_append(&list, argv[i])->util =
+ (void *)(intptr_t)st.st_size;
+ QSORT(list.items, list.nr, cmp_by_st_size);
+ for (i = offset; i < list.nr; i+= stride)
+ printf("%s\n", list.items[i].string);
+
+ return !!res;
+ }
+
if (argc > 1 && !strcmp(argv[1], "protect_ntfs_hfs"))
return !!protect_ntfs_hfs_benchmark(argc - 1, argv + 1);
diff --git a/t/helper/test-pkt-line.c b/t/helper/test-pkt-line.c
index 282d536..12ca698 100644
--- a/t/helper/test-pkt-line.c
+++ b/t/helper/test-pkt-line.c
@@ -67,7 +67,7 @@ static void unpack_sideband(void)
case PACKET_READ_NORMAL:
band = reader.line[0] & 0xff;
if (band < 1 || band > 2)
- die("unexpected side band %d", band);
+ continue; /* skip non-sideband packets */
fd = band;
write_or_die(fd, reader.line + 1, reader.pktlen - 1);
diff --git a/t/helper/test-prio-queue.c b/t/helper/test-prio-queue.c
index 5bc9c46..f402844 100644
--- a/t/helper/test-prio-queue.c
+++ b/t/helper/test-prio-queue.c
@@ -40,7 +40,7 @@ int cmd__prio_queue(int argc, const char **argv)
} else if (!strcmp(*argv, "stack")) {
pq.compare = NULL;
} else {
- int *v = malloc(sizeof(*v));
+ int *v = xmalloc(sizeof(*v));
*v = atoi(*argv);
prio_queue_put(&pq, v);
}
diff --git a/t/helper/test-progress.c b/t/helper/test-progress.c
new file mode 100644
index 0000000..5d05cbe
--- /dev/null
+++ b/t/helper/test-progress.c
@@ -0,0 +1,74 @@
+/*
+ * A test helper to exercise the progress display.
+ *
+ * Reads instructions from standard input, one instruction per line:
+ *
+ * "progress <items>" - Call display_progress() with the given item count
+ * as parameter.
+ * "throughput <bytes> <millis> - Call display_throughput() with the given
+ * byte count as parameter. The 'millis'
+ * specify the time elapsed since the
+ * start_progress() call.
+ * "update" - Set the 'progress_update' flag.
+ *
+ * See 't0500-progress-display.sh' for examples.
+ */
+#define GIT_TEST_PROGRESS_ONLY
+#include "test-tool.h"
+#include "gettext.h"
+#include "parse-options.h"
+#include "progress.h"
+#include "strbuf.h"
+
+int cmd__progress(int argc, const char **argv)
+{
+ int total = 0;
+ const char *title;
+ struct strbuf line = STRBUF_INIT;
+ struct progress *progress;
+
+ const char *usage[] = {
+ "test-tool progress [--total=<n>] <progress-title>",
+ NULL
+ };
+ struct option options[] = {
+ OPT_INTEGER(0, "total", &total, "total number of items"),
+ OPT_END(),
+ };
+
+ argc = parse_options(argc, argv, NULL, options, usage, 0);
+ if (argc != 1)
+ die("need a title for the progress output");
+ title = argv[0];
+
+ progress_testing = 1;
+ progress = start_progress(title, total);
+ while (strbuf_getline(&line, stdin) != EOF) {
+ char *end;
+
+ if (skip_prefix(line.buf, "progress ", (const char **) &end)) {
+ uint64_t item_count = strtoull(end, &end, 10);
+ if (*end != '\0')
+ die("invalid input: '%s'\n", line.buf);
+ display_progress(progress, item_count);
+ } else if (skip_prefix(line.buf, "throughput ",
+ (const char **) &end)) {
+ uint64_t byte_count, test_ms;
+
+ byte_count = strtoull(end, &end, 10);
+ if (*end != ' ')
+ die("invalid input: '%s'\n", line.buf);
+ test_ms = strtoull(end + 1, &end, 10);
+ if (*end != '\0')
+ die("invalid input: '%s'\n", line.buf);
+ progress_test_ns = test_ms * 1000 * 1000;
+ display_throughput(progress, byte_count);
+ } else if (!strcmp(line.buf, "update"))
+ progress_test_force_update();
+ else
+ die("invalid input: '%s'\n", line.buf);
+ }
+ stop_progress(&progress);
+
+ return 0;
+}
diff --git a/t/helper/test-read-cache.c b/t/helper/test-read-cache.c
index d674c88..244977a 100644
--- a/t/helper/test-read-cache.c
+++ b/t/helper/test-read-cache.c
@@ -1,14 +1,35 @@
#include "test-tool.h"
#include "cache.h"
+#include "config.h"
int cmd__read_cache(int argc, const char **argv)
{
int i, cnt = 1;
+ const char *name = NULL;
+
+ if (argc > 1 && skip_prefix(argv[1], "--print-and-refresh=", &name)) {
+ argc--;
+ argv++;
+ }
+
if (argc == 2)
cnt = strtol(argv[1], NULL, 0);
setup_git_directory();
+ git_config(git_default_config, NULL);
for (i = 0; i < cnt; i++) {
read_cache();
+ if (name) {
+ int pos;
+
+ refresh_index(&the_index, REFRESH_QUIET,
+ NULL, NULL, NULL);
+ pos = index_name_pos(&the_index, name, strlen(name));
+ if (pos < 0)
+ die("%s not in index", name);
+ printf("%s is%s up to date\n", name,
+ ce_uptodate(the_index.cache[pos]) ? "" : " not");
+ write_file(name, "%d\n", i);
+ }
discard_cache();
}
return 0;
diff --git a/t/helper/test-read-graph.c b/t/helper/test-read-graph.c
new file mode 100644
index 0000000..6d0c962
--- /dev/null
+++ b/t/helper/test-read-graph.c
@@ -0,0 +1,46 @@
+#include "test-tool.h"
+#include "cache.h"
+#include "commit-graph.h"
+#include "repository.h"
+#include "object-store.h"
+
+int cmd__read_graph(int argc, const char **argv)
+{
+ struct commit_graph *graph = NULL;
+ struct object_directory *odb;
+
+ setup_git_directory();
+ odb = the_repository->objects->odb;
+
+ graph = read_commit_graph_one(the_repository, odb);
+ if (!graph)
+ return 1;
+
+
+ printf("header: %08x %d %d %d %d\n",
+ ntohl(*(uint32_t*)graph->data),
+ *(unsigned char*)(graph->data + 4),
+ *(unsigned char*)(graph->data + 5),
+ *(unsigned char*)(graph->data + 6),
+ *(unsigned char*)(graph->data + 7));
+ printf("num_commits: %u\n", graph->num_commits);
+ printf("chunks:");
+
+ if (graph->chunk_oid_fanout)
+ printf(" oid_fanout");
+ if (graph->chunk_oid_lookup)
+ printf(" oid_lookup");
+ if (graph->chunk_commit_data)
+ printf(" commit_metadata");
+ if (graph->chunk_extra_edges)
+ printf(" extra_edges");
+ if (graph->chunk_bloom_indexes)
+ printf(" bloom_indexes");
+ if (graph->chunk_bloom_data)
+ printf(" bloom_data");
+ printf("\n");
+
+ UNLEAK(graph);
+
+ return 0;
+}
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index e9e0541..799fc00 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -233,7 +233,7 @@ static int cmd_update_ref(struct ref_store *refs, const char **argv)
{
const char *msg = notnull(*argv++, "msg");
const char *refname = notnull(*argv++, "refname");
- const char *new_sha1_buf = notnull(*argv++, "old-sha1");
+ const char *new_sha1_buf = notnull(*argv++, "new-sha1");
const char *old_sha1_buf = notnull(*argv++, "old-sha1");
unsigned int flags = arg_flags(*argv++, "flags");
struct object_id old_oid;
diff --git a/t/helper/test-repository.c b/t/helper/test-repository.c
index 6a84a53..56f0e3c 100644
--- a/t/helper/test-repository.c
+++ b/t/helper/test-repository.c
@@ -17,9 +17,13 @@ static void test_parse_commit_in_graph(const char *gitdir, const char *worktree,
setup_git_env(gitdir);
+ memset(the_repository, 0, sizeof(*the_repository));
+
if (repo_init(&r, gitdir, worktree))
die("Couldn't init repo");
+ repo_set_hash_algo(the_repository, hash_algo_by_ptr(r.hash_algo));
+
c = lookup_commit(&r, commit_oid);
if (!parse_commit_in_graph(&r, c))
@@ -43,9 +47,13 @@ static void test_get_commit_tree_in_graph(const char *gitdir,
setup_git_env(gitdir);
+ memset(the_repository, 0, sizeof(*the_repository));
+
if (repo_init(&r, gitdir, worktree))
die("Couldn't init repo");
+ repo_set_hash_algo(the_repository, hash_algo_by_ptr(r.hash_algo));
+
c = lookup_commit(&r, commit_oid);
/*
@@ -65,6 +73,10 @@ static void test_get_commit_tree_in_graph(const char *gitdir,
int cmd__repository(int argc, const char **argv)
{
+ int nongit_ok = 0;
+
+ setup_git_directory_gently(&nongit_ok);
+
if (argc < 2)
die("must have at least 2 arguments");
if (!strcmp(argv[1], "parse_commit_in_graph")) {
diff --git a/t/helper/test-run-command.c b/t/helper/test-run-command.c
index 8579b1f..1646aa2 100644
--- a/t/helper/test-run-command.c
+++ b/t/helper/test-run-command.c
@@ -10,9 +10,14 @@
#include "test-tool.h"
#include "git-compat-util.h"
+#include "cache.h"
#include "run-command.h"
#include "argv-array.h"
#include "strbuf.h"
+#include "parse-options.h"
+#include "string-list.h"
+#include "thread-utils.h"
+#include "wildmatch.h"
#include "gettext.h"
#include "parse-options.h"
@@ -50,6 +55,151 @@ static int task_finished(int result,
return 1;
}
+struct testsuite {
+ struct string_list tests, failed;
+ int next;
+ int quiet, immediate, verbose, verbose_log, trace, write_junit_xml;
+};
+#define TESTSUITE_INIT \
+ { STRING_LIST_INIT_DUP, STRING_LIST_INIT_DUP, -1, 0, 0, 0, 0, 0, 0 }
+
+static int next_test(struct child_process *cp, struct strbuf *err, void *cb,
+ void **task_cb)
+{
+ struct testsuite *suite = cb;
+ const char *test;
+ if (suite->next >= suite->tests.nr)
+ return 0;
+
+ test = suite->tests.items[suite->next++].string;
+ argv_array_pushl(&cp->args, "sh", test, NULL);
+ if (suite->quiet)
+ argv_array_push(&cp->args, "--quiet");
+ if (suite->immediate)
+ argv_array_push(&cp->args, "-i");
+ if (suite->verbose)
+ argv_array_push(&cp->args, "-v");
+ if (suite->verbose_log)
+ argv_array_push(&cp->args, "-V");
+ if (suite->trace)
+ argv_array_push(&cp->args, "-x");
+ if (suite->write_junit_xml)
+ argv_array_push(&cp->args, "--write-junit-xml");
+
+ strbuf_addf(err, "Output of '%s':\n", test);
+ *task_cb = (void *)test;
+
+ return 1;
+}
+
+static int test_finished(int result, struct strbuf *err, void *cb,
+ void *task_cb)
+{
+ struct testsuite *suite = cb;
+ const char *name = (const char *)task_cb;
+
+ if (result)
+ string_list_append(&suite->failed, name);
+
+ strbuf_addf(err, "%s: '%s'\n", result ? "FAIL" : "SUCCESS", name);
+
+ return 0;
+}
+
+static int test_failed(struct strbuf *out, void *cb, void *task_cb)
+{
+ struct testsuite *suite = cb;
+ const char *name = (const char *)task_cb;
+
+ string_list_append(&suite->failed, name);
+ strbuf_addf(out, "FAILED TO START: '%s'\n", name);
+
+ return 0;
+}
+
+static const char * const testsuite_usage[] = {
+ "test-run-command testsuite [<options>] [<pattern>...]",
+ NULL
+};
+
+static int testsuite(int argc, const char **argv)
+{
+ struct testsuite suite = TESTSUITE_INIT;
+ int max_jobs = 1, i, ret;
+ DIR *dir;
+ struct dirent *d;
+ struct option options[] = {
+ OPT_BOOL('i', "immediate", &suite.immediate,
+ "stop at first failed test case(s)"),
+ OPT_INTEGER('j', "jobs", &max_jobs, "run <N> jobs in parallel"),
+ OPT_BOOL('q', "quiet", &suite.quiet, "be terse"),
+ OPT_BOOL('v', "verbose", &suite.verbose, "be verbose"),
+ OPT_BOOL('V', "verbose-log", &suite.verbose_log,
+ "be verbose, redirected to a file"),
+ OPT_BOOL('x', "trace", &suite.trace, "trace shell commands"),
+ OPT_BOOL(0, "write-junit-xml", &suite.write_junit_xml,
+ "write JUnit-style XML files"),
+ OPT_END()
+ };
+
+ memset(&suite, 0, sizeof(suite));
+ suite.tests.strdup_strings = suite.failed.strdup_strings = 1;
+
+ argc = parse_options(argc, argv, NULL, options,
+ testsuite_usage, PARSE_OPT_STOP_AT_NON_OPTION);
+
+ if (max_jobs <= 0)
+ max_jobs = online_cpus();
+
+ dir = opendir(".");
+ if (!dir)
+ die("Could not open the current directory");
+ while ((d = readdir(dir))) {
+ const char *p = d->d_name;
+
+ if (*p != 't' || !isdigit(p[1]) || !isdigit(p[2]) ||
+ !isdigit(p[3]) || !isdigit(p[4]) || p[5] != '-' ||
+ !ends_with(p, ".sh"))
+ continue;
+
+ /* No pattern: match all */
+ if (!argc) {
+ string_list_append(&suite.tests, p);
+ continue;
+ }
+
+ for (i = 0; i < argc; i++)
+ if (!wildmatch(argv[i], p, 0)) {
+ string_list_append(&suite.tests, p);
+ break;
+ }
+ }
+ closedir(dir);
+
+ if (!suite.tests.nr)
+ die("No tests match!");
+ if (max_jobs > suite.tests.nr)
+ max_jobs = suite.tests.nr;
+
+ fprintf(stderr, "Running %d tests (%d at a time)\n",
+ suite.tests.nr, max_jobs);
+
+ ret = run_processes_parallel(max_jobs, next_test, test_failed,
+ test_finished, &suite);
+
+ if (suite.failed.nr > 0) {
+ ret = 1;
+ fprintf(stderr, "%d tests failed:\n\n", suite.failed.nr);
+ for (i = 0; i < suite.failed.nr; i++)
+ fprintf(stderr, "\t%s\n", suite.failed.items[i].string);
+ }
+
+ string_list_clear(&suite.tests, 0);
+ string_list_clear(&suite.failed, 0);
+
+ return !!ret;
+}
+
static uint64_t my_random_next = 1234;
static uint64_t my_random(void)
@@ -178,11 +328,58 @@ static int quote_echo(int argc, const char **argv)
return 0;
}
+static int inherit_handle(const char *argv0)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+ char path[PATH_MAX];
+ int tmp;
+
+ /* First, open an inheritable handle */
+ xsnprintf(path, sizeof(path), "out-XXXXXX");
+ tmp = xmkstemp(path);
+
+ argv_array_pushl(&cp.args,
+ "test-tool", argv0, "inherited-handle-child", NULL);
+ cp.in = -1;
+ cp.no_stdout = cp.no_stderr = 1;
+ if (start_command(&cp) < 0)
+ die("Could not start child process");
+
+ /* Then close it, and try to delete it. */
+ close(tmp);
+ if (unlink(path))
+ die("Could not delete '%s'", path);
+
+ if (close(cp.in) < 0 || finish_command(&cp) < 0)
+ die("Child did not finish");
+
+ return 0;
+}
+
+static int inherit_handle_child(void)
+{
+ struct strbuf buf = STRBUF_INIT;
+
+ if (strbuf_read(&buf, 0, 0) < 0)
+ die("Could not read stdin");
+ printf("Received %s\n", buf.buf);
+ strbuf_release(&buf);
+
+ return 0;
+}
+
int cmd__run_command(int argc, const char **argv)
{
struct child_process proc = CHILD_PROCESS_INIT;
int jobs;
+ if (argc > 1 && !strcmp(argv[1], "testsuite"))
+ exit(testsuite(argc - 1, argv + 1));
+ if (!strcmp(argv[1], "inherited-handle"))
+ exit(inherit_handle(argv[0]));
+ if (!strcmp(argv[1], "inherited-handle-child"))
+ exit(inherit_handle_child());
+
if (argc >= 2 && !strcmp(argv[1], "quote-stress-test"))
return !!quote_stress_test(argc - 1, argv + 1);
diff --git a/t/helper/test-serve-v2.c b/t/helper/test-serve-v2.c
new file mode 100644
index 0000000..aee35e5
--- /dev/null
+++ b/t/helper/test-serve-v2.c
@@ -0,0 +1,31 @@
+#include "test-tool.h"
+#include "cache.h"
+#include "parse-options.h"
+#include "serve.h"
+
+static char const * const serve_usage[] = {
+ N_("test-tool serve-v2 [<options>]"),
+ NULL
+};
+
+int cmd__serve_v2(int argc, const char **argv)
+{
+ struct serve_options opts = SERVE_OPTIONS_INIT;
+
+ struct option options[] = {
+ OPT_BOOL(0, "stateless-rpc", &opts.stateless_rpc,
+ N_("quit after a single request/response exchange")),
+ OPT_BOOL(0, "advertise-capabilities", &opts.advertise_capabilities,
+ N_("exit immediately after advertising capabilities")),
+ OPT_END()
+ };
+ const char *prefix = setup_git_directory();
+
+ /* ignore all unknown cmdline switches for now */
+ argc = parse_options(argc, argv, prefix, options, serve_usage,
+ PARSE_OPT_KEEP_DASHDASH |
+ PARSE_OPT_KEEP_UNKNOWN);
+ serve(&opts);
+
+ return 0;
+}
diff --git a/t/helper/test-sha1.c b/t/helper/test-sha1.c
index 1ba0675..d860c38 100644
--- a/t/helper/test-sha1.c
+++ b/t/helper/test-sha1.c
@@ -3,55 +3,5 @@
int cmd__sha1(int ac, const char **av)
{
- git_SHA_CTX ctx;
- unsigned char sha1[20];
- unsigned bufsz = 8192;
- int binary = 0;
- char *buffer;
-
- if (ac == 2) {
- if (!strcmp(av[1], "-b"))
- binary = 1;
- else
- bufsz = strtoul(av[1], NULL, 10) * 1024 * 1024;
- }
-
- if (!bufsz)
- bufsz = 8192;
-
- while ((buffer = malloc(bufsz)) == NULL) {
- fprintf(stderr, "bufsz %u is too big, halving...\n", bufsz);
- bufsz /= 2;
- if (bufsz < 1024)
- die("OOPS");
- }
-
- git_SHA1_Init(&ctx);
-
- while (1) {
- ssize_t sz, this_sz;
- char *cp = buffer;
- unsigned room = bufsz;
- this_sz = 0;
- while (room) {
- sz = xread(0, cp, room);
- if (sz == 0)
- break;
- if (sz < 0)
- die_errno("test-sha1");
- this_sz += sz;
- cp += sz;
- room -= sz;
- }
- if (this_sz == 0)
- break;
- git_SHA1_Update(&ctx, buffer, this_sz);
- }
- git_SHA1_Final(sha1, &ctx);
-
- if (binary)
- fwrite(sha1, 1, 20, stdout);
- else
- puts(sha1_to_hex(sha1));
- exit(0);
+ return cmd_hash_impl(ac, av, GIT_HASH_SHA1);
}
diff --git a/t/helper/test-sha256.c b/t/helper/test-sha256.c
new file mode 100644
index 0000000..0ac6a99
--- /dev/null
+++ b/t/helper/test-sha256.c
@@ -0,0 +1,7 @@
+#include "test-tool.h"
+#include "cache.h"
+
+int cmd__sha256(int ac, const char **av)
+{
+ return cmd_hash_impl(ac, av, GIT_HASH_SHA256);
+}
diff --git a/t/helper/test-sigchain.c b/t/helper/test-sigchain.c
index 77ac5bc..d013bcc 100644
--- a/t/helper/test-sigchain.c
+++ b/t/helper/test-sigchain.c
@@ -14,7 +14,8 @@ X(two)
X(three)
#undef X
-int cmd__sigchain(int argc, const char **argv) {
+int cmd__sigchain(int argc, const char **argv)
+{
sigchain_push(SIGTERM, one);
sigchain_push(SIGTERM, two);
sigchain_push(SIGTERM, three);
diff --git a/t/helper/test-submodule-nested-repo-config.c b/t/helper/test-submodule-nested-repo-config.c
index a31e2a9..bc97929 100644
--- a/t/helper/test-submodule-nested-repo-config.c
+++ b/t/helper/test-submodule-nested-repo-config.c
@@ -10,19 +10,21 @@ static void die_usage(int argc, const char **argv, const char *msg)
int cmd__submodule_nested_repo_config(int argc, const char **argv)
{
- struct repository submodule;
+ struct repository subrepo;
+ const struct submodule *sub;
if (argc < 3)
die_usage(argc, argv, "Wrong number of arguments.");
setup_git_directory();
- if (repo_submodule_init(&submodule, the_repository, argv[1])) {
+ sub = submodule_from_path(the_repository, &null_oid, argv[1]);
+ if (repo_submodule_init(&subrepo, the_repository, sub)) {
die_usage(argc, argv, "Submodule not found.");
}
/* Read the config of _child_ submodules. */
- print_config_from_gitmodules(&submodule, argv[2]);
+ print_config_from_gitmodules(&subrepo, argv[2]);
submodule_free(the_repository);
diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c
index bfb195b..590b2ef 100644
--- a/t/helper/test-tool.c
+++ b/t/helper/test-tool.c
@@ -1,5 +1,12 @@
#include "git-compat-util.h"
#include "test-tool.h"
+#include "trace2.h"
+#include "parse-options.h"
+
+static const char * const test_tool_usage[] = {
+ "test-tool [-C <directory>] <command [<arguments>...]]",
+ NULL
+};
struct test_cmd {
const char *name;
@@ -7,11 +14,14 @@ struct test_cmd {
};
static struct test_cmd cmds[] = {
+ { "advise", cmd__advise_if_enabled },
+ { "bloom", cmd__bloom },
{ "chmtime", cmd__chmtime },
{ "config", cmd__config },
{ "ctype", cmd__ctype },
{ "date", cmd__date },
{ "delta", cmd__delta },
+ { "dir-iterator", cmd__dir_iterator },
{ "drop-caches", cmd__drop_caches },
{ "dump-cache-tree", cmd__dump_cache_tree },
{ "dump-fsmonitor", cmd__dump_fsmonitor },
@@ -19,20 +29,27 @@ static struct test_cmd cmds[] = {
{ "dump-untracked-cache", cmd__dump_untracked_cache },
{ "example-decorate", cmd__example_decorate },
{ "genrandom", cmd__genrandom },
+ { "genzeros", cmd__genzeros },
{ "hashmap", cmd__hashmap },
+ { "hash-speed", cmd__hash_speed },
{ "index-version", cmd__index_version },
{ "json-writer", cmd__json_writer },
{ "lazy-init-name-hash", cmd__lazy_init_name_hash },
{ "match-trees", cmd__match_trees },
{ "mergesort", cmd__mergesort },
{ "mktemp", cmd__mktemp },
+ { "oid-array", cmd__oid_array },
+ { "oidmap", cmd__oidmap },
{ "online-cpus", cmd__online_cpus },
{ "parse-options", cmd__parse_options },
+ { "parse-pathspec-file", cmd__parse_pathspec_file },
{ "path-utils", cmd__path_utils },
{ "pkt-line", cmd__pkt_line },
{ "prio-queue", cmd__prio_queue },
+ { "progress", cmd__progress },
{ "reach", cmd__reach },
{ "read-cache", cmd__read_cache },
+ { "read-graph", cmd__read_graph },
{ "read-midx", cmd__read_midx },
{ "ref-store", cmd__ref_store },
{ "regex", cmd__regex },
@@ -40,15 +57,18 @@ static struct test_cmd cmds[] = {
{ "revision-walking", cmd__revision_walking },
{ "run-command", cmd__run_command },
{ "scrap-cache-tree", cmd__scrap_cache_tree },
+ { "serve-v2", cmd__serve_v2 },
{ "sha1", cmd__sha1 },
- { "sha1-array", cmd__sha1_array },
+ { "sha256", cmd__sha256 },
{ "sigchain", cmd__sigchain },
{ "strcmp-offset", cmd__strcmp_offset },
{ "string-list", cmd__string_list },
{ "submodule-config", cmd__submodule_config },
{ "submodule-nested-repo-config", cmd__submodule_nested_repo_config },
{ "subprocess", cmd__subprocess },
+ { "trace2", cmd__trace2 },
{ "urlmatch-normalization", cmd__urlmatch_normalization },
+ { "xml-encode", cmd__xml_encode },
{ "wildmatch", cmd__wildmatch },
#ifdef GIT_WINDOWS_NATIVE
{ "windows-named-pipe", cmd__windows_named_pipe },
@@ -69,15 +89,31 @@ static NORETURN void die_usage(void)
int cmd_main(int argc, const char **argv)
{
int i;
+ const char *working_directory = NULL;
+ struct option options[] = {
+ OPT_STRING('C', NULL, &working_directory, "directory",
+ "change the working directory"),
+ OPT_END()
+ };
BUG_exit_code = 99;
+ argc = parse_options(argc, argv, NULL, options, test_tool_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION |
+ PARSE_OPT_KEEP_ARGV0);
+
if (argc < 2)
die_usage();
+ if (working_directory && chdir(working_directory) < 0)
+ die("Could not cd to '%s'", working_directory);
+
for (i = 0; i < ARRAY_SIZE(cmds); i++) {
if (!strcmp(cmds[i].name, argv[1])) {
argv++;
argc--;
+ trace2_cmd_name(cmds[i].name);
+ trace2_cmd_list_config();
+ trace2_cmd_list_env_vars();
return cmds[i].fn(argc, argv);
}
}
diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h
index 042f124..ddc8e99 100644
--- a/t/helper/test-tool.h
+++ b/t/helper/test-tool.h
@@ -1,13 +1,17 @@
#ifndef TEST_TOOL_H
#define TEST_TOOL_H
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
#include "git-compat-util.h"
+int cmd__advise_if_enabled(int argc, const char **argv);
+int cmd__bloom(int argc, const char **argv);
int cmd__chmtime(int argc, const char **argv);
int cmd__config(int argc, const char **argv);
int cmd__ctype(int argc, const char **argv);
int cmd__date(int argc, const char **argv);
int cmd__delta(int argc, const char **argv);
+int cmd__dir_iterator(int argc, const char **argv);
int cmd__drop_caches(int argc, const char **argv);
int cmd__dump_cache_tree(int argc, const char **argv);
int cmd__dump_fsmonitor(int argc, const char **argv);
@@ -15,20 +19,26 @@ int cmd__dump_split_index(int argc, const char **argv);
int cmd__dump_untracked_cache(int argc, const char **argv);
int cmd__example_decorate(int argc, const char **argv);
int cmd__genrandom(int argc, const char **argv);
+int cmd__genzeros(int argc, const char **argv);
int cmd__hashmap(int argc, const char **argv);
+int cmd__hash_speed(int argc, const char **argv);
int cmd__index_version(int argc, const char **argv);
int cmd__json_writer(int argc, const char **argv);
int cmd__lazy_init_name_hash(int argc, const char **argv);
int cmd__match_trees(int argc, const char **argv);
int cmd__mergesort(int argc, const char **argv);
int cmd__mktemp(int argc, const char **argv);
+int cmd__oidmap(int argc, const char **argv);
int cmd__online_cpus(int argc, const char **argv);
int cmd__parse_options(int argc, const char **argv);
+int cmd__parse_pathspec_file(int argc, const char** argv);
int cmd__path_utils(int argc, const char **argv);
int cmd__pkt_line(int argc, const char **argv);
int cmd__prio_queue(int argc, const char **argv);
+int cmd__progress(int argc, const char **argv);
int cmd__reach(int argc, const char **argv);
int cmd__read_cache(int argc, const char **argv);
+int cmd__read_graph(int argc, const char **argv);
int cmd__read_midx(int argc, const char **argv);
int cmd__ref_store(int argc, const char **argv);
int cmd__regex(int argc, const char **argv);
@@ -36,19 +46,25 @@ int cmd__repository(int argc, const char **argv);
int cmd__revision_walking(int argc, const char **argv);
int cmd__run_command(int argc, const char **argv);
int cmd__scrap_cache_tree(int argc, const char **argv);
+int cmd__serve_v2(int argc, const char **argv);
int cmd__sha1(int argc, const char **argv);
-int cmd__sha1_array(int argc, const char **argv);
+int cmd__oid_array(int argc, const char **argv);
+int cmd__sha256(int argc, const char **argv);
int cmd__sigchain(int argc, const char **argv);
int cmd__strcmp_offset(int argc, const char **argv);
int cmd__string_list(int argc, const char **argv);
int cmd__submodule_config(int argc, const char **argv);
int cmd__submodule_nested_repo_config(int argc, const char **argv);
int cmd__subprocess(int argc, const char **argv);
+int cmd__trace2(int argc, const char **argv);
int cmd__urlmatch_normalization(int argc, const char **argv);
+int cmd__xml_encode(int argc, const char **argv);
int cmd__wildmatch(int argc, const char **argv);
#ifdef GIT_WINDOWS_NATIVE
int cmd__windows_named_pipe(int argc, const char **argv);
#endif
int cmd__write_cache(int argc, const char **argv);
+int cmd_hash_impl(int ac, const char **av, int algo);
+
#endif
diff --git a/t/helper/test-trace2.c b/t/helper/test-trace2.c
new file mode 100644
index 0000000..197819c
--- /dev/null
+++ b/t/helper/test-trace2.c
@@ -0,0 +1,273 @@
+#include "test-tool.h"
+#include "cache.h"
+#include "argv-array.h"
+#include "run-command.h"
+#include "exec-cmd.h"
+#include "config.h"
+
+typedef int(fn_unit_test)(int argc, const char **argv);
+
+struct unit_test {
+ fn_unit_test *ut_fn;
+ const char *ut_name;
+ const char *ut_usage;
+};
+
+#define MyOk 0
+#define MyError 1
+
+static int get_i(int *p_value, const char *data)
+{
+ char *endptr;
+
+ if (!data || !*data)
+ return MyError;
+
+ *p_value = strtol(data, &endptr, 10);
+ if (*endptr || errno == ERANGE)
+ return MyError;
+
+ return MyOk;
+}
+
+/*
+ * Cause process to exit with the requested value via "return".
+ *
+ * Rely on test-tool.c:cmd_main() to call trace2_cmd_exit()
+ * with our result.
+ *
+ * Test harness can confirm:
+ * [] the process-exit value.
+ * [] the "code" field in the "exit" trace2 event.
+ * [] the "code" field in the "atexit" trace2 event.
+ * [] the "name" field in the "cmd_name" trace2 event.
+ * [] "def_param" events for all of the "interesting" pre-defined
+ * config settings.
+ */
+static int ut_001return(int argc, const char **argv)
+{
+ int rc;
+
+ if (get_i(&rc, argv[0]))
+ die("expect <exit_code>");
+
+ return rc;
+}
+
+/*
+ * Cause the process to exit with the requested value via "exit()".
+ *
+ * Test harness can confirm:
+ * [] the "code" field in the "exit" trace2 event.
+ * [] the "code" field in the "atexit" trace2 event.
+ * [] the "name" field in the "cmd_name" trace2 event.
+ * [] "def_param" events for all of the "interesting" pre-defined
+ * config settings.
+ */
+static int ut_002exit(int argc, const char **argv)
+{
+ int rc;
+
+ if (get_i(&rc, argv[0]))
+ die("expect <exit_code>");
+
+ exit(rc);
+}
+
+/*
+ * Send an "error" event with each value in argv. Normally, git only issues
+ * a single "error" event immediately before issuing an "exit" event (such
+ * as in die() or BUG()), but multiple "error" events are allowed.
+ *
+ * Test harness can confirm:
+ * [] a trace2 "error" event for each value in argv.
+ * [] the "name" field in the "cmd_name" trace2 event.
+ * [] (optional) the file:line in the "exit" event refers to this function.
+ */
+static int ut_003error(int argc, const char **argv)
+{
+ int k;
+
+ if (!argv[0] || !*argv[0])
+ die("expect <error_message>");
+
+ for (k = 0; k < argc; k++)
+ error("%s", argv[k]);
+
+ return 0;
+}
+
+/*
+ * Run a child process and wait for it to finish and exit with its return code.
+ * test-tool trace2 004child [<child-command-line>]
+ *
+ * For example:
+ * test-tool trace2 004child git version
+ * test-tool trace2 004child test-tool trace2 001return 0
+ * test-tool trace2 004child test-tool trace2 004child test-tool trace2 004child
+ * test-tool trace2 004child git -c alias.xyz=version xyz
+ *
+ * Test harness can confirm:
+ * [] the "name" field in the "cmd_name" trace2 event.
+ * [] that the outer process has a single component SID (or depth "d0" in
+ * the PERF stream).
+ * [] that "child_start" and "child_exit" events are generated for the child.
+ * [] if the child process is an instrumented executable:
+ * [] that "version", "start", ..., "exit", and "atexit" events are
+ * generated by the child process.
+ * [] that the child process events have a multiple component SID (or
+ * depth "dN+1" in the PERF stream).
+ * [] that the child exit code is propagated to the parent process "exit"
+ * and "atexit" events..
+ * [] (optional) that the "t_abs" field in the child process "atexit" event
+ * is less than the "t_rel" field in the "child_exit" event of the parent
+ * process.
+ * [] if the child process is like the alias example above,
+ * [] (optional) the child process attempts to run "git-xyx" as a dashed
+ * command.
+ * [] the child process emits an "alias" event with "xyz" => "version"
+ * [] the child process runs "git version" as a child process.
+ * [] the child process has a 3 component SID (or depth "d2" in the PERF
+ * stream).
+ */
+static int ut_004child(int argc, const char **argv)
+{
+ int result;
+
+ /*
+ * Allow empty <child_command_line> so we can do arbitrarily deep
+ * command nesting and let the last one be null.
+ */
+ if (!argc)
+ return 0;
+
+ result = run_command_v_opt(argv, 0);
+ exit(result);
+}
+
+/*
+ * Exec a git command. This may either create a child process (Windows)
+ * or replace the existing process.
+ * test-tool trace2 005exec <git_command_args>
+ *
+ * For example:
+ * test-tool trace2 005exec version
+ *
+ * Test harness can confirm (on Windows):
+ * [] the "name" field in the "cmd_name" trace2 event.
+ * [] that the outer process has a single component SID (or depth "d0" in
+ * the PERF stream).
+ * [] that "exec" and "exec_result" events are generated for the child
+ * process (since the Windows compatibility layer fakes an exec() with
+ * a CreateProcess(), WaitForSingleObject(), and exit()).
+ * [] that the child process has multiple component SID (or depth "dN+1"
+ * in the PERF stream).
+ *
+ * Test harness can confirm (on platforms with a real exec() function):
+ * [] TODO talk about process replacement and how it affects SID.
+ */
+static int ut_005exec(int argc, const char **argv)
+{
+ int result;
+
+ if (!argc)
+ return 0;
+
+ result = execv_git_cmd(argv);
+ return result;
+}
+
+static int ut_006data(int argc, const char **argv)
+{
+ const char *usage_error =
+ "expect <cat0> <k0> <v0> [<cat1> <k1> <v1> [...]]";
+
+ if (argc % 3 != 0)
+ die("%s", usage_error);
+
+ while (argc) {
+ if (!argv[0] || !*argv[0] || !argv[1] || !*argv[1] ||
+ !argv[2] || !*argv[2])
+ die("%s", usage_error);
+
+ trace2_data_string(argv[0], the_repository, argv[1], argv[2]);
+ argv += 3;
+ argc -= 3;
+ }
+
+ return 0;
+}
+
+/*
+ * Usage:
+ * test-tool trace2 <ut_name_1> <ut_usage_1>
+ * test-tool trace2 <ut_name_2> <ut_usage_2>
+ * ...
+ */
+#define USAGE_PREFIX "test-tool trace2"
+
+/* clang-format off */
+static struct unit_test ut_table[] = {
+ { ut_001return, "001return", "<exit_code>" },
+ { ut_002exit, "002exit", "<exit_code>" },
+ { ut_003error, "003error", "<error_message>+" },
+ { ut_004child, "004child", "[<child_command_line>]" },
+ { ut_005exec, "005exec", "<git_command_args>" },
+ { ut_006data, "006data", "[<category> <key> <value>]+" },
+};
+/* clang-format on */
+
+/* clang-format off */
+#define for_each_ut(k, ut_k) \
+ for (k = 0, ut_k = &ut_table[k]; \
+ k < ARRAY_SIZE(ut_table); \
+ k++, ut_k = &ut_table[k])
+/* clang-format on */
+
+static int print_usage(void)
+{
+ int k;
+ struct unit_test *ut_k;
+
+ fprintf(stderr, "usage:\n");
+ for_each_ut (k, ut_k)
+ fprintf(stderr, "\t%s %s %s\n", USAGE_PREFIX, ut_k->ut_name,
+ ut_k->ut_usage);
+
+ return 129;
+}
+
+/*
+ * Issue various trace2 events for testing.
+ *
+ * We assume that these trace2 routines has already been called:
+ * [] trace2_initialize() [common-main.c:main()]
+ * [] trace2_cmd_start() [common-main.c:main()]
+ * [] trace2_cmd_name() [test-tool.c:cmd_main()]
+ * [] tracd2_cmd_list_config() [test-tool.c:cmd_main()]
+ * So that:
+ * [] the various trace2 streams are open.
+ * [] the process SID has been created.
+ * [] the "version" event has been generated.
+ * [] the "start" event has been generated.
+ * [] the "cmd_name" event has been generated.
+ * [] this writes various "def_param" events for interesting config values.
+ *
+ * We further assume that if we return (rather than exit()), trace2_cmd_exit()
+ * will be called by test-tool.c:cmd_main().
+ */
+int cmd__trace2(int argc, const char **argv)
+{
+ int k;
+ struct unit_test *ut_k;
+
+ argc--; /* skip over "trace2" arg */
+ argv++;
+
+ if (argc)
+ for_each_ut (k, ut_k)
+ if (!strcmp(argv[0], ut_k->ut_name))
+ return ut_k->ut_fn(argc - 1, argv + 1);
+
+ return print_usage();
+}
diff --git a/t/helper/test-windows-named-pipe.c b/t/helper/test-windows-named-pipe.c
index b4b752b..ae52183 100644
--- a/t/helper/test-windows-named-pipe.c
+++ b/t/helper/test-windows-named-pipe.c
@@ -19,7 +19,7 @@ int cmd__windows_named_pipe(int argc, const char **argv)
if (argc < 2)
goto print_usage;
filename = argv[1];
- if (strchr(filename, '/') || strchr(filename, '\\'))
+ if (strpbrk(filename, "/\\"))
goto print_usage;
strbuf_addf(&pathname, "//./pipe/%s", filename);
diff --git a/t/helper/test-xml-encode.c b/t/helper/test-xml-encode.c
new file mode 100644
index 0000000..a648bbd
--- /dev/null
+++ b/t/helper/test-xml-encode.c
@@ -0,0 +1,80 @@
+#include "test-tool.h"
+
+static const char *utf8_replace_character = "&#xfffd;";
+
+/*
+ * Encodes (possibly incorrect) UTF-8 on <stdin> to <stdout>, to be embedded
+ * in an XML file.
+ */
+int cmd__xml_encode(int argc, const char **argv)
+{
+ unsigned char buf[1024], tmp[4], *tmp2 = NULL;
+ ssize_t cur = 0, len = 1, remaining = 0;
+ unsigned char ch;
+
+ for (;;) {
+ if (++cur == len) {
+ len = xread(0, buf, sizeof(buf));
+ if (!len)
+ return 0;
+ if (len < 0)
+ die_errno("Could not read <stdin>");
+ cur = 0;
+ }
+ ch = buf[cur];
+
+ if (tmp2) {
+ if ((ch & 0xc0) != 0x80) {
+ fputs(utf8_replace_character, stdout);
+ tmp2 = NULL;
+ cur--;
+ continue;
+ }
+ *tmp2 = ch;
+ tmp2++;
+ if (--remaining == 0) {
+ fwrite(tmp, tmp2 - tmp, 1, stdout);
+ tmp2 = NULL;
+ }
+ continue;
+ }
+
+ if (!(ch & 0x80)) {
+ /* 0xxxxxxx */
+ if (ch == '&')
+ fputs("&amp;", stdout);
+ else if (ch == '\'')
+ fputs("&apos;", stdout);
+ else if (ch == '"')
+ fputs("&quot;", stdout);
+ else if (ch == '<')
+ fputs("&lt;", stdout);
+ else if (ch == '>')
+ fputs("&gt;", stdout);
+ else if (ch >= 0x20)
+ fputc(ch, stdout);
+ else if (ch == 0x09 || ch == 0x0a || ch == 0x0d)
+ fprintf(stdout, "&#x%02x;", ch);
+ else
+ fputs(utf8_replace_character, stdout);
+ } else if ((ch & 0xe0) == 0xc0) {
+ /* 110XXXXx 10xxxxxx */
+ tmp[0] = ch;
+ remaining = 1;
+ tmp2 = tmp + 1;
+ } else if ((ch & 0xf0) == 0xe0) {
+ /* 1110XXXX 10Xxxxxx 10xxxxxx */
+ tmp[0] = ch;
+ remaining = 2;
+ tmp2 = tmp + 1;
+ } else if ((ch & 0xf8) == 0xf0) {
+ /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */
+ tmp[0] = ch;
+ remaining = 3;
+ tmp2 = tmp + 1;
+ } else
+ fputs(utf8_replace_character, stdout);
+ }
+
+ return 0;
+}
diff --git a/t/interop/i5500-git-daemon.sh b/t/interop/i5500-git-daemon.sh
index 1daf694..4d22e42 100755
--- a/t/interop/i5500-git-daemon.sh
+++ b/t/interop/i5500-git-daemon.sh
@@ -37,5 +37,4 @@ test_expect_success "fetch with $VERSION_B" '
test_cmp expect actual
'
-stop_git_daemon
test_done
diff --git a/t/lib-bash.sh b/t/lib-bash.sh
index 2be955f..b0b6060 100644
--- a/t/lib-bash.sh
+++ b/t/lib-bash.sh
@@ -2,10 +2,12 @@
# to run under Bash; primarily intended for tests of the completion
# script.
-if test -n "$BASH" && test -z "$POSIXLY_CORRECT"; then
+if test -n "$BASH" && test -z "$POSIXLY_CORRECT"
+then
# we are in full-on bash mode
true
-elif type bash >/dev/null 2>&1; then
+elif type bash >/dev/null 2>&1
+then
# execute in full-on bash mode
unset POSIXLY_CORRECT
exec bash "$0" "$@"
diff --git a/t/lib-credential.sh b/t/lib-credential.sh
index bb88cc0..dea2cbe 100755..100644
--- a/t/lib-credential.sh
+++ b/t/lib-credential.sh
@@ -1,4 +1,5 @@
-#!/bin/sh
+# Shell library for testing credential handling including helpers. See t0302
+# for an example of testing a specific helper.
# Try a set of credential helpers; the expected stdin,
# stdout and stderr should be provided on stdin,
diff --git a/t/lib-git-daemon.sh b/t/lib-git-daemon.sh
index f98de95..e625692 100644
--- a/t/lib-git-daemon.sh
+++ b/t/lib-git-daemon.sh
@@ -13,11 +13,9 @@
#
# test_expect_success ...
#
-# stop_git_daemon
# test_done
-test_tristate GIT_TEST_GIT_DAEMON
-if test "$GIT_TEST_GIT_DAEMON" = false
+if ! test_bool_env GIT_TEST_GIT_DAEMON true
then
skip_all="git-daemon testing disabled (unset GIT_TEST_GIT_DAEMON to enable)"
test_done
@@ -25,16 +23,18 @@ fi
if test_have_prereq !PIPE
then
- test_skip_or_die $GIT_TEST_GIT_DAEMON "file system does not support FIFOs"
+ test_skip_or_die GIT_TEST_GIT_DAEMON "file system does not support FIFOs"
fi
-LIB_GIT_DAEMON_PORT=${LIB_GIT_DAEMON_PORT-${this_test#t}}
+test_set_port LIB_GIT_DAEMON_PORT
GIT_DAEMON_PID=
+GIT_DAEMON_PIDFILE="$PWD"/daemon.pid
GIT_DAEMON_DOCUMENT_ROOT_PATH="$PWD"/repo
GIT_DAEMON_HOST_PORT=127.0.0.1:$LIB_GIT_DAEMON_PORT
GIT_DAEMON_URL=git://$GIT_DAEMON_HOST_PORT
+registered_stop_git_daemon_atexit_handler=
start_git_daemon() {
if test -n "$GIT_DAEMON_PID"
then
@@ -43,38 +43,36 @@ start_git_daemon() {
mkdir -p "$GIT_DAEMON_DOCUMENT_ROOT_PATH"
- trap 'code=$?; stop_git_daemon; (exit $code); die' EXIT
+ # One of the test scripts stops and then re-starts 'git daemon'.
+ # Don't register and then run the same atexit handlers several times.
+ if test -z "$registered_stop_git_daemon_atexit_handler"
+ then
+ test_atexit 'stop_git_daemon'
+ registered_stop_git_daemon_atexit_handler=AlreadyDone
+ fi
say >&3 "Starting git daemon ..."
mkfifo git_daemon_output
${LIB_GIT_DAEMON_COMMAND:-git daemon} \
--listen=127.0.0.1 --port="$LIB_GIT_DAEMON_PORT" \
- --reuseaddr --verbose \
+ --reuseaddr --verbose --pid-file="$GIT_DAEMON_PIDFILE" \
--base-path="$GIT_DAEMON_DOCUMENT_ROOT_PATH" \
"$@" "$GIT_DAEMON_DOCUMENT_ROOT_PATH" \
>&3 2>git_daemon_output &
GIT_DAEMON_PID=$!
- >daemon.log
{
read -r line <&7
- printf "%s\n" "$line"
- printf >&4 "%s\n" "$line"
- (
- while read -r line <&7
- do
- printf "%s\n" "$line"
- printf >&4 "%s\n" "$line"
- done
- ) &
- } 7<git_daemon_output >>"$TRASH_DIRECTORY/daemon.log" &&
+ printf "%s\n" "$line" >&4
+ cat <&7 >&4 &
+ } 7<git_daemon_output &&
# Check expected output
if test x"$(expr "$line" : "\[[0-9]*\] \(.*\)")" != x"Ready to rumble"
then
kill "$GIT_DAEMON_PID"
wait "$GIT_DAEMON_PID"
- trap 'die' EXIT
- test_skip_or_die $GIT_TEST_GIT_DAEMON \
+ unset GIT_DAEMON_PID
+ test_skip_or_die GIT_TEST_GIT_DAEMON \
"git daemon failed to start"
fi
}
@@ -85,8 +83,6 @@ stop_git_daemon() {
return
fi
- trap 'die' EXIT
-
# kill git-daemon child of git
say >&3 "Stopping git daemon ..."
kill "$GIT_DAEMON_PID"
@@ -96,8 +92,9 @@ stop_git_daemon() {
then
error "git daemon exited with status: $ret"
fi
+ kill "$(cat "$GIT_DAEMON_PIDFILE")" 2>/dev/null
GIT_DAEMON_PID=
- rm -f git_daemon_output
+ rm -f git_daemon_output "$GIT_DAEMON_PIDFILE"
}
# A stripped-down version of a netcat client, that connects to a "host:port"
diff --git a/t/lib-git-p4.sh b/t/lib-git-p4.sh
index c275994..5aff2ab 100644
--- a/t/lib-git-p4.sh
+++ b/t/lib-git-p4.sh
@@ -44,23 +44,7 @@ native_path () {
echo "$path"
}
-# On Solaris the 'date +%s' function is not supported and therefore we
-# need this replacement.
-# Attention: This function is not safe again against time offset updates
-# at runtime (e.g. via NTP). The 'clock_gettime(CLOCK_MONOTONIC)'
-# function could fix that but it is not in Python until 3.3.
-time_in_seconds () {
- (cd / && "$PYTHON_PATH" -c 'import time; print(int(time.time()))')
-}
-
-# Try to pick a unique port: guess a large number, then hope
-# no more than one of each test is running.
-#
-# This does not handle the case where somebody else is running the
-# same tests and has chosen the same ports.
-testid=${this_test#t}
-git_p4_test_start=9800
-P4DPORT=$((10669 + ($testid - $git_p4_test_start)))
+test_set_port P4DPORT
P4PORT=localhost:$P4DPORT
P4CLIENT=client
@@ -74,14 +58,9 @@ cli="$TRASH_DIRECTORY/cli"
git="$TRASH_DIRECTORY/git"
pidfile="$TRASH_DIRECTORY/p4d.pid"
-# Sometimes "prove" seems to hang on exit because p4d is still running
-cleanup () {
- if test -f "$pidfile"
- then
- kill -9 $(cat "$pidfile") 2>/dev/null && exit 255
- fi
+stop_p4d_and_watchdog () {
+ kill -9 $p4d_pid $watchdog_pid
}
-trap cleanup EXIT
# git p4 submit generates a temp file, which will
# not get cleaned up if the submission fails. Don't
@@ -89,7 +68,16 @@ trap cleanup EXIT
TMPDIR="$TRASH_DIRECTORY"
export TMPDIR
+registered_stop_p4d_atexit_handler=
start_p4d () {
+ # One of the test scripts stops and then re-starts p4d.
+ # Don't register and then run the same atexit handlers several times.
+ if test -z "$registered_stop_p4d_atexit_handler"
+ then
+ test_atexit 'stop_p4d_and_watchdog'
+ registered_stop_p4d_atexit_handler=AlreadyDone
+ fi
+
mkdir -p "$db" "$cli" "$git" &&
rm -f "$pidfile" &&
(
@@ -99,6 +87,7 @@ start_p4d () {
echo $! >"$pidfile"
}
) &&
+ p4d_pid=$(cat "$pidfile")
# This gives p4d a long time to start up, as it can be
# quite slow depending on the machine. Set this environment
@@ -106,18 +95,18 @@ start_p4d () {
# an automated test setup. If the p4d process dies, that
# will be caught with the "kill -0" check below.
i=${P4D_START_PATIENCE:-300}
- pid=$(cat "$pidfile")
- timeout=$(($(time_in_seconds) + $P4D_TIMEOUT))
+ nr_tries_left=$P4D_TIMEOUT
while true
do
- if test $(time_in_seconds) -gt $timeout
+ if test $nr_tries_left -eq 0
then
- kill -9 $pid
+ kill -9 $p4d_pid
exit 1
fi
sleep 1
- done &
+ nr_tries_left=$(($nr_tries_left - 1))
+ done 2>/dev/null 4>&2 &
watchdog_pid=$!
ready=
@@ -130,7 +119,7 @@ start_p4d () {
break
fi
# fail if p4d died
- kill -0 $pid 2>/dev/null || break
+ kill -0 $p4d_pid 2>/dev/null || break
echo waiting for p4d to start
sleep 1
i=$(( $i - 1 ))
@@ -170,34 +159,23 @@ p4_add_job () {
}
retry_until_success () {
- timeout=$(($(time_in_seconds) + $RETRY_TIMEOUT))
- until "$@" 2>/dev/null || test $(time_in_seconds) -gt $timeout
- do
- sleep 1
- done
-}
-
-retry_until_fail () {
- timeout=$(($(time_in_seconds) + $RETRY_TIMEOUT))
- until ! "$@" 2>/dev/null || test $(time_in_seconds) -gt $timeout
+ nr_tries_left=$RETRY_TIMEOUT
+ until "$@" 2>/dev/null || test $nr_tries_left -eq 0
do
sleep 1
+ nr_tries_left=$(($nr_tries_left - 1))
done
}
-kill_p4d () {
- pid=$(cat "$pidfile")
- retry_until_fail kill $pid
- retry_until_fail kill -9 $pid
- # complain if it would not die
- test_must_fail kill $pid >/dev/null 2>&1 &&
- rm -rf "$db" "$cli" "$pidfile" &&
- retry_until_fail kill -9 $watchdog_pid
+stop_and_cleanup_p4d () {
+ kill -9 $p4d_pid $watchdog_pid
+ wait $p4d_pid
+ rm -rf "$db" "$cli" "$pidfile"
}
cleanup_git () {
retry_until_success rm -r "$git"
- test_must_fail test -d "$git" &&
+ test_path_is_missing "$git" &&
retry_until_success mkdir "$git"
}
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index a8130f9..7d248e6 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -13,6 +13,7 @@ fi
GIT_DIR=$PWD/.git
GIT_SVN_DIR=$GIT_DIR/svn/refs/remotes/git-svn
SVN_TREE=$GIT_SVN_DIR/svn-tree
+test_set_port SVNSERVE_PORT
svn >/dev/null 2>&1
if test $? -ne 1
@@ -68,19 +69,12 @@ svn_cmd () {
maybe_start_httpd () {
loc=${1-svn}
- test_tristate GIT_SVN_TEST_HTTPD
- case $GIT_SVN_TEST_HTTPD in
- true)
+ if test_bool_env GIT_TEST_SVN_HTTPD false
+ then
. "$TEST_DIRECTORY"/lib-httpd.sh
LIB_HTTPD_SVN="$loc"
start_httpd
- ;;
- *)
- stop_httpd () {
- : noop
- }
- ;;
- esac
+ fi
}
convert_to_rev_db () {
@@ -110,8 +104,7 @@ EOF
}
require_svnserve () {
- test_tristate GIT_TEST_SVNSERVE
- if ! test "$GIT_TEST_SVNSERVE" = true
+ if ! test_bool_env GIT_TEST_SVNSERVE false
then
skip_all='skipping svnserve test. (set $GIT_TEST_SVNSERVE to enable)'
test_done
@@ -119,7 +112,6 @@ require_svnserve () {
}
start_svnserve () {
- SVNSERVE_PORT=${SVNSERVE_PORT-${this_test#t}}
svnserve --listen-port $SVNSERVE_PORT \
--root "$rawsvnrepo" \
--listen-once \
diff --git a/t/lib-gpg.sh b/t/lib-gpg.sh
index f1277be..9fc5241 100755..100644
--- a/t/lib-gpg.sh
+++ b/t/lib-gpg.sh
@@ -1,14 +1,25 @@
-#!/bin/sh
+# We always set GNUPGHOME, even if no usable GPG was found, as
+#
+# - It does not hurt, and
+#
+# - we cannot set global environment variables in lazy prereqs because they are
+# executed in an eval'ed subshell that changes the working directory to a
+# temporary one.
+
+GNUPGHOME="$PWD/gpghome"
+export GNUPGHOME
+
+test_lazy_prereq GPG '
+ gpg_version=$(gpg --version 2>&1)
+ test $? != 127 || exit 1
-gpg_version=$(gpg --version 2>&1)
-if test $? != 127
-then
# As said here: http://www.gnupg.org/documentation/faqs.html#q6.19
- # the gpg version 1.0.6 didn't parse trust packets correctly, so for
+ # the gpg version 1.0.6 did not parse trust packets correctly, so for
# that version, creation of signed tags using the generated key fails.
case "$gpg_version" in
- 'gpg (GnuPG) 1.0.6'*)
+ "gpg (GnuPG) 1.0.6"*)
say "Your version of gpg (1.0.6) is too buggy for testing"
+ exit 1
;;
*)
# Available key info:
@@ -27,56 +38,54 @@ then
# To export ownertrust:
# gpg --homedir /tmp/gpghome --export-ownertrust \
# > lib-gpg/ownertrust
- mkdir ./gpghome &&
- chmod 0700 ./gpghome &&
- GNUPGHOME="$(pwd)/gpghome" &&
- export GNUPGHOME &&
- (gpgconf --kill gpg-agent >/dev/null 2>&1 || : ) &&
- gpg --homedir "${GNUPGHOME}" 2>/dev/null --import \
+ mkdir "$GNUPGHOME" &&
+ chmod 0700 "$GNUPGHOME" &&
+ (gpgconf --kill gpg-agent || : ) &&
+ gpg --homedir "${GNUPGHOME}" --import \
"$TEST_DIRECTORY"/lib-gpg/keyring.gpg &&
- gpg --homedir "${GNUPGHOME}" 2>/dev/null --import-ownertrust \
+ gpg --homedir "${GNUPGHOME}" --import-ownertrust \
"$TEST_DIRECTORY"/lib-gpg/ownertrust &&
- gpg --homedir "${GNUPGHOME}" </dev/null >/dev/null 2>&1 \
- --sign -u committer@example.com &&
- test_set_prereq GPG &&
- # Available key info:
- # * see t/lib-gpg/gpgsm-gen-key.in
- # To generate new certificate:
- # * no passphrase
- # gpgsm --homedir /tmp/gpghome/ \
- # -o /tmp/gpgsm.crt.user \
- # --generate-key \
- # --batch t/lib-gpg/gpgsm-gen-key.in
- # To import certificate:
- # gpgsm --homedir /tmp/gpghome/ \
- # --import /tmp/gpgsm.crt.user
- # To export into a .p12 we can later import:
- # gpgsm --homedir /tmp/gpghome/ \
- # -o t/lib-gpg/gpgsm_cert.p12 \
- # --export-secret-key-p12 "committer@example.com"
- echo | gpgsm --homedir "${GNUPGHOME}" 2>/dev/null \
- --passphrase-fd 0 --pinentry-mode loopback \
- --import "$TEST_DIRECTORY"/lib-gpg/gpgsm_cert.p12 &&
-
- gpgsm --homedir "${GNUPGHOME}" 2>/dev/null -K |
- grep fingerprint: |
- cut -d" " -f4 |
- tr -d '\n' >"${GNUPGHOME}/trustlist.txt" &&
-
- echo " S relax" >> ${GNUPGHOME}/trustlist.txt &&
- (gpgconf --kill gpg-agent >/dev/null 2>&1 || : ) &&
- echo hello | gpgsm --homedir "${GNUPGHOME}" >/dev/null \
- -u committer@example.com -o /dev/null --sign - 2>&1 &&
- test_set_prereq GPGSM
+ gpg --homedir "${GNUPGHOME}" </dev/null >/dev/null \
+ --sign -u committer@example.com
;;
esac
-fi
+'
+
+test_lazy_prereq GPGSM '
+ test_have_prereq GPG &&
+ # Available key info:
+ # * see t/lib-gpg/gpgsm-gen-key.in
+ # To generate new certificate:
+ # * no passphrase
+ # gpgsm --homedir /tmp/gpghome/ \
+ # -o /tmp/gpgsm.crt.user \
+ # --generate-key \
+ # --batch t/lib-gpg/gpgsm-gen-key.in
+ # To import certificate:
+ # gpgsm --homedir /tmp/gpghome/ \
+ # --import /tmp/gpgsm.crt.user
+ # To export into a .p12 we can later import:
+ # gpgsm --homedir /tmp/gpghome/ \
+ # -o t/lib-gpg/gpgsm_cert.p12 \
+ # --export-secret-key-p12 "committer@example.com"
+ echo | gpgsm --homedir "${GNUPGHOME}" \
+ --passphrase-fd 0 --pinentry-mode loopback \
+ --import "$TEST_DIRECTORY"/lib-gpg/gpgsm_cert.p12 &&
+
+ gpgsm --homedir "${GNUPGHOME}" -K |
+ grep fingerprint: |
+ cut -d" " -f4 |
+ tr -d "\\n" >"${GNUPGHOME}/trustlist.txt" &&
+
+ echo " S relax" >>"${GNUPGHOME}/trustlist.txt" &&
+ echo hello | gpgsm --homedir "${GNUPGHOME}" >/dev/null \
+ -u committer@example.com -o /dev/null --sign -
+'
-if test_have_prereq GPG &&
- echo | gpg --homedir "${GNUPGHOME}" -b --rfc1991 >/dev/null 2>&1
-then
- test_set_prereq RFC1991
-fi
+test_lazy_prereq RFC1991 '
+ test_have_prereq GPG &&
+ echo | gpg --homedir "${GNUPGHOME}" -b --rfc1991 >/dev/null
+'
sanitize_pgp() {
perl -ne '
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index a8729f8..1449ee9 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -14,7 +14,6 @@
#
# test_expect_success ...
#
-# stop_httpd
# test_done
#
# Can be configured using the following variables.
@@ -42,15 +41,14 @@ then
test_done
fi
-test_tristate GIT_TEST_HTTPD
-if test "$GIT_TEST_HTTPD" = false
+if ! test_bool_env GIT_TEST_HTTPD true
then
skip_all="Network testing disabled (unset GIT_TEST_HTTPD to enable)"
test_done
fi
if ! test_have_prereq NOT_ROOT; then
- test_skip_or_die $GIT_TEST_HTTPD \
+ test_skip_or_die GIT_TEST_HTTPD \
"Cannot run httpd tests as root"
fi
@@ -82,7 +80,7 @@ case $(uname) in
esac
LIB_HTTPD_PATH=${LIB_HTTPD_PATH-"$DEFAULT_HTTPD_PATH"}
-LIB_HTTPD_PORT=${LIB_HTTPD_PORT-${this_test#t}}
+test_set_port LIB_HTTPD_PORT
TEST_PATH="$TEST_DIRECTORY"/lib-httpd
HTTPD_ROOT_PATH="$PWD"/httpd
@@ -91,11 +89,12 @@ HTTPD_DOCUMENT_ROOT_PATH=$HTTPD_ROOT_PATH/www
# hack to suppress apache PassEnv warnings
GIT_VALGRIND=$GIT_VALGRIND; export GIT_VALGRIND
GIT_VALGRIND_OPTIONS=$GIT_VALGRIND_OPTIONS; export GIT_VALGRIND_OPTIONS
+GIT_TEST_SIDEBAND_ALL=$GIT_TEST_SIDEBAND_ALL; export GIT_TEST_SIDEBAND_ALL
GIT_TRACE=$GIT_TRACE; export GIT_TRACE
if ! test -x "$LIB_HTTPD_PATH"
then
- test_skip_or_die $GIT_TEST_HTTPD "no web server found at '$LIB_HTTPD_PATH'"
+ test_skip_or_die GIT_TEST_HTTPD "no web server found at '$LIB_HTTPD_PATH'"
fi
HTTPD_VERSION=$($LIB_HTTPD_PATH -v | \
@@ -107,19 +106,19 @@ then
then
if ! test $HTTPD_VERSION -ge 2
then
- test_skip_or_die $GIT_TEST_HTTPD \
+ test_skip_or_die GIT_TEST_HTTPD \
"at least Apache version 2 is required"
fi
if ! test -d "$DEFAULT_HTTPD_MODULE_PATH"
then
- test_skip_or_die $GIT_TEST_HTTPD \
+ test_skip_or_die GIT_TEST_HTTPD \
"Apache module directory not found"
fi
LIB_HTTPD_MODULE_PATH="$DEFAULT_HTTPD_MODULE_PATH"
fi
else
- test_skip_or_die $GIT_TEST_HTTPD \
+ test_skip_or_die GIT_TEST_HTTPD \
"Could not identify web server at '$LIB_HTTPD_PATH'"
fi
@@ -131,8 +130,9 @@ prepare_httpd() {
mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH"
cp "$TEST_PATH"/passwd "$HTTPD_ROOT_PATH"
install_script broken-smart-http.sh
+ install_script error-smart-http.sh
install_script error.sh
- install_script apply-one-time-sed.sh
+ install_script apply-one-time-perl.sh
ln -s "$LIB_HTTPD_MODULE_PATH" "$HTTPD_ROOT_PATH/modules"
@@ -174,7 +174,7 @@ prepare_httpd() {
start_httpd() {
prepare_httpd >&3 2>&4
- trap 'code=$?; stop_httpd; (exit $code); die' EXIT
+ test_atexit stop_httpd
"$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
-f "$TEST_PATH/apache.conf" $HTTPD_PARA \
@@ -182,15 +182,12 @@ start_httpd() {
>&3 2>&4
if test $? -ne 0
then
- trap 'die' EXIT
cat "$HTTPD_ROOT_PATH"/error.log >&4 2>/dev/null
- test_skip_or_die $GIT_TEST_HTTPD "web server setup failed"
+ test_skip_or_die GIT_TEST_HTTPD "web server setup failed"
fi
}
stop_httpd() {
- trap 'die' EXIT
-
"$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
-f "$TEST_PATH/apache.conf" $HTTPD_PARA -k stop
}
diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf
index 581c010..994e529 100644
--- a/t/lib-httpd/apache.conf
+++ b/t/lib-httpd/apache.conf
@@ -76,8 +76,10 @@ PassEnv GIT_VALGRIND
PassEnv GIT_VALGRIND_OPTIONS
PassEnv GNUPGHOME
PassEnv ASAN_OPTIONS
+PassEnv LSAN_OPTIONS
PassEnv GIT_TRACE
PassEnv GIT_CONFIG_NOSYSTEM
+PassEnv GIT_TEST_SIDEBAND_ALL
SetEnvIf Git-Protocol ".*" GIT_PROTOCOL=$0
@@ -111,24 +113,29 @@ Alias /auth/dumb/ www/auth/dumb/
SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
SetEnv GIT_HTTP_EXPORT_ALL
</LocationMatch>
-<LocationMatch /one_time_sed/>
+<LocationMatch /one_time_perl/>
SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
SetEnv GIT_HTTP_EXPORT_ALL
</LocationMatch>
+ScriptAliasMatch /error_git_upload_pack/(.*)/git-upload-pack error.sh/
ScriptAliasMatch /smart_*[^/]*/(.*) ${GIT_EXEC_PATH}/git-http-backend/$1
ScriptAlias /broken_smart/ broken-smart-http.sh/
+ScriptAlias /error_smart/ error-smart-http.sh/
ScriptAlias /error/ error.sh/
-ScriptAliasMatch /one_time_sed/(.*) apply-one-time-sed.sh/$1
+ScriptAliasMatch /one_time_perl/(.*) apply-one-time-perl.sh/$1
<Directory ${GIT_EXEC_PATH}>
Options FollowSymlinks
</Directory>
<Files broken-smart-http.sh>
Options ExecCGI
</Files>
+<Files error-smart-http.sh>
+ Options ExecCGI
+</Files>
<Files error.sh>
Options ExecCGI
</Files>
-<Files apply-one-time-sed.sh>
+<Files apply-one-time-perl.sh>
Options ExecCGI
</Files>
<Files ${GIT_EXEC_PATH}/git-http-backend>
diff --git a/t/lib-httpd/apply-one-time-perl.sh b/t/lib-httpd/apply-one-time-perl.sh
new file mode 100644
index 0000000..09a0abd
--- /dev/null
+++ b/t/lib-httpd/apply-one-time-perl.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+# If "one-time-perl" exists in $HTTPD_ROOT_PATH, run perl on the HTTP response,
+# using the contents of "one-time-perl" as the perl command to be run. If the
+# response was modified as a result, delete "one-time-perl" so that subsequent
+# HTTP responses are no longer modified.
+#
+# This can be used to simulate the effects of the repository changing in
+# between HTTP request-response pairs.
+if test -f one-time-perl
+then
+ LC_ALL=C
+ export LC_ALL
+
+ "$GIT_EXEC_PATH/git-http-backend" >out
+ perl -pe "$(cat one-time-perl)" out >out_modified
+
+ if cmp -s out out_modified
+ then
+ cat out
+ else
+ cat out_modified
+ rm one-time-perl
+ fi
+else
+ "$GIT_EXEC_PATH/git-http-backend"
+fi
diff --git a/t/lib-httpd/apply-one-time-sed.sh b/t/lib-httpd/apply-one-time-sed.sh
deleted file mode 100644
index fcef728..0000000
--- a/t/lib-httpd/apply-one-time-sed.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/sh
-
-# If "one-time-sed" exists in $HTTPD_ROOT_PATH, run sed on the HTTP response,
-# using the contents of "one-time-sed" as the sed command to be run. If the
-# response was modified as a result, delete "one-time-sed" so that subsequent
-# HTTP responses are no longer modified.
-#
-# This can be used to simulate the effects of the repository changing in
-# between HTTP request-response pairs.
-if [ -e one-time-sed ]; then
- "$GIT_EXEC_PATH/git-http-backend" >out
- sed "$(cat one-time-sed)" <out >out_modified
-
- if diff out out_modified >/dev/null; then
- cat out
- else
- cat out_modified
- rm one-time-sed
- fi
-else
- "$GIT_EXEC_PATH/git-http-backend"
-fi
diff --git a/t/lib-httpd/error-smart-http.sh b/t/lib-httpd/error-smart-http.sh
new file mode 100644
index 0000000..e65d447
--- /dev/null
+++ b/t/lib-httpd/error-smart-http.sh
@@ -0,0 +1,3 @@
+echo "Content-Type: application/x-git-upload-pack-advertisement"
+echo
+printf "%s" "0019ERR server-side error"
diff --git a/t/lib-log-graph.sh b/t/lib-log-graph.sh
new file mode 100644
index 0000000..1184cce
--- /dev/null
+++ b/t/lib-log-graph.sh
@@ -0,0 +1,28 @@
+# Helps shared by the test scripts for comparing log graphs.
+
+sanitize_log_output () {
+ sed -e 's/ *$//' \
+ -e 's/commit [0-9a-f]*$/commit COMMIT_OBJECT_NAME/' \
+ -e 's/Merge: [ 0-9a-f]*$/Merge: MERGE_PARENTS/' \
+ -e 's/Merge tag.*/Merge HEADS DESCRIPTION/' \
+ -e 's/Merge commit.*/Merge HEADS DESCRIPTION/' \
+ -e 's/index [0-9a-f]*\.\.[0-9a-f]*/index BEFORE..AFTER/'
+}
+
+lib_test_cmp_graph () {
+ git log --graph "$@" >output &&
+ sed 's/ *$//' >output.sanitized <output &&
+ test_i18ncmp expect output.sanitized
+}
+
+lib_test_cmp_short_graph () {
+ git log --graph --pretty=short "$@" >output &&
+ sanitize_log_output >output.sanitized <output &&
+ test_i18ncmp expect output.sanitized
+}
+
+lib_test_cmp_colored_graph () {
+ git log --graph --color=always "$@" >output.colors.raw &&
+ test_decode_color <output.colors.raw | sed "s/ *\$//" >output.colors &&
+ test_cmp expect.colors output.colors
+}
diff --git a/t/lib-pack.sh b/t/lib-pack.sh
index c4d907a..f346317 100644
--- a/t/lib-pack.sh
+++ b/t/lib-pack.sh
@@ -35,9 +35,11 @@ pack_header () {
# have hardcoded some well-known objects. See the case statements below for the
# complete list.
pack_obj () {
+ test_oid_init
+
case "$1" in
# empty blob
- e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)
+ $EMPTY_BLOB)
case "$2" in
'')
printf '\060\170\234\003\0\0\0\0\1'
@@ -47,7 +49,7 @@ pack_obj () {
;;
# blob containing "\7\76"
- e68fe8129b546b101aee9510c5328e7f21ca1d18)
+ $(test_oid packlib_7_76))
case "$2" in
'')
printf '\062\170\234\143\267\3\0\0\116\0\106'
@@ -59,11 +61,18 @@ pack_obj () {
printf '\234\143\142\142\142\267\003\0\0\151\0\114'
return
;;
+ 37c8e2c15bb22b912e59b43fd51a4f7e9465ed0b5084c5a1411d991cbe630683)
+ printf '\165\67\310\342\301\133\262\53\221\56\131' &&
+ printf '\264\77\325\32\117\176\224\145\355\13\120' &&
+ printf '\204\305\241\101\35\231\34\276\143\6\203\170' &&
+ printf '\234\143\142\142\142\267\003\0\0\151\0\114'
+ return
+ ;;
esac
;;
# blob containing "\7\0"
- 01d7713666f4de822776c7622c10f1b07de280dc)
+ $(test_oid packlib_7_0))
case "$2" in
'')
printf '\062\170\234\143\147\0\0\0\20\0\10'
@@ -75,6 +84,13 @@ pack_obj () {
printf '\143\142\142\142\147\0\0\0\53\0\16'
return
;;
+ 5d8e6fc40f2dab00e6983a48523fe57e621f46434cb58dbd4422fba03380d886)
+ printf '\165\135\216\157\304\17\55\253\0\346\230\72' &&
+ printf '\110\122\77\345\176\142\37\106\103\114\265' &&
+ printf '\215\275\104\42\373\240\63\200\330\206\170\234' &&
+ printf '\143\142\142\142\147\0\0\0\53\0\16'
+ return
+ ;;
esac
;;
esac
@@ -86,7 +102,7 @@ pack_obj () {
then
echo "$1" | git pack-objects --stdout >pack_obj.tmp &&
size=$(wc -c <pack_obj.tmp) &&
- dd if=pack_obj.tmp bs=1 count=$((size - 20 - 12)) skip=12 &&
+ dd if=pack_obj.tmp bs=1 count=$((size - $(test_oid rawsz) - 12)) skip=12 &&
rm -f pack_obj.tmp
return
fi
@@ -97,7 +113,8 @@ pack_obj () {
# Compute and append pack trailer to "$1"
pack_trailer () {
- test-tool sha1 -b <"$1" >trailer.tmp &&
+ test_oid_init &&
+ test-tool $(test_oid algo) -b <"$1" >trailer.tmp &&
cat trailer.tmp >>"$1" &&
rm -f trailer.tmp
}
@@ -108,3 +125,11 @@ pack_trailer () {
clear_packs () {
rm -f .git/objects/pack/*
}
+
+test_oid_cache <<-EOF
+packlib_7_0 sha1:01d7713666f4de822776c7622c10f1b07de280dc
+packlib_7_0 sha256:37c8e2c15bb22b912e59b43fd51a4f7e9465ed0b5084c5a1411d991cbe630683
+
+packlib_7_76 sha1:e68fe8129b546b101aee9510c5328e7f21ca1d18
+packlib_7_76 sha256:5d8e6fc40f2dab00e6983a48523fe57e621f46434cb58dbd4422fba03380d886
+EOF
diff --git a/t/lib-patch-mode.sh b/t/lib-patch-mode.sh
index 06c3c91..cfd76bf 100644
--- a/t/lib-patch-mode.sh
+++ b/t/lib-patch-mode.sh
@@ -2,28 +2,40 @@
. ./test-lib.sh
+# set_state <path> <worktree-content> <index-content>
+#
+# Prepare the content for path in worktree and the index as specified.
set_state () {
echo "$3" > "$1" &&
git add "$1" &&
echo "$2" > "$1"
}
+# save_state <path>
+#
+# Save index/worktree content of <path> in the files _worktree_<path>
+# and _index_<path>
save_state () {
noslash="$(echo "$1" | tr / _)" &&
cat "$1" > _worktree_"$noslash" &&
git show :"$1" > _index_"$noslash"
}
+# set_and_save_state <path> <worktree-content> <index-content>
set_and_save_state () {
set_state "$@" &&
save_state "$1"
}
+# verify_state <path> <expected-worktree-content> <expected-index-content>
verify_state () {
test "$(cat "$1")" = "$2" &&
test "$(git show :"$1")" = "$3"
}
+# verify_saved_state <path>
+#
+# Call verify_state with expected contents from the last save_state
verify_saved_state () {
noslash="$(echo "$1" | tr / _)" &&
verify_state "$1" "$(cat _worktree_"$noslash")" "$(cat _index_"$noslash")"
diff --git a/t/lib-rebase.sh b/t/lib-rebase.sh
index 7ea30e5..b72c051 100644
--- a/t/lib-rebase.sh
+++ b/t/lib-rebase.sh
@@ -44,10 +44,10 @@ set_fake_editor () {
rm -f "$1"
echo 'rebase -i script before editing:'
cat "$1".tmp
- action=pick
+ action=\&
for line in $FAKE_LINES; do
case $line in
- pick|p|squash|s|fixup|f|edit|e|reword|r|drop|d)
+ pick|p|squash|s|fixup|f|edit|e|reword|r|drop|d|label|l|reset|r|merge|m)
action="$line";;
exec_*|x_*|break|b)
echo "$line" | sed 's/_/ /g' >> "$1";;
@@ -58,11 +58,12 @@ set_fake_editor () {
bad)
action="badcmd";;
fakesha)
+ test \& != "$action" || action=pick
echo "$action XXXXXXX False commit" >> "$1"
action=pick;;
*)
- sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
- action=pick;;
+ sed -n "${line}s/^[a-z][a-z]*/$action/p" < "$1".tmp >> "$1"
+ action=\&;;
esac
done
echo 'rebase -i script after editing:'
@@ -118,3 +119,31 @@ make_empty () {
git commit --allow-empty -m "$1" &&
git tag "$1"
}
+
+# Call this (inside test_expect_success) at the end of a test file to
+# check that no tests have changed editor related environment
+# variables or config settings
+test_editor_unchanged () {
+ # We're only interested in exported variables hence 'sh -c'
+ sh -c 'cat >actual <<-EOF
+ EDITOR=$EDITOR
+ FAKE_COMMIT_AMEND=$FAKE_COMMIT_AMEND
+ FAKE_COMMIT_MESSAGE=$FAKE_COMMIT_MESSAGE
+ FAKE_LINES=$FAKE_LINES
+ GIT_EDITOR=$GIT_EDITOR
+ GIT_SEQUENCE_EDITOR=$GIT_SEQUENCE_EDITOR
+ core.editor=$(git config core.editor)
+ sequence.editor=$(git config sequence.editor)
+ EOF'
+ cat >expect <<-\EOF
+ EDITOR=:
+ FAKE_COMMIT_AMEND=
+ FAKE_COMMIT_MESSAGE=
+ FAKE_LINES=
+ GIT_EDITOR=
+ GIT_SEQUENCE_EDITOR=
+ core.editor=
+ sequence.editor=
+ EOF
+ test_cmp expect actual
+}
diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh
index 0163917..64fc648 100755..100644
--- a/t/lib-submodule-update.sh
+++ b/t/lib-submodule-update.sh
@@ -139,7 +139,7 @@ create_lib_submodule_repo () {
git revert HEAD &&
git checkout -b invalid_sub1 add_sub1 &&
- git update-index --cacheinfo 160000 0123456789012345678901234567890123456789 sub1 &&
+ git update-index --cacheinfo 160000 $(test_oid numeric) sub1 &&
git commit -m "Invalid sub1 commit" &&
git checkout -b valid_sub1 &&
git revert HEAD &&
@@ -196,6 +196,7 @@ test_git_directory_exists() {
# the submodule repo if it doesn't exist and configures the most problematic
# settings for diff.ignoreSubmodules.
prolog () {
+ test_oid_init &&
(test -d submodule_update_repo || create_lib_submodule_repo) &&
test_config_global diff.ignoreSubmodules all &&
test_config diff.ignoreSubmodules all
@@ -235,7 +236,7 @@ reset_work_tree_to_interested () {
then
mkdir -p submodule_update/.git/modules/sub1/modules &&
cp -r submodule_update_repo/.git/modules/sub1/modules/sub2 submodule_update/.git/modules/sub1/modules/sub2
- GIT_WORK_TREE=. git -C submodule_update/.git/modules/sub1/modules/sub2 config --unset core.worktree
+ # core.worktree is unset for sub2 as it is not checked out
fi &&
# indicate we are interested in the submodule:
git -C submodule_update config submodule.sub1.url "bogus" &&
@@ -296,7 +297,7 @@ test_submodule_content () {
# - Directory containing tracked files replaced by submodule
# - Submodule replaced by tracked files in directory
# - Submodule replaced by tracked file with the same name
-# - tracked file replaced by submodule
+# - Tracked file replaced by submodule
#
# The default is that submodule contents aren't changed until "git submodule
# update" is run. And even then that command doesn't delete the work tree of
@@ -620,11 +621,13 @@ test_submodule_forced_switch () {
# - Directory containing tracked files replaced by submodule
# - Submodule replaced by tracked files in directory
# - Submodule replaced by tracked file with the same name
-# - tracked file replaced by submodule
+# - Tracked file replaced by submodule
#
# New test cases
# - Removing a submodule with a git directory absorbs the submodules
# git directory first into the superproject.
+# - Switching from no submodule to nested submodules
+# - Switching from nested submodules to no submodule
# Internal function; use test_submodule_switch_recursing_with_args() or
# test_submodule_forced_switch_recursing_with_args() instead.
@@ -657,22 +660,6 @@ test_submodule_recursing_with_args_common() {
test_submodule_content sub1 origin/add_sub1
)
'
- test_expect_success "$command: submodule branch is not changed, detach HEAD instead" '
- prolog &&
- reset_work_tree_to_interested add_sub1 &&
- (
- cd submodule_update &&
- git -C sub1 checkout -b keep_branch &&
- git -C sub1 rev-parse HEAD >expect &&
- git branch -t modify_sub1 origin/modify_sub1 &&
- $command modify_sub1 &&
- test_superproject_content origin/modify_sub1 &&
- test_submodule_content sub1 origin/modify_sub1 &&
- git -C sub1 rev-parse keep_branch >actual &&
- test_cmp expect actual &&
- test_must_fail git -C sub1 symbolic-ref HEAD
- )
- '
# Replacing a tracked file with a submodule produces a checked out submodule
test_expect_success "$command: replace tracked file with submodule checks out submodule" '
@@ -698,6 +685,19 @@ test_submodule_recursing_with_args_common() {
test_submodule_content sub1 origin/replace_directory_with_sub1
)
'
+ # Switching to a commit with nested submodules recursively checks them out
+ test_expect_success "$command: nested submodules are checked out" '
+ prolog &&
+ reset_work_tree_to_interested no_submodule &&
+ (
+ cd submodule_update &&
+ git branch -t modify_sub1_recursively origin/modify_sub1_recursively &&
+ $command modify_sub1_recursively &&
+ test_superproject_content origin/modify_sub1_recursively &&
+ test_submodule_content sub1 origin/modify_sub1_recursively &&
+ test_submodule_content -C sub1 sub2 origin/modify_sub1_recursively
+ )
+ '
######################## Disappearing submodule #######################
# Removing a submodule removes its work tree ...
@@ -709,7 +709,8 @@ test_submodule_recursing_with_args_common() {
git branch -t remove_sub1 origin/remove_sub1 &&
$command remove_sub1 &&
test_superproject_content origin/remove_sub1 &&
- ! test -e sub1
+ ! test -e sub1 &&
+ test_must_fail git config -f .git/modules/sub1/config core.worktree
)
'
# ... absorbing a .git directory along the way.
@@ -760,6 +761,21 @@ test_submodule_recursing_with_args_common() {
)
'
+ # Switching to a commit without nested submodules removes their worktrees
+ test_expect_success "$command: worktrees of nested submodules are removed" '
+ prolog &&
+ reset_work_tree_to_interested add_nested_sub &&
+ (
+ cd submodule_update &&
+ git branch -t no_submodule origin/no_submodule &&
+ $command no_submodule &&
+ test_superproject_content origin/no_submodule &&
+ ! test_path_is_dir sub1 &&
+ test_must_fail git config -f .git/modules/sub1/config core.worktree &&
+ test_must_fail git config -f .git/modules/sub1/modules/sub2/config core.worktree
+ )
+ '
+
########################## Modified submodule #########################
# Updating a submodule sha1 updates the submodule's work tree
test_expect_success "$command: modified submodule updates submodule work tree" '
@@ -787,6 +803,23 @@ test_submodule_recursing_with_args_common() {
test_submodule_content sub1 origin/add_sub1
)
'
+ # Updating a submodule does not touch the currently checked out branch in the submodule
+ test_expect_success "$command: submodule branch is not changed, detach HEAD instead" '
+ prolog &&
+ reset_work_tree_to_interested add_sub1 &&
+ (
+ cd submodule_update &&
+ git -C sub1 checkout -b keep_branch &&
+ git -C sub1 rev-parse HEAD >expect &&
+ git branch -t modify_sub1 origin/modify_sub1 &&
+ $command modify_sub1 &&
+ test_superproject_content origin/modify_sub1 &&
+ test_submodule_content sub1 origin/modify_sub1 &&
+ git -C sub1 rev-parse keep_branch >actual &&
+ test_cmp expect actual &&
+ test_must_fail git -C sub1 symbolic-ref HEAD
+ )
+ '
}
# Declares and invokes several tests that, in various situations, checks that
@@ -906,7 +939,6 @@ test_submodule_switch_recursing_with_args () {
)
'
- # recursing deeper than one level doesn't work yet.
test_expect_success "$command: modified submodule updates submodule recursively" '
prolog &&
reset_work_tree_to_interested add_nested_sub &&
diff --git a/t/oid-info/hash-info b/t/oid-info/hash-info
index ccdbfdf..d0736dd 100644
--- a/t/oid-info/hash-info
+++ b/t/oid-info/hash-info
@@ -6,3 +6,12 @@ hexsz sha256:64
zero sha1:0000000000000000000000000000000000000000
zero sha256:0000000000000000000000000000000000000000000000000000000000000000
+
+algo sha1:sha1
+algo sha256:sha256
+
+empty_blob sha1:e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
+empty_blob sha256:473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813
+
+empty_tree sha1:4b825dc642cb6eb9a060e54bf8d69288fbee4904
+empty_tree sha256:6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321
diff --git a/t/perf/README b/t/perf/README
index be12090..c7b70e2 100644
--- a/t/perf/README
+++ b/t/perf/README
@@ -45,7 +45,7 @@ call the aggregation script to summarize the results:
$ ./p0001-rev-list.sh
[...]
- $ GIT_BUILD_DIR=/path/to/other/git ./p0001-rev-list.sh
+ $ ./run /path/to/other/git -- ./p0001-rev-list.sh
[...]
$ ./aggregate.perl . /path/to/other/git ./p0001-rev-list.sh
diff --git a/t/perf/aggregate.perl b/t/perf/aggregate.perl
index 494907a..14e4cda 100755
--- a/t/perf/aggregate.perl
+++ b/t/perf/aggregate.perl
@@ -3,9 +3,8 @@
use lib '../../perl/build/lib';
use strict;
use warnings;
-use JSON;
use Getopt::Long;
-use Git;
+use Cwd qw(realpath);
sub get_times {
my $name = shift;
@@ -85,6 +84,11 @@ sub format_size {
return $out;
}
+sub sane_backticks {
+ open(my $fh, '-|', @_);
+ return <$fh>;
+}
+
my (@dirs, %dirnames, %dirabbrevs, %prefixes, @tests,
$codespeed, $sortby, $subsection, $reponame);
@@ -99,18 +103,22 @@ usage() unless $rc;
while (scalar @ARGV) {
my $arg = $ARGV[0];
my $dir;
+ my $prefix = '';
last if -f $arg or $arg eq "--";
if (! -d $arg) {
- my $rev = Git::command_oneline(qw(rev-parse --verify), $arg);
+ my $rev = sane_backticks(qw(git rev-parse --verify), $arg);
+ chomp $rev;
$dir = "build/".$rev;
+ } elsif ($arg eq '.') {
+ $dir = '.';
} else {
- $arg =~ s{/*$}{};
- $dir = $arg;
- $dirabbrevs{$dir} = $dir;
+ $dir = realpath($arg);
+ $dirnames{$dir} = $dir;
+ $prefix .= 'bindir';
}
push @dirs, $dir;
- $dirnames{$dir} = $arg;
- my $prefix = $dir;
+ $dirnames{$dir} ||= $arg;
+ $prefix .= $dir;
$prefix =~ tr/^a-zA-Z0-9/_/c;
$prefixes{$dir} = $prefix . '.';
shift @ARGV;
@@ -216,13 +224,7 @@ sub print_default_results {
for my $i (0..$#dirs) {
my $d = $dirs[$i];
my $base = "$resultsdir/$prefixes{$d}$t";
- $times{$prefixes{$d}.$t} = [];
- foreach my $type (qw(times size)) {
- if (-e "$base.$type") {
- $times{$prefixes{$d}.$t} = [get_times("$base.$type")];
- last;
- }
- }
+ $times{$prefixes{$d}.$t} = [get_times("$base.result")];
my ($r,$u,$s) = @{$times{$prefixes{$d}.$t}};
my $w = length format_times($r,$u,$s,$firstr);
$colwidth[$i] = $w if $w > $colwidth[$i];
@@ -264,7 +266,7 @@ sub print_sorted_results {
my ($prevr, $prevu, $prevs, $prevrev);
for my $i (0..$#dirs) {
my $d = $dirs[$i];
- my ($r, $u, $s) = get_times("$resultsdir/$prefixes{$d}$t.times");
+ my ($r, $u, $s) = get_times("$resultsdir/$prefixes{$d}$t.result");
if ($i > 0 and defined $r and defined $prevr and $prevr > 0) {
my $percent = 100.0 * ($r - $prevr) / $prevr;
push @evolutions, { "percent" => $percent,
@@ -312,9 +314,6 @@ sub print_codespeed_results {
$environment = $reponame;
} elsif (exists $ENV{GIT_PERF_REPO_NAME} and $ENV{GIT_PERF_REPO_NAME} ne "") {
$environment = $ENV{GIT_PERF_REPO_NAME};
- } elsif (exists $ENV{GIT_TEST_INSTALLED} and $ENV{GIT_TEST_INSTALLED} ne "") {
- $environment = $ENV{GIT_TEST_INSTALLED};
- $environment =~ s|/bin-wrappers$||;
} else {
$environment = `uname -r`;
chomp $environment;
@@ -327,7 +326,7 @@ sub print_codespeed_results {
my $commitid = $prefixes{$d};
$commitid =~ s/^build_//;
$commitid =~ s/\.$//;
- my ($result_value, $u, $s) = get_times("$resultsdir/$prefixes{$d}$t.times");
+ my ($result_value, $u, $s) = get_times("$resultsdir/$prefixes{$d}$t.result");
my %vals = (
"commitid" => $commitid,
@@ -342,7 +341,8 @@ sub print_codespeed_results {
}
}
- print to_json(\@data, {utf8 => 1, pretty => 1, canonical => 1}), "\n";
+ require JSON;
+ print JSON::to_json(\@data, {utf8 => 1, pretty => 1, canonical => 1}), "\n";
}
binmode STDOUT, ":utf8" or die "PANIC on binmode: $!";
diff --git a/t/perf/bisect_regression b/t/perf/bisect_regression
index a94d995..ce47e16 100755
--- a/t/perf/bisect_regression
+++ b/t/perf/bisect_regression
@@ -51,7 +51,7 @@ oldtime=$(echo "$oldtime" | sed -e 's/^\([0-9]\+\.[0-9]\+\).*$/\1/')
newtime=$(echo "$newtime" | sed -e 's/^\([0-9]\+\.[0-9]\+\).*$/\1/')
test $(echo "$newtime" "$oldtime" | awk '{ print ($1 > $2) }') = 1 ||
- die "New time '$newtime' shoud be greater than old time '$oldtime'"
+ die "New time '$newtime' should be greater than old time '$oldtime'"
tmpdir=$(mktemp -d -t bisect_regression_XXXXXX) || die "Failed to create temp directory"
echo "$oldtime" >"$tmpdir/oldtime" || die "Failed to write to '$tmpdir/oldtime'"
diff --git a/t/perf/p0001-rev-list.sh b/t/perf/p0001-rev-list.sh
index ebf1724..3042a85 100755
--- a/t/perf/p0001-rev-list.sh
+++ b/t/perf/p0001-rev-list.sh
@@ -14,6 +14,24 @@ test_perf 'rev-list --all --objects' '
git rev-list --all --objects >/dev/null
'
+test_perf 'rev-list --parents' '
+ git rev-list --parents HEAD >/dev/null
+'
+
+test_expect_success 'create dummy file' '
+ echo unlikely-to-already-be-there >dummy &&
+ git add dummy &&
+ git commit -m dummy
+'
+
+test_perf 'rev-list -- dummy' '
+ git rev-list HEAD -- dummy
+'
+
+test_perf 'rev-list --parents -- dummy' '
+ git rev-list --parents HEAD -- dummy
+'
+
test_expect_success 'create new unreferenced commit' '
commit=$(git commit-tree HEAD^{tree} -p HEAD) &&
test_export commit
diff --git a/t/perf/p5302-pack-index.sh b/t/perf/p5302-pack-index.sh
index 99bdb16..a9b3e11 100755
--- a/t/perf/p5302-pack-index.sh
+++ b/t/perf/p5302-pack-index.sh
@@ -13,35 +13,40 @@ test_expect_success 'repack' '
export PACK
'
-test_expect_success 'create target repositories' '
- for repo in t1 t2 t3 t4 t5 t6
- do
- git init --bare $repo
- done
-'
-
test_perf 'index-pack 0 threads' '
- GIT_DIR=t1 git index-pack --threads=1 --stdin < $PACK
+ rm -rf repo.git &&
+ git init --bare repo.git &&
+ GIT_DIR=repo.git git index-pack --threads=1 --stdin < $PACK
'
test_perf 'index-pack 1 thread ' '
- GIT_DIR=t2 GIT_FORCE_THREADS=1 git index-pack --threads=1 --stdin < $PACK
+ rm -rf repo.git &&
+ git init --bare repo.git &&
+ GIT_DIR=repo.git GIT_FORCE_THREADS=1 git index-pack --threads=1 --stdin < $PACK
'
test_perf 'index-pack 2 threads' '
- GIT_DIR=t3 git index-pack --threads=2 --stdin < $PACK
+ rm -rf repo.git &&
+ git init --bare repo.git &&
+ GIT_DIR=repo.git git index-pack --threads=2 --stdin < $PACK
'
test_perf 'index-pack 4 threads' '
- GIT_DIR=t4 git index-pack --threads=4 --stdin < $PACK
+ rm -rf repo.git &&
+ git init --bare repo.git &&
+ GIT_DIR=repo.git git index-pack --threads=4 --stdin < $PACK
'
test_perf 'index-pack 8 threads' '
- GIT_DIR=t5 git index-pack --threads=8 --stdin < $PACK
+ rm -rf repo.git &&
+ git init --bare repo.git &&
+ GIT_DIR=repo.git git index-pack --threads=8 --stdin < $PACK
'
test_perf 'index-pack default number of threads' '
- GIT_DIR=t6 git index-pack --stdin < $PACK
+ rm -rf repo.git &&
+ git init --bare repo.git &&
+ GIT_DIR=repo.git git index-pack --stdin < $PACK
'
test_done
diff --git a/t/perf/p5303-many-packs.sh b/t/perf/p5303-many-packs.sh
index 3779851..7ee7916 100755
--- a/t/perf/p5303-many-packs.sh
+++ b/t/perf/p5303-many-packs.sh
@@ -77,6 +77,7 @@ do
# actual pack generation, without smudging the on-disk setup
# between trials.
test_perf "repack ($nr_packs)" '
+ GIT_TEST_FULL_IN_PACK_ARRAY=1 \
git pack-objects --keep-true-parents \
--honor-pack-keep --non-empty --all \
--reflog --indexed-objects --delta-base-offset \
@@ -84,4 +85,22 @@ do
'
done
+# Measure pack loading with 10,000 packs.
+test_expect_success 'generate lots of packs' '
+ for i in $(test_seq 10000); do
+ echo "blob"
+ echo "data <<EOF"
+ echo "blob $i"
+ echo "EOF"
+ echo "checkpoint"
+ done |
+ git -c fastimport.unpackLimit=0 fast-import
+'
+
+# The purpose of this test is to evaluate load time for a large number
+# of packs while doing as little other work as possible.
+test_perf "load 10,000 packs" '
+ git rev-parse --verify "HEAD^{commit}"
+'
+
test_done
diff --git a/t/perf/p5304-prune.sh b/t/perf/p5304-prune.sh
new file mode 100755
index 0000000..83baedb
--- /dev/null
+++ b/t/perf/p5304-prune.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+test_description='performance tests of prune'
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+test_expect_success 'remove reachable loose objects' '
+ git repack -ad
+'
+
+test_expect_success 'remove unreachable loose objects' '
+ git prune
+'
+
+test_expect_success 'confirm there are no loose objects' '
+ git count-objects | grep ^0
+'
+
+test_perf 'prune with no objects' '
+ git prune
+'
+
+test_expect_success 'repack with bitmaps' '
+ git repack -adb
+'
+
+# We have to create the object in each trial run, since otherwise
+# runs after the first see no object and just skip the traversal entirely!
+test_perf 'prune with bitmaps' '
+ echo "probably not present in repo" | git hash-object -w --stdin &&
+ git prune
+'
+
+test_done
diff --git a/t/perf/p5310-pack-bitmaps.sh b/t/perf/p5310-pack-bitmaps.sh
index bb91dbb..80c53ed 100755
--- a/t/perf/p5310-pack-bitmaps.sh
+++ b/t/perf/p5310-pack-bitmaps.sh
@@ -12,8 +12,7 @@ test_perf_large_repo
# We intentionally use the deprecated pack.writebitmaps
# config so that we can test against older versions of git.
test_expect_success 'setup bitmap config' '
- git config pack.writebitmaps true &&
- git config pack.writebitmaphashcache true
+ git config pack.writebitmaps true
'
test_perf 'repack to disk' '
@@ -32,14 +31,32 @@ test_perf 'simulated fetch' '
} | git pack-objects --revs --stdout >/dev/null
'
-test_perf 'pack to file' '
- git pack-objects --all pack1 </dev/null >/dev/null
-'
-
test_perf 'pack to file (bitmap)' '
git pack-objects --use-bitmap-index --all pack1b </dev/null >/dev/null
'
+test_perf 'rev-list (commits)' '
+ git rev-list --all --use-bitmap-index >/dev/null
+'
+
+test_perf 'rev-list (objects)' '
+ git rev-list --all --use-bitmap-index --objects >/dev/null
+'
+
+test_perf 'rev-list count with blob:none' '
+ git rev-list --use-bitmap-index --count --objects --all \
+ --filter=blob:none >/dev/null
+'
+
+test_perf 'rev-list count with blob:limit=1k' '
+ git rev-list --use-bitmap-index --count --objects --all \
+ --filter=blob:limit=1k >/dev/null
+'
+
+test_perf 'simulated partial clone' '
+ git pack-objects --stdout --all --filter=blob:none </dev/null >/dev/null
+'
+
test_expect_success 'create partial bitmap state' '
# pick a commit to represent the repo tip in the past
cutoff=$(git rev-list HEAD~100 -1) &&
diff --git a/t/perf/p5311-pack-bitmaps-fetch.sh b/t/perf/p5311-pack-bitmaps-fetch.sh
index b045759..47c3fd7 100755
--- a/t/perf/p5311-pack-bitmaps-fetch.sh
+++ b/t/perf/p5311-pack-bitmaps-fetch.sh
@@ -7,7 +7,6 @@ test_perf_default_repo
test_expect_success 'create bitmapped server repo' '
git config pack.writebitmaps true &&
- git config pack.writebitmaphashcache true &&
git repack -ad
'
diff --git a/t/perf/p5600-partial-clone.sh b/t/perf/p5600-partial-clone.sh
new file mode 100755
index 0000000..3e04bd2
--- /dev/null
+++ b/t/perf/p5600-partial-clone.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+test_description='performance of partial clones'
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+test_expect_success 'enable server-side config' '
+ git config uploadpack.allowFilter true &&
+ git config uploadpack.allowAnySHA1InWant true
+'
+
+test_perf 'clone without blobs' '
+ rm -rf bare.git &&
+ git clone --no-local --bare --filter=blob:none . bare.git
+'
+
+test_perf 'checkout of result' '
+ rm -rf worktree &&
+ mkdir -p worktree/.git &&
+ tar -C bare.git -cf - . | tar -C worktree/.git -xf - &&
+ git -C worktree config core.bare false &&
+ git -C worktree checkout -f
+'
+
+test_done
diff --git a/t/perf/p5601-clone-reference.sh b/t/perf/p5601-clone-reference.sh
new file mode 100755
index 0000000..68fed66
--- /dev/null
+++ b/t/perf/p5601-clone-reference.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+test_description='speed of clone --reference'
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+test_expect_success 'create shareable repository' '
+ git clone --bare . shared.git
+'
+
+test_expect_success 'advance base repository' '
+ # Do not use test_commit here; its test_tick will
+ # use some ancient hard-coded date. The resulting clock
+ # skew will cause pack-objects to traverse in a very
+ # sub-optimal order, skewing the results.
+ echo content >new-file-that-does-not-exist &&
+ git add new-file-that-does-not-exist &&
+ git commit -m "new commit"
+'
+
+test_perf 'clone --reference' '
+ rm -rf dst.git &&
+ git clone --no-local --bare --reference shared.git . dst.git
+'
+
+test_done
diff --git a/t/perf/p9300-fast-import-export.sh b/t/perf/p9300-fast-import-export.sh
new file mode 100755
index 0000000..586161e
--- /dev/null
+++ b/t/perf/p9300-fast-import-export.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+test_description='test fast-import and fast-export performance'
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+# Use --no-data here to produce a vastly smaller export file.
+# This is much cheaper to work with but should still exercise
+# fast-import pretty well (we'll still process all commits and
+# trees, which account for 60% or more of objects in most repos).
+#
+# Use --reencode to avoid the default of aborting on non-utf8 commits,
+# which lets this test run against a wider variety of sample repos.
+test_perf 'export (no-blobs)' '
+ git fast-export --reencode=yes --no-data HEAD >export
+'
+
+test_perf 'import (no-blobs)' '
+ git fast-import --force <export
+'
+
+test_done
diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh
index 2e33ab3..13e3893 100644
--- a/t/perf/perf-lib.sh
+++ b/t/perf/perf-lib.sh
@@ -17,37 +17,27 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see http://www.gnu.org/licenses/ .
-# do the --tee work early; it otherwise confuses our careful
-# GIT_BUILD_DIR mangling
-case "$GIT_TEST_TEE_STARTED, $* " in
-done,*)
- # do not redirect again
- ;;
-*' --tee '*|*' --va'*)
- mkdir -p test-results
- BASE=test-results/$(basename "$0" .sh)
- (GIT_TEST_TEE_STARTED=done ${SHELL-sh} "$0" "$@" 2>&1;
- echo $? > $BASE.exit) | tee $BASE.out
- test "$(cat $BASE.exit)" = 0
- exit
- ;;
-esac
-
+# These variables must be set before the inclusion of test-lib.sh below,
+# because it will change our working directory.
TEST_DIRECTORY=$(pwd)/..
TEST_OUTPUT_DIRECTORY=$(pwd)
-if test -z "$GIT_TEST_INSTALLED"; then
- perf_results_prefix=
-else
- perf_results_prefix=$(printf "%s" "${GIT_TEST_INSTALLED%/bin-wrappers}" | tr -c "[a-zA-Z0-9]" "[_*]")"."
- # make the tested dir absolute
- GIT_TEST_INSTALLED=$(cd "$GIT_TEST_INSTALLED" && pwd)
-fi
TEST_NO_CREATE_REPO=t
TEST_NO_MALLOC_CHECK=t
. ../test-lib.sh
+if test -n "$GIT_TEST_INSTALLED" -a -z "$PERF_SET_GIT_TEST_INSTALLED"
+then
+ error "Do not use GIT_TEST_INSTALLED with the perf tests.
+
+Instead use:
+
+ ./run <path-to-git> -- <tests>
+
+See t/perf/README for details."
+fi
+
# Variables from test-lib that are normally internal to the tests; we
# need to export them for test_perf subshells
export TEST_DIRECTORY TRASH_DIRECTORY GIT_BUILD_DIR GIT_TEST_CMP
@@ -191,7 +181,7 @@ test_wrapper_ () {
base=$(basename "$0" .sh)
echo "$test_count" >>"$perf_results_dir"/$base.subtests
echo "$1" >"$perf_results_dir"/$base.$test_count.descr
- base="$perf_results_dir"/"$perf_results_prefix$(basename "$0" .sh)"."$test_count"
+ base="$perf_results_dir"/"$PERF_RESULTS_PREFIX$(basename "$0" .sh)"."$test_count"
"$test_wrapper_func_" "$@"
fi
@@ -224,7 +214,7 @@ test_perf_ () {
else
test_ok_ "$1"
fi
- "$TEST_DIRECTORY"/perf/min_time.perl test_time.* >"$base".times
+ "$TEST_DIRECTORY"/perf/min_time.perl test_time.* >"$base".result
}
test_perf () {
@@ -233,7 +223,7 @@ test_perf () {
test_size_ () {
say >&3 "running: $2"
- if test_eval_ "$2" 3>"$base".size; then
+ if test_eval_ "$2" 3>"$base".result; then
test_ok_ "$1"
else
test_failure_ "$@"
diff --git a/t/perf/run b/t/perf/run
index 9aaa733..c7b8610 100755
--- a/t/perf/run
+++ b/t/perf/run
@@ -70,6 +70,24 @@ build_git_rev () {
) || die "failed to build revision '$mydir'"
}
+set_git_test_installed () {
+ mydir=$1
+
+ mydir_abs=$(cd $mydir && pwd)
+ mydir_abs_wrappers="$mydir_abs_wrappers/bin-wrappers"
+ if test -d "$mydir_abs_wrappers"
+ then
+ GIT_TEST_INSTALLED=$mydir_abs_wrappers
+ else
+ # Older versions of git lacked bin-wrappers;
+ # fallback to the files in the root.
+ GIT_TEST_INSTALLED=$mydir_abs
+ fi
+ export GIT_TEST_INSTALLED
+ PERF_SET_GIT_TEST_INSTALLED=true
+ export PERF_SET_GIT_TEST_INSTALLED
+}
+
run_dirs_helper () {
mydir=${1%/}
shift
@@ -79,7 +97,16 @@ run_dirs_helper () {
if test $# -gt 0 -a "$1" = --; then
shift
fi
- if [ ! -d "$mydir" ]; then
+
+ PERF_RESULTS_PREFIX=
+ if test "$mydir" = "."
+ then
+ unset GIT_TEST_INSTALLED
+ elif test -d "$mydir"
+ then
+ PERF_RESULTS_PREFIX=bindir$(cd $mydir && printf "%s" "$(pwd)" | tr -c "[a-zA-Z0-9]" "_").
+ set_git_test_installed "$mydir"
+ else
rev=$(git rev-parse --verify "$mydir" 2>/dev/null) ||
die "'$mydir' is neither a directory nor a valid revision"
if [ ! -d build/$rev ]; then
@@ -87,16 +114,12 @@ run_dirs_helper () {
fi
build_git_rev $rev "$mydir"
mydir=build/$rev
+
+ PERF_RESULTS_PREFIX=build_$rev.
+ set_git_test_installed "$mydir"
fi
- if test "$mydir" = .; then
- unset GIT_TEST_INSTALLED
- else
- GIT_TEST_INSTALLED="$mydir/bin-wrappers"
- # Older versions of git lacked bin-wrappers; fallback to the
- # files in the root.
- test -d "$GIT_TEST_INSTALLED" || GIT_TEST_INSTALLED=$mydir
- export GIT_TEST_INSTALLED
- fi
+ export PERF_RESULTS_PREFIX
+
run_one_dir "$@"
}
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index b656600..f58f3de 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -20,28 +20,27 @@ modification *should* take notice and update the test vectors here.
. ./test-lib.sh
-try_local_x () {
- local x="local" &&
- echo "$x"
+try_local_xy () {
+ local x="local" y="alsolocal" &&
+ echo "$x $y"
}
-# This test is an experiment to check whether any Git users are using
-# Shells that don't support the "local" keyword. "local" is not
+# Check whether the shell supports the "local" keyword. "local" is not
# POSIX-standard, but it is very widely supported by POSIX-compliant
-# shells, and if it doesn't cause problems for people, we would like
-# to be able to use it in Git code.
+# shells, and we rely on it within Git's test framework.
#
-# For now, this is the only test that requires "local". If your shell
-# fails this test, you can ignore the failure, but please report the
-# problem to the Git mailing list <git@vger.kernel.org>, as it might
-# convince us to continue avoiding the use of "local".
+# If your shell fails this test, the results of other tests may be
+# unreliable. You may wish to report the problem to the Git mailing
+# list <git@vger.kernel.org>, as it could cause us to reconsider
+# relying on "local".
test_expect_success 'verify that the running shell supports "local"' '
x="notlocal" &&
- echo "local" >expected1 &&
- try_local_x >actual1 &&
+ y="alsonotlocal" &&
+ echo "local alsolocal" >expected1 &&
+ try_local_xy >actual1 &&
test_cmp expected1 actual1 &&
- echo "notlocal" >expected2 &&
- echo "$x" >actual2 &&
+ echo "notlocal alsonotlocal" >expected2 &&
+ echo "$x $y" >actual2 &&
test_cmp expected2 actual2
'
@@ -99,6 +98,7 @@ _run_sub_test_lib_test_common () {
export TEST_DIRECTORY &&
TEST_OUTPUT_DIRECTORY=$(pwd) &&
export TEST_OUTPUT_DIRECTORY &&
+ sane_unset GIT_TEST_FAIL_PREREQS &&
if test -z "$neg"
then
./"$name.sh" "$@" >out 2>err
@@ -128,7 +128,7 @@ check_sub_test_lib_test () {
check_sub_test_lib_test_err () {
name="$1" # stdin is the expected output from the test
- # expected error output is in descriptior 3
+ # expected error output is in descriptor 3
(
cd "$name" &&
sed -e 's/^> //' -e 's/Z$//' >expect.out &&
@@ -156,7 +156,7 @@ test_expect_success 'pretend we have a fully passing test suite' "
"
test_expect_success 'pretend we have a partially passing test suite' "
- test_must_fail run_sub_test_lib_test \
+ run_sub_test_lib_test_err \
partial-pass '2/3 tests passing' <<-\\EOF &&
test_expect_success 'passing test #1' 'true'
test_expect_success 'failing test #2' 'false'
@@ -220,7 +220,7 @@ test_expect_success 'pretend we have fixed one of two known breakages (run in su
"
test_expect_success 'pretend we have a pass, fail, and known breakage' "
- test_must_fail run_sub_test_lib_test \
+ run_sub_test_lib_test_err \
mixed-results1 'mixed results #1' <<-\\EOF &&
test_expect_success 'passing test' 'true'
test_expect_success 'failing test' 'false'
@@ -239,7 +239,7 @@ test_expect_success 'pretend we have a pass, fail, and known breakage' "
"
test_expect_success 'pretend we have a mix of all possible results' "
- test_must_fail run_sub_test_lib_test \
+ run_sub_test_lib_test_err \
mixed-results2 'mixed results #2' <<-\\EOF &&
test_expect_success 'passing test' 'true'
test_expect_success 'passing test' 'true'
@@ -275,24 +275,24 @@ test_expect_success 'pretend we have a mix of all possible results' "
"
test_expect_success C_LOCALE_OUTPUT 'test --verbose' '
- test_must_fail run_sub_test_lib_test \
- test-verbose "test verbose" --verbose <<-\EOF &&
+ run_sub_test_lib_test_err \
+ t1234-verbose "test verbose" --verbose <<-\EOF &&
test_expect_success "passing test" true
test_expect_success "test with output" "echo foo"
test_expect_success "failing test" false
test_done
EOF
- mv test-verbose/out test-verbose/out+ &&
- grep -v "^Initialized empty" test-verbose/out+ >test-verbose/out &&
- check_sub_test_lib_test test-verbose <<-\EOF
- > expecting success: true
+ mv t1234-verbose/out t1234-verbose/out+ &&
+ grep -v "^Initialized empty" t1234-verbose/out+ >t1234-verbose/out &&
+ check_sub_test_lib_test t1234-verbose <<-\EOF
+ > expecting success of 1234.1 '\''passing test'\'': true
> ok 1 - passing test
> Z
- > expecting success: echo foo
+ > expecting success of 1234.2 '\''test with output'\'': echo foo
> foo
> ok 2 - test with output
> Z
- > expecting success: false
+ > expecting success of 1234.3 '\''failing test'\'': false
> not ok 3 - failing test
> # false
> Z
@@ -302,18 +302,18 @@ test_expect_success C_LOCALE_OUTPUT 'test --verbose' '
'
test_expect_success 'test --verbose-only' '
- test_must_fail run_sub_test_lib_test \
- test-verbose-only-2 "test verbose-only=2" \
+ run_sub_test_lib_test_err \
+ t2345-verbose-only-2 "test verbose-only=2" \
--verbose-only=2 <<-\EOF &&
test_expect_success "passing test" true
test_expect_success "test with output" "echo foo"
test_expect_success "failing test" false
test_done
EOF
- check_sub_test_lib_test test-verbose-only-2 <<-\EOF
+ check_sub_test_lib_test t2345-verbose-only-2 <<-\EOF
> ok 1 - passing test
> Z
- > expecting success: echo foo
+ > expecting success of 2345.2 '\''test with output'\'': echo foo
> foo
> ok 2 - test with output
> Z
@@ -393,6 +393,44 @@ test_expect_success 'GIT_SKIP_TESTS sh pattern' "
)
"
+test_expect_success 'GIT_SKIP_TESTS entire suite' "
+ (
+ GIT_SKIP_TESTS='git' && export GIT_SKIP_TESTS &&
+ run_sub_test_lib_test git-skip-tests-entire-suite \
+ 'GIT_SKIP_TESTS entire suite' <<-\\EOF &&
+ for i in 1 2 3
+ do
+ test_expect_success \"passing test #\$i\" 'true'
+ done
+ test_done
+ EOF
+ check_sub_test_lib_test git-skip-tests-entire-suite <<-\\EOF
+ > 1..0 # SKIP skip all tests in git
+ EOF
+ )
+"
+
+test_expect_success 'GIT_SKIP_TESTS does not skip unmatched suite' "
+ (
+ GIT_SKIP_TESTS='notgit' && export GIT_SKIP_TESTS &&
+ run_sub_test_lib_test git-skip-tests-unmatched-suite \
+ 'GIT_SKIP_TESTS does not skip unmatched suite' <<-\\EOF &&
+ for i in 1 2 3
+ do
+ test_expect_success \"passing test #\$i\" 'true'
+ done
+ test_done
+ EOF
+ check_sub_test_lib_test git-skip-tests-unmatched-suite <<-\\EOF
+ > ok 1 - passing test #1
+ > ok 2 - passing test #2
+ > ok 3 - passing test #3
+ > # passed all 3 test(s)
+ > 1..3
+ EOF
+ )
+"
+
test_expect_success '--run basic' "
run_sub_test_lib_test run-basic \
'--run basic' --run='1 3 5' <<-\\EOF &&
@@ -726,7 +764,7 @@ donthaveit=yes
test_expect_success DONTHAVEIT 'unmet prerequisite causes test to be skipped' '
donthaveit=no
'
-if test $haveit$donthaveit != yesyes
+if test -z "$GIT_TEST_FAIL_PREREQS_INTERNAL" -a $haveit$donthaveit != yesyes
then
say "bug in test framework: prerequisite tags do not work reliably"
exit 1
@@ -747,7 +785,7 @@ donthaveiteither=yes
test_expect_success DONTHAVEIT,HAVEIT 'unmet prerequisites causes test to be skipped' '
donthaveiteither=no
'
-if test $haveit$donthaveit$donthaveiteither != yesyesyes
+if test -z "$GIT_TEST_FAIL_PREREQS_INTERNAL" -a $haveit$donthaveit$donthaveiteither != yesyesyes
then
say "bug in test framework: multiple prerequisite tags do not work reliably"
exit 1
@@ -763,7 +801,7 @@ test_expect_success !LAZY_TRUE 'missing lazy prereqs skip tests' '
donthavetrue=no
'
-if test "$havetrue$donthavetrue" != yesyes
+if test -z "$GIT_TEST_FAIL_PREREQS_INTERNAL" -a "$havetrue$donthavetrue" != yesyes
then
say 'bug in test framework: lazy prerequisites do not work'
exit 1
@@ -779,7 +817,7 @@ test_expect_success LAZY_FALSE 'missing negative lazy prereqs will skip' '
havefalse=no
'
-if test "$nothavefalse$havefalse" != yesyes
+if test -z "$GIT_TEST_FAIL_PREREQS_INTERNAL" -a "$nothavefalse$havefalse" != yesyes
then
say 'bug in test framework: negative lazy prerequisites do not work'
exit 1
@@ -790,14 +828,27 @@ test_expect_success 'tests clean up after themselves' '
test_when_finished clean=yes
'
-if test $clean != yes
+if test -z "$GIT_TEST_FAIL_PREREQS_INTERNAL" -a $clean != yes
then
say "bug in test framework: basic cleanup command does not work reliably"
exit 1
fi
+test_expect_success 'lazy prereqs do not turn off tracing' "
+ run_sub_test_lib_test lazy-prereq-and-tracing \
+ 'lazy prereqs and -x' -v -x <<-\\EOF &&
+ test_lazy_prereq LAZY true
+
+ test_expect_success lazy 'test_have_prereq LAZY && echo trace'
+
+ test_done
+ EOF
+
+ grep 'echo trace' lazy-prereq-and-tracing/err
+"
+
test_expect_success 'tests clean up even on failures' "
- test_must_fail run_sub_test_lib_test \
+ run_sub_test_lib_test_err \
failing-cleanup 'Failing tests with cleanup commands' <<-\\EOF &&
test_expect_success 'tests clean up even after a failure' '
touch clean-after-failure &&
@@ -825,6 +876,24 @@ test_expect_success 'tests clean up even on failures' "
EOF
"
+test_expect_success 'test_atexit is run' "
+ run_sub_test_lib_test_err \
+ atexit-cleanup 'Run atexit commands' -i <<-\\EOF &&
+ test_expect_success 'tests clean up even after a failure' '
+ > ../../clean-atexit &&
+ test_atexit rm ../../clean-atexit &&
+ > ../../also-clean-atexit &&
+ test_atexit rm ../../also-clean-atexit &&
+ > ../../dont-clean-atexit &&
+ (exit 1)
+ '
+ test_done
+ EOF
+ test_path_is_file dont-clean-atexit &&
+ test_path_is_missing clean-atexit &&
+ test_path_is_missing also-clean-atexit
+"
+
test_expect_success 'test_oid setup' '
test_oid_init
'
@@ -862,6 +931,40 @@ test_expect_success 'test_oid can look up data for SHA-256' '
test "$hexsz" -eq 64
'
+test_expect_success 'test_bool_env' '
+ (
+ sane_unset envvar &&
+
+ test_bool_env envvar true &&
+ ! test_bool_env envvar false &&
+
+ envvar= &&
+ export envvar &&
+ ! test_bool_env envvar true &&
+ ! test_bool_env envvar false &&
+
+ envvar=true &&
+ test_bool_env envvar true &&
+ test_bool_env envvar false &&
+
+ envvar=false &&
+ ! test_bool_env envvar true &&
+ ! test_bool_env envvar false &&
+
+ envvar=invalid &&
+ # When encountering an invalid bool value, test_bool_env
+ # prints its error message to the original stderr of the
+ # test script, hence the redirection of fd 7, and aborts
+ # with "exit 1", hence the subshell.
+ ! ( test_bool_env envvar true ) 7>err &&
+ grep "error: test_bool_env requires bool values" err &&
+
+ envvar=true &&
+ ! ( test_bool_env envvar invalid ) 7>err &&
+ grep "error: test_bool_env requires bool values" err
+ )
+'
+
################################################################
# Basics of the basics
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index 42a263c..1edd5ae 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -93,6 +93,7 @@ test_expect_success 'No extra GIT_* on alias scripts' '
sed -n \
-e "/^GIT_PREFIX=/d" \
-e "/^GIT_TEXTDOMAINDIR=/d" \
+ -e "/^GIT_TRACE2_PARENT/d" \
-e "/^GIT_/s/=.*//p" |
sort
EOF
@@ -174,7 +175,7 @@ test_expect_success 'reinit' '
test_expect_success 'init with --template' '
mkdir template-source &&
echo content >template-source/file &&
- git init --template=../template-source template-custom &&
+ git init --template=template-source template-custom &&
test_cmp template-source/file template-custom/.git/file
'
@@ -310,8 +311,8 @@ test_expect_success 'init prefers command line to GIT_DIR' '
test_expect_success 'init with separate gitdir' '
rm -rf newdir &&
git init --separate-git-dir realgitdir newdir &&
- echo "gitdir: $(pwd)/realgitdir" >expected &&
- test_cmp expected newdir/.git &&
+ newdir_git="$(cat newdir/.git)" &&
+ test_cmp_fspath "$(pwd)/realgitdir" "${newdir_git#gitdir: }" &&
test_path_is_dir realgitdir/refs
'
@@ -360,12 +361,9 @@ test_expect_success 're-init on .git file' '
'
test_expect_success 're-init to update git link' '
- (
- cd newdir &&
- git init --separate-git-dir ../surrealgitdir
- ) &&
- echo "gitdir: $(pwd)/surrealgitdir" >expected &&
- test_cmp expected newdir/.git &&
+ git -C newdir init --separate-git-dir ../surrealgitdir &&
+ newdir_git="$(cat newdir/.git)" &&
+ test_cmp_fspath "$(pwd)/surrealgitdir" "${newdir_git#gitdir: }" &&
test_path_is_dir surrealgitdir/refs &&
test_path_is_missing realgitdir/refs
'
@@ -373,12 +371,9 @@ test_expect_success 're-init to update git link' '
test_expect_success 're-init to move gitdir' '
rm -rf newdir realgitdir surrealgitdir &&
git init newdir &&
- (
- cd newdir &&
- git init --separate-git-dir ../realgitdir
- ) &&
- echo "gitdir: $(pwd)/realgitdir" >expected &&
- test_cmp expected newdir/.git &&
+ git -C newdir init --separate-git-dir ../realgitdir &&
+ newdir_git="$(cat newdir/.git)" &&
+ test_cmp_fspath "$(pwd)/realgitdir" "${newdir_git#gitdir: }" &&
test_path_is_dir realgitdir/refs
'
@@ -397,13 +392,6 @@ test_expect_success SYMLINKS 're-init to move gitdir symlink' '
test_path_is_dir realgitdir/refs
'
-# Tests for the hidden file attribute on windows
-is_hidden () {
- # Use the output of `attrib`, ignore the absolute path
- case "$(attrib "$1")" in *H*?:*) return 0;; esac
- return 1
-}
-
test_expect_success MINGW '.git hidden' '
rm -rf newdir &&
(
@@ -411,7 +399,7 @@ test_expect_success MINGW '.git hidden' '
mkdir newdir &&
cd newdir &&
git init &&
- is_hidden .git
+ test_path_is_hidden .git
) &&
check_config newdir/.git false unset
'
@@ -453,6 +441,17 @@ test_expect_success 're-init from a linked worktree' '
)
'
+test_expect_success MINGW 'core.hidedotfiles = false' '
+ git config --global core.hidedotfiles false &&
+ rm -rf newdir &&
+ mkdir newdir &&
+ (
+ sane_unset GIT_DIR GIT_WORK_TREE GIT_CONFIG &&
+ git -C newdir init
+ ) &&
+ ! is_hidden newdir/.git
+'
+
test_expect_success MINGW 'redirect std handles' '
GIT_REDIRECT_STDOUT=output.txt git rev-parse --git-dir &&
test .git = "$(cat output.txt)" &&
@@ -461,8 +460,8 @@ test_expect_success MINGW 'redirect std handles' '
GIT_REDIRECT_STDOUT=output.txt \
GIT_REDIRECT_STDERR="2>&1" \
git rev-parse --git-dir --verify refs/invalid &&
- printf ".git\nfatal: Needed a single revision\n" >expect &&
- test_cmp expect output.txt
+ grep "^\\.git\$" output.txt &&
+ grep "Needed a single revision" output.txt
'
test_done
diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh
index 22499bc..b660593 100755
--- a/t/t0003-attributes.sh
+++ b/t/t0003-attributes.sh
@@ -5,19 +5,16 @@ test_description=gitattributes
. ./test-lib.sh
attr_check () {
- path="$1" expect="$2"
+ path="$1" expect="$2" git_opts="$3" &&
- git $3 check-attr test -- "$path" >actual 2>err &&
- echo "$path: test: $2" >expect &&
+ git $git_opts check-attr test -- "$path" >actual 2>err &&
+ echo "$path: test: $expect" >expect &&
test_cmp expect actual &&
- test_line_count = 0 err
+ test_must_be_empty err
}
attr_check_quote () {
-
- path="$1"
- quoted_path="$2"
- expect="$3"
+ path="$1" quoted_path="$2" expect="$3" &&
git check-attr test -- "$path" >actual &&
echo "\"$quoted_path\": test: $expect" >expect &&
@@ -27,7 +24,7 @@ attr_check_quote () {
test_expect_success 'open-quoted pathname' '
echo "\"a test=a" >.gitattributes &&
- test_must_fail attr_check a a
+ attr_check a unspecified
'
@@ -112,20 +109,20 @@ test_expect_success 'attribute test' '
test_expect_success 'attribute matching is case sensitive when core.ignorecase=0' '
- test_must_fail attr_check F f "-c core.ignorecase=0" &&
- test_must_fail attr_check a/F f "-c core.ignorecase=0" &&
- test_must_fail attr_check a/c/F f "-c core.ignorecase=0" &&
- test_must_fail attr_check a/G a/g "-c core.ignorecase=0" &&
- test_must_fail attr_check a/B/g a/b/g "-c core.ignorecase=0" &&
- test_must_fail attr_check a/b/G a/b/g "-c core.ignorecase=0" &&
- test_must_fail attr_check a/b/H a/b/h "-c core.ignorecase=0" &&
- test_must_fail attr_check a/b/D/g "a/b/d/*" "-c core.ignorecase=0" &&
- test_must_fail attr_check oNoFf unset "-c core.ignorecase=0" &&
- test_must_fail attr_check oFfOn set "-c core.ignorecase=0" &&
+ attr_check F unspecified "-c core.ignorecase=0" &&
+ attr_check a/F unspecified "-c core.ignorecase=0" &&
+ attr_check a/c/F unspecified "-c core.ignorecase=0" &&
+ attr_check a/G unspecified "-c core.ignorecase=0" &&
+ attr_check a/B/g a/g "-c core.ignorecase=0" &&
+ attr_check a/b/G unspecified "-c core.ignorecase=0" &&
+ attr_check a/b/H unspecified "-c core.ignorecase=0" &&
+ attr_check a/b/D/g a/g "-c core.ignorecase=0" &&
+ attr_check oNoFf unspecified "-c core.ignorecase=0" &&
+ attr_check oFfOn unspecified "-c core.ignorecase=0" &&
attr_check NO unspecified "-c core.ignorecase=0" &&
- test_must_fail attr_check a/b/D/NO "a/b/d/*" "-c core.ignorecase=0" &&
+ attr_check a/b/D/NO unspecified "-c core.ignorecase=0" &&
attr_check a/b/d/YES a/b/d/* "-c core.ignorecase=0" &&
- test_must_fail attr_check a/E/f "A/e/F" "-c core.ignorecase=0"
+ attr_check a/E/f f "-c core.ignorecase=0"
'
@@ -149,8 +146,8 @@ test_expect_success 'attribute matching is case insensitive when core.ignorecase
'
test_expect_success CASE_INSENSITIVE_FS 'additional case insensitivity tests' '
- test_must_fail attr_check a/B/D/g "a/b/d/*" "-c core.ignorecase=0" &&
- test_must_fail attr_check A/B/D/NO "a/b/d/*" "-c core.ignorecase=0" &&
+ attr_check a/B/D/g a/g "-c core.ignorecase=0" &&
+ attr_check A/B/D/NO unspecified "-c core.ignorecase=0" &&
attr_check A/b/h a/b/h "-c core.ignorecase=1" &&
attr_check a/B/D/g "a/b/d/*" "-c core.ignorecase=1" &&
attr_check A/B/D/NO "a/b/d/*" "-c core.ignorecase=1"
@@ -244,7 +241,7 @@ EOF
git check-attr foo -- "a/b/f" >>actual 2>>err &&
git check-attr foo -- "a/b/c/f" >>actual 2>>err &&
test_cmp expect actual &&
- test_line_count = 0 err
+ test_must_be_empty err
'
test_expect_success '"**" with no slashes test' '
@@ -265,7 +262,7 @@ EOF
git check-attr foo -- "a/b/f" >>actual 2>>err &&
git check-attr foo -- "a/b/c/f" >>actual 2>>err &&
test_cmp expect actual &&
- test_line_count = 0 err
+ test_must_be_empty err
'
test_expect_success 'using --git-dir and --work-tree' '
@@ -322,4 +319,24 @@ test_expect_success 'bare repository: test info/attributes' '
)
'
+test_expect_success 'binary macro expanded by -a' '
+ echo "file binary" >.gitattributes &&
+ cat >expect <<-\EOF &&
+ file: binary: set
+ file: diff: unset
+ file: merge: unset
+ file: text: unset
+ EOF
+ git check-attr -a file >actual &&
+ test_cmp expect actual
+'
+
+
+test_expect_success 'query binary macro directly' '
+ echo "file binary" >.gitattributes &&
+ echo file: binary: set >expect &&
+ git check-attr binary file >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index ffb2975..75ee9a9 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -4,10 +4,10 @@ test_description='test date parsing and printing'
. ./test-lib.sh
# arbitrary reference time: 2009-08-30 19:20:00
-TEST_DATE_NOW=1251660000; export TEST_DATE_NOW
+GIT_TEST_DATE_NOW=1251660000; export GIT_TEST_DATE_NOW
check_relative() {
- t=$(($TEST_DATE_NOW - $1))
+ t=$(($GIT_TEST_DATE_NOW - $1))
echo "$t -> $2" >expect
test_expect_${3:-success} "relative date ($2)" "
test-tool date relative $t >actual &&
@@ -81,6 +81,11 @@ check_parse 2008-02 bad
check_parse 2008-02-14 bad
check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 +0000'
check_parse '2008-02-14 20:30:45 -0500' '2008-02-14 20:30:45 -0500'
+check_parse '2008.02.14 20:30:45 -0500' '2008-02-14 20:30:45 -0500'
+check_parse '20080214T203045-04:00' '2008-02-14 20:30:45 -0400'
+check_parse '20080214T203045 -04:00' '2008-02-14 20:30:45 -0400'
+check_parse '20080214T203045.019-04:00' '2008-02-14 20:30:45 -0400'
+check_parse '2008-02-14 20:30:45.019-04:00' '2008-02-14 20:30:45 -0400'
check_parse '2008-02-14 20:30:45 -0015' '2008-02-14 20:30:45 -0015'
check_parse '2008-02-14 20:30:45 -5' '2008-02-14 20:30:45 +0000'
check_parse '2008-02-14 20:30:45 -5:' '2008-02-14 20:30:45 +0000'
@@ -103,6 +108,7 @@ check_approxidate 5.seconds.ago '2009-08-30 19:19:55'
check_approxidate 10.minutes.ago '2009-08-30 19:10:00'
check_approxidate yesterday '2009-08-29 19:20:00'
check_approxidate 3.days.ago '2009-08-27 19:20:00'
+check_approxidate '12:34:56.3.days.ago' '2009-08-27 12:34:56'
check_approxidate 3.weeks.ago '2009-08-09 19:20:00'
check_approxidate 3.months.ago '2009-05-30 19:20:00'
check_approxidate 2.years.3.months.ago '2007-05-30 19:20:00'
@@ -128,4 +134,22 @@ check_approxidate '6AM, June 7, 2009' '2009-06-07 06:00:00'
check_approxidate '2008-12-01' '2008-12-01 19:20:00'
check_approxidate '2009-12-01' '2009-12-01 19:20:00'
+check_date_format_human() {
+ t=$(($GIT_TEST_DATE_NOW - $1))
+ echo "$t -> $2" >expect
+ test_expect_success "human date $t" '
+ test-tool date human $t >actual &&
+ test_i18ncmp expect actual
+'
+}
+
+check_date_format_human 18000 "5 hours ago" # 5 hours ago
+check_date_format_human 432000 "Tue Aug 25 19:20" # 5 days ago
+check_date_format_human 1728000 "Mon Aug 10 19:20" # 3 weeks ago
+check_date_format_human 13000000 "Thu Apr 2 08:13" # 5 months ago
+check_date_format_human 31449600 "Aug 31 2008" # 12 months ago
+check_date_format_human 37500000 "Jun 22 2008" # 1 year, 2 months ago
+check_date_format_human 55188000 "Dec 1 2007" # 1 year, 9 months ago
+check_date_format_human 630000000 "Sep 13 1989" # 20 years ago
+
test_done
diff --git a/t/t0007-git-var.sh b/t/t0007-git-var.sh
index 5868a87..88b9ae8 100755
--- a/t/t0007-git-var.sh
+++ b/t/t0007-git-var.sh
@@ -17,7 +17,7 @@ test_expect_success 'get GIT_COMMITTER_IDENT' '
test_cmp expect actual
'
-test_expect_success !AUTOIDENT 'requested identites are strict' '
+test_expect_success !FAIL_PREREQS,!AUTOIDENT 'requested identities are strict' '
(
sane_unset GIT_COMMITTER_NAME &&
sane_unset GIT_COMMITTER_EMAIL &&
diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh
index 1744cee..370a389 100755
--- a/t/t0008-ignores.sh
+++ b/t/t0008-ignores.sh
@@ -424,9 +424,24 @@ test_expect_success 'local ignore inside a sub-directory with --verbose' '
)
'
-test_expect_success_multi 'nested include' \
- 'a/b/.gitignore:8:!on* a/b/one' '
- test_check_ignore "a/b/one"
+test_expect_success 'nested include of negated pattern' '
+ expect "" &&
+ test_check_ignore "a/b/one" 1
+'
+
+test_expect_success 'nested include of negated pattern with -q' '
+ expect "" &&
+ test_check_ignore "-q a/b/one" 1
+'
+
+test_expect_success 'nested include of negated pattern with -v' '
+ expect "a/b/.gitignore:8:!on* a/b/one" &&
+ test_check_ignore "-v a/b/one" 0
+'
+
+test_expect_success 'nested include of negated pattern with -v -n' '
+ expect "a/b/.gitignore:8:!on* a/b/one" &&
+ test_check_ignore "-v -n a/b/one" 0
'
############################################################################
@@ -460,7 +475,6 @@ test_expect_success 'cd to ignored sub-directory' '
expect_from_stdin <<-\EOF &&
foo
twoooo
- ../one
seven
../../one
EOF
@@ -543,7 +557,6 @@ test_expect_success 'global ignore' '
globalthree
a/globalthree
a/per-repo
- globaltwo
EOF
test_check_ignore "globalone per-repo globalthree a/globalthree a/per-repo not-ignored globaltwo"
'
@@ -586,17 +599,7 @@ EOF
cat <<-\EOF >expected-default
one
a/one
- a/b/on
- a/b/one
- a/b/one one
- a/b/one two
- "a/b/one\"three"
- a/b/two
a/b/twooo
- globaltwo
- a/globaltwo
- a/b/globaltwo
- b/globaltwo
EOF
cat <<-EOF >expected-verbose
.gitignore:1:one one
@@ -696,8 +699,12 @@ cat <<-EOF >expected-all
$global_excludes:2:!globaltwo ../b/globaltwo
:: c/not-ignored
EOF
+cat <<-EOF >expected-default
+../one
+one
+b/twooo
+EOF
grep -v '^:: ' expected-all >expected-verbose
-sed -e 's/.* //' expected-verbose >expected-default
broken_c_unquote stdin >stdin0
diff --git a/t/t0011-hashmap.sh b/t/t0011-hashmap.sh
index 3f1f505..5343ffd 100755
--- a/t/t0011-hashmap.sh
+++ b/t/t0011-hashmap.sh
@@ -9,15 +9,6 @@ test_hashmap() {
test_cmp expect actual
}
-test_expect_success 'hash functions' '
-
-test_hashmap "hash key1" "2215982743 2215982743 116372151 116372151" &&
-test_hashmap "hash key2" "2215982740 2215982740 116372148 116372148" &&
-test_hashmap "hash fooBarFrotz" "1383912807 1383912807 3189766727 3189766727" &&
-test_hashmap "hash foobarfrotz" "2862305959 2862305959 3189766727 3189766727"
-
-'
-
test_expect_success 'put' '
test_hashmap "put key1 value1
@@ -179,31 +170,45 @@ NULL
'
test_expect_success 'iterate' '
-
-test_hashmap "put key1 value1
-put key2 value2
-put fooBarFrotz value3
-iterate" "NULL
-NULL
-NULL
-key2 value2
-key1 value1
-fooBarFrotz value3"
-
+ test-tool hashmap >actual.raw <<-\EOF &&
+ put key1 value1
+ put key2 value2
+ put fooBarFrotz value3
+ iterate
+ EOF
+
+ cat >expect <<-\EOF &&
+ NULL
+ NULL
+ NULL
+ fooBarFrotz value3
+ key1 value1
+ key2 value2
+ EOF
+
+ sort <actual.raw >actual &&
+ test_cmp expect actual
'
test_expect_success 'iterate (case insensitive)' '
-
-test_hashmap "put key1 value1
-put key2 value2
-put fooBarFrotz value3
-iterate" "NULL
-NULL
-NULL
-fooBarFrotz value3
-key2 value2
-key1 value1" ignorecase
-
+ test-tool hashmap ignorecase >actual.raw <<-\EOF &&
+ put key1 value1
+ put key2 value2
+ put fooBarFrotz value3
+ iterate
+ EOF
+
+ cat >expect <<-\EOF &&
+ NULL
+ NULL
+ NULL
+ fooBarFrotz value3
+ key1 value1
+ key2 value2
+ EOF
+
+ sort <actual.raw >actual &&
+ test_cmp expect actual
'
test_expect_success 'grow / shrink' '
diff --git a/t/t0014-alias.sh b/t/t0014-alias.sh
index a070e64..8d3d914 100755
--- a/t/t0014-alias.sh
+++ b/t/t0014-alias.sh
@@ -37,4 +37,11 @@ test_expect_success 'looping aliases - internal execution' '
# test_i18ngrep "^fatal: alias loop detected: expansion of" output
#'
+test_expect_success 'run-command formats empty args properly' '
+ test_must_fail env GIT_TRACE=1 git frotz a "" b " " c 2>actual.raw &&
+ sed -ne "/run_command:/s/.*trace: run_command: //p" actual.raw >actual &&
+ echo "git-frotz a '\'''\'' b '\'' '\'' c" >expect &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t0015-hash.sh b/t/t0015-hash.sh
new file mode 100755
index 0000000..291e906
--- /dev/null
+++ b/t/t0015-hash.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+test_description='test basic hash implementation'
+. ./test-lib.sh
+
+
+test_expect_success 'test basic SHA-1 hash values' '
+ test-tool sha1 </dev/null >actual &&
+ grep da39a3ee5e6b4b0d3255bfef95601890afd80709 actual &&
+ printf "a" | test-tool sha1 >actual &&
+ grep 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 actual &&
+ printf "abc" | test-tool sha1 >actual &&
+ grep a9993e364706816aba3e25717850c26c9cd0d89d actual &&
+ printf "message digest" | test-tool sha1 >actual &&
+ grep c12252ceda8be8994d5fa0290a47231c1d16aae3 actual &&
+ printf "abcdefghijklmnopqrstuvwxyz" | test-tool sha1 >actual &&
+ grep 32d10c7b8cf96570ca04ce37f2a19d84240d3a89 actual &&
+ perl -e "$| = 1; print q{aaaaaaaaaa} for 1..100000;" | \
+ test-tool sha1 >actual &&
+ grep 34aa973cd4c4daa4f61eeb2bdbad27316534016f actual &&
+ printf "blob 0\0" | test-tool sha1 >actual &&
+ grep e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 actual &&
+ printf "blob 3\0abc" | test-tool sha1 >actual &&
+ grep f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f actual &&
+ printf "tree 0\0" | test-tool sha1 >actual &&
+ grep 4b825dc642cb6eb9a060e54bf8d69288fbee4904 actual
+'
+
+test_expect_success 'test basic SHA-256 hash values' '
+ test-tool sha256 </dev/null >actual &&
+ grep e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 actual &&
+ printf "a" | test-tool sha256 >actual &&
+ grep ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb actual &&
+ printf "abc" | test-tool sha256 >actual &&
+ grep ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad actual &&
+ printf "message digest" | test-tool sha256 >actual &&
+ grep f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650 actual &&
+ printf "abcdefghijklmnopqrstuvwxyz" | test-tool sha256 >actual &&
+ grep 71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73 actual &&
+ # Try to exercise the chunking code by turning autoflush on.
+ perl -e "$| = 1; print q{aaaaaaaaaa} for 1..100000;" | \
+ test-tool sha256 >actual &&
+ grep cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0 actual &&
+ perl -e "$| = 1; print q{abcdefghijklmnopqrstuvwxyz} for 1..100000;" | \
+ test-tool sha256 >actual &&
+ grep e406ba321ca712ad35a698bf0af8d61fc4dc40eca6bdcea4697962724ccbde35 actual &&
+ printf "blob 0\0" | test-tool sha256 >actual &&
+ grep 473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813 actual &&
+ printf "blob 3\0abc" | test-tool sha256 >actual &&
+ grep c1cf6e465077930e88dc5136641d402f72a229ddd996f627d60e9639eaba35a6 actual &&
+ printf "tree 0\0" | test-tool sha256 >actual &&
+ grep 6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321 actual
+'
+
+test_done
diff --git a/t/t0016-oidmap.sh b/t/t0016-oidmap.sh
new file mode 100755
index 0000000..31f8276
--- /dev/null
+++ b/t/t0016-oidmap.sh
@@ -0,0 +1,110 @@
+#!/bin/sh
+
+test_description='test oidmap'
+. ./test-lib.sh
+
+# This purposefully is very similar to t0011-hashmap.sh
+
+test_oidmap () {
+ echo "$1" | test-tool oidmap $3 >actual &&
+ echo "$2" >expect &&
+ test_cmp expect actual
+}
+
+
+test_expect_success 'setup' '
+
+ test_commit one &&
+ test_commit two &&
+ test_commit three &&
+ test_commit four
+
+'
+
+test_expect_success 'put' '
+
+test_oidmap "put one 1
+put two 2
+put invalidOid 4
+put three 3" "NULL
+NULL
+Unknown oid: invalidOid
+NULL"
+
+'
+
+test_expect_success 'replace' '
+
+test_oidmap "put one 1
+put two 2
+put three 3
+put invalidOid 4
+put two deux
+put one un" "NULL
+NULL
+NULL
+Unknown oid: invalidOid
+2
+1"
+
+'
+
+test_expect_success 'get' '
+
+test_oidmap "put one 1
+put two 2
+put three 3
+get two
+get four
+get invalidOid
+get one" "NULL
+NULL
+NULL
+2
+NULL
+Unknown oid: invalidOid
+1"
+
+'
+
+test_expect_success 'remove' '
+
+test_oidmap "put one 1
+put two 2
+put three 3
+remove one
+remove two
+remove invalidOid
+remove four" "NULL
+NULL
+NULL
+1
+2
+Unknown oid: invalidOid
+NULL"
+
+'
+
+test_expect_success 'iterate' '
+ test-tool oidmap >actual.raw <<-\EOF &&
+ put one 1
+ put two 2
+ put three 3
+ iterate
+ EOF
+
+ # sort "expect" too so we do not rely on the order of particular oids
+ sort >expect <<-EOF &&
+ NULL
+ NULL
+ NULL
+ $(git rev-parse one) 1
+ $(git rev-parse two) 2
+ $(git rev-parse three) 3
+ EOF
+
+ sort <actual.raw >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t0017-env-helper.sh b/t/t0017-env-helper.sh
new file mode 100755
index 0000000..c1ecf6a
--- /dev/null
+++ b/t/t0017-env-helper.sh
@@ -0,0 +1,99 @@
+#!/bin/sh
+
+test_description='test env--helper'
+
+. ./test-lib.sh
+
+
+test_expect_success 'env--helper usage' '
+ test_must_fail git env--helper &&
+ test_must_fail git env--helper --type=bool &&
+ test_must_fail git env--helper --type=ulong &&
+ test_must_fail git env--helper --type=bool &&
+ test_must_fail git env--helper --type=bool --default &&
+ test_must_fail git env--helper --type=bool --default= &&
+ test_must_fail git env--helper --defaultxyz
+'
+
+test_expect_success 'env--helper bad default values' '
+ test_must_fail git env--helper --type=bool --default=1xyz MISSING &&
+ test_must_fail git env--helper --type=ulong --default=1xyz MISSING
+'
+
+test_expect_success 'env--helper --type=bool' '
+ # Test various --default bool values
+ echo true >expected &&
+ git env--helper --type=bool --default=1 MISSING >actual &&
+ test_cmp expected actual &&
+ git env--helper --type=bool --default=yes MISSING >actual &&
+ test_cmp expected actual &&
+ git env--helper --type=bool --default=true MISSING >actual &&
+ test_cmp expected actual &&
+ echo false >expected &&
+ test_must_fail git env--helper --type=bool --default=0 MISSING >actual &&
+ test_cmp expected actual &&
+ test_must_fail git env--helper --type=bool --default=no MISSING >actual &&
+ test_cmp expected actual &&
+ test_must_fail git env--helper --type=bool --default=false MISSING >actual &&
+ test_cmp expected actual &&
+
+ # No output with --exit-code
+ git env--helper --type=bool --default=true --exit-code MISSING >actual.out 2>actual.err &&
+ test_must_be_empty actual.out &&
+ test_must_be_empty actual.err &&
+ test_must_fail git env--helper --type=bool --default=false --exit-code MISSING >actual.out 2>actual.err &&
+ test_must_be_empty actual.out &&
+ test_must_be_empty actual.err &&
+
+ # Existing variable
+ EXISTS=true git env--helper --type=bool --default=false --exit-code EXISTS >actual.out 2>actual.err &&
+ test_must_be_empty actual.out &&
+ test_must_be_empty actual.err &&
+ test_must_fail \
+ env EXISTS=false \
+ git env--helper --type=bool --default=true --exit-code EXISTS >actual.out 2>actual.err &&
+ test_must_be_empty actual.out &&
+ test_must_be_empty actual.err
+'
+
+test_expect_success 'env--helper --type=ulong' '
+ echo 1234567890 >expected &&
+ git env--helper --type=ulong --default=1234567890 MISSING >actual.out 2>actual.err &&
+ test_cmp expected actual.out &&
+ test_must_be_empty actual.err &&
+
+ echo 0 >expected &&
+ test_must_fail git env--helper --type=ulong --default=0 MISSING >actual &&
+ test_cmp expected actual &&
+
+ git env--helper --type=ulong --default=1234567890 --exit-code MISSING >actual.out 2>actual.err &&
+ test_must_be_empty actual.out &&
+ test_must_be_empty actual.err &&
+
+ EXISTS=1234567890 git env--helper --type=ulong --default=0 EXISTS --exit-code >actual.out 2>actual.err &&
+ test_must_be_empty actual.out &&
+ test_must_be_empty actual.err &&
+
+ echo 1234567890 >expected &&
+ EXISTS=1234567890 git env--helper --type=ulong --default=0 EXISTS >actual.out 2>actual.err &&
+ test_cmp expected actual.out &&
+ test_must_be_empty actual.err
+'
+
+test_expect_success 'env--helper reads config thanks to trace2' '
+ mkdir home &&
+ git config -f home/.gitconfig include.path cycle &&
+ git config -f home/cycle include.path .gitconfig &&
+
+ test_must_fail \
+ env HOME="$(pwd)/home" GIT_TEST_GETTEXT_POISON=false \
+ git config -l 2>err &&
+ grep "exceeded maximum include depth" err &&
+
+ test_must_fail \
+ env HOME="$(pwd)/home" GIT_TEST_GETTEXT_POISON=true \
+ git -C cycle env--helper --type=bool --default=0 --exit-code GIT_TEST_GETTEXT_POISON 2>err &&
+ grep "# GETTEXT POISON #" err
+'
+
+test_done
diff --git a/t/t0018-advice.sh b/t/t0018-advice.sh
new file mode 100755
index 0000000..e03554d
--- /dev/null
+++ b/t/t0018-advice.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+test_description='Test advise_if_enabled functionality'
+
+. ./test-lib.sh
+
+test_expect_success 'advice should be printed when config variable is unset' '
+ cat >expect <<-\EOF &&
+ hint: This is a piece of advice
+ hint: Disable this message with "git config advice.nestedTag false"
+ EOF
+ test-tool advise "This is a piece of advice" 2>actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'advice should be printed when config variable is set to true' '
+ cat >expect <<-\EOF &&
+ hint: This is a piece of advice
+ hint: Disable this message with "git config advice.nestedTag false"
+ EOF
+ test_config advice.nestedTag true &&
+ test-tool advise "This is a piece of advice" 2>actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'advice should not be printed when config variable is set to false' '
+ test_config advice.nestedTag false &&
+ test-tool advise "This is a piece of advice" 2>actual &&
+ test_must_be_empty actual
+'
+
+test_done
diff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh
index 854da0a..b63ba62 100755
--- a/t/t0020-crlf.sh
+++ b/t/t0020-crlf.sh
@@ -159,8 +159,8 @@ test_expect_success 'checkout with autocrlf=input' '
rm -f tmp one dir/two three &&
git config core.autocrlf input &&
git read-tree --reset -u HEAD &&
- test_must_fail has_cr one &&
- test_must_fail has_cr dir/two &&
+ ! has_cr one &&
+ ! has_cr dir/two &&
git update-index -- one dir/two &&
test "$one" = $(git hash-object --stdin <one) &&
test "$two" = $(git hash-object --stdin <dir/two) &&
@@ -237,9 +237,9 @@ test_expect_success '.gitattributes says two is binary' '
git config core.autocrlf true &&
git read-tree --reset -u HEAD &&
- test_must_fail has_cr dir/two &&
+ ! has_cr dir/two &&
verbose has_cr one &&
- test_must_fail has_cr three
+ ! has_cr three
'
test_expect_success '.gitattributes says two is input' '
@@ -248,7 +248,7 @@ test_expect_success '.gitattributes says two is input' '
echo "two crlf=input" >.gitattributes &&
git read-tree --reset -u HEAD &&
- test_must_fail has_cr dir/two
+ ! has_cr dir/two
'
test_expect_success '.gitattributes says two and three are text' '
@@ -270,7 +270,7 @@ test_expect_success 'in-tree .gitattributes (1)' '
rm -rf tmp one dir .gitattributes patch.file three &&
git read-tree --reset -u HEAD &&
- test_must_fail has_cr one &&
+ ! has_cr one &&
verbose has_cr three
'
@@ -280,7 +280,7 @@ test_expect_success 'in-tree .gitattributes (2)' '
git read-tree --reset HEAD &&
git checkout-index -f -q -u -a &&
- test_must_fail has_cr one &&
+ ! has_cr one &&
verbose has_cr three
'
@@ -291,7 +291,7 @@ test_expect_success 'in-tree .gitattributes (3)' '
git checkout-index -u .gitattributes &&
git checkout-index -u one dir/two three &&
- test_must_fail has_cr one &&
+ ! has_cr one &&
verbose has_cr three
'
@@ -302,7 +302,7 @@ test_expect_success 'in-tree .gitattributes (4)' '
git checkout-index -u one dir/two three &&
git checkout-index -u .gitattributes &&
- test_must_fail has_cr one &&
+ ! has_cr one &&
verbose has_cr three
'
diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
index fd5f1ac..4bfffa9 100755
--- a/t/t0021-conversion.sh
+++ b/t/t0021-conversion.sh
@@ -24,7 +24,7 @@ generate_random_characters () {
}
file_size () {
- perl -e 'print -s $ARGV[0]' "$1"
+ test-tool path-utils file-size "$1"
}
filter_git () {
@@ -35,7 +35,7 @@ filter_git () {
# Compare two files and ensure that `clean` and `smudge` respectively are
# called at least once if specified in the `expect` file. The actual
# invocation count is not relevant because their number can vary.
-# c.f. http://public-inbox.org/git/xmqqshv18i8i.fsf@gitster.mtv.corp.google.com/
+# c.f. http://lore.kernel.org/git/xmqqshv18i8i.fsf@gitster.mtv.corp.google.com/
test_cmp_count () {
expect=$1
actual=$2
@@ -50,7 +50,7 @@ test_cmp_count () {
# Compare two files but exclude all `clean` invocations because Git can
# call `clean` zero or more times.
-# c.f. http://public-inbox.org/git/xmqqshv18i8i.fsf@gitster.mtv.corp.google.com/
+# c.f. http://lore.kernel.org/git/xmqqshv18i8i.fsf@gitster.mtv.corp.google.com/
test_cmp_exclude_clean () {
expect=$1
actual=$2
@@ -364,6 +364,10 @@ test_expect_success PERL 'required process filter should filter data' '
S=$(file_size test.r) &&
S2=$(file_size test2.r) &&
S3=$(file_size "testsubdir/test3 '\''sq'\'',\$x=.r") &&
+ M=$(git hash-object test.r) &&
+ M2=$(git hash-object test2.r) &&
+ M3=$(git hash-object "testsubdir/test3 '\''sq'\'',\$x=.r") &&
+ EMPTY=$(git hash-object /dev/null) &&
filter_git add . &&
cat >expected.log <<-EOF &&
@@ -378,18 +382,23 @@ test_expect_success PERL 'required process filter should filter data' '
test_cmp_count expected.log debug.log &&
git commit -m "test commit 2" &&
+ MASTER=$(git rev-parse --verify master) &&
+ META="ref=refs/heads/master treeish=$MASTER" &&
rm -f test2.r "testsubdir/test3 '\''sq'\'',\$x=.r" &&
filter_git checkout --quiet --no-progress . &&
cat >expected.log <<-EOF &&
START
init handshake complete
- IN: smudge test2.r $S2 [OK] -- OUT: $S2 . [OK]
- IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $S3 [OK] -- OUT: $S3 . [OK]
+ IN: smudge test2.r blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
+ IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r blob=$M3 $S3 [OK] -- OUT: $S3 . [OK]
STOP
EOF
test_cmp_exclude_clean expected.log debug.log &&
+ # Make sure that the file appears dirty, so checkout below has to
+ # run the configured filter.
+ touch test.r &&
filter_git checkout --quiet --no-progress empty-branch &&
cat >expected.log <<-EOF &&
START
@@ -403,10 +412,10 @@ test_expect_success PERL 'required process filter should filter data' '
cat >expected.log <<-EOF &&
START
init handshake complete
- IN: smudge test.r $S [OK] -- OUT: $S . [OK]
- IN: smudge test2.r $S2 [OK] -- OUT: $S2 . [OK]
- IN: smudge test4-empty.r 0 [OK] -- OUT: 0 [OK]
- IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $S3 [OK] -- OUT: $S3 . [OK]
+ IN: smudge test.r $META blob=$M $S [OK] -- OUT: $S . [OK]
+ IN: smudge test2.r $META blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
+ IN: smudge test4-empty.r $META blob=$EMPTY 0 [OK] -- OUT: 0 [OK]
+ IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $META blob=$M3 $S3 [OK] -- OUT: $S3 . [OK]
STOP
EOF
test_cmp_exclude_clean expected.log debug.log &&
@@ -417,6 +426,117 @@ test_expect_success PERL 'required process filter should filter data' '
)
'
+test_expect_success PERL 'required process filter should filter data for various subcommands' '
+ test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
+ test_config_global filter.protocol.required true &&
+ (
+ cd repo &&
+
+ S=$(file_size test.r) &&
+ S2=$(file_size test2.r) &&
+ S3=$(file_size "testsubdir/test3 '\''sq'\'',\$x=.r") &&
+ M=$(git hash-object test.r) &&
+ M2=$(git hash-object test2.r) &&
+ M3=$(git hash-object "testsubdir/test3 '\''sq'\'',\$x=.r") &&
+ EMPTY=$(git hash-object /dev/null) &&
+
+ MASTER=$(git rev-parse --verify master) &&
+
+ cp "$TEST_ROOT/test.o" test5.r &&
+ git add test5.r &&
+ git commit -m "test commit 3" &&
+ git checkout empty-branch &&
+ filter_git rebase --onto empty-branch master^^ master &&
+ MASTER2=$(git rev-parse --verify master) &&
+ META="ref=refs/heads/master treeish=$MASTER2" &&
+ cat >expected.log <<-EOF &&
+ START
+ init handshake complete
+ IN: smudge test.r $META blob=$M $S [OK] -- OUT: $S . [OK]
+ IN: smudge test2.r $META blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
+ IN: smudge test4-empty.r $META blob=$EMPTY 0 [OK] -- OUT: 0 [OK]
+ IN: smudge test5.r $META blob=$M $S [OK] -- OUT: $S . [OK]
+ IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $META blob=$M3 $S3 [OK] -- OUT: $S3 . [OK]
+ STOP
+ EOF
+ test_cmp_exclude_clean expected.log debug.log &&
+
+ git reset --hard empty-branch &&
+ filter_git reset --hard $MASTER &&
+ META="treeish=$MASTER" &&
+ cat >expected.log <<-EOF &&
+ START
+ init handshake complete
+ IN: smudge test.r $META blob=$M $S [OK] -- OUT: $S . [OK]
+ IN: smudge test2.r $META blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
+ IN: smudge test4-empty.r $META blob=$EMPTY 0 [OK] -- OUT: 0 [OK]
+ IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $META blob=$M3 $S3 [OK] -- OUT: $S3 . [OK]
+ STOP
+ EOF
+ test_cmp_exclude_clean expected.log debug.log &&
+
+ git branch old-master $MASTER &&
+ git reset --hard empty-branch &&
+ filter_git reset --hard old-master &&
+ META="ref=refs/heads/old-master treeish=$MASTER" &&
+ cat >expected.log <<-EOF &&
+ START
+ init handshake complete
+ IN: smudge test.r $META blob=$M $S [OK] -- OUT: $S . [OK]
+ IN: smudge test2.r $META blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
+ IN: smudge test4-empty.r $META blob=$EMPTY 0 [OK] -- OUT: 0 [OK]
+ IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $META blob=$M3 $S3 [OK] -- OUT: $S3 . [OK]
+ STOP
+ EOF
+ test_cmp_exclude_clean expected.log debug.log &&
+
+ git checkout -b merge empty-branch &&
+ git branch -f master $MASTER2 &&
+ filter_git merge master &&
+ META="treeish=$MASTER2" &&
+ cat >expected.log <<-EOF &&
+ START
+ init handshake complete
+ IN: smudge test.r $META blob=$M $S [OK] -- OUT: $S . [OK]
+ IN: smudge test2.r $META blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
+ IN: smudge test4-empty.r $META blob=$EMPTY 0 [OK] -- OUT: 0 [OK]
+ IN: smudge test5.r $META blob=$M $S [OK] -- OUT: $S . [OK]
+ IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $META blob=$M3 $S3 [OK] -- OUT: $S3 . [OK]
+ STOP
+ EOF
+ test_cmp_exclude_clean expected.log debug.log &&
+
+ filter_git archive master >/dev/null &&
+ META="ref=refs/heads/master treeish=$MASTER2" &&
+ cat >expected.log <<-EOF &&
+ START
+ init handshake complete
+ IN: smudge test.r $META blob=$M $S [OK] -- OUT: $S . [OK]
+ IN: smudge test2.r $META blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
+ IN: smudge test4-empty.r $META blob=$EMPTY 0 [OK] -- OUT: 0 [OK]
+ IN: smudge test5.r $META blob=$M $S [OK] -- OUT: $S . [OK]
+ IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $META blob=$M3 $S3 [OK] -- OUT: $S3 . [OK]
+ STOP
+ EOF
+ test_cmp_exclude_clean expected.log debug.log &&
+
+ TREE="$(git rev-parse $MASTER2^{tree})" &&
+ filter_git archive $TREE >/dev/null &&
+ META="treeish=$TREE" &&
+ cat >expected.log <<-EOF &&
+ START
+ init handshake complete
+ IN: smudge test.r $META blob=$M $S [OK] -- OUT: $S . [OK]
+ IN: smudge test2.r $META blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
+ IN: smudge test4-empty.r $META blob=$EMPTY 0 [OK] -- OUT: 0 [OK]
+ IN: smudge test5.r $META blob=$M $S [OK] -- OUT: $S . [OK]
+ IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $META blob=$M3 $S3 [OK] -- OUT: $S3 . [OK]
+ STOP
+ EOF
+ test_cmp_exclude_clean expected.log debug.log
+ )
+'
+
test_expect_success PERL 'required process filter takes precedence' '
test_config_global filter.protocol.clean false &&
test_config_global filter.protocol.process "rot13-filter.pl debug.log clean" &&
@@ -516,17 +636,22 @@ test_expect_success PERL 'required process filter should process multiple packet
EOF
test_cmp_count expected.log debug.log &&
- rm -f *.file &&
+ M1="blob=$(git hash-object 1pkt_1__.file)" &&
+ M2="blob=$(git hash-object 2pkt_1+1.file)" &&
+ M3="blob=$(git hash-object 2pkt_2-1.file)" &&
+ M4="blob=$(git hash-object 2pkt_2__.file)" &&
+ M5="blob=$(git hash-object 3pkt_2+1.file)" &&
+ rm -f *.file debug.log &&
filter_git checkout --quiet --no-progress -- *.file &&
cat >expected.log <<-EOF &&
START
init handshake complete
- IN: smudge 1pkt_1__.file $(($S )) [OK] -- OUT: $(($S )) . [OK]
- IN: smudge 2pkt_1+1.file $(($S +1)) [OK] -- OUT: $(($S +1)) .. [OK]
- IN: smudge 2pkt_2-1.file $(($S*2-1)) [OK] -- OUT: $(($S*2-1)) .. [OK]
- IN: smudge 2pkt_2__.file $(($S*2 )) [OK] -- OUT: $(($S*2 )) .. [OK]
- IN: smudge 3pkt_2+1.file $(($S*2+1)) [OK] -- OUT: $(($S*2+1)) ... [OK]
+ IN: smudge 1pkt_1__.file $M1 $(($S )) [OK] -- OUT: $(($S )) . [OK]
+ IN: smudge 2pkt_1+1.file $M2 $(($S +1)) [OK] -- OUT: $(($S +1)) .. [OK]
+ IN: smudge 2pkt_2-1.file $M3 $(($S*2-1)) [OK] -- OUT: $(($S*2-1)) .. [OK]
+ IN: smudge 2pkt_2__.file $M4 $(($S*2 )) [OK] -- OUT: $(($S*2 )) .. [OK]
+ IN: smudge 3pkt_2+1.file $M5 $(($S*2+1)) [OK] -- OUT: $(($S*2+1)) ... [OK]
STOP
EOF
test_cmp_exclude_clean expected.log debug.log &&
@@ -575,6 +700,10 @@ test_expect_success PERL 'process filter should restart after unexpected write f
S=$(file_size test.r) &&
S2=$(file_size test2.r) &&
SF=$(file_size smudge-write-fail.r) &&
+ M=$(git hash-object test.r) &&
+ M2=$(git hash-object test2.r) &&
+ MF=$(git hash-object smudge-write-fail.r) &&
+ rm -f debug.log &&
git add . &&
rm -f *.r &&
@@ -588,11 +717,11 @@ test_expect_success PERL 'process filter should restart after unexpected write f
cat >expected.log <<-EOF &&
START
init handshake complete
- IN: smudge smudge-write-fail.r $SF [OK] -- [WRITE FAIL]
+ IN: smudge smudge-write-fail.r blob=$MF $SF [OK] -- [WRITE FAIL]
START
init handshake complete
- IN: smudge test.r $S [OK] -- OUT: $S . [OK]
- IN: smudge test2.r $S2 [OK] -- OUT: $S2 . [OK]
+ IN: smudge test.r blob=$M $S [OK] -- OUT: $S . [OK]
+ IN: smudge test2.r blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
STOP
EOF
test_cmp_exclude_clean expected.log debug.log &&
@@ -626,6 +755,10 @@ test_expect_success PERL 'process filter should not be restarted if it signals a
S=$(file_size test.r) &&
S2=$(file_size test2.r) &&
SE=$(file_size error.r) &&
+ M=$(git hash-object test.r) &&
+ M2=$(git hash-object test2.r) &&
+ ME=$(git hash-object error.r) &&
+ rm -f debug.log &&
git add . &&
rm -f *.r &&
@@ -634,9 +767,9 @@ test_expect_success PERL 'process filter should not be restarted if it signals a
cat >expected.log <<-EOF &&
START
init handshake complete
- IN: smudge error.r $SE [OK] -- [ERROR]
- IN: smudge test.r $S [OK] -- OUT: $S . [OK]
- IN: smudge test2.r $S2 [OK] -- OUT: $S2 . [OK]
+ IN: smudge error.r blob=$ME $SE [OK] -- [ERROR]
+ IN: smudge test.r blob=$M $S [OK] -- OUT: $S . [OK]
+ IN: smudge test2.r blob=$M2 $S2 [OK] -- OUT: $S2 . [OK]
STOP
EOF
test_cmp_exclude_clean expected.log debug.log &&
@@ -662,18 +795,21 @@ test_expect_success PERL 'process filter abort stops processing of all further f
echo "error this blob and all future blobs" >abort.o &&
cp abort.o abort.r &&
+ M="blob=$(git hash-object abort.r)" &&
+ rm -f debug.log &&
SA=$(file_size abort.r) &&
git add . &&
rm -f *.r &&
+
# Note: This test assumes that Git filters files in alphabetical
# order ("abort.r" before "test.r").
filter_git checkout --quiet --no-progress . &&
cat >expected.log <<-EOF &&
START
init handshake complete
- IN: smudge abort.r $SA [OK] -- [ABORT]
+ IN: smudge abort.r $M $SA [OK] -- [ABORT]
STOP
EOF
test_cmp_exclude_clean expected.log debug.log &&
@@ -724,27 +860,29 @@ test_expect_success PERL 'delayed checkout in process filter' '
) &&
S=$(file_size "$TEST_ROOT/test.o") &&
+ PM="ref=refs/heads/master treeish=$(git -C repo rev-parse --verify master) " &&
+ M="${PM}blob=$(git -C repo rev-parse --verify master:test.a)" &&
cat >a.exp <<-EOF &&
START
init handshake complete
- IN: smudge test.a $S [OK] -- OUT: $S . [OK]
- IN: smudge test-delay10.a $S [OK] -- [DELAYED]
- IN: smudge test-delay11.a $S [OK] -- [DELAYED]
- IN: smudge test-delay20.a $S [OK] -- [DELAYED]
+ IN: smudge test.a $M $S [OK] -- OUT: $S . [OK]
+ IN: smudge test-delay10.a $M $S [OK] -- [DELAYED]
+ IN: smudge test-delay11.a $M $S [OK] -- [DELAYED]
+ IN: smudge test-delay20.a $M $S [OK] -- [DELAYED]
IN: list_available_blobs test-delay10.a test-delay11.a [OK]
- IN: smudge test-delay10.a 0 [OK] -- OUT: $S . [OK]
- IN: smudge test-delay11.a 0 [OK] -- OUT: $S . [OK]
+ IN: smudge test-delay10.a $M 0 [OK] -- OUT: $S . [OK]
+ IN: smudge test-delay11.a $M 0 [OK] -- OUT: $S . [OK]
IN: list_available_blobs test-delay20.a [OK]
- IN: smudge test-delay20.a 0 [OK] -- OUT: $S . [OK]
+ IN: smudge test-delay20.a $M 0 [OK] -- OUT: $S . [OK]
IN: list_available_blobs [OK]
STOP
EOF
cat >b.exp <<-EOF &&
START
init handshake complete
- IN: smudge test-delay10.b $S [OK] -- [DELAYED]
+ IN: smudge test-delay10.b $M $S [OK] -- [DELAYED]
IN: list_available_blobs test-delay10.b [OK]
- IN: smudge test-delay10.b 0 [OK] -- OUT: $S . [OK]
+ IN: smudge test-delay10.b $M 0 [OK] -- OUT: $S . [OK]
IN: list_available_blobs [OK]
STOP
EOF
@@ -764,8 +902,11 @@ test_expect_success PERL 'delayed checkout in process filter' '
rm *.a *.b &&
filter_git checkout . &&
- test_cmp_count ../a.exp a.log &&
- test_cmp_count ../b.exp b.log &&
+ # We are not checking out a ref here, so filter out ref metadata.
+ sed -e "s!$PM!!" ../a.exp >a.exp.filtered &&
+ sed -e "s!$PM!!" ../b.exp >b.exp.filtered &&
+ test_cmp_count a.exp.filtered a.log &&
+ test_cmp_count b.exp.filtered b.log &&
test_cmp_committed_rot13 "$TEST_ROOT/test.o" test.a &&
test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay10.a &&
@@ -792,7 +933,6 @@ test_expect_success PERL 'missing file in delayed checkout' '
rm -rf repo-cloned &&
test_must_fail git clone repo repo-cloned 2>git-stderr.log &&
- cat git-stderr.log &&
grep "error: .missing-delay\.a. was not filtered properly" git-stderr.log
'
diff --git a/t/t0021/rot13-filter.pl b/t/t0021/rot13-filter.pl
index 4701072..cd32a82 100644
--- a/t/t0021/rot13-filter.pl
+++ b/t/t0021/rot13-filter.pl
@@ -135,7 +135,13 @@ while (1) {
if ( exists $DELAY{$pathname} and $DELAY{$pathname}{"requested"} == 0 ) {
$DELAY{$pathname}{"requested"} = 1;
}
+ } elsif ($buffer =~ /^(ref|treeish|blob)=/) {
+ print $debug " $buffer";
} else {
+ # In general, filters need to be graceful about
+ # new metadata, since it's documented that we
+ # can pass any key-value pairs, but for tests,
+ # let's be a little stricter.
die "Unknown message '$buffer'";
}
diff --git a/t/t0025-crlf-renormalize.sh b/t/t0025-crlf-renormalize.sh
index 9d9e02a..e13363a 100755
--- a/t/t0025-crlf-renormalize.sh
+++ b/t/t0025-crlf-renormalize.sh
@@ -27,4 +27,13 @@ test_expect_success 'renormalize CRLF in repo' '
test_cmp expect actual
'
+test_expect_success 'ignore-errors not mistaken for renormalize' '
+ git reset --hard &&
+ echo "*.txt text=auto" >.gitattributes &&
+ git ls-files --eol >expect &&
+ git add --ignore-errors "*.txt" &&
+ git ls-files --eol >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t0027-auto-crlf.sh b/t/t0027-auto-crlf.sh
index beb5927..9fcd56f 100755
--- a/t/t0027-auto-crlf.sh
+++ b/t/t0027-auto-crlf.sh
@@ -15,8 +15,10 @@ compare_ws_file () {
pfx=$1
exp=$2.expect
act=$pfx.actual.$3
- tr '\015\000abcdef0123456789' QN00000000000000000 <"$2" >"$exp" &&
- tr '\015\000abcdef0123456789' QN00000000000000000 <"$3" >"$act" &&
+ tr '\015\000abcdef0123456789' QN00000000000000000 <"$2" |
+ sed -e "s/0000*/$ZERO_OID/" >"$exp" &&
+ tr '\015\000abcdef0123456789' QN00000000000000000 <"$3" |
+ sed -e "s/0000*/$ZERO_OID/" >"$act" &&
test_cmp "$exp" "$act" &&
rm "$exp" "$act"
}
@@ -213,7 +215,7 @@ stats_ascii () {
}
-# contruct the attr/ returned by git ls-files --eol
+# construct the attr/ returned by git ls-files --eol
# Take none (=empty), one or two args
# convert.c: eol=XX overrides text=auto
attr_ascii () {
@@ -293,9 +295,9 @@ checkout_files () {
do
rm crlf_false_attr__$f.txt &&
if test -z "$ceol"; then
- git checkout crlf_false_attr__$f.txt
+ git checkout -- crlf_false_attr__$f.txt
else
- git -c core.eol=$ceol checkout crlf_false_attr__$f.txt
+ git -c core.eol=$ceol checkout -- crlf_false_attr__$f.txt
fi
done
diff --git a/t/t0028-working-tree-encoding.sh b/t/t0028-working-tree-encoding.sh
index 7e87b5a..bfc4fb9 100755
--- a/t/t0028-working-tree-encoding.sh
+++ b/t/t0028-working-tree-encoding.sh
@@ -6,14 +6,41 @@ test_description='working-tree-encoding conversion via gitattributes'
GIT_TRACE_WORKING_TREE_ENCODING=1 && export GIT_TRACE_WORKING_TREE_ENCODING
+test_lazy_prereq NO_UTF16_BOM '
+ test $(printf abc | iconv -f UTF-8 -t UTF-16 | wc -c) = 6
+'
+
+test_lazy_prereq NO_UTF32_BOM '
+ test $(printf abc | iconv -f UTF-8 -t UTF-32 | wc -c) = 12
+'
+
+write_utf16 () {
+ if test_have_prereq NO_UTF16_BOM
+ then
+ printf '\376\377'
+ fi &&
+ iconv -f UTF-8 -t UTF-16
+}
+
+write_utf32 () {
+ if test_have_prereq NO_UTF32_BOM
+ then
+ printf '\0\0\376\377'
+ fi &&
+ iconv -f UTF-8 -t UTF-32
+}
+
test_expect_success 'setup test files' '
git config core.eol lf &&
text="hallo there!\ncan you read me?" &&
echo "*.utf16 text working-tree-encoding=utf-16" >.gitattributes &&
+ echo "*.utf16lebom text working-tree-encoding=UTF-16LE-BOM" >>.gitattributes &&
printf "$text" >test.utf8.raw &&
- printf "$text" | iconv -f UTF-8 -t UTF-16 >test.utf16.raw &&
- printf "$text" | iconv -f UTF-8 -t UTF-32 >test.utf32.raw &&
+ printf "$text" | write_utf16 >test.utf16.raw &&
+ printf "$text" | write_utf32 >test.utf32.raw &&
+ printf "\377\376" >test.utf16lebom.raw &&
+ printf "$text" | iconv -f UTF-8 -t UTF-16LE >>test.utf16lebom.raw &&
# Line ending tests
printf "one\ntwo\nthree\n" >lf.utf8.raw &&
@@ -22,17 +49,18 @@ test_expect_success 'setup test files' '
# BOM tests
printf "\0a\0b\0c" >nobom.utf16be.raw &&
printf "a\0b\0c\0" >nobom.utf16le.raw &&
- printf "\376\777\0a\0b\0c" >bebom.utf16be.raw &&
- printf "\777\376a\0b\0c\0" >lebom.utf16le.raw &&
+ printf "\376\377\0a\0b\0c" >bebom.utf16be.raw &&
+ printf "\377\376a\0b\0c\0" >lebom.utf16le.raw &&
printf "\0\0\0a\0\0\0b\0\0\0c" >nobom.utf32be.raw &&
printf "a\0\0\0b\0\0\0c\0\0\0" >nobom.utf32le.raw &&
- printf "\0\0\376\777\0\0\0a\0\0\0b\0\0\0c" >bebom.utf32be.raw &&
- printf "\777\376\0\0a\0\0\0b\0\0\0c\0\0\0" >lebom.utf32le.raw &&
+ printf "\0\0\376\377\0\0\0a\0\0\0b\0\0\0c" >bebom.utf32be.raw &&
+ printf "\377\376\0\0a\0\0\0b\0\0\0c\0\0\0" >lebom.utf32le.raw &&
# Add only UTF-16 file, we will add the UTF-32 file later
cp test.utf16.raw test.utf16 &&
cp test.utf32.raw test.utf32 &&
- git add .gitattributes test.utf16 &&
+ cp test.utf16lebom.raw test.utf16lebom &&
+ git add .gitattributes test.utf16 test.utf16lebom &&
git commit -m initial
'
@@ -51,6 +79,12 @@ test_expect_success 're-encode to UTF-16 on checkout' '
test_cmp_bin test.utf16.raw test.utf16
'
+test_expect_success 're-encode to UTF-16-LE-BOM on checkout' '
+ rm test.utf16lebom &&
+ git checkout test.utf16lebom &&
+ test_cmp_bin test.utf16lebom.raw test.utf16lebom
+'
+
test_expect_success 'check $GIT_DIR/info/attributes support' '
test_when_finished "rm -f test.utf32.git" &&
test_when_finished "git reset --hard HEAD" &&
@@ -114,8 +148,8 @@ do
test_when_finished "rm -f crlf.utf${i}.raw lf.utf${i}.raw" &&
test_when_finished "git reset --hard HEAD^" &&
- cat lf.utf8.raw | iconv -f UTF-8 -t UTF-${i} >lf.utf${i}.raw &&
- cat crlf.utf8.raw | iconv -f UTF-8 -t UTF-${i} >crlf.utf${i}.raw &&
+ cat lf.utf8.raw | write_utf${i} >lf.utf${i}.raw &&
+ cat crlf.utf8.raw | write_utf${i} >crlf.utf${i}.raw &&
cp crlf.utf${i}.raw eol.utf${i} &&
cat >expectIndexLF <<-EOF &&
@@ -213,7 +247,7 @@ test_expect_success ICONV_SHIFT_JIS 'check roundtrip encoding' '
text="hallo there!\nroundtrip test here!" &&
printf "$text" | iconv -f UTF-8 -t SHIFT-JIS >roundtrip.shift &&
- printf "$text" | iconv -f UTF-8 -t UTF-16 >roundtrip.utf16 &&
+ printf "$text" | write_utf16 >roundtrip.utf16 &&
echo "*.shift text working-tree-encoding=SHIFT-JIS" >>.gitattributes &&
# SHIFT-JIS encoded files are round-trip checked by default...
@@ -246,4 +280,43 @@ test_expect_success ICONV_SHIFT_JIS 'check roundtrip encoding' '
git reset
'
+# $1: checkout encoding
+# $2: test string
+# $3: binary test string in checkout encoding
+test_commit_utf8_checkout_other () {
+ encoding="$1"
+ orig_string="$2"
+ expect_bytes="$3"
+
+ test_expect_success "Commit UTF-8, checkout $encoding" '
+ test_when_finished "git checkout HEAD -- .gitattributes" &&
+
+ test_ext="commit_utf8_checkout_$encoding" &&
+ test_file="test.$test_ext" &&
+
+ # Commit as UTF-8
+ echo "*.$test_ext text working-tree-encoding=UTF-8" >.gitattributes &&
+ printf "$orig_string" >$test_file &&
+ git add $test_file &&
+ git commit -m "Test data" &&
+
+ # Checkout in tested encoding
+ rm $test_file &&
+ echo "*.$test_ext text working-tree-encoding=$encoding" >.gitattributes &&
+ git checkout HEAD -- $test_file &&
+
+ # Test
+ printf $expect_bytes >$test_file.raw &&
+ test_cmp_bin $test_file.raw $test_file
+ '
+}
+
+test_commit_utf8_checkout_other "UTF-8" "Test Тест" "\124\145\163\164\040\320\242\320\265\321\201\321\202"
+test_commit_utf8_checkout_other "UTF-16LE" "Test Тест" "\124\000\145\000\163\000\164\000\040\000\042\004\065\004\101\004\102\004"
+test_commit_utf8_checkout_other "UTF-16BE" "Test Тест" "\000\124\000\145\000\163\000\164\000\040\004\042\004\065\004\101\004\102"
+test_commit_utf8_checkout_other "UTF-16LE-BOM" "Test Тест" "\377\376\124\000\145\000\163\000\164\000\040\000\042\004\065\004\101\004\102\004"
+test_commit_utf8_checkout_other "UTF-16BE-BOM" "Test Тест" "\376\377\000\124\000\145\000\163\000\164\000\040\004\042\004\065\004\101\004\102"
+test_commit_utf8_checkout_other "UTF-32LE" "Test Тест" "\124\000\000\000\145\000\000\000\163\000\000\000\164\000\000\000\040\000\000\000\042\004\000\000\065\004\000\000\101\004\000\000\102\004\000\000"
+test_commit_utf8_checkout_other "UTF-32BE" "Test Тест" "\000\000\000\124\000\000\000\145\000\000\000\163\000\000\000\164\000\000\000\040\000\000\004\042\000\000\004\065\000\000\004\101\000\000\004\102"
+
test_done
diff --git a/t/t0030-stripspace.sh b/t/t0030-stripspace.sh
index 5ce47e8..0c24a0f 100755
--- a/t/t0030-stripspace.sh
+++ b/t/t0030-stripspace.sh
@@ -430,9 +430,15 @@ test_expect_success '-c with changed comment char' '
test_expect_success '-c with comment char defined in .git/config' '
test_config core.commentchar = &&
printf "= foo\n" >expect &&
- printf "foo" | (
- mkdir sub && cd sub && git stripspace -c
- ) >actual &&
+ rm -fr sub &&
+ mkdir sub &&
+ printf "foo" | git -C sub stripspace -c >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '-c outside git repository' '
+ printf "# foo\n" >expect &&
+ printf "foo" | nongit git stripspace -c >actual &&
test_cmp expect actual
'
diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
index f5b1086..f8178ee 100755
--- a/t/t0040-parse-options.sh
+++ b/t/t0040-parse-options.sh
@@ -23,6 +23,8 @@ usage: test-tool parse-options <options>
-j <n> get a integer, too
-m, --magnitude <n> get a magnitude
--set23 set integer to 23
+ --mode1 set integer to 1 (cmdmode option)
+ --mode2 set integer to 2 (cmdmode option)
-L, --length <str> get length of <str>
-F, --file <file> set file to <file>
@@ -48,6 +50,12 @@ Standard options
-q, --quiet be quiet
--expect <string> expected output in the variable dump
+Alias
+ -A, --alias-source <string>
+ get a string
+ -Z, --alias-target <string>
+ alias of --alias-source
+
EOF
test_expect_success 'test help' '
@@ -203,41 +211,56 @@ file: (not set)
EOF
test_expect_success 'unambiguously abbreviated option' '
+ GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
test-tool parse-options --int 2 --boolean --no-bo >output 2>output.err &&
test_must_be_empty output.err &&
test_cmp expect output
'
test_expect_success 'unambiguously abbreviated option with "="' '
+ GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
test-tool parse-options --expect="integer: 2" --int=2
'
test_expect_success 'ambiguously abbreviated option' '
- test_expect_code 129 test-tool parse-options --strin 123
+ test_expect_code 129 env GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
+ test-tool parse-options --strin 123
'
test_expect_success 'non ambiguous option (after two options it abbreviates)' '
+ GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
test-tool parse-options --expect="string: 123" --st 123
'
+test_expect_success 'Alias options do not contribute to abbreviation' '
+ test-tool parse-options --alias-source 123 >output &&
+ grep "^string: 123" output &&
+ test-tool parse-options --alias-target 123 >output &&
+ grep "^string: 123" output &&
+ test_must_fail test-tool parse-options --alias &&
+ GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
+ test-tool parse-options --alias 123 >output &&
+ grep "^string: 123" output
+'
+
cat >typo.err <<\EOF
-error: did you mean `--boolean` (with two dashes ?)
+error: did you mean `--boolean` (with two dashes)?
EOF
test_expect_success 'detect possible typos' '
test_must_fail test-tool parse-options -boolean >output 2>output.err &&
test_must_be_empty output &&
- test_cmp typo.err output.err
+ test_i18ncmp typo.err output.err
'
cat >typo.err <<\EOF
-error: did you mean `--ambiguous` (with two dashes ?)
+error: did you mean `--ambiguous` (with two dashes)?
EOF
test_expect_success 'detect possible typos' '
test_must_fail test-tool parse-options -ambiguous >output 2>output.err &&
test_must_be_empty output &&
- test_cmp typo.err output.err
+ test_i18ncmp typo.err output.err
'
test_expect_success 'keep some options as arguments' '
@@ -303,6 +326,22 @@ test_expect_success 'OPT_NEGBIT() works' '
test-tool parse-options --expect="boolean: 6" -bb --no-neg-or4
'
+test_expect_success 'OPT_CMDMODE() works' '
+ test-tool parse-options --expect="integer: 1" --mode1
+'
+
+test_expect_success 'OPT_CMDMODE() detects incompatibility' '
+ test_must_fail test-tool parse-options --mode1 --mode2 >output 2>output.err &&
+ test_must_be_empty output &&
+ test_i18ngrep "incompatible with --mode" output.err
+'
+
+test_expect_success 'OPT_CMDMODE() detects incompatibility with something else' '
+ test_must_fail test-tool parse-options --set23 --mode2 >output 2>output.err &&
+ test_must_be_empty output &&
+ test_i18ngrep "incompatible with something else" output.err
+'
+
test_expect_success 'OPT_COUNTUP() with PARSE_OPT_NODASH works' '
test-tool parse-options --expect="boolean: 6" + + + + + +
'
@@ -325,6 +364,7 @@ file: (not set)
EOF
test_expect_success 'negation of OPT_NONEG flags is not ambiguous' '
+ GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
test-tool parse-options --no-ambig >output 2>output.err &&
test_must_be_empty output.err &&
test_cmp expect output
@@ -370,4 +410,18 @@ test_expect_success '--no-verbose resets multiple verbose to 0' '
test-tool parse-options --expect="verbose: 0" -v -v -v --no-verbose
'
+test_expect_success 'GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS works' '
+ GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
+ test-tool parse-options --ye &&
+ test_must_fail env GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=true \
+ test-tool parse-options --ye
+'
+
+test_expect_success '--end-of-options treats remainder as args' '
+ test-tool parse-options \
+ --expect="verbose: -1" \
+ --expect="arg 00: --verbose" \
+ --end-of-options --verbose
+'
+
test_done
diff --git a/t/t0050-filesystem.sh b/t/t0050-filesystem.sh
index 192c94e..608673f 100755
--- a/t/t0050-filesystem.sh
+++ b/t/t0050-filesystem.sh
@@ -131,4 +131,24 @@ $test_unicode 'merge (silent unicode normalization)' '
git merge topic
'
+test_expect_success CASE_INSENSITIVE_FS 'checkout with no pathspec and a case insensitive fs' '
+ git init repo &&
+ (
+ cd repo &&
+
+ >Gitweb &&
+ git add Gitweb &&
+ git commit -m "add Gitweb" &&
+
+ git checkout --orphan todo &&
+ git reset --hard &&
+ mkdir -p gitweb/subdir &&
+ >gitweb/subdir/file &&
+ git add gitweb &&
+ git commit -m "add gitweb/subdir/file" &&
+
+ git checkout master
+ )
+'
+
test_done
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index b193ed4..56db5c8 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -294,9 +294,13 @@ test_git_path GIT_OBJECT_DIRECTORY=foo objects/foo foo/foo
test_git_path GIT_OBJECT_DIRECTORY=foo objects2 .git/objects2
test_expect_success 'setup common repository' 'git --git-dir=bar init'
test_git_path GIT_COMMON_DIR=bar index .git/index
+test_git_path GIT_COMMON_DIR=bar index.lock .git/index.lock
test_git_path GIT_COMMON_DIR=bar HEAD .git/HEAD
test_git_path GIT_COMMON_DIR=bar logs/HEAD .git/logs/HEAD
+test_git_path GIT_COMMON_DIR=bar logs/HEAD.lock .git/logs/HEAD.lock
test_git_path GIT_COMMON_DIR=bar logs/refs/bisect/foo .git/logs/refs/bisect/foo
+test_git_path GIT_COMMON_DIR=bar logs/refs bar/logs/refs
+test_git_path GIT_COMMON_DIR=bar logs/refs/ bar/logs/refs/
test_git_path GIT_COMMON_DIR=bar logs/refs/bisec/foo bar/logs/refs/bisec/foo
test_git_path GIT_COMMON_DIR=bar logs/refs/bisec bar/logs/refs/bisec
test_git_path GIT_COMMON_DIR=bar logs/refs/bisectfoo bar/logs/refs/bisectfoo
@@ -465,11 +469,15 @@ test_expect_success 'match .gitmodules' '
'
test_expect_success MINGW 'is_valid_path() on Windows' '
- test-tool path-utils is_valid_path \
+ test-tool path-utils is_valid_path \
win32 \
"win32 x" \
../hello.txt \
C:\\git \
+ comm \
+ conout.c \
+ com0.c \
+ lptN \
\
--not \
"win32 " \
@@ -477,7 +485,14 @@ test_expect_success MINGW 'is_valid_path() on Windows' '
"win32." \
"win32 . ." \
.../hello.txt \
- colon:test
+ colon:test \
+ "AUX.c" \
+ "abc/conOut\$ .xyz/test" \
+ lpt8 \
+ com9.c \
+ "lpt*" \
+ Nul \
+ "PRN./abc"
'
test_done
diff --git a/t/t0061-run-command.sh b/t/t0061-run-command.sh
index 96bf6d6..7d59967 100755
--- a/t/t0061-run-command.sh
+++ b/t/t0061-run-command.sh
@@ -12,6 +12,10 @@ cat >hello-script <<-EOF
cat hello-script
EOF
+test_expect_success MINGW 'subprocess inherits only std handles' '
+ test-tool run-command inherited-handle
+'
+
test_expect_success 'start_command reports ENOENT (slash)' '
test-tool run-command start-command-ENOENT ./does-not-exist 2>err &&
test_i18ngrep "\./does-not-exist" err
@@ -31,7 +35,15 @@ test_expect_success 'run_command can run a command' '
test_must_be_empty err
'
-test_expect_success 'run_command is restricted to PATH' '
+
+test_lazy_prereq RUNS_COMMANDS_FROM_PWD '
+ write_script runs-commands-from-pwd <<-\EOF &&
+ true
+ EOF
+ runs-commands-from-pwd >/dev/null 2>&1
+'
+
+test_expect_success !RUNS_COMMANDS_FROM_PWD 'run_command is restricted to PATH' '
write_script should-not-run <<-\EOF &&
echo yikes
EOF
@@ -158,7 +170,8 @@ test_trace () {
expect="$1"
shift
GIT_TRACE=1 test-tool run-command "$@" run-command true 2>&1 >/dev/null | \
- sed -e 's/.* run_command: //' -e '/trace: .*/d' >actual &&
+ sed -e 's/.* run_command: //' -e '/trace: .*/d' \
+ -e '/RUNTIME_PREFIX requested/d' >actual &&
echo "$expect true" >expect &&
test_cmp expect actual
}
@@ -191,4 +204,33 @@ test_expect_success 'GIT_TRACE with environment variables' '
)
'
+test_expect_success MINGW 'verify curlies are quoted properly' '
+ : force the rev-parse through the MSYS2 Bash &&
+ git -c alias.r="!git rev-parse" r -- a{b}c >actual &&
+ cat >expect <<-\EOF &&
+ --
+ a{b}c
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success MINGW 'can spawn .bat with argv[0] containing spaces' '
+ bat="$TRASH_DIRECTORY/bat with spaces in name.bat" &&
+
+ # Every .bat invocation will log its arguments to file "out"
+ rm -f out &&
+ echo "echo %* >>out" >"$bat" &&
+
+ # Ask git to invoke .bat; clone will fail due to fake SSH helper
+ test_must_fail env GIT_SSH="$bat" git clone myhost:src ssh-clone &&
+
+ # Spawning .bat can fail if there are two quoted cmd.exe arguments.
+ # .bat itself is first (due to spaces in name), so just one more is
+ # needed to verify. GIT_SSH will invoke .bat multiple times:
+ # 1) -G myhost
+ # 2) myhost "git-upload-pack src"
+ # First invocation will always succeed. Test the second one.
+ grep "git-upload-pack" out
+'
+
test_done
diff --git a/t/t0064-sha1-array.sh b/t/t0064-sha1-array.sh
index 5dda570..45685af 100755
--- a/t/t0064-sha1-array.sh
+++ b/t/t0064-sha1-array.sh
@@ -18,7 +18,7 @@ test_expect_success 'ordered enumeration' '
{
echoid append 88 44 aa 55 &&
echo for_each_unique
- } | test-tool sha1-array >actual &&
+ } | test-tool oid-array >actual &&
test_cmp expect actual
'
@@ -28,7 +28,7 @@ test_expect_success 'ordered enumeration with duplicate suppression' '
echoid append 88 44 aa 55 &&
echoid append 88 44 aa 55 &&
echo for_each_unique
- } | test-tool sha1-array >actual &&
+ } | test-tool oid-array >actual &&
test_cmp expect actual
'
@@ -36,7 +36,7 @@ test_expect_success 'lookup' '
{
echoid append 88 44 aa 55 &&
echoid lookup 55
- } | test-tool sha1-array >actual &&
+ } | test-tool oid-array >actual &&
n=$(cat actual) &&
test "$n" -eq 1
'
@@ -45,7 +45,7 @@ test_expect_success 'lookup non-existing entry' '
{
echoid append 88 44 aa 55 &&
echoid lookup 33
- } | test-tool sha1-array >actual &&
+ } | test-tool oid-array >actual &&
n=$(cat actual) &&
test "$n" -lt 0
'
@@ -55,7 +55,7 @@ test_expect_success 'lookup with duplicates' '
echoid append 88 44 aa 55 &&
echoid append 88 44 aa 55 &&
echoid lookup 55
- } | test-tool sha1-array >actual &&
+ } | test-tool oid-array >actual &&
n=$(cat actual) &&
test "$n" -ge 2 &&
test "$n" -le 3
@@ -66,7 +66,7 @@ test_expect_success 'lookup non-existing entry with duplicates' '
echoid append 88 44 aa 55 &&
echoid append 88 44 aa 55 &&
echoid lookup 66
- } | test-tool sha1-array >actual &&
+ } | test-tool oid-array >actual &&
n=$(cat actual) &&
test "$n" -lt 0
'
@@ -81,7 +81,7 @@ test_expect_success 'lookup with almost duplicate values' '
echo "append $id1" &&
echo "append $id2" &&
echoid lookup 55
- } | test-tool sha1-array >actual &&
+ } | test-tool oid-array >actual &&
n=$(cat actual) &&
test "$n" -eq 0
'
@@ -90,7 +90,7 @@ test_expect_success 'lookup with single duplicate value' '
{
echoid append 55 55 &&
echoid lookup 55
- } | test-tool sha1-array >actual &&
+ } | test-tool oid-array >actual &&
n=$(cat actual) &&
test "$n" -ge 0 &&
test "$n" -le 1
diff --git a/t/t0066-dir-iterator.sh b/t/t0066-dir-iterator.sh
new file mode 100755
index 0000000..92910e4
--- /dev/null
+++ b/t/t0066-dir-iterator.sh
@@ -0,0 +1,148 @@
+#!/bin/sh
+
+test_description='Test the dir-iterator functionality'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ mkdir -p dir &&
+ mkdir -p dir/a/b/c/ &&
+ >dir/b &&
+ >dir/c &&
+ mkdir -p dir/d/e/d/ &&
+ >dir/a/b/c/d &&
+ >dir/a/e &&
+ >dir/d/e/d/a &&
+
+ mkdir -p dir2/a/b/c/ &&
+ >dir2/a/b/c/d
+'
+
+test_expect_success 'dir-iterator should iterate through all files' '
+ cat >expected-iteration-sorted-output <<-EOF &&
+ [d] (a) [a] ./dir/a
+ [d] (a/b) [b] ./dir/a/b
+ [d] (a/b/c) [c] ./dir/a/b/c
+ [d] (d) [d] ./dir/d
+ [d] (d/e) [e] ./dir/d/e
+ [d] (d/e/d) [d] ./dir/d/e/d
+ [f] (a/b/c/d) [d] ./dir/a/b/c/d
+ [f] (a/e) [e] ./dir/a/e
+ [f] (b) [b] ./dir/b
+ [f] (c) [c] ./dir/c
+ [f] (d/e/d/a) [a] ./dir/d/e/d/a
+ EOF
+
+ test-tool dir-iterator ./dir >out &&
+ sort out >./actual-iteration-sorted-output &&
+
+ test_cmp expected-iteration-sorted-output actual-iteration-sorted-output
+'
+
+test_expect_success 'dir-iterator should list files in the correct order' '
+ cat >expected-pre-order-output <<-EOF &&
+ [d] (a) [a] ./dir2/a
+ [d] (a/b) [b] ./dir2/a/b
+ [d] (a/b/c) [c] ./dir2/a/b/c
+ [f] (a/b/c/d) [d] ./dir2/a/b/c/d
+ EOF
+
+ test-tool dir-iterator ./dir2 >actual-pre-order-output &&
+
+ test_cmp expected-pre-order-output actual-pre-order-output
+'
+
+test_expect_success 'begin should fail upon inexistent paths' '
+ test_must_fail test-tool dir-iterator ./inexistent-path \
+ >actual-inexistent-path-output &&
+ echo "dir_iterator_begin failure: ENOENT" >expected-inexistent-path-output &&
+ test_cmp expected-inexistent-path-output actual-inexistent-path-output
+'
+
+test_expect_success 'begin should fail upon non directory paths' '
+ test_must_fail test-tool dir-iterator ./dir/b >actual-non-dir-output &&
+ echo "dir_iterator_begin failure: ENOTDIR" >expected-non-dir-output &&
+ test_cmp expected-non-dir-output actual-non-dir-output
+'
+
+test_expect_success POSIXPERM,SANITY 'advance should not fail on errors by default' '
+ cat >expected-no-permissions-output <<-EOF &&
+ [d] (a) [a] ./dir3/a
+ EOF
+
+ mkdir -p dir3/a &&
+ >dir3/a/b &&
+ chmod 0 dir3/a &&
+
+ test-tool dir-iterator ./dir3 >actual-no-permissions-output &&
+ test_cmp expected-no-permissions-output actual-no-permissions-output &&
+ chmod 755 dir3/a &&
+ rm -rf dir3
+'
+
+test_expect_success POSIXPERM,SANITY 'advance should fail on errors, w/ pedantic flag' '
+ cat >expected-no-permissions-pedantic-output <<-EOF &&
+ [d] (a) [a] ./dir3/a
+ dir_iterator_advance failure
+ EOF
+
+ mkdir -p dir3/a &&
+ >dir3/a/b &&
+ chmod 0 dir3/a &&
+
+ test_must_fail test-tool dir-iterator --pedantic ./dir3 \
+ >actual-no-permissions-pedantic-output &&
+ test_cmp expected-no-permissions-pedantic-output \
+ actual-no-permissions-pedantic-output &&
+ chmod 755 dir3/a &&
+ rm -rf dir3
+'
+
+test_expect_success SYMLINKS 'setup dirs with symlinks' '
+ mkdir -p dir4/a &&
+ mkdir -p dir4/b/c &&
+ >dir4/a/d &&
+ ln -s d dir4/a/e &&
+ ln -s ../b dir4/a/f &&
+
+ mkdir -p dir5/a/b &&
+ mkdir -p dir5/a/c &&
+ ln -s ../c dir5/a/b/d &&
+ ln -s ../ dir5/a/b/e &&
+ ln -s ../../ dir5/a/b/f
+'
+
+test_expect_success SYMLINKS 'dir-iterator should not follow symlinks by default' '
+ cat >expected-no-follow-sorted-output <<-EOF &&
+ [d] (a) [a] ./dir4/a
+ [d] (b) [b] ./dir4/b
+ [d] (b/c) [c] ./dir4/b/c
+ [f] (a/d) [d] ./dir4/a/d
+ [s] (a/e) [e] ./dir4/a/e
+ [s] (a/f) [f] ./dir4/a/f
+ EOF
+
+ test-tool dir-iterator ./dir4 >out &&
+ sort out >actual-no-follow-sorted-output &&
+
+ test_cmp expected-no-follow-sorted-output actual-no-follow-sorted-output
+'
+
+test_expect_success SYMLINKS 'dir-iterator should follow symlinks w/ follow flag' '
+ cat >expected-follow-sorted-output <<-EOF &&
+ [d] (a) [a] ./dir4/a
+ [d] (a/f) [f] ./dir4/a/f
+ [d] (a/f/c) [c] ./dir4/a/f/c
+ [d] (b) [b] ./dir4/b
+ [d] (b/c) [c] ./dir4/b/c
+ [f] (a/d) [d] ./dir4/a/d
+ [f] (a/e) [e] ./dir4/a/e
+ EOF
+
+ test-tool dir-iterator --follow-symlinks ./dir4 >out &&
+ sort out >actual-follow-sorted-output &&
+
+ test_cmp expected-follow-sorted-output actual-follow-sorted-output
+'
+
+test_done
diff --git a/t/t0067-parse_pathspec_file.sh b/t/t0067-parse_pathspec_file.sh
new file mode 100755
index 0000000..7bab49f
--- /dev/null
+++ b/t/t0067-parse_pathspec_file.sh
@@ -0,0 +1,108 @@
+#!/bin/sh
+
+test_description='Test parse_pathspec_file()'
+
+. ./test-lib.sh
+
+test_expect_success 'one item from stdin' '
+ cat >expect <<-\EOF &&
+ fileA.t
+ EOF
+
+ echo fileA.t |
+ test-tool parse-pathspec-file --pathspec-from-file=- >actual &&
+
+ test_cmp expect actual
+'
+
+test_expect_success 'one item from file' '
+ cat >expect <<-\EOF &&
+ fileA.t
+ EOF
+
+ echo fileA.t >list &&
+ test-tool parse-pathspec-file --pathspec-from-file=list >actual &&
+
+ test_cmp expect actual
+'
+
+test_expect_success 'NUL delimiters' '
+ cat >expect <<-\EOF &&
+ fileA.t
+ fileB.t
+ EOF
+
+ printf "fileA.t\0fileB.t\0" |
+ test-tool parse-pathspec-file --pathspec-from-file=- --pathspec-file-nul >actual &&
+
+ test_cmp expect actual
+'
+
+test_expect_success 'LF delimiters' '
+ cat >expect <<-\EOF &&
+ fileA.t
+ fileB.t
+ EOF
+
+ printf "fileA.t\nfileB.t\n" |
+ test-tool parse-pathspec-file --pathspec-from-file=- >actual &&
+
+ test_cmp expect actual
+'
+
+test_expect_success 'no trailing delimiter' '
+ cat >expect <<-\EOF &&
+ fileA.t
+ fileB.t
+ EOF
+
+ printf "fileA.t\nfileB.t" |
+ test-tool parse-pathspec-file --pathspec-from-file=- >actual &&
+
+ test_cmp expect actual
+'
+
+test_expect_success 'CRLF delimiters' '
+ cat >expect <<-\EOF &&
+ fileA.t
+ fileB.t
+ EOF
+
+ printf "fileA.t\r\nfileB.t\r\n" |
+ test-tool parse-pathspec-file --pathspec-from-file=- >actual &&
+
+ test_cmp expect actual
+'
+
+test_expect_success 'quotes' '
+ cat >expect <<-\EOF &&
+ fileA.t
+ EOF
+
+ cat >list <<-\EOF &&
+ "file\101.t"
+ EOF
+
+ test-tool parse-pathspec-file --pathspec-from-file=list >actual &&
+
+ test_cmp expect actual
+'
+
+test_expect_success '--pathspec-file-nul takes quotes literally' '
+ # Note: there is an extra newline because --pathspec-file-nul takes
+ # input \n literally, too
+ cat >expect <<-\EOF &&
+ "file\101.t"
+
+ EOF
+
+ cat >list <<-\EOF &&
+ "file\101.t"
+ EOF
+
+ test-tool parse-pathspec-file --pathspec-from-file=list --pathspec-file-nul >actual &&
+
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t0090-cache-tree.sh b/t/t0090-cache-tree.sh
index 504334e..5a63369 100755
--- a/t/t0090-cache-tree.sh
+++ b/t/t0090-cache-tree.sh
@@ -21,9 +21,10 @@ generate_expected_cache_tree_rec () {
parent="$2" &&
# ls-files might have foo/bar, foo/bar/baz, and foo/bar/quux
# We want to count only foo because it's the only direct child
- subtrees=$(git ls-files|grep /|cut -d / -f 1|uniq) &&
+ git ls-files >files &&
+ subtrees=$(grep / files|cut -d / -f 1|uniq) &&
subtree_count=$(echo "$subtrees"|awk -v c=0 '$1 != "" {++c} END {print c}') &&
- entries=$(git ls-files|wc -l) &&
+ entries=$(wc -l <files) &&
printf "SHA $dir (%d entries, %d subtrees)\n" "$entries" "$subtree_count" &&
for subtree in $subtrees
do
@@ -162,8 +163,8 @@ test_expect_success PERL 'commit --interactive gives cache-tree on partial commi
'
test_expect_success PERL 'commit -p with shrinking cache-tree' '
- mkdir -p deep/subdir &&
- echo content >deep/subdir/file &&
+ mkdir -p deep/very-long-subdir &&
+ echo content >deep/very-long-subdir/file &&
git add deep &&
git commit -m add &&
git rm -r deep &&
diff --git a/t/t0091-bugreport.sh b/t/t0091-bugreport.sh
new file mode 100755
index 0000000..2e73658
--- /dev/null
+++ b/t/t0091-bugreport.sh
@@ -0,0 +1,61 @@
+#!/bin/sh
+
+test_description='git bugreport'
+
+. ./test-lib.sh
+
+# Headers "[System Info]" will be followed by a non-empty line if we put some
+# information there; we can make sure all our headers were followed by some
+# information to check if the command was successful.
+HEADER_PATTERN="^\[.*\]$"
+
+check_all_headers_populated () {
+ while read -r line
+ do
+ if test "$(grep "$HEADER_PATTERN" "$line")"
+ then
+ echo "$line"
+ read -r nextline
+ if test -z "$nextline"; then
+ return 1;
+ fi
+ fi
+ done
+}
+
+test_expect_success 'creates a report with content in the right places' '
+ test_when_finished rm git-bugreport-check-headers.txt &&
+ git bugreport -s check-headers &&
+ check_all_headers_populated <git-bugreport-check-headers.txt
+'
+
+test_expect_success 'dies if file with same name as report already exists' '
+ test_when_finished rm git-bugreport-duplicate.txt &&
+ >>git-bugreport-duplicate.txt &&
+ test_must_fail git bugreport --suffix duplicate
+'
+
+test_expect_success '--output-directory puts the report in the provided dir' '
+ test_when_finished rm -fr foo/ &&
+ git bugreport -o foo/ &&
+ test_path_is_file foo/git-bugreport-*
+'
+
+test_expect_success 'incorrect arguments abort with usage' '
+ test_must_fail git bugreport --false 2>output &&
+ test_i18ngrep usage output &&
+ test_path_is_missing git-bugreport-*
+'
+
+test_expect_success 'runs outside of a git dir' '
+ test_when_finished rm non-repo/git-bugreport-* &&
+ nongit git bugreport
+'
+
+test_expect_success 'can create leading directories outside of a git dir' '
+ test_when_finished rm -fr foo/bar/baz &&
+ nongit git bugreport -o foo/bar/baz
+'
+
+
+test_done
diff --git a/t/t0095-bloom.sh b/t/t0095-bloom.sh
new file mode 100755
index 0000000..8f9eef1
--- /dev/null
+++ b/t/t0095-bloom.sh
@@ -0,0 +1,117 @@
+#!/bin/sh
+
+test_description='Testing the various Bloom filter computations in bloom.c'
+. ./test-lib.sh
+
+test_expect_success 'compute unseeded murmur3 hash for empty string' '
+ cat >expect <<-\EOF &&
+ Murmur3 Hash with seed=0:0x00000000
+ EOF
+ test-tool bloom get_murmur3 "" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'compute unseeded murmur3 hash for test string 1' '
+ cat >expect <<-\EOF &&
+ Murmur3 Hash with seed=0:0x627b0c2c
+ EOF
+ test-tool bloom get_murmur3 "Hello world!" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'compute unseeded murmur3 hash for test string 2' '
+ cat >expect <<-\EOF &&
+ Murmur3 Hash with seed=0:0x2e4ff723
+ EOF
+ test-tool bloom get_murmur3 "The quick brown fox jumps over the lazy dog" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'compute bloom key for empty string' '
+ cat >expect <<-\EOF &&
+ Hashes:0x5615800c|0x5b966560|0x61174ab4|0x66983008|0x6c19155c|0x7199fab0|0x771ae004|
+ Filter_Length:2
+ Filter_Data:11|11|
+ EOF
+ test-tool bloom generate_filter "" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'compute bloom key for whitespace' '
+ cat >expect <<-\EOF &&
+ Hashes:0xf178874c|0x5f3d6eb6|0xcd025620|0x3ac73d8a|0xa88c24f4|0x16510c5e|0x8415f3c8|
+ Filter_Length:2
+ Filter_Data:51|55|
+ EOF
+ test-tool bloom generate_filter " " >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'compute bloom key for test string 1' '
+ cat >expect <<-\EOF &&
+ Hashes:0xb270de9b|0x1bb6f26e|0x84fd0641|0xee431a14|0x57892de7|0xc0cf41ba|0x2a15558d|
+ Filter_Length:2
+ Filter_Data:92|6c|
+ EOF
+ test-tool bloom generate_filter "Hello world!" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'compute bloom key for test string 2' '
+ cat >expect <<-\EOF &&
+ Hashes:0x20ab385b|0xf5237fe2|0xc99bc769|0x9e140ef0|0x728c5677|0x47049dfe|0x1b7ce585|
+ Filter_Length:2
+ Filter_Data:a5|4a|
+ EOF
+ test-tool bloom generate_filter "file.txt" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'get bloom filters for commit with no changes' '
+ git init &&
+ git commit --allow-empty -m "c0" &&
+ cat >expect <<-\EOF &&
+ Filter_Length:0
+ Filter_Data:
+ EOF
+ test-tool bloom get_filter_for_commit "$(git rev-parse HEAD)" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'get bloom filter for commit with 10 changes' '
+ rm actual &&
+ rm expect &&
+ mkdir smallDir &&
+ for i in $(test_seq 0 9)
+ do
+ echo $i >smallDir/$i
+ done &&
+ git add smallDir &&
+ git commit -m "commit with 10 changes" &&
+ cat >expect <<-\EOF &&
+ Filter_Length:25
+ Filter_Data:82|a0|65|47|0c|92|90|c0|a1|40|02|a0|e2|40|e0|04|0a|9a|66|cf|80|19|85|42|23|
+ EOF
+ test-tool bloom get_filter_for_commit "$(git rev-parse HEAD)" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success EXPENSIVE 'get bloom filter for commit with 513 changes' '
+ rm actual &&
+ rm expect &&
+ mkdir bigDir &&
+ for i in $(test_seq 0 512)
+ do
+ echo $i >bigDir/$i
+ done &&
+ git add bigDir &&
+ git commit -m "commit with 513 changes" &&
+ cat >expect <<-\EOF &&
+ Filter_Length:0
+ Filter_Data:
+ EOF
+ test-tool bloom get_filter_for_commit "$(git rev-parse HEAD)" >actual &&
+ test_cmp expect actual
+'
+
+test_done \ No newline at end of file
diff --git a/t/t0205-gettext-poison.sh b/t/t0205-gettext-poison.sh
index a06269f..f9fa16a 100755
--- a/t/t0205-gettext-poison.sh
+++ b/t/t0205-gettext-poison.sh
@@ -5,7 +5,7 @@
test_description='Gettext Shell poison'
-GIT_TEST_GETTEXT_POISON=YesPlease
+GIT_TEST_GETTEXT_POISON=true
export GIT_TEST_GETTEXT_POISON
. ./lib-gettext.sh
@@ -31,4 +31,9 @@ test_expect_success 'eval_gettext: our eval_gettext() fallback has poison semant
test_cmp expect actual
'
+test_expect_success "gettext: invalid GIT_TEST_GETTEXT_POISON value doesn't infinitely loop" "
+ test_must_fail env GIT_TEST_GETTEXT_POISON=xyz git version 2>error &&
+ grep \"fatal: bad numeric config value 'xyz' for 'GIT_TEST_GETTEXT_POISON': invalid unit\" error
+"
+
test_done
diff --git a/t/t0210-trace2-normal.sh b/t/t0210-trace2-normal.sh
new file mode 100755
index 0000000..ce7574e
--- /dev/null
+++ b/t/t0210-trace2-normal.sh
@@ -0,0 +1,189 @@
+#!/bin/sh
+
+test_description='test trace2 facility (normal target)'
+. ./test-lib.sh
+
+# Turn off any inherited trace2 settings for this test.
+sane_unset GIT_TRACE2 GIT_TRACE2_PERF GIT_TRACE2_EVENT
+sane_unset GIT_TRACE2_BRIEF
+sane_unset GIT_TRACE2_CONFIG_PARAMS
+
+# Add t/helper directory to PATH so that we can use a relative
+# path to run nested instances of test-tool.exe (see 004child).
+# This helps with HEREDOC comparisons later.
+TTDIR="$GIT_BUILD_DIR/t/helper/" && export TTDIR
+PATH="$TTDIR:$PATH" && export PATH
+
+# Warning: use of 'test_cmp' may run test-tool.exe and/or git.exe
+# Warning: to do the actual diff/comparison, so the HEREDOCs here
+# Warning: only cover our actual calls to test-tool and/or git.
+# Warning: So you may see extra lines in artifact files when
+# Warning: interactively debugging.
+
+V=$(git version | sed -e 's/^git version //') && export V
+
+# There are multiple trace2 targets: normal, perf, and event.
+# Trace2 events will/can be written to each active target (subject
+# to whatever filtering that target decides to do).
+# This script tests the normal target in isolation.
+#
+# Defer setting GIT_TRACE2 until the actual command line we want to test
+# because hidden git and test-tool commands run by the test harness
+# can contaminate our output.
+
+# Enable "brief" feature which turns off "<clock> <file>:<line> " prefix.
+GIT_TRACE2_BRIEF=1 && export GIT_TRACE2_BRIEF
+
+# Basic tests of the trace2 normal stream. Since this stream is used
+# primarily with printf-style debugging/tracing, we do limited testing
+# here.
+#
+# We do confirm the following API features:
+# [] the 'version <v>' event
+# [] the 'start <argv>' event
+# [] the 'cmd_name <name>' event
+# [] the 'exit <time> code:<code>' event
+# [] the 'atexit <time> code:<code>' event
+#
+# Fields of the form _FIELD_ are tokens that have been replaced (such
+# as the elapsed time).
+
+# Verb 001return
+#
+# Implicit return from cmd_<verb> function propagates <code>.
+
+test_expect_success 'normal stream, return code 0' '
+ test_when_finished "rm trace.normal actual expect" &&
+ GIT_TRACE2="$(pwd)/trace.normal" test-tool trace2 001return 0 &&
+ perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ cat >expect <<-EOF &&
+ version $V
+ start _EXE_ trace2 001return 0
+ cmd_name trace2 (trace2)
+ exit elapsed:_TIME_ code:0
+ atexit elapsed:_TIME_ code:0
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'normal stream, return code 1' '
+ test_when_finished "rm trace.normal actual expect" &&
+ test_must_fail env GIT_TRACE2="$(pwd)/trace.normal" test-tool trace2 001return 1 &&
+ perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ cat >expect <<-EOF &&
+ version $V
+ start _EXE_ trace2 001return 1
+ cmd_name trace2 (trace2)
+ exit elapsed:_TIME_ code:1
+ atexit elapsed:_TIME_ code:1
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'automatic filename' '
+ test_when_finished "rm -r traces actual expect" &&
+ mkdir traces &&
+ GIT_TRACE2="$(pwd)/traces" test-tool trace2 001return 0 &&
+ perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <"$(ls traces/*)" >actual &&
+ cat >expect <<-EOF &&
+ version $V
+ start _EXE_ trace2 001return 0
+ cmd_name trace2 (trace2)
+ exit elapsed:_TIME_ code:0
+ atexit elapsed:_TIME_ code:0
+ EOF
+ test_cmp expect actual
+'
+
+# Verb 002exit
+#
+# Explicit exit(code) from within cmd_<verb> propagates <code>.
+
+test_expect_success 'normal stream, exit code 0' '
+ test_when_finished "rm trace.normal actual expect" &&
+ GIT_TRACE2="$(pwd)/trace.normal" test-tool trace2 002exit 0 &&
+ perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ cat >expect <<-EOF &&
+ version $V
+ start _EXE_ trace2 002exit 0
+ cmd_name trace2 (trace2)
+ exit elapsed:_TIME_ code:0
+ atexit elapsed:_TIME_ code:0
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'normal stream, exit code 1' '
+ test_when_finished "rm trace.normal actual expect" &&
+ test_must_fail env GIT_TRACE2="$(pwd)/trace.normal" test-tool trace2 002exit 1 &&
+ perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ cat >expect <<-EOF &&
+ version $V
+ start _EXE_ trace2 002exit 1
+ cmd_name trace2 (trace2)
+ exit elapsed:_TIME_ code:1
+ atexit elapsed:_TIME_ code:1
+ EOF
+ test_cmp expect actual
+'
+
+# Verb 003error
+#
+# To the above, add multiple 'error <msg>' events
+
+test_expect_success 'normal stream, error event' '
+ test_when_finished "rm trace.normal actual expect" &&
+ GIT_TRACE2="$(pwd)/trace.normal" test-tool trace2 003error "hello world" "this is a test" &&
+ perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ cat >expect <<-EOF &&
+ version $V
+ start _EXE_ trace2 003error '\''hello world'\'' '\''this is a test'\''
+ cmd_name trace2 (trace2)
+ error hello world
+ error this is a test
+ exit elapsed:_TIME_ code:0
+ atexit elapsed:_TIME_ code:0
+ EOF
+ test_cmp expect actual
+'
+
+sane_unset GIT_TRACE2_BRIEF
+
+# Now test without environment variables and get all Trace2 settings
+# from the global config.
+
+test_expect_success 'using global config, normal stream, return code 0' '
+ test_when_finished "rm trace.normal actual expect" &&
+ test_config_global trace2.normalBrief 1 &&
+ test_config_global trace2.normalTarget "$(pwd)/trace.normal" &&
+ test-tool trace2 001return 0 &&
+ perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ cat >expect <<-EOF &&
+ version $V
+ start _EXE_ trace2 001return 0
+ cmd_name trace2 (trace2)
+ exit elapsed:_TIME_ code:0
+ atexit elapsed:_TIME_ code:0
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'using global config with include' '
+ test_when_finished "rm trace.normal actual expect real.gitconfig" &&
+ test_config_global trace2.normalBrief 1 &&
+ test_config_global trace2.normalTarget "$(pwd)/trace.normal" &&
+ mv "$(pwd)/.gitconfig" "$(pwd)/real.gitconfig" &&
+ test_config_global include.path "$(pwd)/real.gitconfig" &&
+ test-tool trace2 001return 0 &&
+ perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ cat >expect <<-EOF &&
+ version $V
+ start _EXE_ trace2 001return 0
+ cmd_name trace2 (trace2)
+ exit elapsed:_TIME_ code:0
+ atexit elapsed:_TIME_ code:0
+ EOF
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t0210/scrub_normal.perl b/t/t0210/scrub_normal.perl
new file mode 100644
index 0000000..c65d1a8
--- /dev/null
+++ b/t/t0210/scrub_normal.perl
@@ -0,0 +1,48 @@
+#!/usr/bin/perl
+#
+# Scrub the variable fields from the normal trace2 output to
+# make testing easier.
+
+use strict;
+use warnings;
+
+my $float = '[0-9]*\.[0-9]+([eE][-+]?[0-9]+)?';
+
+# This code assumes that the trace2 data was written with bare
+# turned on (which omits the "<clock> <file>:<line>" prefix.
+
+while (<>) {
+ # Various messages include an elapsed time in the middle
+ # of the message. Replace the time with a placeholder to
+ # simplify our HEREDOC in the test script.
+ s/elapsed:$float/elapsed:_TIME_/g;
+
+ my $line = $_;
+
+ # we expect:
+ # start <argv0> [<argv1> [<argv2> [...]]]
+ #
+ # where argv0 might be a relative or absolute path, with
+ # or without quotes, and platform dependent. Replace argv0
+ # with a token for HEREDOC matching in the test script.
+
+ if ($line =~ m/^start/) {
+ $line =~ /^start\s+(.*)/;
+ my $argv = $1;
+ $argv =~ m/(\'[^\']*\'|[^ ]+)\s+(.*)/;
+ my $argv_0 = $1;
+ my $argv_rest = $2;
+
+ print "start _EXE_ $argv_rest\n";
+ }
+ elsif ($line =~ m/^cmd_path/) {
+ # Likewise, the 'cmd_path' message breaks out argv[0].
+ #
+ # This line is only emitted when RUNTIME_PREFIX is defined,
+ # so just omit it for testing purposes.
+ # print "cmd_path _EXE_\n";
+ }
+ else {
+ print "$line";
+ }
+}
diff --git a/t/t0211-trace2-perf.sh b/t/t0211-trace2-perf.sh
new file mode 100755
index 0000000..6ee8ee3
--- /dev/null
+++ b/t/t0211-trace2-perf.sh
@@ -0,0 +1,174 @@
+#!/bin/sh
+
+test_description='test trace2 facility (perf target)'
+. ./test-lib.sh
+
+# Turn off any inherited trace2 settings for this test.
+sane_unset GIT_TRACE2 GIT_TRACE2_PERF GIT_TRACE2_EVENT
+sane_unset GIT_TRACE2_PERF_BRIEF
+sane_unset GIT_TRACE2_CONFIG_PARAMS
+
+# Add t/helper directory to PATH so that we can use a relative
+# path to run nested instances of test-tool.exe (see 004child).
+# This helps with HEREDOC comparisons later.
+TTDIR="$GIT_BUILD_DIR/t/helper/" && export TTDIR
+PATH="$TTDIR:$PATH" && export PATH
+
+# Warning: use of 'test_cmp' may run test-tool.exe and/or git.exe
+# Warning: to do the actual diff/comparison, so the HEREDOCs here
+# Warning: only cover our actual calls to test-tool and/or git.
+# Warning: So you may see extra lines in artifact files when
+# Warning: interactively debugging.
+
+V=$(git version | sed -e 's/^git version //') && export V
+
+# There are multiple trace2 targets: normal, perf, and event.
+# Trace2 events will/can be written to each active target (subject
+# to whatever filtering that target decides to do).
+# Test each target independently.
+#
+# Defer setting GIT_TRACE2_PERF until the actual command we want to
+# test because hidden git and test-tool commands in the test
+# harness can contaminate our output.
+
+# Enable "brief" feature which turns off the prefix:
+# "<clock> <file>:<line> | <nr_parents> | "
+GIT_TRACE2_PERF_BRIEF=1 && export GIT_TRACE2_PERF_BRIEF
+
+# Repeat some of the t0210 tests using the perf target stream instead of
+# the normal stream.
+#
+# Tokens here of the form _FIELD_ have been replaced in the observed output.
+
+# Verb 001return
+#
+# Implicit return from cmd_<verb> function propagates <code>.
+
+test_expect_success 'perf stream, return code 0' '
+ test_when_finished "rm trace.perf actual expect" &&
+ GIT_TRACE2_PERF="$(pwd)/trace.perf" test-tool trace2 001return 0 &&
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
+ cat >expect <<-EOF &&
+ d0|main|version|||||$V
+ d0|main|start||_T_ABS_|||_EXE_ trace2 001return 0
+ d0|main|cmd_name|||||trace2 (trace2)
+ d0|main|exit||_T_ABS_|||code:0
+ d0|main|atexit||_T_ABS_|||code:0
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'perf stream, return code 1' '
+ test_when_finished "rm trace.perf actual expect" &&
+ test_must_fail env GIT_TRACE2_PERF="$(pwd)/trace.perf" test-tool trace2 001return 1 &&
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
+ cat >expect <<-EOF &&
+ d0|main|version|||||$V
+ d0|main|start||_T_ABS_|||_EXE_ trace2 001return 1
+ d0|main|cmd_name|||||trace2 (trace2)
+ d0|main|exit||_T_ABS_|||code:1
+ d0|main|atexit||_T_ABS_|||code:1
+ EOF
+ test_cmp expect actual
+'
+
+# Verb 003error
+#
+# To the above, add multiple 'error <msg>' events
+
+test_expect_success 'perf stream, error event' '
+ test_when_finished "rm trace.perf actual expect" &&
+ GIT_TRACE2_PERF="$(pwd)/trace.perf" test-tool trace2 003error "hello world" "this is a test" &&
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
+ cat >expect <<-EOF &&
+ d0|main|version|||||$V
+ d0|main|start||_T_ABS_|||_EXE_ trace2 003error '\''hello world'\'' '\''this is a test'\''
+ d0|main|cmd_name|||||trace2 (trace2)
+ d0|main|error|||||hello world
+ d0|main|error|||||this is a test
+ d0|main|exit||_T_ABS_|||code:0
+ d0|main|atexit||_T_ABS_|||code:0
+ EOF
+ test_cmp expect actual
+'
+
+# Verb 004child
+#
+# Test nested spawning of child processes.
+#
+# Conceptually, this looks like:
+# P1: TT trace2 004child
+# P2: |--- TT trace2 004child
+# P3: |--- TT trace2 001return 0
+#
+# Which should generate events:
+# P1: version
+# P1: start
+# P1: cmd_name
+# P1: child_start
+# P2: version
+# P2: start
+# P2: cmd_name
+# P2: child_start
+# P3: version
+# P3: start
+# P3: cmd_name
+# P3: exit
+# P3: atexit
+# P2: child_exit
+# P2: exit
+# P2: atexit
+# P1: child_exit
+# P1: exit
+# P1: atexit
+
+test_expect_success 'perf stream, child processes' '
+ test_when_finished "rm trace.perf actual expect" &&
+ GIT_TRACE2_PERF="$(pwd)/trace.perf" test-tool trace2 004child test-tool trace2 004child test-tool trace2 001return 0 &&
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
+ cat >expect <<-EOF &&
+ d0|main|version|||||$V
+ d0|main|start||_T_ABS_|||_EXE_ trace2 004child test-tool trace2 004child test-tool trace2 001return 0
+ d0|main|cmd_name|||||trace2 (trace2)
+ d0|main|child_start||_T_ABS_|||[ch0] class:? argv:[test-tool trace2 004child test-tool trace2 001return 0]
+ d1|main|version|||||$V
+ d1|main|start||_T_ABS_|||_EXE_ trace2 004child test-tool trace2 001return 0
+ d1|main|cmd_name|||||trace2 (trace2/trace2)
+ d1|main|child_start||_T_ABS_|||[ch0] class:? argv:[test-tool trace2 001return 0]
+ d2|main|version|||||$V
+ d2|main|start||_T_ABS_|||_EXE_ trace2 001return 0
+ d2|main|cmd_name|||||trace2 (trace2/trace2/trace2)
+ d2|main|exit||_T_ABS_|||code:0
+ d2|main|atexit||_T_ABS_|||code:0
+ d1|main|child_exit||_T_ABS_|_T_REL_||[ch0] pid:_PID_ code:0
+ d1|main|exit||_T_ABS_|||code:0
+ d1|main|atexit||_T_ABS_|||code:0
+ d0|main|child_exit||_T_ABS_|_T_REL_||[ch0] pid:_PID_ code:0
+ d0|main|exit||_T_ABS_|||code:0
+ d0|main|atexit||_T_ABS_|||code:0
+ EOF
+ test_cmp expect actual
+'
+
+sane_unset GIT_TRACE2_PERF_BRIEF
+
+# Now test without environment variables and get all Trace2 settings
+# from the global config.
+
+test_expect_success 'using global config, perf stream, return code 0' '
+ test_when_finished "rm trace.perf actual expect" &&
+ test_config_global trace2.perfBrief 1 &&
+ test_config_global trace2.perfTarget "$(pwd)/trace.perf" &&
+ test-tool trace2 001return 0 &&
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
+ cat >expect <<-EOF &&
+ d0|main|version|||||$V
+ d0|main|start||_T_ABS_|||_EXE_ trace2 001return 0
+ d0|main|cmd_name|||||trace2 (trace2)
+ d0|main|exit||_T_ABS_|||code:0
+ d0|main|atexit||_T_ABS_|||code:0
+ EOF
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t0211/scrub_perf.perl b/t/t0211/scrub_perf.perl
new file mode 100644
index 0000000..351af78
--- /dev/null
+++ b/t/t0211/scrub_perf.perl
@@ -0,0 +1,76 @@
+#!/usr/bin/perl
+#
+# Scrub the variable fields from the perf trace2 output to
+# make testing easier.
+
+use strict;
+use warnings;
+
+my $qpath = '\'[^\']*\'|[^ ]*';
+
+my $col_depth=0;
+my $col_thread=1;
+my $col_event=2;
+my $col_repo=3;
+my $col_t_abs=4;
+my $col_t_rel=5;
+my $col_category=6;
+my $col_rest=7;
+
+# This code assumes that the trace2 data was written with bare
+# turned on (which omits the "<clock> <file>:<line> | <parents>"
+# prefix.
+
+while (<>) {
+ my @tokens = split /\|/;
+
+ foreach my $col (@tokens) { $col =~ s/^\s+|\s+$//g; }
+
+ if ($tokens[$col_event] =~ m/^start/) {
+ # The 'start' message lists the contents of argv in $col_rest.
+ # On some platforms (Windows), argv[0] is *sometimes* a canonical
+ # absolute path to the EXE rather than the value passed in the
+ # shell script. Replace it with a placeholder to simplify our
+ # HEREDOC in the test script.
+ my $argv0;
+ my $argvRest;
+ $tokens[$col_rest] =~ s/^($qpath)\W*(.*)/_EXE_ $2/;
+ }
+ elsif ($tokens[$col_event] =~ m/cmd_path/) {
+ # Likewise, the 'cmd_path' message breaks out argv[0].
+ #
+ # This line is only emitted when RUNTIME_PREFIX is defined,
+ # so just omit it for testing purposes.
+ # $tokens[$col_rest] = "_EXE_";
+ goto SKIP_LINE;
+ }
+ elsif ($tokens[$col_event] =~ m/child_exit/) {
+ $tokens[$col_rest] =~ s/ pid:\d* / pid:_PID_ /;
+ }
+ elsif ($tokens[$col_event] =~ m/data/) {
+ if ($tokens[$col_category] =~ m/process/) {
+ # 'data' and 'data_json' events containing 'process'
+ # category data are assumed to be platform-specific
+ # and highly variable. Just omit them.
+ goto SKIP_LINE;
+ }
+ }
+
+ # t_abs and t_rel are either blank or a float. Replace the float
+ # with a constant for matching the HEREDOC in the test script.
+ if ($tokens[$col_t_abs] =~ m/\d/) {
+ $tokens[$col_t_abs] = "_T_ABS_";
+ }
+ if ($tokens[$col_t_rel] =~ m/\d/) {
+ $tokens[$col_t_rel] = "_T_REL_";
+ }
+
+ my $out;
+
+ $out = join('|', @tokens);
+ print "$out\n";
+
+ SKIP_LINE:
+}
+
+
diff --git a/t/t0212-trace2-event.sh b/t/t0212-trace2-event.sh
new file mode 100755
index 0000000..1529155
--- /dev/null
+++ b/t/t0212-trace2-event.sh
@@ -0,0 +1,324 @@
+#!/bin/sh
+
+test_description='test trace2 facility'
+. ./test-lib.sh
+
+# Turn off any inherited trace2 settings for this test.
+sane_unset GIT_TRACE2 GIT_TRACE2_PERF GIT_TRACE2_EVENT
+sane_unset GIT_TRACE2_BARE
+sane_unset GIT_TRACE2_CONFIG_PARAMS
+
+perl -MJSON::PP -e 0 >/dev/null 2>&1 && test_set_prereq JSON_PP
+
+# Add t/helper directory to PATH so that we can use a relative
+# path to run nested instances of test-tool.exe (see 004child).
+# This helps with HEREDOC comparisons later.
+TTDIR="$GIT_BUILD_DIR/t/helper/" && export TTDIR
+PATH="$TTDIR:$PATH" && export PATH
+
+# Warning: use of 'test_cmp' may run test-tool.exe and/or git.exe
+# Warning: to do the actual diff/comparison, so the HEREDOCs here
+# Warning: only cover our actual calls to test-tool and/or git.
+# Warning: So you may see extra lines in artifact files when
+# Warning: interactively debugging.
+
+V=$(git version | sed -e 's/^git version //') && export V
+
+# There are multiple trace2 targets: normal, perf, and event.
+# Trace2 events will/can be written to each active target (subject
+# to whatever filtering that target decides to do).
+# Test each target independently.
+#
+# Defer setting GIT_TRACE2_PERF until the actual command we want to
+# test because hidden git and test-tool commands in the test
+# harness can contaminate our output.
+
+# We don't bother repeating the 001return and 002exit tests, since they
+# have coverage in the normal and perf targets.
+
+# Verb 003error
+#
+# To the above, add multiple 'error <msg>' events
+
+test_expect_success JSON_PP 'event stream, error event' '
+ test_when_finished "rm trace.event actual expect" &&
+ GIT_TRACE2_EVENT="$(pwd)/trace.event" test-tool trace2 003error "hello world" "this is a test" &&
+ perl "$TEST_DIRECTORY/t0212/parse_events.perl" <trace.event >actual &&
+ sed -e "s/^|//" >expect <<-EOF &&
+ |VAR1 = {
+ | "_SID0_":{
+ | "argv":[
+ | "_EXE_",
+ | "trace2",
+ | "003error",
+ | "hello world",
+ | "this is a test"
+ | ],
+ | "errors":[
+ | "%s",
+ | "%s"
+ | ],
+ | "exit_code":0,
+ | "hierarchy":"trace2",
+ | "name":"trace2",
+ | "version":"$V"
+ | }
+ |};
+ EOF
+ test_cmp expect actual
+'
+
+# Verb 004child
+#
+# Test nested spawning of child processes.
+#
+# Conceptually, this looks like:
+# P1: TT trace2 004child
+# P2: |--- TT trace2 004child
+# P3: |--- TT trace2 001return 0
+
+test_expect_success JSON_PP 'event stream, return code 0' '
+ test_when_finished "rm trace.event actual expect" &&
+ GIT_TRACE2_EVENT="$(pwd)/trace.event" test-tool trace2 004child test-tool trace2 004child test-tool trace2 001return 0 &&
+ perl "$TEST_DIRECTORY/t0212/parse_events.perl" <trace.event >actual &&
+ sed -e "s/^|//" >expect <<-EOF &&
+ |VAR1 = {
+ | "_SID0_":{
+ | "argv":[
+ | "_EXE_",
+ | "trace2",
+ | "004child",
+ | "test-tool",
+ | "trace2",
+ | "004child",
+ | "test-tool",
+ | "trace2",
+ | "001return",
+ | "0"
+ | ],
+ | "child":{
+ | "0":{
+ | "child_argv":[
+ | "_EXE_",
+ | "trace2",
+ | "004child",
+ | "test-tool",
+ | "trace2",
+ | "001return",
+ | "0"
+ | ],
+ | "child_class":"?",
+ | "child_code":0,
+ | "use_shell":0
+ | }
+ | },
+ | "exit_code":0,
+ | "hierarchy":"trace2",
+ | "name":"trace2",
+ | "version":"$V"
+ | },
+ | "_SID0_/_SID1_":{
+ | "argv":[
+ | "_EXE_",
+ | "trace2",
+ | "004child",
+ | "test-tool",
+ | "trace2",
+ | "001return",
+ | "0"
+ | ],
+ | "child":{
+ | "0":{
+ | "child_argv":[
+ | "_EXE_",
+ | "trace2",
+ | "001return",
+ | "0"
+ | ],
+ | "child_class":"?",
+ | "child_code":0,
+ | "use_shell":0
+ | }
+ | },
+ | "exit_code":0,
+ | "hierarchy":"trace2/trace2",
+ | "name":"trace2",
+ | "version":"$V"
+ | },
+ | "_SID0_/_SID1_/_SID2_":{
+ | "argv":[
+ | "_EXE_",
+ | "trace2",
+ | "001return",
+ | "0"
+ | ],
+ | "exit_code":0,
+ | "hierarchy":"trace2/trace2/trace2",
+ | "name":"trace2",
+ | "version":"$V"
+ | }
+ |};
+ EOF
+ test_cmp expect actual
+'
+
+# Test listing of all "interesting" config settings.
+
+test_expect_success JSON_PP 'event stream, list config' '
+ test_when_finished "rm trace.event actual expect" &&
+ git config --local t0212.abc 1 &&
+ git config --local t0212.def "hello world" &&
+ GIT_TRACE2_EVENT="$(pwd)/trace.event" GIT_TRACE2_CONFIG_PARAMS="t0212.*" test-tool trace2 001return 0 &&
+ perl "$TEST_DIRECTORY/t0212/parse_events.perl" <trace.event >actual &&
+ sed -e "s/^|//" >expect <<-EOF &&
+ |VAR1 = {
+ | "_SID0_":{
+ | "argv":[
+ | "_EXE_",
+ | "trace2",
+ | "001return",
+ | "0"
+ | ],
+ | "exit_code":0,
+ | "hierarchy":"trace2",
+ | "name":"trace2",
+ | "params":[
+ | {
+ | "param":"t0212.abc",
+ | "value":"1"
+ | },
+ | {
+ | "param":"t0212.def",
+ | "value":"hello world"
+ | }
+ | ],
+ | "version":"$V"
+ | }
+ |};
+ EOF
+ test_cmp expect actual
+'
+
+# Test listing of all "interesting" environment variables.
+
+test_expect_success JSON_PP 'event stream, list env vars' '
+ test_when_finished "rm trace.event actual expect" &&
+ GIT_TRACE2_EVENT="$(pwd)/trace.event" \
+ GIT_TRACE2_ENV_VARS="A_VAR,OTHER_VAR,MISSING" \
+ A_VAR=1 OTHER_VAR="hello world" test-tool trace2 001return 0 &&
+ perl "$TEST_DIRECTORY/t0212/parse_events.perl" <trace.event >actual &&
+ sed -e "s/^|//" >expect <<-EOF &&
+ |VAR1 = {
+ | "_SID0_":{
+ | "argv":[
+ | "_EXE_",
+ | "trace2",
+ | "001return",
+ | "0"
+ | ],
+ | "exit_code":0,
+ | "hierarchy":"trace2",
+ | "name":"trace2",
+ | "params":[
+ | {
+ | "param":"A_VAR",
+ | "value":"1"
+ | },
+ | {
+ | "param":"OTHER_VAR",
+ | "value":"hello world"
+ | }
+ | ],
+ | "version":"$V"
+ | }
+ |};
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success JSON_PP 'basic trace2_data' '
+ test_when_finished "rm trace.event actual expect" &&
+ GIT_TRACE2_EVENT="$(pwd)/trace.event" test-tool trace2 006data test_category k1 v1 test_category k2 v2 &&
+ perl "$TEST_DIRECTORY/t0212/parse_events.perl" <trace.event >actual &&
+ sed -e "s/^|//" >expect <<-EOF &&
+ |VAR1 = {
+ | "_SID0_":{
+ | "argv":[
+ | "_EXE_",
+ | "trace2",
+ | "006data",
+ | "test_category",
+ | "k1",
+ | "v1",
+ | "test_category",
+ | "k2",
+ | "v2"
+ | ],
+ | "data":{
+ | "test_category":{
+ | "k1":"v1",
+ | "k2":"v2"
+ | }
+ | },
+ | "exit_code":0,
+ | "hierarchy":"trace2",
+ | "name":"trace2",
+ | "version":"$V"
+ | }
+ |};
+ EOF
+ test_cmp expect actual
+'
+
+# Now test without environment variables and get all Trace2 settings
+# from the global config.
+
+test_expect_success JSON_PP 'using global config, event stream, error event' '
+ test_when_finished "rm trace.event actual expect" &&
+ test_config_global trace2.eventTarget "$(pwd)/trace.event" &&
+ test-tool trace2 003error "hello world" "this is a test" &&
+ perl "$TEST_DIRECTORY/t0212/parse_events.perl" <trace.event >actual &&
+ sed -e "s/^|//" >expect <<-EOF &&
+ |VAR1 = {
+ | "_SID0_":{
+ | "argv":[
+ | "_EXE_",
+ | "trace2",
+ | "003error",
+ | "hello world",
+ | "this is a test"
+ | ],
+ | "errors":[
+ | "%s",
+ | "%s"
+ | ],
+ | "exit_code":0,
+ | "hierarchy":"trace2",
+ | "name":"trace2",
+ | "version":"$V"
+ | }
+ |};
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'discard traces when there are too many files' '
+ mkdir trace_target_dir &&
+ test_when_finished "rm -r trace_target_dir" &&
+ (
+ GIT_TRACE2_MAX_FILES=5 &&
+ export GIT_TRACE2_MAX_FILES &&
+ cd trace_target_dir &&
+ test_seq $GIT_TRACE2_MAX_FILES >../expected_filenames.txt &&
+ xargs touch <../expected_filenames.txt &&
+ cd .. &&
+ GIT_TRACE2_EVENT="$(pwd)/trace_target_dir" test-tool trace2 001return 0
+ ) &&
+ echo git-trace2-discard >>expected_filenames.txt &&
+ ls trace_target_dir >ls_output.txt &&
+ test_cmp expected_filenames.txt ls_output.txt &&
+ head -n1 trace_target_dir/git-trace2-discard | grep \"event\":\"version\" &&
+ head -n2 trace_target_dir/git-trace2-discard | tail -n1 | grep \"event\":\"too_many_files\"
+'
+
+test_done
diff --git a/t/t0212/parse_events.perl b/t/t0212/parse_events.perl
new file mode 100644
index 0000000..6584bb5
--- /dev/null
+++ b/t/t0212/parse_events.perl
@@ -0,0 +1,251 @@
+#!/usr/bin/perl
+#
+# Parse event stream and convert individual events into a summary
+# record for the process.
+#
+# Git.exe generates one or more "event" records for each API method,
+# such as "start <argv>" and "exit <code>", during the life of the git
+# process. Additionally, the input may contain interleaved events
+# from multiple concurrent git processes and/or multiple threads from
+# within a git process.
+#
+# Accumulate events for each process (based on its unique SID) in a
+# dictionary and emit process summary records.
+#
+# Convert some of the variable fields (such as elapsed time) into
+# placeholders (or omit them) to make HEREDOC comparisons easier in
+# the test scripts.
+#
+# We may also omit fields not (currently) useful for testing purposes.
+
+use strict;
+use warnings;
+use JSON::PP;
+use Data::Dumper;
+use Getopt::Long;
+
+# The version of the trace2 event target format that we understand.
+# This is reported in the 'version' event in the 'evt' field.
+# It comes from the GIT_TRACE2_EVENT_VERSION macro in trace2/tr2_tgt_event.c
+my $evt_version = '1';
+
+my $show_children = 1;
+my $show_exec = 1;
+my $show_threads = 1;
+
+# A hack to generate test HEREDOC data for pasting into the test script.
+# Usage:
+# cd "t/trash directory.t0212-trace2-event"
+# $TT trace ... >trace.event
+# VV=$(../../git.exe version | sed -e 's/^git version //')
+# perl ../t0212/parse_events.perl --HEREDOC --VERSION=$VV <trace.event >heredoc
+# Then paste heredoc into your new test.
+
+my $gen_heredoc = 0;
+my $gen_version = '';
+
+GetOptions("children!" => \$show_children,
+ "exec!" => \$show_exec,
+ "threads!" => \$show_threads,
+ "HEREDOC!" => \$gen_heredoc,
+ "VERSION=s" => \$gen_version )
+ or die("Error in command line arguments\n");
+
+
+# SIDs contains timestamps and PIDs of the process and its parents.
+# This makes it difficult to match up in a HEREDOC in the test script.
+# Build a map from actual SIDs to predictable constant values and yet
+# keep the parent/child relationships. For example:
+# {..., "sid":"1539706952458276-8652", ...}
+# {..., "sid":"1539706952458276-8652/1539706952649493-15452", ...}
+# becomes:
+# {..., "sid":"_SID1_", ...}
+# {..., "sid":"_SID1_/_SID2_", ...}
+my $sid_map;
+my $sid_count = 0;
+
+my $processes;
+
+while (<>) {
+ my $line = decode_json( $_ );
+
+ my $sid = "";
+ my $sid_sep = "";
+
+ my $raw_sid = $line->{'sid'};
+ my @raw_sid_parts = split /\//, $raw_sid;
+ foreach my $raw_sid_k (@raw_sid_parts) {
+ if (!exists $sid_map->{$raw_sid_k}) {
+ $sid_map->{$raw_sid_k} = '_SID' . $sid_count . '_';
+ $sid_count++;
+ }
+ $sid = $sid . $sid_sep . $sid_map->{$raw_sid_k};
+ $sid_sep = '/';
+ }
+
+ my $event = $line->{'event'};
+
+ if ($event eq 'version') {
+ $processes->{$sid}->{'version'} = $line->{'exe'};
+ if ($gen_heredoc == 1 && $gen_version eq $line->{'exe'}) {
+ # If we are generating data FOR the test script, replace
+ # the reported git.exe version with a reference to an
+ # environment variable. When our output is pasted into
+ # the test script, it will then be expanded in future
+ # test runs to the THEN current version of git.exe.
+ # We assume that the test script uses env var $V.
+ $processes->{$sid}->{'version'} = "\$V";
+ }
+ }
+
+ elsif ($event eq 'start') {
+ $processes->{$sid}->{'argv'} = $line->{'argv'};
+ $processes->{$sid}->{'argv'}[0] = "_EXE_";
+ }
+
+ elsif ($event eq 'exit') {
+ $processes->{$sid}->{'exit_code'} = $line->{'code'};
+ }
+
+ elsif ($event eq 'atexit') {
+ $processes->{$sid}->{'exit_code'} = $line->{'code'};
+ }
+
+ elsif ($event eq 'error') {
+ # For HEREDOC purposes, use the error message format string if
+ # available, rather than the formatted message (which probably
+ # has an absolute pathname).
+ if (exists $line->{'fmt'}) {
+ push( @{$processes->{$sid}->{'errors'}}, $line->{'fmt'} );
+ }
+ elsif (exists $line->{'msg'}) {
+ push( @{$processes->{$sid}->{'errors'}}, $line->{'msg'} );
+ }
+ }
+
+ elsif ($event eq 'cmd_path') {
+ ## $processes->{$sid}->{'path'} = $line->{'path'};
+ #
+ # Like in the 'start' event, we need to replace the value of
+ # argv[0] with a token for HEREDOC purposes. However, the
+ # event is only emitted when RUNTIME_PREFIX is defined, so
+ # just omit it for testing purposes.
+ # $processes->{$sid}->{'path'} = "_EXE_";
+ }
+
+ elsif ($event eq 'cmd_name') {
+ $processes->{$sid}->{'name'} = $line->{'name'};
+ $processes->{$sid}->{'hierarchy'} = $line->{'hierarchy'};
+ }
+
+ elsif ($event eq 'alias') {
+ $processes->{$sid}->{'alias'}->{'key'} = $line->{'alias'};
+ $processes->{$sid}->{'alias'}->{'argv'} = $line->{'argv'};
+ }
+
+ elsif ($event eq 'def_param') {
+ my $kv;
+ $kv->{'param'} = $line->{'param'};
+ $kv->{'value'} = $line->{'value'};
+ push( @{$processes->{$sid}->{'params'}}, $kv );
+ }
+
+ elsif ($event eq 'child_start') {
+ if ($show_children == 1) {
+ $processes->{$sid}->{'child'}->{$line->{'child_id'}}->{'child_class'} = $line->{'child_class'};
+ $processes->{$sid}->{'child'}->{$line->{'child_id'}}->{'child_argv'} = $line->{'argv'};
+ $processes->{$sid}->{'child'}->{$line->{'child_id'}}->{'child_argv'}[0] = "_EXE_";
+ $processes->{$sid}->{'child'}->{$line->{'child_id'}}->{'use_shell'} = $line->{'use_shell'} ? 1 : 0;
+ }
+ }
+
+ elsif ($event eq 'child_exit') {
+ if ($show_children == 1) {
+ $processes->{$sid}->{'child'}->{$line->{'child_id'}}->{'child_code'} = $line->{'code'};
+ }
+ }
+
+ # TODO decide what information we want to test from thread events.
+
+ elsif ($event eq 'thread_start') {
+ if ($show_threads == 1) {
+ }
+ }
+
+ elsif ($event eq 'thread_exit') {
+ if ($show_threads == 1) {
+ }
+ }
+
+ # TODO decide what information we want to test from exec events.
+
+ elsif ($event eq 'exec') {
+ if ($show_exec == 1) {
+ }
+ }
+
+ elsif ($event eq 'exec_result') {
+ if ($show_exec == 1) {
+ }
+ }
+
+ elsif ($event eq 'def_param') {
+ # Accumulate parameter key/value pairs by key rather than in an array
+ # so that we get overwrite (last one wins) effects.
+ $processes->{$sid}->{'params'}->{$line->{'param'}} = $line->{'value'};
+ }
+
+ elsif ($event eq 'def_repo') {
+ # $processes->{$sid}->{'repos'}->{$line->{'repo'}} = $line->{'worktree'};
+ $processes->{$sid}->{'repos'}->{$line->{'repo'}} = "_WORKTREE_";
+ }
+
+ # A series of potentially nested and threaded region and data events
+ # is fundamentally incompatibile with the type of summary record we
+ # are building in this script. Since they are intended for
+ # perf-trace-like analysis rather than a result summary, we ignore
+ # most of them here.
+
+ # elsif ($event eq 'region_enter') {
+ # }
+ # elsif ($event eq 'region_leave') {
+ # }
+
+ elsif ($event eq 'data') {
+ my $cat = $line->{'category'};
+ if ($cat eq 'test_category') {
+
+ my $key = $line->{'key'};
+ my $value = $line->{'value'};
+ $processes->{$sid}->{'data'}->{$cat}->{$key} = $value;
+ }
+ }
+
+ # This trace2 target does not emit 'printf' events.
+ #
+ # elsif ($event eq 'printf') {
+ # }
+}
+
+# Dump the resulting hash into something that we can compare against
+# in the test script. These options make Dumper output look a little
+# bit like JSON. Also convert variable references of the form "$VAR*"
+# so that the matching HEREDOC doesn't need to escape it.
+
+$Data::Dumper::Sortkeys = 1;
+$Data::Dumper::Indent = 1;
+$Data::Dumper::Purity = 1;
+$Data::Dumper::Pair = ':';
+
+my $out = Dumper($processes);
+$out =~ s/'/"/g;
+$out =~ s/\$VAR/VAR/g;
+
+# Finally, if we're running this script to generate (manually confirmed)
+# data to add to the test script, guard the indentation.
+
+if ($gen_heredoc == 1) {
+ $out =~ s/^/\t\|/gms;
+}
+
+print $out;
diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh
index 6d44e7e..bc2d740 100755
--- a/t/t0300-credentials.sh
+++ b/t/t0300-credentials.sh
@@ -315,6 +315,102 @@ test_expect_success 'do not match configured credential' '
EOF
'
+test_expect_success 'match multiple configured helpers' '
+ test_config credential.helper "verbatim \"\" \"\"" &&
+ test_config credential.https://example.com.helper "$HELPER" &&
+ check fill <<-\EOF
+ protocol=https
+ host=example.com
+ path=repo.git
+ --
+ protocol=https
+ host=example.com
+ username=foo
+ password=bar
+ --
+ verbatim: get
+ verbatim: protocol=https
+ verbatim: host=example.com
+ EOF
+'
+
+test_expect_success 'match multiple configured helpers with URLs' '
+ test_config credential.https://example.com/repo.git.helper "verbatim \"\" \"\"" &&
+ test_config credential.https://example.com.helper "$HELPER" &&
+ check fill <<-\EOF
+ protocol=https
+ host=example.com
+ path=repo.git
+ --
+ protocol=https
+ host=example.com
+ username=foo
+ password=bar
+ --
+ verbatim: get
+ verbatim: protocol=https
+ verbatim: host=example.com
+ EOF
+'
+
+test_expect_success 'match percent-encoded values' '
+ test_config credential.https://example.com/%2566.git.helper "$HELPER" &&
+ check fill <<-\EOF
+ url=https://example.com/%2566.git
+ --
+ protocol=https
+ host=example.com
+ username=foo
+ password=bar
+ --
+ EOF
+'
+
+test_expect_success 'match percent-encoded UTF-8 values in path' '
+ test_config credential.https://example.com.useHttpPath true &&
+ test_config credential.https://example.com/perú.git.helper "$HELPER" &&
+ check fill <<-\EOF
+ url=https://example.com/per%C3%BA.git
+ --
+ protocol=https
+ host=example.com
+ path=perú.git
+ username=foo
+ password=bar
+ --
+ EOF
+'
+
+test_expect_success 'match percent-encoded values in username' '
+ test_config credential.https://user%2fname@example.com/foo/bar.git.helper "$HELPER" &&
+ check fill <<-\EOF
+ url=https://user%2fname@example.com/foo/bar.git
+ --
+ protocol=https
+ host=example.com
+ username=foo
+ password=bar
+ --
+ EOF
+'
+
+test_expect_success 'fetch with multiple path components' '
+ test_unconfig credential.helper &&
+ test_config credential.https://example.com/foo/repo.git.helper "verbatim foo bar" &&
+ check fill <<-\EOF
+ url=https://example.com/foo/repo.git
+ --
+ protocol=https
+ host=example.com
+ username=foo
+ password=bar
+ --
+ verbatim: get
+ verbatim: protocol=https
+ verbatim: host=example.com
+ EOF
+'
+
test_expect_success 'pull username from config' '
test_config credential.https://example.com.username foo &&
check fill <<-\EOF
@@ -330,6 +426,63 @@ test_expect_success 'pull username from config' '
EOF
'
+test_expect_success 'honors username from URL over helper (URL)' '
+ test_config credential.https://example.com.username bob &&
+ test_config credential.https://example.com.helper "verbatim \"\" bar" &&
+ check fill <<-\EOF
+ url=https://alice@example.com
+ --
+ protocol=https
+ host=example.com
+ username=alice
+ password=bar
+ --
+ verbatim: get
+ verbatim: protocol=https
+ verbatim: host=example.com
+ verbatim: username=alice
+ EOF
+'
+
+test_expect_success 'honors username from URL over helper (components)' '
+ test_config credential.https://example.com.username bob &&
+ test_config credential.https://example.com.helper "verbatim \"\" bar" &&
+ check fill <<-\EOF
+ protocol=https
+ host=example.com
+ username=alice
+ --
+ protocol=https
+ host=example.com
+ username=alice
+ password=bar
+ --
+ verbatim: get
+ verbatim: protocol=https
+ verbatim: host=example.com
+ verbatim: username=alice
+ EOF
+'
+
+test_expect_success 'last matching username wins' '
+ test_config credential.https://example.com/path.git.username bob &&
+ test_config credential.https://example.com.username alice &&
+ test_config credential.https://example.com.helper "verbatim \"\" bar" &&
+ check fill <<-\EOF
+ url=https://example.com/path.git
+ --
+ protocol=https
+ host=example.com
+ username=alice
+ password=bar
+ --
+ verbatim: get
+ verbatim: protocol=https
+ verbatim: host=example.com
+ verbatim: username=alice
+ EOF
+'
+
test_expect_success 'http paths can be part of context' '
check fill "verbatim foo bar" <<-\EOF &&
protocol=https
@@ -364,6 +517,26 @@ test_expect_success 'http paths can be part of context' '
EOF
'
+test_expect_success 'context uses urlmatch' '
+ test_config "credential.https://*.org.useHttpPath" true &&
+ check fill "verbatim foo bar" <<-\EOF
+ protocol=https
+ host=example.org
+ path=foo.git
+ --
+ protocol=https
+ host=example.org
+ path=foo.git
+ username=foo
+ password=bar
+ --
+ verbatim: get
+ verbatim: protocol=https
+ verbatim: host=example.org
+ verbatim: path=foo.git
+ EOF
+'
+
test_expect_success 'helpers can abort the process' '
test_must_fail git \
-c credential.helper=quit \
@@ -404,7 +577,7 @@ test_expect_success 'url parser rejects embedded newlines' '
url=https://one.example.com?%0ahost=two.example.com/
EOF
cat >expect <<-\EOF &&
- warning: url contains a newline in its host component: https://one.example.com?%0ahost=two.example.com/
+ warning: url contains a newline in its path component: https://one.example.com?%0ahost=two.example.com/
fatal: credential url cannot be parsed: https://one.example.com?%0ahost=two.example.com/
EOF
test_i18ncmp expect stderr
@@ -447,4 +620,76 @@ test_expect_success 'credential system refuses to work with missing protocol' '
test_i18ncmp expect stderr
'
+# usage: check_host_and_path <url> <expected-host> <expected-path>
+check_host_and_path () {
+ # we always parse the path component, but we need this to make sure it
+ # is passed to the helper
+ test_config credential.useHTTPPath true &&
+ check fill "verbatim user pass" <<-EOF
+ url=$1
+ --
+ protocol=https
+ host=$2
+ path=$3
+ username=user
+ password=pass
+ --
+ verbatim: get
+ verbatim: protocol=https
+ verbatim: host=$2
+ verbatim: path=$3
+ EOF
+}
+
+test_expect_success 'url parser handles bare query marker' '
+ check_host_and_path https://example.com?foo.git example.com ?foo.git
+'
+
+test_expect_success 'url parser handles bare fragment marker' '
+ check_host_and_path https://example.com#foo.git example.com "#foo.git"
+'
+
+test_expect_success 'url parser not confused by encoded markers' '
+ check_host_and_path https://example.com%23%3f%2f/foo.git \
+ "example.com#?/" foo.git
+'
+
+test_expect_success 'credential config with partial URLs' '
+ echo "echo password=yep" | write_script git-credential-yep &&
+ test_write_lines url=https://user@example.com/repo.git >stdin &&
+ for partial in \
+ example.com \
+ user@example.com \
+ https:// \
+ https://example.com \
+ https://example.com/ \
+ https://user@example.com \
+ https://user@example.com/ \
+ https://example.com/repo.git \
+ https://user@example.com/repo.git \
+ /repo.git
+ do
+ git -c credential.$partial.helper=yep \
+ credential fill <stdin >stdout &&
+ grep yep stdout ||
+ return 1
+ done &&
+
+ for partial in \
+ dont.use.this \
+ http:// \
+ /repo
+ do
+ git -c credential.$partial.helper=yep \
+ credential fill <stdin >stdout &&
+ ! grep yep stdout ||
+ return 1
+ done &&
+
+ git -c credential.$partial.helper=yep \
+ -c credential.with%0anewline.username=uh-oh \
+ credential fill <stdin >stdout 2>stderr &&
+ test_i18ngrep "skipping credential lookup for key" stderr
+'
+
test_done
diff --git a/t/t0301-credential-cache.sh b/t/t0301-credential-cache.sh
index fd92533..ebd5fa5 100755
--- a/t/t0301-credential-cache.sh
+++ b/t/t0301-credential-cache.sh
@@ -10,7 +10,7 @@ test -z "$NO_UNIX_SOCKETS" || {
}
# don't leave a stale daemon running
-trap 'code=$?; git credential-cache exit; (exit $code); die' EXIT
+test_atexit 'git credential-cache exit'
# test that the daemon works with no special setup
helper_test cache
@@ -108,9 +108,4 @@ test_expect_success SYMLINKS 'use user socket if user directory is a symlink to
helper_test_timeout cache --timeout=1
-# we can't rely on our "trap" above working after test_done,
-# as test_done will delete the trash directory containing
-# our socket, leaving us with no way to access the daemon.
-git credential-cache exit
-
test_done
diff --git a/t/t0302-credential-store.sh b/t/t0302-credential-store.sh
index d6b54e8..716bf1a 100755
--- a/t/t0302-credential-store.sh
+++ b/t/t0302-credential-store.sh
@@ -107,7 +107,6 @@ test_expect_success 'store: if both xdg and home files exist, only store in home
test_must_be_empty "$HOME/.config/git/credentials"
'
-
test_expect_success 'erase: erase matching credentials from both xdg and home files' '
echo "https://home-user:home-pass@example.com" >"$HOME/.git-credentials" &&
mkdir -p "$HOME/.config/git" &&
@@ -120,4 +119,94 @@ test_expect_success 'erase: erase matching credentials from both xdg and home fi
test_must_be_empty "$HOME/.config/git/credentials"
'
+invalid_credential_test() {
+ test_expect_success "get: ignore credentials without $1 as invalid" '
+ echo "$2" >"$HOME/.git-credentials" &&
+ check fill store <<-\EOF
+ protocol=https
+ host=example.com
+ --
+ protocol=https
+ host=example.com
+ username=askpass-username
+ password=askpass-password
+ --
+ askpass: Username for '\''https://example.com'\'':
+ askpass: Password for '\''https://askpass-username@example.com'\'':
+ --
+ EOF
+ '
+}
+
+invalid_credential_test "scheme" ://user:pass@example.com
+invalid_credential_test "valid host/path" https://user:pass@
+invalid_credential_test "username/password" https://pass@example.com
+
+test_expect_success 'get: credentials with DOS line endings are invalid' '
+ printf "https://user:pass@example.com\r\n" >"$HOME/.git-credentials" &&
+ check fill store <<-\EOF
+ protocol=https
+ host=example.com
+ --
+ protocol=https
+ host=example.com
+ username=askpass-username
+ password=askpass-password
+ --
+ askpass: Username for '\''https://example.com'\'':
+ askpass: Password for '\''https://askpass-username@example.com'\'':
+ --
+ EOF
+'
+
+test_expect_success 'get: credentials with path and DOS line endings are valid' '
+ printf "https://user:pass@example.com/repo.git\r\n" >"$HOME/.git-credentials" &&
+ check fill store <<-\EOF
+ url=https://example.com/repo.git
+ --
+ protocol=https
+ host=example.com
+ username=user
+ password=pass
+ --
+ EOF
+'
+
+test_expect_success 'get: credentials with DOS line endings are invalid if path is relevant' '
+ printf "https://user:pass@example.com/repo.git\r\n" >"$HOME/.git-credentials" &&
+ test_config credential.useHttpPath true &&
+ check fill store <<-\EOF
+ url=https://example.com/repo.git
+ --
+ protocol=https
+ host=example.com
+ path=repo.git
+ username=askpass-username
+ password=askpass-password
+ --
+ askpass: Username for '\''https://example.com/repo.git'\'':
+ askpass: Password for '\''https://askpass-username@example.com/repo.git'\'':
+ --
+ EOF
+'
+
+test_expect_success 'get: store file can contain empty/bogus lines' '
+ echo "" >"$HOME/.git-credentials" &&
+ q_to_tab <<-\CREDENTIAL >>"$HOME/.git-credentials" &&
+ #comment
+ Q
+ https://user:pass@example.com
+ CREDENTIAL
+ check fill store <<-\EOF
+ protocol=https
+ host=example.com
+ --
+ protocol=https
+ host=example.com
+ username=user
+ password=pass
+ --
+ EOF
+'
+
test_done
diff --git a/t/t0410-partial-clone.sh b/t/t0410-partial-clone.sh
index ba3887f..a3988bd 100755
--- a/t/t0410-partial-clone.sh
+++ b/t/t0410-partial-clone.sh
@@ -26,7 +26,7 @@ promise_and_delete () {
test_expect_success 'extensions.partialclone without filter' '
test_create_repo server &&
git clone --filter="blob:none" "file://$(pwd)/server" client &&
- git -C client config --unset core.partialclonefilter &&
+ git -C client config --unset remote.origin.partialclonefilter &&
git -C client fetch origin
'
@@ -166,8 +166,9 @@ test_expect_success 'fetching of missing objects' '
# associated packfile contains the object
ls repo/.git/objects/pack/pack-*.promisor >promisorlist &&
test_line_count = 1 promisorlist &&
- IDX=$(cat promisorlist | sed "s/promisor$/idx/") &&
- git verify-pack --verbose "$IDX" | grep "$HASH"
+ IDX=$(sed "s/promisor$/idx/" promisorlist) &&
+ git verify-pack --verbose "$IDX" >out &&
+ grep "$HASH" out
'
test_expect_success 'fetching of missing objects works with ref-in-want enabled' '
@@ -182,8 +183,55 @@ test_expect_success 'fetching of missing objects works with ref-in-want enabled'
grep "git< fetch=.*ref-in-want" trace
'
+test_expect_success 'fetching of missing objects from another promisor remote' '
+ git clone "file://$(pwd)/server" server2 &&
+ test_commit -C server2 bar &&
+ git -C server2 repack -a -d --write-bitmap-index &&
+ HASH2=$(git -C server2 rev-parse bar) &&
+
+ git -C repo remote add server2 "file://$(pwd)/server2" &&
+ git -C repo config remote.server2.promisor true &&
+ git -C repo cat-file -p "$HASH2" &&
+
+ git -C repo fetch server2 &&
+ rm -rf repo/.git/objects/* &&
+ git -C repo cat-file -p "$HASH2" &&
+
+ # Ensure that the .promisor file is written, and check that its
+ # associated packfile contains the object
+ ls repo/.git/objects/pack/pack-*.promisor >promisorlist &&
+ test_line_count = 1 promisorlist &&
+ IDX=$(sed "s/promisor$/idx/" promisorlist) &&
+ git verify-pack --verbose "$IDX" >out &&
+ grep "$HASH2" out
+'
+
+test_expect_success 'fetching of missing objects configures a promisor remote' '
+ git clone "file://$(pwd)/server" server3 &&
+ test_commit -C server3 baz &&
+ git -C server3 repack -a -d --write-bitmap-index &&
+ HASH3=$(git -C server3 rev-parse baz) &&
+ git -C server3 config uploadpack.allowfilter 1 &&
+
+ rm repo/.git/objects/pack/pack-*.promisor &&
+
+ git -C repo remote add server3 "file://$(pwd)/server3" &&
+ git -C repo fetch --filter="blob:none" server3 $HASH3 &&
+
+ test_cmp_config -C repo true remote.server3.promisor &&
+
+ # Ensure that the .promisor file is written, and check that its
+ # associated packfile contains the object
+ ls repo/.git/objects/pack/pack-*.promisor >promisorlist &&
+ test_line_count = 1 promisorlist &&
+ IDX=$(sed "s/promisor$/idx/" promisorlist) &&
+ git verify-pack --verbose "$IDX" >out &&
+ grep "$HASH3" out
+'
+
test_expect_success 'fetching of missing blobs works' '
- rm -rf server repo &&
+ rm -rf server server2 repo &&
+ rm -rf server server3 repo &&
test_create_repo server &&
test_commit -C server foo &&
git -C server repack -a -d --write-bitmap-index &&
@@ -234,7 +282,7 @@ test_expect_success 'rev-list stops traversal at missing and promised commit' '
git -C repo config core.repositoryformatversion 1 &&
git -C repo config extensions.partialclone "arbitrary string" &&
- GIT_TEST_COMMIT_GRAPH=0 git -C repo rev-list --exclude-promisor-objects --objects bar >out &&
+ GIT_TEST_COMMIT_GRAPH=0 git -C repo -c core.commitGraph=false rev-list --exclude-promisor-objects --objects bar >out &&
grep $(git -C repo rev-parse bar) out &&
! grep $FOO out
'
@@ -349,7 +397,7 @@ test_expect_success 'rev-list stops traversal at promisor commit, tree, and blob
grep $(git -C repo rev-parse bar) out # sanity check that some walking was done
'
-test_expect_success 'rev-list accepts missing and promised objects on command line' '
+test_expect_success 'rev-list dies for missing objects on cmd line' '
rm -rf repo &&
test_create_repo repo &&
test_commit -C repo foo &&
@@ -366,7 +414,32 @@ test_expect_success 'rev-list accepts missing and promised objects on command li
git -C repo config core.repositoryformatversion 1 &&
git -C repo config extensions.partialclone "arbitrary string" &&
- git -C repo rev-list --exclude-promisor-objects --objects "$COMMIT" "$TREE" "$BLOB"
+
+ for OBJ in "$COMMIT" "$TREE" "$BLOB"; do
+ test_must_fail git -C repo rev-list --objects \
+ --exclude-promisor-objects "$OBJ" &&
+ test_must_fail git -C repo rev-list --objects-edge-aggressive \
+ --exclude-promisor-objects "$OBJ" &&
+
+ # Do not die or crash when --ignore-missing is passed.
+ git -C repo rev-list --ignore-missing --objects \
+ --exclude-promisor-objects "$OBJ" &&
+ git -C repo rev-list --ignore-missing --objects-edge-aggressive \
+ --exclude-promisor-objects "$OBJ"
+ done
+'
+
+test_expect_success 'single promisor remote can be re-initialized gracefully' '
+ # ensure one promisor is in the promisors list
+ rm -rf repo &&
+ test_create_repo repo &&
+ test_create_repo other &&
+ git -C repo remote add foo "file://$(pwd)/other" &&
+ git -C repo config remote.foo.promisor true &&
+ git -C repo config extensions.partialclone foo &&
+
+ # reinitialize the promisors list
+ git -C repo fetch --filter=blob:none foo
'
test_expect_success 'gc repacks promisor objects separately from non-promisor objects' '
@@ -480,7 +553,20 @@ test_expect_success 'gc stops traversal when a missing but promised object is re
! grep "$TREE_HASH" out
'
-LIB_HTTPD_PORT=12345 # default port, 410, cannot be used as non-root
+test_expect_success 'do not fetch when checking existence of tree we construct ourselves' '
+ rm -rf repo &&
+ test_create_repo repo &&
+ test_commit -C repo base &&
+ test_commit -C repo side1 &&
+ git -C repo checkout base &&
+ test_commit -C repo side2 &&
+
+ git -C repo config core.repositoryformatversion 1 &&
+ git -C repo config extensions.partialclone "arbitrary string" &&
+
+ git -C repo cherry-pick side1
+'
+
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
@@ -503,10 +589,12 @@ test_expect_success 'fetching of missing objects from an HTTP server' '
# associated packfile contains the object
ls repo/.git/objects/pack/pack-*.promisor >promisorlist &&
test_line_count = 1 promisorlist &&
- IDX=$(cat promisorlist | sed "s/promisor$/idx/") &&
- git verify-pack --verbose "$IDX" | grep "$HASH"
+ IDX=$(sed "s/promisor$/idx/" promisorlist) &&
+ git verify-pack --verbose "$IDX" >out &&
+ grep "$HASH" out
'
-stop_httpd
+# DO NOT add non-httpd-specific tests here, because the last part of this
+# test script is only executed when httpd is available and enabled.
test_done
diff --git a/t/t0500-progress-display.sh b/t/t0500-progress-display.sh
new file mode 100755
index 0000000..d2d088d
--- /dev/null
+++ b/t/t0500-progress-display.sh
@@ -0,0 +1,286 @@
+#!/bin/sh
+
+test_description='progress display'
+
+. ./test-lib.sh
+
+show_cr () {
+ tr '\015' Q | sed -e "s/Q/<CR>\\$LF/g"
+}
+
+test_expect_success 'simple progress display' '
+ cat >expect <<-\EOF &&
+ Working hard: 1<CR>
+ Working hard: 2<CR>
+ Working hard: 5<CR>
+ Working hard: 5, done.
+ EOF
+
+ cat >in <<-\EOF &&
+ update
+ progress 1
+ update
+ progress 2
+ progress 3
+ progress 4
+ update
+ progress 5
+ EOF
+ test-tool progress "Working hard" <in 2>stderr &&
+
+ show_cr <stderr >out &&
+ test_i18ncmp expect out
+'
+
+test_expect_success 'progress display with total' '
+ cat >expect <<-\EOF &&
+ Working hard: 33% (1/3)<CR>
+ Working hard: 66% (2/3)<CR>
+ Working hard: 100% (3/3)<CR>
+ Working hard: 100% (3/3), done.
+ EOF
+
+ cat >in <<-\EOF &&
+ progress 1
+ progress 2
+ progress 3
+ EOF
+ test-tool progress --total=3 "Working hard" <in 2>stderr &&
+
+ show_cr <stderr >out &&
+ test_i18ncmp expect out
+'
+
+test_expect_success 'progress display breaks long lines #1' '
+ sed -e "s/Z$//" >expect <<\EOF &&
+Working hard.......2.........3.........4.........5.........6: 0% (100/100000)<CR>
+Working hard.......2.........3.........4.........5.........6: 1% (1000/100000)<CR>
+Working hard.......2.........3.........4.........5.........6: Z
+ 10% (10000/100000)<CR>
+ 100% (100000/100000)<CR>
+ 100% (100000/100000), done.
+EOF
+
+ cat >in <<-\EOF &&
+ progress 100
+ progress 1000
+ progress 10000
+ progress 100000
+ EOF
+ test-tool progress --total=100000 \
+ "Working hard.......2.........3.........4.........5.........6" \
+ <in 2>stderr &&
+
+ show_cr <stderr >out &&
+ test_i18ncmp expect out
+'
+
+test_expect_success 'progress display breaks long lines #2' '
+ # Note: we do not need that many spaces after the title to cover up
+ # the last line before breaking the progress line.
+ sed -e "s/Z$//" >expect <<\EOF &&
+Working hard.......2.........3.........4.........5.........6: 0% (1/100000)<CR>
+Working hard.......2.........3.........4.........5.........6: 0% (2/100000)<CR>
+Working hard.......2.........3.........4.........5.........6: Z
+ 10% (10000/100000)<CR>
+ 100% (100000/100000)<CR>
+ 100% (100000/100000), done.
+EOF
+
+ cat >in <<-\EOF &&
+ update
+ progress 1
+ update
+ progress 2
+ progress 10000
+ progress 100000
+ EOF
+ test-tool progress --total=100000 \
+ "Working hard.......2.........3.........4.........5.........6" \
+ <in 2>stderr &&
+
+ show_cr <stderr >out &&
+ test_i18ncmp expect out
+'
+
+test_expect_success 'progress display breaks long lines #3 - even the first is too long' '
+ # Note: we do not actually need any spaces at the end of the title
+ # line, because there is no previous progress line to cover up.
+ sed -e "s/Z$//" >expect <<\EOF &&
+Working hard.......2.........3.........4.........5.........6: Z
+ 25% (25000/100000)<CR>
+ 50% (50000/100000)<CR>
+ 75% (75000/100000)<CR>
+ 100% (100000/100000)<CR>
+ 100% (100000/100000), done.
+EOF
+
+ cat >in <<-\EOF &&
+ progress 25000
+ progress 50000
+ progress 75000
+ progress 100000
+ EOF
+ test-tool progress --total=100000 \
+ "Working hard.......2.........3.........4.........5.........6" \
+ <in 2>stderr &&
+
+ show_cr <stderr >out &&
+ test_i18ncmp expect out
+'
+
+test_expect_success 'progress display breaks long lines #4 - title line matches terminal width' '
+ cat >expect <<\EOF &&
+Working hard.......2.........3.........4.........5.........6.........7.........:
+ 25% (25000/100000)<CR>
+ 50% (50000/100000)<CR>
+ 75% (75000/100000)<CR>
+ 100% (100000/100000)<CR>
+ 100% (100000/100000), done.
+EOF
+
+ cat >in <<-\EOF &&
+ progress 25000
+ progress 50000
+ progress 75000
+ progress 100000
+ EOF
+ test-tool progress --total=100000 \
+ "Working hard.......2.........3.........4.........5.........6.........7........." \
+ <in 2>stderr &&
+
+ show_cr <stderr >out &&
+ test_i18ncmp expect out
+'
+
+# Progress counter goes backwards, this should not happen in practice.
+test_expect_success 'progress shortens - crazy caller' '
+ cat >expect <<-\EOF &&
+ Working hard: 10% (100/1000)<CR>
+ Working hard: 20% (200/1000)<CR>
+ Working hard: 0% (1/1000) <CR>
+ Working hard: 100% (1000/1000)<CR>
+ Working hard: 100% (1000/1000), done.
+ EOF
+
+ cat >in <<-\EOF &&
+ progress 100
+ progress 200
+ progress 1
+ progress 1000
+ EOF
+ test-tool progress --total=1000 "Working hard" <in 2>stderr &&
+
+ show_cr <stderr >out &&
+ test_i18ncmp expect out
+'
+
+test_expect_success 'progress display with throughput' '
+ cat >expect <<-\EOF &&
+ Working hard: 10<CR>
+ Working hard: 20, 200.00 KiB | 100.00 KiB/s<CR>
+ Working hard: 30, 300.00 KiB | 100.00 KiB/s<CR>
+ Working hard: 40, 400.00 KiB | 100.00 KiB/s<CR>
+ Working hard: 40, 400.00 KiB | 100.00 KiB/s, done.
+ EOF
+
+ cat >in <<-\EOF &&
+ throughput 102400 1000
+ update
+ progress 10
+ throughput 204800 2000
+ update
+ progress 20
+ throughput 307200 3000
+ update
+ progress 30
+ throughput 409600 4000
+ update
+ progress 40
+ EOF
+ test-tool progress "Working hard" <in 2>stderr &&
+
+ show_cr <stderr >out &&
+ test_i18ncmp expect out
+'
+
+test_expect_success 'progress display with throughput and total' '
+ cat >expect <<-\EOF &&
+ Working hard: 25% (10/40)<CR>
+ Working hard: 50% (20/40), 200.00 KiB | 100.00 KiB/s<CR>
+ Working hard: 75% (30/40), 300.00 KiB | 100.00 KiB/s<CR>
+ Working hard: 100% (40/40), 400.00 KiB | 100.00 KiB/s<CR>
+ Working hard: 100% (40/40), 400.00 KiB | 100.00 KiB/s, done.
+ EOF
+
+ cat >in <<-\EOF &&
+ throughput 102400 1000
+ progress 10
+ throughput 204800 2000
+ progress 20
+ throughput 307200 3000
+ progress 30
+ throughput 409600 4000
+ progress 40
+ EOF
+ test-tool progress --total=40 "Working hard" <in 2>stderr &&
+
+ show_cr <stderr >out &&
+ test_i18ncmp expect out
+'
+
+test_expect_success 'cover up after throughput shortens' '
+ cat >expect <<-\EOF &&
+ Working hard: 1<CR>
+ Working hard: 2, 800.00 KiB | 400.00 KiB/s<CR>
+ Working hard: 3, 1.17 MiB | 400.00 KiB/s <CR>
+ Working hard: 4, 1.56 MiB | 400.00 KiB/s<CR>
+ Working hard: 4, 1.56 MiB | 400.00 KiB/s, done.
+ EOF
+
+ cat >in <<-\EOF &&
+ throughput 409600 1000
+ update
+ progress 1
+ throughput 819200 2000
+ update
+ progress 2
+ throughput 1228800 3000
+ update
+ progress 3
+ throughput 1638400 4000
+ update
+ progress 4
+ EOF
+ test-tool progress "Working hard" <in 2>stderr &&
+
+ show_cr <stderr >out &&
+ test_i18ncmp expect out
+'
+
+test_expect_success 'cover up after throughput shortens a lot' '
+ cat >expect <<-\EOF &&
+ Working hard: 1<CR>
+ Working hard: 2, 1000.00 KiB | 1000.00 KiB/s<CR>
+ Working hard: 3, 3.00 MiB | 1.50 MiB/s <CR>
+ Working hard: 3, 3.00 MiB | 1024.00 KiB/s, done.
+ EOF
+
+ cat >in <<-\EOF &&
+ throughput 1 1000
+ update
+ progress 1
+ throughput 1024000 2000
+ update
+ progress 2
+ throughput 3145728 3000
+ update
+ progress 3
+ EOF
+ test-tool progress "Working hard" <in 2>stderr &&
+
+ show_cr <stderr >out &&
+ test_i18ncmp expect out
+'
+
+test_done
diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh
index a377530..64b340f 100755
--- a/t/t1007-hash-object.sh
+++ b/t/t1007-hash-object.sh
@@ -9,22 +9,19 @@ echo_without_newline() {
}
test_blob_does_not_exist() {
- test_expect_success SHA1 'blob does not exist in database' "
+ test_expect_success 'blob does not exist in database' "
test_must_fail git cat-file blob $1
"
}
test_blob_exists() {
- test_expect_success SHA1 'blob exists in database' "
+ test_expect_success 'blob exists in database' "
git cat-file blob $1
"
}
hello_content="Hello World"
-hello_sha1=5e1c309dae7f45e0f39b1bf3ac3cd9db12e7d689
-
example_content="This is an example"
-example_sha1=ddd3f836d3e3fbb7ae289aa9ae83536f76956399
setup_repo() {
echo_without_newline "$hello_content" > hello
@@ -44,7 +41,16 @@ pop_repo() {
rm -rf $test_repo
}
-setup_repo
+test_expect_success 'setup' '
+ setup_repo &&
+ test_oid_cache <<-EOF
+ hello sha1:5e1c309dae7f45e0f39b1bf3ac3cd9db12e7d689
+ hello sha256:1e3b6c04d2eeb2b3e45c8a330445404c0b7cc7b257e2b097167d26f5230090c4
+
+ example sha1:ddd3f836d3e3fbb7ae289aa9ae83536f76956399
+ example sha256:b44fe1fe65589848253737db859bd490453510719d7424daab03daf0767b85ae
+ EOF
+'
# Argument checking
@@ -73,23 +79,23 @@ test_expect_success "Can't use --path with --no-filters" '
push_repo
-test_expect_success SHA1 'hash a file' '
- test $hello_sha1 = $(git hash-object hello)
+test_expect_success 'hash a file' '
+ test "$(test_oid hello)" = $(git hash-object hello)
'
-test_blob_does_not_exist $hello_sha1
+test_blob_does_not_exist "$(test_oid hello)"
-test_expect_success SHA1 'hash from stdin' '
- test $example_sha1 = $(git hash-object --stdin < example)
+test_expect_success 'hash from stdin' '
+ test "$(test_oid example)" = $(git hash-object --stdin < example)
'
-test_blob_does_not_exist $example_sha1
+test_blob_does_not_exist "$(test_oid example)"
-test_expect_success SHA1 'hash a file and write to database' '
- test $hello_sha1 = $(git hash-object -w hello)
+test_expect_success 'hash a file and write to database' '
+ test "$(test_oid hello)" = $(git hash-object -w hello)
'
-test_blob_exists $hello_sha1
+test_blob_exists "$(test_oid hello)"
test_expect_success 'git hash-object --stdin file1 <file0 first operates on file0, then file1' '
echo foo > file1 &&
@@ -161,11 +167,11 @@ pop_repo
for args in "-w --stdin" "--stdin -w"; do
push_repo
- test_expect_success SHA1 "hash from stdin and write to database ($args)" '
- test $example_sha1 = $(git hash-object $args < example)
+ test_expect_success "hash from stdin and write to database ($args)" '
+ test "$(test_oid example)" = $(git hash-object $args < example)
'
- test_blob_exists $example_sha1
+ test_blob_exists "$(test_oid example)"
pop_repo
done
@@ -173,22 +179,22 @@ done
filenames="hello
example"
-sha1s="$hello_sha1
-$example_sha1"
+oids="$(test_oid hello)
+$(test_oid example)"
-test_expect_success SHA1 "hash two files with names on stdin" '
- test "$sha1s" = "$(echo_without_newline "$filenames" | git hash-object --stdin-paths)"
+test_expect_success "hash two files with names on stdin" '
+ test "$oids" = "$(echo_without_newline "$filenames" | git hash-object --stdin-paths)"
'
for args in "-w --stdin-paths" "--stdin-paths -w"; do
push_repo
- test_expect_success SHA1 "hash two files with names on stdin and write to database ($args)" '
- test "$sha1s" = "$(echo_without_newline "$filenames" | git hash-object $args)"
+ test_expect_success "hash two files with names on stdin and write to database ($args)" '
+ test "$oids" = "$(echo_without_newline "$filenames" | git hash-object $args)"
'
- test_blob_exists $hello_sha1
- test_blob_exists $example_sha1
+ test_blob_exists "$(test_oid hello)"
+ test_blob_exists "$(test_oid example)"
pop_repo
done
@@ -199,10 +205,6 @@ test_expect_success 'too-short tree' '
test_i18ngrep "too-short tree object" err
'
-hex2oct() {
- perl -ne 'printf "\\%03o", hex for /../g'
-}
-
test_expect_success 'malformed mode in tree' '
hex_sha1=$(echo foo | git hash-object --stdin -w) &&
bin_sha1=$(echo $hex_sha1 | hex2oct) &&
diff --git a/t/t1011-read-tree-sparse-checkout.sh b/t/t1011-read-tree-sparse-checkout.sh
index ba71b15..63223e1 100755
--- a/t/t1011-read-tree-sparse-checkout.sh
+++ b/t/t1011-read-tree-sparse-checkout.sh
@@ -215,7 +215,6 @@ test_expect_success 'read-tree adds to worktree, dirty case' '
'
test_expect_success 'index removal and worktree narrowing at the same time' '
- >empty &&
echo init.t >.git/info/sparse-checkout &&
echo sub/added >>.git/info/sparse-checkout &&
git checkout -f top &&
@@ -223,7 +222,7 @@ test_expect_success 'index removal and worktree narrowing at the same time' '
git checkout removed &&
git ls-files sub/added >result &&
test ! -f sub/added &&
- test_cmp empty result
+ test_must_be_empty result
'
test_expect_success 'read-tree --reset removes outside worktree' '
@@ -234,18 +233,19 @@ test_expect_success 'read-tree --reset removes outside worktree' '
test_must_be_empty result
'
-test_expect_success 'print errors when failed to update worktree' '
+test_expect_success 'print warnings when some worktree updates disabled' '
echo sub >.git/info/sparse-checkout &&
git checkout -f init &&
mkdir sub &&
touch sub/added sub/addedtoo &&
- test_must_fail git checkout top 2>actual &&
+ # Use -q to suppress "Previous HEAD position" and "Head is now at" msgs
+ git checkout -q top 2>actual &&
cat >expected <<\EOF &&
-error: The following untracked working tree files would be overwritten by checkout:
+warning: The following paths were already present and thus not updated despite sparse patterns:
sub/added
sub/addedtoo
-Please move or remove them before you switch branches.
-Aborting
+
+After fixing the above paths, you may want to run `git sparse-checkout reapply`.
EOF
test_i18ncmp expected actual
'
diff --git a/t/t1050-large.sh b/t/t1050-large.sh
index 1a9b21b..184b479 100755
--- a/t/t1050-large.sh
+++ b/t/t1050-large.sh
@@ -8,7 +8,7 @@ test_description='adding and checking out large blobs'
# This should be moved to test-lib.sh together with the
# copy in t0021 after both topics have graduated to 'master'.
file_size () {
- perl -e 'print -s $ARGV[0]' "$1"
+ test-tool path-utils file-size "$1"
}
test_expect_success setup '
@@ -53,7 +53,8 @@ test_expect_success 'add a large file or two' '
for p in .git/objects/pack/pack-*.pack
do
count=$(( $count + 1 ))
- if test -f "$p" && idx=${p%.pack}.idx && test -f "$idx"
+ if test_path_is_file "$p" &&
+ idx=${p%.pack}.idx && test_path_is_file "$idx"
then
continue
fi
@@ -65,7 +66,7 @@ test_expect_success 'add a large file or two' '
test $cnt = 2 &&
for l in .git/objects/??/??????????????????????????????????????
do
- test -f "$l" || continue
+ test_path_is_file "$l" || continue
bad=t
done &&
test -z "$bad" &&
@@ -76,7 +77,8 @@ test_expect_success 'add a large file or two' '
for p in .git/objects/pack/pack-*.pack
do
count=$(( $count + 1 ))
- if test -f "$p" && idx=${p%.pack}.idx && test -f "$idx"
+ if test_path_is_file "$p" &&
+ idx=${p%.pack}.idx && test_path_is_file "$idx"
then
continue
fi
@@ -111,7 +113,7 @@ test_expect_success 'packsize limit' '
count=0 &&
for pi in .git/objects/pack/pack-*.idx
do
- test -f "$pi" && count=$(( $count + 1 ))
+ test_path_is_file "$pi" && count=$(( $count + 1 ))
done &&
test $count = 2 &&
@@ -194,15 +196,15 @@ test_expect_success 'pack-objects with large loose object' '
test_cmp huge actual
'
-test_expect_success 'tar achiving' '
+test_expect_success 'tar archiving' '
git archive --format=tar HEAD >/dev/null
'
-test_expect_success 'zip achiving, store only' '
+test_expect_success 'zip archiving, store only' '
git archive --format=zip -0 HEAD >/dev/null
'
-test_expect_success 'zip achiving, deflate' '
+test_expect_success 'zip archiving, deflate' '
git archive --format=zip HEAD >/dev/null
'
diff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh
index 4feb651..bc89371 100755
--- a/t/t1060-object-corruption.sh
+++ b/t/t1060-object-corruption.sh
@@ -127,4 +127,14 @@ test_expect_success 'fetch into corrupted repo with index-pack' '
)
'
+test_expect_success 'internal tree objects are not "missing"' '
+ git init missing-empty &&
+ (
+ cd missing-empty &&
+ empty_tree=$(git hash-object -t tree /dev/null) &&
+ commit=$(echo foo | git commit-tree $empty_tree) &&
+ git rev-list --objects $commit
+ )
+'
+
test_done
diff --git a/t/t1090-sparse-checkout-scope.sh b/t/t1090-sparse-checkout-scope.sh
index 090b7fc..40cc004 100755
--- a/t/t1090-sparse-checkout-scope.sh
+++ b/t/t1090-sparse-checkout-scope.sh
@@ -31,20 +31,6 @@ test_expect_success 'perform sparse checkout of master' '
test_path_is_file c
'
-test_expect_success 'checkout -b checkout.optimizeNewBranch interaction' '
- cp .git/info/sparse-checkout .git/info/sparse-checkout.bak &&
- test_when_finished "
- mv -f .git/info/sparse-checkout.bak .git/info/sparse-checkout
- git checkout master
- " &&
- echo "/b" >>.git/info/sparse-checkout &&
- test "$(git ls-files -t b)" = "S b" &&
- git -c checkout.optimizeNewBranch=true checkout -b fast &&
- test "$(git ls-files -t b)" = "S b" &&
- git checkout -b slow &&
- test "$(git ls-files -t b)" = "H b"
-'
-
test_expect_success 'merge feature branch into sparse checkout of master' '
git merge feature &&
test_path_is_file a &&
diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh
new file mode 100755
index 0000000..dee99ee
--- /dev/null
+++ b/t/t1091-sparse-checkout-builtin.sh
@@ -0,0 +1,609 @@
+#!/bin/sh
+
+test_description='sparse checkout builtin tests'
+
+. ./test-lib.sh
+
+list_files() {
+ # Do not replace this with 'ls "$1"', as "ls" with BSD-lineage
+ # enables "-A" by default for root and ends up including ".git" and
+ # such in its output. (Note, though, that running the test suite as
+ # root is generally not recommended.)
+ (cd "$1" && printf '%s\n' *)
+}
+
+check_files() {
+ list_files "$1" >actual &&
+ shift &&
+ printf "%s\n" $@ >expect &&
+ test_cmp expect actual
+}
+
+test_expect_success 'setup' '
+ git init repo &&
+ (
+ cd repo &&
+ echo "initial" >a &&
+ mkdir folder1 folder2 deep &&
+ mkdir deep/deeper1 deep/deeper2 &&
+ mkdir deep/deeper1/deepest &&
+ cp a folder1 &&
+ cp a folder2 &&
+ cp a deep &&
+ cp a deep/deeper1 &&
+ cp a deep/deeper2 &&
+ cp a deep/deeper1/deepest &&
+ git add . &&
+ git commit -m "initial commit"
+ )
+'
+
+test_expect_success 'git sparse-checkout list (empty)' '
+ git -C repo sparse-checkout list >list 2>err &&
+ test_must_be_empty list &&
+ test_i18ngrep "this worktree is not sparse (sparse-checkout file may not exist)" err
+'
+
+test_expect_success 'git sparse-checkout list (populated)' '
+ test_when_finished rm -f repo/.git/info/sparse-checkout &&
+ cat >repo/.git/info/sparse-checkout <<-\EOF &&
+ /folder1/*
+ /deep/
+ **/a
+ !*bin*
+ EOF
+ cp repo/.git/info/sparse-checkout expect &&
+ git -C repo sparse-checkout list >list &&
+ test_cmp expect list
+'
+
+test_expect_success 'git sparse-checkout init' '
+ git -C repo sparse-checkout init &&
+ cat >expect <<-\EOF &&
+ /*
+ !/*/
+ EOF
+ test_cmp expect repo/.git/info/sparse-checkout &&
+ test_cmp_config -C repo true core.sparsecheckout &&
+ check_files repo a
+'
+
+test_expect_success 'git sparse-checkout list after init' '
+ git -C repo sparse-checkout list >actual &&
+ cat >expect <<-\EOF &&
+ /*
+ !/*/
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'init with existing sparse-checkout' '
+ echo "*folder*" >> repo/.git/info/sparse-checkout &&
+ git -C repo sparse-checkout init &&
+ cat >expect <<-\EOF &&
+ /*
+ !/*/
+ *folder*
+ EOF
+ test_cmp expect repo/.git/info/sparse-checkout &&
+ check_files repo a folder1 folder2
+'
+
+test_expect_success 'clone --sparse' '
+ git clone --sparse "file://$(pwd)/repo" clone &&
+ git -C clone sparse-checkout list >actual &&
+ cat >expect <<-\EOF &&
+ /*
+ !/*/
+ EOF
+ test_cmp expect actual &&
+ check_files clone a
+'
+
+test_expect_success 'set enables config' '
+ git init empty-config &&
+ (
+ cd empty-config &&
+ test_commit test file &&
+ test_path_is_missing .git/config.worktree &&
+ test_must_fail git sparse-checkout set nothing &&
+ test_path_is_file .git/config.worktree &&
+ test_must_fail git config core.sparseCheckout &&
+ git sparse-checkout set "/*" &&
+ test_cmp_config true core.sparseCheckout
+ )
+'
+
+test_expect_success 'set sparse-checkout using builtin' '
+ git -C repo sparse-checkout set "/*" "!/*/" "*folder*" &&
+ cat >expect <<-\EOF &&
+ /*
+ !/*/
+ *folder*
+ EOF
+ git -C repo sparse-checkout list >actual &&
+ test_cmp expect actual &&
+ test_cmp expect repo/.git/info/sparse-checkout &&
+ check_files repo a folder1 folder2
+'
+
+test_expect_success 'set sparse-checkout using --stdin' '
+ cat >expect <<-\EOF &&
+ /*
+ !/*/
+ /folder1/
+ /folder2/
+ EOF
+ git -C repo sparse-checkout set --stdin <expect &&
+ git -C repo sparse-checkout list >actual &&
+ test_cmp expect actual &&
+ test_cmp expect repo/.git/info/sparse-checkout &&
+ check_files repo "a folder1 folder2"
+'
+
+test_expect_success 'add to sparse-checkout' '
+ cat repo/.git/info/sparse-checkout >expect &&
+ cat >add <<-\EOF &&
+ pattern1
+ /folder1/
+ pattern2
+ EOF
+ cat add >>expect &&
+ git -C repo sparse-checkout add --stdin <add &&
+ git -C repo sparse-checkout list >actual &&
+ test_cmp expect actual &&
+ test_cmp expect repo/.git/info/sparse-checkout &&
+ check_files repo "a folder1 folder2"
+'
+
+test_expect_success 'cone mode: match patterns' '
+ git -C repo config --worktree core.sparseCheckoutCone true &&
+ rm -rf repo/a repo/folder1 repo/folder2 &&
+ git -C repo read-tree -mu HEAD 2>err &&
+ test_i18ngrep ! "disabling cone patterns" err &&
+ git -C repo reset --hard &&
+ check_files repo a folder1 folder2
+'
+
+test_expect_success 'cone mode: warn on bad pattern' '
+ test_when_finished mv sparse-checkout repo/.git/info/ &&
+ cp repo/.git/info/sparse-checkout . &&
+ echo "!/deep/deeper/*" >>repo/.git/info/sparse-checkout &&
+ git -C repo read-tree -mu HEAD 2>err &&
+ test_i18ngrep "unrecognized negative pattern" err
+'
+
+test_expect_success 'sparse-checkout disable' '
+ test_when_finished rm -rf repo/.git/info/sparse-checkout &&
+ git -C repo sparse-checkout disable &&
+ test_path_is_file repo/.git/info/sparse-checkout &&
+ git -C repo config --list >config &&
+ test_must_fail git config core.sparseCheckout &&
+ check_files repo a deep folder1 folder2
+'
+
+test_expect_success 'cone mode: init and set' '
+ git -C repo sparse-checkout init --cone &&
+ git -C repo config --list >config &&
+ test_i18ngrep "core.sparsecheckoutcone=true" config &&
+ list_files repo >dir &&
+ echo a >expect &&
+ test_cmp expect dir &&
+ git -C repo sparse-checkout set deep/deeper1/deepest/ 2>err &&
+ test_must_be_empty err &&
+ check_files repo a deep &&
+ check_files repo/deep a deeper1 &&
+ check_files repo/deep/deeper1 a deepest &&
+ cat >expect <<-\EOF &&
+ /*
+ !/*/
+ /deep/
+ !/deep/*/
+ /deep/deeper1/
+ !/deep/deeper1/*/
+ /deep/deeper1/deepest/
+ EOF
+ test_cmp expect repo/.git/info/sparse-checkout &&
+ git -C repo sparse-checkout set --stdin 2>err <<-\EOF &&
+ folder1
+ folder2
+ EOF
+ test_must_be_empty err &&
+ check_files repo a folder1 folder2
+'
+
+test_expect_success 'cone mode: list' '
+ cat >expect <<-\EOF &&
+ folder1
+ folder2
+ EOF
+ git -C repo sparse-checkout set --stdin <expect &&
+ git -C repo sparse-checkout list >actual 2>err &&
+ test_must_be_empty err &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cone mode: set with nested folders' '
+ git -C repo sparse-checkout set deep deep/deeper1/deepest 2>err &&
+ test_line_count = 0 err &&
+ cat >expect <<-\EOF &&
+ /*
+ !/*/
+ /deep/
+ EOF
+ test_cmp repo/.git/info/sparse-checkout expect
+'
+
+test_expect_success 'cone mode: add independent path' '
+ git -C repo sparse-checkout set deep/deeper1 &&
+ git -C repo sparse-checkout add folder1 &&
+ cat >expect <<-\EOF &&
+ /*
+ !/*/
+ /deep/
+ !/deep/*/
+ /deep/deeper1/
+ /folder1/
+ EOF
+ test_cmp expect repo/.git/info/sparse-checkout &&
+ check_files repo a deep folder1
+'
+
+test_expect_success 'cone mode: add sibling path' '
+ git -C repo sparse-checkout set deep/deeper1 &&
+ git -C repo sparse-checkout add deep/deeper2 &&
+ cat >expect <<-\EOF &&
+ /*
+ !/*/
+ /deep/
+ !/deep/*/
+ /deep/deeper1/
+ /deep/deeper2/
+ EOF
+ test_cmp expect repo/.git/info/sparse-checkout &&
+ check_files repo a deep
+'
+
+test_expect_success 'cone mode: add parent path' '
+ git -C repo sparse-checkout set deep/deeper1 folder1 &&
+ git -C repo sparse-checkout add deep &&
+ cat >expect <<-\EOF &&
+ /*
+ !/*/
+ /deep/
+ /folder1/
+ EOF
+ test_cmp expect repo/.git/info/sparse-checkout &&
+ check_files repo a deep folder1
+'
+
+test_expect_success 'not-up-to-date does not block rest of sparsification' '
+ test_when_finished git -C repo sparse-checkout disable &&
+ test_when_finished git -C repo reset --hard &&
+ git -C repo sparse-checkout set deep &&
+
+ echo update >repo/deep/deeper2/a &&
+ cp repo/.git/info/sparse-checkout expect &&
+ test_write_lines "!/deep/*/" "/deep/deeper1/" >>expect &&
+
+ git -C repo sparse-checkout set deep/deeper1 2>err &&
+
+ test_i18ngrep "The following paths are not up to date" err &&
+ test_cmp expect repo/.git/info/sparse-checkout &&
+ check_files repo/deep a deeper1 deeper2 &&
+ check_files repo/deep/deeper1 a deepest &&
+ check_files repo/deep/deeper1/deepest a &&
+ check_files repo/deep/deeper2 a
+'
+
+test_expect_success 'revert to old sparse-checkout on empty update' '
+ git init empty-test &&
+ (
+ echo >file &&
+ git add file &&
+ git commit -m "test" &&
+ test_must_fail git sparse-checkout set nothing 2>err &&
+ test_i18ngrep "Sparse checkout leaves no entry on working directory" err &&
+ test_i18ngrep ! ".git/index.lock" err &&
+ git sparse-checkout set file
+ )
+'
+
+test_expect_success 'fail when lock is taken' '
+ test_when_finished rm -rf repo/.git/info/sparse-checkout.lock &&
+ touch repo/.git/info/sparse-checkout.lock &&
+ test_must_fail git -C repo sparse-checkout set deep 2>err &&
+ test_i18ngrep "Unable to create .*\.lock" err
+'
+
+test_expect_success '.gitignore should not warn about cone mode' '
+ git -C repo config --worktree core.sparseCheckoutCone true &&
+ echo "**/bin/*" >repo/.gitignore &&
+ git -C repo reset --hard 2>err &&
+ test_i18ngrep ! "disabling cone patterns" err
+'
+
+test_expect_success 'sparse-checkout (init|set|disable) warns with dirty status' '
+ git clone repo dirty &&
+ echo dirty >dirty/folder1/a &&
+
+ git -C dirty sparse-checkout init 2>err &&
+ test_i18ngrep "warning.*The following paths are not up to date" err &&
+
+ git -C dirty sparse-checkout set /folder2/* /deep/deeper1/* 2>err &&
+ test_i18ngrep "warning.*The following paths are not up to date" err &&
+ test_path_is_file dirty/folder1/a &&
+
+ git -C dirty sparse-checkout disable 2>err &&
+ test_must_be_empty err &&
+
+ git -C dirty reset --hard &&
+ git -C dirty sparse-checkout init &&
+ git -C dirty sparse-checkout set /folder2/* /deep/deeper1/* &&
+ test_path_is_missing dirty/folder1/a &&
+ git -C dirty sparse-checkout disable &&
+ test_path_is_file dirty/folder1/a
+'
+
+test_expect_success 'sparse-checkout (init|set|disable) warns with unmerged status' '
+ git clone repo unmerged &&
+
+ cat >input <<-EOF &&
+ 0 0000000000000000000000000000000000000000 folder1/a
+ 100644 $(git -C unmerged rev-parse HEAD:folder1/a) 1 folder1/a
+ EOF
+ git -C unmerged update-index --index-info <input &&
+
+ git -C unmerged sparse-checkout init 2>err &&
+ test_i18ngrep "warning.*The following paths are unmerged" err &&
+
+ git -C unmerged sparse-checkout set /folder2/* /deep/deeper1/* 2>err &&
+ test_i18ngrep "warning.*The following paths are unmerged" err &&
+ test_path_is_file dirty/folder1/a &&
+
+ git -C unmerged sparse-checkout disable 2>err &&
+ test_i18ngrep "warning.*The following paths are unmerged" err &&
+
+ git -C unmerged reset --hard &&
+ git -C unmerged sparse-checkout init &&
+ git -C unmerged sparse-checkout set /folder2/* /deep/deeper1/* &&
+ git -C unmerged sparse-checkout disable
+'
+
+test_expect_success 'sparse-checkout reapply' '
+ git clone repo tweak &&
+
+ echo dirty >tweak/deep/deeper2/a &&
+
+ cat >input <<-EOF &&
+ 0 0000000000000000000000000000000000000000 folder1/a
+ 100644 $(git -C tweak rev-parse HEAD:folder1/a) 1 folder1/a
+ EOF
+ git -C tweak update-index --index-info <input &&
+
+ git -C tweak sparse-checkout init --cone 2>err &&
+ test_i18ngrep "warning.*The following paths are not up to date" err &&
+ test_i18ngrep "warning.*The following paths are unmerged" err &&
+
+ git -C tweak sparse-checkout set folder2 deep/deeper1 2>err &&
+ test_i18ngrep "warning.*The following paths are not up to date" err &&
+ test_i18ngrep "warning.*The following paths are unmerged" err &&
+
+ git -C tweak sparse-checkout reapply 2>err &&
+ test_i18ngrep "warning.*The following paths are not up to date" err &&
+ test_path_is_file tweak/deep/deeper2/a &&
+ test_i18ngrep "warning.*The following paths are unmerged" err &&
+ test_path_is_file tweak/folder1/a &&
+
+ git -C tweak checkout HEAD deep/deeper2/a &&
+ git -C tweak sparse-checkout reapply 2>err &&
+ test_i18ngrep ! "warning.*The following paths are not up to date" err &&
+ test_path_is_missing tweak/deep/deeper2/a &&
+ test_i18ngrep "warning.*The following paths are unmerged" err &&
+ test_path_is_file tweak/folder1/a &&
+
+ git -C tweak add folder1/a &&
+ git -C tweak sparse-checkout reapply 2>err &&
+ test_must_be_empty err &&
+ test_path_is_missing tweak/deep/deeper2/a &&
+ test_path_is_missing tweak/folder1/a &&
+
+ git -C tweak sparse-checkout disable
+'
+
+test_expect_success 'cone mode: set with core.ignoreCase=true' '
+ rm repo/.git/info/sparse-checkout &&
+ git -C repo sparse-checkout init --cone &&
+ git -C repo -c core.ignoreCase=true sparse-checkout set folder1 &&
+ cat >expect <<-\EOF &&
+ /*
+ !/*/
+ /folder1/
+ EOF
+ test_cmp expect repo/.git/info/sparse-checkout &&
+ check_files repo a folder1
+'
+
+test_expect_success 'interaction with submodules' '
+ git clone repo super &&
+ (
+ cd super &&
+ mkdir modules &&
+ git submodule add ../repo modules/child &&
+ git add . &&
+ git commit -m "add submodule" &&
+ git sparse-checkout init --cone &&
+ git sparse-checkout set folder1
+ ) &&
+ check_files super a folder1 modules &&
+ check_files super/modules/child a deep folder1 folder2
+'
+
+test_expect_success 'different sparse-checkouts with worktrees' '
+ git -C repo worktree add --detach ../worktree &&
+ check_files worktree "a deep folder1 folder2" &&
+ git -C worktree sparse-checkout init --cone &&
+ git -C repo sparse-checkout set folder1 &&
+ git -C worktree sparse-checkout set deep/deeper1 &&
+ check_files repo a folder1 &&
+ check_files worktree a deep
+'
+
+test_expect_success 'set using filename keeps file on-disk' '
+ git -C repo sparse-checkout set a deep &&
+ cat >expect <<-\EOF &&
+ /*
+ !/*/
+ /a/
+ /deep/
+ EOF
+ test_cmp expect repo/.git/info/sparse-checkout &&
+ check_files repo a deep
+'
+
+check_read_tree_errors () {
+ REPO=$1
+ FILES=$2
+ ERRORS=$3
+ git -C $REPO -c core.sparseCheckoutCone=false read-tree -mu HEAD 2>err &&
+ test_must_be_empty err &&
+ check_files $REPO "$FILES" &&
+ git -C $REPO read-tree -mu HEAD 2>err &&
+ if test -z "$ERRORS"
+ then
+ test_must_be_empty err
+ else
+ test_i18ngrep "$ERRORS" err
+ fi &&
+ check_files $REPO $FILES
+}
+
+test_expect_success 'pattern-checks: /A/**' '
+ cat >repo/.git/info/sparse-checkout <<-\EOF &&
+ /*
+ !/*/
+ /folder1/**
+ EOF
+ check_read_tree_errors repo "a folder1" "disabling cone pattern matching"
+'
+
+test_expect_success 'pattern-checks: /A/**/B/' '
+ cat >repo/.git/info/sparse-checkout <<-\EOF &&
+ /*
+ !/*/
+ /deep/**/deepest
+ EOF
+ check_read_tree_errors repo "a deep" "disabling cone pattern matching" &&
+ check_files repo/deep "deeper1" &&
+ check_files repo/deep/deeper1 "deepest"
+'
+
+test_expect_success 'pattern-checks: too short' '
+ cat >repo/.git/info/sparse-checkout <<-\EOF &&
+ /*
+ !/*/
+ /
+ EOF
+ check_read_tree_errors repo "a" "disabling cone pattern matching"
+'
+test_expect_success 'pattern-checks: not too short' '
+ cat >repo/.git/info/sparse-checkout <<-\EOF &&
+ /*
+ !/*/
+ /b/
+ EOF
+ git -C repo read-tree -mu HEAD 2>err &&
+ test_must_be_empty err &&
+ check_files repo a
+'
+
+test_expect_success 'pattern-checks: trailing "*"' '
+ cat >repo/.git/info/sparse-checkout <<-\EOF &&
+ /*
+ !/*/
+ /a*
+ EOF
+ check_read_tree_errors repo "a" "disabling cone pattern matching"
+'
+
+test_expect_success 'pattern-checks: starting "*"' '
+ cat >repo/.git/info/sparse-checkout <<-\EOF &&
+ /*
+ !/*/
+ *eep/
+ EOF
+ check_read_tree_errors repo "a deep" "disabling cone pattern matching"
+'
+
+test_expect_success 'pattern-checks: contained glob characters' '
+ for c in "[a]" "\\" "?" "*"
+ do
+ cat >repo/.git/info/sparse-checkout <<-EOF &&
+ /*
+ !/*/
+ something$c-else/
+ EOF
+ check_read_tree_errors repo "a" "disabling cone pattern matching"
+ done
+'
+
+test_expect_success BSLASHPSPEC 'pattern-checks: escaped characters' '
+ git clone repo escaped &&
+ TREEOID=$(git -C escaped rev-parse HEAD:folder1) &&
+ NEWTREE=$(git -C escaped mktree <<-EOF
+ $(git -C escaped ls-tree HEAD)
+ 040000 tree $TREEOID zbad\\dir
+ 040000 tree $TREEOID zdoes*exist
+ 040000 tree $TREEOID zglob[!a]?
+ EOF
+ ) &&
+ COMMIT=$(git -C escaped commit-tree $NEWTREE -p HEAD) &&
+ git -C escaped reset --hard $COMMIT &&
+ check_files escaped "a deep folder1 folder2 zbad\\dir zdoes*exist" zglob[!a]? &&
+ git -C escaped sparse-checkout init --cone &&
+ git -C escaped sparse-checkout set zbad\\dir/bogus "zdoes*not*exist" "zdoes*exist" "zglob[!a]?" &&
+ cat >expect <<-\EOF &&
+ /*
+ !/*/
+ /zbad\\dir/
+ !/zbad\\dir/*/
+ /zbad\\dir/bogus/
+ /zdoes\*exist/
+ /zdoes\*not\*exist/
+ /zglob\[!a]\?/
+ EOF
+ test_cmp expect escaped/.git/info/sparse-checkout &&
+ check_read_tree_errors escaped "a zbad\\dir zdoes*exist zglob[!a]?" &&
+ git -C escaped ls-tree -d --name-only HEAD >list-expect &&
+ git -C escaped sparse-checkout set --stdin <list-expect &&
+ cat >expect <<-\EOF &&
+ /*
+ !/*/
+ /deep/
+ /folder1/
+ /folder2/
+ /zbad\\dir/
+ /zdoes\*exist/
+ /zglob\[!a]\?/
+ EOF
+ test_cmp expect escaped/.git/info/sparse-checkout &&
+ check_files escaped "a deep folder1 folder2 zbad\\dir zdoes*exist" zglob[!a]? &&
+ git -C escaped sparse-checkout list >list-actual &&
+ test_cmp list-expect list-actual
+'
+
+test_expect_success MINGW 'cone mode replaces backslashes with slashes' '
+ git -C repo sparse-checkout set deep\\deeper1 &&
+ cat >expect <<-\EOF &&
+ /*
+ !/*/
+ /deep/
+ !/deep/*/
+ /deep/deeper1/
+ EOF
+ test_cmp expect repo/.git/info/sparse-checkout &&
+ check_files repo a deep &&
+ check_files repo/deep a deeper1
+'
+
+test_done
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index 9652b24..97ebfe1 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -892,6 +892,7 @@ test_expect_success 'get --expiry-date' '
1510348087
0
EOF
+ : "work around heredoc parsing bug fixed in dash 0.5.7 (in ec2c84d)" &&
{
echo "$rel_out $(git config --expiry-date date.valid1)"
git config --expiry-date date.valid2 &&
@@ -1190,47 +1191,47 @@ test_expect_success 'old-fashioned settings are case insensitive' '
test_when_finished "rm -f testConfig testConfig_expect testConfig_actual" &&
cat >testConfig_actual <<-EOF &&
- [V.A]
- r = value1
+ [V.A]
+ r = value1
EOF
q_to_tab >testConfig_expect <<-EOF &&
- [V.A]
- Qr = value2
+ [V.A]
+ Qr = value2
EOF
git config -f testConfig_actual "v.a.r" value2 &&
test_cmp testConfig_expect testConfig_actual &&
cat >testConfig_actual <<-EOF &&
- [V.A]
- r = value1
+ [V.A]
+ r = value1
EOF
q_to_tab >testConfig_expect <<-EOF &&
- [V.A]
- QR = value2
+ [V.A]
+ QR = value2
EOF
git config -f testConfig_actual "V.a.R" value2 &&
test_cmp testConfig_expect testConfig_actual &&
cat >testConfig_actual <<-EOF &&
- [V.A]
- r = value1
+ [V.A]
+ r = value1
EOF
q_to_tab >testConfig_expect <<-EOF &&
- [V.A]
- r = value1
- Qr = value2
+ [V.A]
+ r = value1
+ Qr = value2
EOF
git config -f testConfig_actual "V.A.r" value2 &&
test_cmp testConfig_expect testConfig_actual &&
cat >testConfig_actual <<-EOF &&
- [V.A]
- r = value1
+ [V.A]
+ r = value1
EOF
q_to_tab >testConfig_expect <<-EOF &&
- [V.A]
- r = value1
- Qr = value2
+ [V.A]
+ r = value1
+ Qr = value2
EOF
git config -f testConfig_actual "v.A.r" value2 &&
test_cmp testConfig_expect testConfig_actual
@@ -1240,26 +1241,26 @@ test_expect_success 'setting different case sensitive subsections ' '
test_when_finished "rm -f testConfig testConfig_expect testConfig_actual" &&
cat >testConfig_actual <<-EOF &&
- [V "A"]
- R = v1
- [K "E"]
- Y = v1
- [a "b"]
- c = v1
- [d "e"]
- f = v1
+ [V "A"]
+ R = v1
+ [K "E"]
+ Y = v1
+ [a "b"]
+ c = v1
+ [d "e"]
+ f = v1
EOF
q_to_tab >testConfig_expect <<-EOF &&
- [V "A"]
- Qr = v2
- [K "E"]
- Qy = v2
- [a "b"]
- Qc = v2
- [d "e"]
- f = v1
- [d "E"]
- Qf = v2
+ [V "A"]
+ Qr = v2
+ [K "E"]
+ Qy = v2
+ [a "b"]
+ Qc = v2
+ [d "e"]
+ f = v1
+ [d "E"]
+ Qf = v2
EOF
# exact match
git config -f testConfig_actual a.b.c v2 &&
@@ -1293,26 +1294,25 @@ test_expect_success 'git -c is not confused by empty environment' '
GIT_CONFIG_PARAMETERS="" git -c x.one=1 config --list
'
-sq="'"
test_expect_success 'detect bogus GIT_CONFIG_PARAMETERS' '
cat >expect <<-\EOF &&
env.one one
env.two two
EOF
- GIT_CONFIG_PARAMETERS="${sq}env.one=one${sq} ${sq}env.two=two${sq}" \
+ GIT_CONFIG_PARAMETERS="${SQ}env.one=one${SQ} ${SQ}env.two=two${SQ}" \
git config --get-regexp "env.*" >actual &&
test_cmp expect actual &&
cat >expect <<-EOF &&
- env.one one${sq}
+ env.one one${SQ}
env.two two
EOF
- GIT_CONFIG_PARAMETERS="${sq}env.one=one${sq}\\$sq$sq$sq ${sq}env.two=two${sq}" \
+ GIT_CONFIG_PARAMETERS="${SQ}env.one=one${SQ}\\$SQ$SQ$SQ ${SQ}env.two=two${SQ}" \
git config --get-regexp "env.*" >actual &&
test_cmp expect actual &&
test_must_fail env \
- GIT_CONFIG_PARAMETERS="${sq}env.one=one${sq}\\$sq ${sq}env.two=two${sq}" \
+ GIT_CONFIG_PARAMETERS="${SQ}env.one=one${SQ}\\$SQ ${SQ}env.two=two${SQ}" \
git config --get-regexp "env.*"
'
@@ -1408,6 +1408,8 @@ test_expect_success 'urlmatch favors more specific URLs' '
cookieFile = /tmp/wildcard.txt
[http "https://*.example.com/wildcardwithsubdomain"]
cookieFile = /tmp/wildcardwithsubdomain.txt
+ [http "https://*.example.*"]
+ cookieFile = /tmp/multiwildcard.txt
[http "https://trailing.example.com"]
cookieFile = /tmp/trailing.txt
[http "https://user@*.example.com/"]
@@ -1454,6 +1456,10 @@ test_expect_success 'urlmatch favors more specific URLs' '
echo http.cookiefile /tmp/sub.txt >expect &&
git config --get-urlmatch HTTP https://user@sub.example.com >actual &&
+ test_cmp expect actual &&
+
+ echo http.cookiefile /tmp/multiwildcard.txt >expect &&
+ git config --get-urlmatch HTTP https://wildcard.example.org >actual &&
test_cmp expect actual
'
@@ -1622,40 +1628,40 @@ test_expect_success 'set up --show-origin tests' '
INCLUDE_DIR="$HOME/include" &&
mkdir -p "$INCLUDE_DIR" &&
cat >"$INCLUDE_DIR"/absolute.include <<-\EOF &&
- [user]
- absolute = include
+ [user]
+ absolute = include
EOF
cat >"$INCLUDE_DIR"/relative.include <<-\EOF &&
- [user]
- relative = include
+ [user]
+ relative = include
EOF
cat >"$HOME"/.gitconfig <<-EOF &&
- [user]
- global = true
- override = global
- [include]
- path = "$INCLUDE_DIR/absolute.include"
+ [user]
+ global = true
+ override = global
+ [include]
+ path = "$INCLUDE_DIR/absolute.include"
EOF
cat >.git/config <<-\EOF
- [user]
- local = true
- override = local
- [include]
- path = ../include/relative.include
+ [user]
+ local = true
+ override = local
+ [include]
+ path = ../include/relative.include
EOF
'
test_expect_success '--show-origin with --list' '
cat >expect <<-EOF &&
- file:$HOME/.gitconfig user.global=true
- file:$HOME/.gitconfig user.override=global
- file:$HOME/.gitconfig include.path=$INCLUDE_DIR/absolute.include
- file:$INCLUDE_DIR/absolute.include user.absolute=include
- file:.git/config user.local=true
- file:.git/config user.override=local
- file:.git/config include.path=../include/relative.include
- file:.git/../include/relative.include user.relative=include
- command line: user.cmdline=true
+ file:$HOME/.gitconfig user.global=true
+ file:$HOME/.gitconfig user.override=global
+ file:$HOME/.gitconfig include.path=$INCLUDE_DIR/absolute.include
+ file:$INCLUDE_DIR/absolute.include user.absolute=include
+ file:.git/config user.local=true
+ file:.git/config user.override=local
+ file:.git/config include.path=../include/relative.include
+ file:.git/../include/relative.include user.relative=include
+ command line: user.cmdline=true
EOF
git -c user.cmdline=true config --list --show-origin >output &&
test_cmp expect output
@@ -1663,16 +1669,16 @@ test_expect_success '--show-origin with --list' '
test_expect_success '--show-origin with --list --null' '
cat >expect <<-EOF &&
- file:$HOME/.gitconfigQuser.global
- trueQfile:$HOME/.gitconfigQuser.override
- globalQfile:$HOME/.gitconfigQinclude.path
- $INCLUDE_DIR/absolute.includeQfile:$INCLUDE_DIR/absolute.includeQuser.absolute
- includeQfile:.git/configQuser.local
- trueQfile:.git/configQuser.override
- localQfile:.git/configQinclude.path
- ../include/relative.includeQfile:.git/../include/relative.includeQuser.relative
- includeQcommand line:Quser.cmdline
- trueQ
+ file:$HOME/.gitconfigQuser.global
+ trueQfile:$HOME/.gitconfigQuser.override
+ globalQfile:$HOME/.gitconfigQinclude.path
+ $INCLUDE_DIR/absolute.includeQfile:$INCLUDE_DIR/absolute.includeQuser.absolute
+ includeQfile:.git/configQuser.local
+ trueQfile:.git/configQuser.override
+ localQfile:.git/configQinclude.path
+ ../include/relative.includeQfile:.git/../include/relative.includeQuser.relative
+ includeQcommand line:Quser.cmdline
+ trueQ
EOF
git -c user.cmdline=true config --null --list --show-origin >output.raw &&
nul_to_q <output.raw >output &&
@@ -1684,9 +1690,9 @@ test_expect_success '--show-origin with --list --null' '
test_expect_success '--show-origin with single file' '
cat >expect <<-\EOF &&
- file:.git/config user.local=true
- file:.git/config user.override=local
- file:.git/config include.path=../include/relative.include
+ file:.git/config user.local=true
+ file:.git/config user.override=local
+ file:.git/config include.path=../include/relative.include
EOF
git config --local --list --show-origin >output &&
test_cmp expect output
@@ -1694,8 +1700,8 @@ test_expect_success '--show-origin with single file' '
test_expect_success '--show-origin with --get-regexp' '
cat >expect <<-EOF &&
- file:$HOME/.gitconfig user.global true
- file:.git/config user.local true
+ file:$HOME/.gitconfig user.global true
+ file:.git/config user.local true
EOF
git config --show-origin --get-regexp "user\.[g|l].*" >output &&
test_cmp expect output
@@ -1703,31 +1709,36 @@ test_expect_success '--show-origin with --get-regexp' '
test_expect_success '--show-origin getting a single key' '
cat >expect <<-\EOF &&
- file:.git/config local
+ file:.git/config local
EOF
git config --show-origin user.override >output &&
test_cmp expect output
'
test_expect_success 'set up custom config file' '
- CUSTOM_CONFIG_FILE="file\" (dq) and spaces.conf" &&
+ CUSTOM_CONFIG_FILE="custom.conf" &&
cat >"$CUSTOM_CONFIG_FILE" <<-\EOF
- [user]
- custom = true
+ [user]
+ custom = true
EOF
'
+test_expect_success !MINGW 'set up custom config file with special name characters' '
+ WEIRDLY_NAMED_FILE="file\" (dq) and spaces.conf" &&
+ cp "$CUSTOM_CONFIG_FILE" "$WEIRDLY_NAMED_FILE"
+'
+
test_expect_success !MINGW '--show-origin escape special file name characters' '
cat >expect <<-\EOF &&
- file:"file\" (dq) and spaces.conf" user.custom=true
+ file:"file\" (dq) and spaces.conf" user.custom=true
EOF
- git config --file "$CUSTOM_CONFIG_FILE" --show-origin --list >output &&
+ git config --file "$WEIRDLY_NAMED_FILE" --show-origin --list >output &&
test_cmp expect output
'
test_expect_success '--show-origin stdin' '
cat >expect <<-\EOF &&
- standard input: user.custom=true
+ standard input: user.custom=true
EOF
git config --file - --show-origin --list <"$CUSTOM_CONFIG_FILE" >output &&
test_cmp expect output
@@ -1735,11 +1746,11 @@ test_expect_success '--show-origin stdin' '
test_expect_success '--show-origin stdin with file include' '
cat >"$INCLUDE_DIR"/stdin.include <<-EOF &&
- [user]
- stdin = include
+ [user]
+ stdin = include
EOF
cat >expect <<-EOF &&
- file:$INCLUDE_DIR/stdin.include include
+ file:$INCLUDE_DIR/stdin.include include
EOF
echo "[include]path=\"$INCLUDE_DIR\"/stdin.include" |
git config --show-origin --includes --file - user.stdin >output &&
@@ -1747,18 +1758,18 @@ test_expect_success '--show-origin stdin with file include' '
test_cmp expect output
'
-test_expect_success !MINGW '--show-origin blob' '
+test_expect_success '--show-origin blob' '
blob=$(git hash-object -w "$CUSTOM_CONFIG_FILE") &&
cat >expect <<-EOF &&
- blob:$blob user.custom=true
+ blob:$blob user.custom=true
EOF
git config --blob=$blob --show-origin --list >output &&
test_cmp expect output
'
-test_expect_success !MINGW '--show-origin blob ref' '
+test_expect_success '--show-origin blob ref' '
cat >expect <<-\EOF &&
- blob:"master:file\" (dq) and spaces.conf" user.custom=true
+ blob:master:custom.conf user.custom=true
EOF
git add "$CUSTOM_CONFIG_FILE" &&
git commit -m "new config file" &&
@@ -1766,6 +1777,65 @@ test_expect_success !MINGW '--show-origin blob ref' '
test_cmp expect output
'
+test_expect_success '--show-scope with --list' '
+ cat >expect <<-EOF &&
+ global user.global=true
+ global user.override=global
+ global include.path=$INCLUDE_DIR/absolute.include
+ global user.absolute=include
+ local user.local=true
+ local user.override=local
+ local include.path=../include/relative.include
+ local user.relative=include
+ command user.cmdline=true
+ EOF
+ git -c user.cmdline=true config --list --show-scope >output &&
+ test_cmp expect output
+'
+
+test_expect_success !MINGW '--show-scope with --blob' '
+ blob=$(git hash-object -w "$CUSTOM_CONFIG_FILE") &&
+ cat >expect <<-EOF &&
+ command user.custom=true
+ EOF
+ git config --blob=$blob --show-scope --list >output &&
+ test_cmp expect output
+'
+
+test_expect_success '--show-scope with --local' '
+ cat >expect <<-\EOF &&
+ local user.local=true
+ local user.override=local
+ local include.path=../include/relative.include
+ EOF
+ git config --local --list --show-scope >output &&
+ test_cmp expect output
+'
+
+test_expect_success '--show-scope getting a single value' '
+ cat >expect <<-\EOF &&
+ local true
+ EOF
+ git config --show-scope --get user.local >output &&
+ test_cmp expect output
+'
+
+test_expect_success '--show-scope with --show-origin' '
+ cat >expect <<-EOF &&
+ global file:$HOME/.gitconfig user.global=true
+ global file:$HOME/.gitconfig user.override=global
+ global file:$HOME/.gitconfig include.path=$INCLUDE_DIR/absolute.include
+ global file:$INCLUDE_DIR/absolute.include user.absolute=include
+ local file:.git/config user.local=true
+ local file:.git/config user.override=local
+ local file:.git/config include.path=../include/relative.include
+ local file:.git/../include/relative.include user.relative=include
+ command command line: user.cmdline=true
+ EOF
+ git -c user.cmdline=true config --list --show-origin --show-scope >output &&
+ test_cmp expect output
+'
+
test_expect_success '--local requires a repo' '
# we expect 128 to ensure that we do not simply
# fail to find anything and return code "1"
diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh
index dfece75..2dc853d 100755
--- a/t/t1301-shared-repo.sh
+++ b/t/t1301-shared-repo.sh
@@ -136,7 +136,7 @@ test_expect_success POSIXPERM 'forced modes' '
(
cd new &&
umask 002 &&
- git init --shared=0660 --template=../templates &&
+ git init --shared=0660 --template=templates &&
>frotz &&
git add frotz &&
git commit -a -m initial &&
@@ -192,7 +192,7 @@ test_expect_success POSIXPERM 're-init respects core.sharedrepository (remote)'
umask 0022 &&
git init --bare --shared=0666 child.git &&
test_path_is_missing child.git/foo &&
- git init --bare --template=../templates child.git &&
+ git init --bare --template=templates child.git &&
echo "-rw-rw-rw-" >expect &&
test_modebits child.git/foo >actual &&
test_cmp expect actual
@@ -203,7 +203,7 @@ test_expect_success POSIXPERM 'template can set core.sharedrepository' '
umask 0022 &&
git config core.sharedrepository 0666 &&
cp .git/config templates/config &&
- git init --bare --template=../templates child.git &&
+ git init --bare --template=templates child.git &&
echo "-rw-rw-rw-" >expect &&
test_modebits child.git/HEAD >actual &&
test_cmp expect actual
diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh
index 6359185..f1e1b28 100755
--- a/t/t1305-config-include.sh
+++ b/t/t1305-config-include.sh
@@ -63,7 +63,7 @@ test_expect_success 'listing includes option and expansion' '
test.one=1
EOF
git config --list >actual.full &&
- grep -v ^core actual.full >actual &&
+ grep -v -e ^core -e ^extensions actual.full >actual &&
test_cmp expect actual
'
@@ -229,6 +229,19 @@ test_expect_success 'conditional include, early config reading' '
)
'
+test_expect_success 'conditional include with /**/' '
+ REPO=foo/bar/repo &&
+ git init $REPO &&
+ cat >>$REPO/.git/config <<-\EOF &&
+ [includeIf "gitdir:**/foo/**/bar/**"]
+ path=bar7
+ EOF
+ echo "[test]seven=7" >$REPO/.git/bar7 &&
+ echo 7 >expect &&
+ git -C $REPO config test.seven >actual &&
+ test_cmp expect actual
+'
+
test_expect_success SYMLINKS 'conditional include, set up symlinked $HOME' '
mkdir real-home &&
ln -s real-home home &&
@@ -296,21 +309,53 @@ test_expect_success SYMLINKS 'conditional include, gitdir matching symlink, icas
)
'
+test_expect_success 'conditional include, onbranch' '
+ echo "[includeIf \"onbranch:foo-branch\"]path=bar9" >>.git/config &&
+ echo "[test]nine=9" >.git/bar9 &&
+ git checkout -b master &&
+ test_must_fail git config test.nine &&
+ git checkout -b foo-branch &&
+ echo 9 >expect &&
+ git config test.nine >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'conditional include, onbranch, wildcard' '
+ echo "[includeIf \"onbranch:?oo-*/**\"]path=bar10" >>.git/config &&
+ echo "[test]ten=10" >.git/bar10 &&
+ git checkout -b not-foo-branch/a &&
+ test_must_fail git config test.ten &&
+
+ echo 10 >expect &&
+ git checkout -b foo-branch/a/b/c &&
+ git config test.ten >actual &&
+ test_cmp expect actual &&
+
+ git checkout -b moo-bar/a &&
+ git config test.ten >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'conditional include, onbranch, implicit /** for /' '
+ echo "[includeIf \"onbranch:foo-dir/\"]path=bar11" >>.git/config &&
+ echo "[test]eleven=11" >.git/bar11 &&
+ git checkout -b not-foo-dir/a &&
+ test_must_fail git config test.eleven &&
+
+ echo 11 >expect &&
+ git checkout -b foo-dir/a/b/c &&
+ git config test.eleven >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'include cycles are detected' '
- cat >.gitconfig <<-\EOF &&
- [test]value = gitconfig
- [include]path = cycle
- EOF
- cat >cycle <<-\EOF &&
- [test]value = cycle
- [include]path = .gitconfig
- EOF
- cat >expect <<-\EOF &&
- gitconfig
- cycle
- EOF
- test_must_fail git config --get-all test.value 2>stderr &&
- test_i18ngrep "exceeded maximum include depth" stderr
+ git init --bare cycle &&
+ git -C cycle config include.path cycle &&
+ git config -f cycle/cycle include.path config &&
+ test_must_fail \
+ env GIT_TEST_GETTEXT_POISON=false \
+ git -C cycle config --get-all test.value 2>stderr &&
+ grep "exceeded maximum include depth" stderr
'
test_done
diff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh
index 21e139a..dd87b43 100755
--- a/t/t1306-xdg-files.sh
+++ b/t/t1306-xdg-files.sh
@@ -153,7 +153,7 @@ test_expect_success 'Checking attributes in both XDG and local attributes files'
test_expect_success 'Checking attributes in a non-XDG global attributes file' '
- test_might_fail rm .gitattributes &&
+ rm -f .gitattributes &&
echo "f attr_f=test" >"$HOME"/my_gitattributes &&
git config core.attributesfile "$HOME"/my_gitattributes &&
echo "f: attr_f: test" >expected &&
@@ -165,7 +165,7 @@ test_expect_success 'Checking attributes in a non-XDG global attributes file' '
test_expect_success 'write: xdg file exists and ~/.gitconfig doesn'\''t' '
mkdir -p "$HOME"/.config/git &&
>"$HOME"/.config/git/config &&
- test_might_fail rm "$HOME"/.gitconfig &&
+ rm -f "$HOME"/.gitconfig &&
git config --global user.name "write_config" &&
echo "[user]" >expected &&
echo " name = write_config" >>expected &&
@@ -183,8 +183,8 @@ test_expect_success 'write: xdg file exists and ~/.gitconfig exists' '
test_expect_success 'write: ~/.config/git/ exists and config file doesn'\''t' '
- test_might_fail rm "$HOME"/.gitconfig &&
- test_might_fail rm "$HOME"/.config/git/config &&
+ rm -f "$HOME"/.gitconfig &&
+ rm -f "$HOME"/.config/git/config &&
git config --global user.name "write_gitconfig" &&
echo "[user]" >expected &&
echo " name = write_gitconfig" >>expected &&
diff --git a/t/t1307-config-blob.sh b/t/t1307-config-blob.sh
index 37dc689..002e6d3 100755
--- a/t/t1307-config-blob.sh
+++ b/t/t1307-config-blob.sh
@@ -74,7 +74,7 @@ test_expect_success 'can parse blob ending with CR' '
'
test_expect_success 'config --blob outside of a repository is an error' '
- test_must_fail nongit git config --blob=foo --list
+ nongit test_must_fail git config --blob=foo --list
'
test_done
diff --git a/t/t1308-config-set.sh b/t/t1308-config-set.sh
index d0a2727..3a527e3 100755
--- a/t/t1308-config-set.sh
+++ b/t/t1308-config-set.sh
@@ -166,14 +166,14 @@ test_expect_success 'find value with highest priority from a configset' '
'
test_expect_success 'find value_list for a key from a configset' '
- cat >except <<-\EOF &&
+ cat >expect <<-\EOF &&
+ lama
+ ball
sam
bat
hask
- lama
- ball
EOF
- test-tool config configset_get_value case.baz config2 .git/config >actual &&
+ test-tool config configset_get_value_multi case.baz config2 .git/config >actual &&
test_cmp expect actual
'
@@ -238,8 +238,8 @@ test_expect_success 'error on modifying repo config without repo' '
cmdline_config="'foo.bar=from-cmdline'"
test_expect_success 'iteration shows correct origins' '
- echo "[foo]bar = from-repo" >.git/config &&
- echo "[foo]bar = from-home" >.gitconfig &&
+ printf "[ignore]\n\tthis = please\n[foo]bar = from-repo\n" >.git/config &&
+ printf "[foo]\n\tbar = from-home\n" >.gitconfig &&
if test_have_prereq MINGW
then
# Use Windows path (i.e. *not* $HOME)
@@ -253,19 +253,29 @@ test_expect_success 'iteration shows correct origins' '
value=from-home
origin=file
name=$HOME_GITCONFIG
+ lno=2
scope=global
+ key=ignore.this
+ value=please
+ origin=file
+ name=.git/config
+ lno=2
+ scope=local
+
key=foo.bar
value=from-repo
origin=file
name=.git/config
- scope=repo
+ lno=3
+ scope=local
key=foo.bar
value=from-cmdline
origin=command line
name=
- scope=cmdline
+ lno=-1
+ scope=command
EOF
GIT_CONFIG_PARAMETERS=$cmdline_config test-tool config iterate >actual &&
test_cmp expect actual
diff --git a/t/t1309-early-config.sh b/t/t1309-early-config.sh
index 413642a..ebb8e1a 100755
--- a/t/t1309-early-config.sh
+++ b/t/t1309-early-config.sh
@@ -29,7 +29,7 @@ test_expect_success 'ceiling' '
cd sub &&
test-tool config read_early_config early.config
) >output &&
- test -z "$(cat output)"
+ test_must_be_empty output
'
test_expect_success 'ceiling #2' '
@@ -89,4 +89,14 @@ test_expect_failure 'ignore .git/ with invalid config' '
test_with_config "["
'
+test_expect_success 'early config and onbranch' '
+ echo "[broken" >broken &&
+ test_with_config "[includeif \"onbranch:master\"]path=../broken"
+'
+
+test_expect_success 'onbranch config outside of git repo' '
+ test_config_global includeIf.onbranch:master.path non-existent &&
+ nongit git help
+'
+
test_done
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 1fbd940..e1197ac 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -344,14 +344,16 @@ test_expect_success "verifying $m's log (logged by config)" '
test_cmp expect .git/logs/$m
'
-git update-ref $m $D
-cat >.git/logs/$m <<EOF
-$Z $C $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150320 -0500
-$C $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150350 -0500
-$A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 -0500
-$F $Z $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150680 -0500
-$Z $E $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 -0500
-EOF
+test_expect_success 'set up for querying the reflog' '
+ git update-ref $m $D &&
+ cat >.git/logs/$m <<-EOF
+ $Z $C $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150320 -0500
+ $C $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150350 -0500
+ $A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 -0500
+ $F $Z $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150680 -0500
+ $Z $E $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 -0500
+ EOF
+'
ed="Thu, 26 May 2005 18:32:00 -0500"
gd="Thu, 26 May 2005 18:33:00 -0500"
@@ -359,55 +361,67 @@ ld="Thu, 26 May 2005 18:43:00 -0500"
test_expect_success 'Query "master@{May 25 2005}" (before history)' '
test_when_finished "rm -f o e" &&
git rev-parse --verify "master@{May 25 2005}" >o 2>e &&
- test $C = $(cat o) &&
- test "warning: Log for '\''master'\'' only goes back to $ed." = "$(cat e)"
+ echo "$C" >expect &&
+ test_cmp expect o &&
+ echo "warning: log for '\''master'\'' only goes back to $ed" >expect &&
+ test_i18ncmp expect e
'
test_expect_success 'Query master@{2005-05-25} (before history)' '
test_when_finished "rm -f o e" &&
git rev-parse --verify master@{2005-05-25} >o 2>e &&
- test $C = $(cat o) &&
- test "warning: Log for '\''master'\'' only goes back to $ed." = "$(cat e)"
+ echo "$C" >expect &&
+ test_cmp expect o &&
+ echo "warning: log for '\''master'\'' only goes back to $ed" >expect &&
+ test_i18ncmp expect e
'
test_expect_success 'Query "master@{May 26 2005 23:31:59}" (1 second before history)' '
test_when_finished "rm -f o e" &&
git rev-parse --verify "master@{May 26 2005 23:31:59}" >o 2>e &&
- test $C = $(cat o) &&
- test "warning: Log for '\''master'\'' only goes back to $ed." = "$(cat e)"
+ echo "$C" >expect &&
+ test_cmp expect o &&
+ echo "warning: log for '\''master'\'' only goes back to $ed" >expect &&
+ test_i18ncmp expect e
'
test_expect_success 'Query "master@{May 26 2005 23:32:00}" (exactly history start)' '
test_when_finished "rm -f o e" &&
git rev-parse --verify "master@{May 26 2005 23:32:00}" >o 2>e &&
- test $C = $(cat o) &&
- test "" = "$(cat e)"
+ echo "$C" >expect &&
+ test_cmp expect o &&
+ test_must_be_empty e
'
test_expect_success 'Query "master@{May 26 2005 23:32:30}" (first non-creation change)' '
test_when_finished "rm -f o e" &&
git rev-parse --verify "master@{May 26 2005 23:32:30}" >o 2>e &&
- test $A = $(cat o) &&
- test "" = "$(cat e)"
+ echo "$A" >expect &&
+ test_cmp expect o &&
+ test_must_be_empty e
'
test_expect_success 'Query "master@{2005-05-26 23:33:01}" (middle of history with gap)' '
test_when_finished "rm -f o e" &&
git rev-parse --verify "master@{2005-05-26 23:33:01}" >o 2>e &&
- test $B = $(cat o) &&
+ echo "$B" >expect &&
+ test_cmp expect o &&
test_i18ngrep -F "warning: log for ref $m has gap after $gd" e
'
test_expect_success 'Query "master@{2005-05-26 23:38:00}" (middle of history)' '
test_when_finished "rm -f o e" &&
git rev-parse --verify "master@{2005-05-26 23:38:00}" >o 2>e &&
- test $Z = $(cat o) &&
- test "" = "$(cat e)"
+ echo "$Z" >expect &&
+ test_cmp expect o &&
+ test_must_be_empty e
'
test_expect_success 'Query "master@{2005-05-26 23:43:00}" (exact end of history)' '
test_when_finished "rm -f o e" &&
git rev-parse --verify "master@{2005-05-26 23:43:00}" >o 2>e &&
- test $E = $(cat o) &&
- test "" = "$(cat e)"
+ echo "$E" >expect &&
+ test_cmp expect o &&
+ test_must_be_empty e
'
test_expect_success 'Query "master@{2005-05-28}" (past end of history)' '
test_when_finished "rm -f o e" &&
git rev-parse --verify "master@{2005-05-28}" >o 2>e &&
- test $D = $(cat o) &&
+ echo "$D" >expect &&
+ test_cmp expect o &&
test_i18ngrep -F "warning: log for ref $m unexpectedly ended on $ld" e
'
@@ -1340,15 +1354,6 @@ test_expect_success 'fails with duplicate ref update via symref' '
test_cmp expect actual
'
-run_with_limited_open_files () {
- (ulimit -n 32 && "$@")
-}
-
-test_lazy_prereq ULIMIT_FILE_DESCRIPTORS '
- test_have_prereq !MINGW,!CYGWIN &&
- run_with_limited_open_files true
-'
-
test_expect_success ULIMIT_FILE_DESCRIPTORS 'large transaction creating branches does not burst open file limit' '
(
for i in $(test_seq 33)
@@ -1390,4 +1395,135 @@ test_expect_success 'handle per-worktree refs in refs/bisect' '
! test_cmp main-head worktree-head
'
+test_expect_success 'transaction handles empty commit' '
+ cat >stdin <<-EOF &&
+ start
+ prepare
+ commit
+ EOF
+ git update-ref --stdin <stdin >actual &&
+ printf "%s: ok\n" start prepare commit >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'transaction handles empty commit with missing prepare' '
+ cat >stdin <<-EOF &&
+ start
+ commit
+ EOF
+ git update-ref --stdin <stdin >actual &&
+ printf "%s: ok\n" start commit >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'transaction handles sole commit' '
+ cat >stdin <<-EOF &&
+ commit
+ EOF
+ git update-ref --stdin <stdin >actual &&
+ printf "%s: ok\n" commit >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'transaction handles empty abort' '
+ cat >stdin <<-EOF &&
+ start
+ prepare
+ abort
+ EOF
+ git update-ref --stdin <stdin >actual &&
+ printf "%s: ok\n" start prepare abort >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'transaction exits on multiple aborts' '
+ cat >stdin <<-EOF &&
+ abort
+ abort
+ EOF
+ test_must_fail git update-ref --stdin <stdin >actual 2>err &&
+ printf "%s: ok\n" abort >expect &&
+ test_cmp expect actual &&
+ grep "fatal: transaction is closed" err
+'
+
+test_expect_success 'transaction exits on start after prepare' '
+ cat >stdin <<-EOF &&
+ prepare
+ start
+ EOF
+ test_must_fail git update-ref --stdin <stdin 2>err >actual &&
+ printf "%s: ok\n" prepare >expect &&
+ test_cmp expect actual &&
+ grep "fatal: prepared transactions can only be closed" err
+'
+
+test_expect_success 'transaction handles empty abort with missing prepare' '
+ cat >stdin <<-EOF &&
+ start
+ abort
+ EOF
+ git update-ref --stdin <stdin >actual &&
+ printf "%s: ok\n" start abort >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'transaction handles sole abort' '
+ cat >stdin <<-EOF &&
+ abort
+ EOF
+ git update-ref --stdin <stdin >actual &&
+ printf "%s: ok\n" abort >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'transaction can handle commit' '
+ cat >stdin <<-EOF &&
+ start
+ create $a HEAD
+ commit
+ EOF
+ git update-ref --stdin <stdin >actual &&
+ printf "%s: ok\n" start commit >expect &&
+ test_cmp expect actual &&
+ git rev-parse HEAD >expect &&
+ git rev-parse $a >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'transaction can handle abort' '
+ cat >stdin <<-EOF &&
+ start
+ create $b HEAD
+ abort
+ EOF
+ git update-ref --stdin <stdin >actual &&
+ printf "%s: ok\n" start abort >expect &&
+ test_cmp expect actual &&
+ test_path_is_missing .git/$b
+'
+
+test_expect_success 'transaction aborts by default' '
+ cat >stdin <<-EOF &&
+ start
+ create $b HEAD
+ EOF
+ git update-ref --stdin <stdin >actual &&
+ printf "%s: ok\n" start >expect &&
+ test_cmp expect actual &&
+ test_path_is_missing .git/$b
+'
+
+test_expect_success 'transaction with prepare aborts by default' '
+ cat >stdin <<-EOF &&
+ start
+ create $b HEAD
+ prepare
+ EOF
+ git update-ref --stdin <stdin >actual &&
+ printf "%s: ok\n" start prepare >expect &&
+ test_cmp expect actual &&
+ test_path_is_missing .git/$b
+'
+
test_done
diff --git a/t/t1404-update-ref-errors.sh b/t/t1404-update-ref-errors.sh
index 51a4f4c..2d142e5 100755
--- a/t/t1404-update-ref-errors.sh
+++ b/t/t1404-update-ref-errors.sh
@@ -32,8 +32,6 @@ test_update_rejected () {
test_cmp unchanged actual
}
-Q="'"
-
# Test adding and deleting D/F-conflicting references in a single
# transaction.
df_test() {
@@ -93,7 +91,7 @@ df_test() {
delname="$delref"
fi &&
cat >expected-err <<-EOF &&
- fatal: cannot lock ref $Q$addname$Q: $Q$delref$Q exists; cannot create $Q$addref$Q
+ fatal: cannot lock ref $SQ$addname$SQ: $SQ$delref$SQ exists; cannot create $SQ$addref$SQ
EOF
$pack &&
if $add_del
@@ -123,7 +121,7 @@ test_expect_success 'existing loose ref is a simple prefix of new' '
prefix=refs/1l &&
test_update_rejected "a c e" false "b c/x d" \
- "$Q$prefix/c$Q exists; cannot create $Q$prefix/c/x$Q"
+ "$SQ$prefix/c$SQ exists; cannot create $SQ$prefix/c/x$SQ"
'
@@ -131,7 +129,7 @@ test_expect_success 'existing packed ref is a simple prefix of new' '
prefix=refs/1p &&
test_update_rejected "a c e" true "b c/x d" \
- "$Q$prefix/c$Q exists; cannot create $Q$prefix/c/x$Q"
+ "$SQ$prefix/c$SQ exists; cannot create $SQ$prefix/c/x$SQ"
'
@@ -139,7 +137,7 @@ test_expect_success 'existing loose ref is a deeper prefix of new' '
prefix=refs/2l &&
test_update_rejected "a c e" false "b c/x/y d" \
- "$Q$prefix/c$Q exists; cannot create $Q$prefix/c/x/y$Q"
+ "$SQ$prefix/c$SQ exists; cannot create $SQ$prefix/c/x/y$SQ"
'
@@ -147,7 +145,7 @@ test_expect_success 'existing packed ref is a deeper prefix of new' '
prefix=refs/2p &&
test_update_rejected "a c e" true "b c/x/y d" \
- "$Q$prefix/c$Q exists; cannot create $Q$prefix/c/x/y$Q"
+ "$SQ$prefix/c$SQ exists; cannot create $SQ$prefix/c/x/y$SQ"
'
@@ -155,7 +153,7 @@ test_expect_success 'new ref is a simple prefix of existing loose' '
prefix=refs/3l &&
test_update_rejected "a c/x e" false "b c d" \
- "$Q$prefix/c/x$Q exists; cannot create $Q$prefix/c$Q"
+ "$SQ$prefix/c/x$SQ exists; cannot create $SQ$prefix/c$SQ"
'
@@ -163,7 +161,7 @@ test_expect_success 'new ref is a simple prefix of existing packed' '
prefix=refs/3p &&
test_update_rejected "a c/x e" true "b c d" \
- "$Q$prefix/c/x$Q exists; cannot create $Q$prefix/c$Q"
+ "$SQ$prefix/c/x$SQ exists; cannot create $SQ$prefix/c$SQ"
'
@@ -171,7 +169,7 @@ test_expect_success 'new ref is a deeper prefix of existing loose' '
prefix=refs/4l &&
test_update_rejected "a c/x/y e" false "b c d" \
- "$Q$prefix/c/x/y$Q exists; cannot create $Q$prefix/c$Q"
+ "$SQ$prefix/c/x/y$SQ exists; cannot create $SQ$prefix/c$SQ"
'
@@ -179,7 +177,7 @@ test_expect_success 'new ref is a deeper prefix of existing packed' '
prefix=refs/4p &&
test_update_rejected "a c/x/y e" true "b c d" \
- "$Q$prefix/c/x/y$Q exists; cannot create $Q$prefix/c$Q"
+ "$SQ$prefix/c/x/y$SQ exists; cannot create $SQ$prefix/c$SQ"
'
@@ -187,7 +185,7 @@ test_expect_success 'one new ref is a simple prefix of another' '
prefix=refs/5 &&
test_update_rejected "a e" false "b c c/x d" \
- "cannot process $Q$prefix/c$Q and $Q$prefix/c/x$Q at the same time"
+ "cannot process $SQ$prefix/c$SQ and $SQ$prefix/c/x$SQ at the same time"
'
@@ -334,7 +332,7 @@ test_expect_success 'D/F conflict prevents indirect delete long packed + indirec
test_expect_success 'missing old value blocks update' '
prefix=refs/missing-update &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $Q$prefix/foo$Q: unable to resolve reference $Q$prefix/foo$Q
+ fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ
EOF
printf "%s\n" "update $prefix/foo $E $D" |
test_must_fail git update-ref --stdin 2>output.err &&
@@ -345,7 +343,7 @@ test_expect_success 'incorrect old value blocks update' '
prefix=refs/incorrect-update &&
git update-ref $prefix/foo $C &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $Q$prefix/foo$Q: is at $C but expected $D
+ fatal: cannot lock ref $SQ$prefix/foo$SQ: is at $C but expected $D
EOF
printf "%s\n" "update $prefix/foo $E $D" |
test_must_fail git update-ref --stdin 2>output.err &&
@@ -356,7 +354,7 @@ test_expect_success 'existing old value blocks create' '
prefix=refs/existing-create &&
git update-ref $prefix/foo $C &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $Q$prefix/foo$Q: reference already exists
+ fatal: cannot lock ref $SQ$prefix/foo$SQ: reference already exists
EOF
printf "%s\n" "create $prefix/foo $E" |
test_must_fail git update-ref --stdin 2>output.err &&
@@ -367,7 +365,7 @@ test_expect_success 'incorrect old value blocks delete' '
prefix=refs/incorrect-delete &&
git update-ref $prefix/foo $C &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $Q$prefix/foo$Q: is at $C but expected $D
+ fatal: cannot lock ref $SQ$prefix/foo$SQ: is at $C but expected $D
EOF
printf "%s\n" "delete $prefix/foo $D" |
test_must_fail git update-ref --stdin 2>output.err &&
@@ -378,7 +376,7 @@ test_expect_success 'missing old value blocks indirect update' '
prefix=refs/missing-indirect-update &&
git symbolic-ref $prefix/symref $prefix/foo &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $Q$prefix/symref$Q: unable to resolve reference $Q$prefix/foo$Q
+ fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ
EOF
printf "%s\n" "update $prefix/symref $E $D" |
test_must_fail git update-ref --stdin 2>output.err &&
@@ -390,7 +388,7 @@ test_expect_success 'incorrect old value blocks indirect update' '
git symbolic-ref $prefix/symref $prefix/foo &&
git update-ref $prefix/foo $C &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $Q$prefix/symref$Q: is at $C but expected $D
+ fatal: cannot lock ref $SQ$prefix/symref$SQ: is at $C but expected $D
EOF
printf "%s\n" "update $prefix/symref $E $D" |
test_must_fail git update-ref --stdin 2>output.err &&
@@ -402,7 +400,7 @@ test_expect_success 'existing old value blocks indirect create' '
git symbolic-ref $prefix/symref $prefix/foo &&
git update-ref $prefix/foo $C &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $Q$prefix/symref$Q: reference already exists
+ fatal: cannot lock ref $SQ$prefix/symref$SQ: reference already exists
EOF
printf "%s\n" "create $prefix/symref $E" |
test_must_fail git update-ref --stdin 2>output.err &&
@@ -414,7 +412,7 @@ test_expect_success 'incorrect old value blocks indirect delete' '
git symbolic-ref $prefix/symref $prefix/foo &&
git update-ref $prefix/foo $C &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $Q$prefix/symref$Q: is at $C but expected $D
+ fatal: cannot lock ref $SQ$prefix/symref$SQ: is at $C but expected $D
EOF
printf "%s\n" "delete $prefix/symref $D" |
test_must_fail git update-ref --stdin 2>output.err &&
@@ -425,7 +423,7 @@ test_expect_success 'missing old value blocks indirect no-deref update' '
prefix=refs/missing-noderef-update &&
git symbolic-ref $prefix/symref $prefix/foo &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $Q$prefix/symref$Q: reference is missing but expected $D
+ fatal: cannot lock ref $SQ$prefix/symref$SQ: reference is missing but expected $D
EOF
printf "%s\n" "option no-deref" "update $prefix/symref $E $D" |
test_must_fail git update-ref --stdin 2>output.err &&
@@ -437,7 +435,7 @@ test_expect_success 'incorrect old value blocks indirect no-deref update' '
git symbolic-ref $prefix/symref $prefix/foo &&
git update-ref $prefix/foo $C &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $Q$prefix/symref$Q: is at $C but expected $D
+ fatal: cannot lock ref $SQ$prefix/symref$SQ: is at $C but expected $D
EOF
printf "%s\n" "option no-deref" "update $prefix/symref $E $D" |
test_must_fail git update-ref --stdin 2>output.err &&
@@ -449,7 +447,7 @@ test_expect_success 'existing old value blocks indirect no-deref create' '
git symbolic-ref $prefix/symref $prefix/foo &&
git update-ref $prefix/foo $C &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $Q$prefix/symref$Q: reference already exists
+ fatal: cannot lock ref $SQ$prefix/symref$SQ: reference already exists
EOF
printf "%s\n" "option no-deref" "create $prefix/symref $E" |
test_must_fail git update-ref --stdin 2>output.err &&
@@ -461,7 +459,7 @@ test_expect_success 'incorrect old value blocks indirect no-deref delete' '
git symbolic-ref $prefix/symref $prefix/foo &&
git update-ref $prefix/foo $C &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $Q$prefix/symref$Q: is at $C but expected $D
+ fatal: cannot lock ref $SQ$prefix/symref$SQ: is at $C but expected $D
EOF
printf "%s\n" "option no-deref" "delete $prefix/symref $D" |
test_must_fail git update-ref --stdin 2>output.err &&
@@ -474,13 +472,13 @@ test_expect_success 'non-empty directory blocks create' '
: >.git/$prefix/foo/bar/baz.lock &&
test_when_finished "rm -f .git/$prefix/foo/bar/baz.lock" &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $Q$prefix/foo$Q: there is a non-empty directory $Q.git/$prefix/foo$Q blocking reference $Q$prefix/foo$Q
+ fatal: cannot lock ref $SQ$prefix/foo$SQ: there is a non-empty directory $SQ.git/$prefix/foo$SQ blocking reference $SQ$prefix/foo$SQ
EOF
printf "%s\n" "update $prefix/foo $C" |
test_must_fail git update-ref --stdin 2>output.err &&
test_cmp expected output.err &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $Q$prefix/foo$Q: unable to resolve reference $Q$prefix/foo$Q
+ fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ
EOF
printf "%s\n" "update $prefix/foo $D $C" |
test_must_fail git update-ref --stdin 2>output.err &&
@@ -493,13 +491,13 @@ test_expect_success 'broken reference blocks create' '
echo "gobbledigook" >.git/$prefix/foo &&
test_when_finished "rm -f .git/$prefix/foo" &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $Q$prefix/foo$Q: unable to resolve reference $Q$prefix/foo$Q: reference broken
+ fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
EOF
printf "%s\n" "update $prefix/foo $C" |
test_must_fail git update-ref --stdin 2>output.err &&
test_cmp expected output.err &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $Q$prefix/foo$Q: unable to resolve reference $Q$prefix/foo$Q: reference broken
+ fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
EOF
printf "%s\n" "update $prefix/foo $D $C" |
test_must_fail git update-ref --stdin 2>output.err &&
@@ -513,13 +511,13 @@ test_expect_success 'non-empty directory blocks indirect create' '
: >.git/$prefix/foo/bar/baz.lock &&
test_when_finished "rm -f .git/$prefix/foo/bar/baz.lock" &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $Q$prefix/symref$Q: there is a non-empty directory $Q.git/$prefix/foo$Q blocking reference $Q$prefix/foo$Q
+ fatal: cannot lock ref $SQ$prefix/symref$SQ: there is a non-empty directory $SQ.git/$prefix/foo$SQ blocking reference $SQ$prefix/foo$SQ
EOF
printf "%s\n" "update $prefix/symref $C" |
test_must_fail git update-ref --stdin 2>output.err &&
test_cmp expected output.err &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $Q$prefix/symref$Q: unable to resolve reference $Q$prefix/foo$Q
+ fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ
EOF
printf "%s\n" "update $prefix/symref $D $C" |
test_must_fail git update-ref --stdin 2>output.err &&
@@ -532,13 +530,13 @@ test_expect_success 'broken reference blocks indirect create' '
echo "gobbledigook" >.git/$prefix/foo &&
test_when_finished "rm -f .git/$prefix/foo" &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $Q$prefix/symref$Q: unable to resolve reference $Q$prefix/foo$Q: reference broken
+ fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
EOF
printf "%s\n" "update $prefix/symref $C" |
test_must_fail git update-ref --stdin 2>output.err &&
test_cmp expected output.err &&
cat >expected <<-EOF &&
- fatal: cannot lock ref $Q$prefix/symref$Q: unable to resolve reference $Q$prefix/foo$Q: reference broken
+ fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
EOF
printf "%s\n" "update $prefix/symref $D $C" |
test_must_fail git update-ref --stdin 2>output.err &&
@@ -614,7 +612,23 @@ test_expect_success 'delete fails cleanly if packed-refs file is locked' '
test_when_finished "rm -f .git/packed-refs.lock" &&
test_must_fail git update-ref -d $prefix/foo >out 2>err &&
git for-each-ref $prefix >actual &&
- test_i18ngrep "Unable to create $Q.*packed-refs.lock$Q: File exists" err &&
+ test_i18ngrep "Unable to create $SQ.*packed-refs.lock$SQ: " err &&
+ test_cmp unchanged actual
+'
+
+test_expect_success 'delete fails cleanly if packed-refs.new write fails' '
+ # Setup and expectations are similar to the test above.
+ prefix=refs/failed-packed-refs &&
+ git update-ref $prefix/foo $C &&
+ git pack-refs --all &&
+ git update-ref $prefix/foo $D &&
+ git for-each-ref $prefix >unchanged &&
+ # This should not happen in practice, but it is an easy way to get a
+ # reliable error (we open with create_tempfile(), which uses O_EXCL).
+ : >.git/packed-refs.new &&
+ test_when_finished "rm -f .git/packed-refs.new" &&
+ test_must_fail git update-ref -d $prefix/foo &&
+ git for-each-ref $prefix >actual &&
test_cmp unchanged actual
'
diff --git a/t/t1406-submodule-ref-store.sh b/t/t1406-submodule-ref-store.sh
index d199d87..36b7ef5 100755
--- a/t/t1406-submodule-ref-store.sh
+++ b/t/t1406-submodule-ref-store.sh
@@ -75,7 +75,7 @@ test_expect_success 'for_each_reflog()' '
'
test_expect_success 'for_each_reflog_ent()' '
- $RUN for-each-reflog-ent HEAD >actual && cat actual &&
+ $RUN for-each-reflog-ent HEAD >actual &&
head -n1 actual | grep first &&
tail -n2 actual | head -n1 | grep master.to.new
'
diff --git a/t/t1409-avoid-packing-refs.sh b/t/t1409-avoid-packing-refs.sh
index e5cb8a2..be12fb6 100755
--- a/t/t1409-avoid-packing-refs.sh
+++ b/t/t1409-avoid-packing-refs.sh
@@ -8,7 +8,7 @@ test_description='avoid rewriting packed-refs unnecessarily'
# shouldn't upset readers, and it should be omitted if the file is
# ever rewritten.
mark_packed_refs () {
- sed -e "s/^\(#.*\)/\1 t1409 /" <.git/packed-refs >.git/packed-refs.new &&
+ sed -e "s/^\(#.*\)/\1 t1409 /" .git/packed-refs >.git/packed-refs.new &&
mv .git/packed-refs.new .git/packed-refs
}
@@ -27,15 +27,15 @@ test_expect_success 'setup' '
'
test_expect_success 'do not create packed-refs file gratuitously' '
- test_must_fail test -f .git/packed-refs &&
+ test_path_is_missing .git/packed-refs &&
git update-ref refs/heads/foo $A &&
- test_must_fail test -f .git/packed-refs &&
+ test_path_is_missing .git/packed-refs &&
git update-ref refs/heads/foo $B &&
- test_must_fail test -f .git/packed-refs &&
+ test_path_is_missing .git/packed-refs &&
git update-ref refs/heads/foo $C $B &&
- test_must_fail test -f .git/packed-refs &&
+ test_path_is_missing .git/packed-refs &&
git update-ref -d refs/heads/foo &&
- test_must_fail test -f .git/packed-refs
+ test_path_is_missing .git/packed-refs
'
test_expect_success 'check that marking the packed-refs file works' '
@@ -46,7 +46,7 @@ test_expect_success 'check that marking the packed-refs file works' '
git for-each-ref >actual &&
test_cmp expected actual &&
git pack-refs --all &&
- test_must_fail check_packed_refs_marked &&
+ ! check_packed_refs_marked &&
git for-each-ref >actual2 &&
test_cmp expected actual2
'
@@ -80,7 +80,7 @@ test_expect_success 'touch packed-refs on delete of packed' '
git pack-refs --all &&
mark_packed_refs &&
git update-ref -d refs/heads/packed-delete &&
- test_must_fail check_packed_refs_marked
+ ! check_packed_refs_marked
'
test_expect_success 'leave packed-refs untouched on update of loose' '
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
index 3e05353..76d9b74 100755
--- a/t/t1410-reflog.sh
+++ b/t/t1410-reflog.sh
@@ -20,24 +20,23 @@ check_have () {
}
check_fsck () {
- output=$(git fsck --full)
+ git fsck --full >fsck.output
case "$1" in
'')
- test -z "$output" ;;
+ test_must_be_empty fsck.output ;;
*)
- echo "$output" | grep "$1" ;;
+ test_i18ngrep "$1" fsck.output ;;
esac
}
corrupt () {
- aa=${1%??????????????????????????????????????} zz=${1#??}
- mv .git/objects/$aa/$zz .git/$aa$zz
+ mv .git/objects/$(test_oid_to_path $1) .git/$1
}
recover () {
- aa=${1%??????????????????????????????????????} zz=${1#??}
+ aa=$(echo $1 | cut -c 1-2)
mkdir -p .git/objects/$aa
- mv .git/$aa$zz .git/objects/$aa/$zz
+ mv .git/$1 .git/objects/$(test_oid_to_path $1)
}
check_dont_have () {
@@ -55,6 +54,7 @@ check_dont_have () {
}
test_expect_success setup '
+ test_oid_init &&
mkdir -p A/B &&
echo rat >C &&
echo ox >A/D &&
@@ -195,7 +195,7 @@ test_expect_success 'delete' '
git reflog delete master@{1} &&
git reflog show master > output &&
- test $(($master_entry_count - 1)) = $(wc -l < output) &&
+ test_line_count = $(($master_entry_count - 1)) output &&
test $HEAD_entry_count = $(git reflog | wc -l) &&
! grep ox < output &&
@@ -209,7 +209,7 @@ test_expect_success 'delete' '
git reflog delete master@{07.04.2005.15:15:00.-0700} &&
git reflog show master > output &&
- test $(($master_entry_count - 1)) = $(wc -l < output) &&
+ test_line_count = $(($master_entry_count - 1)) output &&
! grep dragon < output
'
@@ -232,25 +232,34 @@ test_expect_success '--expire=never' '
'
test_expect_success 'gc.reflogexpire=never' '
+ test_config gc.reflogexpire never &&
+ test_config gc.reflogexpireunreachable never &&
+
+ git reflog expire --verbose --all >output &&
+ test_line_count = 9 output &&
- git config gc.reflogexpire never &&
- git config gc.reflogexpireunreachable never &&
- git reflog expire --verbose --all &&
git reflog refs/heads/master >output &&
test_line_count = 4 output
'
test_expect_success 'gc.reflogexpire=false' '
+ test_config gc.reflogexpire false &&
+ test_config gc.reflogexpireunreachable false &&
- git config gc.reflogexpire false &&
- git config gc.reflogexpireunreachable false &&
git reflog expire --verbose --all &&
git reflog refs/heads/master >output &&
- test_line_count = 4 output &&
+ test_line_count = 4 output
+
+'
- git config --unset gc.reflogexpire &&
- git config --unset gc.reflogexpireunreachable
+test_expect_success 'git reflog expire unknown reference' '
+ test_config gc.reflogexpire never &&
+ test_config gc.reflogexpireunreachable never &&
+ test_must_fail git reflog expire master@{123} 2>stderr &&
+ test_i18ngrep "points nowhere" stderr &&
+ test_must_fail git reflog expire does-not-exist 2>stderr &&
+ test_i18ngrep "points nowhere" stderr
'
test_expect_success 'checkout should not delete log for packed ref' '
@@ -304,12 +313,12 @@ test_expect_success 'stale dirs do not cause d/f conflicts (reflogs off)' '
# Each line is 114 characters, so we need 75 to still have a few before the
# last 8K. The 89-character padding on the final entry lines up our
# newline exactly.
-test_expect_success 'parsing reverse reflogs at BUFSIZ boundaries' '
+test_expect_success SHA1 'parsing reverse reflogs at BUFSIZ boundaries' '
git checkout -b reflogskip &&
- z38=00000000000000000000000000000000000000 &&
+ zf=$(test_oid zero_2) &&
ident="abc <xyz> 0000000001 +0000" &&
for i in $(test_seq 1 75); do
- printf "$z38%02d $z38%02d %s\t" $i $(($i+1)) "$ident" &&
+ printf "$zf%02d $zf%02d %s\t" $i $(($i+1)) "$ident" &&
if test $i = 75; then
for j in $(test_seq 1 89); do
printf X
@@ -320,7 +329,7 @@ test_expect_success 'parsing reverse reflogs at BUFSIZ boundaries' '
printf "\n"
done >.git/logs/refs/heads/reflogskip &&
git rev-parse reflogskip@{73} >actual &&
- echo ${z38}03 >expect &&
+ echo ${zf}03 >expect &&
test_cmp expect actual
'
diff --git a/t/t1414-reflog-walk.sh b/t/t1414-reflog-walk.sh
index feb1efd..1181a9f 100755
--- a/t/t1414-reflog-walk.sh
+++ b/t/t1414-reflog-walk.sh
@@ -18,10 +18,9 @@ do_walk () {
git log -g --format="%gd %gs" "$@"
}
-sq="'"
test_expect_success 'set up expected reflog' '
cat >expect.all <<-EOF
- HEAD@{0} commit (merge): Merge branch ${sq}master${sq} into side
+ HEAD@{0} commit (merge): Merge branch ${SQ}master${SQ} into side
HEAD@{1} commit: three
HEAD@{2} checkout: moving from master to side
HEAD@{3} commit: two
diff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh
index b664e51..bb2c757 100755
--- a/t/t1415-worktree-refs.sh
+++ b/t/t1415-worktree-refs.sh
@@ -76,4 +76,39 @@ test_expect_success 'reflog of worktrees/xx/HEAD' '
test_cmp expected actual.wt2
'
+test_expect_success 'for-each-ref from main repo' '
+ mkdir fer1 &&
+ git -C fer1 init repo &&
+ test_commit -C fer1/repo initial &&
+ git -C fer1/repo worktree add ../second &&
+ git -C fer1/repo update-ref refs/bisect/main HEAD &&
+ git -C fer1/repo update-ref refs/rewritten/main HEAD &&
+ git -C fer1/repo update-ref refs/worktree/main HEAD &&
+ git -C fer1/repo for-each-ref --format="%(refname)" | grep main >actual &&
+ cat >expected <<-\EOF &&
+ refs/bisect/main
+ refs/rewritten/main
+ refs/worktree/main
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'for-each-ref from linked repo' '
+ mkdir fer2 &&
+ git -C fer2 init repo &&
+ test_commit -C fer2/repo initial &&
+ git -C fer2/repo worktree add ../second &&
+ git -C fer2/second update-ref refs/bisect/second HEAD &&
+ git -C fer2/second update-ref refs/rewritten/second HEAD &&
+ git -C fer2/second update-ref refs/worktree/second HEAD &&
+ git -C fer2/second for-each-ref --format="%(refname)" | grep second >actual &&
+ cat >expected <<-\EOF &&
+ refs/bisect/second
+ refs/heads/second
+ refs/rewritten/second
+ refs/worktree/second
+ EOF
+ test_cmp expected actual
+'
+
test_done
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index c767e27..449ebc5 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -9,6 +9,7 @@ test_description='git fsck random collection of tests
. ./test-lib.sh
test_expect_success setup '
+ test_oid_init &&
git config gc.auto 0 &&
git config i18n.commitencoding ISO-8859-1 &&
test_commit A fileA one &&
@@ -54,8 +55,8 @@ test_expect_success 'setup: helpers for corruption tests' '
test_expect_success 'object with bad sha1' '
sha=$(echo blob | git hash-object -w --stdin) &&
- old=$(echo $sha | sed "s+^..+&/+") &&
- new=$(dirname $old)/ffffffffffffffffffffffffffffffffffffff &&
+ old=$(test_oid_to_path "$sha") &&
+ new=$(dirname $old)/$(test_oid ff_2) &&
sha="$(dirname $new)$(basename $new)" &&
mv .git/objects/$old .git/objects/$new &&
test_when_finished "remove_object $sha" &&
@@ -69,26 +70,23 @@ test_expect_success 'object with bad sha1' '
test_when_finished "git update-ref -d refs/heads/bogus" &&
test_must_fail git fsck 2>out &&
- cat out &&
- grep "$sha.*corrupt" out
+ test_i18ngrep "$sha.*corrupt" out
'
test_expect_success 'branch pointing to non-commit' '
git rev-parse HEAD^{tree} >.git/refs/heads/invalid &&
test_when_finished "git update-ref -d refs/heads/invalid" &&
test_must_fail git fsck 2>out &&
- cat out &&
- grep "not a commit" out
+ test_i18ngrep "not a commit" out
'
test_expect_success 'HEAD link pointing at a funny object' '
test_when_finished "mv .git/SAVED_HEAD .git/HEAD" &&
mv .git/HEAD .git/SAVED_HEAD &&
- echo 0000000000000000000000000000000000000000 >.git/HEAD &&
+ echo $ZERO_OID >.git/HEAD &&
# avoid corrupt/broken HEAD from interfering with repo discovery
test_must_fail env GIT_DIR=.git git fsck 2>out &&
- cat out &&
- grep "detached HEAD points" out
+ test_i18ngrep "detached HEAD points" out
'
test_expect_success 'HEAD link pointing at a funny place' '
@@ -97,8 +95,7 @@ test_expect_success 'HEAD link pointing at a funny place' '
echo "ref: refs/funny/place" >.git/HEAD &&
# avoid corrupt/broken HEAD from interfering with repo discovery
test_must_fail env GIT_DIR=.git git fsck 2>out &&
- cat out &&
- grep "HEAD points to something strange" out
+ test_i18ngrep "HEAD points to something strange" out
'
test_expect_success 'HEAD link pointing at a funny object (from different wt)' '
@@ -109,7 +106,7 @@ test_expect_success 'HEAD link pointing at a funny object (from different wt)' '
echo $ZERO_OID >.git/HEAD &&
# avoid corrupt/broken HEAD from interfering with repo discovery
test_must_fail git -C wt fsck 2>out &&
- grep "main-worktree/HEAD: detached HEAD points" out
+ test_i18ngrep "main-worktree/HEAD: detached HEAD points" out
'
test_expect_success 'other worktree HEAD link pointing at a funny object' '
@@ -117,7 +114,7 @@ test_expect_success 'other worktree HEAD link pointing at a funny object' '
git worktree add other &&
echo $ZERO_OID >.git/worktrees/other/HEAD &&
test_must_fail git fsck 2>out &&
- grep "worktrees/other/HEAD: detached HEAD points" out
+ test_i18ngrep "worktrees/other/HEAD: detached HEAD points" out
'
test_expect_success 'other worktree HEAD link pointing at missing object' '
@@ -125,7 +122,7 @@ test_expect_success 'other worktree HEAD link pointing at missing object' '
git worktree add other &&
echo "Contents missing from repo" | git hash-object --stdin >.git/worktrees/other/HEAD &&
test_must_fail git fsck 2>out &&
- grep "worktrees/other/HEAD: invalid sha1 pointer" out
+ test_i18ngrep "worktrees/other/HEAD: invalid sha1 pointer" out
'
test_expect_success 'other worktree HEAD link pointing at a funny place' '
@@ -133,7 +130,31 @@ test_expect_success 'other worktree HEAD link pointing at a funny place' '
git worktree add other &&
echo "ref: refs/funny/place" >.git/worktrees/other/HEAD &&
test_must_fail git fsck 2>out &&
- grep "worktrees/other/HEAD points to something strange" out
+ test_i18ngrep "worktrees/other/HEAD points to something strange" out
+'
+
+test_expect_success 'commit with multiple signatures is okay' '
+ git cat-file commit HEAD >basis &&
+ cat >sigs <<-EOF &&
+ gpgsig -----BEGIN PGP SIGNATURE-----
+ VGhpcyBpcyBub3QgcmVhbGx5IGEgc2lnbmF0dXJlLg==
+ -----END PGP SIGNATURE-----
+ gpgsig-sha256 -----BEGIN PGP SIGNATURE-----
+ VGhpcyBpcyBub3QgcmVhbGx5IGEgc2lnbmF0dXJlLg==
+ -----END PGP SIGNATURE-----
+ EOF
+ sed -e "/^committer/q" basis >okay &&
+ cat sigs >>okay &&
+ echo >>okay &&
+ sed -e "1,/^$/d" basis >>okay &&
+ cat okay &&
+ new=$(git hash-object -t commit -w --stdin <okay) &&
+ test_when_finished "remove_object $new" &&
+ git update-ref refs/heads/bogus "$new" &&
+ test_when_finished "git update-ref -d refs/heads/bogus" &&
+ git fsck 2>out &&
+ cat out &&
+ ! grep "commit $new" out
'
test_expect_success 'email without @ is okay' '
@@ -144,7 +165,6 @@ test_expect_success 'email without @ is okay' '
git update-ref refs/heads/bogus "$new" &&
test_when_finished "git update-ref -d refs/heads/bogus" &&
git fsck 2>out &&
- cat out &&
! grep "commit $new" out
'
@@ -156,8 +176,7 @@ test_expect_success 'email with embedded > is not okay' '
git update-ref refs/heads/bogus "$new" &&
test_when_finished "git update-ref -d refs/heads/bogus" &&
test_must_fail git fsck 2>out &&
- cat out &&
- grep "error in commit $new" out
+ test_i18ngrep "error in commit $new" out
'
test_expect_success 'missing < email delimiter is reported nicely' '
@@ -168,8 +187,7 @@ test_expect_success 'missing < email delimiter is reported nicely' '
git update-ref refs/heads/bogus "$new" &&
test_when_finished "git update-ref -d refs/heads/bogus" &&
test_must_fail git fsck 2>out &&
- cat out &&
- grep "error in commit $new.* - bad name" out
+ test_i18ngrep "error in commit $new.* - bad name" out
'
test_expect_success 'missing email is reported nicely' '
@@ -180,8 +198,7 @@ test_expect_success 'missing email is reported nicely' '
git update-ref refs/heads/bogus "$new" &&
test_when_finished "git update-ref -d refs/heads/bogus" &&
test_must_fail git fsck 2>out &&
- cat out &&
- grep "error in commit $new.* - missing email" out
+ test_i18ngrep "error in commit $new.* - missing email" out
'
test_expect_success '> in name is reported' '
@@ -192,8 +209,7 @@ test_expect_success '> in name is reported' '
git update-ref refs/heads/bogus "$new" &&
test_when_finished "git update-ref -d refs/heads/bogus" &&
test_must_fail git fsck 2>out &&
- cat out &&
- grep "error in commit $new" out
+ test_i18ngrep "error in commit $new" out
'
# date is 2^64 + 1
@@ -206,8 +222,7 @@ test_expect_success 'integer overflow in timestamps is reported' '
git update-ref refs/heads/bogus "$new" &&
test_when_finished "git update-ref -d refs/heads/bogus" &&
test_must_fail git fsck 2>out &&
- cat out &&
- grep "error in commit $new.*integer overflow" out
+ test_i18ngrep "error in commit $new.*integer overflow" out
'
test_expect_success 'commit with NUL in header' '
@@ -218,8 +233,7 @@ test_expect_success 'commit with NUL in header' '
git update-ref refs/heads/bogus "$new" &&
test_when_finished "git update-ref -d refs/heads/bogus" &&
test_must_fail git fsck 2>out &&
- cat out &&
- grep "error in commit $new.*unterminated header: NUL at offset" out
+ test_i18ngrep "error in commit $new.*unterminated header: NUL at offset" out
'
test_expect_success 'tree object with duplicate entries' '
@@ -240,14 +254,20 @@ test_expect_success 'tree object with duplicate entries' '
git hash-object -w -t tree --stdin
) &&
test_must_fail git fsck 2>out &&
- grep "error in tree .*contains duplicate file entries" out
+ test_i18ngrep "error in tree .*contains duplicate file entries" out
'
test_expect_success 'unparseable tree object' '
+ test_oid_cache <<-\EOF &&
+ junk sha1:twenty-bytes-of-junk
+ junk sha256:twenty-bytes-of-junk-twelve-more
+ EOF
+
test_when_finished "git update-ref -d refs/heads/wrong" &&
test_when_finished "remove_object \$tree_sha1" &&
test_when_finished "remove_object \$commit_sha1" &&
- tree_sha1=$(printf "100644 \0twenty-bytes-of-junk" | git hash-object -t tree --stdin -w --literally) &&
+ junk=$(test_oid junk) &&
+ tree_sha1=$(printf "100644 \0$junk" | git hash-object -t tree --stdin -w --literally) &&
commit_sha1=$(git commit-tree $tree_sha1) &&
git update-ref refs/heads/wrong $commit_sha1 &&
test_must_fail git fsck 2>out &&
@@ -256,10 +276,6 @@ test_expect_success 'unparseable tree object' '
test_i18ngrep ! "fatal: empty filename in tree entry" out
'
-hex2oct() {
- perl -ne 'printf "\\%03o", hex for /../g'
-}
-
test_expect_success 'tree entry with type mismatch' '
test_when_finished "remove_object \$blob" &&
test_when_finished "remove_object \$tree" &&
@@ -279,8 +295,9 @@ test_expect_success 'tree entry with type mismatch' '
'
test_expect_success 'tag pointing to nonexistent' '
- cat >invalid-tag <<-\EOF &&
- object ffffffffffffffffffffffffffffffffffffffff
+ badoid=$(test_oid deadbeef) &&
+ cat >invalid-tag <<-EOF &&
+ object $badoid
type commit
tag invalid
tagger T A Gger <tagger@example.com> 1234567890 -0000
@@ -293,8 +310,7 @@ test_expect_success 'tag pointing to nonexistent' '
echo $tag >.git/refs/tags/invalid &&
test_when_finished "git update-ref -d refs/tags/invalid" &&
test_must_fail git fsck --tags >out &&
- cat out &&
- grep "broken link" out
+ test_i18ngrep "broken link" out
'
test_expect_success 'tag pointing to something else than its type' '
@@ -336,7 +352,7 @@ test_expect_success 'tag with incorrect tag name & missing tagger' '
warning in tag $tag: badTagName: invalid '\''tag'\'' name: wrong name format
warning in tag $tag: missingTaggerEntry: invalid format - expected '\''tagger'\'' line
EOF
- test_cmp expect out
+ test_i18ncmp expect out
'
test_expect_success 'tag with bad tagger' '
@@ -355,7 +371,7 @@ test_expect_success 'tag with bad tagger' '
echo $tag >.git/refs/tags/wrong &&
test_when_finished "git update-ref -d refs/tags/wrong" &&
test_must_fail git fsck --tags 2>out &&
- grep "error in tag .*: invalid author/committer" out
+ test_i18ngrep "error in tag .*: invalid author/committer" out
'
test_expect_success 'tag with NUL in header' '
@@ -374,8 +390,7 @@ test_expect_success 'tag with NUL in header' '
echo $tag >.git/refs/tags/wrong &&
test_when_finished "git update-ref -d refs/tags/wrong" &&
test_must_fail git fsck --tags 2>out &&
- cat out &&
- grep "error in tag $tag.*unterminated header: NUL at offset" out
+ test_i18ngrep "error in tag $tag.*unterminated header: NUL at offset" out
'
test_expect_success 'cleaned up' '
@@ -390,8 +405,8 @@ test_expect_success 'rev-list --verify-objects' '
test_expect_success 'rev-list --verify-objects with bad sha1' '
sha=$(echo blob | git hash-object -w --stdin) &&
- old=$(echo $sha | sed "s+^..+&/+") &&
- new=$(dirname $old)/ffffffffffffffffffffffffffffffffffffff &&
+ old=$(test_oid_to_path $sha) &&
+ new=$(dirname $old)/$(test_oid ff_2) &&
sha="$(dirname $new)$(basename $new)" &&
mv .git/objects/$old .git/objects/$new &&
test_when_finished "remove_object $sha" &&
@@ -405,8 +420,7 @@ test_expect_success 'rev-list --verify-objects with bad sha1' '
test_when_finished "git update-ref -d refs/heads/bogus" &&
test_might_fail git rev-list --verify-objects refs/heads/bogus >/dev/null 2>out &&
- cat out &&
- test_i18ngrep -q "error: sha1 mismatch 63ffffffffffffffffffffffffffffffffffffff" out
+ test_i18ngrep -q "error: hash mismatch $(dirname $new)$(test_oid ff_2)" out
'
test_expect_success 'force fsck to ignore double author' '
@@ -421,28 +435,25 @@ test_expect_success 'force fsck to ignore double author' '
'
_bz='\0'
-_bz5="$_bz$_bz$_bz$_bz$_bz"
-_bz20="$_bz5$_bz5$_bz5$_bz5"
+_bzoid=$(printf $ZERO_OID | sed -e 's/00/\\0/g')
test_expect_success 'fsck notices blob entry pointing to null sha1' '
(git init null-blob &&
cd null-blob &&
- sha=$(printf "100644 file$_bz$_bz20" |
+ sha=$(printf "100644 file$_bz$_bzoid" |
git hash-object -w --stdin -t tree) &&
git fsck 2>out &&
- cat out &&
- grep "warning.*null sha1" out
+ test_i18ngrep "warning.*null sha1" out
)
'
test_expect_success 'fsck notices submodule entry pointing to null sha1' '
(git init null-commit &&
cd null-commit &&
- sha=$(printf "160000 submodule$_bz$_bz20" |
+ sha=$(printf "160000 submodule$_bz$_bzoid" |
git hash-object -w --stdin -t tree) &&
git fsck 2>out &&
- cat out &&
- grep "warning.*null sha1" out
+ test_i18ngrep "warning.*null sha1" out
)
'
@@ -463,8 +474,7 @@ while read name path pretty; do
printf "$mode $type %s\t%s" "$value" "$path" >bad &&
bad_tree=$(git mktree <bad) &&
git fsck 2>out &&
- cat out &&
- grep "warning.*tree $bad_tree" out
+ test_i18ngrep "warning.*tree $bad_tree" out
)'
done <<-\EOF
100644 blob
@@ -510,9 +520,9 @@ test_expect_success 'NUL in commit' '
git branch bad $(cat name) &&
test_must_fail git -c fsck.nulInCommit=error fsck 2>warn.1 &&
- grep nulInCommit warn.1 &&
+ test_i18ngrep nulInCommit warn.1 &&
git fsck 2>warn.2 &&
- grep nulInCommit warn.2
+ test_i18ngrep nulInCommit warn.2
)
'
@@ -591,7 +601,7 @@ test_expect_success 'fsck --connectivity-only' '
# its type. That lets us see that --connectivity-only is
# not actually looking at the contents, but leaves it
# free to examine the type if it chooses.
- empty=.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 &&
+ empty=.git/objects/$(test_oid_to_path $EMPTY_BLOB) &&
blob=$(echo unrelated | git hash-object -w --stdin) &&
mv -f $(sha1_file $blob) $empty &&
@@ -630,18 +640,20 @@ test_expect_success 'fsck --name-objects' '
remove_object $(git rev-parse julius:caesar.t) &&
test_must_fail git fsck --name-objects >out &&
tree=$(git rev-parse --verify julius:) &&
- egrep "$tree \((refs/heads/master|HEAD)@\{[0-9]*\}:" out
+ test_i18ngrep "$tree (refs/tags/julius:" out
)
'
test_expect_success 'alternate objects are correctly blamed' '
test_when_finished "rm -rf alt.git .git/objects/info/alternates" &&
+ name=$(test_oid numeric) &&
+ path=$(test_oid_to_path "$name") &&
git init --bare alt.git &&
echo "../../alt.git/objects" >.git/objects/info/alternates &&
- mkdir alt.git/objects/12 &&
- >alt.git/objects/12/34567890123456789012345678901234567890 &&
+ mkdir alt.git/objects/$(dirname $path) &&
+ >alt.git/objects/$(dirname $path)/$(basename $path) &&
test_must_fail git fsck >out 2>&1 &&
- grep alt.git out
+ test_i18ngrep alt.git out
'
test_expect_success 'fsck errors in packed objects' '
@@ -660,8 +672,8 @@ test_expect_success 'fsck errors in packed objects' '
remove_object $one &&
remove_object $two &&
test_must_fail git fsck 2>out &&
- grep "error in commit $one.* - bad name" out &&
- grep "error in commit $two.* - bad name" out &&
+ test_i18ngrep "error in commit $one.* - bad name" out &&
+ test_i18ngrep "error in commit $two.* - bad name" out &&
! grep corrupt out
'
@@ -741,7 +753,7 @@ test_expect_success 'fsck detects truncated loose object' '
# for each of type, we have one version which is referenced by another object
# (and so while unreachable, not dangling), and another variant which really is
# dangling.
-test_expect_success 'fsck notices dangling objects' '
+test_expect_success 'create dangling-object repository' '
git init dangling &&
(
cd dangling &&
@@ -752,16 +764,31 @@ test_expect_success 'fsck notices dangling objects' '
commit=$(git commit-tree $tree) &&
dcommit=$(git commit-tree -p $commit $tree) &&
- cat >expect <<-EOF &&
+ cat >expect <<-EOF
dangling blob $dblob
dangling commit $dcommit
dangling tree $dtree
EOF
+ )
+'
+test_expect_success 'fsck notices dangling objects' '
+ (
+ cd dangling &&
git fsck >actual &&
# the output order is non-deterministic, as it comes from a hash
sort <actual >actual.sorted &&
- test_cmp expect actual.sorted
+ test_i18ncmp expect actual.sorted
+ )
+'
+
+test_expect_success 'fsck --connectivity-only notices dangling objects' '
+ (
+ cd dangling &&
+ git fsck --connectivity-only >actual &&
+ # the output order is non-deterministic, as it comes from a hash
+ sort <actual >actual.sorted &&
+ test_i18ncmp expect actual.sorted
)
'
@@ -809,7 +836,7 @@ test_expect_success 'detect corrupt index file in fsck' '
test_when_finished "mv .git/index.backup .git/index" &&
corrupt_index_checksum &&
test_must_fail git fsck --cache 2>errors &&
- grep "bad index file" errors
+ test_i18ngrep "bad index file" errors
'
test_done
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index 01abee5..603019b 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -59,6 +59,7 @@ test_rev_parse () {
ROOT=$(pwd)
test_expect_success 'setup' '
+ test_oid_init &&
mkdir -p sub/dir work &&
cp -R .git repo.git
'
@@ -131,6 +132,30 @@ test_expect_success 'rev-parse --is-shallow-repository in non-shallow repo' '
test_cmp expect actual
'
+test_expect_success 'rev-parse --show-object-format in repo' '
+ echo "$(test_oid algo)" >expect &&
+ git rev-parse --show-object-format >actual &&
+ test_cmp expect actual &&
+ git rev-parse --show-object-format=storage >actual &&
+ test_cmp expect actual &&
+ git rev-parse --show-object-format=input >actual &&
+ test_cmp expect actual &&
+ git rev-parse --show-object-format=output >actual &&
+ test_cmp expect actual &&
+ test_must_fail git rev-parse --show-object-format=squeamish-ossifrage 2>err &&
+ grep "unknown mode for --show-object-format: squeamish-ossifrage" err
+'
+
+test_expect_success '--show-toplevel from subdir of working tree' '
+ pwd >expect &&
+ git -C sub/dir rev-parse --show-toplevel >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--show-toplevel from inside .git' '
+ test_must_fail git -C .git rev-parse --show-toplevel
+'
+
test_expect_success 'showing the superproject correctly' '
git rev-parse --show-superproject-working-tree >out &&
test_must_be_empty out &&
diff --git a/t/t1501-work-tree.sh b/t/t1501-work-tree.sh
index 3498d3d..b755580 100755
--- a/t/t1501-work-tree.sh
+++ b/t/t1501-work-tree.sh
@@ -350,7 +350,7 @@ test_expect_success 'Multi-worktree setup' '
mkdir work &&
mkdir -p repo.git/repos/foo &&
cp repo.git/HEAD repo.git/index repo.git/repos/foo &&
- test_might_fail cp repo.git/sharedindex.* repo.git/repos/foo &&
+ { cp repo.git/sharedindex.* repo.git/repos/foo || :; } &&
sane_unset GIT_DIR GIT_CONFIG GIT_WORK_TREE
'
diff --git a/t/t1506-rev-parse-diagnosis.sh b/t/t1506-rev-parse-diagnosis.sh
index 4ee009d..52edcbd 100755
--- a/t/t1506-rev-parse-diagnosis.sh
+++ b/t/t1506-rev-parse-diagnosis.sh
@@ -8,12 +8,11 @@ exec </dev/null
test_did_you_mean ()
{
- sq="'" &&
cat >expected <<-EOF &&
- fatal: Path '$2$3' $4, but not ${5:-$sq$3$sq}.
- Did you mean '$1:$2$3'${2:+ aka $sq$1:./$3$sq}?
+ fatal: path '$2$3' $4, but not ${5:-$SQ$3$SQ}
+ hint: Did you mean '$1:$2$3'${2:+ aka $SQ$1:./$3$SQ}?
EOF
- test_cmp expected error
+ test_i18ncmp expected error
}
HASH_file=
@@ -104,66 +103,66 @@ test_expect_success 'correct relative file objects (6)' '
test_expect_success 'incorrect revision id' '
test_must_fail git rev-parse foobar:file.txt 2>error &&
- grep "Invalid object name '"'"'foobar'"'"'." error &&
- test_must_fail git rev-parse foobar 2> error &&
+ test_i18ngrep "invalid object name .foobar." error &&
+ test_must_fail git rev-parse foobar 2>error &&
test_i18ngrep "unknown revision or path not in the working tree." error
'
test_expect_success 'incorrect file in sha1:path' '
- test_must_fail git rev-parse HEAD:nothing.txt 2> error &&
- grep "fatal: Path '"'"'nothing.txt'"'"' does not exist in '"'"'HEAD'"'"'" error &&
- test_must_fail git rev-parse HEAD:index-only.txt 2> error &&
- grep "fatal: Path '"'"'index-only.txt'"'"' exists on disk, but not in '"'"'HEAD'"'"'." error &&
+ test_must_fail git rev-parse HEAD:nothing.txt 2>error &&
+ test_i18ngrep "path .nothing.txt. does not exist in .HEAD." error &&
+ test_must_fail git rev-parse HEAD:index-only.txt 2>error &&
+ test_i18ngrep "path .index-only.txt. exists on disk, but not in .HEAD." error &&
(cd subdir &&
- test_must_fail git rev-parse HEAD:file2.txt 2> error &&
+ test_must_fail git rev-parse HEAD:file2.txt 2>error &&
test_did_you_mean HEAD subdir/ file2.txt exists )
'
test_expect_success 'incorrect file in :path and :N:path' '
- test_must_fail git rev-parse :nothing.txt 2> error &&
- grep "fatal: Path '"'"'nothing.txt'"'"' does not exist (neither on disk nor in the index)." error &&
- test_must_fail git rev-parse :1:nothing.txt 2> error &&
- grep "Path '"'"'nothing.txt'"'"' does not exist (neither on disk nor in the index)." error &&
- test_must_fail git rev-parse :1:file.txt 2> error &&
+ test_must_fail git rev-parse :nothing.txt 2>error &&
+ test_i18ngrep "path .nothing.txt. does not exist (neither on disk nor in the index)" error &&
+ test_must_fail git rev-parse :1:nothing.txt 2>error &&
+ test_i18ngrep "path .nothing.txt. does not exist (neither on disk nor in the index)" error &&
+ test_must_fail git rev-parse :1:file.txt 2>error &&
test_did_you_mean ":0" "" file.txt "is in the index" "at stage 1" &&
(cd subdir &&
- test_must_fail git rev-parse :1:file.txt 2> error &&
+ test_must_fail git rev-parse :1:file.txt 2>error &&
test_did_you_mean ":0" "" file.txt "is in the index" "at stage 1" &&
- test_must_fail git rev-parse :file2.txt 2> error &&
+ test_must_fail git rev-parse :file2.txt 2>error &&
test_did_you_mean ":0" subdir/ file2.txt "is in the index" &&
- test_must_fail git rev-parse :2:file2.txt 2> error &&
+ test_must_fail git rev-parse :2:file2.txt 2>error &&
test_did_you_mean :0 subdir/ file2.txt "is in the index") &&
- test_must_fail git rev-parse :disk-only.txt 2> error &&
- grep "fatal: Path '"'"'disk-only.txt'"'"' exists on disk, but not in the index." error
+ test_must_fail git rev-parse :disk-only.txt 2>error &&
+ test_i18ngrep "path .disk-only.txt. exists on disk, but not in the index" error
'
test_expect_success 'invalid @{n} reference' '
test_must_fail git rev-parse master@{99999} >output 2>error &&
- test -z "$(cat output)" &&
- grep "fatal: Log for [^ ]* only has [0-9][0-9]* entries." error &&
+ test_must_be_empty output &&
+ test_i18ngrep "log for [^ ]* only has [0-9][0-9]* entries" error &&
test_must_fail git rev-parse --verify master@{99999} >output 2>error &&
- test -z "$(cat output)" &&
- grep "fatal: Log for [^ ]* only has [0-9][0-9]* entries." error
+ test_must_be_empty output &&
+ test_i18ngrep "log for [^ ]* only has [0-9][0-9]* entries" error
'
test_expect_success 'relative path not found' '
(
cd subdir &&
test_must_fail git rev-parse HEAD:./nonexistent.txt 2>error &&
- grep subdir/nonexistent.txt error
+ test_i18ngrep subdir/nonexistent.txt error
)
'
test_expect_success 'relative path outside worktree' '
test_must_fail git rev-parse HEAD:../file.txt >output 2>error &&
- test -z "$(cat output)" &&
+ test_must_be_empty output &&
test_i18ngrep "outside repository" error
'
test_expect_success 'relative path when cwd is outside worktree' '
test_must_fail git --git-dir=.git --work-tree=subdir rev-parse HEAD:./file.txt >output 2>error &&
- test -z "$(cat output)" &&
- grep "relative path syntax can.t be used outside working tree." error
+ test_must_be_empty output &&
+ test_i18ngrep "relative path syntax can.t be used outside working tree" error
'
test_expect_success '<commit>:file correctly diagnosed after a pathname' '
@@ -215,4 +214,26 @@ test_expect_success 'arg before dashdash must be a revision (ambiguous)' '
test_cmp expect actual
'
+test_expect_success 'reject Nth parent if N is too high' '
+ test_must_fail git rev-parse HEAD^100000000000000000000000000000000
+'
+
+test_expect_success 'reject Nth ancestor if N is too high' '
+ test_must_fail git rev-parse HEAD~100000000000000000000000000000000
+'
+
+test_expect_success 'pathspecs with wildcards are not ambiguous' '
+ echo "*.c" >expect &&
+ git rev-parse "*.c" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'backslash does not trigger wildcard rule' '
+ test_must_fail git rev-parse "foo\\bar"
+'
+
+test_expect_success 'escaped char does not trigger wildcard rule' '
+ test_must_fail git rev-parse "foo\\*bar"
+'
+
test_done
diff --git a/t/t1507-rev-parse-upstream.sh b/t/t1507-rev-parse-upstream.sh
index fa3e499..dfc0d96 100755
--- a/t/t1507-rev-parse-upstream.sh
+++ b/t/t1507-rev-parse-upstream.sh
@@ -28,16 +28,9 @@ test_expect_success 'setup' '
)
'
-sq="'"
-
-full_name () {
- (cd clone &&
- git rev-parse --symbolic-full-name "$@")
-}
-
commit_subject () {
(cd clone &&
- git show -s --pretty=format:%s "$@")
+ git show -s --pretty=tformat:%s "$@")
}
error_message () {
@@ -46,63 +39,78 @@ error_message () {
}
test_expect_success '@{upstream} resolves to correct full name' '
- test refs/remotes/origin/master = "$(full_name @{upstream})" &&
- test refs/remotes/origin/master = "$(full_name @{UPSTREAM})" &&
- test refs/remotes/origin/master = "$(full_name @{UpSTReam})"
+ echo refs/remotes/origin/master >expect &&
+ git -C clone rev-parse --symbolic-full-name @{upstream} >actual &&
+ test_cmp expect actual &&
+ git -C clone rev-parse --symbolic-full-name @{UPSTREAM} >actual &&
+ test_cmp expect actual &&
+ git -C clone rev-parse --symbolic-full-name @{UpSTReam} >actual &&
+ test_cmp expect actual
'
test_expect_success '@{u} resolves to correct full name' '
- test refs/remotes/origin/master = "$(full_name @{u})" &&
- test refs/remotes/origin/master = "$(full_name @{U})"
+ echo refs/remotes/origin/master >expect &&
+ git -C clone rev-parse --symbolic-full-name @{u} >actual &&
+ test_cmp expect actual &&
+ git -C clone rev-parse --symbolic-full-name @{U} >actual &&
+ test_cmp expect actual
'
test_expect_success 'my-side@{upstream} resolves to correct full name' '
- test refs/remotes/origin/side = "$(full_name my-side@{u})"
+ echo refs/remotes/origin/side >expect &&
+ git -C clone rev-parse --symbolic-full-name my-side@{u} >actual &&
+ test_cmp expect actual
'
test_expect_success 'upstream of branch with @ in middle' '
- full_name fun@ny@{u} >actual &&
+ git -C clone rev-parse --symbolic-full-name fun@ny@{u} >actual &&
echo refs/remotes/origin/side >expect &&
test_cmp expect actual &&
- full_name fun@ny@{U} >actual &&
+ git -C clone rev-parse --symbolic-full-name fun@ny@{U} >actual &&
test_cmp expect actual
'
test_expect_success 'upstream of branch with @ at start' '
- full_name @funny@{u} >actual &&
+ git -C clone rev-parse --symbolic-full-name @funny@{u} >actual &&
echo refs/remotes/origin/side >expect &&
test_cmp expect actual
'
test_expect_success 'upstream of branch with @ at end' '
- full_name funny@@{u} >actual &&
+ git -C clone rev-parse --symbolic-full-name funny@@{u} >actual &&
echo refs/remotes/origin/side >expect &&
test_cmp expect actual
'
test_expect_success 'refs/heads/my-side@{upstream} does not resolve to my-side{upstream}' '
- test_must_fail full_name refs/heads/my-side@{upstream}
+ test_must_fail git -C clone rev-parse --symbolic-full-name refs/heads/my-side@{upstream}
'
test_expect_success 'my-side@{u} resolves to correct commit' '
git checkout side &&
test_commit 5 &&
(cd clone && git fetch) &&
- test 2 = "$(commit_subject my-side)" &&
- test 5 = "$(commit_subject my-side@{u})"
+ echo 2 >expect &&
+ commit_subject my-side >actual &&
+ test_cmp expect actual &&
+ echo 5 >expect &&
+ commit_subject my-side@{u} >actual
'
test_expect_success 'not-tracking@{u} fails' '
- test_must_fail full_name non-tracking@{u} &&
+ test_must_fail git -C clone rev-parse --symbolic-full-name non-tracking@{u} &&
(cd clone && git checkout --no-track -b non-tracking) &&
- test_must_fail full_name non-tracking@{u}
+ test_must_fail git -C clone rev-parse --symbolic-full-name non-tracking@{u}
'
test_expect_success '<branch>@{u}@{1} resolves correctly' '
test_commit 6 &&
(cd clone && git fetch) &&
- test 5 = $(commit_subject my-side@{u}@{1}) &&
- test 5 = $(commit_subject my-side@{U}@{1})
+ echo 5 >expect &&
+ commit_subject my-side@{u}@{1} >actual &&
+ test_cmp expect actual &&
+ commit_subject my-side@{U}@{1} >actual &&
+ test_cmp expect actual
'
test_expect_success '@{u} without specifying branch fails on a detached HEAD' '
@@ -129,7 +137,7 @@ test_expect_success 'merge my-side@{u} records the correct name' '
git branch -t new my-side@{u} &&
git merge -s ours new@{u} &&
git show -s --pretty=tformat:%s >actual &&
- echo "Merge remote-tracking branch ${sq}origin/side${sq}" >expect &&
+ echo "Merge remote-tracking branch ${SQ}origin/side${SQ}" >expect &&
test_cmp expect actual
)
'
@@ -151,12 +159,14 @@ test_expect_success 'checkout other@{u}' '
'
test_expect_success 'branch@{u} works when tracking a local branch' '
- test refs/heads/master = "$(full_name local-master@{u})"
+ echo refs/heads/master >expect &&
+ git -C clone rev-parse --symbolic-full-name local-master@{u} >actual &&
+ test_cmp expect actual
'
test_expect_success 'branch@{u} error message when no upstream' '
cat >expect <<-EOF &&
- fatal: no upstream configured for branch ${sq}non-tracking${sq}
+ fatal: no upstream configured for branch ${SQ}non-tracking${SQ}
EOF
error_message non-tracking@{u} &&
test_i18ncmp expect error
@@ -164,7 +174,7 @@ test_expect_success 'branch@{u} error message when no upstream' '
test_expect_success '@{u} error message when no upstream' '
cat >expect <<-EOF &&
- fatal: no upstream configured for branch ${sq}master${sq}
+ fatal: no upstream configured for branch ${SQ}master${SQ}
EOF
test_must_fail git rev-parse --verify @{u} 2>actual &&
test_i18ncmp expect actual
@@ -172,7 +182,7 @@ test_expect_success '@{u} error message when no upstream' '
test_expect_success 'branch@{u} error message with misspelt branch' '
cat >expect <<-EOF &&
- fatal: no such branch: ${sq}no-such-branch${sq}
+ fatal: no such branch: ${SQ}no-such-branch${SQ}
EOF
error_message no-such-branch@{u} &&
test_i18ncmp expect error
@@ -189,7 +199,7 @@ test_expect_success '@{u} error message when not on a branch' '
test_expect_success 'branch@{u} error message if upstream branch not fetched' '
cat >expect <<-EOF &&
- fatal: upstream branch ${sq}refs/heads/side${sq} not stored as a remote-tracking branch
+ fatal: upstream branch ${SQ}refs/heads/side${SQ} not stored as a remote-tracking branch
EOF
error_message bad-upstream@{u} &&
test_i18ncmp expect error
@@ -205,35 +215,37 @@ test_expect_success 'pull works when tracking a local branch' '
# makes sense if the previous one succeeded
test_expect_success '@{u} works when tracking a local branch' '
- test refs/heads/master = "$(full_name @{u})"
+ echo refs/heads/master >expect &&
+ git -C clone rev-parse --symbolic-full-name @{u} >actual &&
+ test_cmp expect actual
'
-commit=$(git rev-parse HEAD)
-cat >expect <<EOF
-commit $commit
-Reflog: master@{0} (C O Mitter <committer@example.com>)
-Reflog message: branch: Created from HEAD
-Author: A U Thor <author@example.com>
-Date: Thu Apr 7 15:15:13 2005 -0700
-
- 3
-EOF
test_expect_success 'log -g other@{u}' '
+ commit=$(git rev-parse HEAD) &&
+ cat >expect <<-EOF &&
+ commit $commit
+ Reflog: master@{0} (C O Mitter <committer@example.com>)
+ Reflog message: branch: Created from HEAD
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:15:13 2005 -0700
+
+ 3
+ EOF
git log -1 -g other@{u} >actual &&
test_cmp expect actual
'
-cat >expect <<EOF
-commit $commit
-Reflog: master@{Thu Apr 7 15:17:13 2005 -0700} (C O Mitter <committer@example.com>)
-Reflog message: branch: Created from HEAD
-Author: A U Thor <author@example.com>
-Date: Thu Apr 7 15:15:13 2005 -0700
-
- 3
-EOF
-
test_expect_success 'log -g other@{u}@{now}' '
+ commit=$(git rev-parse HEAD) &&
+ cat >expect <<-EOF &&
+ commit $commit
+ Reflog: master@{Thu Apr 7 15:17:13 2005 -0700} (C O Mitter <committer@example.com>)
+ Reflog message: branch: Created from HEAD
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:15:13 2005 -0700
+
+ 3
+ EOF
git log -1 -g other@{u}@{now} >actual &&
test_cmp expect actual
'
diff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh
index e4d5b56..18fa6cf 100755
--- a/t/t1512-rev-parse-disambiguation.sh
+++ b/t/t1512-rev-parse-disambiguation.sh
@@ -282,7 +282,7 @@ test_expect_success 'rev-parse --disambiguate' '
# commits created by commit-tree in earlier tests share a
# different prefix.
git rev-parse --disambiguate=000000000 >actual &&
- test $(wc -l <actual) = 16 &&
+ test_line_count = 16 actual &&
test "$(sed -e "s/^\(.........\).*/\1/" actual | sort -u)" = 000000000
'
@@ -339,7 +339,7 @@ test_expect_success C_LOCALE_OUTPUT 'ambiguity hints' '
test_expect_success C_LOCALE_OUTPUT 'ambiguity hints respect type' '
test_must_fail git rev-parse 000000000^{commit} 2>stderr &&
grep ^hint: stderr >hints &&
- # 5 commits, 1 tag (which is a commitish), plus intro line
+ # 5 commits, 1 tag (which is a committish), plus intro line
test_line_count = 7 hints
'
@@ -388,4 +388,14 @@ test_expect_success C_LOCALE_OUTPUT 'ambiguous commits are printed by type first
done
'
+test_expect_success 'cat-file --batch and --batch-check show ambiguous' '
+ echo "0000 ambiguous" >expect &&
+ echo 0000 | git cat-file --batch-check >actual 2>err &&
+ test_cmp expect actual &&
+ test_i18ngrep hint: err &&
+ echo 0000 | git cat-file --batch >actual 2>err &&
+ test_cmp expect actual &&
+ test_i18ngrep hint: err
+'
+
test_done
diff --git a/t/t1600-index.sh b/t/t1600-index.sh
index 42962ed..b7c31aa 100755
--- a/t/t1600-index.sh
+++ b/t/t1600-index.sh
@@ -59,17 +59,42 @@ test_expect_success 'out of bounds index.version issues warning' '
)
'
-test_expect_success 'GIT_INDEX_VERSION takes precedence over config' '
+test_index_version () {
+ INDEX_VERSION_CONFIG=$1 &&
+ FEATURE_MANY_FILES=$2 &&
+ ENV_VAR_VERSION=$3
+ EXPECTED_OUTPUT_VERSION=$4 &&
(
rm -f .git/index &&
- GIT_INDEX_VERSION=4 &&
- export GIT_INDEX_VERSION &&
- git config --add index.version 2 &&
+ rm -f .git/config &&
+ if test "$INDEX_VERSION_CONFIG" -ne 0
+ then
+ git config --add index.version $INDEX_VERSION_CONFIG
+ fi &&
+ git config --add feature.manyFiles $FEATURE_MANY_FILES
+ if test "$ENV_VAR_VERSION" -ne 0
+ then
+ GIT_INDEX_VERSION=$ENV_VAR_VERSION &&
+ export GIT_INDEX_VERSION
+ else
+ unset GIT_INDEX_VERSION
+ fi &&
git add a 2>&1 &&
- echo 4 >expect &&
+ echo $EXPECTED_OUTPUT_VERSION >expect &&
test-tool index-version <.git/index >actual &&
test_cmp expect actual
)
+}
+
+test_expect_success 'index version config precedence' '
+ test_index_version 0 false 0 2 &&
+ test_index_version 2 false 0 2 &&
+ test_index_version 3 false 0 2 &&
+ test_index_version 4 false 0 4 &&
+ test_index_version 2 false 4 4 &&
+ test_index_version 2 true 0 2 &&
+ test_index_version 0 true 0 4 &&
+ test_index_version 0 true 2 2
'
test_done
diff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh
index 4667e1a..12a5568 100755
--- a/t/t1700-split-index.sh
+++ b/t/t1700-split-index.sh
@@ -20,6 +20,22 @@ create_non_racy_file () {
test-tool chmtime =-5 "$1"
}
+test_expect_success 'setup' '
+ test_oid_cache <<-EOF
+ own_v3 sha1:8299b0bcd1ac364e5f1d7768efb62fa2da79a339
+ own_v3 sha256:38a6d2925e3eceec33ad7b34cbff4e0086caa0daf28f31e51f5bd94b4a7af86b
+
+ base_v3 sha1:39d890139ee5356c7ef572216cebcd27aa41f9df
+ base_v3 sha256:c9baeadf905112bf6c17aefbd7d02267afd70ded613c30cafed2d40cb506e1ed
+
+ own_v4 sha1:432ef4b63f32193984f339431fd50ca796493569
+ own_v4 sha256:6738ac6319c25b694afa7bcc313deb182d1a59b68bf7a47b4296de83478c0420
+
+ base_v4 sha1:508851a7f0dfa8691e9f69c7f055865389012491
+ base_v4 sha256:3177d4adfdd4b6904f7e921d91d715a471c0dde7cf6a4bba574927f02b699508
+ EOF
+'
+
test_expect_success 'enable split index' '
git config splitIndex.maxPercentChange 100 &&
git update-index --split-index &&
@@ -29,11 +45,11 @@ test_expect_success 'enable split index' '
# NEEDSWORK: Stop hard-coding checksums.
if test "$indexversion" = "4"
then
- own=432ef4b63f32193984f339431fd50ca796493569
- base=508851a7f0dfa8691e9f69c7f055865389012491
+ own=$(test_oid own_v4)
+ base=$(test_oid base_v4)
else
- own=8299b0bcd1ac364e5f1d7768efb62fa2da79a339
- base=39d890139ee5356c7ef572216cebcd27aa41f9df
+ own=$(test_oid own_v3)
+ base=$(test_oid base_v3)
fi &&
cat >expect <<-EOF &&
@@ -99,17 +115,18 @@ test_expect_success 'enable split index again, "one" now belongs to base index"'
test_expect_success 'modify original file, base index untouched' '
echo modified | create_non_racy_file one &&
+ file1_blob=$(git hash-object one) &&
git update-index one &&
git ls-files --stage >ls-files.actual &&
cat >ls-files.expect <<-EOF &&
- 100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0 one
+ 100644 $file1_blob 0 one
EOF
test_cmp ls-files.expect ls-files.actual &&
test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
q_to_tab >expect <<-EOF &&
$BASE
- 100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
+ 100644 $file1_blob 0Q
replacements: 0
deletions:
EOF
@@ -121,7 +138,7 @@ test_expect_success 'add another file, which stays index' '
git update-index --add two &&
git ls-files --stage >ls-files.actual &&
cat >ls-files.expect <<-EOF &&
- 100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0 one
+ 100644 $file1_blob 0 one
100644 $EMPTY_BLOB 0 two
EOF
test_cmp ls-files.expect ls-files.actual &&
@@ -129,7 +146,7 @@ test_expect_success 'add another file, which stays index' '
test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
q_to_tab >expect <<-EOF &&
$BASE
- 100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
+ 100644 $file1_blob 0Q
100644 $EMPTY_BLOB 0 two
replacements: 0
deletions:
@@ -141,14 +158,14 @@ test_expect_success 'remove file not in base index' '
git update-index --force-remove two &&
git ls-files --stage >ls-files.actual &&
cat >ls-files.expect <<-EOF &&
- 100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0 one
+ 100644 $file1_blob 0 one
EOF
test_cmp ls-files.expect ls-files.actual &&
test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
q_to_tab >expect <<-EOF &&
$BASE
- 100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
+ 100644 $file1_blob 0Q
replacements: 0
deletions:
EOF
@@ -237,9 +254,9 @@ test_expect_success 'set core.splitIndex config variable to true' '
git update-index --add three &&
git ls-files --stage >ls-files.actual &&
cat >ls-files.expect <<-EOF &&
- 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one
- 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 three
- 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 two
+ 100644 $EMPTY_BLOB 0 one
+ 100644 $EMPTY_BLOB 0 three
+ 100644 $EMPTY_BLOB 0 two
EOF
test_cmp ls-files.expect ls-files.actual &&
BASE=$(test-tool dump-split-index .git/index | grep "^base") &&
@@ -257,8 +274,8 @@ test_expect_success 'set core.splitIndex config variable to false' '
git update-index --force-remove three &&
git ls-files --stage >ls-files.actual &&
cat >ls-files.expect <<-EOF &&
- 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one
- 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 two
+ 100644 $EMPTY_BLOB 0 one
+ 100644 $EMPTY_BLOB 0 two
EOF
test_cmp ls-files.expect ls-files.actual &&
test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
@@ -285,7 +302,7 @@ test_expect_success 'set core.splitIndex config variable back to true' '
test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
cat >expect <<-EOF &&
$BASE
- 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 four
+ 100644 $EMPTY_BLOB 0 four
replacements:
deletions:
EOF
@@ -309,7 +326,7 @@ test_expect_success 'check behavior with splitIndex.maxPercentChange unset' '
test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
cat >expect <<-EOF &&
$BASE
- 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 six
+ 100644 $EMPTY_BLOB 0 six
replacements:
deletions:
EOF
@@ -472,4 +489,22 @@ test_expect_success 'writing split index with null sha1 does not write cache tre
test_line_count = 0 cache-tree.out
'
+test_expect_success 'do not refresh null base index' '
+ test_create_repo merge &&
+ (
+ cd merge &&
+ test_commit initial &&
+ git checkout -b side-branch &&
+ test_commit extra &&
+ git checkout master &&
+ git update-index --split-index &&
+ test_commit more &&
+ # must not write a new shareindex, or we wont catch the problem
+ git -c splitIndex.maxPercentChange=100 merge --no-edit side-branch 2>err &&
+ # i.e. do not expect warnings like
+ # could not freshen shared index .../shareindex.00000...
+ test_must_be_empty err
+ )
+'
+
test_done
diff --git a/t/t2014-switch.sh b/t/t2014-checkout-switch.sh
index ccfb147..ccfb147 100755
--- a/t/t2014-switch.sh
+++ b/t/t2014-checkout-switch.sh
diff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh
index 2131fb2..2158315 100755
--- a/t/t2018-checkout-branch.sh
+++ b/t/t2018-checkout-branch.sh
@@ -1,50 +1,76 @@
#!/bin/sh
-test_description='checkout '
+test_description='checkout'
. ./test-lib.sh
-# Arguments: <branch> <sha> [<checkout options>]
+# Arguments: [!] <branch> <oid> [<checkout options>]
#
# Runs "git checkout" to switch to <branch>, testing that
#
# 1) we are on the specified branch, <branch>;
-# 2) HEAD is <sha>; if <sha> is not specified, the old HEAD is used.
+# 2) HEAD is <oid>; if <oid> is not specified, the old HEAD is used.
#
# If <checkout options> is not specified, "git checkout" is run with -b.
-do_checkout() {
+#
+# If the first argument is `!`, "git checkout" is expected to fail when
+# it is run.
+do_checkout () {
+ should_fail= &&
+ if test "x$1" = "x!"
+ then
+ should_fail=yes &&
+ shift
+ fi &&
exp_branch=$1 &&
exp_ref="refs/heads/$exp_branch" &&
- # if <sha> is not specified, use HEAD.
- exp_sha=${2:-$(git rev-parse --verify HEAD)} &&
+ # if <oid> is not specified, use HEAD.
+ exp_oid=${2:-$(git rev-parse --verify HEAD)} &&
# default options for git checkout: -b
- if [ -z "$3" ]; then
+ if test -z "$3"
+ then
opts="-b"
else
opts="$3"
fi
- git checkout $opts $exp_branch $exp_sha &&
+ if test -n "$should_fail"
+ then
+ test_must_fail git checkout $opts $exp_branch $exp_oid
+ else
+ git checkout $opts $exp_branch $exp_oid &&
+ echo "$exp_ref" >ref.expect &&
+ git rev-parse --symbolic-full-name HEAD >ref.actual &&
+ test_cmp ref.expect ref.actual &&
+ echo "$exp_oid" >oid.expect &&
+ git rev-parse --verify HEAD >oid.actual &&
+ test_cmp oid.expect oid.actual
+ fi
+}
- test $exp_ref = $(git rev-parse --symbolic-full-name HEAD) &&
- test $exp_sha = $(git rev-parse --verify HEAD)
+test_dirty_unmergeable () {
+ test_expect_code 1 git diff --exit-code
}
-test_dirty_unmergeable() {
- ! git diff --exit-code >/dev/null
+test_dirty_unmergeable_discards_changes () {
+ git diff --exit-code
}
-setup_dirty_unmergeable() {
+setup_dirty_unmergeable () {
echo >>file1 change2
}
-test_dirty_mergeable() {
- ! git diff --cached --exit-code >/dev/null
+test_dirty_mergeable () {
+ test_expect_code 1 git diff --cached --exit-code
}
-setup_dirty_mergeable() {
+test_dirty_mergeable_discards_changes () {
+ git diff --cached --exit-code
+}
+
+setup_dirty_mergeable () {
echo >file2 file2 &&
git add file2
}
@@ -60,38 +86,47 @@ test_expect_success 'setup' '
'
test_expect_success 'checkout -b to a new branch, set to HEAD' '
+ test_when_finished "
+ git checkout branch1 &&
+ test_might_fail git branch -D branch2" &&
do_checkout branch2
'
-test_expect_success 'checkout -b to a new branch, set to an explicit ref' '
- git checkout branch1 &&
- git branch -D branch2 &&
+test_expect_success 'checkout -b to a merge base' '
+ test_when_finished "
+ git checkout branch1 &&
+ test_might_fail git branch -D branch2" &&
+ git checkout -b branch2 branch1...
+'
+test_expect_success 'checkout -b to a new branch, set to an explicit ref' '
+ test_when_finished "
+ git checkout branch1 &&
+ test_might_fail git branch -D branch2" &&
do_checkout branch2 $HEAD1
'
test_expect_success 'checkout -b to a new branch with unmergeable changes fails' '
- git checkout branch1 &&
-
- # clean up from previous test
- git branch -D branch2 &&
-
setup_dirty_unmergeable &&
- test_must_fail do_checkout branch2 $HEAD1 &&
+ do_checkout ! branch2 $HEAD1 &&
test_dirty_unmergeable
'
test_expect_success 'checkout -f -b to a new branch with unmergeable changes discards changes' '
+ test_when_finished "
+ git checkout branch1 &&
+ test_might_fail git branch -D branch2" &&
+
# still dirty and on branch1
do_checkout branch2 $HEAD1 "-f -b" &&
- test_must_fail test_dirty_unmergeable
+ test_dirty_unmergeable_discards_changes
'
test_expect_success 'checkout -b to a new branch preserves mergeable changes' '
- git checkout branch1 &&
-
- # clean up from previous test
- git branch -D branch2 &&
+ test_when_finished "
+ git reset --hard &&
+ git checkout branch1 &&
+ test_might_fail git branch -D branch2" &&
setup_dirty_mergeable &&
do_checkout branch2 $HEAD1 &&
@@ -99,27 +134,18 @@ test_expect_success 'checkout -b to a new branch preserves mergeable changes' '
'
test_expect_success 'checkout -f -b to a new branch with mergeable changes discards changes' '
- # clean up from previous test
- git reset --hard &&
-
- git checkout branch1 &&
-
- # clean up from previous test
- git branch -D branch2 &&
-
+ test_when_finished git reset --hard HEAD &&
setup_dirty_mergeable &&
do_checkout branch2 $HEAD1 "-f -b" &&
- test_must_fail test_dirty_mergeable
+ test_dirty_mergeable_discards_changes
'
test_expect_success 'checkout -b to an existing branch fails' '
- git reset --hard HEAD &&
-
- test_must_fail do_checkout branch2 $HEAD2
+ test_when_finished git reset --hard HEAD &&
+ do_checkout ! branch2 $HEAD2
'
test_expect_success 'checkout -b to @{-1} fails with the right branch name' '
- git reset --hard HEAD &&
git checkout branch1 &&
git checkout branch2 &&
echo >expect "fatal: A branch named '\''branch1'\'' already exists." &&
@@ -133,8 +159,15 @@ test_expect_success 'checkout -B to an existing branch resets branch to HEAD' '
do_checkout branch2 "" -B
'
+test_expect_success 'checkout -B to a merge base' '
+ git checkout branch1 &&
+
+ git checkout -B branch2 branch1...
+'
+
test_expect_success 'checkout -B to an existing branch from detached HEAD resets branch to HEAD' '
- git checkout $(git rev-parse --verify HEAD) &&
+ head=$(git rev-parse --verify HEAD) &&
+ git checkout "$head" &&
do_checkout branch2 "" -B
'
@@ -149,17 +182,18 @@ test_expect_success 'checkout -B to an existing branch with unmergeable changes
git checkout branch1 &&
setup_dirty_unmergeable &&
- test_must_fail do_checkout branch2 $HEAD1 -B &&
+ do_checkout ! branch2 $HEAD1 -B &&
test_dirty_unmergeable
'
test_expect_success 'checkout -f -B to an existing branch with unmergeable changes discards changes' '
# still dirty and on branch1
do_checkout branch2 $HEAD1 "-f -B" &&
- test_must_fail test_dirty_unmergeable
+ test_dirty_unmergeable_discards_changes
'
test_expect_success 'checkout -B to an existing branch preserves mergeable changes' '
+ test_when_finished git reset --hard &&
git checkout branch1 &&
setup_dirty_mergeable &&
@@ -168,14 +202,11 @@ test_expect_success 'checkout -B to an existing branch preserves mergeable chang
'
test_expect_success 'checkout -f -B to an existing branch with mergeable changes discards changes' '
- # clean up from previous test
- git reset --hard &&
-
git checkout branch1 &&
setup_dirty_mergeable &&
do_checkout branch2 $HEAD1 "-f -B" &&
- test_must_fail test_dirty_mergeable
+ test_dirty_mergeable_discards_changes
'
test_expect_success 'checkout -b <describe>' '
@@ -198,4 +229,35 @@ test_expect_success 'checkout -B to the current branch works' '
test_dirty_mergeable
'
+test_expect_success 'checkout -b after clone --no-checkout does a checkout of HEAD' '
+ git init src &&
+ test_commit -C src a &&
+ rev="$(git -C src rev-parse HEAD)" &&
+ git clone --no-checkout src dest &&
+ git -C dest checkout "$rev" -b branch &&
+ test_path_is_file dest/a.t
+'
+
+test_expect_success 'checkout -b to a new branch preserves mergeable changes despite sparse-checkout' '
+ test_when_finished "
+ git reset --hard &&
+ git checkout branch1-scratch &&
+ test_might_fail git branch -D branch3 &&
+ git config core.sparseCheckout false &&
+ rm .git/info/sparse-checkout" &&
+
+ test_commit file2 &&
+
+ echo stuff >>file1 &&
+ echo file2 >.git/info/sparse-checkout &&
+ git config core.sparseCheckout true &&
+
+ CURHEAD=$(git rev-parse HEAD) &&
+ do_checkout branch3 $CURHEAD &&
+
+ echo file1 >expect &&
+ git diff --name-only >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh
index 1fa6706..b748db9 100755
--- a/t/t2020-checkout-detach.sh
+++ b/t/t2020-checkout-detach.sh
@@ -195,16 +195,22 @@ test_expect_success 'describe_detached_head prints no SHA-1 ellipsis when not as
# The first detach operation is more chatty than the following ones.
cat >1st_detach <<-EOF &&
- Note: checking out 'HEAD^'.
+ Note: switching to 'HEAD^'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
- state without impacting any branches by performing another checkout.
+ state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
- do so (now or later) by using -b with the checkout command again. Example:
+ do so (now or later) by using -c with the switch command. Example:
- git checkout -b <new-branch-name>
+ git switch -c <new-branch-name>
+
+ Or undo this operation with:
+
+ git switch -
+
+ Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at \$commit three
EOF
@@ -271,16 +277,22 @@ test_expect_success 'describe_detached_head does print SHA-1 ellipsis when asked
# The first detach operation is more chatty than the following ones.
cat >1st_detach <<-EOF &&
- Note: checking out 'HEAD^'.
+ Note: switching to 'HEAD^'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
- state without impacting any branches by performing another checkout.
+ state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
- do so (now or later) by using -b with the checkout command again. Example:
+ do so (now or later) by using -c with the switch command. Example:
+
+ git switch -c <new-branch-name>
+
+ Or undo this operation with:
+
+ git switch -
- git checkout -b <new-branch-name>
+ Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at \$commit... three
EOF
diff --git a/t/t2022-checkout-paths.sh b/t/t2022-checkout-paths.sh
index fc3eb43..6844afa 100755
--- a/t/t2022-checkout-paths.sh
+++ b/t/t2022-checkout-paths.sh
@@ -78,4 +78,15 @@ test_expect_success 'do not touch files that are already up-to-date' '
test_cmp expect actual
'
+test_expect_success 'checkout HEAD adds deleted intent-to-add file back to index' '
+ echo "nonempty" >nonempty &&
+ >empty &&
+ git add nonempty empty &&
+ git commit -m "create files to be deleted" &&
+ git rm --cached nonempty empty &&
+ git add -N nonempty empty &&
+ git checkout HEAD nonempty empty &&
+ git diff --cached --exit-code
+'
+
test_done
diff --git a/t/t2023-checkout-m.sh b/t/t2023-checkout-m.sh
index 7e18985..fca3f85 100755
--- a/t/t2023-checkout-m.sh
+++ b/t/t2023-checkout-m.sh
@@ -46,4 +46,28 @@ test_expect_success '-m restores 3-way conflicted+resolved file' '
test_cmp both.txt.conflicted.cleaned both.txt.cleaned
'
+test_expect_success 'force checkout a conflict file creates stage zero entry' '
+ git init co-force &&
+ (
+ cd co-force &&
+ echo a >a &&
+ git add a &&
+ git commit -ama &&
+ A_OBJ=$(git rev-parse :a) &&
+ git branch topic &&
+ echo b >a &&
+ git commit -amb &&
+ B_OBJ=$(git rev-parse :a) &&
+ git checkout topic &&
+ echo c >a &&
+ C_OBJ=$(git hash-object a) &&
+ git checkout -m master &&
+ test_cmp_rev :1:a $A_OBJ &&
+ test_cmp_rev :2:a $B_OBJ &&
+ test_cmp_rev :3:a $C_OBJ &&
+ git checkout -f topic &&
+ test_cmp_rev :0:a $A_OBJ
+ )
+'
+
test_done
diff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh
index 69b6774..accfa9a 100755
--- a/t/t2024-checkout-dwim.sh
+++ b/t/t2024-checkout-dwim.sh
@@ -37,7 +37,9 @@ test_expect_success 'setup' '
git checkout -b foo &&
test_commit a_foo &&
git checkout -b bar &&
- test_commit a_bar
+ test_commit a_bar &&
+ git checkout -b ambiguous_branch_and_file &&
+ test_commit a_ambiguous_branch_and_file
) &&
git init repo_b &&
(
@@ -46,7 +48,9 @@ test_expect_success 'setup' '
git checkout -b foo &&
test_commit b_foo &&
git checkout -b baz &&
- test_commit b_baz
+ test_commit b_baz &&
+ git checkout -b ambiguous_branch_and_file &&
+ test_commit b_ambiguous_branch_and_file
) &&
git remote add repo_a repo_a &&
git remote add repo_b repo_b &&
@@ -75,6 +79,26 @@ test_expect_success 'checkout of branch from multiple remotes fails #1' '
test_branch master
'
+test_expect_success 'when arg matches multiple remotes, do not fallback to interpreting as pathspec' '
+ # create a file with name matching remote branch name
+ git checkout -b t_ambiguous_branch_and_file &&
+ >ambiguous_branch_and_file &&
+ git add ambiguous_branch_and_file &&
+ git commit -m "ambiguous_branch_and_file" &&
+
+ # modify file to verify that it will not be touched by checkout
+ test_when_finished "git checkout -- ambiguous_branch_and_file" &&
+ echo "file contents" >ambiguous_branch_and_file &&
+ cp ambiguous_branch_and_file expect &&
+
+ test_must_fail git checkout ambiguous_branch_and_file 2>err &&
+
+ test_i18ngrep "matched multiple (2) remote tracking branches" err &&
+
+ # file must not be altered
+ test_cmp expect ambiguous_branch_and_file
+'
+
test_expect_success 'checkout of branch from multiple remotes fails with advice' '
git checkout -B master &&
test_might_fail git branch -D foo &&
@@ -278,4 +302,35 @@ test_expect_success 'loosely defined local base branch is reported correctly' '
test_cmp expect actual
'
+test_expect_success 'reject when arg could be part of dwim branch' '
+ git remote add foo file://non-existent-place &&
+ git update-ref refs/remotes/foo/dwim-arg HEAD &&
+ echo foo >dwim-arg &&
+ git add dwim-arg &&
+ echo bar >dwim-arg &&
+ test_must_fail git checkout dwim-arg &&
+ test_must_fail git rev-parse refs/heads/dwim-arg -- &&
+ grep bar dwim-arg
+'
+
+test_expect_success 'disambiguate dwim branch and checkout path (1)' '
+ git update-ref refs/remotes/foo/dwim-arg1 HEAD &&
+ echo foo >dwim-arg1 &&
+ git add dwim-arg1 &&
+ echo bar >dwim-arg1 &&
+ git checkout -- dwim-arg1 &&
+ test_must_fail git rev-parse refs/heads/dwim-arg1 -- &&
+ grep foo dwim-arg1
+'
+
+test_expect_success 'disambiguate dwim branch and checkout path (2)' '
+ git update-ref refs/remotes/foo/dwim-arg2 HEAD &&
+ echo foo >dwim-arg2 &&
+ git add dwim-arg2 &&
+ echo bar >dwim-arg2 &&
+ git checkout dwim-arg2 -- &&
+ git rev-parse refs/heads/dwim-arg2 -- &&
+ grep bar dwim-arg2
+'
+
test_done
diff --git a/t/t2025-checkout-no-overlay.sh b/t/t2025-checkout-no-overlay.sh
new file mode 100755
index 0000000..76330cb
--- /dev/null
+++ b/t/t2025-checkout-no-overlay.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+test_description='checkout --no-overlay <tree-ish> -- <pathspec>'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ git commit --allow-empty -m "initial"
+'
+
+test_expect_success 'checkout --no-overlay deletes files not in <tree-ish>' '
+ >file &&
+ mkdir dir &&
+ >dir/file1 &&
+ git add file dir/file1 &&
+ git checkout --no-overlay HEAD -- file &&
+ test_path_is_missing file &&
+ test_path_is_file dir/file1
+'
+
+test_expect_success 'checkout --no-overlay removing last file from directory' '
+ git checkout --no-overlay HEAD -- dir/file1 &&
+ test_path_is_missing dir
+'
+
+test_expect_success 'checkout -p --overlay is disallowed' '
+ test_must_fail git checkout -p --overlay HEAD 2>actual &&
+ test_i18ngrep "fatal: -p and --overlay are mutually exclusive" actual
+'
+
+test_expect_success '--no-overlay --theirs with D/F conflict deletes file' '
+ test_commit file1 file1 &&
+ test_commit file2 file2 &&
+ git rm --cached file1 &&
+ echo 1234 >file1 &&
+ F1=$(git rev-parse HEAD:file1) &&
+ F2=$(git rev-parse HEAD:file2) &&
+ {
+ echo "100644 $F1 1 file1" &&
+ echo "100644 $F2 2 file1"
+ } | git update-index --index-info &&
+ test_path_is_file file1 &&
+ git checkout --theirs --no-overlay -- file1 &&
+ test_path_is_missing file1
+'
+
+test_done
diff --git a/t/t2026-checkout-pathspec-file.sh b/t/t2026-checkout-pathspec-file.sh
new file mode 100755
index 0000000..43d31d7
--- /dev/null
+++ b/t/t2026-checkout-pathspec-file.sh
@@ -0,0 +1,163 @@
+#!/bin/sh
+
+test_description='checkout --pathspec-from-file'
+
+. ./test-lib.sh
+
+test_tick
+
+test_expect_success setup '
+ test_commit file0 &&
+
+ echo 1 >fileA.t &&
+ echo 1 >fileB.t &&
+ echo 1 >fileC.t &&
+ echo 1 >fileD.t &&
+ git add fileA.t fileB.t fileC.t fileD.t &&
+ git commit -m "files 1" &&
+
+ echo 2 >fileA.t &&
+ echo 2 >fileB.t &&
+ echo 2 >fileC.t &&
+ echo 2 >fileD.t &&
+ git add fileA.t fileB.t fileC.t fileD.t &&
+ git commit -m "files 2" &&
+
+ git tag checkpoint
+'
+
+restore_checkpoint () {
+ git reset --hard checkpoint
+}
+
+verify_expect () {
+ git status --porcelain --untracked-files=no -- fileA.t fileB.t fileC.t fileD.t >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success '--pathspec-from-file from stdin' '
+ restore_checkpoint &&
+
+ echo fileA.t | git checkout --pathspec-from-file=- HEAD^1 &&
+
+ cat >expect <<-\EOF &&
+ M fileA.t
+ EOF
+ verify_expect
+'
+
+test_expect_success '--pathspec-from-file from file' '
+ restore_checkpoint &&
+
+ echo fileA.t >list &&
+ git checkout --pathspec-from-file=list HEAD^1 &&
+
+ cat >expect <<-\EOF &&
+ M fileA.t
+ EOF
+ verify_expect
+'
+
+test_expect_success 'NUL delimiters' '
+ restore_checkpoint &&
+
+ printf "fileA.t\0fileB.t\0" | git checkout --pathspec-from-file=- --pathspec-file-nul HEAD^1 &&
+
+ cat >expect <<-\EOF &&
+ M fileA.t
+ M fileB.t
+ EOF
+ verify_expect
+'
+
+test_expect_success 'LF delimiters' '
+ restore_checkpoint &&
+
+ printf "fileA.t\nfileB.t\n" | git checkout --pathspec-from-file=- HEAD^1 &&
+
+ cat >expect <<-\EOF &&
+ M fileA.t
+ M fileB.t
+ EOF
+ verify_expect
+'
+
+test_expect_success 'no trailing delimiter' '
+ restore_checkpoint &&
+
+ printf "fileA.t\nfileB.t" | git checkout --pathspec-from-file=- HEAD^1 &&
+
+ cat >expect <<-\EOF &&
+ M fileA.t
+ M fileB.t
+ EOF
+ verify_expect
+'
+
+test_expect_success 'CRLF delimiters' '
+ restore_checkpoint &&
+
+ printf "fileA.t\r\nfileB.t\r\n" | git checkout --pathspec-from-file=- HEAD^1 &&
+
+ cat >expect <<-\EOF &&
+ M fileA.t
+ M fileB.t
+ EOF
+ verify_expect
+'
+
+test_expect_success 'quotes' '
+ restore_checkpoint &&
+
+ cat >list <<-\EOF &&
+ "file\101.t"
+ EOF
+
+ git checkout --pathspec-from-file=list HEAD^1 &&
+
+ cat >expect <<-\EOF &&
+ M fileA.t
+ EOF
+ verify_expect
+'
+
+test_expect_success 'quotes not compatible with --pathspec-file-nul' '
+ restore_checkpoint &&
+
+ cat >list <<-\EOF &&
+ "file\101.t"
+ EOF
+
+ test_must_fail git checkout --pathspec-from-file=list --pathspec-file-nul HEAD^1
+'
+
+test_expect_success 'only touches what was listed' '
+ restore_checkpoint &&
+
+ printf "fileB.t\nfileC.t\n" | git checkout --pathspec-from-file=- HEAD^1 &&
+
+ cat >expect <<-\EOF &&
+ M fileB.t
+ M fileC.t
+ EOF
+ verify_expect
+'
+
+test_expect_success 'error conditions' '
+ restore_checkpoint &&
+ echo fileA.t >list &&
+
+ test_must_fail git checkout --pathspec-from-file=list --detach 2>err &&
+ test_i18ngre