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/README4
-rw-r--r--t/annotate-tests.sh4
-rw-r--r--t/chainlint.sed370
-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.expect7
-rw-r--r--t/chainlint/here-doc.test30
-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.expect10
-rw-r--r--t/chainlint/subshell-here-doc.test35
-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.pl31
-rw-r--r--t/helper/test-drop-caches.c4
-rw-r--r--t/helper/test-json-writer.c565
-rw-r--r--t/helper/test-pkt-line.c33
-rw-r--r--t/helper/test-repository.c82
-rw-r--r--t/helper/test-tool.c2
-rw-r--r--t/helper/test-tool.h2
-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.sh22
-rw-r--r--t/lib-httpd/apache.conf8
-rw-r--r--t/lib-httpd/apply-one-time-sed.sh22
-rwxr-xr-xt/lib-submodule-update.sh13
-rwxr-xr-xt/t0000-basic.sh2
-rwxr-xr-xt/t0001-init.sh5
-rwxr-xr-xt/t0003-attributes.sh24
-rwxr-xr-xt/t0008-ignores.sh9
-rwxr-xr-xt/t0019-json-writer.sh331
-rw-r--r--t/t0019/parse_json.perl52
-rwxr-xr-xt/t0020-crlf.sh10
-rwxr-xr-xt/t0021-conversion.sh6
-rwxr-xr-xt/t0030-stripspace.sh17
-rwxr-xr-xt/t0060-path-utils.sh13
-rwxr-xr-xt/t0070-fundamental.sh2
-rwxr-xr-xt/t0090-cache-tree.sh2
-rwxr-xr-xt/t0300-credentials.sh3
-rwxr-xr-xt/t0410-partial-clone.sh93
-rwxr-xr-xt/t1004-read-tree-m-u-wf.sh14
-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/t1300-config.sh89
-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.sh4
-rwxr-xr-xt/t1410-reflog.sh4
-rwxr-xr-xt/t1411-reflog-show.sh6
-rwxr-xr-xt/t1450-fsck.sh2
-rwxr-xr-xt/t1507-rev-parse-upstream.sh9
-rwxr-xr-xt/t1512-rev-parse-disambiguation.sh6
-rwxr-xr-xt/t1700-split-index.sh2
-rwxr-xr-xt/t2016-checkout-patch.sh24
-rwxr-xr-xt/t2024-checkout-dwim.sh63
-rwxr-xr-xt/t2025-worktree-add.sh25
-rwxr-xr-xt/t2103-update-index-ignore-missing.sh2
-rwxr-xr-xt/t2202-add-addremove.sh17
-rwxr-xr-xt/t2203-add-intent.sh64
-rwxr-xr-xt/t3000-ls-files-others.sh2
-rwxr-xr-xt/t3001-ls-files-others-exclude.sh15
-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.sh59
-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.sh80
-rwxr-xr-xt/t3201-branch-contains.sh15
-rwxr-xr-xt/t3206-range-diff.sh145
-rw-r--r--t/t3206/history.export604
-rwxr-xr-xt/t3210-pack-refs.sh6
-rwxr-xr-xt/t3301-notes.sh8
-rwxr-xr-xt/t3310-notes-merge-manual-resolve.sh6
-rwxr-xr-xt/t3400-rebase.sh8
-rwxr-xr-xt/t3401-rebase-and-am-rename.sh105
-rwxr-xr-xt/t3402-rebase-merge.sh4
-rwxr-xr-xt/t3404-rebase-interactive.sh69
-rwxr-xr-xt/t3405-rebase-malformed.sh11
-rwxr-xr-xt/t3418-rebase-continue.sh40
-rwxr-xr-xt/t3422-rebase-incompatible-options.sh88
-rwxr-xr-xt/t3423-rebase-reword.sh48
-rwxr-xr-xt/t3430-rebase-merges.sh81
-rwxr-xr-xt/t3507-cherry-pick-conflict.sh13
-rwxr-xr-xt/t3510-cherry-pick-sequence.sh14
-rwxr-xr-xt/t3600-rm.sh8
-rwxr-xr-xt/t3700-add.sh11
-rwxr-xr-xt/t3701-add-interactive.sh59
-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.sh10
-rwxr-xr-xt/t4012-diff-binary.sh6
-rwxr-xr-xt/t4014-format-patch.sh6
-rwxr-xr-xt/t4015-diff-whitespace.sh260
-rw-r--r--t/t4018/php-abstract-class4
-rw-r--r--t/t4018/php-class4
-rw-r--r--t/t4018/php-final-class4
-rw-r--r--t/t4018/php-function4
-rw-r--r--t/t4018/php-interface4
-rw-r--r--t/t4018/php-method7
-rw-r--r--t/t4018/php-trait7
-rwxr-xr-xt/t4024-diff-optimize-common.sh16
-rwxr-xr-xt/t4025-hunk-header.sh8
-rwxr-xr-xt/t4039-diff-assume-unchanged.sh3
-rwxr-xr-xt/t4041-diff-submodule-option.sh4
-rwxr-xr-xt/t4060-diff-submodule-option-diff-format.sh2
-rwxr-xr-xt/t4121-apply-diffs.sh2
-rwxr-xr-xt/t4135-apply-weird-filenames.sh10
-rwxr-xr-xt/t4150-am.sh39
-rwxr-xr-xt/t4200-rerere.sh9
-rwxr-xr-xt/t4202-log.sh43
-rwxr-xr-xt/t4208-log-magic-pathspec.sh26
-rwxr-xr-xt/t4210-log-i18n.sh6
-rwxr-xr-xt/t4211-line-log.sh5
-rwxr-xr-xt/t4254-am-corrupt.sh2
-rwxr-xr-xt/t5300-pack-object.sh2
-rwxr-xr-xt/t5302-pack-index.sh2
-rwxr-xr-xt/t5310-pack-bitmaps.sh16
-rwxr-xr-xt/t5313-pack-bounds-checks.sh3
-rwxr-xr-xt/t5317-pack-objects-filter-objects.sh16
-rwxr-xr-xt/t5318-commit-graph.sh251
-rwxr-xr-xt/t5400-send-pack.sh4
-rwxr-xr-xt/t5401-update-hooks.sh4
-rwxr-xr-xt/t5405-send-pack-rewind.sh3
-rwxr-xr-xt/t5406-remote-rejects.sh5
-rwxr-xr-xt/t5407-post-rewrite-hook.sh2
-rwxr-xr-xt/t5409-colorize-remote-messages.sh101
-rwxr-xr-xt/t5500-fetch-pack.sh96
-rwxr-xr-xt/t5504-fetch-receive-strict.sh126
-rwxr-xr-xt/t5505-remote.sh18
-rwxr-xr-xt/t5510-fetch.sh117
-rwxr-xr-xt/t5512-ls-remote.sh10
-rwxr-xr-xt/t5514-fetch-multiple.sh3
-rwxr-xr-xt/t5516-fetch-push.sh75
-rwxr-xr-xt/t5517-push-mirror.sh10
-rwxr-xr-xt/t5520-pull.sh24
-rwxr-xr-xt/t5526-fetch-submodules.sh8
-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/t5537-fetch-shallow.sh45
-rwxr-xr-xt/t5541-http-push-smart.sh28
-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.sh156
-rwxr-xr-xt/t5562/invoke-with-content-length.pl37
-rwxr-xr-xt/t5570-git-daemon.sh6
-rwxr-xr-xt/t5573-pull-verify-signatures.sh2
-rwxr-xr-xt/t5601-clone.sh2
-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.sh108
-rwxr-xr-xt/t5702-protocol-v2.sh78
-rwxr-xr-xt/t5703-upload-pack-ref-in-want.sh377
-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/t6018-rev-list-glob.sh12
-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/t6029-merge-subtree.sh44
-rwxr-xr-xt/t6036-recursive-corner-cases.sh1606
-rwxr-xr-xt/t6042-merge-rename-corner-cases.sh1212
-rwxr-xr-xt/t6043-merge-rename-directories.sh2
-rwxr-xr-xt/t6044-merge-unrelated-index-changes.sh67
-rwxr-xr-xt/t6046-merge-skip-unneeded-updates.sh4
-rwxr-xr-xt/t6050-replace.sh6
-rwxr-xr-xt/t6060-merge-index.sh3
-rwxr-xr-xt/t6300-for-each-ref.sh10
-rwxr-xr-xt/t7001-mv.sh2
-rwxr-xr-xt/t7003-filter-branch.sh15
-rwxr-xr-xt/t7004-tag.sh35
-rwxr-xr-xt/t7006-pager.sh3
-rwxr-xr-xt/t7030-verify-tag.sh39
-rwxr-xr-xt/t7063-status-untracked-cache.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.sh41
-rwxr-xr-xt/t7301-clean-interactive.sh41
-rwxr-xr-xt/t7400-submodule-basic.sh20
-rwxr-xr-xt/t7401-submodule-summary.sh3
-rwxr-xr-xt/t7405-submodule-merge.sh173
-rwxr-xr-xt/t7406-submodule-update.sh54
-rwxr-xr-xt/t7407-submodule-foreach.sh38
-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.sh56
-rwxr-xr-xt/t7502-commit.sh3
-rwxr-xr-xt/t7504-commit-msg-hook.sh4
-rwxr-xr-xt/t7506-status-submodule.sh10
-rwxr-xr-xt/t7508-status.sh4
-rwxr-xr-xt/t7510-signed-commit.sh14
-rwxr-xr-xt/t7610-mergetool.sh11
-rwxr-xr-xt/t7611-merge-abort.sh118
-rwxr-xr-xt/t7612-merge-verify-signatures.sh2
-rwxr-xr-xt/t7810-grep.sh120
-rwxr-xr-xt/t8003-blame-corner-cases.sh12
-rwxr-xr-xt/t9001-send-email.sh70
-rwxr-xr-xt/t9100-git-svn-basic.sh2
-rwxr-xr-xt/t9101-git-svn-props.sh2
-rwxr-xr-xt/t9104-git-svn-follow-parent.sh4
-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/t9134-git-svn-ignore-paths.sh6
-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.sh6
-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/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/t9833-errors.sh4
-rwxr-xr-xt/t9902-completion.sh51
-rwxr-xr-xt/t9903-bash-prompt.sh15
-rw-r--r--t/test-lib-functions.sh8
-rw-r--r--t/test-lib.sh17
327 files changed, 10159 insertions, 2155 deletions
diff --git a/t/.gitattributes b/t/.gitattributes
index 3bd959a..e7aceda 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 4e731dc..348715f 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 96317a3..c83fd18 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 8373a27..9028b47 100644
--- a/t/README
+++ b/t/README
@@ -315,6 +315,10 @@ 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.
+
Naming Tests
------------
diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh
index 093832f..6da48a2 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 0000000..8544df3
--- /dev/null
+++ b/t/chainlint.sed
@@ -0,0 +1,370 @@
+#------------------------------------------------------------------------------
+# 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/[ ]*<<//
+ :hereslurp
+ N
+ /^<\([^>]*\)>.*\n[ ]*\1[ ]*$/!{
+ s/\n.*$//
+ bhereslurp
+ }
+ 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 "...\"
+/\\$/bincomplete
+# 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/bcheckchain
+# 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 "(...) &&"
+/^[ ]*(.*)[ ]*&&[ ]*$/bcheckchain
+# nested one-liner "(...)"
+/^[ ]*(.*)[ ]*$/bcheckchain
+# nested one-liner "(...) >x" (or "2>x" or "<x" or "|x")
+/^[ ]*(.*)[ ]*[0-9]*[<>|]/bcheckchain
+# nested multi-line "(...\n...)"
+/^[ ]*(/bnest
+# multi-line "{...\n...}"
+/^[ ]*{/bblock
+# closing ")" on own line -- exit subshell
+/^[ ]*)/bclosesolo
+# "$((...))" -- arithmetic expansion; not closing ")"
+/\$(([^)][^)]*))[^)]*$/bcheckchain
+# "$(...)" -- command substitution; not closing ")"
+/\$([^)][^)]*)[^)]*$/bcheckchain
+# multi-line "$(...\n...)" -- command substitution; treat as nested subshell
+/\$([^)]*$/bnest
+# "=(...)" -- Bash array assignment; not closing ")"
+/=(/bcheckchain
+# closing "...) &&"
+/)[ ]*&&[ ]*$/bclose
+# closing "...)"
+/)[ ]*$/bclose
+# closing "...) >x" (or "2>x" or "<x" or "|x")
+/)[ ]*[<>|]/bclose
+:checkchain
+# 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
+:incomplete
+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/[ ]*<<//
+:hereslurpsub
+N
+/^<\([^>]*\)>.*\n[ ]*\1[ ]*$/!{
+ s/\n.*$//
+ bhereslurpsub
+}
+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
+bcheckchain
+
+# found nested multi-line "(...\n...)" -- pass through untouched
+:nest
+x
+:nestslurp
+n
+# closing ")" on own line -- stop nested slurp
+/^[ ]*)/bnestclose
+# comment -- not closing ")" if in comment
+/^[ ]*#/bnestcontinue
+# "$((...))" -- arithmetic expansion; not closing ")"
+/\$(([^)][^)]*))[^)]*$/bnestcontinue
+# "$(...)" -- command substitution; not closing ")"
+/\$([^)][^)]*)[^)]*$/bnestcontinue
+# closing "...)" -- stop nested slurp
+/)/bnestclose
+:nestcontinue
+x
+bnestslurp
+:nestclose
+s/^/>>/
+# is it "))" which closes nested and parent subshells?
+/)[ ]*)/bslurp
+bcheckchain
+
+# found multi-line "{...\n...}" block -- pass through untouched
+:block
+x
+n
+# closing "}" -- stop block slurp
+/}/bcheckchain
+bblock
+
+# found closing ")" on own line -- drop "suspect" from final line of subshell
+# since that line legitimately lacks "&&" and exit subshell loop
+:closesolo
+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 0000000..09457d3
--- /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 0000000..1620696
--- /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 0000000..c4a830d
--- /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 0000000..92bbb77
--- /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 0000000..3be939e
--- /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 0000000..f6dd143
--- /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 0000000..fed7e89
--- /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 0000000..d859151
--- /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 0000000..55b0f42
--- /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 0000000..3cc67b6
--- /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 0000000..41f121f
--- /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 0000000..5ef6ff7
--- /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 0000000..2a910f9
--- /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 0000000..72d482f
--- /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 0000000..1846887
--- /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 0000000..508ca44
--- /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 0000000..ad4118e
--- /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 0000000..3bbb002
--- /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 0000000..3be939e
--- /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 0000000..113c0c4
--- /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 0000000..9674b88
--- /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 0000000..571bbd8
--- /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 0000000..ab2a026
--- /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 0000000..eed774a
--- /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 0000000..8c0260d
--- /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 0000000..a841d78
--- /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 0000000..b506d46
--- /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 0000000..0499fa4
--- /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 0000000..84d8bde
--- /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 0000000..2f03820
--- /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 0000000..bf78454
--- /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 0000000..4e6ab69
--- /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 0000000..c33cf56
--- /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 0000000..7db7626
--- /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 0000000..f011e33
--- /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 0000000..b857ff5
--- /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 0000000..e5fb752
--- /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 0000000..899bc5d
--- /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 0000000..32038a0
--- /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 0000000..a53edbc
--- /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 0000000..aff6568
--- /dev/null
+++ b/t/chainlint/here-doc.expect
@@ -0,0 +1,7 @@
+boodle wobba gorgo snoot wafta snurb &&
+
+cat >foo &&
+
+cat >bar &&
+
+horticulture
diff --git a/t/chainlint/here-doc.test b/t/chainlint/here-doc.test
new file mode 100644
index 0000000..f2bb14b
--- /dev/null
+++ b/t/chainlint/here-doc.test
@@ -0,0 +1,30 @@
+# 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 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 0000000..03d3ceb
--- /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 0000000..daf22da
--- /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 0000000..5953c7b
--- /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 0000000..9bd8e9a
--- /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 0000000..2f3ebab
--- /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 0000000..d856658
--- /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 0000000..fc9f250
--- /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 0000000..8f26856
--- /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 0000000..088e622
--- /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 0000000..93e8ba8
--- /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 0000000..59b6c8b
--- /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 0000000..3000583
--- /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 0000000..170cb59
--- /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 0000000..287ab89
--- /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 0000000..cf18429
--- /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 0000000..c9598e9
--- /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 0000000..c2a59ff
--- /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 0000000..8fd656c
--- /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 0000000..0c9ef1c
--- /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 0000000..f35404b
--- /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 0000000..15b68d4
--- /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 0000000..0ff136a
--- /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 0000000..c8165ad
--- /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 0000000..998b05a
--- /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 0000000..237f227
--- /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 0000000..ec9acb9
--- /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 0000000..98b3d88
--- /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 0000000..4fd2d6e
--- /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 0000000..211b901
--- /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 0000000..e6af4de
--- /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 0000000..1d79384
--- /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 0000000..d82c8eb
--- /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 0000000..7663ea7
--- /dev/null
+++ b/t/chainlint/subshell-here-doc.expect
@@ -0,0 +1,10 @@
+(
+ echo wobba gorgo snoot wafta snurb &&
+?!AMP?! cat >bip
+ echo >bop
+>) &&
+(
+ cat >bup &&
+ cat >bup2 &&
+ meep
+>)
diff --git a/t/chainlint/subshell-here-doc.test b/t/chainlint/subshell-here-doc.test
new file mode 100644
index 0000000..b6b5a9b
--- /dev/null
+++ b/t/chainlint/subshell-here-doc.test
@@ -0,0 +1,35 @@
+(
+# 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
+ meep
+)
diff --git a/t/chainlint/subshell-one-liner.expect b/t/chainlint/subshell-one-liner.expect
new file mode 100644
index 0000000..5116282
--- /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 0000000..37fa643
--- /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 0000000..c991342
--- /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 0000000..277d835
--- /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 0000000..13cff2c
--- /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 0000000..f1df085
--- /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 e07f028..d5823f7 100755
--- a/t/check-non-portable-shell.pl
+++ b/t/check-non-portable-shell.pl
@@ -7,22 +7,43 @@ use strict;
use warnings;
my $exit_code=0;
+my %func;
sub err {
my $msg = shift;
+ s/^\s+//;
+ s/\s+$//;
+ s/\s+/ /g;
print "$ARGV:$.: error: $msg: $_\n";
$exit_code = 1;
}
+# glean names of shell functions
+for my $i (@ARGV) {
+ open(my $f, '<', $i) or die "$0: $i: $!\n";
+ while (<$f>) {
+ $func{$1} = 1 if /^\s*(\w+)\s*\(\)\s*{\s*$/;
+ }
+ close $f;
+}
+
while (<>) {
chomp;
+ # stitch together incomplete lines (those ending with "\")
+ while (s/\\$//) {
+ $_ .= readline;
+ chomp;
+ }
+
/\bsed\s+-i/ and err 'sed -i is not portable';
- /\becho\s+-[neE]/ and err 'echo with option is not portable (please use printf)';
+ /\becho\s+-[neE]/ and err 'echo with option is not portable (use printf)';
/^\s*declare\s+/ and err 'arrays/declare not portable';
- /^\s*[^#]\s*which\s/ and err 'which is not portable (please use type)';
- /\btest\s+[^=]*==/ and err '"test a == b" is not portable (please use =)';
- /\bwc -l.*"\s*=/ and err '`"$(wc -l)"` is not portable (please use test_line_count)';
- /\bexport\s+[A-Za-z0-9_]*=/ and err '"export FOO=bar" is not portable (please use FOO=bar && export FOO)';
+ /^\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)';
+ /\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"';
# this resets our $. for each file
close ARGV if eof;
}
diff --git a/t/helper/test-drop-caches.c b/t/helper/test-drop-caches.c
index d6bcfdd..f65e301 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-json-writer.c b/t/helper/test-json-writer.c
new file mode 100644
index 0000000..37c4525
--- /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-pkt-line.c b/t/helper/test-pkt-line.c
index 0f19e53..30775f9 100644
--- a/t/helper/test-pkt-line.c
+++ b/t/helper/test-pkt-line.c
@@ -1,3 +1,4 @@
+#include "cache.h"
#include "pkt-line.h"
static void pack_line(const char *line)
@@ -48,6 +49,36 @@ static void unpack(void)
}
}
+static void unpack_sideband(void)
+{
+ struct packet_reader reader;
+ packet_reader_init(&reader, 0, NULL, 0,
+ PACKET_READ_GENTLE_ON_EOF |
+ PACKET_READ_CHOMP_NEWLINE);
+
+ while (packet_reader_read(&reader) != PACKET_READ_EOF) {
+ int band;
+ int fd;
+
+ switch (reader.status) {
+ case PACKET_READ_EOF:
+ break;
+ case PACKET_READ_NORMAL:
+ band = reader.line[0] & 0xff;
+ if (band < 1 || band > 2)
+ die("unexpected side band %d", band);
+ fd = band;
+
+ write_or_die(fd, reader.line + 1, reader.pktlen - 1);
+ break;
+ case PACKET_READ_FLUSH:
+ return;
+ case PACKET_READ_DELIM:
+ break;
+ }
+ }
+}
+
int cmd_main(int argc, const char **argv)
{
if (argc < 2)
@@ -57,6 +88,8 @@ int cmd_main(int argc, const char **argv)
pack(argc - 2, argv + 2);
else if (!strcmp(argv[1], "unpack"))
unpack();
+ else if (!strcmp(argv[1], "unpack-sideband"))
+ unpack_sideband();
else
die("invalid argument '%s'", argv[1]);
diff --git a/t/helper/test-repository.c b/t/helper/test-repository.c
new file mode 100644
index 0000000..2762ca6
--- /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 805a45d..0edafcf 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 },
@@ -29,6 +30,7 @@ static struct test_cmd cmds[] = {
{ "read-cache", cmd__read_cache },
{ "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 7116ddf..e926c41 100644
--- a/t/helper/test-tool.h
+++ b/t/helper/test-tool.h
@@ -13,6 +13,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);
@@ -23,6 +24,7 @@ int cmd__prio_queue(int argc, const char **argv);
int cmd__read_cache(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 a5d3b2c..3fe0287 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 0000000..a7fd87c
--- /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 0000000..94ffad0
--- /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 435a374..a8729f8 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -132,6 +132,7 @@ prepare_httpd() {
cp "$TEST_PATH"/passwd "$HTTPD_ROOT_PATH"
install_script broken-smart-http.sh
install_script error.sh
+ install_script apply-one-time-sed.sh
ln -s "$LIB_HTTPD_MODULE_PATH" "$HTTPD_ROOT_PATH/modules"
@@ -287,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-httpd/apache.conf b/t/lib-httpd/apache.conf
index 724d9ae..581c010 100644
--- a/t/lib-httpd/apache.conf
+++ b/t/lib-httpd/apache.conf
@@ -111,9 +111,14 @@ Alias /auth/dumb/ www/auth/dumb/
SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
SetEnv GIT_HTTP_EXPORT_ALL
</LocationMatch>
+<LocationMatch /one_time_sed/>
+ SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+ SetEnv GIT_HTTP_EXPORT_ALL
+</LocationMatch>
ScriptAliasMatch /smart_*[^/]*/(.*) ${GIT_EXEC_PATH}/git-http-backend/$1
ScriptAlias /broken_smart/ broken-smart-http.sh/
ScriptAlias /error/ error.sh/
+ScriptAliasMatch /one_time_sed/(.*) apply-one-time-sed.sh/$1
<Directory ${GIT_EXEC_PATH}>
Options FollowSymlinks
</Directory>
@@ -123,6 +128,9 @@ ScriptAlias /error/ error.sh/
<Files error.sh>
Options ExecCGI
</Files>
+<Files apply-one-time-sed.sh>
+ Options ExecCGI
+</Files>
<Files ${GIT_EXEC_PATH}/git-http-backend>
Options ExecCGI
</Files>
diff --git a/t/lib-httpd/apply-one-time-sed.sh b/t/lib-httpd/apply-one-time-sed.sh
new file mode 100644
index 0000000..fcef728
--- /dev/null
+++ b/t/lib-httpd/apply-one-time-sed.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+# If "one-time-sed" exists in $HTTPD_ROOT_PATH, run sed on the HTTP response,
+# using the contents of "one-time-sed" as the sed command to be run. If the
+# response was modified as a result, delete "one-time-sed" so that subsequent
+# HTTP responses are no longer modified.
+#
+# This can be used to simulate the effects of the repository changing in
+# between HTTP request-response pairs.
+if [ -e one-time-sed ]; then
+ "$GIT_EXEC_PATH/git-http-backend" >out
+ sed "$(cat one-time-sed)" <out >out_modified
+
+ if diff out out_modified >/dev/null; then
+ cat out
+ else
+ cat out_modified
+ rm one-time-sed
+ fi
+else
+ "$GIT_EXEC_PATH/git-http-backend"
+fi
diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh
index 1f38a85..5b56b23 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
- GIT_WORK_TREE=. git -C submodule_update/.git/modules/sub1/modules/sub2 config --unset core.worktree
+ # core.worktree is unset for sub2 as it is not checked out
fi &&
# indicate we are interested in the submodule:
git -C submodule_update config submodule.sub1.url "bogus" &&
@@ -709,7 +709,8 @@ test_submodule_recursing_with_args_common() {
git branch -t remove_sub1 origin/remove_sub1 &&
$command remove_sub1 &&
test_superproject_content origin/remove_sub1 &&
- ! test -e sub1
+ ! test -e sub1 &&
+ test_must_fail git config -f .git/modules/sub1/config core.worktree
)
'
# ... absorbing a .git directory along the way.
@@ -755,7 +756,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
)
'
@@ -781,7 +782,8 @@ test_submodule_recursing_with_args_common() {
(
cd submodule_update &&
git branch -t invalid_sub1 origin/invalid_sub1 &&
- test_must_fail $command invalid_sub1 &&
+ test_must_fail $command invalid_sub1 2>err &&
+ test_i18ngrep sub1 err &&
test_superproject_content origin/add_sub1 &&
test_submodule_content sub1 origin/add_sub1
)
@@ -842,7 +844,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
@@ -969,7 +971,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/t0000-basic.sh b/t/t0000-basic.sh
index af61d08..34859fe 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -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 c413bff..ca85aae 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -287,6 +287,7 @@ test_expect_success 'init notices EEXIST (2)' '
'
test_expect_success POSIXPERM,SANITY 'init notices EPERM' '
+ test_when_finished "chmod +w newdir" &&
rm -fr newdir &&
mkdir newdir &&
chmod -w newdir &&
@@ -407,7 +408,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 &&
@@ -419,7 +420,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 f19ae4f..5c37c2e 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 &&
(
@@ -287,7 +287,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 +312,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 c03f155..1744cee 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 0000000..3b0c336
--- /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 0000000..ca4e5bf
--- /dev/null
+++ b/t/t0019/parse_json.perl
@@ -0,0 +1,52 @@
+#!/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 (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 71350e0..5f05698 100755
--- a/t/t0020-crlf.sh
+++ b/t/t0020-crlf.sh
@@ -98,6 +98,16 @@ test_expect_success 'safecrlf: git diff demotes safecrlf=true to warn' '
'
+test_expect_success 'safecrlf: no warning with safecrlf=false' '
+ git config core.autocrlf input &&
+ git config core.safecrlf false &&
+
+ for w in I am all CRLF; do echo $w; done | append_cr >allcrlf &&
+ git add allcrlf 2>err &&
+ test_must_be_empty err
+'
+
+
test_expect_success 'switch off autocrlf, safecrlf, reset HEAD' '
git config core.autocrlf false &&
git config core.safecrlf false &&
diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
index 9479a4a..308cd28 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/t0030-stripspace.sh b/t/t0030-stripspace.sh
index bbf3e39..b77948c 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 \
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 21a8b53..cd74c0a 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/t0070-fundamental.sh b/t/t0070-fundamental.sh
index 23fbe64..7b111a5 100755
--- a/t/t0070-fundamental.sh
+++ b/t/t0070-fundamental.sh
@@ -19,8 +19,8 @@ test_expect_success 'mktemp to nonexistent directory prints filename' '
test_expect_success POSIXPERM,SANITY 'mktemp to unwritable directory prints filename' '
mkdir cannotwrite &&
- chmod -w cannotwrite &&
test_when_finished "chmod +w cannotwrite" &&
+ chmod -w cannotwrite &&
test_must_fail test-tool mktemp cannotwrite/testXXXXXX 2>err &&
grep "cannotwrite/test" err
'
diff --git a/t/t0090-cache-tree.sh b/t/t0090-cache-tree.sh
index 0c61268..28ea93f 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
'
diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh
index 03bd31e..82eaaea 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 cc18b75..1281300 100755
--- a/t/t0410-partial-clone.sh
+++ b/t/t0410-partial-clone.sh
@@ -23,7 +23,15 @@ promise_and_delete () {
delete_object repo "$HASH"
}
+test_expect_success 'extensions.partialclone without filter' '
+ test_create_repo server &&
+ git clone --filter="blob:none" "file://$(pwd)/server" client &&
+ git -C client config --unset core.partialclonefilter &&
+ git -C client fetch origin
+'
+
test_expect_success 'missing reflog object, but promised by a commit, passes fsck' '
+ rm -rf repo &&
test_create_repo repo &&
test_commit -C repo my_commit &&
@@ -263,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 c7ce5d8..c13578a 100755
--- a/t/t1004-read-tree-m-u-wf.sh
+++ b/t/t1004-read-tree-m-u-wf.sh
@@ -179,6 +179,8 @@ test_expect_success 'funny symlink in work tree' '
test_expect_success SANITY 'funny symlink in work tree, un-unlink-able' '
+ test_when_finished "chmod u+w a 2>/dev/null; rm -fr a b" &&
+
rm -fr a b &&
git reset --hard &&
@@ -188,10 +190,6 @@ test_expect_success SANITY 'funny symlink in work tree, un-unlink-able' '
'
-# clean-up from the above test
-chmod a+w a 2>/dev/null
-rm -fr a b
-
test_expect_success 'D/F setup' '
git reset --hard &&
@@ -212,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 0745685..83b09e1 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 13dd510..7f19d59 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 4c50ed9..cf96016 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 0c6f48f..ba71b15 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 0000000..55d22da
--- /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 df3183e..c2df75e 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 f9eb143..1a9b21b 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/t1300-config.sh b/t/t1300-config.sh
index 03c2237..4976e2f 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -888,7 +888,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 +1218,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 f035ee4..6359185 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 8b14ab1..21e139a 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 3e00d1a..d0a2727 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 e1fd0f0..7c8df20 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 30354fd..5d955c3 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 3a887b5..2a42a58 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
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
index 553e26d..8293131 100755
--- a/t/t1410-reflog.sh
+++ b/t/t1410-reflog.sh
@@ -339,8 +339,8 @@ test_expect_failure 'reflog with non-commit entries displays all entries' '
'
test_expect_success 'reflog expire operates on symref not referrent' '
- git branch -l the_symref &&
- git branch -l referrent &&
+ git branch --create-reflog the_symref &&
+ git branch --create-reflog referrent &&
git update-ref referrent HEAD &&
git symbolic-ref refs/heads/the_symref refs/heads/referrent &&
test_when_finished "rm -f .git/refs/heads/referrent.lock" &&
diff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh
index 5969077..4d62cee 100755
--- a/t/t1411-reflog-show.sh
+++ b/t/t1411-reflog-show.sh
@@ -159,9 +159,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 91fd714..7b7602d 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -372,7 +372,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/t1507-rev-parse-upstream.sh b/t/t1507-rev-parse-upstream.sh
index 93c77ea..fa3e499 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/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh
index 96fe375..e4d5b56 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/t1700-split-index.sh b/t/t1700-split-index.sh
index 1e81b33..39133bc 100755
--- a/t/t1700-split-index.sh
+++ b/t/t1700-split-index.sh
@@ -435,7 +435,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/t2016-checkout-patch.sh b/t/t2016-checkout-patch.sh
index 9cd0ac4..47aeb0b 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 3e5ac81..69b6774 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 d2e49f7..07d2923 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 &&
diff --git a/t/t2103-update-index-ignore-missing.sh b/t/t2103-update-index-ignore-missing.sh
index 332694e..0114f05 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/t2202-add-addremove.sh b/t/t2202-add-addremove.sh
index 6a5a316..9ee6590 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 04d840a..e7a400b 100755
--- a/t/t2203-add-intent.sh
+++ b/t/t2203-add-intent.sh
@@ -70,8 +70,7 @@ test_expect_success 'i-t-a entry is simply ignored' '
git commit -m second &&
test $(git ls-tree HEAD -- nitfol | wc -l) = 0 &&
test $(git diff --name-only HEAD -- nitfol | wc -l) = 1 &&
- test $(git diff --name-only --ita-invisible-in-index HEAD -- nitfol | wc -l) = 0 &&
- test $(git diff --name-only --ita-invisible-in-index -- nitfol | wc -l) = 1
+ test $(git diff --name-only -- nitfol | wc -l) = 1
'
test_expect_success 'can commit with an unrelated i-t-a entry in index' '
@@ -99,13 +98,13 @@ test_expect_success 'cache-tree invalidates i-t-a paths' '
: >dir/bar &&
git add -N dir/bar &&
- git diff --cached --name-only >actual &&
+ git diff --name-only >actual &&
echo dir/bar >expect &&
test_cmp expect actual &&
git write-tree >/dev/null &&
- git diff --cached --name-only >actual &&
+ git diff --name-only >actual &&
echo dir/bar >expect &&
test_cmp expect actual
'
@@ -186,7 +185,19 @@ test_expect_success 'rename detection finds the right names' '
cat >expected.3 <<-EOF &&
2 .R N... 100644 100644 100644 $hash $hash R100 third first
EOF
- test_cmp expected.3 actual.3
+ test_cmp expected.3 actual.3 &&
+
+ git diff --stat >actual.4 &&
+ cat >expected.4 <<-EOF &&
+ first => third | 0
+ 1 file changed, 0 insertions(+), 0 deletions(-)
+ EOF
+ test_cmp expected.4 actual.4 &&
+
+ git diff --cached --stat >actual.5 &&
+ : >expected.5 &&
+ test_cmp expected.5 actual.5
+
)
'
@@ -222,5 +233,46 @@ test_expect_success 'double rename detection in status' '
)
'
-test_done
+test_expect_success 'diff-files/diff-cached shows ita as new/not-new files' '
+ git reset --hard &&
+ echo new >new-ita &&
+ git add -N new-ita &&
+ git diff --summary >actual &&
+ echo " create mode 100644 new-ita" >expected &&
+ test_cmp expected actual &&
+ git diff --cached --summary >actual2 &&
+ : >expected2 &&
+ test_cmp expected2 actual2
+'
+
+test_expect_success '"diff HEAD" includes ita as new files' '
+ git reset --hard &&
+ echo new >new-ita &&
+ git add -N new-ita &&
+ git diff HEAD >actual &&
+ cat >expected <<-\EOF &&
+ diff --git a/new-ita b/new-ita
+ new file mode 100644
+ index 0000000..3e75765
+ --- /dev/null
+ +++ b/new-ita
+ @@ -0,0 +1 @@
+ +new
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'apply --intent-to-add' '
+ git reset --hard &&
+ echo new >new-ita &&
+ git add -N new-ita &&
+ git diff >expected &&
+ grep "new file" expected &&
+ git reset --hard &&
+ git apply --intent-to-add expected &&
+ git diff >actual &&
+ test_cmp expected actual
+'
+
+test_done
diff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh
index c525656..afd4756 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 3fc484e..3b47647 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,8 +273,7 @@ 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' '
diff --git a/t/t3005-ls-files-relative.sh b/t/t3005-ls-files-relative.sh
index 3778694..209b4c7 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 202ad65..e109c3f 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 08af596..64f0473 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 3563e77..ff641b3 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 e59b0a3..3824756 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
new file mode 100755
index 0000000..0c0b433
--- /dev/null
+++ b/t/t3035-merge-sparse.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+
+test_description='merge with sparse files'
+
+. ./test-lib.sh
+
+# test_file $filename $content
+test_file () {
+ echo "$2" > "$1" &&
+ git add "$1"
+}
+
+# test_commit_this $message_and_tag
+test_commit_this () {
+ git commit -m "$1" &&
+ git tag "$1"
+}
+
+test_expect_success 'setup' '
+ : >empty &&
+ test_file checked-out init &&
+ test_file modify_delete modify_delete_init &&
+ test_commit_this init &&
+ test_file modify_delete modify_delete_theirs &&
+ test_commit_this theirs &&
+ git reset --hard init &&
+ git rm modify_delete &&
+ test_commit_this ours &&
+ git config core.sparseCheckout true &&
+ echo "/checked-out" >.git/info/sparse-checkout &&
+ git reset --hard &&
+ ! git merge theirs
+'
+
+test_expect_success 'reset --hard works after the conflict' '
+ git reset --hard
+'
+
+test_expect_success 'is reset properly' '
+ git status --porcelain -- modify_delete >out &&
+ test_cmp empty out &&
+ test_path_is_missing modify_delete
+'
+
+test_expect_success 'setup: conflict back' '
+ ! git merge theirs
+'
+
+test_expect_success 'Merge abort works after the conflict' '
+ git merge --abort
+'
+
+test_expect_success 'is aborted properly' '
+ git status --porcelain -- modify_delete >out &&
+ test_cmp empty out &&
+ test_path_is_missing modify_delete
+'
+
+test_done
diff --git a/t/t3050-subprojects-fetch.sh b/t/t3050-subprojects-fetch.sh
index 2f5f41a..f1f09ab 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 dce1021..46aca0a 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 e804377..1e16c6b 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 0846798..93f21ab 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -49,9 +49,9 @@ test_expect_success 'git branch HEAD should fail' '
cat >expect <<EOF
$ZERO_OID $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master
EOF
-test_expect_success 'git branch -l d/e/f should create a branch and a log' '
+test_expect_success 'git branch --create-reflog d/e/f should create a branch and a log' '
GIT_COMMITTER_DATE="2005-05-26 23:30" \
- git branch -l d/e/f &&
+ git -c core.logallrefupdates=false branch --create-reflog d/e/f &&
test_path_is_file .git/refs/heads/d/e/f &&
test_path_is_file .git/logs/refs/heads/d/e/f &&
test_cmp expect .git/logs/refs/heads/d/e/f
@@ -82,7 +82,7 @@ test_expect_success 'git branch -m dumps usage' '
test_expect_success 'git branch -m m broken_symref should work' '
test_when_finished "git branch -D broken_symref" &&
- git branch -l m &&
+ git branch --create-reflog m &&
git symbolic-ref refs/heads/broken_symref refs/heads/i_am_broken &&
git branch -m m broken_symref &&
git reflog exists refs/heads/broken_symref &&
@@ -90,13 +90,13 @@ test_expect_success 'git branch -m m broken_symref should work' '
'
test_expect_success 'git branch -m m m/m should work' '
- git branch -l m &&
+ git branch --create-reflog m &&
git branch -m m m/m &&
git reflog exists refs/heads/m/m
'
test_expect_success 'git branch -m n/n n should work' '
- git branch -l n/n &&
+ git branch --create-reflog n/n &&
git branch -m n/n n &&
git reflog exists refs/heads/n
'
@@ -378,9 +378,9 @@ mv .git/config-saved .git/config
git config branch.s/s.dummy Hello
test_expect_success 'git branch -m s/s s should work when s/t is deleted' '
- git branch -l s/s &&
+ git branch --create-reflog s/s &&
git reflog exists refs/heads/s/s &&
- git branch -l s/t &&
+ git branch --create-reflog s/t &&
git reflog exists refs/heads/s/t &&
git branch -d s/t &&
git branch -m s/s s &&
@@ -444,7 +444,7 @@ test_expect_success 'git branch --copy dumps usage' '
'
test_expect_success 'git branch -c d e should work' '
- git branch -l d &&
+ git branch --create-reflog d &&
git reflog exists refs/heads/d &&
git config branch.d.dummy Hello &&
git branch -c d e &&
@@ -459,7 +459,7 @@ test_expect_success 'git branch -c d e should work' '
'
test_expect_success 'git branch --copy is a synonym for -c' '
- git branch -l copy &&
+ git branch --create-reflog copy &&
git reflog exists refs/heads/copy &&
git config branch.copy.dummy Hello &&
git branch --copy copy copy-to &&
@@ -486,7 +486,7 @@ test_expect_success 'git branch -c ee ef should copy ee to create branch ef' '
'
test_expect_success 'git branch -c f/f g/g should work' '
- git branch -l f/f &&
+ git branch --create-reflog f/f &&
git reflog exists refs/heads/f/f &&
git config branch.f/f.dummy Hello &&
git branch -c f/f g/g &&
@@ -497,7 +497,7 @@ test_expect_success 'git branch -c f/f g/g should work' '
'
test_expect_success 'git branch -c m2 m2 should work' '
- git branch -l m2 &&
+ git branch --create-reflog m2 &&
git reflog exists refs/heads/m2 &&
git config branch.m2.dummy Hello &&
git branch -c m2 m2 &&
@@ -506,18 +506,18 @@ test_expect_success 'git branch -c m2 m2 should work' '
'
test_expect_success 'git branch -c zz zz/zz should fail' '
- git branch -l zz &&
+ git branch --create-reflog zz &&
git reflog exists refs/heads/zz &&
test_must_fail git branch -c zz zz/zz
'
test_expect_success 'git branch -c b/b b should fail' '
- git branch -l b/b &&
+ git branch --create-reflog b/b &&
test_must_fail git branch -c b/b b
'
test_expect_success 'git branch -C o/q o/p should work when o/p exists' '
- git branch -l o/q &&
+ git branch --create-reflog o/q &&
git reflog exists refs/heads/o/q &&
git reflog exists refs/heads/o/p &&
git branch -C o/q o/p
@@ -570,10 +570,10 @@ test_expect_success 'git branch -C master5 master5 should work when master is ch
'
test_expect_success 'git branch -C ab cd should overwrite existing config for cd' '
- git branch -l cd &&
+ git branch --create-reflog cd &&
git reflog exists refs/heads/cd &&
git config branch.cd.dummy CD &&
- git branch -l ab &&
+ git branch --create-reflog ab &&
git reflog exists refs/heads/ab &&
git config branch.ab.dummy AB &&
git branch -C ab cd &&
@@ -685,7 +685,7 @@ test_expect_success 'renaming a symref is not allowed' '
'
test_expect_success SYMLINKS 'git branch -m u v should fail when the reflog for u is a symlink' '
- git branch -l u &&
+ git branch --create-reflog u &&
mv .git/logs/refs/heads/u real-u &&
ln -s real-u .git/logs/refs/heads/u &&
test_must_fail git branch -m u v
@@ -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 0ef1b6f..0ea4fc4 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 0000000..2237c7f
--- /dev/null
+++ b/t/t3206-range-diff.sh
@@ -0,0 +1,145 @@
+#!/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!
+ +
+ Zdiff --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_done
diff --git a/t/t3206/history.export b/t/t3206/history.export
new file mode 100644
index 0000000..b8ffff0
--- /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 afa27ff..7333d7d 100755
--- a/t/t3210-pack-refs.sh
+++ b/t/t3210-pack-refs.sh
@@ -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 2d200fd..ac62dc0 100755
--- a/t/t3301-notes.sh
+++ b/t/t3301-notes.sh
@@ -914,7 +914,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 +939,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 +972,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 +1059,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/t3310-notes-merge-manual-resolve.sh b/t/t3310-notes-merge-manual-resolve.sh
index 9c1bf6e..68436ee 100755
--- a/t/t3310-notes-merge-manual-resolve.sh
+++ b/t/t3310-notes-merge-manual-resolve.sh
@@ -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 &&
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 72d9564..3996ee0 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
new file mode 100755
index 0000000..8f83295
--- /dev/null
+++ b/t/t3401-rebase-and-am-rename.sh
@@ -0,0 +1,105 @@
+#!/bin/sh
+
+test_description='git rebase + directory rename tests'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+test_expect_success 'setup testcase' '
+ test_create_repo dir-rename &&
+ (
+ cd dir-rename &&
+
+ mkdir x &&
+ test_seq 1 10 >x/a &&
+ test_seq 11 20 >x/b &&
+ test_seq 21 30 >x/c &&
+ test_write_lines a b c d e f g h i >l &&
+ git add x l &&
+ git commit -m "Initial" &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git checkout A &&
+ git mv x y &&
+ git mv l letters &&
+ git commit -m "Rename x to y, l to letters" &&
+
+ git checkout B &&
+ echo j >>l &&
+ test_seq 31 40 >x/d &&
+ git add l x/d &&
+ git commit -m "Modify l, add x/d"
+ )
+'
+
+test_expect_success 'rebase --interactive: directory rename detected' '
+ (
+ cd dir-rename &&
+
+ git checkout B^0 &&
+
+ set_fake_editor &&
+ FAKE_LINES="1" git rebase --interactive A &&
+
+ git ls-files -s >out &&
+ test_line_count = 5 out &&
+
+ test_path_is_file y/d &&
+ test_path_is_missing x/d
+ )
+'
+
+test_expect_failure 'rebase (am): directory rename detected' '
+ (
+ cd dir-rename &&
+
+ git checkout B^0 &&
+
+ git rebase A &&
+
+ git ls-files -s >out &&
+ test_line_count = 5 out &&
+
+ test_path_is_file y/d &&
+ test_path_is_missing x/d
+ )
+'
+
+test_expect_success 'rebase --merge: directory rename detected' '
+ (
+ cd dir-rename &&
+
+ git checkout B^0 &&
+
+ git rebase --merge A &&
+
+ git ls-files -s >out &&
+ test_line_count = 5 out &&
+
+ test_path_is_file y/d &&
+ test_path_is_missing x/d
+ )
+'
+
+test_expect_failure 'am: directory rename detected' '
+ (
+ cd dir-rename &&
+
+ git checkout A^0 &&
+
+ git format-patch -1 B &&
+
+ git am --3way 0001*.patch &&
+
+ git ls-files -s >out &&
+ test_line_count = 5 out &&
+
+ test_path_is_file y/d &&
+ test_path_is_missing x/d
+ )
+'
+
+test_done
diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh
index 488945e..a1ec501 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 352a52e..4c7b1ea 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 &&
@@ -264,11 +273,18 @@ test_expect_success 'retain authorship' '
'
test_expect_success 'retain authorship w/ conflicts' '
+ oGIT_AUTHOR_NAME=$GIT_AUTHOR_NAME &&
+ test_when_finished "GIT_AUTHOR_NAME=\$oGIT_AUTHOR_NAME" &&
+
git reset --hard twerp &&
test_commit a conflict a conflict-a &&
git reset --hard twerp &&
- GIT_AUTHOR_NAME=AttributeMe \
+
+ GIT_AUTHOR_NAME=AttributeMe &&
+ export GIT_AUTHOR_NAME &&
test_commit b conflict b conflict-b &&
+ GIT_AUTHOR_NAME=$oGIT_AUTHOR_NAME &&
+
set_fake_editor &&
test_must_fail git rebase -i conflict-a &&
echo resolved >conflict &&
@@ -509,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 &&
@@ -523,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 &&
@@ -553,15 +569,16 @@ test_expect_success '--continue tries to commit, even for "edit"' '
'
test_expect_success 'aborted --continue does not squash commits after "edit"' '
+ test_when_finished "git rebase --abort" &&
old=$(git rev-parse HEAD) &&
test_tick &&
set_fake_editor &&
FAKE_LINES="edit 1" git rebase -i HEAD^ &&
echo "edited again" > file7 &&
git add file7 &&
- test_must_fail env FAKE_COMMIT_MESSAGE=" " git rebase --continue &&
- test $old = $(git rev-parse HEAD) &&
- git rebase --abort
+ echo all the things >>conflict &&
+ test_must_fail git rebase --continue &&
+ test $old = $(git rev-parse HEAD)
'
test_expect_success 'auto-amend only edited commits after "edit"' '
@@ -981,7 +998,35 @@ test_expect_success 'rebase -i --root reword root commit' '
test -z "$(git show -s --format=%p HEAD^)"
'
+test_expect_success 'rebase -i --root when root has untracked file confilct' '
+ test_when_finished "reset_rebase" &&
+ git checkout -b failing-root-pick A &&
+ echo x >file2 &&
+ git rm file1 &&
+ git commit -m "remove file 1 add file 2" &&
+ echo z >file1 &&
+ set_fake_editor &&
+ test_must_fail env FAKE_LINES="1 2" git rebase -i --root &&
+ rm file1 &&
+ git rebase --continue &&
+ test "$(git log -1 --format=%B)" = "remove file 1 add file 2" &&
+ test "$(git rev-list --count HEAD)" = 2
+'
+
+test_expect_success 'rebase -i --root reword root when root has untracked file conflict' '
+ test_when_finished "reset_rebase" &&
+ echo z>file1 &&
+ set_fake_editor &&
+ test_must_fail env FAKE_LINES="reword 1 2" \
+ FAKE_COMMIT_MESSAGE="Modified A" git rebase -i --root &&
+ rm file1 &&
+ FAKE_COMMIT_MESSAGE="Reworded A" git rebase --continue &&
+ test "$(git log -1 --format=%B HEAD^)" = "Reworded A" &&
+ test "$(git rev-list --count HEAD)" = 2
+'
+
test_expect_success C_LOCALE_OUTPUT 'rebase --edit-todo does not work on non-interactive rebase' '
+ git checkout reword-root-branch &&
git reset --hard &&
git checkout conflict-branch &&
set_fake_editor &&
@@ -1202,7 +1247,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' '
@@ -1379,4 +1424,12 @@ 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 &&
+ FAKE_LINES="2 1" git rebase -i --root &&
+ git cat-file commit HEAD^ >out &&
+ grep "^author ..*> [0-9][0-9]* [-+][0-9][0-9][0-9][0-9]$" out
+'
+
test_done
diff --git a/t/t3405-rebase-malformed.sh b/t/t3405-rebase-malformed.sh
index cb7c6de..da94ddd 100755
--- a/t/t3405-rebase-malformed.sh
+++ b/t/t3405-rebase-malformed.sh
@@ -77,19 +77,14 @@ test_expect_success 'rebase commit with diff in message' '
'
test_expect_success 'rebase -m commit with empty message' '
- test_must_fail git rebase -m master empty-message-merge &&
- git rebase --abort &&
- git rebase -m --allow-empty-message master empty-message-merge
+ git rebase -m master empty-message-merge
'
test_expect_success 'rebase -i commit with empty message' '
git checkout diff-in-message &&
set_fake_editor &&
- test_must_fail env FAKE_COMMIT_MESSAGE=" " FAKE_LINES="reword 1" \
- git rebase -i HEAD^ &&
- git rebase --abort &&
- FAKE_COMMIT_MESSAGE=" " FAKE_LINES="reword 1" \
- git rebase -i --allow-empty-message HEAD^
+ env FAKE_COMMIT_MESSAGE=" " FAKE_LINES="reword 1" \
+ git rebase -i HEAD^
'
test_done
diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh
index 03bf1b8..25099d7 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,39 @@ 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
+'
+
+test_expect_success 'rebase -i --continue handles merge strategy and options' '
+ rm -fr .git/rebase-* &&
+ git reset --hard commit-new-file-F2-on-topic-branch &&
+ test_commit "commit-new-file-F3-on-topic-branch-for-dash-i" F3 32 &&
+ test_when_finished "rm -fr test-bin funny.was.run funny.args" &&
+ mkdir test-bin &&
+ cat >test-bin/git-merge-funny <<-EOF &&
+ #!$SHELL_PATH
+ echo "\$@" >>funny.args
+ case "\$1" in --opt) ;; *) exit 2 ;; esac
+ case "\$2" in --foo) ;; *) exit 2 ;; esac
+ case "\$4" in --) ;; *) exit 2 ;; esac
+ shift 2 &&
+ >funny.was.run &&
+ exec git merge-recursive "\$@"
+ EOF
+ chmod +x test-bin/git-merge-funny &&
+ (
+ PATH=./test-bin:$PATH &&
+ test_must_fail git rebase -i -s funny -Xopt -Xfoo master topic
+ ) &&
+ test -f funny.was.run &&
+ rm funny.was.run &&
+ echo "Resolved" >F2 &&
+ git add F2 &&
+ (
+ PATH=./test-bin:$PATH &&
git rebase --continue
) &&
test -f funny.was.run
@@ -128,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/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh
new file mode 100755
index 0000000..bb78a6e
--- /dev/null
+++ b/t/t3422-rebase-incompatible-options.sh
@@ -0,0 +1,88 @@
+#!/bin/sh
+
+test_description='test if rebase detects and aborts on incompatible options'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_seq 2 9 >foo &&
+ git add foo &&
+ git commit -m orig &&
+
+ git branch A &&
+ git branch B &&
+
+ git checkout A &&
+ test_seq 1 9 >foo &&
+ git add foo &&
+ git commit -m A &&
+
+ git checkout B &&
+ echo "q qfoo();" | q_to_tab >>foo &&
+ git add foo &&
+ git commit -m B
+'
+
+#
+# Rebase has lots of useful options like --whitepsace=fix, which are
+# actually all built in terms of flags to git-am. Since neither
+# --merge nor --interactive (nor any options that imply those two) use
+# git-am, using them together will result in flags like --whitespace=fix
+# being ignored. Make sure rebase warns the user and aborts instead.
+#
+
+test_rebase_am_only () {
+ opt=$1
+ shift
+ test_expect_success "$opt incompatible with --merge" "
+ git checkout B^0 &&
+ test_must_fail git rebase $opt --merge A
+ "
+
+ test_expect_success "$opt incompatible with --strategy=ours" "
+ git checkout B^0 &&
+ test_must_fail git rebase $opt --strategy=ours A
+ "
+
+ test_expect_success "$opt incompatible with --strategy-option=ours" "
+ git checkout B^0 &&
+ test_must_fail git rebase $opt --strategy-option=ours A
+ "
+
+ test_expect_success "$opt incompatible with --interactive" "
+ git checkout B^0 &&
+ test_must_fail git rebase $opt --interactive A
+ "
+
+ test_expect_success "$opt incompatible with --exec" "
+ git checkout B^0 &&
+ test_must_fail git rebase $opt --exec 'true' A
+ "
+
+}
+
+test_rebase_am_only --whitespace=fix
+test_rebase_am_only --ignore-whitespace
+test_rebase_am_only --committer-date-is-author-date
+test_rebase_am_only -C4
+
+test_expect_success '--preserve-merges incompatible with --signoff' '
+ git checkout B^0 &&
+ test_must_fail git rebase --preserve-merges --signoff A
+'
+
+test_expect_success '--preserve-merges incompatible with --rebase-merges' '
+ git checkout B^0 &&
+ test_must_fail git rebase --preserve-merges --rebase-merges A
+'
+
+test_expect_success '--rebase-merges incompatible with --strategy' '
+ git checkout B^0 &&
+ test_must_fail git rebase --rebase-merges -s resolve A
+'
+
+test_expect_success '--rebase-merges incompatible with --strategy-option' '
+ git checkout B^0 &&
+ test_must_fail git rebase --rebase-merges -Xignore-space-change A
+'
+
+test_done
diff --git a/t/t3423-rebase-reword.sh b/t/t3423-rebase-reword.sh
new file mode 100755
index 0000000..6963750
--- /dev/null
+++ b/t/t3423-rebase-reword.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+test_description='git rebase interactive with rewording'
+
+. ./test-lib.sh
+
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+test_expect_success 'setup' '
+ test_commit master file-1 test &&
+
+ git checkout -b stuff &&
+
+ test_commit feature_a file-2 aaa &&
+ test_commit feature_b file-2 ddd
+'
+
+test_expect_success 'reword without issues functions as intended' '
+ test_when_finished "reset_rebase" &&
+
+ git checkout stuff^0 &&
+
+ set_fake_editor &&
+ FAKE_LINES="pick 1 reword 2" FAKE_COMMIT_MESSAGE="feature_b_reworded" \
+ git rebase -i -v master &&
+
+ test "$(git log -1 --format=%B)" = "feature_b_reworded" &&
+ test $(git rev-list --count HEAD) = 3
+'
+
+test_expect_success 'reword after a conflict preserves commit' '
+ test_when_finished "reset_rebase" &&
+
+ git checkout stuff^0 &&
+
+ set_fake_editor &&
+ test_must_fail env FAKE_LINES="reword 2" \
+ git rebase -i -v master &&
+
+ git checkout --theirs file-2 &&
+ git add file-2 &&
+ FAKE_COMMIT_MESSAGE="feature_b_reworded" git rebase --continue &&
+
+ test "$(git log -1 --format=%B)" = "feature_b_reworded" &&
+ test $(git rev-list --count HEAD) = 2
+'
+
+test_done
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index 78f7c99..aa7bfc8 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 7c5ad08..0db1661 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 b42cd66..c84eeef 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
@@ -480,11 +485,16 @@ test_expect_success 'malformed instruction sheet 2' '
test_expect_code 128 git cherry-pick --continue
'
-test_expect_success 'empty commit set' '
+test_expect_success 'empty commit set (no commits to walk)' '
pristine_detach initial &&
test_expect_code 128 git cherry-pick base..base
'
+test_expect_success 'empty commit set (culled during walk)' '
+ pristine_detach initial &&
+ test_expect_code 128 git cherry-pick -2 --author=no.such.author base
+'
+
test_expect_success 'malformed instruction sheet 3' '
pristine_detach initial &&
test_expect_code 1 git cherry-pick base..anotherpick &&
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index b8fbdef..5829dfd 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'
diff --git a/t/t3700-add.sh b/t/t3700-add.sh
index 07af05d..37729ba 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 b170fb0..609fbfd 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,50 @@ 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
+'
+
+test_expect_success 'setup file' '
+ test_write_lines a "" b "" c >file &&
+ git add file &&
+ test_write_lines a "" d "" c >file
+'
+
+test_expect_success 'setup patch' '
+ SP=" " &&
+ NULL="" &&
+ cat >patch <<-EOF
+ @@ -1,4 +1,4 @@
+ a
+ $NULL
+ -b
+ +f
+ $SP
+ c
+ EOF
+'
+
+test_expect_success 'setup expected' '
+ cat >expected <<-EOF
+ diff --git a/file b/file
+ index b5dd6c9..f910ae9 100644
+ --- a/file
+ +++ b/file
+ @@ -1,5 +1,5 @@
+ a
+ $SP
+ -f
+ +d
+ $SP
+ c
+ EOF
+'
+
+test_expect_success 'edit can strip spaces from empty context lines' '
+ test_write_lines e n q | git add -p 2>error &&
+ test_must_be_empty error &&
git diff >output &&
diff_cmp expected output
'
diff --git a/t/t3904-stash-patch.sh b/t/t3904-stash-patch.sh
index 83744f8..9546b6f 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 26dd5b7..54ce19e 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 bf40303..c16486a 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 35b35a8..281f8fa 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 cf0f3a1..108c012 100755
--- a/t/t4011-diff-symlink.sh
+++ b/t/t4011-diff-symlink.sh
@@ -139,11 +139,13 @@ test_expect_success SYMLINKS 'setup symlinks with attributes' '
test_expect_success SYMLINKS 'symlinks do not respect userdiff config by path' '
cat >expect <<-\EOF &&
diff --git a/file.bin b/file.bin
- index e69de29..d95f3ad 100644
- Binary files a/file.bin and b/file.bin differ
+ new file mode 100644
+ index 0000000..d95f3ad
+ Binary files /dev/null and b/file.bin differ
diff --git a/link.bin b/link.bin
- index e69de29..dce41ec 120000
- --- a/link.bin
+ new file mode 120000
+ index 0000000..dce41ec
+ --- /dev/null
+++ b/link.bin
@@ -0,0 +1 @@
+file.bin
diff --git a/t/t4012-diff-binary.sh b/t/t4012-diff-binary.sh
index 0a8af76..6579c81 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/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 028d550..53880da 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -1554,13 +1554,15 @@ test_expect_success 'format-patch -o overrides format.outputDirectory' '
test_expect_success 'format-patch --base' '
git checkout side &&
- git format-patch --stdout --base=HEAD~3 -1 | tail -n 7 >actual &&
+ git format-patch --stdout --base=HEAD~3 -1 | tail -n 7 >actual1 &&
+ git format-patch --stdout --base=HEAD~3 HEAD~.. | tail -n 7 >actual2 &&
echo >expected &&
echo "base-commit: $(git rev-parse HEAD~3)" >>expected &&
echo "prerequisite-patch-id: $(git show --patch HEAD~2 | git patch-id --stable | awk "{print \$1}")" >>expected &&
echo "prerequisite-patch-id: $(git show --patch HEAD~1 | git patch-id --stable | awk "{print \$1}")" >>expected &&
signature >> expected &&
- test_cmp expected actual
+ test_cmp expected actual1 &&
+ test_cmp expected actual2
'
test_expect_success 'format-patch --base errors out when base commit is in revision list' '
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index 17df491..35fc8b5 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' '
@@ -1223,7 +1220,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 +1268,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 +1355,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 +1434,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 +1457,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 +1497,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 +1520,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 +1563,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 +1586,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 +1633,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 +1671,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 +1713,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 +1757,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/t4018/php-abstract-class b/t/t4018/php-abstract-class
new file mode 100644
index 0000000..5213e12
--- /dev/null
+++ b/t/t4018/php-abstract-class
@@ -0,0 +1,4 @@
+abstract class RIGHT
+{
+ const FOO = 'ChangeMe';
+}
diff --git a/t/t4018/php-class b/t/t4018/php-class
new file mode 100644
index 0000000..7785b63
--- /dev/null
+++ b/t/t4018/php-class
@@ -0,0 +1,4 @@
+class RIGHT
+{
+ const FOO = 'ChangeMe';
+}
diff --git a/t/t4018/php-final-class b/t/t4018/php-final-class
new file mode 100644
index 0000000..69f5710
--- /dev/null
+++ b/t/t4018/php-final-class
@@ -0,0 +1,4 @@
+final class RIGHT
+{
+ const FOO = 'ChangeMe';
+}
diff --git a/t/t4018/php-function b/t/t4018/php-function
new file mode 100644
index 0000000..35717c5
--- /dev/null
+++ b/t/t4018/php-function
@@ -0,0 +1,4 @@
+function RIGHT()
+{
+ return 'ChangeMe';
+}
diff --git a/t/t4018/php-interface b/t/t4018/php-interface
new file mode 100644
index 0000000..86b49ad
--- /dev/null
+++ b/t/t4018/php-interface
@@ -0,0 +1,4 @@
+interface RIGHT
+{
+ public function foo($ChangeMe);
+}
diff --git a/t/t4018/php-method b/t/t4018/php-method
new file mode 100644
index 0000000..03af1a6
--- /dev/null
+++ b/t/t4018/php-method
@@ -0,0 +1,7 @@
+class Klass
+{
+ public static function RIGHT()
+ {
+ return 'ChangeMe';
+ }
+}
diff --git a/t/t4018/php-trait b/t/t4018/php-trait
new file mode 100644
index 0000000..65b8c82
--- /dev/null
+++ b/t/t4018/php-trait
@@ -0,0 +1,7 @@
+trait RIGHT
+{
+ public function foo($ChangeMe)
+ {
+ return 'foo';
+ }
+}
diff --git a/t/t4024-diff-optimize-common.sh b/t/t4024-diff-optimize-common.sh
index 7e76018..6b44ce1 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 7a3dbc1..fa44e78 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/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh
index 23c0e35..53ac44b 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 058ee08..4e3499e 100755
--- a/t/t4041-diff-submodule-option.sh
+++ b/t/t4041-diff-submodule-option.sh
@@ -498,7 +498,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 +527,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/t4060-diff-submodule-option-diff-format.sh b/t/t4060-diff-submodule-option-diff-format.sh
index 4b168d0..0eba462 100755
--- a/t/t4060-diff-submodule-option-diff-format.sh
+++ b/t/t4060-diff-submodule-option-diff-format.sh
@@ -721,7 +721,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/t4121-apply-diffs.sh b/t/t4121-apply-diffs.sh
index aff551a..66368ef 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/t4135-apply-weird-filenames.sh b/t/t4135-apply-weird-filenames.sh
index c7c688f..6bc3fb9 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 1ebc587..01867a9 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' '
diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh
index 8417e5a..65da74c 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 &&
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index 25b1f8c..153a506 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/t4208-log-magic-pathspec.sh b/t/t4208-log-magic-pathspec.sh
index 62f335b..4c8f3b8 100755
--- a/t/t4208-log-magic-pathspec.sh
+++ b/t/t4208-log-magic-pathspec.sh
@@ -25,6 +25,32 @@ test_expect_success '"git log :/a -- " should not be ambiguous' '
git log :/a --
'
+test_expect_success '"git log :/detached -- " should find a commit only in HEAD' '
+ test_when_finished "git checkout master" &&
+ git checkout --detach &&
+ # Must manually call `test_tick` instead of using `test_commit`,
+ # because the latter additionally creates a tag, which would make
+ # the commit reachable not only via HEAD.
+ test_tick &&
+ git commit --allow-empty -m detached &&
+ test_tick &&
+ git commit --allow-empty -m something-else &&
+ git log :/detached --
+'
+
+test_expect_success '"git log :/detached -- " should not find an orphaned commit' '
+ test_must_fail git log :/detached --
+'
+
+test_expect_success '"git log :/detached -- " should find HEAD only of own worktree' '
+ git worktree add other-tree HEAD &&
+ git -C other-tree checkout --detach &&
+ test_tick &&
+ git -C other-tree commit --allow-empty -m other-detached &&
+ git -C other-tree log :/other-detached -- &&
+ test_must_fail git log :/other-detached --
+'
+
test_expect_success '"git log -- :/a" should not be ambiguous' '
git log -- :/a
'
diff --git a/t/t4210-log-i18n.sh b/t/t4210-log-i18n.sh
index e585fe6..7c51943 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 d0377fa..436b13a 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' '
diff --git a/t/t4254-am-corrupt.sh b/t/t4254-am-corrupt.sh
index 168739c..fd3bdbf 100755
--- a/t/t4254-am-corrupt.sh
+++ b/t/t4254-am-corrupt.sh
@@ -25,7 +25,7 @@ test_expect_success setup '
# fatal: unable to write file '(null)' mode 100644: Bad address
# Also, it had the unwanted side-effect of deleting f.
test_expect_success 'try to apply corrupted patch' '
- test_must_fail git am bad-patch.diff 2>actual
+ test_must_fail git -c advice.amWorkDir=false am bad-patch.diff 2>actual
'
test_expect_success 'compare diagnostic; ensure file is still here' '
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
index 2336d09..6c620cd 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 bb9b8bb..91d51b3 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/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh
index 2d22a17..557bd0d 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' '
diff --git a/t/t5313-pack-bounds-checks.sh b/t/t5313-pack-bounds-checks.sh
index 4fe4ad9..f1708d4 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/t5317-pack-objects-filter-objects.sh b/t/t5317-pack-objects-filter-objects.sh
index 1b0acc3..6710c8b 100755
--- a/t/t5317-pack-objects-filter-objects.sh
+++ b/t/t5317-pack-objects-filter-objects.sh
@@ -160,6 +160,22 @@ test_expect_success 'verify blob:limit=1k' '
test_cmp observed expected
'
+test_expect_success 'verify explicitly specifying oversized blob in input' '
+ git -C r2 ls-files -s large.1000 large.10000 \
+ | awk -f print_2.awk \
+ | sort >expected &&
+ git -C r2 pack-objects --rev --stdout --filter=blob:limit=1k >filter.pack <<-EOF &&
+ HEAD
+ $(git -C r2 rev-parse HEAD:large.10000)
+ EOF
+ git -C r2 index-pack ../filter.pack &&
+ git -C r2 verify-pack -v ../filter.pack \
+ | grep blob \
+ | awk -f print_1.awk \
+ | sort >observed &&
+ test_cmp observed expected
+'
+
test_expect_success 'verify blob:limit=1m' '
git -C r2 ls-files -s large.1000 large.10000 \
| awk -f print_2.awk \
diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh
index a380419..3c1ffad 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
}
@@ -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 &&
@@ -221,4 +236,236 @@ test_expect_success 'write graph in bare repo' '
graph_git_behavior 'bare repo with graph, commit 8 vs merge 1' bare commits/8 merge/1
graph_git_behavior 'bare repo with graph, commit 8 vs merge 2' bare commits/8 merge/2
+test_expect_success 'perform fast-forward merge in full repo' '
+ cd "$TRASH_DIRECTORY/full" &&
+ git checkout -b merge-5-to-8 commits/5 &&
+ git merge commits/8 &&
+ git show-ref -s merge-5-to-8 >output &&
+ git show-ref -s commits/8 >expect &&
+ 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/t5400-send-pack.sh b/t/t5400-send-pack.sh
index 911eae1..f1932ea 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 7f278d8c..b5f886a 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
'
diff --git a/t/t5405-send-pack-rewind.sh b/t/t5405-send-pack-rewind.sh
index 4bda18a..235fb76 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 59e80a5..ff06f99 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/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh
index 7a48236..9b2a274 100755
--- a/t/t5407-post-rewrite-hook.sh
+++ b/t/t5407-post-rewrite-hook.sh
@@ -113,7 +113,7 @@ test_expect_success 'git rebase -m' '
test_expect_success 'git rebase -m --skip' '
git reset --hard D &&
clear_hook_input &&
- test_must_fail git rebase --onto A B &&
+ test_must_fail git rebase -m --onto A B &&
test_must_fail git rebase --skip &&
echo D > foo &&
git add foo &&
diff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh
new file mode 100755
index 0000000..f81b681
--- /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 d4f4351..8f94523 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -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
)
'
@@ -518,6 +518,54 @@ test_expect_success 'test --all, --depth, and explicit tag' '
) >out-adt 2>error-adt
'
+test_expect_success 'test --all with tag to non-tip' '
+ git commit --allow-empty -m non-tip &&
+ git commit --allow-empty -m tip &&
+ git tag -m "annotated" non-tip HEAD^ &&
+ (
+ cd client &&
+ git fetch-pack --all ..
+ )
+'
+
+test_expect_success 'test --all wrt tag to non-commits' '
+ # create tag-to-{blob,tree,commit,tag}, making sure all tagged objects
+ # are reachable only via created tag references.
+ blob=$(echo "hello blob" | git hash-object -t blob -w --stdin) &&
+ git tag -a -m "tag -> blob" tag-to-blob $blob &&
+
+ tree=$(printf "100644 blob $blob\tfile" | git mktree) &&
+ git tag -a -m "tag -> tree" tag-to-tree $tree &&
+
+ tree2=$(printf "100644 blob $blob\tfile2" | git mktree) &&
+ commit=$(git commit-tree -m "hello commit" $tree) &&
+ git tag -a -m "tag -> commit" tag-to-commit $commit &&
+
+ blob2=$(echo "hello blob2" | git hash-object -t blob -w --stdin) &&
+ tag=$(git mktag <<-EOF
+ object $blob2
+ type blob
+ tag tag-to-blob2
+ tagger author A U Thor <author@example.com> 0 +0000
+
+ hello tag
+ EOF
+ ) &&
+ git tag -a -m "tag -> tag" tag-to-tag $tag &&
+
+ # `fetch-pack --all` should succeed fetching all those objects.
+ mkdir fetchall &&
+ (
+ cd fetchall &&
+ git init &&
+ git fetch-pack --all .. &&
+ git cat-file blob $blob >/dev/null &&
+ git cat-file tree $tree >/dev/null &&
+ git cat-file commit $commit >/dev/null &&
+ git cat-file tag $tag >/dev/null
+ )
+'
+
test_expect_success 'shallow fetch with tags does not break the repository' '
mkdir repo1 &&
(
@@ -711,6 +759,17 @@ test_expect_success 'fetch shallow since ...' '
test_cmp expected actual
'
+test_expect_success 'clone shallow since selects no commits' '
+ test_create_repo shallow-since-the-future &&
+ (
+ cd shallow-since-the-future &&
+ GIT_COMMITTER_DATE="100000000 +0700" git commit --allow-empty -m one &&
+ GIT_COMMITTER_DATE="200000000 +0700" git commit --allow-empty -m two &&
+ GIT_COMMITTER_DATE="300000000 +0700" git commit --allow-empty -m three &&
+ test_must_fail git clone --shallow-since "900000000 +0700" "file://$(pwd)/." ../shallow111
+ )
+'
+
test_expect_success 'shallow clone exclude tag two' '
test_create_repo shallow-exclude &&
(
@@ -755,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 49d3621..62f3569 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 a6c0178..241e6a3 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/t5510-fetch.sh b/t/t5510-fetch.sh
index e402aee..3b7b305 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 () {
@@ -613,7 +648,7 @@ test_configured_prune_type () {
git rev-parse --verify refs/tags/newtag
) &&
- # now remove it
+ # now remove them
git branch -d newbranch &&
git tag -d newtag &&
@@ -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 6a94948..bc5703f 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 4b4b667..0030c92 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 a5077d8..539c25a 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
) &&
(
@@ -965,26 +965,51 @@ test_expect_success 'push into aliased refs (inconsistent)' '
)
'
-test_expect_success 'push requires --force to update lightweight tag' '
- mk_test testrepo heads/master &&
- mk_child testrepo child1 &&
- mk_child testrepo child2 &&
- (
- cd child1 &&
- git tag Tag &&
- git push ../child2 Tag &&
- git push ../child2 Tag &&
- >file1 &&
- git add file1 &&
- git commit -m "file1" &&
- git tag -f Tag &&
- test_must_fail git push ../child2 Tag &&
- git push --force ../child2 Tag &&
- git tag -f Tag &&
- test_must_fail git push ../child2 Tag HEAD~ &&
- git push --force ../child2 Tag
- )
-'
+test_force_push_tag () {
+ tag_type_description=$1
+ tag_args=$2
+
+ test_expect_success 'force pushing required to update lightweight tag' "
+ mk_test testrepo heads/master &&
+ mk_child testrepo child1 &&
+ mk_child testrepo child2 &&
+ (
+ cd child1 &&
+ git tag testTag &&
+ git push ../child2 testTag &&
+ >file1 &&
+ git add file1 &&
+ git commit -m 'file1' &&
+ git tag $tag_args testTag &&
+ test_must_fail git push ../child2 testTag &&
+ git push --force ../child2 testTag &&
+ git tag $tag_args testTag HEAD~ &&
+ test_must_fail git push ../child2 testTag &&
+ git push --force ../child2 testTag &&
+
+ # Clobbering without + in refspec needs --force
+ git tag -f testTag &&
+ test_must_fail git push ../child2 'refs/tags/*:refs/tags/*' &&
+ git push --force ../child2 'refs/tags/*:refs/tags/*' &&
+
+ # Clobbering with + in refspec does not need --force
+ git tag -f testTag HEAD~ &&
+ git push ../child2 '+refs/tags/*:refs/tags/*' &&
+
+ # Clobbering with --no-force still obeys + in refspec
+ git tag -f testTag &&
+ git push --no-force ../child2 '+refs/tags/*:refs/tags/*' &&
+
+ # Clobbering with/without --force and 'tag <name>' format
+ git tag -f testTag HEAD~ &&
+ test_must_fail git push ../child2 tag testTag &&
+ git push --force ../child2 tag testTag
+ )
+ "
+}
+
+test_force_push_tag "lightweight tag" "-f"
+test_force_push_tag "annotated tag" "-f -a -m'msg'"
test_expect_success 'push --porcelain' '
mk_empty testrepo &&
@@ -1011,7 +1036,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" &&
@@ -1025,7 +1050,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" &&
@@ -1333,7 +1358,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 02f160a..c05a661 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 59c4b77..5e501c8 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/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh
index 9cc4b56..0f730d7 100755
--- a/t/t5526-fetch-submodules.sh
+++ b/t/t5526-fetch-submodules.sh
@@ -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
@@ -574,11 +574,7 @@ test_expect_success "fetch new commits when submodule got renamed" '
git clone . downstream_rename &&
(
cd downstream_rename &&
- git submodule update --init &&
-# NEEDSWORK: we omitted --recursive for the submodule update here since
-# that does not work. See test 7001 for mv "moving nested submodules"
-# for details. Once that is fixed we should add the --recursive option
-# here.
+ git submodule update --init --recursive &&
git checkout -b rename &&
git mv submodule submodule_renamed &&
(
diff --git a/t/t5531-deep-submodule-push.sh b/t/t5531-deep-submodule-push.sh
index 39cb2c1..e2c37fd 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 d38ecee..0b0eb1d 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 1cea758..030331f 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/t5537-fetch-shallow.sh b/t/t5537-fetch-shallow.sh
index df8d2f0..7045685 100755
--- a/t/t5537-fetch-shallow.sh
+++ b/t/t5537-fetch-shallow.sh
@@ -175,8 +175,8 @@ EOF
test_expect_success POSIXPERM,SANITY 'shallow fetch from a read-only repo' '
cp -R .git read-only.git &&
- find read-only.git -print | xargs chmod -w &&
test_when_finished "find read-only.git -type d -print | xargs chmod +w" &&
+ find read-only.git -print | xargs chmod -w &&
git clone --no-local --depth=2 read-only.git from-read-only &&
git --git-dir=from-read-only/.git log --format=%s >actual &&
cat >expect <<EOF &&
@@ -186,4 +186,47 @@ EOF
test_cmp expect actual
'
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+REPO="$HTTPD_DOCUMENT_ROOT_PATH/repo"
+
+test_expect_success 'shallow fetches check connectivity before writing shallow file' '
+ rm -rf "$REPO" client &&
+
+ git init "$REPO" &&
+ test_commit -C "$REPO" one &&
+ test_commit -C "$REPO" two &&
+ test_commit -C "$REPO" three &&
+
+ git init client &&
+
+ # Use protocol v2 to ensure that shallow information is sent exactly
+ # once by the server, since we are planning to manipulate it.
+ git -C "$REPO" config protocol.version 2 &&
+ git -C client config protocol.version 2 &&
+
+ git -C client fetch --depth=2 "$HTTPD_URL/one_time_sed/repo" master:a_branch &&
+
+ # Craft a situation in which the server sends back an unshallow request
+ # with an empty packfile. This is done by refetching with a shorter
+ # depth (to ensure that the packfile is empty), and overwriting the
+ # shallow line in the response with the unshallow line we want.
+ printf "s/0034shallow %s/0036unshallow %s/" \
+ "$(git -C "$REPO" rev-parse HEAD)" \
+ "$(git -C "$REPO" rev-parse HEAD^)" \
+ >"$HTTPD_ROOT_PATH/one-time-sed" &&
+ test_must_fail git -C client fetch --depth=1 "$HTTPD_URL/one_time_sed/repo" \
+ master:a_branch &&
+
+ # Ensure that the one-time-sed script was used.
+ ! test -e "$HTTPD_ROOT_PATH/one-time-sed" &&
+
+ # Ensure that the resulting repo is consistent, despite our failure to
+ # fetch.
+ git -C client fsck
+'
+
+stop_httpd
+
test_done
diff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh
index a2af693..a0fc400 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 \
diff --git a/t/t5543-atomic-push.sh b/t/t5543-atomic-push.sh
index 3480b33..7079bcf 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 913089b..771f36f 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 0000000..5ad5bec
--- /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 $(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 $(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 $(seq 8)
+ do
+ git -C client checkout --orphan b$i &&
+ test_commit -C client b$i.c0
+ done &&
+ for j in $(seq 19)
+ do
+ for i in $(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 $(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 84a9557..1c49054 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 0000000..43570ce
--- /dev/null
+++ b/t/t5562-http-backend-content-length.sh
@@ -0,0 +1,156 @@
+#!/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_done
diff --git a/t/t5562/invoke-with-content-length.pl b/t/t5562/invoke-with-content-length.pl
new file mode 100755
index 0000000..6c2aae7
--- /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 0d4c520..a571f22 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' '
diff --git a/t/t5573-pull-verify-signatures.sh b/t/t5573-pull-verify-signatures.sh
index 9594e89..747775c 100755
--- a/t/t5573-pull-verify-signatures.sh
+++ b/t/t5573-pull-verify-signatures.sh
@@ -29,7 +29,7 @@ test_expect_success GPG 'create repositories with signed commits' '
echo 4 >d && git add d &&
test_tick && git commit -S -m "bad" &&
git cat-file commit HEAD >raw &&
- sed -e "s/bad/forged bad/" raw >forged &&
+ sed -e "s/^bad/forged bad/" raw >forged &&
git hash-object -w -t commit forged >forged.commit &&
git checkout $(cat forged.commit)
) &&
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index 0b62037..ddaa96a 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -618,7 +618,7 @@ 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) &&
diff --git a/t/t5605-clone-local.sh b/t/t5605-clone-local.sh
index 3c087e9..af23419 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 df822d9..2c6bc07 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 fac5a73..5582b3d 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 cee5565..bbbe753 100755
--- a/t/t5616-partial-clone.sh
+++ b/t/t5616-partial-clone.sh
@@ -154,4 +154,112 @@ test_expect_success 'partial clone with transfer.fsckobjects=1 uses index-pack -
grep "git index-pack.*--fsck-objects" trace
'
+test_expect_success 'partial clone fetches blobs pointed to by refs even if normally filtered out' '
+ rm -rf src dst &&
+ git init src &&
+ test_commit -C src x &&
+ test_config -C src uploadpack.allowfilter 1 &&
+ test_config -C src uploadpack.allowanysha1inwant 1 &&
+
+ # Create a tag pointing to a blob.
+ BLOB=$(echo blob-contents | git -C src hash-object --stdin -w) &&
+ git -C src tag myblob "$BLOB" &&
+
+ git clone --filter="blob:none" "file://$(pwd)/src" dst 2>err &&
+ ! grep "does not point to a valid object" err &&
+ git -C dst fsck
+'
+
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+# Converts bytes into a form suitable for inclusion in a sed command. For
+# example, "printf 'ab\r\n' | hex_unpack" results in '\x61\x62\x0d\x0a'.
+sed_escape () {
+ perl -e '$/ = undef; $input = <>; print unpack("H2" x length($input), $input)' |
+ sed 's/\(..\)/\\x\1/g'
+}
+
+test_expect_success 'upon cloning, check that all refs point to objects' '
+ 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 a tag pointing to a blob.
+ BLOB=$(echo blob-contents | git -C "$SERVER" hash-object --stdin -w) &&
+ git -C "$SERVER" tag myblob "$BLOB" &&
+
+ # Craft a packfile not including that blob.
+ git -C "$SERVER" rev-parse HEAD |
+ git -C "$SERVER" pack-objects --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 &&
+ test_must_fail git -c protocol.version=2 clone \
+ --filter=blob:none $HTTPD_URL/one_time_sed/server repo 2>err &&
+
+ grep "did not send all necessary objects" err &&
+
+ # Ensure that the one-time-sed script was used.
+ ! 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 a4fe650..3beeed4 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
new file mode 100755
index 0000000..a73c55a
--- /dev/null
+++ b/t/t5703-upload-pack-ref-in-want.sh
@@ -0,0 +1,377 @@
+#!/bin/sh
+
+test_description='upload-pack ref-in-want'
+
+. ./test-lib.sh
+
+get_actual_refs () {
+ sed -n -e '/wanted-refs/,/0001/{
+ /wanted-refs/d
+ /0001/d
+ p
+ }' <out | test-pkt-line unpack >actual_refs
+}
+
+get_actual_commits () {
+ sed -n -e '/packfile/,/0000/{
+ /packfile/d
+ p
+ }' <out | test-pkt-line unpack-sideband >o.pack &&
+ git index-pack o.pack &&
+ git verify-pack -v o.idx | grep commit | cut -c-40 | sort >actual_commits
+}
+
+check_output () {
+ get_actual_refs &&
+ test_cmp expected_refs actual_refs &&
+ get_actual_commits &&
+ test_cmp expected_commits actual_commits
+}
+
+# c(o/foo) d(o/bar)
+# \ /
+# b e(baz) f(master)
+# \__ | __/
+# \ | /
+# a
+test_expect_success 'setup repository' '
+ test_commit a &&
+ git checkout -b o/foo &&
+ test_commit b &&
+ test_commit c &&
+ git checkout -b o/bar b &&
+ test_commit d &&
+ git checkout -b baz a &&
+ test_commit e &&
+ git checkout master &&
+ test_commit f
+'
+
+test_expect_success 'config controls ref-in-want advertisement' '
+ git serve --advertise-capabilities >out &&
+ ! grep -a ref-in-want out &&
+
+ git config uploadpack.allowRefInWant false &&
+ git serve --advertise-capabilities >out &&
+ ! grep -a ref-in-want out &&
+
+ git config uploadpack.allowRefInWant true &&
+ git serve --advertise-capabilities >out &&
+ grep -a ref-in-want out
+'
+
+test_expect_success 'invalid want-ref line' '
+ test-pkt-line pack >in <<-EOF &&
+ command=fetch
+ 0001
+ no-progress
+ want-ref refs/heads/non-existent
+ done
+ 0000
+ EOF
+
+ test_must_fail git serve --stateless-rpc 2>out <in &&
+ grep "unknown ref" out
+'
+
+test_expect_success 'basic want-ref' '
+ cat >expected_refs <<-EOF &&
+ $(git rev-parse f) refs/heads/master
+ EOF
+ git rev-parse f | sort >expected_commits &&
+
+ test-pkt-line pack >in <<-EOF &&
+ command=fetch
+ 0001
+ no-progress
+ want-ref refs/heads/master
+ have $(git rev-parse a)
+ done
+ 0000
+ EOF
+
+ git serve --stateless-rpc >out <in &&
+ check_output
+'
+
+test_expect_success 'multiple want-ref lines' '
+ cat >expected_refs <<-EOF &&
+ $(git rev-parse c) refs/heads/o/foo
+ $(git rev-parse d) refs/heads/o/bar
+ EOF
+ git rev-parse c d | sort >expected_commits &&
+
+ test-pkt-line pack >in <<-EOF &&
+ command=fetch
+ 0001
+ no-progress
+ want-ref refs/heads/o/foo
+ want-ref refs/heads/o/bar
+ have $(git rev-parse b)
+ done
+ 0000
+ EOF
+
+ git serve --stateless-rpc >out <in &&
+ check_output
+'
+
+test_expect_success 'mix want and want-ref' '
+ cat >expected_refs <<-EOF &&
+ $(git rev-parse f) refs/heads/master
+ EOF
+ git rev-parse e f | sort >expected_commits &&
+
+ test-pkt-line pack >in <<-EOF &&
+ command=fetch
+ 0001
+ no-progress
+ want-ref refs/heads/master
+ want $(git rev-parse e)
+ have $(git rev-parse a)
+ done
+ 0000
+ EOF
+
+ git serve --stateless-rpc >out <in &&
+ check_output
+'
+
+test_expect_success 'want-ref with ref we already have commit for' '
+ cat >expected_refs <<-EOF &&
+ $(git rev-parse c) refs/heads/o/foo
+ EOF
+ >expected_commits &&
+
+ test-pkt-line pack >in <<-EOF &&
+ command=fetch
+ 0001
+ no-progress
+ want-ref refs/heads/o/foo
+ have $(git rev-parse c)
+ done
+ 0000
+ EOF
+
+ git serve --stateless-rpc >out <in &&
+ check_output
+'
+
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+REPO="$HTTPD_DOCUMENT_ROOT_PATH/repo"
+LOCAL_PRISTINE="$(pwd)/local_pristine"
+
+test_expect_success 'setup repos for change-while-negotiating test' '
+ (
+ git init "$REPO" &&
+ cd "$REPO" &&
+ >.git/git-daemon-export-ok &&
+ test_commit m1 &&
+ git tag -d m1 &&
+
+ # Local repo with many commits (so that negotiation will take
+ # more than 1 request/response pair)
+ 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 &&
+
+ # Add novel commits to upstream
+ git checkout master &&
+ cd "$REPO" &&
+ test_commit m2 &&
+ test_commit m3 &&
+ git tag -d m2 m3
+ ) &&
+ git -C "$LOCAL_PRISTINE" remote set-url origin "http://127.0.0.1:$LIB_HTTPD_PORT/one_time_sed/repo" &&
+ git -C "$LOCAL_PRISTINE" config protocol.version 2
+'
+
+inconsistency () {
+ # Simulate that the server initially reports $2 as the ref
+ # corresponding to $1, and after that, $1 as the ref corresponding to
+ # $1. This corresponds to the real-life situation where the server's
+ # repository appears to change during negotiation, for example, when
+ # different servers in a load-balancing arrangement serve (stateless)
+ # RPCs during a single negotiation.
+ printf "s/%s/%s/" \
+ $(git -C "$REPO" rev-parse $1 | tr -d "\n") \
+ $(git -C "$REPO" rev-parse $2 | tr -d "\n") \
+ >"$HTTPD_ROOT_PATH/one-time-sed"
+}
+
+test_expect_success 'server is initially ahead - no ref in want' '
+ git -C "$REPO" config uploadpack.allowRefInWant false &&
+ rm -rf local &&
+ cp -r "$LOCAL_PRISTINE" local &&
+ inconsistency master 1234567890123456789012345678901234567890 &&
+ test_must_fail git -C local fetch 2>err &&
+ grep "ERR upload-pack: not our ref" err
+'
+
+test_expect_success 'server is initially ahead - ref in want' '
+ git -C "$REPO" config uploadpack.allowRefInWant true &&
+ rm -rf local &&
+ cp -r "$LOCAL_PRISTINE" local &&
+ inconsistency master 1234567890123456789012345678901234567890 &&
+ git -C local fetch &&
+
+ git -C "$REPO" rev-parse --verify master >expected &&
+ git -C local rev-parse --verify refs/remotes/origin/master >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'server is initially behind - no ref in want' '
+ git -C "$REPO" config uploadpack.allowRefInWant false &&
+ rm -rf local &&
+ cp -r "$LOCAL_PRISTINE" local &&
+ inconsistency master "master^" &&
+ git -C local fetch &&
+
+ git -C "$REPO" rev-parse --verify "master^" >expected &&
+ git -C local rev-parse --verify refs/remotes/origin/master >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'server is initially behind - ref in want' '
+ git -C "$REPO" config uploadpack.allowRefInWant true &&
+ rm -rf local &&
+ cp -r "$LOCAL_PRISTINE" local &&
+ inconsistency master "master^" &&
+ git -C local fetch &&
+
+ git -C "$REPO" rev-parse --verify "master" >expected &&
+ git -C local rev-parse --verify refs/remotes/origin/master >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'server loses a ref - ref in want' '
+ git -C "$REPO" config uploadpack.allowRefInWant true &&
+ rm -rf local &&
+ cp -r "$LOCAL_PRISTINE" local &&
+ echo "s/master/raster/" >"$HTTPD_ROOT_PATH/one-time-sed" &&
+ test_must_fail git -C local fetch 2>err &&
+
+ grep "ERR unknown ref refs/heads/raster" err
+'
+
+stop_httpd
+
+REPO="$(pwd)/repo"
+LOCAL_PRISTINE="$(pwd)/local_pristine"
+
+# $REPO
+# c(o/foo) d(o/bar)
+# \ /
+# b e(baz) f(master)
+# \__ | __/
+# \ | /
+# a
+#
+# $LOCAL_PRISTINE
+# s32(side)
+# |
+# .
+# .
+# |
+# a(master)
+test_expect_success 'setup repos for fetching with ref-in-want tests' '
+ (
+ git init "$REPO" &&
+ cd "$REPO" &&
+ test_commit a &&
+
+ # Local repo with many commits (so that negotiation will take
+ # more than 1 request/response pair)
+ rm -rf "$LOCAL_PRISTINE" &&
+ 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 &&
+
+ # Add novel commits to upstream
+ git checkout master &&
+ cd "$REPO" &&
+ git checkout -b o/foo &&
+ test_commit b &&
+ test_commit c &&
+ git checkout -b o/bar b &&
+ test_commit d &&
+ git checkout -b baz a &&
+ test_commit e &&
+ git checkout master &&
+ test_commit f
+ ) &&
+ git -C "$REPO" config uploadpack.allowRefInWant true &&
+ git -C "$LOCAL_PRISTINE" config protocol.version 2
+'
+
+test_expect_success 'fetching with exact OID' '
+ test_when_finished "rm -f log" &&
+
+ rm -rf local &&
+ cp -r "$LOCAL_PRISTINE" local &&
+ GIT_TRACE_PACKET="$(pwd)/log" git -C local fetch origin \
+ $(git -C "$REPO" rev-parse d):refs/heads/actual &&
+
+ git -C "$REPO" rev-parse "d" >expected &&
+ git -C local rev-parse refs/heads/actual >actual &&
+ test_cmp expected actual &&
+ grep "want $(git -C "$REPO" rev-parse d)" log
+'
+
+test_expect_success 'fetching multiple refs' '
+ test_when_finished "rm -f log" &&
+
+ rm -rf local &&
+ cp -r "$LOCAL_PRISTINE" local &&
+ GIT_TRACE_PACKET="$(pwd)/log" git -C local fetch origin master baz &&
+
+ git -C "$REPO" rev-parse "master" "baz" >expected &&
+ git -C local rev-parse refs/remotes/origin/master refs/remotes/origin/baz >actual &&
+ test_cmp expected actual &&
+ grep "want-ref refs/heads/master" log &&
+ grep "want-ref refs/heads/baz" log
+'
+
+test_expect_success 'fetching ref and exact OID' '
+ test_when_finished "rm -f log" &&
+
+ rm -rf local &&
+ cp -r "$LOCAL_PRISTINE" local &&
+ GIT_TRACE_PACKET="$(pwd)/log" git -C local fetch origin \
+ master $(git -C "$REPO" rev-parse b):refs/heads/actual &&
+
+ git -C "$REPO" rev-parse "master" "b" >expected &&
+ git -C local rev-parse refs/remotes/origin/master refs/heads/actual >actual &&
+ test_cmp expected actual &&
+ grep "want $(git -C "$REPO" rev-parse b)" log &&
+ grep "want-ref refs/heads/master" log
+'
+
+test_expect_success 'fetching with wildcard that does not match any refs' '
+ test_when_finished "rm -f log" &&
+
+ rm -rf local &&
+ cp -r "$LOCAL_PRISTINE" local &&
+ git -C local fetch origin refs/heads/none*:refs/heads/* >out &&
+ test_must_be_empty out
+'
+
+test_expect_success 'fetching with wildcard that matches multiple refs' '
+ test_when_finished "rm -f log" &&
+
+ rm -rf local &&
+ cp -r "$LOCAL_PRISTINE" local &&
+ GIT_TRACE_PACKET="$(pwd)/log" git -C local fetch origin refs/heads/o*:refs/heads/o* &&
+
+ git -C "$REPO" rev-parse "o/foo" "o/bar" >expected &&
+ git -C local rev-parse "o/foo" "o/bar" >actual &&
+ test_cmp expected actual &&
+ grep "want-ref refs/heads/o/foo" log &&
+ grep "want-ref refs/heads/o/bar" log
+'
+
+test_done
diff --git a/t/t5801-remote-helpers.sh b/t/t5801-remote-helpers.sh
index 362b158..aaaa722 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 969e4e9..fb4d295 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 20e3e25..916d969 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 aa2d360..44c726e 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/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh
index d3453c5..02936c2 100755
--- a/t/t6018-rev-list-glob.sh
+++ b/t/t6018-rev-list-glob.sh
@@ -256,31 +256,27 @@ test_expect_success 'rev-list accumulates multiple --exclude' '
'
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_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 dabebae..beadaf6 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 2af1bee..46b506b 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 b760c22..53cc9b2 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/t6029-merge-subtree.sh b/t/t6029-merge-subtree.sh
index 3e69245..793f0c8 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 18aa88b..59e52c5 100755
--- a/t/t6036-recursive-corner-cases.sh
+++ b/t/t6036-recursive-corner-cases.sh
@@ -4,12 +4,6 @@ test_description='recursive merge corner cases involving criss-cross merges'
. ./test-lib.sh
-get_clean_checkout () {
- git reset --hard &&
- git clean -fdqx &&
- git checkout "$1"
-}
-
#
# L1 L2
# o---o
@@ -21,51 +15,66 @@ get_clean_checkout () {
#
test_expect_success 'setup basic criss-cross + rename with no modifications' '
- ten="0 1 2 3 4 5 6 7 8 9" &&
- for i in $ten
- do
- echo line $i in a sample file
- done >one &&
- for i in $ten
- do
- echo line $i in another sample file
- done >two &&
- git add one two &&
- test_tick && git commit -m initial &&
-
- git branch L1 &&
- git checkout -b R1 &&
- git mv one three &&
- test_tick && git commit -m R1 &&
-
- git checkout L1 &&
- git mv two three &&
- test_tick && git commit -m L1 &&
-
- git checkout L1^0 &&
- test_tick && git merge -s ours R1 &&
- git tag L2 &&
-
- git checkout R1^0 &&
- test_tick && git merge -s ours L1 &&
- git tag R2
+ test_create_repo basic-rename &&
+ (
+ cd basic-rename &&
+
+ ten="0 1 2 3 4 5 6 7 8 9" &&
+ for i in $ten
+ do
+ echo line $i in a sample file
+ done >one &&
+ for i in $ten
+ do
+ echo line $i in another sample file
+ done >two &&
+ git add one two &&
+ test_tick && git commit -m initial &&
+
+ git branch L1 &&
+ git checkout -b R1 &&
+ git mv one three &&
+ test_tick && git commit -m R1 &&
+
+ git checkout L1 &&
+ git mv two three &&
+ test_tick && git commit -m L1 &&
+
+ git checkout L1^0 &&
+ test_tick && git merge -s ours R1 &&
+ git tag L2 &&
+
+ git checkout R1^0 &&
+ test_tick && git merge -s ours L1 &&
+ git tag R2
+ )
'
test_expect_success 'merge simple rename+criss-cross with no modifications' '
- git reset --hard &&
- git checkout L2^0 &&
-
- test_must_fail git merge -s recursive R2^0 &&
-
- test 2 = $(git ls-files -s | wc -l) &&
- test 2 = $(git ls-files -u | wc -l) &&
- test 2 = $(git ls-files -o | wc -l) &&
-
- test $(git rev-parse :2:three) = $(git rev-parse L2:three) &&
- test $(git rev-parse :3:three) = $(git rev-parse R2:three) &&
-
- test $(git rev-parse L2:three) = $(git hash-object three~HEAD) &&
- test $(git rev-parse R2:three) = $(git hash-object three~R2^0)
+ (
+ cd basic-rename &&
+
+ git reset --hard &&
+ git checkout L2^0 &&
+
+ test_must_fail git merge -s recursive R2^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 = 3 out &&
+
+ git rev-parse >expect \
+ L2:three R2:three \
+ L2:three R2:three &&
+ git rev-parse >actual \
+ :2:three :3:three &&
+ git hash-object >>actual \
+ three~HEAD three~R2^0 &&
+ test_cmp expect actual
+ )
'
#
@@ -81,58 +90,67 @@ test_expect_success 'merge simple rename+criss-cross with no modifications' '
#
test_expect_success 'setup criss-cross + rename merges with basic modification' '
- git rm -rf . &&
- git clean -fdqx &&
- rm -rf .git &&
- git init &&
-
- ten="0 1 2 3 4 5 6 7 8 9" &&
- for i in $ten
- do
- echo line $i in a sample file
- done >one &&
- for i in $ten
- do
- echo line $i in another sample file
- done >two &&
- git add one two &&
- test_tick && git commit -m initial &&
-
- git branch L1 &&
- git checkout -b R1 &&
- git mv one three &&
- echo more >>two &&
- git add two &&
- test_tick && git commit -m R1 &&
-
- git checkout L1 &&
- git mv two three &&
- test_tick && git commit -m L1 &&
-
- git checkout L1^0 &&
- test_tick && git merge -s ours R1 &&
- git tag L2 &&
-
- git checkout R1^0 &&
- test_tick && git merge -s ours L1 &&
- git tag R2
+ test_create_repo rename-modify &&
+ (
+ cd rename-modify &&
+
+ ten="0 1 2 3 4 5 6 7 8 9" &&
+ for i in $ten
+ do
+ echo line $i in a sample file
+ done >one &&
+ for i in $ten
+ do
+ echo line $i in another sample file
+ done >two &&
+ git add one two &&
+ test_tick && git commit -m initial &&
+
+ git branch L1 &&
+ git checkout -b R1 &&
+ git mv one three &&
+ echo more >>two &&
+ git add two &&
+ test_tick && git commit -m R1 &&
+
+ git checkout L1 &&
+ git mv two three &&
+ test_tick && git commit -m L1 &&
+
+ git checkout L1^0 &&
+ test_tick && git merge -s ours R1 &&
+ git tag L2 &&
+
+ git checkout R1^0 &&
+ test_tick && git merge -s ours L1 &&
+ git tag R2
+ )
'
test_expect_success 'merge criss-cross + rename merges with basic modification' '
- git reset --hard &&
- git checkout L2^0 &&
-
- test_must_fail git merge -s recursive R2^0 &&
-
- test 2 = $(git ls-files -s | wc -l) &&
- test 2 = $(git ls-files -u | wc -l) &&
- test 2 = $(git ls-files -o | wc -l) &&
-
- test $(git rev-parse :2:three) = $(git rev-parse L2:three) &&
- test $(git rev-parse :3:three) = $(git rev-parse R2:three) &&
-
- test $(git rev-parse L2:three) = $(git hash-object three~HEAD) &&
- test $(git rev-parse R2:three) = $(git hash-object three~R2^0)
+ (
+ cd rename-modify &&
+
+ git checkout L2^0 &&
+
+ test_must_fail git merge -s recursive R2^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 = 3 out &&
+
+ git rev-parse >expect \
+ L2:three R2:three \
+ L2:three R2:three &&
+ git rev-parse >actual \
+ :2:three :3:three &&
+ git hash-object >>actual \
+ three~HEAD three~R2^0 &&
+ test_cmp expect actual
+ )
'
#
@@ -156,64 +174,74 @@ test_expect_success 'merge criss-cross + rename merges with basic modification'
#
test_expect_success 'setup differently handled merges of rename/add conflict' '
- git rm -rf . &&
- git clean -fdqx &&
- rm -rf .git &&
- git init &&
-
- printf "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n" >a &&
- git add a &&
- test_tick && git commit -m A &&
-
- git branch B &&
- git checkout -b C &&
- echo 10 >>a &&
- echo "other content" >>new_a &&
- git add a new_a &&
- test_tick && git commit -m C &&
-
- git checkout B &&
- git mv a new_a &&
- test_tick && git commit -m B &&
-
- git checkout B^0 &&
- test_must_fail git merge C &&
- git clean -f &&
- test_tick && git commit -m D &&
- git tag D &&
-
- git checkout C^0 &&
- test_must_fail git merge B &&
- rm new_a~HEAD new_a &&
- printf "Incorrectly merged content" >>new_a &&
- git add -u &&
- test_tick && git commit -m E &&
- git tag E
+ test_create_repo rename-add &&
+ (
+ cd rename-add &&
+
+ printf "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n" >a &&
+ git add a &&
+ test_tick && git commit -m A &&
+
+ git branch B &&
+ git checkout -b C &&
+ echo 10 >>a &&
+ echo "other content" >>new_a &&
+ git add a new_a &&
+ test_tick && git commit -m C &&
+
+ git checkout B &&
+ git mv a new_a &&
+ test_tick && git commit -m B &&
+
+ git checkout B^0 &&
+ test_must_fail git merge C &&
+ git clean -f &&
+ test_tick && git commit -m D &&
+ git tag D &&
+
+ git checkout C^0 &&
+ test_must_fail git merge B &&
+ rm new_a~HEAD new_a &&
+ printf "Incorrectly merged content" >>new_a &&
+ git add -u &&
+ test_tick && git commit -m E &&
+ git tag E
+ )
'
test_expect_success 'git detects differently handled merges conflict' '
- git reset --hard &&
- git checkout D^0 &&
-
- test_must_fail git merge -s recursive E^0 &&
-
- test 3 = $(git ls-files -s | wc -l) &&
- test 3 = $(git ls-files -u | wc -l) &&
- test 0 = $(git ls-files -o | wc -l) &&
-
- test $(git rev-parse :2:new_a) = $(git rev-parse D:new_a) &&
- test $(git rev-parse :3:new_a) = $(git rev-parse E:new_a) &&
-
- git cat-file -p B:new_a >>merged &&
- git cat-file -p C:new_a >>merge-me &&
- >empty &&
- test_must_fail git merge-file \
- -L "Temporary merge branch 2" \
- -L "" \
- -L "Temporary merge branch 1" \
- merged empty merge-me &&
- sed -e "s/^\([<=>]\)/\1\1\1/" merged >merged-internal &&
- test $(git rev-parse :1:new_a) = $(git hash-object merged-internal)
+ (
+ cd rename-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 = 3 out &&
+ git ls-files -o >out &&
+ test_line_count = 1 out &&
+
+ git rev-parse >expect \
+ D:new_a E:new_a &&
+ git rev-parse >actual \
+ :2:new_a :3:new_a &&
+ test_cmp expect actual &&
+
+ git cat-file -p B:new_a >ours &&
+ git cat-file -p C:new_a >theirs &&
+ >empty &&
+ test_must_fail git merge-file \
+ -L "Temporary merge branch 2" \
+ -L "" \
+ -L "Temporary merge branch 1" \
+ ours empty theirs &&
+ sed -e "s/^\([<=>]\)/\1\1\1/" ours >expect &&
+ git cat-file -p :1:new_a >actual &&
+ test_cmp expect actual
+ )
'
#
@@ -236,220 +264,428 @@ test_expect_success 'git detects differently handled merges conflict' '
# Merging commits D & E should result in modify/delete conflict.
test_expect_success 'setup criss-cross + modify/delete resolved differently' '
- git rm -rf . &&
- git clean -fdqx &&
- rm -rf .git &&
- git init &&
-
- echo A >file &&
- git add file &&
- test_tick &&
- git commit -m A &&
-
- git branch B &&
- git checkout -b C &&
- git rm file &&
- test_tick &&
- git commit -m C &&
-
- git checkout B &&
- echo B >file &&
- git add file &&
- test_tick &&
- git commit -m B &&
-
- git checkout B^0 &&
- test_must_fail git merge C &&
- echo B >file &&
- git add file &&
- test_tick &&
- git commit -m D &&
- git tag D &&
-
- git checkout C^0 &&
- test_must_fail git merge B &&
- git rm file &&
- test_tick &&
- git commit -m E &&
- git tag E
+ test_create_repo modify-delete &&
+ (
+ cd modify-delete &&
+
+ echo A >file &&
+ git add file &&
+ test_tick &&
+ git commit -m A &&
+
+ git branch B &&
+ git checkout -b C &&
+ git rm file &&
+ test_tick &&
+ git commit -m C &&
+
+ git checkout B &&
+ echo B >file &&
+ git add file &&
+ test_tick &&
+ git commit -m B &&
+
+ git checkout B^0 &&
+ test_must_fail git merge C &&
+ echo B >file &&
+ git add file &&
+ test_tick &&
+ git commit -m D &&
+ git tag D &&
+
+ git checkout C^0 &&
+ test_must_fail git merge B &&
+ git rm file &&
+ test_tick &&
+ git commit -m E &&
+ git tag E
+ )
'
test_expect_success 'git detects conflict merging criss-cross+modify/delete' '
- git checkout D^0 &&
+ (
+ cd modify-delete &&
- test_must_fail git merge -s recursive E^0 &&
+ git checkout D^0 &&
- test 2 -eq $(git ls-files -s | wc -l) &&
- test 2 -eq $(git ls-files -u | wc -l) &&
+ test_must_fail git merge -s recursive E^0 &&
- test $(git rev-parse :1:file) = $(git rev-parse master:file) &&
- test $(git rev-parse :2:file) = $(git rev-parse B:file)
+ git ls-files -s >out &&
+ test_line_count = 2 out &&
+ git ls-files -u >out &&
+ test_line_count = 2 out &&
+
+ git rev-parse >expect \
+ master:file B:file &&
+ git rev-parse >actual \
+ :1:file :2:file &&
+ test_cmp expect actual
+ )
'
test_expect_success 'git detects conflict merging criss-cross+modify/delete, reverse direction' '
- git reset --hard &&
- git checkout E^0 &&
+ (
+ cd modify-delete &&
+
+ git reset --hard &&
+ git checkout E^0 &&
- test_must_fail git merge -s recursive D^0 &&
+ test_must_fail git merge -s recursive D^0 &&
- test 2 -eq $(git ls-files -s | wc -l) &&
- test 2 -eq $(git ls-files -u | wc -l) &&
+ git ls-files -s >out &&
+ test_line_count = 2 out &&
+ git ls-files -u >out &&
+ test_line_count = 2 out &&
- test $(git rev-parse :1:file) = $(git rev-parse master:file) &&
- test $(git rev-parse :3:file) = $(git rev-parse B:file)
+ git rev-parse >expect \
+ master:file B:file &&
+ git rev-parse >actual \
+ :1:file :3:file &&
+ test_cmp expect actual
+ )
'
+# 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.
#
-# 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 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.
#
-# Merge of D & E2 has similar issues for path 'a', but should always result
-# in a modify/delete conflict for path 'a/file'.
+# 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
#
-# We run each merge in both directions, to check for directional issues
-# with D/F conflict handling.
+# 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.
+#
+#
+# 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' '
- git rm -rf . &&
- git clean -fdqx &&
- rm -rf .git &&
- git init &&
-
- >ignore-me &&
- git add ignore-me &&
- test_tick &&
- git commit -m A &&
- git tag A &&
-
- git branch B &&
- git checkout -b C &&
- mkdir a &&
- echo 10 >a/file &&
- git add a/file &&
- test_tick &&
- git commit -m C &&
-
- git checkout B &&
- echo 5 >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 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 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 add a/file &&
- test_tick &&
- git commit -m E2 &&
- git tag E2
+ test_create_repo directory-file &&
+ (
+ cd directory-file &&
+
+ >ignore-me &&
+ git add ignore-me &&
+ test_tick &&
+ git commit -m A &&
+ git tag A &&
+
+ git branch B &&
+ git checkout -b C &&
+ mkdir a &&
+ 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 &&
+ test_write_lines 1 2 3 4 5 6 7 >a &&
+ git add a &&
+ test_tick &&
+ git commit -m B &&
+
+ git checkout B^0 &&
+ 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 &&
+ git merge -s ours -m E1 B^0 &&
+ git tag E1 &&
+
+ git checkout C^0 &&
+ 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 &&
+ 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' '
- get_clean_checkout D^0 &&
+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 D1^0 &&
+
+ test_must_fail git merge -s recursive E1^0 &&
+
+ git ls-files -s >out &&
+ test_line_count = 2 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 B:a &&
+ git rev-parse >actual \
+ :0:ignore-me :2:a &&
+ test_cmp expect actual
+ )
+'
- test_must_fail git merge -s recursive E1^0 &&
+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" &&
+ (
+ cd directory-file &&
+
+ git checkout E1^0 &&
+
+ test_must_fail git merge -s recursive D1^0 &&
+
+ git ls-files -s >out &&
+ test_line_count = 2 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 B:a &&
+ git rev-parse >actual \
+ :0:ignore-me :3:a &&
+ test_cmp expect actual
+ )
+'
- test 2 -eq $(git ls-files -s | wc -l) &&
- test 1 -eq $(git ls-files -u | wc -l) &&
- test 0 -eq $(git ls-files -o | wc -l) &&
+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 &&
- test $(git rev-parse :0:ignore-me) = $(git rev-parse A:ignore-me) &&
- test $(git rev-parse :2:a) = $(git rev-parse B:a)
-'
+ git checkout D1^0 &&
-test_expect_success 'merge of E1 & D fails but has appropriate contents' '
- get_clean_checkout E1^0 &&
+ test_must_fail git merge -s recursive E2^0 &&
- test_must_fail git merge -s recursive D^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 = 2 out &&
- test 2 -eq $(git ls-files -s | wc -l) &&
- test 1 -eq $(git ls-files -u | wc -l) &&
- test 0 -eq $(git ls-files -o | wc -l) &&
+ git rev-parse >expect \
+ 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 $(git rev-parse :0:ignore-me) = $(git rev-parse A:ignore-me) &&
- test $(git rev-parse :3:a) = $(git rev-parse B:a)
+ test_path_is_file a~HEAD
+ )
'
-test_expect_success 'merge of D & E2 fails but has appropriate contents' '
- get_clean_checkout D^0 &&
+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" &&
+ (
+ cd directory-file &&
- test_must_fail git merge -s recursive E2^0 &&
+ git checkout E2^0 &&
- test 4 -eq $(git ls-files -s | wc -l) &&
- test 3 -eq $(git ls-files -u | wc -l) &&
- test 1 -eq $(git ls-files -o | wc -l) &&
+ test_must_fail git merge -s recursive D1^0 &&
- test $(git rev-parse :2:a) = $(git rev-parse B:a) &&
- test $(git rev-parse :3:a/file) = $(git rev-parse E2:a/file) &&
- test $(git rev-parse :1:a/file) = $(git rev-parse C:a/file) &&
- test $(git rev-parse :0:ignore-me) = $(git rev-parse A:ignore-me) &&
+ 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 = 2 out &&
- test -f a~HEAD
-'
-
-test_expect_success 'merge of E2 & D fails but has appropriate contents' '
- get_clean_checkout E2^0 &&
+ git rev-parse >expect \
+ 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_must_fail git merge -s recursive D^0 &&
+ test_path_is_file a~D1^0
+ )
+'
- test 4 -eq $(git ls-files -s | wc -l) &&
- test 3 -eq $(git ls-files -u | wc -l) &&
- test 1 -eq $(git ls-files -o | wc -l) &&
+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 $(git rev-parse :3:a) = $(git rev-parse B:a) &&
- test $(git rev-parse :2:a/file) = $(git rev-parse E2:a/file) &&
- test $(git rev-parse :1:a/file) = $(git rev-parse C:a/file) &&
- test $(git rev-parse :0:ignore-me) = $(git rev-parse A:ignore-me) &&
+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 -f a~D^0
+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
+ )
'
#
@@ -492,52 +728,58 @@ test_expect_success 'merge of E2 & D fails but has appropriate contents' '
# but that may cancel out at the final merge stage".
test_expect_success 'setup rename/rename(1to2)/modify followed by what looks like rename/rename(2to1)/modify' '
- git reset --hard &&
- git rm -rf . &&
- git clean -fdqx &&
- rm -rf .git &&
- git init &&
-
- printf "1\n2\n3\n4\n5\n6\n" >a &&
- git add a &&
- git commit -m A &&
- git tag A &&
-
- git checkout -b B A &&
- git mv a b &&
- echo 7 >>b &&
- git add -u &&
- git commit -m B &&
-
- git checkout -b C A &&
- git mv a c &&
- git commit -m C &&
-
- git checkout -q B^0 &&
- git merge --no-commit -s ours C^0 &&
- git mv b newname &&
- git commit -m "Merge commit C^0 into HEAD" &&
- git tag D &&
-
- git checkout -q C^0 &&
- git merge --no-commit -s ours B^0 &&
- git mv c newname &&
- printf "7\n8\n" >>newname &&
- git add -u &&
- git commit -m "Merge commit B^0 into HEAD" &&
- git tag E
+ test_create_repo rename-squared-squared &&
+ (
+ cd rename-squared-squared &&
+
+ printf "1\n2\n3\n4\n5\n6\n" >a &&
+ git add a &&
+ git commit -m A &&
+ git tag A &&
+
+ git checkout -b B A &&
+ git mv a b &&
+ echo 7 >>b &&
+ git add -u &&
+ git commit -m B &&
+
+ git checkout -b C A &&
+ git mv a c &&
+ git commit -m C &&
+
+ git checkout -q B^0 &&
+ git merge --no-commit -s ours C^0 &&
+ git mv b newname &&
+ git commit -m "Merge commit C^0 into HEAD" &&
+ git tag D &&
+
+ git checkout -q C^0 &&
+ git merge --no-commit -s ours B^0 &&
+ git mv c newname &&
+ printf "7\n8\n" >>newname &&
+ git add -u &&
+ git commit -m "Merge commit B^0 into HEAD" &&
+ git tag E
+ )
'
test_expect_success 'handle rename/rename(1to2)/modify followed by what looks like rename/rename(2to1)/modify' '
- git checkout D^0 &&
+ (
+ cd rename-squared-squared &&
+
+ git checkout D^0 &&
- git merge -s recursive E^0 &&
+ git merge -s recursive E^0 &&
- test 1 -eq $(git ls-files -s | wc -l) &&
- test 0 -eq $(git ls-files -u | wc -l) &&
- test 0 -eq $(git ls-files -o | wc -l) &&
+ git ls-files -s >out &&
+ test_line_count = 1 out &&
+ git ls-files -u >out &&
+ test_line_count = 0 out &&
+ git ls-files -o >out &&
+ test_line_count = 1 out &&
- test $(git rev-parse HEAD:newname) = $(git rev-parse E:newname)
+ test $(git rev-parse HEAD:newname) = $(git rev-parse E:newname)
+ )
'
#
@@ -562,59 +804,72 @@ test_expect_success 'handle rename/rename(1to2)/modify followed by what looks li
# renaming carefully (both in the virtual merge base and later), and getting
# content merge handled.
-test_expect_success 'setup criss-cross + rename/rename/add + modify/modify' '
- git rm -rf . &&
- git clean -fdqx &&
- rm -rf .git &&
- git init &&
-
- printf "lots\nof\nwords\nand\ncontent\n" >a &&
- git add a &&
- git commit -m A &&
- git tag A &&
-
- git checkout -b B A &&
- git mv a b &&
- git commit -m B &&
-
- git checkout -b C A &&
- git mv a c &&
- printf "2\n3\n4\n5\n6\n7\n" >a &&
- git add a &&
- git commit -m C &&
-
- git checkout B^0 &&
- git merge --no-commit -s ours C^0 &&
- git checkout C -- a c &&
- mv a old_a &&
- echo 1 >a &&
- cat old_a >>a &&
- rm old_a &&
- git add -u &&
- git commit -m "Merge commit C^0 into HEAD" &&
- git tag D &&
-
- git checkout C^0 &&
- git merge --no-commit -s ours B^0 &&
- git checkout B -- b &&
- echo 8 >>a &&
- git add -u &&
- git commit -m "Merge commit B^0 into HEAD" &&
- git tag E
+test_expect_success 'setup criss-cross + rename/rename/add-source + modify/modify' '
+ test_create_repo rename-rename-add-source &&
+ (
+ cd rename-rename-add-source &&
+
+ printf "lots\nof\nwords\nand\ncontent\n" >a &&
+ git add a &&
+ git commit -m A &&
+ git tag A &&
+
+ git checkout -b B A &&
+ git mv a b &&
+ git commit -m B &&
+
+ git checkout -b C A &&
+ git mv a c &&
+ printf "2\n3\n4\n5\n6\n7\n" >a &&
+ git add a &&
+ git commit -m C &&
+
+ git checkout B^0 &&
+ git merge --no-commit -s ours C^0 &&
+ git checkout C -- a c &&
+ mv a old_a &&
+ echo 1 >a &&
+ cat old_a >>a &&
+ rm old_a &&
+ git add -u &&
+ git commit -m "Merge commit C^0 into HEAD" &&
+ git tag D &&
+
+ git checkout C^0 &&
+ git merge --no-commit -s ours B^0 &&
+ git checkout B -- b &&
+ echo 8 >>a &&
+ git add -u &&
+ git commit -m "Merge commit B^0 into HEAD" &&
+ git tag E
+ )
'
test_expect_failure 'detect rename/rename/add-source for virtual merge-base' '
- git checkout D^0 &&
-
- git merge -s recursive E^0 &&
-
- test 3 -eq $(git ls-files -s | wc -l) &&
- test 0 -eq $(git ls-files -u | wc -l) &&
- test 0 -eq $(git ls-files -o | wc -l) &&
-
- test $(git rev-parse HEAD:b) = $(git rev-parse A:a) &&
- test $(git rev-parse HEAD:c) = $(git rev-parse A:a) &&
- test "$(cat a)" = "$(printf "1\n2\n3\n4\n5\n6\n7\n8\n")"
+ (
+ cd rename-rename-add-source &&
+
+ git checkout D^0 &&
+
+ git merge -s recursive E^0 &&
+
+ git ls-files -s >out &&
+ test_line_count = 3 out &&
+ git ls-files -u >out &&
+ test_line_count = 0 out &&
+ git ls-files -o >out &&
+ test_line_count = 1 out &&
+
+ printf "1\n2\n3\n4\n5\n6\n7\n8\n" >correct &&
+ git rev-parse >expect \
+ A:a A:a \
+ correct &&
+ git rev-parse >actual \
+ :0:b :0:c &&
+ git hash-object >>actual \
+ a &&
+ test_cmp expect actual
+ )
'
#
@@ -638,52 +893,513 @@ test_expect_failure 'detect rename/rename/add-source for virtual merge-base' '
# base of B & C needs to not delete B:c for that to work, though...
test_expect_success 'setup criss-cross+rename/rename/add-dest + simple modify' '
- git rm -rf . &&
- git clean -fdqx &&
- rm -rf .git &&
- git init &&
-
- >a &&
- git add a &&
- git commit -m A &&
- git tag A &&
-
- git checkout -b B A &&
- git mv a b &&
- printf "1\n2\n3\n4\n5\n6\n7\n" >c &&
- git add c &&
- git commit -m B &&
-
- git checkout -b C A &&
- git mv a c &&
- git commit -m C &&
-
- git checkout B^0 &&
- git merge --no-commit -s ours C^0 &&
- git mv b a &&
- git commit -m "D is like B but renames b back to a" &&
- git tag D &&
-
- git checkout B^0 &&
- git merge --no-commit -s ours C^0 &&
- git mv b a &&
- echo 8 >>c &&
- git add c &&
- git commit -m "E like D but has mod in c" &&
- git tag E
+ test_create_repo rename-rename-add-dest &&
+ (
+ cd rename-rename-add-dest &&
+
+ >a &&
+ git add a &&
+ git commit -m A &&
+ git tag A &&
+
+ git checkout -b B A &&
+ git mv a b &&
+ printf "1\n2\n3\n4\n5\n6\n7\n" >c &&
+ git add c &&
+ git commit -m B &&
+
+ git checkout -b C A &&
+ git mv a c &&
+ git commit -m C &&
+
+ git checkout B^0 &&
+ git merge --no-commit -s ours C^0 &&
+ git mv b a &&
+ git commit -m "D is like B but renames b back to a" &&
+ git tag D &&
+
+ git checkout B^0 &&
+ git merge --no-commit -s ours C^0 &&
+ git mv b a &&
+ echo 8 >>c &&
+ git add c &&
+ git commit -m "E like D but has mod in c" &&
+ git tag E
+ )
'
test_expect_success 'virtual merge base handles rename/rename(1to2)/add-dest' '
- git checkout D^0 &&
+ (
+ cd rename-rename-add-dest &&
+
+ git checkout D^0 &&
+
+ git merge -s recursive E^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:a E:c &&
+ git rev-parse >actual \
+ :0:a :0:c &&
+ test_cmp expect actual
+ )
+'
+
+#
+# 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 merge -s recursive E^0 &&
+ git checkout D^0 &&
- test 2 -eq $(git ls-files -s | wc -l) &&
- test 0 -eq $(git ls-files -u | wc -l) &&
- test 0 -eq $(git ls-files -o | wc -l) &&
+ test_must_fail git merge -s recursive E^0 &&
- test $(git rev-parse HEAD:a) = $(git rev-parse A:a) &&
- test $(git rev-parse HEAD:c) = $(git rev-parse E:c)
+ 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 411550d..b97aca7 100755
--- a/t/t6042-merge-rename-corner-cases.sh
+++ b/t/t6042-merge-rename-corner-cases.sh
@@ -6,31 +6,40 @@ test_description="recursive merge corner cases w/ renames but not criss-crosses"
. ./test-lib.sh
test_expect_success 'setup rename/delete + untracked file' '
- echo "A pretty inscription" >ring &&
- git add ring &&
- test_tick &&
- git commit -m beginning &&
-
- git branch people &&
- git checkout -b rename-the-ring &&
- git mv ring one-ring-to-rule-them-all &&
- test_tick &&
- git commit -m fullname &&
-
- git checkout people &&
- git rm ring &&
- echo gollum >owner &&
- git add owner &&
- test_tick &&
- git commit -m track-people-instead-of-objects &&
- echo "Myyy PRECIOUSSS" >ring
+ test_create_repo rename-delete-untracked &&
+ (
+ cd rename-delete-untracked &&
+
+ echo "A pretty inscription" >ring &&
+ git add ring &&
+ test_tick &&
+ git commit -m beginning &&
+
+ git branch people &&
+ git checkout -b rename-the-ring &&
+ git mv ring one-ring-to-rule-them-all &&
+ test_tick &&
+ git commit -m fullname &&
+
+ git checkout people &&
+ git rm ring &&
+ echo gollum >owner &&
+ git add owner &&
+ test_tick &&
+ git commit -m track-people-instead-of-objects &&
+ echo "Myyy PRECIOUSSS" >ring
+ )
'
test_expect_success "Does git preserve Gollum's precious artifact?" '
- test_must_fail git merge -s recursive rename-the-ring &&
+ (
+ cd rename-delete-untracked &&
- # Make sure git did not delete an untracked file
- test -f ring
+ test_must_fail git merge -s recursive rename-the-ring &&
+
+ # Make sure git did not delete an untracked file
+ test_path_is_file ring
+ )
'
# Testcase setup for rename/modify/add-source:
@@ -41,96 +50,125 @@ test_expect_success "Does git preserve Gollum's precious artifact?" '
# We should be able to merge B & C cleanly
test_expect_success 'setup rename/modify/add-source conflict' '
- git rm -rf . &&
- git clean -fdqx &&
- rm -rf .git &&
- git init &&
-
- printf "1\n2\n3\n4\n5\n6\n7\n" >a &&
- git add a &&
- git commit -m A &&
- git tag A &&
-
- git checkout -b B A &&
- echo 8 >>a &&
- git add a &&
- git commit -m B &&
-
- git checkout -b C A &&
- git mv a b &&
- echo something completely different >a &&
- git add a &&
- git commit -m C
+ test_create_repo rename-modify-add-source &&
+ (
+ cd rename-modify-add-source &&
+
+ printf "1\n2\n3\n4\n5\n6\n7\n" >a &&
+ git add a &&
+ git commit -m A &&
+ git tag A &&
+
+ git checkout -b B A &&
+ echo 8 >>a &&
+ git add a &&
+ git commit -m B &&
+
+ git checkout -b C A &&
+ git mv a b &&
+ echo something completely different >a &&
+ git add a &&
+ git commit -m C
+ )
'
test_expect_failure 'rename/modify/add-source conflict resolvable' '
- git checkout B^0 &&
+ (
+ cd rename-modify-add-source &&
- git merge -s recursive C^0 &&
+ git checkout B^0 &&
- test $(git rev-parse B:a) = $(git rev-parse b) &&
- test $(git rev-parse C:a) = $(git rev-parse a)
+ git merge -s recursive C^0 &&
+
+ git rev-parse >expect \
+ B:a C:a &&
+ git rev-parse >actual \
+ b c &&
+ test_cmp expect actual
+ )
'
test_expect_success 'setup resolvable conflict missed if rename missed' '
- git rm -rf . &&
- git clean -fdqx &&
- rm -rf .git &&
- git init &&
-
- printf "1\n2\n3\n4\n5\n" >a &&
- echo foo >b &&
- git add a b &&
- git commit -m A &&
- git tag A &&
-
- git checkout -b B A &&
- git mv a c &&
- echo "Completely different content" >a &&
- git add a &&
- git commit -m B &&
-
- git checkout -b C A &&
- echo 6 >>a &&
- git add a &&
- git commit -m C
+ test_create_repo break-detection-1 &&
+ (
+ cd break-detection-1 &&
+
+ printf "1\n2\n3\n4\n5\n" >a &&
+ echo foo >b &&
+ git add a b &&
+ git commit -m A &&
+ git tag A &&
+
+ git checkout -b B A &&
+ git mv a c &&
+ echo "Completely different content" >a &&
+ git add a &&
+ git commit -m B &&
+
+ git checkout -b C A &&
+ echo 6 >>a &&
+ git add a &&
+ git commit -m C
+ )
'
test_expect_failure 'conflict caused if rename not detected' '
- git checkout -q C^0 &&
- git merge -s recursive B^0 &&
-
- test 3 -eq $(git ls-files -s | wc -l) &&
- test 0 -eq $(git ls-files -u | wc -l) &&
- test 0 -eq $(git ls-files -o | wc -l) &&
-
- test_line_count = 6 c &&
- test $(git rev-parse HEAD:a) = $(git rev-parse B:a) &&
- test $(git rev-parse HEAD:b) = $(git rev-parse A:b)
+ (
+ cd break-detection-1 &&
+
+ git checkout -q C^0 &&
+ git merge -s recursive B^0 &&
+
+ git ls-files -s >out &&
+ test_line_count = 3 out &&
+ git ls-files -u >out &&
+ test_line_count = 0 out &&
+ git ls-files -o >out &&
+ test_line_count = 1 out &&
+
+ test_line_count = 6 c &&
+ git rev-parse >expect \
+ B:a A:b &&
+ git rev-parse >actual \
+ :0:a :0:b &&
+ test_cmp expect actual
+ )
'
test_expect_success 'setup conflict resolved wrong if rename missed' '
- git reset --hard &&
- git clean -f &&
-
- git checkout -b D A &&
- echo 7 >>a &&
- git add a &&
- git mv a c &&
- echo "Completely different content" >a &&
- git add a &&
- git commit -m D &&
-
- git checkout -b E A &&
- git rm a &&
- echo "Completely different content" >>a &&
- git add a &&
- git commit -m E
+ test_create_repo break-detection-2 &&
+ (
+ cd break-detection-2 &&
+
+ printf "1\n2\n3\n4\n5\n" >a &&
+ echo foo >b &&
+ git add a b &&
+ git commit -m A &&
+ git tag A &&
+
+ git checkout -b D A &&
+ echo 7 >>a &&
+ git add a &&
+ git mv a c &&
+ echo "Completely different content" >a &&
+ git add a &&
+ git commit -m D &&
+
+ git checkout -b E A &&
+ git rm a &&
+ echo "Completely different content" >>a &&
+ git add a &&
+ git commit -m E
+ )
'
test_expect_failure 'missed conflict if rename not detected' '
- git checkout -q E^0 &&
- test_must_fail git merge -s recursive D^0
+ (
+ cd break-detection-2 &&
+
+ git checkout -q E^0 &&
+ test_must_fail git merge -s recursive D^0
+ )
'
# Tests for undetected rename/add-source causing a file to erroneously be
@@ -145,198 +183,232 @@ test_expect_failure 'missed conflict if rename not detected' '
# Commit C: rename a->b, add unrelated a
test_expect_success 'setup undetected rename/add-source causes data loss' '
- git rm -rf . &&
- git clean -fdqx &&
- rm -rf .git &&
- git init &&
-
- printf "1\n2\n3\n4\n5\n" >a &&
- git add a &&
- git commit -m A &&
- git tag A &&
-
- git checkout -b B A &&
- git mv a b &&
- git commit -m B &&
-
- git checkout -b C A &&
- git mv a b &&
- echo foobar >a &&
- git add a &&
- git commit -m C
+ test_create_repo break-detection-3 &&
+ (
+ cd break-detection-3 &&
+
+ printf "1\n2\n3\n4\n5\n" >a &&
+ git add a &&
+ git commit -m A &&
+ git tag A &&
+
+ git checkout -b B A &&
+ git mv a b &&
+ git commit -m B &&
+
+ git checkout -b C A &&
+ git mv a b &&
+ echo foobar >a &&
+ git add a &&
+ git commit -m C
+ )
'
test_expect_failure 'detect rename/add-source and preserve all data' '
- git checkout B^0 &&
+ (
+ cd break-detection-3 &&
- git merge -s recursive C^0 &&
+ git checkout B^0 &&
- test 2 -eq $(git ls-files -s | wc -l) &&
- test 2 -eq $(git ls-files -u | wc -l) &&
- test 0 -eq $(git ls-files -o | wc -l) &&
+ git merge -s recursive C^0 &&
- test -f a &&
- test -f b &&
+ 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 &&
- test $(git rev-parse HEAD:b) = $(git rev-parse A:a) &&
- test $(git rev-parse HEAD:a) = $(git rev-parse C:a)
+ test_path_is_file a &&
+ test_path_is_file b &&
+
+ git rev-parse >expect \
+ A:a C:a &&
+ git rev-parse >actual \
+ :0:b :0:a &&
+ test_cmp expect actual
+ )
'
test_expect_failure 'detect rename/add-source and preserve all data, merge other way' '
- git checkout C^0 &&
+ (
+ cd break-detection-3 &&
+
+ git checkout C^0 &&
- git merge -s recursive B^0 &&
+ git merge -s recursive B^0 &&
- test 2 -eq $(git ls-files -s | wc -l) &&
- test 2 -eq $(git ls-files -u | wc -l) &&
- test 0 -eq $(git ls-files -o | wc -l) &&
+ 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 &&
- test -f a &&
- test -f b &&
+ test_path_is_file a &&
+ test_path_is_file b &&
- test $(git rev-parse HEAD:b) = $(git rev-parse A:a) &&
- test $(git rev-parse HEAD:a) = $(git rev-parse C:a)
+ git rev-parse >expect \
+ A:a C:a &&
+ git rev-parse >actual \
+ :0:b :0:a &&
+ test_cmp expect actual
+ )
'
test_expect_success 'setup content merge + rename/directory conflict' '
- git rm -rf . &&
- git clean -fdqx &&
- rm -rf .git &&
- git init &&
-
- printf "1\n2\n3\n4\n5\n6\n" >file &&
- git add file &&
- test_tick &&
- git commit -m base &&
- git tag base &&
-
- git checkout -b right &&
- echo 7 >>file &&
- mkdir newfile &&
- echo junk >newfile/realfile &&
- git add file newfile/realfile &&
- test_tick &&
- git commit -m right &&
-
- git checkout -b left-conflict base &&
- echo 8 >>file &&
- git add file &&
- git mv file newfile &&
- test_tick &&
- git commit -m left &&
-
- git checkout -b left-clean base &&
- echo 0 >newfile &&
- cat file >>newfile &&
- git add newfile &&
- git rm file &&
- test_tick &&
- git commit -m left
+ test_create_repo rename-directory-1 &&
+ (
+ cd rename-directory-1 &&
+
+ printf "1\n2\n3\n4\n5\n6\n" >file &&
+ git add file &&
+ test_tick &&
+ git commit -m base &&
+ git tag base &&
+
+ git checkout -b right &&
+ echo 7 >>file &&
+ mkdir newfile &&
+ echo junk >newfile/realfile &&
+ git add file newfile/realfile &&
+ test_tick &&
+ git commit -m right &&
+
+ git checkout -b left-conflict base &&
+ echo 8 >>file &&
+ git add file &&
+ git mv file newfile &&
+ test_tick &&
+ git commit -m left &&
+
+ git checkout -b left-clean base &&
+ echo 0 >newfile &&
+ cat file >>newfile &&
+ git add newfile &&
+ git rm file &&
+ test_tick &&
+ git commit -m left
+ )
'
test_expect_success 'rename/directory conflict + clean content merge' '
- git reset --hard &&
- git reset --hard &&
- git clean -fdqx &&
+ (
+ cd rename-directory-1 &&
- git checkout left-clean^0 &&
+ git checkout left-clean^0 &&
- test_must_fail git merge -s recursive right^0 &&
+ test_must_fail git merge -s recursive right^0 &&
- test 2 -eq $(git ls-files -s | wc -l) &&
- test 1 -eq $(git ls-files -u | wc -l) &&
- test 1 -eq $(git ls-files -o | wc -l) &&
+ git ls-files -s >out &&
+ test_line_count = 2 out &&
+ git ls-files -u >out &&
+ test_line_count = 1 out &&
+ git ls-files -o >out &&
+ test_line_count = 2 out &&
- echo 0 >expect &&
- git cat-file -p base:file >>expect &&
- echo 7 >>expect &&
- test_cmp expect newfile~HEAD &&
+ echo 0 >expect &&
+ git cat-file -p base:file >>expect &&
+ echo 7 >>expect &&
+ test_cmp expect newfile~HEAD &&
- test $(git rev-parse :2:newfile) = $(git hash-object expect) &&
+ test $(git rev-parse :2:newfile) = $(git hash-object expect) &&
- test -f newfile/realfile &&
- test -f newfile~HEAD
+ test_path_is_file newfile/realfile &&
+ test_path_is_file newfile~HEAD
+ )
'
test_expect_success 'rename/directory conflict + content merge conflict' '
- git reset --hard &&
- git reset --hard &&
- git clean -fdqx &&
-
- git checkout left-conflict^0 &&
-
- test_must_fail git merge -s recursive right^0 &&
-
- test 4 -eq $(git ls-files -s | wc -l) &&
- test 3 -eq $(git ls-files -u | wc -l) &&
- test 1 -eq $(git ls-files -o | wc -l) &&
-
- git cat-file -p left-conflict:newfile >left &&
- git cat-file -p base:file >base &&
- git cat-file -p right:file >right &&
- test_must_fail git merge-file \
- -L "HEAD:newfile" \
- -L "" \
- -L "right^0:file" \
- left base right &&
- test_cmp left newfile~HEAD &&
-
- test $(git rev-parse :1:newfile) = $(git rev-parse base:file) &&
- test $(git rev-parse :2:newfile) = $(git rev-parse left-conflict:newfile) &&
- test $(git rev-parse :3:newfile) = $(git rev-parse right:file) &&
-
- test -f newfile/realfile &&
- test -f newfile~HEAD
+ (
+ cd rename-directory-1 &&
+
+ git reset --hard &&
+ git clean -fdqx &&
+
+ git checkout left-conflict^0 &&
+
+ test_must_fail git merge -s recursive right^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 = 2 out &&
+
+ git cat-file -p left-conflict:newfile >left &&
+ git cat-file -p base:file >base &&
+ git cat-file -p right:file >right &&
+ test_must_fail git merge-file \
+ -L "HEAD:newfile" \
+ -L "" \
+ -L "right^0:file" \
+ left base right &&
+ test_cmp left newfile~HEAD &&
+
+ git rev-parse >expect \
+ base:file left-conflict:newfile right:file &&
+ git rev-parse >actual \
+ :1:newfile :2:newfile :3:newfile &&
+ test_cmp expect actual &&
+
+ test_path_is_file newfile/realfile &&
+ test_path_is_file newfile~HEAD
+ )
'
test_expect_success 'setup content merge + rename/directory conflict w/ disappearing dir' '
- git reset --hard &&
- git rm -rf . &&
- git clean -fdqx &&
- rm -rf .git &&
- git init &&
-
- mkdir sub &&
- printf "1\n2\n3\n4\n5\n6\n" >sub/file &&
- git add sub/file &&
- test_tick &&
- git commit -m base &&
- git tag base &&
-
- git checkout -b right &&
- echo 7 >>sub/file &&
- git add sub/file &&
- test_tick &&
- git commit -m right &&
-
- git checkout -b left base &&
- echo 0 >newfile &&
- cat sub/file >>newfile &&
- git rm sub/file &&
- mv newfile sub &&
- git add sub &&
- test_tick &&
- git commit -m left
+ test_create_repo rename-directory-2 &&
+ (
+ cd rename-directory-2 &&
+
+ mkdir sub &&
+ printf "1\n2\n3\n4\n5\n6\n" >sub/file &&
+ git add sub/file &&
+ test_tick &&
+ git commit -m base &&
+ git tag base &&
+
+ git checkout -b right &&
+ echo 7 >>sub/file &&
+ git add sub/file &&
+ test_tick &&
+ git commit -m right &&
+
+ git checkout -b left base &&
+ echo 0 >newfile &&
+ cat sub/file >>newfile &&
+ git rm sub/file &&
+ mv newfile sub &&
+ git add sub &&
+ test_tick &&
+ git commit -m left
+ )
'
test_expect_success 'disappearing dir in rename/directory conflict handled' '
- git reset --hard &&
- git clean -fdqx &&
+ (
+ cd rename-directory-2 &&
- git checkout left^0 &&
+ git checkout left^0 &&
- git merge -s recursive right^0 &&
+ git merge -s recursive right^0 &&
- test 1 -eq $(git ls-files -s | wc -l) &&
- test 0 -eq $(git ls-files -u | wc -l) &&
- test 0 -eq $(git ls-files -o | wc -l) &&
+ git ls-files -s >out &&
+ test_line_count = 1 out &&
+ git ls-files -u >out &&
+ test_line_count = 0 out &&
+ git ls-files -o >out &&
+ test_line_count = 1 out &&
- echo 0 >expect &&
- git cat-file -p base:sub/file >>expect &&
- echo 7 >>expect &&
- test_cmp expect sub &&
+ echo 0 >expect &&
+ git cat-file -p base:sub/file >>expect &&
+ echo 7 >>expect &&
+ test_cmp expect sub &&
- test -f sub
+ test_path_is_file sub
+ )
'
# Test for all kinds of things that can go wrong with rename/rename (2to1):
@@ -352,48 +424,59 @@ test_expect_success 'disappearing dir in rename/directory conflict handled' '
# * Nothing else should be present. Is anything?
test_expect_success 'setup rename/rename (2to1) + modify/modify' '
- git rm -rf . &&
- git clean -fdqx &&
- rm -rf .git &&
- git init &&
-
- printf "1\n2\n3\n4\n5\n" >a &&
- printf "5\n4\n3\n2\n1\n" >b &&
- git add a b &&
- git commit -m A &&
- git tag A &&
-
- git checkout -b B A &&
- git mv a c &&
- echo 0 >>b &&
- git add b &&
- git commit -m B &&
-
- git checkout -b C A &&
- git mv b c &&
- echo 6 >>a &&
- git add a &&
- git commit -m C
+ test_create_repo rename-rename-2to1 &&
+ (
+ cd rename-rename-2to1 &&
+
+ printf "1\n2\n3\n4\n5\n" >a &&
+ printf "5\n4\n3\n2\n1\n" >b &&
+ git add a b &&
+ git commit -m A &&
+ git tag A &&
+
+ git checkout -b B A &&
+ git mv a c &&
+ echo 0 >>b &&
+ git add b &&
+ git commit -m B &&
+
+ git checkout -b C A &&
+ git mv b c &&
+ echo 6 >>a &&
+ git add a &&
+ git commit -m C
+ )
'
test_expect_success 'handle rename/rename (2to1) conflict correctly' '
- git checkout B^0 &&
-
- test_must_fail git merge -s recursive C^0 >out &&
- test_i18ngrep "CONFLICT (rename/rename)" out &&
-
- test 2 -eq $(git ls-files -s | wc -l) &&
- test 2 -eq $(git ls-files -u | wc -l) &&
- test 2 -eq $(git ls-files -u c | wc -l) &&
- test 3 -eq $(git ls-files -o | wc -l) &&
-
- test ! -f a &&
- test ! -f b &&
- test -f c~HEAD &&
- test -f c~C^0 &&
-
- test $(git hash-object c~HEAD) = $(git rev-parse C:a) &&
- test $(git hash-object c~C^0) = $(git rev-parse B:b)
+ (
+ cd rename-rename-2to1 &&
+
+ git checkout B^0 &&
+
+ test_must_fail git merge -s recursive C^0 >out &&
+ test_i18ngrep "CONFLICT (rename/rename)" out &&
+
+ git ls-files -s >out &&
+ test_line_count = 2 out &&
+ git ls-files -u >out &&
+ test_line_count = 2 out &&
+ git ls-files -u c >out &&
+ test_line_count = 2 out &&
+ git ls-files -o >out &&
+ test_line_count = 3 out &&
+
+ test_path_is_missing a &&
+ test_path_is_missing b &&
+ test_path_is_file c~HEAD &&
+ test_path_is_file c~C^0 &&
+
+ git rev-parse >expect \
+ C:a B:b &&
+ git hash-object >actual \
+ c~HEAD c~C^0 &&
+ test_cmp expect actual
+ )
'
# Testcase setup for simple rename/rename (1to2) conflict:
@@ -401,44 +484,53 @@ test_expect_success 'handle rename/rename (2to1) conflict correctly' '
# Commit B: rename a->b
# Commit C: rename a->c
test_expect_success 'setup simple rename/rename (1to2) conflict' '
- git rm -rf . &&
- git clean -fdqx &&
- rm -rf .git &&
- git init &&
-
- echo stuff >a &&
- git add a &&
- test_tick &&
- git commit -m A &&
- git tag A &&
-
- git checkout -b B A &&
- git mv a b &&
- test_tick &&
- git commit -m B &&
-
- git checkout -b C A &&
- git mv a c &&
- test_tick &&
- git commit -m C
+ test_create_repo rename-rename-1to2 &&
+ (
+ cd rename-rename-1to2 &&
+
+ echo stuff >a &&
+ git add a &&
+ test_tick &&
+ git commit -m A &&
+ git tag A &&
+
+ git checkout -b B A &&
+ git mv a b &&
+ test_tick &&
+ git commit -m B &&
+
+ git checkout -b C A &&
+ git mv a c &&
+ test_tick &&
+ git commit -m C
+ )
'
test_expect_success 'merge has correct working tree contents' '
- git checkout C^0 &&
-
- test_must_fail git merge -s recursive B^0 &&
-
- test 3 -eq $(git ls-files -s | wc -l) &&
- test 3 -eq $(git ls-files -u | wc -l) &&
- test 0 -eq $(git ls-files -o | wc -l) &&
-
- test $(git rev-parse :1:a) = $(git rev-parse A:a) &&
- test $(git rev-parse :3:b) = $(git rev-parse A:a) &&
- test $(git rev-parse :2:c) = $(git rev-parse A:a) &&
-
- test ! -f a &&
- test $(git hash-object b) = $(git rev-parse A:a) &&
- test $(git hash-object c) = $(git rev-parse A:a)
+ (
+ cd rename-rename-1to2 &&
+
+ git checkout C^0 &&
+
+ test_must_fail git merge -s recursive B^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 &&
+
+ test_path_is_missing a &&
+ git rev-parse >expect \
+ A:a A:a A:a \
+ A:a A:a &&
+ git rev-parse >actual \
+ :1:a :3:b :2:c &&
+ git hash-object >>actual \
+ b c &&
+ test_cmp expect actual
+ )
'
# Testcase setup for rename/rename(1to2)/add-source conflict:
@@ -449,130 +541,400 @@ test_expect_success 'merge has correct working tree contents' '
# Merging of B & C should NOT be clean; there's a rename/rename conflict
test_expect_success 'setup rename/rename(1to2)/add-source conflict' '
- git rm -rf . &&
- git clean -fdqx &&
- rm -rf .git &&
- git init &&
-
- printf "1\n2\n3\n4\n5\n6\n7\n" >a &&
- git add a &&
- git commit -m A &&
- git tag A &&
-
- git checkout -b B A &&
- git mv a b &&
- git commit -m B &&
-
- git checkout -b C A &&
- git mv a c &&
- echo something completely different >a &&
- git add a &&
- git commit -m C
+ test_create_repo rename-rename-1to2-add-source-1 &&
+ (
+ cd rename-rename-1to2-add-source-1 &&
+
+ printf "1\n2\n3\n4\n5\n6\n7\n" >a &&
+ git add a &&
+ git commit -m A &&
+ git tag A &&
+
+ git checkout -b B A &&
+ git mv a b &&
+ git commit -m B &&
+
+ git checkout -b C A &&
+ git mv a c &&
+ echo something completely different >a &&
+ git add a &&
+ git commit -m C
+ )
'
test_expect_failure 'detect conflict with rename/rename(1to2)/add-source merge' '
- git checkout B^0 &&
+ (
+ cd rename-rename-1to2-add-source-1 &&
+
+ git checkout B^0 &&
- test_must_fail git merge -s recursive C^0 &&
+ test_must_fail git merge -s recursive C^0 &&
- test 4 -eq $(git ls-files -s | wc -l) &&
- test 0 -eq $(git ls-files -o | wc -l) &&
+ git ls-files -s >out &&
+ test_line_count = 4 out &&
+ git ls-files -o >out &&
+ test_line_count = 1 out &&
- test $(git rev-parse 3:a) = $(git rev-parse C:a) &&
- test $(git rev-parse 1:a) = $(git rev-parse A:a) &&
- test $(git rev-parse 2:b) = $(git rev-parse B:b) &&
- test $(git rev-parse 3:c) = $(git rev-parse C:c) &&
+ git rev-parse >expect \
+ 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 -f a &&
- test -f b &&
- test -f c
+ test_path_is_file a &&
+ test_path_is_file b &&
+ test_path_is_file c
+ )
'
test_expect_success 'setup rename/rename(1to2)/add-source resolvable conflict' '
- git rm -rf . &&
- git clean -fdqx &&
- rm -rf .git &&
- git init &&
-
- >a &&
- git add a &&
- test_tick &&
- git commit -m base &&
- git tag A &&
-
- git checkout -b B A &&
- git mv a b &&
- test_tick &&
- git commit -m one &&
-
- git checkout -b C A &&
- git mv a b &&
- echo important-info >a &&
- git add a &&
- test_tick &&
- git commit -m two
+ test_create_repo rename-rename-1to2-add-source-2 &&
+ (
+ cd rename-rename-1to2-add-source-2 &&
+
+ >a &&
+ git add a &&
+ test_tick &&
+ git commit -m base &&
+ git tag A &&
+
+ git checkout -b B A &&
+ git mv a b &&
+ test_tick &&
+ git commit -m one &&
+
+ git checkout -b C A &&
+ git mv a b &&
+ echo important-info >a &&
+ git add a &&
+ test_tick &&
+ git commit -m two
+ )
'
test_expect_failure 'rename/rename/add-source still tracks new a file' '
- git checkout C^0 &&
- git merge -s recursive B^0 &&
-
- test 2 -eq $(git ls-files -s | wc -l) &&
- test 0 -eq $(git ls-files -o | wc -l) &&
-
- test $(git rev-parse HEAD:a) = $(git rev-parse C:a) &&
- test $(git rev-parse HEAD:b) = $(git rev-parse A:a)
+ (
+ cd rename-rename-1to2-add-source-2 &&
+
+ git checkout C^0 &&
+ git merge -s recursive B^0 &&
+
+ git ls-files -s >out &&
+ test_line_count = 2 out &&
+ git ls-files -o >out &&
+ test_line_count = 1 out &&
+
+ git rev-parse >expect \
+ C:a A:a &&
+ git rev-parse >actual \
+ :0:a :0:b &&
+ test_cmp expect actual
+ )
'
test_expect_success 'setup rename/rename(1to2)/add-dest conflict' '
- git rm -rf . &&
- git clean -fdqx &&
- rm -rf .git &&
- git init &&
-
- echo stuff >a &&
- git add a &&
- test_tick &&
- git commit -m base &&
- git tag A &&
-
- git checkout -b B A &&
- git mv a b &&
- echo precious-data >c &&
- git add c &&
- test_tick &&
- git commit -m one &&
-
- git checkout -b C A &&
- git mv a c &&
- echo important-info >b &&
- git add b &&
- test_tick &&
- git commit -m two
+ test_create_repo rename-rename-1to2-add-dest &&
+ (
+ cd rename-rename-1to2-add-dest &&
+
+ echo stuff >a &&
+ git add a &&
+ test_tick &&
+ git commit -m base &&
+ git tag A &&
+
+ git checkout -b B A &&
+ git mv a b &&
+ echo precious-data >c &&
+ git add c &&
+ test_tick &&
+ git commit -m one &&
+
+ git checkout -b C A &&
+ git mv a c &&
+ echo important-info >b &&
+ git add b &&
+ test_tick &&
+ git commit -m two
+ )
'
test_expect_success 'rename/rename/add-dest merge still knows about conflicting file versions' '
- git checkout C^0 &&
- test_must_fail git merge -s recursive B^0 &&
-
- test 5 -eq $(git ls-files -s | wc -l) &&
- test 2 -eq $(git ls-files -u b | wc -l) &&
- test 2 -eq $(git ls-files -u c | wc -l) &&
- test 4 -eq $(git ls-files -o | wc -l) &&
-
- test $(git rev-parse :1:a) = $(git rev-parse A:a) &&
- test $(git rev-parse :2:b) = $(git rev-parse C:b) &&
- test $(git rev-parse :3:b) = $(git rev-parse B:b) &&
- test $(git rev-parse :2:c) = $(git rev-parse C:c) &&
- test $(git rev-parse :3:c) = $(git rev-parse B:c) &&
-
- test $(git hash-object c~HEAD) = $(git rev-parse C:c) &&
- test $(git hash-object c~B\^0) = $(git rev-parse B:c) &&
- test $(git hash-object b~HEAD) = $(git rev-parse C:b) &&
- test $(git hash-object b~B\^0) = $(git rev-parse B:b) &&
-
- test ! -f b &&
- test ! -f c
+ (
+ cd rename-rename-1to2-add-dest &&
+
+ git checkout C^0 &&
+ test_must_fail git merge -s recursive B^0 &&
+
+ git ls-files -s >out &&
+ test_line_count = 5 out &&
+ git ls-files -u b >out &&
+ test_line_count = 2 out &&
+ git ls-files -u c >out &&
+ test_line_count = 2 out &&
+ git ls-files -o >out &&
+ test_line_count = 5 out &&
+
+ git rev-parse >expect \
+ 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 &&
+
+ 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_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 2e28f29..4a71f17 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 23b86fb..5e3779e 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/t6046-merge-skip-unneeded-updates.sh b/t/t6046-merge-skip-unneeded-updates.sh
index fcefffc..38e24f7 100755
--- a/t/t6046-merge-skip-unneeded-updates.sh
+++ b/t/t6046-merge-skip-unneeded-updates.sh
@@ -366,7 +366,9 @@ test_expect_success '2c-check: Modify b & add c VS rename b->c' '
git checkout A^0 &&
- GIT_MERGE_VERBOSITY=3 test_must_fail git merge -s recursive B^0 >out 2>err &&
+ GIT_MERGE_VERBOSITY=3 &&
+ export GIT_MERGE_VERBOSITY &&
+ test_must_fail git merge -s recursive B^0 >out 2>err &&
test_i18ngrep "CONFLICT (rename/add): Rename b->c" out &&
test_i18ngrep ! "Skipped c" out &&
diff --git a/t/t6050-replace.sh b/t/t6050-replace.sh
index aa3e249..86374a9 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 debadbd..ddf34f0 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/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index 48379aa..024f8c0 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -795,4 +795,14 @@ test_expect_success ':remotename and :remoteref' '
)
'
+test_expect_success 'for-each-ref --ignore-case ignores case' '
+ git for-each-ref --format="%(refname)" refs/heads/MASTER >actual &&
+ test_must_be_empty actual &&
+
+ echo refs/heads/master >expect &&
+ git for-each-ref --format="%(refname)" --ignore-case \
+ refs/heads/MASTER >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index cc3fd2b..9e59e5a 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -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/t7003-filter-branch.sh b/t/t7003-filter-branch.sh
index ec4b160..e23de7d 100755
--- a/t/t7003-filter-branch.sh
+++ b/t/t7003-filter-branch.sh
@@ -107,6 +107,21 @@ test_expect_success 'test that the directory was renamed' '
test dir/D = "$(cat diroh/D.t)"
'
+V=$(git rev-parse HEAD)
+
+test_expect_success 'populate --state-branch' '
+ git filter-branch --state-branch state -f --tree-filter "touch file || :" HEAD
+'
+
+W=$(git rev-parse HEAD)
+
+test_expect_success 'using --state-branch to skip already rewritten commits' '
+ test_when_finished git reset --hard $V &&
+ git reset --hard $V &&
+ git filter-branch --state-branch state -f --tree-filter "touch file || :" HEAD &&
+ test_cmp_rev $W HEAD
+'
+
git tag oldD HEAD~4
test_expect_success 'rewrite one branch, keeping a side branch' '
git branch modD oldD &&
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index d7b319e..465eb4e 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -693,9 +693,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 +973,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 +1352,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 +1393,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 +1476,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
@@ -1606,9 +1615,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 +1913,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 +1927,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 7541ba5..00e09a3 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/t7030-verify-tag.sh b/t/t7030-verify-tag.sh
index b4b49ee..041e319 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,9 +79,16 @@ 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 "s/seventh/7th forged/" raw >forged1 &&
+ sed -e "/^tag / s/seventh/7th forged/" raw >forged1 &&
git hash-object -w -t tag forged1 >forged1.tag &&
test_must_fail git verify-tag $(cat forged1.tag) 2>actual1 &&
grep "BAD signature from" actual1 &&
@@ -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 c61e304..2da57fc 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/t7102-reset.sh b/t/t7102-reset.sh
index 95653a0..97be0d9 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 98b7d7b..bd10a96 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 0f95f00..ecb85c3 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 ab9da61..94cb039 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -528,10 +528,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 +549,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 +572,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 &&