summaryrefslogtreecommitdiff
path: root/t
diff options
context:
space:
mode:
Diffstat (limited to 't')
-rw-r--r--t/.gitattributes2
-rw-r--r--t/.gitignore1
-rw-r--r--t/Makefile25
-rw-r--r--t/README12
-rw-r--r--t/annotate-tests.sh4
-rw-r--r--t/chainlint.sed369
-rw-r--r--t/chainlint/arithmetic-expansion.expect9
-rw-r--r--t/chainlint/arithmetic-expansion.test11
-rw-r--r--t/chainlint/bash-array.expect10
-rw-r--r--t/chainlint/bash-array.test12
-rw-r--r--t/chainlint/blank-line.expect4
-rw-r--r--t/chainlint/blank-line.test10
-rw-r--r--t/chainlint/block.expect12
-rw-r--r--t/chainlint/block.test15
-rw-r--r--t/chainlint/broken-chain.expect6
-rw-r--r--t/chainlint/broken-chain.test8
-rw-r--r--t/chainlint/case.expect19
-rw-r--r--t/chainlint/case.test23
-rw-r--r--t/chainlint/close-nested-and-parent-together.expect4
-rw-r--r--t/chainlint/close-nested-and-parent-together.test3
-rw-r--r--t/chainlint/close-subshell.expect25
-rw-r--r--t/chainlint/close-subshell.test27
-rw-r--r--t/chainlint/command-substitution.expect9
-rw-r--r--t/chainlint/command-substitution.test11
-rw-r--r--t/chainlint/comment.expect4
-rw-r--r--t/chainlint/comment.test11
-rw-r--r--t/chainlint/complex-if-in-cuddled-loop.expect10
-rw-r--r--t/chainlint/complex-if-in-cuddled-loop.test11
-rw-r--r--t/chainlint/cuddled-if-then-else.expect7
-rw-r--r--t/chainlint/cuddled-if-then-else.test7
-rw-r--r--t/chainlint/cuddled-loop.expect5
-rw-r--r--t/chainlint/cuddled-loop.test7
-rw-r--r--t/chainlint/cuddled.expect21
-rw-r--r--t/chainlint/cuddled.test23
-rw-r--r--t/chainlint/exit-loop.expect24
-rw-r--r--t/chainlint/exit-loop.test27
-rw-r--r--t/chainlint/exit-subshell.expect5
-rw-r--r--t/chainlint/exit-subshell.test6
-rw-r--r--t/chainlint/for-loop.expect11
-rw-r--r--t/chainlint/for-loop.test19
-rw-r--r--t/chainlint/here-doc-close-subshell.expect2
-rw-r--r--t/chainlint/here-doc-close-subshell.test5
-rw-r--r--t/chainlint/here-doc-multi-line-command-subst.expect5
-rw-r--r--t/chainlint/here-doc-multi-line-command-subst.test9
-rw-r--r--t/chainlint/here-doc-multi-line-string.expect4
-rw-r--r--t/chainlint/here-doc-multi-line-string.test8
-rw-r--r--t/chainlint/here-doc.expect9
-rw-r--r--t/chainlint/here-doc.test37
-rw-r--r--t/chainlint/if-in-loop.expect12
-rw-r--r--t/chainlint/if-in-loop.test15
-rw-r--r--t/chainlint/if-then-else.expect19
-rw-r--r--t/chainlint/if-then-else.test28
-rw-r--r--t/chainlint/incomplete-line.expect4
-rw-r--r--t/chainlint/incomplete-line.test12
-rw-r--r--t/chainlint/inline-comment.expect9
-rw-r--r--t/chainlint/inline-comment.test12
-rw-r--r--t/chainlint/loop-in-if.expect12
-rw-r--r--t/chainlint/loop-in-if.test15
-rw-r--r--t/chainlint/multi-line-nested-command-substitution.expect18
-rw-r--r--t/chainlint/multi-line-nested-command-substitution.test18
-rw-r--r--t/chainlint/multi-line-string.expect15
-rw-r--r--t/chainlint/multi-line-string.test27
-rw-r--r--t/chainlint/negated-one-liner.expect5
-rw-r--r--t/chainlint/negated-one-liner.test7
-rw-r--r--t/chainlint/nested-cuddled-subshell.expect19
-rw-r--r--t/chainlint/nested-cuddled-subshell.test31
-rw-r--r--t/chainlint/nested-here-doc.expect7
-rw-r--r--t/chainlint/nested-here-doc.test33
-rw-r--r--t/chainlint/nested-subshell-comment.expect11
-rw-r--r--t/chainlint/nested-subshell-comment.test13
-rw-r--r--t/chainlint/nested-subshell.expect12
-rw-r--r--t/chainlint/nested-subshell.test14
-rw-r--r--t/chainlint/one-liner.expect9
-rw-r--r--t/chainlint/one-liner.test12
-rw-r--r--t/chainlint/p4-filespec.expect4
-rw-r--r--t/chainlint/p4-filespec.test5
-rw-r--r--t/chainlint/pipe.expect8
-rw-r--r--t/chainlint/pipe.test12
-rw-r--r--t/chainlint/semicolon.expect20
-rw-r--r--t/chainlint/semicolon.test25
-rw-r--r--t/chainlint/subshell-here-doc.expect11
-rw-r--r--t/chainlint/subshell-here-doc.test39
-rw-r--r--t/chainlint/subshell-one-liner.expect14
-rw-r--r--t/chainlint/subshell-one-liner.test24
-rw-r--r--t/chainlint/t7900-subtree.expect10
-rw-r--r--t/chainlint/t7900-subtree.test22
-rw-r--r--t/chainlint/while-loop.expect11
-rw-r--r--t/chainlint/while-loop.test19
-rwxr-xr-xt/check-non-portable-shell.pl3
-rw-r--r--t/helper/test-delta.c8
-rw-r--r--t/helper/test-drop-caches.c4
-rw-r--r--t/helper/test-dump-cache-tree.c2
-rw-r--r--t/helper/test-json-writer.c565
-rw-r--r--t/helper/test-reach.c130
-rw-r--r--t/helper/test-read-midx.c51
-rw-r--r--t/helper/test-repository.c82
-rw-r--r--t/helper/test-tool.c4
-rw-r--r--t/helper/test-tool.h6
-rwxr-xr-xt/lib-gpg.sh28
-rw-r--r--t/lib-gpg/gpgsm-gen-key.in8
-rw-r--r--t/lib-gpg/gpgsm_cert.p12bin0 -> 2652 bytes
-rw-r--r--t/lib-httpd.sh21
-rw-r--r--t/lib-rebase.sh6
-rwxr-xr-xt/lib-submodule-update.sh10
-rw-r--r--t/perf/README25
-rwxr-xr-xt/perf/aggregate.perl69
-rwxr-xr-xt/perf/p5311-pack-bitmaps-fetch.sh45
-rw-r--r--t/perf/perf-lib.sh74
-rwxr-xr-xt/t0000-basic.sh4
-rwxr-xr-xt/t0001-init.sh9
-rwxr-xr-xt/t0003-attributes.sh27
-rwxr-xr-xt/t0008-ignores.sh9
-rwxr-xr-xt/t0019-json-writer.sh331
-rw-r--r--t/t0019/parse_json.perl55
-rwxr-xr-xt/t0020-crlf.sh2
-rwxr-xr-xt/t0021-conversion.sh6
-rwxr-xr-xt/t0028-working-tree-encoding.sh6
-rwxr-xr-xt/t0030-stripspace.sh39
-rwxr-xr-xt/t0040-parse-options.sh4
-rwxr-xr-xt/t0060-path-utils.sh13
-rwxr-xr-xt/t0061-run-command.sh9
-rwxr-xr-xt/t0090-cache-tree.sh4
-rwxr-xr-xt/t0203-gettext-setlocale-sanity.sh4
-rwxr-xr-xt/t0300-credentials.sh3
-rwxr-xr-xt/t0410-partial-clone.sh87
-rwxr-xr-xt/t1004-read-tree-m-u-wf.sh8
-rwxr-xr-xt/t1005-read-tree-reset.sh10
-rwxr-xr-xt/t1006-cat-file.sh17
-rwxr-xr-xt/t1008-read-tree-overlay.sh2
-rwxr-xr-xt/t1011-read-tree-sparse-checkout.sh3
-rwxr-xr-xt/t1015-read-index-unmerged.sh123
-rwxr-xr-xt/t1020-subdirectory.sh2
-rwxr-xr-xt/t1050-large.sh6
-rwxr-xr-xt/t1090-sparse-checkout-scope.sh14
-rwxr-xr-xt/t1300-config.sh94
-rwxr-xr-xt/t1305-config-include.sh2
-rwxr-xr-xt/t1306-xdg-files.sh6
-rwxr-xr-xt/t1308-config-set.sh2
-rwxr-xr-xt/t1400-update-ref.sh20
-rwxr-xr-xt/t1403-show-ref.sh46
-rwxr-xr-xt/t1404-update-ref-errors.sh10
-rwxr-xr-xt/t1410-reflog.sh3
-rwxr-xr-xt/t1411-reflog-show.sh9
-rwxr-xr-xt/t1450-fsck.sh13
-rwxr-xr-xt/t1501-work-tree.sh2
-rwxr-xr-xt/t1507-rev-parse-upstream.sh9
-rwxr-xr-xt/t1510-repo-setup.sh24
-rwxr-xr-xt/t1512-rev-parse-disambiguation.sh6
-rwxr-xr-xt/t1600-index.sh3
-rwxr-xr-xt/t1700-split-index.sh6
-rwxr-xr-xt/t2013-checkout-submodule.sh6
-rwxr-xr-xt/t2016-checkout-patch.sh24
-rwxr-xr-xt/t2024-checkout-dwim.sh63
-rwxr-xr-xt/t2025-worktree-add.sh43
-rwxr-xr-xt/t2028-worktree-move.sh44
-rwxr-xr-xt/t2103-update-index-ignore-missing.sh2
-rwxr-xr-xt/t2200-add-update.sh3
-rwxr-xr-xt/t2202-add-addremove.sh17
-rwxr-xr-xt/t2203-add-intent.sh6
-rwxr-xr-xt/t2204-add-ignored.sh8
-rwxr-xr-xt/t3000-ls-files-others.sh2
-rwxr-xr-xt/t3001-ls-files-others-exclude.sh21
-rwxr-xr-xt/t3004-ls-files-basic.sh6
-rwxr-xr-xt/t3005-ls-files-relative.sh8
-rwxr-xr-xt/t3006-ls-files-long.sh2
-rwxr-xr-xt/t3008-ls-files-lazy-init-name-hash.sh8
-rwxr-xr-xt/t3030-merge-recursive.sh340
-rwxr-xr-xt/t3031-merge-criscross.sh2
-rwxr-xr-xt/t3035-merge-sparse.sh5
-rwxr-xr-xt/t3050-subprojects-fetch.sh8
-rwxr-xr-xt/t3070-wildmatch.sh3
-rwxr-xr-xt/t3102-ls-tree-wildcards.sh2
-rwxr-xr-xt/t3200-branch.sh46
-rwxr-xr-xt/t3201-branch-contains.sh15
-rwxr-xr-xt/t3206-range-diff.sh196
-rw-r--r--t/t3206/history.export604
-rwxr-xr-xt/t3210-pack-refs.sh12
-rwxr-xr-xt/t3301-notes.sh12
-rwxr-xr-xt/t3308-notes-merge.sh2
-rwxr-xr-xt/t3310-notes-merge-manual-resolve.sh14
-rwxr-xr-xt/t3400-rebase.sh8
-rwxr-xr-xt/t3401-rebase-and-am-rename.sh110
-rwxr-xr-xt/t3402-rebase-merge.sh4
-rwxr-xr-xt/t3404-rebase-interactive.sh42
-rwxr-xr-xt/t3418-rebase-continue.sh8
-rwxr-xr-xt/t3420-rebase-autostash.sh8
-rwxr-xr-xt/t3430-rebase-merges.sh81
-rwxr-xr-xt/t3507-cherry-pick-conflict.sh13
-rwxr-xr-xt/t3510-cherry-pick-sequence.sh7
-rwxr-xr-xt/t3600-rm.sh14
-rwxr-xr-xt/t3700-add.sh11
-rwxr-xr-xt/t3701-add-interactive.sh16
-rwxr-xr-xt/t3903-stash.sh2
-rwxr-xr-xt/t3904-stash-patch.sh8
-rwxr-xr-xt/t3910-mac-os-precompose.sh3
-rwxr-xr-xt/t4001-diff-rename.sh2
-rwxr-xr-xt/t4010-diff-pathspec.sh7
-rwxr-xr-xt/t4011-diff-symlink.sh2
-rwxr-xr-xt/t4012-diff-binary.sh6
-rwxr-xr-xt/t4013-diff-various.sh2
-rwxr-xr-xt/t4014-format-patch.sh34
-rwxr-xr-xt/t4015-diff-whitespace.sh264
-rwxr-xr-xt/t4019-diff-wserror.sh2
-rwxr-xr-xt/t4024-diff-optimize-common.sh16
-rwxr-xr-xt/t4025-hunk-header.sh8
-rwxr-xr-xt/t4027-diff-submodule.sh29
-rwxr-xr-xt/t4039-diff-assume-unchanged.sh3
-rwxr-xr-xt/t4041-diff-submodule-option.sh22
-rwxr-xr-xt/t4047-diff-dirstat.sh4
-rwxr-xr-xt/t4051-diff-function-context.sh2
-rwxr-xr-xt/t4060-diff-submodule-option-diff-format.sh20
-rwxr-xr-xt/t4116-apply-reverse.sh2
-rwxr-xr-xt/t4121-apply-diffs.sh2
-rwxr-xr-xt/t4124-apply-ws-rule.sh2
-rwxr-xr-xt/t4132-apply-removal.sh5
-rwxr-xr-xt/t4135-apply-weird-filenames.sh10
-rwxr-xr-xt/t4150-am.sh43
-rwxr-xr-xt/t4200-rerere.sh74
-rwxr-xr-xt/t4201-shortlog.sh2
-rwxr-xr-xt/t4202-log.sh43
-rwxr-xr-xt/t4203-mailmap.sh4
-rwxr-xr-xt/t4205-log-pretty-formats.sh23
-rwxr-xr-xt/t4210-log-i18n.sh6
-rwxr-xr-xt/t4211-line-log.sh7
-rwxr-xr-xt/t4212-log-corrupt.sh6
-rwxr-xr-xt/t4256-am-format-flowed.sh19
-rw-r--r--t/t4256/1/mailinfo.c1245
-rw-r--r--t/t4256/1/mailinfo.c.orig1185
-rw-r--r--t/t4256/1/patch129
-rwxr-xr-xt/t4300-merge-tree.sh34
-rwxr-xr-xt/t5300-pack-object.sh2
-rwxr-xr-xt/t5302-pack-index.sh2
-rwxr-xr-xt/t5303-pack-corruption-resilience.sh89
-rwxr-xr-xt/t5304-prune.sh3
-rwxr-xr-xt/t5307-pack-missing-commit.sh4
-rwxr-xr-xt/t5310-pack-bitmaps.sh111
-rwxr-xr-xt/t5313-pack-bounds-checks.sh3
-rwxr-xr-xt/t5314-pack-cycle-detection.sh3
-rwxr-xr-xt/t5318-commit-graph.sh244
-rwxr-xr-xt/t5319-multi-pack-index.sh217
-rwxr-xr-xt/t5320-delta-islands.sh143
-rwxr-xr-xt/t5400-send-pack.sh4
-rwxr-xr-xt/t5401-update-hooks.sh14
-rwxr-xr-xt/t5405-send-pack-rewind.sh3
-rwxr-xr-xt/t5406-remote-rejects.sh5
-rwxr-xr-xt/t5409-colorize-remote-messages.sh101
-rwxr-xr-xt/t5500-fetch-pack.sh39
-rwxr-xr-xt/t5504-fetch-receive-strict.sh126
-rwxr-xr-xt/t5505-remote.sh18
-rwxr-xr-xt/t5509-fetch-push-namespaces.sh2
-rwxr-xr-xt/t5510-fetch.sh115
-rwxr-xr-xt/t5512-ls-remote.sh10
-rwxr-xr-xt/t5514-fetch-multiple.sh3
-rwxr-xr-xt/t5516-fetch-push.sh10
-rwxr-xr-xt/t5517-push-mirror.sh10
-rwxr-xr-xt/t5520-pull.sh24
-rwxr-xr-xt/t5523-push-upstream.sh2
-rwxr-xr-xt/t5526-fetch-submodules.sh47
-rwxr-xr-xt/t5531-deep-submodule-push.sh2
-rwxr-xr-xt/t5533-push-cas.sh6
-rwxr-xr-xt/t5534-push-signed.sh63
-rwxr-xr-xt/t5541-http-push-smart.sh30
-rwxr-xr-xt/t5543-atomic-push.sh2
-rwxr-xr-xt/t5551-http-fetch-smart.sh26
-rwxr-xr-xt/t5552-skipping-fetch-negotiator.sh215
-rwxr-xr-xt/t5561-http-backend.sh8
-rwxr-xr-xt/t5562-http-backend-content-length.sh167
-rwxr-xr-xt/t5562/invoke-with-content-length.pl37
-rwxr-xr-xt/t5570-git-daemon.sh8
-rwxr-xr-xt/t5601-clone.sh10
-rwxr-xr-xt/t5605-clone-local.sh2
-rwxr-xr-xt/t5608-clone-2gb.sh2
-rwxr-xr-xt/t5612-clone-refspec.sh9
-rwxr-xr-xt/t5616-partial-clone.sh44
-rwxr-xr-xt/t5702-protocol-v2.sh78
-rwxr-xr-xt/t5703-upload-pack-ref-in-want.sh4
-rwxr-xr-xt/t5801-remote-helpers.sh10
-rwxr-xr-xt/t6000-rev-list-misc.sh3
-rwxr-xr-xt/t6009-rev-list-parent.sh6
-rwxr-xr-xt/t6010-merge-base.sh2
-rwxr-xr-xt/t6011-rev-list-with-bad-commit.sh7
-rwxr-xr-xt/t6018-rev-list-glob.sh16
-rwxr-xr-xt/t6019-rev-list-ancestry-path.sh3
-rwxr-xr-xt/t6020-merge-df.sh3
-rwxr-xr-xt/t6022-merge-rename.sh3
-rwxr-xr-xt/t6024-recursive-merge.sh6
-rwxr-xr-xt/t6029-merge-subtree.sh44
-rwxr-xr-xt/t6036-recursive-corner-cases.sh713
-rwxr-xr-xt/t6042-merge-rename-corner-cases.sh254
-rwxr-xr-xt/t6043-merge-rename-directories.sh2
-rwxr-xr-xt/t6044-merge-unrelated-index-changes.sh67
-rwxr-xr-xt/t6050-replace.sh6
-rwxr-xr-xt/t6060-merge-index.sh3
-rwxr-xr-xt/t6112-rev-list-filters-objects.sh3
-rwxr-xr-xt/t6120-describe.sh3
-rwxr-xr-xt/t6130-pathspec-noglob.sh9
-rwxr-xr-xt/t6200-fmt-merge-msg.sh4
-rwxr-xr-xt/t6300-for-each-ref.sh26
-rwxr-xr-xt/t6600-test-reach.sh242
-rwxr-xr-xt/t7001-mv.sh8
-rwxr-xr-xt/t7004-tag.sh43
-rwxr-xr-xt/t7006-pager.sh3
-rwxr-xr-xt/t7008-grep-binary.sh6
-rwxr-xr-xt/t7030-verify-tag.sh37
-rwxr-xr-xt/t7063-status-untracked-cache.sh5
-rwxr-xr-xt/t7064-wtstatus-pv2.sh5
-rwxr-xr-xt/t7102-reset.sh6
-rwxr-xr-xt/t7105-reset-patch.sh12
-rwxr-xr-xt/t7106-reset-unborn-branch.sh9
-rwxr-xr-xt/t7201-co.sh48
-rwxr-xr-xt/t7301-clean-interactive.sh41
-rwxr-xr-xt/t7400-submodule-basic.sh48
-rwxr-xr-xt/t7401-submodule-summary.sh5
-rwxr-xr-xt/t7405-submodule-merge.sh173
-rwxr-xr-xt/t7406-submodule-update.sh54
-rwxr-xr-xt/t7408-submodule-reference.sh2
-rwxr-xr-xt/t7410-submodule-checkout-to.sh99
-rwxr-xr-xt/t7415-submodule-names.sh15
-rwxr-xr-xt/t7501-commit.sh80
-rwxr-xr-xt/t7502-commit.sh3
-rwxr-xr-xt/t7504-commit-msg-hook.sh4
-rwxr-xr-xt/t7506-status-submodule.sh10
-rwxr-xr-xt/t7510-signed-commit.sh7
-rwxr-xr-xt/t7513-interpret-trailers.sh42
-rwxr-xr-xt/t7600-merge.sh9
-rwxr-xr-xt/t7610-mergetool.sh14
-rwxr-xr-xt/t7611-merge-abort.sh118
-rwxr-xr-xt/t7810-grep.sh58
-rwxr-xr-xt/t7811-grep-open.sh18
-rwxr-xr-xt/t8003-blame-corner-cases.sh12
-rwxr-xr-xt/t8010-cat-file-filters.sh2
-rwxr-xr-xt/t9001-send-email.sh13
-rwxr-xr-xt/t9011-svn-da.sh14
-rwxr-xr-xt/t9100-git-svn-basic.sh2
-rwxr-xr-xt/t9101-git-svn-props.sh2
-rwxr-xr-xt/t9119-git-svn-info.sh120
-rwxr-xr-xt/t9122-git-svn-author.sh6
-rwxr-xr-xt/t9129-git-svn-i18n-commitencoding.sh2
-rwxr-xr-xt/t9130-git-svn-authors-file.sh4
-rwxr-xr-xt/t9131-git-svn-empty-symlink.sh6
-rwxr-xr-xt/t9134-git-svn-ignore-paths.sh6
-rwxr-xr-xt/t9135-git-svn-moved-branch-empty-file.sh3
-rwxr-xr-xt/t9137-git-svn-dcommit-clobber-series.sh2
-rwxr-xr-xt/t9138-git-svn-authors-prog.sh6
-rwxr-xr-xt/t9146-git-svn-empty-dirs.sh20
-rwxr-xr-xt/t9147-git-svn-include-paths.sh6
-rwxr-xr-xt/t9152-svn-empty-dirs-after-gc.sh2
-rwxr-xr-xt/t9164-git-svn-dcommit-concurrent.sh2
-rwxr-xr-xt/t9165-git-svn-fetch-merge-branch-of-branch.sh2
-rwxr-xr-xt/t9200-git-cvsexportcommit.sh10
-rwxr-xr-xt/t9300-fast-import.sh12
-rwxr-xr-xt/t9302-fast-import-unpack-limit.sh2
-rwxr-xr-xt/t9400-git-cvsserver-server.sh8
-rwxr-xr-xt/t9600-cvsimport.sh2
-rwxr-xr-xt/t9800-git-p4-basic.sh29
-rwxr-xr-xt/t9802-git-p4-filetype.sh2
-rwxr-xr-xt/t9806-git-p4-options.sh2
-rwxr-xr-xt/t9810-git-p4-rcs.sh2
-rwxr-xr-xt/t9811-git-p4-label-import.sh2
-rwxr-xr-xt/t9814-git-p4-rename.sh18
-rwxr-xr-xt/t9815-git-p4-submit-fail.sh2
-rwxr-xr-xt/t9830-git-p4-symlink-dir.sh2
-rwxr-xr-xt/t9831-git-p4-triggers.sh2
-rwxr-xr-xt/t9902-completion.sh10
-rwxr-xr-xt/t9903-bash-prompt.sh21
-rw-r--r--t/test-lib-functions.sh10
-rw-r--r--t/test-lib.sh27
367 files changed, 12038 insertions, 1678 deletions
diff --git a/t/.gitattributes b/t/.gitattributes
index 3bd959ae52..e7acedabe1 100644
--- a/t/.gitattributes
+++ b/t/.gitattributes
@@ -1,6 +1,8 @@
t[0-9][0-9][0-9][0-9]/* -whitespace
+/chainlint/*.expect eol=lf
/diff-lib/* eol=lf
/t0110/url-* binary
+/t3206/* eol=lf
/t3900/*.txt eol=lf
/t3901/*.txt eol=lf
/t4034/*/* eol=lf
diff --git a/t/.gitignore b/t/.gitignore
index 4e731dc1e3..348715f0e4 100644
--- a/t/.gitignore
+++ b/t/.gitignore
@@ -1,3 +1,4 @@
/trash directory*
/test-results
/.prove
+/chainlinttmp
diff --git a/t/Makefile b/t/Makefile
index 96317a35f4..c83fd18861 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -18,8 +18,10 @@ TEST_LINT ?= test-lint
ifdef TEST_OUTPUT_DIRECTORY
TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results
+CHAINLINTTMP = $(TEST_OUTPUT_DIRECTORY)/chainlinttmp
else
TEST_RESULTS_DIRECTORY = test-results
+CHAINLINTTMP = chainlinttmp
endif
# Shell quote;
@@ -27,14 +29,17 @@ SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
TEST_SHELL_PATH_SQ = $(subst ','\'',$(TEST_SHELL_PATH))
PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
TEST_RESULTS_DIRECTORY_SQ = $(subst ','\'',$(TEST_RESULTS_DIRECTORY))
+CHAINLINTTMP_SQ = $(subst ','\'',$(CHAINLINTTMP))
T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))
TGITWEB = $(sort $(wildcard t95[0-9][0-9]-*.sh))
THELPERS = $(sort $(filter-out $(T),$(wildcard *.sh)))
+CHAINLINTTESTS = $(sort $(patsubst chainlint/%.test,%,$(wildcard chainlint/*.test)))
+CHAINLINT = sed -f chainlint.sed
all: $(DEFAULT_TEST_TARGET)
-test: pre-clean $(TEST_LINT)
+test: pre-clean check-chainlint $(TEST_LINT)
$(MAKE) aggregate-results-and-cleanup
failed:
@@ -43,7 +48,7 @@ failed:
sed -n 's/\.counts$$/.sh/p') && \
test -z "$$failed" || $(MAKE) $$failed
-prove: pre-clean $(TEST_LINT)
+prove: pre-clean check-chainlint $(TEST_LINT)
@echo "*** prove ***"; $(PROVE) --exec '$(TEST_SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)
$(MAKE) clean-except-prove-cache
@@ -53,13 +58,25 @@ $(T):
pre-clean:
$(RM) -r '$(TEST_RESULTS_DIRECTORY_SQ)'
-clean-except-prove-cache:
+clean-except-prove-cache: clean-chainlint
$(RM) -r 'trash directory'.* '$(TEST_RESULTS_DIRECTORY_SQ)'
$(RM) -r valgrind/bin
clean: clean-except-prove-cache
$(RM) .prove
+clean-chainlint:
+ $(RM) -r '$(CHAINLINTTMP_SQ)'
+
+check-chainlint:
+ @mkdir -p '$(CHAINLINTTMP_SQ)' && \
+ err=0 && \
+ for i in $(CHAINLINTTESTS); do \
+ $(CHAINLINT) <chainlint/$$i.test | \
+ sed -e '/^# LINT: /d' >'$(CHAINLINTTMP_SQ)'/$$i.actual && \
+ diff -u chainlint/$$i.expect '$(CHAINLINTTMP_SQ)'/$$i.actual || err=1; \
+ done && exit $$err
+
test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax \
test-lint-filenames
@@ -102,4 +119,4 @@ valgrind:
perf:
$(MAKE) -C perf/ all
-.PHONY: pre-clean $(T) aggregate-results clean valgrind perf
+.PHONY: pre-clean $(T) aggregate-results clean valgrind perf check-chainlint clean-chainlint
diff --git a/t/README b/t/README
index 8373a27fea..3ea6c85460 100644
--- a/t/README
+++ b/t/README
@@ -315,6 +315,18 @@ packs on demand. This normally only happens when the object size is
over 2GB. This variable forces the code path on any object larger than
<n> bytes.
+GIT_TEST_OE_DELTA_SIZE=<n> exercises the uncomon pack-objects code
+path where deltas larger than this limit require extra memory
+allocation for bookkeeping.
+
+GIT_TEST_VALIDATE_INDEX_CACHE_ENTRIES=<boolean> checks that cache-tree
+records are valid when the index is written out or after a merge. This
+is mostly to catch missing invalidation. Default is true.
+
+GIT_TEST_COMMIT_GRAPH=<boolean>, when true, forces the commit-graph to
+be written after every 'git commit' command, and overrides the
+'core.commitGraph' setting to true.
+
Naming Tests
------------
diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh
index 093832fef1..6da48a2e0a 100644
--- a/t/annotate-tests.sh
+++ b/t/annotate-tests.sh
@@ -320,11 +320,11 @@ test_expect_success 'blame -L ,Y (Y == nlines)' '
test_expect_success 'blame -L ,Y (Y == nlines + 1)' '
n=$(expr $(wc -l <file) + 2) &&
- test_must_fail $PROG -L,$n file
+ check_count -L,$n A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1
'
test_expect_success 'blame -L ,Y (Y > nlines)' '
- test_must_fail $PROG -L,12345 file
+ check_count -L,12345 A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1
'
test_expect_success 'blame -L multiple (disjoint)' '
diff --git a/t/chainlint.sed b/t/chainlint.sed
new file mode 100644
index 0000000000..70df40e34b
--- /dev/null
+++ b/t/chainlint.sed
@@ -0,0 +1,369 @@
+#------------------------------------------------------------------------------
+# 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?!", and lines which chain
+# commands with ";" internally rather than "&&" are flagged "?!SEMI?!". 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 "&&", so "?!SEMI?!" is never removed
+# from a line (even though "?!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 >out" is seen, the here-doc tag is moved to the front
+# of the line enclosed in angle brackets as a sentinel, giving "<EOF>cat >out".
+# 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 >out\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 >out\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 >out".
+#
+# To facilitate regression testing (and manual debugging), a ">" annotation is
+# applied to the line containing ")" which closes a subshell, ">>" to a line
+# closing a nested subshell, and ">>>" to a line closing both at once. This
+# makes it easy to detect whether the heuristics correctly identify
+# end-of-subshell.
+#------------------------------------------------------------------------------
+
+# 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_]/ {
+ s/^\(.*\)<<[ ]*[-\\'"]*\([A-Za-z0-9_][A-Za-z0-9_]*\)['"]*/<\2>\1<</
+ s/[ ]*<<//
+ :hered
+ N
+ /^<\([^>]*\)>.*\n[ ]*\1[ ]*$/!{
+ s/\n.*$//
+ bhered
+ }
+ s/^<[^>]*>//
+ s/\n.*$//
+}
+
+# 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...)"
+/^[ ]*(/bsubshell
+
+# 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/^/?!SEMI?!/
+}
+b
+
+:subshell
+# bare "(" line? -- stash for later printing
+/^[ ]*([ ]*$/ {
+ h
+ bnextline
+}
+# "(..." line -- split off and stash "(", then process "..." as its own line
+x
+s/.*/(/
+x
+s/(//
+bslurp
+
+:nextline
+N
+s/.*\n//
+
+:slurp
+# incomplete line "...\"
+/\\$/bicmplte
+# multi-line quoted string "...\n..."?
+/"/bdqstring
+# multi-line quoted string '...\n...'? (but not contraction in string "it's")
+/'/{
+ /"[^'"]*'[^'"]*"/!bsqstring
+}
+:folded
+# here-doc -- swallow it
+/<<[ ]*[-\\'"]*[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
+/^[ ]*#/bnextline
+/^[ ]*$/bnextline
+# 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/bcontinue
+/^[ ]*while[ ]/bcontinue
+/^[ ]*do[ ]/bcontinue
+/^[ ]*do[ ]*$/bcontinue
+/;[ ]*do/bcontinue
+/^[ ]*done[ ]*&&[ ]*$/bdone
+/^[ ]*done[ ]*$/bdone
+/^[ ]*done[ ]*[<>|]/bdone
+/^[ ]*done[ ]*)/bdone
+/||[ ]*exit[ ]/bcontinue
+/||[ ]*exit[ ]*$/bcontinue
+# multi-line "if...elsif...else...fi"
+/^[ ]*if[ ]/bcontinue
+/^[ ]*then[ ]/bcontinue
+/^[ ]*then[ ]*$/bcontinue
+/;[ ]*then/bcontinue
+/^[ ]*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/^/?!SEMI?!/
+ }
+}
+# line ends with pipe "...|" -- valid; not missing "&&"
+/|[ ]*$/bcontinue
+# missing end-of-line "&&" -- mark suspect
+/&&[ ]*$/!s/^/?!AMP?!/
+:continue
+# retrieve and print previous line
+x
+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
+:dqstring
+# remove all quote pairs
+s/"\([^"]*\)"/@!\1@!/g
+# done if no dangling quote
+/"/!bdqdone
+# otherwise, slurp next line and try again
+N
+s/\n//
+bdqstring
+:dqdone
+s/@!/"/g
+bfolded
+
+# check for multi-line single-quoted string '...\n...' -- fold to one line
+:sqstring
+# remove all quote pairs
+s/'\([^']*\)'/@!\1@!/g
+# done if no dangling quote
+/'/!bsqdone
+# otherwise, slurp next line and try again
+N
+s/\n//
+bsqstring
+: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_]*\)['"]*/<\2>\1<</
+s/[ ]*<<//
+:heredsub
+N
+/^<\([^>]*\)>.*\n[ ]*\1[ ]*$/!{
+ s/\n.*$//
+ bheredsub
+}
+s/^<[^>]*>//
+s/\n.*$//
+bfolded
+
+# found "case ... in" -- pass through untouched
+:case
+x
+n
+/^[ ]*esac/bslurp
+bcase
+
+# found "else" or "elif" -- drop "suspect" from final line before "else" since
+# that line legitimately lacks "&&"
+:else
+x
+s/?!AMP?!//
+x
+bcontinue
+
+# 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?!//
+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
+:nstslurp
+n
+# closing ")" on own line -- stop nested slurp
+/^[ ]*)/bnstclose
+# comment -- not closing ")" if in comment
+/^[ ]*#/bnstcnt
+# "$((...))" -- arithmetic expansion; not closing ")"
+/\$(([^)][^)]*))[^)]*$/bnstcnt
+# "$(...)" -- command substitution; not closing ")"
+/\$([^)][^)]*)[^)]*$/bnstcnt
+# closing "...)" -- stop nested slurp
+/)/bnstclose
+:nstcnt
+x
+bnstslurp
+:nstclose
+s/^/>>/
+# is it "))" which closes nested and parent subshells?
+/)[ ]*)/bslurp
+bchkchn
+
+# found multi-line "{...\n...}" block -- pass through untouched
+:block
+x
+n
+# 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?!//
+p
+x
+s/^/>/
+b
+
+# found closing "...)" -- exit subshell loop
+:close
+x
+p
+x
+s/^/>/
+b
diff --git a/t/chainlint/arithmetic-expansion.expect b/t/chainlint/arithmetic-expansion.expect
new file mode 100644
index 0000000000..09457d3196
--- /dev/null
+++ b/t/chainlint/arithmetic-expansion.expect
@@ -0,0 +1,9 @@
+(
+ foo &&
+ bar=$((42 + 1)) &&
+ baz
+>) &&
+(
+?!AMP?! bar=$((42 + 1))
+ baz
+>)
diff --git a/t/chainlint/arithmetic-expansion.test b/t/chainlint/arithmetic-expansion.test
new file mode 100644
index 0000000000..16206960d8
--- /dev/null
+++ b/t/chainlint/arithmetic-expansion.test
@@ -0,0 +1,11 @@
+(
+ foo &&
+# LINT: closing ")" of $((...)) not misinterpreted as subshell-closing ")"
+ bar=$((42 + 1)) &&
+ baz
+) &&
+(
+# LINT: missing "&&" on $((...))
+ bar=$((42 + 1))
+ baz
+)
diff --git a/t/chainlint/bash-array.expect b/t/chainlint/bash-array.expect
new file mode 100644
index 0000000000..c4a830d1c1
--- /dev/null
+++ b/t/chainlint/bash-array.expect
@@ -0,0 +1,10 @@
+(
+ foo &&
+ bar=(gumbo stumbo wumbo) &&
+ baz
+>) &&
+(
+ foo &&
+ bar=${#bar[@]} &&
+ baz
+>)
diff --git a/t/chainlint/bash-array.test b/t/chainlint/bash-array.test
new file mode 100644
index 0000000000..92bbb777b8
--- /dev/null
+++ b/t/chainlint/bash-array.test
@@ -0,0 +1,12 @@
+(
+ foo &&
+# LINT: ")" in Bash array assignment not misinterpreted as subshell-closing ")"
+ bar=(gumbo stumbo wumbo) &&
+ baz
+) &&
+(
+ foo &&
+# LINT: Bash array length operator not misinterpreted as comment
+ bar=${#bar[@]} &&
+ baz
+)
diff --git a/t/chainlint/blank-line.expect b/t/chainlint/blank-line.expect
new file mode 100644
index 0000000000..3be939ed38
--- /dev/null
+++ b/t/chainlint/blank-line.expect
@@ -0,0 +1,4 @@
+(
+ nothing &&
+ something
+>)
diff --git a/t/chainlint/blank-line.test b/t/chainlint/blank-line.test
new file mode 100644
index 0000000000..f6dd14302b
--- /dev/null
+++ b/t/chainlint/blank-line.test
@@ -0,0 +1,10 @@
+(
+
+ nothing &&
+
+ something
+# LINT: swallow blank lines since final _statement_ before subshell end is
+# LINT: significant to "&&"-check, not final _line_ (which might be blank)
+
+
+)
diff --git a/t/chainlint/block.expect b/t/chainlint/block.expect
new file mode 100644
index 0000000000..fed7e89ae8
--- /dev/null
+++ b/t/chainlint/block.expect
@@ -0,0 +1,12 @@
+(
+ foo &&
+ {
+ echo a
+ echo b
+ } &&
+ bar &&
+ {
+ echo c
+?!AMP?! }
+ baz
+>)
diff --git a/t/chainlint/block.test b/t/chainlint/block.test
new file mode 100644
index 0000000000..d859151af1
--- /dev/null
+++ b/t/chainlint/block.test
@@ -0,0 +1,15 @@
+(
+# LINT: missing "&&" in block not currently detected (for consistency with
+# LINT: --chain-lint at top level and to provide escape hatch if needed)
+ foo &&
+ {
+ echo a
+ echo b
+ } &&
+ bar &&
+# LINT: missing "&&" at closing "}"
+ {
+ echo c
+ }
+ baz
+)
diff --git a/t/chainlint/broken-chain.expect b/t/chainlint/broken-chain.expect
new file mode 100644
index 0000000000..55b0f42a53
--- /dev/null
+++ b/t/chainlint/broken-chain.expect
@@ -0,0 +1,6 @@
+(
+ foo &&
+?!AMP?! bar
+ baz &&
+ wop
+>)
diff --git a/t/chainlint/broken-chain.test b/t/chainlint/broken-chain.test
new file mode 100644
index 0000000000..3cc67b65d0
--- /dev/null
+++ b/t/chainlint/broken-chain.test
@@ -0,0 +1,8 @@
+(
+ foo &&
+# LINT: missing "&&" from 'bar'
+ bar
+ baz &&
+# LINT: final statement before closing ")" legitimately lacks "&&"
+ wop
+)
diff --git a/t/chainlint/case.expect b/t/chainlint/case.expect
new file mode 100644
index 0000000000..41f121fbbf
--- /dev/null
+++ b/t/chainlint/case.expect
@@ -0,0 +1,19 @@
+(
+ case "$x" in
+ x) foo ;;
+ *) bar ;;
+ esac &&
+ foobar
+>) &&
+(
+ case "$x" in
+ x) foo ;;
+ *) bar ;;
+?!AMP?! esac
+ foobar
+>) &&
+(
+ case "$x" in 1) true;; esac &&
+?!AMP?! case "$y" in 2) false;; esac
+ foobar
+>)
diff --git a/t/chainlint/case.test b/t/chainlint/case.test
new file mode 100644
index 0000000000..5ef6ff7db5
--- /dev/null
+++ b/t/chainlint/case.test
@@ -0,0 +1,23 @@
+(
+# LINT: "...)" arms in 'case' not misinterpreted as subshell-closing ")"
+ case "$x" in
+ x) foo ;;
+ *) bar ;;
+ esac &&
+ foobar
+) &&
+(
+# LINT: missing "&&" on 'esac'
+ case "$x" in
+ x) foo ;;
+ *) bar ;;
+ esac
+ foobar
+) &&
+(
+# LINT: "...)" arm in one-liner 'case' not misinterpreted as closing ")"
+ case "$x" in 1) true;; esac &&
+# LINT: same but missing "&&"
+ case "$y" in 2) false;; esac
+ foobar
+)
diff --git a/t/chainlint/close-nested-and-parent-together.expect b/t/chainlint/close-nested-and-parent-together.expect
new file mode 100644
index 0000000000..2a910f9d66
--- /dev/null
+++ b/t/chainlint/close-nested-and-parent-together.expect
@@ -0,0 +1,4 @@
+(
+cd foo &&
+ (bar &&
+>>> baz))
diff --git a/t/chainlint/close-nested-and-parent-together.test b/t/chainlint/close-nested-and-parent-together.test
new file mode 100644
index 0000000000..72d482f76d
--- /dev/null
+++ b/t/chainlint/close-nested-and-parent-together.test
@@ -0,0 +1,3 @@
+(cd foo &&
+ (bar &&
+ baz))
diff --git a/t/chainlint/close-subshell.expect b/t/chainlint/close-subshell.expect
new file mode 100644
index 0000000000..184688718a
--- /dev/null
+++ b/t/chainlint/close-subshell.expect
@@ -0,0 +1,25 @@
+(
+ foo
+>) &&
+(
+ bar
+>) >out &&
+(
+ baz
+>) 2>err &&
+(
+ boo
+>) <input &&
+(
+ bip
+>) | wuzzle &&
+(
+ bop
+>) | fazz fozz &&
+(
+ bup
+>) |
+fuzzle &&
+(
+ yop
+>)
diff --git a/t/chainlint/close-subshell.test b/t/chainlint/close-subshell.test
new file mode 100644
index 0000000000..508ca447fd
--- /dev/null
+++ b/t/chainlint/close-subshell.test
@@ -0,0 +1,27 @@
+# LINT: closing ")" with various decorations ("&&", ">", "|", etc.)
+(
+ foo
+) &&
+(
+ bar
+) >out &&
+(
+ baz
+) 2>err &&
+(
+ boo
+) <input &&
+(
+ bip
+) | wuzzle &&
+(
+ bop
+) | fazz \
+ fozz &&
+(
+ bup
+) |
+fuzzle &&
+(
+ yop
+)
diff --git a/t/chainlint/command-substitution.expect b/t/chainlint/command-substitution.expect
new file mode 100644
index 0000000000..ad4118e537
--- /dev/null
+++ b/t/chainlint/command-substitution.expect
@@ -0,0 +1,9 @@
+(
+ foo &&
+ bar=$(gobble) &&
+ baz
+>) &&
+(
+?!AMP?! bar=$(gobble blocks)
+ baz
+>)
diff --git a/t/chainlint/command-substitution.test b/t/chainlint/command-substitution.test
new file mode 100644
index 0000000000..3bbb002a4c
--- /dev/null
+++ b/t/chainlint/command-substitution.test
@@ -0,0 +1,11 @@
+(
+ foo &&
+# LINT: closing ")" of $(...) not misinterpreted as subshell-closing ")"
+ bar=$(gobble) &&
+ baz
+) &&
+(
+# LINT: missing "&&" on $(...)
+ bar=$(gobble blocks)
+ baz
+)
diff --git a/t/chainlint/comment.expect b/t/chainlint/comment.expect
new file mode 100644
index 0000000000..3be939ed38
--- /dev/null
+++ b/t/chainlint/comment.expect
@@ -0,0 +1,4 @@
+(
+ nothing &&
+ something
+>)
diff --git a/t/chainlint/comment.test b/t/chainlint/comment.test
new file mode 100644
index 0000000000..113c0c466f
--- /dev/null
+++ b/t/chainlint/comment.test
@@ -0,0 +1,11 @@
+(
+# LINT: swallow comment lines
+ # comment 1
+ nothing &&
+ # comment 2
+ something
+# LINT: swallow comment lines since final _statement_ before subshell end is
+# LINT: significant to "&&"-check, not final _line_ (which might be comment)
+ # comment 3
+ # comment 4
+)
diff --git a/t/chainlint/complex-if-in-cuddled-loop.expect b/t/chainlint/complex-if-in-cuddled-loop.expect
new file mode 100644
index 0000000000..9674b88cf2
--- /dev/null
+++ b/t/chainlint/complex-if-in-cuddled-loop.expect
@@ -0,0 +1,10 @@
+(
+for i in a b c; do
+ if test "$(echo $(waffle bat))" = "eleventeen" &&
+ test "$x" = "$y"; then
+ :
+ else
+ echo >file
+ fi
+> done) &&
+test ! -f file
diff --git a/t/chainlint/complex-if-in-cuddled-loop.test b/t/chainlint/complex-if-in-cuddled-loop.test
new file mode 100644
index 0000000000..571bbd85cd
--- /dev/null
+++ b/t/chainlint/complex-if-in-cuddled-loop.test
@@ -0,0 +1,11 @@
+# LINT: 'for' loop cuddled with "(" and ")" and nested 'if' with complex
+# LINT: multi-line condition; indented with spaces, not tabs
+(for i in a b c; do
+ if test "$(echo $(waffle bat))" = "eleventeen" &&
+ test "$x" = "$y"; then
+ :
+ else
+ echo >file
+ fi
+ done) &&
+test ! -f file
diff --git a/t/chainlint/cuddled-if-then-else.expect b/t/chainlint/cuddled-if-then-else.expect
new file mode 100644
index 0000000000..ab2a026fbc
--- /dev/null
+++ b/t/chainlint/cuddled-if-then-else.expect
@@ -0,0 +1,7 @@
+(
+if test -z ""; then
+ echo empty
+ else
+ echo bizzy
+> fi) &&
+echo foobar
diff --git a/t/chainlint/cuddled-if-then-else.test b/t/chainlint/cuddled-if-then-else.test
new file mode 100644
index 0000000000..eed774a9d6
--- /dev/null
+++ b/t/chainlint/cuddled-if-then-else.test
@@ -0,0 +1,7 @@
+# LINT: 'if' cuddled with "(" and ")"; indented with spaces, not tabs
+(if test -z ""; then
+ echo empty
+ else
+ echo bizzy
+ fi) &&
+echo foobar
diff --git a/t/chainlint/cuddled-loop.expect b/t/chainlint/cuddled-loop.expect
new file mode 100644
index 0000000000..8c0260d7f1
--- /dev/null
+++ b/t/chainlint/cuddled-loop.expect
@@ -0,0 +1,5 @@
+(
+ while read x
+ do foobar bop || exit 1
+> done <file ) &&
+outside subshell
diff --git a/t/chainlint/cuddled-loop.test b/t/chainlint/cuddled-loop.test
new file mode 100644
index 0000000000..a841d781f0
--- /dev/null
+++ b/t/chainlint/cuddled-loop.test
@@ -0,0 +1,7 @@
+# LINT: 'while' loop cuddled with "(" and ")", with embedded (allowed)
+# LINT: "|| exit {n}" to exit loop early, and using redirection "<" to feed
+# LINT: loop; indented with spaces, not tabs
+( while read x
+ do foobar bop || exit 1
+ done <file ) &&
+outside subshell
diff --git a/t/chainlint/cuddled.expect b/t/chainlint/cuddled.expect
new file mode 100644
index 0000000000..b506d46221
--- /dev/null
+++ b/t/chainlint/cuddled.expect
@@ -0,0 +1,21 @@
+(
+cd foo &&
+ bar
+>) &&
+
+(
+?!AMP?!cd foo
+ bar
+>) &&
+
+(
+ cd foo &&
+> bar) &&
+
+(
+cd foo &&
+> bar) &&
+
+(
+?!AMP?!cd foo
+> bar)
diff --git a/t/chainlint/cuddled.test b/t/chainlint/cuddled.test
new file mode 100644
index 0000000000..0499fa4180
--- /dev/null
+++ b/t/chainlint/cuddled.test
@@ -0,0 +1,23 @@
+# LINT: first subshell statement cuddled with opening "("; for implementation
+# LINT: simplicity, "(..." is split into two lines, "(" and "..."
+(cd foo &&
+ bar
+) &&
+
+# LINT: same with missing "&&"
+(cd foo
+ bar
+) &&
+
+# LINT: closing ")" cuddled with final subshell statement
+(
+ cd foo &&
+ bar) &&
+
+# LINT: "(" and ")" cuddled with first and final subshell statements
+(cd foo &&
+ bar) &&
+
+# LINT: same with missing "&&"
+(cd foo
+ bar)
diff --git a/t/chainlint/exit-loop.expect b/t/chainlint/exit-loop.expect
new file mode 100644
index 0000000000..84d8bdebc0
--- /dev/null
+++ b/t/chainlint/exit-loop.expect
@@ -0,0 +1,24 @@
+(
+ for i in a b c
+ do
+ foo || exit 1
+ bar &&
+ baz
+ done
+>) &&
+(
+ while true
+ do
+ foo || exit 1
+ bar &&
+ baz
+ done
+>) &&
+(
+ i=0 &&
+ while test $i -lt 10
+ do
+ echo $i || exit
+ i=$(($i + 1))
+ done
+>)
diff --git a/t/chainlint/exit-loop.test b/t/chainlint/exit-loop.test
new file mode 100644
index 0000000000..2f038207e1
--- /dev/null
+++ b/t/chainlint/exit-loop.test
@@ -0,0 +1,27 @@
+(
+ for i in a b c
+ do
+# LINT: "|| exit {n}" valid for-loop escape in subshell; no "&&" needed
+ foo || exit 1
+ bar &&
+ baz
+ done
+) &&
+(
+ while true
+ do
+# LINT: "|| exit {n}" valid while-loop escape in subshell; no "&&" needed
+ foo || exit 1
+ bar &&
+ baz
+ done
+) &&
+(
+ i=0 &&
+ while test $i -lt 10
+ do
+# LINT: "|| exit" (sans exit code) valid escape in subshell; no "&&" needed
+ echo $i || exit
+ i=$(($i + 1))
+ done
+)
diff --git a/t/chainlint/exit-subshell.expect b/t/chainlint/exit-subshell.expect
new file mode 100644
index 0000000000..bf78454f74
--- /dev/null
+++ b/t/chainlint/exit-subshell.expect
@@ -0,0 +1,5 @@
+(
+ foo || exit 1
+ bar &&
+ baz
+>)
diff --git a/t/chainlint/exit-subshell.test b/t/chainlint/exit-subshell.test
new file mode 100644
index 0000000000..4e6ab69b88
--- /dev/null
+++ b/t/chainlint/exit-subshell.test
@@ -0,0 +1,6 @@
+(
+# LINT: "|| exit {n}" valid subshell escape without hurting &&-chain
+ foo || exit 1
+ bar &&
+ baz
+)
diff --git a/t/chainlint/for-loop.expect b/t/chainlint/for-loop.expect
new file mode 100644
index 0000000000..c33cf56ee7
--- /dev/null
+++ b/t/chainlint/for-loop.expect
@@ -0,0 +1,11 @@
+(
+ for i in a b c
+ do
+?!AMP?! echo $i
+ cat
+?!AMP?! done
+ for i in a b c; do
+ echo $i &&
+ cat $i
+ done
+>)
diff --git a/t/chainlint/for-loop.test b/t/chainlint/for-loop.test
new file mode 100644
index 0000000000..7db76262bc
--- /dev/null
+++ b/t/chainlint/for-loop.test
@@ -0,0 +1,19 @@
+(
+# LINT: 'for', 'do', 'done' do not need "&&"
+ for i in a b c
+ do
+# LINT: missing "&&" on 'echo'
+ echo $i
+# LINT: last statement of while does not need "&&"
+ cat <<-\EOF
+ bar
+ EOF
+# LINT: missing "&&" on 'done'
+ done
+
+# LINT: 'do' on same line as 'for'
+ for i in a b c; do
+ echo $i &&
+ cat $i
+ done
+)
diff --git a/t/chainlint/here-doc-close-subshell.expect b/t/chainlint/here-doc-close-subshell.expect
new file mode 100644
index 0000000000..f011e335e5
--- /dev/null
+++ b/t/chainlint/here-doc-close-subshell.expect
@@ -0,0 +1,2 @@
+(
+> cat)
diff --git a/t/chainlint/here-doc-close-subshell.test b/t/chainlint/here-doc-close-subshell.test
new file mode 100644
index 0000000000..b857ff5467
--- /dev/null
+++ b/t/chainlint/here-doc-close-subshell.test
@@ -0,0 +1,5 @@
+(
+# LINT: line contains here-doc and closes nested subshell
+ cat <<-\INPUT)
+ fizz
+ INPUT
diff --git a/t/chainlint/here-doc-multi-line-command-subst.expect b/t/chainlint/here-doc-multi-line-command-subst.expect
new file mode 100644
index 0000000000..e5fb752d2f
--- /dev/null
+++ b/t/chainlint/here-doc-multi-line-command-subst.expect
@@ -0,0 +1,5 @@
+(
+ x=$(bobble &&
+?!AMP?!>> wiffle)
+ echo $x
+>)
diff --git a/t/chainlint/here-doc-multi-line-command-subst.test b/t/chainlint/here-doc-multi-line-command-subst.test
new file mode 100644
index 0000000000..899bc5de8b
--- /dev/null
+++ b/t/chainlint/here-doc-multi-line-command-subst.test
@@ -0,0 +1,9 @@
+(
+# LINT: line contains here-doc and opens multi-line $(...)
+ x=$(bobble <<-\END &&
+ fossil
+ vegetable
+ END
+ wiffle)
+ echo $x
+)
diff --git a/t/chainlint/here-doc-multi-line-string.expect b/t/chainlint/here-doc-multi-line-string.expect
new file mode 100644
index 0000000000..32038a070c
--- /dev/null
+++ b/t/chainlint/here-doc-multi-line-string.expect
@@ -0,0 +1,4 @@
+(
+?!AMP?! cat && echo "multi-line string"
+ bap
+>)
diff --git a/t/chainlint/here-doc-multi-line-string.test b/t/chainlint/here-doc-multi-line-string.test
new file mode 100644
index 0000000000..a53edbcc8d
--- /dev/null
+++ b/t/chainlint/here-doc-multi-line-string.test
@@ -0,0 +1,8 @@
+(
+# LINT: line contains here-doc and opens multi-line string
+ cat <<-\TXT && echo "multi-line
+ string"
+ fizzle
+ TXT
+ bap
+)
diff --git a/t/chainlint/here-doc.expect b/t/chainlint/here-doc.expect
new file mode 100644
index 0000000000..534b065e38
--- /dev/null
+++ b/t/chainlint/here-doc.expect
@@ -0,0 +1,9 @@
+boodle wobba gorgo snoot wafta snurb &&
+
+cat >foo &&
+
+cat >bar &&
+
+cat >boo &&
+
+horticulture
diff --git a/t/chainlint/here-doc.test b/t/chainlint/here-doc.test
new file mode 100644
index 0000000000..ad4ce8afd9
--- /dev/null
+++ b/t/chainlint/here-doc.test
@@ -0,0 +1,37 @@
+# LINT: stitch together incomplete \-ending lines
+# LINT: swallow here-doc to avoid false positives in content
+boodle wobba \
+ gorgo snoot \
+ wafta snurb <<EOF &&
+quoth the raven,
+nevermore...
+EOF
+
+# LINT: swallow here-doc with arbitrary tag
+cat <<-Arbitrary_Tag_42 >foo &&
+snoz
+boz
+woz
+Arbitrary_Tag_42
+
+# LINT: swallow 'quoted' here-doc
+cat <<'FUMP' >bar &&
+snoz
+boz
+woz
+FUMP
+
+# LINT: swallow "quoted" here-doc
+cat <<"zump" >boo &&
+snoz
+boz
+woz
+zump
+
+# LINT: swallow here-doc (EOF is last line of test)
+horticulture <<\EOF
+gomez
+morticia
+wednesday
+pugsly
+EOF
diff --git a/t/chainlint/if-in-loop.expect b/t/chainlint/if-in-loop.expect
new file mode 100644
index 0000000000..03d3ceb22d
--- /dev/null
+++ b/t/chainlint/if-in-loop.expect
@@ -0,0 +1,12 @@
+(
+ for i in a b c
+ do
+ if false
+ then
+?!AMP?! echo "err"
+ exit 1
+?!AMP?! fi
+ foo
+?!AMP?! done
+ bar
+>)
diff --git a/t/chainlint/if-in-loop.test b/t/chainlint/if-in-loop.test
new file mode 100644
index 0000000000..daf22da164
--- /dev/null
+++ b/t/chainlint/if-in-loop.test
@@ -0,0 +1,15 @@
+(
+ for i in a b c
+ do
+ if false
+ then
+# LINT: missing "&&" on 'echo'
+ echo "err"
+ exit 1
+# LINT: missing "&&" on 'fi'
+ fi
+ foo
+# LINT: missing "&&" on 'done'
+ done
+ bar
+)
diff --git a/t/chainlint/if-then-else.expect b/t/chainlint/if-then-else.expect
new file mode 100644
index 0000000000..5953c7bfbc
--- /dev/null
+++ b/t/chainlint/if-then-else.expect
@@ -0,0 +1,19 @@
+(
+ if test -n ""
+ then
+?!AMP?! echo very
+ echo empty
+ elif test -z ""
+ echo foo
+ else
+ echo foo &&
+ cat
+?!AMP?! fi
+ echo poodle
+>) &&
+(
+ if test -n ""; then
+ echo very &&
+?!AMP?! echo empty
+ if
+>)
diff --git a/t/chainlint/if-then-else.test b/t/chainlint/if-then-else.test
new file mode 100644
index 0000000000..9bd8e9a4c6
--- /dev/null
+++ b/t/chainlint/if-then-else.test
@@ -0,0 +1,28 @@
+(
+# LINT: 'if', 'then', 'elif', 'else', 'fi' do not need "&&"
+ if test -n ""
+ then
+# LINT: missing "&&" on 'echo'
+ echo very
+# LINT: last statement before 'elif' does not need "&&"
+ echo empty
+ elif test -z ""
+# LINT: last statement before 'else' does not need "&&"
+ echo foo
+ else
+ echo foo &&
+# LINT: last statement before 'fi' does not need "&&"
+ cat <<-\EOF
+ bar
+ EOF
+# LINT: missing "&&" on 'fi'
+ fi
+ echo poodle
+) &&
+(
+# LINT: 'then' on same line as 'if'
+ if test -n ""; then
+ echo very &&
+ echo empty
+ if
+)
diff --git a/t/chainlint/incomplete-line.expect b/t/chainlint/incomplete-line.expect
new file mode 100644
index 0000000000..2f3ebabdc2
--- /dev/null
+++ b/t/chainlint/incomplete-line.expect
@@ -0,0 +1,4 @@
+line 1 line 2 line 3 line 4 &&
+(
+ line 5 line 6 line 7 line 8
+>)
diff --git a/t/chainlint/incomplete-line.test b/t/chainlint/incomplete-line.test
new file mode 100644
index 0000000000..d856658083
--- /dev/null
+++ b/t/chainlint/incomplete-line.test
@@ -0,0 +1,12 @@
+# LINT: stitch together all incomplete \-ending lines
+line 1 \
+line 2 \
+line 3 \
+line 4 &&
+(
+# LINT: stitch together all incomplete \-ending lines (subshell)
+ line 5 \
+ line 6 \
+ line 7 \
+ line 8
+)
diff --git a/t/chainlint/inline-comment.expect b/t/chainlint/inline-comment.expect
new file mode 100644
index 0000000000..fc9f250ac4
--- /dev/null
+++ b/t/chainlint/inline-comment.expect
@@ -0,0 +1,9 @@
+(
+ foobar &&
+?!AMP?! barfoo
+ flibble "not a # comment"
+>) &&
+
+(
+cd foo &&
+> flibble "not a # comment")
diff --git a/t/chainlint/inline-comment.test b/t/chainlint/inline-comment.test
new file mode 100644
index 0000000000..8f26856e77
--- /dev/null
+++ b/t/chainlint/inline-comment.test
@@ -0,0 +1,12 @@
+(
+# LINT: swallow inline comment (leaving command intact)
+ foobar && # comment 1
+# LINT: mispositioned "&&" (correctly) swallowed with comment
+ barfoo # wrong position for &&
+# LINT: "#" in string not misinterpreted as comment
+ flibble "not a # comment"
+) &&
+
+# LINT: "#" in string in cuddled subshell not misinterpreted as comment
+(cd foo &&
+ flibble "not a # comment")
diff --git a/t/chainlint/loop-in-if.expect b/t/chainlint/loop-in-if.expect
new file mode 100644
index 0000000000..088e622c31
--- /dev/null
+++ b/t/chainlint/loop-in-if.expect
@@ -0,0 +1,12 @@
+(
+ if true
+ then
+ while true
+ do
+?!AMP?! echo "pop"
+ echo "glup"
+?!AMP?! done
+ foo
+?!AMP?! fi
+ bar
+>)
diff --git a/t/chainlint/loop-in-if.test b/t/chainlint/loop-in-if.test
new file mode 100644
index 0000000000..93e8ba8e4d
--- /dev/null
+++ b/t/chainlint/loop-in-if.test
@@ -0,0 +1,15 @@
+(
+ if true
+ then
+ while true
+ do
+# LINT: missing "&&" on 'echo'
+ echo "pop"
+ echo "glup"
+# LINT: missing "&&" on 'done'
+ done
+ foo
+# LINT: missing "&&" on 'fi'
+ fi
+ bar
+)
diff --git a/t/chainlint/multi-line-nested-command-substitution.expect b/t/chainlint/multi-line-nested-command-substitution.expect
new file mode 100644
index 0000000000..59b6c8b850
--- /dev/null
+++ b/t/chainlint/multi-line-nested-command-substitution.expect
@@ -0,0 +1,18 @@
+(
+ foo &&
+ x=$(
+ echo bar |
+ cat
+>> ) &&
+ echo ok
+>) |
+sort &&
+(
+ bar &&
+ x=$(echo bar |
+ cat
+>> ) &&
+ y=$(echo baz |
+>> fip) &&
+ echo fail
+>)
diff --git a/t/chainlint/multi-line-nested-command-substitution.test b/t/chainlint/multi-line-nested-command-substitution.test
new file mode 100644
index 0000000000..300058341b
--- /dev/null
+++ b/t/chainlint/multi-line-nested-command-substitution.test
@@ -0,0 +1,18 @@
+(
+ foo &&
+ x=$(
+ echo bar |
+ cat
+ ) &&
+ echo ok
+) |
+sort &&
+(
+ bar &&
+ x=$(echo bar |
+ cat
+ ) &&
+ y=$(echo baz |
+ fip) &&
+ echo fail
+)
diff --git a/t/chainlint/multi-line-string.expect b/t/chainlint/multi-line-string.expect
new file mode 100644
index 0000000000..170cb59993
--- /dev/null
+++ b/t/chainlint/multi-line-string.expect
@@ -0,0 +1,15 @@
+(
+ x="line 1 line 2 line 3" &&
+?!AMP?! y='line 1 line2'
+ foobar
+>) &&
+(
+ echo "there's nothing to see here" &&
+ exit
+>) &&
+(
+ echo "xyz" "abc def ghi" &&
+ echo 'xyz' 'abc def ghi' &&
+ echo 'xyz' "abc def ghi" &&
+ barfoo
+>)
diff --git a/t/chainlint/multi-line-string.test b/t/chainlint/multi-line-string.test
new file mode 100644
index 0000000000..287ab89705
--- /dev/null
+++ b/t/chainlint/multi-line-string.test
@@ -0,0 +1,27 @@
+(
+ x="line 1
+ line 2
+ line 3" &&
+# LINT: missing "&&" on assignment
+ y='line 1
+ line2'
+ foobar
+) &&
+(
+# LINT: apostrophe (in a contraction) within string not misinterpreted as
+# LINT: starting multi-line single-quoted string
+ echo "there's nothing to see here" &&
+ exit
+) &&
+(
+ echo "xyz" "abc
+ def
+ ghi" &&
+ echo 'xyz' 'abc
+ def
+ ghi' &&
+ echo 'xyz' "abc
+ def
+ ghi" &&
+ barfoo
+)
diff --git a/t/chainlint/negated-one-liner.expect b/t/chainlint/negated-one-liner.expect
new file mode 100644
index 0000000000..cf18429d03
--- /dev/null
+++ b/t/chainlint/negated-one-liner.expect
@@ -0,0 +1,5 @@
+! (foo && bar) &&
+! (foo && bar) >baz &&
+
+?!SEMI?!! (foo; bar) &&
+?!SEMI?!! (foo; bar) >baz
diff --git a/t/chainlint/negated-one-liner.test b/t/chainlint/negated-one-liner.test
new file mode 100644
index 0000000000..c9598e9153
--- /dev/null
+++ b/t/chainlint/negated-one-liner.test
@@ -0,0 +1,7 @@
+# LINT: top-level one-liner subshell
+! (foo && bar) &&
+! (foo && bar) >baz &&
+
+# LINT: top-level one-liner subshell missing internal "&&"
+! (foo; bar) &&
+! (foo; bar) >baz
diff --git a/t/chainlint/nested-cuddled-subshell.expect b/t/chainlint/nested-cuddled-subshell.expect
new file mode 100644
index 0000000000..c2a59ffc33
--- /dev/null
+++ b/t/chainlint/nested-cuddled-subshell.expect
@@ -0,0 +1,19 @@
+(
+ (cd foo &&
+ bar
+>> ) &&
+ (cd foo &&
+ bar
+?!AMP?!>> )
+ (
+ cd foo &&
+>> bar) &&
+ (
+ cd foo &&
+?!AMP?!>> bar)
+ (cd foo &&
+>> bar) &&
+ (cd foo &&
+?!AMP?!>> bar)
+ foobar
+>)
diff --git a/t/chainlint/nested-cuddled-subshell.test b/t/chainlint/nested-cuddled-subshell.test
new file mode 100644
index 0000000000..8fd656c7b5
--- /dev/null
+++ b/t/chainlint/nested-cuddled-subshell.test
@@ -0,0 +1,31 @@
+(
+# LINT: opening "(" cuddled with first nested subshell statement
+ (cd foo &&
+ bar
+ ) &&
+
+# LINT: same but "&&" missing
+ (cd foo &&
+ bar
+ )
+
+# LINT: closing ")" cuddled with final nested subshell statement
+ (
+ cd foo &&
+ bar) &&
+
+# LINT: same but "&&" missing
+ (
+ cd foo &&
+ bar)
+
+# LINT: "(" and ")" cuddled with first and final subshell statements
+ (cd foo &&
+ bar) &&
+
+# LINT: same but "&&" missing
+ (cd foo &&
+ bar)
+
+ foobar
+)
diff --git a/t/chainlint/nested-here-doc.expect b/t/chainlint/nested-here-doc.expect
new file mode 100644
index 0000000000..0c9ef1cfc6
--- /dev/null
+++ b/t/chainlint/nested-here-doc.expect
@@ -0,0 +1,7 @@
+cat >foop &&
+
+(
+ cat &&
+?!AMP?! cat
+ foobar
+>)
diff --git a/t/chainlint/nested-here-doc.test b/t/chainlint/nested-here-doc.test
new file mode 100644
index 0000000000..f35404bf0f
--- /dev/null
+++ b/t/chainlint/nested-here-doc.test
@@ -0,0 +1,33 @@
+# LINT: inner "EOF" not misintrepreted as closing ARBITRARY here-doc
+cat <<ARBITRARY >foop &&
+naddle
+fub <<EOF
+ nozzle
+ noodle
+EOF
+formp
+ARBITRARY
+
+(
+# LINT: inner "EOF" not misintrepreted as closing INPUT_END here-doc
+ cat <<-\INPUT_END &&
+ fish are mice
+ but geese go slow
+ data <<EOF
+ perl is lerp
+ and nothing else
+ EOF
+ toink
+ INPUT_END
+
+# LINT: same but missing "&&"
+ cat <<-\EOT
+ text goes here
+ data <<EOF
+ data goes here
+ EOF
+ more test here
+ EOT
+
+ foobar
+)
diff --git a/t/chainlint/nested-subshell-comment.expect b/t/chainlint/nested-subshell-comment.expect
new file mode 100644
index 0000000000..15b68d4373
--- /dev/null
+++ b/t/chainlint/nested-subshell-comment.expect
@@ -0,0 +1,11 @@
+(
+ foo &&
+ (
+ bar &&
+ # bottles wobble while fiddles gobble
+ # minor numbers of cows (or do they?)
+ baz &&
+ snaff
+?!AMP?!>> )
+ fuzzy
+>)
diff --git a/t/chainlint/nested-subshell-comment.test b/t/chainlint/nested-subshell-comment.test
new file mode 100644
index 0000000000..0ff136ab3c
--- /dev/null
+++ b/t/chainlint/nested-subshell-comment.test
@@ -0,0 +1,13 @@
+(
+ foo &&
+ (
+ bar &&
+# LINT: ")" in comment in nested subshell not misinterpreted as closing ")"
+ # bottles wobble while fiddles gobble
+ # minor numbers of cows (or do they?)
+ baz &&
+ snaff
+# LINT: missing "&&" on ')'
+ )
+ fuzzy
+)
diff --git a/t/chainlint/nested-subshell.expect b/t/chainlint/nested-subshell.expect
new file mode 100644
index 0000000000..c8165ad19e
--- /dev/null
+++ b/t/chainlint/nested-subshell.expect
@@ -0,0 +1,12 @@
+(
+ cd foo &&
+ (
+ echo a &&
+ echo b
+>> ) >file &&
+ cd foo &&
+ (
+ echo a
+ echo b
+>> ) >file
+>)
diff --git a/t/chainlint/nested-subshell.test b/t/chainlint/nested-subshell.test
new file mode 100644
index 0000000000..998b05a47d
--- /dev/null
+++ b/t/chainlint/nested-subshell.test
@@ -0,0 +1,14 @@
+(
+ cd foo &&
+ (
+ echo a &&
+ echo b
+ ) >file &&
+
+ cd foo &&
+ (
+# LINT: nested multi-line subshell not presently checked for missing "&&"
+ echo a
+ echo b
+ ) >file
+)
diff --git a/t/chainlint/one-liner.expect b/t/chainlint/one-liner.expect
new file mode 100644
index 0000000000..237f227349
--- /dev/null
+++ b/t/chainlint/one-liner.expect
@@ -0,0 +1,9 @@
+(foo && bar) &&
+(foo && bar) |
+(foo && bar) >baz &&
+
+?!SEMI?!(foo; bar) &&
+?!SEMI?!(foo; bar) |
+?!SEMI?!(foo; bar) >baz
+
+(foo "bar; baz")
diff --git a/t/chainlint/one-liner.test b/t/chainlint/one-liner.test
new file mode 100644
index 0000000000..ec9acb9825
--- /dev/null
+++ b/t/chainlint/one-liner.test
@@ -0,0 +1,12 @@
+# LINT: top-level one-liner subshell
+(foo && bar) &&
+(foo && bar) |
+(foo && bar) >baz &&
+
+# LINT: top-level one-liner subshell missing internal "&&"
+(foo; bar) &&
+(foo; bar) |
+(foo; bar) >baz
+
+# LINT: ";" in string not misinterpreted as broken &&-chain
+(foo "bar; baz")
diff --git a/t/chainlint/p4-filespec.expect b/t/chainlint/p4-filespec.expect
new file mode 100644
index 0000000000..98b3d881fd
--- /dev/null
+++ b/t/chainlint/p4-filespec.expect
@@ -0,0 +1,4 @@
+(
+ p4 print -1 //depot/fiddle#42 >file &&
+ foobar
+>)
diff --git a/t/chainlint/p4-filespec.test b/t/chainlint/p4-filespec.test
new file mode 100644
index 0000000000..4fd2d6e2b8
--- /dev/null
+++ b/t/chainlint/p4-filespec.test
@@ -0,0 +1,5 @@
+(
+# LINT: Perforce revspec in filespec not misinterpreted as in-line comment
+ p4 print -1 //depot/fiddle#42 >file &&
+ foobar
+)
diff --git a/t/chainlint/pipe.expect b/t/chainlint/pipe.expect
new file mode 100644
index 0000000000..211b901dbc
--- /dev/null
+++ b/t/chainlint/pipe.expect
@@ -0,0 +1,8 @@
+(
+ foo |
+ bar |
+ baz &&
+ fish |
+?!AMP?! cow
+ sunder
+>)
diff --git a/t/chainlint/pipe.test b/t/chainlint/pipe.test
new file mode 100644
index 0000000000..e6af4de916
--- /dev/null
+++ b/t/chainlint/pipe.test
@@ -0,0 +1,12 @@
+(
+# LINT: no "&&" needed on line ending with "|"
+ foo |
+ bar |
+ baz &&
+
+# LINT: final line of pipe sequence ('cow') lacking "&&"
+ fish |
+ cow
+
+ sunder
+)
diff --git a/t/chainlint/semicolon.expect b/t/chainlint/semicolon.expect
new file mode 100644
index 0000000000..1d79384606
--- /dev/null
+++ b/t/chainlint/semicolon.expect
@@ -0,0 +1,20 @@
+(
+?!AMP?!?!SEMI?! cat foo ; echo bar
+?!SEMI?! cat foo ; echo bar
+>) &&
+(
+?!SEMI?! cat foo ; echo bar &&
+?!SEMI?! cat foo ; echo bar
+>) &&
+(
+ echo "foo; bar" &&
+?!SEMI?! cat foo; echo bar
+>) &&
+(
+?!SEMI?! foo;
+>) &&
+(
+cd foo &&
+ for i in a b c; do
+?!SEMI?! echo;
+> done)
diff --git a/t/chainlint/semicolon.test b/t/chainlint/semicolon.test
new file mode 100644
index 0000000000..d82c8ebbc0
--- /dev/null
+++ b/t/chainlint/semicolon.test
@@ -0,0 +1,25 @@
+(
+# LINT: missing internal "&&" and ending "&&"
+ cat foo ; echo bar
+# LINT: final statement before ")" only missing internal "&&"
+ cat foo ; echo bar
+) &&
+(
+# LINT: missing internal "&&"
+ cat foo ; echo bar &&
+ cat foo ; echo bar
+) &&
+(
+# LINT: not fooled by semicolon in string
+ echo "foo; bar" &&
+ cat foo; echo bar
+) &&
+(
+# LINT: unnecessary terminating semicolon
+ foo;
+) &&
+(cd foo &&
+ for i in a b c; do
+# LINT: unnecessary terminating semicolon
+ echo;
+ done)
diff --git a/t/chainlint/subshell-here-doc.expect b/t/chainlint/subshell-here-doc.expect
new file mode 100644
index 0000000000..74723e7340
--- /dev/null
+++ b/t/chainlint/subshell-here-doc.expect
@@ -0,0 +1,11 @@
+(
+ echo wobba gorgo snoot wafta snurb &&
+?!AMP?! cat >bip
+ echo >bop
+>) &&
+(
+ cat >bup &&
+ cat >bup2 &&
+ cat >bup3 &&
+ meep
+>)
diff --git a/t/chainlint/subshell-here-doc.test b/t/chainlint/subshell-here-doc.test
new file mode 100644
index 0000000000..f6b3ba4214
--- /dev/null
+++ b/t/chainlint/subshell-here-doc.test
@@ -0,0 +1,39 @@
+(
+# LINT: stitch together incomplete \-ending lines
+# LINT: swallow here-doc to avoid false positives in content
+ echo wobba \
+ gorgo snoot \
+ wafta snurb <<-EOF &&
+ quoth the raven,
+ nevermore...
+ EOF
+
+# LINT: missing "&&" on 'cat'
+ cat <<EOF >bip
+ fish fly high
+ EOF
+
+# LINT: swallow here-doc (EOF is last line of subshell)
+ echo <<-\EOF >bop
+ gomez
+ morticia
+ wednesday
+ pugsly
+ EOF
+) &&
+(
+# LINT: swallow here-doc with arbitrary tag
+ cat <<-\ARBITRARY >bup &&
+ glink
+ FIZZ
+ ARBITRARY
+ cat <<-'ARBITRARY2' >bup2 &&
+ glink
+ FIZZ
+ ARBITRARY2
+ cat <<-"ARBITRARY3" >bup3 &&
+ glink
+ FIZZ
+ ARBITRARY3
+ meep
+)
diff --git a/t/chainlint/subshell-one-liner.expect b/t/chainlint/subshell-one-liner.expect
new file mode 100644
index 0000000000..51162821d7
--- /dev/null
+++ b/t/chainlint/subshell-one-liner.expect
@@ -0,0 +1,14 @@
+(
+ (foo && bar) &&
+ (foo && bar) |
+ (foo && bar) >baz &&
+?!SEMI?! (foo; bar) &&
+?!SEMI?! (foo; bar) |
+?!SEMI?! (foo; bar) >baz &&
+ (foo || exit 1) &&
+ (foo || exit 1) |
+ (foo || exit 1) >baz &&
+?!AMP?! (foo && bar)
+?!AMP?!?!SEMI?! (foo && bar; baz)
+ foobar
+>)
diff --git a/t/chainlint/subshell-one-liner.test b/t/chainlint/subshell-one-liner.test
new file mode 100644
index 0000000000..37fa643c20
--- /dev/null
+++ b/t/chainlint/subshell-one-liner.test
@@ -0,0 +1,24 @@
+(
+# LINT: nested one-liner subshell
+ (foo && bar) &&
+ (foo && bar) |
+ (foo && bar) >baz &&
+
+# LINT: nested one-liner subshell missing internal "&&"
+ (foo; bar) &&
+ (foo; bar) |
+ (foo; bar) >baz &&
+
+# LINT: nested one-liner subshell with "|| exit"
+ (foo || exit 1) &&
+ (foo || exit 1) |
+ (foo || exit 1) >baz &&
+
+# LINT: nested one-liner subshell lacking ending "&&"
+ (foo && bar)
+
+# LINT: nested one-liner subshell missing internal "&&" and lacking ending "&&"
+ (foo && bar; baz)
+
+ foobar
+)
diff --git a/t/chainlint/t7900-subtree.expect b/t/chainlint/t7900-subtree.expect
new file mode 100644
index 0000000000..c9913429e6
--- /dev/null
+++ b/t/chainlint/t7900-subtree.expect
@@ -0,0 +1,10 @@
+(
+ chks="sub1sub2sub3sub4" &&
+ chks_sub=$(cat | sed 's,^,sub dir/,'
+>>) &&
+ chkms="main-sub1main-sub2main-sub3main-sub4" &&
+ chkms_sub=$(cat | sed 's,^,sub dir/,'
+>>) &&
+ subfiles=$(git ls-files) &&
+ check_equal "$subfiles" "$chkms$chks"
+>)
diff --git a/t/chainlint/t7900-subtree.test b/t/chainlint/t7900-subtree.test
new file mode 100644
index 0000000000..277d8358df
--- /dev/null
+++ b/t/chainlint/t7900-subtree.test
@@ -0,0 +1,22 @@
+(
+ chks="sub1
+sub2
+sub3
+sub4" &&
+ chks_sub=$(cat <<TXT | sed 's,^,sub dir/,'
+$chks
+TXT
+) &&
+ chkms="main-sub1
+main-sub2
+main-sub3
+main-sub4" &&
+ chkms_sub=$(cat <<TXT | sed 's,^,sub dir/,'
+$chkms
+TXT
+) &&
+
+ subfiles=$(git ls-files) &&
+ check_equal "$subfiles" "$chkms
+$chks"
+)
diff --git a/t/chainlint/while-loop.expect b/t/chainlint/while-loop.expect
new file mode 100644
index 0000000000..13cff2c0a5
--- /dev/null
+++ b/t/chainlint/while-loop.expect
@@ -0,0 +1,11 @@
+(
+ while true
+ do
+?!AMP?! echo foo
+ cat
+?!AMP?! done
+ while true; do
+ echo foo &&
+ cat bar
+ done
+>)
diff --git a/t/chainlint/while-loop.test b/t/chainlint/while-loop.test
new file mode 100644
index 0000000000..f1df085bf0
--- /dev/null
+++ b/t/chainlint/while-loop.test
@@ -0,0 +1,19 @@
+(
+# LINT: 'while, 'do', 'done' do not need "&&"
+ while true
+ do
+# LINT: missing "&&" on 'echo'
+ echo foo
+# LINT: last statement of while does not need "&&"
+ cat <<-\EOF
+ bar
+ EOF
+# LINT: missing "&&" on 'done'
+ done
+
+# LINT: 'do' on same line as 'while'
+ while true; do
+ echo foo &&
+ cat bar
+ done
+)
diff --git a/t/check-non-portable-shell.pl b/t/check-non-portable-shell.pl
index d5823f71d8..b45bdac688 100755
--- a/t/check-non-portable-shell.pl
+++ b/t/check-non-portable-shell.pl
@@ -41,6 +41,9 @@ while (<>) {
/^\s*[^#]\s*which\s/ and err 'which is not portable (use type)';
/\btest\s+[^=]*==/ and err '"test a == b" is not portable (use =)';
/\bwc -l.*"\s*=/ and err '`"$(wc -l)"` is not portable (use test_line_count)';
+ /\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)';
/\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-delta.c b/t/helper/test-delta.c
index 34c7259248..e749a49c88 100644
--- a/t/helper/test-delta.c
+++ b/t/helper/test-delta.c
@@ -34,8 +34,8 @@ int cmd__delta(int argc, const char **argv)
return 1;
}
from_size = st.st_size;
- from_buf = mmap(NULL, from_size, PROT_READ, MAP_PRIVATE, fd, 0);
- if (from_buf == MAP_FAILED) {
+ from_buf = xmalloc(from_size);
+ if (read_in_full(fd, from_buf, from_size) < 0) {
perror(argv[2]);
close(fd);
return 1;
@@ -48,8 +48,8 @@ int cmd__delta(int argc, const char **argv)
return 1;
}
data_size = st.st_size;
- data_buf = mmap(NULL, data_size, PROT_READ, MAP_PRIVATE, fd, 0);
- if (data_buf == MAP_FAILED) {
+ data_buf = xmalloc(data_size);
+ if (read_in_full(fd, data_buf, data_size) < 0) {
perror(argv[3]);
close(fd);
return 1;
diff --git a/t/helper/test-drop-caches.c b/t/helper/test-drop-caches.c
index d6bcfddf13..f65e301f9d 100644
--- a/t/helper/test-drop-caches.c
+++ b/t/helper/test-drop-caches.c
@@ -16,8 +16,8 @@ static int cmd_sync(void)
if ((0 == dwRet) || (dwRet > MAX_PATH))
return error("Error getting current directory");
- if ((Buffer[0] < 'A') || (Buffer[0] > 'Z'))
- return error("Invalid drive letter '%c'", Buffer[0]);
+ if (!has_dos_drive_prefix(Buffer))
+ return error("'%s': invalid drive letter", Buffer);
szVolumeAccessPath[4] = Buffer[0];
hVolWrite = CreateFile(szVolumeAccessPath, GENERIC_READ | GENERIC_WRITE,
diff --git a/t/helper/test-dump-cache-tree.c b/t/helper/test-dump-cache-tree.c
index 98a4891f1d..6a3f88f5f5 100644
--- a/t/helper/test-dump-cache-tree.c
+++ b/t/helper/test-dump-cache-tree.c
@@ -33,7 +33,7 @@ static int dump_cache_tree(struct cache_tree *it,
}
else {
dump_one(it, pfx, "");
- if (oidcmp(&it->oid, &ref->oid) ||
+ if (!oideq(&it->oid, &ref->oid) ||
ref->entry_count != it->entry_count ||
ref->subtree_nr != it->subtree_nr) {
/* claims to be valid but is lying */
diff --git a/t/helper/test-json-writer.c b/t/helper/test-json-writer.c
new file mode 100644
index 0000000000..37c452535f
--- /dev/null
+++ b/t/helper/test-json-writer.c
@@ -0,0 +1,565 @@
+#include "test-tool.h"
+#include "cache.h"
+#include "json-writer.h"
+
+static const char *expect_obj1 = "{\"a\":\"abc\",\"b\":42,\"c\":true}";
+static const char *expect_obj2 = "{\"a\":-1,\"b\":2147483647,\"c\":0}";
+static const char *expect_obj3 = "{\"a\":0,\"b\":4294967295,\"c\":9223372036854775807}";
+static const char *expect_obj4 = "{\"t\":true,\"f\":false,\"n\":null}";
+static const char *expect_obj5 = "{\"abc\\tdef\":\"abc\\\\def\"}";
+static const char *expect_obj6 = "{\"a\":3.14}";
+
+static const char *pretty_obj1 = ("{\n"
+ " \"a\": \"abc\",\n"
+ " \"b\": 42,\n"
+ " \"c\": true\n"
+ "}");
+static const char *pretty_obj2 = ("{\n"
+ " \"a\": -1,\n"
+ " \"b\": 2147483647,\n"
+ " \"c\": 0\n"
+ "}");
+static const char *pretty_obj3 = ("{\n"
+ " \"a\": 0,\n"
+ " \"b\": 4294967295,\n"
+ " \"c\": 9223372036854775807\n"
+ "}");
+static const char *pretty_obj4 = ("{\n"
+ " \"t\": true,\n"
+ " \"f\": false,\n"
+ " \"n\": null\n"
+ "}");
+
+static struct json_writer obj1 = JSON_WRITER_INIT;
+static struct json_writer obj2 = JSON_WRITER_INIT;
+static struct json_writer obj3 = JSON_WRITER_INIT;
+static struct json_writer obj4 = JSON_WRITER_INIT;
+static struct json_writer obj5 = JSON_WRITER_INIT;
+static struct json_writer obj6 = JSON_WRITER_INIT;
+
+static void make_obj1(int pretty)
+{
+ jw_object_begin(&obj1, pretty);
+ {
+ jw_object_string(&obj1, "a", "abc");
+ jw_object_intmax(&obj1, "b", 42);
+ jw_object_true(&obj1, "c");
+ }
+ jw_end(&obj1);
+}
+
+static void make_obj2(int pretty)
+{
+ jw_object_begin(&obj2, pretty);
+ {
+ jw_object_intmax(&obj2, "a", -1);
+ jw_object_intmax(&obj2, "b", 0x7fffffff);
+ jw_object_intmax(&obj2, "c", 0);
+ }
+ jw_end(&obj2);
+}
+
+static void make_obj3(int pretty)
+{
+ jw_object_begin(&obj3, pretty);
+ {
+ jw_object_intmax(&obj3, "a", 0);
+ jw_object_intmax(&obj3, "b", 0xffffffff);
+ jw_object_intmax(&obj3, "c", 0x7fffffffffffffffULL);
+ }
+ jw_end(&obj3);
+}
+
+static void make_obj4(int pretty)
+{
+ jw_object_begin(&obj4, pretty);
+ {
+ jw_object_true(&obj4, "t");
+ jw_object_false(&obj4, "f");
+ jw_object_null(&obj4, "n");
+ }
+ jw_end(&obj4);
+}
+
+static void make_obj5(int pretty)
+{
+ jw_object_begin(&obj5, pretty);
+ {
+ jw_object_string(&obj5, "abc" "\x09" "def", "abc" "\\" "def");
+ }
+ jw_end(&obj5);
+}
+
+static void make_obj6(int pretty)
+{
+ jw_object_begin(&obj6, pretty);
+ {
+ jw_object_double(&obj6, "a", 2, 3.14159);
+ }
+ jw_end(&obj6);
+}
+
+static const char *expect_arr1 = "[\"abc\",42,true]";
+static const char *expect_arr2 = "[-1,2147483647,0]";
+static const char *expect_arr3 = "[0,4294967295,9223372036854775807]";
+static const char *expect_arr4 = "[true,false,null]";
+
+static const char *pretty_arr1 = ("[\n"
+ " \"abc\",\n"
+ " 42,\n"
+ " true\n"
+ "]");
+static const char *pretty_arr2 = ("[\n"
+ " -1,\n"
+ " 2147483647,\n"
+ " 0\n"
+ "]");
+static const char *pretty_arr3 = ("[\n"
+ " 0,\n"
+ " 4294967295,\n"
+ " 9223372036854775807\n"
+ "]");
+static const char *pretty_arr4 = ("[\n"
+ " true,\n"
+ " false,\n"
+ " null\n"
+ "]");
+
+static struct json_writer arr1 = JSON_WRITER_INIT;
+static struct json_writer arr2 = JSON_WRITER_INIT;
+static struct json_writer arr3 = JSON_WRITER_INIT;
+static struct json_writer arr4 = JSON_WRITER_INIT;
+
+static void make_arr1(int pretty)
+{
+ jw_array_begin(&arr1, pretty);
+ {
+ jw_array_string(&arr1, "abc");
+ jw_array_intmax(&arr1, 42);
+ jw_array_true(&arr1);
+ }
+ jw_end(&arr1);
+}
+
+static void make_arr2(int pretty)
+{
+ jw_array_begin(&arr2, pretty);
+ {
+ jw_array_intmax(&arr2, -1);
+ jw_array_intmax(&arr2, 0x7fffffff);
+ jw_array_intmax(&arr2, 0);
+ }
+ jw_end(&arr2);
+}
+
+static void make_arr3(int pretty)
+{
+ jw_array_begin(&arr3, pretty);
+ {
+ jw_array_intmax(&arr3, 0);
+ jw_array_intmax(&arr3, 0xffffffff);
+ jw_array_intmax(&arr3, 0x7fffffffffffffffULL);
+ }
+ jw_end(&arr3);
+}
+
+static void make_arr4(int pretty)
+{
+ jw_array_begin(&arr4, pretty);
+ {
+ jw_array_true(&arr4);
+ jw_array_false(&arr4);
+ jw_array_null(&arr4);
+ }
+ jw_end(&arr4);
+}
+
+static char *expect_nest1 =
+ "{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},\"arr1\":[\"abc\",42,true]}";
+
+static struct json_writer nest1 = JSON_WRITER_INIT;
+
+static void make_nest1(int pretty)
+{
+ jw_object_begin(&nest1, pretty);
+ {
+ jw_object_sub_jw(&nest1, "obj1", &obj1);
+ jw_object_sub_jw(&nest1, "arr1", &arr1);
+ }
+ jw_end(&nest1);
+}
+
+static char *expect_inline1 =
+ "{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},\"arr1\":[\"abc\",42,true]}";
+
+static char *pretty_inline1 =
+ ("{\n"
+ " \"obj1\": {\n"
+ " \"a\": \"abc\",\n"
+ " \"b\": 42,\n"
+ " \"c\": true\n"
+ " },\n"
+ " \"arr1\": [\n"
+ " \"abc\",\n"
+ " 42,\n"
+ " true\n"
+ " ]\n"
+ "}");
+
+static struct json_writer inline1 = JSON_WRITER_INIT;
+
+static void make_inline1(int pretty)
+{
+ jw_object_begin(&inline1, pretty);
+ {
+ jw_object_inline_begin_object(&inline1, "obj1");
+ {
+ jw_object_string(&inline1, "a", "abc");
+ jw_object_intmax(&inline1, "b", 42);
+ jw_object_true(&inline1, "c");
+ }
+ jw_end(&inline1);
+ jw_object_inline_begin_array(&inline1, "arr1");
+ {
+ jw_array_string(&inline1, "abc");
+ jw_array_intmax(&inline1, 42);
+ jw_array_true(&inline1);
+ }
+ jw_end(&inline1);
+ }
+ jw_end(&inline1);
+}
+
+static char *expect_inline2 =
+ "[[1,2],[3,4],{\"a\":\"abc\"}]";
+
+static char *pretty_inline2 =
+ ("[\n"
+ " [\n"
+ " 1,\n"
+ " 2\n"
+ " ],\n"
+ " [\n"
+ " 3,\n"
+ " 4\n"
+ " ],\n"
+ " {\n"
+ " \"a\": \"abc\"\n"
+ " }\n"
+ "]");
+
+static struct json_writer inline2 = JSON_WRITER_INIT;
+
+static void make_inline2(int pretty)
+{
+ jw_array_begin(&inline2, pretty);
+ {
+ jw_array_inline_begin_array(&inline2);
+ {
+ jw_array_intmax(&inline2, 1);
+ jw_array_intmax(&inline2, 2);
+ }
+ jw_end(&inline2);
+ jw_array_inline_begin_array(&inline2);
+ {
+ jw_array_intmax(&inline2, 3);
+ jw_array_intmax(&inline2, 4);
+ }
+ jw_end(&inline2);
+ jw_array_inline_begin_object(&inline2);
+ {
+ jw_object_string(&inline2, "a", "abc");
+ }
+ jw_end(&inline2);
+ }
+ jw_end(&inline2);
+}
+
+/*
+ * When super is compact, we expect subs to be compacted (even if originally
+ * pretty).
+ */
+static const char *expect_mixed1 =
+ ("{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},"
+ "\"arr1\":[\"abc\",42,true]}");
+
+/*
+ * When super is pretty, a compact sub (obj1) is kept compact and a pretty
+ * sub (arr1) is re-indented.
+ */
+static const char *pretty_mixed1 =
+ ("{\n"
+ " \"obj1\": {\"a\":\"abc\",\"b\":42,\"c\":true},\n"
+ " \"arr1\": [\n"
+ " \"abc\",\n"
+ " 42,\n"
+ " true\n"
+ " ]\n"
+ "}");
+
+static struct json_writer mixed1 = JSON_WRITER_INIT;
+
+static void make_mixed1(int pretty)
+{
+ jw_init(&obj1);
+ jw_init(&arr1);
+
+ make_obj1(0); /* obj1 is compact */
+ make_arr1(1); /* arr1 is pretty */
+
+ jw_object_begin(&mixed1, pretty);
+ {
+ jw_object_sub_jw(&mixed1, "obj1", &obj1);
+ jw_object_sub_jw(&mixed1, "arr1", &arr1);
+ }
+ jw_end(&mixed1);
+}
+
+static void cmp(const char *test, const struct json_writer *jw, const char *exp)
+{
+ if (!strcmp(jw->json.buf, exp))
+ return;
+
+ printf("error[%s]: observed '%s' expected '%s'\n",
+ test, jw->json.buf, exp);
+ exit(1);
+}
+
+#define t(v) do { make_##v(0); cmp(#v, &v, expect_##v); } while (0)
+#define p(v) do { make_##v(1); cmp(#v, &v, pretty_##v); } while (0)
+
+/*
+ * Run some basic regression tests with some known patterns.
+ * These tests also demonstrate how to use the jw_ API.
+ */
+static int unit_tests(void)
+{
+ /* comptact (canonical) forms */
+ t(obj1);
+ t(obj2);
+ t(obj3);
+ t(obj4);
+ t(obj5);
+ t(obj6);
+
+ t(arr1);
+ t(arr2);
+ t(arr3);
+ t(arr4);
+
+ t(nest1);
+
+ t(inline1);
+ t(inline2);
+
+ jw_init(&obj1);
+ jw_init(&obj2);
+ jw_init(&obj3);
+ jw_init(&obj4);
+
+ jw_init(&arr1);
+ jw_init(&arr2);
+ jw_init(&arr3);
+ jw_init(&arr4);
+
+ jw_init(&inline1);
+ jw_init(&inline2);
+
+ /* pretty forms */
+ p(obj1);
+ p(obj2);
+ p(obj3);
+ p(obj4);
+
+ p(arr1);
+ p(arr2);
+ p(arr3);
+ p(arr4);
+
+ p(inline1);
+ p(inline2);
+
+ /* mixed forms */
+ t(mixed1);
+ jw_init(&mixed1);
+ p(mixed1);
+
+ return 0;
+}
+
+static void get_s(int line_nr, char **s_in)
+{
+ *s_in = strtok(NULL, " ");
+ if (!*s_in)
+ die("line[%d]: expected: <s>", line_nr);
+}
+
+static void get_i(int line_nr, intmax_t *s_in)
+{
+ char *s;
+ char *endptr;
+
+ get_s(line_nr, &s);
+
+ *s_in = strtol(s, &endptr, 10);
+ if (*endptr || errno == ERANGE)
+ die("line[%d]: invalid integer value", line_nr);
+}
+
+static void get_d(int line_nr, double *s_in)
+{
+ char *s;
+ char *endptr;
+
+ get_s(line_nr, &s);
+
+ *s_in = strtod(s, &endptr);
+ if (*endptr || errno == ERANGE)
+ die("line[%d]: invalid float value", line_nr);
+}
+
+static int pretty;
+
+#define MAX_LINE_LENGTH (64 * 1024)
+
+static char *get_trimmed_line(char *buf, int buf_size)
+{
+ int len;
+
+ if (!fgets(buf, buf_size, stdin))
+ return NULL;
+
+ len = strlen(buf);
+ while (len > 0) {
+ char c = buf[len - 1];
+ if (c == '\n' || c == '\r' || c == ' ' || c == '\t')
+ buf[--len] = 0;
+ else
+ break;
+ }
+
+ while (*buf == ' ' || *buf == '\t')
+ buf++;
+
+ return buf;
+}
+
+static int scripted(void)
+{
+ struct json_writer jw = JSON_WRITER_INIT;
+ char buf[MAX_LINE_LENGTH];
+ char *line;
+ int line_nr = 0;
+
+ line = get_trimmed_line(buf, MAX_LINE_LENGTH);
+ if (!line)
+ return 0;
+
+ if (!strcmp(line, "object"))
+ jw_object_begin(&jw, pretty);
+ else if (!strcmp(line, "array"))
+ jw_array_begin(&jw, pretty);
+ else
+ die("expected first line to be 'object' or 'array'");
+
+ while ((line = get_trimmed_line(buf, MAX_LINE_LENGTH)) != NULL) {
+ char *verb;
+ char *key;
+ char *s_value;
+ intmax_t i_value;
+ double d_value;
+
+ line_nr++;
+
+ verb = strtok(line, " ");
+
+ if (!strcmp(verb, "end")) {
+ jw_end(&jw);
+ }
+ else if (!strcmp(verb, "object-string")) {
+ get_s(line_nr, &key);
+ get_s(line_nr, &s_value);
+ jw_object_string(&jw, key, s_value);
+ }
+ else if (!strcmp(verb, "object-int")) {
+ get_s(line_nr, &key);
+ get_i(line_nr, &i_value);
+ jw_object_intmax(&jw, key, i_value);
+ }
+ else if (!strcmp(verb, "object-double")) {
+ get_s(line_nr, &key);
+ get_i(line_nr, &i_value);
+ get_d(line_nr, &d_value);
+ jw_object_double(&jw, key, i_value, d_value);
+ }
+ else if (!strcmp(verb, "object-true")) {
+ get_s(line_nr, &key);
+ jw_object_true(&jw, key);
+ }
+ else if (!strcmp(verb, "object-false")) {
+ get_s(line_nr, &key);
+ jw_object_false(&jw, key);
+ }
+ else if (!strcmp(verb, "object-null")) {
+ get_s(line_nr, &key);
+ jw_object_null(&jw, key);
+ }
+ else if (!strcmp(verb, "object-object")) {
+ get_s(line_nr, &key);
+ jw_object_inline_begin_object(&jw, key);
+ }
+ else if (!strcmp(verb, "object-array")) {
+ get_s(line_nr, &key);
+ jw_object_inline_begin_array(&jw, key);
+ }
+ else if (!strcmp(verb, "array-string")) {
+ get_s(line_nr, &s_value);
+ jw_array_string(&jw, s_value);
+ }
+ else if (!strcmp(verb, "array-int")) {
+ get_i(line_nr, &i_value);
+ jw_array_intmax(&jw, i_value);
+ }
+ else if (!strcmp(verb, "array-double")) {
+ get_i(line_nr, &i_value);
+ get_d(line_nr, &d_value);
+ jw_array_double(&jw, i_value, d_value);
+ }
+ else if (!strcmp(verb, "array-true"))
+ jw_array_true(&jw);
+ else if (!strcmp(verb, "array-false"))
+ jw_array_false(&jw);
+ else if (!strcmp(verb, "array-null"))
+ jw_array_null(&jw);
+ else if (!strcmp(verb, "array-object"))
+ jw_array_inline_begin_object(&jw);
+ else if (!strcmp(verb, "array-array"))
+ jw_array_inline_begin_array(&jw);
+ else
+ die("unrecognized token: '%s'", verb);
+ }
+
+ if (!jw_is_terminated(&jw))
+ die("json not terminated: '%s'", jw.json.buf);
+
+ printf("%s\n", jw.json.buf);
+
+ strbuf_release(&jw.json);
+ return 0;
+}
+
+int cmd__json_writer(int argc, const char **argv)
+{
+ argc--; /* skip over "json-writer" arg */
+ argv++;
+
+ if (argc > 0 && argv[0][0] == '-') {
+ if (!strcmp(argv[0], "-u") || !strcmp(argv[0], "--unit"))
+ return unit_tests();
+
+ if (!strcmp(argv[0], "-p") || !strcmp(argv[0], "--pretty"))
+ pretty = 1;
+ }
+
+ return scripted();
+}
diff --git a/t/helper/test-reach.c b/t/helper/test-reach.c
new file mode 100644
index 0000000000..eb21103998
--- /dev/null
+++ b/t/helper/test-reach.c
@@ -0,0 +1,130 @@
+#include "test-tool.h"
+#include "cache.h"
+#include "commit.h"
+#include "commit-reach.h"
+#include "config.h"
+#include "parse-options.h"
+#include "ref-filter.h"
+#include "string-list.h"
+#include "tag.h"
+
+static void print_sorted_commit_ids(struct commit_list *list)
+{
+ int i;
+ struct string_list s = STRING_LIST_INIT_DUP;
+
+ while (list) {
+ string_list_append(&s, oid_to_hex(&list->item->object.oid));
+ list = list->next;
+ }
+
+ string_list_sort(&s);
+
+ for (i = 0; i < s.nr; i++)
+ printf("%s\n", s.items[i].string);
+
+ string_list_clear(&s, 0);
+}
+
+int cmd__reach(int ac, const char **av)
+{
+ struct object_id oid_A, oid_B;
+ struct commit *A, *B;
+ struct commit_list *X, *Y;
+ struct commit **X_array;
+ int X_nr, X_alloc;
+ struct strbuf buf = STRBUF_INIT;
+ struct repository *r = the_repository;
+
+ setup_git_directory();
+
+ if (ac < 2)
+ exit(1);
+
+ A = B = NULL;
+ X = Y = NULL;
+ X_nr = 0;
+ X_alloc = 16;
+ ALLOC_ARRAY(X_array, X_alloc);
+
+ while (strbuf_getline(&buf, stdin) != EOF) {
+ struct object_id oid;
+ struct object *o;
+ struct commit *c;
+ if (buf.len < 3)
+ continue;
+
+ if (get_oid_committish(buf.buf + 2, &oid))
+ die("failed to resolve %s", buf.buf + 2);
+
+ o = parse_object(r, &oid);
+ o = deref_tag_noverify(o);
+
+ if (!o)
+ die("failed to load commit for input %s resulting in oid %s\n",
+ buf.buf, oid_to_hex(&oid));
+
+ c = object_as_type(r, o, OBJ_COMMIT, 0);
+
+ if (!c)
+ die("failed to load commit for input %s resulting in oid %s\n",
+ buf.buf, oid_to_hex(&oid));
+
+ switch (buf.buf[0]) {
+ case 'A':
+ oidcpy(&oid_A, &oid);
+ A = c;
+ break;
+
+ case 'B':
+ oidcpy(&oid_B, &oid);
+ B = c;
+ break;
+
+ case 'X':
+ commit_list_insert(c, &X);
+ ALLOC_GROW(X_array, X_nr + 1, X_alloc);
+ X_array[X_nr++] = c;
+ break;
+
+ case 'Y':
+ commit_list_insert(c, &Y);
+ break;
+
+ default:
+ die("unexpected start of line: %c", buf.buf[0]);
+ }
+ }
+ strbuf_release(&buf);
+
+ if (!strcmp(av[1], "ref_newer"))
+ printf("%s(A,B):%d\n", av[1], ref_newer(&oid_A, &oid_B));
+ else if (!strcmp(av[1], "in_merge_bases"))
+ printf("%s(A,B):%d\n", av[1], in_merge_bases(A, B));
+ else if (!strcmp(av[1], "is_descendant_of"))
+ printf("%s(A,X):%d\n", av[1], is_descendant_of(A, X));
+ else if (!strcmp(av[1], "get_merge_bases_many")) {
+ struct commit_list *list = get_merge_bases_many(A, X_nr, X_array);
+ printf("%s(A,X):\n", av[1]);
+ print_sorted_commit_ids(list);
+ } else if (!strcmp(av[1], "reduce_heads")) {
+ struct commit_list *list = reduce_heads(X);
+ printf("%s(X):\n", av[1]);
+ print_sorted_commit_ids(list);
+ } else if (!strcmp(av[1], "can_all_from_reach")) {
+ printf("%s(X,Y):%d\n", av[1], can_all_from_reach(X, Y, 1));
+ } else if (!strcmp(av[1], "commit_contains")) {
+ struct ref_filter filter;
+ struct contains_cache cache;
+ init_contains_cache(&cache);
+
+ if (ac > 2 && !strcmp(av[2], "--tag"))
+ filter.with_commit_tag_algo = 1;
+ else
+ filter.with_commit_tag_algo = 0;
+
+ printf("%s(_,A,X,_):%d\n", av[1], commit_contains(&filter, A, X, &cache));
+ }
+
+ exit(0);
+}
diff --git a/t/helper/test-read-midx.c b/t/helper/test-read-midx.c
new file mode 100644
index 0000000000..831b586d02
--- /dev/null
+++ b/t/helper/test-read-midx.c
@@ -0,0 +1,51 @@
+#include "test-tool.h"
+#include "cache.h"
+#include "midx.h"
+#include "repository.h"
+#include "object-store.h"
+
+static int read_midx_file(const char *object_dir)
+{
+ uint32_t i;
+ struct multi_pack_index *m = load_multi_pack_index(object_dir, 1);
+
+ if (!m)
+ return 1;
+
+ printf("header: %08x %d %d %d\n",
+ m->signature,
+ m->version,
+ m->num_chunks,
+ m->num_packs);
+
+ printf("chunks:");
+
+ if (m->chunk_pack_names)
+ printf(" pack-names");
+ if (m->chunk_oid_fanout)
+ printf(" oid-fanout");
+ if (m->chunk_oid_lookup)
+ printf(" oid-lookup");
+ if (m->chunk_object_offsets)
+ printf(" object-offsets");
+ if (m->chunk_large_offsets)
+ printf(" large-offsets");
+
+ printf("\nnum_objects: %d\n", m->num_objects);
+
+ printf("packs:\n");
+ for (i = 0; i < m->num_packs; i++)
+ printf("%s\n", m->pack_names[i]);
+
+ printf("object-dir: %s\n", m->object_dir);
+
+ return 0;
+}
+
+int cmd__read_midx(int argc, const char **argv)
+{
+ if (argc != 2)
+ usage("read-midx <object-dir>");
+
+ return read_midx_file(argv[1]);
+}
diff --git a/t/helper/test-repository.c b/t/helper/test-repository.c
new file mode 100644
index 0000000000..2762ca6562
--- /dev/null
+++ b/t/helper/test-repository.c
@@ -0,0 +1,82 @@
+#include "test-tool.h"
+#include "cache.h"
+#include "commit-graph.h"
+#include "commit.h"
+#include "config.h"
+#include "object-store.h"
+#include "object.h"
+#include "repository.h"
+#include "tree.h"
+
+static void test_parse_commit_in_graph(const char *gitdir, const char *worktree,
+ const struct object_id *commit_oid)
+{
+ struct repository r;
+ struct commit *c;
+ struct commit_list *parent;
+
+ repo_init(&r, gitdir, worktree);
+
+ c = lookup_commit(&r, commit_oid);
+
+ if (!parse_commit_in_graph(&r, c))
+ die("Couldn't parse commit");
+
+ printf("%"PRItime, c->date);
+ for (parent = c->parents; parent; parent = parent->next)
+ printf(" %s", oid_to_hex(&parent->item->object.oid));
+ printf("\n");
+
+ repo_clear(&r);
+}
+
+static void test_get_commit_tree_in_graph(const char *gitdir,
+ const char *worktree,
+ const struct object_id *commit_oid)
+{
+ struct repository r;
+ struct commit *c;
+ struct tree *tree;
+
+ repo_init(&r, gitdir, worktree);
+
+ c = lookup_commit(&r, commit_oid);
+
+ /*
+ * get_commit_tree_in_graph does not automatically parse the commit, so
+ * parse it first.
+ */
+ if (!parse_commit_in_graph(&r, c))
+ die("Couldn't parse commit");
+ tree = get_commit_tree_in_graph(&r, c);
+ if (!tree)
+ die("Couldn't get commit tree");
+
+ printf("%s\n", oid_to_hex(&tree->object.oid));
+
+ repo_clear(&r);
+}
+
+int cmd__repository(int argc, const char **argv)
+{
+ if (argc < 2)
+ die("must have at least 2 arguments");
+ if (!strcmp(argv[1], "parse_commit_in_graph")) {
+ struct object_id oid;
+ if (argc < 5)
+ die("not enough arguments");
+ if (parse_oid_hex(argv[4], &oid, &argv[4]))
+ die("cannot parse oid '%s'", argv[4]);
+ test_parse_commit_in_graph(argv[2], argv[3], &oid);
+ } else if (!strcmp(argv[1], "get_commit_tree_in_graph")) {
+ struct object_id oid;
+ if (argc < 5)
+ die("not enough arguments");
+ if (parse_oid_hex(argv[4], &oid, &argv[4]))
+ die("cannot parse oid '%s'", argv[4]);
+ test_get_commit_tree_in_graph(argv[2], argv[3], &oid);
+ } else {
+ die("unrecognized '%s'", argv[1]);
+ }
+ return 0;
+}
diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c
index 805a45de9c..bef50c4dcc 100644
--- a/t/helper/test-tool.c
+++ b/t/helper/test-tool.c
@@ -19,6 +19,7 @@ static struct test_cmd cmds[] = {
{ "genrandom", cmd__genrandom },
{ "hashmap", cmd__hashmap },
{ "index-version", cmd__index_version },
+ { "json-writer", cmd__json_writer },
{ "lazy-init-name-hash", cmd__lazy_init_name_hash },
{ "match-trees", cmd__match_trees },
{ "mergesort", cmd__mergesort },
@@ -26,9 +27,12 @@ static struct test_cmd cmds[] = {
{ "online-cpus", cmd__online_cpus },
{ "path-utils", cmd__path_utils },
{ "prio-queue", cmd__prio_queue },
+ { "reach", cmd__reach },
{ "read-cache", cmd__read_cache },
+ { "read-midx", cmd__read_midx },
{ "ref-store", cmd__ref_store },
{ "regex", cmd__regex },
+ { "repository", cmd__repository },
{ "revision-walking", cmd__revision_walking },
{ "run-command", cmd__run_command },
{ "scrap-cache-tree", cmd__scrap_cache_tree },
diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h
index 7116ddfb94..321982e4bc 100644
--- a/t/helper/test-tool.h
+++ b/t/helper/test-tool.h
@@ -1,6 +1,8 @@
#ifndef __TEST_TOOL_H__
#define __TEST_TOOL_H__
+#include "git-compat-util.h"
+
int cmd__chmtime(int argc, const char **argv);
int cmd__config(int argc, const char **argv);
int cmd__ctype(int argc, const char **argv);
@@ -13,6 +15,7 @@ int cmd__example_decorate(int argc, const char **argv);
int cmd__genrandom(int argc, const char **argv);
int cmd__hashmap(int argc, const char **argv);
int cmd__index_version(int argc, const char **argv);
+int cmd__json_writer(int argc, const char **argv);
int cmd__lazy_init_name_hash(int argc, const char **argv);
int cmd__match_trees(int argc, const char **argv);
int cmd__mergesort(int argc, const char **argv);
@@ -20,9 +23,12 @@ int cmd__mktemp(int argc, const char **argv);
int cmd__online_cpus(int argc, const char **argv);
int cmd__path_utils(int argc, const char **argv);
int cmd__prio_queue(int argc, const char **argv);
+int cmd__reach(int argc, const char **argv);
int cmd__read_cache(int argc, const char **argv);
+int cmd__read_midx(int argc, const char **argv);
int cmd__ref_store(int argc, const char **argv);
int cmd__regex(int argc, const char **argv);
+int cmd__repository(int argc, const char **argv);
int cmd__revision_walking(int argc, const char **argv);
int cmd__run_command(int argc, const char **argv);
int cmd__scrap_cache_tree(int argc, const char **argv);
diff --git a/t/lib-gpg.sh b/t/lib-gpg.sh
index a5d3b2cbaa..3fe02876c1 100755
--- a/t/lib-gpg.sh
+++ b/t/lib-gpg.sh
@@ -38,7 +38,33 @@ then
"$TEST_DIRECTORY"/lib-gpg/ownertrust &&
gpg --homedir "${GNUPGHOME}" </dev/null >/dev/null 2>&1 \
--sign -u committer@example.com &&
- test_set_prereq GPG
+ test_set_prereq GPG &&
+ # Available key info:
+ # * see t/lib-gpg/gpgsm-gen-key.in
+ # To generate new certificate:
+ # * no passphrase
+ # gpgsm --homedir /tmp/gpghome/ \
+ # -o /tmp/gpgsm.crt.user \
+ # --generate-key \
+ # --batch t/lib-gpg/gpgsm-gen-key.in
+ # To import certificate:
+ # gpgsm --homedir /tmp/gpghome/ \
+ # --import /tmp/gpgsm.crt.user
+ # To export into a .p12 we can later import:
+ # gpgsm --homedir /tmp/gpghome/ \
+ # -o t/lib-gpg/gpgsm_cert.p12 \
+ # --export-secret-key-p12 "committer@example.com"
+ echo | gpgsm --homedir "${GNUPGHOME}" 2>/dev/null \
+ --passphrase-fd 0 --pinentry-mode loopback \
+ --import "$TEST_DIRECTORY"/lib-gpg/gpgsm_cert.p12 &&
+ gpgsm --homedir "${GNUPGHOME}" 2>/dev/null -K \
+ | grep fingerprint: | cut -d" " -f4 | tr -d '\n' > \
+ ${GNUPGHOME}/trustlist.txt &&
+ echo " S relax" >> ${GNUPGHOME}/trustlist.txt &&
+ (gpgconf --kill gpg-agent >/dev/null 2>&1 || : ) &&
+ echo hello | gpgsm --homedir "${GNUPGHOME}" >/dev/null \
+ -u committer@example.com -o /dev/null --sign - 2>&1 &&
+ test_set_prereq GPGSM
;;
esac
fi
diff --git a/t/lib-gpg/gpgsm-gen-key.in b/t/lib-gpg/gpgsm-gen-key.in
new file mode 100644
index 0000000000..a7fd87c069
--- /dev/null
+++ b/t/lib-gpg/gpgsm-gen-key.in
@@ -0,0 +1,8 @@
+Key-Type: RSA
+Key-Length: 2048
+Key-Usage: sign
+Serial: random
+Name-DN: CN=C O Mitter, O=Example, SN=C O, GN=Mitter
+Name-Email: committer@example.com
+Not-Before: 1970-01-01 00:00:00
+Not-After: 3000-01-01 00:00:00
diff --git a/t/lib-gpg/gpgsm_cert.p12 b/t/lib-gpg/gpgsm_cert.p12
new file mode 100644
index 0000000000..94ffad0d31
--- /dev/null
+++ b/t/lib-gpg/gpgsm_cert.p12
Binary files differ
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index ed41b155af..a8729f8232 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -288,3 +288,24 @@ expect_askpass() {
test_cmp "$TRASH_DIRECTORY/askpass-expect" \
"$TRASH_DIRECTORY/askpass-query"
}
+
+strip_access_log() {
+ sed -e "
+ s/^.* \"//
+ s/\"//
+ s/ [1-9][0-9]*\$//
+ s/^GET /GET /
+ " "$HTTPD_ROOT_PATH"/access.log
+}
+
+# Requires one argument: the name of a file containing the expected stripped
+# access log entries.
+check_access_log() {
+ sort "$1" >"$1".sorted &&
+ strip_access_log >access.log.stripped &&
+ sort access.log.stripped >access.log.sorted &&
+ if ! test_cmp "$1".sorted access.log.sorted
+ then
+ test_cmp "$1" access.log.stripped
+ fi
+}
diff --git a/t/lib-rebase.sh b/t/lib-rebase.sh
index 25a77ee5cb..2ca9fb69d6 100644
--- a/t/lib-rebase.sh
+++ b/t/lib-rebase.sh
@@ -14,8 +14,8 @@
# specified line.
#
# "<cmd> <lineno>" -- add a line with the specified command
-# ("squash", "fixup", "edit", "reword" or "drop") and the SHA1 taken
-# from the specified line.
+# ("pick", "squash", "fixup", "edit", "reword" or "drop") and the
+# SHA1 taken from the specified line.
#
# "exec_cmd_with_args" -- add an "exec cmd with args" line.
#
@@ -47,7 +47,7 @@ set_fake_editor () {
action=pick
for line in $FAKE_LINES; do
case $line in
- squash|fixup|edit|reword|drop)
+ pick|squash|fixup|edit|reword|drop)
action="$line";;
exec*)
echo "$line" | sed 's/_/ /g' >> "$1";;
diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh
index be78cdc1ff..016391723c 100755
--- a/t/lib-submodule-update.sh
+++ b/t/lib-submodule-update.sh
@@ -235,7 +235,7 @@ reset_work_tree_to_interested () {
then
mkdir -p submodule_update/.git/modules/sub1/modules &&
cp -r submodule_update_repo/.git/modules/sub1/modules/sub2 submodule_update/.git/modules/sub1/modules/sub2
- # core.worktree is unset for sub2 as it is not checked out
+ GIT_WORK_TREE=. git -C submodule_update/.git/modules/sub1/modules/sub2 config --unset core.worktree
fi &&
# indicate we are interested in the submodule:
git -C submodule_update config submodule.sub1.url "bogus" &&
@@ -709,8 +709,7 @@ test_submodule_recursing_with_args_common() {
git branch -t remove_sub1 origin/remove_sub1 &&
$command remove_sub1 &&
test_superproject_content origin/remove_sub1 &&
- ! test -e sub1 &&
- test_must_fail git config -f .git/modules/sub1/config core.worktree
+ ! test -e sub1
)
'
# ... absorbing a .git directory along the way.
@@ -756,7 +755,7 @@ test_submodule_recursing_with_args_common() {
: >sub1/untrackedfile &&
test_must_fail $command replace_sub1_with_file &&
test_superproject_content origin/add_sub1 &&
- test_submodule_content sub1 origin/add_sub1
+ test_submodule_content sub1 origin/add_sub1 &&
test -f sub1/untracked_file
)
'
@@ -844,7 +843,7 @@ test_submodule_switch_recursing_with_args () {
cd submodule_update &&
git branch -t add_sub1 origin/add_sub1 &&
: >sub1 &&
- echo sub1 >.git/info/exclude
+ echo sub1 >.git/info/exclude &&
$command add_sub1 &&
test_superproject_content origin/add_sub1 &&
test_submodule_content sub1 origin/add_sub1
@@ -971,7 +970,6 @@ test_submodule_forced_switch_recursing_with_args () {
rm -rf .git/modules/sub1 &&
$command replace_sub1_with_directory &&
test_superproject_content origin/replace_sub1_with_directory &&
- test_submodule_content sub1 origin/modify_sub1
test_git_directory_exists sub1
)
'
diff --git a/t/perf/README b/t/perf/README
index 21321a0f36..be12090c38 100644
--- a/t/perf/README
+++ b/t/perf/README
@@ -168,3 +168,28 @@ that
While we have tried to make sure that it can cope with embedded
whitespace and other special characters, it will not work with
multi-line data.
+
+Rather than tracking the performance by run-time as `test_perf` does, you
+may also track output size by using `test_size`. The stdout of the
+function should be a single numeric value, which will be captured and
+shown in the aggregated output. For example:
+
+ test_perf 'time foo' '
+ ./foo >foo.out
+ '
+
+ test_size 'output size'
+ wc -c <foo.out
+ '
+
+might produce output like:
+
+ Test origin HEAD
+ -------------------------------------------------------------
+ 1234.1 time foo 0.37(0.79+0.02) 0.26(0.51+0.02) -29.7%
+ 1234.2 output size 4.3M 3.6M -14.7%
+
+The item being measured (and its units) is up to the test; the context
+and the test title should make it clear to the user whether bigger or
+smaller numbers are better. Unlike test_perf, the test code will only be
+run once, since output sizes tend to be more deterministic than timings.
diff --git a/t/perf/aggregate.perl b/t/perf/aggregate.perl
index bc865160e7..494907a892 100755
--- a/t/perf/aggregate.perl
+++ b/t/perf/aggregate.perl
@@ -13,27 +13,42 @@ sub get_times {
my $line = <$fh>;
return undef if not defined $line;
close $fh or die "cannot close $name: $!";
- $line =~ /^(?:(\d+):)?(\d+):(\d+(?:\.\d+)?) (\d+(?:\.\d+)?) (\d+(?:\.\d+)?)$/
- or die "bad input line: $line";
- my $rt = ((defined $1 ? $1 : 0.0)*60+$2)*60+$3;
- return ($rt, $4, $5);
+ # times
+ if ($line =~ /^(?:(\d+):)?(\d+):(\d+(?:\.\d+)?) (\d+(?:\.\d+)?) (\d+(?:\.\d+)?)$/) {
+ my $rt = ((defined $1 ? $1 : 0.0)*60+$2)*60+$3;
+ return ($rt, $4, $5);
+ # size
+ } elsif ($line =~ /^\d+$/) {
+ return $&;
+ } else {
+ die "bad input line: $line";
+ }
+}
+
+sub relative_change {
+ my ($r, $firstr) = @_;
+ if ($firstr > 0) {
+ return sprintf "%+.1f%%", 100.0*($r-$firstr)/$firstr;
+ } elsif ($r == 0) {
+ return "=";
+ } else {
+ return "+inf";
+ }
}
sub format_times {
my ($r, $u, $s, $firstr) = @_;
+ # no value means we did not finish the test
if (!defined $r) {
return "<missing>";
}
- my $out = sprintf "%.2f(%.2f+%.2f)", $r, $u, $s;
- if (defined $firstr) {
- if ($firstr > 0) {
- $out .= sprintf " %+.1f%%", 100.0*($r-$firstr)/$firstr;
- } elsif ($r == 0) {
- $out .= " =";
- } else {
- $out .= " +inf";
- }
+ # a single value means we have a size, not times
+ if (!defined $u) {
+ return format_size($r, $firstr);
}
+ # otherwise, we have real/user/system times
+ my $out = sprintf "%.2f(%.2f+%.2f)", $r, $u, $s;
+ $out .= ' ' . relative_change($r, $firstr) if defined $firstr;
return $out;
}
@@ -51,6 +66,25 @@ EOT
exit(1);
}
+sub human_size {
+ my $n = shift;
+ my @units = ('', qw(K M G));
+ while ($n > 900 && @units > 1) {
+ $n /= 1000;
+ shift @units;
+ }
+ return $n unless length $units[0];
+ return sprintf '%.1f%s', $n, $units[0];
+}
+
+sub format_size {
+ my ($size, $first) = @_;
+ # match the width of a time: 0.00(0.00+0.00)
+ my $out = sprintf '%15s', human_size($size);
+ $out .= ' ' . relative_change($size, $first) if defined $first;
+ return $out;
+}
+
my (@dirs, %dirnames, %dirabbrevs, %prefixes, @tests,
$codespeed, $sortby, $subsection, $reponame);
@@ -181,7 +215,14 @@ sub print_default_results {
my $firstr;
for my $i (0..$#dirs) {
my $d = $dirs[$i];
- $times{$prefixes{$d}.$t} = [get_times("$resultsdir/$prefixes{$d}$t.times")];
+ my $base = "$resultsdir/$prefixes{$d}$t";
+ $times{$prefixes{$d}.$t} = [];
+ foreach my $type (qw(times size)) {
+ if (-e "$base.$type") {
+ $times{$prefixes{$d}.$t} = [get_times("$base.$type")];
+ last;
+ }
+ }
my ($r,$u,$s) = @{$times{$prefixes{$d}.$t}};
my $w = length format_times($r,$u,$s,$firstr);
$colwidth[$i] = $w if $w > $colwidth[$i];
diff --git a/t/perf/p5311-pack-bitmaps-fetch.sh b/t/perf/p5311-pack-bitmaps-fetch.sh
new file mode 100755
index 0000000000..b04575951f
--- /dev/null
+++ b/t/perf/p5311-pack-bitmaps-fetch.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+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 config pack.writebitmaphashcache 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_perf "server $title" '
+ git pack-objects --stdout --revs \
+ --thin --delta-base-offset \
+ <revs >tmp.pack
+ '
+
+ test_size "size $title" '
+ wc -c <tmp.pack
+ '
+
+ test_perf "client $title" '
+ git index-pack --stdin --fix-thin <tmp.pack
+ '
+done
+
+test_done
diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh
index e4c343a6b7..11d1922cf5 100644
--- a/t/perf/perf-lib.sh
+++ b/t/perf/perf-lib.sh
@@ -179,8 +179,8 @@ exit $ret' >&3 2>&4
return "$eval_ret"
}
-
-test_perf () {
+test_wrapper_ () {
+ test_wrapper_func_=$1; shift
test_start_
test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
test "$#" = 2 ||
@@ -191,35 +191,57 @@ test_perf () {
base=$(basename "$0" .sh)
echo "$test_count" >>"$perf_results_dir"/$base.subtests
echo "$1" >"$perf_results_dir"/$base.$test_count.descr
- if test -z "$verbose"; then
- printf "%s" "perf $test_count - $1:"
- else
- echo "perf $test_count - $1:"
- fi
- for i in $(test_seq 1 $GIT_PERF_REPEAT_COUNT); do
- say >&3 "running: $2"
- if test_run_perf_ "$2"
- then
- if test -z "$verbose"; then
- printf " %s" "$i"
- else
- echo "* timing run $i/$GIT_PERF_REPEAT_COUNT:"
- fi
+ base="$perf_results_dir"/"$perf_results_prefix$(basename "$0" .sh)"."$test_count"
+ "$test_wrapper_func_" "$@"
+ fi
+
+ test_finish_
+}
+
+test_perf_ () {
+ if test -z "$verbose"; then
+ printf "%s" "perf $test_count - $1:"
+ else
+ echo "perf $test_count - $1:"
+ fi
+ for i in $(test_seq 1 $GIT_PERF_REPEAT_COUNT); do
+ say >&3 "running: $2"
+ if test_run_perf_ "$2"
+ then
+ if test -z "$verbose"; then
+ printf " %s" "$i"
else
- test -z "$verbose" && echo
- test_failure_ "$@"
- break
+ echo "* timing run $i/$GIT_PERF_REPEAT_COUNT:"
fi
- done
- if test -z "$verbose"; then
- echo " ok"
else
- test_ok_ "$1"
+ test -z "$verbose" && echo
+ test_failure_ "$@"
+ break
fi
- base="$perf_results_dir"/"$perf_results_prefix$(basename "$0" .sh)"."$test_count"
- "$TEST_DIRECTORY"/perf/min_time.perl test_time.* >"$base".times
+ done
+ if test -z "$verbose"; then
+ echo " ok"
+ else
+ test_ok_ "$1"
fi
- test_finish_
+ "$TEST_DIRECTORY"/perf/min_time.perl test_time.* >"$base".times
+}
+
+test_perf () {
+ test_wrapper_ test_perf_ "$@"
+}
+
+test_size_ () {
+ say >&3 "running: $2"
+ if test_eval_ "$2" 3>"$base".size; then
+ test_ok_ "$1"
+ else
+ test_failure_ "$@"
+ fi
+}
+
+test_size () {
+ test_wrapper_ test_size_ "$@"
}
# We extend test_done to print timings at the end (./run disables this
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index af61d083b4..850f651e4e 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -116,7 +116,7 @@ check_sub_test_lib_test () {
name="$1" # stdin is the expected output from the test
(
cd "$name" &&
- ! test -s err &&
+ test_must_be_empty err &&
sed -e 's/^> //' -e 's/Z$//' >expect &&
test_cmp expect out
)
@@ -1081,7 +1081,7 @@ test_expect_success 'very long name in the index handled sanely' '
(
git ls-files -s path4 |
sed -e "s/ .*/ /" |
- tr -d "\012"
+ tr -d "\012" &&
echo "$a"
) | git update-index --index-info &&
len=$(git ls-files "a*" | wc -c) &&
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index 4c865051e7..182da069f1 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -167,9 +167,8 @@ test_expect_success 'reinit' '
) &&
test_i18ngrep "Initialized empty" again/out1 &&
test_i18ngrep "Reinitialized existing" again/out2 &&
- >again/empty &&
- test_i18ncmp again/empty again/err1 &&
- test_i18ncmp again/empty again/err2
+ test_must_be_empty again/err1 &&
+ test_must_be_empty again/err2
'
test_expect_success 'init with --template' '
@@ -408,7 +407,7 @@ is_hidden () {
test_expect_success MINGW '.git hidden' '
rm -rf newdir &&
(
- unset GIT_DIR GIT_WORK_TREE
+ sane_unset GIT_DIR GIT_WORK_TREE &&
mkdir newdir &&
cd newdir &&
git init &&
@@ -420,7 +419,7 @@ test_expect_success MINGW '.git hidden' '
test_expect_success MINGW 'bare git dir not hidden' '
rm -rf newdir &&
(
- unset GIT_DIR GIT_WORK_TREE GIT_CONFIG
+ sane_unset GIT_DIR GIT_WORK_TREE GIT_CONFIG &&
mkdir newdir &&
cd newdir &&
git --bare init
diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh
index f19ae4f8cc..22499bce5f 100755
--- a/t/t0003-attributes.sh
+++ b/t/t0003-attributes.sh
@@ -34,15 +34,15 @@ test_expect_success 'open-quoted pathname' '
test_expect_success 'setup' '
mkdir -p a/b/d a/c b &&
(
- echo "[attr]notest !test"
- echo "\" d \" test=d"
- echo " e test=e"
- echo " e\" test=e"
- echo "f test=f"
- echo "a/i test=a/i"
- echo "onoff test -test"
- echo "offon -test test"
- echo "no notest"
+ echo "[attr]notest !test" &&
+ echo "\" d \" test=d" &&
+ echo " e test=e" &&
+ echo " e\" test=e" &&
+ echo "f test=f" &&
+ echo "a/i test=a/i" &&
+ echo "onoff test -test" &&
+ echo "offon -test test" &&
+ echo "no notest" &&
echo "A/e/F test=A/e/F"
) >.gitattributes &&
(
@@ -51,7 +51,7 @@ test_expect_success 'setup' '
) >a/.gitattributes &&
(
echo "h test=a/b/h" &&
- echo "d/* test=a/b/d/*"
+ echo "d/* test=a/b/d/*" &&
echo "d/yes notest"
) >a/b/.gitattributes &&
(
@@ -208,9 +208,8 @@ test_expect_success 'attribute test: --all option' '
'
test_expect_success 'attribute test: --cached option' '
- : >empty &&
git check-attr --cached --stdin --all <stdin-all | sort >actual &&
- test_cmp empty actual &&
+ test_must_be_empty actual &&
git add .gitattributes a/.gitattributes a/b/.gitattributes &&
git check-attr --cached --stdin --all <stdin-all | sort >actual &&
test_cmp specified-all actual
@@ -287,7 +286,7 @@ test_expect_success 'bare repository: check that .gitattribute is ignored' '
(
cd bare.git &&
(
- echo "f test=f"
+ echo "f test=f" &&
echo "a/i test=a/i"
) >.gitattributes &&
attr_check f unspecified &&
@@ -312,7 +311,7 @@ test_expect_success 'bare repository: test info/attributes' '
(
cd bare.git &&
(
- echo "f test=f"
+ echo "f test=f" &&
echo "a/i test=a/i"
) >info/attributes &&
attr_check f f &&
diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh
index c03f155a35..1744cee5e9 100755
--- a/t/t0008-ignores.sh
+++ b/t/t0008-ignores.sh
@@ -807,10 +807,9 @@ test_expect_success 'trailing whitespace is ignored' '
cat >expect <<EOF &&
whitespace/untracked
EOF
- : >err.expect &&
git ls-files -o -X ignore whitespace >actual 2>err &&
test_cmp expect actual &&
- test_cmp err.expect err
+ test_must_be_empty err
'
test_expect_success !MINGW 'quoting allows trailing whitespace' '
@@ -820,10 +819,9 @@ test_expect_success !MINGW 'quoting allows trailing whitespace' '
>whitespace/untracked &&
echo "whitespace/trailing\\ \\ " >ignore &&
echo whitespace/untracked >expect &&
- : >err.expect &&
git ls-files -o -X ignore whitespace >actual 2>err &&
test_cmp expect actual &&
- test_cmp err.expect err
+ test_must_be_empty err
'
test_expect_success !MINGW,!CYGWIN 'correct handling of backslashes' '
@@ -845,10 +843,9 @@ test_expect_success !MINGW,!CYGWIN 'correct handling of backslashes' '
whitespace/trailing 6 \\a\\Z
EOF
echo whitespace/untracked >expect &&
- >err.expect &&
git ls-files -o -X ignore whitespace >actual 2>err &&
test_cmp expect actual &&
- test_cmp err.expect err
+ test_must_be_empty err
'
test_expect_success 'info/exclude trumps core.excludesfile' '
diff --git a/t/t0019-json-writer.sh b/t/t0019-json-writer.sh
new file mode 100755
index 0000000000..3b0c336b38
--- /dev/null
+++ b/t/t0019-json-writer.sh
@@ -0,0 +1,331 @@
+#!/bin/sh
+
+test_description='test json-writer JSON generation'
+. ./test-lib.sh
+
+test_expect_success 'unit test of json-writer routines' '
+ test-tool json-writer -u
+'
+
+test_expect_success 'trivial object' '
+ cat >expect <<-\EOF &&
+ {}
+ EOF
+ cat >input <<-\EOF &&
+ object
+ end
+ EOF
+ test-tool json-writer <input >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'trivial array' '
+ cat >expect <<-\EOF &&
+ []
+ EOF
+ cat >input <<-\EOF &&
+ array
+ end
+ EOF
+ test-tool json-writer <input >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'simple object' '
+ cat >expect <<-\EOF &&
+ {"a":"abc","b":42,"c":3.14,"d":true,"e":false,"f":null}
+ EOF
+ cat >input <<-\EOF &&
+ object
+ object-string a abc
+ object-int b 42
+ object-double c 2 3.140
+ object-true d
+ object-false e
+ object-null f
+ end
+ EOF
+ test-tool json-writer <input >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'simple array' '
+ cat >expect <<-\EOF &&
+ ["abc",42,3.14,true,false,null]
+ EOF
+ cat >input <<-\EOF &&
+ array
+ array-string abc
+ array-int 42
+ array-double 2 3.140
+ array-true
+ array-false
+ array-null
+ end
+ EOF
+ test-tool json-writer <input >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'escape quoting string' '
+ cat >expect <<-\EOF &&
+ {"a":"abc\\def"}
+ EOF
+ cat >input <<-\EOF &&
+ object
+ object-string a abc\def
+ end
+ EOF
+ test-tool json-writer <input >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'escape quoting string 2' '
+ cat >expect <<-\EOF &&
+ {"a":"abc\"def"}
+ EOF
+ cat >input <<-\EOF &&
+ object
+ object-string a abc"def
+ end
+ EOF
+ test-tool json-writer <input >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'nested inline object' '
+ cat >expect <<-\EOF &&
+ {"a":"abc","b":42,"sub1":{"c":3.14,"d":true,"sub2":{"e":false,"f":null}}}
+ EOF
+ cat >input <<-\EOF &&
+ object
+ object-string a abc
+ object-int b 42
+ object-object sub1
+ object-double c 2 3.140
+ object-true d
+ object-object sub2
+ object-false e
+ object-null f
+ end
+ end
+ end
+ EOF
+ test-tool json-writer <input >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'nested inline array' '
+ cat >expect <<-\EOF &&
+ ["abc",42,[3.14,true,[false,null]]]
+ EOF
+ cat >input <<-\EOF &&
+ array
+ array-string abc
+ array-int 42
+ array-array
+ array-double 2 3.140
+ array-true
+ array-array
+ array-false
+ array-null
+ end
+ end
+ end
+ EOF
+ test-tool json-writer <input >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'nested inline object and array' '
+ cat >expect <<-\EOF &&
+ {"a":"abc","b":42,"sub1":{"c":3.14,"d":true,"sub2":[false,null]}}
+ EOF
+ cat >input <<-\EOF &&
+ object
+ object-string a abc
+ object-int b 42
+ object-object sub1
+ object-double c 2 3.140
+ object-true d
+ object-array sub2
+ array-false
+ array-null
+ end
+ end
+ end
+ EOF
+ test-tool json-writer <input >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'nested inline object and array 2' '
+ cat >expect <<-\EOF &&
+ {"a":"abc","b":42,"sub1":{"c":3.14,"d":true,"sub2":[false,{"g":0,"h":1},null]}}
+ EOF
+ cat >input <<-\EOF &&
+ object
+ object-string a abc
+ object-int b 42
+ object-object sub1
+ object-double c 2 3.140
+ object-true d
+ object-array sub2
+ array-false
+ array-object
+ object-int g 0
+ object-int h 1
+ end
+ array-null
+ end
+ end
+ end
+ EOF
+ test-tool json-writer <input >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'pretty nested inline object and array 2' '
+ sed -e "s/^|//" >expect <<-\EOF &&
+ |{
+ | "a": "abc",
+ | "b": 42,
+ | "sub1": {
+ | "c": 3.14,
+ | "d": true,
+ | "sub2": [
+ | false,
+ | {
+ | "g": 0,
+ | "h": 1
+ | },
+ | null
+ | ]
+ | }
+ |}
+ EOF
+ cat >input <<-\EOF &&
+ object
+ object-string a abc
+ object-int b 42
+ object-object sub1
+ object-double c 2 3.140
+ object-true d
+ object-array sub2
+ array-false
+ array-object
+ object-int g 0
+ object-int h 1
+ end
+ array-null
+ end
+ end
+ end
+ EOF
+ test-tool json-writer -p <input >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'inline object with no members' '
+ cat >expect <<-\EOF &&
+ {"a":"abc","empty":{},"b":42}
+ EOF
+ cat >input <<-\EOF &&
+ object
+ object-string a abc
+ object-object empty
+ end
+ object-int b 42
+ end
+ EOF
+ test-tool json-writer <input >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'inline array with no members' '
+ cat >expect <<-\EOF &&
+ {"a":"abc","empty":[],"b":42}
+ EOF
+ cat >input <<-\EOF &&
+ object
+ object-string a abc
+ object-array empty
+ end
+ object-int b 42
+ end
+ EOF
+ test-tool json-writer <input >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'larger empty example' '
+ cat >expect <<-\EOF &&
+ {"a":"abc","empty":[{},{},{},[],{}],"b":42}
+ EOF
+ cat >input <<-\EOF &&
+ object
+ object-string a abc
+ object-array empty
+ array-object
+ end
+ array-object
+ end
+ array-object
+ end
+ array-array
+ end
+ array-object
+ end
+ end
+ object-int b 42
+ end
+ EOF
+ test-tool json-writer <input >actual &&
+ test_cmp expect actual
+'
+
+test_lazy_prereq PERLJSON '
+ perl -MJSON -e "exit 0"
+'
+
+# As a sanity check, ask Perl to parse our generated JSON and recursively
+# dump the resulting data in sorted order. Confirm that that matches our
+# expectations.
+test_expect_success PERLJSON 'parse JSON using Perl' '
+ cat >expect <<-\EOF &&
+ row[0].a abc
+ row[0].b 42
+ row[0].sub1 hash
+ row[0].sub1.c 3.14
+ row[0].sub1.d 1
+ row[0].sub1.sub2 array
+ row[0].sub1.sub2[0] 0
+ row[0].sub1.sub2[1] hash
+ row[0].sub1.sub2[1].g 0
+ row[0].sub1.sub2[1].h 1
+ row[0].sub1.sub2[2] null
+ EOF
+ cat >input <<-\EOF &&
+ object
+ object-string a abc
+ object-int b 42
+ object-object sub1
+ object-double c 2 3.140
+ object-true d
+ object-array sub2
+ array-false
+ array-object
+ object-int g 0
+ object-int h 1
+ end
+ array-null
+ end
+ end
+ end
+ EOF
+ test-tool json-writer <input >output.json &&
+ perl "$TEST_DIRECTORY"/t0019/parse_json.perl <output.json >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t0019/parse_json.perl b/t/t0019/parse_json.perl
new file mode 100644
index 0000000000..fea87fb81b
--- /dev/null
+++ b/t/t0019/parse_json.perl
@@ -0,0 +1,55 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+use JSON;
+
+sub dump_array {
+ my ($label_in, $ary_ref) = @_;
+ my @ary = @$ary_ref;
+
+ for ( my $i = 0; $i <= $#{ $ary_ref }; $i++ )
+ {
+ my $label = "$label_in\[$i\]";
+ dump_item($label, $ary[$i]);
+ }
+}
+
+sub dump_hash {
+ my ($label_in, $obj_ref) = @_;
+ my %obj = %$obj_ref;
+
+ foreach my $k (sort keys %obj) {
+ my $label = (length($label_in) > 0) ? "$label_in.$k" : "$k";
+ my $value = $obj{$k};
+
+ dump_item($label, $value);
+ }
+}
+
+sub dump_item {
+ my ($label_in, $value) = @_;
+ if (ref($value) eq 'ARRAY') {
+ print "$label_in array\n";
+ dump_array($label_in, $value);
+ } elsif (ref($value) eq 'HASH') {
+ print "$label_in hash\n";
+ dump_hash($label_in, $value);
+ } elsif (ref $value) {
+ my $bool = $value ? 1 : 0;
+ print "$label_in $bool\n";
+ } elsif (defined $value) {
+ print "$label_in $value\n";
+ } else {
+ print "$label_in null\n";
+ }
+}
+
+my $row = 0;
+while (<>) {
+ my $data = decode_json( $_ );
+ my $label = "row[$row]";
+
+ dump_hash($label, $data);
+ $row++;
+}
+
diff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh
index 5f056982a5..854da0ae16 100755
--- a/t/t0020-crlf.sh
+++ b/t/t0020-crlf.sh
@@ -160,7 +160,7 @@ test_expect_success 'checkout with autocrlf=input' '
git config core.autocrlf input &&
git read-tree --reset -u HEAD &&
test_must_fail has_cr one &&
- test_must_fail has_cr two &&
+ test_must_fail has_cr dir/two &&
git update-index -- one dir/two &&
test "$one" = $(git hash-object --stdin <one) &&
test "$two" = $(git hash-object --stdin <dir/two) &&
diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
index 9479a4aaab..308cd28f3b 100755
--- a/t/t0021-conversion.sh
+++ b/t/t0021-conversion.sh
@@ -583,7 +583,7 @@ test_expect_success PERL 'process filter should restart after unexpected write f
git checkout --quiet --no-progress . 2>git-stderr.log &&
grep "smudge write error at" git-stderr.log &&
- grep "error: external filter" git-stderr.log &&
+ test_i18ngrep "error: external filter" git-stderr.log &&
cat >expected.log <<-EOF &&
START
@@ -785,7 +785,7 @@ test_expect_success PERL 'missing file in delayed checkout' '
cd repo &&
git init &&
echo "*.a filter=bug" >.gitattributes &&
- cp "$TEST_ROOT/test.o" missing-delay.a
+ cp "$TEST_ROOT/test.o" missing-delay.a &&
git add . &&
git commit -m "test commit"
) &&
@@ -807,7 +807,7 @@ test_expect_success PERL 'invalid file in delayed checkout' '
git init &&
echo "*.a filter=bug" >.gitattributes &&
cp "$TEST_ROOT/test.o" invalid-delay.a &&
- cp "$TEST_ROOT/test.o" unfiltered
+ cp "$TEST_ROOT/test.o" unfiltered &&
git add . &&
git commit -m "test commit"
) &&
diff --git a/t/t0028-working-tree-encoding.sh b/t/t0028-working-tree-encoding.sh
index 12b8eb963a..7e87b5a200 100755
--- a/t/t0028-working-tree-encoding.sh
+++ b/t/t0028-working-tree-encoding.sh
@@ -203,7 +203,11 @@ test_expect_success 'error if encoding garbage is already in Git' '
test_i18ngrep "error: BOM is required" err.out
'
-test_expect_success 'check roundtrip encoding' '
+test_lazy_prereq ICONV_SHIFT_JIS '
+ iconv -f UTF-8 -t SHIFT-JIS </dev/null
+'
+
+test_expect_success ICONV_SHIFT_JIS 'check roundtrip encoding' '
test_when_finished "rm -f roundtrip.shift roundtrip.utf16" &&
test_when_finished "git reset --hard HEAD" &&
diff --git a/t/t0030-stripspace.sh b/t/t0030-stripspace.sh
index bbf3e39e3d..5ce47e8af5 100755
--- a/t/t0030-stripspace.sh
+++ b/t/t0030-stripspace.sh
@@ -110,31 +110,30 @@ test_expect_success \
test_expect_success \
'only consecutive blank lines should be completely removed' '
- > expect &&
printf "\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
printf "\n\n\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
printf "$sss\n$sss\n$sss\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
printf "$sss$sss\n$sss\n\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
printf "\n$sss\n$sss$sss\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
printf "$sss$sss$sss$sss\n\n\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
printf "\n$sss$sss$sss$sss\n\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
printf "\n\n$sss$sss$sss$sss\n" | git stripspace >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success \
@@ -321,22 +320,20 @@ test_expect_success \
test_expect_success \
'spaces with newline at end should be replaced with empty string' '
- printf "" >expect &&
-
echo | git stripspace >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
echo "$sss" | git stripspace >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
echo "$sss$sss" | git stripspace >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
echo "$sss$sss$sss" | git stripspace >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
echo "$sss$sss$sss$sss" | git stripspace >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success \
@@ -350,19 +347,17 @@ test_expect_success \
test_expect_success \
'spaces without newline at end should be replaced with empty string' '
- printf "" >expect &&
-
printf "" | git stripspace >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
printf "$sss$sss" | git stripspace >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
printf "$sss$sss$sss" | git stripspace >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
printf "$sss$sss$sss$sss" | git stripspace >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success \
diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
index 04d474c84f..5b0560fa20 100755
--- a/t/t0040-parse-options.sh
+++ b/t/t0040-parse-options.sh
@@ -286,11 +286,9 @@ test_expect_success 'OPT_CALLBACK() and OPT_BIT() work' '
test_cmp expect output
'
->expect
-
test_expect_success 'OPT_CALLBACK() and callback errors work' '
test_must_fail test-parse-options --no-length >output 2>output.err &&
- test_i18ncmp expect output &&
+ test_must_be_empty output &&
test_must_be_empty output.err
'
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 21a8b53132..cd74c0a471 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -330,6 +330,9 @@ test_submodule_relative_url "(null)" "../foo" "../submodule" "../submodule"
test_submodule_relative_url "(null)" "./foo/bar" "../submodule" "foo/submodule"
test_submodule_relative_url "(null)" "./foo" "../submodule" "submodule"
test_submodule_relative_url "(null)" "//somewhere else/repo" "../subrepo" "//somewhere else/subrepo"
+test_submodule_relative_url "(null)" "//somewhere else/repo" "../../subrepo" "//subrepo"
+test_submodule_relative_url "(null)" "//somewhere else/repo" "../../../subrepo" "/subrepo"
+test_submodule_relative_url "(null)" "//somewhere else/repo" "../../../../subrepo" "subrepo"
test_submodule_relative_url "(null)" "$(pwd)/subsuper_update_r" "../subsubsuper_update_r" "$(pwd)/subsubsuper_update_r"
test_submodule_relative_url "(null)" "$(pwd)/super_update_r2" "../subsuper_update_r" "$(pwd)/subsuper_update_r"
test_submodule_relative_url "(null)" "$(pwd)/." "../." "$(pwd)/."
@@ -344,10 +347,20 @@ test_submodule_relative_url "(null)" "file:///tmp/repo" "../subrepo" "file:///tm
test_submodule_relative_url "(null)" "foo/bar" "../submodule" "foo/submodule"
test_submodule_relative_url "(null)" "foo" "../submodule" "submodule"
test_submodule_relative_url "(null)" "helper:://hostname/repo" "../subrepo" "helper:://hostname/subrepo"
+test_submodule_relative_url "(null)" "helper:://hostname/repo" "../../subrepo" "helper:://subrepo"
+test_submodule_relative_url "(null)" "helper:://hostname/repo" "../../../subrepo" "helper::/subrepo"
+test_submodule_relative_url "(null)" "helper:://hostname/repo" "../../../../subrepo" "helper::subrepo"
+test_submodule_relative_url "(null)" "helper:://hostname/repo" "../../../../../subrepo" "helper:subrepo"
+test_submodule_relative_url "(null)" "helper:://hostname/repo" "../../../../../../subrepo" ".:subrepo"
test_submodule_relative_url "(null)" "ssh://hostname/repo" "../subrepo" "ssh://hostname/subrepo"
+test_submodule_relative_url "(null)" "ssh://hostname/repo" "../../subrepo" "ssh://subrepo"
+test_submodule_relative_url "(null)" "ssh://hostname/repo" "../../../subrepo" "ssh:/subrepo"
+test_submodule_relative_url "(null)" "ssh://hostname/repo" "../../../../subrepo" "ssh:subrepo"
+test_submodule_relative_url "(null)" "ssh://hostname/repo" "../../../../../subrepo" ".:subrepo"
test_submodule_relative_url "(null)" "ssh://hostname:22/repo" "../subrepo" "ssh://hostname:22/subrepo"
test_submodule_relative_url "(null)" "user@host:path/to/repo" "../subrepo" "user@host:path/to/subrepo"
test_submodule_relative_url "(null)" "user@host:repo" "../subrepo" "user@host:subrepo"
+test_submodule_relative_url "(null)" "user@host:repo" "../../subrepo" ".:subrepo"
test_expect_success 'match .gitmodules' '
test-tool path-utils is_dotgitmodules \
diff --git a/t/t0061-run-command.sh b/t/t0061-run-command.sh
index c887ed5b45..3e131c5325 100755
--- a/t/t0061-run-command.sh
+++ b/t/t0061-run-command.sh
@@ -11,7 +11,6 @@ cat >hello-script <<-EOF
#!$SHELL_PATH
cat hello-script
EOF
->empty
test_expect_success 'start_command reports ENOENT' '
test-tool run-command start-command-ENOENT ./does-not-exist
@@ -23,7 +22,7 @@ test_expect_success 'run_command can run a command' '
test-tool run-command run-command ./hello.sh >actual 2>err &&
test_cmp hello-script actual &&
- test_cmp empty err
+ test_must_be_empty err
'
test_expect_success !MINGW 'run_command can run a script without a #! line' '
@@ -34,7 +33,7 @@ test_expect_success !MINGW 'run_command can run a script without a #! line' '
test-tool run-command run-command ./hello >actual 2>err &&
test_cmp hello-script actual &&
- test_cmp empty err
+ test_must_be_empty err
'
test_expect_success 'run_command does not try to execute a directory' '
@@ -47,7 +46,7 @@ test_expect_success 'run_command does not try to execute a directory' '
PATH=$PWD/bin1:$PWD/bin2:$PATH \
test-tool run-command run-command greet >actual 2>err &&
test_cmp bin2/greet actual &&
- test_cmp empty err
+ test_must_be_empty err
'
test_expect_success POSIXPERM 'run_command passes over non-executable file' '
@@ -64,7 +63,7 @@ test_expect_success POSIXPERM 'run_command passes over non-executable file' '
PATH=$PWD/bin1:$PWD/bin2:$PATH \
test-tool run-command run-command greet >actual 2>err &&
test_cmp bin2/greet actual &&
- test_cmp empty err
+ test_must_be_empty err
'
test_expect_success POSIXPERM 'run_command reports EACCES' '
diff --git a/t/t0090-cache-tree.sh b/t/t0090-cache-tree.sh
index 0c61268fd2..7de40141ca 100755
--- a/t/t0090-cache-tree.sh
+++ b/t/t0090-cache-tree.sh
@@ -156,7 +156,7 @@ test_expect_success PERL 'commit --interactive gives cache-tree on partial commi
return 44;
}
EOT
- (echo p; echo 1; echo; echo s; echo n; echo y; echo q) |
+ test_write_lines p 1 "" s n y q |
git commit --interactive -m foo &&
test_cache_tree
'
@@ -239,7 +239,7 @@ test_expect_success 'no phantom error when switching trees' '
>newdir/one &&
git add newdir/one &&
git checkout 2>errors &&
- ! test -s errors
+ test_must_be_empty errors
'
test_expect_success 'switching trees does not invalidate shared index' '
diff --git a/t/t0203-gettext-setlocale-sanity.sh b/t/t0203-gettext-setlocale-sanity.sh
index 71b0d74b4d..0ce1f22eff 100755
--- a/t/t0203-gettext-setlocale-sanity.sh
+++ b/t/t0203-gettext-setlocale-sanity.sh
@@ -11,7 +11,7 @@ test_expect_success 'git show a ISO-8859-1 commit under C locale' '
. "$TEST_DIRECTORY"/t3901/8859-1.txt &&
test_commit "iso-c-commit" iso-under-c &&
git show >out 2>err &&
- ! test -s err &&
+ test_must_be_empty err &&
grep -q "iso-c-commit" out
'
@@ -19,7 +19,7 @@ test_expect_success GETTEXT_LOCALE 'git show a ISO-8859-1 commit under a UTF-8 l
. "$TEST_DIRECTORY"/t3901/8859-1.txt &&
test_commit "iso-utf8-commit" iso-under-utf8 &&
LANGUAGE=is LC_ALL="$is_IS_locale" git show >out 2>err &&
- ! test -s err &&
+ test_must_be_empty err &&
grep -q "iso-utf8-commit" out
'
diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh
index 03bd31e9f2..82eaaea0f4 100755
--- a/t/t0300-credentials.sh
+++ b/t/t0300-credentials.sh
@@ -294,8 +294,7 @@ test_expect_success 'helpers can abort the process' '
-c credential.helper="!f() { echo quit=1; }; f" \
-c credential.helper="verbatim foo bar" \
credential fill >stdout &&
- >expect &&
- test_cmp expect stdout
+ test_must_be_empty stdout
'
test_expect_success 'empty helper spec resets helper list' '
diff --git a/t/t0410-partial-clone.sh b/t/t0410-partial-clone.sh
index 4984ca583d..b35bc89f1e 100755
--- a/t/t0410-partial-clone.sh
+++ b/t/t0410-partial-clone.sh
@@ -181,7 +181,7 @@ test_expect_success 'rev-list stops traversal at missing and promised commit' '
git -C repo config core.repositoryformatversion 1 &&
git -C repo config extensions.partialclone "arbitrary string" &&
- git -C repo rev-list --exclude-promisor-objects --objects bar >out &&
+ GIT_TEST_COMMIT_GRAPH=0 git -C repo rev-list --exclude-promisor-objects --objects bar >out &&
grep $(git -C repo rev-parse bar) out &&
! grep $FOO out
'
@@ -271,28 +271,91 @@ test_expect_success 'rev-list accepts missing and promised objects on command li
git -C repo rev-list --exclude-promisor-objects --objects "$COMMIT" "$TREE" "$BLOB"
'
-test_expect_success 'gc does not repack promisor objects' '
+test_expect_success 'gc repacks promisor objects separately from non-promisor objects' '
rm -rf repo &&
test_create_repo repo &&
- test_commit -C repo my_commit &&
+ test_commit -C repo one &&
+ test_commit -C repo two &&
- TREE_HASH=$(git -C repo rev-parse HEAD^{tree}) &&
- HASH=$(printf "$TREE_HASH\n" | pack_as_from_promisor) &&
+ TREE_ONE=$(git -C repo rev-parse one^{tree}) &&
+ printf "$TREE_ONE\n" | pack_as_from_promisor &&
+ TREE_TWO=$(git -C repo rev-parse two^{tree}) &&
+ printf "$TREE_TWO\n" | pack_as_from_promisor &&
git -C repo config core.repositoryformatversion 1 &&
git -C repo config extensions.partialclone "arbitrary string" &&
git -C repo gc &&
- # Ensure that the promisor packfile still exists, and remove it
- test -e repo/.git/objects/pack/pack-$HASH.pack &&
- rm repo/.git/objects/pack/pack-$HASH.* &&
-
- # Ensure that the single other pack contains the commit, but not the tree
+ # Ensure that exactly one promisor packfile exists, and that it
+ # contains the trees but not the commits
+ ls repo/.git/objects/pack/pack-*.promisor >promisorlist &&
+ test_line_count = 1 promisorlist &&
+ PROMISOR_PACKFILE=$(sed "s/.promisor/.pack/" <promisorlist) &&
+ git verify-pack $PROMISOR_PACKFILE -v >out &&
+ grep "$TREE_ONE" out &&
+ grep "$TREE_TWO" out &&
+ ! grep "$(git -C repo rev-parse one)" out &&
+ ! grep "$(git -C repo rev-parse two)" out &&
+
+ # Remove the promisor packfile and associated files
+ rm $(sed "s/.promisor//" <promisorlist).* &&
+
+ # Ensure that the single other pack contains the commits, but not the
+ # trees
ls repo/.git/objects/pack/pack-*.pack >packlist &&
test_line_count = 1 packlist &&
git verify-pack repo/.git/objects/pack/pack-*.pack -v >out &&
- grep "$(git -C repo rev-parse HEAD)" out &&
- ! grep "$TREE_HASH" out
+ grep "$(git -C repo rev-parse one)" out &&
+ grep "$(git -C repo rev-parse two)" out &&
+ ! grep "$TREE_ONE" out &&
+ ! grep "$TREE_TWO" out
+'
+
+test_expect_success 'gc does not repack promisor objects if there are none' '
+ rm -rf repo &&
+ test_create_repo repo &&
+ test_commit -C repo one &&
+
+ git -C repo config core.repositoryformatversion 1 &&
+ git -C repo config extensions.partialclone "arbitrary string" &&
+ git -C repo gc &&
+
+ # Ensure that only one pack exists
+ ls repo/.git/objects/pack/pack-*.pack >packlist &&
+ test_line_count = 1 packlist
+'
+
+repack_and_check () {
+ rm -rf repo2 &&
+ cp -r repo repo2 &&
+ git -C repo2 repack $1 -d &&
+ git -C repo2 fsck &&
+
+ git -C repo2 cat-file -e $2 &&
+ git -C repo2 cat-file -e $3
+}
+
+test_expect_success 'repack -d does not irreversibly delete promisor objects' '
+ rm -rf repo &&
+ test_create_repo repo &&
+ git -C repo config core.repositoryformatversion 1 &&
+ git -C repo config extensions.partialclone "arbitrary string" &&
+
+ git -C repo commit --allow-empty -m one &&
+ git -C repo commit --allow-empty -m two &&
+ git -C repo commit --allow-empty -m three &&
+ git -C repo commit --allow-empty -m four &&
+ ONE=$(git -C repo rev-parse HEAD^^^) &&
+ TWO=$(git -C repo rev-parse HEAD^^) &&
+ THREE=$(git -C repo rev-parse HEAD^) &&
+
+ printf "$TWO\n" | pack_as_from_promisor &&
+ printf "$THREE\n" | pack_as_from_promisor &&
+ delete_object repo "$ONE" &&
+
+ repack_and_check -a "$TWO" "$THREE" &&
+ repack_and_check -A "$TWO" "$THREE" &&
+ repack_and_check -l "$TWO" "$THREE"
'
test_expect_success 'gc stops traversal when a missing but promised object is reached' '
diff --git a/t/t1004-read-tree-m-u-wf.sh b/t/t1004-read-tree-m-u-wf.sh
index 826cd32e23..c13578a635 100755
--- a/t/t1004-read-tree-m-u-wf.sh
+++ b/t/t1004-read-tree-m-u-wf.sh
@@ -210,10 +210,10 @@ test_expect_success 'D/F' '
read_tree_u_must_succeed -m -u branch-point side-b side-a &&
git ls-files -u >actual &&
(
- a=$(git rev-parse branch-point:subdir/file2)
- b=$(git rev-parse side-a:subdir/file2/another)
- echo "100644 $a 1 subdir/file2"
- echo "100644 $a 2 subdir/file2"
+ a=$(git rev-parse branch-point:subdir/file2) &&
+ b=$(git rev-parse side-a:subdir/file2/another) &&
+ echo "100644 $a 1 subdir/file2" &&
+ echo "100644 $a 2 subdir/file2" &&
echo "100644 $b 3 subdir/file2/another"
) >expect &&
test_cmp expect actual
diff --git a/t/t1005-read-tree-reset.sh b/t/t1005-read-tree-reset.sh
index 074568500a..83b09e1310 100755
--- a/t/t1005-read-tree-reset.sh
+++ b/t/t1005-read-tree-reset.sh
@@ -33,7 +33,7 @@ test_expect_success 'reset should remove remnants from a failed merge' '
git ls-files -s >expect &&
sha1=$(git rev-parse :new) &&
(
- echo "100644 $sha1 1 old"
+ echo "100644 $sha1 1 old" &&
echo "100644 $sha1 3 old"
) | git update-index --index-info &&
>old &&
@@ -48,7 +48,7 @@ test_expect_success 'two-way reset should remove remnants too' '
git ls-files -s >expect &&
sha1=$(git rev-parse :new) &&
(
- echo "100644 $sha1 1 old"
+ echo "100644 $sha1 1 old" &&
echo "100644 $sha1 3 old"
) | git update-index --index-info &&
>old &&
@@ -63,7 +63,7 @@ test_expect_success 'Porcelain reset should remove remnants too' '
git ls-files -s >expect &&
sha1=$(git rev-parse :new) &&
(
- echo "100644 $sha1 1 old"
+ echo "100644 $sha1 1 old" &&
echo "100644 $sha1 3 old"
) | git update-index --index-info &&
>old &&
@@ -78,7 +78,7 @@ test_expect_success 'Porcelain checkout -f should remove remnants too' '
git ls-files -s >expect &&
sha1=$(git rev-parse :new) &&
(
- echo "100644 $sha1 1 old"
+ echo "100644 $sha1 1 old" &&
echo "100644 $sha1 3 old"
) | git update-index --index-info &&
>old &&
@@ -93,7 +93,7 @@ test_expect_success 'Porcelain checkout -f HEAD should remove remnants too' '
git ls-files -s >expect &&
sha1=$(git rev-parse :new) &&
(
- echo "100644 $sha1 1 old"
+ echo "100644 $sha1 1 old" &&
echo "100644 $sha1 3 old"
) | git update-index --index-info &&
>old &&
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index 13dd510b2e..7f19d591f2 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -550,8 +550,8 @@ test_expect_success 'git cat-file --batch --follow-symlink returns correct sha a
test_expect_success 'cat-file --batch-all-objects shows all objects' '
# make new repos so we know the full set of objects; we will
# also make sure that there are some packed and some loose
- # objects, some referenced and some not, and that there are
- # some available only via alternates.
+ # objects, some referenced and some not, some duplicates, and that
+ # there are some available only via alternates.
git init all-one &&
(
cd all-one &&
@@ -567,10 +567,23 @@ test_expect_success 'cat-file --batch-all-objects shows all objects' '
cd all-two &&
echo local-unref | git hash-object -w --stdin
) >>expect.unsorted &&
+ git -C all-two rev-parse HEAD:file |
+ git -C all-two pack-objects .git/objects/pack/pack &&
sort <expect.unsorted >expect &&
git -C all-two cat-file --batch-all-objects \
--batch-check="%(objectname)" >actual &&
test_cmp expect actual
'
+# The only user-visible difference is that the objects are no longer sorted,
+# and the resulting sort order is undefined. So we can only check that it
+# produces the same objects as the ordered case, but that at least exercises
+# the code.
+test_expect_success 'cat-file --unordered works' '
+ git -C all-two cat-file --batch-all-objects --unordered \
+ --batch-check="%(objectname)" >actual.unsorted &&
+ sort <actual.unsorted >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t1008-read-tree-overlay.sh b/t/t1008-read-tree-overlay.sh
index 4c50ed955e..cf96016844 100755
--- a/t/t1008-read-tree-overlay.sh
+++ b/t/t1008-read-tree-overlay.sh
@@ -23,7 +23,7 @@ test_expect_success setup '
test_expect_success 'multi-read' '
read_tree_must_succeed initial master side &&
- (echo a; echo b/c) >expect &&
+ test_write_lines a b/c >expect &&
git ls-files >actual &&
test_cmp expect actual
'
diff --git a/t/t1011-read-tree-sparse-checkout.sh b/t/t1011-read-tree-sparse-checkout.sh
index 0c6f48f302..ba71b159ba 100755
--- a/t/t1011-read-tree-sparse-checkout.sh
+++ b/t/t1011-read-tree-sparse-checkout.sh
@@ -227,12 +227,11 @@ test_expect_success 'index removal and worktree narrowing at the same time' '
'
test_expect_success 'read-tree --reset removes outside worktree' '
- >empty &&
echo init.t >.git/info/sparse-checkout &&
git checkout -f top &&
git reset --hard removed &&
git ls-files sub/added >result &&
- test_cmp empty result
+ test_must_be_empty result
'
test_expect_success 'print errors when failed to update worktree' '
diff --git a/t/t1015-read-index-unmerged.sh b/t/t1015-read-index-unmerged.sh
new file mode 100755
index 0000000000..55d22da32c
--- /dev/null
+++ b/t/t1015-read-index-unmerged.sh
@@ -0,0 +1,123 @@
+#!/bin/sh
+
+test_description='Test various callers of read_index_unmerged'
+. ./test-lib.sh
+
+test_expect_success 'setup modify/delete + directory/file conflict' '
+ test_create_repo df_plus_modify_delete &&
+ (
+ cd df_plus_modify_delete &&
+
+ test_write_lines a b c d e f g h >letters &&
+ git add letters &&
+ git commit -m initial &&
+
+ git checkout -b modify &&
+ # Throw in letters.txt for sorting order fun
+ # ("letters.txt" sorts between "letters" and "letters/file")
+ echo i >>letters &&
+ echo "version 2" >letters.txt &&
+ git add letters letters.txt &&
+ git commit -m modified &&
+
+ git checkout -b delete HEAD^ &&
+ git rm letters &&
+ mkdir letters &&
+ >letters/file &&
+ echo "version 1" >letters.txt &&
+ git add letters letters.txt &&
+ git commit -m deleted
+ )
+'
+
+test_expect_success 'read-tree --reset cleans unmerged entries' '
+ test_when_finished "git -C df_plus_modify_delete clean -f" &&
+ test_when_finished "git -C df_plus_modify_delete reset --hard" &&
+ (
+ cd df_plus_modify_delete &&
+
+ git checkout delete^0 &&
+ test_must_fail git merge modify &&
+
+ git read-tree --reset HEAD &&
+ git ls-files -u >conflicts &&
+ test_must_be_empty conflicts
+ )
+'
+
+test_expect_success 'One reset --hard cleans unmerged entries' '
+ test_when_finished "git -C df_plus_modify_delete clean -f" &&
+ test_when_finished "git -C df_plus_modify_delete reset --hard" &&
+ (
+ cd df_plus_modify_delete &&
+
+ git checkout delete^0 &&
+ test_must_fail git merge modify &&
+
+ git reset --hard &&
+ test_path_is_missing .git/MERGE_HEAD &&
+ git ls-files -u >conflicts &&
+ test_must_be_empty conflicts
+ )
+'
+
+test_expect_success 'setup directory/file conflict + simple edit/edit' '
+ test_create_repo df_plus_edit_edit &&
+ (
+ cd df_plus_edit_edit &&
+
+ test_seq 1 10 >numbers &&
+ git add numbers &&
+ git commit -m initial &&
+
+ git checkout -b d-edit &&
+ mkdir foo &&
+ echo content >foo/bar &&
+ git add foo &&
+ echo 11 >>numbers &&
+ git add numbers &&
+ git commit -m "directory and edit" &&
+
+ git checkout -b f-edit d-edit^1 &&
+ echo content >foo &&
+ git add foo &&
+ echo eleven >>numbers &&
+ git add numbers &&
+ git commit -m "file and edit"
+ )
+'
+
+test_expect_success 'git merge --abort succeeds despite D/F conflict' '
+ test_when_finished "git -C df_plus_edit_edit clean -f" &&
+ test_when_finished "git -C df_plus_edit_edit reset --hard" &&
+ (
+ cd df_plus_edit_edit &&
+
+ git checkout f-edit^0 &&
+ test_must_fail git merge d-edit^0 &&
+
+ git merge --abort &&
+ test_path_is_missing .git/MERGE_HEAD &&
+ git ls-files -u >conflicts &&
+ test_must_be_empty conflicts
+ )
+'
+
+test_expect_success 'git am --skip succeeds despite D/F conflict' '
+ test_when_finished "git -C df_plus_edit_edit clean -f" &&
+ test_when_finished "git -C df_plus_edit_edit reset --hard" &&
+ (
+ cd df_plus_edit_edit &&
+
+ git checkout f-edit^0 &&
+ git format-patch -1 d-edit &&
+ test_must_fail git am -3 0001*.patch &&
+
+ git am --skip &&
+ test_path_is_missing .git/rebase-apply &&
+ git ls-files -u >conflicts &&
+ test_must_be_empty conflicts
+ )
+'
+
+test_done
diff --git a/t/t1020-subdirectory.sh b/t/t1020-subdirectory.sh
index df3183ea1a..c2df75e495 100755
--- a/t/t1020-subdirectory.sh
+++ b/t/t1020-subdirectory.sh
@@ -148,7 +148,7 @@ test_expect_success 'GIT_PREFIX for built-ins' '
(
cd dir &&
echo "change" >two &&
- GIT_EXTERNAL_DIFF=./diff git diff >../actual
+ GIT_EXTERNAL_DIFF=./diff git diff >../actual &&
git checkout -- two
) &&
test_cmp expect actual
diff --git a/t/t1050-large.sh b/t/t1050-large.sh
index f9eb143f43..1a9b21b293 100755
--- a/t/t1050-large.sh
+++ b/t/t1050-large.sh
@@ -108,7 +108,7 @@ test_expect_success 'packsize limit' '
test-tool genrandom "c" $(( 128 * 1024 )) >mid3 &&
git add mid1 mid2 mid3 &&
- count=0
+ count=0 &&
for pi in .git/objects/pack/pack-*.idx
do
test -f "$pi" && count=$(( $count + 1 ))
@@ -116,8 +116,8 @@ test_expect_success 'packsize limit' '
test $count = 2 &&
(
- git hash-object --stdin <mid1
- git hash-object --stdin <mid2
+ git hash-object --stdin <mid1 &&
+ git hash-object --stdin <mid2 &&
git hash-object --stdin <mid3
) |
sort >expect &&
diff --git a/t/t1090-sparse-checkout-scope.sh b/t/t1090-sparse-checkout-scope.sh
index 1f61eb3e88..25d7c700f6 100755
--- a/t/t1090-sparse-checkout-scope.sh
+++ b/t/t1090-sparse-checkout-scope.sh
@@ -31,6 +31,20 @@ test_expect_success 'perform sparse checkout of master' '
test_path_is_file c
'
+test_expect_success 'checkout -b checkout.optimizeNewBranch interaction' '
+ cp .git/info/sparse-checkout .git/info/sparse-checkout.bak &&
+ test_when_finished "
+ mv -f .git/info/sparse-checkout.bak .git/info/sparse-checkout
+ git checkout master
+ " &&
+ echo "/b" >>.git/info/sparse-checkout &&
+ test "$(git ls-files -t b)" = "S b" &&
+ git -c checkout.optimizeNewBranch=true checkout -b fast &&
+ test "$(git ls-files -t b)" = "S b" &&
+ git checkout -b slow &&
+ test "$(git ls-files -t b)" = "H b"
+'
+
test_expect_success 'merge feature branch into sparse checkout of master' '
git merge feature &&
test_path_is_file a &&
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index 03c223708e..cdf1fed5d1 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -346,12 +346,9 @@ test_expect_success 'working --list' '
git config --list > output &&
test_cmp expect output
'
-cat > expect << EOF
-EOF
-
test_expect_success '--list without repo produces empty output' '
git --git-dir=nonexistent config --list >output &&
- test_cmp expect output
+ test_must_be_empty output
'
cat > expect << EOF
@@ -888,7 +885,7 @@ EOF
test_expect_success !MINGW 'get --path copes with unset $HOME' '
(
- unset HOME;
+ sane_unset HOME &&
test_must_fail git config --get --path path.home \
>result 2>msg &&
git config --get --path path.normal >>result &&
@@ -1218,6 +1215,93 @@ test_expect_success 'last one wins: three level vars' '
test_cmp expect actual
'
+test_expect_success 'old-fashioned settings are case insensitive' '
+ test_when_finished "rm -f testConfig testConfig_expect testConfig_actual" &&
+
+ cat >testConfig_actual <<-EOF &&
+ [V.A]
+ r = value1
+ EOF
+ q_to_tab >testConfig_expect <<-EOF &&
+ [V.A]
+ Qr = value2
+ EOF
+ git config -f testConfig_actual "v.a.r" value2 &&
+ test_cmp testConfig_expect testConfig_actual &&
+
+ cat >testConfig_actual <<-EOF &&
+ [V.A]
+ r = value1
+ EOF
+ q_to_tab >testConfig_expect <<-EOF &&
+ [V.A]
+ QR = value2
+ EOF
+ git config -f testConfig_actual "V.a.R" value2 &&
+ test_cmp testConfig_expect testConfig_actual &&
+
+ cat >testConfig_actual <<-EOF &&
+ [V.A]
+ r = value1
+ EOF
+ q_to_tab >testConfig_expect <<-EOF &&
+ [V.A]
+ r = value1
+ Qr = value2
+ EOF
+ git config -f testConfig_actual "V.A.r" value2 &&
+ test_cmp testConfig_expect testConfig_actual &&
+
+ cat >testConfig_actual <<-EOF &&
+ [V.A]
+ r = value1
+ EOF
+ q_to_tab >testConfig_expect <<-EOF &&
+ [V.A]
+ r = value1
+ Qr = value2
+ EOF
+ git config -f testConfig_actual "v.A.r" value2 &&
+ test_cmp testConfig_expect testConfig_actual
+'
+
+test_expect_success 'setting different case sensitive subsections ' '
+ test_when_finished "rm -f testConfig testConfig_expect testConfig_actual" &&
+
+ cat >testConfig_actual <<-EOF &&
+ [V "A"]
+ R = v1
+ [K "E"]
+ Y = v1
+ [a "b"]
+ c = v1
+ [d "e"]
+ f = v1
+ EOF
+ q_to_tab >testConfig_expect <<-EOF &&
+ [V "A"]
+ Qr = v2
+ [K "E"]
+ Qy = v2
+ [a "b"]
+ Qc = v2
+ [d "e"]
+ f = v1
+ [d "E"]
+ Qf = v2
+ EOF
+ # exact match
+ git config -f testConfig_actual a.b.c v2 &&
+ # match section and subsection, key is cased differently.
+ git config -f testConfig_actual K.E.y v2 &&
+ # section and key are matched case insensitive, but subsection needs
+ # to match; When writing out new values only the key is adjusted
+ git config -f testConfig_actual v.A.r v2 &&
+ # subsection is not matched:
+ git config -f testConfig_actual d.E.f v2 &&
+ test_cmp testConfig_expect testConfig_actual
+'
+
for VAR in a .a a. a.0b a."b c". a."b c".0d
do
test_expect_success "git -c $VAR=VAL rejects invalid '$VAR'" '
diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh
index f035ee40a3..635918505d 100755
--- a/t/t1305-config-include.sh
+++ b/t/t1305-config-include.sh
@@ -310,7 +310,7 @@ test_expect_success 'include cycles are detected' '
cycle
EOF
test_must_fail git config --get-all test.value 2>stderr &&
- grep "exceeded maximum include depth" stderr
+ test_i18ngrep "exceeded maximum include depth" stderr
'
test_done
diff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh
index 8b14ab187c..21e139a313 100755
--- a/t/t1306-xdg-files.sh
+++ b/t/t1306-xdg-files.sh
@@ -114,11 +114,10 @@ test_expect_success 'Exclusion in a non-XDG global ignore file' '
'
test_expect_success 'Checking XDG ignore file when HOME is unset' '
- >expected &&
(sane_unset HOME &&
git config --unset core.excludesfile &&
git ls-files --exclude-standard --ignored >actual) &&
- test_cmp expected actual
+ test_must_be_empty actual
'
test_expect_success 'Checking attributes in the XDG attributes file' '
@@ -132,10 +131,9 @@ test_expect_success 'Checking attributes in the XDG attributes file' '
'
test_expect_success 'Checking XDG attributes when HOME is unset' '
- >expected &&
(sane_unset HOME &&
git check-attr -a f >actual) &&
- test_cmp expected actual
+ test_must_be_empty actual
'
test_expect_success '$XDG_CONFIG_HOME overrides $HOME/.config/git/attributes' '
diff --git a/t/t1308-config-set.sh b/t/t1308-config-set.sh
index 3e00d1af01..d0a2727b85 100755
--- a/t/t1308-config-set.sh
+++ b/t/t1308-config-set.sh
@@ -233,7 +233,7 @@ test_expect_success 'check line errors for malformed values' '
test_expect_success 'error on modifying repo config without repo' '
nongit test_must_fail git config a.b c 2>err &&
- grep "not in a git directory" err
+ test_i18ngrep "not in a git directory" err
'
cmdline_config="'foo.bar=from-cmdline'"
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index e1fd0f0ca8..7c8df20955 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -390,7 +390,7 @@ test_expect_success 'Query "master@{2005-05-26 23:33:01}" (middle of history wit
test_when_finished "rm -f o e" &&
git rev-parse --verify "master@{2005-05-26 23:33:01}" >o 2>e &&
test $B = $(cat o) &&
- test "warning: Log for ref $m has gap after $gd." = "$(cat e)"
+ test_i18ngrep -F "warning: log for ref $m has gap after $gd" e
'
test_expect_success 'Query "master@{2005-05-26 23:38:00}" (middle of history)' '
test_when_finished "rm -f o e" &&
@@ -408,7 +408,7 @@ test_expect_success 'Query "master@{2005-05-28}" (past end of history)' '
test_when_finished "rm -f o e" &&
git rev-parse --verify "master@{2005-05-28}" >o 2>e &&
test $D = $(cat o) &&
- test "warning: Log for ref $m unexpectedly ended on $ld." = "$(cat e)"
+ test_i18ngrep -F "warning: log for ref $m unexpectedly ended on $ld" e
'
rm -f .git/$m .git/logs/$m expect
@@ -462,7 +462,7 @@ test_expect_success 'git cat-file blob master@{2005-05-26 23:42}:F (expect OTHER
test_expect_success 'given old value for missing pseudoref, do not create' '
test_must_fail git update-ref PSEUDOREF $A $B 2>err &&
test_path_is_missing .git/PSEUDOREF &&
- grep "could not read ref" err
+ test_i18ngrep "could not read ref" err
'
test_expect_success 'create pseudoref' '
@@ -483,7 +483,7 @@ test_expect_success 'overwrite pseudoref with correct old value' '
test_expect_success 'do not overwrite pseudoref with wrong old value' '
test_must_fail git update-ref PSEUDOREF $D $E 2>err &&
test $C = $(cat .git/PSEUDOREF) &&
- grep "unexpected object ID" err
+ test_i18ngrep "unexpected object ID" err
'
test_expect_success 'delete pseudoref' '
@@ -495,7 +495,7 @@ test_expect_success 'do not delete pseudoref with wrong old value' '
git update-ref PSEUDOREF $A &&
test_must_fail git update-ref -d PSEUDOREF $B 2>err &&
test $A = $(cat .git/PSEUDOREF) &&
- grep "unexpected object ID" err
+ test_i18ngrep "unexpected object ID" err
'
test_expect_success 'delete pseudoref with correct old value' '
@@ -512,7 +512,7 @@ test_expect_success 'do not overwrite pseudoref with old OID zero' '
test_when_finished git update-ref -d PSEUDOREF &&
test_must_fail git update-ref PSEUDOREF $B $Z 2>err &&
test $A = $(cat .git/PSEUDOREF) &&
- grep "already exists" err
+ test_i18ngrep "already exists" err
'
# Test --stdin
@@ -650,7 +650,7 @@ test_expect_success 'stdin fails with duplicate refs' '
create $a $m
EOF
test_must_fail git update-ref --stdin <stdin 2>err &&
- grep "fatal: multiple updates for ref '"'"'$a'"'"' not allowed." err
+ test_i18ngrep "fatal: multiple updates for ref '"'"'$a'"'"' not allowed" err
'
test_expect_success 'stdin create ref works' '
@@ -1052,7 +1052,7 @@ test_expect_success 'stdin -z fails option with unknown name' '
test_expect_success 'stdin -z fails with duplicate refs' '
printf $F "create $a" "$m" "create $b" "$m" "create $a" "$m" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: multiple updates for ref '"'"'$a'"'"' not allowed." err
+ test_i18ngrep "fatal: multiple updates for ref '"'"'$a'"'"' not allowed" err
'
test_expect_success 'stdin -z create ref works' '
@@ -1283,7 +1283,7 @@ test_expect_success 'fails with duplicate HEAD update' '
update HEAD $B
EOF
test_must_fail git update-ref --stdin <stdin 2>err &&
- grep "fatal: multiple updates for '\''HEAD'\'' (including one via its referent .refs/heads/target1.) are not allowed" err &&
+ test_i18ngrep "fatal: multiple updates for '\''HEAD'\'' (including one via its referent .refs/heads/target1.) are not allowed" err &&
echo "refs/heads/target1" >expect &&
git symbolic-ref HEAD >actual &&
test_cmp expect actual &&
@@ -1300,7 +1300,7 @@ test_expect_success 'fails with duplicate ref update via symref' '
update refs/heads/symref2 $B
EOF
test_must_fail git update-ref --stdin <stdin 2>err &&
- grep "fatal: multiple updates for '\''refs/heads/target2'\'' (including one via symref .refs/heads/symref2.) are not allowed" err &&
+ test_i18ngrep "fatal: multiple updates for '\''refs/heads/target2'\'' (including one via symref .refs/heads/symref2.) are not allowed" err &&
echo "refs/heads/target2" >expect &&
git symbolic-ref refs/heads/symref2 >actual &&
test_cmp expect actual &&
diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh
index 30354fd26c..5d955c3bff 100755
--- a/t/t1403-show-ref.sh
+++ b/t/t1403-show-ref.sh
@@ -26,26 +26,22 @@ test_expect_success 'show-ref' '
git show-ref refs/tags/A >actual &&
test_cmp expect actual &&
- >expect &&
-
test_must_fail git show-ref D >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'show-ref -q' '
- >expect &&
-
git show-ref -q A >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
git show-ref -q tags/A >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
git show-ref -q refs/tags/A >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
test_must_fail git show-ref -q D >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'show-ref --verify' '
@@ -54,32 +50,28 @@ test_expect_success 'show-ref --verify' '
git show-ref --verify refs/tags/A >actual &&
test_cmp expect actual &&
- >expect &&
-
test_must_fail git show-ref --verify A >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
test_must_fail git show-ref --verify tags/A >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
test_must_fail git show-ref --verify D >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'show-ref --verify -q' '
- >expect &&
-
git show-ref --verify -q refs/tags/A >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
test_must_fail git show-ref --verify -q A >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
test_must_fail git show-ref --verify -q tags/A >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
test_must_fail git show-ref --verify -q D >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'show-ref -d' '
@@ -113,19 +105,17 @@ test_expect_success 'show-ref -d' '
git show-ref -d --verify refs/heads/master >actual &&
test_cmp expect actual &&
- >expect &&
-
test_must_fail git show-ref -d --verify master >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
test_must_fail git show-ref -d --verify heads/master >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
test_must_fail git show-ref --verify -d A C >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
test_must_fail git show-ref --verify -d tags/A tags/C >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
@@ -178,10 +168,8 @@ test_expect_success 'show-ref --verify HEAD' '
git show-ref --verify HEAD >actual &&
test_cmp expect actual &&
- >expect &&
-
git show-ref --verify -q HEAD >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'show-ref --verify with dangling ref' '
diff --git a/t/t1404-update-ref-errors.sh b/t/t1404-update-ref-errors.sh
index 3a887b5113..51a4f4c0ac 100755
--- a/t/t1404-update-ref-errors.sh
+++ b/t/t1404-update-ref-errors.sh
@@ -27,7 +27,7 @@ test_update_rejected () {
fi &&
printf "create $prefix/%s $C\n" $create >input &&
test_must_fail git update-ref --stdin <input 2>output.err &&
- grep -F "$error" output.err &&
+ test_i18ngrep -F "$error" output.err &&
git for-each-ref $prefix >actual &&
test_cmp unchanged actual
}
@@ -103,7 +103,7 @@ df_test() {
printf "%s\n" "delete $delname" "create $addname $D"
fi >commands &&
test_must_fail git update-ref --stdin <commands 2>output.err &&
- test_cmp expected-err output.err &&
+ test_i18ncmp expected-err output.err &&
printf "%s\n" "$C $delref" >expected-refs &&
git for-each-ref --format="%(objectname) %(refname)" $prefix/r >actual-refs &&
test_cmp expected-refs actual-refs
@@ -559,9 +559,9 @@ test_expect_success 'no bogus intermediate values during delete' '
{
# Note: the following command is intentionally run in the
# background. We increase the timeout so that `update-ref`
- # attempts to acquire the `packed-refs` lock for longer than
- # it takes for us to do the check then delete it:
- git -c core.packedrefstimeout=3000 update-ref -d $prefix/foo &
+ # attempts to acquire the `packed-refs` lock for much longer
+ # than it takes for us to do the check then delete it:
+ git -c core.packedrefstimeout=30000 update-ref -d $prefix/foo &
} &&
pid2=$! &&
# Give update-ref plenty of time to get to the point where it tries
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
index 8293131001..388b0611d8 100755
--- a/t/t1410-reflog.sh
+++ b/t/t1410-reflog.sh
@@ -290,9 +290,8 @@ test_expect_success 'stale dirs do not cause d/f conflicts (reflogs off)' '
# same as before, but we only create a reflog for "one" if
# it already exists, which it does not
git -c core.logallrefupdates=false branch one master &&
- : >expect &&
git log -g --format="%gd %gs" one >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
# Triggering the bug detected by this test requires a newline to fall
diff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh
index 596907758d..985daf1def 100755
--- a/t/t1411-reflog-show.sh
+++ b/t/t1411-reflog-show.sh
@@ -136,13 +136,12 @@ test_expect_success '--date magic does not override explicit @{0} syntax' '
test_cmp expect actual
'
-: >expect
test_expect_success 'empty reflog file' '
git branch empty &&
git reflog expire --expire=all refs/heads/empty &&
git log -g empty >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
# This guards against the alternative of showing the diffs vs. the
@@ -159,9 +158,9 @@ test_expect_success 'git log -g -p shows diffs vs. parents' '
git log -1 -p HEAD^ >log.one &&
git log -1 -p HEAD >log.two &&
(
- cat log.one; echo
- cat log.two; echo
- cat log.one; echo
+ cat log.one && echo &&
+ cat log.two && echo &&
+ cat log.one && echo &&
cat log.two
) >expect &&
test_cmp expect actual
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index 91fd71444d..0f2dd26f74 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -16,8 +16,7 @@ test_expect_success setup '
git checkout HEAD^0 &&
test_commit B fileB two &&
git tag -d A B &&
- git reflog expire --expire=now --all &&
- >empty
+ git reflog expire --expire=now --all
'
test_expect_success 'loose objects borrowed from alternate are not missing' '
@@ -29,12 +28,12 @@ test_expect_success 'loose objects borrowed from alternate are not missing' '
test_commit C fileC one &&
git fsck --no-dangling >../actual 2>&1
) &&
- test_cmp empty actual
+ test_must_be_empty actual
'
test_expect_success 'HEAD is part of refs, valid objects appear valid' '
git fsck >actual 2>&1 &&
- test_cmp empty actual
+ test_must_be_empty actual
'
# Corruption tests follow. Make sure to remove all traces of the
@@ -346,12 +345,12 @@ test_expect_success 'tag with NUL in header' '
test_expect_success 'cleaned up' '
git fsck >actual 2>&1 &&
- test_cmp empty actual
+ test_must_be_empty actual
'
test_expect_success 'rev-list --verify-objects' '
git rev-list --verify-objects --all >/dev/null 2>out &&
- test_cmp empty out
+ test_must_be_empty out
'
test_expect_success 'rev-list --verify-objects with bad sha1' '
@@ -372,7 +371,7 @@ test_expect_success 'rev-list --verify-objects with bad sha1' '
test_might_fail git rev-list --verify-objects refs/heads/bogus >/dev/null 2>out &&
cat out &&
- grep -q "error: sha1 mismatch 63ffffffffffffffffffffffffffffffffffffff" out
+ test_i18ngrep -q "error: sha1 mismatch 63ffffffffffffffffffffffffffffffffffffff" out
'
test_expect_success 'force fsck to ignore double author' '
diff --git a/t/t1501-work-tree.sh b/t/t1501-work-tree.sh
index afcdfafe45..3498d3d55e 100755
--- a/t/t1501-work-tree.sh
+++ b/t/t1501-work-tree.sh
@@ -41,7 +41,7 @@ test_expect_success 'setup: helper for testing rev-parse' '
# rev-parse --show-prefix should output
# a single newline when at the top of the work tree,
# but we test for that separately.
- test -z "$4" && ! test -s actual.prefix ||
+ test -z "$4" && test_must_be_empty actual.prefix ||
test_cmp expected.prefix actual.prefix
fi
}
diff --git a/t/t1507-rev-parse-upstream.sh b/t/t1507-rev-parse-upstream.sh
index 93c77eac45..fa3e499641 100755
--- a/t/t1507-rev-parse-upstream.sh
+++ b/t/t1507-rev-parse-upstream.sh
@@ -123,9 +123,9 @@ test_expect_success 'checkout -b new my-side@{u} forks from the same' '
test_expect_success 'merge my-side@{u} records the correct name' '
(
- cd clone || exit
- git checkout master || exit
- git branch -D new ;# can fail but is ok
+ cd clone &&
+ git checkout master &&
+ test_might_fail git branch -D new &&
git branch -t new my-side@{u} &&
git merge -s ours new@{u} &&
git show -s --pretty=tformat:%s >actual &&
@@ -138,8 +138,7 @@ test_expect_success 'branch -d other@{u}' '
git checkout -t -b other master &&
git branch -d @{u} &&
git for-each-ref refs/heads/master >actual &&
- >expect &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'checkout other@{u}' '
diff --git a/t/t1510-repo-setup.sh b/t/t1510-repo-setup.sh
index 972bd9c785..9974457f56 100755
--- a/t/t1510-repo-setup.sh
+++ b/t/t1510-repo-setup.sh
@@ -234,14 +234,14 @@ test_expect_success '#0: nonbare repo, no explicit configuration' '
try_repo 0 unset unset unset "" unset \
.git "$here/0" "$here/0" "(null)" \
.git "$here/0" "$here/0" sub/ 2>message &&
- ! test -s message
+ test_must_be_empty message
'
test_expect_success '#1: GIT_WORK_TREE without explicit GIT_DIR is accepted' '
try_repo 1 "$here" unset unset "" unset \
"$here/1/.git" "$here" "$here" 1/ \
"$here/1/.git" "$here" "$here" 1/sub/ 2>message &&
- ! test -s message
+ test_must_be_empty message
'
test_expect_success '#2: worktree defaults to cwd with explicit GIT_DIR' '
@@ -268,7 +268,7 @@ test_expect_success '#4: core.worktree without GIT_DIR set is accepted' '
try_case 4 unset unset \
.git "$here/4/sub" "$here/4" "(null)" \
"$here/4/.git" "$here/4/sub" "$here/4/sub" "(null)" 2>message &&
- ! test -s message
+ test_must_be_empty message
'
test_expect_success '#5: core.worktree + GIT_WORK_TREE is accepted' '
@@ -279,7 +279,7 @@ test_expect_success '#5: core.worktree + GIT_WORK_TREE is accepted' '
try_repo 5a .. unset "$here/5a" "" unset \
"$here/5a/.git" "$here" "$here" 5a/ \
"$here/5a/.git" "$here/5a" "$here/5a" sub/ &&
- ! test -s message
+ test_must_be_empty message
'
test_expect_success '#6: setting GIT_DIR brings core.worktree to life' '
@@ -376,7 +376,7 @@ test_expect_success '#9: GIT_WORK_TREE accepted with gitfile' '
try_repo 9 wt unset unset gitfile unset \
"$here/9.git" "$here/9/wt" "$here/9" "(null)" \
"$here/9.git" "$here/9/sub/wt" "$here/9/sub" "(null)" 2>message &&
- ! test -s message
+ test_must_be_empty message
'
test_expect_success '#10: GIT_DIR can point to gitfile' '
@@ -402,7 +402,7 @@ test_expect_success '#12: core.worktree with gitfile is accepted' '
try_repo 12 unset unset "$here/12" gitfile unset \
"$here/12.git" "$here/12" "$here/12" "(null)" \
"$here/12.git" "$here/12" "$here/12" sub/ 2>message &&
- ! test -s message
+ test_must_be_empty message
'
test_expect_success '#13: core.worktree+GIT_WORK_TREE accepted (with gitfile)' '
@@ -410,7 +410,7 @@ test_expect_success '#13: core.worktree+GIT_WORK_TREE accepted (with gitfile)' '
try_repo 13 non-existent-too unset non-existent gitfile unset \
"$here/13.git" "$here/13/non-existent-too" "$here/13" "(null)" \
"$here/13.git" "$here/13/sub/non-existent-too" "$here/13/sub" "(null)" 2>message &&
- ! test -s message
+ test_must_be_empty message
'
# case #14.
@@ -565,7 +565,7 @@ test_expect_success '#17: GIT_WORK_TREE without explicit GIT_DIR is accepted (ba
try_repo 17c "$here/17c" unset unset "" true \
.git "$here/17c" "$here/17c" "(null)" \
"$here/17c/.git" "$here/17c" "$here/17c" sub/ 2>message &&
- ! test -s message
+ test_must_be_empty message
'
test_expect_success '#18: bare .git named by GIT_DIR has no worktree' '
@@ -594,7 +594,7 @@ test_expect_success '#20a: core.worktree without GIT_DIR accepted (inside .git)'
"$here/20a/.git" "$here/20a" "$here/20a" .git/wt/ &&
try_case 20a/.git/wt/sub unset unset \
"$here/20a/.git" "$here/20a" "$here/20a" .git/wt/sub/ &&
- ! test -s message
+ test_must_be_empty message
'
test_expect_success '#20b/c: core.worktree and core.bare conflict' '
@@ -626,7 +626,7 @@ test_expect_success '#21: setup, core.worktree warns before overriding core.bare
export GIT_WORK_TREE &&
git status >/dev/null
) 2>message &&
- ! test -s message
+ test_must_be_empty message
'
run_wt_tests 21
@@ -742,7 +742,7 @@ test_expect_success '#25: GIT_WORK_TREE accepted if GIT_DIR unset (bare gitfile
try_repo 25 "$here/25" unset unset gitfile true \
"$here/25.git" "$here/25" "$here/25" "(null)" \
"$here/25.git" "$here/25" "$here/25" "sub/" 2>message &&
- ! test -s message
+ test_must_be_empty message
'
test_expect_success '#26: bare repo has no worktree (GIT_DIR -> gitfile case)' '
@@ -780,7 +780,7 @@ test_expect_success '#29: setup' '
export GIT_WORK_TREE &&
git status
) 2>message &&
- ! test -s message
+ test_must_be_empty message
'
run_wt_tests 29 gitfile
diff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh
index 96fe3754c8..e4d5b56014 100755
--- a/t/t1512-rev-parse-disambiguation.sh
+++ b/t/t1512-rev-parse-disambiguation.sh
@@ -34,8 +34,8 @@ test_expect_success 'blob and tree' '
for i in 0 1 2 3 4 5 6 7 8 9
do
echo $i
- done
- echo
+ done &&
+ echo &&
echo b1rwzyc3
) >a0blgqsjc &&
@@ -222,7 +222,7 @@ test_expect_success 'more history' '
test_might_fail git rm -f a0blgqsjc &&
(
- git cat-file blob $side:f5518nwu
+ git cat-file blob $side:f5518nwu &&
echo j3l0i9s6
) >ab2gs879 &&
git add ab2gs879 &&
diff --git a/t/t1600-index.sh b/t/t1600-index.sh
index c4422312f4..42962ed7d4 100755
--- a/t/t1600-index.sh
+++ b/t/t1600-index.sh
@@ -41,8 +41,7 @@ test_expect_success 'no warning with bogus GIT_INDEX_VERSION and existing index'
GIT_INDEX_VERSION=1 &&
export GIT_INDEX_VERSION &&
git add a 2>actual.err &&
- >expect.err &&
- test_i18ncmp expect.err actual.err
+ test_must_be_empty actual.err
)
'
diff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh
index 1e81b33b2e..b3b4d83eaf 100755
--- a/t/t1700-split-index.sh
+++ b/t/t1700-split-index.sh
@@ -143,9 +143,7 @@ test_expect_success 'remove file not in base index' '
test_expect_success 'remove file in base index' '
git update-index --force-remove one &&
git ls-files --stage >ls-files.actual &&
- cat >ls-files.expect <<-EOF &&
- EOF
- test_cmp ls-files.expect ls-files.actual &&
+ test_must_be_empty ls-files.actual &&
test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
cat >expect <<-EOF &&
@@ -435,7 +433,7 @@ test_expect_success 'writing split index with null sha1 does not write cache tre
commit=$(git commit-tree $tree -p HEAD <msg) &&
git update-ref HEAD "$commit" &&
GIT_ALLOW_NULL_SHA1=1 git reset --hard &&
- (test-tool dump-cache-tree >cache-tree.out || true) &&
+ test_might_fail test-tool dump-cache-tree >cache-tree.out &&
test_line_count = 0 cache-tree.out
'
diff --git a/t/t2013-checkout-submodule.sh b/t/t2013-checkout-submodule.sh
index 6ef15738e4..8f86b5f4b2 100755
--- a/t/t2013-checkout-submodule.sh
+++ b/t/t2013-checkout-submodule.sh
@@ -44,7 +44,7 @@ test_expect_success '"checkout <submodule>" honors diff.ignoreSubmodules' '
git config diff.ignoreSubmodules dirty &&
echo x> submodule/untracked &&
git checkout HEAD >actual 2>&1 &&
- ! test -s actual
+ test_must_be_empty actual
'
test_expect_success '"checkout <submodule>" honors submodule.*.ignore from .gitmodules' '
@@ -52,7 +52,7 @@ test_expect_success '"checkout <submodule>" honors submodule.*.ignore from .gitm
git config -f .gitmodules submodule.submodule.path submodule &&
git config -f .gitmodules submodule.submodule.ignore untracked &&
git checkout HEAD >actual 2>&1 &&
- ! test -s actual
+ test_must_be_empty actual
'
test_expect_success '"checkout <submodule>" honors submodule.*.ignore from .git/config' '
@@ -60,7 +60,7 @@ test_expect_success '"checkout <submodule>" honors submodule.*.ignore from .git/
git config submodule.submodule.path submodule &&
git config submodule.submodule.ignore all &&
git checkout HEAD >actual 2>&1 &&
- ! test -s actual
+ test_must_be_empty actual
'
KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS=1
diff --git a/t/t2016-checkout-patch.sh b/t/t2016-checkout-patch.sh
index 9cd0ac4ba3..47aeb0b167 100755
--- a/t/t2016-checkout-patch.sh
+++ b/t/t2016-checkout-patch.sh
@@ -20,33 +20,33 @@ test_expect_success PERL 'setup' '
test_expect_success PERL 'saying "n" does nothing' '
set_and_save_state dir/foo work head &&
- (echo n; echo n) | git checkout -p &&
+ test_write_lines n n | git checkout -p &&
verify_saved_state bar &&
verify_saved_state dir/foo
'
test_expect_success PERL 'git checkout -p' '
- (echo n; echo y) | git checkout -p &&
+ test_write_lines n y | git checkout -p &&
verify_saved_state bar &&
verify_state dir/foo head head
'
test_expect_success PERL 'git checkout -p with staged changes' '
set_state dir/foo work index &&
- (echo n; echo y) | git checkout -p &&
+ test_write_lines n y | git checkout -p &&
verify_saved_state bar &&
verify_state dir/foo index index
'
test_expect_success PERL 'git checkout -p HEAD with NO staged changes: abort' '
set_and_save_state dir/foo work head &&
- (echo n; echo y; echo n) | git checkout -p HEAD &&
+ test_write_lines n y n | git checkout -p HEAD &&
verify_saved_state bar &&
verify_saved_state dir/foo
'
test_expect_success PERL 'git checkout -p HEAD with NO staged changes: apply' '
- (echo n; echo y; echo y) | git checkout -p HEAD &&
+ test_write_lines n y y | git checkout -p HEAD &&
verify_saved_state bar &&
verify_state dir/foo head head
'
@@ -54,14 +54,14 @@ test_expect_success PERL 'git checkout -p HEAD with NO staged changes: apply' '
test_expect_success PERL 'git checkout -p HEAD with change already staged' '
set_state dir/foo index index &&
# the third n is to get out in case it mistakenly does not apply
- (echo n; echo y; echo n) | git checkout -p HEAD &&
+ test_write_lines n y n | git checkout -p HEAD &&
verify_saved_state bar &&
verify_state dir/foo head head
'
test_expect_success PERL 'git checkout -p HEAD^' '
# the third n is to get out in case it mistakenly does not apply
- (echo n; echo y; echo n) | git checkout -p HEAD^ &&
+ test_write_lines n y n | git checkout -p HEAD^ &&
verify_saved_state bar &&
verify_state dir/foo parent parent
'
@@ -69,7 +69,7 @@ test_expect_success PERL 'git checkout -p HEAD^' '
test_expect_success PERL 'git checkout -p handles deletion' '
set_state dir/foo work index &&
rm dir/foo &&
- (echo n; echo y) | git checkout -p &&
+ test_write_lines n y | git checkout -p &&
verify_saved_state bar &&
verify_state dir/foo index index
'
@@ -81,21 +81,21 @@ test_expect_success PERL 'git checkout -p handles deletion' '
test_expect_success PERL 'path limiting works: dir' '
set_state dir/foo work head &&
- (echo y; echo n) | git checkout -p dir &&
+ test_write_lines y n | git checkout -p dir &&
verify_saved_state bar &&
verify_state dir/foo head head
'
test_expect_success PERL 'path limiting works: -- dir' '
set_state dir/foo work head &&
- (echo y; echo n) | git checkout -p -- dir &&
+ test_write_lines y n | git checkout -p -- dir &&
verify_saved_state bar &&
verify_state dir/foo head head
'
test_expect_success PERL 'path limiting works: HEAD^ -- dir' '
# the third n is to get out in case it mistakenly does not apply
- (echo y; echo n; echo n) | git checkout -p HEAD^ -- dir &&
+ test_write_lines y n n | git checkout -p HEAD^ -- dir &&
verify_saved_state bar &&
verify_state dir/foo parent parent
'
@@ -103,7 +103,7 @@ test_expect_success PERL 'path limiting works: HEAD^ -- dir' '
test_expect_success PERL 'path limiting works: foo inside dir' '
set_state dir/foo work head &&
# the third n is to get out in case it mistakenly does not apply
- (echo y; echo n; echo n) | (cd dir && git checkout -p foo) &&
+ test_write_lines y n n | (cd dir && git checkout -p foo) &&
verify_saved_state bar &&
verify_state dir/foo head head
'
diff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh
index 3e5ac81bd2..69b6774d10 100755
--- a/t/t2024-checkout-dwim.sh
+++ b/t/t2024-checkout-dwim.sh
@@ -23,6 +23,11 @@ test_branch_upstream () {
test_cmp expect.upstream actual.upstream
}
+status_uno_is_clean () {
+ git status -uno --porcelain >status.actual &&
+ test_must_be_empty status.actual
+}
+
test_expect_success 'setup' '
test_commit my_master &&
git init repo_a &&
@@ -55,6 +60,7 @@ test_expect_success 'checkout of non-existing branch fails' '
test_might_fail git branch -D xyzzy &&
test_must_fail git checkout xyzzy &&
+ status_uno_is_clean &&
test_must_fail git rev-parse --verify refs/heads/xyzzy &&
test_branch master
'
@@ -64,15 +70,52 @@ test_expect_success 'checkout of branch from multiple remotes fails #1' '
test_might_fail git branch -D foo &&
test_must_fail git checkout foo &&
+ status_uno_is_clean &&
test_must_fail git rev-parse --verify refs/heads/foo &&
test_branch master
'
+test_expect_success 'checkout of branch from multiple remotes fails with advice' '
+ git checkout -B master &&
+ test_might_fail git branch -D foo &&
+ test_must_fail git checkout foo 2>stderr &&
+ test_branch master &&
+ status_uno_is_clean &&
+ test_i18ngrep "^hint: " stderr &&
+ test_must_fail git -c advice.checkoutAmbiguousRemoteBranchName=false \
+ checkout foo 2>stderr &&
+ test_branch master &&
+ status_uno_is_clean &&
+ test_i18ngrep ! "^hint: " stderr
+'
+
+test_expect_success PERL 'checkout -p with multiple remotes does not print advice' '
+ git checkout -B master &&
+ test_might_fail git branch -D foo &&
+
+ git checkout -p foo 2>stderr &&
+ test_i18ngrep ! "^hint: " stderr &&
+ status_uno_is_clean
+'
+
+test_expect_success 'checkout of branch from multiple remotes succeeds with checkout.defaultRemote #1' '
+ git checkout -B master &&
+ status_uno_is_clean &&
+ test_might_fail git branch -D foo &&
+
+ git -c checkout.defaultRemote=repo_a checkout foo &&
+ status_uno_is_clean &&
+ test_branch foo &&
+ test_cmp_rev remotes/repo_a/foo HEAD &&
+ test_branch_upstream foo repo_a foo
+'
+
test_expect_success 'checkout of branch from a single remote succeeds #1' '
git checkout -B master &&
test_might_fail git branch -D bar &&
git checkout bar &&
+ status_uno_is_clean &&
test_branch bar &&
test_cmp_rev remotes/repo_a/bar HEAD &&
test_branch_upstream bar repo_a bar
@@ -83,6 +126,7 @@ test_expect_success 'checkout of branch from a single remote succeeds #2' '
test_might_fail git branch -D baz &&
git checkout baz &&
+ status_uno_is_clean &&
test_branch baz &&
test_cmp_rev remotes/other_b/baz HEAD &&
test_branch_upstream baz repo_b baz
@@ -90,6 +134,7 @@ test_expect_success 'checkout of branch from a single remote succeeds #2' '
test_expect_success '--no-guess suppresses branch auto-vivification' '
git checkout -B master &&
+ status_uno_is_clean &&
test_might_fail git branch -D bar &&
test_must_fail git checkout --no-guess bar &&
@@ -99,6 +144,7 @@ test_expect_success '--no-guess suppresses branch auto-vivification' '
test_expect_success 'setup more remotes with unconventional refspecs' '
git checkout -B master &&
+ status_uno_is_clean &&
git init repo_c &&
(
cd repo_c &&
@@ -128,27 +174,33 @@ test_expect_success 'setup more remotes with unconventional refspecs' '
test_expect_success 'checkout of branch from multiple remotes fails #2' '
git checkout -B master &&
+ status_uno_is_clean &&
test_might_fail git branch -D bar &&
test_must_fail git checkout bar &&
+ status_uno_is_clean &&
test_must_fail git rev-parse --verify refs/heads/bar &&
test_branch master
'
test_expect_success 'checkout of branch from multiple remotes fails #3' '
git checkout -B master &&
+ status_uno_is_clean &&
test_might_fail git branch -D baz &&
test_must_fail git checkout baz &&
+ status_uno_is_clean &&
test_must_fail git rev-parse --verify refs/heads/baz &&
test_branch master
'
test_expect_success 'checkout of branch from a single remote succeeds #3' '
git checkout -B master &&
+ status_uno_is_clean &&
test_might_fail git branch -D spam &&
git checkout spam &&
+ status_uno_is_clean &&
test_branch spam &&
test_cmp_rev refs/remotes/extra_dir/repo_c/extra_dir/spam HEAD &&
test_branch_upstream spam repo_c spam
@@ -156,9 +208,11 @@ test_expect_success 'checkout of branch from a single remote succeeds #3' '
test_expect_success 'checkout of branch from a single remote succeeds #4' '
git checkout -B master &&
+ status_uno_is_clean &&
test_might_fail git branch -D eggs &&
git checkout eggs &&
+ status_uno_is_clean &&
test_branch eggs &&
test_cmp_rev refs/repo_d/eggs HEAD &&
test_branch_upstream eggs repo_d eggs
@@ -166,32 +220,38 @@ test_expect_success 'checkout of branch from a single remote succeeds #4' '
test_expect_success 'checkout of branch with a file having the same name fails' '
git checkout -B master &&
+ status_uno_is_clean &&
test_might_fail git branch -D spam &&
>spam &&
test_must_fail git checkout spam &&
+ status_uno_is_clean &&
test_must_fail git rev-parse --verify refs/heads/spam &&
test_branch master
'
test_expect_success 'checkout of branch with a file in subdir having the same name fails' '
git checkout -B master &&
+ status_uno_is_clean &&
test_might_fail git branch -D spam &&
>spam &&
mkdir sub &&
mv spam sub/spam &&
test_must_fail git -C sub checkout spam &&
+ status_uno_is_clean &&
test_must_fail git rev-parse --verify refs/heads/spam &&
test_branch master
'
test_expect_success 'checkout <branch> -- succeeds, even if a file with the same name exists' '
git checkout -B master &&
+ status_uno_is_clean &&
test_might_fail git branch -D spam &&
>spam &&
git checkout spam -- &&
+ status_uno_is_clean &&
test_branch spam &&
test_cmp_rev refs/remotes/extra_dir/repo_c/extra_dir/spam HEAD &&
test_branch_upstream spam repo_c spam
@@ -200,6 +260,7 @@ test_expect_success 'checkout <branch> -- succeeds, even if a file with the same
test_expect_success 'loosely defined local base branch is reported correctly' '
git checkout master &&
+ status_uno_is_clean &&
git branch strict &&
git branch loose &&
git commit --allow-empty -m "a bit more" &&
@@ -210,7 +271,9 @@ test_expect_success 'loosely defined local base branch is reported correctly' '
test_config branch.loose.merge master &&
git checkout strict | sed -e "s/strict/BRANCHNAME/g" >expect &&
+ status_uno_is_clean &&
git checkout loose | sed -e "s/loose/BRANCHNAME/g" >actual &&
+ status_uno_is_clean &&
test_cmp expect actual
'
diff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh
index d2e49f7632..286bba35d8 100755
--- a/t/t2025-worktree-add.sh
+++ b/t/t2025-worktree-add.sh
@@ -252,6 +252,11 @@ test_expect_success 'add -B' '
test_cmp_rev master^ poodle
'
+test_expect_success 'add --quiet' '
+ git worktree add --quiet another-worktree master 2>actual &&
+ test_must_be_empty actual
+'
+
test_expect_success 'local clone from linked checkout' '
git clone --local here here-clone &&
( cd here-clone && git fsck )
@@ -402,6 +407,26 @@ test_expect_success '"add" <path> <branch> dwims' '
)
'
+test_expect_success '"add" <path> <branch> dwims with checkout.defaultRemote' '
+ test_when_finished rm -rf repo_upstream repo_dwim foo &&
+ setup_remote_repo repo_upstream repo_dwim &&
+ git init repo_dwim &&
+ (
+ cd repo_dwim &&
+ git remote add repo_upstream2 ../repo_upstream &&
+ git fetch repo_upstream2 &&
+ test_must_fail git worktree add ../foo foo &&
+ git -c checkout.defaultRemote=repo_upstream worktree add ../foo foo &&
+ git status -uno --porcelain >status.actual &&
+ test_must_be_empty status.actual
+ ) &&
+ (
+ cd foo &&
+ test_branch_upstream foo repo_upstream foo &&
+ test_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo
+ )
+'
+
test_expect_success 'git worktree add does not match remote' '
test_when_finished rm -rf repo_a repo_b foo &&
setup_remote_repo repo_a repo_b &&
@@ -527,4 +552,22 @@ test_expect_success '"add" in bare repo invokes post-checkout hook' '
test_cmp hook.expect goozy/hook.actual
'
+test_expect_success '"add" an existing but missing worktree' '
+ git worktree add --detach pneu &&
+ test_must_fail git worktree add --detach pneu &&
+ rm -fr pneu &&
+ test_must_fail git worktree add --detach pneu &&
+ git worktree add --force --detach pneu
+'
+
+test_expect_success '"add" an existing locked but missing worktree' '
+ git worktree add --detach gnoo &&
+ git worktree lock gnoo &&
+ test_when_finished "git worktree unlock gnoo || :" &&
+ rm -fr gnoo &&
+ test_must_fail git worktree add --detach gnoo &&
+ test_must_fail git worktree add --force --detach gnoo &&
+ git worktree add --force --force --detach gnoo
+'
+
test_done
diff --git a/t/t2028-worktree-move.sh b/t/t2028-worktree-move.sh
index 5f7d45b7b7..33c0337733 100755
--- a/t/t2028-worktree-move.sh
+++ b/t/t2028-worktree-move.sh
@@ -98,6 +98,20 @@ test_expect_success 'move worktree to another dir' '
test_cmp expected2 actual2
'
+test_expect_success 'move locked worktree (force)' '
+ test_when_finished "
+ git worktree unlock flump || :
+ git worktree remove flump || :
+ git worktree unlock ploof || :
+ git worktree remove ploof || :
+ " &&
+ git worktree add --detach flump &&
+ git worktree lock flump &&
+ test_must_fail git worktree move flump ploof" &&
+ test_must_fail git worktree move --force flump ploof" &&
+ git worktree move --force --force flump ploof
+'
+
test_expect_success 'remove main worktree' '
test_must_fail git worktree remove .
'
@@ -141,4 +155,34 @@ test_expect_success 'NOT remove missing-but-locked worktree' '
test_path_is_dir .git/worktrees/gone-but-locked
'
+test_expect_success 'proper error when worktree not found' '
+ for i in noodle noodle/bork
+ do
+ test_must_fail git worktree lock $i 2>err &&
+ test_i18ngrep "not a working tree" err || return 1
+ done
+'
+
+test_expect_success 'remove locked worktree (force)' '
+ git worktree add --detach gumby &&
+ test_when_finished "git worktree remove gumby || :" &&
+ git worktree lock gumby &&
+ test_when_finished "git worktree unlock gumby || :" &&
+ test_must_fail git worktree remove gumby &&
+ test_must_fail git worktree remove --force gumby &&
+ git worktree remove --force --force gumby
+'
+
+test_expect_success 'remove cleans up .git/worktrees when empty' '
+ git init moog &&
+ (
+ cd moog &&
+ test_commit bim &&
+ git worktree add --detach goom &&
+ test_path_exists .git/worktrees &&
+ git worktree remove goom &&
+ test_path_is_missing .git/worktrees
+ )
+'
+
test_done
diff --git a/t/t2103-update-index-ignore-missing.sh b/t/t2103-update-index-ignore-missing.sh
index 332694e7d3..0114f05228 100755
--- a/t/t2103-update-index-ignore-missing.sh
+++ b/t/t2103-update-index-ignore-missing.sh
@@ -32,7 +32,7 @@ test_expect_success basics '
test_create_repo xyzzy &&
cd xyzzy &&
>file &&
- git add file
+ git add file &&
git commit -m "sub initial"
) &&
git add xyzzy &&
diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh
index 314c73c5a7..f764b7e3f5 100755
--- a/t/t2200-add-update.sh
+++ b/t/t2200-add-update.sh
@@ -88,9 +88,8 @@ test_expect_success 'non-qualified update in subdir updates from the root' '
echo even more >>sub2 &&
git add -u
) &&
- : >expect &&
git diff-files --name-only >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'replace a file with a symlink' '
diff --git a/t/t2202-add-addremove.sh b/t/t2202-add-addremove.sh
index 6a5a3166b1..9ee659098c 100755
--- a/t/t2202-add-addremove.sh
+++ b/t/t2202-add-addremove.sh
@@ -6,12 +6,12 @@ test_description='git add --all'
test_expect_success setup '
(
- echo .gitignore
+ echo .gitignore &&
echo will-remove
) >expect &&
(
- echo actual
- echo expect
+ echo actual &&
+ echo expect &&
echo ignored
) >.gitignore &&
git --literal-pathspecs add --all &&
@@ -25,10 +25,10 @@ test_expect_success setup '
test_expect_success 'git add --all' '
(
- echo .gitignore
- echo not-ignored
- echo "M .gitignore"
- echo "A not-ignored"
+ echo .gitignore &&
+ echo not-ignored &&
+ echo "M .gitignore" &&
+ echo "A not-ignored" &&
echo "D will-remove"
) >expect &&
>ignored &&
@@ -48,8 +48,7 @@ test_expect_success 'Just "git add" is a no-op' '
>will-not-be-added &&
git add &&
git diff-index --name-status --cached HEAD >actual &&
- >expect &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_done
diff --git a/t/t2203-add-intent.sh b/t/t2203-add-intent.sh
index e7a400b4c7..68e54d5c44 100755
--- a/t/t2203-add-intent.sh
+++ b/t/t2203-add-intent.sh
@@ -195,8 +195,7 @@ test_expect_success 'rename detection finds the right names' '
test_cmp expected.4 actual.4 &&
git diff --cached --stat >actual.5 &&
- : >expected.5 &&
- test_cmp expected.5 actual.5
+ test_must_be_empty actual.5
)
'
@@ -241,8 +240,7 @@ test_expect_success 'diff-files/diff-cached shows ita as new/not-new files' '
echo " create mode 100644 new-ita" >expected &&
test_cmp expected actual &&
git diff --cached --summary >actual2 &&
- : >expected2 &&
- test_cmp expected2 actual2
+ test_must_be_empty actual2
'
diff --git a/t/t2204-add-ignored.sh b/t/t2204-add-ignored.sh
index 8340ac2f07..2e07365bbb 100755
--- a/t/t2204-add-ignored.sh
+++ b/t/t2204-add-ignored.sh
@@ -31,7 +31,7 @@ do
rm -f .git/index &&
test_must_fail git add "$i" 2>err &&
git ls-files "$i" >out &&
- ! test -s out
+ test_must_be_empty out
'
test_expect_success "complaints for ignored $i output" '
@@ -42,7 +42,7 @@ do
rm -f .git/index &&
test_must_fail git add "$i" file 2>err &&
git ls-files "$i" >out &&
- ! test -s out
+ test_must_be_empty out
'
test_expect_success "complaints for ignored $i with unignored file output" '
test_i18ngrep -e "Use -f if" err
@@ -57,7 +57,7 @@ do
cd dir &&
test_must_fail git add "$i" 2>err &&
git ls-files "$i" >out &&
- ! test -s out
+ test_must_be_empty out
)
'
@@ -77,7 +77,7 @@ do
cd sub &&
test_must_fail git add "$i" 2>err &&
git ls-files "$i" >out &&
- ! test -s out
+ test_must_be_empty out
)
'
diff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh
index c525656b2c..afd4756134 100755
--- a/t/t3000-ls-files-others.sh
+++ b/t/t3000-ls-files-others.sh
@@ -84,7 +84,7 @@ test_expect_success SYMLINKS 'ls-files --others with symlinked submodule' '
) &&
(
cd super &&
- "$SHELL_PATH" "$TEST_DIRECTORY/../contrib/workdir/git-new-workdir" ../sub sub
+ "$SHELL_PATH" "$TEST_DIRECTORY/../contrib/workdir/git-new-workdir" ../sub sub &&
git ls-files --others --exclude-standard >../actual
) &&
echo sub/ >expect &&
diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh
index 3fc484e8c3..1ec7cb57c7 100755
--- a/t/t3001-ls-files-others-exclude.sh
+++ b/t/t3001-ls-files-others-exclude.sh
@@ -210,8 +210,7 @@ test_expect_success 'subdirectory ignore (toplevel)' '
cd top &&
git ls-files -o --exclude-standard
) >actual &&
- >expect &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'subdirectory ignore (l1/l2)' '
@@ -219,8 +218,7 @@ test_expect_success 'subdirectory ignore (l1/l2)' '
cd top/l1/l2 &&
git ls-files -o --exclude-standard
) >actual &&
- >expect &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'subdirectory ignore (l1)' '
@@ -228,8 +226,7 @@ test_expect_success 'subdirectory ignore (l1)' '
cd top/l1 &&
git ls-files -o --exclude-standard
) >actual &&
- >expect &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'show/hide empty ignored directory (setup)' '
@@ -251,8 +248,7 @@ test_expect_success 'hide empty ignored directory with --no-empty-directory' '
cd top &&
git ls-files -o -i --exclude l1 --directory --no-empty-directory
) >actual &&
- >expect &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'show/hide empty ignored sub-directory (setup)' '
@@ -277,14 +273,12 @@ test_expect_success 'hide empty ignored sub-directory with --no-empty-directory'
cd top &&
git ls-files -o -i --exclude l1 --directory --no-empty-directory
) >actual &&
- >expect &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'pattern matches prefix completely' '
- : >expect &&
git ls-files -i -o --exclude "/three/a.3[abc]" >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'ls-files with "**" patterns' '
@@ -300,9 +294,8 @@ EOF
test_expect_success 'ls-files with "**" patterns and no slashes' '
- : >expect &&
git ls-files -o -i --exclude "one**a.1" >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_done
diff --git a/t/t3004-ls-files-basic.sh b/t/t3004-ls-files-basic.sh
index 9c7adbdbe1..9fd5a1f188 100755
--- a/t/t3004-ls-files-basic.sh
+++ b/t/t3004-ls-files-basic.sh
@@ -8,16 +8,14 @@ command-line arguments.
. ./test-lib.sh
->empty
-
test_expect_success 'ls-files in empty repository' '
git ls-files >actual &&
- test_cmp empty actual
+ test_must_be_empty actual
'
test_expect_success 'ls-files with nonexistent path' '
git ls-files doesnotexist >actual &&
- test_cmp empty actual
+ test_must_be_empty actual
'
test_expect_success 'ls-files with nonsense option' '
diff --git a/t/t3005-ls-files-relative.sh b/t/t3005-ls-files-relative.sh
index 377869432e..209b4c7cd8 100755
--- a/t/t3005-ls-files-relative.sh
+++ b/t/t3005-ls-files-relative.sh
@@ -44,13 +44,13 @@ test_expect_success 'ls-files -c' '
cd top/sub &&
for f in ../y*
do
- echo "error: pathspec $sq$f$sq did not match any file(s) known to git."
+ echo "error: pathspec $sq$f$sq did not match any file(s) known to git"
done >expect.err &&
echo "Did you forget to ${sq}git add${sq}?" >>expect.err &&
ls ../x* >expect.out &&
test_must_fail git ls-files -c --error-unmatch ../[xy]* >actual.out 2>actual.err &&
test_cmp expect.out actual.out &&
- test_cmp expect.err actual.err
+ test_i18ncmp expect.err actual.err
)
'
@@ -59,13 +59,13 @@ test_expect_success 'ls-files -o' '
cd top/sub &&
for f in ../x*
do
- echo "error: pathspec $sq$f$sq did not match any file(s) known to git."
+ echo "error: pathspec $sq$f$sq did not match any file(s) known to git"
done >expect.err &&
echo "Did you forget to ${sq}git add${sq}?" >>expect.err &&
ls ../y* >expect.out &&
test_must_fail git ls-files -o --error-unmatch ../[xy]* >actual.out 2>actual.err &&
test_cmp expect.out actual.out &&
- test_cmp expect.err actual.err
+ test_i18ncmp expect.err actual.err
)
'
diff --git a/t/t3006-ls-files-long.sh b/t/t3006-ls-files-long.sh
index 202ad658b8..e109c3fbfb 100755
--- a/t/t3006-ls-files-long.sh
+++ b/t/t3006-ls-files-long.sh
@@ -29,7 +29,7 @@ test_expect_success 'overly-long path does not replace another by mistake' '
printf "$pat" "$blob_a" "$path_a" "$blob_z" "$path_z" |
git update-index --add --index-info &&
(
- echo "$path_a"
+ echo "$path_a" &&
echo "$path_z"
) >expect &&
git ls-files >actual &&
diff --git a/t/t3008-ls-files-lazy-init-name-hash.sh b/t/t3008-ls-files-lazy-init-name-hash.sh
index 08af596ba6..64f047332b 100755
--- a/t/t3008-ls-files-lazy-init-name-hash.sh
+++ b/t/t3008-ls-files-lazy-init-name-hash.sh
@@ -14,10 +14,10 @@ LAZY_THREAD_COST=2000
test_expect_success 'no buffer overflow in lazy_init_name_hash' '
(
- test_seq $LAZY_THREAD_COST | sed "s/^/a_/"
- echo b/b/b
- test_seq $LAZY_THREAD_COST | sed "s/^/c_/"
- test_seq 50 | sed "s/^/d_/" | tr "\n" "/"; echo d
+ test_seq $LAZY_THREAD_COST | sed "s/^/a_/" &&
+ echo b/b/b &&
+ test_seq $LAZY_THREAD_COST | sed "s/^/c_/" &&
+ test_seq 50 | sed "s/^/d_/" | tr "\n" "/" && echo d
) |
sed "s/^/100644 $EMPTY_BLOB /" |
git update-index --index-info &&
diff --git a/t/t3030-merge-recursive.sh b/t/t3030-merge-recursive.sh
index 3563e77b37..ff641b348a 100755
--- a/t/t3030-merge-recursive.sh
+++ b/t/t3030-merge-recursive.sh
@@ -36,15 +36,15 @@ test_expect_success 'setup 1' '
test_tick &&
git commit -m "master modifies a and d/e" &&
c1=$(git rev-parse --verify HEAD) &&
- ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ ( git ls-tree -r HEAD && git ls-files -s ) >actual &&
(
- echo "100644 blob $o1 a"
- echo "100644 blob $o0 b"
- echo "100644 blob $o0 c"
- echo "100644 blob $o1 d/e"
- echo "100644 $o1 0 a"
- echo "100644 $o0 0 b"
- echo "100644 $o0 0 c"
+ echo "100644 blob $o1 a" &&
+ echo "100644 blob $o0 b" &&
+ echo "100644 blob $o0 c" &&
+ echo "100644 blob $o1 d/e" &&
+ echo "100644 $o1 0 a" &&
+ echo "100644 $o0 0 b" &&
+ echo "100644 $o0 0 c" &&
echo "100644 $o1 0 d/e"
) >expected &&
test_cmp expected actual
@@ -54,15 +54,15 @@ test_expect_success 'setup 2' '
rm -rf [abcd] &&
git checkout side &&
- ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ ( git ls-tree -r HEAD && git ls-files -s ) >actual &&
(
- echo "100644 blob $o0 a"
- echo "100644 blob $o0 b"
- echo "100644 blob $o0 c"
- echo "100644 blob $o0 d/e"
- echo "100644 $o0 0 a"
- echo "100644 $o0 0 b"
- echo "100644 $o0 0 c"
+ echo "100644 blob $o0 a" &&
+ echo "100644 blob $o0 b" &&
+ echo "100644 blob $o0 c" &&
+ echo "100644 blob $o0 d/e" &&
+ echo "100644 $o0 0 a" &&
+ echo "100644 $o0 0 b" &&
+ echo "100644 $o0 0 c" &&
echo "100644 $o0 0 d/e"
) >expected &&
test_cmp expected actual &&
@@ -75,15 +75,15 @@ test_expect_success 'setup 2' '
test_tick &&
git commit -m "side modifies a" &&
c2=$(git rev-parse --verify HEAD) &&
- ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ ( git ls-tree -r HEAD && git ls-files -s ) >actual &&
(
- echo "100644 blob $o2 a"
- echo "100644 blob $o0 b"
- echo "100644 blob $o0 c"
- echo "100644 blob $o0 d/e"
- echo "100644 $o2 0 a"
- echo "100644 $o0 0 b"
- echo "100644 $o0 0 c"
+ echo "100644 blob $o2 a" &&
+ echo "100644 blob $o0 b" &&
+ echo "100644 blob $o0 c" &&
+ echo "100644 blob $o0 d/e" &&
+ echo "100644 $o2 0 a" &&
+ echo "100644 $o0 0 b" &&
+ echo "100644 $o0 0 c" &&
echo "100644 $o0 0 d/e"
) >expected &&
test_cmp expected actual
@@ -93,15 +93,15 @@ test_expect_success 'setup 3' '
rm -rf [abcd] &&
git checkout df-1 &&
- ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ ( git ls-tree -r HEAD && git ls-files -s ) >actual &&
(
- echo "100644 blob $o0 a"
- echo "100644 blob $o0 b"
- echo "100644 blob $o0 c"
- echo "100644 blob $o0 d/e"
- echo "100644 $o0 0 a"
- echo "100644 $o0 0 b"
- echo "100644 $o0 0 c"
+ echo "100644 blob $o0 a" &&
+ echo "100644 blob $o0 b" &&
+ echo "100644 blob $o0 c" &&
+ echo "100644 blob $o0 d/e" &&
+ echo "100644 $o0 0 a" &&
+ echo "100644 $o0 0 b" &&
+ echo "100644 $o0 0 c" &&
echo "100644 $o0 0 d/e"
) >expected &&
test_cmp expected actual &&
@@ -112,15 +112,15 @@ test_expect_success 'setup 3' '
test_tick &&
git commit -m "df-1 makes b/c" &&
c3=$(git rev-parse --verify HEAD) &&
- ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ ( git ls-tree -r HEAD && git ls-files -s ) >actual &&
(
- echo "100644 blob $o0 a"
- echo "100644 blob $o3 b/c"
- echo "100644 blob $o0 c"
- echo "100644 blob $o0 d/e"
- echo "100644 $o0 0 a"
- echo "100644 $o3 0 b/c"
- echo "100644 $o0 0 c"
+ echo "100644 blob $o0 a" &&
+ echo "100644 blob $o3 b/c" &&
+ echo "100644 blob $o0 c" &&
+ echo "100644 blob $o0 d/e" &&
+ echo "100644 $o0 0 a" &&
+ echo "100644 $o3 0 b/c" &&
+ echo "100644 $o0 0 c" &&
echo "100644 $o0 0 d/e"
) >expected &&
test_cmp expected actual
@@ -130,15 +130,15 @@ test_expect_success 'setup 4' '
rm -rf [abcd] &&
git checkout df-2 &&
- ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ ( git ls-tree -r HEAD && git ls-files -s ) >actual &&
(
- echo "100644 blob $o0 a"
- echo "100644 blob $o0 b"
- echo "100644 blob $o0 c"
- echo "100644 blob $o0 d/e"
- echo "100644 $o0 0 a"
- echo "100644 $o0 0 b"
- echo "100644 $o0 0 c"
+ echo "100644 blob $o0 a" &&
+ echo "100644 blob $o0 b" &&
+ echo "100644 blob $o0 c" &&
+ echo "100644 blob $o0 d/e" &&
+ echo "100644 $o0 0 a" &&
+ echo "100644 $o0 0 b" &&
+ echo "100644 $o0 0 c" &&
echo "100644 $o0 0 d/e"
) >expected &&
test_cmp expected actual &&
@@ -149,15 +149,15 @@ test_expect_success 'setup 4' '
test_tick &&
git commit -m "df-2 makes a/c" &&
c4=$(git rev-parse --verify HEAD) &&
- ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ ( git ls-tree -r HEAD && git ls-files -s ) >actual &&
(
- echo "100644 blob $o4 a/c"
- echo "100644 blob $o0 b"
- echo "100644 blob $o0 c"
- echo "100644 blob $o0 d/e"
- echo "100644 $o4 0 a/c"
- echo "100644 $o0 0 b"
- echo "100644 $o0 0 c"
+ echo "100644 blob $o4 a/c" &&
+ echo "100644 blob $o0 b" &&
+ echo "100644 blob $o0 c" &&
+ echo "100644 blob $o0 d/e" &&
+ echo "100644 $o4 0 a/c" &&
+ echo "100644 $o0 0 b" &&
+ echo "100644 $o0 0 c" &&
echo "100644 $o0 0 d/e"
) >expected &&
test_cmp expected actual
@@ -167,15 +167,15 @@ test_expect_success 'setup 5' '
rm -rf [abcd] &&
git checkout remove &&
- ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ ( git ls-tree -r HEAD && git ls-files -s ) >actual &&
(
- echo "100644 blob $o0 a"
- echo "100644 blob $o0 b"
- echo "100644 blob $o0 c"
- echo "100644 blob $o0 d/e"
- echo "100644 $o0 0 a"
- echo "100644 $o0 0 b"
- echo "100644 $o0 0 c"
+ echo "100644 blob $o0 a" &&
+ echo "100644 blob $o0 b" &&
+ echo "100644 blob $o0 c" &&
+ echo "100644 blob $o0 d/e" &&
+ echo "100644 $o0 0 a" &&
+ echo "100644 $o0 0 b" &&
+ echo "100644 $o0 0 c" &&
echo "100644 $o0 0 d/e"
) >expected &&
test_cmp expected actual &&
@@ -190,13 +190,13 @@ test_expect_success 'setup 5' '
test_tick &&
git commit -m "remove removes b and modifies a" &&
c5=$(git rev-parse --verify HEAD) &&
- ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ ( git ls-tree -r HEAD && git ls-files -s ) >actual &&
(
- echo "100644 blob $o5 a"
- echo "100644 blob $o0 c"
- echo "100644 blob $o0 d/e"
- echo "100644 $o5 0 a"
- echo "100644 $o0 0 c"
+ echo "100644 blob $o5 a" &&
+ echo "100644 blob $o0 c" &&
+ echo "100644 blob $o0 d/e" &&
+ echo "100644 $o5 0 a" &&
+ echo "100644 $o0 0 c" &&
echo "100644 $o0 0 d/e"
) >expected &&
test_cmp expected actual
@@ -207,15 +207,15 @@ test_expect_success 'setup 6' '
rm -rf [abcd] &&
git checkout df-3 &&
- ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ ( git ls-tree -r HEAD && git ls-files -s ) >actual &&
(
- echo "100644 blob $o0 a"
- echo "100644 blob $o0 b"
- echo "100644 blob $o0 c"
- echo "100644 blob $o0 d/e"
- echo "100644 $o0 0 a"
- echo "100644 $o0 0 b"
- echo "100644 $o0 0 c"
+ echo "100644 blob $o0 a" &&
+ echo "100644 blob $o0 b" &&
+ echo "100644 blob $o0 c" &&
+ echo "100644 blob $o0 d/e" &&
+ echo "100644 $o0 0 a" &&
+ echo "100644 $o0 0 b" &&
+ echo "100644 $o0 0 c" &&
echo "100644 $o0 0 d/e"
) >expected &&
test_cmp expected actual &&
@@ -226,15 +226,15 @@ test_expect_success 'setup 6' '
test_tick &&
git commit -m "df-3 makes d" &&
c6=$(git rev-parse --verify HEAD) &&
- ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ ( git ls-tree -r HEAD && git ls-files -s ) >actual &&
(
- echo "100644 blob $o0 a"
- echo "100644 blob $o0 b"
- echo "100644 blob $o0 c"
- echo "100644 blob $o6 d"
- echo "100644 $o0 0 a"
- echo "100644 $o0 0 b"
- echo "100644 $o0 0 c"
+ echo "100644 blob $o0 a" &&
+ echo "100644 blob $o0 b" &&
+ echo "100644 blob $o0 c" &&
+ echo "100644 blob $o6 d" &&
+ echo "100644 $o0 0 a" &&
+ echo "100644 $o0 0 b" &&
+ echo "100644 $o0 0 c" &&
echo "100644 $o6 0 d"
) >expected &&
test_cmp expected actual
@@ -286,11 +286,11 @@ test_expect_success 'merge-recursive result' '
git ls-files -s >actual &&
(
- echo "100644 $o0 1 a"
- echo "100644 $o2 2 a"
- echo "100644 $o1 3 a"
- echo "100644 $o0 0 b"
- echo "100644 $o0 0 c"
+ echo "100644 $o0 1 a" &&
+ echo "100644 $o2 2 a" &&
+ echo "100644 $o1 3 a" &&
+ echo "100644 $o0 0 b" &&
+ echo "100644 $o0 0 c" &&
echo "100644 $o1 0 d/e"
) >expected &&
test_cmp expected actual
@@ -325,10 +325,10 @@ test_expect_success 'merge-recursive remove conflict' '
git ls-files -s >actual &&
(
- echo "100644 $o0 1 a"
- echo "100644 $o1 2 a"
- echo "100644 $o5 3 a"
- echo "100644 $o0 0 c"
+ echo "100644 $o0 1 a" &&
+ echo "100644 $o1 2 a" &&
+ echo "100644 $o5 3 a" &&
+ echo "100644 $o0 0 c" &&
echo "100644 $o1 0 d/e"
) >expected &&
test_cmp expected actual
@@ -347,9 +347,9 @@ test_expect_success 'merge-recursive result' '
git ls-files -s >actual &&
(
- echo "100644 $o1 0 a"
- echo "100644 $o3 0 b/c"
- echo "100644 $o0 0 c"
+ echo "100644 $o1 0 a" &&
+ echo "100644 $o3 0 b/c" &&
+ echo "100644 $o0 0 c" &&
echo "100644 $o1 0 d/e"
) >expected &&
test_cmp expected actual
@@ -369,11 +369,11 @@ test_expect_success 'merge-recursive d/f conflict result' '
git ls-files -s >actual &&
(
- echo "100644 $o0 1 a"
- echo "100644 $o1 2 a"
- echo "100644 $o4 0 a/c"
- echo "100644 $o0 0 b"
- echo "100644 $o0 0 c"
+ echo "100644 $o0 1 a" &&
+ echo "100644 $o1 2 a" &&
+ echo "100644 $o4 0 a/c" &&
+ echo "100644 $o0 0 b" &&
+ echo "100644 $o0 0 c" &&
echo "100644 $o1 0 d/e"
) >expected &&
test_cmp expected actual
@@ -393,11 +393,11 @@ test_expect_success 'merge-recursive d/f conflict result the other way' '
git ls-files -s >actual &&
(
- echo "100644 $o0 1 a"
- echo "100644 $o1 3 a"
- echo "100644 $o4 0 a/c"
- echo "100644 $o0 0 b"
- echo "100644 $o0 0 c"
+ echo "100644 $o0 1 a" &&
+ echo "100644 $o1 3 a" &&
+ echo "100644 $o4 0 a/c" &&
+ echo "100644 $o0 0 b" &&
+ echo "100644 $o0 0 c" &&
echo "100644 $o1 0 d/e"
) >expected &&
test_cmp expected actual
@@ -417,11 +417,11 @@ test_expect_success 'merge-recursive d/f conflict result' '
git ls-files -s >actual &&
(
- echo "100644 $o1 0 a"
- echo "100644 $o0 0 b"
- echo "100644 $o0 0 c"
- echo "100644 $o6 3 d"
- echo "100644 $o0 1 d/e"
+ echo "100644 $o1 0 a" &&
+ echo "100644 $o0 0 b" &&
+ echo "100644 $o0 0 c" &&
+ echo "100644 $o6 3 d" &&
+ echo "100644 $o0 1 d/e" &&
echo "100644 $o1 2 d/e"
) >expected &&
test_cmp expected actual
@@ -441,11 +441,11 @@ test_expect_success 'merge-recursive d/f conflict result' '
git ls-files -s >actual &&
(
- echo "100644 $o1 0 a"
- echo "100644 $o0 0 b"
- echo "100644 $o0 0 c"
- echo "100644 $o6 2 d"
- echo "100644 $o0 1 d/e"
+ echo "100644 $o1 0 a" &&
+ echo "100644 $o0 0 b" &&
+ echo "100644 $o0 0 c" &&
+ echo "100644 $o6 2 d" &&
+ echo "100644 $o0 1 d/e" &&
echo "100644 $o1 3 d/e"
) >expected &&
test_cmp expected actual
@@ -465,13 +465,13 @@ test_expect_success 'reset and bind merge' '
git read-tree --prefix=M/ master &&
git ls-files -s >actual &&
(
- echo "100644 $o1 0 M/a"
- echo "100644 $o0 0 M/b"
- echo "100644 $o0 0 M/c"
- echo "100644 $o1 0 M/d/e"
- echo "100644 $o1 0 a"
- echo "100644 $o0 0 b"
- echo "100644 $o0 0 c"
+ echo "100644 $o1 0 M/a" &&
+ echo "100644 $o0 0 M/b" &&
+ echo "100644 $o0 0 M/c" &&
+ echo "100644 $o1 0 M/d/e" &&
+ echo "100644 $o1 0 a" &&
+ echo "100644 $o0 0 b" &&
+ echo "100644 $o0 0 c" &&
echo "100644 $o1 0 d/e"
) >expected &&
test_cmp expected actual &&
@@ -479,17 +479,17 @@ test_expect_success 'reset and bind merge' '
git read-tree --prefix=a1/ master &&
git ls-files -s >actual &&
(
- echo "100644 $o1 0 M/a"
- echo "100644 $o0 0 M/b"
- echo "100644 $o0 0 M/c"
- echo "100644 $o1 0 M/d/e"
- echo "100644 $o1 0 a"
- echo "100644 $o1 0 a1/a"
- echo "100644 $o0 0 a1/b"
- echo "100644 $o0 0 a1/c"
- echo "100644 $o1 0 a1/d/e"
- echo "100644 $o0 0 b"
- echo "100644 $o0 0 c"
+ echo "100644 $o1 0 M/a" &&
+ echo "100644 $o0 0 M/b" &&
+ echo "100644 $o0 0 M/c" &&
+ echo "100644 $o1 0 M/d/e" &&
+ echo "100644 $o1 0 a" &&
+ echo "100644 $o1 0 a1/a" &&
+ echo "100644 $o0 0 a1/b" &&
+ echo "100644 $o0 0 a1/c" &&
+ echo "100644 $o1 0 a1/d/e" &&
+ echo "100644 $o0 0 b" &&
+ echo "100644 $o0 0 c" &&
echo "100644 $o1 0 d/e"
) >expected &&
test_cmp expected actual &&
@@ -497,21 +497,21 @@ test_expect_success 'reset and bind merge' '
git read-tree --prefix=z/ master &&
git ls-files -s >actual &&
(
- echo "100644 $o1 0 M/a"
- echo "100644 $o0 0 M/b"
- echo "100644 $o0 0 M/c"
- echo "100644 $o1 0 M/d/e"
- echo "100644 $o1 0 a"
- echo "100644 $o1 0 a1/a"
- echo "100644 $o0 0 a1/b"
- echo "100644 $o0 0 a1/c"
- echo "100644 $o1 0 a1/d/e"
- echo "100644 $o0 0 b"
- echo "100644 $o0 0 c"
- echo "100644 $o1 0 d/e"
- echo "100644 $o1 0 z/a"
- echo "100644 $o0 0 z/b"
- echo "100644 $o0 0 z/c"
+ echo "100644 $o1 0 M/a" &&
+ echo "100644 $o0 0 M/b" &&
+ echo "100644 $o0 0 M/c" &&
+ echo "100644 $o1 0 M/d/e" &&
+ echo "100644 $o1 0 a" &&
+ echo "100644 $o1 0 a1/a" &&
+ echo "100644 $o0 0 a1/b" &&
+ echo "100644 $o0 0 a1/c" &&
+ echo "100644 $o1 0 a1/d/e" &&
+ echo "100644 $o0 0 b" &&
+ echo "100644 $o0 0 c" &&
+ echo "100644 $o1 0 d/e" &&
+ echo "100644 $o1 0 z/a" &&
+ echo "100644 $o0 0 z/b" &&
+ echo "100644 $o0 0 z/c" &&
echo "100644 $o1 0 z/d/e"
) >expected &&
test_cmp expected actual
@@ -589,8 +589,8 @@ test_expect_success 'merge-recursive simple w/submodule result' '
git ls-files -s >actual &&
(
- echo "100644 $o5 0 a"
- echo "100644 $o0 0 c"
+ echo "100644 $o5 0 a" &&
+ echo "100644 $o0 0 c" &&
echo "160000 $c1 0 d"
) >expected &&
test_cmp expected actual
@@ -601,13 +601,13 @@ test_expect_success 'merge-recursive copy vs. rename' '
git merge rename &&
( git ls-tree -r HEAD && git ls-files -s ) >actual &&
(
- echo "100644 blob $o0 b"
- echo "100644 blob $o0 c"
- echo "100644 blob $o0 d/e"
- echo "100644 blob $o0 e"
- echo "100644 $o0 0 b"
- echo "100644 $o0 0 c"
- echo "100644 $o0 0 d/e"
+ echo "100644 blob $o0 b" &&
+ echo "100644 blob $o0 c" &&
+ echo "100644 blob $o0 d/e" &&
+ echo "100644 blob $o0 e" &&
+ echo "100644 $o0 0 b" &&
+ echo "100644 $o0 0 c" &&
+ echo "100644 $o0 0 d/e" &&
echo "100644 $o0 0 e"
) >expected &&
test_cmp expected actual
@@ -617,17 +617,17 @@ test_expect_failure 'merge-recursive rename vs. rename/symlink' '
git checkout -f rename &&
git merge rename-ln &&
- ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ ( git ls-tree -r HEAD && git ls-files -s ) >actual &&
(
- echo "120000 blob $oln a"
- echo "100644 blob $o0 b"
- echo "100644 blob $o0 c"
- echo "100644 blob $o0 d/e"
- echo "100644 blob $o0 e"
- echo "120000 $oln 0 a"
- echo "100644 $o0 0 b"
- echo "100644 $o0 0 c"
- echo "100644 $o0 0 d/e"
+ echo "120000 blob $oln a" &&
+ echo "100644 blob $o0 b" &&
+ echo "100644 blob $o0 c" &&
+ echo "100644 blob $o0 d/e" &&
+ echo "100644 blob $o0 e" &&
+ echo "120000 $oln 0 a" &&
+ echo "100644 $o0 0 b" &&
+ echo "100644 $o0 0 c" &&
+ echo "100644 $o0 0 d/e" &&
echo "100644 $o0 0 e"
) >expected &&
test_cmp expected actual
diff --git a/t/t3031-merge-criscross.sh b/t/t3031-merge-criscross.sh
index e59b0a32d6..3824756a02 100755
--- a/t/t3031-merge-criscross.sh
+++ b/t/t3031-merge-criscross.sh
@@ -88,7 +88,7 @@ test_expect_success 'setup repo with criss-cross history' '
git branch G
'
-test_expect_success 'recursive merge between F and G, causes segfault' '
+test_expect_success 'recursive merge between F and G does not cause segfault' '
git merge F
'
diff --git a/t/t3035-merge-sparse.sh b/t/t3035-merge-sparse.sh
index 0c0b433bd3..c4b4a94324 100755
--- a/t/t3035-merge-sparse.sh
+++ b/t/t3035-merge-sparse.sh
@@ -17,7 +17,6 @@ test_commit_this () {
}
test_expect_success 'setup' '
- : >empty &&
test_file checked-out init &&
test_file modify_delete modify_delete_init &&
test_commit_this init &&
@@ -38,7 +37,7 @@ test_expect_success 'reset --hard works after the conflict' '
test_expect_success 'is reset properly' '
git status --porcelain -- modify_delete >out &&
- test_cmp empty out &&
+ test_must_be_empty out &&
test_path_is_missing modify_delete
'
@@ -52,7 +51,7 @@ test_expect_success 'Merge abort works after the conflict' '
test_expect_success 'is aborted properly' '
git status --porcelain -- modify_delete >out &&
- test_cmp empty out &&
+ test_must_be_empty out &&
test_path_is_missing modify_delete
'
diff --git a/t/t3050-subprojects-fetch.sh b/t/t3050-subprojects-fetch.sh
index 2f5f41a012..f1f09abdd9 100755
--- a/t/t3050-subprojects-fetch.sh
+++ b/t/t3050-subprojects-fetch.sh
@@ -21,10 +21,10 @@ test_expect_success setup '
test_expect_success clone '
git clone "file://$(pwd)/.git" cloned &&
- (git rev-parse HEAD; git ls-files -s) >expected &&
+ (git rev-parse HEAD && git ls-files -s) >expected &&
(
cd cloned &&
- (git rev-parse HEAD; git ls-files -s) >../actual
+ (git rev-parse HEAD && git ls-files -s) >../actual
) &&
test_cmp expected actual
'
@@ -40,11 +40,11 @@ test_expect_success advance '
'
test_expect_success fetch '
- (git rev-parse HEAD; git ls-files -s) >expected &&
+ (git rev-parse HEAD && git ls-files -s) >expected &&
(
cd cloned &&
git pull &&
- (git rev-parse HEAD; git ls-files -s) >../actual
+ (git rev-parse HEAD && git ls-files -s) >../actual
) &&
test_cmp expected actual
'
diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh
index dce102130f..46aca0af10 100755
--- a/t/t3070-wildmatch.sh
+++ b/t/t3070-wildmatch.sh
@@ -101,8 +101,7 @@ match_with_ls_files() {
match_stdout_stderr_cmp="
tr -d '\0' <actual.raw >actual &&
- >expect.err &&
- test_cmp expect.err actual.err &&
+ test_must_be_empty actual.err &&
test_cmp expect actual"
if test "$match_expect" = 'E'
diff --git a/t/t3102-ls-tree-wildcards.sh b/t/t3102-ls-tree-wildcards.sh
index e804377f1c..1e16c6b8ea 100755
--- a/t/t3102-ls-tree-wildcards.sh
+++ b/t/t3102-ls-tree-wildcards.sh
@@ -23,7 +23,7 @@ test_expect_success 'ls-tree outside prefix' '
cat >expect <<-EOF &&
100644 blob $EMPTY_BLOB ../a[a]/three
EOF
- ( cd aa && git ls-tree -r HEAD "../a[a]"; ) >actual &&
+ ( cd aa && git ls-tree -r HEAD "../a[a]" ) >actual &&
test_cmp expect actual
'
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index dbca665da4..93f21ab078 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -1305,4 +1305,50 @@ test_expect_success 'tracking with unexpected .fetch refspec' '
)
'
+test_expect_success 'configured committerdate sort' '
+ git init sort &&
+ (
+ cd sort &&
+ git config branch.sort committerdate &&
+ test_commit initial &&
+ git checkout -b a &&
+ test_commit a &&
+ git checkout -b c &&
+ test_commit c &&
+ git checkout -b b &&
+ test_commit b &&
+ git branch >actual &&
+ cat >expect <<-\EOF &&
+ master
+ a
+ c
+ * b
+ EOF
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'option override configured sort' '
+ (
+ cd sort &&
+ git config branch.sort committerdate &&
+ git branch --sort=refname >actual &&
+ cat >expect <<-\EOF &&
+ a
+ * b
+ c
+ master
+ EOF
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'invalid sort parameter in configuration' '
+ (
+ cd sort &&
+ git config branch.sort "v:notvalid" &&
+ test_must_fail git branch
+ )
+'
+
test_done
diff --git a/t/t3201-branch-contains.sh b/t/t3201-branch-contains.sh
index 0ef1b6fdcc..0ea4fc4694 100755
--- a/t/t3201-branch-contains.sh
+++ b/t/t3201-branch-contains.sh
@@ -48,16 +48,14 @@ test_expect_success 'branch --contains master' '
test_expect_success 'branch --no-contains=master' '
git branch --no-contains=master >actual &&
- >expect &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'branch --no-contains master' '
git branch --no-contains master >actual &&
- >expect &&
- test_cmp expect actual
+ test_must_be_empty actual
'
@@ -94,8 +92,7 @@ test_expect_success 'branch --contains with pattern implies --list' '
test_expect_success 'branch --no-contains with pattern implies --list' '
git branch --no-contains=master master >actual &&
- >expect &&
- test_cmp expect actual
+ test_must_be_empty actual
'
@@ -123,8 +120,7 @@ test_expect_success 'branch --merged with pattern implies --list' '
test_expect_success 'side: branch --no-merged' '
git branch --no-merged >actual &&
- >expect &&
- test_cmp expect actual
+ test_must_be_empty actual
'
@@ -152,8 +148,7 @@ test_expect_success 'master: branch --no-merged' '
test_expect_success 'branch --no-merged with pattern implies --list' '
git branch --no-merged=master master >actual &&
- >expect &&
- test_cmp expect actual
+ test_must_be_empty actual
'
diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh
new file mode 100755
index 0000000000..88ebed1dfa
--- /dev/null
+++ b/t/t3206-range-diff.sh
@@ -0,0 +1,196 @@
+#!/bin/sh
+
+test_description='range-diff tests'
+
+. ./test-lib.sh
+
+# Note that because of the range-diff's heuristics, test_commit does more
+# harm than good. We need some real history.
+
+test_expect_success 'setup' '
+ git fast-import < "$TEST_DIRECTORY"/t3206/history.export
+'
+
+test_expect_success 'simple A..B A..C (unmodified)' '
+ git range-diff --no-color master..topic master..unmodified \
+ >actual &&
+ cat >expected <<-EOF &&
+ 1: 4de457d = 1: 35b9b25 s/5/A/
+ 2: fccce22 = 2: de345ab s/4/A/
+ 3: 147e64e = 3: 9af6654 s/11/B/
+ 4: a63e992 = 4: 2901f77 s/12/B/
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'simple B...C (unmodified)' '
+ git range-diff --no-color topic...unmodified >actual &&
+ # same "expected" as above
+ test_cmp expected actual
+'
+
+test_expect_success 'simple A B C (unmodified)' '
+ git range-diff --no-color master topic unmodified >actual &&
+ # same "expected" as above
+ test_cmp expected actual
+'
+
+test_expect_success 'trivial reordering' '
+ git range-diff --no-color master topic reordered >actual &&
+ cat >expected <<-EOF &&
+ 1: 4de457d = 1: aca177a s/5/A/
+ 3: 147e64e = 2: 14ad629 s/11/B/
+ 4: a63e992 = 3: ee58208 s/12/B/
+ 2: fccce22 = 4: 307b27a s/4/A/
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'removed a commit' '
+ git range-diff --no-color master topic removed >actual &&
+ cat >expected <<-EOF &&
+ 1: 4de457d = 1: 7657159 s/5/A/
+ 2: fccce22 < -: ------- s/4/A/
+ 3: 147e64e = 2: 43d84d3 s/11/B/
+ 4: a63e992 = 3: a740396 s/12/B/
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'added a commit' '
+ git range-diff --no-color master topic added >actual &&
+ cat >expected <<-EOF &&
+ 1: 4de457d = 1: 2716022 s/5/A/
+ 2: fccce22 = 2: b62accd s/4/A/
+ -: ------- > 3: df46cfa s/6/A/
+ 3: 147e64e = 4: 3e64548 s/11/B/
+ 4: a63e992 = 5: 12b4063 s/12/B/
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'new base, A B C' '
+ git range-diff --no-color master topic rebased >actual &&
+ cat >expected <<-EOF &&
+ 1: 4de457d = 1: cc9c443 s/5/A/
+ 2: fccce22 = 2: c5d9641 s/4/A/
+ 3: 147e64e = 3: 28cc2b6 s/11/B/
+ 4: a63e992 = 4: 5628ab7 s/12/B/
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'new base, B...C' '
+ # this syntax includes the commits from master!
+ git range-diff --no-color topic...rebased >actual &&
+ cat >expected <<-EOF &&
+ -: ------- > 1: a31b12e unrelated
+ 1: 4de457d = 2: cc9c443 s/5/A/
+ 2: fccce22 = 3: c5d9641 s/4/A/
+ 3: 147e64e = 4: 28cc2b6 s/11/B/
+ 4: a63e992 = 5: 5628ab7 s/12/B/
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'changed commit' '
+ git range-diff --no-color topic...changed >actual &&
+ cat >expected <<-EOF &&
+ 1: 4de457d = 1: a4b3333 s/5/A/
+ 2: fccce22 = 2: f51d370 s/4/A/
+ 3: 147e64e ! 3: 0559556 s/11/B/
+ @@ -10,7 +10,7 @@
+ 9
+ 10
+ -11
+ -+B
+ ++BB
+ 12
+ 13
+ 14
+ 4: a63e992 ! 4: d966c5c s/12/B/
+ @@ -8,7 +8,7 @@
+ @@
+ 9
+ 10
+ - B
+ + BB
+ -12
+ +B
+ 13
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'changed message' '
+ git range-diff --no-color topic...changed-message >actual &&
+ sed s/Z/\ /g >expected <<-EOF &&
+ 1: 4de457d = 1: f686024 s/5/A/
+ 2: fccce22 ! 2: 4ab067d s/4/A/
+ @@ -2,6 +2,8 @@
+ Z
+ Z s/4/A/
+ Z
+ + Also a silly comment here!
+ +
+ Z diff --git a/file b/file
+ Z --- a/file
+ Z +++ b/file
+ 3: 147e64e = 3: b9cb956 s/11/B/
+ 4: a63e992 = 4: 8add5f1 s/12/B/
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'dual-coloring' '
+ sed -e "s|^:||" >expect <<-\EOF &&
+ :<YELLOW>1: a4b3333 = 1: f686024 s/5/A/<RESET>
+ :<RED>2: f51d370 <RESET><YELLOW>!<RESET><GREEN> 2: 4ab067d<RESET><YELLOW> s/4/A/<RESET>
+ : <REVERSE><CYAN>@@ -2,6 +2,8 @@<RESET>
+ : <RESET>
+ : s/4/A/<RESET>
+ : <RESET>
+ : <REVERSE><GREEN>+<RESET><BOLD> Also a silly comment here!<RESET>
+ : <REVERSE><GREEN>+<RESET>
+ : diff --git a/file b/file<RESET>
+ : --- a/file<RESET>
+ : +++ b/file<RESET>
+ :<RED>3: 0559556 <RESET><YELLOW>!<RESET><GREEN> 3: b9cb956<RESET><YELLOW> s/11/B/<RESET>
+ : <REVERSE><CYAN>@@ -10,7 +10,7 @@<RESET>
+ : 9<RESET>
+ : 10<RESET>
+ : <RED> -11<RESET>
+ : <REVERSE><RED>-<RESET><FAINT;GREEN>+BB<RESET>
+ : <REVERSE><GREEN>+<RESET><BOLD;GREEN>+B<RESET>
+ : 12<RESET>
+ : 13<RESET>
+ : 14<RESET>
+ :<RED>4: d966c5c <RESET><YELLOW>!<RESET><GREEN> 4: 8add5f1<RESET><YELLOW> s/12/B/<RESET>
+ : <REVERSE><CYAN>@@ -8,7 +8,7 @@<RESET>
+ : <CYAN> @@<RESET>
+ : 9<RESET>
+ : 10<RESET>
+ : <REVERSE><RED>-<RESET><FAINT> BB<RESET>
+ : <REVERSE><GREEN>+<RESET><BOLD> B<RESET>
+ : <RED> -12<RESET>
+ : <GREEN> +B<RESET>
+ : 13<RESET>
+ EOF
+ git range-diff changed...changed-message --color --dual-color >actual.raw &&
+ test_decode_color >actual <actual.raw &&
+ test_cmp expect actual
+'
+
+for prev in topic master..topic
+do
+ test_expect_success "format-patch --range-diff=$prev" '
+ git format-patch --stdout --cover-letter --range-diff=$prev \
+ master..unmodified >actual &&
+ grep "= 1: .* s/5/A" actual &&
+ grep "= 2: .* s/4/A" actual &&
+ grep "= 3: .* s/11/B" actual &&
+ grep "= 4: .* s/12/B" actual
+ '
+done
+
+test_done
diff --git a/t/t3206/history.export b/t/t3206/history.export
new file mode 100644
index 0000000000..b8ffff0940
--- /dev/null
+++ b/t/t3206/history.export
@@ -0,0 +1,604 @@
+blob
+mark :1
+data 51
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
+reset refs/heads/removed
+commit refs/heads/removed
+mark :2
+author Thomas Rast <trast@inf.ethz.ch> 1374424921 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374484724 +0200
+data 8
+initial
+M 100644 :1 file
+
+blob
+mark :3
+data 51
+1
+2
+3
+4
+A
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
+commit refs/heads/topic
+mark :4
+author Thomas Rast <trast@inf.ethz.ch> 1374485014 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485014 +0200
+data 7
+s/5/A/
+from :2
+M 100644 :3 file
+
+blob
+mark :5
+data 51
+1
+2
+3
+A
+A
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
+commit refs/heads/topic
+mark :6
+author Thomas Rast <trast@inf.ethz.ch> 1374485024 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485024 +0200
+data 7
+s/4/A/
+from :4
+M 100644 :5 file
+
+blob
+mark :7
+data 50
+1
+2
+3
+A
+A
+6
+7
+8
+9
+10
+B
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
+commit refs/heads/topic
+mark :8
+author Thomas Rast <trast@inf.ethz.ch> 1374485036 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485036 +0200
+data 8
+s/11/B/
+from :6
+M 100644 :7 file
+
+blob
+mark :9
+data 49
+1
+2
+3
+A
+A
+6
+7
+8
+9
+10
+B
+B
+13
+14
+15
+16
+17
+18
+19
+20
+
+commit refs/heads/topic
+mark :10
+author Thomas Rast <trast@inf.ethz.ch> 1374485044 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485044 +0200
+data 8
+s/12/B/
+from :8
+M 100644 :9 file
+
+blob
+mark :11
+data 10
+unrelated
+
+commit refs/heads/master
+mark :12
+author Thomas Rast <trast@inf.ethz.ch> 1374485127 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485127 +0200
+data 10
+unrelated
+from :2
+M 100644 :11 otherfile
+
+commit refs/heads/rebased
+mark :13
+author Thomas Rast <trast@inf.ethz.ch> 1374485014 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485137 +0200
+data 7
+s/5/A/
+from :12
+M 100644 :3 file
+
+commit refs/heads/rebased
+mark :14
+author Thomas Rast <trast@inf.ethz.ch> 1374485024 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485138 +0200
+data 7
+s/4/A/
+from :13
+M 100644 :5 file
+
+commit refs/heads/rebased
+mark :15
+author Thomas Rast <trast@inf.ethz.ch> 1374485036 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485138 +0200
+data 8
+s/11/B/
+from :14
+M 100644 :7 file
+
+commit refs/heads/rebased
+mark :16
+author Thomas Rast <trast@inf.ethz.ch> 1374485044 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485138 +0200
+data 8
+s/12/B/
+from :15
+M 100644 :9 file
+
+commit refs/heads/added
+mark :17
+author Thomas Rast <trast@inf.ethz.ch> 1374485014 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485341 +0200
+data 7
+s/5/A/
+from :2
+M 100644 :3 file
+
+commit refs/heads/added
+mark :18
+author Thomas Rast <trast@inf.ethz.ch> 1374485024 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485341 +0200
+data 7
+s/4/A/
+from :17
+M 100644 :5 file
+
+blob
+mark :19
+data 51
+1
+2
+3
+A
+A
+A
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
+commit refs/heads/added
+mark :20
+author Thomas Rast <trast@inf.ethz.ch> 1374485186 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485341 +0200
+data 7
+s/6/A/
+from :18
+M 100644 :19 file
+
+blob
+mark :21
+data 50
+1
+2
+3
+A
+A
+A
+7
+8
+9
+10
+B
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
+commit refs/heads/added
+mark :22
+author Thomas Rast <trast@inf.ethz.ch> 1374485036 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485341 +0200
+data 8
+s/11/B/
+from :20
+M 100644 :21 file
+
+blob
+mark :23
+data 49
+1
+2
+3
+A
+A
+A
+7
+8
+9
+10
+B
+B
+13
+14
+15
+16
+17
+18
+19
+20
+
+commit refs/heads/added
+mark :24
+author Thomas Rast <trast@inf.ethz.ch> 1374485044 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485341 +0200
+data 8
+s/12/B/
+from :22
+M 100644 :23 file
+
+commit refs/heads/reordered
+mark :25
+author Thomas Rast <trast@inf.ethz.ch> 1374485014 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485350 +0200
+data 7
+s/5/A/
+from :2
+M 100644 :3 file
+
+blob
+mark :26
+data 50
+1
+2
+3
+4
+A
+6
+7
+8
+9
+10
+B
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
+commit refs/heads/reordered
+mark :27
+author Thomas Rast <trast@inf.ethz.ch> 1374485036 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485350 +0200
+data 8
+s/11/B/
+from :25
+M 100644 :26 file
+
+blob
+mark :28
+data 49
+1
+2
+3
+4
+A
+6
+7
+8
+9
+10
+B
+B
+13
+14
+15
+16
+17
+18
+19
+20
+
+commit refs/heads/reordered
+mark :29
+author Thomas Rast <trast@inf.ethz.ch> 1374485044 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485350 +0200
+data 8
+s/12/B/
+from :27
+M 100644 :28 file
+
+commit refs/heads/reordered
+mark :30
+author Thomas Rast <trast@inf.ethz.ch> 1374485024 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485350 +0200
+data 7
+s/4/A/
+from :29
+M 100644 :9 file
+
+commit refs/heads/changed
+mark :31
+author Thomas Rast <trast@inf.ethz.ch> 1374485014 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485507 +0200
+data 7
+s/5/A/
+from :2
+M 100644 :3 file
+
+commit refs/heads/changed
+mark :32
+author Thomas Rast <trast@inf.ethz.ch> 1374485024 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485507 +0200
+data 7
+s/4/A/
+from :31
+M 100644 :5 file
+
+blob
+mark :33
+data 51
+1
+2
+3
+A
+A
+6
+7
+8
+9
+10
+BB
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
+commit refs/heads/changed
+mark :34
+author Thomas Rast <trast@inf.ethz.ch> 1374485036 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485507 +0200
+data 8
+s/11/B/
+from :32
+M 100644 :33 file
+
+blob
+mark :35
+data 50
+1
+2
+3
+A
+A
+6
+7
+8
+9
+10
+BB
+B
+13
+14
+15
+16
+17
+18
+19
+20
+
+commit refs/heads/changed
+mark :36
+author Thomas Rast <trast@inf.ethz.ch> 1374485044 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485507 +0200
+data 8
+s/12/B/
+from :34
+M 100644 :35 file
+
+commit refs/heads/changed-message
+mark :37
+author Thomas Rast <trast@inf.ethz.ch> 1374485014 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485530 +0200
+data 7
+s/5/A/
+from :2
+M 100644 :3 file
+
+commit refs/heads/changed-message
+mark :38
+author Thomas Rast <trast@inf.ethz.ch> 1374485024 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485530 +0200
+data 35
+s/4/A/
+
+Also a silly comment here!
+from :37
+M 100644 :5 file
+
+commit refs/heads/changed-message
+mark :39
+author Thomas Rast <trast@inf.ethz.ch> 1374485036 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485536 +0200
+data 8
+s/11/B/
+from :38
+M 100644 :7 file
+
+commit refs/heads/changed-message
+mark :40
+author Thomas Rast <trast@inf.ethz.ch> 1374485044 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485536 +0200
+data 8
+s/12/B/
+from :39
+M 100644 :9 file
+
+commit refs/heads/unmodified
+mark :41
+author Thomas Rast <trast@inf.ethz.ch> 1374485014 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485631 +0200
+data 7
+s/5/A/
+from :2
+M 100644 :3 file
+
+commit refs/heads/unmodified
+mark :42
+author Thomas Rast <trast@inf.ethz.ch> 1374485024 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485631 +0200
+data 7
+s/4/A/
+from :41
+M 100644 :5 file
+
+commit refs/heads/unmodified
+mark :43
+author Thomas Rast <trast@inf.ethz.ch> 1374485036 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485632 +0200
+data 8
+s/11/B/
+from :42
+M 100644 :7 file
+
+commit refs/heads/unmodified
+mark :44
+author Thomas Rast <trast@inf.ethz.ch> 1374485044 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374485632 +0200
+data 8
+s/12/B/
+from :43
+M 100644 :9 file
+
+commit refs/heads/removed
+mark :45
+author Thomas Rast <trast@inf.ethz.ch> 1374485014 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374486061 +0200
+data 7
+s/5/A/
+from :2
+M 100644 :3 file
+
+commit refs/heads/removed
+mark :46
+author Thomas Rast <trast@inf.ethz.ch> 1374485036 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374486061 +0200
+data 8
+s/11/B/
+from :45
+M 100644 :26 file
+
+commit refs/heads/removed
+mark :47
+author Thomas Rast <trast@inf.ethz.ch> 1374485044 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1374486061 +0200
+data 8
+s/12/B/
+from :46
+M 100644 :28 file
+
+reset refs/heads/removed
+from :47
+
diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh
index afa27ffe2d..9ea5fa4fd2 100755
--- a/t/t3210-pack-refs.sh
+++ b/t/t3210-pack-refs.sh
@@ -127,7 +127,7 @@ test_expect_success 'explicit pack-refs with dangling packed reference' '
git reflog expire --expire=all --all &&
git prune --expire=all &&
git pack-refs --all 2>result &&
- test_cmp /dev/null result
+ test_must_be_empty result
'
test_expect_success 'delete ref with dangling packed version' '
@@ -139,7 +139,7 @@ test_expect_success 'delete ref with dangling packed version' '
git reflog expire --expire=all --all &&
git prune --expire=all &&
git branch -d lamb 2>result &&
- test_cmp /dev/null result
+ test_must_be_empty result
'
test_expect_success 'delete ref while another dangling packed ref' '
@@ -150,7 +150,7 @@ test_expect_success 'delete ref while another dangling packed ref' '
git reflog expire --expire=all --all &&
git prune --expire=all &&
git branch -d lamb 2>result &&
- test_cmp /dev/null result
+ test_must_be_empty result
'
test_expect_success 'pack ref directly below refs/' '
@@ -186,7 +186,7 @@ test_expect_success 'notice d/f conflict with existing directory' '
test_expect_success 'existing directory reports concrete ref' '
test_must_fail git branch foo 2>stderr &&
- grep refs/heads/foo/bar/baz stderr
+ test_i18ngrep refs/heads/foo/bar/baz stderr
'
test_expect_success 'notice d/f conflict with existing ref' '
@@ -231,9 +231,9 @@ test_expect_success 'timeout if packed-refs.lock exists' '
test_expect_success 'retry acquiring packed-refs.lock' '
LOCK=.git/packed-refs.lock &&
>"$LOCK" &&
- test_when_finished "wait; rm -f $LOCK" &&
+ test_when_finished "wait && rm -f $LOCK" &&
{
- ( sleep 1 ; rm -f $LOCK ) &
+ ( sleep 1 && rm -f $LOCK ) &
} &&
git -c core.packedrefstimeout=3000 pack-refs --all --prune
'
diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh
index 2d200fdf36..84bbf88cf9 100755
--- a/t/t3301-notes.sh
+++ b/t/t3301-notes.sh
@@ -481,10 +481,8 @@ test_expect_success 'list specific note with "git notes list <object>"' '
'
test_expect_success 'listing non-existing notes fails' '
- cat >expect <<-EOF &&
- EOF
test_must_fail git notes list HEAD >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'append to existing note with "git notes append"' '
@@ -914,7 +912,7 @@ test_expect_success 'git notes copy --stdin' '
${indent}
${indent}yet another note
EOF
- (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \
+ (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^) &&
echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
git notes copy --stdin &&
git log -2 >actual &&
@@ -939,7 +937,7 @@ test_expect_success 'git notes copy --for-rewrite (unconfigured)' '
EOF
test_commit 14th &&
test_commit 15th &&
- (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \
+ (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^) &&
echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
git notes copy --for-rewrite=foo &&
git log -2 >actual &&
@@ -972,7 +970,7 @@ test_expect_success 'git notes copy --for-rewrite (enabled)' '
EOF
test_config notes.rewriteMode overwrite &&
test_config notes.rewriteRef "refs/notes/*" &&
- (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \
+ (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^) &&
echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
git notes copy --for-rewrite=foo &&
git log -2 >actual &&
@@ -1059,7 +1057,7 @@ test_expect_success 'git notes copy --for-rewrite (append two to one)' '
git notes add -f -m"append 2" HEAD^^ &&
test_config notes.rewriteMode concatenate &&
test_config notes.rewriteRef "refs/notes/*" &&
- (echo $(git rev-parse HEAD^) $(git rev-parse HEAD);
+ (echo $(git rev-parse HEAD^) $(git rev-parse HEAD) &&
echo $(git rev-parse HEAD^^) $(git rev-parse HEAD)) |
git notes copy --for-rewrite=foo &&
git log -1 >actual &&
diff --git a/t/t3308-notes-merge.sh b/t/t3308-notes-merge.sh
index ab946a5153..d60588ec8f 100755
--- a/t/t3308-notes-merge.sh
+++ b/t/t3308-notes-merge.sh
@@ -183,7 +183,7 @@ test_expect_success 'merge empty notes ref (z => y)' '
git notes add -m "foo" &&
git notes remove &&
git notes >output_notes_z &&
- test_cmp /dev/null output_notes_z &&
+ test_must_be_empty output_notes_z &&
# Do the merge (z => y)
git config core.notesRef refs/notes/y &&
git notes merge z &&
diff --git a/t/t3310-notes-merge-manual-resolve.sh b/t/t3310-notes-merge-manual-resolve.sh
index 9c1bf6eb3d..2dea846e25 100755
--- a/t/t3310-notes-merge-manual-resolve.sh
+++ b/t/t3310-notes-merge-manual-resolve.sh
@@ -337,7 +337,7 @@ EOF
git notes merge --commit &&
# No .git/NOTES_MERGE_* files left
test_might_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
- test_cmp /dev/null output &&
+ test_must_be_empty output &&
# Merge commit has pre-merge y and pre-merge z as parents
test "$(git rev-parse refs/notes/m^1)" = "$(cat pre_merge_y)" &&
test "$(git rev-parse refs/notes/m^2)" = "$(cat pre_merge_z)" &&
@@ -399,7 +399,7 @@ test_expect_success 'abort notes merge' '
git notes merge --abort &&
# No .git/NOTES_MERGE_* files left
test_might_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
- test_cmp /dev/null output &&
+ test_must_be_empty output &&
# m has not moved (still == y)
test "$(git rev-parse refs/notes/m)" = "$(cat pre_merge_y)" &&
# Verify that other notes refs has not changed (w, x, y and z)
@@ -466,7 +466,7 @@ EOF
git notes merge --commit &&
# No .git/NOTES_MERGE_* files left
test_might_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
- test_cmp /dev/null output &&
+ test_must_be_empty output &&
# Merge commit has pre-merge y and pre-merge z as parents
test "$(git rev-parse refs/notes/m^1)" = "$(cat pre_merge_y)" &&
test "$(git rev-parse refs/notes/m^2)" = "$(cat pre_merge_z)" &&
@@ -541,9 +541,9 @@ EOF
test "$(git rev-parse refs/notes/y)" = "$(git rev-parse NOTES_MERGE_PARTIAL^1)" &&
test "$(git rev-parse refs/notes/m)" != "$(git rev-parse NOTES_MERGE_PARTIAL^1)" &&
# Mention refs/notes/m, and its current and expected value in output
- grep -q "refs/notes/m" output &&
- grep -q "$(git rev-parse refs/notes/m)" output &&
- grep -q "$(git rev-parse NOTES_MERGE_PARTIAL^1)" output &&
+ test_i18ngrep -q "refs/notes/m" output &&
+ test_i18ngrep -q "$(git rev-parse refs/notes/m)" output &&
+ test_i18ngrep -q "$(git rev-parse NOTES_MERGE_PARTIAL^1)" output &&
# Verify that other notes refs has not changed (w, x, y and z)
verify_notes w &&
verify_notes x &&
@@ -555,7 +555,7 @@ test_expect_success 'resolve situation by aborting the notes merge' '
git notes merge --abort &&
# No .git/NOTES_MERGE_* files left
test_might_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
- test_cmp /dev/null output &&
+ test_must_be_empty output &&
# m has not moved (still == w)
test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/w)" &&
# Verify that other notes refs has not changed (w, x, y and z)
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 72d9564747..3996ee0135 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -200,10 +200,10 @@ test_expect_success 'rebase -q is quiet' '
test_expect_success 'Rebase a commit that sprinkles CRs in' '
(
- echo "One"
- echo "TwoQ"
- echo "Three"
- echo "FQur"
+ echo "One" &&
+ echo "TwoQ" &&
+ echo "Three" &&
+ echo "FQur" &&
echo "Five"
) | q_to_cr >CR &&
git add CR &&
diff --git a/t/t3401-rebase-and-am-rename.sh b/t/t3401-rebase-and-am-rename.sh
index 8f832957fc..e0b5111993 100755
--- a/t/t3401-rebase-and-am-rename.sh
+++ b/t/t3401-rebase-and-am-rename.sh
@@ -5,7 +5,7 @@ test_description='git rebase + directory rename tests'
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-rebase.sh
-test_expect_success 'setup testcase' '
+test_expect_success 'setup testcase where directory rename should be detected' '
test_create_repo dir-rename &&
(
cd dir-rename &&
@@ -102,4 +102,112 @@ test_expect_failure 'am: directory rename detected' '
)
'
+test_expect_success 'setup testcase where directory rename should NOT be detected' '
+ test_create_repo no-dir-rename &&
+ (
+ cd no-dir-rename &&
+
+ mkdir x &&
+ test_seq 1 10 >x/a &&
+ test_seq 11 20 >x/b &&
+ test_seq 21 30 >x/c &&
+ echo original >project_info &&
+ git add x project_info &&
+ git commit -m "Initial" &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git checkout A &&
+ echo v2 >project_info &&
+ git add project_info &&
+ git commit -m "Modify project_info" &&
+
+ git checkout B &&
+ mkdir y &&
+ git mv x/c y/c &&
+ echo v1 >project_info &&
+ git add project_info &&
+ git commit -m "Rename x/c to y/c, modify project_info"
+ )
+'
+
+test_expect_success 'rebase --interactive: NO directory rename' '
+ test_when_finished "git -C no-dir-rename rebase --abort" &&
+ (
+ cd no-dir-rename &&
+
+ git checkout B^0 &&
+
+ set_fake_editor &&
+ test_must_fail env FAKE_LINES="1" git rebase --interactive A &&
+
+ git ls-files -s >out &&
+ test_line_count = 6 out &&
+
+ test_path_is_file x/a &&
+ test_path_is_file x/b &&
+ test_path_is_missing x/c
+ )
+'
+
+test_expect_success 'rebase (am): NO directory rename' '
+ test_when_finished "git -C no-dir-rename rebase --abort" &&
+ (
+ cd no-dir-rename &&
+
+ git checkout B^0 &&
+
+ set_fake_editor &&
+ test_must_fail git rebase A &&
+
+ git ls-files -s >out &&
+ test_line_count = 6 out &&
+
+ test_path_is_file x/a &&
+ test_path_is_file x/b &&
+ test_path_is_missing x/c
+ )
+'
+
+test_expect_success 'rebase --merge: NO directory rename' '
+ test_when_finished "git -C no-dir-rename rebase --abort" &&
+ (
+ cd no-dir-rename &&
+
+ git checkout B^0 &&
+
+ set_fake_editor &&
+ test_must_fail git rebase --merge A &&
+
+ git ls-files -s >out &&
+ test_line_count = 6 out &&
+
+ test_path_is_file x/a &&
+ test_path_is_file x/b &&
+ test_path_is_missing x/c
+ )
+'
+
+test_expect_success 'am: NO directory rename' '
+ test_when_finished "git -C no-dir-rename am --abort" &&
+ (
+ cd no-dir-rename &&
+
+ git checkout A^0 &&
+
+ git format-patch -1 B &&
+
+ test_must_fail git am --3way 0001*.patch &&
+
+ git ls-files -s >out &&
+ test_line_count = 6 out &&
+
+ test_path_is_file x/a &&
+ test_path_is_file x/b &&
+ test_path_is_missing x/c
+ )
+'
+
test_done
diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh
index 488945e007..a1ec501a87 100755
--- a/t/t3402-rebase-merge.sh
+++ b/t/t3402-rebase-merge.sh
@@ -25,7 +25,7 @@ test_expect_success setup '
git commit -a -m"master updates a bit more." &&
git checkout side &&
- (echo "0 $T" ; cat original) >renamed &&
+ (echo "0 $T" && cat original) >renamed &&
git add renamed &&
git update-index --force-remove original &&
git commit -a -m"side renames and edits." &&
@@ -143,7 +143,7 @@ test_expect_success 'rebase -s funny -Xopt' '
git checkout -b test-funny master^ &&
test_commit funny &&
(
- PATH=./test-bin:$PATH
+ PATH=./test-bin:$PATH &&
git rebase -s funny -Xopt master
) &&
test -f funny.was.run
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 01616901bd..86bba5ed7c 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -119,6 +119,15 @@ test_expect_success 'rebase -i with exec allows git commands in subdirs' '
)
'
+test_expect_success 'rebase -i sets work tree properly' '
+ test_when_finished "rm -rf subdir" &&
+ test_when_finished "test_might_fail git rebase --abort" &&
+ mkdir subdir &&
+ git rebase -x "(cd subdir && git rev-parse --show-toplevel)" HEAD^ \
+ >actual &&
+ ! grep "/subdir$" actual
+'
+
test_expect_success 'rebase -i with the exec command checks tree cleanness' '
git checkout master &&
set_fake_editor &&
@@ -516,7 +525,7 @@ test_expect_success 'interrupted squash works as expected' '
one=$(git rev-parse HEAD~3) &&
set_fake_editor &&
test_must_fail env FAKE_LINES="1 squash 3 2" git rebase -i HEAD~3 &&
- (echo one; echo two; echo four) > conflict &&
+ test_write_lines one two four > conflict &&
git add conflict &&
test_must_fail git rebase --continue &&
echo resolved > conflict &&
@@ -530,10 +539,10 @@ test_expect_success 'interrupted squash works as expected (case 2)' '
one=$(git rev-parse HEAD~3) &&
set_fake_editor &&
test_must_fail env FAKE_LINES="3 squash 1 2" git rebase -i HEAD~3 &&
- (echo one; echo four) > conflict &&
+ test_write_lines one four > conflict &&
git add conflict &&
test_must_fail git rebase --continue &&
- (echo one; echo two; echo four) > conflict &&
+ test_write_lines one two four > conflict &&
git add conflict &&
test_must_fail git rebase --continue &&
echo resolved > conflict &&
@@ -787,16 +796,15 @@ test_expect_success 'always cherry-pick with --no-ff' '
git tag original-no-ff-branch &&
set_fake_editor &&
git rebase -i --no-ff A &&
- touch empty &&
for p in 0 1 2
do
test ! $(git rev-parse HEAD~$p) = $(git rev-parse original-no-ff-branch~$p) &&
git diff HEAD~$p original-no-ff-branch~$p > out &&
- test_cmp empty out
+ test_must_be_empty out
done &&
test $(git rev-parse HEAD~3) = $(git rev-parse original-no-ff-branch~3) &&
git diff HEAD~3 original-no-ff-branch~3 > out &&
- test_cmp empty out
+ test_must_be_empty out
'
test_expect_success 'set up commits with funny messages' '
@@ -1238,7 +1246,7 @@ rebase_setup_and_clean () {
test_might_fail git branch -D $1 &&
test_might_fail git rebase --abort
" &&
- git checkout -b $1 master
+ git checkout -b $1 ${2:-master}
}
test_expect_success 'drop' '
@@ -1415,4 +1423,24 @@ test_expect_success 'rebase -i --gpg-sign=<key-id> overrides commit.gpgSign' '
test_i18ngrep "$SQ-S\"S I Gner\"$SQ" err
'
+test_expect_success 'valid author header after --root swap' '
+ rebase_setup_and_clean author-header no-conflict-branch &&
+ set_fake_editor &&
+ git commit --amend --author="Au ${SQ}thor <author@example.com>" --no-edit &&
+ git cat-file commit HEAD | grep ^author >expected &&
+ FAKE_LINES="5 1" git rebase -i --root &&
+ git cat-file commit HEAD^ | grep ^author >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'valid author header when author contains single quote' '
+ rebase_setup_and_clean author-header no-conflict-branch &&
+ set_fake_editor &&
+ git commit --amend --author="Au ${SQ}thor <author@example.com>" --no-edit &&
+ git cat-file commit HEAD | grep ^author >expected &&
+ FAKE_LINES="2" git rebase -i HEAD~2 &&
+ git cat-file commit HEAD | grep ^author >actual &&
+ test_cmp expected actual
+'
+
test_done
diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh
index c145dbac38..25099d715c 100755
--- a/t/t3418-rebase-continue.sh
+++ b/t/t3418-rebase-continue.sh
@@ -60,7 +60,7 @@ test_expect_success 'rebase --continue remembers merge strategy and options' '
EOF
chmod +x test-bin/git-merge-funny &&
(
- PATH=./test-bin:$PATH
+ PATH=./test-bin:$PATH &&
test_must_fail git rebase -s funny -Xopt master topic
) &&
test -f funny.was.run &&
@@ -68,7 +68,7 @@ test_expect_success 'rebase --continue remembers merge strategy and options' '
echo "Resolved" >F2 &&
git add F2 &&
(
- PATH=./test-bin:$PATH
+ PATH=./test-bin:$PATH &&
git rebase --continue
) &&
test -f funny.was.run
@@ -160,13 +160,15 @@ test_expect_success '--skip after failed fixup cleans commit message' '
: The first squash was skipped, therefore: &&
git show HEAD >out &&
test_i18ngrep "# This is a combination of 2 commits" out &&
+ test_i18ngrep "# This is the commit message #2:" out &&
(test_set_editor "$PWD/copy-editor.sh" && git rebase --skip) &&
git show HEAD >out &&
test_i18ngrep ! "# This is a combination" out &&
: Final squash failed, but there was still a squash &&
- test_i18ngrep "# This is a combination of 2 commits" .git/copy.txt
+ test_i18ngrep "# This is a combination of 2 commits" .git/copy.txt &&
+ test_i18ngrep "# This is the commit message #2:" .git/copy.txt
'
test_expect_success 'setup rerere database' '
diff --git a/t/t3420-rebase-autostash.sh b/t/t3420-rebase-autostash.sh
index e243700660..0c4eefec76 100755
--- a/t/t3420-rebase-autostash.sh
+++ b/t/t3420-rebase-autostash.sh
@@ -202,7 +202,7 @@ testrebase () {
echo dirty >>file3 &&
test_must_fail git rebase$type related-onto-branch &&
test_path_is_file $dotest/autostash &&
- ! grep dirty file3 &&
+ test_path_is_missing file3 &&
rm -rf $dotest &&
git reset --hard &&
git checkout feature-branch
@@ -216,7 +216,7 @@ testrebase () {
echo dirty >>file3 &&
test_must_fail git rebase$type related-onto-branch &&
test_path_is_file $dotest/autostash &&
- ! grep dirty file3 &&
+ test_path_is_missing file3 &&
echo "conflicting-plus-goodbye" >file2 &&
git add file2 &&
git rebase --continue &&
@@ -233,7 +233,7 @@ testrebase () {
echo dirty >>file3 &&
test_must_fail git rebase$type related-onto-branch &&
test_path_is_file $dotest/autostash &&
- ! grep dirty file3 &&
+ test_path_is_missing file3 &&
git rebase --skip &&
test_path_is_missing $dotest/autostash &&
grep dirty file3 &&
@@ -248,7 +248,7 @@ testrebase () {
echo dirty >>file3 &&
test_must_fail git rebase$type related-onto-branch &&
test_path_is_file $dotest/autostash &&
- ! grep dirty file3 &&
+ test_path_is_missing file3 &&
git rebase --abort &&
test_path_is_missing $dotest/autostash &&
grep dirty file3 &&
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index 78f7c99580..aa7bfc88ec 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -13,8 +13,10 @@ Initial setup:
-- B -- (first)
/ \
A - C - D - E - H (master)
- \ /
- F - G (second)
+ \ \ /
+ \ F - G (second)
+ \
+ Conflicting-G
'
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-rebase.sh
@@ -49,7 +51,9 @@ test_expect_success 'setup' '
git merge --no-commit G &&
test_tick &&
git commit -m H &&
- git tag -m H H
+ git tag -m H H &&
+ git checkout A &&
+ test_commit conflicting-G G.t
'
test_expect_success 'create completely different structure' '
@@ -72,7 +76,7 @@ test_expect_success 'create completely different structure' '
EOF
test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
test_tick &&
- git rebase -i -r A &&
+ git rebase -i -r A master &&
test_cmp_graph <<-\EOF
* Merge the topic branch '\''onebranch'\''
|\
@@ -125,7 +129,7 @@ test_expect_success '`reset` refuses to overwrite untracked files' '
git rebase --abort
'
-test_expect_success 'failed `merge` writes patch (may be rescheduled, too)' '
+test_expect_success 'failed `merge -C` writes patch (may be rescheduled, too)' '
test_when_finished "test_might_fail git rebase --abort" &&
git checkout -b conflicting-merge A &&
@@ -141,13 +145,25 @@ test_expect_success 'failed `merge` writes patch (may be rescheduled, too)' '
: fail because of merge conflict &&
rm G.t .git/rebase-merge/patch &&
- git reset --hard &&
- test_commit conflicting-G G.t not-G conflicting-G &&
+ git reset --hard conflicting-G &&
test_must_fail git rebase --continue &&
! grep "^merge -C .* G$" .git/rebase-merge/git-rebase-todo &&
test_path_is_file .git/rebase-merge/patch
'
+SQ="'"
+test_expect_success 'failed `merge <branch>` does not crash' '
+ test_when_finished "test_might_fail git rebase --abort" &&
+ git checkout conflicting-G &&
+
+ echo "merge G" >script-from-scratch &&
+ test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
+ test_tick &&
+ test_must_fail git rebase -ir HEAD &&
+ ! grep "^merge G$" .git/rebase-merge/git-rebase-todo &&
+ grep "^Merge branch ${SQ}G${SQ}$" .git/rebase-merge/message
+'
+
test_expect_success 'with a branch tip that was cherry-picked already' '
git checkout -b already-upstream master &&
base="$(git rev-parse --verify HEAD)" &&
@@ -329,4 +345,55 @@ test_expect_success 'labels that are object IDs are rewritten' '
! grep "^label $third$" .git/ORIGINAL-TODO
'
+test_expect_success 'octopus merges' '
+ git checkout -b three &&
+ test_commit before-octopus &&
+ test_commit three &&
+ git checkout -b two HEAD^ &&
+ test_commit two &&
+ git checkout -b one HEAD^ &&
+ test_commit one &&
+ test_tick &&
+ (GIT_AUTHOR_NAME="Hank" GIT_AUTHOR_EMAIL="hank@sea.world" \
+ git merge -m "Tüntenfüsch" two three) &&
+
+ : fast forward if possible &&
+ before="$(git rev-parse --verify HEAD)" &&
+ test_tick &&
+ git rebase -i -r HEAD^^ &&
+ test_cmp_rev HEAD $before &&
+
+ test_tick &&
+ git rebase -i --force -r HEAD^^ &&
+ test "Hank" = "$(git show -s --format=%an HEAD)" &&
+ test "$before" != $(git rev-parse HEAD) &&
+ test_cmp_graph HEAD^^.. <<-\EOF
+ *-. Tüntenfüsch
+ |\ \
+ | | * three
+ | * | two
+ | |/
+ * | one
+ |/
+ o before-octopus
+ EOF
+'
+
+test_expect_success 'with --autosquash and --exec' '
+ git checkout -b with-exec H &&
+ echo Booh >B.t &&
+ test_tick &&
+ git commit --fixup B B.t &&
+ write_script show.sh <<-\EOF &&
+ subject="$(git show -s --format=%s HEAD)"
+ content="$(git diff HEAD^! | tail -n 1)"
+ echo "$subject: $content"
+ EOF
+ test_tick &&
+ git rebase -ir --autosquash --exec ./show.sh A >actual &&
+ grep "B: +Booh" actual &&
+ grep "E: +Booh" actual &&
+ grep "G: +G" actual
+'
+
test_done
diff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh
index 7c5ad08626..0db166152a 100755
--- a/t/t3507-cherry-pick-conflict.sh
+++ b/t/t3507-cherry-pick-conflict.sh
@@ -392,4 +392,17 @@ test_expect_success 'commit --amend -s places the sign-off at the right place' '
test_cmp expect actual
'
+test_expect_success 'cherry-pick preserves sparse-checkout' '
+ pristine_detach initial &&
+ test_config core.sparseCheckout true &&
+ test_when_finished "
+ echo \"/*\" >.git/info/sparse-checkout
+ git read-tree --reset -u HEAD
+ rm .git/info/sparse-checkout" &&
+ echo /unrelated >.git/info/sparse-checkout &&
+ git read-tree --reset -u HEAD &&
+ test_must_fail git cherry-pick -Xours picked>actual &&
+ test_i18ngrep ! "Changes not staged for commit:" actual
+'
+
test_done
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
index 3505b6aa14..c84eeefdc9 100755
--- a/t/t3510-cherry-pick-sequence.sh
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -103,7 +103,8 @@ test_expect_success '--quit cleans up sequencer state' '
pristine_detach initial &&
test_expect_code 1 git cherry-pick base..picked &&
git cherry-pick --quit &&
- test_path_is_missing .git/sequencer
+ test_path_is_missing .git/sequencer &&
+ test_path_is_missing .git/CHERRY_PICK_HEAD
'
test_expect_success '--quit keeps HEAD and conflicted index intact' '
@@ -132,6 +133,7 @@ test_expect_success '--abort to cancel multiple cherry-pick' '
test_expect_code 1 git cherry-pick base..anotherpick &&
git cherry-pick --abort &&
test_path_is_missing .git/sequencer &&
+ test_path_is_missing .git/CHERRY_PICK_HEAD &&
test_cmp_rev initial HEAD &&
git update-index --refresh &&
git diff-index --exit-code HEAD
@@ -142,6 +144,7 @@ test_expect_success '--abort to cancel single cherry-pick' '
test_expect_code 1 git cherry-pick picked &&
git cherry-pick --abort &&
test_path_is_missing .git/sequencer &&
+ test_path_is_missing .git/CHERRY_PICK_HEAD &&
test_cmp_rev initial HEAD &&
git update-index --refresh &&
git diff-index --exit-code HEAD
@@ -162,6 +165,7 @@ test_expect_success 'cherry-pick --abort to cancel multiple revert' '
test_expect_code 1 git revert base..picked &&
git cherry-pick --abort &&
test_path_is_missing .git/sequencer &&
+ test_path_is_missing .git/CHERRY_PICK_HEAD &&
test_cmp_rev anotherpick HEAD &&
git update-index --refresh &&
git diff-index --exit-code HEAD
@@ -239,6 +243,7 @@ test_expect_success '--abort after last commit in sequence' '
test_expect_code 1 git cherry-pick base..picked &&
git cherry-pick --abort &&
test_path_is_missing .git/sequencer &&
+ test_path_is_missing .git/CHERRY_PICK_HEAD &&
test_cmp_rev initial HEAD &&
git update-index --refresh &&
git diff-index --exit-code HEAD
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index b8fbdefcdc..04e5d42bd3 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -14,15 +14,13 @@ test_expect_success \
git add -- foo bar baz 'space embedded' -q &&
git commit -m 'add normal files'"
-if test_have_prereq !MINGW && touch -- 'tab embedded' 'newline
-embedded' 2>/dev/null
-then
- test_set_prereq FUNNYNAMES
-else
+if test_have_prereq !FUNNYNAMES; then
say 'Your filesystem does not allow tabs in filenames.'
fi
test_expect_success FUNNYNAMES 'add files with funny names' "
+ touch -- 'tab embedded' 'newline
+embedded' &&
git add -- 'tab embedded' 'newline
embedded' &&
git commit -m 'add files with tabs and newlines'
@@ -382,7 +380,7 @@ test_expect_success 'rm does not complain when no .gitmodules file is found' '
git submodule update &&
git rm .gitmodules &&
git rm submod >actual 2>actual.err &&
- ! test -s actual.err &&
+ test_must_be_empty actual.err &&
! test -d submod &&
! test -f submod/.git &&
git status -s -uno >actual &&
@@ -400,7 +398,7 @@ test_expect_success 'rm will error out on a modified .gitmodules file unless sta
git diff-files --quiet -- submod &&
git add .gitmodules &&
git rm submod >actual 2>actual.err &&
- ! test -s actual.err &&
+ test_must_be_empty actual.err &&
! test -d submod &&
! test -f submod/.git &&
git status -s -uno >actual &&
@@ -694,7 +692,7 @@ test_expect_success 'checking out a commit after submodule removal needs manual
test_cmp expected actual &&
rm -rf submod &&
git status -s -uno --ignore-submodules=none >actual &&
- ! test -s actual
+ test_must_be_empty actual
'
test_expect_success 'rm of d/f when d has become a non-directory' '
diff --git a/t/t3700-add.sh b/t/t3700-add.sh
index 07af05d7ae..37729ba258 100755
--- a/t/t3700-add.sh
+++ b/t/t3700-add.sh
@@ -156,9 +156,9 @@ test_expect_success 'git add with filemode=0, symlinks=0, and unmerged entries'
test_expect_success 'git add with filemode=0, symlinks=0 prefers stage 2 over stage 1' '
git rm --cached -f file symlink &&
(
- echo "100644 $(git hash-object -w stage1) 1 file"
- echo "100755 $(git hash-object -w stage2) 2 file"
- echo "100644 $(printf 1 | git hash-object -w -t blob --stdin) 1 symlink"
+ echo "100644 $(git hash-object -w stage1) 1 file" &&
+ echo "100755 $(git hash-object -w stage2) 2 file" &&
+ echo "100644 $(printf 1 | git hash-object -w -t blob --stdin) 1 symlink" &&
echo "120000 $(printf 2 | git hash-object -w -t blob --stdin) 2 symlink"
) | git update-index --index-info &&
git config core.filemode 0 &&
@@ -188,9 +188,8 @@ test_expect_success 'git add --refresh with pathspec' '
git add foo bar baz && H=$(git rev-parse :foo) && git rm -f foo &&
echo "100644 $H 3 foo" | git update-index --index-info &&
test-tool chmtime -60 bar baz &&
- >expect &&
git add --refresh bar >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
git diff-files --name-only >actual &&
! grep bar actual&&
@@ -265,7 +264,7 @@ test_expect_success 'git add to resolve conflicts on otherwise ignored path' '
git reset --hard &&
H=$(git rev-parse :1/2/a) &&
(
- echo "100644 $H 1 track-this"
+ echo "100644 $H 1 track-this" &&
echo "100644 $H 3 track-this"
) | git update-index --index-info &&
echo track-this >>.gitignore &&
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index 3e9139dca8..609fbfdc31 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -46,13 +46,13 @@ test_expect_success 'setup expected' '
'
test_expect_success 'diff works (initial)' '
- (echo d; echo 1) | git add -i >output &&
+ test_write_lines d 1 | git add -i >output &&
sed -ne "/new file/,/content/p" <output >diff &&
diff_cmp expected diff
'
test_expect_success 'revert works (initial)' '
git add file &&
- (echo r; echo 1) | git add -i &&
+ test_write_lines r 1 | git add -i &&
git ls-files >output &&
! grep . output
'
@@ -83,13 +83,13 @@ test_expect_success 'setup expected' '
'
test_expect_success 'diff works (commit)' '
- (echo d; echo 1) | git add -i >output &&
+ test_write_lines d 1 | git add -i >output &&
sed -ne "/^index/,/content/p" <output >diff &&
diff_cmp expected diff
'
test_expect_success 'revert works (commit)' '
git add file &&
- (echo r; echo 1) | git add -i &&
+ test_write_lines r 1 | git add -i &&
git add -i </dev/null >output &&
grep "unchanged *+3/-0 file" output
'
@@ -102,7 +102,7 @@ test_expect_success 'setup expected' '
test_expect_success 'dummy edit works' '
test_set_editor : &&
- (echo e; echo a) | git add -p &&
+ test_write_lines e a | git add -p &&
git diff > diff &&
diff_cmp expected diff
'
@@ -127,7 +127,7 @@ test_expect_success 'setup fake editor' '
test_expect_success 'bad edit rejected' '
git reset &&
- (echo e; echo n; echo d) | git add -p >output &&
+ test_write_lines e n d | git add -p >output &&
grep "hunk does not apply" output
'
@@ -140,7 +140,7 @@ test_expect_success 'setup patch' '
test_expect_success 'garbage edit rejected' '
git reset &&
- (echo e; echo n; echo d) | git add -p >output &&
+ test_write_lines e n d | git add -p >output &&
grep "hunk does not apply" output
'
@@ -170,7 +170,7 @@ test_expect_success 'setup expected' '
'
test_expect_success 'real edit works' '
- (echo e; echo n; echo d) | git add -p &&
+ test_write_lines e n d | git add -p &&
git diff >output &&
diff_cmp expected output
'
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 1f871d3cca..6450bc6698 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -724,7 +724,7 @@ test_expect_success 'store updates stash ref and reflog' '
git add bazzy &&
STASH_ID=$(git stash create) &&
git reset --hard &&
- ! grep quux bazzy &&
+ test_path_is_missing bazzy &&
git stash store -m quuxery $STASH_ID &&
test $(git rev-parse stash) = $STASH_ID &&
git reflog --format=%H stash| grep $STASH_ID &&
diff --git a/t/t3904-stash-patch.sh b/t/t3904-stash-patch.sh
index 83744f8c93..9546b6f8a4 100755
--- a/t/t3904-stash-patch.sh
+++ b/t/t3904-stash-patch.sh
@@ -29,14 +29,14 @@ test_expect_success 'setup' '
test_expect_success 'saying "n" does nothing' '
set_state HEAD HEADfile_work HEADfile_index &&
set_state dir/foo work index &&
- (echo n; echo n; echo n) | test_must_fail git stash save -p &&
+ test_write_lines n n n | test_must_fail git stash save -p &&
verify_state HEAD HEADfile_work HEADfile_index &&
verify_saved_state bar &&
verify_state dir/foo work index
'
test_expect_success 'git stash -p' '
- (echo y; echo n; echo y) | git stash save -p &&
+ test_write_lines y n y | git stash save -p &&
verify_state HEAD committed HEADfile_index &&
verify_saved_state bar &&
verify_state dir/foo head index &&
@@ -51,7 +51,7 @@ test_expect_success 'git stash -p --no-keep-index' '
set_state HEAD HEADfile_work HEADfile_index &&
set_state bar bar_work bar_index &&
set_state dir/foo work index &&
- (echo y; echo n; echo y) | git stash save -p --no-keep-index &&
+ test_write_lines y n y | git stash save -p --no-keep-index &&
verify_state HEAD committed committed &&
verify_state bar bar_work dummy &&
verify_state dir/foo head head &&
@@ -66,7 +66,7 @@ test_expect_success 'git stash --no-keep-index -p' '
set_state HEAD HEADfile_work HEADfile_index &&
set_state bar bar_work bar_index &&
set_state dir/foo work index &&
- (echo y; echo n; echo y) | git stash save --no-keep-index -p &&
+ test_write_lines y n y | git stash save --no-keep-index -p &&
verify_state HEAD committed committed &&
verify_state dir/foo head head &&
verify_state bar bar_work dummy &&
diff --git a/t/t3910-mac-os-precompose.sh b/t/t3910-mac-os-precompose.sh
index 26dd5b7f78..54ce19e353 100755
--- a/t/t3910-mac-os-precompose.sh
+++ b/t/t3910-mac-os-precompose.sh
@@ -187,9 +187,8 @@ test_expect_failure 'handle existing decomposed filenames' '
echo content >"verbatim.$Adiarnfd" &&
git -c core.precomposeunicode=false add "verbatim.$Adiarnfd" &&
git commit -m "existing decomposed file" &&
- >expect &&
git ls-files --exclude-standard -o "verbatim*" >untracked &&
- test_cmp expect untracked
+ test_must_be_empty untracked
'
# Test if the global core.precomposeunicode stops autosensing
diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh
index bf4030371a..c16486a9d4 100755
--- a/t/t4001-diff-rename.sh
+++ b/t/t4001-diff-rename.sh
@@ -180,7 +180,7 @@ test_expect_success 'setup for many rename source candidates' '
git add "path??" &&
test_tick &&
git commit -m "hundred" &&
- (cat path1; echo new) >new-path &&
+ (cat path1 && echo new) >new-path &&
echo old >>path1 &&
git add new-path path1 &&
git diff -l 4 -C -C --cached --name-status >actual 2>actual.err &&
diff --git a/t/t4010-diff-pathspec.sh b/t/t4010-diff-pathspec.sh
index 35b35a81c8..281f8fad0c 100755
--- a/t/t4010-diff-pathspec.sh
+++ b/t/t4010-diff-pathspec.sh
@@ -74,8 +74,7 @@ test_expect_success 'diff-tree pathspec' '
tree2=$(git write-tree) &&
echo "$tree2" &&
git diff-tree -r --name-only $tree $tree2 -- pa path1/a >current &&
- >expected &&
- test_cmp expected current
+ test_must_be_empty current
'
test_expect_success 'diff-tree with wildcard shows dir also matches' '
@@ -111,10 +110,10 @@ test_expect_success 'diff-tree -r with wildcard' '
test_expect_success 'setup submodules' '
test_tick &&
git init submod &&
- ( cd submod && test_commit first; ) &&
+ ( cd submod && test_commit first ) &&
git add submod &&
git commit -m first &&
- ( cd submod && test_commit second; ) &&
+ ( cd submod && test_commit second ) &&
git add submod &&
git commit -m second
'
diff --git a/t/t4011-diff-symlink.sh b/t/t4011-diff-symlink.sh
index 108c012a3a..5ae19b987d 100755
--- a/t/t4011-diff-symlink.sh
+++ b/t/t4011-diff-symlink.sh
@@ -126,7 +126,7 @@ test_expect_success SYMLINKS 'diff symlinks with non-existing targets' '
ln -s take\ over brain &&
test_must_fail git diff --no-index pinky brain >output 2>output.err &&
grep narf output &&
- ! test -s output.err
+ test_must_be_empty output.err
'
test_expect_success SYMLINKS 'setup symlinks with attributes' '
diff --git a/t/t4012-diff-binary.sh b/t/t4012-diff-binary.sh
index 0a8af76aab..6579c81216 100755
--- a/t/t4012-diff-binary.sh
+++ b/t/t4012-diff-binary.sh
@@ -102,10 +102,8 @@ test_expect_success 'apply binary patch' '
test_expect_success 'diff --no-index with binary creation' '
echo Q | q_to_nul >binary &&
- (: hide error code from diff, which just indicates differences
- git diff --binary --no-index /dev/null binary >current ||
- true
- ) &&
+ # hide error code from diff, which just indicates differences
+ test_might_fail git diff --binary --no-index /dev/null binary >current &&
rm binary &&
git apply --binary <current &&
echo Q >expected &&
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index f8d853595b..73f7038253 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -140,7 +140,7 @@ do
expect="$TEST_DIRECTORY/t4013/diff.$test"
actual="$pfx-diff.$test"
- test_expect_success "git $cmd # magic is ${magic:-"(not used)"}" '
+ test_expect_success "git $cmd # magic is ${magic:-(not used)}" '
{
echo "$ git $cmd"
case "$magic" in
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 53880da7bb..909c743c13 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -1717,4 +1717,38 @@ test_expect_success 'format-patch --pretty=mboxrd' '
test_cmp expect actual
'
+test_expect_success 'interdiff: setup' '
+ git checkout -b boop master &&
+ test_commit fnorp blorp &&
+ test_commit fleep blorp
+'
+
+test_expect_success 'interdiff: cover-letter' '
+ sed "y/q/ /" >expect <<-\EOF &&
+ +fleep
+ --q
+ EOF
+ git format-patch --cover-letter --interdiff=boop~2 -1 boop &&
+ test_i18ngrep "^Interdiff:$" 0000-cover-letter.patch &&
+ test_i18ngrep ! "^Interdiff:$" 0001-fleep.patch &&
+ sed "1,/^@@ /d; /^-- $/q" <0000-cover-letter.patch >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'interdiff: reroll-count' '
+ git format-patch --cover-letter --interdiff=boop~2 -v2 -1 boop &&
+ test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
+'
+
+test_expect_success 'interdiff: solo-patch' '
+ cat >expect <<-\EOF &&
+ +fleep
+
+ EOF
+ git format-patch --interdiff=boop~2 -1 boop &&
+ test_i18ngrep "^Interdiff:$" 0001-fleep.patch &&
+ sed "1,/^ @@ /d; /^$/q" <0001-fleep.patch >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index 17df491a3a..a9fb226c5a 100755
--- a/t/t4015-diff-whitespace.sh
+++ b/t/t4015-diff-whitespace.sh
@@ -93,21 +93,20 @@ test_expect_success 'another test, without options' '
git diff >out &&
test_cmp expect out &&
- >expect &&
git diff -w >out &&
- test_cmp expect out &&
+ test_must_be_empty out &&
git diff -w -b >out &&
- test_cmp expect out &&
+ test_must_be_empty out &&
git diff -w --ignore-space-at-eol >out &&
- test_cmp expect out &&
+ test_must_be_empty out &&
git diff -w -b --ignore-space-at-eol >out &&
- test_cmp expect out &&
+ test_must_be_empty out &&
git diff -w --ignore-cr-at-eol >out &&
- test_cmp expect out &&
+ test_must_be_empty out &&
tr "Q_" "\015 " <<-\EOF >expect &&
diff --git a/x b/x
@@ -182,8 +181,7 @@ test_expect_success 'ignore-blank-lines: only new lines' '
test_seq 5 | sed "/3/i\\
" >x &&
git diff --ignore-blank-lines >out &&
- >expect &&
- test_cmp expect out
+ test_must_be_empty out
'
test_expect_success 'ignore-blank-lines: only new lines with space' '
@@ -192,8 +190,7 @@ test_expect_success 'ignore-blank-lines: only new lines with space' '
test_seq 5 | sed "/3/i\\
" >x &&
git diff -w --ignore-blank-lines >out &&
- >expect &&
- test_cmp expect out
+ test_must_be_empty out
'
test_expect_success 'ignore-blank-lines: after change' '
@@ -779,8 +776,6 @@ test_expect_success 'checkdiff allows new blank lines' '
git diff --check
'
-cat <<EOF >expect
-EOF
test_expect_success 'whitespace-only changes not reported' '
git reset --hard &&
echo >x "hello world" &&
@@ -788,7 +783,7 @@ test_expect_success 'whitespace-only changes not reported' '
git commit -m "hello 1" &&
echo >x "hello world" &&
git diff -b >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
cat <<EOF >expect
@@ -1223,7 +1218,7 @@ test_expect_success 'plain moved code, inside file' '
test_cmp expected actual
'
-test_expect_success 'detect permutations inside moved code -- dimmed_zebra' '
+test_expect_success 'detect blocks of moved code' '
git reset --hard &&
cat <<-\EOF >lines.txt &&
long line 1
@@ -1271,9 +1266,52 @@ test_expect_success 'detect permutations inside moved code -- dimmed_zebra' '
test_config color.diff.newMovedDimmed "normal cyan" &&
test_config color.diff.oldMovedAlternativeDimmed "normal blue" &&
test_config color.diff.newMovedAlternativeDimmed "normal yellow" &&
- git diff HEAD --no-renames --color-moved=dimmed_zebra --color |
- grep -v "index" |
- test_decode_color >actual &&
+ git diff HEAD --no-renames --color-moved=blocks --color >actual.raw &&
+ grep -v "index" actual.raw | test_decode_color >actual &&
+ cat <<-\EOF >expected &&
+ <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+ <BOLD>--- a/lines.txt<RESET>
+ <BOLD>+++ b/lines.txt<RESET>
+ <CYAN>@@ -1,16 +1,16 @@<RESET>
+ <MAGENTA>-long line 1<RESET>
+ <MAGENTA>-long line 2<RESET>
+ <MAGENTA>-long line 3<RESET>
+ line 4<RESET>
+ line 5<RESET>
+ line 6<RESET>
+ line 7<RESET>
+ line 8<RESET>
+ line 9<RESET>
+ <CYAN>+<RESET><CYAN>long line 1<RESET>
+ <CYAN>+<RESET><CYAN>long line 2<RESET>
+ <CYAN>+<RESET><CYAN>long line 3<RESET>
+ <CYAN>+<RESET><CYAN>long line 14<RESET>
+ <CYAN>+<RESET><CYAN>long line 15<RESET>
+ <CYAN>+<RESET><CYAN>long line 16<RESET>
+ line 10<RESET>
+ line 11<RESET>
+ line 12<RESET>
+ line 13<RESET>
+ <MAGENTA>-long line 14<RESET>
+ <MAGENTA>-long line 15<RESET>
+ <MAGENTA>-long line 16<RESET>
+ EOF
+ test_cmp expected actual
+
+'
+
+test_expect_success 'detect permutations inside moved code -- dimmed-zebra' '
+ # reuse setup from test before!
+ test_config color.diff.oldMoved "magenta" &&
+ test_config color.diff.newMoved "cyan" &&
+ test_config color.diff.oldMovedAlternative "blue" &&
+ test_config color.diff.newMovedAlternative "yellow" &&
+ test_config color.diff.oldMovedDimmed "normal magenta" &&
+ test_config color.diff.newMovedDimmed "normal cyan" &&
+ test_config color.diff.oldMovedAlternativeDimmed "normal blue" &&
+ test_config color.diff.newMovedAlternativeDimmed "normal yellow" &&
+ git diff HEAD --no-renames --color-moved=dimmed-zebra --color >actual.raw &&
+ grep -v "index" actual.raw | test_decode_color >actual &&
cat <<-\EOF >expected &&
<BOLD>diff --git a/lines.txt b/lines.txt<RESET>
<BOLD>--- a/lines.txt<RESET>
@@ -1315,9 +1353,8 @@ test_expect_success 'cmd option assumes configured colored-moved' '
test_config color.diff.oldMovedAlternativeDimmed "normal blue" &&
test_config color.diff.newMovedAlternativeDimmed "normal yellow" &&
test_config diff.colorMoved zebra &&
- git diff HEAD --no-renames --color-moved --color |
- grep -v "index" |
- test_decode_color >actual &&
+ git diff HEAD --no-renames --color-moved --color >actual.raw &&
+ grep -v "index" actual.raw | test_decode_color >actual &&
cat <<-\EOF >expected &&
<BOLD>diff --git a/lines.txt b/lines.txt<RESET>
<BOLD>--- a/lines.txt<RESET>
@@ -1395,9 +1432,8 @@ test_expect_success 'move detection ignoring whitespace ' '
line 4
line 5
EOF
- git diff HEAD --no-renames --color-moved --color |
- grep -v "index" |
- test_decode_color >actual &&
+ git diff HEAD --no-renames --color-moved --color >actual.raw &&
+ grep -v "index" actual.raw | test_decode_color >actual &&
cat <<-\EOF >expected &&
<BOLD>diff --git a/lines.txt b/lines.txt<RESET>
<BOLD>--- a/lines.txt<RESET>
@@ -1419,9 +1455,9 @@ test_expect_success 'move detection ignoring whitespace ' '
EOF
test_cmp expected actual &&
- git diff HEAD --no-renames -w --color-moved --color |
- grep -v "index" |
- test_decode_color >actual &&
+ git diff HEAD --no-renames --color-moved --color \
+ --color-moved-ws=ignore-all-space >actual.raw &&
+ grep -v "index" actual.raw | test_decode_color >actual &&
cat <<-\EOF >expected &&
<BOLD>diff --git a/lines.txt b/lines.txt<RESET>
<BOLD>--- a/lines.txt<RESET>
@@ -1459,9 +1495,8 @@ test_expect_success 'move detection ignoring whitespace changes' '
line 5
EOF
- git diff HEAD --no-renames --color-moved --color |
- grep -v "index" |
- test_decode_color >actual &&
+ git diff HEAD --no-renames --color-moved --color >actual.raw &&
+ grep -v "index" actual.raw | test_decode_color >actual &&
cat <<-\EOF >expected &&
<BOLD>diff --git a/lines.txt b/lines.txt<RESET>
<BOLD>--- a/lines.txt<RESET>
@@ -1483,9 +1518,9 @@ test_expect_success 'move detection ignoring whitespace changes' '
EOF
test_cmp expected actual &&
- git diff HEAD --no-renames -b --color-moved --color |
- grep -v "index" |
- test_decode_color >actual &&
+ git diff HEAD --no-renames --color-moved --color \
+ --color-moved-ws=ignore-space-change >actual.raw &&
+ grep -v "index" actual.raw | test_decode_color >actual &&
cat <<-\EOF >expected &&
<BOLD>diff --git a/lines.txt b/lines.txt<RESET>
<BOLD>--- a/lines.txt<RESET>
@@ -1526,9 +1561,8 @@ test_expect_success 'move detection ignoring whitespace at eol' '
# avoid cluttering the output with complaints about our eol whitespace
test_config core.whitespace -blank-at-eol &&
- git diff HEAD --no-renames --color-moved --color |
- grep -v "index" |
- test_decode_color >actual &&
+ git diff HEAD --no-renames --color-moved --color >actual.raw &&
+ grep -v "index" actual.raw | test_decode_color >actual &&
cat <<-\EOF >expected &&
<BOLD>diff --git a/lines.txt b/lines.txt<RESET>
<BOLD>--- a/lines.txt<RESET>
@@ -1550,9 +1584,9 @@ test_expect_success 'move detection ignoring whitespace at eol' '
EOF
test_cmp expected actual &&
- git diff HEAD --no-renames --ignore-space-at-eol --color-moved --color |
- grep -v "index" |
- test_decode_color >actual &&
+ git diff HEAD --no-renames --color-moved --color \
+ --color-moved-ws=ignore-space-at-eol >actual.raw &&
+ grep -v "index" actual.raw | test_decode_color >actual &&
cat <<-\EOF >expected &&
<BOLD>diff --git a/lines.txt b/lines.txt<RESET>
<BOLD>--- a/lines.txt<RESET>
@@ -1597,9 +1631,8 @@ test_expect_success '--color-moved block at end of diff output respects MIN_ALNU
irrelevant_line
EOF
- git diff HEAD --color-moved=zebra --color --no-renames |
- grep -v "index" |
- test_decode_color >actual &&
+ git diff HEAD --color-moved=zebra --color --no-renames >actual.raw &&
+ grep -v "index" actual.raw | test_decode_color >actual &&
cat >expected <<-\EOF &&
<BOLD>diff --git a/bar b/bar<RESET>
<BOLD>--- a/bar<RESET>
@@ -1636,9 +1669,8 @@ test_expect_success '--color-moved respects MIN_ALNUM_COUNT' '
nineteen chars 456789
EOF
- git diff HEAD --color-moved=zebra --color --no-renames |
- grep -v "index" |
- test_decode_color >actual &&
+ git diff HEAD --color-moved=zebra --color --no-renames >actual.raw &&
+ grep -v "index" actual.raw | test_decode_color >actual &&
cat >expected <<-\EOF &&
<BOLD>diff --git a/bar b/bar<RESET>
<BOLD>--- a/bar<RESET>
@@ -1679,7 +1711,8 @@ test_expect_success '--color-moved treats adjacent blocks as separate for MIN_AL
7charsA
EOF
- git diff HEAD --color-moved=zebra --color --no-renames | grep -v "index" | test_decode_color >actual &&
+ git diff HEAD --color-moved=zebra --color --no-renames >actual.raw &&
+ grep -v "index" actual.raw | test_decode_color >actual &&
cat >expected <<-\EOF &&
<BOLD>diff --git a/bar b/bar<RESET>
<BOLD>--- a/bar<RESET>
@@ -1722,7 +1755,146 @@ test_expect_success 'move detection with submodules' '
# nor did we mess with it another way
git diff --submodule=diff --color | test_decode_color >expect &&
- test_cmp expect decoded_actual
+ test_cmp expect decoded_actual &&
+ rm -rf bananas &&
+ git submodule deinit bananas
+'
+
+test_expect_success 'only move detection ignores white spaces' '
+ git reset --hard &&
+ q_to_tab <<-\EOF >text.txt &&
+ a long line to exceed per-line minimum
+ another long line to exceed per-line minimum
+ original file
+ EOF
+ git add text.txt &&
+ git commit -m "add text" &&
+ q_to_tab <<-\EOF >text.txt &&
+ Qa long line to exceed per-line minimum
+ Qanother long line to exceed per-line minimum
+ new file
+ EOF
+
+ # Make sure we get a different diff using -w
+ git diff --color --color-moved -w >actual.raw &&
+ grep -v "index" actual.raw | test_decode_color >actual &&
+ q_to_tab <<-\EOF >expected &&
+ <BOLD>diff --git a/text.txt b/text.txt<RESET>
+ <BOLD>--- a/text.txt<RESET>
+ <BOLD>+++ b/text.txt<RESET>
+ <CYAN>@@ -1,3 +1,3 @@<RESET>
+ Qa long line to exceed per-line minimum<RESET>
+ Qanother long line to exceed per-line minimum<RESET>
+ <RED>-original file<RESET>
+ <GREEN>+<RESET><GREEN>new file<RESET>
+ EOF
+ test_cmp expected actual &&
+
+ # And now ignoring white space only in the move detection
+ git diff --color --color-moved \
+ --color-moved-ws=ignore-all-space,ignore-space-change,ignore-space-at-eol >actual.raw &&
+ grep -v "index" actual.raw | test_decode_color >actual &&
+ q_to_tab <<-\EOF >expected &&
+ <BOLD>diff --git a/text.txt b/text.txt<RESET>
+ <BOLD>--- a/text.txt<RESET>
+ <BOLD>+++ b/text.txt<RESET>
+ <CYAN>@@ -1,3 +1,3 @@<RESET>
+ <BOLD;MAGENTA>-a long line to exceed per-line minimum<RESET>
+ <BOLD;MAGENTA>-another long line to exceed per-line minimum<RESET>
+ <RED>-original file<RESET>
+ <BOLD;YELLOW>+<RESET>Q<BOLD;YELLOW>a long line to exceed per-line minimum<RESET>
+ <BOLD;YELLOW>+<RESET>Q<BOLD;YELLOW>another long line to exceed per-line minimum<RESET>
+ <GREEN>+<RESET><GREEN>new file<RESET>
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'compare whitespace delta across moved blocks' '
+
+ git reset --hard &&
+ q_to_tab <<-\EOF >text.txt &&
+ QIndented
+ QText across
+ Qsome lines
+ QBut! <- this stands out
+ QAdjusting with
+ QQdifferent starting
+ Qwhite spaces
+ QAnother outlier
+ QQQIndented
+ QQQText across
+ QQQfive lines
+ QQQthat has similar lines
+ QQQto previous blocks, but with different indent
+ QQQYetQAnotherQoutlierQ
+ EOF
+
+ git add text.txt &&
+ git commit -m "add text.txt" &&
+
+ q_to_tab <<-\EOF >text.txt &&
+ QQIndented
+ QQText across
+ QQsome lines
+ QQQBut! <- this stands out
+ Adjusting with
+ Qdifferent starting
+ white spaces
+ AnotherQoutlier
+ QQIndented
+ QQText across
+ QQfive lines
+ QQthat has similar lines
+ QQto previous blocks, but with different indent
+ QQYetQAnotherQoutlier
+ EOF
+
+ git diff --color --color-moved --color-moved-ws=allow-indentation-change >actual.raw &&
+ grep -v "index" actual.raw | test_decode_color >actual &&
+
+ q_to_tab <<-\EOF >expected &&
+ <BOLD>diff --git a/text.txt b/text.txt<RESET>
+ <BOLD>--- a/text.txt<RESET>
+ <BOLD>+++ b/text.txt<RESET>
+ <CYAN>@@ -1,14 +1,14 @@<RESET>
+ <BOLD;MAGENTA>-QIndented<RESET>
+ <BOLD;MAGENTA>-QText across<RESET>
+ <BOLD;MAGENTA>-Qsome lines<RESET>
+ <RED>-QBut! <- this stands out<RESET>
+ <BOLD;MAGENTA>-QAdjusting with<RESET>
+ <BOLD;MAGENTA>-QQdifferent starting<RESET>
+ <BOLD;MAGENTA>-Qwhite spaces<RESET>
+ <RED>-QAnother outlier<RESET>
+ <BOLD;MAGENTA>-QQQIndented<RESET>
+ <BOLD;MAGENTA>-QQQText across<RESET>
+ <BOLD;MAGENTA>-QQQfive lines<RESET>
+ <BOLD;MAGENTA>-QQQthat has similar lines<RESET>
+ <BOLD;MAGENTA>-QQQto previous blocks, but with different indent<RESET>
+ <RED>-QQQYetQAnotherQoutlierQ<RESET>
+ <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>Indented<RESET>
+ <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>Text across<RESET>
+ <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>some lines<RESET>
+ <GREEN>+<RESET>QQQ<GREEN>But! <- this stands out<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN>Adjusting with<RESET>
+ <BOLD;CYAN>+<RESET>Q<BOLD;CYAN>different starting<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN>white spaces<RESET>
+ <GREEN>+<RESET><GREEN>AnotherQoutlier<RESET>
+ <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>Indented<RESET>
+ <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>Text across<RESET>
+ <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>five lines<RESET>
+ <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>that has similar lines<RESET>
+ <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>to previous blocks, but with different indent<RESET>
+ <GREEN>+<RESET>QQ<GREEN>YetQAnotherQoutlier<RESET>
+ EOF
+
+ test_cmp expected actual
+'
+
+test_expect_success 'compare whitespace delta incompatible with other space options' '
+ test_must_fail git diff \
+ --color-moved-ws=allow-indentation-change,ignore-all-space \
+ 2>err &&
+ test_i18ngrep allow-indentation-change err
'
test_done
diff --git a/t/t4019-diff-wserror.sh b/t/t4019-diff-wserror.sh
index a5019759bc..c6135c7548 100755
--- a/t/t4019-diff-wserror.sh
+++ b/t/t4019-diff-wserror.sh
@@ -260,7 +260,7 @@ test_expect_success 'trailing empty lines (2)' '
echo "F -whitespace" >.gitattributes &&
git diff --check >output &&
- ! test -s output
+ test_must_be_empty output
'
diff --git a/t/t4024-diff-optimize-common.sh b/t/t4024-diff-optimize-common.sh
index 7e76018296..6b44ce1493 100755
--- a/t/t4024-diff-optimize-common.sh
+++ b/t/t4024-diff-optimize-common.sh
@@ -127,17 +127,17 @@ test_expect_success setup '
for n in $sample
do
- ( zs $n ; echo a ) >file-a$n &&
- ( echo b; zs $n; echo ) >file-b$n &&
- ( printf c; zs $n ) >file-c$n &&
- ( echo d; zs $n ) >file-d$n &&
+ ( zs $n && echo a ) >file-a$n &&
+ ( echo b && zs $n && echo ) >file-b$n &&
+ ( printf c && zs $n ) >file-c$n &&
+ ( echo d && zs $n ) >file-d$n &&
git add file-a$n file-b$n file-c$n file-d$n &&
- ( zs $n ; echo A ) >file-a$n &&
- ( echo B; zs $n; echo ) >file-b$n &&
- ( printf C; zs $n ) >file-c$n &&
- ( echo D; zs $n ) >file-d$n &&
+ ( zs $n && echo A ) >file-a$n &&
+ ( echo B && zs $n && echo ) >file-b$n &&
+ ( printf C && zs $n ) >file-c$n &&
+ ( echo D && zs $n ) >file-d$n &&
expect_pattern $n || return 1
diff --git a/t/t4025-hunk-header.sh b/t/t4025-hunk-header.sh
index 7a3dbc1ea2..fa44e78869 100755
--- a/t/t4025-hunk-header.sh
+++ b/t/t4025-hunk-header.sh
@@ -12,12 +12,12 @@ NS="$N$N$N$N$N$N$N$N$N$N$N$N$N"
test_expect_success setup '
(
- echo "A $NS"
+ echo "A $NS" &&
for c in B C D E F G H I J K
do
echo " $c"
- done
- echo "L $NS"
+ done &&
+ echo "L $NS" &&
for c in M N O P Q R S T U V
do
echo " $c"
@@ -34,7 +34,7 @@ test_expect_success 'hunk header truncation with an overly long line' '
git diff | sed -n -e "s/^.*@@//p" >actual &&
(
- echo " A $N$N$N$N$N$N$N$N$N2"
+ echo " A $N$N$N$N$N$N$N$N$N2" &&
echo " L $N$N$N$N$N$N$N$N$N1"
) >expected &&
test_cmp actual expected
diff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh
index 6304130ad4..9aa8e2b39b 100755
--- a/t/t4027-diff-submodule.sh
+++ b/t/t4027-diff-submodule.sh
@@ -104,19 +104,19 @@ test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match)'
expect_from_to >expect.body $subprev $subprev-dirty &&
test_cmp expect.body actual.body &&
git diff --ignore-submodules HEAD >actual2 &&
- ! test -s actual2 &&
+ test_must_be_empty actual2 &&
git diff --ignore-submodules=untracked HEAD >actual3 &&
sed -e "1,/^@@/d" actual3 >actual3.body &&
expect_from_to >expect.body $subprev $subprev-dirty &&
test_cmp expect.body actual3.body &&
git diff --ignore-submodules=dirty HEAD >actual4 &&
- ! test -s actual4
+ test_must_be_empty actual4
'
test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match) [.gitmodules]' '
git config diff.ignoreSubmodules dirty &&
git diff HEAD >actual &&
- ! test -s actual &&
+ test_must_be_empty actual &&
git config --add -f .gitmodules submodule.subname.ignore none &&
git config --add -f .gitmodules submodule.subname.path sub &&
git diff HEAD >actual &&
@@ -126,7 +126,7 @@ test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match)
git config -f .gitmodules submodule.subname.ignore all &&
git config -f .gitmodules submodule.subname.path sub &&
git diff HEAD >actual2 &&
- ! test -s actual2 &&
+ test_must_be_empty actual2 &&
git config -f .gitmodules submodule.subname.ignore untracked &&
git diff HEAD >actual3 &&
sed -e "1,/^@@/d" actual3 >actual3.body &&
@@ -134,7 +134,7 @@ test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match)
test_cmp expect.body actual3.body &&
git config -f .gitmodules submodule.subname.ignore dirty &&
git diff HEAD >actual4 &&
- ! test -s actual4 &&
+ test_must_be_empty actual4 &&
git config submodule.subname.ignore none &&
git config submodule.subname.path sub &&
git diff HEAD >actual &&
@@ -172,24 +172,24 @@ test_expect_success 'git diff HEAD with dirty submodule (untracked, refs match)'
expect_from_to >expect.body $subprev $subprev-dirty &&
test_cmp expect.body actual.body &&
git diff --ignore-submodules=all HEAD >actual2 &&
- ! test -s actual2 &&
+ test_must_be_empty actual2 &&
git diff --ignore-submodules=untracked HEAD >actual3 &&
- ! test -s actual3 &&
+ test_must_be_empty actual3 &&
git diff --ignore-submodules=dirty HEAD >actual4 &&
- ! test -s actual4
+ test_must_be_empty actual4
'
test_expect_success 'git diff HEAD with dirty submodule (untracked, refs match) [.gitmodules]' '
git config --add -f .gitmodules submodule.subname.ignore all &&
git config --add -f .gitmodules submodule.subname.path sub &&
git diff HEAD >actual2 &&
- ! test -s actual2 &&
+ test_must_be_empty actual2 &&
git config -f .gitmodules submodule.subname.ignore untracked &&
git diff HEAD >actual3 &&
- ! test -s actual3 &&
+ test_must_be_empty actual3 &&
git config -f .gitmodules submodule.subname.ignore dirty &&
git diff HEAD >actual4 &&
- ! test -s actual4 &&
+ test_must_be_empty actual4 &&
git config submodule.subname.ignore none &&
git config submodule.subname.path sub &&
git diff HEAD >actual &&
@@ -211,7 +211,7 @@ test_expect_success 'git diff between submodule commits' '
expect_from_to >expect.body $subtip $subprev &&
test_cmp expect.body actual.body &&
git diff --ignore-submodules HEAD^..HEAD >actual &&
- ! test -s actual
+ test_must_be_empty actual
'
test_expect_success 'git diff between submodule commits [.gitmodules]' '
@@ -227,7 +227,7 @@ test_expect_success 'git diff between submodule commits [.gitmodules]' '
test_cmp expect.body actual.body &&
git config -f .gitmodules submodule.subname.ignore all &&
git diff HEAD^..HEAD >actual &&
- ! test -s actual &&
+ test_must_be_empty actual &&
git config submodule.subname.ignore dirty &&
git config submodule.subname.path sub &&
git diff HEAD^..HEAD >actual &&
@@ -239,10 +239,9 @@ test_expect_success 'git diff between submodule commits [.gitmodules]' '
'
test_expect_success 'git diff (empty submodule dir)' '
- : >empty &&
rm -rf sub/* sub/.git &&
git diff > actual.empty &&
- test_cmp empty actual.empty
+ test_must_be_empty actual.empty
'
test_expect_success 'conflicted submodule setup' '
diff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh
index 23c0e357a7..53ac44b0f0 100755
--- a/t/t4039-diff-assume-unchanged.sh
+++ b/t/t4039-diff-assume-unchanged.sh
@@ -34,9 +34,8 @@ test_expect_success POSIXPERM 'find-copies-harder is not confused by mode bits'
git add exec &&
git commit -m exec &&
git update-index --assume-unchanged exec &&
- >expect &&
git diff-files --find-copies-harder -- exec >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_done
diff --git a/t/t4041-diff-submodule-option.sh b/t/t4041-diff-submodule-option.sh
index 058ee0829d..619bf97098 100755
--- a/t/t4041-diff-submodule-option.sh
+++ b/t/t4041-diff-submodule-option.sh
@@ -257,9 +257,7 @@ test_expect_success 'typechanged submodule(blob->submodule)' '
commit_file sm1 &&
test_expect_success 'submodule is up to date' '
git diff-index -p --submodule=log HEAD >actual &&
- cat >expected <<-EOF &&
- EOF
- test_cmp expected actual
+ test_must_be_empty actual
'
test_expect_success 'submodule contains untracked content' '
@@ -273,17 +271,17 @@ test_expect_success 'submodule contains untracked content' '
test_expect_success 'submodule contains untracked content (untracked ignored)' '
git diff-index -p --ignore-submodules=untracked --submodule=log HEAD >actual &&
- ! test -s actual
+ test_must_be_empty actual
'
test_expect_success 'submodule contains untracked content (dirty ignored)' '
git diff-index -p --ignore-submodules=dirty --submodule=log HEAD >actual &&
- ! test -s actual
+ test_must_be_empty actual
'
test_expect_success 'submodule contains untracked content (all ignored)' '
git diff-index -p --ignore-submodules=all --submodule=log HEAD >actual &&
- ! test -s actual
+ test_must_be_empty actual
'
test_expect_success 'submodule contains untracked and modifed content' '
@@ -308,13 +306,13 @@ test_expect_success 'submodule contains untracked and modifed content (untracked
test_expect_success 'submodule contains untracked and modifed content (dirty ignored)' '
echo new > sm1/foo6 &&
git diff-index -p --ignore-submodules=dirty --submodule=log HEAD >actual &&
- ! test -s actual
+ test_must_be_empty actual
'
test_expect_success 'submodule contains untracked and modifed content (all ignored)' '
echo new > sm1/foo6 &&
git diff-index -p --ignore-submodules --submodule=log HEAD >actual &&
- ! test -s actual
+ test_must_be_empty actual
'
test_expect_success 'submodule contains modifed content' '
@@ -368,7 +366,7 @@ test_expect_success 'modified submodule contains untracked content (dirty ignore
test_expect_success 'modified submodule contains untracked content (all ignored)' '
git diff-index -p --ignore-submodules=all --submodule=log HEAD >actual &&
- ! test -s actual
+ test_must_be_empty actual
'
test_expect_success 'modified submodule contains untracked and modifed content' '
@@ -407,7 +405,7 @@ test_expect_success 'modified submodule contains untracked and modifed content (
test_expect_success 'modified submodule contains untracked and modifed content (all ignored)' '
echo modification >> sm1/foo6 &&
git diff-index -p --ignore-submodules --submodule=log HEAD >actual &&
- ! test -s actual
+ test_must_be_empty actual
'
test_expect_success 'modified submodule contains modifed content' '
@@ -498,7 +496,7 @@ test_expect_success 'given commit --submodule=short' '
test_expect_success 'setup .git file for sm2' '
(cd sm2 &&
REAL="$(pwd)/../.real" &&
- mv .git "$REAL"
+ mv .git "$REAL" &&
echo "gitdir: $REAL" >.git)
'
@@ -527,7 +525,7 @@ test_expect_success 'diff --submodule with objects referenced by alternates' '
git commit -m "sub a"
) &&
(cd sub_alt &&
- sha1_before=$(git rev-parse --short HEAD)
+ sha1_before=$(git rev-parse --short HEAD) &&
echo b >b &&
git add b &&
git commit -m b &&
diff --git a/t/t4047-diff-dirstat.sh b/t/t4047-diff-dirstat.sh
index 447a8ffa3a..7fec2cb9cd 100755
--- a/t/t4047-diff-dirstat.sh
+++ b/t/t4047-diff-dirstat.sh
@@ -940,7 +940,7 @@ test_expect_success 'diff.dirstat=0,lines' '
test_expect_success '--dirstat=future_param,lines,0 should fail loudly' '
test_must_fail git diff --dirstat=future_param,lines,0 HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&
test_debug "cat actual_error" &&
- test_cmp /dev/null actual_diff_dirstat &&
+ test_must_be_empty actual_diff_dirstat &&
test_i18ngrep -q "future_param" actual_error &&
test_i18ngrep -q "\--dirstat" actual_error
'
@@ -948,7 +948,7 @@ test_expect_success '--dirstat=future_param,lines,0 should fail loudly' '
test_expect_success '--dirstat=dummy1,cumulative,2dummy should report both unrecognized parameters' '
test_must_fail git diff --dirstat=dummy1,cumulative,2dummy HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&
test_debug "cat actual_error" &&
- test_cmp /dev/null actual_diff_dirstat &&
+ test_must_be_empty actual_diff_dirstat &&
test_i18ngrep -q "dummy1" actual_error &&
test_i18ngrep -q "2dummy" actual_error &&
test_i18ngrep -q "\--dirstat" actual_error
diff --git a/t/t4051-diff-function-context.sh b/t/t4051-diff-function-context.sh
index 2d76a971c4..4838a1df8b 100755
--- a/t/t4051-diff-function-context.sh
+++ b/t/t4051-diff-function-context.sh
@@ -174,7 +174,7 @@ test_expect_success ' context does not include other functions' '
'
test_expect_success ' context does not include preceding empty lines' '
- test "$(first_context_line <long_common_tail.diff.diff)" != " "
+ test "$(first_context_line <long_common_tail.diff)" != " "
'
check_diff changed_hello_appended 'changed function plus appended function'
diff --git a/t/t4060-diff-submodule-option-diff-format.sh b/t/t4060-diff-submodule-option-diff-format.sh
index 4b168d0ed7..9dcb69df5c 100755
--- a/t/t4060-diff-submodule-option-diff-format.sh
+++ b/t/t4060-diff-submodule-option-diff-format.sh
@@ -392,9 +392,7 @@ test_expect_success 'typechanged submodule(blob->submodule)' '
commit_file sm1 &&
test_expect_success 'submodule is up to date' '
git diff-index -p --submodule=diff HEAD >actual &&
- cat >expected <<-EOF &&
- EOF
- test_cmp expected actual
+ test_must_be_empty actual
'
test_expect_success 'submodule contains untracked content' '
@@ -408,17 +406,17 @@ test_expect_success 'submodule contains untracked content' '
test_expect_success 'submodule contains untracked content (untracked ignored)' '
git diff-index -p --ignore-submodules=untracked --submodule=diff HEAD >actual &&
- ! test -s actual
+ test_must_be_empty actual
'
test_expect_success 'submodule contains untracked content (dirty ignored)' '
git diff-index -p --ignore-submodules=dirty --submodule=diff HEAD >actual &&
- ! test -s actual
+ test_must_be_empty actual
'
test_expect_success 'submodule contains untracked content (all ignored)' '
git diff-index -p --ignore-submodules=all --submodule=diff HEAD >actual &&
- ! test -s actual
+ test_must_be_empty actual
'
test_expect_success 'submodule contains untracked and modified content' '
@@ -458,13 +456,13 @@ test_expect_success 'submodule contains untracked and modified content (untracke
test_expect_success 'submodule contains untracked and modified content (dirty ignored)' '
echo new > sm1/foo6 &&
git diff-index -p --ignore-submodules=dirty --submodule=diff HEAD >actual &&
- ! test -s actual
+ test_must_be_empty actual
'
test_expect_success 'submodule contains untracked and modified content (all ignored)' '
echo new > sm1/foo6 &&
git diff-index -p --ignore-submodules --submodule=diff HEAD >actual &&
- ! test -s actual
+ test_must_be_empty actual
'
test_expect_success 'submodule contains modified content' '
@@ -549,7 +547,7 @@ test_expect_success 'modified submodule contains untracked content (dirty ignore
test_expect_success 'modified submodule contains untracked content (all ignored)' '
git diff-index -p --ignore-submodules=all --submodule=diff HEAD >actual &&
- ! test -s actual
+ test_must_be_empty actual
'
test_expect_success 'modified submodule contains untracked and modified content' '
@@ -609,7 +607,7 @@ test_expect_success 'modified submodule contains untracked and modified content
test_expect_success 'modified submodule contains untracked and modified content (all ignored)' '
echo modification >> sm1/foo6 &&
git diff-index -p --ignore-submodules --submodule=diff HEAD >actual &&
- ! test -s actual
+ test_must_be_empty actual
'
# NOT OK
@@ -721,7 +719,7 @@ test_expect_success 'given commit' '
test_expect_success 'setup .git file for sm2' '
(cd sm2 &&
REAL="$(pwd)/../.real" &&
- mv .git "$REAL"
+ mv .git "$REAL" &&
echo "gitdir: $REAL" >.git)
'
diff --git a/t/t4116-apply-reverse.sh b/t/t4116-apply-reverse.sh
index ce8567f496..b99e65c086 100755
--- a/t/t4116-apply-reverse.sh
+++ b/t/t4116-apply-reverse.sh
@@ -42,7 +42,7 @@ test_expect_success 'apply in reverse' '
git reset --hard second &&
git apply --reverse --binary --index patch &&
git diff >diff &&
- test_cmp /dev/null diff
+ test_must_be_empty diff
'
diff --git a/t/t4121-apply-diffs.sh b/t/t4121-apply-diffs.sh
index aff551a1d7..66368effd5 100755
--- a/t/t4121-apply-diffs.sh
+++ b/t/t4121-apply-diffs.sh
@@ -27,6 +27,6 @@ test_expect_success 'setup' \
test_expect_success \
'check if contextually independent diffs for the same file apply' \
- '( git diff test~2 test~1; git diff test~1 test~0 )| git apply'
+ '( git diff test~2 test~1 && git diff test~1 test~0 )| git apply'
test_done
diff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh
index 4fc27c51f7..7e32237a2a 100755
--- a/t/t4124-apply-ws-rule.sh
+++ b/t/t4124-apply-ws-rule.sh
@@ -100,7 +100,7 @@ test_expect_success 'whitespace=warn, default rule' '
test_expect_success 'whitespace=error-all, default rule' '
test_must_fail apply_patch --whitespace=error-all &&
- ! test -s target
+ test_must_be_empty target
'
diff --git a/t/t4132-apply-removal.sh b/t/t4132-apply-removal.sh
index a2bc1cd37d..fec1d6fa51 100755
--- a/t/t4132-apply-removal.sh
+++ b/t/t4132-apply-removal.sh
@@ -49,8 +49,7 @@ test_expect_success setup '
sed -e "s/TS0/$timeGMT/" -e "s/TS1/$epocGMT/" <d >removeGMT.patch &&
sed -e "s/TS0/$timeWest/" -e "s/TS1/$epocWest2/" <d >removeWest2.patch &&
- echo something >something &&
- >empty
+ echo something >something
'
for patch in *.patch
@@ -81,7 +80,7 @@ do
git add file &&
git apply --index $patch &&
test -f file &&
- test_cmp empty file
+ test_must_be_empty file
;;
remove*)
# must remove the file
diff --git a/t/t4135-apply-weird-filenames.sh b/t/t4135-apply-weird-filenames.sh
index c7c688fcc4..6bc3fb97a7 100755
--- a/t/t4135-apply-weird-filenames.sh
+++ b/t/t4135-apply-weird-filenames.sh
@@ -15,15 +15,7 @@ test_expect_success 'setup' '
git checkout -f preimage^0 &&
git read-tree -u --reset HEAD &&
git update-index --refresh
- } &&
-
- test_when_finished "rm -f \"tab embedded.txt\"" &&
- test_when_finished "rm -f '\''\"quoteembedded\".txt'\''" &&
- if test_have_prereq !MINGW &&
- touch -- "tab embedded.txt" '\''"quoteembedded".txt'\''
- then
- test_set_prereq FUNNYNAMES
- fi
+ }
'
try_filename() {
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 1ebc587f8f..55b577d919 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -69,13 +69,15 @@ test_expect_success 'setup: messages' '
EOF
- cat >scissors-msg <<-\EOF &&
- Test git-am with scissors line
+ cat >msg-without-scissors-line <<-\EOF &&
+ Test that git-am --scissors cuts at the scissors line
This line should be included in the commit message.
EOF
- cat - scissors-msg >no-scissors-msg <<-\EOF &&
+ printf "Subject: " >subject-prefix &&
+
+ cat - subject-prefix msg-without-scissors-line >msg-with-scissors-line <<-\EOF &&
This line should not be included in the commit message with --scissors enabled.
- - >8 - - remove everything above this line - - >8 - -
@@ -148,18 +150,17 @@ test_expect_success setup '
} >patch1-hg.eml &&
- echo scissors-file >scissors-file &&
- git add scissors-file &&
- git commit -F scissors-msg &&
- git tag scissors &&
- git format-patch --stdout scissors^ >scissors-patch.eml &&
+ echo file >file &&
+ git add file &&
+ git commit -F msg-without-scissors-line &&
+ git tag expected-for-scissors &&
git reset --hard HEAD^ &&
- echo no-scissors-file >no-scissors-file &&
- git add no-scissors-file &&
- git commit -F no-scissors-msg &&
- git tag no-scissors &&
- git format-patch --stdout no-scissors^ >no-scissors-patch.eml &&
+ echo file >file &&
+ git add file &&
+ git commit -F msg-with-scissors-line &&
+ git tag expected-for-no-scissors &&
+ git format-patch --stdout expected-for-no-scissors^ >patch-with-scissors-line.eml &&
git reset --hard HEAD^ &&
sed -n -e "3,\$p" msg >file &&
@@ -416,10 +417,10 @@ test_expect_success 'am --scissors cuts the message at the scissors line' '
rm -fr .git/rebase-apply &&
git reset --hard &&
git checkout second &&
- git am --scissors scissors-patch.eml &&
+ git am --scissors patch-with-scissors-line.eml &&
test_path_is_missing .git/rebase-apply &&
- git diff --exit-code scissors &&
- test_cmp_rev scissors HEAD
+ git diff --exit-code expected-for-scissors &&
+ test_cmp_rev expected-for-scissors HEAD
'
test_expect_success 'am --no-scissors overrides mailinfo.scissors' '
@@ -427,10 +428,10 @@ test_expect_success 'am --no-scissors overrides mailinfo.scissors' '
git reset --hard &&
git checkout second &&
test_config mailinfo.scissors true &&
- git am --no-scissors no-scissors-patch.eml &&
+ git am --no-scissors patch-with-scissors-line.eml &&
test_path_is_missing .git/rebase-apply &&
- git diff --exit-code no-scissors &&
- test_cmp_rev no-scissors HEAD
+ git diff --exit-code expected-for-no-scissors &&
+ test_cmp_rev expected-for-no-scissors HEAD
'
test_expect_success 'setup: new author and committer' '
@@ -651,7 +652,7 @@ test_expect_success 'am -3 -q is quiet' '
git checkout -f lorem2 &&
git reset base3way --hard &&
git am -3 -q lorem-move.patch >output.out 2>&1 &&
- ! test -s output.out
+ test_must_be_empty output.out
'
test_expect_success 'am pauses on conflict' '
@@ -874,7 +875,7 @@ test_expect_success 'am -q is quiet' '
git checkout first &&
test_tick &&
git am -q <patch1 >output.out 2>&1 &&
- ! test -s output.out
+ test_must_be_empty output.out
'
test_expect_success 'am empty-file does not infloop' '
diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh
index 8417e5a4b1..428b3c1e9e 100755
--- a/t/t4200-rerere.sh
+++ b/t/t4200-rerere.sh
@@ -267,8 +267,7 @@ rerere_gc_custom_expiry_test () {
git -c "gc.rerereresolved=$right_now" \
-c "gc.rerereunresolved=$right_now" rerere gc &&
find .git/rr-cache -type f | sort >actual &&
- >expect &&
- test_cmp expect actual
+ test_must_be_empty actual
'
}
@@ -536,9 +535,8 @@ test_expect_success 'multiple identical conflicts' '
# We resolved file1 and file2
git rerere &&
- >expect &&
git rerere remaining >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
# We must have recorded both of them
count_pre_post 2 2 &&
@@ -548,9 +546,8 @@ test_expect_success 'multiple identical conflicts' '
test_must_fail git merge six.1 &&
git rerere &&
- >expect &&
git rerere remaining >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
concat_insert short 6.1 6.2 >file1.expect &&
concat_insert long 6.1 6.2 >file2.expect &&
@@ -580,4 +577,69 @@ test_expect_success 'multiple identical conflicts' '
count_pre_post 0 0
'
+test_expect_success 'rerere with unexpected conflict markers does not crash' '
+ git reset --hard &&
+
+ git checkout -b branch-1 master &&
+ echo "bar" >test &&
+ git add test &&
+ git commit -q -m two &&
+
+ git reset --hard &&
+ git checkout -b branch-2 master &&
+ echo "foo" >test &&
+ git add test &&
+ git commit -q -a -m one &&
+
+ test_must_fail git merge branch-1 &&
+ echo "<<<<<<< a" >test &&
+ git rerere &&
+
+ git rerere clear
+'
+
+test_expect_success 'rerere with inner conflict markers' '
+ git reset --hard &&
+
+ git checkout -b A master &&
+ echo "bar" >test &&
+ git add test &&
+ git commit -q -m two &&
+ echo "baz" >test &&
+ git add test &&
+ git commit -q -m three &&
+
+ git reset --hard &&
+ git checkout -b B master &&
+ echo "foo" >test &&
+ git add test &&
+ git commit -q -a -m one &&
+
+ test_must_fail git merge A~ &&
+ git add test &&
+ git commit -q -m "will solve conflicts later" &&
+ test_must_fail git merge A &&
+
+ echo "resolved" >test &&
+ git add test &&
+ git commit -q -m "solved conflict" &&
+
+ echo "resolved" >expect &&
+
+ git reset --hard HEAD~~ &&
+ test_must_fail git merge A~ &&
+ git add test &&
+ git commit -q -m "will solve conflicts later" &&
+ test_must_fail git merge A &&
+ cat test >actual &&
+ test_cmp expect actual &&
+
+ git add test &&
+ git commit -m "rerere solved conflict" &&
+ git reset --hard HEAD~ &&
+ test_must_fail git merge A &&
+ cat test >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh
index 58c2773676..d3a7ce6bbb 100755
--- a/t/t4201-shortlog.sh
+++ b/t/t4201-shortlog.sh
@@ -192,7 +192,7 @@ test_expect_success 'shortlog with revision pseudo options' '
test_expect_success 'shortlog with --output=<file>' '
git shortlog --output=shortlog -1 master >output &&
- test ! -s output &&
+ test_must_be_empty output &&
test_line_count = 3 shortlog
'
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index 25b1f8cc73..153a506151 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -340,10 +340,9 @@ test_expect_success PCRE 'log -F -E --perl-regexp --grep=<pcre> uses PCRE' '
'
test_expect_success 'log with grep.patternType configuration' '
- >expect &&
git -c grep.patterntype=fixed \
log -1 --pretty=tformat:%s --grep=s.c.nd >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'log with grep.patternType configuration and command line' '
@@ -1556,12 +1555,28 @@ test_expect_success GPG 'setup signed branch' '
git commit -S -m signed_commit
'
+test_expect_success GPGSM 'setup signed branch x509' '
+ test_when_finished "git reset --hard && git checkout master" &&
+ git checkout -b signed-x509 master &&
+ echo foo >foo &&
+ git add foo &&
+ test_config gpg.format x509 &&
+ test_config user.signingkey $GIT_COMMITTER_EMAIL &&
+ git commit -S -m signed_commit
+'
+
test_expect_success GPG 'log --graph --show-signature' '
git log --graph --show-signature -n1 signed >actual &&
grep "^| gpg: Signature made" actual &&
grep "^| gpg: Good signature" actual
'
+test_expect_success GPGSM 'log --graph --show-signature x509' '
+ git log --graph --show-signature -n1 signed-x509 >actual &&
+ grep "^| gpgsm: Signature made" actual &&
+ grep "^| gpgsm: Good signature" actual
+'
+
test_expect_success GPG 'log --graph --show-signature for merged tag' '
test_when_finished "git reset --hard && git checkout master" &&
git checkout -b plain master &&
@@ -1581,6 +1596,27 @@ test_expect_success GPG 'log --graph --show-signature for merged tag' '
grep "^| | gpg: Good signature" actual
'
+test_expect_success GPGSM 'log --graph --show-signature for merged tag x509' '
+ test_when_finished "git reset --hard && git checkout master" &&
+ test_config gpg.format x509 &&
+ test_config user.signingkey $GIT_COMMITTER_EMAIL &&
+ git checkout -b plain-x509 master &&
+ echo aaa >bar &&
+ git add bar &&
+ git commit -m bar_commit &&
+ git checkout -b tagged-x509 master &&
+ echo bbb >baz &&
+ git add baz &&
+ git commit -m baz_commit &&
+ git tag -s -m signed_tag_msg signed_tag_x509 &&
+ git checkout plain-x509 &&
+ git merge --no-ff -m msg signed_tag_x509 &&
+ git log --graph --show-signature -n1 plain-x509 >actual &&
+ grep "^|\\\ merged tag" actual &&
+ grep "^| | gpgsm: Signature made" actual &&
+ grep "^| | gpgsm: Good signature" actual
+'
+
test_expect_success GPG '--no-show-signature overrides --show-signature' '
git log -1 --show-signature --no-show-signature signed >actual &&
! grep "^gpg:" actual
@@ -1625,9 +1661,8 @@ test_expect_success 'log diagnoses bogus HEAD' '
'
test_expect_success 'log does not default to HEAD when rev input is given' '
- >expect &&
git log --branches=does-not-exist >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'set up --source tests' '
diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh
index 0dd8b65d7c..43b1522ea2 100755
--- a/t/t4203-mailmap.sh
+++ b/t/t4203-mailmap.sh
@@ -461,11 +461,9 @@ test_expect_success 'Grep author with log.mailmap' '
test_cmp expect actual
'
->expect
-
test_expect_success 'Only grep replaced author with --use-mailmap' '
git log --use-mailmap --author "<cto@coompany.xx>" >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
# git blame
diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh
index 2052cadb11..978a8a66ff 100755
--- a/t/t4205-log-pretty-formats.sh
+++ b/t/t4205-log-pretty-formats.sh
@@ -598,4 +598,27 @@ test_expect_success ':only and :unfold work together' '
test_cmp expect actual
'
+test_expect_success 'trailer parsing not fooled by --- line' '
+ git commit --allow-empty -F - <<-\EOF &&
+ this is the subject
+
+ This is the body. The message has a "---" line which would confuse a
+ message+patch parser. But here we know we have only a commit message,
+ so we get it right.
+
+ trailer: wrong
+ ---
+ This is more body.
+
+ trailer: right
+ EOF
+
+ {
+ echo "trailer: right" &&
+ echo
+ } >expect &&
+ git log --no-walk --format="%(trailers)" >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t4210-log-i18n.sh b/t/t4210-log-i18n.sh
index e585fe6129..7c519436ef 100755
--- a/t/t4210-log-i18n.sh
+++ b/t/t4210-log-i18n.sh
@@ -44,15 +44,13 @@ test_expect_success !MINGW 'log --grep searches in log output encoding (latin1)'
'
test_expect_success !MINGW 'log --grep does not find non-reencoded values (utf8)' '
- >expect &&
git log --encoding=utf8 --format=%s --grep=$latin1_e >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'log --grep does not find non-reencoded values (latin1)' '
- >expect &&
git log --encoding=ISO-8859-1 --format=%s --grep=$utf8_e >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_done
diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh
index d0377fae5c..ef1322148e 100755
--- a/t/t4211-line-log.sh
+++ b/t/t4211-line-log.sh
@@ -60,7 +60,6 @@ test_bad_opts "-L 1:nonexistent" "There is no path"
test_bad_opts "-L 1:simple" "There is no path"
test_bad_opts "-L '/foo:b.c'" "argument not .start,end:file"
test_bad_opts "-L 1000:b.c" "has only.*lines"
-test_bad_opts "-L 1,1000:b.c" "has only.*lines"
test_bad_opts "-L :b.c" "argument not .start,end:file"
test_bad_opts "-L :foo:b.c" "no match"
@@ -86,12 +85,12 @@ test_expect_success '-L ,Y (Y == nlines)' '
test_expect_success '-L ,Y (Y == nlines + 1)' '
n=$(expr $(wc -l <b.c) + 1) &&
- test_must_fail git log -L ,$n:b.c
+ git log -L ,$n:b.c
'
test_expect_success '-L ,Y (Y == nlines + 2)' '
n=$(expr $(wc -l <b.c) + 2) &&
- test_must_fail git log -L ,$n:b.c
+ git log -L ,$n:b.c
'
test_expect_success '-L with --first-parent and a merge' '
@@ -102,7 +101,7 @@ test_expect_success '-L with --first-parent and a merge' '
test_expect_success '-L with --output' '
git checkout parallel-change &&
git log --output=log -L :main:b.c >output &&
- test ! -s output &&
+ test_must_be_empty output &&
test_line_count = 70 log
'
diff --git a/t/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh
index 22aa8b7c0e..03b952c90d 100755
--- a/t/t4212-log-corrupt.sh
+++ b/t/t4212-log-corrupt.sh
@@ -26,22 +26,20 @@ test_expect_success 'git log with broken author email' '
echo
echo " foo"
} >expect.out &&
- : >expect.err &&
git log broken_email >actual.out 2>actual.err &&
test_cmp expect.out actual.out &&
- test_cmp expect.err actual.err
+ test_must_be_empty actual.err
'
test_expect_success 'git log --format with broken author email' '
echo "A U Thor+author@example.com+Thu Apr 7 15:13:13 2005 -0700" >expect.out &&
- : >expect.err &&
git log --format="%an+%ae+%ad" broken_email >actual.out 2>actual.err &&
test_cmp expect.out actual.out &&
- test_cmp expect.err actual.err
+ test_must_be_empty actual.err
'
munge_author_date () {
diff --git a/t/t4256-am-format-flowed.sh b/t/t4256-am-format-flowed.sh
new file mode 100755
index 0000000000..6340310e9a
--- /dev/null
+++ b/t/t4256-am-format-flowed.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+test_description='test format=flowed support of git am'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ cp "$TEST_DIRECTORY/t4256/1/mailinfo.c.orig" mailinfo.c &&
+ git add mailinfo.c &&
+ git commit -m initial
+'
+
+test_expect_success 'am with format=flowed' '
+ git am <"$TEST_DIRECTORY/t4256/1/patch" >stdout 2>stderr &&
+ test_i18ngrep "warning: Patch sent with format=flowed" stderr &&
+ test_cmp "$TEST_DIRECTORY/t4256/1/mailinfo.c" mailinfo.c
+'
+
+test_done
diff --git a/t/t4256/1/mailinfo.c b/t/t4256/1/mailinfo.c
new file mode 100644
index 0000000000..b395adbdf2
--- /dev/null
+++ b/t/t4256/1/mailinfo.c
@@ -0,0 +1,1245 @@
+#include "cache.h"
+#include "config.h"
+#include "utf8.h"
+#include "strbuf.h"
+#include "mailinfo.h"
+
+static void cleanup_space(struct strbuf *sb)
+{
+ size_t pos, cnt;
+ for (pos = 0; pos < sb->len; pos++) {
+ if (isspace(sb->buf[pos])) {
+ sb->buf[pos] = ' ';
+ for (cnt = 0; isspace(sb->buf[pos + cnt + 1]); cnt++);
+ strbuf_remove(sb, pos + 1, cnt);
+ }
+ }
+}
+
+static void get_sane_name(struct strbuf *out, struct strbuf *name, struct strbuf *email)
+{
+ struct strbuf *src = name;
+ if (name->len < 3 || 60 < name->len || strchr(name->buf, '@') ||
+ strchr(name->buf, '<') || strchr(name->buf, '>'))
+ src = email;
+ else if (name == out)
+ return;
+ strbuf_reset(out);
+ strbuf_addbuf(out, src);
+}
+
+static void parse_bogus_from(struct mailinfo *mi, const struct strbuf *line)
+{
+ /* John Doe <johndoe> */
+
+ char *bra, *ket;
+ /* This is fallback, so do not bother if we already have an
+ * e-mail address.
+ */
+ if (mi->email.len)
+ return;
+
+ bra = strchr(line->buf, '<');
+ if (!bra)
+ return;
+ ket = strchr(bra, '>');
+ if (!ket)
+ return;
+
+ strbuf_reset(&mi->email);
+ strbuf_add(&mi->email, bra + 1, ket - bra - 1);
+
+ strbuf_reset(&mi->name);
+ strbuf_add(&mi->name, line->buf, bra - line->buf);
+ strbuf_trim(&mi->name);
+ get_sane_name(&mi->name, &mi->name, &mi->email);
+}
+
+static const char *unquote_comment(struct strbuf *outbuf, const char *in)
+{
+ int c;
+ int take_next_literally = 0;
+
+ strbuf_addch(outbuf, '(');
+
+ while ((c = *in++) != 0) {
+ if (take_next_literally == 1) {
+ take_next_literally = 0;
+ } else {
+ switch (c) {
+ case '\\':
+ take_next_literally = 1;
+ continue;
+ case '(':
+ in = unquote_comment(outbuf, in);
+ continue;
+ case ')':
+ strbuf_addch(outbuf, ')');
+ return in;
+ }
+ }
+
+ strbuf_addch(outbuf, c);
+ }
+
+ return in;
+}
+
+static const char *unquote_quoted_string(struct strbuf *outbuf, const char *in)
+{
+ int c;
+ int take_next_literally = 0;
+
+ while ((c = *in++) != 0) {
+ if (take_next_literally == 1) {
+ take_next_literally = 0;
+ } else {
+ switch (c) {
+ case '\\':
+ take_next_literally = 1;
+ continue;
+ case '"':
+ return in;
+ }
+ }
+
+ strbuf_addch(outbuf, c);
+ }
+
+ return in;
+}
+
+static void unquote_quoted_pair(struct strbuf *line)
+{
+ struct strbuf outbuf;
+ const char *in = line->buf;
+ int c;
+
+ strbuf_init(&outbuf, line->len);
+
+ while ((c = *in++) != 0) {
+ switch (c) {
+ case '"':
+ in = unquote_quoted_string(&outbuf, in);
+ continue;
+ case '(':
+ in = unquote_comment(&outbuf, in);
+ continue;
+ }
+
+ strbuf_addch(&outbuf, c);
+ }
+
+ strbuf_swap(&outbuf, line);
+ strbuf_release(&outbuf);
+
+}
+
+static void handle_from(struct mailinfo *mi, const struct strbuf *from)
+{
+ char *at;
+ size_t el;
+ struct strbuf f;
+
+ strbuf_init(&f, from->len);
+ strbuf_addbuf(&f, from);
+
+ unquote_quoted_pair(&f);
+
+ at = strchr(f.buf, '@');
+ if (!at) {
+ parse_bogus_from(mi, from);
+ goto out;
+ }
+
+ /*
+ * If we already have one email, don't take any confusing lines
+ */
+ if (mi->email.len && strchr(at + 1, '@'))
+ goto out;
+
+ /* Pick up the string around '@', possibly delimited with <>
+ * pair; that is the email part.
+ */
+ while (at > f.buf) {
+ char c = at[-1];
+ if (isspace(c))
+ break;
+ if (c == '<') {
+ at[-1] = ' ';
+ break;
+ }
+ at--;
+ }
+ el = strcspn(at, " \n\t\r\v\f>");
+ strbuf_reset(&mi->email);
+ strbuf_add(&mi->email, at, el);
+ strbuf_remove(&f, at - f.buf, el + (at[el] ? 1 : 0));
+
+ /* The remainder is name. It could be
+ *
+ * - "John Doe <john.doe@xz>" (a), or
+ * - "john.doe@xz (John Doe)" (b), or
+ * - "John (zzz) Doe <john.doe@xz> (Comment)" (c)
+ *
+ * but we have removed the email part, so
+ *
+ * - remove extra spaces which could stay after email (case 'c'), and
+ * - trim from both ends, possibly removing the () pair at the end
+ * (cases 'a' and 'b').
+ */
+ cleanup_space(&f);
+ strbuf_trim(&f);
+ if (f.buf[0] == '(' && f.len && f.buf[f.len - 1] == ')') {
+ strbuf_remove(&f, 0, 1);
+ strbuf_setlen(&f, f.len - 1);
+ }
+
+ get_sane_name(&mi->name, &f, &mi->email);
+out:
+ strbuf_release(&f);
+}
+
+static void handle_header(struct strbuf **out, const struct strbuf *line)
+{
+ if (!*out) {
+ *out = xmalloc(sizeof(struct strbuf));
+ strbuf_init(*out, line->len);
+ } else
+ strbuf_reset(*out);
+
+ strbuf_addbuf(*out, line);
+}
+
+/* NOTE NOTE NOTE. We do not claim we do full MIME. We just attempt
+ * to have enough heuristics to grok MIME encoded patches often found
+ * on our mailing lists. For example, we do not even treat header lines
+ * case insensitively.
+ */
+
+static int slurp_attr(const char *line, const char *name, struct strbuf *attr)
+{
+ const char *ends, *ap = strcasestr(line, name);
+ size_t sz;
+
+ strbuf_setlen(attr, 0);
+ if (!ap)
+ return 0;
+ ap += strlen(name);
+ if (*ap == '"') {
+ ap++;
+ ends = "\"";
+ }
+ else
+ ends = "; \t";
+ sz = strcspn(ap, ends);
+ strbuf_add(attr, ap, sz);
+ return 1;
+}
+
+static int has_attr_value(const char *line, const char *name, const char *value)
+{
+ struct strbuf sb = STRBUF_INIT;
+ int rc = slurp_attr(line, name, &sb) && !strcasecmp(sb.buf, value);
+ strbuf_release(&sb);
+ return rc;
+}
+
+static void handle_content_type(struct mailinfo *mi, struct strbuf *line)
+{
+ struct strbuf *boundary = xmalloc(sizeof(struct strbuf));
+ strbuf_init(boundary, line->len);
+
+ mi->format_flowed = has_attr_value(line->buf, "format=", "flowed");
+ mi->delsp = has_attr_value(line->buf, "delsp=", "yes");
+
+ if (slurp_attr(line->buf, "boundary=", boundary)) {
+ strbuf_insert(boundary, 0, "--", 2);
+ if (++mi->content_top >= &mi->content[MAX_BOUNDARIES]) {
+ error("Too many boundaries to handle");
+ mi->input_error = -1;
+ mi->content_top = &mi->content[MAX_BOUNDARIES] - 1;
+ return;
+ }
+ *(mi->content_top) = boundary;
+ boundary = NULL;
+ }
+ slurp_attr(line->buf, "charset=", &mi->charset);
+
+ if (boundary) {
+ strbuf_release(boundary);
+ free(boundary);
+ }
+}
+
+static void handle_content_transfer_encoding(struct mailinfo *mi,
+ const struct strbuf *line)
+{
+ if (strcasestr(line->buf, "base64"))
+ mi->transfer_encoding = TE_BASE64;
+ else if (strcasestr(line->buf, "quoted-printable"))
+ mi->transfer_encoding = TE_QP;
+ else
+ mi->transfer_encoding = TE_DONTCARE;
+}
+
+static int is_multipart_boundary(struct mailinfo *mi, const struct strbuf *line)
+{
+ struct strbuf *content_top = *(mi->content_top);
+
+ return ((content_top->len <= line->len) &&
+ !memcmp(line->buf, content_top->buf, content_top->len));
+}
+
+static void cleanup_subject(struct mailinfo *mi, struct strbuf *subject)
+{
+ size_t at = 0;
+
+ while (at < subject->len) {
+ char *pos;
+ size_t remove;
+
+ switch (subject->buf[at]) {
+ case 'r': case 'R':
+ if (subject->len <= at + 3)
+ break;
+ if ((subject->buf[at + 1] == 'e' ||
+ subject->buf[at + 1] == 'E') &&
+ subject->buf[at + 2] == ':') {
+ strbuf_remove(subject, at, 3);
+ continue;
+ }
+ at++;
+ break;
+ case ' ': case '\t': case ':':
+ strbuf_remove(subject, at, 1);
+ continue;
+ case '[':
+ pos = strchr(subject->buf + at, ']');
+ if (!pos)
+ break;
+ remove = pos - subject->buf + at + 1;
+ if (!mi->keep_non_patch_brackets_in_subject ||
+ (7 <= remove &&
+ memmem(subject->buf + at, remove, "PATCH", 5)))
+ strbuf_remove(subject, at, remove);
+ else {
+ at += remove;
+ /*
+ * If the input had a space after the ], keep
+ * it. We don't bother with finding the end of
+ * the space, since we later normalize it
+ * anyway.
+ */
+ if (isspace(subject->buf[at]))
+ at += 1;
+ }
+ continue;
+ }
+ break;
+ }
+ strbuf_trim(subject);
+}
+
+#define MAX_HDR_PARSED 10
+static const char *header[MAX_HDR_PARSED] = {
+ "From","Subject","Date",
+};
+
+static inline int cmp_header(const struct strbuf *line, const char *hdr)
+{
+ int len = strlen(hdr);
+ return !strncasecmp(line->buf, hdr, len) && line->len > len &&
+ line->buf[len] == ':' && isspace(line->buf[len + 1]);
+}
+
+static int is_format_patch_separator(const char *line, int len)
+{
+ static const char SAMPLE[] =
+ "From e6807f3efca28b30decfecb1732a56c7db1137ee Mon Sep 17 00:00:00 2001\n";
+ const char *cp;
+
+ if (len != strlen(SAMPLE))
+ return 0;
+ if (!skip_prefix(line, "From ", &cp))
+ return 0;
+ if (strspn(cp, "0123456789abcdef") != 40)
+ return 0;
+ cp += 40;
+ return !memcmp(SAMPLE + (cp - line), cp, strlen(SAMPLE) - (cp - line));
+}
+
+static struct strbuf *decode_q_segment(const struct strbuf *q_seg, int rfc2047)
+{
+ const char *in = q_seg->buf;
+ int c;
+ struct strbuf *out = xmalloc(sizeof(struct strbuf));
+ strbuf_init(out, q_seg->len);
+
+ while ((c = *in++) != 0) {
+ if (c == '=') {
+ int ch, d = *in;
+ if (d == '\n' || !d)
+ break; /* drop trailing newline */
+ ch = hex2chr(in);
+ if (ch >= 0) {
+ strbuf_addch(out, ch);
+ in += 2;
+ continue;
+ }
+ /* garbage -- fall through */
+ }
+ if (rfc2047 && c == '_') /* rfc2047 4.2 (2) */
+ c = 0x20;
+ strbuf_addch(out, c);
+ }
+ return out;
+}
+
+static struct strbuf *decode_b_segment(const struct strbuf *b_seg)
+{
+ /* Decode in..ep, possibly in-place to ot */
+ int c, pos = 0, acc = 0;
+ const char *in = b_seg->buf;
+ struct strbuf *out = xmalloc(sizeof(struct strbuf));
+ strbuf_init(out, b_seg->len);
+
+ while ((c = *in++) != 0) {
+ if (c == '+')
+ c = 62;
+ else if (c == '/')
+ c = 63;
+ else if ('A' <= c && c <= 'Z')
+ c -= 'A';
+ else if ('a' <= c && c <= 'z')
+ c -= 'a' - 26;
+ else if ('0' <= c && c <= '9')
+ c -= '0' - 52;
+ else
+ continue; /* garbage */
+ switch (pos++) {
+ case 0:
+ acc = (c << 2);
+ break;
+ case 1:
+ strbuf_addch(out, (acc | (c >> 4)));
+ acc = (c & 15) << 4;
+ break;
+ case 2:
+ strbuf_addch(out, (acc | (c >> 2)));
+ acc = (c & 3) << 6;
+ break;
+ case 3:
+ strbuf_addch(out, (acc | c));
+ acc = pos = 0;
+ break;
+ }
+ }
+ return out;
+}
+
+static int convert_to_utf8(struct mailinfo *mi,
+ struct strbuf *line, const char *charset)
+{
+ char *out;
+
+ if (!mi->metainfo_charset || !charset || !*charset)
+ return 0;
+
+ if (same_encoding(mi->metainfo_charset, charset))
+ return 0;
+ out = reencode_string(line->buf, mi->metainfo_charset, charset);
+ if (!out) {
+ mi->input_error = -1;
+ return error("cannot convert from %s to %s",
+ charset, mi->metainfo_charset);
+ }
+ strbuf_attach(line, out, strlen(out), strlen(out));
+ return 0;
+}
+
+static void decode_header(struct mailinfo *mi, struct strbuf *it)
+{
+ char *in, *ep, *cp;
+ struct strbuf outbuf = STRBUF_INIT, *dec;
+ struct strbuf charset_q = STRBUF_INIT, piecebuf = STRBUF_INIT;
+ int found_error = 1; /* pessimism */
+
+ in = it->buf;
+ while (in - it->buf <= it->len && (ep = strstr(in, "=?")) != NULL) {
+ int encoding;
+ strbuf_reset(&charset_q);
+ strbuf_reset(&piecebuf);
+
+ if (in != ep) {
+ /*
+ * We are about to process an encoded-word
+ * that begins at ep, but there is something
+ * before the encoded word.
+ */
+ char *scan;
+ for (scan = in; scan < ep; scan++)
+ if (!isspace(*scan))
+ break;
+
+ if (scan != ep || in == it->buf) {
+ /*
+ * We should not lose that "something",
+ * unless we have just processed an
+ * encoded-word, and there is only LWS
+ * before the one we are about to process.
+ */
+ strbuf_add(&outbuf, in, ep - in);
+ }
+ }
+ /* E.g.
+ * ep : "=?iso-2022-jp?B?GyR...?= foo"
+ * ep : "=?ISO-8859-1?Q?Foo=FCbar?= baz"
+ */
+ ep += 2;
+
+ if (ep - it->buf >= it->len || !(cp = strchr(ep, '?')))
+ goto release_return;
+
+ if (cp + 3 - it->buf > it->len)
+ goto release_return;
+ strbuf_add(&charset_q, ep, cp - ep);
+
+ encoding = cp[1];
+ if (!encoding || cp[2] != '?')
+ goto release_return;
+ ep = strstr(cp + 3, "?=");
+ if (!ep)
+ goto release_return;
+ strbuf_add(&piecebuf, cp + 3, ep - cp - 3);
+ switch (tolower(encoding)) {
+ default:
+ goto release_return;
+ case 'b':
+ dec = decode_b_segment(&piecebuf);
+ break;
+ case 'q':
+ dec = decode_q_segment(&piecebuf, 1);
+ break;
+ }
+ if (convert_to_utf8(mi, dec, charset_q.buf))
+ goto release_return;
+
+ strbuf_addbuf(&outbuf, dec);
+ strbuf_release(dec);
+ free(dec);
+ in = ep + 2;
+ }
+ strbuf_addstr(&outbuf, in);
+ strbuf_reset(it);
+ strbuf_addbuf(it, &outbuf);
+ found_error = 0;
+release_return:
+ strbuf_release(&outbuf);
+ strbuf_release(&charset_q);
+ strbuf_release(&piecebuf);
+
+ if (found_error)
+ mi->input_error = -1;
+}
+
+static int check_header(struct mailinfo *mi,
+ const struct strbuf *line,
+ struct strbuf *hdr_data[], int overwrite)
+{
+ int i, ret = 0, len;
+ struct strbuf sb = STRBUF_INIT;
+
+ /* search for the interesting parts */
+ for (i = 0; header[i]; i++) {
+ int len = strlen(header[i]);
+ if ((!hdr_data[i] || overwrite) && cmp_header(line, header[i])) {
+ /* Unwrap inline B and Q encoding, and optionally
+ * normalize the meta information to utf8.
+ */
+ strbuf_add(&sb, line->buf + len + 2, line->len - len - 2);
+ decode_header(mi, &sb);
+ handle_header(&hdr_data[i], &sb);
+ ret = 1;
+ goto check_header_out;
+ }
+ }
+
+ /* Content stuff */
+ if (cmp_header(line, "Content-Type")) {
+ len = strlen("Content-Type: ");
+ strbuf_add(&sb, line->buf + len, line->len - len);
+ decode_header(mi, &sb);
+ strbuf_insert(&sb, 0, "Content-Type: ", len);
+ handle_content_type(mi, &sb);
+ ret = 1;
+ goto check_header_out;
+ }
+ if (cmp_header(line, "Content-Transfer-Encoding")) {
+ len = strlen("Content-Transfer-Encoding: ");
+ strbuf_add(&sb, line->buf + len, line->len - len);
+ decode_header(mi, &sb);
+ handle_content_transfer_encoding(mi, &sb);
+ ret = 1;
+ goto check_header_out;
+ }
+ if (cmp_header(line, "Message-Id")) {
+ len = strlen("Message-Id: ");
+ strbuf_add(&sb, line->buf + len, line->len - len);
+ decode_header(mi, &sb);
+ if (mi->add_message_id)
+ mi->message_id = strbuf_detach(&sb, NULL);
+ ret = 1;
+ goto check_header_out;
+ }
+
+check_header_out:
+ strbuf_release(&sb);
+ return ret;
+}
+
+/*
+ * Returns 1 if the given line or any line beginning with the given line is an
+ * in-body header (that is, check_header will succeed when passed
+ * mi->s_hdr_data).
+ */
+static int is_inbody_header(const struct mailinfo *mi,
+ const struct strbuf *line)
+{
+ int i;
+ for (i = 0; header[i]; i++)
+ if (!mi->s_hdr_data[i] && cmp_header(line, header[i]))
+ return 1;
+ return 0;
+}
+
+static void decode_transfer_encoding(struct mailinfo *mi, struct strbuf *line)
+{
+ struct strbuf *ret;
+
+ switch (mi->transfer_encoding) {
+ case TE_QP:
+ ret = decode_q_segment(line, 0);
+ break;
+ case TE_BASE64:
+ ret = decode_b_segment(line);
+ break;
+ case TE_DONTCARE:
+ default:
+ return;
+ }
+ strbuf_reset(line);
+ strbuf_addbuf(line, ret);
+ strbuf_release(ret);
+ free(ret);
+}
+
+static inline int patchbreak(const struct strbuf *line)
+{
+ size_t i;
+
+ /* Beginning of a "diff -" header? */
+ if (starts_with(line->buf, "diff -"))
+ return 1;
+
+ /* CVS "Index: " line? */
+ if (starts_with(line->buf, "Index: "))
+ return 1;
+
+ /*
+ * "--- <filename>" starts patches without headers
+ * "---<sp>*" is a manual separator
+ */
+ if (line->len < 4)
+ return 0;
+
+ if (starts_with(line->buf, "---")) {
+ /* space followed by a filename? */
+ if (line->buf[3] == ' ' && !isspace(line->buf[4]))
+ return 1;
+ /* Just whitespace? */
+ for (i = 3; i < line->len; i++) {
+ unsigned char c = line->buf[i];
+ if (c == '\n')
+ return 1;
+ if (!isspace(c))
+ break;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+static int is_scissors_line(const char *line)
+{
+ const char *c;
+ int scissors = 0, gap = 0;
+ const char *first_nonblank = NULL, *last_nonblank = NULL;
+ int visible, perforation = 0, in_perforation = 0;
+
+ for (c = line; *c; c++) {
+ if (isspace(*c)) {
+ if (in_perforation) {
+ perforation++;
+ gap++;
+ }
+ continue;
+ }
+ last_nonblank = c;
+ if (first_nonblank == NULL)
+ first_nonblank = c;
+ if (*c == '-') {
+ in_perforation = 1;
+ perforation++;
+ continue;
+ }
+ if ((!memcmp(c, ">8", 2) || !memcmp(c, "8<", 2) ||
+ !memcmp(c, ">%", 2) || !memcmp(c, "%<", 2))) {
+ in_perforation = 1;
+ perforation += 2;
+ scissors += 2;
+ c++;
+ continue;
+ }
+ in_perforation = 0;
+ }
+
+ /*
+ * The mark must be at least 8 bytes long (e.g. "-- >8 --").
+ * Even though there can be arbitrary cruft on the same line
+ * (e.g. "cut here"), in order to avoid misidentification, the
+ * perforation must occupy more than a third of the visible
+ * width of the line, and dashes and scissors must occupy more
+ * than half of the perforation.
+ */
+
+ if (first_nonblank && last_nonblank)
+ visible = last_nonblank - first_nonblank + 1;
+ else
+ visible = 0;
+ return (scissors && 8 <= visible &&
+ visible < perforation * 3 &&
+ gap * 2 < perforation);
+}
+
+static void flush_inbody_header_accum(struct mailinfo *mi)
+{
+ if (!mi->inbody_header_accum.len)
+ return;
+ if (!check_header(mi, &mi->inbody_header_accum, mi->s_hdr_data, 0))
+ BUG("inbody_header_accum, if not empty, must always contain a valid in-body header");
+ strbuf_reset(&mi->inbody_header_accum);
+}
+
+static int check_inbody_header(struct mailinfo *mi, const struct strbuf *line)
+{
+ if (mi->inbody_header_accum.len &&
+ (line->buf[0] == ' ' || line->buf[0] == '\t')) {
+ if (mi->use_scissors && is_scissors_line(line->buf)) {
+ /*
+ * This is a scissors line; do not consider this line
+ * as a header continuation line.
+ */
+ flush_inbody_header_accum(mi);
+ return 0;
+ }
+ strbuf_strip_suffix(&mi->inbody_header_accum, "\n");
+ strbuf_addbuf(&mi->inbody_header_accum, line);
+ return 1;
+ }
+
+ flush_inbody_header_accum(mi);
+
+ if (starts_with(line->buf, ">From") && isspace(line->buf[5]))
+ return is_format_patch_separator(line->buf + 1, line->len - 1);
+ if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) {
+ int i;
+ for (i = 0; header[i]; i++)
+ if (!strcmp("Subject", header[i])) {
+ handle_header(&mi->s_hdr_data[i], line);
+ return 1;
+ }
+ return 0;
+ }
+ if (is_inbody_header(mi, line)) {
+ strbuf_addbuf(&mi->inbody_header_accum, line);
+ return 1;
+ }
+ return 0;
+}
+
+static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
+{
+ assert(!mi->filter_stage);
+
+ if (mi->header_stage) {
+ if (!line->len || (line->len == 1 && line->buf[0] == '\n')) {
+ if (mi->inbody_header_accum.len) {
+ flush_inbody_header_accum(mi);
+ mi->header_stage = 0;
+ }
+ return 0;
+ }
+ }
+
+ if (mi->use_inbody_headers && mi->header_stage) {
+ mi->header_stage = check_inbody_header(mi, line);
+ if (mi->header_stage)
+ return 0;
+ } else
+ /* Only trim the first (blank) line of the commit message
+ * when ignoring in-body headers.
+ */
+ mi->header_stage = 0;
+
+ /* normalize the log message to UTF-8. */
+ if (convert_to_utf8(mi, line, mi->charset.buf))
+ return 0; /* mi->input_error already set */
+
+ if (mi->use_scissors && is_scissors_line(line->buf)) {
+ int i;
+
+ strbuf_setlen(&mi->log_message, 0);
+ mi->header_stage = 1;
+
+ /*
+ * We may have already read "secondary headers"; purge
+ * them to give ourselves a clean restart.
+ */
+ for (i = 0; header[i]; i++) {
+ if (mi->s_hdr_data[i])
+ strbuf_release(mi->s_hdr_data[i]);
+ mi->s_hdr_data[i] = NULL;
+ }
+ return 0;
+ }
+
+ if (patchbreak(line)) {
+ if (mi->message_id)
+ strbuf_addf(&mi->log_message,
+ "Message-Id: %s\n", mi->message_id);
+ return 1;
+ }
+
+ strbuf_addbuf(&mi->log_message, line);
+ return 0;
+}
+
+static void handle_patch(struct mailinfo *mi, const struct strbuf *line)
+{
+ fwrite(line->buf, 1, line->len, mi->patchfile);
+ mi->patch_lines++;
+}
+
+static void handle_filter(struct mailinfo *mi, struct strbuf *line)
+{
+ switch (mi->filter_stage) {
+ case 0:
+ if (!handle_commit_msg(mi, line))
+ break;
+ mi->filter_stage++;
+ /* fallthrough */
+ case 1:
+ handle_patch(mi, line);
+ break;
+ }
+}
+
+static int is_rfc2822_header(const struct strbuf *line)
+{
+ /*
+ * The section that defines the loosest possible
+ * field name is "3.6.8 Optional fields".
+ *
+ * optional-field = field-name ":" unstructured CRLF
+ * field-name = 1*ftext
+ * ftext = %d33-57 / %59-126
+ */
+ int ch;
+ char *cp = line->buf;
+
+ /* Count mbox From headers as headers */
+ if (starts_with(cp, "From ") || starts_with(cp, ">From "))
+ return 1;
+
+ while ((ch = *cp++)) {
+ if (ch == ':')
+ return 1;
+ if ((33 <= ch && ch <= 57) ||
+ (59 <= ch && ch <= 126))
+ continue;
+ break;
+ }
+ return 0;
+}
+
+static int read_one_header_line(struct strbuf *line, FILE *in)
+{
+ struct strbuf continuation = STRBUF_INIT;
+
+ /* Get the first part of the line. */
+ if (strbuf_getline_lf(line, in))
+ return 0;
+
+ /*
+ * Is it an empty line or not a valid rfc2822 header?
+ * If so, stop here, and return false ("not a header")
+ */
+ strbuf_rtrim(line);
+ if (!line->len || !is_rfc2822_header(line)) {
+ /* Re-add the newline */
+ strbuf_addch(line, '\n');
+ return 0;
+ }
+
+ /*
+ * Now we need to eat all the continuation lines..
+ * Yuck, 2822 header "folding"
+ */
+ for (;;) {
+ int peek;
+
+ peek = fgetc(in);
+ if (peek == EOF)
+ break;
+ ungetc(peek, in);
+ if (peek != ' ' && peek != '\t')
+ break;
+ if (strbuf_getline_lf(&continuation, in))
+ break;
+ continuation.buf[0] = ' ';
+ strbuf_rtrim(&continuation);
+ strbuf_addbuf(line, &continuation);
+ }
+ strbuf_release(&continuation);
+
+ return 1;
+}
+
+static int find_boundary(struct mailinfo *mi, struct strbuf *line)
+{
+ while (!strbuf_getline_lf(line, mi->input)) {
+ if (*(mi->content_top) && is_multipart_boundary(mi, line))
+ return 1;
+ }
+ return 0;
+}
+
+static int handle_boundary(struct mailinfo *mi, struct strbuf *line)
+{
+ struct strbuf newline = STRBUF_INIT;
+
+ strbuf_addch(&newline, '\n');
+again:
+ if (line->len >= (*(mi->content_top))->len + 2 &&
+ !memcmp(line->buf + (*(mi->content_top))->len, "--", 2)) {
+ /* we hit an end boundary */
+ /* pop the current boundary off the stack */
+ strbuf_release(*(mi->content_top));
+ FREE_AND_NULL(*(mi->content_top));
+
+ /* technically won't happen as is_multipart_boundary()
+ will fail first. But just in case..
+ */
+ if (--mi->content_top < mi->content) {
+ error("Detected mismatched boundaries, can't recover");
+ mi->input_error = -1;
+ mi->content_top = mi->content;
+ strbuf_release(&newline);
+ return 0;
+ }
+ handle_filter(mi, &newline);
+ strbuf_release(&newline);
+ if (mi->input_error)
+ return 0;
+
+ /* skip to the next boundary */
+ if (!find_boundary(mi, line))
+ return 0;
+ goto again;
+ }
+
+ /* set some defaults */
+ mi->transfer_encoding = TE_DONTCARE;
+ strbuf_reset(&mi->charset);
+
+ /* slurp in this section's info */
+ while (read_one_header_line(line, mi->input))
+ check_header(mi, line, mi->p_hdr_data, 0);
+
+ strbuf_release(&newline);
+ /* replenish line */
+ if (strbuf_getline_lf(line, mi->input))
+ return 0;
+ strbuf_addch(line, '\n');
+ return 1;
+}
+
+static void handle_filter_flowed(struct mailinfo *mi, struct strbuf *line,
+ struct strbuf *prev)
+{
+ size_t len = line->len;
+ const char *rest;
+
+ if (!mi->format_flowed) {
+ handle_filter(mi, line);
+ return;
+ }
+
+ if (line->buf[len - 1] == '\n') {
+ len--;
+ if (len && line->buf[len - 1] == '\r')
+ len--;
+ }
+
+ /* Keep signature separator as-is. */
+ if (skip_prefix(line->buf, "-- ", &rest) && rest - line->buf == len) {
+ if (prev->len) {
+ handle_filter(mi, prev);
+ strbuf_reset(prev);
+ }
+ handle_filter(mi, line);
+ return;
+ }
+
+ /* Unstuff space-stuffed line. */
+ if (len && line->buf[0] == ' ') {
+ strbuf_remove(line, 0, 1);
+ len--;
+ }
+
+ /* Save flowed line for later, but without the soft line break. */
+ if (len && line->buf[len - 1] == ' ') {
+ strbuf_add(prev, line->buf, len - !!mi->delsp);
+ return;
+ }
+
+ /* Prepend any previous partial lines */
+ strbuf_insert(line, 0, prev->buf, prev->len);
+ strbuf_reset(prev);
+
+ handle_filter(mi, line);
+}
+
+static void handle_body(struct mailinfo *mi, struct strbuf *line)
+{
+ struct strbuf prev = STRBUF_INIT;
+
+ /* Skip up to the first boundary */
+ if (*(mi->content_top)) {
+ if (!find_boundary(mi, line))
+ goto handle_body_out;
+ }
+
+ do {
+ /* process any boundary lines */
+ if (*(mi->content_top) && is_multipart_boundary(mi, line)) {
+ /* flush any leftover */
+ if (prev.len) {
+ handle_filter(mi, &prev);
+ strbuf_reset(&prev);
+ }
+ if (!handle_boundary(mi, line))
+ goto handle_body_out;
+ }
+
+ /* Unwrap transfer encoding */
+ decode_transfer_encoding(mi, line);
+
+ switch (mi->transfer_encoding) {
+ case TE_BASE64:
+ case TE_QP:
+ {
+ struct strbuf **lines, **it, *sb;
+
+ /* Prepend any previous partial lines */
+ strbuf_insert(line, 0, prev.buf, prev.len);
+ strbuf_reset(&prev);
+
+ /*
+ * This is a decoded line that may contain
+ * multiple new lines. Pass only one chunk
+ * at a time to handle_filter()
+ */
+ lines = strbuf_split(line, '\n');
+ for (it = lines; (sb = *it); it++) {
+ if (*(it + 1) == NULL) /* The last line */
+ if (sb->buf[sb->len - 1] != '\n') {
+ /* Partial line, save it for later. */
+ strbuf_addbuf(&prev, sb);
+ break;
+ }
+ handle_filter_flowed(mi, sb, &prev);
+ }
+ /*
+ * The partial chunk is saved in "prev" and will be
+ * appended by the next iteration of read_line_with_nul().
+ */
+ strbuf_list_free(lines);
+ break;
+ }
+ default:
+ handle_filter_flowed(mi, line, &prev);
+ }
+
+ if (mi->input_error)
+ break;
+ } while (!strbuf_getwholeline(line, mi->input, '\n'));
+
+ if (prev.len)
+ handle_filter(mi, &prev);
+
+ flush_inbody_header_accum(mi);
+
+handle_body_out:
+ strbuf_release(&prev);
+}
+
+static void output_header_lines(FILE *fout, const char *hdr, const struct strbuf *data)
+{
+ const char *sp = data->buf;
+ while (1) {
+ char *ep = strchr(sp, '\n');
+ int len;
+ if (!ep)
+ len = strlen(sp);
+ else
+ len = ep - sp;
+ fprintf(fout, "%s: %.*s\n", hdr, len, sp);
+ if (!ep)
+ break;
+ sp = ep + 1;
+ }
+}
+
+static void handle_info(struct mailinfo *mi)
+{
+ struct strbuf *hdr;
+ int i;
+
+ for (i = 0; header[i]; i++) {
+ /* only print inbody headers if we output a patch file */
+ if (mi->patch_lines && mi->s_hdr_data[i])
+ hdr = mi->s_hdr_data[i];
+ else if (mi->p_hdr_data[i])
+ hdr = mi->p_hdr_data[i];
+ else
+ continue;
+
+ if (!strcmp(header[i], "Subject")) {
+ if (!mi->keep_subject) {
+ cleanup_subject(mi, hdr);
+ cleanup_space(hdr);
+ }
+ output_header_lines(mi->output, "Subject", hdr);
+ } else if (!strcmp(header[i], "From")) {
+ cleanup_space(hdr);
+ handle_from(mi, hdr);
+ fprintf(mi->output, "Author: %s\n", mi->name.buf);
+ fprintf(mi->output, "Email: %s\n", mi->email.buf);
+ } else {
+ cleanup_space(hdr);
+ fprintf(mi->output, "%s: %s\n", header[i], hdr->buf);
+ }
+ }
+ fprintf(mi->output, "\n");
+}
+
+int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
+{
+ FILE *cmitmsg;
+ int peek;
+ struct strbuf line = STRBUF_INIT;
+
+ cmitmsg = fopen(msg, "w");
+ if (!cmitmsg) {
+ perror(msg);
+ return -1;
+ }
+ mi->patchfile = fopen(patch, "w");
+ if (!mi->patchfile) {
+ perror(patch);
+ fclose(cmitmsg);
+ return -1;
+ }
+
+ mi->p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*(mi->p_hdr_data)));
+ mi->s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*(mi->s_hdr_data)));
+
+ do {
+ peek = fgetc(mi->input);
+ if (peek == EOF) {
+ fclose(cmitmsg);
+ return error("empty patch: '%s'", patch);
+ }
+ } while (isspace(peek));
+ ungetc(peek, mi->input);
+
+ /* process the email header */
+ while (read_one_header_line(&line, mi->input))
+ check_header(mi, &line, mi->p_hdr_data, 1);
+
+ handle_body(mi, &line);
+ fwrite(mi->log_message.buf, 1, mi->log_message.len, cmitmsg);
+ fclose(cmitmsg);
+ fclose(mi->patchfile);
+
+ handle_info(mi);
+ strbuf_release(&line);
+ return mi->input_error;
+}
+
+static int git_mailinfo_config(const char *var, const char *value, void *mi_)
+{
+ struct mailinfo *mi = mi_;
+
+ if (!starts_with(var, "mailinfo."))
+ return git_default_config(var, value, NULL);
+ if (!strcmp(var, "mailinfo.scissors")) {
+ mi->use_scissors = git_config_bool(var, value);
+ return 0;
+ }
+ /* perhaps others here */
+ return 0;
+}
+
+void setup_mailinfo(struct mailinfo *mi)
+{
+ memset(mi, 0, sizeof(*mi));
+ strbuf_init(&mi->name, 0);
+ strbuf_init(&mi->email, 0);
+ strbuf_init(&mi->charset, 0);
+ strbuf_init(&mi->log_message, 0);
+ strbuf_init(&mi->inbody_header_accum, 0);
+ mi->header_stage = 1;
+ mi->use_inbody_headers = 1;
+ mi->content_top = mi->content;
+ git_config(git_mailinfo_config, mi);
+}
+
+void clear_mailinfo(struct mailinfo *mi)
+{
+ int i;
+
+ strbuf_release(&mi->name);
+ strbuf_release(&mi->email);
+ strbuf_release(&mi->charset);
+ strbuf_release(&mi->inbody_header_accum);
+ free(mi->message_id);
+
+ if (mi->p_hdr_data)
+ for (i = 0; mi->p_hdr_data[i]; i++)
+ strbuf_release(mi->p_hdr_data[i]);
+ free(mi->p_hdr_data);
+ if (mi->s_hdr_data)
+ for (i = 0; mi->s_hdr_data[i]; i++)
+ strbuf_release(mi->s_hdr_data[i]);
+ free(mi->s_hdr_data);
+
+ while (mi->content < mi->content_top) {
+ free(*(mi->content_top));
+ mi->content_top--;
+ }
+
+ strbuf_release(&mi->log_message);
+}
diff --git a/t/t4256/1/mailinfo.c.orig b/t/t4256/1/mailinfo.c.orig
new file mode 100644
index 0000000000..3281a37d51
--- /dev/null
+++ b/t/t4256/1/mailinfo.c.orig
@@ -0,0 +1,1185 @@
+#include "cache.h"
+#include "config.h"
+#include "utf8.h"
+#include "strbuf.h"
+#include "mailinfo.h"
+
+static void cleanup_space(struct strbuf *sb)
+{
+ size_t pos, cnt;
+ for (pos = 0; pos < sb->len; pos++) {
+ if (isspace(sb->buf[pos])) {
+ sb->buf[pos] = ' ';
+ for (cnt = 0; isspace(sb->buf[pos + cnt + 1]); cnt++);
+ strbuf_remove(sb, pos + 1, cnt);
+ }
+ }
+}
+
+static void get_sane_name(struct strbuf *out, struct strbuf *name, struct strbuf *email)
+{
+ struct strbuf *src = name;
+ if (name->len < 3 || 60 < name->len || strchr(name->buf, '@') ||
+ strchr(name->buf, '<') || strchr(name->buf, '>'))
+ src = email;
+ else if (name == out)
+ return;
+ strbuf_reset(out);
+ strbuf_addbuf(out, src);
+}
+
+static void parse_bogus_from(struct mailinfo *mi, const struct strbuf *line)
+{
+ /* John Doe <johndoe> */
+
+ char *bra, *ket;
+ /* This is fallback, so do not bother if we already have an
+ * e-mail address.
+ */
+ if (mi->email.len)
+ return;
+
+ bra = strchr(line->buf, '<');
+ if (!bra)
+ return;
+ ket = strchr(bra, '>');
+ if (!ket)
+ return;
+
+ strbuf_reset(&mi->email);
+ strbuf_add(&mi->email, bra + 1, ket - bra - 1);
+
+ strbuf_reset(&mi->name);
+ strbuf_add(&mi->name, line->buf, bra - line->buf);
+ strbuf_trim(&mi->name);
+ get_sane_name(&mi->name, &mi->name, &mi->email);
+}
+
+static const char *unquote_comment(struct strbuf *outbuf, const char *in)
+{
+ int c;
+ int take_next_literally = 0;
+
+ strbuf_addch(outbuf, '(');
+
+ while ((c = *in++) != 0) {
+ if (take_next_literally == 1) {
+ take_next_literally = 0;
+ } else {
+ switch (c) {
+ case '\\':
+ take_next_literally = 1;
+ continue;
+ case '(':
+ in = unquote_comment(outbuf, in);
+ continue;
+ case ')':
+ strbuf_addch(outbuf, ')');
+ return in;
+ }
+ }
+
+ strbuf_addch(outbuf, c);
+ }
+
+ return in;
+}
+
+static const char *unquote_quoted_string(struct strbuf *outbuf, const char *in)
+{
+ int c;
+ int take_next_literally = 0;
+
+ while ((c = *in++) != 0) {
+ if (take_next_literally == 1) {
+ take_next_literally = 0;
+ } else {
+ switch (c) {
+ case '\\':
+ take_next_literally = 1;
+ continue;
+ case '"':
+ return in;
+ }
+ }
+
+ strbuf_addch(outbuf, c);
+ }
+
+ return in;
+}
+
+static void unquote_quoted_pair(struct strbuf *line)
+{
+ struct strbuf outbuf;
+ const char *in = line->buf;
+ int c;
+
+ strbuf_init(&outbuf, line->len);
+
+ while ((c = *in++) != 0) {
+ switch (c) {
+ case '"':
+ in = unquote_quoted_string(&outbuf, in);
+ continue;
+ case '(':
+ in = unquote_comment(&outbuf, in);
+ continue;
+ }
+
+ strbuf_addch(&outbuf, c);
+ }
+
+ strbuf_swap(&outbuf, line);
+ strbuf_release(&outbuf);
+
+}
+
+static void handle_from(struct mailinfo *mi, const struct strbuf *from)
+{
+ char *at;
+ size_t el;
+ struct strbuf f;
+
+ strbuf_init(&f, from->len);
+ strbuf_addbuf(&f, from);
+
+ unquote_quoted_pair(&f);
+
+ at = strchr(f.buf, '@');
+ if (!at) {
+ parse_bogus_from(mi, from);
+ goto out;
+ }
+
+ /*
+ * If we already have one email, don't take any confusing lines
+ */
+ if (mi->email.len && strchr(at + 1, '@'))
+ goto out;
+
+ /* Pick up the string around '@', possibly delimited with <>
+ * pair; that is the email part.
+ */
+ while (at > f.buf) {
+ char c = at[-1];
+ if (isspace(c))
+ break;
+ if (c == '<') {
+ at[-1] = ' ';
+ break;
+ }
+ at--;
+ }
+ el = strcspn(at, " \n\t\r\v\f>");
+ strbuf_reset(&mi->email);
+ strbuf_add(&mi->email, at, el);
+ strbuf_remove(&f, at - f.buf, el + (at[el] ? 1 : 0));
+
+ /* The remainder is name. It could be
+ *
+ * - "John Doe <john.doe@xz>" (a), or
+ * - "john.doe@xz (John Doe)" (b), or
+ * - "John (zzz) Doe <john.doe@xz> (Comment)" (c)
+ *
+ * but we have removed the email part, so
+ *
+ * - remove extra spaces which could stay after email (case 'c'), and
+ * - trim from both ends, possibly removing the () pair at the end
+ * (cases 'a' and 'b').
+ */
+ cleanup_space(&f);
+ strbuf_trim(&f);
+ if (f.buf[0] == '(' && f.len && f.buf[f.len - 1] == ')') {
+ strbuf_remove(&f, 0, 1);
+ strbuf_setlen(&f, f.len - 1);
+ }
+
+ get_sane_name(&mi->name, &f, &mi->email);
+out:
+ strbuf_release(&f);
+}
+
+static void handle_header(struct strbuf **out, const struct strbuf *line)
+{
+ if (!*out) {
+ *out = xmalloc(sizeof(struct strbuf));
+ strbuf_init(*out, line->len);
+ } else
+ strbuf_reset(*out);
+
+ strbuf_addbuf(*out, line);
+}
+
+/* NOTE NOTE NOTE. We do not claim we do full MIME. We just attempt
+ * to have enough heuristics to grok MIME encoded patches often found
+ * on our mailing lists. For example, we do not even treat header lines
+ * case insensitively.
+ */
+
+static int slurp_attr(const char *line, const char *name, struct strbuf *attr)
+{
+ const char *ends, *ap = strcasestr(line, name);
+ size_t sz;
+
+ strbuf_setlen(attr, 0);
+ if (!ap)
+ return 0;
+ ap += strlen(name);
+ if (*ap == '"') {
+ ap++;
+ ends = "\"";
+ }
+ else
+ ends = "; \t";
+ sz = strcspn(ap, ends);
+ strbuf_add(attr, ap, sz);
+ return 1;
+}
+
+static void handle_content_type(struct mailinfo *mi, struct strbuf *line)
+{
+ struct strbuf *boundary = xmalloc(sizeof(struct strbuf));
+ strbuf_init(boundary, line->len);
+
+ if (slurp_attr(line->buf, "boundary=", boundary)) {
+ strbuf_insert(boundary, 0, "--", 2);
+ if (++mi->content_top >= &mi->content[MAX_BOUNDARIES]) {
+ error("Too many boundaries to handle");
+ mi->input_error = -1;
+ mi->content_top = &mi->content[MAX_BOUNDARIES] - 1;
+ return;
+ }
+ *(mi->content_top) = boundary;
+ boundary = NULL;
+ }
+ slurp_attr(line->buf, "charset=", &mi->charset);
+
+ if (boundary) {
+ strbuf_release(boundary);
+ free(boundary);
+ }
+}
+
+static void handle_content_transfer_encoding(struct mailinfo *mi,
+ const struct strbuf *line)
+{
+ if (strcasestr(line->buf, "base64"))
+ mi->transfer_encoding = TE_BASE64;
+ else if (strcasestr(line->buf, "quoted-printable"))
+ mi->transfer_encoding = TE_QP;
+ else
+ mi->transfer_encoding = TE_DONTCARE;
+}
+
+static int is_multipart_boundary(struct mailinfo *mi, const struct strbuf *line)
+{
+ struct strbuf *content_top = *(mi->content_top);
+
+ return ((content_top->len <= line->len) &&
+ !memcmp(line->buf, content_top->buf, content_top->len));
+}
+
+static void cleanup_subject(struct mailinfo *mi, struct strbuf *subject)
+{
+ size_t at = 0;
+
+ while (at < subject->len) {
+ char *pos;
+ size_t remove;
+
+ switch (subject->buf[at]) {
+ case 'r': case 'R':
+ if (subject->len <= at + 3)
+ break;
+ if ((subject->buf[at + 1] == 'e' ||
+ subject->buf[at + 1] == 'E') &&
+ subject->buf[at + 2] == ':') {
+ strbuf_remove(subject, at, 3);
+ continue;
+ }
+ at++;
+ break;
+ case ' ': case '\t': case ':':
+ strbuf_remove(subject, at, 1);
+ continue;
+ case '[':
+ pos = strchr(subject->buf + at, ']');
+ if (!pos)
+ break;
+ remove = pos - subject->buf + at + 1;
+ if (!mi->keep_non_patch_brackets_in_subject ||
+ (7 <= remove &&
+ memmem(subject->buf + at, remove, "PATCH", 5)))
+ strbuf_remove(subject, at, remove);
+ else {
+ at += remove;
+ /*
+ * If the input had a space after the ], keep
+ * it. We don't bother with finding the end of
+ * the space, since we later normalize it
+ * anyway.
+ */
+ if (isspace(subject->buf[at]))
+ at += 1;
+ }
+ continue;
+ }
+ break;
+ }
+ strbuf_trim(subject);
+}
+
+#define MAX_HDR_PARSED 10
+static const char *header[MAX_HDR_PARSED] = {
+ "From","Subject","Date",
+};
+
+static inline int cmp_header(const struct strbuf *line, const char *hdr)
+{
+ int len = strlen(hdr);
+ return !strncasecmp(line->buf, hdr, len) && line->len > len &&
+ line->buf[len] == ':' && isspace(line->buf[len + 1]);
+}
+
+static int is_format_patch_separator(const char *line, int len)
+{
+ static const char SAMPLE[] =
+ "From e6807f3efca28b30decfecb1732a56c7db1137ee Mon Sep 17 00:00:00 2001\n";
+ const char *cp;
+
+ if (len != strlen(SAMPLE))
+ return 0;
+ if (!skip_prefix(line, "From ", &cp))
+ return 0;
+ if (strspn(cp, "0123456789abcdef") != 40)
+ return 0;
+ cp += 40;
+ return !memcmp(SAMPLE + (cp - line), cp, strlen(SAMPLE) - (cp - line));
+}
+
+static struct strbuf *decode_q_segment(const struct strbuf *q_seg, int rfc2047)
+{
+ const char *in = q_seg->buf;
+ int c;
+ struct strbuf *out = xmalloc(sizeof(struct strbuf));
+ strbuf_init(out, q_seg->len);
+
+ while ((c = *in++) != 0) {
+ if (c == '=') {
+ int ch, d = *in;
+ if (d == '\n' || !d)
+ break; /* drop trailing newline */
+ ch = hex2chr(in);
+ if (ch >= 0) {
+ strbuf_addch(out, ch);
+ in += 2;
+ continue;
+ }
+ /* garbage -- fall through */
+ }
+ if (rfc2047 && c == '_') /* rfc2047 4.2 (2) */
+ c = 0x20;
+ strbuf_addch(out, c);
+ }
+ return out;
+}
+
+static struct strbuf *decode_b_segment(const struct strbuf *b_seg)
+{
+ /* Decode in..ep, possibly in-place to ot */
+ int c, pos = 0, acc = 0;
+ const char *in = b_seg->buf;
+ struct strbuf *out = xmalloc(sizeof(struct strbuf));
+ strbuf_init(out, b_seg->len);
+
+ while ((c = *in++) != 0) {
+ if (c == '+')
+ c = 62;
+ else if (c == '/')
+ c = 63;
+ else if ('A' <= c && c <= 'Z')
+ c -= 'A';
+ else if ('a' <= c && c <= 'z')
+ c -= 'a' - 26;
+ else if ('0' <= c && c <= '9')
+ c -= '0' - 52;
+ else
+ continue; /* garbage */
+ switch (pos++) {
+ case 0:
+ acc = (c << 2);
+ break;
+ case 1:
+ strbuf_addch(out, (acc | (c >> 4)));
+ acc = (c & 15) << 4;
+ break;
+ case 2:
+ strbuf_addch(out, (acc | (c >> 2)));
+ acc = (c & 3) << 6;
+ break;
+ case 3:
+ strbuf_addch(out, (acc | c));
+ acc = pos = 0;
+ break;
+ }
+ }
+ return out;
+}
+
+static int convert_to_utf8(struct mailinfo *mi,
+ struct strbuf *line, const char *charset)
+{
+ char *out;
+
+ if (!mi->metainfo_charset || !charset || !*charset)
+ return 0;
+
+ if (same_encoding(mi->metainfo_charset, charset))
+ return 0;
+ out = reencode_string(line->buf, mi->metainfo_charset, charset);
+ if (!out) {
+ mi->input_error = -1;
+ return error("cannot convert from %s to %s",
+ charset, mi->metainfo_charset);
+ }
+ strbuf_attach(line, out, strlen(out), strlen(out));
+ return 0;
+}
+
+static void decode_header(struct mailinfo *mi, struct strbuf *it)
+{
+ char *in, *ep, *cp;
+ struct strbuf outbuf = STRBUF_INIT, *dec;
+ struct strbuf charset_q = STRBUF_INIT, piecebuf = STRBUF_INIT;
+ int found_error = 1; /* pessimism */
+
+ in = it->buf;
+ while (in - it->buf <= it->len && (ep = strstr(in, "=?")) != NULL) {
+ int encoding;
+ strbuf_reset(&charset_q);
+ strbuf_reset(&piecebuf);
+
+ if (in != ep) {
+ /*
+ * We are about to process an encoded-word
+ * that begins at ep, but there is something
+ * before the encoded word.
+ */
+ char *scan;
+ for (scan = in; scan < ep; scan++)
+ if (!isspace(*scan))
+ break;
+
+ if (scan != ep || in == it->buf) {
+ /*
+ * We should not lose that "something",
+ * unless we have just processed an
+ * encoded-word, and there is only LWS
+ * before the one we are about to process.
+ */
+ strbuf_add(&outbuf, in, ep - in);
+ }
+ }
+ /* E.g.
+ * ep : "=?iso-2022-jp?B?GyR...?= foo"
+ * ep : "=?ISO-8859-1?Q?Foo=FCbar?= baz"
+ */
+ ep += 2;
+
+ if (ep - it->buf >= it->len || !(cp = strchr(ep, '?')))
+ goto release_return;
+
+ if (cp + 3 - it->buf > it->len)
+ goto release_return;
+ strbuf_add(&charset_q, ep, cp - ep);
+
+ encoding = cp[1];
+ if (!encoding || cp[2] != '?')
+ goto release_return;
+ ep = strstr(cp + 3, "?=");
+ if (!ep)
+ goto release_return;
+ strbuf_add(&piecebuf, cp + 3, ep - cp - 3);
+ switch (tolower(encoding)) {
+ default:
+ goto release_return;
+ case 'b':
+ dec = decode_b_segment(&piecebuf);
+ break;
+ case 'q':
+ dec = decode_q_segment(&piecebuf, 1);
+ break;
+ }
+ if (convert_to_utf8(mi, dec, charset_q.buf))
+ goto release_return;
+
+ strbuf_addbuf(&outbuf, dec);
+ strbuf_release(dec);
+ free(dec);
+ in = ep + 2;
+ }
+ strbuf_addstr(&outbuf, in);
+ strbuf_reset(it);
+ strbuf_addbuf(it, &outbuf);
+ found_error = 0;
+release_return:
+ strbuf_release(&outbuf);
+ strbuf_release(&charset_q);
+ strbuf_release(&piecebuf);
+
+ if (found_error)
+ mi->input_error = -1;
+}
+
+static int check_header(struct mailinfo *mi,
+ const struct strbuf *line,
+ struct strbuf *hdr_data[], int overwrite)
+{
+ int i, ret = 0, len;
+ struct strbuf sb = STRBUF_INIT;
+
+ /* search for the interesting parts */
+ for (i = 0; header[i]; i++) {
+ int len = strlen(header[i]);
+ if ((!hdr_data[i] || overwrite) && cmp_header(line, header[i])) {
+ /* Unwrap inline B and Q encoding, and optionally
+ * normalize the meta information to utf8.
+ */
+ strbuf_add(&sb, line->buf + len + 2, line->len - len - 2);
+ decode_header(mi, &sb);
+ handle_header(&hdr_data[i], &sb);
+ ret = 1;
+ goto check_header_out;
+ }
+ }
+
+ /* Content stuff */
+ if (cmp_header(line, "Content-Type")) {
+ len = strlen("Content-Type: ");
+ strbuf_add(&sb, line->buf + len, line->len - len);
+ decode_header(mi, &sb);
+ strbuf_insert(&sb, 0, "Content-Type: ", len);
+ handle_content_type(mi, &sb);
+ ret = 1;
+ goto check_header_out;
+ }
+ if (cmp_header(line, "Content-Transfer-Encoding")) {
+ len = strlen("Content-Transfer-Encoding: ");
+ strbuf_add(&sb, line->buf + len, line->len - len);
+ decode_header(mi, &sb);
+ handle_content_transfer_encoding(mi, &sb);
+ ret = 1;
+ goto check_header_out;
+ }
+ if (cmp_header(line, "Message-Id")) {
+ len = strlen("Message-Id: ");
+ strbuf_add(&sb, line->buf + len, line->len - len);
+ decode_header(mi, &sb);
+ if (mi->add_message_id)
+ mi->message_id = strbuf_detach(&sb, NULL);
+ ret = 1;
+ goto check_header_out;
+ }
+
+check_header_out:
+ strbuf_release(&sb);
+ return ret;
+}
+
+/*
+ * Returns 1 if the given line or any line beginning with the given line is an
+ * in-body header (that is, check_header will succeed when passed
+ * mi->s_hdr_data).
+ */
+static int is_inbody_header(const struct mailinfo *mi,
+ const struct strbuf *line)
+{
+ int i;
+ for (i = 0; header[i]; i++)
+ if (!mi->s_hdr_data[i] && cmp_header(line, header[i]))
+ return 1;
+ return 0;
+}
+
+static void decode_transfer_encoding(struct mailinfo *mi, struct strbuf *line)
+{
+ struct strbuf *ret;
+
+ switch (mi->transfer_encoding) {
+ case TE_QP:
+ ret = decode_q_segment(line, 0);
+ break;
+ case TE_BASE64:
+ ret = decode_b_segment(line);
+ break;
+ case TE_DONTCARE:
+ default:
+ return;
+ }
+ strbuf_reset(line);
+ strbuf_addbuf(line, ret);
+ strbuf_release(ret);
+ free(ret);
+}
+
+static inline int patchbreak(const struct strbuf *line)
+{
+ size_t i;
+
+ /* Beginning of a "diff -" header? */
+ if (starts_with(line->buf, "diff -"))
+ return 1;
+
+ /* CVS "Index: " line? */
+ if (starts_with(line->buf, "Index: "))
+ return 1;
+
+ /*
+ * "--- <filename>" starts patches without headers
+ * "---<sp>*" is a manual separator
+ */
+ if (line->len < 4)
+ return 0;
+
+ if (starts_with(line->buf, "---")) {
+ /* space followed by a filename? */
+ if (line->buf[3] == ' ' && !isspace(line->buf[4]))
+ return 1;
+ /* Just whitespace? */
+ for (i = 3; i < line->len; i++) {
+ unsigned char c = line->buf[i];
+ if (c == '\n')
+ return 1;
+ if (!isspace(c))
+ break;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+static int is_scissors_line(const char *line)
+{
+ const char *c;
+ int scissors = 0, gap = 0;
+ const char *first_nonblank = NULL, *last_nonblank = NULL;
+ int visible, perforation = 0, in_perforation = 0;
+
+ for (c = line; *c; c++) {
+ if (isspace(*c)) {
+ if (in_perforation) {
+ perforation++;
+ gap++;
+ }
+ continue;
+ }
+ last_nonblank = c;
+ if (first_nonblank == NULL)
+ first_nonblank = c;
+ if (*c == '-') {
+ in_perforation = 1;
+ perforation++;
+ continue;
+ }
+ if ((!memcmp(c, ">8", 2) || !memcmp(c, "8<", 2) ||
+ !memcmp(c, ">%", 2) || !memcmp(c, "%<", 2))) {
+ in_perforation = 1;
+ perforation += 2;
+ scissors += 2;
+ c++;
+ continue;
+ }
+ in_perforation = 0;
+ }
+
+ /*
+ * The mark must be at least 8 bytes long (e.g. "-- >8 --").
+ * Even though there can be arbitrary cruft on the same line
+ * (e.g. "cut here"), in order to avoid misidentification, the
+ * perforation must occupy more than a third of the visible
+ * width of the line, and dashes and scissors must occupy more
+ * than half of the perforation.
+ */
+
+ if (first_nonblank && last_nonblank)
+ visible = last_nonblank - first_nonblank + 1;
+ else
+ visible = 0;
+ return (scissors && 8 <= visible &&
+ visible < perforation * 3 &&
+ gap * 2 < perforation);
+}
+
+static void flush_inbody_header_accum(struct mailinfo *mi)
+{
+ if (!mi->inbody_header_accum.len)
+ return;
+ if (!check_header(mi, &mi->inbody_header_accum, mi->s_hdr_data, 0))
+ BUG("inbody_header_accum, if not empty, must always contain a valid in-body header");
+ strbuf_reset(&mi->inbody_header_accum);
+}
+
+static int check_inbody_header(struct mailinfo *mi, const struct strbuf *line)
+{
+ if (mi->inbody_header_accum.len &&
+ (line->buf[0] == ' ' || line->buf[0] == '\t')) {
+ if (mi->use_scissors && is_scissors_line(line->buf)) {
+ /*
+ * This is a scissors line; do not consider this line
+ * as a header continuation line.
+ */
+ flush_inbody_header_accum(mi);
+ return 0;
+ }
+ strbuf_strip_suffix(&mi->inbody_header_accum, "\n");
+ strbuf_addbuf(&mi->inbody_header_accum, line);
+ return 1;
+ }
+
+ flush_inbody_header_accum(mi);
+
+ if (starts_with(line->buf, ">From") && isspace(line->buf[5]))
+ return is_format_patch_separator(line->buf + 1, line->len - 1);
+ if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) {
+ int i;
+ for (i = 0; header[i]; i++)
+ if (!strcmp("Subject", header[i])) {
+ handle_header(&mi->s_hdr_data[i], line);
+ return 1;
+ }
+ return 0;
+ }
+ if (is_inbody_header(mi, line)) {
+ strbuf_addbuf(&mi->inbody_header_accum, line);
+ return 1;
+ }
+ return 0;
+}
+
+static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
+{
+ assert(!mi->filter_stage);
+
+ if (mi->header_stage) {
+ if (!line->len || (line->len == 1 && line->buf[0] == '\n')) {
+ if (mi->inbody_header_accum.len) {
+ flush_inbody_header_accum(mi);
+ mi->header_stage = 0;
+ }
+ return 0;
+ }
+ }
+
+ if (mi->use_inbody_headers && mi->header_stage) {
+ mi->header_stage = check_inbody_header(mi, line);
+ if (mi->header_stage)
+ return 0;
+ } else
+ /* Only trim the first (blank) line of the commit message
+ * when ignoring in-body headers.
+ */
+ mi->header_stage = 0;
+
+ /* normalize the log message to UTF-8. */
+ if (convert_to_utf8(mi, line, mi->charset.buf))
+ return 0; /* mi->input_error already set */
+
+ if (mi->use_scissors && is_scissors_line(line->buf)) {
+ int i;
+
+ strbuf_setlen(&mi->log_message, 0);
+ mi->header_stage = 1;
+
+ /*
+ * We may have already read "secondary headers"; purge
+ * them to give ourselves a clean restart.
+ */
+ for (i = 0; header[i]; i++) {
+ if (mi->s_hdr_data[i])
+ strbuf_release(mi->s_hdr_data[i]);
+ mi->s_hdr_data[i] = NULL;
+ }
+ return 0;
+ }
+
+ if (patchbreak(line)) {
+ if (mi->message_id)
+ strbuf_addf(&mi->log_message,
+ "Message-Id: %s\n", mi->message_id);
+ return 1;
+ }
+
+ strbuf_addbuf(&mi->log_message, line);
+ return 0;
+}
+
+static void handle_patch(struct mailinfo *mi, const struct strbuf *line)
+{
+ fwrite(line->buf, 1, line->len, mi->patchfile);
+ mi->patch_lines++;
+}
+
+static void handle_filter(struct mailinfo *mi, struct strbuf *line)
+{
+ switch (mi->filter_stage) {
+ case 0:
+ if (!handle_commit_msg(mi, line))
+ break;
+ mi->filter_stage++;
+ /* fallthrough */
+ case 1:
+ handle_patch(mi, line);
+ break;
+ }
+}
+
+static int is_rfc2822_header(const struct strbuf *line)
+{
+ /*
+ * The section that defines the loosest possible
+ * field name is "3.6.8 Optional fields".
+ *
+ * optional-field = field-name ":" unstructured CRLF
+ * field-name = 1*ftext
+ * ftext = %d33-57 / %59-126
+ */
+ int ch;
+ char *cp = line->buf;
+
+ /* Count mbox From headers as headers */
+ if (starts_with(cp, "From ") || starts_with(cp, ">From "))
+ return 1;
+
+ while ((ch = *cp++)) {
+ if (ch == ':')
+ return 1;
+ if ((33 <= ch && ch <= 57) ||
+ (59 <= ch && ch <= 126))
+ continue;
+ break;
+ }
+ return 0;
+}
+
+static int read_one_header_line(struct strbuf *line, FILE *in)
+{
+ struct strbuf continuation = STRBUF_INIT;
+
+ /* Get the first part of the line. */
+ if (strbuf_getline_lf(line, in))
+ return 0;
+
+ /*
+ * Is it an empty line or not a valid rfc2822 header?
+ * If so, stop here, and return false ("not a header")
+ */
+ strbuf_rtrim(line);
+ if (!line->len || !is_rfc2822_header(line)) {
+ /* Re-add the newline */
+ strbuf_addch(line, '\n');
+ return 0;
+ }
+
+ /*
+ * Now we need to eat all the continuation lines..
+ * Yuck, 2822 header "folding"
+ */
+ for (;;) {
+ int peek;
+
+ peek = fgetc(in);
+ if (peek == EOF)
+ break;
+ ungetc(peek, in);
+ if (peek != ' ' && peek != '\t')
+ break;
+ if (strbuf_getline_lf(&continuation, in))
+ break;
+ continuation.buf[0] = ' ';
+ strbuf_rtrim(&continuation);
+ strbuf_addbuf(line, &continuation);
+ }
+ strbuf_release(&continuation);
+
+ return 1;
+}
+
+static int find_boundary(struct mailinfo *mi, struct strbuf *line)
+{
+ while (!strbuf_getline_lf(line, mi->input)) {
+ if (*(mi->content_top) && is_multipart_boundary(mi, line))
+ return 1;
+ }
+ return 0;
+}
+
+static int handle_boundary(struct mailinfo *mi, struct strbuf *line)
+{
+ struct strbuf newline = STRBUF_INIT;
+
+ strbuf_addch(&newline, '\n');
+again:
+ if (line->len >= (*(mi->content_top))->len + 2 &&
+ !memcmp(line->buf + (*(mi->content_top))->len, "--", 2)) {
+ /* we hit an end boundary */
+ /* pop the current boundary off the stack */
+ strbuf_release(*(mi->content_top));
+ FREE_AND_NULL(*(mi->content_top));
+
+ /* technically won't happen as is_multipart_boundary()
+ will fail first. But just in case..
+ */
+ if (--mi->content_top < mi->content) {
+ error("Detected mismatched boundaries, can't recover");
+ mi->input_error = -1;
+ mi->content_top = mi->content;
+ strbuf_release(&newline);
+ return 0;
+ }
+ handle_filter(mi, &newline);
+ strbuf_release(&newline);
+ if (mi->input_error)
+ return 0;
+
+ /* skip to the next boundary */
+ if (!find_boundary(mi, line))
+ return 0;
+ goto again;
+ }
+
+ /* set some defaults */
+ mi->transfer_encoding = TE_DONTCARE;
+ strbuf_reset(&mi->charset);
+
+ /* slurp in this section's info */
+ while (read_one_header_line(line, mi->input))
+ check_header(mi, line, mi->p_hdr_data, 0);
+
+ strbuf_release(&newline);
+ /* replenish line */
+ if (strbuf_getline_lf(line, mi->input))
+ return 0;
+ strbuf_addch(line, '\n');
+ return 1;
+}
+
+static void handle_body(struct mailinfo *mi, struct strbuf *line)
+{
+ struct strbuf prev = STRBUF_INIT;
+
+ /* Skip up to the first boundary */
+ if (*(mi->content_top)) {
+ if (!find_boundary(mi, line))
+ goto handle_body_out;
+ }
+
+ do {
+ /* process any boundary lines */
+ if (*(mi->content_top) && is_multipart_boundary(mi, line)) {
+ /* flush any leftover */
+ if (prev.len) {
+ handle_filter(mi, &prev);
+ strbuf_reset(&prev);
+ }
+ if (!handle_boundary(mi, line))
+ goto handle_body_out;
+ }
+
+ /* Unwrap transfer encoding */
+ decode_transfer_encoding(mi, line);
+
+ switch (mi->transfer_encoding) {
+ case TE_BASE64:
+ case TE_QP:
+ {
+ struct strbuf **lines, **it, *sb;
+
+ /* Prepend any previous partial lines */
+ strbuf_insert(line, 0, prev.buf, prev.len);
+ strbuf_reset(&prev);
+
+ /*
+ * This is a decoded line that may contain
+ * multiple new lines. Pass only one chunk
+ * at a time to handle_filter()
+ */
+ lines = strbuf_split(line, '\n');
+ for (it = lines; (sb = *it); it++) {
+ if (*(it + 1) == NULL) /* The last line */
+ if (sb->buf[sb->len - 1] != '\n') {
+ /* Partial line, save it for later. */
+ strbuf_addbuf(&prev, sb);
+ break;
+ }
+ handle_filter(mi, sb);
+ }
+ /*
+ * The partial chunk is saved in "prev" and will be
+ * appended by the next iteration of read_line_with_nul().
+ */
+ strbuf_list_free(lines);
+ break;
+ }
+ default:
+ handle_filter(mi, line);
+ }
+
+ if (mi->input_error)
+ break;
+ } while (!strbuf_getwholeline(line, mi->input, '\n'));
+
+ flush_inbody_header_accum(mi);
+
+handle_body_out:
+ strbuf_release(&prev);
+}
+
+static void output_header_lines(FILE *fout, const char *hdr, const struct strbuf *data)
+{
+ const char *sp = data->buf;
+ while (1) {
+ char *ep = strchr(sp, '\n');
+ int len;
+ if (!ep)
+ len = strlen(sp);
+ else
+ len = ep - sp;
+ fprintf(fout, "%s: %.*s\n", hdr, len, sp);
+ if (!ep)
+ break;
+ sp = ep + 1;
+ }
+}
+
+static void handle_info(struct mailinfo *mi)
+{
+ struct strbuf *hdr;
+ int i;
+
+ for (i = 0; header[i]; i++) {
+ /* only print inbody headers if we output a patch file */
+ if (mi->patch_lines && mi->s_hdr_data[i])
+ hdr = mi->s_hdr_data[i];
+ else if (mi->p_hdr_data[i])
+ hdr = mi->p_hdr_data[i];
+ else
+ continue;
+
+ if (!strcmp(header[i], "Subject")) {
+ if (!mi->keep_subject) {
+ cleanup_subject(mi, hdr);
+ cleanup_space(hdr);
+ }
+ output_header_lines(mi->output, "Subject", hdr);
+ } else if (!strcmp(header[i], "From")) {
+ cleanup_space(hdr);
+ handle_from(mi, hdr);
+ fprintf(mi->output, "Author: %s\n", mi->name.buf);
+ fprintf(mi->output, "Email: %s\n", mi->email.buf);
+ } else {
+ cleanup_space(hdr);
+ fprintf(mi->output, "%s: %s\n", header[i], hdr->buf);
+ }
+ }
+ fprintf(mi->output, "\n");
+}
+
+int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
+{
+ FILE *cmitmsg;
+ int peek;
+ struct strbuf line = STRBUF_INIT;
+
+ cmitmsg = fopen(msg, "w");
+ if (!cmitmsg) {
+ perror(msg);
+ return -1;
+ }
+ mi->patchfile = fopen(patch, "w");
+ if (!mi->patchfile) {
+ perror(patch);
+ fclose(cmitmsg);
+ return -1;
+ }
+
+ mi->p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*(mi->p_hdr_data)));
+ mi->s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*(mi->s_hdr_data)));
+
+ do {
+ peek = fgetc(mi->input);
+ if (peek == EOF) {
+ fclose(cmitmsg);
+ return error("empty patch: '%s'", patch);
+ }
+ } while (isspace(peek));
+ ungetc(peek, mi->input);
+
+ /* process the email header */
+ while (read_one_header_line(&line, mi->input))
+ check_header(mi, &line, mi->p_hdr_data, 1);
+
+ handle_body(mi, &line);
+ fwrite(mi->log_message.buf, 1, mi->log_message.len, cmitmsg);
+ fclose(cmitmsg);
+ fclose(mi->patchfile);
+
+ handle_info(mi);
+ strbuf_release(&line);
+ return mi->input_error;
+}
+
+static int git_mailinfo_config(const char *var, const char *value, void *mi_)
+{
+ struct mailinfo *mi = mi_;
+
+ if (!starts_with(var, "mailinfo."))
+ return git_default_config(var, value, NULL);
+ if (!strcmp(var, "mailinfo.scissors")) {
+ mi->use_scissors = git_config_bool(var, value);
+ return 0;
+ }
+ /* perhaps others here */
+ return 0;
+}
+
+void setup_mailinfo(struct mailinfo *mi)
+{
+ memset(mi, 0, sizeof(*mi));
+ strbuf_init(&mi->name, 0);
+ strbuf_init(&mi->email, 0);
+ strbuf_init(&mi->charset, 0);
+ strbuf_init(&mi->log_message, 0);
+ strbuf_init(&mi->inbody_header_accum, 0);
+ mi->header_stage = 1;
+ mi->use_inbody_headers = 1;
+ mi->content_top = mi->content;
+ git_config(git_mailinfo_config, mi);
+}
+
+void clear_mailinfo(struct mailinfo *mi)
+{
+ int i;
+
+ strbuf_release(&mi->name);
+ strbuf_release(&mi->email);
+ strbuf_release(&mi->charset);
+ strbuf_release(&mi->inbody_header_accum);
+ free(mi->message_id);
+
+ if (mi->p_hdr_data)
+ for (i = 0; mi->p_hdr_data[i]; i++)
+ strbuf_release(mi->p_hdr_data[i]);
+ free(mi->p_hdr_data);
+ if (mi->s_hdr_data)
+ for (i = 0; mi->s_hdr_data[i]; i++)
+ strbuf_release(mi->s_hdr_data[i]);
+ free(mi->s_hdr_data);
+
+ while (mi->content < mi->content_top) {
+ free(*(mi->content_top));
+ mi->content_top--;
+ }
+
+ strbuf_release(&mi->log_message);
+}
diff --git a/t/t4256/1/patch b/t/t4256/1/patch
new file mode 100644
index 0000000000..bd0d8b0251
--- /dev/null
+++ b/t/t4256/1/patch
@@ -0,0 +1,129 @@
+From: A <author@example.com>
+Subject: [PATCH] mailinfo: support format=flowed
+Message-ID: <aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa@example.com>
+Date: Sat, 25 Aug 2018 22:04:50 +0200
+User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:60.0) Gecko/20100101
+ Thunderbird/60.0
+MIME-Version: 1.0
+Content-Type: text/plain; charset=utf-8; format=flowed
+Content-Language: en-US
+Content-Transfer-Encoding: 7bit
+
+---
+ mailinfo.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 62 insertions(+), 2 deletions(-)
+
+diff --git a/mailinfo.c b/mailinfo.c
+index 3281a37d51..b395adbdf2 100644
+--- a/mailinfo.c
++++ b/mailinfo.c
+@@ -237,11 +237,22 @@ static int slurp_attr(const char *line, const char
+*name, struct strbuf *attr)
+ return 1;
+ }
+
++static int has_attr_value(const char *line, const char *name, const
+char *value)
++{
++ struct strbuf sb = STRBUF_INIT;
++ int rc = slurp_attr(line, name, &sb) && !strcasecmp(sb.buf, value);
++ strbuf_release(&sb);
++ return rc;
++}
++
+ static void handle_content_type(struct mailinfo *mi, struct strbuf *line)
+ {
+ struct strbuf *boundary = xmalloc(sizeof(struct strbuf));
+ strbuf_init(boundary, line->len);
+
++ mi->format_flowed = has_attr_value(line->buf, "format=", "flowed");
++ mi->delsp = has_attr_value(line->buf, "delsp=", "yes");
++
+ if (slurp_attr(line->buf, "boundary=", boundary)) {
+ strbuf_insert(boundary, 0, "--", 2);
+ if (++mi->content_top >= &mi->content[MAX_BOUNDARIES]) {
+@@ -964,6 +975,52 @@ static int handle_boundary(struct mailinfo *mi,
+struct strbuf *line)
+ return 1;
+ }
+
++static void handle_filter_flowed(struct mailinfo *mi, struct strbuf *line,
++ struct strbuf *prev)
++{
++ size_t len = line->len;
++ const char *rest;
++
++ if (!mi->format_flowed) {
++ handle_filter(mi, line);
++ return;
++ }
++
++ if (line->buf[len - 1] == '\n') {
++ len--;
++ if (len && line->buf[len - 1] == '\r')
++ len--;
++ }
++
++ /* Keep signature separator as-is. */
++ if (skip_prefix(line->buf, "-- ", &rest) && rest - line->buf == len) {
++ if (prev->len) {
++ handle_filter(mi, prev);
++ strbuf_reset(prev);
++ }
++ handle_filter(mi, line);
++ return;
++ }
++
++ /* Unstuff space-stuffed line. */
++ if (len && line->buf[0] == ' ') {
++ strbuf_remove(line, 0, 1);
++ len--;
++ }
++
++ /* Save flowed line for later, but without the soft line break. */
++ if (len && line->buf[len - 1] == ' ') {
++ strbuf_add(prev, line->buf, len - !!mi->delsp);
++ return;
++ }
++
++ /* Prepend any previous partial lines */
++ strbuf_insert(line, 0, prev->buf, prev->len);
++ strbuf_reset(prev);
++
++ handle_filter(mi, line);
++}
++
+ static void handle_body(struct mailinfo *mi, struct strbuf *line)
+ {
+ struct strbuf prev = STRBUF_INIT;
+@@ -1012,7 +1069,7 @@ static void handle_body(struct mailinfo *mi,
+struct strbuf *line)
+ strbuf_addbuf(&prev, sb);
+ break;
+ }
+- handle_filter(mi, sb);
++ handle_filter_flowed(mi, sb, &prev);
+ }
+ /*
+ * The partial chunk is saved in "prev" and will be
+@@ -1022,13 +1079,16 @@ static void handle_body(struct mailinfo *mi,
+struct strbuf *line)
+ break;
+ }
+ default:
+- handle_filter(mi, line);
++ handle_filter_flowed(mi, line, &prev);
+ }
+
+ if (mi->input_error)
+ break;
+ } while (!strbuf_getwholeline(line, mi->input, '\n'));
+
++ if (prev.len)
++ handle_filter(mi, &prev);
++
+ flush_inbody_header_accum(mi);
+
+ handle_body_out:
+--
+2.18.0
diff --git a/t/t4300-merge-tree.sh b/t/t4300-merge-tree.sh
index 9015e47654..d87cc7d9ef 100755
--- a/t/t4300-merge-tree.sh
+++ b/t/t4300-merge-tree.sh
@@ -25,25 +25,19 @@ EXPECTED
'
test_expect_success 'file add !A, B' '
- cat >expected <<\EXPECTED &&
-EXPECTED
-
git reset --hard initial &&
test_commit "add-not-a-b" "ONE" "AAA" &&
git merge-tree initial add-not-a-b initial >actual &&
- test_cmp expected actual
+ test_must_be_empty actual
'
test_expect_success 'file add A, B (same)' '
- cat >expected <<\EXPECTED &&
-EXPECTED
-
git reset --hard initial &&
test_commit "add-a-b-same-A" "ONE" "AAA" &&
git reset --hard initial &&
test_commit "add-a-b-same-B" "ONE" "AAA" &&
git merge-tree initial add-a-b-same-A add-a-b-same-B >actual &&
- test_cmp expected actual
+ test_must_be_empty actual
'
test_expect_success 'file add A, B (different)' '
@@ -68,13 +62,10 @@ EXPECTED
'
test_expect_success 'file change A, !B' '
- cat >expected <<\EXPECTED &&
-EXPECTED
-
git reset --hard initial &&
test_commit "change-a-not-b" "initial-file" "BBB" &&
git merge-tree initial change-a-not-b initial >actual &&
- test_cmp expected actual
+ test_must_be_empty actual
'
test_expect_success 'file change !A, B' '
@@ -94,15 +85,12 @@ EXPECTED
'
test_expect_success 'file change A, B (same)' '
- cat >expected <<\EXPECTED &&
-EXPECTED
-
git reset --hard initial &&
test_commit "change-a-b-same-A" "initial-file" "AAA" &&
git reset --hard initial &&
test_commit "change-a-b-same-B" "initial-file" "AAA" &&
git merge-tree initial change-a-b-same-A change-a-b-same-B >actual &&
- test_cmp expected actual
+ test_must_be_empty actual
'
test_expect_success 'file change A, B (different)' '
@@ -175,16 +163,13 @@ AAA" &&
'
test_expect_success 'file remove A, !B' '
- cat >expected <<\EXPECTED &&
-EXPECTED
-
git reset --hard initial &&
test_commit "rm-a-not-b-base" "ONE" "AAA" &&
git rm ONE &&
git commit -m "rm-a-not-b" &&
git tag "rm-a-not-b" &&
git merge-tree rm-a-not-b-base rm-a-not-b rm-a-not-b-base >actual &&
- test_cmp expected actual
+ test_must_be_empty actual
'
test_expect_success 'file remove !A, B' '
@@ -206,16 +191,13 @@ EXPECTED
'
test_expect_success 'file remove A, B (same)' '
- cat >expected <<\EXPECTED &&
-EXPECTED
-
git reset --hard initial &&
test_commit "rm-a-b-base" "ONE" "AAA" &&
git rm ONE &&
git commit -m "rm-a-b" &&
git tag "rm-a-b" &&
git merge-tree rm-a-b-base rm-a-b rm-a-b >actual &&
- test_cmp expected actual
+ test_must_be_empty actual
'
test_expect_success 'file change A, remove B' '
@@ -260,13 +242,11 @@ EXPECTED
'
test_expect_success 'tree add A, B (same)' '
- cat >expect <<-\EOF &&
- EOF
git reset --hard initial &&
mkdir sub &&
test_commit "add sub/file" "sub/file" "file" add-tree-A &&
git merge-tree initial add-tree-A add-tree-A >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'tree add A, B (different)' '
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
index 2336d09dcc..6c620cd540 100755
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
@@ -191,7 +191,7 @@ test_expect_success 'survive missing objects/pack directory' '
mkdir missing-pack &&
cd missing-pack &&
git init &&
- GOP=.git/objects/pack
+ GOP=.git/objects/pack &&
rm -fr $GOP &&
git index-pack --stdin --keep=test <../test-3-${packname_3}.pack &&
test -f $GOP/pack-${packname_3}.pack &&
diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh
index bb9b8bb309..91d51b35f9 100755
--- a/t/t5302-pack-index.sh
+++ b/t/t5302-pack-index.sh
@@ -237,7 +237,7 @@ test_expect_success 'running index-pack in the object store' '
rm -f .git/objects/pack/* &&
cp test-1-${pack1}.pack .git/objects/pack/pack-${pack1}.pack &&
(
- cd .git/objects/pack
+ cd .git/objects/pack &&
git index-pack pack-${pack1}.pack
) &&
test -f .git/objects/pack/pack-${pack1}.idx
diff --git a/t/t5303-pack-corruption-resilience.sh b/t/t5303-pack-corruption-resilience.sh
index 3634e258f8..41e6dc4dcf 100755
--- a/t/t5303-pack-corruption-resilience.sh
+++ b/t/t5303-pack-corruption-resilience.sh
@@ -311,4 +311,93 @@ test_expect_success \
test_must_fail git cat-file blob $blob_2 > /dev/null &&
test_must_fail git cat-file blob $blob_3 > /dev/null'
+# \0 - empty base
+# \1 - one byte in result
+# \1 - one literal byte (X)
+test_expect_success \
+ 'apply good minimal delta' \
+ 'printf "\0\1\1X" > minimal_delta &&
+ test-tool delta -p /dev/null minimal_delta /dev/null'
+
+# \0 - empty base
+# \1 - 1 byte in result
+# \2 - two literal bytes (one too many)
+test_expect_success \
+ 'apply delta with too many literal bytes' \
+ 'printf "\0\1\2XX" > too_big_literal &&
+ test_must_fail test-tool delta -p /dev/null too_big_literal /dev/null'
+
+# \4 - four bytes in base
+# \1 - one byte in result
+# \221 - copy, one byte offset, one byte size
+# \0 - copy from offset 0
+# \2 - copy two bytes (one too many)
+test_expect_success \
+ 'apply delta with too many copied bytes' \
+ 'printf "\4\1\221\0\2" > too_big_copy &&
+ printf base >base &&
+ test_must_fail test-tool delta -p base too_big_copy /dev/null'
+
+# \0 - empty base
+# \2 - two bytes in result
+# \2 - two literal bytes (we are short one)
+test_expect_success \
+ 'apply delta with too few literal bytes' \
+ 'printf "\0\2\2X" > truncated_delta &&
+ test_must_fail test-tool delta -p /dev/null truncated_delta /dev/null'
+
+# \0 - empty base
+# \1 - one byte in result
+# \221 - copy, one byte offset, one byte size
+# \0 - copy from offset 0
+# \1 - copy one byte (we are short one)
+test_expect_success \
+ 'apply delta with too few bytes in base' \
+ 'printf "\0\1\221\0\1" > truncated_base &&
+ test_must_fail test-tool delta -p /dev/null truncated_base /dev/null'
+
+# \4 - four bytes in base
+# \2 - two bytes in result
+# \1 - one literal byte (X)
+# \221 - copy, one byte offset, one byte size
+# (offset/size missing)
+#
+# Note that the literal byte is necessary to get past the uninteresting minimum
+# delta size check.
+test_expect_success \
+ 'apply delta with truncated copy parameters' \
+ 'printf "\4\2\1X\221" > truncated_copy_delta &&
+ printf base >base &&
+ test_must_fail test-tool delta -p base truncated_copy_delta /dev/null'
+
+# \0 - empty base
+# \1 - one byte in result
+# \1 - one literal byte (X)
+# \1 - trailing garbage command
+test_expect_success \
+ 'apply delta with trailing garbage literal' \
+ 'printf "\0\1\1X\1" > tail_garbage_literal &&
+ test_must_fail test-tool delta -p /dev/null tail_garbage_literal /dev/null'
+
+# \4 - four bytes in base
+# \1 - one byte in result
+# \1 - one literal byte (X)
+# \221 - copy, one byte offset, one byte size
+# \0 - copy from offset 0
+# \1 - copy 1 byte
+test_expect_success \
+ 'apply delta with trailing garbage copy' \
+ 'printf "\4\1\1X\221\0\1" > tail_garbage_copy &&
+ printf base >base &&
+ test_must_fail test-tool delta -p /dev/null tail_garbage_copy /dev/null'
+
+# \0 - empty base
+# \1 - one byte in result
+# \1 - one literal byte (X)
+# \0 - bogus opcode
+test_expect_success \
+ 'apply delta with trailing garbage opcode' \
+ 'printf "\0\1\1X\0" > tail_garbage_opcode &&
+ test_must_fail test-tool delta -p /dev/null tail_garbage_opcode /dev/null'
+
test_done
diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh
index f20f03c103..270da21ac3 100755
--- a/t/t5304-prune.sh
+++ b/t/t5304-prune.sh
@@ -112,8 +112,7 @@ test_expect_success 'prune: do not prune detached HEAD with no reflog' '
# (should be removed and disabled by previous test)
test_path_is_missing .git/logs &&
git prune -n >prune_actual &&
- : >prune_expected &&
- test_cmp prune_actual prune_expected
+ test_must_be_empty prune_actual
'
diff --git a/t/t5307-pack-missing-commit.sh b/t/t5307-pack-missing-commit.sh
index ae52a1882d..dacb440b27 100755
--- a/t/t5307-pack-missing-commit.sh
+++ b/t/t5307-pack-missing-commit.sh
@@ -24,11 +24,11 @@ test_expect_success 'check corruption' '
'
test_expect_success 'rev-list notices corruption (1)' '
- test_must_fail git rev-list HEAD
+ test_must_fail env GIT_TEST_COMMIT_GRAPH=0 git rev-list HEAD
'
test_expect_success 'rev-list notices corruption (2)' '
- test_must_fail git rev-list --objects HEAD
+ test_must_fail env GIT_TEST_COMMIT_GRAPH=0 git rev-list --objects HEAD
'
test_expect_success 'pack-objects notices corruption' '
diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh
index 2d22a17c4a..1be3459c5b 100755
--- a/t/t5310-pack-bitmaps.sh
+++ b/t/t5310-pack-bitmaps.sh
@@ -9,7 +9,8 @@ objpath () {
# show objects present in pack ($1 should be associated *.idx)
list_packed_objects () {
- git show-index <"$1" | cut -d' ' -f2
+ git show-index <"$1" >object-list &&
+ cut -d' ' -f2 object-list
}
# has_any pattern-file content-file
@@ -204,8 +205,8 @@ test_expect_success 'pack-objects to file can use bitmap' '
# 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 &&
+ list_packed_objects packa-$packasha1.idx >packa.objects &&
+ list_packed_objects packb-$packbsha1.idx >packb.objects &&
test_cmp packa.objects packb.objects
'
@@ -309,9 +310,8 @@ test_expect_success 'pack reuse respects --honor-pack-keep' '
done &&
reusable_pack --honor-pack-keep >empty.pack &&
git index-pack empty.pack &&
- >expect &&
git show-index <empty.idx >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'pack reuse respects --local' '
@@ -319,17 +319,15 @@ test_expect_success 'pack reuse respects --local' '
test_when_finished "mv alt.git/objects/pack/* .git/objects/pack/" &&
reusable_pack --local >empty.pack &&
git index-pack empty.pack &&
- >expect &&
git show-index <empty.idx >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'pack reuse respects --incremental' '
reusable_pack --incremental >empty.pack &&
git index-pack empty.pack &&
- >expect &&
git show-index <empty.idx >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'truncated bitmap fails gracefully' '
@@ -337,11 +335,104 @@ test_expect_success 'truncated bitmap fails gracefully' '
git rev-list --use-bitmap-index --count --all >expect &&
bitmap=$(ls .git/objects/pack/*.bitmap) &&
test_when_finished "rm -f $bitmap" &&
- head -c 512 <$bitmap >$bitmap.tmp &&
+ 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 corrupt stderr
'
+# have_delta <obj> <expected_base>
+#
+# Note that because this relies on cat-file, it might find _any_ copy of an
+# object in the repository. The caller is responsible for making sure
+# there's only one (e.g., via "repack -ad", or having just fetched a copy).
+have_delta () {
+ echo $2 >expect &&
+ echo $1 | git cat-file --batch-check="%(deltabase)" >actual &&
+ test_cmp expect actual
+}
+
+# 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_done
diff --git a/t/t5313-pack-bounds-checks.sh b/t/t5313-pack-bounds-checks.sh
index 4fe4ad9d61..f1708d415e 100755
--- a/t/t5313-pack-bounds-checks.sh
+++ b/t/t5313-pack-bounds-checks.sh
@@ -90,9 +90,8 @@ test_expect_success 'matched bogus object count' '
# Unlike above, we should notice early that the .idx is totally
# bogus, and not even enumerate its contents.
- >expect &&
git cat-file --batch-all-objects --batch-check >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
# But as before, we can do the same object-access checks.
test_must_fail git cat-file blob $object &&
diff --git a/t/t5314-pack-cycle-detection.sh b/t/t5314-pack-cycle-detection.sh
index f31995d3d2..e525466de0 100755
--- a/t/t5314-pack-cycle-detection.sh
+++ b/t/t5314-pack-cycle-detection.sh
@@ -98,9 +98,8 @@ test_expect_success 'repack' '
# We first want to check that we do not have any internal errors,
# and also that we do not hit the last-ditch cycle-breaking code
# in write_object(), which will issue a warning to stderr.
- >expect &&
git repack -ad 2>stderr &&
- test_cmp expect stderr &&
+ test_must_be_empty stderr &&
# And then double-check that the resulting pack is usable (i.e.,
# we did not fail to notice any cycles). We know we are accessing
diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh
index 77d85aefe7..0c500f7ca2 100755
--- a/t/t5318-commit-graph.sh
+++ b/t/t5318-commit-graph.sh
@@ -11,6 +11,11 @@ test_expect_success 'setup full repo' '
objdir=".git/objects"
'
+test_expect_success 'verify graph with no graph file' '
+ cd "$TRASH_DIRECTORY/full" &&
+ git commit-graph verify
+'
+
test_expect_success 'write graph with no packs' '
cd "$TRASH_DIRECTORY/full" &&
git commit-graph write --object-dir . &&
@@ -28,8 +33,8 @@ test_expect_success 'create commits and repack' '
'
graph_git_two_modes() {
- git -c core.graph=true $1 >output
- git -c core.graph=false $1 >expect
+ git -c core.commitGraph=true $1 >output
+ git -c core.commitGraph=false $1 >expect
test_cmp output expect
}
@@ -129,7 +134,7 @@ test_expect_success 'Add one more commit' '
git branch commits/8 &&
ls $objdir/pack | grep idx >existing-idx &&
git repack &&
- ls $objdir/pack| grep idx | grep -v --file=existing-idx >new-idx
+ ls $objdir/pack| grep idx | grep -v -f existing-idx >new-idx
'
# Current graph structure:
@@ -200,6 +205,16 @@ test_expect_success 'build graph from commits with append' '
graph_git_behavior 'append graph, commit 8 vs merge 1' full commits/8 merge/1
graph_git_behavior 'append graph, commit 8 vs merge 2' full commits/8 merge/2
+test_expect_success 'build graph using --reachable' '
+ cd "$TRASH_DIRECTORY/full" &&
+ git commit-graph write --reachable &&
+ test_path_is_file $objdir/info/commit-graph &&
+ graph_read_expect "11" "large_edges"
+'
+
+graph_git_behavior 'append graph, commit 8 vs merge 1' full commits/8 merge/1
+graph_git_behavior 'append graph, commit 8 vs merge 2' full commits/8 merge/2
+
test_expect_success 'setup bare repo' '
cd "$TRASH_DIRECTORY" &&
git clone --bare --no-local full bare &&
@@ -230,4 +245,227 @@ test_expect_success 'perform fast-forward merge in full repo' '
test_cmp expect output
'
+test_expect_success 'check that gc computes commit-graph' '
+ cd "$TRASH_DIRECTORY/full" &&
+ git commit --allow-empty -m "blank" &&
+ git commit-graph write --reachable &&
+ cp $objdir/info/commit-graph commit-graph-before-gc &&
+ git reset --hard HEAD~1 &&
+ git config gc.writeCommitGraph true &&
+ git gc &&
+ cp $objdir/info/commit-graph commit-graph-after-gc &&
+ ! test_cmp_bin commit-graph-before-gc commit-graph-after-gc &&
+ git commit-graph write --reachable &&
+ test_cmp_bin commit-graph-after-gc $objdir/info/commit-graph
+'
+
+# the verify tests below expect the commit-graph to contain
+# exactly the commits reachable from the commits/8 branch.
+# If the file changes the set of commits in the list, then the
+# offsets into the binary file will result in different edits
+# and the tests will likely break.
+
+test_expect_success 'git commit-graph verify' '
+ cd "$TRASH_DIRECTORY/full" &&
+ git rev-parse commits/8 | git commit-graph write --stdin-commits &&
+ git commit-graph verify >output
+'
+
+NUM_COMMITS=9
+NUM_OCTOPUS_EDGES=2
+HASH_LEN=20
+GRAPH_BYTE_VERSION=4
+GRAPH_BYTE_HASH=5
+GRAPH_BYTE_CHUNK_COUNT=6
+GRAPH_CHUNK_LOOKUP_OFFSET=8
+GRAPH_CHUNK_LOOKUP_WIDTH=12
+GRAPH_CHUNK_LOOKUP_ROWS=5
+GRAPH_BYTE_OID_FANOUT_ID=$GRAPH_CHUNK_LOOKUP_OFFSET
+GRAPH_BYTE_OID_LOOKUP_ID=$(($GRAPH_CHUNK_LOOKUP_OFFSET + \
+ 1 * $GRAPH_CHUNK_LOOKUP_WIDTH))
+GRAPH_BYTE_COMMIT_DATA_ID=$(($GRAPH_CHUNK_LOOKUP_OFFSET + \
+ 2 * $GRAPH_CHUNK_LOOKUP_WIDTH))
+GRAPH_FANOUT_OFFSET=$(($GRAPH_CHUNK_LOOKUP_OFFSET + \
+ $GRAPH_CHUNK_LOOKUP_WIDTH * $GRAPH_CHUNK_LOOKUP_ROWS))
+GRAPH_BYTE_FANOUT1=$(($GRAPH_FANOUT_OFFSET + 4 * 4))
+GRAPH_BYTE_FANOUT2=$(($GRAPH_FANOUT_OFFSET + 4 * 255))
+GRAPH_OID_LOOKUP_OFFSET=$(($GRAPH_FANOUT_OFFSET + 4 * 256))
+GRAPH_BYTE_OID_LOOKUP_ORDER=$(($GRAPH_OID_LOOKUP_OFFSET + $HASH_LEN * 8))
+GRAPH_BYTE_OID_LOOKUP_MISSING=$(($GRAPH_OID_LOOKUP_OFFSET + $HASH_LEN * 4 + 10))
+GRAPH_COMMIT_DATA_OFFSET=$(($GRAPH_OID_LOOKUP_OFFSET + $HASH_LEN * $NUM_COMMITS))
+GRAPH_BYTE_COMMIT_TREE=$GRAPH_COMMIT_DATA_OFFSET
+GRAPH_BYTE_COMMIT_PARENT=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN))
+GRAPH_BYTE_COMMIT_EXTRA_PARENT=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 4))
+GRAPH_BYTE_COMMIT_WRONG_PARENT=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 3))
+GRAPH_BYTE_COMMIT_GENERATION=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 11))
+GRAPH_BYTE_COMMIT_DATE=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 12))
+GRAPH_COMMIT_DATA_WIDTH=$(($HASH_LEN + 16))
+GRAPH_OCTOPUS_DATA_OFFSET=$(($GRAPH_COMMIT_DATA_OFFSET + \
+ $GRAPH_COMMIT_DATA_WIDTH * $NUM_COMMITS))
+GRAPH_BYTE_OCTOPUS=$(($GRAPH_OCTOPUS_DATA_OFFSET + 4))
+GRAPH_BYTE_FOOTER=$(($GRAPH_OCTOPUS_DATA_OFFSET + 4 * $NUM_OCTOPUS_EDGES))
+
+# usage: corrupt_graph_and_verify <position> <data> <string>
+# Manipulates the commit-graph file at the position
+# by inserting the data, then runs 'git commit-graph verify'
+# and places the output in the file 'err'. Test 'err' for
+# the given string.
+corrupt_graph_and_verify() {
+ pos=$1
+ data="${2:-\0}"
+ grepstr=$3
+ cd "$TRASH_DIRECTORY/full" &&
+ test_when_finished mv commit-graph-backup $objdir/info/commit-graph &&
+ cp $objdir/info/commit-graph commit-graph-backup &&
+ printf "$data" | dd of="$objdir/info/commit-graph" bs=1 seek="$pos" conv=notrunc &&
+ test_must_fail git commit-graph verify 2>test_err &&
+ grep -v "^+" test_err >err
+ test_i18ngrep "$grepstr" err
+}
+
+test_expect_success 'detect bad signature' '
+ corrupt_graph_and_verify 0 "\0" \
+ "graph signature"
+'
+
+test_expect_success 'detect bad version' '
+ corrupt_graph_and_verify $GRAPH_BYTE_VERSION "\02" \
+ "graph version"
+'
+
+test_expect_success 'detect bad hash version' '
+ corrupt_graph_and_verify $GRAPH_BYTE_HASH "\02" \
+ "hash version"
+'
+
+test_expect_success 'detect low chunk count' '
+ corrupt_graph_and_verify $GRAPH_BYTE_CHUNK_COUNT "\02" \
+ "missing the .* chunk"
+'
+
+test_expect_success 'detect missing OID fanout chunk' '
+ corrupt_graph_and_verify $GRAPH_BYTE_OID_FANOUT_ID "\0" \
+ "missing the OID Fanout chunk"
+'
+
+test_expect_success 'detect missing OID lookup chunk' '
+ corrupt_graph_and_verify $GRAPH_BYTE_OID_LOOKUP_ID "\0" \
+ "missing the OID Lookup chunk"
+'
+
+test_expect_success 'detect missing commit data chunk' '
+ corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_DATA_ID "\0" \
+ "missing the Commit Data chunk"
+'
+
+test_expect_success 'detect incorrect fanout' '
+ corrupt_graph_and_verify $GRAPH_BYTE_FANOUT1 "\01" \
+ "fanout value"
+'
+
+test_expect_success 'detect incorrect fanout final value' '
+ corrupt_graph_and_verify $GRAPH_BYTE_FANOUT2 "\01" \
+ "fanout value"
+'
+
+test_expect_success 'detect incorrect OID order' '
+ corrupt_graph_and_verify $GRAPH_BYTE_OID_LOOKUP_ORDER "\01" \
+ "incorrect OID order"
+'
+
+test_expect_success 'detect OID not in object database' '
+ corrupt_graph_and_verify $GRAPH_BYTE_OID_LOOKUP_MISSING "\01" \
+ "from object database"
+'
+
+test_expect_success 'detect incorrect tree OID' '
+ corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_TREE "\01" \
+ "root tree OID for commit"
+'
+
+test_expect_success 'detect incorrect parent int-id' '
+ corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_PARENT "\01" \
+ "invalid parent"
+'
+
+test_expect_success 'detect extra parent int-id' '
+ corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_EXTRA_PARENT "\00" \
+ "is too long"
+'
+
+test_expect_success 'detect wrong parent' '
+ corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_WRONG_PARENT "\01" \
+ "commit-graph parent for"
+'
+
+test_expect_success 'detect incorrect generation number' '
+ corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_GENERATION "\070" \
+ "generation for commit"
+'
+
+test_expect_success 'detect incorrect generation number' '
+ corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_GENERATION "\01" \
+ "non-zero generation number"
+'
+
+test_expect_success 'detect incorrect commit date' '
+ corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_DATE "\01" \
+ "commit date"
+'
+
+test_expect_success 'detect incorrect parent for octopus merge' '
+ corrupt_graph_and_verify $GRAPH_BYTE_OCTOPUS "\01" \
+ "invalid parent"
+'
+
+test_expect_success 'detect invalid checksum hash' '
+ corrupt_graph_and_verify $GRAPH_BYTE_FOOTER "\00" \
+ "incorrect checksum"
+'
+
+test_expect_success 'git fsck (checks commit-graph)' '
+ cd "$TRASH_DIRECTORY/full" &&
+ git fsck &&
+ corrupt_graph_and_verify $GRAPH_BYTE_FOOTER "\00" \
+ "incorrect checksum" &&
+ test_must_fail git fsck
+'
+
+test_expect_success 'setup non-the_repository tests' '
+ rm -rf repo &&
+ git init repo &&
+ test_commit -C repo one &&
+ test_commit -C repo two &&
+ git -C repo config core.commitGraph true &&
+ git -C repo rev-parse two | \
+ git -C repo commit-graph write --stdin-commits
+'
+
+test_expect_success 'parse_commit_in_graph works for non-the_repository' '
+ test-tool repository parse_commit_in_graph \
+ repo/.git repo "$(git -C repo rev-parse two)" >actual &&
+ {
+ git -C repo log --pretty=format:"%ct " -1 &&
+ git -C repo rev-parse one
+ } >expect &&
+ test_cmp expect actual &&
+
+ test-tool repository parse_commit_in_graph \
+ repo/.git repo "$(git -C repo rev-parse one)" >actual &&
+ git -C repo log --pretty="%ct" -1 one >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'get_commit_tree_in_graph works for non-the_repository' '
+ test-tool repository get_commit_tree_in_graph \
+ repo/.git repo "$(git -C repo rev-parse two)" >actual &&
+ git -C repo rev-parse two^{tree} >expect &&
+ test_cmp expect actual &&
+
+ test-tool repository get_commit_tree_in_graph \
+ repo/.git repo "$(git -C repo rev-parse one)" >actual &&
+ git -C repo rev-parse one^{tree} >expect &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh
new file mode 100755
index 0000000000..6f56b38674
--- /dev/null
+++ b/t/t5319-multi-pack-index.sh
@@ -0,0 +1,217 @@
+#!/bin/sh
+
+test_description='multi-pack-indexes'
+. ./test-lib.sh
+
+objdir=.git/objects
+
+midx_read_expect () {
+ NUM_PACKS=$1
+ NUM_OBJECTS=$2
+ NUM_CHUNKS=$3
+ OBJECT_DIR=$4
+ EXTRA_CHUNKS="$5"
+ {
+ cat <<-EOF &&
+ header: 4d494458 1 $NUM_CHUNKS $NUM_PACKS
+ chunks: pack-names oid-fanout oid-lookup object-offsets$EXTRA_CHUNKS
+ num_objects: $NUM_OBJECTS
+ packs:
+ EOF
+ if test $NUM_PACKS -ge 1
+ then
+ ls $OBJECT_DIR/pack/ | grep idx | sort
+ fi &&
+ printf "object-dir: $OBJECT_DIR\n"
+ } >expect &&
+ test-tool read-midx $OBJECT_DIR >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'write midx with no packs' '
+ test_when_finished rm -f pack/multi-pack-index &&
+ git multi-pack-index --object-dir=. write &&
+ midx_read_expect 0 0 4 .
+'
+
+generate_objects () {
+ i=$1
+ iii=$(printf '%03i' $i)
+ {
+ test-tool genrandom "bar" 200 &&
+ test-tool genrandom "baz $iii" 50
+ } >wide_delta_$iii &&
+ {
+ test-tool genrandom "foo"$i 100 &&
+ test-tool genrandom "foo"$(( $i + 1 )) 100 &&
+ test-tool genrandom "foo"$(( $i + 2 )) 100
+ } >deep_delta_$iii &&
+ {
+ echo $iii &&
+ test-tool genrandom "$iii" 8192
+ } >file_$iii &&
+ git update-index --add file_$iii deep_delta_$iii wide_delta_$iii
+}
+
+commit_and_list_objects () {
+ {
+ echo 101 &&
+ test-tool genrandom 100 8192;
+ } >file_101 &&
+ git update-index --add file_101 &&
+ tree=$(git write-tree) &&
+ commit=$(git commit-tree $tree -p HEAD</dev/null) &&
+ {
+ echo $tree &&
+ git ls-tree $tree | sed -e "s/.* \\([0-9a-f]*\\) .*/\\1/"
+ } >obj-list &&
+ git reset --hard $commit
+}
+
+test_expect_success 'create objects' '
+ test_commit initial &&
+ for i in $(test_seq 1 5)
+ do
+ generate_objects $i
+ done &&
+ commit_and_list_objects
+'
+
+test_expect_success 'write midx with one v1 pack' '
+ pack=$(git pack-objects --index-version=1 $objdir/pack/test <obj-list) &&
+ test_when_finished rm $objdir/pack/test-$pack.pack \
+ $objdir/pack/test-$pack.idx $objdir/pack/multi-pack-index &&
+ git multi-pack-index --object-dir=$objdir write &&
+ midx_read_expect 1 18 4 $objdir
+'
+
+midx_git_two_modes () {
+ if [ "$2" = "sorted" ]
+ then
+ git -c core.multiPackIndex=false $1 | sort >expect &&
+ git -c core.multiPackIndex=true $1 | sort >actual
+ else
+ git -c core.multiPackIndex=false $1 >expect &&
+ git -c core.multiPackIndex=true $1 >actual
+ fi &&
+ test_cmp expect actual
+}
+
+compare_results_with_midx () {
+ MSG=$1
+ test_expect_success "check normal git operations: $MSG" '
+ midx_git_two_modes "rev-list --objects --all" &&
+ midx_git_two_modes "log --raw" &&
+ midx_git_two_modes "count-objects --verbose" &&
+ midx_git_two_modes "cat-file --batch-all-objects --buffer --batch-check" &&
+ midx_git_two_modes "cat-file --batch-all-objects --buffer --batch-check --unsorted" sorted
+ '
+}
+
+test_expect_success 'write midx with one v2 pack' '
+ git pack-objects --index-version=2,0x40 $objdir/pack/test <obj-list &&
+ git multi-pack-index --object-dir=$objdir write &&
+ midx_read_expect 1 18 4 $objdir
+'
+
+compare_results_with_midx "one v2 pack"
+
+test_expect_success 'add more objects' '
+ for i in $(test_seq 6 10)
+ do
+ generate_objects $i
+ done &&
+ commit_and_list_objects
+'
+
+test_expect_success 'write midx with two packs' '
+ git pack-objects --index-version=1 $objdir/pack/test-2 <obj-list &&
+ git multi-pack-index --object-dir=$objdir write &&
+ midx_read_expect 2 34 4 $objdir
+'
+
+compare_results_with_midx "two packs"
+
+test_expect_success 'add more packs' '
+ for j in $(test_seq 11 20)
+ do
+ generate_objects $j &&
+ commit_and_list_objects &&
+ git pack-objects --index-version=2 $objdir/pack/test-pack <obj-list
+ done
+'
+
+compare_results_with_midx "mixed mode (two packs + extra)"
+
+test_expect_success 'write midx with twelve packs' '
+ git multi-pack-index --object-dir=$objdir write &&
+ midx_read_expect 12 74 4 $objdir
+'
+
+compare_results_with_midx "twelve packs"
+
+test_expect_success 'repack removes multi-pack-index' '
+ test_path_is_file $objdir/pack/multi-pack-index &&
+ git repack -adf &&
+ test_path_is_missing $objdir/pack/multi-pack-index
+'
+
+compare_results_with_midx "after repack"
+
+test_expect_success 'multi-pack-index and pack-bitmap' '
+ git -c repack.writeBitmaps=true repack -ad &&
+ git multi-pack-index write &&
+ git rev-list --test-bitmap HEAD
+'
+
+test_expect_success 'multi-pack-index and alternates' '
+ git init --bare alt.git &&
+ echo $(pwd)/alt.git/objects >.git/objects/info/alternates &&
+ echo content1 >file1 &&
+ altblob=$(GIT_DIR=alt.git git hash-object -w file1) &&
+ git cat-file blob $altblob &&
+ git rev-list --all
+'
+
+compare_results_with_midx "with alternate (local midx)"
+
+test_expect_success 'multi-pack-index in an alternate' '
+ mv .git/objects/pack/* alt.git/objects/pack &&
+ test_commit add_local_objects &&
+ git repack --local &&
+ git multi-pack-index write &&
+ midx_read_expect 1 3 4 $objdir &&
+ git reset --hard HEAD~1 &&
+ rm -f .git/objects/pack/*
+'
+
+compare_results_with_midx "with alternate (remote midx)"
+
+
+# usage: corrupt_data <file> <pos> [<data>]
+corrupt_data () {
+ file=$1
+ pos=$2
+ data="${3:-\0}"
+ printf "$data" | dd of="$file" bs=1 seek="$pos" conv=notrunc
+}
+
+# Force 64-bit offsets by manipulating the idx file.
+# This makes the IDX file _incorrect_ so be careful to clean up after!
+test_expect_success 'force some 64-bit offsets with pack-objects' '
+ mkdir objects64 &&
+ mkdir objects64/pack &&
+ for i in $(test_seq 1 11)
+ do
+ generate_objects 11
+ done &&
+ commit_and_list_objects &&
+ pack64=$(git pack-objects --index-version=2,0x40 objects64/pack/test-64 <obj-list) &&
+ idx64=objects64/pack/test-64-$pack64.idx &&
+ chmod u+w $idx64 &&
+ corrupt_data $idx64 2999 "\02" &&
+ midx64=$(git multi-pack-index --object-dir=objects64 write) &&
+ midx_read_expect 1 63 5 objects64 " large-offsets"
+'
+
+test_done
diff --git a/t/t5320-delta-islands.sh b/t/t5320-delta-islands.sh
new file mode 100755
index 0000000000..fea92a5777
--- /dev/null
+++ b/t/t5320-delta-islands.sh
@@ -0,0 +1,143 @@
+#!/bin/sh
+
+test_description='exercise delta islands'
+. ./test-lib.sh
+
+# returns true iff $1 is a delta based on $2
+is_delta_base () {
+ delta_base=$(echo "$1" | git cat-file --batch-check='%(deltabase)') &&
+ echo >&2 "$1 has base $delta_base" &&
+ test "$delta_base" = "$2"
+}
+
+# generate a commit on branch $1 with a single file, "file", whose
+# content is mostly based on the seed $2, but with a unique bit
+# of content $3 appended. This should allow us to see whether
+# blobs of different refs delta against each other.
+commit() {
+ blob=$({ test-tool genrandom "$2" 10240 && echo "$3"; } |
+ git hash-object -w --stdin) &&
+ tree=$(printf '100644 blob %s\tfile\n' "$blob" | git mktree) &&
+ commit=$(echo "$2-$3" | git commit-tree "$tree" ${4:+-p "$4"}) &&
+ git update-ref "refs/heads/$1" "$commit" &&
+ eval "$1"'=$(git rev-parse $1:file)' &&
+ eval "echo >&2 $1=\$$1"
+}
+
+test_expect_success 'setup commits' '
+ commit one seed 1 &&
+ commit two seed 12
+'
+
+# Note: This is heavily dependent on the "prefer larger objects as base"
+# heuristic.
+test_expect_success 'vanilla repack deltas one against two' '
+ git repack -adf &&
+ is_delta_base $one $two
+'
+
+test_expect_success 'island repack with no island definition is vanilla' '
+ git repack -adfi &&
+ is_delta_base $one $two
+'
+
+test_expect_success 'island repack with no matches is vanilla' '
+ git -c "pack.island=refs/foo" repack -adfi &&
+ is_delta_base $one $two
+'
+
+test_expect_success 'separate islands disallows delta' '
+ git -c "pack.island=refs/heads/(.*)" repack -adfi &&
+ ! is_delta_base $one $two &&
+ ! is_delta_base $two $one
+'
+
+test_expect_success 'same island allows delta' '
+ git -c "pack.island=refs/heads" repack -adfi &&
+ is_delta_base $one $two
+'
+
+test_expect_success 'coalesce same-named islands' '
+ git \
+ -c "pack.island=refs/(.*)/one" \
+ -c "pack.island=refs/(.*)/two" \
+ repack -adfi &&
+ is_delta_base $one $two
+'
+
+test_expect_success 'island restrictions drop reused deltas' '
+ git repack -adfi &&
+ is_delta_base $one $two &&
+ git -c "pack.island=refs/heads/(.*)" repack -adi &&
+ ! is_delta_base $one $two &&
+ ! is_delta_base $two $one
+'
+
+test_expect_success 'island regexes are left-anchored' '
+ git -c "pack.island=heads/(.*)" repack -adfi &&
+ is_delta_base $one $two
+'
+
+test_expect_success 'island regexes follow last-one-wins scheme' '
+ git \
+ -c "pack.island=refs/heads/(.*)" \
+ -c "pack.island=refs/heads/" \
+ repack -adfi &&
+ is_delta_base $one $two
+'
+
+test_expect_success 'setup shared history' '
+ commit root shared root &&
+ commit one shared 1 root &&
+ commit two shared 12-long root
+'
+
+# We know that $two will be preferred as a base from $one,
+# because we can transform it with a pure deletion.
+#
+# We also expect $root as a delta against $two by the "longest is base" rule.
+test_expect_success 'vanilla delta goes between branches' '
+ git repack -adf &&
+ is_delta_base $one $two &&
+ is_delta_base $root $two
+'
+
+# Here we should allow $one to base itself on $root; even though
+# they are in different islands, the objects in $root are in a superset
+# of islands compared to those in $one.
+#
+# Similarly, $two can delta against $root by our rules. And unlike $one,
+# in which we are just allowing it, the island rules actually put $root
+# as a possible base for $two, which it would not otherwise be (due to the size
+# sorting).
+test_expect_success 'deltas allowed against superset islands' '
+ git -c "pack.island=refs/heads/(.*)" repack -adfi &&
+ is_delta_base $one $root &&
+ is_delta_base $two $root
+'
+
+# We are going to test the packfile order here, so we again have to make some
+# assumptions. We assume that "$root", as part of our core "one", must come
+# before "$two". This should be guaranteed by the island code. However, for
+# this test to fail without islands, we are also assuming that it would not
+# otherwise do so. This is true by the current write order, which will put
+# commits (and their contents) before their parents.
+test_expect_success 'island core places core objects first' '
+ cat >expect <<-EOF &&
+ $root
+ $two
+ EOF
+ git -c "pack.island=refs/heads/(.*)" \
+ -c "pack.islandcore=one" \
+ repack -adfi &&
+ git verify-pack -v .git/objects/pack/*.pack |
+ cut -d" " -f1 |
+ egrep "$root|$two" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'unmatched island core is not fatal' '
+ git -c "pack.islandcore=one" repack -adfi
+'
+
+test_done
diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh
index 911eae1bf7..f1932ea431 100755
--- a/t/t5400-send-pack.sh
+++ b/t/t5400-send-pack.sh
@@ -86,7 +86,7 @@ test_expect_success 'push can be used to delete a ref' '
test_expect_success 'refuse deleting push with denyDeletes' '
(
cd victim &&
- ( git branch -D extra || : ) &&
+ test_might_fail git branch -D extra &&
git config receive.denyDeletes true &&
git branch extra master
) &&
@@ -119,7 +119,7 @@ test_expect_success 'override denyDeletes with git -c receive-pack' '
test_expect_success 'denyNonFastforwards trumps --force' '
(
cd victim &&
- ( git branch -D extra || : ) &&
+ test_might_fail git branch -D extra &&
git config receive.denyNonFastforwards true
) &&
victim_orig=$(cd victim && git rev-parse --verify master) &&
diff --git a/t/t5401-update-hooks.sh b/t/t5401-update-hooks.sh
index 7f278d8ce9..956d69f5b1 100755
--- a/t/t5401-update-hooks.sh
+++ b/t/t5401-update-hooks.sh
@@ -82,13 +82,13 @@ test_expect_success 'hooks ran' '
'
test_expect_success 'pre-receive hook input' '
- (echo $commit0 $commit1 refs/heads/master;
+ (echo $commit0 $commit1 refs/heads/master &&
echo $commit1 $commit0 refs/heads/tofail
) | test_cmp - victim.git/pre-receive.stdin
'
test_expect_success 'update hook arguments' '
- (echo refs/heads/master $commit0 $commit1;
+ (echo refs/heads/master $commit0 $commit1 &&
echo refs/heads/tofail $commit1 $commit0
) | test_cmp - victim.git/update.args
'
@@ -104,17 +104,17 @@ test_expect_success 'post-update hook arguments' '
'
test_expect_success 'all hook stdin is /dev/null' '
- ! test -s victim.git/update.stdin &&
- ! test -s victim.git/post-update.stdin
+ test_must_be_empty victim.git/update.stdin &&
+ test_must_be_empty victim.git/post-update.stdin
'
test_expect_success 'all *-receive hook args are empty' '
- ! test -s victim.git/pre-receive.args &&
- ! test -s victim.git/post-receive.args
+ test_must_be_empty victim.git/pre-receive.args &&
+ test_must_be_empty victim.git/post-receive.args
'
test_expect_success 'send-pack produced no output' '
- ! test -s send.out
+ test_must_be_empty send.out
'
cat <<EOF >expect
diff --git a/t/t5405-send-pack-rewind.sh b/t/t5405-send-pack-rewind.sh
index 4bda18a662..235fb7686a 100755
--- a/t/t5405-send-pack-rewind.sh
+++ b/t/t5405-send-pack-rewind.sh
@@ -25,8 +25,7 @@ test_expect_success 'non forced push should die not segfault' '
(
cd another &&
- git push .. master:master
- test $? = 1
+ test_must_fail git push .. master:master
)
'
diff --git a/t/t5406-remote-rejects.sh b/t/t5406-remote-rejects.sh
index 59e80a5ea2..ff06f99649 100755
--- a/t/t5406-remote-rejects.sh
+++ b/t/t5406-remote-rejects.sh
@@ -6,8 +6,9 @@ test_description='remote push rejects are reported by client'
test_expect_success 'setup' '
mkdir .git/hooks &&
- (echo "#!/bin/sh" ; echo "exit 1") >.git/hooks/update &&
- chmod +x .git/hooks/update &&
+ write_script .git/hooks/update <<-\EOF &&
+ exit 1
+ EOF
echo 1 >file &&
git add file &&
git commit -m 1 &&
diff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh
new file mode 100755
index 0000000000..f81b6813c0
--- /dev/null
+++ b/t/t5409-colorize-remote-messages.sh
@@ -0,0 +1,101 @@
+#!/bin/sh
+
+test_description='remote messages are colorized on the client'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ mkdir .git/hooks &&
+ write_script .git/hooks/update <<-\EOF &&
+ echo error: error
+ echo ERROR: also highlighted
+ echo hint: hint
+ echo hinting: not highlighted
+ echo success: success
+ echo warning: warning
+ echo prefixerror: error
+ echo " " "error: leading space"
+ echo " "
+ echo Err
+ exit 0
+ EOF
+ echo 1 >file &&
+ git add file &&
+ git commit -m 1 &&
+ git clone . child &&
+ (
+ cd child &&
+ test_commit message2 file content2
+ )
+'
+
+test_expect_success 'keywords' '
+ git --git-dir child/.git -c color.remote=always push -f origin HEAD:refs/heads/keywords 2>output &&
+ test_decode_color <output >decoded &&
+ grep "<BOLD;RED>error<RESET>: error" decoded &&
+ grep "<YELLOW>hint<RESET>:" decoded &&
+ grep "<BOLD;GREEN>success<RESET>:" decoded &&
+ grep "<BOLD;YELLOW>warning<RESET>:" decoded
+'
+
+test_expect_success 'whole words at line start' '
+ git --git-dir child/.git -c color.remote=always push -f origin HEAD:refs/heads/whole-words 2>output &&
+ test_decode_color <output >decoded &&
+ grep "<YELLOW>hint<RESET>:" decoded &&
+ grep "hinting: not highlighted" decoded &&
+ grep "prefixerror: error" decoded
+'
+
+test_expect_success 'short line' '
+ git -C child -c color.remote=always push -f origin HEAD:short-line 2>output &&
+ test_decode_color <output >decoded &&
+ grep "remote: Err" decoded
+'
+
+test_expect_success 'case-insensitive' '
+ git --git-dir child/.git -c color.remote=always push -f origin HEAD:refs/heads/case-insensitive 2>output &&
+ cat output &&
+ test_decode_color <output >decoded &&
+ grep "<BOLD;RED>error<RESET>: error" decoded &&
+ grep "<BOLD;RED>ERROR<RESET>: also highlighted" decoded
+'
+
+test_expect_success 'leading space' '
+ git --git-dir child/.git -c color.remote=always push -f origin HEAD:refs/heads/leading-space 2>output && cat output &&
+ test_decode_color <output >decoded &&
+ grep " <BOLD;RED>error<RESET>: leading space" decoded
+'
+
+test_expect_success 'spaces only' '
+ git -C child -c color.remote=always push -f origin HEAD:only-space 2>output &&
+ test_decode_color <output >decoded &&
+ grep "remote: " decoded
+'
+
+test_expect_success 'no coloring for redirected output' '
+ git --git-dir child/.git push -f origin HEAD:refs/heads/redirected-output 2>output &&
+ test_decode_color <output >decoded &&
+ grep "error: error" decoded
+'
+
+test_expect_success 'push with customized color' '
+ git --git-dir child/.git -c color.remote=always -c color.remote.error=blue push -f origin HEAD:refs/heads/customized-color 2>output &&
+ test_decode_color <output >decoded &&
+ grep "<BLUE>error<RESET>:" decoded &&
+ grep "<BOLD;GREEN>success<RESET>:" decoded
+'
+
+
+test_expect_success 'error in customized color' '
+ git --git-dir child/.git -c color.remote=always -c color.remote.error=i-am-not-a-color push -f origin HEAD:refs/heads/error-customized-color 2>output &&
+ test_decode_color <output >decoded &&
+ grep "<BOLD;GREEN>success<RESET>:" decoded
+'
+
+test_expect_success 'fallback to color.ui' '
+ git --git-dir child/.git -c color.ui=always push -f origin HEAD:refs/heads/fallback-color-ui 2>output &&
+ test_decode_color <output >decoded &&
+ grep "<BOLD;RED>error<RESET>: error" decoded
+'
+
+test_done
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index 3d33ab3875..1b5a4a6d38 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -161,7 +161,7 @@ test_expect_success 'clone shallow object count' '
test_expect_success 'clone shallow object count (part 2)' '
sed -e "/^in-pack:/d" -e "/^packs:/d" -e "/^size-pack:/d" \
-e "/: 0$/d" count.shallow > count_output &&
- ! test -s count_output
+ test_must_be_empty count_output
'
test_expect_success 'fsck in shallow repo' '
@@ -259,7 +259,7 @@ test_expect_success 'clone shallow object count' '
test_expect_success 'pull in shallow repo with missing merge base' '
(
cd shallow &&
- git fetch --depth 4 .. A
+ git fetch --depth 4 .. A &&
test_must_fail git merge --allow-unrelated-histories FETCH_HEAD
)
'
@@ -403,7 +403,7 @@ test_expect_success 'fetch creating new shallow root' '
git fetch --depth=1 --progress 2>actual &&
# This should fetch only the empty commit, no tree or
# blob objects
- grep "remote: Total 1" actual
+ test_i18ngrep "remote: Total 1" actual
)
'
@@ -814,6 +814,39 @@ test_expect_success 'fetching deepen' '
)
'
+test_expect_success 'use ref advertisement to prune "have" lines sent' '
+ rm -rf server client &&
+ git init server &&
+ test_commit -C server both_have_1 &&
+ git -C server tag -d both_have_1 &&
+ test_commit -C server both_have_2 &&
+
+ git clone server client &&
+ test_commit -C server server_has &&
+ test_commit -C client client_has &&
+
+ # In both protocol v0 and v2, ensure that the parent of both_have_2 is
+ # not sent as a "have" line. The client should know that the server has
+ # both_have_2, so it only needs to inform the server that it has
+ # both_have_2, and the server can infer the rest.
+
+ rm -f trace &&
+ cp -r client clientv0 &&
+ GIT_TRACE_PACKET="$(pwd)/trace" git -C clientv0 \
+ fetch origin server_has both_have_2 &&
+ grep "have $(git -C client rev-parse client_has)" trace &&
+ grep "have $(git -C client rev-parse both_have_2)" trace &&
+ ! grep "have $(git -C client rev-parse both_have_2^)" trace &&
+
+ rm -f trace &&
+ cp -r client clientv2 &&
+ GIT_TRACE_PACKET="$(pwd)/trace" git -C clientv2 -c protocol.version=2 \
+ fetch origin server_has both_have_2 &&
+ grep "have $(git -C client rev-parse client_has)" trace &&
+ grep "have $(git -C client rev-parse both_have_2)" trace &&
+ ! grep "have $(git -C client rev-parse both_have_2^)" trace
+'
+
test_expect_success 'filtering by size' '
rm -rf server client &&
test_create_repo server &&
diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh
index 49d3621a92..62f3569891 100755
--- a/t/t5504-fetch-receive-strict.sh
+++ b/t/t5504-fetch-receive-strict.sh
@@ -3,13 +3,16 @@
test_description='fetch/receive strict mode'
. ./test-lib.sh
-test_expect_success setup '
+test_expect_success 'setup and inject "corrupt or missing" object' '
echo hello >greetings &&
git add greetings &&
git commit -m greetings &&
S=$(git rev-parse :greetings | sed -e "s|^..|&/|") &&
X=$(echo bye | git hash-object -w --stdin | sed -e "s|^..|&/|") &&
+ echo $S >S &&
+ echo $X >X &&
+ cp .git/objects/$S .git/objects/$S.back &&
mv -f .git/objects/$X .git/objects/$S &&
test_must_fail git fsck
@@ -115,6 +118,13 @@ test_expect_success 'push with transfer.fsckobjects' '
test_cmp exp act
'
+test_expect_success 'repair the "corrupt or missing" object' '
+ mv -f .git/objects/$(cat S) .git/objects/$(cat X) &&
+ mv .git/objects/$(cat S).back .git/objects/$(cat S) &&
+ rm -rf .git/objects/$(cat X) &&
+ git fsck
+'
+
cat >bogus-commit <<EOF
tree $EMPTY_TREE
author Bugs Bunny 1234567890 +0000
@@ -123,6 +133,14 @@ committer Bugs Bunny <bugs@bun.ni> 1234567890 +0000
This commit object intentionally broken
EOF
+test_expect_success 'fsck with invalid or bogus skipList input' '
+ git -c fsck.skipList=/dev/null -c fsck.missingEmail=ignore fsck &&
+ test_must_fail git -c fsck.skipList=does-not-exist -c fsck.missingEmail=ignore fsck 2>err &&
+ test_i18ngrep "Could not open skip list: does-not-exist" err &&
+ test_must_fail git -c fsck.skipList=.git/config -c fsck.missingEmail=ignore fsck 2>err &&
+ test_i18ngrep "Invalid SHA-1: \[core\]" err
+'
+
test_expect_success 'push with receive.fsck.skipList' '
commit="$(git hash-object -t commit -w --stdin <bogus-commit)" &&
git push . $commit:refs/heads/bogus &&
@@ -130,11 +148,61 @@ test_expect_success 'push with receive.fsck.skipList' '
git init dst &&
git --git-dir=dst/.git config receive.fsckObjects true &&
test_must_fail git push --porcelain dst bogus &&
- git --git-dir=dst/.git config receive.fsck.skipList SKIP &&
echo $commit >dst/.git/SKIP &&
+
+ # receive.fsck.* does not fall back on fsck.*
+ git --git-dir=dst/.git config fsck.skipList SKIP &&
+ test_must_fail git push --porcelain dst bogus &&
+
+ # Invalid and/or bogus skipList input
+ git --git-dir=dst/.git config receive.fsck.skipList /dev/null &&
+ test_must_fail git push --porcelain dst bogus &&
+ git --git-dir=dst/.git config receive.fsck.skipList does-not-exist &&
+ test_must_fail git push --porcelain dst bogus 2>err &&
+ test_i18ngrep "Could not open skip list: does-not-exist" err &&
+ git --git-dir=dst/.git config receive.fsck.skipList config &&
+ test_must_fail git push --porcelain dst bogus 2>err &&
+ test_i18ngrep "Invalid SHA-1: \[core\]" err &&
+
+ git --git-dir=dst/.git config receive.fsck.skipList SKIP &&
git push --porcelain dst bogus
'
+test_expect_success 'fetch with fetch.fsck.skipList' '
+ commit="$(git hash-object -t commit -w --stdin <bogus-commit)" &&
+ refspec=refs/heads/bogus:refs/heads/bogus &&
+ git push . $commit:refs/heads/bogus &&
+ rm -rf dst &&
+ git init dst &&
+ git --git-dir=dst/.git config fetch.fsckObjects true &&
+ test_must_fail git --git-dir=dst/.git fetch "file://$(pwd)" $refspec &&
+ git --git-dir=dst/.git config fetch.fsck.skipList /dev/null &&
+ test_must_fail git --git-dir=dst/.git fetch "file://$(pwd)" $refspec &&
+ echo $commit >dst/.git/SKIP &&
+
+ # fetch.fsck.* does not fall back on fsck.*
+ git --git-dir=dst/.git config fsck.skipList dst/.git/SKIP &&
+ test_must_fail git --git-dir=dst/.git fetch "file://$(pwd)" $refspec &&
+
+ # Invalid and/or bogus skipList input
+ git --git-dir=dst/.git config fetch.fsck.skipList /dev/null &&
+ test_must_fail git --git-dir=dst/.git fetch "file://$(pwd)" $refspec &&
+ git --git-dir=dst/.git config fetch.fsck.skipList does-not-exist &&
+ test_must_fail git --git-dir=dst/.git fetch "file://$(pwd)" $refspec 2>err &&
+ test_i18ngrep "Could not open skip list: does-not-exist" err &&
+ git --git-dir=dst/.git config fetch.fsck.skipList dst/.git/config &&
+ test_must_fail git --git-dir=dst/.git fetch "file://$(pwd)" $refspec 2>err &&
+ test_i18ngrep "Invalid SHA-1: \[core\]" err &&
+
+ git --git-dir=dst/.git config fetch.fsck.skipList dst/.git/SKIP &&
+ git --git-dir=dst/.git fetch "file://$(pwd)" $refspec
+'
+
+test_expect_success 'fsck.<unknownmsg-id> dies' '
+ test_must_fail git -c fsck.whatEver=ignore fsck 2>err &&
+ test_i18ngrep "Unhandled message id: whatever" err
+'
+
test_expect_success 'push with receive.fsck.missingEmail=warn' '
commit="$(git hash-object -t commit -w --stdin <bogus-commit)" &&
git push . $commit:refs/heads/bogus &&
@@ -142,19 +210,58 @@ test_expect_success 'push with receive.fsck.missingEmail=warn' '
git init dst &&
git --git-dir=dst/.git config receive.fsckobjects true &&
test_must_fail git push --porcelain dst bogus &&
+
+ # receive.fsck.<msg-id> does not fall back on fsck.<msg-id>
+ git --git-dir=dst/.git config fsck.missingEmail warn &&
+ test_must_fail git push --porcelain dst bogus &&
+
+ # receive.fsck.<unknownmsg-id> warns
+ git --git-dir=dst/.git config \
+ receive.fsck.whatEver error &&
+
git --git-dir=dst/.git config \
receive.fsck.missingEmail warn &&
git push --porcelain dst bogus >act 2>&1 &&
grep "missingEmail" act &&
+ test_i18ngrep "Skipping unknown msg id.*whatever" act &&
git --git-dir=dst/.git branch -D bogus &&
git --git-dir=dst/.git config --add \
receive.fsck.missingEmail ignore &&
- git --git-dir=dst/.git config --add \
- receive.fsck.badDate warn &&
git push --porcelain dst bogus >act 2>&1 &&
! grep "missingEmail" act
'
+test_expect_success 'fetch with fetch.fsck.missingEmail=warn' '
+ commit="$(git hash-object -t commit -w --stdin <bogus-commit)" &&
+ refspec=refs/heads/bogus:refs/heads/bogus &&
+ git push . $commit:refs/heads/bogus &&
+ rm -rf dst &&
+ git init dst &&
+ git --git-dir=dst/.git config fetch.fsckobjects true &&
+ test_must_fail git --git-dir=dst/.git fetch "file://$(pwd)" $refspec &&
+
+ # fetch.fsck.<msg-id> does not fall back on fsck.<msg-id>
+ git --git-dir=dst/.git config fsck.missingEmail warn &&
+ test_must_fail git --git-dir=dst/.git fetch "file://$(pwd)" $refspec &&
+
+ # receive.fsck.<unknownmsg-id> warns
+ git --git-dir=dst/.git config \
+ fetch.fsck.whatEver error &&
+
+ git --git-dir=dst/.git config \
+ fetch.fsck.missingEmail warn &&
+ git --git-dir=dst/.git fetch "file://$(pwd)" $refspec >act 2>&1 &&
+ grep "missingEmail" act &&
+ test_i18ngrep "Skipping unknown msg id.*whatever" act &&
+ rm -rf dst &&
+ git init dst &&
+ git --git-dir=dst/.git config fetch.fsckobjects true &&
+ git --git-dir=dst/.git config \
+ fetch.fsck.missingEmail ignore &&
+ git --git-dir=dst/.git fetch "file://$(pwd)" $refspec >act 2>&1 &&
+ ! grep "missingEmail" act
+'
+
test_expect_success \
'receive.fsck.unterminatedHeader=warn triggers error' '
rm -rf dst &&
@@ -166,4 +273,15 @@ test_expect_success \
grep "Cannot demote unterminatedheader" act
'
+test_expect_success \
+ 'fetch.fsck.unterminatedHeader=warn triggers error' '
+ rm -rf dst &&
+ git init dst &&
+ git --git-dir=dst/.git config fetch.fsckobjects true &&
+ git --git-dir=dst/.git config \
+ fetch.fsck.unterminatedheader warn &&
+ test_must_fail git --git-dir=dst/.git fetch "file://$(pwd)" HEAD &&
+ grep "Cannot demote unterminatedheader" act
+'
+
test_done
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index a6c0178f3a..241e6a319d 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -74,8 +74,7 @@ test_expect_success 'add another remote' '
git for-each-ref "--format=%(refname)" refs/remotes |
sed -e "/^refs\/remotes\/origin\//d" \
-e "/^refs\/remotes\/second\//d" >actual &&
- >expect &&
- test_cmp expect actual
+ test_must_be_empty actual
)
'
@@ -112,8 +111,7 @@ test_expect_success C_LOCALE_OUTPUT 'remove remote' '
check_remote_track origin master side &&
git for-each-ref "--format=%(refname)" refs/remotes |
sed -e "/^refs\/remotes\/origin\//d" >actual &&
- >expect &&
- test_cmp expect actual
+ test_must_be_empty actual
)
'
@@ -348,17 +346,13 @@ URL: $(pwd)/one
EOF
test_expect_success 'prune --dry-run' '
- (
- cd one &&
- git branch -m side2 side) &&
+ git -C one branch -m side2 side &&
+ test_when_finished "git -C one branch -m side side2" &&
(
cd test &&
git remote prune --dry-run origin >output &&
git rev-parse refs/remotes/origin/side2 &&
test_must_fail git rev-parse refs/remotes/origin/side &&
- (
- cd ../one &&
- git branch -m side side2) &&
test_i18ncmp expect output
)
'
@@ -848,7 +842,7 @@ test_expect_success 'migrate a remote from named file in $GIT_DIR/branches (2)'
git remote rename origin origin &&
test_path_is_missing .git/branches/origin &&
test "$(git config remote.origin.url)" = "quux" &&
- test "$(git config remote.origin.fetch)" = "refs/heads/foom:refs/heads/origin"
+ test "$(git config remote.origin.fetch)" = "refs/heads/foom:refs/heads/origin" &&
test "$(git config remote.origin.push)" = "HEAD:refs/heads/foom"
)
'
@@ -876,7 +870,7 @@ test_expect_success 'remote prune to cause a dangling symref' '
cd eight &&
test_must_fail git branch nomore origin
) 2>err &&
- grep "dangling symref" err
+ test_i18ngrep "dangling symref" err
'
test_expect_success 'show empty remote' '
diff --git a/t/t5509-fetch-push-namespaces.sh b/t/t5509-fetch-push-namespaces.sh
index 75c570adca..c88df78c0b 100755
--- a/t/t5509-fetch-push-namespaces.sh
+++ b/t/t5509-fetch-push-namespaces.sh
@@ -44,7 +44,7 @@ test_expect_success 'pushing into a repository using a ref namespace' '
test_cmp expected actual &&
# Try a namespace with no content
git ls-remote "ext::git --namespace=garbage %s ../pushee" >actual &&
- test_cmp /dev/null actual &&
+ test_must_be_empty actual &&
git ls-remote pushee-unnamespaced >actual &&
sed -e "s|refs/|refs/namespaces/namespace/refs/|" expected >expected.unnamespaced &&
test_cmp expected.unnamespaced actual
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 6ab093207a..3b7b30568c 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -535,6 +535,41 @@ test_expect_success "should be able to fetch with duplicate refspecs" '
)
'
+test_expect_success 'LHS of refspec follows ref disambiguation rules' '
+ mkdir lhs-ambiguous &&
+ (
+ cd lhs-ambiguous &&
+ git init server &&
+ test_commit -C server unwanted &&
+ test_commit -C server wanted &&
+
+ git init client &&
+
+ # Check a name coming after "refs" alphabetically ...
+ git -C server update-ref refs/heads/s wanted &&
+ git -C server update-ref refs/heads/refs/heads/s unwanted &&
+ git -C client fetch ../server +refs/heads/s:refs/heads/checkthis &&
+ git -C server rev-parse wanted >expect &&
+ git -C client rev-parse checkthis >actual &&
+ test_cmp expect actual &&
+
+ # ... and one before.
+ git -C server update-ref refs/heads/q wanted &&
+ git -C server update-ref refs/heads/refs/heads/q unwanted &&
+ git -C client fetch ../server +refs/heads/q:refs/heads/checkthis &&
+ git -C server rev-parse wanted >expect &&
+ git -C client rev-parse checkthis >actual &&
+ test_cmp expect actual &&
+
+ # Tags are preferred over branches like refs/{heads,tags}/*
+ git -C server update-ref refs/tags/t wanted &&
+ git -C server update-ref refs/heads/t unwanted &&
+ git -C client fetch ../server +t:refs/heads/checkthis &&
+ git -C server rev-parse wanted >expect &&
+ git -C client rev-parse checkthis >actual
+ )
+'
+
# configured prune tests
set_config_tristate () {
@@ -828,9 +863,11 @@ test_expect_success 'fetching with auto-gc does not lock up' '
test_commit test2 &&
(
cd auto-gc &&
+ git config fetch.unpackLimit 1 &&
git config gc.autoPackLimit 1 &&
git config gc.autoDetach false &&
GIT_ASK_YESNO="$D/askyesno" git fetch >fetch.out 2>&1 &&
+ test_i18ngrep "Auto packing the repository" fetch.out &&
! grep "Should I try again" fetch.out
)
'
@@ -865,4 +902,82 @@ test_expect_success C_LOCALE_OUTPUT 'fetch compact output' '
test_cmp expect actual
'
+setup_negotiation_tip () {
+ SERVER="$1"
+ URL="$2"
+ USE_PROTOCOL_V2="$3"
+
+ rm -rf "$SERVER" client trace &&
+ git init "$SERVER" &&
+ test_commit -C "$SERVER" alpha_1 &&
+ test_commit -C "$SERVER" alpha_2 &&
+ git -C "$SERVER" checkout --orphan beta &&
+ test_commit -C "$SERVER" beta_1 &&
+ test_commit -C "$SERVER" beta_2 &&
+
+ git clone "$URL" client &&
+
+ if test "$USE_PROTOCOL_V2" -eq 1
+ then
+ git -C "$SERVER" config protocol.version 2 &&
+ git -C client config protocol.version 2
+ fi &&
+
+ test_commit -C "$SERVER" beta_s &&
+ git -C "$SERVER" checkout master &&
+ test_commit -C "$SERVER" alpha_s &&
+ git -C "$SERVER" tag -d alpha_1 alpha_2 beta_1 beta_2
+}
+
+check_negotiation_tip () {
+ # Ensure that {alpha,beta}_1 are sent as "have", but not {alpha_beta}_2
+ ALPHA_1=$(git -C client rev-parse alpha_1) &&
+ grep "fetch> have $ALPHA_1" trace &&
+ BETA_1=$(git -C client rev-parse beta_1) &&
+ grep "fetch> have $BETA_1" trace &&
+ ALPHA_2=$(git -C client rev-parse alpha_2) &&
+ ! grep "fetch> have $ALPHA_2" trace &&
+ BETA_2=$(git -C client rev-parse beta_2) &&
+ ! grep "fetch> have $BETA_2" trace
+}
+
+test_expect_success '--negotiation-tip limits "have" lines sent' '
+ setup_negotiation_tip server server 0 &&
+ GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
+ --negotiation-tip=alpha_1 --negotiation-tip=beta_1 \
+ origin alpha_s beta_s &&
+ check_negotiation_tip
+'
+
+test_expect_success '--negotiation-tip understands globs' '
+ setup_negotiation_tip server server 0 &&
+ GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
+ --negotiation-tip=*_1 \
+ origin alpha_s beta_s &&
+ check_negotiation_tip
+'
+
+test_expect_success '--negotiation-tip understands abbreviated SHA-1' '
+ setup_negotiation_tip server server 0 &&
+ GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
+ --negotiation-tip=$(git -C client rev-parse --short alpha_1) \
+ --negotiation-tip=$(git -C client rev-parse --short beta_1) \
+ origin alpha_s beta_s &&
+ check_negotiation_tip
+'
+
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success '--negotiation-tip limits "have" lines sent with HTTP protocol v2' '
+ setup_negotiation_tip "$HTTPD_DOCUMENT_ROOT_PATH/server" \
+ "$HTTPD_URL/smart/server" 1 &&
+ GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
+ --negotiation-tip=alpha_1 --negotiation-tip=beta_1 \
+ origin alpha_s beta_s &&
+ check_negotiation_tip
+'
+
+stop_httpd
+
test_done
diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh
index 6a949484d0..bc5703ff9b 100755
--- a/t/t5512-ls-remote.sh
+++ b/t/t5512-ls-remote.sh
@@ -15,7 +15,7 @@ test_expect_success setup '
git tag mark1.10 &&
git show-ref --tags -d | sed -e "s/ / /" >expected.tag &&
(
- echo "$(git rev-parse HEAD) HEAD"
+ echo "$(git rev-parse HEAD) HEAD" &&
git show-ref -d | sed -e "s/ / /"
) >expected.all &&
@@ -105,7 +105,7 @@ test_expect_success 'use branch.<name>.remote if possible' '
git clone . other.git &&
(
cd other.git &&
- echo "$(git rev-parse HEAD) HEAD"
+ echo "$(git rev-parse HEAD) HEAD" &&
git show-ref | sed -e "s/ / /"
) >exp &&
@@ -155,14 +155,12 @@ test_expect_success 'die with non-2 for wrong repository even with --exit-code'
test_expect_success 'Report success even when nothing matches' '
git ls-remote other.git "refs/nsn/*" >actual &&
- >expect &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'Report no-match with --exit-code' '
test_expect_code 2 git ls-remote --exit-code other.git "refs/nsn/*" >actual &&
- >expect &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'Report match with --exit-code' '
diff --git a/t/t5514-fetch-multiple.sh b/t/t5514-fetch-multiple.sh
index 4b4b6673b8..0030c92e1a 100755
--- a/t/t5514-fetch-multiple.sh
+++ b/t/t5514-fetch-multiple.sh
@@ -152,7 +152,6 @@ test_expect_success 'git fetch --multiple (ignoring skipFetchAll)' '
'
test_expect_success 'git fetch --all --no-tags' '
- >expect &&
git clone one test5 &&
git clone test5 test6 &&
(cd test5 && git tag test-tag) &&
@@ -161,7 +160,7 @@ test_expect_success 'git fetch --all --no-tags' '
git fetch --all --no-tags &&
git tag >output
) &&
- test_cmp expect test6/output
+ test_must_be_empty test6/output
'
test_expect_success 'git fetch --all --tags' '
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index c545b16d13..7a8f56db53 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -923,7 +923,7 @@ test_expect_success 'push into aliased refs (consistent)' '
(
cd child1 &&
git branch foo &&
- git symbolic-ref refs/heads/bar refs/heads/foo
+ git symbolic-ref refs/heads/bar refs/heads/foo &&
git config receive.denyCurrentBranch false
) &&
(
@@ -945,7 +945,7 @@ test_expect_success 'push into aliased refs (inconsistent)' '
(
cd child1 &&
git branch foo &&
- git symbolic-ref refs/heads/bar refs/heads/foo
+ git symbolic-ref refs/heads/bar refs/heads/foo &&
git config receive.denyCurrentBranch false
) &&
(
@@ -1061,7 +1061,7 @@ test_expect_success 'push --porcelain rejected' '
mk_empty testrepo &&
git push testrepo refs/heads/master:refs/remotes/origin/master &&
(cd testrepo &&
- git reset --hard origin/master^
+ git reset --hard origin/master^ &&
git config receive.denyCurrentBranch true) &&
echo >.git/foo "To testrepo" &&
@@ -1075,7 +1075,7 @@ test_expect_success 'push --porcelain --dry-run rejected' '
mk_empty testrepo &&
git push testrepo refs/heads/master:refs/remotes/origin/master &&
(cd testrepo &&
- git reset --hard origin/master
+ git reset --hard origin/master &&
git config receive.denyCurrentBranch true) &&
echo >.git/foo "To testrepo" &&
@@ -1383,7 +1383,7 @@ test_expect_success 'push --follow-tag only pushes relevant tags' '
git commit --allow-empty -m "future commit" &&
git tag -m "future" future &&
git checkout master &&
- git for-each-ref refs/heads/master refs/tags/tag >../expect
+ git for-each-ref refs/heads/master refs/tags/tag >../expect &&
git push --follow-tag ../dst master
) &&
(
diff --git a/t/t5517-push-mirror.sh b/t/t5517-push-mirror.sh
index 02f160aae0..c05a661400 100755
--- a/t/t5517-push-mirror.sh
+++ b/t/t5517-push-mirror.sh
@@ -71,7 +71,7 @@ test_expect_success 'push mirror force updates existing branches' '
git push --mirror up &&
echo two >foo && git add foo && git commit -m two &&
git push --mirror up &&
- git reset --hard HEAD^
+ git reset --hard HEAD^ &&
git push --mirror up
) &&
master_master=$(cd master && git show-ref -s --verify refs/heads/master) &&
@@ -88,7 +88,7 @@ test_expect_success 'push mirror removes branches' '
echo one >foo && git add foo && git commit -m one &&
git branch remove master &&
git push --mirror up &&
- git branch -D remove
+ git branch -D remove &&
git push --mirror up
) &&
(
@@ -170,7 +170,7 @@ test_expect_success 'push mirror force updates existing tags' '
echo two >foo && git add foo && git commit -m two &&
git tag -f tmaster master &&
git push --mirror up &&
- git reset --hard HEAD^
+ git reset --hard HEAD^ &&
git tag -f tmaster master &&
git push --mirror up
) &&
@@ -188,7 +188,7 @@ test_expect_success 'push mirror removes tags' '
echo one >foo && git add foo && git commit -m one &&
git tag -f tremove master &&
git push --mirror up &&
- git tag -d tremove
+ git tag -d tremove &&
git push --mirror up
) &&
(
@@ -235,7 +235,7 @@ test_expect_success 'remote.foo.mirror adds and removes branches' '
git branch keep master &&
git branch remove master &&
git push up &&
- git branch -D remove
+ git branch -D remove &&
git push up
) &&
(
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index 59c4b778d3..5e501c8b08 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -475,10 +475,22 @@ test_expect_success 'pull.rebase=interactive' '
false
EOF
test_set_editor "$TRASH_DIRECTORY/fake-editor" &&
+ test_when_finished "test_might_fail git rebase --abort" &&
test_must_fail git pull --rebase=interactive . copy &&
test "I was here" = "$(cat fake.out)"
'
+test_expect_success 'pull --rebase=i' '
+ write_script "$TRASH_DIRECTORY/fake-editor" <<-\EOF &&
+ echo I was here, too >fake.out &&
+ false
+ EOF
+ test_set_editor "$TRASH_DIRECTORY/fake-editor" &&
+ test_when_finished "test_might_fail git rebase --abort" &&
+ test_must_fail git pull --rebase=i . copy &&
+ test "I was here, too" = "$(cat fake.out)"
+'
+
test_expect_success 'pull.rebase=invalid fails' '
git reset --hard before-preserve-rebase &&
test_config pull.rebase invalid &&
@@ -618,6 +630,18 @@ test_expect_success 'pull --rebase fails on unborn branch with staged changes' '
)
'
+test_expect_success 'pull --rebase fails on corrupt HEAD' '
+ test_when_finished "rm -rf corrupt" &&
+ git init corrupt &&
+ (
+ cd corrupt &&
+ test_commit one &&
+ obj=$(git rev-parse --verify HEAD | sed "s#^..#&/#") &&
+ rm -f .git/objects/$obj &&
+ test_must_fail git pull --rebase
+ )
+'
+
test_expect_success 'setup for detecting upstreamed changes' '
mkdir src &&
(cd src &&
diff --git a/t/t5523-push-upstream.sh b/t/t5523-push-upstream.sh
index d6981ba304..c0df81a014 100755
--- a/t/t5523-push-upstream.sh
+++ b/t/t5523-push-upstream.sh
@@ -113,7 +113,7 @@ test_expect_success TTY 'quiet push' '
ensure_fresh_upstream &&
test_terminal git push --quiet --no-progress upstream master 2>&1 | tee output &&
- test_cmp /dev/null output
+ test_must_be_empty output
'
test_done
diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh
index 359e03ff83..6c2f9b2ba2 100755
--- a/t/t5526-fetch-submodules.sh
+++ b/t/t5526-fetch-submodules.sh
@@ -98,8 +98,8 @@ test_expect_success "fetch alone only fetches superproject" '
cd downstream &&
git fetch >../actual.out 2>../actual.err
) &&
- ! test -s actual.out &&
- ! test -s actual.err
+ test_must_be_empty actual.out &&
+ test_must_be_empty actual.err
'
test_expect_success "fetch --no-recurse-submodules only fetches superproject" '
@@ -107,8 +107,8 @@ test_expect_success "fetch --no-recurse-submodules only fetches superproject" '
cd downstream &&
git fetch --no-recurse-submodules >../actual.out 2>../actual.err
) &&
- ! test -s actual.out &&
- ! test -s actual.err
+ test_must_be_empty actual.out &&
+ test_must_be_empty actual.err
'
test_expect_success "using fetchRecurseSubmodules=true in .gitmodules recurses into submodules" '
@@ -127,8 +127,8 @@ test_expect_success "--no-recurse-submodules overrides .gitmodules config" '
cd downstream &&
git fetch --no-recurse-submodules >../actual.out 2>../actual.err
) &&
- ! test -s actual.out &&
- ! test -s actual.err
+ test_must_be_empty actual.out &&
+ test_must_be_empty actual.err
'
test_expect_success "using fetchRecurseSubmodules=false in .git/config overrides setting in .gitmodules" '
@@ -137,8 +137,8 @@ test_expect_success "using fetchRecurseSubmodules=false in .git/config overrides
git config submodule.submodule.fetchRecurseSubmodules false &&
git fetch >../actual.out 2>../actual.err
) &&
- ! test -s actual.out &&
- ! test -s actual.err
+ test_must_be_empty actual.out &&
+ test_must_be_empty actual.err
'
test_expect_success "--recurse-submodules overrides fetchRecurseSubmodules setting from .git/config" '
@@ -157,8 +157,8 @@ test_expect_success "--quiet propagates to submodules" '
cd downstream &&
git fetch --recurse-submodules --quiet >../actual.out 2>../actual.err
) &&
- ! test -s actual.out &&
- ! test -s actual.err
+ test_must_be_empty actual.out &&
+ test_must_be_empty actual.err
'
test_expect_success "--quiet propagates to parallel submodules" '
@@ -166,8 +166,8 @@ test_expect_success "--quiet propagates to parallel submodules" '
cd downstream &&
git fetch --recurse-submodules -j 2 --quiet >../actual.out 2>../actual.err
) &&
- ! test -s actual.out &&
- ! test -s actual.err
+ test_must_be_empty actual.out &&
+ test_must_be_empty actual.err
'
test_expect_success "--dry-run propagates to submodules" '
@@ -221,8 +221,8 @@ test_expect_success "--no-recurse-submodules overrides config setting" '
git config fetch.recurseSubmodules true &&
git fetch --no-recurse-submodules >../actual.out 2>../actual.err
) &&
- ! test -s actual.out &&
- ! test -s actual.err
+ test_must_be_empty actual.out &&
+ test_must_be_empty actual.err
'
test_expect_success "Recursion doesn't happen when no new commits are fetched in the superproject" '
@@ -235,8 +235,8 @@ test_expect_success "Recursion doesn't happen when no new commits are fetched in
git config --unset fetch.recurseSubmodules &&
git fetch >../actual.out 2>../actual.err
) &&
- ! test -s actual.out &&
- ! test -s actual.err
+ test_must_be_empty actual.out &&
+ test_must_be_empty actual.err
'
test_expect_success "Recursion stops when no new submodule commits are fetched" '
@@ -268,7 +268,7 @@ test_expect_success "Recursion doesn't happen when new superproject commits don'
cd downstream &&
git fetch >../actual.out 2>../actual.err
) &&
- ! test -s actual.out &&
+ test_must_be_empty actual.out &&
test_i18ncmp expect.err.file actual.err
'
@@ -357,8 +357,8 @@ test_expect_success "'--recurse-submodules=on-demand' doesn't recurse when no ne
git fetch --recurse-submodules=on-demand >../actual.out 2>../actual.err &&
git config --unset fetch.recurseSubmodules
) &&
- ! test -s actual.out &&
- ! test -s actual.err
+ test_must_be_empty actual.out &&
+ test_must_be_empty actual.err
'
test_expect_success "'--recurse-submodules=on-demand' recurses as deep as necessary (and ignores config)" '
@@ -379,7 +379,7 @@ test_expect_success "'--recurse-submodules=on-demand' recurses as deep as necess
git config -f .gitmodules submodule.subdir/deepsubmodule.fetchRecursive false
) &&
git fetch --recurse-submodules=on-demand >../actual.out 2>../actual.err &&
- git config --unset fetch.recurseSubmodules
+ git config --unset fetch.recurseSubmodules &&
(
cd submodule &&
git config --unset -f .gitmodules submodule.subdir/deepsubmodule.fetchRecursive
@@ -402,7 +402,7 @@ test_expect_success "'--recurse-submodules=on-demand' stops when no new submodul
cd downstream &&
git fetch --recurse-submodules=on-demand >../actual.out 2>../actual.err
) &&
- ! test -s actual.out &&
+ test_must_be_empty actual.out &&
test_i18ncmp expect.err.file actual.err
'
@@ -477,7 +477,7 @@ test_expect_success "don't fetch submodule when newly recorded commits are alrea
cd downstream &&
git fetch >../actual.out 2>../actual.err
) &&
- ! test -s actual.out &&
+ test_must_be_empty actual.out &&
test_i18ncmp expect.err actual.err &&
(
cd submodule &&
@@ -495,7 +495,6 @@ test_expect_success "'fetch.recurseSubmodules=on-demand' works also without .git
git add submodule &&
git rm .gitmodules &&
git commit -m "new submodule without .gitmodules" &&
- printf "" >expect.out &&
head2=$(git rev-parse --short HEAD) &&
echo "From $pwd/." >expect.err.2 &&
echo " $head1..$head2 master -> origin/master" >>expect.err.2 &&
@@ -514,7 +513,7 @@ test_expect_success "'fetch.recurseSubmodules=on-demand' works also without .git
git config --unset fetch.recurseSubmodules &&
git reset --hard
) &&
- test_i18ncmp expect.out actual.out &&
+ test_must_be_empty actual.out &&
test_i18ncmp expect.err.2 actual.err &&
git checkout HEAD^ -- .gitmodules &&
git add .gitmodules &&
diff --git a/t/t5531-deep-submodule-push.sh b/t/t5531-deep-submodule-push.sh
index 39cb2c1c34..e2c37fd978 100755
--- a/t/t5531-deep-submodule-push.sh
+++ b/t/t5531-deep-submodule-push.sh
@@ -354,7 +354,7 @@ test_expect_success 'push succeeds if submodule has no remote and is on the firs
git clone a a1 &&
(
cd a1 &&
- git init b
+ git init b &&
(
cd b &&
>junk &&
diff --git a/t/t5533-push-cas.sh b/t/t5533-push-cas.sh
index d38ecee217..0b0eb1d025 100755
--- a/t/t5533-push-cas.sh
+++ b/t/t5533-push-cas.sh
@@ -142,9 +142,8 @@ test_expect_success 'push to delete (protected, forced)' '
cd dst &&
git push --force --force-with-lease=master:master^ origin :master
) &&
- >expect &&
git ls-remote src refs/heads/master >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'push to delete (allowed)' '
@@ -154,9 +153,8 @@ test_expect_success 'push to delete (allowed)' '
git push --force-with-lease=master origin :master 2>err &&
grep deleted err
) &&
- >expect &&
git ls-remote src refs/heads/master >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'cover everything with default force-with-lease (protected)' '
diff --git a/t/t5534-push-signed.sh b/t/t5534-push-signed.sh
index 1cea758f78..030331f1c5 100755
--- a/t/t5534-push-signed.sh
+++ b/t/t5534-push-signed.sh
@@ -194,10 +194,12 @@ test_expect_success GPG 'fail without key and heed user.signingkey' '
EOF
- unset GIT_COMMITTER_EMAIL &&
- git config user.email hasnokey@nowhere.com &&
- test_must_fail git push --signed dst noop ff +noff &&
- git config user.signingkey committer@example.com &&
+ test_config user.email hasnokey@nowhere.com &&
+ (
+ sane_unset GIT_COMMITTER_EMAIL &&
+ test_must_fail git push --signed dst noop ff +noff
+ ) &&
+ test_config user.signingkey $GIT_COMMITTER_EMAIL &&
git push --signed dst noop ff +noff &&
(
@@ -218,4 +220,57 @@ test_expect_success GPG 'fail without key and heed user.signingkey' '
test_cmp expect dst/push-cert-status
'
+test_expect_success GPGSM 'fail without key and heed user.signingkey x509' '
+ test_config gpg.format x509 &&
+ prepare_dst &&
+ mkdir -p dst/.git/hooks &&
+ git -C dst config receive.certnonceseed sekrit &&
+ write_script dst/.git/hooks/post-receive <<-\EOF &&
+ # discard the update list
+ cat >/dev/null
+ # record the push certificate
+ if test -n "${GIT_PUSH_CERT-}"
+ then
+ git cat-file blob $GIT_PUSH_CERT >../push-cert
+ fi &&
+
+ cat >../push-cert-status <<E_O_F
+ SIGNER=${GIT_PUSH_CERT_SIGNER-nobody}
+ KEY=${GIT_PUSH_CERT_KEY-nokey}
+ STATUS=${GIT_PUSH_CERT_STATUS-nostatus}
+ NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus}
+ NONCE=${GIT_PUSH_CERT_NONCE-nononce}
+ E_O_F
+
+ EOF
+
+ test_config user.email hasnokey@nowhere.com &&
+ test_config user.signingkey "" &&
+ (
+ sane_unset GIT_COMMITTER_EMAIL &&
+ test_must_fail git push --signed dst noop ff +noff
+ ) &&
+ test_config user.signingkey $GIT_COMMITTER_EMAIL &&
+ git push --signed dst noop ff +noff &&
+
+ (
+ cat <<-\EOF &&
+ SIGNER=/CN=C O Mitter/O=Example/SN=C O/GN=Mitter
+ KEY=
+ STATUS=G
+ NONCE_STATUS=OK
+ EOF
+ sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
+ ) >expect.in &&
+ key=$(cat "${GNUPGHOME}/trustlist.txt" | cut -d" " -f1 | tr -d ":") &&
+ sed -e "s/^KEY=/KEY=${key}/" expect.in >expect &&
+
+ noop=$(git rev-parse noop) &&
+ ff=$(git rev-parse ff) &&
+ noff=$(git rev-parse noff) &&
+ grep "$noop $ff refs/heads/ff" dst/push-cert &&
+ grep "$noop $noff refs/heads/noff" dst/push-cert &&
+ test_cmp expect dst/push-cert-status
+'
+
test_done
diff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh
index a2af693068..5475afc052 100755
--- a/t/t5541-http-push-smart.sh
+++ b/t/t5541-http-push-smart.sh
@@ -38,25 +38,16 @@ GET /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200
EOF
test_expect_success 'no empty path components' '
+ # Clear the log, so that it does not affect the "used receive-pack
+ # service" test which reads the log too.
+ test_when_finished ">\"\$HTTPD_ROOT_PATH\"/access.log" &&
+
# In the URL, add a trailing slash, and see if git appends yet another
# slash.
cd "$ROOT_PATH" &&
git clone $HTTPD_URL/smart/test_repo.git/ test_repo_clone &&
- sed -e "
- s/^.* \"//
- s/\"//
- s/ [1-9][0-9]*\$//
- s/^GET /GET /
- " >act <"$HTTPD_ROOT_PATH"/access.log &&
-
- # Clear the log, so that it does not affect the "used receive-pack
- # service" test which reads the log too.
- #
- # We do this before the actual comparison to ensure the log is cleared.
- echo > "$HTTPD_ROOT_PATH"/access.log &&
-
- test_cmp exp act
+ check_access_log exp
'
test_expect_success 'clone remote repository' '
@@ -124,7 +115,6 @@ test_expect_success 'rejected update prints status' '
rm -f "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update"
cat >exp <<EOF
-
GET /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200
GET /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
@@ -138,13 +128,7 @@ GET /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
EOF
test_expect_success 'used receive-pack service' '
- sed -e "
- s/^.* \"//
- s/\"//
- s/ [1-9][0-9]*\$//
- s/^GET /GET /
- " >act <"$HTTPD_ROOT_PATH"/access.log &&
- test_cmp exp act
+ check_access_log exp
'
test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
@@ -226,7 +210,7 @@ test_expect_success TTY 'push --quiet silences status and progress' '
cd "$ROOT_PATH"/test_repo_clone &&
test_commit quiet &&
test_terminal git push --quiet >output 2>&1 &&
- test_cmp /dev/null output
+ test_must_be_empty output
'
test_expect_success TTY 'push --no-progress silences progress but not status' '
diff --git a/t/t5543-atomic-push.sh b/t/t5543-atomic-push.sh
index 3480b33007..7079bcf9a0 100755
--- a/t/t5543-atomic-push.sh
+++ b/t/t5543-atomic-push.sh
@@ -178,7 +178,7 @@ test_expect_success 'atomic push obeys update hook preventing a branch to be pus
test_expect_success 'atomic push is not advertised if configured' '
mk_repo_pair &&
(
- cd upstream
+ cd upstream &&
git config receive.advertiseatomic 0
) &&
(
diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh
index 913089b144..771f36f9ff 100755
--- a/t/t5551-http-fetch-smart.sh
+++ b/t/t5551-http-fetch-smart.sh
@@ -103,13 +103,7 @@ GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
POST /smart/repo.git/git-upload-pack HTTP/1.1 200
EOF
test_expect_success 'used upload-pack service' '
- sed -e "
- s/^.* \"//
- s/\"//
- s/ [1-9][0-9]*\$//
- s/^GET /GET /
- " >act <"$HTTPD_ROOT_PATH"/access.log &&
- test_cmp exp act
+ check_access_log exp
'
test_expect_success 'follow redirects (301)' '
@@ -369,6 +363,24 @@ test_expect_success 'custom http headers' '
submodule update sub
'
+test_expect_success 'using fetch command in remote-curl updates refs' '
+ SERVER="$HTTPD_DOCUMENT_ROOT_PATH/twobranch" &&
+ rm -rf "$SERVER" client &&
+
+ git init "$SERVER" &&
+ test_commit -C "$SERVER" foo &&
+ git -C "$SERVER" update-ref refs/heads/anotherbranch foo &&
+
+ git clone $HTTPD_URL/smart/twobranch client &&
+
+ test_commit -C "$SERVER" bar &&
+ git -C client -c protocol.version=0 fetch &&
+
+ git -C "$SERVER" rev-parse master >expect &&
+ git -C client rev-parse origin/master >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'GIT_REDACT_COOKIES redacts cookies' '
rm -rf clone &&
echo "Set-Cookie: Foo=1" >cookies &&
diff --git a/t/t5552-skipping-fetch-negotiator.sh b/t/t5552-skipping-fetch-negotiator.sh
new file mode 100755
index 0000000000..30857b84a8
--- /dev/null
+++ b/t/t5552-skipping-fetch-negotiator.sh
@@ -0,0 +1,215 @@
+#!/bin/sh
+
+test_description='test skipping fetch negotiator'
+. ./test-lib.sh
+
+have_sent () {
+ while test "$#" -ne 0
+ do
+ grep "fetch> have $(git -C client rev-parse $1)" trace
+ if test $? -ne 0
+ then
+ echo "No have $(git -C client rev-parse $1) ($1)"
+ return 1
+ fi
+ shift
+ done
+}
+
+have_not_sent () {
+ while test "$#" -ne 0
+ do
+ grep "fetch> have $(git -C client rev-parse $1)" trace
+ if test $? -eq 0
+ then
+ return 1
+ fi
+ shift
+ done
+}
+
+# trace_fetch <client_dir> <server_dir> [args]
+#
+# Trace the packet output of fetch, but make sure we disable the variable
+# in the child upload-pack, so we don't combine the results in the same file.
+trace_fetch () {
+ client=$1; shift
+ server=$1; shift
+ GIT_TRACE_PACKET="$(pwd)/trace" \
+ git -C "$client" fetch \
+ --upload-pack 'unset GIT_TRACE_PACKET; git-upload-pack' \
+ "$server" "$@"
+}
+
+test_expect_success 'commits with no parents are sent regardless of skip distance' '
+ git init server &&
+ test_commit -C server to_fetch &&
+
+ git init client &&
+ for i in $(test_seq 7)
+ do
+ test_commit -C client c$i
+ done &&
+
+ # We send: "c7" (skip 1) "c5" (skip 2) "c2" (skip 4). After that, since
+ # "c1" has no parent, it is still sent as "have" even though it would
+ # normally be skipped.
+ test_config -C client fetch.negotiationalgorithm skipping &&
+ trace_fetch client "$(pwd)/server" &&
+ have_sent c7 c5 c2 c1 &&
+ have_not_sent c6 c4 c3
+'
+
+test_expect_success 'unknown fetch.negotiationAlgorithm values error out' '
+ rm -rf server client trace &&
+ git init server &&
+ test_commit -C server to_fetch &&
+
+ git init client &&
+ test_commit -C client on_client &&
+ git -C client checkout on_client &&
+
+ test_config -C client fetch.negotiationAlgorithm invalid &&
+ test_must_fail git -C client fetch "$(pwd)/server" 2>err &&
+ test_i18ngrep "unknown fetch negotiation algorithm" err &&
+
+ # Explicit "default" value
+ test_config -C client fetch.negotiationAlgorithm default &&
+ git -C client -c fetch.negotiationAlgorithm=default fetch "$(pwd)/server" &&
+
+ # Implementation detail: If there is nothing to fetch, we will not error out
+ test_config -C client fetch.negotiationAlgorithm invalid &&
+ git -C client fetch "$(pwd)/server" 2>err &&
+ test_i18ngrep ! "unknown fetch negotiation algorithm" err
+'
+
+test_expect_success 'when two skips collide, favor the larger one' '
+ rm -rf server client trace &&
+ git init server &&
+ test_commit -C server to_fetch &&
+
+ git init client &&
+ for i in $(test_seq 11)
+ do
+ test_commit -C client c$i
+ done &&
+ git -C client checkout c5 &&
+ test_commit -C client c5side &&
+
+ # Before reaching c5, we send "c5side" (skip 1) and "c11" (skip 1) "c9"
+ # (skip 2) "c6" (skip 4). The larger skip (skip 4) takes precedence, so
+ # the next "have" sent will be "c1" (from "c6" skip 4) and not "c4"
+ # (from "c5side" skip 1).
+ test_config -C client fetch.negotiationalgorithm skipping &&
+ trace_fetch client "$(pwd)/server" &&
+ have_sent c5side c11 c9 c6 c1 &&
+ have_not_sent c10 c8 c7 c5 c4 c3 c2
+'
+
+test_expect_success 'use ref advertisement to filter out commits' '
+ rm -rf server client trace &&
+ git init server &&
+ test_commit -C server c1 &&
+ test_commit -C server c2 &&
+ test_commit -C server c3 &&
+ git -C server tag -d c1 c2 c3 &&
+
+ git clone server client &&
+ test_commit -C client c4 &&
+ test_commit -C client c5 &&
+ git -C client checkout c4^^ &&
+ test_commit -C client c2side &&
+
+ git -C server checkout --orphan anotherbranch &&
+ test_commit -C server to_fetch &&
+
+ # The server advertising "c3" (as "refs/heads/master") means that we do
+ # not need to send any ancestors of "c3", but we still need to send "c3"
+ # itself.
+ test_config -C client fetch.negotiationalgorithm skipping &&
+ trace_fetch client origin to_fetch &&
+ have_sent c5 c4^ c2side &&
+ have_not_sent c4 c4^^ c4^^^
+'
+
+test_expect_success 'handle clock skew' '
+ rm -rf server client trace &&
+ git init server &&
+ test_commit -C server to_fetch &&
+
+ git init client &&
+
+ # 2 regular commits
+ test_tick=2000000000 &&
+ test_commit -C client c1 &&
+ test_commit -C client c2 &&
+
+ # 4 old commits
+ test_tick=1000000000 &&
+ git -C client checkout c1 &&
+ test_commit -C client old1 &&
+ test_commit -C client old2 &&
+ test_commit -C client old3 &&
+ test_commit -C client old4 &&
+
+ # "c2" and "c1" are popped first, then "old4" to "old1". "old1" would
+ # normally be skipped, but is treated as a commit without a parent here
+ # and sent, because (due to clock skew) its only parent has already been
+ # popped off the priority queue.
+ test_config -C client fetch.negotiationalgorithm skipping &&
+ trace_fetch client "$(pwd)/server" &&
+ have_sent c2 c1 old4 old2 old1 &&
+ have_not_sent old3
+'
+
+test_expect_success 'do not send "have" with ancestors of commits that server ACKed' '
+ rm -rf server client trace &&
+ git init server &&
+ test_commit -C server to_fetch &&
+
+ git init client &&
+ for i in $(test_seq 8)
+ do
+ git -C client checkout --orphan b$i &&
+ test_commit -C client b$i.c0
+ done &&
+ for j in $(test_seq 19)
+ do
+ for i in $(test_seq 8)
+ do
+ git -C client checkout b$i &&
+ test_commit -C client b$i.c$j
+ done
+ done &&
+
+ # Copy this branch over to the server and add a commit on it so that it
+ # is reachable but not advertised.
+ git -C server fetch --no-tags "$(pwd)/client" b1:refs/heads/b1 &&
+ git -C server checkout b1 &&
+ test_commit -C server commit-on-b1 &&
+
+ test_config -C client fetch.negotiationalgorithm skipping &&
+ trace_fetch client "$(pwd)/server" to_fetch &&
+ grep " fetch" trace &&
+
+ # fetch-pack sends 2 requests each containing 16 "have" lines before
+ # processing the first response. In these 2 requests, 4 commits from
+ # each branch are sent. Just check the first branch.
+ have_sent b1.c19 b1.c17 b1.c14 b1.c9 &&
+ have_not_sent b1.c18 b1.c16 b1.c15 b1.c13 b1.c12 b1.c11 b1.c10 &&
+
+ # While fetch-pack is processing the first response, it should read that
+ # the server ACKs b1.c19 and b1.c17.
+ grep "fetch< ACK $(git -C client rev-parse b1.c19) common" trace &&
+ grep "fetch< ACK $(git -C client rev-parse b1.c17) common" trace &&
+
+ # fetch-pack should thus not send any more commits in the b1 branch, but
+ # should still send the others (in this test, just check b2).
+ for i in $(test_seq 0 8)
+ do
+ have_not_sent b1.c$i
+ done &&
+ have_sent b2.c1 b2.c0
+'
+
+test_done
diff --git a/t/t5561-http-backend.sh b/t/t5561-http-backend.sh
index 84a955770a..1c49054595 100755
--- a/t/t5561-http-backend.sh
+++ b/t/t5561-http-backend.sh
@@ -129,13 +129,7 @@ GET /smart/repo.git/info/refs?service=git-receive-pack HTTP/1.1 403 -
POST /smart/repo.git/git-receive-pack HTTP/1.1 403 -
EOF
test_expect_success 'server request log matches test results' '
- sed -e "
- s/^.* \"//
- s/\"//
- s/ [1-9][0-9]*\$//
- s/^GET /GET /
- " >act <"$HTTPD_ROOT_PATH"/access.log &&
- test_cmp exp act
+ check_access_log exp
'
stop_httpd
diff --git a/t/t5562-http-backend-content-length.sh b/t/t5562-http-backend-content-length.sh
new file mode 100755
index 0000000000..b24d8b05a4
--- /dev/null
+++ b/t/t5562-http-backend-content-length.sh
@@ -0,0 +1,167 @@
+#!/bin/sh
+
+test_description='test git-http-backend respects CONTENT_LENGTH'
+. ./test-lib.sh
+
+test_lazy_prereq GZIP 'gzip --version'
+
+verify_http_result() {
+ # some fatal errors still produce status 200
+ # so check if there is the error message
+ if grep 'fatal:' act.err
+ then
+ return 1
+ fi
+
+ if ! grep "Status" act.out >act
+ then
+ printf "Status: 200 OK\r\n" >act
+ fi
+ printf "Status: $1\r\n" >exp &&
+ test_cmp exp act
+}
+
+test_http_env() {
+ handler_type="$1"
+ request_body="$2"
+ shift
+ env \
+ CONTENT_TYPE="application/x-git-$handler_type-pack-request" \
+ QUERY_STRING="/repo.git/git-$handler_type-pack" \
+ PATH_TRANSLATED="$PWD/.git/git-$handler_type-pack" \
+ GIT_HTTP_EXPORT_ALL=TRUE \
+ REQUEST_METHOD=POST \
+ "$TEST_DIRECTORY"/t5562/invoke-with-content-length.pl \
+ "$request_body" git http-backend >act.out 2>act.err
+}
+
+ssize_b100dots() {
+ # hardcoded ((size_t) SSIZE_MAX) + 1
+ case "$(build_option sizeof-size_t)" in
+ 8) echo 9223372036854775808;;
+ 4) echo 2147483648;;
+ *) die "Unexpected ssize_t size: $(build_option sizeof-size_t)";;
+ esac
+}
+
+test_expect_success 'setup' '
+ HTTP_CONTENT_ENCODING="identity" &&
+ export HTTP_CONTENT_ENCODING &&
+ git config http.receivepack true &&
+ test_commit c0 &&
+ test_commit c1 &&
+ hash_head=$(git rev-parse HEAD) &&
+ hash_prev=$(git rev-parse HEAD~1) &&
+ printf "want %s" "$hash_head" | packetize >fetch_body &&
+ printf 0000 >>fetch_body &&
+ printf "have %s" "$hash_prev" | packetize >>fetch_body &&
+ printf done | packetize >>fetch_body &&
+ test_copy_bytes 10 <fetch_body >fetch_body.trunc &&
+ hash_next=$(git commit-tree -p HEAD -m next HEAD^{tree}) &&
+ printf "%s %s refs/heads/newbranch\\0report-status\\n" "$_z40" "$hash_next" | packetize >push_body &&
+ printf 0000 >>push_body &&
+ echo "$hash_next" | git pack-objects --stdout >>push_body &&
+ test_copy_bytes 10 <push_body >push_body.trunc &&
+ : >empty_body
+'
+
+test_expect_success GZIP 'setup, compression related' '
+ gzip -c fetch_body >fetch_body.gz &&
+ test_copy_bytes 10 <fetch_body.gz >fetch_body.gz.trunc &&
+ gzip -c push_body >push_body.gz &&
+ test_copy_bytes 10 <push_body.gz >push_body.gz.trunc
+'
+
+test_expect_success 'fetch plain' '
+ test_http_env upload fetch_body &&
+ verify_http_result "200 OK"
+'
+
+test_expect_success 'fetch plain truncated' '
+ test_http_env upload fetch_body.trunc &&
+ ! verify_http_result "200 OK"
+'
+
+test_expect_success 'fetch plain empty' '
+ test_http_env upload empty_body &&
+ ! verify_http_result "200 OK"
+'
+
+test_expect_success GZIP 'fetch gzipped' '
+ test_env HTTP_CONTENT_ENCODING="gzip" test_http_env upload fetch_body.gz &&
+ verify_http_result "200 OK"
+'
+
+test_expect_success GZIP 'fetch gzipped truncated' '
+ test_env HTTP_CONTENT_ENCODING="gzip" test_http_env upload fetch_body.gz.trunc &&
+ ! verify_http_result "200 OK"
+'
+
+test_expect_success GZIP 'fetch gzipped empty' '
+ test_env HTTP_CONTENT_ENCODING="gzip" test_http_env upload empty_body &&
+ ! verify_http_result "200 OK"
+'
+
+test_expect_success GZIP 'push plain' '
+ test_when_finished "git branch -D newbranch" &&
+ test_http_env receive push_body &&
+ verify_http_result "200 OK" &&
+ git rev-parse newbranch >act.head &&
+ echo "$hash_next" >exp.head &&
+ test_cmp act.head exp.head
+'
+
+test_expect_success 'push plain truncated' '
+ test_http_env receive push_body.trunc &&
+ ! verify_http_result "200 OK"
+'
+
+test_expect_success 'push plain empty' '
+ test_http_env receive empty_body &&
+ ! verify_http_result "200 OK"
+'
+
+test_expect_success GZIP 'push gzipped' '
+ test_when_finished "git branch -D newbranch" &&
+ test_env HTTP_CONTENT_ENCODING="gzip" test_http_env receive push_body.gz &&
+ verify_http_result "200 OK" &&
+ git rev-parse newbranch >act.head &&
+ echo "$hash_next" >exp.head &&
+ test_cmp act.head exp.head
+'
+
+test_expect_success GZIP 'push gzipped truncated' '
+ test_env HTTP_CONTENT_ENCODING="gzip" test_http_env receive push_body.gz.trunc &&
+ ! verify_http_result "200 OK"
+'
+
+test_expect_success GZIP 'push gzipped empty' '
+ test_env HTTP_CONTENT_ENCODING="gzip" test_http_env receive empty_body &&
+ ! verify_http_result "200 OK"
+'
+
+test_expect_success 'CONTENT_LENGTH overflow ssite_t' '
+ NOT_FIT_IN_SSIZE=$(ssize_b100dots) &&
+ env \
+ CONTENT_TYPE=application/x-git-upload-pack-request \
+ QUERY_STRING=/repo.git/git-upload-pack \
+ PATH_TRANSLATED="$PWD"/.git/git-upload-pack \
+ GIT_HTTP_EXPORT_ALL=TRUE \
+ REQUEST_METHOD=POST \
+ CONTENT_LENGTH="$NOT_FIT_IN_SSIZE" \
+ git http-backend </dev/zero >/dev/null 2>err &&
+ grep "fatal:.*CONTENT_LENGTH" err
+'
+
+test_expect_success 'empty CONTENT_LENGTH' '
+ env \
+ QUERY_STRING="service=git-receive-pack" \
+ PATH_TRANSLATED="$PWD"/.git/info/refs \
+ GIT_HTTP_EXPORT_ALL=TRUE \
+ REQUEST_METHOD=GET \
+ CONTENT_LENGTH="" \
+ git http-backend <empty_body >act.out 2>act.err &&
+ verify_http_result "200 OK"
+'
+
+test_done
diff --git a/t/t5562/invoke-with-content-length.pl b/t/t5562/invoke-with-content-length.pl
new file mode 100755
index 0000000000..6c2aae7692
--- /dev/null
+++ b/t/t5562/invoke-with-content-length.pl
@@ -0,0 +1,37 @@
+#!/usr/bin/perl
+use 5.008;
+use strict;
+use warnings;
+
+my $body_filename = $ARGV[0];
+my @command = @ARGV[1 .. $#ARGV];
+
+# read data
+my $body_size = -s $body_filename;
+$ENV{"CONTENT_LENGTH"} = $body_size;
+open(my $body_fh, "<", $body_filename) or die "Cannot open $body_filename: $!";
+my $body_data;
+defined read($body_fh, $body_data, $body_size) or die "Cannot read $body_filename: $!";
+close($body_fh);
+
+my $exited = 0;
+$SIG{"CHLD"} = sub {
+ $exited = 1;
+};
+
+# write data
+my $pid = open(my $out, "|-", @command);
+{
+ # disable buffering at $out
+ my $old_selected = select;
+ select $out;
+ $| = 1;
+ select $old_selected;
+}
+print $out $body_data or die "Cannot write data: $!";
+
+sleep 60; # is interrupted by SIGCHLD
+if (!$exited) {
+ close($out);
+ die "Command did not exit after reading whole body";
+}
diff --git a/t/t5570-git-daemon.sh b/t/t5570-git-daemon.sh
index 0d4c52016b..7466aad111 100755
--- a/t/t5570-git-daemon.sh
+++ b/t/t5570-git-daemon.sh
@@ -7,9 +7,9 @@ test_description='test fetching over git protocol'
start_git_daemon
check_verbose_connect () {
- grep -F "Looking up 127.0.0.1 ..." stderr &&
- grep -F "Connecting to 127.0.0.1 (port " stderr &&
- grep -F "done." stderr
+ test_i18ngrep -F "Looking up 127.0.0.1 ..." stderr &&
+ test_i18ngrep -F "Connecting to 127.0.0.1 (port " stderr &&
+ test_i18ngrep -F "done." stderr
}
test_expect_success 'setup repository' '
@@ -51,7 +51,7 @@ test_expect_success 'no-op fetch -v stderr is as expected' '
test_expect_success 'no-op fetch without "-v" is quiet' '
(cd clone && git fetch 2>../stderr) &&
- ! test -s stderr
+ test_must_be_empty stderr
'
test_expect_success 'remote detects correct HEAD' '
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index 0b62037744..f1a49e94f5 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -618,16 +618,22 @@ hex2oct () {
test_expect_success 'clone on case-insensitive fs' '
git init icasefs &&
(
- cd icasefs
+ cd icasefs &&
o=$(git hash-object -w --stdin </dev/null | hex2oct) &&
t=$(printf "100644 X\0${o}100644 x\0${o}" |
git hash-object -w -t tree --stdin) &&
c=$(git commit-tree -m bogus $t) &&
git update-ref refs/heads/bogus $c &&
- git clone -b bogus . bogus
+ git clone -b bogus . bogus 2>warning
)
'
+test_expect_success !MINGW,!CYGWIN,CASE_INSENSITIVE_FS 'colliding file detection' '
+ grep X icasefs/warning &&
+ grep x icasefs/warning &&
+ test_i18ngrep "the following paths have collided" icasefs/warning
+'
+
partial_clone () {
SERVER="$1" &&
URL="$2" &&
diff --git a/t/t5605-clone-local.sh b/t/t5605-clone-local.sh
index 3c087e907c..af23419ebf 100755
--- a/t/t5605-clone-local.sh
+++ b/t/t5605-clone-local.sh
@@ -94,7 +94,7 @@ test_expect_success 'clone empty repository' '
git config receive.denyCurrentBranch warn) &&
git clone empty empty-clone &&
test_tick &&
- (cd empty-clone
+ (cd empty-clone &&
echo "content" >> foo &&
git add foo &&
git commit -m "Initial commit" &&
diff --git a/t/t5608-clone-2gb.sh b/t/t5608-clone-2gb.sh
index df822d9a3e..2c6bc07344 100755
--- a/t/t5608-clone-2gb.sh
+++ b/t/t5608-clone-2gb.sh
@@ -23,7 +23,7 @@ test_expect_success CLONE_2GB 'setup' '
printf "blob\nmark :$i\ndata $blobsize\n" &&
#test-tool genrandom $i $blobsize &&
printf "%-${blobsize}s" $i &&
- echo "M 100644 :$i $i" >> commit
+ echo "M 100644 :$i $i" >> commit &&
i=$(($i+1)) ||
echo $? > exit-status
done &&
diff --git a/t/t5612-clone-refspec.sh b/t/t5612-clone-refspec.sh
index 6ea8f50dae..e36ac01661 100755
--- a/t/t5612-clone-refspec.sh
+++ b/t/t5612-clone-refspec.sh
@@ -97,8 +97,7 @@ test_expect_success 'clone with --no-tags' '
git fetch &&
git for-each-ref refs/tags >../actual
) &&
- >expect &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success '--single-branch while HEAD pointing at master' '
@@ -140,8 +139,7 @@ test_expect_success '--single-branch while HEAD pointing at master and --no-tags
git fetch &&
git for-each-ref refs/tags >../actual
) &&
- >expect &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
test_line_count = 0 actual &&
# get tags with --tags overrides tagOpt
(
@@ -230,8 +228,7 @@ test_expect_success '--single-branch with detached' '
-e "s|/remotes/origin/|/heads/|" >../actual
) &&
# nothing
- >expect &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_done
diff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh
index 44d8e80171..bbbe7537df 100755
--- a/t/t5616-partial-clone.sh
+++ b/t/t5616-partial-clone.sh
@@ -216,6 +216,50 @@ test_expect_success 'upon cloning, check that all refs point to objects' '
! test -e "$HTTPD_ROOT_PATH/one-time-sed"
'
+test_expect_success 'when partial cloning, tolerate server not sending target of tag' '
+ SERVER="$HTTPD_DOCUMENT_ROOT_PATH/server" &&
+ rm -rf "$SERVER" repo &&
+ test_create_repo "$SERVER" &&
+ test_commit -C "$SERVER" foo &&
+ test_config -C "$SERVER" uploadpack.allowfilter 1 &&
+ test_config -C "$SERVER" uploadpack.allowanysha1inwant 1 &&
+
+ # Create an annotated tag pointing to a blob.
+ BLOB=$(echo blob-contents | git -C "$SERVER" hash-object --stdin -w) &&
+ git -C "$SERVER" tag -m message -a myblob "$BLOB" &&
+
+ # Craft a packfile including the tag, but not the blob it points to.
+ # Also, omit objects referenced from HEAD in order to force a second
+ # fetch (to fetch missing objects) upon the automatic checkout that
+ # happens after a clone.
+ printf "%s\n%s\n--not\n%s\n%s\n" \
+ $(git -C "$SERVER" rev-parse HEAD) \
+ $(git -C "$SERVER" rev-parse myblob) \
+ $(git -C "$SERVER" rev-parse HEAD^{tree}) \
+ $(git -C "$SERVER" rev-parse myblob^{blob}) |
+ git -C "$SERVER" pack-objects --thin --stdout >incomplete.pack &&
+
+ # Replace the existing packfile with the crafted one. The protocol
+ # requires that the packfile be sent in sideband 1, hence the extra
+ # \x01 byte at the beginning.
+ printf "1,/packfile/!c %04x\\\\x01%s0000" \
+ "$(($(wc -c <incomplete.pack) + 5))" \
+ "$(sed_escape <incomplete.pack)" \
+ >"$HTTPD_ROOT_PATH/one-time-sed" &&
+
+ # Use protocol v2 because the sed command looks for the "packfile"
+ # section header.
+ test_config -C "$SERVER" protocol.version 2 &&
+
+ # Exercise to make sure it works.
+ git -c protocol.version=2 clone \
+ --filter=blob:none $HTTPD_URL/one_time_sed/server repo 2> err &&
+ ! grep "missing object referenced by" err &&
+
+ # Ensure that the one-time-sed script was used.
+ ! test -e "$HTTPD_ROOT_PATH/one-time-sed"
+'
+
stop_httpd
test_done
diff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh
index a4fe6508bd..3beeed4546 100755
--- a/t/t5702-protocol-v2.sh
+++ b/t/t5702-protocol-v2.sh
@@ -181,7 +181,12 @@ test_expect_success 'clone with file:// using protocol v2' '
test_cmp expect actual &&
# Server responded using protocol v2
- grep "clone< version 2" log
+ grep "clone< version 2" log &&
+
+ # Client sent ref-prefixes to filter the ref-advertisement
+ grep "ref-prefix HEAD" log &&
+ grep "ref-prefix refs/heads/" log &&
+ grep "ref-prefix refs/tags/" log
'
test_expect_success 'fetch with file:// using protocol v2' '
@@ -204,6 +209,7 @@ test_expect_success 'ref advertisment is filtered during fetch using protocol v2
test_when_finished "rm -f log" &&
test_commit -C file_parent three &&
+ git -C file_parent branch unwanted-branch three &&
GIT_TRACE_PACKET="$(pwd)/log" git -C file_child -c protocol.version=2 \
fetch origin master &&
@@ -212,9 +218,8 @@ test_expect_success 'ref advertisment is filtered during fetch using protocol v2
git -C file_parent log -1 --format=%s >expect &&
test_cmp expect actual &&
- ! grep "refs/tags/one" log &&
- ! grep "refs/tags/two" log &&
- ! grep "refs/tags/three" log
+ grep "refs/heads/master" log &&
+ ! grep "refs/heads/unwanted-branch" log
'
test_expect_success 'server-options are sent when fetching' '
@@ -359,6 +364,71 @@ test_expect_success 'default refspec is used to filter ref when fetchcing' '
grep "ref-prefix refs/tags/" log
'
+test_expect_success 'fetch supports various ways of have lines' '
+ rm -rf server client trace &&
+ git init server &&
+ test_commit -C server dwim &&
+ TREE=$(git -C server rev-parse HEAD^{tree}) &&
+ git -C server tag exact \
+ $(git -C server commit-tree -m a "$TREE") &&
+ git -C server tag dwim-unwanted \
+ $(git -C server commit-tree -m b "$TREE") &&
+ git -C server tag exact-unwanted \
+ $(git -C server commit-tree -m c "$TREE") &&
+ git -C server tag prefix1 \
+ $(git -C server commit-tree -m d "$TREE") &&
+ git -C server tag prefix2 \
+ $(git -C server commit-tree -m e "$TREE") &&
+ git -C server tag fetch-by-sha1 \
+ $(git -C server commit-tree -m f "$TREE") &&
+ git -C server tag completely-unrelated \
+ $(git -C server commit-tree -m g "$TREE") &&
+
+ git init client &&
+ GIT_TRACE_PACKET="$(pwd)/trace" git -C client -c protocol.version=2 \
+ fetch "file://$(pwd)/server" \
+ dwim \
+ refs/tags/exact \
+ refs/tags/prefix*:refs/tags/prefix* \
+ "$(git -C server rev-parse fetch-by-sha1)" &&
+
+ # Ensure that the appropriate prefixes are sent (using a sample)
+ grep "fetch> ref-prefix dwim" trace &&
+ grep "fetch> ref-prefix refs/heads/dwim" trace &&
+ grep "fetch> ref-prefix refs/tags/prefix" trace &&
+
+ # Ensure that the correct objects are returned
+ git -C client cat-file -e $(git -C server rev-parse dwim) &&
+ git -C client cat-file -e $(git -C server rev-parse exact) &&
+ git -C client cat-file -e $(git -C server rev-parse prefix1) &&
+ git -C client cat-file -e $(git -C server rev-parse prefix2) &&
+ git -C client cat-file -e $(git -C server rev-parse fetch-by-sha1) &&
+ test_must_fail git -C client cat-file -e \
+ $(git -C server rev-parse dwim-unwanted) &&
+ test_must_fail git -C client cat-file -e \
+ $(git -C server rev-parse exact-unwanted) &&
+ test_must_fail git -C client cat-file -e \
+ $(git -C server rev-parse completely-unrelated)
+'
+
+test_expect_success 'fetch supports include-tag and tag following' '
+ rm -rf server client trace &&
+ git init server &&
+
+ test_commit -C server to_fetch &&
+ git -C server tag -a annotated_tag -m message &&
+
+ git init client &&
+ GIT_TRACE_PACKET="$(pwd)/trace" git -C client -c protocol.version=2 \
+ fetch "$(pwd)/server" to_fetch:to_fetch &&
+
+ grep "fetch> ref-prefix to_fetch" trace &&
+ grep "fetch> ref-prefix refs/tags/" trace &&
+ grep "fetch> include-tag" trace &&
+
+ git -C client cat-file -e $(git -C client rev-parse annotated_tag)
+'
+
# Test protocol v2 with 'http://' transport
#
. "$TEST_DIRECTORY"/lib-httpd.sh
diff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh
index a73c55a47e..d1ccc22331 100755
--- a/t/t5703-upload-pack-ref-in-want.sh
+++ b/t/t5703-upload-pack-ref-in-want.sh
@@ -176,7 +176,7 @@ test_expect_success 'setup repos for change-while-negotiating test' '
git clone "http://127.0.0.1:$LIB_HTTPD_PORT/smart/repo" "$LOCAL_PRISTINE" &&
cd "$LOCAL_PRISTINE" &&
git checkout -b side &&
- for i in $(seq 1 33); do test_commit s$i; done &&
+ for i in $(test_seq 1 33); do test_commit s$i; done &&
# Add novel commits to upstream
git checkout master &&
@@ -289,7 +289,7 @@ test_expect_success 'setup repos for fetching with ref-in-want tests' '
git clone "file://$REPO" "$LOCAL_PRISTINE" &&
cd "$LOCAL_PRISTINE" &&
git checkout -b side &&
- for i in $(seq 1 33); do test_commit s$i; done &&
+ for i in $(test_seq 1 33); do test_commit s$i; done &&
# Add novel commits to upstream
git checkout master &&
diff --git a/t/t5801-remote-helpers.sh b/t/t5801-remote-helpers.sh
index 362b1581e0..aaaa722cca 100755
--- a/t/t5801-remote-helpers.sh
+++ b/t/t5801-remote-helpers.sh
@@ -96,7 +96,7 @@ test_expect_success 'push new branch with old:new refspec' '
test_expect_success 'push new branch with HEAD:new refspec' '
(cd local &&
- git checkout new-name
+ git checkout new-name &&
git push origin HEAD:new-refspec-2
) &&
compare_refs local HEAD server refs/heads/new-refspec-2
@@ -126,7 +126,7 @@ test_expect_success 'forced push' '
test_expect_success 'cloning without refspec' '
GIT_REMOTE_TESTGIT_REFSPEC="" \
git clone "testgit::${PWD}/server" local2 2>error &&
- grep "This remote helper should implement refspec capability" error &&
+ test_i18ngrep "this remote helper should implement refspec capability" error &&
compare_refs local2 HEAD server HEAD
'
@@ -134,7 +134,7 @@ test_expect_success 'pulling without refspecs' '
(cd local2 &&
git reset --hard &&
GIT_REMOTE_TESTGIT_REFSPEC="" git pull 2>../error) &&
- grep "This remote helper should implement refspec capability" error &&
+ test_i18ngrep "this remote helper should implement refspec capability" error &&
compare_refs local2 HEAD server HEAD
'
@@ -146,7 +146,7 @@ test_expect_success 'pushing without refspecs' '
GIT_REMOTE_TESTGIT_REFSPEC="" &&
export GIT_REMOTE_TESTGIT_REFSPEC &&
test_must_fail git push 2>../error) &&
- grep "remote-helper doesn.t support push; refspec needed" error
+ test_i18ngrep "remote-helper doesn.t support push; refspec needed" error
'
test_expect_success 'pulling without marks' '
@@ -246,7 +246,7 @@ test_expect_success 'proper failure checks for fetching' '
(cd local &&
test_must_fail env GIT_REMOTE_TESTGIT_FAILURE=1 git fetch 2>error &&
cat error &&
- grep -q "Error while running fast-import" error
+ test_i18ngrep -q "error while running fast-import" error
)
'
diff --git a/t/t6000-rev-list-misc.sh b/t/t6000-rev-list-misc.sh
index 969e4e9e52..fb4d295aa0 100755
--- a/t/t6000-rev-list-misc.sh
+++ b/t/t6000-rev-list-misc.sh
@@ -58,8 +58,7 @@ test_expect_success 'rev-list A..B and rev-list ^A B are the same' '
test_expect_success 'propagate uninteresting flag down correctly' '
git rev-list --objects ^HEAD^{tree} HEAD^{tree} >actual &&
- >expect &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'symleft flag bit is propagated down from tag' '
diff --git a/t/t6009-rev-list-parent.sh b/t/t6009-rev-list-parent.sh
index 20e3e2554a..916d9692bc 100755
--- a/t/t6009-rev-list-parent.sh
+++ b/t/t6009-rev-list-parent.sh
@@ -31,8 +31,7 @@ test_expect_success setup '
test_expect_success 'one is ancestor of others and should not be shown' '
git rev-list one --not four >result &&
- >expect &&
- test_cmp expect result
+ test_must_be_empty result
'
@@ -144,8 +143,7 @@ test_expect_success 'ancestors with the same commit time' '
test_commit t$i
done &&
git rev-list t1^! --not t$i >result &&
- >expect &&
- test_cmp expect result
+ test_must_be_empty result
'
test_done
diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh
index aa2d360ce3..44c726ea39 100755
--- a/t/t6010-merge-base.sh
+++ b/t/t6010-merge-base.sh
@@ -245,7 +245,7 @@ test_expect_success 'using reflog to find the fork point' '
git commit --allow-empty -m "Derived #$count" &&
git rev-parse HEAD >derived$count &&
git checkout -B base $E || exit 1
- done
+ done &&
for count in 1 2 3
do
diff --git a/t/t6011-rev-list-with-bad-commit.sh b/t/t6011-rev-list-with-bad-commit.sh
index e51eb41f4b..545b461e51 100755
--- a/t/t6011-rev-list-with-bad-commit.sh
+++ b/t/t6011-rev-list-with-bad-commit.sh
@@ -41,10 +41,9 @@ test_expect_success 'corrupt second commit object' \
test_must_fail git fsck --full
'
-test_expect_success 'rev-list should fail' \
- '
- test_must_fail git rev-list --all > /dev/null
- '
+test_expect_success 'rev-list should fail' '
+ test_must_fail env GIT_TEST_COMMIT_GRAPH=0 git rev-list --all > /dev/null
+'
test_expect_success 'git repack _MUST_ fail' \
'
diff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh
index d3453c583c..db8a7834d8 100755
--- a/t/t6018-rev-list-glob.sh
+++ b/t/t6018-rev-list-glob.sh
@@ -255,32 +255,28 @@ test_expect_success 'rev-list accumulates multiple --exclude' '
compare rev-list "--exclude=refs/remotes/* --exclude=refs/tags/* --all" --branches
'
-test_expect_failure 'rev-list should succeed with empty output on empty stdin' '
- >expect &&
- git rev-list --stdin <expect >actual &&
- test_cmp expect actual
+test_expect_success 'rev-list should succeed with empty output on empty stdin' '
+ git rev-list --stdin </dev/null >actual &&
+ test_must_be_empty actual
'
test_expect_success 'rev-list should succeed with empty output with all refs excluded' '
- >expect &&
git rev-list --exclude=* --all >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'rev-list should succeed with empty output with empty --all' '
(
test_create_repo empty &&
cd empty &&
- >expect &&
git rev-list --all >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
)
'
test_expect_success 'rev-list should succeed with empty output with empty glob' '
- >expect &&
git rev-list --glob=does-not-match-anything >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'shortlog accepts --glob/--tags/--remotes' '
diff --git a/t/t6019-rev-list-ancestry-path.sh b/t/t6019-rev-list-ancestry-path.sh
index dabebaee0b..beadaf6cca 100755
--- a/t/t6019-rev-list-ancestry-path.sh
+++ b/t/t6019-rev-list-ancestry-path.sh
@@ -95,10 +95,9 @@ test_expect_success 'rev-list --ancestry-path F...I' '
# G.t is dropped in an "-s ours" merge
test_expect_success 'rev-list G..M -- G.t' '
- >expect &&
git rev-list --format=%s G..M -- G.t |
sed -e "/^commit /d" >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'rev-list --ancestry-path G..M -- G.t' '
diff --git a/t/t6020-merge-df.sh b/t/t6020-merge-df.sh
index 2af1beec5f..46b506b3b7 100755
--- a/t/t6020-merge-df.sh
+++ b/t/t6020-merge-df.sh
@@ -89,9 +89,6 @@ test_expect_success 'modify/delete + directory/file conflict' '
'
test_expect_success 'modify/delete + directory/file conflict; other way' '
- # Yes, we really need the double reset since "letters" appears as
- # both a file and a directory.
- git reset --hard &&
git reset --hard &&
git clean -f &&
git checkout modify^0 &&
diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh
index b760c223c6..53cc9b2ffb 100755
--- a/t/t6022-merge-rename.sh
+++ b/t/t6022-merge-rename.sh
@@ -893,8 +893,7 @@ test_expect_success 'do not follow renames for empty files' '
git mv empty1 empty2 &&
git commit -m rename &&
test_must_fail git merge empty-base &&
- >expect &&
- test_cmp expect empty2
+ test_must_be_empty empty2
'
test_done
diff --git a/t/t6024-recursive-merge.sh b/t/t6024-recursive-merge.sh
index 3f59e58dfb..27c7de90ce 100755
--- a/t/t6024-recursive-merge.sh
+++ b/t/t6024-recursive-merge.sh
@@ -60,9 +60,9 @@ git update-index a1 &&
GIT_AUTHOR_DATE="2006-12-12 23:00:08" git commit -m F
'
-test_expect_success "combined merge conflicts" "
- test_must_fail git merge -m final G
-"
+test_expect_success 'combined merge conflicts' '
+ test_must_fail env GIT_TEST_COMMIT_GRAPH=0 git merge -m final G
+'
cat > expect << EOF
<<<<<<< HEAD
diff --git a/t/t6029-merge-subtree.sh b/t/t6029-merge-subtree.sh
index 3e692454a7..793f0c8bf3 100755
--- a/t/t6029-merge-subtree.sh
+++ b/t/t6029-merge-subtree.sh
@@ -29,6 +29,34 @@ test_expect_success 'subtree available and works like recursive' '
'
+test_expect_success 'setup branch sub' '
+ git checkout --orphan sub &&
+ git rm -rf . &&
+ test_commit foo
+'
+
+test_expect_success 'setup branch main' '
+ git checkout -b main master &&
+ git merge -s ours --no-commit --allow-unrelated-histories sub &&
+ git read-tree --prefix=dir/ -u sub &&
+ git commit -m "initial merge of sub into main" &&
+ test_path_is_file dir/foo.t &&
+ test_path_is_file hello
+'
+
+test_expect_success 'update branch sub' '
+ git checkout sub &&
+ test_commit bar
+'
+
+test_expect_success 'update branch main' '
+ git checkout main &&
+ git merge -s subtree sub -m "second merge of sub into main" &&
+ test_path_is_file dir/bar.t &&
+ test_path_is_file dir/foo.t &&
+ test_path_is_file hello
+'
+
test_expect_success 'setup' '
mkdir git-gui &&
cd git-gui &&
@@ -55,7 +83,7 @@ test_expect_success 'initial merge' '
git checkout -b work &&
git ls-files -s >actual &&
(
- echo "100644 $o1 0 git-gui/git-gui.sh"
+ echo "100644 $o1 0 git-gui/git-gui.sh" &&
echo "100644 $o2 0 git.c"
) >expected &&
test_cmp expected actual
@@ -72,7 +100,7 @@ test_expect_success 'merge update' '
git pull -s subtree gui master2 &&
git ls-files -s >actual &&
(
- echo "100644 $o3 0 git-gui/git-gui.sh"
+ echo "100644 $o3 0 git-gui/git-gui.sh" &&
echo "100644 $o2 0 git.c"
) >expected &&
test_cmp expected actual
@@ -88,8 +116,8 @@ test_expect_success 'initial ambiguous subtree' '
git checkout -b work2 &&
git ls-files -s >actual &&
(
- echo "100644 $o1 0 git-gui/git-gui.sh"
- echo "100644 $o1 0 git-gui2/git-gui.sh"
+ echo "100644 $o1 0 git-gui/git-gui.sh" &&
+ echo "100644 $o1 0 git-gui2/git-gui.sh" &&
echo "100644 $o2 0 git.c"
) >expected &&
test_cmp expected actual
@@ -101,8 +129,8 @@ test_expect_success 'merge using explicit' '
git pull -Xsubtree=git-gui gui master2 &&
git ls-files -s >actual &&
(
- echo "100644 $o3 0 git-gui/git-gui.sh"
- echo "100644 $o1 0 git-gui2/git-gui.sh"
+ echo "100644 $o3 0 git-gui/git-gui.sh" &&
+ echo "100644 $o1 0 git-gui2/git-gui.sh" &&
echo "100644 $o2 0 git.c"
) >expected &&
test_cmp expected actual
@@ -114,8 +142,8 @@ test_expect_success 'merge2 using explicit' '
git pull -Xsubtree=git-gui2 gui master2 &&
git ls-files -s >actual &&
(
- echo "100644 $o1 0 git-gui/git-gui.sh"
- echo "100644 $o3 0 git-gui2/git-gui.sh"
+ echo "100644 $o1 0 git-gui/git-gui.sh" &&
+ echo "100644 $o3 0 git-gui2/git-gui.sh" &&
echo "100644 $o2 0 git.c"
) >expected &&
test_cmp expected actual
diff --git a/t/t6036-recursive-corner-cases.sh b/t/t6036-recursive-corner-cases.sh
index b5621303d6..59e52c5a09 100755
--- a/t/t6036-recursive-corner-cases.sh
+++ b/t/t6036-recursive-corner-cases.sh
@@ -72,7 +72,7 @@ test_expect_success 'merge simple rename+criss-cross with no modifications' '
git rev-parse >actual \
:2:three :3:three &&
git hash-object >>actual \
- three~HEAD three~R2^0
+ three~HEAD three~R2^0 &&
test_cmp expect actual
)
'
@@ -148,7 +148,7 @@ test_expect_success 'merge criss-cross + rename merges with basic modification'
git rev-parse >actual \
:2:three :3:three &&
git hash-object >>actual \
- three~HEAD three~R2^0
+ three~HEAD three~R2^0 &&
test_cmp expect actual
)
'
@@ -228,7 +228,7 @@ test_expect_success 'git detects differently handled merges conflict' '
D:new_a E:new_a &&
git rev-parse >actual \
:2:new_a :3:new_a &&
- test_cmp expect actual
+ test_cmp expect actual &&
git cat-file -p B:new_a >ours &&
git cat-file -p C:new_a >theirs &&
@@ -345,40 +345,97 @@ test_expect_success 'git detects conflict merging criss-cross+modify/delete, rev
)
'
+# SORRY FOR THE SUPER LONG DESCRIPTION, BUT THIS NEXT ONE IS HAIRY
#
# criss-cross + d/f conflict via add/add:
# Commit A: Neither file 'a' nor directory 'a/' exists.
# Commit B: Introduce 'a'
# Commit C: Introduce 'a/file'
-# Commit D: Merge B & C, keeping 'a' and deleting 'a/'
-#
-# Two different later cases:
+# Commit D1: Merge B & C, keeping 'a' and deleting 'a/'
# Commit E1: Merge B & C, deleting 'a' but keeping 'a/file'
-# Commit E2: Merge B & C, deleting 'a' but keeping a slightly modified 'a/file'
#
-# B D
+# B D1 or D2
# o---o
# / \ / \
# A o X ? F
# \ / \ /
# o---o
-# C E1 or E2
+# C E1 or E2 or E3
+#
+# I'll describe D2, E2, & E3 (which are alternatives for D1 & E1) more below...
+#
+# Merging D1 & E1 requires we first create a virtual merge base X from
+# merging A & B in memory. There are several possibilities for the merge-base:
+# 1: Keep both 'a' and 'a/file' (assuming crazy filesystem allowing a tree
+# with a directory and file at same path): results in merge of D1 & E1
+# being clean with both files deleted. Bad (no conflict detected).
+# 2: Keep 'a' but not 'a/file': Merging D1 & E1 is clean and matches E1. Bad.
+# 3: Keep 'a/file' but not 'a': Merging D1 & E1 is clean and matches D1. Bad.
+# 4: Keep neither file: Merging D1 & E1 reports the D/F add/add conflict.
+#
+# So 4 sounds good for this case, but if we were to merge D1 & E3, where E3
+# is defined as:
+# Commit E3: Merge B & C, keeping modified a, and deleting a/
+# then we'd get an add/add conflict for 'a', which seems suboptimal. A little
+# creativity leads us to an alternate choice:
+# 5: Keep 'a' as 'a~$UNIQUE' and a/file; results:
+# Merge D1 & E1: rename/delete conflict for 'a'; a/file silently deleted
+# Merge D1 & E3 is clean, as expected.
#
-# Merging D & E1 requires we first create a virtual merge base X from
-# merging A & B in memory. Now, if X could keep both 'a' and 'a/file' in
-# the index, then the merge of D & E1 could be resolved cleanly with both
-# 'a' and 'a/file' removed. Since git does not currently allow creating
-# such a tree, the best we can do is have X contain both 'a~<unique>' and
-# 'a/file' resulting in the merge of D and E1 having a rename/delete
-# conflict for 'a'. (Although this merge appears to be unsolvable with git
-# currently, git could do a lot better than it currently does with these
-# d/f conflicts, which is the purpose of this test.)
+# So choice 5 at least provides some kind of conflict for the original case,
+# and can merge cleanly as expected with D1 and E3. It also made things just
+# slightly funny for merging D1 and e$, where E4 is defined as:
+# Commit E4: Merge B & C, modifying 'a' and renaming to 'a2', and deleting 'a/'
+# in this case, we'll get a rename/rename(1to2) conflict because a~$UNIQUE
+# gets renamed to 'a' in D1 and to 'a2' in E4. But that's better than having
+# two files (both 'a' and 'a2') sitting around without the user being notified
+# that we could detect they were related and need to be merged. Also, choice
+# 5 makes the handling of 'a/file' seem suboptimal. What if we were to merge
+# D2 and E4, where D2 is:
+# Commit D2: Merge B & C, renaming 'a'->'a2', keeping 'a/file'
+# This would result in a clean merge with 'a2' having three-way merged
+# contents (good), and deleting 'a/' (bad) -- it doesn't detect the
+# conflict in how the different sides treated a/file differently.
+# Continuing down the creative route:
+# 6: Keep 'a' as 'a~$UNIQUE1' and keep 'a/' as 'a~$UNIQUE2/'; results:
+# Merge D1 & E1: rename/delete conflict for 'a' and each path under 'a/'.
+# Merge D1 & E3: clean, as expected.
+# Merge D1 & E4: rename/rename(1to2) conflict on 'a' vs 'a2'.
+# Merge D2 & E4: clean for 'a2', rename/delete for a/file
#
-# Merge of D & E2 has similar issues for path 'a', but should always result
-# in a modify/delete conflict for path 'a/file'.
+# Choice 6 could cause rename detection to take longer (providing more targets
+# that need to be searched). Also, the conflict message for each path under
+# 'a/' might be annoying unless we can detect it at the directory level, print
+# it once, and then suppress it for individual filepaths underneath.
#
-# We run each merge in both directions, to check for directional issues
-# with D/F conflict handling.
+#
+# As of time of writing, git uses choice 5. Directory rename detection and
+# rename detection performance improvements might make choice 6 a desirable
+# improvement. But we can at least document where we fall short for now...
+#
+#
+# Historically, this testcase also used:
+# Commit E2: Merge B & C, deleting 'a' but keeping slightly modified 'a/file'
+# The merge of D1 & E2 is very similar to D1 & E1 -- it has similar issues for
+# path 'a', but should always result in a modify/delete conflict for path
+# 'a/file'. These tests ran the two merges
+# D1 & E1
+# D1 & E2
+# in both directions, to check for directional issues with D/F conflict
+# handling. Later we added
+# D1 & E3
+# D1 & E4
+# D2 & E4
+# for good measure, though we only ran those one way because we had pretty
+# good confidence in merge-recursive's directional handling of D/F issues.
+#
+# Just to summarize all the intermediate merge commits:
+# Commit D1: Merge B & C, keeping a and deleting a/
+# Commit D2: Merge B & C, renaming a->a2, keeping a/file
+# Commit E1: Merge B & C, deleting a but keeping a/file
+# Commit E2: Merge B & C, deleting a but keeping slightly modified a/file
+# Commit E3: Merge B & C, keeping modified a, and deleting a/
+# Commit E4: Merge B & C, modifying 'a' and renaming to 'a2', and deleting 'a/'
#
test_expect_success 'setup differently handled merges of directory/file conflict' '
@@ -395,56 +452,70 @@ test_expect_success 'setup differently handled merges of directory/file conflict
git branch B &&
git checkout -b C &&
mkdir a &&
- echo 10 >a/file &&
+ test_write_lines a b c d e f g >a/file &&
git add a/file &&
test_tick &&
git commit -m C &&
git checkout B &&
- echo 5 >a &&
+ test_write_lines 1 2 3 4 5 6 7 >a &&
git add a &&
test_tick &&
git commit -m B &&
git checkout B^0 &&
- test_must_fail git merge C &&
- git clean -f &&
- rm -rf a/ &&
- echo 5 >a &&
- git add a &&
- test_tick &&
- git commit -m D &&
- git tag D &&
+ git merge -s ours -m D1 C^0 &&
+ git tag D1 &&
+
+ git checkout B^0 &&
+ test_must_fail git merge C^0 &&
+ git clean -fd &&
+ git rm -rf a/ &&
+ git rm a &&
+ git cat-file -p B:a >a2 &&
+ git add a2 &&
+ git commit -m D2 &&
+ git tag D2 &&
git checkout C^0 &&
- test_must_fail git merge B &&
- git clean -f &&
- git rm --cached a &&
- echo 10 >a/file &&
- git add a/file &&
- test_tick &&
- git commit -m E1 &&
+ git merge -s ours -m E1 B^0 &&
git tag E1 &&
git checkout C^0 &&
- test_must_fail git merge B &&
- git clean -f &&
- git rm --cached a &&
- printf "10\n11\n" >a/file &&
+ git merge -s ours -m E2 B^0 &&
+ test_write_lines a b c d e f g h >a/file &&
git add a/file &&
- test_tick &&
- git commit -m E2 &&
- git tag E2
+ git commit --amend -C HEAD &&
+ git tag E2 &&
+
+ git checkout C^0 &&
+ test_must_fail git merge B^0 &&
+ git clean -fd &&
+ git rm -rf a/ &&
+ test_write_lines 1 2 3 4 5 6 7 8 >a &&
+ git add a &&
+ git commit -m E3 &&
+ git tag E3 &&
+
+ git checkout C^0 &&
+ test_must_fail git merge B^0 &&
+ git clean -fd &&
+ git rm -rf a/ &&
+ git rm a &&
+ test_write_lines 1 2 3 4 5 6 7 8 >a2 &&
+ git add a2 &&
+ git commit -m E4 &&
+ git tag E4
)
'
-test_expect_success 'merge of D & E1 fails but has appropriate contents' '
+test_expect_success 'merge of D1 & E1 fails but has appropriate contents' '
test_when_finished "git -C directory-file reset --hard" &&
test_when_finished "git -C directory-file clean -fdqx" &&
(
cd directory-file &&
- git checkout D^0 &&
+ git checkout D1^0 &&
test_must_fail git merge -s recursive E1^0 &&
@@ -463,7 +534,7 @@ test_expect_success 'merge of D & E1 fails but has appropriate contents' '
)
'
-test_expect_success 'merge of E1 & D fails but has appropriate contents' '
+test_expect_success 'merge of E1 & D1 fails but has appropriate contents' '
test_when_finished "git -C directory-file reset --hard" &&
test_when_finished "git -C directory-file clean -fdqx" &&
(
@@ -471,7 +542,7 @@ test_expect_success 'merge of E1 & D fails but has appropriate contents' '
git checkout E1^0 &&
- test_must_fail git merge -s recursive D^0 &&
+ test_must_fail git merge -s recursive D1^0 &&
git ls-files -s >out &&
test_line_count = 2 out &&
@@ -488,13 +559,13 @@ test_expect_success 'merge of E1 & D fails but has appropriate contents' '
)
'
-test_expect_success 'merge of D & E2 fails but has appropriate contents' '
+test_expect_success 'merge of D1 & E2 fails but has appropriate contents' '
test_when_finished "git -C directory-file reset --hard" &&
test_when_finished "git -C directory-file clean -fdqx" &&
(
cd directory-file &&
- git checkout D^0 &&
+ git checkout D1^0 &&
test_must_fail git merge -s recursive E2^0 &&
@@ -506,16 +577,16 @@ test_expect_success 'merge of D & E2 fails but has appropriate contents' '
test_line_count = 2 out &&
git rev-parse >expect \
- B:a E2:a/file c:a/file A:ignore-me &&
+ B:a E2:a/file C:a/file A:ignore-me &&
git rev-parse >actual \
:2:a :3:a/file :1:a/file :0:ignore-me &&
- test_cmp expect actual
+ test_cmp expect actual &&
test_path_is_file a~HEAD
)
'
-test_expect_success 'merge of E2 & D fails but has appropriate contents' '
+test_expect_success 'merge of E2 & D1 fails but has appropriate contents' '
test_when_finished "git -C directory-file reset --hard" &&
test_when_finished "git -C directory-file clean -fdqx" &&
(
@@ -523,7 +594,7 @@ test_expect_success 'merge of E2 & D fails but has appropriate contents' '
git checkout E2^0 &&
- test_must_fail git merge -s recursive D^0 &&
+ test_must_fail git merge -s recursive D1^0 &&
git ls-files -s >out &&
test_line_count = 4 out &&
@@ -533,12 +604,87 @@ test_expect_success 'merge of E2 & D fails but has appropriate contents' '
test_line_count = 2 out &&
git rev-parse >expect \
- B:a E2:a/file c:a/file A:ignore-me &&
+ B:a E2:a/file C:a/file A:ignore-me &&
git rev-parse >actual \
:3:a :2:a/file :1:a/file :0:ignore-me &&
+ test_cmp expect actual &&
+
+ test_path_is_file a~D1^0
+ )
+'
+
+test_expect_success 'merge of D1 & E3 succeeds' '
+ test_when_finished "git -C directory-file reset --hard" &&
+ test_when_finished "git -C directory-file clean -fdqx" &&
+ (
+ cd directory-file &&
+
+ git checkout D1^0 &&
+
+ git merge -s recursive E3^0 &&
+
+ git ls-files -s >out &&
+ test_line_count = 2 out &&
+ git ls-files -u >out &&
+ test_line_count = 0 out &&
+ git ls-files -o >out &&
+ test_line_count = 1 out &&
+
+ git rev-parse >expect \
+ A:ignore-me E3:a &&
+ git rev-parse >actual \
+ :0:ignore-me :0:a &&
test_cmp expect actual
+ )
+'
- test_path_is_file a~D^0
+test_expect_success 'merge of D1 & E4 notifies user a and a2 are related' '
+ test_when_finished "git -C directory-file reset --hard" &&
+ test_when_finished "git -C directory-file clean -fdqx" &&
+ (
+ cd directory-file &&
+
+ git checkout D1^0 &&
+
+ test_must_fail git merge -s recursive E4^0 &&
+
+ git ls-files -s >out &&
+ test_line_count = 4 out &&
+ git ls-files -u >out &&
+ test_line_count = 3 out &&
+ git ls-files -o >out &&
+ test_line_count = 1 out &&
+
+ git rev-parse >expect \
+ A:ignore-me B:a D1:a E4:a2 &&
+ git rev-parse >actual \
+ :0:ignore-me :1:a~Temporary\ merge\ branch\ 2 :2:a :3:a2 &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_failure 'merge of D2 & E4 merges a2s & reports conflict for a/file' '
+ test_when_finished "git -C directory-file reset --hard" &&
+ test_when_finished "git -C directory-file clean -fdqx" &&
+ (
+ cd directory-file &&
+
+ git checkout D2^0 &&
+
+ test_must_fail git merge -s recursive E4^0 &&
+
+ git ls-files -s >out &&
+ test_line_count = 3 out &&
+ git ls-files -u >out &&
+ test_line_count = 1 out &&
+ git ls-files -o >out &&
+ test_line_count = 1 out &&
+
+ git rev-parse >expect \
+ A:ignore-me E4:a2 D2:a/file &&
+ git rev-parse >actual \
+ :0:ignore-me :0:a2 :2:a/file &&
+ test_cmp expect actual
)
'
@@ -805,4 +951,455 @@ test_expect_success 'virtual merge base handles rename/rename(1to2)/add-dest' '
)
'
+#
+# criss-cross with modify/modify on a symlink:
+#
+# B D
+# o---o
+# / \ / \
+# A o X ? F
+# \ / \ /
+# o---o
+# C E
+#
+# Commit A: simple simlink fickle->lagoon
+# Commit B: redirect fickle->disneyland
+# Commit C: redirect fickle->home
+# Commit D: merge B&C, resolving in favor of B
+# Commit E: merge B&C, resolving in favor of C
+#
+# This is an obvious modify/modify conflict for the symlink 'fickle'. Can
+# git detect it?
+
+test_expect_success 'setup symlink modify/modify' '
+ test_create_repo symlink-modify-modify &&
+ (
+ cd symlink-modify-modify &&
+
+ test_ln_s_add lagoon fickle &&
+ git commit -m A &&
+ git tag A &&
+
+ git checkout -b B A &&
+ git rm fickle &&
+ test_ln_s_add disneyland fickle &&
+ git commit -m B &&
+
+ git checkout -b C A &&
+ git rm fickle &&
+ test_ln_s_add home fickle &&
+ git add fickle &&
+ git commit -m C &&
+
+ git checkout -q B^0 &&
+ git merge -s ours -m D C^0 &&
+ git tag D &&
+
+ git checkout -q C^0 &&
+ git merge -s ours -m E B^0 &&
+ git tag E
+ )
+'
+
+test_expect_failure 'check symlink modify/modify' '
+ (
+ cd symlink-modify-modify &&
+
+ git checkout D^0 &&
+
+ test_must_fail git merge -s recursive E^0 &&
+
+ git ls-files -s >out &&
+ test_line_count = 3 out &&
+ git ls-files -u >out &&
+ test_line_count = 3 out &&
+ git ls-files -o >out &&
+ test_line_count = 1 out
+ )
+'
+
+#
+# criss-cross with add/add of a symlink:
+#
+# B D
+# o---o
+# / \ / \
+# A o X ? F
+# \ / \ /
+# o---o
+# C E
+#
+# Commit A: No symlink or path exists yet
+# Commit B: set up symlink: fickle->disneyland
+# Commit C: set up symlink: fickle->home
+# Commit D: merge B&C, resolving in favor of B
+# Commit E: merge B&C, resolving in favor of C
+#
+# This is an obvious add/add conflict for the symlink 'fickle'. Can
+# git detect it?
+
+test_expect_success 'setup symlink add/add' '
+ test_create_repo symlink-add-add &&
+ (
+ cd symlink-add-add &&
+
+ touch ignoreme &&
+ git add ignoreme &&
+ git commit -m A &&
+ git tag A &&
+
+ git checkout -b B A &&
+ test_ln_s_add disneyland fickle &&
+ git commit -m B &&
+
+ git checkout -b C A &&
+ test_ln_s_add home fickle &&
+ git add fickle &&
+ git commit -m C &&
+
+ git checkout -q B^0 &&
+ git merge -s ours -m D C^0 &&
+ git tag D &&
+
+ git checkout -q C^0 &&
+ git merge -s ours -m E B^0 &&
+ git tag E
+ )
+'
+
+test_expect_failure 'check symlink add/add' '
+ (
+ cd symlink-add-add &&
+
+ git checkout D^0 &&
+
+ test_must_fail git merge -s recursive E^0 &&
+
+ git ls-files -s >out &&
+ test_line_count = 2 out &&
+ git ls-files -u >out &&
+ test_line_count = 2 out &&
+ git ls-files -o >out &&
+ test_line_count = 1 out
+ )
+'
+
+#
+# criss-cross with modify/modify on a submodule:
+#
+# B D
+# o---o
+# / \ / \
+# A o X ? F
+# \ / \ /
+# o---o
+# C E
+#
+# Commit A: simple submodule repo
+# Commit B: update repo
+# Commit C: update repo differently
+# Commit D: merge B&C, resolving in favor of B
+# Commit E: merge B&C, resolving in favor of C
+#
+# This is an obvious modify/modify conflict for the submodule 'repo'. Can
+# git detect it?
+
+test_expect_success 'setup submodule modify/modify' '
+ test_create_repo submodule-modify-modify &&
+ (
+ cd submodule-modify-modify &&
+
+ test_create_repo submod &&
+ (
+ cd submod &&
+ touch file-A &&
+ git add file-A &&
+ git commit -m A &&
+ git tag A &&
+
+ git checkout -b B A &&
+ touch file-B &&
+ git add file-B &&
+ git commit -m B &&
+ git tag B &&
+
+ git checkout -b C A &&
+ touch file-C &&
+ git add file-C &&
+ git commit -m C &&
+ git tag C
+ ) &&
+
+ git -C submod reset --hard A &&
+ git add submod &&
+ git commit -m A &&
+ git tag A &&
+
+ git checkout -b B A &&
+ git -C submod reset --hard B &&
+ git add submod &&
+ git commit -m B &&
+
+ git checkout -b C A &&
+ git -C submod reset --hard C &&
+ git add submod &&
+ git commit -m C &&
+
+ git checkout -q B^0 &&
+ git merge -s ours -m D C^0 &&
+ git tag D &&
+
+ git checkout -q C^0 &&
+ git merge -s ours -m E B^0 &&
+ git tag E
+ )
+'
+
+test_expect_failure 'check submodule modify/modify' '
+ (
+ cd submodule-modify-modify &&
+
+ git checkout D^0 &&
+
+ test_must_fail git merge -s recursive E^0 &&
+
+ git ls-files -s >out &&
+ test_line_count = 3 out &&
+ git ls-files -u >out &&
+ test_line_count = 3 out &&
+ git ls-files -o >out &&
+ test_line_count = 1 out
+ )
+'
+
+#
+# criss-cross with add/add on a submodule:
+#
+# B D
+# o---o
+# / \ / \
+# A o X ? F
+# \ / \ /
+# o---o
+# C E
+#
+# Commit A: nothing of note
+# Commit B: introduce submodule repo
+# Commit C: introduce submodule repo at different commit
+# Commit D: merge B&C, resolving in favor of B
+# Commit E: merge B&C, resolving in favor of C
+#
+# This is an obvious add/add conflict for the submodule 'repo'. Can
+# git detect it?
+
+test_expect_success 'setup submodule add/add' '
+ test_create_repo submodule-add-add &&
+ (
+ cd submodule-add-add &&
+
+ test_create_repo submod &&
+ (
+ cd submod &&
+ touch file-A &&
+ git add file-A &&
+ git commit -m A &&
+ git tag A &&
+
+ git checkout -b B A &&
+ touch file-B &&
+ git add file-B &&
+ git commit -m B &&
+ git tag B &&
+
+ git checkout -b C A &&
+ touch file-C &&
+ git add file-C &&
+ git commit -m C &&
+ git tag C
+ ) &&
+
+ touch irrelevant-file &&
+ git add irrelevant-file &&
+ git commit -m A &&
+ git tag A &&
+
+ git checkout -b B A &&
+ git -C submod reset --hard B &&
+ git add submod &&
+ git commit -m B &&
+
+ git checkout -b C A &&
+ git -C submod reset --hard C &&
+ git add submod &&
+ git commit -m C &&
+
+ git checkout -q B^0 &&
+ git merge -s ours -m D C^0 &&
+ git tag D &&
+
+ git checkout -q C^0 &&
+ git merge -s ours -m E B^0 &&
+ git tag E
+ )
+'
+
+test_expect_failure 'check submodule add/add' '
+ (
+ cd submodule-add-add &&
+
+ git checkout D^0 &&
+
+ test_must_fail git merge -s recursive E^0 &&
+
+ git ls-files -s >out &&
+ test_line_count = 3 out &&
+ git ls-files -u >out &&
+ test_line_count = 2 out &&
+ git ls-files -o >out &&
+ test_line_count = 1 out
+ )
+'
+
+#
+# criss-cross with conflicting entry types:
+#
+# B D
+# o---o
+# / \ / \
+# A o X ? F
+# \ / \ /
+# o---o
+# C E
+#
+# Commit A: nothing of note
+# Commit B: introduce submodule 'path'
+# Commit C: introduce symlink 'path'
+# Commit D: merge B&C, resolving in favor of B
+# Commit E: merge B&C, resolving in favor of C
+#
+# 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 &&
+ (
+ cd submodule-symlink-add-add &&
+
+ test_create_repo path &&
+ (
+ cd path &&
+ touch file-B &&
+ git add file-B &&
+ git commit -m B &&
+ git tag B
+ ) &&
+
+ touch irrelevant-file &&
+ git add irrelevant-file &&
+ git commit -m A &&
+ git tag A &&
+
+ git checkout -b B A &&
+ git -C path reset --hard B &&
+ git add path &&
+ git commit -m B &&
+
+ git checkout -b C A &&
+ rm -rf path/ &&
+ test_ln_s_add irrelevant-file path &&
+ git commit -m C &&
+
+ git checkout -q B^0 &&
+ git merge -s ours -m D C^0 &&
+ git tag D &&
+
+ git checkout -q C^0 &&
+ git merge -s ours -m E B^0 &&
+ git tag E
+ )
+'
+
+test_expect_failure 'check conflicting entry types (submodule vs symlink)' '
+ (
+ cd submodule-symlink-add-add &&
+
+ git checkout D^0 &&
+
+ test_must_fail git merge -s recursive E^0 &&
+
+ git ls-files -s >out &&
+ test_line_count = 3 out &&
+ git ls-files -u >out &&
+ test_line_count = 2 out &&
+ git ls-files -o >out &&
+ test_line_count = 1 out
+ )
+'
+
+#
+# criss-cross with regular files that have conflicting modes:
+#
+# B D
+# o---o
+# / \ / \
+# A o X ? F
+# \ / \ /
+# o---o
+# C E
+#
+# Commit A: nothing of note
+# Commit B: introduce file source_me.bash, not executable
+# Commit C: introduce file source_me.bash, executable
+# Commit D: merge B&C, resolving in favor of B
+# Commit E: merge B&C, resolving in favor of C
+#
+# 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 &&
+ (
+ cd regular-file-mode-conflict &&
+
+ touch irrelevant-file &&
+ git add irrelevant-file &&
+ git commit -m A &&
+ git tag A &&
+
+ git checkout -b B A &&
+ echo "command_to_run" >source_me.bash &&
+ git add source_me.bash &&
+ git commit -m B &&
+
+ git checkout -b C A &&
+ echo "command_to_run" >source_me.bash &&
+ git add source_me.bash &&
+ test_chmod +x source_me.bash &&
+ git commit -m C &&
+
+ git checkout -q B^0 &&
+ git merge -s ours -m D C^0 &&
+ git tag D &&
+
+ git checkout -q C^0 &&
+ git merge -s ours -m E B^0 &&
+ git tag E
+ )
+'
+
+test_expect_failure 'check conflicting modes for regular file' '
+ (
+ cd regular-file-mode-conflict &&
+
+ git checkout D^0 &&
+
+ test_must_fail git merge -s recursive E^0 &&
+
+ git ls-files -s >out &&
+ test_line_count = 3 out &&
+ git ls-files -u >out &&
+ test_line_count = 2 out &&
+ git ls-files -o >out &&
+ test_line_count = 1 out
+ )
+'
+
test_done
diff --git a/t/t6042-merge-rename-corner-cases.sh b/t/t6042-merge-rename-corner-cases.sh
index 1cbd946fc2..b97aca7fa2 100755
--- a/t/t6042-merge-rename-corner-cases.sh
+++ b/t/t6042-merge-rename-corner-cases.sh
@@ -324,7 +324,6 @@ test_expect_success 'rename/directory conflict + content merge conflict' '
cd rename-directory-1 &&
git reset --hard &&
- git reset --hard &&
git clean -fdqx &&
git checkout left-conflict^0 &&
@@ -352,7 +351,7 @@ test_expect_success 'rename/directory conflict + content merge conflict' '
base:file left-conflict:newfile right:file &&
git rev-parse >actual \
:1:newfile :2:newfile :3:newfile &&
- test_cmp expect actual
+ test_cmp expect actual &&
test_path_is_file newfile/realfile &&
test_path_is_file newfile~HEAD
@@ -580,7 +579,7 @@ test_expect_failure 'detect conflict with rename/rename(1to2)/add-source merge'
C:a A:a B:b C:C &&
git rev-parse >actual \
:3:a :1:a :2:b :3:c &&
- test_cmp expect actual
+ test_cmp expect actual &&
test_path_is_file a &&
test_path_is_file b &&
@@ -680,17 +679,262 @@ test_expect_success 'rename/rename/add-dest merge still knows about conflicting
A:a C:b B:b C:c B:c &&
git rev-parse >actual \
:1:a :2:b :3:b :2:c :3:c &&
- test_cmp expect actual
+ test_cmp expect actual &&
git rev-parse >expect \
C:c B:c C:b B:b &&
git hash-object >actual \
c~HEAD c~B\^0 b~HEAD b~B\^0 &&
- test_cmp expect actual
+ test_cmp expect actual &&
test_path_is_missing b &&
test_path_is_missing c
)
'
+# Testcase rad, rename/add/delete
+# Commit O: foo
+# Commit A: rm foo, add different bar
+# Commit B: rename foo->bar
+# Expected: CONFLICT (rename/add/delete), two-way merged bar
+
+test_expect_success 'rad-setup: rename/add/delete conflict' '
+ test_create_repo 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"
+ )
+'
+
+test_expect_failure 'rad-check: rename/add/delete conflict' '
+ (
+ cd rad &&
+
+ git checkout B^0 &&
+ test_must_fail git merge -s recursive A^0 >out 2>err &&
+
+ # Not sure whether the output should contain just one
+ # "CONFLICT (rename/add/delete)" line, or if it should break
+ # it into a pair of "CONFLICT (rename/delete)" and
+ # "CONFLICT (rename/add)"; allow for either.
+ test_i18ngrep "CONFLICT (rename.*add)" out &&
+ test_i18ngrep "CONFLICT (rename.*delete)" out &&
+ test_must_be_empty err &&
+
+ git ls-files -s >file_count &&
+ test_line_count = 2 file_count &&
+ git ls-files -u >file_count &&
+ test_line_count = 2 file_count &&
+ git ls-files -o >file_count &&
+ test_line_count = 2 file_count &&
+
+ git rev-parse >actual \
+ :2:bar :3:bar &&
+ git rev-parse >expect \
+ B:bar A:bar &&
+
+ test_cmp file_is_missing foo &&
+ # bar should have two-way merged contents of the different
+ # versions of bar; check that content from both sides is
+ # present.
+ grep original bar &&
+ grep different bar
+ )
+'
+
+# Testcase rrdd, rename/rename(2to1)/delete/delete
+# Commit O: foo, bar
+# Commit A: rename foo->baz, rm bar
+# Commit B: rename bar->baz, rm foo
+# Expected: CONFLICT (rename/rename/delete/delete), two-way merged baz
+
+test_expect_success 'rrdd-setup: rename/rename(2to1)/delete/delete conflict' '
+ test_create_repo 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"
+ )
+'
+
+test_expect_failure 'rrdd-check: rename/rename(2to1)/delete/delete conflict' '
+ (
+ cd rrdd &&
+
+ git checkout A^0 &&
+ test_must_fail git merge -s recursive B^0 >out 2>err &&
+
+ # Not sure whether the output should contain just one
+ # "CONFLICT (rename/rename/delete/delete)" line, or if it
+ # should break it into three: "CONFLICT (rename/rename)" and
+ # two "CONFLICT (rename/delete)" lines; allow for either.
+ test_i18ngrep "CONFLICT (rename/rename)" out &&
+ test_i18ngrep "CONFLICT (rename.*delete)" out &&
+ test_must_be_empty err &&
+
+ git ls-files -s >file_count &&
+ test_line_count = 2 file_count &&
+ git ls-files -u >file_count &&
+ test_line_count = 2 file_count &&
+ git ls-files -o >file_count &&
+ test_line_count = 2 file_count &&
+
+ git rev-parse >actual \
+ :2:baz :3:baz &&
+ git rev-parse >expect \
+ O:foo O:bar &&
+
+ test_cmp file_is_missing foo &&
+ test_cmp file_is_missing bar &&
+ # baz should have two-way merged contents of the original
+ # contents of foo and bar; check that content from both sides
+ # is present.
+ grep foo baz &&
+ grep bar baz
+ )
+'
+
+# Testcase mod6, chains of rename/rename(1to2) and rename/rename(2to1)
+# Commit O: one, three, five
+# Commit A: one->two, three->four, five->six
+# Commit B: one->six, three->two, five->four
+# Expected: six CONFLICT(rename/rename) messages, each path in two of the
+# multi-way merged contents found in two, four, six
+
+test_expect_success 'mod6-setup: chains of rename/rename(1to2) and rename/rename(2to1)' '
+ test_create_repo 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"
+ )
+'
+
+test_expect_failure 'mod6-check: chains of rename/rename(1to2) and rename/rename(2to1)' '
+ (
+ cd mod6 &&
+
+ git checkout A^0 &&
+
+ test_must_fail git merge -s recursive B^0 >out 2>err &&
+
+ test_i18ngrep "CONFLICT (rename/rename)" out &&
+ test_must_be_empty err &&
+
+ git ls-files -s >file_count &&
+ test_line_count = 6 file_count &&
+ git ls-files -u >file_count &&
+ test_line_count = 6 file_count &&
+ git ls-files -o >file_count &&
+ test_line_count = 3 file_count &&
+
+ test_seq 10 20 >merged-one &&
+ test_seq 51 60 >merged-five &&
+ # Determine what the merge of three would give us.
+ test_seq 30 40 >three-side-A &&
+ test_seq 31 39 >three-side-B &&
+ echo forty >three-side-B &&
+ >empty &&
+ test_must_fail git merge-file \
+ -L "HEAD" \
+ -L "" \
+ -L "B^0" \
+ three-side-A empty three-side-B &&
+ sed -e "s/^\([<=>]\)/\1\1\1/" three-side-A >merged-three &&
+
+ # Verify the index is as expected
+ git rev-parse >actual \
+ :2:two :3:two \
+ :2:four :3:four \
+ :2:six :3:six &&
+ git hash-object >expect \
+ merged-one merged-three \
+ merged-three merged-five \
+ merged-five merged-one &&
+ test_cmp expect actual &&
+
+ git cat-file -p :2:two >expect &&
+ git cat-file -p :3:two >other &&
+ test_must_fail git merge-file \
+ -L "HEAD" -L "" -L "B^0" \
+ expect empty other &&
+ test_cmp expect two &&
+
+ git cat-file -p :2:four >expect &&
+ git cat-file -p :3:four >other &&
+ test_must_fail git merge-file \
+ -L "HEAD" -L "" -L "B^0" \
+ expect empty other &&
+ test_cmp expect four &&
+
+ git cat-file -p :2:six >expect &&
+ git cat-file -p :3:six >other &&
+ test_must_fail git merge-file \
+ -L "HEAD" -L "" -L "B^0" \
+ expect empty other &&
+ test_cmp expect six
+ )
+'
+
test_done
diff --git a/t/t6043-merge-rename-directories.sh b/t/t6043-merge-rename-directories.sh
index 2e28f2908d..4a71f17edd 100755
--- a/t/t6043-merge-rename-directories.sh
+++ b/t/t6043-merge-rename-directories.sh
@@ -3583,7 +3583,7 @@ test_expect_success '11d-check: Avoid losing not-uptodate with rename + D/F conf
grep -q stuff z/c &&
test_seq 1 10 >expected &&
echo stuff >>expected &&
- test_cmp expected z/c
+ test_cmp expected z/c &&
git ls-files -s >out &&
test_line_count = 4 out &&
diff --git a/t/t6044-merge-unrelated-index-changes.sh b/t/t6044-merge-unrelated-index-changes.sh
index 23b86fb977..5e3779ebc9 100755
--- a/t/t6044-merge-unrelated-index-changes.sh
+++ b/t/t6044-merge-unrelated-index-changes.sh
@@ -82,7 +82,8 @@ test_expect_success 'ff update, important file modified' '
touch subdir/e &&
git add subdir/e &&
- test_must_fail git merge E^0
+ test_must_fail git merge E^0 &&
+ test_path_is_missing .git/MERGE_HEAD
'
test_expect_success 'resolve, trivial' '
@@ -91,7 +92,8 @@ test_expect_success 'resolve, trivial' '
touch random_file && git add random_file &&
- test_must_fail git merge -s resolve C^0
+ test_must_fail git merge -s resolve C^0 &&
+ test_path_is_missing .git/MERGE_HEAD
'
test_expect_success 'resolve, non-trivial' '
@@ -100,7 +102,8 @@ test_expect_success 'resolve, non-trivial' '
touch random_file && git add random_file &&
- test_must_fail git merge -s resolve D^0
+ test_must_fail git merge -s resolve D^0 &&
+ test_path_is_missing .git/MERGE_HEAD
'
test_expect_success 'recursive' '
@@ -109,7 +112,8 @@ test_expect_success 'recursive' '
touch random_file && git add random_file &&
- test_must_fail git merge -s recursive C^0
+ test_must_fail git merge -s recursive C^0 &&
+ test_path_is_missing .git/MERGE_HEAD
'
test_expect_success 'recursive, when merge branch matches merge base' '
@@ -118,7 +122,45 @@ test_expect_success 'recursive, when merge branch matches merge base' '
touch random_file && git add random_file &&
- test_must_fail git merge -s recursive F^0
+ test_must_fail git merge -s recursive F^0 &&
+ test_path_is_missing .git/MERGE_HEAD
+'
+
+test_expect_success 'merge-recursive, when index==head but head!=HEAD' '
+ git reset --hard &&
+ git checkout C^0 &&
+
+ # Make index match B
+ git diff C B -- | git apply --cached &&
+ # Merge B & F, with B as "head"
+ git merge-recursive A -- B F > out &&
+ test_i18ngrep "Already up to date" out
+'
+
+test_expect_success 'recursive, when file has staged changes not matching HEAD nor what a merge would give' '
+ git reset --hard &&
+ git checkout B^0 &&
+
+ mkdir subdir &&
+ test_seq 1 10 >subdir/a &&
+ git add subdir/a &&
+
+ # We have staged changes; merge should error out
+ test_must_fail git merge -s recursive E^0 2>err &&
+ test_i18ngrep "changes to the following files would be overwritten" err
+'
+
+test_expect_success 'recursive, when file has staged changes matching what a merge would give' '
+ git reset --hard &&
+ git checkout B^0 &&
+
+ mkdir subdir &&
+ test_seq 1 11 >subdir/a &&
+ git add subdir/a &&
+
+ # We have staged changes; merge should error out
+ test_must_fail git merge -s recursive E^0 2>err &&
+ test_i18ngrep "changes to the following files would be overwritten" err
'
test_expect_success 'octopus, unrelated file touched' '
@@ -127,7 +169,8 @@ test_expect_success 'octopus, unrelated file touched' '
touch random_file && git add random_file &&
- test_must_fail git merge C^0 D^0
+ test_must_fail git merge C^0 D^0 &&
+ test_path_is_missing .git/MERGE_HEAD
'
test_expect_success 'octopus, related file removed' '
@@ -136,7 +179,8 @@ test_expect_success 'octopus, related file removed' '
git rm b &&
- test_must_fail git merge C^0 D^0
+ test_must_fail git merge C^0 D^0 &&
+ test_path_is_missing .git/MERGE_HEAD
'
test_expect_success 'octopus, related file modified' '
@@ -145,7 +189,8 @@ test_expect_success 'octopus, related file modified' '
echo 12 >>a && git add a &&
- test_must_fail git merge C^0 D^0
+ test_must_fail git merge C^0 D^0 &&
+ test_path_is_missing .git/MERGE_HEAD
'
test_expect_success 'ours' '
@@ -154,7 +199,8 @@ test_expect_success 'ours' '
touch random_file && git add random_file &&
- test_must_fail git merge -s ours C^0
+ test_must_fail git merge -s ours C^0 &&
+ test_path_is_missing .git/MERGE_HEAD
'
test_expect_success 'subtree' '
@@ -163,7 +209,8 @@ test_expect_success 'subtree' '
touch random_file && git add random_file &&
- test_must_fail git merge -s subtree E^0
+ test_must_fail git merge -s subtree E^0 &&
+ test_path_is_missing .git/MERGE_HEAD
'
test_done
diff --git a/t/t6050-replace.sh b/t/t6050-replace.sh
index aa3e249639..86374a9c52 100755
--- a/t/t6050-replace.sh
+++ b/t/t6050-replace.sh
@@ -113,6 +113,12 @@ test_expect_success 'test GIT_NO_REPLACE_OBJECTS env variable' '
GIT_NO_REPLACE_OBJECTS=1 git show $HASH2 | grep "A U Thor"
'
+test_expect_success 'test core.usereplacerefs config option' '
+ test_config core.usereplacerefs false &&
+ git cat-file commit $HASH2 | grep "author A U Thor" &&
+ git show $HASH2 | grep "A U Thor"
+'
+
cat >tag.sig <<EOF
object $HASH2
type commit
diff --git a/t/t6060-merge-index.sh b/t/t6060-merge-index.sh
index debadbd299..ddf34f0115 100755
--- a/t/t6060-merge-index.sh
+++ b/t/t6060-merge-index.sh
@@ -44,8 +44,7 @@ test_expect_success 'read-tree does not resolve content merge' '
test_expect_success 'git merge-index git-merge-one-file resolves' '
git merge-index git-merge-one-file -a &&
git diff-files --name-only --diff-filter=U >unmerged &&
- >expect &&
- test_cmp expect unmerged &&
+ test_must_be_empty unmerged &&
test_cmp expect-merged file &&
git cat-file blob :file >file-index &&
test_cmp expect-merged file-index
diff --git a/t/t6112-rev-list-filters-objects.sh b/t/t6112-rev-list-filters-objects.sh
index 0a37dd5f97..d4ff0b3bef 100755
--- a/t/t6112-rev-list-filters-objects.sh
+++ b/t/t6112-rev-list-filters-objects.sh
@@ -113,12 +113,11 @@ test_expect_success 'verify blob:limit=1k' '
'
test_expect_success 'verify blob:limit=1m' '
- cat </dev/null >expected &&
git -C r2 rev-list HEAD --quiet --objects --filter-print-omitted --filter=blob:limit=1m \
| awk -f print_1.awk \
| sed "s/~//" \
| sort >observed &&
- test_cmp observed expected
+ test_must_be_empty observed
'
# Test sparse:path=<path> filter.
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index 84dd1cb690..d639d94696 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -121,10 +121,9 @@ test_expect_success 'describe --contains defaults to HEAD without commit-ish' '
test_cmp expect actual
'
-: >err.expect
check_describe tags/A --all A^0
test_expect_success 'no warning was displayed for A' '
- test_cmp err.expect err.actual
+ test_must_be_empty err.actual
'
test_expect_success 'rename tag A to Q locally' '
diff --git a/t/t6130-pathspec-noglob.sh b/t/t6130-pathspec-noglob.sh
index 658353277e..37760233a5 100755
--- a/t/t6130-pathspec-noglob.sh
+++ b/t/t6130-pathspec-noglob.sh
@@ -97,9 +97,8 @@ test_expect_success 'no-glob option matches literally (bracket)' '
'
test_expect_success 'no-glob option disables :(literal)' '
- : >expect &&
git --literal-pathspecs log --format=%s -- ":(literal)foo" >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'no-glob environment variable works' '
@@ -130,9 +129,8 @@ test_expect_success '**/ works with :(glob)' '
'
test_expect_success '**/ does not work with --noglob-pathspecs' '
- : >expect &&
git --noglob-pathspecs log --format=%s -- "**/bar" >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success '**/ works with :(glob) and --noglob-pathspecs' '
@@ -154,9 +152,8 @@ test_expect_success '**/ works with --glob-pathspecs' '
'
test_expect_success '**/ does not work with :(literal) and --glob-pathspecs' '
- : >expect &&
git --glob-pathspecs log --format=%s -- ":(literal)**/bar" >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_done
diff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh
index a54a52aaa4..93f23cfa82 100755
--- a/t/t6200-fmt-merge-msg.sh
+++ b/t/t6200-fmt-merge-msg.sh
@@ -366,8 +366,6 @@ test_expect_success 'merge-msg with nothing to merge' '
test_unconfig merge.log &&
test_config merge.summary yes &&
- >empty &&
-
(
cd remote &&
git checkout -b unrelated &&
@@ -376,7 +374,7 @@ test_expect_success 'merge-msg with nothing to merge' '
git fmt-merge-msg <.git/FETCH_HEAD >../actual
) &&
- test_cmp empty actual
+ test_must_be_empty actual
'
test_expect_success 'merge-msg tag' '
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index e0496da812..97bfbee6e8 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -715,6 +715,29 @@ test_expect_success 'basic atom: head contents:trailers' '
test_cmp expect actual.clean
'
+test_expect_success 'trailer parsing not fooled by --- line' '
+ git commit --allow-empty -F - <<-\EOF &&
+ this is the subject
+
+ This is the body. The message has a "---" line which would confuse a
+ message+patch parser. But here we know we have only a commit message,
+ so we get it right.
+
+ trailer: wrong
+ ---
+ This is more body.
+
+ trailer: right
+ EOF
+
+ {
+ echo "trailer: right" &&
+ echo
+ } >expect &&
+ git for-each-ref --format="%(trailers)" refs/heads/master >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'Add symbolic ref for the following tests' '
git symbolic-ref refs/heads/sym refs/heads/master
'
@@ -796,9 +819,8 @@ test_expect_success ':remotename and :remoteref' '
'
test_expect_success 'for-each-ref --ignore-case ignores case' '
- >expect &&
git for-each-ref --format="%(refname)" refs/heads/MASTER >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
echo refs/heads/master >expect &&
git for-each-ref --format="%(refname)" --ignore-case \
diff --git a/t/t6600-test-reach.sh b/t/t6600-test-reach.sh
new file mode 100755
index 0000000000..d139a00d1d
--- /dev/null
+++ b/t/t6600-test-reach.sh
@@ -0,0 +1,242 @@
+#!/bin/sh
+
+test_description='basic commit reachability tests'
+
+. ./test-lib.sh
+
+# Construct a grid-like commit graph with points (x,y)
+# with 1 <= x <= 10, 1 <= y <= 10, where (x,y) has
+# parents (x-1, y) and (x, y-1), keeping in mind that
+# we drop a parent if a coordinate is nonpositive.
+#
+# (10,10)
+# / \
+# (10,9) (9,10)
+# / \ / \
+# (10,8) (9,9) (8,10)
+# / \ / \ / \
+# ( continued...)
+# \ / \ / \ /
+# (3,1) (2,2) (1,3)
+# \ / \ /
+# (2,1) (2,1)
+# \ /
+# (1,1)
+#
+# We use branch 'commit-x-y' to refer to (x,y).
+# This grid allows interesting reachability and
+# non-reachability queries: (x,y) can reach (x',y')
+# if and only if x' <= x and y' <= y.
+test_expect_success 'setup' '
+ for i in $(test_seq 1 10)
+ do
+ test_commit "1-$i" &&
+ git branch -f commit-1-$i
+ done &&
+ for j in $(test_seq 1 9)
+ do
+ git reset --hard commit-$j-1 &&
+ x=$(($j + 1)) &&
+ test_commit "$x-1" &&
+ git branch -f commit-$x-1 &&
+
+ for i in $(test_seq 2 10)
+ do
+ git merge commit-$j-$i -m "$x-$i" &&
+ git branch -f commit-$x-$i
+ done
+ done &&
+ git commit-graph write --reachable &&
+ mv .git/objects/info/commit-graph commit-graph-full &&
+ git show-ref -s commit-5-5 | git commit-graph write --stdin-commits &&
+ mv .git/objects/info/commit-graph commit-graph-half &&
+ git config core.commitGraph true
+'
+
+test_three_modes () {
+ test_when_finished rm -rf .git/objects/info/commit-graph &&
+ test-tool reach $1 <input >actual &&
+ test_cmp expect actual &&
+ cp commit-graph-full .git/objects/info/commit-graph &&
+ test-tool reach $1 <input >actual &&
+ test_cmp expect actual &&
+ cp commit-graph-half .git/objects/info/commit-graph &&
+ test-tool reach $1 <input >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'ref_newer:miss' '
+ cat >input <<-\EOF &&
+ A:commit-5-7
+ B:commit-4-9
+ EOF
+ echo "ref_newer(A,B):0" >expect &&
+ test_three_modes ref_newer
+'
+
+test_expect_success 'ref_newer:hit' '
+ cat >input <<-\EOF &&
+ A:commit-5-7
+ B:commit-2-3
+ EOF
+ echo "ref_newer(A,B):1" >expect &&
+ test_three_modes ref_newer
+'
+
+test_expect_success 'in_merge_bases:hit' '
+ cat >input <<-\EOF &&
+ A:commit-5-7
+ B:commit-8-8
+ EOF
+ echo "in_merge_bases(A,B):1" >expect &&
+ test_three_modes in_merge_bases
+'
+
+test_expect_success 'in_merge_bases:miss' '
+ cat >input <<-\EOF &&
+ A:commit-6-8
+ B:commit-5-9
+ EOF
+ echo "in_merge_bases(A,B):0" >expect &&
+ test_three_modes in_merge_bases
+'
+
+test_expect_success 'is_descendant_of:hit' '
+ cat >input <<-\EOF &&
+ A:commit-5-7
+ X:commit-4-8
+ X:commit-6-6
+ X:commit-1-1
+ EOF
+ echo "is_descendant_of(A,X):1" >expect &&
+ test_three_modes is_descendant_of
+'
+
+test_expect_success 'is_descendant_of:miss' '
+ cat >input <<-\EOF &&
+ A:commit-6-8
+ X:commit-5-9
+ X:commit-4-10
+ X:commit-7-6
+ EOF
+ echo "is_descendant_of(A,X):0" >expect &&
+ test_three_modes is_descendant_of
+'
+
+test_expect_success 'get_merge_bases_many' '
+ cat >input <<-\EOF &&
+ A:commit-5-7
+ X:commit-4-8
+ X:commit-6-6
+ X:commit-8-3
+ EOF
+ {
+ echo "get_merge_bases_many(A,X):" &&
+ git rev-parse commit-5-6 \
+ commit-4-7 | sort
+ } >expect &&
+ test_three_modes get_merge_bases_many
+'
+
+test_expect_success 'reduce_heads' '
+ cat >input <<-\EOF &&
+ X:commit-1-10
+ X:commit-2-8
+ X:commit-3-6
+ X:commit-4-4
+ X:commit-1-7
+ X:commit-2-5
+ X:commit-3-3
+ X:commit-5-1
+ EOF
+ {
+ echo "reduce_heads(X):" &&
+ git rev-parse commit-5-1 \
+ commit-4-4 \
+ commit-3-6 \
+ commit-2-8 \
+ commit-1-10 | sort
+ } >expect &&
+ test_three_modes reduce_heads
+'
+
+test_expect_success 'can_all_from_reach:hit' '
+ cat >input <<-\EOF &&
+ X:commit-2-10
+ X:commit-3-9
+ X:commit-4-8
+ X:commit-5-7
+ X:commit-6-6
+ X:commit-7-5
+ X:commit-8-4
+ X:commit-9-3
+ Y:commit-1-9
+ Y:commit-2-8
+ Y:commit-3-7
+ Y:commit-4-6
+ Y:commit-5-5
+ Y:commit-6-4
+ Y:commit-7-3
+ Y:commit-8-1
+ EOF
+ echo "can_all_from_reach(X,Y):1" >expect &&
+ test_three_modes can_all_from_reach
+'
+
+test_expect_success 'can_all_from_reach:miss' '
+ cat >input <<-\EOF &&
+ X:commit-2-10
+ X:commit-3-9
+ X:commit-4-8
+ X:commit-5-7
+ X:commit-6-6
+ X:commit-7-5
+ X:commit-8-4
+ X:commit-9-3
+ Y:commit-1-9
+ Y:commit-2-8
+ Y:commit-3-7
+ Y:commit-4-6
+ Y:commit-5-5
+ Y:commit-6-4
+ Y:commit-8-5
+ EOF
+ echo "can_all_from_reach(X,Y):0" >expect &&
+ test_three_modes can_all_from_reach
+'
+
+test_expect_success 'commit_contains:hit' '
+ cat >input <<-\EOF &&
+ A:commit-7-7
+ X:commit-2-10
+ X:commit-3-9
+ X:commit-4-8
+ X:commit-5-7
+ X:commit-6-6
+ X:commit-7-5
+ X:commit-8-4
+ X:commit-9-3
+ EOF
+ echo "commit_contains(_,A,X,_):1" >expect &&
+ test_three_modes commit_contains &&
+ test_three_modes commit_contains --tag
+'
+
+test_expect_success 'commit_contains:miss' '
+ cat >input <<-\EOF &&
+ A:commit-6-5
+ X:commit-2-10
+ X:commit-3-9
+ X:commit-4-8
+ X:commit-5-7
+ X:commit-6-6
+ X:commit-7-5
+ X:commit-8-4
+ X:commit-9-3
+ EOF
+ echo "commit_contains(_,A,X,_):0" >expect &&
+ test_three_modes commit_contains &&
+ test_three_modes commit_contains --tag
+'
+
+test_done
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index cc3fd2baf2..36b50d0b4c 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -384,7 +384,7 @@ test_expect_success 'mv does not complain when no .gitmodules file is found' '
entry="$(git ls-files --stage sub | cut -f 1)" &&
mkdir mod &&
git mv sub mod/sub 2>actual.err &&
- ! test -s actual.err &&
+ test_must_be_empty actual.err &&
! test -e sub &&
[ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
(
@@ -408,7 +408,7 @@ test_expect_success 'mv will error out on a modified .gitmodules file unless sta
git diff-files --quiet -- sub &&
git add .gitmodules &&
git mv sub mod/sub 2>actual.err &&
- ! test -s actual.err &&
+ test_must_be_empty actual.err &&
! test -e sub &&
[ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
(
@@ -469,7 +469,7 @@ test_expect_success 'checking out a commit before submodule moved needs manual u
git update-index --refresh &&
git diff-files --quiet -- sub .gitmodules &&
git status -s sub2 >actual &&
- ! test -s actual
+ test_must_be_empty actual
'
test_expect_success 'mv -k does not accidentally destroy submodules' '
@@ -509,7 +509,7 @@ test_expect_success 'moving nested submodules' '
touch nested_level1 &&
git init &&
git add . &&
- git commit -m "nested level 1"
+ git commit -m "nested level 1" &&
git submodule add ../sub_nested_nested &&
git commit -m "add nested level 2"
) &&
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index d7b319e919..0b01862c23 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -325,11 +325,10 @@ test_expect_success \
test_cmp expect actual
'
->expect
test_expect_success \
'listing tags using v.* should print nothing because none have v.' '
git tag -l "v.*" > actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
cat >expect <<EOF
@@ -693,9 +692,8 @@ test_expect_success \
'
test_expect_success 'The -n 100 invocation means -n --list 100, not -n100' '
- >expect &&
git tag -n 100 >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
git tag -m "A msg" 100 &&
echo "100 A msg" >expect &&
@@ -974,9 +972,8 @@ test_expect_success GPG 'verifying a proper tag with --format pass and format ac
'
test_expect_success GPG 'verifying a forged tag with --format should fail silently' '
- >expect &&
test_must_fail git tag -v --format="tagname : %(tag)" "forged-tag" >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
# blank and empty messages for signed tags:
@@ -1354,6 +1351,19 @@ test_expect_success GPG \
'test_config gpg.program echo &&
test_must_fail git tag -s -m tail tag-gpg-failure'
+# try to sign with bad user.signingkey
+test_expect_success GPGSM \
+ 'git tag -s fails if gpgsm is misconfigured (bad key)' \
+ 'test_config user.signingkey BobTheMouse &&
+ test_config gpg.format x509 &&
+ test_must_fail git tag -s -m tail tag-gpg-failure'
+
+# try to produce invalid signature
+test_expect_success GPGSM \
+ 'git tag -s fails if gpgsm is misconfigured (bad signature format)' \
+ 'test_config gpg.x509.program echo &&
+ test_config gpg.format x509 &&
+ test_must_fail git tag -s -m tail tag-gpg-failure'
# try to verify without gpg:
@@ -1382,9 +1392,8 @@ test_expect_success 'message in editor has initial comment: first line' '
test_expect_success \
'message in editor has initial comment: remainder' '
# remove commented lines from the remainder -- should be empty
- >rest.expect &&
sed -e 1d -e "/^#/d" <actual >rest.actual &&
- test_cmp rest.expect rest.actual
+ test_must_be_empty rest.actual
'
get_tag_header reuse $commit commit $time >expect
@@ -1466,19 +1475,18 @@ test_expect_success 'checking that first commit is in all tags (relative)' "
# All the --contains tests above, but with --no-contains
test_expect_success 'checking that first commit is not listed in any tag with --no-contains (hash)' "
- >expected &&
git tag -l --no-contains $hash1 v* >actual &&
- test_cmp expected actual
+ test_must_be_empty actual
"
test_expect_success 'checking that first commit is in all tags (tag)' "
git tag -l --no-contains v1.0 v* >actual &&
- test_cmp expected actual
+ test_must_be_empty actual
"
test_expect_success 'checking that first commit is in all tags (relative)' "
git tag -l --no-contains HEAD~2 v* >actual &&
- test_cmp expected actual
+ test_must_be_empty actual
"
cat > expected <<EOF
@@ -1502,12 +1510,9 @@ test_expect_success 'inverse of the last test, with --no-contains' "
test_cmp expected actual
"
-cat > expected <<EOF
-EOF
-
test_expect_success 'checking that third commit has no tags' "
git tag -l --contains $hash3 v* >actual &&
- test_cmp expected actual
+ test_must_be_empty actual
"
cat > expected <<EOF
@@ -1606,9 +1611,8 @@ test_expect_success 'checking that --contains can be used in non-list mode' '
'
test_expect_success 'checking that initial commit is in all tags with --no-contains' "
- >expected &&
git tag -l --no-contains $hash1 v* >actual &&
- test_cmp expected actual
+ test_must_be_empty actual
"
# mixing modes and options:
@@ -1905,7 +1909,6 @@ test_expect_success 'version sort with very long prerelease suffix' '
'
test_expect_success ULIMIT_STACK_SIZE '--contains and --no-contains work in a deep repo' '
- >expect &&
i=1 &&
while test $i -lt 8000
do
@@ -1920,7 +1923,7 @@ EOF"
git checkout master &&
git tag far-far-away HEAD^ &&
run_with_limited_stack git tag --contains HEAD >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
run_with_limited_stack git tag --no-contains HEAD >actual &&
test_line_count "-gt" 10 actual
'
diff --git a/t/t7006-pager.sh b/t/t7006-pager.sh
index 7541ba5edb..00e09a375c 100755
--- a/t/t7006-pager.sh
+++ b/t/t7006-pager.sh
@@ -626,12 +626,11 @@ test_expect_success TTY 'sub-commands of externals use their own pager' '
test_expect_success TTY 'external command pagers override sub-commands' '
sane_unset PAGER GIT_PAGER &&
- >expect &&
>actual &&
test_config pager.external false &&
test_config pager.log "sed s/^/log:/ >actual" &&
test_terminal git --exec-path=. external log --format=%s -1 &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'command with underscores does not complain' '
diff --git a/t/t7008-grep-binary.sh b/t/t7008-grep-binary.sh
index 615e7e0162..2d87c49b75 100755
--- a/t/t7008-grep-binary.sh
+++ b/t/t7008-grep-binary.sh
@@ -57,9 +57,8 @@ test_expect_success 'git grep -ah ina a' '
'
test_expect_success 'git grep -I ina a' '
- : >expect &&
test_must_fail git grep -I ina a >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'git grep -c ina a' '
@@ -81,9 +80,8 @@ test_expect_success 'git grep -L bar a' '
'
test_expect_success 'git grep -q ina a' '
- : >expect &&
git grep -q ina a >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'git grep -F ile a' '
diff --git a/t/t7030-verify-tag.sh b/t/t7030-verify-tag.sh
index 291a1e2b07..041e319e79 100755
--- a/t/t7030-verify-tag.sh
+++ b/t/t7030-verify-tag.sh
@@ -41,6 +41,13 @@ test_expect_success GPG 'create signed tags' '
git tag -uB7227189 -m eighth eighth-signed-alt
'
+test_expect_success GPGSM 'create signed tags x509 ' '
+ test_config gpg.format x509 &&
+ test_config user.signingkey $GIT_COMMITTER_EMAIL &&
+ echo 9 >file && test_tick && git commit -a -m "nineth gpgsm-signed" &&
+ git tag -s -m nineth nineth-signed-x509
+'
+
test_expect_success GPG 'verify and show signatures' '
(
for tag in initial second merge fourth-signed sixth-signed seventh-signed
@@ -72,6 +79,13 @@ test_expect_success GPG 'verify and show signatures' '
)
'
+test_expect_success GPGSM 'verify and show signatures x509' '
+ git verify-tag nineth-signed-x509 2>actual &&
+ grep "Good signature from" actual &&
+ ! grep "BAD signature from" actual &&
+ echo nineth-signed-x509 OK
+'
+
test_expect_success GPG 'detect fudged signature' '
git cat-file tag seventh-signed >raw &&
sed -e "/^tag / s/seventh/7th forged/" raw >forged1 &&
@@ -112,6 +126,13 @@ test_expect_success GPG 'verify signatures with --raw' '
)
'
+test_expect_success GPGSM 'verify signatures with --raw x509' '
+ git verify-tag --raw nineth-signed-x509 2>actual &&
+ grep "GOODSIG" actual &&
+ ! grep "BADSIG" actual &&
+ echo nineth-signed-x509 OK
+'
+
test_expect_success GPG 'verify multiple tags' '
tags="fourth-signed sixth-signed seventh-signed" &&
for i in $tags
@@ -125,6 +146,19 @@ test_expect_success GPG 'verify multiple tags' '
test_cmp expect.stderr actual.stderr
'
+test_expect_success GPGSM 'verify multiple tags x509' '
+ tags="seventh-signed nineth-signed-x509" &&
+ for i in $tags
+ do
+ git verify-tag -v --raw $i || return 1
+ done >expect.stdout 2>expect.stderr.1 &&
+ grep "^.GNUPG:." <expect.stderr.1 >expect.stderr &&
+ git verify-tag -v --raw $tags >actual.stdout 2>actual.stderr.1 &&
+ grep "^.GNUPG:." <actual.stderr.1 >actual.stderr &&
+ test_cmp expect.stdout actual.stdout &&
+ test_cmp expect.stderr actual.stderr
+'
+
test_expect_success GPG 'verifying tag with --format' '
cat >expect <<-\EOF &&
tagname : fourth-signed
@@ -134,9 +168,8 @@ test_expect_success GPG 'verifying tag with --format' '
'
test_expect_success GPG 'verifying a forged tag with --format should fail silently' '
- >expect &&
test_must_fail git verify-tag --format="tagname : %(tag)" $(cat forged1.tag) >actual-forged &&
- test_cmp expect actual-forged
+ test_must_be_empty actual-forged
'
test_done
diff --git a/t/t7063-status-untracked-cache.sh b/t/t7063-status-untracked-cache.sh
index c61e304e97..2da57fce7b 100755
--- a/t/t7063-status-untracked-cache.sh
+++ b/t/t7063-status-untracked-cache.sh
@@ -26,9 +26,8 @@ avoid_racy() {
}
status_is_clean() {
- >../status.expect &&
git status --porcelain >../status.actual &&
- test_cmp ../status.expect ../status.actual
+ test_must_be_empty ../status.actual
}
test_lazy_prereq UNTRACKED_CACHE '
@@ -666,7 +665,7 @@ test_expect_success 'test ident field is working' '
mkdir ../other_worktree &&
cp -R done dthree dtwo four three ../other_worktree &&
GIT_WORK_TREE=../other_worktree git status 2>../err &&
- echo "warning: Untracked cache is disabled on this system or location." >../expect &&
+ echo "warning: untracked cache is disabled on this system or location" >../expect &&
test_i18ncmp ../expect ../err
'
diff --git a/t/t7064-wtstatus-pv2.sh b/t/t7064-wtstatus-pv2.sh
index b9a86d3347..11eccc231a 100755
--- a/t/t7064-wtstatus-pv2.sh
+++ b/t/t7064-wtstatus-pv2.sh
@@ -364,11 +364,8 @@ test_expect_success 'verify upstream fields in branch header' '
test_cmp expect actual &&
## Repeat the above but without --branch.
- cat >expect <<-EOF &&
- EOF
-
git status --porcelain=v2 --untracked-files=all >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
## Test upstream-gone case. Fake this by pointing origin/master at
## a non-existing commit.
diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh
index 95653a08ca..97be0d968d 100755
--- a/t/t7102-reset.sh
+++ b/t/t7102-reset.sh
@@ -549,8 +549,7 @@ test_expect_success 'reset -N keeps removed files as intent-to-add' '
tree=$(git write-tree) &&
git ls-tree $tree new-file >actual &&
- >expect &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
git diff --name-only >actual &&
echo new-file >expect &&
@@ -563,9 +562,8 @@ test_expect_success 'reset --mixed sets up work tree' '
cd mixed_worktree &&
test_commit dummy
) &&
- : >expect &&
git --git-dir=mixed_worktree/.git --work-tree=mixed_worktree reset >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_done
diff --git a/t/t7105-reset-patch.sh b/t/t7105-reset-patch.sh
index 98b7d7b969..bd10a96727 100755
--- a/t/t7105-reset-patch.sh
+++ b/t/t7105-reset-patch.sh
@@ -19,20 +19,20 @@ test_expect_success PERL 'setup' '
test_expect_success PERL 'saying "n" does nothing' '
set_and_save_state dir/foo work work &&
- (echo n; echo n) | git reset -p &&
+ test_write_lines n n | git reset -p &&
verify_saved_state dir/foo &&
verify_saved_state bar
'
test_expect_success PERL 'git reset -p' '
- (echo n; echo y) | git reset -p >output &&
+ test_write_lines n y | git reset -p >output &&
verify_state dir/foo work head &&
verify_saved_state bar &&
test_i18ngrep "Unstage" output
'
test_expect_success PERL 'git reset -p HEAD^' '
- (echo n; echo y) | git reset -p HEAD^ >output &&
+ test_write_lines n y | git reset -p HEAD^ >output &&
verify_state dir/foo work parent &&
verify_saved_state bar &&
test_i18ngrep "Apply" output
@@ -45,20 +45,20 @@ test_expect_success PERL 'git reset -p HEAD^' '
test_expect_success PERL 'git reset -p dir' '
set_state dir/foo work work &&
- (echo y; echo n) | git reset -p dir &&
+ test_write_lines y n | git reset -p dir &&
verify_state dir/foo work head &&
verify_saved_state bar
'
test_expect_success PERL 'git reset -p -- foo (inside dir)' '
set_state dir/foo work work &&
- (echo y; echo n) | (cd dir && git reset -p -- foo) &&
+ test_write_lines y n | (cd dir && git reset -p -- foo) &&
verify_state dir/foo work head &&
verify_saved_state bar
'
test_expect_success PERL 'git reset -p HEAD^ -- dir' '
- (echo y; echo n) | git reset -p HEAD^ -- dir &&
+ test_write_lines y n | git reset -p HEAD^ -- dir &&
verify_state dir/foo work parent &&
verify_saved_state bar
'
diff --git a/t/t7106-reset-unborn-branch.sh b/t/t7106-reset-unborn-branch.sh
index 0f95f00477..ecb85c3b82 100755
--- a/t/t7106-reset-unborn-branch.sh
+++ b/t/t7106-reset-unborn-branch.sh
@@ -12,9 +12,8 @@ test_expect_success 'reset' '
git add a b &&
git reset &&
- >expect &&
git ls-files >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'reset HEAD' '
@@ -39,9 +38,8 @@ test_expect_success PERL 'reset -p' '
echo y >yes &&
git reset -p <yes >output &&
- >expect &&
git ls-files >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
test_i18ngrep "Unstage" output
'
@@ -61,9 +59,8 @@ test_expect_success 'reset --hard' '
test_when_finished "echo a >a" &&
git reset --hard &&
- >expect &&
git ls-files >actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
test_path_is_missing a
'
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index ab9da61da3..324933acfe 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -116,9 +116,8 @@ test_expect_success "checkout -m with dirty tree" '
git diff --name-status side >current.side &&
test_cmp expect.side current.side &&
- : >expect.index &&
git diff --cached >current.index &&
- test_cmp expect.index current.index
+ test_must_be_empty current.index
'
test_expect_success "checkout -m with dirty tree, renamed" '
@@ -139,7 +138,7 @@ test_expect_success "checkout -m with dirty tree, renamed" '
test_cmp expect uno &&
! test -f one &&
git diff --cached >current &&
- ! test -s current
+ test_must_be_empty current
'
@@ -163,7 +162,7 @@ test_expect_success 'checkout -m with merge conflict' '
fill d2 aT d7 aS >expect &&
test_cmp current expect &&
git diff --cached two >current &&
- ! test -s current
+ test_must_be_empty current
'
test_expect_success 'format of merge conflict from checkout -m' '
@@ -528,10 +527,10 @@ test_expect_success 'checkout with --merge' '
cat sample >filf &&
git checkout -m -- fild file filf &&
(
- echo "<<<<<<< ours"
- echo ourside
- echo "======="
- echo theirside
+ echo "<<<<<<< ours" &&
+ echo ourside &&
+ echo "=======" &&
+ echo theirside &&
echo ">>>>>>> theirs"
) >merged &&
test_cmp expect fild &&
@@ -549,12 +548,12 @@ test_expect_success 'checkout with --merge, in diff3 -m style' '
cat sample >filf &&
git checkout -m -- fild file filf &&
(
- echo "<<<<<<< ours"
- echo ourside
- echo "||||||| base"
- echo original
- echo "======="
- echo theirside
+ echo "<<<<<<< ours" &&
+ echo ourside &&
+ echo "||||||| base" &&
+ echo original &&
+ echo "=======" &&
+ echo theirside &&
echo ">>>>>>> theirs"
) >merged &&
test_cmp expect fild &&
@@ -572,10 +571,10 @@ test_expect_success 'checkout --conflict=merge, overriding config' '
cat sample >filf &&
git checkout --conflict=merge -- fild file filf &&
(
- echo "<<<<<<< ours"
- echo ourside
- echo "======="
- echo theirside
+ echo "<<<<<<< ours" &&
+ echo ourside &&
+ echo "=======" &&
+ echo theirside &&
echo ">>>>>>> theirs"
) >merged &&
test_cmp expect fild &&
@@ -593,12 +592,12 @@ test_expect_success 'checkout --conflict=diff3' '
cat sample >filf &&
git checkout --conflict=diff3 -- fild file filf &&
(
- echo "<<<<<<< ours"
- echo ourside
- echo "||||||| base"
- echo original
- echo "======="
- echo theirside
+ echo "<<<<<<< ours" &&
+ echo ourside &&
+ echo "||||||| base" &&
+ echo original &&
+ echo "=======" &&
+ echo theirside &&
echo ">>>>>>> theirs"
) >merged &&
test_cmp expect fild &&
@@ -673,7 +672,6 @@ test_expect_success 'custom merge driver with checkout -m' '
do
grep $t arm || exit 1
done
- exit 0
) &&
mv arm expect &&
diff --git a/t/t7301-clean-interactive.sh b/t/t7301-clean-interactive.sh
index 1bf9789c8a..a07e8b86de 100755
--- a/t/t7301-clean-interactive.sh
+++ b/t/t7301-clean-interactive.sh
@@ -107,7 +107,7 @@ test_expect_success 'git clean -id (filter all)' '
mkdir -p build docs &&
touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
docs/manual.txt obj.o build/lib.so &&
- (echo f; echo "*"; echo; echo c) | \
+ test_write_lines f "*" "" c |
git clean -id &&
test -f Makefile &&
test -f README &&
@@ -129,7 +129,7 @@ test_expect_success 'git clean -id (filter patterns)' '
mkdir -p build docs &&
touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
docs/manual.txt obj.o build/lib.so &&
- (echo f; echo "part3.* *.out"; echo; echo c) | \
+ test_write_lines f "part3.* *.out" "" c |
git clean -id &&
test -f Makefile &&
test -f README &&
@@ -151,7 +151,7 @@ test_expect_success 'git clean -id (filter patterns 2)' '
mkdir -p build docs &&
touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
docs/manual.txt obj.o build/lib.so &&
- (echo f; echo "* !*.out"; echo; echo c) | \
+ test_write_lines f "* !*.out" "" c |
git clean -id &&
test -f Makefile &&
test -f README &&
@@ -173,7 +173,7 @@ test_expect_success 'git clean -id (select - all)' '
mkdir -p build docs &&
touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
docs/manual.txt obj.o build/lib.so &&
- (echo s; echo "*"; echo; echo c) | \
+ test_write_lines s "*" "" c |
git clean -id &&
test -f Makefile &&
test -f README &&
@@ -195,7 +195,7 @@ test_expect_success 'git clean -id (select - none)' '
mkdir -p build docs &&
touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
docs/manual.txt obj.o build/lib.so &&
- (echo s; echo; echo c) | \
+ test_write_lines s "" c |
git clean -id &&
test -f Makefile &&
test -f README &&
@@ -217,7 +217,7 @@ test_expect_success 'git clean -id (select - number)' '
mkdir -p build docs &&
touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
docs/manual.txt obj.o build/lib.so &&
- (echo s; echo 3; echo; echo c) | \
+ test_write_lines s 3 "" c |
git clean -id &&
test -f Makefile &&
test -f README &&
@@ -239,7 +239,7 @@ test_expect_success 'git clean -id (select - number 2)' '
mkdir -p build docs &&
touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
docs/manual.txt obj.o build/lib.so &&
- (echo s; echo 2 3; echo 5; echo; echo c) | \
+ test_write_lines s "2 3" 5 "" c |
git clean -id &&
test -f Makefile &&
test -f README &&
@@ -261,7 +261,7 @@ test_expect_success 'git clean -id (select - number 3)' '
mkdir -p build docs &&
touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
docs/manual.txt obj.o build/lib.so &&
- (echo s; echo 3,4 5; echo; echo c) | \
+ test_write_lines s "3,4 5" "" c |
git clean -id &&
test -f Makefile &&
test -f README &&
@@ -282,7 +282,7 @@ test_expect_success 'git clean -id (select - filenames)' '
mkdir -p build docs &&
touch a.out foo.txt bar.txt baz.txt &&
- (echo s; echo a.out fo ba bar; echo; echo c) | \
+ test_write_lines s "a.out fo ba bar" "" c |
git clean -id &&
test -f Makefile &&
test ! -f a.out &&
@@ -298,7 +298,7 @@ test_expect_success 'git clean -id (select - range)' '
mkdir -p build docs &&
touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
docs/manual.txt obj.o build/lib.so &&
- (echo s; echo 1,3-4; echo 2; echo; echo c) | \
+ test_write_lines s "1,3-4" 2 "" c |
git clean -id &&
test -f Makefile &&
test -f README &&
@@ -320,7 +320,7 @@ test_expect_success 'git clean -id (select - range 2)' '
mkdir -p build docs &&
touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
docs/manual.txt obj.o build/lib.so &&
- (echo s; echo 4- 1; echo; echo c) | \
+ test_write_lines s "4- 1" "" c |
git clean -id &&
test -f Makefile &&
test -f README &&
@@ -342,7 +342,7 @@ test_expect_success 'git clean -id (inverse select)' '
mkdir -p build docs &&
touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
docs/manual.txt obj.o build/lib.so &&
- (echo s; echo "*"; echo -5- 1 -2; echo; echo c) | \
+ test_write_lines s "*" "-5- 1 -2" "" c |
git clean -id &&
test -f Makefile &&
test -f README &&
@@ -364,7 +364,7 @@ test_expect_success 'git clean -id (ask)' '
mkdir -p build docs &&
touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
docs/manual.txt obj.o build/lib.so &&
- (echo a; echo Y; echo y; echo no; echo yes; echo bad; echo) | \
+ test_write_lines a Y y no yes bad "" |
git clean -id &&
test -f Makefile &&
test -f README &&
@@ -386,7 +386,7 @@ test_expect_success 'git clean -id (ask - Ctrl+D)' '
mkdir -p build docs &&
touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
docs/manual.txt obj.o build/lib.so &&
- (echo a; echo Y; echo no; echo yes; echo "\04") | \
+ test_write_lines a Y no yes "\04" |
git clean -id &&
test -f Makefile &&
test -f README &&
@@ -408,8 +408,8 @@ test_expect_success 'git clean -id with prefix and path (filter)' '
mkdir -p build docs &&
touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
docs/manual.txt obj.o build/lib.so &&
- (cd build/ && \
- (echo f; echo "docs"; echo "*.h"; echo ; echo c) | \
+ (cd build/ &&
+ test_write_lines f docs "*.h" "" c |
git clean -id ..) &&
test -f Makefile &&
test -f README &&
@@ -431,9 +431,8 @@ test_expect_success 'git clean -id with prefix and path (select by name)' '
mkdir -p build docs &&
touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
docs/manual.txt obj.o build/lib.so &&
- (cd build/ && \
- (echo s; echo "../docs/"; echo "../src/part3.c"; \
- echo "../src/part4.c"; echo; echo c) | \
+ (cd build/ &&
+ test_write_lines s ../docs/ ../src/part3.c ../src/part4.c "" c |
git clean -id ..) &&
test -f Makefile &&
test -f README &&
@@ -455,8 +454,8 @@ test_expect_success 'git clean -id with prefix and path (ask)' '
mkdir -p build docs &&
touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
docs/manual.txt obj.o build/lib.so &&
- (cd build/ && \
- (echo a; echo Y; echo y; echo no; echo yes; echo bad; echo) | \
+ (cd build/ &&
+ test_write_lines a Y y no yes bad "" |
git clean -id ..) &&
test -f Makefile &&
test -f README &&
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index 48fd14fae6..c0ffc1022a 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -101,7 +101,6 @@ inspect() {
test_expect_success 'submodule add' '
echo "refs/heads/master" >expect &&
- >empty &&
(
cd addtest &&
@@ -123,7 +122,7 @@ test_expect_success 'submodule add' '
inspect addtest/submod ../.. &&
test_cmp expect heads &&
test_cmp expect head &&
- test_cmp empty untracked
+ test_must_be_empty untracked
'
test_expect_success 'setup parent and one repository' '
@@ -171,12 +170,13 @@ test_expect_success 'submodule add to .gitignored path with --force' '
test_expect_success 'submodule add to reconfigure existing submodule with --force' '
(
cd addtest-ignore &&
- git submodule add --force bogus-url submod &&
- git submodule add -b initial "$submodurl" submod-branch &&
- test "bogus-url" = "$(git config -f .gitmodules submodule.submod.url)" &&
- test "bogus-url" = "$(git config submodule.submod.url)" &&
+ bogus_url="$(pwd)/bogus-url" &&
+ git submodule add --force "$bogus_url" submod &&
+ git submodule add --force -b initial "$submodurl" submod-branch &&
+ test "$bogus_url" = "$(git config -f .gitmodules submodule.submod.url)" &&
+ test "$bogus_url" = "$(git config submodule.submod.url)" &&
# Restore the url
- git submodule add --force "$submodurl" submod
+ git submodule add --force "$submodurl" submod &&
test "$submodurl" = "$(git config -f .gitmodules submodule.submod.url)" &&
test "$submodurl" = "$(git config submodule.submod.url)"
)
@@ -188,7 +188,6 @@ test_expect_success 'submodule add --branch' '
refs/heads/initial
refs/heads/master
EOF
- >empty &&
(
cd addtest &&
@@ -201,12 +200,11 @@ test_expect_success 'submodule add --branch' '
inspect addtest/submod-branch ../.. &&
test_cmp expect-heads heads &&
test_cmp expect-head head &&
- test_cmp empty untracked
+ test_must_be_empty untracked
'
test_expect_success 'submodule add with ./ in path' '
echo "refs/heads/master" >expect &&
- >empty &&
(
cd addtest &&
@@ -218,12 +216,11 @@ test_expect_success 'submodule add with ./ in path' '
inspect addtest/dotsubmod/frotz ../../.. &&
test_cmp expect heads &&
test_cmp expect head &&
- test_cmp empty untracked
+ test_must_be_empty untracked
'
test_expect_success 'submodule add with /././ in path' '
echo "refs/heads/master" >expect &&
- >empty &&
(
cd addtest &&
@@ -235,12 +232,11 @@ test_expect_success 'submodule add with /././ in path' '
inspect addtest/dotslashdotsubmod/frotz ../../.. &&
test_cmp expect heads &&
test_cmp expect head &&
- test_cmp empty untracked
+ test_must_be_empty untracked
'
test_expect_success 'submodule add with // in path' '
echo "refs/heads/master" >expect &&
- >empty &&
(
cd addtest &&
@@ -252,12 +248,11 @@ test_expect_success 'submodule add with // in path' '
inspect addtest/slashslashsubmod/frotz ../../.. &&
test_cmp expect heads &&
test_cmp expect head &&
- test_cmp empty untracked
+ test_must_be_empty untracked
'
test_expect_success 'submodule add with /.. in path' '
echo "refs/heads/master" >expect &&
- >empty &&
(
cd addtest &&
@@ -269,12 +264,11 @@ test_expect_success 'submodule add with /.. in path' '
inspect addtest/realsubmod ../.. &&
test_cmp expect heads &&
test_cmp expect head &&
- test_cmp empty untracked
+ test_must_be_empty untracked
'
test_expect_success 'submodule add with ./, /.. and // in path' '
echo "refs/heads/master" >expect &&
- >empty &&
(
cd addtest &&
@@ -286,7 +280,7 @@ test_expect_success 'submodule add with ./, /.. and // in path' '
inspect addtest/realsubmod2 ../.. &&
test_cmp expect heads &&
test_cmp expect head &&
- test_cmp empty untracked
+ test_must_be_empty untracked
'
test_expect_success !CYGWIN 'submodule add with \\ in path' '
@@ -305,7 +299,6 @@ test_expect_success !CYGWIN 'submodule add with \\ in path' '
test_expect_success 'submodule add in subdirectory' '
echo "refs/heads/master" >expect &&
- >empty &&
mkdir addtest/sub &&
(
@@ -318,7 +311,7 @@ test_expect_success 'submodule add in subdirectory' '
inspect addtest/realsubmod3 ../.. &&
test_cmp expect heads &&
test_cmp expect head &&
- test_cmp empty untracked
+ test_must_be_empty untracked
'
test_expect_success 'submodule add in subdirectory with relative path should fail' '
@@ -377,7 +370,7 @@ test_expect_success 'init should register submodule url in .git/config' '
test_failure_with_unknown_submodule () {
test_must_fail git submodule $1 no-such-submodule 2>output.err &&
- grep "^error: .*no-such-submodule" output.err
+ test_i18ngrep "^error: .*no-such-submodule" output.err
}
test_expect_success 'init should fail with unknown submodule' '
@@ -501,8 +494,6 @@ test_expect_success 'checkout superproject with subproject already present' '
'
test_expect_success 'apply submodule diff' '
- >empty &&
-
git branch second &&
(
cd init &&
@@ -517,7 +508,7 @@ test_expect_success 'apply submodule diff' '
git apply --index P.diff &&
git diff --cached master >staged &&
- test_cmp empty staged
+ test_must_be_empty staged
'
test_expect_success 'update --init' '
@@ -818,7 +809,7 @@ test_expect_success '../bar/a/b/c works with relative local path - ../foo/bar.gi
cp pristine-.git-config .git/config &&
cp pristine-.gitmodules .gitmodules &&
mkdir -p a/b/c &&
- (cd a/b/c; git init) &&
+ (cd a/b/c && git init) &&
git config remote.origin.url ../foo/bar.git &&
git submodule add ../bar/a/b/c ./a/b/c &&
git submodule init &&
@@ -993,11 +984,6 @@ test_expect_success 'submodule deinit should remove the whole submodule section
rmdir init
'
-test_expect_success 'submodule deinit should unset core.worktree' '
- test_path_is_file .git/modules/example/config &&
- test_must_fail git config -f .git/modules/example/config core.worktree
-'
-
test_expect_success 'submodule deinit from subdirectory' '
git submodule update --init &&
git config submodule.example.foo bar &&
diff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh
index 4e4c455502..9bc841d085 100755
--- a/t/t7401-submodule-summary.sh
+++ b/t/t7401-submodule-summary.sh
@@ -64,8 +64,7 @@ test_expect_success 'added submodule (subdirectory only)' "
cd sub &&
git submodule summary . >../actual
) &&
- >expected &&
- test_cmp expected actual
+ test_must_be_empty actual
"
test_expect_success 'added submodule (subdirectory with explicit path)' "
@@ -301,7 +300,7 @@ test_expect_success 'should not fail in an empty repo' "
git init xyzzy &&
cd xyzzy &&
git submodule summary >output 2>&1 &&
- test_cmp output /dev/null
+ test_must_be_empty output
"
test_done
diff --git a/t/t7405-submodule-merge.sh b/t/t7405-submodule-merge.sh
index 7bfb2f498d..7855bd8648 100755
--- a/t/t7405-submodule-merge.sh
+++ b/t/t7405-submodule-merge.sh
@@ -279,4 +279,177 @@ test_expect_success 'recursive merge with submodule' '
grep "$(cat expect3)" actual > /dev/null)
'
+# File/submodule conflict
+# Commit O: <empty>
+# Commit A: path (submodule)
+# Commit B: path
+# 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 &&
+ (
+ cd file-submodule &&
+
+ git commit --allow-empty -m O &&
+
+ git branch A &&
+ git branch B &&
+
+ git checkout B &&
+ echo content >path &&
+ git add path &&
+ git commit -m B &&
+
+ git checkout A &&
+ test_create_repo path &&
+ test_commit -C path world &&
+ git submodule add ./path &&
+ git commit -m A
+ )
+'
+
+test_expect_failure 'file/submodule conflict' '
+ test_when_finished "git -C file-submodule reset --hard" &&
+ (
+ cd file-submodule &&
+
+ git checkout A^0 &&
+ test_must_fail git merge B^0 &&
+
+ git ls-files -s >out &&
+ test_line_count = 3 out &&
+ git ls-files -u >out &&
+ test_line_count = 2 out &&
+
+ # path/ is still a submodule
+ test_path_is_dir path/.git &&
+
+ # There is a submodule at "path", so B:path cannot be written
+ # there. We expect it to be written somewhere in the same
+ # directory, though, so just grep for its content in all
+ # files, and ignore "grep: path: Is a directory" message
+ echo Checking if contents from B:path showed up anywhere &&
+ grep -q content * 2>/dev/null
+ )
+'
+
+test_expect_success 'file/submodule conflict; merge --abort works afterward' '
+ test_when_finished "git -C file-submodule reset --hard" &&
+ (
+ cd file-submodule &&
+
+ git checkout A^0 &&
+ test_must_fail git merge B^0 >out 2>err &&
+
+ test_path_is_file .git/MERGE_HEAD &&
+ git merge --abort
+ )
+'
+
+# Directory/submodule conflict
+# Commit O: <empty>
+# Commit A: path (submodule), with sole tracked file named 'world'
+# Commit B1: path/file
+# Commit B2: path/world
+#
+# Expected from merge of A & B1:
+# Contents under path/ from commit B1 are renamed elsewhere; we do not
+# want to write files from one of our tracked directories into a submodule
+#
+# Expected from merge of A & B2:
+# Similar to last merge, but with a slight twist: we don't want paths
+# under the submodule to be treated as untracked or in the way.
+
+test_expect_success 'setup directory/submodule conflict' '
+ test_create_repo directory-submodule &&
+ (
+ cd directory-submodule &&
+
+ git commit --allow-empty -m O &&
+
+ git branch A &&
+ git branch B1 &&
+ git branch B2 &&
+
+ git checkout B1 &&
+ mkdir path &&
+ echo contents >path/file &&
+ git add path/file &&
+ git commit -m B1 &&
+
+ git checkout B2 &&
+ mkdir path &&
+ echo contents >path/world &&
+ git add path/world &&
+ git commit -m B2 &&
+
+ git checkout A &&
+ test_create_repo path &&
+ test_commit -C path hello world &&
+ git submodule add ./path &&
+ git commit -m A
+ )
+'
+
+test_expect_failure 'directory/submodule conflict; keep submodule clean' '
+ test_when_finished "git -C directory-submodule reset --hard" &&
+ (
+ cd directory-submodule &&
+
+ git checkout A^0 &&
+ test_must_fail git merge B1^0 &&
+
+ git ls-files -s >out &&
+ test_line_count = 3 out &&
+ git ls-files -u >out &&
+ test_line_count = 1 out &&
+
+ # path/ is still a submodule
+ test_path_is_dir path/.git &&
+
+ echo Checking if contents from B1:path/file showed up &&
+ # Would rather use grep -r, but that is GNU extension...
+ git ls-files -co | xargs grep -q contents 2>/dev/null &&
+
+ # However, B1:path/file should NOT have shown up at path/file,
+ # because we should not write into the submodule
+ test_path_is_missing path/file
+ )
+'
+
+test_expect_failure 'directory/submodule conflict; should not treat submodule files as untracked or in the way' '
+ test_when_finished "git -C directory-submodule/path reset --hard" &&
+ test_when_finished "git -C directory-submodule reset --hard" &&
+ (
+ cd directory-submodule &&
+
+ git checkout A^0 &&
+ test_must_fail git merge B2^0 >out 2>err &&
+
+ # We do not want files within the submodule to prevent the
+ # merge from starting; we should not be writing to such paths
+ # anyway.
+ test_i18ngrep ! "refusing to lose untracked file at" err
+ )
+'
+
+test_expect_failure 'directory/submodule conflict; merge --abort works afterward' '
+ test_when_finished "git -C directory-submodule/path reset --hard" &&
+ test_when_finished "git -C directory-submodule reset --hard" &&
+ (
+ cd directory-submodule &&
+
+ git checkout A^0 &&
+ test_must_fail git merge B2^0 &&
+ test_path_is_file .git/MERGE_HEAD &&
+
+ # merge --abort should succeed, should clear .git/MERGE_HEAD,
+ # and should not leave behind any conflicted files
+ git merge --abort &&
+ test_path_is_missing .git/MERGE_HEAD &&
+ git ls-files -u >conflicts &&
+ test_must_be_empty conflicts
+ )
+'
+
test_done
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index 9e0d31700e..10dc91620a 100755
--- a/t/t7406-submodule-update.sh
+++ b/t/t7406-submodule-update.sh
@@ -115,17 +115,17 @@ Submodule path '../super/submodule': checked out '$submodulesha1'
EOF
cat <<EOF >expect2
+Cloning into '$pwd/recursivesuper/super/merging'...
+Cloning into '$pwd/recursivesuper/super/none'...
+Cloning into '$pwd/recursivesuper/super/rebasing'...
+Cloning into '$pwd/recursivesuper/super/submodule'...
Submodule 'merging' ($pwd/merging) registered for path '../super/merging'
Submodule 'none' ($pwd/none) registered for path '../super/none'
Submodule 'rebasing' ($pwd/rebasing) registered for path '../super/rebasing'
Submodule 'submodule' ($pwd/submodule) registered for path '../super/submodule'
-Cloning into '$pwd/recursivesuper/super/merging'...
done.
-Cloning into '$pwd/recursivesuper/super/none'...
done.
-Cloning into '$pwd/recursivesuper/super/rebasing'...
done.
-Cloning into '$pwd/recursivesuper/super/submodule'...
done.
EOF
@@ -137,7 +137,8 @@ test_expect_success 'submodule update --init --recursive from subdirectory' '
git submodule update --init --recursive ../super >../../actual 2>../../actual2
) &&
test_i18ncmp expect actual &&
- test_i18ncmp expect2 actual2
+ sort actual2 >actual2.sorted &&
+ test_i18ncmp expect2 actual2.sorted
'
cat <<EOF >expect2
@@ -174,7 +175,7 @@ test_expect_success 'submodule update does not fetch already present commits' '
git submodule update > ../actual 2> ../actual.err
) &&
test_i18ncmp expected actual &&
- ! test -s actual.err
+ test_must_be_empty actual.err
'
test_expect_success 'submodule update should fail due to local changes' '
@@ -481,7 +482,8 @@ test_expect_success 'recursive submodule update - command in .git/config catches
test_expect_success 'submodule init does not copy command into .git/config' '
(cd super &&
- H=$(git ls-files -s submodule | cut -d" " -f2) &&
+ git ls-files -s submodule >out &&
+ H=$(cut -d" " -f2 out) &&
mkdir submodule1 &&
git update-index --add --cacheinfo 160000 $H submodule1 &&
git config -f .gitmodules submodule.submodule1.path submodule1 &&
@@ -579,9 +581,11 @@ test_expect_success 'submodule update - update=none in .git/config' '
git checkout master &&
compare_head
) &&
- git diff --raw | grep " submodule" &&
+ git diff --name-only >out &&
+ grep ^submodule$ out &&
git submodule update &&
- git diff --raw | grep " submodule" &&
+ git diff --name-only >out &&
+ grep ^submodule$ out &&
(cd submodule &&
compare_head
) &&
@@ -597,11 +601,13 @@ test_expect_success 'submodule update - update=none in .git/config but --checkou
git checkout master &&
compare_head
) &&
- git diff --raw | grep " submodule" &&
+ git diff --name-only >out &&
+ grep ^submodule$ out &&
git submodule update --checkout &&
- test_must_fail git diff --raw \| grep " submodule" &&
+ git diff --name-only >out &&
+ ! grep ^submodule$ out &&
(cd submodule &&
- test_must_fail compare_head
+ ! compare_head
) &&
git config --unset submodule.submodule.update
)
@@ -615,8 +621,8 @@ test_expect_success 'submodule update --init skips submodule with update=none' '
git clone super cloned &&
(cd cloned &&
git submodule update --init &&
- test -e submodule/.git &&
- test_must_fail test -e none/.git
+ test_path_exists submodule/.git &&
+ test_path_is_missing none/.git
)
'
@@ -865,9 +871,9 @@ test_expect_success 'submodule update places git-dir in superprojects git-dir re
(cd submodule/subsubmodule &&
git log > ../../expected
) &&
- (cd .git/modules/submodule/modules/subsubmodule
+ (cd .git/modules/submodule/modules/subsubmodule &&
git log > ../../../../../actual
- )
+ ) &&
test_cmp actual expected
)
'
@@ -885,8 +891,9 @@ test_expect_success 'submodule update properly revives a moved submodule' '
H=$(git rev-parse --short HEAD) &&
git commit -am "pre move" &&
H2=$(git rev-parse --short HEAD) &&
- git status | sed "s/$H/XXX/" >expect &&
- H=$(cd submodule2; git rev-parse HEAD) &&
+ git status >out &&
+ sed "s/$H/XXX/" out >expect &&
+ H=$(cd submodule2 && git rev-parse HEAD) &&
git rm --cached submodule2 &&
rm -rf submodule2 &&
mkdir -p "moved/sub module" &&
@@ -894,7 +901,8 @@ test_expect_success 'submodule update properly revives a moved submodule' '
git config -f .gitmodules submodule.submodule2.path "moved/sub module" &&
git commit -am "post move" &&
git submodule update &&
- git status | sed "s/$H2/XXX/" >actual &&
+ git status > out &&
+ sed "s/$H2/XXX/" out >actual &&
test_cmp expect actual
)
'
@@ -912,7 +920,7 @@ test_expect_success SYMLINKS 'submodule update can handle symbolic links in pwd'
test_expect_success 'submodule update clone shallow submodule' '
test_when_finished "rm -rf super3" &&
- first=$(git -C cloned submodule status submodule |cut -c2-41) &&
+ first=$(git -C cloned rev-parse HEAD:submodule) &&
second=$(git -C submodule rev-parse HEAD) &&
commit_count=$(git -C submodule rev-list --count $first^..$second) &&
git clone cloned super3 &&
@@ -922,7 +930,8 @@ test_expect_success 'submodule update clone shallow submodule' '
sed -e "s#url = ../#url = file://$pwd/#" <.gitmodules >.gitmodules.tmp &&
mv -f .gitmodules.tmp .gitmodules &&
git submodule update --init --depth=$commit_count &&
- test 1 = $(git -C submodule log --oneline | wc -l)
+ git -C submodule log --oneline >out &&
+ test_line_count = 1 out
)
'
@@ -938,7 +947,8 @@ test_expect_success 'submodule update clone shallow submodule outside of depth'
test_i18ngrep "Direct fetching of that commit failed." actual &&
git -C ../submodule config uploadpack.allowReachableSHA1InWant true &&
git submodule update --init --depth=1 >actual &&
- test 1 = $(git -C submodule log --oneline | wc -l)
+ git -C submodule log --oneline >out &&
+ test_line_count = 1 out
)
'
diff --git a/t/t7408-submodule-reference.sh b/t/t7408-submodule-reference.sh
index 08d9add05e..34ac28c056 100755
--- a/t/t7408-submodule-reference.sh
+++ b/t/t7408-submodule-reference.sh
@@ -148,7 +148,7 @@ test_expect_success 'preparing second superproject with a nested submodule plus
cd supersuper &&
echo "I am super super." >file &&
git add file &&
- git commit -m B-super-super-initial
+ git commit -m B-super-super-initial &&
git submodule add "file://$base_dir/super" subwithsub &&
git commit -m B-super-super-added &&
git submodule update --init --recursive &&
diff --git a/t/t7410-submodule-checkout-to.sh b/t/t7410-submodule-checkout-to.sh
index 1acef32647..f1b492ebc4 100755
--- a/t/t7410-submodule-checkout-to.sh
+++ b/t/t7410-submodule-checkout-to.sh
@@ -6,55 +6,72 @@ test_description='Combination of submodules and multiple workdirs'
base_path=$(pwd -P)
-test_expect_success 'setup: make origin' \
- 'mkdir -p origin/sub && ( cd origin/sub && git init &&
- echo file1 >file1 &&
- git add file1 &&
- git commit -m file1 ) &&
- mkdir -p origin/main && ( cd origin/main && git init &&
- git submodule add ../sub &&
- git commit -m "add sub" ) &&
- ( cd origin/sub &&
- echo file1updated >file1 &&
- git add file1 &&
- git commit -m "file1 updated" ) &&
- ( cd origin/main/sub && git pull ) &&
- ( cd origin/main &&
- git add sub &&
- git commit -m "sub updated" )'
-
-test_expect_success 'setup: clone' \
- 'mkdir clone && ( cd clone &&
- git clone --recursive "$base_path/origin/main")'
+test_expect_success 'setup: make origin' '
+ mkdir -p origin/sub &&
+ (
+ cd origin/sub && git init &&
+ echo file1 >file1 &&
+ git add file1 &&
+ git commit -m file1
+ ) &&
+ mkdir -p origin/main &&
+ (
+ cd origin/main && git init &&
+ git submodule add ../sub &&
+ git commit -m "add sub"
+ ) &&
+ (
+ cd origin/sub &&
+ echo file1updated >file1 &&
+ git add file1 &&
+ git commit -m "file1 updated"
+ ) &&
+ git -C origin/main/sub pull &&
+ (
+ cd origin/main &&
+ git add sub &&
+ git commit -m "sub updated"
+ )
+'
+
+test_expect_success 'setup: clone' '
+ mkdir clone &&
+ git -C clone clone --recursive "$base_path/origin/main"
+'
rev1_hash_main=$(git --git-dir=origin/main/.git show --pretty=format:%h -q "HEAD~1")
rev1_hash_sub=$(git --git-dir=origin/sub/.git show --pretty=format:%h -q "HEAD~1")
-test_expect_success 'checkout main' \
- 'mkdir default_checkout &&
- (cd clone/main &&
- git worktree add "$base_path/default_checkout/main" "$rev1_hash_main")'
+test_expect_success 'checkout main' '
+ mkdir default_checkout &&
+ git -C clone/main worktree add "$base_path/default_checkout/main" "$rev1_hash_main"
+'
-test_expect_failure 'can see submodule diffs just after checkout' \
- '(cd default_checkout/main && git diff --submodule master"^!" | grep "file1 updated")'
+test_expect_failure 'can see submodule diffs just after checkout' '
+ git -C default_checkout/main diff --submodule master"^!" >out &&
+ grep "file1 updated" out
+'
-test_expect_success 'checkout main and initialize independed clones' \
- 'mkdir fully_cloned_submodule &&
- (cd clone/main &&
- git worktree add "$base_path/fully_cloned_submodule/main" "$rev1_hash_main") &&
- (cd fully_cloned_submodule/main && git submodule update)'
+test_expect_success 'checkout main and initialize independent clones' '
+ mkdir fully_cloned_submodule &&
+ git -C clone/main worktree add "$base_path/fully_cloned_submodule/main" "$rev1_hash_main" &&
+ git -C fully_cloned_submodule/main submodule update
+'
-test_expect_success 'can see submodule diffs after independed cloning' \
- '(cd fully_cloned_submodule/main && git diff --submodule master"^!" | grep "file1 updated")'
+test_expect_success 'can see submodule diffs after independent cloning' '
+ git -C fully_cloned_submodule/main diff --submodule master"^!" >out &&
+ grep "file1 updated" out
+'
-test_expect_success 'checkout sub manually' \
- 'mkdir linked_submodule &&
- (cd clone/main &&
- git worktree add "$base_path/linked_submodule/main" "$rev1_hash_main") &&
- (cd clone/main/sub &&
- git worktree add "$base_path/linked_submodule/main/sub" "$rev1_hash_sub")'
+test_expect_success 'checkout sub manually' '
+ mkdir linked_submodule &&
+ git -C clone/main worktree add "$base_path/linked_submodule/main" "$rev1_hash_main" &&
+ git -C clone/main/sub worktree add "$base_path/linked_submodule/main/sub" "$rev1_hash_sub"
+'
-test_expect_success 'can see submodule diffs after manual checkout of linked submodule' \
- '(cd linked_submodule/main && git diff --submodule master"^!" | grep "file1 updated")'
+test_expect_success 'can see submodule diffs after manual checkout of linked submodule' '
+ git -C linked_submodule/main diff --submodule master"^!" >out &&
+ grep "file1 updated" out
+'
test_done
diff --git a/t/t7415-submodule-names.sh b/t/t7415-submodule-names.sh
index b68c5f5e85..293e2e1963 100755
--- a/t/t7415-submodule-names.sh
+++ b/t/t7415-submodule-names.sh
@@ -176,4 +176,19 @@ test_expect_success 'fsck detects non-blob .gitmodules' '
)
'
+test_expect_success 'fsck detects corrupt .gitmodules' '
+ git init corrupt &&
+ (
+ cd corrupt &&
+
+ echo "[broken" >.gitmodules &&
+ git add .gitmodules &&
+ git commit -m "broken gitmodules" &&
+
+ git fsck 2>output &&
+ grep gitmodulesParse output &&
+ test_i18ngrep ! "bad config" output
+ )
+'
+
test_done
diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh
index 9dbbd01fc0..1a6773ee68 100755
--- a/t/t7501-commit.sh
+++ b/t/t7501-commit.sh
@@ -47,7 +47,7 @@ test_expect_success 'paths and -a do not mix' '
test_expect_success PERL 'can use paths with --interactive' '
echo bong-o-bong >file &&
# 2: update, 1:st path, that is all, 7: quit
- ( echo 2; echo 1; echo; echo 7 ) |
+ test_write_lines 2 1 "" 7 |
git commit -m foo --interactive file &&
git reset --hard HEAD^
'
@@ -293,7 +293,7 @@ test_expect_success PERL 'interactive add' '
test_expect_success PERL "commit --interactive doesn't change index if editor aborts" '
echo zoo >file &&
test_must_fail git diff --exit-code >diff1 &&
- (echo u ; echo "*" ; echo q) |
+ test_write_lines u "*" q |
(
EDITOR=: &&
export EDITOR &&
@@ -411,8 +411,8 @@ test_expect_success 'sign off (1)' '
git commit -s -m "thank you" &&
git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
(
- echo thank you
- echo
+ echo thank you &&
+ echo &&
git var GIT_COMMITTER_IDENT |
sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /"
) >expected &&
@@ -430,9 +430,9 @@ test_expect_success 'sign off (2)' '
$existing" &&
git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
(
- echo thank you
- echo
- echo $existing
+ echo thank you &&
+ echo &&
+ echo $existing &&
git var GIT_COMMITTER_IDENT |
sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /"
) >expected &&
@@ -450,9 +450,9 @@ test_expect_success 'signoff gap' '
$alt" &&
git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
(
- echo welcome
- echo
- echo $alt
+ echo welcome &&
+ echo &&
+ echo $alt &&
git var GIT_COMMITTER_IDENT |
sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /"
) >expected &&
@@ -470,11 +470,11 @@ We have now
$alt" &&
git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
(
- echo welcome
- echo
- echo We have now
- echo $alt
- echo
+ echo welcome &&
+ echo &&
+ echo We have now &&
+ echo $alt &&
+ echo &&
git var GIT_COMMITTER_IDENT |
sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /"
) >expected &&
@@ -491,11 +491,11 @@ non-trailer line
Myfooter: x" &&
git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
(
- echo subject
- echo
- echo non-trailer line
- echo Myfooter: x
- echo
+ echo subject &&
+ echo &&
+ echo non-trailer line &&
+ echo Myfooter: x &&
+ echo &&
echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
) >expected &&
test_cmp expected actual &&
@@ -508,15 +508,31 @@ non-trailer line
Myfooter: x" &&
git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
(
- echo subject
- echo
- echo non-trailer line
- echo Myfooter: x
+ echo subject &&
+ echo &&
+ echo non-trailer line &&
+ echo Myfooter: x &&
echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
) >expected &&
test_cmp expected actual
'
+test_expect_success 'signoff not confused by ---' '
+ cat >expected <<-EOF &&
+ subject
+
+ body
+ ---
+ these dashes confuse the parser!
+
+ Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+ EOF
+ # should be a noop, since we already signed
+ git commit --allow-empty --signoff -F expected &&
+ git log -1 --pretty=format:%B >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'multiple -m' '
>negative &&
@@ -524,10 +540,10 @@ test_expect_success 'multiple -m' '
git commit -m "one" -m "two" -m "three" &&
git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
(
- echo one
- echo
- echo two
- echo
+ echo one &&
+ echo &&
+ echo two &&
+ echo &&
echo three
) >expected &&
test_cmp expected actual
@@ -582,13 +598,11 @@ test_expect_success 'same tree (merge and amend merge)' '
git merge -s ours side -m "empty ok" &&
git diff HEAD^ HEAD >actual &&
- : >expected &&
- test_cmp expected actual &&
+ test_must_be_empty actual &&
git commit --amend -m "empty really ok" &&
git diff HEAD^ HEAD >actual &&
- : >expected &&
- test_cmp expected actual
+ test_must_be_empty actual
'
@@ -677,7 +691,7 @@ test_expect_success '--dry-run with conflicts fixed from a merge' '
git checkout -b branch-2 HEAD^1 &&
echo "commit-2-state" >test-file &&
git commit -m "commit 2" -i test-file &&
- ! $(git merge --no-commit commit-1) &&
+ test_must_fail git merge --no-commit commit-1 &&
echo "commit-2-state" >test-file &&
git add test-file &&
git commit --dry-run &&
diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh
index d33a3cb331..ca4a740da0 100755
--- a/t/t7502-commit.sh
+++ b/t/t7502-commit.sh
@@ -393,7 +393,6 @@ EOF
test_expect_success !AUTOIDENT 'do not fire editor when committer is bogus' '
>.git/result &&
- >expect &&
echo >>negative &&
(
@@ -403,7 +402,7 @@ test_expect_success !AUTOIDENT 'do not fire editor when committer is bogus' '
export GIT_EDITOR &&
test_must_fail git commit -e -m sample -a
) &&
- test_cmp expect .git/result
+ test_must_be_empty .git/result
'
test_expect_success 'do not fire editor if -m <msg> was given' '
diff --git a/t/t7504-commit-msg-hook.sh b/t/t7504-commit-msg-hook.sh
index 302a3a2082..31b9c6a2c1 100755
--- a/t/t7504-commit-msg-hook.sh
+++ b/t/t7504-commit-msg-hook.sh
@@ -157,6 +157,7 @@ test_expect_success 'merge bypasses failing hook with --no-verify' '
test_when_finished "git branch -D newbranch" &&
test_when_finished "git checkout -f master" &&
git checkout --orphan newbranch &&
+ git rm -f file &&
: >file2 &&
git add file2 &&
git commit --no-verify file2 -m in-side-branch &&
@@ -168,7 +169,7 @@ test_expect_success 'merge bypasses failing hook with --no-verify' '
chmod -x "$HOOK"
test_expect_success POSIXPERM 'with non-executable hook' '
- echo "content" >> file &&
+ echo "content" >file &&
git add file &&
git commit -m "content"
@@ -249,6 +250,7 @@ test_expect_success 'hook called in git-merge picks up commit message' '
test_when_finished "git branch -D newbranch" &&
test_when_finished "git checkout -f master" &&
git checkout --orphan newbranch &&
+ git rm -f file &&
: >file2 &&
git add file2 &&
git commit --no-verify file2 -m in-side-branch &&
diff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh
index b4b74dbe29..943708fb04 100755
--- a/t/t7506-status-submodule.sh
+++ b/t/t7506-status-submodule.sh
@@ -193,9 +193,9 @@ test_expect_success 'status with added and untracked file in modified submodule
test_expect_success 'setup .git file for sub' '
(cd sub &&
- rm -f new-file
+ rm -f new-file &&
REAL="$(pwd)/../.real" &&
- mv .git "$REAL"
+ mv .git "$REAL" &&
echo "gitdir: $REAL" >.git) &&
echo .real >>.gitignore &&
git commit -m "added .real to .gitignore" .gitignore
@@ -209,12 +209,12 @@ test_expect_success 'status with added file in modified submodule with .git file
test_expect_success 'status with a lot of untracked files in the submodule' '
(
- cd sub
+ cd sub &&
i=0 &&
while test $i -lt 1024
do
- >some-file-$i
- i=$(( $i + 1 ))
+ >some-file-$i &&
+ i=$(( $i + 1 )) || exit 1
done
) &&
git status --porcelain sub 2>err.actual &&
diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh
index 6e2015ed9a..4e37ff8f16 100755
--- a/t/t7510-signed-commit.sh
+++ b/t/t7510-signed-commit.sh
@@ -227,4 +227,11 @@ test_expect_success GPG 'log.showsignature behaves like --show-signature' '
grep "gpg: Good signature" actual
'
+test_expect_success GPG 'check config gpg.format values' '
+ test_config gpg.format openpgp &&
+ git commit -S --amend -m "success" &&
+ test_config gpg.format OpEnPgP &&
+ test_must_fail git commit -S --amend -m "fail"
+'
+
test_done
diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
index 164719d1c9..c441861331 100755
--- a/t/t7513-interpret-trailers.sh
+++ b/t/t7513-interpret-trailers.sh
@@ -1417,4 +1417,46 @@ test_expect_success 'unfold' '
test_cmp expected actual
'
+test_expect_success 'handling of --- lines in input' '
+ echo "real-trailer: just right" >expected &&
+
+ git interpret-trailers --parse >actual <<-\EOF &&
+ subject
+
+ body
+
+ not-a-trailer: too soon
+ ------ this is just a line in the commit message with a bunch of
+ ------ dashes; it does not have any syntactic meaning.
+
+ real-trailer: just right
+ ---
+ below the dashed line may be a patch, etc.
+
+ not-a-trailer: too late
+ EOF
+
+ test_cmp expected actual
+'
+
+test_expect_success 'suppress --- handling' '
+ echo "real-trailer: just right" >expected &&
+
+ git interpret-trailers --parse --no-divider >actual <<-\EOF &&
+ subject
+
+ This commit message has a "---" in it, but because we tell
+ interpret-trailers not to respect that, it has no effect.
+
+ not-a-trailer: too soon
+ ---
+
+ This is still the commit message body.
+
+ real-trailer: just right
+ EOF
+
+ test_cmp expected actual
+'
+
test_done
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
index 6736d8d131..106148254d 100755
--- a/t/t7600-merge.sh
+++ b/t/t7600-merge.sh
@@ -38,7 +38,6 @@ printf '%s\n' '1 X' 2 3 4 5 6 7 8 9 >result.1
printf '%s\n' '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5
printf '%s\n' '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9
printf '%s\n' 1 2 3 4 5 6 7 8 '9 Z' >result.9z
->empty
create_merge_msgs () {
echo "Merge tag 'c2'" >msg.1-5 &&
@@ -58,8 +57,6 @@ create_merge_msgs () {
echo &&
git log --no-merges ^HEAD c2 c3
} >squash.1-5-9 &&
- : >msg.nologff &&
- : >msg.nolognoff &&
{
echo "* tag 'c3':" &&
echo " commit 3"
@@ -519,7 +516,7 @@ test_expect_success 'tolerate unknown values for merge.ff' '
test_tick &&
git merge c1 2>message &&
verify_head "$c1" &&
- test_cmp empty message
+ test_must_be_empty message
'
test_expect_success 'combining --squash and --no-ff is refused' '
@@ -551,13 +548,13 @@ test_expect_success 'merge log message' '
git reset --hard c0 &&
git merge --no-log c2 &&
git show -s --pretty=format:%b HEAD >msg.act &&
- test_cmp msg.nologff msg.act &&
+ test_must_be_empty msg.act &&
git reset --hard c0 &&
test_config branch.master.mergeoptions "--no-ff" &&
git merge --no-log c2 &&
git show -s --pretty=format:%b HEAD >msg.act &&
- test_cmp msg.nolognoff msg.act &&
+ test_must_be_empty msg.act &&
git merge --log c3 &&
git show -s --pretty=format:%b HEAD >msg.act &&
diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh
index 1a430b9c40..a9fb971615 100755
--- a/t/t7610-mergetool.sh
+++ b/t/t7610-mergetool.sh
@@ -57,18 +57,18 @@ test_expect_success 'setup' '
git checkout -b delete-base branch1 &&
mkdir -p a/a &&
- (echo one; echo two; echo 3; echo 4) >a/a/file.txt &&
+ test_write_lines one two 3 4 >a/a/file.txt &&
git add a/a/file.txt &&
git commit -m"base file" &&
git checkout -b move-to-b delete-base &&
mkdir -p b/b &&
git mv a/a/file.txt b/b/file.txt &&
- (echo one; echo two; echo 4) >b/b/file.txt &&
+ test_write_lines one two 4 >b/b/file.txt &&
git commit -a -m"move to b" &&
git checkout -b move-to-c delete-base &&
mkdir -p c/c &&
git mv a/a/file.txt c/c/file.txt &&
- (echo one; echo two; echo 3) >c/c/file.txt &&
+ test_write_lines one two 3 >c/c/file.txt &&
git commit -a -m"move to c" &&
git checkout -b stash1 master &&
@@ -328,9 +328,8 @@ test_expect_success 'mergetool produces no errors when keepBackup is used' '
git checkout -b test$test_count move-to-c &&
test_config mergetool.keepBackup true &&
test_must_fail git merge move-to-b &&
- : >expect &&
echo d | git mergetool a/a/file.txt 2>actual &&
- test_cmp expect actual &&
+ test_must_be_empty actual &&
! test -d a
'
@@ -349,7 +348,7 @@ test_expect_success 'mergetool keeps tempfiles when aborting delete/delete' '
git checkout -b test$test_count move-to-c &&
test_config mergetool.keepTemporaries true &&
test_must_fail git merge move-to-b &&
- ! (echo a; echo n) | git mergetool a/a/file.txt &&
+ ! test_write_lines a n | git mergetool a/a/file.txt &&
test -d a/a &&
cat >expect <<-\EOF &&
file_BASE_.txt
@@ -620,8 +619,7 @@ test_expect_success 'file with no base' '
git checkout -b test$test_count branch1 &&
test_must_fail git merge master &&
git mergetool --no-prompt --tool mybase -- both &&
- >expected &&
- test_cmp expected both
+ test_must_be_empty both
'
test_expect_success 'custom commands override built-ins' '
diff --git a/t/t7611-merge-abort.sh b/t/t7611-merge-abort.sh
index 7b4798e8e4..7c84a518aa 100755
--- a/t/t7611-merge-abort.sh
+++ b/t/t7611-merge-abort.sh
@@ -187,31 +187,6 @@ test_expect_success 'Fail clean merge with matching dirty worktree' '
test_cmp expect actual
'
-test_expect_success 'Abort clean merge with matching dirty index' '
- git add bar &&
- git diff --staged > expect &&
- git merge --no-commit clean_branch &&
- test -f .git/MERGE_HEAD &&
- ### When aborting the merge, git will discard all staged changes,
- ### including those that were staged pre-merge. In other words,
- ### --abort will LOSE any staged changes (the staged changes that
- ### are lost must match the merge result, or the merge would not
- ### have been allowed to start). Change expectations accordingly:
- rm expect &&
- touch expect &&
- # Abort merge
- git merge --abort &&
- test ! -f .git/MERGE_HEAD &&
- test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
- git diff --staged > actual &&
- test_cmp expect actual &&
- test -z "$(git diff)"
-'
-
-test_expect_success 'Reset worktree changes' '
- git reset --hard "$pre_merge_head"
-'
-
test_expect_success 'Fail conflicting merge with matching dirty worktree' '
echo barf > bar &&
git diff > expect &&
@@ -223,97 +198,4 @@ test_expect_success 'Fail conflicting merge with matching dirty worktree' '
test_cmp expect actual
'
-test_expect_success 'Abort conflicting merge with matching dirty index' '
- git add bar &&
- git diff --staged > expect &&
- test_must_fail git merge conflict_branch &&
- test -f .git/MERGE_HEAD &&
- ### When aborting the merge, git will discard all staged changes,
- ### including those that were staged pre-merge. In other words,
- ### --abort will LOSE any staged changes (the staged changes that
- ### are lost must match the merge result, or the merge would not
- ### have been allowed to start). Change expectations accordingly:
- rm expect &&
- touch expect &&
- # Abort merge
- git merge --abort &&
- test ! -f .git/MERGE_HEAD &&
- test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
- git diff --staged > actual &&
- test_cmp expect actual &&
- test -z "$(git diff)"
-'
-
-test_expect_success 'Reset worktree changes' '
- git reset --hard "$pre_merge_head"
-'
-
-test_expect_success 'Abort merge with pre- and post-merge worktree changes' '
- # Pre-merge worktree changes
- echo xyzzy > foo &&
- echo barf > bar &&
- git add bar &&
- git diff > expect &&
- git diff --staged > expect-staged &&
- # Perform merge
- test_must_fail git merge conflict_branch &&
- test -f .git/MERGE_HEAD &&
- # Post-merge worktree changes
- echo yzxxz > foo &&
- echo blech > baz &&
- ### When aborting the merge, git will discard staged changes (bar)
- ### and unmerged changes (baz). Other changes that are neither
- ### staged nor marked as unmerged (foo), will be preserved. For
- ### these changed, git cannot tell pre-merge changes apart from
- ### post-merge changes, so the post-merge changes will be
- ### preserved. Change expectations accordingly:
- git diff -- foo > expect &&
- rm expect-staged &&
- touch expect-staged &&
- # Abort merge
- git merge --abort &&
- test ! -f .git/MERGE_HEAD &&
- test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
- git diff > actual &&
- test_cmp expect actual &&
- git diff --staged > actual-staged &&
- test_cmp expect-staged actual-staged
-'
-
-test_expect_success 'Reset worktree changes' '
- git reset --hard "$pre_merge_head"
-'
-
-test_expect_success 'Abort merge with pre- and post-merge index changes' '
- # Pre-merge worktree changes
- echo xyzzy > foo &&
- echo barf > bar &&
- git add bar &&
- git diff > expect &&
- git diff --staged > expect-staged &&
- # Perform merge
- test_must_fail git merge conflict_branch &&
- test -f .git/MERGE_HEAD &&
- # Post-merge worktree changes
- echo yzxxz > foo &&
- echo blech > baz &&
- git add foo bar &&
- ### When aborting the merge, git will discard all staged changes
- ### (foo, bar and baz), and no changes will be preserved. Whether
- ### the changes were staged pre- or post-merge does not matter
- ### (except for not preventing starting the merge).
- ### Change expectations accordingly:
- rm expect expect-staged &&
- touch expect &&
- touch expect-staged &&
- # Abort merge
- git merge --abort &&
- test ! -f .git/MERGE_HEAD &&
- test "$pre_merge_head" = "$(git rev-parse HEAD)" &&
- git diff > actual &&
- test_cmp expect actual &&
- git diff --staged > actual-staged &&
- test_cmp expect-staged actual-staged
-'
-
test_done
diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh
index 9312c8daf5..be5c1bd553 100755
--- a/t/t7810-grep.sh
+++ b/t/t7810-grep.sh
@@ -217,9 +217,8 @@ do
'
test_expect_success "grep -w $L (w)" '
- : >expected &&
test_must_fail git grep -n -w -e "^w" $H >actual &&
- test_cmp expected actual
+ test_must_be_empty actual
'
test_expect_success "grep -w $L (x)" '
@@ -239,29 +238,42 @@ do
'
test_expect_success "grep -w $L (y-2)" '
- : >expected &&
if git grep -n -w -e "^y y" $H >actual
then
echo should not have matched
cat actual
false
else
- test_cmp expected actual
+ test_must_be_empty actual
fi
'
test_expect_success "grep -w $L (z)" '
- : >expected &&
if git grep -n -w -e "^z" $H >actual
then
echo should not have matched
cat actual
false
else
- test_cmp expected actual
+ test_must_be_empty actual
fi
'
+ test_expect_success "grep $L (with --column, --only-matching)" '
+ {
+ echo ${HC}file:1:5:mmap
+ echo ${HC}file:2:5:mmap
+ echo ${HC}file:3:5:mmap
+ echo ${HC}file:3:13:mmap
+ echo ${HC}file:4:5:mmap
+ echo ${HC}file:4:13:mmap
+ echo ${HC}file:5:5:mmap
+ echo ${HC}file:5:13:mmap
+ } >expected &&
+ git grep --column -n -o -e mmap $H >actual &&
+ test_cmp expected actual
+ '
+
test_expect_success "grep $L (t-1)" '
echo "${HC}t/t:1:test" >expected &&
git grep -n -e test $H >actual &&
@@ -483,7 +495,7 @@ test_expect_success 'grep -L -C' '
test_expect_success 'grep --files-without-match --quiet' '
git grep --files-without-match --quiet nonexistent_string >actual &&
- test_cmp /dev/null actual
+ test_must_be_empty actual
'
cat >expected <<EOF
@@ -604,11 +616,10 @@ z:zzz
EOF
test_expect_success 'grep -q, silently report matches' '
- >empty &&
git grep -q mmap >actual &&
- test_cmp empty actual &&
+ test_must_be_empty actual &&
test_must_fail git grep -q qfwfq >actual &&
- test_cmp empty actual
+ test_must_be_empty actual
'
test_expect_success 'grep -C1 hunk mark between files' '
@@ -676,8 +687,7 @@ test_expect_success 'log grep (5)' '
test_expect_success 'log grep (6)' '
git log --author=-0700 --pretty=tformat:%s >actual &&
- >expect &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'log grep (7)' '
@@ -702,8 +712,7 @@ test_expect_success 'log grep (9)' '
test_expect_success 'log grep (9)' '
git log -g --grep-reflog="commit: third" --author="non-existent" --pretty=tformat:%s >actual &&
- : >expect &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'log --grep-reflog can only be used under -g' '
@@ -793,15 +802,13 @@ test_expect_success 'log --all-match --grep --grep --author takes intersection'
'
test_expect_success 'log --author does not search in timestamp' '
- : >expect &&
git log --author="$GIT_AUTHOR_DATE" >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'log --committer does not search in timestamp' '
- : >expect &&
git log --committer="$GIT_COMMITTER_DATE" >actual &&
- test_cmp expect actual
+ test_must_be_empty actual
'
test_expect_success 'grep with CE_VALID file' '
@@ -940,10 +947,9 @@ test_expect_success 'grep from a subdirectory to search wider area (1)' '
test_expect_success 'grep from a subdirectory to search wider area (2)' '
mkdir -p s &&
(
- cd s || exit 1
- ( git grep xxyyzz .. >out ; echo $? >status )
- ! test -s out &&
- test 1 = $(cat status)
+ cd s &&
+ test_expect_code 1 git grep xxyyzz .. >out &&
+ test_must_be_empty out
)
'
@@ -1052,13 +1058,12 @@ test_expect_success 'inside git repository but with --no-index' '
echo ".gitignore:.*o*" &&
cat is/expect.unignored
} >is/expect.full &&
- : >is/expect.empty &&
echo file2:world >is/expect.sub &&
(
cd is/git &&
git init &&
test_must_fail git grep o >../actual.full &&
- test_cmp ../expect.empty ../actual.full &&
+ test_must_be_empty ../actual.full &&
git grep --untracked o >../actual.unignored &&
test_cmp ../expect.unignored ../actual.unignored &&
@@ -1071,7 +1076,7 @@ test_expect_success 'inside git repository but with --no-index' '
cd sub &&
test_must_fail git grep o >../../actual.sub &&
- test_cmp ../../expect.empty ../../actual.sub &&
+ test_must_be_empty ../../actual.sub &&
git grep --no-index o >../../actual.sub &&
test_cmp ../../expect.sub ../../actual.sub &&
@@ -1237,10 +1242,9 @@ test_expect_success !PCRE 'grep -P pattern errors without PCRE' '
'
test_expect_success 'grep pattern with grep.extendedRegexp=true' '
- >empty &&
test_must_fail git -c grep.extendedregexp=true \
grep "\p{Ps}.*?\p{Pe}" hello.c >actual &&
- test_cmp empty actual
+ test_must_be_empty actual
'
test_expect_success PCRE 'grep -P pattern with grep.extendedRegexp=true' '
diff --git a/t/t7811-grep-open.sh b/t/t7811-grep-open.sh
index e1951a5cbb..d1ebfd88c7 100755
--- a/t/t7811-grep-open.sh
+++ b/t/t7811-grep-open.sh
@@ -51,14 +51,13 @@ test_expect_success SIMPLEPAGER 'git grep -O' '
grep.h
EOF
echo grep.h >expect.notless &&
- >empty &&
PATH=.:$PATH git grep -O GREP_PATTERN >out &&
{
test_cmp expect.less pager-args ||
test_cmp expect.notless pager-args
} &&
- test_cmp empty out
+ test_must_be_empty out
'
test_expect_success 'git grep -O --cached' '
@@ -72,7 +71,6 @@ test_expect_success 'git grep -O --no-index' '
grep.h
untracked
EOF
- >empty &&
(
GIT_PAGER='\''printf "%s\n" >pager-args'\'' &&
@@ -80,7 +78,7 @@ test_expect_success 'git grep -O --no-index' '
git grep --no-index -O GREP_PATTERN >out
) &&
test_cmp expect pager-args &&
- test_cmp empty out
+ test_must_be_empty out
'
test_expect_success 'setup: fake "less"' '
@@ -96,15 +94,14 @@ test_expect_success 'git grep -O jumps to line in less' '
+/*GREP_PATTERN
grep.h
EOF
- >empty &&
GIT_PAGER=./less git grep -O GREP_PATTERN >out &&
test_cmp expect actual &&
- test_cmp empty out &&
+ test_must_be_empty out &&
git grep -O./less GREP_PATTERN >out2 &&
test_cmp expect actual &&
- test_cmp empty out2
+ test_must_be_empty out2
'
test_expect_success 'modified file' '
@@ -122,7 +119,7 @@ test_expect_success 'modified file' '
test_when_finished "git checkout HEAD unrelated" &&
GIT_PAGER=./less git grep -F -O "enum grep_pat_token" >out &&
test_cmp expect actual &&
- test_cmp empty out
+ test_must_be_empty out
'
test_expect_success 'copes with color settings' '
@@ -138,7 +135,6 @@ test_expect_success 'copes with color settings' '
test_expect_success 'run from subdir' '
rm -f actual &&
echo grep.c >expect &&
- >empty &&
(
cd subdir &&
@@ -156,8 +152,8 @@ test_expect_success 'run from subdir' '
;;
esac &&
test_cmp expect args &&
- test_cmp empty out &&
- test_cmp empty out2
+ test_must_be_empty out &&
+ test_must_be_empty out2
'
test_done
diff --git a/t/t8003-blame-corner-cases.sh b/t/t8003-blame-corner-cases.sh
index 661f9d430d..c92a47b6d5 100755
--- a/t/t8003-blame-corner-cases.sh
+++ b/t/t8003-blame-corner-cases.sh
@@ -216,14 +216,18 @@ test_expect_success 'blame -L with invalid start' '
'
test_expect_success 'blame -L with invalid end' '
- test_must_fail git blame -L1,5 tres 2>errors &&
- test_i18ngrep "has only 2 lines" errors
+ git blame -L1,5 tres >out &&
+ test_line_count = 2 out
'
test_expect_success 'blame parses <end> part of -L' '
git blame -L1,1 tres >out &&
- cat out &&
- test $(wc -l < out) -eq 1
+ test_line_count = 1 out
+'
+
+test_expect_success 'blame -Ln,-(n+1)' '
+ git blame -L3,-4 nine_lines >out &&
+ test_line_count = 3 out
'
test_expect_success 'indent of line numbers, nine lines' '
diff --git a/t/t8010-cat-file-filters.sh b/t/t8010-cat-file-filters.sh
index 0f86c19174..31de4b64dc 100755
--- a/t/t8010-cat-file-filters.sh
+++ b/t/t8010-cat-file-filters.sh
@@ -47,7 +47,7 @@ test_expect_success 'cat-file --textconv --path=<path> works' '
test_expect_success '--path=<path> complains without --textconv/--filters' '
sha1=$(git rev-parse -q --verify HEAD:world.txt) &&
test_must_fail git cat-file --path=hello.txt blob $sha1 >actual 2>err &&
- test ! -s actual &&
+ test_must_be_empty actual &&
grep "path.*needs.*filters" err
'
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 1da282c415..1ef1a19003 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -253,10 +253,9 @@ test_suppress_self () {
mv msgtxt1 msgtxt1-$3 &&
sed -e '/^$/q' msgtxt1-$3 >"msghdr1-$3" &&
- >"expected-no-cc-$3" &&
(grep '^Cc:' msghdr1-$3 >"actual-no-cc-$3";
- test_cmp expected-no-cc-$3 actual-no-cc-$3)
+ test_must_be_empty actual-no-cc-$3)
}
test_suppress_self_unquoted () {
@@ -332,7 +331,7 @@ test_expect_success $PREREQ 'Show all headers' '
test_expect_success $PREREQ 'Prompting works' '
clean_fake_sendmail &&
- (echo "to@example.com"
+ (echo "to@example.com" &&
echo ""
) | GIT_SEND_EMAIL_NOTTY=1 git send-email \
--smtp-server="$(pwd)/fake.sendmail" \
@@ -509,8 +508,8 @@ test_expect_success $PREREQ 'Invalid In-Reply-To' '
test_expect_success $PREREQ 'Valid In-Reply-To when prompting' '
clean_fake_sendmail &&
- (echo "From Example <from@example.com>"
- echo "To Example <to@example.com>"
+ (echo "From Example <from@example.com>" &&
+ echo "To Example <to@example.com>" &&
echo ""
) | GIT_SEND_EMAIL_NOTTY=1 git send-email \
--smtp-server="$(pwd)/fake.sendmail" \
@@ -2023,11 +2022,11 @@ test_expect_success $PREREQ 'invoke hook' '
# Verify error message when a patch is rejected by the hook
sed -e "s/add master/x/" ../0001-add-master.patch >../another.patch &&
- git send-email \
+ test_must_fail git send-email \
--from="Example <nobody@example.com>" \
--to=nobody@example.com \
--smtp-server="$(pwd)/../fake.sendmail" \
- ../another.patch 2>err
+ ../another.patch 2>err &&
test_i18ngrep "rejected by sendemail-validate hook" err
)
'
diff --git a/t/t9011-svn-da.sh b/t/t9011-svn-da.sh
index b38d16f9db..ab1ef28fd9 100755
--- a/t/t9011-svn-da.sh
+++ b/t/t9011-svn-da.sh
@@ -18,7 +18,7 @@ test_expect_success 'reject empty delta' '
test_expect_success 'delta can empty file' '
printf "SVNQ" | q_to_nul >clear.delta &&
test-svn-fe -d preimage clear.delta 4 >actual &&
- test_cmp empty actual
+ test_must_be_empty actual
'
test_expect_success 'reject svndiff2' '
@@ -29,7 +29,7 @@ test_expect_success 'reject svndiff2' '
test_expect_success 'one-window empty delta' '
printf "SVNQ%s" "QQQQQ" | q_to_nul >clear.onewindow &&
test-svn-fe -d preimage clear.onewindow 9 >actual &&
- test_cmp empty actual
+ test_must_be_empty actual
'
test_expect_success 'reject incomplete window header' '
@@ -50,7 +50,7 @@ test_expect_success 'two-window empty delta' '
printf "SVNQ%s%s" "QQQQQ" "QQQQQ" | q_to_nul >clear.twowindow &&
test-svn-fe -d preimage clear.twowindow 14 >actual &&
test_must_fail test-svn-fe -d preimage clear.twowindow 13 &&
- test_cmp empty actual
+ test_must_be_empty actual
'
test_expect_success 'noisy zeroes' '
@@ -60,7 +60,7 @@ test_expect_success 'noisy zeroes' '
q_to_nul >clear.noisy &&
len=$(wc -c <clear.noisy) &&
test-svn-fe -d preimage clear.noisy $len &&
- test_cmp empty actual
+ test_must_be_empty actual
'
test_expect_success 'reject variable-length int in magic' '
@@ -83,7 +83,7 @@ test_expect_success 'reject truncated integer' '
test_expect_success 'nonempty (but unused) preimage view' '
printf "SVNQ%b" "Q\003QQQ" | q_to_nul >clear.readpreimage &&
test-svn-fe -d preimage clear.readpreimage 9 >actual &&
- test_cmp empty actual
+ test_must_be_empty actual
'
test_expect_success 'preimage view: right endpoint cannot backtrack' '
@@ -99,7 +99,7 @@ test_expect_success 'preimage view: left endpoint can advance' '
q_to_nul >clear.shrinkbacktrack &&
test-svn-fe -d preimage clear.preshrink 14 >actual &&
test_must_fail test-svn-fe -d preimage clear.shrinkbacktrack 14 &&
- test_cmp empty actual
+ test_must_be_empty actual
'
test_expect_success 'preimage view: offsets compared by value' '
@@ -109,7 +109,7 @@ test_expect_success 'preimage view: offsets compared by value' '
q_to_nul >clear.noisyadvance &&
test_must_fail test-svn-fe -d preimage clear.noisybacktrack 15 &&
test-svn-fe -d preimage clear.noisyadvance 15 &&
- test_cmp empty actual
+ test_must_be_empty actual
'
test_expect_success 'preimage view: reject truncated preimage' '
diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
index c937330a5f..9af6078844 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -31,7 +31,7 @@ test_expect_success \
(
cd import &&
echo foo >foo &&
- ln -s foo foo.link
+ ln -s foo foo.link &&
mkdir -p dir/a/b/c/d/e &&
echo "deep dir" >dir/a/b/c/d/e/file &&
mkdir bar &&
diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh
index 07bfb63777..8a5c8dc1aa 100755
--- a/t/t9101-git-svn-props.sh
+++ b/t/t9101-git-svn-props.sh
@@ -149,7 +149,7 @@ test_expect_success 'test show-ignore' "
svn_cmd up &&
svn_cmd propset -R svn:ignore '
no-such-file*
-' .
+' . &&
svn_cmd commit -m 'propset svn:ignore'
) &&
git svn show-ignore > show-ignore.got &&
diff --git a/t/t9119-git-svn-info.sh b/t/t9119-git-svn-info.sh
index 88241baee3..8201c3e808 100755
--- a/t/t9119-git-svn-info.sh
+++ b/t/t9119-git-svn-info.sh
@@ -22,8 +22,8 @@ esac
# same value as "svn info" (i.e. the commit timestamp that touched the
# path most recently); do not expect that field to match.
test_cmp_info () {
- sed -e '/^Text Last Updated:/d' "$1" >tmp.expect
- sed -e '/^Text Last Updated:/d' "$2" >tmp.actual
+ sed -e '/^Text Last Updated:/d' "$1" >tmp.expect &&
+ sed -e '/^Text Last Updated:/d' "$2" >tmp.actual &&
test_cmp tmp.expect tmp.actual &&
rm -f tmp.expect tmp.actual
}
@@ -59,24 +59,24 @@ test_expect_success 'setup repository and import' '
'
test_expect_success 'info' "
- (cd svnwc; svn info) > expected.info &&
- (cd gitwc; git svn info) > actual.info &&
+ (cd svnwc && svn info) > expected.info &&
+ (cd gitwc && git svn info) > actual.info &&
test_cmp_info expected.info actual.info
"
test_expect_success 'info --url' '
- test "$(cd gitwc; git svn info --url)" = "$quoted_svnrepo"
+ test "$(cd gitwc && git svn info --url)" = "$quoted_svnrepo"
'
test_expect_success 'info .' "
- (cd svnwc; svn info .) > expected.info-dot &&
- (cd gitwc; git svn info .) > actual.info-dot &&
+ (cd svnwc && svn info .) > expected.info-dot &&
+ (cd gitwc && git svn info .) > actual.info-dot &&
test_cmp_info expected.info-dot actual.info-dot
"
test_expect_success 'info $(pwd)' '
- (cd svnwc; svn info "$(pwd)") >expected.info-pwd &&
- (cd gitwc; git svn info "$(pwd)") >actual.info-pwd &&
+ (cd svnwc && svn info "$(pwd)") >expected.info-pwd &&
+ (cd gitwc && git svn info "$(pwd)") >actual.info-pwd &&
grep -v ^Path: <expected.info-pwd >expected.info-np &&
grep -v ^Path: <actual.info-pwd >actual.info-np &&
test_cmp_info expected.info-np actual.info-np &&
@@ -85,8 +85,8 @@ test_expect_success 'info $(pwd)' '
'
test_expect_success 'info $(pwd)/../___wc' '
- (cd svnwc; svn info "$(pwd)/../svnwc") >expected.info-pwd &&
- (cd gitwc; git svn info "$(pwd)/../gitwc") >actual.info-pwd &&
+ (cd svnwc && svn info "$(pwd)/../svnwc") >expected.info-pwd &&
+ (cd gitwc && git svn info "$(pwd)/../gitwc") >actual.info-pwd &&
grep -v ^Path: <expected.info-pwd >expected.info-np &&
grep -v ^Path: <actual.info-pwd >actual.info-np &&
test_cmp_info expected.info-np actual.info-np &&
@@ -95,8 +95,8 @@ test_expect_success 'info $(pwd)/../___wc' '
'
test_expect_success 'info $(pwd)/../___wc//file' '
- (cd svnwc; svn info "$(pwd)/../svnwc//file") >expected.info-pwd &&
- (cd gitwc; git svn info "$(pwd)/../gitwc//file") >actual.info-pwd &&
+ (cd svnwc && svn info "$(pwd)/../svnwc//file") >expected.info-pwd &&
+ (cd gitwc && git svn info "$(pwd)/../gitwc//file") >actual.info-pwd &&
grep -v ^Path: <expected.info-pwd >expected.info-np &&
grep -v ^Path: <actual.info-pwd >actual.info-np &&
test_cmp_info expected.info-np actual.info-np &&
@@ -105,56 +105,56 @@ test_expect_success 'info $(pwd)/../___wc//file' '
'
test_expect_success 'info --url .' '
- test "$(cd gitwc; git svn info --url .)" = "$quoted_svnrepo"
+ test "$(cd gitwc && git svn info --url .)" = "$quoted_svnrepo"
'
test_expect_success 'info file' "
- (cd svnwc; svn info file) > expected.info-file &&
- (cd gitwc; git svn info file) > actual.info-file &&
+ (cd svnwc && svn info file) > expected.info-file &&
+ (cd gitwc && git svn info file) > actual.info-file &&
test_cmp_info expected.info-file actual.info-file
"
test_expect_success 'info --url file' '
- test "$(cd gitwc; git svn info --url file)" = "$quoted_svnrepo/file"
+ test "$(cd gitwc && git svn info --url file)" = "$quoted_svnrepo/file"
'
test_expect_success 'info directory' "
- (cd svnwc; svn info directory) > expected.info-directory &&
- (cd gitwc; git svn info directory) > actual.info-directory &&
+ (cd svnwc && svn info directory) > expected.info-directory &&
+ (cd gitwc && git svn info directory) > actual.info-directory &&
test_cmp_info expected.info-directory actual.info-directory
"
test_expect_success 'info inside directory' "
- (cd svnwc/directory; svn info) > expected.info-inside-directory &&
- (cd gitwc/directory; git svn info) > actual.info-inside-directory &&
+ (cd svnwc/directory && svn info) > expected.info-inside-directory &&
+ (cd gitwc/directory && git svn info) > actual.info-inside-directory &&
test_cmp_info expected.info-inside-directory actual.info-inside-directory
"
test_expect_success 'info --url directory' '
- test "$(cd gitwc; git svn info --url directory)" = "$quoted_svnrepo/directory"
+ test "$(cd gitwc && git svn info --url directory)" = "$quoted_svnrepo/directory"
'
test_expect_success 'info symlink-file' "
- (cd svnwc; svn info symlink-file) > expected.info-symlink-file &&
- (cd gitwc; git svn info symlink-file) > actual.info-symlink-file &&
+ (cd svnwc && svn info symlink-file) > expected.info-symlink-file &&
+ (cd gitwc && git svn info symlink-file) > actual.info-symlink-file &&
test_cmp_info expected.info-symlink-file actual.info-symlink-file
"
test_expect_success 'info --url symlink-file' '
- test "$(cd gitwc; git svn info --url symlink-file)" \
+ test "$(cd gitwc && git svn info --url symlink-file)" \
= "$quoted_svnrepo/symlink-file"
'
test_expect_success 'info symlink-directory' "
- (cd svnwc; svn info symlink-directory) \
+ (cd svnwc && svn info symlink-directory) \
> expected.info-symlink-directory &&
- (cd gitwc; git svn info symlink-directory) \
+ (cd gitwc && git svn info symlink-directory) \
> actual.info-symlink-directory &&
test_cmp_info expected.info-symlink-directory actual.info-symlink-directory
"
test_expect_success 'info --url symlink-directory' '
- test "$(cd gitwc; git svn info --url symlink-directory)" \
+ test "$(cd gitwc && git svn info --url symlink-directory)" \
= "$quoted_svnrepo/symlink-directory"
'
@@ -169,13 +169,13 @@ test_expect_success 'info added-file' "
cd svnwc &&
svn_cmd add added-file > /dev/null
) &&
- (cd svnwc; svn info added-file) > expected.info-added-file &&
- (cd gitwc; git svn info added-file) > actual.info-added-file &&
+ (cd svnwc && svn info added-file) > expected.info-added-file &&
+ (cd gitwc && git svn info added-file) > actual.info-added-file &&
test_cmp_info expected.info-added-file actual.info-added-file
"
test_expect_success 'info --url added-file' '
- test "$(cd gitwc; git svn info --url added-file)" \
+ test "$(cd gitwc && git svn info --url added-file)" \
= "$quoted_svnrepo/added-file"
'
@@ -190,15 +190,15 @@ test_expect_success 'info added-directory' "
cd gitwc &&
git add added-directory
) &&
- (cd svnwc; svn info added-directory) \
+ (cd svnwc && svn info added-directory) \
> expected.info-added-directory &&
- (cd gitwc; git svn info added-directory) \
+ (cd gitwc && git svn info added-directory) \
> actual.info-added-directory &&
test_cmp_info expected.info-added-directory actual.info-added-directory
"
test_expect_success 'info --url added-directory' '
- test "$(cd gitwc; git svn info --url added-directory)" \
+ test "$(cd gitwc && git svn info --url added-directory)" \
= "$quoted_svnrepo/added-directory"
'
@@ -213,16 +213,16 @@ test_expect_success 'info added-symlink-file' "
ln -s added-file added-symlink-file &&
svn_cmd add added-symlink-file > /dev/null
) &&
- (cd svnwc; svn info added-symlink-file) \
+ (cd svnwc && svn info added-symlink-file) \
> expected.info-added-symlink-file &&
- (cd gitwc; git svn info added-symlink-file) \
+ (cd gitwc && git svn info added-symlink-file) \
> actual.info-added-symlink-file &&
test_cmp_info expected.info-added-symlink-file \
actual.info-added-symlink-file
"
test_expect_success 'info --url added-symlink-file' '
- test "$(cd gitwc; git svn info --url added-symlink-file)" \
+ test "$(cd gitwc && git svn info --url added-symlink-file)" \
= "$quoted_svnrepo/added-symlink-file"
'
@@ -237,16 +237,16 @@ test_expect_success 'info added-symlink-directory' "
ln -s added-directory added-symlink-directory &&
svn_cmd add added-symlink-directory > /dev/null
) &&
- (cd svnwc; svn info added-symlink-directory) \
+ (cd svnwc && svn info added-symlink-directory) \
> expected.info-added-symlink-directory &&
- (cd gitwc; git svn info added-symlink-directory) \
+ (cd gitwc && git svn info added-symlink-directory) \
> actual.info-added-symlink-directory &&
test_cmp_info expected.info-added-symlink-directory \
actual.info-added-symlink-directory
"
test_expect_success 'info --url added-symlink-directory' '
- test "$(cd gitwc; git svn info --url added-symlink-directory)" \
+ test "$(cd gitwc && git svn info --url added-symlink-directory)" \
= "$quoted_svnrepo/added-symlink-directory"
'
@@ -259,13 +259,13 @@ test_expect_success 'info deleted-file' "
cd svnwc &&
svn_cmd rm --force file > /dev/null
) &&
- (cd svnwc; svn info file) >expected.info-deleted-file &&
- (cd gitwc; git svn info file) >actual.info-deleted-file &&
+ (cd svnwc && svn info file) >expected.info-deleted-file &&
+ (cd gitwc && git svn info file) >actual.info-deleted-file &&
test_cmp_info expected.info-deleted-file actual.info-deleted-file
"
test_expect_success 'info --url file (deleted)' '
- test "$(cd gitwc; git svn info --url file)" \
+ test "$(cd gitwc && git svn info --url file)" \
= "$quoted_svnrepo/file"
'
@@ -278,13 +278,13 @@ test_expect_success 'info deleted-directory' "
cd svnwc &&
svn_cmd rm --force directory > /dev/null
) &&
- (cd svnwc; svn info directory) >expected.info-deleted-directory &&
- (cd gitwc; git svn info directory) >actual.info-deleted-directory &&
+ (cd svnwc && svn info directory) >expected.info-deleted-directory &&
+ (cd gitwc && git svn info directory) >actual.info-deleted-directory &&
test_cmp_info expected.info-deleted-directory actual.info-deleted-directory
"
test_expect_success 'info --url directory (deleted)' '
- test "$(cd gitwc; git svn info --url directory)" \
+ test "$(cd gitwc && git svn info --url directory)" \
= "$quoted_svnrepo/directory"
'
@@ -297,13 +297,13 @@ test_expect_success 'info deleted-symlink-file' "
cd svnwc &&
svn_cmd rm --force symlink-file > /dev/null
) &&
- (cd svnwc; svn info symlink-file) >expected.info-deleted-symlink-file &&
- (cd gitwc; git svn info symlink-file) >actual.info-deleted-symlink-file &&
+ (cd svnwc && svn info symlink-file) >expected.info-deleted-symlink-file &&
+ (cd gitwc && git svn info symlink-file) >actual.info-deleted-symlink-file &&
test_cmp_info expected.info-deleted-symlink-file actual.info-deleted-symlink-file
"
test_expect_success 'info --url symlink-file (deleted)' '
- test "$(cd gitwc; git svn info --url symlink-file)" \
+ test "$(cd gitwc && git svn info --url symlink-file)" \
= "$quoted_svnrepo/symlink-file"
'
@@ -316,13 +316,13 @@ test_expect_success 'info deleted-symlink-directory' "
cd svnwc &&
svn_cmd rm --force symlink-directory > /dev/null
) &&
- (cd svnwc; svn info symlink-directory) >expected.info-deleted-symlink-directory &&
- (cd gitwc; git svn info symlink-directory) >actual.info-deleted-symlink-directory &&
+ (cd svnwc && svn info symlink-directory) >expected.info-deleted-symlink-directory &&
+ (cd gitwc && git svn info symlink-directory) >actual.info-deleted-symlink-directory &&
test_cmp_info expected.info-deleted-symlink-directory actual.info-deleted-symlink-directory
"
test_expect_success 'info --url symlink-directory (deleted)' '
- test "$(cd gitwc; git svn info --url symlink-directory)" \
+ test "$(cd gitwc && git svn info --url symlink-directory)" \
= "$quoted_svnrepo/symlink-directory"
'
@@ -331,27 +331,27 @@ test_expect_success 'info --url symlink-directory (deleted)' '
test_expect_success 'info unknown-file' "
echo two > gitwc/unknown-file &&
- (cd gitwc; test_must_fail git svn info unknown-file) \
+ (cd gitwc && test_must_fail git svn info unknown-file) \
2> actual.info-unknown-file &&
grep unknown-file actual.info-unknown-file
"
test_expect_success 'info --url unknown-file' '
echo two > gitwc/unknown-file &&
- (cd gitwc; test_must_fail git svn info --url unknown-file) \
+ (cd gitwc && test_must_fail git svn info --url unknown-file) \
2> actual.info-url-unknown-file &&
grep unknown-file actual.info-url-unknown-file
'
test_expect_success 'info unknown-directory' "
mkdir gitwc/unknown-directory svnwc/unknown-directory &&
- (cd gitwc; test_must_fail git svn info unknown-directory) \
+ (cd gitwc && test_must_fail git svn info unknown-directory) \
2> actual.info-unknown-directory &&
grep unknown-directory actual.info-unknown-directory
"
test_expect_success 'info --url unknown-directory' '
- (cd gitwc; test_must_fail git svn info --url unknown-directory) \
+ (cd gitwc && test_must_fail git svn info --url unknown-directory) \
2> actual.info-url-unknown-directory &&
grep unknown-directory actual.info-url-unknown-directory
'
@@ -361,13 +361,13 @@ test_expect_success 'info unknown-symlink-file' "
cd gitwc &&
ln -s unknown-file unknown-symlink-file
) &&
- (cd gitwc; test_must_fail git svn info unknown-symlink-file) \
+ (cd gitwc && test_must_fail git svn info unknown-symlink-file) \
2> actual.info-unknown-symlink-file &&
grep unknown-symlink-file actual.info-unknown-symlink-file
"
test_expect_success 'info --url unknown-symlink-file' '
- (cd gitwc; test_must_fail git svn info --url unknown-symlink-file) \
+ (cd gitwc && test_must_fail git svn info --url unknown-symlink-file) \
2> actual.info-url-unknown-symlink-file &&
grep unknown-symlink-file actual.info-url-unknown-symlink-file
'
@@ -377,13 +377,13 @@ test_expect_success 'info unknown-symlink-directory' "
cd gitwc &&
ln -s unknown-directory unknown-symlink-directory
) &&
- (cd gitwc; test_must_fail git svn info unknown-symlink-directory) \
+ (cd gitwc && test_must_fail git svn info unknown-symlink-directory) \
2> actual.info-unknown-symlink-directory &&
grep unknown-symlink-directory actual.info-unknown-symlink-directory
"
test_expect_success 'info --url unknown-symlink-directory' '
- (cd gitwc; test_must_fail git svn info --url unknown-symlink-directory) \
+ (cd gitwc && test_must_fail git svn info --url unknown-symlink-directory) \
2> actual.info-url-unknown-symlink-directory &&
grep unknown-symlink-directory actual.info-url-unknown-symlink-directory
'
diff --git a/t/t9122-git-svn-author.sh b/t/t9122-git-svn-author.sh
index 30013b7bb9..9e8fe38e7e 100755
--- a/t/t9122-git-svn-author.sh
+++ b/t/t9122-git-svn-author.sh
@@ -7,8 +7,8 @@ test_expect_success 'setup svn repository' '
svn_cmd checkout "$svnrepo" work.svn &&
(
cd work.svn &&
- echo >file
- svn_cmd add file
+ echo >file &&
+ svn_cmd add file &&
svn_cmd commit -m "first commit" file
)
'
@@ -17,7 +17,7 @@ test_expect_success 'interact with it via git svn' '
mkdir work.git &&
(
cd work.git &&
- git svn init "$svnrepo"
+ git svn init "$svnrepo" &&
git svn fetch &&
echo modification >file &&
diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh
index 8dbd6476fa..2c213ae654 100755
--- a/t/t9129-git-svn-i18n-commitencoding.sh
+++ b/t/t9129-git-svn-i18n-commitencoding.sh
@@ -51,7 +51,7 @@ do
git add F &&
git commit -a -F "$TEST_DIRECTORY"/t3900/$H.txt &&
E=$(git cat-file commit HEAD | sed -ne "s/^encoding //p") &&
- test "z$E" = "z$H"
+ test "z$E" = "z$H" &&
compare_git_head_with "$TEST_DIRECTORY"/t3900/$H.txt
)
'
diff --git a/t/t9130-git-svn-authors-file.sh b/t/t9130-git-svn-authors-file.sh
index d8262854bb..cb764bcadc 100755
--- a/t/t9130-git-svn-authors-file.sh
+++ b/t/t9130-git-svn-authors-file.sh
@@ -25,7 +25,7 @@ test_expect_success 'start import with incomplete authors file' '
test_expect_success 'imported 2 revisions successfully' '
(
- cd x
+ cd x &&
git rev-list refs/remotes/git-svn >actual &&
test_line_count = 2 actual &&
git rev-list -1 --pretty=raw refs/remotes/git-svn >actual &&
@@ -42,7 +42,7 @@ EOF
test_expect_success 'continues to import once authors have been added' '
(
- cd x
+ cd x &&
git svn fetch --authors-file=../svn-authors &&
git rev-list refs/remotes/git-svn >actual &&
test_line_count = 4 actual &&
diff --git a/t/t9131-git-svn-empty-symlink.sh b/t/t9131-git-svn-empty-symlink.sh
index f762038f0e..3bf4255aa3 100755
--- a/t/t9131-git-svn-empty-symlink.sh
+++ b/t/t9131-git-svn-empty-symlink.sh
@@ -85,7 +85,7 @@ EOF
test_expect_success 'clone using git svn' 'git svn clone -r1 "$svnrepo" x'
test_expect_success 'enable broken symlink workaround' \
'(cd x && git config svn.brokenSymlinkWorkaround true)'
-test_expect_success '"bar" is an empty file' 'test -f x/bar && ! test -s x/bar'
+test_expect_success '"bar" is an empty file' 'test_must_be_empty x/bar'
test_expect_success 'get "bar" => symlink fix from svn' \
'(cd x && git svn rebase)'
test_expect_success SYMLINKS '"bar" becomes a symlink' 'test -h x/bar'
@@ -94,14 +94,14 @@ test_expect_success SYMLINKS '"bar" becomes a symlink' 'test -h x/bar'
test_expect_success 'clone using git svn' 'git svn clone -r1 "$svnrepo" y'
test_expect_success 'disable broken symlink workaround' \
'(cd y && git config svn.brokenSymlinkWorkaround false)'
-test_expect_success '"bar" is an empty file' 'test -f y/bar && ! test -s y/bar'
+test_expect_success '"bar" is an empty file' 'test_must_be_empty y/bar'
test_expect_success 'get "bar" => symlink fix from svn' \
'(cd y && git svn rebase)'
test_expect_success '"bar" does not become a symlink' '! test -L y/bar'
# svn.brokenSymlinkWorkaround is unset
test_expect_success 'clone using git svn' 'git svn clone -r1 "$svnrepo" z'
-test_expect_success '"bar" is an empty file' 'test -f z/bar && ! test -s z/bar'
+test_expect_success '"bar" is an empty file' 'test_must_be_empty z/bar'
test_expect_success 'get "bar" => symlink fix from svn' \
'(cd z && git svn rebase)'
test_expect_success '"bar" does not become a symlink' '! test -L z/bar'
diff --git a/t/t9134-git-svn-ignore-paths.sh b/t/t9134-git-svn-ignore-paths.sh
index 09ff10cd9b..fff49c4100 100755
--- a/t/t9134-git-svn-ignore-paths.sh
+++ b/t/t9134-git-svn-ignore-paths.sh
@@ -82,7 +82,7 @@ test_expect_success 'update git svn-cloned repo (option ignore)' '
test_expect_success 'SVN-side change inside of ignored www' '
(
cd s &&
- echo zaq >> www/test_www.txt
+ 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"
@@ -114,8 +114,8 @@ test_expect_success 'update git svn-cloned repo (option ignore)' '
test_expect_success 'SVN-side change in and out of ignored www' '
(
cd s &&
- echo cvf >> www/test_www.txt
- echo ygg >> qqq/test_qqq.txt
+ echo cvf >> www/test_www.txt &&
+ 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"
diff --git a/t/t9135-git-svn-moved-branch-empty-file.sh b/t/t9135-git-svn-moved-branch-empty-file.sh
index 93db45db7d..2f80b216fe 100755
--- a/t/t9135-git-svn-moved-branch-empty-file.sh
+++ b/t/t9135-git-svn-moved-branch-empty-file.sh
@@ -13,8 +13,7 @@ test_expect_success 'test that b1 exists and is empty' '
(
cd x &&
git reset --hard origin/branch-c &&
- test -f b1 &&
- ! test -s b1
+ test_must_be_empty b1
)
'
diff --git a/t/t9137-git-svn-dcommit-clobber-series.sh b/t/t9137-git-svn-dcommit-clobber-series.sh
index 5fa07a369f..067b15bad2 100755
--- a/t/t9137-git-svn-dcommit-clobber-series.sh
+++ b/t/t9137-git-svn-dcommit-clobber-series.sh
@@ -7,7 +7,7 @@ test_description='git svn dcommit clobber series'
test_expect_success 'initialize repo' '
mkdir import &&
(cd import &&
- awk "BEGIN { for (i = 1; i < 64; i++) { print i } }" > file
+ awk "BEGIN { for (i = 1; i < 64; i++) { print i } }" > file &&
svn_cmd import -m "initial" . "$svnrepo"
) &&
git svn init "$svnrepo" &&
diff --git a/t/t9138-git-svn-authors-prog.sh b/t/t9138-git-svn-authors-prog.sh
index 93ef44fae8..027b416720 100755
--- a/t/t9138-git-svn-authors-prog.sh
+++ b/t/t9138-git-svn-authors-prog.sh
@@ -38,7 +38,7 @@ test_expect_success 'import authors with prog and file' '
test_expect_success 'imported 6 revisions successfully' '
(
- cd x
+ cd x &&
git rev-list refs/remotes/git-svn >actual &&
test_line_count = 6 actual
)
@@ -46,7 +46,7 @@ test_expect_success 'imported 6 revisions successfully' '
test_expect_success 'authors-prog ran correctly' '
(
- cd x
+ cd x &&
git rev-list -1 --pretty=raw refs/remotes/git-svn~1 >actual &&
grep "^author ee-foo <ee-foo@example\.com> " actual &&
git rev-list -1 --pretty=raw refs/remotes/git-svn~2 >actual &&
@@ -62,7 +62,7 @@ test_expect_success 'authors-prog ran correctly' '
test_expect_success 'authors-file overrode authors-prog' '
(
- cd x
+ cd x &&
git rev-list -1 --pretty=raw refs/remotes/git-svn >actual &&
grep "^author FFFFFFF FFFFFFF <fFf@other\.example\.com> " actual
)
diff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh
index 6d3130e618..5f91c0d68b 100755
--- a/t/t9146-git-svn-empty-dirs.sh
+++ b/t/t9146-git-svn-empty-dirs.sh
@@ -21,7 +21,7 @@ test_expect_success 'empty directories exist' '
do
if ! test -d "$i"
then
- echo >&2 "$i does not exist"
+ echo >&2 "$i does not exist" &&
exit 1
fi
done
@@ -38,7 +38,7 @@ test_expect_success 'option automkdirs set to false' '
do
if test -d "$i"
then
- echo >&2 "$i exists"
+ echo >&2 "$i exists" &&
exit 1
fi
done
@@ -63,7 +63,7 @@ test_expect_success 'git svn mkdirs recreates empty directories' '
do
if ! test -d "$i"
then
- echo >&2 "$i does not exist"
+ echo >&2 "$i does not exist" &&
exit 1
fi
done
@@ -79,21 +79,21 @@ test_expect_success 'git svn mkdirs -r works' '
do
if ! test -d "$i"
then
- echo >&2 "$i does not exist"
+ echo >&2 "$i does not exist" &&
exit 1
fi
- done
+ done &&
if test -d "! !"
then
- echo >&2 "$i should not exist"
+ echo >&2 "$i should not exist" &&
exit 1
- fi
+ fi &&
git svn mkdirs -r8 &&
if ! test -d "! !"
then
- echo >&2 "$i not exist"
+ echo >&2 "$i not exist" &&
exit 1
fi
)
@@ -115,7 +115,7 @@ test_expect_success 'empty directories in trunk exist' '
do
if ! test -d "$i"
then
- echo >&2 "$i does not exist"
+ echo >&2 "$i does not exist" &&
exit 1
fi
done
@@ -148,7 +148,7 @@ test_expect_success 'git svn gc-ed files work' '
do
if ! test -d "$i"
then
- echo >&2 "$i does not exist"
+ echo >&2 "$i does not exist" &&
exit 1
fi
done
diff --git a/t/t9147-git-svn-include-paths.sh b/t/t9147-git-svn-include-paths.sh
index a90ff58629..d292bf9f55 100755
--- a/t/t9147-git-svn-include-paths.sh
+++ b/t/t9147-git-svn-include-paths.sh
@@ -84,7 +84,7 @@ test_expect_success 'update git svn-cloned repo (option include)' '
test_expect_success 'SVN-side change inside of ignored www' '
(
cd s &&
- echo zaq >> www/test_www.txt
+ 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"
@@ -116,8 +116,8 @@ test_expect_success 'update git svn-cloned repo (option include)' '
test_expect_success 'SVN-side change in and out of included qqq' '
(
cd s &&
- echo cvf >> www/test_www.txt
- echo ygg >> qqq/test_qqq.txt
+ echo cvf >> www/test_www.txt &&
+ 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"
diff --git a/t/t9152-svn-empty-dirs-after-gc.sh b/t/t9152-svn-empty-dirs-after-gc.sh
index 301e779709..89f285d082 100755
--- a/t/t9152-svn-empty-dirs-after-gc.sh
+++ b/t/t9152-svn-empty-dirs-after-gc.sh
@@ -30,7 +30,7 @@ test_expect_success 'git svn mkdirs recreates empty directories after git svn gc
do
if ! test -d "$i"
then
- echo >&2 "$i does not exist"
+ echo >&2 "$i does not exist" &&
exit 1
fi
done
diff --git a/t/t9164-git-svn-dcommit-concurrent.sh b/t/t9164-git-svn-dcommit-concurrent.sh
index d8464d4218..90346ff4e9 100755
--- a/t/t9164-git-svn-dcommit-concurrent.sh
+++ b/t/t9164-git-svn-dcommit-concurrent.sh
@@ -12,7 +12,7 @@ test_expect_success 'setup svn repository' '
svn_cmd checkout "$svnrepo" work.svn &&
(
cd work.svn &&
- echo >file && echo > auto_updated_file
+ echo >file && echo > auto_updated_file &&
svn_cmd add file auto_updated_file &&
svn_cmd commit -m "initial commit"
) &&
diff --git a/t/t9165-git-svn-fetch-merge-branch-of-branch.sh b/t/t9165-git-svn-fetch-merge-branch-of-branch.sh
index fa3ef3b1f7..a4813c2b09 100755
--- a/t/t9165-git-svn-fetch-merge-branch-of-branch.sh
+++ b/t/t9165-git-svn-fetch-merge-branch-of-branch.sh
@@ -39,7 +39,7 @@ test_expect_success 'initialize source svn repo' '
svn_cmd commit -m trunk &&
svn_cmd switch "$svnrepo"/branches/branch2 &&
svn_cmd merge "$svnrepo"/trunk &&
- svn_cmd commit -m "merge trunk"
+ svn_cmd commit -m "merge trunk" &&
svn_cmd switch "$svnrepo"/trunk &&
svn_cmd merge --reintegrate "$svnrepo"/branches/branch2 &&
svn_cmd commit -m "merge branch2"
diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
index 1319415ba8..c5946cb0b8 100755
--- a/t/t9200-git-cvsexportcommit.sh
+++ b/t/t9200-git-cvsexportcommit.sh
@@ -43,11 +43,11 @@ check_entries () {
sed -ne '/^\//p' "$1/CVS/Entries" | sort | cut -d/ -f2,3,5 >actual
if test -z "$2"
then
- >expected
+ test_must_be_empty actual
else
printf '%s\n' "$2" | tr '|' '\012' >expected
+ test_cmp expected actual
fi
- test_cmp expected actual
}
test_expect_success \
@@ -187,7 +187,7 @@ test_expect_success \
git commit -a -m "Update with spaces" &&
id=$(git rev-list --max-count=1 HEAD) &&
(cd "$CVSWORK" &&
- git cvsexportcommit -c $id
+ git cvsexportcommit -c $id &&
check_entries "G g" "with spaces.png/1.2/-kb|with spaces.txt/1.2/"
)'
@@ -245,7 +245,7 @@ test_expect_success FILEMODE \
git add G/off &&
git commit -a -m "Execute test" &&
(cd "$CVSWORK" &&
- git cvsexportcommit -c HEAD
+ git cvsexportcommit -c HEAD &&
test -x G/on &&
! test -x G/off
)'
@@ -303,7 +303,7 @@ test_expect_success 're-commit a removed filename which remains in CVS attic' '
git add attic_gremlin &&
git commit -m "Added attic_gremlin" &&
git cvsexportcommit -w "$CVSWORK" -c HEAD &&
- (cd "$CVSWORK"; cvs -Q update -d) &&
+ (cd "$CVSWORK" && cvs -Q update -d) &&
test -f "$CVSWORK/attic_gremlin"
'
diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
index 9e7f96223d..40fe7e4976 100755
--- a/t/t9300-fast-import.sh
+++ b/t/t9300-fast-import.sh
@@ -2191,12 +2191,11 @@ test_expect_success 'R: --import-marks-if-exists' '
test_expect_success 'R: feature import-marks-if-exists' '
rm -f io.marks &&
- >expect &&
git fast-import --export-marks=io.marks <<-\EOF &&
feature import-marks-if-exists=not_io.marks
EOF
- test_cmp expect io.marks &&
+ test_must_be_empty io.marks &&
blob=$(echo hi | git hash-object --stdin) &&
@@ -2227,13 +2226,11 @@ test_expect_success 'R: feature import-marks-if-exists' '
EOF
test_cmp expect io.marks &&
- >expect &&
-
git fast-import --import-marks-if-exists=not_io.marks \
--export-marks=io.marks <<-\EOF &&
feature import-marks-if-exists=io.marks
EOF
- test_cmp expect io.marks
+ test_must_be_empty io.marks
'
test_expect_success 'R: import to output marks works without any content' '
@@ -3147,7 +3144,10 @@ background_import_then_checkpoint () {
echo $! >V.pid
# We don't mind if fast-import has already died by the time the test
# ends.
- test_when_finished "exec 8>&-; exec 9>&-; kill $(cat V.pid) || true"
+ test_when_finished "
+ exec 8>&-; exec 9>&-;
+ kill $(cat V.pid) && wait $(cat V.pid)
+ true"
# Start in the background to ensure we adhere strictly to (blocking)
# pipes writing sequence. We want to assume that the write below could
diff --git a/t/t9302-fast-import-unpack-limit.sh b/t/t9302-fast-import-unpack-limit.sh
index a04de14677..bb1c39cfcc 100755
--- a/t/t9302-fast-import-unpack-limit.sh
+++ b/t/t9302-fast-import-unpack-limit.sh
@@ -80,7 +80,7 @@ test_expect_success 'lookups after checkpoint works' '
do
if test $n -gt 30
then
- echo >&2 "checkpoint did not update branch"
+ echo >&2 "checkpoint did not update branch" &&
exit 1
else
n=$(($n + 1))
diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh
index 06742748e9..a5e5dca753 100755
--- a/t/t9400-git-cvsserver-server.sh
+++ b/t/t9400-git-cvsserver-server.sh
@@ -328,7 +328,7 @@ test_expect_success 'cvs update (subdirectories)' \
'(for dir in A A/B A/B/C A/D E; do
mkdir $dir &&
echo "test file in $dir" >"$dir/file_in_$(echo $dir|sed -e "s#/# #g")" &&
- git add $dir;
+ git add $dir
done) &&
git commit -q -m "deep sub directory structure" &&
git push gitcvs.git >/dev/null &&
@@ -371,7 +371,7 @@ test_expect_success 'cvs update (merge)' \
'echo Line 0 >expected &&
for i in 1 2 3 4 5 6 7
do
- echo Line $i >>merge
+ echo Line $i >>merge &&
echo Line $i >>expected
done &&
echo Line 8 >>expected &&
@@ -382,7 +382,7 @@ test_expect_success 'cvs update (merge)' \
GIT_CONFIG="$git_config" cvs -Q update &&
test "$(echo $(grep merge CVS/Entries|cut -d/ -f2,3,5))" = "merge/1.1/" &&
test_cmp merge ../merge &&
- ( echo Line 0; cat merge ) >merge.tmp &&
+ ( echo Line 0 && cat merge ) >merge.tmp &&
mv merge.tmp merge &&
cd "$WORKDIR" &&
echo Line 8 >>merge &&
@@ -410,7 +410,7 @@ do
done
test_expect_success 'cvs update (conflict merge)' \
- '( echo LINE 0; cat merge ) >merge.tmp &&
+ '( echo LINE 0 && cat merge ) >merge.tmp &&
mv merge.tmp merge &&
git add merge &&
git commit -q -m "Merge test (conflict)" &&
diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh
index 804ce3850f..5dfee07d9a 100755
--- a/t/t9600-cvsimport.sh
+++ b/t/t9600-cvsimport.sh
@@ -135,7 +135,7 @@ test_expect_success PERL 'second update has correct .git/cvs-revisions' '
(cd module-git &&
git log --format="o_fortuna 1.1 %H" -1 HEAD^^ &&
- git log --format="o_fortuna 1.2 %H" -1 HEAD^
+ git log --format="o_fortuna 1.2 %H" -1 HEAD^ &&
git log --format="tick 1.1 %H" -1 HEAD) > expected &&
test_cmp expected module-git/.git/cvs-revisions
'
diff --git a/t/t9800-git-p4-basic.sh b/t/t9800-git-p4-basic.sh
index 4849edc4ef..729cd25770 100755
--- a/t/t9800-git-p4-basic.sh
+++ b/t/t9800-git-p4-basic.sh
@@ -261,6 +261,35 @@ test_expect_success 'unresolvable host in P4PORT should display error' '
)
'
+# Test following scenarios:
+# - Without ".git/hooks/p4-pre-submit" , submit should continue
+# - With the hook returning 0, submit should continue
+# - With the hook returning 1, submit should abort
+test_expect_success 'run hook p4-pre-submit before submit' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ echo "hello world" >hello.txt &&
+ git add hello.txt &&
+ git commit -m "add hello.txt" &&
+ git config git-p4.skipSubmitEdit true &&
+ git p4 submit --dry-run >out &&
+ grep "Would apply" out &&
+ mkdir -p .git/hooks &&
+ write_script .git/hooks/p4-pre-submit <<-\EOF &&
+ exit 0
+ EOF
+ git p4 submit --dry-run >out &&
+ grep "Would apply" out &&
+ write_script .git/hooks/p4-pre-submit <<-\EOF &&
+ exit 1
+ EOF
+ test_must_fail git p4 submit --dry-run >errs 2>&1 &&
+ ! grep "Would apply" errs
+ )
+'
+
test_expect_success 'submit from detached head' '
test_when_finished cleanup_git &&
git p4 clone --dest="$git" //depot &&
diff --git a/t/t9802-git-p4-filetype.sh b/t/t9802-git-p4-filetype.sh
index 1fc9b33aeb..9978352d78 100755
--- a/t/t9802-git-p4-filetype.sh
+++ b/t/t9802-git-p4-filetype.sh
@@ -310,7 +310,7 @@ test_expect_success SYMLINKS 'empty symlink target' '
# p4 to sync here will make it generate errors.
cd "$cli" &&
p4 print -q //depot/empty-symlink#2 >out &&
- test ! -s out
+ test_must_be_empty out
) &&
test_when_finished cleanup_git &&
diff --git a/t/t9806-git-p4-options.sh b/t/t9806-git-p4-options.sh
index 1ab76c4246..3f5291b857 100755
--- a/t/t9806-git-p4-options.sh
+++ b/t/t9806-git-p4-options.sh
@@ -134,7 +134,7 @@ test_expect_success 'clone --changesfile' '
(
cd "$git" &&
git log --oneline p4/master >lines &&
- test_line_count = 2 lines
+ test_line_count = 2 lines &&
test_path_is_file file1 &&
test_path_is_missing file2 &&
test_path_is_file file3
diff --git a/t/t9810-git-p4-rcs.sh b/t/t9810-git-p4-rcs.sh
index 8134ab439b..cc53debe19 100755
--- a/t/t9810-git-p4-rcs.sh
+++ b/t/t9810-git-p4-rcs.sh
@@ -161,7 +161,7 @@ test_expect_success 'cleanup after failure' '
test_expect_success 'ktext expansion should not expand multi-line $File::' '
(
cd "$cli" &&
- cat >lv.pm <<-\EOF
+ cat >lv.pm <<-\EOF &&
my $wanted = sub { my $f = $File::Find::name;
if ( -f && $f =~ /foo/ ) {
EOF
diff --git a/t/t9811-git-p4-label-import.sh b/t/t9811-git-p4-label-import.sh
index decb66ba30..602b0a5d5c 100755
--- a/t/t9811-git-p4-label-import.sh
+++ b/t/t9811-git-p4-label-import.sh
@@ -133,7 +133,7 @@ test_expect_success 'export git tags to p4' '
p4 labels ... | grep LIGHTWEIGHT_TAG &&
p4 label -o GIT_TAG_1 | grep "tag created in git:xyzzy" &&
p4 sync ...@GIT_TAG_1 &&
- ! test -f main/f10
+ ! test -f main/f10 &&
p4 sync ...@GIT_TAG_2 &&
test -f main/f10
)
diff --git a/t/t9814-git-p4-rename.sh b/t/t9814-git-p4-rename.sh
index e7e0268e98..60baa06e27 100755
--- a/t/t9814-git-p4-rename.sh
+++ b/t/t9814-git-p4-rename.sh
@@ -9,23 +9,11 @@ test_expect_success 'start p4d' '
'
# We rely on this behavior to detect for p4 move availability.
-test_expect_success 'p4 help unknown returns 1' '
+test_expect_success '"p4 help unknown" errors out' '
(
cd "$cli" &&
- (
- p4 help client >errs 2>&1
- echo $? >retval
- )
- echo 0 >expected &&
- test_cmp expected retval &&
- rm retval &&
- (
- p4 help nosuchcommand >errs 2>&1
- echo $? >retval
- )
- echo 1 >expected &&
- test_cmp expected retval &&
- rm retval
+ p4 help client &&
+ ! p4 help nosuchcommand
)
'
diff --git a/t/t9815-git-p4-submit-fail.sh b/t/t9815-git-p4-submit-fail.sh
index 37b42d03a2..eaf03a6563 100755
--- a/t/t9815-git-p4-submit-fail.sh
+++ b/t/t9815-git-p4-submit-fail.sh
@@ -394,7 +394,7 @@ test_expect_success 'cleanup rename after submit cancel' '
(
cd "$cli" &&
test_path_is_missing text2 &&
- p4 fstat -T action text2 2>&1 | grep "no such file"
+ p4 fstat -T action text2 2>&1 | grep "no such file" &&
test_path_is_file text &&
! p4 fstat -T action text
)
diff --git a/t/t9830-git-p4-symlink-dir.sh b/t/t9830-git-p4-symlink-dir.sh
index 3dc528bb1e..2ad1b0810d 100755
--- a/t/t9830-git-p4-symlink-dir.sh
+++ b/t/t9830-git-p4-symlink-dir.sh
@@ -30,7 +30,7 @@ test_expect_success 'symlinked directory' '
(
cd "$cli" &&
p4 sync &&
- test -L some/sub/directory/subdir2
+ test -L some/sub/directory/subdir2 &&
test_path_is_file some/sub/directory/subdir2/file.t
)
diff --git a/t/t9831-git-p4-triggers.sh b/t/t9831-git-p4-triggers.sh
index bbcf14c664..be44c9751a 100755
--- a/t/t9831-git-p4-triggers.sh
+++ b/t/t9831-git-p4-triggers.sh
@@ -13,7 +13,7 @@ test_expect_success 'init depot' '
cd "$cli" &&
echo file1 >file1 &&
p4 add file1 &&
- p4 submit -d "change 1"
+ p4 submit -d "change 1" &&
echo file2 >file2 &&
p4 add file2 &&
p4 submit -d "change 2"
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 3b3a7b66e4..175f83d704 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -1103,7 +1103,7 @@ test_expect_success '__git_complete_refs - remote' '
master-in-other Z
EOF
(
- cur=
+ cur= &&
__git_complete_refs --remote=other &&
print_comp
) &&
@@ -1122,7 +1122,7 @@ test_expect_success '__git_complete_refs - track' '
master-in-other Z
EOF
(
- cur=
+ cur= &&
__git_complete_refs --track &&
print_comp
) &&
@@ -1278,7 +1278,7 @@ test_expect_success 'setup for path completion tests' '
touch BS\\dir/DQ\"file \
'$'separators\034in\035dir/sep\036in\037file''
then
- test_set_prereq FUNNYNAMES
+ test_set_prereq FUNNIERNAMES
else
rm -rf BS\\dir '$'separators\034in\035dir''
fi
@@ -1320,7 +1320,7 @@ test_expect_success '__git_complete_index_file - UTF-8 in ls-files output' '
test_path_completion árvíztűrő/С "árvíztűrő/Сайн яваарай"
'
-test_expect_success FUNNYNAMES \
+test_expect_success FUNNIERNAMES \
'__git_complete_index_file - C-style escapes in ls-files output' '
test_path_completion BS \
BS\\dir &&
@@ -1332,7 +1332,7 @@ test_expect_success FUNNYNAMES \
BS\\dir/DQ\"file
'
-test_expect_success FUNNYNAMES \
+test_expect_success FUNNIERNAMES \
'__git_complete_index_file - \nnn-escaped characters in ls-files output' '
test_path_completion sep '$'separators\034in\035dir'' &&
test_path_completion '$'separators\034i'' \
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index c3b89ae783..81a5179e28 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -63,18 +63,15 @@ test_expect_success 'prompt - unborn branch' '
test_cmp expected "$actual"
'
-repo_with_newline='repo
-with
-newline'
-
-if test_have_prereq !MINGW && mkdir "$repo_with_newline" 2>/dev/null
-then
- test_set_prereq FUNNYNAMES
-else
+if test_have_prereq !FUNNYNAMES; then
say 'Your filesystem does not allow newlines in filenames.'
fi
test_expect_success FUNNYNAMES 'prompt - with newline in path' '
+ repo_with_newline="repo
+with
+newline" &&
+ mkdir "$repo_with_newline" &&
printf " (master)" >expected &&
git init "$repo_with_newline" &&
test_when_finished "rm -rf \"$repo_with_newline\"" &&
@@ -516,10 +513,9 @@ test_expect_success 'prompt - format string starting with dash' '
test_expect_success 'prompt - pc mode' '
printf "BEFORE: (\${__git_ps1_branch_name}):AFTER\\nmaster" >expected &&
- printf "" >expected_output &&
(
__git_ps1 "BEFORE:" ":AFTER" >"$actual" &&
- test_cmp expected_output "$actual" &&
+ test_must_be_empty "$actual" &&
printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
) &&
test_cmp expected "$actual"
@@ -529,7 +525,7 @@ test_expect_success 'prompt - bash color pc mode - branch name' '
printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear}):AFTER\\nmaster" >expected &&
(
GIT_PS1_SHOWCOLORHINTS=y &&
- __git_ps1 "BEFORE:" ":AFTER" >"$actual"
+ __git_ps1 "BEFORE:" ":AFTER" >"$actual" &&
printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
) &&
test_cmp expected "$actual"
@@ -715,13 +711,12 @@ test_expect_success 'prompt - hide if pwd ignored - env var set, config disabled
'
test_expect_success 'prompt - hide if pwd ignored - env var set, config unset' '
- printf "" >expected &&
(
cd ignored_dir &&
GIT_PS1_HIDE_IF_PWD_IGNORED=y &&
__git_ps1 >"$actual"
) &&
- test_cmp expected "$actual"
+ test_must_be_empty "$actual"
'
test_expect_success 'prompt - hide if pwd ignored - env var set, config unset, pc mode' '
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 2b2181dca0..d82fac9d79 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -42,6 +42,8 @@ test_decode_color () {
function name(n) {
if (n == 0) return "RESET";
if (n == 1) return "BOLD";
+ if (n == 2) return "FAINT";
+ if (n == 3) return "ITALIC";
if (n == 7) return "REVERSE";
if (n == 30) return "BLACK";
if (n == 31) return "RED";
@@ -565,6 +567,14 @@ test_path_is_dir () {
fi
}
+test_path_exists () {
+ if ! test -e "$1"
+ then
+ echo "Path $1 doesn't exist. $2"
+ false
+ fi
+}
+
# Check if the directory exists and is empty as expected, barf otherwise.
test_dir_is_empty () {
test_path_is_dir "$1" &&
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 28315706be..3f95bfda60 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -675,7 +675,8 @@ 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)"
+ if $(printf '%s\n' "$1" | sed -f "$GIT_BUILD_DIR/t/chainlint.sed" | grep -q '?![A-Z][A-Z]*?!') ||
+ test "OK-117" != "$(test_eval_ "(exit 117) && $1${LF}${LF}echo OK-\$?" 3>&1)"
then
error "bug in the test script: broken &&-chain or run-away HERE-DOC: $1"
fi
@@ -866,7 +867,7 @@ then
# handle only executables, unless they are shell libraries that
# need to be in the exec-path.
test -x "$1" ||
- test "# " = "$(head -c 2 <"$1")" ||
+ test "# " = "$(test_copy_bytes 2 <"$1")" ||
return;
base=$(basename "$1")
@@ -881,7 +882,7 @@ then
# do not override scripts
if test -x "$symlink_target" &&
test ! -d "$symlink_target" &&
- test "#!" != "$(head -c 2 < "$symlink_target")"
+ test "#!" != "$(test_copy_bytes 2 <"$symlink_target")"
then
symlink_target=../valgrind.sh
fi
@@ -1082,6 +1083,12 @@ else
test_set_prereq C_LOCALE_OUTPUT
fi
+if test -z "$GIT_TEST_CHECK_CACHE_TREE"
+then
+ GIT_TEST_CHECK_CACHE_TREE=true
+ export GIT_TEST_CHECK_CACHE_TREE
+fi
+
test_lazy_prereq PIPE '
# test whether the filesystem supports FIFOs
test_have_prereq !MINGW,!CYGWIN &&
@@ -1103,6 +1110,20 @@ test_lazy_prereq CASE_INSENSITIVE_FS '
test "$(cat CamelCase)" != good
'
+test_lazy_prereq FUNNYNAMES '
+ test_have_prereq !MINGW &&
+ touch -- \
+ "FUNNYNAMES tab embedded" \
+ "FUNNYNAMES \"quote embedded\"" \
+ "FUNNYNAMES newline
+embedded" 2>/dev/null &&
+ rm -- \
+ "FUNNYNAMES tab embedded" \
+ "FUNNYNAMES \"quote embedded\"" \
+ "FUNNYNAMES newline
+embedded" 2>/dev/null
+'
+
test_lazy_prereq UTF8_NFD_TO_NFC '
# check whether FS converts nfd unicode to nfc
auml=$(printf "\303\244")