aboutsummaryrefslogtreecommitdiffstats
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.pl770
-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.expect15
-rw-r--r--t/chainlint/block.test15
-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/command-substitution-subsubshell.expect2
-rw-r--r--t/chainlint/command-substitution-subsubshell.test3
-rw-r--r--t/chainlint/complex-if-in-cuddled-loop.expect2
-rw-r--r--t/chainlint/double-here-doc.expect2
-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.expect3
-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.expect4
-rw-r--r--t/chainlint/function.expect11
-rw-r--r--t/chainlint/function.test13
-rw-r--r--t/chainlint/here-doc-indent-operator.expect5
-rw-r--r--t/chainlint/here-doc-indent-operator.test13
-rw-r--r--t/chainlint/here-doc-multi-line-string.expect3
-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/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-loop-detect-failure.expect31
-rw-r--r--t/chainlint/nested-loop-detect-failure.test35
-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/t7900-subtree.expect13
-rw-r--r--t/chainlint/token-pasting.expect27
-rw-r--r--t/chainlint/token-pasting.test32
-rw-r--r--t/chainlint/while-loop.expect4
-rwxr-xr-xt/check-non-portable-shell.pl1
-rw-r--r--t/helper/test-bundle-uri.c95
-rw-r--r--t/helper/test-config.c2
-rw-r--r--t/helper/test-crontab.c23
-rw-r--r--t/helper/test-fast-rebase.c2
-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-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-serve-v2.c2
-rw-r--r--t/helper/test-submodule-config.c11
-rw-r--r--t/helper/test-submodule.c140
-rw-r--r--t/helper/test-tool-utils.h9
-rw-r--r--t/helper/test-tool.c11
-rw-r--r--t/helper/test-tool.h5
-rw-r--r--t/helper/test-trace2.c187
-rw-r--r--t/helper/test-userdiff.c2
-rw-r--r--t/lib-bitmap.sh2
-rw-r--r--t/lib-httpd/apache.conf2
-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.sh2
-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/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/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.sh253
-rwxr-xr-xt/t0050-filesystem.sh1
-rwxr-xr-xt/t0060-path-utils.sh2
-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/t1006-cat-file.sh72
-rwxr-xr-xt/t1020-subdirectory.sh1
-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/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.sh26
-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.sh19
-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.sh6
-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/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/t5100-mailinfo.sh4
-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.sh13
-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.sh42
-rwxr-xr-xt/t5520-pull.sh17
-rwxr-xr-xt/t5526-fetch-submodules.sh9
-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.sh81
-rwxr-xr-xt/t5557-http-get.sh39
-rwxr-xr-xt/t5558-clone-bundle-uri.sh356
-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/t6019-rev-list-ancestry-path.sh101
-rwxr-xr-xt/t6102-rev-list-unexpected-objects.sh4
-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/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/t7001-mv.sh2
-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.sh60
-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.sh1
-rwxr-xr-xt/t7408-submodule-reference.sh4
-rwxr-xr-xt/t7409-submodule-detached-work-tree.sh4
-rwxr-xr-xt/t7411-submodule-config.sh3
-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.sh4
-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/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/t7700-repack.sh61
-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.sh42
-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.sh213
-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.sh118
-rw-r--r--t/test-lib.sh287
335 files changed, 9213 insertions, 2334 deletions
diff --git a/t/Makefile b/t/Makefile
index 7f56e52f76..882782a519 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 | \
+ grep -v '^[ ]*$$' >'$(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 4f9981cf5e..979b2d4833 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 0000000000..976db4b8a0
--- /dev/null
+++ b/t/chainlint.pl
@@ -0,0 +1,770 @@
+#!/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,
+ heretags => []
+ } => $class;
+}
+
+sub scan_heredoc_tag {
+ my $self = shift @_;
+ ${$self->{buff}} =~ /\G(-?)/gc;
+ my $indented = $1;
+ my $tag = $self->scan_token();
+ $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;
+ return "'" . $1;
+}
+
+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;
+ 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");
+ }
+ 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;
+ }
+ 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(' ', $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 $indent = $tag =~ s/^\t// ? '\\s*' : '';
+ $$b =~ /(?:\G|\n)$indent\Q$tag\E(?:\n|\z)/gc;
+ }
+}
+
+sub scan_token {
+ my $self = shift @_;
+ my $b = $self->{buff};
+ my $token = '';
+RESTART:
+ $$b =~ /\G[ \t]+/gc; # skip whitespace (but not newline)
+ return "\n" 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;
+ 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->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;
+ next if $c eq "\n" && length($token); # line splice
+ goto RESTART if $c eq "\n"; # line splice
+ $token .= '\\' . $c;
+ next;
+ }
+ die("internal error scanning character '$c'\n");
+ }
+ return length($token) ? $token : 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 =~ $stop;
+}
+
+sub expect {
+ my ($self, $expect) = @_;
+ my $token = $self->next_token();
+ return $token if defined($token) && $token eq $expect;
+ push(@{$self->{output}}, "?!ERR?! expected '$expect' but found '" . (defined($token) ? $token : "<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 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 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 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 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 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 eq 'elif';
+ push(@tokens, $self->expect('elif'));
+ }
+ my $token = $self->peek();
+ if (defined($token) && $token 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 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 eq "\n";
+
+ my $token;
+ my @tokens = $cmd;
+ if ($cmd eq '!') {
+ push(@tokens, $self->parse_cmd());
+ return @tokens;
+ } elsif (my $f = $compound{$cmd}) {
+ push(@tokens, $self->$f());
+ } elsif (defined($token = $self->peek()) && $token eq '(') {
+ if ($cmd !~ /\w=$/) {
+ push(@tokens, $self->parse_func());
+ return @tokens;
+ }
+ $tokens[-1] .= join(' ', $self->parse_bash_array_assignment());
+ }
+
+ while (defined(my $token = $self->next_token())) {
+ $self->untoken($token), last if $self->stop_at($token);
+ push(@tokens, $token);
+ last if $token =~ /^(?:[;&\n|]|&&|\|\|)$/;
+ }
+ push(@tokens, $self->next_token()) if $tokens[-1] ne "\n" && defined($token = $self->peek()) && $token 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 find_non_nl {
+ my $tokens = shift @_;
+ my $n = shift @_;
+ $n = $#$tokens if !defined($n);
+ $n-- while $n >= 0 && $$tokens[$n] 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] !~ $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(/^(?: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);
+ splice(@tokens, $n + 1, 0, '?!LOOP?!');
+ 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) = @_;
+ goto DONE unless @$tokens;
+ goto DONE if @$cmd == 1 && $$cmd[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 =~ /\$\?/;
+ }
+
+ # 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] =~ /^(?:false|return|exit)$/) {
+ @$tokens = grep(!/^\?!AMP\?!$/, @$tokens);
+ goto DONE;
+ }
+
+ # flag missing "&&" at end of previous command
+ my $n = find_non_nl($tokens);
+ splice(@$tokens, $n + 1, 0, '?!AMP?!') 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 @_ : $_;
+ # 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();
+ return unless $emit_all || grep(/\?![^?]+\?!/, @tokens);
+ my $c = main::fd_colors(1);
+ my $checked = join(' ', @tokens);
+ $checked =~ s/^\n//;
+ $checked =~ s/^ //mg;
+ $checked =~ s/ $//mg;
+ $checked =~ s/(\?![^?]+\?!)/$c->{rev}$c->{red}$1$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] =~ /^test_expect_(?:success|failure)$/;
+ my $n = $#tokens;
+ $n-- while $n >= 0 && $tokens[$n] =~ /^(?:[;&\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 => '', reset => '', blue => '', green => '', red => '');
+my %COLORS = ();
+sub get_colors {
+ return \%COLORS if %COLORS;
+ if (exists($ENV{NO_COLOR}) ||
+ 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 setaf 1 >/dev/null 2>&1") != 0) {
+ %COLORS = @NOCOLORS;
+ return \%COLORS;
+ }
+ %COLORS = (bold => `tput bold`,
+ rev => `tput rev`,
+ reset => `tput sgr0`,
+ blue => `tput setaf 4`,
+ green => `tput setaf 2`,
+ red => `tput setaf 1`);
+ chomp(%COLORS);
+ 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*:/, <>)); } 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 dc4ce37cb5..0000000000
--- 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 0000000000..48ed4eb124
--- /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 0000000000..cecccad19f
--- /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.expect b/t/chainlint/block.expect
index da60257ebc..a3bcea492a 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 0a82fd579f..4ab69a4afc 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/chain-break-background.expect b/t/chainlint/chain-break-background.expect
new file mode 100644
index 0000000000..28f9114f42
--- /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 0000000000..e10f656b05
--- /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 0000000000..47a3457710
--- /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 0000000000..f0af71d8bd
--- /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 0000000000..989766fb85
--- /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 0000000000..a5aaff8c8a
--- /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 0000000000..1732d221c3
--- /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 0000000000..46542edf88
--- /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 0000000000..f4bada9463
--- /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 0000000000..a6602a7b99
--- /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 0000000000..574cdceb07
--- /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 0000000000..86f81ece63
--- /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 0000000000..af0369d328
--- /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 0000000000..4ff6ddd8cb
--- /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/command-substitution-subsubshell.expect b/t/chainlint/command-substitution-subsubshell.expect
new file mode 100644
index 0000000000..ab2f79e845
--- /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 0000000000..321de2951c
--- /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/complex-if-in-cuddled-loop.expect b/t/chainlint/complex-if-in-cuddled-loop.expect
index 2fca183409..dac2d0fd1d 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 0000000000..75477bb1ad
--- /dev/null
+++ b/t/chainlint/double-here-doc.expect
@@ -0,0 +1,2 @@
+run_sub_test_lib_test_err run-inv-range-start "--run invalid range start" --run="a-5" <<-EOF &&
+check_sub_test_lib_test_err run-inv-range-start <<-EOF_OUT 3 <<-EOF_ERR
diff --git a/t/chainlint/double-here-doc.test b/t/chainlint/double-here-doc.test
new file mode 100644
index 0000000000..cd584a4357
--- /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 0000000000..bf9ced60d4
--- /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 0000000000..b40714439f
--- /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 0000000000..10724987a5
--- /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 0000000000..d2f4219cbb
--- /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 0000000000..f42f2d41ba
--- /dev/null
+++ b/t/chainlint/empty-here-doc.expect
@@ -0,0 +1,3 @@
+git ls-tree $tree path > current &&
+cat > expected <<EOF &&
+test_output
diff --git a/t/chainlint/empty-here-doc.test b/t/chainlint/empty-here-doc.test
new file mode 100644
index 0000000000..24fc165de3
--- /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 0000000000..2d961a58c6
--- /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 0000000000..323595b5bd
--- /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 0000000000..a21007a63f
--- /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 0000000000..1084eccb89
--- /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 6671b8cd84..a5810c9bdd 100644
--- a/t/chainlint/for-loop.expect
+++ b/t/chainlint/for-loop.expect
@@ -2,10 +2,10 @@
for i in a b c
do
echo $i ?!AMP?!
- cat <<-EOF
+ cat <<-EOF ?!LOOP?!
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 0000000000..a14388e6b9
--- /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 0000000000..5ee59562c9
--- /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-indent-operator.expect b/t/chainlint/here-doc-indent-operator.expect
new file mode 100644
index 0000000000..fb6cf7285d
--- /dev/null
+++ b/t/chainlint/here-doc-indent-operator.expect
@@ -0,0 +1,5 @@
+cat > expect <<-EOF &&
+
+cat > expect <<-EOF ?!AMP?!
+
+cleanup
diff --git a/t/chainlint/here-doc-indent-operator.test b/t/chainlint/here-doc-indent-operator.test
new file mode 100644
index 0000000000..c8a6f18eb4
--- /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-string.expect b/t/chainlint/here-doc-multi-line-string.expect
index 2578191ca8..be64b26869 100644
--- a/t/chainlint/here-doc-multi-line-string.expect
+++ b/t/chainlint/here-doc-multi-line-string.expect
@@ -1,4 +1,5 @@
(
- cat <<-TXT && echo "multi-line string" ?!AMP?!
+ cat <<-TXT && echo "multi-line
+ string" ?!AMP?!
bap
)
diff --git a/t/chainlint/if-condition-split.expect b/t/chainlint/if-condition-split.expect
new file mode 100644
index 0000000000..ee745ef8d7
--- /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 0000000000..240daa9fd5
--- /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 03b82a3e58..d6514ae749 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 f0cf19cfad..90c23976fe 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/loop-detect-failure.expect b/t/chainlint/loop-detect-failure.expect
new file mode 100644
index 0000000000..a66025c39d
--- /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 0000000000..b9791cc802
--- /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 0000000000..0ad23bb35e
--- /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" &&
+
+ 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 0000000000..1c6c23cfc9
--- /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 e1be42376c..6c5d6e5b24 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 0000000000..0b82ecc4b9
--- /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 0000000000..efb77da897
--- /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 ab0dadf748..27ff95218e 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-loop-detect-failure.expect b/t/chainlint/nested-loop-detect-failure.expect
new file mode 100644
index 0000000000..4793a0e8e1
--- /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 0000000000..e6f0c1acfb
--- /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.expect b/t/chainlint/nested-subshell.expect
index 41a48adaa2..02e0a9f1bb 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 0000000000..51a3dc7c54
--- /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 0000000000..4bd8c066c7
--- /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 0000000000..cfc0549bef
--- /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 0000000000..f90b171300
--- /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 ed0b3707ae..3aa2259f36 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 0000000000..cf0b591cf7
--- /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 0000000000..77a425e0c7
--- /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/t7900-subtree.expect b/t/chainlint/t7900-subtree.expect
index 1cccc7bf7e..69167da2f2 100644
--- a/t/chainlint/t7900-subtree.expect
+++ b/t/chainlint/t7900-subtree.expect
@@ -1,10 +1,17 @@
(
- chks="sub1sub2sub3sub4" &&
+ chks="sub1
+sub2
+sub3
+sub4" &&
chks_sub=$(cat <<TXT | sed "s,^,sub dir/,"
) &&
- chkms="main-sub1main-sub2main-sub3main-sub4" &&
+ chkms="main-sub1
+main-sub2
+main-sub3
+main-sub4" &&
chkms_sub=$(cat <<TXT | sed "s,^,sub dir/,"
) &&
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 0000000000..342360bcd0
--- /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 0000000000..b4610ce815
--- /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 0d3a9b3d12..f272aa21fe 100644
--- a/t/chainlint/while-loop.expect
+++ b/t/chainlint/while-loop.expect
@@ -2,10 +2,10 @@
while true
do
echo foo ?!AMP?!
- cat <<-EOF
+ cat <<-EOF ?!LOOP?!
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 fd3303552b..dd8107cd7d 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 0000000000..25afd39342
--- /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-config.c b/t/helper/test-config.c
index a6e936721f..4ba9eb6560 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 e7c0137a47..e6c1b1e22b 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-fast-rebase.c b/t/helper/test-fast-rebase.c
index 4e5553e202..45665ec19a 100644
--- a/t/helper/test-fast-rebase.c
+++ b/t/helper/test-fast-rebase.c
@@ -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-mergesort.c b/t/helper/test-mergesort.c
index 202e54a7ff..335e5bb3a9 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 48d3cf6692..506835521a 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 d20e1b7a18..f69709d674 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 cc08506cf0..a4b305f494 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-ref-store.c b/t/helper/test-ref-store.c
index 4d18bfb1ca..ae8a5648da 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 0000000000..f8d564c622
--- /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 c9283b47af..3ecb830f4a 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-serve-v2.c b/t/helper/test-serve-v2.c
index 28e905afc3..824e5c0a95 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-submodule-config.c b/t/helper/test-submodule-config.c
index e2692746df..22a41c4092 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 0000000000..b7d117cd55
--- /dev/null
+++ b/t/helper/test-submodule.c
@@ -0,0 +1,140 @@
+#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 struct test_cmd cmds[] = {
+ { "check-name", cmd__submodule_check_name },
+ { "is-active", cmd__submodule_is_active },
+ { "resolve-relative-url", cmd__submodule_resolve_relative_url},
+};
+
+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 0000000000..6a0e5e0074
--- /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 318fdbab0c..01cda9358d 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,11 @@ 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 },
{ "chmtime", cmd__chmtime },
{ "config", cmd__config },
{ "crontab", cmd__crontab },
@@ -51,7 +48,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 +64,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 },
@@ -78,6 +78,7 @@ static struct test_cmd cmds[] = {
{ "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 bb79927163..ca2948066f 100644
--- a/t/helper/test-tool.h
+++ b/t/helper/test-tool.h
@@ -7,6 +7,7 @@
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__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);
@@ -68,6 +72,7 @@ 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 a714130ece..1b092c6071 100644
--- a/t/helper/test-trace2.c
+++ b/t/helper/test-trace2.c
@@ -229,6 +229,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 +429,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 f013f8a31e..a2b56b9cae 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/lib-bitmap.sh b/t/lib-bitmap.sh
index a95537e759..f595937094 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/apache.conf b/t/lib-httpd/apache.conf
index 497b9b9d92..706799391b 100644
--- a/t/lib-httpd/apache.conf
+++ b/t/lib-httpd/apache.conf
@@ -80,6 +80,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 0000000000..d0bf509a16
--- /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 03e0abbdb8..2d31fcfda1 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 fb9127a66f..8f217d7be7 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 63d3bc7cec..55a8feb1dc 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 1afc08fe7f..85be14e4dd 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 900b385c4b..c481c012d2 100755
--- a/t/perf/p0006-read-tree-checkout.sh
+++ b/t/perf/p0006-read-tree-checkout.sh
@@ -46,7 +46,7 @@ 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 "switch between br_base br_ballast ($nr_files)" '
diff --git a/t/perf/p2000-sparse-operations.sh b/t/perf/p2000-sparse-operations.sh
index c181110a43..3242cfe91a 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 7ad4f237bc..b1399f1007 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 47c3fd7581..426fab87e3 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 0000000000..0684b690af
--- /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 f2fa228f16..d082e6cacb 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/p7527-builtin-fsmonitor.sh b/t/perf/p7527-builtin-fsmonitor.sh
index 9338b9ea00..c3f9a4caa4 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 0000000000..265f7cd1fe
--- /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 27c2801792..e7786775a9 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 55219aa405..34115edec3 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 17a268ccd1..502b4bcf9e 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 f6356db183..26eaca095a 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 2e9d652d82..8114fac73b 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 6c33a43690..dbfc5c8267 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/t0021-conversion.sh b/t/t0021-conversion.sh
index 1c840348bd..abecd75e4e 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 7bb93768f3..0000000000
--- 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 7f80f46393..a94ac1eae3 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 0ed14971a5..471cb37ac2 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 f4d737dadd..dc3496897a 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 ecbdc8238d..11c15a48aa 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 ed2fb620a9..5cc62306e3 100755
--- a/t/t0040-parse-options.sh
+++ b/t/t0040-parse-options.sh
@@ -456,4 +456,257 @@ 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_done
diff --git a/t/t0050-filesystem.sh b/t/t0050-filesystem.sh
index 5c9dc90d0b..325eb1c3cd 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 1f2007e62b..68e29c904a 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/t0091-bugreport.sh b/t/t0091-bugreport.sh
index 08f5fe9cae..b6d2f591ac 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 0000000000..133e5747d6
--- /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 daeb4a5e3e..b567383eb8 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 df2ea34932..5a6f28051b 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 0ce1f22eff..86cff324ff 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 22d0845544..0b3436e8ca 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 '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 '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 299999f0f8..7a50bae646 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 1e864cf317..5b7bee888d 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 0000000000..cd3969e852
--- /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 0000000000..a0777acd66
--- /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 bd5313caec..cdc077ce12 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/t1006-cat-file.sh b/t/t1006-cat-file.sh
index dadf3b1458..23b8942edb 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -88,7 +88,8 @@ done
for opt in --buffer \
--follow-symlinks \
- --batch-all-objects
+ --batch-all-objects \
+ -z
do
test_expect_success "usage: bad option combination: $opt without batch mode" '
test_incompatible_usage git cat-file $opt &&
@@ -100,6 +101,10 @@ echo_without_newline () {
printf '%s' "$*"
}
+echo_without_newline_nul () {
+ echo_without_newline "$@" | tr '\n' '\0'
+}
+
strlen () {
echo_without_newline "$1" | wc -c | sed -e 's/^ *//'
}
@@ -398,6 +403,12 @@ test_expect_success '--batch with multiple sha1s gives correct format' '
test "$(maybe_remove_timestamp "$batch_output" 1)" = "$(maybe_remove_timestamp "$(echo_without_newline "$batch_input" | git cat-file --batch)" 1)"
'
+test_expect_success '--batch, -z with multiple sha1s gives correct format' '
+ echo_without_newline_nul "$batch_input" >in &&
+ test "$(maybe_remove_timestamp "$batch_output" 1)" = \
+ "$(maybe_remove_timestamp "$(git cat-file --batch -z <in)" 1)"
+'
+
batch_check_input="$hello_sha1
$tree_sha1
$commit_sha1
@@ -418,6 +429,30 @@ test_expect_success "--batch-check with multiple sha1s gives correct format" '
"$(echo_without_newline "$batch_check_input" | git cat-file --batch-check)"
'
+test_expect_success "--batch-check, -z with multiple sha1s gives correct format" '
+ echo_without_newline_nul "$batch_check_input" >in &&
+ test "$batch_check_output" = "$(git cat-file --batch-check -z <in)"
+'
+
+test_expect_success FUNNYNAMES '--batch-check, -z with newline in input' '
+ touch -- "newline${LF}embedded" &&
+ git add -- "newline${LF}embedded" &&
+ git commit -m "file with newline embedded" &&
+ test_tick &&
+
+ printf "HEAD:newline${LF}embedded" >in &&
+ git cat-file --batch-check -z <in >actual &&
+
+ echo "$(git rev-parse "HEAD:newline${LF}embedded") blob 0" >expect &&
+ test_cmp expect actual
+'
+
+batch_command_multiple_info="info $hello_sha1
+info $tree_sha1
+info $commit_sha1
+info $tag_sha1
+info deadbeef"
+
test_expect_success '--batch-command with multiple info calls gives correct format' '
cat >expect <<-EOF &&
$hello_sha1 blob $hello_size
@@ -427,17 +462,23 @@ test_expect_success '--batch-command with multiple info calls gives correct form
deadbeef missing
EOF
- git cat-file --batch-command --buffer >actual <<-EOF &&
- info $hello_sha1
- info $tree_sha1
- info $commit_sha1
- info $tag_sha1
- info deadbeef
- EOF
+ echo "$batch_command_multiple_info" >in &&
+ git cat-file --batch-command --buffer <in >actual &&
+
+ test_cmp expect actual &&
+
+ echo "$batch_command_multiple_info" | tr "\n" "\0" >in &&
+ git cat-file --batch-command --buffer -z <in >actual &&
test_cmp expect actual
'
+batch_command_multiple_contents="contents $hello_sha1
+contents $commit_sha1
+contents $tag_sha1
+contents deadbeef
+flush"
+
test_expect_success '--batch-command with multiple command calls gives correct format' '
remove_timestamp >expect <<-EOF &&
$hello_sha1 blob $hello_size
@@ -449,13 +490,14 @@ test_expect_success '--batch-command with multiple command calls gives correct f
deadbeef missing
EOF
- git cat-file --batch-command --buffer >actual_raw <<-EOF &&
- contents $hello_sha1
- contents $commit_sha1
- contents $tag_sha1
- contents deadbeef
- flush
- EOF
+ echo "$batch_command_multiple_contents" >in &&
+ git cat-file --batch-command --buffer <in >actual_raw &&
+
+ remove_timestamp <actual_raw >actual &&
+ test_cmp expect actual &&
+
+ echo "$batch_command_multiple_contents" | tr "\n" "\0" >in &&
+ git cat-file --batch-command --buffer -z <in >actual_raw &&
remove_timestamp <actual_raw >actual &&
test_cmp expect actual
diff --git a/t/t1020-subdirectory.sh b/t/t1020-subdirectory.sh
index 9fdbb2af80..45eef9457f 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/t1060-object-corruption.sh b/t/t1060-object-corruption.sh
index 5b8e47e346..35261afc9d 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 de1ec89007..b563d6c263 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 763c6cc684..801919009e 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/t1304-default-acl.sh b/t/t1304-default-acl.sh
index 335d3f3211..c69ae41306 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 9fb0b90f25..d708acdb81 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 51f8291628..e4627cf1b6 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 ad8006c813..05b1881c59 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 d51ecd5e92..2268bca3c1 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 ab7f31f1dc..ace4556788 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 1c2df08333..81de584ea2 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 284fe18e72..de1d48f3ba 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 ba43168d12..bc136833c1 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 5dc221ef38..d8fa489998 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 210f429887..43fcb7c0bf 100755
--- a/t/t1800-hook.sh
+++ b/t/t1800-hook.sh
@@ -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 7705e3a317..5d119871d4 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 bc46713a43..2eab6474f8 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 7b327b7544..81e772fb4e 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 c683e60007..5ffe1a41e2 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 2525457961..f3511cd43a 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 43d950de64..98265ba1b4 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 f3242fef6b..d587e0b20d 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 a4e1a178e0..230a55e99a 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 b172c26ca4..11018f37c7 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 50815acd3e..019a40df2c 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 e07ac6c6dc..1ed0aa967e 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 2682b1f43a..190e2f6eed 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 f9539968e4..5d871fde96 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 9723c2827c..7f605f865b 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,17 @@ 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 -v -d t should work' '
git branch t &&
git rev-parse --verify refs/heads/t &&
@@ -306,6 +317,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 +1393,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 f2b9199007..ea7cfd1951 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 993a6b5eff..793bf4d269 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 d12e4e4cc6..84dd0cd26d 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 cfde6b237f..fe72b24716 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 d742be8840..3288aaec7d 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 64a9915761..1ec1fb6715 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 1aa366a410..ae316502c4 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 688b01e3eb..4f5abb5ad2 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' '
diff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh
index d17b450e81..ceca160005 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 78c27496d6..a364530d76 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 3e04802cb0..ea501f2b42 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 295040f2fe..7181f176b8 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 43fcb68f27..693934ee8b 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 7a9f1127a4..ba069dccbd 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/t3431-rebase-fork-point.sh b/t/t3431-rebase-fork-point.sh
index 1d0b15380e..70e8136356 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 5f8ba2c739..6aa2aeb628 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 0000000000..b92a3ce46b
--- /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 c657840db3..f22d1ddead 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 e74a318ac3..0e8afe49ed 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 8689b48589..51afbd7b24 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 b354fb39de..5841f280fb 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 a1801a8cbd..82bfb2fd2a 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 2a4c3fd61c..376cc8f4ab 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 a52e53dd2d..0f7348ec21 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 0276edbe3d..4c661d4d54 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 c509143c81..c64d9d2f40 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 056e922164..dfcf3a0aaa 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 3f9b872ece..6b0b334a5d 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 0000000000..1c030a6554
--- /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 0000000000..d6e7928784
--- /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 f5e20e1e14..c7df1f5814 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 0000000000..88be82cce3
--- /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 0000000000..5d22618bb6
--- /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 fbec8ad2ef..de1da4673d 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 ed461f481e..5bc28ad9f0 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 858a5522f9..c1ac09ecc7 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 9a292bac70..2ce26e585c 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 4838a1df8b..725278ad19 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 04b8a1542a..9a7505cbb8 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 49bca7b48d..d489230df8 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 d86e38abd8..97c6424cd5 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 804f2a82e8..28f42a4046 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 35f94957fc..07323ebafe 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 da3e64f811..8ff3640766 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 0000000000..58742d4fc5
--- /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 3095b1b2ff..8e4effebdb 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 6e66352558..2ce2b41174 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 a730c0db98..a7fa94ce0a 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 36ac6aff1e..ded33a82e2 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 7f0c1dcc0f..2e8f5ad7b8 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 f091259a55..cac85591b5 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/t5100-mailinfo.sh b/t/t5100-mailinfo.sh
index cebad1048c..db11cababd 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/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh
index f775fc1ce6..6d693eef82 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 872a95df33..9dae60f73e 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 8bacd96275..c80ea9e8b7 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 1b0cd82359..049c5fc8ea 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 afbe93f162..b5f9b10922 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 124d47603d..406363381f 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 4fe57414c1..0882cbb6e4 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 d30ba632c8..e65e311cd7 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 8968f7a08d..303f7a5d84 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 f785cb0617..8c8af99b84 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 () {
@@ -70,9 +71,15 @@ test_expect_success 'unpack big object in stream (core.fsyncmethod=batch)' '
GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
GIT_TEST_FSYNC=true \
git -C dest.git $BATCH_CONFIGURATION unpack-objects <pack-$PACK.pack &&
- check_fsync_events trace2.txt <<-\EOF &&
+ if grep "core.fsyncMethod = batch is unsupported" trace2.txt
+ then
+ flush_count=7
+ else
+ flush_count=1
+ fi &&
+ check_fsync_events trace2.txt <<-EOF &&
"key":"fsync/writeout-only","value":"6"
- "key":"fsync/hardware-flush","value":"1"
+ "key":"fsync/hardware-flush","value":"$flush_count"
EOF
test_dir_is_empty dest.git/objects/pack &&
@@ -87,7 +94,7 @@ test_expect_success 'do not unpack existing large objects' '
# The destination came up with the exact same pack...
DEST_PACK=$(echo dest.git/objects/pack/pack-*.pack) &&
- test_cmp pack-$PACK.pack $DEST_PACK &&
+ cmp pack-$PACK.pack $DEST_PACK &&
# ...and wrote no loose objects
test_stdout_line_count = 0 find dest.git/objects -type f ! -name "pack-*"
diff --git a/t/t5402-post-merge-hook.sh b/t/t5402-post-merge-hook.sh
index 915af2de95..46ebdfbeeb 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 ee6d2dde9f..d18f2823d8 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 195fc64dd4..5ebbaa4896 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 b0b795aca9..ac4099ca89 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 6c7370f87f..43b7bcd715 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 b45879a760..c0b745e33b 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 f3356f9ea8..4f2bfaf005 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,37 +1853,6 @@ 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 &&
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index 081808009b..0b72112fb1 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 a301b56db8..75da8acf8f 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 &&
@@ -714,7 +715,13 @@ test_expect_success 'fetching submodules respects parallel settings' '
GIT_TRACE=$(pwd)/trace.out git fetch &&
grep "8 tasks" 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/t5537-fetch-shallow.sh b/t/t5537-fetch-shallow.sh
index 10e9a7ff26..37f7547a4c 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 214228349a..a158e7d2c0 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 d7cf85ffea..8f182a3cbf 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 245532df88..64c6c9f59e 100755
--- a/t/t5551-http-fetch-smart.sh
+++ b/t/t5551-http-fetch-smart.sh
@@ -181,8 +181,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
'
@@ -580,4 +580,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 0000000000..76a4bbd16a
--- /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 0000000000..9155f31fa2
--- /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/t5572-pull-submodule.sh b/t/t5572-pull-submodule.sh
index a35396fadf..09097eff3f 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 cf3be0584f..b2524a24c2 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 24340e6d56..2734e37e88 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 8f676d6b0c..cf221e92c4 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 5504b519c7..0c85ef834a 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 4a3778d04a..037941b95d 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 ca8f80083a..6884338249 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 5d42a355a8..b33cd4afca 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 9d6cd7d986..df74f80061 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 0000000000..c2fe3f9c5a
--- /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 3153a0d891..2cdef6fdf9 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/t6019-rev-list-ancestry-path.sh b/t/t6019-rev-list-ancestry-path.sh
index af57a04b7f..738da23628 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/t6102-rev-list-unexpected-objects.sh b/t/t6102-rev-list-unexpected-objects.sh
index cf0195e826..4a9a4436e2 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,7 +121,7 @@ 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)' '
+test_expect_success 'TODO (should fail!): traverse unexpected non-blob tag (lone)' '
git rev-list --objects $tag
'
diff --git a/t/t6115-rev-list-du.sh b/t/t6115-rev-list-du.sh
index b4aef32b71..d59111dede 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 9fdafeb1e9..cada952f9a 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 0f1cb49ced..3214d9db97 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/t6400-merge-df.sh b/t/t6400-merge-df.sh
index 57a67cf362..3de4ef6bd9 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 b8735c6db4..36215518b6 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 7435fce71e..29e2b25ce5 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 99abefd44b..8650a88c40 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 0753fc95f4..e8a28717ce 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 7763c1ba98..8a1ba6d23a 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 6ae2489286..b6182723aa 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 affea255fe..b4f4a313f4 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 690c8482b1..17b54d625d 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,11 @@ test_expect_merge_algorithm failure success 'check symlink add/add' '
# git detect it?
test_expect_success 'setup submodule modify/modify' '
- test_create_repo submodule-modify-modify &&
+ git init submodule-modify-modify &&
(
cd submodule-modify-modify &&
- test_create_repo submod &&
+ git init submod &&
(
cd submod &&
touch file-A &&
@@ -1332,11 +1332,11 @@ test_expect_merge_algorithm failure success 'check submodule modify/modify' '
# git detect it?
test_expect_success 'setup submodule add/add' '
- test_create_repo submodule-add-add &&
+ git init submodule-add-add &&
(
cd submodule-add-add &&
- test_create_repo submod &&
+ git init submod &&
(
cd submod &&
touch file-A &&
@@ -1419,11 +1419,11 @@ test_expect_merge_algorithm failure success 'check submodule add/add' '
# This is an obvious add/add conflict for 'path'. Can git detect it?
test_expect_success 'setup conflicting entry types (submodule vs symlink)' '
- test_create_repo submodule-symlink-add-add &&
+ git init submodule-symlink-add-add &&
(
cd submodule-symlink-add-add &&
- test_create_repo path &&
+ git init path &&
(
cd path &&
touch file-B &&
@@ -1494,7 +1494,7 @@ test_expect_merge_algorithm failure success 'check conflicting entry types (subm
# This is an obvious add/add mode conflict. Can git detect it?
test_expect_success 'setup conflicting modes for regular file' '
- test_create_repo regular-file-mode-conflict &&
+ git init regular-file-mode-conflict &&
(
cd regular-file-mode-conflict &&
@@ -1571,7 +1571,7 @@ test_expect_failure 'check conflicting modes for regular file' '
# to ensure that we handle it as well as practical.
test_expect_success 'setup nested conflicts' '
- test_create_repo nested_conflicts &&
+ git init nested_conflicts &&
(
cd nested_conflicts &&
@@ -1757,7 +1757,7 @@ test_expect_success 'check nested conflicts' '
# have three levels of conflict markers. Can we distinguish all three?
test_expect_success 'setup virtual merge base with nested conflicts' '
- test_create_repo virtual_merge_base_has_nested_conflicts &&
+ git init virtual_merge_base_has_nested_conflicts &&
(
cd virtual_merge_base_has_nested_conflicts &&
diff --git a/t/t6421-merge-partial-clone.sh b/t/t6421-merge-partial-clone.sh
index 36bcd7c328..5413e5dd9d 100755
--- a/t/t6421-merge-partial-clone.sh
+++ b/t/t6421-merge-partial-clone.sh
@@ -31,7 +31,7 @@ test_description="limiting blob downloads when merging with partial clones"
test_setup_repo () {
test -d server && return
- test_create_repo server &&
+ git init server &&
(
cd server &&
diff --git a/t/t6422-merge-rename-corner-cases.sh b/t/t6422-merge-rename-corner-cases.sh
index 9b65768aed..346253c7c8 100755
--- a/t/t6422-merge-rename-corner-cases.sh
+++ b/t/t6422-merge-rename-corner-cases.sh
@@ -11,7 +11,7 @@ TEST_PASSES_SANITIZE_LEAK=true
. "$TEST_DIRECTORY"/lib-merge.sh
test_setup_rename_delete_untracked () {
- test_create_repo rename-delete-untracked &&
+ git init rename-delete-untracked &&
(
cd rename-delete-untracked &&
@@ -56,7 +56,7 @@ test_expect_success "Does git preserve Gollum's precious artifact?" '
# We should be able to merge B & C cleanly
test_setup_rename_modify_add_source () {
- test_create_repo rename-modify-add-source &&
+ git init rename-modify-add-source &&
(
cd rename-modify-add-source &&
@@ -96,7 +96,7 @@ test_expect_failure 'rename/modify/add-source conflict resolvable' '
'
test_setup_break_detection_1 () {
- test_create_repo break-detection-1 &&
+ git init break-detection-1 &&
(
cd break-detection-1 &&
@@ -144,7 +144,7 @@ test_expect_failure 'conflict caused if rename not detected' '
'
test_setup_break_detection_2 () {
- test_create_repo break-detection-2 &&
+ git init break-detection-2 &&
(
cd break-detection-2 &&
@@ -192,7 +192,7 @@ test_expect_failure 'missed conflict if rename not detected' '
# Commit C: rename a->b, add unrelated a
test_setup_break_detection_3 () {
- test_create_repo break-detection-3 &&
+ git init break-detection-3 &&
(
cd break-detection-3 &&
@@ -268,7 +268,7 @@ test_expect_failure 'detect rename/add-source and preserve all data, merge other
'
test_setup_rename_directory () {
- test_create_repo rename-directory-$1 &&
+ git init rename-directory-$1 &&
(
cd rename-directory-$1 &&
@@ -386,7 +386,7 @@ test_expect_success 'rename/directory conflict + content merge conflict' '
'
test_setup_rename_directory_2 () {
- test_create_repo rename-directory-2 &&
+ git init rename-directory-2 &&
(
cd rename-directory-2 &&
@@ -445,7 +445,7 @@ test_expect_success 'disappearing dir in rename/directory conflict handled' '
# Commit B: modify a, add different b
test_setup_rename_with_content_merge_and_add () {
- test_create_repo rename-with-content-merge-and-add-$1 &&
+ git init rename-with-content-merge-and-add-$1 &&
(
cd rename-with-content-merge-and-add-$1 &&
@@ -570,7 +570,7 @@ test_expect_success 'handle rename-with-content-merge vs. add, merge other way'
# * Nothing else should be present. Is anything?
test_setup_rename_rename_2to1 () {
- test_create_repo rename-rename-2to1 &&
+ git init rename-rename-2to1 &&
(
cd rename-rename-2to1 &&
@@ -642,7 +642,7 @@ test_expect_success 'handle rename/rename (2to1) conflict correctly' '
# Commit B: rename a->b
# Commit C: rename a->c
test_setup_rename_rename_1to2 () {
- test_create_repo rename-rename-1to2 &&
+ git init rename-rename-1to2 &&
(
cd rename-rename-1to2 &&
@@ -700,7 +700,7 @@ test_expect_success 'merge has correct working tree contents' '
# Merging of B & C should NOT be clean; there's a rename/rename conflict
test_setup_rename_rename_1to2_add_source_1 () {
- test_create_repo rename-rename-1to2-add-source-1 &&
+ git init rename-rename-1to2-add-source-1 &&
(
cd rename-rename-1to2-add-source-1 &&
@@ -748,7 +748,7 @@ test_expect_failure 'detect conflict with rename/rename(1to2)/add-source merge'
'
test_setup_rename_rename_1to2_add_source_2 () {
- test_create_repo rename-rename-1to2-add-source-2 &&
+ git init rename-rename-1to2-add-source-2 &&
(
cd rename-rename-1to2-add-source-2 &&
@@ -794,7 +794,7 @@ test_expect_failure 'rename/rename/add-source still tracks new a file' '
'
test_setup_rename_rename_1to2_add_dest () {
- test_create_repo rename-rename-1to2-add-dest &&
+ git init rename-rename-1to2-add-dest &&
(
cd rename-rename-1to2-add-dest &&
@@ -874,7 +874,7 @@ test_expect_success 'rename/rename/add-dest merge still knows about conflicting
# Expected: CONFLICT (rename/add/delete), two-way merged bar
test_setup_rad () {
- test_create_repo rad &&
+ git init rad &&
(
cd rad &&
echo "original file" >foo &&
@@ -946,7 +946,7 @@ test_expect_merge_algorithm failure success 'rad-check: rename/add/delete confli
# Expected: CONFLICT (rename/rename/delete/delete), two-way merged baz
test_setup_rrdd () {
- test_create_repo rrdd &&
+ git init rrdd &&
(
cd rrdd &&
echo foo >foo &&
@@ -1022,7 +1022,7 @@ test_expect_merge_algorithm failure success 'rrdd-check: rename/rename(2to1)/del
# multi-way merged contents found in two, four, six
test_setup_mod6 () {
- test_create_repo mod6 &&
+ git init mod6 &&
(
cd mod6 &&
test_seq 11 19 >one &&
@@ -1160,7 +1160,7 @@ test_conflicts_with_adds_and_renames() {
# tree
test_setup_collision_conflict () {
#test_expect_success "setup simple $sideL/$sideR conflict" '
- test_create_repo simple_${sideL}_${sideR} &&
+ git init simple_${sideL}_${sideR} &&
(
cd simple_${sideL}_${sideR} &&
@@ -1308,7 +1308,7 @@ test_conflicts_with_adds_and_renames add add
# So, we have four different conflicting files that all end up at path
# 'three'.
test_setup_nested_conflicts_from_rename_rename () {
- test_create_repo nested_conflicts_from_rename_rename &&
+ git init nested_conflicts_from_rename_rename &&
(
cd nested_conflicts_from_rename_rename &&
@@ -1417,7 +1417,7 @@ test_expect_success 'check nested conflicts from rename/rename(2to1)' '
# Expected: CONFLICT(rename/rename) message, three unstaged entries in the
# index, and contents of orig-[AB] at path orig-[AB]
test_setup_rename_rename_1_to_2_binary () {
- test_create_repo rename_rename_1_to_2_binary &&
+ git init rename_rename_1_to_2_binary &&
(
cd rename_rename_1_to_2_binary &&
diff --git a/t/t6423-merge-rename-directories.sh b/t/t6423-merge-rename-directories.sh
index 99baf77cbf..944de75b80 100755
--- a/t/t6423-merge-rename-directories.sh
+++ b/t/t6423-merge-rename-directories.sh
@@ -40,7 +40,7 @@ test_description="recursive merge with directory renames"
# Expected: y/{b,c,d,e/f}
test_setup_1a () {
- test_create_repo 1a &&
+ git init 1a &&
(
cd 1a &&
@@ -106,7 +106,7 @@ test_expect_success '1a: Simple directory rename detection' '
# Expected: y/{b,c,d,e}
test_setup_1b () {
- test_create_repo 1b &&
+ git init 1b &&
(
cd 1b &&
@@ -169,7 +169,7 @@ test_expect_success '1b: Merge a directory with another' '
# Expected: y/{b,c,d} (because x/d -> z/d -> y/d)
test_setup_1c () {
- test_create_repo 1c &&
+ git init 1c &&
(
cd 1c &&
@@ -232,7 +232,7 @@ test_expect_success '1c: Transitive renaming' '
# y/wham_1 & z/wham_2 should too...giving us a conflict.
test_setup_1d () {
- test_create_repo 1d &&
+ git init 1d &&
(
cd 1d &&
@@ -328,7 +328,7 @@ test_expect_success '1d: Directory renames cause a rename/rename(2to1) conflict'
# Expected: y/{newb,newc,d}
test_setup_1e () {
- test_create_repo 1e &&
+ git init 1e &&
(
cd 1e &&
@@ -387,7 +387,7 @@ test_expect_success '1e: Renamed directory, with all files being renamed too' '
# Expected: y/{b,c}, x/{d,e,f,g}
test_setup_1f () {
- test_create_repo 1f &&
+ git init 1f &&
(
cd 1f &&
@@ -476,7 +476,7 @@ test_expect_success '1f: Split a directory into two other directories' '
# Commit B: z/{b,c,d}
# Expected: y/b, w/c, z/d, with warning about z/ -> (y/ vs. w/) conflict
test_setup_2a () {
- test_create_repo 2a &&
+ git init 2a &&
(
cd 2a &&
@@ -538,7 +538,7 @@ test_expect_success '2a: Directory split into two on one side, with equal number
# Commit B: z/{b,c}, x/d
# Expected: y/b, w/c, x/d; No warning about z/ -> (y/ vs. w/) conflict
test_setup_2b () {
- test_create_repo 2b &&
+ git init 2b &&
(
cd 2b &&
@@ -620,7 +620,7 @@ test_expect_success '2b: Directory split into two on one side, with equal number
# Commit B: y/{b,c}, x/d
# Expected: y/{b,c}, x/d
test_setup_3a () {
- test_create_repo 3a &&
+ git init 3a &&
(
cd 3a &&
@@ -684,7 +684,7 @@ test_expect_success '3a: Avoid implicit rename if involved as source on other si
# end up with CONFLICT:(z/d -> y/d vs. x/d vs. w/d), i.e. a
# rename/rename/rename(1to3) conflict, which is just weird.
test_setup_3b () {
- test_create_repo 3b &&
+ git init 3b &&
(
cd 3b &&
@@ -807,7 +807,7 @@ test_expect_success '3b: Avoid implicit rename if involved as source on current
# NOTE: Even though most files from z moved to y, we don't want f to follow.
test_setup_4a () {
- test_create_repo 4a &&
+ git init 4a &&
(
cd 4a &&
@@ -896,7 +896,7 @@ test_expect_success '4a: Directory split, with original directory still present'
# index.
test_setup_5a () {
- test_create_repo 5a &&
+ git init 5a &&
(
cd 5a &&
@@ -971,7 +971,7 @@ test_expect_success '5a: Merge directories, other side adds files to original an
# back to git behavior without the directory rename detection.
test_setup_5b () {
- test_create_repo 5b &&
+ git init 5b &&
(
cd 5b &&
@@ -1048,7 +1048,7 @@ test_expect_success '5b: Rename/delete in order to get add/add/add conflict' '
# though, because it doesn't have anything in the way.
test_setup_5c () {
- test_create_repo 5c &&
+ git init 5c &&
(
cd 5c &&
@@ -1138,7 +1138,7 @@ test_expect_success '5c: Transitive rename would cause rename/rename/rename/add/
# directory rename detection for z/f -> y/f.
test_setup_5d () {
- test_create_repo 5d &&
+ git init 5d &&
(
cd 5d &&
@@ -1239,7 +1239,7 @@ test_expect_success '5d: Directory/file/file conflict due to directory rename' '
# it is also involved in a rename/delete conflict.
test_setup_6a () {
- test_create_repo 6a &&
+ git init 6a &&
(
cd 6a &&
@@ -1337,7 +1337,7 @@ test_expect_success '6a: Tricky rename/delete' '
# the behavior on testcases 6b2 and 8e, and introduced this 6b1 testcase.
test_setup_6b1 () {
- test_create_repo 6b1 &&
+ git init 6b1 &&
(
cd 6b1 &&
@@ -1415,7 +1415,7 @@ test_expect_merge_algorithm failure success '6b1: Same renames done on both side
# the z/ -> y/ rename.
test_setup_6b2 () {
- test_create_repo 6b2 &&
+ git init 6b2 &&
(
cd 6b2 &&
@@ -1479,7 +1479,7 @@ test_expect_merge_algorithm failure success '6b2: Same rename done on both sides
# "accidentally detect a rename" and give us y/{b,c,d}.
test_setup_6c () {
- test_create_repo 6c &&
+ git init 6c &&
(
cd 6c &&
@@ -1542,7 +1542,7 @@ test_expect_success '6c: Rename only done on same side' '
# doesn't "accidentally detect a rename" and give us y/{b,c,d}.
test_setup_6d () {
- test_create_repo 6d &&
+ git init 6d &&
(
cd 6d &&
@@ -1605,7 +1605,7 @@ test_expect_success '6d: We do not always want transitive renaming' '
# add/add conflict on y/d_1 vs y/d_2.
test_setup_6e () {
- test_create_repo 6e &&
+ git init 6e &&
(
cd 6e &&
@@ -1700,7 +1700,7 @@ test_expect_success '6e: Add/add from one side' '
# NOTE: There's a rename of z/ here, y/ has more renames, so z/d -> y/d.
test_setup_7a () {
- test_create_repo 7a &&
+ git init 7a &&
(
cd 7a &&
@@ -1772,7 +1772,7 @@ test_expect_success '7a: rename-dir vs. rename-dir (NOT split evenly) PLUS add-o
# Expected: y/{b,c}, CONFLICT(rename/rename(2to1): x/d_1, w/d_2 -> y_d)
test_setup_7b () {
- test_create_repo 7b &&
+ git init 7b &&
(
cd 7b &&
@@ -1861,7 +1861,7 @@ test_expect_success '7b: rename/rename(2to1), but only due to transitive rename'
# nor CONFLiCT x/d -> w/d vs. y/d vs. z/d)
test_setup_7c () {
- test_create_repo 7c &&
+ git init 7c &&
(
cd 7c &&
@@ -1926,7 +1926,7 @@ test_expect_success '7c: rename/rename(1to...2or3); transitive rename may add co
# NOTE: z->y so NOT CONFLICT(delete x/d vs rename to z/d)
test_setup_7d () {
- test_create_repo 7d &&
+ git init 7d &&
(
cd 7d &&
@@ -2027,7 +2027,7 @@ test_expect_success '7d: transitive rename involved in rename/delete; how is it
# how it's resolved.
test_setup_7e () {
- test_create_repo 7e &&
+ git init 7e &&
(
cd 7e &&
@@ -2137,7 +2137,7 @@ test_expect_success '7e: transitive rename in rename/delete AND dirs in the way'
# we potentially could.
test_setup_8a () {
- test_create_repo 8a &&
+ git init 8a &&
(
cd 8a &&
@@ -2216,7 +2216,7 @@ test_expect_success '8a: Dual-directory rename, one into the others way' '
# e_1 and e_2.
test_setup_8b () {
- test_create_repo 8b &&
+ git init 8b &&
(
cd 8b &&
@@ -2290,7 +2290,7 @@ test_expect_success '8b: Dual-directory rename, one into the others way, with co
# notes in 8d.
test_setup_8c () {
- test_create_repo 8c &&
+ git init 8c &&
(
cd 8c &&
@@ -2370,7 +2370,7 @@ test_expect_success '8c: modify/delete or rename+modify/delete' '
# differently.
test_setup_8d () {
- test_create_repo 8d &&
+ git init 8d &&
(
cd 8d &&
@@ -2453,7 +2453,7 @@ test_expect_success '8d: rename/delete...or not?' '
# the behavior, and predict it without computing as many details.
test_setup_8e () {
- test_create_repo 8e &&
+ git init 8e &&
(
cd 8e &&
@@ -2537,7 +2537,7 @@ test_expect_success '8e: Both sides rename, one side adds to original directory'
# of that could take the new file in commit B at z/i to x/w/i or x/i.
test_setup_9a () {
- test_create_repo 9a &&
+ git init 9a &&
(
cd 9a &&
@@ -2609,7 +2609,7 @@ test_expect_success '9a: Inner renamed directory within outer renamed directory'
# Expected: y/{b,c,d_merged}
test_setup_9b () {
- test_create_repo 9b &&
+ git init 9b &&
(
cd 9b &&
@@ -2697,7 +2697,7 @@ test_expect_success '9b: Transitive rename with content merge' '
# history for any implicit directory renames.
test_setup_9c () {
- test_create_repo 9c &&
+ git init 9c &&
(
cd 9c &&
@@ -2786,7 +2786,7 @@ test_expect_success '9c: Doubly transitive rename?' '
# testcases and simplifies things for the user.
test_setup_9d () {
- test_create_repo 9d &&
+ git init 9d &&
(
cd 9d &&
@@ -2861,7 +2861,7 @@ test_expect_success '9d: N-way transitive rename?' '
# dir1/yo, dir2/yo, dir3/yo, dirN/yo
test_setup_9e () {
- test_create_repo 9e &&
+ git init 9e &&
(
cd 9e &&
@@ -2954,7 +2954,7 @@ test_expect_success '9e: N-to-1 whammo' '
# Expected: priority/{a,b}/$more_files, priority/c
test_setup_9f () {
- test_create_repo 9f &&
+ git init 9f &&
(
cd 9f &&
@@ -3027,7 +3027,7 @@ test_expect_success '9f: Renamed directory that only contained immediate subdirs
# viewpoint...
test_setup_9g () {
- test_create_repo 9g &&
+ git init 9g &&
(
cd 9g &&
@@ -3096,7 +3096,7 @@ test_expect_failure '9g: Renamed directory that only contained immediate subdirs
# NOTE: If we applied the z/ -> y/ rename to z/d, then we'd end up with
# a rename/rename(1to2) conflict (z/d -> y/d vs. x/d)
test_setup_9h () {
- test_create_repo 9h &&
+ git init 9h &&
(
cd 9h &&
@@ -3177,7 +3177,7 @@ test_expect_success '9h: Avoid dir rename on merely modified path' '
# ERROR_MSG(untracked working tree files would be overwritten by merge)
test_setup_10a () {
- test_create_repo 10a &&
+ git init 10a &&
(
cd 10a &&
@@ -3243,7 +3243,7 @@ test_expect_success '10a: Overwrite untracked with normal rename/delete' '
# ERROR_MSG(refusing to lose untracked file at 'y/d')
test_setup_10b () {
- test_create_repo 10b &&
+ git init 10b &&
(
cd 10b &&
@@ -3334,7 +3334,7 @@ test_expect_success '10b: Overwrite untracked with dir rename + delete' '
# ERROR_MSG(Refusing to lose untracked file at y/c)
test_setup_10c () {
- test_create_repo 10c_$1 &&
+ git init 10c_$1 &&
(
cd 10c_$1 &&
@@ -3472,7 +3472,7 @@ test_expect_success '10c2: Overwrite untracked with dir rename/rename(1to2), oth
# ERROR_MSG(Refusing to lose untracked file at y/wham)
test_setup_10d () {
- test_create_repo 10d &&
+ git init 10d &&
(
cd 10d &&
@@ -3568,7 +3568,7 @@ test_expect_success '10d: Delete untracked with dir rename/rename(2to1)' '
# Expected: y/{a,b,c} + untracked z/c
test_setup_10e () {
- test_create_repo 10e &&
+ git init 10e &&
(
cd 10e &&
@@ -3650,7 +3650,7 @@ test_expect_merge_algorithm failure success '10e: Does git complain about untrac
# z/c with uncommitted mods on top of A:z/c_v1
test_setup_11a () {
- test_create_repo 11a &&
+ git init 11a &&
(
cd 11a &&
@@ -3728,7 +3728,7 @@ test_expect_success '11a: Avoid losing dirty contents with simple rename' '
test_setup_11b () {
- test_create_repo 11b &&
+ git init 11b &&
(
cd 11b &&
@@ -3810,7 +3810,7 @@ test_expect_success '11b: Avoid losing dirty file involved in directory rename'
# y/c left untouched (still has uncommitted mods)
test_setup_11c () {
- test_create_repo 11c &&
+ git init 11c &&
(
cd 11c &&
@@ -3883,7 +3883,7 @@ test_expect_success '11c: Avoid losing not-uptodate with rename + D/F conflict'
# y/{a,c~HEAD,c/d}, x/b, now-untracked z/c_v1 with uncommitted mods
test_setup_11d () {
- test_create_repo 11d &&
+ git init 11d &&
(
cd 11d &&
@@ -3968,7 +3968,7 @@ test_expect_success '11d: Avoid losing not-uptodate with rename + D/F conflict'
# y/c has dirty file from before merge
test_setup_11e () {
- test_create_repo 11e &&
+ git init 11e &&
(
cd 11e &&
@@ -4060,7 +4060,7 @@ test_expect_success '11e: Avoid deleting not-uptodate with dir rename/rename(1to
# ERROR_MSG(Refusing to lose dirty file at y/wham)
test_setup_11f () {
- test_create_repo 11f &&
+ git init 11f &&
(
cd 11f &&
@@ -4155,7 +4155,7 @@ test_expect_success '11f: Avoid deleting not-uptodate with dir rename/rename(2to
# Expected: node1/{leaf1,leaf2,leaf5,node2/{leaf3,leaf4,leaf6}}
test_setup_12a () {
- test_create_repo 12a &&
+ git init 12a &&
(
cd 12a &&
@@ -4238,7 +4238,7 @@ test_expect_success '12a: Moving one directory hierarchy into another' '
# node2/node1/{leaf1, leaf2}
test_setup_12b1 () {
- test_create_repo 12b1 &&
+ git init 12b1 &&
(
cd 12b1 &&
@@ -4327,7 +4327,7 @@ test_expect_merge_algorithm failure success '12b1: Moving two directory hierarch
# even simple rules give weird results when given weird inputs.
test_setup_12b2 () {
- test_create_repo 12b2 &&
+ git init 12b2 &&
(
cd 12b2 &&
@@ -4402,7 +4402,7 @@ test_expect_success '12b2: Moving two directory hierarchies into each other' '
# each side of the merge.
test_setup_12c1 () {
- test_create_repo 12c1 &&
+ git init 12c1 &&
(
cd 12c1 &&
@@ -4492,7 +4492,7 @@ test_expect_merge_algorithm failure success '12c1: Moving one directory hierarch
# on each side of the merge.
test_setup_12c2 () {
- test_create_repo 12c2 &&
+ git init 12c2 &&
(
cd 12c2 &&
@@ -4584,7 +4584,7 @@ test_expect_success '12c2: Moving one directory hierarchy into another w/ conten
# Expected: subdir/foo, bar
test_setup_12d () {
- test_create_repo 12d &&
+ git init 12d &&
(
cd 12d &&
@@ -4642,7 +4642,7 @@ test_expect_success '12d: Rename/merge subdir into the root, variant 1' '
# Expected: foo, bar
test_setup_12e () {
- test_create_repo 12e &&
+ git init 12e &&
(
cd 12e &&
@@ -4743,7 +4743,7 @@ test_expect_success '12e: Rename/merge subdir into the root, variant 2' '
# pick and re-applying them in the subsequent one.
test_setup_12f () {
- test_create_repo 12f &&
+ git init 12f &&
(
cd 12f &&
@@ -4902,7 +4902,7 @@ test_expect_merge_algorithm failure success '12f: Trivial directory resolve, cac
# Expected: newfile_{merged}, newdir/{a_B,b_B,c_A}
test_setup_12g () {
- test_create_repo 12g &&
+ git init 12g &&
(
cd 12g &&
@@ -4973,7 +4973,7 @@ test_expect_success '12g: Testcase with two kinds of "relevant" renames' '
# Expected: newdir/{alpha_2, b}
test_setup_12h () {
- test_create_repo 12h &&
+ git init 12h &&
(
cd 12h &&
@@ -5032,7 +5032,7 @@ test_expect_failure '12h: renaming a file within a renamed directory' '
# source/bar vs. source/subdir/bar
test_setup_12i () {
- test_create_repo 12i &&
+ git init 12i &&
(
cd 12i &&
@@ -5090,7 +5090,7 @@ test_expect_success '12i: Directory rename causes rename-to-self' '
# Expected: {foo, bar, baz_2}, with conflicts on bar vs. subdir/bar
test_setup_12j () {
- test_create_repo 12j &&
+ git init 12j &&
(
cd 12j &&
@@ -5148,7 +5148,7 @@ test_expect_success '12j: Directory rename to root causes rename-to-self' '
# Expected: dirA/{foo, bar, baz_2}, with conflicts on dirA/bar vs. dirB/bar
test_setup_12k () {
- test_create_repo 12k &&
+ git init 12k &&
(
cd 12k &&
@@ -5218,7 +5218,7 @@ test_expect_success '12k: Directory rename with sibling causes rename-to-self' '
# is needed for there to be a sub1/ -> sub3/ rename.
test_setup_12l () {
- test_create_repo 12l_$1 &&
+ git init 12l_$1 &&
(
cd 12l_$1 &&
@@ -5304,6 +5304,62 @@ test_expect_merge_algorithm failure success '12l (A into B): Rename into each ot
)
'
+# Testcase 12m, Directory rename, plus change of parent dir to symlink
+# Commit O: dir/subdir/file
+# Commit A: renamed-dir/subdir/file
+# Commit B: dir/subdir
+# In words:
+# A: dir/subdir/ -> renamed-dir/subdir
+# B: delete dir/subdir/file, add dir/subdir as symlink
+#
+# Expected: CONFLICT (rename/delete): renamed-dir/subdir/file,
+# CONFLICT (file location): renamed-dir/subdir vs. dir/subdir
+# CONFLICT (directory/file): renamed-dir/subdir symlink has
+# renamed-dir/subdir in the way
+
+test_setup_12m () {
+ git init 12m &&
+ (
+ cd 12m &&
+
+ mkdir -p dir/subdir &&
+ echo 1 >dir/subdir/file &&
+ git add . &&
+ git commit -m "O" &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git switch A &&
+ git mv dir/ renamed-dir/ &&
+ git add . &&
+ git commit -m "A" &&
+
+ git switch B &&
+ git rm dir/subdir/file &&
+ mkdir dir &&
+ ln -s /dev/null dir/subdir &&
+ git add . &&
+ git commit -m "B"
+ )
+}
+
+test_expect_merge_algorithm failure success '12m: Change parent of renamed-dir to symlink on other side' '
+ test_setup_12m &&
+ (
+ cd 12m &&
+
+ git checkout -q A^0 &&
+
+ test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 &&
+
+ test_stdout_line_count = 3 git ls-files -s &&
+ test_stdout_line_count = 2 ls -1 renamed-dir &&
+ test_path_is_missing dir
+ )
+'
+
###########################################################################
# SECTION 13: Checking informational and conflict messages
#
@@ -5322,7 +5378,7 @@ test_expect_merge_algorithm failure success '12l (A into B): Rename into each ot
# Expected: y/{b,c,d,e/f}, with notices/conflicts for both y/d and y/e/f
test_setup_13a () {
- test_create_repo 13a_$1 &&
+ git init 13a_$1 &&
(
cd 13a_$1 &&
@@ -5409,7 +5465,7 @@ test_expect_success '13a(info): messages for newly added files' '
# one about content, and one about file location
test_setup_13b () {
- test_create_repo 13b_$1 &&
+ git init 13b_$1 &&
(
cd 13b_$1 &&
@@ -5496,7 +5552,7 @@ test_expect_success '13b(info): messages for transitive rename with conflicted c
# shown in testcase 13d.
test_setup_13c () {
- test_create_repo 13c_$1 &&
+ git init 13c_$1 &&
(
cd 13c_$1 &&
@@ -5584,7 +5640,7 @@ test_expect_success '13c(info): messages for rename/rename(1to1) via transitive
# No conflict in where a/y ends up, so put it in d/y.
test_setup_13d () {
- test_create_repo 13d_$1 &&
+ git init 13d_$1 &&
(
cd 13d_$1 &&
@@ -5710,7 +5766,7 @@ test_expect_success '13d(info): messages for rename/rename(1to1) via dual transi
# least avoids hitting a BUG().
#
test_setup_13e () {
- test_create_repo 13e &&
+ git init 13e &&
(
cd 13e &&
diff --git a/t/t6425-merge-rename-delete.sh b/t/t6425-merge-rename-delete.sh
index 459b431a60..93cd2869b1 100755
--- a/t/t6425-merge-rename-delete.sh
+++ b/t/t6425-merge-rename-delete.sh
@@ -4,6 +4,7 @@ test_description='Merge-recursive rename/delete conflict message'
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 'rename/delete' '
diff --git a/t/t6426-merge-skip-unneeded-updates.sh b/t/t6426-merge-skip-unneeded-updates.sh
index 7b5f1c1dcd..2bb8e7f09b 100755
--- a/t/t6426-merge-skip-unneeded-updates.sh
+++ b/t/t6426-merge-skip-unneeded-updates.sh
@@ -38,7 +38,7 @@ test_description="merge cases"
# Expected: b_2
test_setup_1a () {
- test_create_repo 1a_$1 &&
+ git init 1a_$1 &&
(
cd 1a_$1 &&
@@ -136,7 +136,7 @@ test_expect_success '1a-R: Modify(A)/Modify(B), change on B subset of A' '
# Expected: c_2
test_setup_2a () {
- test_create_repo 2a_$1 &&
+ git init 2a_$1 &&
(
cd 2a_$1 &&
@@ -229,7 +229,7 @@ test_expect_success '2a-R: Modify/rename, merge into rename side' '
# Expected: c_2
test_setup_2b () {
- test_create_repo 2b_$1 &&
+ git init 2b_$1 &&
(
cd 2b_$1 &&
@@ -336,7 +336,7 @@ test_expect_success '2b-R: Rename+Mod(A)/Mod(B), B mods subset of A' '
# not make that particular mistake.
test_setup_2c () {
- test_create_repo 2c &&
+ git init 2c &&
(
cd 2c &&
@@ -437,7 +437,7 @@ test_expect_success '2c: Modify b & add c VS rename b->c' '
# Expected: bar/{bq_2, whatever}
test_setup_3a () {
- test_create_repo 3a_$1 &&
+ git init 3a_$1 &&
(
cd 3a_$1 &&
@@ -537,7 +537,7 @@ test_expect_success '3a-R: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
# Expected: bar/{bq_2, whatever}
test_setup_3b () {
- test_create_repo 3b_$1 &&
+ git init 3b_$1 &&
(
cd 3b_$1 &&
@@ -642,7 +642,7 @@ test_expect_success '3b-R: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
# Expected: b_2 for merge, b_4 in working copy
test_setup_4a () {
- test_create_repo 4a &&
+ git init 4a &&
(
cd 4a &&
@@ -714,7 +714,7 @@ test_expect_merge_algorithm failure success '4a: Change on A, change on B subset
# Expected: c_2
test_setup_4b () {
- test_create_repo 4b &&
+ git init 4b &&
(
cd 4b &&
diff --git a/t/t6427-diff3-conflict-markers.sh b/t/t6427-diff3-conflict-markers.sh
index a9ee4cb207..dd5fe6a402 100755
--- a/t/t6427-diff3-conflict-markers.sh
+++ b/t/t6427-diff3-conflict-markers.sh
@@ -19,7 +19,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
#
test_expect_success 'setup no merge base' '
- test_create_repo no_merge_base &&
+ git init no_merge_base &&
(
cd no_merge_base &&
@@ -55,7 +55,7 @@ test_expect_success 'check no merge base' '
#
test_expect_success 'setup unique merge base' '
- test_create_repo unique_merge_base &&
+ git init unique_merge_base &&
(
cd unique_merge_base &&
@@ -116,7 +116,7 @@ test_expect_success 'check unique merge base' '
#
test_expect_success 'setup multiple merge bases' '
- test_create_repo multiple_merge_bases &&
+ git init multiple_merge_bases &&
(
cd multiple_merge_bases &&
@@ -190,7 +190,7 @@ test_expect_success 'check multiple merge bases' '
'
test_expect_success 'rebase --merge describes parent of commit being picked' '
- test_create_repo rebase &&
+ git init rebase &&
(
cd rebase &&
test_commit base file &&
@@ -212,7 +212,7 @@ test_expect_success 'rebase --apply describes fake ancestor base' '
'
test_setup_zdiff3 () {
- test_create_repo zdiff3 &&
+ git init zdiff3 &&
(
cd zdiff3 &&
diff --git a/t/t6428-merge-conflicts-sparse.sh b/t/t6428-merge-conflicts-sparse.sh
index 064be1b629..9919c3fa7c 100755
--- a/t/t6428-merge-conflicts-sparse.sh
+++ b/t/t6428-merge-conflicts-sparse.sh
@@ -29,7 +29,7 @@ test_description="merge cases"
# Testcase basic, conflicting changes in 'numerals'
test_setup_numerals () {
- test_create_repo numerals_$1 &&
+ git init numerals_$1 &&
(
cd numerals_$1 &&
diff --git a/t/t6429-merge-sequence-rename-caching.sh b/t/t6429-merge-sequence-rename-caching.sh
index e1ce919916..d02fa16614 100755
--- a/t/t6429-merge-sequence-rename-caching.sh
+++ b/t/t6429-merge-sequence-rename-caching.sh
@@ -35,7 +35,7 @@ test_description="remember regular & dir renames in sequence of merges"
# preventing us from finding new renames.
#
test_expect_success 'caching renames does not preclude finding new ones' '
- test_create_repo caching-renames-and-new-renames &&
+ git init caching-renames-and-new-renames &&
(
cd caching-renames-and-new-renames &&
@@ -106,7 +106,7 @@ test_expect_success 'caching renames does not preclude finding new ones' '
# should be able to only run rename detection on the upstream side one
# time.)
test_expect_success 'cherry-pick both a commit and its immediate revert' '
- test_create_repo pick-commit-and-its-immediate-revert &&
+ git init pick-commit-and-its-immediate-revert &&
(
cd pick-commit-and-its-immediate-revert &&
@@ -162,7 +162,7 @@ test_expect_success 'cherry-pick both a commit and its immediate revert' '
# could cause a spurious rename/add conflict.
#
test_expect_success 'rename same file identically, then reintroduce it' '
- test_create_repo rename-rename-1to1-then-add-old-filename &&
+ git init rename-rename-1to1-then-add-old-filename &&
(
cd rename-rename-1to1-then-add-old-filename &&
@@ -229,7 +229,7 @@ test_expect_success 'rename same file identically, then reintroduce it' '
# cached, the directory rename could put newfile in the wrong directory.
#
test_expect_success 'rename same file identically, then add file to old dir' '
- test_create_repo rename-rename-1to1-then-add-file-to-old-dir &&
+ git init rename-rename-1to1-then-add-file-to-old-dir &&
(
cd rename-rename-1to1-then-add-file-to-old-dir &&
@@ -311,7 +311,7 @@ test_expect_success 'rename same file identically, then add file to old dir' '
# should avoid the need to re-detect upstream renames.)
#
test_expect_success 'cached dir rename does not prevent noticing later conflict' '
- test_create_repo dir-rename-cache-not-occluding-later-conflict &&
+ git init dir-rename-cache-not-occluding-later-conflict &&
(
cd dir-rename-cache-not-occluding-later-conflict &&
@@ -365,7 +365,7 @@ test_expect_success 'cached dir rename does not prevent noticing later conflict'
# Helper for the next two tests
test_setup_upstream_rename () {
- test_create_repo $1 &&
+ git init $1 &&
(
cd $1 &&
@@ -537,7 +537,7 @@ test_expect_success 'dir rename unneeded, then rename existing file into old dir
# Helper for the next two tests
test_setup_topic_rename () {
- test_create_repo $1 &&
+ git init $1 &&
(
cd $1 &&
@@ -725,7 +725,7 @@ test_expect_success 'avoid assuming we detected renames' '
mkdir unrelated &&
for i in $(test_seq 1 10)
do
- >unrelated/$i
+ >unrelated/$i || exit 1
done &&
test_seq 2 10 >numbers &&
test_seq 12 20 >values &&
diff --git a/t/t6431-merge-criscross.sh b/t/t6431-merge-criscross.sh
index 3824756a02..3fe14cd73e 100755
--- a/t/t6431-merge-criscross.sh
+++ b/t/t6431-merge-criscross.sh
@@ -2,6 +2,7 @@
test_description='merge-recursive backend test'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# A <- create some files
diff --git a/t/t6437-submodule-merge.sh b/t/t6437-submodule-merge.sh
index c253bf759a..c9a86f2e94 100755
--- a/t/t6437-submodule-merge.sh
+++ b/t/t6437-submodule-merge.sh
@@ -103,8 +103,25 @@ test_expect_success 'setup for merge search' '
echo "file-c" > file-c &&
git add file-c &&
git commit -m "sub-c") &&
- git commit -a -m "c" &&
+ git commit -a -m "c")
+'
+test_expect_success 'merging should conflict for non fast-forward' '
+ test_when_finished "git -C merge-search reset --hard" &&
+ (cd merge-search &&
+ git checkout -b test-nonforward-a b &&
+ if test "$GIT_TEST_MERGE_ALGORITHM" = ort
+ then
+ test_must_fail git merge c >actual &&
+ sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-c)" &&
+ grep "$sub_expect" actual
+ else
+ test_must_fail git merge c 2> actual
+ fi)
+'
+
+test_expect_success 'finish setup for merge-search' '
+ (cd merge-search &&
git checkout -b d a &&
(cd sub &&
git checkout -b sub-d sub-b &&
@@ -129,14 +146,16 @@ test_expect_success 'merge with one side as a fast-forward of the other' '
test_cmp expect actual)
'
-test_expect_success 'merging should conflict for non fast-forward' '
+test_expect_success 'merging should conflict for non fast-forward (resolution exists)' '
(cd merge-search &&
- git checkout -b test-nonforward b &&
+ git checkout -b test-nonforward-b b &&
(cd sub &&
git rev-parse --short sub-d > ../expect) &&
if test "$GIT_TEST_MERGE_ALGORITHM" = ort
then
- test_must_fail git merge c >actual
+ test_must_fail git merge c >actual &&
+ sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-c)" &&
+ grep "$sub_expect" actual
else
test_must_fail git merge c 2> actual
fi &&
@@ -161,7 +180,9 @@ test_expect_success 'merging should fail for ambiguous common parent' '
) &&
if test "$GIT_TEST_MERGE_ALGORITHM" = ort
then
- test_must_fail git merge c >actual
+ test_must_fail git merge c >actual &&
+ sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-c)" &&
+ grep "$sub_expect" actual
else
test_must_fail git merge c 2> actual
fi &&
@@ -205,7 +226,12 @@ test_expect_success 'merging should fail for changes that are backwards' '
git commit -a -m "f" &&
git checkout -b test-backward e &&
- test_must_fail git merge f)
+ test_must_fail git merge f >actual &&
+ if test "$GIT_TEST_MERGE_ALGORITHM" = ort
+ then
+ sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-d)" &&
+ grep "$sub_expect" actual
+ fi)
'
@@ -310,7 +336,7 @@ test_expect_success 'recursive merge with submodule' '
# Expected: path/ is submodule and file contents for B's path are somewhere
test_expect_success 'setup file/submodule conflict' '
- test_create_repo file-submodule &&
+ git init file-submodule &&
(
cd file-submodule &&
@@ -325,7 +351,7 @@ test_expect_success 'setup file/submodule conflict' '
git commit -m B &&
git checkout A &&
- test_create_repo path &&
+ git init path &&
test_commit -C path world &&
git submodule add ./path &&
git commit -m A
@@ -385,7 +411,7 @@ test_expect_success 'file/submodule conflict; merge --abort works afterward' '
# under the submodule to be treated as untracked or in the way.
test_expect_success 'setup directory/submodule conflict' '
- test_create_repo directory-submodule &&
+ git init directory-submodule &&
(
cd directory-submodule &&
@@ -408,7 +434,7 @@ test_expect_success 'setup directory/submodule conflict' '
git commit -m B2 &&
git checkout A &&
- test_create_repo path &&
+ git init path &&
test_commit -C path hello world &&
git submodule add ./path &&
git commit -m A
@@ -476,4 +502,44 @@ test_expect_failure 'directory/submodule conflict; merge --abort works afterward
)
'
+# Setup:
+# - Submodule has 2 commits: a and b
+# - Superproject branch 'a' adds and commits submodule pointing to 'commit a'
+# - Superproject branch 'b' adds and commits submodule pointing to 'commit b'
+# If these two branches are now merged, there is no merge base
+test_expect_success 'setup for null merge base' '
+ mkdir no-merge-base &&
+ (cd no-merge-base &&
+ git init &&
+ mkdir sub &&
+ (cd sub &&
+ git init &&
+ echo "file-a" > file-a &&
+ git add file-a &&
+ git commit -m "commit a") &&
+ git commit --allow-empty -m init &&
+ git branch init &&
+ git checkout -b a init &&
+ git add sub &&
+ git commit -m "a" &&
+ git switch main &&
+ (cd sub &&
+ echo "file-b" > file-b &&
+ git add file-b &&
+ git commit -m "commit b"))
+'
+
+test_expect_success 'merging should fail with no merge base' '
+ (cd no-merge-base &&
+ git checkout -b b init &&
+ git add sub &&
+ git commit -m "b" &&
+ test_must_fail git merge a >actual &&
+ if test "$GIT_TEST_MERGE_ALGORITHM" = ort
+ then
+ sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short HEAD^1)" &&
+ grep "$sub_expect" actual
+ fi)
+'
+
test_done
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index a402908142..8c37bceb33 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -324,6 +324,7 @@ test_expect_success SYMLINKS 'check moved symlink' '
rm -f moved symlink
test_expect_success 'setup submodule' '
+ test_config_global protocol.file.allow always &&
git commit -m initial &&
git reset --hard &&
git submodule add ./. sub &&
@@ -509,6 +510,7 @@ test_expect_success 'moving a submodule in nested directories' '
'
test_expect_success 'moving nested submodules' '
+ test_config_global protocol.file.allow always &&
git commit -am "cleanup commit" &&
mkdir sub_nested_nested &&
(
diff --git a/t/t7002-mv-sparse-checkout.sh b/t/t7002-mv-sparse-checkout.sh
index 71fe29690f..26582ae4e5 100755
--- a/t/t7002-mv-sparse-checkout.sh
+++ b/t/t7002-mv-sparse-checkout.sh
@@ -28,12 +28,25 @@ test_expect_success 'setup' "
updated in the index:
EOF
- cat >sparse_hint <<-EOF
+ cat >sparse_hint <<-EOF &&
hint: If you intend to update such entries, try one of the following:
hint: * Use the --sparse option.
hint: * Disable or modify the sparsity rules.
hint: Disable this message with \"git config advice.updateSparsePath false\"
EOF
+
+ cat >dirty_error_header <<-EOF &&
+ The following paths have been moved outside the
+ sparse-checkout definition but are not sparse due to local
+ modifications.
+ EOF
+
+ cat >dirty_hint <<-EOF
+ hint: To correct the sparsity of these paths, do the following:
+ hint: * Use \"git add --sparse <paths>\" to update the index
+ hint: * Use \"git sparse-checkout reapply\" to apply the sparsity rules
+ hint: Disable this message with \"git config advice.updateSparsePath false\"
+ EOF
"
test_expect_success 'mv refuses to move sparse-to-sparse' '
@@ -290,4 +303,215 @@ test_expect_success 'move sparse file to existing destination with --force and -
test_cmp expect sub/file1
'
+test_expect_success 'move clean path from in-cone to out-of-cone' '
+ test_when_finished "cleanup_sparse_checkout" &&
+ setup_sparse_checkout &&
+
+ test_must_fail git mv sub/d folder1 2>stderr &&
+ cat sparse_error_header >expect &&
+ echo "folder1/d" >>expect &&
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+
+ git mv --sparse sub/d folder1 2>stderr &&
+ test_must_be_empty stderr &&
+
+ test_path_is_missing sub/d &&
+ test_path_is_missing folder1/d &&
+ git ls-files -t >actual &&
+ ! grep "^H sub/d\$" actual &&
+ grep "S folder1/d" actual
+'
+
+test_expect_success 'move clean path from in-cone to out-of-cone overwrite' '
+ test_when_finished "cleanup_sparse_checkout" &&
+ setup_sparse_checkout &&
+ echo "sub/file1 overwrite" >sub/file1 &&
+ git add sub/file1 &&
+
+ test_must_fail git mv sub/file1 folder1 2>stderr &&
+ cat sparse_error_header >expect &&
+ echo "folder1/file1" >>expect &&
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+
+ test_must_fail git mv --sparse sub/file1 folder1 2>stderr &&
+ echo "fatal: destination exists in the index, source=sub/file1, destination=folder1/file1" \
+ >expect &&
+ test_cmp expect stderr &&
+
+ git mv --sparse -f sub/file1 folder1 2>stderr &&
+ test_must_be_empty stderr &&
+
+ test_path_is_missing sub/file1 &&
+ test_path_is_missing folder1/file1 &&
+ git ls-files -t >actual &&
+ ! grep "H sub/file1" actual &&
+ grep "S folder1/file1" actual &&
+
+ # compare file content before move and after move
+ echo "sub/file1 overwrite" >expect &&
+ git ls-files -s -- folder1/file1 | awk "{print \$2}" >oid &&
+ git cat-file blob $(cat oid) >actual &&
+ test_cmp expect actual
+'
+
+# This test is testing the same behavior as the
+# "move clean path from in-cone to out-of-cone overwrite" above.
+# The only difference is the <destination> changes from "folder1" to "folder1/file1"
+test_expect_success 'move clean path from in-cone to out-of-cone file overwrite' '
+ test_when_finished "cleanup_sparse_checkout" &&
+ setup_sparse_checkout &&
+ echo "sub/file1 overwrite" >sub/file1 &&
+ git add sub/file1 &&
+
+ test_must_fail git mv sub/file1 folder1/file1 2>stderr &&
+ cat sparse_error_header >expect &&
+ echo "folder1/file1" >>expect &&
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+
+ test_must_fail git mv --sparse sub/file1 folder1/file1 2>stderr &&
+ echo "fatal: destination exists in the index, source=sub/file1, destination=folder1/file1" \
+ >expect &&
+ test_cmp expect stderr &&
+
+ git mv --sparse -f sub/file1 folder1/file1 2>stderr &&
+ test_must_be_empty stderr &&
+
+ test_path_is_missing sub/file1 &&
+ test_path_is_missing folder1/file1 &&
+ git ls-files -t >actual &&
+ ! grep "H sub/file1" actual &&
+ grep "S folder1/file1" actual &&
+
+ # compare file content before move and after move
+ echo "sub/file1 overwrite" >expect &&
+ git ls-files -s -- folder1/file1 | awk "{print \$2}" >oid &&
+ git cat-file blob $(cat oid) >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'move directory with one of the files overwrite' '
+ test_when_finished "cleanup_sparse_checkout" &&
+ mkdir -p folder1/dir &&
+ touch folder1/dir/file1 &&
+ git add folder1 &&
+ git sparse-checkout set --cone sub &&
+
+ echo test >sub/dir/file1 &&
+ git add sub/dir/file1 &&
+
+ test_must_fail git mv sub/dir folder1 2>stderr &&
+ cat sparse_error_header >expect &&
+ echo "folder1/dir/e" >>expect &&
+ echo "folder1/dir/file1" >>expect &&
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+
+ test_must_fail git mv --sparse sub/dir folder1 2>stderr &&
+ echo "fatal: destination exists in the index, source=sub/dir/file1, destination=folder1/dir/file1" \
+ >expect &&
+ test_cmp expect stderr &&
+
+ git mv --sparse -f sub/dir folder1 2>stderr &&
+ test_must_be_empty stderr &&
+
+ test_path_is_missing sub/dir/file1 &&
+ test_path_is_missing sub/dir/e &&
+ test_path_is_missing folder1/file1 &&
+ git ls-files -t >actual &&
+ ! grep "H sub/dir/file1" actual &&
+ ! grep "H sub/dir/e" actual &&
+ grep "S folder1/dir/file1" actual &&
+
+ # compare file content before move and after move
+ echo test >expect &&
+ git ls-files -s -- folder1/dir/file1 | awk "{print \$2}" >oid &&
+ git cat-file blob $(cat oid) >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'move dirty path from in-cone to out-of-cone' '
+ test_when_finished "cleanup_sparse_checkout" &&
+ setup_sparse_checkout &&
+ echo "modified" >>sub/d &&
+
+ test_must_fail git mv sub/d folder1 2>stderr &&
+ cat sparse_error_header >expect &&
+ echo "folder1/d" >>expect &&
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+
+ git mv --sparse sub/d folder1 2>stderr &&
+ cat dirty_error_header >expect &&
+ echo "folder1/d" >>expect &&
+ cat dirty_hint >>expect &&
+ test_cmp expect stderr &&
+
+ test_path_is_missing sub/d &&
+ test_path_is_file folder1/d &&
+ git ls-files -t >actual &&
+ ! grep "^H sub/d\$" actual &&
+ grep "H folder1/d" actual
+'
+
+test_expect_success 'move dir from in-cone to out-of-cone' '
+ test_when_finished "cleanup_sparse_checkout" &&
+ setup_sparse_checkout &&
+ mkdir sub/dir/deep &&
+
+ test_must_fail git mv sub/dir folder1 2>stderr &&
+ cat sparse_error_header >expect &&
+ echo "folder1/dir/e" >>expect &&
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+
+ git mv --sparse sub/dir folder1 2>stderr &&
+ test_must_be_empty stderr &&
+
+ test_path_is_missing sub/dir &&
+ test_path_is_missing folder1 &&
+ git ls-files -t >actual &&
+ ! grep "H sub/dir/e" actual &&
+ grep "S folder1/dir/e" actual
+'
+
+test_expect_success 'move partially-dirty dir from in-cone to out-of-cone' '
+ test_when_finished "cleanup_sparse_checkout" &&
+ setup_sparse_checkout &&
+ mkdir sub/dir/deep &&
+ touch sub/dir/e2 sub/dir/e3 &&
+ git add sub/dir/e2 sub/dir/e3 &&
+ echo "modified" >>sub/dir/e2 &&
+ echo "modified" >>sub/dir/e3 &&
+
+ test_must_fail git mv sub/dir folder1 2>stderr &&
+ cat sparse_error_header >expect &&
+ echo "folder1/dir/e" >>expect &&
+ echo "folder1/dir/e2" >>expect &&
+ echo "folder1/dir/e3" >>expect &&
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+
+ git mv --sparse sub/dir folder1 2>stderr &&
+ cat dirty_error_header >expect &&
+ echo "folder1/dir/e2" >>expect &&
+ echo "folder1/dir/e3" >>expect &&
+ cat dirty_hint >>expect &&
+ test_cmp expect stderr &&
+
+ test_path_is_missing sub/dir &&
+ test_path_is_missing folder1/dir/e &&
+ test_path_is_file folder1/dir/e2 &&
+ test_path_is_file folder1/dir/e3 &&
+ git ls-files -t >actual &&
+ ! grep "H sub/dir/e" actual &&
+ ! grep "H sub/dir/e2" actual &&
+ ! grep "H sub/dir/e3" actual &&
+ grep "S folder1/dir/e" actual &&
+ grep "H folder1/dir/e2" actual &&
+ grep "H folder1/dir/e3" actual
+'
+
test_done
diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh
index e18a218952..f6aebe92ff 100755
--- a/t/t7003-filter-branch.sh
+++ b/t/t7003-filter-branch.sh
@@ -49,7 +49,7 @@ test_expect_success 'result is really identical' '
test_expect_success 'rewrite bare repository identically' '
(git config core.bare true && cd .git &&
git filter-branch branch > filter-output 2>&1 &&
- ! fgrep fatal filter-output)
+ ! grep fatal filter-output)
'
git config core.bare false
test_expect_success 'result is really identical' '
@@ -506,7 +506,7 @@ test_expect_success 'rewrite repository including refs that point at non-commit
git tag -a -m "tag to a tree" treetag $new_tree &&
git reset --hard HEAD &&
git filter-branch -f -- --all >filter-output 2>&1 &&
- ! fgrep fatal filter-output
+ ! grep fatal filter-output
'
test_expect_success 'filter-branch handles ref deletion' '
diff --git a/t/t7007-show.sh b/t/t7007-show.sh
index d6cc69e0f2..f908a4d1ab 100755
--- a/t/t7007-show.sh
+++ b/t/t7007-show.sh
@@ -2,6 +2,7 @@
test_description='git show'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t7060-wtstatus.sh b/t/t7060-wtstatus.sh
index 0f4344c55e..aaeb4a5334 100755
--- a/t/t7060-wtstatus.sh
+++ b/t/t7060-wtstatus.sh
@@ -5,6 +5,7 @@ test_description='basic work tree status reporting'
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/t7062-wtstatus-ignorecase.sh b/t/t7062-wtstatus-ignorecase.sh
index 73709dbeee..caf372a3d4 100755
--- a/t/t7062-wtstatus-ignorecase.sh
+++ b/t/t7062-wtstatus-ignorecase.sh
@@ -2,6 +2,7 @@
test_description='git-status with core.ignorecase=true'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'status with hash collisions' '
diff --git a/t/t7064-wtstatus-pv2.sh b/t/t7064-wtstatus-pv2.sh
index 20a0d2afc2..11884d2fc3 100755
--- a/t/t7064-wtstatus-pv2.sh
+++ b/t/t7064-wtstatus-pv2.sh
@@ -473,6 +473,7 @@ test_expect_success 'create and add submodule, submodule appears clean (A. S...)
git checkout initial-branch &&
git clone . sub_repo &&
git clone . super_repo &&
+ test_config_global protocol.file.allow always &&
( cd super_repo &&
git submodule add ../sub_repo sub1 &&
diff --git a/t/t7110-reset-merge.sh b/t/t7110-reset-merge.sh
index 3d62e10b53..eb881be95b 100755
--- a/t/t7110-reset-merge.sh
+++ b/t/t7110-reset-merge.sh
@@ -5,6 +5,7 @@
test_description='Tests for "git reset" with "--merge" and "--keep" options'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t7111-reset-table.sh b/t/t7111-reset-table.sh
index ce421ad5ac..78f25c1c7e 100755
--- a/t/t7111-reset-table.sh
+++ b/t/t7111-reset-table.sh
@@ -5,6 +5,7 @@
test_description='Tests to check that "reset" options follow a known table'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh
index 0399701e62..c975eb54d2 100755
--- a/t/t7300-clean.sh
+++ b/t/t7300-clean.sh
@@ -480,6 +480,7 @@ test_expect_success 'should not clean submodules' '
git init &&
test_commit msg hello.world
) &&
+ test_config_global protocol.file.allow always &&
git submodule add ./repo/.git sub1 &&
git commit -m "sub1" &&
git branch before_sub2 &&
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index e7cec2e457..a989aafaf5 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -14,6 +14,36 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
+test_expect_success 'setup - enable local submodules' '
+ git config --global protocol.file.allow always
+'
+
+test_expect_success 'submodule usage: -h' '
+ git submodule -h >out 2>err &&
+ grep "^usage: git submodule" out &&
+ test_must_be_empty err
+'
+
+test_expect_success 'submodule usage: --recursive' '
+ test_expect_code 1 git submodule --recursive >out 2>err &&
+ grep "^usage: git submodule" err &&
+ test_must_be_empty out
+'
+
+test_expect_success 'submodule usage: status --' '
+ test_expect_code 1 git submodule -- &&
+ test_expect_code 1 git submodule --end-of-options
+'
+
+for opt in '--quiet' '--cached'
+do
+ test_expect_success "submodule usage: status $opt" '
+ git submodule $opt &&
+ git submodule status $opt &&
+ git submodule $opt status
+ '
+done
+
test_expect_success 'submodule deinit works on empty repository' '
git submodule deinit --all
'
@@ -152,6 +182,11 @@ test_expect_success 'submodule add' '
test_must_be_empty untracked
'
+test_expect_success !WINDOWS 'submodule add (absolute path)' '
+ test_when_finished "git reset --hard" &&
+ git submodule add "$submodurl" "$submodurl/add-abs"
+'
+
test_expect_success 'setup parent and one repository' '
test_create_repo parent &&
test_commit -C parent one
@@ -1224,31 +1259,6 @@ test_expect_success 'submodule add clone shallow submodule' '
)
'
-test_expect_success 'submodule helper list is not confused by common prefixes' '
- mkdir -p dir1/b &&
- (
- cd dir1/b &&
- git init &&
- echo hi >testfile2 &&
- git add . &&
- git commit -m "test1"
- ) &&
- mkdir -p dir2/b &&
- (
- cd dir2/b &&
- git init &&
- echo hello >testfile1 &&
- git add . &&
- git commit -m "test2"
- ) &&
- git submodule add /dir1/b dir1/b &&
- git submodule add /dir2/b dir2/b &&
- git commit -m "first submodule commit" &&
- git submodule--helper list dir1/b | cut -f 2 >actual &&
- echo "dir1/b" >expect &&
- test_cmp expect actual
-'
-
test_expect_success 'setup superproject with submodules' '
git init sub1 &&
test_commit -C sub1 test &&
diff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh
index 9c3cc4cf40..542b3331a7 100755
--- a/t/t7401-submodule-summary.sh
+++ b/t/t7401-submodule-summary.sh
@@ -17,6 +17,7 @@ This test script tries to verify the sanity of summary subcommand of git submodu
# various reasons, one of them being that there are lots of commands taking place
# outside of 'test_expect_success' block, which is no longer in good-style.
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
add_file () {
diff --git a/t/t7402-submodule-rebase.sh b/t/t7402-submodule-rebase.sh
index 8e32f19007..ebeca12a71 100755
--- a/t/t7402-submodule-rebase.sh
+++ b/t/t7402-submodule-rebase.sh
@@ -104,7 +104,7 @@ test_expect_success 'rebasing submodule that should conflict' '
test_tick &&
git commit -m fourth &&
- test_must_fail git rebase --onto HEAD^^ HEAD^ HEAD^0 &&
+ test_must_fail git rebase --onto HEAD^^ HEAD^ HEAD^0 >actual_output &&
git ls-files -s submodule >actual &&
(
cd submodule &&
@@ -112,7 +112,12 @@ test_expect_success 'rebasing submodule that should conflict' '
echo "160000 $(git rev-parse HEAD^^) 2 submodule" &&
echo "160000 $(git rev-parse HEAD) 3 submodule"
) >expect &&
- test_cmp expect actual
+ test_cmp expect actual &&
+ if test "$GIT_TEST_MERGE_ALGORITHM" = ort
+ then
+ sub_expect="go to submodule (submodule), and either merge commit $(git -C submodule rev-parse --short HEAD^0)" &&
+ grep "$sub_expect" actual_output
+ fi
'
test_done
diff --git a/t/t7403-submodule-sync.sh b/t/t7403-submodule-sync.sh
index 7d2ac3322b..ea92ef52a5 100755
--- a/t/t7403-submodule-sync.sh
+++ b/t/t7403-submodule-sync.sh
@@ -14,6 +14,8 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
test_expect_success setup '
+ git config --global protocol.file.allow always &&
+
echo file >file &&
git add file &&
test_tick &&
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index 6cc07460dd..f094e3d7f3 100755
--- a/t/t7406-submodule-update.sh
+++ b/t/t7406-submodule-update.sh
@@ -25,6 +25,7 @@ compare_head()
test_expect_success 'setup a submodule tree' '
+ git config --global protocol.file.allow always &&
echo file > file &&
git add file &&
test_tick &&
@@ -769,7 +770,7 @@ test_expect_success 'submodule update continues after recursive checkout error'
echo "" > file
)
) &&
- test_must_fail git submodule update --recursive &&
+ test_expect_code 1 git submodule update --recursive &&
(cd submodule2 &&
git rev-parse --verify HEAD >../actual
) &&
diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh
index e2f110b786..59bd150166 100755
--- a/t/t7407-submodule-foreach.sh
+++ b/t/t7407-submodule-foreach.sh
@@ -16,6 +16,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
test_expect_success 'setup a submodule tree' '
+ git config --global protocol.file.allow always &&
echo file > file &&
git add file &&
test_tick &&
diff --git a/t/t7408-submodule-reference.sh b/t/t7408-submodule-reference.sh
index c3a4545510..d6040e0a33 100755
--- a/t/t7408-submodule-reference.sh
+++ b/t/t7408-submodule-reference.sh
@@ -17,6 +17,10 @@ test_alternate_is_used () {
test_cmp expect actual
}
+test_expect_success 'setup' '
+ git config --global protocol.file.allow always
+'
+
test_expect_success 'preparing first repository' '
test_create_repo A &&
(
diff --git a/t/t7409-submodule-detached-work-tree.sh b/t/t7409-submodule-detached-work-tree.sh
index e17ac81a89..374ed481e9 100755
--- a/t/t7409-submodule-detached-work-tree.sh
+++ b/t/t7409-submodule-detached-work-tree.sh
@@ -15,6 +15,10 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
+test_expect_success 'setup' '
+ git config --global protocol.file.allow always
+'
+
test_expect_success 'submodule on detached working tree' '
git init --bare remote &&
test_create_repo bundle1 &&
diff --git a/t/t7411-submodule-config.sh b/t/t7411-submodule-config.sh
index ad28e93880..c583c4e373 100755
--- a/t/t7411-submodule-config.sh
+++ b/t/t7411-submodule-config.sh
@@ -12,6 +12,9 @@ from the database and from the worktree works.
TEST_NO_CREATE_REPO=1
. ./test-lib.sh
+test_expect_success 'setup' '
+ git config --global protocol.file.allow always
+'
test_expect_success 'submodule config cache setup' '
mkdir submodule &&
(cd submodule &&
diff --git a/t/t7412-submodule-absorbgitdirs.sh b/t/t7412-submodule-absorbgitdirs.sh
index 1cfa150768..2859695c6d 100755
--- a/t/t7412-submodule-absorbgitdirs.sh
+++ b/t/t7412-submodule-absorbgitdirs.sh
@@ -6,6 +6,7 @@ This test verifies that `git submodue absorbgitdirs` moves a submodules git
directory into the superproject.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup a real submodule' '
diff --git a/t/t7413-submodule-is-active.sh b/t/t7413-submodule-is-active.sh
index c8e7e98331..7cdc263764 100755
--- a/t/t7413-submodule-is-active.sh
+++ b/t/t7413-submodule-is-active.sh
@@ -1,14 +1,19 @@
#!/bin/sh
-test_description='Test submodule--helper is-active
+test_description='Test with test-tool submodule is-active
-This test verifies that `git submodue--helper is-active` correctly identifies
+This test verifies that `test-tool submodule is-active` correctly identifies
submodules which are "active" and interesting to the user.
+
+This is a unit test of the submodule.c is_submodule_active() function,
+which is also indirectly tested elsewhere.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
+ git config --global protocol.file.allow always &&
git init sub &&
test_commit -C sub initial &&
git init super &&
@@ -25,13 +30,13 @@ test_expect_success 'setup' '
'
test_expect_success 'is-active works with urls' '
- git -C super submodule--helper is-active sub1 &&
- git -C super submodule--helper is-active sub2 &&
+ test-tool -C super submodule is-active sub1 &&
+ test-tool -C super submodule is-active sub2 &&
git -C super config --unset submodule.sub1.URL &&
- test_must_fail git -C super submodule--helper is-active sub1 &&
+ test_must_fail test-tool -C super submodule is-active sub1 &&
git -C super config submodule.sub1.URL ../sub &&
- git -C super submodule--helper is-active sub1
+ test-tool -C super submodule is-active sub1
'
test_expect_success 'is-active works with submodule.<name>.active config' '
@@ -39,11 +44,11 @@ test_expect_success 'is-active works with submodule.<name>.active config' '
test_when_finished "git -C super config submodule.sub1.URL ../sub" &&
git -C super config --bool submodule.sub1.active "false" &&
- test_must_fail git -C super submodule--helper is-active sub1 &&
+ test_must_fail test-tool -C super submodule is-active sub1 &&
git -C super config --bool submodule.sub1.active "true" &&
git -C super config --unset submodule.sub1.URL &&
- git -C super submodule--helper is-active sub1
+ test-tool -C super submodule is-active sub1
'
test_expect_success 'is-active works with basic submodule.active config' '
@@ -53,17 +58,17 @@ test_expect_success 'is-active works with basic submodule.active config' '
git -C super config --add submodule.active "." &&
git -C super config --unset submodule.sub1.URL &&
- git -C super submodule--helper is-active sub1 &&
- git -C super submodule--helper is-active sub2
+ test-tool -C super submodule is-active sub1 &&
+ test-tool -C super submodule is-active sub2
'
test_expect_success 'is-active correctly works with paths that are not submodules' '
test_when_finished "git -C super config --unset-all submodule.active" &&
- test_must_fail git -C super submodule--helper is-active not-a-submodule &&
+ test_must_fail test-tool -C super submodule is-active not-a-submodule &&
git -C super config --add submodule.active "." &&
- test_must_fail git -C super submodule--helper is-active not-a-submodule
+ test_must_fail test-tool -C super submodule is-active not-a-submodule
'
test_expect_success 'is-active works with exclusions in submodule.active config' '
@@ -72,8 +77,8 @@ test_expect_success 'is-active works with exclusions in submodule.active config'
git -C super config --add submodule.active "." &&
git -C super config --add submodule.active ":(exclude)sub1" &&
- test_must_fail git -C super submodule--helper is-active sub1 &&
- git -C super submodule--helper is-active sub2
+ test_must_fail test-tool -C super submodule is-active sub1 &&
+ test-tool -C super submodule is-active sub2
'
test_expect_success 'is-active with submodule.active and submodule.<name>.active' '
@@ -85,8 +90,8 @@ test_expect_success 'is-active with submodule.active and submodule.<name>.active
git -C super config --bool submodule.sub1.active "false" &&
git -C super config --bool submodule.sub2.active "true" &&
- test_must_fail git -C super submodule--helper is-active sub1 &&
- git -C super submodule--helper is-active sub2
+ test_must_fail test-tool -C super submodule is-active sub1 &&
+ test-tool -C super submodule is-active sub2
'
test_expect_success 'is-active, submodule.active and submodule add' '
diff --git a/t/t7414-submodule-mistakes.sh b/t/t7414-submodule-mistakes.sh
index f2e7df59cf..101afff30f 100755
--- a/t/t7414-submodule-mistakes.sh
+++ b/t/t7414-submodule-mistakes.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='handling of common mistakes people may make with submodules'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'create embedded repository' '
@@ -30,7 +32,8 @@ test_expect_success 'no warning when updating entry' '
test_expect_success 'submodule add does not warn' '
test_when_finished "git rm -rf submodule .gitmodules" &&
- git submodule add ./embed submodule 2>stderr &&
+ git -c protocol.file.allow=always \
+ submodule add ./embed submodule 2>stderr &&
test_i18ngrep ! warning stderr
'
diff --git a/t/t7416-submodule-dash-url.sh b/t/t7416-submodule-dash-url.sh
index d21dc8b009..3ebd985981 100755
--- a/t/t7416-submodule-dash-url.sh
+++ b/t/t7416-submodule-dash-url.sh
@@ -3,6 +3,10 @@
test_description='check handling of disallowed .gitmodule urls'
. ./test-lib.sh
+test_expect_success 'setup' '
+ git config --global protocol.file.allow always
+'
+
test_expect_success 'create submodule with protected dash in url' '
git init upstream &&
git -C upstream commit --allow-empty -m base &&
diff --git a/t/t7417-submodule-path-url.sh b/t/t7417-submodule-path-url.sh
index f0f6b9fa9e..2f4b25dfd7 100755
--- a/t/t7417-submodule-path-url.sh
+++ b/t/t7417-submodule-path-url.sh
@@ -6,6 +6,10 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
+test_expect_success 'setup' '
+ git config --global protocol.file.allow always
+'
+
test_expect_success 'create submodule with dash in path' '
git init upstream &&
git -C upstream commit --allow-empty -m base &&
diff --git a/t/t7418-submodule-sparse-gitmodules.sh b/t/t7418-submodule-sparse-gitmodules.sh
index 57d7ab3ece..d5874200fd 100755
--- a/t/t7418-submodule-sparse-gitmodules.sh
+++ b/t/t7418-submodule-sparse-gitmodules.sh
@@ -17,6 +17,10 @@ export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
. ./test-lib.sh
+test_expect_success 'setup' '
+ git config --global protocol.file.allow always
+'
+
test_expect_success 'sparse checkout setup which hides .gitmodules' '
git init upstream &&
git init submodule &&
diff --git a/t/t7419-submodule-set-branch.sh b/t/t7419-submodule-set-branch.sh
index 3b925c302f..232065504c 100755
--- a/t/t7419-submodule-set-branch.sh
+++ b/t/t7419-submodule-set-branch.sh
@@ -9,9 +9,14 @@ This test verifies that the set-branch subcommand of git-submodule is working
as expected.
'
+TEST_PASSES_SANITIZE_LEAK=true
TEST_NO_CREATE_REPO=1
. ./test-lib.sh
+test_expect_success 'setup' '
+ git config --global protocol.file.allow always
+'
+
test_expect_success 'submodule config cache setup' '
mkdir submodule &&
(cd submodule &&
diff --git a/t/t7420-submodule-set-url.sh b/t/t7420-submodule-set-url.sh
index ef0cb6e8e1..d6bf62b3ac 100755
--- a/t/t7420-submodule-set-url.sh
+++ b/t/t7420-submodule-set-url.sh
@@ -12,6 +12,10 @@ as expected.
TEST_NO_CREATE_REPO=1
. ./test-lib.sh
+test_expect_success 'setup' '
+ git config --global protocol.file.allow always
+'
+
test_expect_success 'submodule config cache setup' '
mkdir submodule &&
(
diff --git a/t/t7421-submodule-summary-add.sh b/t/t7421-submodule-summary-add.sh
index b070f13714..ce64d8b137 100755
--- a/t/t7421-submodule-summary-add.sh
+++ b/t/t7421-submodule-summary-add.sh
@@ -12,6 +12,10 @@ while making sure to add submodules using `git submodule add` instead of
. ./test-lib.sh
+test_expect_success 'setup' '
+ git config --global protocol.file.allow always
+'
+
test_expect_success 'summary test environment setup' '
git init sm &&
test_commit -C sm "add file" file file-content file-tag &&
diff --git a/t/t7450-bad-git-dotfiles.sh b/t/t7450-bad-git-dotfiles.sh
index 41706c1c9f..ba1f569bcb 100755
--- a/t/t7450-bad-git-dotfiles.sh
+++ b/t/t7450-bad-git-dotfiles.sh
@@ -15,13 +15,17 @@ Such as:
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-pack.sh
+test_expect_success 'setup' '
+ git config --global protocol.file.allow always
+'
+
test_expect_success 'check names' '
cat >expect <<-\EOF &&
valid
valid/with/paths
EOF
- git submodule--helper check-name >actual <<-\EOF &&
+ test-tool submodule check-name >actual <<-\EOF &&
valid
valid/with/paths
diff --git a/t/t7503-pre-commit-and-pre-merge-commit-hooks.sh b/t/t7503-pre-commit-and-pre-merge-commit-hooks.sh
index ad1eb64ba0..aa004b70a8 100755
--- a/t/t7503-pre-commit-and-pre-merge-commit-hooks.sh
+++ b/t/t7503-pre-commit-and-pre-merge-commit-hooks.sh
@@ -5,6 +5,7 @@ test_description='pre-commit and pre-merge-commit hooks'
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 'root commit' '
diff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh
index 3fcb44767f..d050091345 100755
--- a/t/t7506-status-submodule.sh
+++ b/t/t7506-status-submodule.sh
@@ -2,6 +2,7 @@
test_description='git status for submodule'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_create_repo_with_commit () {
@@ -251,6 +252,7 @@ test_expect_success 'status with merge conflict in .gitmodules' '
test_create_repo_with_commit sub1 &&
test_tick &&
test_create_repo_with_commit sub2 &&
+ test_config_global protocol.file.allow always &&
(
cd super &&
prev=$(git rev-parse HEAD) &&
@@ -326,6 +328,7 @@ test_expect_success 'diff --submodule with merge conflict in .gitmodules' '
# sub2 will have an untracked file
# sub3 will have an untracked repository
test_expect_success 'setup superproject with untracked file in nested submodule' '
+ test_config_global protocol.file.allow always &&
(
cd super &&
git clean -dfx &&
diff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh
index ed2653d46f..916470c48b 100755
--- a/t/t7507-commit-verbose.sh
+++ b/t/t7507-commit-verbose.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='verbose commit template'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
write_script "check-for-diff" <<\EOF &&
@@ -74,6 +76,7 @@ test_expect_success 'diff in message is retained with -v' '
test_expect_success 'submodule log is stripped out too with -v' '
git config diff.submodule log &&
+ test_config_global protocol.file.allow always &&
git submodule add ./. sub &&
git commit -m "sub added" &&
(
diff --git a/t/t7527-builtin-fsmonitor.sh b/t/t7527-builtin-fsmonitor.sh
index 56c0dfffea..4abc74db2b 100755
--- a/t/t7527-builtin-fsmonitor.sh
+++ b/t/t7527-builtin-fsmonitor.sh
@@ -813,6 +813,10 @@ my_match_and_clean () {
git -C super/dir_1/dir_2/sub clean -d -f
}
+test_expect_success 'submodule setup' '
+ git config --global protocol.file.allow always
+'
+
test_expect_success 'submodule always visited' '
test_when_finished "git -C super fsmonitor--daemon stop; \
rm -rf super; \
@@ -939,9 +943,9 @@ test_expect_success CASE_INSENSITIVE_FS 'case insensitive+preserving' '
# directories and files that we touched. We may or may not get a
# trailing slash on modified directories.
#
- egrep "^event: abc/?$" ./insensitive.trace &&
- egrep "^event: abc/def/?$" ./insensitive.trace &&
- egrep "^event: abc/def/xyz$" ./insensitive.trace
+ grep -E "^event: abc/?$" ./insensitive.trace &&
+ grep -E "^event: abc/def/?$" ./insensitive.trace &&
+ grep -E "^event: abc/def/xyz$" ./insensitive.trace
'
# The variable "unicode_debug" is defined in the following library
@@ -983,20 +987,20 @@ test_expect_success !UNICODE_COMPOSITION_SENSITIVE 'Unicode nfc/nfd' '
then
# We should have seen NFC event from OS.
# We should not have synthesized an NFD event.
- egrep "^event: nfc/c_${utf8_nfc}/?$" ./unicode.trace &&
- egrep -v "^event: nfc/c_${utf8_nfd}/?$" ./unicode.trace
+ grep -E "^event: nfc/c_${utf8_nfc}/?$" ./unicode.trace &&
+ grep -E -v "^event: nfc/c_${utf8_nfd}/?$" ./unicode.trace
else
# We should have seen NFD event from OS.
# We should have synthesized an NFC event.
- egrep "^event: nfc/c_${utf8_nfd}/?$" ./unicode.trace &&
- egrep "^event: nfc/c_${utf8_nfc}/?$" ./unicode.trace
+ grep -E "^event: nfc/c_${utf8_nfd}/?$" ./unicode.trace &&
+ grep -E "^event: nfc/c_${utf8_nfc}/?$" ./unicode.trace
fi &&
# We assume UNICODE_NFD_PRESERVED.
# We should have seen explicit NFD from OS.
# We should have synthesized an NFC event.
- egrep "^event: nfd/d_${utf8_nfd}/?$" ./unicode.trace &&
- egrep "^event: nfd/d_${utf8_nfc}/?$" ./unicode.trace
+ grep -E "^event: nfd/d_${utf8_nfd}/?$" ./unicode.trace &&
+ grep -E "^event: nfd/d_${utf8_nfc}/?$" ./unicode.trace
'
test_done
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
index f0f6fda150..7c3f6ed994 100755
--- a/t/t7600-merge.sh
+++ b/t/t7600-merge.sh
@@ -255,6 +255,15 @@ test_expect_success 'merge --squash c3 with c7' '
test_cmp expect actual
'
+test_expect_success 'merge --squash --autostash conflict does not attempt to apply autostash' '
+ git reset --hard c3 &&
+ >unrelated &&
+ git add unrelated &&
+ test_must_fail git merge --squash c7 --autostash >out 2>err &&
+ ! grep "Applying autostash resulted in conflicts." err &&
+ grep "When finished, apply stashed changes with \`git stash pop\`" out
+'
+
test_expect_success 'merge c3 with c7 with commit.cleanup = scissors' '
git config commit.cleanup scissors &&
git reset --hard c3 &&
diff --git a/t/t7609-mergetool--lib.sh b/t/t7609-mergetool--lib.sh
index 330d6d603d..8b1c3bd39f 100755
--- a/t/t7609-mergetool--lib.sh
+++ b/t/t7609-mergetool--lib.sh
@@ -4,6 +4,7 @@ test_description='git mergetool
Testing basic merge tools options'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'mergetool --tool=vimdiff creates the expected layout' '
diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh
index ca45c4cd2c..5be483bf88 100755
--- a/t/t7700-repack.sh
+++ b/t/t7700-repack.sh
@@ -426,12 +426,73 @@ test_expect_success '--write-midx -b packs non-kept objects' '
)
'
+test_expect_success '--write-midx removes stale pack-based bitmaps' '
+ rm -fr repo &&
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ test_commit base &&
+ GIT_TEST_MULTI_PACK_INDEX=0 git repack -Ab &&
+
+ pack_bitmap=$(ls $objdir/pack/pack-*.bitmap) &&
+ test_path_is_file "$pack_bitmap" &&
+
+ test_commit tip &&
+ GIT_TEST_MULTI_PACK_INDEX=0 git repack -bm &&
+
+ test_path_is_file $midx &&
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
+ test_path_is_missing $pack_bitmap
+ )
+'
+
+test_expect_success '--write-midx with --pack-kept-objects' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit one &&
+ test_commit two &&
+
+ one="$(echo "one" | git pack-objects --revs $objdir/pack/pack)" &&
+ two="$(echo "one..two" | git pack-objects --revs $objdir/pack/pack)" &&
+
+ keep="$objdir/pack/pack-$one.keep" &&
+ touch "$keep" &&
+
+ git repack --write-midx --write-bitmap-index --geometric=2 -d \
+ --pack-kept-objects &&
+
+ test_path_is_file $keep &&
+ test_path_is_file $midx &&
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap
+ )
+'
+
test_expect_success TTY '--quiet disables progress' '
test_terminal env GIT_PROGRESS_DELAY=0 \
git -C midx repack -ad --quiet --write-midx 2>stderr &&
test_must_be_empty stderr
'
+test_expect_success 'clean up .tmp-* packs on error' '
+ test_must_fail ok=sigpipe git \
+ -c repack.cruftwindow=bogus \
+ repack -ad --cruft &&
+ find $objdir/pack -name '.tmp-*' >tmpfiles &&
+ test_must_be_empty tmpfiles
+'
+
+test_expect_success 'repack -ad cleans up old .tmp-* packs' '
+ git rev-parse HEAD >input &&
+ git pack-objects $objdir/pack/.tmp-1234 <input &&
+ git repack -ad &&
+ find $objdir/pack -name '.tmp-*' >tmpfiles &&
+ test_must_be_empty tmpfiles
+'
+
test_expect_success 'setup for update-server-info' '
git init update-server-info &&
test_commit -C update-server-info message
diff --git a/t/t7701-repack-unpack-unreachable.sh b/t/t7701-repack-unpack-unreachable.sh
index 937f89ee8c..b7ac4f598a 100755
--- a/t/t7701-repack-unpack-unreachable.sh
+++ b/t/t7701-repack-unpack-unreachable.sh
@@ -35,7 +35,7 @@ test_expect_success '-A with -d option leaves unreachable objects unpacked' '
git repack -A -d -l &&
# verify objects are packed in repository
test 3 = $(git verify-pack -v -- .git/objects/pack/*.idx |
- egrep "^($fsha1|$csha1|$tsha1) " |
+ grep -E "^($fsha1|$csha1|$tsha1) " |
sort | uniq | wc -l) &&
git show $fsha1 &&
git show $csha1 &&
@@ -49,7 +49,7 @@ test_expect_success '-A with -d option leaves unreachable objects unpacked' '
git repack -A -d -l &&
# verify objects are retained unpacked
test 0 = $(git verify-pack -v -- .git/objects/pack/*.idx |
- egrep "^($fsha1|$csha1|$tsha1) " |
+ grep -E "^($fsha1|$csha1|$tsha1) " |
sort | uniq | wc -l) &&
git show $fsha1 &&
git show $csha1 &&
diff --git a/t/t7703-repack-geometric.sh b/t/t7703-repack-geometric.sh
index da87f8b2d8..8821fbd2dd 100755
--- a/t/t7703-repack-geometric.sh
+++ b/t/t7703-repack-geometric.sh
@@ -176,8 +176,12 @@ test_expect_success '--geometric ignores kept packs' '
# be repacked, too.
git repack --geometric 2 -d --pack-kept-objects &&
+ # After repacking, two packs remain: one new one (containing the
+ # objects in both the .keep and non-kept pack), and the .keep
+ # pack (since `--pack-kept-objects -d` does not actually delete
+ # the kept pack).
find $objdir/pack -name "*.pack" >after &&
- test_line_count = 1 after
+ test_line_count = 2 after
)
'
diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh
index 096456292c..24297e26ca 100755
--- a/t/t7800-difftool.sh
+++ b/t/t7800-difftool.sh
@@ -636,6 +636,7 @@ test_expect_success 'difftool --no-symlinks detects conflict ' '
test_expect_success 'difftool properly honors gitlink and core.worktree' '
test_when_finished rm -rf submod/ule &&
+ test_config_global protocol.file.allow always &&
git submodule add ./. submod/ule &&
test_config -C submod/ule diff.tool checktrees &&
test_config -C submod/ule difftool.checktrees.cmd '\''
diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh
index 0f937990a0..8eded6ab27 100755
--- a/t/t7810-grep.sh
+++ b/t/t7810-grep.sh
@@ -18,6 +18,9 @@ test_invalid_grep_expression() {
'
}
+LC_ALL=en_US.UTF-8 test-tool regex '^.$' '¿' &&
+ test_set_prereq MB_REGEX
+
cat >hello.c <<EOF
#include <assert.h>
#include <stdio.h>
@@ -88,6 +91,10 @@ test_expect_success setup '
echo unusual >"\"unusual\" pathname" &&
echo unusual >"t/nested \"unusual\" pathname"
fi &&
+ if test_have_prereq MB_REGEX
+ then
+ echo "¿" >reverse-question-mark
+ fi &&
git add . &&
test_tick &&
git commit -m initial
@@ -569,6 +576,14 @@ do
'
done
+test_expect_success MB_REGEX 'grep exactly one char in single-char multibyte file' '
+ LC_ALL=en_US.UTF-8 git grep "^.$" reverse-question-mark
+'
+
+test_expect_success MB_REGEX 'grep two chars in single-char multibyte file' '
+ LC_ALL=en_US.UTF-8 test_expect_code 1 git grep ".." reverse-question-mark
+'
+
cat >expected <<EOF
file
EOF
diff --git a/t/t7814-grep-recurse-submodules.sh b/t/t7814-grep-recurse-submodules.sh
index 3ad80526c4..8143817b19 100755
--- a/t/t7814-grep-recurse-submodules.sh
+++ b/t/t7814-grep-recurse-submodules.sh
@@ -197,6 +197,7 @@ test_expect_success !MINGW 'grep recurse submodule colon in name' '
git -C "su:b" commit -m "add fi:le" &&
test_tick &&
+ test_config_global protocol.file.allow always &&
git -C parent submodule add "../su:b" "su:b" &&
git -C parent commit -m "add submodule" &&
test_tick &&
@@ -231,6 +232,7 @@ test_expect_success 'grep history with moved submoules' '
git -C sub commit -m "add file" &&
test_tick &&
+ test_config_global protocol.file.allow always &&
git -C parent submodule add ../sub dir/sub &&
git -C parent commit -m "add submodule" &&
test_tick &&
@@ -275,6 +277,7 @@ test_expect_success 'grep using relative path' '
mkdir parent/src &&
echo "(1|2)d(3|4)" >parent/src/file2 &&
git -C parent add src/file2 &&
+ test_config_global protocol.file.allow always &&
git -C parent submodule add ../sub &&
git -C parent commit -m "add files and submodule" &&
test_tick &&
@@ -317,6 +320,7 @@ test_expect_success 'grep from a subdir' '
mkdir parent/src &&
echo "(1|2)d(3|4)" >parent/src/file &&
git -C parent add src/file &&
+ test_config_global protocol.file.allow always &&
git -C parent submodule add ../sub src/sub &&
git -C parent submodule add ../sub sub &&
git -C parent commit -m "add files and submodules" &&
@@ -550,6 +554,7 @@ test_expect_failure 'grep saves textconv cache in the appropriate repository' '
test_expect_success 'grep partially-cloned submodule' '
# Set up clean superproject and submodule for partial cloning.
+ test_config_global protocol.file.allow always &&
git init super &&
git init super/sub &&
(
diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh
index 74aa638475..96bdd42045 100755
--- a/t/t7900-maintenance.sh
+++ b/t/t7900-maintenance.sh
@@ -32,11 +32,13 @@ test_systemd_analyze_verify () {
}
test_expect_success 'help text' '
- test_expect_code 129 git maintenance -h 2>err &&
- test_i18ngrep "usage: git maintenance <subcommand>" err &&
- test_expect_code 128 git maintenance barf 2>err &&
- test_i18ngrep "invalid subcommand: barf" err &&
+ test_expect_code 129 git maintenance -h >actual &&
+ test_i18ngrep "usage: git maintenance <subcommand>" actual &&
+ test_expect_code 129 git maintenance barf 2>err &&
+ test_i18ngrep "unknown subcommand: \`barf'\''" err &&
+ test_i18ngrep "usage: git maintenance" err &&
test_expect_code 129 git maintenance 2>err &&
+ test_i18ngrep "error: need a subcommand" err &&
test_i18ngrep "usage: git maintenance" err
'
@@ -162,7 +164,6 @@ test_expect_success 'prefetch multiple remotes' '
test_cmp_rev refs/remotes/remote1/one refs/prefetch/remotes/remote1/one &&
test_cmp_rev refs/remotes/remote2/two refs/prefetch/remotes/remote2/two &&
- test_cmp_config refs/prefetch/ log.excludedecoration &&
git log --oneline --decorate --all >log &&
! grep "prefetch" log &&
@@ -173,26 +174,6 @@ test_expect_success 'prefetch multiple remotes' '
test_subcommand git fetch remote2 $fetchargs <skip-remote1.txt
'
-test_expect_success 'prefetch and existing log.excludeDecoration values' '
- git config --unset-all log.excludeDecoration &&
- git config log.excludeDecoration refs/remotes/remote1/ &&
- git maintenance run --task=prefetch &&
-
- git config --get-all log.excludeDecoration >out &&
- grep refs/remotes/remote1/ out &&
- grep refs/prefetch/ out &&
-
- git log --oneline --decorate --all >log &&
- ! grep "prefetch" log &&
- ! grep "remote1" log &&
- grep "remote2" log &&
-
- # a second run does not change the config
- git maintenance run --task=prefetch &&
- git log --oneline --decorate --all >log2 &&
- test_cmp log log2
-'
-
test_expect_success 'loose-objects task' '
# Repack everything so we know the state of the object dir
git repack -adk &&
@@ -499,6 +480,11 @@ test_expect_success 'maintenance.strategy inheritance' '
test_expect_success 'register and unregister' '
test_when_finished git config --global --unset-all maintenance.repo &&
+
+ test_must_fail git maintenance unregister 2>err &&
+ grep "is not registered" err &&
+ git maintenance unregister --force &&
+
git config --global --add maintenance.repo /existing1 &&
git config --global --add maintenance.repo /existing2 &&
git config --global --get-all maintenance.repo >before &&
@@ -512,7 +498,11 @@ test_expect_success 'register and unregister' '
git maintenance unregister &&
git config --global --get-all maintenance.repo >actual &&
- test_cmp before actual
+ test_cmp before actual &&
+
+ test_must_fail git maintenance unregister 2>err &&
+ grep "is not registered" err &&
+ git maintenance unregister --force
'
test_expect_success !MINGW 'register and unregister with regex metacharacters' '
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 01c74b8b07..1130ef21b3 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -1519,7 +1519,7 @@ test_expect_success $PREREQ 'asks about and fixes 8bit encodings' '
grep "do not declare a Content-Transfer-Encoding" stdout &&
grep email-using-8bit stdout &&
grep "Which 8bit encoding" stdout &&
- egrep "Content|MIME" msgtxt1 >actual &&
+ grep -E "Content|MIME" msgtxt1 >actual &&
test_cmp content-type-decl actual
'
@@ -1530,7 +1530,7 @@ test_expect_success $PREREQ 'sendemail.8bitEncoding works' '
git send-email --from=author@example.com --to=nobody@example.com \
--smtp-server="$(pwd)/fake.sendmail" \
email-using-8bit >stdout &&
- egrep "Content|MIME" msgtxt1 >actual &&
+ grep -E "Content|MIME" msgtxt1 >actual &&
test_cmp content-type-decl actual
'
@@ -1545,7 +1545,7 @@ test_expect_success $PREREQ 'sendemail.8bitEncoding in .git/config overrides --g
git send-email --from=author@example.com --to=nobody@example.com \
--smtp-server="$(pwd)/fake.sendmail" \
email-using-8bit >stdout &&
- egrep "Content|MIME" msgtxt1 >actual &&
+ grep -E "Content|MIME" msgtxt1 >actual &&
test_cmp content-type-decl actual
'
@@ -1557,7 +1557,7 @@ test_expect_success $PREREQ '--8bit-encoding overrides sendemail.8bitEncoding' '
--smtp-server="$(pwd)/fake.sendmail" \
--8bit-encoding=UTF-8 \
email-using-8bit >stdout &&
- egrep "Content|MIME" msgtxt1 >actual &&
+ grep -E "Content|MIME" msgtxt1 >actual &&
test_cmp content-type-decl actual
'
diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
index 7c5b847f58..fea41b3c36 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -8,7 +8,6 @@ test_description='git svn basic tests'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
prepare_utf8_locale
diff --git a/t/t9122-git-svn-author.sh b/t/t9122-git-svn-author.sh
index 527ba3d293..0fc289ae0f 100755
--- a/t/t9122-git-svn-author.sh
+++ b/t/t9122-git-svn-author.sh
@@ -2,7 +2,6 @@
test_description='git svn authorship'
-TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
test_expect_success 'setup svn repository' '
diff --git a/t/t9133-git-svn-nested-git-repo.sh b/t/t9133-git-svn-nested-git-repo.sh
index f894860867..d8d536269c 100755
--- a/t/t9133-git-svn-nested-git-repo.sh
+++ b/t/t9133-git-svn-nested-git-repo.sh
@@ -35,7 +35,7 @@ test_expect_success 'SVN-side change outside of .git' '
echo b >> a &&
svn_cmd commit -m "SVN-side change outside of .git" &&
svn_cmd up &&
- svn_cmd log -v | fgrep "SVN-side change outside of .git"
+ svn_cmd log -v | grep -F "SVN-side change outside of .git"
)
'
@@ -59,7 +59,7 @@ test_expect_success 'SVN-side change inside of .git' '
svn_cmd add --force .git &&
svn_cmd commit -m "SVN-side change inside of .git" &&
svn_cmd up &&
- svn_cmd log -v | fgrep "SVN-side change inside of .git"
+ svn_cmd log -v | grep -F "SVN-side change inside of .git"
)
'
@@ -82,7 +82,7 @@ test_expect_success 'SVN-side change in and out of .git' '
git commit -m "add a inside an SVN repo" &&
svn_cmd commit -m "SVN-side change in and out of .git" &&
svn_cmd up &&
- svn_cmd log -v | fgrep "SVN-side change in and out of .git"
+ svn_cmd log -v | grep -F "SVN-side change in and out of .git"
)
'
diff --git a/t/t9134-git-svn-ignore-paths.sh b/t/t9134-git-svn-ignore-paths.sh
index 4a77eb9f60..3188400226 100755
--- a/t/t9134-git-svn-ignore-paths.sh
+++ b/t/t9134-git-svn-ignore-paths.sh
@@ -43,7 +43,7 @@ test_expect_success 'init+fetch an SVN repository with ignored www directory' '
test_expect_success 'verify ignore-paths config saved by clone' '
(
cd g &&
- git config --get svn-remote.svn.ignore-paths | fgrep "www"
+ git config --get svn-remote.svn.ignore-paths | grep www
)
'
@@ -53,7 +53,7 @@ test_expect_success 'SVN-side change outside of www' '
echo b >> qqq/test_qqq.txt &&
svn_cmd commit -m "SVN-side change outside of www" &&
svn_cmd up &&
- svn_cmd log -v | fgrep "SVN-side change outside of www"
+ svn_cmd log -v | grep "SVN-side change outside of www"
)
'
@@ -85,7 +85,7 @@ test_expect_success 'SVN-side change inside of ignored www' '
echo zaq >> www/test_www.txt &&
svn_cmd commit -m "SVN-side change inside of www/test_www.txt" &&
svn_cmd up &&
- svn_cmd log -v | fgrep "SVN-side change inside of www/test_www.txt"
+ svn_cmd log -v | grep -F "SVN-side change inside of www/test_www.txt"
)
'
@@ -118,7 +118,7 @@ test_expect_success 'SVN-side change in and out of ignored www' '
echo ygg >> qqq/test_qqq.txt &&
svn_cmd commit -m "SVN-side change in and out of ignored www" &&
svn_cmd up &&
- svn_cmd log -v | fgrep "SVN-side change in and out of ignored www"
+ svn_cmd log -v | grep "SVN-side change in and out of ignored www"
)
'
diff --git a/t/t9140-git-svn-reset.sh b/t/t9140-git-svn-reset.sh
index e855904629..a420b2a87a 100755
--- a/t/t9140-git-svn-reset.sh
+++ b/t/t9140-git-svn-reset.sh
@@ -43,7 +43,7 @@ test_expect_success 'fetch fails on modified hidden file' '
git svn find-rev refs/remotes/git-svn > ../expect &&
test_must_fail git svn fetch 2> ../errors &&
git svn find-rev refs/remotes/git-svn > ../expect2 ) &&
- fgrep "not found in commit" errors &&
+ grep "not found in commit" errors &&
test_cmp expect expect2
'
@@ -59,7 +59,7 @@ test_expect_success 'refetch succeeds not ignoring any files' '
( cd g &&
git svn fetch &&
git svn rebase &&
- fgrep "mod hidden" hid/hid.txt
+ grep "mod hidden" hid/hid.txt
)
'
diff --git a/t/t9147-git-svn-include-paths.sh b/t/t9147-git-svn-include-paths.sh
index 257fc8f2f8..63fa0b6732 100755
--- a/t/t9147-git-svn-include-paths.sh
+++ b/t/t9147-git-svn-include-paths.sh
@@ -45,7 +45,7 @@ test_expect_success 'init+fetch an SVN repository with included qqq directory' '
test_expect_success 'verify include-paths config saved by clone' '
(
cd g &&
- git config --get svn-remote.svn.include-paths | fgrep "qqq"
+ git config --get svn-remote.svn.include-paths | grep qqq
)
'
@@ -55,7 +55,7 @@ test_expect_success 'SVN-side change outside of www' '
echo b >> qqq/test_qqq.txt &&
svn_cmd commit -m "SVN-side change outside of www" &&
svn_cmd up &&
- svn_cmd log -v | fgrep "SVN-side change outside of www"
+ svn_cmd log -v | grep "SVN-side change outside of www"
)
'
@@ -87,7 +87,7 @@ test_expect_success 'SVN-side change inside of ignored www' '
echo zaq >> www/test_www.txt &&
svn_cmd commit -m "SVN-side change inside of www/test_www.txt" &&
svn_cmd up &&
- svn_cmd log -v | fgrep "SVN-side change inside of www/test_www.txt"
+ svn_cmd log -v | grep "SVN-side change inside of www/test_www.txt"
)
'
@@ -120,7 +120,7 @@ test_expect_success 'SVN-side change in and out of included qqq' '
echo ygg >> qqq/test_qqq.txt &&
svn_cmd commit -m "SVN-side change in and out of ignored www" &&
svn_cmd up &&
- svn_cmd log -v | fgrep "SVN-side change in and out of ignored www"
+ svn_cmd log -v | grep "SVN-side change in and out of ignored www"
)
'
diff --git a/t/t9162-git-svn-dcommit-interactive.sh b/t/t9162-git-svn-dcommit-interactive.sh
index e2aa8ed88a..b3ce033a0d 100755
--- a/t/t9162-git-svn-dcommit-interactive.sh
+++ b/t/t9162-git-svn-dcommit-interactive.sh
@@ -4,7 +4,6 @@
test_description='git svn dcommit --interactive series'
-TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
test_expect_success 'initialize repo' '
diff --git a/t/t9210-scalar.sh b/t/t9210-scalar.sh
new file mode 100755
index 0000000000..be51a8bb7a
--- /dev/null
+++ b/t/t9210-scalar.sh
@@ -0,0 +1,213 @@
+#!/bin/sh
+
+test_description='test the `scalar` command'
+
+. ./test-lib.sh
+
+GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt,launchctl:true,schtasks:true"
+export GIT_TEST_MAINT_SCHEDULER
+
+test_expect_success 'scalar shows a usage' '
+ test_expect_code 129 scalar -h
+'
+
+test_expect_success 'scalar invoked on enlistment root' '
+ test_when_finished rm -rf test src deeper &&
+
+ for enlistment_root in test src deeper/test
+ do
+ git init ${enlistment_root}/src &&
+
+ # Register
+ scalar register ${enlistment_root} &&
+ scalar list >out &&
+ grep "$(pwd)/${enlistment_root}/src\$" out &&
+
+ # Delete (including enlistment root)
+ scalar delete $enlistment_root &&
+ test_path_is_missing $enlistment_root &&
+ scalar list >out &&
+ ! grep "^$(pwd)/${enlistment_root}/src\$" out || return 1
+ done
+'
+
+test_expect_success 'scalar invoked on enlistment src repo' '
+ test_when_finished rm -rf test src deeper &&
+
+ for enlistment_root in test src deeper/test
+ do
+ git init ${enlistment_root}/src &&
+
+ # Register
+ scalar register ${enlistment_root}/src &&
+ scalar list >out &&
+ grep "$(pwd)/${enlistment_root}/src\$" out &&
+
+ # Delete (will not include enlistment root)
+ scalar delete ${enlistment_root}/src &&
+ test_path_is_dir $enlistment_root &&
+ scalar list >out &&
+ ! grep "^$(pwd)/${enlistment_root}/src\$" out || return 1
+ done
+'
+
+test_expect_success 'scalar invoked when enlistment root and repo are the same' '
+ test_when_finished rm -rf test src deeper &&
+
+ for enlistment_root in test src deeper/test
+ do
+ git init ${enlistment_root} &&
+
+ # Register
+ scalar register ${enlistment_root} &&
+ scalar list >out &&
+ grep "$(pwd)/${enlistment_root}\$" out &&
+
+ # Delete (will not include enlistment root)
+ scalar delete ${enlistment_root} &&
+ test_path_is_missing $enlistment_root &&
+ scalar list >out &&
+ ! grep "^$(pwd)/${enlistment_root}\$" out &&
+
+ # Make sure we did not accidentally delete the trash dir
+ test_path_is_dir "$TRASH_DIRECTORY" || return 1
+ done
+'
+
+test_expect_success 'scalar repo search respects GIT_CEILING_DIRECTORIES' '
+ test_when_finished rm -rf test &&
+
+ git init test/src &&
+ mkdir -p test/src/deep &&
+ GIT_CEILING_DIRECTORIES="$(pwd)/test/src" &&
+ ! scalar register test/src/deep 2>err &&
+ grep "not a git repository" err
+'
+
+test_expect_success 'scalar enlistments need a worktree' '
+ test_when_finished rm -rf bare test &&
+
+ git init --bare bare/src &&
+ ! scalar register bare/src 2>err &&
+ grep "Scalar enlistments require a worktree" err &&
+
+ git init test/src &&
+ ! scalar register test/src/.git 2>err &&
+ grep "Scalar enlistments require a worktree" err
+'
+
+test_expect_success FSMONITOR_DAEMON 'scalar register starts fsmon daemon' '
+ git init test/src &&
+ test_must_fail git -C test/src fsmonitor--daemon status &&
+ scalar register test/src &&
+ git -C test/src fsmonitor--daemon status &&
+ test_cmp_config -C test/src true core.fsmonitor
+'
+
+test_expect_success 'scalar unregister' '
+ git init vanish/src &&
+ scalar register vanish/src &&
+ git config --get --global --fixed-value \
+ maintenance.repo "$(pwd)/vanish/src" &&
+ scalar list >scalar.repos &&
+ grep -F "$(pwd)/vanish/src" scalar.repos &&
+ rm -rf vanish/src/.git &&
+ scalar unregister vanish &&
+ test_must_fail git config --get --global --fixed-value \
+ maintenance.repo "$(pwd)/vanish/src" &&
+ scalar list >scalar.repos &&
+ ! grep -F "$(pwd)/vanish/src" scalar.repos &&
+
+ # scalar unregister should be idempotent
+ scalar unregister vanish
+'
+
+test_expect_success 'set up repository to clone' '
+ test_commit first &&
+ test_commit second &&
+ test_commit third &&
+ git switch -c parallel first &&
+ mkdir -p 1/2 &&
+ test_commit 1/2/3 &&
+ git config uploadPack.allowFilter true &&
+ git config uploadPack.allowAnySHA1InWant true
+'
+
+test_expect_success 'scalar clone' '
+ second=$(git rev-parse --verify second:second.t) &&
+ scalar clone "file://$(pwd)" cloned --single-branch &&
+ (
+ cd cloned/src &&
+
+ git config --get --global --fixed-value maintenance.repo \
+ "$(pwd)" &&
+
+ git for-each-ref --format="%(refname)" refs/remotes/origin/ >actual &&
+ echo "refs/remotes/origin/parallel" >expect &&
+ test_cmp expect actual &&
+
+ test_path_is_missing 1/2 &&
+ test_must_fail git rev-list --missing=print $second &&
+ git rev-list $second &&
+ git cat-file blob $second >actual &&
+ echo "second" >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'scalar reconfigure' '
+ git init one/src &&
+ scalar register one &&
+ git -C one/src config core.preloadIndex false &&
+ scalar reconfigure one &&
+ test true = "$(git -C one/src config core.preloadIndex)" &&
+ git -C one/src config core.preloadIndex false &&
+ scalar reconfigure -a &&
+ test true = "$(git -C one/src config core.preloadIndex)"
+'
+
+test_expect_success 'scalar delete without enlistment shows a usage' '
+ test_expect_code 129 scalar delete
+'
+
+test_expect_success 'scalar delete with enlistment' '
+ scalar delete cloned &&
+ test_path_is_missing cloned
+'
+
+test_expect_success 'scalar supports -c/-C' '
+ test_when_finished "scalar delete sub" &&
+ git init sub &&
+ scalar -C sub -c status.aheadBehind=bogus register &&
+ test -z "$(git -C sub config --local status.aheadBehind)" &&
+ test true = "$(git -C sub config core.preloadIndex)"
+'
+
+test_expect_success '`scalar [...] <dir>` errors out when dir is missing' '
+ ! scalar run config cloned 2>err &&
+ grep "cloned. does not exist" err
+'
+
+SQ="'"
+test_expect_success UNZIP 'scalar diagnose' '
+ scalar clone "file://$(pwd)" cloned --single-branch &&
+ git repack &&
+ echo "$(pwd)/.git/objects/" >>cloned/src/.git/objects/info/alternates &&
+ test_commit -C cloned/src loose &&
+ scalar diagnose cloned >out 2>err &&
+ grep "Available space" out &&
+ sed -n "s/.*$SQ\\(.*\\.zip\\)$SQ.*/\\1/p" <err >zip_path &&
+ zip_path=$(cat zip_path) &&
+ test -n "$zip_path" &&
+ "$GIT_UNZIP" -v "$zip_path" &&
+ folder=${zip_path%.zip} &&
+ test_path_is_missing "$folder" &&
+ "$GIT_UNZIP" -p "$zip_path" diagnostics.log >out &&
+ test_file_not_empty out &&
+ "$GIT_UNZIP" -p "$zip_path" packs-local.txt >out &&
+ grep "$(pwd)/.git/objects" out &&
+ "$GIT_UNZIP" -p "$zip_path" objects-local.txt >out &&
+ grep "^Total: [1-9]" out
+'
+
+test_done
diff --git a/t/t9211-scalar-clone.sh b/t/t9211-scalar-clone.sh
new file mode 100755
index 0000000000..dd33d87e9b
--- /dev/null
+++ b/t/t9211-scalar-clone.sh
@@ -0,0 +1,151 @@
+#!/bin/sh
+
+test_description='test the `scalar clone` subcommand'
+
+. ./test-lib.sh
+
+GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt,launchctl:true,schtasks:true"
+export GIT_TEST_MAINT_SCHEDULER
+
+test_expect_success 'set up repository to clone' '
+ rm -rf .git &&
+ git init to-clone &&
+ (
+ cd to-clone &&
+ git branch -m base &&
+
+ test_commit first &&
+ test_commit second &&
+ test_commit third &&
+
+ git switch -c parallel first &&
+ mkdir -p 1/2 &&
+ test_commit 1/2/3 &&
+
+ git switch base &&
+
+ # By default, permit
+ git config uploadpack.allowfilter true &&
+ git config uploadpack.allowanysha1inwant true
+ )
+'
+
+cleanup_clone () {
+ rm -rf "$1"
+}
+
+test_expect_success 'creates content in enlistment root' '
+ enlistment=cloned &&
+
+ scalar clone "file://$(pwd)/to-clone" $enlistment &&
+ ls -A $enlistment >enlistment-root &&
+ test_line_count = 1 enlistment-root &&
+ test_path_is_dir $enlistment/src &&
+ test_path_is_dir $enlistment/src/.git &&
+
+ cleanup_clone $enlistment
+'
+
+test_expect_success 'with spaces' '
+ enlistment="cloned with space" &&
+
+ scalar clone "file://$(pwd)/to-clone" "$enlistment" &&
+ test_path_is_dir "$enlistment" &&
+ test_path_is_dir "$enlistment/src" &&
+ test_path_is_dir "$enlistment/src/.git" &&
+
+ cleanup_clone "$enlistment"
+'
+
+test_expect_success 'partial clone if supported by server' '
+ enlistment=partial-clone &&
+
+ scalar clone "file://$(pwd)/to-clone" $enlistment &&
+
+ (
+ cd $enlistment/src &&
+
+ # Two promisor packs: one for refs, the other for blobs
+ ls .git/objects/pack/pack-*.promisor >promisorlist &&
+ test_line_count = 2 promisorlist
+ ) &&
+
+ cleanup_clone $enlistment
+'
+
+test_expect_success 'fall back on full clone if partial unsupported' '
+ enlistment=no-partial-support &&
+
+ test_config -C to-clone uploadpack.allowfilter false &&
+ test_config -C to-clone uploadpack.allowanysha1inwant false &&
+
+ scalar clone "file://$(pwd)/to-clone" $enlistment 2>err &&
+ grep "filtering not recognized by server, ignoring" err &&
+
+ (
+ cd $enlistment/src &&
+
+ # Still get a refs promisor file, but none for blobs
+ ls .git/objects/pack/pack-*.promisor >promisorlist &&
+ test_line_count = 1 promisorlist
+ ) &&
+
+ cleanup_clone $enlistment
+'
+
+test_expect_success 'initializes sparse-checkout by default' '
+ enlistment=sparse &&
+
+ scalar clone "file://$(pwd)/to-clone" $enlistment &&
+ (
+ cd $enlistment/src &&
+ test_cmp_config true core.sparseCheckout &&
+ test_cmp_config true core.sparseCheckoutCone
+ ) &&
+
+ cleanup_clone $enlistment
+'
+
+test_expect_success '--full-clone does not create sparse-checkout' '
+ enlistment=full-clone &&
+
+ scalar clone --full-clone "file://$(pwd)/to-clone" $enlistment &&
+ (
+ cd $enlistment/src &&
+ test_cmp_config "" --default "" core.sparseCheckout &&
+ test_cmp_config "" --default "" core.sparseCheckoutCone
+ ) &&
+
+ cleanup_clone $enlistment
+'
+
+test_expect_success '--single-branch clones HEAD only' '
+ enlistment=single-branch &&
+
+ scalar clone --single-branch "file://$(pwd)/to-clone" $enlistment &&
+ (
+ cd $enlistment/src &&
+ git for-each-ref refs/remotes/origin >out &&
+ test_line_count = 1 out &&
+ grep "refs/remotes/origin/base" out
+ ) &&
+
+ cleanup_clone $enlistment
+'
+
+test_expect_success '--no-single-branch clones all branches' '
+ enlistment=no-single-branch &&
+
+ scalar clone --no-single-branch "file://$(pwd)/to-clone" $enlistment &&
+ (
+ cd $enlistment/src &&
+ git for-each-ref refs/remotes/origin >out &&
+ test_line_count = 2 out &&
+ grep "refs/remotes/origin/base" out &&
+ grep "refs/remotes/origin/parallel" out
+ ) &&
+
+ cleanup_clone $enlistment
+'
+
+test_done
diff --git a/t/t9304-fast-import-marks.sh b/t/t9304-fast-import-marks.sh
index bed01c99ea..a98ef032d9 100755
--- a/t/t9304-fast-import-marks.sh
+++ b/t/t9304-fast-import-marks.sh
@@ -25,6 +25,7 @@ test_expect_success 'import with large marks file' '
'
test_expect_success 'setup dump with submodule' '
+ test_config_global protocol.file.allow always &&
git submodule add "$PWD" sub &&
git commit -m "add submodule" &&
git fast-export HEAD >dump
diff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh
index fc99703fc5..ff21a12ee6 100755
--- a/t/t9350-fast-export.sh
+++ b/t/t9350-fast-export.sh
@@ -268,6 +268,7 @@ test_expect_success 'signed-tags=warn-strip' '
test_expect_success 'setup submodule' '
+ test_config_global protocol.file.allow always &&
git checkout -f main &&
mkdir sub &&
(
@@ -293,6 +294,7 @@ test_expect_success 'setup submodule' '
test_expect_success 'submodule fast-export | fast-import' '
+ test_config_global protocol.file.allow always &&
SUBENT1=$(git ls-tree main^ sub) &&
SUBENT2=$(git ls-tree main sub) &&
rm -rf new &&
diff --git a/t/t9700-perl-git.sh b/t/t9700-perl-git.sh
index 102c133112..b105d6d9d5 100755
--- a/t/t9700-perl-git.sh
+++ b/t/t9700-perl-git.sh
@@ -4,17 +4,12 @@
#
test_description='perl interface (Git.pm)'
-. ./test-lib.sh
-if ! test_have_prereq PERL; then
- skip_all='skipping perl interface tests, perl not available'
- test_done
-fi
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-perl.sh
-perl -MTest::More -e 0 2>/dev/null || {
- skip_all="Perl Test::More unavailable, skipping test"
- test_done
-}
+skip_all_if_no_Test_More
# set up test repository
@@ -50,11 +45,13 @@ test_expect_success \
git config --add test.pathmulti bar
'
-# The external test will outputs its own plan
-test_external_has_tap=1
+test_expect_success 'set up bare repository' '
+ git init --bare bare.git
+'
-test_external_without_stderr \
- 'Perl API' \
- perl "$TEST_DIRECTORY"/t9700/test.pl
+test_expect_success 'use t9700/test.pl to test Git.pm' '
+ "$PERL_PATH" "$TEST_DIRECTORY"/t9700/test.pl 2>stderr &&
+ test_must_be_empty stderr
+'
test_done
diff --git a/t/t9700/test.pl b/t/t9700/test.pl
index e046f7db76..6d753708d2 100755
--- a/t/t9700/test.pl
+++ b/t/t9700/test.pl
@@ -30,6 +30,18 @@ BEGIN { use_ok('Git') }
# set up
our $abs_repo_dir = cwd();
ok(our $r = Git->repository(Directory => "."), "open repository");
+{
+ local $ENV{GIT_TEST_ASSUME_DIFFERENT_OWNER} = 1;
+ my $failed;
+
+ $failed = eval { Git->repository(Directory => $abs_repo_dir) };
+ ok(!$failed, "reject unsafe non-bare repository");
+ like($@, qr/not a git repository/i, "unsafe error message");
+
+ $failed = eval { Git->repository(Directory => "$abs_repo_dir/bare.git") };
+ ok(!$failed, "reject unsafe bare repository");
+ like($@, qr/not a git repository/i, "unsafe error message");
+}
# config
is($r->config("test.string"), "value", "config scalar: string");
diff --git a/t/t9814-git-p4-rename.sh b/t/t9814-git-p4-rename.sh
index 468767cbf4..2a9838f37f 100755
--- a/t/t9814-git-p4-rename.sh
+++ b/t/t9814-git-p4-rename.sh
@@ -216,7 +216,7 @@ test_expect_success 'detect copies' '
# variable exists, which allows admins to disable the "p4 move" command.
test_lazy_prereq P4D_HAVE_CONFIGURABLE_RUN_MOVE_ALLOW '
p4 configure show run.move.allow >out &&
- egrep ^run.move.allow: out
+ grep -E ^run.move.allow: out
'
# If move can be disabled, turn it off and test p4 move handling
diff --git a/t/t9815-git-p4-submit-fail.sh b/t/t9815-git-p4-submit-fail.sh
index 9779dc0d11..0ca9937de6 100755
--- a/t/t9815-git-p4-submit-fail.sh
+++ b/t/t9815-git-p4-submit-fail.sh
@@ -417,8 +417,8 @@ test_expect_success 'cleanup chmod after submit cancel' '
! p4 fstat -T action text &&
test_path_is_file text+x &&
! p4 fstat -T action text+x &&
- ls -l text | egrep ^-r-- &&
- ls -l text+x | egrep ^-r-x
+ ls -l text | grep -E ^-r-- &&
+ ls -l text+x | grep -E ^-r-x
)
'
diff --git a/t/t9850-shell.sh b/t/t9850-shell.sh
new file mode 100755
index 0000000000..cfc71c3bd4
--- /dev/null
+++ b/t/t9850-shell.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+test_description='git shell tests'
+. ./test-lib.sh
+
+test_expect_success 'shell allows upload-pack' '
+ printf 0000 >input &&
+ git upload-pack . <input >expect &&
+ git shell -c "git-upload-pack $SQ.$SQ" <input >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'shell forbids other commands' '
+ test_must_fail git shell -c "git config foo.bar baz"
+'
+
+test_expect_success 'shell forbids interactive use by default' '
+ test_must_fail git shell
+'
+
+test_expect_success 'shell allows interactive command' '
+ mkdir git-shell-commands &&
+ write_script git-shell-commands/ping <<-\EOF &&
+ echo pong
+ EOF
+ echo pong >expect &&
+ echo ping | git shell >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'shell complains of overlong commands' '
+ perl -e "print \"a\" x 2**12 for (0..2**19)" |
+ test_must_fail git shell 2>err &&
+ grep "too long" err
+'
+
+test_done
diff --git a/t/t9901-git-web--browse.sh b/t/t9901-git-web--browse.sh
index de7152f827..19f56e5680 100755
--- a/t/t9901-git-web--browse.sh
+++ b/t/t9901-git-web--browse.sh
@@ -5,6 +5,7 @@ test_description='git web--browse basic tests
This test checks that git web--browse can handle various valid URLs.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_web_browse () {
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index 6a30f5719c..d459fae655 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -759,4 +759,20 @@ test_expect_success 'prompt - hide if pwd ignored - inside gitdir' '
test_cmp expected "$actual"
'
+test_expect_success 'prompt - conflict indicator' '
+ printf " (main|CONFLICT)" >expected &&
+ echo "stash" >file &&
+ git stash &&
+ test_when_finished "git stash drop" &&
+ echo "commit" >file &&
+ git commit -m "commit" file &&
+ test_when_finished "git reset --hard HEAD~" &&
+ test_must_fail git stash apply &&
+ (
+ GIT_PS1_SHOWCONFLICTSTATE="yes" &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
test_done
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 8c44856eae..29d914a12b 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -273,13 +273,13 @@ debug () {
# <file>, <contents>, and <tag> all default to <message>.
test_commit () {
- notick= &&
- echo=echo &&
- append= &&
- author= &&
- signoff= &&
- indir= &&
- tag=light &&
+ local notick= &&
+ local echo=echo &&
+ local append= &&
+ local author= &&
+ local signoff= &&
+ local indir= &&
+ local tag=light &&
while test $# != 0
do
case "$1" in
@@ -322,7 +322,7 @@ test_commit () {
shift
done &&
indir=${indir:+"$indir"/} &&
- file=${2:-"$1.t"} &&
+ local file=${2:-"$1.t"} &&
if test -n "$append"
then
$echo "${3-$1}" >>"$indir$file"
@@ -633,7 +633,7 @@ test_hook () {
# - Explicitly using test_have_prereq.
#
# - Implicitly by specifying the prerequisite tag in the calls to
-# test_expect_{success,failure} and test_external{,_without_stderr}.
+# test_expect_{success,failure}
#
# The single parameter is the prerequisite tag (a simple word, in all
# capital letters by convention).
@@ -835,93 +835,6 @@ test_expect_success () {
test_finish_
}
-# test_external runs external test scripts that provide continuous
-# test output about their progress, and succeeds/fails on
-# zero/non-zero exit code. It outputs the test output on stdout even
-# in non-verbose mode, and announces the external script with "# run
-# <n>: ..." before running it. When providing relative paths, keep in
-# mind that all scripts run in "trash directory".
-# Usage: test_external description command arguments...
-# Example: test_external 'Perl API' perl ../path/to/test.pl
-test_external () {
- test "$#" = 4 && { test_prereq=$1; shift; } || test_prereq=
- test "$#" = 3 ||
- BUG "not 3 or 4 parameters to test_external"
- descr="$1"
- shift
- test_verify_prereq
- export test_prereq
- if ! test_skip "$descr" "$@"
- then
- # Announce the script to reduce confusion about the
- # test output that follows.
- say_color "" "# run $test_count: $descr ($*)"
- # Export TEST_DIRECTORY, TRASH_DIRECTORY and GIT_TEST_LONG
- # to be able to use them in script
- export TEST_DIRECTORY TRASH_DIRECTORY GIT_TEST_LONG
- # Run command; redirect its stderr to &4 as in
- # test_run_, but keep its stdout on our stdout even in
- # non-verbose mode.
- "$@" 2>&4
- if test "$?" = 0
- then
- if test $test_external_has_tap -eq 0; then
- test_ok_ "$descr"
- else
- say_color "" "# test_external test $descr was ok"
- test_success=$(($test_success + 1))
- fi
- else
- if test $test_external_has_tap -eq 0; then
- test_failure_ "$descr" "$@"
- else
- say_color error "# test_external test $descr failed: $@"
- test_failure=$(($test_failure + 1))
- fi
- fi
- fi
-}
-
-# Like test_external, but in addition tests that the command generated
-# no output on stderr.
-test_external_without_stderr () {
- # The temporary file has no (and must have no) security
- # implications.
- tmp=${TMPDIR:-/tmp}
- stderr="$tmp/git-external-stderr.$$.tmp"
- test_external "$@" 4> "$stderr"
- test -f "$stderr" || error "Internal error: $stderr disappeared."
- descr="no stderr: $1"
- shift
- say >&3 "# expecting no stderr from previous command"
- if test ! -s "$stderr"
- then
- rm "$stderr"
-
- if test $test_external_has_tap -eq 0; then
- test_ok_ "$descr"
- else
- say_color "" "# test_external_without_stderr test $descr was ok"
- test_success=$(($test_success + 1))
- fi
- else
- if test "$verbose" = t
- then
- output=$(echo; echo "# Stderr is:"; cat "$stderr")
- else
- output=
- fi
- # rm first in case test_failure exits.
- rm "$stderr"
- if test $test_external_has_tap -eq 0; then
- test_failure_ "$descr" "$@" "$output"
- else
- say_color error "# test_external_without_stderr test $descr failed: $@: $output"
- test_failure=$(($test_failure + 1))
- fi
- fi
-}
-
# debugging-friendly alternatives to "test [-f|-d|-e]"
# The commands test the existence or non-existence of $1
test_path_is_file () {
@@ -984,7 +897,7 @@ test_path_is_symlink () {
test_dir_is_empty () {
test "$#" -ne 1 && BUG "1 param"
test_path_is_dir "$1" &&
- if test -n "$(ls -a1 "$1" | egrep -v '^\.\.?$')"
+ if test -n "$(ls -a1 "$1" | grep -E -v '^\.\.?$')"
then
echo "Directory '$1' is not empty, it contains:"
ls -la "$1"
@@ -1955,3 +1868,14 @@ test_is_magic_mtime () {
rm -f .git/test-mtime-actual
return $ret
}
+
+# Given two filenames, parse both using 'git config --list --file'
+# and compare the sorted output of those commands. Useful when
+# wanting to ignore whitespace differences and sorting concerns.
+test_cmp_config_output () {
+ git config --list --file="$1" >config-expect &&
+ git config --list --file="$2" >config-actual &&
+ sort config-expect >sorted-expect &&
+ sort config-actual >sorted-actual &&
+ test_cmp sorted-expect sorted-actual
+}
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 7726d1da88..6db377f68b 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -47,6 +47,16 @@ then
echo "PANIC: Running in a $TEST_DIRECTORY that doesn't end in '/t'?" >&2
exit 1
fi
+if test -f "$GIT_BUILD_DIR/GIT-BUILD-DIR"
+then
+ GIT_BUILD_DIR="$(cat "$GIT_BUILD_DIR/GIT-BUILD-DIR")" || exit 1
+ # On Windows, we must convert Windows paths lest they contain a colon
+ case "$(uname -s)" in
+ *MINGW*)
+ GIT_BUILD_DIR="$(cygpath -au "$GIT_BUILD_DIR")"
+ ;;
+ esac
+fi
# Prepend a string to a VAR using an arbitrary ":" delimiter, not
# adding the delimiter if VAR or VALUE is empty. I.e. a generalized:
@@ -238,6 +248,9 @@ parse_option () {
;;
esac
;;
+ --invert-exit-code)
+ invert_exit_code=t
+ ;;
*)
echo "error: unknown test option '$opt'" >&2; exit 1 ;;
esac
@@ -302,6 +315,11 @@ TEST_NUMBER="${TEST_NAME%%-*}"
TEST_NUMBER="${TEST_NUMBER#t}"
TEST_RESULTS_DIR="$TEST_OUTPUT_DIRECTORY/test-results"
TEST_RESULTS_BASE="$TEST_RESULTS_DIR/$TEST_NAME$TEST_STRESS_JOB_SFX"
+TEST_RESULTS_SAN_FILE_PFX=trace
+TEST_RESULTS_SAN_DIR_SFX=leak
+TEST_RESULTS_SAN_FILE=
+TEST_RESULTS_SAN_DIR="$TEST_RESULTS_DIR/$TEST_NAME.$TEST_RESULTS_SAN_DIR_SFX"
+TEST_RESULTS_SAN_DIR_NR_LEAKS_STARTUP=
TRASH_DIRECTORY="trash directory.$TEST_NAME$TEST_STRESS_JOB_SFX"
test -n "$root" && TRASH_DIRECTORY="$root/$TRASH_DIRECTORY"
case "$TRASH_DIRECTORY" in
@@ -309,6 +327,16 @@ case "$TRASH_DIRECTORY" in
*) TRASH_DIRECTORY="$TEST_OUTPUT_DIRECTORY/$TRASH_DIRECTORY" ;;
esac
+# Utility functions using $TEST_RESULTS_* variables
+nr_san_dir_leaks_ () {
+ # stderr piped to /dev/null because the directory may have
+ # been "rmdir"'d already.
+ find "$TEST_RESULTS_SAN_DIR" \
+ -type f \
+ -name "$TEST_RESULTS_SAN_FILE_PFX.*" 2>/dev/null |
+ wc -l
+}
+
# If --stress was passed, run this test repeatedly in several parallel loops.
if test "$GIT_TEST_STRESS_STARTED" = "done"
then
@@ -545,9 +573,11 @@ case $GIT_TEST_FSYNC in
esac
# Add libc MALLOC and MALLOC_PERTURB test only if we are not executing
-# the test with valgrind and have not compiled with SANITIZE=address.
+# the test with valgrind and have not compiled with conflict SANITIZE
+# options.
if test -n "$valgrind" ||
test -n "$SANITIZE_ADDRESS" ||
+ test -n "$SANITIZE_LEAK" ||
test -n "$TEST_NO_MALLOC_CHECK"
then
setup_malloc_check () {
@@ -557,14 +587,19 @@ then
: nothing
}
else
+ _USE_GLIBC_TUNABLES=
+ if _GLIBC_VERSION=$(getconf GNU_LIBC_VERSION 2>/dev/null) &&
+ _GLIBC_VERSION=${_GLIBC_VERSION#"glibc "} &&
+ expr 2.34 \<= "$_GLIBC_VERSION" >/dev/null
+ then
+ _USE_GLIBC_TUNABLES=YesPlease
+ fi
setup_malloc_check () {
local g
local t
MALLOC_CHECK_=3 MALLOC_PERTURB_=165
export MALLOC_CHECK_ MALLOC_PERTURB_
- if _GLIBC_VERSION=$(getconf GNU_LIBC_VERSION 2>/dev/null) &&
- _GLIBC_VERSION=${_GLIBC_VERSION#"glibc "} &&
- expr 2.34 \<= "$_GLIBC_VERSION" >/dev/null
+ if test -n "$_USE_GLIBC_TUNABLES"
then
g=
LD_PRELOAD="libc_malloc_debug.so.0"
@@ -788,15 +823,31 @@ test_ok_ () {
finalize_test_case_output ok "$@"
}
+_invert_exit_code_failure_end_blurb () {
+ say_color warn "# faked up failures as TODO & now exiting with 0 due to --invert-exit-code"
+}
+
test_failure_ () {
failure_label=$1
test_failure=$(($test_failure + 1))
- say_color error "not ok $test_count - $1"
+ local pfx=""
+ if test -n "$invert_exit_code" # && test -n "$HARNESS_ACTIVE"
+ then
+ pfx="# TODO induced breakage (--invert-exit-code):"
+ fi
+ say_color error "not ok $test_count - ${pfx:+$pfx }$1"
shift
printf '%s\n' "$*" | sed -e 's/^/# /'
if test -n "$immediate"
then
say_color error "1..$test_count"
+ if test -n "$invert_exit_code"
+ then
+ finalize_test_output
+ _invert_exit_code_failure_end_blurb
+ GIT_EXIT_OK=t
+ exit 0
+ fi
_error_exit
fi
finalize_test_case_output failure "$failure_label" "$@"
@@ -804,14 +855,14 @@ test_failure_ () {
test_known_broken_ok_ () {
test_fixed=$(($test_fixed+1))
- say_color error "ok $test_count - $@ # TODO known breakage vanished"
- finalize_test_case_output fixed "$@"
+ say_color error "ok $test_count - $1 # TODO known breakage vanished"
+ finalize_test_case_output fixed "$1"
}
test_known_broken_failure_ () {
test_broken=$(($test_broken+1))
- say_color warn "not ok $test_count - $@ # TODO known breakage"
- finalize_test_case_output broken "$@"
+ say_color warn "not ok $test_count - $1 # TODO known breakage"
+ finalize_test_case_output broken "$1"
}
test_debug () {
@@ -1052,11 +1103,7 @@ test_run_ () {
trace=
# 117 is magic because it is unlikely to match the exit
# code of other programs
- if test "OK-117" != "$(test_eval_ "(exit 117) && $1${LF}${LF}echo OK-\$?" 3>&1)" ||
- {
- test "${GIT_TEST_CHAIN_LINT_HARDER:-${GIT_TEST_CHAIN_LINT_HARDER_DEFAULT:-1}}" != 0 &&
- $(printf '%s\n' "$1" | sed -f "$GIT_BUILD_DIR/t/chainlint.sed" | grep -q '?![A-Z][A-Z]*?!')
- }
+ if test "OK-117" != "$(test_eval_ "(exit 117) && $1${LF}${LF}echo OK-\$?" 3>&1)"
then
BUG "broken &&-chain or run-away HERE-DOC: $1"
fi
@@ -1168,9 +1215,67 @@ test_atexit_handler () {
teardown_malloc_check
}
-test_done () {
- GIT_EXIT_OK=t
+sanitize_leak_log_message_ () {
+ local new="$1" &&
+ local old="$2" &&
+ local file="$3" &&
+
+ printf "With SANITIZE=leak at exit we have %d leak logs, but started with %d
+
+This means that we have a blindspot where git is leaking but we're
+losing the exit code somewhere, or not propagating it appropriately
+upwards!
+
+See the logs at \"%s.*\";
+those logs are reproduced below." \
+ "$new" "$old" "$file"
+}
+check_test_results_san_file_ () {
+ if test -z "$TEST_RESULTS_SAN_FILE"
+ then
+ return
+ fi &&
+ local old="$TEST_RESULTS_SAN_DIR_NR_LEAKS_STARTUP" &&
+ local new="$(nr_san_dir_leaks_)" &&
+
+ if test $new -le $old
+ then
+ return
+ fi &&
+ local out="$(sanitize_leak_log_message_ "$new" "$old" "$TEST_RESULTS_SAN_FILE")" &&
+ say_color error "$out" &&
+ if test "$old" != 0
+ then
+ echo &&
+ say_color error "The logs include output from past runs to avoid" &&
+ say_color error "that remove 'test-results' between runs."
+ fi &&
+ say_color error "$(cat "$TEST_RESULTS_SAN_FILE".*)" &&
+
+ if test -n "$passes_sanitize_leak" && test "$test_failure" = 0
+ then
+ say "As TEST_PASSES_SANITIZE_LEAK=true and our logs show we're leaking, exit non-zero!" &&
+ invert_exit_code=t
+ elif test -n "$passes_sanitize_leak"
+ then
+ say "As TEST_PASSES_SANITIZE_LEAK=true and our logs show we're leaking, and we're failing for other reasons too..." &&
+ invert_exit_code=
+ elif test -n "$sanitize_leak_check" && test "$test_failure" = 0
+ then
+ say "As TEST_PASSES_SANITIZE_LEAK=true isn't set the above leak is 'ok' with GIT_TEST_PASSING_SANITIZE_LEAK=check" &&
+ invert_exit_code=
+ elif test -n "$sanitize_leak_check"
+ then
+ say "As TEST_PASSES_SANITIZE_LEAK=true isn't set the above leak is 'ok' with GIT_TEST_PASSING_SANITIZE_LEAK=check" &&
+ invert_exit_code=t
+ else
+ say "With GIT_TEST_SANITIZE_LEAK_LOG=true our logs revealed a memory leak, exit non-zero!" &&
+ invert_exit_code=t
+ fi
+}
+
+test_done () {
# Run the atexit commands _before_ the trash directory is
# removed, so the commands can access pidfiles and socket files.
test_atexit_handler
@@ -1210,28 +1315,32 @@ test_done () {
fi
case "$test_failure" in
0)
- if test $test_external_has_tap -eq 0
+ if test $test_remaining -gt 0
then
- if test $test_remaining -gt 0
- then
- say_color pass "# passed all $msg"
- fi
-
- # Maybe print SKIP message
- test -z "$skip_all" || skip_all="# SKIP $skip_all"
- case "$test_count" in
- 0)
- say "1..$test_count${skip_all:+ $skip_all}"
- ;;
- *)
- test -z "$skip_all" ||
- say_color warn "$skip_all"
- say "1..$test_count"
- ;;
- esac
+ say_color pass "# passed all $msg"
fi
- if test -z "$debug" && test -n "$remove_trash"
+ # Maybe print SKIP message
+ test -z "$skip_all" || skip_all="# SKIP $skip_all"
+ case "$test_count" in
+ 0)
+ say "1..$test_count${skip_all:+ $skip_all}"
+ ;;
+ *)
+ test -z "$skip_all" ||
+ say_color warn "$skip_all"
+ say "1..$test_count"
+ ;;
+ esac
+
+ if test -n "$stress" && test -n "$invert_exit_code"
+ then
+ # We're about to move our "$TRASH_DIRECTORY"
+ # to "$TRASH_DIRECTORY.stress-failed" if
+ # --stress is combined with
+ # --invert-exit-code.
+ say "with --stress and --invert-exit-code we're not removing '$TRASH_DIRECTORY'"
+ elif test -z "$debug" && test -n "$remove_trash"
then
test -d "$TRASH_DIRECTORY" ||
error "Tests passed but trash directory already removed before test cleanup; aborting"
@@ -1244,17 +1353,35 @@ test_done () {
} ||
error "Tests passed but test cleanup failed; aborting"
fi
+
+ check_test_results_san_file_ "$test_failure"
+
+ if test -z "$skip_all" && test -n "$invert_exit_code"
+ then
+ say_color warn "# faking up non-zero exit with --invert-exit-code"
+ GIT_EXIT_OK=t
+ exit 1
+ fi
+
test_at_end_hook_
+ GIT_EXIT_OK=t
exit 0 ;;
*)
- if test $test_external_has_tap -eq 0
+ say_color error "# failed $test_failure among $msg"
+ say "1..$test_count"
+
+ check_test_results_san_file_ "$test_failure"
+
+ if test -n "$invert_exit_code"
then
- say_color error "# failed $test_failure among $msg"
- say "1..$test_count"
+ _invert_exit_code_failure_end_blurb
+ GIT_EXIT_OK=t
+ exit 0
fi
+ GIT_EXIT_OK=t
exit 1 ;;
esac
@@ -1387,14 +1514,12 @@ fi
GITPERLLIB="$GIT_BUILD_DIR"/perl/build/lib
export GITPERLLIB
test -d "$GIT_BUILD_DIR"/templates/blt || {
- error "You haven't built things yet, have you?"
+ BAIL_OUT "You haven't built things yet, have you?"
}
if ! test -x "$GIT_BUILD_DIR"/t/helper/test-tool$X
then
- echo >&2 'You need to build test-tool:'
- echo >&2 'Run "make t/helper/test-tool" in the source (toplevel) directory'
- exit 1
+ BAIL_OUT 'You need to build test-tool; Run "make t/helper/test-tool" in the source (toplevel) directory'
fi
# Are we running this test at all?
@@ -1408,24 +1533,76 @@ then
test_done
fi
-# skip non-whitelisted tests when compiled with SANITIZE=leak
+BAIL_OUT_ENV_NEEDS_SANITIZE_LEAK () {
+ BAIL_OUT "$1 has no effect except when compiled with SANITIZE=leak"
+}
+
if test -n "$SANITIZE_LEAK"
then
- if test_bool_env GIT_TEST_PASSING_SANITIZE_LEAK false
+ # Normalize with test_bool_env
+ passes_sanitize_leak=
+
+ # We need to see TEST_PASSES_SANITIZE_LEAK in "git
+ # env--helper" (via test_bool_env)
+ export TEST_PASSES_SANITIZE_LEAK
+ if test_bool_env TEST_PASSES_SANITIZE_LEAK false
then
- # We need to see it in "git env--helper" (via
- # test_bool_env)
- export TEST_PASSES_SANITIZE_LEAK
+ passes_sanitize_leak=t
+ fi
+
+ if test "$GIT_TEST_PASSING_SANITIZE_LEAK" = "check"
+ then
+ sanitize_leak_check=t
+ if test -n "$invert_exit_code"
+ then
+ BAIL_OUT "cannot use --invert-exit-code under GIT_TEST_PASSING_SANITIZE_LEAK=check"
+ fi
- if ! test_bool_env TEST_PASSES_SANITIZE_LEAK false
+ if test -z "$passes_sanitize_leak"
then
- skip_all="skipping $this_test under GIT_TEST_PASSING_SANITIZE_LEAK=true"
- test_done
+ say "in GIT_TEST_PASSING_SANITIZE_LEAK=check mode, setting --invert-exit-code for TEST_PASSES_SANITIZE_LEAK != true"
+ invert_exit_code=t
fi
+ elif test -z "$passes_sanitize_leak" &&
+ test_bool_env GIT_TEST_PASSING_SANITIZE_LEAK false
+ then
+ skip_all="skipping $this_test under GIT_TEST_PASSING_SANITIZE_LEAK=true"
+ test_done
+ fi
+
+ if test_bool_env GIT_TEST_SANITIZE_LEAK_LOG false
+ then
+ if ! mkdir -p "$TEST_RESULTS_SAN_DIR"
+ then
+ BAIL_OUT "cannot create $TEST_RESULTS_SAN_DIR"
+ fi &&
+ TEST_RESULTS_SAN_FILE="$TEST_RESULTS_SAN_DIR/$TEST_RESULTS_SAN_FILE_PFX"
+
+ # In case "test-results" is left over from a previous
+ # run: Only report if new leaks show up.
+ TEST_RESULTS_SAN_DIR_NR_LEAKS_STARTUP=$(nr_san_dir_leaks_)
+
+ # Don't litter *.leak dirs if there was nothing to report
+ test_atexit "rmdir \"$TEST_RESULTS_SAN_DIR\" 2>/dev/null || :"
+
+ prepend_var LSAN_OPTIONS : dedup_token_length=9999
+ prepend_var LSAN_OPTIONS : log_exe_name=1
+ prepend_var LSAN_OPTIONS : log_path=\"$TEST_RESULTS_SAN_FILE\"
+ export LSAN_OPTIONS
fi
-elif test_bool_env GIT_TEST_PASSING_SANITIZE_LEAK false
+elif test "$GIT_TEST_PASSING_SANITIZE_LEAK" = "check" ||
+ test_bool_env GIT_TEST_PASSING_SANITIZE_LEAK false
+then
+ BAIL_OUT_ENV_NEEDS_SANITIZE_LEAK "GIT_TEST_PASSING_SANITIZE_LEAK=true"
+elif test_bool_env GIT_TEST_SANITIZE_LEAK_LOG false
then
- BAIL_OUT "GIT_TEST_PASSING_SANITIZE_LEAK=true has no effect except when compiled with SANITIZE=leak"
+ BAIL_OUT_ENV_NEEDS_SANITIZE_LEAK "GIT_TEST_SANITIZE_LEAK_LOG=true"
+fi
+
+if test "${GIT_TEST_CHAIN_LINT:-1}" != 0
+then
+ "$PERL_PATH" "$TEST_DIRECTORY/chainlint.pl" "$0" ||
+ BUG "lint error (see '?!...!? annotations above)"
fi
# Last-minute variable setup
@@ -1448,9 +1625,7 @@ remove_trash_directory () {
# Test repository
remove_trash_directory "$TRASH_DIRECTORY" || {
- GIT_EXIT_OK=t
- echo >&5 "FATAL: Cannot prepare test area"
- exit 1
+ BAIL_OUT 'cannot prepare test area'
}
remove_trash=t
@@ -1466,7 +1641,7 @@ fi
# Use -P to resolve symlinks in our working directory so that the cwd
# in subprocesses like git equals our $PWD (for pathname comparisons).
-cd -P "$TRASH_DIRECTORY" || exit 1
+cd -P "$TRASH_DIRECTORY" || BAIL_OUT "cannot cd -P to \"$TRASH_DIRECTORY\""
start_test_output "$0"