summaryrefslogtreecommitdiff
path: root/t
diff options
context:
space:
mode:
Diffstat (limited to 't')
-rw-r--r--t/Makefile52
-rw-r--r--t/README78
-rwxr-xr-xt/chainlint.pl823
-rw-r--r--t/chainlint.sed399
-rw-r--r--t/chainlint/blank-line-before-esac.expect18
-rw-r--r--t/chainlint/blank-line-before-esac.test19
-rw-r--r--t/chainlint/block-comment.expect2
-rw-r--r--t/chainlint/block.expect15
-rw-r--r--t/chainlint/block.test15
-rw-r--r--t/chainlint/case-comment.expect3
-rw-r--r--t/chainlint/chain-break-background.expect9
-rw-r--r--t/chainlint/chain-break-background.test10
-rw-r--r--t/chainlint/chain-break-continue.expect12
-rw-r--r--t/chainlint/chain-break-continue.test13
-rw-r--r--t/chainlint/chain-break-false.expect9
-rw-r--r--t/chainlint/chain-break-false.test10
-rw-r--r--t/chainlint/chain-break-return-exit.expect19
-rw-r--r--t/chainlint/chain-break-return-exit.test23
-rw-r--r--t/chainlint/chain-break-status.expect9
-rw-r--r--t/chainlint/chain-break-status.test11
-rw-r--r--t/chainlint/chained-block.expect9
-rw-r--r--t/chainlint/chained-block.test11
-rw-r--r--t/chainlint/chained-subshell.expect10
-rw-r--r--t/chainlint/chained-subshell.test13
-rw-r--r--t/chainlint/close-subshell.expect3
-rw-r--r--t/chainlint/command-substitution-subsubshell.expect2
-rw-r--r--t/chainlint/command-substitution-subsubshell.test3
-rw-r--r--t/chainlint/comment.expect4
-rw-r--r--t/chainlint/complex-if-in-cuddled-loop.expect2
-rw-r--r--t/chainlint/double-here-doc.expect12
-rw-r--r--t/chainlint/double-here-doc.test12
-rw-r--r--t/chainlint/dqstring-line-splice.expect3
-rw-r--r--t/chainlint/dqstring-line-splice.test7
-rw-r--r--t/chainlint/dqstring-no-interpolate.expect11
-rw-r--r--t/chainlint/dqstring-no-interpolate.test15
-rw-r--r--t/chainlint/empty-here-doc.expect4
-rw-r--r--t/chainlint/empty-here-doc.test5
-rw-r--r--t/chainlint/exclamation.expect4
-rw-r--r--t/chainlint/exclamation.test8
-rw-r--r--t/chainlint/for-loop-abbreviated.expect5
-rw-r--r--t/chainlint/for-loop-abbreviated.test6
-rw-r--r--t/chainlint/for-loop.expect6
-rw-r--r--t/chainlint/function.expect11
-rw-r--r--t/chainlint/function.test13
-rw-r--r--t/chainlint/here-doc-close-subshell.expect4
-rw-r--r--t/chainlint/here-doc-indent-operator.expect11
-rw-r--r--t/chainlint/here-doc-indent-operator.test13
-rw-r--r--t/chainlint/here-doc-multi-line-command-subst.expect5
-rw-r--r--t/chainlint/here-doc-multi-line-string.expect5
-rw-r--r--t/chainlint/here-doc.expect24
-rw-r--r--t/chainlint/if-condition-split.expect7
-rw-r--r--t/chainlint/if-condition-split.test8
-rw-r--r--t/chainlint/if-in-loop.expect2
-rw-r--r--t/chainlint/if-in-loop.test2
-rw-r--r--t/chainlint/if-then-else.expect4
-rw-r--r--t/chainlint/incomplete-line.expect10
-rw-r--r--t/chainlint/inline-comment.expect4
-rw-r--r--t/chainlint/loop-detect-failure.expect15
-rw-r--r--t/chainlint/loop-detect-failure.test17
-rw-r--r--t/chainlint/loop-detect-status.expect18
-rw-r--r--t/chainlint/loop-detect-status.test19
-rw-r--r--t/chainlint/loop-in-if.expect2
-rw-r--r--t/chainlint/loop-upstream-pipe.expect10
-rw-r--r--t/chainlint/loop-upstream-pipe.test11
-rw-r--r--t/chainlint/multi-line-string.expect11
-rw-r--r--t/chainlint/nested-here-doc.expect27
-rw-r--r--t/chainlint/nested-loop-detect-failure.expect31
-rw-r--r--t/chainlint/nested-loop-detect-failure.test35
-rw-r--r--t/chainlint/nested-subshell-comment.expect2
-rw-r--r--t/chainlint/nested-subshell.expect2
-rw-r--r--t/chainlint/one-liner-for-loop.expect9
-rw-r--r--t/chainlint/one-liner-for-loop.test10
-rw-r--r--t/chainlint/return-loop.expect5
-rw-r--r--t/chainlint/return-loop.test6
-rw-r--r--t/chainlint/semicolon.expect2
-rw-r--r--t/chainlint/sqstring-in-sqstring.expect4
-rw-r--r--t/chainlint/sqstring-in-sqstring.test5
-rw-r--r--t/chainlint/subshell-here-doc.expect28
-rw-r--r--t/chainlint/t7900-subtree.expect17
-rw-r--r--t/chainlint/token-pasting.expect27
-rw-r--r--t/chainlint/token-pasting.test32
-rw-r--r--t/chainlint/while-loop.expect6
-rwxr-xr-xt/check-non-portable-shell.pl1
-rw-r--r--t/helper/test-bundle-uri.c95
-rw-r--r--t/helper/test-cache-tree.c65
-rw-r--r--t/helper/test-config.c2
-rw-r--r--t/helper/test-crontab.c23
-rw-r--r--t/helper/test-dump-cache-tree.c5
-rw-r--r--t/helper/test-dump-split-index.c1
-rw-r--r--t/helper/test-dump-untracked-cache.c4
-rw-r--r--t/helper/test-fake-ssh.c7
-rw-r--r--t/helper/test-fast-rebase.c6
-rw-r--r--t/helper/test-lazy-init-name-hash.c21
-rw-r--r--t/helper/test-mergesort.c40
-rw-r--r--t/helper/test-parse-options.c128
-rw-r--r--t/helper/test-path-utils.c3
-rw-r--r--t/helper/test-proc-receive.c2
-rw-r--r--t/helper/test-read-cache.c5
-rw-r--r--t/helper/test-ref-store.c4
-rw-r--r--t/helper/test-rot13-filter.c382
-rw-r--r--t/helper/test-run-command.c77
-rw-r--r--t/helper/test-scrap-cache-tree.c9
-rw-r--r--t/helper/test-serve-v2.c2
-rw-r--r--t/helper/test-sha1.c8
-rw-r--r--t/helper/test-submodule-config.c11
-rw-r--r--t/helper/test-submodule.c224
-rw-r--r--t/helper/test-tool-utils.h9
-rw-r--r--t/helper/test-tool.c13
-rw-r--r--t/helper/test-tool.h8
-rw-r--r--t/helper/test-trace2.c191
-rw-r--r--t/helper/test-userdiff.c2
-rw-r--r--t/helper/test-write-cache.c6
-rw-r--r--t/lib-bitmap.sh2
-rw-r--r--t/lib-httpd.sh8
-rw-r--r--t/lib-httpd/apache.conf21
-rw-r--r--t/lib-perl.sh19
-rw-r--r--t/lib-submodule-update.sh1
-rw-r--r--t/perf/README4
-rw-r--r--t/perf/lib-bitmap.sh31
-rwxr-xr-xt/perf/p0004-lazy-init-name-hash.sh2
-rwxr-xr-xt/perf/p0006-read-tree-checkout.sh10
-rwxr-xr-xt/perf/p0090-cache-tree.sh36
-rwxr-xr-xt/perf/p2000-sparse-operations.sh2
-rwxr-xr-xt/perf/p5310-pack-bitmaps.sh78
-rwxr-xr-xt/perf/p5311-pack-bitmaps-fetch.sh74
-rwxr-xr-xt/perf/p5312-pack-bitmaps-revs.sh35
-rwxr-xr-xt/perf/p5326-multi-pack-bitmaps.sh103
-rwxr-xr-xt/perf/p7102-reset.sh21
-rwxr-xr-xt/perf/p7527-builtin-fsmonitor.sh2
-rwxr-xr-xt/perf/p9210-scalar.sh39
-rw-r--r--t/perf/perf-lib.sh13
-rwxr-xr-xt/perf/run7
-rwxr-xr-xt/t0000-basic.sh72
-rwxr-xr-xt/t0002-gitfile.sh2
-rwxr-xr-xt/t0004-unwritable.sh2
-rwxr-xr-xt/t0012-help.sh14
-rwxr-xr-xt/t0013-sha1dc.sh6
-rwxr-xr-xt/t0021-conversion.sh71
-rw-r--r--t/t0021/rot13-filter.pl247
-rwxr-xr-xt/t0027-auto-crlf.sh8
-rwxr-xr-xt/t0032-reftable-unittest.sh1
-rwxr-xr-xt/t0033-safe-directory.sh10
-rwxr-xr-xt/t0035-safe-bare-repository.sh9
-rwxr-xr-xt/t0040-parse-options.sh265
-rwxr-xr-xt/t0050-filesystem.sh1
-rwxr-xr-xt/t0060-path-utils.sh2
-rwxr-xr-xt/t0061-run-command.sh15
-rwxr-xr-xt/t0068-for-each-repo.sh6
-rwxr-xr-xt/t0091-bugreport.sh48
-rwxr-xr-xt/t0092-diagnose.sh72
-rwxr-xr-xt/t0095-bloom.sh2
-rwxr-xr-xt/t0202-gettext-perl.sh22
-rwxr-xr-xt/t0203-gettext-setlocale-sanity.sh1
-rwxr-xr-xt/t0211-trace2-perf.sh95
-rw-r--r--t/t0211/scrub_perf.perl6
-rwxr-xr-xt/t0410-partial-clone.sh14
-rwxr-xr-xt/t0450-txt-doc-vs-help.sh172
-rw-r--r--t/t0450/txt-help-mismatches58
-rwxr-xr-xt/t1002-read-tree-m-u-2way.sh16
-rwxr-xr-xt/t1020-subdirectory.sh1
-rwxr-xr-xt/t1022-read-tree-partial-clone.sh2
-rwxr-xr-xt/t1050-large.sh6
-rwxr-xr-xt/t1060-object-corruption.sh7
-rwxr-xr-xt/t1091-sparse-checkout-builtin.sh3
-rwxr-xr-xt/t1092-sparse-checkout-compatibility.sh216
-rwxr-xr-xt/t1300-config.sh6
-rwxr-xr-xt/t1304-default-acl.sh4
-rwxr-xr-xt/t1401-symbolic-ref.sh24
-rwxr-xr-xt/t1405-main-ref-store.sh1
-rwxr-xr-xt/t1407-worktree-ref-store.sh1
-rwxr-xr-xt/t1418-reflog-exists.sh1
-rwxr-xr-xt/t1450-fsck.sh62
-rwxr-xr-xt/t1500-rev-parse.sh4
-rwxr-xr-xt/t1502-rev-parse-parseopt.sh7
-rwxr-xr-xt/t1503-rev-parse-verify.sh2
-rwxr-xr-xt/t1701-racy-split-index.sh1
-rwxr-xr-xt/t1800-hook.sh28
-rwxr-xr-xt/t2006-checkout-index-basic.sh1
-rwxr-xr-xt/t2020-checkout-detach.sh1
-rwxr-xr-xt/t2023-checkout-m.sh1
-rwxr-xr-xt/t2080-parallel-checkout-basics.sh10
-rwxr-xr-xt/t2082-parallel-checkout-attributes.sh7
-rwxr-xr-xt/t2205-add-worktree-config.sh1
-rwxr-xr-xt/t2400-worktree-add.sh2
-rwxr-xr-xt/t2403-worktree-move.sh8
-rwxr-xr-xt/t2405-worktree-submodule.sh1
-rwxr-xr-xt/t2407-worktree-heads.sh4
-rwxr-xr-xt/t3001-ls-files-others-exclude.sh4
-rwxr-xr-xt/t3012-ls-files-dedup.sh1
-rwxr-xr-xt/t3070-wildmatch.sh5
-rwxr-xr-xt/t3200-branch.sh55
-rwxr-xr-xt/t3202-show-branch.sh46
-rwxr-xr-xt/t3204-branch-name-interpretation.sh24
-rwxr-xr-xt/t3206-range-diff.sh15
-rwxr-xr-xt/t3207-branch-submodule.sh1
-rwxr-xr-xt/t3301-notes.sh5
-rwxr-xr-xt/t3305-notes-fanout.sh4
-rwxr-xr-xt/t3307-notes-man.sh1
-rwxr-xr-xt/t3404-rebase-interactive.sh113
-rwxr-xr-xt/t3406-rebase-message.sh185
-rwxr-xr-xt/t3415-rebase-autosquash.sh13
-rwxr-xr-xt/t3416-rebase-onto-threedots.sh62
-rwxr-xr-xt/t3419-rebase-patch-id.sh63
-rwxr-xr-xt/t3420-rebase-autostash.sh2
-rwxr-xr-xt/t3426-rebase-submodule.sh3
-rwxr-xr-xt/t3430-rebase-merges.sh17
-rwxr-xr-xt/t3431-rebase-fork-point.sh2
-rwxr-xr-xt/t3435-rebase-gpg-sign.sh8
-rwxr-xr-xt/t3438-rebase-broken-files.sh59
-rwxr-xr-xt/t3512-cherry-pick-submodule.sh2
-rwxr-xr-xt/t3600-rm.sh3
-rwxr-xr-xt/t3700-add.sh2
-rwxr-xr-xt/t3701-add-interactive.sh44
-rwxr-xr-xt/t3702-add-edit.sh2
-rwxr-xr-xt/t3903-stash.sh2
-rwxr-xr-xt/t3906-stash-submodule.sh2
-rwxr-xr-xt/t3920-crlf-messages.sh2
-rwxr-xr-xt/t4012-diff-binary.sh14
-rwxr-xr-xt/t4013-diff-various.sh2
-rw-r--r--t/t4013/diff.log_--decorate=full_--all2
-rw-r--r--t/t4013/diff.log_--decorate=full_--clear-decorations_--all61
-rw-r--r--t/t4013/diff.log_--decorate=full_--decorate-all_--all61
-rw-r--r--t/t4013/diff.log_--decorate_--all2
-rw-r--r--t/t4013/diff.log_--decorate_--clear-decorations_--all61
-rw-r--r--t/t4013/diff.log_--decorate_--decorate-all_--all61
-rwxr-xr-xt/t4014-format-patch.sh45
-rwxr-xr-xt/t4017-diff-retval.sh1
-rwxr-xr-xt/t4020-diff-external.sh4
-rwxr-xr-xt/t4038-diff-combined.sh10
-rwxr-xr-xt/t4051-diff-function-context.sh1
-rwxr-xr-xt/t4057-diff-combined-paths.sh1
-rwxr-xr-xt/t4059-diff-submodule-not-initialized.sh2
-rwxr-xr-xt/t4060-diff-submodule-option-diff-format.sh2
-rwxr-xr-xt/t4067-diff-partial-clone.sh1
-rwxr-xr-xt/t4069-remerge-diff.sh29
-rwxr-xr-xt/t4114-apply-typechange.sh1
-rwxr-xr-xt/t4141-apply-too-large.sh23
-rwxr-xr-xt/t4201-shortlog.sh39
-rwxr-xr-xt/t4202-log.sh145
-rwxr-xr-xt/t4204-patch-id.sh95
-rwxr-xr-xt/t4207-log-decoration-colors.sh90
-rwxr-xr-xt/t4208-log-magic-pathspec.sh1
-rwxr-xr-xt/t4301-merge-tree-write-tree.sh625
-rwxr-xr-xt/t5000-tar-tree.sh7
-rwxr-xr-xt/t5100-mailinfo.sh4
-rwxr-xr-xt/t5304-prune.sh8
-rwxr-xr-xt/t5310-pack-bitmaps.sh785
-rwxr-xr-xt/t5311-pack-bitmaps-shallow.sh53
-rwxr-xr-xt/t5315-pack-objects-compression.sh1
-rwxr-xr-xt/t5318-commit-graph.sh4
-rwxr-xr-xt/t5319-multi-pack-index.sh94
-rwxr-xr-xt/t5320-delta-islands.sh2
-rwxr-xr-xt/t5326-multi-pack-bitmaps.sh467
-rwxr-xr-xt/t5327-multi-pack-bitmaps-rev.sh24
-rwxr-xr-xt/t5329-pack-objects-cruft.sh9
-rwxr-xr-xt/t5351-unpack-large-objects.sh1
-rwxr-xr-xt/t5402-post-merge-hook.sh1
-rwxr-xr-xt/t5500-fetch-pack.sh4
-rwxr-xr-xt/t5503-tagfollow.sh1
-rwxr-xr-xt/t5504-fetch-receive-strict.sh17
-rwxr-xr-xt/t5505-remote.sh31
-rwxr-xr-xt/t5510-fetch.sh1
-rwxr-xr-xt/t5516-fetch-push.sh48
-rwxr-xr-xt/t5520-pull.sh17
-rwxr-xr-xt/t5526-fetch-submodules.sh17
-rwxr-xr-xt/t5531-deep-submodule-push.sh50
-rwxr-xr-xt/t5537-fetch-shallow.sh5
-rwxr-xr-xt/t5545-push-options.sh1
-rwxr-xr-xt/t5550-http-fetch-dumb.sh2
-rwxr-xr-xt/t5551-http-fetch-smart.sh94
-rwxr-xr-xt/t5557-http-get.sh39
-rwxr-xr-xt/t5558-clone-bundle-uri.sh356
-rwxr-xr-xt/t5559-http-fetch-smart-http2.sh4
-rwxr-xr-xt/t5572-pull-submodule.sh4
-rwxr-xr-xt/t5601-clone.sh30
-rwxr-xr-xt/t5604-clone-reference.sh50
-rwxr-xr-xt/t5606-clone-options.sh17
-rwxr-xr-xt/t5614-clone-submodules-shallow.sh8
-rwxr-xr-xt/t5616-partial-clone.sh9
-rwxr-xr-xt/t5617-clone-submodules-remote.sh1
-rwxr-xr-xt/t5702-protocol-v2.sh2
-rwxr-xr-xt/t5703-upload-pack-ref-in-want.sh6
-rwxr-xr-xt/t5750-bundle-uri-parse.sh171
-rwxr-xr-xt/t6008-rev-list-submodule.sh3
-rwxr-xr-xt/t6018-rev-list-glob.sh40
-rwxr-xr-xt/t6019-rev-list-ancestry-path.sh101
-rwxr-xr-xt/t6021-rev-list-exclude-hidden.sh163
-rwxr-xr-xt/t6030-bisect-porcelain.sh10
-rwxr-xr-xt/t6102-rev-list-unexpected-objects.sh6
-rwxr-xr-xt/t6115-rev-list-du.sh22
-rwxr-xr-xt/t6132-pathspec-exclude.sh6
-rwxr-xr-xt/t6134-pathspec-in-submodule.sh3
-rwxr-xr-xt/t6300-for-each-ref.sh40
-rwxr-xr-xt/t6400-merge-df.sh2
-rwxr-xr-xt/t6404-recursive-merge.sh1
-rwxr-xr-xt/t6405-merge-symlinks.sh1
-rwxr-xr-xt/t6406-merge-attr.sh4
-rwxr-xr-xt/t6407-merge-binary.sh1
-rwxr-xr-xt/t6408-merge-up-to-date.sh1
-rwxr-xr-xt/t6411-merge-filemode.sh1
-rwxr-xr-xt/t6413-merge-crlf.sh1
-rwxr-xr-xt/t6416-recursive-corner-cases.sh38
-rwxr-xr-xt/t6421-merge-partial-clone.sh2
-rwxr-xr-xt/t6422-merge-rename-corner-cases.sh38
-rwxr-xr-xt/t6423-merge-rename-directories.sh194
-rwxr-xr-xt/t6425-merge-rename-delete.sh1
-rwxr-xr-xt/t6426-merge-skip-unneeded-updates.sh16
-rwxr-xr-xt/t6427-diff3-conflict-markers.sh10
-rwxr-xr-xt/t6428-merge-conflicts-sparse.sh2
-rwxr-xr-xt/t6429-merge-sequence-rename-caching.sh16
-rwxr-xr-xt/t6431-merge-criscross.sh1
-rwxr-xr-xt/t6437-submodule-merge.sh86
-rwxr-xr-xt/t6500-gc.sh96
-rwxr-xr-xt/t7001-mv.sh64
-rwxr-xr-xt/t7002-mv-sparse-checkout.sh226
-rwxr-xr-xt/t7003-filter-branch.sh4
-rwxr-xr-xt/t7007-show.sh1
-rwxr-xr-xt/t7060-wtstatus.sh1
-rwxr-xr-xt/t7062-wtstatus-ignorecase.sh1
-rwxr-xr-xt/t7064-wtstatus-pv2.sh1
-rwxr-xr-xt/t7110-reset-merge.sh1
-rwxr-xr-xt/t7111-reset-table.sh1
-rwxr-xr-xt/t7300-clean.sh1
-rwxr-xr-xt/t7400-submodule-basic.sh70
-rwxr-xr-xt/t7401-submodule-summary.sh1
-rwxr-xr-xt/t7402-submodule-rebase.sh9
-rwxr-xr-xt/t7403-submodule-sync.sh2
-rwxr-xr-xt/t7406-submodule-update.sh3
-rwxr-xr-xt/t7407-submodule-foreach.sh6
-rwxr-xr-xt/t7408-submodule-reference.sh4
-rwxr-xr-xt/t7409-submodule-detached-work-tree.sh4
-rwxr-xr-xt/t7411-submodule-config.sh39
-rwxr-xr-xt/t7412-submodule-absorbgitdirs.sh1
-rwxr-xr-xt/t7413-submodule-is-active.sh37
-rwxr-xr-xt/t7414-submodule-mistakes.sh5
-rwxr-xr-xt/t7416-submodule-dash-url.sh4
-rwxr-xr-xt/t7417-submodule-path-url.sh4
-rwxr-xr-xt/t7418-submodule-sparse-gitmodules.sh8
-rwxr-xr-xt/t7419-submodule-set-branch.sh5
-rwxr-xr-xt/t7420-submodule-set-url.sh4
-rwxr-xr-xt/t7421-submodule-summary-add.sh4
-rwxr-xr-xt/t7422-submodule-output.sh170
-rwxr-xr-xt/t7450-bad-git-dotfiles.sh6
-rwxr-xr-xt/t7503-pre-commit-and-pre-merge-commit-hooks.sh1
-rwxr-xr-xt/t7506-status-submodule.sh3
-rwxr-xr-xt/t7507-commit-verbose.sh3
-rwxr-xr-xt/t7527-builtin-fsmonitor.sh22
-rwxr-xr-xt/t7600-merge.sh9
-rwxr-xr-xt/t7609-mergetool--lib.sh1
-rwxr-xr-xt/t7610-mergetool.sh4
-rwxr-xr-xt/t7700-repack.sh198
-rwxr-xr-xt/t7701-repack-unpack-unreachable.sh4
-rwxr-xr-xt/t7703-repack-geometric.sh6
-rwxr-xr-xt/t7800-difftool.sh1
-rwxr-xr-xt/t7810-grep.sh15
-rwxr-xr-xt/t7814-grep-recurse-submodules.sh5
-rwxr-xr-xt/t7900-maintenance.sh61
-rwxr-xr-xt/t9001-send-email.sh8
-rwxr-xr-xt/t9100-git-svn-basic.sh1
-rwxr-xr-xt/t9122-git-svn-author.sh1
-rwxr-xr-xt/t9133-git-svn-nested-git-repo.sh6
-rwxr-xr-xt/t9134-git-svn-ignore-paths.sh8
-rwxr-xr-xt/t9140-git-svn-reset.sh4
-rwxr-xr-xt/t9147-git-svn-include-paths.sh8
-rwxr-xr-xt/t9162-git-svn-dcommit-interactive.sh1
-rwxr-xr-xt/t9210-scalar.sh227
-rwxr-xr-xt/t9211-scalar-clone.sh151
-rwxr-xr-xt/t9304-fast-import-marks.sh1
-rwxr-xr-xt/t9350-fast-export.sh2
-rwxr-xr-xt/t9700-perl-git.sh25
-rwxr-xr-xt/t9700/test.pl12
-rwxr-xr-xt/t9814-git-p4-rename.sh2
-rwxr-xr-xt/t9815-git-p4-submit-fail.sh4
-rwxr-xr-xt/t9850-shell.sh37
-rwxr-xr-xt/t9901-git-web--browse.sh1
-rwxr-xr-xt/t9903-bash-prompt.sh16
-rw-r--r--t/test-lib-functions.sh122
-rw-r--r--t/test-lib.sh287
378 files changed, 10672 insertions, 2438 deletions
diff --git a/t/Makefile b/t/Makefile
index 7f56e52..2c2b252 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -36,14 +36,21 @@ CHAINLINTTMP_SQ = $(subst ','\'',$(CHAINLINTTMP))
T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))
THELPERS = $(sort $(filter-out $(T),$(wildcard *.sh)))
+TLIBS = $(sort $(wildcard lib-*.sh)) annotate-tests.sh
TPERF = $(sort $(wildcard perf/p[0-9][0-9][0-9][0-9]-*.sh))
+TINTEROP = $(sort $(wildcard interop/i[0-9][0-9][0-9][0-9]-*.sh))
CHAINLINTTESTS = $(sort $(patsubst chainlint/%.test,%,$(wildcard chainlint/*.test)))
-CHAINLINT = sed -f chainlint.sed
+CHAINLINT = '$(PERL_PATH_SQ)' chainlint.pl
+
+# `test-chainlint` (which is a dependency of `test-lint`, `test` and `prove`)
+# checks all tests in all scripts via a single invocation, so tell individual
+# scripts not to "chainlint" themselves
+CHAINLINTSUPPRESS = GIT_TEST_CHAIN_LINT=0 && export GIT_TEST_CHAIN_LINT &&
all: $(DEFAULT_TEST_TARGET)
test: pre-clean check-chainlint $(TEST_LINT)
- $(MAKE) aggregate-results-and-cleanup
+ $(CHAINLINTSUPPRESS) $(MAKE) aggregate-results-and-cleanup
failed:
@failed=$$(cd '$(TEST_RESULTS_DIRECTORY_SQ)' && \
@@ -52,7 +59,7 @@ failed:
test -z "$$failed" || $(MAKE) $$failed
prove: pre-clean check-chainlint $(TEST_LINT)
- @echo "*** prove ***"; $(PROVE) --exec '$(TEST_SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)
+ @echo "*** prove ***"; $(CHAINLINTSUPPRESS) $(PROVE) --exec '$(TEST_SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)
$(MAKE) clean-except-prove-cache
$(T):
@@ -62,10 +69,11 @@ pre-clean:
$(RM) -r '$(TEST_RESULTS_DIRECTORY_SQ)'
clean-except-prove-cache: clean-chainlint
- $(RM) -r 'trash directory'.* '$(TEST_RESULTS_DIRECTORY_SQ)'
+ $(RM) -r 'trash directory'.*
$(RM) -r valgrind/bin
clean: clean-except-prove-cache
+ $(RM) -r '$(TEST_RESULTS_DIRECTORY_SQ)'
$(RM) .prove
clean-chainlint:
@@ -73,13 +81,35 @@ clean-chainlint:
check-chainlint:
@mkdir -p '$(CHAINLINTTMP_SQ)' && \
- sed -e '/^# LINT: /d' $(patsubst %,chainlint/%.test,$(CHAINLINTTESTS)) >'$(CHAINLINTTMP_SQ)'/tests && \
- sed -e '/^[ ]*$$/d' $(patsubst %,chainlint/%.expect,$(CHAINLINTTESTS)) >'$(CHAINLINTTMP_SQ)'/expect && \
- $(CHAINLINT) '$(CHAINLINTTMP_SQ)'/tests | grep -v '^[ ]*$$' >'$(CHAINLINTTMP_SQ)'/actual && \
- diff -u '$(CHAINLINTTMP_SQ)'/expect '$(CHAINLINTTMP_SQ)'/actual
+ for i in $(CHAINLINTTESTS); do \
+ echo "test_expect_success '$$i' '" && \
+ sed -e '/^# LINT: /d' chainlint/$$i.test && \
+ echo "'"; \
+ done >'$(CHAINLINTTMP_SQ)'/tests && \
+ { \
+ echo "# chainlint: $(CHAINLINTTMP_SQ)/tests" && \
+ for i in $(CHAINLINTTESTS); do \
+ echo "# chainlint: $$i" && \
+ sed -e '/^[ ]*$$/d' chainlint/$$i.expect; \
+ done \
+ } >'$(CHAINLINTTMP_SQ)'/expect && \
+ $(CHAINLINT) --emit-all '$(CHAINLINTTMP_SQ)'/tests | \
+ sed -e 's/^[1-9][0-9]* //;/^[ ]*$$/d' >'$(CHAINLINTTMP_SQ)'/actual && \
+ if test -f ../GIT-BUILD-OPTIONS; then \
+ . ../GIT-BUILD-OPTIONS; \
+ fi && \
+ if test -x ../git$$X; then \
+ DIFFW="../git$$X --no-pager diff -w --no-index"; \
+ else \
+ DIFFW="diff -w -u"; \
+ fi && \
+ $$DIFFW '$(CHAINLINTTMP_SQ)'/expect '$(CHAINLINTTMP_SQ)'/actual
test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax \
test-lint-filenames
+ifneq ($(GIT_TEST_CHAIN_LINT),0)
+test-lint: test-chainlint
+endif
test-lint-duplicates:
@dups=`echo $(T) $(TPERF) | tr ' ' '\n' | sed 's/-.*//' | sort | uniq -d` && \
@@ -102,6 +132,9 @@ test-lint-filenames:
test -z "$$bad" || { \
echo >&2 "non-portable file name(s): $$bad"; exit 1; }
+test-chainlint:
+ @$(CHAINLINT) $(T) $(TLIBS) $(TPERF) $(TINTEROP)
+
aggregate-results-and-cleanup: $(T)
$(MAKE) aggregate-results
$(MAKE) clean
@@ -117,4 +150,5 @@ valgrind:
perf:
$(MAKE) -C perf/ all
-.PHONY: pre-clean $(T) aggregate-results clean valgrind perf check-chainlint clean-chainlint
+.PHONY: pre-clean $(T) aggregate-results clean valgrind perf \
+ check-chainlint clean-chainlint test-chainlint
diff --git a/t/README b/t/README
index 4f9981c..979b2d4 100644
--- a/t/README
+++ b/t/README
@@ -196,11 +196,6 @@ appropriately before running "make". Short options can be bundled, i.e.
this feature by setting the GIT_TEST_CHAIN_LINT environment
variable to "1" or "0", respectively.
- A few test scripts disable some of the more advanced
- chain-linting detection in the name of efficiency. You can
- override this by setting the GIT_TEST_CHAIN_LINT_HARDER
- environment variable to "1".
-
--stress::
Run the test script repeatedly in multiple parallel jobs until
one of them fails. Useful for reproducing rare failures in
@@ -366,12 +361,47 @@ excluded as so much relies on it, but this might change in the future.
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_PASSING_SANITIZE_LEAK=<boolean> when compiled with
-SANITIZE=leak will run only those tests that have whitelisted
-themselves as passing with no memory leaks. Tests can be whitelisted
-by setting "TEST_PASSES_SANITIZE_LEAK=true" before sourcing
-"test-lib.sh" itself at the top of the test script. This test mode is
-used by the "linux-leaks" CI target.
+GIT_TEST_PASSING_SANITIZE_LEAK=true skips those tests that haven't
+declared themselves as leak-free by setting
+"TEST_PASSES_SANITIZE_LEAK=true" before sourcing "test-lib.sh". This
+test mode is used by the "linux-leaks" CI target.
+
+GIT_TEST_PASSING_SANITIZE_LEAK=check checks that our
+"TEST_PASSES_SANITIZE_LEAK=true" markings are current. Rather than
+skipping those tests that haven't set "TEST_PASSES_SANITIZE_LEAK=true"
+before sourcing "test-lib.sh" this mode runs them with
+"--invert-exit-code". This is used to check that there's a one-to-one
+mapping between "TEST_PASSES_SANITIZE_LEAK=true" and those tests that
+pass under "SANITIZE=leak". This is especially useful when testing a
+series that fixes various memory leaks with "git rebase -x".
+
+GIT_TEST_SANITIZE_LEAK_LOG=true will log memory leaks to
+"test-results/$TEST_NAME.leak/trace.*" files. The logs include a
+"dedup_token" (see +"ASAN_OPTIONS=help=1 ./git") and other options to
+make logs +machine-readable.
+
+With GIT_TEST_SANITIZE_LEAK_LOG=true we'll look at the leak logs
+before exiting and exit on failure if the logs showed that we had a
+memory leak, even if the test itself would have otherwise passed. This
+allows us to catch e.g. missing &&-chaining. This is especially useful
+when combined with "GIT_TEST_PASSING_SANITIZE_LEAK", see below.
+
+GIT_TEST_PASSING_SANITIZE_LEAK=check when combined with "--immediate"
+will run to completion faster, and result in the same failing
+tests. The only practical reason to run
+GIT_TEST_PASSING_SANITIZE_LEAK=check without "--immediate" is to
+combine it with "GIT_TEST_SANITIZE_LEAK_LOG=true". If we stop at the
+first failing test case our leak logs won't show subsequent leaks we
+might have run into.
+
+GIT_TEST_PASSING_SANITIZE_LEAK=(true|check) will not catch all memory
+leaks unless combined with GIT_TEST_SANITIZE_LEAK_LOG=true. Some tests
+run "git" (or "test-tool" etc.) without properly checking the exit
+code, or git will invoke itself and fail to ferry the abort() exit
+code to the original caller. When the two modes are combined we'll
+look at the "test-results/$TEST_NAME.leak/trace.*" files at the end of
+the test run to see if had memory leaks which the test itself didn't
+catch.
GIT_TEST_PROTOCOL_VERSION=<n>, when set, makes 'protocol.version'
default to n.
@@ -935,32 +965,6 @@ see test-lib-functions.sh for the full list and their options.
test_done
fi
- - test_external [<prereq>] <message> <external> <script>
-
- Execute a <script> with an <external> interpreter (like perl). This
- was added for tests like t9700-perl-git.sh which do most of their
- work in an external test script.
-
- test_external \
- 'GitwebCache::*FileCache*' \
- perl "$TEST_DIRECTORY"/t9503/test_cache_interface.pl
-
- If the test is outputting its own TAP you should set the
- test_external_has_tap variable somewhere before calling the first
- test_external* function. See t9700-perl-git.sh for an example.
-
- # The external test will outputs its own plan
- test_external_has_tap=1
-
- - test_external_without_stderr [<prereq>] <message> <external> <script>
-
- Like test_external but fail if there's any output on stderr,
- instead of checking the exit code.
-
- test_external_without_stderr \
- 'Perl API' \
- perl "$TEST_DIRECTORY"/t9700/test.pl
-
- test_expect_code <exit-code> <command>
Run a command and ensure that it exits with the given exit code.
diff --git a/t/chainlint.pl b/t/chainlint.pl
new file mode 100755
index 0000000..e966412
--- /dev/null
+++ b/t/chainlint.pl
@@ -0,0 +1,823 @@
+#!/usr/bin/env perl
+#
+# Copyright (c) 2021-2022 Eric Sunshine <sunshine@sunshineco.com>
+#
+# This tool scans shell scripts for test definitions and checks those tests for
+# problems, such as broken &&-chains, which might hide bugs in the tests
+# themselves or in behaviors being exercised by the tests.
+#
+# Input arguments are pathnames of shell scripts containing test definitions,
+# or globs referencing a collection of scripts. For each problem discovered,
+# the pathname of the script containing the test is printed along with the test
+# name and the test body with a `?!FOO?!` annotation at the location of each
+# detected problem, where "FOO" is a tag such as "AMP" which indicates a broken
+# &&-chain. Returns zero if no problems are discovered, otherwise non-zero.
+
+use warnings;
+use strict;
+use Config;
+use File::Glob;
+use Getopt::Long;
+
+my $jobs = -1;
+my $show_stats;
+my $emit_all;
+
+# Lexer tokenizes POSIX shell scripts. It is roughly modeled after section 2.3
+# "Token Recognition" of POSIX chapter 2 "Shell Command Language". Although
+# similar to lexical analyzers for other languages, this one differs in a few
+# substantial ways due to quirks of the shell command language.
+#
+# For instance, in many languages, newline is just whitespace like space or
+# TAB, but in shell a newline is a command separator, thus a distinct lexical
+# token. A newline is significant and returned as a distinct token even at the
+# end of a shell comment.
+#
+# In other languages, `1+2` would typically be scanned as three tokens
+# (`1`, `+`, and `2`), but in shell it is a single token. However, the similar
+# `1 + 2`, which embeds whitepace, is scanned as three token in shell, as well.
+# In shell, several characters with special meaning lose that meaning when not
+# surrounded by whitespace. For instance, the negation operator `!` is special
+# when standing alone surrounded by whitespace; whereas in `foo!uucp` it is
+# just a plain character in the longer token "foo!uucp". In many other
+# languages, `"string"/foo:'string'` might be scanned as five tokens ("string",
+# `/`, `foo`, `:`, and 'string'), but in shell, it is just a single token.
+#
+# The lexical analyzer for the shell command language is also somewhat unusual
+# in that it recursively invokes the parser to handle the body of `$(...)`
+# expressions which can contain arbitrary shell code. Such expressions may be
+# encountered both inside and outside of double-quoted strings.
+#
+# The lexical analyzer is responsible for consuming shell here-doc bodies which
+# extend from the line following a `<<TAG` operator until a line consisting
+# solely of `TAG`. Here-doc consumption begins when a newline is encountered.
+# It is legal for multiple here-doc `<<TAG` operators to be present on a single
+# line, in which case their bodies must be present one following the next, and
+# are consumed in the (left-to-right) order the `<<TAG` operators appear on the
+# line. A special complication is that the bodies of all here-docs must be
+# consumed when the newline is encountered even if the parse context depth has
+# changed. For instance, in `cat <<A && x=$(cat <<B &&\n`, bodies of here-docs
+# "A" and "B" must be consumed even though "A" was introduced outside the
+# recursive parse context in which "B" was introduced and in which the newline
+# is encountered.
+package Lexer;
+
+sub new {
+ my ($class, $parser, $s) = @_;
+ bless {
+ parser => $parser,
+ buff => $s,
+ lineno => 1,
+ heretags => []
+ } => $class;
+}
+
+sub scan_heredoc_tag {
+ my $self = shift @_;
+ ${$self->{buff}} =~ /\G(-?)/gc;
+ my $indented = $1;
+ my $token = $self->scan_token();
+ return "<<$indented" unless $token;
+ my $tag = $token->[0];
+ $tag =~ s/['"\\]//g;
+ push(@{$self->{heretags}}, $indented ? "\t$tag" : "$tag");
+ return "<<$indented$tag";
+}
+
+sub scan_op {
+ my ($self, $c) = @_;
+ my $b = $self->{buff};
+ return $c unless $$b =~ /\G(.)/sgc;
+ my $cc = $c . $1;
+ return scan_heredoc_tag($self) if $cc eq '<<';
+ return $cc if $cc =~ /^(?:&&|\|\||>>|;;|<&|>&|<>|>\|)$/;
+ pos($$b)--;
+ return $c;
+}
+
+sub scan_sqstring {
+ my $self = shift @_;
+ ${$self->{buff}} =~ /\G([^']*'|.*\z)/sgc;
+ my $s = $1;
+ $self->{lineno} += () = $s =~ /\n/sg;
+ return "'" . $s;
+}
+
+sub scan_dqstring {
+ my $self = shift @_;
+ my $b = $self->{buff};
+ my $s = '"';
+ while (1) {
+ # slurp up non-special characters
+ $s .= $1 if $$b =~ /\G([^"\$\\]+)/gc;
+ # handle special characters
+ last unless $$b =~ /\G(.)/sgc;
+ my $c = $1;
+ $s .= '"', last if $c eq '"';
+ $s .= '$' . $self->scan_dollar(), next if $c eq '$';
+ if ($c eq '\\') {
+ $s .= '\\', last unless $$b =~ /\G(.)/sgc;
+ $c = $1;
+ $self->{lineno}++, next if $c eq "\n"; # line splice
+ # backslash escapes only $, `, ", \ in dq-string
+ $s .= '\\' unless $c =~ /^[\$`"\\]$/;
+ $s .= $c;
+ next;
+ }
+ die("internal error scanning dq-string '$c'\n");
+ }
+ $self->{lineno} += () = $s =~ /\n/sg;
+ return $s;
+}
+
+sub scan_balanced {
+ my ($self, $c1, $c2) = @_;
+ my $b = $self->{buff};
+ my $depth = 1;
+ my $s = $c1;
+ while ($$b =~ /\G([^\Q$c1$c2\E]*(?:[\Q$c1$c2\E]|\z))/gc) {
+ $s .= $1;
+ $depth++, next if $s =~ /\Q$c1\E$/;
+ $depth--;
+ last if $depth == 0;
+ }
+ $self->{lineno} += () = $s =~ /\n/sg;
+ return $s;
+}
+
+sub scan_subst {
+ my $self = shift @_;
+ my @tokens = $self->{parser}->parse(qr/^\)$/);
+ $self->{parser}->next_token(); # closing ")"
+ return @tokens;
+}
+
+sub scan_dollar {
+ my $self = shift @_;
+ my $b = $self->{buff};
+ return $self->scan_balanced('(', ')') if $$b =~ /\G\((?=\()/gc; # $((...))
+ return '(' . join(' ', map {$_->[0]} $self->scan_subst()) . ')' if $$b =~ /\G\(/gc; # $(...)
+ return $self->scan_balanced('{', '}') if $$b =~ /\G\{/gc; # ${...}
+ return $1 if $$b =~ /\G(\w+)/gc; # $var
+ return $1 if $$b =~ /\G([@*#?$!0-9-])/gc; # $*, $1, $$, etc.
+ return '';
+}
+
+sub swallow_heredocs {
+ my $self = shift @_;
+ my $b = $self->{buff};
+ my $tags = $self->{heretags};
+ while (my $tag = shift @$tags) {
+ my $start = pos($$b);
+ my $indent = $tag =~ s/^\t// ? '\\s*' : '';
+ $$b =~ /(?:\G|\n)$indent\Q$tag\E(?:\n|\z)/gc;
+ my $body = substr($$b, $start, pos($$b) - $start);
+ $self->{lineno} += () = $body =~ /\n/sg;
+ }
+}
+
+sub scan_token {
+ my $self = shift @_;
+ my $b = $self->{buff};
+ my $token = '';
+ my ($start, $startln);
+RESTART:
+ $startln = $self->{lineno};
+ $$b =~ /\G[ \t]+/gc; # skip whitespace (but not newline)
+ $start = pos($$b) || 0;
+ $self->{lineno}++, return ["\n", $start, pos($$b), $startln, $startln] if $$b =~ /\G#[^\n]*(?:\n|\z)/gc; # comment
+ while (1) {
+ # slurp up non-special characters
+ $token .= $1 if $$b =~ /\G([^\\;&|<>(){}'"\$\s]+)/gc;
+ # handle special characters
+ last unless $$b =~ /\G(.)/sgc;
+ my $c = $1;
+ pos($$b)--, last if $c =~ /^[ \t]$/; # whitespace ends token
+ pos($$b)--, last if length($token) && $c =~ /^[;&|<>(){}\n]$/;
+ $token .= $self->scan_sqstring(), next if $c eq "'";
+ $token .= $self->scan_dqstring(), next if $c eq '"';
+ $token .= $c . $self->scan_dollar(), next if $c eq '$';
+ $self->{lineno}++, $self->swallow_heredocs(), $token = $c, last if $c eq "\n";
+ $token = $self->scan_op($c), last if $c =~ /^[;&|<>]$/;
+ $token = $c, last if $c =~ /^[(){}]$/;
+ if ($c eq '\\') {
+ $token .= '\\', last unless $$b =~ /\G(.)/sgc;
+ $c = $1;
+ $self->{lineno}++, next if $c eq "\n" && length($token); # line splice
+ $self->{lineno}++, goto RESTART if $c eq "\n"; # line splice
+ $token .= '\\' . $c;
+ next;
+ }
+ die("internal error scanning character '$c'\n");
+ }
+ return length($token) ? [$token, $start, pos($$b), $startln, $self->{lineno}] : undef;
+}
+
+# ShellParser parses POSIX shell scripts (with minor extensions for Bash). It
+# is a recursive descent parser very roughly modeled after section 2.10 "Shell
+# Grammar" of POSIX chapter 2 "Shell Command Language".
+package ShellParser;
+
+sub new {
+ my ($class, $s) = @_;
+ my $self = bless {
+ buff => [],
+ stop => [],
+ output => []
+ } => $class;
+ $self->{lexer} = Lexer->new($self, $s);
+ return $self;
+}
+
+sub next_token {
+ my $self = shift @_;
+ return pop(@{$self->{buff}}) if @{$self->{buff}};
+ return $self->{lexer}->scan_token();
+}
+
+sub untoken {
+ my $self = shift @_;
+ push(@{$self->{buff}}, @_);
+}
+
+sub peek {
+ my $self = shift @_;
+ my $token = $self->next_token();
+ return undef unless defined($token);
+ $self->untoken($token);
+ return $token;
+}
+
+sub stop_at {
+ my ($self, $token) = @_;
+ return 1 unless defined($token);
+ my $stop = ${$self->{stop}}[-1] if @{$self->{stop}};
+ return defined($stop) && $token->[0] =~ $stop;
+}
+
+sub expect {
+ my ($self, $expect) = @_;
+ my $token = $self->next_token();
+ return $token if defined($token) && $token->[0] eq $expect;
+ push(@{$self->{output}}, "?!ERR?! expected '$expect' but found '" . (defined($token) ? $token->[0] : "<end-of-input>") . "'\n");
+ $self->untoken($token) if defined($token);
+ return ();
+}
+
+sub optional_newlines {
+ my $self = shift @_;
+ my @tokens;
+ while (my $token = $self->peek()) {
+ last unless $token->[0] eq "\n";
+ push(@tokens, $self->next_token());
+ }
+ return @tokens;
+}
+
+sub parse_group {
+ my $self = shift @_;
+ return ($self->parse(qr/^}$/),
+ $self->expect('}'));
+}
+
+sub parse_subshell {
+ my $self = shift @_;
+ return ($self->parse(qr/^\)$/),
+ $self->expect(')'));
+}
+
+sub parse_case_pattern {
+ my $self = shift @_;
+ my @tokens;
+ while (defined(my $token = $self->next_token())) {
+ push(@tokens, $token);
+ last if $token->[0] eq ')';
+ }
+ return @tokens;
+}
+
+sub parse_case {
+ my $self = shift @_;
+ my @tokens;
+ push(@tokens,
+ $self->next_token(), # subject
+ $self->optional_newlines(),
+ $self->expect('in'),
+ $self->optional_newlines());
+ while (1) {
+ my $token = $self->peek();
+ last unless defined($token) && $token->[0] ne 'esac';
+ push(@tokens,
+ $self->parse_case_pattern(),
+ $self->optional_newlines(),
+ $self->parse(qr/^(?:;;|esac)$/)); # item body
+ $token = $self->peek();
+ last unless defined($token) && $token->[0] ne 'esac';
+ push(@tokens,
+ $self->expect(';;'),
+ $self->optional_newlines());
+ }
+ push(@tokens, $self->expect('esac'));
+ return @tokens;
+}
+
+sub parse_for {
+ my $self = shift @_;
+ my @tokens;
+ push(@tokens,
+ $self->next_token(), # variable
+ $self->optional_newlines());
+ my $token = $self->peek();
+ if (defined($token) && $token->[0] eq 'in') {
+ push(@tokens,
+ $self->expect('in'),
+ $self->optional_newlines());
+ }
+ push(@tokens,
+ $self->parse(qr/^do$/), # items
+ $self->expect('do'),
+ $self->optional_newlines(),
+ $self->parse_loop_body(),
+ $self->expect('done'));
+ return @tokens;
+}
+
+sub parse_if {
+ my $self = shift @_;
+ my @tokens;
+ while (1) {
+ push(@tokens,
+ $self->parse(qr/^then$/), # if/elif condition
+ $self->expect('then'),
+ $self->optional_newlines(),
+ $self->parse(qr/^(?:elif|else|fi)$/)); # if/elif body
+ my $token = $self->peek();
+ last unless defined($token) && $token->[0] eq 'elif';
+ push(@tokens, $self->expect('elif'));
+ }
+ my $token = $self->peek();
+ if (defined($token) && $token->[0] eq 'else') {
+ push(@tokens,
+ $self->expect('else'),
+ $self->optional_newlines(),
+ $self->parse(qr/^fi$/)); # else body
+ }
+ push(@tokens, $self->expect('fi'));
+ return @tokens;
+}
+
+sub parse_loop_body {
+ my $self = shift @_;
+ return $self->parse(qr/^done$/);
+}
+
+sub parse_loop {
+ my $self = shift @_;
+ return ($self->parse(qr/^do$/), # condition
+ $self->expect('do'),
+ $self->optional_newlines(),
+ $self->parse_loop_body(),
+ $self->expect('done'));
+}
+
+sub parse_func {
+ my $self = shift @_;
+ return ($self->expect('('),
+ $self->expect(')'),
+ $self->optional_newlines(),
+ $self->parse_cmd()); # body
+}
+
+sub parse_bash_array_assignment {
+ my $self = shift @_;
+ my @tokens = $self->expect('(');
+ while (defined(my $token = $self->next_token())) {
+ push(@tokens, $token);
+ last if $token->[0] eq ')';
+ }
+ return @tokens;
+}
+
+my %compound = (
+ '{' => \&parse_group,
+ '(' => \&parse_subshell,
+ 'case' => \&parse_case,
+ 'for' => \&parse_for,
+ 'if' => \&parse_if,
+ 'until' => \&parse_loop,
+ 'while' => \&parse_loop);
+
+sub parse_cmd {
+ my $self = shift @_;
+ my $cmd = $self->next_token();
+ return () unless defined($cmd);
+ return $cmd if $cmd->[0] eq "\n";
+
+ my $token;
+ my @tokens = $cmd;
+ if ($cmd->[0] eq '!') {
+ push(@tokens, $self->parse_cmd());
+ return @tokens;
+ } elsif (my $f = $compound{$cmd->[0]}) {
+ push(@tokens, $self->$f());
+ } elsif (defined($token = $self->peek()) && $token->[0] eq '(') {
+ if ($cmd->[0] !~ /\w=$/) {
+ push(@tokens, $self->parse_func());
+ return @tokens;
+ }
+ my @array = $self->parse_bash_array_assignment();
+ $tokens[-1]->[0] .= join(' ', map {$_->[0]} @array);
+ $tokens[-1]->[2] = $array[$#array][2] if @array;
+ }
+
+ while (defined(my $token = $self->next_token())) {
+ $self->untoken($token), last if $self->stop_at($token);
+ push(@tokens, $token);
+ last if $token->[0] =~ /^(?:[;&\n|]|&&|\|\|)$/;
+ }
+ push(@tokens, $self->next_token()) if $tokens[-1]->[0] ne "\n" && defined($token = $self->peek()) && $token->[0] eq "\n";
+ return @tokens;
+}
+
+sub accumulate {
+ my ($self, $tokens, $cmd) = @_;
+ push(@$tokens, @$cmd);
+}
+
+sub parse {
+ my ($self, $stop) = @_;
+ push(@{$self->{stop}}, $stop);
+ goto DONE if $self->stop_at($self->peek());
+ my @tokens;
+ while (my @cmd = $self->parse_cmd()) {
+ $self->accumulate(\@tokens, \@cmd);
+ last if $self->stop_at($self->peek());
+ }
+DONE:
+ pop(@{$self->{stop}});
+ return @tokens;
+}
+
+# TestParser is a subclass of ShellParser which, beyond parsing shell script
+# code, is also imbued with semantic knowledge of test construction, and checks
+# tests for common problems (such as broken &&-chains) which might hide bugs in
+# the tests themselves or in behaviors being exercised by the tests. As such,
+# TestParser is only called upon to parse test bodies, not the top-level
+# scripts in which the tests are defined.
+package TestParser;
+
+use base 'ShellParser';
+
+sub new {
+ my $class = shift @_;
+ my $self = $class->SUPER::new(@_);
+ $self->{problems} = [];
+ return $self;
+}
+
+sub find_non_nl {
+ my $tokens = shift @_;
+ my $n = shift @_;
+ $n = $#$tokens if !defined($n);
+ $n-- while $n >= 0 && $$tokens[$n]->[0] eq "\n";
+ return $n;
+}
+
+sub ends_with {
+ my ($tokens, $needles) = @_;
+ my $n = find_non_nl($tokens);
+ for my $needle (reverse(@$needles)) {
+ return undef if $n < 0;
+ $n = find_non_nl($tokens, $n), next if $needle eq "\n";
+ return undef if $$tokens[$n]->[0] !~ $needle;
+ $n--;
+ }
+ return 1;
+}
+
+sub match_ending {
+ my ($tokens, $endings) = @_;
+ for my $needles (@$endings) {
+ next if @$tokens < scalar(grep {$_ ne "\n"} @$needles);
+ return 1 if ends_with($tokens, $needles);
+ }
+ return undef;
+}
+
+sub parse_loop_body {
+ my $self = shift @_;
+ my @tokens = $self->SUPER::parse_loop_body(@_);
+ # did loop signal failure via "|| return" or "|| exit"?
+ return @tokens if !@tokens || grep {$_->[0] =~ /^(?:return|exit|\$\?)$/} @tokens;
+ # did loop upstream of a pipe signal failure via "|| echo 'impossible
+ # text'" as the final command in the loop body?
+ return @tokens if ends_with(\@tokens, [qr/^\|\|$/, "\n", qr/^echo$/, qr/^.+$/]);
+ # flag missing "return/exit" handling explicit failure in loop body
+ my $n = find_non_nl(\@tokens);
+ push(@{$self->{problems}}, ['LOOP', $tokens[$n]]);
+ return @tokens;
+}
+
+my @safe_endings = (
+ [qr/^(?:&&|\|\||\||&)$/],
+ [qr/^(?:exit|return)$/, qr/^(?:\d+|\$\?)$/],
+ [qr/^(?:exit|return)$/, qr/^(?:\d+|\$\?)$/, qr/^;$/],
+ [qr/^(?:exit|return|continue)$/],
+ [qr/^(?:exit|return|continue)$/, qr/^;$/]);
+
+sub accumulate {
+ my ($self, $tokens, $cmd) = @_;
+ my $problems = $self->{problems};
+
+ # no previous command to check for missing "&&"
+ goto DONE unless @$tokens;
+
+ # new command is empty line; can't yet check if previous is missing "&&"
+ goto DONE if @$cmd == 1 && $$cmd[0]->[0] eq "\n";
+
+ # did previous command end with "&&", "|", "|| return" or similar?
+ goto DONE if match_ending($tokens, \@safe_endings);
+
+ # if this command handles "$?" specially, then okay for previous
+ # command to be missing "&&"
+ for my $token (@$cmd) {
+ goto DONE if $token->[0] =~ /\$\?/;
+ }
+
+ # if this command is "false", "return 1", or "exit 1" (which signal
+ # failure explicitly), then okay for all preceding commands to be
+ # missing "&&"
+ if ($$cmd[0]->[0] =~ /^(?:false|return|exit)$/) {
+ @$problems = grep {$_->[0] ne 'AMP'} @$problems;
+ goto DONE;
+ }
+
+ # flag missing "&&" at end of previous command
+ my $n = find_non_nl($tokens);
+ push(@$problems, ['AMP', $tokens->[$n]]) unless $n < 0;
+
+DONE:
+ $self->SUPER::accumulate($tokens, $cmd);
+}
+
+# ScriptParser is a subclass of ShellParser which identifies individual test
+# definitions within test scripts, and passes each test body through TestParser
+# to identify possible problems. ShellParser detects test definitions not only
+# at the top-level of test scripts but also within compound commands such as
+# loops and function definitions.
+package ScriptParser;
+
+use base 'ShellParser';
+
+sub new {
+ my $class = shift @_;
+ my $self = $class->SUPER::new(@_);
+ $self->{ntests} = 0;
+ return $self;
+}
+
+# extract the raw content of a token, which may be a single string or a
+# composition of multiple strings and non-string character runs; for instance,
+# `"test body"` unwraps to `test body`; `word"a b"42'c d'` to `worda b42c d`
+sub unwrap {
+ my $token = (@_ ? shift @_ : $_)->[0];
+ # simple case: 'sqstring' or "dqstring"
+ return $token if $token =~ s/^'([^']*)'$/$1/;
+ return $token if $token =~ s/^"([^"]*)"$/$1/;
+
+ # composite case
+ my ($s, $q, $escaped);
+ while (1) {
+ # slurp up non-special characters
+ $s .= $1 if $token =~ /\G([^\\'"]*)/gc;
+ # handle special characters
+ last unless $token =~ /\G(.)/sgc;
+ my $c = $1;
+ $q = undef, next if defined($q) && $c eq $q;
+ $q = $c, next if !defined($q) && $c =~ /^['"]$/;
+ if ($c eq '\\') {
+ last unless $token =~ /\G(.)/sgc;
+ $c = $1;
+ $s .= '\\' if $c eq "\n"; # preserve line splice
+ }
+ $s .= $c;
+ }
+ return $s
+}
+
+sub check_test {
+ my $self = shift @_;
+ my ($title, $body) = map(unwrap, @_);
+ $self->{ntests}++;
+ my $parser = TestParser->new(\$body);
+ my @tokens = $parser->parse();
+ my $problems = $parser->{problems};
+ return unless $emit_all || @$problems;
+ my $c = main::fd_colors(1);
+ my $lineno = $_[1]->[3];
+ my $start = 0;
+ my $checked = '';
+ for (sort {$a->[1]->[2] <=> $b->[1]->[2]} @$problems) {
+ my ($label, $token) = @$_;
+ my $pos = $token->[2];
+ $checked .= substr($body, $start, $pos - $start) . " ?!$label?! ";
+ $start = $pos;
+ }
+ $checked .= substr($body, $start);
+ $checked =~ s/^/$lineno++ . ' '/mge;
+ $checked =~ s/^\d+ \n//;
+ $checked =~ s/(\s) \?!/$1?!/mg;
+ $checked =~ s/\?! (\s)/?!$1/mg;
+ $checked =~ s/(\?![^?]+\?!)/$c->{rev}$c->{red}$1$c->{reset}/mg;
+ $checked =~ s/^\d+/$c->{dim}$&$c->{reset}/mg;
+ $checked .= "\n" unless $checked =~ /\n$/;
+ push(@{$self->{output}}, "$c->{blue}# chainlint: $title$c->{reset}\n$checked");
+}
+
+sub parse_cmd {
+ my $self = shift @_;
+ my @tokens = $self->SUPER::parse_cmd();
+ return @tokens unless @tokens && $tokens[0]->[0] =~ /^test_expect_(?:success|failure)$/;
+ my $n = $#tokens;
+ $n-- while $n >= 0 && $tokens[$n]->[0] =~ /^(?:[;&\n|]|&&|\|\|)$/;
+ $self->check_test($tokens[1], $tokens[2]) if $n == 2; # title body
+ $self->check_test($tokens[2], $tokens[3]) if $n > 2; # prereq title body
+ return @tokens;
+}
+
+# main contains high-level functionality for processing command-line switches,
+# feeding input test scripts to ScriptParser, and reporting results.
+package main;
+
+my $getnow = sub { return time(); };
+my $interval = sub { return time() - shift; };
+if (eval {require Time::HiRes; Time::HiRes->import(); 1;}) {
+ $getnow = sub { return [Time::HiRes::gettimeofday()]; };
+ $interval = sub { return Time::HiRes::tv_interval(shift); };
+}
+
+# Restore TERM if test framework set it to "dumb" so 'tput' will work; do this
+# outside of get_colors() since under 'ithreads' all threads use %ENV of main
+# thread and ignore %ENV changes in subthreads.
+$ENV{TERM} = $ENV{USER_TERM} if $ENV{USER_TERM};
+
+my @NOCOLORS = (bold => '', rev => '', dim => '', reset => '', blue => '', green => '', red => '');
+my %COLORS = ();
+sub get_colors {
+ return \%COLORS if %COLORS;
+ if (exists($ENV{NO_COLOR})) {
+ %COLORS = @NOCOLORS;
+ return \%COLORS;
+ }
+ if ($ENV{TERM} =~ /xterm|xterm-\d+color|xterm-new|xterm-direct|nsterm|nsterm-\d+color|nsterm-direct/) {
+ %COLORS = (bold => "\e[1m",
+ rev => "\e[7m",
+ dim => "\e[2m",
+ reset => "\e[0m",
+ blue => "\e[34m",
+ green => "\e[32m",
+ red => "\e[31m");
+ return \%COLORS;
+ }
+ if (system("tput sgr0 >/dev/null 2>&1") == 0 &&
+ system("tput bold >/dev/null 2>&1") == 0 &&
+ system("tput rev >/dev/null 2>&1") == 0 &&
+ system("tput dim >/dev/null 2>&1") == 0 &&
+ system("tput setaf 1 >/dev/null 2>&1") == 0) {
+ %COLORS = (bold => `tput bold`,
+ rev => `tput rev`,
+ dim => `tput dim`,
+ reset => `tput sgr0`,
+ blue => `tput setaf 4`,
+ green => `tput setaf 2`,
+ red => `tput setaf 1`);
+ return \%COLORS;
+ }
+ %COLORS = @NOCOLORS;
+ return \%COLORS;
+}
+
+my %FD_COLORS = ();
+sub fd_colors {
+ my $fd = shift;
+ return $FD_COLORS{$fd} if exists($FD_COLORS{$fd});
+ $FD_COLORS{$fd} = -t $fd ? get_colors() : {@NOCOLORS};
+ return $FD_COLORS{$fd};
+}
+
+sub ncores {
+ # Windows
+ return $ENV{NUMBER_OF_PROCESSORS} if exists($ENV{NUMBER_OF_PROCESSORS});
+ # Linux / MSYS2 / Cygwin / WSL
+ do { local @ARGV='/proc/cpuinfo'; return scalar(grep(/^processor[\s\d]*:/, <>)); } if -r '/proc/cpuinfo';
+ # macOS & BSD
+ return qx/sysctl -n hw.ncpu/ if $^O =~ /(?:^darwin$|bsd)/;
+ return 1;
+}
+
+sub show_stats {
+ my ($start_time, $stats) = @_;
+ my $walltime = $interval->($start_time);
+ my ($usertime) = times();
+ my ($total_workers, $total_scripts, $total_tests, $total_errs) = (0, 0, 0, 0);
+ my $c = fd_colors(2);
+ print(STDERR $c->{green});
+ for (@$stats) {
+ my ($worker, $nscripts, $ntests, $nerrs) = @$_;
+ print(STDERR "worker $worker: $nscripts scripts, $ntests tests, $nerrs errors\n");
+ $total_workers++;
+ $total_scripts += $nscripts;
+ $total_tests += $ntests;
+ $total_errs += $nerrs;
+ }
+ printf(STDERR "total: %d workers, %d scripts, %d tests, %d errors, %.2fs/%.2fs (wall/user)$c->{reset}\n", $total_workers, $total_scripts, $total_tests, $total_errs, $walltime, $usertime);
+}
+
+sub check_script {
+ my ($id, $next_script, $emit) = @_;
+ my ($nscripts, $ntests, $nerrs) = (0, 0, 0);
+ while (my $path = $next_script->()) {
+ $nscripts++;
+ my $fh;
+ unless (open($fh, "<", $path)) {
+ $emit->("?!ERR?! $path: $!\n");
+ next;
+ }
+ my $s = do { local $/; <$fh> };
+ close($fh);
+ my $parser = ScriptParser->new(\$s);
+ 1 while $parser->parse_cmd();
+ if (@{$parser->{output}}) {
+ my $c = fd_colors(1);
+ my $s = join('', @{$parser->{output}});
+ $emit->("$c->{bold}$c->{blue}# chainlint: $path$c->{reset}\n" . $s);
+ $nerrs += () = $s =~ /\?![^?]+\?!/g;
+ }
+ $ntests += $parser->{ntests};
+ }
+ return [$id, $nscripts, $ntests, $nerrs];
+}
+
+sub exit_code {
+ my $stats = shift @_;
+ for (@$stats) {
+ my ($worker, $nscripts, $ntests, $nerrs) = @$_;
+ return 1 if $nerrs;
+ }
+ return 0;
+}
+
+Getopt::Long::Configure(qw{bundling});
+GetOptions(
+ "emit-all!" => \$emit_all,
+ "jobs|j=i" => \$jobs,
+ "stats|show-stats!" => \$show_stats) or die("option error\n");
+$jobs = ncores() if $jobs < 1;
+
+my $start_time = $getnow->();
+my @stats;
+
+my @scripts;
+push(@scripts, File::Glob::bsd_glob($_)) for (@ARGV);
+unless (@scripts) {
+ show_stats($start_time, \@stats) if $show_stats;
+ exit;
+}
+
+unless ($Config{useithreads} && eval {
+ require threads; threads->import();
+ require Thread::Queue; Thread::Queue->import();
+ 1;
+ }) {
+ push(@stats, check_script(1, sub { shift(@scripts); }, sub { print(@_); }));
+ show_stats($start_time, \@stats) if $show_stats;
+ exit(exit_code(\@stats));
+}
+
+my $script_queue = Thread::Queue->new();
+my $output_queue = Thread::Queue->new();
+
+sub next_script { return $script_queue->dequeue(); }
+sub emit { $output_queue->enqueue(@_); }
+
+sub monitor {
+ while (my $s = $output_queue->dequeue()) {
+ print($s);
+ }
+}
+
+my $mon = threads->create({'context' => 'void'}, \&monitor);
+threads->create({'context' => 'list'}, \&check_script, $_, \&next_script, \&emit) for 1..$jobs;
+
+$script_queue->enqueue(@scripts);
+$script_queue->end();
+
+for (threads->list()) {
+ push(@stats, $_->join()) unless $_ == $mon;
+}
+
+$output_queue->end();
+$mon->join();
+
+show_stats($start_time, \@stats) if $show_stats;
+exit(exit_code(\@stats));
diff --git a/t/chainlint.sed b/t/chainlint.sed
deleted file mode 100644
index dc4ce37..0000000
--- a/t/chainlint.sed
+++ /dev/null
@@ -1,399 +0,0 @@
-#------------------------------------------------------------------------------
-# Detect broken &&-chains in tests.
-#
-# At present, only &&-chains in subshells are examined by this linter;
-# top-level &&-chains are instead checked directly by the test framework. Like
-# the top-level &&-chain linter, the subshell linter (intentionally) does not
-# check &&-chains within {...} blocks.
-#
-# Checking for &&-chain breakage is done line-by-line by pure textual
-# inspection.
-#
-# Incomplete lines (those ending with "\") are stitched together with following
-# lines to simplify processing, particularly of "one-liner" statements.
-# Top-level here-docs are swallowed to avoid false positives within the
-# here-doc body, although the statement to which the here-doc is attached is
-# retained.
-#
-# Heuristics are used to detect end-of-subshell when the closing ")" is cuddled
-# with the final subshell statement on the same line:
-#
-# (cd foo &&
-# bar)
-#
-# in order to avoid misinterpreting the ")" in constructs such as "x=$(...)"
-# and "case $x in *)" as ending the subshell.
-#
-# Lines missing a final "&&" are flagged with "?!AMP?!", as are lines which
-# chain commands with ";" internally rather than "&&". A line may be flagged
-# for both violations.
-#
-# Detection of a missing &&-link in a multi-line subshell is complicated by the
-# fact that the last statement before the closing ")" must not end with "&&".
-# Since processing is line-by-line, it is not known whether a missing "&&" is
-# legitimate or not until the _next_ line is seen. To accommodate this, within
-# multi-line subshells, each line is stored in sed's "hold" area until after
-# the next line is seen and processed. If the next line is a stand-alone ")",
-# then a missing "&&" on the previous line is legitimate; otherwise a missing
-# "&&" is a break in the &&-chain.
-#
-# (
-# cd foo &&
-# bar
-# )
-#
-# In practical terms, when "bar" is encountered, it is flagged with "?!AMP?!",
-# but when the stand-alone ")" line is seen which closes the subshell, the
-# "?!AMP?!" violation is removed from the "bar" line (retrieved from the "hold"
-# area) since the final statement of a subshell must not end with "&&". The
-# final line of a subshell may still break the &&-chain by using ";" internally
-# to chain commands together rather than "&&", but an internal "?!AMP?!" is
-# never removed from a line even though a line-ending "?!AMP?!" might be.
-#
-# Care is taken to recognize the last _statement_ of a multi-line subshell, not
-# necessarily the last textual _line_ within the subshell, since &&-chaining
-# applies to statements, not to lines. Consequently, blank lines, comment
-# lines, and here-docs are swallowed (but not the command to which the here-doc
-# is attached), leaving the last statement in the "hold" area, not the last
-# line, thus simplifying &&-link checking.
-#
-# The final statement before "done" in for- and while-loops, and before "elif",
-# "else", and "fi" in if-then-else likewise must not end with "&&", thus
-# receives similar treatment.
-#
-# Swallowing here-docs with arbitrary tags requires a bit of finesse. When a
-# line such as "cat <<EOF" is seen, the here-doc tag is copied to the front of
-# the line enclosed in angle brackets as a sentinel, giving "<EOF>cat <<EOF".
-# As each subsequent line is read, it is appended to the target line and a
-# (whitespace-loose) back-reference match /^<(.*)>\n\1$/ is attempted to see if
-# the content inside "<...>" matches the entirety of the newly-read line. For
-# instance, if the next line read is "some data", when concatenated with the
-# target line, it becomes "<EOF>cat <<EOF\nsome data", and a match is attempted
-# to see if "EOF" matches "some data". Since it doesn't, the next line is
-# attempted. When a line consisting of only "EOF" (and possible whitespace) is
-# encountered, it is appended to the target line giving "<EOF>cat <<EOF\nEOF",
-# in which case the "EOF" inside "<...>" does match the text following the
-# newline, thus the closing here-doc tag has been found. The closing tag line
-# and the "<...>" prefix on the target line are then discarded, leaving just
-# the target line "cat <<EOF".
-#------------------------------------------------------------------------------
-
-# incomplete line -- slurp up next line
-:squash
-/\\$/ {
- N
- s/\\\n//
- bsquash
-}
-
-# here-doc -- swallow it to avoid false hits within its body (but keep the
-# command to which it was attached)
-/<<-*[ ]*[\\'"]*[A-Za-z0-9_]/ {
- /"[^"]*<<[^"]*"/bnotdoc
- s/^\(.*<<-*[ ]*\)[\\'"]*\([A-Za-z0-9_][A-Za-z0-9_]*\)['"]*/<\2>\1\2/
- :hered
- N
- /^<\([^>]*\)>.*\n[ ]*\1[ ]*$/!{
- s/\n.*$//
- bhered
- }
- s/^<[^>]*>//
- s/\n.*$//
-}
-:notdoc
-
-# one-liner "(...) &&"
-/^[ ]*!*[ ]*(..*)[ ]*&&[ ]*$/boneline
-
-# same as above but without trailing "&&"
-/^[ ]*!*[ ]*(..*)[ ]*$/boneline
-
-# one-liner "(...) >x" (or "2>x" or "<x" or "|x" or "&"
-/^[ ]*!*[ ]*(..*)[ ]*[0-9]*[<>|&]/boneline
-
-# multi-line "(...\n...)"
-/^[ ]*(/bsubsh
-
-# innocuous line -- print it and advance to next line
-b
-
-# found one-liner "(...)" -- mark suspect if it uses ";" internally rather than
-# "&&" (but not ";" in a string)
-:oneline
-/;/{
- /"[^"]*;[^"]*"/!s/;/; ?!AMP?!/
-}
-b
-
-:subsh
-# bare "(" line? -- stash for later printing
-/^[ ]*([ ]*$/ {
- h
- bnextln
-}
-# "(..." line -- "(" opening subshell cuddled with command; temporarily replace
-# "(" with sentinel "^" and process the line as if "(" had been seen solo on
-# the preceding line; this temporary replacement prevents several rules from
-# accidentally thinking "(" introduces a nested subshell; "^" is changed back
-# to "(" at output time
-x
-s/.*//
-x
-s/(/^/
-bslurp
-
-:nextln
-N
-s/.*\n//
-
-:slurp
-# incomplete line "...\"
-/\\$/bicmplte
-# multi-line quoted string "...\n..."?
-/"/bdqstr
-# multi-line quoted string '...\n...'? (but not contraction in string "it's")
-/'/{
- /"[^'"]*'[^'"]*"/!bsqstr
-}
-:folded
-# here-doc -- swallow it (but not "<<" in a string)
-/<<-*[ ]*[\\'"]*[A-Za-z0-9_]/{
- /"[^"]*<<[^"]*"/!bheredoc
-}
-# comment or empty line -- discard since final non-comment, non-empty line
-# before closing ")", "done", "elsif", "else", or "fi" will need to be
-# re-visited to drop "suspect" marking since final line of those constructs
-# legitimately lacks "&&", so "suspect" mark must be removed
-/^[ ]*#/bnextln
-/^[ ]*$/bnextln
-# in-line comment -- strip it (but not "#" in a string, Bash ${#...} array
-# length, or Perforce "//depot/path#42" revision in filespec)
-/[ ]#/{
- /"[^"]*#[^"]*"/!s/[ ]#.*$//
-}
-# one-liner "case ... esac"
-/^[ ^]*case[ ]*..*esac/bchkchn
-# multi-line "case ... esac"
-/^[ ^]*case[ ]..*[ ]in/bcase
-# multi-line "for ... done" or "while ... done"
-/^[ ^]*for[ ]..*[ ]in/bcont
-/^[ ^]*while[ ]/bcont
-/^[ ]*do[ ]/bcont
-/^[ ]*do[ ]*$/bcont
-/;[ ]*do/bcont
-/^[ ]*done[ ]*&&[ ]*$/bdone
-/^[ ]*done[ ]*$/bdone
-/^[ ]*done[ ]*[<>|]/bdone
-/^[ ]*done[ ]*)/bdone
-/||[ ]*exit[ ]/bcont
-/||[ ]*exit[ ]*$/bcont
-# multi-line "if...elsif...else...fi"
-/^[ ^]*if[ ]/bcont
-/^[ ]*then[ ]/bcont
-/^[ ]*then[ ]*$/bcont
-/;[ ]*then/bcont
-/^[ ]*elif[ ]/belse
-/^[ ]*elif[ ]*$/belse
-/^[ ]*else[ ]/belse
-/^[ ]*else[ ]*$/belse
-/^[ ]*fi[ ]*&&[ ]*$/bdone
-/^[ ]*fi[ ]*$/bdone
-/^[ ]*fi[ ]*[<>|]/bdone
-/^[ ]*fi[ ]*)/bdone
-# nested one-liner "(...) &&"
-/^[ ^]*(.*)[ ]*&&[ ]*$/bchkchn
-# nested one-liner "(...)"
-/^[ ^]*(.*)[ ]*$/bchkchn
-# nested one-liner "(...) >x" (or "2>x" or "<x" or "|x")
-/^[ ^]*(.*)[ ]*[0-9]*[<>|]/bchkchn
-# nested multi-line "(...\n...)"
-/^[ ^]*(/bnest
-# multi-line "{...\n...}"
-/^[ ^]*{/bblock
-# closing ")" on own line -- exit subshell
-/^[ ]*)/bclssolo
-# "$((...))" -- arithmetic expansion; not closing ")"
-/\$(([^)][^)]*))[^)]*$/bchkchn
-# "$(...)" -- command substitution; not closing ")"
-/\$([^)][^)]*)[^)]*$/bchkchn
-# multi-line "$(...\n...)" -- command substitution; treat as nested subshell
-/\$([^)]*$/bnest
-# "=(...)" -- Bash array assignment; not closing ")"
-/=(/bchkchn
-# closing "...) &&"
-/)[ ]*&&[ ]*$/bclose
-# closing "...)"
-/)[ ]*$/bclose
-# closing "...) >x" (or "2>x" or "<x" or "|x")
-/)[ ]*[<>|]/bclose
-:chkchn
-# mark suspect if line uses ";" internally rather than "&&" (but not ";" in a
-# string and not ";;" in one-liner "case...esac")
-/;/{
- /;;/!{
- /"[^"]*;[^"]*"/!s/;/; ?!AMP?!/
- }
-}
-# line ends with pipe "...|" -- valid; not missing "&&"
-/|[ ]*$/bcont
-# missing end-of-line "&&" -- mark suspect
-/&&[ ]*$/!s/$/ ?!AMP?!/
-:cont
-# retrieve and print previous line
-x
-s/^\([ ]*\)^/\1(/
-s/?!HERE?!/<</g
-n
-bslurp
-
-# found incomplete line "...\" -- slurp up next line
-:icmplte
-N
-s/\\\n//
-bslurp
-
-# check for multi-line double-quoted string "...\n..." -- fold to one line
-:dqstr
-# remove all quote pairs
-s/"\([^"]*\)"/@!\1@!/g
-# done if no dangling quote
-/"/!bdqdone
-# otherwise, slurp next line and try again
-N
-s/\n//
-bdqstr
-:dqdone
-s/@!/"/g
-bfolded
-
-# check for multi-line single-quoted string '...\n...' -- fold to one line
-:sqstr
-# remove all quote pairs
-s/'\([^']*\)'/@!\1@!/g
-# done if no dangling quote
-/'/!bsqdone
-# otherwise, slurp next line and try again
-N
-s/\n//
-bsqstr
-:sqdone
-s/@!/'/g
-bfolded
-
-# found here-doc -- swallow it to avoid false hits within its body (but keep
-# the command to which it was attached)
-:heredoc
-s/^\(.*\)<<\(-*[ ]*\)[\\'"]*\([A-Za-z0-9_][A-Za-z0-9_]*\)['"]*/<\3>\1?!HERE?!\2\3/
-:hdocsub
-N
-/^<\([^>]*\)>.*\n[ ]*\1[ ]*$/!{
- s/\n.*$//
- bhdocsub
-}
-s/^<[^>]*>//
-s/\n.*$//
-bfolded
-
-# found "case ... in" -- pass through untouched
-:case
-x
-s/^\([ ]*\)^/\1(/
-s/?!HERE?!/<</g
-n
-:cascom
-/^[ ]*#/{
- N
- s/.*\n//
- bcascom
-}
-/^[ ]*esac/bslurp
-bcase
-
-# found "else" or "elif" -- drop "suspect" from final line before "else" since
-# that line legitimately lacks "&&"
-:else
-x
-s/\( ?!AMP?!\)* ?!AMP?!$//
-x
-bcont
-
-# found "done" closing for-loop or while-loop, or "fi" closing if-then -- drop
-# "suspect" from final contained line since that line legitimately lacks "&&"
-:done
-x
-s/\( ?!AMP?!\)* ?!AMP?!$//
-x
-# is 'done' or 'fi' cuddled with ")" to close subshell?
-/done.*)/bclose
-/fi.*)/bclose
-bchkchn
-
-# found nested multi-line "(...\n...)" -- pass through untouched
-:nest
-x
-:nstslrp
-s/^\([ ]*\)^/\1(/
-s/?!HERE?!/<</g
-n
-:nstcom
-# comment -- not closing ")" if in comment
-/^[ ]*#/{
- N
- s/.*\n//
- bnstcom
-}
-# closing ")" on own line -- stop nested slurp
-/^[ ]*)/bnstcl
-# "$((...))" -- arithmetic expansion; not closing ")"
-/\$(([^)][^)]*))[^)]*$/bnstcnt
-# "$(...)" -- command substitution; not closing ")"
-/\$([^)][^)]*)[^)]*$/bnstcnt
-# closing "...)" -- stop nested slurp
-/)/bnstcl
-:nstcnt
-x
-bnstslrp
-:nstcl
-# is it "))" which closes nested and parent subshells?
-/)[ ]*)/bslurp
-bchkchn
-
-# found multi-line "{...\n...}" block -- pass through untouched
-:block
-x
-s/^\([ ]*\)^/\1(/
-s/?!HERE?!/<</g
-n
-:blkcom
-/^[ ]*#/{
- N
- s/.*\n//
- bblkcom
-}
-# closing "}" -- stop block slurp
-/}/bchkchn
-bblock
-
-# found closing ")" on own line -- drop "suspect" from final line of subshell
-# since that line legitimately lacks "&&" and exit subshell loop
-:clssolo
-x
-s/\( ?!AMP?!\)* ?!AMP?!$//
-s/^\([ ]*\)^/\1(/
-s/?!HERE?!/<</g
-p
-x
-s/^\([ ]*\)^/\1(/
-s/?!HERE?!/<</g
-b
-
-# found closing "...)" -- exit subshell loop
-:close
-x
-s/^\([ ]*\)^/\1(/
-s/?!HERE?!/<</g
-p
-x
-s/^\([ ]*\)^/\1(/
-s/?!HERE?!/<</g
-b
diff --git a/t/chainlint/blank-line-before-esac.expect b/t/chainlint/blank-line-before-esac.expect
new file mode 100644
index 0000000..48ed4eb
--- /dev/null
+++ b/t/chainlint/blank-line-before-esac.expect
@@ -0,0 +1,18 @@
+test_done ( ) {
+ case "$test_failure" in
+ 0 )
+ test_at_end_hook_
+
+ exit 0 ;;
+
+ * )
+ if test $test_external_has_tap -eq 0
+ then
+ say_color error "# failed $test_failure among $msg"
+ say "1..$test_count"
+ fi
+
+ exit 1 ;;
+
+ esac
+}
diff --git a/t/chainlint/blank-line-before-esac.test b/t/chainlint/blank-line-before-esac.test
new file mode 100644
index 0000000..cecccad
--- /dev/null
+++ b/t/chainlint/blank-line-before-esac.test
@@ -0,0 +1,19 @@
+# LINT: blank line before "esac"
+test_done () {
+ case "$test_failure" in
+ 0)
+ test_at_end_hook_
+
+ exit 0 ;;
+
+ *)
+ if test $test_external_has_tap -eq 0
+ then
+ say_color error "# failed $test_failure among $msg"
+ say "1..$test_count"
+ fi
+
+ exit 1 ;;
+
+ esac
+}
diff --git a/t/chainlint/block-comment.expect b/t/chainlint/block-comment.expect
index d10b2ee..df2beea 100644
--- a/t/chainlint/block-comment.expect
+++ b/t/chainlint/block-comment.expect
@@ -1,6 +1,8 @@
(
{
+ # show a
echo a &&
+ # show b
echo b
}
)
diff --git a/t/chainlint/block.expect b/t/chainlint/block.expect
index da60257..a3bcea4 100644
--- a/t/chainlint/block.expect
+++ b/t/chainlint/block.expect
@@ -1,7 +1,7 @@
(
foo &&
{
- echo a
+ echo a ?!AMP?!
echo b
} &&
bar &&
@@ -9,4 +9,15 @@
echo c
} ?!AMP?!
baz
-)
+) &&
+
+{
+ echo a ; ?!AMP?! echo b
+} &&
+{ echo a ; ?!AMP?! echo b ; } &&
+
+{
+ echo "${var}9" &&
+ echo "done"
+} &&
+finis
diff --git a/t/chainlint/block.test b/t/chainlint/block.test
index 0a82fd5..4ab69a4 100644
--- a/t/chainlint/block.test
+++ b/t/chainlint/block.test
@@ -11,4 +11,17 @@
echo c
}
baz
-)
+) &&
+
+# LINT: ";" not allowed in place of "&&"
+{
+ echo a; echo b
+} &&
+{ echo a; echo b; } &&
+
+# LINT: "}" inside string not mistaken as end of block
+{
+ echo "${var}9" &&
+ echo "done"
+} &&
+finis
diff --git a/t/chainlint/case-comment.expect b/t/chainlint/case-comment.expect
index 1e4b054..641c157 100644
--- a/t/chainlint/case-comment.expect
+++ b/t/chainlint/case-comment.expect
@@ -1,7 +1,10 @@
(
case "$x" in
+ # found foo
x) foo ;;
+ # found other
*)
+ # treat it as bar
bar
;;
esac
diff --git a/t/chainlint/chain-break-background.expect b/t/chainlint/chain-break-background.expect
new file mode 100644
index 0000000..28f9114
--- /dev/null
+++ b/t/chainlint/chain-break-background.expect
@@ -0,0 +1,9 @@
+JGIT_DAEMON_PID= &&
+git init --bare empty.git &&
+> empty.git/git-daemon-export-ok &&
+mkfifo jgit_daemon_output &&
+{
+ jgit daemon --port="$JGIT_DAEMON_PORT" . > jgit_daemon_output &
+ JGIT_DAEMON_PID=$!
+} &&
+test_expect_code 2 git ls-remote --exit-code git://localhost:$JGIT_DAEMON_PORT/empty.git
diff --git a/t/chainlint/chain-break-background.test b/t/chainlint/chain-break-background.test
new file mode 100644
index 0000000..e10f656
--- /dev/null
+++ b/t/chainlint/chain-break-background.test
@@ -0,0 +1,10 @@
+JGIT_DAEMON_PID= &&
+git init --bare empty.git &&
+>empty.git/git-daemon-export-ok &&
+mkfifo jgit_daemon_output &&
+{
+# LINT: exit status of "&" is always 0 so &&-chaining immaterial
+ jgit daemon --port="$JGIT_DAEMON_PORT" . >jgit_daemon_output &
+ JGIT_DAEMON_PID=$!
+} &&
+test_expect_code 2 git ls-remote --exit-code git://localhost:$JGIT_DAEMON_PORT/empty.git
diff --git a/t/chainlint/chain-break-continue.expect b/t/chainlint/chain-break-continue.expect
new file mode 100644
index 0000000..47a3457
--- /dev/null
+++ b/t/chainlint/chain-break-continue.expect
@@ -0,0 +1,12 @@
+git ls-tree --name-only -r refs/notes/many_notes |
+while read path
+do
+ test "$path" = "foobar/non-note.txt" && continue
+ test "$path" = "deadbeef" && continue
+ test "$path" = "de/adbeef" && continue
+
+ if test $(expr length "$path") -ne $hexsz
+ then
+ return 1
+ fi
+done
diff --git a/t/chainlint/chain-break-continue.test b/t/chainlint/chain-break-continue.test
new file mode 100644
index 0000000..f0af71d
--- /dev/null
+++ b/t/chainlint/chain-break-continue.test
@@ -0,0 +1,13 @@
+git ls-tree --name-only -r refs/notes/many_notes |
+while read path
+do
+# LINT: broken &&-chain okay if explicit "continue"
+ test "$path" = "foobar/non-note.txt" && continue
+ test "$path" = "deadbeef" && continue
+ test "$path" = "de/adbeef" && continue
+
+ if test $(expr length "$path") -ne $hexsz
+ then
+ return 1
+ fi
+done
diff --git a/t/chainlint/chain-break-false.expect b/t/chainlint/chain-break-false.expect
new file mode 100644
index 0000000..989766f
--- /dev/null
+++ b/t/chainlint/chain-break-false.expect
@@ -0,0 +1,9 @@
+if condition not satisified
+then
+ echo it did not work...
+ echo failed!
+ false
+else
+ echo it went okay ?!AMP?!
+ congratulate user
+fi
diff --git a/t/chainlint/chain-break-false.test b/t/chainlint/chain-break-false.test
new file mode 100644
index 0000000..a5aaff8
--- /dev/null
+++ b/t/chainlint/chain-break-false.test
@@ -0,0 +1,10 @@
+# LINT: broken &&-chain okay if explicit "false" signals failure
+if condition not satisified
+then
+ echo it did not work...
+ echo failed!
+ false
+else
+ echo it went okay
+ congratulate user
+fi
diff --git a/t/chainlint/chain-break-return-exit.expect b/t/chainlint/chain-break-return-exit.expect
new file mode 100644
index 0000000..1732d22
--- /dev/null
+++ b/t/chainlint/chain-break-return-exit.expect
@@ -0,0 +1,19 @@
+case "$(git ls-files)" in
+one ) echo pass one ;;
+* ) echo bad one ; return 1 ;;
+esac &&
+(
+ case "$(git ls-files)" in
+ two ) echo pass two ;;
+ * ) echo bad two ; exit 1 ;;
+esac
+) &&
+case "$(git ls-files)" in
+dir/two"$LF"one ) echo pass both ;;
+* ) echo bad ; return 1 ;;
+esac &&
+
+for i in 1 2 3 4 ; do
+ git checkout main -b $i || return $?
+ test_commit $i $i $i tag$i || return $?
+done
diff --git a/t/chainlint/chain-break-return-exit.test b/t/chainlint/chain-break-return-exit.test
new file mode 100644
index 0000000..46542ed
--- /dev/null
+++ b/t/chainlint/chain-break-return-exit.test
@@ -0,0 +1,23 @@
+case "$(git ls-files)" in
+one) echo pass one ;;
+# LINT: broken &&-chain okay if explicit "return 1" signals failuire
+*) echo bad one; return 1 ;;
+esac &&
+(
+ case "$(git ls-files)" in
+ two) echo pass two ;;
+# LINT: broken &&-chain okay if explicit "exit 1" signals failuire
+ *) echo bad two; exit 1 ;;
+ esac
+) &&
+case "$(git ls-files)" in
+dir/two"$LF"one) echo pass both ;;
+# LINT: broken &&-chain okay if explicit "return 1" signals failuire
+*) echo bad; return 1 ;;
+esac &&
+
+for i in 1 2 3 4 ; do
+# LINT: broken &&-chain okay if explicit "return $?" signals failure
+ git checkout main -b $i || return $?
+ test_commit $i $i $i tag$i || return $?
+done
diff --git a/t/chainlint/chain-break-status.expect b/t/chainlint/chain-break-status.expect
new file mode 100644
index 0000000..f4bada9
--- /dev/null
+++ b/t/chainlint/chain-break-status.expect
@@ -0,0 +1,9 @@
+OUT=$(( ( large_git ; echo $? 1 >& 3 ) | : ) 3 >& 1) &&
+test_match_signal 13 "$OUT" &&
+
+{ test-tool sigchain > actual ; ret=$? ; } &&
+{
+ test_match_signal 15 "$ret" ||
+ test "$ret" = 3
+} &&
+test_cmp expect actual
diff --git a/t/chainlint/chain-break-status.test b/t/chainlint/chain-break-status.test
new file mode 100644
index 0000000..a6602a7
--- /dev/null
+++ b/t/chainlint/chain-break-status.test
@@ -0,0 +1,11 @@
+# LINT: broken &&-chain okay if next command handles "$?" explicitly
+OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 ) &&
+test_match_signal 13 "$OUT" &&
+
+# LINT: broken &&-chain okay if next command handles "$?" explicitly
+{ test-tool sigchain >actual; ret=$?; } &&
+{
+ test_match_signal 15 "$ret" ||
+ test "$ret" = 3
+} &&
+test_cmp expect actual
diff --git a/t/chainlint/chained-block.expect b/t/chainlint/chained-block.expect
new file mode 100644
index 0000000..574cdce
--- /dev/null
+++ b/t/chainlint/chained-block.expect
@@ -0,0 +1,9 @@
+echo nobody home && {
+ test the doohicky ?!AMP?!
+ right now
+} &&
+
+GIT_EXTERNAL_DIFF=echo git diff | {
+ read path oldfile oldhex oldmode newfile newhex newmode &&
+ test "z$oh" = "z$oldhex"
+}
diff --git a/t/chainlint/chained-block.test b/t/chainlint/chained-block.test
new file mode 100644
index 0000000..86f81ec
--- /dev/null
+++ b/t/chainlint/chained-block.test
@@ -0,0 +1,11 @@
+# LINT: start of block chained to preceding command
+echo nobody home && {
+ test the doohicky
+ right now
+} &&
+
+# LINT: preceding command pipes to block on same line
+GIT_EXTERNAL_DIFF=echo git diff | {
+ read path oldfile oldhex oldmode newfile newhex newmode &&
+ test "z$oh" = "z$oldhex"
+}
diff --git a/t/chainlint/chained-subshell.expect b/t/chainlint/chained-subshell.expect
new file mode 100644
index 0000000..af0369d
--- /dev/null
+++ b/t/chainlint/chained-subshell.expect
@@ -0,0 +1,10 @@
+mkdir sub && (
+ cd sub &&
+ foo the bar ?!AMP?!
+ nuff said
+) &&
+
+cut "-d " -f actual | ( read s1 s2 s3 &&
+test -f $s1 ?!AMP?!
+test $(cat $s2) = tree2path1 &&
+test $(cat $s3) = tree3path1 )
diff --git a/t/chainlint/chained-subshell.test b/t/chainlint/chained-subshell.test
new file mode 100644
index 0000000..4ff6ddd
--- /dev/null
+++ b/t/chainlint/chained-subshell.test
@@ -0,0 +1,13 @@
+# LINT: start of subshell chained to preceding command
+mkdir sub && (
+ cd sub &&
+ foo the bar
+ nuff said
+) &&
+
+# LINT: preceding command pipes to subshell on same line
+cut "-d " -f actual | (read s1 s2 s3 &&
+test -f $s1
+test $(cat $s2) = tree2path1 &&
+# LINT: closing subshell ")" correctly detected on same line as "$(...)"
+test $(cat $s3) = tree3path1)
diff --git a/t/chainlint/close-subshell.expect b/t/chainlint/close-subshell.expect
index 0f87db9..2192a28 100644
--- a/t/chainlint/close-subshell.expect
+++ b/t/chainlint/close-subshell.expect
@@ -15,7 +15,8 @@
) | wuzzle &&
(
bop
-) | fazz fozz &&
+) | fazz \
+ fozz &&
(
bup
) |
diff --git a/t/chainlint/command-substitution-subsubshell.expect b/t/chainlint/command-substitution-subsubshell.expect
new file mode 100644
index 0000000..ab2f79e
--- /dev/null
+++ b/t/chainlint/command-substitution-subsubshell.expect
@@ -0,0 +1,2 @@
+OUT=$(( ( large_git 1 >& 3 ) | : ) 3 >& 1) &&
+test_match_signal 13 "$OUT"
diff --git a/t/chainlint/command-substitution-subsubshell.test b/t/chainlint/command-substitution-subsubshell.test
new file mode 100644
index 0000000..321de29
--- /dev/null
+++ b/t/chainlint/command-substitution-subsubshell.test
@@ -0,0 +1,3 @@
+# LINT: subshell nested in subshell nested in command substitution
+OUT=$( ((large_git 1>&3) | :) 3>&1 ) &&
+test_match_signal 13 "$OUT"
diff --git a/t/chainlint/comment.expect b/t/chainlint/comment.expect
index f76fde1..a68f1f9 100644
--- a/t/chainlint/comment.expect
+++ b/t/chainlint/comment.expect
@@ -1,4 +1,8 @@
(
+ # comment 1
nothing &&
+ # comment 2
something
+ # comment 3
+ # comment 4
)
diff --git a/t/chainlint/complex-if-in-cuddled-loop.expect b/t/chainlint/complex-if-in-cuddled-loop.expect
index 2fca183..dac2d0f 100644
--- a/t/chainlint/complex-if-in-cuddled-loop.expect
+++ b/t/chainlint/complex-if-in-cuddled-loop.expect
@@ -4,6 +4,6 @@
:
else
echo >file
- fi
+ fi ?!LOOP?!
done) &&
test ! -f file
diff --git a/t/chainlint/double-here-doc.expect b/t/chainlint/double-here-doc.expect
new file mode 100644
index 0000000..cd584a4
--- /dev/null
+++ b/t/chainlint/double-here-doc.expect
@@ -0,0 +1,12 @@
+run_sub_test_lib_test_err run-inv-range-start \
+ "--run invalid range start" \
+ --run="a-5" <<-\EOF &&
+test_expect_success "passing test #1" "true"
+test_done
+EOF
+check_sub_test_lib_test_err run-inv-range-start \
+ <<-\EOF_OUT 3<<-EOF_ERR
+> FATAL: Unexpected exit with code 1
+EOF_OUT
+> error: --run: invalid non-numeric in range start: ${SQ}a-5${SQ}
+EOF_ERR
diff --git a/t/chainlint/double-here-doc.test b/t/chainlint/double-here-doc.test
new file mode 100644
index 0000000..cd584a4
--- /dev/null
+++ b/t/chainlint/double-here-doc.test
@@ -0,0 +1,12 @@
+run_sub_test_lib_test_err run-inv-range-start \
+ "--run invalid range start" \
+ --run="a-5" <<-\EOF &&
+test_expect_success "passing test #1" "true"
+test_done
+EOF
+check_sub_test_lib_test_err run-inv-range-start \
+ <<-\EOF_OUT 3<<-EOF_ERR
+> FATAL: Unexpected exit with code 1
+EOF_OUT
+> error: --run: invalid non-numeric in range start: ${SQ}a-5${SQ}
+EOF_ERR
diff --git a/t/chainlint/dqstring-line-splice.expect b/t/chainlint/dqstring-line-splice.expect
new file mode 100644
index 0000000..bf9ced6
--- /dev/null
+++ b/t/chainlint/dqstring-line-splice.expect
@@ -0,0 +1,3 @@
+echo 'fatal: reword option of --fixup is mutually exclusive with' '--patch/--interactive/--all/--include/--only' > expect &&
+test_must_fail git commit --fixup=reword:HEAD~ $1 2 > actual &&
+test_cmp expect actual
diff --git a/t/chainlint/dqstring-line-splice.test b/t/chainlint/dqstring-line-splice.test
new file mode 100644
index 0000000..b407144
--- /dev/null
+++ b/t/chainlint/dqstring-line-splice.test
@@ -0,0 +1,7 @@
+# LINT: line-splice within DQ-string
+'"
+echo 'fatal: reword option of --fixup is mutually exclusive with'\
+ '--patch/--interactive/--all/--include/--only' >expect &&
+test_must_fail git commit --fixup=reword:HEAD~ $1 2>actual &&
+test_cmp expect actual
+"'
diff --git a/t/chainlint/dqstring-no-interpolate.expect b/t/chainlint/dqstring-no-interpolate.expect
new file mode 100644
index 0000000..1072498
--- /dev/null
+++ b/t/chainlint/dqstring-no-interpolate.expect
@@ -0,0 +1,11 @@
+grep "^ ! [rejected][ ]*$BRANCH -> $BRANCH (non-fast-forward)$" out &&
+
+grep "^\.git$" output.txt &&
+
+
+(
+ cd client$version &&
+ GIT_TEST_PROTOCOL_VERSION=$version git fetch-pack --no-progress .. $(cat ../input)
+) > output &&
+ cut -d ' ' -f 2 < output | sort > actual &&
+ test_cmp expect actual
diff --git a/t/chainlint/dqstring-no-interpolate.test b/t/chainlint/dqstring-no-interpolate.test
new file mode 100644
index 0000000..d2f4219
--- /dev/null
+++ b/t/chainlint/dqstring-no-interpolate.test
@@ -0,0 +1,15 @@
+# LINT: regex dollar-sign eol anchor in double-quoted string not special
+grep "^ ! \[rejected\][ ]*$BRANCH -> $BRANCH (non-fast-forward)$" out &&
+
+# LINT: escaped "$" not mistaken for variable expansion
+grep "^\\.git\$" output.txt &&
+
+'"
+(
+ cd client$version &&
+# LINT: escaped dollar-sign in double-quoted test body
+ GIT_TEST_PROTOCOL_VERSION=$version git fetch-pack --no-progress .. \$(cat ../input)
+) >output &&
+ cut -d ' ' -f 2 <output | sort >actual &&
+ test_cmp expect actual
+"'
diff --git a/t/chainlint/empty-here-doc.expect b/t/chainlint/empty-here-doc.expect
new file mode 100644
index 0000000..e8733c9
--- /dev/null
+++ b/t/chainlint/empty-here-doc.expect
@@ -0,0 +1,4 @@
+git ls-tree $tree path > current &&
+cat > expected <<\EOF &&
+EOF
+test_output
diff --git a/t/chainlint/empty-here-doc.test b/t/chainlint/empty-here-doc.test
new file mode 100644
index 0000000..24fc165
--- /dev/null
+++ b/t/chainlint/empty-here-doc.test
@@ -0,0 +1,5 @@
+git ls-tree $tree path >current &&
+# LINT: empty here-doc
+cat >expected <<\EOF &&
+EOF
+test_output
diff --git a/t/chainlint/exclamation.expect b/t/chainlint/exclamation.expect
new file mode 100644
index 0000000..2d961a5
--- /dev/null
+++ b/t/chainlint/exclamation.expect
@@ -0,0 +1,4 @@
+if ! condition ; then echo nope ; else yep ; fi &&
+test_prerequisite !MINGW &&
+mail uucp!address &&
+echo !whatever!
diff --git a/t/chainlint/exclamation.test b/t/chainlint/exclamation.test
new file mode 100644
index 0000000..323595b
--- /dev/null
+++ b/t/chainlint/exclamation.test
@@ -0,0 +1,8 @@
+# LINT: "! word" is two tokens
+if ! condition; then echo nope; else yep; fi &&
+# LINT: "!word" is single token, not two tokens "!" and "word"
+test_prerequisite !MINGW &&
+# LINT: "word!word" is single token, not three tokens "word", "!", and "word"
+mail uucp!address &&
+# LINT: "!word!" is single token, not three tokens "!", "word", and "!"
+echo !whatever!
diff --git a/t/chainlint/for-loop-abbreviated.expect b/t/chainlint/for-loop-abbreviated.expect
new file mode 100644
index 0000000..a21007a
--- /dev/null
+++ b/t/chainlint/for-loop-abbreviated.expect
@@ -0,0 +1,5 @@
+for it
+do
+ path=$(expr "$it" : ( [^:]*) ) &&
+ git update-index --add "$path" || exit
+done
diff --git a/t/chainlint/for-loop-abbreviated.test b/t/chainlint/for-loop-abbreviated.test
new file mode 100644
index 0000000..1084ecc
--- /dev/null
+++ b/t/chainlint/for-loop-abbreviated.test
@@ -0,0 +1,6 @@
+# LINT: for-loop lacking optional "in [word...]" before "do"
+for it
+do
+ path=$(expr "$it" : '\([^:]*\)') &&
+ git update-index --add "$path" || exit
+done
diff --git a/t/chainlint/for-loop.expect b/t/chainlint/for-loop.expect
index 6671b8c..d65c821 100644
--- a/t/chainlint/for-loop.expect
+++ b/t/chainlint/for-loop.expect
@@ -2,10 +2,12 @@
for i in a b c
do
echo $i ?!AMP?!
- cat <<-EOF
+ cat <<-\EOF ?!LOOP?!
+ bar
+ EOF
done ?!AMP?!
for i in a b c; do
echo $i &&
- cat $i
+ cat $i ?!LOOP?!
done
)
diff --git a/t/chainlint/function.expect b/t/chainlint/function.expect
new file mode 100644
index 0000000..a14388e
--- /dev/null
+++ b/t/chainlint/function.expect
@@ -0,0 +1,11 @@
+sha1_file ( ) {
+ echo "$*" | sed "s#..#.git/objects/&/#"
+} &&
+
+remove_object ( ) {
+ file=$(sha1_file "$*") &&
+ test -e "$file" ?!AMP?!
+ rm -f "$file"
+} ?!AMP?!
+
+sha1_file arg && remove_object arg
diff --git a/t/chainlint/function.test b/t/chainlint/function.test
new file mode 100644
index 0000000..5ee5956
--- /dev/null
+++ b/t/chainlint/function.test
@@ -0,0 +1,13 @@
+# LINT: "()" in function definition not mistaken for subshell
+sha1_file() {
+ echo "$*" | sed "s#..#.git/objects/&/#"
+} &&
+
+# LINT: broken &&-chain in function and after function
+remove_object() {
+ file=$(sha1_file "$*") &&
+ test -e "$file"
+ rm -f "$file"
+}
+
+sha1_file arg && remove_object arg
diff --git a/t/chainlint/here-doc-close-subshell.expect b/t/chainlint/here-doc-close-subshell.expect
index 2af9ced..7d9c2b5 100644
--- a/t/chainlint/here-doc-close-subshell.expect
+++ b/t/chainlint/here-doc-close-subshell.expect
@@ -1,2 +1,4 @@
(
- cat <<-INPUT)
+ cat <<-\INPUT)
+ fizz
+ INPUT
diff --git a/t/chainlint/here-doc-indent-operator.expect b/t/chainlint/here-doc-indent-operator.expect
new file mode 100644
index 0000000..f92a7ce
--- /dev/null
+++ b/t/chainlint/here-doc-indent-operator.expect
@@ -0,0 +1,11 @@
+cat >expect <<- EOF &&
+header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0
+num_commits: $1
+chunks: oid_fanout oid_lookup commit_metadata generation_data bloom_indexes bloom_data
+EOF
+
+cat >expect << -EOF ?!AMP?!
+this is not indented
+-EOF
+
+cleanup
diff --git a/t/chainlint/here-doc-indent-operator.test b/t/chainlint/here-doc-indent-operator.test
new file mode 100644
index 0000000..c8a6f18
--- /dev/null
+++ b/t/chainlint/here-doc-indent-operator.test
@@ -0,0 +1,13 @@
+# LINT: whitespace between operator "<<-" and tag legal
+cat >expect <<- EOF &&
+header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0
+num_commits: $1
+chunks: oid_fanout oid_lookup commit_metadata generation_data bloom_indexes bloom_data
+EOF
+
+# LINT: not an indented here-doc; just a plain here-doc with tag named "-EOF"
+cat >expect << -EOF
+this is not indented
+-EOF
+
+cleanup
diff --git a/t/chainlint/here-doc-multi-line-command-subst.expect b/t/chainlint/here-doc-multi-line-command-subst.expect
index f8b3aa7..b7364c8 100644
--- a/t/chainlint/here-doc-multi-line-command-subst.expect
+++ b/t/chainlint/here-doc-multi-line-command-subst.expect
@@ -1,5 +1,8 @@
(
- x=$(bobble <<-END &&
+ x=$(bobble <<-\END &&
+ fossil
+ vegetable
+ END
wiffle) ?!AMP?!
echo $x
)
diff --git a/t/chainlint/here-doc-multi-line-string.expect b/t/chainlint/here-doc-multi-line-string.expect
index 2578191..6c13bdc 100644
--- a/t/chainlint/here-doc-multi-line-string.expect
+++ b/t/chainlint/here-doc-multi-line-string.expect
@@ -1,4 +1,7 @@
(
- cat <<-TXT && echo "multi-line string" ?!AMP?!
+ cat <<-\TXT && echo "multi-line
+ string" ?!AMP?!
+ fizzle
+ TXT
bap
)
diff --git a/t/chainlint/here-doc.expect b/t/chainlint/here-doc.expect
index 110059b..1df3f78 100644
--- a/t/chainlint/here-doc.expect
+++ b/t/chainlint/here-doc.expect
@@ -1,7 +1,25 @@
-boodle wobba gorgo snoot wafta snurb <<EOF &&
+boodle wobba \
+ gorgo snoot \
+ wafta snurb <<EOF &&
+quoth the raven,
+nevermore...
+EOF
cat <<-Arbitrary_Tag_42 >foo &&
+snoz
+boz
+woz
+Arbitrary_Tag_42
-cat <<zump >boo &&
+cat <<"zump" >boo &&
+snoz
+boz
+woz
+zump
-horticulture <<EOF
+horticulture <<\EOF
+gomez
+morticia
+wednesday
+pugsly
+EOF
diff --git a/t/chainlint/if-condition-split.expect b/t/chainlint/if-condition-split.expect
new file mode 100644
index 0000000..ee745ef
--- /dev/null
+++ b/t/chainlint/if-condition-split.expect
@@ -0,0 +1,7 @@
+if bob &&
+ marcia ||
+ kevin
+then
+ echo "nomads" ?!AMP?!
+ echo "for sure"
+fi
diff --git a/t/chainlint/if-condition-split.test b/t/chainlint/if-condition-split.test
new file mode 100644
index 0000000..240daa9
--- /dev/null
+++ b/t/chainlint/if-condition-split.test
@@ -0,0 +1,8 @@
+# LINT: "if" condition split across multiple lines at "&&" or "||"
+if bob &&
+ marcia ||
+ kevin
+then
+ echo "nomads"
+ echo "for sure"
+fi
diff --git a/t/chainlint/if-in-loop.expect b/t/chainlint/if-in-loop.expect
index 03b82a3..d6514ae 100644
--- a/t/chainlint/if-in-loop.expect
+++ b/t/chainlint/if-in-loop.expect
@@ -3,7 +3,7 @@
do
if false
then
- echo "err" ?!AMP?!
+ echo "err"
exit 1
fi ?!AMP?!
foo
diff --git a/t/chainlint/if-in-loop.test b/t/chainlint/if-in-loop.test
index f0cf19c..90c2397 100644
--- a/t/chainlint/if-in-loop.test
+++ b/t/chainlint/if-in-loop.test
@@ -3,7 +3,7 @@
do
if false
then
-# LINT: missing "&&" on "echo"
+# LINT: missing "&&" on "echo" okay since "exit 1" signals error explicitly
echo "err"
exit 1
# LINT: missing "&&" on "fi"
diff --git a/t/chainlint/if-then-else.expect b/t/chainlint/if-then-else.expect
index 44d86c3..cbaaf85 100644
--- a/t/chainlint/if-then-else.expect
+++ b/t/chainlint/if-then-else.expect
@@ -8,7 +8,9 @@
echo foo
else
echo foo &&
- cat <<-EOF
+ cat <<-\EOF
+ bar
+ EOF
fi ?!AMP?!
echo poodle
) &&
diff --git a/t/chainlint/incomplete-line.expect b/t/chainlint/incomplete-line.expect
index ffac8f9..134d3a1 100644
--- a/t/chainlint/incomplete-line.expect
+++ b/t/chainlint/incomplete-line.expect
@@ -1,4 +1,10 @@
-line 1 line 2 line 3 line 4 &&
+line 1 \
+line 2 \
+line 3 \
+line 4 &&
(
- line 5 line 6 line 7 line 8
+ line 5 \
+ line 6 \
+ line 7 \
+ line 8
)
diff --git a/t/chainlint/inline-comment.expect b/t/chainlint/inline-comment.expect
index dd0dace..6bad218 100644
--- a/t/chainlint/inline-comment.expect
+++ b/t/chainlint/inline-comment.expect
@@ -1,6 +1,6 @@
(
- foobar &&
- barfoo ?!AMP?!
+ foobar && # comment 1
+ barfoo ?!AMP?! # wrong position for &&
flibble "not a # comment"
) &&
diff --git a/t/chainlint/loop-detect-failure.expect b/t/chainlint/loop-detect-failure.expect
new file mode 100644
index 0000000..a66025c
--- /dev/null
+++ b/t/chainlint/loop-detect-failure.expect
@@ -0,0 +1,15 @@
+git init r1 &&
+for n in 1 2 3 4 5
+do
+ echo "This is file: $n" > r1/file.$n &&
+ git -C r1 add file.$n &&
+ git -C r1 commit -m "$n" || return 1
+done &&
+
+git init r2 &&
+for n in 1000 10000
+do
+ printf "%"$n"s" X > r2/large.$n &&
+ git -C r2 add large.$n &&
+ git -C r2 commit -m "$n" ?!LOOP?!
+done
diff --git a/t/chainlint/loop-detect-failure.test b/t/chainlint/loop-detect-failure.test
new file mode 100644
index 0000000..b9791cc
--- /dev/null
+++ b/t/chainlint/loop-detect-failure.test
@@ -0,0 +1,17 @@
+git init r1 &&
+# LINT: loop handles failure explicitly with "|| return 1"
+for n in 1 2 3 4 5
+do
+ echo "This is file: $n" > r1/file.$n &&
+ git -C r1 add file.$n &&
+ git -C r1 commit -m "$n" || return 1
+done &&
+
+git init r2 &&
+# LINT: loop fails to handle failure explicitly with "|| return 1"
+for n in 1000 10000
+do
+ printf "%"$n"s" X > r2/large.$n &&
+ git -C r2 add large.$n &&
+ git -C r2 commit -m "$n"
+done
diff --git a/t/chainlint/loop-detect-status.expect b/t/chainlint/loop-detect-status.expect
new file mode 100644
index 0000000..24da9e8
--- /dev/null
+++ b/t/chainlint/loop-detect-status.expect
@@ -0,0 +1,18 @@
+( while test $i -le $blobcount
+do
+ printf "Generating blob $i/$blobcount\r" >& 2 &&
+ printf "blob\nmark :$i\ndata $blobsize\n" &&
+ #test-tool genrandom $i $blobsize &&
+ printf "%-${blobsize}s" $i &&
+ echo "M 100644 :$i $i" >> commit &&
+ i=$(($i+1)) ||
+ echo $? > exit-status
+done &&
+echo "commit refs/heads/main" &&
+echo "author A U Thor <author@email.com> 123456789 +0000" &&
+echo "committer C O Mitter <committer@email.com> 123456789 +0000" &&
+echo "data 5" &&
+echo ">2gb" &&
+cat commit ) |
+git fast-import --big-file-threshold=2 &&
+test ! -f exit-status
diff --git a/t/chainlint/loop-detect-status.test b/t/chainlint/loop-detect-status.test
new file mode 100644
index 0000000..1c6c23c
--- /dev/null
+++ b/t/chainlint/loop-detect-status.test
@@ -0,0 +1,19 @@
+# LINT: "$?" handled explicitly within loop body
+(while test $i -le $blobcount
+ do
+ printf "Generating blob $i/$blobcount\r" >&2 &&
+ printf "blob\nmark :$i\ndata $blobsize\n" &&
+ #test-tool genrandom $i $blobsize &&
+ printf "%-${blobsize}s" $i &&
+ echo "M 100644 :$i $i" >> commit &&
+ i=$(($i+1)) ||
+ echo $? > exit-status
+ done &&
+ echo "commit refs/heads/main" &&
+ echo "author A U Thor <author@email.com> 123456789 +0000" &&
+ echo "committer C O Mitter <committer@email.com> 123456789 +0000" &&
+ echo "data 5" &&
+ echo ">2gb" &&
+ cat commit) |
+git fast-import --big-file-threshold=2 &&
+test ! -f exit-status
diff --git a/t/chainlint/loop-in-if.expect b/t/chainlint/loop-in-if.expect
index e1be423..6c5d6e5 100644
--- a/t/chainlint/loop-in-if.expect
+++ b/t/chainlint/loop-in-if.expect
@@ -4,7 +4,7 @@
while true
do
echo "pop" ?!AMP?!
- echo "glup"
+ echo "glup" ?!LOOP?!
done ?!AMP?!
foo
fi ?!AMP?!
diff --git a/t/chainlint/loop-upstream-pipe.expect b/t/chainlint/loop-upstream-pipe.expect
new file mode 100644
index 0000000..0b82ecc
--- /dev/null
+++ b/t/chainlint/loop-upstream-pipe.expect
@@ -0,0 +1,10 @@
+(
+ git rev-list --objects --no-object-names base..loose |
+ while read oid
+ do
+ path="$objdir/$(test_oid_to_path "$oid")" &&
+ printf "%s %d\n" "$oid" "$(test-tool chmtime --get "$path")" ||
+ echo "object list generation failed for $oid"
+ done |
+ sort -k1
+) >expect &&
diff --git a/t/chainlint/loop-upstream-pipe.test b/t/chainlint/loop-upstream-pipe.test
new file mode 100644
index 0000000..efb77da
--- /dev/null
+++ b/t/chainlint/loop-upstream-pipe.test
@@ -0,0 +1,11 @@
+(
+ git rev-list --objects --no-object-names base..loose |
+ while read oid
+ do
+# LINT: "|| echo" signals failure in loop upstream of a pipe
+ path="$objdir/$(test_oid_to_path "$oid")" &&
+ printf "%s %d\n" "$oid" "$(test-tool chmtime --get "$path")" ||
+ echo "object list generation failed for $oid"
+ done |
+ sort -k1
+) >expect &&
diff --git a/t/chainlint/multi-line-string.expect b/t/chainlint/multi-line-string.expect
index ab0dadf..27ff952 100644
--- a/t/chainlint/multi-line-string.expect
+++ b/t/chainlint/multi-line-string.expect
@@ -1,9 +1,14 @@
(
- x="line 1 line 2 line 3" &&
- y="line 1 line2" ?!AMP?!
+ x="line 1
+ line 2
+ line 3" &&
+ y="line 1
+ line2" ?!AMP?!
foobar
) &&
(
- echo "xyz" "abc def ghi" &&
+ echo "xyz" "abc
+ def
+ ghi" &&
barfoo
)
diff --git a/t/chainlint/nested-here-doc.expect b/t/chainlint/nested-here-doc.expect
index e3bef63..29b3832 100644
--- a/t/chainlint/nested-here-doc.expect
+++ b/t/chainlint/nested-here-doc.expect
@@ -1,7 +1,30 @@
cat <<ARBITRARY >foop &&
+naddle
+fub <<EOF
+ nozzle
+ noodle
+EOF
+formp
+ARBITRARY
(
- cat <<-INPUT_END &&
- cat <<-EOT ?!AMP?!
+ cat <<-\INPUT_END &&
+ fish are mice
+ but geese go slow
+ data <<EOF
+ perl is lerp
+ and nothing else
+ EOF
+ toink
+ INPUT_END
+
+ cat <<-\EOT ?!AMP?!
+ text goes here
+ data <<EOF
+ data goes here
+ EOF
+ more test here
+ EOT
+
foobar
)
diff --git a/t/chainlint/nested-loop-detect-failure.expect b/t/chainlint/nested-loop-detect-failure.expect
new file mode 100644
index 0000000..4793a0e
--- /dev/null
+++ b/t/chainlint/nested-loop-detect-failure.expect
@@ -0,0 +1,31 @@
+for i in 0 1 2 3 4 5 6 7 8 9 ;
+do
+ for j in 0 1 2 3 4 5 6 7 8 9 ;
+ do
+ echo "$i$j" > "path$i$j" ?!LOOP?!
+ done ?!LOOP?!
+done &&
+
+for i in 0 1 2 3 4 5 6 7 8 9 ;
+do
+ for j in 0 1 2 3 4 5 6 7 8 9 ;
+ do
+ echo "$i$j" > "path$i$j" || return 1
+ done
+done &&
+
+for i in 0 1 2 3 4 5 6 7 8 9 ;
+do
+ for j in 0 1 2 3 4 5 6 7 8 9 ;
+ do
+ echo "$i$j" > "path$i$j" ?!LOOP?!
+ done || return 1
+done &&
+
+for i in 0 1 2 3 4 5 6 7 8 9 ;
+do
+ for j in 0 1 2 3 4 5 6 7 8 9 ;
+ do
+ echo "$i$j" > "path$i$j" || return 1
+ done || return 1
+done
diff --git a/t/chainlint/nested-loop-detect-failure.test b/t/chainlint/nested-loop-detect-failure.test
new file mode 100644
index 0000000..e6f0c1a
--- /dev/null
+++ b/t/chainlint/nested-loop-detect-failure.test
@@ -0,0 +1,35 @@
+# LINT: neither loop handles failure explicitly with "|| return 1"
+for i in 0 1 2 3 4 5 6 7 8 9;
+do
+ for j in 0 1 2 3 4 5 6 7 8 9;
+ do
+ echo "$i$j" >"path$i$j"
+ done
+done &&
+
+# LINT: inner loop handles failure explicitly with "|| return 1"
+for i in 0 1 2 3 4 5 6 7 8 9;
+do
+ for j in 0 1 2 3 4 5 6 7 8 9;
+ do
+ echo "$i$j" >"path$i$j" || return 1
+ done
+done &&
+
+# LINT: outer loop handles failure explicitly with "|| return 1"
+for i in 0 1 2 3 4 5 6 7 8 9;
+do
+ for j in 0 1 2 3 4 5 6 7 8 9;
+ do
+ echo "$i$j" >"path$i$j"
+ done || return 1
+done &&
+
+# LINT: inner & outer loops handles failure explicitly with "|| return 1"
+for i in 0 1 2 3 4 5 6 7 8 9;
+do
+ for j in 0 1 2 3 4 5 6 7 8 9;
+ do
+ echo "$i$j" >"path$i$j" || return 1
+ done || return 1
+done
diff --git a/t/chainlint/nested-subshell-comment.expect b/t/chainlint/nested-subshell-comment.expect
index be4b27a..9138cf3 100644
--- a/t/chainlint/nested-subshell-comment.expect
+++ b/t/chainlint/nested-subshell-comment.expect
@@ -2,6 +2,8 @@
foo &&
(
bar &&
+ # bottles wobble while fiddles gobble
+ # minor numbers of cows (or do they?)
baz &&
snaff
) ?!AMP?!
diff --git a/t/chainlint/nested-subshell.expect b/t/chainlint/nested-subshell.expect
index 41a48ad..02e0a9f 100644
--- a/t/chainlint/nested-subshell.expect
+++ b/t/chainlint/nested-subshell.expect
@@ -6,7 +6,7 @@
) >file &&
cd foo &&
(
- echo a
+ echo a ?!AMP?!
echo b
) >file
)
diff --git a/t/chainlint/one-liner-for-loop.expect b/t/chainlint/one-liner-for-loop.expect
new file mode 100644
index 0000000..51a3dc7
--- /dev/null
+++ b/t/chainlint/one-liner-for-loop.expect
@@ -0,0 +1,9 @@
+git init dir-rename-and-content &&
+(
+ cd dir-rename-and-content &&
+ test_write_lines 1 2 3 4 5 >foo &&
+ mkdir olddir &&
+ for i in a b c; do echo $i >olddir/$i; ?!LOOP?! done ?!AMP?!
+ git add foo olddir &&
+ git commit -m "original" &&
+)
diff --git a/t/chainlint/one-liner-for-loop.test b/t/chainlint/one-liner-for-loop.test
new file mode 100644
index 0000000..4bd8c06
--- /dev/null
+++ b/t/chainlint/one-liner-for-loop.test
@@ -0,0 +1,10 @@
+git init dir-rename-and-content &&
+(
+ cd dir-rename-and-content &&
+ test_write_lines 1 2 3 4 5 >foo &&
+ mkdir olddir &&
+# LINT: one-liner for-loop missing "|| exit"; also broken &&-chain
+ for i in a b c; do echo $i >olddir/$i; done
+ git add foo olddir &&
+ git commit -m "original" &&
+)
diff --git a/t/chainlint/return-loop.expect b/t/chainlint/return-loop.expect
new file mode 100644
index 0000000..cfc0549
--- /dev/null
+++ b/t/chainlint/return-loop.expect
@@ -0,0 +1,5 @@
+while test $i -lt $((num - 5))
+do
+ git notes add -m "notes for commit$i" HEAD~$i || return 1
+ i=$((i + 1))
+done
diff --git a/t/chainlint/return-loop.test b/t/chainlint/return-loop.test
new file mode 100644
index 0000000..f90b171
--- /dev/null
+++ b/t/chainlint/return-loop.test
@@ -0,0 +1,6 @@
+while test $i -lt $((num - 5))
+do
+# LINT: "|| return {n}" valid loop escape outside subshell; no "&&" needed
+ git notes add -m "notes for commit$i" HEAD~$i || return 1
+ i=$((i + 1))
+done
diff --git a/t/chainlint/semicolon.expect b/t/chainlint/semicolon.expect
index ed0b370..3aa2259 100644
--- a/t/chainlint/semicolon.expect
+++ b/t/chainlint/semicolon.expect
@@ -15,5 +15,5 @@
) &&
(cd foo &&
for i in a b c; do
- echo;
+ echo; ?!LOOP?!
done)
diff --git a/t/chainlint/sqstring-in-sqstring.expect b/t/chainlint/sqstring-in-sqstring.expect
new file mode 100644
index 0000000..cf0b591
--- /dev/null
+++ b/t/chainlint/sqstring-in-sqstring.expect
@@ -0,0 +1,4 @@
+perl -e '
+ defined($_ = -s $_) or die for @ARGV;
+ exit 1 if $ARGV[0] <= $ARGV[1];
+' test-2-$packname_2.pack test-3-$packname_3.pack
diff --git a/t/chainlint/sqstring-in-sqstring.test b/t/chainlint/sqstring-in-sqstring.test
new file mode 100644
index 0000000..77a425e
--- /dev/null
+++ b/t/chainlint/sqstring-in-sqstring.test
@@ -0,0 +1,5 @@
+# LINT: SQ-string Perl code fragment within SQ-string
+perl -e '\''
+ defined($_ = -s $_) or die for @ARGV;
+ exit 1 if $ARGV[0] <= $ARGV[1];
+'\'' test-2-$packname_2.pack test-3-$packname_3.pack
diff --git a/t/chainlint/subshell-here-doc.expect b/t/chainlint/subshell-here-doc.expect
index 029d129..5278927 100644
--- a/t/chainlint/subshell-here-doc.expect
+++ b/t/chainlint/subshell-here-doc.expect
@@ -1,10 +1,30 @@
(
- echo wobba gorgo snoot wafta snurb <<-EOF &&
+ echo wobba \
+ gorgo snoot \
+ wafta snurb <<-EOF &&
+ quoth the raven,
+ nevermore...
+ EOF
+
cat <<EOF >bip ?!AMP?!
- echo <<-EOF >bop
+ fish fly high
+EOF
+
+ echo <<-\EOF >bop
+ gomez
+ morticia
+ wednesday
+ pugsly
+ EOF
) &&
(
- cat <<-ARBITRARY >bup &&
- cat <<-ARBITRARY3 >bup3 &&
+ cat <<-\ARBITRARY >bup &&
+ glink
+ FIZZ
+ ARBITRARY
+ cat <<-"ARBITRARY3" >bup3 &&
+ glink
+ FIZZ
+ ARBITRARY3
meep
)
diff --git a/t/chainlint/t7900-subtree.expect b/t/chainlint/t7900-subtree.expect
index 1cccc7b..71b3b3b 100644
--- a/t/chainlint/t7900-subtree.expect
+++ b/t/chainlint/t7900-subtree.expect
@@ -1,10 +1,21 @@
(
- chks="sub1sub2sub3sub4" &&
+ chks="sub1
+sub2
+sub3
+sub4" &&
chks_sub=$(cat <<TXT | sed "s,^,sub dir/,"
+$chks
+TXT
) &&
- chkms="main-sub1main-sub2main-sub3main-sub4" &&
+ chkms="main-sub1
+main-sub2
+main-sub3
+main-sub4" &&
chkms_sub=$(cat <<TXT | sed "s,^,sub dir/,"
+$chkms
+TXT
) &&
subfiles=$(git ls-files) &&
- check_equal "$subfiles" "$chkms$chks"
+ check_equal "$subfiles" "$chkms
+$chks"
)
diff --git a/t/chainlint/token-pasting.expect b/t/chainlint/token-pasting.expect
new file mode 100644
index 0000000..342360b
--- /dev/null
+++ b/t/chainlint/token-pasting.expect
@@ -0,0 +1,27 @@
+git config filter.rot13.smudge ./rot13.sh &&
+git config filter.rot13.clean ./rot13.sh &&
+
+{
+ echo "*.t filter=rot13" ?!AMP?!
+ echo "*.i ident"
+} > .gitattributes &&
+
+{
+ echo a b c d e f g h i j k l m ?!AMP?!
+ echo n o p q r s t u v w x y z ?!AMP?!
+ echo '$Id$'
+} > test &&
+cat test > test.t &&
+cat test > test.o &&
+cat test > test.i &&
+git add test test.t test.i &&
+rm -f test test.t test.i &&
+git checkout -- test test.t test.i &&
+
+echo "content-test2" > test2.o &&
+echo "content-test3 - filename with special characters" > "test3 'sq',$x=.o" ?!AMP?!
+
+downstream_url_for_sed=$(
+ printf "%sn" "$downstream_url" |
+ sed -e 's/\/\\/g' -e 's/[[/.*^$]/\&/g'
+)
diff --git a/t/chainlint/token-pasting.test b/t/chainlint/token-pasting.test
new file mode 100644
index 0000000..b4610ce
--- /dev/null
+++ b/t/chainlint/token-pasting.test
@@ -0,0 +1,32 @@
+# LINT: single token; composite of multiple strings
+git config filter.rot13.smudge ./rot13.sh &&
+git config filter.rot13.clean ./rot13.sh &&
+
+{
+ echo "*.t filter=rot13"
+ echo "*.i ident"
+} >.gitattributes &&
+
+{
+ echo a b c d e f g h i j k l m
+ echo n o p q r s t u v w x y z
+# LINT: exit/enter string context and escaped-quote outside of string
+ echo '\''$Id$'\''
+} >test &&
+cat test >test.t &&
+cat test >test.o &&
+cat test >test.i &&
+git add test test.t test.i &&
+rm -f test test.t test.i &&
+git checkout -- test test.t test.i &&
+
+echo "content-test2" >test2.o &&
+# LINT: exit/enter string context and escaped-quote outside of string
+echo "content-test3 - filename with special characters" >"test3 '\''sq'\'',\$x=.o"
+
+# LINT: single token; composite of multiple strings
+downstream_url_for_sed=$(
+ printf "%s\n" "$downstream_url" |
+# LINT: exit/enter string context; "&" inside string not command terminator
+ sed -e '\''s/\\/\\\\/g'\'' -e '\''s/[[/.*^$]/\\&/g'\''
+)
diff --git a/t/chainlint/while-loop.expect b/t/chainlint/while-loop.expect
index 0d3a9b3..1f5eaea 100644
--- a/t/chainlint/while-loop.expect
+++ b/t/chainlint/while-loop.expect
@@ -2,10 +2,12 @@
while true
do
echo foo ?!AMP?!
- cat <<-EOF
+ cat <<-\EOF ?!LOOP?!
+ bar
+ EOF
done ?!AMP?!
while true; do
echo foo &&
- cat bar
+ cat bar ?!LOOP?!
done
)
diff --git a/t/check-non-portable-shell.pl b/t/check-non-portable-shell.pl
index fd33035..dd8107c 100755
--- a/t/check-non-portable-shell.pl
+++ b/t/check-non-portable-shell.pl
@@ -45,6 +45,7 @@ while (<>) {
/\bhead\s+-c\b/ and err 'head -c is not portable (use test_copy_bytes BYTES <file >out)';
/(?:\$\(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)';
+ /\b[ef]grep\b/ and err 'egrep/fgrep obsolescent (use grep -E/-F)';
/\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
err '"FOO=bar shell_func" assignment extends beyond "shell_func"';
diff --git a/t/helper/test-bundle-uri.c b/t/helper/test-bundle-uri.c
new file mode 100644
index 0000000..25afd39
--- /dev/null
+++ b/t/helper/test-bundle-uri.c
@@ -0,0 +1,95 @@
+#include "test-tool.h"
+#include "parse-options.h"
+#include "bundle-uri.h"
+#include "strbuf.h"
+#include "string-list.h"
+
+enum input_mode {
+ KEY_VALUE_PAIRS,
+ CONFIG_FILE,
+};
+
+static int cmd__bundle_uri_parse(int argc, const char **argv, enum input_mode mode)
+{
+ const char *key_value_usage[] = {
+ "test-tool bundle-uri parse-key-values <input>",
+ NULL
+ };
+ const char *config_usage[] = {
+ "test-tool bundle-uri parse-config <input>",
+ NULL
+ };
+ const char **usage = key_value_usage;
+ struct option options[] = {
+ OPT_END(),
+ };
+ struct strbuf sb = STRBUF_INIT;
+ struct bundle_list list;
+ int err = 0;
+ FILE *fp;
+
+ if (mode == CONFIG_FILE)
+ usage = config_usage;
+
+ argc = parse_options(argc, argv, NULL, options, usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+
+ init_bundle_list(&list);
+
+ switch (mode) {
+ case KEY_VALUE_PAIRS:
+ if (argc != 1)
+ goto usage;
+ fp = fopen(argv[0], "r");
+ if (!fp)
+ die("failed to open '%s'", argv[0]);
+ while (strbuf_getline(&sb, fp) != EOF) {
+ if (bundle_uri_parse_line(&list, sb.buf))
+ err = error("bad line: '%s'", sb.buf);
+ }
+ fclose(fp);
+ break;
+
+ case CONFIG_FILE:
+ if (argc != 1)
+ goto usage;
+ err = bundle_uri_parse_config_format("<uri>", argv[0], &list);
+ break;
+ }
+ strbuf_release(&sb);
+
+ print_bundle_list(stdout, &list);
+
+ clear_bundle_list(&list);
+
+ return !!err;
+
+usage:
+ usage_with_options(usage, options);
+}
+
+int cmd__bundle_uri(int argc, const char **argv)
+{
+ const char *usage[] = {
+ "test-tool bundle-uri <subcommand> [<options>]",
+ NULL
+ };
+ struct option options[] = {
+ OPT_END(),
+ };
+
+ argc = parse_options(argc, argv, NULL, options, usage,
+ PARSE_OPT_STOP_AT_NON_OPTION |
+ PARSE_OPT_KEEP_ARGV0);
+ if (argc == 1)
+ goto usage;
+
+ if (!strcmp(argv[1], "parse-key-values"))
+ return cmd__bundle_uri_parse(argc - 1, argv + 1, KEY_VALUE_PAIRS);
+ if (!strcmp(argv[1], "parse-config"))
+ return cmd__bundle_uri_parse(argc - 1, argv + 1, CONFIG_FILE);
+ error("there is no test-tool bundle-uri tool '%s'", argv[1]);
+
+usage:
+ usage_with_options(usage, options);
+}
diff --git a/t/helper/test-cache-tree.c b/t/helper/test-cache-tree.c
new file mode 100644
index 0000000..9159a17
--- /dev/null
+++ b/t/helper/test-cache-tree.c
@@ -0,0 +1,65 @@
+#define USE_THE_INDEX_VARIABLE
+#include "test-tool.h"
+#include "cache.h"
+#include "tree.h"
+#include "cache-tree.h"
+#include "parse-options.h"
+
+static char const * const test_cache_tree_usage[] = {
+ N_("test-tool cache-tree <options> (control|prime|update)"),
+ NULL
+};
+
+int cmd__cache_tree(int argc, const char **argv)
+{
+ struct object_id oid;
+ struct tree *tree;
+ int empty = 0;
+ int invalidate_qty = 0;
+ int i;
+
+ struct option options[] = {
+ OPT_BOOL(0, "empty", &empty,
+ N_("clear the cache tree before each iteration")),
+ OPT_INTEGER_F(0, "invalidate", &invalidate_qty,
+ N_("number of entries in the cache tree to invalidate (default 0)"),
+ PARSE_OPT_NONEG),
+ OPT_END()
+ };
+
+ setup_git_directory();
+
+ argc = parse_options(argc, argv, NULL, options, test_cache_tree_usage, 0);
+
+ if (repo_read_index(the_repository) < 0)
+ die(_("unable to read index file"));
+
+ oidcpy(&oid, &the_index.cache_tree->oid);
+ tree = parse_tree_indirect(&oid);
+ if (!tree)
+ die(_("not a tree object: %s"), oid_to_hex(&oid));
+
+ if (empty) {
+ /* clear the cache tree & allocate a new one */
+ cache_tree_free(&the_index.cache_tree);
+ the_index.cache_tree = cache_tree();
+ } else if (invalidate_qty) {
+ /* invalidate the specified number of unique paths */
+ float f_interval = (float)the_index.cache_nr / invalidate_qty;
+ int interval = f_interval < 1.0 ? 1 : (int)f_interval;
+ for (i = 0; i < invalidate_qty && i * interval < the_index.cache_nr; i++)
+ cache_tree_invalidate_path(&the_index, the_index.cache[i * interval]->name);
+ }
+
+ if (argc != 1)
+ usage_with_options(test_cache_tree_usage, options);
+ else if (!strcmp(argv[0], "prime"))
+ prime_cache_tree(the_repository, &the_index, tree);
+ else if (!strcmp(argv[0], "update"))
+ cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
+ /* use "control" subcommand to specify no-op */
+ else if (!!strcmp(argv[0], "control"))
+ die(_("Unhandled subcommand '%s'"), argv[0]);
+
+ return 0;
+}
diff --git a/t/helper/test-config.c b/t/helper/test-config.c
index a6e9367..4ba9eb6 100644
--- a/t/helper/test-config.c
+++ b/t/helper/test-config.c
@@ -37,7 +37,7 @@
*
*/
-static int iterate_cb(const char *var, const char *value, void *data)
+static int iterate_cb(const char *var, const char *value, void *data UNUSED)
{
static int nr;
diff --git a/t/helper/test-crontab.c b/t/helper/test-crontab.c
index e7c0137..e6c1b1e 100644
--- a/t/helper/test-crontab.c
+++ b/t/helper/test-crontab.c
@@ -2,33 +2,34 @@
#include "cache.h"
/*
- * Usage: test-tool cron <file> [-l]
+ * Usage: test-tool crontab <file> -l|<input>
*
* If -l is specified, then write the contents of <file> to stdout.
- * Otherwise, write from stdin into <file>.
+ * Otherwise, copy the contents of <input> into <file>.
*/
int cmd__crontab(int argc, const char **argv)
{
int a;
FILE *from, *to;
- if (argc == 3 && !strcmp(argv[2], "-l")) {
+ if (argc != 3)
+ usage("test-tool crontab <file> -l|<input>");
+
+ if (!strcmp(argv[2], "-l")) {
from = fopen(argv[1], "r");
if (!from)
return 0;
to = stdout;
- } else if (argc == 2) {
- from = stdin;
- to = fopen(argv[1], "w");
- } else
- return error("unknown arguments");
+ } else {
+ from = xfopen(argv[2], "r");
+ to = xfopen(argv[1], "w");
+ }
while ((a = fgetc(from)) != EOF)
fputc(a, to);
- if (argc == 3)
- fclose(from);
- else
+ fclose(from);
+ if (to != stdout)
fclose(to);
return 0;
diff --git a/t/helper/test-dump-cache-tree.c b/t/helper/test-dump-cache-tree.c
index 0d6d7f1..454f17b 100644
--- a/t/helper/test-dump-cache-tree.c
+++ b/t/helper/test-dump-cache-tree.c
@@ -1,3 +1,4 @@
+#define USE_THE_INDEX_VARIABLE
#include "test-tool.h"
#include "cache.h"
#include "tree.h"
@@ -62,12 +63,12 @@ int cmd__dump_cache_tree(int ac, const char **av)
int ret;
setup_git_directory();
- if (read_cache() < 0)
+ if (repo_read_index(the_repository) < 0)
die("unable to read index file");
istate = the_index;
istate.cache_tree = another;
cache_tree_update(&istate, WRITE_TREE_DRY_RUN);
- ret = dump_cache_tree(active_cache_tree, another, "");
+ ret = dump_cache_tree(the_index.cache_tree, another, "");
cache_tree_free(&another);
return ret;
diff --git a/t/helper/test-dump-split-index.c b/t/helper/test-dump-split-index.c
index a209880..0ea97b8 100644
--- a/t/helper/test-dump-split-index.c
+++ b/t/helper/test-dump-split-index.c
@@ -1,3 +1,4 @@
+#define USE_THE_INDEX_VARIABLE
#include "test-tool.h"
#include "cache.h"
#include "split-index.h"
diff --git a/t/helper/test-dump-untracked-cache.c b/t/helper/test-dump-untracked-cache.c
index 9901061..6d53683 100644
--- a/t/helper/test-dump-untracked-cache.c
+++ b/t/helper/test-dump-untracked-cache.c
@@ -1,4 +1,4 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
#include "test-tool.h"
#include "cache.h"
#include "dir.h"
@@ -51,7 +51,7 @@ int cmd__dump_untracked_cache(int ac, const char **av)
xsetenv("GIT_CONFIG_VALUE_0", "keep", 1);
setup_git_directory();
- if (read_cache() < 0)
+ if (repo_read_index(the_repository) < 0)
die("unable to read index file");
uc = the_index.untracked;
if (!uc) {
diff --git a/t/helper/test-fake-ssh.c b/t/helper/test-fake-ssh.c
index 12beee9..2e576bc 100644
--- a/t/helper/test-fake-ssh.c
+++ b/t/helper/test-fake-ssh.c
@@ -8,7 +8,7 @@ int cmd_main(int argc, const char **argv)
struct strbuf buf = STRBUF_INIT;
FILE *f;
int i;
- const char *child_argv[] = { NULL, NULL };
+ struct child_process cmd = CHILD_PROCESS_INIT;
/* First, print all parameters into $TRASH_DIRECTORY/ssh-output */
if (!trash_directory)
@@ -25,6 +25,7 @@ int cmd_main(int argc, const char **argv)
/* Now, evaluate the *last* parameter */
if (argc < 2)
return 0;
- child_argv[0] = argv[argc - 1];
- return run_command_v_opt(child_argv, RUN_USING_SHELL);
+ cmd.use_shell = 1;
+ strvec_push(&cmd.args, argv[argc - 1]);
+ return run_command(&cmd);
}
diff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c
index 4e5553e..efc82dd 100644
--- a/t/helper/test-fast-rebase.c
+++ b/t/helper/test-fast-rebase.c
@@ -10,7 +10,7 @@
* refactoring is the better route).
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
#include "test-tool.h"
#include "cache-tree.h"
@@ -123,7 +123,7 @@ int cmd__fast_rebase(int argc, const char **argv)
die(_("Cannot read HEAD"));
assert(oideq(&onto->object.oid, &head));
- hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
+ repo_hold_locked_index(the_repository, &lock, LOCK_DIE_ON_ERROR);
if (repo_read_index(the_repository) < 0)
BUG("Could not read index");
@@ -184,8 +184,6 @@ int cmd__fast_rebase(int argc, const char **argv)
last_picked_commit = commit;
last_commit = create_commit(result.tree, commit, last_commit);
}
- /* TODO: There should be some kind of rev_info_free(&revs) call... */
- memset(&revs, 0, sizeof(revs));
merge_switch_to_result(&merge_opt, head_tree, &result, 1, !result.clean);
diff --git a/t/helper/test-lazy-init-name-hash.c b/t/helper/test-lazy-init-name-hash.c
index cd1b4c9..ab86c14 100644
--- a/t/helper/test-lazy-init-name-hash.c
+++ b/t/helper/test-lazy-init-name-hash.c
@@ -1,3 +1,4 @@
+#define USE_THE_INDEX_VARIABLE
#include "test-tool.h"
#include "cache.h"
#include "parse-options.h"
@@ -32,7 +33,7 @@ static void dump_run(void)
struct dir_entry *dir;
struct cache_entry *ce;
- read_cache();
+ repo_read_index(the_repository);
if (single) {
test_lazy_init_name_hash(&the_index, 0);
} else {
@@ -49,7 +50,7 @@ static void dump_run(void)
ent /* member name */)
printf("name %08x %s\n", ce->ent.hash, ce->name);
- discard_cache();
+ discard_index(&the_index);
}
/*
@@ -66,7 +67,7 @@ static uint64_t time_runs(int try_threaded)
for (i = 0; i < count; i++) {
t0 = getnanotime();
- read_cache();
+ repo_read_index(the_repository);
t1 = getnanotime();
nr_threads_used = test_lazy_init_name_hash(&the_index, try_threaded);
t2 = getnanotime();
@@ -89,7 +90,7 @@ static uint64_t time_runs(int try_threaded)
the_index.cache_nr);
fflush(stdout);
- discard_cache();
+ discard_index(&the_index);
}
avg = sum / count;
@@ -113,9 +114,9 @@ static void analyze_run(void)
int i;
int nr;
- read_cache();
+ repo_read_index(the_repository);
cache_nr_limit = the_index.cache_nr;
- discard_cache();
+ discard_index(&the_index);
nr = analyze;
while (1) {
@@ -128,23 +129,23 @@ static void analyze_run(void)
nr = cache_nr_limit;
for (i = 0; i < count; i++) {
- read_cache();
+ repo_read_index(the_repository);
the_index.cache_nr = nr; /* cheap truncate of index */
t1s = getnanotime();
test_lazy_init_name_hash(&the_index, 0);
t2s = getnanotime();
sum_single += (t2s - t1s);
the_index.cache_nr = cache_nr_limit;
- discard_cache();
+ discard_index(&the_index);
- read_cache();
+ repo_read_index(the_repository);
the_index.cache_nr = nr; /* cheap truncate of index */
t1m = getnanotime();
nr_threads_used = test_lazy_init_name_hash(&the_index, 1);
t2m = getnanotime();
sum_multi += (t2m - t1m);
the_index.cache_nr = cache_nr_limit;
- discard_cache();
+ discard_index(&the_index);
if (!nr_threads_used)
printf(" [size %8d] [single %f] non-threaded code path used\n",
diff --git a/t/helper/test-mergesort.c b/t/helper/test-mergesort.c
index 202e54a..335e5bb 100644
--- a/t/helper/test-mergesort.c
+++ b/t/helper/test-mergesort.c
@@ -22,21 +22,35 @@ static int compare_strings(const struct line *x, const struct line *y)
static int sort_stdin(void)
{
- struct line *line, *p = NULL, *lines = NULL;
+ struct line *lines;
+ struct line **tail = &lines;
struct strbuf sb = STRBUF_INIT;
-
- while (!strbuf_getline(&sb, stdin)) {
- line = xmalloc(sizeof(struct line));
- line->text = strbuf_detach(&sb, NULL);
- if (p) {
- line->next = p->next;
- p->next = line;
- } else {
- line->next = NULL;
- lines = line;
- }
- p = line;
+ struct mem_pool lines_pool;
+ char *p;
+
+ strbuf_read(&sb, 0, 0);
+
+ /*
+ * Split by newline, but don't create an item
+ * for the empty string after the last separator.
+ */
+ if (sb.len && sb.buf[sb.len - 1] == '\n')
+ strbuf_setlen(&sb, sb.len - 1);
+
+ mem_pool_init(&lines_pool, 0);
+ p = sb.buf;
+ for (;;) {
+ char *eol = strchr(p, '\n');
+ struct line *line = mem_pool_alloc(&lines_pool, sizeof(*line));
+ line->text = p;
+ *tail = line;
+ tail = &line->next;
+ if (!eol)
+ break;
+ *eol = '\0';
+ p = eol + 1;
}
+ *tail = NULL;
sort_lines(&lines, compare_strings);
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
index 48d3cf6..5068355 100644
--- a/t/helper/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -192,3 +192,131 @@ int cmd__parse_options(int argc, const char **argv)
return ret;
}
+
+static void print_args(int argc, const char **argv)
+{
+ int i;
+ for (i = 0; i < argc; i++)
+ printf("arg %02d: %s\n", i, argv[i]);
+}
+
+static int parse_options_flags__cmd(int argc, const char **argv,
+ enum parse_opt_flags test_flags)
+{
+ const char *usage[] = {
+ "<...> cmd [options]",
+ NULL
+ };
+ int opt = 0;
+ const struct option options[] = {
+ OPT_INTEGER('o', "opt", &opt, "an integer option"),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, NULL, options, usage, test_flags);
+
+ printf("opt: %d\n", opt);
+ print_args(argc, argv);
+
+ return 0;
+}
+
+static enum parse_opt_flags test_flags = 0;
+static const struct option test_flag_options[] = {
+ OPT_GROUP("flag-options:"),
+ OPT_BIT(0, "keep-dashdash", &test_flags,
+ "pass PARSE_OPT_KEEP_DASHDASH to parse_options()",
+ PARSE_OPT_KEEP_DASHDASH),
+ OPT_BIT(0, "stop-at-non-option", &test_flags,
+ "pass PARSE_OPT_STOP_AT_NON_OPTION to parse_options()",
+ PARSE_OPT_STOP_AT_NON_OPTION),
+ OPT_BIT(0, "keep-argv0", &test_flags,
+ "pass PARSE_OPT_KEEP_ARGV0 to parse_options()",
+ PARSE_OPT_KEEP_ARGV0),
+ OPT_BIT(0, "keep-unknown-opt", &test_flags,
+ "pass PARSE_OPT_KEEP_UNKNOWN_OPT to parse_options()",
+ PARSE_OPT_KEEP_UNKNOWN_OPT),
+ OPT_BIT(0, "no-internal-help", &test_flags,
+ "pass PARSE_OPT_NO_INTERNAL_HELP to parse_options()",
+ PARSE_OPT_NO_INTERNAL_HELP),
+ OPT_BIT(0, "subcommand-optional", &test_flags,
+ "pass PARSE_OPT_SUBCOMMAND_OPTIONAL to parse_options()",
+ PARSE_OPT_SUBCOMMAND_OPTIONAL),
+ OPT_END()
+};
+
+int cmd__parse_options_flags(int argc, const char **argv)
+{
+ const char *usage[] = {
+ "test-tool parse-options-flags [flag-options] cmd [options]",
+ NULL
+ };
+
+ argc = parse_options(argc, argv, NULL, test_flag_options, usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+
+ if (!argc || strcmp(argv[0], "cmd")) {
+ error("'cmd' is mandatory");
+ usage_with_options(usage, test_flag_options);
+ }
+
+ return parse_options_flags__cmd(argc, argv, test_flags);
+}
+
+static int subcmd_one(int argc, const char **argv, const char *prefix)
+{
+ printf("fn: subcmd_one\n");
+ print_args(argc, argv);
+ return 0;
+}
+
+static int subcmd_two(int argc, const char **argv, const char *prefix)
+{
+ printf("fn: subcmd_two\n");
+ print_args(argc, argv);
+ return 0;
+}
+
+static int parse_subcommand__cmd(int argc, const char **argv,
+ enum parse_opt_flags test_flags)
+{
+ const char *usage[] = {
+ "<...> cmd subcmd-one",
+ "<...> cmd subcmd-two",
+ NULL
+ };
+ parse_opt_subcommand_fn *fn = NULL;
+ int opt = 0;
+ struct option options[] = {
+ OPT_SUBCOMMAND("subcmd-one", &fn, subcmd_one),
+ OPT_SUBCOMMAND("subcmd-two", &fn, subcmd_two),
+ OPT_INTEGER('o', "opt", &opt, "an integer option"),
+ OPT_END()
+ };
+
+ if (test_flags & PARSE_OPT_SUBCOMMAND_OPTIONAL)
+ fn = subcmd_one;
+ argc = parse_options(argc, argv, NULL, options, usage, test_flags);
+
+ printf("opt: %d\n", opt);
+
+ return fn(argc, argv, NULL);
+}
+
+int cmd__parse_subcommand(int argc, const char **argv)
+{
+ const char *usage[] = {
+ "test-tool parse-subcommand [flag-options] cmd <subcommand>",
+ NULL
+ };
+
+ argc = parse_options(argc, argv, NULL, test_flag_options, usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+
+ if (!argc || strcmp(argv[0], "cmd")) {
+ error("'cmd' is mandatory");
+ usage_with_options(usage, test_flag_options);
+ }
+
+ return parse_subcommand__cmd(argc, argv, test_flags);
+}
diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c
index d20e1b7..f69709d 100644
--- a/t/helper/test-path-utils.c
+++ b/t/helper/test-path-utils.c
@@ -8,7 +8,8 @@
* GIT_CEILING_DIRECTORIES. If the path is unusable for some reason,
* die with an explanation.
*/
-static int normalize_ceiling_entry(struct string_list_item *item, void *unused)
+static int normalize_ceiling_entry(struct string_list_item *item,
+ void *data UNUSED)
{
char *ceil = item->string;
diff --git a/t/helper/test-proc-receive.c b/t/helper/test-proc-receive.c
index cc08506..a4b305f 100644
--- a/t/helper/test-proc-receive.c
+++ b/t/helper/test-proc-receive.c
@@ -6,7 +6,7 @@
#include "test-tool.h"
static const char *proc_receive_usage[] = {
- "test-tool proc-receive [<options>...]",
+ "test-tool proc-receive [<options>]",
NULL
};
diff --git a/t/helper/test-read-cache.c b/t/helper/test-read-cache.c
index b736ef1..23e9e27 100644
--- a/t/helper/test-read-cache.c
+++ b/t/helper/test-read-cache.c
@@ -1,3 +1,4 @@
+#define USE_THE_INDEX_VARIABLE
#include "test-tool.h"
#include "cache.h"
#include "config.h"
@@ -20,7 +21,7 @@ int cmd__read_cache(int argc, const char **argv)
git_config(git_default_config, NULL);
for (i = 0; i < cnt; i++) {
- read_cache();
+ repo_read_index(the_repository);
if (name) {
int pos;
@@ -33,7 +34,7 @@ int cmd__read_cache(int argc, const char **argv)
ce_uptodate(the_index.cache[pos]) ? "" : " not");
write_file(name, "%d\n", i);
}
- discard_cache();
+ discard_index(&the_index);
}
return 0;
}
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index 4d18bfb..ae8a564 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -161,7 +161,7 @@ static int cmd_rename_ref(struct ref_store *refs, const char **argv)
}
static int each_ref(const char *refname, const struct object_id *oid,
- int flags, void *cb_data)
+ int flags, void *cb_data UNUSED)
{
printf("%s %s 0x%x\n", oid_to_hex(oid), refname, flags);
return 0;
@@ -207,7 +207,7 @@ static int cmd_for_each_reflog(struct ref_store *refs, const char **argv)
static int each_reflog(struct object_id *old_oid, struct object_id *new_oid,
const char *committer, timestamp_t timestamp,
- int tz, const char *msg, void *cb_data)
+ int tz, const char *msg, void *cb_data UNUSED)
{
printf("%s %s %s %" PRItime " %+05d%s%s", oid_to_hex(old_oid),
oid_to_hex(new_oid), committer, timestamp, tz,
diff --git a/t/helper/test-rot13-filter.c b/t/helper/test-rot13-filter.c
new file mode 100644
index 0000000..f8d564c
--- /dev/null
+++ b/t/helper/test-rot13-filter.c
@@ -0,0 +1,382 @@
+/*
+ * Example implementation for the Git filter protocol version 2
+ * See Documentation/gitattributes.txt, section "Filter Protocol"
+ *
+ * Usage: test-tool rot13-filter [--always-delay] --log=<path> <capabilities>
+ *
+ * Log path defines a debug log file that the script writes to. The
+ * subsequent arguments define a list of supported protocol capabilities
+ * ("clean", "smudge", etc).
+ *
+ * When --always-delay is given all pathnames with the "can-delay" flag
+ * that don't appear on the list bellow are delayed with a count of 1
+ * (see more below).
+ *
+ * This implementation supports special test cases:
+ * (1) If data with the pathname "clean-write-fail.r" is processed with
+ * a "clean" operation then the write operation will die.
+ * (2) If data with the pathname "smudge-write-fail.r" is processed with
+ * a "smudge" operation then the write operation will die.
+ * (3) If data with the pathname "error.r" is processed with any
+ * operation then the filter signals that it cannot or does not want
+ * to process the file.
+ * (4) If data with the pathname "abort.r" is processed with any
+ * operation then the filter signals that it cannot or does not want
+ * to process the file and any file after that is processed with the
+ * same command.
+ * (5) If data with a pathname that is a key in the delay hash is
+ * requested (e.g. "test-delay10.a") then the filter responds with
+ * a "delay" status and sets the "requested" field in the delay hash.
+ * The filter will signal the availability of this object after
+ * "count" (field in delay hash) "list_available_blobs" commands.
+ * (6) If data with the pathname "missing-delay.a" is processed that the
+ * filter will drop the path from the "list_available_blobs" response.
+ * (7) If data with the pathname "invalid-delay.a" is processed that the
+ * filter will add the path "unfiltered" which was not delayed before
+ * to the "list_available_blobs" response.
+ */
+
+#include "test-tool.h"
+#include "pkt-line.h"
+#include "string-list.h"
+#include "strmap.h"
+#include "parse-options.h"
+
+static FILE *logfile;
+static int always_delay, has_clean_cap, has_smudge_cap;
+static struct strmap delay = STRMAP_INIT;
+
+static inline const char *str_or_null(const char *str)
+{
+ return str ? str : "(null)";
+}
+
+static char *rot13(char *str)
+{
+ char *c;
+ for (c = str; *c; c++)
+ if (isalpha(*c))
+ *c += tolower(*c) < 'n' ? 13 : -13;
+ return str;
+}
+
+static char *get_value(char *buf, const char *key)
+{
+ const char *orig_buf = buf;
+ if (!buf ||
+ !skip_prefix((const char *)buf, key, (const char **)&buf) ||
+ !skip_prefix((const char *)buf, "=", (const char **)&buf) ||
+ !*buf)
+ die("expected key '%s', got '%s'", key, str_or_null(orig_buf));
+ return buf;
+}
+
+/*
+ * Read a text packet, expecting that it is in the form "key=value" for
+ * the given key. An EOF does not trigger any error and is reported
+ * back to the caller with NULL. Die if the "key" part of "key=value" does
+ * not match the given key, or the value part is empty.
+ */
+static char *packet_key_val_read(const char *key)
+{
+ char *buf;
+ if (packet_read_line_gently(0, NULL, &buf) < 0)
+ return NULL;
+ return xstrdup(get_value(buf, key));
+}
+
+static inline void assert_remote_capability(struct strset *caps, const char *cap)
+{
+ if (!strset_contains(caps, cap))
+ die("required '%s' capability not available from remote", cap);
+}
+
+static void read_capabilities(struct strset *remote_caps)
+{
+ for (;;) {
+ char *buf = packet_read_line(0, NULL);
+ if (!buf)
+ break;
+ strset_add(remote_caps, get_value(buf, "capability"));
+ }
+
+ assert_remote_capability(remote_caps, "clean");
+ assert_remote_capability(remote_caps, "smudge");
+ assert_remote_capability(remote_caps, "delay");
+}
+
+static void check_and_write_capabilities(struct strset *remote_caps,
+ const char **caps, int nr_caps)
+{
+ int i;
+ for (i = 0; i < nr_caps; i++) {
+ if (!strset_contains(remote_caps, caps[i]))
+ die("our capability '%s' is not available from remote",
+ caps[i]);
+ packet_write_fmt(1, "capability=%s\n", caps[i]);
+ }
+ packet_flush(1);
+}
+
+struct delay_entry {
+ int requested, count;
+ char *output;
+};
+
+static void free_delay_entries(void)
+{
+ struct hashmap_iter iter;
+ struct strmap_entry *ent;
+
+ strmap_for_each_entry(&delay, &iter, ent) {
+ struct delay_entry *delay_entry = ent->value;
+ free(delay_entry->output);
+ free(delay_entry);
+ }
+ strmap_clear(&delay, 0);
+}
+
+static void add_delay_entry(char *pathname, int count, int requested)
+{
+ struct delay_entry *entry = xcalloc(1, sizeof(*entry));
+ entry->count = count;
+ entry->requested = requested;
+ if (strmap_put(&delay, pathname, entry))
+ BUG("adding the same path twice to delay hash?");
+}
+
+static void reply_list_available_blobs_cmd(void)
+{
+ struct hashmap_iter iter;
+ struct strmap_entry *ent;
+ struct string_list_item *str_item;
+ struct string_list paths = STRING_LIST_INIT_NODUP;
+
+ /* flush */
+ if (packet_read_line(0, NULL))
+ die("bad list_available_blobs end");
+
+ strmap_for_each_entry(&delay, &iter, ent) {
+ struct delay_entry *delay_entry = ent->value;
+ if (!delay_entry->requested)
+ continue;
+ delay_entry->count--;
+ if (!strcmp(ent->key, "invalid-delay.a")) {
+ /* Send Git a pathname that was not delayed earlier */
+ packet_write_fmt(1, "pathname=unfiltered");
+ }
+ if (!strcmp(ent->key, "missing-delay.a")) {
+ /* Do not signal Git that this file is available */
+ } else if (!delay_entry->count) {
+ string_list_append(&paths, ent->key);
+ packet_write_fmt(1, "pathname=%s", ent->key);
+ }
+ }
+
+ /* Print paths in sorted order. */
+ string_list_sort(&paths);
+ for_each_string_list_item(str_item, &paths)
+ fprintf(logfile, " %s", str_item->string);
+ string_list_clear(&paths, 0);
+
+ packet_flush(1);
+
+ fprintf(logfile, " [OK]\n");
+ packet_write_fmt(1, "status=success");
+ packet_flush(1);
+}
+
+static void command_loop(void)
+{
+ for (;;) {
+ char *buf, *output;
+ char *pathname;
+ struct delay_entry *entry;
+ struct strbuf input = STRBUF_INIT;
+ char *command = packet_key_val_read("command");
+
+ if (!command) {
+ fprintf(logfile, "STOP\n");
+ break;
+ }
+ fprintf(logfile, "IN: %s", command);
+
+ if (!strcmp(command, "list_available_blobs")) {
+ reply_list_available_blobs_cmd();
+ free(command);
+ continue;
+ }
+
+ pathname = packet_key_val_read("pathname");
+ if (!pathname)
+ die("unexpected EOF while expecting pathname");
+ fprintf(logfile, " %s", pathname);
+
+ /* Read until flush */
+ while ((buf = packet_read_line(0, NULL))) {
+ if (!strcmp(buf, "can-delay=1")) {
+ entry = strmap_get(&delay, pathname);
+ if (entry && !entry->requested)
+ entry->requested = 1;
+ else if (!entry && always_delay)
+ add_delay_entry(pathname, 1, 1);
+ } else if (starts_with(buf, "ref=") ||
+ starts_with(buf, "treeish=") ||
+ starts_with(buf, "blob=")) {
+ fprintf(logfile, " %s", buf);
+ } 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 '%s'", buf);
+ }
+ }
+
+ read_packetized_to_strbuf(0, &input, 0);
+ fprintf(logfile, " %"PRIuMAX" [OK] -- ", (uintmax_t)input.len);
+
+ entry = strmap_get(&delay, pathname);
+ if (entry && entry->output) {
+ output = entry->output;
+ } else if (!strcmp(pathname, "error.r") || !strcmp(pathname, "abort.r")) {
+ output = "";
+ } else if (!strcmp(command, "clean") && has_clean_cap) {
+ output = rot13(input.buf);
+ } else if (!strcmp(command, "smudge") && has_smudge_cap) {
+ output = rot13(input.buf);
+ } else {
+ die("bad command '%s'", command);
+ }
+
+ if (!strcmp(pathname, "error.r")) {
+ fprintf(logfile, "[ERROR]\n");
+ packet_write_fmt(1, "status=error");
+ packet_flush(1);
+ } else if (!strcmp(pathname, "abort.r")) {
+ fprintf(logfile, "[ABORT]\n");
+ packet_write_fmt(1, "status=abort");
+ packet_flush(1);
+ } else if (!strcmp(command, "smudge") &&
+ (entry = strmap_get(&delay, pathname)) &&
+ entry->requested == 1) {
+ fprintf(logfile, "[DELAYED]\n");
+ packet_write_fmt(1, "status=delayed");
+ packet_flush(1);
+ entry->requested = 2;
+ if (entry->output != output) {
+ free(entry->output);
+ entry->output = xstrdup(output);
+ }
+ } else {
+ int i, nr_packets = 0;
+ size_t output_len;
+ const char *p;
+ packet_write_fmt(1, "status=success");
+ packet_flush(1);
+
+ if (skip_prefix(pathname, command, &p) &&
+ !strcmp(p, "-write-fail.r")) {
+ fprintf(logfile, "[WRITE FAIL]\n");
+ die("%s write error", command);
+ }
+
+ output_len = strlen(output);
+ fprintf(logfile, "OUT: %"PRIuMAX" ", (uintmax_t)output_len);
+
+ if (write_packetized_from_buf_no_flush_count(output,
+ output_len, 1, &nr_packets))
+ die("failed to write buffer to stdout");
+ packet_flush(1);
+
+ for (i = 0; i < nr_packets; i++)
+ fprintf(logfile, ".");
+ fprintf(logfile, " [OK]\n");
+
+ packet_flush(1);
+ }
+ free(pathname);
+ strbuf_release(&input);
+ free(command);
+ }
+}
+
+static void packet_initialize(void)
+{
+ char *pkt_buf = packet_read_line(0, NULL);
+
+ if (!pkt_buf || strcmp(pkt_buf, "git-filter-client"))
+ die("bad initialize: '%s'", str_or_null(pkt_buf));
+
+ pkt_buf = packet_read_line(0, NULL);
+ if (!pkt_buf || strcmp(pkt_buf, "version=2"))
+ die("bad version: '%s'", str_or_null(pkt_buf));
+
+ pkt_buf = packet_read_line(0, NULL);
+ if (pkt_buf)
+ die("bad version end: '%s'", pkt_buf);
+
+ packet_write_fmt(1, "git-filter-server");
+ packet_write_fmt(1, "version=2");
+ packet_flush(1);
+}
+
+static const char *rot13_usage[] = {
+ "test-tool rot13-filter [--always-delay] --log=<path> <capabilities>",
+ NULL
+};
+
+int cmd__rot13_filter(int argc, const char **argv)
+{
+ int i, nr_caps;
+ struct strset remote_caps = STRSET_INIT;
+ const char *log_path = NULL;
+
+ struct option options[] = {
+ OPT_BOOL(0, "always-delay", &always_delay,
+ "delay all paths with the can-delay flag"),
+ OPT_STRING(0, "log", &log_path, "path",
+ "path to the debug log file"),
+ OPT_END()
+ };
+ nr_caps = parse_options(argc, argv, NULL, options, rot13_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+
+ if (!log_path || !nr_caps)
+ usage_with_options(rot13_usage, options);
+
+ logfile = fopen(log_path, "a");
+ if (!logfile)
+ die_errno("failed to open log file");
+
+ for (i = 0; i < nr_caps; i++) {
+ if (!strcmp(argv[i], "smudge"))
+ has_smudge_cap = 1;
+ if (!strcmp(argv[i], "clean"))
+ has_clean_cap = 1;
+ }
+
+ add_delay_entry("test-delay10.a", 1, 0);
+ add_delay_entry("test-delay11.a", 1, 0);
+ add_delay_entry("test-delay20.a", 2, 0);
+ add_delay_entry("test-delay10.b", 1, 0);
+ add_delay_entry("missing-delay.a", 1, 0);
+ add_delay_entry("invalid-delay.a", 1, 0);
+
+ fprintf(logfile, "START\n");
+ packet_initialize();
+
+ read_capabilities(&remote_caps);
+ check_and_write_capabilities(&remote_caps, argv, nr_caps);
+ fprintf(logfile, "init handshake complete\n");
+ strset_clear(&remote_caps);
+
+ command_loop();
+
+ if (fclose(logfile))
+ die_errno("error closing logfile");
+ free_delay_entries();
+ return 0;
+}
diff --git a/t/helper/test-run-command.c b/t/helper/test-run-command.c
index c9283b4..3ecb830 100644
--- a/t/helper/test-run-command.c
+++ b/t/helper/test-run-command.c
@@ -136,7 +136,7 @@ static const char * const testsuite_usage[] = {
static int testsuite(int argc, const char **argv)
{
struct testsuite suite = TESTSUITE_INIT;
- int max_jobs = 1, i, ret;
+ int max_jobs = 1, i, ret = 0;
DIR *dir;
struct dirent *d;
struct option options[] = {
@@ -152,6 +152,12 @@ static int testsuite(int argc, const char **argv)
"write JUnit-style XML files"),
OPT_END()
};
+ struct run_process_parallel_opts opts = {
+ .get_next_task = next_test,
+ .start_failure = test_failed,
+ .task_finished = test_finished,
+ .data = &suite,
+ };
argc = parse_options(argc, argv, NULL, options,
testsuite_usage, PARSE_OPT_STOP_AT_NON_OPTION);
@@ -192,8 +198,8 @@ static int testsuite(int argc, const char **argv)
fprintf(stderr, "Running %"PRIuMAX" tests (%d at a time)\n",
(uintmax_t)suite.tests.nr, max_jobs);
- ret = run_processes_parallel(max_jobs, next_test, test_failed,
- test_finished, &suite);
+ opts.processes = max_jobs;
+ run_processes_parallel(&opts);
if (suite.failed.nr > 0) {
ret = 1;
@@ -206,7 +212,7 @@ static int testsuite(int argc, const char **argv)
string_list_clear(&suite.tests, 0);
string_list_clear(&suite.failed, 0);
- return !!ret;
+ return ret;
}
static uint64_t my_random_next = 1234;
@@ -381,13 +387,17 @@ int cmd__run_command(int argc, const char **argv)
{
struct child_process proc = CHILD_PROCESS_INIT;
int jobs;
+ int ret;
+ struct run_process_parallel_opts opts = {
+ .data = &proc,
+ };
if (argc > 1 && !strcmp(argv[1], "testsuite"))
- exit(testsuite(argc - 1, argv + 1));
+ return testsuite(argc - 1, argv + 1);
if (!strcmp(argv[1], "inherited-handle"))
- exit(inherit_handle(argv[0]));
+ return inherit_handle(argv[0]);
if (!strcmp(argv[1], "inherited-handle-child"))
- exit(inherit_handle_child());
+ return inherit_handle_child();
if (argc >= 2 && !strcmp(argv[1], "quote-stress-test"))
return !!quote_stress_test(argc - 1, argv + 1);
@@ -404,41 +414,52 @@ int cmd__run_command(int argc, const char **argv)
argv += 2;
argc -= 2;
}
- if (argc < 3)
- return 1;
+ if (argc < 3) {
+ ret = 1;
+ goto cleanup;
+ }
strvec_pushv(&proc.args, (const char **)argv + 2);
if (!strcmp(argv[1], "start-command-ENOENT")) {
- if (start_command(&proc) < 0 && errno == ENOENT)
- return 0;
+ if (start_command(&proc) < 0 && errno == ENOENT) {
+ ret = 0;
+ goto cleanup;
+ }
fprintf(stderr, "FAIL %s\n", argv[1]);
return 1;
}
- if (!strcmp(argv[1], "run-command"))
- exit(run_command(&proc));
+ if (!strcmp(argv[1], "run-command")) {
+ ret = run_command(&proc);
+ goto cleanup;
+ }
if (!strcmp(argv[1], "--ungroup")) {
argv += 1;
argc -= 1;
- run_processes_parallel_ungroup = 1;
+ opts.ungroup = 1;
}
jobs = atoi(argv[2]);
strvec_clear(&proc.args);
strvec_pushv(&proc.args, (const char **)argv + 3);
- if (!strcmp(argv[1], "run-command-parallel"))
- exit(run_processes_parallel(jobs, parallel_next,
- NULL, NULL, &proc));
-
- if (!strcmp(argv[1], "run-command-abort"))
- exit(run_processes_parallel(jobs, parallel_next,
- NULL, task_finished, &proc));
-
- if (!strcmp(argv[1], "run-command-no-jobs"))
- exit(run_processes_parallel(jobs, no_job,
- NULL, task_finished, &proc));
-
- fprintf(stderr, "check usage\n");
- return 1;
+ if (!strcmp(argv[1], "run-command-parallel")) {
+ opts.get_next_task = parallel_next;
+ } else if (!strcmp(argv[1], "run-command-abort")) {
+ opts.get_next_task = parallel_next;
+ opts.task_finished = task_finished;
+ } else if (!strcmp(argv[1], "run-command-no-jobs")) {
+ opts.get_next_task = no_job;
+ opts.task_finished = task_finished;
+ } else {
+ ret = 1;
+ fprintf(stderr, "check usage\n");
+ goto cleanup;
+ }
+ opts.processes = jobs;
+ run_processes_parallel(&opts);
+ ret = 0;
+cleanup:
+ child_process_clear(&proc);
+ return ret;
}
diff --git a/t/helper/test-scrap-cache-tree.c b/t/helper/test-scrap-cache-tree.c
index 026c802..a26107e 100644
--- a/t/helper/test-scrap-cache-tree.c
+++ b/t/helper/test-scrap-cache-tree.c
@@ -1,3 +1,4 @@
+#define USE_THE_INDEX_VARIABLE
#include "test-tool.h"
#include "cache.h"
#include "lockfile.h"
@@ -9,11 +10,11 @@ int cmd__scrap_cache_tree(int ac, const char **av)
struct lock_file index_lock = LOCK_INIT;
setup_git_directory();
- hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
- if (read_cache() < 0)
+ repo_hold_locked_index(the_repository, &index_lock, LOCK_DIE_ON_ERROR);
+ if (repo_read_index(the_repository) < 0)
die("unable to read index file");
- cache_tree_free(&active_cache_tree);
- active_cache_tree = NULL;
+ cache_tree_free(&the_index.cache_tree);
+ the_index.cache_tree = NULL;
if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
die("unable to write index file");
return 0;
diff --git a/t/helper/test-serve-v2.c b/t/helper/test-serve-v2.c
index 28e905a..824e5c0 100644
--- a/t/helper/test-serve-v2.c
+++ b/t/helper/test-serve-v2.c
@@ -24,7 +24,7 @@ int cmd__serve_v2(int argc, const char **argv)
/* ignore all unknown cmdline switches for now */
argc = parse_options(argc, argv, prefix, options, serve_usage,
PARSE_OPT_KEEP_DASHDASH |
- PARSE_OPT_KEEP_UNKNOWN);
+ PARSE_OPT_KEEP_UNKNOWN_OPT);
if (advertise_capabilities)
protocol_v2_advertise_capabilities();
diff --git a/t/helper/test-sha1.c b/t/helper/test-sha1.c
index d860c38..71fe5c6 100644
--- a/t/helper/test-sha1.c
+++ b/t/helper/test-sha1.c
@@ -5,3 +5,11 @@ int cmd__sha1(int ac, const char **av)
{
return cmd_hash_impl(ac, av, GIT_HASH_SHA1);
}
+
+int cmd__sha1_is_sha1dc(int argc UNUSED, const char **argv UNUSED)
+{
+#ifdef platform_SHA_IS_SHA1DC
+ return 0;
+#endif
+ return 1;
+}
diff --git a/t/helper/test-submodule-config.c b/t/helper/test-submodule-config.c
index e269274..22a41c4 100644
--- a/t/helper/test-submodule-config.c
+++ b/t/helper/test-submodule-config.c
@@ -15,14 +15,11 @@ int cmd__submodule_config(int argc, const char **argv)
{
const char **arg = argv;
int my_argc = argc;
- int output_url = 0;
int lookup_name = 0;
arg++;
my_argc--;
while (arg[0] && starts_with(arg[0], "--")) {
- if (!strcmp(arg[0], "--url"))
- output_url = 1;
if (!strcmp(arg[0], "--name"))
lookup_name = 1;
arg++;
@@ -57,12 +54,8 @@ int cmd__submodule_config(int argc, const char **argv)
if (!submodule)
die_usage(argc, argv, "Submodule not found.");
- if (output_url)
- printf("Submodule url: '%s' for path '%s'\n",
- submodule->url, submodule->path);
- else
- printf("Submodule name: '%s' for path '%s'\n",
- submodule->name, submodule->path);
+ printf("Submodule name: '%s' for path '%s'\n", submodule->name,
+ submodule->path);
arg += 2;
}
diff --git a/t/helper/test-submodule.c b/t/helper/test-submodule.c
new file mode 100644
index 0000000..e060cc6
--- /dev/null
+++ b/t/helper/test-submodule.c
@@ -0,0 +1,224 @@
+#include "test-tool.h"
+#include "test-tool-utils.h"
+#include "cache.h"
+#include "parse-options.h"
+#include "remote.h"
+#include "submodule-config.h"
+#include "submodule.h"
+
+#define TEST_TOOL_CHECK_NAME_USAGE \
+ "test-tool submodule check-name <name>"
+static const char *submodule_check_name_usage[] = {
+ TEST_TOOL_CHECK_NAME_USAGE,
+ NULL
+};
+
+#define TEST_TOOL_IS_ACTIVE_USAGE \
+ "test-tool submodule is-active <name>"
+static const char *submodule_is_active_usage[] = {
+ TEST_TOOL_IS_ACTIVE_USAGE,
+ NULL
+};
+
+#define TEST_TOOL_RESOLVE_RELATIVE_URL_USAGE \
+ "test-tool submodule resolve-relative-url <up_path> <remoteurl> <url>"
+static const char *submodule_resolve_relative_url_usage[] = {
+ TEST_TOOL_RESOLVE_RELATIVE_URL_USAGE,
+ NULL,
+};
+
+static const char *submodule_usage[] = {
+ TEST_TOOL_CHECK_NAME_USAGE,
+ TEST_TOOL_IS_ACTIVE_USAGE,
+ TEST_TOOL_RESOLVE_RELATIVE_URL_USAGE,
+ NULL
+};
+
+/*
+ * Exit non-zero if any of the submodule names given on the command line is
+ * invalid. If no names are given, filter stdin to print only valid names
+ * (which is primarily intended for testing).
+ */
+static int check_name(int argc, const char **argv)
+{
+ if (argc > 1) {
+ while (*++argv) {
+ if (check_submodule_name(*argv) < 0)
+ return 1;
+ }
+ } else {
+ struct strbuf buf = STRBUF_INIT;
+ while (strbuf_getline(&buf, stdin) != EOF) {
+ if (!check_submodule_name(buf.buf))
+ printf("%s\n", buf.buf);
+ }
+ strbuf_release(&buf);
+ }
+ return 0;
+}
+
+static int cmd__submodule_check_name(int argc, const char **argv)
+{
+ struct option options[] = {
+ OPT_END()
+ };
+ argc = parse_options(argc, argv, "test-tools", options,
+ submodule_check_name_usage, 0);
+ if (argc)
+ usage_with_options(submodule_check_name_usage, options);
+
+ return check_name(argc, argv);
+}
+
+static int cmd__submodule_is_active(int argc, const char **argv)
+{
+ struct option options[] = {
+ OPT_END()
+ };
+ argc = parse_options(argc, argv, "test-tools", options,
+ submodule_is_active_usage, 0);
+ if (argc != 1)
+ usage_with_options(submodule_is_active_usage, options);
+
+ setup_git_directory();
+
+ return !is_submodule_active(the_repository, argv[0]);
+}
+
+static int cmd__submodule_resolve_relative_url(int argc, const char **argv)
+{
+ char *remoteurl, *res;
+ const char *up_path, *url;
+ struct option options[] = {
+ OPT_END()
+ };
+ argc = parse_options(argc, argv, "test-tools", options,
+ submodule_resolve_relative_url_usage, 0);
+ if (argc != 3)
+ usage_with_options(submodule_resolve_relative_url_usage, options);
+
+ up_path = argv[0];
+ remoteurl = xstrdup(argv[1]);
+ url = argv[2];
+
+ if (!strcmp(up_path, "(null)"))
+ up_path = NULL;
+
+ res = relative_url(remoteurl, url, up_path);
+ puts(res);
+ free(res);
+ free(remoteurl);
+ return 0;
+}
+
+static int cmd__submodule_config_list(int argc, const char **argv)
+{
+ struct option options[] = {
+ OPT_END()
+ };
+ const char *const usage[] = {
+ "test-tool submodule config-list <key>",
+ NULL
+ };
+ argc = parse_options(argc, argv, "test-tools", options, usage,
+ PARSE_OPT_KEEP_ARGV0);
+
+ setup_git_directory();
+
+ if (argc == 2)
+ return print_config_from_gitmodules(the_repository, argv[1]);
+ usage_with_options(usage, options);
+}
+
+static int cmd__submodule_config_set(int argc, const char **argv)
+{
+ struct option options[] = {
+ OPT_END()
+ };
+ const char *const usage[] = {
+ "test-tool submodule config-set <key> <value>",
+ NULL
+ };
+ argc = parse_options(argc, argv, "test-tools", options, usage,
+ PARSE_OPT_KEEP_ARGV0);
+
+ setup_git_directory();
+
+ /* Equivalent to ACTION_SET in builtin/config.c */
+ if (argc == 3) {
+ if (!is_writing_gitmodules_ok())
+ die("please make sure that the .gitmodules file is in the working tree");
+
+ return config_set_in_gitmodules_file_gently(argv[1], argv[2]);
+ }
+ usage_with_options(usage, options);
+}
+
+static int cmd__submodule_config_unset(int argc, const char **argv)
+{
+ struct option options[] = {
+ OPT_END()
+ };
+ const char *const usage[] = {
+ "test-tool submodule config-unset <key>",
+ NULL
+ };
+
+ setup_git_directory();
+
+ if (argc == 2) {
+ if (!is_writing_gitmodules_ok())
+ die("please make sure that the .gitmodules file is in the working tree");
+ return config_set_in_gitmodules_file_gently(argv[1], NULL);
+ }
+ usage_with_options(usage, options);
+}
+
+static int cmd__submodule_config_writeable(int argc, const char **argv)
+{
+ struct option options[] = {
+ OPT_END()
+ };
+ const char *const usage[] = {
+ "test-tool submodule config-writeable",
+ NULL
+ };
+ setup_git_directory();
+
+ if (argc == 1)
+ return is_writing_gitmodules_ok() ? 0 : -1;
+
+ usage_with_options(usage, options);
+}
+
+static struct test_cmd cmds[] = {
+ { "check-name", cmd__submodule_check_name },
+ { "is-active", cmd__submodule_is_active },
+ { "resolve-relative-url", cmd__submodule_resolve_relative_url},
+ { "config-list", cmd__submodule_config_list },
+ { "config-set", cmd__submodule_config_set },
+ { "config-unset", cmd__submodule_config_unset },
+ { "config-writeable", cmd__submodule_config_writeable },
+};
+
+int cmd__submodule(int argc, const char **argv)
+{
+ struct option options[] = {
+ OPT_END()
+ };
+ size_t i;
+
+ argc = parse_options(argc, argv, "test-tools", options, submodule_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+ if (argc < 1)
+ usage_with_options(submodule_usage, options);
+
+ for (i = 0; i < ARRAY_SIZE(cmds); i++)
+ if (!strcmp(cmds[i].name, argv[0]))
+ return cmds[i].fn(argc, argv);
+
+ usage_msg_optf("unknown subcommand '%s'", submodule_usage, options,
+ argv[0]);
+
+ return 0;
+}
diff --git a/t/helper/test-tool-utils.h b/t/helper/test-tool-utils.h
new file mode 100644
index 0000000..6a0e5e0
--- /dev/null
+++ b/t/helper/test-tool-utils.h
@@ -0,0 +1,9 @@
+#ifndef TEST_TOOL_UTILS_H
+#define TEST_TOOL_UTILS_H
+
+struct test_cmd {
+ const char *name;
+ int (*fn)(int argc, const char **argv);
+};
+
+#endif
diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c
index 318fdba..7eb1a26 100644
--- a/t/helper/test-tool.c
+++ b/t/helper/test-tool.c
@@ -1,5 +1,6 @@
#include "git-compat-util.h"
#include "test-tool.h"
+#include "test-tool-utils.h"
#include "trace2.h"
#include "parse-options.h"
@@ -8,15 +9,12 @@ static const char * const test_tool_usage[] = {
NULL
};
-struct test_cmd {
- const char *name;
- int (*fn)(int argc, const char **argv);
-};
-
static struct test_cmd cmds[] = {
{ "advise", cmd__advise_if_enabled },
{ "bitmap", cmd__bitmap },
{ "bloom", cmd__bloom },
+ { "bundle-uri", cmd__bundle_uri },
+ { "cache-tree", cmd__cache_tree },
{ "chmtime", cmd__chmtime },
{ "config", cmd__config },
{ "crontab", cmd__crontab },
@@ -51,7 +49,9 @@ static struct test_cmd cmds[] = {
{ "online-cpus", cmd__online_cpus },
{ "pack-mtimes", cmd__pack_mtimes },
{ "parse-options", cmd__parse_options },
+ { "parse-options-flags", cmd__parse_options_flags },
{ "parse-pathspec-file", cmd__parse_pathspec_file },
+ { "parse-subcommand", cmd__parse_subcommand },
{ "partial-clone", cmd__partial_clone },
{ "path-utils", cmd__path_utils },
{ "pcre2-config", cmd__pcre2_config },
@@ -65,6 +65,7 @@ static struct test_cmd cmds[] = {
{ "read-midx", cmd__read_midx },
{ "ref-store", cmd__ref_store },
{ "reftable", cmd__reftable },
+ { "rot13-filter", cmd__rot13_filter },
{ "dump-reftable", cmd__dump_reftable },
{ "regex", cmd__regex },
{ "repository", cmd__repository },
@@ -73,11 +74,13 @@ static struct test_cmd cmds[] = {
{ "scrap-cache-tree", cmd__scrap_cache_tree },
{ "serve-v2", cmd__serve_v2 },
{ "sha1", cmd__sha1 },
+ { "sha1-is-sha1dc", cmd__sha1_is_sha1dc },
{ "sha256", cmd__sha256 },
{ "sigchain", cmd__sigchain },
{ "simple-ipc", cmd__simple_ipc },
{ "strcmp-offset", cmd__strcmp_offset },
{ "string-list", cmd__string_list },
+ { "submodule", cmd__submodule },
{ "submodule-config", cmd__submodule_config },
{ "submodule-nested-repo-config", cmd__submodule_nested_repo_config },
{ "subprocess", cmd__subprocess },
diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h
index bb79927..2e20a16 100644
--- a/t/helper/test-tool.h
+++ b/t/helper/test-tool.h
@@ -1,12 +1,13 @@
#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__bitmap(int argc, const char **argv);
int cmd__bloom(int argc, const char **argv);
+int cmd__bundle_uri(int argc, const char **argv);
+int cmd__cache_tree(int argc, const char **argv);
int cmd__chmtime(int argc, const char **argv);
int cmd__config(int argc, const char **argv);
int cmd__crontab(int argc, const char **argv);
@@ -41,7 +42,9 @@ int cmd__oidtree(int argc, const char **argv);
int cmd__online_cpus(int argc, const char **argv);
int cmd__pack_mtimes(int argc, const char **argv);
int cmd__parse_options(int argc, const char **argv);
+int cmd__parse_options_flags(int argc, const char **argv);
int cmd__parse_pathspec_file(int argc, const char** argv);
+int cmd__parse_subcommand(int argc, const char **argv);
int cmd__partial_clone(int argc, const char **argv);
int cmd__path_utils(int argc, const char **argv);
int cmd__pcre2_config(int argc, const char **argv);
@@ -54,6 +57,7 @@ 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__rot13_filter(int argc, const char **argv);
int cmd__reftable(int argc, const char **argv);
int cmd__regex(int argc, const char **argv);
int cmd__repository(int argc, const char **argv);
@@ -62,12 +66,14 @@ 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_is_sha1dc(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__simple_ipc(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(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);
diff --git a/t/helper/test-trace2.c b/t/helper/test-trace2.c
index a714130..f374c21 100644
--- a/t/helper/test-trace2.c
+++ b/t/helper/test-trace2.c
@@ -132,6 +132,7 @@ static int ut_003error(int argc, const char **argv)
*/
static int ut_004child(int argc, const char **argv)
{
+ struct child_process cmd = CHILD_PROCESS_INIT;
int result;
/*
@@ -141,7 +142,8 @@ static int ut_004child(int argc, const char **argv)
if (!argc)
return 0;
- result = run_command_v_opt(argv, 0);
+ strvec_pushv(&cmd.args, argv);
+ result = run_command(&cmd);
exit(result);
}
@@ -229,6 +231,187 @@ static int ut_010bug_BUG(int argc, const char **argv)
}
/*
+ * Single-threaded timer test. Create several intervals using the
+ * TEST1 timer. The test script can verify that an aggregate Trace2
+ * "timer" event is emitted indicating that we started+stopped the
+ * timer the requested number of times.
+ */
+static int ut_100timer(int argc, const char **argv)
+{
+ const char *usage_error =
+ "expect <count> <ms_delay>";
+
+ int count = 0;
+ int delay = 0;
+ int k;
+
+ if (argc != 2)
+ die("%s", usage_error);
+ if (get_i(&count, argv[0]))
+ die("%s", usage_error);
+ if (get_i(&delay, argv[1]))
+ die("%s", usage_error);
+
+ for (k = 0; k < count; k++) {
+ trace2_timer_start(TRACE2_TIMER_ID_TEST1);
+ sleep_millisec(delay);
+ trace2_timer_stop(TRACE2_TIMER_ID_TEST1);
+ }
+
+ return 0;
+}
+
+struct ut_101_data {
+ int count;
+ int delay;
+};
+
+static void *ut_101timer_thread_proc(void *_ut_101_data)
+{
+ struct ut_101_data *data = _ut_101_data;
+ int k;
+
+ trace2_thread_start("ut_101");
+
+ for (k = 0; k < data->count; k++) {
+ trace2_timer_start(TRACE2_TIMER_ID_TEST2);
+ sleep_millisec(data->delay);
+ trace2_timer_stop(TRACE2_TIMER_ID_TEST2);
+ }
+
+ trace2_thread_exit();
+ return NULL;
+}
+
+/*
+ * Multi-threaded timer test. Create several threads that each create
+ * several intervals using the TEST2 timer. The test script can verify
+ * that an individual Trace2 "th_timer" events for each thread and an
+ * aggregate "timer" event are generated.
+ */
+static int ut_101timer(int argc, const char **argv)
+{
+ const char *usage_error =
+ "expect <count> <ms_delay> <threads>";
+
+ struct ut_101_data data = { 0, 0 };
+ int nr_threads = 0;
+ int k;
+ pthread_t *pids = NULL;
+
+ if (argc != 3)
+ die("%s", usage_error);
+ if (get_i(&data.count, argv[0]))
+ die("%s", usage_error);
+ if (get_i(&data.delay, argv[1]))
+ die("%s", usage_error);
+ if (get_i(&nr_threads, argv[2]))
+ die("%s", usage_error);
+
+ CALLOC_ARRAY(pids, nr_threads);
+
+ for (k = 0; k < nr_threads; k++) {
+ if (pthread_create(&pids[k], NULL, ut_101timer_thread_proc, &data))
+ die("failed to create thread[%d]", k);
+ }
+
+ for (k = 0; k < nr_threads; k++) {
+ if (pthread_join(pids[k], NULL))
+ die("failed to join thread[%d]", k);
+ }
+
+ free(pids);
+
+ return 0;
+}
+
+/*
+ * Single-threaded counter test. Add several values to the TEST1 counter.
+ * The test script can verify that the final sum is reported in the "counter"
+ * event.
+ */
+static int ut_200counter(int argc, const char **argv)
+{
+ const char *usage_error =
+ "expect <v1> [<v2> [...]]";
+ int value;
+ int k;
+
+ if (argc < 1)
+ die("%s", usage_error);
+
+ for (k = 0; k < argc; k++) {
+ if (get_i(&value, argv[k]))
+ die("invalid value[%s] -- %s",
+ argv[k], usage_error);
+ trace2_counter_add(TRACE2_COUNTER_ID_TEST1, value);
+ }
+
+ return 0;
+}
+
+/*
+ * Multi-threaded counter test. Create seveal threads that each increment
+ * the TEST2 global counter. The test script can verify that an individual
+ * "th_counter" event is generated with a partial sum for each thread and
+ * that a final aggregate "counter" event is generated.
+ */
+
+struct ut_201_data {
+ int v1;
+ int v2;
+};
+
+static void *ut_201counter_thread_proc(void *_ut_201_data)
+{
+ struct ut_201_data *data = _ut_201_data;
+
+ trace2_thread_start("ut_201");
+
+ trace2_counter_add(TRACE2_COUNTER_ID_TEST2, data->v1);
+ trace2_counter_add(TRACE2_COUNTER_ID_TEST2, data->v2);
+
+ trace2_thread_exit();
+ return NULL;
+}
+
+static int ut_201counter(int argc, const char **argv)
+{
+ const char *usage_error =
+ "expect <v1> <v2> <threads>";
+
+ struct ut_201_data data = { 0, 0 };
+ int nr_threads = 0;
+ int k;
+ pthread_t *pids = NULL;
+
+ if (argc != 3)
+ die("%s", usage_error);
+ if (get_i(&data.v1, argv[0]))
+ die("%s", usage_error);
+ if (get_i(&data.v2, argv[1]))
+ die("%s", usage_error);
+ if (get_i(&nr_threads, argv[2]))
+ die("%s", usage_error);
+
+ CALLOC_ARRAY(pids, nr_threads);
+
+ for (k = 0; k < nr_threads; k++) {
+ if (pthread_create(&pids[k], NULL, ut_201counter_thread_proc, &data))
+ die("failed to create thread[%d]", k);
+ }
+
+ for (k = 0; k < nr_threads; k++) {
+ if (pthread_join(pids[k], NULL))
+ die("failed to join thread[%d]", k);
+ }
+
+ free(pids);
+
+ return 0;
+}
+
+/*
* Usage:
* test-tool trace2 <ut_name_1> <ut_usage_1>
* test-tool trace2 <ut_name_2> <ut_usage_2>
@@ -248,6 +431,12 @@ static struct unit_test ut_table[] = {
{ ut_008bug, "008bug", "" },
{ ut_009bug_BUG, "009bug_BUG","" },
{ ut_010bug_BUG, "010bug_BUG","" },
+
+ { ut_100timer, "100timer", "<count> <ms_delay>" },
+ { ut_101timer, "101timer", "<count> <ms_delay> <threads>" },
+
+ { ut_200counter, "200counter", "<v1> [<v2> [<v3> [...]]]" },
+ { ut_201counter, "201counter", "<v1> <v2> <threads>" },
};
/* clang-format on */
diff --git a/t/helper/test-userdiff.c b/t/helper/test-userdiff.c
index f013f8a..a2b56b9 100644
--- a/t/helper/test-userdiff.c
+++ b/t/helper/test-userdiff.c
@@ -12,7 +12,7 @@ static int driver_cb(struct userdiff_driver *driver,
return 0;
}
-static int cmd__userdiff_config(const char *var, const char *value, void *cb)
+static int cmd__userdiff_config(const char *var, const char *value, void *cb UNUSED)
{
if (userdiff_config(var, value) < 0)
return -1;
diff --git a/t/helper/test-write-cache.c b/t/helper/test-write-cache.c
index 8837717..7d45cd6 100644
--- a/t/helper/test-write-cache.c
+++ b/t/helper/test-write-cache.c
@@ -1,3 +1,4 @@
+#define USE_THE_INDEX_VARIABLE
#include "test-tool.h"
#include "cache.h"
#include "lockfile.h"
@@ -9,9 +10,10 @@ int cmd__write_cache(int argc, const char **argv)
if (argc == 2)
cnt = strtol(argv[1], NULL, 0);
setup_git_directory();
- read_cache();
+ repo_read_index(the_repository);
for (i = 0; i < cnt; i++) {
- hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
+ repo_hold_locked_index(the_repository, &index_lock,
+ LOCK_DIE_ON_ERROR);
if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
die("unable to write index file");
}
diff --git a/t/lib-bitmap.sh b/t/lib-bitmap.sh
index a95537e..f595937 100644
--- a/t/lib-bitmap.sh
+++ b/t/lib-bitmap.sh
@@ -440,7 +440,7 @@ midx_bitmap_partial_tests () {
test_commit packed &&
git repack &&
test_commit loose &&
- git multi-pack-index write --bitmap 2>err &&
+ git multi-pack-index write --bitmap &&
test_path_is_file $midx &&
test_path_is_file $midx-$(midx_checksum $objdir).bitmap
'
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index 1f6b9b0..608949e 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -65,7 +65,8 @@ done
for DEFAULT_HTTPD_MODULE_PATH in '/usr/libexec/apache2' \
'/usr/lib/apache2/modules' \
'/usr/lib64/httpd/modules' \
- '/usr/lib/httpd/modules'
+ '/usr/lib/httpd/modules' \
+ '/usr/libexec/httpd'
do
if test -d "$DEFAULT_HTTPD_MODULE_PATH"
then
@@ -174,6 +175,11 @@ prepare_httpd() {
fi
}
+enable_http2 () {
+ HTTPD_PARA="$HTTPD_PARA -DHTTP2"
+ test_set_prereq HTTP2
+}
+
start_httpd() {
prepare_httpd >&3 2>&4
diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf
index 497b9b9..0294739 100644
--- a/t/lib-httpd/apache.conf
+++ b/t/lib-httpd/apache.conf
@@ -29,6 +29,11 @@ ErrorLog error.log
LoadModule setenvif_module modules/mod_setenvif.so
</IfModule>
+<IfDefine HTTP2>
+LoadModule http2_module modules/mod_http2.so
+Protocols h2c
+</IfDefine>
+
<IfVersion < 2.4>
LockFile accept.lock
</IfVersion>
@@ -64,12 +69,20 @@ LockFile accept.lock
<IfModule !mod_access_compat.c>
LoadModule access_compat_module modules/mod_access_compat.so
</IfModule>
-<IfModule !mod_mpm_prefork.c>
- LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
-</IfModule>
<IfModule !mod_unixd.c>
LoadModule unixd_module modules/mod_unixd.so
</IfModule>
+
+<IfDefine HTTP2>
+<IfModule !mod_mpm_event.c>
+ LoadModule mpm_event_module modules/mod_mpm_event.so
+</IfModule>
+</IfDefine>
+<IfDefine !HTTP2>
+<IfModule !mod_mpm_prefork.c>
+ LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
+</IfModule>
+</IfDefine>
</IfVersion>
PassEnv GIT_VALGRIND
@@ -80,6 +93,8 @@ PassEnv LSAN_OPTIONS
PassEnv GIT_TRACE
PassEnv GIT_CONFIG_NOSYSTEM
PassEnv GIT_TEST_SIDEBAND_ALL
+PassEnv LANG
+PassEnv LC_ALL
Alias /dumb/ www/
Alias /auth/dumb/ www/auth/dumb/
diff --git a/t/lib-perl.sh b/t/lib-perl.sh
new file mode 100644
index 0000000..d0bf509
--- /dev/null
+++ b/t/lib-perl.sh
@@ -0,0 +1,19 @@
+# Copyright (c) 2022 Ævar Arnfjörð Bjarmason
+
+test_lazy_prereq PERL_TEST_MORE '
+ perl -MTest::More -e 0
+'
+
+skip_all_if_no_Test_More () {
+ if ! test_have_prereq PERL
+ then
+ skip_all='skipping perl interface tests, perl not available'
+ test_done
+ fi
+
+ if ! test_have_prereq PERL_TEST_MORE
+ then
+ skip_all="Perl Test::More unavailable, skipping test"
+ test_done
+ fi
+}
diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh
index 03e0abb..2d31fcf 100644
--- a/t/lib-submodule-update.sh
+++ b/t/lib-submodule-update.sh
@@ -197,6 +197,7 @@ test_git_directory_exists () {
# the submodule repo if it doesn't exist and configures the most problematic
# settings for diff.ignoreSubmodules.
prolog () {
+ test_config_global protocol.file.allow always &&
(test -d submodule_update_repo || create_lib_submodule_repo) &&
test_config_global diff.ignoreSubmodules all &&
test_config diff.ignoreSubmodules all
diff --git a/t/perf/README b/t/perf/README
index fb9127a..8f217d7 100644
--- a/t/perf/README
+++ b/t/perf/README
@@ -95,6 +95,10 @@ You can set the following variables (also in your config.mak):
Git (e.g., performance of index-pack as the number of threads
changes). These can be enabled with GIT_PERF_EXTRA.
+ GIT_PERF_USE_SCALAR
+ Boolean indicating whether to register test repo(s) with Scalar
+ before executing tests.
+
You can also pass the options taken by ordinary git tests; the most
useful one is:
diff --git a/t/perf/lib-bitmap.sh b/t/perf/lib-bitmap.sh
index 63d3bc7..55a8feb 100644
--- a/t/perf/lib-bitmap.sh
+++ b/t/perf/lib-bitmap.sh
@@ -67,3 +67,34 @@ test_partial_bitmap () {
--filter=tree:0 >/dev/null
'
}
+
+test_pack_bitmap () {
+ test_perf "repack to disk" '
+ git repack -ad
+ '
+
+ test_full_bitmap
+
+ 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) &&
+ orig_tip=$(git rev-parse HEAD) &&
+
+ # now kill off all of the refs and pretend we had
+ # just the one tip
+ rm -rf .git/logs .git/refs/* .git/packed-refs &&
+ git update-ref HEAD $cutoff &&
+
+ # and then repack, which will leave us with a nice
+ # big bitmap pack of the "old" history, and all of
+ # the new history will be loose, as if it had been pushed
+ # up incrementally and exploded via unpack-objects
+ git repack -Ad &&
+
+ # and now restore our original tip, as if the pushes
+ # had happened
+ git update-ref HEAD $orig_tip
+ '
+
+ test_partial_bitmap
+}
diff --git a/t/perf/p0004-lazy-init-name-hash.sh b/t/perf/p0004-lazy-init-name-hash.sh
index 1afc08f..85be14e 100755
--- a/t/perf/p0004-lazy-init-name-hash.sh
+++ b/t/perf/p0004-lazy-init-name-hash.sh
@@ -49,7 +49,7 @@ test_perf "single-threaded, $desc" "
test-tool lazy-init-name-hash --single --count=$count
"
-test_perf REPO_BIG_ENOUGH_FOR_MULTI "multi-threaded, $desc" "
+test_perf "multi-threaded, $desc" --prereq REPO_BIG_ENOUGH_FOR_MULTI "
test-tool lazy-init-name-hash --multi --count=$count
"
diff --git a/t/perf/p0006-read-tree-checkout.sh b/t/perf/p0006-read-tree-checkout.sh
index 900b385..325566e 100755
--- a/t/perf/p0006-read-tree-checkout.sh
+++ b/t/perf/p0006-read-tree-checkout.sh
@@ -46,7 +46,15 @@ test_expect_success "setup repo" '
'
test_perf "read-tree br_base br_ballast ($nr_files)" '
- git read-tree -m br_base br_ballast -n
+ git read-tree -n -m br_base br_ballast
+'
+
+test_perf "read-tree br_ballast_plus_1 ($nr_files)" '
+ # Run read-tree 100 times for clearer performance results & comparisons
+ for i in $(test_seq 100)
+ do
+ git read-tree -n -m br_ballast_plus_1 || return 1
+ done
'
test_perf "switch between br_base br_ballast ($nr_files)" '
diff --git a/t/perf/p0090-cache-tree.sh b/t/perf/p0090-cache-tree.sh
new file mode 100755
index 0000000..a8eabca
--- /dev/null
+++ b/t/perf/p0090-cache-tree.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+test_description="Tests performance of cache tree update operations"
+
+. ./perf-lib.sh
+
+test_perf_large_repo
+test_checkout_worktree
+
+count=100
+
+test_expect_success 'setup cache tree' '
+ git write-tree
+'
+
+test_cache_tree () {
+ test_perf "$1, $3" "
+ for i in \$(test_seq $count)
+ do
+ test-tool cache-tree $4 $2
+ done
+ "
+}
+
+test_cache_tree_update_functions () {
+ test_cache_tree 'no-op' 'control' "$1" "$2"
+ test_cache_tree 'prime_cache_tree' 'prime' "$1" "$2"
+ test_cache_tree 'cache_tree_update' 'update' "$1" "$2"
+}
+
+test_cache_tree_update_functions "clean" ""
+test_cache_tree_update_functions "invalidate 2" "--invalidate 2"
+test_cache_tree_update_functions "invalidate 50" "--invalidate 50"
+test_cache_tree_update_functions "empty" "--empty"
+
+test_done
diff --git a/t/perf/p2000-sparse-operations.sh b/t/perf/p2000-sparse-operations.sh
index c181110..3242cfe 100755
--- a/t/perf/p2000-sparse-operations.sh
+++ b/t/perf/p2000-sparse-operations.sh
@@ -123,5 +123,7 @@ test_perf_on_all git blame $SPARSE_CONE/f3/a
test_perf_on_all git read-tree -mu HEAD
test_perf_on_all git checkout-index -f --all
test_perf_on_all git update-index --add --remove $SPARSE_CONE/a
+test_perf_on_all "git rm -f $SPARSE_CONE/a && git checkout HEAD -- $SPARSE_CONE/a"
+test_perf_on_all git grep --cached --sparse bogus -- "f2/f1/f1/*"
test_done
diff --git a/t/perf/p5310-pack-bitmaps.sh b/t/perf/p5310-pack-bitmaps.sh
index 7ad4f23..b1399f1 100755
--- a/t/perf/p5310-pack-bitmaps.sh
+++ b/t/perf/p5310-pack-bitmaps.sh
@@ -4,51 +4,37 @@ test_description='Tests pack performance using bitmaps'
. ./perf-lib.sh
. "${TEST_DIRECTORY}/perf/lib-bitmap.sh"
-test_perf_large_repo
-
-# note that we do everything through config,
-# since we want to be able to compare bitmap-aware
-# git versus non-bitmap git
-#
-# 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
-'
-
-# we need to create the tag up front such that it is covered by the repack and
-# thus by generated bitmaps.
-test_expect_success 'create tags' '
- git tag --message="tag pointing to HEAD" perf-tag HEAD
-'
-
-test_perf 'repack to disk' '
- git repack -ad
-'
-
-test_full_bitmap
-
-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) &&
- orig_tip=$(git rev-parse HEAD) &&
-
- # now kill off all of the refs and pretend we had
- # just the one tip
- rm -rf .git/logs .git/refs/* .git/packed-refs &&
- git update-ref HEAD $cutoff &&
-
- # and then repack, which will leave us with a nice
- # big bitmap pack of the "old" history, and all of
- # the new history will be loose, as if it had been pushed
- # up incrementally and exploded via unpack-objects
- git repack -Ad &&
-
- # and now restore our original tip, as if the pushes
- # had happened
- git update-ref HEAD $orig_tip
-'
-
-test_partial_bitmap
+test_lookup_pack_bitmap () {
+ test_expect_success 'start the test from scratch' '
+ rm -rf * .git
+ '
+
+ test_perf_large_repo
+
+ # note that we do everything through config,
+ # since we want to be able to compare bitmap-aware
+ # git versus non-bitmap git
+ #
+ # 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
+ '
+
+ # we need to create the tag up front such that it is covered by the repack and
+ # thus by generated bitmaps.
+ test_expect_success 'create tags' '
+ git tag --message="tag pointing to HEAD" perf-tag HEAD
+ '
+
+ test_perf "enable lookup table: $1" '
+ git config pack.writeBitmapLookupTable '"$1"'
+ '
+
+ test_pack_bitmap
+}
+
+test_lookup_pack_bitmap false
+test_lookup_pack_bitmap true
test_done
diff --git a/t/perf/p5311-pack-bitmaps-fetch.sh b/t/perf/p5311-pack-bitmaps-fetch.sh
index 47c3fd7..426fab8 100755
--- a/t/perf/p5311-pack-bitmaps-fetch.sh
+++ b/t/perf/p5311-pack-bitmaps-fetch.sh
@@ -3,42 +3,52 @@
test_description='performance of fetches from bitmapped packs'
. ./perf-lib.sh
-test_perf_default_repo
-
-test_expect_success 'create bitmapped server repo' '
- git config pack.writebitmaps true &&
- git repack -ad
-'
-
-# simulate a fetch from a repository that last fetched N days ago, for
-# various values of N. We do so by following the first-parent chain,
-# and assume the first entry in the chain that is N days older than the current
-# HEAD is where the HEAD would have been then.
-for days in 1 2 4 8 16 32 64 128; do
- title=$(printf '%10s' "($days days)")
- test_expect_success "setup revs from $days days ago" '
- now=$(git log -1 --format=%ct HEAD) &&
- then=$(($now - ($days * 86400))) &&
- tip=$(git rev-list -1 --first-parent --until=$then HEAD) &&
- {
- echo HEAD &&
- echo ^$tip
- } >revs
+test_fetch_bitmaps () {
+ test_expect_success 'setup test directory' '
+ rm -fr * .git
'
- test_perf "server $title" '
- git pack-objects --stdout --revs \
- --thin --delta-base-offset \
- <revs >tmp.pack
- '
+ test_perf_default_repo
- test_size "size $title" '
- wc -c <tmp.pack
+ test_expect_success 'create bitmapped server repo' '
+ git config pack.writebitmaps true &&
+ git config pack.writeBitmapLookupTable '"$1"' &&
+ git repack -ad
'
- test_perf "client $title" '
- git index-pack --stdin --fix-thin <tmp.pack
- '
-done
+ # simulate a fetch from a repository that last fetched N days ago, for
+ # various values of N. We do so by following the first-parent chain,
+ # and assume the first entry in the chain that is N days older than the current
+ # HEAD is where the HEAD would have been then.
+ for days in 1 2 4 8 16 32 64 128; do
+ title=$(printf '%10s' "($days days)")
+ test_expect_success "setup revs from $days days ago" '
+ now=$(git log -1 --format=%ct HEAD) &&
+ then=$(($now - ($days * 86400))) &&
+ tip=$(git rev-list -1 --first-parent --until=$then HEAD) &&
+ {
+ echo HEAD &&
+ echo ^$tip
+ } >revs
+ '
+
+ test_perf "server $title (lookup=$1)" '
+ git pack-objects --stdout --revs \
+ --thin --delta-base-offset \
+ <revs >tmp.pack
+ '
+
+ test_size "size $title" '
+ wc -c <tmp.pack
+ '
+
+ test_perf "client $title (lookup=$1)" '
+ git index-pack --stdin --fix-thin <tmp.pack
+ '
+ done
+}
+
+test_fetch_bitmaps true
+test_fetch_bitmaps false
test_done
diff --git a/t/perf/p5312-pack-bitmaps-revs.sh b/t/perf/p5312-pack-bitmaps-revs.sh
new file mode 100755
index 0000000..0684b69
--- /dev/null
+++ b/t/perf/p5312-pack-bitmaps-revs.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+test_description='Tests pack performance using bitmaps (rev index enabled)'
+. ./perf-lib.sh
+. "${TEST_DIRECTORY}/perf/lib-bitmap.sh"
+
+test_lookup_pack_bitmap () {
+ test_expect_success 'start the test from scratch' '
+ rm -rf * .git
+ '
+
+ test_perf_large_repo
+
+ test_expect_success 'setup bitmap config' '
+ git config pack.writebitmaps true &&
+ git config pack.writeReverseIndex true
+ '
+
+ # we need to create the tag up front such that it is covered by the repack and
+ # thus by generated bitmaps.
+ test_expect_success 'create tags' '
+ git tag --message="tag pointing to HEAD" perf-tag HEAD
+ '
+
+ test_perf "enable lookup table: $1" '
+ git config pack.writeBitmapLookupTable '"$1"'
+ '
+
+ test_pack_bitmap
+}
+
+test_lookup_pack_bitmap false
+test_lookup_pack_bitmap true
+
+test_done
diff --git a/t/perf/p5326-multi-pack-bitmaps.sh b/t/perf/p5326-multi-pack-bitmaps.sh
index f2fa228..d082e6c 100755
--- a/t/perf/p5326-multi-pack-bitmaps.sh
+++ b/t/perf/p5326-multi-pack-bitmaps.sh
@@ -4,49 +4,64 @@ test_description='Tests performance using midx bitmaps'
. ./perf-lib.sh
. "${TEST_DIRECTORY}/perf/lib-bitmap.sh"
-test_perf_large_repo
-
-# we need to create the tag up front such that it is covered by the repack and
-# thus by generated bitmaps.
-test_expect_success 'create tags' '
- git tag --message="tag pointing to HEAD" perf-tag HEAD
-'
-
-test_expect_success 'start with bitmapped pack' '
- git repack -adb
-'
-
-test_perf 'setup multi-pack index' '
- git multi-pack-index write --bitmap
-'
-
-test_expect_success 'drop pack bitmap' '
- rm -f .git/objects/pack/pack-*.bitmap
-'
-
-test_full_bitmap
-
-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) &&
- orig_tip=$(git rev-parse HEAD) &&
-
- # now pretend we have just one tip
- rm -rf .git/logs .git/refs/* .git/packed-refs &&
- git update-ref HEAD $cutoff &&
-
- # and then repack, which will leave us with a nice
- # big bitmap pack of the "old" history, and all of
- # the new history will be loose, as if it had been pushed
- # up incrementally and exploded via unpack-objects
- git repack -Ad &&
- git multi-pack-index write --bitmap &&
-
- # and now restore our original tip, as if the pushes
- # had happened
- git update-ref HEAD $orig_tip
-'
-
-test_partial_bitmap
+test_bitmap () {
+ local enabled="$1"
+
+ test_expect_success "remove existing repo (lookup=$enabled)" '
+ rm -fr * .git
+ '
+
+ test_perf_large_repo
+
+ # we need to create the tag up front such that it is covered by the repack and
+ # thus by generated bitmaps.
+ test_expect_success 'create tags' '
+ git tag --message="tag pointing to HEAD" perf-tag HEAD
+ '
+
+ test_expect_success "use lookup table: $enabled" '
+ git config pack.writeBitmapLookupTable '"$enabled"'
+ '
+
+ test_expect_success "start with bitmapped pack (lookup=$enabled)" '
+ git repack -adb
+ '
+
+ test_perf "setup multi-pack index (lookup=$enabled)" '
+ git multi-pack-index write --bitmap
+ '
+
+ test_expect_success "drop pack bitmap (lookup=$enabled)" '
+ rm -f .git/objects/pack/pack-*.bitmap
+ '
+
+ test_full_bitmap
+
+ test_expect_success "create partial bitmap state (lookup=$enabled)" '
+ # pick a commit to represent the repo tip in the past
+ cutoff=$(git rev-list HEAD~100 -1) &&
+ orig_tip=$(git rev-parse HEAD) &&
+
+ # now pretend we have just one tip
+ rm -rf .git/logs .git/refs/* .git/packed-refs &&
+ git update-ref HEAD $cutoff &&
+
+ # and then repack, which will leave us with a nice
+ # big bitmap pack of the "old" history, and all of
+ # the new history will be loose, as if it had been pushed
+ # up incrementally and exploded via unpack-objects
+ git repack -Ad &&
+ git multi-pack-index write --bitmap &&
+
+ # and now restore our original tip, as if the pushes
+ # had happened
+ git update-ref HEAD $orig_tip
+ '
+
+ test_partial_bitmap
+}
+
+test_bitmap false
+test_bitmap true
test_done
diff --git a/t/perf/p7102-reset.sh b/t/perf/p7102-reset.sh
new file mode 100755
index 0000000..9b039e8
--- /dev/null
+++ b/t/perf/p7102-reset.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+test_description='performance of reset'
+. ./perf-lib.sh
+
+test_perf_default_repo
+test_checkout_worktree
+
+test_perf 'reset --hard with change in tree' '
+ base=$(git rev-parse HEAD) &&
+ test_commit --no-tag A &&
+ new=$(git rev-parse HEAD) &&
+
+ for i in $(test_seq 10)
+ do
+ git reset --hard $new &&
+ git reset --hard $base || return $?
+ done
+'
+
+test_done
diff --git a/t/perf/p7527-builtin-fsmonitor.sh b/t/perf/p7527-builtin-fsmonitor.sh
index 9338b9e..c3f9a4c 100755
--- a/t/perf/p7527-builtin-fsmonitor.sh
+++ b/t/perf/p7527-builtin-fsmonitor.sh
@@ -249,7 +249,7 @@ test_expect_success "Cleanup temp and matrix branches" "
do
for fsm_val in $fsm_values
do
- cleanup $uc_val $fsm_val
+ cleanup $uc_val $fsm_val || return 1
done
done
"
diff --git a/t/perf/p9210-scalar.sh b/t/perf/p9210-scalar.sh
new file mode 100755
index 0000000..265f7cd
--- /dev/null
+++ b/t/perf/p9210-scalar.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+test_description='test scalar performance'
+. ./perf-lib.sh
+
+test_perf_large_repo "$TRASH_DIRECTORY/to-clone"
+
+test_expect_success 'enable server-side partial clone' '
+ git -C to-clone config uploadpack.allowFilter true &&
+ git -C to-clone config uploadpack.allowAnySHA1InWant true
+'
+
+test_perf 'scalar clone' '
+ rm -rf scalar-clone &&
+ scalar clone "file://$(pwd)/to-clone" scalar-clone
+'
+
+test_perf 'git clone' '
+ rm -rf git-clone &&
+ git clone "file://$(pwd)/to-clone" git-clone
+'
+
+test_compare_perf () {
+ command=$1
+ shift
+ args=$*
+ test_perf "$command $args (scalar)" "
+ $command -C scalar-clone/src $args
+ "
+
+ test_perf "$command $args (non-scalar)" "
+ $command -C git-clone $args
+ "
+}
+
+test_compare_perf git status
+test_compare_perf test_commit --append --no-tag A
+
+test_done
diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh
index 27c2801..e778677 100644
--- a/t/perf/perf-lib.sh
+++ b/t/perf/perf-lib.sh
@@ -49,6 +49,9 @@ export TEST_DIRECTORY TRASH_DIRECTORY GIT_BUILD_DIR GIT_TEST_CMP
MODERN_GIT=$GIT_BUILD_DIR/bin-wrappers/git
export MODERN_GIT
+MODERN_SCALAR=$GIT_BUILD_DIR/bin-wrappers/scalar
+export MODERN_SCALAR
+
perf_results_dir=$TEST_RESULTS_DIR
test -n "$GIT_PERF_SUBSECTION" && perf_results_dir="$perf_results_dir/$GIT_PERF_SUBSECTION"
mkdir -p "$perf_results_dir"
@@ -120,6 +123,10 @@ test_perf_create_repo_from () {
# status" due to a locked index. Since we have
# a copy it's fine to remove the lock.
rm .git/index.lock
+ fi &&
+ if test_bool_env GIT_PERF_USE_SCALAR false
+ then
+ "$MODERN_SCALAR" register
fi
) || error "failed to copy repository '$source' to '$repo'"
}
@@ -130,7 +137,11 @@ test_perf_fresh_repo () {
"$MODERN_GIT" init -q "$repo" &&
(
cd "$repo" &&
- test_perf_do_repo_symlink_config_
+ test_perf_do_repo_symlink_config_ &&
+ if test_bool_env GIT_PERF_USE_SCALAR false
+ then
+ "$MODERN_SCALAR" register
+ fi
)
}
diff --git a/t/perf/run b/t/perf/run
index 55219aa..34115ed 100755
--- a/t/perf/run
+++ b/t/perf/run
@@ -171,6 +171,9 @@ run_subsection () {
get_var_from_env_or_config "GIT_PERF_MAKE_COMMAND" "perf" "makeCommand"
get_var_from_env_or_config "GIT_PERF_MAKE_OPTS" "perf" "makeOpts"
+ get_var_from_env_or_config "GIT_PERF_USE_SCALAR" "perf" "useScalar" "--bool"
+ export GIT_PERF_USE_SCALAR
+
get_var_from_env_or_config "GIT_PERF_REPO_NAME" "perf" "repoName"
export GIT_PERF_REPO_NAME
@@ -229,10 +232,10 @@ then
)
elif test -n "$GIT_PERF_SUBSECTION"
then
- egrep "^$GIT_PERF_SUBSECTION\$" "$TEST_RESULTS_DIR"/run_subsections.names >/dev/null ||
+ grep -E "^$GIT_PERF_SUBSECTION\$" "$TEST_RESULTS_DIR"/run_subsections.names >/dev/null ||
die "subsection '$GIT_PERF_SUBSECTION' not found in '$GIT_PERF_CONFIG_FILE'"
- egrep "^$GIT_PERF_SUBSECTION\$" "$TEST_RESULTS_DIR"/run_subsections.names | while read -r subsec
+ grep -E "^$GIT_PERF_SUBSECTION\$" "$TEST_RESULTS_DIR"/run_subsections.names | while read -r subsec
do
(
GIT_PERF_SUBSECTION="$subsec"
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 17a268c..502b4bc 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -578,6 +578,78 @@ test_expect_success 'subtest: --run invalid range end' '
EOF_ERR
'
+test_expect_success 'subtest: --invert-exit-code without --immediate' '
+ run_sub_test_lib_test_err full-pass \
+ --invert-exit-code &&
+ check_sub_test_lib_test_err full-pass \
+ <<-\EOF_OUT 3<<-EOF_ERR
+ ok 1 - passing test #1
+ ok 2 - passing test #2
+ ok 3 - passing test #3
+ # passed all 3 test(s)
+ 1..3
+ # faking up non-zero exit with --invert-exit-code
+ EOF_OUT
+ EOF_ERR
+'
+
+test_expect_success 'subtest: --invert-exit-code with --immediate: all passed' '
+ run_sub_test_lib_test_err full-pass \
+ --invert-exit-code --immediate &&
+ check_sub_test_lib_test_err full-pass \
+ <<-\EOF_OUT 3<<-EOF_ERR
+ ok 1 - passing test #1
+ ok 2 - passing test #2
+ ok 3 - passing test #3
+ # passed all 3 test(s)
+ 1..3
+ # faking up non-zero exit with --invert-exit-code
+ EOF_OUT
+ EOF_ERR
+'
+
+test_expect_success 'subtest: --invert-exit-code without --immediate: partial pass' '
+ run_sub_test_lib_test partial-pass \
+ --invert-exit-code &&
+ check_sub_test_lib_test partial-pass <<-\EOF
+ ok 1 - passing test #1
+ not ok 2 - # TODO induced breakage (--invert-exit-code): failing test #2
+ # false
+ ok 3 - passing test #3
+ # failed 1 among 3 test(s)
+ 1..3
+ # faked up failures as TODO & now exiting with 0 due to --invert-exit-code
+ EOF
+'
+
+test_expect_success 'subtest: --invert-exit-code with --immediate: partial pass' '
+ run_sub_test_lib_test partial-pass \
+ --invert-exit-code --immediate &&
+ check_sub_test_lib_test partial-pass \
+ <<-\EOF_OUT 3<<-EOF_ERR
+ ok 1 - passing test #1
+ not ok 2 - # TODO induced breakage (--invert-exit-code): failing test #2
+ # false
+ 1..2
+ # faked up failures as TODO & now exiting with 0 due to --invert-exit-code
+ EOF_OUT
+ EOF_ERR
+'
+
+test_expect_success 'subtest: --invert-exit-code --immediate: got a failure' '
+ run_sub_test_lib_test partial-pass \
+ --invert-exit-code --immediate &&
+ check_sub_test_lib_test_err partial-pass \
+ <<-\EOF_OUT 3<<-EOF_ERR
+ ok 1 - passing test #1
+ not ok 2 - # TODO induced breakage (--invert-exit-code): failing test #2
+ # false
+ 1..2
+ # faked up failures as TODO & now exiting with 0 due to --invert-exit-code
+ EOF_OUT
+ EOF_ERR
+'
+
test_expect_success 'subtest: tests respect prerequisites' '
write_and_run_sub_test_lib_test prereqs <<-\EOF &&
diff --git a/t/t0002-gitfile.sh b/t/t0002-gitfile.sh
index f6356db..26eaca0 100755
--- a/t/t0002-gitfile.sh
+++ b/t/t0002-gitfile.sh
@@ -65,7 +65,7 @@ test_expect_success 'check commit-tree' '
test_path_is_file "$REAL/objects/$(objpath $SHA)"
'
-test_expect_success !SANITIZE_LEAK 'check rev-list' '
+test_expect_success 'check rev-list' '
git update-ref "HEAD" "$SHA" &&
git rev-list HEAD >actual &&
echo $SHA >expected &&
diff --git a/t/t0004-unwritable.sh b/t/t0004-unwritable.sh
index 2e9d652..8114fac 100755
--- a/t/t0004-unwritable.sh
+++ b/t/t0004-unwritable.sh
@@ -31,7 +31,7 @@ test_expect_success WRITE_TREE_OUT 'write-tree output on unwritable repository'
test_cmp expect out.write-tree
'
-test_expect_success POSIXPERM,SANITY,!SANITIZE_LEAK 'commit should notice unwritable repository' '
+test_expect_success POSIXPERM,SANITY 'commit should notice unwritable repository' '
test_when_finished "chmod 775 .git/objects .git/objects/??" &&
chmod a-w .git/objects .git/objects/?? &&
test_must_fail git commit -m second 2>out.commit
diff --git a/t/t0012-help.sh b/t/t0012-help.sh
index 6c33a43..dbfc5c8 100755
--- a/t/t0012-help.sh
+++ b/t/t0012-help.sh
@@ -44,6 +44,8 @@ test_expect_success 'invalid usage' '
test_expect_code 129 git help -g add &&
test_expect_code 129 git help -a -g &&
+ test_expect_code 129 git help --user-interfaces add &&
+
test_expect_code 129 git help -g -c &&
test_expect_code 129 git help --config-for-completion add &&
test_expect_code 129 git help --config-sections-for-completion add
@@ -104,9 +106,9 @@ test_expect_success 'git help' '
test_i18ngrep "^ commit " help.output &&
test_i18ngrep "^ fetch " help.output
'
+
test_expect_success 'git help -g' '
git help -g >help.output &&
- test_i18ngrep "^ attributes " help.output &&
test_i18ngrep "^ everyday " help.output &&
test_i18ngrep "^ tutorial " help.output
'
@@ -127,6 +129,12 @@ test_expect_success 'git help succeeds without git.html' '
test_cmp expect test-browser.log
'
+test_expect_success 'git help --user-interfaces' '
+ git help --user-interfaces >help.output &&
+ grep "^ attributes " help.output &&
+ grep "^ mailmap " help.output
+'
+
test_expect_success 'git help -c' '
git help -c >help.output &&
cat >expect <<-\EOF &&
@@ -220,6 +228,10 @@ test_expect_success "'git help -a' section spacing" '
Low-level Commands / Syncing Repositories
Low-level Commands / Internal Helpers
+
+ User-facing repository, command and file interfaces
+
+ Developer-facing file formats, protocols and other interfaces
EOF
test_cmp expect actual
'
diff --git a/t/t0013-sha1dc.sh b/t/t0013-sha1dc.sh
index 9ad7608..5324047 100755
--- a/t/t0013-sha1dc.sh
+++ b/t/t0013-sha1dc.sh
@@ -6,9 +6,11 @@ TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
TEST_DATA="$TEST_DIRECTORY/t0013"
-if test -z "$DC_SHA1"
+test_lazy_prereq SHA1_IS_SHA1DC 'test-tool sha1-is-sha1dc'
+
+if ! test_have_prereq SHA1_IS_SHA1DC
then
- skip_all='skipping sha1 collision tests, DC_SHA1 not set'
+ skip_all='skipping sha1 collision tests, not using sha1collisiondetection'
test_done
fi
diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
index 1c84034..abecd75 100755
--- a/t/t0021-conversion.sh
+++ b/t/t0021-conversion.sh
@@ -17,9 +17,6 @@ tr \
'nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'
EOF
-write_script rot13-filter.pl "$PERL_PATH" \
- <"$TEST_DIRECTORY"/t0021/rot13-filter.pl
-
generate_random_characters () {
LEN=$1
NAME=$2
@@ -365,8 +362,8 @@ test_expect_success 'diff does not reuse worktree files that need cleaning' '
test_line_count = 0 count
'
-test_expect_success PERL 'required process filter should filter data' '
- test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
+test_expect_success 'required process filter should filter data' '
+ test_config_global filter.protocol.process "test-tool rot13-filter --log=debug.log clean smudge" &&
test_config_global filter.protocol.required true &&
rm -rf repo &&
mkdir repo &&
@@ -450,8 +447,8 @@ 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_expect_success 'required process filter should filter data for various subcommands' '
+ test_config_global filter.protocol.process "test-tool rot13-filter --log=debug.log clean smudge" &&
test_config_global filter.protocol.required true &&
(
cd repo &&
@@ -561,9 +558,9 @@ test_expect_success PERL 'required process filter should filter data for various
)
'
-test_expect_success PERL 'required process filter takes precedence' '
+test_expect_success 'required process filter takes precedence' '
test_config_global filter.protocol.clean false &&
- test_config_global filter.protocol.process "rot13-filter.pl debug.log clean" &&
+ test_config_global filter.protocol.process "test-tool rot13-filter --log=debug.log clean" &&
test_config_global filter.protocol.required true &&
rm -rf repo &&
mkdir repo &&
@@ -587,8 +584,8 @@ test_expect_success PERL 'required process filter takes precedence' '
)
'
-test_expect_success PERL 'required process filter should be used only for "clean" operation only' '
- test_config_global filter.protocol.process "rot13-filter.pl debug.log clean" &&
+test_expect_success 'required process filter should be used only for "clean" operation only' '
+ test_config_global filter.protocol.process "test-tool rot13-filter --log=debug.log clean" &&
rm -rf repo &&
mkdir repo &&
(
@@ -622,8 +619,8 @@ test_expect_success PERL 'required process filter should be used only for "clean
)
'
-test_expect_success PERL 'required process filter should process multiple packets' '
- test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
+test_expect_success 'required process filter should process multiple packets' '
+ test_config_global filter.protocol.process "test-tool rot13-filter --log=debug.log clean smudge" &&
test_config_global filter.protocol.required true &&
rm -rf repo &&
@@ -687,8 +684,8 @@ test_expect_success PERL 'required process filter should process multiple packet
)
'
-test_expect_success PERL 'required process filter with clean error should fail' '
- test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
+test_expect_success 'required process filter with clean error should fail' '
+ test_config_global filter.protocol.process "test-tool rot13-filter --log=debug.log clean smudge" &&
test_config_global filter.protocol.required true &&
rm -rf repo &&
mkdir repo &&
@@ -706,8 +703,8 @@ test_expect_success PERL 'required process filter with clean error should fail'
)
'
-test_expect_success PERL 'process filter should restart after unexpected write failure' '
- test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
+test_expect_success 'process filter should restart after unexpected write failure' '
+ test_config_global filter.protocol.process "test-tool rot13-filter --log=debug.log clean smudge" &&
rm -rf repo &&
mkdir repo &&
(
@@ -735,7 +732,7 @@ test_expect_success PERL 'process filter should restart after unexpected write f
rm -f debug.log &&
git checkout --quiet --no-progress . 2>git-stderr.log &&
- grep "smudge write error at" git-stderr.log &&
+ grep "smudge write error" git-stderr.log &&
test_i18ngrep "error: external filter" git-stderr.log &&
cat >expected.log <<-EOF &&
@@ -761,8 +758,8 @@ test_expect_success PERL 'process filter should restart after unexpected write f
)
'
-test_expect_success PERL 'process filter should not be restarted if it signals an error' '
- test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
+test_expect_success 'process filter should not be restarted if it signals an error' '
+ test_config_global filter.protocol.process "test-tool rot13-filter --log=debug.log clean smudge" &&
rm -rf repo &&
mkdir repo &&
(
@@ -804,8 +801,8 @@ test_expect_success PERL 'process filter should not be restarted if it signals a
)
'
-test_expect_success PERL 'process filter abort stops processing of all further files' '
- test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
+test_expect_success 'process filter abort stops processing of all further files' '
+ test_config_global filter.protocol.process "test-tool rot13-filter --log=debug.log clean smudge" &&
rm -rf repo &&
mkdir repo &&
(
@@ -861,10 +858,10 @@ test_expect_success PERL 'invalid process filter must fail (and not hang!)' '
)
'
-test_expect_success PERL 'delayed checkout in process filter' '
- test_config_global filter.a.process "rot13-filter.pl a.log clean smudge delay" &&
+test_expect_success 'delayed checkout in process filter' '
+ test_config_global filter.a.process "test-tool rot13-filter --log=a.log clean smudge delay" &&
test_config_global filter.a.required true &&
- test_config_global filter.b.process "rot13-filter.pl b.log clean smudge delay" &&
+ test_config_global filter.b.process "test-tool rot13-filter --log=b.log clean smudge delay" &&
test_config_global filter.b.required true &&
rm -rf repo &&
@@ -940,8 +937,8 @@ test_expect_success PERL 'delayed checkout in process filter' '
)
'
-test_expect_success PERL 'missing file in delayed checkout' '
- test_config_global filter.bug.process "rot13-filter.pl bug.log clean smudge delay" &&
+test_expect_success 'missing file in delayed checkout' '
+ test_config_global filter.bug.process "test-tool rot13-filter --log=bug.log clean smudge delay" &&
test_config_global filter.bug.required true &&
rm -rf repo &&
@@ -960,8 +957,8 @@ test_expect_success PERL 'missing file in delayed checkout' '
grep "error: .missing-delay\.a. was not filtered properly" git-stderr.log
'
-test_expect_success PERL 'invalid file in delayed checkout' '
- test_config_global filter.bug.process "rot13-filter.pl bug.log clean smudge delay" &&
+test_expect_success 'invalid file in delayed checkout' '
+ test_config_global filter.bug.process "test-tool rot13-filter --log=bug.log clean smudge delay" &&
test_config_global filter.bug.required true &&
rm -rf repo &&
@@ -990,10 +987,10 @@ do
mode_prereq='UTF8_NFD_TO_NFC' ;;
esac
- test_expect_success PERL,SYMLINKS,$mode_prereq \
+ test_expect_success SYMLINKS,$mode_prereq \
"delayed checkout with $mode-collision don't write to the wrong place" '
test_config_global filter.delay.process \
- "\"$TEST_ROOT/rot13-filter.pl\" --always-delay delayed.log clean smudge delay" &&
+ "test-tool rot13-filter --always-delay --log=delayed.log clean smudge delay" &&
test_config_global filter.delay.required true &&
git init $mode-collision &&
@@ -1026,12 +1023,12 @@ do
'
done
-test_expect_success PERL,SYMLINKS,CASE_INSENSITIVE_FS \
+test_expect_success SYMLINKS,CASE_INSENSITIVE_FS \
"delayed checkout with submodule collision don't write to the wrong place" '
git init collision-with-submodule &&
(
cd collision-with-submodule &&
- git config filter.delay.process "\"$TEST_ROOT/rot13-filter.pl\" --always-delay delayed.log clean smudge delay" &&
+ git config filter.delay.process "test-tool rot13-filter --always-delay --log=delayed.log clean smudge delay" &&
git config filter.delay.required true &&
# We need Git to treat the submodule "a" and the
@@ -1062,11 +1059,11 @@ test_expect_success PERL,SYMLINKS,CASE_INSENSITIVE_FS \
)
'
-test_expect_success PERL 'setup for progress tests' '
+test_expect_success 'setup for progress tests' '
git init progress &&
(
cd progress &&
- git config filter.delay.process "rot13-filter.pl delay-progress.log clean smudge delay" &&
+ git config filter.delay.process "test-tool rot13-filter --log=delay-progress.log clean smudge delay" &&
git config filter.delay.required true &&
echo "*.a filter=delay" >.gitattributes &&
@@ -1132,12 +1129,12 @@ do
'
done
-test_expect_success PERL 'delayed checkout correctly reports the number of updated entries' '
+test_expect_success 'delayed checkout correctly reports the number of updated entries' '
rm -rf repo &&
git init repo &&
(
cd repo &&
- git config filter.delay.process "../rot13-filter.pl delayed.log clean smudge delay" &&
+ git config filter.delay.process "test-tool rot13-filter --log=delayed.log clean smudge delay" &&
git config filter.delay.required true &&
echo "*.a filter=delay" >.gitattributes &&
diff --git a/t/t0021/rot13-filter.pl b/t/t0021/rot13-filter.pl
deleted file mode 100644
index 7bb9376..0000000
--- a/t/t0021/rot13-filter.pl
+++ /dev/null
@@ -1,247 +0,0 @@
-#
-# Example implementation for the Git filter protocol version 2
-# See Documentation/gitattributes.txt, section "Filter Protocol"
-#
-# Usage: rot13-filter.pl [--always-delay] <log path> <capabilities>
-#
-# Log path defines a debug log file that the script writes to. The
-# subsequent arguments define a list of supported protocol capabilities
-# ("clean", "smudge", etc).
-#
-# When --always-delay is given all pathnames with the "can-delay" flag
-# that don't appear on the list bellow are delayed with a count of 1
-# (see more below).
-#
-# This implementation supports special test cases:
-# (1) If data with the pathname "clean-write-fail.r" is processed with
-# a "clean" operation then the write operation will die.
-# (2) If data with the pathname "smudge-write-fail.r" is processed with
-# a "smudge" operation then the write operation will die.
-# (3) If data with the pathname "error.r" is processed with any
-# operation then the filter signals that it cannot or does not want
-# to process the file.
-# (4) If data with the pathname "abort.r" is processed with any
-# operation then the filter signals that it cannot or does not want
-# to process the file and any file after that is processed with the
-# same command.
-# (5) If data with a pathname that is a key in the DELAY hash is
-# requested (e.g. "test-delay10.a") then the filter responds with
-# a "delay" status and sets the "requested" field in the DELAY hash.
-# The filter will signal the availability of this object after
-# "count" (field in DELAY hash) "list_available_blobs" commands.
-# (6) If data with the pathname "missing-delay.a" is processed that the
-# filter will drop the path from the "list_available_blobs" response.
-# (7) If data with the pathname "invalid-delay.a" is processed that the
-# filter will add the path "unfiltered" which was not delayed before
-# to the "list_available_blobs" response.
-#
-
-use 5.008;
-sub gitperllib {
- # Git assumes that all path lists are Unix-y colon-separated ones. But
- # when the Git for Windows executes the test suite, its MSYS2 Bash
- # calls git.exe, and colon-separated path lists are converted into
- # Windows-y semicolon-separated lists of *Windows* paths (which
- # naturally contain a colon after the drive letter, so splitting by
- # colons simply does not cut it).
- #
- # Detect semicolon-separated path list and handle them appropriately.
-
- if ($ENV{GITPERLLIB} =~ /;/) {
- return split(/;/, $ENV{GITPERLLIB});
- }
- return split(/:/, $ENV{GITPERLLIB});
-}
-use lib (gitperllib());
-use strict;
-use warnings;
-use IO::File;
-use Git::Packet;
-
-my $MAX_PACKET_CONTENT_SIZE = 65516;
-
-my $always_delay = 0;
-if ( $ARGV[0] eq '--always-delay' ) {
- $always_delay = 1;
- shift @ARGV;
-}
-
-my $log_file = shift @ARGV;
-my @capabilities = @ARGV;
-
-open my $debug, ">>", $log_file or die "cannot open log file: $!";
-
-my %DELAY = (
- 'test-delay10.a' => { "requested" => 0, "count" => 1 },
- 'test-delay11.a' => { "requested" => 0, "count" => 1 },
- 'test-delay20.a' => { "requested" => 0, "count" => 2 },
- 'test-delay10.b' => { "requested" => 0, "count" => 1 },
- 'missing-delay.a' => { "requested" => 0, "count" => 1 },
- 'invalid-delay.a' => { "requested" => 0, "count" => 1 },
-);
-
-sub rot13 {
- my $str = shift;
- $str =~ y/A-Za-z/N-ZA-Mn-za-m/;
- return $str;
-}
-
-print $debug "START\n";
-$debug->flush();
-
-packet_initialize("git-filter", 2);
-
-my %remote_caps = packet_read_and_check_capabilities("clean", "smudge", "delay");
-packet_check_and_write_capabilities(\%remote_caps, @capabilities);
-
-print $debug "init handshake complete\n";
-$debug->flush();
-
-while (1) {
- my ( $res, $command ) = packet_key_val_read("command");
- if ( $res == -1 ) {
- print $debug "STOP\n";
- exit();
- }
- print $debug "IN: $command";
- $debug->flush();
-
- if ( $command eq "list_available_blobs" ) {
- # Flush
- packet_compare_lists([1, ""], packet_bin_read()) ||
- die "bad list_available_blobs end";
-
- foreach my $pathname ( sort keys %DELAY ) {
- if ( $DELAY{$pathname}{"requested"} >= 1 ) {
- $DELAY{$pathname}{"count"} = $DELAY{$pathname}{"count"} - 1;
- if ( $pathname eq "invalid-delay.a" ) {
- # Send Git a pathname that was not delayed earlier
- packet_txt_write("pathname=unfiltered");
- }
- if ( $pathname eq "missing-delay.a" ) {
- # Do not signal Git that this file is available
- } elsif ( $DELAY{$pathname}{"count"} == 0 ) {
- print $debug " $pathname";
- packet_txt_write("pathname=$pathname");
- }
- }
- }
-
- packet_flush();
-
- print $debug " [OK]\n";
- $debug->flush();
- packet_txt_write("status=success");
- packet_flush();
- } else {
- my ( $res, $pathname ) = packet_key_val_read("pathname");
- if ( $res == -1 ) {
- die "unexpected EOF while expecting pathname";
- }
- print $debug " $pathname";
- $debug->flush();
-
- # Read until flush
- my ( $done, $buffer ) = packet_txt_read();
- while ( $buffer ne '' ) {
- if ( $buffer eq "can-delay=1" ) {
- if ( exists $DELAY{$pathname} and $DELAY{$pathname}{"requested"} == 0 ) {
- $DELAY{$pathname}{"requested"} = 1;
- } elsif ( !exists $DELAY{$pathname} and $always_delay ) {
- $DELAY{$pathname} = { "requested" => 1, "count" => 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'";
- }
-
- ( $done, $buffer ) = packet_txt_read();
- }
- if ( $done == -1 ) {
- die "unexpected EOF after pathname '$pathname'";
- }
-
- my $input = "";
- {
- binmode(STDIN);
- my $buffer;
- my $done = 0;
- while ( !$done ) {
- ( $done, $buffer ) = packet_bin_read();
- $input .= $buffer;
- }
- if ( $done == -1 ) {
- die "unexpected EOF while reading input for '$pathname'";
- }
- print $debug " " . length($input) . " [OK] -- ";
- $debug->flush();
- }
-
- my $output;
- if ( exists $DELAY{$pathname} and exists $DELAY{$pathname}{"output"} ) {
- $output = $DELAY{$pathname}{"output"}
- } elsif ( $pathname eq "error.r" or $pathname eq "abort.r" ) {
- $output = "";
- } elsif ( $command eq "clean" and grep( /^clean$/, @capabilities ) ) {
- $output = rot13($input);
- } elsif ( $command eq "smudge" and grep( /^smudge$/, @capabilities ) ) {
- $output = rot13($input);
- } else {
- die "bad command '$command'";
- }
-
- if ( $pathname eq "error.r" ) {
- print $debug "[ERROR]\n";
- $debug->flush();
- packet_txt_write("status=error");
- packet_flush();
- } elsif ( $pathname eq "abort.r" ) {
- print $debug "[ABORT]\n";
- $debug->flush();
- packet_txt_write("status=abort");
- packet_flush();
- } elsif ( $command eq "smudge" and
- exists $DELAY{$pathname} and
- $DELAY{$pathname}{"requested"} == 1 ) {
- print $debug "[DELAYED]\n";
- $debug->flush();
- packet_txt_write("status=delayed");
- packet_flush();
- $DELAY{$pathname}{"requested"} = 2;
- $DELAY{$pathname}{"output"} = $output;
- } else {
- packet_txt_write("status=success");
- packet_flush();
-
- if ( $pathname eq "${command}-write-fail.r" ) {
- print $debug "[WRITE FAIL]\n";
- $debug->flush();
- die "${command} write error";
- }
-
- print $debug "OUT: " . length($output) . " ";
- $debug->flush();
-
- while ( length($output) > 0 ) {
- my $packet = substr( $output, 0, $MAX_PACKET_CONTENT_SIZE );
- packet_bin_write($packet);
- # dots represent the number of packets
- print $debug ".";
- if ( length($output) > $MAX_PACKET_CONTENT_SIZE ) {
- $output = substr( $output, $MAX_PACKET_CONTENT_SIZE );
- } else {
- $output = "";
- }
- }
- packet_flush();
- print $debug " [OK]\n";
- $debug->flush();
- packet_flush();
- }
- }
-}
diff --git a/t/t0027-auto-crlf.sh b/t/t0027-auto-crlf.sh
index 7f80f46..a94ac1e 100755
--- a/t/t0027-auto-crlf.sh
+++ b/t/t0027-auto-crlf.sh
@@ -2,6 +2,7 @@
test_description='CRLF conversion all combinations'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
compare_files () {
@@ -386,9 +387,7 @@ test_expect_success 'setup main' '
test_tick
'
-# Disable extra chain-linting for the next set of tests. There are many
-# auto-generated ones that are not worth checking over and over.
-GIT_TEST_CHAIN_LINT_HARDER_DEFAULT=0
+
warn_LF_CRLF="LF will be replaced by CRLF"
warn_CRLF_LF="CRLF will be replaced by LF"
@@ -605,9 +604,6 @@ do
checkout_files "" "$id" "crlf" true "" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
done
-# The rest of the tests are unique; do the usual linting.
-unset GIT_TEST_CHAIN_LINT_HARDER_DEFAULT
-
# Should be the last test case: remove some files from the worktree
test_expect_success 'ls-files --eol -d -z' '
rm crlf_false_attr__CRLF.txt crlf_false_attr__CRLF_mix_LF.txt crlf_false_attr__LF.txt .gitattributes &&
diff --git a/t/t0032-reftable-unittest.sh b/t/t0032-reftable-unittest.sh
index 0ed1497..471cb37 100755
--- a/t/t0032-reftable-unittest.sh
+++ b/t/t0032-reftable-unittest.sh
@@ -5,6 +5,7 @@
test_description='reftable unittests'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'unittests' '
diff --git a/t/t0033-safe-directory.sh b/t/t0033-safe-directory.sh
index f4d737d..dc34968 100755
--- a/t/t0033-safe-directory.sh
+++ b/t/t0033-safe-directory.sh
@@ -2,6 +2,7 @@
test_description='verify safe.directory checks'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
GIT_TEST_ASSUME_DIFFERENT_OWNER=1
@@ -70,4 +71,13 @@ test_expect_success 'safe.directory=*, but is reset' '
expect_rejected_dir
'
+test_expect_success 'safe.directory in included file' '
+ cat >gitconfig-include <<-EOF &&
+ [safe]
+ directory = "$(pwd)"
+ EOF
+ git config --global --add include.path "$(pwd)/gitconfig-include" &&
+ git status
+'
+
test_done
diff --git a/t/t0035-safe-bare-repository.sh b/t/t0035-safe-bare-repository.sh
index ecbdc82..11c15a4 100755
--- a/t/t0035-safe-bare-repository.sh
+++ b/t/t0035-safe-bare-repository.sh
@@ -51,4 +51,13 @@ test_expect_success 'safe.bareRepository on the command line' '
-c safe.bareRepository=all
'
+test_expect_success 'safe.bareRepository in included file' '
+ cat >gitconfig-include <<-\EOF &&
+ [safe]
+ bareRepository = explicit
+ EOF
+ git config --global --add include.path "$(pwd)/gitconfig-include" &&
+ expect_rejected -C outer-repo/bare-repo
+'
+
test_done
diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
index ed2fb62..7d7ecfd 100755
--- a/t/t0040-parse-options.sh
+++ b/t/t0040-parse-options.sh
@@ -456,4 +456,269 @@ test_expect_success '--end-of-options treats remainder as args' '
--end-of-options --verbose
'
+test_expect_success 'KEEP_DASHDASH works' '
+ test-tool parse-options-flags --keep-dashdash cmd --opt=1 -- --opt=2 --unknown >actual &&
+ cat >expect <<-\EOF &&
+ opt: 1
+ arg 00: --
+ arg 01: --opt=2
+ arg 02: --unknown
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'KEEP_ARGV0 works' '
+ test-tool parse-options-flags --keep-argv0 cmd arg0 --opt=3 >actual &&
+ cat >expect <<-\EOF &&
+ opt: 3
+ arg 00: cmd
+ arg 01: arg0
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'STOP_AT_NON_OPTION works' '
+ test-tool parse-options-flags --stop-at-non-option cmd --opt=4 arg0 --opt=5 --unknown >actual &&
+ cat >expect <<-\EOF &&
+ opt: 4
+ arg 00: arg0
+ arg 01: --opt=5
+ arg 02: --unknown
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'KEEP_UNKNOWN_OPT works' '
+ test-tool parse-options-flags --keep-unknown-opt cmd --unknown=1 --opt=6 -u2 >actual &&
+ cat >expect <<-\EOF &&
+ opt: 6
+ arg 00: --unknown=1
+ arg 01: -u2
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'NO_INTERNAL_HELP works for -h' '
+ test_expect_code 129 test-tool parse-options-flags --no-internal-help cmd -h 2>err &&
+ grep "^error: unknown switch \`h$SQ" err &&
+ grep "^usage: " err
+'
+
+for help_opt in help help-all
+do
+ test_expect_success "NO_INTERNAL_HELP works for --$help_opt" "
+ test_expect_code 129 test-tool parse-options-flags --no-internal-help cmd --$help_opt 2>err &&
+ grep '^error: unknown option \`'$help_opt\' err &&
+ grep '^usage: ' err
+ "
+done
+
+test_expect_success 'KEEP_UNKNOWN_OPT | NO_INTERNAL_HELP works' '
+ test-tool parse-options-flags --keep-unknown-opt --no-internal-help cmd -h --help --help-all >actual &&
+ cat >expect <<-\EOF &&
+ opt: 0
+ arg 00: -h
+ arg 01: --help
+ arg 02: --help-all
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'subcommand - no subcommand shows error and usage' '
+ test_expect_code 129 test-tool parse-subcommand cmd 2>err &&
+ grep "^error: need a subcommand" err &&
+ grep ^usage: err
+'
+
+test_expect_success 'subcommand - subcommand after -- shows error and usage' '
+ test_expect_code 129 test-tool parse-subcommand cmd -- subcmd-one 2>err &&
+ grep "^error: need a subcommand" err &&
+ grep ^usage: err
+'
+
+test_expect_success 'subcommand - subcommand after --end-of-options shows error and usage' '
+ test_expect_code 129 test-tool parse-subcommand cmd --end-of-options subcmd-one 2>err &&
+ grep "^error: need a subcommand" err &&
+ grep ^usage: err
+'
+
+test_expect_success 'subcommand - unknown subcommand shows error and usage' '
+ test_expect_code 129 test-tool parse-subcommand cmd nope 2>err &&
+ grep "^error: unknown subcommand: \`nope$SQ" err &&
+ grep ^usage: err
+'
+
+test_expect_success 'subcommand - subcommands cannot be abbreviated' '
+ test_expect_code 129 test-tool parse-subcommand cmd subcmd-o 2>err &&
+ grep "^error: unknown subcommand: \`subcmd-o$SQ$" err &&
+ grep ^usage: err
+'
+
+test_expect_success 'subcommand - no negated subcommands' '
+ test_expect_code 129 test-tool parse-subcommand cmd no-subcmd-one 2>err &&
+ grep "^error: unknown subcommand: \`no-subcmd-one$SQ" err &&
+ grep ^usage: err
+'
+
+test_expect_success 'subcommand - simple' '
+ test-tool parse-subcommand cmd subcmd-two >actual &&
+ cat >expect <<-\EOF &&
+ opt: 0
+ fn: subcmd_two
+ arg 00: subcmd-two
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'subcommand - stop parsing at the first subcommand' '
+ test-tool parse-subcommand cmd --opt=1 subcmd-two subcmd-one --opt=2 >actual &&
+ cat >expect <<-\EOF &&
+ opt: 1
+ fn: subcmd_two
+ arg 00: subcmd-two
+ arg 01: subcmd-one
+ arg 02: --opt=2
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'subcommand - KEEP_ARGV0' '
+ test-tool parse-subcommand --keep-argv0 cmd subcmd-two >actual &&
+ cat >expect <<-\EOF &&
+ opt: 0
+ fn: subcmd_two
+ arg 00: cmd
+ arg 01: subcmd-two
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'subcommand - SUBCOMMAND_OPTIONAL + subcommand not given' '
+ test-tool parse-subcommand --subcommand-optional cmd >actual &&
+ cat >expect <<-\EOF &&
+ opt: 0
+ fn: subcmd_one
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'subcommand - SUBCOMMAND_OPTIONAL + given subcommand' '
+ test-tool parse-subcommand --subcommand-optional cmd subcmd-two branch file >actual &&
+ cat >expect <<-\EOF &&
+ opt: 0
+ fn: subcmd_two
+ arg 00: subcmd-two
+ arg 01: branch
+ arg 02: file
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'subcommand - SUBCOMMAND_OPTIONAL + subcommand not given + unknown dashless args' '
+ test-tool parse-subcommand --subcommand-optional cmd branch file >actual &&
+ cat >expect <<-\EOF &&
+ opt: 0
+ fn: subcmd_one
+ arg 00: branch
+ arg 01: file
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'subcommand - SUBCOMMAND_OPTIONAL + subcommand not given + unknown option' '
+ test_expect_code 129 test-tool parse-subcommand --subcommand-optional cmd --subcommand-opt 2>err &&
+ grep "^error: unknown option" err &&
+ grep ^usage: err
+'
+
+test_expect_success 'subcommand - SUBCOMMAND_OPTIONAL | KEEP_UNKNOWN_OPT + subcommand not given + unknown option' '
+ test-tool parse-subcommand --subcommand-optional --keep-unknown-opt cmd --subcommand-opt >actual &&
+ cat >expect <<-\EOF &&
+ opt: 0
+ fn: subcmd_one
+ arg 00: --subcommand-opt
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'subcommand - SUBCOMMAND_OPTIONAL | KEEP_UNKNOWN_OPT + subcommand ignored after unknown option' '
+ test-tool parse-subcommand --subcommand-optional --keep-unknown-opt cmd --subcommand-opt subcmd-two >actual &&
+ cat >expect <<-\EOF &&
+ opt: 0
+ fn: subcmd_one
+ arg 00: --subcommand-opt
+ arg 01: subcmd-two
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'subcommand - SUBCOMMAND_OPTIONAL | KEEP_UNKNOWN_OPT + command and subcommand options cannot be mixed' '
+ test-tool parse-subcommand --subcommand-optional --keep-unknown-opt cmd --subcommand-opt branch --opt=1 >actual &&
+ cat >expect <<-\EOF &&
+ opt: 0
+ fn: subcmd_one
+ arg 00: --subcommand-opt
+ arg 01: branch
+ arg 02: --opt=1
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'subcommand - SUBCOMMAND_OPTIONAL | KEEP_UNKNOWN_OPT | KEEP_ARGV0' '
+ test-tool parse-subcommand --subcommand-optional --keep-unknown-opt --keep-argv0 cmd --subcommand-opt branch >actual &&
+ cat >expect <<-\EOF &&
+ opt: 0
+ fn: subcmd_one
+ arg 00: cmd
+ arg 01: --subcommand-opt
+ arg 02: branch
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'subcommand - SUBCOMMAND_OPTIONAL | KEEP_UNKNOWN_OPT | KEEP_DASHDASH' '
+ test-tool parse-subcommand --subcommand-optional --keep-unknown-opt --keep-dashdash cmd -- --subcommand-opt file >actual &&
+ cat >expect <<-\EOF &&
+ opt: 0
+ fn: subcmd_one
+ arg 00: --
+ arg 01: --subcommand-opt
+ arg 02: file
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'subcommand - completion helper' '
+ test-tool parse-subcommand cmd --git-completion-helper >actual &&
+ echo "subcmd-one subcmd-two --opt= --no-opt" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'subcommands are incompatible with STOP_AT_NON_OPTION' '
+ test_must_fail test-tool parse-subcommand --stop-at-non-option cmd subcmd-one 2>err &&
+ grep ^BUG err
+'
+
+test_expect_success 'subcommands are incompatible with KEEP_UNKNOWN_OPT unless in combination with SUBCOMMAND_OPTIONAL' '
+ test_must_fail test-tool parse-subcommand --keep-unknown-opt cmd subcmd-two 2>err &&
+ grep ^BUG err
+'
+
+test_expect_success 'subcommands are incompatible with KEEP_DASHDASH unless in combination with SUBCOMMAND_OPTIONAL' '
+ test_must_fail test-tool parse-subcommand --keep-dashdash cmd subcmd-two 2>err &&
+ grep ^BUG err
+'
+
+test_expect_success 'negative magnitude' '
+ test_must_fail test-tool parse-options --magnitude -1 >out 2>err &&
+ grep "non-negative integer" err &&
+ test_must_be_empty out
+'
+
+test_expect_success 'magnitude with units but no numbers' '
+ test_must_fail test-tool parse-options --magnitude m >out 2>err &&
+ grep "non-negative integer" err &&
+ test_must_be_empty out
+'
+
test_done
diff --git a/t/t0050-filesystem.sh b/t/t0050-filesystem.sh
index 5c9dc90..325eb1c 100755
--- a/t/t0050-filesystem.sh
+++ b/t/t0050-filesystem.sh
@@ -5,6 +5,7 @@ test_description='Various filesystem issues'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
auml=$(printf '\303\244')
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 1f2007e..68e29c9 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -22,7 +22,7 @@ relative_path() {
test_submodule_relative_url() {
test_expect_success "test_submodule_relative_url: $1 $2 $3 => $4" "
- actual=\$(git submodule--helper resolve-relative-url-test '$1' '$2' '$3') &&
+ actual=\$(test-tool submodule resolve-relative-url '$1' '$2' '$3') &&
test \"\$actual\" = '$4'
"
}
diff --git a/t/t0061-run-command.sh b/t/t0061-run-command.sh
index 7b5423e..e2411f6 100755
--- a/t/t0061-run-command.sh
+++ b/t/t0061-run-command.sh
@@ -130,7 +130,8 @@ World
EOF
test_expect_success 'run_command runs in parallel with more jobs available than tasks' '
- test-tool run-command run-command-parallel 5 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual &&
+ test-tool run-command run-command-parallel 5 sh -c "printf \"%s\n%s\n\" Hello World" >out 2>actual &&
+ test_must_be_empty out &&
test_cmp expect actual
'
@@ -141,7 +142,8 @@ test_expect_success 'run_command runs ungrouped in parallel with more jobs avail
'
test_expect_success 'run_command runs in parallel with as many jobs as tasks' '
- test-tool run-command run-command-parallel 4 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual &&
+ test-tool run-command run-command-parallel 4 sh -c "printf \"%s\n%s\n\" Hello World" >out 2>actual &&
+ test_must_be_empty out &&
test_cmp expect actual
'
@@ -152,7 +154,8 @@ test_expect_success 'run_command runs ungrouped in parallel with as many jobs as
'
test_expect_success 'run_command runs in parallel with more tasks than jobs available' '
- test-tool run-command run-command-parallel 3 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual &&
+ test-tool run-command run-command-parallel 3 sh -c "printf \"%s\n%s\n\" Hello World" >out 2>actual &&
+ test_must_be_empty out &&
test_cmp expect actual
'
@@ -172,7 +175,8 @@ asking for a quick stop
EOF
test_expect_success 'run_command is asked to abort gracefully' '
- test-tool run-command run-command-abort 3 false 2>actual &&
+ test-tool run-command run-command-abort 3 false >out 2>actual &&
+ test_must_be_empty out &&
test_cmp expect actual
'
@@ -187,7 +191,8 @@ no further jobs available
EOF
test_expect_success 'run_command outputs ' '
- test-tool run-command run-command-no-jobs 3 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual &&
+ test-tool run-command run-command-no-jobs 3 sh -c "printf \"%s\n%s\n\" Hello World" >out 2>actual &&
+ test_must_be_empty out &&
test_cmp expect actual
'
diff --git a/t/t0068-for-each-repo.sh b/t/t0068-for-each-repo.sh
index 4675e85..c6e0d65 100755
--- a/t/t0068-for-each-repo.sh
+++ b/t/t0068-for-each-repo.sh
@@ -8,9 +8,11 @@ test_expect_success 'run based on configured value' '
git init one &&
git init two &&
git init three &&
+ git init ~/four &&
git -C two commit --allow-empty -m "DID NOT RUN" &&
git config run.key "$TRASH_DIRECTORY/one" &&
git config --add run.key "$TRASH_DIRECTORY/three" &&
+ git config --add run.key "~/four" &&
git for-each-repo --config=run.key commit --allow-empty -m "ran" &&
git -C one log -1 --pretty=format:%s >message &&
grep ran message &&
@@ -18,12 +20,16 @@ test_expect_success 'run based on configured value' '
! grep ran message &&
git -C three log -1 --pretty=format:%s >message &&
grep ran message &&
+ git -C ~/four log -1 --pretty=format:%s >message &&
+ grep ran message &&
git for-each-repo --config=run.key -- commit --allow-empty -m "ran again" &&
git -C one log -1 --pretty=format:%s >message &&
grep again message &&
git -C two log -1 --pretty=format:%s >message &&
! grep again message &&
git -C three log -1 --pretty=format:%s >message &&
+ grep again message &&
+ git -C ~/four log -1 --pretty=format:%s >message &&
grep again message
'
diff --git a/t/t0091-bugreport.sh b/t/t0091-bugreport.sh
index 08f5fe9..b6d2f59 100755
--- a/t/t0091-bugreport.sh
+++ b/t/t0091-bugreport.sh
@@ -78,4 +78,52 @@ test_expect_success 'indicates populated hooks' '
test_cmp expect actual
'
+test_expect_success UNZIP '--diagnose creates diagnostics zip archive' '
+ test_when_finished rm -rf report &&
+
+ git bugreport --diagnose -o report -s test >out &&
+
+ zip_path=report/git-diagnostics-test.zip &&
+ grep "Available space" out &&
+ test_path_is_file "$zip_path" &&
+
+ # Check zipped archive content
+ "$GIT_UNZIP" -p "$zip_path" diagnostics.log >out &&
+ test_file_not_empty out &&
+
+ "$GIT_UNZIP" -p "$zip_path" packs-local.txt >out &&
+ grep ".git/objects" out &&
+
+ "$GIT_UNZIP" -p "$zip_path" objects-local.txt >out &&
+ grep "^Total: [0-9][0-9]*" out &&
+
+ # Should not include .git directory contents by default
+ ! "$GIT_UNZIP" -l "$zip_path" | grep ".git/"
+'
+
+test_expect_success UNZIP '--diagnose=stats excludes .git dir contents' '
+ test_when_finished rm -rf report &&
+
+ git bugreport --diagnose=stats -o report -s test >out &&
+
+ # Includes pack quantity/size info
+ "$GIT_UNZIP" -p "$zip_path" packs-local.txt >out &&
+ grep ".git/objects" out &&
+
+ # Does not include .git directory contents
+ ! "$GIT_UNZIP" -l "$zip_path" | grep ".git/"
+'
+
+test_expect_success UNZIP '--diagnose=all includes .git dir contents' '
+ test_when_finished rm -rf report &&
+
+ git bugreport --diagnose=all -o report -s test >out &&
+
+ # Includes .git directory contents
+ "$GIT_UNZIP" -l "$zip_path" | grep ".git/" &&
+
+ "$GIT_UNZIP" -p "$zip_path" .git/HEAD >out &&
+ test_file_not_empty out
+'
+
test_done
diff --git a/t/t0092-diagnose.sh b/t/t0092-diagnose.sh
new file mode 100755
index 0000000..133e574
--- /dev/null
+++ b/t/t0092-diagnose.sh
@@ -0,0 +1,72 @@
+#!/bin/sh
+
+test_description='git diagnose'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success UNZIP 'creates diagnostics zip archive' '
+ test_when_finished rm -rf report &&
+
+ git diagnose -o report -s test >out &&
+ grep "Available space" out &&
+
+ zip_path=report/git-diagnostics-test.zip &&
+ test_path_is_file "$zip_path" &&
+
+ # Check zipped archive content
+ "$GIT_UNZIP" -p "$zip_path" diagnostics.log >out &&
+ test_file_not_empty out &&
+
+ "$GIT_UNZIP" -p "$zip_path" packs-local.txt >out &&
+ grep ".git/objects" out &&
+
+ "$GIT_UNZIP" -p "$zip_path" objects-local.txt >out &&
+ grep "^Total: [0-9][0-9]*" out &&
+
+ # Should not include .git directory contents by default
+ ! "$GIT_UNZIP" -l "$zip_path" | grep ".git/"
+'
+
+test_expect_success UNZIP 'counts loose objects' '
+ test_commit A &&
+
+ # After committing, should have non-zero loose objects
+ git diagnose -o test-count -s 1 >out &&
+ zip_path=test-count/git-diagnostics-1.zip &&
+ "$GIT_UNZIP" -p "$zip_path" objects-local.txt >out &&
+ grep "^Total: [1-9][0-9]* loose objects" out
+'
+
+test_expect_success UNZIP '--mode=stats excludes .git dir contents' '
+ test_when_finished rm -rf report &&
+
+ git diagnose -o report -s test --mode=stats >out &&
+
+ # Includes pack quantity/size info
+ zip_path=report/git-diagnostics-test.zip &&
+ "$GIT_UNZIP" -p "$zip_path" packs-local.txt >out &&
+ grep ".git/objects" out &&
+
+ # Does not include .git directory contents
+ ! "$GIT_UNZIP" -l "$zip_path" | grep ".git/"
+'
+
+test_expect_success UNZIP '--mode=all includes .git dir contents' '
+ test_when_finished rm -rf report &&
+
+ git diagnose -o report -s test --mode=all >out &&
+
+ # Includes pack quantity/size info
+ zip_path=report/git-diagnostics-test.zip &&
+ "$GIT_UNZIP" -p "$zip_path" packs-local.txt >out &&
+ grep ".git/objects" out &&
+
+ # Includes .git directory contents
+ "$GIT_UNZIP" -l "$zip_path" | grep ".git/" &&
+
+ "$GIT_UNZIP" -p "$zip_path" .git/HEAD >out &&
+ test_file_not_empty out
+'
+
+test_done
diff --git a/t/t0095-bloom.sh b/t/t0095-bloom.sh
index daeb4a5..b567383 100755
--- a/t/t0095-bloom.sh
+++ b/t/t0095-bloom.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='Testing the various Bloom filter computations in bloom.c'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'compute unseeded murmur3 hash for empty string' '
diff --git a/t/t0202-gettext-perl.sh b/t/t0202-gettext-perl.sh
index df2ea34..5a6f280 100755
--- a/t/t0202-gettext-perl.sh
+++ b/t/t0202-gettext-perl.sh
@@ -7,22 +7,12 @@ test_description='Perl gettext interface (Git::I18N)'
TEST_PASSES_SANITIZE_LEAK=true
. ./lib-gettext.sh
+. "$TEST_DIRECTORY"/lib-perl.sh
+skip_all_if_no_Test_More
-if ! test_have_prereq PERL; then
- skip_all='skipping perl interface tests, perl not available'
- test_done
-fi
-
-perl -MTest::More -e 0 2>/dev/null || {
- skip_all="Perl Test::More unavailable, skipping test"
- test_done
-}
-
-# The external test will outputs its own plan
-test_external_has_tap=1
-
-test_external_without_stderr \
- 'Perl Git::I18N API' \
- perl "$TEST_DIRECTORY"/t0202/test.pl
+test_expect_success 'run t0202/test.pl to test Git::I18N.pm' '
+ "$PERL_PATH" "$TEST_DIRECTORY"/t0202/test.pl 2>stderr &&
+ test_must_be_empty stderr
+'
test_done
diff --git a/t/t0203-gettext-setlocale-sanity.sh b/t/t0203-gettext-setlocale-sanity.sh
index 0ce1f22..86cff32 100755
--- a/t/t0203-gettext-setlocale-sanity.sh
+++ b/t/t0203-gettext-setlocale-sanity.sh
@@ -5,6 +5,7 @@
test_description="The Git C functions aren't broken by setlocale(3)"
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-gettext.sh
test_expect_success 'git show a ISO-8859-1 commit under C locale' '
diff --git a/t/t0211-trace2-perf.sh b/t/t0211-trace2-perf.sh
index 22d0845..b4e9135 100755
--- a/t/t0211-trace2-perf.sh
+++ b/t/t0211-trace2-perf.sh
@@ -173,4 +173,99 @@ test_expect_success 'using global config, perf stream, return code 0' '
test_cmp expect actual
'
+# Exercise the stopwatch timers in a loop and confirm that we have
+# as many start/stop intervals as expected. We cannot really test the
+# actual (total, min, max) timer values, so we have to assume that they
+# are good, but we can verify the interval count.
+#
+# The timer "test/test1" should only emit a global summary "timer" event.
+# The timer "test/test2" should emit per-thread "th_timer" events and a
+# global summary "timer" event.
+
+have_timer_event () {
+ thread=$1 event=$2 category=$3 name=$4 intervals=$5 file=$6 &&
+
+ pattern="d0|${thread}|${event}||||${category}|name:${name} intervals:${intervals}" &&
+
+ grep "${pattern}" ${file}
+}
+
+test_expect_success 'stopwatch timer test/test1' '
+ test_when_finished "rm trace.perf actual" &&
+ test_config_global trace2.perfBrief 1 &&
+ test_config_global trace2.perfTarget "$(pwd)/trace.perf" &&
+
+ # Use the timer "test1" 5 times from "main".
+ test-tool trace2 100timer 5 10 &&
+
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
+
+ have_timer_event "main" "timer" "test" "test1" 5 actual
+'
+
+test_expect_success PTHREAD 'stopwatch timer test/test2' '
+ test_when_finished "rm trace.perf actual" &&
+ test_config_global trace2.perfBrief 1 &&
+ test_config_global trace2.perfTarget "$(pwd)/trace.perf" &&
+
+ # Use the timer "test2" 5 times each in 3 threads.
+ test-tool trace2 101timer 5 10 3 &&
+
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
+
+ # So we should have 3 per-thread events of 5 each.
+ have_timer_event "th01:ut_101" "th_timer" "test" "test2" 5 actual &&
+ have_timer_event "th02:ut_101" "th_timer" "test" "test2" 5 actual &&
+ have_timer_event "th03:ut_101" "th_timer" "test" "test2" 5 actual &&
+
+ # And we should have 15 total uses.
+ have_timer_event "main" "timer" "test" "test2" 15 actual
+'
+
+# Exercise the global counters and confirm that we get the expected values.
+#
+# The counter "test/test1" should only emit a global summary "counter" event.
+# The counter "test/test2" could emit per-thread "th_counter" events and a
+# global summary "counter" event.
+
+have_counter_event () {
+ thread=$1 event=$2 category=$3 name=$4 value=$5 file=$6 &&
+
+ pattern="d0|${thread}|${event}||||${category}|name:${name} value:${value}" &&
+
+ grep "${patern}" ${file}
+}
+
+test_expect_success 'global counter test/test1' '
+ test_when_finished "rm trace.perf actual" &&
+ test_config_global trace2.perfBrief 1 &&
+ test_config_global trace2.perfTarget "$(pwd)/trace.perf" &&
+
+ # Use the counter "test1" and add n integers.
+ test-tool trace2 200counter 1 2 3 4 5 &&
+
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
+
+ have_counter_event "main" "counter" "test" "test1" 15 actual
+'
+
+test_expect_success PTHREAD 'global counter test/test2' '
+ test_when_finished "rm trace.perf actual" &&
+ test_config_global trace2.perfBrief 1 &&
+ test_config_global trace2.perfTarget "$(pwd)/trace.perf" &&
+
+ # Add 2 integers to the counter "test2" in each of 3 threads.
+ test-tool trace2 201counter 7 13 3 &&
+
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
+
+ # So we should have 3 per-thread events of 5 each.
+ have_counter_event "th01:ut_201" "th_counter" "test" "test2" 20 actual &&
+ have_counter_event "th02:ut_201" "th_counter" "test" "test2" 20 actual &&
+ have_counter_event "th03:ut_201" "th_counter" "test" "test2" 20 actual &&
+
+ # And we should have a single event with the total across all threads.
+ have_counter_event "main" "counter" "test" "test2" 60 actual
+'
+
test_done
diff --git a/t/t0211/scrub_perf.perl b/t/t0211/scrub_perf.perl
index 299999f..7a50bae 100644
--- a/t/t0211/scrub_perf.perl
+++ b/t/t0211/scrub_perf.perl
@@ -64,6 +64,12 @@ while (<>) {
goto SKIP_LINE;
}
}
+ elsif ($tokens[$col_event] =~ m/timer/) {
+ # This also captures "th_timer" events
+ $tokens[$col_rest] =~ s/ total:\d+\.\d*/ total:_T_TOTAL_/;
+ $tokens[$col_rest] =~ s/ min:\d+\.\d*/ min:_T_MIN_/;
+ $tokens[$col_rest] =~ s/ max:\d+\.\d*/ max:_T_MAX_/;
+ }
# 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.
diff --git a/t/t0410-partial-clone.sh b/t/t0410-partial-clone.sh
index 1e864cf..5b7bee8 100755
--- a/t/t0410-partial-clone.sh
+++ b/t/t0410-partial-clone.sh
@@ -215,6 +215,20 @@ test_expect_success 'fetching of missing objects' '
grep "$HASH" out
'
+test_expect_success 'fetching of a promised object that promisor remote no longer has' '
+ rm -f err &&
+ test_create_repo unreliable-server &&
+ git -C unreliable-server config uploadpack.allowanysha1inwant 1 &&
+ git -C unreliable-server config uploadpack.allowfilter 1 &&
+ test_commit -C unreliable-server foo &&
+
+ git clone --filter=blob:none --no-checkout "file://$(pwd)/unreliable-server" unreliable-client &&
+
+ rm -rf unreliable-server/.git/objects/* &&
+ test_must_fail git -C unreliable-client checkout HEAD 2>err &&
+ grep "could not fetch.*from promisor remote" err
+'
+
test_expect_success 'fetching of missing objects works with ref-in-want enabled' '
# ref-in-want requires protocol version 2
git -C server config protocol.version 2 &&
diff --git a/t/t0450-txt-doc-vs-help.sh b/t/t0450-txt-doc-vs-help.sh
new file mode 100755
index 0000000..cd3969e
--- /dev/null
+++ b/t/t0450-txt-doc-vs-help.sh
@@ -0,0 +1,172 @@
+#!/bin/sh
+
+test_description='assert (unbuilt) Documentation/*.txt and -h output
+
+Run this with --debug to see a summary of where we still fail to make
+the two versions consistent with one another.'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'setup: list of builtins' '
+ git --list-cmds=builtins >builtins
+'
+
+test_expect_success 'list of txt and help mismatches is sorted' '
+ sort -u "$TEST_DIRECTORY"/t0450/txt-help-mismatches >expect &&
+ if ! test_cmp expect "$TEST_DIRECTORY"/t0450/txt-help-mismatches
+ then
+ BUG "please keep the list of txt and help mismatches sorted"
+ fi
+'
+
+help_to_synopsis () {
+ builtin="$1" &&
+ out_dir="out/$builtin" &&
+ out="$out_dir/help.synopsis" &&
+ if test -f "$out"
+ then
+ echo "$out" &&
+ return 0
+ fi &&
+ mkdir -p "$out_dir" &&
+ test_expect_code 129 git $builtin -h >"$out.raw" 2>&1 &&
+ sed -n \
+ -e '1,/^$/ {
+ /^$/d;
+ s/^usage: //;
+ s/^ *or: //;
+ p;
+ }' <"$out.raw" >"$out" &&
+ echo "$out"
+}
+
+builtin_to_txt () {
+ echo "$GIT_BUILD_DIR/Documentation/git-$1.txt"
+}
+
+txt_to_synopsis () {
+ builtin="$1" &&
+ out_dir="out/$builtin" &&
+ out="$out_dir/txt.synopsis" &&
+ if test -f "$out"
+ then
+ echo "$out" &&
+ return 0
+ fi &&
+ b2t="$(builtin_to_txt "$builtin")" &&
+ sed -n \
+ -e '/^\[verse\]$/,/^$/ {
+ /^$/d;
+ /^\[verse\]$/d;
+
+ s/{litdd}/--/g;
+ s/'\''\(git[ a-z-]*\)'\''/\1/g;
+
+ p;
+ }' \
+ <"$b2t" >"$out" &&
+ echo "$out"
+}
+
+check_dashed_labels () {
+ ! grep -E "<[^>_-]+_" "$1"
+}
+
+HT=" "
+
+align_after_nl () {
+ builtin="$1" &&
+ len=$(printf "git %s " "$builtin" | wc -c) &&
+ pad=$(printf "%${len}s" "") &&
+
+ sed "s/^[ $HT][ $HT]*/$pad/"
+}
+
+test_debug '>failing'
+while read builtin
+do
+ # -h output assertions
+ test_expect_success "$builtin -h output has no \t" '
+ h2s="$(help_to_synopsis "$builtin")" &&
+ ! grep "$HT" "$h2s"
+ '
+
+ test_expect_success "$builtin -h output has dashed labels" '
+ check_dashed_labels "$(help_to_synopsis "$builtin")"
+ '
+
+ test_expect_success "$builtin -h output has consistent spacing" '
+ h2s="$(help_to_synopsis "$builtin")" &&
+ sed -n \
+ -e "/^ / {
+ s/[^ ].*//;
+ p;
+ }" \
+ <"$h2s" >help &&
+ sort -u help >help.ws &&
+ if test -s help.ws
+ then
+ test_line_count = 1 help.ws
+ fi
+ '
+
+ txt="$(builtin_to_txt "$builtin")" &&
+ preq="$(echo BUILTIN_TXT_$builtin | tr '[:lower:]-' '[:upper:]_')" &&
+
+ if test -f "$txt"
+ then
+ test_set_prereq "$preq"
+ fi &&
+
+ # *.txt output assertions
+ test_expect_success "$preq" "$builtin *.txt SYNOPSIS has dashed labels" '
+ check_dashed_labels "$(txt_to_synopsis "$builtin")"
+ '
+
+ # *.txt output consistency assertions
+ result=
+ if grep -q "^$builtin$" "$TEST_DIRECTORY"/t0450/txt-help-mismatches
+ then
+ result=failure
+ else
+ result=success
+ fi &&
+ test_expect_$result "$preq" "$builtin -h output and SYNOPSIS agree" '
+ t2s="$(txt_to_synopsis "$builtin")" &&
+ if test "$builtin" = "merge-tree"
+ then
+ test_when_finished "rm -f t2s.new" &&
+ sed -e '\''s/ (deprecated)$//g'\'' <"$t2s" >t2s.new
+ t2s=t2s.new
+ fi &&
+ h2s="$(help_to_synopsis "$builtin")" &&
+
+ # The *.txt and -h use different spacing for the
+ # alignment of continued usage output, normalize it.
+ align_after_nl "$builtin" <"$t2s" >txt &&
+ align_after_nl "$builtin" <"$h2s" >help &&
+ test_cmp txt help
+ '
+
+ if test_have_prereq "$preq" && test -e txt && test -e help
+ then
+ test_debug '
+ if test_cmp txt help >cmp 2>/dev/null
+ then
+ echo "=== DONE: $builtin ==="
+ else
+ echo "=== TODO: $builtin ===" &&
+ cat cmp
+ fi >>failing
+ '
+
+ # Not in test_expect_success in case --run is being
+ # used with --debug
+ rm -f txt help tmp 2>/dev/null
+ fi
+done <builtins
+
+test_debug 'say "$(cat failing)"'
+
+test_done
diff --git a/t/t0450/txt-help-mismatches b/t/t0450/txt-help-mismatches
new file mode 100644
index 0000000..a0777ac
--- /dev/null
+++ b/t/t0450/txt-help-mismatches
@@ -0,0 +1,58 @@
+add
+am
+apply
+archive
+bisect
+blame
+branch
+check-ref-format
+checkout
+checkout-index
+clone
+column
+config
+credential
+credential-cache
+credential-store
+fast-export
+fast-import
+fetch-pack
+fmt-merge-msg
+for-each-ref
+format-patch
+fsck-objects
+fsmonitor--daemon
+gc
+grep
+index-pack
+init-db
+log
+ls-files
+ls-tree
+mailinfo
+mailsplit
+maintenance
+merge
+merge-file
+merge-index
+merge-one-file
+multi-pack-index
+name-rev
+notes
+pack-objects
+push
+range-diff
+rebase
+remote
+remote-ext
+remote-fd
+repack
+reset
+restore
+rev-parse
+show
+stage
+switch
+update-index
+update-ref
+whatchanged
diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh
index bd5313c..cdc077c 100755
--- a/t/t1002-read-tree-m-u-2way.sh
+++ b/t/t1002-read-tree-m-u-2way.sh
@@ -154,7 +154,7 @@ test_expect_success \
read_tree_u_must_succeed --reset -u $treeH &&
echo frotz frotz >frotz &&
git update-index --add frotz &&
- if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
+ ! read_tree_u_must_succeed -m -u $treeH $treeM'
test_expect_success \
'9 - conflicting addition.' \
@@ -163,7 +163,7 @@ test_expect_success \
echo frotz frotz >frotz &&
git update-index --add frotz &&
echo frotz >frotz &&
- if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
+ ! read_tree_u_must_succeed -m -u $treeH $treeM'
test_expect_success \
'10 - path removed.' \
@@ -186,7 +186,7 @@ test_expect_success \
echo rezrov >rezrov &&
git update-index --add rezrov &&
echo rezrov rezrov >rezrov &&
- if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
+ ! read_tree_u_must_succeed -m -u $treeH $treeM'
test_expect_success \
'12 - unmatching local changes being removed.' \
@@ -194,7 +194,7 @@ test_expect_success \
read_tree_u_must_succeed --reset -u $treeH &&
echo rezrov rezrov >rezrov &&
git update-index --add rezrov &&
- if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
+ ! read_tree_u_must_succeed -m -u $treeH $treeM'
test_expect_success \
'13 - unmatching local changes being removed.' \
@@ -203,7 +203,7 @@ test_expect_success \
echo rezrov rezrov >rezrov &&
git update-index --add rezrov &&
echo rezrov >rezrov &&
- if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
+ ! read_tree_u_must_succeed -m -u $treeH $treeM'
cat >expected <<EOF
-100644 X 0 nitfol
@@ -251,7 +251,7 @@ test_expect_success \
read_tree_u_must_succeed --reset -u $treeH &&
echo bozbar bozbar >bozbar &&
git update-index --add bozbar &&
- if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
+ ! read_tree_u_must_succeed -m -u $treeH $treeM'
test_expect_success \
'17 - conflicting local change.' \
@@ -260,7 +260,7 @@ test_expect_success \
echo bozbar bozbar >bozbar &&
git update-index --add bozbar &&
echo bozbar bozbar bozbar >bozbar &&
- if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
+ ! read_tree_u_must_succeed -m -u $treeH $treeM'
test_expect_success \
'18 - local change already having a good result.' \
@@ -316,7 +316,7 @@ test_expect_success \
echo bozbar >bozbar &&
git update-index --add bozbar &&
echo gnusto gnusto >bozbar &&
- if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
+ ! read_tree_u_must_succeed -m -u $treeH $treeM'
# Also make sure we did not break DF vs DF/DF case.
test_expect_success \
diff --git a/t/t1020-subdirectory.sh b/t/t1020-subdirectory.sh
index 9fdbb2a..45eef94 100755
--- a/t/t1020-subdirectory.sh
+++ b/t/t1020-subdirectory.sh
@@ -6,6 +6,7 @@
test_description='Try various core-level commands in subdirectory.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-read-tree.sh
diff --git a/t/t1022-read-tree-partial-clone.sh b/t/t1022-read-tree-partial-clone.sh
index a9953b6..da53971 100755
--- a/t/t1022-read-tree-partial-clone.sh
+++ b/t/t1022-read-tree-partial-clone.sh
@@ -19,7 +19,7 @@ test_expect_success 'read-tree in partial clone prefetches in one batch' '
git -C server config uploadpack.allowfilter 1 &&
git -C server config uploadpack.allowanysha1inwant 1 &&
git clone --bare --filter=blob:none "file://$(pwd)/server" client &&
- GIT_TRACE_PACKET="$(pwd)/trace" git -C client read-tree $TREE &&
+ GIT_TRACE_PACKET="$(pwd)/trace" git -C client read-tree $TREE $TREE &&
# "done" marks the end of negotiation (once per fetch). Expect that
# only one fetch occurs.
diff --git a/t/t1050-large.sh b/t/t1050-large.sh
index 4f3aa17..c71932b 100755
--- a/t/t1050-large.sh
+++ b/t/t1050-large.sh
@@ -5,6 +5,12 @@ test_description='adding and checking out large blobs'
. ./test-lib.sh
+test_expect_success 'core.bigFileThreshold must be non-negative' '
+ test_must_fail git -c core.bigFileThreshold=-1 rev-parse >out 2>err &&
+ grep "bad numeric config value" err &&
+ test_must_be_empty out
+'
+
test_expect_success setup '
# clone does not allow us to pass core.bigfilethreshold to
# new repos, so set core.bigfilethreshold globally
diff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh
index 5b8e47e..35261af 100755
--- a/t/t1060-object-corruption.sh
+++ b/t/t1060-object-corruption.sh
@@ -139,4 +139,11 @@ test_expect_success 'internal tree objects are not "missing"' '
)
'
+test_expect_success 'partial clone of corrupted repository' '
+ test_config -C misnamed uploadpack.allowFilter true &&
+ git clone --no-local --no-checkout --filter=blob:none \
+ misnamed corrupt-partial && \
+ test_must_fail git -C corrupt-partial checkout --force
+'
+
test_done
diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh
index de1ec89..b563d6c 100755
--- a/t/t1091-sparse-checkout-builtin.sh
+++ b/t/t1091-sparse-checkout-builtin.sh
@@ -560,7 +560,8 @@ test_expect_success 'interaction with submodules' '
(
cd super &&
mkdir modules &&
- git submodule add ../repo modules/child &&
+ git -c protocol.file.allow=always \
+ submodule add ../repo modules/child &&
git add . &&
git commit -m "add submodule" &&
git sparse-checkout init --cone &&
diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh
index 763c6cc..8019190 100755
--- a/t/t1092-sparse-checkout-compatibility.sh
+++ b/t/t1092-sparse-checkout-compatibility.sh
@@ -162,6 +162,19 @@ init_repos () {
git -C sparse-index sparse-checkout set deep
}
+init_repos_as_submodules () {
+ git reset --hard &&
+ init_repos &&
+ git submodule add ./full-checkout &&
+ git submodule add ./sparse-checkout &&
+ git submodule add ./sparse-index &&
+
+ git submodule status >actual &&
+ grep full-checkout actual &&
+ grep sparse-checkout actual &&
+ grep sparse-index actual
+}
+
run_on_sparse () {
(
cd sparse-checkout &&
@@ -372,6 +385,23 @@ test_expect_success 'deep changes during checkout' '
test_all_match git checkout base
'
+test_expect_success 'checkout with modified sparse directory' '
+ init_repos &&
+
+ test_all_match git checkout rename-in-to-out -- . &&
+ test_sparse_match git sparse-checkout reapply &&
+ test_all_match git checkout base
+'
+
+test_expect_success 'checkout orphan then non-orphan' '
+ init_repos &&
+
+ test_all_match git checkout --orphan test-orphan &&
+ test_all_match git status --porcelain=v2 &&
+ test_all_match git checkout base &&
+ test_all_match git status --porcelain=v2
+'
+
test_expect_success 'add outside sparse cone' '
init_repos &&
@@ -548,7 +578,7 @@ test_expect_success 'blame with pathspec inside sparse definition' '
deep/deeper1/a \
deep/deeper1/deepest/a
do
- test_all_match git blame $file
+ test_all_match git blame $file || return 1
done
'
@@ -559,7 +589,7 @@ test_expect_success 'blame with pathspec outside sparse definition' '
init_repos &&
test_sparse_match git sparse-checkout set &&
- for file in a \
+ for file in \
deep/a \
deep/deeper1/a \
deep/deeper1/deepest/a
@@ -571,7 +601,7 @@ test_expect_success 'blame with pathspec outside sparse definition' '
# We compare sparse-checkout-err and sparse-index-err in
# `test_sparse_match`. Given we know they are the same, we
# only check the content of sparse-index-err here.
- test_cmp expect sparse-index-err
+ test_cmp expect sparse-index-err || return 1
done
'
@@ -687,6 +717,23 @@ test_expect_success 'reset with wildcard pathspec' '
test_all_match git ls-files -s -- folder1
'
+test_expect_success 'reset hard with removed sparse dir' '
+ init_repos &&
+
+ run_on_all git rm -r --sparse folder1 &&
+ test_all_match git status --porcelain=v2 &&
+
+ test_all_match git reset --hard &&
+ test_all_match git status --porcelain=v2 &&
+
+ cat >expect <<-\EOF &&
+ folder1/
+ EOF
+
+ git -C sparse-index ls-files --sparse folder1 >out &&
+ test_cmp expect out
+'
+
test_expect_success 'update-index modify outside sparse definition' '
init_repos &&
@@ -912,7 +959,7 @@ test_expect_success 'read-tree --prefix' '
test_all_match git read-tree --prefix=deep/deeper1/deepest -u deepest &&
test_all_match git status --porcelain=v2 &&
- test_all_match git rm -rf --sparse folder1/ &&
+ run_on_all git rm -rf --sparse folder1/ &&
test_all_match git read-tree --prefix=folder1/ -u update-folder1 &&
test_all_match git status --porcelain=v2 &&
@@ -1268,6 +1315,8 @@ test_expect_success 'submodule handling' '
test_all_match git add modules &&
test_all_match git commit -m "add modules directory" &&
+ test_config_global protocol.file.allow always &&
+
run_on_all git submodule add "$(pwd)/initial-repo" modules/sub &&
test_all_match git commit -m "add submodule" &&
@@ -1340,10 +1389,14 @@ ensure_not_expanded () {
shift &&
test_must_fail env \
GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
- git -C sparse-index "$@" || return 1
+ git -C sparse-index "$@" \
+ >sparse-index-out \
+ 2>sparse-index-error || return 1
else
GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
- git -C sparse-index "$@" || return 1
+ git -C sparse-index "$@" \
+ >sparse-index-out \
+ 2>sparse-index-error || return 1
fi &&
test_region ! index ensure_full_index trace2.txt
}
@@ -1542,7 +1595,7 @@ test_expect_success 'sparse index is not expanded: blame' '
deep/deeper1/a \
deep/deeper1/deepest/a
do
- ensure_not_expanded blame $file
+ ensure_not_expanded blame $file || return 1
done
'
@@ -1853,4 +1906,153 @@ test_expect_success 'mv directory from out-of-cone to in-cone' '
grep -e "H deep/0/1" actual
'
+test_expect_success 'rm pathspec inside sparse definition' '
+ init_repos &&
+
+ test_all_match git rm deep/a &&
+ test_all_match git status --porcelain=v2 &&
+
+ # test wildcard
+ run_on_all git reset --hard &&
+ test_all_match git rm deep/* &&
+ test_all_match git status --porcelain=v2 &&
+
+ # test recursive rm
+ run_on_all git reset --hard &&
+ test_all_match git rm -r deep &&
+ test_all_match git status --porcelain=v2
+'
+
+test_expect_success 'rm pathspec outside sparse definition' '
+ init_repos &&
+
+ for file in folder1/a folder1/0/1
+ do
+ test_sparse_match test_must_fail git rm $file &&
+ test_sparse_match test_must_fail git rm --cached $file &&
+ test_sparse_match git rm --sparse $file &&
+ test_sparse_match git status --porcelain=v2 || return 1
+ done &&
+
+ cat >folder1-full <<-EOF &&
+ rm ${SQ}folder1/0/0/0${SQ}
+ rm ${SQ}folder1/0/1${SQ}
+ rm ${SQ}folder1/a${SQ}
+ EOF
+
+ cat >folder1-sparse <<-EOF &&
+ rm ${SQ}folder1/${SQ}
+ EOF
+
+ # test wildcard
+ run_on_sparse git reset --hard &&
+ run_on_sparse git sparse-checkout reapply &&
+ test_sparse_match test_must_fail git rm folder1/* &&
+ run_on_sparse git rm --sparse folder1/* &&
+ test_cmp folder1-full sparse-checkout-out &&
+ test_cmp folder1-sparse sparse-index-out &&
+ test_sparse_match git status --porcelain=v2 &&
+
+ # test recursive rm
+ run_on_sparse git reset --hard &&
+ run_on_sparse git sparse-checkout reapply &&
+ test_sparse_match test_must_fail git rm --sparse folder1 &&
+ run_on_sparse git rm --sparse -r folder1 &&
+ test_cmp folder1-full sparse-checkout-out &&
+ test_cmp folder1-sparse sparse-index-out &&
+ test_sparse_match git status --porcelain=v2
+'
+
+test_expect_success 'rm pathspec expands index when necessary' '
+ init_repos &&
+
+ # in-cone pathspec (do not expand)
+ ensure_not_expanded rm "deep/deep*" &&
+ test_must_be_empty sparse-index-err &&
+
+ # out-of-cone pathspec (expand)
+ ! ensure_not_expanded rm --sparse "folder1/a*" &&
+ test_must_be_empty sparse-index-err &&
+
+ # pathspec that should expand index
+ ! ensure_not_expanded rm "*/a" &&
+ test_must_be_empty sparse-index-err &&
+
+ ! ensure_not_expanded rm "**a" &&
+ test_must_be_empty sparse-index-err
+'
+
+test_expect_success 'sparse index is not expanded: rm' '
+ init_repos &&
+
+ ensure_not_expanded rm deep/a &&
+
+ # test in-cone wildcard
+ git -C sparse-index reset --hard &&
+ ensure_not_expanded rm deep/* &&
+
+ # test recursive rm
+ git -C sparse-index reset --hard &&
+ ensure_not_expanded rm -r deep
+'
+
+test_expect_success 'grep with and --cached' '
+ init_repos &&
+
+ test_all_match git grep --cached a &&
+ test_all_match git grep --cached a -- "folder1/*"
+'
+
+test_expect_success 'grep is not expanded' '
+ init_repos &&
+
+ ensure_not_expanded grep a &&
+ ensure_not_expanded grep a -- deep/* &&
+
+ # All files within the folder1/* pathspec are sparse,
+ # so this command does not find any matches
+ ensure_not_expanded ! grep a -- folder1/* &&
+
+ # test out-of-cone pathspec with or without wildcard
+ ensure_not_expanded grep --cached a -- "folder1/a" &&
+ ensure_not_expanded grep --cached a -- "folder1/*" &&
+
+ # test in-cone pathspec with or without wildcard
+ ensure_not_expanded grep --cached a -- "deep/a" &&
+ ensure_not_expanded grep --cached a -- "deep/*"
+'
+
+# NEEDSWORK: when running `grep` in the superproject with --recurse-submodules,
+# Git expands the index of the submodules unexpectedly. Even though `grep`
+# builtin is marked as "command_requires_full_index = 0", this config is only
+# useful for the superproject. Namely, the submodules have their own configs,
+# which are _not_ populated by the one-time sparse-index feature switch.
+test_expect_failure 'grep within submodules is not expanded' '
+ init_repos_as_submodules &&
+
+ # do not use ensure_not_expanded() here, becasue `grep` should be
+ # run in the superproject, not in "./sparse-index"
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+ git grep --cached --recurse-submodules a -- "*/folder1/*" &&
+ test_region ! index ensure_full_index trace2.txt
+'
+
+# NEEDSWORK: this test is not actually testing the code. The design purpose
+# of this test is to verify the grep result when the submodules are using a
+# sparse-index. Namely, we want "folder1/" as a tree (a sparse directory); but
+# because of the index expansion, we are now grepping the "folder1/a" blob.
+# Because of the problem stated above 'grep within submodules is not expanded',
+# we don't have the ideal test environment yet.
+test_expect_success 'grep sparse directory within submodules' '
+ init_repos_as_submodules &&
+
+ cat >expect <<-\EOF &&
+ full-checkout/folder1/a:a
+ sparse-checkout/folder1/a:a
+ sparse-index/folder1/a:a
+ EOF
+ git grep --cached --recurse-submodules a -- "*/folder1/*" >actual &&
+ test_cmp actual expect
+'
+
test_done
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index c6661e6..2575279 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -2228,6 +2228,12 @@ test_expect_success '--type rejects unknown specifiers' '
test_i18ngrep "unrecognized --type argument" error
'
+test_expect_success '--type=int requires at least one digit' '
+ test_must_fail git config --type int --default m some.key >out 2>error &&
+ grep "bad numeric config value" error &&
+ test_must_be_empty out
+'
+
test_expect_success '--replace-all does not invent newlines' '
q_to_tab >.git/config <<-\EOF &&
[abc]key
diff --git a/t/t1304-default-acl.sh b/t/t1304-default-acl.sh
index 335d3f3..c69ae41 100755
--- a/t/t1304-default-acl.sh
+++ b/t/t1304-default-acl.sh
@@ -18,7 +18,7 @@ test_expect_success 'checking for a working acl setup' '
if setfacl -m d:m:rwx -m u:root:rwx . &&
getfacl . | grep user:root:rwx &&
touch should-have-readable-acl &&
- getfacl should-have-readable-acl | egrep "mask::?rw-"
+ getfacl should-have-readable-acl | grep -E "mask::?rw-"
then
test_set_prereq SETFACL
fi
@@ -34,7 +34,7 @@ check_perms_and_acl () {
getfacl "$1" > actual &&
grep -q "user:root:rwx" actual &&
grep -q "user:${LOGNAME}:rwx" actual &&
- egrep "mask::?r--" actual > /dev/null 2>&1 &&
+ grep -E "mask::?r--" actual > /dev/null 2>&1 &&
grep -q "group::---" actual || false
}
diff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh
index 9fb0b90..d708acd 100755
--- a/t/t1401-symbolic-ref.sh
+++ b/t/t1401-symbolic-ref.sh
@@ -165,4 +165,28 @@ test_expect_success 'symbolic-ref can resolve d/f name (ENOTDIR)' '
test_cmp expect actual
'
+test_expect_success 'symbolic-ref refuses invalid target for non-HEAD' '
+ test_must_fail git symbolic-ref refs/heads/invalid foo..bar
+'
+
+test_expect_success 'symbolic-ref allows top-level target for non-HEAD' '
+ git symbolic-ref refs/heads/top-level FETCH_HEAD &&
+ git update-ref FETCH_HEAD HEAD &&
+ test_cmp_rev top-level HEAD
+'
+
+test_expect_success 'symbolic-ref pointing at another' '
+ git update-ref refs/heads/maint-2.37 HEAD &&
+ git symbolic-ref refs/heads/maint refs/heads/maint-2.37 &&
+ git checkout maint &&
+
+ git symbolic-ref HEAD >actual &&
+ echo refs/heads/maint-2.37 >expect &&
+ test_cmp expect actual &&
+
+ git symbolic-ref --no-recurse HEAD >actual &&
+ echo refs/heads/maint >expect &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t1405-main-ref-store.sh b/t/t1405-main-ref-store.sh
index 51f8291..e4627cf 100755
--- a/t/t1405-main-ref-store.sh
+++ b/t/t1405-main-ref-store.sh
@@ -5,6 +5,7 @@ test_description='test main ref store api'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
RUN="test-tool ref-store main"
diff --git a/t/t1407-worktree-ref-store.sh b/t/t1407-worktree-ref-store.sh
index ad8006c..05b1881 100755
--- a/t/t1407-worktree-ref-store.sh
+++ b/t/t1407-worktree-ref-store.sh
@@ -5,6 +5,7 @@ test_description='test worktree ref store api'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
RWT="test-tool ref-store worktree:wt"
diff --git a/t/t1418-reflog-exists.sh b/t/t1418-reflog-exists.sh
index d51ecd5..2268bca 100755
--- a/t/t1418-reflog-exists.sh
+++ b/t/t1418-reflog-exists.sh
@@ -4,6 +4,7 @@ test_description='Test reflog display routines'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index ab7f31f..ace4556 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -364,6 +364,20 @@ test_expect_success 'tree entry with type mismatch' '
test_i18ngrep ! "dangling blob" out
'
+test_expect_success 'tree entry with bogus mode' '
+ test_when_finished "remove_object \$blob" &&
+ test_when_finished "remove_object \$tree" &&
+ blob=$(echo blob | git hash-object -w --stdin) &&
+ blob_oct=$(echo $blob | hex2oct) &&
+ tree=$(printf "100000 foo\0${blob_oct}" |
+ git hash-object -t tree --stdin -w --literally) &&
+ git fsck 2>err &&
+ cat >expect <<-EOF &&
+ warning in tree $tree: badFilemode: contains bad file modes
+ EOF
+ test_cmp expect err
+'
+
test_expect_success 'tag pointing to nonexistent' '
badoid=$(test_oid deadbeef) &&
cat >invalid-tag <<-EOF &&
@@ -493,6 +507,54 @@ test_expect_success 'rev-list --verify-objects with bad sha1' '
test_i18ngrep -q "error: hash mismatch $(dirname $new)$(test_oid ff_2)" out
'
+# An actual bit corruption is more likely than swapped commits, but
+# this provides an easy way to have commits which don't match their purported
+# hashes, but which aren't so broken we can't read them at all.
+test_expect_success 'rev-list --verify-objects notices swapped commits' '
+ git init swapped-commits &&
+ (
+ cd swapped-commits &&
+ test_commit one &&
+ test_commit two &&
+ one_oid=$(git rev-parse HEAD) &&
+ two_oid=$(git rev-parse HEAD^) &&
+ one=.git/objects/$(test_oid_to_path $one_oid) &&
+ two=.git/objects/$(test_oid_to_path $two_oid) &&
+ mv $one tmp &&
+ mv $two $one &&
+ mv tmp $two &&
+ test_must_fail git rev-list --verify-objects HEAD
+ )
+'
+
+test_expect_success 'set up repository with commit-graph' '
+ git init corrupt-graph &&
+ (
+ cd corrupt-graph &&
+ test_commit one &&
+ test_commit two &&
+ git commit-graph write --reachable
+ )
+'
+
+corrupt_graph_obj () {
+ oid=$(git -C corrupt-graph rev-parse "$1") &&
+ obj=corrupt-graph/.git/objects/$(test_oid_to_path $oid) &&
+ test_when_finished 'mv backup $obj' &&
+ mv $obj backup &&
+ echo garbage >$obj
+}
+
+test_expect_success 'rev-list --verify-objects with commit graph (tip)' '
+ corrupt_graph_obj HEAD &&
+ test_must_fail git -C corrupt-graph rev-list --verify-objects HEAD
+'
+
+test_expect_success 'rev-list --verify-objects with commit graph (parent)' '
+ corrupt_graph_obj HEAD^ &&
+ test_must_fail git -C corrupt-graph rev-list --verify-objects HEAD
+'
+
test_expect_success 'force fsck to ignore double author' '
git cat-file commit HEAD >basis &&
sed "s/^author .*/&,&/" <basis | tr , \\n >multiple-authors &&
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index 1c2df08..81de584 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -4,6 +4,7 @@ test_description='test git rev-parse'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_one () {
@@ -225,7 +226,8 @@ test_expect_success 'showing the superproject correctly' '
test_commit -C super test_commit &&
test_create_repo sub &&
test_commit -C sub test_commit &&
- git -C super submodule add ../sub dir/sub &&
+ git -c protocol.file.allow=always \
+ -C super submodule add ../sub dir/sub &&
echo $(pwd)/super >expect &&
git -C super/dir/sub rev-parse --show-superproject-working-tree >out &&
test_cmp expect out &&
diff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh
index 284fe18..de1d48f 100755
--- a/t/t1502-rev-parse-parseopt.sh
+++ b/t/t1502-rev-parse-parseopt.sh
@@ -306,6 +306,13 @@ test_expect_success 'test --parseopt help output: "wrapped" options normal "or:"
test_cmp expect actual
'
+test_expect_success 'test --parseopt invalid opt-spec' '
+ test_write_lines x -- "=, x" >spec &&
+ echo "fatal: missing opt-spec before option flags" >expect &&
+ test_must_fail git rev-parse --parseopt -- >out <spec 2>err &&
+ test_cmp expect err
+'
+
test_expect_success 'test --parseopt help output: multi-line blurb after empty line' '
sed -e "s/^|//" >spec <<-\EOF &&
|cmd [--some-option]
diff --git a/t/t1503-rev-parse-verify.sh b/t/t1503-rev-parse-verify.sh
index ba43168..bc13683 100755
--- a/t/t1503-rev-parse-verify.sh
+++ b/t/t1503-rev-parse-verify.sh
@@ -132,7 +132,7 @@ test_expect_success 'use --default' '
test_must_fail git rev-parse --verify --default bar
'
-test_expect_success !SANITIZE_LEAK 'main@{n} for various n' '
+test_expect_success 'main@{n} for various n' '
git reflog >out &&
N=$(wc -l <out) &&
Nm1=$(($N-1)) &&
diff --git a/t/t1701-racy-split-index.sh b/t/t1701-racy-split-index.sh
index 5dc221e..d8fa489 100755
--- a/t/t1701-racy-split-index.sh
+++ b/t/t1701-racy-split-index.sh
@@ -5,6 +5,7 @@
test_description='racy split index'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t1800-hook.sh b/t/t1800-hook.sh
index 210f429..2ef3579 100755
--- a/t/t1800-hook.sh
+++ b/t/t1800-hook.sh
@@ -95,7 +95,7 @@ test_expect_success 'git hook run -- out-of-repo runs excluded' '
test_expect_success 'git -c core.hooksPath=<PATH> hook run' '
mkdir my-hooks &&
write_script my-hooks/test-hook <<-\EOF &&
- echo Hook ran $1 >>actual
+ echo Hook ran $1
EOF
cat >expect <<-\EOF &&
@@ -151,4 +151,30 @@ test_expect_success TTY 'git commit: stdout and stderr are connected to a TTY' '
test_hook_tty commit -m"B.new"
'
+test_expect_success 'git hook run a hook with a bad shebang' '
+ test_when_finished "rm -rf bad-hooks" &&
+ mkdir bad-hooks &&
+ write_script bad-hooks/test-hook "/bad/path/no/spaces" </dev/null &&
+
+ # TODO: We should emit the same (or at least a more similar)
+ # error on MINGW (essentially Git for Windows) and all other
+ # platforms.. See the OS-specific code in start_command()
+ if test_have_prereq !MINGW
+ then
+ cat >expect <<-\EOF
+ fatal: cannot run bad-hooks/test-hook: ...
+ EOF
+ else
+ cat >expect <<-\EOF
+ error: cannot spawn bad-hooks/test-hook: ...
+ EOF
+ fi &&
+ test_expect_code 1 git \
+ -c core.hooksPath=bad-hooks \
+ hook run test-hook >out 2>err &&
+ test_must_be_empty out &&
+ sed -e "s/test-hook: .*/test-hook: .../" <err >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t2006-checkout-index-basic.sh b/t/t2006-checkout-index-basic.sh
index 7705e3a..5d11987 100755
--- a/t/t2006-checkout-index-basic.sh
+++ b/t/t2006-checkout-index-basic.sh
@@ -3,6 +3,7 @@
test_description='basic checkout-index tests
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'checkout-index --gobbledegook' '
diff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh
index bc46713..2eab647 100755
--- a/t/t2020-checkout-detach.sh
+++ b/t/t2020-checkout-detach.sh
@@ -4,6 +4,7 @@ test_description='checkout into detached HEAD state'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
check_detached () {
diff --git a/t/t2023-checkout-m.sh b/t/t2023-checkout-m.sh
index 7b327b7..81e772f 100755
--- a/t/t2023-checkout-m.sh
+++ b/t/t2023-checkout-m.sh
@@ -7,6 +7,7 @@ Ensures that checkout -m on a resolved file restores the conflicted file'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t2080-parallel-checkout-basics.sh b/t/t2080-parallel-checkout-basics.sh
index c683e60..5ffe1a4 100755
--- a/t/t2080-parallel-checkout-basics.sh
+++ b/t/t2080-parallel-checkout-basics.sh
@@ -41,6 +41,8 @@ TEST_NO_CREATE_REPO=1
# - m/m (file)
#
test_expect_success 'setup repo for checkout with various types of changes' '
+ test_config_global protocol.file.allow always &&
+
git init sub &&
(
cd sub &&
@@ -140,6 +142,7 @@ do
esac
test_expect_success "$mode checkout on clone" '
+ test_config_global protocol.file.allow always &&
repo=various_${mode}_clone &&
set_checkout_config $workers $threshold &&
test_checkout_workers $expected_workers \
@@ -230,12 +233,9 @@ test_expect_success SYMLINKS 'parallel checkout checks for symlinks in leading d
# check the final report including sequential, parallel, and delayed entries
# all at the same time. So we must have finer control of the parallel checkout
# variables.
-test_expect_success PERL '"git checkout ." report should not include failed entries' '
- write_script rot13-filter.pl "$PERL_PATH" \
- <"$TEST_DIRECTORY"/t0021/rot13-filter.pl &&
-
+test_expect_success '"git checkout ." report should not include failed entries' '
test_config_global filter.delay.process \
- "\"$(pwd)/rot13-filter.pl\" --always-delay delayed.log clean smudge delay" &&
+ "test-tool rot13-filter --always-delay --log=delayed.log clean smudge delay" &&
test_config_global filter.delay.required true &&
test_config_global filter.cat.clean cat &&
test_config_global filter.cat.smudge cat &&
diff --git a/t/t2082-parallel-checkout-attributes.sh b/t/t2082-parallel-checkout-attributes.sh
index 2525457..f3511cd 100755
--- a/t/t2082-parallel-checkout-attributes.sh
+++ b/t/t2082-parallel-checkout-attributes.sh
@@ -138,12 +138,9 @@ test_expect_success 'parallel-checkout and external filter' '
# The delayed queue is independent from the parallel queue, and they should be
# able to work together in the same checkout process.
#
-test_expect_success PERL 'parallel-checkout and delayed checkout' '
- write_script rot13-filter.pl "$PERL_PATH" \
- <"$TEST_DIRECTORY"/t0021/rot13-filter.pl &&
-
+test_expect_success 'parallel-checkout and delayed checkout' '
test_config_global filter.delay.process \
- "\"$(pwd)/rot13-filter.pl\" --always-delay \"$(pwd)/delayed.log\" clean smudge delay" &&
+ "test-tool rot13-filter --always-delay --log=\"$(pwd)/delayed.log\" clean smudge delay" &&
test_config_global filter.delay.required true &&
echo "abcd" >original &&
diff --git a/t/t2205-add-worktree-config.sh b/t/t2205-add-worktree-config.sh
index 43d950d..98265ba 100755
--- a/t/t2205-add-worktree-config.sh
+++ b/t/t2205-add-worktree-config.sh
@@ -17,6 +17,7 @@ outside the repository. Two instances for which this can occur are tested:
repository can be added to the index.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success '1a: setup--config worktree' '
diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh
index f3242fe..d587e0b 100755
--- a/t/t2400-worktree-add.sh
+++ b/t/t2400-worktree-add.sh
@@ -669,6 +669,7 @@ test_expect_success '"add" should not fail because of another bad worktree' '
'
test_expect_success '"add" with uninitialized submodule, with submodule.recurse unset' '
+ test_config_global protocol.file.allow always &&
test_create_repo submodule &&
test_commit -C submodule first &&
test_create_repo project &&
@@ -684,6 +685,7 @@ test_expect_success '"add" with uninitialized submodule, with submodule.recurse
'
test_expect_success '"add" with initialized submodule, with submodule.recurse unset' '
+ test_config_global protocol.file.allow always &&
git -C project-clone submodule update --init &&
git -C project-clone worktree add ../project-4
'
diff --git a/t/t2403-worktree-move.sh b/t/t2403-worktree-move.sh
index a4e1a17..230a55e 100755
--- a/t/t2403-worktree-move.sh
+++ b/t/t2403-worktree-move.sh
@@ -2,6 +2,7 @@
test_description='test git worktree move, remove, lock and unlock'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -138,7 +139,8 @@ test_expect_success 'move a repo with uninitialized submodule' '
(
cd withsub &&
test_commit initial &&
- git submodule add "$PWD"/.git sub &&
+ git -c protocol.file.allow=always \
+ submodule add "$PWD"/.git sub &&
git commit -m withsub &&
git worktree add second HEAD &&
git worktree move second third
@@ -148,7 +150,7 @@ test_expect_success 'move a repo with uninitialized submodule' '
test_expect_success 'not move a repo with initialized submodule' '
(
cd withsub &&
- git -C third submodule update &&
+ git -c protocol.file.allow=always -C third submodule update &&
test_must_fail git worktree move third forth
)
'
@@ -227,6 +229,7 @@ test_expect_success 'remove cleans up .git/worktrees when empty' '
'
test_expect_success 'remove a repo with uninitialized submodule' '
+ test_config_global protocol.file.allow always &&
(
cd withsub &&
git worktree add to-remove HEAD &&
@@ -235,6 +238,7 @@ test_expect_success 'remove a repo with uninitialized submodule' '
'
test_expect_success 'not remove a repo with initialized submodule' '
+ test_config_global protocol.file.allow always &&
(
cd withsub &&
git worktree add to-remove HEAD &&
diff --git a/t/t2405-worktree-submodule.sh b/t/t2405-worktree-submodule.sh
index b172c26..11018f3 100755
--- a/t/t2405-worktree-submodule.sh
+++ b/t/t2405-worktree-submodule.sh
@@ -10,6 +10,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
base_path=$(pwd -P)
test_expect_success 'setup: create origin repos' '
+ git config --global protocol.file.allow always &&
git init origin/sub &&
test_commit -C origin/sub file1 &&
git init origin/main &&
diff --git a/t/t2407-worktree-heads.sh b/t/t2407-worktree-heads.sh
index 50815ac..019a40d 100755
--- a/t/t2407-worktree-heads.sh
+++ b/t/t2407-worktree-heads.sh
@@ -41,10 +41,10 @@ test_expect_success 'setup' '
test_expect_success 'refuse to overwrite: checked out in worktree' '
for i in 1 2 3 4
do
- test_must_fail git branch -f wt-$i HEAD 2>err
+ test_must_fail git branch -f wt-$i HEAD 2>err &&
grep "cannot force update the branch" err &&
- test_must_fail git branch -D wt-$i 2>err
+ test_must_fail git branch -D wt-$i 2>err &&
grep "Cannot delete branch" err || return 1
done
'
diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh
index e07ac6c..1ed0aa9 100755
--- a/t/t3001-ls-files-others-exclude.sh
+++ b/t/t3001-ls-files-others-exclude.sh
@@ -103,7 +103,7 @@ test_expect_success 'git ls-files --others with various exclude options.' '
test_cmp expect output
'
-test_expect_success !SANITIZE_LEAK 'restore gitignore' '
+test_expect_success 'restore gitignore' '
git checkout --ignore-skip-worktree-bits $allignores &&
rm .git/index
'
@@ -126,7 +126,7 @@ cat > expect << EOF
# three/
EOF
-test_expect_success !SANITIZE_LEAK 'git status honors core.excludesfile' \
+test_expect_success 'git status honors core.excludesfile' \
'test_cmp expect output'
test_expect_success 'trailing slash in exclude allows directory match(1)' '
diff --git a/t/t3012-ls-files-dedup.sh b/t/t3012-ls-files-dedup.sh
index 2682b1f..190e2f6 100755
--- a/t/t3012-ls-files-dedup.sh
+++ b/t/t3012-ls-files-dedup.sh
@@ -2,6 +2,7 @@
test_description='git ls-files --deduplicate test'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh
index f953996..5d871fd 100755
--- a/t/t3070-wildmatch.sh
+++ b/t/t3070-wildmatch.sh
@@ -5,11 +5,6 @@ test_description='wildmatch tests'
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
-# Disable expensive chain-lint tests; all of the tests in this script
-# are variants of a few trivial test-tool invocations, and there are a lot of
-# them.
-GIT_TEST_CHAIN_LINT_HARDER_DEFAULT=0
-
should_create_test_file() {
file=$1
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index 9723c28..5a169b6 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -201,8 +201,8 @@ test_expect_success 'git branch -M baz bam should succeed when baz is checked ou
test_expect_success 'git branch -M baz bam should add entries to .git/logs/HEAD' '
msg="Branch: renamed refs/heads/baz to refs/heads/bam" &&
- grep " 0\{40\}.*$msg$" .git/logs/HEAD &&
- grep "^0\{40\}.*$msg$" .git/logs/HEAD
+ grep " $ZERO_OID.*$msg$" .git/logs/HEAD &&
+ grep "^$ZERO_OID.*$msg$" .git/logs/HEAD
'
test_expect_success 'git branch -M should leave orphaned HEAD alone' '
@@ -268,6 +268,53 @@ test_expect_success 'git branch -M topic topic should work when main is checked
git branch -M topic topic
'
+test_expect_success 'git branch -M and -C fail on detached HEAD' '
+ git checkout HEAD^{} &&
+ test_when_finished git checkout - &&
+ echo "fatal: cannot rename the current branch while not on any." >expect &&
+ test_must_fail git branch -M must-fail 2>err &&
+ test_cmp expect err &&
+ echo "fatal: cannot copy the current branch while not on any." >expect &&
+ test_must_fail git branch -C must-fail 2>err &&
+ test_cmp expect err
+'
+
+test_expect_success 'git branch -d on orphan HEAD (merged)' '
+ test_when_finished git checkout main &&
+ git checkout --orphan orphan &&
+ test_when_finished "rm -rf .git/objects/commit-graph*" &&
+ git commit-graph write --reachable &&
+ git branch --track to-delete main &&
+ git branch -d to-delete
+'
+
+test_expect_success 'git branch -d on orphan HEAD (merged, graph)' '
+ test_when_finished git checkout main &&
+ git checkout --orphan orphan &&
+ git branch --track to-delete main &&
+ git branch -d to-delete
+'
+
+test_expect_success 'git branch -d on orphan HEAD (unmerged)' '
+ test_when_finished git checkout main &&
+ git checkout --orphan orphan &&
+ test_when_finished "git branch -D to-delete" &&
+ git branch to-delete main &&
+ test_must_fail git branch -d to-delete 2>err &&
+ grep "not fully merged" err
+'
+
+test_expect_success 'git branch -d on orphan HEAD (unmerged, graph)' '
+ test_when_finished git checkout main &&
+ git checkout --orphan orphan &&
+ test_when_finished "git branch -D to-delete" &&
+ git branch to-delete main &&
+ test_when_finished "rm -rf .git/objects/commit-graph*" &&
+ git commit-graph write --reachable &&
+ test_must_fail git branch -d to-delete 2>err &&
+ grep "not fully merged" err
+'
+
test_expect_success 'git branch -v -d t should work' '
git branch t &&
git rev-parse --verify refs/heads/t &&
@@ -306,6 +353,7 @@ test_expect_success 'deleting checked-out branch from repo that is a submodule'
git init repo1 &&
git init repo1/sub &&
test_commit -C repo1/sub x &&
+ test_config_global protocol.file.allow always &&
git -C repo1 submodule add ./sub &&
git -C repo1 commit -m "adding sub" &&
@@ -1381,6 +1429,9 @@ test_expect_success 'branch --delete --force removes dangling branch' '
'
test_expect_success 'use --edit-description' '
+ EDITOR=: git branch --edit-description &&
+ test_expect_code 1 git config branch.main.description &&
+
write_script editor <<-\EOF &&
echo "New contents" >"$1"
EOF
diff --git a/t/t3202-show-branch.sh b/t/t3202-show-branch.sh
index f2b9199..ea7cfd1 100755
--- a/t/t3202-show-branch.sh
+++ b/t/t3202-show-branch.sh
@@ -7,6 +7,28 @@ test_description='test show-branch'
# arbitrary reference time: 2009-08-30 19:20:00
GIT_TEST_DATE_NOW=1251660000; export GIT_TEST_DATE_NOW
+test_expect_success 'error descriptions on empty repository' '
+ current=$(git branch --show-current) &&
+ cat >expect <<-EOF &&
+ error: No commit on branch '\''$current'\'' yet.
+ EOF
+ test_must_fail git branch --edit-description 2>actual &&
+ test_cmp expect actual &&
+ test_must_fail git branch --edit-description $current 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'fatal descriptions on empty repository' '
+ current=$(git branch --show-current) &&
+ cat >expect <<-EOF &&
+ fatal: No commit on branch '\''$current'\'' yet.
+ EOF
+ test_must_fail git branch --set-upstream-to=non-existent 2>actual &&
+ test_cmp expect actual &&
+ test_must_fail git branch -c new-branch 2>actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'setup' '
test_commit initial &&
for i in $(test_seq 1 10)
@@ -175,4 +197,28 @@ done <<\EOF
--reflog --current
EOF
+test_expect_success 'error descriptions on non-existent branch' '
+ cat >expect <<-EOF &&
+ error: No branch named '\''non-existent'\'.'
+ EOF
+ test_must_fail git branch --edit-description non-existent 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'fatal descriptions on non-existent branch' '
+ cat >expect <<-EOF &&
+ fatal: branch '\''non-existent'\'' does not exist
+ EOF
+ test_must_fail git branch --set-upstream-to=non-existent non-existent 2>actual &&
+ test_cmp expect actual &&
+
+ cat >expect <<-EOF &&
+ fatal: No branch named '\''non-existent'\''.
+ EOF
+ test_must_fail git branch -c non-existent new-branch 2>actual &&
+ test_cmp expect actual &&
+ test_must_fail git branch -m non-existent new-branch 2>actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t3204-branch-name-interpretation.sh b/t/t3204-branch-name-interpretation.sh
index 993a6b5..793bf4d 100755
--- a/t/t3204-branch-name-interpretation.sh
+++ b/t/t3204-branch-name-interpretation.sh
@@ -133,4 +133,28 @@ test_expect_success 'checkout does not treat remote @{upstream} as a branch' '
expect_branch HEAD one
'
+test_expect_success 'edit-description via @{-1}' '
+ git checkout -b desc-branch &&
+ git checkout -b non-desc-branch &&
+ write_script editor <<-\EOF &&
+ echo "Branch description" >"$1"
+ EOF
+ EDITOR=./editor git branch --edit-description @{-1} &&
+ test_must_fail git config branch.non-desc-branch.description &&
+ git config branch.desc-branch.description >actual &&
+ printf "Branch description\n\n" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'modify branch upstream via "@{-1}" and "@{-1}@{upstream}"' '
+ git checkout -b upstream-branch &&
+ git checkout -b upstream-other -t upstream-branch &&
+ git branch --set-upstream-to upstream-other @{-1} &&
+ git config branch.upstream-branch.merge >actual &&
+ echo "refs/heads/upstream-other" >expect &&
+ test_cmp expect actual &&
+ git branch --unset-upstream @{-1}@{upstream} &&
+ test_must_fail git config branch.upstream-other.merge
+'
+
test_done
diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh
index d12e4e4..84dd0cd 100755
--- a/t/t3206-range-diff.sh
+++ b/t/t3206-range-diff.sh
@@ -162,7 +162,7 @@ test_expect_success 'A^! and A^-<n> (unmodified)' '
'
test_expect_success 'A^{/..} is not mistaken for a range' '
- test_must_fail git range-diff topic^.. topic^{/..} 2>error &&
+ test_must_fail git range-diff topic^.. topic^{/..} -- 2>error &&
test_i18ngrep "not a commit range" error
'
@@ -772,6 +772,17 @@ test_expect_success '--left-only/--right-only' '
test_cmp expect actual
'
+test_expect_success 'ranges with pathspecs' '
+ git range-diff topic...mode-only-change -- other-file >actual &&
+ test_line_count = 2 actual &&
+ topic_oid=$(git rev-parse --short topic) &&
+ mode_change_oid=$(git rev-parse --short mode-only-change^) &&
+ file_change_oid=$(git rev-parse --short mode-only-change) &&
+ grep "$mode_change_oid" actual &&
+ ! grep "$file_change_oid" actual &&
+ ! grep "$topic_oid" actual
+'
+
test_expect_success 'submodule changes are shown irrespective of diff.submodule' '
git init sub-repo &&
test_commit -C sub-repo sub-first &&
@@ -782,7 +793,7 @@ test_expect_success 'submodule changes are shown irrespective of diff.submodule'
sub_oid3=$(git -C sub-repo rev-parse HEAD) &&
git checkout -b main-sub topic &&
- git submodule add ./sub-repo sub &&
+ git -c protocol.file.allow=always submodule add ./sub-repo sub &&
git -C sub checkout --detach sub-first &&
git commit -m "add sub" sub &&
sup_oid1=$(git rev-parse --short HEAD) &&
diff --git a/t/t3207-branch-submodule.sh b/t/t3207-branch-submodule.sh
index cfde6b2..fe72b24 100755
--- a/t/t3207-branch-submodule.sh
+++ b/t/t3207-branch-submodule.sh
@@ -28,6 +28,7 @@ test_no_branch () {
}
test_expect_success 'setup superproject and submodule' '
+ git config --global protocol.file.allow always &&
mkdir test_dirs &&
(
cd test_dirs &&
diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh
index d742be8..3288aae 100755
--- a/t/t3301-notes.sh
+++ b/t/t3301-notes.sh
@@ -505,6 +505,11 @@ test_expect_success 'list notes with "git notes"' '
test_cmp expect actual
'
+test_expect_success '"git notes" without subcommand does not take arguments' '
+ test_expect_code 129 git notes HEAD^^ 2>err &&
+ grep "^error: unknown subcommand" err
+'
+
test_expect_success 'list specific note with "git notes list <object>"' '
git rev-parse refs/notes/commits:$commit_3 >expect &&
git notes list HEAD^^ >actual &&
diff --git a/t/t3305-notes-fanout.sh b/t/t3305-notes-fanout.sh
index 64a9915..1ec1fb6 100755
--- a/t/t3305-notes-fanout.sh
+++ b/t/t3305-notes-fanout.sh
@@ -9,7 +9,7 @@ path_has_fanout() {
path=$1 &&
fanout=$2 &&
after_last_slash=$(($(test_oid hexsz) - $fanout * 2)) &&
- echo $path | grep -q "^\([0-9a-f]\{2\}/\)\{$fanout\}[0-9a-f]\{$after_last_slash\}$"
+ echo $path | grep -q -E "^([0-9a-f]{2}/){$fanout}[0-9a-f]{$after_last_slash}$"
}
touched_one_note_with_fanout() {
@@ -51,7 +51,7 @@ test_expect_success 'creating many notes with git-notes' '
done
'
-test_expect_success !SANITIZE_LEAK 'many notes created correctly with git-notes' '
+test_expect_success 'many notes created correctly with git-notes' '
git log >output.raw &&
grep "^ " output.raw >output &&
i=$num_notes &&
diff --git a/t/t3307-notes-man.sh b/t/t3307-notes-man.sh
index 1aa366a..ae31650 100755
--- a/t/t3307-notes-man.sh
+++ b/t/t3307-notes-man.sh
@@ -4,6 +4,7 @@ test_description='Examples from the git-notes man page
Make sure the manual is not full of lies.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 688b01e..462cefd 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -1244,9 +1244,9 @@ test_expect_success 'short commit ID collide' '
test $colliding_id = "$(git rev-parse HEAD | cut -c 1-4)" &&
grep "^pick $colliding_id " \
.git/rebase-merge/git-rebase-todo.tmp &&
- grep "^pick [0-9a-f]\{$hexsz\}" \
+ grep -E "^pick [0-9a-f]{$hexsz}" \
.git/rebase-merge/git-rebase-todo &&
- grep "^pick [0-9a-f]\{$hexsz\}" \
+ grep -E "^pick [0-9a-f]{$hexsz}" \
.git/rebase-merge/git-rebase-todo.backup &&
git rebase --continue
) &&
@@ -1261,7 +1261,7 @@ test_expect_success 'respect core.abbrev' '
set_cat_todo_editor &&
test_must_fail git rebase -i HEAD~4 >todo-list
) &&
- test 4 = $(grep -c "pick [0-9a-f]\{12,\}" todo-list)
+ test 4 = $(grep -c -E "pick [0-9a-f]{12,}" todo-list)
'
test_expect_success 'todo count' '
@@ -1964,6 +1964,113 @@ test_expect_success 'respect user edits to update-ref steps' '
test_cmp_rev HEAD refs/heads/no-conflict-branch
'
+test_expect_success '--update-refs: all update-ref lines removed' '
+ git checkout -b test-refs-not-removed no-conflict-branch &&
+ git branch -f base HEAD~4 &&
+ git branch -f first HEAD~3 &&
+ git branch -f second HEAD~3 &&
+ git branch -f third HEAD~1 &&
+ git branch -f tip &&
+
+ test_commit test-refs-not-removed &&
+ git commit --amend --fixup first &&
+
+ git rev-parse first second third tip no-conflict-branch >expect-oids &&
+
+ (
+ set_cat_todo_editor &&
+ test_must_fail git rebase -i --update-refs base >todo.raw &&
+ sed -e "/^update-ref/d" <todo.raw >todo
+ ) &&
+ (
+ set_replace_editor todo &&
+ git rebase -i --update-refs base
+ ) &&
+
+ # Ensure refs are not deleted and their OIDs have not changed
+ git rev-parse first second third tip no-conflict-branch >actual-oids &&
+ test_cmp expect-oids actual-oids
+'
+
+test_expect_success '--update-refs: all update-ref lines removed, then some re-added' '
+ git checkout -b test-refs-not-removed2 no-conflict-branch &&
+ git branch -f base HEAD~4 &&
+ git branch -f first HEAD~3 &&
+ git branch -f second HEAD~3 &&
+ git branch -f third HEAD~1 &&
+ git branch -f tip &&
+
+ test_commit test-refs-not-removed2 &&
+ git commit --amend --fixup first &&
+
+ git rev-parse first second third >expect-oids &&
+
+ (
+ set_cat_todo_editor &&
+ test_must_fail git rebase -i \
+ --autosquash --update-refs \
+ base >todo.raw &&
+ sed -e "/^update-ref/d" <todo.raw >todo
+ ) &&
+
+ # Add a break to the end of the todo so we can edit later
+ echo "break" >>todo &&
+
+ (
+ set_replace_editor todo &&
+ git rebase -i --autosquash --update-refs base &&
+ echo "update-ref refs/heads/tip" >todo &&
+ git rebase --edit-todo &&
+ git rebase --continue
+ ) &&
+
+ # Ensure first/second/third are unchanged, but tip is updated
+ git rev-parse first second third >actual-oids &&
+ test_cmp expect-oids actual-oids &&
+ test_cmp_rev HEAD tip
+'
+
+test_expect_success '--update-refs: --edit-todo with no update-ref lines' '
+ git checkout -b test-refs-not-removed3 no-conflict-branch &&
+ git branch -f base HEAD~4 &&
+ git branch -f first HEAD~3 &&
+ git branch -f second HEAD~3 &&
+ git branch -f third HEAD~1 &&
+ git branch -f tip &&
+
+ test_commit test-refs-not-removed3 &&
+ git commit --amend --fixup first &&
+
+ git rev-parse first second third tip no-conflict-branch >expect-oids &&
+
+ (
+ set_cat_todo_editor &&
+ test_must_fail git rebase -i \
+ --autosquash --update-refs \
+ base >todo.raw &&
+ sed -e "/^update-ref/d" <todo.raw >todo
+ ) &&
+
+ # Add a break to the beginning of the todo so we can resume with no
+ # update-ref lines
+ echo "break" >todo.new &&
+ cat todo >>todo.new &&
+
+ (
+ set_replace_editor todo.new &&
+ git rebase -i --autosquash --update-refs base &&
+
+ # Make no changes when editing so update-refs is still empty
+ cat todo >todo.new &&
+ git rebase --edit-todo &&
+ git rebase --continue
+ ) &&
+
+ # Ensure refs are not deleted and their OIDs have not changed
+ git rev-parse first second third tip no-conflict-branch >actual-oids &&
+ test_cmp expect-oids actual-oids
+'
+
test_expect_success '--update-refs: check failed ref update' '
git checkout -B update-refs-error no-conflict-branch &&
git branch -f base HEAD~4 &&
diff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh
index d17b450..ceca160 100755
--- a/t/t3406-rebase-message.sh
+++ b/t/t3406-rebase-message.sh
@@ -10,10 +10,16 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
test_expect_success 'setup' '
test_commit O fileO &&
test_commit X fileX &&
+ git branch fast-forward &&
test_commit A fileA &&
test_commit B fileB &&
test_commit Y fileY &&
+ git checkout -b conflicts O &&
+ test_commit P &&
+ test_commit conflict-X fileX &&
+ test_commit Q &&
+
git checkout -b topic O &&
git cherry-pick A B &&
test_commit Z fileZ &&
@@ -79,54 +85,165 @@ test_expect_success 'error out early upon -C<n> or --whitespace=<bad>' '
test_i18ngrep "Invalid whitespace option" err
'
-test_expect_success 'GIT_REFLOG_ACTION' '
- git checkout start &&
- test_commit reflog-onto &&
- git checkout -b reflog-topic start &&
- test_commit reflog-to-rebase &&
+write_reflog_expect () {
+ if test $mode = --apply
+ then
+ sed 's/(continue)/(pick)/'
+ else
+ cat
+ fi >expect
+}
- git rebase reflog-onto &&
- git log -g --format=%gs -3 >actual &&
- cat >expect <<-\EOF &&
- rebase (finish): returning to refs/heads/reflog-topic
- rebase (pick): reflog-to-rebase
- rebase (start): checkout reflog-onto
+test_reflog () {
+ mode=$1
+ reflog_action="$2"
+
+ test_expect_success "rebase $mode reflog${reflog_action:+ GIT_REFLOG_ACTION=$reflog_action}" '
+ git checkout conflicts &&
+ test_when_finished "git reset --hard Q" &&
+
+ (
+ if test -n "$reflog_action"
+ then
+ GIT_REFLOG_ACTION="$reflog_action" &&
+ export GIT_REFLOG_ACTION
+ fi &&
+ test_must_fail git rebase $mode main &&
+ echo resolved >fileX &&
+ git add fileX &&
+ git rebase --continue
+ ) &&
+
+ git log -g --format=%gs -5 >actual &&
+ write_reflog_expect <<-EOF &&
+ ${reflog_action:-rebase} (finish): returning to refs/heads/conflicts
+ ${reflog_action:-rebase} (pick): Q
+ ${reflog_action:-rebase} (continue): conflict-X
+ ${reflog_action:-rebase} (pick): P
+ ${reflog_action:-rebase} (start): checkout main
EOF
test_cmp expect actual &&
- git checkout -b reflog-prefix reflog-to-rebase &&
- GIT_REFLOG_ACTION=change-the-reflog git rebase reflog-onto &&
- git log -g --format=%gs -3 >actual &&
- cat >expect <<-\EOF &&
- change-the-reflog (finish): returning to refs/heads/reflog-prefix
- change-the-reflog (pick): reflog-to-rebase
- change-the-reflog (start): checkout reflog-onto
+ git log -g --format=%gs -1 conflicts >actual &&
+ write_reflog_expect <<-EOF &&
+ ${reflog_action:-rebase} (finish): refs/heads/conflicts onto $(git rev-parse main)
+ EOF
+ test_cmp expect actual &&
+
+ # check there is only one new entry in the branch reflog
+ test_cmp_rev conflicts@{1} Q
+ '
+
+ test_expect_success "rebase $mode fast-forward reflog${reflog_action:+ GIT_REFLOG_ACTION=$reflog_action}" '
+ git checkout fast-forward &&
+ test_when_finished "git reset --hard X" &&
+
+ (
+ if test -n "$reflog_action"
+ then
+ GIT_REFLOG_ACTION="$reflog_action" &&
+ export GIT_REFLOG_ACTION
+ fi &&
+ git rebase $mode main
+ ) &&
+
+ git log -g --format=%gs -2 >actual &&
+ write_reflog_expect <<-EOF &&
+ ${reflog_action:-rebase} (finish): returning to refs/heads/fast-forward
+ ${reflog_action:-rebase} (start): checkout main
+ EOF
+ test_cmp expect actual &&
+
+ git log -g --format=%gs -1 fast-forward >actual &&
+ write_reflog_expect <<-EOF &&
+ ${reflog_action:-rebase} (finish): refs/heads/fast-forward onto $(git rev-parse main)
+ EOF
+ test_cmp expect actual &&
+
+ # check there is only one new entry in the branch reflog
+ test_cmp_rev fast-forward@{1} X
+ '
+
+ test_expect_success "rebase $mode --skip reflog${reflog_action:+ GIT_REFLOG_ACTION=$reflog_action}" '
+ git checkout conflicts &&
+ test_when_finished "git reset --hard Q" &&
+
+ (
+ if test -n "$reflog_action"
+ then
+ GIT_REFLOG_ACTION="$reflog_action" &&
+ export GIT_REFLOG_ACTION
+ fi &&
+ test_must_fail git rebase $mode main &&
+ git rebase --skip
+ ) &&
+
+ git log -g --format=%gs -4 >actual &&
+ write_reflog_expect <<-EOF &&
+ ${reflog_action:-rebase} (finish): returning to refs/heads/conflicts
+ ${reflog_action:-rebase} (pick): Q
+ ${reflog_action:-rebase} (pick): P
+ ${reflog_action:-rebase} (start): checkout main
EOF
test_cmp expect actual
-'
+ '
-test_expect_success 'rebase --apply reflog' '
- git checkout -b reflog-apply start &&
- old_head_reflog="$(git log -g --format=%gs -1 HEAD)" &&
+ test_expect_success "rebase $mode --abort reflog${reflog_action:+ GIT_REFLOG_ACTION=$reflog_action}" '
+ git checkout conflicts &&
+ test_when_finished "git reset --hard Q" &&
- git rebase --apply Y &&
+ git log -g -1 conflicts >branch-expect &&
+ (
+ if test -n "$reflog_action"
+ then
+ GIT_REFLOG_ACTION="$reflog_action" &&
+ export GIT_REFLOG_ACTION
+ fi &&
+ test_must_fail git rebase $mode main &&
+ git rebase --abort
+ ) &&
- git log -g --format=%gs -4 HEAD >actual &&
- cat >expect <<-EOF &&
- rebase finished: returning to refs/heads/reflog-apply
- rebase: Z
- rebase: checkout Y
- $old_head_reflog
+ git log -g --format=%gs -3 >actual &&
+ write_reflog_expect <<-EOF &&
+ ${reflog_action:-rebase} (abort): returning to refs/heads/conflicts
+ ${reflog_action:-rebase} (pick): P
+ ${reflog_action:-rebase} (start): checkout main
EOF
test_cmp expect actual &&
- git log -g --format=%gs -2 reflog-apply >actual &&
- cat >expect <<-EOF &&
- rebase finished: refs/heads/reflog-apply onto $(git rev-parse Y)
- branch: Created from start
+ # check branch reflog is unchanged
+ git log -g -1 conflicts >branch-actual &&
+ test_cmp branch-expect branch-actual
+ '
+
+ test_expect_success "rebase $mode --abort detached HEAD reflog${reflog_action:+ GIT_REFLOG_ACTION=$reflog_action}" '
+ git checkout Q &&
+ test_when_finished "git reset --hard Q" &&
+
+ (
+ if test -n "$reflog_action"
+ then
+ GIT_REFLOG_ACTION="$reflog_action" &&
+ export GIT_REFLOG_ACTION
+ fi &&
+ test_must_fail git rebase $mode main &&
+ git rebase --abort
+ ) &&
+
+ git log -g --format=%gs -3 >actual &&
+ write_reflog_expect <<-EOF &&
+ ${reflog_action:-rebase} (abort): returning to $(git rev-parse Q)
+ ${reflog_action:-rebase} (pick): P
+ ${reflog_action:-rebase} (start): checkout main
EOF
test_cmp expect actual
-'
+ '
+}
+
+test_reflog --merge
+test_reflog --merge my-reflog-action
+test_reflog --apply
+test_reflog --apply my-reflog-action
test_expect_success 'rebase -i onto unrelated history' '
git init unrelated &&
diff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh
index 78c2749..a364530 100755
--- a/t/t3415-rebase-autosquash.sh
+++ b/t/t3415-rebase-autosquash.sh
@@ -232,6 +232,19 @@ test_expect_success 'auto squash that matches longer sha1' '
test_line_count = 1 actual
'
+test_expect_success 'auto squash of fixup commit that matches branch name which points back to fixup commit' '
+ git reset --hard base &&
+ git commit --allow-empty -m "fixup! self-cycle" &&
+ git branch self-cycle &&
+ GIT_SEQUENCE_EDITOR="cat >tmp" git rebase --autosquash -i HEAD^^ &&
+ sed -ne "/^[^#]/{s/[0-9a-f]\{7,\}/HASH/g;p;}" tmp >actual &&
+ cat <<-EOF >expect &&
+ pick HASH second commit
+ pick HASH fixup! self-cycle # empty
+ EOF
+ test_cmp expect actual
+'
+
test_auto_commit_flags () {
git reset --hard base &&
echo 1 >file1 &&
diff --git a/t/t3416-rebase-onto-threedots.sh b/t/t3416-rebase-onto-threedots.sh
index 3e04802..ea501f2 100755
--- a/t/t3416-rebase-onto-threedots.sh
+++ b/t/t3416-rebase-onto-threedots.sh
@@ -79,8 +79,10 @@ test_expect_success 'rebase -i --onto main...topic' '
git reset --hard &&
git checkout topic &&
git reset --hard G &&
- set_fake_editor &&
- EXPECT_COUNT=1 git rebase -i --onto main...topic F &&
+ (
+ set_fake_editor &&
+ EXPECT_COUNT=1 git rebase -i --onto main...topic F
+ ) &&
git rev-parse HEAD^1 >actual &&
git rev-parse C^0 >expect &&
test_cmp expect actual
@@ -90,20 +92,22 @@ test_expect_success 'rebase -i --onto main...' '
git reset --hard &&
git checkout topic &&
git reset --hard G &&
- set_fake_editor &&
- EXPECT_COUNT=1 git rebase -i --onto main... F &&
+ (
+ set_fake_editor &&
+ EXPECT_COUNT=1 git rebase -i --onto main... F
+ ) &&
git rev-parse HEAD^1 >actual &&
git rev-parse C^0 >expect &&
test_cmp expect actual
'
-test_expect_success 'rebase -i --onto main...side' '
+test_expect_success 'rebase --onto main...side requires a single merge-base' '
git reset --hard &&
git checkout side &&
git reset --hard K &&
- set_fake_editor &&
- test_must_fail git rebase -i --onto main...side J
+ test_must_fail git rebase -i --onto main...side J 2>err &&
+ grep "need exactly one merge base" err
'
test_expect_success 'rebase --keep-base --onto incompatible' '
@@ -156,8 +160,10 @@ test_expect_success 'rebase -i --keep-base main from topic' '
git checkout topic &&
git reset --hard G &&
- set_fake_editor &&
- EXPECT_COUNT=2 git rebase -i --keep-base main &&
+ (
+ set_fake_editor &&
+ EXPECT_COUNT=2 git rebase -i --keep-base main
+ ) &&
git rev-parse C >base.expect &&
git merge-base main HEAD >base.actual &&
test_cmp base.expect base.actual &&
@@ -171,8 +177,10 @@ test_expect_success 'rebase -i --keep-base main topic from main' '
git checkout main &&
git branch -f topic G &&
- set_fake_editor &&
- EXPECT_COUNT=2 git rebase -i --keep-base main topic &&
+ (
+ set_fake_editor &&
+ EXPECT_COUNT=2 git rebase -i --keep-base main topic
+ ) &&
git rev-parse C >base.expect &&
git merge-base main HEAD >base.actual &&
test_cmp base.expect base.actual &&
@@ -182,13 +190,39 @@ test_expect_success 'rebase -i --keep-base main topic from main' '
test_cmp expect actual
'
-test_expect_success 'rebase -i --keep-base main from side' '
+test_expect_success 'rebase --keep-base requires a single merge base' '
git reset --hard &&
git checkout side &&
git reset --hard K &&
- set_fake_editor &&
- test_must_fail git rebase -i --keep-base main
+ test_must_fail git rebase -i --keep-base main 2>err &&
+ grep "need exactly one merge base with branch" err
+'
+
+test_expect_success 'rebase --keep-base keeps cherry picks' '
+ git checkout -f -B main E &&
+ git cherry-pick F &&
+ (
+ set_fake_editor &&
+ EXPECT_COUNT=2 git rebase -i --keep-base HEAD G
+ ) &&
+ test_cmp_rev HEAD G
+'
+
+test_expect_success 'rebase --keep-base --no-reapply-cherry-picks' '
+ git checkout -f -B main E &&
+ git cherry-pick F &&
+ (
+ set_fake_editor &&
+ EXPECT_COUNT=1 git rebase -i --keep-base \
+ --no-reapply-cherry-picks HEAD G
+ ) &&
+ test_cmp_rev HEAD^ C
+'
+
+# This must be the last test in this file
+test_expect_success '$EDITOR and friends are unchanged' '
+ test_editor_unchanged
'
test_done
diff --git a/t/t3419-rebase-patch-id.sh b/t/t3419-rebase-patch-id.sh
index 295040f..7181f17 100755
--- a/t/t3419-rebase-patch-id.sh
+++ b/t/t3419-rebase-patch-id.sh
@@ -43,15 +43,26 @@ test_expect_success 'setup: 500 lines' '
git add newfile &&
git commit -q -m "add small file" &&
- git cherry-pick main >/dev/null 2>&1
-'
+ git cherry-pick main >/dev/null 2>&1 &&
+
+ git branch -f squashed main &&
+ git checkout -q -f squashed &&
+ git reset -q --soft HEAD~2 &&
+ git commit -q -m squashed &&
+
+ git branch -f mode main &&
+ git checkout -q -f mode &&
+ test_chmod +x file &&
+ git commit -q -a --amend &&
-test_expect_success 'setup attributes' '
- echo "file binary" >.gitattributes
+ git branch -f modeother other &&
+ git checkout -q -f modeother &&
+ test_chmod +x file &&
+ git commit -q -a --amend
'
test_expect_success 'detect upstream patch' '
- git checkout -q main &&
+ git checkout -q main^{} &&
scramble file &&
git add file &&
git commit -q -m "change big file again" &&
@@ -61,14 +72,46 @@ test_expect_success 'detect upstream patch' '
test_must_be_empty revs
'
+test_expect_success 'detect upstream patch binary' '
+ echo "file binary" >.gitattributes &&
+ git checkout -q other^{} &&
+ git rebase main &&
+ git rev-list main...HEAD~ >revs &&
+ test_must_be_empty revs &&
+ test_when_finished "rm .gitattributes"
+'
+
+test_expect_success 'detect upstream patch modechange' '
+ git checkout -q modeother^{} &&
+ git rebase mode &&
+ git rev-list mode...HEAD~ >revs &&
+ test_must_be_empty revs
+'
+
test_expect_success 'do not drop patch' '
- git branch -f squashed main &&
- git checkout -q -f squashed &&
- git reset -q --soft HEAD~2 &&
- git commit -q -m squashed &&
git checkout -q other^{} &&
test_must_fail git rebase squashed &&
- git rebase --quit
+ test_when_finished "git rebase --abort"
+'
+
+test_expect_success 'do not drop patch binary' '
+ echo "file binary" >.gitattributes &&
+ git checkout -q other^{} &&
+ test_must_fail git rebase squashed &&
+ test_when_finished "git rebase --abort" &&
+ test_when_finished "rm .gitattributes"
+'
+
+test_expect_success 'do not drop patch modechange' '
+ git checkout -q modeother^{} &&
+ git rebase other &&
+ cat >expected <<-\EOF &&
+ diff --git a/file b/file
+ old mode 100644
+ new mode 100755
+ EOF
+ git diff HEAD~ >modediff &&
+ test_cmp expected modediff
'
test_done
diff --git a/t/t3420-rebase-autostash.sh b/t/t3420-rebase-autostash.sh
index 43fcb68..693934e 100755
--- a/t/t3420-rebase-autostash.sh
+++ b/t/t3420-rebase-autostash.sh
@@ -310,7 +310,7 @@ test_expect_success 'autostash is saved on editor failure with conflict' '
test_expect_success 'autostash with dirty submodules' '
test_when_finished "git reset --hard && git checkout main" &&
git checkout -b with-submodule &&
- git submodule add ./ sub &&
+ git -c protocol.file.allow=always submodule add ./ sub &&
test_tick &&
git commit -m add-submodule &&
echo changed >sub/file0 &&
diff --git a/t/t3426-rebase-submodule.sh b/t/t3426-rebase-submodule.sh
index 7a9f112..ba069dc 100755
--- a/t/t3426-rebase-submodule.sh
+++ b/t/t3426-rebase-submodule.sh
@@ -48,7 +48,8 @@ test_expect_success 'rebase interactive ignores modified submodules' '
git init sub &&
git -C sub commit --allow-empty -m "Initial commit" &&
git init super &&
- git -C super submodule add ../sub &&
+ git -c protocol.file.allow=always \
+ -C super submodule add ../sub &&
git -C super config submodule.sub.ignore dirty &&
>super/foo &&
git -C super add foo &&
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index f351701..fa2a06c 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -138,6 +138,23 @@ test_expect_success '`reset` refuses to overwrite untracked files' '
git rebase --abort
'
+test_expect_success '`reset` rejects trees' '
+ test_when_finished "test_might_fail git rebase --abort" &&
+ test_must_fail env GIT_SEQUENCE_EDITOR="echo reset A^{tree} >" \
+ git rebase -i B C >out 2>err &&
+ grep "object .* is a tree" err &&
+ test_must_be_empty out
+'
+
+test_expect_success '`reset` only looks for labels under refs/rewritten/' '
+ test_when_finished "test_might_fail git rebase --abort" &&
+ git branch refs/rewritten/my-label A &&
+ test_must_fail env GIT_SEQUENCE_EDITOR="echo reset my-label >" \
+ git rebase -i B C >out 2>err &&
+ grep "could not resolve ${SQ}my-label${SQ}" err &&
+ test_must_be_empty out
+'
+
test_expect_success 'failed `merge -C` writes patch (may be rescheduled, too)' '
test_when_finished "test_might_fail git rebase --abort" &&
git checkout -b conflicting-merge A &&
diff --git a/t/t3431-rebase-fork-point.sh b/t/t3431-rebase-fork-point.sh
index 1d0b153..70e8136 100755
--- a/t/t3431-rebase-fork-point.sh
+++ b/t/t3431-rebase-fork-point.sh
@@ -50,7 +50,7 @@ test_rebase () {
test_rebase 'G F E D B A'
test_rebase 'G F D B A' --onto D
-test_rebase 'G F B A' --keep-base
+test_rebase 'G F C B A' --keep-base
test_rebase 'G F C E D B A' --no-fork-point
test_rebase 'G F C D B A' --no-fork-point --onto D
test_rebase 'G F C B A' --no-fork-point --keep-base
diff --git a/t/t3435-rebase-gpg-sign.sh b/t/t3435-rebase-gpg-sign.sh
index 5f8ba2c..6aa2aeb 100755
--- a/t/t3435-rebase-gpg-sign.sh
+++ b/t/t3435-rebase-gpg-sign.sh
@@ -64,14 +64,6 @@ test_rebase_gpg_sign ! true -i --no-gpg-sign
test_rebase_gpg_sign ! true -i --gpg-sign --no-gpg-sign
test_rebase_gpg_sign false -i --no-gpg-sign --gpg-sign
-test_expect_failure 'rebase -p --no-gpg-sign override commit.gpgsign' '
- test_when_finished "git clean -f" &&
- git reset --hard merged &&
- git config commit.gpgsign true &&
- git rebase -p --no-gpg-sign --onto=one fork-point main &&
- test_must_fail git verify-commit HEAD
-'
-
test_expect_success 'rebase -r, merge strategy, --gpg-sign will sign commit' '
git reset --hard merged &&
test_unconfig commit.gpgsign &&
diff --git a/t/t3438-rebase-broken-files.sh b/t/t3438-rebase-broken-files.sh
new file mode 100755
index 0000000..b92a3ce
--- /dev/null
+++ b/t/t3438-rebase-broken-files.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+
+test_description='rebase behavior when on-disk files are broken'
+. ./test-lib.sh
+
+test_expect_success 'set up conflicting branches' '
+ test_commit base file &&
+ git checkout -b branch1 &&
+ test_commit one file &&
+ git checkout -b branch2 HEAD^ &&
+ test_commit two file
+'
+
+create_conflict () {
+ test_when_finished "git rebase --abort" &&
+ git checkout -B tmp branch2 &&
+ test_must_fail git rebase branch1
+}
+
+check_resolve_fails () {
+ echo resolved >file &&
+ git add file &&
+ test_must_fail git rebase --continue
+}
+
+for item in NAME EMAIL DATE
+do
+ test_expect_success "detect missing GIT_AUTHOR_$item" '
+ create_conflict &&
+
+ grep -v $item .git/rebase-merge/author-script >tmp &&
+ mv tmp .git/rebase-merge/author-script &&
+
+ check_resolve_fails
+ '
+done
+
+for item in NAME EMAIL DATE
+do
+ test_expect_success "detect duplicate GIT_AUTHOR_$item" '
+ create_conflict &&
+
+ grep -i $item .git/rebase-merge/author-script >tmp &&
+ cat tmp >>.git/rebase-merge/author-script &&
+
+ check_resolve_fails
+ '
+done
+
+test_expect_success 'unknown key in author-script' '
+ create_conflict &&
+
+ echo "GIT_AUTHOR_BOGUS=${SQ}whatever${SQ}" \
+ >>.git/rebase-merge/author-script &&
+
+ check_resolve_fails
+'
+
+test_done
diff --git a/t/t3512-cherry-pick-submodule.sh b/t/t3512-cherry-pick-submodule.sh
index c657840..f22d1dd 100755
--- a/t/t3512-cherry-pick-submodule.sh
+++ b/t/t3512-cherry-pick-submodule.sh
@@ -16,6 +16,8 @@ fi
test_submodule_switch "cherry-pick"
test_expect_success 'unrelated submodule/file conflict is ignored' '
+ test_config_global protocol.file.allow always &&
+
test_create_repo sub &&
touch sub/file &&
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index e74a318..0e8afe4 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -333,7 +333,7 @@ test_expect_success 'rm removes empty submodules from work tree' '
test_expect_success 'rm removes removed submodule from index and .gitmodules' '
git reset --hard &&
- git submodule update &&
+ git -c protocol.file.allow=always submodule update &&
rm -rf submod &&
git rm submod &&
git status -s -uno --ignore-submodules=none >actual &&
@@ -639,6 +639,7 @@ cat >expect.deepmodified <<EOF
EOF
test_expect_success 'setup subsubmodule' '
+ test_config_global protocol.file.allow always &&
git reset --hard &&
git submodule update &&
(
diff --git a/t/t3700-add.sh b/t/t3700-add.sh
index 8689b48..51afbd7 100755
--- a/t/t3700-add.sh
+++ b/t/t3700-add.sh
@@ -291,7 +291,7 @@ test_expect_success BSLASHPSPEC "git add 'fo\\[ou\\]bar' ignores foobar" '
git reset --hard &&
touch fo\[ou\]bar foobar &&
git add '\''fo\[ou\]bar'\'' &&
- git ls-files fo\[ou\]bar | fgrep fo\[ou\]bar &&
+ git ls-files fo\[ou\]bar | grep -F fo\[ou\]bar &&
! ( git ls-files foobar | grep foobar )
'
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index b354fb3..5841f28 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -7,9 +7,9 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
-if ! test_have_prereq PERL
+if test_have_prereq !ADD_I_USE_BUILTIN,!PERL
then
- skip_all='skipping add -i tests, perl not available'
+ skip_all='skipping add -i (scripted) tests, perl not available'
test_done
fi
@@ -761,9 +761,33 @@ test_expect_success 'detect bogus diffFilter output' '
git reset --hard &&
echo content >test &&
- test_config interactive.diffFilter "sed 1d" &&
+ test_config interactive.diffFilter "sed 6d" &&
printf y >y &&
- force_color test_must_fail git add -p <y
+ force_color test_must_fail git add -p <y >output 2>&1 &&
+ grep "mismatched output" output
+'
+
+test_expect_success 'handle iffy colored hunk headers' '
+ git reset --hard &&
+
+ echo content >test &&
+ printf n >n &&
+ force_color git -c interactive.diffFilter="sed s/.*@@.*/XX/" \
+ add -p >output 2>&1 <n &&
+ grep "^XX$" output
+'
+
+test_expect_success 'handle very large filtered diff' '
+ git reset --hard &&
+ # The specific number here is not important, but it must
+ # be large enough that the output of "git diff --color"
+ # fills up the pipe buffer. 10,000 results in ~200k of
+ # colored output.
+ test_seq 10000 >test &&
+ test_config interactive.diffFilter cat &&
+ printf y >y &&
+ force_color git add -p >output 2>&1 <y &&
+ git diff-files --exit-code -- test
'
test_expect_success 'diff.algorithm is passed to `git diff-files`' '
@@ -931,6 +955,18 @@ test_expect_success 'status ignores dirty submodules (except HEAD)' '
! grep dirty-otherwise output
'
+test_expect_success 'handle submodules' '
+ echo 123 >>for-submodules/dirty-otherwise/initial.t &&
+
+ force_color git -C for-submodules add -p dirty-otherwise >output 2>&1 &&
+ grep "No changes" output &&
+
+ force_color git -C for-submodules add -p dirty-head >output 2>&1 <y &&
+ git -C for-submodules ls-files --stage dirty-head >actual &&
+ rev="$(git -C for-submodules/dirty-head rev-parse HEAD)" &&
+ grep "$rev" actual
+'
+
test_expect_success 'set up pathological context' '
git reset --hard &&
test_write_lines a a a a a a a a a a a >a &&
diff --git a/t/t3702-add-edit.sh b/t/t3702-add-edit.sh
index a1801a8..82bfb2f 100755
--- a/t/t3702-add-edit.sh
+++ b/t/t3702-add-edit.sh
@@ -100,7 +100,7 @@ EOF
echo "#!$SHELL_PATH" >fake-editor.sh
cat >> fake-editor.sh <<\EOF
-egrep -v '^index' "$1" >orig-patch &&
+grep -E -v '^index' "$1" >orig-patch &&
mv -f patch "$1"
EOF
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 2a4c3fd..376cc8f 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -25,7 +25,7 @@ test_expect_success 'usage on main command -h emits a summary of subcommands' '
grep -F "or: git stash show" usage
'
-test_expect_failure 'usage for subcommands should emit subcommand usage' '
+test_expect_success 'usage for subcommands should emit subcommand usage' '
test_expect_code 129 git stash push -h >usage &&
grep -F "usage: git stash [push" usage
'
diff --git a/t/t3906-stash-submodule.sh b/t/t3906-stash-submodule.sh
index a52e53d..0f7348e 100755
--- a/t/t3906-stash-submodule.sh
+++ b/t/t3906-stash-submodule.sh
@@ -36,7 +36,7 @@ setup_basic () {
git init main &&
(
cd main &&
- git submodule add ../sub &&
+ git -c protocol.file.allow=always submodule add ../sub &&
test_commit main_file
)
}
diff --git a/t/t3920-crlf-messages.sh b/t/t3920-crlf-messages.sh
index 0276edb..4c661d4 100755
--- a/t/t3920-crlf-messages.sh
+++ b/t/t3920-crlf-messages.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='Test ref-filter and pretty APIs for commit and tag messages using CRLF'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
LIB_CRLF_BRANCHES=""
diff --git a/t/t4012-diff-binary.sh b/t/t4012-diff-binary.sh
index c509143..c64d9d2 100755
--- a/t/t4012-diff-binary.sh
+++ b/t/t4012-diff-binary.sh
@@ -113,20 +113,20 @@ test_expect_success 'diff --no-index with binary creation' '
'
cat >expect <<EOF
- binfile | Bin 0 -> 1026 bytes
- textfile | 10000 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ binfilë | Bin 0 -> 1026 bytes
+ tëxtfilë | 10000 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
EOF
test_expect_success 'diff --stat with binary files and big change count' '
- printf "\01\00%1024d" 1 >binfile &&
- git add binfile &&
+ printf "\01\00%1024d" 1 >binfilë &&
+ git add binfilë &&
i=0 &&
while test $i -lt 10000; do
echo $i &&
i=$(($i + 1)) || return 1
- done >textfile &&
- git add textfile &&
- git diff --cached --stat binfile textfile >output &&
+ done >tëxtfilë &&
+ git add tëxtfilë &&
+ git -c core.quotepath=false diff --cached --stat binfilë tëxtfilë >output &&
grep " | " output >actual &&
test_cmp expect actual
'
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index 056e922..dfcf3a0 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -352,6 +352,8 @@ log -GF -p --pickaxe-all master
log -IA -IB -I1 -I2 -p master
log --decorate --all
log --decorate=full --all
+log --decorate --clear-decorations --all
+log --decorate=full --clear-decorations --all
rev-list --parents HEAD
rev-list --children HEAD
diff --git a/t/t4013/diff.log_--decorate=full_--all b/t/t4013/diff.log_--decorate=full_--all
index 3f9b872..6b0b334 100644
--- a/t/t4013/diff.log_--decorate=full_--all
+++ b/t/t4013/diff.log_--decorate=full_--all
@@ -20,7 +20,7 @@ Date: Mon Jun 26 00:06:00 2006 +0000
Rearranged lines in dir/sub
-commit cbacedd14cb8b89255a2c02b59e77a2e9a8021a0 (refs/notes/commits)
+commit cbacedd14cb8b89255a2c02b59e77a2e9a8021a0
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:06:00 2006 +0000
diff --git a/t/t4013/diff.log_--decorate=full_--clear-decorations_--all b/t/t4013/diff.log_--decorate=full_--clear-decorations_--all
new file mode 100644
index 0000000..1c030a6
--- /dev/null
+++ b/t/t4013/diff.log_--decorate=full_--clear-decorations_--all
@@ -0,0 +1,61 @@
+$ git log --decorate=full --clear-decorations --all
+commit b7e0bc69303b488b47deca799a7d723971dfa6cd (refs/heads/mode)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ update mode
+
+commit a6f364368ca320bc5a92e18912e16fa6b3dff598 (refs/heads/note)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ update mode (file2)
+
+Notes:
+ note
+
+commit cd4e72fd96faed3f0ba949dc42967430374e2290 (refs/heads/rearrange)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ Rearranged lines in dir/sub
+
+commit cbacedd14cb8b89255a2c02b59e77a2e9a8021a0 (refs/notes/commits)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ Notes added by 'git notes add'
+
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> refs/heads/master)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a (refs/heads/side)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a (refs/heads/initial)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+$
diff --git a/t/t4013/diff.log_--decorate=full_--decorate-all_--all b/t/t4013/diff.log_--decorate=full_--decorate-all_--all
new file mode 100644
index 0000000..d6e7928
--- /dev/null
+++ b/t/t4013/diff.log_--decorate=full_--decorate-all_--all
@@ -0,0 +1,61 @@
+$ git log --decorate=full --decorate-all --all
+commit b7e0bc69303b488b47deca799a7d723971dfa6cd (refs/heads/mode)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ update mode
+
+commit a6f364368ca320bc5a92e18912e16fa6b3dff598 (refs/heads/note)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ update mode (file2)
+
+Notes:
+ note
+
+commit cd4e72fd96faed3f0ba949dc42967430374e2290 (refs/heads/rearrange)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ Rearranged lines in dir/sub
+
+commit cbacedd14cb8b89255a2c02b59e77a2e9a8021a0 (refs/notes/commits)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ Notes added by 'git notes add'
+
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> refs/heads/master)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a (refs/heads/side)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a (refs/heads/initial)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+$
diff --git a/t/t4013/diff.log_--decorate_--all b/t/t4013/diff.log_--decorate_--all
index f5e20e1..c7df1f5 100644
--- a/t/t4013/diff.log_--decorate_--all
+++ b/t/t4013/diff.log_--decorate_--all
@@ -20,7 +20,7 @@ Date: Mon Jun 26 00:06:00 2006 +0000
Rearranged lines in dir/sub
-commit cbacedd14cb8b89255a2c02b59e77a2e9a8021a0 (refs/notes/commits)
+commit cbacedd14cb8b89255a2c02b59e77a2e9a8021a0
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:06:00 2006 +0000
diff --git a/t/t4013/diff.log_--decorate_--clear-decorations_--all b/t/t4013/diff.log_--decorate_--clear-decorations_--all
new file mode 100644
index 0000000..88be82c
--- /dev/null
+++ b/t/t4013/diff.log_--decorate_--clear-decorations_--all
@@ -0,0 +1,61 @@
+$ git log --decorate --clear-decorations --all
+commit b7e0bc69303b488b47deca799a7d723971dfa6cd (mode)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ update mode
+
+commit a6f364368ca320bc5a92e18912e16fa6b3dff598 (note)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ update mode (file2)
+
+Notes:
+ note
+
+commit cd4e72fd96faed3f0ba949dc42967430374e2290 (rearrange)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ Rearranged lines in dir/sub
+
+commit cbacedd14cb8b89255a2c02b59e77a2e9a8021a0 (refs/notes/commits)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ Notes added by 'git notes add'
+
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> master)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a (side)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a (initial)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+$
diff --git a/t/t4013/diff.log_--decorate_--decorate-all_--all b/t/t4013/diff.log_--decorate_--decorate-all_--all
new file mode 100644
index 0000000..5d22618
--- /dev/null
+++ b/t/t4013/diff.log_--decorate_--decorate-all_--all
@@ -0,0 +1,61 @@
+$ git log --decorate --decorate-all --all
+commit b7e0bc69303b488b47deca799a7d723971dfa6cd (mode)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ update mode
+
+commit a6f364368ca320bc5a92e18912e16fa6b3dff598 (note)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ update mode (file2)
+
+Notes:
+ note
+
+commit cd4e72fd96faed3f0ba949dc42967430374e2290 (rearrange)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ Rearranged lines in dir/sub
+
+commit cbacedd14cb8b89255a2c02b59e77a2e9a8021a0 (refs/notes/commits)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ Notes added by 'git notes add'
+
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> master)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a (side)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a (initial)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+$
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index fbec8ad..de1da46 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -1400,6 +1400,43 @@ test_expect_success '--from omits redundant in-body header' '
test_cmp expect patch.head
'
+test_expect_success 'with --force-in-body-from, redundant in-body from is kept' '
+ git format-patch --force-in-body-from \
+ -1 --stdout --from="A U Thor <author@example.com>" >patch &&
+ cat >expect <<-\EOF &&
+ From: A U Thor <author@example.com>
+
+ From: A U Thor <author@example.com>
+
+ EOF
+ sed -ne "/^From:/p; /^$/p; /^---$/q" patch >patch.head &&
+ test_cmp expect patch.head
+'
+
+test_expect_success 'format.forceInBodyFrom, equivalent to --force-in-body-from' '
+ git -c format.forceInBodyFrom=yes format-patch \
+ -1 --stdout --from="A U Thor <author@example.com>" >patch &&
+ cat >expect <<-\EOF &&
+ From: A U Thor <author@example.com>
+
+ From: A U Thor <author@example.com>
+
+ EOF
+ sed -ne "/^From:/p; /^$/p; /^---$/q" patch >patch.head &&
+ test_cmp expect patch.head
+'
+
+test_expect_success 'format.forceInBodyFrom, equivalent to --force-in-body-from' '
+ git -c format.forceInBodyFrom=yes format-patch --no-force-in-body-from \
+ -1 --stdout --from="A U Thor <author@example.com>" >patch &&
+ cat >expect <<-\EOF &&
+ From: A U Thor <author@example.com>
+
+ EOF
+ sed -ne "/^From:/p; /^$/p; /^---$/q" patch >patch.head &&
+ test_cmp expect patch.head
+'
+
test_expect_success 'in-body headers trigger content encoding' '
test_env GIT_AUTHOR_NAME="éxötìc" test_commit exotic &&
test_when_finished "git reset --hard HEAD^" &&
@@ -1420,7 +1457,7 @@ append_signoff()
C=$(git commit-tree HEAD^^{tree} -p HEAD) &&
git format-patch --stdout --signoff $C^..$C >append_signoff.patch &&
sed -n -e "1,/^---$/p" append_signoff.patch |
- egrep -n "^Subject|Sign|^$"
+ grep -E -n "^Subject|Sign|^$"
}
test_expect_success 'signoff: commit with no body' '
@@ -2237,10 +2274,10 @@ test_expect_success 'format-patch --base with --attach' '
test_expect_success 'format-patch --attach cover-letter only is non-multipart' '
test_when_finished "rm -fr patches" &&
git format-patch -o patches --cover-letter --attach=mimemime --base=HEAD~ -1 &&
- ! egrep "^--+mimemime" patches/0000*.patch &&
- egrep "^--+mimemime$" patches/0001*.patch >output &&
+ ! grep -E "^--+mimemime" patches/0000*.patch &&
+ grep -E "^--+mimemime$" patches/0001*.patch >output &&
test_line_count = 2 output &&
- egrep "^--+mimemime--$" patches/0001*.patch >output &&
+ grep -E "^--+mimemime--$" patches/0001*.patch >output &&
test_line_count = 1 output
'
diff --git a/t/t4017-diff-retval.sh b/t/t4017-diff-retval.sh
index ed461f4..5bc28ad 100755
--- a/t/t4017-diff-retval.sh
+++ b/t/t4017-diff-retval.sh
@@ -5,6 +5,7 @@ test_description='Return value of diffs'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh
index 858a552..c1ac09e 100755
--- a/t/t4020-diff-external.sh
+++ b/t/t4020-diff-external.sh
@@ -33,7 +33,7 @@ test_expect_success 'GIT_EXTERNAL_DIFF environment' '
'
-test_expect_success !SANITIZE_LEAK 'GIT_EXTERNAL_DIFF environment should apply only to diff' '
+test_expect_success 'GIT_EXTERNAL_DIFF environment should apply only to diff' '
GIT_EXTERNAL_DIFF=echo git log -p -1 HEAD >out &&
grep "^diff --git a/file b/file" out
@@ -74,7 +74,7 @@ test_expect_success 'diff.external' '
test_cmp expect actual
'
-test_expect_success !SANITIZE_LEAK 'diff.external should apply only to diff' '
+test_expect_success 'diff.external should apply only to diff' '
test_config diff.external echo &&
git log -p -1 HEAD >out &&
grep "^diff --git a/file b/file" out
diff --git a/t/t4038-diff-combined.sh b/t/t4038-diff-combined.sh
index 9a292ba..2ce26e5 100755
--- a/t/t4038-diff-combined.sh
+++ b/t/t4038-diff-combined.sh
@@ -80,11 +80,21 @@ test_expect_success 'check combined output (1)' '
verify_helper sidewithone
'
+test_expect_success 'check combined output (1) with git diff <rev>^!' '
+ git diff sidewithone^! -- >sidewithone &&
+ verify_helper sidewithone
+'
+
test_expect_success 'check combined output (2)' '
git show sidesansone -- >sidesansone &&
verify_helper sidesansone
'
+test_expect_success 'check combined output (2) with git diff <rev>^!' '
+ git diff sidesansone^! -- >sidesansone &&
+ verify_helper sidesansone
+'
+
test_expect_success 'diagnose truncated file' '
>file &&
git add file &&
diff --git a/t/t4051-diff-function-context.sh b/t/t4051-diff-function-context.sh
index 4838a1d..725278a 100755
--- a/t/t4051-diff-function-context.sh
+++ b/t/t4051-diff-function-context.sh
@@ -2,6 +2,7 @@
test_description='diff function context'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
dir="$TEST_DIRECTORY/t4051"
diff --git a/t/t4057-diff-combined-paths.sh b/t/t4057-diff-combined-paths.sh
index 04b8a15..9a7505c 100755
--- a/t/t4057-diff-combined-paths.sh
+++ b/t/t4057-diff-combined-paths.sh
@@ -5,6 +5,7 @@ test_description='combined diff show only paths that are different to all parent
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# verify that diffc.expect matches output of
diff --git a/t/t4059-diff-submodule-not-initialized.sh b/t/t4059-diff-submodule-not-initialized.sh
index 49bca7b..d489230 100755
--- a/t/t4059-diff-submodule-not-initialized.sh
+++ b/t/t4059-diff-submodule-not-initialized.sh
@@ -49,7 +49,7 @@ test_expect_success 'setup - submodules' '
'
test_expect_success 'setup - git submodule add' '
- git submodule add ./sm2 sm1 &&
+ git -c protocol.file.allow=always submodule add ./sm2 sm1 &&
commit_file sm1 .gitmodules &&
git diff-tree -p --no-commit-id --submodule=log HEAD -- sm1 >actual &&
cat >expected <<-EOF &&
diff --git a/t/t4060-diff-submodule-option-diff-format.sh b/t/t4060-diff-submodule-option-diff-format.sh
index d86e38a..97c6424 100755
--- a/t/t4060-diff-submodule-option-diff-format.sh
+++ b/t/t4060-diff-submodule-option-diff-format.sh
@@ -840,7 +840,7 @@ rm sm2
mv sm2-bak sm2
test_expect_success 'setup nested submodule' '
- git -C sm2 submodule add ../sm2 nested &&
+ git -c protocol.file.allow=always -C sm2 submodule add ../sm2 nested &&
git -C sm2 commit -a -m "nested sub" &&
head10=$(git -C sm2 rev-parse --short --verify HEAD)
'
diff --git a/t/t4067-diff-partial-clone.sh b/t/t4067-diff-partial-clone.sh
index 804f2a8..28f42a4 100755
--- a/t/t4067-diff-partial-clone.sh
+++ b/t/t4067-diff-partial-clone.sh
@@ -77,6 +77,7 @@ test_expect_success 'diff skips same-OID blobs' '
test_expect_success 'when fetching missing objects, diff skips GITLINKs' '
test_when_finished "rm -rf sub server client trace" &&
+ test_config_global protocol.file.allow always &&
test_create_repo sub &&
test_commit -C sub first &&
diff --git a/t/t4069-remerge-diff.sh b/t/t4069-remerge-diff.sh
index 35f9495..07323eb 100755
--- a/t/t4069-remerge-diff.sh
+++ b/t/t4069-remerge-diff.sh
@@ -56,6 +56,11 @@ test_expect_success 'remerge-diff on a clean merge' '
test_cmp expect actual
'
+test_expect_success 'remerge-diff on a clean merge with a filter' '
+ git show --oneline --remerge-diff --diff-filter=U bc_resolution >actual &&
+ test_must_be_empty actual
+'
+
test_expect_success 'remerge-diff with both a resolved conflict and an unrelated change' '
git log -1 --oneline ab_resolution >tmp &&
cat <<-EOF >>tmp &&
@@ -89,6 +94,22 @@ test_expect_success 'remerge-diff with both a resolved conflict and an unrelated
test_cmp expect actual
'
+test_expect_success 'pickaxe still includes additional headers for relevant changes' '
+ # reuses "expect" from the previous testcase
+
+ git log --oneline --remerge-diff -Sacht ab_resolution >tmp &&
+ sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'can filter out additional headers with pickaxe' '
+ git show --remerge-diff --submodule=log --find-object=HEAD ab_resolution >actual &&
+ test_must_be_empty actual &&
+
+ git show --remerge-diff -S"not present" --all >actual &&
+ test_must_be_empty actual
+'
+
test_expect_success 'setup non-content conflicts' '
git switch --orphan base &&
@@ -184,6 +205,14 @@ test_expect_success 'remerge-diff w/ diff-filter=U: all conflict headers, no dif
test_cmp expect actual
'
+test_expect_success 'submodule formatting ignores additional headers' '
+ # Reuses "expect" from last testcase
+
+ git show --oneline --remerge-diff --diff-filter=U --submodule=log >tmp &&
+ sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'remerge-diff w/ diff-filter=R: relevant file + conflict header' '
git log -1 --oneline resolution >tmp &&
cat <<-EOF >>tmp &&
diff --git a/t/t4114-apply-typechange.sh b/t/t4114-apply-typechange.sh
index da3e64f..8ff3640 100755
--- a/t/t4114-apply-typechange.sh
+++ b/t/t4114-apply-typechange.sh
@@ -7,6 +7,7 @@ test_description='git apply should not get confused with type changes.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup repository and commits' '
diff --git a/t/t4141-apply-too-large.sh b/t/t4141-apply-too-large.sh
new file mode 100755
index 0000000..58742d4
--- /dev/null
+++ b/t/t4141-apply-too-large.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+test_description='git apply with too-large patch'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success EXPENSIVE 'git apply rejects patches that are too large' '
+ sz=$((1024 * 1024 * 1023)) &&
+ {
+ cat <<-\EOF &&
+ diff --git a/file b/file
+ new file mode 100644
+ --- /dev/null
+ +++ b/file
+ @@ -0,0 +1 @@
+ EOF
+ test-tool genzeros
+ } | test_copy_bytes $sz | test_must_fail git apply 2>err &&
+ grep "git apply: failed to read" err
+'
+
+test_done
diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh
index 3095b1b..8e4effe 100755
--- a/t/t4201-shortlog.sh
+++ b/t/t4201-shortlog.sh
@@ -83,6 +83,13 @@ test_expect_success 'pretty format' '
test_cmp expect log.predictable
'
+test_expect_success 'pretty format (with --date)' '
+ sed "s/SUBJECT/2005-04-07 OBJECT_NAME/" expect.template >expect &&
+ git shortlog --format="%ad %H" --date=short HEAD >log &&
+ fuzz log >log.predictable &&
+ test_cmp expect log.predictable
+'
+
test_expect_success '--abbrev' '
sed s/SUBJECT/OBJID/ expect.template >expect &&
git shortlog --format="%h" --abbrev=35 HEAD >log &&
@@ -237,6 +244,26 @@ test_expect_success 'shortlog --group=trailer:signed-off-by' '
test_cmp expect actual
'
+test_expect_success 'shortlog --group=format' '
+ git shortlog -s --date="format:%Y" --group="format:%cN (%cd)" \
+ HEAD >actual &&
+ cat >expect <<-\EOF &&
+ 4 C O Mitter (2005)
+ 1 Sin Nombre (2005)
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'shortlog --group=<format> DWIM' '
+ git shortlog -s --date="format:%Y" --group="%cN (%cd)" HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'shortlog bogus --group' '
+ test_must_fail git shortlog --group=bogus HEAD 2>err &&
+ grep "unknown group type" err
+'
+
test_expect_success 'trailer idents are split' '
cat >expect <<-\EOF &&
2 C O Mitter
@@ -319,6 +346,18 @@ test_expect_success 'shortlog can match multiple groups' '
test_cmp expect actual
'
+test_expect_success 'shortlog can match multiple format groups' '
+ GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME" \
+ git commit --allow-empty -m "identical names" &&
+ test_tick &&
+ cat >expect <<-\EOF &&
+ 2 A U Thor
+ 1 C O Mitter
+ EOF
+ git shortlog -ns --group="%cn" --group="%an" -2 HEAD >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'set up option selection tests' '
git commit --allow-empty -F - <<-\EOF
subject
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index 6e66352..2ce2b41 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -249,6 +249,15 @@ test_expect_success 'log --grep' '
test_cmp expect actual
'
+for noop_opt in --invert-grep --all-match
+do
+ test_expect_success "log $noop_opt without --grep is a NOOP" '
+ git log >expect &&
+ git log $noop_opt >actual &&
+ test_cmp expect actual
+ '
+done
+
cat > expect << EOF
second
initial
@@ -704,9 +713,12 @@ test_expect_success 'set up more tangled history' '
git checkout -b tangle HEAD~6 &&
test_commit tangle-a tangle-a a &&
git merge main~3 &&
+ git update-ref refs/prefetch/merge HEAD &&
git merge side~1 &&
+ git update-ref refs/rewritten/merge HEAD &&
git checkout main &&
git merge tangle &&
+ git update-ref refs/hidden/tangle HEAD &&
git checkout -b reach &&
test_commit reach &&
git checkout main &&
@@ -974,9 +986,9 @@ test_expect_success 'decorate-refs-exclude and simplify-by-decoration' '
Merge-tag-reach (HEAD -> main)
reach (tag: reach, reach)
seventh (tag: seventh)
- Merge-branch-tangle
- Merge-branch-side-early-part-into-tangle (tangle)
- tangle-a (tag: tangle-a)
+ Merge-branch-tangle (refs/hidden/tangle)
+ Merge-branch-side-early-part-into-tangle (refs/rewritten/merge, tangle)
+ Merge-branch-main-early-part-into-tangle (refs/prefetch/merge)
EOF
git log -n6 --decorate=short --pretty="tformat:%f%d" \
--decorate-refs-exclude="*octopus*" \
@@ -1025,6 +1037,115 @@ test_expect_success 'decorate-refs and simplify-by-decoration without output' '
test_cmp expect actual
'
+test_expect_success 'decorate-refs-exclude HEAD' '
+ git log --decorate=full --oneline \
+ --decorate-refs-exclude="HEAD" >actual &&
+ ! grep HEAD actual
+'
+
+test_expect_success 'decorate-refs focus from default' '
+ git log --decorate=full --oneline \
+ --decorate-refs="refs/heads" >actual &&
+ ! grep HEAD actual
+'
+
+test_expect_success '--clear-decorations overrides defaults' '
+ cat >expect.default <<-\EOF &&
+ Merge-tag-reach (HEAD -> refs/heads/main)
+ Merge-tags-octopus-a-and-octopus-b
+ seventh (tag: refs/tags/seventh)
+ octopus-b (tag: refs/tags/octopus-b, refs/heads/octopus-b)
+ octopus-a (tag: refs/tags/octopus-a, refs/heads/octopus-a)
+ reach (tag: refs/tags/reach, refs/heads/reach)
+ Merge-branch-tangle
+ Merge-branch-side-early-part-into-tangle (refs/heads/tangle)
+ Merge-branch-main-early-part-into-tangle
+ tangle-a (tag: refs/tags/tangle-a)
+ Merge-branch-side
+ side-2 (tag: refs/tags/side-2, refs/heads/side)
+ side-1 (tag: refs/tags/side-1)
+ Second
+ sixth
+ fifth
+ fourth
+ third
+ second
+ initial
+ EOF
+ git log --decorate=full --pretty="tformat:%f%d" >actual &&
+ test_cmp expect.default actual &&
+
+ cat >expect.all <<-\EOF &&
+ Merge-tag-reach (HEAD -> refs/heads/main)
+ Merge-tags-octopus-a-and-octopus-b
+ seventh (tag: refs/tags/seventh)
+ octopus-b (tag: refs/tags/octopus-b, refs/heads/octopus-b)
+ octopus-a (tag: refs/tags/octopus-a, refs/heads/octopus-a)
+ reach (tag: refs/tags/reach, refs/heads/reach)
+ Merge-branch-tangle (refs/hidden/tangle)
+ Merge-branch-side-early-part-into-tangle (refs/rewritten/merge, refs/heads/tangle)
+ Merge-branch-main-early-part-into-tangle (refs/prefetch/merge)
+ tangle-a (tag: refs/tags/tangle-a)
+ Merge-branch-side
+ side-2 (tag: refs/tags/side-2, refs/heads/side)
+ side-1 (tag: refs/tags/side-1)
+ Second
+ sixth
+ fifth
+ fourth
+ third
+ second
+ initial
+ EOF
+ git log --decorate=full --pretty="tformat:%f%d" \
+ --clear-decorations >actual &&
+ test_cmp expect.all actual &&
+ git -c log.initialDecorationSet=all log \
+ --decorate=full --pretty="tformat:%f%d" >actual &&
+ test_cmp expect.all actual
+'
+
+test_expect_success '--clear-decorations clears previous exclusions' '
+ cat >expect.all <<-\EOF &&
+ Merge-tag-reach (HEAD -> refs/heads/main)
+ reach (tag: refs/tags/reach, refs/heads/reach)
+ Merge-tags-octopus-a-and-octopus-b
+ octopus-b (tag: refs/tags/octopus-b, refs/heads/octopus-b)
+ octopus-a (tag: refs/tags/octopus-a, refs/heads/octopus-a)
+ seventh (tag: refs/tags/seventh)
+ Merge-branch-tangle (refs/hidden/tangle)
+ Merge-branch-side-early-part-into-tangle (refs/rewritten/merge, refs/heads/tangle)
+ Merge-branch-main-early-part-into-tangle (refs/prefetch/merge)
+ tangle-a (tag: refs/tags/tangle-a)
+ side-2 (tag: refs/tags/side-2, refs/heads/side)
+ side-1 (tag: refs/tags/side-1)
+ initial
+ EOF
+
+ git log --decorate=full --pretty="tformat:%f%d" \
+ --simplify-by-decoration \
+ --decorate-refs-exclude="heads/octopus*" \
+ --decorate-refs="heads" \
+ --clear-decorations >actual &&
+ test_cmp expect.all actual &&
+
+ cat >expect.filtered <<-\EOF &&
+ Merge-tags-octopus-a-and-octopus-b
+ octopus-b (refs/heads/octopus-b)
+ octopus-a (refs/heads/octopus-a)
+ initial
+ EOF
+
+ git log --decorate=full --pretty="tformat:%f%d" \
+ --simplify-by-decoration \
+ --decorate-refs-exclude="heads/octopus" \
+ --decorate-refs="heads" \
+ --clear-decorations \
+ --decorate-refs-exclude="tags/" \
+ --decorate-refs="heads/octopus*" >actual &&
+ test_cmp expect.filtered actual
+'
+
test_expect_success 'log.decorate config parsing' '
git log --oneline --decorate=full >expect.full &&
git log --oneline --decorate=short >expect.short &&
@@ -2112,9 +2233,9 @@ test_expect_success REFFILES 'log diagnoses bogus HEAD hash' '
test_i18ngrep broken stderr
'
-test_expect_success 'log diagnoses bogus HEAD symref' '
+test_expect_success REFFILES 'log diagnoses bogus HEAD symref' '
git init empty &&
- git --git-dir empty/.git symbolic-ref HEAD refs/heads/invalid.lock &&
+ echo "ref: refs/heads/invalid.lock" > empty/.git/HEAD &&
test_must_fail git -C empty log 2>stderr &&
test_i18ngrep broken stderr &&
test_must_fail git -C empty log --default totally-bogus 2>stderr &&
@@ -2192,6 +2313,20 @@ test_expect_success 'log --decorate includes all levels of tag annotated tags' '
test_cmp expect actual
'
+test_expect_success 'log --decorate does not include things outside filter' '
+ reflist="refs/prefetch refs/rebase-merge refs/bundle" &&
+
+ for ref in $reflist
+ do
+ git update-ref $ref/fake HEAD || return 1
+ done &&
+
+ git log --decorate=full --oneline >actual &&
+
+ # None of the refs are visible:
+ ! grep /fake actual
+'
+
test_expect_success 'log --end-of-options' '
git update-ref refs/heads/--source HEAD &&
git log --end-of-options --source >actual &&
diff --git a/t/t4204-patch-id.sh b/t/t4204-patch-id.sh
index a730c0d..a7fa94c 100755
--- a/t/t4204-patch-id.sh
+++ b/t/t4204-patch-id.sh
@@ -8,13 +8,13 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
test_expect_success 'setup' '
- as="a a a a a a a a" && # eight a
- test_write_lines $as >foo &&
- test_write_lines $as >bar &&
+ str="ab cd ef gh ij kl mn op" &&
+ test_write_lines $str >foo &&
+ test_write_lines $str >bar &&
git add foo bar &&
git commit -a -m initial &&
- test_write_lines $as b >foo &&
- test_write_lines $as b >bar &&
+ test_write_lines $str b >foo &&
+ test_write_lines $str b >bar &&
git commit -a -m first &&
git checkout -b same main &&
git commit --amend -m same-msg &&
@@ -22,8 +22,23 @@ test_expect_success 'setup' '
echo c >foo &&
echo c >bar &&
git commit --amend -a -m notsame-msg &&
+ git checkout -b with_space main~ &&
+ cat >foo <<-\EOF &&
+ a b
+ c d
+ e f
+ g h
+ i j
+ k l
+ m n
+ op
+ EOF
+ cp foo bar &&
+ git add foo bar &&
+ git commit --amend -m "with spaces" &&
test_write_lines bar foo >bar-then-foo &&
test_write_lines foo bar >foo-then-bar
+
'
test_expect_success 'patch-id output is well-formed' '
@@ -42,7 +57,7 @@ calc_patch_id () {
}
get_top_diff () {
- git log -p -1 "$@" -O bar-then-foo --
+ git log -p -1 "$@" -O bar-then-foo --full-index --
}
get_patch_id () {
@@ -61,6 +76,33 @@ test_expect_success 'patch-id detects inequality' '
get_patch_id notsame &&
! test_cmp patch-id_main patch-id_notsame
'
+test_expect_success 'patch-id detects equality binary' '
+ cat >.gitattributes <<-\EOF &&
+ foo binary
+ bar binary
+ EOF
+ get_patch_id main &&
+ get_patch_id same &&
+ git log -p -1 --binary main >top-diff.output &&
+ calc_patch_id <top-diff.output main_binpatch &&
+ git log -p -1 --binary same >top-diff.output &&
+ calc_patch_id <top-diff.output same_binpatch &&
+ test_cmp patch-id_main patch-id_main_binpatch &&
+ test_cmp patch-id_same patch-id_same_binpatch &&
+ test_cmp patch-id_main patch-id_same &&
+ test_when_finished "rm .gitattributes"
+'
+
+test_expect_success 'patch-id detects inequality binary' '
+ cat >.gitattributes <<-\EOF &&
+ foo binary
+ bar binary
+ EOF
+ get_patch_id main &&
+ get_patch_id notsame &&
+ ! test_cmp patch-id_main patch-id_notsame &&
+ test_when_finished "rm .gitattributes"
+'
test_expect_success 'patch-id supports git-format-patch output' '
get_patch_id main &&
@@ -101,9 +143,21 @@ test_patch_id_file_order () {
git format-patch -1 --stdout -O foo-then-bar >format-patch.output &&
calc_patch_id <format-patch.output "ordered-$name" "$@" &&
cmp_patch_id $relevant "$name" "ordered-$name"
+}
+test_patch_id_whitespace () {
+ relevant="$1"
+ shift
+ name="ws-${1}-$relevant"
+ shift
+ get_top_diff "main~" >top-diff.output &&
+ calc_patch_id <top-diff.output "$name" "$@" &&
+ get_top_diff "with_space" >top-diff.output &&
+ calc_patch_id <top-diff.output "ws-$name" "$@" &&
+ cmp_patch_id $relevant "$name" "ws-$name"
}
+
# combined test for options: add more tests here to make them
# run with all options
test_patch_id () {
@@ -119,6 +173,14 @@ test_expect_success 'file order is relevant with --unstable' '
test_patch_id_file_order relevant --unstable --unstable
'
+test_expect_success 'whitespace is relevant with --verbatim' '
+ test_patch_id_whitespace relevant --verbatim --verbatim
+'
+
+test_expect_success 'whitespace is irrelevant without --verbatim' '
+ test_patch_id_whitespace irrelevant --stable --stable
+'
+
#Now test various option combinations.
test_expect_success 'default is unstable' '
test_patch_id relevant default
@@ -134,6 +196,17 @@ test_expect_success 'patchid.stable = false is unstable' '
test_patch_id relevant patchid.stable=false
'
+test_expect_success 'patchid.verbatim = true is correct and stable' '
+ test_config patchid.verbatim true &&
+ test_patch_id_whitespace relevant patchid.verbatim=true &&
+ test_patch_id irrelevant patchid.verbatim=true
+'
+
+test_expect_success 'patchid.verbatim = false is unstable' '
+ test_config patchid.verbatim false &&
+ test_patch_id relevant patchid.verbatim=false
+'
+
test_expect_success '--unstable overrides patchid.stable = true' '
test_config patchid.stable true &&
test_patch_id relevant patchid.stable=true--unstable --unstable
@@ -144,6 +217,11 @@ test_expect_success '--stable overrides patchid.stable = false' '
test_patch_id irrelevant patchid.stable=false--stable --stable
'
+test_expect_success '--verbatim overrides patchid.stable = false' '
+ test_config patchid.stable false &&
+ test_patch_id_whitespace relevant stable=false--verbatim --verbatim
+'
+
test_expect_success 'patch-id supports git-format-patch MIME output' '
get_patch_id main &&
git checkout same &&
@@ -198,7 +276,10 @@ test_expect_success 'patch-id handles no-nl-at-eof markers' '
EOF
calc_patch_id nonl <nonl &&
calc_patch_id withnl <withnl &&
- test_cmp patch-id_nonl patch-id_withnl
+ test_cmp patch-id_nonl patch-id_withnl &&
+ calc_patch_id nonl-inc-ws --verbatim <nonl &&
+ calc_patch_id withnl-inc-ws --verbatim <withnl &&
+ ! test_cmp patch-id_nonl-inc-ws patch-id_withnl-inc-ws
'
test_expect_success 'patch-id handles diffs with one line of before/after' '
diff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh
index 36ac6af..ded33a8 100755
--- a/t/t4207-log-decoration-colors.sh
+++ b/t/t4207-log-decoration-colors.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2010 Nazri Ramliy
#
-test_description='Test for "git log --decorate" colors'
+test_description='test "git log --decorate" colors'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
@@ -17,6 +17,7 @@ test_expect_success setup '
git config color.decorate.remoteBranch red &&
git config color.decorate.tag "reverse bold yellow" &&
git config color.decorate.stash magenta &&
+ git config color.decorate.grafted black &&
git config color.decorate.HEAD cyan &&
c_reset="<RESET>" &&
@@ -27,6 +28,7 @@ test_expect_success setup '
c_tag="<BOLD;REVERSE;YELLOW>" &&
c_stash="<MAGENTA>" &&
c_HEAD="<CYAN>" &&
+ c_grafted="<BLACK>" &&
test_commit A &&
git clone . other &&
@@ -42,25 +44,79 @@ test_expect_success setup '
git stash save Changes to A.t
'
-cat >expected <<EOF
-${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD ->\
- ${c_reset}${c_branch}main${c_reset}${c_commit},\
- ${c_reset}${c_tag}tag: v1.0${c_reset}${c_commit},\
- ${c_reset}${c_tag}tag: B${c_reset}${c_commit})${c_reset} B
-${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A1${c_reset}${c_commit},\
- ${c_reset}${c_remoteBranch}other/main${c_reset}${c_commit})${c_reset} A1
-${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_stash}refs/stash${c_reset}${c_commit})${c_reset}\
- On main: Changes to A.t
-${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A${c_reset}${c_commit})${c_reset} A
-EOF
+cmp_filtered_decorations () {
+ sed "s/$OID_REGEX/COMMIT_ID/" actual | test_decode_color >filtered &&
+ test_cmp expect filtered
+}
# We want log to show all, but the second parent to refs/stash is irrelevant
# to this test since it does not contain any decoration, hence --first-parent
-test_expect_success 'Commit Decorations Colored Correctly' '
- git log --first-parent --abbrev=10 --all --decorate --oneline --color=always |
- sed "s/[0-9a-f]\{10,10\}/COMMIT_ID/" |
- test_decode_color >out &&
- test_cmp expected out
+test_expect_success 'commit decorations colored correctly' '
+ cat >expect <<-EOF &&
+ ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD -> \
+${c_reset}${c_branch}main${c_reset}${c_commit}, \
+${c_reset}${c_tag}tag: v1.0${c_reset}${c_commit}, \
+${c_reset}${c_tag}tag: B${c_reset}${c_commit})${c_reset} B
+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A1${c_reset}${c_commit}, \
+${c_reset}${c_remoteBranch}other/main${c_reset}${c_commit})${c_reset} A1
+ ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_stash}refs/stash${c_reset}${c_commit})${c_reset} \
+On main: Changes to A.t
+ ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A${c_reset}${c_commit})${c_reset} A
+ EOF
+
+ git log --first-parent --no-abbrev --decorate --oneline --color=always --all >actual &&
+ cmp_filtered_decorations
+'
+
+test_expect_success 'test coloring with replace-objects' '
+ test_when_finished rm -rf .git/refs/replace* &&
+ test_commit C &&
+ test_commit D &&
+
+ git replace HEAD~1 HEAD~2 &&
+
+ cat >expect <<-EOF &&
+ ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD -> \
+${c_reset}${c_branch}main${c_reset}${c_commit}, \
+${c_reset}${c_tag}tag: D${c_reset}${c_commit})${c_reset} D
+ ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: C${c_reset}${c_commit}, \
+${c_reset}${c_grafted}replaced${c_reset}${c_commit})${c_reset} B
+ ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A${c_reset}${c_commit})${c_reset} A
+EOF
+
+ git log --first-parent --no-abbrev --decorate --oneline --color=always HEAD >actual &&
+ cmp_filtered_decorations &&
+ git replace -d HEAD~1 &&
+
+ GIT_REPLACE_REF_BASE=refs/replace2/ git replace HEAD~1 HEAD~2 &&
+ GIT_REPLACE_REF_BASE=refs/replace2/ git log --first-parent \
+ --no-abbrev --decorate --oneline --color=always HEAD >actual &&
+ cmp_filtered_decorations
+'
+
+test_expect_success 'test coloring with grafted commit' '
+ test_when_finished rm -rf .git/refs/replace* &&
+
+ git replace --graft HEAD HEAD~2 &&
+
+ cat >expect <<-EOF &&
+ ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD -> \
+${c_reset}${c_branch}main${c_reset}${c_commit}, \
+${c_reset}${c_tag}tag: D${c_reset}${c_commit}, \
+${c_reset}${c_grafted}replaced${c_reset}${c_commit})${c_reset} D
+ ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: v1.0${c_reset}${c_commit}, \
+${c_reset}${c_tag}tag: B${c_reset}${c_commit})${c_reset} B
+ ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A${c_reset}${c_commit})${c_reset} A
+ EOF
+
+ git log --first-parent --no-abbrev --decorate --oneline --color=always HEAD >actual &&
+ cmp_filtered_decorations &&
+ git replace -d HEAD &&
+
+ GIT_REPLACE_REF_BASE=refs/replace2/ git replace --graft HEAD HEAD~2 &&
+ GIT_REPLACE_REF_BASE=refs/replace2/ git log --first-parent \
+ --no-abbrev --decorate --oneline --color=always HEAD >actual &&
+ cmp_filtered_decorations
'
test_done
diff --git a/t/t4208-log-magic-pathspec.sh b/t/t4208-log-magic-pathspec.sh
index 7f0c1dc..2e8f5ad 100755
--- a/t/t4208-log-magic-pathspec.sh
+++ b/t/t4208-log-magic-pathspec.sh
@@ -124,6 +124,7 @@ test_expect_success 'command line pathspec parsing for "git log"' '
test_expect_success 'tree_entry_interesting does not match past submodule boundaries' '
test_when_finished "rm -rf repo submodule" &&
+ test_config_global protocol.file.allow always &&
git init submodule &&
test_commit -C submodule initial &&
git init repo &&
diff --git a/t/t4301-merge-tree-write-tree.sh b/t/t4301-merge-tree-write-tree.sh
index f091259..cac8559 100755
--- a/t/t4301-merge-tree-write-tree.sh
+++ b/t/t4301-merge-tree-write-tree.sh
@@ -137,6 +137,579 @@ test_expect_success 'test conflict notices and such' '
test_cmp expect actual
'
+# directory rename + content conflict
+# Commit O: foo, olddir/{a,b,c}
+# Commit A: modify foo, newdir/{a,b,c}
+# Commit B: modify foo differently & rename foo -> olddir/bar
+# Expected: CONFLICT(content) for for newdir/bar (not olddir/bar or foo)
+
+test_expect_success 'directory rename + content conflict' '
+ # Setup
+ git init dir-rename-and-content &&
+ (
+ cd dir-rename-and-content &&
+ test_write_lines 1 2 3 4 5 >foo &&
+ mkdir olddir &&
+ for i in a b c; do echo $i >olddir/$i || exit 1; done &&
+ git add foo olddir &&
+ git commit -m "original" &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git checkout A &&
+ test_write_lines 1 2 3 4 5 6 >foo &&
+ git add foo &&
+ git mv olddir newdir &&
+ git commit -m "Modify foo, rename olddir to newdir" &&
+
+ git checkout B &&
+ test_write_lines 1 2 3 4 5 six >foo &&
+ git add foo &&
+ git mv foo olddir/bar &&
+ git commit -m "Modify foo & rename foo -> olddir/bar"
+ ) &&
+ # Testing
+ (
+ cd dir-rename-and-content &&
+
+ test_expect_code 1 \
+ git merge-tree -z A^0 B^0 >out &&
+ echo >>out &&
+ anonymize_hash out >actual &&
+ q_to_tab <<-\EOF | lf_to_nul >expect &&
+ HASH
+ 100644 HASH 1Qnewdir/bar
+ 100644 HASH 2Qnewdir/bar
+ 100644 HASH 3Qnewdir/bar
+ EOF
+
+ q_to_nul <<-EOF >>expect &&
+ Q2Qnewdir/barQolddir/barQCONFLICT (directory rename suggested)QCONFLICT (file location): foo renamed to olddir/bar in B^0, inside a directory that was renamed in A^0, suggesting it should perhaps be moved to newdir/bar.
+ Q1Qnewdir/barQAuto-mergingQAuto-merging newdir/bar
+ Q1Qnewdir/barQCONFLICT (contents)QCONFLICT (content): Merge conflict in newdir/bar
+ Q
+ EOF
+ test_cmp expect actual
+ )
+'
+
+# rename/delete + modify/delete handling
+# Commit O: foo
+# Commit A: modify foo + rename to bar
+# Commit B: delete foo
+# Expected: CONFLICT(rename/delete) + CONFLICT(modify/delete)
+
+test_expect_success 'rename/delete handling' '
+ # Setup
+ git init rename-delete &&
+ (
+ cd rename-delete &&
+ test_write_lines 1 2 3 4 5 >foo &&
+ git add foo &&
+ git commit -m "original" &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git checkout A &&
+ test_write_lines 1 2 3 4 5 6 >foo &&
+ git add foo &&
+ git mv foo bar &&
+ git commit -m "Modify foo, rename to bar" &&
+
+ git checkout B &&
+ git rm foo &&
+ git commit -m "remove foo"
+ ) &&
+ # Testing
+ (
+ cd rename-delete &&
+
+ test_expect_code 1 \
+ git merge-tree -z A^0 B^0 >out &&
+ echo >>out &&
+ anonymize_hash out >actual &&
+ q_to_tab <<-\EOF | lf_to_nul >expect &&
+ HASH
+ 100644 HASH 1Qbar
+ 100644 HASH 2Qbar
+ EOF
+
+ q_to_nul <<-EOF >>expect &&
+ Q2QbarQfooQCONFLICT (rename/delete)QCONFLICT (rename/delete): foo renamed to bar in A^0, but deleted in B^0.
+ Q1QbarQCONFLICT (modify/delete)QCONFLICT (modify/delete): bar deleted in B^0 and modified in A^0. Version A^0 of bar left in tree.
+ Q
+ EOF
+ test_cmp expect actual
+ )
+'
+
+# rename/add handling
+# Commit O: foo
+# Commit A: modify foo, add different bar
+# Commit B: modify & rename foo->bar
+# Expected: CONFLICT(add/add) [via rename collide] for bar
+
+test_expect_success 'rename/add handling' '
+ # Setup
+ git init rename-add &&
+ (
+ cd rename-add &&
+ test_write_lines original 1 2 3 4 5 >foo &&
+ git add foo &&
+ git commit -m "original" &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git checkout A &&
+ test_write_lines 1 2 3 4 5 >foo &&
+ echo "different file" >bar &&
+ git add foo bar &&
+ git commit -m "Modify foo, add bar" &&
+
+ git checkout B &&
+ test_write_lines original 1 2 3 4 5 6 >foo &&
+ git add foo &&
+ git mv foo bar &&
+ git commit -m "rename foo to bar"
+ ) &&
+ # Testing
+ (
+ cd rename-add &&
+
+ test_expect_code 1 \
+ git merge-tree -z A^0 B^0 >out &&
+ echo >>out &&
+
+ #
+ # First, check that the bar that appears at stage 3 does not
+ # correspond to an individual blob anywhere in history
+ #
+ hash=$(cat out | tr "\0" "\n" | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
+ git rev-list --objects --all >all_blobs &&
+ ! grep $hash all_blobs &&
+
+ #
+ # Second, check anonymized hash output against expectation
+ #
+ anonymize_hash out >actual &&
+ q_to_tab <<-\EOF | lf_to_nul >expect &&
+ HASH
+ 100644 HASH 2Qbar
+ 100644 HASH 3Qbar
+ EOF
+
+ q_to_nul <<-EOF >>expect &&
+ Q1QbarQAuto-mergingQAuto-merging bar
+ Q1QbarQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in bar
+ Q1QfooQAuto-mergingQAuto-merging foo
+ Q
+ EOF
+ test_cmp expect actual
+ )
+'
+
+# rename/add, where add is a mode conflict
+# Commit O: foo
+# Commit A: modify foo, add symlink bar
+# Commit B: modify & rename foo->bar
+# Expected: CONFLICT(distinct modes) for bar
+
+test_expect_success SYMLINKS 'rename/add, where add is a mode conflict' '
+ # Setup
+ git init rename-add-symlink &&
+ (
+ cd rename-add-symlink &&
+ test_write_lines original 1 2 3 4 5 >foo &&
+ git add foo &&
+ git commit -m "original" &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git checkout A &&
+ test_write_lines 1 2 3 4 5 >foo &&
+ ln -s foo bar &&
+ git add foo bar &&
+ git commit -m "Modify foo, add symlink bar" &&
+
+ git checkout B &&
+ test_write_lines original 1 2 3 4 5 6 >foo &&
+ git add foo &&
+ git mv foo bar &&
+ git commit -m "rename foo to bar"
+ ) &&
+ # Testing
+ (
+ cd rename-add-symlink &&
+
+ test_expect_code 1 \
+ git merge-tree -z A^0 B^0 >out &&
+ echo >>out &&
+
+ #
+ # First, check that the bar that appears at stage 3 does not
+ # correspond to an individual blob anywhere in history
+ #
+ hash=$(cat out | tr "\0" "\n" | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
+ git rev-list --objects --all >all_blobs &&
+ ! grep $hash all_blobs &&
+
+ #
+ # Second, check anonymized hash output against expectation
+ #
+ anonymize_hash out >actual &&
+ q_to_tab <<-\EOF | lf_to_nul >expect &&
+ HASH
+ 120000 HASH 2Qbar
+ 100644 HASH 3Qbar~B^0
+ EOF
+
+ q_to_nul <<-EOF >>expect &&
+ Q2QbarQbar~B^0QCONFLICT (distinct modes)QCONFLICT (distinct types): bar had different types on each side; renamed one of them so each can be recorded somewhere.
+ Q1QfooQAuto-mergingQAuto-merging foo
+ Q
+ EOF
+ test_cmp expect actual
+ )
+'
+
+# rename/rename(1to2) + content conflict handling
+# Commit O: foo
+# Commit A: modify foo & rename to bar
+# Commit B: modify foo & rename to baz
+# Expected: CONFLICT(rename/rename)
+
+test_expect_success 'rename/rename + content conflict' '
+ # Setup
+ git init rr-plus-content &&
+ (
+ cd rr-plus-content &&
+ test_write_lines 1 2 3 4 5 >foo &&
+ git add foo &&
+ git commit -m "original" &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git checkout A &&
+ test_write_lines 1 2 3 4 5 six >foo &&
+ git add foo &&
+ git mv foo bar &&
+ git commit -m "Modify foo + rename to bar" &&
+
+ git checkout B &&
+ test_write_lines 1 2 3 4 5 6 >foo &&
+ git add foo &&
+ git mv foo baz &&
+ git commit -m "Modify foo + rename to baz"
+ ) &&
+ # Testing
+ (
+ cd rr-plus-content &&
+
+ test_expect_code 1 \
+ git merge-tree -z A^0 B^0 >out &&
+ echo >>out &&
+ anonymize_hash out >actual &&
+ q_to_tab <<-\EOF | lf_to_nul >expect &&
+ HASH
+ 100644 HASH 2Qbar
+ 100644 HASH 3Qbaz
+ 100644 HASH 1Qfoo
+ EOF
+
+ q_to_nul <<-EOF >>expect &&
+ Q1QfooQAuto-mergingQAuto-merging foo
+ Q3QfooQbarQbazQCONFLICT (rename/rename)QCONFLICT (rename/rename): foo renamed to bar in A^0 and to baz in B^0.
+ Q
+ EOF
+ test_cmp expect actual
+ )
+'
+
+# rename/add/delete
+# Commit O: foo
+# Commit A: rm foo, add different bar
+# Commit B: rename foo->bar
+# Expected: CONFLICT (rename/delete), CONFLICT(add/add) [via rename collide]
+# for bar
+
+test_expect_success 'rename/add/delete conflict' '
+ # Setup
+ git init rad &&
+ (
+ cd rad &&
+ echo "original file" >foo &&
+ git add foo &&
+ git commit -m "original" &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git checkout A &&
+ git rm foo &&
+ echo "different file" >bar &&
+ git add bar &&
+ git commit -m "Remove foo, add bar" &&
+
+ git checkout B &&
+ git mv foo bar &&
+ git commit -m "rename foo to bar"
+ ) &&
+ # Testing
+ (
+ cd rad &&
+
+ test_expect_code 1 \
+ git merge-tree -z B^0 A^0 >out &&
+ echo >>out &&
+ anonymize_hash out >actual &&
+
+ q_to_tab <<-\EOF | lf_to_nul >expect &&
+ HASH
+ 100644 HASH 2Qbar
+ 100644 HASH 3Qbar
+
+ EOF
+
+ q_to_nul <<-EOF >>expect &&
+ 2QbarQfooQCONFLICT (rename/delete)QCONFLICT (rename/delete): foo renamed to bar in B^0, but deleted in A^0.
+ Q1QbarQAuto-mergingQAuto-merging bar
+ Q1QbarQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in bar
+ Q
+ EOF
+ test_cmp expect actual
+ )
+'
+
+# rename/rename(2to1)/delete/delete
+# Commit O: foo, bar
+# Commit A: rename foo->baz, rm bar
+# Commit B: rename bar->baz, rm foo
+# Expected: 2x CONFLICT (rename/delete), CONFLICT (add/add) via colliding
+# renames for baz
+
+test_expect_success 'rename/rename(2to1)/delete/delete conflict' '
+ # Setup
+ git init rrdd &&
+ (
+ cd rrdd &&
+ echo foo >foo &&
+ echo bar >bar &&
+ git add foo bar &&
+ git commit -m O &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git checkout A &&
+ git mv foo baz &&
+ git rm bar &&
+ git commit -m "Rename foo, remove bar" &&
+
+ git checkout B &&
+ git mv bar baz &&
+ git rm foo &&
+ git commit -m "Rename bar, remove foo"
+ ) &&
+ # Testing
+ (
+ cd rrdd &&
+
+ test_expect_code 1 \
+ git merge-tree -z A^0 B^0 >out &&
+ echo >>out &&
+ anonymize_hash out >actual &&
+
+ q_to_tab <<-\EOF | lf_to_nul >expect &&
+ HASH
+ 100644 HASH 2Qbaz
+ 100644 HASH 3Qbaz
+
+ EOF
+
+ q_to_nul <<-EOF >>expect &&
+ 2QbazQbarQCONFLICT (rename/delete)QCONFLICT (rename/delete): bar renamed to baz in B^0, but deleted in A^0.
+ Q2QbazQfooQCONFLICT (rename/delete)QCONFLICT (rename/delete): foo renamed to baz in A^0, but deleted in B^0.
+ Q1QbazQAuto-mergingQAuto-merging baz
+ Q1QbazQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in baz
+ Q
+ EOF
+ test_cmp expect actual
+ )
+'
+
+# mod6: chains of rename/rename(1to2) + add/add via colliding renames
+# Commit O: one, three, five
+# Commit A: one->two, three->four, five->six
+# Commit B: one->six, three->two, five->four
+# Expected: three CONFLICT(rename/rename) messages + three CONFLICT(add/add)
+# messages; each path in two of the multi-way merged contents
+# found in two, four, six
+
+test_expect_success 'mod6: chains of rename/rename(1to2) and add/add via colliding renames' '
+ # Setup
+ git init mod6 &&
+ (
+ cd mod6 &&
+ test_seq 11 19 >one &&
+ test_seq 31 39 >three &&
+ test_seq 51 59 >five &&
+ git add . &&
+ test_tick &&
+ git commit -m "O" &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git checkout A &&
+ test_seq 10 19 >one &&
+ echo 40 >>three &&
+ git add one three &&
+ git mv one two &&
+ git mv three four &&
+ git mv five six &&
+ test_tick &&
+ git commit -m "A" &&
+
+ git checkout B &&
+ echo 20 >>one &&
+ echo forty >>three &&
+ echo 60 >>five &&
+ git add one three five &&
+ git mv one six &&
+ git mv three two &&
+ git mv five four &&
+ test_tick &&
+ git commit -m "B"
+ ) &&
+ # Testing
+ (
+ cd mod6 &&
+
+ test_expect_code 1 \
+ git merge-tree -z A^0 B^0 >out &&
+ echo >>out &&
+
+ #
+ # First, check that some of the hashes that appear as stage
+ # conflict entries do not appear as individual blobs anywhere
+ # in history.
+ #
+ hash1=$(cat out | tr "\0" "\n" | head | grep 2.four | cut -f 2 -d " ") &&
+ hash2=$(cat out | tr "\0" "\n" | head | grep 3.two | cut -f 2 -d " ") &&
+ git rev-list --objects --all >all_blobs &&
+ ! grep $hash1 all_blobs &&
+ ! grep $hash2 all_blobs &&
+
+ #
+ # Now compare anonymized hash output with expectation
+ #
+ anonymize_hash out >actual &&
+ q_to_tab <<-\EOF | lf_to_nul >expect &&
+ HASH
+ 100644 HASH 1Qfive
+ 100644 HASH 2Qfour
+ 100644 HASH 3Qfour
+ 100644 HASH 1Qone
+ 100644 HASH 2Qsix
+ 100644 HASH 3Qsix
+ 100644 HASH 1Qthree
+ 100644 HASH 2Qtwo
+ 100644 HASH 3Qtwo
+
+ EOF
+
+ q_to_nul <<-EOF >>expect &&
+ 3QfiveQsixQfourQCONFLICT (rename/rename)QCONFLICT (rename/rename): five renamed to six in A^0 and to four in B^0.
+ Q1QfourQAuto-mergingQAuto-merging four
+ Q1QfourQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in four
+ Q1QoneQAuto-mergingQAuto-merging one
+ Q3QoneQtwoQsixQCONFLICT (rename/rename)QCONFLICT (rename/rename): one renamed to two in A^0 and to six in B^0.
+ Q1QsixQAuto-mergingQAuto-merging six
+ Q1QsixQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in six
+ Q1QthreeQAuto-mergingQAuto-merging three
+ Q3QthreeQfourQtwoQCONFLICT (rename/rename)QCONFLICT (rename/rename): three renamed to four in A^0 and to two in B^0.
+ Q1QtwoQAuto-mergingQAuto-merging two
+ Q1QtwoQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in two
+ Q
+ EOF
+ test_cmp expect actual
+ )
+'
+
+# directory rename + rename/delete + modify/delete + directory/file conflict
+# Commit O: foo, olddir/{a,b,c}
+# Commit A: delete foo, rename olddir/ -> newdir/, add newdir/bar/file
+# Commit B: modify foo & rename foo -> olddir/bar
+# Expected: CONFLICT(content) for for newdir/bar (not olddir/bar or foo)
+
+test_expect_success 'directory rename + rename/delete + modify/delete + directory/file conflict' '
+ # Setup
+ git init 4-stacked-conflict &&
+ (
+ cd 4-stacked-conflict &&
+ test_write_lines 1 2 3 4 5 >foo &&
+ mkdir olddir &&
+ for i in a b c; do echo $i >olddir/$i || exit 1; done &&
+ git add foo olddir &&
+ git commit -m "original" &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git checkout A &&
+ git rm foo &&
+ git mv olddir newdir &&
+ mkdir newdir/bar &&
+ >newdir/bar/file &&
+ git add newdir/bar/file &&
+ git commit -m "rm foo, olddir/ -> newdir/, + newdir/bar/file" &&
+
+ git checkout B &&
+ test_write_lines 1 2 3 4 5 6 >foo &&
+ git add foo &&
+ git mv foo olddir/bar &&
+ git commit -m "Modify foo & rename foo -> olddir/bar"
+ ) &&
+ # Testing
+ (
+ cd 4-stacked-conflict &&
+
+ test_expect_code 1 \
+ git merge-tree -z A^0 B^0 >out &&
+ echo >>out &&
+ anonymize_hash out >actual &&
+
+ q_to_tab <<-\EOF | lf_to_nul >expect &&
+ HASH
+ 100644 HASH 1Qnewdir/bar~B^0
+ 100644 HASH 3Qnewdir/bar~B^0
+ EOF
+
+ q_to_nul <<-EOF >>expect &&
+ Q2Qnewdir/barQolddir/barQCONFLICT (directory rename suggested)QCONFLICT (file location): foo renamed to olddir/bar in B^0, inside a directory that was renamed in A^0, suggesting it should perhaps be moved to newdir/bar.
+ Q2Qnewdir/barQfooQCONFLICT (rename/delete)QCONFLICT (rename/delete): foo renamed to newdir/bar in B^0, but deleted in A^0.
+ Q2Qnewdir/bar~B^0Qnewdir/barQCONFLICT (file/directory)QCONFLICT (file/directory): directory in the way of newdir/bar from B^0; moving it to newdir/bar~B^0 instead.
+ Q1Qnewdir/bar~B^0QCONFLICT (modify/delete)QCONFLICT (modify/delete): newdir/bar~B^0 deleted in A^0 and modified in B^0. Version B^0 of newdir/bar~B^0 left in tree.
+ Q
+ EOF
+ test_cmp expect actual
+ )
+'
+
for opt in $(git merge-tree --git-completion-helper-all)
do
if test $opt = "--trivial-merge" || test $opt = "--write-tree"
@@ -187,8 +760,8 @@ test_expect_success 'NUL terminated conflicted file "lines"' '
git commit -m "Renamed numbers" &&
test_expect_code 1 git merge-tree --write-tree -z tweak1 side2 >out &&
+ echo >>out &&
anonymize_hash out >actual &&
- printf "\\n" >>actual &&
# Expected results:
# "greeting" should merge with conflicts
@@ -237,4 +810,54 @@ test_expect_success 'can override merge of unrelated histories' '
test_cmp expect actual
'
+test_expect_success SANITY 'merge-ort fails gracefully in a read-only repository' '
+ git init --bare read-only &&
+ git push read-only side1 side2 side3 &&
+ test_when_finished "chmod -R u+w read-only" &&
+ chmod -R a-w read-only &&
+ test_must_fail git -C read-only merge-tree side1 side3 &&
+ test_must_fail git -C read-only merge-tree side1 side2
+'
+
+test_expect_success '--stdin with both a successful and a conflicted merge' '
+ printf "side1 side3\nside1 side2" | git merge-tree --stdin >actual &&
+
+ git checkout side1^0 &&
+ git merge side3 &&
+
+ printf "1\0" >expect &&
+ git rev-parse HEAD^{tree} | lf_to_nul >>expect &&
+ printf "\0" >>expect &&
+
+ git checkout side1^0 &&
+ test_must_fail git merge side2 &&
+ sed s/HEAD/side1/ greeting >tmp &&
+ mv tmp greeting &&
+ git add -u &&
+ git mv whatever~HEAD whatever~side1 &&
+
+ printf "0\0" >>expect &&
+ git write-tree | lf_to_nul >>expect &&
+
+ cat <<-EOF | q_to_tab | lf_to_nul >>expect &&
+ 100644 $(git rev-parse side1~1:greeting) 1Qgreeting
+ 100644 $(git rev-parse side1:greeting) 2Qgreeting
+ 100644 $(git rev-parse side2:greeting) 3Qgreeting
+ 100644 $(git rev-parse side1~1:whatever) 1Qwhatever~side1
+ 100644 $(git rev-parse side1:whatever) 2Qwhatever~side1
+ EOF
+
+ q_to_nul <<-EOF >>expect &&
+ Q1QgreetingQAuto-mergingQAuto-merging greeting
+ Q1QgreetingQCONFLICT (contents)QCONFLICT (content): Merge conflict in greeting
+ Q1QnumbersQAuto-mergingQAuto-merging numbers
+ Q2Qwhatever~side1QwhateverQCONFLICT (file/directory)QCONFLICT (file/directory): directory in the way of whatever from side1; moving it to whatever~side1 instead.
+ Q1Qwhatever~side1QCONFLICT (modify/delete)QCONFLICT (modify/delete): whatever~side1 deleted in side2 and modified in side1. Version side1 of whatever~side1 left in tree.
+ EOF
+
+ printf "\0\0" >>expect &&
+
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index eaa0b22..d473048 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -342,6 +342,13 @@ test_expect_success 'only enabled filters are available remotely' '
test_cmp_bin remote.bar config.bar
'
+test_expect_success 'invalid filter is reported only once' '
+ test_must_fail git -c tar.invalid.command= archive --format=invalid \
+ HEAD >out 2>err &&
+ test_must_be_empty out &&
+ test_line_count = 1 err
+'
+
test_expect_success 'git archive --format=tgz' '
git archive --format=tgz HEAD >j.tgz
'
diff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh
index cebad10..db11cab 100755
--- a/t/t5100-mailinfo.sh
+++ b/t/t5100-mailinfo.sh
@@ -201,13 +201,13 @@ test_expect_success 'mailinfo -b double [PATCH]' '
test z"$subj" = z"Subject: message"
'
-test_expect_failure 'mailinfo -b trailing [PATCH]' '
+test_expect_success 'mailinfo -b trailing [PATCH]' '
subj="$(echo "Subject: [other] [PATCH] message" |
git mailinfo -b /dev/null /dev/null)" &&
test z"$subj" = z"Subject: [other] message"
'
-test_expect_failure 'mailinfo -b separated double [PATCH]' '
+test_expect_success 'mailinfo -b separated double [PATCH]' '
subj="$(echo "Subject: [PATCH] [other] [PATCH] message" |
git mailinfo -b /dev/null /dev/null)" &&
test z"$subj" = z"Subject: [other] message"
diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh
index 8ae314a..d65a5f9 100755
--- a/t/t5304-prune.sh
+++ b/t/t5304-prune.sh
@@ -29,6 +29,14 @@ test_expect_success setup '
git gc
'
+test_expect_success 'bare repo prune is quiet without $GIT_DIR/objects/pack' '
+ git clone -q --shared --template= --bare . bare.git &&
+ rmdir bare.git/objects/pack &&
+ git --git-dir=bare.git prune --no-progress 2>prune.err &&
+ test_must_be_empty prune.err &&
+ rm -r bare.git prune.err
+'
+
test_expect_success 'prune stale packs' '
orig_pack=$(echo .git/objects/pack/*.pack) &&
>.git/objects/tmp_1.pack &&
diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh
index f775fc1..6d693ee 100755
--- a/t/t5310-pack-bitmaps.sh
+++ b/t/t5310-pack-bitmaps.sh
@@ -26,22 +26,415 @@ has_any () {
grep -Ff "$1" "$2"
}
-setup_bitmap_history
-
-test_expect_success 'setup writing bitmaps during repack' '
- git config repack.writeBitmaps true
-'
-
-test_expect_success 'full repack creates bitmaps' '
- GIT_TRACE2_EVENT="$(pwd)/trace" \
+test_bitmap_cases () {
+ writeLookupTable=false
+ for i in "$@"
+ do
+ case "$i" in
+ "pack.writeBitmapLookupTable") writeLookupTable=true;;
+ esac
+ done
+
+ test_expect_success 'setup test repository' '
+ rm -fr * .git &&
+ git init &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"'
+ '
+ setup_bitmap_history
+
+ test_expect_success 'setup writing bitmaps during repack' '
+ git config repack.writeBitmaps true
+ '
+
+ test_expect_success 'full repack creates bitmaps' '
+ GIT_TRACE2_EVENT="$(pwd)/trace" \
+ git repack -ad &&
+ ls .git/objects/pack/ | grep bitmap >output &&
+ test_line_count = 1 output &&
+ grep "\"key\":\"num_selected_commits\",\"value\":\"106\"" trace &&
+ grep "\"key\":\"num_maximal_commits\",\"value\":\"107\"" trace
+ '
+
+ basic_bitmap_tests
+
+ test_expect_success 'pack-objects respects --local (non-local loose)' '
+ git init --bare alt.git &&
+ echo $(pwd)/alt.git/objects >.git/objects/info/alternates &&
+ echo content1 >file1 &&
+ # non-local loose object which is not present in bitmapped pack
+ altblob=$(GIT_DIR=alt.git git hash-object -w file1) &&
+ # non-local loose object which is also present in bitmapped pack
+ git cat-file blob $blob | GIT_DIR=alt.git git hash-object -w --stdin &&
+ git add file1 &&
+ test_tick &&
+ git commit -m commit_file1 &&
+ echo HEAD | git pack-objects --local --stdout --revs >1.pack &&
+ git index-pack 1.pack &&
+ list_packed_objects 1.idx >1.objects &&
+ printf "%s\n" "$altblob" "$blob" >nonlocal-loose &&
+ ! has_any nonlocal-loose 1.objects
+ '
+
+ test_expect_success 'pack-objects respects --honor-pack-keep (local non-bitmapped pack)' '
+ echo content2 >file2 &&
+ blob2=$(git hash-object -w file2) &&
+ git add file2 &&
+ test_tick &&
+ git commit -m commit_file2 &&
+ printf "%s\n" "$blob2" "$bitmaptip" >keepobjects &&
+ pack2=$(git pack-objects pack2 <keepobjects) &&
+ mv pack2-$pack2.* .git/objects/pack/ &&
+ >.git/objects/pack/pack2-$pack2.keep &&
+ rm $(objpath $blob2) &&
+ echo HEAD | git pack-objects --honor-pack-keep --stdout --revs >2a.pack &&
+ git index-pack 2a.pack &&
+ list_packed_objects 2a.idx >2a.objects &&
+ ! has_any keepobjects 2a.objects
+ '
+
+ test_expect_success 'pack-objects respects --local (non-local pack)' '
+ mv .git/objects/pack/pack2-$pack2.* alt.git/objects/pack/ &&
+ echo HEAD | git pack-objects --local --stdout --revs >2b.pack &&
+ git index-pack 2b.pack &&
+ list_packed_objects 2b.idx >2b.objects &&
+ ! has_any keepobjects 2b.objects
+ '
+
+ test_expect_success 'pack-objects respects --honor-pack-keep (local bitmapped pack)' '
+ ls .git/objects/pack/ | grep bitmap >output &&
+ test_line_count = 1 output &&
+ packbitmap=$(basename $(cat output) .bitmap) &&
+ list_packed_objects .git/objects/pack/$packbitmap.idx >packbitmap.objects &&
+ test_when_finished "rm -f .git/objects/pack/$packbitmap.keep" &&
+ >.git/objects/pack/$packbitmap.keep &&
+ echo HEAD | git pack-objects --honor-pack-keep --stdout --revs >3a.pack &&
+ git index-pack 3a.pack &&
+ list_packed_objects 3a.idx >3a.objects &&
+ ! has_any packbitmap.objects 3a.objects
+ '
+
+ test_expect_success 'pack-objects respects --local (non-local bitmapped pack)' '
+ mv .git/objects/pack/$packbitmap.* alt.git/objects/pack/ &&
+ rm -f .git/objects/pack/multi-pack-index &&
+ test_when_finished "mv alt.git/objects/pack/$packbitmap.* .git/objects/pack/" &&
+ echo HEAD | git pack-objects --local --stdout --revs >3b.pack &&
+ git index-pack 3b.pack &&
+ list_packed_objects 3b.idx >3b.objects &&
+ ! has_any packbitmap.objects 3b.objects
+ '
+
+ test_expect_success 'pack-objects to file can use bitmap' '
+ # make sure we still have 1 bitmap index from previous tests
+ ls .git/objects/pack/ | grep bitmap >output &&
+ test_line_count = 1 output &&
+ # verify equivalent packs are generated with/without using bitmap index
+ packasha1=$(git pack-objects --no-use-bitmap-index --all packa </dev/null) &&
+ packbsha1=$(git pack-objects --use-bitmap-index --all packb </dev/null) &&
+ list_packed_objects packa-$packasha1.idx >packa.objects &&
+ list_packed_objects packb-$packbsha1.idx >packb.objects &&
+ test_cmp packa.objects packb.objects
+ '
+
+ test_expect_success 'full repack, reusing previous bitmaps' '
git repack -ad &&
- ls .git/objects/pack/ | grep bitmap >output &&
- test_line_count = 1 output &&
- grep "\"key\":\"num_selected_commits\",\"value\":\"106\"" trace &&
- grep "\"key\":\"num_maximal_commits\",\"value\":\"107\"" trace
-'
+ ls .git/objects/pack/ | grep bitmap >output &&
+ test_line_count = 1 output
+ '
+
+ test_expect_success 'fetch (full bitmap)' '
+ git --git-dir=clone.git fetch origin second:second &&
+ git rev-parse HEAD >expect &&
+ git --git-dir=clone.git rev-parse HEAD >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success 'create objects for missing-HAVE tests' '
+ blob=$(echo "missing have" | git hash-object -w --stdin) &&
+ tree=$(printf "100644 blob $blob\tfile\n" | git mktree) &&
+ parent=$(echo parent | git commit-tree $tree) &&
+ commit=$(echo commit | git commit-tree $tree -p $parent) &&
+ cat >revs <<-EOF
+ HEAD
+ ^HEAD^
+ ^$commit
+ EOF
+ '
+
+ test_expect_success 'pack-objects respects --incremental' '
+ cat >revs2 <<-EOF &&
+ HEAD
+ $commit
+ EOF
+ git pack-objects --incremental --stdout --revs <revs2 >4.pack &&
+ git index-pack 4.pack &&
+ list_packed_objects 4.idx >4.objects &&
+ test_line_count = 4 4.objects &&
+ git rev-list --objects $commit >revlist &&
+ cut -d" " -f1 revlist |sort >objects &&
+ test_cmp 4.objects objects
+ '
+
+ test_expect_success 'pack with missing blob' '
+ rm $(objpath $blob) &&
+ git pack-objects --stdout --revs <revs >/dev/null
+ '
+
+ test_expect_success 'pack with missing tree' '
+ rm $(objpath $tree) &&
+ git pack-objects --stdout --revs <revs >/dev/null
+ '
+
+ test_expect_success 'pack with missing parent' '
+ rm $(objpath $parent) &&
+ git pack-objects --stdout --revs <revs >/dev/null
+ '
+
+ test_expect_success JGIT,SHA1 'we can read jgit bitmaps' '
+ git clone --bare . compat-jgit.git &&
+ (
+ cd compat-jgit.git &&
+ rm -f objects/pack/*.bitmap &&
+ jgit gc &&
+ git rev-list --test-bitmap HEAD
+ )
+ '
+
+ test_expect_success JGIT,SHA1 'jgit can read our bitmaps' '
+ git clone --bare . compat-us.git &&
+ (
+ cd compat-us.git &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
+ git repack -adb &&
+ # jgit gc will barf if it does not like our bitmaps
+ jgit gc
+ )
+ '
+
+ test_expect_success 'splitting packs does not generate bogus bitmaps' '
+ test-tool genrandom foo $((1024 * 1024)) >rand &&
+ git add rand &&
+ git commit -m "commit with big file" &&
+ git -c pack.packSizeLimit=500k repack -adb &&
+ git init --bare no-bitmaps.git &&
+ git -C no-bitmaps.git fetch .. HEAD
+ '
+
+ test_expect_success 'set up reusable pack' '
+ rm -f .git/objects/pack/*.keep &&
+ git repack -adb &&
+ reusable_pack () {
+ git for-each-ref --format="%(objectname)" |
+ git pack-objects --delta-base-offset --revs --stdout "$@"
+ }
+ '
+
+ test_expect_success 'pack reuse respects --honor-pack-keep' '
+ test_when_finished "rm -f .git/objects/pack/*.keep" &&
+ for i in .git/objects/pack/*.pack
+ do
+ >${i%.pack}.keep || return 1
+ done &&
+ reusable_pack --honor-pack-keep >empty.pack &&
+ git index-pack empty.pack &&
+ git show-index <empty.idx >actual &&
+ test_must_be_empty actual
+ '
+
+ test_expect_success 'pack reuse respects --local' '
+ mv .git/objects/pack/* alt.git/objects/pack/ &&
+ test_when_finished "mv alt.git/objects/pack/* .git/objects/pack/" &&
+ reusable_pack --local >empty.pack &&
+ git index-pack empty.pack &&
+ git show-index <empty.idx >actual &&
+ test_must_be_empty actual
+ '
+
+ test_expect_success 'pack reuse respects --incremental' '
+ reusable_pack --incremental >empty.pack &&
+ git index-pack empty.pack &&
+ git show-index <empty.idx >actual &&
+ test_must_be_empty actual
+ '
+
+ test_expect_success 'truncated bitmap fails gracefully (ewah)' '
+ test_config pack.writebitmaphashcache false &&
+ test_config pack.writebitmaplookuptable false &&
+ git repack -ad &&
+ git rev-list --use-bitmap-index --count --all >expect &&
+ bitmap=$(ls .git/objects/pack/*.bitmap) &&
+ test_when_finished "rm -f $bitmap" &&
+ test_copy_bytes 256 <$bitmap >$bitmap.tmp &&
+ mv -f $bitmap.tmp $bitmap &&
+ git rev-list --use-bitmap-index --count --all >actual 2>stderr &&
+ test_cmp expect actual &&
+ test_i18ngrep corrupt.ewah.bitmap stderr
+ '
+
+ test_expect_success 'truncated bitmap fails gracefully (cache)' '
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
+ git repack -ad &&
+ git rev-list --use-bitmap-index --count --all >expect &&
+ bitmap=$(ls .git/objects/pack/*.bitmap) &&
+ test_when_finished "rm -f $bitmap" &&
+ test_copy_bytes 512 <$bitmap >$bitmap.tmp &&
+ mv -f $bitmap.tmp $bitmap &&
+ git rev-list --use-bitmap-index --count --all >actual 2>stderr &&
+ test_cmp expect actual &&
+ test_i18ngrep corrupted.bitmap.index stderr
+ '
+
+ # Create a state of history with these properties:
+ #
+ # - refs that allow a client to fetch some new history, while sharing some old
+ # history with the server; we use branches delta-reuse-old and
+ # delta-reuse-new here
+ #
+ # - the new history contains an object that is stored on the server as a delta
+ # against a base that is in the old history
+ #
+ # - the base object is not immediately reachable from the tip of the old
+ # history; finding it would involve digging down through history we know the
+ # other side has
+ #
+ # This should result in a state where fetching from old->new would not
+ # traditionally reuse the on-disk delta (because we'd have to dig to realize
+ # that the client has it), but we will do so if bitmaps can tell us cheaply
+ # that the other side has it.
+ test_expect_success 'set up thin delta-reuse parent' '
+ # This first commit contains the buried base object.
+ test-tool genrandom delta 16384 >file &&
+ git add file &&
+ git commit -m "delta base" &&
+ base=$(git rev-parse --verify HEAD:file) &&
+
+ # These intermediate commits bury the base back in history.
+ # This becomes the "old" state.
+ for i in 1 2 3 4 5
+ do
+ echo $i >file &&
+ git commit -am "intermediate $i" || return 1
+ done &&
+ git branch delta-reuse-old &&
+
+ # And now our new history has a delta against the buried base. Note
+ # that this must be smaller than the original file, since pack-objects
+ # prefers to create deltas from smaller objects to larger.
+ test-tool genrandom delta 16300 >file &&
+ git commit -am "delta result" &&
+ delta=$(git rev-parse --verify HEAD:file) &&
+ git branch delta-reuse-new &&
+
+ # Repack with bitmaps and double check that we have the expected delta
+ # relationship.
+ git repack -adb &&
+ have_delta $delta $base
+ '
+
+ # Now we can sanity-check the non-bitmap behavior (that the server is not able
+ # to reuse the delta). This isn't strictly something we care about, so this
+ # test could be scrapped in the future. But it makes sure that the next test is
+ # actually triggering the feature we want.
+ #
+ # Note that our tools for working with on-the-wire "thin" packs are limited. So
+ # we actually perform the fetch, retain the resulting pack, and inspect the
+ # result.
+ test_expect_success 'fetch without bitmaps ignores delta against old base' '
+ test_config pack.usebitmaps false &&
+ test_when_finished "rm -rf client.git" &&
+ git init --bare client.git &&
+ (
+ cd client.git &&
+ git config transfer.unpackLimit 1 &&
+ git fetch .. delta-reuse-old:delta-reuse-old &&
+ git fetch .. delta-reuse-new:delta-reuse-new &&
+ have_delta $delta $ZERO_OID
+ )
+ '
+
+ # And do the same for the bitmap case, where we do expect to find the delta.
+ test_expect_success 'fetch with bitmaps can reuse old base' '
+ test_config pack.usebitmaps true &&
+ test_when_finished "rm -rf client.git" &&
+ git init --bare client.git &&
+ (
+ cd client.git &&
+ git config transfer.unpackLimit 1 &&
+ git fetch .. delta-reuse-old:delta-reuse-old &&
+ git fetch .. delta-reuse-new:delta-reuse-new &&
+ have_delta $delta $base
+ )
+ '
+
+ test_expect_success 'pack.preferBitmapTips' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
+
+ # create enough commits that not all are receive bitmap
+ # coverage even if they are all at the tip of some reference.
+ test_commit_bulk --message="%s" 103 &&
+
+ git rev-list HEAD >commits.raw &&
+ sort <commits.raw >commits &&
+
+ git log --format="create refs/tags/%s %H" HEAD >refs &&
+ git update-ref --stdin <refs &&
+
+ git repack -adb &&
+ test-tool bitmap list-commits | sort >bitmaps &&
+
+ # remember which commits did not receive bitmaps
+ comm -13 bitmaps commits >before &&
+ test_file_not_empty before &&
+
+ # mark the commits which did not receive bitmaps as preferred,
+ # and generate the bitmap again
+ perl -pe "s{^}{create refs/tags/include/$. }" <before |
+ git update-ref --stdin &&
+ git -c pack.preferBitmapTips=refs/tags/include repack -adb &&
+
+ # finally, check that the commit(s) without bitmap coverage
+ # are not the same ones as before
+ test-tool bitmap list-commits | sort >bitmaps &&
+ comm -13 bitmaps commits >after &&
+
+ ! test_cmp before after
+ )
+ '
+
+ test_expect_success 'complains about multiple pack bitmaps' '
+ rm -fr repo &&
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
+
+ test_commit base &&
+
+ git repack -adb &&
+ bitmap="$(ls .git/objects/pack/pack-*.bitmap)" &&
+ mv "$bitmap" "$bitmap.bak" &&
+
+ test_commit other &&
+ git repack -ab &&
+
+ mv "$bitmap.bak" "$bitmap" &&
+
+ find .git/objects/pack -type f -name "*.pack" >packs &&
+ find .git/objects/pack -type f -name "*.bitmap" >bitmaps &&
+ test_line_count = 2 packs &&
+ test_line_count = 2 bitmaps &&
+
+ git rev-list --use-bitmap-index HEAD 2>err &&
+ grep "ignoring extra bitmap file" err
+ )
+ '
+}
-basic_bitmap_tests
+test_bitmap_cases
test_expect_success 'incremental repack fails when bitmaps are requested' '
test_commit more-1 &&
@@ -54,219 +447,17 @@ test_expect_success 'incremental repack can disable bitmaps' '
git repack -d --no-write-bitmap-index
'
-test_expect_success 'pack-objects respects --local (non-local loose)' '
- git init --bare alt.git &&
- echo $(pwd)/alt.git/objects >.git/objects/info/alternates &&
- echo content1 >file1 &&
- # non-local loose object which is not present in bitmapped pack
- altblob=$(GIT_DIR=alt.git git hash-object -w file1) &&
- # non-local loose object which is also present in bitmapped pack
- git cat-file blob $blob | GIT_DIR=alt.git git hash-object -w --stdin &&
- git add file1 &&
- test_tick &&
- git commit -m commit_file1 &&
- echo HEAD | git pack-objects --local --stdout --revs >1.pack &&
- git index-pack 1.pack &&
- list_packed_objects 1.idx >1.objects &&
- printf "%s\n" "$altblob" "$blob" >nonlocal-loose &&
- ! has_any nonlocal-loose 1.objects
-'
-
-test_expect_success 'pack-objects respects --honor-pack-keep (local non-bitmapped pack)' '
- echo content2 >file2 &&
- blob2=$(git hash-object -w file2) &&
- git add file2 &&
- test_tick &&
- git commit -m commit_file2 &&
- printf "%s\n" "$blob2" "$bitmaptip" >keepobjects &&
- pack2=$(git pack-objects pack2 <keepobjects) &&
- mv pack2-$pack2.* .git/objects/pack/ &&
- >.git/objects/pack/pack2-$pack2.keep &&
- rm $(objpath $blob2) &&
- echo HEAD | git pack-objects --honor-pack-keep --stdout --revs >2a.pack &&
- git index-pack 2a.pack &&
- list_packed_objects 2a.idx >2a.objects &&
- ! has_any keepobjects 2a.objects
-'
-
-test_expect_success 'pack-objects respects --local (non-local pack)' '
- mv .git/objects/pack/pack2-$pack2.* alt.git/objects/pack/ &&
- echo HEAD | git pack-objects --local --stdout --revs >2b.pack &&
- git index-pack 2b.pack &&
- list_packed_objects 2b.idx >2b.objects &&
- ! has_any keepobjects 2b.objects
-'
-
-test_expect_success 'pack-objects respects --honor-pack-keep (local bitmapped pack)' '
- ls .git/objects/pack/ | grep bitmap >output &&
- test_line_count = 1 output &&
- packbitmap=$(basename $(cat output) .bitmap) &&
- list_packed_objects .git/objects/pack/$packbitmap.idx >packbitmap.objects &&
- test_when_finished "rm -f .git/objects/pack/$packbitmap.keep" &&
- >.git/objects/pack/$packbitmap.keep &&
- echo HEAD | git pack-objects --honor-pack-keep --stdout --revs >3a.pack &&
- git index-pack 3a.pack &&
- list_packed_objects 3a.idx >3a.objects &&
- ! has_any packbitmap.objects 3a.objects
-'
-
-test_expect_success 'pack-objects respects --local (non-local bitmapped pack)' '
- mv .git/objects/pack/$packbitmap.* alt.git/objects/pack/ &&
- rm -f .git/objects/pack/multi-pack-index &&
- test_when_finished "mv alt.git/objects/pack/$packbitmap.* .git/objects/pack/" &&
- echo HEAD | git pack-objects --local --stdout --revs >3b.pack &&
- git index-pack 3b.pack &&
- list_packed_objects 3b.idx >3b.objects &&
- ! has_any packbitmap.objects 3b.objects
-'
-
-test_expect_success 'pack-objects to file can use bitmap' '
- # make sure we still have 1 bitmap index from previous tests
- ls .git/objects/pack/ | grep bitmap >output &&
- test_line_count = 1 output &&
- # verify equivalent packs are generated with/without using bitmap index
- packasha1=$(git pack-objects --no-use-bitmap-index --all packa </dev/null) &&
- packbsha1=$(git pack-objects --use-bitmap-index --all packb </dev/null) &&
- list_packed_objects packa-$packasha1.idx >packa.objects &&
- list_packed_objects packb-$packbsha1.idx >packb.objects &&
- test_cmp packa.objects packb.objects
-'
-
-test_expect_success 'full repack, reusing previous bitmaps' '
- git repack -ad &&
- ls .git/objects/pack/ | grep bitmap >output &&
- test_line_count = 1 output
-'
-
-test_expect_success 'fetch (full bitmap)' '
- git --git-dir=clone.git fetch origin second:second &&
- git rev-parse HEAD >expect &&
- git --git-dir=clone.git rev-parse HEAD >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'create objects for missing-HAVE tests' '
- blob=$(echo "missing have" | git hash-object -w --stdin) &&
- tree=$(printf "100644 blob $blob\tfile\n" | git mktree) &&
- parent=$(echo parent | git commit-tree $tree) &&
- commit=$(echo commit | git commit-tree $tree -p $parent) &&
- cat >revs <<-EOF
- HEAD
- ^HEAD^
- ^$commit
- EOF
-'
-
-test_expect_success 'pack-objects respects --incremental' '
- cat >revs2 <<-EOF &&
- HEAD
- $commit
- EOF
- git pack-objects --incremental --stdout --revs <revs2 >4.pack &&
- git index-pack 4.pack &&
- list_packed_objects 4.idx >4.objects &&
- test_line_count = 4 4.objects &&
- git rev-list --objects $commit >revlist &&
- cut -d" " -f1 revlist |sort >objects &&
- test_cmp 4.objects objects
-'
-
-test_expect_success 'pack with missing blob' '
- rm $(objpath $blob) &&
- git pack-objects --stdout --revs <revs >/dev/null
-'
-
-test_expect_success 'pack with missing tree' '
- rm $(objpath $tree) &&
- git pack-objects --stdout --revs <revs >/dev/null
-'
-
-test_expect_success 'pack with missing parent' '
- rm $(objpath $parent) &&
- git pack-objects --stdout --revs <revs >/dev/null
-'
-
-test_expect_success JGIT,SHA1 'we can read jgit bitmaps' '
- git clone --bare . compat-jgit.git &&
- (
- cd compat-jgit.git &&
- rm -f objects/pack/*.bitmap &&
- jgit gc &&
- git rev-list --test-bitmap HEAD
- )
-'
-
-test_expect_success JGIT,SHA1 'jgit can read our bitmaps' '
- git clone --bare . compat-us.git &&
- (
- cd compat-us.git &&
- git repack -adb &&
- # jgit gc will barf if it does not like our bitmaps
- jgit gc
- )
-'
-
-test_expect_success 'splitting packs does not generate bogus bitmaps' '
- test-tool genrandom foo $((1024 * 1024)) >rand &&
- git add rand &&
- git commit -m "commit with big file" &&
- git -c pack.packSizeLimit=500k repack -adb &&
- git init --bare no-bitmaps.git &&
- git -C no-bitmaps.git fetch .. HEAD
-'
-
-test_expect_success 'set up reusable pack' '
- rm -f .git/objects/pack/*.keep &&
- git repack -adb &&
- reusable_pack () {
- git for-each-ref --format="%(objectname)" |
- git pack-objects --delta-base-offset --revs --stdout "$@"
- }
-'
-
-test_expect_success 'pack reuse respects --honor-pack-keep' '
- test_when_finished "rm -f .git/objects/pack/*.keep" &&
- for i in .git/objects/pack/*.pack
- do
- >${i%.pack}.keep || return 1
- done &&
- reusable_pack --honor-pack-keep >empty.pack &&
- git index-pack empty.pack &&
- git show-index <empty.idx >actual &&
- test_must_be_empty actual
-'
-
-test_expect_success 'pack reuse respects --local' '
- mv .git/objects/pack/* alt.git/objects/pack/ &&
- test_when_finished "mv alt.git/objects/pack/* .git/objects/pack/" &&
- reusable_pack --local >empty.pack &&
- git index-pack empty.pack &&
- git show-index <empty.idx >actual &&
- test_must_be_empty actual
-'
+test_bitmap_cases "pack.writeBitmapLookupTable"
-test_expect_success 'pack reuse respects --incremental' '
- reusable_pack --incremental >empty.pack &&
- git index-pack empty.pack &&
- git show-index <empty.idx >actual &&
- test_must_be_empty actual
+test_expect_success 'verify writing bitmap lookup table when enabled' '
+ GIT_TRACE2_EVENT="$(pwd)/trace2" \
+ git repack -ad &&
+ grep "\"label\":\"writing_lookup_table\"" trace2
'
-test_expect_success 'truncated bitmap fails gracefully (ewah)' '
+test_expect_success 'truncated bitmap fails gracefully (lookup table)' '
test_config pack.writebitmaphashcache false &&
- git repack -ad &&
- git rev-list --use-bitmap-index --count --all >expect &&
- bitmap=$(ls .git/objects/pack/*.bitmap) &&
- test_when_finished "rm -f $bitmap" &&
- test_copy_bytes 256 <$bitmap >$bitmap.tmp &&
- mv -f $bitmap.tmp $bitmap &&
- git rev-list --use-bitmap-index --count --all >actual 2>stderr &&
- test_cmp expect actual &&
- test_i18ngrep corrupt.ewah.bitmap stderr
-'
-
-test_expect_success 'truncated bitmap fails gracefully (cache)' '
- git repack -ad &&
+ git repack -adb &&
git rev-list --use-bitmap-index --count --all >expect &&
bitmap=$(ls .git/objects/pack/*.bitmap) &&
test_when_finished "rm -f $bitmap" &&
@@ -277,152 +468,4 @@ test_expect_success 'truncated bitmap fails gracefully (cache)' '
test_i18ngrep corrupted.bitmap.index stderr
'
-# Create a state of history with these properties:
-#
-# - refs that allow a client to fetch some new history, while sharing some old
-# history with the server; we use branches delta-reuse-old and
-# delta-reuse-new here
-#
-# - the new history contains an object that is stored on the server as a delta
-# against a base that is in the old history
-#
-# - the base object is not immediately reachable from the tip of the old
-# history; finding it would involve digging down through history we know the
-# other side has
-#
-# This should result in a state where fetching from old->new would not
-# traditionally reuse the on-disk delta (because we'd have to dig to realize
-# that the client has it), but we will do so if bitmaps can tell us cheaply
-# that the other side has it.
-test_expect_success 'set up thin delta-reuse parent' '
- # This first commit contains the buried base object.
- test-tool genrandom delta 16384 >file &&
- git add file &&
- git commit -m "delta base" &&
- base=$(git rev-parse --verify HEAD:file) &&
-
- # These intermediate commits bury the base back in history.
- # This becomes the "old" state.
- for i in 1 2 3 4 5
- do
- echo $i >file &&
- git commit -am "intermediate $i" || return 1
- done &&
- git branch delta-reuse-old &&
-
- # And now our new history has a delta against the buried base. Note
- # that this must be smaller than the original file, since pack-objects
- # prefers to create deltas from smaller objects to larger.
- test-tool genrandom delta 16300 >file &&
- git commit -am "delta result" &&
- delta=$(git rev-parse --verify HEAD:file) &&
- git branch delta-reuse-new &&
-
- # Repack with bitmaps and double check that we have the expected delta
- # relationship.
- git repack -adb &&
- have_delta $delta $base
-'
-
-# Now we can sanity-check the non-bitmap behavior (that the server is not able
-# to reuse the delta). This isn't strictly something we care about, so this
-# test could be scrapped in the future. But it makes sure that the next test is
-# actually triggering the feature we want.
-#
-# Note that our tools for working with on-the-wire "thin" packs are limited. So
-# we actually perform the fetch, retain the resulting pack, and inspect the
-# result.
-test_expect_success 'fetch without bitmaps ignores delta against old base' '
- test_config pack.usebitmaps false &&
- test_when_finished "rm -rf client.git" &&
- git init --bare client.git &&
- (
- cd client.git &&
- git config transfer.unpackLimit 1 &&
- git fetch .. delta-reuse-old:delta-reuse-old &&
- git fetch .. delta-reuse-new:delta-reuse-new &&
- have_delta $delta $ZERO_OID
- )
-'
-
-# And do the same for the bitmap case, where we do expect to find the delta.
-test_expect_success 'fetch with bitmaps can reuse old base' '
- test_config pack.usebitmaps true &&
- test_when_finished "rm -rf client.git" &&
- git init --bare client.git &&
- (
- cd client.git &&
- git config transfer.unpackLimit 1 &&
- git fetch .. delta-reuse-old:delta-reuse-old &&
- git fetch .. delta-reuse-new:delta-reuse-new &&
- have_delta $delta $base
- )
-'
-
-test_expect_success 'pack.preferBitmapTips' '
- git init repo &&
- test_when_finished "rm -fr repo" &&
- (
- cd repo &&
-
- # create enough commits that not all are receive bitmap
- # coverage even if they are all at the tip of some reference.
- test_commit_bulk --message="%s" 103 &&
-
- git rev-list HEAD >commits.raw &&
- sort <commits.raw >commits &&
-
- git log --format="create refs/tags/%s %H" HEAD >refs &&
- git update-ref --stdin <refs &&
-
- git repack -adb &&
- test-tool bitmap list-commits | sort >bitmaps &&
-
- # remember which commits did not receive bitmaps
- comm -13 bitmaps commits >before &&
- test_file_not_empty before &&
-
- # mark the commits which did not receive bitmaps as preferred,
- # and generate the bitmap again
- perl -pe "s{^}{create refs/tags/include/$. }" <before |
- git update-ref --stdin &&
- git -c pack.preferBitmapTips=refs/tags/include repack -adb &&
-
- # finally, check that the commit(s) without bitmap coverage
- # are not the same ones as before
- test-tool bitmap list-commits | sort >bitmaps &&
- comm -13 bitmaps commits >after &&
-
- ! test_cmp before after
- )
-'
-
-test_expect_success 'complains about multiple pack bitmaps' '
- rm -fr repo &&
- git init repo &&
- test_when_finished "rm -fr repo" &&
- (
- cd repo &&
-
- test_commit base &&
-
- git repack -adb &&
- bitmap="$(ls .git/objects/pack/pack-*.bitmap)" &&
- mv "$bitmap" "$bitmap.bak" &&
-
- test_commit other &&
- git repack -ab &&
-
- mv "$bitmap.bak" "$bitmap" &&
-
- find .git/objects/pack -type f -name "*.pack" >packs &&
- find .git/objects/pack -type f -name "*.bitmap" >bitmaps &&
- test_line_count = 2 packs &&
- test_line_count = 2 bitmaps &&
-
- git rev-list --use-bitmap-index HEAD 2>err &&
- grep "ignoring extra bitmap file" err
- )
-'
-
test_done
diff --git a/t/t5311-pack-bitmaps-shallow.sh b/t/t5311-pack-bitmaps-shallow.sh
index 872a95d..9dae60f 100755
--- a/t/t5311-pack-bitmaps-shallow.sh
+++ b/t/t5311-pack-bitmaps-shallow.sh
@@ -17,23 +17,40 @@ test_description='check bitmap operation with shallow repositories'
# the tree for A. But in a shallow one, we've grafted away
# A, and fetching A to B requires that the other side send
# us the tree for file=1.
-test_expect_success 'setup shallow repo' '
- echo 1 >file &&
- git add file &&
- git commit -m orig &&
- echo 2 >file &&
- git commit -a -m update &&
- git clone --no-local --bare --depth=1 . shallow.git &&
- echo 1 >file &&
- git commit -a -m repeat
-'
-
-test_expect_success 'turn on bitmaps in the parent' '
- git repack -adb
-'
-
-test_expect_success 'shallow fetch from bitmapped repo' '
- (cd shallow.git && git fetch)
-'
+test_shallow_bitmaps () {
+ writeLookupTable=false
+
+ for i in "$@"
+ do
+ case $i in
+ "pack.writeBitmapLookupTable") writeLookupTable=true;;
+ esac
+ done
+
+ test_expect_success 'setup shallow repo' '
+ rm -rf * .git &&
+ git init &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
+ echo 1 >file &&
+ git add file &&
+ git commit -m orig &&
+ echo 2 >file &&
+ git commit -a -m update &&
+ git clone --no-local --bare --depth=1 . shallow.git &&
+ echo 1 >file &&
+ git commit -a -m repeat
+ '
+
+ test_expect_success 'turn on bitmaps in the parent' '
+ git repack -adb
+ '
+
+ test_expect_success 'shallow fetch from bitmapped repo' '
+ (cd shallow.git && git fetch)
+ '
+}
+
+test_shallow_bitmaps
+test_shallow_bitmaps "pack.writeBitmapLookupTable"
test_done
diff --git a/t/t5315-pack-objects-compression.sh b/t/t5315-pack-objects-compression.sh
index 8bacd96..c80ea9e 100755
--- a/t/t5315-pack-objects-compression.sh
+++ b/t/t5315-pack-objects-compression.sh
@@ -2,6 +2,7 @@
test_description='pack-object compression configuration'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh
index 1b0cd82..049c5fc 100755
--- a/t/t5318-commit-graph.sh
+++ b/t/t5318-commit-graph.sh
@@ -12,12 +12,12 @@ test_expect_success 'usage' '
test_expect_success 'usage shown without sub-command' '
test_expect_code 129 git commit-graph 2>err &&
- ! grep error: err
+ grep usage: err
'
test_expect_success 'usage shown with an error on unknown sub-command' '
cat >expect <<-\EOF &&
- error: unrecognized subcommand: unknown
+ error: unknown subcommand: `unknown'\''
EOF
test_expect_code 129 git commit-graph unknown 2>stderr &&
grep error stderr >actual &&
diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh
index afbe93f..b5f9b10 100755
--- a/t/t5319-multi-pack-index.sh
+++ b/t/t5319-multi-pack-index.sh
@@ -784,6 +784,70 @@ test_expect_success 'repack creates a new pack' '
)
'
+test_expect_success 'repack (all) ignores cruft pack' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit base &&
+ test_commit --no-tag unreachable &&
+
+ git reset --hard base &&
+ git reflog expire --all --expire=all &&
+ git repack --cruft -d &&
+
+ git multi-pack-index write &&
+
+ find $objdir/pack | sort >before &&
+ git multi-pack-index repack --batch-size=0 &&
+ find $objdir/pack | sort >after &&
+
+ test_cmp before after
+ )
+'
+
+test_expect_success 'repack (--batch-size) ignores cruft pack' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit_bulk 5 &&
+ test_commit --no-tag unreachable &&
+
+ git reset --hard HEAD^ &&
+ git reflog expire --all --expire=all &&
+ git repack --cruft -d &&
+
+ test_commit four &&
+
+ find $objdir/pack -type f -name "*.pack" | sort >before &&
+ git repack -d &&
+ find $objdir/pack -type f -name "*.pack" | sort >after &&
+
+ pack="$(comm -13 before after)" &&
+ test_file_size "$pack" >sz &&
+ # Set --batch-size to twice the size of the pack created
+ # in the previous step, since this is enough to
+ # accommodate it and the cruft pack.
+ #
+ # This means that the MIDX machinery *could* combine the
+ # new and cruft packs together.
+ #
+ # We ensure that it does not below.
+ batch="$((($(cat sz) * 2)))" &&
+
+ git multi-pack-index write &&
+
+ find $objdir/pack | sort >before &&
+ git multi-pack-index repack --batch-size=$batch &&
+ find $objdir/pack | sort >after &&
+
+ test_cmp before after
+ )
+'
+
test_expect_success 'expire removes repacked packs' '
(
cd dup &&
@@ -847,6 +911,36 @@ test_expect_success 'expire respects .keep files' '
)
'
+test_expect_success 'expiring unreferenced cruft pack retains pack' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit base &&
+ test_commit --no-tag unreachable &&
+ unreachable=$(git rev-parse HEAD) &&
+
+ git reset --hard base &&
+ git reflog expire --all --expire=all &&
+ git repack --cruft -d &&
+ mtimes="$(ls $objdir/pack/pack-*.mtimes)" &&
+
+ echo "base..$unreachable" >in &&
+ pack="$(git pack-objects --revs --delta-base-offset \
+ $objdir/pack/pack <in)" &&
+
+ # Preferring the contents of "$pack" will leave the
+ # cruft pack unreferenced (ie., none of the objects
+ # contained in the cruft pack will have their MIDX copy
+ # selected from the cruft pack).
+ git multi-pack-index write --preferred-pack="pack-$pack.pack" &&
+ git multi-pack-index expire &&
+
+ test_path_is_file "$mtimes"
+ )
+'
+
test_expect_success 'repack --batch-size=0 repacks everything' '
cp -r dup dup2 &&
(
diff --git a/t/t5320-delta-islands.sh b/t/t5320-delta-islands.sh
index 124d476..4063633 100755
--- a/t/t5320-delta-islands.sh
+++ b/t/t5320-delta-islands.sh
@@ -134,7 +134,7 @@ test_expect_success 'island core places core objects first' '
repack -adfi &&
git verify-pack -v .git/objects/pack/*.pack |
cut -d" " -f1 |
- egrep "$root|$two" >actual &&
+ grep -E "$root|$two" >actual &&
test_cmp expect actual
'
diff --git a/t/t5326-multi-pack-bitmaps.sh b/t/t5326-multi-pack-bitmaps.sh
index 4fe5741..0882cbb 100755
--- a/t/t5326-multi-pack-bitmaps.sh
+++ b/t/t5326-multi-pack-bitmaps.sh
@@ -15,17 +15,24 @@ GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0
sane_unset GIT_TEST_MIDX_WRITE_REV
sane_unset GIT_TEST_MIDX_READ_RIDX
-midx_bitmap_core
-
bitmap_reuse_tests() {
from=$1
to=$2
+ writeLookupTable=false
+
+ for i in $3-${$#}
+ do
+ case $i in
+ "pack.writeBitmapLookupTable") writeLookupTable=true;;
+ esac
+ done
test_expect_success "setup pack reuse tests ($from -> $to)" '
rm -fr repo &&
git init repo &&
(
cd repo &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
test_commit_bulk 16 &&
git tag old-tip &&
@@ -43,6 +50,7 @@ bitmap_reuse_tests() {
test_expect_success "build bitmap from existing ($from -> $to)" '
(
cd repo &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
test_commit_bulk --id=further 16 &&
git tag new-tip &&
@@ -59,6 +67,7 @@ bitmap_reuse_tests() {
test_expect_success "verify resulting bitmaps ($from -> $to)" '
(
cd repo &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
git for-each-ref &&
git rev-list --test-bitmap refs/tags/old-tip &&
git rev-list --test-bitmap refs/tags/new-tip
@@ -66,244 +75,362 @@ bitmap_reuse_tests() {
'
}
-bitmap_reuse_tests 'pack' 'MIDX'
-bitmap_reuse_tests 'MIDX' 'pack'
-bitmap_reuse_tests 'MIDX' 'MIDX'
+test_midx_bitmap_cases () {
+ writeLookupTable=false
+ writeBitmapLookupTable=
+
+ for i in "$@"
+ do
+ case $i in
+ "pack.writeBitmapLookupTable")
+ writeLookupTable=true
+ writeBitmapLookupTable="$i"
+ ;;
+ esac
+ done
+
+ test_expect_success 'setup test_repository' '
+ rm -rf * .git &&
+ git init &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"'
+ '
-test_expect_success 'missing object closure fails gracefully' '
- rm -fr repo &&
- git init repo &&
- test_when_finished "rm -fr repo" &&
- (
- cd repo &&
+ midx_bitmap_core
- test_commit loose &&
- test_commit packed &&
+ bitmap_reuse_tests 'pack' 'MIDX' "$writeBitmapLookupTable"
+ bitmap_reuse_tests 'MIDX' 'pack' "$writeBitmapLookupTable"
+ bitmap_reuse_tests 'MIDX' 'MIDX' "$writeBitmapLookupTable"
- # Do not pass "--revs"; we want a pack without the "loose"
- # commit.
- git pack-objects $objdir/pack/pack <<-EOF &&
- $(git rev-parse packed)
- EOF
+ test_expect_success 'missing object closure fails gracefully' '
+ rm -fr repo &&
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
- test_must_fail git multi-pack-index write --bitmap 2>err &&
- grep "doesn.t have full closure" err &&
- test_path_is_missing $midx
- )
-'
+ test_commit loose &&
+ test_commit packed &&
-midx_bitmap_partial_tests
+ # Do not pass "--revs"; we want a pack without the "loose"
+ # commit.
+ git pack-objects $objdir/pack/pack <<-EOF &&
+ $(git rev-parse packed)
+ EOF
-test_expect_success 'removing a MIDX clears stale bitmaps' '
- rm -fr repo &&
- git init repo &&
- test_when_finished "rm -fr repo" &&
- (
- cd repo &&
- test_commit base &&
- git repack &&
- git multi-pack-index write --bitmap &&
+ test_must_fail git multi-pack-index write --bitmap 2>err &&
+ grep "doesn.t have full closure" err &&
+ test_path_is_missing $midx
+ )
+ '
- # Write a MIDX and bitmap; remove the MIDX but leave the bitmap.
- stale_bitmap=$midx-$(midx_checksum $objdir).bitmap &&
- rm $midx &&
+ midx_bitmap_partial_tests
- # Then write a new MIDX.
- test_commit new &&
- git repack &&
- git multi-pack-index write --bitmap &&
+ test_expect_success 'removing a MIDX clears stale bitmaps' '
+ rm -fr repo &&
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
+ test_commit base &&
+ git repack &&
+ git multi-pack-index write --bitmap &&
+
+ # Write a MIDX and bitmap; remove the MIDX but leave the bitmap.
+ stale_bitmap=$midx-$(midx_checksum $objdir).bitmap &&
+ rm $midx &&
+
+ # Then write a new MIDX.
+ test_commit new &&
+ git repack &&
+ git multi-pack-index write --bitmap &&
+
+ test_path_is_file $midx &&
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
+ test_path_is_missing $stale_bitmap
+ )
+ '
- test_path_is_file $midx &&
- test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
- test_path_is_missing $stale_bitmap
- )
-'
+ test_expect_success 'pack.preferBitmapTips' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
-test_expect_success 'pack.preferBitmapTips' '
- git init repo &&
- test_when_finished "rm -fr repo" &&
- (
- cd repo &&
+ test_commit_bulk --message="%s" 103 &&
- test_commit_bulk --message="%s" 103 &&
+ git log --format="%H" >commits.raw &&
+ sort <commits.raw >commits &&
- git log --format="%H" >commits.raw &&
- sort <commits.raw >commits &&
+ git log --format="create refs/tags/%s %H" HEAD >refs &&
+ git update-ref --stdin <refs &&
- git log --format="create refs/tags/%s %H" HEAD >refs &&
- git update-ref --stdin <refs &&
+ git multi-pack-index write --bitmap &&
+ test_path_is_file $midx &&
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
- git multi-pack-index write --bitmap &&
- test_path_is_file $midx &&
- test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
+ test-tool bitmap list-commits | sort >bitmaps &&
+ comm -13 bitmaps commits >before &&
+ test_line_count = 1 before &&
- test-tool bitmap list-commits | sort >bitmaps &&
- comm -13 bitmaps commits >before &&
- test_line_count = 1 before &&
+ perl -ne "printf(\"create refs/tags/include/%d \", $.); print" \
+ <before | git update-ref --stdin &&
- perl -ne "printf(\"create refs/tags/include/%d \", $.); print" \
- <before | git update-ref --stdin &&
+ rm -fr $midx-$(midx_checksum $objdir).bitmap &&
+ rm -fr $midx &&
- rm -fr $midx-$(midx_checksum $objdir).bitmap &&
- rm -fr $midx &&
+ git -c pack.preferBitmapTips=refs/tags/include \
+ multi-pack-index write --bitmap &&
+ test-tool bitmap list-commits | sort >bitmaps &&
+ comm -13 bitmaps commits >after &&
- git -c pack.preferBitmapTips=refs/tags/include \
- multi-pack-index write --bitmap &&
- test-tool bitmap list-commits | sort >bitmaps &&
- comm -13 bitmaps commits >after &&
+ ! test_cmp before after
+ )
+ '
- ! test_cmp before after
- )
-'
+ test_expect_success 'writing a bitmap with --refs-snapshot' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
-test_expect_success 'writing a bitmap with --refs-snapshot' '
- git init repo &&
- test_when_finished "rm -fr repo" &&
- (
- cd repo &&
+ test_commit one &&
+ test_commit two &&
- test_commit one &&
- test_commit two &&
+ git rev-parse one >snapshot &&
- git rev-parse one >snapshot &&
+ git repack -ad &&
- git repack -ad &&
+ # First, write a MIDX which see both refs/tags/one and
+ # refs/tags/two (causing both of those commits to receive
+ # bitmaps).
+ git multi-pack-index write --bitmap &&
- # First, write a MIDX which see both refs/tags/one and
- # refs/tags/two (causing both of those commits to receive
- # bitmaps).
- git multi-pack-index write --bitmap &&
+ test_path_is_file $midx &&
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
- test_path_is_file $midx &&
- test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
+ test-tool bitmap list-commits | sort >bitmaps &&
+ grep "$(git rev-parse one)" bitmaps &&
+ grep "$(git rev-parse two)" bitmaps &&
- test-tool bitmap list-commits | sort >bitmaps &&
- grep "$(git rev-parse one)" bitmaps &&
- grep "$(git rev-parse two)" bitmaps &&
+ rm -fr $midx-$(midx_checksum $objdir).bitmap &&
+ rm -fr $midx &&
- rm -fr $midx-$(midx_checksum $objdir).bitmap &&
- rm -fr $midx &&
+ # Then again, but with a refs snapshot which only sees
+ # refs/tags/one.
+ git multi-pack-index write --bitmap --refs-snapshot=snapshot &&
- # Then again, but with a refs snapshot which only sees
- # refs/tags/one.
- git multi-pack-index write --bitmap --refs-snapshot=snapshot &&
+ test_path_is_file $midx &&
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
- test_path_is_file $midx &&
- test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
+ test-tool bitmap list-commits | sort >bitmaps &&
+ grep "$(git rev-parse one)" bitmaps &&
+ ! grep "$(git rev-parse two)" bitmaps
+ )
+ '
- test-tool bitmap list-commits | sort >bitmaps &&
- grep "$(git rev-parse one)" bitmaps &&
- ! grep "$(git rev-parse two)" bitmaps
- )
-'
+ test_expect_success 'write a bitmap with --refs-snapshot (preferred tips)' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
-test_expect_success 'write a bitmap with --refs-snapshot (preferred tips)' '
- git init repo &&
- test_when_finished "rm -fr repo" &&
- (
- cd repo &&
+ test_commit_bulk --message="%s" 103 &&
- test_commit_bulk --message="%s" 103 &&
+ git log --format="%H" >commits.raw &&
+ sort <commits.raw >commits &&
- git log --format="%H" >commits.raw &&
- sort <commits.raw >commits &&
+ git log --format="create refs/tags/%s %H" HEAD >refs &&
+ git update-ref --stdin <refs &&
- git log --format="create refs/tags/%s %H" HEAD >refs &&
- git update-ref --stdin <refs &&
+ git multi-pack-index write --bitmap &&
+ test_path_is_file $midx &&
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
- git multi-pack-index write --bitmap &&
- test_path_is_file $midx &&
- test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
+ test-tool bitmap list-commits | sort >bitmaps &&
+ comm -13 bitmaps commits >before &&
+ test_line_count = 1 before &&
- test-tool bitmap list-commits | sort >bitmaps &&
- comm -13 bitmaps commits >before &&
- test_line_count = 1 before &&
+ (
+ grep -vf before commits.raw &&
+ # mark missing commits as preferred
+ sed "s/^/+/" before
+ ) >snapshot &&
+ rm -fr $midx-$(midx_checksum $objdir).bitmap &&
+ rm -fr $midx &&
+
+ git multi-pack-index write --bitmap --refs-snapshot=snapshot &&
+ test-tool bitmap list-commits | sort >bitmaps &&
+ comm -13 bitmaps commits >after &&
+
+ ! test_cmp before after
+ )
+ '
+
+ test_expect_success 'hash-cache values are propagated from pack bitmaps' '
+ rm -fr repo &&
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
(
- grep -vf before commits.raw &&
- # mark missing commits as preferred
- sed "s/^/+/" before
- ) >snapshot &&
+ cd repo &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
- rm -fr $midx-$(midx_checksum $objdir).bitmap &&
- rm -fr $midx &&
+ test_commit base &&
+ test_commit base2 &&
+ git repack -adb &&
- git multi-pack-index write --bitmap --refs-snapshot=snapshot &&
- test-tool bitmap list-commits | sort >bitmaps &&
- comm -13 bitmaps commits >after &&
+ test-tool bitmap dump-hashes >pack.raw &&
+ test_file_not_empty pack.raw &&
+ sort pack.raw >pack.hashes &&
- ! test_cmp before after
- )
-'
+ test_commit new &&
+ git repack &&
+ git multi-pack-index write --bitmap &&
-test_expect_success 'hash-cache values are propagated from pack bitmaps' '
- rm -fr repo &&
- git init repo &&
- test_when_finished "rm -fr repo" &&
- (
- cd repo &&
+ test-tool bitmap dump-hashes >midx.raw &&
+ sort midx.raw >midx.hashes &&
- test_commit base &&
- test_commit base2 &&
- git repack -adb &&
+ # ensure that every namehash in the pack bitmap can be found in
+ # the midx bitmap (i.e., that there are no oid-namehash pairs
+ # unique to the pack bitmap).
+ comm -23 pack.hashes midx.hashes >dropped.hashes &&
+ test_must_be_empty dropped.hashes
+ )
+ '
- test-tool bitmap dump-hashes >pack.raw &&
- test_file_not_empty pack.raw &&
- sort pack.raw >pack.hashes &&
+ test_expect_success 'no .bitmap is written without any objects' '
+ rm -fr repo &&
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
- test_commit new &&
- git repack &&
- git multi-pack-index write --bitmap &&
+ empty="$(git pack-objects $objdir/pack/pack </dev/null)" &&
+ cat >packs <<-EOF &&
+ pack-$empty.idx
+ EOF
- test-tool bitmap dump-hashes >midx.raw &&
- sort midx.raw >midx.hashes &&
+ git multi-pack-index write --bitmap --stdin-packs \
+ <packs 2>err &&
- # ensure that every namehash in the pack bitmap can be found in
- # the midx bitmap (i.e., that there are no oid-namehash pairs
- # unique to the pack bitmap).
- comm -23 pack.hashes midx.hashes >dropped.hashes &&
- test_must_be_empty dropped.hashes
- )
-'
+ grep "bitmap without any objects" err &&
+
+ test_path_is_file $midx &&
+ test_path_is_missing $midx-$(midx_checksum $objdir).bitmap
+ )
+ '
+
+ test_expect_success 'graceful fallback when missing reverse index' '
+ rm -fr repo &&
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
+
+ test_commit base &&
+
+ # write a pack and MIDX bitmap containing base
+ git repack -adb &&
+ git multi-pack-index write --bitmap &&
+
+ GIT_TEST_MIDX_READ_RIDX=0 \
+ git rev-list --use-bitmap-index HEAD 2>err &&
+ ! grep "ignoring extra bitmap file" err
+ )
+ '
+}
+
+test_midx_bitmap_cases
-test_expect_success 'no .bitmap is written without any objects' '
+test_midx_bitmap_cases "pack.writeBitmapLookupTable"
+
+test_expect_success 'multi-pack-index write writes lookup table if enabled' '
rm -fr repo &&
git init repo &&
test_when_finished "rm -fr repo" &&
(
cd repo &&
+ test_commit base &&
+ git config pack.writeBitmapLookupTable true &&
+ git repack -ad &&
+ GIT_TRACE2_EVENT="$(pwd)/trace" \
+ git multi-pack-index write --bitmap &&
+ grep "\"label\":\"writing_lookup_table\"" trace
+ )
+'
+
+test_expect_success 'preferred pack change with existing MIDX bitmap' '
+ git init preferred-pack-with-existing &&
+ (
+ cd preferred-pack-with-existing &&
+
+ test_commit base &&
+ test_commit other &&
+
+ git rev-list --objects --no-object-names base >p1.objects &&
+ git rev-list --objects --no-object-names other >p2.objects &&
- empty="$(git pack-objects $objdir/pack/pack </dev/null)" &&
- cat >packs <<-EOF &&
- pack-$empty.idx
- EOF
+ p1="$(git pack-objects "$objdir/pack/pack" \
+ --delta-base-offset <p1.objects)" &&
+ p2="$(git pack-objects "$objdir/pack/pack" \
+ --delta-base-offset <p2.objects)" &&
- git multi-pack-index write --bitmap --stdin-packs \
- <packs 2>err &&
+ # Generate a MIDX containing the first two packs,
+ # marking p1 as preferred, and ensure that it can be
+ # successfully cloned.
+ git multi-pack-index write --bitmap \
+ --preferred-pack="pack-$p1.pack" &&
+ test_path_is_file $midx &&
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
+ git clone --no-local . clone1 &&
- grep "bitmap without any objects" err &&
+ # Then generate a new pack which sorts ahead of any
+ # existing pack (by tweaking the pack prefix).
+ test_commit foo &&
+ git pack-objects --all --unpacked $objdir/pack/pack0 &&
+ # Generate a new MIDX which changes the preferred pack
+ # to a pack contained in the existing MIDX.
+ git multi-pack-index write --bitmap \
+ --preferred-pack="pack-$p2.pack" &&
test_path_is_file $midx &&
- test_path_is_missing $midx-$(midx_checksum $objdir).bitmap
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
+
+ # When the above circumstances are met, the preferred
+ # pack should change appropriately and clones should
+ # (still) succeed.
+ git clone --no-local . clone2
)
'
-test_expect_success 'graceful fallback when missing reverse index' '
+test_expect_success 'tagged commits are selected for bitmapping' '
rm -fr repo &&
git init repo &&
test_when_finished "rm -fr repo" &&
(
cd repo &&
- test_commit base &&
+ test_commit --annotate base &&
+ git repack -d &&
+
+ # Remove refs/heads/main which points at the commit directly,
+ # leaving only a reference to the annotated tag.
+ git branch -M main &&
+ git checkout base &&
+ git branch -d main &&
- # write a pack and MIDX bitmap containing base
- git repack -adb &&
git multi-pack-index write --bitmap &&
- GIT_TEST_MIDX_READ_RIDX=0 \
- git rev-list --use-bitmap-index HEAD 2>err &&
- ! grep "ignoring extra bitmap file" err
+ git rev-parse HEAD >want &&
+ test-tool bitmap list-commits >actual &&
+ grep $(cat want) actual
)
'
diff --git a/t/t5327-multi-pack-bitmaps-rev.sh b/t/t5327-multi-pack-bitmaps-rev.sh
index d30ba63..e65e311 100755
--- a/t/t5327-multi-pack-bitmaps-rev.sh
+++ b/t/t5327-multi-pack-bitmaps-rev.sh
@@ -17,7 +17,27 @@ GIT_TEST_MIDX_READ_RIDX=0
export GIT_TEST_MIDX_WRITE_REV
export GIT_TEST_MIDX_READ_RIDX
-midx_bitmap_core rev
-midx_bitmap_partial_tests rev
+test_midx_bitmap_rev () {
+ writeLookupTable=false
+
+ for i in "$@"
+ do
+ case $i in
+ "pack.writeBitmapLookupTable") writeLookupTable=true;;
+ esac
+ done
+
+ test_expect_success 'setup bitmap config' '
+ rm -rf * .git &&
+ git init &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"'
+ '
+
+ midx_bitmap_core rev
+ midx_bitmap_partial_tests rev
+}
+
+test_midx_bitmap_rev
+test_midx_bitmap_rev "pack.writeBitmapLookupTable"
test_done
diff --git a/t/t5329-pack-objects-cruft.sh b/t/t5329-pack-objects-cruft.sh
index 8968f7a..303f7a5 100755
--- a/t/t5329-pack-objects-cruft.sh
+++ b/t/t5329-pack-objects-cruft.sh
@@ -29,7 +29,8 @@ basic_cruft_pack_tests () {
while read oid
do
path="$objdir/$(test_oid_to_path "$oid")" &&
- printf "%s %d\n" "$oid" "$(test-tool chmtime --get "$path")"
+ printf "%s %d\n" "$oid" "$(test-tool chmtime --get "$path")" ||
+ echo "object list generation failed for $oid"
done |
sort -k1
) >expect &&
@@ -232,7 +233,7 @@ test_expect_success 'cruft tags rescue tagged objects' '
while read oid
do
test-tool chmtime -1000 \
- "$objdir/$(test_oid_to_path $oid)"
+ "$objdir/$(test_oid_to_path $oid)" || exit 1
done <objects &&
test-tool chmtime -500 \
@@ -272,7 +273,7 @@ test_expect_success 'cruft commits rescue parents, trees' '
while read object
do
test-tool chmtime -1000 \
- "$objdir/$(test_oid_to_path $object)"
+ "$objdir/$(test_oid_to_path $object)" || exit 1
done <objects &&
test-tool chmtime +500 "$objdir/$(test_oid_to_path \
$(git rev-parse HEAD))" &&
@@ -345,7 +346,7 @@ test_expect_success 'expired objects are pruned' '
while read object
do
test-tool chmtime -1000 \
- "$objdir/$(test_oid_to_path $object)"
+ "$objdir/$(test_oid_to_path $object)" || exit 1
done <objects &&
keep="$(basename "$(ls $packdir/pack-*.pack)")" &&
diff --git a/t/t5351-unpack-large-objects.sh b/t/t5351-unpack-large-objects.sh
index e936f91..8c8af99 100755
--- a/t/t5351-unpack-large-objects.sh
+++ b/t/t5351-unpack-large-objects.sh
@@ -5,6 +5,7 @@
test_description='git unpack-objects with large objects'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
prepare_dest () {
diff --git a/t/t5402-post-merge-hook.sh b/t/t5402-post-merge-hook.sh
index 915af2d..46ebdfb 100755
--- a/t/t5402-post-merge-hook.sh
+++ b/t/t5402-post-merge-hook.sh
@@ -7,6 +7,7 @@ test_description='Test the post-merge hook.'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index ee6d2dd..d18f282 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -407,6 +407,7 @@ test_expect_success 'in_vain not triggered before first ACK' '
'
test_expect_success 'in_vain resetted upon ACK' '
+ test_when_finished rm -f log trace2 &&
rm -rf myserver myclient &&
git init myserver &&
@@ -432,7 +433,8 @@ test_expect_success 'in_vain resetted upon ACK' '
# first. The 256th commit is common between the client and the server,
# and should reset in_vain. This allows negotiation to continue until
# the client reports that first_anotherbranch_commit is common.
- git -C myclient fetch --progress origin main 2>log &&
+ GIT_TRACE2_EVENT="$(pwd)/trace2" git -C myclient fetch --progress origin main 2>log &&
+ grep \"key\":\"total_rounds\",\"value\":\"6\" trace2 &&
test_i18ngrep "Total 3 " log
'
diff --git a/t/t5503-tagfollow.sh b/t/t5503-tagfollow.sh
index 195fc64..5ebbaa4 100755
--- a/t/t5503-tagfollow.sh
+++ b/t/t5503-tagfollow.sh
@@ -5,6 +5,7 @@ test_description='test automatic tag following'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# End state of the repository:
diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh
index b0b795a..ac4099c 100755
--- a/t/t5504-fetch-receive-strict.sh
+++ b/t/t5504-fetch-receive-strict.sh
@@ -352,4 +352,21 @@ test_expect_success \
grep "Cannot demote unterminatedheader" act
'
+test_expect_success 'badFilemode is not a strict error' '
+ git init --bare badmode.git &&
+ tree=$(
+ cd badmode.git &&
+ blob=$(echo blob | git hash-object -w --stdin | hex2oct) &&
+ printf "123456 foo\0${blob}" |
+ git hash-object -t tree --stdin -w --literally
+ ) &&
+
+ rm -rf dst.git &&
+ git init --bare dst.git &&
+ git -C dst.git config transfer.fsckObjects true &&
+
+ git -C badmode.git push ../dst.git $tree:refs/tags/tree 2>err &&
+ grep "$tree: badFilemode" err
+'
+
test_done
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index 6c7370f..43b7bcd 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -241,6 +241,26 @@ test_expect_success 'add invalid foreign_vcs remote' '
test_cmp expect actual
'
+test_expect_success 'without subcommand' '
+ echo origin >expect &&
+ git -C test remote >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'without subcommand accepts -v' '
+ cat >expect <<-EOF &&
+ origin $(pwd)/one (fetch)
+ origin $(pwd)/one (push)
+ EOF
+ git -C test remote -v >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'without subcommand does not take arguments' '
+ test_expect_code 129 git -C test remote origin 2>err &&
+ grep "^error: unknown subcommand:" err
+'
+
cat >test/expect <<EOF
* remote origin
Fetch URL: $(pwd)/one
@@ -882,6 +902,17 @@ test_expect_success 'rename a remote renames repo remote.pushDefault but keeps g
)
'
+test_expect_success 'rename handles remote without fetch refspec' '
+ git clone --bare one no-refspec.git &&
+ # confirm assumption that bare clone does not create refspec
+ test_expect_code 5 \
+ git -C no-refspec.git config --unset-all remote.origin.fetch &&
+ git -C no-refspec.git config remote.origin.url >expect &&
+ git -C no-refspec.git remote rename origin foo &&
+ git -C no-refspec.git config remote.foo.url >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'rename does not update a non-default fetch refspec' '
git clone one four.one &&
(
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index b45879a..c0b745e 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -790,6 +790,7 @@ test_expect_success 'fetch.writeCommitGraph' '
'
test_expect_success 'fetch.writeCommitGraph with submodules' '
+ test_config_global protocol.file.allow always &&
git clone dups super &&
(
cd super &&
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index f3356f9..98a27a29 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -200,7 +200,10 @@ test_expect_success 'push with negotiation' '
test_commit -C testrepo unrelated_commit &&
git -C testrepo config receive.hideRefs refs/remotes/origin/first_commit &&
test_when_finished "rm event" &&
- GIT_TRACE2_EVENT="$(pwd)/event" git -c protocol.version=2 -c push.negotiate=1 push testrepo refs/heads/main:refs/remotes/origin/main &&
+ GIT_TRACE2_EVENT="$(pwd)/event" \
+ git -c protocol.version=2 -c push.negotiate=1 \
+ push testrepo refs/heads/main:refs/remotes/origin/main &&
+ grep \"key\":\"total_rounds\",\"value\":\"1\" event &&
grep_wrote 2 event # 1 commit, 1 tree
'
@@ -219,12 +222,16 @@ test_expect_success 'push with negotiation proceeds anyway even if negotiation f
test_expect_success 'push with negotiation does not attempt to fetch submodules' '
mk_empty submodule_upstream &&
test_commit -C submodule_upstream submodule_commit &&
+ test_config_global protocol.file.allow always &&
git submodule add ./submodule_upstream submodule &&
mk_empty testrepo &&
git push testrepo $the_first_commit:refs/remotes/origin/first_commit &&
test_commit -C testrepo unrelated_commit &&
git -C testrepo config receive.hideRefs refs/remotes/origin/first_commit &&
- git -c submodule.recurse=true -c protocol.version=2 -c push.negotiate=1 push testrepo refs/heads/main:refs/remotes/origin/main 2>err &&
+ GIT_TRACE2_EVENT="$(pwd)/event" git -c submodule.recurse=true \
+ -c protocol.version=2 -c push.negotiate=1 \
+ push testrepo refs/heads/main:refs/remotes/origin/main 2>err &&
+ grep \"key\":\"total_rounds\",\"value\":\"1\" event &&
! grep "Fetching submodule" err
'
@@ -1846,55 +1853,24 @@ test_expect_success 'refuse to push a hidden ref, and make sure do not pollute t
test_dir_is_empty testrepo/.git/objects/pack
'
-test_expect_success LIBCURL 'fetch warns or fails when using username:password' '
- message="URL '\''https://username:<redacted>@localhost/'\'' uses plaintext credentials" &&
- test_must_fail git -c transfer.credentialsInUrl=allow fetch https://username:password@localhost 2>err &&
- ! grep "$message" err &&
-
- test_must_fail git -c transfer.credentialsInUrl=warn fetch https://username:password@localhost 2>err &&
- grep "warning: $message" err >warnings &&
- test_line_count = 3 warnings &&
-
- test_must_fail git -c transfer.credentialsInUrl=die fetch https://username:password@localhost 2>err &&
- grep "fatal: $message" err >warnings &&
- test_line_count = 1 warnings &&
-
- test_must_fail git -c transfer.credentialsInUrl=die fetch https://username:@localhost 2>err &&
- grep "fatal: $message" err >warnings &&
- test_line_count = 1 warnings
-'
-
-
-test_expect_success LIBCURL 'push warns or fails when using username:password' '
- message="URL '\''https://username:<redacted>@localhost/'\'' uses plaintext credentials" &&
- test_must_fail git -c transfer.credentialsInUrl=allow push https://username:password@localhost 2>err &&
- ! grep "$message" err &&
-
- test_must_fail git -c transfer.credentialsInUrl=warn push https://username:password@localhost 2>err &&
- grep "warning: $message" err >warnings &&
- test_must_fail git -c transfer.credentialsInUrl=die push https://username:password@localhost 2>err &&
- grep "fatal: $message" err >warnings &&
- test_line_count = 1 warnings
-'
-
test_expect_success 'push with config push.useBitmaps' '
mk_test testrepo heads/main &&
git checkout main &&
test_unconfig push.useBitmaps &&
GIT_TRACE2_EVENT="$PWD/default" \
- git push testrepo main:test &&
+ git push --quiet testrepo main:test &&
test_subcommand git pack-objects --all-progress-implied --revs --stdout \
--thin --delta-base-offset -q <default &&
test_config push.useBitmaps true &&
GIT_TRACE2_EVENT="$PWD/true" \
- git push testrepo main:test2 &&
+ git push --quiet testrepo main:test2 &&
test_subcommand git pack-objects --all-progress-implied --revs --stdout \
--thin --delta-base-offset -q <true &&
test_config push.useBitmaps false &&
GIT_TRACE2_EVENT="$PWD/false" \
- git push testrepo main:test3 &&
+ git push --quiet testrepo main:test3 &&
test_subcommand git pack-objects --all-progress-implied --revs --stdout \
--thin --delta-base-offset -q --no-use-bitmap-index <false
'
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index 0818080..0b72112 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -218,6 +218,23 @@ test_expect_success 'fail if upstream branch does not exist' '
test_cmp expect file
'
+test_expect_success 'fetch upstream branch even if refspec excludes it' '
+ # the branch names are not important here except that
+ # the first one must not be a prefix of the second,
+ # since otherwise the ref-prefix protocol extension
+ # would match both
+ git branch in-refspec HEAD^ &&
+ git branch not-in-refspec HEAD &&
+ git init -b in-refspec downstream &&
+ git -C downstream remote add -t in-refspec origin "file://$(pwd)/.git" &&
+ git -C downstream config branch.in-refspec.remote origin &&
+ git -C downstream config branch.in-refspec.merge refs/heads/not-in-refspec &&
+ git -C downstream pull &&
+ git rev-parse --verify not-in-refspec >expect &&
+ git -C downstream rev-parse --verify HEAD >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'fail if the index has unresolved entries' '
git checkout -b third second^ &&
test_when_finished "git checkout -f copy && git branch -D third" &&
diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh
index a301b56..b9546ef 100755
--- a/t/t5526-fetch-submodules.sh
+++ b/t/t5526-fetch-submodules.sh
@@ -127,6 +127,7 @@ verify_fetch_result () {
}
test_expect_success setup '
+ git config --global protocol.file.allow always &&
mkdir deepsubmodule &&
(
cd deepsubmodule &&
@@ -177,6 +178,7 @@ test_expect_success "submodule.recurse option triggers recursive fetch" '
'
test_expect_success "fetch --recurse-submodules -j2 has the same output behaviour" '
+ test_when_finished "rm -f trace.out" &&
add_submodule_commits &&
(
cd downstream &&
@@ -704,17 +706,30 @@ test_expect_success "'fetch.recurseSubmodules=on-demand' works also without .git
test_expect_success 'fetching submodules respects parallel settings' '
git config fetch.recurseSubmodules true &&
+ test_when_finished "rm -f downstream/trace.out" &&
(
cd downstream &&
GIT_TRACE=$(pwd)/trace.out git fetch &&
grep "1 tasks" trace.out &&
+ >trace.out &&
+
GIT_TRACE=$(pwd)/trace.out git fetch --jobs 7 &&
grep "7 tasks" trace.out &&
+ >trace.out &&
+
git config submodule.fetchJobs 8 &&
GIT_TRACE=$(pwd)/trace.out git fetch &&
grep "8 tasks" trace.out &&
+ >trace.out &&
+
GIT_TRACE=$(pwd)/trace.out git fetch --jobs 9 &&
- grep "9 tasks" trace.out
+ grep "9 tasks" trace.out &&
+ >trace.out &&
+
+ GIT_TRACE=$(pwd)/trace.out git -c submodule.fetchJobs=0 fetch &&
+ grep "preparing to run up to [0-9]* tasks" trace.out &&
+ ! grep "up to 0 tasks" trace.out &&
+ >trace.out
)
'
diff --git a/t/t5531-deep-submodule-push.sh b/t/t5531-deep-submodule-push.sh
index 3f58b51..302e4cb 100755
--- a/t/t5531-deep-submodule-push.sh
+++ b/t/t5531-deep-submodule-push.sh
@@ -512,6 +512,56 @@ test_expect_success 'push only unpushed submodules recursively' '
test_cmp expected_pub actual_pub
'
+setup_subsub () {
+ git init upstream &&
+ git init upstream/sub &&
+ git init upstream/sub/deepsub &&
+ test_commit -C upstream/sub/deepsub innermost &&
+ git -C upstream/sub submodule add ./deepsub deepsub &&
+ git -C upstream/sub commit -m middle &&
+ git -C upstream submodule add ./sub sub &&
+ git -C upstream commit -m outermost &&
+
+ git -c protocol.file.allow=always clone --recurse-submodules upstream downstream &&
+ git -C downstream/sub/deepsub checkout -b downstream-branch &&
+ git -C downstream/sub checkout -b downstream-branch &&
+ git -C downstream checkout -b downstream-branch
+}
+
+new_downstream_commits () {
+ test_commit -C downstream/sub/deepsub new-innermost &&
+ git -C downstream/sub add deepsub &&
+ git -C downstream/sub commit -m new-middle &&
+ git -C downstream add sub &&
+ git -C downstream commit -m new-outermost
+}
+
+test_expect_success 'push with push.recurseSubmodules=only on superproject' '
+ test_when_finished rm -rf upstream downstream &&
+ setup_subsub &&
+ new_downstream_commits &&
+ git -C downstream config push.recurseSubmodules only &&
+ git -C downstream push origin downstream-branch &&
+
+ test_must_fail git -C upstream rev-parse refs/heads/downstream-branch &&
+ git -C upstream/sub rev-parse refs/heads/downstream-branch &&
+ test_must_fail git -C upstream/sub/deepsub rev-parse refs/heads/downstream-branch
+'
+
+test_expect_success 'push with push.recurseSubmodules=only on superproject and top-level submodule' '
+ test_when_finished rm -rf upstream downstream &&
+ setup_subsub &&
+ new_downstream_commits &&
+ git -C downstream config push.recurseSubmodules only &&
+ git -C downstream/sub config push.recurseSubmodules only &&
+ git -C downstream push origin downstream-branch 2> err &&
+
+ test_must_fail git -C upstream rev-parse refs/heads/downstream-branch &&
+ git -C upstream/sub rev-parse refs/heads/downstream-branch &&
+ git -C upstream/sub/deepsub rev-parse refs/heads/downstream-branch &&
+ grep "recursing into submodule with push.recurseSubmodules=only; using on-demand instead" err
+'
+
test_expect_success 'push propagating the remotes name to a submodule' '
git -C work remote add origin ../pub.git &&
git -C work remote add pub ../pub.git &&
diff --git a/t/t5537-fetch-shallow.sh b/t/t5537-fetch-shallow.sh
index 10e9a7f..37f7547 100755
--- a/t/t5537-fetch-shallow.sh
+++ b/t/t5537-fetch-shallow.sh
@@ -162,6 +162,8 @@ test_expect_success 'fetch --update-shallow' '
'
test_expect_success 'fetch --update-shallow into a repo with submodules' '
+ test_config_global protocol.file.allow always &&
+
git init a-submodule &&
test_commit -C a-submodule foo &&
@@ -175,7 +177,8 @@ test_expect_success 'fetch --update-shallow into a repo with submodules' '
test_expect_success 'fetch --update-shallow a commit that is also a shallow point into a repo with submodules' '
test_when_finished "rm -rf repo-with-sub" &&
git init repo-with-sub &&
- git -C repo-with-sub submodule add ../a-submodule a-submodule &&
+ git -c protocol.file.allow=always -C repo-with-sub \
+ submodule add ../a-submodule a-submodule &&
git -C repo-with-sub commit -m "added submodule" &&
SHALLOW=$(cat shallow/.git/shallow) &&
diff --git a/t/t5545-push-options.sh b/t/t5545-push-options.sh
index 2142283..a158e7d 100755
--- a/t/t5545-push-options.sh
+++ b/t/t5545-push-options.sh
@@ -119,6 +119,7 @@ test_expect_success 'push options and submodules' '
test_commit -C parent one &&
git -C parent push --mirror up &&
+ test_config_global protocol.file.allow always &&
git -C parent submodule add ../upstream workbench &&
git -C parent/workbench remote add up ../../upstream &&
git -C parent commit -m "add submodule" &&
diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh
index d7cf85f..8f182a3 100755
--- a/t/t5550-http-fetch-dumb.sh
+++ b/t/t5550-http-fetch-dumb.sh
@@ -234,7 +234,7 @@ test_expect_success 'http-fetch --packfile' '
--index-pack-arg=--keep \
"$HTTPD_URL"/dumb/repo_pack.git/$p >out &&
- grep "^keep.[0-9a-f]\{16,\}$" out &&
+ grep -E "^keep.[0-9a-f]{16,}$" out &&
cut -c6- out >packhash &&
# Ensure that the expected files are generated
diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh
index 245532d..bc0719a 100755
--- a/t/t5551-http-fetch-smart.sh
+++ b/t/t5551-http-fetch-smart.sh
@@ -1,13 +1,19 @@
#!/bin/sh
-test_description='test smart fetching over http via http-backend'
+: ${HTTP_PROTO:=HTTP}
+test_description="test smart fetching over http via http-backend ($HTTP_PROTO)"
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-httpd.sh
+test "$HTTP_PROTO" = "HTTP/2" && enable_http2
start_httpd
+test_expect_success HTTP2 'enable client-side http/2' '
+ git config --global http.version HTTP/2
+'
+
test_expect_success 'setup repository' '
git config push.default matching &&
echo content >file &&
@@ -181,8 +187,8 @@ test_expect_success 'no-op half-auth fetch does not require a password' '
# This is not possible with protocol v2, since both objects and refs
# are obtained from the "git-upload-pack" path. A solution to this is
# to teach the server and client to be able to inline ls-refs requests
- # as an Extra Parameter (see pack-protocol.txt), so that "info/refs"
- # can serve refs, just like it does in protocol v0.
+ # as an Extra Parameter (see "git help gitformat-pack-protocol"), so that
+ # "info/refs" can serve refs, just like it does in protocol v0.
GIT_TEST_PROTOCOL_VERSION=0 git --git-dir=half-auth fetch &&
expect_askpass none
'
@@ -347,7 +353,10 @@ test_expect_success CMDLINE_LIMIT \
test_expect_success 'large fetch-pack requests can be sent using chunked encoding' '
GIT_TRACE_CURL=true git -c http.postbuffer=65536 \
clone --bare "$HTTPD_URL/smart/repo.git" split.git 2>err &&
- grep "^=> Send header: Transfer-Encoding: chunked" err
+ {
+ test_have_prereq HTTP2 ||
+ grep "^=> Send header: Transfer-Encoding: chunked" err
+ }
'
test_expect_success 'test allowreachablesha1inwant' '
@@ -580,4 +589,81 @@ test_expect_success 'passing hostname resolution information works' '
git -c "http.curloptResolve=$BOGUS_HOST:$LIB_HTTPD_PORT:127.0.0.1" ls-remote "$BOGUS_HTTPD_URL/smart/repo.git" >/dev/null
'
+# here user%40host is the URL-encoded version of user@host,
+# which is our intentionally-odd username to catch parsing errors
+url_user=$HTTPD_URL_USER/auth/smart/repo.git
+url_userpass=$HTTPD_URL_USER_PASS/auth/smart/repo.git
+url_userblank=$HTTPD_PROTO://user%40host:@$HTTPD_DEST/auth/smart/repo.git
+message="URL .*:<redacted>@.* uses plaintext credentials"
+
+test_expect_success 'clone warns or fails when using username:password' '
+ test_when_finished "rm -rf attempt*" &&
+
+ git -c transfer.credentialsInUrl=allow \
+ clone $url_userpass attempt1 2>err &&
+ ! grep "$message" err &&
+
+ git -c transfer.credentialsInUrl=warn \
+ clone $url_userpass attempt2 2>err &&
+ grep "warning: $message" err >warnings &&
+ test_line_count -ge 1 warnings &&
+
+ test_must_fail git -c transfer.credentialsInUrl=die \
+ clone $url_userpass attempt3 2>err &&
+ grep "fatal: $message" err >warnings &&
+ test_line_count -ge 1 warnings &&
+
+ test_must_fail git -c transfer.credentialsInUrl=die \
+ clone $url_userblank attempt4 2>err &&
+ grep "fatal: $message" err >warnings &&
+ test_line_count -ge 1 warnings
+'
+
+test_expect_success 'clone does not detect username:password when it is https://username@domain:port/' '
+ test_when_finished "rm -rf attempt1" &&
+
+ # we are relying on lib-httpd for url construction, so document our
+ # assumptions
+ case "$HTTPD_URL_USER" in
+ *:[0-9]*) : ok ;;
+ *) BUG "httpd url does not have port: $HTTPD_URL_USER"
+ esac &&
+
+ git -c transfer.credentialsInUrl=warn clone $url_user attempt1 2>err &&
+ ! grep "uses plaintext credentials" err
+'
+
+test_expect_success 'fetch warns or fails when using username:password' '
+ git -c transfer.credentialsInUrl=allow fetch $url_userpass 2>err &&
+ ! grep "$message" err &&
+
+ git -c transfer.credentialsInUrl=warn fetch $url_userpass 2>err &&
+ grep "warning: $message" err >warnings &&
+ test_line_count -ge 1 warnings &&
+
+ test_must_fail git -c transfer.credentialsInUrl=die \
+ fetch $url_userpass 2>err &&
+ grep "fatal: $message" err >warnings &&
+ test_line_count -ge 1 warnings &&
+
+ test_must_fail git -c transfer.credentialsInUrl=die \
+ fetch $url_userblank 2>err &&
+ grep "fatal: $message" err >warnings &&
+ test_line_count -ge 1 warnings
+'
+
+
+test_expect_success 'push warns or fails when using username:password' '
+ git -c transfer.credentialsInUrl=allow push $url_userpass 2>err &&
+ ! grep "$message" err &&
+
+ git -c transfer.credentialsInUrl=warn push $url_userpass 2>err &&
+ grep "warning: $message" err >warnings &&
+
+ test_must_fail git -c transfer.credentialsInUrl=die \
+ push $url_userpass 2>err &&
+ grep "fatal: $message" err >warnings &&
+ test_line_count -ge 1 warnings
+'
+
test_done
diff --git a/t/t5557-http-get.sh b/t/t5557-http-get.sh
new file mode 100755
index 0000000..76a4bbd
--- /dev/null
+++ b/t/t5557-http-get.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+test_description='test downloading a file by URL'
+
+TEST_PASSES_SANITIZE_LEAK=true
+
+. ./test-lib.sh
+
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success 'get by URL: 404' '
+ test_when_finished "rm -f file.temp" &&
+ url="$HTTPD_URL/none.txt" &&
+ cat >input <<-EOF &&
+ capabilities
+ get $url file1
+ EOF
+
+ test_must_fail git remote-http $url <input 2>err &&
+ test_path_is_missing file1 &&
+ grep "failed to download file at URL" err
+'
+
+test_expect_success 'get by URL: 200' '
+ echo data >"$HTTPD_DOCUMENT_ROOT_PATH/exists.txt" &&
+
+ url="$HTTPD_URL/exists.txt" &&
+ cat >input <<-EOF &&
+ capabilities
+ get $url file2
+
+ EOF
+
+ git remote-http $url <input &&
+ test_cmp "$HTTPD_DOCUMENT_ROOT_PATH/exists.txt" file2
+'
+
+test_done
diff --git a/t/t5558-clone-bundle-uri.sh b/t/t5558-clone-bundle-uri.sh
new file mode 100755
index 0000000..9155f31
--- /dev/null
+++ b/t/t5558-clone-bundle-uri.sh
@@ -0,0 +1,356 @@
+#!/bin/sh
+
+test_description='test fetching bundles with --bundle-uri'
+
+. ./test-lib.sh
+
+test_expect_success 'fail to clone from non-existent file' '
+ test_when_finished rm -rf test &&
+ git clone --bundle-uri="$(pwd)/does-not-exist" . test 2>err &&
+ grep "failed to download bundle from URI" err
+'
+
+test_expect_success 'fail to clone from non-bundle file' '
+ test_when_finished rm -rf test &&
+ echo bogus >bogus &&
+ git clone --bundle-uri="$(pwd)/bogus" . test 2>err &&
+ grep "is not a bundle" err
+'
+
+test_expect_success 'create bundle' '
+ git init clone-from &&
+ git -C clone-from checkout -b topic &&
+ test_commit -C clone-from A &&
+ test_commit -C clone-from B &&
+ git -C clone-from bundle create B.bundle topic
+'
+
+test_expect_success 'clone with path bundle' '
+ git clone --bundle-uri="clone-from/B.bundle" \
+ clone-from clone-path &&
+ git -C clone-path rev-parse refs/bundles/topic >actual &&
+ git -C clone-from rev-parse topic >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'clone with file:// bundle' '
+ git clone --bundle-uri="file://$(pwd)/clone-from/B.bundle" \
+ clone-from clone-file &&
+ git -C clone-file rev-parse refs/bundles/topic >actual &&
+ git -C clone-from rev-parse topic >expect &&
+ test_cmp expect actual
+'
+
+# To get interesting tests for bundle lists, we need to construct a
+# somewhat-interesting commit history.
+#
+# ---------------- bundle-4
+#
+# 4
+# / \
+# ----|---|------- bundle-3
+# | |
+# | 3
+# | |
+# ----|---|------- bundle-2
+# | |
+# 2 |
+# | |
+# ----|---|------- bundle-1
+# \ /
+# 1
+# |
+# (previous commits)
+test_expect_success 'construct incremental bundle list' '
+ (
+ cd clone-from &&
+ git checkout -b base &&
+ test_commit 1 &&
+ git checkout -b left &&
+ test_commit 2 &&
+ git checkout -b right base &&
+ test_commit 3 &&
+ git checkout -b merge left &&
+ git merge right -m "4" &&
+
+ git bundle create bundle-1.bundle base &&
+ git bundle create bundle-2.bundle base..left &&
+ git bundle create bundle-3.bundle base..right &&
+ git bundle create bundle-4.bundle merge --not left right
+ )
+'
+
+test_expect_success 'clone bundle list (file, no heuristic)' '
+ cat >bundle-list <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+
+ [bundle "bundle-1"]
+ uri = file://$(pwd)/clone-from/bundle-1.bundle
+
+ [bundle "bundle-2"]
+ uri = file://$(pwd)/clone-from/bundle-2.bundle
+
+ [bundle "bundle-3"]
+ uri = file://$(pwd)/clone-from/bundle-3.bundle
+
+ [bundle "bundle-4"]
+ uri = file://$(pwd)/clone-from/bundle-4.bundle
+ EOF
+
+ git clone --bundle-uri="file://$(pwd)/bundle-list" \
+ clone-from clone-list-file 2>err &&
+ ! grep "Repository lacks these prerequisite commits" err &&
+
+ git -C clone-from for-each-ref --format="%(objectname)" >oids &&
+ git -C clone-list-file cat-file --batch-check <oids &&
+
+ git -C clone-list-file for-each-ref --format="%(refname)" >refs &&
+ grep "refs/bundles/" refs >actual &&
+ cat >expect <<-\EOF &&
+ refs/bundles/base
+ refs/bundles/left
+ refs/bundles/merge
+ refs/bundles/right
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'clone bundle list (file, all mode, some failures)' '
+ cat >bundle-list <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+
+ # Does not exist. Should be skipped.
+ [bundle "bundle-0"]
+ uri = file://$(pwd)/clone-from/bundle-0.bundle
+
+ [bundle "bundle-1"]
+ uri = file://$(pwd)/clone-from/bundle-1.bundle
+
+ [bundle "bundle-2"]
+ uri = file://$(pwd)/clone-from/bundle-2.bundle
+
+ # No bundle-3 means bundle-4 will not apply.
+
+ [bundle "bundle-4"]
+ uri = file://$(pwd)/clone-from/bundle-4.bundle
+
+ # Does not exist. Should be skipped.
+ [bundle "bundle-5"]
+ uri = file://$(pwd)/clone-from/bundle-5.bundle
+ EOF
+
+ GIT_TRACE2_PERF=1 \
+ git clone --bundle-uri="file://$(pwd)/bundle-list" \
+ clone-from clone-all-some 2>err &&
+ ! grep "Repository lacks these prerequisite commits" err &&
+ ! grep "fatal" err &&
+ grep "warning: failed to download bundle from URI" err &&
+
+ git -C clone-from for-each-ref --format="%(objectname)" >oids &&
+ git -C clone-all-some cat-file --batch-check <oids &&
+
+ git -C clone-all-some for-each-ref --format="%(refname)" >refs &&
+ grep "refs/bundles/" refs >actual &&
+ cat >expect <<-\EOF &&
+ refs/bundles/base
+ refs/bundles/left
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'clone bundle list (file, all mode, all failures)' '
+ cat >bundle-list <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+
+ # Does not exist. Should be skipped.
+ [bundle "bundle-0"]
+ uri = file://$(pwd)/clone-from/bundle-0.bundle
+
+ # Does not exist. Should be skipped.
+ [bundle "bundle-5"]
+ uri = file://$(pwd)/clone-from/bundle-5.bundle
+ EOF
+
+ git clone --bundle-uri="file://$(pwd)/bundle-list" \
+ clone-from clone-all-fail 2>err &&
+ ! grep "Repository lacks these prerequisite commits" err &&
+ ! grep "fatal" err &&
+ grep "warning: failed to download bundle from URI" err &&
+
+ git -C clone-from for-each-ref --format="%(objectname)" >oids &&
+ git -C clone-all-fail cat-file --batch-check <oids &&
+
+ git -C clone-all-fail for-each-ref --format="%(refname)" >refs &&
+ ! grep "refs/bundles/" refs
+'
+
+test_expect_success 'clone bundle list (file, any mode)' '
+ cat >bundle-list <<-EOF &&
+ [bundle]
+ version = 1
+ mode = any
+
+ # Does not exist. Should be skipped.
+ [bundle "bundle-0"]
+ uri = file://$(pwd)/clone-from/bundle-0.bundle
+
+ [bundle "bundle-1"]
+ uri = file://$(pwd)/clone-from/bundle-1.bundle
+
+ # Does not exist. Should be skipped.
+ [bundle "bundle-5"]
+ uri = file://$(pwd)/clone-from/bundle-5.bundle
+ EOF
+
+ git clone --bundle-uri="file://$(pwd)/bundle-list" \
+ clone-from clone-any-file 2>err &&
+ ! grep "Repository lacks these prerequisite commits" err &&
+
+ git -C clone-from for-each-ref --format="%(objectname)" >oids &&
+ git -C clone-any-file cat-file --batch-check <oids &&
+
+ git -C clone-any-file for-each-ref --format="%(refname)" >refs &&
+ grep "refs/bundles/" refs >actual &&
+ cat >expect <<-\EOF &&
+ refs/bundles/base
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'clone bundle list (file, any mode, all failures)' '
+ cat >bundle-list <<-EOF &&
+ [bundle]
+ version = 1
+ mode = any
+
+ # Does not exist. Should be skipped.
+ [bundle "bundle-0"]
+ uri = $HTTPD_URL/bundle-0.bundle
+
+ # Does not exist. Should be skipped.
+ [bundle "bundle-5"]
+ uri = $HTTPD_URL/bundle-5.bundle
+ EOF
+
+ git clone --bundle-uri="file://$(pwd)/bundle-list" \
+ clone-from clone-any-fail 2>err &&
+ ! grep "fatal" err &&
+ grep "warning: failed to download bundle from URI" err &&
+
+ git -C clone-from for-each-ref --format="%(objectname)" >oids &&
+ git -C clone-any-fail cat-file --batch-check <oids &&
+
+ git -C clone-any-fail for-each-ref --format="%(refname)" >refs &&
+ ! grep "refs/bundles/" refs
+'
+
+#########################################################################
+# HTTP tests begin here
+
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success 'fail to fetch from non-existent HTTP URL' '
+ test_when_finished rm -rf test &&
+ git clone --bundle-uri="$HTTPD_URL/does-not-exist" . test 2>err &&
+ grep "failed to download bundle from URI" err
+'
+
+test_expect_success 'fail to fetch from non-bundle HTTP URL' '
+ test_when_finished rm -rf test &&
+ echo bogus >"$HTTPD_DOCUMENT_ROOT_PATH/bogus" &&
+ git clone --bundle-uri="$HTTPD_URL/bogus" . test 2>err &&
+ grep "is not a bundle" err
+'
+
+test_expect_success 'clone HTTP bundle' '
+ cp clone-from/B.bundle "$HTTPD_DOCUMENT_ROOT_PATH/B.bundle" &&
+
+ git clone --no-local --mirror clone-from \
+ "$HTTPD_DOCUMENT_ROOT_PATH/fetch.git" &&
+
+ git clone --bundle-uri="$HTTPD_URL/B.bundle" \
+ "$HTTPD_URL/smart/fetch.git" clone-http &&
+ git -C clone-http rev-parse refs/bundles/topic >actual &&
+ git -C clone-from rev-parse topic >expect &&
+ test_cmp expect actual &&
+
+ test_config -C clone-http log.excludedecoration refs/bundle/
+'
+
+test_expect_success 'clone bundle list (HTTP, no heuristic)' '
+ cp clone-from/bundle-*.bundle "$HTTPD_DOCUMENT_ROOT_PATH/" &&
+ cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+
+ [bundle "bundle-1"]
+ uri = $HTTPD_URL/bundle-1.bundle
+
+ [bundle "bundle-2"]
+ uri = $HTTPD_URL/bundle-2.bundle
+
+ [bundle "bundle-3"]
+ uri = $HTTPD_URL/bundle-3.bundle
+
+ [bundle "bundle-4"]
+ uri = $HTTPD_URL/bundle-4.bundle
+ EOF
+
+ git clone --bundle-uri="$HTTPD_URL/bundle-list" \
+ clone-from clone-list-http 2>err &&
+ ! grep "Repository lacks these prerequisite commits" err &&
+
+ git -C clone-from for-each-ref --format="%(objectname)" >oids &&
+ git -C clone-list-http cat-file --batch-check <oids
+'
+
+test_expect_success 'clone bundle list (HTTP, any mode)' '
+ cp clone-from/bundle-*.bundle "$HTTPD_DOCUMENT_ROOT_PATH/" &&
+ cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle]
+ version = 1
+ mode = any
+
+ # Does not exist. Should be skipped.
+ [bundle "bundle-0"]
+ uri = $HTTPD_URL/bundle-0.bundle
+
+ [bundle "bundle-1"]
+ uri = $HTTPD_URL/bundle-1.bundle
+
+ # Does not exist. Should be skipped.
+ [bundle "bundle-5"]
+ uri = $HTTPD_URL/bundle-5.bundle
+ EOF
+
+ git clone --bundle-uri="$HTTPD_URL/bundle-list" \
+ clone-from clone-any-http 2>err &&
+ ! grep "fatal" err &&
+ grep "warning: failed to download bundle from URI" err &&
+
+ git -C clone-from for-each-ref --format="%(objectname)" >oids &&
+ git -C clone-any-http cat-file --batch-check <oids &&
+
+ git -C clone-list-file for-each-ref --format="%(refname)" >refs &&
+ grep "refs/bundles/" refs >actual &&
+ cat >expect <<-\EOF &&
+ refs/bundles/base
+ refs/bundles/left
+ refs/bundles/merge
+ refs/bundles/right
+ EOF
+ test_cmp expect actual
+'
+
+# Do not add tests here unless they use the HTTP server, as they will
+# not run unless the HTTP dependencies exist.
+
+test_done
diff --git a/t/t5559-http-fetch-smart-http2.sh b/t/t5559-http-fetch-smart-http2.sh
new file mode 100755
index 0000000..9eece71
--- /dev/null
+++ b/t/t5559-http-fetch-smart-http2.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+HTTP_PROTO=HTTP/2
+. ./t5551-http-fetch-smart.sh
diff --git a/t/t5572-pull-submodule.sh b/t/t5572-pull-submodule.sh
index a35396f..09097ef 100755
--- a/t/t5572-pull-submodule.sh
+++ b/t/t5572-pull-submodule.sh
@@ -52,6 +52,10 @@ then
fi
test_submodule_switch_func "git_pull_noff"
+test_expect_success 'setup' '
+ git config --global protocol.file.allow always
+'
+
test_expect_success 'pull --recurse-submodule setup' '
test_create_repo child &&
test_commit -C child bar &&
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index cf3be05..b2524a2 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -71,29 +71,6 @@ test_expect_success 'clone respects GIT_WORK_TREE' '
'
-test_expect_success LIBCURL 'clone warns or fails when using username:password' '
- message="URL '\''https://username:<redacted>@localhost/'\'' uses plaintext credentials" &&
- test_must_fail git -c transfer.credentialsInUrl=allow clone https://username:password@localhost attempt1 2>err &&
- ! grep "$message" err &&
-
- test_must_fail git -c transfer.credentialsInUrl=warn clone https://username:password@localhost attempt2 2>err &&
- grep "warning: $message" err >warnings &&
- test_line_count = 2 warnings &&
-
- test_must_fail git -c transfer.credentialsInUrl=die clone https://username:password@localhost attempt3 2>err &&
- grep "fatal: $message" err >warnings &&
- test_line_count = 1 warnings &&
-
- test_must_fail git -c transfer.credentialsInUrl=die clone https://username:@localhost attempt3 2>err &&
- grep "fatal: $message" err >warnings &&
- test_line_count = 1 warnings
-'
-
-test_expect_success LIBCURL 'clone does not detect username:password when it is https://username@domain:port/' '
- test_must_fail git -c transfer.credentialsInUrl=warn clone https://username@localhost:8080 attempt3 2>err &&
- ! grep "uses plaintext credentials" err
-'
-
test_expect_success 'clone from hooks' '
test_create_repo r0 &&
@@ -743,7 +720,11 @@ test_expect_success 'batch missing blob request during checkout' '
# Ensure that there is only one negotiation by checking that there is
# only "done" line sent. ("done" marks the end of negotiation.)
- GIT_TRACE_PACKET="$(pwd)/trace" git -C client checkout HEAD^ &&
+ GIT_TRACE_PACKET="$(pwd)/trace" \
+ GIT_TRACE2_EVENT="$(pwd)/trace2_event" \
+ git -C client -c trace2.eventNesting=5 checkout HEAD^ &&
+ grep \"key\":\"total_rounds\",\"value\":\"1\" trace2_event >trace_lines &&
+ test_line_count = 1 trace_lines &&
grep "fetch> done" trace >done_lines &&
test_line_count = 1 done_lines
'
@@ -763,6 +744,7 @@ test_expect_success 'batch missing blob request does not inadvertently try to fe
echo aa >server/a &&
echo bb >server/b &&
# Also add a gitlink pointing to an arbitrary repository
+ test_config_global protocol.file.allow always &&
git -C server submodule add "$(pwd)/repo_for_submodule" c &&
git -C server add a b c &&
git -C server commit -m x &&
diff --git a/t/t5604-clone-reference.sh b/t/t5604-clone-reference.sh
index 24340e6..2734e37 100755
--- a/t/t5604-clone-reference.sh
+++ b/t/t5604-clone-reference.sh
@@ -303,8 +303,6 @@ test_expect_success SYMLINKS 'setup repo with manually symlinked or unknown file
ln -s ../an-object $obj &&
cd ../ &&
- find . -type f | sort >../../../T.objects-files.raw &&
- find . -type l | sort >../../../T.objects-symlinks.raw &&
echo unknown_content >unknown_file
) &&
git -C T fsck &&
@@ -313,19 +311,27 @@ test_expect_success SYMLINKS 'setup repo with manually symlinked or unknown file
test_expect_success SYMLINKS 'clone repo with symlinked or unknown files at objects/' '
- for option in --local --no-hardlinks --shared --dissociate
+ # None of these options work when cloning locally, since T has
+ # symlinks in its `$GIT_DIR/objects` directory
+ for option in --local --no-hardlinks --dissociate
do
- git clone $option T T$option || return 1 &&
- git -C T$option fsck || return 1 &&
- git -C T$option rev-list --all --objects >T$option.objects &&
- test_cmp T.objects T$option.objects &&
- (
- cd T$option/.git/objects &&
- find . -type f | sort >../../../T$option.objects-files.raw &&
- find . -type l | sort >../../../T$option.objects-symlinks.raw
- )
+ test_must_fail git clone $option T T$option 2>err || return 1 &&
+ test_i18ngrep "symlink.*exists" err || return 1
done &&
+ # But `--shared` clones should still work, even when specifying
+ # a local path *and* that repository has symlinks present in its
+ # `$GIT_DIR/objects` directory.
+ git clone --shared T T--shared &&
+ git -C T--shared fsck &&
+ git -C T--shared rev-list --all --objects >T--shared.objects &&
+ test_cmp T.objects T--shared.objects &&
+ (
+ cd T--shared/.git/objects &&
+ find . -type f | sort >../../../T--shared.objects-files.raw &&
+ find . -type l | sort >../../../T--shared.objects-symlinks.raw
+ ) &&
+
for raw in $(ls T*.raw)
do
sed -e "s!/../!/Y/!; s![0-9a-f]\{38,\}!Z!" -e "/commit-graph/d" \
@@ -333,26 +339,6 @@ test_expect_success SYMLINKS 'clone repo with symlinked or unknown files at obje
sort $raw.de-sha-1 >$raw.de-sha || return 1
done &&
- cat >expected-files <<-EOF &&
- ./Y/Z
- ./Y/Z
- ./Y/Z
- ./a-loose-dir/Z
- ./an-object
- ./info/packs
- ./pack/pack-Z.idx
- ./pack/pack-Z.pack
- ./packs/pack-Z.idx
- ./packs/pack-Z.pack
- ./unknown_file
- EOF
-
- for option in --local --no-hardlinks --dissociate
- do
- test_cmp expected-files T$option.objects-files.raw.de-sha || return 1 &&
- test_must_be_empty T$option.objects-symlinks.raw.de-sha || return 1
- done &&
-
echo ./info/alternates >expected-files &&
test_cmp expected-files T--shared.objects-files.raw &&
test_must_be_empty T--shared.objects-symlinks.raw
diff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh
index 8f676d6..cf221e9 100755
--- a/t/t5606-clone-options.sh
+++ b/t/t5606-clone-options.sh
@@ -42,11 +42,12 @@ test_expect_success 'rejects invalid -o/--origin' '
'
-test_expect_success 'disallows --bare with --origin' '
+test_expect_success 'clone --bare -o' '
- test_must_fail git clone -o foo --bare parent clone-bare-o 2>err &&
- test_debug "cat err" &&
- test_i18ngrep -e "options .--bare. and .--origin foo. cannot be used together" err
+ git clone -o foo --bare parent clone-bare-o &&
+ (cd parent && pwd) >expect &&
+ git -C clone-bare-o config remote.foo.url >actual &&
+ test_cmp expect actual
'
@@ -58,6 +59,14 @@ test_expect_success 'disallows --bare with --separate-git-dir' '
'
+test_expect_success 'disallows --bundle-uri with shallow options' '
+ for option in --depth=1 --shallow-since=01-01-2000 --shallow-exclude=HEAD
+ do
+ test_must_fail git clone --bundle-uri=bundle $option from to 2>err &&
+ grep "bundle-uri is incompatible" err || return 1
+ done
+'
+
test_expect_success 'reject cloning shallow repository' '
test_when_finished "rm -rf repo" &&
test_must_fail git clone --reject-shallow shallow-repo out 2>err &&
diff --git a/t/t5614-clone-submodules-shallow.sh b/t/t5614-clone-submodules-shallow.sh
index 5504b51..0c85ef8 100755
--- a/t/t5614-clone-submodules-shallow.sh
+++ b/t/t5614-clone-submodules-shallow.sh
@@ -24,6 +24,7 @@ test_expect_success 'setup' '
test_expect_success 'nonshallow clone implies nonshallow submodule' '
test_when_finished "rm -rf super_clone" &&
+ test_config_global protocol.file.allow always &&
git clone --recurse-submodules "file://$pwd/." super_clone &&
git -C super_clone log --oneline >lines &&
test_line_count = 3 lines &&
@@ -33,6 +34,7 @@ test_expect_success 'nonshallow clone implies nonshallow submodule' '
test_expect_success 'shallow clone with shallow submodule' '
test_when_finished "rm -rf super_clone" &&
+ test_config_global protocol.file.allow always &&
git clone --recurse-submodules --depth 2 --shallow-submodules "file://$pwd/." super_clone &&
git -C super_clone log --oneline >lines &&
test_line_count = 2 lines &&
@@ -42,6 +44,7 @@ test_expect_success 'shallow clone with shallow submodule' '
test_expect_success 'shallow clone does not imply shallow submodule' '
test_when_finished "rm -rf super_clone" &&
+ test_config_global protocol.file.allow always &&
git clone --recurse-submodules --depth 2 "file://$pwd/." super_clone &&
git -C super_clone log --oneline >lines &&
test_line_count = 2 lines &&
@@ -51,6 +54,7 @@ test_expect_success 'shallow clone does not imply shallow submodule' '
test_expect_success 'shallow clone with non shallow submodule' '
test_when_finished "rm -rf super_clone" &&
+ test_config_global protocol.file.allow always &&
git clone --recurse-submodules --depth 2 --no-shallow-submodules "file://$pwd/." super_clone &&
git -C super_clone log --oneline >lines &&
test_line_count = 2 lines &&
@@ -60,6 +64,7 @@ test_expect_success 'shallow clone with non shallow submodule' '
test_expect_success 'non shallow clone with shallow submodule' '
test_when_finished "rm -rf super_clone" &&
+ test_config_global protocol.file.allow always &&
git clone --recurse-submodules --no-local --shallow-submodules "file://$pwd/." super_clone &&
git -C super_clone log --oneline >lines &&
test_line_count = 3 lines &&
@@ -69,6 +74,7 @@ test_expect_success 'non shallow clone with shallow submodule' '
test_expect_success 'clone follows shallow recommendation' '
test_when_finished "rm -rf super_clone" &&
+ test_config_global protocol.file.allow always &&
git config -f .gitmodules submodule.sub.shallow true &&
git add .gitmodules &&
git commit -m "recommend shallow for sub" &&
@@ -87,6 +93,7 @@ test_expect_success 'clone follows shallow recommendation' '
test_expect_success 'get unshallow recommended shallow submodule' '
test_when_finished "rm -rf super_clone" &&
+ test_config_global protocol.file.allow always &&
git clone --no-local "file://$pwd/." super_clone &&
(
cd super_clone &&
@@ -103,6 +110,7 @@ test_expect_success 'get unshallow recommended shallow submodule' '
test_expect_success 'clone follows non shallow recommendation' '
test_when_finished "rm -rf super_clone" &&
+ test_config_global protocol.file.allow always &&
git config -f .gitmodules submodule.sub.shallow false &&
git add .gitmodules &&
git commit -m "recommend non shallow for sub" &&
diff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh
index 4a3778d..037941b 100755
--- a/t/t5616-partial-clone.sh
+++ b/t/t5616-partial-clone.sh
@@ -49,6 +49,13 @@ test_expect_success 'do partial clone 1' '
test "$(git -C pc1 config --local remote.origin.partialclonefilter)" = "blob:none"
'
+test_expect_success 'rev-list --missing=allow-promisor on partial clone' '
+ git -C pc1 rev-list --objects --missing=allow-promisor HEAD >actual &&
+ git -C pc1 rev-list --objects --missing=print HEAD >expect.raw &&
+ grep -v "^?" expect.raw >expect &&
+ test_cmp expect actual
+'
+
test_expect_success 'verify that .promisor file contains refs fetched' '
ls pc1/.git/objects/pack/pack-*.promisor >promisorlist &&
test_line_count = 1 promisorlist &&
@@ -253,6 +260,8 @@ test_expect_success 'partial clone with transfer.fsckobjects=1 works with submod
test_config -C src_with_sub uploadpack.allowfilter 1 &&
test_config -C src_with_sub uploadpack.allowanysha1inwant 1 &&
+ test_config_global protocol.file.allow always &&
+
git -C src_with_sub submodule add "file://$(pwd)/submodule" mysub &&
git -C src_with_sub commit -m "commit with submodule" &&
diff --git a/t/t5617-clone-submodules-remote.sh b/t/t5617-clone-submodules-remote.sh
index ca8f800..6884338 100755
--- a/t/t5617-clone-submodules-remote.sh
+++ b/t/t5617-clone-submodules-remote.sh
@@ -10,6 +10,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
pwd=$(pwd)
test_expect_success 'setup' '
+ git config --global protocol.file.allow always &&
git checkout -b main &&
test_commit commit1 &&
mkdir sub &&
diff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh
index 5d42a35..b33cd4a 100755
--- a/t/t5702-protocol-v2.sh
+++ b/t/t5702-protocol-v2.sh
@@ -1001,7 +1001,7 @@ test_expect_success 'part of packfile response provided as URI' '
do
git verify-pack --object-format=$(test_oid algo) --verbose $idx >out &&
{
- grep "^[0-9a-f]\{16,\} " out || :
+ grep -E "^[0-9a-f]{16,} " out || :
} >out.objectlist &&
if test_line_count = 1 out.objectlist
then
diff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh
index 9d6cd7d..df74f80 100755
--- a/t/t5703-upload-pack-ref-in-want.sh
+++ b/t/t5703-upload-pack-ref-in-want.sh
@@ -229,14 +229,16 @@ test_expect_success 'setup repos for fetching with ref-in-want tests' '
'
test_expect_success 'fetching with exact OID' '
- test_when_finished "rm -f log" &&
+ test_when_finished "rm -f log trace2" &&
rm -rf local &&
cp -r "$LOCAL_PRISTINE" local &&
oid=$(git -C "$REPO" rev-parse d) &&
- GIT_TRACE_PACKET="$(pwd)/log" git -C local fetch origin \
+ GIT_TRACE_PACKET="$(pwd)/log" GIT_TRACE2_EVENT="$(pwd)/trace2" \
+ git -C local fetch origin \
"$oid":refs/heads/actual &&
+ grep \"key\":\"total_rounds\",\"value\":\"2\" trace2 &&
git -C "$REPO" rev-parse "d" >expected &&
git -C local rev-parse refs/heads/actual >actual &&
test_cmp expected actual &&
diff --git a/t/t5750-bundle-uri-parse.sh b/t/t5750-bundle-uri-parse.sh
new file mode 100755
index 0000000..c2fe3f9
--- /dev/null
+++ b/t/t5750-bundle-uri-parse.sh
@@ -0,0 +1,171 @@
+#!/bin/sh
+
+test_description="Test bundle-uri bundle_uri_parse_line()"
+
+TEST_NO_CREATE_REPO=1
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'bundle_uri_parse_line() just URIs' '
+ cat >in <<-\EOF &&
+ bundle.one.uri=http://example.com/bundle.bdl
+ bundle.two.uri=https://example.com/bundle.bdl
+ bundle.three.uri=file:///usr/share/git/bundle.bdl
+ EOF
+
+ cat >expect <<-\EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ [bundle "one"]
+ uri = http://example.com/bundle.bdl
+ [bundle "two"]
+ uri = https://example.com/bundle.bdl
+ [bundle "three"]
+ uri = file:///usr/share/git/bundle.bdl
+ EOF
+
+ test-tool bundle-uri parse-key-values in >actual 2>err &&
+ test_must_be_empty err &&
+ test_cmp_config_output expect actual
+'
+
+test_expect_success 'bundle_uri_parse_line() parsing edge cases: empty key or value' '
+ cat >in <<-\EOF &&
+ =bogus-value
+ bogus-key=
+ EOF
+
+ cat >err.expect <<-EOF &&
+ error: bundle-uri: line has empty key or value
+ error: bad line: '\''=bogus-value'\''
+ error: bundle-uri: line has empty key or value
+ error: bad line: '\''bogus-key='\''
+ EOF
+
+ cat >expect <<-\EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ EOF
+
+ test_must_fail test-tool bundle-uri parse-key-values in >actual 2>err &&
+ test_cmp err.expect err &&
+ test_cmp_config_output expect actual
+'
+
+test_expect_success 'bundle_uri_parse_line() parsing edge cases: empty lines' '
+ cat >in <<-\EOF &&
+ bundle.one.uri=http://example.com/bundle.bdl
+
+ bundle.two.uri=https://example.com/bundle.bdl
+
+ bundle.three.uri=file:///usr/share/git/bundle.bdl
+ EOF
+
+ cat >err.expect <<-\EOF &&
+ error: bundle-uri: got an empty line
+ error: bad line: '\'''\''
+ error: bundle-uri: got an empty line
+ error: bad line: '\'''\''
+ EOF
+
+ # We fail, but try to continue parsing regardless
+ cat >expect <<-\EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ [bundle "one"]
+ uri = http://example.com/bundle.bdl
+ [bundle "two"]
+ uri = https://example.com/bundle.bdl
+ [bundle "three"]
+ uri = file:///usr/share/git/bundle.bdl
+ EOF
+
+ test_must_fail test-tool bundle-uri parse-key-values in >actual 2>err &&
+ test_cmp err.expect err &&
+ test_cmp_config_output expect actual
+'
+
+test_expect_success 'bundle_uri_parse_line() parsing edge cases: duplicate lines' '
+ cat >in <<-\EOF &&
+ bundle.one.uri=http://example.com/bundle.bdl
+ bundle.two.uri=https://example.com/bundle.bdl
+ bundle.one.uri=https://example.com/bundle-2.bdl
+ bundle.three.uri=file:///usr/share/git/bundle.bdl
+ EOF
+
+ cat >err.expect <<-\EOF &&
+ error: bad line: '\''bundle.one.uri=https://example.com/bundle-2.bdl'\''
+ EOF
+
+ # We fail, but try to continue parsing regardless
+ cat >expect <<-\EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ [bundle "one"]
+ uri = http://example.com/bundle.bdl
+ [bundle "two"]
+ uri = https://example.com/bundle.bdl
+ [bundle "three"]
+ uri = file:///usr/share/git/bundle.bdl
+ EOF
+
+ test_must_fail test-tool bundle-uri parse-key-values in >actual 2>err &&
+ test_cmp err.expect err &&
+ test_cmp_config_output expect actual
+'
+
+test_expect_success 'parse config format: just URIs' '
+ cat >expect <<-\EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ [bundle "one"]
+ uri = http://example.com/bundle.bdl
+ [bundle "two"]
+ uri = https://example.com/bundle.bdl
+ [bundle "three"]
+ uri = file:///usr/share/git/bundle.bdl
+ EOF
+
+ test-tool bundle-uri parse-config expect >actual 2>err &&
+ test_must_be_empty err &&
+ test_cmp_config_output expect actual
+'
+
+test_expect_success 'parse config format edge cases: empty key or value' '
+ cat >in1 <<-\EOF &&
+ = bogus-value
+ EOF
+
+ cat >err1 <<-EOF &&
+ error: bad config line 1 in file in1
+ EOF
+
+ cat >expect <<-\EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ EOF
+
+ test_must_fail test-tool bundle-uri parse-config in1 >actual 2>err &&
+ test_cmp err1 err &&
+ test_cmp_config_output expect actual &&
+
+ cat >in2 <<-\EOF &&
+ bogus-key =
+ EOF
+
+ cat >err2 <<-EOF &&
+ error: bad config line 1 in file in2
+ EOF
+
+ test_must_fail test-tool bundle-uri parse-config in2 >actual 2>err &&
+ test_cmp err2 err &&
+ test_cmp_config_output expect actual
+'
+
+test_done
diff --git a/t/t6008-rev-list-submodule.sh b/t/t6008-rev-list-submodule.sh
index 3153a0d..2cdef6f 100755
--- a/t/t6008-rev-list-submodule.sh
+++ b/t/t6008-rev-list-submodule.sh
@@ -8,6 +8,7 @@ test_description='git rev-list involving submodules that this repo has'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -26,7 +27,7 @@ test_expect_success 'setup' '
: > super-file &&
git add super-file &&
- git submodule add "$(pwd)" sub &&
+ git -c protocol.file.allow=always submodule add "$(pwd)" sub &&
git symbolic-ref HEAD refs/heads/super &&
test_tick &&
git commit -m super-initial &&
diff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh
index e1abc5c..aabf590 100755
--- a/t/t6018-rev-list-glob.sh
+++ b/t/t6018-rev-list-glob.sh
@@ -187,6 +187,46 @@ test_expect_success 'rev-parse --exclude=ref with --remotes=glob' '
compare rev-parse "--exclude=upstream/x --remotes=upstream/*" "upstream/one upstream/two"
'
+for section in receive uploadpack
+do
+ test_expect_success "rev-parse --exclude-hidden=$section with --all" '
+ compare "-c transfer.hideRefs=refs/remotes/ rev-parse" "--branches --tags" "--exclude-hidden=$section --all"
+ '
+
+ test_expect_success "rev-parse --exclude-hidden=$section with --all" '
+ compare "-c transfer.hideRefs=refs/heads/subspace/ rev-parse" "--exclude=refs/heads/subspace/* --all" "--exclude-hidden=$section --all"
+ '
+
+ test_expect_success "rev-parse --exclude-hidden=$section with --glob" '
+ compare "-c transfer.hideRefs=refs/heads/subspace/ rev-parse" "--exclude=refs/heads/subspace/* --glob=refs/heads/*" "--exclude-hidden=$section --glob=refs/heads/*"
+ '
+
+ test_expect_success "rev-parse --exclude-hidden=$section can be passed once per pseudo-ref" '
+ compare "-c transfer.hideRefs=refs/remotes/ rev-parse" "--branches --tags --branches --tags" "--exclude-hidden=$section --all --exclude-hidden=$section --all"
+ '
+
+ test_expect_success "rev-parse --exclude-hidden=$section can only be passed once per pseudo-ref" '
+ echo "fatal: --exclude-hidden= passed more than once" >expected &&
+ test_must_fail git rev-parse --exclude-hidden=$section --exclude-hidden=$section 2>err &&
+ test_cmp expected err
+ '
+
+ for pseudoopt in branches tags remotes
+ do
+ test_expect_success "rev-parse --exclude-hidden=$section fails with --$pseudoopt" '
+ echo "error: --exclude-hidden cannot be used together with --$pseudoopt" >expected &&
+ test_must_fail git rev-parse --exclude-hidden=$section --$pseudoopt 2>err &&
+ test_cmp expected err
+ '
+
+ test_expect_success "rev-parse --exclude-hidden=$section fails with --$pseudoopt=pattern" '
+ echo "error: --exclude-hidden cannot be used together with --$pseudoopt" >expected &&
+ test_must_fail git rev-parse --exclude-hidden=$section --$pseudoopt=pattern 2>err &&
+ test_cmp expected err
+ '
+ done
+done
+
test_expect_success 'rev-list --exclude=glob with --branches=glob' '
compare rev-list "--exclude=subspace-* --branches=sub*" "subspace/one subspace/two"
'
diff --git a/t/t6019-rev-list-ancestry-path.sh b/t/t6019-rev-list-ancestry-path.sh
index af57a04..738da23 100755
--- a/t/t6019-rev-list-ancestry-path.sh
+++ b/t/t6019-rev-list-ancestry-path.sh
@@ -8,8 +8,13 @@ test_description='--ancestry-path'
# / \
# A-------K---------------L--M
#
-# D..M == E F G H I J K L M
-# --ancestry-path D..M == E F H I J L M
+# D..M == E F G H I J K L M
+# --ancestry-path D..M == E F H I J L M
+# --ancestry-path=F D..M == E F J L M
+# --ancestry-path=G D..M == G H I J L M
+# --ancestry-path=H D..M == E G H I J L M
+# --ancestry-path=K D..M == K L M
+# --ancestry-path=K --ancestry-path=F D..M == E F J K L M
#
# D..M -- M.t == M
# --ancestry-path D..M -- M.t == M
@@ -50,73 +55,41 @@ test_expect_success setup '
test_commit M
'
-test_expect_success 'rev-list D..M' '
- test_write_lines E F G H I J K L M >expect &&
- git rev-list --format=%s D..M |
- sed -e "/^commit /d" |
- sort >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'rev-list --ancestry-path D..M' '
- test_write_lines E F H I J L M >expect &&
- git rev-list --ancestry-path --format=%s D..M |
- sed -e "/^commit /d" |
- sort >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'rev-list D..M -- M.t' '
- echo M >expect &&
- git rev-list --format=%s D..M -- M.t |
- sed -e "/^commit /d" >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'rev-list --ancestry-path D..M -- M.t' '
- echo M >expect &&
- git rev-list --ancestry-path --format=%s D..M -- M.t |
- sed -e "/^commit /d" >actual &&
- test_cmp expect actual
-'
+test_ancestry () {
+ args=$1
+ expected=$2
+ test_expect_success "log $args" "
+ test_write_lines $expected >expect &&
+ git log --format=%s $args >raw &&
+
+ if test -n \"$expected\"
+ then
+ sort raw >actual &&
+ test_cmp expect actual
+ else
+ test_must_be_empty raw
+ fi
+ "
+}
-test_expect_success 'rev-list F...I' '
- test_write_lines F G H I >expect &&
- git rev-list --format=%s F...I |
- sed -e "/^commit /d" |
- sort >actual &&
- test_cmp expect actual
-'
+test_ancestry "D..M" "E F G H I J K L M"
-test_expect_success 'rev-list --ancestry-path F...I' '
- test_write_lines F H I >expect &&
- git rev-list --ancestry-path --format=%s F...I |
- sed -e "/^commit /d" |
- sort >actual &&
- test_cmp expect actual
-'
+test_ancestry "--ancestry-path D..M" "E F H I J L M"
+test_ancestry "--ancestry-path=F D..M" "E F J L M"
+test_ancestry "--ancestry-path=G D..M" "G H I J L M"
+test_ancestry "--ancestry-path=H D..M" "E G H I J L M"
+test_ancestry "--ancestry-path=K D..M" "K L M"
+test_ancestry "--ancestry-path=F --ancestry-path=K D..M" "E F J K L M"
-# G.t is dropped in an "-s ours" merge
-test_expect_success 'rev-list G..M -- G.t' '
- git rev-list --format=%s G..M -- G.t |
- sed -e "/^commit /d" >actual &&
- test_must_be_empty actual
-'
+test_ancestry "D..M -- M.t" "M"
+test_ancestry "--ancestry-path D..M -- M.t" "M"
-test_expect_success 'rev-list --ancestry-path G..M -- G.t' '
- echo L >expect &&
- git rev-list --ancestry-path --format=%s G..M -- G.t |
- sed -e "/^commit /d" >actual &&
- test_cmp expect actual
-'
+test_ancestry "F...I" "F G H I"
+test_ancestry "--ancestry-path F...I" "F H I"
-test_expect_success 'rev-list --ancestry-path --simplify-merges G^..M -- G.t' '
- test_write_lines G L >expect &&
- git rev-list --ancestry-path --simplify-merges --format=%s G^..M -- G.t |
- sed -e "/^commit /d" |
- sort >actual &&
- test_cmp expect actual
-'
+test_ancestry "G..M -- G.t" ""
+test_ancestry "--ancestry-path G..M -- G.t" "L"
+test_ancestry "--ancestry-path --simplify-merges G^..M -- G.t" "G L"
# b---bc
# / \ /
diff --git a/t/t6021-rev-list-exclude-hidden.sh b/t/t6021-rev-list-exclude-hidden.sh
new file mode 100755
index 0000000..32b2b09
--- /dev/null
+++ b/t/t6021-rev-list-exclude-hidden.sh
@@ -0,0 +1,163 @@
+#!/bin/sh
+
+test_description='git rev-list --exclude-hidden test'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit_bulk --id=commit --ref=refs/heads/branch 1 &&
+ COMMIT=$(git rev-parse refs/heads/branch) &&
+ test_commit_bulk --id=tag --ref=refs/tags/lightweight 1 &&
+ TAG=$(git rev-parse refs/tags/lightweight) &&
+ test_commit_bulk --id=hidden --ref=refs/hidden/commit 1 &&
+ HIDDEN=$(git rev-parse refs/hidden/commit) &&
+ test_commit_bulk --id=namespace --ref=refs/namespaces/namespace/refs/namespaced/commit 1 &&
+ NAMESPACE=$(git rev-parse refs/namespaces/namespace/refs/namespaced/commit)
+'
+
+test_expect_success 'invalid section' '
+ echo "fatal: unsupported section for hidden refs: unsupported" >expected &&
+ test_must_fail git rev-list --exclude-hidden=unsupported 2>err &&
+ test_cmp expected err
+'
+
+for section in receive uploadpack
+do
+ test_expect_success "$section: passed multiple times" '
+ echo "fatal: --exclude-hidden= passed more than once" >expected &&
+ test_must_fail git rev-list --exclude-hidden=$section --exclude-hidden=$section 2>err &&
+ test_cmp expected err
+ '
+
+ test_expect_success "$section: without hiddenRefs" '
+ git rev-list --exclude-hidden=$section --all >out &&
+ cat >expected <<-EOF &&
+ $NAMESPACE
+ $HIDDEN
+ $TAG
+ $COMMIT
+ EOF
+ test_cmp expected out
+ '
+
+ test_expect_success "$section: hidden via transfer.hideRefs" '
+ git -c transfer.hideRefs=refs/hidden/ rev-list --exclude-hidden=$section --all >out &&
+ cat >expected <<-EOF &&
+ $NAMESPACE
+ $TAG
+ $COMMIT
+ EOF
+ test_cmp expected out
+ '
+
+ test_expect_success "$section: hidden via $section.hideRefs" '
+ git -c $section.hideRefs=refs/hidden/ rev-list --exclude-hidden=$section --all >out &&
+ cat >expected <<-EOF &&
+ $NAMESPACE
+ $TAG
+ $COMMIT
+ EOF
+ test_cmp expected out
+ '
+
+ test_expect_success "$section: respects both transfer.hideRefs and $section.hideRefs" '
+ git -c transfer.hideRefs=refs/tags/ -c $section.hideRefs=refs/hidden/ rev-list --exclude-hidden=$section --all >out &&
+ cat >expected <<-EOF &&
+ $NAMESPACE
+ $COMMIT
+ EOF
+ test_cmp expected out
+ '
+
+ test_expect_success "$section: negation without hidden refs marks everything as uninteresting" '
+ git rev-list --all --exclude-hidden=$section --not --all >out &&
+ test_must_be_empty out
+ '
+
+ test_expect_success "$section: negation with hidden refs marks them as interesting" '
+ git -c transfer.hideRefs=refs/hidden/ rev-list --all --exclude-hidden=$section --not --all >out &&
+ cat >expected <<-EOF &&
+ $HIDDEN
+ EOF
+ test_cmp expected out
+ '
+
+ test_expect_success "$section: hidden refs and excludes work together" '
+ git -c transfer.hideRefs=refs/hidden/ rev-list --exclude=refs/tags/* --exclude-hidden=$section --all >out &&
+ cat >expected <<-EOF &&
+ $NAMESPACE
+ $COMMIT
+ EOF
+ test_cmp expected out
+ '
+
+ test_expect_success "$section: excluded hidden refs get reset" '
+ git -c transfer.hideRefs=refs/ rev-list --exclude-hidden=$section --all --all >out &&
+ cat >expected <<-EOF &&
+ $NAMESPACE
+ $HIDDEN
+ $TAG
+ $COMMIT
+ EOF
+ test_cmp expected out
+ '
+
+ test_expect_success "$section: excluded hidden refs can be used with multiple pseudo-refs" '
+ git -c transfer.hideRefs=refs/ rev-list --exclude-hidden=$section --all --exclude-hidden=$section --all >out &&
+ test_must_be_empty out
+ '
+
+ test_expect_success "$section: works with --glob" '
+ git -c transfer.hideRefs=refs/hidden/ rev-list --exclude-hidden=$section --glob=refs/h* >out &&
+ cat >expected <<-EOF &&
+ $COMMIT
+ EOF
+ test_cmp expected out
+ '
+
+ test_expect_success "$section: operates on stripped refs by default" '
+ GIT_NAMESPACE=namespace git -c transfer.hideRefs=refs/namespaced/ rev-list --exclude-hidden=$section --all >out &&
+ cat >expected <<-EOF &&
+ $HIDDEN
+ $TAG
+ $COMMIT
+ EOF
+ test_cmp expected out
+ '
+
+ test_expect_success "$section: does not hide namespace by default" '
+ GIT_NAMESPACE=namespace git -c transfer.hideRefs=refs/namespaces/namespace/ rev-list --exclude-hidden=$section --all >out &&
+ cat >expected <<-EOF &&
+ $NAMESPACE
+ $HIDDEN
+ $TAG
+ $COMMIT
+ EOF
+ test_cmp expected out
+ '
+
+ test_expect_success "$section: can operate on unstripped refs" '
+ GIT_NAMESPACE=namespace git -c transfer.hideRefs=^refs/namespaces/namespace/ rev-list --exclude-hidden=$section --all >out &&
+ cat >expected <<-EOF &&
+ $HIDDEN
+ $TAG
+ $COMMIT
+ EOF
+ test_cmp expected out
+ '
+
+ for pseudoopt in remotes branches tags
+ do
+ test_expect_success "$section: fails with --$pseudoopt" '
+ test_must_fail git rev-list --exclude-hidden=$section --$pseudoopt 2>err &&
+ test_i18ngrep "error: --exclude-hidden cannot be used together with --$pseudoopt" err
+ '
+
+ test_expect_success "$section: fails with --$pseudoopt=pattern" '
+ test_must_fail git rev-list --exclude-hidden=$section --$pseudoopt=pattern 2>err &&
+ test_i18ngrep "error: --exclude-hidden cannot be used together with --$pseudoopt" err
+ '
+ done
+done
+
+test_done
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index 83931d4..6dbbe62 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -266,6 +266,16 @@ test_expect_success '"git bisect run" simple case' '
git bisect reset
'
+# We want to make sure no arguments has been eaten
+test_expect_success '"git bisect run" simple case' '
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ git bisect run printf "%s %s\n" reset --bisect-skip >my_bisect_log.txt &&
+ grep -e "reset --bisect-skip" my_bisect_log.txt &&
+ git bisect reset
+'
+
# We want to automatically find the commit that
# added "Ciao" into hello.
test_expect_success '"git bisect run" with more complex "git bisect start"' '
diff --git a/t/t6102-rev-list-unexpected-objects.sh b/t/t6102-rev-list-unexpected-objects.sh
index cf0195e..9350b5f 100755
--- a/t/t6102-rev-list-unexpected-objects.sh
+++ b/t/t6102-rev-list-unexpected-objects.sh
@@ -17,7 +17,7 @@ test_expect_success 'setup unexpected non-blob entry' '
broken_tree="$(git hash-object -w --literally -t tree broken-tree)"
'
-test_expect_success !SANITIZE_LEAK 'TODO (should fail!): traverse unexpected non-blob entry (lone)' '
+test_expect_success 'TODO (should fail!): traverse unexpected non-blob entry (lone)' '
sed "s/Z$//" >expect <<-EOF &&
$broken_tree Z
$tree foo
@@ -121,8 +121,8 @@ test_expect_success 'setup unexpected non-blob tag' '
tag=$(git hash-object -w --literally -t tag broken-tag)
'
-test_expect_success !SANITIZE_LEAK 'TODO (should fail!): traverse unexpected non-blob tag (lone)' '
- git rev-list --objects $tag
+test_expect_success 'traverse unexpected non-blob tag (lone)' '
+ test_must_fail git rev-list --objects $tag
'
test_expect_success 'traverse unexpected non-blob tag (seen)' '
diff --git a/t/t6115-rev-list-du.sh b/t/t6115-rev-list-du.sh
index b4aef32..d59111d 100755
--- a/t/t6115-rev-list-du.sh
+++ b/t/t6115-rev-list-du.sh
@@ -48,4 +48,26 @@ check_du HEAD
check_du --objects HEAD
check_du --objects HEAD^..HEAD
+# As mentioned above, don't use hardcode sizes as actual size, but use the
+# output from git cat-file.
+test_expect_success 'rev-list --disk-usage=human' '
+ git rev-list --objects HEAD --disk-usage=human >actual &&
+ disk_usage_slow --objects HEAD >actual_size &&
+ grep "$(cat actual_size) bytes" actual
+'
+
+test_expect_success 'rev-list --disk-usage=human with bitmaps' '
+ git rev-list --objects HEAD --use-bitmap-index --disk-usage=human >actual &&
+ disk_usage_slow --objects HEAD >actual_size &&
+ grep "$(cat actual_size) bytes" actual
+'
+
+test_expect_success 'rev-list use --disk-usage unproperly' '
+ test_must_fail git rev-list --objects HEAD --disk-usage=typo 2>err &&
+ cat >expect <<-\EOF &&
+ fatal: invalid value for '\''--disk-usage=<format>'\'': '\''typo'\'', the only allowed format is '\''human'\''
+ EOF
+ test_cmp err expect
+'
+
test_done
diff --git a/t/t6132-pathspec-exclude.sh b/t/t6132-pathspec-exclude.sh
index 9fdafeb..cada952 100755
--- a/t/t6132-pathspec-exclude.sh
+++ b/t/t6132-pathspec-exclude.sh
@@ -293,7 +293,11 @@ test_expect_success 'add with all negative' '
test_cmp expect actual
'
-test_expect_success 'add -p with all negative' '
+test_lazy_prereq ADD_I_USE_BUILTIN_OR_PERL '
+ test_have_prereq ADD_I_USE_BUILTIN || test_have_prereq PERL
+'
+
+test_expect_success ADD_I_USE_BUILTIN_OR_PERL 'add -p with all negative' '
H=$(git rev-parse HEAD) &&
git reset --hard $H &&
git clean -f &&
diff --git a/t/t6134-pathspec-in-submodule.sh b/t/t6134-pathspec-in-submodule.sh
index 0f1cb49..3214d9d 100755
--- a/t/t6134-pathspec-in-submodule.sh
+++ b/t/t6134-pathspec-in-submodule.sh
@@ -2,6 +2,7 @@
test_description='test case exclude pathspec'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup a submodule' '
@@ -9,7 +10,7 @@ test_expect_success 'setup a submodule' '
: >pretzel/a &&
git -C pretzel add a &&
git -C pretzel commit -m "add a file" -- a &&
- git submodule add ./pretzel sub &&
+ git -c protocol.file.allow=always submodule add ./pretzel sub &&
git commit -a -m "add submodule" &&
git submodule deinit --all
'
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index dcaab72..fa38b87 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -1406,4 +1406,44 @@ test_expect_success 'for-each-ref reports broken tags' '
refs/tags/broken-tag-*
'
+test_expect_success 'set up tag with signature and no blank lines' '
+ git tag -F - fake-sig-no-blanks <<-\EOF
+ this is the subject
+ -----BEGIN PGP SIGNATURE-----
+ not a real signature, but we just care about the
+ subject/body parsing. It is important here that
+ there are no blank lines in the signature.
+ -----END PGP SIGNATURE-----
+ EOF
+'
+
+test_atom refs/tags/fake-sig-no-blanks contents:subject 'this is the subject'
+test_atom refs/tags/fake-sig-no-blanks contents:body ''
+test_atom refs/tags/fake-sig-no-blanks contents:signature "$sig"
+
+test_expect_success 'set up tag with CRLF signature' '
+ append_cr <<-\EOF |
+ this is the subject
+ -----BEGIN PGP SIGNATURE-----
+
+ not a real signature, but we just care about
+ the subject/body parsing. It is important here
+ that there is a blank line separating this
+ from the signature header.
+ -----END PGP SIGNATURE-----
+ EOF
+ git tag -F - --cleanup=verbatim fake-sig-crlf
+'
+
+test_atom refs/tags/fake-sig-crlf contents:subject 'this is the subject'
+test_atom refs/tags/fake-sig-crlf contents:body ''
+
+# CRLF is retained in the signature, so we have to pass our expected value
+# through append_cr. But test_atom requires a shell string, which means command
+# substitution, and the shell will strip trailing newlines from the output of
+# the substitution. Hack around it by adding and then removing a dummy line.
+sig_crlf="$(printf "%s" "$sig" | append_cr; echo dummy)"
+sig_crlf=${sig_crlf%dummy}
+test_atom refs/tags/fake-sig-crlf contents:signature "$sig_crlf"
+
test_done
diff --git a/t/t6400-merge-df.sh b/t/t6400-merge-df.sh
index 57a67cf..3de4ef6 100755
--- a/t/t6400-merge-df.sh
+++ b/t/t6400-merge-df.sh
@@ -126,7 +126,7 @@ test_expect_success 'Simple merge in repo with interesting pathnames' '
# foo/bar-2/baz
# The fact that foo/bar-2 appears between foo/bar and foo/bar/baz
# can trip up some codepaths, and is the point of this test.
- test_create_repo name-ordering &&
+ git init name-ordering &&
(
cd name-ordering &&
diff --git a/t/t6404-recursive-merge.sh b/t/t6404-recursive-merge.sh
index b8735c6..3621551 100755
--- a/t/t6404-recursive-merge.sh
+++ b/t/t6404-recursive-merge.sh
@@ -4,6 +4,7 @@ test_description='Test merge without common ancestors'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# This scenario is based on a real-world repository of Shawn Pearce.
diff --git a/t/t6405-merge-symlinks.sh b/t/t6405-merge-symlinks.sh
index 7435fce..29e2b25 100755
--- a/t/t6405-merge-symlinks.sh
+++ b/t/t6405-merge-symlinks.sh
@@ -11,6 +11,7 @@ if core.symlinks is false.'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t6406-merge-attr.sh b/t/t6406-merge-attr.sh
index 99abefd..8650a88 100755
--- a/t/t6406-merge-attr.sh
+++ b/t/t6406-merge-attr.sh
@@ -162,8 +162,8 @@ test_expect_success 'custom merge backend' '
'
test_expect_success 'up-to-date merge without common ancestor' '
- test_create_repo repo1 &&
- test_create_repo repo2 &&
+ git init repo1 &&
+ git init repo2 &&
test_tick &&
(
cd repo1 &&
diff --git a/t/t6407-merge-binary.sh b/t/t6407-merge-binary.sh
index 0753fc9..e8a2871 100755
--- a/t/t6407-merge-binary.sh
+++ b/t/t6407-merge-binary.sh
@@ -5,7 +5,6 @@ test_description='ask merge-recursive to merge binary files'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t6408-merge-up-to-date.sh b/t/t6408-merge-up-to-date.sh
index 7763c1b..8a1ba6d 100755
--- a/t/t6408-merge-up-to-date.sh
+++ b/t/t6408-merge-up-to-date.sh
@@ -2,6 +2,7 @@
test_description='merge fast-forward and up to date'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t6411-merge-filemode.sh b/t/t6411-merge-filemode.sh
index 6ae2489..b618272 100755
--- a/t/t6411-merge-filemode.sh
+++ b/t/t6411-merge-filemode.sh
@@ -4,6 +4,7 @@ test_description='merge: handle file mode'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'set up mode change in one branch' '
diff --git a/t/t6413-merge-crlf.sh b/t/t6413-merge-crlf.sh
index affea25..b4f4a31 100755
--- a/t/t6413-merge-crlf.sh
+++ b/t/t6413-merge-crlf.sh
@@ -11,6 +11,7 @@ test_description='merge conflict in crlf repo
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t6416-recursive-corner-cases.sh b/t/t6416-recursive-corner-cases.sh
index 690c848..17b54d6 100755
--- a/t/t6416-recursive-corner-cases.sh
+++ b/t/t6416-recursive-corner-cases.sh
@@ -19,7 +19,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
#
test_expect_success 'setup basic criss-cross + rename with no modifications' '
- test_create_repo basic-rename &&
+ git init basic-rename &&
(
cd basic-rename &&
@@ -85,7 +85,7 @@ test_expect_success 'merge simple rename+criss-cross with no modifications' '
#
test_expect_success 'setup criss-cross + rename merges with basic modification' '
- test_create_repo rename-modify &&
+ git init rename-modify &&
(
cd rename-modify &&
@@ -160,7 +160,7 @@ test_expect_success 'merge criss-cross + rename merges with basic modification'
#
test_expect_success 'setup differently handled merges of rename/add conflict' '
- test_create_repo rename-add &&
+ git init rename-add &&
(
cd rename-add &&
@@ -324,7 +324,7 @@ test_expect_success 'git detects differently handled merges conflict, swapped' '
# Merging commits D & E should result in modify/delete conflict.
test_expect_success 'setup criss-cross + modify/delete resolved differently' '
- test_create_repo modify-delete &&
+ git init modify-delete &&
(
cd modify-delete &&
@@ -499,7 +499,7 @@ test_expect_success 'git detects conflict merging criss-cross+modify/delete, rev
#
test_expect_success 'setup differently handled merges of directory/file conflict' '
- test_create_repo directory-file &&
+ git init directory-file &&
(
cd directory-file &&
@@ -867,7 +867,7 @@ test_expect_failure 'merge of D2 & E4 merges a2s & reports conflict for a/file'
# but that may cancel out at the final merge stage".
test_expect_success 'setup rename/rename(1to2)/modify followed by what looks like rename/rename(2to1)/modify' '
- test_create_repo rename-squared-squared &&
+ git init rename-squared-squared &&
(
cd rename-squared-squared &&
@@ -944,7 +944,7 @@ test_expect_success 'handle rename/rename(1to2)/modify followed by what looks li
# content merge handled.
test_expect_success 'setup criss-cross + rename/rename/add-source + modify/modify' '
- test_create_repo rename-rename-add-source &&
+ git init rename-rename-add-source &&
(
cd rename-rename-add-source &&
@@ -1032,7 +1032,7 @@ test_expect_failure 'detect rename/rename/add-source for virtual merge-base' '
# base of B & C needs to not delete B:c for that to work, though...
test_expect_success 'setup criss-cross+rename/rename/add-dest + simple modify' '
- test_create_repo rename-rename-add-dest &&
+ git init rename-rename-add-dest &&
(
cd rename-rename-add-dest &&
@@ -1111,7 +1111,7 @@ test_expect_success 'virtual merge base handles rename/rename(1to2)/add-dest' '
# git detect it?
test_expect_success 'setup symlink modify/modify' '
- test_create_repo symlink-modify-modify &&
+ git init symlink-modify-modify &&
(
cd symlink-modify-modify &&
@@ -1178,7 +1178,7 @@ test_expect_merge_algorithm failure success 'check symlink modify/modify' '
# git detect it?
test_expect_success 'setup symlink add/add' '
- test_create_repo symlink-add-add &&
+ git init symlink-add-add &&
(
cd symlink-add-add &&
@@ -1244,11 +1244