summaryrefslogtreecommitdiff
path: root/t
diff options
context:
space:
mode:
Diffstat (limited to 't')
-rw-r--r--t/.gitattributes2
-rw-r--r--t/Makefile76
-rw-r--r--t/README218
-rwxr-xr-xt/aggregate-results.sh19
-rw-r--r--t/annotate-tests.sh77
-rwxr-xr-xt/chainlint.pl832
-rw-r--r--t/chainlint.sed369
-rw-r--r--t/chainlint/arithmetic-expansion.expect6
-rw-r--r--t/chainlint/bash-array.expect4
-rw-r--r--t/chainlint/blank-line-before-esac.expect18
-rw-r--r--t/chainlint/blank-line-before-esac.test19
-rw-r--r--t/chainlint/blank-line.expect6
-rw-r--r--t/chainlint/blank-line.test2
-rw-r--r--t/chainlint/block-comment.expect8
-rw-r--r--t/chainlint/block-comment.test8
-rw-r--r--t/chainlint/block.expect17
-rw-r--r--t/chainlint/block.test18
-rw-r--r--t/chainlint/broken-chain.expect4
-rw-r--r--t/chainlint/broken-chain.test2
-rw-r--r--t/chainlint/case-comment.expect11
-rw-r--r--t/chainlint/case-comment.test11
-rw-r--r--t/chainlint/case.expect10
-rw-r--r--t/chainlint/case.test6
-rw-r--r--t/chainlint/chain-break-background.expect9
-rw-r--r--t/chainlint/chain-break-background.test10
-rw-r--r--t/chainlint/chain-break-continue.expect12
-rw-r--r--t/chainlint/chain-break-continue.test13
-rw-r--r--t/chainlint/chain-break-false.expect9
-rw-r--r--t/chainlint/chain-break-false.test10
-rw-r--r--t/chainlint/chain-break-return-exit.expect19
-rw-r--r--t/chainlint/chain-break-return-exit.test23
-rw-r--r--t/chainlint/chain-break-status.expect9
-rw-r--r--t/chainlint/chain-break-status.test11
-rw-r--r--t/chainlint/chained-block.expect9
-rw-r--r--t/chainlint/chained-block.test11
-rw-r--r--t/chainlint/chained-subshell.expect10
-rw-r--r--t/chainlint/chained-subshell.test13
-rw-r--r--t/chainlint/close-nested-and-parent-together.expect5
-rw-r--r--t/chainlint/close-subshell.expect17
-rw-r--r--t/chainlint/command-substitution-subsubshell.expect2
-rw-r--r--t/chainlint/command-substitution-subsubshell.test3
-rw-r--r--t/chainlint/command-substitution.expect6
-rw-r--r--t/chainlint/comment.expect6
-rw-r--r--t/chainlint/complex-if-in-cuddled-loop.expect7
-rw-r--r--t/chainlint/complex-if-in-cuddled-loop.test2
-rw-r--r--t/chainlint/cuddled-if-then-else.expect5
-rw-r--r--t/chainlint/cuddled-if-then-else.test2
-rw-r--r--t/chainlint/cuddled-loop.expect5
-rw-r--r--t/chainlint/cuddled-loop.test2
-rw-r--r--t/chainlint/cuddled.expect22
-rw-r--r--t/chainlint/cuddled.test3
-rw-r--r--t/chainlint/double-here-doc.expect12
-rw-r--r--t/chainlint/double-here-doc.test12
-rw-r--r--t/chainlint/dqstring-line-splice.expect5
-rw-r--r--t/chainlint/dqstring-line-splice.test7
-rw-r--r--t/chainlint/dqstring-no-interpolate.expect12
-rw-r--r--t/chainlint/dqstring-no-interpolate.test15
-rw-r--r--t/chainlint/empty-here-doc.expect4
-rw-r--r--t/chainlint/empty-here-doc.test5
-rw-r--r--t/chainlint/exclamation.expect4
-rw-r--r--t/chainlint/exclamation.test8
-rw-r--r--t/chainlint/exit-loop.expect6
-rw-r--r--t/chainlint/exit-subshell.expect2
-rw-r--r--t/chainlint/for-loop-abbreviated.expect5
-rw-r--r--t/chainlint/for-loop-abbreviated.test6
-rw-r--r--t/chainlint/for-loop.expect13
-rw-r--r--t/chainlint/for-loop.test8
-rw-r--r--t/chainlint/function.expect11
-rw-r--r--t/chainlint/function.test13
-rw-r--r--t/chainlint/here-doc-close-subshell.expect4
-rw-r--r--t/chainlint/here-doc-indent-operator.expect11
-rw-r--r--t/chainlint/here-doc-indent-operator.test13
-rw-r--r--t/chainlint/here-doc-multi-line-command-subst.expect9
-rw-r--r--t/chainlint/here-doc-multi-line-string.expect7
-rw-r--r--t/chainlint/here-doc.expect28
-rw-r--r--t/chainlint/here-doc.test7
-rw-r--r--t/chainlint/if-condition-split.expect7
-rw-r--r--t/chainlint/if-condition-split.test8
-rw-r--r--t/chainlint/if-in-loop.expect8
-rw-r--r--t/chainlint/if-in-loop.test6
-rw-r--r--t/chainlint/if-then-else.expect17
-rw-r--r--t/chainlint/if-then-else.test17
-rw-r--r--t/chainlint/incomplete-line.expect12
-rw-r--r--t/chainlint/inline-comment.expect11
-rw-r--r--t/chainlint/loop-detect-failure.expect15
-rw-r--r--t/chainlint/loop-detect-failure.test17
-rw-r--r--t/chainlint/loop-detect-status.expect18
-rw-r--r--t/chainlint/loop-detect-status.test19
-rw-r--r--t/chainlint/loop-in-if.expect10
-rw-r--r--t/chainlint/loop-in-if.test6
-rw-r--r--t/chainlint/loop-upstream-pipe.expect10
-rw-r--r--t/chainlint/loop-upstream-pipe.test11
-rw-r--r--t/chainlint/multi-line-nested-command-substitution.expect10
-rw-r--r--t/chainlint/multi-line-string.expect21
-rw-r--r--t/chainlint/multi-line-string.test16
-rw-r--r--t/chainlint/negated-one-liner.expect4
-rw-r--r--t/chainlint/nested-cuddled-subshell.expect20
-rw-r--r--t/chainlint/nested-here-doc.expect31
-rw-r--r--t/chainlint/nested-loop-detect-failure.expect31
-rw-r--r--t/chainlint/nested-loop-detect-failure.test35
-rw-r--r--t/chainlint/nested-subshell-comment.expect4
-rw-r--r--t/chainlint/nested-subshell-comment.test2
-rw-r--r--t/chainlint/nested-subshell.expect9
-rw-r--r--t/chainlint/nested-subshell.test1
-rw-r--r--t/chainlint/not-heredoc.expect14
-rw-r--r--t/chainlint/not-heredoc.test16
-rw-r--r--t/chainlint/one-liner-for-loop.expect9
-rw-r--r--t/chainlint/one-liner-for-loop.test10
-rw-r--r--t/chainlint/one-liner.expect6
-rw-r--r--t/chainlint/one-liner.test4
-rw-r--r--t/chainlint/p4-filespec.expect2
-rw-r--r--t/chainlint/pipe.expect6
-rw-r--r--t/chainlint/pipe.test2
-rw-r--r--t/chainlint/return-loop.expect5
-rw-r--r--t/chainlint/return-loop.test6
-rw-r--r--t/chainlint/semicolon.expect27
-rw-r--r--t/chainlint/semicolon.test4
-rw-r--r--t/chainlint/sqstring-in-sqstring.expect4
-rw-r--r--t/chainlint/sqstring-in-sqstring.test5
-rw-r--r--t/chainlint/subshell-here-doc.expect35
-rw-r--r--t/chainlint/subshell-here-doc.test8
-rw-r--r--t/chainlint/subshell-one-liner.expect17
-rw-r--r--t/chainlint/t7900-subtree.expect28
-rw-r--r--t/chainlint/t7900-subtree.test4
-rw-r--r--t/chainlint/token-pasting.expect27
-rw-r--r--t/chainlint/token-pasting.test32
-rw-r--r--t/chainlint/unclosed-here-doc-indent.expect4
-rw-r--r--t/chainlint/unclosed-here-doc-indent.test4
-rw-r--r--t/chainlint/unclosed-here-doc.expect7
-rw-r--r--t/chainlint/unclosed-here-doc.test7
-rw-r--r--t/chainlint/while-loop.expect13
-rw-r--r--t/chainlint/while-loop.test8
-rwxr-xr-xt/check-non-portable-shell.pl3
-rw-r--r--t/helper/test-advise.c2
-rw-r--r--t/helper/test-bitmap.c13
-rw-r--r--t/helper/test-bloom.c9
-rw-r--r--t/helper/test-bundle-uri.c135
-rw-r--r--t/helper/test-cache-tree.c69
-rw-r--r--t/helper/test-chmtime.c17
-rw-r--r--t/helper/test-config.c67
-rw-r--r--t/helper/test-crontab.c24
-rw-r--r--t/helper/test-csprng.c29
-rw-r--r--t/helper/test-ctype.c43
-rw-r--r--t/helper/test-date.c15
-rw-r--r--t/helper/test-delete-gpgsig.c62
-rw-r--r--t/helper/test-delta.c22
-rw-r--r--t/helper/test-dir-iterator.c6
-rw-r--r--t/helper/test-drop-caches.c6
-rw-r--r--t/helper/test-dump-cache-tree.c19
-rw-r--r--t/helper/test-dump-fsmonitor.c6
-rw-r--r--t/helper/test-dump-split-index.c10
-rw-r--r--t/helper/test-dump-untracked-cache.c17
-rw-r--r--t/helper/test-env-helper.c100
-rw-r--r--t/helper/test-example-decorate.c7
-rw-r--r--t/helper/test-fake-ssh.c8
-rw-r--r--t/helper/test-fast-rebase.c225
-rw-r--r--t/helper/test-find-pack.c50
-rw-r--r--t/helper/test-fsmonitor-client.c224
-rw-r--r--t/helper/test-genzeros.c24
-rw-r--r--t/helper/test-hash-speed.c2
-rw-r--r--t/helper/test-hash.c3
-rw-r--r--t/helper/test-hashmap.c22
-rw-r--r--t/helper/test-hexdump.c30
-rw-r--r--t/helper/test-index-version.c15
-rw-r--r--t/helper/test-json-writer.c93
-rw-r--r--t/helper/test-lazy-init-name-hash.c28
-rw-r--r--t/helper/test-match-trees.c12
-rw-r--r--t/helper/test-mergesort.c407
-rw-r--r--t/helper/test-oid-array.c12
-rw-r--r--t/helper/test-oidmap.c33
-rw-r--r--t/helper/test-oidtree.c11
-rw-r--r--t/helper/test-online-cpus.c2
-rw-r--r--t/helper/test-pack-mtimes.c57
-rw-r--r--t/helper/test-parse-options.c156
-rw-r--r--t/helper/test-parse-pathspec-file.c4
-rw-r--r--t/helper/test-partial-clone.c5
-rw-r--r--t/helper/test-path-utils.c21
-rw-r--r--t/helper/test-pcre2-config.c1
-rw-r--r--t/helper/test-pkt-line.c63
-rw-r--r--t/helper/test-prio-queue.c50
-rw-r--r--t/helper/test-proc-receive.c7
-rw-r--r--t/helper/test-progress.c51
-rw-r--r--t/helper/test-reach.c24
-rw-r--r--t/helper/test-read-cache.c68
-rw-r--r--t/helper/test-read-graph.c19
-rw-r--r--t/helper/test-read-midx.c77
-rw-r--r--t/helper/test-ref-store.c152
-rw-r--r--t/helper/test-reftable.c23
-rw-r--r--t/helper/test-regex.c11
-rw-r--r--t/helper/test-repository.c6
-rw-r--r--t/helper/test-revision-walking.c7
-rw-r--r--t/helper/test-rot13-filter.c382
-rw-r--r--t/helper/test-run-command.c150
-rw-r--r--t/helper/test-scrap-cache-tree.c14
-rw-r--r--t/helper/test-serve-v2.c19
-rw-r--r--t/helper/test-sha1.c10
-rw-r--r--t/helper/test-sha256.c2
-rw-r--r--t/helper/test-sigchain.c3
-rw-r--r--t/helper/test-simple-ipc.c238
-rw-r--r--t/helper/test-strcmp-offset.c4
-rw-r--r--t/helper/test-string-list.c6
-rw-r--r--t/helper/test-submodule-config.c20
-rw-r--r--t/helper/test-submodule-nested-repo-config.c6
-rw-r--r--t/helper/test-submodule.c242
-rw-r--r--t/helper/test-subprocess.c4
-rw-r--r--t/helper/test-tool-utils.h9
-rw-r--r--t/helper/test-tool.c29
-rw-r--r--t/helper/test-tool.h22
-rw-r--r--t/helper/test-trace2.c287
-rw-r--r--t/helper/test-truncate.c25
-rw-r--r--t/helper/test-urlmatch-normalization.c11
-rw-r--r--t/helper/test-userdiff.c6
-rw-r--r--t/helper/test-wildmatch.c2
-rw-r--r--t/helper/test-write-cache.c10
-rw-r--r--t/helper/test-xml-encode.c2
-rw-r--r--t/interop/Makefile3
-rw-r--r--t/interop/interop-lib.sh2
-rw-r--r--t/lib-bitmap.sh425
-rw-r--r--t/lib-bundle-uri-protocol.sh216
-rw-r--r--t/lib-chunk.sh17
-rw-r--r--t/lib-chunk/corrupt-chunk-file.pl90
-rwxr-xr-xt/lib-commit-graph.sh74
-rw-r--r--t/lib-credential.sh240
-rw-r--r--t/lib-cvs.sh4
-rw-r--r--t/lib-diff-alternative.sh59
-rw-r--r--t/lib-diff-data.sh22
-rw-r--r--t/lib-diff.sh2
-rw-r--r--t/lib-diff/COPYING361
-rw-r--r--t/lib-diff/README46
-rw-r--r--t/lib-git-p4.sh3
-rw-r--r--t/lib-git-svn.sh4
-rw-r--r--t/lib-gpg.sh118
-rw-r--r--t/lib-httpd.sh90
-rw-r--r--t/lib-httpd/apache.conf88
-rw-r--r--t/lib-httpd/apply-one-time-perl.sh2
-rw-r--r--t/lib-httpd/error-no-report.sh6
-rw-r--r--t/lib-httpd/nph-custom-auth.sh39
-rw-r--r--t/lib-httpd/passwd2
-rw-r--r--t/lib-httpd/proxy-passwd1
-rw-r--r--t/lib-httpd/ssl.cnf2
-rw-r--r--t/lib-midx.sh8
-rw-r--r--t/lib-pager.sh2
-rw-r--r--t/lib-parallel-checkout.sh8
-rw-r--r--t/lib-patch-mode.sh11
-rw-r--r--t/lib-perl.sh19
-rw-r--r--t/lib-proto-disable.sh6
-rw-r--r--t/lib-read-tree-m-3way.sh168
-rw-r--r--t/lib-rebase.sh46
-rw-r--r--t/lib-submodule-update.sh38
-rw-r--r--t/lib-subtest.sh95
-rw-r--r--t/lib-sudo.sh15
-rw-r--r--t/lib-t3100.sh10
-rwxr-xr-xt/lib-unicode-nfc-nfd.sh162
-rw-r--r--t/lib-unique-files.sh34
-rw-r--r--t/oid-info/hash-info12
-rw-r--r--t/oid-info/oid2
-rw-r--r--t/perf/Makefile3
-rw-r--r--t/perf/README4
-rwxr-xr-xt/perf/aggregate.perl4
-rw-r--r--t/perf/config2
-rw-r--r--t/perf/lib-bitmap.sh100
-rwxr-xr-xt/perf/p0004-lazy-init-name-hash.sh2
-rwxr-xr-xt/perf/p0005-status.sh12
-rwxr-xr-xt/perf/p0006-read-tree-checkout.sh30
-rwxr-xr-xt/perf/p0007-write-cache.sh4
-rwxr-xr-xt/perf/p0008-odb-fsync.sh82
-rwxr-xr-xt/perf/p0071-sort.sh40
-rwxr-xr-xt/perf/p0090-cache-tree.sh36
-rwxr-xr-xt/perf/p0100-globbing.sh4
-rwxr-xr-xt/perf/p1006-cat-file.sh12
-rwxr-xr-xt/perf/p1400-update-ref.sh4
-rwxr-xr-xt/perf/p1451-fsck-skip-list.sh2
-rwxr-xr-xt/perf/p1500-graph-walks.sh50
-rwxr-xr-xt/perf/p2000-sparse-operations.sh25
-rwxr-xr-xt/perf/p3400-rebase.sh4
-rwxr-xr-xt/perf/p4002-diff-color-moved.sh57
-rwxr-xr-xt/perf/p4220-log-grep-engines.sh3
-rwxr-xr-xt/perf/p4221-log-grep-engines-fixed.sh3
-rwxr-xr-xt/perf/p5302-pack-index.sh19
-rwxr-xr-xt/perf/p5303-many-packs.sh10
-rwxr-xr-xt/perf/p5310-pack-bitmaps.sh141
-rwxr-xr-xt/perf/p5311-pack-bitmaps-fetch.sh74
-rwxr-xr-xt/perf/p5312-pack-bitmaps-revs.sh34
-rwxr-xr-xt/perf/p5326-multi-pack-bitmaps.sh67
-rwxr-xr-xt/perf/p5332-multi-pack-reuse.sh81
-rwxr-xr-xt/perf/p6300-for-each-ref.sh87
-rwxr-xr-xt/perf/p7102-reset.sh21
-rwxr-xr-xt/perf/p7519-fsmonitor.sh86
-rwxr-xr-xt/perf/p7527-builtin-fsmonitor.sh257
-rwxr-xr-xt/perf/p7820-grep-engines.sh6
-rwxr-xr-xt/perf/p7822-grep-perl-character.sh42
-rwxr-xr-xt/perf/p9210-scalar.sh39
-rw-r--r--t/perf/perf-lib.sh91
-rwxr-xr-xt/perf/repos/inflate-repo.sh2
-rwxr-xr-xt/perf/run18
-rwxr-xr-xt/t0000-basic.sh575
-rwxr-xr-xt/t0001-init.sh99
-rwxr-xr-xt/t0002-gitfile.sh15
-rwxr-xr-xt/t0003-attributes.sh283
-rwxr-xr-xt/t0004-unwritable.sh48
-rwxr-xr-xt/t0005-signals.sh4
-rwxr-xr-xt/t0006-date.sh23
-rwxr-xr-xt/t0007-git-var.sh231
-rwxr-xr-xt/t0008-ignores.sh62
-rwxr-xr-xt/t0009-prio-queue.sh64
-rwxr-xr-xt/t0010-racy-git.sh32
-rwxr-xr-xt/t0011-hashmap.sh8
-rwxr-xr-xt/t0012-help.sh196
-rwxr-xr-xt/t0013-sha1dc.sh10
-rwxr-xr-xt/t0014-alias.sh8
-rwxr-xr-xt/t0015-hash.sh9
-rwxr-xr-xt/t0016-oidmap.sh2
-rwxr-xr-xt/t0017-env-helper.sh63
-rwxr-xr-xt/t0018-advice.sh2
-rwxr-xr-xt/t0019-json-writer.sh2
-rwxr-xr-xt/t0020-crlf.sh67
-rwxr-xr-xt/t0021-conversion.sh159
-rw-r--r--t/t0021/rot13-filter.pl247
-rwxr-xr-xt/t0022-crlf-rename.sh5
-rwxr-xr-xt/t0023-crlf-am.sh1
-rwxr-xr-xt/t0024-crlf-archive.sh14
-rwxr-xr-xt/t0025-crlf-renormalize.sh5
-rwxr-xr-xt/t0026-eol-config.sh5
-rwxr-xr-xt/t0027-auto-crlf.sh100
-rwxr-xr-xt/t0028-working-tree-encoding.sh39
-rwxr-xr-xt/t0029-core-unsetenvvars.sh4
-rwxr-xr-xt/t0030-stripspace.sh557
-rwxr-xr-xt/t0032-reftable-unittest.sh16
-rwxr-xr-xt/t0033-safe-directory.sh83
-rwxr-xr-xt/t0034-root-safe-directory.sh93
-rwxr-xr-xt/t0035-safe-bare-repository.sh107
-rwxr-xr-xt/t0040-parse-options.sh407
-rwxr-xr-xt/t0041-usage.sh33
-rwxr-xr-xt/t0050-filesystem.sh4
-rwxr-xr-xt/t0051-windows-named-pipe.sh7
-rwxr-xr-xt/t0052-simple-ipc.sh1
-rwxr-xr-xt/t0055-beyond-symlinks.sh15
-rwxr-xr-xt/t0056-git-C.sh1
-rwxr-xr-xt/t0060-path-utils.sh135
-rwxr-xr-xt/t0061-run-command.sh52
-rwxr-xr-xt/t0062-revision-walking.sh1
-rwxr-xr-xt/t0063-string-list.sh52
-rwxr-xr-xt/t0064-oid-array.sh2
-rwxr-xr-xt/t0065-strcmp-offset.sh1
-rwxr-xr-xt/t0066-dir-iterator.sh24
-rwxr-xr-xt/t0067-parse_pathspec_file.sh1
-rwxr-xr-xt/t0068-for-each-repo.sh26
-rwxr-xr-xt/t0069-oidtree.sh13
-rwxr-xr-xt/t0070-fundamental.sh67
-rwxr-xr-xt/t0071-sort.sh12
-rwxr-xr-xt/t0080-unit-test-output.sh59
-rwxr-xr-xt/t0081-find-pack.sh82
-rwxr-xr-xt/t0090-cache-tree.sh3
-rwxr-xr-xt/t0091-bugreport.sh151
-rwxr-xr-xt/t0092-diagnose.sh72
-rwxr-xr-xt/t0095-bloom.sh8
-rwxr-xr-xt/t0100-previous.sh9
-rwxr-xr-xt/t0101-at-syntax.sh2
-rwxr-xr-xt/t0110-urlmatch-normalization.sh4
-rwxr-xr-xt/t0200-gettext-basic.sh1
-rwxr-xr-xt/t0201-gettext-fallbacks.sh1
-rwxr-xr-xt/t0202-gettext-perl.sh23
-rwxr-xr-xt/t0202/test.pl2
-rwxr-xr-xt/t0203-gettext-setlocale-sanity.sh1
-rwxr-xr-xt/t0204-gettext-reencode-sanity.sh3
-rwxr-xr-xt/t0210-trace2-normal.sh96
-rwxr-xr-xt/t0211-trace2-perf.sh347
-rw-r--r--t/t0211/scrub_perf.perl10
-rwxr-xr-xt/t0212-trace2-event.sh42
-rw-r--r--t/t0212/parse_events.perl19
-rwxr-xr-xt/t0300-credentials.sh116
-rwxr-xr-xt/t0301-credential-cache.sh42
-rwxr-xr-xt/t0303-credential-external.sh28
-rwxr-xr-xt/t0410-partial-clone.sh72
-rwxr-xr-xt/t0450-txt-doc-vs-help.sh174
-rw-r--r--t/t0450/txt-help-mismatches58
-rwxr-xr-xt/t0500-progress-display.sh109
-rwxr-xr-xt/t0600-reffiles-backend.sh475
-rwxr-xr-xt/t0601-reffiles-pack-refs.sh383
-rwxr-xr-xt/t0610-reftable-basics.sh1032
-rwxr-xr-xt/t0611-reftable-httpd.sh26
-rwxr-xr-xt/t1000-read-tree-m-3way.sh2
-rwxr-xr-xt/t1001-read-tree-m-2way.sh11
-rwxr-xr-xt/t1002-read-tree-m-u-2way.sh596
-rwxr-xr-xt/t1003-read-tree-prefix.sh11
-rwxr-xr-xt/t1005-read-tree-reset.sh16
-rwxr-xr-xt/t1006-cat-file.sh950
-rwxr-xr-xt/t1007-hash-object.sh36
-rwxr-xr-xt/t1008-read-tree-overlay.sh1
-rwxr-xr-xt/t1009-read-tree-new-index.sh1
-rwxr-xr-xt/t1010-mktree.sh9
-rwxr-xr-xt/t1011-read-tree-sparse-checkout.sh72
-rwxr-xr-xt/t1012-read-tree-df.sh1
-rwxr-xr-xt/t1013-read-tree-submodule.sh1
-rwxr-xr-xt/t1014-read-tree-confusing.sh2
-rwxr-xr-xt/t1016-compatObjectFormat.sh281
-rwxr-xr-xt/t1016/gpg2
-rwxr-xr-xt/t1020-subdirectory.sh11
-rwxr-xr-xt/t1022-read-tree-partial-clone.sh7
-rwxr-xr-xt/t1050-large.sh48
-rwxr-xr-xt/t1051-large-conversion.sh28
-rwxr-xr-xt/t1060-object-corruption.sh11
-rwxr-xr-xt/t1090-sparse-checkout-scope.sh24
-rwxr-xr-xt/t1091-sparse-checkout-builtin.sh517
-rwxr-xr-xt/t1092-sparse-checkout-compatibility.sh1730
-rwxr-xr-xt/t1100-commit-tree-options.sh1
-rwxr-xr-xt/t1300-config.sh503
-rwxr-xr-xt/t1301-shared-repo.sh49
-rwxr-xr-xt/t1302-repo-version.sh26
-rwxr-xr-xt/t1303-wacky-config.sh2
-rwxr-xr-xt/t1304-default-acl.sh5
-rwxr-xr-xt/t1305-config-include.sh1
-rwxr-xr-xt/t1307-config-blob.sh4
-rwxr-xr-xt/t1308-config-set.sh128
-rwxr-xr-xt/t1309-early-config.sh3
-rwxr-xr-xt/t1310-config-default.sh5
-rwxr-xr-xt/t1350-config-hooks-path.sh4
-rwxr-xr-xt/t1400-update-ref.sh242
-rwxr-xr-xt/t1401-symbolic-ref.sh68
-rwxr-xr-xt/t1402-check-ref-format.sh1
-rwxr-xr-xt/t1403-show-ref.sh103
-rwxr-xr-xt/t1404-update-ref-errors.sh247
-rwxr-xr-xt/t1405-main-ref-store.sh27
-rwxr-xr-xt/t1406-submodule-ref-store.sh15
-rwxr-xr-xt/t1407-worktree-ref-store.sh38
-rwxr-xr-xt/t1408-packed-refs.sh1
-rwxr-xr-xt/t1409-avoid-packing-refs.sh7
-rwxr-xr-xt/t1410-reflog.sh214
-rwxr-xr-xt/t1411-reflog-show.sh6
-rwxr-xr-xt/t1412-reflog-loop.sh2
-rwxr-xr-xt/t1413-reflog-detach.sh1
-rwxr-xr-xt/t1414-reflog-walk.sh11
-rwxr-xr-xt/t1415-worktree-refs.sh12
-rwxr-xr-xt/t1416-ref-transaction-hooks.sh26
-rwxr-xr-xt/t1417-reflog-updateref.sh69
-rwxr-xr-xt/t1418-reflog-exists.sh38
-rwxr-xr-xt/t1419-exclude-refs.sh128
-rwxr-xr-xt/t1420-lost-found.sh2
-rwxr-xr-xt/t1430-bad-ref-name.sh155
-rwxr-xr-xt/t1450-fsck.sh385
-rwxr-xr-xt/t1451-fsck-buffer.sh142
-rwxr-xr-xt/t1500-rev-parse.sh46
-rwxr-xr-xt/t1501-work-tree.sh2
-rwxr-xr-xt/t1502-rev-parse-parseopt.sh199
-rw-r--r--t/t1502/.gitattributes1
-rw-r--r--t/t1502/optionspec-neg8
-rw-r--r--t/t1502/optionspec-neg.help12
-rwxr-xr-xt/t1502/optionspec.help36
-rwxr-xr-xt/t1503-rev-parse-verify.sh9
-rwxr-xr-xt/t1504-ceiling-dirs.sh10
-rwxr-xr-xt/t1505-rev-parse-last.sh1
-rwxr-xr-xt/t1506-rev-parse-diagnosis.sh35
-rwxr-xr-xt/t1507-rev-parse-upstream.sh15
-rwxr-xr-xt/t1508-at-combinations.sh1
-rwxr-xr-xt/t1509-root-work-tree.sh10
-rwxr-xr-xt/t1509/prepare-chroot.sh2
-rwxr-xr-xt/t1510-repo-setup.sh1
-rwxr-xr-xt/t1512-rev-parse-disambiguation.sh99
-rwxr-xr-xt/t1513-rev-parse-prefix.sh1
-rwxr-xr-xt/t1514-rev-parse-push.sh1
-rwxr-xr-xt/t1515-rev-parse-outside-repo.sh2
-rwxr-xr-xt/t1600-index.sh47
-rwxr-xr-xt/t1700-split-index.sh40
-rwxr-xr-xt/t1701-racy-split-index.sh1
-rwxr-xr-xt/t1800-hook.sh188
-rwxr-xr-xt/t2000-conflict-when-checking-files-out.sh1
-rwxr-xr-xt/t2002-checkout-cache-u.sh1
-rwxr-xr-xt/t2003-checkout-cache-mkdir.sh1
-rwxr-xr-xt/t2004-checkout-cache-temp.sh27
-rwxr-xr-xt/t2005-checkout-index-symlinks.sh9
-rwxr-xr-xt/t2006-checkout-index-basic.sh15
-rwxr-xr-xt/t2007-checkout-symlink.sh1
-rwxr-xr-xt/t2008-checkout-subdir.sh1
-rwxr-xr-xt/t2009-checkout-statinfo.sh1
-rwxr-xr-xt/t2010-checkout-ambiguous.sh5
-rwxr-xr-xt/t2011-checkout-invalid-head.sh10
-rwxr-xr-xt/t2012-checkout-last.sh56
-rwxr-xr-xt/t2014-checkout-switch.sh2
-rwxr-xr-xt/t2015-checkout-unborn.sh12
-rwxr-xr-xt/t2016-checkout-patch.sh79
-rwxr-xr-xt/t2017-checkout-orphan.sh14
-rwxr-xr-xt/t2018-checkout-branch.sh24
-rwxr-xr-xt/t2019-checkout-ambiguous-ref.sh14
-rwxr-xr-xt/t2020-checkout-detach.sh26
-rwxr-xr-xt/t2021-checkout-overwrite.sh17
-rwxr-xr-xt/t2022-checkout-paths.sh1
-rwxr-xr-xt/t2023-checkout-m.sh1
-rwxr-xr-xt/t2024-checkout-dwim.sh17
-rwxr-xr-xt/t2025-checkout-no-overlay.sh3
-rwxr-xr-xt/t2026-checkout-pathspec-file.sh9
-rwxr-xr-xt/t2027-checkout-track.sh26
-rwxr-xr-xt/t2030-unresolve-info.sh118
-rwxr-xr-xt/t2050-git-dir-relative.sh1
-rwxr-xr-xt/t2060-switch.sh70
-rwxr-xr-xt/t2070-restore.sh84
-rwxr-xr-xt/t2071-restore-patch.sh47
-rwxr-xr-xt/t2072-restore-pathspec-file.sh9
-rwxr-xr-xt/t2080-parallel-checkout-basics.sh48
-rwxr-xr-xt/t2081-parallel-checkout-collisions.sh1
-rwxr-xr-xt/t2082-parallel-checkout-attributes.sh7
-rwxr-xr-xt/t2100-update-cache-badpath.sh1
-rwxr-xr-xt/t2101-update-index-reupdate.sh1
-rwxr-xr-xt/t2102-update-index-symlinks.sh3
-rwxr-xr-xt/t2103-update-index-ignore-missing.sh3
-rwxr-xr-xt/t2104-update-index-skip-worktree.sh37
-rwxr-xr-xt/t2105-update-index-gitfile.sh1
-rwxr-xr-xt/t2106-update-index-assume-unchanged.sh3
-rwxr-xr-xt/t2107-update-index-basic.sh68
-rwxr-xr-xt/t2108-update-index-refresh-racy.sh64
-rwxr-xr-xt/t2200-add-update.sh75
-rwxr-xr-xt/t2201-add-update-typechange.sh11
-rwxr-xr-xt/t2202-add-addremove.sh1
-rwxr-xr-xt/t2203-add-intent.sh9
-rwxr-xr-xt/t2204-add-ignored.sh9
-rwxr-xr-xt/t2205-add-worktree-config.sh266
-rwxr-xr-xt/t2300-cd-to-toplevel.sh1
-rwxr-xr-xt/t2400-worktree-add.sh607
-rwxr-xr-xt/t2401-worktree-prune.sh25
-rwxr-xr-xt/t2402-worktree-list.sh28
-rwxr-xr-xt/t2403-worktree-move.sh10
-rwxr-xr-xt/t2404-worktree-config.sh1
-rwxr-xr-xt/t2405-worktree-submodule.sh1
-rwxr-xr-xt/t2406-worktree-repair.sh43
-rwxr-xr-xt/t2407-worktree-heads.sh180
-rwxr-xr-xt/t2500-untracked-overwriting.sh244
-rwxr-xr-xt/t2501-cwd-empty.sh277
-rwxr-xr-xt/t3000-ls-files-others.sh2
-rwxr-xr-xt/t3001-ls-files-others-exclude.sh43
-rwxr-xr-xt/t3002-ls-files-dashpath.sh88
-rwxr-xr-xt/t3003-ls-files-exclude.sh2
-rwxr-xr-xt/t3004-ls-files-basic.sh5
-rwxr-xr-xt/t3005-ls-files-relative.sh11
-rwxr-xr-xt/t3006-ls-files-long.sh2
-rwxr-xr-xt/t3007-ls-files-recurse-submodules.sh55
-rwxr-xr-xt/t3008-ls-files-lazy-init-name-hash.sh1
-rwxr-xr-xt/t3009-ls-files-others-nonsubmodule.sh1
-rwxr-xr-xt/t3010-ls-files-killed-modified.sh2
-rwxr-xr-xt/t3012-ls-files-dedup.sh1
-rwxr-xr-xt/t3013-ls-files-format.sh146
-rwxr-xr-xt/t3020-ls-files-error-unmatch.sh14
-rwxr-xr-xt/t3040-subprojects-basic.sh2
-rwxr-xr-xt/t3050-subprojects-fetch.sh1
-rwxr-xr-xt/t3060-ls-files-with-tree.sh14
-rwxr-xr-xt/t3070-wildmatch.sh19
-rwxr-xr-xt/t3100-ls-tree-restrict.sh2
-rwxr-xr-xt/t3101-ls-tree-dirname.sh65
-rwxr-xr-xt/t3102-ls-tree-wildcards.sh1
-rwxr-xr-xt/t3103-ls-tree-misc.sh16
-rwxr-xr-xt/t3104-ls-tree-format.sh80
-rwxr-xr-xt/t3105-ls-tree-output.sh192
-rwxr-xr-xt/t3200-branch.sh525
-rwxr-xr-xt/t3201-branch-contains.sh3
-rwxr-xr-xt/t3202-show-branch.sh145
-rwxr-xr-xt/t3203-branch-output.sh57
-rwxr-xr-xt/t3204-branch-name-interpretation.sh35
-rwxr-xr-xt/t3205-branch-color.sh4
-rwxr-xr-xt/t3206-range-diff.sh146
-rwxr-xr-xt/t3207-branch-submodule.sh329
-rwxr-xr-xt/t3210-pack-refs.sh259
-rwxr-xr-xt/t3211-peel-ref.sh1
-rwxr-xr-xt/t3300-funny-names.sh1
-rwxr-xr-xt/t3301-notes.sh178
-rwxr-xr-xt/t3302-notes-index-expensive.sh6
-rwxr-xr-xt/t3303-notes-subtrees.sh15
-rwxr-xr-xt/t3304-notes-mixed.sh1
-rwxr-xr-xt/t3305-notes-fanout.sh18
-rwxr-xr-xt/t3307-notes-man.sh1
-rwxr-xr-xt/t3309-notes-merge-auto-resolve.sh7
-rwxr-xr-xt/t3310-notes-merge-manual-resolve.sh22
-rwxr-xr-xt/t3320-notes-merge-worktrees.sh5
-rwxr-xr-xt/t3321-notes-stripspace.sh578
-rwxr-xr-xt/t3400-rebase.sh65
-rwxr-xr-xt/t3402-rebase-merge.sh32
-rwxr-xr-xt/t3403-rebase-skip.sh30
-rwxr-xr-xt/t3404-rebase-interactive.sh639
-rwxr-xr-xt/t3405-rebase-malformed.sh1
-rwxr-xr-xt/t3406-rebase-message.sh192
-rwxr-xr-xt/t3407-rebase-abort.sh122
-rwxr-xr-xt/t3408-rebase-multi-line.sh11
-rwxr-xr-xt/t3409-rebase-environ.sh24
-rwxr-xr-xt/t3409-rebase-preserve-merges.sh130
-rwxr-xr-xt/t3410-rebase-preserve-dropped-merges.sh90
-rwxr-xr-xt/t3411-rebase-preserve-around-merges.sh80
-rwxr-xr-xt/t3412-rebase-root.sh58
-rwxr-xr-xt/t3413-rebase-hook.sh19
-rwxr-xr-xt/t3414-rebase-preserve-onto.sh85
-rwxr-xr-xt/t3415-rebase-autosquash.sh51
-rwxr-xr-xt/t3416-rebase-onto-threedots.sh88
-rwxr-xr-xt/t3417-rebase-whitespace-fix.sh4
-rwxr-xr-xt/t3418-rebase-continue.sh181
-rwxr-xr-xt/t3419-rebase-patch-id.sh64
-rwxr-xr-xt/t3420-rebase-autostash.sh12
-rwxr-xr-xt/t3421-rebase-topology-linear.sh19
-rwxr-xr-xt/t3422-rebase-incompatible-options.sh89
-rwxr-xr-xt/t3423-rebase-reword.sh1
-rwxr-xr-xt/t3424-rebase-empty.sh55
-rwxr-xr-xt/t3425-rebase-topology-merges.sh153
-rwxr-xr-xt/t3426-rebase-submodule.sh4
-rwxr-xr-xt/t3427-rebase-subtree.sh38
-rwxr-xr-xt/t3428-rebase-signoff.sh68
-rwxr-xr-xt/t3429-rebase-edit-todo.sh8
-rwxr-xr-xt/t3430-rebase-merges.sh116
-rwxr-xr-xt/t3431-rebase-fork-point.sh5
-rwxr-xr-xt/t3432-rebase-fast-forward.sh1
-rwxr-xr-xt/t3433-rebase-across-mode-change.sh1
-rwxr-xr-xt/t3435-rebase-gpg-sign.sh7
-rwxr-xr-xt/t3436-rebase-more-options.sh29
-rwxr-xr-xt/t3437-rebase-fixup-options.sh42
-rwxr-xr-xt/t3438-rebase-broken-files.sh70
-rwxr-xr-xt/t3500-cherry.sh69
-rwxr-xr-xt/t3501-revert-cherry-pick.sh111
-rwxr-xr-xt/t3502-cherry-pick-merge.sh1
-rwxr-xr-xt/t3503-cherry-pick-root.sh1
-rwxr-xr-xt/t3505-cherry-pick-empty.sh51
-rwxr-xr-xt/t3506-cherry-pick-ff.sh1
-rwxr-xr-xt/t3507-cherry-pick-conflict.sh12
-rwxr-xr-xt/t3508-cherry-pick-many-commits.sh2
-rwxr-xr-xt/t3510-cherry-pick-sequence.sh39
-rwxr-xr-xt/t3511-cherry-pick-x.sh1
-rwxr-xr-xt/t3512-cherry-pick-submodule.sh2
-rwxr-xr-xt/t3600-rm.sh18
-rwxr-xr-xt/t3601-rm-pathspec-file.sh7
-rwxr-xr-xt/t3602-rm-sparse-checkout.sh71
-rwxr-xr-xt/t3650-replay-basics.sh198
-rwxr-xr-xt/t3700-add.sh144
-rwxr-xr-xt/t3701-add-interactive.sh220
-rwxr-xr-xt/t3702-add-edit.sh4
-rwxr-xr-xt/t3703-add-magic-pathspec.sh1
-rwxr-xr-xt/t3704-add-pathspec-file.sh13
-rwxr-xr-xt/t3705-add-sparse-checkout.sh87
-rwxr-xr-xt/t3800-mktag.sh7
-rwxr-xr-xt/t3900-i18n-commit.sh8
-rwxr-xr-xt/t3901-i18n-patch.sh2
-rwxr-xr-xt/t3902-quoted.sh1
-rwxr-xr-xt/t3903-stash.sh339
-rwxr-xr-xt/t3904-stash-patch.sh6
-rwxr-xr-xt/t3905-stash-include-untracked.sh8
-rwxr-xr-xt/t3906-stash-submodule.sh2
-rwxr-xr-xt/t3908-stash-in-worktree.sh1
-rwxr-xr-xt/t3909-stash-pathspec-file.sh6
-rwxr-xr-xt/t3920-crlf-messages.sh12
-rwxr-xr-xt/t4000-diff-format.sh36
-rwxr-xr-xt/t4001-diff-rename.sh60
-rwxr-xr-xt/t4002-diff-basic.sh245
-rwxr-xr-xt/t4003-diff-rename-1.sh64
-rwxr-xr-xt/t4004-diff-rename-symlink.sh44
-rwxr-xr-xt/t4005-diff-rename-2.sh6
-rwxr-xr-xt/t4006-diff-mode.sh2
-rwxr-xr-xt/t4007-rename-3.sh7
-rwxr-xr-xt/t4008-diff-break-rewrite.sh4
-rwxr-xr-xt/t4009-diff-rename-4.sh6
-rwxr-xr-xt/t4010-diff-pathspec.sh2
-rwxr-xr-xt/t4011-diff-symlink.sh6
-rwxr-xr-xt/t4012-diff-binary.sh17
-rwxr-xr-xt/t4013-diff-various.sh179
-rw-r--r--t/t4013/diff.log_--decorate=full_--all2
-rw-r--r--t/t4013/diff.log_--decorate=full_--clear-decorations_--all61
-rw-r--r--t/t4013/diff.log_--decorate=full_--decorate-all_--all61
-rw-r--r--t/t4013/diff.log_--decorate_--all2
-rw-r--r--t/t4013/diff.log_--decorate_--clear-decorations_--all61
-rw-r--r--t/t4013/diff.log_--decorate_--decorate-all_--all61
-rwxr-xr-xt/t4014-format-patch.sh279
-rwxr-xr-xt/t4015-diff-whitespace.sh278
-rwxr-xr-xt/t4016-diff-quote.sh1
-rwxr-xr-xt/t4017-diff-retval.sh6
-rwxr-xr-xt/t4018-diff-funcname.sh31
-rw-r--r--t/t4018/csharp-exclude-assignments20
-rw-r--r--t/t4018/csharp-exclude-control-statements34
-rw-r--r--t/t4018/csharp-exclude-exceptions29
-rw-r--r--t/t4018/csharp-exclude-generic-method-calls12
-rw-r--r--t/t4018/csharp-exclude-init-dispose22
-rw-r--r--t/t4018/csharp-exclude-iterations26
-rw-r--r--t/t4018/csharp-exclude-method-calls20
-rw-r--r--t/t4018/csharp-exclude-other18
-rw-r--r--t/t4018/csharp-method10
-rw-r--r--t/t4018/csharp-method-array10
-rw-r--r--t/t4018/csharp-method-explicit12
-rw-r--r--t/t4018/csharp-method-generics11
-rw-r--r--t/t4018/csharp-method-generics-alternate-spaces11
-rw-r--r--t/t4018/csharp-method-modifiers13
-rw-r--r--t/t4018/csharp-method-multiline10
-rw-r--r--t/t4018/csharp-method-params10
-rw-r--r--t/t4018/csharp-method-special-chars11
-rw-r--r--t/t4018/csharp-method-with-spacing10
-rw-r--r--t/t4018/csharp-property11
-rw-r--r--t/t4018/csharp-property-braces-same-line10
-rw-r--r--t/t4018/java-class-brace-on-separate-line6
-rw-r--r--t/t4018/java-class-space-before-type-parameters6
-rw-r--r--t/t4018/java-class-type-parameters6
-rw-r--r--t/t4018/java-class-type-parameters-implements6
-rw-r--r--t/t4018/java-interface-type-parameters6
-rw-r--r--t/t4018/java-interface-type-parameters-extends6
-rw-r--r--t/t4018/java-non-sealed8
-rw-r--r--t/t4018/java-record6
-rw-r--r--t/t4018/java-record-space-before-components6
-rw-r--r--t/t4018/java-record-type-parameters6
-rw-r--r--t/t4018/java-sealed7
-rw-r--r--t/t4018/java-sealed-permits6
-rw-r--r--t/t4018/java-sealed-type-parameters6
-rw-r--r--t/t4018/java-sealed-type-parameters-implements-permits6
-rw-r--r--t/t4018/java-sealed-type-parameters-permits6
-rw-r--r--t/t4018/kotlin-class5
-rw-r--r--t/t4018/kotlin-enum-class5
-rw-r--r--t/t4018/kotlin-fun5
-rw-r--r--t/t4018/kotlin-inheritace-class5
-rw-r--r--t/t4018/kotlin-inline-class5
-rw-r--r--t/t4018/kotlin-interface5
-rw-r--r--t/t4018/kotlin-nested-fun9
-rw-r--r--t/t4018/kotlin-public-class5
-rw-r--r--t/t4018/kotlin-sealed-class5
-rwxr-xr-xt/t4019-diff-wserror.sh5
-rwxr-xr-xt/t4020-diff-external.sh156
-rwxr-xr-xt/t4021-format-patch-numbered.sh1
-rwxr-xr-xt/t4022-diff-rewrite.sh8
-rwxr-xr-xt/t4023-diff-rename-typechange.sh29
-rwxr-xr-xt/t4024-diff-optimize-common.sh3
-rwxr-xr-xt/t4025-hunk-header.sh11
-rwxr-xr-xt/t4026-color.sh18
-rwxr-xr-xt/t4027-diff-submodule.sh7
-rwxr-xr-xt/t4028-format-patch-mime-headers.sh2
-rwxr-xr-xt/t4029-diff-trailing-space.sh1
-rwxr-xr-xt/t4031-diff-rewrite-binary.sh2
-rwxr-xr-xt/t4032-diff-inter-hunk-context.sh1
-rwxr-xr-xt/t4033-diff-patience.sh1
-rwxr-xr-xt/t4034-diff-words.sh6
-rw-r--r--t/t4034/cpp/expect63
-rw-r--r--t/t4034/cpp/post47
-rw-r--r--t/t4034/cpp/pre41
-rw-r--r--t/t4034/kotlin/expect43
-rw-r--r--t/t4034/kotlin/post30
-rw-r--r--t/t4034/kotlin/pre30
-rwxr-xr-xt/t4035-diff-quiet.sh1
-rwxr-xr-xt/t4036-format-patch-signer-mime.sh1
-rwxr-xr-xt/t4037-diff-r-t-dirs.sh1
-rwxr-xr-xt/t4038-diff-combined.sh12
-rwxr-xr-xt/t4039-diff-assume-unchanged.sh1
-rwxr-xr-xt/t4040-whitespace-status.sh5
-rwxr-xr-xt/t4042-diff-textconv-caching.sh22
-rwxr-xr-xt/t4044-diff-index-unique-abbrev.sh6
-rwxr-xr-xt/t4045-diff-relative.sh31
-rwxr-xr-xt/t4046-diff-unmerged.sh22
-rwxr-xr-xt/t4047-diff-dirstat.sh24
-rwxr-xr-xt/t4049-diff-stat-count.sh4
-rwxr-xr-xt/t4050-diff-histogram.sh1
-rwxr-xr-xt/t4051-diff-function-context.sh1
-rwxr-xr-xt/t4052-stat-output.sh100
-rwxr-xr-xt/t4053-diff-no-index.sh94
-rwxr-xr-xt/t4054-diff-bogus-tree.sh4
-rwxr-xr-xt/t4055-diff-context.sh5
-rwxr-xr-xt/t4057-diff-combined-paths.sh17
-rwxr-xr-xt/t4058-diff-duplicates.sh2
-rwxr-xr-xt/t4059-diff-submodule-not-initialized.sh2
-rwxr-xr-xt/t4060-diff-submodule-option-diff-format.sh161
-rwxr-xr-xt/t4062-diff-pickaxe.sh3
-rwxr-xr-xt/t4063-diff-blobs.sh2
-rwxr-xr-xt/t4066-diff-emit-delay.sh1
-rwxr-xr-xt/t4067-diff-partial-clone.sh6
-rwxr-xr-xt/t4068-diff-symmetric-merge-base.sh37
-rwxr-xr-xt/t4069-remerge-diff.sh320
-rwxr-xr-xt/t4100-apply-stat.sh2
-rwxr-xr-xt/t4101-apply-nonl.sh2
-rwxr-xr-xt/t4102-apply-rename.sh2
-rwxr-xr-xt/t4103-apply-binary.sh1
-rwxr-xr-xt/t4104-apply-boundary.sh1
-rwxr-xr-xt/t4105-apply-fuzz.sh12
-rwxr-xr-xt/t4106-apply-stdin.sh7
-rwxr-xr-xt/t4108-apply-threeway.sh18
-rwxr-xr-xt/t4109-apply-multifrag.sh2
-rwxr-xr-xt/t4110-apply-scan.sh2
-rwxr-xr-xt/t4111-apply-subdir.sh1
-rwxr-xr-xt/t4112-apply-renames.sh2
-rwxr-xr-xt/t4113-apply-ending.sh1
-rwxr-xr-xt/t4114-apply-typechange.sh1
-rwxr-xr-xt/t4115-apply-symlink.sh97
-rwxr-xr-xt/t4116-apply-reverse.sh6
-rwxr-xr-xt/t4117-apply-reject.sh21
-rwxr-xr-xt/t4118-apply-empty-context.sh8
-rwxr-xr-xt/t4119-apply-config.sh2
-rwxr-xr-xt/t4120-apply-popt.sh4
-rwxr-xr-xt/t4121-apply-diffs.sh1
-rwxr-xr-xt/t4122-apply-symlink-inside.sh15
-rwxr-xr-xt/t4123-apply-shrink.sh17
-rwxr-xr-xt/t4124-apply-ws-rule.sh58
-rwxr-xr-xt/t4125-apply-ws-fuzz.sh5
-rwxr-xr-xt/t4126-apply-empty.sh52
-rwxr-xr-xt/t4127-apply-same-fn.sh7
-rwxr-xr-xt/t4128-apply-root.sh35
-rwxr-xr-xt/t4129-apply-samemode.sh39
-rwxr-xr-xt/t4130-apply-criss-cross-rename.sh2
-rwxr-xr-xt/t4132-apply-removal.sh2
-rwxr-xr-xt/t4133-apply-filenames.sh10
-rwxr-xr-xt/t4134-apply-submodule.sh2
-rwxr-xr-xt/t4135-apply-weird-filenames.sh1
-rwxr-xr-xt/t4136-apply-check.sh2
-rwxr-xr-xt/t4138-apply-ws-expansion.sh36
-rwxr-xr-xt/t4139-apply-escape.sh2
-rwxr-xr-xt/t4140-apply-ita.sh1
-rwxr-xr-xt/t4141-apply-too-large.sh23
-rwxr-xr-xt/t4150-am.sh179
-rwxr-xr-xt/t4151-am-abort.sh51
-rwxr-xr-xt/t4152-am-subjects.sh2
-rwxr-xr-xt/t4153-am-resume-override-opts.sh2
-rwxr-xr-xt/t4200-rerere.sh70
-rwxr-xr-xt/t4201-shortlog.sh73
-rwxr-xr-xt/t4202-log.sh536
-rwxr-xr-xt/t4203-mailmap.sh130
-rwxr-xr-xt/t4204-patch-id.sh218
-rwxr-xr-xt/t4205-log-pretty-formats.sh238
-rwxr-xr-xt/t4206-log-follow-harder-copies.sh38
-rwxr-xr-xt/t4207-log-decoration-colors.sh103
-rwxr-xr-xt/t4208-log-magic-pathspec.sh7
-rwxr-xr-xt/t4209-log-pickaxe.sh14
-rwxr-xr-xt/t4210-log-i18n.sh11
-rwxr-xr-xt/t4211-line-log.sh28
-rwxr-xr-xt/t4212-log-corrupt.sh66
-rwxr-xr-xt/t4213-log-tabexpand.sh1
-rwxr-xr-xt/t4214-log-graph-octopus.sh1
-rwxr-xr-xt/t4215-log-skewed-merges.sh1
-rwxr-xr-xt/t4216-log-bloom.sh57
-rwxr-xr-xt/t4217-log-limit.sh42
-rwxr-xr-xt/t4252-am-options.sh2
-rwxr-xr-xt/t4254-am-corrupt.sh4
-rwxr-xr-xt/t4256-am-format-flowed.sh3
-rwxr-xr-xt/t4257-am-interactive.sh2
-rwxr-xr-xt/t4258-am-quoted-cr.sh1
-rw-r--r--t/t4258/mbox2
-rwxr-xr-xt/t4300-merge-tree.sh47
-rwxr-xr-xt/t4301-merge-tree-write-tree.sh993
-rwxr-xr-xt/t5000-tar-tree.sh105
-rwxr-xr-xt/t5001-archive-attr.sh24
-rwxr-xr-xt/t5002-archive-attr-pattern.sh6
-rwxr-xr-xt/t5003-archive-zip.sh61
-rwxr-xr-xt/t5004-archive-corner-cases.sh8
-rwxr-xr-xt/t5100-mailinfo.sh30
-rw-r--r--t/t5100/comment.expect2
-rw-r--r--t/t5100/comment.in2
-rw-r--r--t/t5100/msg00022
-rw-r--r--t/t5100/msg00032
-rw-r--r--t/t5100/msg0012--message-id2
-rw-r--r--t/t5100/quoted-cr.mbox4
-rw-r--r--t/t5100/sample.mbox6
-rwxr-xr-xt/t5200-update-server-info.sh1
-rwxr-xr-xt/t5300-pack-object.sh417
-rwxr-xr-xt/t5301-sliding-window.sh102
-rwxr-xr-xt/t5302-pack-index.sh18
-rwxr-xr-xt/t5303-pack-corruption-resilience.sh586
-rwxr-xr-xt/t5304-prune.sh83
-rwxr-xr-xt/t5306-pack-nobase.sh96
-rwxr-xr-xt/t5307-pack-missing-commit.sh3
-rwxr-xr-xt/t5308-pack-detect-duplicates.sh2
-rwxr-xr-xt/t5309-pack-delta-cycles.sh2
-rwxr-xr-xt/t5310-pack-bitmaps.sh960
-rwxr-xr-xt/t5311-pack-bitmaps-shallow.sh55
-rwxr-xr-xt/t5312-prune-corruption.sh75
-rwxr-xr-xt/t5313-pack-bounds-checks.sh4
-rwxr-xr-xt/t5314-pack-cycle-detection.sh21
-rwxr-xr-xt/t5315-pack-objects-compression.sh1
-rwxr-xr-xt/t5316-pack-delta-depth.sh15
-rwxr-xr-xt/t5317-pack-objects-filter-objects.sh142
-rwxr-xr-xt/t5318-commit-graph.sh645
-rwxr-xr-xt/t5319-multi-pack-index.sh408
-rwxr-xr-xt/t5320-delta-islands.sh4
-rwxr-xr-xt/t5321-pack-large-objects.sh2
-rwxr-xr-xt/t5322-pack-objects-sparse.sh5
-rwxr-xr-xt/t5324-split-commit-graph.sh138
-rwxr-xr-xt/t5325-reverse-index.sh92
-rwxr-xr-xt/t5326-multi-pack-bitmaps.sh533
-rwxr-xr-xt/t5327-multi-pack-bitmaps-rev.sh43
-rwxr-xr-xt/t5328-commit-graph-64bit-time.sh87
-rwxr-xr-xt/t5329-pack-objects-cruft.sh947
-rwxr-xr-xt/t5330-no-lazy-fetch-with-commit-graph.sh48
-rwxr-xr-xt/t5331-pack-objects-stdin.sh240
-rwxr-xr-xt/t5332-multi-pack-reuse.sh207
-rwxr-xr-xt/t5351-unpack-large-objects.sh103
-rwxr-xr-xt/t5401-update-hooks.sh72
-rwxr-xr-xt/t5402-post-merge-hook.sh17
-rwxr-xr-xt/t5403-post-checkout-hook.sh71
-rwxr-xr-xt/t5404-tracking-branches.sh1
-rwxr-xr-xt/t5405-send-pack-rewind.sh1
-rwxr-xr-xt/t5406-remote-rejects.sh3
-rwxr-xr-xt/t5407-post-rewrite-hook.sh62
-rwxr-xr-xt/t5409-colorize-remote-messages.sh2
-rwxr-xr-xt/t5410-receive-pack-alternates.sh1
-rwxr-xr-xt/t5411-proc-receive-hook.sh4
-rw-r--r--t/t5411/once-0010-report-status-v1.sh2
-rw-r--r--t/t5411/test-0002-pre-receive-declined.sh4
-rw-r--r--t/t5411/test-0003-pre-receive-declined--porcelain.sh2
-rw-r--r--t/t5411/test-0013-bad-protocol.sh20
-rw-r--r--t/t5411/test-0014-bad-protocol--porcelain.sh18
-rw-r--r--t/t5411/test-0020-report-ng.sh4
-rw-r--r--t/t5411/test-0021-report-ng--porcelain.sh4
-rw-r--r--t/t5411/test-0022-report-unexpect-ref.sh2
-rw-r--r--t/t5411/test-0023-report-unexpect-ref--porcelain.sh2
-rw-r--r--t/t5411/test-0024-report-unknown-ref.sh2
-rw-r--r--t/t5411/test-0025-report-unknown-ref--porcelain.sh2
-rw-r--r--t/t5411/test-0026-push-options.sh8
-rw-r--r--t/t5411/test-0027-push-options--porcelain.sh8
-rw-r--r--t/t5411/test-0030-report-ok.sh2
-rw-r--r--t/t5411/test-0031-report-ok--porcelain.sh2
-rw-r--r--t/t5411/test-0032-report-with-options.sh14
-rw-r--r--t/t5411/test-0033-report-with-options--porcelain.sh14
-rw-r--r--t/t5411/test-0034-report-ft.sh2
-rw-r--r--t/t5411/test-0035-report-ft--porcelain.sh2
-rw-r--r--t/t5411/test-0036-report-multi-rewrite-for-one-ref.sh6
-rw-r--r--t/t5411/test-0037-report-multi-rewrite-for-one-ref--porcelain.sh6
-rw-r--r--t/t5411/test-0038-report-mixed-refs.sh2
-rw-r--r--t/t5411/test-0039-report-mixed-refs--porcelain.sh2
-rw-r--r--t/t5411/test-0040-process-all-refs.sh2
-rw-r--r--t/t5411/test-0041-process-all-refs--porcelain.sh2
-rw-r--r--t/t5411/test-0050-proc-receive-refs-with-modifiers.sh4
-rwxr-xr-xt/t5500-fetch-pack.sh84
-rwxr-xr-xt/t5502-quickfetch.sh3
-rwxr-xr-xt/t5503-tagfollow.sh1
-rwxr-xr-xt/t5504-fetch-receive-strict.sh46
-rwxr-xr-xt/t5505-remote.sh152
-rwxr-xr-xt/t5506-remote-groups.sh10
-rwxr-xr-xt/t5507-remote-environment.sh2
-rwxr-xr-xt/t5510-fetch.sh163
-rwxr-xr-xt/t5511-refspec.sh1
-rwxr-xr-xt/t5512-ls-remote.sh175
-rwxr-xr-xt/t5513-fetch-track.sh1
-rwxr-xr-xt/t5514-fetch-multiple.sh177
-rwxr-xr-xt/t5515-fetch-merge-logic.sh39
-rwxr-xr-xt/t5516-fetch-push.sh374
-rwxr-xr-xt/t5517-push-mirror.sh1
-rwxr-xr-xt/t5518-fetch-exit-status.sh1
-rwxr-xr-xt/t5520-pull.sh88
-rwxr-xr-xt/t5521-pull-options.sh31
-rwxr-xr-xt/t5522-pull-symlink.sh5
-rwxr-xr-xt/t5523-push-upstream.sh25
-rwxr-xr-xt/t5524-pull-msg.sh1
-rwxr-xr-xt/t5525-fetch-tagopt.sh1
-rwxr-xr-xt/t5526-fetch-submodules.sh621
-rwxr-xr-xt/t5527-fetch-odd-refs.sh1
-rwxr-xr-xt/t5528-push-default.sh79
-rwxr-xr-xt/t5529-push-errors.sh2
-rwxr-xr-xt/t5530-upload-pack-error.sh5
-rwxr-xr-xt/t5531-deep-submodule-push.sh55
-rwxr-xr-xt/t5532-fetch-proxy.sh2
-rwxr-xr-xt/t5534-push-signed.sh127
-rwxr-xr-xt/t5536-fetch-conflicts.sh8
-rwxr-xr-xt/t5537-fetch-shallow.sh24
-rwxr-xr-xt/t5540-http-push-webdav.sh10
-rwxr-xr-xt/t5541-http-push-smart.sh162
-rwxr-xr-xt/t5543-atomic-push.sh19
-rwxr-xr-xt/t5544-pack-objects-hook.sh9
-rwxr-xr-xt/t5545-push-options.sh6
-rwxr-xr-xt/t5546-receive-limits.sh37
-rwxr-xr-xt/t5547-push-quarantine.sh6
-rwxr-xr-xt/t5548-push-porcelain.sh2
-rwxr-xr-xt/t5550-http-fetch-dumb.sh51
-rwxr-xr-xt/t5551-http-fetch-smart.sh351
-rwxr-xr-xt/t5552-skipping-fetch-negotiator.sh26
-rwxr-xr-xt/t5553-set-upstream.sh22
-rwxr-xr-xt/t5554-noop-fetch-negotiator.sh2
-rwxr-xr-xt/t5555-http-smart-common.sh161
-rwxr-xr-xt/t5557-http-get.sh39
-rwxr-xr-xt/t5558-clone-bundle-uri.sh1076
-rwxr-xr-xt/t5559-http-fetch-smart-http2.sh5
-rwxr-xr-xt/t5560-http-backend-noserver.sh1
-rwxr-xr-xt/t5561-http-backend.sh1
-rwxr-xr-xt/t5562-http-backend-content-length.sh4
-rw-r--r--t/t5562/invoke-with-content-length.pl18
-rwxr-xr-xt/t5563-simple-http-auth.sh329
-rwxr-xr-xt/t5564-http-proxy.sh41
-rwxr-xr-xt/t5570-git-daemon.sh12
-rwxr-xr-xt/t5571-pre-push-hook.sh135
-rwxr-xr-xt/t5572-pull-submodule.sh35
-rwxr-xr-xt/t5573-pull-verify-signatures.sh28
-rwxr-xr-xt/t5574-fetch-output.sh304
-rwxr-xr-xt/t5580-unc-paths.sh3
-rwxr-xr-xt/t5583-push-branches.sh116
-rwxr-xr-xt/t5600-clone-fail-cleanup.sh5
-rwxr-xr-xt/t5601-clone.sh151
-rwxr-xr-xt/t5602-clone-remote-exec.sh1
-rwxr-xr-xt/t5603-clone-dirname.sh2
-rwxr-xr-xt/t5604-clone-reference.sh67
-rwxr-xr-xt/t5605-clone-local.sh41
-rwxr-xr-xt/t5606-clone-options.sh38
-rwxr-xr-xt/t5607-clone-bundle.sh4
-rwxr-xr-xt/t5609-clone-branch.sh1
-rwxr-xr-xt/t5610-clone-detached.sh1
-rwxr-xr-xt/t5611-clone-config.sh7
-rwxr-xr-xt/t5613-info-alternate.sh2
-rwxr-xr-xt/t5614-clone-submodules-shallow.sh9
-rwxr-xr-xt/t5615-alternate-env.sh2
-rwxr-xr-xt/t5616-partial-clone.sh195
-rwxr-xr-xt/t5617-clone-submodules-remote.sh43
-rwxr-xr-xt/t5618-alternate-refs.sh2
-rwxr-xr-xt/t5619-clone-local-ambiguous-transport.sh70
-rwxr-xr-xt/t5700-protocol-v1.sh46
-rwxr-xr-xt/t5701-git-serve.sh148
-rwxr-xr-xt/t5702-protocol-v2.sh252
-rwxr-xr-xt/t5703-upload-pack-ref-in-want.sh13
-rwxr-xr-xt/t5704-protocol-violations.sh21
-rwxr-xr-xt/t5705-session-id-in-capabilities.sh3
-rwxr-xr-xt/t5730-protocol-v2-bundle-uri-file.sh17
-rwxr-xr-xt/t5731-protocol-v2-bundle-uri-git.sh17
-rwxr-xr-xt/t5732-protocol-v2-bundle-uri-http.sh17
-rwxr-xr-xt/t5750-bundle-uri-parse.sh290
-rwxr-xr-xt/t5801-remote-helpers.sh8
-rwxr-xr-xt/t5801/git-remote-testgit9
-rwxr-xr-xt/t5810-proto-disable-local.sh2
-rwxr-xr-xt/t5811-proto-disable-git.sh2
-rwxr-xr-xt/t5812-proto-disable-http.sh4
-rwxr-xr-xt/t5813-proto-disable-ssh.sh2
-rwxr-xr-xt/t5815-submodule-protos.sh4
-rwxr-xr-xt/t5900-repo-selection.sh2
-rwxr-xr-xt/t6000-rev-list-misc.sh36
-rwxr-xr-xt/t6001-rev-list-graft.sh5
-rwxr-xr-xt/t6002-rev-list-bisect.sh1
-rwxr-xr-xt/t6003-rev-list-topo-order.sh24
-rwxr-xr-xt/t6005-rev-list-count.sh65
-rwxr-xr-xt/t6006-rev-list-format.sh2
-rwxr-xr-xt/t6007-rev-list-cherry-pick-file.sh26
-rwxr-xr-xt/t6008-rev-list-submodule.sh3
-rwxr-xr-xt/t6009-rev-list-parent.sh18
-rwxr-xr-xt/t6011-rev-list-with-bad-commit.sh1
-rwxr-xr-xt/t6012-rev-list-simplify.sh30
-rwxr-xr-xt/t6014-rev-list-all.sh1
-rwxr-xr-xt/t6017-rev-list-stdin.sh72
-rwxr-xr-xt/t6018-rev-list-glob.sh39
-rwxr-xr-xt/t6019-rev-list-ancestry-path.sh101
-rwxr-xr-xt/t6020-bundle-misc.sh190
-rwxr-xr-xt/t6021-rev-list-exclude-hidden.sh164
-rwxr-xr-xt/t6022-rev-list-missing.sh149
-rwxr-xr-xt/t6030-bisect-porcelain.sh327
-rwxr-xr-xt/t6040-tracking-info.sh19
-rwxr-xr-xt/t6050-replace.sh306
-rwxr-xr-xt/t6060-merge-index.sh6
-rwxr-xr-xt/t6100-rev-list-in-order.sh1
-rwxr-xr-xt/t6101-rev-parse-parents.sh5
-rwxr-xr-xt/t6102-rev-list-unexpected-objects.sh28
-rwxr-xr-xt/t6110-rev-list-sparse.sh1
-rwxr-xr-xt/t6111-rev-list-treesame.sh3
-rwxr-xr-xt/t6112-rev-list-filters-objects.sh26
-rwxr-xr-xt/t6113-rev-list-bitmap-filters.sh16
-rwxr-xr-xt/t6114-keep-packs.sh2
-rwxr-xr-xt/t6115-rev-list-du.sh29
-rwxr-xr-xt/t6120-describe.sh156
-rwxr-xr-xt/t6131-pathspec-icase.sh2
-rwxr-xr-xt/t6132-pathspec-exclude.sh183
-rwxr-xr-xt/t6134-pathspec-in-submodule.sh5
-rwxr-xr-xt/t6135-pathspec-with-attrs.sh189
-rwxr-xr-xt/t6136-pathspec-in-bare.sh9
-rwxr-xr-xt/t6200-fmt-merge-msg.sh144
-rwxr-xr-xt/t6300-for-each-ref.sh757
-rwxr-xr-xt/t6301-for-each-ref-errors.sh28
-rwxr-xr-xt/t6302-for-each-ref-filter.sh36
-rwxr-xr-xt/t6400-merge-df.sh2
-rwxr-xr-xt/t6401-merge-criss-cross.sh2
-rwxr-xr-xt/t6402-merge-rename.sh18
-rwxr-xr-xt/t6403-merge-file.sh184
-rwxr-xr-xt/t6404-recursive-merge.sh10
-rwxr-xr-xt/t6405-merge-symlinks.sh1
-rwxr-xr-xt/t6406-merge-attr.sh60
-rwxr-xr-xt/t6407-merge-binary.sh23
-rwxr-xr-xt/t6408-merge-up-to-date.sh1
-rwxr-xr-xt/t6409-merge-subtree.sh6
-rwxr-xr-xt/t6411-merge-filemode.sh9
-rwxr-xr-xt/t6412-merge-large-rename.sh10
-rwxr-xr-xt/t6413-merge-crlf.sh5
-rwxr-xr-xt/t6414-merge-rename-nocruft.sh1
-rwxr-xr-xt/t6415-merge-dir-to-symlink.sh7
-rwxr-xr-xt/t6416-recursive-corner-cases.sh69
-rwxr-xr-xt/t6417-merge-ours-theirs.sh6
-rwxr-xr-xt/t6418-merge-text-auto.sh27
-rwxr-xr-xt/t6421-merge-partial-clone.sh6
-rwxr-xr-xt/t6422-merge-rename-corner-cases.sh57
-rwxr-xr-xt/t6423-merge-rename-directories.sh447
-rwxr-xr-xt/t6424-merge-unrelated-index-changes.sh104
-rwxr-xr-xt/t6425-merge-rename-delete.sh5
-rwxr-xr-xt/t6426-merge-skip-unneeded-updates.sh76
-rwxr-xr-xt/t6427-diff3-conflict-markers.sh98
-rwxr-xr-xt/t6428-merge-conflicts-sparse.sh29
-rwxr-xr-xt/t6429-merge-sequence-rename-caching.sh126
-rwxr-xr-xt/t6430-merge-recursive.sh14
-rwxr-xr-xt/t6431-merge-criscross.sh1
-rwxr-xr-xt/t6433-merge-toplevel.sh5
-rwxr-xr-xt/t6435-merge-sparse.sh3
-rwxr-xr-xt/t6436-merge-overwrite.sh7
-rwxr-xr-xt/t6437-submodule-merge.sh94
-rwxr-xr-xt/t6439-merge-co-error-msgs.sh2
-rwxr-xr-xt/t6500-gc.sh218
-rwxr-xr-xt/t6501-freshen-objects.sh13
-rwxr-xr-xt/t6600-test-reach.sh173
-rwxr-xr-xt/t6700-tree-depth.sh95
-rwxr-xr-xt/t7001-mv.sh136
-rwxr-xr-xt/t7002-mv-sparse-checkout.sh517
-rwxr-xr-xt/t7003-filter-branch.sh9
-rwxr-xr-xt/t7004-tag.sh160
-rwxr-xr-xt/t7006-pager.sh59
-rwxr-xr-xt/t7007-show.sh1
-rwxr-xr-xt/t7008-filter-branch-null-sha1.sh1
-rwxr-xr-xt/t7010-setup.sh2
-rwxr-xr-xt/t7011-skip-worktree-reading.sh1
-rwxr-xr-xt/t7012-skip-worktree-writing.sh46
-rwxr-xr-xt/t7030-verify-tag.sh2
-rwxr-xr-xt/t7031-verify-tag-signed-ssh.sh213
-rwxr-xr-xt/t7060-wtstatus.sh1
-rwxr-xr-xt/t7062-wtstatus-ignorecase.sh1
-rwxr-xr-xt/t7063-status-untracked-cache.sh142
-rwxr-xr-xt/t7064-wtstatus-pv2.sh20
-rwxr-xr-xt/t7101-reset-empty-subdirs.sh51
-rwxr-xr-xt/t7102-reset.sh63
-rwxr-xr-xt/t7103-reset-bare.sh7
-rwxr-xr-xt/t7104-reset-hard.sh1
-rwxr-xr-xt/t7105-reset-patch.sh44
-rwxr-xr-xt/t7106-reset-unborn-branch.sh6
-rwxr-xr-xt/t7107-reset-pathspec-file.sh11
-rwxr-xr-xt/t7110-reset-merge.sh315
-rwxr-xr-xt/t7111-reset-table.sh21
-rwxr-xr-xt/t7112-reset-submodule.sh1
-rwxr-xr-xt/t7113-post-index-change-hook.sh8
-rwxr-xr-xt/t7201-co.sh227
-rwxr-xr-xt/t7300-clean.sh37
-rwxr-xr-xt/t7301-clean-interactive.sh491
-rwxr-xr-xt/t7400-submodule-basic.sh164
-rwxr-xr-xt/t7401-submodule-summary.sh1
-rwxr-xr-xt/t7402-submodule-rebase.sh33
-rwxr-xr-xt/t7403-submodule-sync.sh7
-rwxr-xr-xt/t7406-submodule-update.sh155
-rwxr-xr-xt/t7407-submodule-foreach.sh6
-rwxr-xr-xt/t7408-submodule-reference.sh18
-rwxr-xr-xt/t7409-submodule-detached-work-tree.sh5
-rwxr-xr-xt/t7411-submodule-config.sh43
-rwxr-xr-xt/t7412-submodule-absorbgitdirs.sh65
-rwxr-xr-xt/t7413-submodule-is-active.sh53
-rwxr-xr-xt/t7414-submodule-mistakes.sh13
-rwxr-xr-xt/t7416-submodule-dash-url.sh10
-rwxr-xr-xt/t7417-submodule-path-url.sh8
-rwxr-xr-xt/t7418-submodule-sparse-gitmodules.sh14
-rwxr-xr-xt/t7419-submodule-set-branch.sh59
-rwxr-xr-xt/t7420-submodule-set-url.sh32
-rwxr-xr-xt/t7421-submodule-summary-add.sh4
-rwxr-xr-xt/t7422-submodule-output.sh170
-rwxr-xr-xt/t7450-bad-git-dotfiles.sh46
-rwxr-xr-xt/t7500-commit-template-squash-signoff.sh15
-rwxr-xr-xt/t7501-commit-basic-functionality.sh95
-rwxr-xr-xt/t7502-commit-porcelain.sh74
-rwxr-xr-xt/t7503-pre-commit-and-pre-merge-commit-hooks.sh151
-rwxr-xr-xt/t7504-commit-msg-hook.sh56
-rwxr-xr-xt/t7505-prepare-commit-msg-hook.sh46
-rwxr-xr-xt/t7506-status-submodule.sh31
-rwxr-xr-xt/t7507-commit-verbose.sh17
-rwxr-xr-xt/t7508-status.sh229
-rwxr-xr-xt/t7509-commit-authorship.sh7
-rwxr-xr-xt/t7510-signed-commit.sh85
-rwxr-xr-xt/t7511-status-index.sh1
-rwxr-xr-xt/t7512-status-help.sh51
-rwxr-xr-xt/t7513-interpret-trailers.sh541
-rwxr-xr-xt/t7514-commit-patch.sh8
-rwxr-xr-xt/t7515-status-symlinks.sh1
-rwxr-xr-xt/t7516-commit-races.sh5
-rwxr-xr-xt/t7517-per-repo-email.sh14
-rwxr-xr-xt/t7518-ident-corner-cases.sh23
-rwxr-xr-xt/t7519-status-fsmonitor.sh133
-rwxr-xr-xt/t7520-ignored-hook-warning.sh20
-rwxr-xr-xt/t7524-commit-summary.sh31
-rwxr-xr-xt/t7525-status-rename.sh55
-rwxr-xr-xt/t7526-commit-pathspec-file.sh15
-rwxr-xr-xt/t7527-builtin-fsmonitor.sh1263
-rwxr-xr-xt/t7528-signed-commit-ssh.sh462
-rwxr-xr-xt/t7600-merge.sh84
-rwxr-xr-xt/t7601-merge-pull-config.sh96
-rwxr-xr-xt/t7602-merge-octopus-many.sh5
-rwxr-xr-xt/t7603-merge-reduce-heads.sh5
-rwxr-xr-xt/t7604-merge-custom-message.sh1
-rwxr-xr-xt/t7605-merge-resolve.sh1
-rwxr-xr-xt/t7607-merge-state.sh33
-rwxr-xr-xt/t7608-merge-messages.sh1
-rwxr-xr-xt/t7609-mergetool--lib.sh15
-rwxr-xr-xt/t7610-mergetool.sh42
-rwxr-xr-xt/t7611-merge-abort.sh4
-rwxr-xr-xt/t7612-merge-verify-signatures.sh25
-rwxr-xr-xt/t7614-merge-signoff.sh1
-rwxr-xr-xt/t7700-repack.sh618
-rwxr-xr-xt/t7701-repack-unpack-unreachable.sh47
-rwxr-xr-xt/t7702-repack-cyclic-alternate.sh2
-rwxr-xr-xt/t7703-repack-geometric.sh273
-rwxr-xr-xt/t7704-repack-cruft.sh414
-rwxr-xr-xt/t7800-difftool.sh232
-rwxr-xr-xt/t7810-grep.sh646
-rwxr-xr-xt/t7811-grep-open.sh5
-rwxr-xr-xt/t7812-grep-icase-non-ascii.sh33
-rwxr-xr-xt/t7813-grep-icase-iso.sh1
-rwxr-xr-xt/t7814-grep-recurse-submodules.sh198
-rwxr-xr-xt/t7815-grep-binary.sh1
-rwxr-xr-xt/t7816-grep-binary-pattern.sh5
-rwxr-xr-xt/t7817-grep-sparse-checkout.sh11
-rwxr-xr-xt/t7900-maintenance.sh328
-rwxr-xr-xt/t8001-annotate.sh1
-rwxr-xr-xt/t8002-blame.sh3
-rwxr-xr-xt/t8003-blame-corner-cases.sh14
-rwxr-xr-xt/t8007-cat-file-textconv.sh44
-rwxr-xr-xt/t8010-cat-file-filters.sh4
-rwxr-xr-xt/t8012-blame-colors.sh1
-rwxr-xr-xt/t8013-blame-ignore-revs.sh34
-rwxr-xr-xt/t8014-blame-ignore-fuzzy.sh4
-rwxr-xr-xt/t9001-send-email.sh279
-rwxr-xr-xt/t9002-column.sh12
-rwxr-xr-xt/t9003-help-autocorrect.sh8
-rwxr-xr-xt/t9004-example.sh2
-rwxr-xr-xt/t9100-git-svn-basic.sh31
-rwxr-xr-xt/t9101-git-svn-props.sh1
-rwxr-xr-xt/t9102-git-svn-deep-rmdir.sh1
-rwxr-xr-xt/t9104-git-svn-follow-parent.sh67
-rwxr-xr-xt/t9106-git-svn-commit-diff-clobber.sh1
-rwxr-xr-xt/t9107-git-svn-migrate.sh8
-rwxr-xr-xt/t9114-git-svn-dcommit-merge.sh2
-rwxr-xr-xt/t9116-git-svn-log.sh1
-rwxr-xr-xt/t9117-git-svn-init-clone.sh16
-rwxr-xr-xt/t9118-git-svn-funky-branch-names.sh2
-rwxr-xr-xt/t9119-git-svn-info.sh2
-rwxr-xr-xt/t9122-git-svn-author.sh1
-rwxr-xr-xt/t9127-git-svn-partial-rebuild.sh1
-rwxr-xr-xt/t9128-git-svn-cmd-branch.sh1
-rwxr-xr-xt/t9129-git-svn-i18n-commitencoding.sh1
-rwxr-xr-xt/t9130-git-svn-authors-file.sh6
-rwxr-xr-xt/t9133-git-svn-nested-git-repo.sh8
-rwxr-xr-xt/t9134-git-svn-ignore-paths.sh24
-rwxr-xr-xt/t9138-git-svn-authors-prog.sh2
-rwxr-xr-xt/t9139-git-svn-non-utf8-commitencoding.sh1
-rwxr-xr-xt/t9140-git-svn-reset.sh4
-rwxr-xr-xt/t9146-git-svn-empty-dirs.sh61
-rwxr-xr-xt/t9147-git-svn-include-paths.sh24
-rwxr-xr-xt/t9151-svn-mergeinfo.sh3
-rwxr-xr-xt/t9152-svn-empty-dirs-after-gc.sh2
-rwxr-xr-xt/t9162-git-svn-dcommit-interactive.sh1
-rwxr-xr-xt/t9164-git-svn-dcommit-concurrent.sh10
-rwxr-xr-xt/t9167-git-svn-cmd-branch-subproject.sh1
-rwxr-xr-xt/t9200-git-cvsexportcommit.sh351
-rwxr-xr-xt/t9210-scalar.sh241
-rwxr-xr-xt/t9211-scalar-clone.sh195
-rwxr-xr-xt/t9300-fast-import.sh683
-rwxr-xr-xt/t9301-fast-import-notes.sh1
-rwxr-xr-xt/t9302-fast-import-unpack-limit.sh2
-rwxr-xr-xt/t9303-fast-import-compression.sh2
-rwxr-xr-xt/t9304-fast-import-marks.sh32
-rwxr-xr-xt/t9350-fast-export.sh57
-rwxr-xr-xt/t9351-fast-export-anonymize.sh2
-rwxr-xr-xt/t9400-git-cvsserver-server.sh537
-rwxr-xr-xt/t9500-gitweb-standalone-no-errors.sh5
-rwxr-xr-xt/t9501-gitweb-standalone-http-status.sh1
-rwxr-xr-xt/t9502-gitweb-standalone-parse-output.sh29
-rwxr-xr-xt/t9603-cvsimport-patchsets.sh3
-rwxr-xr-xt/t9604-cvsimport-timestamps.sh29
-rwxr-xr-xt/t9700-perl-git.sh90
-rwxr-xr-xt/t9700/test.pl14
-rwxr-xr-xt/t9800-git-p4-basic.sh118
-rwxr-xr-xt/t9801-git-p4-branch.sh12
-rwxr-xr-xt/t9802-git-p4-filetype.sh36
-rwxr-xr-xt/t9807-git-p4-submit.sh14
-rwxr-xr-xt/t9810-git-p4-rcs.sh15
-rwxr-xr-xt/t9814-git-p4-rename.sh2
-rwxr-xr-xt/t9815-git-p4-submit-fail.sh16
-rwxr-xr-xt/t9816-git-p4-locked.sh2
-rwxr-xr-xt/t9818-git-p4-block.sh6
-rwxr-xr-xt/t9824-git-p4-git-lfs.sh4
-rwxr-xr-xt/t9835-git-p4-metadata-encoding-python2.sh213
-rwxr-xr-xt/t9836-git-p4-metadata-encoding-python3.sh214
-rwxr-xr-xt/t9850-shell.sh37
-rwxr-xr-xt/t9901-git-web--browse.sh1
-rwxr-xr-xt/t9902-completion.sh586
-rwxr-xr-xt/t9903-bash-prompt.sh26
-rw-r--r--t/test-lib-functions.sh569
-rw-r--r--t/test-lib-github-workflow-markup.sh56
-rw-r--r--t/test-lib-junit.sh132
-rw-r--r--t/test-lib.sh643
-rwxr-xr-xt/test-terminal.perl2
-rw-r--r--t/unit-tests/.gitignore1
-rw-r--r--t/unit-tests/t-basic.c95
-rw-r--r--t/unit-tests/t-ctype.c53
-rw-r--r--t/unit-tests/t-mem-pool.c31
-rw-r--r--t/unit-tests/t-prio-queue.c91
-rw-r--r--t/unit-tests/t-strbuf.c120
-rw-r--r--t/unit-tests/test-lib.c407
-rw-r--r--t/unit-tests/test-lib.h149
-rwxr-xr-xt/valgrind/valgrind.sh2
1279 files changed, 63178 insertions, 15543 deletions
diff --git a/t/.gitattributes b/t/.gitattributes
index dafa17c..b9cea17 100644
--- a/t/.gitattributes
+++ b/t/.gitattributes
@@ -1,6 +1,5 @@
t[0-9][0-9][0-9][0-9]/* -whitespace
/chainlint/*.expect eol=lf
-/lib-diff/* eol=lf
/t0110/url-* binary
/t3206/* eol=lf
/t3900/*.txt eol=lf
@@ -23,3 +22,4 @@ t[0-9][0-9][0-9][0-9]/* -whitespace
/t7500/* eol=lf
/t8005/*.txt eol=lf
/t9*/*.dump eol=lf
+/t0040*.sh whitespace=-indent-with-non-tab
diff --git a/t/Makefile b/t/Makefile
index 882d26e..2d95046 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -1,8 +1,15 @@
+# The default target of this Makefile is...
+all::
+
+# Import tree-wide shared Makefile behavior and libraries
+include ../shared.mak
+
# Run tests
#
# Copyright (c) 2005 Junio C Hamano
#
+-include ../config.mak.uname
-include ../config.mak.autogen
-include ../config.mak
@@ -14,6 +21,7 @@ TAR ?= $(TAR)
RM ?= rm -f
PROVE ?= prove
DEFAULT_TEST_TARGET ?= test
+DEFAULT_UNIT_TEST_TARGET ?= unit-tests-raw
TEST_LINT ?= test-lint
ifdef TEST_OUTPUT_DIRECTORY
@@ -32,16 +40,25 @@ 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)))
+TLIBS = $(sort $(wildcard lib-*.sh)) annotate-tests.sh
TPERF = $(sort $(wildcard perf/p[0-9][0-9][0-9][0-9]-*.sh))
+TINTEROP = $(sort $(wildcard interop/i[0-9][0-9][0-9][0-9]-*.sh))
CHAINLINTTESTS = $(sort $(patsubst chainlint/%.test,%,$(wildcard chainlint/*.test)))
-CHAINLINT = sed -f chainlint.sed
+CHAINLINT = '$(PERL_PATH_SQ)' chainlint.pl
+UNIT_TEST_SOURCES = $(wildcard unit-tests/t-*.c)
+UNIT_TEST_PROGRAMS = $(patsubst unit-tests/%.c,unit-tests/bin/%$(X),$(UNIT_TEST_SOURCES))
+UNIT_TESTS = $(sort $(filter-out unit-tests/bin/t-basic%,$(UNIT_TEST_PROGRAMS)))
+
+# `test-chainlint` (which is a dependency of `test-lint`, `test` and `prove`)
+# checks all tests in all scripts via a single invocation, so tell individual
+# scripts not to run the external "chainlint.pl" script themselves
+CHAINLINTSUPPRESS = GIT_TEST_EXT_CHAIN_LINT=0 && export GIT_TEST_EXT_CHAIN_LINT &&
-all: $(DEFAULT_TEST_TARGET)
+all:: $(DEFAULT_TEST_TARGET)
test: pre-clean check-chainlint $(TEST_LINT)
- $(MAKE) aggregate-results-and-cleanup
+ $(CHAINLINTSUPPRESS) $(MAKE) aggregate-results-and-cleanup
failed:
@failed=$$(cd '$(TEST_RESULTS_DIRECTORY_SQ)' && \
@@ -50,20 +67,32 @@ failed:
test -z "$$failed" || $(MAKE) $$failed
prove: pre-clean check-chainlint $(TEST_LINT)
- @echo "*** prove ***"; $(PROVE) --exec '$(TEST_SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)
+ @echo "*** prove ***"; $(CHAINLINTSUPPRESS) $(PROVE) --exec '$(TEST_SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)
$(MAKE) clean-except-prove-cache
$(T):
@echo "*** $@ ***"; '$(TEST_SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
+$(UNIT_TESTS):
+ @echo "*** $@ ***"; $@
+
+.PHONY: unit-tests unit-tests-raw unit-tests-prove
+unit-tests: $(DEFAULT_UNIT_TEST_TARGET)
+
+unit-tests-raw: $(UNIT_TESTS)
+
+unit-tests-prove:
+ @echo "*** prove - unit tests ***"; $(PROVE) $(GIT_PROVE_OPTS) $(UNIT_TESTS)
+
pre-clean:
$(RM) -r '$(TEST_RESULTS_DIRECTORY_SQ)'
clean-except-prove-cache: clean-chainlint
- $(RM) -r 'trash directory'.* '$(TEST_RESULTS_DIRECTORY_SQ)'
+ $(RM) -r 'trash directory'.*
$(RM) -r valgrind/bin
clean: clean-except-prove-cache
+ $(RM) -r '$(TEST_RESULTS_DIRECTORY_SQ)'
$(RM) .prove
clean-chainlint:
@@ -71,15 +100,27 @@ clean-chainlint:
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
+ echo "test_expect_success '$$i' '" && \
+ sed -e '/^# LINT: /d' chainlint/$$i.test && \
+ echo "'"; \
+ done >'$(CHAINLINTTMP_SQ)'/tests && \
+ { \
+ echo "# chainlint: $(CHAINLINTTMP_SQ)/tests" && \
+ for i in $(CHAINLINTTESTS); do \
+ echo "# chainlint: $$i" && \
+ cat chainlint/$$i.expect; \
+ done \
+ } >'$(CHAINLINTTMP_SQ)'/expect && \
+ $(CHAINLINT) --emit-all '$(CHAINLINTTMP_SQ)'/tests | \
+ sed -e 's/^[1-9][0-9]* //' >'$(CHAINLINTTMP_SQ)'/actual && \
+ diff -u '$(CHAINLINTTMP_SQ)'/expect '$(CHAINLINTTMP_SQ)'/actual
test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax \
test-lint-filenames
+ifneq ($(GIT_TEST_CHAIN_LINT),0)
+test-lint: test-chainlint
+endif
test-lint-duplicates:
@dups=`echo $(T) $(TPERF) | tr ' ' '\n' | sed 's/-.*//' | sort | uniq -d` && \
@@ -102,17 +143,15 @@ test-lint-filenames:
test -z "$$bad" || { \
echo >&2 "non-portable file name(s): $$bad"; exit 1; }
+test-chainlint:
+ @$(CHAINLINT) $(T) $(TLIBS) $(TPERF) $(TINTEROP)
+
aggregate-results-and-cleanup: $(T)
$(MAKE) aggregate-results
$(MAKE) clean
aggregate-results:
- for f in '$(TEST_RESULTS_DIRECTORY_SQ)'/t*-*.counts; do \
- echo "$$f"; \
- done | '$(SHELL_PATH_SQ)' ./aggregate-results.sh
-
-gitweb-test:
- $(MAKE) $(TGITWEB)
+ @'$(SHELL_PATH_SQ)' ./aggregate-results.sh '$(TEST_RESULTS_DIRECTORY_SQ)'
valgrind:
$(MAKE) GIT_TEST_OPTS="$(GIT_TEST_OPTS) --valgrind"
@@ -120,4 +159,5 @@ valgrind:
perf:
$(MAKE) -C perf/ all
-.PHONY: pre-clean $(T) aggregate-results clean valgrind perf check-chainlint clean-chainlint
+.PHONY: pre-clean $(T) aggregate-results clean valgrind perf \
+ check-chainlint clean-chainlint test-chainlint $(UNIT_TESTS)
diff --git a/t/README b/t/README
index e924bd8..d9e0e07 100644
--- a/t/README
+++ b/t/README
@@ -32,7 +32,14 @@ the tests.
ok 2 - plain with GIT_WORK_TREE
ok 3 - plain bare
-Since the tests all output TAP (see http://testanything.org) they can
+t/Makefile defines a target for each test file, such that you can also use
+shell pattern matching to run a subset of the tests:
+
+ make *checkout*
+
+will run all tests with 'checkout' in their filename.
+
+Since the tests all output TAP (see https://testanything.org) they can
be run with any TAP harness. Here's an example of parallel testing
powered by a recent version of prove(1):
@@ -196,11 +203,6 @@ appropriately before running "make". Short options can be bundled, i.e.
this feature by setting the GIT_TEST_CHAIN_LINT environment
variable to "1" or "0", respectively.
- A few test scripts disable some of the more advanced
- chain-linting detection in the name of efficiency. You can
- override this by setting the GIT_TEST_CHAIN_LINT_HARDER
- environment variable to "1".
-
--stress::
Run the test script repeatedly in multiple parallel jobs until
one of them fails. Useful for reproducing rare failures in
@@ -267,8 +269,8 @@ The argument for --run, <test-selector>, is a list of description
substrings or globs or individual test numbers or ranges with an
optional negation prefix (of '!') that define what tests in a test
suite to include (or exclude, if negated) in the run. A range is two
-numbers separated with a dash and matches a range of tests with both
-ends been included. You may omit the first or the second number to
+numbers separated with a dash and specifies an inclusive range of tests
+to run. You may omit the first or the second number to
mean "from the first test" or "up to the very last test" respectively.
The argument to --run is split on commas into separate strings,
@@ -279,10 +281,10 @@ text that you want to match includes a comma, use the glob character
on all tests that match either the glob *rebase* or the glob
*merge?cherry-pick*.
-If --run starts with an unprefixed number or range the initial
-set of tests to run is empty. If the first item starts with '!'
+If --run starts with an unprefixed number or range, the initial
+set of tests to run is empty. If the first item starts with '!',
all the tests are added to the initial set. After initial set is
-determined every test number or range is added or excluded from
+determined, every test number or range is added or excluded from
the set one by one, from left to right.
For example, to run only tests up to a specific test (21), one
@@ -366,6 +368,48 @@ excluded as so much relies on it, but this might change in the future.
GIT_TEST_SPLIT_INDEX=<boolean> forces split-index mode on the whole
test suite. Accept any boolean values that are accepted by git-config.
+GIT_TEST_PASSING_SANITIZE_LEAK=true skips those tests that haven't
+declared themselves as leak-free by setting
+"TEST_PASSES_SANITIZE_LEAK=true" before sourcing "test-lib.sh". This
+test mode is used by the "linux-leaks" CI target.
+
+GIT_TEST_PASSING_SANITIZE_LEAK=check checks that our
+"TEST_PASSES_SANITIZE_LEAK=true" markings are current. Rather than
+skipping those tests that haven't set "TEST_PASSES_SANITIZE_LEAK=true"
+before sourcing "test-lib.sh" this mode runs them with
+"--invert-exit-code". This is used to check that there's a one-to-one
+mapping between "TEST_PASSES_SANITIZE_LEAK=true" and those tests that
+pass under "SANITIZE=leak". This is especially useful when testing a
+series that fixes various memory leaks with "git rebase -x".
+
+GIT_TEST_SANITIZE_LEAK_LOG=true will log memory leaks to
+"test-results/$TEST_NAME.leak/trace.*" files. The logs include a
+"dedup_token" (see +"ASAN_OPTIONS=help=1 ./git") and other options to
+make logs +machine-readable.
+
+With GIT_TEST_SANITIZE_LEAK_LOG=true we'll look at the leak logs
+before exiting and exit on failure if the logs showed that we had a
+memory leak, even if the test itself would have otherwise passed. This
+allows us to catch e.g. missing &&-chaining. This is especially useful
+when combined with "GIT_TEST_PASSING_SANITIZE_LEAK", see below.
+
+GIT_TEST_PASSING_SANITIZE_LEAK=check when combined with "--immediate"
+will run to completion faster, and result in the same failing
+tests. The only practical reason to run
+GIT_TEST_PASSING_SANITIZE_LEAK=check without "--immediate" is to
+combine it with "GIT_TEST_SANITIZE_LEAK_LOG=true". If we stop at the
+first failing test case our leak logs won't show subsequent leaks we
+might have run into.
+
+GIT_TEST_PASSING_SANITIZE_LEAK=(true|check) will not catch all memory
+leaks unless combined with GIT_TEST_SANITIZE_LEAK_LOG=true. Some tests
+run "git" (or "test-tool" etc.) without properly checking the exit
+code, or git will invoke itself and fail to ferry the abort() exit
+code to the original caller. When the two modes are combined we'll
+look at the "test-results/$TEST_NAME.leak/trace.*" files at the end of
+the test run to see if had memory leaks which the test itself didn't
+catch.
+
GIT_TEST_PROTOCOL_VERSION=<n>, when set, makes 'protocol.version'
default to n.
@@ -398,13 +442,17 @@ every 'git commit-graph write', as if the `--changed-paths` option was
passed in.
GIT_TEST_FSMONITOR=$PWD/t7519/fsmonitor-all exercises the fsmonitor
-code path for utilizing a file system monitor to speed up detecting
-new or changed files.
+code paths for utilizing a (hook based) file system monitor to speed up
+detecting new or changed files.
GIT_TEST_INDEX_VERSION=<n> exercises the index read/write code path
for the index version specified. Can be set to any valid version
(currently 2, 3, or 4).
+GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=<boolean> if enabled will
+use the boundary-based bitmap traversal algorithm. See the documentation
+of `pack.useBitmapBoundaryTraversal` for more details.
+
GIT_TEST_PACK_SPARSE=<boolean> if disabled will default the pack-objects
builtin to use the non-sparse object walk. This can still be overridden by
the --sparse command-line argument.
@@ -412,10 +460,6 @@ the --sparse command-line argument.
GIT_TEST_PRELOAD_INDEX=<boolean> exercises the preload-index code path
by overriding the minimum number of cache entries required per thread.
-GIT_TEST_ADD_I_USE_BUILTIN=<boolean>, when true, enables the
-built-in version of git add -i. See 'add.interactive.useBuiltin' in
-git-config(1).
-
GIT_TEST_INDEX_THREADS=<n> enables exercising the multi-threaded loading
of the index for the whole test suite by bypassing the default number of
cache entries and thread minimums. Setting this to 1 will make the
@@ -425,6 +469,10 @@ GIT_TEST_MULTI_PACK_INDEX=<boolean>, when true, forces the multi-pack-
index to be written after every 'git repack' command, and overrides the
'core.multiPackIndex' setting to true.
+GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=<boolean>, when true, sets the
+'--bitmap' option on all invocations of 'git multi-pack-index write',
+and ignores pack-objects' '--write-bitmap-index'.
+
GIT_TEST_SIDEBAND_ALL=<boolean>, when true, overrides the
'uploadpack.allowSidebandAll' setting to true, and when false, forces
fetch-pack to not request sideband-all (even if the server advertises
@@ -438,7 +486,10 @@ GIT_TEST_DEFAULT_HASH=<hash-algo> specifies which hash algorithm to
use in the test scripts. Recognized values for <hash-algo> are "sha1"
and "sha256".
-GIT_TEST_WRITE_REV_INDEX=<boolean>, when true enables the
+GIT_TEST_DEFAULT_REF_FORMAT=<format> specifies which ref storage format
+to use in the test scripts. Recognized values for <format> are "files".
+
+GIT_TEST_NO_WRITE_REV_INDEX=<boolean>, when true disables the
'pack.writeReverseIndex' setting.
GIT_TEST_SPARSE_INDEX=<boolean>, when true enables index writes to use the
@@ -448,6 +499,19 @@ GIT_TEST_CHECKOUT_WORKERS=<n> overrides the 'checkout.workers' setting
to <n> and 'checkout.thresholdForParallelism' to 0, forcing the
execution of the parallel-checkout code.
+GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=<boolean>, when true, makes
+registering submodule ODBs as alternates a fatal action. Support for
+this environment variable can be removed once the migration to
+explicitly providing repositories when accessing submodule objects is
+complete or needs to be abandoned for whatever reason (in which case the
+migrated codepaths still retain their performance benefits).
+
+GIT_TEST_REQUIRE_PREREQ=<list> allows specifying a space separated list of
+prereqs that are required to succeed. If a prereq in this list is triggered by
+a test and then fails then the whole test run will abort. This can help to make
+sure the expected tests are executed and not silently skipped when their
+dependency breaks or is simply not present in a new environment.
+
Naming Tests
------------
@@ -523,13 +587,67 @@ This test harness library does the following things:
consistently when command line arguments --verbose (or -v),
--debug (or -d), and --immediate (or -i) is given.
+Recommended style
+-----------------
+
+ - Keep the test_expect_* function call and test title on
+ the same line.
+
+ For example, with test_expect_success, write it like:
+
+ test_expect_success 'test title' '
+ ... test body ...
+ '
+
+ Instead of:
+
+ test_expect_success \
+ 'test title' \
+ '... test body ...'
+
+ - End the line with an opening single quote.
+
+ - Indent here-document bodies, and use "<<-" instead of "<<"
+ to strip leading TABs used for indentation:
+
+ test_expect_success 'test something' '
+ cat >expect <<-\EOF &&
+ one
+ two
+ three
+ EOF
+ test_something > actual &&
+ test_cmp expect actual
+ '
+
+ Instead of:
+
+ test_expect_success 'test something' '
+ cat >expect <<\EOF &&
+ one
+ two
+ three
+ EOF
+ test_something > actual &&
+ test_cmp expect actual
+ '
+
+ - Quote or escape the EOF delimiter that begins a here-document if
+ there is no parameter or other expansion in it, to signal readers
+ that they can skim it more casually:
+
+ cmd <<-\EOF
+ literal here-document text without any expansion
+ EOF
+
+
Do's & don'ts
-------------
Here are a few examples of things you probably should and shouldn't do
when writing tests.
-Here are the "do's:"
+The "do's:"
- Put all code inside test_expect_success and other assertions.
@@ -613,6 +731,26 @@ Here are the "do's:"
Note that we still &&-chain the loop to propagate failures from
earlier commands.
+ - Repeat tests with slightly different arguments in a loop.
+
+ In some cases it may make sense to re-run the same set of tests with
+ different options or commands to ensure that the command behaves
+ despite the different parameters. This can be achieved by looping
+ around a specific parameter:
+
+ for arg in '' "--foo"
+ do
+ test_expect_success "test command ${arg:-without arguments}" '
+ command $arg
+ '
+ done
+
+ Note that while the test title uses double quotes ("), the test body
+ should continue to use single quotes (') to avoid breakage in case the
+ values contain e.g. quoting characters. The loop variable will be
+ accessible regardless of the single quotes as the test body is passed
+ to `eval`.
+
And here are the "don'ts:"
@@ -779,7 +917,7 @@ see test-lib-functions.sh for the full list and their options.
rare case where your test depends on more than one:
test_expect_success PERL,PYTHON 'yo dawg' \
- ' test $(perl -E 'print eval "1 +" . qx[python -c "print 2"]') == "4" '
+ ' test $(perl -E '\''print eval "1 +" . qx[python -c "print(2)"]'\'') = "4" '
- test_expect_failure [<prereq>] <message> <script>
@@ -856,32 +994,6 @@ see test-lib-functions.sh for the full list and their options.
test_done
fi
- - test_external [<prereq>] <message> <external> <script>
-
- Execute a <script> with an <external> interpreter (like perl). This
- was added for tests like t9700-perl-git.sh which do most of their
- work in an external test script.
-
- test_external \
- 'GitwebCache::*FileCache*' \
- perl "$TEST_DIRECTORY"/t9503/test_cache_interface.pl
-
- If the test is outputting its own TAP you should set the
- test_external_has_tap variable somewhere before calling the first
- test_external* function. See t9700-perl-git.sh for an example.
-
- # The external test will outputs its own plan
- test_external_has_tap=1
-
- - test_external_without_stderr [<prereq>] <message> <external> <script>
-
- Like test_external but fail if there's any output on stderr,
- instead of checking the exit code.
-
- test_external_without_stderr \
- 'Perl API' \
- perl "$TEST_DIRECTORY"/t9700/test.pl
-
- test_expect_code <exit-code> <command>
Run a command and ensure that it exits with the given exit code.
@@ -1019,6 +1131,12 @@ see test-lib-functions.sh for the full list and their options.
the symbolic link in the file system and a part that does; then only
the latter part need be protected by a SYMLINKS prerequisite (see below).
+ - test_path_is_executable
+
+ This tests whether a file is executable and prints an error message
+ if not. This must be used only under the POSIXPERM prerequisite
+ (see below).
+
- test_oid_init
This function loads facts and useful object IDs related to the hash
@@ -1148,8 +1266,8 @@ and it knows that the object ID of an empty tree is a certain
because the things the very basic core test tries to achieve is
to serve as a basis for people who are changing the Git internals
drastically. For these people, after making certain changes,
-not seeing failures from the basic test _is_ a failure. And
-such drastic changes to the core Git that even changes these
+not seeing failures from the basic test _is_ a failure. Any
+Git core changes so drastic that they change even these
otherwise supposedly stable object IDs should be accompanied by
an update to t0000-basic.sh.
@@ -1159,7 +1277,7 @@ knowledge of the core Git internals. If all the test scripts
hardcoded the object IDs like t0000-basic.sh does, that defeats
the purpose of t0000-basic.sh, which is to isolate that level of
validation in one place. Your test also ends up needing
-updating when such a change to the internal happens, so do _not_
+an update whenever the internals change, so do _not_
do it and leave the low level of validation to t0000-basic.sh.
Test coverage
@@ -1190,7 +1308,7 @@ Devel::Cover module. To install it do:
sudo aptitude install libdevel-cover-perl
# From the CPAN with cpanminus
- curl -L http://cpanmin.us | perl - --sudo --self-upgrade
+ curl -L https://cpanmin.us/ | perl - --sudo --self-upgrade
cpanm --sudo Devel::Cover
Then, at the top-level:
diff --git a/t/aggregate-results.sh b/t/aggregate-results.sh
index 7913e20..6e3bcc4 100755
--- a/t/aggregate-results.sh
+++ b/t/aggregate-results.sh
@@ -6,8 +6,9 @@ success=0
failed=0
broken=0
total=0
+missing_prereq=
-while read file
+for file in "$1"/t*-*.counts
do
while read type value
do
@@ -30,10 +31,26 @@ do
broken=$(($broken + $value)) ;;
total)
total=$(($total + $value)) ;;
+ missing_prereq)
+ missing_prereq="$missing_prereq,$value" ;;
esac
done <"$file"
done
+if test -n "$missing_prereq"
+then
+ unique_missing_prereq=$(
+ echo $missing_prereq |
+ tr -s "," "\n" |
+ grep -v '^$' |
+ sort -u |
+ paste -s -d ' ')
+ if test -n "$unique_missing_prereq"
+ then
+ printf "\nmissing prereq: $unique_missing_prereq\n\n"
+ fi
+fi
+
if test -n "$failed_tests"
then
printf "\nfailed test(s):$failed_tests\n\n"
diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh
index d3b299e..8757245 100644
--- a/t/annotate-tests.sh
+++ b/t/annotate-tests.sh
@@ -56,6 +56,10 @@ check_count () {
' "$@" <actual
}
+get_progress_result () {
+ tr '\015' '\012' | tail -n 1
+}
+
test_expect_success 'setup A lines' '
echo "1A quick brown fox jumps over the" >file &&
echo "lazy dog" >>file &&
@@ -68,6 +72,32 @@ test_expect_success 'blame 1 author' '
check_count A 2
'
+test_expect_success 'blame working copy' '
+ test_when_finished "git restore file" &&
+ echo "1A quick brown fox jumps over the" >file &&
+ echo "another lazy dog" >>file &&
+ check_count A 1 "Not Committed Yet" 1
+'
+
+test_expect_success 'blame with --contents' '
+ check_count --contents=file A 2
+'
+
+test_expect_success 'blame with --contents in a bare repo' '
+ git clone --bare . bare-contents.git &&
+ (
+ cd bare-contents.git &&
+ echo "1A quick brown fox jumps over the" >contents &&
+ check_count --contents=contents A 1
+ )
+'
+
+test_expect_success 'blame with --contents changed' '
+ echo "1A quick brown fox jumps over the" >contents &&
+ echo "another lazy dog" >>contents &&
+ check_count --contents=contents A 1 "External file (--contents)" 1
+'
+
test_expect_success 'blame in a bare repo without starting commit' '
git clone --bare . bare.git &&
(
@@ -94,6 +124,10 @@ test_expect_success 'blame 2 authors' '
check_count A 2 B 2
'
+test_expect_success 'blame with --contents and revision' '
+ check_count -h testTag --contents=file A 2 "External file (--contents)" 2
+'
+
test_expect_success 'setup B1 lines (branch1)' '
git checkout -b branch1 main &&
echo "3A slow green fox jumps into the" >>file &&
@@ -149,7 +183,7 @@ test_expect_success 'blame evil merge' '
test_expect_success 'blame huge graft' '
test_when_finished "git checkout branch2" &&
- test_when_finished "rm -f .git/info/grafts" &&
+ test_when_finished "rm -rf .git/info" &&
graft= &&
for i in 0 1 2
do
@@ -161,9 +195,10 @@ test_expect_success 'blame huge graft' '
GIT_AUTHOR_NAME=$i$j GIT_AUTHOR_EMAIL=$i$j@test.git \
git commit -a -m "$i$j" &&
commit=$(git rev-parse --verify HEAD) &&
- graft="$graft$commit "
+ graft="$graft$commit " || return 1
done
done &&
+ mkdir .git/info &&
printf "%s " $graft >.git/info/grafts &&
check_count -h 00 01 1 10 1
'
@@ -497,7 +532,7 @@ test_expect_success 'blame -L :funcname with userdiff driver' '
"$(cat file.template)" &&
test_commit --author "B <B@test.git>" \
"change" "$fortran_file" \
- "$(cat file.template | sed -e s/ChangeMe/IWasChanged/)" &&
+ "$(sed -e s/ChangeMe/IWasChanged/ file.template)" &&
check_count -f "$fortran_file" -L:RIGHT A 3 B 1
'
@@ -604,3 +639,39 @@ test_expect_success 'blame -L X,-N (non-numeric N)' '
test_expect_success 'blame -L ,^/RE/' '
test_must_fail $PROG -L1,^/99/ file
'
+
+test_expect_success 'blame progress on a full file' '
+ cat >expect <<-\EOF &&
+ Blaming lines: 100% (10/10), done.
+ EOF
+
+ GIT_PROGRESS_DELAY=0 \
+ git blame --progress hello.c 2>stderr &&
+
+ get_progress_result <stderr >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'blame progress on a single range' '
+ cat >expect <<-\EOF &&
+ Blaming lines: 100% (4/4), done.
+ EOF
+
+ GIT_PROGRESS_DELAY=0 \
+ git blame --progress -L 3,6 hello.c 2>stderr &&
+
+ get_progress_result <stderr >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'blame progress on multiple ranges' '
+ cat >expect <<-\EOF &&
+ Blaming lines: 100% (7/7), done.
+ EOF
+
+ GIT_PROGRESS_DELAY=0 \
+ git blame --progress -L 3,6 -L 8,10 hello.c 2>stderr &&
+
+ get_progress_result <stderr >actual &&
+ test_cmp expect actual
+'
diff --git a/t/chainlint.pl b/t/chainlint.pl
new file mode 100755
index 0000000..556ee91
--- /dev/null
+++ b/t/chainlint.pl
@@ -0,0 +1,832 @@
+#!/usr/bin/env perl
+#
+# Copyright (c) 2021-2022 Eric Sunshine <sunshine@sunshineco.com>
+#
+# This tool scans shell scripts for test definitions and checks those tests for
+# problems, such as broken &&-chains, which might hide bugs in the tests
+# themselves or in behaviors being exercised by the tests.
+#
+# Input arguments are pathnames of shell scripts containing test definitions,
+# or globs referencing a collection of scripts. For each problem discovered,
+# the pathname of the script containing the test is printed along with the test
+# name and the test body with a `?!FOO?!` annotation at the location of each
+# detected problem, where "FOO" is a tag such as "AMP" which indicates a broken
+# &&-chain. Returns zero if no problems are discovered, otherwise non-zero.
+
+use warnings;
+use strict;
+use Config;
+use File::Glob;
+use Getopt::Long;
+
+my $jobs = -1;
+my $show_stats;
+my $emit_all;
+
+# Lexer tokenizes POSIX shell scripts. It is roughly modeled after section 2.3
+# "Token Recognition" of POSIX chapter 2 "Shell Command Language". Although
+# similar to lexical analyzers for other languages, this one differs in a few
+# substantial ways due to quirks of the shell command language.
+#
+# For instance, in many languages, newline is just whitespace like space or
+# TAB, but in shell a newline is a command separator, thus a distinct lexical
+# token. A newline is significant and returned as a distinct token even at the
+# end of a shell comment.
+#
+# In other languages, `1+2` would typically be scanned as three tokens
+# (`1`, `+`, and `2`), but in shell it is a single token. However, the similar
+# `1 + 2`, which embeds whitepace, is scanned as three token in shell, as well.
+# In shell, several characters with special meaning lose that meaning when not
+# surrounded by whitespace. For instance, the negation operator `!` is special
+# when standing alone surrounded by whitespace; whereas in `foo!uucp` it is
+# just a plain character in the longer token "foo!uucp". In many other
+# languages, `"string"/foo:'string'` might be scanned as five tokens ("string",
+# `/`, `foo`, `:`, and 'string'), but in shell, it is just a single token.
+#
+# The lexical analyzer for the shell command language is also somewhat unusual
+# in that it recursively invokes the parser to handle the body of `$(...)`
+# expressions which can contain arbitrary shell code. Such expressions may be
+# encountered both inside and outside of double-quoted strings.
+#
+# The lexical analyzer is responsible for consuming shell here-doc bodies which
+# extend from the line following a `<<TAG` operator until a line consisting
+# solely of `TAG`. Here-doc consumption begins when a newline is encountered.
+# It is legal for multiple here-doc `<<TAG` operators to be present on a single
+# line, in which case their bodies must be present one following the next, and
+# are consumed in the (left-to-right) order the `<<TAG` operators appear on the
+# line. A special complication is that the bodies of all here-docs must be
+# consumed when the newline is encountered even if the parse context depth has
+# changed. For instance, in `cat <<A && x=$(cat <<B &&\n`, bodies of here-docs
+# "A" and "B" must be consumed even though "A" was introduced outside the
+# recursive parse context in which "B" was introduced and in which the newline
+# is encountered.
+package Lexer;
+
+sub new {
+ my ($class, $parser, $s) = @_;
+ bless {
+ parser => $parser,
+ buff => $s,
+ lineno => 1,
+ heretags => []
+ } => $class;
+}
+
+sub scan_heredoc_tag {
+ my $self = shift @_;
+ ${$self->{buff}} =~ /\G(-?)/gc;
+ my $indented = $1;
+ my $token = $self->scan_token();
+ return "<<$indented" unless $token;
+ my $tag = $token->[0];
+ $tag =~ s/['"\\]//g;
+ $$token[0] = $indented ? "\t$tag" : "$tag";
+ push(@{$self->{heretags}}, $token);
+ return "<<$indented$tag";
+}
+
+sub scan_op {
+ my ($self, $c) = @_;
+ my $b = $self->{buff};
+ return $c unless $$b =~ /\G(.)/sgc;
+ my $cc = $c . $1;
+ return scan_heredoc_tag($self) if $cc eq '<<';
+ return $cc if $cc =~ /^(?:&&|\|\||>>|;;|<&|>&|<>|>\|)$/;
+ pos($$b)--;
+ return $c;
+}
+
+sub scan_sqstring {
+ my $self = shift @_;
+ ${$self->{buff}} =~ /\G([^']*'|.*\z)/sgc;
+ my $s = $1;
+ $self->{lineno} += () = $s =~ /\n/sg;
+ return "'" . $s;
+}
+
+sub scan_dqstring {
+ my $self = shift @_;
+ my $b = $self->{buff};
+ my $s = '"';
+ while (1) {
+ # slurp up non-special characters
+ $s .= $1 if $$b =~ /\G([^"\$\\]+)/gc;
+ # handle special characters
+ last unless $$b =~ /\G(.)/sgc;
+ my $c = $1;
+ $s .= '"', last if $c eq '"';
+ $s .= '$' . $self->scan_dollar(), next if $c eq '$';
+ if ($c eq '\\') {
+ $s .= '\\', last unless $$b =~ /\G(.)/sgc;
+ $c = $1;
+ $self->{lineno}++, next if $c eq "\n"; # line splice
+ # backslash escapes only $, `, ", \ in dq-string
+ $s .= '\\' unless $c =~ /^[\$`"\\]$/;
+ $s .= $c;
+ next;
+ }
+ die("internal error scanning dq-string '$c'\n");
+ }
+ $self->{lineno} += () = $s =~ /\n/sg;
+ return $s;
+}
+
+sub scan_balanced {
+ my ($self, $c1, $c2) = @_;
+ my $b = $self->{buff};
+ my $depth = 1;
+ my $s = $c1;
+ while ($$b =~ /\G([^\Q$c1$c2\E]*(?:[\Q$c1$c2\E]|\z))/gc) {
+ $s .= $1;
+ $depth++, next if $s =~ /\Q$c1\E$/;
+ $depth--;
+ last if $depth == 0;
+ }
+ $self->{lineno} += () = $s =~ /\n/sg;
+ return $s;
+}
+
+sub scan_subst {
+ my $self = shift @_;
+ my @tokens = $self->{parser}->parse(qr/^\)$/);
+ $self->{parser}->next_token(); # closing ")"
+ return @tokens;
+}
+
+sub scan_dollar {
+ my $self = shift @_;
+ my $b = $self->{buff};
+ return $self->scan_balanced('(', ')') if $$b =~ /\G\((?=\()/gc; # $((...))
+ return '(' . join(' ', map {$_->[0]} $self->scan_subst()) . ')' if $$b =~ /\G\(/gc; # $(...)
+ return $self->scan_balanced('{', '}') if $$b =~ /\G\{/gc; # ${...}
+ return $1 if $$b =~ /\G(\w+)/gc; # $var
+ return $1 if $$b =~ /\G([@*#?$!0-9-])/gc; # $*, $1, $$, etc.
+ return '';
+}
+
+sub swallow_heredocs {
+ my $self = shift @_;
+ my $b = $self->{buff};
+ my $tags = $self->{heretags};
+ while (my $tag = shift @$tags) {
+ my $start = pos($$b);
+ my $indent = $$tag[0] =~ s/^\t// ? '\\s*' : '';
+ $$b =~ /(?:\G|\n)$indent\Q$$tag[0]\E(?:\n|\z)/gc;
+ if (pos($$b) > $start) {
+ my $body = substr($$b, $start, pos($$b) - $start);
+ $self->{lineno} += () = $body =~ /\n/sg;
+ next;
+ }
+ push(@{$self->{parser}->{problems}}, ['UNCLOSED-HEREDOC', $tag]);
+ $$b =~ /(?:\G|\n).*\z/gc; # consume rest of input
+ my $body = substr($$b, $start, pos($$b) - $start);
+ $self->{lineno} += () = $body =~ /\n/sg;
+ last;
+ }
+}
+
+sub scan_token {
+ my $self = shift @_;
+ my $b = $self->{buff};
+ my $token = '';
+ my ($start, $startln);
+RESTART:
+ $startln = $self->{lineno};
+ $$b =~ /\G[ \t]+/gc; # skip whitespace (but not newline)
+ $start = pos($$b) || 0;
+ $self->{lineno}++, return ["\n", $start, pos($$b), $startln, $startln] if $$b =~ /\G#[^\n]*(?:\n|\z)/gc; # comment
+ while (1) {
+ # slurp up non-special characters
+ $token .= $1 if $$b =~ /\G([^\\;&|<>(){}'"\$\s]+)/gc;
+ # handle special characters
+ last unless $$b =~ /\G(.)/sgc;
+ my $c = $1;
+ pos($$b)--, last if $c =~ /^[ \t]$/; # whitespace ends token
+ pos($$b)--, last if length($token) && $c =~ /^[;&|<>(){}\n]$/;
+ $token .= $self->scan_sqstring(), next if $c eq "'";
+ $token .= $self->scan_dqstring(), next if $c eq '"';
+ $token .= $c . $self->scan_dollar(), next if $c eq '$';
+ $self->{lineno}++, $self->swallow_heredocs(), $token = $c, last if $c eq "\n";
+ $token = $self->scan_op($c), last if $c =~ /^[;&|<>]$/;
+ $token = $c, last if $c =~ /^[(){}]$/;
+ if ($c eq '\\') {
+ $token .= '\\', last unless $$b =~ /\G(.)/sgc;
+ $c = $1;
+ $self->{lineno}++, next if $c eq "\n" && length($token); # line splice
+ $self->{lineno}++, goto RESTART if $c eq "\n"; # line splice
+ $token .= '\\' . $c;
+ next;
+ }
+ die("internal error scanning character '$c'\n");
+ }
+ return length($token) ? [$token, $start, pos($$b), $startln, $self->{lineno}] : undef;
+}
+
+# ShellParser parses POSIX shell scripts (with minor extensions for Bash). It
+# is a recursive descent parser very roughly modeled after section 2.10 "Shell
+# Grammar" of POSIX chapter 2 "Shell Command Language".
+package ShellParser;
+
+sub new {
+ my ($class, $s) = @_;
+ my $self = bless {
+ buff => [],
+ stop => [],
+ output => []
+ } => $class;
+ $self->{lexer} = Lexer->new($self, $s);
+ return $self;
+}
+
+sub next_token {
+ my $self = shift @_;
+ return pop(@{$self->{buff}}) if @{$self->{buff}};
+ return $self->{lexer}->scan_token();
+}
+
+sub untoken {
+ my $self = shift @_;
+ push(@{$self->{buff}}, @_);
+}
+
+sub peek {
+ my $self = shift @_;
+ my $token = $self->next_token();
+ return undef unless defined($token);
+ $self->untoken($token);
+ return $token;
+}
+
+sub stop_at {
+ my ($self, $token) = @_;
+ return 1 unless defined($token);
+ my $stop = ${$self->{stop}}[-1] if @{$self->{stop}};
+ return defined($stop) && $token->[0] =~ $stop;
+}
+
+sub expect {
+ my ($self, $expect) = @_;
+ my $token = $self->next_token();
+ return $token if defined($token) && $token->[0] eq $expect;
+ push(@{$self->{output}}, "?!ERR?! expected '$expect' but found '" . (defined($token) ? $token->[0] : "<end-of-input>") . "'\n");
+ $self->untoken($token) if defined($token);
+ return ();
+}
+
+sub optional_newlines {
+ my $self = shift @_;
+ my @tokens;
+ while (my $token = $self->peek()) {
+ last unless $token->[0] eq "\n";
+ push(@tokens, $self->next_token());
+ }
+ return @tokens;
+}
+
+sub parse_group {
+ my $self = shift @_;
+ return ($self->parse(qr/^}$/),
+ $self->expect('}'));
+}
+
+sub parse_subshell {
+ my $self = shift @_;
+ return ($self->parse(qr/^\)$/),
+ $self->expect(')'));
+}
+
+sub parse_case_pattern {
+ my $self = shift @_;
+ my @tokens;
+ while (defined(my $token = $self->next_token())) {
+ push(@tokens, $token);
+ last if $token->[0] eq ')';
+ }
+ return @tokens;
+}
+
+sub parse_case {
+ my $self = shift @_;
+ my @tokens;
+ push(@tokens,
+ $self->next_token(), # subject
+ $self->optional_newlines(),
+ $self->expect('in'),
+ $self->optional_newlines());
+ while (1) {
+ my $token = $self->peek();
+ last unless defined($token) && $token->[0] ne 'esac';
+ push(@tokens,
+ $self->parse_case_pattern(),
+ $self->optional_newlines(),
+ $self->parse(qr/^(?:;;|esac)$/)); # item body
+ $token = $self->peek();
+ last unless defined($token) && $token->[0] ne 'esac';
+ push(@tokens,
+ $self->expect(';;'),
+ $self->optional_newlines());
+ }
+ push(@tokens, $self->expect('esac'));
+ return @tokens;
+}
+
+sub parse_for {
+ my $self = shift @_;
+ my @tokens;
+ push(@tokens,
+ $self->next_token(), # variable
+ $self->optional_newlines());
+ my $token = $self->peek();
+ if (defined($token) && $token->[0] eq 'in') {
+ push(@tokens,
+ $self->expect('in'),
+ $self->optional_newlines());
+ }
+ push(@tokens,
+ $self->parse(qr/^do$/), # items
+ $self->expect('do'),
+ $self->optional_newlines(),
+ $self->parse_loop_body(),
+ $self->expect('done'));
+ return @tokens;
+}
+
+sub parse_if {
+ my $self = shift @_;
+ my @tokens;
+ while (1) {
+ push(@tokens,
+ $self->parse(qr/^then$/), # if/elif condition
+ $self->expect('then'),
+ $self->optional_newlines(),
+ $self->parse(qr/^(?:elif|else|fi)$/)); # if/elif body
+ my $token = $self->peek();
+ last unless defined($token) && $token->[0] eq 'elif';
+ push(@tokens, $self->expect('elif'));
+ }
+ my $token = $self->peek();
+ if (defined($token) && $token->[0] eq 'else') {
+ push(@tokens,
+ $self->expect('else'),
+ $self->optional_newlines(),
+ $self->parse(qr/^fi$/)); # else body
+ }
+ push(@tokens, $self->expect('fi'));
+ return @tokens;
+}
+
+sub parse_loop_body {
+ my $self = shift @_;
+ return $self->parse(qr/^done$/);
+}
+
+sub parse_loop {
+ my $self = shift @_;
+ return ($self->parse(qr/^do$/), # condition
+ $self->expect('do'),
+ $self->optional_newlines(),
+ $self->parse_loop_body(),
+ $self->expect('done'));
+}
+
+sub parse_func {
+ my $self = shift @_;
+ return ($self->expect('('),
+ $self->expect(')'),
+ $self->optional_newlines(),
+ $self->parse_cmd()); # body
+}
+
+sub parse_bash_array_assignment {
+ my $self = shift @_;
+ my @tokens = $self->expect('(');
+ while (defined(my $token = $self->next_token())) {
+ push(@tokens, $token);
+ last if $token->[0] eq ')';
+ }
+ return @tokens;
+}
+
+my %compound = (
+ '{' => \&parse_group,
+ '(' => \&parse_subshell,
+ 'case' => \&parse_case,
+ 'for' => \&parse_for,
+ 'if' => \&parse_if,
+ 'until' => \&parse_loop,
+ 'while' => \&parse_loop);
+
+sub parse_cmd {
+ my $self = shift @_;
+ my $cmd = $self->next_token();
+ return () unless defined($cmd);
+ return $cmd if $cmd->[0] eq "\n";
+
+ my $token;
+ my @tokens = $cmd;
+ if ($cmd->[0] eq '!') {
+ push(@tokens, $self->parse_cmd());
+ return @tokens;
+ } elsif (my $f = $compound{$cmd->[0]}) {
+ push(@tokens, $self->$f());
+ } elsif (defined($token = $self->peek()) && $token->[0] eq '(') {
+ if ($cmd->[0] !~ /\w=$/) {
+ push(@tokens, $self->parse_func());
+ return @tokens;
+ }
+ my @array = $self->parse_bash_array_assignment();
+ $tokens[-1]->[0] .= join(' ', map {$_->[0]} @array);
+ $tokens[-1]->[2] = $array[$#array][2] if @array;
+ }
+
+ while (defined(my $token = $self->next_token())) {
+ $self->untoken($token), last if $self->stop_at($token);
+ push(@tokens, $token);
+ last if $token->[0] =~ /^(?:[;&\n|]|&&|\|\|)$/;
+ }
+ push(@tokens, $self->next_token()) if $tokens[-1]->[0] ne "\n" && defined($token = $self->peek()) && $token->[0] eq "\n";
+ return @tokens;
+}
+
+sub accumulate {
+ my ($self, $tokens, $cmd) = @_;
+ push(@$tokens, @$cmd);
+}
+
+sub parse {
+ my ($self, $stop) = @_;
+ push(@{$self->{stop}}, $stop);
+ goto DONE if $self->stop_at($self->peek());
+ my @tokens;
+ while (my @cmd = $self->parse_cmd()) {
+ $self->accumulate(\@tokens, \@cmd);
+ last if $self->stop_at($self->peek());
+ }
+DONE:
+ pop(@{$self->{stop}});
+ return @tokens;
+}
+
+# TestParser is a subclass of ShellParser which, beyond parsing shell script
+# code, is also imbued with semantic knowledge of test construction, and checks
+# tests for common problems (such as broken &&-chains) which might hide bugs in
+# the tests themselves or in behaviors being exercised by the tests. As such,
+# TestParser is only called upon to parse test bodies, not the top-level
+# scripts in which the tests are defined.
+package TestParser;
+
+use base 'ShellParser';
+
+sub new {
+ my $class = shift @_;
+ my $self = $class->SUPER::new(@_);
+ $self->{problems} = [];
+ return $self;
+}
+
+sub find_non_nl {
+ my $tokens = shift @_;
+ my $n = shift @_;
+ $n = $#$tokens if !defined($n);
+ $n-- while $n >= 0 && $$tokens[$n]->[0] eq "\n";
+ return $n;
+}
+
+sub ends_with {
+ my ($tokens, $needles) = @_;
+ my $n = find_non_nl($tokens);
+ for my $needle (reverse(@$needles)) {
+ return undef if $n < 0;
+ $n = find_non_nl($tokens, $n), next if $needle eq "\n";
+ return undef if $$tokens[$n]->[0] !~ $needle;
+ $n--;
+ }
+ return 1;
+}
+
+sub match_ending {
+ my ($tokens, $endings) = @_;
+ for my $needles (@$endings) {
+ next if @$tokens < scalar(grep {$_ ne "\n"} @$needles);
+ return 1 if ends_with($tokens, $needles);
+ }
+ return undef;
+}
+
+sub parse_loop_body {
+ my $self = shift @_;
+ my @tokens = $self->SUPER::parse_loop_body(@_);
+ # did loop signal failure via "|| return" or "|| exit"?
+ return @tokens if !@tokens || grep {$_->[0] =~ /^(?:return|exit|\$\?)$/} @tokens;
+ # did loop upstream of a pipe signal failure via "|| echo 'impossible
+ # text'" as the final command in the loop body?
+ return @tokens if ends_with(\@tokens, [qr/^\|\|$/, "\n", qr/^echo$/, qr/^.+$/]);
+ # flag missing "return/exit" handling explicit failure in loop body
+ my $n = find_non_nl(\@tokens);
+ push(@{$self->{problems}}, ['LOOP', $tokens[$n]]);
+ return @tokens;
+}
+
+my @safe_endings = (
+ [qr/^(?:&&|\|\||\||&)$/],
+ [qr/^(?:exit|return)$/, qr/^(?:\d+|\$\?)$/],
+ [qr/^(?:exit|return)$/, qr/^(?:\d+|\$\?)$/, qr/^;$/],
+ [qr/^(?:exit|return|continue)$/],
+ [qr/^(?:exit|return|continue)$/, qr/^;$/]);
+
+sub accumulate {
+ my ($self, $tokens, $cmd) = @_;
+ my $problems = $self->{problems};
+
+ # no previous command to check for missing "&&"
+ goto DONE unless @$tokens;
+
+ # new command is empty line; can't yet check if previous is missing "&&"
+ goto DONE if @$cmd == 1 && $$cmd[0]->[0] eq "\n";
+
+ # did previous command end with "&&", "|", "|| return" or similar?
+ goto DONE if match_ending($tokens, \@safe_endings);
+
+ # if this command handles "$?" specially, then okay for previous
+ # command to be missing "&&"
+ for my $token (@$cmd) {
+ goto DONE if $token->[0] =~ /\$\?/;
+ }
+
+ # if this command is "false", "return 1", or "exit 1" (which signal
+ # failure explicitly), then okay for all preceding commands to be
+ # missing "&&"
+ if ($$cmd[0]->[0] =~ /^(?:false|return|exit)$/) {
+ @$problems = grep {$_->[0] ne 'AMP'} @$problems;
+ goto DONE;
+ }
+
+ # flag missing "&&" at end of previous command
+ my $n = find_non_nl($tokens);
+ push(@$problems, ['AMP', $tokens->[$n]]) unless $n < 0;
+
+DONE:
+ $self->SUPER::accumulate($tokens, $cmd);
+}
+
+# ScriptParser is a subclass of ShellParser which identifies individual test
+# definitions within test scripts, and passes each test body through TestParser
+# to identify possible problems. ShellParser detects test definitions not only
+# at the top-level of test scripts but also within compound commands such as
+# loops and function definitions.
+package ScriptParser;
+
+use base 'ShellParser';
+
+sub new {
+ my $class = shift @_;
+ my $self = $class->SUPER::new(@_);
+ $self->{ntests} = 0;
+ return $self;
+}
+
+# extract the raw content of a token, which may be a single string or a
+# composition of multiple strings and non-string character runs; for instance,
+# `"test body"` unwraps to `test body`; `word"a b"42'c d'` to `worda b42c d`
+sub unwrap {
+ my $token = (@_ ? shift @_ : $_)->[0];
+ # simple case: 'sqstring' or "dqstring"
+ return $token if $token =~ s/^'([^']*)'$/$1/;
+ return $token if $token =~ s/^"([^"]*)"$/$1/;
+
+ # composite case
+ my ($s, $q, $escaped);
+ while (1) {
+ # slurp up non-special characters
+ $s .= $1 if $token =~ /\G([^\\'"]*)/gc;
+ # handle special characters
+ last unless $token =~ /\G(.)/sgc;
+ my $c = $1;
+ $q = undef, next if defined($q) && $c eq $q;
+ $q = $c, next if !defined($q) && $c =~ /^['"]$/;
+ if ($c eq '\\') {
+ last unless $token =~ /\G(.)/sgc;
+ $c = $1;
+ $s .= '\\' if $c eq "\n"; # preserve line splice
+ }
+ $s .= $c;
+ }
+ return $s
+}
+
+sub check_test {
+ my $self = shift @_;
+ my ($title, $body) = map(unwrap, @_);
+ $self->{ntests}++;
+ my $parser = TestParser->new(\$body);
+ my @tokens = $parser->parse();
+ my $problems = $parser->{problems};
+ return unless $emit_all || @$problems;
+ my $c = main::fd_colors(1);
+ my $lineno = $_[1]->[3];
+ my $start = 0;
+ my $checked = '';
+ for (sort {$a->[1]->[2] <=> $b->[1]->[2]} @$problems) {
+ my ($label, $token) = @$_;
+ my $pos = $token->[2];
+ $checked .= substr($body, $start, $pos - $start) . " ?!$label?! ";
+ $start = $pos;
+ }
+ $checked .= substr($body, $start);
+ $checked =~ s/^/$lineno++ . ' '/mge;
+ $checked =~ s/^\d+ \n//;
+ $checked =~ s/(\s) \?!/$1?!/mg;
+ $checked =~ s/\?! (\s)/?!$1/mg;
+ $checked =~ s/(\?![^?]+\?!)/$c->{rev}$c->{red}$1$c->{reset}/mg;
+ $checked =~ s/^\d+/$c->{dim}$&$c->{reset}/mg;
+ $checked .= "\n" unless $checked =~ /\n$/;
+ push(@{$self->{output}}, "$c->{blue}# chainlint: $title$c->{reset}\n$checked");
+}
+
+sub parse_cmd {
+ my $self = shift @_;
+ my @tokens = $self->SUPER::parse_cmd();
+ return @tokens unless @tokens && $tokens[0]->[0] =~ /^test_expect_(?:success|failure)$/;
+ my $n = $#tokens;
+ $n-- while $n >= 0 && $tokens[$n]->[0] =~ /^(?:[;&\n|]|&&|\|\|)$/;
+ $self->check_test($tokens[1], $tokens[2]) if $n == 2; # title body
+ $self->check_test($tokens[2], $tokens[3]) if $n > 2; # prereq title body
+ return @tokens;
+}
+
+# main contains high-level functionality for processing command-line switches,
+# feeding input test scripts to ScriptParser, and reporting results.
+package main;
+
+my $getnow = sub { return time(); };
+my $interval = sub { return time() - shift; };
+if (eval {require Time::HiRes; Time::HiRes->import(); 1;}) {
+ $getnow = sub { return [Time::HiRes::gettimeofday()]; };
+ $interval = sub { return Time::HiRes::tv_interval(shift); };
+}
+
+# Restore TERM if test framework set it to "dumb" so 'tput' will work; do this
+# outside of get_colors() since under 'ithreads' all threads use %ENV of main
+# thread and ignore %ENV changes in subthreads.
+$ENV{TERM} = $ENV{USER_TERM} if $ENV{USER_TERM};
+
+my @NOCOLORS = (bold => '', rev => '', dim => '', reset => '', blue => '', green => '', red => '');
+my %COLORS = ();
+sub get_colors {
+ return \%COLORS if %COLORS;
+ if (exists($ENV{NO_COLOR})) {
+ %COLORS = @NOCOLORS;
+ return \%COLORS;
+ }
+ if ($ENV{TERM} =~ /xterm|xterm-\d+color|xterm-new|xterm-direct|nsterm|nsterm-\d+color|nsterm-direct/) {
+ %COLORS = (bold => "\e[1m",
+ rev => "\e[7m",
+ dim => "\e[2m",
+ reset => "\e[0m",
+ blue => "\e[34m",
+ green => "\e[32m",
+ red => "\e[31m");
+ return \%COLORS;
+ }
+ if (system("tput sgr0 >/dev/null 2>&1") == 0 &&
+ system("tput bold >/dev/null 2>&1") == 0 &&
+ system("tput rev >/dev/null 2>&1") == 0 &&
+ system("tput dim >/dev/null 2>&1") == 0 &&
+ system("tput setaf 1 >/dev/null 2>&1") == 0) {
+ %COLORS = (bold => `tput bold`,
+ rev => `tput rev`,
+ dim => `tput dim`,
+ reset => `tput sgr0`,
+ blue => `tput setaf 4`,
+ green => `tput setaf 2`,
+ red => `tput setaf 1`);
+ return \%COLORS;
+ }
+ %COLORS = @NOCOLORS;
+ return \%COLORS;
+}
+
+my %FD_COLORS = ();
+sub fd_colors {
+ my $fd = shift;
+ return $FD_COLORS{$fd} if exists($FD_COLORS{$fd});
+ $FD_COLORS{$fd} = -t $fd ? get_colors() : {@NOCOLORS};
+ return $FD_COLORS{$fd};
+}
+
+sub ncores {
+ # Windows
+ return $ENV{NUMBER_OF_PROCESSORS} if exists($ENV{NUMBER_OF_PROCESSORS});
+ # Linux / MSYS2 / Cygwin / WSL
+ do { local @ARGV='/proc/cpuinfo'; return scalar(grep(/^processor[\s\d]*:/, <>)); } if -r '/proc/cpuinfo';
+ # macOS & BSD
+ return qx/sysctl -n hw.ncpu/ if $^O =~ /(?:^darwin$|bsd)/;
+ return 1;
+}
+
+sub show_stats {
+ my ($start_time, $stats) = @_;
+ my $walltime = $interval->($start_time);
+ my ($usertime) = times();
+ my ($total_workers, $total_scripts, $total_tests, $total_errs) = (0, 0, 0, 0);
+ my $c = fd_colors(2);
+ print(STDERR $c->{green});
+ for (@$stats) {
+ my ($worker, $nscripts, $ntests, $nerrs) = @$_;
+ print(STDERR "worker $worker: $nscripts scripts, $ntests tests, $nerrs errors\n");
+ $total_workers++;
+ $total_scripts += $nscripts;
+ $total_tests += $ntests;
+ $total_errs += $nerrs;
+ }
+ printf(STDERR "total: %d workers, %d scripts, %d tests, %d errors, %.2fs/%.2fs (wall/user)$c->{reset}\n", $total_workers, $total_scripts, $total_tests, $total_errs, $walltime, $usertime);
+}
+
+sub check_script {
+ my ($id, $next_script, $emit) = @_;
+ my ($nscripts, $ntests, $nerrs) = (0, 0, 0);
+ while (my $path = $next_script->()) {
+ $nscripts++;
+ my $fh;
+ unless (open($fh, "<", $path)) {
+ $emit->("?!ERR?! $path: $!\n");
+ next;
+ }
+ my $s = do { local $/; <$fh> };
+ close($fh);
+ my $parser = ScriptParser->new(\$s);
+ 1 while $parser->parse_cmd();
+ if (@{$parser->{output}}) {
+ my $c = fd_colors(1);
+ my $s = join('', @{$parser->{output}});
+ $emit->("$c->{bold}$c->{blue}# chainlint: $path$c->{reset}\n" . $s);
+ $nerrs += () = $s =~ /\?![^?]+\?!/g;
+ }
+ $ntests += $parser->{ntests};
+ }
+ return [$id, $nscripts, $ntests, $nerrs];
+}
+
+sub exit_code {
+ my $stats = shift @_;
+ for (@$stats) {
+ my ($worker, $nscripts, $ntests, $nerrs) = @$_;
+ return 1 if $nerrs;
+ }
+ return 0;
+}
+
+Getopt::Long::Configure(qw{bundling});
+GetOptions(
+ "emit-all!" => \$emit_all,
+ "jobs|j=i" => \$jobs,
+ "stats|show-stats!" => \$show_stats) or die("option error\n");
+$jobs = ncores() if $jobs < 1;
+
+my $start_time = $getnow->();
+my @stats;
+
+my @scripts;
+push(@scripts, File::Glob::bsd_glob($_)) for (@ARGV);
+unless (@scripts) {
+ show_stats($start_time, \@stats) if $show_stats;
+ exit;
+}
+
+unless ($Config{useithreads} && eval {
+ require threads; threads->import();
+ require Thread::Queue; Thread::Queue->import();
+ 1;
+ }) {
+ push(@stats, check_script(1, sub { shift(@scripts); }, sub { print(@_); }));
+ show_stats($start_time, \@stats) if $show_stats;
+ exit(exit_code(\@stats));
+}
+
+my $script_queue = Thread::Queue->new();
+my $output_queue = Thread::Queue->new();
+
+sub next_script { return $script_queue->dequeue(); }
+sub emit { $output_queue->enqueue(@_); }
+
+sub monitor {
+ while (my $s = $output_queue->dequeue()) {
+ print($s);
+ }
+}
+
+my $mon = threads->create({'context' => 'void'}, \&monitor);
+threads->create({'context' => 'list'}, \&check_script, $_, \&next_script, \&emit) for 1..$jobs;
+
+$script_queue->enqueue(@scripts);
+$script_queue->end();
+
+for (threads->list()) {
+ push(@stats, $_->join()) unless $_ == $mon;
+}
+
+$output_queue->end();
+$mon->join();
+
+show_stats($start_time, \@stats) if $show_stats;
+exit(exit_code(\@stats));
diff --git a/t/chainlint.sed b/t/chainlint.sed
deleted file mode 100644
index 8a25c5b..0000000
--- a/t/chainlint.sed
+++ /dev/null
@@ -1,369 +0,0 @@
-#------------------------------------------------------------------------------
-# Detect broken &&-chains in tests.
-#
-# At present, only &&-chains in subshells are examined by this linter;
-# top-level &&-chains are instead checked directly by the test framework. Like
-# the top-level &&-chain linter, the subshell linter (intentionally) does not
-# check &&-chains within {...} blocks.
-#
-# Checking for &&-chain breakage is done line-by-line by pure textual
-# inspection.
-#
-# Incomplete lines (those ending with "\") are stitched together with following
-# lines to simplify processing, particularly of "one-liner" statements.
-# Top-level here-docs are swallowed to avoid false positives within the
-# here-doc body, although the statement to which the here-doc is attached is
-# retained.
-#
-# Heuristics are used to detect end-of-subshell when the closing ")" is cuddled
-# with the final subshell statement on the same line:
-#
-# (cd foo &&
-# bar)
-#
-# in order to avoid misinterpreting the ")" in constructs such as "x=$(...)"
-# and "case $x in *)" as ending the subshell.
-#
-# Lines missing a final "&&" are flagged with "?!AMP?!", and lines which chain
-# commands with ";" internally rather than "&&" are flagged "?!SEMI?!". A line
-# may be flagged for both violations.
-#
-# Detection of a missing &&-link in a multi-line subshell is complicated by the
-# fact that the last statement before the closing ")" must not end with "&&".
-# Since processing is line-by-line, it is not known whether a missing "&&" is
-# legitimate or not until the _next_ line is seen. To accommodate this, within
-# multi-line subshells, each line is stored in sed's "hold" area until after
-# the next line is seen and processed. If the next line is a stand-alone ")",
-# then a missing "&&" on the previous line is legitimate; otherwise a missing
-# "&&" is a break in the &&-chain.
-#
-# (
-# cd foo &&
-# bar
-# )
-#
-# In practical terms, when "bar" is encountered, it is flagged with "?!AMP?!",
-# but when the stand-alone ")" line is seen which closes the subshell, the
-# "?!AMP?!" violation is removed from the "bar" line (retrieved from the "hold"
-# area) since the final statement of a subshell must not end with "&&". The
-# final line of a subshell may still break the &&-chain by using ";" internally
-# to chain commands together rather than "&&", so "?!SEMI?!" is never removed
-# from a line (even though "?!AMP?!" might be).
-#
-# Care is taken to recognize the last _statement_ of a multi-line subshell, not
-# necessarily the last textual _line_ within the subshell, since &&-chaining
-# applies to statements, not to lines. Consequently, blank lines, comment
-# lines, and here-docs are swallowed (but not the command to which the here-doc
-# is attached), leaving the last statement in the "hold" area, not the last
-# line, thus simplifying &&-link checking.
-#
-# The final statement before "done" in for- and while-loops, and before "elif",
-# "else", and "fi" in if-then-else likewise must not end with "&&", thus
-# receives similar treatment.
-#
-# Swallowing here-docs with arbitrary tags requires a bit of finesse. When a
-# line such as "cat <<EOF >out" is seen, the here-doc tag is moved to the front
-# of the line enclosed in angle brackets as a sentinel, giving "<EOF>cat >out".
-# As each subsequent line is read, it is appended to the target line and a
-# (whitespace-loose) back-reference match /^<(.*)>\n\1$/ is attempted to see if
-# the content inside "<...>" matches the entirety of the newly-read line. For
-# instance, if the next line read is "some data", when concatenated with the
-# target line, it becomes "<EOF>cat >out\nsome data", and a match is attempted
-# to see if "EOF" matches "some data". Since it doesn't, the next line is
-# attempted. When a line consisting of only "EOF" (and possible whitespace) is
-# encountered, it is appended to the target line giving "<EOF>cat >out\nEOF",
-# in which case the "EOF" inside "<...>" does match the text following the
-# newline, thus the closing here-doc tag has been found. The closing tag line
-# and the "<...>" prefix on the target line are then discarded, leaving just
-# the target line "cat >out".
-#
-# To facilitate regression testing (and manual debugging), a ">" annotation is
-# applied to the line containing ")" which closes a subshell, ">>" to a line
-# closing a nested subshell, and ">>>" to a line closing both at once. This
-# makes it easy to detect whether the heuristics correctly identify
-# end-of-subshell.
-#------------------------------------------------------------------------------
-
-# incomplete line -- slurp up next line
-:squash
-/\\$/ {
- N
- s/\\\n//
- bsquash
-}
-
-# here-doc -- swallow it to avoid false hits within its body (but keep the
-# command to which it was attached)
-/<<[ ]*[-\\'"]*[A-Za-z0-9_]/ {
- s/^\(.*\)<<[ ]*[-\\'"]*\([A-Za-z0-9_][A-Za-z0-9_]*\)['"]*/<\2>\1<</
- s/[ ]*<<//
- :hered
- N
- /^<\([^>]*\)>.*\n[ ]*\1[ ]*$/!{
- s/\n.*$//
- bhered
- }
- s/^<[^>]*>//
- s/\n.*$//
-}
-
-# one-liner "(...) &&"
-/^[ ]*!*[ ]*(..*)[ ]*&&[ ]*$/boneline
-
-# same as above but without trailing "&&"
-/^[ ]*!*[ ]*(..*)[ ]*$/boneline
-
-# one-liner "(...) >x" (or "2>x" or "<x" or "|x" or "&"
-/^[ ]*!*[ ]*(..*)[ ]*[0-9]*[<>|&]/boneline
-
-# multi-line "(...\n...)"
-/^[ ]*(/bsubsh
-
-# innocuous line -- print it and advance to next line
-b
-
-# found one-liner "(...)" -- mark suspect if it uses ";" internally rather than
-# "&&" (but not ";" in a string)
-:oneline
-/;/{
- /"[^"]*;[^"]*"/!s/^/?!SEMI?!/
-}
-b
-
-:subsh
-# bare "(" line? -- stash for later printing
-/^[ ]*([ ]*$/ {
- h
- bnextln
-}
-# "(..." line -- split off and stash "(", then process "..." as its own line
-x
-s/.*/(/
-x
-s/(//
-bslurp
-
-:nextln
-N
-s/.*\n//
-
-:slurp
-# incomplete line "...\"
-/\\$/bicmplte
-# multi-line quoted string "...\n..."?
-/"/bdqstr
-# multi-line quoted string '...\n...'? (but not contraction in string "it's")
-/'/{
- /"[^'"]*'[^'"]*"/!bsqstr
-}
-:folded
-# here-doc -- swallow it
-/<<[ ]*[-\\'"]*[A-Za-z0-9_]/bheredoc
-# comment or empty line -- discard since final non-comment, non-empty line
-# before closing ")", "done", "elsif", "else", or "fi" will need to be
-# re-visited to drop "suspect" marking since final line of those constructs
-# legitimately lacks "&&", so "suspect" mark must be removed
-/^[ ]*#/bnextln
-/^[ ]*$/bnextln
-# in-line comment -- strip it (but not "#" in a string, Bash ${#...} array
-# length, or Perforce "//depot/path#42" revision in filespec)
-/[ ]#/{
- /"[^"]*#[^"]*"/!s/[ ]#.*$//
-}
-# one-liner "case ... esac"
-/^[ ]*case[ ]*..*esac/bchkchn
-# multi-line "case ... esac"
-/^[ ]*case[ ]..*[ ]in/bcase
-# multi-line "for ... done" or "while ... done"
-/^[ ]*for[ ]..*[ ]in/bcont
-/^[ ]*while[ ]/bcont
-/^[ ]*do[ ]/bcont
-/^[ ]*do[ ]*$/bcont
-/;[ ]*do/bcont
-/^[ ]*done[ ]*&&[ ]*$/bdone
-/^[ ]*done[ ]*$/bdone
-/^[ ]*done[ ]*[<>|]/bdone
-/^[ ]*done[ ]*)/bdone
-/||[ ]*exit[ ]/bcont
-/||[ ]*exit[ ]*$/bcont
-# multi-line "if...elsif...else...fi"
-/^[ ]*if[ ]/bcont
-/^[ ]*then[ ]/bcont
-/^[ ]*then[ ]*$/bcont
-/;[ ]*then/bcont
-/^[ ]*elif[ ]/belse
-/^[ ]*elif[ ]*$/belse
-/^[ ]*else[ ]/belse
-/^[ ]*else[ ]*$/belse
-/^[ ]*fi[ ]*&&[ ]*$/bdone
-/^[ ]*fi[ ]*$/bdone
-/^[ ]*fi[ ]*[<>|]/bdone
-/^[ ]*fi[ ]*)/bdone
-# nested one-liner "(...) &&"
-/^[ ]*(.*)[ ]*&&[ ]*$/bchkchn
-# nested one-liner "(...)"
-/^[ ]*(.*)[ ]*$/bchkchn
-# nested one-liner "(...) >x" (or "2>x" or "<x" or "|x")
-/^[ ]*(.*)[ ]*[0-9]*[<>|]/bchkchn
-# nested multi-line "(...\n...)"
-/^[ ]*(/bnest
-# multi-line "{...\n...}"
-/^[ ]*{/bblock
-# closing ")" on own line -- exit subshell
-/^[ ]*)/bclssolo
-# "$((...))" -- arithmetic expansion; not closing ")"
-/\$(([^)][^)]*))[^)]*$/bchkchn
-# "$(...)" -- command substitution; not closing ")"
-/\$([^)][^)]*)[^)]*$/bchkchn
-# multi-line "$(...\n...)" -- command substitution; treat as nested subshell
-/\$([^)]*$/bnest
-# "=(...)" -- Bash array assignment; not closing ")"
-/=(/bchkchn
-# closing "...) &&"
-/)[ ]*&&[ ]*$/bclose
-# closing "...)"
-/)[ ]*$/bclose
-# closing "...) >x" (or "2>x" or "<x" or "|x")
-/)[ ]*[<>|]/bclose
-:chkchn
-# mark suspect if line uses ";" internally rather than "&&" (but not ";" in a
-# string and not ";;" in one-liner "case...esac")
-/;/{
- /;;/!{
- /"[^"]*;[^"]*"/!s/^/?!SEMI?!/
- }
-}
-# line ends with pipe "...|" -- valid; not missing "&&"
-/|[ ]*$/bcont
-# missing end-of-line "&&" -- mark suspect
-/&&[ ]*$/!s/^/?!AMP?!/
-:cont
-# retrieve and print previous line
-x
-n
-bslurp
-
-# found incomplete line "...\" -- slurp up next line
-:icmplte
-N
-s/\\\n//
-bslurp
-
-# check for multi-line double-quoted string "...\n..." -- fold to one line
-:dqstr
-# remove all quote pairs
-s/"\([^"]*\)"/@!\1@!/g
-# done if no dangling quote
-/"/!bdqdone
-# otherwise, slurp next line and try again
-N
-s/\n//
-bdqstr
-:dqdone
-s/@!/"/g
-bfolded
-
-# check for multi-line single-quoted string '...\n...' -- fold to one line
-:sqstr
-# remove all quote pairs
-s/'\([^']*\)'/@!\1@!/g
-# done if no dangling quote
-/'/!bsqdone
-# otherwise, slurp next line and try again
-N
-s/\n//
-bsqstr
-:sqdone
-s/@!/'/g
-bfolded
-
-# found here-doc -- swallow it to avoid false hits within its body (but keep
-# the command to which it was attached)
-:heredoc
-s/^\(.*\)<<[ ]*[-\\'"]*\([A-Za-z0-9_][A-Za-z0-9_]*\)['"]*/<\2>\1<</
-s/[ ]*<<//
-:hdocsub
-N
-/^<\([^>]*\)>.*\n[ ]*\1[ ]*$/!{
- s/\n.*$//
- bhdocsub
-}
-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
-bcont
-
-# found "done" closing for-loop or while-loop, or "fi" closing if-then -- drop
-# "suspect" from final contained line since that line legitimately lacks "&&"
-:done
-x
-s/?!AMP?!//
-x
-# is 'done' or 'fi' cuddled with ")" to close subshell?
-/done.*)/bclose
-/fi.*)/bclose
-bchkchn
-
-# found nested multi-line "(...\n...)" -- pass through untouched
-:nest
-x
-:nstslrp
-n
-# closing ")" on own line -- stop nested slurp
-/^[ ]*)/bnstcl
-# comment -- not closing ")" if in comment
-/^[ ]*#/bnstcnt
-# "$((...))" -- arithmetic expansion; not closing ")"
-/\$(([^)][^)]*))[^)]*$/bnstcnt
-# "$(...)" -- command substitution; not closing ")"
-/\$([^)][^)]*)[^)]*$/bnstcnt
-# closing "...)" -- stop nested slurp
-/)/bnstcl
-:nstcnt
-x
-bnstslrp
-:nstcl
-s/^/>>/
-# is it "))" which closes nested and parent subshells?
-/)[ ]*)/bslurp
-bchkchn
-
-# found multi-line "{...\n...}" block -- pass through untouched
-:block
-x
-n
-# closing "}" -- stop block slurp
-/}/bchkchn
-bblock
-
-# found closing ")" on own line -- drop "suspect" from final line of subshell
-# since that line legitimately lacks "&&" and exit subshell loop
-:clssolo
-x
-s/?!AMP?!//
-p
-x
-s/^/>/
-b
-
-# found closing "...)" -- exit subshell loop
-:close
-x
-p
-x
-s/^/>/
-b
diff --git a/t/chainlint/arithmetic-expansion.expect b/t/chainlint/arithmetic-expansion.expect
index 09457d3..46ee104 100644
--- a/t/chainlint/arithmetic-expansion.expect
+++ b/t/chainlint/arithmetic-expansion.expect
@@ -2,8 +2,8 @@
foo &&
bar=$((42 + 1)) &&
baz
->) &&
+) &&
(
-?!AMP?! bar=$((42 + 1))
+ bar=$((42 + 1)) ?!AMP?!
baz
->)
+)
diff --git a/t/chainlint/bash-array.expect b/t/chainlint/bash-array.expect
index c4a830d..4c34eae 100644
--- a/t/chainlint/bash-array.expect
+++ b/t/chainlint/bash-array.expect
@@ -2,9 +2,9 @@
foo &&
bar=(gumbo stumbo wumbo) &&
baz
->) &&
+) &&
(
foo &&
bar=${#bar[@]} &&
baz
->)
+)
diff --git a/t/chainlint/blank-line-before-esac.expect b/t/chainlint/blank-line-before-esac.expect
new file mode 100644
index 0000000..056e030
--- /dev/null
+++ b/t/chainlint/blank-line-before-esac.expect
@@ -0,0 +1,18 @@
+test_done () {
+ case "$test_failure" in
+ 0)
+ test_at_end_hook_
+
+ exit 0 ;;
+
+ *)
+ if test $test_external_has_tap -eq 0
+ then
+ say_color error "# failed $test_failure among $msg"
+ say "1..$test_count"
+ fi
+
+ exit 1 ;;
+
+ esac
+}
diff --git a/t/chainlint/blank-line-before-esac.test b/t/chainlint/blank-line-before-esac.test
new file mode 100644
index 0000000..cecccad
--- /dev/null
+++ b/t/chainlint/blank-line-before-esac.test
@@ -0,0 +1,19 @@
+# LINT: blank line before "esac"
+test_done () {
+ case "$test_failure" in
+ 0)
+ test_at_end_hook_
+
+ exit 0 ;;
+
+ *)
+ if test $test_external_has_tap -eq 0
+ then
+ say_color error "# failed $test_failure among $msg"
+ say "1..$test_count"
+ fi
+
+ exit 1 ;;
+
+ esac
+}
diff --git a/t/chainlint/blank-line.expect b/t/chainlint/blank-line.expect
index 3be939e..b47827d 100644
--- a/t/chainlint/blank-line.expect
+++ b/t/chainlint/blank-line.expect
@@ -1,4 +1,8 @@
(
+
nothing &&
+
something
->)
+
+
+)
diff --git a/t/chainlint/blank-line.test b/t/chainlint/blank-line.test
index f6dd143..0fdf15b 100644
--- a/t/chainlint/blank-line.test
+++ b/t/chainlint/blank-line.test
@@ -3,7 +3,7 @@
nothing &&
something
-# LINT: swallow blank lines since final _statement_ before subshell end is
+# LINT: ignore 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-comment.expect b/t/chainlint/block-comment.expect
new file mode 100644
index 0000000..df2beea
--- /dev/null
+++ b/t/chainlint/block-comment.expect
@@ -0,0 +1,8 @@
+(
+ {
+ # show a
+ echo a &&
+ # show b
+ echo b
+ }
+)
diff --git a/t/chainlint/block-comment.test b/t/chainlint/block-comment.test
new file mode 100644
index 0000000..df2beea
--- /dev/null
+++ b/t/chainlint/block-comment.test
@@ -0,0 +1,8 @@
+(
+ {
+ # show a
+ echo a &&
+ # show b
+ echo b
+ }
+)
diff --git a/t/chainlint/block.expect b/t/chainlint/block.expect
index fed7e89..1c87326 100644
--- a/t/chainlint/block.expect
+++ b/t/chainlint/block.expect
@@ -1,12 +1,23 @@
(
foo &&
{
- echo a
+ echo a ?!AMP?!
echo b
} &&
bar &&
{
echo c
-?!AMP?! }
+ } ?!AMP?!
baz
->)
+) &&
+
+{
+ echo a; ?!AMP?! echo b
+} &&
+{ echo a; ?!AMP?! echo b; } &&
+
+{
+ echo "${var}9" &&
+ echo "done"
+} &&
+finis
diff --git a/t/chainlint/block.test b/t/chainlint/block.test
index d859151..4ab69a4 100644
--- a/t/chainlint/block.test
+++ b/t/chainlint/block.test
@@ -1,6 +1,5 @@
(
-# LINT: missing "&&" in block not currently detected (for consistency with
-# LINT: --chain-lint at top level and to provide escape hatch if needed)
+# LINT: missing "&&" after first "echo"
foo &&
{
echo a
@@ -12,4 +11,17 @@
echo c
}
baz
-)
+) &&
+
+# LINT: ";" not allowed in place of "&&"
+{
+ echo a; echo b
+} &&
+{ echo a; echo b; } &&
+
+# LINT: "}" inside string not mistaken as end of block
+{
+ echo "${var}9" &&
+ echo "done"
+} &&
+finis
diff --git a/t/chainlint/broken-chain.expect b/t/chainlint/broken-chain.expect
index 55b0f42..cfb58fb 100644
--- a/t/chainlint/broken-chain.expect
+++ b/t/chainlint/broken-chain.expect
@@ -1,6 +1,6 @@
(
foo &&
-?!AMP?! bar
+ bar ?!AMP?!
baz &&
wop
->)
+)
diff --git a/t/chainlint/broken-chain.test b/t/chainlint/broken-chain.test
index 3cc67b6..2a44aa7 100644
--- a/t/chainlint/broken-chain.test
+++ b/t/chainlint/broken-chain.test
@@ -1,6 +1,6 @@
(
foo &&
-# LINT: missing "&&" from 'bar'
+# LINT: missing "&&" from "bar"
bar
baz &&
# LINT: final statement before closing ")" legitimately lacks "&&"
diff --git a/t/chainlint/case-comment.expect b/t/chainlint/case-comment.expect
new file mode 100644
index 0000000..641c157
--- /dev/null
+++ b/t/chainlint/case-comment.expect
@@ -0,0 +1,11 @@
+(
+ case "$x" in
+ # found foo
+ x) foo ;;
+ # found other
+ *)
+ # treat it as bar
+ bar
+ ;;
+ esac
+)
diff --git a/t/chainlint/case-comment.test b/t/chainlint/case-comment.test
new file mode 100644
index 0000000..641c157
--- /dev/null
+++ b/t/chainlint/case-comment.test
@@ -0,0 +1,11 @@
+(
+ case "$x" in
+ # found foo
+ x) foo ;;
+ # found other
+ *)
+ # treat it as bar
+ bar
+ ;;
+ esac
+)
diff --git a/t/chainlint/case.expect b/t/chainlint/case.expect
index 41f121f..31f280d 100644
--- a/t/chainlint/case.expect
+++ b/t/chainlint/case.expect
@@ -4,16 +4,16 @@
*) bar ;;
esac &&
foobar
->) &&
+) &&
(
case "$x" in
x) foo ;;
*) bar ;;
-?!AMP?! esac
+ esac ?!AMP?!
foobar
->) &&
+) &&
(
case "$x" in 1) true;; esac &&
-?!AMP?! case "$y" in 2) false;; esac
+ case "$y" in 2) false;; esac ?!AMP?!
foobar
->)
+)
diff --git a/t/chainlint/case.test b/t/chainlint/case.test
index 5ef6ff7..4cb086b 100644
--- a/t/chainlint/case.test
+++ b/t/chainlint/case.test
@@ -1,5 +1,5 @@
(
-# LINT: "...)" arms in 'case' not misinterpreted as subshell-closing ")"
+# LINT: "...)" arms in "case" not misinterpreted as subshell-closing ")"
case "$x" in
x) foo ;;
*) bar ;;
@@ -7,7 +7,7 @@
foobar
) &&
(
-# LINT: missing "&&" on 'esac'
+# LINT: missing "&&" on "esac"
case "$x" in
x) foo ;;
*) bar ;;
@@ -15,7 +15,7 @@
foobar
) &&
(
-# LINT: "...)" arm in one-liner 'case' not misinterpreted as closing ")"
+# 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
diff --git a/t/chainlint/chain-break-background.expect b/t/chainlint/chain-break-background.expect
new file mode 100644
index 0000000..20d0bb5
--- /dev/null
+++ b/t/chainlint/chain-break-background.expect
@@ -0,0 +1,9 @@
+JGIT_DAEMON_PID= &&
+git init --bare empty.git &&
+>empty.git/git-daemon-export-ok &&
+mkfifo jgit_daemon_output &&
+{
+ jgit daemon --port="$JGIT_DAEMON_PORT" . >jgit_daemon_output &
+ JGIT_DAEMON_PID=$!
+} &&
+test_expect_code 2 git ls-remote --exit-code git://localhost:$JGIT_DAEMON_PORT/empty.git
diff --git a/t/chainlint/chain-break-background.test b/t/chainlint/chain-break-background.test
new file mode 100644
index 0000000..e10f656
--- /dev/null
+++ b/t/chainlint/chain-break-background.test
@@ -0,0 +1,10 @@
+JGIT_DAEMON_PID= &&
+git init --bare empty.git &&
+>empty.git/git-daemon-export-ok &&
+mkfifo jgit_daemon_output &&
+{
+# LINT: exit status of "&" is always 0 so &&-chaining immaterial
+ jgit daemon --port="$JGIT_DAEMON_PORT" . >jgit_daemon_output &
+ JGIT_DAEMON_PID=$!
+} &&
+test_expect_code 2 git ls-remote --exit-code git://localhost:$JGIT_DAEMON_PORT/empty.git
diff --git a/t/chainlint/chain-break-continue.expect b/t/chainlint/chain-break-continue.expect
new file mode 100644
index 0000000..47a3457
--- /dev/null
+++ b/t/chainlint/chain-break-continue.expect
@@ -0,0 +1,12 @@
+git ls-tree --name-only -r refs/notes/many_notes |
+while read path
+do
+ test "$path" = "foobar/non-note.txt" && continue
+ test "$path" = "deadbeef" && continue
+ test "$path" = "de/adbeef" && continue
+
+ if test $(expr length "$path") -ne $hexsz
+ then
+ return 1
+ fi
+done
diff --git a/t/chainlint/chain-break-continue.test b/t/chainlint/chain-break-continue.test
new file mode 100644
index 0000000..f0af71d
--- /dev/null
+++ b/t/chainlint/chain-break-continue.test
@@ -0,0 +1,13 @@
+git ls-tree --name-only -r refs/notes/many_notes |
+while read path
+do
+# LINT: broken &&-chain okay if explicit "continue"
+ test "$path" = "foobar/non-note.txt" && continue
+ test "$path" = "deadbeef" && continue
+ test "$path" = "de/adbeef" && continue
+
+ if test $(expr length "$path") -ne $hexsz
+ then
+ return 1
+ fi
+done
diff --git a/t/chainlint/chain-break-false.expect b/t/chainlint/chain-break-false.expect
new file mode 100644
index 0000000..989766f
--- /dev/null
+++ b/t/chainlint/chain-break-false.expect
@@ -0,0 +1,9 @@
+if condition not satisified
+then
+ echo it did not work...
+ echo failed!
+ false
+else
+ echo it went okay ?!AMP?!
+ congratulate user
+fi
diff --git a/t/chainlint/chain-break-false.test b/t/chainlint/chain-break-false.test
new file mode 100644
index 0000000..a5aaff8
--- /dev/null
+++ b/t/chainlint/chain-break-false.test
@@ -0,0 +1,10 @@
+# LINT: broken &&-chain okay if explicit "false" signals failure
+if condition not satisified
+then
+ echo it did not work...
+ echo failed!
+ false
+else
+ echo it went okay
+ congratulate user
+fi
diff --git a/t/chainlint/chain-break-return-exit.expect b/t/chainlint/chain-break-return-exit.expect
new file mode 100644
index 0000000..4cd18e2
--- /dev/null
+++ b/t/chainlint/chain-break-return-exit.expect
@@ -0,0 +1,19 @@
+case "$(git ls-files)" in
+one) echo pass one ;;
+*) echo bad one; return 1 ;;
+esac &&
+(
+ case "$(git ls-files)" in
+ two) echo pass two ;;
+ *) echo bad two; exit 1 ;;
+ esac
+) &&
+case "$(git ls-files)" in
+dir/two"$LF"one) echo pass both ;;
+*) echo bad; return 1 ;;
+esac &&
+
+for i in 1 2 3 4 ; do
+ git checkout main -b $i || return $?
+ test_commit $i $i $i tag$i || return $?
+done
diff --git a/t/chainlint/chain-break-return-exit.test b/t/chainlint/chain-break-return-exit.test
new file mode 100644
index 0000000..46542ed
--- /dev/null
+++ b/t/chainlint/chain-break-return-exit.test
@@ -0,0 +1,23 @@
+case "$(git ls-files)" in
+one) echo pass one ;;
+# LINT: broken &&-chain okay if explicit "return 1" signals failuire
+*) echo bad one; return 1 ;;
+esac &&
+(
+ case "$(git ls-files)" in
+ two) echo pass two ;;
+# LINT: broken &&-chain okay if explicit "exit 1" signals failuire
+ *) echo bad two; exit 1 ;;
+ esac
+) &&
+case "$(git ls-files)" in
+dir/two"$LF"one) echo pass both ;;
+# LINT: broken &&-chain okay if explicit "return 1" signals failuire
+*) echo bad; return 1 ;;
+esac &&
+
+for i in 1 2 3 4 ; do
+# LINT: broken &&-chain okay if explicit "return $?" signals failure
+ git checkout main -b $i || return $?
+ test_commit $i $i $i tag$i || return $?
+done
diff --git a/t/chainlint/chain-break-status.expect b/t/chainlint/chain-break-status.expect
new file mode 100644
index 0000000..e6b3b21
--- /dev/null
+++ b/t/chainlint/chain-break-status.expect
@@ -0,0 +1,9 @@
+OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 ) &&
+test_match_signal 13 "$OUT" &&
+
+{ test-tool sigchain >actual; ret=$?; } &&
+{
+ test_match_signal 15 "$ret" ||
+ test "$ret" = 3
+} &&
+test_cmp expect actual
diff --git a/t/chainlint/chain-break-status.test b/t/chainlint/chain-break-status.test
new file mode 100644
index 0000000..a6602a7
--- /dev/null
+++ b/t/chainlint/chain-break-status.test
@@ -0,0 +1,11 @@
+# LINT: broken &&-chain okay if next command handles "$?" explicitly
+OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 ) &&
+test_match_signal 13 "$OUT" &&
+
+# LINT: broken &&-chain okay if next command handles "$?" explicitly
+{ test-tool sigchain >actual; ret=$?; } &&
+{
+ test_match_signal 15 "$ret" ||
+ test "$ret" = 3
+} &&
+test_cmp expect actual
diff --git a/t/chainlint/chained-block.expect b/t/chainlint/chained-block.expect
new file mode 100644
index 0000000..574cdce
--- /dev/null
+++ b/t/chainlint/chained-block.expect
@@ -0,0 +1,9 @@
+echo nobody home && {
+ test the doohicky ?!AMP?!
+ right now
+} &&
+
+GIT_EXTERNAL_DIFF=echo git diff | {
+ read path oldfile oldhex oldmode newfile newhex newmode &&
+ test "z$oh" = "z$oldhex"
+}
diff --git a/t/chainlint/chained-block.test b/t/chainlint/chained-block.test
new file mode 100644
index 0000000..86f81ec
--- /dev/null
+++ b/t/chainlint/chained-block.test
@@ -0,0 +1,11 @@
+# LINT: start of block chained to preceding command
+echo nobody home && {
+ test the doohicky
+ right now
+} &&
+
+# LINT: preceding command pipes to block on same line
+GIT_EXTERNAL_DIFF=echo git diff | {
+ read path oldfile oldhex oldmode newfile newhex newmode &&
+ test "z$oh" = "z$oldhex"
+}
diff --git a/t/chainlint/chained-subshell.expect b/t/chainlint/chained-subshell.expect
new file mode 100644
index 0000000..83810ea
--- /dev/null
+++ b/t/chainlint/chained-subshell.expect
@@ -0,0 +1,10 @@
+mkdir sub && (
+ cd sub &&
+ foo the bar ?!AMP?!
+ nuff said
+) &&
+
+cut "-d " -f actual | (read s1 s2 s3 &&
+test -f $s1 ?!AMP?!
+test $(cat $s2) = tree2path1 &&
+test $(cat $s3) = tree3path1)
diff --git a/t/chainlint/chained-subshell.test b/t/chainlint/chained-subshell.test
new file mode 100644
index 0000000..4ff6ddd
--- /dev/null
+++ b/t/chainlint/chained-subshell.test
@@ -0,0 +1,13 @@
+# LINT: start of subshell chained to preceding command
+mkdir sub && (
+ cd sub &&
+ foo the bar
+ nuff said
+) &&
+
+# LINT: preceding command pipes to subshell on same line
+cut "-d " -f actual | (read s1 s2 s3 &&
+test -f $s1
+test $(cat $s2) = tree2path1 &&
+# LINT: closing subshell ")" correctly detected on same line as "$(...)"
+test $(cat $s3) = tree3path1)
diff --git a/t/chainlint/close-nested-and-parent-together.expect b/t/chainlint/close-nested-and-parent-together.expect
index 2a910f9..72d482f 100644
--- a/t/chainlint/close-nested-and-parent-together.expect
+++ b/t/chainlint/close-nested-and-parent-together.expect
@@ -1,4 +1,3 @@
-(
-cd foo &&
+(cd foo &&
(bar &&
->>> baz))
+ baz))
diff --git a/t/chainlint/close-subshell.expect b/t/chainlint/close-subshell.expect
index 1846887..2192a28 100644
--- a/t/chainlint/close-subshell.expect
+++ b/t/chainlint/close-subshell.expect
@@ -1,25 +1,26 @@
(
foo
->) &&
+) &&
(
bar
->) >out &&
+) >out &&
(
baz
->) 2>err &&
+) 2>err &&
(
boo
->) <input &&
+) <input &&
(
bip
->) | wuzzle &&
+) | wuzzle &&
(
bop
->) | fazz fozz &&
+) | fazz \
+ fozz &&
(
bup
->) |
+) |
fuzzle &&
(
yop
->)
+)
diff --git a/t/chainlint/command-substitution-subsubshell.expect b/t/chainlint/command-substitution-subsubshell.expect
new file mode 100644
index 0000000..ec42f2c
--- /dev/null
+++ b/t/chainlint/command-substitution-subsubshell.expect
@@ -0,0 +1,2 @@
+OUT=$( ((large_git 1>&3) | :) 3>&1 ) &&
+test_match_signal 13 "$OUT"
diff --git a/t/chainlint/command-substitution-subsubshell.test b/t/chainlint/command-substitution-subsubshell.test
new file mode 100644
index 0000000..321de29
--- /dev/null
+++ b/t/chainlint/command-substitution-subsubshell.test
@@ -0,0 +1,3 @@
+# LINT: subshell nested in subshell nested in command substitution
+OUT=$( ((large_git 1>&3) | :) 3>&1 ) &&
+test_match_signal 13 "$OUT"
diff --git a/t/chainlint/command-substitution.expect b/t/chainlint/command-substitution.expect
index ad4118e..c72e4df 100644
--- a/t/chainlint/command-substitution.expect
+++ b/t/chainlint/command-substitution.expect
@@ -2,8 +2,8 @@
foo &&
bar=$(gobble) &&
baz
->) &&
+) &&
(
-?!AMP?! bar=$(gobble blocks)
+ bar=$(gobble blocks) ?!AMP?!
baz
->)
+)
diff --git a/t/chainlint/comment.expect b/t/chainlint/comment.expect
index 3be939e..a68f1f9 100644
--- a/t/chainlint/comment.expect
+++ b/t/chainlint/comment.expect
@@ -1,4 +1,8 @@
(
+ # comment 1
nothing &&
+ # comment 2
something
->)
+ # comment 3
+ # comment 4
+)
diff --git a/t/chainlint/complex-if-in-cuddled-loop.expect b/t/chainlint/complex-if-in-cuddled-loop.expect
index 9674b88..dac2d0f 100644
--- a/t/chainlint/complex-if-in-cuddled-loop.expect
+++ b/t/chainlint/complex-if-in-cuddled-loop.expect
@@ -1,10 +1,9 @@
-(
-for i in a b c; do
+(for i in a b c; do
if test "$(echo $(waffle bat))" = "eleventeen" &&
test "$x" = "$y"; then
:
else
echo >file
- fi
-> done) &&
+ fi ?!LOOP?!
+ done) &&
test ! -f file
diff --git a/t/chainlint/complex-if-in-cuddled-loop.test b/t/chainlint/complex-if-in-cuddled-loop.test
index 571bbd8..5efeda5 100644
--- a/t/chainlint/complex-if-in-cuddled-loop.test
+++ b/t/chainlint/complex-if-in-cuddled-loop.test
@@ -1,4 +1,4 @@
-# LINT: 'for' loop cuddled with "(" and ")" and nested 'if' with complex
+# 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" &&
diff --git a/t/chainlint/cuddled-if-then-else.expect b/t/chainlint/cuddled-if-then-else.expect
index ab2a026..1d8ed58 100644
--- a/t/chainlint/cuddled-if-then-else.expect
+++ b/t/chainlint/cuddled-if-then-else.expect
@@ -1,7 +1,6 @@
-(
-if test -z ""; then
+(if test -z ""; then
echo empty
else
echo bizzy
-> fi) &&
+ fi) &&
echo foobar
diff --git a/t/chainlint/cuddled-if-then-else.test b/t/chainlint/cuddled-if-then-else.test
index eed774a..7c53f4e 100644
--- a/t/chainlint/cuddled-if-then-else.test
+++ b/t/chainlint/cuddled-if-then-else.test
@@ -1,4 +1,4 @@
-# LINT: 'if' cuddled with "(" and ")"; indented with spaces, not tabs
+# LINT: "if" cuddled with "(" and ")"; indented with spaces, not tabs
(if test -z ""; then
echo empty
else
diff --git a/t/chainlint/cuddled-loop.expect b/t/chainlint/cuddled-loop.expect
index 8c0260d..9cf2607 100644
--- a/t/chainlint/cuddled-loop.expect
+++ b/t/chainlint/cuddled-loop.expect
@@ -1,5 +1,4 @@
-(
- while read x
+( while read x
do foobar bop || exit 1
-> done <file ) &&
+ done <file ) &&
outside subshell
diff --git a/t/chainlint/cuddled-loop.test b/t/chainlint/cuddled-loop.test
index a841d78..3c2a62f 100644
--- a/t/chainlint/cuddled-loop.test
+++ b/t/chainlint/cuddled-loop.test
@@ -1,4 +1,4 @@
-# LINT: 'while' loop cuddled with "(" and ")", with embedded (allowed)
+# 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
diff --git a/t/chainlint/cuddled.expect b/t/chainlint/cuddled.expect
index b506d46..c3e0be4 100644
--- a/t/chainlint/cuddled.expect
+++ b/t/chainlint/cuddled.expect
@@ -1,21 +1,17 @@
-(
-cd foo &&
+(cd foo &&
bar
->) &&
+) &&
-(
-?!AMP?!cd foo
+(cd foo ?!AMP?!
bar
->) &&
+) &&
(
cd foo &&
-> bar) &&
+ bar) &&
-(
-cd foo &&
-> bar) &&
+(cd foo &&
+ bar) &&
-(
-?!AMP?!cd foo
-> bar)
+(cd foo ?!AMP?!
+ bar)
diff --git a/t/chainlint/cuddled.test b/t/chainlint/cuddled.test
index 0499fa4..257b5b5 100644
--- a/t/chainlint/cuddled.test
+++ b/t/chainlint/cuddled.test
@@ -1,5 +1,4 @@
-# LINT: first subshell statement cuddled with opening "("; for implementation
-# LINT: simplicity, "(..." is split into two lines, "(" and "..."
+# LINT: first subshell statement cuddled with opening "("
(cd foo &&
bar
) &&
diff --git a/t/chainlint/double-here-doc.expect b/t/chainlint/double-here-doc.expect
new file mode 100644
index 0000000..cd584a4
--- /dev/null
+++ b/t/chainlint/double-here-doc.expect
@@ -0,0 +1,12 @@
+run_sub_test_lib_test_err run-inv-range-start \
+ "--run invalid range start" \
+ --run="a-5" <<-\EOF &&
+test_expect_success "passing test #1" "true"
+test_done
+EOF
+check_sub_test_lib_test_err run-inv-range-start \
+ <<-\EOF_OUT 3<<-EOF_ERR
+> FATAL: Unexpected exit with code 1
+EOF_OUT
+> error: --run: invalid non-numeric in range start: ${SQ}a-5${SQ}
+EOF_ERR
diff --git a/t/chainlint/double-here-doc.test b/t/chainlint/double-here-doc.test
new file mode 100644
index 0000000..cd584a4
--- /dev/null
+++ b/t/chainlint/double-here-doc.test
@@ -0,0 +1,12 @@
+run_sub_test_lib_test_err run-inv-range-start \
+ "--run invalid range start" \
+ --run="a-5" <<-\EOF &&
+test_expect_success "passing test #1" "true"
+test_done
+EOF
+check_sub_test_lib_test_err run-inv-range-start \
+ <<-\EOF_OUT 3<<-EOF_ERR
+> FATAL: Unexpected exit with code 1
+EOF_OUT
+> error: --run: invalid non-numeric in range start: ${SQ}a-5${SQ}
+EOF_ERR
diff --git a/t/chainlint/dqstring-line-splice.expect b/t/chainlint/dqstring-line-splice.expect
new file mode 100644
index 0000000..37eab80
--- /dev/null
+++ b/t/chainlint/dqstring-line-splice.expect
@@ -0,0 +1,5 @@
+
+echo 'fatal: reword option of --fixup is mutually exclusive with' '--patch/--interactive/--all/--include/--only' >expect &&
+test_must_fail git commit --fixup=reword:HEAD~ $1 2>actual &&
+test_cmp expect actual
+
diff --git a/t/chainlint/dqstring-line-splice.test b/t/chainlint/dqstring-line-splice.test
new file mode 100644
index 0000000..b407144
--- /dev/null
+++ b/t/chainlint/dqstring-line-splice.test
@@ -0,0 +1,7 @@
+# LINT: line-splice within DQ-string
+'"
+echo 'fatal: reword option of --fixup is mutually exclusive with'\
+ '--patch/--interactive/--all/--include/--only' >expect &&
+test_must_fail git commit --fixup=reword:HEAD~ $1 2>actual &&
+test_cmp expect actual
+"'
diff --git a/t/chainlint/dqstring-no-interpolate.expect b/t/chainlint/dqstring-no-interpolate.expect
new file mode 100644
index 0000000..087eda1
--- /dev/null
+++ b/t/chainlint/dqstring-no-interpolate.expect
@@ -0,0 +1,12 @@
+grep "^ ! [rejected][ ]*$BRANCH -> $BRANCH (non-fast-forward)$" out &&
+
+grep "^\.git$" output.txt &&
+
+
+(
+ cd client$version &&
+ GIT_TEST_PROTOCOL_VERSION=$version git fetch-pack --no-progress .. $(cat ../input)
+) >output &&
+ cut -d ' ' -f 2 <output | sort >actual &&
+ test_cmp expect actual
+
diff --git a/t/chainlint/dqstring-no-interpolate.test b/t/chainlint/dqstring-no-interpolate.test
new file mode 100644
index 0000000..d2f4219
--- /dev/null
+++ b/t/chainlint/dqstring-no-interpolate.test
@@ -0,0 +1,15 @@
+# LINT: regex dollar-sign eol anchor in double-quoted string not special
+grep "^ ! \[rejected\][ ]*$BRANCH -> $BRANCH (non-fast-forward)$" out &&
+
+# LINT: escaped "$" not mistaken for variable expansion
+grep "^\\.git\$" output.txt &&
+
+'"
+(
+ cd client$version &&
+# LINT: escaped dollar-sign in double-quoted test body
+ GIT_TEST_PROTOCOL_VERSION=$version git fetch-pack --no-progress .. \$(cat ../input)
+) >output &&
+ cut -d ' ' -f 2 <output | sort >actual &&
+ test_cmp expect actual
+"'
diff --git a/t/chainlint/empty-here-doc.expect b/t/chainlint/empty-here-doc.expect
new file mode 100644
index 0000000..8507721
--- /dev/null
+++ b/t/chainlint/empty-here-doc.expect
@@ -0,0 +1,4 @@
+git ls-tree $tree path >current &&
+cat >expected <<\EOF &&
+EOF
+test_output
diff --git a/t/chainlint/empty-here-doc.test b/t/chainlint/empty-here-doc.test
new file mode 100644
index 0000000..24fc165
--- /dev/null
+++ b/t/chainlint/empty-here-doc.test
@@ -0,0 +1,5 @@
+git ls-tree $tree path >current &&
+# LINT: empty here-doc
+cat >expected <<\EOF &&
+EOF
+test_output
diff --git a/t/chainlint/exclamation.expect b/t/chainlint/exclamation.expect
new file mode 100644
index 0000000..765a35b
--- /dev/null
+++ b/t/chainlint/exclamation.expect
@@ -0,0 +1,4 @@
+if ! condition; then echo nope; else yep; fi &&
+test_prerequisite !MINGW &&
+mail uucp!address &&
+echo !whatever!
diff --git a/t/chainlint/exclamation.test b/t/chainlint/exclamation.test
new file mode 100644
index 0000000..323595b
--- /dev/null
+++ b/t/chainlint/exclamation.test
@@ -0,0 +1,8 @@
+# LINT: "! word" is two tokens
+if ! condition; then echo nope; else yep; fi &&
+# LINT: "!word" is single token, not two tokens "!" and "word"
+test_prerequisite !MINGW &&
+# LINT: "word!word" is single token, not three tokens "word", "!", and "word"
+mail uucp!address &&
+# LINT: "!word!" is single token, not three tokens "!", "word", and "!"
+echo !whatever!
diff --git a/t/chainlint/exit-loop.expect b/t/chainlint/exit-loop.expect
index 84d8bde..f76aa60 100644
--- a/t/chainlint/exit-loop.expect
+++ b/t/chainlint/exit-loop.expect
@@ -5,7 +5,7 @@
bar &&
baz
done
->) &&
+) &&
(
while true
do
@@ -13,7 +13,7 @@
bar &&
baz
done
->) &&
+) &&
(
i=0 &&
while test $i -lt 10
@@ -21,4 +21,4 @@
echo $i || exit
i=$(($i + 1))
done
->)
+)
diff --git a/t/chainlint/exit-subshell.expect b/t/chainlint/exit-subshell.expect
index bf78454..da80339 100644
--- a/t/chainlint/exit-subshell.expect
+++ b/t/chainlint/exit-subshell.expect
@@ -2,4 +2,4 @@
foo || exit 1
bar &&
baz
->)
+)
diff --git a/t/chainlint/for-loop-abbreviated.expect b/t/chainlint/for-loop-abbreviated.expect
new file mode 100644
index 0000000..02c0d15
--- /dev/null
+++ b/t/chainlint/for-loop-abbreviated.expect
@@ -0,0 +1,5 @@
+for it
+do
+ path=$(expr "$it" : ([^:]*)) &&
+ git update-index --add "$path" || exit
+done
diff --git a/t/chainlint/for-loop-abbreviated.test b/t/chainlint/for-loop-abbreviated.test
new file mode 100644
index 0000000..1084ecc
--- /dev/null
+++ b/t/chainlint/for-loop-abbreviated.test
@@ -0,0 +1,6 @@
+# LINT: for-loop lacking optional "in [word...]" before "do"
+for it
+do
+ path=$(expr "$it" : '\([^:]*\)') &&
+ git update-index --add "$path" || exit
+done
diff --git a/t/chainlint/for-loop.expect b/t/chainlint/for-loop.expect
index c33cf56..d2237f1 100644
--- a/t/chainlint/for-loop.expect
+++ b/t/chainlint/for-loop.expect
@@ -1,11 +1,14 @@
(
for i in a b c
do
-?!AMP?! echo $i
- cat
-?!AMP?! done
+ echo $i ?!AMP?!
+ cat <<-\EOF ?!LOOP?!
+ bar
+ EOF
+ done ?!AMP?!
+
for i in a b c; do
echo $i &&
- cat $i
+ cat $i ?!LOOP?!
done
->)
+)
diff --git a/t/chainlint/for-loop.test b/t/chainlint/for-loop.test
index 7db7626..6cb3428 100644
--- a/t/chainlint/for-loop.test
+++ b/t/chainlint/for-loop.test
@@ -1,17 +1,17 @@
(
-# LINT: 'for', 'do', 'done' do not need "&&"
+# LINT: "for", "do", "done" do not need "&&"
for i in a b c
do
-# LINT: missing "&&" on 'echo'
+# LINT: missing "&&" on "echo"
echo $i
# LINT: last statement of while does not need "&&"
cat <<-\EOF
bar
EOF
-# LINT: missing "&&" on 'done'
+# LINT: missing "&&" on "done"
done
-# LINT: 'do' on same line as 'for'
+# LINT: "do" on same line as "for"
for i in a b c; do
echo $i &&
cat $i
diff --git a/t/chainlint/function.expect b/t/chainlint/function.expect
new file mode 100644
index 0000000..dd7c997
--- /dev/null
+++ b/t/chainlint/function.expect
@@ -0,0 +1,11 @@
+sha1_file() {
+ echo "$*" | sed "s#..#.git/objects/&/#"
+} &&
+
+remove_object() {
+ file=$(sha1_file "$*") &&
+ test -e "$file" ?!AMP?!
+ rm -f "$file"
+} ?!AMP?!
+
+sha1_file arg && remove_object arg
diff --git a/t/chainlint/function.test b/t/chainlint/function.test
new file mode 100644
index 0000000..5ee5956
--- /dev/null
+++ b/t/chainlint/function.test
@@ -0,0 +1,13 @@
+# LINT: "()" in function definition not mistaken for subshell
+sha1_file() {
+ echo "$*" | sed "s#..#.git/objects/&/#"
+} &&
+
+# LINT: broken &&-chain in function and after function
+remove_object() {
+ file=$(sha1_file "$*") &&
+ test -e "$file"
+ rm -f "$file"
+}
+
+sha1_file arg && remove_object arg
diff --git a/t/chainlint/here-doc-close-subshell.expect b/t/chainlint/here-doc-close-subshell.expect
index f011e33..7d9c2b5 100644
--- a/t/chainlint/here-doc-close-subshell.expect
+++ b/t/chainlint/here-doc-close-subshell.expect
@@ -1,2 +1,4 @@
(
-> cat)
+ cat <<-\INPUT)
+ fizz
+ INPUT
diff --git a/t/chainlint/here-doc-indent-operator.expect b/t/chainlint/here-doc-indent-operator.expect
new file mode 100644
index 0000000..f92a7ce
--- /dev/null
+++ b/t/chainlint/here-doc-indent-operator.expect
@@ -0,0 +1,11 @@
+cat >expect <<- EOF &&
+header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0
+num_commits: $1
+chunks: oid_fanout oid_lookup commit_metadata generation_data bloom_indexes bloom_data
+EOF
+
+cat >expect << -EOF ?!AMP?!
+this is not indented
+-EOF
+
+cleanup
diff --git a/t/chainlint/here-doc-indent-operator.test b/t/chainlint/here-doc-indent-operator.test
new file mode 100644
index 0000000..c8a6f18
--- /dev/null
+++ b/t/chainlint/here-doc-indent-operator.test
@@ -0,0 +1,13 @@
+# LINT: whitespace between operator "<<-" and tag legal
+cat >expect <<- EOF &&
+header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0
+num_commits: $1
+chunks: oid_fanout oid_lookup commit_metadata generation_data bloom_indexes bloom_data
+EOF
+
+# LINT: not an indented here-doc; just a plain here-doc with tag named "-EOF"
+cat >expect << -EOF
+this is not indented
+-EOF
+
+cleanup
diff --git a/t/chainlint/here-doc-multi-line-command-subst.expect b/t/chainlint/here-doc-multi-line-command-subst.expect
index e5fb752..b7364c8 100644
--- a/t/chainlint/here-doc-multi-line-command-subst.expect
+++ b/t/chainlint/here-doc-multi-line-command-subst.expect
@@ -1,5 +1,8 @@
(
- x=$(bobble &&
-?!AMP?!>> wiffle)
+ x=$(bobble <<-\END &&
+ fossil
+ vegetable
+ END
+ wiffle) ?!AMP?!
echo $x
->)
+)
diff --git a/t/chainlint/here-doc-multi-line-string.expect b/t/chainlint/here-doc-multi-line-string.expect
index 32038a0..6c13bdc 100644
--- a/t/chainlint/here-doc-multi-line-string.expect
+++ b/t/chainlint/here-doc-multi-line-string.expect
@@ -1,4 +1,7 @@
(
-?!AMP?! cat && echo "multi-line string"
+ cat <<-\TXT && echo "multi-line
+ string" ?!AMP?!
+ fizzle
+ TXT
bap
->)
+)
diff --git a/t/chainlint/here-doc.expect b/t/chainlint/here-doc.expect
index 534b065..91b9612 100644
--- a/t/chainlint/here-doc.expect
+++ b/t/chainlint/here-doc.expect
@@ -1,9 +1,25 @@
-boodle wobba gorgo snoot wafta snurb &&
+boodle wobba \
+ gorgo snoot \
+ wafta snurb <<EOF &&
+quoth the raven,
+nevermore...
+EOF
-cat >foo &&
+cat <<-Arbitrary_Tag_42 >foo &&
+snoz
+boz
+woz
+Arbitrary_Tag_42
-cat >bar &&
+cat <<"zump" >boo &&
+snoz
+boz
+woz
+zump
-cat >boo &&
-
-horticulture
+horticulture <<\EOF
+gomez
+morticia
+wednesday
+pugsly
+EOF
diff --git a/t/chainlint/here-doc.test b/t/chainlint/here-doc.test
index ad4ce8a..3f5f92c 100644
--- a/t/chainlint/here-doc.test
+++ b/t/chainlint/here-doc.test
@@ -14,13 +14,6 @@ boz
woz
Arbitrary_Tag_42
-# LINT: swallow 'quoted' here-doc
-cat <<'FUMP' >bar &&
-snoz
-boz
-woz
-FUMP
-
# LINT: swallow "quoted" here-doc
cat <<"zump" >boo &&
snoz
diff --git a/t/chainlint/if-condition-split.expect b/t/chainlint/if-condition-split.expect
new file mode 100644
index 0000000..ee745ef
--- /dev/null
+++ b/t/chainlint/if-condition-split.expect
@@ -0,0 +1,7 @@
+if bob &&
+ marcia ||
+ kevin
+then
+ echo "nomads" ?!AMP?!
+ echo "for sure"
+fi
diff --git a/t/chainlint/if-condition-split.test b/t/chainlint/if-condition-split.test
new file mode 100644
index 0000000..240daa9
--- /dev/null
+++ b/t/chainlint/if-condition-split.test
@@ -0,0 +1,8 @@
+# LINT: "if" condition split across multiple lines at "&&" or "||"
+if bob &&
+ marcia ||
+ kevin
+then
+ echo "nomads"
+ echo "for sure"
+fi
diff --git a/t/chainlint/if-in-loop.expect b/t/chainlint/if-in-loop.expect
index 03d3ceb..d6514ae 100644
--- a/t/chainlint/if-in-loop.expect
+++ b/t/chainlint/if-in-loop.expect
@@ -3,10 +3,10 @@
do
if false
then
-?!AMP?! echo "err"
+ echo "err"
exit 1
-?!AMP?! fi
+ fi ?!AMP?!
foo
-?!AMP?! done
+ done ?!AMP?!
bar
->)
+)
diff --git a/t/chainlint/if-in-loop.test b/t/chainlint/if-in-loop.test
index daf22da..90c2397 100644
--- a/t/chainlint/if-in-loop.test
+++ b/t/chainlint/if-in-loop.test
@@ -3,13 +3,13 @@
do
if false
then
-# LINT: missing "&&" on 'echo'
+# LINT: missing "&&" on "echo" okay since "exit 1" signals error explicitly
echo "err"
exit 1
-# LINT: missing "&&" on 'fi'
+# LINT: missing "&&" on "fi"
fi
foo
-# LINT: missing "&&" on 'done'
+# LINT: missing "&&" on "done"
done
bar
)
diff --git a/t/chainlint/if-then-else.expect b/t/chainlint/if-then-else.expect
index 5953c7b..cbaaf85 100644
--- a/t/chainlint/if-then-else.expect
+++ b/t/chainlint/if-then-else.expect
@@ -1,19 +1,22 @@
(
if test -n ""
then
-?!AMP?! echo very
+ echo very ?!AMP?!
echo empty
elif test -z ""
+ then
echo foo
else
echo foo &&
- cat
-?!AMP?! fi
+ cat <<-\EOF
+ bar
+ EOF
+ fi ?!AMP?!
echo poodle
->) &&
+) &&
(
if test -n ""; then
echo very &&
-?!AMP?! echo empty
- if
->)
+ echo empty
+ fi
+)
diff --git a/t/chainlint/if-then-else.test b/t/chainlint/if-then-else.test
index 9bd8e9a..2055336 100644
--- a/t/chainlint/if-then-else.test
+++ b/t/chainlint/if-then-else.test
@@ -1,28 +1,29 @@
(
-# LINT: 'if', 'then', 'elif', 'else', 'fi' do not need "&&"
+# LINT: "if", "then", "elif", "else", "fi" do not need "&&"
if test -n ""
then
-# LINT: missing "&&" on 'echo'
+# LINT: missing "&&" on "echo"
echo very
-# LINT: last statement before 'elif' does not need "&&"
+# LINT: last statement before "elif" does not need "&&"
echo empty
elif test -z ""
-# LINT: last statement before 'else' does not need "&&"
+ then
+# LINT: last statement before "else" does not need "&&"
echo foo
else
echo foo &&
-# LINT: last statement before 'fi' does not need "&&"
+# LINT: last statement before "fi" does not need "&&"
cat <<-\EOF
bar
EOF
-# LINT: missing "&&" on 'fi'
+# LINT: missing "&&" on "fi"
fi
echo poodle
) &&
(
-# LINT: 'then' on same line as 'if'
+# LINT: "then" on same line as "if"
if test -n ""; then
echo very &&
echo empty
- if
+ fi
)
diff --git a/t/chainlint/incomplete-line.expect b/t/chainlint/incomplete-line.expect
index 2f3ebab..134d3a1 100644
--- a/t/chainlint/incomplete-line.expect
+++ b/t/chainlint/incomplete-line.expect
@@ -1,4 +1,10 @@
-line 1 line 2 line 3 line 4 &&
+line 1 \
+line 2 \
+line 3 \
+line 4 &&
(
- line 5 line 6 line 7 line 8
->)
+ line 5 \
+ line 6 \
+ line 7 \
+ line 8
+)
diff --git a/t/chainlint/inline-comment.expect b/t/chainlint/inline-comment.expect
index fc9f250..6bad218 100644
--- a/t/chainlint/inline-comment.expect
+++ b/t/chainlint/inline-comment.expect
@@ -1,9 +1,8 @@
(
- foobar &&
-?!AMP?! barfoo
+ foobar && # comment 1
+ barfoo ?!AMP?! # wrong position for &&
flibble "not a # comment"
->) &&
+) &&
-(
-cd foo &&
-> flibble "not a # comment")
+(cd foo &&
+ flibble "not a # comment")
diff --git a/t/chainlint/loop-detect-failure.expect b/t/chainlint/loop-detect-failure.expect
new file mode 100644
index 0000000..a66025c
--- /dev/null
+++ b/t/chainlint/loop-detect-failure.expect
@@ -0,0 +1,15 @@
+git init r1 &&
+for n in 1 2 3 4 5
+do
+ echo "This is file: $n" > r1/file.$n &&
+ git -C r1 add file.$n &&
+ git -C r1 commit -m "$n" || return 1
+done &&
+
+git init r2 &&
+for n in 1000 10000
+do
+ printf "%"$n"s" X > r2/large.$n &&
+ git -C r2 add large.$n &&
+ git -C r2 commit -m "$n" ?!LOOP?!
+done
diff --git a/t/chainlint/loop-detect-failure.test b/t/chainlint/loop-detect-failure.test
new file mode 100644
index 0000000..b9791cc
--- /dev/null
+++ b/t/chainlint/loop-detect-failure.test
@@ -0,0 +1,17 @@
+git init r1 &&
+# LINT: loop handles failure explicitly with "|| return 1"
+for n in 1 2 3 4 5
+do
+ echo "This is file: $n" > r1/file.$n &&
+ git -C r1 add file.$n &&
+ git -C r1 commit -m "$n" || return 1
+done &&
+
+git init r2 &&
+# LINT: loop fails to handle failure explicitly with "|| return 1"
+for n in 1000 10000
+do
+ printf "%"$n"s" X > r2/large.$n &&
+ git -C r2 add large.$n &&
+ git -C r2 commit -m "$n"
+done
diff --git a/t/chainlint/loop-detect-status.expect b/t/chainlint/loop-detect-status.expect
new file mode 100644
index 0000000..7ce3a34
--- /dev/null
+++ b/t/chainlint/loop-detect-status.expect
@@ -0,0 +1,18 @@
+(while test $i -le $blobcount
+ do
+ printf "Generating blob $i/$blobcount\r" >&2 &&
+ printf "blob\nmark :$i\ndata $blobsize\n" &&
+ #test-tool genrandom $i $blobsize &&
+ printf "%-${blobsize}s" $i &&
+ echo "M 100644 :$i $i" >> commit &&
+ i=$(($i+1)) ||
+ echo $? > exit-status
+ done &&
+ echo "commit refs/heads/main" &&
+ echo "author A U Thor <author@email.com> 123456789 +0000" &&
+ echo "committer C O Mitter <committer@email.com> 123456789 +0000" &&
+ echo "data 5" &&
+ echo ">2gb" &&
+ cat commit) |
+git fast-import --big-file-threshold=2 &&
+test ! -f exit-status
diff --git a/t/chainlint/loop-detect-status.test b/t/chainlint/loop-detect-status.test
new file mode 100644
index 0000000..1c6c23c
--- /dev/null
+++ b/t/chainlint/loop-detect-status.test
@@ -0,0 +1,19 @@
+# LINT: "$?" handled explicitly within loop body
+(while test $i -le $blobcount
+ do
+ printf "Generating blob $i/$blobcount\r" >&2 &&
+ printf "blob\nmark :$i\ndata $blobsize\n" &&
+ #test-tool genrandom $i $blobsize &&
+ printf "%-${blobsize}s" $i &&
+ echo "M 100644 :$i $i" >> commit &&
+ i=$(($i+1)) ||
+ echo $? > exit-status
+ done &&
+ echo "commit refs/heads/main" &&
+ echo "author A U Thor <author@email.com> 123456789 +0000" &&
+ echo "committer C O Mitter <committer@email.com> 123456789 +0000" &&
+ echo "data 5" &&
+ echo ">2gb" &&
+ cat commit) |
+git fast-import --big-file-threshold=2 &&
+test ! -f exit-status
diff --git a/t/chainlint/loop-in-if.expect b/t/chainlint/loop-in-if.expect
index 088e622..6c5d6e5 100644
--- a/t/chainlint/loop-in-if.expect
+++ b/t/chainlint/loop-in-if.expect
@@ -3,10 +3,10 @@
then
while true
do
-?!AMP?! echo "pop"
- echo "glup"
-?!AMP?! done
+ echo "pop" ?!AMP?!
+ echo "glup" ?!LOOP?!
+ done ?!AMP?!
foo
-?!AMP?! fi
+ fi ?!AMP?!
bar
->)
+)
diff --git a/t/chainlint/loop-in-if.test b/t/chainlint/loop-in-if.test
index 93e8ba8..dfcc3f9 100644
--- a/t/chainlint/loop-in-if.test
+++ b/t/chainlint/loop-in-if.test
@@ -3,13 +3,13 @@
then
while true
do
-# LINT: missing "&&" on 'echo'
+# LINT: missing "&&" on "echo"
echo "pop"
echo "glup"
-# LINT: missing "&&" on 'done'
+# LINT: missing "&&" on "done"
done
foo
-# LINT: missing "&&" on 'fi'
+# LINT: missing "&&" on "fi"
fi
bar
)
diff --git a/t/chainlint/loop-upstream-pipe.expect b/t/chainlint/loop-upstream-pipe.expect
new file mode 100644
index 0000000..0b82ecc
--- /dev/null
+++ b/t/chainlint/loop-upstream-pipe.expect
@@ -0,0 +1,10 @@
+(
+ git rev-list --objects --no-object-names base..loose |
+ while read oid
+ do
+ path="$objdir/$(test_oid_to_path "$oid")" &&
+ printf "%s %d\n" "$oid" "$(test-tool chmtime --get "$path")" ||
+ echo "object list generation failed for $oid"
+ done |
+ sort -k1
+) >expect &&
diff --git a/t/chainlint/loop-upstream-pipe.test b/t/chainlint/loop-upstream-pipe.test
new file mode 100644
index 0000000..efb77da
--- /dev/null
+++ b/t/chainlint/loop-upstream-pipe.test
@@ -0,0 +1,11 @@
+(
+ git rev-list --objects --no-object-names base..loose |
+ while read oid
+ do
+# LINT: "|| echo" signals failure in loop upstream of a pipe
+ path="$objdir/$(test_oid_to_path "$oid")" &&
+ printf "%s %d\n" "$oid" "$(test-tool chmtime --get "$path")" ||
+ echo "object list generation failed for $oid"
+ done |
+ sort -k1
+) >expect &&
diff --git a/t/chainlint/multi-line-nested-command-substitution.expect b/t/chainlint/multi-line-nested-command-substitution.expect
index 59b6c8b..3000583 100644
--- a/t/chainlint/multi-line-nested-command-substitution.expect
+++ b/t/chainlint/multi-line-nested-command-substitution.expect
@@ -3,16 +3,16 @@
x=$(
echo bar |
cat
->> ) &&
+ ) &&
echo ok
->) |
+) |
sort &&
(
bar &&
x=$(echo bar |
cat
->> ) &&
+ ) &&
y=$(echo baz |
->> fip) &&
+ fip) &&
echo fail
->)
+)
diff --git a/t/chainlint/multi-line-string.expect b/t/chainlint/multi-line-string.expect
index 170cb59..27ff952 100644
--- a/t/chainlint/multi-line-string.expect
+++ b/t/chainlint/multi-line-string.expect
@@ -1,15 +1,14 @@
(
- x="line 1 line 2 line 3" &&
-?!AMP?! y='line 1 line2'
+ x="line 1
+ line 2
+ line 3" &&
+ y="line 1
+ line2" ?!AMP?!
foobar
->) &&
+) &&
(
- echo "there's nothing to see here" &&
- exit
->) &&
-(
- echo "xyz" "abc def ghi" &&
- 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
index 287ab89..4a0af21 100644
--- a/t/chainlint/multi-line-string.test
+++ b/t/chainlint/multi-line-string.test
@@ -3,25 +3,13 @@
line 2
line 3" &&
# LINT: missing "&&" on assignment
- y='line 1
- line2'
+ 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
index cf18429..ad4c2d9 100644
--- a/t/chainlint/negated-one-liner.expect
+++ b/t/chainlint/negated-one-liner.expect
@@ -1,5 +1,5 @@
! (foo && bar) &&
! (foo && bar) >baz &&
-?!SEMI?!! (foo; bar) &&
-?!SEMI?!! (foo; bar) >baz
+! (foo; ?!AMP?! bar) &&
+! (foo; ?!AMP?! bar) >baz
diff --git a/t/chainlint/nested-cuddled-subshell.expect b/t/chainlint/nested-cuddled-subshell.expect
index c2a59ff..3836049 100644
--- a/t/chainlint/nested-cuddled-subshell.expect
+++ b/t/chainlint/nested-cuddled-subshell.expect
@@ -1,19 +1,25 @@
(
(cd foo &&
bar
->> ) &&
+ ) &&
+
(cd foo &&
bar
-?!AMP?!>> )
+ ) ?!AMP?!
+
(
cd foo &&
->> bar) &&
+ bar) &&
+
(
cd foo &&
-?!AMP?!>> bar)
+ bar) ?!AMP?!
+
(cd foo &&
->> bar) &&
+ bar) &&
+
(cd foo &&
-?!AMP?!>> bar)
+ bar) ?!AMP?!
+
foobar
->)
+)
diff --git a/t/chainlint/nested-here-doc.expect b/t/chainlint/nested-here-doc.expect
index 0c9ef1c..29b3832 100644
--- a/t/chainlint/nested-here-doc.expect
+++ b/t/chainlint/nested-here-doc.expect
@@ -1,7 +1,30 @@
-cat >foop &&
+cat <<ARBITRARY >foop &&
+naddle
+fub <<EOF
+ nozzle
+ noodle
+EOF
+formp
+ARBITRARY
(
- cat &&
-?!AMP?! cat
+ cat <<-\INPUT_END &&
+ fish are mice
+ but geese go slow
+ data <<EOF
+ perl is lerp
+ and nothing else
+ EOF
+ toink
+ INPUT_END
+
+ cat <<-\EOT ?!AMP?!
+ text goes here
+ data <<EOF
+ data goes here
+ EOF
+ more test here
+ EOT
+
foobar
->)
+)
diff --git a/t/chainlint/nested-loop-detect-failure.expect b/t/chainlint/nested-loop-detect-failure.expect
new file mode 100644
index 0000000..3461df4
--- /dev/null
+++ b/t/chainlint/nested-loop-detect-failure.expect
@@ -0,0 +1,31 @@
+for i in 0 1 2 3 4 5 6 7 8 9;
+do
+ for j in 0 1 2 3 4 5 6 7 8 9;
+ do
+ echo "$i$j" >"path$i$j" ?!LOOP?!
+ done ?!LOOP?!
+done &&
+
+for i in 0 1 2 3 4 5 6 7 8 9;
+do
+ for j in 0 1 2 3 4 5 6 7 8 9;
+ do
+ echo "$i$j" >"path$i$j" || return 1
+ done
+done &&
+
+for i in 0 1 2 3 4 5 6 7 8 9;
+do
+ for j in 0 1 2 3 4 5 6 7 8 9;
+ do
+ echo "$i$j" >"path$i$j" ?!LOOP?!
+ done || return 1
+done &&
+
+for i in 0 1 2 3 4 5 6 7 8 9;
+do
+ for j in 0 1 2 3 4 5 6 7 8 9;
+ do
+ echo "$i$j" >"path$i$j" || return 1
+ done || return 1
+done
diff --git a/t/chainlint/nested-loop-detect-failure.test b/t/chainlint/nested-loop-detect-failure.test
new file mode 100644
index 0000000..e6f0c1a
--- /dev/null
+++ b/t/chainlint/nested-loop-detect-failure.test
@@ -0,0 +1,35 @@
+# LINT: neither loop handles failure explicitly with "|| return 1"
+for i in 0 1 2 3 4 5 6 7 8 9;
+do
+ for j in 0 1 2 3 4 5 6 7 8 9;
+ do
+ echo "$i$j" >"path$i$j"
+ done
+done &&
+
+# LINT: inner loop handles failure explicitly with "|| return 1"
+for i in 0 1 2 3 4 5 6 7 8 9;
+do
+ for j in 0 1 2 3 4 5 6 7 8 9;
+ do
+ echo "$i$j" >"path$i$j" || return 1
+ done
+done &&
+
+# LINT: outer loop handles failure explicitly with "|| return 1"
+for i in 0 1 2 3 4 5 6 7 8 9;
+do
+ for j in 0 1 2 3 4 5 6 7 8 9;
+ do
+ echo "$i$j" >"path$i$j"
+ done || return 1
+done &&
+
+# LINT: inner & outer loops handles failure explicitly with "|| return 1"
+for i in 0 1 2 3 4 5 6 7 8 9;
+do
+ for j in 0 1 2 3 4 5 6 7 8 9;
+ do
+ echo "$i$j" >"path$i$j" || return 1
+ done || return 1
+done
diff --git a/t/chainlint/nested-subshell-comment.expect b/t/chainlint/nested-subshell-comment.expect
index 15b68d4..9138cf3 100644
--- a/t/chainlint/nested-subshell-comment.expect
+++ b/t/chainlint/nested-subshell-comment.expect
@@ -6,6 +6,6 @@
# minor numbers of cows (or do they?)
baz &&
snaff
-?!AMP?!>> )
+ ) ?!AMP?!
fuzzy
->)
+)
diff --git a/t/chainlint/nested-subshell-comment.test b/t/chainlint/nested-subshell-comment.test
index 0ff136a..0215cdb 100644
--- a/t/chainlint/nested-subshell-comment.test
+++ b/t/chainlint/nested-subshell-comment.test
@@ -7,7 +7,7 @@
# minor numbers of cows (or do they?)
baz &&
snaff
-# LINT: missing "&&" on ')'
+# LINT: missing "&&" on ")"
)
fuzzy
)
diff --git a/t/chainlint/nested-subshell.expect b/t/chainlint/nested-subshell.expect
index c8165ad..73ff285 100644
--- a/t/chainlint/nested-subshell.expect
+++ b/t/chainlint/nested-subshell.expect
@@ -3,10 +3,11 @@
(
echo a &&
echo b
->> ) >file &&
+ ) >file &&
+
cd foo &&
(
- echo a
+ echo a ?!AMP?!
echo b
->> ) >file
->)
+ ) >file
+)
diff --git a/t/chainlint/nested-subshell.test b/t/chainlint/nested-subshell.test
index 998b05a..440ee99 100644
--- a/t/chainlint/nested-subshell.test
+++ b/t/chainlint/nested-subshell.test
@@ -7,7 +7,6 @@
cd foo &&
(
-# LINT: nested multi-line subshell not presently checked for missing "&&"
echo a
echo b
) >file
diff --git a/t/chainlint/not-heredoc.expect b/t/chainlint/not-heredoc.expect
new file mode 100644
index 0000000..2e9bb13
--- /dev/null
+++ b/t/chainlint/not-heredoc.expect
@@ -0,0 +1,14 @@
+echo "<<<<<<< ours" &&
+echo ourside &&
+echo "=======" &&
+echo theirside &&
+echo ">>>>>>> theirs" &&
+
+(
+ echo "<<<<<<< ours" &&
+ echo ourside &&
+ echo "=======" &&
+ echo theirside &&
+ echo ">>>>>>> theirs" ?!AMP?!
+ poodle
+) >merged
diff --git a/t/chainlint/not-heredoc.test b/t/chainlint/not-heredoc.test
new file mode 100644
index 0000000..9aa5734
--- /dev/null
+++ b/t/chainlint/not-heredoc.test
@@ -0,0 +1,16 @@
+# LINT: "<< ours" inside string is not here-doc
+echo "<<<<<<< ours" &&
+echo ourside &&
+echo "=======" &&
+echo theirside &&
+echo ">>>>>>> theirs" &&
+
+(
+# LINT: "<< ours" inside string is not here-doc
+ echo "<<<<<<< ours" &&
+ echo ourside &&
+ echo "=======" &&
+ echo theirside &&
+ echo ">>>>>>> theirs"
+ poodle
+) >merged
diff --git a/t/chainlint/one-liner-for-loop.expect b/t/chainlint/one-liner-for-loop.expect
new file mode 100644
index 0000000..51a3dc7
--- /dev/null
+++ b/t/chainlint/one-liner-for-loop.expect
@@ -0,0 +1,9 @@
+git init dir-rename-and-content &&
+(
+ cd dir-rename-and-content &&
+ test_write_lines 1 2 3 4 5 >foo &&
+ mkdir olddir &&
+ for i in a b c; do echo $i >olddir/$i; ?!LOOP?! done ?!AMP?!
+ git add foo olddir &&
+ git commit -m "original" &&
+)
diff --git a/t/chainlint/one-liner-for-loop.test b/t/chainlint/one-liner-for-loop.test
new file mode 100644
index 0000000..4bd8c06
--- /dev/null
+++ b/t/chainlint/one-liner-for-loop.test
@@ -0,0 +1,10 @@
+git init dir-rename-and-content &&
+(
+ cd dir-rename-and-content &&
+ test_write_lines 1 2 3 4 5 >foo &&
+ mkdir olddir &&
+# LINT: one-liner for-loop missing "|| exit"; also broken &&-chain
+ for i in a b c; do echo $i >olddir/$i; done
+ git add foo olddir &&
+ git commit -m "original" &&
+)
diff --git a/t/chainlint/one-liner.expect b/t/chainlint/one-liner.expect
index 237f227..57a7a44 100644
--- a/t/chainlint/one-liner.expect
+++ b/t/chainlint/one-liner.expect
@@ -2,8 +2,8 @@
(foo && bar) |
(foo && bar) >baz &&
-?!SEMI?!(foo; bar) &&
-?!SEMI?!(foo; bar) |
-?!SEMI?!(foo; bar) >baz
+(foo; ?!AMP?! bar) &&
+(foo; ?!AMP?! bar) |
+(foo; ?!AMP?! bar) >baz &&
(foo "bar; baz")
diff --git a/t/chainlint/one-liner.test b/t/chainlint/one-liner.test
index ec9acb9..be9858f 100644
--- a/t/chainlint/one-liner.test
+++ b/t/chainlint/one-liner.test
@@ -3,10 +3,10 @@
(foo && bar) |
(foo && bar) >baz &&
-# LINT: top-level one-liner subshell missing internal "&&"
+# LINT: top-level one-liner subshell missing internal "&&" and broken &&-chain
(foo; bar) &&
(foo; bar) |
-(foo; bar) >baz
+(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
index 98b3d88..1290fd1 100644
--- a/t/chainlint/p4-filespec.expect
+++ b/t/chainlint/p4-filespec.expect
@@ -1,4 +1,4 @@
(
p4 print -1 //depot/fiddle#42 >file &&
foobar
->)
+)
diff --git a/t/chainlint/pipe.expect b/t/chainlint/pipe.expect
index 211b901..811971b 100644
--- a/t/chainlint/pipe.expect
+++ b/t/chainlint/pipe.expect
@@ -2,7 +2,9 @@
foo |
bar |
baz &&
+
fish |
-?!AMP?! cow
+ cow ?!AMP?!
+
sunder
->)
+)
diff --git a/t/chainlint/pipe.test b/t/chainlint/pipe.test
index e6af4de..dd82534 100644
--- a/t/chainlint/pipe.test
+++ b/t/chainlint/pipe.test
@@ -4,7 +4,7 @@
bar |
baz &&
-# LINT: final line of pipe sequence ('cow') lacking "&&"
+# LINT: final line of pipe sequence ("cow") lacking "&&"
fish |
cow
diff --git a/t/chainlint/return-loop.expect b/t/chainlint/return-loop.expect
new file mode 100644
index 0000000..cfc0549
--- /dev/null
+++ b/t/chainlint/return-loop.expect
@@ -0,0 +1,5 @@
+while test $i -lt $((num - 5))
+do
+ git notes add -m "notes for commit$i" HEAD~$i || return 1
+ i=$((i + 1))
+done
diff --git a/t/chainlint/return-loop.test b/t/chainlint/return-loop.test
new file mode 100644
index 0000000..f90b171
--- /dev/null
+++ b/t/chainlint/return-loop.test
@@ -0,0 +1,6 @@
+while test $i -lt $((num - 5))
+do
+# LINT: "|| return {n}" valid loop escape outside subshell; no "&&" needed
+ git notes add -m "notes for commit$i" HEAD~$i || return 1
+ i=$((i + 1))
+done
diff --git a/t/chainlint/semicolon.expect b/t/chainlint/semicolon.expect
index 1d79384..3aa2259 100644
--- a/t/chainlint/semicolon.expect
+++ b/t/chainlint/semicolon.expect
@@ -1,20 +1,19 @@
(
-?!AMP?!?!SEMI?! cat foo ; echo bar
-?!SEMI?! cat foo ; echo bar
->) &&
+ cat foo ; ?!AMP?! echo bar ?!AMP?!
+ cat foo ; ?!AMP?! echo bar
+) &&
(
-?!SEMI?! cat foo ; echo bar &&
-?!SEMI?! cat foo ; echo bar
->) &&
+ cat foo ; ?!AMP?! echo bar &&
+ cat foo ; ?!AMP?! echo bar
+) &&
(
echo "foo; bar" &&
-?!SEMI?! cat foo; echo bar
->) &&
+ cat foo; ?!AMP?! echo bar
+) &&
(
-?!SEMI?! foo;
->) &&
-(
-cd foo &&
+ foo;
+) &&
+(cd foo &&
for i in a b c; do
-?!SEMI?! echo;
-> done)
+ echo; ?!LOOP?!
+ done)
diff --git a/t/chainlint/semicolon.test b/t/chainlint/semicolon.test
index d82c8eb..67e1192 100644
--- a/t/chainlint/semicolon.test
+++ b/t/chainlint/semicolon.test
@@ -15,11 +15,11 @@
cat foo; echo bar
) &&
(
-# LINT: unnecessary terminating semicolon
+# LINT: semicolon unnecessary but legitimate
foo;
) &&
(cd foo &&
for i in a b c; do
-# LINT: unnecessary terminating semicolon
+# LINT: semicolon unnecessary but legitimate
echo;
done)
diff --git a/t/chainlint/sqstring-in-sqstring.expect b/t/chainlint/sqstring-in-sqstring.expect
new file mode 100644
index 0000000..cf0b591
--- /dev/null
+++ b/t/chainlint/sqstring-in-sqstring.expect
@@ -0,0 +1,4 @@
+perl -e '
+ defined($_ = -s $_) or die for @ARGV;
+ exit 1 if $ARGV[0] <= $ARGV[1];
+' test-2-$packname_2.pack test-3-$packname_3.pack
diff --git a/t/chainlint/sqstring-in-sqstring.test b/t/chainlint/sqstring-in-sqstring.test
new file mode 100644
index 0000000..77a425e
--- /dev/null
+++ b/t/chainlint/sqstring-in-sqstring.test
@@ -0,0 +1,5 @@
+# LINT: SQ-string Perl code fragment within SQ-string
+perl -e '\''
+ defined($_ = -s $_) or die for @ARGV;
+ exit 1 if $ARGV[0] <= $ARGV[1];
+'\'' test-2-$packname_2.pack test-3-$packname_3.pack
diff --git a/t/chainlint/subshell-here-doc.expect b/t/chainlint/subshell-here-doc.expect
index 74723e7..75d6f60 100644
--- a/t/chainlint/subshell-here-doc.expect
+++ b/t/chainlint/subshell-here-doc.expect
@@ -1,11 +1,30 @@
(
- echo wobba gorgo snoot wafta snurb &&
-?!AMP?! cat >bip
- echo >bop
->) &&
+ echo wobba \
+ gorgo snoot \
+ wafta snurb <<-EOF &&
+ quoth the raven,
+ nevermore...
+ EOF
+
+ cat <<EOF >bip ?!AMP?!
+ fish fly high
+EOF
+
+ echo <<-\EOF >bop
+ gomez
+ morticia
+ wednesday
+ pugsly
+ EOF
+) &&
(
- cat >bup &&
- cat >bup2 &&
- cat >bup3 &&
+ cat <<-\ARBITRARY >bup &&
+ glink
+ FIZZ
+ ARBITRARY
+ cat <<-"ARBITRARY3" >bup3 &&
+ glink
+ FIZZ
+ ARBITRARY3
meep
->)
+)
diff --git a/t/chainlint/subshell-here-doc.test b/t/chainlint/subshell-here-doc.test
index f6b3ba4..d40eb65 100644
--- a/t/chainlint/subshell-here-doc.test
+++ b/t/chainlint/subshell-here-doc.test
@@ -8,10 +8,10 @@
nevermore...
EOF
-# LINT: missing "&&" on 'cat'
+# LINT: missing "&&" on "cat"
cat <<EOF >bip
fish fly high
- EOF
+EOF
# LINT: swallow here-doc (EOF is last line of subshell)
echo <<-\EOF >bop
@@ -27,10 +27,6 @@
glink
FIZZ
ARBITRARY
- cat <<-'ARBITRARY2' >bup2 &&
- glink
- FIZZ
- ARBITRARY2
cat <<-"ARBITRARY3" >bup3 &&
glink
FIZZ
diff --git a/t/chainlint/subshell-one-liner.expect b/t/chainlint/subshell-one-liner.expect
index 5116282..8f69499 100644
--- a/t/chainlint/subshell-one-liner.expect
+++ b/t/chainlint/subshell-one-liner.expect
@@ -2,13 +2,18 @@
(foo && bar) &&
(foo && bar) |
(foo && bar) >baz &&
-?!SEMI?! (foo; bar) &&
-?!SEMI?! (foo; bar) |
-?!SEMI?! (foo; bar) >baz &&
+
+ (foo; ?!AMP?! bar) &&
+ (foo; ?!AMP?! bar) |
+ (foo; ?!AMP?! bar) >baz &&
+
(foo || exit 1) &&
(foo || exit 1) |
(foo || exit 1) >baz &&
-?!AMP?! (foo && bar)
-?!AMP?!?!SEMI?! (foo && bar; baz)
+
+ (foo && bar) ?!AMP?!
+
+ (foo && bar; ?!AMP?! baz) ?!AMP?!
+
foobar
->)
+)
diff --git a/t/chainlint/t7900-subtree.expect b/t/chainlint/t7900-subtree.expect
index c991342..02f3129 100644
--- a/t/chainlint/t7900-subtree.expect
+++ b/t/chainlint/t7900-subtree.expect
@@ -1,10 +1,22 @@
(
- chks="sub1sub2sub3sub4" &&
- chks_sub=$(cat | sed 's,^,sub dir/,'
->>) &&
- chkms="main-sub1main-sub2main-sub3main-sub4" &&
- chkms_sub=$(cat | sed 's,^,sub dir/,'
->>) &&
+ 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"
->)
+ check_equal "$subfiles" "$chkms
+$chks"
+)
diff --git a/t/chainlint/t7900-subtree.test b/t/chainlint/t7900-subtree.test
index 277d835..02f3129 100644
--- a/t/chainlint/t7900-subtree.test
+++ b/t/chainlint/t7900-subtree.test
@@ -3,7 +3,7 @@
sub2
sub3
sub4" &&
- chks_sub=$(cat <<TXT | sed 's,^,sub dir/,'
+ chks_sub=$(cat <<TXT | sed "s,^,sub dir/,"
$chks
TXT
) &&
@@ -11,7 +11,7 @@ TXT
main-sub2
main-sub3
main-sub4" &&
- chkms_sub=$(cat <<TXT | sed 's,^,sub dir/,'
+ chkms_sub=$(cat <<TXT | sed "s,^,sub dir/,"
$chkms
TXT
) &&
diff --git a/t/chainlint/token-pasting.expect b/t/chainlint/token-pasting.expect
new file mode 100644
index 0000000..6a38791
--- /dev/null
+++ b/t/chainlint/token-pasting.expect
@@ -0,0 +1,27 @@
+git config filter.rot13.smudge ./rot13.sh &&
+git config filter.rot13.clean ./rot13.sh &&
+
+{
+ echo "*.t filter=rot13" ?!AMP?!
+ echo "*.i ident"
+} >.gitattributes &&
+
+{
+ echo a b c d e f g h i j k l m ?!AMP?!
+ echo n o p q r s t u v w x y z ?!AMP?!
+ echo '$Id$'
+} >test &&
+cat test >test.t &&
+cat test >test.o &&
+cat test >test.i &&
+git add test test.t test.i &&
+rm -f test test.t test.i &&
+git checkout -- test test.t test.i &&
+
+echo "content-test2" >test2.o &&
+echo "content-test3 - filename with special characters" >"test3 'sq',$x=.o" ?!AMP?!
+
+downstream_url_for_sed=$(
+ printf "%sn" "$downstream_url" |
+ sed -e 's/\/\\/g' -e 's/[[/.*^$]/\&/g'
+)
diff --git a/t/chainlint/token-pasting.test b/t/chainlint/token-pasting.test
new file mode 100644
index 0000000..b4610ce
--- /dev/null
+++ b/t/chainlint/token-pasting.test
@@ -0,0 +1,32 @@
+# LINT: single token; composite of multiple strings
+git config filter.rot13.smudge ./rot13.sh &&
+git config filter.rot13.clean ./rot13.sh &&
+
+{
+ echo "*.t filter=rot13"
+ echo "*.i ident"
+} >.gitattributes &&
+
+{
+ echo a b c d e f g h i j k l m
+ echo n o p q r s t u v w x y z
+# LINT: exit/enter string context and escaped-quote outside of string
+ echo '\''$Id$'\''
+} >test &&
+cat test >test.t &&
+cat test >test.o &&
+cat test >test.i &&
+git add test test.t test.i &&
+rm -f test test.t test.i &&
+git checkout -- test test.t test.i &&
+
+echo "content-test2" >test2.o &&
+# LINT: exit/enter string context and escaped-quote outside of string
+echo "content-test3 - filename with special characters" >"test3 '\''sq'\'',\$x=.o"
+
+# LINT: single token; composite of multiple strings
+downstream_url_for_sed=$(
+ printf "%s\n" "$downstream_url" |
+# LINT: exit/enter string context; "&" inside string not command terminator
+ sed -e '\''s/\\/\\\\/g'\'' -e '\''s/[[/.*^$]/\\&/g'\''
+)
diff --git a/t/chainlint/unclosed-here-doc-indent.expect b/t/chainlint/unclosed-here-doc-indent.expect
new file mode 100644
index 0000000..7c30a1a
--- /dev/null
+++ b/t/chainlint/unclosed-here-doc-indent.expect
@@ -0,0 +1,4 @@
+command_which_is_run &&
+cat >expect <<-\EOF ?!UNCLOSED-HEREDOC?! &&
+we forget to end the here-doc
+command_which_is_gobbled
diff --git a/t/chainlint/unclosed-here-doc-indent.test b/t/chainlint/unclosed-here-doc-indent.test
new file mode 100644
index 0000000..5c841a9
--- /dev/null
+++ b/t/chainlint/unclosed-here-doc-indent.test
@@ -0,0 +1,4 @@
+command_which_is_run &&
+cat >expect <<-\EOF &&
+we forget to end the here-doc
+command_which_is_gobbled
diff --git a/t/chainlint/unclosed-here-doc.expect b/t/chainlint/unclosed-here-doc.expect
new file mode 100644
index 0000000..d65e50f
--- /dev/null
+++ b/t/chainlint/unclosed-here-doc.expect
@@ -0,0 +1,7 @@
+command_which_is_run &&
+cat >expect <<\EOF ?!UNCLOSED-HEREDOC?! &&
+ we try to end the here-doc below,
+ but the indentation throws us off
+ since the operator is not "<<-".
+ EOF
+command_which_is_gobbled
diff --git a/t/chainlint/unclosed-here-doc.test b/t/chainlint/unclosed-here-doc.test
new file mode 100644
index 0000000..69d3786
--- /dev/null
+++ b/t/chainlint/unclosed-here-doc.test
@@ -0,0 +1,7 @@
+command_which_is_run &&
+cat >expect <<\EOF &&
+ we try to end the here-doc below,
+ but the indentation throws us off
+ since the operator is not "<<-".
+ EOF
+command_which_is_gobbled
diff --git a/t/chainlint/while-loop.expect b/t/chainlint/while-loop.expect
index 13cff2c..06c1567 100644
--- a/t/chainlint/while-loop.expect
+++ b/t/chainlint/while-loop.expect
@@ -1,11 +1,14 @@
(
while true
do
-?!AMP?! echo foo
- cat
-?!AMP?! done
+ echo foo ?!AMP?!
+ cat <<-\EOF ?!LOOP?!
+ bar
+ EOF
+ done ?!AMP?!
+
while true; do
echo foo &&
- cat bar
+ cat bar ?!LOOP?!
done
->)
+)
diff --git a/t/chainlint/while-loop.test b/t/chainlint/while-loop.test
index f1df085..d09fb01 100644
--- a/t/chainlint/while-loop.test
+++ b/t/chainlint/while-loop.test
@@ -1,17 +1,17 @@
(
-# LINT: 'while, 'do', 'done' do not need "&&"
+# LINT: "while", "do", "done" do not need "&&"
while true
do
-# LINT: missing "&&" on 'echo'
+# LINT: missing "&&" on "echo"
echo foo
# LINT: last statement of while does not need "&&"
cat <<-\EOF
bar
EOF
-# LINT: missing "&&" on 'done'
+# LINT: missing "&&" on "done"
done
-# LINT: 'do' on same line as 'while'
+# LINT: "do" on same line as "while"
while true; do
echo foo &&
cat bar
diff --git a/t/check-non-portable-shell.pl b/t/check-non-portable-shell.pl
index fd33035..b2b28c2 100755
--- a/t/check-non-portable-shell.pl
+++ b/t/check-non-portable-shell.pl
@@ -45,7 +45,10 @@ while (<>) {
/\bhead\s+-c\b/ and err 'head -c is not portable (use test_copy_bytes BYTES <file >out)';
/(?:\$\(seq|^\s*seq\b)/ and err 'seq is not portable (use test_seq)';
/\bgrep\b.*--file\b/ and err 'grep --file FILE is not portable (use grep -f FILE)';
+ /\b[ef]grep\b/ and err 'egrep/fgrep obsolescent (use grep -E/-F)';
/\bexport\s+[A-Za-z0-9_]*=/ and err '"export FOO=bar" is not portable (use FOO=bar && export FOO)';
+ /\blocal\s+[A-Za-z0-9_]*=\$([A-Za-z0-9_{]|[(][^(])/ and
+ err q(quote "$val" in 'local var=$val');
/^\s*([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and exists($func{$4}) and
err '"FOO=bar shell_func" assignment extends beyond "shell_func"';
$line = '';
diff --git a/t/helper/test-advise.c b/t/helper/test-advise.c
index cb88113..8a3fd00 100644
--- a/t/helper/test-advise.c
+++ b/t/helper/test-advise.c
@@ -1,7 +1,7 @@
#include "test-tool.h"
-#include "cache.h"
#include "advice.h"
#include "config.h"
+#include "setup.h"
int cmd__advise_if_enabled(int argc, const char **argv)
{
diff --git a/t/helper/test-bitmap.c b/t/helper/test-bitmap.c
index 134a1e9..af43ee1 100644
--- a/t/helper/test-bitmap.c
+++ b/t/helper/test-bitmap.c
@@ -1,12 +1,18 @@
#include "test-tool.h"
-#include "cache.h"
+#include "git-compat-util.h"
#include "pack-bitmap.h"
+#include "setup.h"
static int bitmap_list_commits(void)
{
return test_bitmap_commits(the_repository);
}
+static int bitmap_dump_hashes(void)
+{
+ return test_bitmap_hashes(the_repository);
+}
+
int cmd__bitmap(int argc, const char **argv)
{
setup_git_directory();
@@ -16,9 +22,12 @@ int cmd__bitmap(int argc, const char **argv)
if (!strcmp(argv[1], "list-commits"))
return bitmap_list_commits();
+ if (!strcmp(argv[1], "dump-hashes"))
+ return bitmap_dump_hashes();
usage:
- usage("\ttest-tool bitmap list-commits");
+ usage("\ttest-tool bitmap list-commits\n"
+ "\ttest-tool bitmap dump-hashes");
return -1;
}
diff --git a/t/helper/test-bloom.c b/t/helper/test-bloom.c
index ad3ef1c..1281e66 100644
--- a/t/helper/test-bloom.c
+++ b/t/helper/test-bloom.c
@@ -1,7 +1,9 @@
-#include "git-compat-util.h"
-#include "bloom.h"
#include "test-tool.h"
+#include "bloom.h"
+#include "hex.h"
#include "commit.h"
+#include "repository.h"
+#include "setup.h"
static struct bloom_filter_settings settings = DEFAULT_BLOOM_FILTER_SETTINGS;
@@ -16,6 +18,7 @@ static void add_string_to_filter(const char *data, struct bloom_filter *filter)
}
printf("\n");
add_key_to_filter(&key, filter, &settings);
+ clear_bloom_key(&key);
}
static void print_bloom_filter(struct bloom_filter *filter) {
@@ -37,7 +40,6 @@ static void get_bloom_filter_for_commit(const struct object_id *commit_oid)
{
struct commit *c;
struct bloom_filter *filter;
- setup_git_directory();
c = lookup_commit(the_repository, commit_oid);
filter = get_or_compute_bloom_filter(the_repository, c, 1,
&settings,
@@ -80,6 +82,7 @@ int cmd__bloom(int argc, const char **argv)
}
print_bloom_filter(&filter);
+ free(filter.data);
}
if (!strcmp(argv[1], "get_filter_for_commit")) {
diff --git a/t/helper/test-bundle-uri.c b/t/helper/test-bundle-uri.c
new file mode 100644
index 0000000..09dc787
--- /dev/null
+++ b/t/helper/test-bundle-uri.c
@@ -0,0 +1,135 @@
+#include "test-tool.h"
+#include "parse-options.h"
+#include "bundle-uri.h"
+#include "gettext.h"
+#include "strbuf.h"
+#include "string-list.h"
+#include "transport.h"
+#include "remote.h"
+
+enum input_mode {
+ KEY_VALUE_PAIRS,
+ CONFIG_FILE,
+};
+
+static int cmd__bundle_uri_parse(int argc, const char **argv, enum input_mode mode)
+{
+ const char *key_value_usage[] = {
+ "test-tool bundle-uri parse-key-values <input>",
+ NULL
+ };
+ const char *config_usage[] = {
+ "test-tool bundle-uri parse-config <input>",
+ NULL
+ };
+ const char **usage = key_value_usage;
+ struct option options[] = {
+ OPT_END(),
+ };
+ struct strbuf sb = STRBUF_INIT;
+ struct bundle_list list;
+ int err = 0;
+ FILE *fp;
+
+ if (mode == CONFIG_FILE)
+ usage = config_usage;
+
+ argc = parse_options(argc, argv, NULL, options, usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+
+ init_bundle_list(&list);
+
+ list.baseURI = xstrdup("<uri>");
+
+ switch (mode) {
+ case KEY_VALUE_PAIRS:
+ if (argc != 1)
+ goto usage;
+ fp = fopen(argv[0], "r");
+ if (!fp)
+ die("failed to open '%s'", argv[0]);
+ while (strbuf_getline(&sb, fp) != EOF) {
+ if (bundle_uri_parse_line(&list, sb.buf))
+ err = error("bad line: '%s'", sb.buf);
+ }
+ fclose(fp);
+ break;
+
+ case CONFIG_FILE:
+ if (argc != 1)
+ goto usage;
+ err = bundle_uri_parse_config_format("<uri>", argv[0], &list);
+ break;
+ }
+ strbuf_release(&sb);
+
+ print_bundle_list(stdout, &list);
+
+ clear_bundle_list(&list);
+
+ return !!err;
+
+usage:
+ usage_with_options(usage, options);
+}
+
+static int cmd_ls_remote(int argc, const char **argv)
+{
+ const char *dest;
+ struct remote *remote;
+ struct transport *transport;
+ int status = 0;
+
+ dest = argc > 1 ? argv[1] : NULL;
+
+ remote = remote_get(dest);
+ if (!remote) {
+ if (dest)
+ die(_("bad repository '%s'"), dest);
+ die(_("no remote configured to get bundle URIs from"));
+ }
+ if (!remote->url_nr)
+ die(_("remote '%s' has no configured URL"), dest);
+
+ transport = transport_get(remote, NULL);
+ if (transport_get_remote_bundle_uri(transport) < 0) {
+ error(_("could not get the bundle-uri list"));
+ status = 1;
+ goto cleanup;
+ }
+
+ print_bundle_list(stdout, transport->bundles);
+
+cleanup:
+ if (transport_disconnect(transport))
+ return 1;
+ return status;
+}
+
+int cmd__bundle_uri(int argc, const char **argv)
+{
+ const char *usage[] = {
+ "test-tool bundle-uri <subcommand> [<options>]",
+ NULL
+ };
+ struct option options[] = {
+ OPT_END(),
+ };
+
+ argc = parse_options(argc, argv, NULL, options, usage,
+ PARSE_OPT_STOP_AT_NON_OPTION |
+ PARSE_OPT_KEEP_ARGV0);
+ if (argc == 1)
+ goto usage;
+
+ if (!strcmp(argv[1], "parse-key-values"))
+ return cmd__bundle_uri_parse(argc - 1, argv + 1, KEY_VALUE_PAIRS);
+ if (!strcmp(argv[1], "parse-config"))
+ return cmd__bundle_uri_parse(argc - 1, argv + 1, CONFIG_FILE);
+ if (!strcmp(argv[1], "ls-remote"))
+ return cmd_ls_remote(argc - 1, argv + 1);
+ error("there is no test-tool bundle-uri tool '%s'", argv[1]);
+
+usage:
+ usage_with_options(usage, options);
+}
diff --git a/t/helper/test-cache-tree.c b/t/helper/test-cache-tree.c
new file mode 100644
index 0000000..e723639
--- /dev/null
+++ b/t/helper/test-cache-tree.c
@@ -0,0 +1,69 @@
+#define USE_THE_INDEX_VARIABLE
+#include "test-tool.h"
+#include "gettext.h"
+#include "hex.h"
+#include "tree.h"
+#include "cache-tree.h"
+#include "parse-options.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
+
+static char const * const test_cache_tree_usage[] = {
+ N_("test-tool cache-tree <options> (control|prime|update)"),
+ NULL
+};
+
+int cmd__cache_tree(int argc, const char **argv)
+{
+ struct object_id oid;
+ struct tree *tree;
+ int empty = 0;
+ int invalidate_qty = 0;
+ int i;
+
+ struct option options[] = {
+ OPT_BOOL(0, "empty", &empty,
+ N_("clear the cache tree before each iteration")),
+ OPT_INTEGER_F(0, "invalidate", &invalidate_qty,
+ N_("number of entries in the cache tree to invalidate (default 0)"),
+ PARSE_OPT_NONEG),
+ OPT_END()
+ };
+
+ setup_git_directory();
+
+ argc = parse_options(argc, argv, NULL, options, test_cache_tree_usage, 0);
+
+ if (repo_read_index(the_repository) < 0)
+ die(_("unable to read index file"));
+
+ oidcpy(&oid, &the_index.cache_tree->oid);
+ tree = parse_tree_indirect(&oid);
+ if (!tree)
+ die(_("not a tree object: %s"), oid_to_hex(&oid));
+
+ if (empty) {
+ /* clear the cache tree & allocate a new one */
+ cache_tree_free(&the_index.cache_tree);
+ the_index.cache_tree = cache_tree();
+ } else if (invalidate_qty) {
+ /* invalidate the specified number of unique paths */
+ float f_interval = (float)the_index.cache_nr / invalidate_qty;
+ int interval = f_interval < 1.0 ? 1 : (int)f_interval;
+ for (i = 0; i < invalidate_qty && i * interval < the_index.cache_nr; i++)
+ cache_tree_invalidate_path(&the_index, the_index.cache[i * interval]->name);
+ }
+
+ if (argc != 1)
+ usage_with_options(test_cache_tree_usage, options);
+ else if (!strcmp(argv[0], "prime"))
+ prime_cache_tree(the_repository, &the_index, tree);
+ else if (!strcmp(argv[0], "update"))
+ cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
+ /* use "control" subcommand to specify no-op */
+ else if (!!strcmp(argv[0], "control"))
+ die(_("Unhandled subcommand '%s'"), argv[0]);
+
+ return 0;
+}
diff --git a/t/helper/test-chmtime.c b/t/helper/test-chmtime.c
index 524b55c..0e55388 100644
--- a/t/helper/test-chmtime.c
+++ b/t/helper/test-chmtime.c
@@ -94,7 +94,7 @@ int cmd__chmtime(int argc, const char **argv)
if (timespec_arg(argv[i], &set_time, &set_eq)) {
++i;
} else {
- if (get == 0) {
+ if (get == 0 && verbose == 0) {
fprintf(stderr, "Not a base-10 integer: %s\n", argv[i] + 1);
goto usage;
}
@@ -134,6 +134,21 @@ int cmd__chmtime(int argc, const char **argv)
}
if (utb.modtime != sb.st_mtime && utime(argv[i], &utb) < 0) {
+#ifdef GIT_WINDOWS_NATIVE
+ if (S_ISDIR(sb.st_mode)) {
+ /*
+ * NEEDSWORK: The Windows version of `utime()`
+ * (aka `mingw_utime()`) does not correctly
+ * handle directory arguments, since it uses
+ * `_wopen()`. Ignore it for now since this
+ * is just a test.
+ */
+ fprintf(stderr,
+ ("Failed to modify time on directory %s. "
+ "Skipping\n"), argv[i]);
+ continue;
+ }
+#endif
fprintf(stderr, "Failed to modify time on %s: %s\n",
argv[i], strerror(errno));
return 1;
diff --git a/t/helper/test-config.c b/t/helper/test-config.c
index a6e9367..ed444ca 100644
--- a/t/helper/test-config.c
+++ b/t/helper/test-config.c
@@ -1,6 +1,6 @@
#include "test-tool.h"
-#include "cache.h"
#include "config.h"
+#include "setup.h"
#include "string-list.h"
/*
@@ -14,6 +14,8 @@
* get_value_multi -> prints all values for the entered key in increasing order
* of priority
*
+ * get -> print return value for the entered key
+ *
* get_int -> print integer value for the entered key or die
*
* get_bool -> print bool value for the entered key or die
@@ -30,6 +32,9 @@
* iterate -> iterate over all values using git_config(), and print some
* data for each
*
+ * git_config_int -> iterate over all values using git_config() and print the
+ * integer value for the entered key or die
+ *
* Examples:
*
* To print the value with highest priority for key "foo.bAr Baz.rock":
@@ -37,8 +42,11 @@
*
*/
-static int iterate_cb(const char *var, const char *value, void *data)
+static int iterate_cb(const char *var, const char *value,
+ const struct config_context *ctx,
+ void *data UNUSED)
{
+ const struct key_value_info *kvi = ctx->kvi;
static int nr;
if (nr++)
@@ -46,15 +54,29 @@ static int iterate_cb(const char *var, const char *value, void *data)
printf("key=%s\n", var);
printf("value=%s\n", value ? value : "(null)");
- printf("origin=%s\n", current_config_origin_type());
- printf("name=%s\n", current_config_name());
- printf("lno=%d\n", current_config_line());
- printf("scope=%s\n", config_scope_name(current_config_scope()));
+ printf("origin=%s\n", config_origin_type_name(kvi->origin_type));
+ printf("name=%s\n", kvi->filename ? kvi->filename : "");
+ printf("lno=%d\n", kvi->linenr);
+ printf("scope=%s\n", config_scope_name(kvi->scope));
return 0;
}
-static int early_config_cb(const char *var, const char *value, void *vdata)
+static int parse_int_cb(const char *var, const char *value,
+ const struct config_context *ctx, void *data)
+{
+ const char *key_to_match = data;
+
+ if (!strcmp(key_to_match, var)) {
+ int parsed = git_config_int(value, value, ctx->kvi);
+ printf("%d\n", parsed);
+ }
+ return 0;
+}
+
+static int early_config_cb(const char *var, const char *value,
+ const struct config_context *ctx UNUSED,
+ void *vdata)
{
const char *key = vdata;
@@ -95,8 +117,7 @@ int cmd__config(int argc, const char **argv)
goto exit1;
}
} else if (argc == 3 && !strcmp(argv[1], "get_value_multi")) {
- strptr = git_config_get_value_multi(argv[2]);
- if (strptr) {
+ if (!git_config_get_value_multi(argv[2], &strptr)) {
for (i = 0; i < strptr->nr; i++) {
v = strptr->items[i].string;
if (!v)
@@ -109,6 +130,26 @@ int cmd__config(int argc, const char **argv)
printf("Value not found for \"%s\"\n", argv[2]);
goto exit1;
}
+ } else if (argc == 3 && !strcmp(argv[1], "get")) {
+ int ret;
+
+ if (!(ret = git_config_get(argv[2])))
+ goto exit0;
+ else if (ret == 1)
+ printf("Value not found for \"%s\"\n", argv[2]);
+ else if (ret == -CONFIG_INVALID_KEY)
+ printf("Key \"%s\" is invalid\n", argv[2]);
+ else if (ret == -CONFIG_NO_SECTION_OR_NAME)
+ printf("Key \"%s\" has no section\n", argv[2]);
+ else
+ /*
+ * A normal caller should just check "ret <
+ * 0", but for our own tests let's BUG() if
+ * our whitelist of git_config_parse_key()
+ * return values isn't exhaustive.
+ */
+ BUG("Key \"%s\" has unknown return %d", argv[2], ret);
+ goto exit1;
} else if (argc == 3 && !strcmp(argv[1], "get_int")) {
if (!git_config_get_int(argv[2], &val)) {
printf("%d\n", val);
@@ -141,7 +182,7 @@ int cmd__config(int argc, const char **argv)
goto exit2;
}
}
- if (!git_configset_get_value(&cs, argv[2], &v)) {
+ if (!git_configset_get_value(&cs, argv[2], &v, NULL)) {
if (!v)
printf("(NULL)\n");
else
@@ -159,8 +200,7 @@ int cmd__config(int argc, const char **argv)
goto exit2;
}
}
- strptr = git_configset_get_value_multi(&cs, argv[2]);
- if (strptr) {
+ if (!git_configset_get_value_multi(&cs, argv[2], &strptr)) {
for (i = 0; i < strptr->nr; i++) {
v = strptr->items[i].string;
if (!v)
@@ -176,6 +216,9 @@ int cmd__config(int argc, const char **argv)
} else if (!strcmp(argv[1], "iterate")) {
git_config(iterate_cb, NULL);
goto exit0;
+ } else if (argc == 3 && !strcmp(argv[1], "git_config_int")) {
+ git_config(parse_int_cb, (void *) argv[2]);
+ goto exit0;
}
die("%s: Please check the syntax and the function name", argv[0]);
diff --git a/t/helper/test-crontab.c b/t/helper/test-crontab.c
index e7c0137..597027a 100644
--- a/t/helper/test-crontab.c
+++ b/t/helper/test-crontab.c
@@ -1,34 +1,34 @@
#include "test-tool.h"
-#include "cache.h"
/*
- * Usage: test-tool cron <file> [-l]
+ * Usage: test-tool crontab <file> -l|<input>
*
* If -l is specified, then write the contents of <file> to stdout.
- * Otherwise, write from stdin into <file>.
+ * Otherwise, copy the contents of <input> into <file>.
*/
int cmd__crontab(int argc, const char **argv)
{
int a;
FILE *from, *to;
- if (argc == 3 && !strcmp(argv[2], "-l")) {
+ if (argc != 3)
+ usage("test-tool crontab <file> -l|<input>");
+
+ if (!strcmp(argv[2], "-l")) {
from = fopen(argv[1], "r");
if (!from)
return 0;
to = stdout;
- } else if (argc == 2) {
- from = stdin;
- to = fopen(argv[1], "w");
- } else
- return error("unknown arguments");
+ } else {
+ from = xfopen(argv[2], "r");
+ to = xfopen(argv[1], "w");
+ }
while ((a = fgetc(from)) != EOF)
fputc(a, to);
- if (argc == 3)
- fclose(from);
- else
+ fclose(from);
+ if (to != stdout)
fclose(to);
return 0;
diff --git a/t/helper/test-csprng.c b/t/helper/test-csprng.c
new file mode 100644
index 0000000..65d1497
--- /dev/null
+++ b/t/helper/test-csprng.c
@@ -0,0 +1,29 @@
+#include "test-tool.h"
+#include "git-compat-util.h"
+
+
+int cmd__csprng(int argc, const char **argv)
+{
+ unsigned long count;
+ unsigned char buf[1024];
+
+ if (argc > 2) {
+ fprintf(stderr, "usage: %s [<size>]\n", argv[0]);
+ return 2;
+ }
+
+ count = (argc == 2) ? strtoul(argv[1], NULL, 0) : -1L;
+
+ while (count) {
+ unsigned long chunk = count < sizeof(buf) ? count : sizeof(buf);
+ if (csprng_bytes(buf, chunk) < 0) {
+ perror("failed to read");
+ return 5;
+ }
+ if (fwrite(buf, chunk, 1, stdout) != chunk)
+ return 1;
+ count -= chunk;
+ }
+
+ return 0;
+}
diff --git a/t/helper/test-ctype.c b/t/helper/test-ctype.c
deleted file mode 100644
index 92c4c23..0000000
--- a/t/helper/test-ctype.c
+++ /dev/null
@@ -1,43 +0,0 @@
-#include "test-tool.h"
-#include "cache.h"
-
-static int rc;
-
-static void report_error(const char *class, int ch)
-{
- printf("%s classifies char %d (0x%02x) wrongly\n", class, ch, ch);
- rc = 1;
-}
-
-static int is_in(const char *s, int ch)
-{
- /* We can't find NUL using strchr. It's classless anyway. */
- if (ch == '\0')
- return 0;
- return !!strchr(s, ch);
-}
-
-#define TEST_CLASS(t,s) { \
- int i; \
- for (i = 0; i < 256; i++) { \
- if (is_in(s, i) != t(i)) \
- report_error(#t, i); \
- } \
-}
-
-#define DIGIT "0123456789"
-#define LOWER "abcdefghijklmnopqrstuvwxyz"
-#define UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-
-int cmd__ctype(int argc, const char **argv)
-{
- TEST_CLASS(isdigit, DIGIT);
- TEST_CLASS(isspace, " \n\r\t");
- TEST_CLASS(isalpha, LOWER UPPER);
- TEST_CLASS(isalnum, LOWER UPPER DIGIT);
- TEST_CLASS(is_glob_special, "*?[\\");
- TEST_CLASS(is_regex_special, "$()*+.?[\\^{|");
- TEST_CLASS(is_pathspec_magic, "!\"#%&',-/:;<=>@_`~");
-
- return rc;
-}
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 099eff4..f25512d 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -1,5 +1,6 @@
#include "test-tool.h"
-#include "cache.h"
+#include "date.h"
+#include "trace.h"
static const char *usage_msg = "\n"
" test-tool date relative [time_t]...\n"
@@ -34,7 +35,7 @@ static void show_human_dates(const char **argv)
static void show_dates(const char **argv, const char *format)
{
- struct date_mode mode;
+ struct date_mode mode = DATE_MODE_INIT;
parse_date_format(format, &mode);
for (; *argv; argv++) {
@@ -51,8 +52,10 @@ static void show_dates(const char **argv, const char *format)
arg++;
tz = atoi(arg);
- printf("%s -> %s\n", *argv, show_date(t, tz, &mode));
+ printf("%s -> %s\n", *argv, show_date(t, tz, mode));
}
+
+ date_mode_release(&mode);
}
static void parse_dates(const char **argv)
@@ -78,7 +81,7 @@ static void parse_approxidate(const char **argv)
{
for (; *argv; argv++) {
timestamp_t t;
- t = approxidate_relative(*argv);
+ t = approxidate(*argv);
printf("%s -> %s\n", *argv, show_date(t, 0, DATE_MODE(ISO8601)));
}
}
@@ -87,7 +90,7 @@ static void parse_approx_timestamp(const char **argv)
{
for (; *argv; argv++) {
timestamp_t t;
- t = approxidate_relative(*argv);
+ t = approxidate(*argv);
printf("%s -> %"PRItime"\n", *argv, t);
}
}
@@ -101,7 +104,7 @@ static void getnanos(const char **argv)
printf("%lf\n", seconds);
}
-int cmd__date(int argc, const char **argv)
+int cmd__date(int argc UNUSED, const char **argv)
{
const char *x;
diff --git a/t/helper/test-delete-gpgsig.c b/t/helper/test-delete-gpgsig.c
new file mode 100644
index 0000000..e36831a
--- /dev/null
+++ b/t/helper/test-delete-gpgsig.c
@@ -0,0 +1,62 @@
+#include "test-tool.h"
+#include "gpg-interface.h"
+#include "strbuf.h"
+
+
+int cmd__delete_gpgsig(int argc, const char **argv)
+{
+ struct strbuf buf = STRBUF_INIT;
+ const char *pattern = "gpgsig";
+ const char *bufptr, *tail, *eol;
+ int deleting = 0;
+ size_t plen;
+
+ if (argc >= 2) {
+ pattern = argv[1];
+ argv++;
+ argc--;
+ }
+
+ plen = strlen(pattern);
+ strbuf_read(&buf, 0, 0);
+
+ if (!strcmp(pattern, "trailer")) {
+ size_t payload_size = parse_signed_buffer(buf.buf, buf.len);
+ fwrite(buf.buf, 1, payload_size, stdout);
+ fflush(stdout);
+ return 0;
+ }
+
+ bufptr = buf.buf;
+ tail = bufptr + buf.len;
+
+ while (bufptr < tail) {
+ /* Find the end of the line */
+ eol = memchr(bufptr, '\n', tail - bufptr);
+ if (!eol)
+ eol = tail;
+
+ /* Drop continuation lines */
+ if (deleting && (bufptr < eol) && (bufptr[0] == ' ')) {
+ bufptr = eol + 1;
+ continue;
+ }
+ deleting = 0;
+
+ /* Does the line match the prefix? */
+ if (((bufptr + plen) < eol) &&
+ !memcmp(bufptr, pattern, plen) &&
+ (bufptr[plen] == ' ')) {
+ deleting = 1;
+ bufptr = eol + 1;
+ continue;
+ }
+
+ /* Print all other lines */
+ fwrite(bufptr, 1, (eol - bufptr) + 1, stdout);
+ bufptr = eol + 1;
+ }
+ fflush(stdout);
+
+ return 0;
+}
diff --git a/t/helper/test-delta.c b/t/helper/test-delta.c
index e749a49..6bc787a 100644
--- a/t/helper/test-delta.c
+++ b/t/helper/test-delta.c
@@ -11,7 +11,6 @@
#include "test-tool.h"
#include "git-compat-util.h"
#include "delta.h"
-#include "cache.h"
static const char usage_str[] =
"test-tool delta (-d|-p) <from_file> <data_file> <out_file>";
@@ -20,8 +19,9 @@ int cmd__delta(int argc, const char **argv)
{
int fd;
struct stat st;
- void *from_buf, *data_buf, *out_buf;
+ void *from_buf = NULL, *data_buf = NULL, *out_buf = NULL;
unsigned long from_size, data_size, out_size;
+ int ret = 1;
if (argc != 5 || (strcmp(argv[1], "-d") && strcmp(argv[1], "-p"))) {
fprintf(stderr, "usage: %s\n", usage_str);
@@ -38,21 +38,21 @@ int cmd__delta(int argc, const char **argv)
if (read_in_full(fd, from_buf, from_size) < 0) {
perror(argv[2]);
close(fd);
- return 1;
+ goto cleanup;
}
close(fd);
fd = open(argv[3], O_RDONLY);
if (fd < 0 || fstat(fd, &st)) {
perror(argv[3]);
- return 1;
+ goto cleanup;
}
data_size = st.st_size;
data_buf = xmalloc(data_size);
if (read_in_full(fd, data_buf, data_size) < 0) {
perror(argv[3]);
close(fd);
- return 1;
+ goto cleanup;
}
close(fd);
@@ -66,14 +66,20 @@ int cmd__delta(int argc, const char **argv)
&out_size);
if (!out_buf) {
fprintf(stderr, "delta operation failed (returned NULL)\n");
- return 1;
+ goto cleanup;
}
fd = open (argv[4], O_WRONLY|O_CREAT|O_TRUNC, 0666);
if (fd < 0 || write_in_full(fd, out_buf, out_size) < 0) {
perror(argv[4]);
- return 1;
+ goto cleanup;
}
- return 0;
+ ret = 0;
+cleanup:
+ free(from_buf);
+ free(data_buf);
+ free(out_buf);
+
+ return ret;
}
diff --git a/t/helper/test-dir-iterator.c b/t/helper/test-dir-iterator.c
index 659b6bf..6b297bd 100644
--- a/t/helper/test-dir-iterator.c
+++ b/t/helper/test-dir-iterator.c
@@ -15,7 +15,7 @@ static const char *error_name(int error_number)
/*
* usage:
- * tool-test dir-iterator [--follow-symlinks] [--pedantic] directory_path
+ * tool-test dir-iterator [--pedantic] directory_path
*/
int cmd__dir_iterator(int argc, const char **argv)
{
@@ -24,9 +24,7 @@ int cmd__dir_iterator(int argc, const char **argv)
int iter_status;
for (++argv, --argc; *argv && starts_with(*argv, "--"); ++argv, --argc) {
- if (strcmp(*argv, "--follow-symlinks") == 0)
- flags |= DIR_ITERATOR_FOLLOW_SYMLINKS;
- else if (strcmp(*argv, "--pedantic") == 0)
+ if (strcmp(*argv, "--pedantic") == 0)
flags |= DIR_ITERATOR_PEDANTIC;
else
die("invalid option '%s'", *argv);
diff --git a/t/helper/test-drop-caches.c b/t/helper/test-drop-caches.c
index 7b42784..73e551c 100644
--- a/t/helper/test-drop-caches.c
+++ b/t/helper/test-drop-caches.c
@@ -3,6 +3,7 @@
#if defined(GIT_WINDOWS_NATIVE)
#include "lazyload.h"
+#include <winnt.h>
static int cmd_sync(void)
{
@@ -86,7 +87,8 @@ static int cmd_dropcaches(void)
{
HANDLE hProcess = GetCurrentProcess();
HANDLE hToken;
- DECLARE_PROC_ADDR(ntdll.dll, DWORD, NtSetSystemInformation, INT, PVOID, ULONG);
+ DECLARE_PROC_ADDR(ntdll.dll, DWORD, NTAPI, NtSetSystemInformation, INT, PVOID,
+ ULONG);
SYSTEM_MEMORY_LIST_COMMAND command;
int status;
@@ -153,7 +155,7 @@ static int cmd_dropcaches(void)
#endif
-int cmd__drop_caches(int argc, const char **argv)
+int cmd__drop_caches(int argc UNUSED, const char **argv UNUSED)
{
cmd_sync();
return cmd_dropcaches();
diff --git a/t/helper/test-dump-cache-tree.c b/t/helper/test-dump-cache-tree.c
index 6a3f88f..c38f546 100644
--- a/t/helper/test-dump-cache-tree.c
+++ b/t/helper/test-dump-cache-tree.c
@@ -1,8 +1,12 @@
+#define USE_THE_INDEX_VARIABLE
#include "test-tool.h"
-#include "cache.h"
+#include "hash.h"
+#include "hex.h"
#include "tree.h"
#include "cache-tree.h"
-
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
static void dump_one(struct cache_tree *it, const char *pfx, const char *x)
{
@@ -55,15 +59,20 @@ static int dump_cache_tree(struct cache_tree *it,
return errs;
}
-int cmd__dump_cache_tree(int ac, const char **av)
+int cmd__dump_cache_tree(int ac UNUSED, const char **av UNUSED)
{
struct index_state istate;
struct cache_tree *another = cache_tree();
+ int ret;
+
setup_git_directory();
- if (read_cache() < 0)
+ if (repo_read_index(the_repository) < 0)
die("unable to read index file");
istate = the_index;
istate.cache_tree = another;
cache_tree_update(&istate, WRITE_TREE_DRY_RUN);
- return dump_cache_tree(active_cache_tree, another, "");
+ ret = dump_cache_tree(the_index.cache_tree, another, "");
+ cache_tree_free(&another);
+
+ return ret;
}
diff --git a/t/helper/test-dump-fsmonitor.c b/t/helper/test-dump-fsmonitor.c
index 975f0ac..4f215fe 100644
--- a/t/helper/test-dump-fsmonitor.c
+++ b/t/helper/test-dump-fsmonitor.c
@@ -1,7 +1,9 @@
#include "test-tool.h"
-#include "cache.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
-int cmd__dump_fsmonitor(int ac, const char **av)
+int cmd__dump_fsmonitor(int ac UNUSED, const char **av UNUSED)
{
struct index_state *istate = the_repository->index;
int i;
diff --git a/t/helper/test-dump-split-index.c b/t/helper/test-dump-split-index.c
index a209880..f29d18e 100644
--- a/t/helper/test-dump-split-index.c
+++ b/t/helper/test-dump-split-index.c
@@ -1,14 +1,18 @@
+#define USE_THE_INDEX_VARIABLE
#include "test-tool.h"
-#include "cache.h"
+#include "hex.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
#include "split-index.h"
#include "ewah/ewok.h"
-static void show_bit(size_t pos, void *data)
+static void show_bit(size_t pos, void *data UNUSED)
{
printf(" %d", (int)pos);
}
-int cmd__dump_split_index(int ac, const char **av)
+int cmd__dump_split_index(int ac UNUSED, const char **av)
{
struct split_index *si;
int i;
diff --git a/t/helper/test-dump-untracked-cache.c b/t/helper/test-dump-untracked-cache.c
index cf0f2c7..b4af971 100644
--- a/t/helper/test-dump-untracked-cache.c
+++ b/t/helper/test-dump-untracked-cache.c
@@ -1,7 +1,10 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
#include "test-tool.h"
-#include "cache.h"
#include "dir.h"
+#include "hex.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
static int compare_untracked(const void *a_, const void *b_)
{
@@ -40,16 +43,18 @@ static void dump(struct untracked_cache_dir *ucd, struct strbuf *base)
strbuf_setlen(base, len);
}
-int cmd__dump_untracked_cache(int ac, const char **av)
+int cmd__dump_untracked_cache(int ac UNUSED, const char **av UNUSED)
{
struct untracked_cache *uc;
struct strbuf base = STRBUF_INIT;
- /* Hack to avoid modifying the untracked cache when we read it */
- ignore_untracked_cache_config = 1;
+ /* Set core.untrackedCache=keep before setup_git_directory() */
+ xsetenv("GIT_CONFIG_COUNT", "1", 1);
+ xsetenv("GIT_CONFIG_KEY_0", "core.untrackedCache", 1);
+ xsetenv("GIT_CONFIG_VALUE_0", "keep", 1);
setup_git_directory();
- if (read_cache() < 0)
+ if (repo_read_index(the_repository) < 0)
die("unable to read index file");
uc = the_index.untracked;
if (!uc) {
diff --git a/t/helper/test-env-helper.c b/t/helper/test-env-helper.c
new file mode 100644
index 0000000..1c48688
--- /dev/null
+++ b/t/helper/test-env-helper.c
@@ -0,0 +1,100 @@
+#include "test-tool.h"
+#include "parse.h"
+#include "parse-options.h"
+
+static char const * const env__helper_usage[] = {
+ "test-tool env-helper --type=[bool|ulong] <options> <env-var>",
+ NULL
+};
+
+enum cmdmode {
+ ENV_HELPER_TYPE_BOOL = 1,
+ ENV_HELPER_TYPE_ULONG
+};
+
+static int option_parse_type(const struct option *opt, const char *arg,
+ int unset)
+{
+ enum cmdmode *cmdmode = opt->value;
+
+ BUG_ON_OPT_NEG(unset);
+
+ if (!strcmp(arg, "bool"))
+ *cmdmode = ENV_HELPER_TYPE_BOOL;
+ else if (!strcmp(arg, "ulong"))
+ *cmdmode = ENV_HELPER_TYPE_ULONG;
+ else
+ die("unrecognized --type argument, %s", arg);
+
+ return 0;
+}
+
+int cmd__env_helper(int argc, const char **argv)
+{
+ int exit_code = 0;
+ const char *env_variable = NULL;
+ const char *env_default = NULL;
+ int ret;
+ int ret_int, default_int;
+ unsigned long ret_ulong, default_ulong;
+ enum cmdmode cmdmode = 0;
+ struct option opts[] = {
+ OPT_CALLBACK_F(0, "type", &cmdmode, "type",
+ "value is given this type", PARSE_OPT_NONEG,
+ option_parse_type),
+ OPT_STRING(0, "default", &env_default, "value",
+ "default for git_env_*(...) to fall back on"),
+ OPT_BOOL(0, "exit-code", &exit_code,
+ "be quiet only use git_env_*() value as exit code"),
+ OPT_END(),
+ };
+
+ argc = parse_options(argc, argv, NULL, opts, env__helper_usage,
+ PARSE_OPT_KEEP_UNKNOWN_OPT);
+ if (env_default && !*env_default)
+ usage_with_options(env__helper_usage, opts);
+ if (!cmdmode)
+ usage_with_options(env__helper_usage, opts);
+ if (argc != 1)
+ usage_with_options(env__helper_usage, opts);
+ env_variable = argv[0];
+
+ switch (cmdmode) {
+ case ENV_HELPER_TYPE_BOOL:
+ if (env_default) {
+ default_int = git_parse_maybe_bool(env_default);
+ if (default_int == -1) {
+ error("option `--default' expects a boolean value with `--type=bool`, not `%s`",
+ env_default);
+ usage_with_options(env__helper_usage, opts);
+ }
+ } else {
+ default_int = 0;
+ }
+ ret_int = git_env_bool(env_variable, default_int);
+ if (!exit_code)
+ puts(ret_int ? "true" : "false");
+ ret = ret_int;
+ break;
+ case ENV_HELPER_TYPE_ULONG:
+ if (env_default) {
+ if (!git_parse_ulong(env_default, &default_ulong)) {
+ error("option `--default' expects an unsigned long value with `--type=ulong`, not `%s`",
+ env_default);
+ usage_with_options(env__helper_usage, opts);
+ }
+ } else {
+ default_ulong = 0;
+ }
+ ret_ulong = git_env_ulong(env_variable, default_ulong);
+ if (!exit_code)
+ printf("%lu\n", ret_ulong);
+ ret = ret_ulong;
+ break;
+ default:
+ BUG("unknown <type> value");
+ break;
+ }
+
+ return !ret;
+}
diff --git a/t/helper/test-example-decorate.c b/t/helper/test-example-decorate.c
index b9d1200..8f59f6b 100644
--- a/t/helper/test-example-decorate.c
+++ b/t/helper/test-example-decorate.c
@@ -1,9 +1,10 @@
#include "test-tool.h"
-#include "cache.h"
+#include "git-compat-util.h"
#include "object.h"
#include "decorate.h"
+#include "repository.h"
-int cmd__example_decorate(int argc, const char **argv)
+int cmd__example_decorate(int argc UNUSED, const char **argv UNUSED)
{
struct decoration n;
struct object_id one_oid = { {1} };
@@ -71,5 +72,7 @@ int cmd__example_decorate(int argc, const char **argv)
if (objects_noticed != 2)
BUG("should have 2 objects");
+ clear_decoration(&n, NULL);
+
return 0;
}
diff --git a/t/helper/test-fake-ssh.c b/t/helper/test-fake-ssh.c
index 12beee9..27323cb 100644
--- a/t/helper/test-fake-ssh.c
+++ b/t/helper/test-fake-ssh.c
@@ -8,7 +8,7 @@ int cmd_main(int argc, const char **argv)
struct strbuf buf = STRBUF_INIT;
FILE *f;
int i;
- const char *child_argv[] = { NULL, NULL };
+ struct child_process cmd = CHILD_PROCESS_INIT;
/* First, print all parameters into $TRASH_DIRECTORY/ssh-output */
if (!trash_directory)
@@ -17,6 +17,7 @@ int cmd_main(int argc, const char **argv)
f = fopen(buf.buf, "w");
if (!f)
die("Could not write to %s", buf.buf);
+ strbuf_release(&buf);
for (i = 0; i < argc; i++)
fprintf(f, "%s%s", i > 0 ? " " : "", i > 0 ? argv[i] : "ssh:");
fprintf(f, "\n");
@@ -25,6 +26,7 @@ int cmd_main(int argc, const char **argv)
/* Now, evaluate the *last* parameter */
if (argc < 2)
return 0;
- child_argv[0] = argv[argc - 1];
- return run_command_v_opt(child_argv, RUN_USING_SHELL);
+ cmd.use_shell = 1;
+ strvec_push(&cmd.args, argv[argc - 1]);
+ return run_command(&cmd);
}
diff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c
deleted file mode 100644
index fc2d460..0000000
--- a/t/helper/test-fast-rebase.c
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * "git fast-rebase" builtin command
- *
- * FAST: Forking Any Subprocesses (is) Taboo
- *
- * This is meant SOLELY as a demo of what is possible. sequencer.c and
- * rebase.c should be refactored to use the ideas here, rather than attempting
- * to extend this file to replace those (unless Phillip or Dscho say that
- * refactoring is too hard and we need a clean slate, but I'm guessing that
- * refactoring is the better route).
- */
-
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "test-tool.h"
-
-#include "cache-tree.h"
-#include "commit.h"
-#include "lockfile.h"
-#include "merge-ort.h"
-#include "refs.h"
-#include "revision.h"
-#include "sequencer.h"
-#include "strvec.h"
-#include "tree.h"
-
-static const char *short_commit_name(struct commit *commit)
-{
- return find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV);
-}
-
-static struct commit *peel_committish(const char *name)
-{
- struct object *obj;
- struct object_id oid;
-
- if (get_oid(name, &oid))
- return NULL;
- obj = parse_object(the_repository, &oid);
- return (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);
-}
-
-static char *get_author(const char *message)
-{
- size_t len;
- const char *a;
-
- a = find_commit_header(message, "author", &len);
- if (a)
- return xmemdupz(a, len);
-
- return NULL;
-}
-
-static struct commit *create_commit(struct tree *tree,
- struct commit *based_on,
- struct commit *parent)
-{
- struct object_id ret;
- struct object *obj;
- struct commit_list *parents = NULL;
- char *author;
- char *sign_commit = NULL;
- struct commit_extra_header *extra;
- struct strbuf msg = STRBUF_INIT;
- const char *out_enc = get_commit_output_encoding();
- const char *message = logmsg_reencode(based_on, NULL, out_enc);
- const char *orig_message = NULL;
- const char *exclude_gpgsig[] = { "gpgsig", NULL };
-
- commit_list_insert(parent, &parents);
- extra = read_commit_extra_headers(based_on, exclude_gpgsig);
- find_commit_subject(message, &orig_message);
- strbuf_addstr(&msg, orig_message);
- author = get_author(message);
- reset_ident_date();
- if (commit_tree_extended(msg.buf, msg.len, &tree->object.oid, parents,
- &ret, author, NULL, sign_commit, extra)) {
- error(_("failed to write commit object"));
- return NULL;
- }
- free(author);
- strbuf_release(&msg);
-
- obj = parse_object(the_repository, &ret);
- return (struct commit *)obj;
-}
-
-int cmd__fast_rebase(int argc, const char **argv)
-{
- struct commit *onto;
- struct commit *last_commit = NULL, *last_picked_commit = NULL;
- struct object_id head;
- struct lock_file lock = LOCK_INIT;
- struct strvec rev_walk_args = STRVEC_INIT;
- struct rev_info revs;
- struct commit *commit;
- struct merge_options merge_opt;
- struct tree *next_tree, *base_tree, *head_tree;
- struct merge_result result;
- struct strbuf reflog_msg = STRBUF_INIT;
- struct strbuf branch_name = STRBUF_INIT;
-
- /*
- * test-tool stuff doesn't set up the git directory by default; need to
- * do that manually.
- */
- setup_git_directory();
-
- if (argc == 2 && !strcmp(argv[1], "-h")) {
- printf("Sorry, I am not a psychiatrist; I can not give you the help you need. Oh, you meant usage...\n");
- exit(129);
- }
-
- if (argc != 5 || strcmp(argv[1], "--onto"))
- die("usage: read the code, figure out how to use it, then do so");
-
- onto = peel_committish(argv[2]);
- strbuf_addf(&branch_name, "refs/heads/%s", argv[4]);
-
- /* Sanity check */
- if (get_oid("HEAD", &head))
- die(_("Cannot read HEAD"));
- assert(oideq(&onto->object.oid, &head));
-
- hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
- if (repo_read_index(the_repository) < 0)
- BUG("Could not read index");
-
- repo_init_revisions(the_repository, &revs, NULL);
- revs.verbose_header = 1;
- revs.max_parents = 1;
- revs.cherry_mark = 1;
- revs.limited = 1;
- revs.reverse = 1;
- revs.right_only = 1;
- revs.sort_order = REV_SORT_IN_GRAPH_ORDER;
- revs.topo_order = 1;
- strvec_pushl(&rev_walk_args, "", argv[4], "--not", argv[3], NULL);
-
- if (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1)
- return error(_("unhandled options"));
-
- strvec_clear(&rev_walk_args);
-
- if (prepare_revision_walk(&revs) < 0)
- return error(_("error preparing revisions"));
-
- init_merge_options(&merge_opt, the_repository);
- memset(&result, 0, sizeof(result));
- merge_opt.show_rename_progress = 1;
- merge_opt.branch1 = "HEAD";
- head_tree = get_commit_tree(onto);
- result.tree = head_tree;
- last_commit = onto;
- while ((commit = get_revision(&revs))) {
- struct commit *base;
-
- fprintf(stderr, "Rebasing %s...\r",
- oid_to_hex(&commit->object.oid));
- assert(commit->parents && !commit->parents->next);
- base = commit->parents->item;
-
- next_tree = get_commit_tree(commit);
- base_tree = get_commit_tree(base);
-
- merge_opt.branch2 = short_commit_name(commit);
- merge_opt.ancestor = xstrfmt("parent of %s", merge_opt.branch2);
-
- merge_incore_nonrecursive(&merge_opt,
- base_tree,
- result.tree,
- next_tree,
- &result);
-
- free((char*)merge_opt.ancestor);
- merge_opt.ancestor = NULL;
- if (!result.clean)
- break;
- last_picked_commit = commit;
- last_commit = create_commit(result.tree, commit, last_commit);
- }
- /* TODO: There should be some kind of rev_info_free(&revs) call... */
- memset(&revs, 0, sizeof(revs));
-
- merge_switch_to_result(&merge_opt, head_tree, &result, 1, !result.clean);
-
- if (result.clean < 0)
- exit(128);
-
- if (result.clean) {
- fprintf(stderr, "\nDone.\n");
- strbuf_addf(&reflog_msg, "finish rebase %s onto %s",
- oid_to_hex(&last_picked_commit->object.oid),
- oid_to_hex(&last_commit->object.oid));
- if (update_ref(reflog_msg.buf, branch_name.buf,
- &last_commit->object.oid,
- &last_picked_commit->object.oid,
- REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) {
- error(_("could not update %s"), argv[4]);
- die("Failed to update %s", argv[4]);
- }
- if (create_symref("HEAD", branch_name.buf, reflog_msg.buf) < 0)
- die(_("unable to update HEAD"));
- strbuf_release(&reflog_msg);
- strbuf_release(&branch_name);
-
- prime_cache_tree(the_repository, the_repository->index,
- result.tree);
- } else {
- fprintf(stderr, "\nAborting: Hit a conflict.\n");
- strbuf_addf(&reflog_msg, "rebase progress up to %s",
- oid_to_hex(&last_picked_commit->object.oid));
- if (update_ref(reflog_msg.buf, "HEAD",
- &last_commit->object.oid,
- &head,
- REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) {
- error(_("could not update %s"), argv[4]);
- die("Failed to update %s", argv[4]);
- }
- }
- if (write_locked_index(&the_index, &lock,
- COMMIT_LOCK | SKIP_IF_UNCHANGED))
- die(_("unable to write %s"), get_index_file());
- return (result.clean == 0);
-}
diff --git a/t/helper/test-find-pack.c b/t/helper/test-find-pack.c
new file mode 100644
index 0000000..e8bd793
--- /dev/null
+++ b/t/helper/test-find-pack.c
@@ -0,0 +1,50 @@
+#include "test-tool.h"
+#include "object-name.h"
+#include "object-store.h"
+#include "packfile.h"
+#include "parse-options.h"
+#include "setup.h"
+
+/*
+ * Display the path(s), one per line, of the packfile(s) containing
+ * the given object.
+ *
+ * If '--check-count <n>' is passed, then error out if the number of
+ * packfiles containing the object is not <n>.
+ */
+
+static const char *find_pack_usage[] = {
+ "test-tool find-pack [--check-count <n>] <object>",
+ NULL
+};
+
+int cmd__find_pack(int argc, const char **argv)
+{
+ struct object_id oid;
+ struct packed_git *p;
+ int count = -1, actual_count = 0;
+ const char *prefix = setup_git_directory();
+
+ struct option options[] = {
+ OPT_INTEGER('c', "check-count", &count, "expected number of packs"),
+ OPT_END(),
+ };
+
+ argc = parse_options(argc, argv, prefix, options, find_pack_usage, 0);
+ if (argc != 1)
+ usage(find_pack_usage[0]);
+
+ if (repo_get_oid(the_repository, argv[0], &oid))
+ die("cannot parse %s as an object name", argv[0]);
+
+ for (p = get_all_packs(the_repository); p; p = p->next)
+ if (find_pack_entry_one(oid.hash, p)) {
+ printf("%s\n", p->pack_name);
+ actual_count++;
+ }
+
+ if (count > -1 && count != actual_count)
+ die("bad packfile count %d instead of %d", actual_count, count);
+
+ return 0;
+}
diff --git a/t/helper/test-fsmonitor-client.c b/t/helper/test-fsmonitor-client.c
new file mode 100644
index 0000000..8280984
--- /dev/null
+++ b/t/helper/test-fsmonitor-client.c
@@ -0,0 +1,224 @@
+/*
+ * test-fsmonitor-client.c: client code to send commands/requests to
+ * a `git fsmonitor--daemon` daemon.
+ */
+
+#include "test-tool.h"
+#include "parse-options.h"
+#include "fsmonitor-ipc.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
+#include "thread-utils.h"
+#include "trace2.h"
+
+#ifndef HAVE_FSMONITOR_DAEMON_BACKEND
+int cmd__fsmonitor_client(int argc UNUSED, const char **argv UNUSED)
+{
+ die("fsmonitor--daemon not available on this platform");
+}
+#else
+
+/*
+ * Read the `.git/index` to get the last token written to the
+ * FSMonitor Index Extension.
+ */
+static const char *get_token_from_index(void)
+{
+ struct index_state *istate = the_repository->index;
+
+ if (do_read_index(istate, the_repository->index_file, 0) < 0)
+ die("unable to read index file");
+ if (!istate->fsmonitor_last_update)
+ die("index file does not have fsmonitor extension");
+
+ return istate->fsmonitor_last_update;
+}
+
+/*
+ * Send an IPC query to a `git-fsmonitor--daemon` daemon and
+ * ask for the changes since the given token or from the last
+ * token in the index extension.
+ *
+ * This will implicitly start a daemon process if necessary. The
+ * daemon process will persist after we exit.
+ */
+static int do_send_query(const char *token)
+{
+ struct strbuf answer = STRBUF_INIT;
+ int ret;
+
+ if (!token || !*token)
+ token = get_token_from_index();
+
+ ret = fsmonitor_ipc__send_query(token, &answer);
+ if (ret < 0)
+ die("could not query fsmonitor--daemon");
+
+ write_in_full(1, answer.buf, answer.len);
+ strbuf_release(&answer);
+
+ return 0;
+}
+
+/*
+ * Send a "flush" command to the `git-fsmonitor--daemon` (if running)
+ * and tell it to flush its cache.
+ *
+ * This feature is primarily used by the test suite to simulate a loss of
+ * sync with the filesystem where we miss kernel events.
+ */
+static int do_send_flush(void)
+{
+ struct strbuf answer = STRBUF_INIT;
+ int ret;
+
+ ret = fsmonitor_ipc__send_command("flush", &answer);
+ if (ret)
+ return ret;
+
+ write_in_full(1, answer.buf, answer.len);
+ strbuf_release(&answer);
+
+ return 0;
+}
+
+struct hammer_thread_data
+{
+ pthread_t pthread_id;
+ int thread_nr;
+
+ int nr_requests;
+ const char *token;
+
+ int sum_successful;
+ int sum_errors;
+};
+
+static void *hammer_thread_proc(void *_hammer_thread_data)
+{
+ struct hammer_thread_data *data = _hammer_thread_data;
+ struct strbuf answer = STRBUF_INIT;
+ int k;
+ int ret;
+
+ trace2_thread_start("hammer");
+
+ for (k = 0; k < data->nr_requests; k++) {
+ strbuf_reset(&answer);
+
+ ret = fsmonitor_ipc__send_query(data->token, &answer);
+ if (ret < 0)
+ data->sum_errors++;
+ else
+ data->sum_successful++;
+ }
+
+ strbuf_release(&answer);
+ trace2_thread_exit();
+ return NULL;
+}
+
+/*
+ * Start a pool of client threads that will each send a series of
+ * commands to the daemon.
+ *
+ * The goal is to overload the daemon with a sustained series of
+ * concurrent requests.
+ */
+static int do_hammer(const char *token, int nr_threads, int nr_requests)
+{
+ struct hammer_thread_data *data = NULL;
+ int k;
+ int sum_join_errors = 0;
+ int sum_commands = 0;
+ int sum_errors = 0;
+
+ if (!token || !*token)
+ token = get_token_from_index();
+ if (nr_threads < 1)
+ nr_threads = 1;
+ if (nr_requests < 1)
+ nr_requests = 1;
+
+ CALLOC_ARRAY(data, nr_threads);
+
+ for (k = 0; k < nr_threads; k++) {
+ struct hammer_thread_data *p = &data[k];
+ p->thread_nr = k;
+ p->nr_requests = nr_requests;
+ p->token = token;
+
+ if (pthread_create(&p->pthread_id, NULL, hammer_thread_proc, p)) {
+ warning("failed to create thread[%d] skipping remainder", k);
+ nr_threads = k;
+ break;
+ }
+ }
+
+ for (k = 0; k < nr_threads; k++) {
+ struct hammer_thread_data *p = &data[k];
+
+ if (pthread_join(p->pthread_id, NULL))
+ sum_join_errors++;
+ sum_commands += p->sum_successful;
+ sum_errors += p->sum_errors;
+ }
+
+ fprintf(stderr, "HAMMER: [threads %d][requests %d] [ok %d][err %d][join %d]\n",
+ nr_threads, nr_requests, sum_commands, sum_errors, sum_join_errors);
+
+ free(data);
+
+ /*
+ * Return an error if any of the _send_query requests failed.
+ * We don't care about thread create/join errors.
+ */
+ return sum_errors > 0;
+}
+
+int cmd__fsmonitor_client(int argc, const char **argv)
+{
+ const char *subcmd;
+ const char *token = NULL;
+ int nr_threads = 1;
+ int nr_requests = 1;
+
+ const char * const fsmonitor_client_usage[] = {
+ "test-tool fsmonitor-client query [<token>]",
+ "test-tool fsmonitor-client flush",
+ "test-tool fsmonitor-client hammer [<token>] [<threads>] [<requests>]",
+ NULL,
+ };
+
+ struct option options[] = {
+ OPT_STRING(0, "token", &token, "token",
+ "command token to send to the server"),
+
+ OPT_INTEGER(0, "threads", &nr_threads, "number of client threads"),
+ OPT_INTEGER(0, "requests", &nr_requests, "number of requests per thread"),
+
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, NULL, options, fsmonitor_client_usage, 0);
+
+ if (argc != 1)
+ usage_with_options(fsmonitor_client_usage, options);
+
+ subcmd = argv[0];
+
+ setup_git_directory();
+
+ if (!strcmp(subcmd, "query"))
+ return !!do_send_query(token);
+
+ if (!strcmp(subcmd, "flush"))
+ return !!do_send_flush();
+
+ if (!strcmp(subcmd, "hammer"))
+ return !!do_hammer(token, nr_threads, nr_requests);
+
+ die("Unhandled subcommand: '%s'", subcmd);
+}
+#endif
diff --git a/t/helper/test-genzeros.c b/t/helper/test-genzeros.c
index 9532f5b..47af843 100644
--- a/t/helper/test-genzeros.c
+++ b/t/helper/test-genzeros.c
@@ -3,18 +3,32 @@
int cmd__genzeros(int argc, const char **argv)
{
- long count;
+ /* static, so that it is NUL-initialized */
+ static const char zeros[256 * 1024];
+ intmax_t count;
+ ssize_t n;
if (argc > 2) {
fprintf(stderr, "usage: %s [<count>]\n", argv[0]);
return 1;
}
- count = argc > 1 ? strtol(argv[1], NULL, 0) : -1L;
+ count = argc > 1 ? strtoimax(argv[1], NULL, 0) : -1;
- while (count < 0 || count--) {
- if (putchar(0) == EOF)
- return -1;
+ /* Writing out individual NUL bytes is slow... */
+ while (count < 0)
+ if (xwrite(1, zeros, ARRAY_SIZE(zeros)) < 0)
+ die_errno("write error");
+
+ while (count > 0) {
+ n = xwrite(1, zeros,
+ count < ARRAY_SIZE(zeros)
+ ? count : ARRAY_SIZE(zeros));
+
+ if (n < 0)
+ die_errno("write error");
+
+ count -= n;
}
return 0;
diff --git a/t/helper/test-hash-speed.c b/t/helper/test-hash-speed.c
index f40d9ad..b235da5 100644
--- a/t/helper/test-hash-speed.c
+++ b/t/helper/test-hash-speed.c
@@ -1,5 +1,5 @@
#include "test-tool.h"
-#include "cache.h"
+#include "hash-ll.h"
#define NUM_SECONDS 3
diff --git a/t/helper/test-hash.c b/t/helper/test-hash.c
index 261c545..45d829c 100644
--- a/t/helper/test-hash.c
+++ b/t/helper/test-hash.c
@@ -1,5 +1,5 @@
#include "test-tool.h"
-#include "cache.h"
+#include "hex.h"
int cmd_hash_impl(int ac, const char **av, int algo)
{
@@ -54,5 +54,6 @@ int cmd_hash_impl(int ac, const char **av, int algo)
fwrite(hash, 1, algop->rawsz, stdout);
else
puts(hash_to_hex_algop(hash, algop));
+ free(buffer);
return 0;
}
diff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c
index 36ff07b..0eb0b3d 100644
--- a/t/helper/test-hashmap.c
+++ b/t/helper/test-hashmap.c
@@ -2,6 +2,7 @@
#include "git-compat-util.h"
#include "hashmap.h"
#include "strbuf.h"
+#include "string-list.h"
struct test_entry
{
@@ -150,6 +151,7 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)
*/
int cmd__hashmap(int argc, const char **argv)
{
+ struct string_list parts = STRING_LIST_INIT_NODUP;
struct strbuf line = STRBUF_INIT;
int icase;
struct hashmap map = HASHMAP_INIT(test_entry_cmp, &icase);
@@ -159,21 +161,26 @@ int cmd__hashmap(int argc, const char **argv)
/* process commands from stdin */
while (strbuf_getline(&line, stdin) != EOF) {
- char *cmd, *p1 = NULL, *p2 = NULL;
+ char *cmd, *p1, *p2;
unsigned int hash = 0;
struct test_entry *entry;
/* break line into command and up to two parameters */
- cmd = strtok(line.buf, DELIM);
+ string_list_setlen(&parts, 0);
+ string_list_split_in_place(&parts, line.buf, DELIM, 2);
+ string_list_remove_empty_items(&parts, 0);
+
/* ignore empty lines */
- if (!cmd || *cmd == '#')
+ if (!parts.nr)
+ continue;
+ if (!*parts.items[0].string || *parts.items[0].string == '#')
continue;
- p1 = strtok(NULL, DELIM);
- if (p1) {
+ cmd = parts.items[0].string;
+ p1 = parts.nr >= 1 ? parts.items[1].string : NULL;
+ p2 = parts.nr >= 2 ? parts.items[2].string : NULL;
+ if (p1)
hash = icase ? strihash(p1) : strhash(p1);
- p2 = strtok(NULL, DELIM);
- }
if (!strcmp("add", cmd) && p1 && p2) {
@@ -260,6 +267,7 @@ int cmd__hashmap(int argc, const char **argv)
}
}
+ string_list_clear(&parts, 0);
strbuf_release(&line);
hashmap_clear_and_free(&map, struct test_entry, ent);
return 0;
diff --git a/t/helper/test-hexdump.c b/t/helper/test-hexdump.c
new file mode 100644
index 0000000..05f55ec
--- /dev/null
+++ b/t/helper/test-hexdump.c
@@ -0,0 +1,30 @@
+#include "test-tool.h"
+#include "git-compat-util.h"
+
+/*
+ * Read stdin and print a hexdump to stdout.
+ */
+int cmd__hexdump(int argc UNUSED, const char **argv UNUSED)
+{
+ char buf[1024];
+ ssize_t i, len;
+ int have_data = 0;
+
+ for (;;) {
+ len = xread(0, buf, sizeof(buf));
+ if (len < 0)
+ die_errno("failure reading stdin");
+ if (!len)
+ break;
+
+ have_data = 1;
+
+ for (i = 0; i < len; i++)
+ printf("%02x ", (unsigned char)buf[i]);
+ }
+
+ if (have_data)
+ putchar('\n');
+
+ return 0;
+}
diff --git a/t/helper/test-index-version.c b/t/helper/test-index-version.c
deleted file mode 100644
index fcd1096..0000000
--- a/t/helper/test-index-version.c
+++ /dev/null
@@ -1,15 +0,0 @@
-#include "test-tool.h"
-#include "cache.h"
-
-int cmd__index_version(int argc, const char **argv)
-{
- struct cache_header hdr;
- int version;
-
- memset(&hdr,0,sizeof(hdr));
- if (read(0, &hdr, sizeof(hdr)) != sizeof(hdr))
- return 0;
- version = ntohl(hdr.hdr_version);
- printf("%d\n", version);
- return 0;
-}
diff --git a/t/helper/test-json-writer.c b/t/helper/test-json-writer.c
index 37c4525..afe393f 100644
--- a/t/helper/test-json-writer.c
+++ b/t/helper/test-json-writer.c
@@ -1,6 +1,6 @@
#include "test-tool.h"
-#include "cache.h"
#include "json-writer.h"
+#include "string-list.h"
static const char *expect_obj1 = "{\"a\":\"abc\",\"b\":42,\"c\":true}";
static const char *expect_obj2 = "{\"a\":-1,\"b\":2147483647,\"c\":0}";
@@ -181,12 +181,18 @@ static struct json_writer nest1 = JSON_WRITER_INIT;
static void make_nest1(int pretty)
{
+ make_obj1(0);
+ make_arr1(0);
+
jw_object_begin(&nest1, pretty);
{
jw_object_sub_jw(&nest1, "obj1", &obj1);
jw_object_sub_jw(&nest1, "arr1", &arr1);
}
jw_end(&nest1);
+
+ jw_release(&obj1);
+ jw_release(&arr1);
}
static char *expect_inline1 =
@@ -313,6 +319,9 @@ static void make_mixed1(int pretty)
jw_object_sub_jw(&mixed1, "arr1", &arr1);
}
jw_end(&mixed1);
+
+ jw_release(&obj1);
+ jw_release(&arr1);
}
static void cmp(const char *test, const struct json_writer *jw, const char *exp)
@@ -325,8 +334,8 @@ static void cmp(const char *test, const struct json_writer *jw, const char *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)
+#define t(v) do { make_##v(0); cmp(#v, &v, expect_##v); jw_release(&v); } while (0)
+#define p(v) do { make_##v(1); cmp(#v, &v, pretty_##v); jw_release(&v); } while (0)
/*
* Run some basic regression tests with some known patterns.
@@ -381,41 +390,46 @@ static int unit_tests(void)
/* mixed forms */
t(mixed1);
- jw_init(&mixed1);
p(mixed1);
return 0;
}
-static void get_s(int line_nr, char **s_in)
+struct line {
+ struct string_list *parts;
+ size_t consumed_nr;
+ int nr;
+};
+
+static void get_s(struct line *line, char **s_in)
{
- *s_in = strtok(NULL, " ");
- if (!*s_in)
- die("line[%d]: expected: <s>", line_nr);
+ if (line->consumed_nr > line->parts->nr)
+ die("line[%d]: expected: <s>", line->nr);
+ *s_in = line->parts->items[line->consumed_nr++].string;
}
-static void get_i(int line_nr, intmax_t *s_in)
+static void get_i(struct line *line, intmax_t *s_in)
{
char *s;
char *endptr;
- get_s(line_nr, &s);
+ get_s(line, &s);
*s_in = strtol(s, &endptr, 10);
if (*endptr || errno == ERANGE)
- die("line[%d]: invalid integer value", line_nr);
+ die("line[%d]: invalid integer value", line->nr);
}
-static void get_d(int line_nr, double *s_in)
+static void get_d(struct line *line, double *s_in)
{
char *s;
char *endptr;
- get_s(line_nr, &s);
+ get_s(line, &s);
*s_in = strtod(s, &endptr);
if (*endptr || errno == ERANGE)
- die("line[%d]: invalid float value", line_nr);
+ die("line[%d]: invalid float value", line->nr);
}
static int pretty;
@@ -446,6 +460,7 @@ static char *get_trimmed_line(char *buf, int buf_size)
static int scripted(void)
{
+ struct string_list parts = STRING_LIST_INIT_NODUP;
struct json_writer jw = JSON_WRITER_INIT;
char buf[MAX_LINE_LENGTH];
char *line;
@@ -463,66 +478,77 @@ static int scripted(void)
die("expected first line to be 'object' or 'array'");
while ((line = get_trimmed_line(buf, MAX_LINE_LENGTH)) != NULL) {
+ struct line state = { 0 };
char *verb;
char *key;
char *s_value;
intmax_t i_value;
double d_value;
- line_nr++;
+ state.parts = &parts;
+ state.nr = ++line_nr;
+
+ /* break line into command and zero or more tokens */
+ string_list_setlen(&parts, 0);
+ string_list_split_in_place(&parts, line, " ", -1);
+ string_list_remove_empty_items(&parts, 0);
+
+ /* ignore empty lines */
+ if (!parts.nr || !*parts.items[0].string)
+ continue;
- verb = strtok(line, " ");
+ verb = parts.items[state.consumed_nr++].string;
if (!strcmp(verb, "end")) {
jw_end(&jw);
}
else if (!strcmp(verb, "object-string")) {
- get_s(line_nr, &key);
- get_s(line_nr, &s_value);
+ get_s(&state, &key);
+ get_s(&state, &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);
+ get_s(&state, &key);
+ get_i(&state, &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);
+ get_s(&state, &key);
+ get_i(&state, &i_value);
+ get_d(&state, &d_value);
jw_object_double(&jw, key, i_value, d_value);
}
else if (!strcmp(verb, "object-true")) {
- get_s(line_nr, &key);
+ get_s(&state, &key);
jw_object_true(&jw, key);
}
else if (!strcmp(verb, "object-false")) {
- get_s(line_nr, &key);
+ get_s(&state, &key);
jw_object_false(&jw, key);
}
else if (!strcmp(verb, "object-null")) {
- get_s(line_nr, &key);
+ get_s(&state, &key);
jw_object_null(&jw, key);
}
else if (!strcmp(verb, "object-object")) {
- get_s(line_nr, &key);
+ get_s(&state, &key);
jw_object_inline_begin_object(&jw, key);
}
else if (!strcmp(verb, "object-array")) {
- get_s(line_nr, &key);
+ get_s(&state, &key);
jw_object_inline_begin_array(&jw, key);
}
else if (!strcmp(verb, "array-string")) {
- get_s(line_nr, &s_value);
+ get_s(&state, &s_value);
jw_array_string(&jw, s_value);
}
else if (!strcmp(verb, "array-int")) {
- get_i(line_nr, &i_value);
+ get_i(&state, &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);
+ get_i(&state, &i_value);
+ get_d(&state, &d_value);
jw_array_double(&jw, i_value, d_value);
}
else if (!strcmp(verb, "array-true"))
@@ -544,7 +570,8 @@ static int scripted(void)
printf("%s\n", jw.json.buf);
- strbuf_release(&jw.json);
+ jw_release(&jw);
+ string_list_clear(&parts, 0);
return 0;
}
diff --git a/t/helper/test-lazy-init-name-hash.c b/t/helper/test-lazy-init-name-hash.c
index cd1b4c9..187a115 100644
--- a/t/helper/test-lazy-init-name-hash.c
+++ b/t/helper/test-lazy-init-name-hash.c
@@ -1,6 +1,12 @@
+#define USE_THE_INDEX_VARIABLE
#include "test-tool.h"
-#include "cache.h"
+#include "environment.h"
+#include "name-hash.h"
#include "parse-options.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
+#include "trace.h"
static int single;
static int multi;
@@ -32,7 +38,7 @@ static void dump_run(void)
struct dir_entry *dir;
struct cache_entry *ce;
- read_cache();
+ repo_read_index(the_repository);
if (single) {
test_lazy_init_name_hash(&the_index, 0);
} else {
@@ -49,7 +55,7 @@ static void dump_run(void)
ent /* member name */)
printf("name %08x %s\n", ce->ent.hash, ce->name);
- discard_cache();
+ discard_index(&the_index);
}
/*
@@ -66,7 +72,7 @@ static uint64_t time_runs(int try_threaded)
for (i = 0; i < count; i++) {
t0 = getnanotime();
- read_cache();
+ repo_read_index(the_repository);
t1 = getnanotime();
nr_threads_used = test_lazy_init_name_hash(&the_index, try_threaded);
t2 = getnanotime();
@@ -89,7 +95,7 @@ static uint64_t time_runs(int try_threaded)
the_index.cache_nr);
fflush(stdout);
- discard_cache();
+ discard_index(&the_index);
}
avg = sum / count;
@@ -113,9 +119,9 @@ static void analyze_run(void)
int i;
int nr;
- read_cache();
+ repo_read_index(the_repository);
cache_nr_limit = the_index.cache_nr;
- discard_cache();
+ discard_index(&the_index);
nr = analyze;
while (1) {
@@ -128,23 +134,23 @@ static void analyze_run(void)
nr = cache_nr_limit;
for (i = 0; i < count; i++) {
- read_cache();
+ repo_read_index(the_repository);
the_index.cache_nr = nr; /* cheap truncate of index */
t1s = getnanotime();
test_lazy_init_name_hash(&the_index, 0);
t2s = getnanotime();
sum_single += (t2s - t1s);
the_index.cache_nr = cache_nr_limit;
- discard_cache();
+ discard_index(&the_index);
- read_cache();
+ repo_read_index(the_repository);
the_index.cache_nr = nr; /* cheap truncate of index */
t1m = getnanotime();
nr_threads_used = test_lazy_init_name_hash(&the_index, 1);
t2m = getnanotime();
sum_multi += (t2m - t1m);
the_index.cache_nr = cache_nr_limit;
- discard_cache();
+ discard_index(&the_index);
if (!nr_threads_used)
printf(" [size %8d] [single %f] non-threaded code path used\n",
diff --git a/t/helper/test-match-trees.c b/t/helper/test-match-trees.c
index 4079fde..d0db5ff 100644
--- a/t/helper/test-match-trees.c
+++ b/t/helper/test-match-trees.c
@@ -1,17 +1,21 @@
#include "test-tool.h"
-#include "cache.h"
+#include "hex.h"
+#include "match-trees.h"
+#include "object-name.h"
+#include "repository.h"
+#include "setup.h"
#include "tree.h"
-int cmd__match_trees(int ac, const char **av)
+int cmd__match_trees(int ac UNUSED, const char **av)
{
struct object_id hash1, hash2, shifted;
struct tree *one, *two;
setup_git_directory();
- if (get_oid(av[1], &hash1))
+ if (repo_get_oid(the_repository, av[1], &hash1))
die("cannot parse %s as an object name", av[1]);
- if (get_oid(av[2], &hash2))
+ if (repo_get_oid(the_repository, av[2], &hash2))
die("cannot parse %s as an object name", av[2]);
one = parse_tree_indirect(&hash1);
if (!one)
diff --git a/t/helper/test-mergesort.c b/t/helper/test-mergesort.c
index c5cffaa..42ccc87 100644
--- a/t/helper/test-mergesort.c
+++ b/t/helper/test-mergesort.c
@@ -1,53 +1,406 @@
#include "test-tool.h"
-#include "cache.h"
+#include "mem-pool.h"
#include "mergesort.h"
+#include "strbuf.h"
+
+static uint32_t minstd_rand(uint32_t *state)
+{
+ *state = (uint64_t)*state * 48271 % 2147483647;
+ return *state;
+}
struct line {
char *text;
struct line *next;
};
-static void *get_next(const void *a)
-{
- return ((const struct line *)a)->next;
-}
-
-static void set_next(void *a, void *b)
-{
- ((struct line *)a)->next = b;
-}
+DEFINE_LIST_SORT(static, sort_lines, struct line, next);
-static int compare_strings(const void *a, const void *b)
+static int compare_strings(const struct line *x, const struct line *y)
{
- const struct line *x = a, *y = b;
return strcmp(x->text, y->text);
}
-int cmd__mergesort(int argc, const char **argv)
+static int sort_stdin(void)
{
- struct line *line, *p = NULL, *lines = NULL;
+ struct line *lines;
+ struct line **tail = &lines;
struct strbuf sb = STRBUF_INIT;
+ struct mem_pool lines_pool;
+ char *p;
+
+ strbuf_read(&sb, 0, 0);
+ /*
+ * Split by newline, but don't create an item
+ * for the empty string after the last separator.
+ */
+ if (sb.len && sb.buf[sb.len - 1] == '\n')
+ strbuf_setlen(&sb, sb.len - 1);
+
+ mem_pool_init(&lines_pool, 0);
+ p = sb.buf;
for (;;) {
- if (strbuf_getwholeline(&sb, stdin, '\n'))
+ char *eol = strchr(p, '\n');
+ struct line *line = mem_pool_alloc(&lines_pool, sizeof(*line));
+ line->text = p;
+ *tail = line;
+ tail = &line->next;
+ if (!eol)
break;
- line = xmalloc(sizeof(struct line));
- line->text = strbuf_detach(&sb, NULL);
- if (p) {
- line->next = p->next;
- p->next = line;
- } else {
- line->next = NULL;
- lines = line;
- }
- p = line;
+ *eol = '\0';
+ p = eol + 1;
}
+ *tail = NULL;
- lines = llist_mergesort(lines, get_next, set_next, compare_strings);
+ sort_lines(&lines, compare_strings);
while (lines) {
- printf("%s", lines->text);
+ puts(lines->text);
lines = lines->next;
}
return 0;
}
+
+static void dist_sawtooth(int *arr, int n, int m)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ arr[i] = i % m;
+}
+
+static void dist_rand(int *arr, int n, int m)
+{
+ int i;
+ uint32_t seed = 1;
+ for (i = 0; i < n; i++)
+ arr[i] = minstd_rand(&seed) % m;
+}
+
+static void dist_stagger(int *arr, int n, int m)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ arr[i] = (i * m + i) % n;
+}
+
+static void dist_plateau(int *arr, int n, int m)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ arr[i] = (i < m) ? i : m;
+}
+
+static void dist_shuffle(int *arr, int n, int m)
+{
+ int i, j, k;
+ uint32_t seed = 1;
+ for (i = j = 0, k = 1; i < n; i++)
+ arr[i] = minstd_rand(&seed) % m ? (j += 2) : (k += 2);
+}
+
+#define DIST(name) { #name, dist_##name }
+
+static struct dist {
+ const char *name;
+ void (*fn)(int *arr, int n, int m);
+} dist[] = {
+ DIST(sawtooth),
+ DIST(rand),
+ DIST(stagger),
+ DIST(plateau),
+ DIST(shuffle),
+};
+
+static const struct dist *get_dist_by_name(const char *name)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(dist); i++) {
+ if (!strcmp(dist[i].name, name))
+ return &dist[i];
+ }
+ return NULL;
+}
+
+static void mode_copy(int *arr, int n)
+{
+ /* nothing */
+}
+
+static void mode_reverse(int *arr, int n)
+{
+ int i, j;
+ for (i = 0, j = n - 1; i < j; i++, j--)
+ SWAP(arr[i], arr[j]);
+}
+
+static void mode_reverse_1st_half(int *arr, int n)
+{
+ mode_reverse(arr, n / 2);
+}
+
+static void mode_reverse_2nd_half(int *arr, int n)
+{
+ int half = n / 2;
+ mode_reverse(arr + half, n - half);
+}
+
+static int compare_ints(const void *av, const void *bv)
+{
+ const int *ap = av, *bp = bv;
+ int a = *ap, b = *bp;
+ return (a > b) - (a < b);
+}
+
+static void mode_sort(int *arr, int n)
+{
+ QSORT(arr, n, compare_ints);
+}
+
+static void mode_dither(int *arr, int n)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ arr[i] += i % 5;
+}
+
+static void unriffle(int *arr, int n, int *tmp)
+{
+ int i, j;
+ COPY_ARRAY(tmp, arr, n);
+ for (i = j = 0; i < n; i += 2)
+ arr[j++] = tmp[i];
+ for (i = 1; i < n; i += 2)
+ arr[j++] = tmp[i];
+}
+
+static void unriffle_recursively(int *arr, int n, int *tmp)
+{
+ if (n > 1) {
+ int half = n / 2;
+ unriffle(arr, n, tmp);
+ unriffle_recursively(arr, half, tmp);
+ unriffle_recursively(arr + half, n - half, tmp);
+ }
+}
+
+static void mode_unriffle(int *arr, int n)
+{
+ int *tmp;
+ ALLOC_ARRAY(tmp, n);
+ unriffle_recursively(arr, n, tmp);
+ free(tmp);
+}
+
+static unsigned int prev_pow2(unsigned int n)
+{
+ unsigned int pow2 = 1;
+ while (pow2 * 2 < n)
+ pow2 *= 2;
+ return pow2;
+}
+
+static void unriffle_recursively_skewed(int *arr, int n, int *tmp)
+{
+ if (n > 1) {
+ int pow2 = prev_pow2(n);
+ int rest = n - pow2;
+ unriffle(arr + pow2 - rest, rest * 2, tmp);
+ unriffle_recursively_skewed(arr, pow2, tmp);
+ unriffle_recursively_skewed(arr + pow2, rest, tmp);
+ }
+}
+
+static void mode_unriffle_skewed(int *arr, int n)
+{
+ int *tmp;
+ ALLOC_ARRAY(tmp, n);
+ unriffle_recursively_skewed(arr, n, tmp);
+ free(tmp);
+}
+
+#define MODE(name) { #name, mode_##name }
+
+static struct mode {
+ const char *name;
+ void (*fn)(int *arr, int n);
+} mode[] = {
+ MODE(copy),
+ MODE(reverse),
+ MODE(reverse_1st_half),
+ MODE(reverse_2nd_half),
+ MODE(sort),
+ MODE(dither),
+ MODE(unriffle),
+ MODE(unriffle_skewed),
+};
+
+static const struct mode *get_mode_by_name(const char *name)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(mode); i++) {
+ if (!strcmp(mode[i].name, name))
+ return &mode[i];
+ }
+ return NULL;
+}
+
+static int generate(int argc, const char **argv)
+{
+ const struct dist *dist = NULL;
+ const struct mode *mode = NULL;
+ int i, n, m, *arr;
+
+ if (argc != 4)
+ return 1;
+
+ dist = get_dist_by_name(argv[0]);
+ mode = get_mode_by_name(argv[1]);
+ n = strtol(argv[2], NULL, 10);
+ m = strtol(argv[3], NULL, 10);
+ if (!dist || !mode)
+ return 1;
+
+ ALLOC_ARRAY(arr, n);
+ dist->fn(arr, n, m);
+ mode->fn(arr, n);
+ for (i = 0; i < n; i++)
+ printf("%08x\n", arr[i]);
+ free(arr);
+ return 0;
+}
+
+static struct stats {
+ int get_next, set_next, compare;
+} stats;
+
+struct number {
+ int value, rank;
+ struct number *next;
+};
+
+DEFINE_LIST_SORT_DEBUG(static, sort_numbers, struct number, next,
+ stats.get_next++, stats.set_next++);
+
+static int compare_numbers(const struct number *an, const struct number *bn)
+{
+ int a = an->value, b = bn->value;
+ stats.compare++;
+ return (a > b) - (a < b);
+}
+
+static void clear_numbers(struct number *list)
+{
+ while (list) {
+ struct number *next = list->next;
+ free(list);
+ list = next;
+ }
+}
+
+static int test(const struct dist *dist, const struct mode *mode, int n, int m)
+{
+ int *arr;
+ size_t i;
+ struct number *curr, *list, **tail;
+ int is_sorted = 1;
+ int is_stable = 1;
+ const char *verdict;
+ int result = -1;
+
+ ALLOC_ARRAY(arr, n);
+ dist->fn(arr, n, m);
+ mode->fn(arr, n);
+ for (i = 0, tail = &list; i < n; i++) {
+ curr = xmalloc(sizeof(*curr));
+ curr->value = arr[i];
+ curr->rank = i;
+ *tail = curr;
+ tail = &curr->next;
+ }
+ *tail = NULL;
+
+ stats.get_next = stats.set_next = stats.compare = 0;
+ sort_numbers(&list, compare_numbers);
+
+ QSORT(arr, n, compare_ints);
+ for (i = 0, curr = list; i < n && curr; i++, curr = curr->next) {
+ if (arr[i] != curr->value)
+ is_sorted = 0;
+ if (curr->next && curr->value == curr->next->value &&
+ curr->rank >= curr->next->rank)
+ is_stable = 0;
+ }
+ if (i < n) {
+ verdict = "too short";
+ } else if (curr) {
+ verdict = "too long";
+ } else if (!is_sorted) {
+ verdict = "not sorted";
+ } else if (!is_stable) {
+ verdict = "unstable";
+ } else {
+ verdict = "OK";
+ result = 0;
+ }
+
+ printf("%-9s %-16s %8d %8d %8d %8d %8d %s\n",
+ dist->name, mode->name, n, m, stats.get_next, stats.set_next,
+ stats.compare, verdict);
+
+ clear_numbers(list);
+ free(arr);
+
+ return result;
+}
+
+/*
+ * A version of the qsort certification program from "Engineering a Sort
+ * Function" by Bentley and McIlroy, Software—Practice and Experience,
+ * Volume 23, Issue 11, 1249–1265 (November 1993).
+ */
+static int run_tests(int argc, const char **argv)
+{
+ const char *argv_default[] = { "100", "1023", "1024", "1025" };
+ if (!argc)
+ return run_tests(ARRAY_SIZE(argv_default), argv_default);
+ printf("%-9s %-16s %8s %8s %8s %8s %8s %s\n",
+ "distribut", "mode", "n", "m", "get_next", "set_next",
+ "compare", "verdict");
+ while (argc--) {
+ int i, j, m, n = strtol(*argv++, NULL, 10);
+ for (i = 0; i < ARRAY_SIZE(dist); i++) {
+ for (j = 0; j < ARRAY_SIZE(mode); j++) {
+ for (m = 1; m < 2 * n; m *= 2) {
+ if (test(&dist[i], &mode[j], n, m))
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+int cmd__mergesort(int argc, const char **argv)
+{
+ int i;
+ const char *sep;
+
+ if (argc == 6 && !strcmp(argv[1], "generate"))
+ return generate(argc - 2, argv + 2);
+ if (argc == 2 && !strcmp(argv[1], "sort"))
+ return sort_stdin();
+ if (argc > 1 && !strcmp(argv[1], "test"))
+ return run_tests(argc - 2, argv + 2);
+ fprintf(stderr, "usage: test-tool mergesort generate <distribution> <mode> <n> <m>\n");
+ fprintf(stderr, " or: test-tool mergesort sort\n");
+ fprintf(stderr, " or: test-tool mergesort test [<n>...]\n");
+ fprintf(stderr, "\n");
+ for (i = 0, sep = "distributions: "; i < ARRAY_SIZE(dist); i++, sep = ", ")
+ fprintf(stderr, "%s%s", sep, dist[i].name);
+ fprintf(stderr, "\n");
+ for (i = 0, sep = "modes: "; i < ARRAY_SIZE(mode); i++, sep = ", ")
+ fprintf(stderr, "%s%s", sep, mode[i].name);
+ fprintf(stderr, "\n");
+ return 129;
+}
diff --git a/t/helper/test-oid-array.c b/t/helper/test-oid-array.c
index b16cd0b..aafe398 100644
--- a/t/helper/test-oid-array.c
+++ b/t/helper/test-oid-array.c
@@ -1,14 +1,16 @@
#include "test-tool.h"
-#include "cache.h"
+#include "hex.h"
#include "oid-array.h"
+#include "setup.h"
+#include "strbuf.h"
-static int print_oid(const struct object_id *oid, void *data)
+static int print_oid(const struct object_id *oid, void *data UNUSED)
{
puts(oid_to_hex(oid));
return 0;
}
-int cmd__oid_array(int argc, const char **argv)
+int cmd__oid_array(int argc UNUSED, const char **argv UNUSED)
{
struct oid_array array = OID_ARRAY_INIT;
struct strbuf line = STRBUF_INIT;
@@ -35,5 +37,9 @@ int cmd__oid_array(int argc, const char **argv)
else
die("unknown command: %s", line.buf);
}
+
+ strbuf_release(&line);
+ oid_array_clear(&array);
+
return 0;
}
diff --git a/t/helper/test-oidmap.c b/t/helper/test-oidmap.c
index 0acf999..bd30244 100644
--- a/t/helper/test-oidmap.c
+++ b/t/helper/test-oidmap.c
@@ -1,7 +1,11 @@
#include "test-tool.h"
-#include "cache.h"
+#include "hex.h"
+#include "object-name.h"
#include "oidmap.h"
+#include "repository.h"
+#include "setup.h"
#include "strbuf.h"
+#include "string-list.h"
/* key is an oid and value is a name (could be a refname for example) */
struct test_entry {
@@ -21,8 +25,9 @@ struct test_entry {
* iterate -> oidkey1 namevalue1\noidkey2 namevalue2\n...
*
*/
-int cmd__oidmap(int argc, const char **argv)
+int cmd__oidmap(int argc UNUSED, const char **argv UNUSED)
{
+ struct string_list parts = STRING_LIST_INIT_NODUP;
struct strbuf line = STRBUF_INIT;
struct oidmap map = OIDMAP_INIT;
@@ -33,23 +38,28 @@ int cmd__oidmap(int argc, const char **argv)
/* process commands from stdin */
while (strbuf_getline(&line, stdin) != EOF) {
- char *cmd, *p1 = NULL, *p2 = NULL;
+ char *cmd, *p1, *p2;
struct test_entry *entry;
struct object_id oid;
/* break line into command and up to two parameters */
- cmd = strtok(line.buf, DELIM);
+ string_list_setlen(&parts, 0);
+ string_list_split_in_place(&parts, line.buf, DELIM, 2);
+ string_list_remove_empty_items(&parts, 0);
+
/* ignore empty lines */
- if (!cmd || *cmd == '#')
+ if (!parts.nr)
+ continue;
+ if (!*parts.items[0].string || *parts.items[0].string == '#')
continue;
- p1 = strtok(NULL, DELIM);
- if (p1)
- p2 = strtok(NULL, DELIM);
+ cmd = parts.items[0].string;
+ p1 = parts.nr >= 1 ? parts.items[1].string : NULL;
+ p2 = parts.nr >= 2 ? parts.items[2].string : NULL;
if (!strcmp("put", cmd) && p1 && p2) {
- if (get_oid(p1, &oid)) {
+ if (repo_get_oid(the_repository, p1, &oid)) {
printf("Unknown oid: %s\n", p1);
continue;
}
@@ -67,7 +77,7 @@ int cmd__oidmap(int argc, const char **argv)
} else if (!strcmp("get", cmd) && p1) {
- if (get_oid(p1, &oid)) {
+ if (repo_get_oid(the_repository, p1, &oid)) {
printf("Unknown oid: %s\n", p1);
continue;
}
@@ -80,7 +90,7 @@ int cmd__oidmap(int argc, const char **argv)
} else if (!strcmp("remove", cmd) && p1) {
- if (get_oid(p1, &oid)) {
+ if (repo_get_oid(the_repository, p1, &oid)) {
printf("Unknown oid: %s\n", p1);
continue;
}
@@ -106,6 +116,7 @@ int cmd__oidmap(int argc, const char **argv)
}
}
+ string_list_clear(&parts, 0);
strbuf_release(&line);
oidmap_free(&map, 1);
return 0;
diff --git a/t/helper/test-oidtree.c b/t/helper/test-oidtree.c
index 180ee28..c7a1d4c 100644
--- a/t/helper/test-oidtree.c
+++ b/t/helper/test-oidtree.c
@@ -1,14 +1,16 @@
#include "test-tool.h"
-#include "cache.h"
+#include "hex.h"
#include "oidtree.h"
+#include "setup.h"
+#include "strbuf.h"
-static enum cb_next print_oid(const struct object_id *oid, void *data)
+static enum cb_next print_oid(const struct object_id *oid, void *data UNUSED)
{
puts(oid_to_hex(oid));
return CB_CONTINUE;
}
-int cmd__oidtree(int argc, const char **argv)
+int cmd__oidtree(int argc UNUSED, const char **argv UNUSED)
{
struct oidtree ot;
struct strbuf line = STRBUF_INIT;
@@ -45,5 +47,8 @@ int cmd__oidtree(int argc, const char **argv)
die("unknown command: %s", line.buf);
}
}
+
+ strbuf_release(&line);
+
return 0;
}
diff --git a/t/helper/test-online-cpus.c b/t/helper/test-online-cpus.c
index 8cb0d53..47dc211 100644
--- a/t/helper/test-online-cpus.c
+++ b/t/helper/test-online-cpus.c
@@ -2,7 +2,7 @@
#include "git-compat-util.h"
#include "thread-utils.h"
-int cmd__online_cpus(int argc, const char **argv)
+int cmd__online_cpus(int argc UNUSED, const char **argv UNUSED)
{
printf("%d\n", online_cpus());
return 0;
diff --git a/t/helper/test-pack-mtimes.c b/t/helper/test-pack-mtimes.c
new file mode 100644
index 0000000..67a964e
--- /dev/null
+++ b/t/helper/test-pack-mtimes.c
@@ -0,0 +1,57 @@
+#include "test-tool.h"
+#include "hex.h"
+#include "strbuf.h"
+#include "object-store-ll.h"
+#include "packfile.h"
+#include "pack-mtimes.h"
+#include "setup.h"
+
+static void dump_mtimes(struct packed_git *p)
+{
+ uint32_t i;
+ if (load_pack_mtimes(p) < 0)
+ die("could not load pack .mtimes");
+
+ for (i = 0; i < p->num_objects; i++) {
+ struct object_id oid;
+ if (nth_packed_object_id(&oid, p, i) < 0)
+ die("could not load object id at position %"PRIu32, i);
+
+ printf("%s %"PRIu32"\n",
+ oid_to_hex(&oid), nth_packed_mtime(p, i));
+ }
+}
+
+static const char *pack_mtimes_usage = "\n"
+" test-tool pack-mtimes <pack-name.mtimes>";
+
+int cmd__pack_mtimes(int argc, const char **argv)
+{
+ struct strbuf buf = STRBUF_INIT;
+ struct packed_git *p;
+
+ setup_git_directory();
+
+ if (argc != 2)
+ usage(pack_mtimes_usage);
+
+ for (p = get_all_packs(the_repository); p; p = p->next) {
+ strbuf_addstr(&buf, basename(p->pack_name));
+ strbuf_strip_suffix(&buf, ".pack");
+ strbuf_addstr(&buf, ".mtimes");
+
+ if (!strcmp(buf.buf, argv[1]))
+ break;
+
+ strbuf_reset(&buf);
+ }
+
+ strbuf_release(&buf);
+
+ if (!p)
+ die("could not find pack '%s'", argv[1]);
+
+ dump_mtimes(p);
+
+ return 0;
+}
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
index 2051ce5..ded8116 100644
--- a/t/helper/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -1,6 +1,6 @@
#include "test-tool.h"
-#include "cache.h"
#include "parse-options.h"
+#include "strbuf.h"
#include "string-list.h"
#include "trace2.h"
@@ -14,7 +14,6 @@ static int dry_run = 0, quiet = 0;
static char *string = NULL;
static char *file = NULL;
static int ambiguous;
-static struct string_list list = STRING_LIST_INIT_NODUP;
static struct {
int called;
@@ -22,6 +21,19 @@ static struct {
int unset;
} length_cb;
+static int mode34_callback(const struct option *opt, const char *arg, int unset)
+{
+ if (unset)
+ *(int *)opt->value = 0;
+ else if (!strcmp(arg, "3"))
+ *(int *)opt->value = 3;
+ else if (!strcmp(arg, "4"))
+ *(int *)opt->value = 4;
+ else
+ return error("invalid value for '%s': '%s'", "--mode34", arg);
+ return 0;
+}
+
static int length_callback(const struct option *opt, const char *arg, int unset)
{
length_cb.called = 1;
@@ -107,6 +119,8 @@ int cmd__parse_options(int argc, const char **argv)
NULL
};
struct string_list expect = STRING_LIST_INIT_NODUP;
+ struct string_list list = STRING_LIST_INIT_NODUP;
+
struct option options[] = {
OPT_BOOL(0, "yes", &boolean, "get a boolean"),
OPT_BOOL('D', "no-doubt", &boolean, "begins with 'no-'"),
@@ -123,6 +137,9 @@ int cmd__parse_options(int argc, const char **argv)
OPT_SET_INT(0, "set23", &integer, "set integer to 23", 23),
OPT_CMDMODE(0, "mode1", &integer, "set integer to 1 (cmdmode option)", 1),
OPT_CMDMODE(0, "mode2", &integer, "set integer to 2 (cmdmode option)", 2),
+ OPT_CALLBACK_F(0, "mode34", &integer, "(3|4)",
+ "set integer to 3 or 4 (cmdmode option)",
+ PARSE_OPT_CMDMODE, mode34_callback),
OPT_CALLBACK('L', "length", &integer, "str",
"get length of <str>", length_callback),
OPT_FILENAME('F', "file", &file, "set file to <file>"),
@@ -132,9 +149,10 @@ int cmd__parse_options(int argc, const char **argv)
OPT_STRING(0, "st", &string, "st", "get another string (pervert ordering)"),
OPT_STRING('o', NULL, &string, "str", "get another string"),
OPT_NOOP_NOARG(0, "obsolete"),
+ OPT_SET_INT_F(0, "longhelp", &integer, "help text of this entry\n"
+ "spans multiple lines", 0, PARSE_OPT_NONEG),
OPT_STRING_LIST(0, "list", &list, "str", "add str to list"),
OPT_GROUP("Magic arguments"),
- OPT_ARGUMENT("quux", NULL, "means --quux"),
OPT_NUMBER_CALLBACK(&integer, "set integer to NUM",
number_callback),
{ OPTION_COUNTUP, '+', NULL, &boolean, NULL, "same as -b",
@@ -186,5 +204,137 @@ int cmd__parse_options(int argc, const char **argv)
for (i = 0; i < argc; i++)
show(&expect, &ret, "arg %02d: %s", i, argv[i]);
+ expect.strdup_strings = 1;
+ string_list_clear(&expect, 0);
+ string_list_clear(&list, 0);
+
return ret;
}
+
+static void print_args(int argc, const char **argv)
+{
+ int i;
+ for (i = 0; i < argc; i++)
+ printf("arg %02d: %s\n", i, argv[i]);
+}
+
+static int parse_options_flags__cmd(int argc, const char **argv,
+ enum parse_opt_flags test_flags)
+{
+ const char *usage[] = {
+ "<...> cmd [options]",
+ NULL
+ };
+ int opt = 0;
+ const struct option options[] = {
+ OPT_INTEGER('o', "opt", &opt, "an integer option"),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, NULL, options, usage, test_flags);
+
+ printf("opt: %d\n", opt);
+ print_args(argc, argv);
+
+ return 0;
+}
+
+static enum parse_opt_flags test_flags = 0;
+static const struct option test_flag_options[] = {
+ OPT_GROUP("flag-options:"),
+ OPT_BIT(0, "keep-dashdash", &test_flags,
+ "pass PARSE_OPT_KEEP_DASHDASH to parse_options()",
+ PARSE_OPT_KEEP_DASHDASH),
+ OPT_BIT(0, "stop-at-non-option", &test_flags,
+ "pass PARSE_OPT_STOP_AT_NON_OPTION to parse_options()",
+ PARSE_OPT_STOP_AT_NON_OPTION),
+ OPT_BIT(0, "keep-argv0", &test_flags,
+ "pass PARSE_OPT_KEEP_ARGV0 to parse_options()",
+ PARSE_OPT_KEEP_ARGV0),
+ OPT_BIT(0, "keep-unknown-opt", &test_flags,
+ "pass PARSE_OPT_KEEP_UNKNOWN_OPT to parse_options()",
+ PARSE_OPT_KEEP_UNKNOWN_OPT),
+ OPT_BIT(0, "no-internal-help", &test_flags,
+ "pass PARSE_OPT_NO_INTERNAL_HELP to parse_options()",
+ PARSE_OPT_NO_INTERNAL_HELP),
+ OPT_BIT(0, "subcommand-optional", &test_flags,
+ "pass PARSE_OPT_SUBCOMMAND_OPTIONAL to parse_options()",
+ PARSE_OPT_SUBCOMMAND_OPTIONAL),
+ OPT_END()
+};
+
+int cmd__parse_options_flags(int argc, const char **argv)
+{
+ const char *usage[] = {
+ "test-tool parse-options-flags [flag-options] cmd [options]",
+ NULL
+ };
+
+ argc = parse_options(argc, argv, NULL, test_flag_options, usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+
+ if (!argc || strcmp(argv[0], "cmd")) {
+ error("'cmd' is mandatory");
+ usage_with_options(usage, test_flag_options);
+ }
+
+ return parse_options_flags__cmd(argc, argv, test_flags);
+}
+
+static int subcmd_one(int argc, const char **argv, const char *prefix UNUSED)
+{
+ printf("fn: subcmd_one\n");
+ print_args(argc, argv);
+ return 0;
+}
+
+static int subcmd_two(int argc, const char **argv, const char *prefix UNUSED)
+{
+ printf("fn: subcmd_two\n");
+ print_args(argc, argv);
+ return 0;
+}
+
+static int parse_subcommand__cmd(int argc, const char **argv,
+ enum parse_opt_flags test_flags)
+{
+ const char *usage[] = {
+ "<...> cmd subcmd-one",
+ "<...> cmd subcmd-two",
+ NULL
+ };
+ parse_opt_subcommand_fn *fn = NULL;
+ int opt = 0;
+ struct option options[] = {
+ OPT_SUBCOMMAND("subcmd-one", &fn, subcmd_one),
+ OPT_SUBCOMMAND("subcmd-two", &fn, subcmd_two),
+ OPT_INTEGER('o', "opt", &opt, "an integer option"),
+ OPT_END()
+ };
+
+ if (test_flags & PARSE_OPT_SUBCOMMAND_OPTIONAL)
+ fn = subcmd_one;
+ argc = parse_options(argc, argv, NULL, options, usage, test_flags);
+
+ printf("opt: %d\n", opt);
+
+ return fn(argc, argv, NULL);
+}
+
+int cmd__parse_subcommand(int argc, const char **argv)
+{
+ const char *usage[] = {
+ "test-tool parse-subcommand [flag-options] cmd <subcommand>",
+ NULL
+ };
+
+ argc = parse_options(argc, argv, NULL, test_flag_options, usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+
+ if (!argc || strcmp(argv[0], "cmd")) {
+ error("'cmd' is mandatory");
+ usage_with_options(usage, test_flag_options);
+ }
+
+ return parse_subcommand__cmd(argc, argv, test_flags);
+}
diff --git a/t/helper/test-parse-pathspec-file.c b/t/helper/test-parse-pathspec-file.c
index b3e08ce..89ecefd 100644
--- a/t/helper/test-parse-pathspec-file.c
+++ b/t/helper/test-parse-pathspec-file.c
@@ -1,12 +1,11 @@
#include "test-tool.h"
#include "parse-options.h"
#include "pathspec.h"
-#include "gettext.h"
int cmd__parse_pathspec_file(int argc, const char **argv)
{
struct pathspec pathspec;
- const char *pathspec_from_file = NULL;
+ char *pathspec_from_file = NULL;
int pathspec_file_nul = 0, i;
static const char *const usage[] = {
@@ -29,5 +28,6 @@ int cmd__parse_pathspec_file(int argc, const char **argv)
printf("%s\n", pathspec.items[i].original);
clear_pathspec(&pathspec);
+ free(pathspec_from_file);
return 0;
}
diff --git a/t/helper/test-partial-clone.c b/t/helper/test-partial-clone.c
index 3f102cf..910a128 100644
--- a/t/helper/test-partial-clone.c
+++ b/t/helper/test-partial-clone.c
@@ -1,7 +1,8 @@
-#include "cache.h"
#include "test-tool.h"
+#include "hex.h"
#include "repository.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "setup.h"
/*
* Prints the size of the object corresponding to the given hash in a specific
diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c
index 229ed41..70396fa 100644
--- a/t/helper/test-path-utils.c
+++ b/t/helper/test-path-utils.c
@@ -1,6 +1,11 @@
#include "test-tool.h"
-#include "cache.h"
+#include "abspath.h"
+#include "environment.h"
+#include "path.h"
+#include "read-cache-ll.h"
+#include "setup.h"
#include "string-list.h"
+#include "trace.h"
#include "utf8.h"
/*
@@ -8,7 +13,8 @@
* GIT_CEILING_DIRECTORIES. If the path is unusable for some reason,
* die with an explanation.
*/
-static int normalize_ceiling_entry(struct string_list_item *item, void *unused)
+static int normalize_ceiling_entry(struct string_list_item *item,
+ void *data UNUSED)
{
char *ceil = item->string;
@@ -296,9 +302,8 @@ int cmd__path_utils(int argc, const char **argv)
if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) {
char *buf = xmallocz(strlen(argv[2]));
int rv = normalize_path_copy(buf, argv[2]);
- if (rv)
- buf = "++failed++";
- puts(buf);
+ puts(rv ? "++failed++" : buf);
+ free(buf);
return 0;
}
@@ -356,7 +361,10 @@ int cmd__path_utils(int argc, const char **argv)
int nongit_ok;
setup_git_directory_gently(&nongit_ok);
while (argc > 3) {
- puts(prefix_path(prefix, prefix_len, argv[3]));
+ char *pfx = prefix_path(prefix, prefix_len, argv[3]);
+
+ puts(pfx);
+ free(pfx);
argc--;
argv++;
}
@@ -366,6 +374,7 @@ int cmd__path_utils(int argc, const char **argv)
if (argc == 4 && !strcmp(argv[1], "strip_path_suffix")) {
char *prefix = strip_path_suffix(argv[2], argv[3]);
printf("%s\n", prefix ? prefix : "(null)");
+ free(prefix);
return 0;
}
diff --git a/t/helper/test-pcre2-config.c b/t/helper/test-pcre2-config.c
index 5258fdd..5d0b2a2 100644
--- a/t/helper/test-pcre2-config.c
+++ b/t/helper/test-pcre2-config.c
@@ -1,5 +1,4 @@
#include "test-tool.h"
-#include "cache.h"
#include "grep.h"
int cmd__pcre2_config(int argc, const char **argv)
diff --git a/t/helper/test-pkt-line.c b/t/helper/test-pkt-line.c
index c5e052e..4daa82f 100644
--- a/t/helper/test-pkt-line.c
+++ b/t/helper/test-pkt-line.c
@@ -1,6 +1,9 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "test-tool.h"
#include "pkt-line.h"
+#include "sideband.h"
+#include "write-or-die.h"
+#include "parse-options.h"
static void pack_line(const char *line)
{
@@ -63,12 +66,33 @@ static void unpack(void)
}
}
-static void unpack_sideband(void)
+static void unpack_sideband(int argc, const char **argv)
{
struct packet_reader reader;
- packet_reader_init(&reader, 0, NULL, 0,
- PACKET_READ_GENTLE_ON_EOF |
- PACKET_READ_CHOMP_NEWLINE);
+ int options = PACKET_READ_GENTLE_ON_EOF;
+ int chomp_newline = 1;
+ int reader_use_sideband = 0;
+ const char *const unpack_sideband_usage[] = {
+ "test_tool unpack_sideband [options...]", NULL
+ };
+ struct option cmd_options[] = {
+ OPT_BOOL(0, "reader-use-sideband", &reader_use_sideband,
+ "set use_sideband bit for packet reader (Default: off)"),
+ OPT_BOOL(0, "chomp-newline", &chomp_newline,
+ "chomp newline in packet (Default: on)"),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, "", cmd_options, unpack_sideband_usage,
+ 0);
+ if (argc > 0)
+ usage_msg_opt(_("too many arguments"), unpack_sideband_usage,
+ cmd_options);
+
+ if (chomp_newline)
+ options |= PACKET_READ_CHOMP_NEWLINE;
+ packet_reader_init(&reader, 0, NULL, 0, options);
+ reader.use_sideband = reader_use_sideband;
while (packet_reader_read(&reader) != PACKET_READ_EOF) {
int band;
@@ -78,6 +102,17 @@ static void unpack_sideband(void)
case PACKET_READ_EOF:
break;
case PACKET_READ_NORMAL:
+ /*
+ * When the "use_sideband" field of the reader is turned
+ * on, sideband packets other than the payload have been
+ * parsed and consumed in packet_reader_read(), and only
+ * the payload arrives here.
+ */
+ if (reader.use_sideband) {
+ write_or_die(1, reader.line, reader.pktlen - 1);
+ break;
+ }
+
band = reader.line[0] & 0xff;
if (band < 1 || band > 2)
continue; /* skip non-sideband packets */
@@ -96,15 +131,31 @@ static void unpack_sideband(void)
static int send_split_sideband(void)
{
+ const char *foo = "Foo.\n";
+ const char *bar = "Bar.\n";
const char *part1 = "Hello,";
const char *primary = "\001primary: regular output\n";
const char *part2 = " world!\n";
+ /* Each sideband message has a trailing newline character. */
+ send_sideband(1, 2, foo, strlen(foo), LARGE_PACKET_MAX);
+ send_sideband(1, 2, bar, strlen(bar), LARGE_PACKET_MAX);
+
+ /*
+ * One sideband message is divided into part1 and part2
+ * by the primary message.
+ */
send_sideband(1, 2, part1, strlen(part1), LARGE_PACKET_MAX);
packet_write(1, primary, strlen(primary));
send_sideband(1, 2, part2, strlen(part2), LARGE_PACKET_MAX);
packet_response_end(1);
+ /*
+ * We use unpack_sideband() to consume packets. A flush packet
+ * is required to end parsing.
+ */
+ packet_flush(1);
+
return 0;
}
@@ -125,7 +176,7 @@ int cmd__pkt_line(int argc, const char **argv)
else if (!strcmp(argv[1], "unpack"))
unpack();
else if (!strcmp(argv[1], "unpack-sideband"))
- unpack_sideband();
+ unpack_sideband(argc - 1, argv + 1);
else if (!strcmp(argv[1], "send-split-sideband"))
send_split_sideband();
else if (!strcmp(argv[1], "receive-sideband"))
diff --git a/t/helper/test-prio-queue.c b/t/helper/test-prio-queue.c
deleted file mode 100644
index f402844..0000000
--- a/t/helper/test-prio-queue.c
+++ /dev/null
@@ -1,50 +0,0 @@
-#include "test-tool.h"
-#include "cache.h"
-#include "prio-queue.h"
-
-static int intcmp(const void *va, const void *vb, void *data)
-{
- const int *a = va, *b = vb;
- return *a - *b;
-}
-
-static void show(int *v)
-{
- if (!v)
- printf("NULL\n");
- else
- printf("%d\n", *v);
- free(v);
-}
-
-int cmd__prio_queue(int argc, const char **argv)
-{
- struct prio_queue pq = { intcmp };
-
- while (*++argv) {
- if (!strcmp(*argv, "get")) {
- void *peek = prio_queue_peek(&pq);
- void *get = prio_queue_get(&pq);
- if (peek != get)
- BUG("peek and get results do not match");
- show(get);
- } else if (!strcmp(*argv, "dump")) {
- void *peek;
- void *get;
- while ((peek = prio_queue_peek(&pq))) {
- get = prio_queue_get(&pq);
- if (peek != get)
- BUG("peek and get results do not match");
- show(get);
- }
- } else if (!strcmp(*argv, "stack")) {
- pq.compare = NULL;
- } else {
- int *v = xmalloc(sizeof(*v));
- *v = atoi(*argv);
- prio_queue_put(&pq, v);
- }
- }
-
- return 0;
-}
diff --git a/t/helper/test-proc-receive.c b/t/helper/test-proc-receive.c
index cc08506..f30022d 100644
--- a/t/helper/test-proc-receive.c
+++ b/t/helper/test-proc-receive.c
@@ -1,12 +1,13 @@
-#include "cache.h"
+#include "test-tool.h"
#include "connect.h"
+#include "hex.h"
#include "parse-options.h"
#include "pkt-line.h"
+#include "setup.h"
#include "sigchain.h"
-#include "test-tool.h"
static const char *proc_receive_usage[] = {
- "test-tool proc-receive [<options>...]",
+ "test-tool proc-receive [<options>]",
NULL
};
diff --git a/t/helper/test-progress.c b/t/helper/test-progress.c
index 5d05cbe..66acb6a 100644
--- a/t/helper/test-progress.c
+++ b/t/helper/test-progress.c
@@ -3,6 +3,9 @@
*
* Reads instructions from standard input, one instruction per line:
*
+ * "start <total>[ <title>]" - Call start_progress(title, total),
+ * Uses the default title of "Working hard"
+ * if the " <title>" is omitted.
* "progress <items>" - Call display_progress() with the given item count
* as parameter.
* "throughput <bytes> <millis> - Call display_throughput() with the given
@@ -10,43 +13,59 @@
* specify the time elapsed since the
* start_progress() call.
* "update" - Set the 'progress_update' flag.
+ * "stop" - Call stop_progress().
*
* See 't0500-progress-display.sh' for examples.
*/
#define GIT_TEST_PROGRESS_ONLY
#include "test-tool.h"
-#include "gettext.h"
#include "parse-options.h"
#include "progress.h"
#include "strbuf.h"
+#include "string-list.h"
int cmd__progress(int argc, const char **argv)
{
- int total = 0;
- const char *title;
+ const char *const default_title = "Working hard";
+ struct string_list titles = STRING_LIST_INIT_DUP;
struct strbuf line = STRBUF_INIT;
- struct progress *progress;
+ struct progress *progress = NULL;
const char *usage[] = {
- "test-tool progress [--total=<n>] <progress-title>",
+ "test-tool progress <stdin",
NULL
};
struct option options[] = {
- OPT_INTEGER(0, "total", &total, "total number of items"),
OPT_END(),
};
argc = parse_options(argc, argv, NULL, options, usage, 0);
- if (argc != 1)
- die("need a title for the progress output");
- title = argv[0];
+ if (argc)
+ usage_with_options(usage, options);
progress_testing = 1;
- progress = start_progress(title, total);
while (strbuf_getline(&line, stdin) != EOF) {
char *end;
- if (skip_prefix(line.buf, "progress ", (const char **) &end)) {
+ if (skip_prefix(line.buf, "start ", (const char **) &end)) {
+ uint64_t total = strtoull(end, &end, 10);
+ const char *title;
+
+ /*
+ * We can't use "end + 1" as an argument to
+ * start_progress(), it doesn't xstrdup() its
+ * "title" argument. We need to hold onto a
+ * valid "char *" for it until the end.
+ */
+ if (!*end)
+ title = default_title;
+ else if (*end == ' ')
+ title = string_list_insert(&titles, end + 1)->string;
+ else
+ die("invalid input: '%s'\n", line.buf);
+
+ progress = start_progress(title, total);
+ } else if (skip_prefix(line.buf, "progress ", (const char **) &end)) {
uint64_t item_count = strtoull(end, &end, 10);
if (*end != '\0')
die("invalid input: '%s'\n", line.buf);
@@ -63,12 +82,16 @@ int cmd__progress(int argc, const char **argv)
die("invalid input: '%s'\n", line.buf);
progress_test_ns = test_ms * 1000 * 1000;
display_throughput(progress, byte_count);
- } else if (!strcmp(line.buf, "update"))
+ } else if (!strcmp(line.buf, "update")) {
progress_test_force_update();
- else
+ } else if (!strcmp(line.buf, "stop")) {
+ stop_progress(&progress);
+ } else {
die("invalid input: '%s'\n", line.buf);
+ }
}
- stop_progress(&progress);
+ strbuf_release(&line);
+ string_list_clear(&titles, 0);
return 0;
}
diff --git a/t/helper/test-reach.c b/t/helper/test-reach.c
index 2f65c7f..1e3b431 100644
--- a/t/helper/test-reach.c
+++ b/t/helper/test-reach.c
@@ -1,10 +1,11 @@
#include "test-tool.h"
-#include "cache.h"
#include "commit.h"
#include "commit-reach.h"
-#include "config.h"
-#include "parse-options.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-name.h"
#include "ref-filter.h"
+#include "setup.h"
#include "string-list.h"
#include "tag.h"
@@ -57,7 +58,7 @@ int cmd__reach(int ac, const char **av)
if (buf.len < 3)
continue;
- if (get_oid_committish(buf.buf + 2, &oid))
+ if (repo_get_oid_committish(the_repository, buf.buf + 2, &oid))
die("failed to resolve %s", buf.buf + 2);
orig = parse_object(r, &oid);
@@ -106,13 +107,20 @@ int cmd__reach(int ac, const char **av)
if (!strcmp(av[1], "ref_newer"))
printf("%s(A,B):%d\n", av[1], ref_newer(&oid_A, &oid_B));
else if (!strcmp(av[1], "in_merge_bases"))
- printf("%s(A,B):%d\n", av[1], in_merge_bases(A, B));
+ printf("%s(A,B):%d\n", av[1],
+ repo_in_merge_bases(the_repository, A, B));
else if (!strcmp(av[1], "in_merge_bases_many"))
- printf("%s(A,X):%d\n", av[1], in_merge_bases_many(A, X_nr, X_array));
+ printf("%s(A,X):%d\n", av[1],
+ repo_in_merge_bases_many(the_repository, A, X_nr, X_array, 0));
else if (!strcmp(av[1], "is_descendant_of"))
printf("%s(A,X):%d\n", av[1], repo_is_descendant_of(r, A, X));
else if (!strcmp(av[1], "get_merge_bases_many")) {
- struct commit_list *list = get_merge_bases_many(A, X_nr, X_array);
+ struct commit_list *list = NULL;
+ if (repo_get_merge_bases_many(the_repository,
+ A, X_nr,
+ X_array,
+ &list) < 0)
+ exit(128);
printf("%s(A,X):\n", av[1]);
print_sorted_commit_ids(list);
} else if (!strcmp(av[1], "reduce_heads")) {
@@ -131,7 +139,7 @@ int cmd__reach(int ac, const char **av)
printf("%s(X,_,_,0,0):%d\n", av[1], can_all_from_reach_with_flag(&X_obj, 2, 4, 0, 0));
} else if (!strcmp(av[1], "commit_contains")) {
- struct ref_filter filter;
+ struct ref_filter filter = REF_FILTER_INIT;
struct contains_cache cache;
init_contains_cache(&cache);
diff --git a/t/helper/test-read-cache.c b/t/helper/test-read-cache.c
index b52c174..1acd362 100644
--- a/t/helper/test-read-cache.c
+++ b/t/helper/test-read-cache.c
@@ -1,82 +1,42 @@
+#define USE_THE_INDEX_VARIABLE
#include "test-tool.h"
-#include "cache.h"
#include "config.h"
-#include "blob.h"
-#include "commit.h"
-#include "tree.h"
-#include "sparse-index.h"
-
-static void print_cache_entry(struct cache_entry *ce)
-{
- const char *type;
- printf("%06o ", ce->ce_mode & 0177777);
-
- if (S_ISSPARSEDIR(ce->ce_mode))
- type = tree_type;
- else if (S_ISGITLINK(ce->ce_mode))
- type = commit_type;
- else
- type = blob_type;
-
- printf("%s %s\t%s\n",
- type,
- oid_to_hex(&ce->oid),
- ce->name);
-}
-
-static void print_cache(struct index_state *istate)
-{
- int i;
- for (i = 0; i < istate->cache_nr; i++)
- print_cache_entry(istate->cache[i]);
-}
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
int cmd__read_cache(int argc, const char **argv)
{
- struct repository *r = the_repository;
int i, cnt = 1;
const char *name = NULL;
- int table = 0, expand = 0;
initialize_the_repository();
- prepare_repo_settings(r);
- r->settings.command_requires_full_index = 0;
- for (++argv, --argc; *argv && starts_with(*argv, "--"); ++argv, --argc) {
- if (skip_prefix(*argv, "--print-and-refresh=", &name))
- continue;
- if (!strcmp(*argv, "--table"))
- table = 1;
- else if (!strcmp(*argv, "--expand"))
- expand = 1;
+ if (argc > 1 && skip_prefix(argv[1], "--print-and-refresh=", &name)) {
+ argc--;
+ argv++;
}
- if (argc == 1)
- cnt = strtol(argv[0], NULL, 0);
+ if (argc == 2)
+ cnt = strtol(argv[1], NULL, 0);
setup_git_directory();
git_config(git_default_config, NULL);
for (i = 0; i < cnt; i++) {
- repo_read_index(r);
-
- if (expand)
- ensure_full_index(r->index);
-
+ repo_read_index(the_repository);
if (name) {
int pos;
- refresh_index(r->index, REFRESH_QUIET,
+ refresh_index(&the_index, REFRESH_QUIET,
NULL, NULL, NULL);
- pos = index_name_pos(r->index, name, strlen(name));
+ pos = index_name_pos(&the_index, name, strlen(name));
if (pos < 0)
die("%s not in index", name);
printf("%s is%s up to date\n", name,
- ce_uptodate(r->index->cache[pos]) ? "" : " not");
+ ce_uptodate(the_index.cache[pos]) ? "" : " not");
write_file(name, "%d\n", i);
}
- if (table)
- print_cache(r->index);
- discard_index(r->index);
+ discard_index(&the_index);
}
return 0;
}
diff --git a/t/helper/test-read-graph.c b/t/helper/test-read-graph.c
index 75927b2..8c7a83f 100644
--- a/t/helper/test-read-graph.c
+++ b/t/helper/test-read-graph.c
@@ -1,10 +1,11 @@
#include "test-tool.h"
-#include "cache.h"
#include "commit-graph.h"
#include "repository.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "bloom.h"
+#include "setup.h"
-int cmd__read_graph(int argc, const char **argv)
+int cmd__read_graph(int argc UNUSED, const char **argv UNUSED)
{
struct commit_graph *graph = NULL;
struct object_directory *odb;
@@ -45,6 +46,18 @@ int cmd__read_graph(int argc, const char **argv)
printf(" bloom_data");
printf("\n");
+ printf("options:");
+ if (graph->bloom_filter_settings)
+ printf(" bloom(%"PRIu32",%"PRIu32",%"PRIu32")",
+ graph->bloom_filter_settings->hash_version,
+ graph->bloom_filter_settings->bits_per_entry,
+ graph->bloom_filter_settings->num_hashes);
+ if (graph->read_generation_data)
+ printf(" read_generation_data");
+ if (graph->topo_levels)
+ printf(" topo_levels");
+ printf("\n");
+
UNLEAK(graph);
return 0;
diff --git a/t/helper/test-read-midx.c b/t/helper/test-read-midx.c
index 7c2eb11..4acae41 100644
--- a/t/helper/test-read-midx.c
+++ b/t/helper/test-read-midx.c
@@ -1,8 +1,12 @@
#include "test-tool.h"
-#include "cache.h"
+#include "hex.h"
#include "midx.h"
#include "repository.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "pack-bitmap.h"
+#include "packfile.h"
+#include "setup.h"
+#include "gettext.h"
static int read_midx_file(const char *object_dir, int show_objects)
{
@@ -54,18 +58,83 @@ static int read_midx_file(const char *object_dir, int show_objects)
printf("%s %"PRIu64"\t%s\n",
oid_to_hex(&oid), e.offset, e.p->pack_name);
}
- return 0;
}
+ close_midx(m);
+
+ return 0;
+}
+
+static int read_midx_checksum(const char *object_dir)
+{
+ struct multi_pack_index *m;
+
+ setup_git_directory();
+ m = load_multi_pack_index(object_dir, 1);
+ if (!m)
+ return 1;
+ printf("%s\n", hash_to_hex(get_midx_checksum(m)));
+ return 0;
+}
+
+static int read_midx_preferred_pack(const char *object_dir)
+{
+ struct multi_pack_index *midx = NULL;
+ uint32_t preferred_pack;
+
+ setup_git_directory();
+
+ midx = load_multi_pack_index(object_dir, 1);
+ if (!midx)
+ return 1;
+
+ if (midx_preferred_pack(midx, &preferred_pack) < 0) {
+ warning(_("could not determine MIDX preferred pack"));
+ return 1;
+ }
+
+ printf("%s\n", midx->pack_names[preferred_pack]);
+ return 0;
+}
+
+static int read_midx_bitmapped_packs(const char *object_dir)
+{
+ struct multi_pack_index *midx = NULL;
+ struct bitmapped_pack pack;
+ uint32_t i;
+
+ setup_git_directory();
+
+ midx = load_multi_pack_index(object_dir, 1);
+ if (!midx)
+ return 1;
+
+ for (i = 0; i < midx->num_packs; i++) {
+ if (nth_bitmapped_pack(the_repository, midx, &pack, i) < 0)
+ return 1;
+
+ printf("%s\n", pack_basename(pack.p));
+ printf(" bitmap_pos: %"PRIuMAX"\n", (uintmax_t)pack.bitmap_pos);
+ printf(" bitmap_nr: %"PRIuMAX"\n", (uintmax_t)pack.bitmap_nr);
+ }
+
+ close_midx(midx);
+
return 0;
}
int cmd__read_midx(int argc, const char **argv)
{
if (!(argc == 2 || argc == 3))
- usage("read-midx [--show-objects] <object-dir>");
+ usage("read-midx [--show-objects|--checksum|--preferred-pack|--bitmap] <object-dir>");
if (!strcmp(argv[1], "--show-objects"))
return read_midx_file(argv[2], 1);
+ else if (!strcmp(argv[1], "--checksum"))
+ return read_midx_checksum(argv[2]);
+ else if (!strcmp(argv[1], "--preferred-pack"))
+ return read_midx_preferred_pack(argv[2]);
+ else if (!strcmp(argv[1], "--bitmap"))
+ return read_midx_bitmapped_packs(argv[2]);
return read_midx_file(argv[1], 0);
}
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index b314b81..82bbf6e 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -1,9 +1,55 @@
#include "test-tool.h"
-#include "cache.h"
+#include "hex.h"
#include "refs.h"
+#include "setup.h"
#include "worktree.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "path.h"
#include "repository.h"
+#include "strbuf.h"
+#include "revision.h"
+
+struct flag_definition {
+ const char *name;
+ uint64_t mask;
+};
+
+#define FLAG_DEF(x) \
+ { \
+#x, (x) \
+ }
+
+static unsigned int parse_flags(const char *str, struct flag_definition *defs)
+{
+ struct string_list masks = STRING_LIST_INIT_DUP;
+ int i = 0;
+ unsigned int result = 0;
+
+ if (!strcmp(str, "0"))
+ return 0;
+
+ string_list_split(&masks, str, ',', 64);
+ for (; i < masks.nr; i++) {
+ const char *name = masks.items[i].string;
+ struct flag_definition *def = defs;
+ int found = 0;
+ while (def->name) {
+ if (!strcmp(def->name, name)) {
+ result |= def->mask;
+ found = 1;
+ break;
+ }
+ def++;
+ }
+ if (!found)
+ die("unknown flag \"%s\"", name);
+ }
+
+ string_list_clear(&masks, 0);
+ return result;
+}
+
+static struct flag_definition empty_flags[] = { { NULL, 0 } };
static const char *notnull(const char *arg, const char *name)
{
@@ -12,9 +58,10 @@ static const char *notnull(const char *arg, const char *name)
return arg;
}
-static unsigned int arg_flags(const char *arg, const char *name)
+static unsigned int arg_flags(const char *arg, const char *name,
+ struct flag_definition *defs)
{
- return atoi(notnull(arg, name));
+ return parse_flags(notnull(arg, name), defs);
}
static const char **get_store(const char **argv, struct ref_store **refs)
@@ -53,6 +100,7 @@ static const char **get_store(const char **argv, struct ref_store **refs)
die("no such worktree: %s", gitdir);
*refs = get_worktree_ref_store(*p);
+ free_worktrees(worktrees);
} else
die("unknown backend %s", argv[0]);
@@ -64,14 +112,6 @@ static const char **get_store(const char **argv, struct ref_store **refs)
return argv + 1;
}
-
-static int cmd_pack_refs(struct ref_store *refs, const char **argv)
-{
- unsigned int flags = arg_flags(*argv++, "flags");
-
- return refs_pack_refs(refs, flags);
-}
-
static int cmd_create_symref(struct ref_store *refs, const char **argv)
{
const char *refname = notnull(*argv++, "refname");
@@ -81,16 +121,27 @@ static int cmd_create_symref(struct ref_store *refs, const char **argv)
return refs_create_symref(refs, refname, target, logmsg);
}
+static struct flag_definition transaction_flags[] = {
+ FLAG_DEF(REF_NO_DEREF),
+ FLAG_DEF(REF_FORCE_CREATE_REFLOG),
+ FLAG_DEF(REF_SKIP_OID_VERIFICATION),
+ FLAG_DEF(REF_SKIP_REFNAME_VERIFICATION),
+ { NULL, 0 }
+};
+
static int cmd_delete_refs(struct ref_store *refs, const char **argv)
{
- unsigned int flags = arg_flags(*argv++, "flags");
+ unsigned int flags = arg_flags(*argv++, "flags", transaction_flags);
const char *msg = *argv++;
struct string_list refnames = STRING_LIST_INIT_NODUP;
+ int result;
while (*argv)
string_list_append(&refnames, *argv++);
- return refs_delete_refs(refs, msg, &refnames, flags);
+ result = refs_delete_refs(refs, msg, &refnames, flags);
+ string_list_clear(&refnames, 0);
+ return result;
}
static int cmd_rename_ref(struct ref_store *refs, const char **argv)
@@ -103,7 +154,7 @@ static int cmd_rename_ref(struct ref_store *refs, const char **argv)
}
static int each_ref(const char *refname, const struct object_id *oid,
- int flags, void *cb_data)
+ int flags, void *cb_data UNUSED)
{
printf("%s %s 0x%x\n", oid_to_hex(oid), refname, flags);
return 0;
@@ -116,11 +167,20 @@ static int cmd_for_each_ref(struct ref_store *refs, const char **argv)
return refs_for_each_ref_in(refs, prefix, each_ref, NULL);
}
+static int cmd_for_each_ref__exclude(struct ref_store *refs, const char **argv)
+{
+ const char *prefix = notnull(*argv++, "prefix");
+ const char **exclude_patterns = argv;
+
+ return refs_for_each_fullref_in(refs, prefix, exclude_patterns, each_ref,
+ NULL);
+}
+
static int cmd_resolve_ref(struct ref_store *refs, const char **argv)
{
struct object_id oid = *null_oid();
const char *refname = notnull(*argv++, "refname");
- int resolve_flags = arg_flags(*argv++, "resolve-flags");
+ int resolve_flags = arg_flags(*argv++, "resolve-flags", empty_flags);
int flags;
const char *ref;
@@ -142,18 +202,25 @@ static int cmd_verify_ref(struct ref_store *refs, const char **argv)
return ret;
}
-static int cmd_for_each_reflog(struct ref_store *refs, const char **argv)
+static int each_reflog(const char *refname, void *cb_data UNUSED)
{
- return refs_for_each_reflog(refs, each_ref, NULL);
+ printf("%s\n", refname);
+ return 0;
}
-static int each_reflog(struct object_id *old_oid, struct object_id *new_oid,
- const char *committer, timestamp_t timestamp,
- int tz, const char *msg, void *cb_data)
+static int cmd_for_each_reflog(struct ref_store *refs,
+ const char **argv UNUSED)
{
- printf("%s %s %s %"PRItime" %d %s\n",
- oid_to_hex(old_oid), oid_to_hex(new_oid),
- committer, timestamp, tz, msg);
+ return refs_for_each_reflog(refs, each_reflog, NULL);
+}
+
+static int each_reflog_ent(struct object_id *old_oid, struct object_id *new_oid,
+ const char *committer, timestamp_t timestamp,
+ int tz, const char *msg, void *cb_data UNUSED)
+{
+ printf("%s %s %s %" PRItime " %+05d%s%s", oid_to_hex(old_oid),
+ oid_to_hex(new_oid), committer, timestamp, tz,
+ *msg == '\n' ? "" : "\t", msg);
return 0;
}
@@ -161,14 +228,14 @@ static int cmd_for_each_reflog_ent(struct ref_store *refs, const char **argv)
{
const char *refname = notnull(*argv++, "refname");
- return refs_for_each_reflog_ent(refs, refname, each_reflog, refs);
+ return refs_for_each_reflog_ent(refs, refname, each_reflog_ent, refs);
}
static int cmd_for_each_reflog_ent_reverse(struct ref_store *refs, const char **argv)
{
const char *refname = notnull(*argv++, "refname");
- return refs_for_each_reflog_ent_reverse(refs, refname, each_reflog, refs);
+ return refs_for_each_reflog_ent_reverse(refs, refname, each_reflog_ent, refs);
}
static int cmd_reflog_exists(struct ref_store *refs, const char **argv)
@@ -181,11 +248,10 @@ static int cmd_reflog_exists(struct ref_store *refs, const char **argv)
static int cmd_create_reflog(struct ref_store *refs, const char **argv)
{
const char *refname = notnull(*argv++, "refname");
- int force_create = arg_flags(*argv++, "force-create");
struct strbuf err = STRBUF_INIT;
int ret;
- ret = refs_create_reflog(refs, refname, force_create, &err);
+ ret = refs_create_reflog(refs, refname, &err);
if (err.len)
puts(err.buf);
return ret;
@@ -198,21 +264,16 @@ static int cmd_delete_reflog(struct ref_store *refs, const char **argv)
return refs_delete_reflog(refs, refname);
}
-static int cmd_reflog_expire(struct ref_store *refs, const char **argv)
-{
- die("not supported yet");
-}
-
static int cmd_delete_ref(struct ref_store *refs, const char **argv)
{
const char *msg = notnull(*argv++, "msg");
const char *refname = notnull(*argv++, "refname");
const char *sha1_buf = notnull(*argv++, "old-sha1");
- unsigned int flags = arg_flags(*argv++, "flags");
+ unsigned int flags = arg_flags(*argv++, "flags", transaction_flags);
struct object_id old_oid;
if (get_oid_hex(sha1_buf, &old_oid))
- die("not sha-1");
+ die("cannot parse %s as %s", sha1_buf, the_hash_algo->name);
return refs_delete_ref(refs, msg, refname, &old_oid, flags);
}
@@ -223,16 +284,20 @@ static int cmd_update_ref(struct ref_store *refs, const char **argv)
const char *refname = notnull(*argv++, "refname");
const char *new_sha1_buf = notnull(*argv++, "new-sha1");
const char *old_sha1_buf = notnull(*argv++, "old-sha1");
- unsigned int flags = arg_flags(*argv++, "flags");
- struct object_id old_oid;
+ unsigned int flags = arg_flags(*argv++, "flags", transaction_flags);
+ struct object_id old_oid, *old_oid_ptr = NULL;
struct object_id new_oid;
- if (get_oid_hex(old_sha1_buf, &old_oid) ||
- get_oid_hex(new_sha1_buf, &new_oid))
- die("not sha-1");
+ if (*old_sha1_buf) {
+ if (get_oid_hex(old_sha1_buf, &old_oid))
+ die("cannot parse %s as %s", old_sha1_buf, the_hash_algo->name);
+ old_oid_ptr = &old_oid;
+ }
+ if (get_oid_hex(new_sha1_buf, &new_oid))
+ die("cannot parse %s as %s", new_sha1_buf, the_hash_algo->name);
return refs_update_ref(refs, msg, refname,
- &new_oid, &old_oid,
+ &new_oid, old_oid_ptr,
flags, UPDATE_REFS_DIE_ON_ERR);
}
@@ -242,11 +307,11 @@ struct command {
};
static struct command commands[] = {
- { "pack-refs", cmd_pack_refs },
{ "create-symref", cmd_create_symref },
{ "delete-refs", cmd_delete_refs },
{ "rename-ref", cmd_rename_ref },
{ "for-each-ref", cmd_for_each_ref },
+ { "for-each-ref--exclude", cmd_for_each_ref__exclude },
{ "resolve-ref", cmd_resolve_ref },
{ "verify-ref", cmd_verify_ref },
{ "for-each-reflog", cmd_for_each_reflog },
@@ -255,7 +320,6 @@ static struct command commands[] = {
{ "reflog-exists", cmd_reflog_exists },
{ "create-reflog", cmd_create_reflog },
{ "delete-reflog", cmd_delete_reflog },
- { "reflog-expire", cmd_reflog_expire },
/*
* backend transaction functions can't be tested separately
*/
@@ -264,7 +328,7 @@ static struct command commands[] = {
{ NULL, NULL }
};
-int cmd__ref_store(int argc, const char **argv)
+int cmd__ref_store(int argc UNUSED, const char **argv)
{
struct ref_store *refs;
const char *func;
diff --git a/t/helper/test-reftable.c b/t/helper/test-reftable.c
new file mode 100644
index 0000000..00237ef
--- /dev/null
+++ b/t/helper/test-reftable.c
@@ -0,0 +1,23 @@
+#include "reftable/system.h"
+#include "reftable/reftable-tests.h"
+#include "test-tool.h"
+
+int cmd__reftable(int argc, const char **argv)
+{
+ /* test from simple to complex. */
+ basics_test_main(argc, argv);
+ record_test_main(argc, argv);
+ block_test_main(argc, argv);
+ tree_test_main(argc, argv);
+ pq_test_main(argc, argv);
+ readwrite_test_main(argc, argv);
+ merged_test_main(argc, argv);
+ stack_test_main(argc, argv);
+ refname_test_main(argc, argv);
+ return 0;
+}
+
+int cmd__dump_reftable(int argc, const char **argv)
+{
+ return reftable_dump_main(argc, (char *const *)argv);
+}
diff --git a/t/helper/test-regex.c b/t/helper/test-regex.c
index d6f28ca..80042ea 100644
--- a/t/helper/test-regex.c
+++ b/t/helper/test-regex.c
@@ -30,10 +30,11 @@ static int test_regex_bug(void)
if (regexec(&r, str, 1, m, 0))
die("no match of pattern '%s' to string '%s'", pat, str);
- /* http://sourceware.org/bugzilla/show_bug.cgi?id=3957 */
+ /* https://sourceware.org/bugzilla/show_bug.cgi?id=3957 */
if (m[0].rm_so == 3) /* matches '\n' when it should not */
die("regex bug confirmed: re-build git with NO_REGEX=1");
+ regfree(&r);
return 0;
}
@@ -94,18 +95,20 @@ int cmd__regex(int argc, const char **argv)
die("failed regcomp() for pattern '%s' (%s)", pat, errbuf);
}
if (!str)
- return 0;
+ goto cleanup;
ret = regexec(&r, str, 1, m, 0);
if (ret) {
if (silent || ret == REG_NOMATCH)
- return ret;
+ goto cleanup;
regerror(ret, &r, errbuf, sizeof(errbuf));
die("failed regexec() for subject '%s' (%s)", str, errbuf);
}
- return 0;
+cleanup:
+ regfree(&r);
+ return ret;
usage:
usage("\ttest-tool regex --bug\n"
"\ttest-tool regex [--silent] <pattern>\n"
diff --git a/t/helper/test-repository.c b/t/helper/test-repository.c
index 56f0e3c..0c7c5aa 100644
--- a/t/helper/test-repository.c
+++ b/t/helper/test-repository.c
@@ -1,11 +1,11 @@
#include "test-tool.h"
-#include "cache.h"
#include "commit-graph.h"
#include "commit.h"
-#include "config.h"
-#include "object-store.h"
+#include "environment.h"
+#include "hex.h"
#include "object.h"
#include "repository.h"
+#include "setup.h"
#include "tree.h"
static void test_parse_commit_in_graph(const char *gitdir, const char *worktree,
diff --git a/t/helper/test-revision-walking.c b/t/helper/test-revision-walking.c
index 625b2db..f346951 100644
--- a/t/helper/test-revision-walking.c
+++ b/t/helper/test-revision-walking.c
@@ -9,17 +9,19 @@
*/
#include "test-tool.h"
-#include "cache.h"
#include "commit.h"
#include "diff.h"
+#include "repository.h"
#include "revision.h"
+#include "setup.h"
static void print_commit(struct commit *commit)
{
struct strbuf sb = STRBUF_INIT;
struct pretty_print_context ctx = {0};
ctx.date_mode.type = DATE_NORMAL;
- format_commit_message(commit, " %m %s", &sb, &ctx);
+ repo_format_commit_message(the_repository, commit, " %m %s", &sb,
+ &ctx);
printf("%s\n", sb.buf);
strbuf_release(&sb);
}
@@ -43,6 +45,7 @@ static int run_revision_walk(void)
}
reset_revision_walk();
+ release_revisions(&rev);
return got_revision;
}
diff --git a/t/helper/test-rot13-filter.c b/t/helper/test-rot13-filter.c
new file mode 100644
index 0000000..f8d564c
--- /dev/null
+++ b/t/helper/test-rot13-filter.c
@@ -0,0 +1,382 @@
+/*
+ * Example implementation for the Git filter protocol version 2
+ * See Documentation/gitattributes.txt, section "Filter Protocol"
+ *
+ * Usage: test-tool rot13-filter [--always-delay] --log=<path> <capabilities>
+ *
+ * Log path defines a debug log file that the script writes to. The
+ * subsequent arguments define a list of supported protocol capabilities
+ * ("clean", "smudge", etc).
+ *
+ * When --always-delay is given all pathnames with the "can-delay" flag
+ * that don't appear on the list bellow are delayed with a count of 1
+ * (see more below).
+ *
+ * This implementation supports special test cases:
+ * (1) If data with the pathname "clean-write-fail.r" is processed with
+ * a "clean" operation then the write operation will die.
+ * (2) If data with the pathname "smudge-write-fail.r" is processed with
+ * a "smudge" operation then the write operation will die.
+ * (3) If data with the pathname "error.r" is processed with any
+ * operation then the filter signals that it cannot or does not want
+ * to process the file.
+ * (4) If data with the pathname "abort.r" is processed with any
+ * operation then the filter signals that it cannot or does not want
+ * to process the file and any file after that is processed with the
+ * same command.
+ * (5) If data with a pathname that is a key in the delay hash is
+ * requested (e.g. "test-delay10.a") then the filter responds with
+ * a "delay" status and sets the "requested" field in the delay hash.
+ * The filter will signal the availability of this object after
+ * "count" (field in delay hash) "list_available_blobs" commands.
+ * (6) If data with the pathname "missing-delay.a" is processed that the
+ * filter will drop the path from the "list_available_blobs" response.
+ * (7) If data with the pathname "invalid-delay.a" is processed that the
+ * filter will add the path "unfiltered" which was not delayed before
+ * to the "list_available_blobs" response.
+ */
+
+#include "test-tool.h"
+#include "pkt-line.h"
+#include "string-list.h"
+#include "strmap.h"
+#include "parse-options.h"
+
+static FILE *logfile;
+static int always_delay, has_clean_cap, has_smudge_cap;
+static struct strmap delay = STRMAP_INIT;
+
+static inline const char *str_or_null(const char *str)
+{
+ return str ? str : "(null)";
+}
+
+static char *rot13(char *str)
+{
+ char *c;
+ for (c = str; *c; c++)
+ if (isalpha(*c))
+ *c += tolower(*c) < 'n' ? 13 : -13;
+ return str;
+}
+
+static char *get_value(char *buf, const char *key)
+{
+ const char *orig_buf = buf;
+ if (!buf ||
+ !skip_prefix((const char *)buf, key, (const char **)&buf) ||
+ !skip_prefix((const char *)buf, "=", (const char **)&buf) ||
+ !*buf)
+ die("expected key '%s', got '%s'", key, str_or_null(orig_buf));
+ return buf;
+}
+
+/*
+ * Read a text packet, expecting that it is in the form "key=value" for
+ * the given key. An EOF does not trigger any error and is reported
+ * back to the caller with NULL. Die if the "key" part of "key=value" does
+ * not match the given key, or the value part is empty.
+ */
+static char *packet_key_val_read(const char *key)
+{
+ char *buf;
+ if (packet_read_line_gently(0, NULL, &buf) < 0)
+ return NULL;
+ return xstrdup(get_value(buf, key));
+}
+
+static inline void assert_remote_capability(struct strset *caps, const char *cap)
+{
+ if (!strset_contains(caps, cap))
+ die("required '%s' capability not available from remote", cap);
+}
+
+static void read_capabilities(struct strset *remote_caps)
+{
+ for (;;) {
+ char *buf = packet_read_line(0, NULL);
+ if (!buf)
+ break;
+ strset_add(remote_caps, get_value(buf, "capability"));
+ }
+
+ assert_remote_capability(remote_caps, "clean");
+ assert_remote_capability(remote_caps, "smudge");
+ assert_remote_capability(remote_caps, "delay");
+}
+
+static void check_and_write_capabilities(struct strset *remote_caps,
+ const char **caps, int nr_caps)
+{
+ int i;
+ for (i = 0; i < nr_caps; i++) {
+ if (!strset_contains(remote_caps, caps[i]))
+ die("our capability '%s' is not available from remote",
+ caps[i]);
+ packet_write_fmt(1, "capability=%s\n", caps[i]);
+ }
+ packet_flush(1);
+}
+
+struct delay_entry {
+ int requested, count;
+ char *output;
+};
+
+static void free_delay_entries(void)
+{
+ struct hashmap_iter iter;
+ struct strmap_entry *ent;
+
+ strmap_for_each_entry(&delay, &iter, ent) {
+ struct delay_entry *delay_entry = ent->value;
+ free(delay_entry->output);
+ free(delay_entry);
+ }
+ strmap_clear(&delay, 0);
+}
+
+static void add_delay_entry(char *pathname, int count, int requested)
+{
+ struct delay_entry *entry = xcalloc(1, sizeof(*entry));
+ entry->count = count;
+ entry->requested = requested;
+ if (strmap_put(&delay, pathname, entry))
+ BUG("adding the same path twice to delay hash?");
+}
+
+static void reply_list_available_blobs_cmd(void)
+{
+ struct hashmap_iter iter;
+ struct strmap_entry *ent;
+ struct string_list_item *str_item;
+ struct string_list paths = STRING_LIST_INIT_NODUP;
+
+ /* flush */
+ if (packet_read_line(0, NULL))
+ die("bad list_available_blobs end");
+
+ strmap_for_each_entry(&delay, &iter, ent) {
+ struct delay_entry *delay_entry = ent->value;
+ if (!delay_entry->requested)
+ continue;
+ delay_entry->count--;
+ if (!strcmp(ent->key, "invalid-delay.a")) {
+ /* Send Git a pathname that was not delayed earlier */
+ packet_write_fmt(1, "pathname=unfiltered");
+ }
+ if (!strcmp(ent->key, "missing-delay.a")) {
+ /* Do not signal Git that this file is available */
+ } else if (!delay_entry->count) {
+ string_list_append(&paths, ent->key);
+ packet_write_fmt(1, "pathname=%s", ent->key);
+ }
+ }
+
+ /* Print paths in sorted order. */
+ string_list_sort(&paths);
+ for_each_string_list_item(str_item, &paths)
+ fprintf(logfile, " %s", str_item->string);
+ string_list_clear(&paths, 0);
+
+ packet_flush(1);
+
+ fprintf(logfile, " [OK]\n");
+ packet_write_fmt(1, "status=success");
+ packet_flush(1);
+}
+
+static void command_loop(void)
+{
+ for (;;) {
+ char *buf, *output;
+ char *pathname;
+ struct delay_entry *entry;
+ struct strbuf input = STRBUF_INIT;
+ char *command = packet_key_val_read("command");
+
+ if (!command) {
+ fprintf(logfile, "STOP\n");
+ break;
+ }
+ fprintf(logfile, "IN: %s", command);
+
+ if (!strcmp(command, "list_available_blobs")) {
+ reply_list_available_blobs_cmd();
+ free(command);
+ continue;
+ }
+
+ pathname = packet_key_val_read("pathname");
+ if (!pathname)
+ die("unexpected EOF while expecting pathname");
+ fprintf(logfile, " %s", pathname);
+
+ /* Read until flush */
+ while ((buf = packet_read_line(0, NULL))) {
+ if (!strcmp(buf, "can-delay=1")) {
+ entry = strmap_get(&delay, pathname);
+ if (entry && !entry->requested)
+ entry->requested = 1;
+ else if (!entry && always_delay)
+ add_delay_entry(pathname, 1, 1);
+ } else if (starts_with(buf, "ref=") ||
+ starts_with(buf, "treeish=") ||
+ starts_with(buf, "blob=")) {
+ fprintf(logfile, " %s", buf);
+ } else {
+ /*
+ * In general, filters need to be graceful about
+ * new metadata, since it's documented that we
+ * can pass any key-value pairs, but for tests,
+ * let's be a little stricter.
+ */
+ die("Unknown message '%s'", buf);
+ }
+ }
+
+ read_packetized_to_strbuf(0, &input, 0);
+ fprintf(logfile, " %"PRIuMAX" [OK] -- ", (uintmax_t)input.len);
+
+ entry = strmap_get(&delay, pathname);
+ if (entry && entry->output) {
+ output = entry->output;
+ } else if (!strcmp(pathname, "error.r") || !strcmp(pathname, "abort.r")) {
+ output = "";
+ } else if (!strcmp(command, "clean") && has_clean_cap) {
+ output = rot13(input.buf);
+ } else if (!strcmp(command, "smudge") && has_smudge_cap) {
+ output = rot13(input.buf);
+ } else {
+ die("bad command '%s'", command);
+ }
+
+ if (!strcmp(pathname, "error.r")) {
+ fprintf(logfile, "[ERROR]\n");
+ packet_write_fmt(1, "status=error");
+ packet_flush(1);
+ } else if (!strcmp(pathname, "abort.r")) {
+ fprintf(logfile, "[ABORT]\n");
+ packet_write_fmt(1, "status=abort");
+ packet_flush(1);
+ } else if (!strcmp(command, "smudge") &&
+ (entry = strmap_get(&delay, pathname)) &&
+ entry->requested == 1) {
+ fprintf(logfile, "[DELAYED]\n");
+ packet_write_fmt(1, "status=delayed");
+ packet_flush(1);
+ entry->requested = 2;
+ if (entry->output != output) {
+ free(entry->output);
+ entry->output = xstrdup(output);
+ }
+ } else {
+ int i, nr_packets = 0;
+ size_t output_len;
+ const char *p;
+ packet_write_fmt(1, "status=success");
+ packet_flush(1);
+
+ if (skip_prefix(pathname, command, &p) &&
+ !strcmp(p, "-write-fail.r")) {
+ fprintf(logfile, "[WRITE FAIL]\n");
+ die("%s write error", command);
+ }
+
+ output_len = strlen(output);
+ fprintf(logfile, "OUT: %"PRIuMAX" ", (uintmax_t)output_len);
+
+ if (write_packetized_from_buf_no_flush_count(output,
+ output_len, 1, &nr_packets))
+ die("failed to write buffer to stdout");
+ packet_flush(1);
+
+ for (i = 0; i < nr_packets; i++)
+ fprintf(logfile, ".");
+ fprintf(logfile, " [OK]\n");
+
+ packet_flush(1);
+ }
+ free(pathname);
+ strbuf_release(&input);
+ free(command);
+ }
+}
+
+static void packet_initialize(void)
+{
+ char *pkt_buf = packet_read_line(0, NULL);
+
+ if (!pkt_buf || strcmp(pkt_buf, "git-filter-client"))
+ die("bad initialize: '%s'", str_or_null(pkt_buf));
+
+ pkt_buf = packet_read_line(0, NULL);
+ if (!pkt_buf || strcmp(pkt_buf, "version=2"))
+ die("bad version: '%s'", str_or_null(pkt_buf));
+
+ pkt_buf = packet_read_line(0, NULL);
+ if (pkt_buf)
+ die("bad version end: '%s'", pkt_buf);
+
+ packet_write_fmt(1, "git-filter-server");
+ packet_write_fmt(1, "version=2");
+ packet_flush(1);
+}
+
+static const char *rot13_usage[] = {
+ "test-tool rot13-filter [--always-delay] --log=<path> <capabilities>",
+ NULL
+};
+
+int cmd__rot13_filter(int argc, const char **argv)
+{
+ int i, nr_caps;
+ struct strset remote_caps = STRSET_INIT;
+ const char *log_path = NULL;
+
+ struct option options[] = {
+ OPT_BOOL(0, "always-delay", &always_delay,
+ "delay all paths with the can-delay flag"),
+ OPT_STRING(0, "log", &log_path, "path",
+ "path to the debug log file"),
+ OPT_END()
+ };
+ nr_caps = parse_options(argc, argv, NULL, options, rot13_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+
+ if (!log_path || !nr_caps)
+ usage_with_options(rot13_usage, options);
+
+ logfile = fopen(log_path, "a");
+ if (!logfile)
+ die_errno("failed to open log file");
+
+ for (i = 0; i < nr_caps; i++) {
+ if (!strcmp(argv[i], "smudge"))
+ has_smudge_cap = 1;
+ if (!strcmp(argv[i], "clean"))
+ has_clean_cap = 1;
+ }
+
+ add_delay_entry("test-delay10.a", 1, 0);
+ add_delay_entry("test-delay11.a", 1, 0);
+ add_delay_entry("test-delay20.a", 2, 0);
+ add_delay_entry("test-delay10.b", 1, 0);
+ add_delay_entry("missing-delay.a", 1, 0);
+ add_delay_entry("invalid-delay.a", 1, 0);
+
+ fprintf(logfile, "START\n");
+ packet_initialize();
+
+ read_capabilities(&remote_caps);
+ check_and_write_capabilities(&remote_caps, argv, nr_caps);
+ fprintf(logfile, "init handshake complete\n");
+ strset_clear(&remote_caps);
+
+ command_loop();
+
+ if (fclose(logfile))
+ die_errno("error closing logfile");
+ free_delay_entries();
+ return 0;
+}
diff --git a/t/helper/test-run-command.c b/t/helper/test-run-command.c
index 7ae03dc..c0ed872 100644
--- a/t/helper/test-run-command.c
+++ b/t/helper/test-run-command.c
@@ -9,8 +9,6 @@
*/
#include "test-tool.h"
-#include "git-compat-util.h"
-#include "cache.h"
#include "run-command.h"
#include "strvec.h"
#include "strbuf.h"
@@ -18,40 +16,48 @@
#include "string-list.h"
#include "thread-utils.h"
#include "wildmatch.h"
-#include "gettext.h"
-#include "parse-options.h"
static int number_callbacks;
static int parallel_next(struct child_process *cp,
struct strbuf *err,
void *cb,
- void **task_cb)
+ void **task_cb UNUSED)
{
struct child_process *d = cb;
if (number_callbacks >= 4)
return 0;
- strvec_pushv(&cp->args, d->argv);
- strbuf_addstr(err, "preloaded output of a child\n");
+ strvec_pushv(&cp->args, d->args.v);
+ if (err)
+ strbuf_addstr(err, "preloaded output of a child\n");
+ else
+ fprintf(stderr, "preloaded output of a child\n");
+
number_callbacks++;
return 1;
}
-static int no_job(struct child_process *cp,
+static int no_job(struct child_process *cp UNUSED,
struct strbuf *err,
- void *cb,
- void **task_cb)
+ void *cb UNUSED,
+ void **task_cb UNUSED)
{
- strbuf_addstr(err, "no further jobs available\n");
+ if (err)
+ strbuf_addstr(err, "no further jobs available\n");
+ else
+ fprintf(stderr, "no further jobs available\n");
return 0;
}
-static int task_finished(int result,
+static int task_finished(int result UNUSED,
struct strbuf *err,
- void *pp_cb,
- void *pp_task_cb)
+ void *pp_cb UNUSED,
+ void *pp_task_cb UNUSED)
{
- strbuf_addstr(err, "asking for a quick stop\n");
+ if (err)
+ strbuf_addstr(err, "asking for a quick stop\n");
+ else
+ fprintf(stderr, "asking for a quick stop\n");
return 1;
}
@@ -60,8 +66,10 @@ struct testsuite {
int next;
int quiet, immediate, verbose, verbose_log, trace, write_junit_xml;
};
-#define TESTSUITE_INIT \
- { STRING_LIST_INIT_DUP, STRING_LIST_INIT_DUP, -1, 0, 0, 0, 0, 0, 0 }
+#define TESTSUITE_INIT { \
+ .tests = STRING_LIST_INIT_DUP, \
+ .failed = STRING_LIST_INIT_DUP, \
+}
static int next_test(struct child_process *cp, struct strbuf *err, void *cb,
void **task_cb)
@@ -125,7 +133,7 @@ static const char * const testsuite_usage[] = {
static int testsuite(int argc, const char **argv)
{
struct testsuite suite = TESTSUITE_INIT;
- int max_jobs = 1, i, ret;
+ int max_jobs = 1, i, ret = 0;
DIR *dir;
struct dirent *d;
struct option options[] = {
@@ -141,9 +149,12 @@ static int testsuite(int argc, const char **argv)
"write JUnit-style XML files"),
OPT_END()
};
-
- memset(&suite, 0, sizeof(suite));
- suite.tests.strdup_strings = suite.failed.strdup_strings = 1;
+ struct run_process_parallel_opts opts = {
+ .get_next_task = next_test,
+ .start_failure = test_failed,
+ .task_finished = test_finished,
+ .data = &suite,
+ };
argc = parse_options(argc, argv, NULL, options,
testsuite_usage, PARSE_OPT_STOP_AT_NON_OPTION);
@@ -181,15 +192,16 @@ static int testsuite(int argc, const char **argv)
if (max_jobs > suite.tests.nr)
max_jobs = suite.tests.nr;
- fprintf(stderr, "Running %d tests (%d at a time)\n",
- suite.tests.nr, max_jobs);
+ fprintf(stderr, "Running %"PRIuMAX" tests (%d at a time)\n",
+ (uintmax_t)suite.tests.nr, max_jobs);
- ret = run_processes_parallel(max_jobs, next_test, test_failed,
- test_finished, &suite);
+ opts.processes = max_jobs;
+ run_processes_parallel(&opts);
if (suite.failed.nr > 0) {
ret = 1;
- fprintf(stderr, "%d tests failed:\n\n", suite.failed.nr);
+ fprintf(stderr, "%"PRIuMAX" tests failed:\n\n",
+ (uintmax_t)suite.failed.nr);
for (i = 0; i < suite.failed.nr; i++)
fprintf(stderr, "\t%s\n", suite.failed.items[i].string);
}
@@ -197,7 +209,7 @@ static int testsuite(int argc, const char **argv)
string_list_clear(&suite.tests, 0);
string_list_clear(&suite.failed, 0);
- return !!ret;
+ return ret;
}
static uint64_t my_random_next = 1234;
@@ -222,9 +234,9 @@ static int quote_stress_test(int argc, const char **argv)
struct strbuf out = STRBUF_INIT;
struct strvec args = STRVEC_INIT;
struct option options[] = {
- OPT_INTEGER('n', "trials", &trials, "Number of trials"),
- OPT_INTEGER('s', "skip", &skip, "Skip <n> trials"),
- OPT_BOOL('m', "msys2", &msys2, "Test quoting for MSYS2's sh"),
+ OPT_INTEGER('n', "trials", &trials, "number of trials"),
+ OPT_INTEGER('s', "skip", &skip, "skip <n> trials"),
+ OPT_BOOL('m', "msys2", &msys2, "test quoting for MSYS2's sh"),
OPT_END()
};
const char * const usage[] = {
@@ -275,7 +287,7 @@ static int quote_stress_test(int argc, const char **argv)
if (i < skip)
continue;
- cp.argv = args.v;
+ strvec_pushv(&cp.args, args.v);
strbuf_reset(&out);
if (pipe_command(&cp, NULL, 0, &out, 0, NULL, 0) < 0)
return error("Failed to spawn child process");
@@ -372,13 +384,17 @@ int cmd__run_command(int argc, const char **argv)
{
struct child_process proc = CHILD_PROCESS_INIT;
int jobs;
+ int ret;
+ struct run_process_parallel_opts opts = {
+ .data = &proc,
+ };
if (argc > 1 && !strcmp(argv[1], "testsuite"))
- exit(testsuite(argc - 1, argv + 1));
+ return testsuite(argc - 1, argv + 1);
if (!strcmp(argv[1], "inherited-handle"))
- exit(inherit_handle(argv[0]));
+ return inherit_handle(argv[0]);
if (!strcmp(argv[1], "inherited-handle-child"))
- exit(inherit_handle_child());
+ return inherit_handle_child();
if (argc >= 2 && !strcmp(argv[1], "quote-stress-test"))
return !!quote_stress_test(argc - 1, argv + 1);
@@ -391,38 +407,56 @@ int cmd__run_command(int argc, const char **argv)
while (!strcmp(argv[1], "env")) {
if (!argv[2])
die("env specifier without a value");
- strvec_push(&proc.env_array, argv[2]);
+ strvec_push(&proc.env, argv[2]);
argv += 2;
argc -= 2;
}
- if (argc < 3)
- return 1;
- proc.argv = (const char **)argv + 2;
+ if (argc < 3) {
+ ret = 1;
+ goto cleanup;
+ }
+ strvec_pushv(&proc.args, (const char **)argv + 2);
if (!strcmp(argv[1], "start-command-ENOENT")) {
- if (start_command(&proc) < 0 && errno == ENOENT)
- return 0;
+ if (start_command(&proc) < 0 && errno == ENOENT) {
+ ret = 0;
+ goto cleanup;
+ }
fprintf(stderr, "FAIL %s\n", argv[1]);
return 1;
}
- if (!strcmp(argv[1], "run-command"))
- exit(run_command(&proc));
-
- jobs = atoi(argv[2]);
- proc.argv = (const char **)argv + 3;
-
- if (!strcmp(argv[1], "run-command-parallel"))
- exit(run_processes_parallel(jobs, parallel_next,
- NULL, NULL, &proc));
-
- if (!strcmp(argv[1], "run-command-abort"))
- exit(run_processes_parallel(jobs, parallel_next,
- NULL, task_finished, &proc));
+ if (!strcmp(argv[1], "run-command")) {
+ ret = run_command(&proc);
+ goto cleanup;
+ }
- if (!strcmp(argv[1], "run-command-no-jobs"))
- exit(run_processes_parallel(jobs, no_job,
- NULL, task_finished, &proc));
+ if (!strcmp(argv[1], "--ungroup")) {
+ argv += 1;
+ argc -= 1;
+ opts.ungroup = 1;
+ }
- fprintf(stderr, "check usage\n");
- return 1;
+ jobs = atoi(argv[2]);
+ strvec_clear(&proc.args);
+ strvec_pushv(&proc.args, (const char **)argv + 3);
+
+ if (!strcmp(argv[1], "run-command-parallel")) {
+ opts.get_next_task = parallel_next;
+ } else if (!strcmp(argv[1], "run-command-abort")) {
+ opts.get_next_task = parallel_next;
+ opts.task_finished = task_finished;
+ } else if (!strcmp(argv[1], "run-command-no-jobs")) {
+ opts.get_next_task = no_job;
+ opts.task_finished = task_finished;
+ } else {
+ ret = 1;
+ fprintf(stderr, "check usage\n");
+ goto cleanup;
+ }
+ opts.processes = jobs;
+ run_processes_parallel(&opts);
+ ret = 0;
+cleanup:
+ child_process_clear(&proc);
+ return ret;
}
diff --git a/t/helper/test-scrap-cache-tree.c b/t/helper/test-scrap-cache-tree.c
index 393f160..0a816a9 100644
--- a/t/helper/test-scrap-cache-tree.c
+++ b/t/helper/test-scrap-cache-tree.c
@@ -1,18 +1,22 @@
+#define USE_THE_INDEX_VARIABLE
#include "test-tool.h"
-#include "cache.h"
#include "lockfile.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
#include "tree.h"
#include "cache-tree.h"
-int cmd__scrap_cache_tree(int ac, const char **av)
+int cmd__scrap_cache_tree(int ac UNUSED, const char **av UNUSED)
{
struct lock_file index_lock = LOCK_INIT;
setup_git_directory();
- hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
- if (read_cache() < 0)
+ repo_hold_locked_index(the_repository, &index_lock, LOCK_DIE_ON_ERROR);
+ if (repo_read_index(the_repository) < 0)
die("unable to read index file");
- active_cache_tree = NULL;
+ cache_tree_free(&the_index.cache_tree);
+ the_index.cache_tree = NULL;
if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
die("unable to write index file");
return 0;
diff --git a/t/helper/test-serve-v2.c b/t/helper/test-serve-v2.c
index aee35e5..054cbcf 100644
--- a/t/helper/test-serve-v2.c
+++ b/t/helper/test-serve-v2.c
@@ -1,7 +1,8 @@
#include "test-tool.h"
-#include "cache.h"
+#include "gettext.h"
#include "parse-options.h"
#include "serve.h"
+#include "setup.h"
static char const * const serve_usage[] = {
N_("test-tool serve-v2 [<options>]"),
@@ -10,12 +11,12 @@ static char const * const serve_usage[] = {
int cmd__serve_v2(int argc, const char **argv)
{
- struct serve_options opts = SERVE_OPTIONS_INIT;
-
+ int stateless_rpc = 0;
+ int advertise_capabilities = 0;
struct option options[] = {
- OPT_BOOL(0, "stateless-rpc", &opts.stateless_rpc,
+ OPT_BOOL(0, "stateless-rpc", &stateless_rpc,
N_("quit after a single request/response exchange")),
- OPT_BOOL(0, "advertise-capabilities", &opts.advertise_capabilities,
+ OPT_BOOL(0, "advertise-capabilities", &advertise_capabilities,
N_("exit immediately after advertising capabilities")),
OPT_END()
};
@@ -24,8 +25,12 @@ int cmd__serve_v2(int argc, const char **argv)
/* ignore all unknown cmdline switches for now */
argc = parse_options(argc, argv, prefix, options, serve_usage,
PARSE_OPT_KEEP_DASHDASH |
- PARSE_OPT_KEEP_UNKNOWN);
- serve(&opts);
+ PARSE_OPT_KEEP_UNKNOWN_OPT);
+
+ if (advertise_capabilities)
+ protocol_v2_advertise_capabilities();
+ else
+ protocol_v2_serve_loop(stateless_rpc);
return 0;
}
diff --git a/t/helper/test-sha1.c b/t/helper/test-sha1.c
index d860c38..dcb7f6c 100644
--- a/t/helper/test-sha1.c
+++ b/t/helper/test-sha1.c
@@ -1,7 +1,15 @@
#include "test-tool.h"
-#include "cache.h"
+#include "hash-ll.h"
int cmd__sha1(int ac, const char **av)
{
return cmd_hash_impl(ac, av, GIT_HASH_SHA1);
}
+
+int cmd__sha1_is_sha1dc(int argc UNUSED, const char **argv UNUSED)
+{
+#ifdef platform_SHA_IS_SHA1DC
+ return 0;
+#endif
+ return 1;
+}
diff --git a/t/helper/test-sha256.c b/t/helper/test-sha256.c
index 0ac6a99..08cf149 100644
--- a/t/helper/test-sha256.c
+++ b/t/helper/test-sha256.c
@@ -1,5 +1,5 @@
#include "test-tool.h"
-#include "cache.h"
+#include "hash-ll.h"
int cmd__sha256(int ac, const char **av)
{
diff --git a/t/helper/test-sigchain.c b/t/helper/test-sigchain.c
index d013bcc..2d5ecf7 100644
--- a/t/helper/test-sigchain.c
+++ b/t/helper/test-sigchain.c
@@ -1,5 +1,4 @@
#include "test-tool.h"
-#include "cache.h"
#include "sigchain.h"
#define X(f) \
@@ -14,7 +13,7 @@ X(two)
X(three)
#undef X
-int cmd__sigchain(int argc, const char **argv)
+int cmd__sigchain(int argc UNUSED, const char **argv UNUSED)
{
sigchain_push(SIGTERM, one);
sigchain_push(SIGTERM, two);
diff --git a/t/helper/test-simple-ipc.c b/t/helper/test-simple-ipc.c
index 42040ef..fb59277 100644
--- a/t/helper/test-simple-ipc.c
+++ b/t/helper/test-simple-ipc.c
@@ -3,12 +3,13 @@
*/
#include "test-tool.h"
-#include "cache.h"
-#include "strbuf.h"
+#include "gettext.h"
#include "simple-ipc.h"
#include "parse-options.h"
#include "thread-utils.h"
#include "strvec.h"
+#include "run-command.h"
+#include "trace2.h"
#ifndef SUPPORTS_SIMPLE_IPC
int cmd__simple_ipc(int argc, const char **argv)
@@ -112,7 +113,7 @@ static int app__slow_command(ipc_server_reply_cb *reply_cb,
/*
* The client sent a command followed by a (possibly very) large buffer.
*/
-static int app__sendbytes_command(const char *received,
+static int app__sendbytes_command(const char *received, size_t received_len,
ipc_server_reply_cb *reply_cb,
struct ipc_server_reply_data *reply_data)
{
@@ -123,6 +124,13 @@ static int app__sendbytes_command(const char *received,
int errs = 0;
int ret;
+ /*
+ * The test is setup to send:
+ * "sendbytes" SP <n * char>
+ */
+ if (received_len < strlen("sendbytes "))
+ BUG("received_len is short in app__sendbytes_command");
+
if (skip_prefix(received, "sendbytes ", &p))
len_ballast = strlen(p);
@@ -160,7 +168,7 @@ static ipc_server_application_cb test_app_cb;
* by this application.
*/
static int test_app_cb(void *application_data,
- const char *command,
+ const char *command, size_t command_len,
ipc_server_reply_cb *reply_cb,
struct ipc_server_reply_data *reply_data)
{
@@ -173,7 +181,7 @@ static int test_app_cb(void *application_data,
if (application_data != (void*)&my_app_data)
BUG("application_cb: application_data pointer wrong");
- if (!strcmp(command, "quit")) {
+ if (command_len == 4 && !strncmp(command, "quit", 4)) {
/*
* The client sent a "quit" command. This is an async
* request for the server to shutdown.
@@ -193,22 +201,23 @@ static int test_app_cb(void *application_data,
return SIMPLE_IPC_QUIT;
}
- if (!strcmp(command, "ping")) {
+ if (command_len == 4 && !strncmp(command, "ping", 4)) {
const char *answer = "pong";
return reply_cb(reply_data, answer, strlen(answer));
}
- if (!strcmp(command, "big"))
+ if (command_len == 3 && !strncmp(command, "big", 3))
return app__big_command(reply_cb, reply_data);
- if (!strcmp(command, "chunk"))
+ if (command_len == 5 && !strncmp(command, "chunk", 5))
return app__chunk_command(reply_cb, reply_data);
- if (!strcmp(command, "slow"))
+ if (command_len == 4 && !strncmp(command, "slow", 4))
return app__slow_command(reply_cb, reply_data);
- if (starts_with(command, "sendbytes "))
- return app__sendbytes_command(command, reply_cb, reply_data);
+ if (command_len >= 10 && starts_with(command, "sendbytes "))
+ return app__sendbytes_command(command, command_len,
+ reply_cb, reply_data);
return app__unhandled_command(command, reply_cb, reply_data);
}
@@ -259,186 +268,73 @@ static int daemon__run_server(void)
*/
ret = ipc_server_run(cl_args.path, &opts, test_app_cb, (void*)&my_app_data);
if (ret == -2)
- error(_("socket/pipe already in use: '%s'"), cl_args.path);
+ error("socket/pipe already in use: '%s'", cl_args.path);
else if (ret == -1)
- error_errno(_("could not start server on: '%s'"), cl_args.path);
+ error_errno("could not start server on: '%s'", cl_args.path);
return ret;
}
-#ifndef GIT_WINDOWS_NATIVE
-/*
- * This is adapted from `daemonize()`. Use `fork()` to directly create and
- * run the daemon in a child process.
- */
-static int spawn_server(pid_t *pid)
-{
- struct ipc_server_opts opts = {
- .nr_threads = cl_args.nr_threads,
- };
-
- *pid = fork();
+static start_bg_wait_cb bg_wait_cb;
- switch (*pid) {
- case 0:
- if (setsid() == -1)
- error_errno(_("setsid failed"));
- close(0);
- close(1);
- close(2);
- sanitize_stdfds();
+static int bg_wait_cb(const struct child_process *cp UNUSED,
+ void *cb_data UNUSED)
+{
+ int s = ipc_get_active_state(cl_args.path);
- return ipc_server_run(cl_args.path, &opts, test_app_cb,
- (void*)&my_app_data);
+ switch (s) {
+ case IPC_STATE__LISTENING:
+ /* child is "ready" */
+ return 0;
- case -1:
- return error_errno(_("could not spawn daemon in the background"));
+ case IPC_STATE__NOT_LISTENING:
+ case IPC_STATE__PATH_NOT_FOUND:
+ /* give child more time */
+ return 1;
default:
- return 0;
+ case IPC_STATE__INVALID_PATH:
+ case IPC_STATE__OTHER_ERROR:
+ /* all the time in world won't help */
+ return -1;
}
}
-#else
-/*
- * Conceptually like `daemonize()` but different because Windows does not
- * have `fork(2)`. Spawn a normal Windows child process but without the
- * limitations of `start_command()` and `finish_command()`.
- */
-static int spawn_server(pid_t *pid)
-{
- char test_tool_exe[MAX_PATH];
- struct strvec args = STRVEC_INIT;
- int in, out;
-
- GetModuleFileNameA(NULL, test_tool_exe, MAX_PATH);
-
- in = open("/dev/null", O_RDONLY);
- out = open("/dev/null", O_WRONLY);
-
- strvec_push(&args, test_tool_exe);
- strvec_push(&args, "simple-ipc");
- strvec_push(&args, "run-daemon");
- strvec_pushf(&args, "--name=%s", cl_args.path);
- strvec_pushf(&args, "--threads=%d", cl_args.nr_threads);
- *pid = mingw_spawnvpe(args.v[0], args.v, NULL, NULL, in, out, out);
- close(in);
- close(out);
-
- strvec_clear(&args);
-
- if (*pid < 0)
- return error(_("could not spawn daemon in the background"));
-
- return 0;
-}
-#endif
-
-/*
- * This is adapted from `wait_or_whine()`. Watch the child process and
- * let it get started and begin listening for requests on the socket
- * before reporting our success.
- */
-static int wait_for_server_startup(pid_t pid_child)
+static int daemon__start_server(void)
{
- int status;
- pid_t pid_seen;
- enum ipc_active_state s;
- time_t time_limit, now;
+ struct child_process cp = CHILD_PROCESS_INIT;
+ enum start_bg_result sbgr;
- time(&time_limit);
- time_limit += cl_args.max_wait_sec;
+ strvec_push(&cp.args, "test-tool");
+ strvec_push(&cp.args, "simple-ipc");
+ strvec_push(&cp.args, "run-daemon");
+ strvec_pushf(&cp.args, "--name=%s", cl_args.path);
+ strvec_pushf(&cp.args, "--threads=%d", cl_args.nr_threads);
- for (;;) {
- pid_seen = waitpid(pid_child, &status, WNOHANG);
-
- if (pid_seen == -1)
- return error_errno(_("waitpid failed"));
-
- else if (pid_seen == 0) {
- /*
- * The child is still running (this should be
- * the normal case). Try to connect to it on
- * the socket and see if it is ready for
- * business.
- *
- * If there is another daemon already running,
- * our child will fail to start (possibly
- * after a timeout on the lock), but we don't
- * care (who responds) if the socket is live.
- */
- s = ipc_get_active_state(cl_args.path);
- if (s == IPC_STATE__LISTENING)
- return 0;
+ cp.no_stdin = 1;
+ cp.no_stdout = 1;
+ cp.no_stderr = 1;
- time(&now);
- if (now > time_limit)
- return error(_("daemon not online yet"));
+ sbgr = start_bg_command(&cp, bg_wait_cb, NULL, cl_args.max_wait_sec);
- continue;
- }
+ switch (sbgr) {
+ case SBGR_READY:
+ return 0;
- else if (pid_seen == pid_child) {
- /*
- * The new child daemon process shutdown while
- * it was starting up, so it is not listening
- * on the socket.
- *
- * Try to ping the socket in the odd chance
- * that another daemon started (or was already
- * running) while our child was starting.
- *
- * Again, we don't care who services the socket.
- */
- s = ipc_get_active_state(cl_args.path);
- if (s == IPC_STATE__LISTENING)
- return 0;
+ default:
+ case SBGR_ERROR:
+ case SBGR_CB_ERROR:
+ return error("daemon failed to start");
- /*
- * We don't care about the WEXITSTATUS() nor
- * any of the WIF*(status) values because
- * `cmd__simple_ipc()` does the `!!result`
- * trick on all function return values.
- *
- * So it is sufficient to just report the
- * early shutdown as an error.
- */
- return error(_("daemon failed to start"));
- }
+ case SBGR_TIMEOUT:
+ return error("daemon not online yet");
- else
- return error(_("waitpid is confused"));
+ case SBGR_DIED:
+ return error("daemon terminated");
}
}
/*
- * This process will start a simple-ipc server in a background process and
- * wait for it to become ready. This is like `daemonize()` but gives us
- * more control and better error reporting (and makes it easier to write
- * unit tests).
- */
-static int daemon__start_server(void)
-{
- pid_t pid_child;
- int ret;
-
- /*
- * Run the actual daemon in a background process.
- */
- ret = spawn_server(&pid_child);
- if (pid_child <= 0)
- return ret;
-
- /*
- * Let the parent wait for the child process to get started
- * and begin listening for requests on the socket.
- */
- ret = wait_for_server_startup(pid_child);
-
- return ret;
-}
-
-/*
* This process will run a quick probe to see if a simple-ipc server
* is active on this path.
*
@@ -488,7 +384,9 @@ static int client__send_ipc(void)
options.wait_if_busy = 1;
options.wait_if_not_found = 0;
- if (!ipc_client_send_command(cl_args.path, &options, command, &buf)) {
+ if (!ipc_client_send_command(cl_args.path, &options,
+ command, strlen(command),
+ &buf)) {
if (buf.len) {
printf("%s\n", buf.buf);
fflush(stdout);
@@ -538,7 +436,7 @@ static int client__stop_server(void)
time(&now);
if (now > time_limit)
- return error(_("daemon has not shutdown yet"));
+ return error("daemon has not shutdown yet");
}
}
@@ -556,7 +454,9 @@ static int do_sendbytes(int bytecount, char byte, const char *path,
strbuf_addstr(&buf_send, "sendbytes ");
strbuf_addchars(&buf_send, byte, bytecount);
- if (!ipc_client_send_command(path, options, buf_send.buf, &buf_resp)) {
+ if (!ipc_client_send_command(path, options,
+ buf_send.buf, buf_send.len,
+ &buf_resp)) {
strbuf_rtrim(&buf_resp);
printf("sent:%c%08d %s\n", byte, bytecount, buf_resp.buf);
fflush(stdout);
diff --git a/t/helper/test-strcmp-offset.c b/t/helper/test-strcmp-offset.c
index 44e4a6d..d8473cf 100644
--- a/t/helper/test-strcmp-offset.c
+++ b/t/helper/test-strcmp-offset.c
@@ -1,7 +1,7 @@
#include "test-tool.h"
-#include "cache.h"
+#include "read-cache-ll.h"
-int cmd__strcmp_offset(int argc, const char **argv)
+int cmd__strcmp_offset(int argc UNUSED, const char **argv)
{
int result;
size_t offset;
diff --git a/t/helper/test-string-list.c b/t/helper/test-string-list.c
index 2123dda..e2aad61 100644
--- a/t/helper/test-string-list.c
+++ b/t/helper/test-string-list.c
@@ -1,5 +1,5 @@
#include "test-tool.h"
-#include "cache.h"
+#include "strbuf.h"
#include "string-list.h"
/*
@@ -62,7 +62,7 @@ int cmd__string_list(int argc, const char **argv)
struct string_list list = STRING_LIST_INIT_NODUP;
int i;
char *s = xstrdup(argv[2]);
- int delim = *argv[3];
+ const char *delim = argv[3];
int maxsplit = atoi(argv[4]);
i = string_list_split_in_place(&list, s, delim, maxsplit);
@@ -111,7 +111,7 @@ int cmd__string_list(int argc, const char **argv)
*/
if (sb.len && sb.buf[sb.len - 1] == '\n')
strbuf_setlen(&sb, sb.len - 1);
- string_list_split_in_place(&list, sb.buf, '\n', -1);
+ string_list_split_in_place(&list, sb.buf, "\n", -1);
string_list_sort(&list);
diff --git a/t/helper/test-submodule-config.c b/t/helper/test-submodule-config.c
index e269274..9df2f03 100644
--- a/t/helper/test-submodule-config.c
+++ b/t/helper/test-submodule-config.c
@@ -1,10 +1,13 @@
#include "test-tool.h"
-#include "cache.h"
#include "config.h"
+#include "hash.h"
+#include "object-name.h"
+#include "repository.h"
+#include "setup.h"
#include "submodule-config.h"
#include "submodule.h"
-static void die_usage(int argc, const char **argv, const char *msg)
+static void die_usage(int argc UNUSED, const char **argv, const char *msg)
{
fprintf(stderr, "%s\n", msg);
fprintf(stderr, "Usage: %s [<commit> <submodulepath>] ...\n", argv[0]);
@@ -15,14 +18,11 @@ int cmd__submodule_config(int argc, const char **argv)
{
const char **arg = argv;
int my_argc = argc;
- int output_url = 0;
int lookup_name = 0;
arg++;
my_argc--;
while (arg[0] && starts_with(arg[0], "--")) {
- if (!strcmp(arg[0], "--url"))
- output_url = 1;
if (!strcmp(arg[0], "--name"))
lookup_name = 1;
arg++;
@@ -45,7 +45,7 @@ int cmd__submodule_config(int argc, const char **argv)
if (commit[0] == '\0')
oidclr(&commit_oid);
- else if (get_oid(commit, &commit_oid) < 0)
+ else if (repo_get_oid(the_repository, commit, &commit_oid) < 0)
die_usage(argc, argv, "Commit not found.");
if (lookup_name) {
@@ -57,12 +57,8 @@ int cmd__submodule_config(int argc, const char **argv)
if (!submodule)
die_usage(argc, argv, "Submodule not found.");
- if (output_url)
- printf("Submodule url: '%s' for path '%s'\n",
- submodule->url, submodule->path);
- else
- printf("Submodule name: '%s' for path '%s'\n",
- submodule->name, submodule->path);
+ printf("Submodule name: '%s' for path '%s'\n", submodule->name,
+ submodule->path);
arg += 2;
}
diff --git a/t/helper/test-submodule-nested-repo-config.c b/t/helper/test-submodule-nested-repo-config.c
index e3f11ff..ecd40de 100644
--- a/t/helper/test-submodule-nested-repo-config.c
+++ b/t/helper/test-submodule-nested-repo-config.c
@@ -1,4 +1,6 @@
#include "test-tool.h"
+#include "repository.h"
+#include "setup.h"
#include "submodule-config.h"
static void die_usage(const char **argv, const char *msg)
@@ -11,15 +13,13 @@ static void die_usage(const char **argv, const char *msg)
int cmd__submodule_nested_repo_config(int argc, const char **argv)
{
struct repository subrepo;
- const struct submodule *sub;
if (argc < 3)
die_usage(argv, "Wrong number of arguments.");
setup_git_directory();
- sub = submodule_from_path(the_repository, null_oid(), argv[1]);
- if (repo_submodule_init(&subrepo, the_repository, sub)) {
+ if (repo_submodule_init(&subrepo, the_repository, argv[1], null_oid())) {
die_usage(argv, "Submodule not found.");
}
diff --git a/t/helper/test-submodule.c b/t/helper/test-submodule.c
new file mode 100644
index 0000000..7197969
--- /dev/null
+++ b/t/helper/test-submodule.c
@@ -0,0 +1,242 @@
+#include "test-tool.h"
+#include "test-tool-utils.h"
+#include "parse-options.h"
+#include "remote.h"
+#include "repository.h"
+#include "setup.h"
+#include "strbuf.h"
+#include "submodule-config.h"
+#include "submodule.h"
+
+#define TEST_TOOL_CHECK_NAME_USAGE \
+ "test-tool submodule check-name"
+static const char *submodule_check_name_usage[] = {
+ TEST_TOOL_CHECK_NAME_USAGE,
+ NULL
+};
+
+#define TEST_TOOL_CHECK_URL_USAGE \
+ "test-tool submodule check-url"
+static const char *submodule_check_url_usage[] = {
+ TEST_TOOL_CHECK_URL_USAGE,
+ NULL
+};
+
+#define TEST_TOOL_IS_ACTIVE_USAGE \
+ "test-tool submodule is-active <name>"
+static const char *submodule_is_active_usage[] = {
+ TEST_TOOL_IS_ACTIVE_USAGE,
+ NULL
+};
+
+#define TEST_TOOL_RESOLVE_RELATIVE_URL_USAGE \
+ "test-tool submodule resolve-relative-url <up_path> <remoteurl> <url>"
+static const char *submodule_resolve_relative_url_usage[] = {
+ TEST_TOOL_RESOLVE_RELATIVE_URL_USAGE,
+ NULL,
+};
+
+static const char *submodule_usage[] = {
+ TEST_TOOL_CHECK_NAME_USAGE,
+ TEST_TOOL_CHECK_URL_USAGE,
+ TEST_TOOL_IS_ACTIVE_USAGE,
+ TEST_TOOL_RESOLVE_RELATIVE_URL_USAGE,
+ NULL
+};
+
+typedef int (*check_fn_t)(const char *);
+
+/*
+ * Apply 'check_fn' to each line of stdin, printing values that pass the check
+ * to stdout.
+ */
+static int check_submodule(check_fn_t check_fn)
+{
+ struct strbuf buf = STRBUF_INIT;
+ while (strbuf_getline(&buf, stdin) != EOF) {
+ if (!check_fn(buf.buf))
+ printf("%s\n", buf.buf);
+ }
+ strbuf_release(&buf);
+ return 0;
+}
+
+static int cmd__submodule_check_name(int argc, const char **argv)
+{
+ struct option options[] = {
+ OPT_END()
+ };
+ argc = parse_options(argc, argv, "test-tools", options,
+ submodule_check_name_usage, 0);
+ if (argc)
+ usage_with_options(submodule_check_name_usage, options);
+
+ return check_submodule(check_submodule_name);
+}
+
+static int cmd__submodule_check_url(int argc, const char **argv)
+{
+ struct option options[] = {
+ OPT_END()
+ };
+ argc = parse_options(argc, argv, "test-tools", options,
+ submodule_check_url_usage, 0);
+ if (argc)
+ usage_with_options(submodule_check_url_usage, options);
+
+ return check_submodule(check_submodule_url);
+}
+
+static int cmd__submodule_is_active(int argc, const char **argv)
+{
+ struct option options[] = {
+ OPT_END()
+ };
+ argc = parse_options(argc, argv, "test-tools", options,
+ submodule_is_active_usage, 0);
+ if (argc != 1)
+ usage_with_options(submodule_is_active_usage, options);
+
+ setup_git_directory();
+
+ return !is_submodule_active(the_repository, argv[0]);
+}
+
+static int cmd__submodule_resolve_relative_url(int argc, const char **argv)
+{
+ char *remoteurl, *res;
+ const char *up_path, *url;
+ struct option options[] = {
+ OPT_END()
+ };
+ argc = parse_options(argc, argv, "test-tools", options,
+ submodule_resolve_relative_url_usage, 0);
+ if (argc != 3)
+ usage_with_options(submodule_resolve_relative_url_usage, options);
+
+ up_path = argv[0];
+ remoteurl = xstrdup(argv[1]);
+ url = argv[2];
+
+ if (!strcmp(up_path, "(null)"))
+ up_path = NULL;
+
+ res = relative_url(remoteurl, url, up_path);
+ puts(res);
+ free(res);
+ free(remoteurl);
+ return 0;
+}
+
+static int cmd__submodule_config_list(int argc, const char **argv)
+{
+ struct option options[] = {
+ OPT_END()
+ };
+ const char *const usage[] = {
+ "test-tool submodule config-list <key>",
+ NULL
+ };
+ argc = parse_options(argc, argv, "test-tools", options, usage,
+ PARSE_OPT_KEEP_ARGV0);
+
+ setup_git_directory();
+
+ if (argc == 2)
+ return print_config_from_gitmodules(the_repository, argv[1]);
+ usage_with_options(usage, options);
+}
+
+static int cmd__submodule_config_set(int argc, const char **argv)
+{
+ struct option options[] = {
+ OPT_END()
+ };
+ const char *const usage[] = {
+ "test-tool submodule config-set <key> <value>",
+ NULL
+ };
+ argc = parse_options(argc, argv, "test-tools", options, usage,
+ PARSE_OPT_KEEP_ARGV0);
+
+ setup_git_directory();
+
+ /* Equivalent to ACTION_SET in builtin/config.c */
+ if (argc == 3) {
+ if (!is_writing_gitmodules_ok())
+ die("please make sure that the .gitmodules file is in the working tree");
+
+ return config_set_in_gitmodules_file_gently(argv[1], argv[2]);
+ }
+ usage_with_options(usage, options);
+}
+
+static int cmd__submodule_config_unset(int argc, const char **argv)
+{
+ struct option options[] = {
+ OPT_END()
+ };
+ const char *const usage[] = {
+ "test-tool submodule config-unset <key>",
+ NULL
+ };
+
+ setup_git_directory();
+
+ if (argc == 2) {
+ if (!is_writing_gitmodules_ok())
+ die("please make sure that the .gitmodules file is in the working tree");
+ return config_set_in_gitmodules_file_gently(argv[1], NULL);
+ }
+ usage_with_options(usage, options);
+}
+
+static int cmd__submodule_config_writeable(int argc, const char **argv UNUSED)
+{
+ struct option options[] = {
+ OPT_END()
+ };
+ const char *const usage[] = {
+ "test-tool submodule config-writeable",
+ NULL
+ };
+ setup_git_directory();
+
+ if (argc == 1)
+ return is_writing_gitmodules_ok() ? 0 : -1;
+
+ usage_with_options(usage, options);
+}
+
+static struct test_cmd cmds[] = {
+ { "check-name", cmd__submodule_check_name },
+ { "check-url", cmd__submodule_check_url },
+ { "is-active", cmd__submodule_is_active },
+ { "resolve-relative-url", cmd__submodule_resolve_relative_url},
+ { "config-list", cmd__submodule_config_list },
+ { "config-set", cmd__submodule_config_set },
+ { "config-unset", cmd__submodule_config_unset },
+ { "config-writeable", cmd__submodule_config_writeable },
+};
+
+int cmd__submodule(int argc, const char **argv)
+{
+ struct option options[] = {
+ OPT_END()
+ };
+ size_t i;
+
+ argc = parse_options(argc, argv, "test-tools", options, submodule_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+ if (argc < 1)
+ usage_with_options(submodule_usage, options);
+
+ for (i = 0; i < ARRAY_SIZE(cmds); i++)
+ if (!strcmp(cmds[i].name, argv[0]))
+ return cmds[i].fn(argc, argv);
+
+ usage_msg_optf("unknown subcommand '%s'", submodule_usage, options,
+ argv[0]);
+
+ return 0;
+}
diff --git a/t/helper/test-subprocess.c b/t/helper/test-subprocess.c
index 92b69de..c344f16 100644
--- a/t/helper/test-subprocess.c
+++ b/t/helper/test-subprocess.c
@@ -1,6 +1,6 @@
#include "test-tool.h"
-#include "cache.h"
#include "run-command.h"
+#include "setup.h"
int cmd__subprocess(int argc, const char **argv)
{
@@ -15,6 +15,6 @@ int cmd__subprocess(int argc, const char **argv)
argv++;
}
cp.git_cmd = 1;
- cp.argv = (const char **)argv + 1;
+ strvec_pushv(&cp.args, (const char **)argv + 1);
return run_command(&cp);
}
diff --git a/t/helper/test-tool-utils.h b/t/helper/test-tool-utils.h
new file mode 100644
index 0000000..6a0e5e0
--- /dev/null
+++ b/t/helper/test-tool-utils.h
@@ -0,0 +1,9 @@
+#ifndef TEST_TOOL_UTILS_H
+#define TEST_TOOL_UTILS_H
+
+struct test_cmd {
+ const char *name;
+ int (*fn)(int argc, const char **argv);
+};
+
+#endif
diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c
index 3ce5585..80a946b 100644
--- a/t/helper/test-tool.c
+++ b/t/helper/test-tool.c
@@ -1,5 +1,6 @@
#include "git-compat-util.h"
#include "test-tool.h"
+#include "test-tool-utils.h"
#include "trace2.h"
#include "parse-options.h"
@@ -8,20 +9,18 @@ static const char * const test_tool_usage[] = {
NULL
};
-struct test_cmd {
- const char *name;
- int (*fn)(int argc, const char **argv);
-};
-
static struct test_cmd cmds[] = {
{ "advise", cmd__advise_if_enabled },
{ "bitmap", cmd__bitmap },
{ "bloom", cmd__bloom },
+ { "bundle-uri", cmd__bundle_uri },
+ { "cache-tree", cmd__cache_tree },
{ "chmtime", cmd__chmtime },
{ "config", cmd__config },
{ "crontab", cmd__crontab },
- { "ctype", cmd__ctype },
+ { "csprng", cmd__csprng },
{ "date", cmd__date },
+ { "delete-gpgsig", cmd__delete_gpgsig },
{ "delta", cmd__delta },
{ "dir-iterator", cmd__dir_iterator },
{ "drop-caches", cmd__drop_caches },
@@ -29,14 +28,16 @@ static struct test_cmd cmds[] = {
{ "dump-fsmonitor", cmd__dump_fsmonitor },
{ "dump-split-index", cmd__dump_split_index },
{ "dump-untracked-cache", cmd__dump_untracked_cache },
+ { "env-helper", cmd__env_helper },
{ "example-decorate", cmd__example_decorate },
- { "fast-rebase", cmd__fast_rebase },
+ { "find-pack", cmd__find_pack },
+ { "fsmonitor-client", cmd__fsmonitor_client },
{ "genrandom", cmd__genrandom },
{ "genzeros", cmd__genzeros },
{ "getcwd", cmd__getcwd },
{ "hashmap", cmd__hashmap },
{ "hash-speed", cmd__hash_speed },
- { "index-version", cmd__index_version },
+ { "hexdump", cmd__hexdump },
{ "json-writer", cmd__json_writer },
{ "lazy-init-name-hash", cmd__lazy_init_name_hash },
{ "match-trees", cmd__match_trees },
@@ -46,20 +47,25 @@ static struct test_cmd cmds[] = {
{ "oidmap", cmd__oidmap },
{ "oidtree", cmd__oidtree },
{ "online-cpus", cmd__online_cpus },
+ { "pack-mtimes", cmd__pack_mtimes },
{ "parse-options", cmd__parse_options },
+ { "parse-options-flags", cmd__parse_options_flags },
{ "parse-pathspec-file", cmd__parse_pathspec_file },
+ { "parse-subcommand", cmd__parse_subcommand },
{ "partial-clone", cmd__partial_clone },
{ "path-utils", cmd__path_utils },
{ "pcre2-config", cmd__pcre2_config },
{ "pkt-line", cmd__pkt_line },
- { "prio-queue", cmd__prio_queue },
- { "proc-receive", cmd__proc_receive},
+ { "proc-receive", cmd__proc_receive },
{ "progress", cmd__progress },
{ "reach", cmd__reach },
{ "read-cache", cmd__read_cache },
{ "read-graph", cmd__read_graph },
{ "read-midx", cmd__read_midx },
{ "ref-store", cmd__ref_store },
+ { "reftable", cmd__reftable },
+ { "rot13-filter", cmd__rot13_filter },
+ { "dump-reftable", cmd__dump_reftable },
{ "regex", cmd__regex },
{ "repository", cmd__repository },
{ "revision-walking", cmd__revision_walking },
@@ -67,15 +73,18 @@ static struct test_cmd cmds[] = {
{ "scrap-cache-tree", cmd__scrap_cache_tree },
{ "serve-v2", cmd__serve_v2 },
{ "sha1", cmd__sha1 },
+ { "sha1-is-sha1dc", cmd__sha1_is_sha1dc },
{ "sha256", cmd__sha256 },
{ "sigchain", cmd__sigchain },
{ "simple-ipc", cmd__simple_ipc },
{ "strcmp-offset", cmd__strcmp_offset },
{ "string-list", cmd__string_list },
+ { "submodule", cmd__submodule },
{ "submodule-config", cmd__submodule_config },
{ "submodule-nested-repo-config", cmd__submodule_nested_repo_config },
{ "subprocess", cmd__subprocess },
{ "trace2", cmd__trace2 },
+ { "truncate", cmd__truncate },
{ "userdiff", cmd__userdiff },
{ "urlmatch-normalization", cmd__urlmatch_normalization },
{ "xml-encode", cmd__xml_encode },
diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h
index 9f0f522..2808b92 100644
--- a/t/helper/test-tool.h
+++ b/t/helper/test-tool.h
@@ -1,32 +1,37 @@
#ifndef TEST_TOOL_H
#define TEST_TOOL_H
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
#include "git-compat-util.h"
int cmd__advise_if_enabled(int argc, const char **argv);
int cmd__bitmap(int argc, const char **argv);
int cmd__bloom(int argc, const char **argv);
+int cmd__bundle_uri(int argc, const char **argv);
+int cmd__cache_tree(int argc, const char **argv);
int cmd__chmtime(int argc, const char **argv);
int cmd__config(int argc, const char **argv);
int cmd__crontab(int argc, const char **argv);
-int cmd__ctype(int argc, const char **argv);
+int cmd__csprng(int argc, const char **argv);
int cmd__date(int argc, const char **argv);
int cmd__delta(int argc, const char **argv);
+int cmd__delete_gpgsig(int argc, const char **argv);
int cmd__dir_iterator(int argc, const char **argv);
int cmd__drop_caches(int argc, const char **argv);
int cmd__dump_cache_tree(int argc, const char **argv);
int cmd__dump_fsmonitor(int argc, const char **argv);
int cmd__dump_split_index(int argc, const char **argv);
int cmd__dump_untracked_cache(int argc, const char **argv);
+int cmd__dump_reftable(int argc, const char **argv);
+int cmd__env_helper(int argc, const char **argv);
int cmd__example_decorate(int argc, const char **argv);
-int cmd__fast_rebase(int argc, const char **argv);
+int cmd__find_pack(int argc, const char **argv);
+int cmd__fsmonitor_client(int argc, const char **argv);
int cmd__genrandom(int argc, const char **argv);
int cmd__genzeros(int argc, const char **argv);
int cmd__getcwd(int argc, const char **argv);
int cmd__hashmap(int argc, const char **argv);
int cmd__hash_speed(int argc, const char **argv);
-int cmd__index_version(int argc, const char **argv);
+int cmd__hexdump(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);
@@ -35,13 +40,15 @@ int cmd__mktemp(int argc, const char **argv);
int cmd__oidmap(int argc, const char **argv);
int cmd__oidtree(int argc, const char **argv);
int cmd__online_cpus(int argc, const char **argv);
+int cmd__pack_mtimes(int argc, const char **argv);
int cmd__parse_options(int argc, const char **argv);
+int cmd__parse_options_flags(int argc, const char **argv);
int cmd__parse_pathspec_file(int argc, const char** argv);
+int cmd__parse_subcommand(int argc, const char **argv);
int cmd__partial_clone(int argc, const char **argv);
int cmd__path_utils(int argc, const char **argv);
int cmd__pcre2_config(int argc, const char **argv);
int cmd__pkt_line(int argc, const char **argv);
-int cmd__prio_queue(int argc, const char **argv);
int cmd__proc_receive(int argc, const char **argv);
int cmd__progress(int argc, const char **argv);
int cmd__reach(int argc, const char **argv);
@@ -49,6 +56,8 @@ int cmd__read_cache(int argc, const char **argv);
int cmd__read_graph(int argc, const char **argv);
int cmd__read_midx(int argc, const char **argv);
int cmd__ref_store(int argc, const char **argv);
+int cmd__rot13_filter(int argc, const char **argv);
+int cmd__reftable(int argc, const char **argv);
int cmd__regex(int argc, const char **argv);
int cmd__repository(int argc, const char **argv);
int cmd__revision_walking(int argc, const char **argv);
@@ -56,16 +65,19 @@ int cmd__run_command(int argc, const char **argv);
int cmd__scrap_cache_tree(int argc, const char **argv);
int cmd__serve_v2(int argc, const char **argv);
int cmd__sha1(int argc, const char **argv);
+int cmd__sha1_is_sha1dc(int argc, const char **argv);
int cmd__oid_array(int argc, const char **argv);
int cmd__sha256(int argc, const char **argv);
int cmd__sigchain(int argc, const char **argv);
int cmd__simple_ipc(int argc, const char **argv);
int cmd__strcmp_offset(int argc, const char **argv);
int cmd__string_list(int argc, const char **argv);
+int cmd__submodule(int argc, const char **argv);
int cmd__submodule_config(int argc, const char **argv);
int cmd__submodule_nested_repo_config(int argc, const char **argv);
int cmd__subprocess(int argc, const char **argv);
int cmd__trace2(int argc, const char **argv);
+int cmd__truncate(int argc, const char **argv);
int cmd__userdiff(int argc, const char **argv);
int cmd__urlmatch_normalization(int argc, const char **argv);
int cmd__xml_encode(int argc, const char **argv);
diff --git a/t/helper/test-trace2.c b/t/helper/test-trace2.c
index f93633f..1adac29 100644
--- a/t/helper/test-trace2.c
+++ b/t/helper/test-trace2.c
@@ -1,9 +1,10 @@
#include "test-tool.h"
-#include "cache.h"
#include "strvec.h"
#include "run-command.h"
#include "exec-cmd.h"
#include "config.h"
+#include "repository.h"
+#include "trace2.h"
typedef int(fn_unit_test)(int argc, const char **argv);
@@ -44,7 +45,7 @@ static int get_i(int *p_value, const char *data)
* [] "def_param" events for all of the "interesting" pre-defined
* config settings.
*/
-static int ut_001return(int argc, const char **argv)
+static int ut_001return(int argc UNUSED, const char **argv)
{
int rc;
@@ -64,7 +65,7 @@ static int ut_001return(int argc, const char **argv)
* [] "def_param" events for all of the "interesting" pre-defined
* config settings.
*/
-static int ut_002exit(int argc, const char **argv)
+static int ut_002exit(int argc UNUSED, const char **argv)
{
int rc;
@@ -132,6 +133,7 @@ static int ut_003error(int argc, const char **argv)
*/
static int ut_004child(int argc, const char **argv)
{
+ struct child_process cmd = CHILD_PROCESS_INIT;
int result;
/*
@@ -141,7 +143,8 @@ static int ut_004child(int argc, const char **argv)
if (!argc)
return 0;
- result = run_command_v_opt(argv, 0);
+ strvec_pushv(&cmd.args, argv);
+ result = run_command(&cmd);
exit(result);
}
@@ -198,7 +201,7 @@ static int ut_006data(int argc, const char **argv)
return 0;
}
-static int ut_007bug(int argc, const char **argv)
+static int ut_007BUG(int argc UNUSED, const char **argv UNUSED)
{
/*
* Exercise BUG() to ensure that the message is printed to trace2.
@@ -206,6 +209,259 @@ static int ut_007bug(int argc, const char **argv)
BUG("the bug message");
}
+static int ut_008bug(int argc UNUSED, const char **argv UNUSED)
+{
+ bug("a bug message");
+ bug("another bug message");
+ BUG_if_bug("an explicit BUG_if_bug() following bug() call(s) is nice, but not required");
+ return 0;
+}
+
+static int ut_009bug_BUG(int argc UNUSED, const char **argv UNUSED)
+{
+ bug("a bug message");
+ bug("another bug message");
+ /* The BUG_if_bug(...) isn't here, but we'll spot bug() calls on exit()! */
+ return 0;
+}
+
+static int ut_010bug_BUG(int argc UNUSED, const char **argv UNUSED)
+{
+ bug("a %s message", "bug");
+ BUG("a %s message", "BUG");
+}
+
+/*
+ * Single-threaded timer test. Create several intervals using the
+ * TEST1 timer. The test script can verify that an aggregate Trace2
+ * "timer" event is emitted indicating that we started+stopped the
+ * timer the requested number of times.
+ */
+static int ut_100timer(int argc, const char **argv)
+{
+ const char *usage_error =
+ "expect <count> <ms_delay>";
+
+ int count = 0;
+ int delay = 0;
+ int k;
+
+ if (argc != 2)
+ die("%s", usage_error);
+ if (get_i(&count, argv[0]))
+ die("%s", usage_error);
+ if (get_i(&delay, argv[1]))
+ die("%s", usage_error);
+
+ for (k = 0; k < count; k++) {
+ trace2_timer_start(TRACE2_TIMER_ID_TEST1);
+ sleep_millisec(delay);
+ trace2_timer_stop(TRACE2_TIMER_ID_TEST1);
+ }
+
+ return 0;
+}
+
+struct ut_101_data {
+ int count;
+ int delay;
+};
+
+static void *ut_101timer_thread_proc(void *_ut_101_data)
+{
+ struct ut_101_data *data = _ut_101_data;
+ int k;
+
+ trace2_thread_start("ut_101");
+
+ for (k = 0; k < data->count; k++) {
+ trace2_timer_start(TRACE2_TIMER_ID_TEST2);
+ sleep_millisec(data->delay);
+ trace2_timer_stop(TRACE2_TIMER_ID_TEST2);
+ }
+
+ trace2_thread_exit();
+ return NULL;
+}
+
+/*
+ * Multi-threaded timer test. Create several threads that each create
+ * several intervals using the TEST2 timer. The test script can verify
+ * that an individual Trace2 "th_timer" events for each thread and an
+ * aggregate "timer" event are generated.
+ */
+static int ut_101timer(int argc, const char **argv)
+{
+ const char *usage_error =
+ "expect <count> <ms_delay> <threads>";
+
+ struct ut_101_data data = { 0, 0 };
+ int nr_threads = 0;
+ int k;
+ pthread_t *pids = NULL;
+
+ if (argc != 3)
+ die("%s", usage_error);
+ if (get_i(&data.count, argv[0]))
+ die("%s", usage_error);
+ if (get_i(&data.delay, argv[1]))
+ die("%s", usage_error);
+ if (get_i(&nr_threads, argv[2]))
+ die("%s", usage_error);
+
+ CALLOC_ARRAY(pids, nr_threads);
+
+ for (k = 0; k < nr_threads; k++) {
+ if (pthread_create(&pids[k], NULL, ut_101timer_thread_proc, &data))
+ die("failed to create thread[%d]", k);
+ }
+
+ for (k = 0; k < nr_threads; k++) {
+ if (pthread_join(pids[k], NULL))
+ die("failed to join thread[%d]", k);
+ }
+
+ free(pids);
+
+ return 0;
+}
+
+/*
+ * Single-threaded counter test. Add several values to the TEST1 counter.
+ * The test script can verify that the final sum is reported in the "counter"
+ * event.
+ */
+static int ut_200counter(int argc, const char **argv)
+{
+ const char *usage_error =
+ "expect <v1> [<v2> [...]]";
+ int value;
+ int k;
+
+ if (argc < 1)
+ die("%s", usage_error);
+
+ for (k = 0; k < argc; k++) {
+ if (get_i(&value, argv[k]))
+ die("invalid value[%s] -- %s",
+ argv[k], usage_error);
+ trace2_counter_add(TRACE2_COUNTER_ID_TEST1, value);
+ }
+
+ return 0;
+}
+
+/*
+ * Multi-threaded counter test. Create seveal threads that each increment
+ * the TEST2 global counter. The test script can verify that an individual
+ * "th_counter" event is generated with a partial sum for each thread and
+ * that a final aggregate "counter" event is generated.
+ */
+
+struct ut_201_data {
+ int v1;
+ int v2;
+};
+
+static void *ut_201counter_thread_proc(void *_ut_201_data)
+{
+ struct ut_201_data *data = _ut_201_data;
+
+ trace2_thread_start("ut_201");
+
+ trace2_counter_add(TRACE2_COUNTER_ID_TEST2, data->v1);
+ trace2_counter_add(TRACE2_COUNTER_ID_TEST2, data->v2);
+
+ trace2_thread_exit();
+ return NULL;
+}
+
+static int ut_201counter(int argc, const char **argv)
+{
+ const char *usage_error =
+ "expect <v1> <v2> <threads>";
+
+ struct ut_201_data data = { 0, 0 };
+ int nr_threads = 0;
+ int k;
+ pthread_t *pids = NULL;
+
+ if (argc != 3)
+ die("%s", usage_error);
+ if (get_i(&data.v1, argv[0]))
+ die("%s", usage_error);
+ if (get_i(&data.v2, argv[1]))
+ die("%s", usage_error);
+ if (get_i(&nr_threads, argv[2]))
+ die("%s", usage_error);
+
+ CALLOC_ARRAY(pids, nr_threads);
+
+ for (k = 0; k < nr_threads; k++) {
+ if (pthread_create(&pids[k], NULL, ut_201counter_thread_proc, &data))
+ die("failed to create thread[%d]", k);
+ }
+
+ for (k = 0; k < nr_threads; k++) {
+ if (pthread_join(pids[k], NULL))
+ die("failed to join thread[%d]", k);
+ }
+
+ free(pids);
+
+ return 0;
+}
+
+static int ut_300redact_start(int argc, const char **argv)
+{
+ if (!argc)
+ die("expect <argv...>");
+
+ trace2_cmd_start(argv);
+
+ return 0;
+}
+
+static int ut_301redact_child_start(int argc, const char **argv)
+{
+ struct child_process cmd = CHILD_PROCESS_INIT;
+ int k;
+
+ if (!argc)
+ die("expect <argv...>");
+
+ for (k = 0; argv[k]; k++)
+ strvec_push(&cmd.args, argv[k]);
+
+ trace2_child_start(&cmd);
+
+ strvec_clear(&cmd.args);
+
+ return 0;
+}
+
+static int ut_302redact_exec(int argc, const char **argv)
+{
+ if (!argc)
+ die("expect <exe> <argv...>");
+
+ trace2_exec(argv[0], &argv[1]);
+
+ return 0;
+}
+
+static int ut_303redact_def_param(int argc, const char **argv)
+{
+ struct key_value_info kvi = KVI_INIT;
+
+ if (argc < 2)
+ die("expect <key> <value>");
+
+ trace2_def_param(argv[0], argv[1], &kvi);
+
+ return 0;
+}
+
/*
* Usage:
* test-tool trace2 <ut_name_1> <ut_usage_1>
@@ -222,7 +478,21 @@ static struct unit_test ut_table[] = {
{ ut_004child, "004child", "[<child_command_line>]" },
{ ut_005exec, "005exec", "<git_command_args>" },
{ ut_006data, "006data", "[<category> <key> <value>]+" },
- { ut_007bug, "007bug", "" },
+ { ut_007BUG, "007bug", "" },
+ { ut_008bug, "008bug", "" },
+ { ut_009bug_BUG, "009bug_BUG","" },
+ { ut_010bug_BUG, "010bug_BUG","" },
+
+ { ut_100timer, "100timer", "<count> <ms_delay>" },
+ { ut_101timer, "101timer", "<count> <ms_delay> <threads>" },
+
+ { ut_200counter, "200counter", "<v1> [<v2> [<v3> [...]]]" },
+ { ut_201counter, "201counter", "<v1> <v2> <threads>" },
+
+ { ut_300redact_start, "300redact_start", "<argv...>" },
+ { ut_301redact_child_start, "301redact_child_start", "<argv...>" },
+ { ut_302redact_exec, "302redact_exec", "<exe> <argv...>" },
+ { ut_303redact_def_param, "303redact_def_param", "<key> <value>" },
};
/* clang-format on */
@@ -262,8 +532,9 @@ static int print_usage(void)
* [] the "cmd_name" event has been generated.
* [] this writes various "def_param" events for interesting config values.
*
- * We further assume that if we return (rather than exit()), trace2_cmd_exit()
- * will be called by test-tool.c:cmd_main().
+ * We return from here and let test-tool.c::cmd_main() pass the exit
+ * code to common-main.c::main(), which will use it to call
+ * trace2_cmd_exit().
*/
int cmd__trace2(int argc, const char **argv)
{
diff --git a/t/helper/test-truncate.c b/t/helper/test-truncate.c
new file mode 100644
index 0000000..3931dea
--- /dev/null
+++ b/t/helper/test-truncate.c
@@ -0,0 +1,25 @@
+#include "test-tool.h"
+#include "git-compat-util.h"
+
+/*
+ * Truncate a file to the given size.
+ */
+int cmd__truncate(int argc, const char **argv)
+{
+ char *p = NULL;
+ uintmax_t sz = 0;
+ int fd = -1;
+
+ if (argc != 3)
+ die("expected filename and size");
+
+ sz = strtoumax(argv[2], &p, 0);
+ if (*p)
+ die("invalid size");
+
+ fd = xopen(argv[1], O_WRONLY | O_CREAT, 0600);
+
+ if (ftruncate(fd, (off_t) sz) < 0)
+ die_errno("failed to truncate file");
+ return 0;
+}
diff --git a/t/helper/test-urlmatch-normalization.c b/t/helper/test-urlmatch-normalization.c
index 8f4d67e..86edd45 100644
--- a/t/helper/test-urlmatch-normalization.c
+++ b/t/helper/test-urlmatch-normalization.c
@@ -5,8 +5,9 @@
int cmd__urlmatch_normalization(int argc, const char **argv)
{
const char usage[] = "test-tool urlmatch-normalization [-p | -l] <url1> | <url1> <url2>";
- char *url1, *url2;
+ char *url1 = NULL, *url2 = NULL;
int opt_p = 0, opt_l = 0;
+ int ret = 0;
/*
* For one url, succeed if url_normalize succeeds on it, fail otherwise.
@@ -39,7 +40,7 @@ int cmd__urlmatch_normalization(int argc, const char **argv)
printf("%s\n", url1);
if (opt_l)
printf("%u\n", (unsigned)info.url_len);
- return 0;
+ goto cleanup;
}
if (opt_p || opt_l)
@@ -47,5 +48,9 @@ int cmd__urlmatch_normalization(int argc, const char **argv)
url1 = url_normalize(argv[1], NULL);
url2 = url_normalize(argv[2], NULL);
- return (url1 && url2 && !strcmp(url1, url2)) ? 0 : 1;
+ ret = (url1 && url2 && !strcmp(url1, url2)) ? 0 : 1;
+cleanup:
+ free(url1);
+ free(url2);
+ return ret;
}
diff --git a/t/helper/test-userdiff.c b/t/helper/test-userdiff.c
index f013f8a..0ce31ce 100644
--- a/t/helper/test-userdiff.c
+++ b/t/helper/test-userdiff.c
@@ -1,5 +1,5 @@
#include "test-tool.h"
-#include "cache.h"
+#include "setup.h"
#include "userdiff.h"
#include "config.h"
@@ -12,7 +12,9 @@ static int driver_cb(struct userdiff_driver *driver,
return 0;
}
-static int cmd__userdiff_config(const char *var, const char *value, void *cb)
+static int cmd__userdiff_config(const char *var, const char *value,
+ const struct config_context *ctx UNUSED,
+ void *cb UNUSED)
{
if (userdiff_config(var, value) < 0)
return -1;
diff --git a/t/helper/test-wildmatch.c b/t/helper/test-wildmatch.c
index 2c103d1..b4ff5f9 100644
--- a/t/helper/test-wildmatch.c
+++ b/t/helper/test-wildmatch.c
@@ -1,5 +1,5 @@
#include "test-tool.h"
-#include "cache.h"
+#include "wildmatch.h"
int cmd__wildmatch(int argc, const char **argv)
{
diff --git a/t/helper/test-write-cache.c b/t/helper/test-write-cache.c
index 8837717..f084034 100644
--- a/t/helper/test-write-cache.c
+++ b/t/helper/test-write-cache.c
@@ -1,6 +1,9 @@
+#define USE_THE_INDEX_VARIABLE
#include "test-tool.h"
-#include "cache.h"
#include "lockfile.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
int cmd__write_cache(int argc, const char **argv)
{
@@ -9,9 +12,10 @@ int cmd__write_cache(int argc, const char **argv)
if (argc == 2)
cnt = strtol(argv[1], NULL, 0);
setup_git_directory();
- read_cache();
+ repo_read_index(the_repository);
for (i = 0; i < cnt; i++) {
- hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
+ repo_hold_locked_index(the_repository, &index_lock,
+ LOCK_DIE_ON_ERROR);
if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
die("unable to write index file");
}
diff --git a/t/helper/test-xml-encode.c b/t/helper/test-xml-encode.c
index a648bbd..b2f330d 100644
--- a/t/helper/test-xml-encode.c
+++ b/t/helper/test-xml-encode.c
@@ -6,7 +6,7 @@ static const char *utf8_replace_character = "&#xfffd;";
* Encodes (possibly incorrect) UTF-8 on <stdin> to <stdout>, to be embedded
* in an XML file.
*/
-int cmd__xml_encode(int argc, const char **argv)
+int cmd__xml_encode(int argc UNUSED, const char **argv UNUSED)
{
unsigned char buf[1024], tmp[4], *tmp2 = NULL;
ssize_t cur = 0, len = 1, remaining = 0;
diff --git a/t/interop/Makefile b/t/interop/Makefile
index 31a4bbc..6911c29 100644
--- a/t/interop/Makefile
+++ b/t/interop/Makefile
@@ -1,3 +1,6 @@
+# Import tree-wide shared Makefile behavior and libraries
+include ../../shared.mak
+
-include ../../config.mak
export GIT_TEST_OPTIONS
diff --git a/t/interop/interop-lib.sh b/t/interop/interop-lib.sh
index 3e0a291..62f4481 100644
--- a/t/interop/interop-lib.sh
+++ b/t/interop/interop-lib.sh
@@ -68,7 +68,7 @@ generate_wrappers () {
wrap_git .bin/git.a "$DIR_A" &&
wrap_git .bin/git.b "$DIR_B" &&
write_script .bin/git <<-\EOF &&
- echo >&2 fatal: test tried to run generic git
+ echo >&2 fatal: test tried to run generic git: $*
exit 1
EOF
PATH=$(pwd)/.bin:$PATH
diff --git a/t/lib-bitmap.sh b/t/lib-bitmap.sh
index fe3f98b..f595937 100644
--- a/t/lib-bitmap.sh
+++ b/t/lib-bitmap.sh
@@ -1,3 +1,9 @@
+# Helpers for scripts testing bitmap functionality; see t5310 for
+# example usage.
+
+objdir=.git/objects
+midx=$objdir/pack/multi-pack-index
+
# Compare a file containing rev-list bitmap traversal output to its non-bitmap
# counterpart. You can't just use test_cmp for this, because the two produce
# subtly different output:
@@ -24,3 +30,422 @@ test_bitmap_traversal () {
test_cmp "$1.normalized" "$2.normalized" &&
rm -f "$1.normalized" "$2.normalized"
}
+
+# To ensure the logic for "maximal commits" is exercised, make
+# the repository a bit more complicated.
+#
+# other second
+# * *
+# (99 commits) (99 commits)
+# * *
+# |\ /|
+# | * octo-other octo-second * |
+# |/|\_________ ____________/|\|
+# | \ \/ __________/ |
+# | | ________/\ / |
+# * |/ * merge-right *
+# | _|__________/ \____________ |
+# |/ | \|
+# (l1) * * merge-left * (r1)
+# | / \________________________ |
+# |/ \|
+# (l2) * * (r2)
+# \___________________________ |
+# \|
+# * (base)
+#
+# We only push bits down the first-parent history, which
+# makes some of these commits unimportant!
+#
+# The important part for the maximal commit algorithm is how
+# the bitmasks are extended. Assuming starting bit positions
+# for second (bit 0) and other (bit 1), the bitmasks at the
+# end should be:
+#
+# second: 1 (maximal, selected)
+# other: 01 (maximal, selected)
+# (base): 11 (maximal)
+#
+# This complicated history was important for a previous
+# version of the walk that guarantees never walking a
+# commit multiple times. That goal might be important
+# again, so preserve this complicated case. For now, this
+# test will guarantee that the bitmaps are computed
+# correctly, even with the repeat calculations.
+setup_bitmap_history() {
+ test_expect_success 'setup repo with moderate-sized history' '
+ test_commit_bulk --id=file 10 &&
+ git branch -M second &&
+ git checkout -b other HEAD~5 &&
+ test_commit_bulk --id=side 10 &&
+
+ # add complicated history setup, including merges and
+ # ambiguous merge-bases
+
+ git checkout -b merge-left other~2 &&
+ git merge second~2 -m "merge-left" &&
+
+ git checkout -b merge-right second~1 &&
+ git merge other~1 -m "merge-right" &&
+
+ git checkout -b octo-second second &&
+ git merge merge-left merge-right -m "octopus-second" &&
+
+ git checkout -b octo-other other &&
+ git merge merge-left merge-right -m "octopus-other" &&
+
+ git checkout other &&
+ git merge octo-other -m "pull octopus" &&
+
+ git checkout second &&
+ git merge octo-second -m "pull octopus" &&
+
+ # Remove these branches so they are not selected
+ # as bitmap tips
+ git branch -D merge-left &&
+ git branch -D merge-right &&
+ git branch -D octo-other &&
+ git branch -D octo-second &&
+
+ # add padding to make these merges less interesting
+ # and avoid having them selected for bitmaps
+ test_commit_bulk --id=file 100 &&
+ git checkout other &&
+ test_commit_bulk --id=side 100 &&
+ git checkout second &&
+
+ bitmaptip=$(git rev-parse second) &&
+ blob=$(echo tagged-blob | git hash-object -w --stdin) &&
+ git tag tagged-blob $blob
+ '
+}
+
+rev_list_tests_head () {
+ test_expect_success "counting commits via bitmap ($state, $branch)" '
+ git rev-list --count $branch >expect &&
+ git rev-list --use-bitmap-index --count $branch >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "counting partial commits via bitmap ($state, $branch)" '
+ git rev-list --count $branch~5..$branch >expect &&
+ git rev-list --use-bitmap-index --count $branch~5..$branch >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "counting commits with limit ($state, $branch)" '
+ git rev-list --count -n 1 $branch >expect &&
+ git rev-list --use-bitmap-index --count -n 1 $branch >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "counting non-linear history ($state, $branch)" '
+ git rev-list --count other...second >expect &&
+ git rev-list --use-bitmap-index --count other...second >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "counting commits with limiting ($state, $branch)" '
+ git rev-list --count $branch -- 1.t >expect &&
+ git rev-list --use-bitmap-index --count $branch -- 1.t >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "counting objects via bitmap ($state, $branch)" '
+ git rev-list --count --objects $branch >expect &&
+ git rev-list --use-bitmap-index --count --objects $branch >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "enumerate commits ($state, $branch)" '
+ git rev-list --use-bitmap-index $branch >actual &&
+ git rev-list $branch >expect &&
+ test_bitmap_traversal --no-confirm-bitmaps expect actual
+ '
+
+ test_expect_success "enumerate --objects ($state, $branch)" '
+ git rev-list --objects --use-bitmap-index $branch >actual &&
+ git rev-list --objects $branch >expect &&
+ test_bitmap_traversal expect actual
+ '
+
+ test_expect_success "bitmap --objects handles non-commit objects ($state, $branch)" '
+ git rev-list --objects --use-bitmap-index $branch tagged-blob >actual &&
+ grep $blob actual
+ '
+}
+
+rev_list_tests () {
+ state=$1
+
+ for branch in "second" "other"
+ do
+ rev_list_tests_head
+ done
+}
+
+basic_bitmap_tests () {
+ tip="$1"
+ test_expect_success 'rev-list --test-bitmap verifies bitmaps' "
+ git rev-list --test-bitmap "${tip:-HEAD}"
+ "
+
+ rev_list_tests 'full bitmap'
+
+ test_expect_success 'clone from bitmapped repository' '
+ rm -fr clone.git &&
+ git clone --no-local --bare . clone.git &&
+ git rev-parse HEAD >expect &&
+ git --git-dir=clone.git rev-parse HEAD >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success 'partial clone from bitmapped repository' '
+ test_config uploadpack.allowfilter true &&
+ rm -fr partial-clone.git &&
+ git clone --no-local --bare --filter=blob:none . partial-clone.git &&
+ (
+ cd partial-clone.git &&
+ pack=$(echo objects/pack/*.pack) &&
+ git verify-pack -v "$pack" >have &&
+ awk "/blob/ { print \$1 }" <have >blobs &&
+ # we expect this single blob because of the direct ref
+ git rev-parse refs/tags/tagged-blob >expect &&
+ test_cmp expect blobs
+ )
+ '
+
+ test_expect_success 'setup further non-bitmapped commits' '
+ test_commit_bulk --id=further 10
+ '
+
+ rev_list_tests 'partial bitmap'
+
+ test_expect_success 'fetch (partial bitmap)' '
+ git --git-dir=clone.git fetch origin second:second &&
+ git rev-parse HEAD >expect &&
+ git --git-dir=clone.git rev-parse HEAD >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success 'enumerating progress counts pack-reused objects' '
+ count=$(git rev-list --objects --all --count) &&
+ git repack -adb &&
+
+ # check first with only reused objects; confirm that our
+ # progress showed the right number, and also that we did
+ # pack-reuse as expected. Check only the final "done"
+ # line of the meter (there may be an arbitrary number of
+ # intermediate lines ending with CR).
+ GIT_PROGRESS_DELAY=0 \
+ git pack-objects --all --stdout --progress \
+ </dev/null >/dev/null 2>stderr &&
+ grep "Enumerating objects: $count, done" stderr &&
+ grep "pack-reused $count" stderr &&
+
+ # now the same but with one non-reused object
+ git commit --allow-empty -m "an extra commit object" &&
+ GIT_PROGRESS_DELAY=0 \
+ git pack-objects --all --stdout --progress \
+ </dev/null >/dev/null 2>stderr &&
+ grep "Enumerating objects: $((count+1)), done" stderr &&
+ grep "pack-reused $count" stderr
+ '
+}
+
+# have_delta <obj> <expected_base>
+#
+# Note that because this relies on cat-file, it might find _any_ copy of an
+# object in the repository. The caller is responsible for making sure
+# there's only one (e.g., via "repack -ad", or having just fetched a copy).
+have_delta () {
+ echo $2 >expect &&
+ echo $1 | git cat-file --batch-check="%(deltabase)" >actual &&
+ test_cmp expect actual
+}
+
+midx_checksum () {
+ test-tool read-midx --checksum "$1"
+}
+
+# midx_pack_source <obj>
+midx_pack_source () {
+ test-tool read-midx --show-objects .git/objects | grep "^$1 " | cut -f2
+}
+
+test_rev_exists () {
+ commit="$1"
+ kind="$2"
+
+ test_expect_success "reverse index exists ($kind)" '
+ GIT_TRACE2_EVENT=$(pwd)/event.trace \
+ git rev-list --test-bitmap "$commit" &&
+
+ if test "rev" = "$kind"
+ then
+ test_path_is_file $midx-$(midx_checksum $objdir).rev
+ fi &&
+ grep "\"category\":\"load_midx_revindex\",\"key\":\"source\",\"value\":\"$kind\"" event.trace
+ '
+}
+
+midx_bitmap_core () {
+ rev_kind="${1:-midx}"
+
+ setup_bitmap_history
+
+ test_expect_success 'create single-pack midx with bitmaps' '
+ git repack -ad &&
+ git multi-pack-index write --bitmap &&
+ test_path_is_file $midx &&
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap
+ '
+
+ test_rev_exists HEAD "$rev_kind"
+
+ basic_bitmap_tests
+
+ test_expect_success 'create new additional packs' '
+ for i in $(test_seq 1 16)
+ do
+ test_commit "$i" &&
+ git repack -d || return 1
+ done &&
+
+ git checkout -b other2 HEAD~8 &&
+ for i in $(test_seq 1 8)
+ do
+ test_commit "side-$i" &&
+ git repack -d || return 1
+ done &&
+ git checkout second
+ '
+
+ test_expect_success 'create multi-pack midx with bitmaps' '
+ git multi-pack-index write --bitmap &&
+
+ ls $objdir/pack/pack-*.pack >packs &&
+ test_line_count = 25 packs &&
+
+ test_path_is_file $midx &&
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap
+ '
+
+ test_rev_exists HEAD "$rev_kind"
+
+ basic_bitmap_tests
+
+ test_expect_success '--no-bitmap is respected when bitmaps exist' '
+ git multi-pack-index write --bitmap &&
+
+ test_commit respect--no-bitmap &&
+ git repack -d &&
+
+ test_path_is_file $midx &&
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
+
+ git multi-pack-index write --no-bitmap &&
+
+ test_path_is_file $midx &&
+ test_path_is_missing $midx-$(midx_checksum $objdir).bitmap &&
+ test_path_is_missing $midx-$(midx_checksum $objdir).rev
+ '
+
+ test_expect_success 'setup midx with base from later pack' '
+ # Write a and b so that "a" is a delta on top of base "b", since Git
+ # prefers to delete contents out of a base rather than add to a shorter
+ # object.
+ test_seq 1 128 >a &&
+ test_seq 1 130 >b &&
+
+ git add a b &&
+ git commit -m "initial commit" &&
+
+ a=$(git rev-parse HEAD:a) &&
+ b=$(git rev-parse HEAD:b) &&
+
+ # In the first pack, "a" is stored as a delta to "b".
+ p1=$(git pack-objects .git/objects/pack/pack <<-EOF
+ $a
+ $b
+ EOF
+ ) &&
+
+ # In the second pack, "a" is missing, and "b" is not a delta nor base to
+ # any other object.
+ p2=$(git pack-objects .git/objects/pack/pack <<-EOF
+ $b
+ $(git rev-parse HEAD)
+ $(git rev-parse HEAD^{tree})
+ EOF
+ ) &&
+
+ git prune-packed &&
+ # Use the second pack as the preferred source, so that "b" occurs
+ # earlier in the MIDX object order, rendering "a" unusable for pack
+ # reuse.
+ git multi-pack-index write --bitmap --preferred-pack=pack-$p2.idx &&
+
+ have_delta $a $b &&
+ test $(midx_pack_source $a) != $(midx_pack_source $b)
+ '
+
+ rev_list_tests 'full bitmap with backwards delta'
+
+ test_expect_success 'clone with bitmaps enabled' '
+ git clone --no-local --bare . clone-reverse-delta.git &&
+ test_when_finished "rm -fr clone-reverse-delta.git" &&
+
+ git rev-parse HEAD >expect &&
+ git --git-dir=clone-reverse-delta.git rev-parse HEAD >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success 'changing the preferred pack does not corrupt bitmaps' '
+ rm -fr repo &&
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit A &&
+ test_commit B &&
+
+ git rev-list --objects --no-object-names HEAD^ >A.objects &&
+ git rev-list --objects --no-object-names HEAD^.. >B.objects &&
+
+ A=$(git pack-objects $objdir/pack/pack <A.objects) &&
+ B=$(git pack-objects $objdir/pack/pack <B.objects) &&
+
+ cat >indexes <<-EOF &&
+ pack-$A.idx
+ pack-$B.idx
+ EOF
+
+ git multi-pack-index write --bitmap --stdin-packs \
+ --preferred-pack=pack-$A.pack <indexes &&
+ git rev-list --test-bitmap A &&
+
+ git multi-pack-index write --bitmap --stdin-packs \
+ --preferred-pack=pack-$B.pack <indexes &&
+ git rev-list --test-bitmap A
+ )
+ '
+}
+
+midx_bitmap_partial_tests () {
+ rev_kind="${1:-midx}"
+
+ test_expect_success 'setup partial bitmaps' '
+ test_commit packed &&
+ git repack &&
+ test_commit loose &&
+ git multi-pack-index write --bitmap &&
+ test_path_is_file $midx &&
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap
+ '
+
+ test_rev_exists HEAD~ "$rev_kind"
+
+ basic_bitmap_tests HEAD~
+}
diff --git a/t/lib-bundle-uri-protocol.sh b/t/lib-bundle-uri-protocol.sh
new file mode 100644
index 0000000..a4a1af8
--- /dev/null
+++ b/t/lib-bundle-uri-protocol.sh
@@ -0,0 +1,216 @@
+# Set up and run tests of the 'bundle-uri' command in protocol v2
+#
+# The test that includes this script should set BUNDLE_URI_PROTOCOL
+# to one of "file", "git", or "http".
+
+BUNDLE_URI_TEST_PARENT=
+BUNDLE_URI_TEST_URI=
+BUNDLE_URI_TEST_BUNDLE_URI=
+case "$BUNDLE_URI_PROTOCOL" in
+file)
+ BUNDLE_URI_PARENT=file_parent
+ BUNDLE_URI_REPO_URI="file://$PWD/file_parent"
+ BUNDLE_URI_BUNDLE_URI="$BUNDLE_URI_REPO_URI/fake.bdl"
+ test_set_prereq BUNDLE_URI_FILE
+ ;;
+git)
+ . "$TEST_DIRECTORY"/lib-git-daemon.sh
+ start_git_daemon --export-all --enable=receive-pack
+ BUNDLE_URI_PARENT="$GIT_DAEMON_DOCUMENT_ROOT_PATH/parent"
+ BUNDLE_URI_REPO_URI="$GIT_DAEMON_URL/parent"
+ BUNDLE_URI_BUNDLE_URI="https://example.com/fake.bdl"
+ test_set_prereq BUNDLE_URI_GIT
+ ;;
+http)
+ . "$TEST_DIRECTORY"/lib-httpd.sh
+ start_httpd
+ BUNDLE_URI_PARENT="$HTTPD_DOCUMENT_ROOT_PATH/http_parent"
+ BUNDLE_URI_REPO_URI="$HTTPD_URL/smart/http_parent"
+ BUNDLE_URI_BUNDLE_URI="https://example.com/fake.bdl"
+ test_set_prereq BUNDLE_URI_HTTP
+ ;;
+*)
+ BUG "Need to pass valid BUNDLE_URI_PROTOCOL (was \"$BUNDLE_URI_PROTOCOL\")"
+ ;;
+esac
+
+test_expect_success "setup protocol v2 $BUNDLE_URI_PROTOCOL:// tests" '
+ git init "$BUNDLE_URI_PARENT" &&
+ test_commit -C "$BUNDLE_URI_PARENT" one &&
+ git -C "$BUNDLE_URI_PARENT" config uploadpack.advertiseBundleURIs true
+'
+
+case "$BUNDLE_URI_PROTOCOL" in
+http)
+ test_expect_success "setup config for $BUNDLE_URI_PROTOCOL:// tests" '
+ git -C "$BUNDLE_URI_PARENT" config http.receivepack true
+ '
+ ;;
+*)
+ ;;
+esac
+BUNDLE_URI_BUNDLE_URI_ESCAPED=$(echo "$BUNDLE_URI_BUNDLE_URI" | test_uri_escape)
+
+test_expect_success "connect with $BUNDLE_URI_PROTOCOL:// using protocol v2: no bundle-uri" '
+ test_when_finished "rm -f log" &&
+ test_when_finished "git -C \"$BUNDLE_URI_PARENT\" config uploadpack.advertiseBundleURIs true" &&
+ git -C "$BUNDLE_URI_PARENT" config uploadpack.advertiseBundleURIs false &&
+
+ GIT_TRACE_PACKET="$PWD/log" \
+ git \
+ -c protocol.version=2 \
+ ls-remote --symref "$BUNDLE_URI_REPO_URI" \
+ >actual 2>err &&
+
+ # Server responded using protocol v2
+ grep "< version 2" log &&
+
+ ! grep bundle-uri log
+'
+
+test_expect_success "connect with $BUNDLE_URI_PROTOCOL:// using protocol v2: have bundle-uri" '
+ test_when_finished "rm -f log" &&
+
+ GIT_TRACE_PACKET="$PWD/log" \
+ git \
+ -c protocol.version=2 \
+ ls-remote --symref "$BUNDLE_URI_REPO_URI" \
+ >actual 2>err &&
+
+ # Server responded using protocol v2
+ grep "< version 2" log &&
+
+ # Server advertised bundle-uri capability
+ grep "< bundle-uri" log
+'
+
+test_expect_success "clone with $BUNDLE_URI_PROTOCOL:// using protocol v2: request bundle-uris" '
+ test_when_finished "rm -rf log* cloned*" &&
+
+ GIT_TRACE_PACKET="$PWD/log" \
+ git \
+ -c transfer.bundleURI=false \
+ -c protocol.version=2 \
+ clone "$BUNDLE_URI_REPO_URI" cloned \
+ >actual 2>err &&
+
+ # Server responded using protocol v2
+ grep "< version 2" log &&
+
+ # Server advertised bundle-uri capability
+ grep "< bundle-uri" log &&
+
+ # Client did not issue bundle-uri command
+ ! grep "> command=bundle-uri" log &&
+
+ GIT_TRACE_PACKET="$PWD/log" \
+ git \
+ -c transfer.bundleURI=true \
+ -c protocol.version=2 \
+ clone "$BUNDLE_URI_REPO_URI" cloned2 \
+ >actual 2>err &&
+
+ # Server responded using protocol v2
+ grep "< version 2" log &&
+
+ # Server advertised bundle-uri capability
+ grep "< bundle-uri" log &&
+
+ # Client issued bundle-uri command
+ grep "> command=bundle-uri" log &&
+
+ GIT_TRACE_PACKET="$PWD/log3" \
+ git \
+ -c transfer.bundleURI=true \
+ -c protocol.version=2 \
+ clone --bundle-uri="$BUNDLE_URI_BUNDLE_URI" \
+ "$BUNDLE_URI_REPO_URI" cloned3 \
+ >actual 2>err &&
+
+ # Server responded using protocol v2
+ grep "< version 2" log3 &&
+
+ # Server advertised bundle-uri capability
+ grep "< bundle-uri" log3 &&
+
+ # Client did not issue bundle-uri command (--bundle-uri override)
+ ! grep "> command=bundle-uri" log3
+'
+
+# The remaining tests will all assume transfer.bundleURI=true
+#
+# This test can be removed when transfer.bundleURI is enabled by default.
+test_expect_success 'enable transfer.bundleURI for remaining tests' '
+ git config --global transfer.bundleURI true
+'
+
+test_expect_success "test bundle-uri with $BUNDLE_URI_PROTOCOL:// using protocol v2" '
+ test_config -C "$BUNDLE_URI_PARENT" \
+ bundle.only.uri "$BUNDLE_URI_BUNDLE_URI_ESCAPED" &&
+
+ # All data about bundle URIs
+ cat >expect <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ [bundle "only"]
+ uri = $BUNDLE_URI_BUNDLE_URI_ESCAPED
+ EOF
+
+ test-tool bundle-uri \
+ ls-remote \
+ "$BUNDLE_URI_REPO_URI" \
+ >actual &&
+ test_cmp_config_output expect actual
+'
+
+test_expect_success "test bundle-uri with $BUNDLE_URI_PROTOCOL:// using protocol v2 and extra data" '
+ test_config -C "$BUNDLE_URI_PARENT" \
+ bundle.only.uri "$BUNDLE_URI_BUNDLE_URI_ESCAPED" &&
+
+ # Extra data should be ignored
+ test_config -C "$BUNDLE_URI_PARENT" bundle.only.extra bogus &&
+
+ # All data about bundle URIs
+ cat >expect <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ [bundle "only"]
+ uri = $BUNDLE_URI_BUNDLE_URI_ESCAPED
+ EOF
+
+ test-tool bundle-uri \
+ ls-remote \
+ "$BUNDLE_URI_REPO_URI" \
+ >actual &&
+ test_cmp_config_output expect actual
+'
+
+test_expect_success "test bundle-uri with $BUNDLE_URI_PROTOCOL:// using protocol v2 with list" '
+ test_config -C "$BUNDLE_URI_PARENT" \
+ bundle.bundle1.uri "$BUNDLE_URI_BUNDLE_URI_ESCAPED-1.bdl" &&
+ test_config -C "$BUNDLE_URI_PARENT" \
+ bundle.bundle2.uri "$BUNDLE_URI_BUNDLE_URI_ESCAPED-2.bdl" &&
+ test_config -C "$BUNDLE_URI_PARENT" \
+ bundle.bundle3.uri "$BUNDLE_URI_BUNDLE_URI_ESCAPED-3.bdl" &&
+
+ # All data about bundle URIs
+ cat >expect <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ [bundle "bundle1"]
+ uri = $BUNDLE_URI_BUNDLE_URI_ESCAPED-1.bdl
+ [bundle "bundle2"]
+ uri = $BUNDLE_URI_BUNDLE_URI_ESCAPED-2.bdl
+ [bundle "bundle3"]
+ uri = $BUNDLE_URI_BUNDLE_URI_ESCAPED-3.bdl
+ EOF
+
+ test-tool bundle-uri \
+ ls-remote \
+ "$BUNDLE_URI_REPO_URI" \
+ >actual &&
+ test_cmp_config_output expect actual
+'
diff --git a/t/lib-chunk.sh b/t/lib-chunk.sh
new file mode 100644
index 0000000..a7cd9c3
--- /dev/null
+++ b/t/lib-chunk.sh
@@ -0,0 +1,17 @@
+# Shell library for working with "chunk" files (commit-graph, midx, etc).
+
+# corrupt_chunk_file <fn> <chunk> <offset> <bytes>
+#
+# Corrupt a chunk-based file (like a commit-graph) by overwriting the bytes
+# found in the chunk specified by the 4-byte <chunk> identifier. If <offset> is
+# "clear", replace the chunk entirely. Otherwise, overwrite data <offset> bytes
+# into the chunk.
+#
+# The <bytes> are interpreted as pairs of hex digits (so "000000FE" would be
+# big-endian 254).
+corrupt_chunk_file () {
+ fn=$1; shift
+ perl "$TEST_DIRECTORY"/lib-chunk/corrupt-chunk-file.pl \
+ "$@" <"$fn" >"$fn.tmp" &&
+ mv "$fn.tmp" "$fn"
+}
diff --git a/t/lib-chunk/corrupt-chunk-file.pl b/t/lib-chunk/corrupt-chunk-file.pl
new file mode 100644
index 0000000..0e11aad
--- /dev/null
+++ b/t/lib-chunk/corrupt-chunk-file.pl
@@ -0,0 +1,90 @@
+#!/usr/bin/perl
+
+my ($chunk, $seek, $bytes) = @ARGV;
+$bytes =~ s/../chr(hex($&))/ge;
+
+binmode STDIN;
+binmode STDOUT;
+
+# A few helpers to read bytes, or read and copy them to the
+# output.
+sub get {
+ my $n = shift;
+ return unless $n;
+ read(STDIN, my $buf, $n)
+ or die "read error or eof: $!\n";
+ return $buf;
+}
+sub copy {
+ my $buf = get(@_);
+ print $buf;
+ return $buf;
+}
+
+# Some platforms' perl builds don't support 64-bit integers, and hence do not
+# allow packing/unpacking quadwords with "Q". The chunk format uses 64-bit file
+# offsets to support files of any size, but in practice our test suite will
+# only use small files. So we can fake it by asking for two 32-bit values and
+# discarding the first (most significant) one, which is equivalent as long as
+# it's just zero.
+sub unpack_quad {
+ my $bytes = shift;
+ my ($n1, $n2) = unpack("NN", $bytes);
+ die "quad value exceeds 32 bits" if $n1;
+ return $n2;
+}
+sub pack_quad {
+ my $n = shift;
+ my $ret = pack("NN", 0, $n);
+ # double check that our original $n did not exceed the 32-bit limit.
+ # This is presumably impossible on a 32-bit system (which would have
+ # truncated much earlier), but would still alert us on a 64-bit build
+ # of a new test that would fail on a 32-bit build (though we'd
+ # presumably see the die() from unpack_quad() in such a case).
+ die "quad round-trip failed" if unpack_quad($ret) != $n;
+ return $ret;
+}
+
+# read until we find table-of-contents entry for chunk;
+# note that we cheat a bit by assuming 4-byte alignment and
+# that no ToC entry will accidentally look like a header.
+#
+# If we don't find the entry, copy() will hit EOF and exit
+# (which should cause the caller to fail the test).
+while (copy(4) ne $chunk) { }
+my $offset = unpack_quad(copy(8));
+
+# In clear mode, our length will change. So figure out
+# the length by comparing to the offset of the next chunk, and
+# then adjust that offset (and all subsequent) ones.
+my $len;
+if ($seek eq "clear") {
+ my $id;
+ do {
+ $id = copy(4);
+ my $next = unpack_quad(get(8));
+ if (!defined $len) {
+ $len = $next - $offset;
+ }
+ print pack_quad($next - $len + length($bytes));
+ } while (unpack("N", $id));
+}
+
+# and now copy up to our existing chunk data
+copy($offset - tell(STDIN));
+if ($seek eq "clear") {
+ # if clearing, skip past existing data
+ get($len);
+} else {
+ # otherwise, copy up to the requested offset,
+ # and skip past the overwritten bytes
+ copy($seek);
+ get(length($bytes));
+}
+
+# now write out the requested bytes, along
+# with any other remaining data
+print $bytes;
+while (read(STDIN, my $buf, 4096)) {
+ print $buf;
+}
diff --git a/t/lib-commit-graph.sh b/t/lib-commit-graph.sh
new file mode 100755
index 0000000..89b2667
--- /dev/null
+++ b/t/lib-commit-graph.sh
@@ -0,0 +1,74 @@
+#!/bin/sh
+
+# Helper functions for testing commit-graphs.
+
+# Initialize OID cache with oid_version
+test_oid_cache <<-EOF
+oid_version sha1:1
+oid_version sha256:2
+EOF
+
+graph_git_two_modes() {
+ git -c core.commitGraph=true $1 >output &&
+ git -c core.commitGraph=false $1 >expect &&
+ test_cmp expect output
+}
+
+# graph_git_behavior <name> <directory> <branch> <compare>
+#
+# Ensures that a handful of traversal operations produce the same
+# results with and without the commit-graph in use.
+#
+# NOTE: it is a bug to call this function with <directory> containing
+# any characters in $IFS.
+graph_git_behavior() {
+ MSG=$1
+ DIR=$2
+ BRANCH=$3
+ COMPARE=$4
+ test_expect_success "check normal git operations: $MSG" '
+ graph_git_two_modes "${DIR:+-C $DIR} log --oneline $BRANCH" &&
+ graph_git_two_modes "${DIR:+-C $DIR} log --topo-order $BRANCH" &&
+ graph_git_two_modes "${DIR:+-C $DIR} log --graph $COMPARE..$BRANCH" &&
+ graph_git_two_modes "${DIR:+-C $DIR} branch -vv" &&
+ graph_git_two_modes "${DIR:+-C $DIR} merge-base -a $BRANCH $COMPARE"
+ '
+}
+
+graph_read_expect() {
+ OPTIONAL=""
+ NUM_CHUNKS=3
+ DIR="."
+ if test "$1" = -C
+ then
+ shift
+ DIR="$1"
+ shift
+ fi
+ if test -n "$2"
+ then
+ OPTIONAL=" $2"
+ NUM_CHUNKS=$((3 + $(echo "$2" | wc -w)))
+ fi
+ GENERATION_VERSION=2
+ if test -n "$3"
+ then
+ GENERATION_VERSION=$3
+ fi
+ OPTIONS=
+ if test $GENERATION_VERSION -gt 1
+ then
+ OPTIONS=" read_generation_data"
+ fi
+ cat >"$DIR/expect" <<-EOF
+ header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0
+ num_commits: $1
+ chunks: oid_fanout oid_lookup commit_metadata$OPTIONAL
+ options:$OPTIONS
+ EOF
+ (
+ cd "$DIR" &&
+ test-tool read-graph >output &&
+ test_cmp expect output
+ )
+}
diff --git a/t/lib-credential.sh b/t/lib-credential.sh
index 5ea8bc9..44799c0 100644
--- a/t/lib-credential.sh
+++ b/t/lib-credential.sh
@@ -43,6 +43,14 @@ helper_test_clean() {
reject $1 https example.com store-user
reject $1 https example.com user1
reject $1 https example.com user2
+ reject $1 https example.com user-expiry
+ reject $1 https example.com user-expiry-overwrite
+ reject $1 https example.com user4
+ reject $1 https example.com user-distinct-pass
+ reject $1 https example.com user-overwrite
+ reject $1 https example.com user-erase1
+ reject $1 https example.com user-erase2
+ reject $1 https victim.example.com user
reject $1 http path.tld user
reject $1 https timeout.tld user
reject $1 https sso.tld
@@ -166,6 +174,49 @@ helper_test() {
EOF
'
+ test_expect_success "helper ($HELPER) overwrites on store" '
+ check approve $HELPER <<-\EOF &&
+ protocol=https
+ host=example.com
+ username=user-overwrite
+ password=pass1
+ EOF
+ check approve $HELPER <<-\EOF &&
+ protocol=https
+ host=example.com
+ username=user-overwrite
+ password=pass2
+ EOF
+ check fill $HELPER <<-\EOF &&
+ protocol=https
+ host=example.com
+ username=user-overwrite
+ --
+ protocol=https
+ host=example.com
+ username=user-overwrite
+ password=pass2
+ EOF
+ check reject $HELPER <<-\EOF &&
+ protocol=https
+ host=example.com
+ username=user-overwrite
+ password=pass2
+ EOF
+ check fill $HELPER <<-\EOF
+ protocol=https
+ host=example.com
+ username=user-overwrite
+ --
+ protocol=https
+ host=example.com
+ username=user-overwrite
+ password=askpass-password
+ --
+ askpass: Password for '\''https://user-overwrite@example.com'\'':
+ EOF
+ '
+
test_expect_success "helper ($HELPER) can forget host" '
check reject $HELPER <<-\EOF &&
protocol=https
@@ -220,6 +271,31 @@ helper_test() {
EOF
'
+ test_expect_success "helper ($HELPER) does not erase a password distinct from input" '
+ check approve $HELPER <<-\EOF &&
+ protocol=https
+ host=example.com
+ username=user-distinct-pass
+ password=pass1
+ EOF
+ check reject $HELPER <<-\EOF &&
+ protocol=https
+ host=example.com
+ username=user-distinct-pass
+ password=pass2
+ EOF
+ check fill $HELPER <<-\EOF
+ protocol=https
+ host=example.com
+ username=user-distinct-pass
+ --
+ protocol=https
+ host=example.com
+ username=user-distinct-pass
+ password=pass1
+ EOF
+ '
+
test_expect_success "helper ($HELPER) can forget user" '
check reject $HELPER <<-\EOF &&
protocol=https
@@ -270,6 +346,66 @@ helper_test() {
password=
EOF
'
+
+ test_expect_success "helper ($HELPER) erases all matching credentials" '
+ check approve $HELPER <<-\EOF &&
+ protocol=https
+ host=example.com
+ username=user-erase1
+ password=pass1
+ EOF
+ check approve $HELPER <<-\EOF &&
+ protocol=https
+ host=example.com
+ username=user-erase2
+ password=pass1
+ EOF
+ check reject $HELPER <<-\EOF &&
+ protocol=https
+ host=example.com
+ EOF
+ check fill $HELPER <<-\EOF
+ protocol=https
+ host=example.com
+ --
+ protocol=https
+ host=example.com
+ username=askpass-username
+ password=askpass-password
+ --
+ askpass: Username for '\''https://example.com'\'':
+ askpass: Password for '\''https://askpass-username@example.com'\'':
+ EOF
+ '
+
+ : ${GIT_TEST_LONG_CRED_BUFFER:=1024}
+ # 23 bytes accounts for "wwwauth[]=basic realm=" plus NUL
+ LONG_VALUE_LEN=$((GIT_TEST_LONG_CRED_BUFFER - 23))
+ LONG_VALUE=$(perl -e 'print "a" x shift' $LONG_VALUE_LEN)
+
+ test_expect_success "helper ($HELPER) not confused by long header" '
+ check approve $HELPER <<-\EOF &&
+ protocol=https
+ host=victim.example.com
+ username=user
+ password=to-be-stolen
+ EOF
+
+ check fill $HELPER <<-EOF
+ protocol=https
+ host=badguy.example.com
+ wwwauth[]=basic realm=${LONG_VALUE}host=victim.example.com
+ --
+ protocol=https
+ host=badguy.example.com
+ username=askpass-username
+ password=askpass-password
+ wwwauth[]=basic realm=${LONG_VALUE}host=victim.example.com
+ --
+ askpass: Username for '\''https://badguy.example.com'\'':
+ askpass: Password for '\''https://askpass-username@badguy.example.com'\'':
+ EOF
+ '
}
helper_test_timeout() {
@@ -298,6 +434,110 @@ helper_test_timeout() {
'
}
+helper_test_password_expiry_utc() {
+ HELPER=$1
+
+ test_expect_success "helper ($HELPER) stores password_expiry_utc" '
+ check approve $HELPER <<-\EOF
+ protocol=https
+ host=example.com
+ username=user-expiry
+ password=pass
+ password_expiry_utc=9999999999
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) gets password_expiry_utc" '
+ check fill $HELPER <<-\EOF
+ protocol=https
+ host=example.com
+ username=user-expiry
+ --
+ protocol=https
+ host=example.com
+ username=user-expiry
+ password=pass
+ password_expiry_utc=9999999999
+ --
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) overwrites when password_expiry_utc changes" '
+ check approve $HELPER <<-\EOF &&
+ protocol=https
+ host=example.com
+ username=user-expiry-overwrite
+ password=pass1
+ password_expiry_utc=9999999998
+ EOF
+ check approve $HELPER <<-\EOF &&
+ protocol=https
+ host=example.com
+ username=user-expiry-overwrite
+ password=pass2
+ password_expiry_utc=9999999999
+ EOF
+ check fill $HELPER <<-\EOF &&
+ protocol=https
+ host=example.com
+ username=user-expiry-overwrite
+ --
+ protocol=https
+ host=example.com
+ username=user-expiry-overwrite
+ password=pass2
+ password_expiry_utc=9999999999
+ EOF
+ check reject $HELPER <<-\EOF &&
+ protocol=https
+ host=example.com
+ username=user-expiry-overwrite
+ password=pass2
+ EOF
+ check fill $HELPER <<-\EOF
+ protocol=https
+ host=example.com
+ username=user-expiry-overwrite
+ --
+ protocol=https
+ host=example.com
+ username=user-expiry-overwrite
+ password=askpass-password
+ --
+ askpass: Password for '\''https://user-expiry-overwrite@example.com'\'':
+ EOF
+ '
+}
+
+helper_test_oauth_refresh_token() {
+ HELPER=$1
+
+ test_expect_success "helper ($HELPER) stores oauth_refresh_token" '
+ check approve $HELPER <<-\EOF
+ protocol=https
+ host=example.com
+ username=user4
+ password=pass
+ oauth_refresh_token=xyzzy
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) gets oauth_refresh_token" '
+ check fill $HELPER <<-\EOF
+ protocol=https
+ host=example.com
+ username=user4
+ --
+ protocol=https
+ host=example.com
+ username=user4
+ password=pass
+ oauth_refresh_token=xyzzy
+ --
+ EOF
+ '
+}
+
write_script askpass <<\EOF
echo >&2 askpass: $*
what=$(echo $1 | cut -d" " -f1 | tr A-Z a-z | tr -cd a-z)
diff --git a/t/lib-cvs.sh b/t/lib-cvs.sh
index 32b3473..57b9b2d 100644
--- a/t/lib-cvs.sh
+++ b/t/lib-cvs.sh
@@ -71,8 +71,8 @@ test_cmp_branch_tree () {
find . -type d -name .git -prune -o -type f -print
) | sort >module-git-"$1".list &&
test_cmp module-cvs-"$1".list module-git-"$1".list &&
- cat module-cvs-"$1".list | while read f
+ while read f
do
test_cmp_branch_file "$1" "$f" || return 1
- done
+ done <module-cvs-"$1".list
}
diff --git a/t/lib-diff-alternative.sh b/t/lib-diff-alternative.sh
index 8d1e408..c4dc2d4 100644
--- a/t/lib-diff-alternative.sh
+++ b/t/lib-diff-alternative.sh
@@ -105,10 +105,67 @@ index $file1..$file2 100644
}
EOF
+ cat >expect_diffstat <<EOF
+ file1 => file2 | 21 ++++++++++-----------
+ 1 file changed, 10 insertions(+), 11 deletions(-)
+EOF
+
STRATEGY=$1
+ test_expect_success "setup attributes files for tests with $STRATEGY" '
+ git checkout -b master &&
+ echo "file* diff=driver" >.gitattributes &&
+ git add file1 file2 .gitattributes &&
+ git commit -m "adding files" &&
+ git checkout -b branchA &&
+ echo "file* diff=driverA" >.gitattributes &&
+ git add .gitattributes &&
+ git commit -m "adding driverA as diff driver" &&
+ git checkout master &&
+ git clone --bare --no-local . bare.git
+ '
+
+ test_expect_success "$STRATEGY diff from attributes" '
+ test_must_fail git -c diff.driver.algorithm=$STRATEGY diff --no-index file1 file2 > output &&
+ test_cmp expect output
+ '
+
+ test_expect_success "diff from attributes with bare repo with source" '
+ git -C bare.git --attr-source=branchA -c diff.driver.algorithm=myers \
+ -c diff.driverA.algorithm=$STRATEGY \
+ diff HEAD:file1 HEAD:file2 >output &&
+ test_cmp expect output
+ '
+
+ test_expect_success "diff from attributes with bare repo with invalid source" '
+ test_must_fail git -C bare.git --attr-source=invalid-branch diff \
+ HEAD:file1 HEAD:file2
+ '
+
+ test_expect_success "$STRATEGY diff from attributes has valid diffstat" '
+ echo "file* diff=driver" >.gitattributes &&
+ git config diff.driver.algorithm "$STRATEGY" &&
+ test_must_fail git diff --stat --no-index file1 file2 > output &&
+ test_cmp expect_diffstat output
+ '
+
test_expect_success "$STRATEGY diff" '
- test_must_fail git diff --no-index "--$STRATEGY" file1 file2 > output &&
+ test_must_fail git diff --no-index "--diff-algorithm=$STRATEGY" file1 file2 > output &&
+ test_cmp expect output
+ '
+
+ test_expect_success "$STRATEGY diff command line precedence before attributes" '
+ echo "file* diff=driver" >.gitattributes &&
+ git config diff.driver.algorithm myers &&
+ test_must_fail git diff --no-index "--diff-algorithm=$STRATEGY" file1 file2 > output &&
+ test_cmp expect output
+ '
+
+ test_expect_success "$STRATEGY diff attributes precedence before config" '
+ git config diff.algorithm default &&
+ echo "file* diff=driver" >.gitattributes &&
+ git config diff.driver.algorithm "$STRATEGY" &&
+ test_must_fail git diff --no-index file1 file2 > output &&
test_cmp expect output
'
diff --git a/t/lib-diff-data.sh b/t/lib-diff-data.sh
new file mode 100644
index 0000000..c64ec18
--- /dev/null
+++ b/t/lib-diff-data.sh
@@ -0,0 +1,22 @@
+COPYING_test_data () {
+ cat <<\EOF
+
+ Note that the only valid version of the GPL as far as this project
+ is concerned is _this_ particular version of the license (ie v2, not
+ v2.2 or v3.x or whatever), unless explicitly otherwise stated.
+
+ HOWEVER, in order to allow a migration to GPLv3 if that seems like
+ a good idea, I also ask that people involved with the project make
+ their preferences known. In particular, if you trust me to make that
+ decision, you might note so in your copyright message, ie something
+ like
+
+ This file is licensed under the GPL v2, or a later version
+ at the discretion of Linus.
+
+ might avoid issues. But we can also just decide to synchronize and
+ contact all copyright holders on record if/when the occasion arises.
+
+ Linus Torvalds
+EOF
+}
diff --git a/t/lib-diff.sh b/t/lib-diff.sh
index 2de880f..c4606bd 100644
--- a/t/lib-diff.sh
+++ b/t/lib-diff.sh
@@ -1,3 +1,5 @@
+. "$TEST_DIRECTORY"/lib-diff-data.sh
+
:
sanitize_diff_raw='/^:/s/ '"\($OID_REGEX\)"' '"\($OID_REGEX\)"' \([A-Z]\)[0-9]* / \1 \2 \3# /'
diff --git a/t/lib-diff/COPYING b/t/lib-diff/COPYING
deleted file mode 100644
index 6ff87c4..0000000
--- a/t/lib-diff/COPYING
+++ /dev/null
@@ -1,361 +0,0 @@
-
- Note that the only valid version of the GPL as far as this project
- is concerned is _this_ particular version of the license (ie v2, not
- v2.2 or v3.x or whatever), unless explicitly otherwise stated.
-
- HOWEVER, in order to allow a migration to GPLv3 if that seems like
- a good idea, I also ask that people involved with the project make
- their preferences known. In particular, if you trust me to make that
- decision, you might note so in your copyright message, ie something
- like
-
- This file is licensed under the GPL v2, or a later version
- at the discretion of Linus.
-
- might avoid issues. But we can also just decide to synchronize and
- contact all copyright holders on record if/when the occasion arises.
-
- Linus Torvalds
-
-----------------------------------------
-
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users. This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it. (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.) You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
- To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have. You must make sure that they, too, receive or can get the
-source code. And you must show them these terms so they know their
-rights.
-
- We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
- Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software. If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
- Finally, any free program is threatened constantly by software
-patents. We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary. To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- GNU GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License. The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language. (Hereinafter, translation is included without limitation in
-the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
- 1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
- 2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
- a) You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
- b) You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
- c) If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
- a) Accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of Sections
- 1 and 2 above on a medium customarily used for software interchange; or,
-
- b) Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
-
- c) Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable. However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
- 4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
- 5. You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Program or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
- 6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
- 7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
- 8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded. In such case, this License incorporates
-the limitation as if written in the body of this License.
-
- 9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation. If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
- 10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission. For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this. Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
- NO WARRANTY
-
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) year name of author
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary. Here is a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
- <signature of Ty Coon>, 1 April 1989
- Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Library General
-Public License instead of this License.
diff --git a/t/lib-diff/README b/t/lib-diff/README
deleted file mode 100644
index 548142c..0000000
--- a/t/lib-diff/README
+++ /dev/null
@@ -1,46 +0,0 @@
-////////////////////////////////////////////////////////////////
-
- GIT - the stupid content tracker
-
-////////////////////////////////////////////////////////////////
-
-"git" can mean anything, depending on your mood.
-
- - random three-letter combination that is pronounceable, and not
- actually used by any common UNIX command. The fact that it is a
- mispronunciation of "get" may or may not be relevant.
- - stupid. contemptible and despicable. simple. Take your pick from the
- dictionary of slang.
- - "global information tracker": you're in a good mood, and it actually
- works for you. Angels sing, and a light suddenly fills the room.
- - "goddamn idiotic truckload of sh*t": when it breaks
-
-Git is a fast, scalable, distributed revision control system with an
-unusually rich command set that provides both high-level operations
-and full access to internals.
-
-Git is an Open Source project covered by the GNU General Public License.
-It was originally written by Linus Torvalds with help of a group of
-hackers around the net. It is currently maintained by Junio C Hamano.
-
-Please read the file INSTALL for installation instructions.
-See Documentation/tutorial.txt to get started, then see
-Documentation/everyday.txt for a useful minimum set of commands,
-and "man git-commandname" for documentation of each command.
-CVS users may also want to read Documentation/cvs-migration.txt.
-
-Many Git online resources are accessible from http://git.or.cz/
-including full documentation and Git related tools.
-
-The user discussion and development of Git take place on the Git
-mailing list -- everyone is welcome to post bug reports, feature
-requests, comments and patches to git@vger.kernel.org. To subscribe
-to the list, send an email with just "subscribe git" in the body to
-majordomo@vger.kernel.org. The mailing list archives are available at
-http://marc.theaimsgroup.com/?l=git and other archival sites.
-
-The messages titled "A note from the maintainer", "What's in
-git.git (stable)" and "What's cooking in git.git (topics)" and
-the discussion following them on the mailing list give a good
-reference for project status, development direction and
-remaining tasks.
diff --git a/t/lib-git-p4.sh b/t/lib-git-p4.sh
index 5aff2ab..2a5b873 100644
--- a/t/lib-git-p4.sh
+++ b/t/lib-git-p4.sh
@@ -142,10 +142,11 @@ start_p4d () {
p4_add_user () {
name=$1 &&
+ fullname="${2:-Dr. $1}"
p4 user -f -i <<-EOF
User: $name
Email: $name@example.com
- FullName: Dr. $name
+ FullName: $fullname
EOF
}
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index 2fde235..ea28971 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -1,3 +1,7 @@
+if test -z "$TEST_FAILS_SANITIZE_LEAK"
+then
+ TEST_PASSES_SANITIZE_LEAK=true
+fi
. ./test-lib.sh
if test -n "$NO_SVN_TESTS"
diff --git a/t/lib-gpg.sh b/t/lib-gpg.sh
index 9fc5241..add11e8 100644
--- a/t/lib-gpg.sh
+++ b/t/lib-gpg.sh
@@ -13,7 +13,7 @@ test_lazy_prereq GPG '
gpg_version=$(gpg --version 2>&1)
test $? != 127 || exit 1
- # As said here: http://www.gnupg.org/documentation/faqs.html#q6.19
+ # As said here: https://web.archive.org/web/20130212022238/https://www.gnupg.org/faq/gnupg-faq.html#why-does-gnupg-1.0.6-bail-out-on-keyrings-used-with-1.0.7
# the gpg version 1.0.6 did not parse trust packets correctly, so for
# that version, creation of signed tags using the generated key fails.
case "$gpg_version" in
@@ -40,7 +40,29 @@ test_lazy_prereq GPG '
# > lib-gpg/ownertrust
mkdir "$GNUPGHOME" &&
chmod 0700 "$GNUPGHOME" &&
- (gpgconf --kill gpg-agent || : ) &&
+ (gpgconf --kill all || : ) &&
+ gpg --homedir "${GNUPGHOME}" --import \
+ "$TEST_DIRECTORY"/lib-gpg/keyring.gpg &&
+ gpg --homedir "${GNUPGHOME}" --import-ownertrust \
+ "$TEST_DIRECTORY"/lib-gpg/ownertrust &&
+ gpg --homedir "${GNUPGHOME}" --update-trustdb &&
+ gpg --homedir "${GNUPGHOME}" </dev/null >/dev/null \
+ --sign -u committer@example.com
+ ;;
+ esac
+'
+
+test_lazy_prereq GPG2 '
+ gpg_version=$(gpg --version 2>&1)
+ test $? != 127 || exit 1
+
+ case "$gpg_version" in
+ "gpg (GnuPG) "[01].*)
+ say "This test requires a GPG version >= v2.0.0"
+ exit 1
+ ;;
+ *)
+ (gpgconf --kill all || : ) &&
gpg --homedir "${GNUPGHOME}" --import \
"$TEST_DIRECTORY"/lib-gpg/keyring.gpg &&
gpg --homedir "${GNUPGHOME}" --import-ownertrust \
@@ -72,12 +94,11 @@ test_lazy_prereq GPGSM '
--passphrase-fd 0 --pinentry-mode loopback \
--import "$TEST_DIRECTORY"/lib-gpg/gpgsm_cert.p12 &&
- gpgsm --homedir "${GNUPGHOME}" -K |
- grep fingerprint: |
- cut -d" " -f4 |
- tr -d "\\n" >"${GNUPGHOME}/trustlist.txt" &&
+ gpgsm --homedir "${GNUPGHOME}" -K --with-colons |
+ awk -F ":" "/^fpr:/ {printf \"%s S relax\\n\", \$10}" \
+ >"${GNUPGHOME}/trustlist.txt" &&
+ (gpgconf --reload all || : ) &&
- echo " S relax" >>"${GNUPGHOME}/trustlist.txt" &&
echo hello | gpgsm --homedir "${GNUPGHOME}" >/dev/null \
-u committer@example.com -o /dev/null --sign -
'
@@ -87,6 +108,89 @@ test_lazy_prereq RFC1991 '
echo | gpg --homedir "${GNUPGHOME}" -b --rfc1991 >/dev/null
'
+GPGSSH_KEY_PRIMARY="${GNUPGHOME}/ed25519_ssh_signing_key"
+GPGSSH_KEY_SECONDARY="${GNUPGHOME}/rsa_2048_ssh_signing_key"
+GPGSSH_KEY_UNTRUSTED="${GNUPGHOME}/untrusted_ssh_signing_key"
+GPGSSH_KEY_EXPIRED="${GNUPGHOME}/expired_ssh_signing_key"
+GPGSSH_KEY_NOTYETVALID="${GNUPGHOME}/notyetvalid_ssh_signing_key"
+GPGSSH_KEY_TIMEBOXEDVALID="${GNUPGHOME}/timeboxed_valid_ssh_signing_key"
+GPGSSH_KEY_TIMEBOXEDINVALID="${GNUPGHOME}/timeboxed_invalid_ssh_signing_key"
+GPGSSH_KEY_WITH_PASSPHRASE="${GNUPGHOME}/protected_ssh_signing_key"
+GPGSSH_KEY_ECDSA="${GNUPGHOME}/ecdsa_ssh_signing_key"
+GPGSSH_KEY_PASSPHRASE="super_secret"
+GPGSSH_ALLOWED_SIGNERS="${GNUPGHOME}/ssh.all_valid.allowedSignersFile"
+
+GPGSSH_GOOD_SIGNATURE_TRUSTED='Good "git" signature for'
+GPGSSH_GOOD_SIGNATURE_UNTRUSTED='Good "git" signature with'
+GPGSSH_KEY_NOT_TRUSTED="No principal matched"
+GPGSSH_BAD_SIGNATURE="Signature verification failed"
+
+test_lazy_prereq GPGSSH '
+ ssh_version=$(ssh-keygen -Y find-principals -n "git" 2>&1)
+ test $? != 127 || exit 1
+ echo $ssh_version | grep -q "find-principals:missing signature file"
+ test $? = 0 || exit 1;
+
+ # Setup some keys and an allowed signers file
+ mkdir -p "${GNUPGHOME}" &&
+ chmod 0700 "${GNUPGHOME}" &&
+ (setfacl -k "${GNUPGHOME}" 2>/dev/null || true) &&
+ ssh-keygen -t ed25519 -N "" -C "git ed25519 key" -f "${GPGSSH_KEY_PRIMARY}" >/dev/null &&
+ ssh-keygen -t rsa -b 2048 -N "" -C "git rsa2048 key" -f "${GPGSSH_KEY_SECONDARY}" >/dev/null &&
+ ssh-keygen -t ed25519 -N "${GPGSSH_KEY_PASSPHRASE}" -C "git ed25519 encrypted key" -f "${GPGSSH_KEY_WITH_PASSPHRASE}" >/dev/null &&
+ ssh-keygen -t ecdsa -N "" -f "${GPGSSH_KEY_ECDSA}" >/dev/null &&
+ ssh-keygen -t ed25519 -N "" -C "git ed25519 key" -f "${GPGSSH_KEY_UNTRUSTED}" >/dev/null &&
+
+ cat >"${GPGSSH_ALLOWED_SIGNERS}" <<-EOF &&
+ "principal with number 1" $(cat "${GPGSSH_KEY_PRIMARY}.pub")"
+ "principal with number 2" $(cat "${GPGSSH_KEY_SECONDARY}.pub")"
+ "principal with number 3" $(cat "${GPGSSH_KEY_WITH_PASSPHRASE}.pub")"
+ "principal with number 4" $(cat "${GPGSSH_KEY_ECDSA}.pub")"
+ EOF
+
+ # Verify if at least one key and ssh-keygen works as expected
+ echo "testpayload" |
+ ssh-keygen -Y sign -n "git" -f "${GPGSSH_KEY_PRIMARY}" >gpgssh_prereq.sig &&
+ ssh-keygen -Y find-principals -f "${GPGSSH_ALLOWED_SIGNERS}" -s gpgssh_prereq.sig &&
+ echo "testpayload" |
+ ssh-keygen -Y verify -n "git" -f "${GPGSSH_ALLOWED_SIGNERS}" -I "principal with number 1" -s gpgssh_prereq.sig
+'
+
+test_lazy_prereq GPGSSH_VERIFYTIME '
+ test_have_prereq GPGSSH &&
+ # Check if ssh-keygen has a verify-time option by passing an invalid date to it
+ ssh-keygen -Overify-time=INVALID -Y check-novalidate -n "git" -s doesnotmatter 2>&1 | grep -q -F "Invalid \"verify-time\"" &&
+
+ # Set up keys with key lifetimes
+ ssh-keygen -t ed25519 -N "" -C "timeboxed valid key" -f "${GPGSSH_KEY_TIMEBOXEDVALID}" >/dev/null &&
+ key_valid=$(cat "${GPGSSH_KEY_TIMEBOXEDVALID}.pub") &&
+ ssh-keygen -t ed25519 -N "" -C "timeboxed invalid key" -f "${GPGSSH_KEY_TIMEBOXEDINVALID}" >/dev/null &&
+ key_invalid=$(cat "${GPGSSH_KEY_TIMEBOXEDINVALID}.pub") &&
+ ssh-keygen -t ed25519 -N "" -C "expired key" -f "${GPGSSH_KEY_EXPIRED}" >/dev/null &&
+ key_expired=$(cat "${GPGSSH_KEY_EXPIRED}.pub") &&
+ ssh-keygen -t ed25519 -N "" -C "not yet valid key" -f "${GPGSSH_KEY_NOTYETVALID}" >/dev/null &&
+ key_notyetvalid=$(cat "${GPGSSH_KEY_NOTYETVALID}.pub") &&
+
+ # Timestamps outside of test_tick span
+ ts2005a=20050401000000 ts2005b=200504020000 &&
+ # Timestamps within test_tick span
+ ts2005c=20050407000000 ts2005d=200504100000 &&
+ # Definitely not yet valid / expired timestamps
+ ts2000=20000101000000 ts2999=29990101000000 &&
+
+ cat >>"${GPGSSH_ALLOWED_SIGNERS}" <<-EOF &&
+ "timeboxed valid key" valid-after="$ts2005c",valid-before="$ts2005d" $key_valid"
+ "timeboxed invalid key" valid-after="$ts2005a",valid-before="$ts2005b" $key_invalid"
+ "principal with expired key" valid-before="$ts2000" $key_expired"
+ "principal with not yet valid key" valid-after="$ts2999" $key_notyetvalid"
+ EOF
+
+ # and verify ssh-keygen verifies the key lifetime
+ echo "testpayload" |
+ ssh-keygen -Y sign -n "git" -f "${GPGSSH_KEY_EXPIRED}" >gpgssh_verifytime_prereq.sig &&
+ ! (ssh-keygen -Y verify -n "git" -f "${GPGSSH_ALLOWED_SIGNERS}" -I "principal with expired key" -s gpgssh_verifytime_prereq.sig)
+'
+
sanitize_pgp() {
perl -ne '
/^-----END PGP/ and $in_pgp = 0;
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index d2edfa4..d83bafe 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -25,11 +25,12 @@
# LIB_HTTPD_DAV enable DAV
# LIB_HTTPD_SVN enable SVN at given location (e.g. "svn")
# LIB_HTTPD_SSL enable SSL
+# LIB_HTTPD_PROXY enable proxy
#
# Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
#
-if test -n "$NO_CURL"
+if ! test_have_prereq LIBCURL
then
skip_all='skipping test, git built without http support'
test_done
@@ -54,20 +55,31 @@ fi
HTTPD_PARA=""
-for DEFAULT_HTTPD_PATH in '/usr/sbin/httpd' '/usr/sbin/apache2'
+for DEFAULT_HTTPD_PATH in '/usr/sbin/httpd' \
+ '/usr/sbin/apache2' \
+ "$(command -v httpd)" \
+ "$(command -v apache2)"
do
- if test -x "$DEFAULT_HTTPD_PATH"
+ if test -n "$DEFAULT_HTTPD_PATH" && test -x "$DEFAULT_HTTPD_PATH"
then
break
fi
done
+if test -x "$DEFAULT_HTTPD_PATH"
+then
+ DETECTED_HTTPD_ROOT="$("$DEFAULT_HTTPD_PATH" -V 2>/dev/null | sed -n 's/^ -D HTTPD_ROOT="\(.*\)"$/\1/p')"
+fi
+
for DEFAULT_HTTPD_MODULE_PATH in '/usr/libexec/apache2' \
'/usr/lib/apache2/modules' \
'/usr/lib64/httpd/modules' \
- '/usr/lib/httpd/modules'
+ '/usr/lib/httpd/modules' \
+ '/usr/libexec/httpd' \
+ '/usr/lib/apache2' \
+ "${DETECTED_HTTPD_ROOT:+${DETECTED_HTTPD_ROOT}/modules}"
do
- if test -d "$DEFAULT_HTTPD_MODULE_PATH"
+ if test -n "$DEFAULT_HTTPD_MODULE_PATH" && test -d "$DEFAULT_HTTPD_MODULE_PATH"
then
break
fi
@@ -98,16 +110,19 @@ then
fi
HTTPD_VERSION=$($LIB_HTTPD_PATH -v | \
- sed -n 's/^Server version: Apache\/\([0-9]*\)\..*$/\1/p; q')
+ sed -n 's/^Server version: Apache\/\([0-9.]*\).*$/\1/p; q')
+HTTPD_VERSION_MAJOR=$(echo $HTTPD_VERSION | cut -d. -f1)
+HTTPD_VERSION_MINOR=$(echo $HTTPD_VERSION | cut -d. -f2)
-if test -n "$HTTPD_VERSION"
+if test -n "$HTTPD_VERSION_MAJOR"
then
if test -z "$LIB_HTTPD_MODULE_PATH"
then
- if ! test $HTTPD_VERSION -ge 2
+ if ! test "$HTTPD_VERSION_MAJOR" -eq 2 ||
+ ! test "$HTTPD_VERSION_MINOR" -ge 4
then
test_skip_or_die GIT_TEST_HTTPD \
- "at least Apache version 2 is required"
+ "at least Apache version 2.4 is required"
fi
if ! test -d "$DEFAULT_HTTPD_MODULE_PATH"
then
@@ -122,6 +137,20 @@ else
"Could not identify web server at '$LIB_HTTPD_PATH'"
fi
+if test -n "$LIB_HTTPD_DAV" && test -f /etc/os-release
+then
+ case "$(grep "^ID=" /etc/os-release | cut -d= -f2-)" in
+ alpine)
+ # The WebDAV module in Alpine Linux is broken at least up to
+ # Alpine v3.16 as the default DBM driver is missing.
+ #
+ # https://gitlab.alpinelinux.org/alpine/aports/-/issues/13112
+ test_skip_or_die GIT_TEST_HTTPD \
+ "Apache WebDAV module does not have default DBM backend driver"
+ ;;
+ esac
+fi
+
install_script () {
write_script "$HTTPD_ROOT_PATH/$1" <"$TEST_PATH/$1"
}
@@ -129,12 +158,15 @@ install_script () {
prepare_httpd() {
mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH"
cp "$TEST_PATH"/passwd "$HTTPD_ROOT_PATH"
+ cp "$TEST_PATH"/proxy-passwd "$HTTPD_ROOT_PATH"
install_script incomplete-length-upload-pack-v2-http.sh
install_script incomplete-body-upload-pack-v2-http.sh
+ install_script error-no-report.sh
install_script broken-smart-http.sh
install_script error-smart-http.sh
install_script error.sh
install_script apply-one-time-perl.sh
+ install_script nph-custom-auth.sh
ln -s "$LIB_HTTPD_MODULE_PATH" "$HTTPD_ROOT_PATH/modules"
@@ -171,6 +203,30 @@ prepare_httpd() {
export LIB_HTTPD_SVN LIB_HTTPD_SVNPATH
fi
fi
+
+ if test -n "$LIB_HTTPD_PROXY"
+ then
+ HTTPD_PARA="$HTTPD_PARA -DPROXY"
+ fi
+}
+
+enable_http2 () {
+ HTTPD_PARA="$HTTPD_PARA -DHTTP2"
+ test_set_prereq HTTP2
+}
+
+enable_cgipassauth () {
+ # We are looking for 2.4.13 or more recent. Since we only support
+ # 2.4 and up, no need to check for older major/minor.
+ if test "$HTTPD_VERSION_MAJOR" = 2 &&
+ test "$HTTPD_VERSION_MINOR" = 4 &&
+ test "$(echo $HTTPD_VERSION | cut -d. -f3)" -lt 13
+ then
+ echo >&4 "apache $HTTPD_VERSION too old for CGIPassAuth"
+ return
+ fi
+ HTTPD_PARA="$HTTPD_PARA -DUSE_CGIPASSAUTH"
+ test_set_prereq CGIPASSAUTH
}
start_httpd() {
@@ -210,8 +266,12 @@ test_http_push_nonff () {
git commit -a -m path2 --amend &&
test_must_fail git push -v origin >output 2>&1 &&
- (cd "$REMOTE_REPO" &&
- test $HEAD = $(git rev-parse --verify HEAD))
+ (
+ cd "$REMOTE_REPO" &&
+ echo "$HEAD" >expect &&
+ git rev-parse --verify HEAD >actual &&
+ test_cmp expect actual
+ )
'
test_expect_success 'non-fast-forward push show ref status' '
@@ -219,7 +279,7 @@ test_http_push_nonff () {
'
test_expect_success 'non-fast-forward push shows help message' '
- test_i18ngrep "Updates were rejected because" output
+ test_grep "Updates were rejected because" output
'
test_expect_${EXPECT_CAS_RESULT} 'force with lease aka cas' '
@@ -273,11 +333,11 @@ expect_askpass() {
none)
;;
pass)
- echo "askpass: Password for 'http://$2@$dest': "
+ echo "askpass: Password for '$HTTPD_PROTO://$2@$dest': "
;;
both)
- echo "askpass: Username for 'http://$dest': "
- echo "askpass: Password for 'http://$2@$dest': "
+ echo "askpass: Username for '$HTTPD_PROTO://$dest': "
+ echo "askpass: Password for '$HTTPD_PROTO://$2@$dest': "
;;
*)
false
diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf
index afa91e3..022276a 100644
--- a/t/lib-httpd/apache.conf
+++ b/t/lib-httpd/apache.conf
@@ -29,17 +29,11 @@ ErrorLog error.log
LoadModule setenvif_module modules/mod_setenvif.so
</IfModule>
-<IfVersion < 2.4>
-LockFile accept.lock
-</IfVersion>
-
-<IfVersion < 2.1>
-<IfModule !mod_auth.c>
- LoadModule auth_module modules/mod_auth.so
-</IfModule>
-</IfVersion>
+<IfDefine HTTP2>
+LoadModule http2_module modules/mod_http2.so
+Protocols h2 h2c
+</IfDefine>
-<IfVersion >= 2.1>
<IfModule !mod_auth_basic.c>
LoadModule auth_basic_module modules/mod_auth_basic.so
</IfModule>
@@ -52,9 +46,23 @@ LockFile accept.lock
<IfModule !mod_authz_host.c>
LoadModule authz_host_module modules/mod_authz_host.so
</IfModule>
-</IfVersion>
-<IfVersion >= 2.4>
+<IfDefine PROXY>
+<IfModule !mod_proxy.c>
+ LoadModule proxy_module modules/mod_proxy.so
+</IfModule>
+<IfModule !mod_proxy_http.c>
+ LoadModule proxy_http_module modules/mod_proxy_http.so
+</IfModule>
+ProxyRequests On
+<Proxy "*">
+ AuthType Basic
+ AuthName "proxy-auth"
+ AuthUserFile proxy-passwd
+ Require valid-user
+</Proxy>
+</IfDefine>
+
<IfModule !mod_authn_core.c>
LoadModule authn_core_module modules/mod_authn_core.so
</IfModule>
@@ -64,28 +72,38 @@ LockFile accept.lock
<IfModule !mod_access_compat.c>
LoadModule access_compat_module modules/mod_access_compat.so
</IfModule>
-<IfModule !mod_mpm_prefork.c>
- LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
-</IfModule>
<IfModule !mod_unixd.c>
LoadModule unixd_module modules/mod_unixd.so
</IfModule>
-</IfVersion>
+
+<IfDefine HTTP2>
+<IfModule !mod_mpm_event.c>
+ LoadModule mpm_event_module modules/mod_mpm_event.so
+</IfModule>
+</IfDefine>
+<IfDefine !HTTP2>
+<IfModule !mod_mpm_prefork.c>
+ LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
+</IfModule>
+</IfDefine>
PassEnv GIT_VALGRIND
PassEnv GIT_VALGRIND_OPTIONS
PassEnv GNUPGHOME
PassEnv ASAN_OPTIONS
PassEnv LSAN_OPTIONS
+PassEnv UBSAN_OPTIONS
PassEnv GIT_TRACE
PassEnv GIT_CONFIG_NOSYSTEM
PassEnv GIT_TEST_SIDEBAND_ALL
-
-SetEnvIf Git-Protocol ".*" GIT_PROTOCOL=$0
+PassEnv LANG
+PassEnv LC_ALL
Alias /dumb/ www/
Alias /auth/dumb/ www/auth/dumb/
+SetEnv PERL_PATH ${PERL_PATH}
+
<LocationMatch /smart/>
SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
SetEnv GIT_HTTP_EXPORT_ALL
@@ -110,6 +128,10 @@ Alias /auth/dumb/ www/auth/dumb/
Header set Set-Cookie name=value
</LocationMatch>
<LocationMatch /smart_headers/>
+ <RequireAll>
+ Require expr %{HTTP:x-magic-one} == 'abra'
+ Require expr %{HTTP:x-magic-two} == 'cadabra'
+ </RequireAll>
SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
SetEnv GIT_HTTP_EXPORT_ALL
</LocationMatch>
@@ -117,14 +139,28 @@ Alias /auth/dumb/ www/auth/dumb/
SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
SetEnv GIT_HTTP_EXPORT_ALL
</LocationMatch>
+<LocationMatch /smart_v0/>
+ SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+ SetEnv GIT_HTTP_EXPORT_ALL
+ SetEnv GIT_PROTOCOL
+</LocationMatch>
+<LocationMatch /custom_auth/>
+ SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+ SetEnv GIT_HTTP_EXPORT_ALL
+ <IfDefine USE_CGIPASSAUTH>
+ CGIPassAuth on
+ </IfDefine>
+</LocationMatch>
ScriptAlias /smart/incomplete_length/git-upload-pack incomplete-length-upload-pack-v2-http.sh/
ScriptAlias /smart/incomplete_body/git-upload-pack incomplete-body-upload-pack-v2-http.sh/
+ScriptAlias /smart/no_report/git-receive-pack error-no-report.sh/
ScriptAliasMatch /error_git_upload_pack/(.*)/git-upload-pack error.sh/
ScriptAliasMatch /smart_*[^/]*/(.*) ${GIT_EXEC_PATH}/git-http-backend/$1
ScriptAlias /broken_smart/ broken-smart-http.sh/
ScriptAlias /error_smart/ error-smart-http.sh/
ScriptAlias /error/ error.sh/
ScriptAliasMatch /one_time_perl/(.*) apply-one-time-perl.sh/$1
+ScriptAliasMatch /custom_auth/(.*) nph-custom-auth.sh/$1
<Directory ${GIT_EXEC_PATH}>
Options FollowSymlinks
</Directory>
@@ -134,6 +170,9 @@ ScriptAliasMatch /one_time_perl/(.*) apply-one-time-perl.sh/$1
<Files incomplete-body-upload-pack-v2-http.sh>
Options ExecCGI
</Files>
+<Files error-no-report.sh>
+ Options ExecCGI
+</Files>
<Files broken-smart-http.sh>
Options ExecCGI
</Files>
@@ -183,18 +222,6 @@ RewriteRule ^/intern-redir/(.*)/foo$ /smart/$1 [PT]
RewriteRule ^/redir-objects/(.*/info/refs)$ /dumb/$1 [PT]
RewriteRule ^/redir-objects/(.*/objects/.*)$ /dumb/$1 [R=301]
-# Apache 2.2 does not understand <RequireAll>, so we use RewriteCond.
-# And as RewriteCond does not allow testing for non-matches, we match
-# the desired case first (one has abra, two has cadabra), and let it
-# pass by marking the RewriteRule as [L], "last rule, do not process
-# any other matching RewriteRules after this"), and then have another
-# RewriteRule that matches all other cases and lets them fail via '[F]',
-# "fail the request".
-RewriteCond %{HTTP:x-magic-one} =abra
-RewriteCond %{HTTP:x-magic-two} =cadabra
-RewriteRule ^/smart_headers/.* - [L]
-RewriteRule ^/smart_headers/.* - [F]
-
<IfDefine SSL>
LoadModule ssl_module modules/mod_ssl.so
@@ -203,7 +230,6 @@ SSLCertificateKeyFile httpd.pem
SSLRandomSeed startup file:/dev/urandom 512
SSLRandomSeed connect file:/dev/urandom 512
SSLSessionCache none
-SSLMutex file:ssl_mutex
SSLEngine On
</IfDefine>
diff --git a/t/lib-httpd/apply-one-time-perl.sh b/t/lib-httpd/apply-one-time-perl.sh
index 09a0abd..d7f9fed 100644
--- a/t/lib-httpd/apply-one-time-perl.sh
+++ b/t/lib-httpd/apply-one-time-perl.sh
@@ -13,7 +13,7 @@ then
export LC_ALL
"$GIT_EXEC_PATH/git-http-backend" >out
- perl -pe "$(cat one-time-perl)" out >out_modified
+ "$PERL_PATH" -pe "$(cat one-time-perl)" out >out_modified
if cmp -s out out_modified
then
diff --git a/t/lib-httpd/error-no-report.sh b/t/lib-httpd/error-no-report.sh
new file mode 100644
index 0000000..39ff75b
--- /dev/null
+++ b/t/lib-httpd/error-no-report.sh
@@ -0,0 +1,6 @@
+echo "Content-Type: application/x-git-receive-pack-result"
+echo
+printf '0013\001000eunpack ok\n'
+printf '0015\002skipping report\n'
+printf '0009\0010000'
+printf '0000'
diff --git a/t/lib-httpd/nph-custom-auth.sh b/t/lib-httpd/nph-custom-auth.sh
new file mode 100644
index 0000000..f5345e7
--- /dev/null
+++ b/t/lib-httpd/nph-custom-auth.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+VALID_CREDS_FILE=custom-auth.valid
+CHALLENGE_FILE=custom-auth.challenge
+
+#
+# If $VALID_CREDS_FILE exists in $HTTPD_ROOT_PATH, consider each line as a valid
+# credential for the current request. Each line in the file is considered a
+# valid HTTP Authorization header value. For example:
+#
+# Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+#
+# If $CHALLENGE_FILE exists in $HTTPD_ROOT_PATH, output the contents as headers
+# in a 401 response if no valid authentication credentials were included in the
+# request. For example:
+#
+# WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
+# WWW-Authenticate: Basic realm="example.com"
+#
+
+if test -n "$HTTP_AUTHORIZATION" && \
+ grep -Fqsx "${HTTP_AUTHORIZATION}" "$VALID_CREDS_FILE"
+then
+ # Note that although git-http-backend returns a status line, it
+ # does so using a CGI 'Status' header. Because this script is an
+ # No Parsed Headers (NPH) script, we must return a real HTTP
+ # status line.
+ # This is only a test script, so we don't bother to check for
+ # the actual status from git-http-backend and always return 200.
+ echo 'HTTP/1.1 200 OK'
+ exec "$GIT_EXEC_PATH"/git-http-backend
+fi
+
+echo 'HTTP/1.1 401 Authorization Required'
+if test -f "$CHALLENGE_FILE"
+then
+ cat "$CHALLENGE_FILE"
+fi
+echo
diff --git a/t/lib-httpd/passwd b/t/lib-httpd/passwd
index 99a34d6..d9c122f 100644
--- a/t/lib-httpd/passwd
+++ b/t/lib-httpd/passwd
@@ -1 +1 @@
-user@host:xb4E8pqD81KQs
+user@host:$apr1$LGPmCZWj$9vxEwj5Z5GzQLBMxp3mCx1
diff --git a/t/lib-httpd/proxy-passwd b/t/lib-httpd/proxy-passwd
new file mode 100644
index 0000000..2ad7705
--- /dev/null
+++ b/t/lib-httpd/proxy-passwd
@@ -0,0 +1 @@
+proxuser:$apr1$RxS6MLkD$DYsqQdflheq4GPNxzJpx5.
diff --git a/t/lib-httpd/ssl.cnf b/t/lib-httpd/ssl.cnf
index 6dab257..812e825 100644
--- a/t/lib-httpd/ssl.cnf
+++ b/t/lib-httpd/ssl.cnf
@@ -1,7 +1,7 @@
RANDFILE = $ENV::RANDFILE_PATH
[ req ]
-default_bits = 1024
+default_bits = 2048
distinguished_name = req_distinguished_name
prompt = no
[ req_distinguished_name ]
diff --git a/t/lib-midx.sh b/t/lib-midx.sh
new file mode 100644
index 0000000..1261994
--- /dev/null
+++ b/t/lib-midx.sh
@@ -0,0 +1,8 @@
+# test_midx_consistent <objdir>
+test_midx_consistent () {
+ ls $1/pack/pack-*.idx | xargs -n 1 basename | sort >expect &&
+ test-tool read-midx $1 | grep ^pack-.*\.idx$ | sort >actual &&
+
+ test_cmp expect actual &&
+ git multi-pack-index --object-dir=$1 verify
+}
diff --git a/t/lib-pager.sh b/t/lib-pager.sh
index 3aa7a3f..e5eb28d 100644
--- a/t/lib-pager.sh
+++ b/t/lib-pager.sh
@@ -3,7 +3,7 @@
test_expect_success 'determine default pager' '
test_might_fail git config --unset core.pager &&
less=$(
- unset PAGER GIT_PAGER;
+ sane_unset PAGER GIT_PAGER &&
git var GIT_PAGER
) &&
test -n "$less"
diff --git a/t/lib-parallel-checkout.sh b/t/lib-parallel-checkout.sh
index 83b279a..8324d6c 100644
--- a/t/lib-parallel-checkout.sh
+++ b/t/lib-parallel-checkout.sh
@@ -20,12 +20,16 @@ test_checkout_workers () {
BUG "too few arguments to test_checkout_workers"
fi &&
- local expected_workers=$1 &&
+ local expected_workers="$1" &&
shift &&
local trace_file=trace-test-checkout-workers &&
rm -f "$trace_file" &&
- GIT_TRACE2="$(pwd)/$trace_file" "$@" 2>&8 &&
+ (
+ GIT_TRACE2="$(pwd)/$trace_file" &&
+ export GIT_TRACE2 &&
+ "$@" 2>&8
+ ) &&
local workers="$(grep "child_start\[..*\] git checkout--worker" "$trace_file" | wc -l)" &&
test $workers -eq $expected_workers &&
diff --git a/t/lib-patch-mode.sh b/t/lib-patch-mode.sh
index cfd76bf..89ca1f7 100644
--- a/t/lib-patch-mode.sh
+++ b/t/lib-patch-mode.sh
@@ -29,8 +29,12 @@ set_and_save_state () {
# verify_state <path> <expected-worktree-content> <expected-index-content>
verify_state () {
- test "$(cat "$1")" = "$2" &&
- test "$(git show :"$1")" = "$3"
+ echo "$2" >expect &&
+ test_cmp expect "$1" &&
+
+ echo "$3" >expect &&
+ git show :"$1" >actual &&
+ test_cmp expect actual
}
# verify_saved_state <path>
@@ -46,5 +50,6 @@ save_head () {
}
verify_saved_head () {
- test "$(cat _head)" = "$(git rev-parse HEAD)"
+ git rev-parse HEAD >actual &&
+ test_cmp _head actual
}
diff --git a/t/lib-perl.sh b/t/lib-perl.sh
new file mode 100644
index 0000000..d0bf509
--- /dev/null
+++ b/t/lib-perl.sh
@@ -0,0 +1,19 @@
+# Copyright (c) 2022 Ævar Arnfjörð Bjarmason
+
+test_lazy_prereq PERL_TEST_MORE '
+ perl -MTest::More -e 0
+'
+
+skip_all_if_no_Test_More () {
+ if ! test_have_prereq PERL
+ then
+ skip_all='skipping perl interface tests, perl not available'
+ test_done
+ fi
+
+ if ! test_have_prereq PERL_TEST_MORE
+ then
+ skip_all="Perl Test::More unavailable, skipping test"
+ test_done
+ fi
+}
diff --git a/t/lib-proto-disable.sh b/t/lib-proto-disable.sh
index 83babe5..890622b 100644
--- a/t/lib-proto-disable.sh
+++ b/t/lib-proto-disable.sh
@@ -1,7 +1,7 @@
# Test routines for checking protocol disabling.
-# Test clone/fetch/push with GIT_ALLOW_PROTOCOL whitelist
-test_whitelist () {
+# Test clone/fetch/push with GIT_ALLOW_PROTOCOL environment variable
+test_allow_var () {
desc=$1
proto=$2
url=$3
@@ -183,7 +183,7 @@ test_config () {
# $2 - machine-readable name of the protocol
# $3 - the URL to try cloning
test_proto () {
- test_whitelist "$@"
+ test_allow_var "$@"
test_config "$@"
}
diff --git a/t/lib-read-tree-m-3way.sh b/t/lib-read-tree-m-3way.sh
index 168329a..2da25b3 100644
--- a/t/lib-read-tree-m-3way.sh
+++ b/t/lib-read-tree-m-3way.sh
@@ -3,21 +3,21 @@
mkdir Z
for a in N D M
do
- for b in N D M
- do
- p=$a$b
+ for b in N D M
+ do
+ p=$a$b
echo This is $p from the original tree. >$p
echo This is Z/$p from the original tree. >Z/$p
- test_expect_success \
- "adding test file $p and Z/$p" \
- 'git update-index --add $p &&
- git update-index --add Z/$p'
+ test_expect_success "adding test file $p and Z/$p" '
+ git update-index --add $p &&
+ git update-index --add Z/$p
+ '
done
done
echo This is SS from the original tree. >SS
-test_expect_success \
- 'adding test file SS' \
- 'git update-index --add SS'
+test_expect_success 'adding test file SS' '
+ git update-index --add SS
+'
cat >TT <<\EOF
This is a trivial merge sample text.
Branch A is expected to upcase this word, here.
@@ -30,12 +30,12 @@ At the very end, here comes another line, that is
the word, expected to be upcased by Branch B.
This concludes the trivial merge sample file.
EOF
-test_expect_success \
- 'adding test file TT' \
- 'git update-index --add TT'
-test_expect_success \
- 'prepare initial tree' \
- 'tree_O=$(git write-tree)'
+test_expect_success 'adding test file TT' '
+ git update-index --add TT
+'
+test_expect_success 'prepare initial tree' '
+ tree_O=$(git write-tree)
+'
################################################################
# Branch A and B makes the changes according to the above matrix.
@@ -45,48 +45,48 @@ test_expect_success \
to_remove=$(echo D? Z/D?)
rm -f $to_remove
-test_expect_success \
- 'change in branch A (removal)' \
- 'git update-index --remove $to_remove'
+test_expect_success 'change in branch A (removal)' '
+ git update-index --remove $to_remove
+'
for p in M? Z/M?
do
- echo This is modified $p in the branch A. >$p
- test_expect_success \
- 'change in branch A (modification)' \
- "git update-index $p"
+ echo This is modified $p in the branch A. >$p
+ test_expect_success 'change in branch A (modification)' '
+ git update-index $p
+ '
done
for p in AN AA Z/AN Z/AA
do
- echo This is added $p in the branch A. >$p
- test_expect_success \
- 'change in branch A (addition)' \
- "git update-index --add $p"
+ echo This is added $p in the branch A. >$p
+ test_expect_success 'change in branch A (addition)' '
+ git update-index --add $p
+ '
done
echo This is SS from the modified tree. >SS
echo This is LL from the modified tree. >LL
-test_expect_success \
- 'change in branch A (addition)' \
- 'git update-index --add LL &&
- git update-index SS'
+test_expect_success 'change in branch A (addition)' '
+ git update-index --add LL &&
+ git update-index SS
+'
mv TT TT-
sed -e '/Branch A/s/word/WORD/g' <TT- >TT
rm -f TT-
-test_expect_success \
- 'change in branch A (edit)' \
- 'git update-index TT'
+test_expect_success 'change in branch A (edit)' '
+ git update-index TT
+'
mkdir DF
echo Branch A makes a file at DF/DF, creating a directory DF. >DF/DF
-test_expect_success \
- 'change in branch A (change file to directory)' \
- 'git update-index --add DF/DF'
+test_expect_success 'change in branch A (change file to directory)' '
+ git update-index --add DF/DF
+'
-test_expect_success \
- 'recording branch A tree' \
- 'tree_A=$(git write-tree)'
+test_expect_success 'recording branch A tree' '
+ tree_A=$(git write-tree)
+'
################################################################
# Branch B
@@ -94,65 +94,65 @@ test_expect_success \
rm -rf [NDMASLT][NDMASLT] Z DF
mkdir Z
-test_expect_success \
- 'reading original tree and checking out' \
- 'git read-tree $tree_O &&
- git checkout-index -a'
+test_expect_success 'reading original tree and checking out' '
+ git read-tree $tree_O &&
+ git checkout-index -a
+'
to_remove=$(echo ?D Z/?D)
rm -f $to_remove
-test_expect_success \
- 'change in branch B (removal)' \
- "git update-index --remove $to_remove"
+test_expect_success 'change in branch B (removal)' '
+ git update-index --remove $to_remove
+'
for p in ?M Z/?M
do
- echo This is modified $p in the branch B. >$p
- test_expect_success \
- 'change in branch B (modification)' \
- "git update-index $p"
+ echo This is modified $p in the branch B. >$p
+ test_expect_success 'change in branch B (modification)' '
+ git update-index $p
+ '
done
for p in NA AA Z/NA Z/AA
do
- echo This is added $p in the branch B. >$p
- test_expect_success \
- 'change in branch B (addition)' \
- "git update-index --add $p"
+ echo This is added $p in the branch B. >$p
+ test_expect_success 'change in branch B (addition)' '
+ git update-index --add $p
+ '
done
echo This is SS from the modified tree. >SS
echo This is LL from the modified tree. >LL
-test_expect_success \
- 'change in branch B (addition and modification)' \
- 'git update-index --add LL &&
- git update-index SS'
+test_expect_success 'change in branch B (addition and modification)' '
+ git update-index --add LL &&
+ git update-index SS
+'
mv TT TT-
sed -e '/Branch B/s/word/WORD/g' <TT- >TT
rm -f TT-
-test_expect_success \
- 'change in branch B (modification)' \
- 'git update-index TT'
+test_expect_success 'change in branch B (modification)' '
+ git update-index TT
+'
echo Branch B makes a file at DF. >DF
-test_expect_success \
- 'change in branch B (addition of a file to conflict with directory)' \
- 'git update-index --add DF'
-
-test_expect_success \
- 'recording branch B tree' \
- 'tree_B=$(git write-tree)'
-
-test_expect_success \
- 'keep contents of 3 trees for easy access' \
- 'rm -f .git/index &&
- git read-tree $tree_O &&
- mkdir .orig-O &&
- git checkout-index --prefix=.orig-O/ -f -q -a &&
- rm -f .git/index &&
- git read-tree $tree_A &&
- mkdir .orig-A &&
- git checkout-index --prefix=.orig-A/ -f -q -a &&
- rm -f .git/index &&
- git read-tree $tree_B &&
- mkdir .orig-B &&
- git checkout-index --prefix=.orig-B/ -f -q -a'
+test_expect_success 'change in branch B (addition of a file to conflict with directory)' '
+ git update-index --add DF
+'
+
+test_expect_success 'recording branch B tree' '
+ tree_B=$(git write-tree)
+'
+
+test_expect_success 'keep contents of 3 trees for easy access' '
+ rm -f .git/index &&
+ git read-tree $tree_O &&
+ mkdir .orig-O &&
+ git checkout-index --prefix=.orig-O/ -f -q -a &&
+ rm -f .git/index &&
+ git read-tree $tree_A &&
+ mkdir .orig-A &&
+ git checkout-index --prefix=.orig-A/ -f -q -a &&
+ rm -f .git/index &&
+ git read-tree $tree_B &&
+ mkdir .orig-B &&
+ git checkout-index --prefix=.orig-B/ -f -q -a
+'
diff --git a/t/lib-rebase.sh b/t/lib-rebase.sh
index ec6b9b1..11d2dc9 100644
--- a/t/lib-rebase.sh
+++ b/t/lib-rebase.sh
@@ -8,18 +8,21 @@
# - check that non-commit messages have a certain line count with $EXPECT_COUNT
# - check the commit count in the commit message header with $EXPECT_HEADER_COUNT
# - rewrite a rebase -i script as directed by $FAKE_LINES.
-# $FAKE_LINES consists of a sequence of words separated by spaces.
-# The following word combinations are possible:
+# $FAKE_LINES consists of a sequence of words separated by spaces;
+# spaces inside the words are encoded as underscores.
+# The following words are possible:
#
-# "<lineno>" -- add a "pick" line with the SHA1 taken from the
-# specified line.
+# "<cmd>" -- override the command for the next line specification. Can be
+# "pick", "squash", "fixup[_-(c|C)]", "edit", "reword", "drop",
+# "merge[_-(c|C)_<rev>]", or "bad" for an invalid command.
#
-# "<cmd> <lineno>" -- add a line with the specified command
-# ("pick", "squash", "fixup"|"fixup_-C"|"fixup_-c", "edit", "reword" or "drop")
-# and the SHA1 taken from the specified line.
+# "<lineno>" -- add a command, using the specified line as a template.
+# If the command has not been overridden, the line will be copied
+# verbatim, usually resulting in a "pick" line.
#
-# "_" -- add a space, like "fixup_-C" implies "fixup -C" and
-# "exec_cmd_with_args" add an "exec cmd with args" line.
+# "fakesha" -- add a command ("pick" by default), using a fake SHA1.
+#
+# "exec_[command...]", "break" -- add the specified command.
#
# "#" -- Add a comment line.
#
@@ -49,7 +52,7 @@ set_fake_editor () {
action=\&
for line in $FAKE_LINES; do
case $line in
- pick|p|squash|s|fixup|f|edit|e|reword|r|drop|d|label|l|reset|r|merge|m)
+ pick|p|squash|s|fixup|f|edit|e|reword|r|drop|d|label|l|reset|t|merge|m)
action="$line";;
exec_*|x_*|break|b)
echo "$line" | sed 's/_/ /g' >> "$1";;
@@ -60,11 +63,11 @@ set_fake_editor () {
">")
echo >> "$1";;
bad)
- action="badcmd";;
+ action="pickled";;
fakesha)
test \& != "$action" || action=pick
echo "$action XXXXXXX False commit" >> "$1"
- action=pick;;
+ action=\&;;
*)
sed -n "${line}s/^[a-z][a-z]*/$action/p" < "$1".tmp >> "$1"
action=\&;;
@@ -207,3 +210,22 @@ check_reworded_commits () {
>reword-log &&
test_cmp reword-expected reword-log
}
+
+# usage: set_replace_editor <file>
+#
+# Replace the todo file with the exact contents of the given file.
+# N.B. sets GIT_SEQUENCE_EDITOR rather than EDITOR so it can be
+# combined with set_fake_editor to reword commits and replace the
+# todo list
+set_replace_editor () {
+ cat >script <<-\EOF &&
+ cat FILENAME >"$1"
+
+ echo 'rebase -i script after editing:'
+ cat "$1"
+ EOF
+
+ sed -e "s/FILENAME/$1/g" script |
+ write_script fake-sequence-editor.sh &&
+ test_set_sequence_editor "$(pwd)/fake-sequence-editor.sh"
+}
diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh
index f7c7df0..36f767c 100644
--- a/t/lib-submodule-update.sh
+++ b/t/lib-submodule-update.sh
@@ -168,20 +168,16 @@ replace_gitfile_with_git_dir () {
# Note that this only supports submodules at the root level of the
# superproject, with the default name, i.e. same as its path.
test_git_directory_is_unchanged () {
- (
- cd ".git/modules/$1" &&
- # does core.worktree point at the right place?
- test "$(git config core.worktree)" = "../../../$1" &&
- # remove it temporarily before comparing, as
- # "$1/.git/config" lacks it...
- git config --unset core.worktree
- ) &&
+ # does core.worktree point at the right place?
+ echo "../../../$1" >expect &&
+ git -C ".git/modules/$1" config core.worktree >actual &&
+ test_cmp expect actual &&
+ # remove it temporarily before comparing, as
+ # "$1/.git/config" lacks it...
+ git -C ".git/modules/$1" config --unset core.worktree &&
diff -r ".git/modules/$1" "$1/.git" &&
- (
- # ... and then restore.
- cd ".git/modules/$1" &&
- git config core.worktree "../../../$1"
- )
+ # ... and then restore.
+ git -C ".git/modules/$1" config core.worktree "../../../$1"
}
test_git_directory_exists () {
@@ -189,7 +185,9 @@ test_git_directory_exists () {
if test -f sub1/.git
then
# does core.worktree point at the right place?
- test "$(git -C .git/modules/$1 config core.worktree)" = "../../../$1"
+ echo "../../../$1" >expect &&
+ git -C ".git/modules/$1" config core.worktree >actual &&
+ test_cmp expect actual
fi
}
@@ -197,6 +195,7 @@ test_git_directory_exists () {
# the submodule repo if it doesn't exist and configures the most problematic
# settings for diff.ignoreSubmodules.
prolog () {
+ test_config_global protocol.file.allow always &&
(test -d submodule_update_repo || create_lib_submodule_repo) &&
test_config_global diff.ignoreSubmodules all &&
test_config diff.ignoreSubmodules all
@@ -207,7 +206,7 @@ prolog () {
# should be updated to an existing commit.
reset_work_tree_to () {
rm -rf submodule_update &&
- git clone submodule_update_repo submodule_update &&
+ git clone --template= submodule_update_repo submodule_update &&
(
cd submodule_update &&
rm -rf sub1 &&
@@ -803,7 +802,7 @@ test_submodule_recursing_with_args_common () {
git branch -t no_submodule origin/no_submodule &&
$command no_submodule &&
test_superproject_content origin/no_submodule &&
- ! test_path_is_dir sub1 &&
+ test_path_is_missing sub1 &&
test_must_fail git config -f .git/modules/sub1/config core.worktree &&
test_must_fail git config -f .git/modules/sub1/modules/sub2/config core.worktree
)
@@ -831,7 +830,7 @@ test_submodule_recursing_with_args_common () {
cd submodule_update &&
git branch -t invalid_sub1 origin/invalid_sub1 &&
test_must_fail $command invalid_sub1 2>err &&
- test_i18ngrep sub1 err &&
+ test_grep sub1 err &&
test_superproject_content origin/add_sub1 &&
test_submodule_content sub1 origin/add_sub1
)
@@ -902,13 +901,14 @@ test_submodule_switch_recursing_with_args () {
'
# ... but an ignored file is fine.
test_expect_$RESULTOI "$command: added submodule removes an untracked ignored file" '
- test_when_finished "rm submodule_update/.git/info/exclude" &&
+ test_when_finished "rm -rf submodule_update/.git/info" &&
prolog &&
reset_work_tree_to_interested no_submodule &&
(
cd submodule_update &&
git branch -t add_sub1 origin/add_sub1 &&
: >sub1 &&
+ mkdir .git/info &&
echo sub1 >.git/info/exclude &&
$command add_sub1 &&
test_superproject_content origin/add_sub1 &&
@@ -951,7 +951,9 @@ test_submodule_switch_recursing_with_args () {
reset_work_tree_to_interested add_sub1 &&
(
cd submodule_update &&
+ rm -rf .git/modules/sub1/info &&
git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
+ mkdir .git/modules/sub1/info &&
echo ignored >.git/modules/sub1/info/exclude &&
: >sub1/ignored &&
$command replace_sub1_with_file &&
diff --git a/t/lib-subtest.sh b/t/lib-subtest.sh
new file mode 100644
index 0000000..56ee927
--- /dev/null
+++ b/t/lib-subtest.sh
@@ -0,0 +1,95 @@
+write_sub_test_lib_test () {
+ name="$1" # stdin is the body of the test code
+ mkdir "$name" &&
+ write_script "$name/$name.sh" "$TEST_SHELL_PATH" <<-EOF &&
+ test_description='A test of test-lib.sh itself'
+
+ # Point to the t/test-lib.sh, which isn't in ../ as usual
+ . "\$TEST_DIRECTORY"/test-lib.sh
+ EOF
+ cat >>"$name/$name.sh"
+}
+
+_run_sub_test_lib_test_common () {
+ cmp_op="$1" want_code="$2" name="$3" # stdin is the body of the test code
+ shift 3
+
+ # intercept pseudo-options at the front of the argument list that we
+ # will not pass to child script
+ skip=
+ while test $# -gt 0
+ do
+ case "$1" in
+ --skip=*)
+ skip=${1#--*=}
+ shift
+ ;;
+ *)
+ break
+ ;;
+ esac
+ done
+
+ (
+ cd "$name" &&
+
+ # Pretend we're not running under a test harness, whether we
+ # are or not. The test-lib output depends on the setting of
+ # this variable, so we need a stable setting under which to run
+ # the sub-test.
+ sane_unset HARNESS_ACTIVE &&
+
+ export TEST_DIRECTORY &&
+ # The child test re-sources GIT-BUILD-OPTIONS and may thus
+ # override the test output directory. We thus pass it as an
+ # explicit override to the child.
+ TEST_OUTPUT_DIRECTORY_OVERRIDE=$(pwd) &&
+ export TEST_OUTPUT_DIRECTORY_OVERRIDE &&
+ GIT_SKIP_TESTS=$skip &&
+ export GIT_SKIP_TESTS &&
+ sane_unset GIT_TEST_FAIL_PREREQS &&
+ ./"$name.sh" "$@" >out 2>err;
+ ret=$? &&
+ test "$ret" "$cmp_op" "$want_code"
+ )
+}
+
+write_and_run_sub_test_lib_test () {
+ name="$1" descr="$2" # stdin is the body of the test code
+ write_sub_test_lib_test "$@" || return 1
+ _run_sub_test_lib_test_common -eq 0 "$@"
+}
+
+write_and_run_sub_test_lib_test_err () {
+ name="$1" descr="$2" # stdin is the body of the test code
+ write_sub_test_lib_test "$@" || return 1
+ _run_sub_test_lib_test_common -eq 1 "$@"
+}
+
+run_sub_test_lib_test () {
+ _run_sub_test_lib_test_common -eq 0 "$@"
+}
+
+run_sub_test_lib_test_err () {
+ _run_sub_test_lib_test_common -eq 1 "$@"
+}
+
+_check_sub_test_lib_test_common () {
+ name="$1" &&
+ sed -e 's/^> //' -e 's/Z$//' >"$name"/expect.out &&
+ test_cmp "$name"/expect.out "$name"/out
+}
+
+check_sub_test_lib_test () {
+ name="$1" # stdin is the expected output from the test
+ _check_sub_test_lib_test_common "$name" &&
+ test_must_be_empty "$name"/err
+}
+
+check_sub_test_lib_test_err () {
+ name="$1" # stdin is the expected output from the test
+ _check_sub_test_lib_test_common "$name" &&
+ # expected error output is in descriptor 3
+ sed -e 's/^> //' -e 's/Z$//' <&3 >"$name"/expect.err &&
+ test_cmp "$name"/expect.err "$name"/err
+}
diff --git a/t/lib-sudo.sh b/t/lib-sudo.sh
new file mode 100644
index 0000000..b4d7788
--- /dev/null
+++ b/t/lib-sudo.sh
@@ -0,0 +1,15 @@
+# Helpers for running git commands under sudo.
+
+# Runs a scriplet passed through stdin under sudo.
+run_with_sudo () {
+ local ret
+ local RUN="$TEST_DIRECTORY/$$.sh"
+ write_script "$RUN" "$TEST_SHELL_PATH"
+ # avoid calling "$RUN" directly so sudo doesn't get a chance to
+ # override the shell, add aditional restrictions or even reject
+ # running the script because its security policy deem it unsafe
+ sudo "$TEST_SHELL_PATH" -c "\"$RUN\""
+ ret=$?
+ rm -f "$RUN"
+ return $ret
+}
diff --git a/t/lib-t3100.sh b/t/lib-t3100.sh
new file mode 100644
index 0000000..eabb5fd
--- /dev/null
+++ b/t/lib-t3100.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+setup_basic_ls_tree_data () {
+ mkdir dir &&
+ test_commit dir/sub-file &&
+ test_commit top-file &&
+ git clone . submodule &&
+ git submodule add ./submodule &&
+ git commit -m"add submodule"
+}
diff --git a/t/lib-unicode-nfc-nfd.sh b/t/lib-unicode-nfc-nfd.sh
new file mode 100755
index 0000000..2223224
--- /dev/null
+++ b/t/lib-unicode-nfc-nfd.sh
@@ -0,0 +1,162 @@
+# Help detect how Unicode NFC and NFD are handled on the filesystem.
+
+# A simple character that has a NFD form.
+#
+# NFC: U+00e9 LATIN SMALL LETTER E WITH ACUTE
+# UTF8(NFC): \xc3 \xa9
+#
+# NFD: U+0065 LATIN SMALL LETTER E
+# U+0301 COMBINING ACUTE ACCENT
+# UTF8(NFD): \x65 + \xcc \x81
+#
+utf8_nfc=$(printf "\xc3\xa9")
+utf8_nfd=$(printf "\x65\xcc\x81")
+
+# Is the OS or the filesystem "Unicode composition sensitive"?
+#
+# That is, does the OS or the filesystem allow files to exist with
+# both the NFC and NFD spellings? Or, does the OS/FS lie to us and
+# tell us that the NFC and NFD forms are equivalent.
+#
+# This is or may be independent of what type of filesystem we have,
+# since it might be handled by the OS at a layer above the FS.
+# Testing shows on MacOS using APFS, HFS+, and FAT32 reports a
+# collision, for example.
+#
+# This does not tell us how the Unicode pathname will be spelled
+# on disk, but rather only that the two spelling "collide". We
+# will examine the actual on disk spelling in a later prereq.
+#
+test_lazy_prereq UNICODE_COMPOSITION_SENSITIVE '
+ mkdir trial_${utf8_nfc} &&
+ mkdir trial_${utf8_nfd}
+'
+
+# Is the spelling of an NFC pathname preserved on disk?
+#
+# On MacOS with HFS+ and FAT32, NFC paths are converted into NFD
+# and on APFS, NFC paths are preserved. As we have established
+# above, this is independent of "composition sensitivity".
+#
+test_lazy_prereq UNICODE_NFC_PRESERVED '
+ mkdir c_${utf8_nfc} &&
+ ls | test-tool hexdump >dump &&
+ grep "63 5f c3 a9" dump
+'
+
+# Is the spelling of an NFD pathname preserved on disk?
+#
+test_lazy_prereq UNICODE_NFD_PRESERVED '
+ mkdir d_${utf8_nfd} &&
+ ls | test-tool hexdump >dump &&
+ grep "64 5f 65 cc 81" dump
+'
+
+# The following _DOUBLE_ forms are more for my curiosity,
+# but there may be quirks lurking when there are multiple
+# combining characters in non-canonical order.
+
+# Unicode also allows multiple combining characters
+# that can be decomposed in pieces.
+#
+# NFC: U+1f67 GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI
+# UTF8(NFC): \xe1 \xbd \xa7
+#
+# NFD1: U+1f61 GREEK SMALL LETTER OMEGA WITH DASIA
+# U+0342 COMBINING GREEK PERISPOMENI
+# UTF8(NFD1): \xe1 \xbd \xa1 + \xcd \x82
+#
+# But U+1f61 decomposes into
+# NFD2: U+03c9 GREEK SMALL LETTER OMEGA
+# U+0314 COMBINING REVERSED COMMA ABOVE
+# UTF8(NFD2): \xcf \x89 + \xcc \x94
+#
+# Yielding: \xcf \x89 + \xcc \x94 + \xcd \x82
+#
+# Note that I've used the canonical ordering of the
+# combinining characters. It is also possible to
+# swap them. My testing shows that that non-standard
+# ordering also causes a collision in mkdir. However,
+# the resulting names don't draw correctly on the
+# terminal (implying that the on-disk format also has
+# them out of order).
+#
+greek_nfc=$(printf "\xe1\xbd\xa7")
+greek_nfd1=$(printf "\xe1\xbd\xa1\xcd\x82")
+greek_nfd2=$(printf "\xcf\x89\xcc\x94\xcd\x82")
+
+# See if a double decomposition also collides.
+#
+test_lazy_prereq UNICODE_DOUBLE_COMPOSITION_SENSITIVE '
+ mkdir trial_${greek_nfc} &&
+ mkdir trial_${greek_nfd2}
+'
+
+# See if the NFC spelling appears on the disk.
+#
+test_lazy_prereq UNICODE_DOUBLE_NFC_PRESERVED '
+ mkdir c_${greek_nfc} &&
+ ls | test-tool hexdump >dump &&
+ grep "63 5f e1 bd a7" dump
+'
+
+# See if the NFD spelling appears on the disk.
+#
+test_lazy_prereq UNICODE_DOUBLE_NFD_PRESERVED '
+ mkdir d_${greek_nfd2} &&
+ ls | test-tool hexdump >dump &&
+ grep "64 5f cf 89 cc 94 cd 82" dump
+'
+
+# The following is for debugging. I found it useful when
+# trying to understand the various (OS, FS) quirks WRT
+# Unicode and how composition/decomposition is handled.
+# For example, when trying to understand how (macOS, APFS)
+# and (macOS, HFS) and (macOS, FAT32) compare.
+#
+# It is rather noisy, so it is disabled by default.
+#
+if test "$unicode_debug" = "true"
+then
+ if test_have_prereq UNICODE_COMPOSITION_SENSITIVE
+ then
+ echo NFC and NFD are distinct on this OS/filesystem.
+ else
+ echo NFC and NFD are aliases on this OS/filesystem.
+ fi
+
+ if test_have_prereq UNICODE_NFC_PRESERVED
+ then
+ echo NFC maintains original spelling.
+ else
+ echo NFC is modified.
+ fi
+
+ if test_have_prereq UNICODE_NFD_PRESERVED
+ then
+ echo NFD maintains original spelling.
+ else
+ echo NFD is modified.
+ fi
+
+ if test_have_prereq UNICODE_DOUBLE_COMPOSITION_SENSITIVE
+ then
+ echo DOUBLE NFC and NFD are distinct on this OS/filesystem.
+ else
+ echo DOUBLE NFC and NFD are aliases on this OS/filesystem.
+ fi
+
+ if test_have_prereq UNICODE_DOUBLE_NFC_PRESERVED
+ then
+ echo Double NFC maintains original spelling.
+ else
+ echo Double NFC is modified.
+ fi
+
+ if test_have_prereq UNICODE_DOUBLE_NFD_PRESERVED
+ then
+ echo Double NFD maintains original spelling.
+ else
+ echo Double NFD is modified.
+ fi
+fi
diff --git a/t/lib-unique-files.sh b/t/lib-unique-files.sh
new file mode 100644
index 0000000..a14080f
--- /dev/null
+++ b/t/lib-unique-files.sh
@@ -0,0 +1,34 @@
+# Helper to create files with unique contents
+
+# Create multiple files with unique contents within this test run. Takes the
+# number of directories, the number of files in each directory, and the base
+# directory.
+#
+# test_create_unique_files 2 3 my_dir -- Creates 2 directories with 3 files
+# each in my_dir, all with contents
+# different from previous invocations
+# of this command in this run.
+
+test_create_unique_files () {
+ test "$#" -ne 3 && BUG "3 param"
+
+ local dirs="$1" &&
+ local files="$2" &&
+ local basedir="$3" &&
+ local counter="0" &&
+ local i &&
+ local j &&
+ test_tick &&
+ local basedata="$basedir$test_tick" &&
+ rm -rf "$basedir" &&
+ for i in $(test_seq $dirs)
+ do
+ local dir="$basedir/dir$i" &&
+ mkdir -p "$dir" &&
+ for j in $(test_seq $files)
+ do
+ counter=$((counter + 1)) &&
+ echo "$basedata.$counter">"$dir/file$j.txt"
+ done
+ done
+}
diff --git a/t/oid-info/hash-info b/t/oid-info/hash-info
index d0736dd..b8a5bcb 100644
--- a/t/oid-info/hash-info
+++ b/t/oid-info/hash-info
@@ -15,3 +15,15 @@ empty_blob sha256:473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a3037218
empty_tree sha1:4b825dc642cb6eb9a060e54bf8d69288fbee4904
empty_tree sha256:6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321
+
+blob17_1 sha1:263
+blob17_1 sha256:34
+
+blob17_2 sha1:410
+blob17_2 sha256:174
+
+blob17_3 sha1:523
+blob17_3 sha256:313
+
+blob17_4 sha1:790
+blob17_4 sha256:481
diff --git a/t/oid-info/oid b/t/oid-info/oid
index a754970..7547d2c 100644
--- a/t/oid-info/oid
+++ b/t/oid-info/oid
@@ -27,3 +27,5 @@ numeric sha1:0123456789012345678901234567890123456789
numeric sha256:0123456789012345678901234567890123456789012345678901234567890123
deadbeef sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef
deadbeef sha256:deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef
+deadbeef_short sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbee
+deadbeef_short sha256:deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbee
diff --git a/t/perf/Makefile b/t/perf/Makefile
index 2465770..e4808ae 100644
--- a/t/perf/Makefile
+++ b/t/perf/Makefile
@@ -1,3 +1,6 @@
+# Import tree-wide shared Makefile behavior and libraries
+include ../../shared.mak
+
-include ../../config.mak
export GIT_TEST_OPTIONS
diff --git a/t/perf/README b/t/perf/README
index fb9127a..8f217d7 100644
--- a/t/perf/README
+++ b/t/perf/README
@@ -95,6 +95,10 @@ You can set the following variables (also in your config.mak):
Git (e.g., performance of index-pack as the number of threads
changes). These can be enabled with GIT_PERF_EXTRA.
+ GIT_PERF_USE_SCALAR
+ Boolean indicating whether to register test repo(s) with Scalar
+ before executing tests.
+
You can also pass the options taken by ordinary git tests; the most
useful one is:
diff --git a/t/perf/aggregate.perl b/t/perf/aggregate.perl
index 82c0df4..575d200 100755
--- a/t/perf/aggregate.perl
+++ b/t/perf/aggregate.perl
@@ -17,8 +17,8 @@ sub get_times {
my $rt = ((defined $1 ? $1 : 0.0)*60+$2)*60+$3;
return ($rt, $4, $5);
# size
- } elsif ($line =~ /^\d+$/) {
- return $&;
+ } elsif ($line =~ /^\s*(\d+)$/) {
+ return $1;
} else {
die "bad input line: $line";
}
diff --git a/t/perf/config b/t/perf/config
new file mode 100644
index 0000000..b92768b
--- /dev/null
+++ b/t/perf/config
@@ -0,0 +1,2 @@
+[gc]
+ auto = 0
diff --git a/t/perf/lib-bitmap.sh b/t/perf/lib-bitmap.sh
new file mode 100644
index 0000000..55a8feb
--- /dev/null
+++ b/t/perf/lib-bitmap.sh
@@ -0,0 +1,100 @@
+# Helper functions for testing bitmap performance; see p5310.
+
+test_full_bitmap () {
+ test_perf 'simulated clone' '
+ git pack-objects --stdout --all </dev/null >/dev/null
+ '
+
+ test_perf 'simulated fetch' '
+ have=$(git rev-list HEAD~100 -1) &&
+ {
+ echo HEAD &&
+ echo ^$have
+ } | git pack-objects --revs --stdout >/dev/null
+ '
+
+ test_perf 'pack to file (bitmap)' '
+ git pack-objects --use-bitmap-index --all pack1b </dev/null >/dev/null
+ '
+
+ test_perf 'rev-list (commits)' '
+ git rev-list --all --use-bitmap-index >/dev/null
+ '
+
+ test_perf 'rev-list (objects)' '
+ git rev-list --all --use-bitmap-index --objects >/dev/null
+ '
+
+ test_perf 'rev-list with tag negated via --not --all (objects)' '
+ git rev-list perf-tag --not --all --use-bitmap-index --objects >/dev/null
+ '
+
+ test_perf 'rev-list with negative tag (objects)' '
+ git rev-list HEAD --not perf-tag --use-bitmap-index --objects >/dev/null
+ '
+
+ test_perf 'rev-list count with blob:none' '
+ git rev-list --use-bitmap-index --count --objects --all \
+ --filter=blob:none >/dev/null
+ '
+
+ test_perf 'rev-list count with blob:limit=1k' '
+ git rev-list --use-bitmap-index --count --objects --all \
+ --filter=blob:limit=1k >/dev/null
+ '
+
+ test_perf 'rev-list count with tree:0' '
+ git rev-list --use-bitmap-index --count --objects --all \
+ --filter=tree:0 >/dev/null
+ '
+
+ test_perf 'simulated partial clone' '
+ git pack-objects --stdout --all --filter=blob:none </dev/null >/dev/null
+ '
+}
+
+test_partial_bitmap () {
+ test_perf 'clone (partial bitmap)' '
+ git pack-objects --stdout --all </dev/null >/dev/null
+ '
+
+ test_perf 'pack to file (partial bitmap)' '
+ git pack-objects --use-bitmap-index --all pack2b </dev/null >/dev/null
+ '
+
+ test_perf 'rev-list with tree filter (partial bitmap)' '
+ git rev-list --use-bitmap-index --count --objects --all \
+ --filter=tree:0 >/dev/null
+ '
+}
+
+test_pack_bitmap () {
+ test_perf "repack to disk" '
+ git repack -ad
+ '
+
+ test_full_bitmap
+
+ test_expect_success "create partial bitmap state" '
+ # pick a commit to represent the repo tip in the past
+ cutoff=$(git rev-list HEAD~100 -1) &&
+ orig_tip=$(git rev-parse HEAD) &&
+
+ # now kill off all of the refs and pretend we had
+ # just the one tip
+ rm -rf .git/logs .git/refs/* .git/packed-refs &&
+ git update-ref HEAD $cutoff &&
+
+ # and then repack, which will leave us with a nice
+ # big bitmap pack of the "old" history, and all of
+ # the new history will be loose, as if it had been pushed
+ # up incrementally and exploded via unpack-objects
+ git repack -Ad &&
+
+ # and now restore our original tip, as if the pushes
+ # had happened
+ git update-ref HEAD $orig_tip
+ '
+
+ test_partial_bitmap
+}
diff --git a/t/perf/p0004-lazy-init-name-hash.sh b/t/perf/p0004-lazy-init-name-hash.sh
index 1afc08f..85be14e 100755
--- a/t/perf/p0004-lazy-init-name-hash.sh
+++ b/t/perf/p0004-lazy-init-name-hash.sh
@@ -49,7 +49,7 @@ test_perf "single-threaded, $desc" "
test-tool lazy-init-name-hash --single --count=$count
"
-test_perf REPO_BIG_ENOUGH_FOR_MULTI "multi-threaded, $desc" "
+test_perf "multi-threaded, $desc" --prereq REPO_BIG_ENOUGH_FOR_MULTI "
test-tool lazy-init-name-hash --multi --count=$count
"
diff --git a/t/perf/p0005-status.sh b/t/perf/p0005-status.sh
index 0b0aa98..ca58d6c 100755
--- a/t/perf/p0005-status.sh
+++ b/t/perf/p0005-status.sh
@@ -24,17 +24,17 @@ test_perf_default_repo
test_expect_success "setup repo" '
if git rev-parse --verify refs/heads/p0006-ballast^{commit}
then
- echo Assuming synthetic repo from many-files.sh
- git branch br_base master
- git branch br_ballast p0006-ballast
- git config --local core.sparsecheckout 1
+ echo Assuming synthetic repo from many-files.sh &&
+ git branch br_base master &&
+ git branch br_ballast p0006-ballast &&
+ git config --local core.sparsecheckout 1 &&
cat >.git/info/sparse-checkout <<-EOF
/*
!ballast/*
EOF
else
- echo Assuming non-synthetic repo...
- git branch br_base $(git rev-list HEAD | tail -n 1)
+ echo Assuming non-synthetic repo... &&
+ git branch br_base $(git rev-list HEAD | tail -n 1) &&
git branch br_ballast HEAD
fi &&
git checkout -q br_ballast &&
diff --git a/t/perf/p0006-read-tree-checkout.sh b/t/perf/p0006-read-tree-checkout.sh
index 78cc23f..325566e 100755
--- a/t/perf/p0006-read-tree-checkout.sh
+++ b/t/perf/p0006-read-tree-checkout.sh
@@ -24,21 +24,21 @@ test_perf_default_repo
test_expect_success "setup repo" '
if git rev-parse --verify refs/heads/p0006-ballast^{commit}
then
- echo Assuming synthetic repo from many-files.sh
- git branch br_base master
- git branch br_ballast p0006-ballast^
- git branch br_ballast_alias p0006-ballast^
- git branch br_ballast_plus_1 p0006-ballast
- git config --local core.sparsecheckout 1
+ echo Assuming synthetic repo from many-files.sh &&
+ git branch br_base master &&
+ git branch br_ballast p0006-ballast^ &&
+ git branch br_ballast_alias p0006-ballast^ &&
+ git branch br_ballast_plus_1 p0006-ballast &&
+ git config --local core.sparsecheckout 1 &&
cat >.git/info/sparse-checkout <<-EOF
/*
!ballast/*
EOF
else
- echo Assuming non-synthetic repo...
- git branch br_base $(git rev-list HEAD | tail -n 1)
- git branch br_ballast HEAD^ || error "no ancestor commit from current head"
- git branch br_ballast_alias HEAD^
+ echo Assuming non-synthetic repo... &&
+ git branch br_base $(git rev-list HEAD | tail -n 1) &&
+ git branch br_ballast HEAD^ || error "no ancestor commit from current head" &&
+ git branch br_ballast_alias HEAD^ &&
git branch br_ballast_plus_1 HEAD
fi &&
git checkout -q br_ballast &&
@@ -46,7 +46,15 @@ test_expect_success "setup repo" '
'
test_perf "read-tree br_base br_ballast ($nr_files)" '
- git read-tree -m br_base br_ballast -n
+ git read-tree -n -m br_base br_ballast
+'
+
+test_perf "read-tree br_ballast_plus_1 ($nr_files)" '
+ # Run read-tree 100 times for clearer performance results & comparisons
+ for i in $(test_seq 100)
+ do
+ git read-tree -n -m br_ballast_plus_1 || return 1
+ done
'
test_perf "switch between br_base br_ballast ($nr_files)" '
diff --git a/t/perf/p0007-write-cache.sh b/t/perf/p0007-write-cache.sh
index 0959526..25d8ff7 100755
--- a/t/perf/p0007-write-cache.sh
+++ b/t/perf/p0007-write-cache.sh
@@ -9,8 +9,8 @@ test_perf_default_repo
test_expect_success "setup repo" '
if git rev-parse --verify refs/heads/p0006-ballast^{commit}
then
- echo Assuming synthetic repo from many-files.sh
- git config --local core.sparsecheckout 1
+ echo Assuming synthetic repo from many-files.sh &&
+ git config --local core.sparsecheckout 1 &&
cat >.git/info/sparse-checkout <<-EOF
/*
!ballast/*
diff --git a/t/perf/p0008-odb-fsync.sh b/t/perf/p0008-odb-fsync.sh
new file mode 100755
index 0000000..b3a90f3
--- /dev/null
+++ b/t/perf/p0008-odb-fsync.sh
@@ -0,0 +1,82 @@
+#!/bin/sh
+#
+# This test measures the performance of adding new files to the object
+# database. The test was originally added to measure the effect of the
+# core.fsyncMethod=batch mode, which is why we are testing different values of
+# that setting explicitly and creating a lot of unique objects.
+
+test_description="Tests performance of adding things to the object database"
+
+. ./perf-lib.sh
+
+. $TEST_DIRECTORY/lib-unique-files.sh
+
+test_perf_fresh_repo
+test_checkout_worktree
+
+dir_count=10
+files_per_dir=50
+total_files=$((dir_count * files_per_dir))
+
+populate_files () {
+ test_create_unique_files $dir_count $files_per_dir files
+}
+
+setup_repo () {
+ (rm -rf .git || 1) &&
+ git init &&
+ test_commit first &&
+ populate_files
+}
+
+test_perf_fsync_cfgs () {
+ local method &&
+ local cfg &&
+ for method in none fsync batch writeout-only
+ do
+ case $method in
+ none)
+ cfg="-c core.fsync=none"
+ ;;
+ *)
+ cfg="-c core.fsync=loose-object -c core.fsyncMethod=$method"
+ esac &&
+
+ # Set GIT_TEST_FSYNC=1 explicitly since fsync is normally
+ # disabled by t/test-lib.sh.
+ if ! test_perf "$1 (fsyncMethod=$method)" \
+ --setup "$2" \
+ "GIT_TEST_FSYNC=1 git $cfg $3"
+ then
+ break
+ fi
+ done
+}
+
+test_perf_fsync_cfgs "add $total_files files" \
+ "setup_repo" \
+ "add -- files"
+
+test_perf_fsync_cfgs "stash $total_files files" \
+ "setup_repo" \
+ "stash push -u -- files"
+
+test_perf_fsync_cfgs "unpack $total_files files" \
+ "
+ setup_repo &&
+ git -c core.fsync=none add -- files &&
+ git -c core.fsync=none commit -q -m second &&
+ echo HEAD | git pack-objects -q --stdout --revs >test_pack.pack &&
+ setup_repo
+ " \
+ "unpack-objects -q <test_pack.pack"
+
+test_perf_fsync_cfgs "commit $total_files files" \
+ "
+ setup_repo &&
+ git -c core.fsync=none add -- files &&
+ populate_files
+ " \
+ "commit -q -a -m test"
+
+test_done
diff --git a/t/perf/p0071-sort.sh b/t/perf/p0071-sort.sh
index 6e924f5..ae4ddac 100755
--- a/t/perf/p0071-sort.sh
+++ b/t/perf/p0071-sort.sh
@@ -11,16 +11,42 @@ test_expect_success 'setup' '
git cat-file --batch >unsorted
'
-test_perf 'sort(1)' '
- sort <unsorted >expect
+test_perf 'sort(1) unsorted' '
+ sort <unsorted >sorted
'
-test_perf 'string_list_sort()' '
- test-tool string-list sort <unsorted >actual
+test_expect_success 'reverse' '
+ sort -r <unsorted >reversed
'
-test_expect_success 'string_list_sort() sorts like sort(1)' '
- test_cmp_bin expect actual
-'
+for file in sorted reversed
+do
+ test_perf "sort(1) $file" "
+ sort <$file >actual
+ "
+done
+
+for file in unsorted sorted reversed
+do
+
+ test_perf "string_list_sort() $file" "
+ test-tool string-list sort <$file >actual
+ "
+
+ test_expect_success "string_list_sort() $file sorts like sort(1)" "
+ test_cmp_bin sorted actual
+ "
+done
+
+for file in unsorted sorted reversed
+do
+ test_perf "DEFINE_LIST_SORT $file" "
+ test-tool mergesort sort <$file >actual
+ "
+
+ test_expect_success "DEFINE_LIST_SORT $file sorts like sort(1)" "
+ test_cmp_bin sorted actual
+ "
+done
test_done
diff --git a/t/perf/p0090-cache-tree.sh b/t/perf/p0090-cache-tree.sh
new file mode 100755
index 0000000..a8eabca
--- /dev/null
+++ b/t/perf/p0090-cache-tree.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+test_description="Tests performance of cache tree update operations"
+
+. ./perf-lib.sh
+
+test_perf_large_repo
+test_checkout_worktree
+
+count=100
+
+test_expect_success 'setup cache tree' '
+ git write-tree
+'
+
+test_cache_tree () {
+ test_perf "$1, $3" "
+ for i in \$(test_seq $count)
+ do
+ test-tool cache-tree $4 $2
+ done
+ "
+}
+
+test_cache_tree_update_functions () {
+ test_cache_tree 'no-op' 'control' "$1" "$2"
+ test_cache_tree 'prime_cache_tree' 'prime' "$1" "$2"
+ test_cache_tree 'cache_tree_update' 'update' "$1" "$2"
+}
+
+test_cache_tree_update_functions "clean" ""
+test_cache_tree_update_functions "invalidate 2" "--invalidate 2"
+test_cache_tree_update_functions "invalidate 50" "--invalidate 50"
+test_cache_tree_update_functions "empty" "--empty"
+
+test_done
diff --git a/t/perf/p0100-globbing.sh b/t/perf/p0100-globbing.sh
index dd18a9c..439e9c8 100755
--- a/t/perf/p0100-globbing.sh
+++ b/t/perf/p0100-globbing.sh
@@ -19,9 +19,9 @@ test_expect_success 'setup' '
printf "a" >>refname &&
for j in $(test_seq 1 $i)
do
- printf "a*" >>refglob.$i
+ printf "a*" >>refglob.$i || return 1
done &&
- echo b >>refglob.$i
+ echo b >>refglob.$i || return 1
done &&
test_commit test $(cat refname).t "" $(cat refname).t
'
diff --git a/t/perf/p1006-cat-file.sh b/t/perf/p1006-cat-file.sh
new file mode 100755
index 0000000..dcd8015
--- /dev/null
+++ b/t/perf/p1006-cat-file.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+test_description='Tests listing object info performance'
+. ./perf-lib.sh
+
+test_perf_large_repo
+
+test_perf 'cat-file --batch-check' '
+ git cat-file --batch-all-objects --batch-check
+'
+
+test_done
diff --git a/t/perf/p1400-update-ref.sh b/t/perf/p1400-update-ref.sh
index dda8a74..a75969c 100755
--- a/t/perf/p1400-update-ref.sh
+++ b/t/perf/p1400-update-ref.sh
@@ -13,7 +13,7 @@ test_expect_success "setup" '
do
printf "start\ncreate refs/heads/%d PRE\ncommit\n" $i &&
printf "start\nupdate refs/heads/%d POST PRE\ncommit\n" $i &&
- printf "start\ndelete refs/heads/%d POST\ncommit\n" $i
+ printf "start\ndelete refs/heads/%d POST\ncommit\n" $i || return 1
done >instructions
'
@@ -22,7 +22,7 @@ test_perf "update-ref" '
do
git update-ref refs/heads/branch PRE &&
git update-ref refs/heads/branch POST PRE &&
- git update-ref -d refs/heads/branch
+ git update-ref -d refs/heads/branch || return 1
done
'
diff --git a/t/perf/p1451-fsck-skip-list.sh b/t/perf/p1451-fsck-skip-list.sh
index c2b97d2..f767d83 100755
--- a/t/perf/p1451-fsck-skip-list.sh
+++ b/t/perf/p1451-fsck-skip-list.sh
@@ -15,7 +15,7 @@ test_expect_success "setup $n bad commits" '
echo "committer C <c@example.com> 1234567890 +0000" &&
echo "data <<EOF" &&
echo "$i.Q." &&
- echo "EOF"
+ echo "EOF" || return 1
done | q_to_nul | git fast-import
'
diff --git a/t/perf/p1500-graph-walks.sh b/t/perf/p1500-graph-walks.sh
new file mode 100755
index 0000000..e14e762
--- /dev/null
+++ b/t/perf/p1500-graph-walks.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+test_description='Commit walk performance tests'
+. ./perf-lib.sh
+
+test_perf_large_repo
+
+test_expect_success 'setup' '
+ git for-each-ref --format="%(refname)" "refs/heads/*" "refs/tags/*" >allrefs &&
+ sort -r allrefs | head -n 50 >refs &&
+ for ref in $(cat refs)
+ do
+ git branch -f ref-$ref $ref &&
+ echo ref-$ref ||
+ return 1
+ done >branches &&
+ for ref in $(cat refs)
+ do
+ git tag -f tag-$ref $ref &&
+ echo tag-$ref ||
+ return 1
+ done >tags &&
+ git commit-graph write --reachable
+'
+
+test_perf 'ahead-behind counts: git for-each-ref' '
+ git for-each-ref --format="%(ahead-behind:HEAD)" --stdin <refs
+'
+
+test_perf 'ahead-behind counts: git branch' '
+ xargs git branch -l --format="%(ahead-behind:HEAD)" <branches
+'
+
+test_perf 'ahead-behind counts: git tag' '
+ xargs git tag -l --format="%(ahead-behind:HEAD)" <tags
+'
+
+test_perf 'contains: git for-each-ref --merged' '
+ git for-each-ref --merged=HEAD --stdin <refs
+'
+
+test_perf 'contains: git branch --merged' '
+ xargs git branch --merged=HEAD <branches
+'
+
+test_perf 'contains: git tag --merged' '
+ xargs git tag --merged=HEAD <tags
+'
+
+test_done
diff --git a/t/perf/p2000-sparse-operations.sh b/t/perf/p2000-sparse-operations.sh
index 5976262..39e92b0 100755
--- a/t/perf/p2000-sparse-operations.sh
+++ b/t/perf/p2000-sparse-operations.sh
@@ -43,6 +43,7 @@ test_expect_success 'setup repo and indexes' '
done &&
git sparse-checkout init --cone &&
+ git tag -a v1.0 -m "Final" &&
git sparse-checkout set $SPARSE_CONE &&
git checkout -b wide $OLD_COMMIT &&
@@ -106,9 +107,33 @@ test_perf_on_all () {
}
test_perf_on_all git status
+test_perf_on_all 'git stash && git stash pop'
+test_perf_on_all 'echo >>new && git stash -u && git stash pop'
test_perf_on_all git add -A
test_perf_on_all git add .
test_perf_on_all git commit -a -m A
test_perf_on_all git checkout -f -
+test_perf_on_all "git sparse-checkout add f2/f3/f1 && git sparse-checkout set $SPARSE_CONE"
+test_perf_on_all git reset
+test_perf_on_all git reset --hard
+test_perf_on_all git reset -- does-not-exist
+test_perf_on_all git diff
+test_perf_on_all git diff --cached
+test_perf_on_all git blame $SPARSE_CONE/a
+test_perf_on_all git blame $SPARSE_CONE/f3/a
+test_perf_on_all git read-tree -mu HEAD
+test_perf_on_all git checkout-index -f --all
+test_perf_on_all git update-index --add --remove $SPARSE_CONE/a
+test_perf_on_all "git rm -f $SPARSE_CONE/a && git checkout HEAD -- $SPARSE_CONE/a"
+test_perf_on_all git grep --cached bogus -- "f2/f1/f1/*"
+test_perf_on_all git write-tree
+test_perf_on_all git describe --dirty
+test_perf_on_all 'echo >>new && git describe --dirty'
+test_perf_on_all git diff-files
+test_perf_on_all git diff-files -- $SPARSE_CONE/a
+test_perf_on_all git diff-tree HEAD
+test_perf_on_all git diff-tree HEAD -- $SPARSE_CONE/a
+test_perf_on_all "git worktree add ../temp && git worktree remove ../temp"
+test_perf_on_all git check-attr -a -- $SPARSE_CONE/a
test_done
diff --git a/t/perf/p3400-rebase.sh b/t/perf/p3400-rebase.sh
index 7a0bb29..e6b0277 100755
--- a/t/perf/p3400-rebase.sh
+++ b/t/perf/p3400-rebase.sh
@@ -18,11 +18,11 @@ test_expect_success 'setup rebasing on top of a lot of changes' '
test_tick &&
git commit -m commit$i unrelated-file$i &&
echo change$i >unrelated-file$i &&
- test_seq 1000 | tac >>unrelated-file$i &&
+ test_seq 1000 | sort -nr >>unrelated-file$i &&
git add unrelated-file$i &&
test_tick &&
git commit -m commit$i-reverse unrelated-file$i ||
- break
+ return 1
done &&
git checkout to-rebase &&
test_commit our-patch interesting-file
diff --git a/t/perf/p4002-diff-color-moved.sh b/t/perf/p4002-diff-color-moved.sh
new file mode 100755
index 0000000..ab2af93
--- /dev/null
+++ b/t/perf/p4002-diff-color-moved.sh
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+test_description='Tests diff --color-moved performance'
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+# The endpoints of the diff can be customized by setting TEST_REV_A
+# and TEST_REV_B in the environment when running this test.
+
+rev="${TEST_REV_A:-v2.28.0}"
+if ! rev_a="$(git rev-parse --quiet --verify "$rev")"
+then
+ skip_all="skipping because '$rev' was not found. \
+ Use TEST_REV_A and TEST_REV_B to set the revs to use"
+ test_done
+fi
+rev="${TEST_REV_B:-v2.29.0}"
+if ! rev_b="$(git rev-parse --quiet --verify "$rev")"
+then
+ skip_all="skipping because '$rev' was not found. \
+ Use TEST_REV_A and TEST_REV_B to set the revs to use"
+ test_done
+fi
+
+GIT_PAGER_IN_USE=1
+test_export GIT_PAGER_IN_USE rev_a rev_b
+
+test_perf 'diff --no-color-moved --no-color-moved-ws large change' '
+ git diff --no-color-moved --no-color-moved-ws $rev_a $rev_b
+'
+
+test_perf 'diff --color-moved --no-color-moved-ws large change' '
+ git diff --color-moved=zebra --no-color-moved-ws $rev_a $rev_b
+'
+
+test_perf 'diff --color-moved-ws=allow-indentation-change large change' '
+ git diff --color-moved=zebra --color-moved-ws=allow-indentation-change \
+ $rev_a $rev_b
+'
+
+test_perf 'log --no-color-moved --no-color-moved-ws' '
+ git log --no-color-moved --no-color-moved-ws --no-merges --patch \
+ -n1000 $rev_b
+'
+
+test_perf 'log --color-moved --no-color-moved-ws' '
+ git log --color-moved=zebra --no-color-moved-ws --no-merges --patch \
+ -n1000 $rev_b
+'
+
+test_perf 'log --color-moved-ws=allow-indentation-change' '
+ git log --color-moved=zebra --color-moved-ws=allow-indentation-change \
+ --no-merges --patch -n1000 $rev_b
+'
+
+test_done
diff --git a/t/perf/p4220-log-grep-engines.sh b/t/perf/p4220-log-grep-engines.sh
index 2bc47de..03fbfbb 100755
--- a/t/perf/p4220-log-grep-engines.sh
+++ b/t/perf/p4220-log-grep-engines.sh
@@ -36,7 +36,8 @@ do
else
prereq=""
fi
- test_perf $prereq "$engine log$GIT_PERF_4220_LOG_OPTS --grep='$pattern'" "
+ test_perf "$engine log$GIT_PERF_4220_LOG_OPTS --grep='$pattern'" \
+ --prereq "$prereq" "
git -c grep.patternType=$engine log --pretty=format:%h$GIT_PERF_4220_LOG_OPTS --grep='$pattern' >'out.$engine' || :
"
done
diff --git a/t/perf/p4221-log-grep-engines-fixed.sh b/t/perf/p4221-log-grep-engines-fixed.sh
index 0609712..0a6d6df 100755
--- a/t/perf/p4221-log-grep-engines-fixed.sh
+++ b/t/perf/p4221-log-grep-engines-fixed.sh
@@ -26,7 +26,8 @@ do
else
prereq=""
fi
- test_perf $prereq "$engine log$GIT_PERF_4221_LOG_OPTS --grep='$pattern'" "
+ test_perf "$engine log$GIT_PERF_4221_LOG_OPTS --grep='$pattern'" \
+ --prereq "$prereq" "
git -c grep.patternType=$engine log --pretty=format:%h$GIT_PERF_4221_LOG_OPTS --grep='$pattern' >'out.$engine' || :
"
done
diff --git a/t/perf/p5302-pack-index.sh b/t/perf/p5302-pack-index.sh
index 228593d..14c601b 100755
--- a/t/perf/p5302-pack-index.sh
+++ b/t/perf/p5302-pack-index.sh
@@ -21,14 +21,13 @@ test_expect_success 'set up thread-counting tests' '
threads= &&
while test $t -gt 0
do
- threads="$t $threads"
- t=$((t / 2))
+ threads="$t $threads" &&
+ t=$((t / 2)) || return 1
done
'
-test_perf PERF_EXTRA 'index-pack 0 threads' '
- rm -rf repo.git &&
- git init --bare repo.git &&
+test_perf 'index-pack 0 threads' --prereq PERF_EXTRA \
+ --setup 'rm -rf repo.git && git init --bare repo.git' '
GIT_DIR=repo.git git index-pack --threads=1 --stdin < $PACK
'
@@ -36,17 +35,15 @@ for t in $threads
do
THREADS=$t
export THREADS
- test_perf PERF_EXTRA "index-pack $t threads" '
- rm -rf repo.git &&
- git init --bare repo.git &&
+ test_perf "index-pack $t threads" --prereq PERF_EXTRA \
+ --setup 'rm -rf repo.git && git init --bare repo.git' '
GIT_DIR=repo.git GIT_FORCE_THREADS=1 \
git index-pack --threads=$THREADS --stdin <$PACK
'
done
-test_perf 'index-pack default number of threads' '
- rm -rf repo.git &&
- git init --bare repo.git &&
+test_perf 'index-pack default number of threads' \
+ --setup 'rm -rf repo.git && git init --bare repo.git' '
GIT_DIR=repo.git git index-pack --stdin < $PACK
'
diff --git a/t/perf/p5303-many-packs.sh b/t/perf/p5303-many-packs.sh
index 35c0cbd..af173a7 100755
--- a/t/perf/p5303-many-packs.sh
+++ b/t/perf/p5303-many-packs.sh
@@ -126,11 +126,11 @@ done
# Measure pack loading with 10,000 packs.
test_expect_success 'generate lots of packs' '
for i in $(test_seq 10000); do
- echo "blob"
- echo "data <<EOF"
- echo "blob $i"
- echo "EOF"
- echo "checkpoint"
+ echo "blob" &&
+ echo "data <<EOF" &&
+ echo "blob $i" &&
+ echo "EOF" &&
+ echo "checkpoint" || return 1
done |
git -c fastimport.unpackLimit=0 fast-import
'
diff --git a/t/perf/p5310-pack-bitmaps.sh b/t/perf/p5310-pack-bitmaps.sh
index 452be01..b1399f1 100755
--- a/t/perf/p5310-pack-bitmaps.sh
+++ b/t/perf/p5310-pack-bitmaps.sh
@@ -2,112 +2,39 @@
test_description='Tests pack performance using bitmaps'
. ./perf-lib.sh
-
-test_perf_large_repo
-
-# note that we do everything through config,
-# since we want to be able to compare bitmap-aware
-# git versus non-bitmap git
-#
-# We intentionally use the deprecated pack.writebitmaps
-# config so that we can test against older versions of git.
-test_expect_success 'setup bitmap config' '
- git config pack.writebitmaps true
-'
-
-# we need to create the tag up front such that it is covered by the repack and
-# thus by generated bitmaps.
-test_expect_success 'create tags' '
- git tag --message="tag pointing to HEAD" perf-tag HEAD
-'
-
-test_perf 'repack to disk' '
- git repack -ad
-'
-
-test_perf 'simulated clone' '
- git pack-objects --stdout --all </dev/null >/dev/null
-'
-
-test_perf 'simulated fetch' '
- have=$(git rev-list HEAD~100 -1) &&
- {
- echo HEAD &&
- echo ^$have
- } | git pack-objects --revs --stdout >/dev/null
-'
-
-test_perf 'pack to file (bitmap)' '
- git pack-objects --use-bitmap-index --all pack1b </dev/null >/dev/null
-'
-
-test_perf 'rev-list (commits)' '
- git rev-list --all --use-bitmap-index >/dev/null
-'
-
-test_perf 'rev-list (objects)' '
- git rev-list --all --use-bitmap-index --objects >/dev/null
-'
-
-test_perf 'rev-list with tag negated via --not --all (objects)' '
- git rev-list perf-tag --not --all --use-bitmap-index --objects >/dev/null
-'
-
-test_perf 'rev-list with negative tag (objects)' '
- git rev-list HEAD --not perf-tag --use-bitmap-index --objects >/dev/null
-'
-
-test_perf 'rev-list count with blob:none' '
- git rev-list --use-bitmap-index --count --objects --all \
- --filter=blob:none >/dev/null
-'
-
-test_perf 'rev-list count with blob:limit=1k' '
- git rev-list --use-bitmap-index --count --objects --all \
- --filter=blob:limit=1k >/dev/null
-'
-
-test_perf 'rev-list count with tree:0' '
- git rev-list --use-bitmap-index --count --objects --all \
- --filter=tree:0 >/dev/null
-'
-
-test_perf 'simulated partial clone' '
- git pack-objects --stdout --all --filter=blob:none </dev/null >/dev/null
-'
-
-test_expect_success 'create partial bitmap state' '
- # pick a commit to represent the repo tip in the past
- cutoff=$(git rev-list HEAD~100 -1) &&
- orig_tip=$(git rev-parse HEAD) &&
-
- # now kill off all of the refs and pretend we had
- # just the one tip
- rm -rf .git/logs .git/refs/* .git/packed-refs &&
- git update-ref HEAD $cutoff &&
-
- # and then repack, which will leave us with a nice
- # big bitmap pack of the "old" history, and all of
- # the new history will be loose, as if it had been pushed
- # up incrementally and exploded via unpack-objects
- git repack -Ad &&
-
- # and now restore our original tip, as if the pushes
- # had happened
- git update-ref HEAD $orig_tip
-'
-
-test_perf 'clone (partial bitmap)' '
- git pack-objects --stdout --all </dev/null >/dev/null
-'
-
-test_perf 'pack to file (partial bitmap)' '
- git pack-objects --use-bitmap-index --all pack2b </dev/null >/dev/null
-'
-
-test_perf 'rev-list with tree filter (partial bitmap)' '
- git rev-list --use-bitmap-index --count --objects --all \
- --filter=tree:0 >/dev/null
-'
+. "${TEST_DIRECTORY}/perf/lib-bitmap.sh"
+
+test_lookup_pack_bitmap () {
+ test_expect_success 'start the test from scratch' '
+ rm -rf * .git
+ '
+
+ test_perf_large_repo
+
+ # note that we do everything through config,
+ # since we want to be able to compare bitmap-aware
+ # git versus non-bitmap git
+ #
+ # We intentionally use the deprecated pack.writebitmaps
+ # config so that we can test against older versions of git.
+ test_expect_success 'setup bitmap config' '
+ git config pack.writebitmaps true
+ '
+
+ # we need to create the tag up front such that it is covered by the repack and
+ # thus by generated bitmaps.
+ test_expect_success 'create tags' '
+ git tag --message="tag pointing to HEAD" perf-tag HEAD
+ '
+
+ test_perf "enable lookup table: $1" '
+ git config pack.writeBitmapLookupTable '"$1"'
+ '
+
+ test_pack_bitmap
+}
+
+test_lookup_pack_bitmap false
+test_lookup_pack_bitmap true
test_done
diff --git a/t/perf/p5311-pack-bitmaps-fetch.sh b/t/perf/p5311-pack-bitmaps-fetch.sh
index 47c3fd7..426fab8 100755
--- a/t/perf/p5311-pack-bitmaps-fetch.sh
+++ b/t/perf/p5311-pack-bitmaps-fetch.sh
@@ -3,42 +3,52 @@
test_description='performance of fetches from bitmapped packs'
. ./perf-lib.sh
-test_perf_default_repo
-
-test_expect_success 'create bitmapped server repo' '
- git config pack.writebitmaps true &&
- git repack -ad
-'
-
-# simulate a fetch from a repository that last fetched N days ago, for
-# various values of N. We do so by following the first-parent chain,
-# and assume the first entry in the chain that is N days older than the current
-# HEAD is where the HEAD would have been then.
-for days in 1 2 4 8 16 32 64 128; do
- title=$(printf '%10s' "($days days)")
- test_expect_success "setup revs from $days days ago" '
- now=$(git log -1 --format=%ct HEAD) &&
- then=$(($now - ($days * 86400))) &&
- tip=$(git rev-list -1 --first-parent --until=$then HEAD) &&
- {
- echo HEAD &&
- echo ^$tip
- } >revs
+test_fetch_bitmaps () {
+ test_expect_success 'setup test directory' '
+ rm -fr * .git
'
- test_perf "server $title" '
- git pack-objects --stdout --revs \
- --thin --delta-base-offset \
- <revs >tmp.pack
- '
+ test_perf_default_repo
- test_size "size $title" '
- wc -c <tmp.pack
+ test_expect_success 'create bitmapped server repo' '
+ git config pack.writebitmaps true &&
+ git config pack.writeBitmapLookupTable '"$1"' &&
+ git repack -ad
'
- test_perf "client $title" '
- git index-pack --stdin --fix-thin <tmp.pack
- '
-done
+ # simulate a fetch from a repository that last fetched N days ago, for
+ # various values of N. We do so by following the first-parent chain,
+ # and assume the first entry in the chain that is N days older than the current
+ # HEAD is where the HEAD would have been then.
+ for days in 1 2 4 8 16 32 64 128; do
+ title=$(printf '%10s' "($days days)")
+ test_expect_success "setup revs from $days days ago" '
+ now=$(git log -1 --format=%ct HEAD) &&
+ then=$(($now - ($days * 86400))) &&
+ tip=$(git rev-list -1 --first-parent --until=$then HEAD) &&
+ {
+ echo HEAD &&
+ echo ^$tip
+ } >revs
+ '
+
+ test_perf "server $title (lookup=$1)" '
+ git pack-objects --stdout --revs \
+ --thin --delta-base-offset \
+ <revs >tmp.pack
+ '
+
+ test_size "size $title" '
+ wc -c <tmp.pack
+ '
+
+ test_perf "client $title (lookup=$1)" '
+ git index-pack --stdin --fix-thin <tmp.pack
+ '
+ done
+}
+
+test_fetch_bitmaps true
+test_fetch_bitmaps false
test_done
diff --git a/t/perf/p5312-pack-bitmaps-revs.sh b/t/perf/p5312-pack-bitmaps-revs.sh
new file mode 100755
index 0000000..ceec606
--- /dev/null
+++ b/t/perf/p5312-pack-bitmaps-revs.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+test_description='Tests pack performance using bitmaps (rev index enabled)'
+. ./perf-lib.sh
+. "${TEST_DIRECTORY}/perf/lib-bitmap.sh"
+
+test_lookup_pack_bitmap () {
+ test_expect_success 'start the test from scratch' '
+ rm -rf * .git
+ '
+
+ test_perf_large_repo
+
+ test_expect_success 'setup bitmap config' '
+ git config pack.writebitmaps true
+ '
+
+ # we need to create the tag up front such that it is covered by the repack and
+ # thus by generated bitmaps.
+ test_expect_success 'create tags' '
+ git tag --message="tag pointing to HEAD" perf-tag HEAD
+ '
+
+ test_perf "enable lookup table: $1" '
+ git config pack.writeBitmapLookupTable '"$1"'
+ '
+
+ test_pack_bitmap
+}
+
+test_lookup_pack_bitmap false
+test_lookup_pack_bitmap true
+
+test_done
diff --git a/t/perf/p5326-multi-pack-bitmaps.sh b/t/perf/p5326-multi-pack-bitmaps.sh
new file mode 100755
index 0000000..d082e6c
--- /dev/null
+++ b/t/perf/p5326-multi-pack-bitmaps.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+test_description='Tests performance using midx bitmaps'
+. ./perf-lib.sh
+. "${TEST_DIRECTORY}/perf/lib-bitmap.sh"
+
+test_bitmap () {
+ local enabled="$1"
+
+ test_expect_success "remove existing repo (lookup=$enabled)" '
+ rm -fr * .git
+ '
+
+ test_perf_large_repo
+
+ # we need to create the tag up front such that it is covered by the repack and
+ # thus by generated bitmaps.
+ test_expect_success 'create tags' '
+ git tag --message="tag pointing to HEAD" perf-tag HEAD
+ '
+
+ test_expect_success "use lookup table: $enabled" '
+ git config pack.writeBitmapLookupTable '"$enabled"'
+ '
+
+ test_expect_success "start with bitmapped pack (lookup=$enabled)" '
+ git repack -adb
+ '
+
+ test_perf "setup multi-pack index (lookup=$enabled)" '
+ git multi-pack-index write --bitmap
+ '
+
+ test_expect_success "drop pack bitmap (lookup=$enabled)" '
+ rm -f .git/objects/pack/pack-*.bitmap
+ '
+
+ test_full_bitmap
+
+ test_expect_success "create partial bitmap state (lookup=$enabled)" '
+ # pick a commit to represent the repo tip in the past
+ cutoff=$(git rev-list HEAD~100 -1) &&
+ orig_tip=$(git rev-parse HEAD) &&
+
+ # now pretend we have just one tip
+ rm -rf .git/logs .git/refs/* .git/packed-refs &&
+ git update-ref HEAD $cutoff &&
+
+ # and then repack, which will leave us with a nice
+ # big bitmap pack of the "old" history, and all of
+ # the new history will be loose, as if it had been pushed
+ # up incrementally and exploded via unpack-objects
+ git repack -Ad &&
+ git multi-pack-index write --bitmap &&
+
+ # and now restore our original tip, as if the pushes
+ # had happened
+ git update-ref HEAD $orig_tip
+ '
+
+ test_partial_bitmap
+}
+
+test_bitmap false
+test_bitmap true
+
+test_done
diff --git a/t/perf/p5332-multi-pack-reuse.sh b/t/perf/p5332-multi-pack-reuse.sh
new file mode 100755
index 0000000..5c6c575
--- /dev/null
+++ b/t/perf/p5332-multi-pack-reuse.sh
@@ -0,0 +1,81 @@
+#!/bin/sh
+
+test_description='tests pack performance with multi-pack reuse'
+
+. ./perf-lib.sh
+. "${TEST_DIRECTORY}/perf/lib-pack.sh"
+
+packdir=.git/objects/pack
+
+test_perf_large_repo
+
+find_pack () {
+ for idx in $packdir/pack-*.idx
+ do
+ if git show-index <$idx | grep -q "$1"
+ then
+ basename $idx
+ fi || return 1
+ done
+}
+
+repack_into_n_chunks () {
+ git repack -adk &&
+
+ test "$1" -eq 1 && return ||
+
+ find $packdir -type f | sort >packs.before &&
+
+ # partition the repository into $1 chunks of consecutive commits, and
+ # then create $1 packs with the objects reachable from each chunk
+ # (excluding any objects reachable from the previous chunks)
+ sz="$(($(git rev-list --count --all) / $1))"
+ for rev in $(git rev-list --all | awk "NR % $sz == 0" | tac)
+ do
+ pack="$(echo "$rev" | git pack-objects --revs \
+ --honor-pack-keep --delta-base-offset $packdir/pack)" &&
+ touch $packdir/pack-$pack.keep || return 1
+ done
+
+ # grab any remaining objects not packed by the previous step(s)
+ git pack-objects --revs --all --honor-pack-keep --delta-base-offset \
+ $packdir/pack &&
+
+ find $packdir -type f | sort >packs.after &&
+
+ # and install the whole thing
+ for f in $(comm -12 packs.before packs.after)
+ do
+ rm -f "$f" || return 1
+ done
+ rm -fr $packdir/*.keep
+}
+
+for nr_packs in 1 10 100
+do
+ test_expect_success "create $nr_packs-pack scenario" '
+ repack_into_n_chunks $nr_packs
+ '
+
+ test_expect_success "setup bitmaps for $nr_packs-pack scenario" '
+ find $packdir -type f -name "*.idx" | sed -e "s/.*\/\(.*\)$/+\1/g" |
+ git multi-pack-index write --stdin-packs --bitmap \
+ --preferred-pack="$(find_pack $(git rev-parse HEAD))"
+ '
+
+ for reuse in single multi
+ do
+ test_perf "clone for $nr_packs-pack scenario ($reuse-pack reuse)" "
+ git for-each-ref --format='%(objectname)' refs/heads refs/tags >in &&
+ git -c pack.allowPackReuse=$reuse pack-objects \
+ --revs --delta-base-offset --use-bitmap-index \
+ --stdout <in >result
+ "
+
+ test_size "clone size for $nr_packs-pack scenario ($reuse-pack reuse)" '
+ wc -c <result
+ '
+ done
+done
+
+test_done
diff --git a/t/perf/p6300-for-each-ref.sh b/t/perf/p6300-for-each-ref.sh
new file mode 100755
index 0000000..fa7289c
--- /dev/null
+++ b/t/perf/p6300-for-each-ref.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+test_description='performance of for-each-ref'
+. ./perf-lib.sh
+
+test_perf_fresh_repo
+
+ref_count_per_type=10000
+test_iteration_count=10
+
+test_expect_success "setup" '
+ test_commit_bulk $(( 1 + $ref_count_per_type )) &&
+
+ # Create refs
+ test_seq $ref_count_per_type |
+ sed "s,.*,update refs/heads/branch_& HEAD~&\nupdate refs/custom/special_& HEAD~&," |
+ git update-ref --stdin &&
+
+ # Create annotated tags
+ for i in $(test_seq $ref_count_per_type)
+ do
+ # Base tags
+ echo "tag tag_$i" &&
+ echo "mark :$i" &&
+ echo "from HEAD~$i" &&
+ printf "tagger %s <%s> %s\n" \
+ "$GIT_COMMITTER_NAME" \
+ "$GIT_COMMITTER_EMAIL" \
+ "$GIT_COMMITTER_DATE" &&
+ echo "data <<EOF" &&
+ echo "tag $i" &&
+ echo "EOF" &&
+
+ # Nested tags
+ echo "tag nested_$i" &&
+ echo "from :$i" &&
+ printf "tagger %s <%s> %s\n" \
+ "$GIT_COMMITTER_NAME" \
+ "$GIT_COMMITTER_EMAIL" \
+ "$GIT_COMMITTER_DATE" &&
+ echo "data <<EOF" &&
+ echo "nested tag $i" &&
+ echo "EOF" || return 1
+ done | git fast-import
+'
+
+test_for_each_ref () {
+ title="for-each-ref"
+ if test $# -gt 0; then
+ title="$title ($1)"
+ shift
+ fi
+ args="$@"
+
+ test_perf "$title" "
+ for i in \$(test_seq $test_iteration_count); do
+ git for-each-ref $args >/dev/null
+ done
+ "
+}
+
+run_tests () {
+ test_for_each_ref "$1"
+ test_for_each_ref "$1, no sort" --no-sort
+ test_for_each_ref "$1, --count=1" --count=1
+ test_for_each_ref "$1, --count=1, no sort" --no-sort --count=1
+ test_for_each_ref "$1, tags" refs/tags/
+ test_for_each_ref "$1, tags, no sort" --no-sort refs/tags/
+ test_for_each_ref "$1, tags, dereferenced" '--format="%(refname) %(objectname) %(*objectname)"' refs/tags/
+ test_for_each_ref "$1, tags, dereferenced, no sort" --no-sort '--format="%(refname) %(objectname) %(*objectname)"' refs/tags/
+
+ test_perf "for-each-ref ($1, tags) + cat-file --batch-check (dereferenced)" "
+ for i in \$(test_seq $test_iteration_count); do
+ git for-each-ref --format='%(objectname)^{} %(refname) %(objectname)' refs/tags/ | \
+ git cat-file --batch-check='%(objectname) %(rest)' >/dev/null
+ done
+ "
+}
+
+run_tests "loose"
+
+test_expect_success 'pack refs' '
+ git pack-refs --all
+'
+run_tests "packed"
+
+test_done
diff --git a/t/perf/p7102-reset.sh b/t/perf/p7102-reset.sh
new file mode 100755
index 0000000..9b039e8
--- /dev/null
+++ b/t/perf/p7102-reset.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+test_description='performance of reset'
+. ./perf-lib.sh
+
+test_perf_default_repo
+test_checkout_worktree
+
+test_perf 'reset --hard with change in tree' '
+ base=$(git rev-parse HEAD) &&
+ test_commit --no-tag A &&
+ new=$(git rev-parse HEAD) &&
+
+ for i in $(test_seq 10)
+ do
+ git reset --hard $new &&
+ git reset --hard $base || return $?
+ done
+'
+
+test_done
diff --git a/t/perf/p7519-fsmonitor.sh b/t/perf/p7519-fsmonitor.sh
index 5eb5044..b1cb238 100755
--- a/t/perf/p7519-fsmonitor.sh
+++ b/t/perf/p7519-fsmonitor.sh
@@ -60,19 +60,7 @@ then
esac
fi
-if test -n "$GIT_PERF_7519_DROP_CACHE"
-then
- # When using GIT_PERF_7519_DROP_CACHE, GIT_PERF_REPEAT_COUNT must be 1 to
- # generate valid results. Otherwise the caching that happens for the nth
- # run will negate the validity of the comparisons.
- if test "$GIT_PERF_REPEAT_COUNT" -ne 1
- then
- echo "warning: Setting GIT_PERF_REPEAT_COUNT=1" >&2
- GIT_PERF_REPEAT_COUNT=1
- fi
-fi
-
-trace_start() {
+trace_start () {
if test -n "$GIT_PERF_7519_TRACE"
then
name="$1"
@@ -91,13 +79,20 @@ trace_start() {
fi
}
-trace_stop() {
+trace_stop () {
if test -n "$GIT_PERF_7519_TRACE"
then
unset GIT_TRACE2_PERF
fi
}
+touch_files () {
+ n=$1 &&
+ d="$n"_files &&
+
+ (cd $d && test_seq 1 $n | xargs touch )
+}
+
test_expect_success "one time repo setup" '
# set untrackedCache depending on the environment
if test -n "$GIT_PERF_7519_UNTRACKED_CACHE"
@@ -119,10 +114,11 @@ test_expect_success "one time repo setup" '
fi &&
mkdir 1_file 10_files 100_files 1000_files 10000_files &&
- for i in $(test_seq 1 10); do touch 10_files/$i; done &&
- for i in $(test_seq 1 100); do touch 100_files/$i; done &&
- for i in $(test_seq 1 1000); do touch 1000_files/$i; done &&
- for i in $(test_seq 1 10000); do touch 10000_files/$i; done &&
+ : 1_file directory should be left empty &&
+ touch_files 10 &&
+ touch_files 100 &&
+ touch_files 1000 &&
+ touch_files 10000 &&
git add 1_file 10_files 100_files 1000_files 10000_files &&
git commit -qm "Add files" &&
@@ -133,7 +129,7 @@ test_expect_success "one time repo setup" '
fi
'
-setup_for_fsmonitor() {
+setup_for_fsmonitor_hook () {
# set INTEGRATION_SCRIPT depending on the environment
if test -n "$INTEGRATION_PATH"
then
@@ -167,14 +163,18 @@ setup_for_fsmonitor() {
test_perf_w_drop_caches () {
if test -n "$GIT_PERF_7519_DROP_CACHE"; then
- test-tool drop-caches
+ test_perf "$1" --setup "test-tool drop-caches" "$2"
+ else
+ test_perf "$@"
fi
-
- test_perf "$@"
}
-test_fsmonitor_suite() {
- if test -n "$INTEGRATION_SCRIPT"; then
+test_fsmonitor_suite () {
+ if test -n "$USE_FSMONITOR_DAEMON"
+ then
+ DESC="builtin fsmonitor--daemon"
+ elif test -n "$INTEGRATION_SCRIPT"
+ then
DESC="fsmonitor=$(basename $INTEGRATION_SCRIPT)"
else
DESC="fsmonitor=disabled"
@@ -199,15 +199,15 @@ test_fsmonitor_suite() {
# Update the mtimes on upto 100k files to make status think
# that they are dirty. For simplicity, omit any files with
- # LFs (i.e. anything that ls-files thinks it needs to dquote).
- # Then fully backslash-quote the paths to capture any
- # whitespace so that they pass thru xargs properly.
+ # LFs (i.e. anything that ls-files thinks it needs to dquote)
+ # and any files with whitespace so that they pass thru xargs
+ # properly.
#
test_perf_w_drop_caches "status (dirty) ($DESC)" '
git ls-files | \
head -100000 | \
grep -v \" | \
- sed '\''s/\(.\)/\\\1/g'\'' | \
+ grep -v " ." | \
xargs test-tool chmtime -300 &&
git status
'
@@ -253,11 +253,11 @@ test_fsmonitor_suite() {
trace_start fsmonitor-watchman
if test -n "$GIT_PERF_7519_FSMONITOR"; then
for INTEGRATION_PATH in $GIT_PERF_7519_FSMONITOR; do
- test_expect_success "setup for fsmonitor $INTEGRATION_PATH" 'setup_for_fsmonitor'
+ test_expect_success "setup for fsmonitor $INTEGRATION_PATH" 'setup_for_fsmonitor_hook'
test_fsmonitor_suite
done
else
- test_expect_success "setup for fsmonitor" 'setup_for_fsmonitor'
+ test_expect_success "setup for fsmonitor hook" 'setup_for_fsmonitor_hook'
test_fsmonitor_suite
fi
@@ -285,4 +285,30 @@ test_expect_success "setup without fsmonitor" '
test_fsmonitor_suite
trace_stop
+#
+# Run a full set of perf tests using the built-in fsmonitor--daemon.
+# It does not use the Hook API, so it has a different setup.
+# Explicitly start the daemon here and before we start client commands
+# so that we can later add custom tracing.
+#
+if test_have_prereq FSMONITOR_DAEMON
+then
+ USE_FSMONITOR_DAEMON=t
+
+ test_expect_success "setup for builtin fsmonitor" '
+ trace_start fsmonitor--daemon--server &&
+ git fsmonitor--daemon start &&
+
+ trace_start fsmonitor--daemon--client &&
+
+ git config core.fsmonitor true &&
+ git update-index --fsmonitor
+ '
+
+ test_fsmonitor_suite
+
+ git fsmonitor--daemon stop
+ trace_stop
+fi
+
test_done
diff --git a/t/perf/p7527-builtin-fsmonitor.sh b/t/perf/p7527-builtin-fsmonitor.sh
new file mode 100755
index 0000000..c3f9a4c
--- /dev/null
+++ b/t/perf/p7527-builtin-fsmonitor.sh
@@ -0,0 +1,257 @@
+#!/bin/sh
+
+test_description="Perf test for the builtin FSMonitor"
+
+. ./perf-lib.sh
+
+if ! test_have_prereq FSMONITOR_DAEMON
+then
+ skip_all="fsmonitor--daemon is not supported on this platform"
+ test_done
+fi
+
+test_lazy_prereq UNTRACKED_CACHE '
+ { git update-index --test-untracked-cache; ret=$?; } &&
+ test $ret -ne 1
+'
+
+# Lie to perf-lib and ask for a new empty repo and avoid
+# the complaints about GIT_PERF_REPO not being big enough
+# the perf hit when GIT_PERF_LARGE_REPO is copied into
+# the trash directory.
+#
+# NEEDSWORK: It would be nice if perf-lib had an option to
+# "borrow" an existing large repo (especially for gigantic
+# monorepos) and use it in-place. For now, fake it here.
+#
+test_perf_fresh_repo
+
+
+# Use a generated synthetic monorepo. If it doesn't exist, we will
+# generate it. If it does exist, we will put it in a known state
+# before we start our timings.
+#
+PARAM_D=5
+PARAM_W=10
+PARAM_F=9
+
+PARAMS="$PARAM_D"."$PARAM_W"."$PARAM_F"
+
+BALLAST_BR=p0006-ballast
+export BALLAST_BR
+
+TMP_BR=tmp_br
+export TMP_BR
+
+REPO=../repos/gen-many-files-"$PARAMS".git
+export REPO
+
+if ! test -d $REPO
+then
+ (cd ../repos; ./many-files.sh -d $PARAM_D -w $PARAM_W -f $PARAM_F)
+fi
+
+
+enable_uc () {
+ git -C $REPO config core.untrackedcache true
+ git -C $REPO update-index --untracked-cache
+ git -C $REPO status >/dev/null 2>&1
+}
+
+disable_uc () {
+ git -C $REPO config core.untrackedcache false
+ git -C $REPO update-index --no-untracked-cache
+ git -C $REPO status >/dev/null 2>&1
+}
+
+start_fsm () {
+ git -C $REPO fsmonitor--daemon start
+ git -C $REPO fsmonitor--daemon status
+ git -C $REPO config core.fsmonitor true
+ git -C $REPO update-index --fsmonitor
+ git -C $REPO status >/dev/null 2>&1
+}
+
+stop_fsm () {
+ git -C $REPO config --unset core.fsmonitor
+ git -C $REPO update-index --no-fsmonitor
+ test_might_fail git -C $REPO fsmonitor--daemon stop 2>/dev/null
+ git -C $REPO status >/dev/null 2>&1
+}
+
+
+# Ensure that FSMonitor is turned off on the borrowed repo.
+#
+test_expect_success "Setup borrowed repo (fsm+uc)" "
+ stop_fsm &&
+ disable_uc
+"
+
+# Also ensure that it starts in a known state.
+#
+# Because we assume that $GIT_PERF_REPEAT_COUNT > 1, we are not going to time
+# the ballast checkout, since only the first invocation does any work and the
+# subsequent ones just print "already on branch" and quit, so the reported
+# time is not useful.
+#
+# Create a temp branch and do all work relative to it so that we don't
+# accidentially alter the real ballast branch.
+#
+test_expect_success "Setup borrowed repo (temp ballast branch)" "
+ test_might_fail git -C $REPO checkout $BALLAST_BR &&
+ test_might_fail git -C $REPO reset --hard &&
+ git -C $REPO clean -d -f &&
+ test_might_fail git -C $REPO branch -D $TMP_BR &&
+ git -C $REPO branch $TMP_BR $BALLAST_BR &&
+ git -C $REPO checkout $TMP_BR
+"
+
+
+echo Data >data.txt
+
+# NEEDSWORK: We assume that $GIT_PERF_REPEAT_COUNT > 1. With
+# FSMonitor enabled, we can get a skewed view of status times, since
+# the index MAY (or may not) be updated after the first invocation
+# which will update the FSMonitor Token, so the subsequent invocations
+# may get a smaller response from the daemon.
+#
+do_status () {
+ msg=$1
+
+ test_perf "$msg" "
+ git -C $REPO status >/dev/null 2>&1
+ "
+}
+
+do_matrix () {
+ uc=$1
+ fsm=$2
+
+ t="[uc $uc][fsm $fsm]"
+ MATRIX_BR="$TMP_BR-$uc-$fsm"
+
+ test_expect_success "$t Setup matrix branch" "
+ git -C $REPO clean -d -f &&
+ git -C $REPO checkout $TMP_BR &&
+ test_might_fail git -C $REPO branch -D $MATRIX_BR &&
+ git -C $REPO branch $MATRIX_BR $TMP_BR &&
+ git -C $REPO checkout $MATRIX_BR
+ "
+
+ if test $uc = true
+ then
+ enable_uc
+ else
+ disable_uc
+ fi
+
+ if test $fsm = true
+ then
+ start_fsm
+ else
+ stop_fsm
+ fi
+
+ do_status "$t status after checkout"
+
+ # Modify many files in the matrix branch.
+ # Stage them.
+ # Commit them.
+ # Rollback.
+ #
+ test_expect_success "$t modify tracked files" "
+ find $REPO -name file1 -exec cp data.txt {} \\;
+ "
+
+ do_status "$t status after big change"
+
+ # Don't bother timing the "add" because _REPEAT_COUNT
+ # issue described above.
+ #
+ test_expect_success "$t add all" "
+ git -C $REPO add -A
+ "
+
+ do_status "$t status after add all"
+
+ test_expect_success "$t add dot" "
+ git -C $REPO add .
+ "
+
+ do_status "$t status after add dot"
+
+ test_expect_success "$t commit staged" "
+ git -C $REPO commit -a -m data
+ "
+
+ do_status "$t status after commit"
+
+ test_expect_success "$t reset HEAD~1 hard" "
+ git -C $REPO reset --hard HEAD~1 >/dev/null 2>&1
+ "
+
+ do_status "$t status after reset hard"
+
+ # Create some untracked files.
+ #
+ test_expect_success "$t create untracked files" "
+ cp -R $REPO/ballast/dir1 $REPO/ballast/xxx1
+ "
+
+ do_status "$t status after create untracked files"
+
+ # Remove the new untracked files.
+ #
+ test_expect_success "$t clean -df" "
+ git -C $REPO clean -d -f
+ "
+
+ do_status "$t status after clean"
+
+ if test $fsm = true
+ then
+ stop_fsm
+ fi
+}
+
+# Begin testing each case in the matrix that we care about.
+#
+uc_values="false"
+test_have_prereq UNTRACKED_CACHE && uc_values="false true"
+
+fsm_values="false true"
+
+for uc_val in $uc_values
+do
+ for fsm_val in $fsm_values
+ do
+ do_matrix $uc_val $fsm_val
+ done
+done
+
+cleanup () {
+ uc=$1
+ fsm=$2
+
+ MATRIX_BR="$TMP_BR-$uc-$fsm"
+
+ test_might_fail git -C $REPO branch -D $MATRIX_BR
+}
+
+
+# We're borrowing this repo. We should leave it in a clean state.
+#
+test_expect_success "Cleanup temp and matrix branches" "
+ git -C $REPO clean -d -f &&
+ test_might_fail git -C $REPO checkout $BALLAST_BR &&
+ test_might_fail git -C $REPO branch -D $TMP_BR &&
+ for uc_val in $uc_values
+ do
+ for fsm_val in $fsm_values
+ do
+ cleanup $uc_val $fsm_val || return 1
+ done
+ done
+"
+
+test_done
diff --git a/t/perf/p7820-grep-engines.sh b/t/perf/p7820-grep-engines.sh
index 8b09c5b..9bfb868 100755
--- a/t/perf/p7820-grep-engines.sh
+++ b/t/perf/p7820-grep-engines.sh
@@ -49,13 +49,15 @@ do
fi
if ! test_have_prereq PERF_GREP_ENGINES_THREADS
then
- test_perf $prereq "$engine grep$GIT_PERF_7820_GREP_OPTS '$pattern'" "
+ test_perf "$engine grep$GIT_PERF_7820_GREP_OPTS '$pattern'" \
+ --prereq "$prereq" "
git -c grep.patternType=$engine grep$GIT_PERF_7820_GREP_OPTS -- '$pattern' >'out.$engine' || :
"
else
for threads in $GIT_PERF_GREP_THREADS
do
- test_perf PTHREADS,$prereq "$engine grep$GIT_PERF_7820_GREP_OPTS '$pattern' with $threads threads" "
+ test_perf "$engine grep$GIT_PERF_7820_GREP_OPTS '$pattern' with $threads threads"
+ --prereq PTHREADS,$prereq "
git -c grep.patternType=$engine -c grep.threads=$threads grep$GIT_PERF_7820_GREP_OPTS -- '$pattern' >'out.$engine.$threads' || :
"
done
diff --git a/t/perf/p7822-grep-perl-character.sh b/t/perf/p7822-grep-perl-character.sh
new file mode 100755
index 0000000..87009c6
--- /dev/null
+++ b/t/perf/p7822-grep-perl-character.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+test_description="git-grep's perl regex
+
+If GIT_PERF_GREP_THREADS is set to a list of threads (e.g. '1 4 8'
+etc.) we will test the patterns under those numbers of threads.
+"
+
+. ./perf-lib.sh
+
+test_perf_large_repo
+test_checkout_worktree
+
+if test -n "$GIT_PERF_GREP_THREADS"
+then
+ test_set_prereq PERF_GREP_ENGINES_THREADS
+fi
+
+for pattern in \
+ '\\bhow' \
+ '\\bÆvar' \
+ '\\d+ \\bÆvar' \
+ '\\bBelón\\b' \
+ '\\w{12}\\b'
+do
+ echo '$pattern' >pat
+ if ! test_have_prereq PERF_GREP_ENGINES_THREADS
+ then
+ test_perf "grep -P '$pattern'" --prereq PCRE "
+ git -P grep -f pat || :
+ "
+ else
+ for threads in $GIT_PERF_GREP_THREADS
+ do
+ test_perf "grep -P '$pattern' with $threads threads" --prereq PTHREADS,PCRE "
+ git -c grep.threads=$threads -P grep -f pat || :
+ "
+ done
+ fi
+done
+
+test_done
diff --git a/t/perf/p9210-scalar.sh b/t/perf/p9210-scalar.sh
new file mode 100755
index 0000000..265f7cd
--- /dev/null
+++ b/t/perf/p9210-scalar.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+test_description='test scalar performance'
+. ./perf-lib.sh
+
+test_perf_large_repo "$TRASH_DIRECTORY/to-clone"
+
+test_expect_success 'enable server-side partial clone' '
+ git -C to-clone config uploadpack.allowFilter true &&
+ git -C to-clone config uploadpack.allowAnySHA1InWant true
+'
+
+test_perf 'scalar clone' '
+ rm -rf scalar-clone &&
+ scalar clone "file://$(pwd)/to-clone" scalar-clone
+'
+
+test_perf 'git clone' '
+ rm -rf git-clone &&
+ git clone "file://$(pwd)/to-clone" git-clone
+'
+
+test_compare_perf () {
+ command=$1
+ shift
+ args=$*
+ test_perf "$command $args (scalar)" "
+ $command -C scalar-clone/src $args
+ "
+
+ test_perf "$command $args (non-scalar)" "
+ $command -C git-clone $args
+ "
+}
+
+test_compare_perf git status
+test_compare_perf test_commit --append --no-tag A
+
+test_done
diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh
index f5ed092..ab0c763 100644
--- a/t/perf/perf-lib.sh
+++ b/t/perf/perf-lib.sh
@@ -15,7 +15,7 @@
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with this program. If not, see http://www.gnu.org/licenses/ .
+# along with this program. If not, see https://www.gnu.org/licenses/ .
# These variables must be set before the inclusion of test-lib.sh below,
# because it will change our working directory.
@@ -27,7 +27,11 @@ TEST_NO_MALLOC_CHECK=t
. ../test-lib.sh
-if test -n "$GIT_TEST_INSTALLED" -a -z "$PERF_SET_GIT_TEST_INSTALLED"
+unset GIT_CONFIG_NOSYSTEM
+GIT_CONFIG_SYSTEM="$TEST_DIRECTORY/perf/config"
+export GIT_CONFIG_SYSTEM
+
+if test -n "$GIT_TEST_INSTALLED" && test -z "$PERF_SET_GIT_TEST_INSTALLED"
then
error "Do not use GIT_TEST_INSTALLED with the perf tests.
@@ -45,6 +49,9 @@ export TEST_DIRECTORY TRASH_DIRECTORY GIT_BUILD_DIR GIT_TEST_CMP
MODERN_GIT=$GIT_BUILD_DIR/bin-wrappers/git
export MODERN_GIT
+MODERN_SCALAR=$GIT_BUILD_DIR/bin-wrappers/scalar
+export MODERN_SCALAR
+
perf_results_dir=$TEST_RESULTS_DIR
test -n "$GIT_PERF_SUBSECTION" && perf_results_dir="$perf_results_dir/$GIT_PERF_SUBSECTION"
mkdir -p "$perf_results_dir"
@@ -74,7 +81,7 @@ test_perf_copy_repo_contents () {
for stuff in "$1"/*
do
case "$stuff" in
- */objects|*/hooks|*/config|*/commondir|*/gitdir|*/worktrees)
+ */objects|*/hooks|*/config|*/commondir|*/gitdir|*/worktrees|*/fsmonitor--daemon*)
;;
*)
cp -R "$stuff" "$repo/.git/" || exit 1
@@ -116,6 +123,10 @@ test_perf_create_repo_from () {
# status" due to a locked index. Since we have
# a copy it's fine to remove the lock.
rm .git/index.lock
+ fi &&
+ if test_bool_env GIT_PERF_USE_SCALAR false
+ then
+ "$MODERN_SCALAR" register
fi
) || error "failed to copy repository '$source' to '$repo'"
}
@@ -126,7 +137,11 @@ test_perf_fresh_repo () {
"$MODERN_GIT" init -q "$repo" &&
(
cd "$repo" &&
- test_perf_do_repo_symlink_config_
+ test_perf_do_repo_symlink_config_ &&
+ if test_bool_env GIT_PERF_USE_SCALAR false
+ then
+ "$MODERN_SCALAR" register
+ fi
)
}
@@ -157,7 +172,7 @@ test_run_perf_ () {
test_cleanup=:
test_export_="test_cleanup"
export test_cleanup test_export_
- "$GTIME" -f "%E %U %S" -o test_time.$i "$SHELL" -c '
+ "$GTIME" -f "%E %U %S" -o test_time.$i "$TEST_SHELL_PATH" -c '
. '"$TEST_DIRECTORY"/test-lib-functions.sh'
test_export () {
test_export_="$test_export_ $*"
@@ -185,19 +200,39 @@ exit $ret' >&3 2>&4
}
test_wrapper_ () {
- test_wrapper_func_=$1; shift
+ local test_wrapper_func_="$1"; shift
+ local test_title_="$1"; shift
test_start_
- test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
- test "$#" = 2 ||
- BUG "not 2 or 3 parameters to test-expect-success"
+ test_prereq=
+ test_perf_setup_=
+ while test $# != 0
+ do
+ case $1 in
+ --prereq)
+ test_prereq=$2
+ shift
+ ;;
+ --setup)
+ test_perf_setup_=$2
+ shift
+ ;;
+ *)
+ break
+ ;;
+ esac
+ shift
+ done
+ test "$#" = 1 || BUG "test_wrapper_ needs 2 positional parameters"
export test_prereq
- if ! test_skip "$@"
+ export test_perf_setup_
+
+ if ! test_skip "$test_title_" "$@"
then
base=$(basename "$0" .sh)
echo "$test_count" >>"$perf_results_dir"/$base.subtests
- echo "$1" >"$perf_results_dir"/$base.$test_count.descr
+ echo "$test_title_" >"$perf_results_dir"/$base.$test_count.descr
base="$perf_results_dir"/"$PERF_RESULTS_PREFIX$(basename "$0" .sh)"."$test_count"
- "$test_wrapper_func_" "$@"
+ "$test_wrapper_func_" "$test_title_" "$@"
fi
test_finish_
@@ -210,6 +245,16 @@ test_perf_ () {
echo "perf $test_count - $1:"
fi
for i in $(test_seq 1 $GIT_PERF_REPEAT_COUNT); do
+ if test -n "$test_perf_setup_"
+ then
+ say >&3 "setup: $test_perf_setup_"
+ if ! test_eval_ $test_perf_setup_
+ then
+ test_failure_ "$test_perf_setup_"
+ break
+ fi
+
+ fi
say >&3 "running: $2"
if test_run_perf_ "$2"
then
@@ -230,13 +275,27 @@ test_perf_ () {
test_ok_ "$1"
fi
"$TEST_DIRECTORY"/perf/min_time.perl test_time.* >"$base".result
+ rm test_time.*
}
+# Usage: test_perf 'title' [options] 'perf-test'
+# Run the performance test script specified in perf-test with
+# optional prerequisite and setup steps.
+# Options:
+# --prereq prerequisites: Skip the test if prequisites aren't met
+# --setup "setup-steps": Run setup steps prior to each measured iteration
+#
test_perf () {
test_wrapper_ test_perf_ "$@"
}
test_size_ () {
+ if test -n "$test_perf_setup_"
+ then
+ say >&3 "setup: $test_perf_setup_"
+ test_eval_ $test_perf_setup_
+ fi
+
say >&3 "running: $2"
if test_eval_ "$2" 3>"$base".result; then
test_ok_ "$1"
@@ -245,6 +304,14 @@ test_size_ () {
fi
}
+# Usage: test_size 'title' [options] 'size-test'
+# Run the size test script specified in size-test with optional
+# prerequisites and setup steps. Returns the numeric value
+# returned by size-test.
+# Options:
+# --prereq prerequisites: Skip the test if prequisites aren't met
+# --setup "setup-steps": Run setup steps prior to the size measurement
+
test_size () {
test_wrapper_ test_size_ "$@"
}
diff --git a/t/perf/repos/inflate-repo.sh b/t/perf/repos/inflate-repo.sh
index fcfc992..412e4b4 100755
--- a/t/perf/repos/inflate-repo.sh
+++ b/t/perf/repos/inflate-repo.sh
@@ -33,7 +33,7 @@ do
done
git ls-tree -r HEAD >GEN_src_list
-nr_src_files=$(cat GEN_src_list | wc -l)
+nr_src_files=$(wc -l <GEN_src_list)
src_branch=$(git symbolic-ref --short HEAD)
diff --git a/t/perf/run b/t/perf/run
index d19dec2..486ead2 100755
--- a/t/perf/run
+++ b/t/perf/run
@@ -74,7 +74,7 @@ set_git_test_installed () {
mydir=$1
mydir_abs=$(cd $mydir && pwd)
- mydir_abs_wrappers="$mydir_abs_wrappers/bin-wrappers"
+ mydir_abs_wrappers="$mydir_abs/bin-wrappers"
if test -d "$mydir_abs_wrappers"
then
GIT_TEST_INSTALLED=$mydir_abs_wrappers
@@ -91,10 +91,10 @@ set_git_test_installed () {
run_dirs_helper () {
mydir=${1%/}
shift
- while test $# -gt 0 -a "$1" != -- -a ! -f "$1"; do
+ while test $# -gt 0 && test "$1" != -- && test ! -f "$1"; do
shift
done
- if test $# -gt 0 -a "$1" = --; then
+ if test $# -gt 0 && test "$1" = --; then
shift
fi
@@ -124,7 +124,7 @@ run_dirs_helper () {
}
run_dirs () {
- while test $# -gt 0 -a "$1" != -- -a ! -f "$1"; do
+ while test $# -gt 0 && test "$1" != -- && test ! -f "$1"; do
run_dirs_helper "$@"
shift
done
@@ -171,13 +171,17 @@ run_subsection () {
get_var_from_env_or_config "GIT_PERF_MAKE_COMMAND" "perf" "makeCommand"
get_var_from_env_or_config "GIT_PERF_MAKE_OPTS" "perf" "makeOpts"
+ get_var_from_env_or_config "GIT_PERF_USE_SCALAR" "perf" "useScalar" "--bool"
+ export GIT_PERF_USE_SCALAR
+
get_var_from_env_or_config "GIT_PERF_REPO_NAME" "perf" "repoName"
export GIT_PERF_REPO_NAME
GIT_PERF_AGGREGATING_LATER=t
export GIT_PERF_AGGREGATING_LATER
- if test $# = 0 -o "$1" = -- -o -f "$1"; then
+ if test $# = 0 || test "$1" = -- || test -f "$1"
+ then
set -- . "$@"
fi
@@ -229,10 +233,10 @@ then
)
elif test -n "$GIT_PERF_SUBSECTION"
then
- egrep "^$GIT_PERF_SUBSECTION\$" "$TEST_RESULTS_DIR"/run_subsections.names >/dev/null ||
+ grep -E "^$GIT_PERF_SUBSECTION\$" "$TEST_RESULTS_DIR"/run_subsections.names >/dev/null ||
die "subsection '$GIT_PERF_SUBSECTION' not found in '$GIT_PERF_CONFIG_FILE'"
- egrep "^$GIT_PERF_SUBSECTION\$" "$TEST_RESULTS_DIR"/run_subsections.names | while read -r subsec
+ grep -E "^$GIT_PERF_SUBSECTION\$" "$TEST_RESULTS_DIR"/run_subsections.names | while read -r subsec
do
(
GIT_PERF_SUBSECTION="$subsec"
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index cb87768..6e300be 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -19,6 +19,7 @@ modification *should* take notice and update the test vectors here.
'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-subtest.sh
try_local_xy () {
local x="local" y="alsolocal" &&
@@ -66,95 +67,8 @@ test_expect_success 'success is reported like this' '
:
'
-_run_sub_test_lib_test_common () {
- neg="$1" name="$2" descr="$3" # stdin is the body of the test code
- shift 3
-
- # intercept pseudo-options at the front of the argument list that we
- # will not pass to child script
- skip=
- while test $# -gt 0
- do
- case "$1" in
- --skip=*)
- skip=${1#--*=}
- shift
- ;;
- *)
- break
- ;;
- esac
- done
-
- mkdir "$name" &&
- (
- # Pretend we're not running under a test harness, whether we
- # are or not. The test-lib output depends on the setting of
- # this variable, so we need a stable setting under which to run
- # the sub-test.
- sane_unset HARNESS_ACTIVE &&
- cd "$name" &&
- write_script "$name.sh" "$TEST_SHELL_PATH" <<-EOF &&
- test_description='$descr (run in sub test-lib)
-
- This is run in a sub test-lib so that we do not get incorrect
- passing metrics
- '
-
- # Point to the t/test-lib.sh, which isn't in ../ as usual
- . "\$TEST_DIRECTORY"/test-lib.sh
- EOF
- cat >>"$name.sh" &&
- export TEST_DIRECTORY &&
- # The child test re-sources GIT-BUILD-OPTIONS and may thus
- # override the test output directory. We thus pass it as an
- # explicit override to the child.
- TEST_OUTPUT_DIRECTORY_OVERRIDE=$(pwd) &&
- export TEST_OUTPUT_DIRECTORY_OVERRIDE &&
- GIT_SKIP_TESTS=$skip &&
- export GIT_SKIP_TESTS &&
- sane_unset GIT_TEST_FAIL_PREREQS &&
- if test -z "$neg"
- then
- ./"$name.sh" "$@" >out 2>err
- else
- ! ./"$name.sh" "$@" >out 2>err
- fi
- )
-}
-
-run_sub_test_lib_test () {
- _run_sub_test_lib_test_common '' "$@"
-}
-
-run_sub_test_lib_test_err () {
- _run_sub_test_lib_test_common '!' "$@"
-}
-
-check_sub_test_lib_test () {
- name="$1" # stdin is the expected output from the test
- (
- cd "$name" &&
- test_must_be_empty err &&
- sed -e 's/^> //' -e 's/Z$//' >expect &&
- test_cmp expect out
- )
-}
-
-check_sub_test_lib_test_err () {
- name="$1" # stdin is the expected output from the test
- # expected error output is in descriptor 3
- (
- cd "$name" &&
- sed -e 's/^> //' -e 's/Z$//' >expect.out &&
- test_cmp expect.out out &&
- sed -e 's/^> //' -e 's/Z$//' <&3 >expect.err &&
- test_cmp expect.err err
- )
-}
-
-test_expect_success 'pretend we have a fully passing test suite' '
- run_sub_test_lib_test full-pass "3 passing tests" <<-\EOF &&
+test_expect_success 'subtest: 3 passing tests' '
+ write_and_run_sub_test_lib_test full-pass <<-\EOF &&
for i in 1 2 3
do
test_expect_success "passing test #$i" "true"
@@ -170,9 +84,8 @@ test_expect_success 'pretend we have a fully passing test suite' '
EOF
'
-test_expect_success 'pretend we have a partially passing test suite' '
- run_sub_test_lib_test_err \
- partial-pass "2/3 tests passing" <<-\EOF &&
+test_expect_success 'subtest: 2/3 tests passing' '
+ write_and_run_sub_test_lib_test_err partial-pass <<-\EOF &&
test_expect_success "passing test #1" "true"
test_expect_success "failing test #2" "false"
test_expect_success "passing test #3" "true"
@@ -188,8 +101,21 @@ test_expect_success 'pretend we have a partially passing test suite' '
EOF
'
-test_expect_success 'pretend we have a known breakage' '
- run_sub_test_lib_test failing-todo "A failing TODO test" <<-\EOF &&
+test_expect_success 'subtest: --immediate' '
+ run_sub_test_lib_test_err partial-pass \
+ --immediate &&
+ check_sub_test_lib_test_err partial-pass \
+ <<-\EOF_OUT 3<<-EOF_ERR
+ > ok 1 - passing test #1
+ > not ok 2 - failing test #2
+ > # false
+ > 1..2
+ EOF_OUT
+ EOF_ERR
+'
+
+test_expect_success 'subtest: a failing TODO test' '
+ write_and_run_sub_test_lib_test failing-todo <<-\EOF &&
test_expect_success "passing test" "true"
test_expect_failure "pretend we have a known breakage" "false"
test_done
@@ -203,8 +129,8 @@ test_expect_success 'pretend we have a known breakage' '
EOF
'
-test_expect_success 'pretend we have fixed a known breakage' '
- run_sub_test_lib_test passing-todo "A passing TODO test" <<-\EOF &&
+test_expect_success 'subtest: a passing TODO test' '
+ write_and_run_sub_test_lib_test passing-todo <<-\EOF &&
test_expect_failure "pretend we have fixed a known breakage" "true"
test_done
EOF
@@ -215,9 +141,8 @@ test_expect_success 'pretend we have fixed a known breakage' '
EOF
'
-test_expect_success 'pretend we have fixed one of two known breakages (run in sub test-lib)' '
- run_sub_test_lib_test partially-passing-todos \
- "2 TODO tests, one passing" <<-\EOF &&
+test_expect_success 'subtest: 2 TODO tests, one passin' '
+ write_and_run_sub_test_lib_test partially-passing-todos <<-\EOF &&
test_expect_failure "pretend we have a known breakage" "false"
test_expect_success "pretend we have a passing test" "true"
test_expect_failure "pretend we have fixed another known breakage" "true"
@@ -234,9 +159,8 @@ test_expect_success 'pretend we have fixed one of two known breakages (run in su
EOF
'
-test_expect_success 'pretend we have a pass, fail, and known breakage' '
- run_sub_test_lib_test_err \
- mixed-results1 "mixed results #1" <<-\EOF &&
+test_expect_success 'subtest: mixed results: pass, failure and a TODO test' '
+ write_and_run_sub_test_lib_test_err mixed-results1 <<-\EOF &&
test_expect_success "passing test" "true"
test_expect_success "failing test" "false"
test_expect_failure "pretend we have a known breakage" "false"
@@ -253,9 +177,8 @@ test_expect_success 'pretend we have a pass, fail, and known breakage' '
EOF
'
-test_expect_success 'pretend we have a mix of all possible results' '
- run_sub_test_lib_test_err \
- mixed-results2 "mixed results #2" <<-\EOF &&
+test_expect_success 'subtest: mixed results: a mixture of all possible results' '
+ write_and_run_sub_test_lib_test_err mixed-results2 <<-\EOF &&
test_expect_success "passing test" "true"
test_expect_success "passing test" "true"
test_expect_success "passing test" "true"
@@ -289,9 +212,8 @@ test_expect_success 'pretend we have a mix of all possible results' '
EOF
'
-test_expect_success 'test --verbose' '
- run_sub_test_lib_test_err \
- t1234-verbose "test verbose" --verbose <<-\EOF &&
+test_expect_success 'subtest: --verbose option' '
+ write_and_run_sub_test_lib_test_err t1234-verbose --verbose <<-\EOF &&
test_expect_success "passing test" true
test_expect_success "test with output" "echo foo"
test_expect_success "failing test" false
@@ -316,19 +238,14 @@ test_expect_success 'test --verbose' '
EOF
'
-test_expect_success 'test --verbose-only' '
+test_expect_success 'subtest: --verbose-only option' '
run_sub_test_lib_test_err \
- t2345-verbose-only-2 "test verbose-only=2" \
- --verbose-only=2 <<-\EOF &&
- test_expect_success "passing test" true
- test_expect_success "test with output" "echo foo"
- test_expect_success "failing test" false
- test_done
- EOF
- check_sub_test_lib_test t2345-verbose-only-2 <<-\EOF
+ t1234-verbose \
+ --verbose-only=2 &&
+ check_sub_test_lib_test t1234-verbose <<-\EOF
> ok 1 - passing test
> Z
- > expecting success of 2345.2 '\''test with output'\'': echo foo
+ > expecting success of 1234.2 '\''test with output'\'': echo foo
> foo
> ok 2 - test with output
> Z
@@ -339,18 +256,11 @@ test_expect_success 'test --verbose-only' '
EOF
'
-test_expect_success 'GIT_SKIP_TESTS' '
+test_expect_success 'subtest: skip one with GIT_SKIP_TESTS' '
(
- run_sub_test_lib_test git-skip-tests-basic \
- "GIT_SKIP_TESTS" \
- --skip="git.2" <<-\EOF &&
- for i in 1 2 3
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test git-skip-tests-basic <<-\EOF
+ run_sub_test_lib_test full-pass \
+ --skip="full.2" &&
+ check_sub_test_lib_test full-pass <<-\EOF
> ok 1 - passing test #1
> ok 2 # skip passing test #2 (GIT_SKIP_TESTS)
> ok 3 - passing test #3
@@ -360,10 +270,9 @@ test_expect_success 'GIT_SKIP_TESTS' '
)
'
-test_expect_success 'GIT_SKIP_TESTS several tests' '
+test_expect_success 'subtest: skip several with GIT_SKIP_TESTS' '
(
- run_sub_test_lib_test git-skip-tests-several \
- "GIT_SKIP_TESTS several tests" \
+ write_and_run_sub_test_lib_test git-skip-tests-several \
--skip="git.2 git.5" <<-\EOF &&
for i in 1 2 3 4 5 6
do
@@ -384,18 +293,11 @@ test_expect_success 'GIT_SKIP_TESTS several tests' '
)
'
-test_expect_success 'GIT_SKIP_TESTS sh pattern' '
+test_expect_success 'subtest: sh pattern skipping with GIT_SKIP_TESTS' '
(
- run_sub_test_lib_test git-skip-tests-sh-pattern \
- "GIT_SKIP_TESTS sh pattern" \
- --skip="git.[2-5]" <<-\EOF &&
- for i in 1 2 3 4 5 6
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test git-skip-tests-sh-pattern <<-\EOF
+ run_sub_test_lib_test git-skip-tests-several \
+ --skip="git.[2-5]" &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> ok 1 - passing test #1
> ok 2 # skip passing test #2 (GIT_SKIP_TESTS)
> ok 3 # skip passing test #3 (GIT_SKIP_TESTS)
@@ -408,35 +310,23 @@ test_expect_success 'GIT_SKIP_TESTS sh pattern' '
)
'
-test_expect_success 'GIT_SKIP_TESTS entire suite' '
+test_expect_success 'subtest: skip entire test suite with GIT_SKIP_TESTS' '
(
- run_sub_test_lib_test git-skip-tests-entire-suite \
- "GIT_SKIP_TESTS entire suite" \
- --skip="git" <<-\EOF &&
- for i in 1 2 3
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test git-skip-tests-entire-suite <<-\EOF
+ GIT_SKIP_TESTS="git" && export GIT_SKIP_TESTS &&
+ run_sub_test_lib_test git-skip-tests-several \
+ --skip="git" &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> 1..0 # SKIP skip all tests in git
EOF
)
'
-test_expect_success 'GIT_SKIP_TESTS does not skip unmatched suite' '
+test_expect_success 'subtest: GIT_SKIP_TESTS does not skip unmatched suite' '
(
- run_sub_test_lib_test git-skip-tests-unmatched-suite \
- "GIT_SKIP_TESTS does not skip unmatched suite" \
- --skip="notgit" <<-\EOF &&
- for i in 1 2 3
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test git-skip-tests-unmatched-suite <<-\EOF
+ GIT_SKIP_TESTS="notgit" && export GIT_SKIP_TESTS &&
+ run_sub_test_lib_test full-pass \
+ --skip="notfull" &&
+ check_sub_test_lib_test full-pass <<-\EOF
> ok 1 - passing test #1
> ok 2 - passing test #2
> ok 3 - passing test #3
@@ -446,16 +336,9 @@ test_expect_success 'GIT_SKIP_TESTS does not skip unmatched suite' '
)
'
-test_expect_success '--run basic' '
- run_sub_test_lib_test run-basic \
- "--run basic" --run="1,3,5" <<-\EOF &&
- for i in 1 2 3 4 5 6
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test run-basic <<-\EOF
+test_expect_success 'subtest: --run basic' '
+ run_sub_test_lib_test git-skip-tests-several --run="1,3,5" &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> ok 1 - passing test #1
> ok 2 # skip passing test #2 (--run)
> ok 3 - passing test #3
@@ -467,16 +350,10 @@ test_expect_success '--run basic' '
EOF
'
-test_expect_success '--run with a range' '
- run_sub_test_lib_test run-range \
- "--run with a range" --run="1-3" <<-\EOF &&
- for i in 1 2 3 4 5 6
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test run-range <<-\EOF
+test_expect_success 'subtest: --run with a range' '
+ run_sub_test_lib_test git-skip-tests-several \
+ --run="1-3" &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> ok 1 - passing test #1
> ok 2 - passing test #2
> ok 3 - passing test #3
@@ -488,16 +365,10 @@ test_expect_success '--run with a range' '
EOF
'
-test_expect_success '--run with two ranges' '
- run_sub_test_lib_test run-two-ranges \
- "--run with two ranges" --run="1-2,5-6" <<-\EOF &&
- for i in 1 2 3 4 5 6
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test run-two-ranges <<-\EOF
+test_expect_success 'subtest: --run with two ranges' '
+ run_sub_test_lib_test git-skip-tests-several \
+ --run="1-2,5-6" &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> ok 1 - passing test #1
> ok 2 - passing test #2
> ok 3 # skip passing test #3 (--run)
@@ -509,16 +380,10 @@ test_expect_success '--run with two ranges' '
EOF
'
-test_expect_success '--run with a left open range' '
- run_sub_test_lib_test run-left-open-range \
- "--run with a left open range" --run="-3" <<-\EOF &&
- for i in 1 2 3 4 5 6
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test run-left-open-range <<-\EOF
+test_expect_success 'subtest: --run with a left open range' '
+ run_sub_test_lib_test git-skip-tests-several \
+ --run="-3" &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> ok 1 - passing test #1
> ok 2 - passing test #2
> ok 3 - passing test #3
@@ -530,16 +395,10 @@ test_expect_success '--run with a left open range' '
EOF
'
-test_expect_success '--run with a right open range' '
- run_sub_test_lib_test run-right-open-range \
- "--run with a right open range" --run="4-" <<-\EOF &&
- for i in 1 2 3 4 5 6
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test run-right-open-range <<-\EOF
+test_expect_success 'subtest: --run with a right open range' '
+ run_sub_test_lib_test git-skip-tests-several \
+ --run="4-" &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> ok 1 # skip passing test #1 (--run)
> ok 2 # skip passing test #2 (--run)
> ok 3 # skip passing test #3 (--run)
@@ -551,16 +410,10 @@ test_expect_success '--run with a right open range' '
EOF
'
-test_expect_success '--run with basic negation' '
- run_sub_test_lib_test run-basic-neg \
- "--run with basic negation" --run="!3" <<-\EOF &&
- for i in 1 2 3 4 5 6
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test run-basic-neg <<-\EOF
+test_expect_success 'subtest: --run with basic negation' '
+ run_sub_test_lib_test git-skip-tests-several \
+ --run="!3" &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> ok 1 - passing test #1
> ok 2 - passing test #2
> ok 3 # skip passing test #3 (--run)
@@ -572,16 +425,10 @@ test_expect_success '--run with basic negation' '
EOF
'
-test_expect_success '--run with two negations' '
- run_sub_test_lib_test run-two-neg \
- "--run with two negations" --run="!3,!6" <<-\EOF &&
- for i in 1 2 3 4 5 6
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test run-two-neg <<-\EOF
+test_expect_success 'subtest: --run with two negations' '
+ run_sub_test_lib_test git-skip-tests-several \
+ --run="!3,!6" &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> ok 1 - passing test #1
> ok 2 - passing test #2
> ok 3 # skip passing test #3 (--run)
@@ -593,16 +440,10 @@ test_expect_success '--run with two negations' '
EOF
'
-test_expect_success '--run a range and negation' '
- run_sub_test_lib_test run-range-and-neg \
- "--run a range and negation" --run="-4,!2" <<-\EOF &&
- for i in 1 2 3 4 5 6
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test run-range-and-neg <<-\EOF
+test_expect_success 'subtest: --run a range and negation' '
+ run_sub_test_lib_test git-skip-tests-several \
+ --run="-4,!2" &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> ok 1 - passing test #1
> ok 2 # skip passing test #2 (--run)
> ok 3 - passing test #3
@@ -614,16 +455,10 @@ test_expect_success '--run a range and negation' '
EOF
'
-test_expect_success '--run range negation' '
- run_sub_test_lib_test run-range-neg \
- "--run range negation" --run="!1-3" <<-\EOF &&
- for i in 1 2 3 4 5 6
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test run-range-neg <<-\EOF
+test_expect_success 'subtest: --run range negation' '
+ run_sub_test_lib_test git-skip-tests-several \
+ --run="!1-3" &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> ok 1 # skip passing test #1 (--run)
> ok 2 # skip passing test #2 (--run)
> ok 3 # skip passing test #3 (--run)
@@ -635,17 +470,10 @@ test_expect_success '--run range negation' '
EOF
'
-test_expect_success '--run include, exclude and include' '
- run_sub_test_lib_test run-inc-neg-inc \
- "--run include, exclude and include" \
- --run="1-5,!1-3,2" <<-\EOF &&
- for i in 1 2 3 4 5 6
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test run-inc-neg-inc <<-\EOF
+test_expect_success 'subtest: --run include, exclude and include' '
+ run_sub_test_lib_test git-skip-tests-several \
+ --run="1-5,!1-3,2" &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> ok 1 # skip passing test #1 (--run)
> ok 2 - passing test #2
> ok 3 # skip passing test #3 (--run)
@@ -657,17 +485,10 @@ test_expect_success '--run include, exclude and include' '
EOF
'
-test_expect_success '--run include, exclude and include, comma separated' '
- run_sub_test_lib_test run-inc-neg-inc-comma \
- "--run include, exclude and include, comma separated" \
- --run=1-5,!1-3,2 <<-\EOF &&
- for i in 1 2 3 4 5 6
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test run-inc-neg-inc-comma <<-\EOF
+test_expect_success 'subtest: --run include, exclude and include, comma separated' '
+ run_sub_test_lib_test git-skip-tests-several \
+ --run=1-5,!1-3,2 &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> ok 1 # skip passing test #1 (--run)
> ok 2 - passing test #2
> ok 3 # skip passing test #3 (--run)
@@ -679,17 +500,10 @@ test_expect_success '--run include, exclude and include, comma separated' '
EOF
'
-test_expect_success '--run exclude and include' '
- run_sub_test_lib_test run-neg-inc \
- "--run exclude and include" \
- --run="!3-,5" <<-\EOF &&
- for i in 1 2 3 4 5 6
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test run-neg-inc <<-\EOF
+test_expect_success 'subtest: --run exclude and include' '
+ run_sub_test_lib_test git-skip-tests-several \
+ --run="!3-,5" &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> ok 1 - passing test #1
> ok 2 - passing test #2
> ok 3 # skip passing test #3 (--run)
@@ -701,17 +515,10 @@ test_expect_success '--run exclude and include' '
EOF
'
-test_expect_success '--run empty selectors' '
- run_sub_test_lib_test run-empty-sel \
- "--run empty selectors" \
- --run="1,,3,,,5" <<-\EOF &&
- for i in 1 2 3 4 5 6
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test run-empty-sel <<-\EOF
+test_expect_success 'subtest: --run empty selectors' '
+ run_sub_test_lib_test git-skip-tests-several \
+ --run="1,,3,,,5" &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> ok 1 - passing test #1
> ok 2 # skip passing test #2 (--run)
> ok 3 - passing test #3
@@ -723,9 +530,8 @@ test_expect_success '--run empty selectors' '
EOF
'
-test_expect_success '--run substring selector' '
- run_sub_test_lib_test run-substring-selector \
- "--run empty selectors" \
+test_expect_success 'subtest: --run substring selector' '
+ write_and_run_sub_test_lib_test run-substring-selector \
--run="relevant" <<-\EOF &&
test_expect_success "relevant test" "true"
for i in 1 2 3 4 5 6
@@ -747,9 +553,8 @@ test_expect_success '--run substring selector' '
EOF
'
-test_expect_success '--run keyword selection' '
- run_sub_test_lib_test_err run-inv-range-start \
- "--run invalid range start" \
+test_expect_success 'subtest: --run keyword selection' '
+ write_and_run_sub_test_lib_test_err run-inv-range-start \
--run="a-5" <<-\EOF &&
test_expect_success "passing test #1" "true"
test_done
@@ -762,14 +567,10 @@ test_expect_success '--run keyword selection' '
EOF_ERR
'
-test_expect_success '--run invalid range end' '
- run_sub_test_lib_test_err run-inv-range-end \
- "--run invalid range end" \
- --run="1-z" <<-\EOF &&
- test_expect_success "passing test #1" "true"
- test_done
- EOF
- check_sub_test_lib_test_err run-inv-range-end \
+test_expect_success 'subtest: --run invalid range end' '
+ run_sub_test_lib_test_err run-inv-range-start \
+ --run="1-z" &&
+ check_sub_test_lib_test_err run-inv-range-start \
<<-\EOF_OUT 3<<-EOF_ERR
> FATAL: Unexpected exit with code 1
EOF_OUT
@@ -777,8 +578,80 @@ test_expect_success '--run invalid range end' '
EOF_ERR
'
-test_expect_success 'tests respect prerequisites' '
- run_sub_test_lib_test prereqs "tests respect prereqs" <<-\EOF &&
+test_expect_success 'subtest: --invert-exit-code without --immediate' '
+ run_sub_test_lib_test_err full-pass \
+ --invert-exit-code &&
+ check_sub_test_lib_test_err full-pass \
+ <<-\EOF_OUT 3<<-EOF_ERR
+ ok 1 - passing test #1
+ ok 2 - passing test #2
+ ok 3 - passing test #3
+ # passed all 3 test(s)
+ 1..3
+ # faking up non-zero exit with --invert-exit-code
+ EOF_OUT
+ EOF_ERR
+'
+
+test_expect_success 'subtest: --invert-exit-code with --immediate: all passed' '
+ run_sub_test_lib_test_err full-pass \
+ --invert-exit-code --immediate &&
+ check_sub_test_lib_test_err full-pass \
+ <<-\EOF_OUT 3<<-EOF_ERR
+ ok 1 - passing test #1
+ ok 2 - passing test #2
+ ok 3 - passing test #3
+ # passed all 3 test(s)
+ 1..3
+ # faking up non-zero exit with --invert-exit-code
+ EOF_OUT
+ EOF_ERR
+'
+
+test_expect_success 'subtest: --invert-exit-code without --immediate: partial pass' '
+ run_sub_test_lib_test partial-pass \
+ --invert-exit-code &&
+ check_sub_test_lib_test partial-pass <<-\EOF
+ ok 1 - passing test #1
+ not ok 2 - # TODO induced breakage (--invert-exit-code): failing test #2
+ # false
+ ok 3 - passing test #3
+ # failed 1 among 3 test(s)
+ 1..3
+ # faked up failures as TODO & now exiting with 0 due to --invert-exit-code
+ EOF
+'
+
+test_expect_success 'subtest: --invert-exit-code with --immediate: partial pass' '
+ run_sub_test_lib_test partial-pass \
+ --invert-exit-code --immediate &&
+ check_sub_test_lib_test partial-pass \
+ <<-\EOF_OUT 3<<-EOF_ERR
+ ok 1 - passing test #1
+ not ok 2 - # TODO induced breakage (--invert-exit-code): failing test #2
+ # false
+ 1..2
+ # faked up failures as TODO & now exiting with 0 due to --invert-exit-code
+ EOF_OUT
+ EOF_ERR
+'
+
+test_expect_success 'subtest: --invert-exit-code --immediate: got a failure' '
+ run_sub_test_lib_test partial-pass \
+ --invert-exit-code --immediate &&
+ check_sub_test_lib_test_err partial-pass \
+ <<-\EOF_OUT 3<<-EOF_ERR
+ ok 1 - passing test #1
+ not ok 2 - # TODO induced breakage (--invert-exit-code): failing test #2
+ # false
+ 1..2
+ # faked up failures as TODO & now exiting with 0 due to --invert-exit-code
+ EOF_OUT
+ EOF_ERR
+'
+
+test_expect_success 'subtest: tests respect prerequisites' '
+ write_and_run_sub_test_lib_test prereqs <<-\EOF &&
test_set_prereq HAVEIT
test_expect_success HAVEIT "prereq is satisfied" "true"
@@ -807,8 +680,8 @@ test_expect_success 'tests respect prerequisites' '
EOF
'
-test_expect_success 'tests respect lazy prerequisites' '
- run_sub_test_lib_test lazy-prereqs "respect lazy prereqs" <<-\EOF &&
+test_expect_success 'subtest: tests respect lazy prerequisites' '
+ write_and_run_sub_test_lib_test lazy-prereqs <<-\EOF &&
test_lazy_prereq LAZY_TRUE true
test_expect_success LAZY_TRUE "lazy prereq is satisifed" "true"
@@ -831,8 +704,8 @@ test_expect_success 'tests respect lazy prerequisites' '
EOF
'
-test_expect_success 'nested lazy prerequisites' '
- run_sub_test_lib_test nested-lazy "nested lazy prereqs" <<-\EOF &&
+test_expect_success 'subtest: nested lazy prerequisites' '
+ write_and_run_sub_test_lib_test nested-lazy <<-\EOF &&
test_lazy_prereq NESTED_INNER "
>inner &&
@@ -857,9 +730,9 @@ test_expect_success 'nested lazy prerequisites' '
EOF
'
-test_expect_success 'lazy prereqs do not turn off tracing' '
- run_sub_test_lib_test lazy-prereq-and-tracing \
- "lazy prereqs and -x" -v -x <<-\EOF &&
+test_expect_success 'subtest: lazy prereqs do not turn off tracing' '
+ write_and_run_sub_test_lib_test lazy-prereq-and-tracing \
+ -v -x <<-\EOF &&
test_lazy_prereq LAZY true
test_expect_success lazy "test_have_prereq LAZY && echo trace"
@@ -870,8 +743,8 @@ test_expect_success 'lazy prereqs do not turn off tracing' '
grep "echo trace" lazy-prereq-and-tracing/err
'
-test_expect_success 'tests clean up after themselves' '
- run_sub_test_lib_test cleanup "test with cleanup" <<-\EOF &&
+test_expect_success 'subtest: tests clean up after themselves' '
+ write_and_run_sub_test_lib_test cleanup <<-\EOF &&
clean=no
test_expect_success "do cleanup" "
test_when_finished clean=yes
@@ -890,9 +763,9 @@ test_expect_success 'tests clean up after themselves' '
EOF
'
-test_expect_success 'tests clean up even on failures' '
- run_sub_test_lib_test_err \
- failing-cleanup "Failing tests with cleanup commands" <<-\EOF &&
+test_expect_success 'subtest: tests clean up even on failures' '
+ write_and_run_sub_test_lib_test_err \
+ failing-cleanup <<-\EOF &&
test_expect_success "tests clean up even after a failure" "
touch clean-after-failure &&
test_when_finished rm clean-after-failure &&
@@ -919,9 +792,9 @@ test_expect_success 'tests clean up even on failures' '
EOF
'
-test_expect_success 'test_atexit is run' '
- run_sub_test_lib_test_err \
- atexit-cleanup "Run atexit commands" -i <<-\EOF &&
+test_expect_success 'subtest: test_atexit is run' '
+ write_and_run_sub_test_lib_test_err \
+ atexit-cleanup -i <<-\EOF &&
test_expect_success "tests clean up even after a failure" "
> ../../clean-atexit &&
test_atexit rm ../../clean-atexit &&
@@ -942,7 +815,8 @@ test_expect_success 'test_oid provides sane info by default' '
grep "^00*\$" actual &&
rawsz="$(test_oid rawsz)" &&
hexsz="$(test_oid hexsz)" &&
- test "$hexsz" -eq $(wc -c <actual) &&
+ # +1 accounts for the trailing newline
+ test $(( $hexsz + 1)) -eq $(wc -c <actual) &&
test $(( $rawsz * 2)) -eq "$hexsz"
'
@@ -953,7 +827,7 @@ test_expect_success 'test_oid can look up data for SHA-1' '
grep "^00*\$" actual &&
rawsz="$(test_oid rawsz)" &&
hexsz="$(test_oid hexsz)" &&
- test $(wc -c <actual) -eq 40 &&
+ test $(wc -c <actual) -eq 41 &&
test "$rawsz" -eq 20 &&
test "$hexsz" -eq 40
'
@@ -965,7 +839,7 @@ test_expect_success 'test_oid can look up data for SHA-256' '
grep "^00*\$" actual &&
rawsz="$(test_oid rawsz)" &&
hexsz="$(test_oid hexsz)" &&
- test $(wc -c <actual) -eq 64 &&
+ test $(wc -c <actual) -eq 65 &&
test "$rawsz" -eq 32 &&
test "$hexsz" -eq 64
'
@@ -1140,7 +1014,7 @@ test_expect_success 'validate object ID for a known tree' '
'
test_expect_success 'showing tree with git ls-tree' '
- git ls-tree $tree >current
+ git ls-tree $tree >current
'
test_expect_success 'git ls-tree output for a known tree' '
@@ -1271,28 +1145,29 @@ P=$(test_oid root)
test_expect_success 'git commit-tree records the correct tree in a commit' '
commit0=$(echo NO | git commit-tree $P) &&
- tree=$(git show --pretty=raw $commit0 |
- sed -n -e "s/^tree //p" -e "/^author /q") &&
+ git show --pretty=raw $commit0 >out &&
+ tree=$(sed -n -e "s/^tree //p" -e "/^author /q" out) &&
test "z$tree" = "z$P"
'
test_expect_success 'git commit-tree records the correct parent in a commit' '
commit1=$(echo NO | git commit-tree $P -p $commit0) &&
- parent=$(git show --pretty=raw $commit1 |
- sed -n -e "s/^parent //p" -e "/^author /q") &&
+ git show --pretty=raw $commit1 >out &&
+ parent=$(sed -n -e "s/^parent //p" -e "/^author /q" out) &&
test "z$commit0" = "z$parent"
'
test_expect_success 'git commit-tree omits duplicated parent in a commit' '
commit2=$(echo NO | git commit-tree $P -p $commit0 -p $commit0) &&
- parent=$(git show --pretty=raw $commit2 |
- sed -n -e "s/^parent //p" -e "/^author /q" |
- sort -u) &&
+ git show --pretty=raw $commit2 >out &&
+ cat >match.sed <<-\EOF &&
+ s/^parent //p
+ /^author /q
+ EOF
+ parent=$(sed -n -f match.sed out | sort -u) &&
test "z$commit0" = "z$parent" &&
- numparent=$(git show --pretty=raw $commit2 |
- sed -n -e "s/^parent //p" -e "/^author /q" |
- wc -l) &&
- test $numparent = 1
+ git show --pretty=raw $commit2 >out &&
+ test_stdout_line_count = 1 sed -n -f match.sed out
'
test_expect_success 'update-index D/F conflict' '
@@ -1300,7 +1175,8 @@ test_expect_success 'update-index D/F conflict' '
mv path2 path0 &&
mv tmp path2 &&
git update-index --add --replace path2 path0/file2 &&
- numpath0=$(git ls-files path0 | wc -l) &&
+ git ls-files path0 >tmp &&
+ numpath0=$(wc -l <tmp) &&
test $numpath0 = 1
'
@@ -1314,13 +1190,14 @@ test_expect_success 'very long name in the index handled sanely' '
>path4 &&
git update-index --add path4 &&
+ git ls-files -s path4 >tmp &&
(
- git ls-files -s path4 |
- sed -e "s/ .*/ /" |
+ sed -e "s/ .*/ /" tmp |
tr -d "\012" &&
echo "$a"
) | git update-index --index-info &&
- len=$(git ls-files "a*" | wc -c) &&
+ git ls-files "a*" >tmp &&
+ len=$(wc -c <tmp) &&
test $len = 4098
'
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index df544bb..b131d66 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -2,10 +2,12 @@
test_description='git init'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
check_config () {
- if test -d "$1" && test -f "$1/config" && test -d "$1/refs"
+ if test_path_is_dir "$1" &&
+ test_path_is_file "$1/config" && test_path_is_dir "$1/refs"
then
: happy
else
@@ -166,8 +168,8 @@ test_expect_success 'reinit' '
git -c init.defaultBranch=initial init >out1 2>err1 &&
git init >out2 2>err2
) &&
- test_i18ngrep "Initialized empty" again/out1 &&
- test_i18ngrep "Reinitialized existing" again/out2 &&
+ test_grep "Initialized empty" again/out1 &&
+ test_grep "Reinitialized existing" again/out2 &&
test_must_be_empty again/err1 &&
test_must_be_empty again/err2
'
@@ -330,7 +332,7 @@ test_expect_success 'init with separate gitdir' '
test_expect_success 'explicit bare & --separate-git-dir incompatible' '
test_must_fail git init --bare --separate-git-dir goop.git bare.git 2>err &&
- test_i18ngrep "mutually exclusive" err
+ test_grep "cannot be used together" err
'
test_expect_success 'implicit bare & --separate-git-dir incompatible' '
@@ -338,7 +340,7 @@ test_expect_success 'implicit bare & --separate-git-dir incompatible' '
mkdir -p bare.git &&
test_must_fail env GIT_DIR=. \
git -C bare.git init --separate-git-dir goop.git 2>err &&
- test_i18ngrep "incompatible" err
+ test_grep "incompatible" err
'
test_expect_success 'bare & --separate-git-dir incompatible within worktree' '
@@ -347,7 +349,7 @@ test_expect_success 'bare & --separate-git-dir incompatible within worktree' '
git clone --bare . bare.git &&
git -C bare.git worktree add --detach ../linkwt &&
test_must_fail git -C linkwt init --separate-git-dir seprepo 2>err &&
- test_i18ngrep "incompatible" err
+ test_grep "incompatible" err
'
test_lazy_prereq GETCWD_IGNORES_PERMS '
@@ -530,6 +532,76 @@ test_expect_success 'init rejects attempts to initialize with different hash' '
test_must_fail git -C sha256 init --object-format=sha1
'
+test_expect_success DEFAULT_REPO_FORMAT 'extensions.refStorage is not allowed with repo version 0' '
+ test_when_finished "rm -rf refstorage" &&
+ git init refstorage &&
+ git -C refstorage config extensions.refStorage files &&
+ test_must_fail git -C refstorage rev-parse 2>err &&
+ grep "repo version is 0, but v1-only extension found" err
+'
+
+test_expect_success DEFAULT_REPO_FORMAT 'extensions.refStorage with files backend' '
+ test_when_finished "rm -rf refstorage" &&
+ git init refstorage &&
+ git -C refstorage config core.repositoryformatversion 1 &&
+ git -C refstorage config extensions.refStorage files &&
+ test_commit -C refstorage A &&
+ git -C refstorage rev-parse --verify HEAD
+'
+
+test_expect_success DEFAULT_REPO_FORMAT 'extensions.refStorage with unknown backend' '
+ test_when_finished "rm -rf refstorage" &&
+ git init refstorage &&
+ git -C refstorage config core.repositoryformatversion 1 &&
+ git -C refstorage config extensions.refStorage garbage &&
+ test_must_fail git -C refstorage rev-parse 2>err &&
+ grep "invalid value for ${SQ}extensions.refstorage${SQ}: ${SQ}garbage${SQ}" err
+'
+
+test_expect_success DEFAULT_REPO_FORMAT 'init with GIT_DEFAULT_REF_FORMAT=files' '
+ test_when_finished "rm -rf refformat" &&
+ GIT_DEFAULT_REF_FORMAT=files git init refformat &&
+ echo 0 >expect &&
+ git -C refformat config core.repositoryformatversion >actual &&
+ test_cmp expect actual &&
+ test_must_fail git -C refformat config extensions.refstorage
+'
+
+test_expect_success 'init with GIT_DEFAULT_REF_FORMAT=garbage' '
+ test_when_finished "rm -rf refformat" &&
+ cat >expect <<-EOF &&
+ fatal: unknown ref storage format ${SQ}garbage${SQ}
+ EOF
+ test_must_fail env GIT_DEFAULT_REF_FORMAT=garbage git init refformat 2>err &&
+ test_cmp expect err
+'
+
+test_expect_success 'init with --ref-format=files' '
+ test_when_finished "rm -rf refformat" &&
+ git init --ref-format=files refformat &&
+ echo files >expect &&
+ git -C refformat rev-parse --show-ref-format >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 're-init with same format' '
+ test_when_finished "rm -rf refformat" &&
+ git init --ref-format=files refformat &&
+ git init --ref-format=files refformat &&
+ echo files >expect &&
+ git -C refformat rev-parse --show-ref-format >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'init with --ref-format=garbage' '
+ test_when_finished "rm -rf refformat" &&
+ cat >expect <<-EOF &&
+ fatal: unknown ref storage format ${SQ}garbage${SQ}
+ EOF
+ test_must_fail git init --ref-format=garbage refformat 2>err &&
+ test_cmp expect err
+'
+
test_expect_success MINGW 'core.hidedotfiles = false' '
git config --global core.hidedotfiles false &&
rm -rf newdir &&
@@ -561,7 +633,7 @@ test_expect_success '--initial-branch' '
: re-initializing should not change the branch name &&
git init --initial-branch=ignore initial-branch-option 2>err &&
- test_i18ngrep "ignored --initial-branch" err &&
+ test_grep "ignored --initial-branch" err &&
git -C initial-branch-option symbolic-ref HEAD >actual &&
grep hello actual
'
@@ -577,7 +649,7 @@ test_expect_success 'advice on unconfigured init.defaultBranch' '
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= git -c color.advice=always \
init unconfigured-default-branch-name 2>err &&
test_decode_color <err >decoded &&
- test_i18ngrep "<YELLOW>hint: " decoded
+ test_grep "<YELLOW>hint: " decoded
'
test_expect_success 'overridden default main branch name (env)' '
@@ -590,15 +662,20 @@ test_expect_success 'overridden default main branch name (env)' '
test_expect_success 'invalid default branch name' '
test_must_fail env GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME="with space" \
git init initial-branch-invalid 2>err &&
- test_i18ngrep "invalid branch name" err
+ test_grep "invalid branch name" err
'
test_expect_success 'branch -m with the initial branch' '
git init rename-initial &&
git -C rename-initial branch -m renamed &&
- test renamed = $(git -C rename-initial symbolic-ref --short HEAD) &&
+ echo renamed >expect &&
+ git -C rename-initial symbolic-ref --short HEAD >actual &&
+ test_cmp expect actual &&
+
git -C rename-initial branch -m renamed again &&
- test again = $(git -C rename-initial symbolic-ref --short HEAD)
+ echo again >expect &&
+ git -C rename-initial symbolic-ref --short HEAD >actual &&
+ test_cmp expect actual
'
test_done
diff --git a/t/t0002-gitfile.sh b/t/t0002-gitfile.sh
index 8440e6a..bf3bf60 100755
--- a/t/t0002-gitfile.sh
+++ b/t/t0002-gitfile.sh
@@ -7,6 +7,7 @@ Verify that plumbing commands work when .git is a file
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
objpath() {
@@ -21,23 +22,25 @@ test_expect_success 'initial setup' '
test_expect_success 'bad setup: invalid .git file format' '
echo "gitdir $REAL" >.git &&
test_must_fail git rev-parse 2>.err &&
- test_i18ngrep "invalid gitfile format" .err
+ test_grep "invalid gitfile format" .err
'
test_expect_success 'bad setup: invalid .git file path' '
echo "gitdir: $REAL.not" >.git &&
test_must_fail git rev-parse 2>.err &&
- test_i18ngrep "not a git repository" .err
+ test_grep "not a git repository" .err
'
test_expect_success 'final setup + check rev-parse --git-dir' '
echo "gitdir: $REAL" >.git &&
- test "$REAL" = "$(git rev-parse --git-dir)"
+ echo "$REAL" >expect &&
+ git rev-parse --git-dir >actual &&
+ test_cmp expect actual
'
test_expect_success 'check hash-object' '
echo "foo" >bar &&
- SHA=$(cat bar | git hash-object -w --stdin) &&
+ SHA=$(git hash-object -w --stdin <bar) &&
test_path_is_file "$REAL/objects/$(objpath $SHA)"
'
@@ -66,7 +69,9 @@ test_expect_success 'check commit-tree' '
test_expect_success 'check rev-list' '
git update-ref "HEAD" "$SHA" &&
- test "$SHA" = "$(git rev-list HEAD)"
+ git rev-list HEAD >actual &&
+ echo $SHA >expected &&
+ test_cmp expected actual
'
test_expect_success 'setup_git_dir twice in subdir' '
diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh
index 1e4c672..774b52c 100755
--- a/t/t0003-attributes.sh
+++ b/t/t0003-attributes.sh
@@ -2,6 +2,8 @@
test_description=gitattributes
+TEST_PASSES_SANITIZE_LEAK=true
+TEST_CREATE_REPO_NO_TEMPLATE=1
. ./test-lib.sh
attr_check_basic () {
@@ -17,13 +19,48 @@ attr_check () {
test_must_be_empty err
}
+attr_check_object_mode_basic () {
+ path="$1" &&
+ expect="$2" &&
+ check_opts="$3" &&
+ git check-attr $check_opts builtin_objectmode -- "$path" >actual 2>err &&
+ echo "$path: builtin_objectmode: $expect" >expect &&
+ test_cmp expect actual
+}
+
+attr_check_object_mode () {
+ attr_check_object_mode_basic "$@" &&
+ test_must_be_empty err
+}
+
attr_check_quote () {
path="$1" quoted_path="$2" expect="$3" &&
git check-attr test -- "$path" >actual &&
echo "\"$quoted_path\": test: $expect" >expect &&
test_cmp expect actual
+}
+
+attr_check_source () {
+ path="$1" expect="$2" source="$3" git_opts="$4" &&
+ echo "$path: test: $expect" >expect &&
+
+ git $git_opts check-attr --source $source test -- "$path" >actual 2>err &&
+ test_cmp expect actual &&
+ test_must_be_empty err &&
+
+ git $git_opts --attr-source="$source" check-attr test -- "$path" >actual 2>err &&
+ test_cmp expect actual &&
+ test_must_be_empty err
+
+ git $git_opts -c "attr.tree=$source" check-attr test -- "$path" >actual 2>err &&
+ test_cmp expect actual &&
+ test_must_be_empty err
+
+ GIT_ATTR_SOURCE="$source" git $git_opts check-attr test -- "$path" >actual 2>err &&
+ test_cmp expect actual &&
+ test_must_be_empty err
}
test_expect_success 'open-quoted pathname' '
@@ -31,7 +68,6 @@ test_expect_success 'open-quoted pathname' '
attr_check a unspecified
'
-
test_expect_success 'setup' '
mkdir -p a/b/d a/c b &&
(
@@ -78,12 +114,23 @@ test_expect_success 'setup' '
EOF
'
+test_expect_success 'setup branches' '
+ mkdir -p foo/bar &&
+ test_commit --printf "add .gitattributes" foo/bar/.gitattributes \
+ "f test=f\na/i test=n\n" tag-1 &&
+ test_commit --printf "add .gitattributes" foo/bar/.gitattributes \
+ "g test=g\na/i test=m\n" tag-2 &&
+ rm foo/bar/.gitattributes
+'
+
test_expect_success 'command line checks' '
test_must_fail git check-attr &&
test_must_fail git check-attr -- &&
test_must_fail git check-attr test &&
test_must_fail git check-attr test -- &&
test_must_fail git check-attr -- f &&
+ test_must_fail git check-attr --source &&
+ test_must_fail git check-attr --source not-a-valid-ref &&
echo "f" | test_must_fail git check-attr --stdin &&
echo "f" | test_must_fail git check-attr --stdin -- f &&
echo "f" | test_must_fail git check-attr --stdin test -- f &&
@@ -201,18 +248,24 @@ test_expect_success 'attribute test: read paths from stdin' '
test_cmp expect actual
'
-test_expect_success 'attribute test: --all option' '
+test_expect_success 'setup --all option' '
grep -v unspecified <expect-all | sort >specified-all &&
- sed -e "s/:.*//" <expect-all | uniq >stdin-all &&
- git check-attr --stdin --all <stdin-all | sort >actual &&
+ sed -e "s/:.*//" <expect-all | uniq >stdin-all
+'
+
+test_expect_success 'attribute test: --all option' '
+ git check-attr --stdin --all <stdin-all >tmp &&
+ sort tmp >actual &&
test_cmp specified-all actual
'
test_expect_success 'attribute test: --cached option' '
- git check-attr --cached --stdin --all <stdin-all | sort >actual &&
+ git check-attr --cached --stdin --all <stdin-all >tmp &&
+ sort tmp >actual &&
test_must_be_empty actual &&
git add .gitattributes a/.gitattributes a/b/.gitattributes &&
- git check-attr --cached --stdin --all <stdin-all | sort >actual &&
+ git check-attr --cached --stdin --all <stdin-all >tmp &&
+ sort tmp >actual &&
test_cmp specified-all actual
'
@@ -224,7 +277,7 @@ test_expect_success 'root subdir attribute test' '
test_expect_success 'negative patterns' '
echo "!f test=bar" >.gitattributes &&
git check-attr test -- '"'"'!f'"'"' 2>errors &&
- test_i18ngrep "Negative patterns are ignored" errors
+ test_grep "Negative patterns are ignored" errors
'
test_expect_success 'patterns starting with exclamation' '
@@ -279,8 +332,17 @@ test_expect_success 'using --git-dir and --work-tree' '
)
'
+test_expect_success 'using --source' '
+ attr_check_source foo/bar/f f tag-1 &&
+ attr_check_source foo/bar/a/i n tag-1 &&
+ attr_check_source foo/bar/f unspecified tag-2 &&
+ attr_check_source foo/bar/a/i m tag-2 &&
+ attr_check_source foo/bar/g g tag-2 &&
+ attr_check_source foo/bar/g unspecified tag-1
+'
+
test_expect_success 'setup bare' '
- git clone --bare . bare.git
+ git clone --template= --bare . bare.git
'
test_expect_success 'bare repository: check that .gitattribute is ignored' '
@@ -298,6 +360,86 @@ test_expect_success 'bare repository: check that .gitattribute is ignored' '
)
'
+bad_attr_source_err="fatal: bad --attr-source or GIT_ATTR_SOURCE"
+
+test_expect_success '--attr-source is bad' '
+ test_when_finished rm -rf empty &&
+ git init empty &&
+ (
+ cd empty &&
+ echo "$bad_attr_source_err" >expect_err &&
+ test_must_fail git --attr-source=HEAD check-attr test -- f/path 2>err &&
+ test_cmp expect_err err
+ )
+'
+
+test_expect_success 'attr.tree when HEAD is unborn' '
+ test_when_finished rm -rf empty &&
+ git init empty &&
+ (
+ cd empty &&
+ echo "f/path: test: unspecified" >expect &&
+ git -c attr.tree=HEAD check-attr test -- f/path >actual 2>err &&
+ test_must_be_empty err &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'bad attr source defaults to reading .gitattributes file' '
+ test_when_finished rm -rf empty &&
+ git init empty &&
+ (
+ cd empty &&
+ echo "f/path test=val" >.gitattributes &&
+ echo "f/path: test: val" >expect &&
+ git -c attr.tree=HEAD check-attr test -- f/path >actual 2>err &&
+ test_must_be_empty err &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'bare repo defaults to reading .gitattributes from HEAD' '
+ test_when_finished rm -rf test bare_with_gitattribute &&
+ git init test &&
+ test_commit -C test gitattributes .gitattributes "f/path test=val" &&
+ git clone --bare test bare_with_gitattribute &&
+ echo "f/path: test: val" >expect &&
+ git -C bare_with_gitattribute check-attr test -- f/path >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'precedence of --attr-source, GIT_ATTR_SOURCE, then attr.tree' '
+ test_when_finished rm -rf empty &&
+ git init empty &&
+ (
+ cd empty &&
+ git checkout -b attr-source &&
+ test_commit "val1" .gitattributes "f/path test=val1" &&
+ git checkout -b attr-tree &&
+ test_commit "val2" .gitattributes "f/path test=val2" &&
+ git checkout attr-source &&
+ echo "f/path: test: val1" >expect &&
+ GIT_ATTR_SOURCE=attr-source git -c attr.tree=attr-tree --attr-source=attr-source \
+ check-attr test -- f/path >actual &&
+ test_cmp expect actual &&
+ GIT_ATTR_SOURCE=attr-source git -c attr.tree=attr-tree \
+ check-attr test -- f/path >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'bare repository: with --source' '
+ (
+ cd bare.git &&
+ attr_check_source foo/bar/f f tag-1 &&
+ attr_check_source foo/bar/a/i n tag-1 &&
+ attr_check_source foo/bar/f unspecified tag-2 &&
+ attr_check_source foo/bar/a/i m tag-2 &&
+ attr_check_source foo/bar/g g tag-2 &&
+ attr_check_source foo/bar/g unspecified tag-1
+ )
+'
+
test_expect_success 'bare repository: check that --cached honors index' '
(
cd bare.git &&
@@ -311,6 +453,7 @@ test_expect_success 'bare repository: check that --cached honors index' '
test_expect_success 'bare repository: test info/attributes' '
(
cd bare.git &&
+ mkdir info &&
(
echo "f test=f" &&
echo "a/i test=a/i"
@@ -356,6 +499,7 @@ test_expect_success SYMLINKS 'symlinks respected in core.attributesFile' '
test_expect_success SYMLINKS 'symlinks respected in info/attributes' '
test_when_finished "rm .git/info/attributes" &&
+ mkdir .git/info &&
ln -s ../../attr .git/info/attributes &&
attr_check file set
'
@@ -366,7 +510,128 @@ test_expect_success SYMLINKS 'symlinks not respected in-tree' '
mkdir subdir &&
ln -s ../attr subdir/.gitattributes &&
attr_check_basic subdir/file unspecified &&
- test_i18ngrep "unable to access.*gitattributes" err
+ test_grep "unable to access.*gitattributes" err
+'
+
+test_expect_success 'large attributes line ignored in tree' '
+ test_when_finished "rm .gitattributes" &&
+ printf "path %02043d" 1 >.gitattributes &&
+ git check-attr --all path >actual 2>err &&
+ echo "warning: ignoring overly long attributes line 1" >expect &&
+ test_cmp expect err &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'large attributes line ignores trailing content in tree' '
+ test_when_finished "rm .gitattributes" &&
+ # older versions of Git broke lines at 2048 bytes; the 2045 bytes
+ # of 0-padding here is accounting for the three bytes of "a 1", which
+ # would knock "trailing" to the "next" line, where it would be
+ # erroneously parsed.
+ printf "a %02045dtrailing attribute\n" 1 >.gitattributes &&
+ git check-attr --all trailing >actual 2>err &&
+ echo "warning: ignoring overly long attributes line 1" >expect &&
+ test_cmp expect err &&
+ test_must_be_empty actual
+'
+
+test_expect_success EXPENSIVE 'large attributes file ignored in tree' '
+ test_when_finished "rm .gitattributes" &&
+ dd if=/dev/zero of=.gitattributes bs=1048576 count=101 2>/dev/null &&
+ git check-attr --all path >/dev/null 2>err &&
+ echo "warning: ignoring overly large gitattributes file ${SQ}.gitattributes${SQ}" >expect &&
+ test_cmp expect err
+'
+
+test_expect_success 'large attributes line ignored in index' '
+ test_when_finished "git update-index --remove .gitattributes" &&
+ blob=$(printf "path %02043d" 1 | git hash-object -w --stdin) &&
+ git update-index --add --cacheinfo 100644,$blob,.gitattributes &&
+ git check-attr --cached --all path >actual 2>err &&
+ echo "warning: ignoring overly long attributes line 1" >expect &&
+ test_cmp expect err &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'large attributes line ignores trailing content in index' '
+ test_when_finished "git update-index --remove .gitattributes" &&
+ blob=$(printf "a %02045dtrailing attribute\n" 1 | git hash-object -w --stdin) &&
+ git update-index --add --cacheinfo 100644,$blob,.gitattributes &&
+ git check-attr --cached --all trailing >actual 2>err &&
+ echo "warning: ignoring overly long attributes line 1" >expect &&
+ test_cmp expect err &&
+ test_must_be_empty actual
+'
+
+test_expect_success EXPENSIVE 'large attributes file ignored in index' '
+ test_when_finished "git update-index --remove .gitattributes" &&
+ blob=$(dd if=/dev/zero bs=1048576 count=101 2>/dev/null | git hash-object -w --stdin) &&
+ git update-index --add --cacheinfo 100644,$blob,.gitattributes &&
+ git check-attr --cached --all path >/dev/null 2>err &&
+ echo "warning: ignoring overly large gitattributes blob ${SQ}.gitattributes${SQ}" >expect &&
+ test_cmp expect err
+'
+
+test_expect_success 'builtin object mode attributes work (dir and regular paths)' '
+ >normal &&
+ attr_check_object_mode normal 100644 &&
+ mkdir dir &&
+ attr_check_object_mode dir 040000
+'
+
+test_expect_success POSIXPERM 'builtin object mode attributes work (executable)' '
+ >exec &&
+ chmod +x exec &&
+ attr_check_object_mode exec 100755
+'
+
+test_expect_success SYMLINKS 'builtin object mode attributes work (symlinks)' '
+ ln -s to_sym sym &&
+ attr_check_object_mode sym 120000
+'
+
+test_expect_success 'native object mode attributes work with --cached' '
+ >normal &&
+ git add normal &&
+ empty_blob=$(git rev-parse :normal) &&
+ git update-index --index-info <<-EOF &&
+ 100755 $empty_blob 0 exec
+ 120000 $empty_blob 0 symlink
+ EOF
+ attr_check_object_mode normal 100644 --cached &&
+ attr_check_object_mode exec 100755 --cached &&
+ attr_check_object_mode symlink 120000 --cached
+'
+
+test_expect_success 'check object mode attributes work for submodules' '
+ mkdir sub &&
+ (
+ cd sub &&
+ git init &&
+ mv .git .real &&
+ echo "gitdir: .real" >.git &&
+ test_commit first
+ ) &&
+ attr_check_object_mode sub 160000 &&
+ attr_check_object_mode sub unspecified --cached &&
+ git add sub &&
+ attr_check_object_mode sub 160000 --cached
+'
+
+test_expect_success 'we do not allow user defined builtin_* attributes' '
+ echo "foo* builtin_foo" >.gitattributes &&
+ git add .gitattributes 2>actual &&
+ echo "builtin_foo is not a valid attribute name: .gitattributes:1" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'user defined builtin_objectmode values are ignored' '
+ echo "foo* builtin_objectmode=12345" >.gitattributes &&
+ git add .gitattributes &&
+ >foo_1 &&
+ attr_check_object_mode_basic foo_1 100644 &&
+ echo "builtin_objectmode is not a valid attribute name: .gitattributes:1" >expect &&
+ test_cmp expect err
'
test_done
diff --git a/t/t0004-unwritable.sh b/t/t0004-unwritable.sh
index e3137d6..8114fac 100755
--- a/t/t0004-unwritable.sh
+++ b/t/t0004-unwritable.sh
@@ -2,6 +2,7 @@
test_description='detect unwritable repository and fail correctly'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -18,27 +19,66 @@ test_expect_success setup '
test_expect_success POSIXPERM,SANITY 'write-tree should notice unwritable repository' '
test_when_finished "chmod 775 .git/objects .git/objects/??" &&
chmod a-w .git/objects .git/objects/?? &&
- test_must_fail git write-tree
+ test_must_fail git write-tree 2>out.write-tree
+'
+
+test_lazy_prereq WRITE_TREE_OUT 'test -e "$TRASH_DIRECTORY"/out.write-tree'
+test_expect_success WRITE_TREE_OUT 'write-tree output on unwritable repository' '
+ cat >expect <<-\EOF &&
+ error: insufficient permission for adding an object to repository database .git/objects
+ fatal: git-write-tree: error building trees
+ EOF
+ test_cmp expect out.write-tree
'
test_expect_success POSIXPERM,SANITY 'commit should notice unwritable repository' '
test_when_finished "chmod 775 .git/objects .git/objects/??" &&
chmod a-w .git/objects .git/objects/?? &&
- test_must_fail git commit -m second
+ test_must_fail git commit -m second 2>out.commit
+'
+
+test_lazy_prereq COMMIT_OUT 'test -e "$TRASH_DIRECTORY"/out.commit'
+test_expect_success COMMIT_OUT 'commit output on unwritable repository' '
+ cat >expect <<-\EOF &&
+ error: insufficient permission for adding an object to repository database .git/objects
+ error: Error building trees
+ EOF
+ test_cmp expect out.commit
'
test_expect_success POSIXPERM,SANITY 'update-index should notice unwritable repository' '
test_when_finished "chmod 775 .git/objects .git/objects/??" &&
echo 6O >file &&
chmod a-w .git/objects .git/objects/?? &&
- test_must_fail git update-index file
+ test_must_fail git update-index file 2>out.update-index
+'
+
+test_lazy_prereq UPDATE_INDEX_OUT 'test -e "$TRASH_DIRECTORY"/out.update-index'
+test_expect_success UPDATE_INDEX_OUT 'update-index output on unwritable repository' '
+ cat >expect <<-\EOF &&
+ error: insufficient permission for adding an object to repository database .git/objects
+ error: file: failed to insert into database
+ fatal: Unable to process path file
+ EOF
+ test_cmp expect out.update-index
'
test_expect_success POSIXPERM,SANITY 'add should notice unwritable repository' '
test_when_finished "chmod 775 .git/objects .git/objects/??" &&
echo b >file &&
chmod a-w .git/objects .git/objects/?? &&
- test_must_fail git add file
+ test_must_fail git add file 2>out.add
+'
+
+test_lazy_prereq ADD_OUT 'test -e "$TRASH_DIRECTORY"/out.add'
+test_expect_success ADD_OUT 'add output on unwritable repository' '
+ cat >expect <<-\EOF &&
+ error: insufficient permission for adding an object to repository database .git/objects
+ error: file: failed to insert into database
+ error: unable to index file '\''file'\''
+ fatal: updating files failed
+ EOF
+ test_cmp expect out.add
'
test_done
diff --git a/t/t0005-signals.sh b/t/t0005-signals.sh
index 4c214bd..eba75a2 100755
--- a/t/t0005-signals.sh
+++ b/t/t0005-signals.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='signals work as we expect'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
cat >expect <<EOF
@@ -46,7 +48,7 @@ test_expect_success !MINGW 'a constipated git dies with SIGPIPE' '
'
test_expect_success !MINGW 'a constipated git dies with SIGPIPE even if parent ignores it' '
- OUT=$( ((trap "" PIPE; large_git; echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=$( ((trap "" PIPE && large_git; echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT"
'
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index 6b757d7..3031256 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test date parsing and printing'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# arbitrary reference time: 2009-08-30 19:20:00
@@ -44,6 +46,7 @@ check_show () {
TIME='1466000000 +0200'
check_show iso8601 "$TIME" '2016-06-15 16:13:20 +0200'
check_show iso8601-strict "$TIME" '2016-06-15T16:13:20+02:00'
+check_show iso8601-strict "$(echo "$TIME" | sed 's/+0200$/+0000/')" '2016-06-15T14:13:20Z'
check_show rfc2822 "$TIME" 'Wed, 15 Jun 2016 16:13:20 +0200'
check_show short "$TIME" '2016-06-15'
check_show default "$TIME" 'Wed Jun 15 16:13:20 2016 +0200'
@@ -63,6 +66,18 @@ check_show 'format-local:%%z' "$TIME" '%z'
check_show 'format:%Y-%m-%d %H:%M:%S' "$TIME" '2016-06-15 16:13:20'
check_show 'format-local:%Y-%m-%d %H:%M:%S' "$TIME" '2016-06-15 09:13:20' '' EST5
+check_show 'format:%s' '123456789 +1234' 123456789
+check_show 'format:%s' '123456789 -1234' 123456789
+check_show 'format-local:%s' '123456789 -1234' 123456789
+
+# negative TZ offset
+TIME='1466000000 -0200'
+check_show iso8601 "$TIME" '2016-06-15 12:13:20 -0200'
+check_show iso8601-strict "$TIME" '2016-06-15T12:13:20-02:00'
+check_show rfc2822 "$TIME" 'Wed, 15 Jun 2016 12:13:20 -0200'
+check_show default "$TIME" 'Wed Jun 15 12:13:20 2016 -0200'
+check_show raw "$TIME" '1466000000 -0200'
+
# arbitrary time absurdly far in the future
FUTURE="5758122296 -0400"
check_show iso "$FUTURE" "2152-06-19 18:24:56 -0400" TIME_IS_64BIT,TIME_T_IS_64BIT
@@ -82,6 +97,13 @@ check_parse 2008-02-14 bad
check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 +0000'
check_parse '2008-02-14 20:30:45 -0500' '2008-02-14 20:30:45 -0500'
check_parse '2008.02.14 20:30:45 -0500' '2008-02-14 20:30:45 -0500'
+check_parse '20080214T20:30:45' '2008-02-14 20:30:45 +0000'
+check_parse '20080214T20:30' '2008-02-14 20:30:00 +0000'
+check_parse '20080214T20' '2008-02-14 20:00:00 +0000'
+check_parse '20080214T203045' '2008-02-14 20:30:45 +0000'
+check_parse '20080214T2030' '2008-02-14 20:30:00 +0000'
+check_parse '20080214T000000.20' '2008-02-14 00:00:00 +0000'
+check_parse '20080214T00:00:00.20' '2008-02-14 00:00:00 +0000'
check_parse '20080214T203045-04:00' '2008-02-14 20:30:45 -0400'
check_parse '20080214T203045 -04:00' '2008-02-14 20:30:45 -0400'
check_parse '20080214T203045.019-04:00' '2008-02-14 20:30:45 -0400'
@@ -93,6 +115,7 @@ check_parse '2008-02-14 20:30:45 -05' '2008-02-14 20:30:45 -0500'
check_parse '2008-02-14 20:30:45 -:30' '2008-02-14 20:30:45 +0000'
check_parse '2008-02-14 20:30:45 -05:00' '2008-02-14 20:30:45 -0500'
check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 -0500' EST5
+check_parse 'Thu, 7 Apr 2005 15:14:13 -0700' '2005-04-07 15:14:13 -0700'
check_approxidate() {
echo "$1 -> $2 +0000" >expect
diff --git a/t/t0007-git-var.sh b/t/t0007-git-var.sh
index 88b9ae8..ff4fd93 100755
--- a/t/t0007-git-var.sh
+++ b/t/t0007-git-var.sh
@@ -1,8 +1,16 @@
#!/bin/sh
test_description='basic sanity checks for git var'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
+sane_unset_all_editors () {
+ sane_unset GIT_EDITOR &&
+ sane_unset VISUAL &&
+ sane_unset EDITOR
+}
+
test_expect_success 'get GIT_AUTHOR_IDENT' '
test_tick &&
echo "$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE" >expect &&
@@ -25,6 +33,198 @@ test_expect_success !FAIL_PREREQS,!AUTOIDENT 'requested identities are strict' '
)
'
+test_expect_success 'get GIT_DEFAULT_BRANCH without configuration' '
+ (
+ sane_unset GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME &&
+ git init defbranch &&
+ git -C defbranch symbolic-ref --short HEAD >expect &&
+ git var GIT_DEFAULT_BRANCH >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'get GIT_DEFAULT_BRANCH with configuration' '
+ test_config init.defaultbranch foo &&
+ (
+ sane_unset GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME &&
+ echo foo >expect &&
+ git var GIT_DEFAULT_BRANCH >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'get GIT_EDITOR without configuration' '
+ (
+ sane_unset_all_editors &&
+ test_expect_code 1 git var GIT_EDITOR >out &&
+ test_must_be_empty out
+ )
+'
+
+test_expect_success 'get GIT_EDITOR with configuration' '
+ test_config core.editor foo &&
+ (
+ sane_unset_all_editors &&
+ echo foo >expect &&
+ git var GIT_EDITOR >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'get GIT_EDITOR with environment variable GIT_EDITOR' '
+ (
+ sane_unset_all_editors &&
+ echo bar >expect &&
+ GIT_EDITOR=bar git var GIT_EDITOR >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'get GIT_EDITOR with environment variable EDITOR' '
+ (
+ sane_unset_all_editors &&
+ echo bar >expect &&
+ EDITOR=bar git var GIT_EDITOR >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'get GIT_EDITOR with configuration and environment variable GIT_EDITOR' '
+ test_config core.editor foo &&
+ (
+ sane_unset_all_editors &&
+ echo bar >expect &&
+ GIT_EDITOR=bar git var GIT_EDITOR >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'get GIT_EDITOR with configuration and environment variable EDITOR' '
+ test_config core.editor foo &&
+ (
+ sane_unset_all_editors &&
+ echo foo >expect &&
+ EDITOR=bar git var GIT_EDITOR >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'get GIT_SEQUENCE_EDITOR without configuration' '
+ (
+ sane_unset GIT_SEQUENCE_EDITOR &&
+ git var GIT_EDITOR >expect &&
+ git var GIT_SEQUENCE_EDITOR >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'get GIT_SEQUENCE_EDITOR with configuration' '
+ test_config sequence.editor foo &&
+ (
+ sane_unset GIT_SEQUENCE_EDITOR &&
+ echo foo >expect &&
+ git var GIT_SEQUENCE_EDITOR >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'get GIT_SEQUENCE_EDITOR with environment variable' '
+ (
+ sane_unset GIT_SEQUENCE_EDITOR &&
+ echo bar >expect &&
+ GIT_SEQUENCE_EDITOR=bar git var GIT_SEQUENCE_EDITOR >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'get GIT_SEQUENCE_EDITOR with configuration and environment variable' '
+ test_config sequence.editor foo &&
+ (
+ sane_unset GIT_SEQUENCE_EDITOR &&
+ echo bar >expect &&
+ GIT_SEQUENCE_EDITOR=bar git var GIT_SEQUENCE_EDITOR >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success POSIXPERM 'GIT_SHELL_PATH points to a valid executable' '
+ shellpath=$(git var GIT_SHELL_PATH) &&
+ test_path_is_executable "$shellpath"
+'
+
+# We know in this environment that our shell will be one of a few fixed values
+# that all end in "sh".
+test_expect_success MINGW 'GIT_SHELL_PATH points to a suitable shell' '
+ shellpath=$(git var GIT_SHELL_PATH) &&
+ case "$shellpath" in
+ *sh) ;;
+ *) return 1;;
+ esac
+'
+
+test_expect_success 'GIT_ATTR_SYSTEM produces expected output' '
+ test_must_fail env GIT_ATTR_NOSYSTEM=1 git var GIT_ATTR_SYSTEM &&
+ (
+ sane_unset GIT_ATTR_NOSYSTEM &&
+ systempath=$(git var GIT_ATTR_SYSTEM) &&
+ test "$systempath" != ""
+ )
+'
+
+test_expect_success 'GIT_ATTR_GLOBAL points to the correct location' '
+ TRASHDIR="$(test-tool path-utils normalize_path_copy "$(pwd)")" &&
+ globalpath=$(XDG_CONFIG_HOME="$TRASHDIR/.config" git var GIT_ATTR_GLOBAL) &&
+ test "$globalpath" = "$TRASHDIR/.config/git/attributes" &&
+ (
+ sane_unset XDG_CONFIG_HOME &&
+ globalpath=$(HOME="$TRASHDIR" git var GIT_ATTR_GLOBAL) &&
+ test "$globalpath" = "$TRASHDIR/.config/git/attributes"
+ )
+'
+
+test_expect_success 'GIT_CONFIG_SYSTEM points to the correct location' '
+ TRASHDIR="$(test-tool path-utils normalize_path_copy "$(pwd)")" &&
+ test_must_fail env GIT_CONFIG_NOSYSTEM=1 git var GIT_CONFIG_SYSTEM &&
+ (
+ sane_unset GIT_CONFIG_NOSYSTEM &&
+ systempath=$(git var GIT_CONFIG_SYSTEM) &&
+ test "$systempath" != "" &&
+ systempath=$(GIT_CONFIG_SYSTEM=/dev/null git var GIT_CONFIG_SYSTEM) &&
+ if test_have_prereq MINGW
+ then
+ test "$systempath" = "nul"
+ else
+ test "$systempath" = "/dev/null"
+ fi &&
+ systempath=$(GIT_CONFIG_SYSTEM="$TRASHDIR/gitconfig" git var GIT_CONFIG_SYSTEM) &&
+ test "$systempath" = "$TRASHDIR/gitconfig"
+ )
+'
+
+test_expect_success 'GIT_CONFIG_GLOBAL points to the correct location' '
+ TRASHDIR="$(test-tool path-utils normalize_path_copy "$(pwd)")" &&
+ HOME="$TRASHDIR" XDG_CONFIG_HOME="$TRASHDIR/foo" git var GIT_CONFIG_GLOBAL >actual &&
+ echo "$TRASHDIR/foo/git/config" >expected &&
+ echo "$TRASHDIR/.gitconfig" >>expected &&
+ test_cmp expected actual &&
+ (
+ sane_unset XDG_CONFIG_HOME &&
+ HOME="$TRASHDIR" git var GIT_CONFIG_GLOBAL >actual &&
+ echo "$TRASHDIR/.config/git/config" >expected &&
+ echo "$TRASHDIR/.gitconfig" >>expected &&
+ test_cmp expected actual &&
+ globalpath=$(GIT_CONFIG_GLOBAL=/dev/null git var GIT_CONFIG_GLOBAL) &&
+ if test_have_prereq MINGW
+ then
+ test "$globalpath" = "nul"
+ else
+ test "$globalpath" = "/dev/null"
+ fi &&
+ globalpath=$(GIT_CONFIG_GLOBAL="$TRASHDIR/gitconfig" git var GIT_CONFIG_GLOBAL) &&
+ test "$globalpath" = "$TRASHDIR/gitconfig"
+ )
+'
+
# For git var -l, we check only a representative variable;
# testing the whole output would make our test too brittle with
# respect to unrelated changes in the test suite's environment.
@@ -42,8 +242,39 @@ test_expect_success 'git var -l lists config' '
test_cmp expect actual.bare
'
+test_expect_success 'git var -l lists multiple global configs' '
+ TRASHDIR="$(test-tool path-utils normalize_path_copy "$(pwd)")" &&
+ HOME="$TRASHDIR" XDG_CONFIG_HOME="$TRASHDIR/foo" git var -l >actual &&
+ grep "^GIT_CONFIG_GLOBAL=" actual >filtered &&
+ echo "GIT_CONFIG_GLOBAL=$TRASHDIR/foo/git/config" >expected &&
+ echo "GIT_CONFIG_GLOBAL=$TRASHDIR/.gitconfig" >>expected &&
+ test_cmp expected filtered
+'
+
+test_expect_success 'git var -l does not split multiline editors' '
+ (
+ GIT_EDITOR="!f() {
+ echo Hello!
+ }; f" &&
+ export GIT_EDITOR &&
+ echo "GIT_EDITOR=$GIT_EDITOR" >expected &&
+ git var -l >var &&
+ sed -n -e "/^GIT_EDITOR/,\$p" var | head -n 3 >actual &&
+ test_cmp expected actual
+ )
+'
+
test_expect_success 'listing and asking for variables are exclusive' '
test_must_fail git var -l GIT_COMMITTER_IDENT
'
+test_expect_success '`git var -l` works even without HOME' '
+ (
+ XDG_CONFIG_HOME= &&
+ export XDG_CONFIG_HOME &&
+ unset HOME &&
+ git var -l
+ )
+'
+
test_done
diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh
index a594b4a..361446b 100755
--- a/t/t0008-ignores.sh
+++ b/t/t0008-ignores.sh
@@ -2,6 +2,8 @@
test_description=check-ignore
+TEST_PASSES_SANITIZE_LEAK=true
+TEST_CREATE_REPO_NO_TEMPLATE=1
. ./test-lib.sh
init_vars () {
@@ -47,7 +49,7 @@ broken_c_unquote_verbose () {
stderr_contains () {
regexp="$1"
- if test_i18ngrep "$regexp" "$HOME/stderr"
+ if test_grep "$regexp" "$HOME/stderr"
then
return 0
else
@@ -199,7 +201,7 @@ test_expect_success 'setup' '
do
: >$dir/not-ignored &&
: >$dir/ignored-and-untracked &&
- : >$dir/ignored-but-in-index
+ : >$dir/ignored-but-in-index || return 1
done &&
git add -f ignored-but-in-index a/ignored-but-in-index &&
cat <<-\EOF >a/.gitignore &&
@@ -224,7 +226,8 @@ test_expect_success 'setup' '
!globaltwo
globalthree
EOF
- cat <<-\EOF >>.git/info/exclude
+ mkdir .git/info &&
+ cat <<-\EOF >.git/info/exclude
per-repo
EOF
'
@@ -542,9 +545,9 @@ test_expect_success_multi 'submodule from subdirectory' '' '
test_expect_success 'global ignore not yet enabled' '
expect_from_stdin <<-\EOF &&
- .git/info/exclude:7:per-repo per-repo
+ .git/info/exclude:1:per-repo per-repo
a/.gitignore:2:*three a/globalthree
- .git/info/exclude:7:per-repo a/per-repo
+ .git/info/exclude:1:per-repo a/per-repo
EOF
test_check_ignore "-v globalone per-repo a/globalthree a/per-repo not-ignored a/globaltwo"
'
@@ -565,10 +568,10 @@ test_expect_success 'global ignore with -v' '
enable_global_excludes &&
expect_from_stdin <<-EOF &&
$global_excludes:1:globalone globalone
- .git/info/exclude:7:per-repo per-repo
+ .git/info/exclude:1:per-repo per-repo
$global_excludes:3:globalthree globalthree
a/.gitignore:2:*three a/globalthree
- .git/info/exclude:7:per-repo a/per-repo
+ .git/info/exclude:1:per-repo a/per-repo
$global_excludes:2:!globaltwo globaltwo
EOF
test_check_ignore "-v globalone per-repo globalthree a/globalthree a/per-repo not-ignored globaltwo"
@@ -802,6 +805,49 @@ test_expect_success 'existing directory and file' '
grep top-level-dir actual
'
+test_expect_success 'exact prefix matching (with root)' '
+ test_when_finished rm -r a &&
+ mkdir -p a/git a/git-foo &&
+ touch a/git/foo a/git-foo/bar &&
+ echo /git/ >a/.gitignore &&
+ git check-ignore a/git a/git/foo a/git-foo a/git-foo/bar >actual &&
+ cat >expect <<-\EOF &&
+ a/git
+ a/git/foo
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'exact prefix matching (without root)' '
+ test_when_finished rm -r a &&
+ mkdir -p a/git a/git-foo &&
+ touch a/git/foo a/git-foo/bar &&
+ echo git/ >a/.gitignore &&
+ git check-ignore a/git a/git/foo a/git-foo a/git-foo/bar >actual &&
+ cat >expect <<-\EOF &&
+ a/git
+ a/git/foo
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'directories and ** matches' '
+ cat >.gitignore <<-\EOF &&
+ data/**
+ !data/**/
+ !data/**/*.txt
+ EOF
+ git check-ignore file \
+ data/file data/data1/file1 data/data1/file1.txt \
+ data/data2/file2 data/data2/file2.txt >actual &&
+ cat >expect <<-\EOF &&
+ data/file
+ data/data1/file1
+ data/data2/file2
+ EOF
+ test_cmp expect actual
+'
+
############################################################################
#
# test whitespace handling
@@ -896,7 +942,7 @@ test_expect_success SYMLINKS 'symlinks not respected in-tree' '
ln -s ignore subdir/.gitignore &&
test_must_fail git check-ignore subdir/file >actual 2>err &&
test_must_be_empty actual &&
- test_i18ngrep "unable to access.*gitignore" err
+ test_grep "unable to access.*gitignore" err
'
test_done
diff --git a/t/t0009-prio-queue.sh b/t/t0009-prio-queue.sh
deleted file mode 100755
index 3941ad2..0000000
--- a/t/t0009-prio-queue.sh
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/bin/sh
-
-test_description='basic tests for priority queue implementation'
-. ./test-lib.sh
-
-cat >expect <<'EOF'
-1
-2
-3
-4
-5
-5
-6
-7
-8
-9
-10
-EOF
-test_expect_success 'basic ordering' '
- test-tool prio-queue 2 6 3 10 9 5 7 4 5 8 1 dump >actual &&
- test_cmp expect actual
-'
-
-cat >expect <<'EOF'
-2
-3
-4
-1
-5
-6
-EOF
-test_expect_success 'mixed put and get' '
- test-tool prio-queue 6 2 4 get 5 3 get get 1 dump >actual &&
- test_cmp expect actual
-'
-
-cat >expect <<'EOF'
-1
-2
-NULL
-1
-2
-NULL
-EOF
-test_expect_success 'notice empty queue' '
- test-tool prio-queue 1 2 get get get 1 2 get get get >actual &&
- test_cmp expect actual
-'
-
-cat >expect <<'EOF'
-3
-2
-6
-4
-5
-1
-8
-EOF
-test_expect_success 'stack order' '
- test-tool prio-queue stack 8 1 5 4 6 2 3 dump >actual &&
- test_cmp expect actual
-'
-
-test_done
diff --git a/t/t0010-racy-git.sh b/t/t0010-racy-git.sh
index 5657c5a..84172a3 100755
--- a/t/t0010-racy-git.sh
+++ b/t/t0010-racy-git.sh
@@ -2,6 +2,7 @@
test_description='racy GIT'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# This test can give false success if your machine is sufficiently
@@ -9,25 +10,24 @@ test_description='racy GIT'
for trial in 0 1 2 3 4
do
- rm -f .git/index
- echo frotz >infocom
- git update-index --add infocom
- echo xyzzy >infocom
-
- files=$(git diff-files -p)
- test_expect_success \
- "Racy GIT trial #$trial part A" \
- 'test "" != "$files"'
-
+ test_expect_success "Racy git trial #$trial part A" '
+ rm -f .git/index &&
+ echo frotz >infocom &&
+ git update-index --add infocom &&
+ echo xyzzy >infocom &&
+
+ git diff-files -p >out &&
+ test_file_not_empty out
+ '
sleep 1
- echo xyzzy >cornerstone
- git update-index --add cornerstone
- files=$(git diff-files -p)
- test_expect_success \
- "Racy GIT trial #$trial part B" \
- 'test "" != "$files"'
+ test_expect_success "Racy git trial #$trial part B" '
+ echo xyzzy >cornerstone &&
+ git update-index --add cornerstone &&
+ git diff-files -p >out &&
+ test_file_not_empty out
+ '
done
test_done
diff --git a/t/t0011-hashmap.sh b/t/t0011-hashmap.sh
index 5343ffd..46e74ad 100755
--- a/t/t0011-hashmap.sh
+++ b/t/t0011-hashmap.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test hashmap and string hash functions'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_hashmap() {
@@ -218,7 +220,7 @@ test_expect_success 'grow / shrink' '
for n in $(test_seq 51)
do
echo put key$n value$n >> in &&
- echo NULL >> expect
+ echo NULL >> expect || return 1
done &&
echo size >> in &&
echo 64 51 >> expect &&
@@ -229,7 +231,7 @@ test_expect_success 'grow / shrink' '
for n in $(test_seq 12)
do
echo remove key$n >> in &&
- echo value$n >> expect
+ echo value$n >> expect || return 1
done &&
echo size >> in &&
echo 256 40 >> expect &&
@@ -237,7 +239,7 @@ test_expect_success 'grow / shrink' '
echo value40 >> expect &&
echo size >> in &&
echo 64 39 >> expect &&
- cat in | test-tool hashmap > out &&
+ test-tool hashmap <in >out &&
test_cmp expect out
'
diff --git a/t/t0012-help.sh b/t/t0012-help.sh
index 5679e29..1d273d9 100755
--- a/t/t0012-help.sh
+++ b/t/t0012-help.sh
@@ -34,6 +34,46 @@ test_expect_success 'basic help commands' '
git help -a >/dev/null
'
+test_expect_success 'invalid usage' '
+ test_expect_code 129 git help -a add &&
+ test_expect_code 129 git help --all add &&
+
+ test_expect_code 129 git help -g add &&
+ test_expect_code 129 git help -a -c &&
+
+ test_expect_code 129 git help -g add &&
+ test_expect_code 129 git help -a -g &&
+
+ test_expect_code 129 git help --user-interfaces add &&
+
+ test_expect_code 129 git help -g -c &&
+ test_expect_code 129 git help --config-for-completion add &&
+ test_expect_code 129 git help --config-sections-for-completion add
+'
+
+for opt in '-a' '-g' '-c' '--config-for-completion' '--config-sections-for-completion'
+do
+ test_expect_success "invalid usage of '$opt' with [-i|-m|-w]" '
+ git help $opt &&
+ test_expect_code 129 git help $opt -i &&
+ test_expect_code 129 git help $opt -m &&
+ test_expect_code 129 git help $opt -w
+ '
+
+ if test "$opt" = "-a"
+ then
+ continue
+ fi
+
+ test_expect_success "invalid usage of '$opt' with --no-external-commands" '
+ test_expect_code 129 git help $opt --no-external-commands
+ '
+
+ test_expect_success "invalid usage of '$opt' with --no-aliases" '
+ test_expect_code 129 git help $opt --no-external-commands
+ '
+done
+
test_expect_success "works for commands and guides by default" '
configure_help &&
git help status &&
@@ -60,28 +100,164 @@ test_expect_success "--help does not work for guides" "
test_expect_success 'git help' '
git help >help.output &&
- test_i18ngrep "^ clone " help.output &&
- test_i18ngrep "^ add " help.output &&
- test_i18ngrep "^ log " help.output &&
- test_i18ngrep "^ commit " help.output &&
- test_i18ngrep "^ fetch " help.output
+ test_grep "^ clone " help.output &&
+ test_grep "^ add " help.output &&
+ test_grep "^ log " help.output &&
+ test_grep "^ commit " help.output &&
+ test_grep "^ fetch " help.output
'
+
test_expect_success 'git help -g' '
git help -g >help.output &&
- test_i18ngrep "^ attributes " help.output &&
- test_i18ngrep "^ everyday " help.output &&
- test_i18ngrep "^ tutorial " help.output
+ test_grep "^ everyday " help.output &&
+ test_grep "^ tutorial " help.output
+'
+
+test_expect_success 'git help fails for non-existing html pages' '
+ configure_help &&
+ mkdir html-empty &&
+ test_must_fail git -c help.htmlpath=html-empty help status &&
+ test_must_be_empty test-browser.log
+'
+
+test_expect_success 'git help succeeds without git.html' '
+ configure_help &&
+ mkdir html-with-docs &&
+ touch html-with-docs/git-status.html &&
+ git -c help.htmlpath=html-with-docs help status &&
+ echo "html-with-docs/git-status.html" >expect &&
+ test_cmp expect test-browser.log
+'
+
+test_expect_success 'git help --user-interfaces' '
+ git help --user-interfaces >help.output &&
+ grep "^ attributes " help.output &&
+ grep "^ mailmap " help.output
+'
+
+test_expect_success 'git help -c' '
+ git help -c >help.output &&
+ cat >expect <<-\EOF &&
+
+ '\''git help config'\'' for more information
+ EOF
+ grep -v -E \
+ -e "^[^.]+\.[^.]+$" \
+ -e "^[^.]+\.[^.]+\.[^.]+$" \
+ help.output >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git help --config-for-completion' '
+ git help -c >human &&
+ grep -E \
+ -e "^[^.]+\.[^.]+$" \
+ -e "^[^.]+\.[^.]+\.[^.]+$" human |
+ sed -e "s/\*.*//" -e "s/<.*//" |
+ sort -u >human.munged &&
+
+ git help --config-for-completion >vars &&
+ test_cmp human.munged vars
+'
+
+test_expect_success 'git help --config-sections-for-completion' '
+ git help -c >human &&
+ grep -E \
+ -e "^[^.]+\.[^.]+$" \
+ -e "^[^.]+\.[^.]+\.[^.]+$" human |
+ sed -e "s/\..*//" |
+ sort -u >human.munged &&
+
+ git help --config-sections-for-completion >sections &&
+ test_cmp human.munged sections
+'
+
+test_section_spacing () {
+ cat >expect &&
+ "$@" >out &&
+ grep -E "(^[^ ]|^$)" out >actual
+}
+
+test_section_spacing_trailer () {
+ test_section_spacing "$@" &&
+ test_expect_code 1 git >out &&
+ sed -n '/list available subcommands/,$p' <out >>expect
+}
+
+
+for cmd in git "git help"
+do
+ test_expect_success "'$cmd' section spacing" '
+ test_section_spacing_trailer git help <<-\EOF &&
+ usage: git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]
+
+ These are common Git commands used in various situations:
+
+ start a working area (see also: git help tutorial)
+
+ work on the current change (see also: git help everyday)
+
+ examine the history and state (see also: git help revisions)
+
+ grow, mark and tweak your common history
+
+ collaborate (see also: git help workflows)
+
+ EOF
+ test_cmp expect actual
+ '
+done
+
+test_expect_success "'git help -a' section spacing" '
+ test_section_spacing \
+ git help -a --no-external-commands --no-aliases <<-\EOF &&
+ See '\''git help <command>'\'' to read about a specific subcommand
+
+ Main Porcelain Commands
+
+ Ancillary Commands / Manipulators
+
+ Ancillary Commands / Interrogators
+
+ Interacting with Others
+
+ Low-level Commands / Manipulators
+
+ Low-level Commands / Interrogators
+
+ Low-level Commands / Syncing Repositories
+
+ Low-level Commands / Internal Helpers
+
+ User-facing repository, command and file interfaces
+
+ Developer-facing file formats, protocols and other interfaces
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "'git help -g' section spacing" '
+ test_section_spacing_trailer git help -g <<-\EOF &&
+ The Git concept guides are:
+
+ EOF
+ test_cmp expect actual
'
test_expect_success 'generate builtin list' '
+ mkdir -p sub &&
git --list-cmds=builtins >builtins
'
while read builtin
do
test_expect_success "$builtin can handle -h" '
- test_expect_code 129 git $builtin -h >output 2>&1 &&
- test_i18ngrep usage output
+ (
+ GIT_CEILING_DIRECTORIES=$(pwd) &&
+ export GIT_CEILING_DIRECTORIES &&
+ test_expect_code 129 git -C sub $builtin -h >output 2>&1
+ ) &&
+ test_grep usage output
'
done <builtins
diff --git a/t/t0013-sha1dc.sh b/t/t0013-sha1dc.sh
index 419f31a..0881417 100755
--- a/t/t0013-sha1dc.sh
+++ b/t/t0013-sha1dc.sh
@@ -1,18 +1,22 @@
#!/bin/sh
test_description='test sha1 collision detection'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
TEST_DATA="$TEST_DIRECTORY/t0013"
-if test -z "$DC_SHA1"
+test_lazy_prereq SHA1_IS_SHA1DC 'test-tool sha1-is-sha1dc'
+
+if ! test_have_prereq SHA1_IS_SHA1DC
then
- skip_all='skipping sha1 collision tests, DC_SHA1 not set'
+ skip_all='skipping sha1 collision tests, not using sha1collisiondetection'
test_done
fi
test_expect_success 'test-sha1 detects shattered pdf' '
test_must_fail test-tool sha1 <"$TEST_DATA/shattered-1.pdf" 2>err &&
- test_i18ngrep collision err &&
+ test_grep collision err &&
grep 38762cf7f55934b34d179ae6a4c80cadccbb7f0a err
'
diff --git a/t/t0014-alias.sh b/t/t0014-alias.sh
index 8d3d914..9556834 100755
--- a/t/t0014-alias.sh
+++ b/t/t0014-alias.sh
@@ -8,7 +8,7 @@ test_expect_success 'nested aliases - internal execution' '
git config alias.nested-internal-1 nested-internal-2 &&
git config alias.nested-internal-2 status &&
git nested-internal-1 >output &&
- test_i18ngrep "^On branch " output
+ test_grep "^On branch " output
'
test_expect_success 'nested aliases - mixed execution' '
@@ -16,7 +16,7 @@ test_expect_success 'nested aliases - mixed execution' '
git config alias.nested-external-2 "!git nested-external-3" &&
git config alias.nested-external-3 status &&
git nested-external-1 >output &&
- test_i18ngrep "^On branch " output
+ test_grep "^On branch " output
'
test_expect_success 'looping aliases - internal execution' '
@@ -24,7 +24,7 @@ test_expect_success 'looping aliases - internal execution' '
git config alias.loop-internal-2 loop-internal-3 &&
git config alias.loop-internal-3 loop-internal-2 &&
test_must_fail git loop-internal-1 2>output &&
- test_i18ngrep "^fatal: alias loop detected: expansion of" output
+ test_grep "^fatal: alias loop detected: expansion of" output
'
# This test is disabled until external loops are fixed, because would block
@@ -34,7 +34,7 @@ test_expect_success 'looping aliases - internal execution' '
# git config alias.loop-mixed-1 loop-mixed-2 &&
# git config alias.loop-mixed-2 "!git loop-mixed-1" &&
# test_must_fail git loop-mixed-1 2>output &&
-# test_i18ngrep "^fatal: alias loop detected: expansion of" output
+# test_grep "^fatal: alias loop detected: expansion of" output
#'
test_expect_success 'run-command formats empty args properly' '
diff --git a/t/t0015-hash.sh b/t/t0015-hash.sh
index 291e906..0a087a1 100755
--- a/t/t0015-hash.sh
+++ b/t/t0015-hash.sh
@@ -1,8 +1,9 @@
#!/bin/sh
test_description='test basic hash implementation'
-. ./test-lib.sh
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
test_expect_success 'test basic SHA-1 hash values' '
test-tool sha1 </dev/null >actual &&
@@ -15,7 +16,7 @@ test_expect_success 'test basic SHA-1 hash values' '
grep c12252ceda8be8994d5fa0290a47231c1d16aae3 actual &&
printf "abcdefghijklmnopqrstuvwxyz" | test-tool sha1 >actual &&
grep 32d10c7b8cf96570ca04ce37f2a19d84240d3a89 actual &&
- perl -e "$| = 1; print q{aaaaaaaaaa} for 1..100000;" | \
+ perl -e "$| = 1; print q{aaaaaaaaaa} for 1..100000;" |
test-tool sha1 >actual &&
grep 34aa973cd4c4daa4f61eeb2bdbad27316534016f actual &&
printf "blob 0\0" | test-tool sha1 >actual &&
@@ -38,10 +39,10 @@ test_expect_success 'test basic SHA-256 hash values' '
printf "abcdefghijklmnopqrstuvwxyz" | test-tool sha256 >actual &&
grep 71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73 actual &&
# Try to exercise the chunking code by turning autoflush on.
- perl -e "$| = 1; print q{aaaaaaaaaa} for 1..100000;" | \
+ perl -e "$| = 1; print q{aaaaaaaaaa} for 1..100000;" |
test-tool sha256 >actual &&
grep cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0 actual &&
- perl -e "$| = 1; print q{abcdefghijklmnopqrstuvwxyz} for 1..100000;" | \
+ perl -e "$| = 1; print q{abcdefghijklmnopqrstuvwxyz} for 1..100000;" |
test-tool sha256 >actual &&
grep e406ba321ca712ad35a698bf0af8d61fc4dc40eca6bdcea4697962724ccbde35 actual &&
printf "blob 0\0" | test-tool sha256 >actual &&
diff --git a/t/t0016-oidmap.sh b/t/t0016-oidmap.sh
index 31f8276..0faef1f 100755
--- a/t/t0016-oidmap.sh
+++ b/t/t0016-oidmap.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test oidmap'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# This purposefully is very similar to t0011-hashmap.sh
diff --git a/t/t0017-env-helper.sh b/t/t0017-env-helper.sh
index 4a159f9..fc14ba0 100755
--- a/t/t0017-env-helper.sh
+++ b/t/t0017-env-helper.sh
@@ -1,86 +1,87 @@
#!/bin/sh
-test_description='test env--helper'
+test_description='test test-tool env-helper'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
-test_expect_success 'env--helper usage' '
- test_must_fail git env--helper &&
- test_must_fail git env--helper --type=bool &&
- test_must_fail git env--helper --type=ulong &&
- test_must_fail git env--helper --type=bool &&
- test_must_fail git env--helper --type=bool --default &&
- test_must_fail git env--helper --type=bool --default= &&
- test_must_fail git env--helper --defaultxyz
+test_expect_success 'test-tool env-helper usage' '
+ test_must_fail test-tool env-helper &&
+ test_must_fail test-tool env-helper --type=bool &&
+ test_must_fail test-tool env-helper --type=ulong &&
+ test_must_fail test-tool env-helper --type=bool &&
+ test_must_fail test-tool env-helper --type=bool --default &&
+ test_must_fail test-tool env-helper --type=bool --default= &&
+ test_must_fail test-tool env-helper --defaultxyz
'
-test_expect_success 'env--helper bad default values' '
- test_must_fail git env--helper --type=bool --default=1xyz MISSING &&
- test_must_fail git env--helper --type=ulong --default=1xyz MISSING
+test_expect_success 'test-tool env-helper bad default values' '
+ test_must_fail test-tool env-helper --type=bool --default=1xyz MISSING &&
+ test_must_fail test-tool env-helper --type=ulong --default=1xyz MISSING
'
-test_expect_success 'env--helper --type=bool' '
+test_expect_success 'test-tool env-helper --type=bool' '
# Test various --default bool values
echo true >expected &&
- git env--helper --type=bool --default=1 MISSING >actual &&
+ test-tool env-helper --type=bool --default=1 MISSING >actual &&
test_cmp expected actual &&
- git env--helper --type=bool --default=yes MISSING >actual &&
+ test-tool env-helper --type=bool --default=yes MISSING >actual &&
test_cmp expected actual &&
- git env--helper --type=bool --default=true MISSING >actual &&
+ test-tool env-helper --type=bool --default=true MISSING >actual &&
test_cmp expected actual &&
echo false >expected &&
- test_must_fail git env--helper --type=bool --default=0 MISSING >actual &&
+ test_must_fail test-tool env-helper --type=bool --default=0 MISSING >actual &&
test_cmp expected actual &&
- test_must_fail git env--helper --type=bool --default=no MISSING >actual &&
+ test_must_fail test-tool env-helper --type=bool --default=no MISSING >actual &&
test_cmp expected actual &&
- test_must_fail git env--helper --type=bool --default=false MISSING >actual &&
+ test_must_fail test-tool env-helper --type=bool --default=false MISSING >actual &&
test_cmp expected actual &&
# No output with --exit-code
- git env--helper --type=bool --default=true --exit-code MISSING >actual.out 2>actual.err &&
+ test-tool env-helper --type=bool --default=true --exit-code MISSING >actual.out 2>actual.err &&
test_must_be_empty actual.out &&
test_must_be_empty actual.err &&
- test_must_fail git env--helper --type=bool --default=false --exit-code MISSING >actual.out 2>actual.err &&
+ test_must_fail test-tool env-helper --type=bool --default=false --exit-code MISSING >actual.out 2>actual.err &&
test_must_be_empty actual.out &&
test_must_be_empty actual.err &&
# Existing variable
- EXISTS=true git env--helper --type=bool --default=false --exit-code EXISTS >actual.out 2>actual.err &&
+ EXISTS=true test-tool env-helper --type=bool --default=false --exit-code EXISTS >actual.out 2>actual.err &&
test_must_be_empty actual.out &&
test_must_be_empty actual.err &&
test_must_fail \
env EXISTS=false \
- git env--helper --type=bool --default=true --exit-code EXISTS >actual.out 2>actual.err &&
+ test-tool env-helper --type=bool --default=true --exit-code EXISTS >actual.out 2>actual.err &&
test_must_be_empty actual.out &&
test_must_be_empty actual.err
'
-test_expect_success 'env--helper --type=ulong' '
+test_expect_success 'test-tool env-helper --type=ulong' '
echo 1234567890 >expected &&
- git env--helper --type=ulong --default=1234567890 MISSING >actual.out 2>actual.err &&
+ test-tool env-helper --type=ulong --default=1234567890 MISSING >actual.out 2>actual.err &&
test_cmp expected actual.out &&
test_must_be_empty actual.err &&
echo 0 >expected &&
- test_must_fail git env--helper --type=ulong --default=0 MISSING >actual &&
+ test_must_fail test-tool env-helper --type=ulong --default=0 MISSING >actual &&
test_cmp expected actual &&
- git env--helper --type=ulong --default=1234567890 --exit-code MISSING >actual.out 2>actual.err &&
+ test-tool env-helper --type=ulong --default=1234567890 --exit-code MISSING >actual.out 2>actual.err &&
test_must_be_empty actual.out &&
test_must_be_empty actual.err &&
- EXISTS=1234567890 git env--helper --type=ulong --default=0 EXISTS --exit-code >actual.out 2>actual.err &&
+ EXISTS=1234567890 test-tool env-helper --type=ulong --default=0 EXISTS --exit-code >actual.out 2>actual.err &&
test_must_be_empty actual.out &&
test_must_be_empty actual.err &&
echo 1234567890 >expected &&
- EXISTS=1234567890 git env--helper --type=ulong --default=0 EXISTS >actual.out 2>actual.err &&
+ EXISTS=1234567890 test-tool env-helper --type=ulong --default=0 EXISTS >actual.out 2>actual.err &&
test_cmp expected actual.out &&
test_must_be_empty actual.err
'
-test_expect_success 'env--helper reads config thanks to trace2' '
+test_expect_success 'test-tool env-helper reads config thanks to trace2' '
mkdir home &&
git config -f home/.gitconfig include.path cycle &&
git config -f home/cycle include.path .gitconfig &&
@@ -92,7 +93,7 @@ test_expect_success 'env--helper reads config thanks to trace2' '
test_must_fail \
env HOME="$(pwd)/home" GIT_TEST_ENV_HELPER=true \
- git -C cycle env--helper --type=bool --default=0 --exit-code GIT_TEST_ENV_HELPER 2>err &&
+ test-tool -C cycle env-helper --type=bool --default=0 --exit-code GIT_TEST_ENV_HELPER 2>err &&
grep "exceeded maximum include depth" err
'
diff --git a/t/t0018-advice.sh b/t/t0018-advice.sh
index 39e5e4b..0dcfb76 100755
--- a/t/t0018-advice.sh
+++ b/t/t0018-advice.sh
@@ -2,6 +2,7 @@
test_description='Test advise_if_enabled functionality'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'advice should be printed when config variable is unset' '
@@ -16,7 +17,6 @@ test_expect_success 'advice should be printed when config variable is unset' '
test_expect_success 'advice should be printed when config variable is set to true' '
cat >expect <<-\EOF &&
hint: This is a piece of advice
- hint: Disable this message with "git config advice.nestedTag false"
EOF
test_config advice.nestedTag true &&
test-tool advise "This is a piece of advice" 2>actual &&
diff --git a/t/t0019-json-writer.sh b/t/t0019-json-writer.sh
index 3b0c336..19a730c 100755
--- a/t/t0019-json-writer.sh
+++ b/t/t0019-json-writer.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test json-writer JSON generation'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'unit test of json-writer routines' '
diff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh
index f25ae8b..81946e8 100755
--- a/t/t0020-crlf.sh
+++ b/t/t0020-crlf.sh
@@ -5,6 +5,7 @@ test_description='CRLF conversion'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
has_cr() {
@@ -22,10 +23,10 @@ test_expect_success setup '
git config core.autocrlf false &&
- for w in Hello world how are you; do echo $w; done >one &&
+ test_write_lines Hello world how are you >one &&
mkdir dir &&
- for w in I am very very fine thank you; do echo $w; done >dir/two &&
- for w in Oh here is NULQin text here; do echo $w; done | q_to_nul >three &&
+ test_write_lines I am very very fine thank you >dir/two &&
+ test_write_lines Oh here is NULQin text here | q_to_nul >three &&
git add . &&
git commit -m initial &&
@@ -35,7 +36,7 @@ test_expect_success setup '
two=$(git rev-parse HEAD:dir/two) &&
three=$(git rev-parse HEAD:three) &&
- for w in Some extra lines here; do echo $w; done >>one &&
+ test_write_lines Some extra lines here >>one &&
git diff >patch.file &&
patched=$(git hash-object --stdin <one) &&
git read-tree --reset -u HEAD
@@ -46,7 +47,7 @@ test_expect_success 'safecrlf: autocrlf=input, all CRLF' '
git config core.autocrlf input &&
git config core.safecrlf true &&
- for w in I am all CRLF; do echo $w; done | append_cr >allcrlf &&
+ test_write_lines I am all CRLF | append_cr >allcrlf &&
test_must_fail git add allcrlf
'
@@ -55,7 +56,7 @@ test_expect_success 'safecrlf: autocrlf=input, mixed LF/CRLF' '
git config core.autocrlf input &&
git config core.safecrlf true &&
- for w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >mixed &&
+ test_write_lines Oh here is CRLFQ in text | q_to_cr >mixed &&
test_must_fail git add mixed
'
@@ -64,7 +65,7 @@ test_expect_success 'safecrlf: autocrlf=true, all LF' '
git config core.autocrlf true &&
git config core.safecrlf true &&
- for w in I am all LF; do echo $w; done >alllf &&
+ test_write_lines I am all LF >alllf &&
test_must_fail git add alllf
'
@@ -73,7 +74,7 @@ test_expect_success 'safecrlf: autocrlf=true mixed LF/CRLF' '
git config core.autocrlf true &&
git config core.safecrlf true &&
- for w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >mixed &&
+ test_write_lines Oh here is CRLFQ in text | q_to_cr >mixed &&
test_must_fail git add mixed
'
@@ -82,10 +83,10 @@ test_expect_success 'safecrlf: print warning only once' '
git config core.autocrlf input &&
git config core.safecrlf warn &&
- for w in I am all LF; do echo $w; done >doublewarn &&
+ test_write_lines I am all LF >doublewarn &&
git add doublewarn &&
git commit -m "nowarn" &&
- for w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >doublewarn &&
+ test_write_lines Oh here is CRLFQ in text | q_to_cr >doublewarn &&
git add doublewarn 2>err &&
grep "CRLF will be replaced by LF" err >err.warnings &&
test_line_count = 1 err.warnings
@@ -103,7 +104,7 @@ 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 &&
+ test_write_lines I am all CRLF | append_cr >allcrlf &&
git add allcrlf 2>err &&
test_must_be_empty err
'
@@ -124,7 +125,7 @@ test_expect_success 'update with autocrlf=input' '
munge_cr append dir/two &&
git update-index -- one dir/two &&
differs=$(git diff-index --cached HEAD) &&
- verbose test -z "$differs"
+ test -z "$differs"
'
@@ -137,7 +138,7 @@ test_expect_success 'update with autocrlf=true' '
munge_cr append dir/two &&
git update-index -- one dir/two &&
differs=$(git diff-index --cached HEAD) &&
- verbose test -z "$differs"
+ test -z "$differs"
'
@@ -152,7 +153,7 @@ test_expect_success 'checkout with autocrlf=true' '
test "$one" = $(git hash-object --stdin <one) &&
test "$two" = $(git hash-object --stdin <dir/two) &&
differs=$(git diff-index --cached HEAD) &&
- verbose test -z "$differs"
+ test -z "$differs"
'
test_expect_success 'checkout with autocrlf=input' '
@@ -166,7 +167,7 @@ test_expect_success 'checkout with autocrlf=input' '
test "$one" = $(git hash-object --stdin <one) &&
test "$two" = $(git hash-object --stdin <dir/two) &&
differs=$(git diff-index --cached HEAD) &&
- verbose test -z "$differs"
+ test -z "$differs"
'
test_expect_success 'apply patch (autocrlf=input)' '
@@ -176,7 +177,7 @@ test_expect_success 'apply patch (autocrlf=input)' '
git read-tree --reset -u HEAD &&
git apply patch.file &&
- verbose test "$patched" = "$(git hash-object --stdin <one)"
+ test "$patched" = "$(git hash-object --stdin <one)"
'
test_expect_success 'apply patch --cached (autocrlf=input)' '
@@ -186,7 +187,7 @@ test_expect_success 'apply patch --cached (autocrlf=input)' '
git read-tree --reset -u HEAD &&
git apply --cached patch.file &&
- verbose test "$patched" = $(git rev-parse :one)
+ test "$patched" = $(git rev-parse :one)
'
test_expect_success 'apply patch --index (autocrlf=input)' '
@@ -196,8 +197,8 @@ test_expect_success 'apply patch --index (autocrlf=input)' '
git read-tree --reset -u HEAD &&
git apply --index patch.file &&
- verbose test "$patched" = $(git rev-parse :one) &&
- verbose test "$patched" = $(git hash-object --stdin <one)
+ test "$patched" = $(git rev-parse :one) &&
+ test "$patched" = $(git hash-object --stdin <one)
'
test_expect_success 'apply patch (autocrlf=true)' '
@@ -207,7 +208,7 @@ test_expect_success 'apply patch (autocrlf=true)' '
git read-tree --reset -u HEAD &&
git apply patch.file &&
- verbose test "$patched" = "$(remove_cr <one | git hash-object --stdin)"
+ test "$patched" = "$(remove_cr <one | git hash-object --stdin)"
'
test_expect_success 'apply patch --cached (autocrlf=true)' '
@@ -217,7 +218,7 @@ test_expect_success 'apply patch --cached (autocrlf=true)' '
git read-tree --reset -u HEAD &&
git apply --cached patch.file &&
- verbose test "$patched" = $(git rev-parse :one)
+ test "$patched" = $(git rev-parse :one)
'
test_expect_success 'apply patch --index (autocrlf=true)' '
@@ -227,8 +228,8 @@ test_expect_success 'apply patch --index (autocrlf=true)' '
git read-tree --reset -u HEAD &&
git apply --index patch.file &&
- verbose test "$patched" = $(git rev-parse :one) &&
- verbose test "$patched" = "$(remove_cr <one | git hash-object --stdin)"
+ test "$patched" = $(git rev-parse :one) &&
+ test "$patched" = "$(remove_cr <one | git hash-object --stdin)"
'
test_expect_success '.gitattributes says two is binary' '
@@ -239,7 +240,7 @@ test_expect_success '.gitattributes says two is binary' '
git read-tree --reset -u HEAD &&
! has_cr dir/two &&
- verbose has_cr one &&
+ has_cr one &&
! has_cr three
'
@@ -258,8 +259,8 @@ test_expect_success '.gitattributes says two and three are text' '
echo "t* crlf" >.gitattributes &&
git read-tree --reset -u HEAD &&
- verbose has_cr dir/two &&
- verbose has_cr three
+ has_cr dir/two &&
+ has_cr three
'
test_expect_success 'in-tree .gitattributes (1)' '
@@ -272,7 +273,7 @@ test_expect_success 'in-tree .gitattributes (1)' '
git read-tree --reset -u HEAD &&
! has_cr one &&
- verbose has_cr three
+ has_cr three
'
test_expect_success 'in-tree .gitattributes (2)' '
@@ -282,7 +283,7 @@ test_expect_success 'in-tree .gitattributes (2)' '
git checkout-index -f -q -u -a &&
! has_cr one &&
- verbose has_cr three
+ has_cr three
'
test_expect_success 'in-tree .gitattributes (3)' '
@@ -293,7 +294,7 @@ test_expect_success 'in-tree .gitattributes (3)' '
git checkout-index -u one dir/two three &&
! has_cr one &&
- verbose has_cr three
+ has_cr three
'
test_expect_success 'in-tree .gitattributes (4)' '
@@ -304,7 +305,7 @@ test_expect_success 'in-tree .gitattributes (4)' '
git checkout-index -u .gitattributes &&
! has_cr one &&
- verbose has_cr three
+ has_cr three
'
test_expect_success 'checkout with existing .gitattributes' '
@@ -351,9 +352,9 @@ test_expect_success 'setting up for new autocrlf tests' '
git config core.autocrlf false &&
git config core.safecrlf false &&
rm -rf .????* * &&
- for w in I am all LF; do echo $w; done >alllf &&
- for w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >mixed &&
- for w in I am all CRLF; do echo $w; done | append_cr >allcrlf &&
+ test_write_lines I am all LF >alllf &&
+ test_write_lines Oh here is CRLFQ in text | q_to_cr >mixed &&
+ test_write_lines I am all CRLF | append_cr >allcrlf &&
git add -A . &&
git commit -m "alllf, allcrlf and mixed only" &&
git tag -a -m "message" autocrlf-checkpoint
diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
index 33dfc9c..0b49970 100755
--- a/t/t0021-conversion.sh
+++ b/t/t0021-conversion.sh
@@ -8,8 +8,8 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
-TEST_ROOT="$PWD"
-PATH=$TEST_ROOT:$PATH
+PATH=$PWD:$PATH
+TEST_ROOT="$(pwd)"
write_script <<\EOF "$TEST_ROOT/rot13.sh"
tr \
@@ -17,9 +17,6 @@ tr \
'nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'
EOF
-write_script rot13-filter.pl "$PERL_PATH" \
- <"$TEST_DIRECTORY"/t0021/rot13-filter.pl
-
generate_random_characters () {
LEN=$1
NAME=$2
@@ -76,13 +73,13 @@ test_expect_success setup '
git config filter.rot13.clean ./rot13.sh &&
{
- echo "*.t filter=rot13"
+ echo "*.t filter=rot13" &&
echo "*.i ident"
} >.gitattributes &&
{
- echo a b c d e f g h i j k l m
- echo n o p q r s t u v w x y z
+ echo a b c d e f g h i j k l m &&
+ echo n o p q r s t u v w x y z &&
echo '\''$Id$'\''
} >test &&
cat test >test.t &&
@@ -118,17 +115,17 @@ test_expect_success check '
# If an expanded ident ever gets into the repository, we want to make sure that
# it is collapsed before being expanded again on checkout
test_expect_success expanded_in_repo '
- {
- echo "File with expanded keywords"
- echo "\$Id\$"
- echo "\$Id:\$"
- echo "\$Id: 0000000000000000000000000000000000000000 \$"
- echo "\$Id: NoSpaceAtEnd\$"
- echo "\$Id:NoSpaceAtFront \$"
- echo "\$Id:NoSpaceAtEitherEnd\$"
- echo "\$Id: NoTerminatingSymbol"
- echo "\$Id: Foreign Commit With Spaces \$"
- } >expanded-keywords.0 &&
+ cat >expanded-keywords.0 <<-\EOF &&
+ File with expanded keywords
+ $Id$
+ $Id:$
+ $Id: 0000000000000000000000000000000000000000 $
+ $Id: NoSpaceAtEnd$
+ $Id:NoSpaceAtFront $
+ $Id:NoSpaceAtEitherEnd$
+ $Id: NoTerminatingSymbol
+ $Id: Foreign Commit With Spaces $
+ EOF
{
cat expanded-keywords.0 &&
@@ -139,17 +136,17 @@ test_expect_success expanded_in_repo '
git commit -m "File with keywords expanded" &&
id=$(git rev-parse --verify :expanded-keywords) &&
- {
- echo "File with expanded keywords"
- echo "\$Id: $id \$"
- echo "\$Id: $id \$"
- echo "\$Id: $id \$"
- echo "\$Id: $id \$"
- echo "\$Id: $id \$"
- echo "\$Id: $id \$"
- echo "\$Id: NoTerminatingSymbol"
- echo "\$Id: Foreign Commit With Spaces \$"
- } >expected-output.0 &&
+ cat >expected-output.0 <<-EOF &&
+ File with expanded keywords
+ \$Id: $id \$
+ \$Id: $id \$
+ \$Id: $id \$
+ \$Id: $id \$
+ \$Id: $id \$
+ \$Id: $id \$
+ \$Id: NoTerminatingSymbol
+ \$Id: Foreign Commit With Spaces \$
+ EOF
{
cat expected-output.0 &&
printf "\$Id: NoTerminatingSymbolAtEOF"
@@ -159,7 +156,7 @@ test_expect_success expanded_in_repo '
printf "\$Id: NoTerminatingSymbolAtEOF"
} >expected-output-crlf &&
{
- echo "expanded-keywords ident"
+ echo "expanded-keywords ident" &&
echo "expanded-keywords-crlf ident text eol=crlf"
} >>.gitattributes &&
@@ -266,7 +263,7 @@ test_expect_success 'required filter with absent clean field' '
echo test >test.ac &&
test_must_fail git add test.ac 2>stderr &&
- test_i18ngrep "fatal: test.ac: clean filter .absentclean. failed" stderr
+ test_grep "fatal: test.ac: clean filter .absentclean. failed" stderr
'
test_expect_success 'required filter with absent smudge field' '
@@ -279,13 +276,13 @@ test_expect_success 'required filter with absent smudge field' '
git add test.as &&
rm -f test.as &&
test_must_fail git checkout -- test.as 2>stderr &&
- test_i18ngrep "fatal: test.as: smudge filter absentsmudge failed" stderr
+ test_grep "fatal: test.as: smudge filter absentsmudge failed" stderr
'
test_expect_success 'filtering large input to small output should use little memory' '
test_config filter.devnull.clean "cat >/dev/null" &&
test_config filter.devnull.required true &&
- for i in $(test_seq 1 30); do printf "%1048576d" 1; done >30MB &&
+ for i in $(test_seq 1 30); do printf "%1048576d" 1 || return 1; done >30MB &&
echo "30MB filter=devnull" >.gitattributes &&
GIT_MMAP_LIMIT=1m GIT_ALLOC_LIMIT=1m git add 30MB
'
@@ -303,7 +300,7 @@ test_expect_success 'filter that does not read is fine' '
test_expect_success EXPENSIVE 'filter large file' '
test_config filter.largefile.smudge cat &&
test_config filter.largefile.clean cat &&
- for i in $(test_seq 1 2048); do printf "%1048576d" 1; done >2GB &&
+ for i in $(test_seq 1 2048); do printf "%1048576d" 1 || return 1; done >2GB &&
echo "2GB filter=largefile" >.gitattributes &&
git add 2GB 2>err &&
test_must_be_empty err &&
@@ -365,8 +362,8 @@ test_expect_success 'diff does not reuse worktree files that need cleaning' '
test_line_count = 0 count
'
-test_expect_success PERL 'required process filter should filter data' '
- test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
+test_expect_success 'required process filter should filter data' '
+ test_config_global filter.protocol.process "test-tool rot13-filter --log=debug.log clean smudge" &&
test_config_global filter.protocol.required true &&
rm -rf repo &&
mkdir repo &&
@@ -450,8 +447,8 @@ test_expect_success PERL 'required process filter should filter data' '
)
'
-test_expect_success PERL 'required process filter should filter data for various subcommands' '
- test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
+test_expect_success 'required process filter should filter data for various subcommands' '
+ test_config_global filter.protocol.process "test-tool rot13-filter --log=debug.log clean smudge" &&
test_config_global filter.protocol.required true &&
(
cd repo &&
@@ -561,9 +558,9 @@ test_expect_success PERL 'required process filter should filter data for various
)
'
-test_expect_success PERL 'required process filter takes precedence' '
+test_expect_success 'required process filter takes precedence' '
test_config_global filter.protocol.clean false &&
- test_config_global filter.protocol.process "rot13-filter.pl debug.log clean" &&
+ test_config_global filter.protocol.process "test-tool rot13-filter --log=debug.log clean" &&
test_config_global filter.protocol.required true &&
rm -rf repo &&
mkdir repo &&
@@ -587,8 +584,8 @@ test_expect_success PERL 'required process filter takes precedence' '
)
'
-test_expect_success PERL 'required process filter should be used only for "clean" operation only' '
- test_config_global filter.protocol.process "rot13-filter.pl debug.log clean" &&
+test_expect_success 'required process filter should be used only for "clean" operation only' '
+ test_config_global filter.protocol.process "test-tool rot13-filter --log=debug.log clean" &&
rm -rf repo &&
mkdir repo &&
(
@@ -622,8 +619,8 @@ test_expect_success PERL 'required process filter should be used only for "clean
)
'
-test_expect_success PERL 'required process filter should process multiple packets' '
- test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
+test_expect_success 'required process filter should process multiple packets' '
+ test_config_global filter.protocol.process "test-tool rot13-filter --log=debug.log clean smudge" &&
test_config_global filter.protocol.required true &&
rm -rf repo &&
@@ -643,7 +640,7 @@ test_expect_success PERL 'required process filter should process multiple packet
for FILE in "$TEST_ROOT"/*.file
do
cp "$FILE" . &&
- rot13.sh <"$FILE" >"$FILE.rot13"
+ rot13.sh <"$FILE" >"$FILE.rot13" || return 1
done &&
echo "*.file filter=protocol" >.gitattributes &&
@@ -682,13 +679,13 @@ test_expect_success PERL 'required process filter should process multiple packet
for FILE in *.file
do
- test_cmp_committed_rot13 "$TEST_ROOT/$FILE" $FILE
+ test_cmp_committed_rot13 "$TEST_ROOT/$FILE" $FILE || return 1
done
)
'
-test_expect_success PERL 'required process filter with clean error should fail' '
- test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
+test_expect_success 'required process filter with clean error should fail' '
+ test_config_global filter.protocol.process "test-tool rot13-filter --log=debug.log clean smudge" &&
test_config_global filter.protocol.required true &&
rm -rf repo &&
mkdir repo &&
@@ -706,8 +703,8 @@ test_expect_success PERL 'required process filter with clean error should fail'
)
'
-test_expect_success PERL 'process filter should restart after unexpected write failure' '
- test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
+test_expect_success 'process filter should restart after unexpected write failure' '
+ test_config_global filter.protocol.process "test-tool rot13-filter --log=debug.log clean smudge" &&
rm -rf repo &&
mkdir repo &&
(
@@ -735,8 +732,8 @@ test_expect_success PERL 'process filter should restart after unexpected write f
rm -f debug.log &&
git checkout --quiet --no-progress . 2>git-stderr.log &&
- grep "smudge write error at" git-stderr.log &&
- test_i18ngrep "error: external filter" git-stderr.log &&
+ grep "smudge write error" git-stderr.log &&
+ test_grep "error: external filter" git-stderr.log &&
cat >expected.log <<-EOF &&
START
@@ -761,8 +758,8 @@ test_expect_success PERL 'process filter should restart after unexpected write f
)
'
-test_expect_success PERL 'process filter should not be restarted if it signals an error' '
- test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
+test_expect_success 'process filter should not be restarted if it signals an error' '
+ test_config_global filter.protocol.process "test-tool rot13-filter --log=debug.log clean smudge" &&
rm -rf repo &&
mkdir repo &&
(
@@ -804,8 +801,8 @@ test_expect_success PERL 'process filter should not be restarted if it signals a
)
'
-test_expect_success PERL 'process filter abort stops processing of all further files' '
- test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
+test_expect_success 'process filter abort stops processing of all further files' '
+ test_config_global filter.protocol.process "test-tool rot13-filter --log=debug.log clean smudge" &&
rm -rf repo &&
mkdir repo &&
(
@@ -861,10 +858,10 @@ test_expect_success PERL 'invalid process filter must fail (and not hang!)' '
)
'
-test_expect_success PERL 'delayed checkout in process filter' '
- test_config_global filter.a.process "rot13-filter.pl a.log clean smudge delay" &&
+test_expect_success 'delayed checkout in process filter' '
+ test_config_global filter.a.process "test-tool rot13-filter --log=a.log clean smudge delay" &&
test_config_global filter.a.required true &&
- test_config_global filter.b.process "rot13-filter.pl b.log clean smudge delay" &&
+ test_config_global filter.b.process "test-tool rot13-filter --log=b.log clean smudge delay" &&
test_config_global filter.b.required true &&
rm -rf repo &&
@@ -940,8 +937,8 @@ test_expect_success PERL 'delayed checkout in process filter' '
)
'
-test_expect_success PERL 'missing file in delayed checkout' '
- test_config_global filter.bug.process "rot13-filter.pl bug.log clean smudge delay" &&
+test_expect_success 'missing file in delayed checkout' '
+ test_config_global filter.bug.process "test-tool rot13-filter --log=bug.log clean smudge delay" &&
test_config_global filter.bug.required true &&
rm -rf repo &&
@@ -960,8 +957,8 @@ test_expect_success PERL 'missing file in delayed checkout' '
grep "error: .missing-delay\.a. was not filtered properly" git-stderr.log
'
-test_expect_success PERL 'invalid file in delayed checkout' '
- test_config_global filter.bug.process "rot13-filter.pl bug.log clean smudge delay" &&
+test_expect_success 'invalid file in delayed checkout' '
+ test_config_global filter.bug.process "test-tool rot13-filter --log=bug.log clean smudge delay" &&
test_config_global filter.bug.required true &&
rm -rf repo &&
@@ -990,10 +987,10 @@ do
mode_prereq='UTF8_NFD_TO_NFC' ;;
esac
- test_expect_success PERL,SYMLINKS,$mode_prereq \
+ test_expect_success SYMLINKS,$mode_prereq \
"delayed checkout with $mode-collision don't write to the wrong place" '
test_config_global filter.delay.process \
- "\"$TEST_ROOT/rot13-filter.pl\" --always-delay delayed.log clean smudge delay" &&
+ "test-tool rot13-filter --always-delay --log=delayed.log clean smudge delay" &&
test_config_global filter.delay.required true &&
git init $mode-collision &&
@@ -1026,12 +1023,12 @@ do
'
done
-test_expect_success PERL,SYMLINKS,CASE_INSENSITIVE_FS \
+test_expect_success SYMLINKS,CASE_INSENSITIVE_FS \
"delayed checkout with submodule collision don't write to the wrong place" '
git init collision-with-submodule &&
(
cd collision-with-submodule &&
- git config filter.delay.process "\"$TEST_ROOT/rot13-filter.pl\" --always-delay delayed.log clean smudge delay" &&
+ git config filter.delay.process "test-tool rot13-filter --always-delay --log=delayed.log clean smudge delay" &&
git config filter.delay.required true &&
# We need Git to treat the submodule "a" and the
@@ -1062,11 +1059,11 @@ test_expect_success PERL,SYMLINKS,CASE_INSENSITIVE_FS \
)
'
-test_expect_success PERL 'setup for progress tests' '
+test_expect_success 'setup for progress tests' '
git init progress &&
(
cd progress &&
- git config filter.delay.process "rot13-filter.pl delay-progress.log clean smudge delay" &&
+ git config filter.delay.process "test-tool rot13-filter --log=delay-progress.log clean smudge delay" &&
git config filter.delay.required true &&
echo "*.a filter=delay" >.gitattributes &&
@@ -1132,4 +1129,26 @@ do
'
done
+test_expect_success 'delayed checkout correctly reports the number of updated entries' '
+ rm -rf repo &&
+ git init repo &&
+ (
+ cd repo &&
+ git config filter.delay.process "test-tool rot13-filter --log=delayed.log clean smudge delay" &&
+ git config filter.delay.required true &&
+
+ echo "*.a filter=delay" >.gitattributes &&
+ echo a >test-delay10.a &&
+ echo a >test-delay11.a &&
+ git add . &&
+ git commit -m files &&
+
+ rm *.a &&
+ git checkout . 2>err &&
+ grep "IN: smudge test-delay10.a .* \\[DELAYED\\]" delayed.log &&
+ grep "IN: smudge test-delay11.a .* \\[DELAYED\\]" delayed.log &&
+ grep "Updated 2 paths from the index" err
+ )
+'
+
test_done
diff --git a/t/t0021/rot13-filter.pl b/t/t0021/rot13-filter.pl
deleted file mode 100644
index 7bb9376..0000000
--- a/t/t0021/rot13-filter.pl
+++ /dev/null
@@ -1,247 +0,0 @@
-#
-# Example implementation for the Git filter protocol version 2
-# See Documentation/gitattributes.txt, section "Filter Protocol"
-#
-# Usage: rot13-filter.pl [--always-delay] <log path> <capabilities>
-#
-# Log path defines a debug log file that the script writes to. The
-# subsequent arguments define a list of supported protocol capabilities
-# ("clean", "smudge", etc).
-#
-# When --always-delay is given all pathnames with the "can-delay" flag
-# that don't appear on the list bellow are delayed with a count of 1
-# (see more below).
-#
-# This implementation supports special test cases:
-# (1) If data with the pathname "clean-write-fail.r" is processed with
-# a "clean" operation then the write operation will die.
-# (2) If data with the pathname "smudge-write-fail.r" is processed with
-# a "smudge" operation then the write operation will die.
-# (3) If data with the pathname "error.r" is processed with any
-# operation then the filter signals that it cannot or does not want
-# to process the file.
-# (4) If data with the pathname "abort.r" is processed with any
-# operation then the filter signals that it cannot or does not want
-# to process the file and any file after that is processed with the
-# same command.
-# (5) If data with a pathname that is a key in the DELAY hash is
-# requested (e.g. "test-delay10.a") then the filter responds with
-# a "delay" status and sets the "requested" field in the DELAY hash.
-# The filter will signal the availability of this object after
-# "count" (field in DELAY hash) "list_available_blobs" commands.
-# (6) If data with the pathname "missing-delay.a" is processed that the
-# filter will drop the path from the "list_available_blobs" response.
-# (7) If data with the pathname "invalid-delay.a" is processed that the
-# filter will add the path "unfiltered" which was not delayed before
-# to the "list_available_blobs" response.
-#
-
-use 5.008;
-sub gitperllib {
- # Git assumes that all path lists are Unix-y colon-separated ones. But
- # when the Git for Windows executes the test suite, its MSYS2 Bash
- # calls git.exe, and colon-separated path lists are converted into
- # Windows-y semicolon-separated lists of *Windows* paths (which
- # naturally contain a colon after the drive letter, so splitting by
- # colons simply does not cut it).
- #
- # Detect semicolon-separated path list and handle them appropriately.
-
- if ($ENV{GITPERLLIB} =~ /;/) {
- return split(/;/, $ENV{GITPERLLIB});
- }
- return split(/:/, $ENV{GITPERLLIB});
-}
-use lib (gitperllib());
-use strict;
-use warnings;
-use IO::File;
-use Git::Packet;
-
-my $MAX_PACKET_CONTENT_SIZE = 65516;
-
-my $always_delay = 0;
-if ( $ARGV[0] eq '--always-delay' ) {
- $always_delay = 1;
- shift @ARGV;
-}
-
-my $log_file = shift @ARGV;
-my @capabilities = @ARGV;
-
-open my $debug, ">>", $log_file or die "cannot open log file: $!";
-
-my %DELAY = (
- 'test-delay10.a' => { "requested" => 0, "count" => 1 },
- 'test-delay11.a' => { "requested" => 0, "count" => 1 },
- 'test-delay20.a' => { "requested" => 0, "count" => 2 },
- 'test-delay10.b' => { "requested" => 0, "count" => 1 },
- 'missing-delay.a' => { "requested" => 0, "count" => 1 },
- 'invalid-delay.a' => { "requested" => 0, "count" => 1 },
-);
-
-sub rot13 {
- my $str = shift;
- $str =~ y/A-Za-z/N-ZA-Mn-za-m/;
- return $str;
-}
-
-print $debug "START\n";
-$debug->flush();
-
-packet_initialize("git-filter", 2);
-
-my %remote_caps = packet_read_and_check_capabilities("clean", "smudge", "delay");
-packet_check_and_write_capabilities(\%remote_caps, @capabilities);
-
-print $debug "init handshake complete\n";
-$debug->flush();
-
-while (1) {
- my ( $res, $command ) = packet_key_val_read("command");
- if ( $res == -1 ) {
- print $debug "STOP\n";
- exit();
- }
- print $debug "IN: $command";
- $debug->flush();
-
- if ( $command eq "list_available_blobs" ) {
- # Flush
- packet_compare_lists([1, ""], packet_bin_read()) ||
- die "bad list_available_blobs end";
-
- foreach my $pathname ( sort keys %DELAY ) {
- if ( $DELAY{$pathname}{"requested"} >= 1 ) {
- $DELAY{$pathname}{"count"} = $DELAY{$pathname}{"count"} - 1;
- if ( $pathname eq "invalid-delay.a" ) {
- # Send Git a pathname that was not delayed earlier
- packet_txt_write("pathname=unfiltered");
- }
- if ( $pathname eq "missing-delay.a" ) {
- # Do not signal Git that this file is available
- } elsif ( $DELAY{$pathname}{"count"} == 0 ) {
- print $debug " $pathname";
- packet_txt_write("pathname=$pathname");
- }
- }
- }
-
- packet_flush();
-
- print $debug " [OK]\n";
- $debug->flush();
- packet_txt_write("status=success");
- packet_flush();
- } else {
- my ( $res, $pathname ) = packet_key_val_read("pathname");
- if ( $res == -1 ) {
- die "unexpected EOF while expecting pathname";
- }
- print $debug " $pathname";
- $debug->flush();
-
- # Read until flush
- my ( $done, $buffer ) = packet_txt_read();
- while ( $buffer ne '' ) {
- if ( $buffer eq "can-delay=1" ) {
- if ( exists $DELAY{$pathname} and $DELAY{$pathname}{"requested"} == 0 ) {
- $DELAY{$pathname}{"requested"} = 1;
- } elsif ( !exists $DELAY{$pathname} and $always_delay ) {
- $DELAY{$pathname} = { "requested" => 1, "count" => 1 };
- }
- } elsif ($buffer =~ /^(ref|treeish|blob)=/) {
- print $debug " $buffer";
- } else {
- # In general, filters need to be graceful about
- # new metadata, since it's documented that we
- # can pass any key-value pairs, but for tests,
- # let's be a little stricter.
- die "Unknown message '$buffer'";
- }
-
- ( $done, $buffer ) = packet_txt_read();
- }
- if ( $done == -1 ) {
- die "unexpected EOF after pathname '$pathname'";
- }
-
- my $input = "";
- {
- binmode(STDIN);
- my $buffer;
- my $done = 0;
- while ( !$done ) {
- ( $done, $buffer ) = packet_bin_read();
- $input .= $buffer;
- }
- if ( $done == -1 ) {
- die "unexpected EOF while reading input for '$pathname'";
- }
- print $debug " " . length($input) . " [OK] -- ";
- $debug->flush();
- }
-
- my $output;
- if ( exists $DELAY{$pathname} and exists $DELAY{$pathname}{"output"} ) {
- $output = $DELAY{$pathname}{"output"}
- } elsif ( $pathname eq "error.r" or $pathname eq "abort.r" ) {
- $output = "";
- } elsif ( $command eq "clean" and grep( /^clean$/, @capabilities ) ) {
- $output = rot13($input);
- } elsif ( $command eq "smudge" and grep( /^smudge$/, @capabilities ) ) {
- $output = rot13($input);
- } else {
- die "bad command '$command'";
- }
-
- if ( $pathname eq "error.r" ) {
- print $debug "[ERROR]\n";
- $debug->flush();
- packet_txt_write("status=error");
- packet_flush();
- } elsif ( $pathname eq "abort.r" ) {
- print $debug "[ABORT]\n";
- $debug->flush();
- packet_txt_write("status=abort");
- packet_flush();
- } elsif ( $command eq "smudge" and
- exists $DELAY{$pathname} and
- $DELAY{$pathname}{"requested"} == 1 ) {
- print $debug "[DELAYED]\n";
- $debug->flush();
- packet_txt_write("status=delayed");
- packet_flush();
- $DELAY{$pathname}{"requested"} = 2;
- $DELAY{$pathname}{"output"} = $output;
- } else {
- packet_txt_write("status=success");
- packet_flush();
-
- if ( $pathname eq "${command}-write-fail.r" ) {
- print $debug "[WRITE FAIL]\n";
- $debug->flush();
- die "${command} write error";
- }
-
- print $debug "OUT: " . length($output) . " ";
- $debug->flush();
-
- while ( length($output) > 0 ) {
- my $packet = substr( $output, 0, $MAX_PACKET_CONTENT_SIZE );
- packet_bin_write($packet);
- # dots represent the number of packets
- print $debug ".";
- if ( length($output) > $MAX_PACKET_CONTENT_SIZE ) {
- $output = substr( $output, $MAX_PACKET_CONTENT_SIZE );
- } else {
- $output = "";
- }
- }
- packet_flush();
- print $debug " [OK]\n";
- $debug->flush();
- packet_flush();
- }
- }
-}
diff --git a/t/t0022-crlf-rename.sh b/t/t0022-crlf-rename.sh
index 7af3fbc..9fe9891 100755
--- a/t/t0022-crlf-rename.sh
+++ b/t/t0022-crlf-rename.sh
@@ -2,6 +2,7 @@
test_description='ignore CR in CRLF sequence while computing similiarity'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -23,8 +24,8 @@ test_expect_success setup '
test_expect_success 'diff -M' '
- git diff-tree -M -r --name-status HEAD^ HEAD |
- sed -e "s/R[0-9]*/RNUM/" >actual &&
+ git diff-tree -M -r --name-status HEAD^ HEAD >tmp &&
+ sed -e "s/R[0-9]*/RNUM/" tmp >actual &&
echo "RNUM sample elpmas" >expect &&
test_cmp expect actual
diff --git a/t/t0023-crlf-am.sh b/t/t0023-crlf-am.sh
index f9bbb91..5758055 100755
--- a/t/t0023-crlf-am.sh
+++ b/t/t0023-crlf-am.sh
@@ -2,6 +2,7 @@
test_description='Test am with auto.crlf'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
cat >patchfile <<\EOF
diff --git a/t/t0024-crlf-archive.sh b/t/t0024-crlf-archive.sh
index 4e9fa3c..a7f4de4 100755
--- a/t/t0024-crlf-archive.sh
+++ b/t/t0024-crlf-archive.sh
@@ -2,13 +2,14 @@
test_description='respect crlf in git archive'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
git config core.autocrlf true &&
- printf "CRLF line ending\r\nAnd another\r\n" > sample &&
+ printf "CRLF line ending\r\nAnd another\r\n" >sample &&
git add sample &&
test_tick &&
@@ -18,8 +19,9 @@ test_expect_success setup '
test_expect_success 'tar archive' '
- git archive --format=tar HEAD |
- ( mkdir untarred && cd untarred && "$TAR" -xf - ) &&
+ git archive --format=tar HEAD >test.tar &&
+ mkdir untarred &&
+ "$TAR" xf test.tar -C untarred &&
test_cmp sample untarred/sample
@@ -29,7 +31,11 @@ test_expect_success UNZIP 'zip archive' '
git archive --format=zip HEAD >test.zip &&
- ( mkdir unzipped && cd unzipped && "$GIT_UNZIP" ../test.zip ) &&
+ mkdir unzipped &&
+ (
+ cd unzipped &&
+ "$GIT_UNZIP" ../test.zip
+ ) &&
test_cmp sample unzipped/sample
diff --git a/t/t0025-crlf-renormalize.sh b/t/t0025-crlf-renormalize.sh
index e13363a..f7202c1 100755
--- a/t/t0025-crlf-renormalize.sh
+++ b/t/t0025-crlf-renormalize.sh
@@ -2,6 +2,7 @@
test_description='CRLF renormalization'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -21,8 +22,8 @@ test_expect_success 'renormalize CRLF in repo' '
i/lf w/lf attr/text=auto LF.txt
i/lf w/mixed attr/text=auto CRLF_mix_LF.txt
EOF
- git ls-files --eol |
- sed -e "s/ / /g" -e "s/ */ /g" |
+ git ls-files --eol >tmp &&
+ sed -e "s/ / /g" -e "s/ */ /g" tmp |
sort >actual &&
test_cmp expect actual
'
diff --git a/t/t0026-eol-config.sh b/t/t0026-eol-config.sh
index c5203e2..f426a18 100755
--- a/t/t0026-eol-config.sh
+++ b/t/t0026-eol-config.sh
@@ -2,6 +2,7 @@
test_description='CRLF conversion'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
has_cr() {
@@ -14,8 +15,8 @@ test_expect_success setup '
echo "one text" > .gitattributes &&
- for w in Hello world how are you; do echo $w; done >one &&
- for w in I am very very fine thank you; do echo $w; done >two &&
+ test_write_lines Hello world how are you >one &&
+ test_write_lines I am very very fine thank you >two &&
git add . &&
git commit -m initial &&
diff --git a/t/t0027-auto-crlf.sh b/t/t0027-auto-crlf.sh
index 4a5c5c6..2f57c86 100755
--- a/t/t0027-auto-crlf.sh
+++ b/t/t0027-auto-crlf.sh
@@ -2,6 +2,7 @@
test_description='CRLF conversion all combinations'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
compare_files () {
@@ -69,7 +70,8 @@ create_NNO_MIX_files () {
cp CRLF ${pfx}_CRLF.txt &&
cp CRLF_mix_LF ${pfx}_CRLF_mix_LF.txt &&
cp LF_mix_CR ${pfx}_LF_mix_CR.txt &&
- cp CRLF_nul ${pfx}_CRLF_nul.txt
+ cp CRLF_nul ${pfx}_CRLF_nul.txt ||
+ return 1
done
done
done
@@ -77,12 +79,12 @@ create_NNO_MIX_files () {
check_warning () {
case "$1" in
- LF_CRLF) echo "warning: LF will be replaced by CRLF" >"$2".expect ;;
- CRLF_LF) echo "warning: CRLF will be replaced by LF" >"$2".expect ;;
- '') >"$2".expect ;;
+ LF_CRLF) echo "LF will be replaced by CRLF" >"$2".expect ;;
+ CRLF_LF) echo "CRLF will be replaced by LF" >"$2".expect ;;
+ '') >"$2".expect ;;
*) echo >&2 "Illegal 1": "$1" ; return false ;;
esac
- grep "will be replaced by" "$2" | sed -e "s/\(.*\) in [^ ]*$/\1/" | uniq >"$2".actual
+ sed -e "s/^.* \([^ ]* will be replaced by [^ ]*\) .*$/\1/" "$2" | uniq >"$2".actual
test_cmp "$2".expect "$2".actual
}
@@ -100,7 +102,8 @@ commit_check_warn () {
do
fname=${pfx}_$f.txt &&
cp $f $fname &&
- git -c core.autocrlf=$crlf add $fname 2>"${pfx}_$f.err"
+ git -c core.autocrlf=$crlf add $fname 2>"${pfx}_$f.err" ||
+ return 1
done &&
git commit -m "core.autocrlf $crlf" &&
check_warning "$lfname" ${pfx}_LF.err &&
@@ -120,15 +123,19 @@ commit_chk_wrnNNO () {
lfmixcr=$1 ; shift
crlfnul=$1 ; shift
pfx=NNO_attr_${attr}_aeol_${aeol}_${crlf}
- #Commit files on top of existing file
- create_gitattributes "$attr" $aeol &&
- for f in LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
- do
- fname=${pfx}_$f.txt &&
- cp $f $fname &&
- printf Z >>"$fname" &&
- git -c core.autocrlf=$crlf add $fname 2>"${pfx}_$f.err"
- done
+
+ test_expect_success 'setup commit NNO files' '
+ #Commit files on top of existing file
+ create_gitattributes "$attr" $aeol &&
+ for f in LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+ do
+ fname=${pfx}_$f.txt &&
+ cp $f $fname &&
+ printf Z >>"$fname" &&
+ git -c core.autocrlf=$crlf add $fname 2>"${pfx}_$f.err" ||
+ return 1
+ done
+ '
test_expect_success "commit NNO files crlf=$crlf attr=$attr LF" '
check_warning "$lfwarn" ${pfx}_LF.err
@@ -162,15 +169,19 @@ commit_MIX_chkwrn () {
lfmixcr=$1 ; shift
crlfnul=$1 ; shift
pfx=MIX_attr_${attr}_aeol_${aeol}_${crlf}
- #Commit file with CLRF_mix_LF on top of existing file
- create_gitattributes "$attr" $aeol &&
- for f in LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
- do
- fname=${pfx}_$f.txt &&
- cp CRLF_mix_LF $fname &&
- printf Z >>"$fname" &&
- git -c core.autocrlf=$crlf add $fname 2>"${pfx}_$f.err"
- done
+
+ test_expect_success 'setup commit file with mixed EOL' '
+ #Commit file with CLRF_mix_LF on top of existing file
+ create_gitattributes "$attr" $aeol &&
+ for f in LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+ do
+ fname=${pfx}_$f.txt &&
+ cp CRLF_mix_LF $fname &&
+ printf Z >>"$fname" &&
+ git -c core.autocrlf=$crlf add $fname 2>"${pfx}_$f.err" ||
+ return 1
+ done
+ '
test_expect_success "commit file with mixed EOL onto LF crlf=$crlf attr=$attr" '
check_warning "$lfwarn" ${pfx}_LF.err
@@ -288,17 +299,17 @@ checkout_files () {
lfmixcrlf=$1 ; shift
lfmixcr=$1 ; shift
crlfnul=$1 ; shift
- create_gitattributes "$attr" $ident $aeol &&
- git config core.autocrlf $crlf &&
+ test_expect_success "setup config for checkout attr=$attr ident=$ident aeol=$aeol core.autocrlf=$crlf" '
+ create_gitattributes "$attr" $ident $aeol &&
+ git config core.autocrlf $crlf
+ '
pfx=eol_${ceol}_crlf_${crlf}_attr_${attr}_ &&
for f in LF CRLF LF_mix_CR CRLF_mix_LF LF_nul
do
- rm crlf_false_attr__$f.txt &&
- if test -z "$ceol"; then
- git checkout -- crlf_false_attr__$f.txt
- else
- git -c core.eol=$ceol checkout -- crlf_false_attr__$f.txt
- fi
+ test_expect_success "setup $f checkout ${ceol:+ with -c core.eol=$ceol}" '
+ rm -f crlf_false_attr__$f.txt &&
+ git ${ceol:+-c core.eol=$ceol} checkout -- crlf_false_attr__$f.txt
+ '
done
test_expect_success "ls-files --eol attr=$attr $ident aeol=$aeol core.autocrlf=$crlf core.eol=$ceol" '
@@ -311,8 +322,8 @@ checkout_files () {
i/-text w/$(stats_ascii $crlfnul) attr/$(attr_ascii $attr $aeol) crlf_false_attr__CRLF_nul.txt
i/-text w/$(stats_ascii $crlfnul) attr/$(attr_ascii $attr $aeol) crlf_false_attr__LF_nul.txt
EOF
- git ls-files --eol crlf_false_attr__* |
- sed -e "s/ / /g" -e "s/ */ /g" |
+ git ls-files --eol crlf_false_attr__* >tmp &&
+ sed -e "s/ / /g" -e "s/ */ /g" tmp |
sort >actual &&
test_cmp expect actual
'
@@ -359,12 +370,12 @@ test_expect_success 'ls-files --eol -o Text/Binary' '
i/ w/crlf TeBi_126_CL
i/ w/-text TeBi_126_CLC
EOF
- git ls-files --eol -o |
+ git ls-files --eol -o >tmp &&
sed -n -e "/TeBi_/{s!attr/[ ]*!!g
s! ! !g
s! *! !g
p
- }" | sort >actual &&
+ }" tmp | sort >actual &&
test_cmp expect actual
'
@@ -386,9 +397,7 @@ test_expect_success 'setup main' '
test_tick
'
-# Disable extra chain-linting for the next set of tests. There are many
-# auto-generated ones that are not worth checking over and over.
-GIT_TEST_CHAIN_LINT_HARDER_DEFAULT=0
+
warn_LF_CRLF="LF will be replaced by CRLF"
warn_CRLF_LF="CRLF will be replaced by LF"
@@ -597,11 +606,14 @@ do
# auto: core.autocrlf=false and core.eol unset(or native) uses native eol
checkout_files auto "$id" "" false "" $NL CRLF CRLF_mix_LF LF_mix_CR LF_nul
checkout_files auto "$id" "" false native $NL CRLF CRLF_mix_LF LF_mix_CR LF_nul
+ # core.autocrlf false, .gitattributes sets eol
+ checkout_files "" "$id" "lf" false "" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
+ checkout_files "" "$id" "crlf" false "" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+ # core.autocrlf true, .gitattributes sets eol
+ checkout_files "" "$id" "lf" true "" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
+ checkout_files "" "$id" "crlf" true "" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
done
-# The rest of the tests are unique; do the usual linting.
-unset GIT_TEST_CHAIN_LINT_HARDER_DEFAULT
-
# Should be the last test case: remove some files from the worktree
test_expect_success 'ls-files --eol -d -z' '
rm crlf_false_attr__CRLF.txt crlf_false_attr__CRLF_mix_LF.txt crlf_false_attr__LF.txt .gitattributes &&
@@ -611,8 +623,8 @@ test_expect_success 'ls-files --eol -d -z' '
i/lf w/ crlf_false_attr__LF.txt
i/mixed w/ crlf_false_attr__CRLF_mix_LF.txt
EOF
- git ls-files --eol -d |
- sed -e "s!attr/[^ ]*!!g" -e "s/ / /g" -e "s/ */ /g" |
+ git ls-files --eol -d >tmp &&
+ sed -e "s!attr/[^ ]*!!g" -e "s/ / /g" -e "s/ */ /g" tmp |
sort >actual &&
test_cmp expect actual
'
diff --git a/t/t0028-working-tree-encoding.sh b/t/t0028-working-tree-encoding.sh
index 82905a2..ad151a3 100755
--- a/t/t0028-working-tree-encoding.sh
+++ b/t/t0028-working-tree-encoding.sh
@@ -5,6 +5,8 @@ test_description='working-tree-encoding conversion via gitattributes'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
+TEST_CREATE_REPO_NO_TEMPLATE=1
. ./test-lib.sh
. "$TEST_DIRECTORY/lib-encoding.sh"
@@ -69,6 +71,7 @@ test_expect_success 'check $GIT_DIR/info/attributes support' '
test_when_finished "rm -f test.utf32.git" &&
test_when_finished "git reset --hard HEAD" &&
+ mkdir .git/info &&
echo "*.utf32 text working-tree-encoding=utf-32" >.git/info/attributes &&
git add test.utf32 &&
@@ -89,23 +92,23 @@ do
# In these cases the BOM is prohibited.
cp bebom.utf${i}be.raw bebom.utf${i}be &&
test_must_fail git add bebom.utf${i}be 2>err.out &&
- test_i18ngrep "fatal: BOM is prohibited .* utf-${i}be" err.out &&
- test_i18ngrep "use UTF-${i} as working-tree-encoding" err.out &&
+ test_grep "fatal: BOM is prohibited .* utf-${i}be" err.out &&
+ test_grep "use UTF-${i} as working-tree-encoding" err.out &&
cp lebom.utf${i}le.raw lebom.utf${i}be &&
test_must_fail git add lebom.utf${i}be 2>err.out &&
- test_i18ngrep "fatal: BOM is prohibited .* utf-${i}be" err.out &&
- test_i18ngrep "use UTF-${i} as working-tree-encoding" err.out &&
+ test_grep "fatal: BOM is prohibited .* utf-${i}be" err.out &&
+ test_grep "use UTF-${i} as working-tree-encoding" err.out &&
cp bebom.utf${i}be.raw bebom.utf${i}le &&
test_must_fail git add bebom.utf${i}le 2>err.out &&
- test_i18ngrep "fatal: BOM is prohibited .* utf-${i}LE" err.out &&
- test_i18ngrep "use UTF-${i} as working-tree-encoding" err.out &&
+ test_grep "fatal: BOM is prohibited .* utf-${i}LE" err.out &&
+ test_grep "use UTF-${i} as working-tree-encoding" err.out &&
cp lebom.utf${i}le.raw lebom.utf${i}le &&
test_must_fail git add lebom.utf${i}le 2>err.out &&
- test_i18ngrep "fatal: BOM is prohibited .* utf-${i}LE" err.out &&
- test_i18ngrep "use UTF-${i} as working-tree-encoding" err.out
+ test_grep "fatal: BOM is prohibited .* utf-${i}LE" err.out &&
+ test_grep "use UTF-${i} as working-tree-encoding" err.out
'
test_expect_success "check required UTF-${i} BOM" '
@@ -115,21 +118,21 @@ do
cp nobom.utf${i}be.raw nobom.utf${i} &&
test_must_fail git add nobom.utf${i} 2>err.out &&
- test_i18ngrep "fatal: BOM is required .* utf-${i}" err.out &&
- test_i18ngrep "use UTF-${i}BE or UTF-${i}LE" err.out &&
+ test_grep "fatal: BOM is required .* utf-${i}" err.out &&
+ test_grep "use UTF-${i}BE or UTF-${i}LE" err.out &&
cp nobom.utf${i}le.raw nobom.utf${i} &&
test_must_fail git add nobom.utf${i} 2>err.out &&
- test_i18ngrep "fatal: BOM is required .* utf-${i}" err.out &&
- test_i18ngrep "use UTF-${i}BE or UTF-${i}LE" err.out
+ test_grep "fatal: BOM is required .* utf-${i}" err.out &&
+ test_grep "use UTF-${i}BE or UTF-${i}LE" err.out
'
test_expect_success "eol conversion for UTF-${i} encoded files on checkout" '
test_when_finished "rm -f crlf.utf${i}.raw lf.utf${i}.raw" &&
test_when_finished "git reset --hard HEAD^" &&
- cat lf.utf8.raw | write_utf${i} >lf.utf${i}.raw &&
- cat crlf.utf8.raw | write_utf${i} >crlf.utf${i}.raw &&
+ write_utf${i} <lf.utf8.raw >lf.utf${i}.raw &&
+ write_utf${i} <crlf.utf8.raw >crlf.utf${i}.raw &&
cp crlf.utf${i}.raw eol.utf${i} &&
cat >expectIndexLF <<-EOF &&
@@ -166,7 +169,7 @@ test_expect_success 'check unsupported encodings' '
echo "*.set text working-tree-encoding" >.gitattributes &&
printf "set" >t.set &&
test_must_fail git add t.set 2>err.out &&
- test_i18ngrep "true/false are no valid working-tree-encodings" err.out &&
+ test_grep "true/false are no valid working-tree-encodings" err.out &&
echo "*.unset text -working-tree-encoding" >.gitattributes &&
printf "unset" >t.unset &&
@@ -179,7 +182,7 @@ test_expect_success 'check unsupported encodings' '
echo "*.garbage text working-tree-encoding=garbage" >.gitattributes &&
printf "garbage" >t.garbage &&
test_must_fail git add t.garbage 2>err.out &&
- test_i18ngrep "failed to encode" err.out
+ test_grep "failed to encode" err.out
'
test_expect_success 'error if encoding round trip is not the same during refresh' '
@@ -198,7 +201,7 @@ test_expect_success 'error if encoding round trip is not the same during refresh
git update-ref refs/heads/main $COMMIT &&
test_must_fail git checkout HEAD^ 2>err.out &&
- test_i18ngrep "error: .* overwritten by checkout:" err.out
+ test_grep "error: .* overwritten by checkout:" err.out
'
test_expect_success 'error if encoding garbage is already in Git' '
@@ -214,7 +217,7 @@ test_expect_success 'error if encoding garbage is already in Git' '
git update-ref refs/heads/main $COMMIT &&
git diff 2>err.out &&
- test_i18ngrep "error: BOM is required" err.out
+ test_grep "error: BOM is required" err.out
'
test_lazy_prereq ICONV_SHIFT_JIS '
diff --git a/t/t0029-core-unsetenvvars.sh b/t/t0029-core-unsetenvvars.sh
index 24ce46a..4e8e90d 100755
--- a/t/t0029-core-unsetenvvars.sh
+++ b/t/t0029-core-unsetenvvars.sh
@@ -2,6 +2,7 @@
test_description='test the Windows-only core.unsetenvvars setting'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
if ! test_have_prereq MINGW
@@ -11,8 +12,7 @@ then
fi
test_expect_success 'setup' '
- mkdir -p "$TRASH_DIRECTORY/.git/hooks" &&
- write_script "$TRASH_DIRECTORY/.git/hooks/pre-commit" <<-\EOF
+ test_hook --setup pre-commit <<-\EOF
echo $HOBBES >&2
EOF
'
diff --git a/t/t0030-stripspace.sh b/t/t0030-stripspace.sh
index 0c24a0f..f10f42f 100755
--- a/t/t0030-stripspace.sh
+++ b/t/t0030-stripspace.sh
@@ -5,6 +5,7 @@
test_description='git stripspace'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
t40='A quick brown fox jumps over the lazy do'
@@ -12,379 +13,382 @@ s40=' '
sss="$s40$s40$s40$s40$s40$s40$s40$s40$s40$s40" # 400
ttt="$t40$t40$t40$t40$t40$t40$t40$t40$t40$t40" # 400
-test_expect_success \
- 'long lines without spaces should be unchanged' '
- echo "$ttt" >expect &&
- git stripspace <expect >actual &&
- test_cmp expect actual &&
+printf_git_stripspace () {
+ printf "$1" | git stripspace
+}
- echo "$ttt$ttt" >expect &&
- git stripspace <expect >actual &&
- test_cmp expect actual &&
+test_expect_success 'long lines without spaces should be unchanged' '
+ echo "$ttt" >expect &&
+ git stripspace <expect >actual &&
+ test_cmp expect actual &&
- echo "$ttt$ttt$ttt" >expect &&
- git stripspace <expect >actual &&
- test_cmp expect actual &&
+ echo "$ttt$ttt" >expect &&
+ git stripspace <expect >actual &&
+ test_cmp expect actual &&
- echo "$ttt$ttt$ttt$ttt" >expect &&
- git stripspace <expect >actual &&
- test_cmp expect actual
+ echo "$ttt$ttt$ttt" >expect &&
+ git stripspace <expect >actual &&
+ test_cmp expect actual &&
+
+ echo "$ttt$ttt$ttt$ttt" >expect &&
+ git stripspace <expect >actual &&
+ test_cmp expect actual
'
-test_expect_success \
- 'lines with spaces at the beginning should be unchanged' '
- echo "$sss$ttt" >expect &&
- git stripspace <expect >actual &&
- test_cmp expect actual &&
+test_expect_success 'lines with spaces at the beginning should be unchanged' '
+ echo "$sss$ttt" >expect &&
+ git stripspace <expect >actual &&
+ test_cmp expect actual &&
- echo "$sss$sss$ttt" >expect &&
- git stripspace <expect >actual &&
- test_cmp expect actual &&
+ echo "$sss$sss$ttt" >expect &&
+ git stripspace <expect >actual &&
+ test_cmp expect actual &&
- echo "$sss$sss$sss$ttt" >expect &&
- git stripspace <expect >actual &&
- test_cmp expect actual
+ echo "$sss$sss$sss$ttt" >expect &&
+ git stripspace <expect >actual &&
+ test_cmp expect actual
'
-test_expect_success \
- 'lines with intermediate spaces should be unchanged' '
- echo "$ttt$sss$ttt" >expect &&
- git stripspace <expect >actual &&
- test_cmp expect actual &&
+test_expect_success 'lines with intermediate spaces should be unchanged' '
+ echo "$ttt$sss$ttt" >expect &&
+ git stripspace <expect >actual &&
+ test_cmp expect actual &&
- echo "$ttt$sss$sss$ttt" >expect &&
- git stripspace <expect >actual &&
- test_cmp expect actual
+ echo "$ttt$sss$sss$ttt" >expect &&
+ git stripspace <expect >actual &&
+ test_cmp expect actual
'
-test_expect_success \
- 'consecutive blank lines should be unified' '
- printf "$ttt\n\n$ttt\n" > expect &&
- printf "$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
- test_cmp expect actual &&
+test_expect_success 'consecutive blank lines should be unified' '
+ printf "$ttt\n\n$ttt\n" > expect &&
+ printf "$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt$ttt\n\n$ttt\n" > expect &&
- printf "$ttt$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt$ttt\n\n$ttt\n" > expect &&
+ printf "$ttt$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt$ttt$ttt\n\n$ttt\n" > expect &&
- printf "$ttt$ttt$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt$ttt$ttt\n\n$ttt\n" > expect &&
+ printf "$ttt$ttt$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt\n\n$ttt\n" > expect &&
- printf "$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt\n\n$ttt\n" > expect &&
+ printf "$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt\n\n$ttt$ttt\n" > expect &&
- printf "$ttt\n\n\n\n\n$ttt$ttt\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt\n\n$ttt$ttt\n" > expect &&
+ printf "$ttt\n\n\n\n\n$ttt$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt\n\n$ttt$ttt$ttt\n" > expect &&
- printf "$ttt\n\n\n\n\n$ttt$ttt$ttt\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt\n\n$ttt$ttt$ttt\n" > expect &&
+ printf "$ttt\n\n\n\n\n$ttt$ttt$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt\n\n$ttt\n" > expect &&
- printf "$ttt\n\t\n \n\n \t\t\n$ttt\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt\n\n$ttt\n" > expect &&
+ printf "$ttt\n\t\n \n\n \t\t\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt$ttt\n\n$ttt\n" > expect &&
- printf "$ttt$ttt\n\t\n \n\n \t\t\n$ttt\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt$ttt\n\n$ttt\n" > expect &&
+ printf "$ttt$ttt\n\t\n \n\n \t\t\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt$ttt$ttt\n\n$ttt\n" > expect &&
- printf "$ttt$ttt$ttt\n\t\n \n\n \t\t\n$ttt\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt$ttt$ttt\n\n$ttt\n" > expect &&
+ printf "$ttt$ttt$ttt\n\t\n \n\n \t\t\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt\n\n$ttt\n" > expect &&
- printf "$ttt\n\t\n \n\n \t\t\n$ttt\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt\n\n$ttt\n" > expect &&
+ printf "$ttt\n\t\n \n\n \t\t\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt\n\n$ttt$ttt\n" > expect &&
- printf "$ttt\n\t\n \n\n \t\t\n$ttt$ttt\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt\n\n$ttt$ttt\n" > expect &&
+ printf "$ttt\n\t\n \n\n \t\t\n$ttt$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt\n\n$ttt$ttt$ttt\n" > expect &&
- printf "$ttt\n\t\n \n\n \t\t\n$ttt$ttt$ttt\n" | git stripspace >actual &&
- test_cmp expect actual
+ printf "$ttt\n\n$ttt$ttt$ttt\n" > expect &&
+ printf "$ttt\n\t\n \n\n \t\t\n$ttt$ttt$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual
'
-test_expect_success \
- 'only consecutive blank lines should be completely removed' '
+test_expect_success 'only consecutive blank lines should be completely removed' '
+ printf "\n" | git stripspace >actual &&
+ test_must_be_empty actual &&
- printf "\n" | git stripspace >actual &&
- test_must_be_empty actual &&
+ printf "\n\n\n" | git stripspace >actual &&
+ test_must_be_empty actual &&
- printf "\n\n\n" | git stripspace >actual &&
- test_must_be_empty actual &&
+ printf "$sss\n$sss\n$sss\n" | git stripspace >actual &&
+ test_must_be_empty actual &&
- printf "$sss\n$sss\n$sss\n" | git stripspace >actual &&
- test_must_be_empty actual &&
+ printf "$sss$sss\n$sss\n\n" | git stripspace >actual &&
+ test_must_be_empty actual &&
- printf "$sss$sss\n$sss\n\n" | git stripspace >actual &&
- test_must_be_empty actual &&
+ printf "\n$sss\n$sss$sss\n" | git stripspace >actual &&
+ test_must_be_empty actual &&
- printf "\n$sss\n$sss$sss\n" | git stripspace >actual &&
- test_must_be_empty actual &&
+ printf "$sss$sss$sss$sss\n\n\n" | git stripspace >actual &&
+ test_must_be_empty actual &&
- printf "$sss$sss$sss$sss\n\n\n" | git stripspace >actual &&
- test_must_be_empty actual &&
+ printf "\n$sss$sss$sss$sss\n\n" | git stripspace >actual &&
+ test_must_be_empty actual &&
- printf "\n$sss$sss$sss$sss\n\n" | git stripspace >actual &&
- test_must_be_empty actual &&
-
- printf "\n\n$sss$sss$sss$sss\n" | git stripspace >actual &&
- test_must_be_empty actual
+ printf "\n\n$sss$sss$sss$sss\n" | git stripspace >actual &&
+ test_must_be_empty actual
'
-test_expect_success \
- 'consecutive blank lines at the beginning should be removed' '
- printf "$ttt\n" > expect &&
- printf "\n$ttt\n" | git stripspace >actual &&
- test_cmp expect actual &&
+test_expect_success 'consecutive blank lines at the beginning should be removed' '
+ printf "$ttt\n" > expect &&
+ printf "\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt\n" > expect &&
- printf "\n\n\n$ttt\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt\n" > expect &&
+ printf "\n\n\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt$ttt\n" > expect &&
- printf "\n\n\n$ttt$ttt\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt$ttt\n" > expect &&
+ printf "\n\n\n$ttt$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt$ttt$ttt\n" > expect &&
- printf "\n\n\n$ttt$ttt$ttt\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt$ttt$ttt\n" > expect &&
+ printf "\n\n\n$ttt$ttt$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt$ttt$ttt$ttt\n" > expect &&
- printf "\n\n\n$ttt$ttt$ttt$ttt\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt$ttt$ttt$ttt\n" > expect &&
+ printf "\n\n\n$ttt$ttt$ttt$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt\n" > expect &&
+ printf "$ttt\n" > expect &&
- printf "$sss\n$sss\n$sss\n$ttt\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$sss\n$sss\n$sss\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "\n$sss\n$sss$sss\n$ttt\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "\n$sss\n$sss$sss\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$sss$sss\n$sss\n\n$ttt\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$sss$sss\n$sss\n\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$sss$sss$sss\n\n\n$ttt\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$sss$sss$sss\n\n\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "\n$sss$sss$sss\n\n$ttt\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "\n$sss$sss$sss\n\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "\n\n$sss$sss$sss\n$ttt\n" | git stripspace >actual &&
- test_cmp expect actual
+ printf "\n\n$sss$sss$sss\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual
'
-test_expect_success \
- 'consecutive blank lines at the end should be removed' '
- printf "$ttt\n" > expect &&
- printf "$ttt\n\n" | git stripspace >actual &&
- test_cmp expect actual &&
+test_expect_success 'consecutive blank lines at the end should be removed' '
+ printf "$ttt\n" > expect &&
+ printf "$ttt\n\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt\n" > expect &&
- printf "$ttt\n\n\n\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt\n" > expect &&
+ printf "$ttt\n\n\n\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt$ttt\n" > expect &&
- printf "$ttt$ttt\n\n\n\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt$ttt\n" > expect &&
+ printf "$ttt$ttt\n\n\n\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt$ttt$ttt\n" > expect &&
- printf "$ttt$ttt$ttt\n\n\n\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt$ttt$ttt\n" > expect &&
+ printf "$ttt$ttt$ttt\n\n\n\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt$ttt$ttt$ttt\n" > expect &&
- printf "$ttt$ttt$ttt$ttt\n\n\n\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt$ttt$ttt$ttt\n" > expect &&
+ printf "$ttt$ttt$ttt$ttt\n\n\n\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt\n" > expect &&
+ printf "$ttt\n" > expect &&
- printf "$ttt\n$sss\n$sss\n$sss\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt\n$sss\n$sss\n$sss\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt\n\n$sss\n$sss$sss\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt\n\n$sss\n$sss$sss\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt\n$sss$sss\n$sss\n\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt\n$sss$sss\n$sss\n\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt\n$sss$sss$sss\n\n\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt\n$sss$sss$sss\n\n\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt\n\n$sss$sss$sss\n\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt\n\n$sss$sss$sss\n\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt\n\n\n$sss$sss$sss\n" | git stripspace >actual &&
- test_cmp expect actual
+ printf "$ttt\n\n\n$sss$sss$sss\n" | git stripspace >actual &&
+ test_cmp expect actual
'
-test_expect_success \
- 'text without newline at end should end with newline' '
- test $(printf "$ttt" | git stripspace | wc -l) -gt 0 &&
- test $(printf "$ttt$ttt" | git stripspace | wc -l) -gt 0 &&
- test $(printf "$ttt$ttt$ttt" | git stripspace | wc -l) -gt 0 &&
- test $(printf "$ttt$ttt$ttt$ttt" | git stripspace | wc -l) -gt 0
+test_expect_success 'text without newline at end should end with newline' '
+ test_stdout_line_count -gt 0 printf_git_stripspace "$ttt" &&
+ test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt" &&
+ test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$ttt" &&
+ test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$ttt$ttt"
'
# text plus spaces at the end:
-test_expect_success \
- 'text plus spaces without newline at end should end with newline' '
- test $(printf "$ttt$sss" | git stripspace | wc -l) -gt 0 &&
- test $(printf "$ttt$ttt$sss" | git stripspace | wc -l) -gt 0 &&
- test $(printf "$ttt$ttt$ttt$sss" | git stripspace | wc -l) -gt 0 &&
- test $(printf "$ttt$sss$sss" | git stripspace | wc -l) -gt 0 &&
- test $(printf "$ttt$ttt$sss$sss" | git stripspace | wc -l) -gt 0 &&
- test $(printf "$ttt$sss$sss$sss" | git stripspace | wc -l) -gt 0
+test_expect_success 'text plus spaces without newline at end should end with newline' '
+ test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$sss" &&
+ test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$sss" &&
+ test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$ttt$sss" &&
+ test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$sss$sss" &&
+ test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$sss$sss" &&
+ test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$sss$sss$sss"
'
-test_expect_success \
- 'text plus spaces without newline at end should not show spaces' '
- ! (printf "$ttt$sss" | git stripspace | grep " " >/dev/null) &&
- ! (printf "$ttt$ttt$sss" | git stripspace | grep " " >/dev/null) &&
- ! (printf "$ttt$ttt$ttt$sss" | git stripspace | grep " " >/dev/null) &&
- ! (printf "$ttt$sss$sss" | git stripspace | grep " " >/dev/null) &&
- ! (printf "$ttt$ttt$sss$sss" | git stripspace | grep " " >/dev/null) &&
- ! (printf "$ttt$sss$sss$sss" | git stripspace | grep " " >/dev/null)
+test_expect_success 'text plus spaces without newline at end should not show spaces' '
+ printf "$ttt$sss" | git stripspace >tmp &&
+ ! grep " " tmp >/dev/null &&
+ printf "$ttt$ttt$sss" | git stripspace >tmp &&
+ ! grep " " tmp >/dev/null &&
+ printf "$ttt$ttt$ttt$sss" | git stripspace >tmp &&
+ ! grep " " tmp >/dev/null &&
+ printf "$ttt$sss$sss" | git stripspace >tmp &&
+ ! grep " " tmp >/dev/null &&
+ printf "$ttt$ttt$sss$sss" | git stripspace >tmp &&
+ ! grep " " tmp >/dev/null &&
+ printf "$ttt$sss$sss$sss" | git stripspace >tmp &&
+ ! grep " " tmp >/dev/null
'
-test_expect_success \
- 'text plus spaces without newline should show the correct lines' '
- printf "$ttt\n" >expect &&
- printf "$ttt$sss" | git stripspace >actual &&
- test_cmp expect actual &&
+test_expect_success 'text plus spaces without newline should show the correct lines' '
+ printf "$ttt\n" >expect &&
+ printf "$ttt$sss" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt\n" >expect &&
- printf "$ttt$sss$sss" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt\n" >expect &&
+ printf "$ttt$sss$sss" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt\n" >expect &&
- printf "$ttt$sss$sss$sss" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt\n" >expect &&
+ printf "$ttt$sss$sss$sss" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt$ttt\n" >expect &&
- printf "$ttt$ttt$sss" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt$ttt\n" >expect &&
+ printf "$ttt$ttt$sss" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt$ttt\n" >expect &&
- printf "$ttt$ttt$sss$sss" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt$ttt\n" >expect &&
+ printf "$ttt$ttt$sss$sss" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt$ttt$ttt\n" >expect &&
- printf "$ttt$ttt$ttt$sss" | git stripspace >actual &&
- test_cmp expect actual
+ printf "$ttt$ttt$ttt\n" >expect &&
+ printf "$ttt$ttt$ttt$sss" | git stripspace >actual &&
+ test_cmp expect actual
'
-test_expect_success \
- 'text plus spaces at end should not show spaces' '
- ! (echo "$ttt$sss" | git stripspace | grep " " >/dev/null) &&
- ! (echo "$ttt$ttt$sss" | git stripspace | grep " " >/dev/null) &&
- ! (echo "$ttt$ttt$ttt$sss" | git stripspace | grep " " >/dev/null) &&
- ! (echo "$ttt$sss$sss" | git stripspace | grep " " >/dev/null) &&
- ! (echo "$ttt$ttt$sss$sss" | git stripspace | grep " " >/dev/null) &&
- ! (echo "$ttt$sss$sss$sss" | git stripspace | grep " " >/dev/null)
+test_expect_success 'text plus spaces at end should not show spaces' '
+ echo "$ttt$sss" | git stripspace >tmp &&
+ ! grep " " tmp >/dev/null &&
+ echo "$ttt$ttt$sss" | git stripspace >tmp &&
+ ! grep " " tmp >/dev/null &&
+ echo "$ttt$ttt$ttt$sss" | git stripspace >tmp &&
+ ! grep " " tmp >/dev/null &&
+ echo "$ttt$sss$sss" | git stripspace >tmp &&
+ ! grep " " tmp >/dev/null &&
+ echo "$ttt$ttt$sss$sss" | git stripspace >tmp &&
+ ! grep " " tmp >/dev/null &&
+ echo "$ttt$sss$sss$sss" | git stripspace >tmp &&
+ ! grep " " tmp >/dev/null
'
-test_expect_success \
- 'text plus spaces at end should be cleaned and newline must remain' '
- echo "$ttt" >expect &&
- echo "$ttt$sss" | git stripspace >actual &&
- test_cmp expect actual &&
+test_expect_success 'text plus spaces at end should be cleaned and newline must remain' '
+ echo "$ttt" >expect &&
+ echo "$ttt$sss" | git stripspace >actual &&
+ test_cmp expect actual &&
- echo "$ttt" >expect &&
- echo "$ttt$sss$sss" | git stripspace >actual &&
- test_cmp expect actual &&
+ echo "$ttt" >expect &&
+ echo "$ttt$sss$sss" | git stripspace >actual &&
+ test_cmp expect actual &&
- echo "$ttt" >expect &&
- echo "$ttt$sss$sss$sss" | git stripspace >actual &&
- test_cmp expect actual &&
+ echo "$ttt" >expect &&
+ echo "$ttt$sss$sss$sss" | git stripspace >actual &&
+ test_cmp expect actual &&
- echo "$ttt$ttt" >expect &&
- echo "$ttt$ttt$sss" | git stripspace >actual &&
- test_cmp expect actual &&
+ echo "$ttt$ttt" >expect &&
+ echo "$ttt$ttt$sss" | git stripspace >actual &&
+ test_cmp expect actual &&
- echo "$ttt$ttt" >expect &&
- echo "$ttt$ttt$sss$sss" | git stripspace >actual &&
- test_cmp expect actual &&
+ echo "$ttt$ttt" >expect &&
+ echo "$ttt$ttt$sss$sss" | git stripspace >actual &&
+ test_cmp expect actual &&
- echo "$ttt$ttt$ttt" >expect &&
- echo "$ttt$ttt$ttt$sss" | git stripspace >actual &&
- test_cmp expect actual
+ echo "$ttt$ttt$ttt" >expect &&
+ echo "$ttt$ttt$ttt$sss" | git stripspace >actual &&
+ test_cmp expect actual
'
# spaces only:
-test_expect_success \
- 'spaces with newline at end should be replaced with empty string' '
- echo | git stripspace >actual &&
- test_must_be_empty actual &&
+test_expect_success 'spaces with newline at end should be replaced with empty string' '
+ echo | git stripspace >actual &&
+ test_must_be_empty actual &&
- echo "$sss" | git stripspace >actual &&
- test_must_be_empty actual &&
+ echo "$sss" | git stripspace >actual &&
+ test_must_be_empty actual &&
- echo "$sss$sss" | git stripspace >actual &&
- test_must_be_empty actual &&
+ echo "$sss$sss" | git stripspace >actual &&
+ test_must_be_empty actual &&
- echo "$sss$sss$sss" | git stripspace >actual &&
- test_must_be_empty actual &&
+ echo "$sss$sss$sss" | git stripspace >actual &&
+ test_must_be_empty actual &&
- echo "$sss$sss$sss$sss" | git stripspace >actual &&
- test_must_be_empty actual
+ echo "$sss$sss$sss$sss" | git stripspace >actual &&
+ test_must_be_empty actual
'
-test_expect_success \
- 'spaces without newline at end should not show spaces' '
- ! (printf "" | git stripspace | grep " " >/dev/null) &&
- ! (printf "$sss" | git stripspace | grep " " >/dev/null) &&
- ! (printf "$sss$sss" | git stripspace | grep " " >/dev/null) &&
- ! (printf "$sss$sss$sss" | git stripspace | grep " " >/dev/null) &&
- ! (printf "$sss$sss$sss$sss" | git stripspace | grep " " >/dev/null)
+test_expect_success 'spaces without newline at end should not show spaces' '
+ printf "" | git stripspace >tmp &&
+ ! grep " " tmp >/dev/null &&
+ printf "$sss" | git stripspace >tmp &&
+ ! grep " " tmp >/dev/null &&
+ printf "$sss$sss" | git stripspace >tmp &&
+ ! grep " " tmp >/dev/null &&
+ printf "$sss$sss$sss" | git stripspace >tmp &&
+ ! grep " " tmp >/dev/null &&
+ printf "$sss$sss$sss$sss" | git stripspace >tmp &&
+ ! grep " " tmp >/dev/null
'
-test_expect_success \
- 'spaces without newline at end should be replaced with empty string' '
- printf "" | git stripspace >actual &&
- test_must_be_empty actual &&
+test_expect_success 'spaces without newline at end should be replaced with empty string' '
+ printf "" | git stripspace >actual &&
+ test_must_be_empty actual &&
- printf "$sss$sss" | git stripspace >actual &&
- test_must_be_empty actual &&
+ printf "$sss$sss" | git stripspace >actual &&
+ test_must_be_empty actual &&
- printf "$sss$sss$sss" | git stripspace >actual &&
- test_must_be_empty actual &&
+ printf "$sss$sss$sss" | git stripspace >actual &&
+ test_must_be_empty actual &&
- printf "$sss$sss$sss$sss" | git stripspace >actual &&
- test_must_be_empty actual
+ printf "$sss$sss$sss$sss" | git stripspace >actual &&
+ test_must_be_empty actual
'
-test_expect_success \
- 'consecutive text lines should be unchanged' '
- printf "$ttt$ttt\n$ttt\n" >expect &&
- printf "$ttt$ttt\n$ttt\n" | git stripspace >actual &&
- test_cmp expect actual &&
+test_expect_success 'consecutive text lines should be unchanged' '
+ printf "$ttt$ttt\n$ttt\n" >expect &&
+ printf "$ttt$ttt\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt\n$ttt$ttt\n$ttt\n" >expect &&
- printf "$ttt\n$ttt$ttt\n$ttt\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt\n$ttt$ttt\n$ttt\n" >expect &&
+ printf "$ttt\n$ttt$ttt\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt\n$ttt\n$ttt\n$ttt$ttt\n" >expect &&
- printf "$ttt\n$ttt\n$ttt\n$ttt$ttt\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt\n$ttt\n$ttt\n$ttt$ttt\n" >expect &&
+ printf "$ttt\n$ttt\n$ttt\n$ttt$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt\n$ttt\n\n$ttt$ttt\n$ttt\n" >expect &&
- printf "$ttt\n$ttt\n\n$ttt$ttt\n$ttt\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt\n$ttt\n\n$ttt$ttt\n$ttt\n" >expect &&
+ printf "$ttt\n$ttt\n\n$ttt$ttt\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt$ttt\n\n$ttt\n$ttt$ttt\n" >expect &&
- printf "$ttt$ttt\n\n$ttt\n$ttt$ttt\n" | git stripspace >actual &&
- test_cmp expect actual &&
+ printf "$ttt$ttt\n\n$ttt\n$ttt$ttt\n" >expect &&
+ printf "$ttt$ttt\n\n$ttt\n$ttt$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
- printf "$ttt\n$ttt$ttt\n\n$ttt\n" >expect &&
- printf "$ttt\n$ttt$ttt\n\n$ttt\n" | git stripspace >actual &&
- test_cmp expect actual
+ printf "$ttt\n$ttt$ttt\n\n$ttt\n" >expect &&
+ printf "$ttt\n$ttt$ttt\n\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual
'
test_expect_success 'strip comments, too' '
@@ -397,6 +401,21 @@ test_expect_success 'strip comments with changed comment char' '
test -z "$(echo "; comment" | git -c core.commentchar=";" stripspace -s)"
'
+test_expect_success 'strip comments with changed comment string' '
+ test ! -z "$(echo "// comment" | git -c core.commentchar=// stripspace)" &&
+ test -z "$(echo "// comment" | git -c core.commentchar="//" stripspace -s)"
+'
+
+test_expect_success 'newline as commentchar is forbidden' '
+ test_must_fail git -c core.commentChar="$LF" stripspace -s 2>err &&
+ grep "core.commentchar cannot contain newline" err
+'
+
+test_expect_success 'empty commentchar is forbidden' '
+ test_must_fail git -c core.commentchar= stripspace -s 2>err &&
+ grep "core.commentchar must have at least one character" err
+'
+
test_expect_success '-c with single line' '
printf "# foo\n" >expect &&
printf "foo" | git stripspace -c >actual &&
diff --git a/t/t0032-reftable-unittest.sh b/t/t0032-reftable-unittest.sh
new file mode 100755
index 0000000..471cb37
--- /dev/null
+++ b/t/t0032-reftable-unittest.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+#
+# Copyright (c) 2020 Google LLC
+#
+
+test_description='reftable unittests'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'unittests' '
+ TMPDIR=$(pwd) && export TMPDIR &&
+ test-tool reftable
+'
+
+test_done
diff --git a/t/t0033-safe-directory.sh b/t/t0033-safe-directory.sh
new file mode 100755
index 0000000..dc34968
--- /dev/null
+++ b/t/t0033-safe-directory.sh
@@ -0,0 +1,83 @@
+#!/bin/sh
+
+test_description='verify safe.directory checks'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+GIT_TEST_ASSUME_DIFFERENT_OWNER=1
+export GIT_TEST_ASSUME_DIFFERENT_OWNER
+
+expect_rejected_dir () {
+ test_must_fail git status 2>err &&
+ grep "dubious ownership" err
+}
+
+test_expect_success 'safe.directory is not set' '
+ expect_rejected_dir
+'
+
+test_expect_success 'safe.directory on the command line' '
+ git -c safe.directory="$(pwd)" status
+'
+
+test_expect_success 'safe.directory in the environment' '
+ env GIT_CONFIG_COUNT=1 \
+ GIT_CONFIG_KEY_0="safe.directory" \
+ GIT_CONFIG_VALUE_0="$(pwd)" \
+ git status
+'
+
+test_expect_success 'safe.directory in GIT_CONFIG_PARAMETERS' '
+ env GIT_CONFIG_PARAMETERS="${SQ}safe.directory${SQ}=${SQ}$(pwd)${SQ}" \
+ git status
+'
+
+test_expect_success 'ignoring safe.directory in repo config' '
+ (
+ unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
+ git config safe.directory "$(pwd)"
+ ) &&
+ expect_rejected_dir
+'
+
+test_expect_success 'safe.directory does not match' '
+ git config --global safe.directory bogus &&
+ expect_rejected_dir
+'
+
+test_expect_success 'path exist as different key' '
+ git config --global foo.bar "$(pwd)" &&
+ expect_rejected_dir
+'
+
+test_expect_success 'safe.directory matches' '
+ git config --global --add safe.directory "$(pwd)" &&
+ git status
+'
+
+test_expect_success 'safe.directory matches, but is reset' '
+ git config --global --add safe.directory "" &&
+ expect_rejected_dir
+'
+
+test_expect_success 'safe.directory=*' '
+ git config --global --add safe.directory "*" &&
+ git status
+'
+
+test_expect_success 'safe.directory=*, but is reset' '
+ git config --global --add safe.directory "" &&
+ expect_rejected_dir
+'
+
+test_expect_success 'safe.directory in included file' '
+ cat >gitconfig-include <<-EOF &&
+ [safe]
+ directory = "$(pwd)"
+ EOF
+ git config --global --add include.path "$(pwd)/gitconfig-include" &&
+ git status
+'
+
+test_done
diff --git a/t/t0034-root-safe-directory.sh b/t/t0034-root-safe-directory.sh
new file mode 100755
index 0000000..ff31176
--- /dev/null
+++ b/t/t0034-root-safe-directory.sh
@@ -0,0 +1,93 @@
+#!/bin/sh
+
+test_description='verify safe.directory checks while running as root'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-sudo.sh
+
+if [ "$GIT_TEST_ALLOW_SUDO" != "YES" ]
+then
+ skip_all="You must set env var GIT_TEST_ALLOW_SUDO=YES in order to run this test"
+ test_done
+fi
+
+if ! test_have_prereq NOT_ROOT
+then
+ skip_all="These tests do not support running as root"
+ test_done
+fi
+
+test_lazy_prereq SUDO '
+ sudo -n id -u >u &&
+ id -u root >r &&
+ test_cmp u r &&
+ command -v git >u &&
+ sudo command -v git >r &&
+ test_cmp u r
+'
+
+if ! test_have_prereq SUDO
+then
+ skip_all="Your sudo/system configuration is either too strict or unsupported"
+ test_done
+fi
+
+test_expect_success SUDO 'setup' '
+ sudo rm -rf root &&
+ mkdir -p root/r &&
+ (
+ cd root/r &&
+ git init
+ )
+'
+
+test_expect_success SUDO 'sudo git status as original owner' '
+ (
+ cd root/r &&
+ git status &&
+ sudo git status
+ )
+'
+
+test_expect_success SUDO 'setup root owned repository' '
+ sudo mkdir -p root/p &&
+ sudo git init root/p
+'
+
+test_expect_success 'cannot access if owned by root' '
+ (
+ cd root/p &&
+ test_must_fail git status
+ )
+'
+
+test_expect_success 'can access if addressed explicitly' '
+ (
+ cd root/p &&
+ GIT_DIR=.git GIT_WORK_TREE=. git status
+ )
+'
+
+test_expect_success SUDO 'can access with sudo if root' '
+ (
+ cd root/p &&
+ sudo git status
+ )
+'
+
+test_expect_success SUDO 'can access with sudo if root by removing SUDO_UID' '
+ (
+ cd root/p &&
+ run_with_sudo <<-END
+ unset SUDO_UID &&
+ git status
+ END
+ )
+'
+
+# this MUST be always the last test
+test_expect_success SUDO 'cleanup' '
+ sudo rm -rf root
+'
+
+test_done
diff --git a/t/t0035-safe-bare-repository.sh b/t/t0035-safe-bare-repository.sh
new file mode 100755
index 0000000..d3cb2a1
--- /dev/null
+++ b/t/t0035-safe-bare-repository.sh
@@ -0,0 +1,107 @@
+#!/bin/sh
+
+test_description='verify safe.bareRepository checks'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+pwd="$(pwd)"
+
+expect_accepted_implicit () {
+ test_when_finished 'rm "$pwd/trace.perf"' &&
+ GIT_TRACE2_PERF="$pwd/trace.perf" git "$@" rev-parse --git-dir &&
+ # Note: we're intentionally only checking that the bare repo has a
+ # directory *prefix* of $pwd
+ grep -F "implicit-bare-repository:$pwd" "$pwd/trace.perf"
+}
+
+expect_accepted_explicit () {
+ test_when_finished 'rm "$pwd/trace.perf"' &&
+ GIT_DIR="$1" GIT_TRACE2_PERF="$pwd/trace.perf" git rev-parse --git-dir &&
+ ! grep -F "implicit-bare-repository:$pwd" "$pwd/trace.perf"
+}
+
+expect_rejected () {
+ test_when_finished 'rm "$pwd/trace.perf"' &&
+ test_env GIT_TRACE2_PERF="$pwd/trace.perf" \
+ test_must_fail git "$@" rev-parse --git-dir 2>err &&
+ grep -F "cannot use bare repository" err &&
+ grep -F "implicit-bare-repository:$pwd" "$pwd/trace.perf"
+}
+
+test_expect_success 'setup an embedded bare repo, secondary worktree and submodule' '
+ git init outer-repo &&
+ git init --bare --initial-branch=main outer-repo/bare-repo &&
+ git -C outer-repo worktree add ../outer-secondary &&
+ test_path_is_dir outer-secondary &&
+ (
+ cd outer-repo &&
+ test_commit A &&
+ git push bare-repo +HEAD:refs/heads/main &&
+ git -c protocol.file.allow=always \
+ submodule add --name subn -- ./bare-repo subd
+ ) &&
+ test_path_is_dir outer-repo/.git/worktrees/outer-secondary &&
+ test_path_is_dir outer-repo/.git/modules/subn
+'
+
+test_expect_success 'safe.bareRepository unset' '
+ test_unconfig --global safe.bareRepository &&
+ expect_accepted_implicit -C outer-repo/bare-repo
+'
+
+test_expect_success 'safe.bareRepository=all' '
+ test_config_global safe.bareRepository all &&
+ expect_accepted_implicit -C outer-repo/bare-repo
+'
+
+test_expect_success 'safe.bareRepository=explicit' '
+ test_config_global safe.bareRepository explicit &&
+ expect_rejected -C outer-repo/bare-repo
+'
+
+test_expect_success 'safe.bareRepository in the repository' '
+ # safe.bareRepository must not be "explicit", otherwise
+ # git config fails with "fatal: not in a git directory" (like
+ # safe.directory)
+ test_config -C outer-repo/bare-repo safe.bareRepository all &&
+ test_config_global safe.bareRepository explicit &&
+ expect_rejected -C outer-repo/bare-repo
+'
+
+test_expect_success 'safe.bareRepository on the command line' '
+ test_config_global safe.bareRepository explicit &&
+ expect_accepted_implicit -C outer-repo/bare-repo \
+ -c safe.bareRepository=all
+'
+
+test_expect_success 'safe.bareRepository in included file' '
+ cat >gitconfig-include <<-\EOF &&
+ [safe]
+ bareRepository = explicit
+ EOF
+ git config --global --add include.path "$(pwd)/gitconfig-include" &&
+ expect_rejected -C outer-repo/bare-repo
+'
+
+test_expect_success 'no trace when GIT_DIR is explicitly provided' '
+ expect_accepted_explicit "$pwd/outer-repo/bare-repo"
+'
+
+test_expect_success 'no trace when "bare repository" is .git' '
+ expect_accepted_implicit -C outer-repo/.git
+'
+
+test_expect_success 'no trace when "bare repository" is a subdir of .git' '
+ expect_accepted_implicit -C outer-repo/.git/objects
+'
+
+test_expect_success 'no trace in $GIT_DIR of secondary worktree' '
+ expect_accepted_implicit -C outer-repo/.git/worktrees/outer-secondary
+'
+
+test_expect_success 'no trace in $GIT_DIR of a submodule' '
+ expect_accepted_implicit -C outer-repo/.git/modules/subn
+'
+
+test_done
diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
index ad4746d..8bb2a8b 100755
--- a/t/t0040-parse-options.sh
+++ b/t/t0040-parse-options.sh
@@ -5,6 +5,7 @@
test_description='our own option parser'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
cat >expect <<\EOF
@@ -12,48 +13,55 @@ usage: test-tool parse-options <options>
A helper function for the parse-options API.
- --yes get a boolean
+ --[no-]yes get a boolean
-D, --no-doubt begins with 'no-'
+ --doubt opposite of --no-doubt
-B, --no-fear be brave
- -b, --boolean increment by one
- -4, --or4 bitwise-or boolean with ...0100
- --neg-or4 same as --no-or4
+ -b, --[no-]boolean increment by one
+ -4, --[no-]or4 bitwise-or boolean with ...0100
+ --[no-]neg-or4 same as --no-or4
- -i, --integer <n> get a integer
+ -i, --[no-]integer <n>
+ get a integer
-j <n> get a integer, too
-m, --magnitude <n> get a magnitude
- --set23 set integer to 23
+ --[no-]set23 set integer to 23
--mode1 set integer to 1 (cmdmode option)
--mode2 set integer to 2 (cmdmode option)
- -L, --length <str> get length of <str>
- -F, --file <file> set file to <file>
+ --[no-]mode34 (3|4) set integer to 3 or 4 (cmdmode option)
+ -L, --[no-]length <str>
+ get length of <str>
+ -F, --[no-]file <file>
+ set file to <file>
String options
- -s, --string <string>
+ -s, --[no-]string <string>
get a string
- --string2 <str> get another string
- --st <st> get another string (pervert ordering)
+ --[no-]string2 <str> get another string
+ --[no-]st <st> get another string (pervert ordering)
-o <str> get another string
- --list <str> add str to list
+ --longhelp help text of this entry
+ spans multiple lines
+ --[no-]list <str> add str to list
Magic arguments
- --quux means --quux
-NUM set integer to NUM
+ same as -b
--ambiguous positive ambiguity
--no-ambiguous negative ambiguity
Standard options
- --abbrev[=<n>] use <n> digits to display object names
- -v, --verbose be verbose
- -n, --dry-run dry run
- -q, --quiet be quiet
- --expect <string> expected output in the variable dump
+ --[no-]abbrev[=<n>] use <n> digits to display object names
+ -v, --[no-]verbose be verbose
+ -n, --[no-]dry-run dry run
+ -q, --[no-]quiet be quiet
+ --[no-]expect <string>
+ expected output in the variable dump
Alias
- -A, --alias-source <string>
+ -A, --[no-]alias-source <string>
get a string
- -Z, --alias-target <string>
+ -Z, --[no-]alias-target <string>
alias of --alias-source
EOF
@@ -169,9 +177,61 @@ test_expect_success 'long options' '
'
test_expect_success 'missing required value' '
- test_expect_code 129 test-tool parse-options -s &&
- test_expect_code 129 test-tool parse-options --string &&
- test_expect_code 129 test-tool parse-options --file
+ cat >expect <<-\EOF &&
+ error: switch `s'\'' requires a value
+ EOF
+ test_expect_code 129 test-tool parse-options -s 2>actual &&
+ test_cmp expect actual &&
+
+ cat >expect <<-\EOF &&
+ error: option `string'\'' requires a value
+ EOF
+ test_expect_code 129 test-tool parse-options --string 2>actual &&
+ test_cmp expect actual &&
+
+ cat >expect <<-\EOF &&
+ error: option `file'\'' requires a value
+ EOF
+ test_expect_code 129 test-tool parse-options --file 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'superfluous value provided: boolean' '
+ cat >expect <<-\EOF &&
+ error: option `yes'\'' takes no value
+ EOF
+ test_expect_code 129 test-tool parse-options --yes=hi 2>actual &&
+ test_cmp expect actual &&
+
+ cat >expect <<-\EOF &&
+ error: option `no-yes'\'' takes no value
+ EOF
+ test_expect_code 129 test-tool parse-options --no-yes=hi 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'superfluous value provided: boolean, abbreviated' '
+ cat >expect <<-\EOF &&
+ error: option `yes'\'' takes no value
+ EOF
+ test_expect_code 129 env GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
+ test-tool parse-options --ye=hi 2>actual &&
+ test_cmp expect actual &&
+
+ cat >expect <<-\EOF &&
+ error: option `no-yes'\'' takes no value
+ EOF
+ test_expect_code 129 env GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
+ test-tool parse-options --no-ye=hi 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'superfluous value provided: cmdmode' '
+ cat >expect <<-\EOF &&
+ error: option `mode1'\'' takes no value
+ EOF
+ test_expect_code 129 test-tool parse-options --mode1=hi 2>actual &&
+ test_cmp expect actual
'
cat >expect <<\EOF
@@ -263,10 +323,6 @@ test_expect_success 'detect possible typos' '
test_cmp typo.err output.err
'
-test_expect_success 'keep some options as arguments' '
- test-tool parse-options --expect="arg 00: --quux" --quux
-'
-
cat >expect <<\EOF
Callback: "four", 0
boolean: 5
@@ -327,19 +383,41 @@ test_expect_success 'OPT_NEGBIT() works' '
'
test_expect_success 'OPT_CMDMODE() works' '
- test-tool parse-options --expect="integer: 1" --mode1
+ test-tool parse-options --expect="integer: 1" --mode1 &&
+ test-tool parse-options --expect="integer: 3" --mode34=3
'
-test_expect_success 'OPT_CMDMODE() detects incompatibility' '
+test_expect_success 'OPT_CMDMODE() detects incompatibility (1)' '
test_must_fail test-tool parse-options --mode1 --mode2 >output 2>output.err &&
test_must_be_empty output &&
- test_i18ngrep "incompatible with --mode" output.err
+ test_grep "mode1" output.err &&
+ test_grep "mode2" output.err &&
+ test_grep "cannot be used together" output.err
'
-test_expect_success 'OPT_CMDMODE() detects incompatibility with something else' '
+test_expect_success 'OPT_CMDMODE() detects incompatibility (2)' '
test_must_fail test-tool parse-options --set23 --mode2 >output 2>output.err &&
test_must_be_empty output &&
- test_i18ngrep "incompatible with something else" output.err
+ test_grep "mode2" output.err &&
+ test_grep "set23" output.err &&
+ test_grep "cannot be used together" output.err
+'
+
+test_expect_success 'OPT_CMDMODE() detects incompatibility (3)' '
+ test_must_fail test-tool parse-options --mode2 --set23 >output 2>output.err &&
+ test_must_be_empty output &&
+ test_grep "mode2" output.err &&
+ test_grep "set23" output.err &&
+ test_grep "cannot be used together" output.err
+'
+
+test_expect_success 'OPT_CMDMODE() detects incompatibility (4)' '
+ test_must_fail test-tool parse-options --mode2 --mode34=3 \
+ >output 2>output.err &&
+ test_must_be_empty output &&
+ test_grep "mode2" output.err &&
+ test_grep "mode34.3" output.err &&
+ test_grep "cannot be used together" output.err
'
test_expect_success 'OPT_COUNTUP() with PARSE_OPT_NODASH works' '
@@ -424,4 +502,269 @@ test_expect_success '--end-of-options treats remainder as args' '
--end-of-options --verbose
'
+test_expect_success 'KEEP_DASHDASH works' '
+ test-tool parse-options-flags --keep-dashdash cmd --opt=1 -- --opt=2 --unknown >actual &&
+ cat >expect <<-\EOF &&
+ opt: 1
+ arg 00: --
+ arg 01: --opt=2
+ arg 02: --unknown
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'KEEP_ARGV0 works' '
+ test-tool parse-options-flags --keep-argv0 cmd arg0 --opt=3 >actual &&
+ cat >expect <<-\EOF &&
+ opt: 3
+ arg 00: cmd
+ arg 01: arg0
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'STOP_AT_NON_OPTION works' '
+ test-tool parse-options-flags --stop-at-non-option cmd --opt=4 arg0 --opt=5 --unknown >actual &&
+ cat >expect <<-\EOF &&
+ opt: 4
+ arg 00: arg0
+ arg 01: --opt=5
+ arg 02: --unknown
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'KEEP_UNKNOWN_OPT works' '
+ test-tool parse-options-flags --keep-unknown-opt cmd --unknown=1 --opt=6 -u2 >actual &&
+ cat >expect <<-\EOF &&
+ opt: 6
+ arg 00: --unknown=1
+ arg 01: -u2
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'NO_INTERNAL_HELP works for -h' '
+ test_expect_code 129 test-tool parse-options-flags --no-internal-help cmd -h 2>err &&
+ grep "^error: unknown switch \`h$SQ" err &&
+ grep "^usage: " err
+'
+
+for help_opt in help help-all
+do
+ test_expect_success "NO_INTERNAL_HELP works for --$help_opt" "
+ test_expect_code 129 test-tool parse-options-flags --no-internal-help cmd --$help_opt 2>err &&
+ grep '^error: unknown option \`'$help_opt\' err &&
+ grep '^usage: ' err
+ "
+done
+
+test_expect_success 'KEEP_UNKNOWN_OPT | NO_INTERNAL_HELP works' '
+ test-tool parse-options-flags --keep-unknown-opt --no-internal-help cmd -h --help --help-all >actual &&
+ cat >expect <<-\EOF &&
+ opt: 0
+ arg 00: -h
+ arg 01: --help
+ arg 02: --help-all
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'subcommand - no subcommand shows error and usage' '
+ test_expect_code 129 test-tool parse-subcommand cmd 2>err &&
+ grep "^error: need a subcommand" err &&
+ grep ^usage: err
+'
+
+test_expect_success 'subcommand - subcommand after -- shows error and usage' '
+ test_expect_code 129 test-tool parse-subcommand cmd -- subcmd-one 2>err &&
+ grep "^error: need a subcommand" err &&
+ grep ^usage: err
+'
+
+test_expect_success 'subcommand - subcommand after --end-of-options shows error and usage' '
+ test_expect_code 129 test-tool parse-subcommand cmd --end-of-options subcmd-one 2>err &&
+ grep "^error: need a subcommand" err &&
+ grep ^usage: err
+'
+
+test_expect_success 'subcommand - unknown subcommand shows error and usage' '
+ test_expect_code 129 test-tool parse-subcommand cmd nope 2>err &&
+ grep "^error: unknown subcommand: \`nope$SQ" err &&
+ grep ^usage: err
+'
+
+test_expect_success 'subcommand - subcommands cannot be abbreviated' '
+ test_expect_code 129 test-tool parse-subcommand cmd subcmd-o 2>err &&
+ grep "^error: unknown subcommand: \`subcmd-o$SQ$" err &&
+ grep ^usage: err
+'
+
+test_expect_success 'subcommand - no negated subcommands' '
+ test_expect_code 129 test-tool parse-subcommand cmd no-subcmd-one 2>err &&
+ grep "^error: unknown subcommand: \`no-subcmd-one$SQ" err &&
+ grep ^usage: err
+'
+
+test_expect_success 'subcommand - simple' '
+ test-tool parse-subcommand cmd subcmd-two >actual &&
+ cat >expect <<-\EOF &&
+ opt: 0
+ fn: subcmd_two
+ arg 00: subcmd-two
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'subcommand - stop parsing at the first subcommand' '
+ test-tool parse-subcommand cmd --opt=1 subcmd-two subcmd-one --opt=2 >actual &&
+ cat >expect <<-\EOF &&
+ opt: 1
+ fn: subcmd_two
+ arg 00: subcmd-two
+ arg 01: subcmd-one
+ arg 02: --opt=2
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'subcommand - KEEP_ARGV0' '
+ test-tool parse-subcommand --keep-argv0 cmd subcmd-two >actual &&
+ cat >expect <<-\EOF &&
+ opt: 0
+ fn: subcmd_two
+ arg 00: cmd
+ arg 01: subcmd-two
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'subcommand - SUBCOMMAND_OPTIONAL + subcommand not given' '
+ test-tool parse-subcommand --subcommand-optional cmd >actual &&
+ cat >expect <<-\EOF &&
+ opt: 0
+ fn: subcmd_one
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'subcommand - SUBCOMMAND_OPTIONAL + given subcommand' '
+ test-tool parse-subcommand --subcommand-optional cmd subcmd-two branch file >actual &&
+ cat >expect <<-\EOF &&
+ opt: 0
+ fn: subcmd_two
+ arg 00: subcmd-two
+ arg 01: branch
+ arg 02: file
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'subcommand - SUBCOMMAND_OPTIONAL + subcommand not given + unknown dashless args' '
+ test-tool parse-subcommand --subcommand-optional cmd branch file >actual &&
+ cat >expect <<-\EOF &&
+ opt: 0
+ fn: subcmd_one
+ arg 00: branch
+ arg 01: file
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'subcommand - SUBCOMMAND_OPTIONAL + subcommand not given + unknown option' '
+ test_expect_code 129 test-tool parse-subcommand --subcommand-optional cmd --subcommand-opt 2>err &&
+ grep "^error: unknown option" err &&
+ grep ^usage: err
+'
+
+test_expect_success 'subcommand - SUBCOMMAND_OPTIONAL | KEEP_UNKNOWN_OPT + subcommand not given + unknown option' '
+ test-tool parse-subcommand --subcommand-optional --keep-unknown-opt cmd --subcommand-opt >actual &&
+ cat >expect <<-\EOF &&
+ opt: 0
+ fn: subcmd_one
+ arg 00: --subcommand-opt
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'subcommand - SUBCOMMAND_OPTIONAL | KEEP_UNKNOWN_OPT + subcommand ignored after unknown option' '
+ test-tool parse-subcommand --subcommand-optional --keep-unknown-opt cmd --subcommand-opt subcmd-two >actual &&
+ cat >expect <<-\EOF &&
+ opt: 0
+ fn: subcmd_one
+ arg 00: --subcommand-opt
+ arg 01: subcmd-two
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'subcommand - SUBCOMMAND_OPTIONAL | KEEP_UNKNOWN_OPT + command and subcommand options cannot be mixed' '
+ test-tool parse-subcommand --subcommand-optional --keep-unknown-opt cmd --subcommand-opt branch --opt=1 >actual &&
+ cat >expect <<-\EOF &&
+ opt: 0
+ fn: subcmd_one
+ arg 00: --subcommand-opt
+ arg 01: branch
+ arg 02: --opt=1
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'subcommand - SUBCOMMAND_OPTIONAL | KEEP_UNKNOWN_OPT | KEEP_ARGV0' '
+ test-tool parse-subcommand --subcommand-optional --keep-unknown-opt --keep-argv0 cmd --subcommand-opt branch >actual &&
+ cat >expect <<-\EOF &&
+ opt: 0
+ fn: subcmd_one
+ arg 00: cmd
+ arg 01: --subcommand-opt
+ arg 02: branch
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'subcommand - SUBCOMMAND_OPTIONAL | KEEP_UNKNOWN_OPT | KEEP_DASHDASH' '
+ test-tool parse-subcommand --subcommand-optional --keep-unknown-opt --keep-dashdash cmd -- --subcommand-opt file >actual &&
+ cat >expect <<-\EOF &&
+ opt: 0
+ fn: subcmd_one
+ arg 00: --
+ arg 01: --subcommand-opt
+ arg 02: file
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'subcommand - completion helper' '
+ test-tool parse-subcommand cmd --git-completion-helper >actual &&
+ echo "subcmd-one subcmd-two --opt= --no-opt" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'subcommands are incompatible with STOP_AT_NON_OPTION' '
+ test_must_fail test-tool parse-subcommand --stop-at-non-option cmd subcmd-one 2>err &&
+ grep ^BUG err
+'
+
+test_expect_success 'subcommands are incompatible with KEEP_UNKNOWN_OPT unless in combination with SUBCOMMAND_OPTIONAL' '
+ test_must_fail test-tool parse-subcommand --keep-unknown-opt cmd subcmd-two 2>err &&
+ grep ^BUG err
+'
+
+test_expect_success 'subcommands are incompatible with KEEP_DASHDASH unless in combination with SUBCOMMAND_OPTIONAL' '
+ test_must_fail test-tool parse-subcommand --keep-dashdash cmd subcmd-two 2>err &&
+ grep ^BUG err
+'
+
+test_expect_success 'negative magnitude' '
+ test_must_fail test-tool parse-options --magnitude -1 >out 2>err &&
+ grep "non-negative integer" err &&
+ test_must_be_empty out
+'
+
+test_expect_success 'magnitude with units but no numbers' '
+ test_must_fail test-tool parse-options --magnitude m >out 2>err &&
+ grep "non-negative integer" err &&
+ test_must_be_empty out
+'
+
test_done
diff --git a/t/t0041-usage.sh b/t/t0041-usage.sh
index c4fc34e..1464294 100755
--- a/t/t0041-usage.sh
+++ b/t/t0041-usage.sh
@@ -5,6 +5,7 @@ test_description='Test commands behavior when given invalid argument value'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup ' '
@@ -20,8 +21,8 @@ test_expect_success 'tag --contains <existent_tag>' '
test_expect_success 'tag --contains <inexistent_tag>' '
test_must_fail git tag --contains "notag" >actual 2>actual.err &&
test_line_count = 0 actual &&
- test_i18ngrep "error" actual.err &&
- test_i18ngrep ! "usage" actual.err
+ test_grep "error" actual.err &&
+ test_grep ! "usage" actual.err
'
test_expect_success 'tag --no-contains <existent_tag>' '
@@ -33,27 +34,27 @@ test_expect_success 'tag --no-contains <existent_tag>' '
test_expect_success 'tag --no-contains <inexistent_tag>' '
test_must_fail git tag --no-contains "notag" >actual 2>actual.err &&
test_line_count = 0 actual &&
- test_i18ngrep "error" actual.err &&
- test_i18ngrep ! "usage" actual.err
+ test_grep "error" actual.err &&
+ test_grep ! "usage" actual.err
'
test_expect_success 'tag usage error' '
test_must_fail git tag --noopt >actual 2>actual.err &&
test_line_count = 0 actual &&
- test_i18ngrep "usage" actual.err
+ test_grep "usage" actual.err
'
test_expect_success 'branch --contains <existent_commit>' '
git branch --contains "main" >actual 2>actual.err &&
- test_i18ngrep "main" actual &&
+ test_grep "main" actual &&
test_line_count = 0 actual.err
'
test_expect_success 'branch --contains <inexistent_commit>' '
test_must_fail git branch --no-contains "nocommit" >actual 2>actual.err &&
test_line_count = 0 actual &&
- test_i18ngrep "error" actual.err &&
- test_i18ngrep ! "usage" actual.err
+ test_grep "error" actual.err &&
+ test_grep ! "usage" actual.err
'
test_expect_success 'branch --no-contains <existent_commit>' '
@@ -65,14 +66,14 @@ test_expect_success 'branch --no-contains <existent_commit>' '
test_expect_success 'branch --no-contains <inexistent_commit>' '
test_must_fail git branch --no-contains "nocommit" >actual 2>actual.err &&
test_line_count = 0 actual &&
- test_i18ngrep "error" actual.err &&
- test_i18ngrep ! "usage" actual.err
+ test_grep "error" actual.err &&
+ test_grep ! "usage" actual.err
'
test_expect_success 'branch usage error' '
test_must_fail git branch --noopt >actual 2>actual.err &&
test_line_count = 0 actual &&
- test_i18ngrep "usage" actual.err
+ test_grep "usage" actual.err
'
test_expect_success 'for-each-ref --contains <existent_object>' '
@@ -84,8 +85,8 @@ test_expect_success 'for-each-ref --contains <existent_object>' '
test_expect_success 'for-each-ref --contains <inexistent_object>' '
test_must_fail git for-each-ref --no-contains "noobject" >actual 2>actual.err &&
test_line_count = 0 actual &&
- test_i18ngrep "error" actual.err &&
- test_i18ngrep ! "usage" actual.err
+ test_grep "error" actual.err &&
+ test_grep ! "usage" actual.err
'
test_expect_success 'for-each-ref --no-contains <existent_object>' '
@@ -97,14 +98,14 @@ test_expect_success 'for-each-ref --no-contains <existent_object>' '
test_expect_success 'for-each-ref --no-contains <inexistent_object>' '
test_must_fail git for-each-ref --no-contains "noobject" >actual 2>actual.err &&
test_line_count = 0 actual &&
- test_i18ngrep "error" actual.err &&
- test_i18ngrep ! "usage" actual.err
+ test_grep "error" actual.err &&
+ test_grep ! "usage" actual.err
'
test_expect_success 'for-each-ref usage error' '
test_must_fail git for-each-ref --noopt >actual 2>actual.err &&
test_line_count = 0 actual &&
- test_i18ngrep "usage" actual.err
+ test_grep "usage" actual.err
'
test_done
diff --git a/t/t0050-filesystem.sh b/t/t0050-filesystem.sh
index afc343c..325eb1c 100755
--- a/t/t0050-filesystem.sh
+++ b/t/t0050-filesystem.sh
@@ -5,6 +5,7 @@ test_description='Various filesystem issues'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
auml=$(printf '\303\244')
@@ -104,7 +105,8 @@ test_expect_failure CASE_INSENSITIVE_FS 'add (with different case)' '
rm camelcase &&
echo 1 >CamelCase &&
git add CamelCase &&
- camel=$(git ls-files | grep -i camelcase) &&
+ git ls-files >tmp &&
+ camel=$(grep -i camelcase tmp) &&
test $(echo "$camel" | wc -l) = 1 &&
test "z$(git cat-file blob :$camel)" = z1
'
diff --git a/t/t0051-windows-named-pipe.sh b/t/t0051-windows-named-pipe.sh
index 10ac92d..412f413 100755
--- a/t/t0051-windows-named-pipe.sh
+++ b/t/t0051-windows-named-pipe.sh
@@ -3,8 +3,13 @@
test_description='Windows named pipes'
. ./test-lib.sh
+if ! test_have_prereq MINGW
+then
+ skip_all='skipping Windows-specific tests'
+ test_done
+fi
-test_expect_success MINGW 'o_append write to named pipe' '
+test_expect_success 'o_append write to named pipe' '
GIT_TRACE="$(pwd)/expect" git status >/dev/null 2>&1 &&
{ test-tool windows-named-pipe t0051 >actual 2>&1 & } &&
pid=$! &&
diff --git a/t/t0052-simple-ipc.sh b/t/t0052-simple-ipc.sh
index ff98be3..1a36a53 100755
--- a/t/t0052-simple-ipc.sh
+++ b/t/t0052-simple-ipc.sh
@@ -2,6 +2,7 @@
test_description='simple command server'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test-tool simple-ipc SUPPORTS_SIMPLE_IPC || {
diff --git a/t/t0055-beyond-symlinks.sh b/t/t0055-beyond-symlinks.sh
index 0c6ff56..c3eb115 100755
--- a/t/t0055-beyond-symlinks.sh
+++ b/t/t0055-beyond-symlinks.sh
@@ -2,6 +2,7 @@
test_description='update-index and add refuse to add beyond symlinks'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success SYMLINKS setup '
@@ -14,12 +15,22 @@ test_expect_success SYMLINKS setup '
test_expect_success SYMLINKS 'update-index --add beyond symlinks' '
test_must_fail git update-index --add c/d &&
- ! ( git ls-files | grep c/d )
+ cat >expect <<-\EOF &&
+ a
+ b/d
+ EOF
+ git ls-files >actual &&
+ test_cmp expect actual
'
test_expect_success SYMLINKS 'add beyond symlinks' '
test_must_fail git add c/d &&
- ! ( git ls-files | grep c/d )
+ cat >expect <<-\EOF &&
+ a
+ b/d
+ EOF
+ git ls-files >actual &&
+ test_cmp expect actual
'
test_done
diff --git a/t/t0056-git-C.sh b/t/t0056-git-C.sh
index 2630e75..752aa8c 100755
--- a/t/t0056-git-C.sh
+++ b/t/t0056-git-C.sh
@@ -2,6 +2,7 @@
test_description='"-C <path>" option and its effects on other path-related options'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success '"git -C <path>" runs git from the directory <path>' '
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 34d1061..0afa3d0 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -5,24 +5,32 @@
test_description='Test various path utilities'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
norm_path() {
expected=$(test-tool path-utils print_path "$2")
- test_expect_success $3 "normalize path: $1 => $2" \
- "test \"\$(test-tool path-utils normalize_path_copy '$1')\" = '$expected'"
+ test_expect_success $3 "normalize path: $1 => $2" "
+ echo '$expected' >expect &&
+ test-tool path-utils normalize_path_copy '$1' >actual &&
+ test_cmp expect actual
+ "
}
relative_path() {
expected=$(test-tool path-utils print_path "$3")
- test_expect_success $4 "relative path: $1 $2 => $3" \
- "test \"\$(test-tool path-utils relative_path '$1' '$2')\" = '$expected'"
+ test_expect_success $4 "relative path: $1 $2 => $3" "
+ echo '$expected' >expect &&
+ test-tool path-utils relative_path '$1' '$2' >actual &&
+ test_cmp expect actual
+ "
}
test_submodule_relative_url() {
test_expect_success "test_submodule_relative_url: $1 $2 $3 => $4" "
- actual=\$(git submodule--helper resolve-relative-url-test '$1' '$2' '$3') &&
- test \"\$actual\" = '$4'
+ echo '$4' >expect &&
+ test-tool submodule resolve-relative-url '$1' '$2' '$3' >actual &&
+ test_cmp expect actual
"
}
@@ -55,14 +63,19 @@ fi
ancestor() {
# We do some math with the expected ancestor length.
expected=$3
- if test -n "$rootoff" && test "x$expected" != x-1; then
- expected=$(($expected-$rootslash))
- test $expected -lt 0 ||
- expected=$(($expected+$rootoff))
- fi
- test_expect_success "longest ancestor: $1 $2 => $expected" \
- "actual=\$(test-tool path-utils longest_ancestor_length '$1' '$2') &&
- test \"\$actual\" = '$expected'"
+ case "$rootoff,$expected,$2" in
+ *,*,//*) ;; # leave UNC paths alone
+ [0-9]*,[0-9]*,/*)
+ # On Windows, expect MSYS2 pseudo root translation for
+ # Unix-style absolute paths
+ expected=$(($expected-$rootslash+$rootoff))
+ ;;
+ esac
+ test_expect_success $4 "longest ancestor: $1 $2 => $expected" "
+ echo '$expected' >expect &&
+ test-tool path-utils longest_ancestor_length '$1' '$2' >actual &&
+ test_cmp expect actual
+ "
}
# Some absolute path tests should be skipped on Windows due to path mangling
@@ -156,9 +169,16 @@ ancestor /foo/bar /foo 4
ancestor /foo/bar /foo:/bar 4
ancestor /foo/bar /bar -1
+# Windows-specific: DOS drives, network shares
+ancestor C:/Users/me C:/ 2 MINGW
+ancestor D:/Users/me C:/ -1 MINGW
+ancestor //server/share/my-directory //server/share/ 14 MINGW
+
test_expect_success 'strip_path_suffix' '
- test c:/msysgit = $(test-tool path-utils strip_path_suffix \
- c:/msysgit/libexec//git-core libexec/git-core)
+ echo c:/msysgit >expect &&
+ test-tool path-utils strip_path_suffix \
+ c:/msysgit/libexec//git-core libexec/git-core >actual &&
+ test_cmp expect actual
'
test_expect_success 'absolute path rejects the empty string' '
@@ -179,35 +199,61 @@ test_expect_success 'real path rejects the empty string' '
'
test_expect_success POSIX 'real path works on absolute paths 1' '
+ echo / >expect &&
+ test-tool path-utils real_path "/" >actual &&
+ test_cmp expect actual &&
+
nopath="hopefully-absent-path" &&
- test "/" = "$(test-tool path-utils real_path "/")" &&
- test "/$nopath" = "$(test-tool path-utils real_path "/$nopath")"
+ echo "/$nopath" >expect &&
+ test-tool path-utils real_path "/$nopath" >actual &&
+ test_cmp expect actual
'
test_expect_success 'real path works on absolute paths 2' '
- nopath="hopefully-absent-path" &&
# Find an existing top-level directory for the remaining tests:
d=$(pwd -P | sed -e "s|^\([^/]*/[^/]*\)/.*|\1|") &&
- test "$d" = "$(test-tool path-utils real_path "$d")" &&
- test "$d/$nopath" = "$(test-tool path-utils real_path "$d/$nopath")"
+ echo "$d" >expect &&
+ test-tool path-utils real_path "$d" >actual &&
+ test_cmp expect actual &&
+
+ nopath="hopefully-absent-path" &&
+ echo "$d/$nopath" >expect &&
+ test-tool path-utils real_path "$d/$nopath" >actual &&
+ test_cmp expect actual
'
test_expect_success POSIX 'real path removes extra leading slashes' '
+ echo "/" >expect &&
+ test-tool path-utils real_path "///" >actual &&
+ test_cmp expect actual &&
+
nopath="hopefully-absent-path" &&
- test "/" = "$(test-tool path-utils real_path "///")" &&
- test "/$nopath" = "$(test-tool path-utils real_path "///$nopath")" &&
+ echo "/$nopath" >expect &&
+ test-tool path-utils real_path "///$nopath" >actual &&
+ test_cmp expect actual &&
+
# Find an existing top-level directory for the remaining tests:
d=$(pwd -P | sed -e "s|^\([^/]*/[^/]*\)/.*|\1|") &&
- test "$d" = "$(test-tool path-utils real_path "//$d")" &&
- test "$d/$nopath" = "$(test-tool path-utils real_path "//$d/$nopath")"
+ echo "$d" >expect &&
+ test-tool path-utils real_path "//$d" >actual &&
+ test_cmp expect actual &&
+
+ echo "$d/$nopath" >expect &&
+ test-tool path-utils real_path "//$d/$nopath" >actual &&
+ test_cmp expect actual
'
test_expect_success 'real path removes other extra slashes' '
- nopath="hopefully-absent-path" &&
# Find an existing top-level directory for the remaining tests:
d=$(pwd -P | sed -e "s|^\([^/]*/[^/]*\)/.*|\1|") &&
- test "$d" = "$(test-tool path-utils real_path "$d///")" &&
- test "$d/$nopath" = "$(test-tool path-utils real_path "$d///$nopath")"
+ echo "$d" >expect &&
+ test-tool path-utils real_path "$d///" >actual &&
+ test_cmp expect actual &&
+
+ nopath="hopefully-absent-path" &&
+ echo "$d/$nopath" >expect &&
+ test-tool path-utils real_path "$d///$nopath" >actual &&
+ test_cmp expect actual
'
test_expect_success SYMLINKS 'real path works on symlinks' '
@@ -216,21 +262,31 @@ test_expect_success SYMLINKS 'real path works on symlinks' '
mkdir second &&
ln -s ../first second/other &&
mkdir third &&
- dir="$(cd .git; pwd -P)" &&
+ dir="$(cd .git && pwd -P)" &&
dir2=third/../second/other/.git &&
- test "$dir" = "$(test-tool path-utils real_path $dir2)" &&
+ echo "$dir" >expect &&
+ test-tool path-utils real_path $dir2 >actual &&
+ test_cmp expect actual &&
file="$dir"/index &&
- test "$file" = "$(test-tool path-utils real_path $dir2/index)" &&
+ echo "$file" >expect &&
+ test-tool path-utils real_path $dir2/index >actual &&
+ test_cmp expect actual &&
basename=blub &&
- test "$dir/$basename" = "$(cd .git && test-tool path-utils real_path "$basename")" &&
+ echo "$dir/$basename" >expect &&
+ test-tool -C .git path-utils real_path "$basename" >actual &&
+ test_cmp expect actual &&
ln -s ../first/file .git/syml &&
- sym="$(cd first; pwd -P)"/file &&
- test "$sym" = "$(test-tool path-utils real_path "$dir2/syml")"
+ sym="$(cd first && pwd -P)"/file &&
+ echo "$sym" >expect &&
+ test-tool path-utils real_path "$dir2/syml" >actual &&
+ test_cmp expect actual
'
test_expect_success SYMLINKS 'prefix_path works with absolute paths to work tree symlinks' '
ln -s target symlink &&
- test "$(test-tool path-utils prefix_path prefix "$(pwd)/symlink")" = "symlink"
+ echo "symlink" >expect &&
+ test-tool path-utils prefix_path prefix "$(pwd)/symlink" >actual &&
+ test_cmp expect actual
'
test_expect_success 'prefix_path works with only absolute path to work tree' '
@@ -246,7 +302,10 @@ test_expect_success 'prefix_path rejects absolute path to dir with same beginnin
test_expect_success SYMLINKS 'prefix_path works with absolute path to a symlink to work tree having same beginning as work tree' '
git init repo &&
ln -s repo repolink &&
- test "a" = "$(cd repo && test-tool path-utils prefix_path prefix "$(pwd)/../repolink/a")"
+ echo "a" >expect &&
+ repo_path="$(cd repo && pwd)" &&
+ test-tool -C repo path-utils prefix_path prefix "$repo_path/../repolink/a" >actual &&
+ test_cmp expect actual
'
relative_path /foo/a/b/c/ /foo/a/b/ c/
@@ -534,7 +593,7 @@ test_lazy_prereq CAN_EXEC_IN_PWD '
./git rev-parse
'
-test_expect_success RUNTIME_PREFIX,CAN_EXEC_IN_PWD 'RUNTIME_PREFIX works' '
+test_expect_success !VALGRIND,RUNTIME_PREFIX,CAN_EXEC_IN_PWD 'RUNTIME_PREFIX works' '
mkdir -p pretend/bin pretend/libexec/git-core &&
echo "echo HERE" | write_script pretend/libexec/git-core/git-here &&
cp "$GIT_EXEC_PATH"/git$X pretend/bin/ &&
@@ -542,7 +601,7 @@ test_expect_success RUNTIME_PREFIX,CAN_EXEC_IN_PWD 'RUNTIME_PREFIX works' '
echo HERE >expect &&
test_cmp expect actual'
-test_expect_success RUNTIME_PREFIX,CAN_EXEC_IN_PWD '%(prefix)/ works' '
+test_expect_success !VALGRIND,RUNTIME_PREFIX,CAN_EXEC_IN_PWD '%(prefix)/ works' '
mkdir -p pretend/bin &&
cp "$GIT_EXEC_PATH"/git$X pretend/bin/ &&
git config yes.path "%(prefix)/yes" &&
diff --git a/t/t0061-run-command.sh b/t/t0061-run-command.sh
index 7d59967..20986b6 100755
--- a/t/t0061-run-command.sh
+++ b/t/t0061-run-command.sh
@@ -5,6 +5,7 @@
test_description='Test run command'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
cat >hello-script <<-EOF
@@ -18,12 +19,12 @@ test_expect_success MINGW 'subprocess inherits only std handles' '
test_expect_success 'start_command reports ENOENT (slash)' '
test-tool run-command start-command-ENOENT ./does-not-exist 2>err &&
- test_i18ngrep "\./does-not-exist" err
+ test_grep "\./does-not-exist" err
'
test_expect_success 'start_command reports ENOENT (no slash)' '
test-tool run-command start-command-ENOENT does-not-exist 2>err &&
- test_i18ngrep "does-not-exist" err
+ test_grep "does-not-exist" err
'
test_expect_success 'run_command can run a command' '
@@ -48,7 +49,7 @@ test_expect_success !RUNS_COMMANDS_FROM_PWD 'run_command is restricted to PATH'
echo yikes
EOF
test_must_fail test-tool run-command run-command should-not-run 2>err &&
- test_i18ngrep "should-not-run" err
+ test_grep "should-not-run" err
'
test_expect_success !MINGW 'run_command can run a script without a #! line' '
@@ -129,20 +130,41 @@ World
EOF
test_expect_success 'run_command runs in parallel with more jobs available than tasks' '
- test-tool run-command run-command-parallel 5 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual &&
+ test-tool run-command run-command-parallel 5 sh -c "printf \"%s\n%s\n\" Hello World" >out 2>actual &&
+ test_must_be_empty out &&
test_cmp expect actual
'
+test_expect_success 'run_command runs ungrouped in parallel with more jobs available than tasks' '
+ test-tool run-command --ungroup run-command-parallel 5 sh -c "printf \"%s\n%s\n\" Hello World" >out 2>err &&
+ test_line_count = 8 out &&
+ test_line_count = 4 err
+'
+
test_expect_success 'run_command runs in parallel with as many jobs as tasks' '
- test-tool run-command run-command-parallel 4 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual &&
+ test-tool run-command run-command-parallel 4 sh -c "printf \"%s\n%s\n\" Hello World" >out 2>actual &&
+ test_must_be_empty out &&
test_cmp expect actual
'
+test_expect_success 'run_command runs ungrouped in parallel with as many jobs as tasks' '
+ test-tool run-command --ungroup run-command-parallel 4 sh -c "printf \"%s\n%s\n\" Hello World" >out 2>err &&
+ test_line_count = 8 out &&
+ test_line_count = 4 err
+'
+
test_expect_success 'run_command runs in parallel with more tasks than jobs available' '
- test-tool run-command run-command-parallel 3 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual &&
+ test-tool run-command run-command-parallel 3 sh -c "printf \"%s\n%s\n\" Hello World" >out 2>actual &&
+ test_must_be_empty out &&
test_cmp expect actual
'
+test_expect_success 'run_command runs ungrouped in parallel with more tasks than jobs available' '
+ test-tool run-command --ungroup run-command-parallel 3 sh -c "printf \"%s\n%s\n\" Hello World" >out 2>err &&
+ test_line_count = 8 out &&
+ test_line_count = 4 err
+'
+
cat >expect <<-EOF
preloaded output of a child
asking for a quick stop
@@ -153,19 +175,33 @@ asking for a quick stop
EOF
test_expect_success 'run_command is asked to abort gracefully' '
- test-tool run-command run-command-abort 3 false 2>actual &&
+ test-tool run-command run-command-abort 3 false >out 2>actual &&
+ test_must_be_empty out &&
test_cmp expect actual
'
+test_expect_success 'run_command is asked to abort gracefully (ungroup)' '
+ test-tool run-command --ungroup run-command-abort 3 false >out 2>err &&
+ test_must_be_empty out &&
+ test_line_count = 6 err
+'
+
cat >expect <<-EOF
no further jobs available
EOF
test_expect_success 'run_command outputs ' '
- test-tool run-command run-command-no-jobs 3 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual &&
+ test-tool run-command run-command-no-jobs 3 sh -c "printf \"%s\n%s\n\" Hello World" >out 2>actual &&
+ test_must_be_empty out &&
test_cmp expect actual
'
+test_expect_success 'run_command outputs (ungroup) ' '
+ test-tool run-command --ungroup run-command-no-jobs 3 sh -c "printf \"%s\n%s\n\" Hello World" >out 2>err &&
+ test_must_be_empty out &&
+ test_cmp expect err
+'
+
test_trace () {
expect="$1"
shift
diff --git a/t/t0062-revision-walking.sh b/t/t0062-revision-walking.sh
index 8e21586..b9480c8 100755
--- a/t/t0062-revision-walking.sh
+++ b/t/t0062-revision-walking.sh
@@ -5,6 +5,7 @@
test_description='Test revision walking api'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
cat >run_twice_expected <<-EOF
diff --git a/t/t0063-string-list.sh b/t/t0063-string-list.sh
index c6ee9f6..1fee6d9 100755
--- a/t/t0063-string-list.sh
+++ b/t/t0063-string-list.sh
@@ -5,6 +5,7 @@
test_description='Test string list functionality'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_split () {
@@ -17,6 +18,14 @@ test_split () {
"
}
+test_split_in_place() {
+ cat >expected &&
+ test_expect_success "split (in place) $1 at $2, max $3" "
+ test-tool string-list split_in_place '$1' '$2' '$3' >actual &&
+ test_cmp expected actual
+ "
+}
+
test_split "foo:bar:baz" ":" "-1" <<EOF
3
[0]: "foo"
@@ -60,6 +69,49 @@ test_split ":" ":" "-1" <<EOF
[1]: ""
EOF
+test_split_in_place "foo:;:bar:;:baz:;:" ":;" "-1" <<EOF
+10
+[0]: "foo"
+[1]: ""
+[2]: ""
+[3]: "bar"
+[4]: ""
+[5]: ""
+[6]: "baz"
+[7]: ""
+[8]: ""
+[9]: ""
+EOF
+
+test_split_in_place "foo:;:bar:;:baz" ":;" "0" <<EOF
+1
+[0]: "foo:;:bar:;:baz"
+EOF
+
+test_split_in_place "foo:;:bar:;:baz" ":;" "1" <<EOF
+2
+[0]: "foo"
+[1]: ";:bar:;:baz"
+EOF
+
+test_split_in_place "foo:;:bar:;:baz" ":;" "2" <<EOF
+3
+[0]: "foo"
+[1]: ""
+[2]: ":bar:;:baz"
+EOF
+
+test_split_in_place "foo:;:bar:;:" ":;" "-1" <<EOF
+7
+[0]: "foo"
+[1]: ""
+[2]: ""
+[3]: "bar"
+[4]: ""
+[5]: ""
+[6]: ""
+EOF
+
test_expect_success "test filter_string_list" '
test "x-" = "x$(test-tool string-list filter - y)" &&
test "x-" = "x$(test-tool string-list filter no y)" &&
diff --git a/t/t0064-oid-array.sh b/t/t0064-oid-array.sh
index 2e5438c..88c89e8 100755
--- a/t/t0064-oid-array.sh
+++ b/t/t0064-oid-array.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='basic tests for the oid array implementation'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
echoid () {
diff --git a/t/t0065-strcmp-offset.sh b/t/t0065-strcmp-offset.sh
index 91fa639..94e34c8 100755
--- a/t/t0065-strcmp-offset.sh
+++ b/t/t0065-strcmp-offset.sh
@@ -2,6 +2,7 @@
test_description='Test strcmp_offset functionality'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
while read s1 s2 expect
diff --git a/t/t0066-dir-iterator.sh b/t/t0066-dir-iterator.sh
index 92910e4..7d0a0da 100755
--- a/t/t0066-dir-iterator.sh
+++ b/t/t0066-dir-iterator.sh
@@ -2,6 +2,7 @@
test_description='Test the dir-iterator functionality'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -105,11 +106,7 @@ test_expect_success SYMLINKS 'setup dirs with symlinks' '
ln -s d dir4/a/e &&
ln -s ../b dir4/a/f &&
- mkdir -p dir5/a/b &&
- mkdir -p dir5/a/c &&
- ln -s ../c dir5/a/b/d &&
- ln -s ../ dir5/a/b/e &&
- ln -s ../../ dir5/a/b/f
+ ln -s dir4 dir5
'
test_expect_success SYMLINKS 'dir-iterator should not follow symlinks by default' '
@@ -128,21 +125,10 @@ test_expect_success SYMLINKS 'dir-iterator should not follow symlinks by default
test_cmp expected-no-follow-sorted-output actual-no-follow-sorted-output
'
-test_expect_success SYMLINKS 'dir-iterator should follow symlinks w/ follow flag' '
- cat >expected-follow-sorted-output <<-EOF &&
- [d] (a) [a] ./dir4/a
- [d] (a/f) [f] ./dir4/a/f
- [d] (a/f/c) [c] ./dir4/a/f/c
- [d] (b) [b] ./dir4/b
- [d] (b/c) [c] ./dir4/b/c
- [f] (a/d) [d] ./dir4/a/d
- [f] (a/e) [e] ./dir4/a/e
- EOF
-
- test-tool dir-iterator --follow-symlinks ./dir4 >out &&
- sort out >actual-follow-sorted-output &&
+test_expect_success SYMLINKS 'dir-iterator does not resolve top-level symlinks' '
+ test_must_fail test-tool dir-iterator ./dir5 >out &&
- test_cmp expected-follow-sorted-output actual-follow-sorted-output
+ grep "ENOTDIR" out
'
test_done
diff --git a/t/t0067-parse_pathspec_file.sh b/t/t0067-parse_pathspec_file.sh
index 7bab49f..0188d04 100755
--- a/t/t0067-parse_pathspec_file.sh
+++ b/t/t0067-parse_pathspec_file.sh
@@ -2,6 +2,7 @@
test_description='Test parse_pathspec_file()'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'one item from stdin' '
diff --git a/t/t0068-for-each-repo.sh b/t/t0068-for-each-repo.sh
index 4675e85..4b90b74 100755
--- a/t/t0068-for-each-repo.sh
+++ b/t/t0068-for-each-repo.sh
@@ -2,15 +2,18 @@
test_description='git for-each-repo builtin'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'run based on configured value' '
git init one &&
git init two &&
git init three &&
+ git init ~/four &&
git -C two commit --allow-empty -m "DID NOT RUN" &&
git config run.key "$TRASH_DIRECTORY/one" &&
git config --add run.key "$TRASH_DIRECTORY/three" &&
+ git config --add run.key "~/four" &&
git for-each-repo --config=run.key commit --allow-empty -m "ran" &&
git -C one log -1 --pretty=format:%s >message &&
grep ran message &&
@@ -18,12 +21,16 @@ test_expect_success 'run based on configured value' '
! grep ran message &&
git -C three log -1 --pretty=format:%s >message &&
grep ran message &&
+ git -C ~/four log -1 --pretty=format:%s >message &&
+ grep ran message &&
git for-each-repo --config=run.key -- commit --allow-empty -m "ran again" &&
git -C one log -1 --pretty=format:%s >message &&
grep again message &&
git -C two log -1 --pretty=format:%s >message &&
! grep again message &&
git -C three log -1 --pretty=format:%s >message &&
+ grep again message &&
+ git -C ~/four log -1 --pretty=format:%s >message &&
grep again message
'
@@ -33,4 +40,23 @@ test_expect_success 'do nothing on empty config' '
git for-each-repo --config=bogus.config -- help --no-such-option
'
+test_expect_success 'error on bad config keys' '
+ test_expect_code 129 git for-each-repo --config=a &&
+ test_expect_code 129 git for-each-repo --config=a.b. &&
+ test_expect_code 129 git for-each-repo --config="'\''.b"
+'
+
+test_expect_success 'error on NULL value for config keys' '
+ cat >>.git/config <<-\EOF &&
+ [empty]
+ key
+ EOF
+ cat >expect <<-\EOF &&
+ error: missing value for '\''empty.key'\''
+ EOF
+ test_expect_code 129 git for-each-repo --config=empty.key 2>actual.raw &&
+ grep ^error actual.raw >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t0069-oidtree.sh b/t/t0069-oidtree.sh
index bfb1397..889db50 100755
--- a/t/t0069-oidtree.sh
+++ b/t/t0069-oidtree.sh
@@ -1,6 +1,7 @@
#!/bin/sh
test_description='basic tests for the oidtree implementation'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
maxhexsz=$(test_oid hexsz)
@@ -27,7 +28,7 @@ test_expect_success 'oidtree insert and contains' '
EOF
{
echoid insert 444 1 2 3 4 5 a b c d e &&
- echoid contains 44 441 440 444 4440 4444
+ echoid contains 44 441 440 444 4440 4444 &&
echo clear
} | test-tool oidtree >actual &&
test_cmp expect actual
@@ -36,11 +37,11 @@ test_expect_success 'oidtree insert and contains' '
test_expect_success 'oidtree each' '
echoid "" 123 321 321 >expect &&
{
- echoid insert f 9 8 123 321 a b c d e
- echo each 12300
- echo each 3211
- echo each 3210
- echo each 32100
+ echoid insert f 9 8 123 321 a b c d e &&
+ echo each 12300 &&
+ echo each 3211 &&
+ echo each 3210 &&
+ echo each 32100 &&
echo clear
} | test-tool oidtree >actual &&
test_cmp expect actual
diff --git a/t/t0070-fundamental.sh b/t/t0070-fundamental.sh
index 8d59905..0ecec2b 100755
--- a/t/t0070-fundamental.sh
+++ b/t/t0070-fundamental.sh
@@ -6,12 +6,9 @@ test_description='check that the most basic functions work
Verify wrappers and compatibility functions.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
-test_expect_success 'character classes (isspace, isalpha etc.)' '
- test-tool ctype
-'
-
test_expect_success 'mktemp to nonexistent directory prints filename' '
test_must_fail test-tool mktemp doesnotexist/testXXXXXX 2>err &&
grep "doesnotexist/test" err
@@ -43,13 +40,71 @@ test_expect_success 'incomplete sideband messages are reassembled' '
test_expect_success 'eof on sideband message is reported' '
printf 1234 >input &&
test-tool pkt-line receive-sideband <input 2>err &&
- test_i18ngrep "unexpected disconnect" err
+ test_grep "unexpected disconnect" err
'
test_expect_success 'missing sideband designator is reported' '
printf 0004 >input &&
test-tool pkt-line receive-sideband <input 2>err &&
- test_i18ngrep "missing sideband" err
+ test_grep "missing sideband" err
+'
+
+test_expect_success 'unpack-sideband: --no-chomp-newline' '
+ test_when_finished "rm -f expect-out expect-err" &&
+ test-tool pkt-line send-split-sideband >split-sideband &&
+ test-tool pkt-line unpack-sideband \
+ --no-chomp-newline <split-sideband >out 2>err &&
+ cat >expect-out <<-EOF &&
+ primary: regular output
+ EOF
+ cat >expect-err <<-EOF &&
+ Foo.
+ Bar.
+ Hello, world!
+ EOF
+ test_cmp expect-out out &&
+ test_cmp expect-err err
+'
+
+test_expect_success 'unpack-sideband: --chomp-newline (default)' '
+ test_when_finished "rm -f expect-out expect-err" &&
+ test-tool pkt-line send-split-sideband >split-sideband &&
+ test-tool pkt-line unpack-sideband \
+ --chomp-newline <split-sideband >out 2>err &&
+ printf "primary: regular output" >expect-out &&
+ printf "Foo.Bar.Hello, world!" >expect-err &&
+ test_cmp expect-out out &&
+ test_cmp expect-err err
+'
+
+test_expect_success 'unpack-sideband: packet_reader_read() consumes sideband, no chomp payload' '
+ test_when_finished "rm -f expect-out expect-err" &&
+ test-tool pkt-line send-split-sideband >split-sideband &&
+ test-tool pkt-line unpack-sideband \
+ --reader-use-sideband \
+ --no-chomp-newline <split-sideband >out 2>err &&
+ cat >expect-out <<-EOF &&
+ primary: regular output
+ EOF
+ printf "remote: Foo. \n" >expect-err &&
+ printf "remote: Bar. \n" >>expect-err &&
+ printf "remote: Hello, world! \n" >>expect-err &&
+ test_cmp expect-out out &&
+ test_cmp expect-err err
+'
+
+test_expect_success 'unpack-sideband: packet_reader_read() consumes sideband, chomp payload' '
+ test_when_finished "rm -f expect-out expect-err" &&
+ test-tool pkt-line send-split-sideband >split-sideband &&
+ test-tool pkt-line unpack-sideband \
+ --reader-use-sideband \
+ --chomp-newline <split-sideband >out 2>err &&
+ printf "primary: regular output" >expect-out &&
+ printf "remote: Foo. \n" >expect-err &&
+ printf "remote: Bar. \n" >>expect-err &&
+ printf "remote: Hello, world! \n" >>expect-err &&
+ test_cmp expect-out out &&
+ test_cmp expect-err err
'
test_done
diff --git a/t/t0071-sort.sh b/t/t0071-sort.sh
new file mode 100755
index 0000000..ba8ad1d
--- /dev/null
+++ b/t/t0071-sort.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+test_description='verify sort functions'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'DEFINE_LIST_SORT_DEBUG' '
+ test-tool mergesort test
+'
+
+test_done
diff --git a/t/t0080-unit-test-output.sh b/t/t0080-unit-test-output.sh
new file mode 100755
index 0000000..6657c11
--- /dev/null
+++ b/t/t0080-unit-test-output.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+
+test_description='Test the output of the unit test framework'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'TAP output from unit tests' '
+ cat >expect <<-EOF &&
+ ok 1 - passing test
+ ok 2 - passing test and assertion return 1
+ # check "1 == 2" failed at t/unit-tests/t-basic.c:76
+ # left: 1
+ # right: 2
+ not ok 3 - failing test
+ ok 4 - failing test and assertion return 0
+ not ok 5 - passing TEST_TODO() # TODO
+ ok 6 - passing TEST_TODO() returns 1
+ # todo check ${SQ}check(x)${SQ} succeeded at t/unit-tests/t-basic.c:25
+ not ok 7 - failing TEST_TODO()
+ ok 8 - failing TEST_TODO() returns 0
+ # check "0" failed at t/unit-tests/t-basic.c:30
+ # skipping test - missing prerequisite
+ # skipping check ${SQ}1${SQ} at t/unit-tests/t-basic.c:32
+ ok 9 - test_skip() # SKIP
+ ok 10 - skipped test returns 1
+ # skipping test - missing prerequisite
+ ok 11 - test_skip() inside TEST_TODO() # SKIP
+ ok 12 - test_skip() inside TEST_TODO() returns 1
+ # check "0" failed at t/unit-tests/t-basic.c:48
+ not ok 13 - TEST_TODO() after failing check
+ ok 14 - TEST_TODO() after failing check returns 0
+ # check "0" failed at t/unit-tests/t-basic.c:56
+ not ok 15 - failing check after TEST_TODO()
+ ok 16 - failing check after TEST_TODO() returns 0
+ # check "!strcmp("\thello\\\\", "there\"\n")" failed at t/unit-tests/t-basic.c:61
+ # left: "\011hello\\\\"
+ # right: "there\"\012"
+ # check "!strcmp("NULL", NULL)" failed at t/unit-tests/t-basic.c:62
+ # left: "NULL"
+ # right: NULL
+ # check "${SQ}a${SQ} == ${SQ}\n${SQ}" failed at t/unit-tests/t-basic.c:63
+ # left: ${SQ}a${SQ}
+ # right: ${SQ}\012${SQ}
+ # check "${SQ}\\\\${SQ} == ${SQ}\\${SQ}${SQ}" failed at t/unit-tests/t-basic.c:64
+ # left: ${SQ}\\\\${SQ}
+ # right: ${SQ}\\${SQ}${SQ}
+ not ok 17 - messages from failing string and char comparison
+ # BUG: test has no checks at t/unit-tests/t-basic.c:91
+ not ok 18 - test with no checks
+ ok 19 - test with no checks returns 0
+ 1..19
+ EOF
+
+ ! "$GIT_BUILD_DIR"/t/unit-tests/bin/t-basic >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t0081-find-pack.sh b/t/t0081-find-pack.sh
new file mode 100755
index 0000000..67b1121
--- /dev/null
+++ b/t/t0081-find-pack.sh
@@ -0,0 +1,82 @@
+#!/bin/sh
+
+test_description='test `test-tool find-pack`'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit one &&
+ test_commit two &&
+ test_commit three &&
+ test_commit four &&
+ test_commit five
+'
+
+test_expect_success 'repack everything into a single packfile' '
+ git repack -a -d --no-write-bitmap-index &&
+
+ head_commit_pack=$(test-tool find-pack HEAD) &&
+ head_tree_pack=$(test-tool find-pack HEAD^{tree}) &&
+ one_pack=$(test-tool find-pack HEAD:one.t) &&
+ three_pack=$(test-tool find-pack HEAD:three.t) &&
+ old_commit_pack=$(test-tool find-pack HEAD~4) &&
+
+ test-tool find-pack --check-count 1 HEAD &&
+ test-tool find-pack --check-count=1 HEAD^{tree} &&
+ ! test-tool find-pack --check-count=0 HEAD:one.t &&
+ ! test-tool find-pack -c 2 HEAD:one.t &&
+ test-tool find-pack -c 1 HEAD:three.t &&
+
+ # Packfile exists at the right path
+ case "$head_commit_pack" in
+ ".git/objects/pack/pack-"*".pack") true ;;
+ *) false ;;
+ esac &&
+ test -f "$head_commit_pack" &&
+
+ # Everything is in the same pack
+ test "$head_commit_pack" = "$head_tree_pack" &&
+ test "$head_commit_pack" = "$one_pack" &&
+ test "$head_commit_pack" = "$three_pack" &&
+ test "$head_commit_pack" = "$old_commit_pack"
+'
+
+test_expect_success 'add more packfiles' '
+ git rev-parse HEAD^{tree} HEAD:two.t HEAD:four.t >objects &&
+ git pack-objects .git/objects/pack/mypackname1 >packhash1 <objects &&
+
+ git rev-parse HEAD~ HEAD~^{tree} HEAD:five.t >objects &&
+ git pack-objects .git/objects/pack/mypackname2 >packhash2 <objects &&
+
+ head_commit_pack=$(test-tool find-pack HEAD) &&
+
+ # HEAD^{tree} is in 2 packfiles
+ test-tool find-pack HEAD^{tree} >head_tree_packs &&
+ grep "$head_commit_pack" head_tree_packs &&
+ grep mypackname1 head_tree_packs &&
+ ! grep mypackname2 head_tree_packs &&
+ test-tool find-pack --check-count 2 HEAD^{tree} &&
+ ! test-tool find-pack --check-count 1 HEAD^{tree} &&
+
+ # HEAD:five.t is also in 2 packfiles
+ test-tool find-pack HEAD:five.t >five_packs &&
+ grep "$head_commit_pack" five_packs &&
+ ! grep mypackname1 five_packs &&
+ grep mypackname2 five_packs &&
+ test-tool find-pack -c 2 HEAD:five.t &&
+ ! test-tool find-pack --check-count=0 HEAD:five.t
+'
+
+test_expect_success 'add more commits (as loose objects)' '
+ test_commit six &&
+ test_commit seven &&
+
+ test -z "$(test-tool find-pack HEAD)" &&
+ test -z "$(test-tool find-pack HEAD:six.t)" &&
+ test-tool find-pack --check-count 0 HEAD &&
+ test-tool find-pack -c 0 HEAD:six.t &&
+ ! test-tool find-pack -c 1 HEAD:seven.t
+'
+
+test_done
diff --git a/t/t0090-cache-tree.sh b/t/t0090-cache-tree.sh
index 9bf66c9..d8e2fc4 100755
--- a/t/t0090-cache-tree.sh
+++ b/t/t0090-cache-tree.sh
@@ -5,6 +5,8 @@ test_description="Test whether cache-tree is properly updated
Tests whether various commands properly update and/or rewrite the
cache-tree extension.
"
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
cmp_cache_tree () {
@@ -195,6 +197,7 @@ test_expect_success 'reset --hard gives cache-tree' '
test_expect_success 'reset --hard without index gives cache-tree' '
rm -f .git/index &&
+ git clean -fd &&
git reset --hard &&
test_cache_tree
'
diff --git a/t/t0091-bugreport.sh b/t/t0091-bugreport.sh
index 526304f..fca3904 100755
--- a/t/t0091-bugreport.sh
+++ b/t/t0091-bugreport.sh
@@ -2,31 +2,53 @@
test_description='git bugreport'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
-# Headers "[System Info]" will be followed by a non-empty line if we put some
-# information there; we can make sure all our headers were followed by some
-# information to check if the command was successful.
-HEADER_PATTERN="^\[.*\]$"
-
-check_all_headers_populated () {
- while read -r line
- do
- if test "$(grep "$HEADER_PATTERN" "$line")"
- then
- echo "$line"
- read -r nextline
- if test -z "$nextline"; then
- return 1;
- fi
- fi
- done
-}
-
-test_expect_success 'creates a report with content in the right places' '
- test_when_finished rm git-bugreport-check-headers.txt &&
- git bugreport -s check-headers &&
- check_all_headers_populated <git-bugreport-check-headers.txt
+test_expect_success 'create a report' '
+ git bugreport -s format &&
+ test_file_not_empty git-bugreport-format.txt
+'
+
+test_expect_success 'report contains wanted template (before first section)' '
+ sed -ne "/^\[/q;p" git-bugreport-format.txt >actual &&
+ cat >expect <<-\EOF &&
+ Thank you for filling out a Git bug report!
+ Please answer the following questions to help us understand your issue.
+
+ What did you do before the bug happened? (Steps to reproduce your issue)
+
+ What did you expect to happen? (Expected behavior)
+
+ What happened instead? (Actual behavior)
+
+ What'\''s different between what you expected and what actually happened?
+
+ Anything else you want to add:
+
+ Please review the rest of the bug report below.
+ You can delete any lines you don'\''t wish to share.
+
+
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'sanity check "System Info" section' '
+ test_when_finished rm -f git-bugreport-format.txt &&
+
+ sed -ne "/^\[System Info\]$/,/^$/p" <git-bugreport-format.txt >system &&
+
+ # The beginning should match "git version --build-options" verbatim,
+ # but rather than checking bit-for-bit equality, just test some basics.
+ grep "git version " system &&
+ grep "shell-path: ." system &&
+
+ # After the version, there should be some more info.
+ # This is bound to differ from environment to environment,
+ # so we just do some rather high-level checks.
+ grep "uname: ." system &&
+ grep "compiler info: ." system
'
test_expect_success 'dies if file with same name as report already exists' '
@@ -43,7 +65,14 @@ test_expect_success '--output-directory puts the report in the provided dir' '
test_expect_success 'incorrect arguments abort with usage' '
test_must_fail git bugreport --false 2>output &&
- test_i18ngrep usage output &&
+ test_grep usage output &&
+ test_path_is_missing git-bugreport-*
+'
+
+test_expect_success 'incorrect positional arguments abort with usage and hint' '
+ test_must_fail git bugreport false 2>output &&
+ test_grep usage output &&
+ test_grep false output &&
test_path_is_missing git-bugreport-*
'
@@ -59,18 +88,70 @@ test_expect_success 'can create leading directories outside of a git dir' '
test_expect_success 'indicates populated hooks' '
test_when_finished rm git-bugreport-hooks.txt &&
- test_when_finished rm -fr .git/hooks &&
- rm -fr .git/hooks &&
- mkdir .git/hooks &&
- for hook in applypatch-msg prepare-commit-msg.sample
- do
- write_script ".git/hooks/$hook" <<-EOF || return 1
- echo "hook $hook exists"
- EOF
- done &&
+
+ test_hook applypatch-msg <<-\EOF &&
+ true
+ EOF
+ test_hook unknown-hook <<-\EOF &&
+ true
+ EOF
git bugreport -s hooks &&
- grep applypatch-msg git-bugreport-hooks.txt &&
- ! grep prepare-commit-msg git-bugreport-hooks.txt
+
+ sort >expect <<-\EOF &&
+ [Enabled Hooks]
+ applypatch-msg
+ EOF
+
+ sed -ne "/^\[Enabled Hooks\]$/,/^$/p" <git-bugreport-hooks.txt >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success UNZIP '--diagnose creates diagnostics zip archive' '
+ test_when_finished rm -rf report &&
+
+ git bugreport --diagnose -o report -s test >out &&
+
+ zip_path=report/git-diagnostics-test.zip &&
+ grep "Available space" out &&
+ test_path_is_file "$zip_path" &&
+
+ # Check zipped archive content
+ "$GIT_UNZIP" -p "$zip_path" diagnostics.log >out &&
+ test_file_not_empty out &&
+
+ "$GIT_UNZIP" -p "$zip_path" packs-local.txt >out &&
+ grep ".git/objects" out &&
+
+ "$GIT_UNZIP" -p "$zip_path" objects-local.txt >out &&
+ grep "^Total: [0-9][0-9]*" out &&
+
+ # Should not include .git directory contents by default
+ ! "$GIT_UNZIP" -l "$zip_path" | grep ".git/"
+'
+
+test_expect_success UNZIP '--diagnose=stats excludes .git dir contents' '
+ test_when_finished rm -rf report &&
+
+ git bugreport --diagnose=stats -o report -s test >out &&
+
+ # Includes pack quantity/size info
+ "$GIT_UNZIP" -p "$zip_path" packs-local.txt >out &&
+ grep ".git/objects" out &&
+
+ # Does not include .git directory contents
+ ! "$GIT_UNZIP" -l "$zip_path" | grep ".git/"
+'
+
+test_expect_success UNZIP '--diagnose=all includes .git dir contents' '
+ test_when_finished rm -rf report &&
+
+ git bugreport --diagnose=all -o report -s test >out &&
+
+ # Includes .git directory contents
+ "$GIT_UNZIP" -l "$zip_path" | grep ".git/" &&
+
+ "$GIT_UNZIP" -p "$zip_path" .git/HEAD >out &&
+ test_file_not_empty out
'
test_done
diff --git a/t/t0092-diagnose.sh b/t/t0092-diagnose.sh
new file mode 100755
index 0000000..133e574
--- /dev/null
+++ b/t/t0092-diagnose.sh
@@ -0,0 +1,72 @@
+#!/bin/sh
+
+test_description='git diagnose'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success UNZIP 'creates diagnostics zip archive' '
+ test_when_finished rm -rf report &&
+
+ git diagnose -o report -s test >out &&
+ grep "Available space" out &&
+
+ zip_path=report/git-diagnostics-test.zip &&
+ test_path_is_file "$zip_path" &&
+
+ # Check zipped archive content
+ "$GIT_UNZIP" -p "$zip_path" diagnostics.log >out &&
+ test_file_not_empty out &&
+
+ "$GIT_UNZIP" -p "$zip_path" packs-local.txt >out &&
+ grep ".git/objects" out &&
+
+ "$GIT_UNZIP" -p "$zip_path" objects-local.txt >out &&
+ grep "^Total: [0-9][0-9]*" out &&
+
+ # Should not include .git directory contents by default
+ ! "$GIT_UNZIP" -l "$zip_path" | grep ".git/"
+'
+
+test_expect_success UNZIP 'counts loose objects' '
+ test_commit A &&
+
+ # After committing, should have non-zero loose objects
+ git diagnose -o test-count -s 1 >out &&
+ zip_path=test-count/git-diagnostics-1.zip &&
+ "$GIT_UNZIP" -p "$zip_path" objects-local.txt >out &&
+ grep "^Total: [1-9][0-9]* loose objects" out
+'
+
+test_expect_success UNZIP '--mode=stats excludes .git dir contents' '
+ test_when_finished rm -rf report &&
+
+ git diagnose -o report -s test --mode=stats >out &&
+
+ # Includes pack quantity/size info
+ zip_path=report/git-diagnostics-test.zip &&
+ "$GIT_UNZIP" -p "$zip_path" packs-local.txt >out &&
+ grep ".git/objects" out &&
+
+ # Does not include .git directory contents
+ ! "$GIT_UNZIP" -l "$zip_path" | grep ".git/"
+'
+
+test_expect_success UNZIP '--mode=all includes .git dir contents' '
+ test_when_finished rm -rf report &&
+
+ git diagnose -o report -s test --mode=all >out &&
+
+ # Includes pack quantity/size info
+ zip_path=report/git-diagnostics-test.zip &&
+ "$GIT_UNZIP" -p "$zip_path" packs-local.txt >out &&
+ grep ".git/objects" out &&
+
+ # Includes .git directory contents
+ "$GIT_UNZIP" -l "$zip_path" | grep ".git/" &&
+
+ "$GIT_UNZIP" -p "$zip_path" .git/HEAD >out &&
+ test_file_not_empty out
+'
+
+test_done
diff --git a/t/t0095-bloom.sh b/t/t0095-bloom.sh
index 7e4ab17..b567383 100755
--- a/t/t0095-bloom.sh
+++ b/t/t0095-bloom.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='Testing the various Bloom filter computations in bloom.c'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'compute unseeded murmur3 hash for empty string' '
@@ -67,7 +69,7 @@ test_expect_success 'compute bloom key for test string 2' '
test_cmp expect actual
'
-test_expect_success 'get bloom filters for commit with no changes' '
+test_expect_success !SANITIZE_LEAK 'get bloom filters for commit with no changes' '
git init &&
git commit --allow-empty -m "c0" &&
cat >expect <<-\EOF &&
@@ -84,7 +86,7 @@ test_expect_success 'get bloom filter for commit with 10 changes' '
mkdir smallDir &&
for i in $(test_seq 0 9)
do
- echo $i >smallDir/$i
+ echo $i >smallDir/$i || return 1
done &&
git add smallDir &&
git commit -m "commit with 10 changes" &&
@@ -102,7 +104,7 @@ test_expect_success EXPENSIVE 'get bloom filter for commit with 513 changes' '
mkdir bigDir &&
for i in $(test_seq 0 511)
do
- echo $i >bigDir/$i
+ echo $i >bigDir/$i || return 1
done &&
git add bigDir &&
git commit -m "commit with 513 changes" &&
diff --git a/t/t0100-previous.sh b/t/t0100-previous.sh
index 69beb59..70a3223 100755
--- a/t/t0100-previous.sh
+++ b/t/t0100-previous.sh
@@ -5,13 +5,16 @@ test_description='previous branch syntax @{-n}'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'branch -d @{-1}' '
test_commit A &&
git checkout -b junk &&
git checkout - &&
- test "$(git symbolic-ref HEAD)" = refs/heads/main &&
+ echo refs/heads/main >expect &&
+ git symbolic-ref HEAD >actual &&
+ test_cmp expect actual &&
git branch -d @{-1} &&
test_must_fail git rev-parse --verify refs/heads/junk
'
@@ -20,7 +23,9 @@ test_expect_success 'branch -d @{-12} when there is not enough switches yet' '
git reflog expire --expire=now &&
git checkout -b junk2 &&
git checkout - &&
- test "$(git symbolic-ref HEAD)" = refs/heads/main &&
+ echo refs/heads/main >expect &&
+ git symbolic-ref HEAD >actual &&
+ test_cmp expect actual &&
test_must_fail git branch -d @{-12} &&
git rev-parse --verify refs/heads/main
'
diff --git a/t/t0101-at-syntax.sh b/t/t0101-at-syntax.sh
index a1998b5..878aadd 100755
--- a/t/t0101-at-syntax.sh
+++ b/t/t0101-at-syntax.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='various @{whatever} syntax tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t0110-urlmatch-normalization.sh b/t/t0110-urlmatch-normalization.sh
index f99529d..12d817f 100755
--- a/t/t0110-urlmatch-normalization.sh
+++ b/t/t0110-urlmatch-normalization.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='urlmatch URL normalization'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# The base name of the test url files
@@ -47,7 +49,7 @@ test_expect_success 'url authority' '
test-tool urlmatch-normalization "scheme://@host" &&
test-tool urlmatch-normalization "scheme://%00@host" &&
! test-tool urlmatch-normalization "scheme://%%@host" &&
- ! test-tool urlmatch-normalization "scheme://host_" &&
+ test-tool urlmatch-normalization "scheme://host_" &&
test-tool urlmatch-normalization "scheme://user:pass@host/" &&
test-tool urlmatch-normalization "scheme://@host/" &&
test-tool urlmatch-normalization "scheme://host/" &&
diff --git a/t/t0200-gettext-basic.sh b/t/t0200-gettext-basic.sh
index 8853d8a..522fb2a 100755
--- a/t/t0200-gettext-basic.sh
+++ b/t/t0200-gettext-basic.sh
@@ -5,6 +5,7 @@
test_description='Gettext support for Git'
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-gettext.sh
test_expect_success "sanity: \$GIT_INTERNAL_GETTEXT_SH_SCHEME is set (to $GIT_INTERNAL_GETTEXT_SH_SCHEME)" '
diff --git a/t/t0201-gettext-fallbacks.sh b/t/t0201-gettext-fallbacks.sh
index 6c74df0..8724ce1 100755
--- a/t/t0201-gettext-fallbacks.sh
+++ b/t/t0201-gettext-fallbacks.sh
@@ -8,6 +8,7 @@ test_description='Gettext Shell fallbacks'
GIT_INTERNAL_GETTEXT_TEST_FALLBACKS=YesPlease
export GIT_INTERNAL_GETTEXT_TEST_FALLBACKS
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-gettext.sh
test_expect_success "sanity: \$GIT_INTERNAL_GETTEXT_SH_SCHEME is set (to $GIT_INTERNAL_GETTEXT_SH_SCHEME)" '
diff --git a/t/t0202-gettext-perl.sh b/t/t0202-gettext-perl.sh
index a29d166..5a6f280 100755
--- a/t/t0202-gettext-perl.sh
+++ b/t/t0202-gettext-perl.sh
@@ -5,23 +5,14 @@
test_description='Perl gettext interface (Git::I18N)'
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-gettext.sh
+. "$TEST_DIRECTORY"/lib-perl.sh
+skip_all_if_no_Test_More
-if ! test_have_prereq PERL; then
- skip_all='skipping perl interface tests, perl not available'
- test_done
-fi
-
-perl -MTest::More -e 0 2>/dev/null || {
- skip_all="Perl Test::More unavailable, skipping test"
- test_done
-}
-
-# The external test will outputs its own plan
-test_external_has_tap=1
-
-test_external_without_stderr \
- 'Perl Git::I18N API' \
- perl "$TEST_DIRECTORY"/t0202/test.pl
+test_expect_success 'run t0202/test.pl to test Git::I18N.pm' '
+ "$PERL_PATH" "$TEST_DIRECTORY"/t0202/test.pl 2>stderr &&
+ test_must_be_empty stderr
+'
test_done
diff --git a/t/t0202/test.pl b/t/t0202/test.pl
index 2cbf7b9..47d96a2 100755
--- a/t/t0202/test.pl
+++ b/t/t0202/test.pl
@@ -1,5 +1,5 @@
#!/usr/bin/perl
-use 5.008;
+use 5.008001;
use lib (split(/:/, $ENV{GITPERLLIB}));
use strict;
use warnings;
diff --git a/t/t0203-gettext-setlocale-sanity.sh b/t/t0203-gettext-setlocale-sanity.sh
index 0ce1f22..86cff32 100755
--- a/t/t0203-gettext-setlocale-sanity.sh
+++ b/t/t0203-gettext-setlocale-sanity.sh
@@ -5,6 +5,7 @@
test_description="The Git C functions aren't broken by setlocale(3)"
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-gettext.sh
test_expect_success 'git show a ISO-8859-1 commit under C locale' '
diff --git a/t/t0204-gettext-reencode-sanity.sh b/t/t0204-gettext-reencode-sanity.sh
index 8437e51..310a450 100755
--- a/t/t0204-gettext-reencode-sanity.sh
+++ b/t/t0204-gettext-reencode-sanity.sh
@@ -5,6 +5,7 @@
test_description="Gettext reencoding of our *.po/*.mo files works"
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-gettext.sh
# The constants used in a tricky observation for undefined behaviour
@@ -81,7 +82,7 @@ test_expect_success GETTEXT_ISO_LOCALE 'gettext.c: git init UTF-8 -> ISO-8859-1'
printf "Bjó til tóma Git lind" >expect &&
LANGUAGE=is LC_ALL="$is_IS_iso_locale" git init repo >actual &&
test_when_finished "rm -rf repo" &&
- grep "^$(cat expect | iconv -f UTF-8 -t ISO8859-1) " actual
+ grep "^$(iconv -f UTF-8 -t ISO8859-1 <expect) " actual
'
test_done
diff --git a/t/t0210-trace2-normal.sh b/t/t0210-trace2-normal.sh
index 0cf3a63..c312657 100755
--- a/t/t0210-trace2-normal.sh
+++ b/t/t0210-trace2-normal.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test trace2 facility (normal target)'
+
+TEST_PASSES_SANITIZE_LEAK=false
. ./test-lib.sh
# Turn off any inherited trace2 settings for this test.
@@ -166,6 +168,82 @@ test_expect_success 'BUG messages are written to trace2' '
test_cmp expect actual
'
+test_expect_success 'bug messages with BUG_if_bug() are written to trace2' '
+ test_when_finished "rm trace.normal actual expect" &&
+ test_expect_code 99 env GIT_TRACE2="$(pwd)/trace.normal" \
+ test-tool trace2 008bug 2>err &&
+ cat >expect <<-\EOF &&
+ a bug message
+ another bug message
+ an explicit BUG_if_bug() following bug() call(s) is nice, but not required
+ EOF
+ sed "s/^.*: //" <err >actual &&
+ test_cmp expect actual &&
+
+ perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ cat >expect <<-EOF &&
+ version $V
+ start _EXE_ trace2 008bug
+ cmd_name trace2 (trace2)
+ error a bug message
+ error another bug message
+ error an explicit BUG_if_bug() following bug() call(s) is nice, but not required
+ exit elapsed:_TIME_ code:99
+ atexit elapsed:_TIME_ code:99
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'bug messages without explicit BUG_if_bug() are written to trace2' '
+ test_when_finished "rm trace.normal actual expect" &&
+ test_expect_code 99 env GIT_TRACE2="$(pwd)/trace.normal" \
+ test-tool trace2 009bug_BUG 2>err &&
+ cat >expect <<-\EOF &&
+ a bug message
+ another bug message
+ had bug() call(s) in this process without explicit BUG_if_bug()
+ EOF
+ sed "s/^.*: //" <err >actual &&
+ test_cmp expect actual &&
+
+ perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ cat >expect <<-EOF &&
+ version $V
+ start _EXE_ trace2 009bug_BUG
+ cmd_name trace2 (trace2)
+ error a bug message
+ error another bug message
+ error on exit(): had bug() call(s) in this process without explicit BUG_if_bug()
+ exit elapsed:_TIME_ code:99
+ atexit elapsed:_TIME_ code:99
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'bug messages followed by BUG() are written to trace2' '
+ test_when_finished "rm trace.normal actual expect" &&
+ test_expect_code 99 env GIT_TRACE2="$(pwd)/trace.normal" \
+ test-tool trace2 010bug_BUG 2>err &&
+ cat >expect <<-\EOF &&
+ a bug message
+ a BUG message
+ EOF
+ sed "s/^.*: //" <err >actual &&
+ test_cmp expect actual &&
+
+ perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ cat >expect <<-EOF &&
+ version $V
+ start _EXE_ trace2 010bug_BUG
+ cmd_name trace2 (trace2)
+ error a bug message
+ error a BUG message
+ exit elapsed:_TIME_ code:99
+ atexit elapsed:_TIME_ code:99
+ EOF
+ test_cmp expect actual
+'
+
sane_unset GIT_TRACE2_BRIEF
# Now test without environment variables and get all Trace2 settings
@@ -205,4 +283,22 @@ test_expect_success 'using global config with include' '
test_cmp expect actual
'
+test_expect_success 'unsafe URLs are redacted by default' '
+ test_when_finished \
+ "rm -r trace.normal unredacted.normal clone clone2" &&
+
+ test_config_global \
+ "url.$(pwd).insteadOf" https://user:pwd@example.com/ &&
+ test_config_global trace2.configParams "core.*,remote.*.url" &&
+
+ GIT_TRACE2="$(pwd)/trace.normal" \
+ git clone https://user:pwd@example.com/ clone &&
+ ! grep user:pwd trace.normal &&
+
+ GIT_TRACE2_REDACT=0 GIT_TRACE2="$(pwd)/unredacted.normal" \
+ git clone https://user:pwd@example.com/ clone2 &&
+ grep "start .* clone https://user:pwd@example.com" unredacted.normal &&
+ grep "remote.origin.url=https://user:pwd@example.com" unredacted.normal
+'
+
test_done
diff --git a/t/t0211-trace2-perf.sh b/t/t0211-trace2-perf.sh
index 6ee8ee3..13ef69b 100755
--- a/t/t0211-trace2-perf.sh
+++ b/t/t0211-trace2-perf.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test trace2 facility (perf target)'
+
+TEST_PASSES_SANITIZE_LEAK=false
. ./test-lib.sh
# Turn off any inherited trace2 settings for this test.
@@ -171,4 +173,349 @@ test_expect_success 'using global config, perf stream, return code 0' '
test_cmp expect actual
'
+# Exercise the stopwatch timers in a loop and confirm that we have
+# as many start/stop intervals as expected. We cannot really test the
+# actual (total, min, max) timer values, so we have to assume that they
+# are good, but we can verify the interval count.
+#
+# The timer "test/test1" should only emit a global summary "timer" event.
+# The timer "test/test2" should emit per-thread "th_timer" events and a
+# global summary "timer" event.
+
+have_timer_event () {
+ thread=$1 event=$2 category=$3 name=$4 intervals=$5 file=$6 &&
+
+ pattern="d0|${thread}|${event}||||${category}|name:${name} intervals:${intervals}" &&
+
+ grep "${pattern}" ${file}
+}
+
+test_expect_success 'stopwatch timer test/test1' '
+ test_when_finished "rm trace.perf actual" &&
+ test_config_global trace2.perfBrief 1 &&
+ test_config_global trace2.perfTarget "$(pwd)/trace.perf" &&
+
+ # Use the timer "test1" 5 times from "main".
+ test-tool trace2 100timer 5 10 &&
+
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
+
+ have_timer_event "main" "timer" "test" "test1" 5 actual
+'
+
+test_expect_success PTHREADS 'stopwatch timer test/test2' '
+ test_when_finished "rm trace.perf actual" &&
+ test_config_global trace2.perfBrief 1 &&
+ test_config_global trace2.perfTarget "$(pwd)/trace.perf" &&
+
+ # Use the timer "test2" 5 times each in 3 threads.
+ test-tool trace2 101timer 5 10 3 &&
+
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
+
+ # So we should have 3 per-thread events of 5 each.
+ have_timer_event "th01:ut_101" "th_timer" "test" "test2" 5 actual &&
+ have_timer_event "th02:ut_101" "th_timer" "test" "test2" 5 actual &&
+ have_timer_event "th03:ut_101" "th_timer" "test" "test2" 5 actual &&
+
+ # And we should have 15 total uses.
+ have_timer_event "main" "timer" "test" "test2" 15 actual
+'
+
+# Exercise the global counters and confirm that we get the expected values.
+#
+# The counter "test/test1" should only emit a global summary "counter" event.
+# The counter "test/test2" could emit per-thread "th_counter" events and a
+# global summary "counter" event.
+
+have_counter_event () {
+ thread=$1 event=$2 category=$3 name=$4 value=$5 file=$6 &&
+
+ pattern="d0|${thread}|${event}||||${category}|name:${name} value:${value}" &&
+
+ grep "${patern}" ${file}
+}
+
+test_expect_success 'global counter test/test1' '
+ test_when_finished "rm trace.perf actual" &&
+ test_config_global trace2.perfBrief 1 &&
+ test_config_global trace2.perfTarget "$(pwd)/trace.perf" &&
+
+ # Use the counter "test1" and add n integers.
+ test-tool trace2 200counter 1 2 3 4 5 &&
+
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
+
+ have_counter_event "main" "counter" "test" "test1" 15 actual
+'
+
+test_expect_success PTHREADS 'global counter test/test2' '
+ test_when_finished "rm trace.perf actual" &&
+ test_config_global trace2.perfBrief 1 &&
+ test_config_global trace2.perfTarget "$(pwd)/trace.perf" &&
+
+ # Add 2 integers to the counter "test2" in each of 3 threads.
+ test-tool trace2 201counter 7 13 3 &&
+
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
+
+ # So we should have 3 per-thread events of 5 each.
+ have_counter_event "th01:ut_201" "th_counter" "test" "test2" 20 actual &&
+ have_counter_event "th02:ut_201" "th_counter" "test" "test2" 20 actual &&
+ have_counter_event "th03:ut_201" "th_counter" "test" "test2" 20 actual &&
+
+ # And we should have a single event with the total across all threads.
+ have_counter_event "main" "counter" "test" "test2" 60 actual
+'
+
+test_expect_success 'unsafe URLs are redacted by default' '
+ test_when_finished \
+ "rm -r actual trace.perf unredacted.perf clone clone2" &&
+
+ test_config_global \
+ "url.$(pwd).insteadOf" https://user:pwd@example.com/ &&
+ test_config_global trace2.configParams "core.*,remote.*.url" &&
+
+ GIT_TRACE2_PERF="$(pwd)/trace.perf" \
+ git clone https://user:pwd@example.com/ clone &&
+ ! grep user:pwd trace.perf &&
+
+ GIT_TRACE2_REDACT=0 GIT_TRACE2_PERF="$(pwd)/unredacted.perf" \
+ git clone https://user:pwd@example.com/ clone2 &&
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <unredacted.perf >actual &&
+ grep "d0|main|start|.* clone https://user:pwd@example.com" actual &&
+ grep "d0|main|def_param|.*|remote.origin.url:https://user:pwd@example.com" actual
+'
+
+# Confirm that the requested command produces a "cmd_name" and a
+# set of "def_param" events.
+#
+try_simple () {
+ test_when_finished "rm prop.perf actual" &&
+
+ cmd=$1 &&
+ cmd_name=$2 &&
+
+ test_config_global "trace2.configParams" "cfg.prop.*" &&
+ test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" &&
+
+ test_config_global "cfg.prop.foo" "red" &&
+
+ ENV_PROP_FOO=blue \
+ GIT_TRACE2_PERF="$(pwd)/prop.perf" \
+ $cmd &&
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <prop.perf >actual &&
+ grep "d0|main|cmd_name|.*|$cmd_name" actual &&
+ grep "d0|main|def_param|.*|cfg.prop.foo:red" actual &&
+ grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual
+}
+
+# Representative mainstream builtin Git command dispatched
+# in run_builtin() in git.c
+#
+test_expect_success 'expect def_params for normal builtin command' '
+ try_simple "git version" "version"
+'
+
+# Representative query command dispatched in handle_options()
+# in git.c
+#
+test_expect_success 'expect def_params for query command' '
+ try_simple "git --man-path" "_query_"
+'
+
+# remote-curl.c does not use the builtin setup in git.c, so confirm
+# that executables built from remote-curl.c emit def_params.
+#
+# Also tests the dashed-command handling where "git foo" silently
+# spawns "git-foo". Make sure that both commands should emit
+# def_params.
+#
+# Pass bogus arguments to remote-https and allow the command to fail
+# because we don't actually have a remote to fetch from. We just want
+# to see the run-dashed code run an executable built from
+# remote-curl.c rather than git.c. Confirm that we get def_param
+# events from both layers.
+#
+test_expect_success 'expect def_params for remote-curl and _run_dashed_' '
+ test_when_finished "rm prop.perf actual" &&
+
+ test_config_global "trace2.configParams" "cfg.prop.*" &&
+ test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" &&
+
+ test_config_global "cfg.prop.foo" "red" &&
+
+ test_might_fail env \
+ ENV_PROP_FOO=blue \
+ GIT_TRACE2_PERF="$(pwd)/prop.perf" \
+ git remote-http x y &&
+
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <prop.perf >actual &&
+
+ grep "d0|main|cmd_name|.*|_run_dashed_" actual &&
+ grep "d0|main|def_param|.*|cfg.prop.foo:red" actual &&
+ grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual &&
+
+ grep "d1|main|cmd_name|.*|remote-curl" actual &&
+ grep "d1|main|def_param|.*|cfg.prop.foo:red" actual &&
+ grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual
+'
+
+# Similarly, `git-http-fetch` is not built from git.c so do a
+# trivial fetch so that the main git.c run-dashed code spawns
+# an executable built from http-fetch.c. Confirm that we get
+# def_param events from both layers.
+#
+test_expect_success 'expect def_params for http-fetch and _run_dashed_' '
+ test_when_finished "rm prop.perf actual" &&
+
+ test_config_global "trace2.configParams" "cfg.prop.*" &&
+ test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" &&
+
+ test_config_global "cfg.prop.foo" "red" &&
+
+ test_might_fail env \
+ ENV_PROP_FOO=blue \
+ GIT_TRACE2_PERF="$(pwd)/prop.perf" \
+ git http-fetch --stdin file:/// <<-EOF &&
+ EOF
+
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <prop.perf >actual &&
+
+ grep "d0|main|cmd_name|.*|_run_dashed_" actual &&
+ grep "d0|main|def_param|.*|cfg.prop.foo:red" actual &&
+ grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual &&
+
+ grep "d1|main|cmd_name|.*|http-fetch" actual &&
+ grep "d1|main|def_param|.*|cfg.prop.foo:red" actual &&
+ grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual
+'
+
+# Historically, alias expansion explicitly emitted the def_param
+# events (independent of whether the command was a builtin, a Git
+# command or arbitrary shell command) so that it wasn't dependent
+# upon the unpeeling of the alias. Let's make sure that we preserve
+# the net effect.
+#
+test_expect_success 'expect def_params during git alias expansion' '
+ test_when_finished "rm prop.perf actual" &&
+
+ test_config_global "trace2.configParams" "cfg.prop.*" &&
+ test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" &&
+
+ test_config_global "cfg.prop.foo" "red" &&
+
+ test_config_global "alias.xxx" "version" &&
+
+ ENV_PROP_FOO=blue \
+ GIT_TRACE2_PERF="$(pwd)/prop.perf" \
+ git xxx &&
+
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <prop.perf >actual &&
+
+ # "git xxx" is first mapped to "git-xxx" and the child will fail.
+ grep "d0|main|cmd_name|.*|_run_dashed_ (_run_dashed_)" actual &&
+
+ # We unpeel that and substitute "version" into "xxx" (giving
+ # "git version") and update the cmd_name event.
+ grep "d0|main|cmd_name|.*|_run_git_alias_ (_run_dashed_/_run_git_alias_)" actual &&
+
+ # These def_param events could be associated with either of the
+ # above cmd_name events. It does not matter.
+ grep "d0|main|def_param|.*|cfg.prop.foo:red" actual &&
+ grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual &&
+
+ # The "git version" child sees a different cmd_name hierarchy.
+ # Also test the def_param (only for completeness).
+ grep "d1|main|cmd_name|.*|version (_run_dashed_/_run_git_alias_/version)" actual &&
+ grep "d1|main|def_param|.*|cfg.prop.foo:red" actual &&
+ grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual
+'
+
+test_expect_success 'expect def_params during shell alias expansion' '
+ test_when_finished "rm prop.perf actual" &&
+
+ test_config_global "trace2.configParams" "cfg.prop.*" &&
+ test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" &&
+
+ test_config_global "cfg.prop.foo" "red" &&
+
+ test_config_global "alias.xxx" "!git version" &&
+
+ ENV_PROP_FOO=blue \
+ GIT_TRACE2_PERF="$(pwd)/prop.perf" \
+ git xxx &&
+
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <prop.perf >actual &&
+
+ # "git xxx" is first mapped to "git-xxx" and the child will fail.
+ grep "d0|main|cmd_name|.*|_run_dashed_ (_run_dashed_)" actual &&
+
+ # We unpeel that and substitute "git version" for "git xxx" (as a
+ # shell command. Another cmd_name event is emitted as we unpeel.
+ grep "d0|main|cmd_name|.*|_run_shell_alias_ (_run_dashed_/_run_shell_alias_)" actual &&
+
+ # These def_param events could be associated with either of the
+ # above cmd_name events. It does not matter.
+ grep "d0|main|def_param|.*|cfg.prop.foo:red" actual &&
+ grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual &&
+
+ # We get the following only because we used a git command for the
+ # shell command. In general, it could have been a shell script and
+ # we would see nothing.
+ #
+ # The child knows the cmd_name hierarchy so it includes it.
+ grep "d1|main|cmd_name|.*|version (_run_dashed_/_run_shell_alias_/version)" actual &&
+ grep "d1|main|def_param|.*|cfg.prop.foo:red" actual &&
+ grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual
+'
+
+test_expect_success 'expect def_params during nested git alias expansion' '
+ test_when_finished "rm prop.perf actual" &&
+
+ test_config_global "trace2.configParams" "cfg.prop.*" &&
+ test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" &&
+
+ test_config_global "cfg.prop.foo" "red" &&
+
+ test_config_global "alias.xxx" "yyy" &&
+ test_config_global "alias.yyy" "version" &&
+
+ ENV_PROP_FOO=blue \
+ GIT_TRACE2_PERF="$(pwd)/prop.perf" \
+ git xxx &&
+
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <prop.perf >actual &&
+
+ # "git xxx" is first mapped to "git-xxx" and try to spawn "git-xxx"
+ # and the child will fail.
+ grep "d0|main|cmd_name|.*|_run_dashed_ (_run_dashed_)" actual &&
+ grep "d0|main|child_start|.*|.* class:dashed argv:\[git-xxx\]" actual &&
+
+ # We unpeel that and substitute "yyy" into "xxx" (giving "git yyy")
+ # and spawn "git-yyy" and the child will fail.
+ grep "d0|main|alias|.*|alias:xxx argv:\[yyy\]" actual &&
+ grep "d0|main|cmd_name|.*|_run_dashed_ (_run_dashed_/_run_dashed_)" actual &&
+ grep "d0|main|child_start|.*|.* class:dashed argv:\[git-yyy\]" actual &&
+
+ # We unpeel that and substitute "version" into "xxx" (giving
+ # "git version") and update the cmd_name event.
+ grep "d0|main|alias|.*|alias:yyy argv:\[version\]" actual &&
+ grep "d0|main|cmd_name|.*|_run_git_alias_ (_run_dashed_/_run_dashed_/_run_git_alias_)" actual &&
+
+ # These def_param events could be associated with any of the
+ # above cmd_name events. It does not matter.
+ grep "d0|main|def_param|.*|cfg.prop.foo:red" actual >actual.matches &&
+ grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual &&
+
+ # However, we do not want them repeated each time we unpeel.
+ test_line_count = 1 actual.matches &&
+
+ # The "git version" child sees a different cmd_name hierarchy.
+ # Also test the def_param (only for completeness).
+ grep "d1|main|cmd_name|.*|version (_run_dashed_/_run_dashed_/_run_git_alias_/version)" actual &&
+ grep "d1|main|def_param|.*|cfg.prop.foo:red" actual &&
+ grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual
+'
+
test_done
diff --git a/t/t0211/scrub_perf.perl b/t/t0211/scrub_perf.perl
index d164b750..7a50bae 100644
--- a/t/t0211/scrub_perf.perl
+++ b/t/t0211/scrub_perf.perl
@@ -59,6 +59,16 @@ while (<>) {
# and highly variable. Just omit them.
goto SKIP_LINE;
}
+ if ($tokens[$col_category] =~ m/fsync/) {
+ # fsync events aren't interesting for the test
+ goto SKIP_LINE;
+ }
+ }
+ elsif ($tokens[$col_event] =~ m/timer/) {
+ # This also captures "th_timer" events
+ $tokens[$col_rest] =~ s/ total:\d+\.\d*/ total:_T_TOTAL_/;
+ $tokens[$col_rest] =~ s/ min:\d+\.\d*/ min:_T_MIN_/;
+ $tokens[$col_rest] =~ s/ max:\d+\.\d*/ max:_T_MAX_/;
}
# t_abs and t_rel are either blank or a float. Replace the float
diff --git a/t/t0212-trace2-event.sh b/t/t0212-trace2-event.sh
index 1529155..147643d 100755
--- a/t/t0212-trace2-event.sh
+++ b/t/t0212-trace2-event.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test trace2 facility'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Turn off any inherited trace2 settings for this test.
@@ -321,4 +323,44 @@ test_expect_success 'discard traces when there are too many files' '
head -n2 trace_target_dir/git-trace2-discard | tail -n1 | grep \"event\":\"too_many_files\"
'
+# In the following "...redact..." tests, skip testing the GIT_TRACE2_REDACT=0
+# case because we would need to exactly model the full JSON event stream like
+# we did in the basic tests above and I do not think it is worth it.
+
+test_expect_success 'unsafe URLs are redacted by default in cmd_start events' '
+ test_when_finished \
+ "rm -r trace.event" &&
+
+ GIT_TRACE2_EVENT="$(pwd)/trace.event" \
+ test-tool trace2 300redact_start git clone https://user:pwd@example.com/ clone2 &&
+ ! grep user:pwd trace.event
+'
+
+test_expect_success 'unsafe URLs are redacted by default in child_start events' '
+ test_when_finished \
+ "rm -r trace.event" &&
+
+ GIT_TRACE2_EVENT="$(pwd)/trace.event" \
+ test-tool trace2 301redact_child_start git clone https://user:pwd@example.com/ clone2 &&
+ ! grep user:pwd trace.event
+'
+
+test_expect_success 'unsafe URLs are redacted by default in exec events' '
+ test_when_finished \
+ "rm -r trace.event" &&
+
+ GIT_TRACE2_EVENT="$(pwd)/trace.event" \
+ test-tool trace2 302redact_exec git clone https://user:pwd@example.com/ clone2 &&
+ ! grep user:pwd trace.event
+'
+
+test_expect_success 'unsafe URLs are redacted by default in def_param events' '
+ test_when_finished \
+ "rm -r trace.event" &&
+
+ GIT_TRACE2_EVENT="$(pwd)/trace.event" \
+ test-tool trace2 303redact_def_param url https://user:pwd@example.com/ &&
+ ! grep user:pwd trace.event
+'
+
test_done
diff --git a/t/t0212/parse_events.perl b/t/t0212/parse_events.perl
index b640856..30a9f51 100644
--- a/t/t0212/parse_events.perl
+++ b/t/t0212/parse_events.perl
@@ -216,12 +216,19 @@ while (<>) {
elsif ($event eq 'data') {
my $cat = $line->{'category'};
- if ($cat eq 'test_category') {
-
- my $key = $line->{'key'};
- my $value = $line->{'value'};
- $processes->{$sid}->{'data'}->{$cat}->{$key} = $value;
- }
+ my $key = $line->{'key'};
+ my $value = $line->{'value'};
+ $processes->{$sid}->{'data'}->{$cat}->{$key} = $value;
+ }
+
+ elsif ($event eq 'data_json') {
+ # NEEDSWORK: Ignore due to
+ # compat/win32/trace2_win32_process_info.c, which should log a
+ # "cmd_ancestry" event instead.
+ }
+
+ else {
+ push @{$processes->{$sid}->{$event}} => $line->{value};
}
# This trace2 target does not emit 'printf' events.
diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh
index 3485c05..400f6bd 100755
--- a/t/t0300-credentials.sh
+++ b/t/t0300-credentials.sh
@@ -35,6 +35,16 @@ test_expect_success 'setup helper scripts' '
test -z "$pass" || echo password=$pass
EOF
+ write_script git-credential-verbatim-with-expiry <<-\EOF &&
+ user=$1; shift
+ pass=$1; shift
+ pexpiry=$1; shift
+ . ./dump
+ test -z "$user" || echo username=$user
+ test -z "$pass" || echo password=$pass
+ test -z "$pexpiry" || echo password_expiry_utc=$pexpiry
+ EOF
+
PATH="$PWD:$PATH"
'
@@ -109,6 +119,43 @@ test_expect_success 'credential_fill continues through partial response' '
EOF
'
+test_expect_success 'credential_fill populates password_expiry_utc' '
+ check fill "verbatim-with-expiry one two 9999999999" <<-\EOF
+ protocol=http
+ host=example.com
+ --
+ protocol=http
+ host=example.com
+ username=one
+ password=two
+ password_expiry_utc=9999999999
+ --
+ verbatim-with-expiry: get
+ verbatim-with-expiry: protocol=http
+ verbatim-with-expiry: host=example.com
+ EOF
+'
+
+test_expect_success 'credential_fill ignores expired password' '
+ check fill "verbatim-with-expiry one two 5" "verbatim three four" <<-\EOF
+ protocol=http
+ host=example.com
+ --
+ protocol=http
+ host=example.com
+ username=three
+ password=four
+ --
+ verbatim-with-expiry: get
+ verbatim-with-expiry: protocol=http
+ verbatim-with-expiry: host=example.com
+ verbatim: get
+ verbatim: protocol=http
+ verbatim: host=example.com
+ verbatim: username=one
+ EOF
+'
+
test_expect_success 'credential_fill passes along metadata' '
check fill "verbatim one two" <<-\EOF
protocol=ftp
@@ -149,6 +196,42 @@ test_expect_success 'credential_approve calls all helpers' '
EOF
'
+test_expect_success 'credential_approve stores password expiry' '
+ check approve useless <<-\EOF
+ protocol=http
+ host=example.com
+ username=foo
+ password=bar
+ password_expiry_utc=9999999999
+ --
+ --
+ useless: store
+ useless: protocol=http
+ useless: host=example.com
+ useless: username=foo
+ useless: password=bar
+ useless: password_expiry_utc=9999999999
+ EOF
+'
+
+test_expect_success 'credential_approve stores oauth refresh token' '
+ check approve useless <<-\EOF
+ protocol=http
+ host=example.com
+ username=foo
+ password=bar
+ oauth_refresh_token=xyzzy
+ --
+ --
+ useless: store
+ useless: protocol=http
+ useless: host=example.com
+ useless: username=foo
+ useless: password=bar
+ useless: oauth_refresh_token=xyzzy
+ EOF
+'
+
test_expect_success 'do not bother storing password-less credential' '
check approve useless <<-\EOF
protocol=http
@@ -159,6 +242,17 @@ test_expect_success 'do not bother storing password-less credential' '
EOF
'
+test_expect_success 'credential_approve does not store expired password' '
+ check approve useless <<-\EOF
+ protocol=http
+ host=example.com
+ username=foo
+ password=bar
+ password_expiry_utc=5
+ --
+ --
+ EOF
+'
test_expect_success 'credential_reject calls all helpers' '
check reject useless "verbatim one two" <<-\EOF
@@ -181,6 +275,24 @@ test_expect_success 'credential_reject calls all helpers' '
EOF
'
+test_expect_success 'credential_reject erases credential regardless of expiry' '
+ check reject useless <<-\EOF
+ protocol=http
+ host=example.com
+ username=foo
+ password=bar
+ password_expiry_utc=5
+ --
+ --
+ useless: erase
+ useless: protocol=http
+ useless: host=example.com
+ useless: username=foo
+ useless: password=bar
+ useless: password_expiry_utc=5
+ EOF
+'
+
test_expect_success 'usernames can be preserved' '
check fill "verbatim \"\" three" <<-\EOF
protocol=http
@@ -714,8 +826,8 @@ test_expect_success 'credential config with partial URLs' '
git -c credential.$partial.helper=yep \
-c credential.with%0anewline.username=uh-oh \
- credential fill <stdin >stdout 2>stderr &&
- test_i18ngrep "skipping credential lookup for key" stderr
+ credential fill <stdin 2>stderr &&
+ test_grep "skipping credential lookup for key" stderr
'
test_done
diff --git a/t/t0301-credential-cache.sh b/t/t0301-credential-cache.sh
index ebd5fa5..f2c146f 100755
--- a/t/t0301-credential-cache.sh
+++ b/t/t0301-credential-cache.sh
@@ -8,12 +8,37 @@ test -z "$NO_UNIX_SOCKETS" || {
skip_all='skipping credential-cache tests, unix sockets not available'
test_done
}
+if test_have_prereq MINGW
+then
+ service_running=$(sc query afunix | grep "4 RUNNING")
+ test -z "$service_running" || {
+ skip_all='skipping credential-cache tests, unix sockets not available'
+ test_done
+ }
+fi
+
+uname_s=$(uname -s)
+case $uname_s in
+*MINGW*)
+ test_path_is_socket () {
+ # `test -S` cannot detect Win10's Unix sockets
+ test_path_exists "$1"
+ }
+ ;;
+*)
+ test_path_is_socket () {
+ test -S "$1"
+ }
+ ;;
+esac
# don't leave a stale daemon running
test_atexit 'git credential-cache exit'
# test that the daemon works with no special setup
helper_test cache
+helper_test_password_expiry_utc cache
+helper_test_oauth_refresh_token cache
test_expect_success 'socket defaults to ~/.cache/git/credential/socket' '
test_when_finished "
@@ -21,7 +46,7 @@ test_expect_success 'socket defaults to ~/.cache/git/credential/socket' '
rmdir -p .cache/git/credential/
" &&
test_path_is_missing "$HOME/.git-credential-cache" &&
- test -S "$HOME/.cache/git/credential/socket"
+ test_path_is_socket "$HOME/.cache/git/credential/socket"
'
XDG_CACHE_HOME="$HOME/xdg"
@@ -31,7 +56,7 @@ helper_test cache
test_expect_success "use custom XDG_CACHE_HOME if set and default sockets are not created" '
test_when_finished "git credential-cache exit" &&
- test -S "$XDG_CACHE_HOME/git/credential/socket" &&
+ test_path_is_socket "$XDG_CACHE_HOME/git/credential/socket" &&
test_path_is_missing "$HOME/.git-credential-cache/socket" &&
test_path_is_missing "$HOME/.cache/git/credential/socket"
'
@@ -48,7 +73,7 @@ test_expect_success 'credential-cache --socket option overrides default location
username=store-user
password=store-pass
EOF
- test -S "$HOME/dir/socket"
+ test_path_is_socket "$HOME/dir/socket"
'
test_expect_success "use custom XDG_CACHE_HOME even if xdg socket exists" '
@@ -62,7 +87,7 @@ test_expect_success "use custom XDG_CACHE_HOME even if xdg socket exists" '
username=store-user
password=store-pass
EOF
- test -S "$HOME/.cache/git/credential/socket" &&
+ test_path_is_socket "$HOME/.cache/git/credential/socket" &&
XDG_CACHE_HOME="$HOME/xdg" &&
export XDG_CACHE_HOME &&
check approve cache <<-\EOF &&
@@ -71,7 +96,7 @@ test_expect_success "use custom XDG_CACHE_HOME even if xdg socket exists" '
username=store-user
password=store-pass
EOF
- test -S "$XDG_CACHE_HOME/git/credential/socket"
+ test_path_is_socket "$XDG_CACHE_HOME/git/credential/socket"
'
test_expect_success 'use user socket if user directory exists' '
@@ -79,14 +104,15 @@ test_expect_success 'use user socket if user directory exists' '
git credential-cache exit &&
rmdir \"\$HOME/.git-credential-cache/\"
" &&
- mkdir -p -m 700 "$HOME/.git-credential-cache/" &&
+ mkdir -p "$HOME/.git-credential-cache/" &&
+ chmod 700 "$HOME/.git-credential-cache/" &&
check approve cache <<-\EOF &&
protocol=https
host=example.com
username=store-user
password=store-pass
EOF
- test -S "$HOME/.git-credential-cache/socket"
+ test_path_is_socket "$HOME/.git-credential-cache/socket"
'
test_expect_success SYMLINKS 'use user socket if user directory is a symlink to a directory' '
@@ -103,7 +129,7 @@ test_expect_success SYMLINKS 'use user socket if user directory is a symlink to
username=store-user
password=store-pass
EOF
- test -S "$HOME/.git-credential-cache/socket"
+ test_path_is_socket "$HOME/.git-credential-cache/socket"
'
helper_test_timeout cache --timeout=1
diff --git a/t/t0303-credential-external.sh b/t/t0303-credential-external.sh
index f028fd1..72ae405 100755
--- a/t/t0303-credential-external.sh
+++ b/t/t0303-credential-external.sh
@@ -32,9 +32,24 @@ commands.
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-credential.sh
+# If we're not given a specific external helper to run against,
+# there isn't much to test. But we can still run through our
+# battery of tests with a fake helper and check that the
+# test themselves are self-consistent and clean up after
+# themselves.
+#
+# We'll use the "store" helper, since we can easily inspect
+# its state by looking at the on-disk file. But since it doesn't
+# implement any caching or expiry logic, we'll cheat and override
+# the "check" function to just report all results as OK.
if test -z "$GIT_TEST_CREDENTIAL_HELPER"; then
- skip_all="used to test external credential helpers"
- test_done
+ GIT_TEST_CREDENTIAL_HELPER=store
+ GIT_TEST_CREDENTIAL_HELPER_TIMEOUT=store
+ check () {
+ test "$1" = "approve" || return 0
+ git -c credential.helper=store credential approve
+ }
+ check_cleanup=t
fi
test -z "$GIT_TEST_CREDENTIAL_HELPER_SETUP" ||
@@ -45,6 +60,8 @@ test -z "$GIT_TEST_CREDENTIAL_HELPER_SETUP" ||
helper_test_clean "$GIT_TEST_CREDENTIAL_HELPER"
helper_test "$GIT_TEST_CREDENTIAL_HELPER"
+helper_test_password_expiry_utc "$GIT_TEST_CREDENTIAL_HELPER"
+helper_test_oauth_refresh_token "$GIT_TEST_CREDENTIAL_HELPER"
if test -z "$GIT_TEST_CREDENTIAL_HELPER_TIMEOUT"; then
say "# skipping timeout tests (GIT_TEST_CREDENTIAL_HELPER_TIMEOUT not set)"
@@ -57,4 +74,11 @@ fi
# might be long-term system storage
helper_test_clean "$GIT_TEST_CREDENTIAL_HELPER"
+if test "$check_cleanup" = "t"
+then
+ test_expect_success 'test cleanup removes everything' '
+ test_must_be_empty "$HOME/.git-credentials"
+ '
+fi
+
test_done
diff --git a/t/t0410-partial-clone.sh b/t/t0410-partial-clone.sh
index a211a66..88a66f0 100755
--- a/t/t0410-partial-clone.sh
+++ b/t/t0410-partial-clone.sh
@@ -4,6 +4,13 @@ test_description='partial clone'
. ./test-lib.sh
+# missing promisor objects cause repacks which write bitmaps to fail
+GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0
+# When enabled, some commands will write commit-graphs. This causes fsck
+# to fail when delete_object() is called because fsck will attempt to
+# verify the out-of-sync commit graph.
+GIT_TEST_COMMIT_GRAPH=0
+
delete_object () {
rm $1/.git/objects/$(echo $2 | sed -e 's|^..|&/|')
}
@@ -42,7 +49,7 @@ test_expect_success 'convert shallow clone to partial clone' '
test_cmp_config -C client 1 core.repositoryformatversion
'
-test_expect_success SHA1 'convert to partial clone with noop extension' '
+test_expect_success DEFAULT_REPO_FORMAT 'convert to partial clone with noop extension' '
rm -fr server client &&
test_create_repo server &&
test_commit -C server my_commit 1 &&
@@ -53,7 +60,7 @@ test_expect_success SHA1 'convert to partial clone with noop extension' '
git -C client fetch --unshallow --filter="blob:none"
'
-test_expect_success SHA1 'converting to partial clone fails with unrecognized extension' '
+test_expect_success DEFAULT_REPO_FORMAT 'converting to partial clone fails with unrecognized extension' '
rm -fr server client &&
test_create_repo server &&
test_commit -C server my_commit 1 &&
@@ -208,6 +215,20 @@ test_expect_success 'fetching of missing objects' '
grep "$HASH" out
'
+test_expect_success 'fetching of a promised object that promisor remote no longer has' '
+ rm -f err &&
+ test_create_repo unreliable-server &&
+ git -C unreliable-server config uploadpack.allowanysha1inwant 1 &&
+ git -C unreliable-server config uploadpack.allowfilter 1 &&
+ test_commit -C unreliable-server foo &&
+
+ git clone --filter=blob:none --no-checkout "file://$(pwd)/unreliable-server" unreliable-client &&
+
+ rm -rf unreliable-server/.git/objects/* &&
+ test_must_fail git -C unreliable-client checkout HEAD 2>err &&
+ grep "could not fetch.*from promisor remote" err
+'
+
test_expect_success 'fetching of missing objects works with ref-in-want enabled' '
# ref-in-want requires protocol version 2
git -C server config protocol.version 2 &&
@@ -319,7 +340,7 @@ test_expect_success 'rev-list stops traversal at missing and promised commit' '
git -C repo config core.repositoryformatversion 1 &&
git -C repo config extensions.partialclone "arbitrary string" &&
- GIT_TEST_COMMIT_GRAPH=0 git -C repo -c core.commitGraph=false rev-list --exclude-promisor-objects --objects bar >out &&
+ git -C repo rev-list --exclude-promisor-objects --objects bar >out &&
grep $(git -C repo rev-parse bar) out &&
! grep $FOO out
'
@@ -462,7 +483,7 @@ test_expect_success 'rev-list dies for missing objects on cmd line' '
git -C repo rev-list --ignore-missing --objects \
--exclude-promisor-objects "$OBJ" &&
git -C repo rev-list --ignore-missing --objects-edge-aggressive \
- --exclude-promisor-objects "$OBJ"
+ --exclude-promisor-objects "$OBJ" || return 1
done
'
@@ -536,7 +557,13 @@ test_expect_success 'gc does not repack promisor objects if there are none' '
repack_and_check () {
rm -rf repo2 &&
cp -r repo repo2 &&
- git -C repo2 repack $1 -d &&
+ if test x"$1" = "x--must-fail"
+ then
+ shift
+ test_must_fail git -C repo2 repack $1 -d
+ else
+ git -C repo2 repack $1 -d
+ fi &&
git -C repo2 fsck &&
git -C repo2 cat-file -e $2 &&
@@ -561,6 +588,7 @@ test_expect_success 'repack -d does not irreversibly delete promisor objects' '
printf "$THREE\n" | pack_as_from_promisor &&
delete_object repo "$ONE" &&
+ repack_and_check --must-fail -ab "$TWO" "$THREE" &&
repack_and_check -a "$TWO" "$THREE" &&
repack_and_check -A "$TWO" "$THREE" &&
repack_and_check -l "$TWO" "$THREE"
@@ -604,6 +632,25 @@ test_expect_success 'do not fetch when checking existence of tree we construct o
git -C repo cherry-pick side1
'
+test_expect_success 'exact rename does not need to fetch the blob lazily' '
+ rm -rf repo partial.git &&
+ test_create_repo repo &&
+ content="some dummy content" &&
+ test_commit -C repo create-a-file file.txt "$content" &&
+ git -C repo mv file.txt new-file.txt &&
+ git -C repo commit -m rename-the-file &&
+ FILE_HASH=$(git -C repo rev-parse HEAD:new-file.txt) &&
+ test_config -C repo uploadpack.allowfilter 1 &&
+ test_config -C repo uploadpack.allowanysha1inwant 1 &&
+
+ git clone --filter=blob:none --bare "file://$(pwd)/repo" partial.git &&
+ git -C partial.git rev-list --objects --missing=print HEAD >out &&
+ grep "[?]$FILE_HASH" out &&
+ git -C partial.git log --follow -- new-file.txt &&
+ git -C partial.git rev-list --objects --missing=print HEAD >out &&
+ grep "[?]$FILE_HASH" out
+'
+
test_expect_success 'lazy-fetch when accessing object not in the_repository' '
rm -rf full partial.git &&
test_create_repo full &&
@@ -618,6 +665,21 @@ test_expect_success 'lazy-fetch when accessing object not in the_repository' '
git -C partial.git rev-list --objects --missing=print HEAD >out &&
grep "[?]$FILE_HASH" out &&
+ # The no-lazy-fetch mechanism prevents Git from fetching
+ test_must_fail env GIT_NO_LAZY_FETCH=1 \
+ git -C partial.git cat-file -e "$FILE_HASH" &&
+
+ # The same with command line option to "git"
+ test_must_fail git --no-lazy-fetch -C partial.git cat-file -e "$FILE_HASH" &&
+
+ # The same, forcing a subprocess via an alias
+ test_must_fail git --no-lazy-fetch -C partial.git \
+ -c alias.foo="!git cat-file" foo -e "$FILE_HASH" &&
+
+ # Sanity check that the file is still missing
+ git -C partial.git rev-list --objects --missing=print HEAD >out &&
+ grep "[?]$FILE_HASH" out &&
+
git -C full cat-file -s "$FILE_HASH" >expect &&
test-tool partial-clone object-info partial.git "$FILE_HASH" >actual &&
test_cmp expect actual &&
diff --git a/t/t0450-txt-doc-vs-help.sh b/t/t0450-txt-doc-vs-help.sh
new file mode 100755
index 0000000..69917d7
--- /dev/null
+++ b/t/t0450-txt-doc-vs-help.sh
@@ -0,0 +1,174 @@
+#!/bin/sh
+
+test_description='assert (unbuilt) Documentation/*.txt and -h output
+
+Run this with --debug to see a summary of where we still fail to make
+the two versions consistent with one another.'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'setup: list of builtins' '
+ git --list-cmds=builtins >builtins
+'
+
+test_expect_success 'list of txt and help mismatches is sorted' '
+ sort -u "$TEST_DIRECTORY"/t0450/txt-help-mismatches >expect &&
+ if ! test_cmp expect "$TEST_DIRECTORY"/t0450/txt-help-mismatches
+ then
+ BUG "please keep the list of txt and help mismatches sorted"
+ fi
+'
+
+help_to_synopsis () {
+ builtin="$1" &&
+ out_dir="out/$builtin" &&
+ out="$out_dir/help.synopsis" &&
+ if test -f "$out"
+ then
+ echo "$out" &&
+ return 0
+ fi &&
+ mkdir -p "$out_dir" &&
+ test_expect_code 129 git $builtin -h >"$out.raw" 2>&1 &&
+ sed -n \
+ -e '1,/^$/ {
+ /^$/d;
+ s/^usage: //;
+ s/^ *or: //;
+ p;
+ }' <"$out.raw" >"$out" &&
+ echo "$out"
+}
+
+builtin_to_txt () {
+ echo "$GIT_BUILD_DIR/Documentation/git-$1.txt"
+}
+
+txt_to_synopsis () {
+ builtin="$1" &&
+ out_dir="out/$builtin" &&
+ out="$out_dir/txt.synopsis" &&
+ if test -f "$out"
+ then
+ echo "$out" &&
+ return 0
+ fi &&
+ b2t="$(builtin_to_txt "$builtin")" &&
+ sed -n \
+ -e '/^\[verse\]$/,/^$/ {
+ /^$/d;
+ /^\[verse\]$/d;
+ s/_//g;
+ s/++//g;
+ s/`//g;
+ s/{litdd}/--/g;
+ s/'\''\(git[ a-z-]*\)'\''/\1/g;
+
+ p;
+ }' \
+ <"$b2t" >"$out" &&
+ echo "$out"
+}
+
+check_dashed_labels () {
+ ! grep -E "<[^>_-]+_" "$1"
+}
+
+HT=" "
+
+align_after_nl () {
+ builtin="$1" &&
+ len=$(printf "git %s " "$builtin" | wc -c) &&
+ pad=$(printf "%${len}s" "") &&
+
+ sed "s/^[ $HT][ $HT]*/$pad/"
+}
+
+test_debug '>failing'
+while read builtin
+do
+ # -h output assertions
+ test_expect_success "$builtin -h output has no \t" '
+ h2s="$(help_to_synopsis "$builtin")" &&
+ ! grep "$HT" "$h2s"
+ '
+
+ test_expect_success "$builtin -h output has dashed labels" '
+ check_dashed_labels "$(help_to_synopsis "$builtin")"
+ '
+
+ test_expect_success "$builtin -h output has consistent spacing" '
+ h2s="$(help_to_synopsis "$builtin")" &&
+ sed -n \
+ -e "/^ / {
+ s/[^ ].*//;
+ p;
+ }" \
+ <"$h2s" >help &&
+ sort -u help >help.ws &&
+ if test -s help.ws
+ then
+ test_line_count = 1 help.ws
+ fi
+ '
+
+ txt="$(builtin_to_txt "$builtin")" &&
+ preq="$(echo BUILTIN_TXT_$builtin | tr '[:lower:]-' '[:upper:]_')" &&
+
+ if test -f "$txt"
+ then
+ test_set_prereq "$preq"
+ fi &&
+
+ # *.txt output assertions
+ test_expect_success "$preq" "$builtin *.txt SYNOPSIS has dashed labels" '
+ check_dashed_labels "$(txt_to_synopsis "$builtin")"
+ '
+
+ # *.txt output consistency assertions
+ result=
+ if grep -q "^$builtin$" "$TEST_DIRECTORY"/t0450/txt-help-mismatches
+ then
+ result=failure
+ else
+ result=success
+ fi &&
+ test_expect_$result "$preq" "$builtin -h output and SYNOPSIS agree" '
+ t2s="$(txt_to_synopsis "$builtin")" &&
+ if test "$builtin" = "merge-tree"
+ then
+ test_when_finished "rm -f t2s.new" &&
+ sed -e '\''s/ (deprecated)$//g'\'' <"$t2s" >t2s.new
+ t2s=t2s.new
+ fi &&
+ h2s="$(help_to_synopsis "$builtin")" &&
+
+ # The *.txt and -h use different spacing for the
+ # alignment of continued usage output, normalize it.
+ align_after_nl "$builtin" <"$t2s" >txt &&
+ align_after_nl "$builtin" <"$h2s" >help &&
+ test_cmp txt help
+ '
+
+ if test_have_prereq "$preq" && test -e txt && test -e help
+ then
+ test_debug '
+ if test_cmp txt help >cmp 2>/dev/null
+ then
+ echo "=== DONE: $builtin ==="
+ else
+ echo "=== TODO: $builtin ===" &&
+ cat cmp
+ fi >>failing
+ '
+
+ # Not in test_expect_success in case --run is being
+ # used with --debug
+ rm -f txt help tmp 2>/dev/null
+ fi
+done <builtins
+
+test_debug 'say "$(cat failing)"'
+
+test_done
diff --git a/t/t0450/txt-help-mismatches b/t/t0450/txt-help-mismatches
new file mode 100644
index 0000000..a0777ac
--- /dev/null
+++ b/t/t0450/txt-help-mismatches
@@ -0,0 +1,58 @@
+add
+am
+apply
+archive
+bisect
+blame
+branch
+check-ref-format
+checkout
+checkout-index
+clone
+column
+config
+credential
+credential-cache
+credential-store
+fast-export
+fast-import
+fetch-pack
+fmt-merge-msg
+for-each-ref
+format-patch
+fsck-objects
+fsmonitor--daemon
+gc
+grep
+index-pack
+init-db
+log
+ls-files
+ls-tree
+mailinfo
+mailsplit
+maintenance
+merge
+merge-file
+merge-index
+merge-one-file
+multi-pack-index
+name-rev
+notes
+pack-objects
+push
+range-diff
+rebase
+remote
+remote-ext
+remote-fd
+repack
+reset
+restore
+rev-parse
+show
+stage
+switch
+update-index
+update-ref
+whatchanged
diff --git a/t/t0500-progress-display.sh b/t/t0500-progress-display.sh
index 22058b5..1eb3a83 100755
--- a/t/t0500-progress-display.sh
+++ b/t/t0500-progress-display.sh
@@ -2,6 +2,7 @@
test_description='progress display'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
show_cr () {
@@ -17,6 +18,7 @@ test_expect_success 'simple progress display' '
EOF
cat >in <<-\EOF &&
+ start 0
update
progress 1
update
@@ -25,8 +27,9 @@ test_expect_success 'simple progress display' '
progress 4
update
progress 5
+ stop
EOF
- test-tool progress "Working hard" <in 2>stderr &&
+ test-tool progress <in 2>stderr &&
show_cr <stderr >out &&
test_cmp expect out
@@ -41,11 +44,13 @@ test_expect_success 'progress display with total' '
EOF
cat >in <<-\EOF &&
+ start 3
progress 1
progress 2
progress 3
+ stop
EOF
- test-tool progress --total=3 "Working hard" <in 2>stderr &&
+ test-tool progress <in 2>stderr &&
show_cr <stderr >out &&
test_cmp expect out
@@ -62,14 +67,14 @@ Working hard.......2.........3.........4.........5.........6:
EOF
cat >in <<-\EOF &&
+ start 100000 Working hard.......2.........3.........4.........5.........6
progress 100
progress 1000
progress 10000
progress 100000
+ stop
EOF
- test-tool progress --total=100000 \
- "Working hard.......2.........3.........4.........5.........6" \
- <in 2>stderr &&
+ test-tool progress <in 2>stderr &&
show_cr <stderr >out &&
test_cmp expect out
@@ -88,16 +93,16 @@ Working hard.......2.........3.........4.........5.........6:
EOF
cat >in <<-\EOF &&
+ start 100000 Working hard.......2.........3.........4.........5.........6
update
progress 1
update
progress 2
progress 10000
progress 100000
+ stop
EOF
- test-tool progress --total=100000 \
- "Working hard.......2.........3.........4.........5.........6" \
- <in 2>stderr &&
+ test-tool progress <in 2>stderr &&
show_cr <stderr >out &&
test_cmp expect out
@@ -116,14 +121,14 @@ Working hard.......2.........3.........4.........5.........6:
EOF
cat >in <<-\EOF &&
+ start 100000 Working hard.......2.........3.........4.........5.........6
progress 25000
progress 50000
progress 75000
progress 100000
+ stop
EOF
- test-tool progress --total=100000 \
- "Working hard.......2.........3.........4.........5.........6" \
- <in 2>stderr &&
+ test-tool progress <in 2>stderr &&
show_cr <stderr >out &&
test_cmp expect out
@@ -140,14 +145,14 @@ Working hard.......2.........3.........4.........5.........6.........7.........:
EOF
cat >in <<-\EOF &&
+ start 100000 Working hard.......2.........3.........4.........5.........6.........7.........
progress 25000
progress 50000
progress 75000
progress 100000
+ stop
EOF
- test-tool progress --total=100000 \
- "Working hard.......2.........3.........4.........5.........6.........7........." \
- <in 2>stderr &&
+ test-tool progress <in 2>stderr &&
show_cr <stderr >out &&
test_cmp expect out
@@ -164,12 +169,14 @@ test_expect_success 'progress shortens - crazy caller' '
EOF
cat >in <<-\EOF &&
+ start 1000
progress 100
progress 200
progress 1
progress 1000
+ stop
EOF
- test-tool progress --total=1000 "Working hard" <in 2>stderr &&
+ test-tool progress <in 2>stderr &&
show_cr <stderr >out &&
test_cmp expect out
@@ -185,6 +192,7 @@ test_expect_success 'progress display with throughput' '
EOF
cat >in <<-\EOF &&
+ start 0
throughput 102400 1000
update
progress 10
@@ -197,8 +205,9 @@ test_expect_success 'progress display with throughput' '
throughput 409600 4000
update
progress 40
+ stop
EOF
- test-tool progress "Working hard" <in 2>stderr &&
+ test-tool progress <in 2>stderr &&
show_cr <stderr >out &&
test_cmp expect out
@@ -214,6 +223,7 @@ test_expect_success 'progress display with throughput and total' '
EOF
cat >in <<-\EOF &&
+ start 40
throughput 102400 1000
progress 10
throughput 204800 2000
@@ -222,8 +232,9 @@ test_expect_success 'progress display with throughput and total' '
progress 30
throughput 409600 4000
progress 40
+ stop
EOF
- test-tool progress --total=40 "Working hard" <in 2>stderr &&
+ test-tool progress <in 2>stderr &&
show_cr <stderr >out &&
test_cmp expect out
@@ -239,6 +250,7 @@ test_expect_success 'cover up after throughput shortens' '
EOF
cat >in <<-\EOF &&
+ start 0
throughput 409600 1000
update
progress 1
@@ -251,8 +263,9 @@ test_expect_success 'cover up after throughput shortens' '
throughput 1638400 4000
update
progress 4
+ stop
EOF
- test-tool progress "Working hard" <in 2>stderr &&
+ test-tool progress <in 2>stderr &&
show_cr <stderr >out &&
test_cmp expect out
@@ -267,6 +280,7 @@ test_expect_success 'cover up after throughput shortens a lot' '
EOF
cat >in <<-\EOF &&
+ start 0
throughput 1 1000
update
progress 1
@@ -276,8 +290,9 @@ test_expect_success 'cover up after throughput shortens a lot' '
throughput 3145728 3000
update
progress 3
+ stop
EOF
- test-tool progress "Working hard" <in 2>stderr &&
+ test-tool progress <in 2>stderr &&
show_cr <stderr >out &&
test_cmp expect out
@@ -285,6 +300,7 @@ test_expect_success 'cover up after throughput shortens a lot' '
test_expect_success 'progress generates traces' '
cat >in <<-\EOF &&
+ start 40
throughput 102400 1000
update
progress 10
@@ -297,10 +313,11 @@ test_expect_success 'progress generates traces' '
throughput 409600 4000
update
progress 40
+ stop
EOF
- GIT_TRACE2_EVENT="$(pwd)/trace.event" test-tool progress --total=40 \
- "Working hard" <in 2>stderr &&
+ GIT_TRACE2_EVENT="$(pwd)/trace.event" test-tool progress \
+ <in 2>stderr &&
# t0212/parse_events.perl intentionally omits regions and data.
test_region progress "Working hard" trace.event &&
@@ -308,4 +325,54 @@ test_expect_success 'progress generates traces' '
grep "\"key\":\"total_bytes\",\"value\":\"409600\"" trace.event
'
+test_expect_success 'progress generates traces: stop / start' '
+ cat >in <<-\EOF &&
+ start 0
+ stop
+ EOF
+
+ GIT_TRACE2_EVENT="$PWD/trace-startstop.event" test-tool progress \
+ <in 2>stderr &&
+ test_region progress "Working hard" trace-startstop.event
+'
+
+test_expect_success 'progress generates traces: start without stop' '
+ cat >in <<-\EOF &&
+ start 0
+ EOF
+
+ GIT_TRACE2_EVENT="$PWD/trace-start.event" \
+ LSAN_OPTIONS=detect_leaks=0 \
+ test-tool progress \
+ <in 2>stderr &&
+ grep region_enter.*progress trace-start.event &&
+ ! grep region_leave.*progress trace-start.event
+'
+
+test_expect_success 'progress generates traces: stop without start' '
+ cat >in <<-\EOF &&
+ stop
+ EOF
+
+ GIT_TRACE2_EVENT="$PWD/trace-stop.event" test-tool progress \
+ <in 2>stderr &&
+ ! grep region_enter.*progress trace-stop.event &&
+ ! grep region_leave.*progress trace-stop.event
+'
+
+test_expect_success 'progress generates traces: start with active progress bar (no stops)' '
+ cat >in <<-\EOF &&
+ start 0 One
+ start 0 Two
+ EOF
+
+ GIT_TRACE2_EVENT="$PWD/trace-2start.event" \
+ LSAN_OPTIONS=detect_leaks=0 \
+ test-tool progress \
+ <in 2>stderr &&
+ grep region_enter.*progress.*One trace-2start.event &&
+ grep region_enter.*progress.*Two trace-2start.event &&
+ ! grep region_leave trace-2start.event
+'
+
test_done
diff --git a/t/t0600-reffiles-backend.sh b/t/t0600-reffiles-backend.sh
new file mode 100755
index 0000000..6421434
--- /dev/null
+++ b/t/t0600-reffiles-backend.sh
@@ -0,0 +1,475 @@
+#!/bin/sh
+
+test_description='Test reffiles backend'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+if ! test_have_prereq REFFILES
+then
+ skip_all='skipping reffiles specific tests'
+ test_done
+fi
+
+test_expect_success 'setup' '
+ git commit --allow-empty -m Initial &&
+ C=$(git rev-parse HEAD) &&
+ git commit --allow-empty -m Second &&
+ D=$(git rev-parse HEAD) &&
+ git commit --allow-empty -m Third &&
+ E=$(git rev-parse HEAD)
+'
+
+test_expect_success 'empty directory should not fool rev-parse' '
+ prefix=refs/e-rev-parse &&
+ git update-ref $prefix/foo $C &&
+ git pack-refs --all &&
+ mkdir -p .git/$prefix/foo/bar/baz &&
+ echo "$C" >expected &&
+ git rev-parse $prefix/foo >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'empty directory should not fool for-each-ref' '
+ prefix=refs/e-for-each-ref &&
+ git update-ref $prefix/foo $C &&
+ git for-each-ref $prefix >expected &&
+ git pack-refs --all &&
+ mkdir -p .git/$prefix/foo/bar/baz &&
+ git for-each-ref $prefix >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'empty directory should not fool create' '
+ prefix=refs/e-create &&
+ mkdir -p .git/$prefix/foo/bar/baz &&
+ printf "create %s $C\n" $prefix/foo |
+ git update-ref --stdin
+'
+
+test_expect_success 'empty directory should not fool verify' '
+ prefix=refs/e-verify &&
+ git update-ref $prefix/foo $C &&
+ git pack-refs --all &&
+ mkdir -p .git/$prefix/foo/bar/baz &&
+ printf "verify %s $C\n" $prefix/foo |
+ git update-ref --stdin
+'
+
+test_expect_success 'empty directory should not fool 1-arg update' '
+ prefix=refs/e-update-1 &&
+ git update-ref $prefix/foo $C &&
+ git pack-refs --all &&
+ mkdir -p .git/$prefix/foo/bar/baz &&
+ printf "update %s $D\n" $prefix/foo |
+ git update-ref --stdin
+'
+
+test_expect_success 'empty directory should not fool 2-arg update' '
+ prefix=refs/e-update-2 &&
+ git update-ref $prefix/foo $C &&
+ git pack-refs --all &&
+ mkdir -p .git/$prefix/foo/bar/baz &&
+ printf "update %s $D $C\n" $prefix/foo |
+ git update-ref --stdin
+'
+
+test_expect_success 'empty directory should not fool 0-arg delete' '
+ prefix=refs/e-delete-0 &&
+ git update-ref $prefix/foo $C &&
+ git pack-refs --all &&
+ mkdir -p .git/$prefix/foo/bar/baz &&
+ printf "delete %s\n" $prefix/foo |
+ git update-ref --stdin
+'
+
+test_expect_success 'empty directory should not fool 1-arg delete' '
+ prefix=refs/e-delete-1 &&
+ git update-ref $prefix/foo $C &&
+ git pack-refs --all &&
+ mkdir -p .git/$prefix/foo/bar/baz &&
+ printf "delete %s $C\n" $prefix/foo |
+ git update-ref --stdin
+'
+
+test_expect_success 'non-empty directory blocks create' '
+ prefix=refs/ne-create &&
+ mkdir -p .git/$prefix/foo/bar &&
+ : >.git/$prefix/foo/bar/baz.lock &&
+ test_when_finished "rm -f .git/$prefix/foo/bar/baz.lock" &&
+ cat >expected <<-EOF &&
+ fatal: cannot lock ref $SQ$prefix/foo$SQ: there is a non-empty directory $SQ.git/$prefix/foo$SQ blocking reference $SQ$prefix/foo$SQ
+ EOF
+ printf "%s\n" "update $prefix/foo $C" |
+ test_must_fail git update-ref --stdin 2>output.err &&
+ test_cmp expected output.err &&
+ cat >expected <<-EOF &&
+ fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ
+ EOF
+ printf "%s\n" "update $prefix/foo $D $C" |
+ test_must_fail git update-ref --stdin 2>output.err &&
+ test_cmp expected output.err
+'
+
+test_expect_success 'broken reference blocks create' '
+ prefix=refs/broken-create &&
+ mkdir -p .git/$prefix &&
+ echo "gobbledigook" >.git/$prefix/foo &&
+ test_when_finished "rm -f .git/$prefix/foo" &&
+ cat >expected <<-EOF &&
+ fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
+ EOF
+ printf "%s\n" "update $prefix/foo $C" |
+ test_must_fail git update-ref --stdin 2>output.err &&
+ test_cmp expected output.err &&
+ cat >expected <<-EOF &&
+ fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
+ EOF
+ printf "%s\n" "update $prefix/foo $D $C" |
+ test_must_fail git update-ref --stdin 2>output.err &&
+ test_cmp expected output.err
+'
+
+test_expect_success 'non-empty directory blocks indirect create' '
+ prefix=refs/ne-indirect-create &&
+ git symbolic-ref $prefix/symref $prefix/foo &&
+ mkdir -p .git/$prefix/foo/bar &&
+ : >.git/$prefix/foo/bar/baz.lock &&
+ test_when_finished "rm -f .git/$prefix/foo/bar/baz.lock" &&
+ cat >expected <<-EOF &&
+ fatal: cannot lock ref $SQ$prefix/symref$SQ: there is a non-empty directory $SQ.git/$prefix/foo$SQ blocking reference $SQ$prefix/foo$SQ
+ EOF
+ printf "%s\n" "update $prefix/symref $C" |
+ test_must_fail git update-ref --stdin 2>output.err &&
+ test_cmp expected output.err &&
+ cat >expected <<-EOF &&
+ fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ
+ EOF
+ printf "%s\n" "update $prefix/symref $D $C" |
+ test_must_fail git update-ref --stdin 2>output.err &&
+ test_cmp expected output.err
+'
+
+test_expect_success 'broken reference blocks indirect create' '
+ prefix=refs/broken-indirect-create &&
+ git symbolic-ref $prefix/symref $prefix/foo &&
+ echo "gobbledigook" >.git/$prefix/foo &&
+ test_when_finished "rm -f .git/$prefix/foo" &&
+ cat >expected <<-EOF &&
+ fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
+ EOF
+ printf "%s\n" "update $prefix/symref $C" |
+ test_must_fail git update-ref --stdin 2>output.err &&
+ test_cmp expected output.err &&
+ cat >expected <<-EOF &&
+ fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
+ EOF
+ printf "%s\n" "update $prefix/symref $D $C" |
+ test_must_fail git update-ref --stdin 2>output.err &&
+ test_cmp expected output.err
+'
+
+test_expect_success 'no bogus intermediate values during delete' '
+ prefix=refs/slow-transaction &&
+ # Set up a reference with differing loose and packed versions:
+ git update-ref $prefix/foo $C &&
+ git pack-refs --all &&
+ git update-ref $prefix/foo $D &&
+ # Now try to update the reference, but hold the `packed-refs` lock
+ # for a while to see what happens while the process is blocked:
+ : >.git/packed-refs.lock &&
+ test_when_finished "rm -f .git/packed-refs.lock" &&
+ {
+ # Note: the following command is intentionally run in the
+ # background. We increase the timeout so that `update-ref`
+ # attempts to acquire the `packed-refs` lock for much longer
+ # than it takes for us to do the check then delete it:
+ git -c core.packedrefstimeout=30000 update-ref -d $prefix/foo &
+ } &&
+ pid2=$! &&
+ # Give update-ref plenty of time to get to the point where it tries
+ # to lock packed-refs:
+ sleep 1 &&
+ # Make sure that update-ref did not complete despite the lock:
+ kill -0 $pid2 &&
+ # Verify that the reference still has its old value:
+ sha1=$(git rev-parse --verify --quiet $prefix/foo || echo undefined) &&
+ case "$sha1" in
+ $D)
+ # This is what we hope for; it means that nothing
+ # user-visible has changed yet.
+ : ;;
+ undefined)
+ # This is not correct; it means the deletion has happened
+ # already even though update-ref should not have been
+ # able to acquire the lock yet.
+ echo "$prefix/foo deleted prematurely" &&
+ break
+ ;;
+ $C)
+ # This value should never be seen. Probably the loose
+ # reference has been deleted but the packed reference
+ # is still there:
+ echo "$prefix/foo incorrectly observed to be C" &&
+ break
+ ;;
+ *)
+ # WTF?
+ echo "unexpected value observed for $prefix/foo: $sha1" &&
+ break
+ ;;
+ esac >out &&
+ rm -f .git/packed-refs.lock &&
+ wait $pid2 &&
+ test_must_be_empty out &&
+ test_must_fail git rev-parse --verify --quiet $prefix/foo
+'
+
+test_expect_success 'delete fails cleanly if packed-refs file is locked' '
+ prefix=refs/locked-packed-refs &&
+ # Set up a reference with differing loose and packed versions:
+ git update-ref $prefix/foo $C &&
+ git pack-refs --all &&
+ git update-ref $prefix/foo $D &&
+ git for-each-ref $prefix >unchanged &&
+ # Now try to delete it while the `packed-refs` lock is held:
+ : >.git/packed-refs.lock &&
+ test_when_finished "rm -f .git/packed-refs.lock" &&
+ test_must_fail git update-ref -d $prefix/foo >out 2>err &&
+ git for-each-ref $prefix >actual &&
+ test_grep "Unable to create $SQ.*packed-refs.lock$SQ: " err &&
+ test_cmp unchanged actual
+'
+
+test_expect_success 'delete fails cleanly if packed-refs.new write fails' '
+ # Setup and expectations are similar to the test above.
+ prefix=refs/failed-packed-refs &&
+ git update-ref $prefix/foo $C &&
+ git pack-refs --all &&
+ git update-ref $prefix/foo $D &&
+ git for-each-ref $prefix >unchanged &&
+ # This should not happen in practice, but it is an easy way to get a
+ # reliable error (we open with create_tempfile(), which uses O_EXCL).
+ : >.git/packed-refs.new &&
+ test_when_finished "rm -f .git/packed-refs.new" &&
+ test_must_fail git update-ref -d $prefix/foo &&
+ git for-each-ref $prefix >actual &&
+ test_cmp unchanged actual
+'
+
+RWT="test-tool ref-store worktree:wt"
+RMAIN="test-tool ref-store worktree:main"
+
+test_expect_success 'setup worktree' '
+ test_commit first &&
+ git worktree add -b wt-main wt &&
+ (
+ cd wt &&
+ test_commit second
+ )
+'
+
+# Some refs (refs/bisect/*, pseudorefs) are kept per worktree, so they should
+# only appear in the for-each-reflog output if it is called from the correct
+# worktree, which is exercised in this test. This test is poorly written for
+# mulitple reasons: 1) it creates invalidly formatted log entres. 2) it uses
+# direct FS access for creating the reflogs. 3) PSEUDO-WT and refs/bisect/random
+# do not create reflogs by default, so it is not testing a realistic scenario.
+test_expect_success 'for_each_reflog()' '
+ echo $ZERO_OID >.git/logs/PSEUDO_MAIN_HEAD &&
+ mkdir -p .git/logs/refs/bisect &&
+ echo $ZERO_OID >.git/logs/refs/bisect/random &&
+
+ echo $ZERO_OID >.git/worktrees/wt/logs/PSEUDO_WT_HEAD &&
+ mkdir -p .git/worktrees/wt/logs/refs/bisect &&
+ echo $ZERO_OID >.git/worktrees/wt/logs/refs/bisect/wt-random &&
+
+ $RWT for-each-reflog >actual &&
+ cat >expected <<-\EOF &&
+ HEAD
+ PSEUDO_WT_HEAD
+ refs/bisect/wt-random
+ refs/heads/main
+ refs/heads/wt-main
+ EOF
+ test_cmp expected actual &&
+
+ $RMAIN for-each-reflog >actual &&
+ cat >expected <<-\EOF &&
+ HEAD
+ PSEUDO_MAIN_HEAD
+ refs/bisect/random
+ refs/heads/main
+ refs/heads/wt-main
+ EOF
+ test_cmp expected actual
+'
+
+# Triggering the bug detected by this test requires a newline to fall
+# exactly BUFSIZ-1 bytes from the end of the file. We don't know
+# what that value is, since it's platform dependent. However, if
+# we choose some value N, we also catch any D which divides N evenly
+# (since we will read backwards in chunks of D). So we choose 8K,
+# which catches glibc (with an 8K BUFSIZ) and *BSD (1K).
+#
+# Each line is 114 characters, so we need 75 to still have a few before the
+# last 8K. The 89-character padding on the final entry lines up our
+# newline exactly.
+test_expect_success SHA1 'parsing reverse reflogs at BUFSIZ boundaries' '
+ git checkout -b reflogskip &&
+ zf=$(test_oid zero_2) &&
+ ident="abc <xyz> 0000000001 +0000" &&
+ for i in $(test_seq 1 75); do
+ printf "$zf%02d $zf%02d %s\t" $i $(($i+1)) "$ident" &&
+ if test $i = 75; then
+ for j in $(test_seq 1 89); do
+ printf X || return 1
+ done
+ else
+ printf X
+ fi &&
+ printf "\n" || return 1
+ done >.git/logs/refs/heads/reflogskip &&
+ git rev-parse reflogskip@{73} >actual &&
+ echo ${zf}03 >expect &&
+ test_cmp expect actual
+'
+
+# This test takes a lock on an individual ref; this is not supported in
+# reftable.
+test_expect_success 'reflog expire operates on symref not 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" &&
+ touch .git/refs/heads/referrent.lock &&
+ git reflog expire --expire=all the_symref
+'
+
+test_expect_success 'empty reflog' '
+ test_when_finished "rm -rf empty" &&
+ git init empty &&
+ test_commit -C empty A &&
+ >empty/.git/logs/refs/heads/foo &&
+ git -C empty reflog expire --all 2>err &&
+ test_must_be_empty err
+'
+
+test_expect_success SYMLINKS 'ref resolution not confused by broken symlinks' '
+ ln -s does-not-exist .git/refs/heads/broken &&
+ test_must_fail git rev-parse --verify broken
+'
+
+test_expect_success 'log diagnoses bogus HEAD hash' '
+ git init empty &&
+ test_when_finished "rm -rf empty" &&
+ echo 1234abcd >empty/.git/refs/heads/main &&
+ test_must_fail git -C empty log 2>stderr &&
+ test_grep broken stderr
+'
+
+test_expect_success 'log diagnoses bogus HEAD symref' '
+ git init empty &&
+ test-tool -C empty ref-store main create-symref HEAD refs/heads/invalid.lock &&
+ test_must_fail git -C empty log 2>stderr &&
+ test_grep broken stderr &&
+ test_must_fail git -C empty log --default totally-bogus 2>stderr &&
+ test_grep broken stderr
+'
+
+test_expect_success 'empty directory removal' '
+ git branch d1/d2/r1 HEAD &&
+ git branch d1/r2 HEAD &&
+ test_path_is_file .git/refs/heads/d1/d2/r1 &&
+ test_path_is_file .git/logs/refs/heads/d1/d2/r1 &&
+ git branch -d d1/d2/r1 &&
+ test_must_fail git show-ref --verify -q refs/heads/d1/d2 &&
+ test_must_fail git show-ref --verify -q logs/refs/heads/d1/d2 &&
+ test_path_is_file .git/refs/heads/d1/r2 &&
+ test_path_is_file .git/logs/refs/heads/d1/r2
+'
+
+test_expect_success 'symref empty directory removal' '
+ git branch e1/e2/r1 HEAD &&
+ git branch e1/r2 HEAD &&
+ git checkout e1/e2/r1 &&
+ test_when_finished "git checkout main" &&
+ test_path_is_file .git/refs/heads/e1/e2/r1 &&
+ test_path_is_file .git/logs/refs/heads/e1/e2/r1 &&
+ git update-ref -d HEAD &&
+ test_must_fail git show-ref --verify -q refs/heads/e1/e2 &&
+ test_must_fail git show-ref --verify -q logs/refs/heads/e1/e2 &&
+ test_path_is_file .git/refs/heads/e1/r2 &&
+ test_path_is_file .git/logs/refs/heads/e1/r2 &&
+ test_path_is_file .git/logs/HEAD
+'
+
+test_expect_success 'directory not created deleting packed ref' '
+ git branch d1/d2/r1 HEAD &&
+ git pack-refs --all &&
+ test_path_is_missing .git/refs/heads/d1/d2 &&
+ git update-ref -d refs/heads/d1/d2/r1 &&
+ test_path_is_missing .git/refs/heads/d1/d2 &&
+ test_path_is_missing .git/refs/heads/d1
+'
+
+test_expect_success SYMLINKS 'git branch -m u v should fail when the reflog for u is a symlink' '
+ 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
+'
+
+test_expect_success SYMLINKS 'git branch -m with symlinked .git/refs' '
+ test_when_finished "rm -rf subdir" &&
+ git init --bare subdir &&
+
+ rm -rfv subdir/refs subdir/objects subdir/packed-refs &&
+ ln -s ../.git/refs subdir/refs &&
+ ln -s ../.git/objects subdir/objects &&
+ ln -s ../.git/packed-refs subdir/packed-refs &&
+
+ git -C subdir rev-parse --absolute-git-dir >subdir.dir &&
+ git rev-parse --absolute-git-dir >our.dir &&
+ ! test_cmp subdir.dir our.dir &&
+
+ git -C subdir log &&
+ git -C subdir branch rename-src &&
+ git rev-parse rename-src >expect &&
+ git -C subdir branch -m rename-src rename-dest &&
+ git rev-parse rename-dest >actual &&
+ test_cmp expect actual &&
+ git branch -D rename-dest
+'
+
+test_expect_success MINGW,SYMLINKS_WINDOWS 'rebase when .git/logs is a symlink' '
+ git checkout main &&
+ mv .git/logs actual_logs &&
+ cmd //c "mklink /D .git\logs ..\actual_logs" &&
+ git rebase -f HEAD^ &&
+ test -L .git/logs &&
+ rm .git/logs &&
+ mv actual_logs .git/logs
+'
+
+test_expect_success POSIXPERM 'git reflog expire honors core.sharedRepository' '
+ umask 077 &&
+ git config core.sharedRepository group &&
+ git reflog expire --all &&
+ actual="$(ls -l .git/logs/refs/heads/main)" &&
+ case "$actual" in
+ -rw-rw-*)
+ : happy
+ ;;
+ *)
+ echo Ooops, .git/logs/refs/heads/main is not 066x [$actual]
+ false
+ ;;
+ esac
+'
+
+test_done
diff --git a/t/t0601-reffiles-pack-refs.sh b/t/t0601-reffiles-pack-refs.sh
new file mode 100755
index 0000000..7d4ab0b
--- /dev/null
+++ b/t/t0601-reffiles-pack-refs.sh
@@ -0,0 +1,383 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Amos Waterland
+# Copyright (c) 2006 Christian Couder
+#
+
+test_description='git pack-refs should not change the branch semantic
+
+This test runs git pack-refs and git show-ref and checks that the branch
+semantic is still the same.
+'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+if ! test_have_prereq REFFILES
+then
+ skip_all='skipping reffiles specific tests'
+ test_done
+fi
+
+test_expect_success 'enable reflogs' '
+ git config core.logallrefupdates true
+'
+
+test_expect_success 'prepare a trivial repository' '
+ echo Hello > A &&
+ git update-index --add A &&
+ git commit -m "Initial commit." &&
+ HEAD=$(git rev-parse --verify HEAD)
+'
+
+test_expect_success 'pack-refs --prune --all' '
+ test_path_is_missing .git/packed-refs &&
+ git pack-refs --no-prune --all &&
+ test_path_is_file .git/packed-refs &&
+ N=$(find .git/refs -type f | wc -l) &&
+ test "$N" != 0 &&
+
+ git pack-refs --prune --all &&
+ test_path_is_file .git/packed-refs &&
+ N=$(find .git/refs -type f) &&
+ test -z "$N"
+'
+
+SHA1=
+
+test_expect_success 'see if git show-ref works as expected' '
+ git branch a &&
+ SHA1=$(cat .git/refs/heads/a) &&
+ echo "$SHA1 refs/heads/a" >expect &&
+ git show-ref a >result &&
+ test_cmp expect result
+'
+
+test_expect_success 'see if a branch still exists when packed' '
+ git branch b &&
+ git pack-refs --all &&
+ rm -f .git/refs/heads/b &&
+ echo "$SHA1 refs/heads/b" >expect &&
+ git show-ref b >result &&
+ test_cmp expect result
+'
+
+test_expect_success 'git branch c/d should barf if branch c exists' '
+ git branch c &&
+ git pack-refs --all &&
+ rm -f .git/refs/heads/c &&
+ test_must_fail git branch c/d
+'
+
+test_expect_success 'see if a branch still exists after git pack-refs --prune' '
+ git branch e &&
+ git pack-refs --all --prune &&
+ echo "$SHA1 refs/heads/e" >expect &&
+ git show-ref e >result &&
+ test_cmp expect result
+'
+
+test_expect_success 'see if git pack-refs --prune remove ref files' '
+ git branch f &&
+ git pack-refs --all --prune &&
+ ! test -f .git/refs/heads/f
+'
+
+test_expect_success 'see if git pack-refs --prune removes empty dirs' '
+ git branch r/s/t &&
+ git pack-refs --all --prune &&
+ ! test -e .git/refs/heads/r
+'
+
+test_expect_success 'git branch g should work when git branch g/h has been deleted' '
+ git branch g/h &&
+ git pack-refs --all --prune &&
+ git branch -d g/h &&
+ git branch g &&
+ git pack-refs --all &&
+ git branch -d g
+'
+
+test_expect_success 'git branch i/j/k should barf if branch i exists' '
+ git branch i &&
+ git pack-refs --all --prune &&
+ test_must_fail git branch i/j/k
+'
+
+test_expect_success 'test git branch k after branch k/l/m and k/lm have been deleted' '
+ git branch k/l &&
+ git branch k/lm &&
+ git branch -d k/l &&
+ git branch k/l/m &&
+ git branch -d k/l/m &&
+ git branch -d k/lm &&
+ git branch k
+'
+
+test_expect_success 'test git branch n after some branch deletion and pruning' '
+ git branch n/o &&
+ git branch n/op &&
+ git branch -d n/o &&
+ git branch n/o/p &&
+ git branch -d n/op &&
+ git pack-refs --all --prune &&
+ git branch -d n/o/p &&
+ git branch n
+'
+
+test_expect_success 'test excluded refs are not packed' '
+ git branch dont_pack1 &&
+ git branch dont_pack2 &&
+ git branch pack_this &&
+ git pack-refs --all --exclude "refs/heads/dont_pack*" &&
+ test -f .git/refs/heads/dont_pack1 &&
+ test -f .git/refs/heads/dont_pack2 &&
+ ! test -f .git/refs/heads/pack_this'
+
+test_expect_success 'test --no-exclude refs clears excluded refs' '
+ git branch dont_pack3 &&
+ git branch dont_pack4 &&
+ git pack-refs --all --exclude "refs/heads/dont_pack*" --no-exclude &&
+ ! test -f .git/refs/heads/dont_pack3 &&
+ ! test -f .git/refs/heads/dont_pack4'
+
+test_expect_success 'test only included refs are packed' '
+ git branch pack_this1 &&
+ git branch pack_this2 &&
+ git tag dont_pack5 &&
+ git pack-refs --include "refs/heads/pack_this*" &&
+ test -f .git/refs/tags/dont_pack5 &&
+ ! test -f .git/refs/heads/pack_this1 &&
+ ! test -f .git/refs/heads/pack_this2'
+
+test_expect_success 'test --no-include refs clears included refs' '
+ git branch pack1 &&
+ git branch pack2 &&
+ git pack-refs --include "refs/heads/pack*" --no-include &&
+ test -f .git/refs/heads/pack1 &&
+ test -f .git/refs/heads/pack2'
+
+test_expect_success 'test --exclude takes precedence over --include' '
+ git branch dont_pack5 &&
+ git pack-refs --include "refs/heads/pack*" --exclude "refs/heads/pack*" &&
+ test -f .git/refs/heads/dont_pack5'
+
+test_expect_success '--auto packs and prunes refs as usual' '
+ git branch auto &&
+ test_path_is_file .git/refs/heads/auto &&
+ git pack-refs --auto --all &&
+ test_path_is_missing .git/refs/heads/auto
+'
+
+test_expect_success 'see if up-to-date packed refs are preserved' '
+ git branch q &&
+ git pack-refs --all --prune &&
+ git update-ref refs/heads/q refs/heads/q &&
+ ! test -f .git/refs/heads/q
+'
+
+test_expect_success 'pack, prune and repack' '
+ git tag foo &&
+ git pack-refs --all --prune &&
+ git show-ref >all-of-them &&
+ git pack-refs &&
+ git show-ref >again &&
+ test_cmp all-of-them again
+'
+
+test_expect_success 'explicit pack-refs with dangling packed reference' '
+ git commit --allow-empty -m "soon to be garbage-collected" &&
+ git pack-refs --all &&
+ git reset --hard HEAD^ &&
+ git reflog expire --expire=all --all &&
+ git prune --expire=all &&
+ git pack-refs --all 2>result &&
+ test_must_be_empty result
+'
+
+test_expect_success 'delete ref with dangling packed version' '
+ git checkout -b lamb &&
+ git commit --allow-empty -m "future garbage" &&
+ git pack-refs --all &&
+ git reset --hard HEAD^ &&
+ git checkout main &&
+ git reflog expire --expire=all --all &&
+ git prune --expire=all &&
+ git branch -d lamb 2>result &&
+ test_must_be_empty result
+'
+
+test_expect_success 'delete ref while another dangling packed ref' '
+ git branch lamb &&
+ git commit --allow-empty -m "future garbage" &&
+ git pack-refs --all &&
+ git reset --hard HEAD^ &&
+ git reflog expire --expire=all --all &&
+ git prune --expire=all &&
+ git branch -d lamb 2>result &&
+ test_must_be_empty result
+'
+
+test_expect_success 'pack ref directly below refs/' '
+ git update-ref refs/top HEAD &&
+ git pack-refs --all --prune &&
+ grep refs/top .git/packed-refs &&
+ test_path_is_missing .git/refs/top
+'
+
+test_expect_success 'do not pack ref in refs/bisect' '
+ git update-ref refs/bisect/local HEAD &&
+ git pack-refs --all --prune &&
+ ! grep refs/bisect/local .git/packed-refs >/dev/null &&
+ test_path_is_file .git/refs/bisect/local
+'
+
+test_expect_success 'disable reflogs' '
+ git config core.logallrefupdates false &&
+ rm -rf .git/logs
+'
+
+test_expect_success 'create packed foo/bar/baz branch' '
+ git branch foo/bar/baz &&
+ git pack-refs --all --prune &&
+ test_path_is_missing .git/refs/heads/foo/bar/baz &&
+ test_must_fail git reflog exists refs/heads/foo/bar/baz
+'
+
+test_expect_success 'notice d/f conflict with existing directory' '
+ test_must_fail git branch foo &&
+ test_must_fail git branch foo/bar
+'
+
+test_expect_success 'existing directory reports concrete ref' '
+ test_must_fail git branch foo 2>stderr &&
+ test_grep refs/heads/foo/bar/baz stderr
+'
+
+test_expect_success 'notice d/f conflict with existing ref' '
+ test_must_fail git branch foo/bar/baz/extra &&
+ test_must_fail git branch foo/bar/baz/lots/of/extra/components
+'
+
+test_expect_success 'reject packed-refs with unterminated line' '
+ cp .git/packed-refs .git/packed-refs.bak &&
+ test_when_finished "mv .git/packed-refs.bak .git/packed-refs" &&
+ printf "%s" "$HEAD refs/zzzzz" >>.git/packed-refs &&
+ echo "fatal: unterminated line in .git/packed-refs: $HEAD refs/zzzzz" >expected_err &&
+ test_must_fail git for-each-ref >out 2>err &&
+ test_cmp expected_err err
+'
+
+test_expect_success 'reject packed-refs containing junk' '
+ cp .git/packed-refs .git/packed-refs.bak &&
+ test_when_finished "mv .git/packed-refs.bak .git/packed-refs" &&
+ printf "%s\n" "bogus content" >>.git/packed-refs &&
+ echo "fatal: unexpected line in .git/packed-refs: bogus content" >expected_err &&
+ test_must_fail git for-each-ref >out 2>err &&
+ test_cmp expected_err err
+'
+
+test_expect_success 'reject packed-refs with a short SHA-1' '
+ cp .git/packed-refs .git/packed-refs.bak &&
+ test_when_finished "mv .git/packed-refs.bak .git/packed-refs" &&
+ printf "%.7s %s\n" $HEAD refs/zzzzz >>.git/packed-refs &&
+ printf "fatal: unexpected line in .git/packed-refs: %.7s %s\n" $HEAD refs/zzzzz >expected_err &&
+ test_must_fail git for-each-ref >out 2>err &&
+ test_cmp expected_err err
+'
+
+test_expect_success 'timeout if packed-refs.lock exists' '
+ LOCK=.git/packed-refs.lock &&
+ >"$LOCK" &&
+ test_when_finished "rm -f $LOCK" &&
+ test_must_fail git pack-refs --all --prune
+'
+
+test_expect_success 'retry acquiring packed-refs.lock' '
+ LOCK=.git/packed-refs.lock &&
+ >"$LOCK" &&
+ test_when_finished "wait && rm -f $LOCK" &&
+ {
+ ( sleep 1 && rm -f $LOCK ) &
+ } &&
+ git -c core.packedrefstimeout=3000 pack-refs --all --prune
+'
+
+test_expect_success SYMLINKS 'pack symlinked packed-refs' '
+ # First make sure that symlinking works when reading:
+ git update-ref refs/heads/lossy refs/heads/main &&
+ git for-each-ref >all-refs-before &&
+ mv .git/packed-refs .git/my-deviant-packed-refs &&
+ ln -s my-deviant-packed-refs .git/packed-refs &&
+ git for-each-ref >all-refs-linked &&
+ test_cmp all-refs-before all-refs-linked &&
+ git pack-refs --all --prune &&
+ git for-each-ref >all-refs-packed &&
+ test_cmp all-refs-before all-refs-packed &&
+ test -h .git/packed-refs &&
+ test "$(test_readlink .git/packed-refs)" = "my-deviant-packed-refs"
+'
+
+# The 'packed-refs' file is stored directly in .git/. This means it is global
+# to the repository, and can only contain refs that are shared across all
+# worktrees.
+test_expect_success 'refs/worktree must not be packed' '
+ test_commit initial &&
+ test_commit wt1 &&
+ test_commit wt2 &&
+ git worktree add wt1 wt1 &&
+ git worktree add wt2 wt2 &&
+ git checkout initial &&
+ git update-ref refs/worktree/foo HEAD &&
+ git -C wt1 update-ref refs/worktree/foo HEAD &&
+ git -C wt2 update-ref refs/worktree/foo HEAD &&
+ git pack-refs --all &&
+ test_path_is_missing .git/refs/tags/wt1 &&
+ test_path_is_file .git/refs/worktree/foo &&
+ test_path_is_file .git/worktrees/wt1/refs/worktree/foo &&
+ test_path_is_file .git/worktrees/wt2/refs/worktree/foo
+'
+
+# we do not want to count on running pack-refs to
+# actually pack it, as it is perfectly reasonable to
+# skip processing a broken ref
+test_expect_success 'create packed-refs file with broken ref' '
+ test_tick && git commit --allow-empty -m one &&
+ recoverable=$(git rev-parse HEAD) &&
+ test_tick && git commit --allow-empty -m two &&
+ missing=$(git rev-parse HEAD) &&
+ rm -f .git/refs/heads/main &&
+ cat >.git/packed-refs <<-EOF &&
+ $missing refs/heads/main
+ $recoverable refs/heads/other
+ EOF
+ echo $missing >expect &&
+ git rev-parse refs/heads/main >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'pack-refs does not silently delete broken packed ref' '
+ git pack-refs --all --prune &&
+ git rev-parse refs/heads/main >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'pack-refs does not drop broken refs during deletion' '
+ git update-ref -d refs/heads/other &&
+ git rev-parse refs/heads/main >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'maintenance --auto unconditionally packs loose refs' '
+ git update-ref refs/heads/something HEAD &&
+ test_path_is_file .git/refs/heads/something &&
+ git rev-parse refs/heads/something >expect &&
+ git maintenance run --task=pack-refs --auto &&
+ test_path_is_missing .git/refs/heads/something &&
+ git rev-parse refs/heads/something >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t0610-reftable-basics.sh b/t/t0610-reftable-basics.sh
new file mode 100755
index 0000000..178791e
--- /dev/null
+++ b/t/t0610-reftable-basics.sh
@@ -0,0 +1,1032 @@
+#!/bin/sh
+#
+# Copyright (c) 2020 Google LLC
+#
+
+test_description='reftable basics'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+if ! test_have_prereq REFTABLE
+then
+ skip_all='skipping reftable tests; set GIT_TEST_DEFAULT_REF_FORMAT=reftable'
+ test_done
+fi
+
+INVALID_OID=$(test_oid 001)
+
+test_expect_success 'init: creates basic reftable structures' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_path_is_dir repo/.git/reftable &&
+ test_path_is_file repo/.git/reftable/tables.list &&
+ echo reftable >expect &&
+ git -C repo rev-parse --show-ref-format >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'init: sha256 object format via environment variable' '
+ test_when_finished "rm -rf repo" &&
+ GIT_DEFAULT_HASH=sha256 git init repo &&
+ cat >expect <<-EOF &&
+ sha256
+ reftable
+ EOF
+ git -C repo rev-parse --show-object-format --show-ref-format >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'init: sha256 object format via option' '
+ test_when_finished "rm -rf repo" &&
+ git init --object-format=sha256 repo &&
+ cat >expect <<-EOF &&
+ sha256
+ reftable
+ EOF
+ git -C repo rev-parse --show-object-format --show-ref-format >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'init: reinitializing reftable backend succeeds' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_commit -C repo A &&
+
+ git -C repo for-each-ref >expect &&
+ git init --ref-format=reftable repo &&
+ git -C repo for-each-ref >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'init: reinitializing files with reftable backend fails' '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=files repo &&
+ test_commit -C repo file &&
+
+ cp repo/.git/HEAD expect &&
+ test_must_fail git init --ref-format=reftable repo &&
+ test_cmp expect repo/.git/HEAD
+'
+
+test_expect_success 'init: reinitializing reftable with files backend fails' '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=reftable repo &&
+ test_commit -C repo file &&
+
+ cp repo/.git/HEAD expect &&
+ test_must_fail git init --ref-format=files repo &&
+ test_cmp expect repo/.git/HEAD
+'
+
+test_expect_perms () {
+ local perms="$1"
+ local file="$2"
+ local actual="$(ls -l "$file")" &&
+
+ case "$actual" in
+ $perms*)
+ : happy
+ ;;
+ *)
+ echo "$(basename $2) is not $perms but $actual"
+ false
+ ;;
+ esac
+}
+
+test_expect_reftable_perms () {
+ local umask="$1"
+ local shared="$2"
+ local expect="$3"
+
+ test_expect_success POSIXPERM "init: honors --shared=$shared with umask $umask" '
+ test_when_finished "rm -rf repo" &&
+ (
+ umask $umask &&
+ git init --shared=$shared repo
+ ) &&
+ test_expect_perms "$expect" repo/.git/reftable/tables.list &&
+ for table in repo/.git/reftable/*.ref
+ do
+ test_expect_perms "$expect" "$table" ||
+ return 1
+ done
+ '
+
+ test_expect_success POSIXPERM "pack-refs: honors --shared=$shared with umask $umask" '
+ test_when_finished "rm -rf repo" &&
+ (
+ umask $umask &&
+ git init --shared=$shared repo &&
+ test_commit -C repo A &&
+ test_line_count = 2 repo/.git/reftable/tables.list &&
+ git -C repo pack-refs
+ ) &&
+ test_expect_perms "$expect" repo/.git/reftable/tables.list &&
+ for table in repo/.git/reftable/*.ref
+ do
+ test_expect_perms "$expect" "$table" ||
+ return 1
+ done
+ '
+}
+
+test_expect_reftable_perms 002 umask "-rw-rw-r--"
+test_expect_reftable_perms 022 umask "-rw-r--r--"
+test_expect_reftable_perms 027 umask "-rw-r-----"
+
+test_expect_reftable_perms 002 group "-rw-rw-r--"
+test_expect_reftable_perms 022 group "-rw-rw-r--"
+test_expect_reftable_perms 027 group "-rw-rw----"
+
+test_expect_reftable_perms 002 world "-rw-rw-r--"
+test_expect_reftable_perms 022 world "-rw-rw-r--"
+test_expect_reftable_perms 027 world "-rw-rw-r--"
+
+test_expect_success 'clone: can clone reftable repository' '
+ test_when_finished "rm -rf repo clone" &&
+ git init repo &&
+ test_commit -C repo message1 file1 &&
+
+ git clone repo cloned &&
+ echo reftable >expect &&
+ git -C cloned rev-parse --show-ref-format >actual &&
+ test_cmp expect actual &&
+ test_path_is_file cloned/file1
+'
+
+test_expect_success 'clone: can clone reffiles into reftable repository' '
+ test_when_finished "rm -rf reffiles reftable" &&
+ git init --ref-format=files reffiles &&
+ test_commit -C reffiles A &&
+ git clone --ref-format=reftable ./reffiles reftable &&
+
+ git -C reffiles rev-parse HEAD >expect &&
+ git -C reftable rev-parse HEAD >actual &&
+ test_cmp expect actual &&
+
+ git -C reftable rev-parse --show-ref-format >actual &&
+ echo reftable >expect &&
+ test_cmp expect actual &&
+
+ git -C reffiles rev-parse --show-ref-format >actual &&
+ echo files >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'clone: can clone reftable into reffiles repository' '
+ test_when_finished "rm -rf reffiles reftable" &&
+ git init --ref-format=reftable reftable &&
+ test_commit -C reftable A &&
+ git clone --ref-format=files ./reftable reffiles &&
+
+ git -C reftable rev-parse HEAD >expect &&
+ git -C reffiles rev-parse HEAD >actual &&
+ test_cmp expect actual &&
+
+ git -C reftable rev-parse --show-ref-format >actual &&
+ echo reftable >expect &&
+ test_cmp expect actual &&
+
+ git -C reffiles rev-parse --show-ref-format >actual &&
+ echo files >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'ref transaction: corrupted tables cause failure' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit file1 &&
+ for f in .git/reftable/*.ref
+ do
+ : >"$f" || return 1
+ done &&
+ test_must_fail git update-ref refs/heads/main HEAD
+ )
+'
+
+test_expect_success 'ref transaction: corrupted tables.list cause failure' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit file1 &&
+ echo garbage >.git/reftable/tables.list &&
+ test_must_fail git update-ref refs/heads/main HEAD
+ )
+'
+
+test_expect_success 'ref transaction: refuses to write ref causing F/D conflict' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_commit -C repo file &&
+ test_must_fail git -C repo update-ref refs/heads/main/forbidden
+'
+
+test_expect_success 'ref transaction: deleting ref with invalid name fails' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_commit -C repo file &&
+ test_must_fail git -C repo update-ref -d ../../my-private-file
+'
+
+test_expect_success 'ref transaction: can skip object ID verification' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_must_fail test-tool -C repo ref-store main update-ref msg refs/heads/branch $INVALID_OID $ZERO_OID 0 &&
+ test-tool -C repo ref-store main update-ref msg refs/heads/branch $INVALID_OID $ZERO_OID REF_SKIP_OID_VERIFICATION
+'
+
+test_expect_success 'ref transaction: updating same ref multiple times fails' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_commit -C repo A &&
+ cat >updates <<-EOF &&
+ update refs/heads/main $A
+ update refs/heads/main $A
+ EOF
+ cat >expect <<-EOF &&
+ fatal: multiple updates for ref ${SQ}refs/heads/main${SQ} not allowed
+ EOF
+ test_must_fail git -C repo update-ref --stdin <updates 2>err &&
+ test_cmp expect err
+'
+
+test_expect_success 'ref transaction: can delete symbolic self-reference with git-symbolic-ref(1)' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ git -C repo symbolic-ref refs/heads/self refs/heads/self &&
+ git -C repo symbolic-ref -d refs/heads/self
+'
+
+test_expect_success 'ref transaction: deleting symbolic self-reference without --no-deref fails' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ git -C repo symbolic-ref refs/heads/self refs/heads/self &&
+ cat >expect <<-EOF &&
+ error: multiple updates for ${SQ}refs/heads/self${SQ} (including one via symref ${SQ}refs/heads/self${SQ}) are not allowed
+ EOF
+ test_must_fail git -C repo update-ref -d refs/heads/self 2>err &&
+ test_cmp expect err
+'
+
+test_expect_success 'ref transaction: deleting symbolic self-reference with --no-deref succeeds' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ git -C repo symbolic-ref refs/heads/self refs/heads/self &&
+ git -C repo update-ref -d --no-deref refs/heads/self
+'
+
+test_expect_success 'ref transaction: creating symbolic ref fails with F/D conflict' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_commit -C repo A &&
+ cat >expect <<-EOF &&
+ error: unable to write symref for refs/heads: file/directory conflict
+ EOF
+ test_must_fail git -C repo symbolic-ref refs/heads refs/heads/foo 2>err &&
+ test_cmp expect err
+'
+
+test_expect_success 'ref transaction: ref deletion' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit file &&
+ HEAD_OID=$(git show-ref -s --verify HEAD) &&
+ cat >expect <<-EOF &&
+ $HEAD_OID refs/heads/main
+ $HEAD_OID refs/tags/file
+ EOF
+ git show-ref >actual &&
+ test_cmp expect actual &&
+
+ test_must_fail git update-ref -d refs/tags/file $INVALID_OID &&
+ git show-ref >actual &&
+ test_cmp expect actual &&
+
+ git update-ref -d refs/tags/file $HEAD_OID &&
+ echo "$HEAD_OID refs/heads/main" >expect &&
+ git show-ref >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'ref transaction: writes cause auto-compaction' '
+ test_when_finished "rm -rf repo" &&
+
+ git init repo &&
+ test_line_count = 1 repo/.git/reftable/tables.list &&
+
+ test_commit -C repo --no-tag A &&
+ test_line_count = 1 repo/.git/reftable/tables.list &&
+
+ test_commit -C repo --no-tag B &&
+ test_line_count = 1 repo/.git/reftable/tables.list
+'
+
+test_expect_success 'ref transaction: env var disables compaction' '
+ test_when_finished "rm -rf repo" &&
+
+ git init repo &&
+ test_commit -C repo A &&
+
+ start=$(wc -l <repo/.git/reftable/tables.list) &&
+ iterations=5 &&
+ expected=$((start + iterations)) &&
+
+ for i in $(test_seq $iterations)
+ do
+ GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
+ git -C repo update-ref branch-$i HEAD || return 1
+ done &&
+ test_line_count = $expected repo/.git/reftable/tables.list &&
+
+ git -C repo update-ref foo HEAD &&
+ test_line_count -lt $expected repo/.git/reftable/tables.list
+'
+
+test_expect_success 'ref transaction: alternating table sizes are compacted' '
+ test_when_finished "rm -rf repo" &&
+
+ git init repo &&
+ test_commit -C repo A &&
+ for i in $(test_seq 5)
+ do
+ git -C repo branch -f foo &&
+ git -C repo branch -d foo || return 1
+ done &&
+ test_line_count = 2 repo/.git/reftable/tables.list
+'
+
+check_fsync_events () {
+ local trace="$1" &&
+ shift &&
+
+ cat >expect &&
+ sed -n \
+ -e '/^{"event":"counter",.*"category":"fsync",/ {
+ s/.*"category":"fsync",//;
+ s/}$//;
+ p;
+ }' \
+ <"$trace" >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'ref transaction: writes are synced' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_commit -C repo initial &&
+
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+ GIT_TEST_FSYNC=true \
+ git -C repo -c core.fsync=reference \
+ -c core.fsyncMethod=fsync update-ref refs/heads/branch HEAD &&
+ check_fsync_events trace2.txt <<-EOF
+ "name":"hardware-flush","count":4
+ EOF
+'
+
+test_expect_success 'ref transaction: empty transaction in empty repo' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_commit -C repo --no-tag A &&
+ git -C repo update-ref -d refs/heads/main &&
+ test-tool -C repo ref-store main delete-refs REF_NO_DEREF msg HEAD &&
+ git -C repo update-ref --stdin <<-EOF
+ prepare
+ commit
+ EOF
+'
+
+test_expect_success 'ref transaction: fails gracefully when auto compaction fails' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ test_commit A &&
+ for i in $(test_seq 10)
+ do
+ git branch branch-$i &&
+ for table in .git/reftable/*.ref
+ do
+ touch "$table.lock" || exit 1
+ done ||
+ exit 1
+ done &&
+ test_line_count = 10 .git/reftable/tables.list
+ )
+'
+
+test_expect_success 'pack-refs: compacts tables' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+
+ test_commit -C repo A &&
+ ls -1 repo/.git/reftable >table-files &&
+ test_line_count = 3 table-files &&
+ test_line_count = 2 repo/.git/reftable/tables.list &&
+
+ git -C repo pack-refs &&
+ ls -1 repo/.git/reftable >table-files &&
+ test_line_count = 2 table-files &&
+ test_line_count = 1 repo/.git/reftable/tables.list
+'
+
+test_expect_success 'pack-refs: compaction raises locking errors' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_commit -C repo A &&
+ touch repo/.git/reftable/tables.list.lock &&
+ cat >expect <<-EOF &&
+ error: unable to compact stack: data is locked
+ EOF
+ test_must_fail git -C repo pack-refs 2>err &&
+ test_cmp expect err
+'
+
+for command in pack-refs gc "maintenance run --task=pack-refs"
+do
+test_expect_success "$command: auto compaction" '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ test_commit A &&
+
+ # We need a bit of setup to ensure that git-gc(1) actually
+ # triggers, and that it does not write anything to the refdb.
+ git config gc.auto 1 &&
+ git config gc.autoDetach 0 &&
+ git config gc.reflogExpire never &&
+ git config gc.reflogExpireUnreachable never &&
+ test_oid blob17_1 | git hash-object -w --stdin &&
+
+ # The tables should have been auto-compacted, and thus auto
+ # compaction should not have to do anything.
+ ls -1 .git/reftable >tables-expect &&
+ test_line_count = 3 tables-expect &&
+ git $command --auto &&
+ ls -1 .git/reftable >tables-actual &&
+ test_cmp tables-expect tables-actual &&
+
+ test_oid blob17_2 | git hash-object -w --stdin &&
+
+ # Lock all tables write some refs. Auto-compaction will be
+ # unable to compact tables and thus fails gracefully, leaving
+ # the stack in a sub-optimal state.
+ ls .git/reftable/*.ref |
+ while read table
+ do
+ touch "$table.lock" || exit 1
+ done &&
+ git branch B &&
+ git branch C &&
+ rm .git/reftable/*.lock &&
+ test_line_count = 4 .git/reftable/tables.list &&
+
+ git $command --auto &&
+ test_line_count = 1 .git/reftable/tables.list
+ )
+'
+done
+
+test_expect_success 'pack-refs: prunes stale tables' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ touch repo/.git/reftable/stale-table.ref &&
+ git -C repo pack-refs &&
+ test_path_is_missing repo/.git/reftable/stable-ref.ref
+'
+
+test_expect_success 'pack-refs: does not prune non-table files' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ touch repo/.git/reftable/garbage &&
+ git -C repo pack-refs &&
+ test_path_is_file repo/.git/reftable/garbage
+'
+
+test_expect_success 'packed-refs: writes are synced' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_commit -C repo initial &&
+ test_line_count = 2 table-files &&
+
+ : >trace2.txt &&
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+ GIT_TEST_FSYNC=true \
+ git -C repo -c core.fsync=reference \
+ -c core.fsyncMethod=fsync pack-refs &&
+ check_fsync_events trace2.txt <<-EOF
+ "name":"hardware-flush","count":2
+ EOF
+'
+
+test_expect_success 'ref iterator: bogus names are flagged' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit --no-tag file &&
+ test-tool ref-store main update-ref msg "refs/heads/bogus..name" $(git rev-parse HEAD) $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+
+ cat >expect <<-EOF &&
+ $ZERO_OID refs/heads/bogus..name 0xc
+ $(git rev-parse HEAD) refs/heads/main 0x0
+ EOF
+ test-tool ref-store main for-each-ref "" >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'ref iterator: missing object IDs are not flagged' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test-tool ref-store main update-ref msg "refs/heads/broken-hash" $INVALID_OID $ZERO_OID REF_SKIP_OID_VERIFICATION &&
+
+ cat >expect <<-EOF &&
+ $INVALID_OID refs/heads/broken-hash 0x0
+ EOF
+ test-tool ref-store main for-each-ref "" >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'basic: commit and list refs' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_commit -C repo file &&
+ test_write_lines refs/heads/main refs/tags/file >expect &&
+ git -C repo for-each-ref --format="%(refname)" >actual &&
+ test_cmp actual expect
+'
+
+test_expect_success 'basic: can write large commit message' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ perl -e "
+ print \"this is a long commit message\" x 50000
+ " >commit-msg &&
+ git -C repo commit --allow-empty --file=../commit-msg
+'
+
+test_expect_success 'basic: show-ref fails with empty repository' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_must_fail git -C repo show-ref >actual &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'basic: can check out unborn branch' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ git -C repo checkout -b main
+'
+
+test_expect_success 'basic: peeled tags are stored' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_commit -C repo file &&
+ git -C repo tag -m "annotated tag" test_tag HEAD &&
+ for ref in refs/heads/main refs/tags/file refs/tags/test_tag refs/tags/test_tag^{}
+ do
+ echo "$(git -C repo rev-parse "$ref") $ref" || return 1
+ done >expect &&
+ git -C repo show-ref -d >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'basic: for-each-ref can print symrefs' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit file &&
+ git branch &&
+ git symbolic-ref refs/heads/sym refs/heads/main &&
+ cat >expected <<-EOF &&
+ refs/heads/main
+ EOF
+ git for-each-ref --format="%(symref)" refs/heads/sym >actual &&
+ test_cmp expected actual
+ )
+'
+
+test_expect_success 'basic: notes' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ write_script fake_editor <<-\EOF &&
+ echo "$MSG" >"$1"
+ echo "$MSG" >&2
+ EOF
+
+ test_commit 1st &&
+ test_commit 2nd &&
+ GIT_EDITOR=./fake_editor MSG=b4 git notes add &&
+ GIT_EDITOR=./fake_editor MSG=b3 git notes edit &&
+ echo b4 >expect &&
+ git notes --ref commits@{1} show >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'basic: stash' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit file &&
+ git stash list >expect &&
+ test_line_count = 0 expect &&
+
+ echo hoi >>file.t &&
+ git stash push -m stashed &&
+ git stash list >expect &&
+ test_line_count = 1 expect &&
+
+ git stash clear &&
+ git stash list >expect &&
+ test_line_count = 0 expect
+ )
+'
+
+test_expect_success 'basic: cherry-pick' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit message1 file1 &&
+ test_commit message2 file2 &&
+ git branch source &&
+ git checkout HEAD^ &&
+ test_commit message3 file3 &&
+ git cherry-pick source &&
+ test_path_is_file file2
+ )
+'
+
+test_expect_success 'basic: rebase' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit message1 file1 &&
+ test_commit message2 file2 &&
+ git branch source &&
+ git checkout HEAD^ &&
+ test_commit message3 file3 &&
+ git rebase source &&
+ test_path_is_file file2
+ )
+'
+
+test_expect_success 'reflog: can delete separate reflog entries' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ test_commit file &&
+ test_commit file2 &&
+ test_commit file3 &&
+ test_commit file4 &&
+ git reflog >actual &&
+ grep file3 actual &&
+
+ git reflog delete HEAD@{1} &&
+ git reflog >actual &&
+ ! grep file3 actual
+ )
+'
+
+test_expect_success 'reflog: can switch to previous branch' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit file1 &&
+ git checkout -b branch1 &&
+ test_commit file2 &&
+ git checkout -b branch2 &&
+ git switch - &&
+ git rev-parse --symbolic-full-name HEAD >actual &&
+ echo refs/heads/branch1 >expect &&
+ test_cmp actual expect
+ )
+'
+
+test_expect_success 'reflog: copying branch writes reflog entry' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit file1 &&
+ test_commit file2 &&
+ oid=$(git rev-parse --short HEAD) &&
+ git branch src &&
+ cat >expect <<-EOF &&
+ ${oid} dst@{0}: Branch: copied refs/heads/src to refs/heads/dst
+ ${oid} dst@{1}: branch: Created from main
+ EOF
+ git branch -c src dst &&
+ git reflog dst >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'reflog: renaming branch writes reflog entry' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ git symbolic-ref HEAD refs/heads/before &&
+ test_commit file &&
+ git show-ref >expected.refs &&
+ sed s/before/after/g <expected.refs >expected &&
+ git branch -M after &&
+ git show-ref >actual &&
+ test_cmp expected actual &&
+ echo refs/heads/after >expected &&
+ git symbolic-ref HEAD >actual &&
+ test_cmp expected actual
+ )
+'
+
+test_expect_success 'reflog: can store empty logs' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ test_must_fail test-tool ref-store main reflog-exists refs/heads/branch &&
+ test-tool ref-store main create-reflog refs/heads/branch &&
+ test-tool ref-store main reflog-exists refs/heads/branch &&
+ test-tool ref-store main for-each-reflog-ent-reverse refs/heads/branch >actual &&
+ test_must_be_empty actual
+ )
+'
+
+test_expect_success 'reflog: expiry empties reflog' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ test_commit initial &&
+ git checkout -b branch &&
+ test_commit fileA &&
+ test_commit fileB &&
+
+ cat >expect <<-EOF &&
+ commit: fileB
+ commit: fileA
+ branch: Created from HEAD
+ EOF
+ git reflog show --format="%gs" refs/heads/branch >actual &&
+ test_cmp expect actual &&
+
+ git reflog expire branch --expire=all &&
+ git reflog show --format="%gs" refs/heads/branch >actual &&
+ test_must_be_empty actual &&
+ test-tool ref-store main reflog-exists refs/heads/branch
+ )
+'
+
+test_expect_success 'reflog: can be deleted' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ test-tool ref-store main reflog-exists refs/heads/main &&
+ test-tool ref-store main delete-reflog refs/heads/main &&
+ test_must_fail test-tool ref-store main reflog-exists refs/heads/main
+ )
+'
+
+test_expect_success 'reflog: garbage collection deletes reflog entries' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ for count in $(test_seq 1 10)
+ do
+ test_commit "number $count" file.t $count number-$count ||
+ return 1
+ done &&
+ git reflog refs/heads/main >actual &&
+ test_line_count = 10 actual &&
+ grep "commit (initial): number 1" actual &&
+ grep "commit: number 10" actual &&
+
+ git gc &&
+ git reflog refs/heads/main >actual &&
+ test_line_count = 0 actual
+ )
+'
+
+test_expect_success 'reflog: updates via HEAD update HEAD reflog' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit main-one &&
+ git checkout -b new-branch &&
+ test_commit new-one &&
+ test_commit new-two &&
+
+ echo new-one >expect &&
+ git log -1 --format=%s HEAD@{1} >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'worktree: adding worktree creates separate stack' '
+ test_when_finished "rm -rf repo worktree" &&
+ git init repo &&
+ test_commit -C repo A &&
+
+ git -C repo worktree add ../worktree &&
+ test_path_is_file repo/.git/worktrees/worktree/refs/heads &&
+ echo "ref: refs/heads/.invalid" >expect &&
+ test_cmp expect repo/.git/worktrees/worktree/HEAD &&
+ test_path_is_dir repo/.git/worktrees/worktree/reftable &&
+ test_path_is_file repo/.git/worktrees/worktree/reftable/tables.list
+'
+
+test_expect_success 'worktree: pack-refs in main repo packs main refs' '
+ test_when_finished "rm -rf repo worktree" &&
+ git init repo &&
+ test_commit -C repo A &&
+
+ GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
+ git -C repo worktree add ../worktree &&
+ GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
+ git -C worktree update-ref refs/worktree/per-worktree HEAD &&
+
+ test_line_count = 4 repo/.git/worktrees/worktree/reftable/tables.list &&
+ test_line_count = 3 repo/.git/reftable/tables.list &&
+ git -C repo pack-refs &&
+ test_line_count = 4 repo/.git/worktrees/worktree/reftable/tables.list &&
+ test_line_count = 1 repo/.git/reftable/tables.list
+'
+
+test_expect_success 'worktree: pack-refs in worktree packs worktree refs' '
+ test_when_finished "rm -rf repo worktree" &&
+ git init repo &&
+ test_commit -C repo A &&
+
+ GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
+ git -C repo worktree add ../worktree &&
+ GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
+ git -C worktree update-ref refs/worktree/per-worktree HEAD &&
+
+ test_line_count = 4 repo/.git/worktrees/worktree/reftable/tables.list &&
+ test_line_count = 3 repo/.git/reftable/tables.list &&
+ git -C worktree pack-refs &&
+ test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
+ test_line_count = 3 repo/.git/reftable/tables.list
+'
+
+test_expect_success 'worktree: creating shared ref updates main stack' '
+ test_when_finished "rm -rf repo worktree" &&
+ git init repo &&
+ test_commit -C repo A &&
+
+ git -C repo worktree add ../worktree &&
+ git -C repo pack-refs &&
+ git -C worktree pack-refs &&
+ test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
+ test_line_count = 1 repo/.git/reftable/tables.list &&
+
+ GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
+ git -C worktree update-ref refs/heads/shared HEAD &&
+ test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
+ test_line_count = 2 repo/.git/reftable/tables.list
+'
+
+test_expect_success 'worktree: creating per-worktree ref updates worktree stack' '
+ test_when_finished "rm -rf repo worktree" &&
+ git init repo &&
+ test_commit -C repo A &&
+
+ git -C repo worktree add ../worktree &&
+ git -C repo pack-refs &&
+ git -C worktree pack-refs &&
+ test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
+ test_line_count = 1 repo/.git/reftable/tables.list &&
+
+ git -C worktree update-ref refs/bisect/per-worktree HEAD &&
+ test_line_count = 2 repo/.git/worktrees/worktree/reftable/tables.list &&
+ test_line_count = 1 repo/.git/reftable/tables.list
+'
+
+test_expect_success 'worktree: creating per-worktree ref from main repo' '
+ test_when_finished "rm -rf repo worktree" &&
+ git init repo &&
+ test_commit -C repo A &&
+
+ git -C repo worktree add ../worktree &&
+ git -C repo pack-refs &&
+ git -C worktree pack-refs &&
+ test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
+ test_line_count = 1 repo/.git/reftable/tables.list &&
+
+ git -C repo update-ref worktrees/worktree/refs/bisect/per-worktree HEAD &&
+ test_line_count = 2 repo/.git/worktrees/worktree/reftable/tables.list &&
+ test_line_count = 1 repo/.git/reftable/tables.list
+'
+
+test_expect_success 'worktree: creating per-worktree ref from second worktree' '
+ test_when_finished "rm -rf repo wt1 wt2" &&
+ git init repo &&
+ test_commit -C repo A &&
+
+ git -C repo worktree add ../wt1 &&
+ git -C repo worktree add ../wt2 &&
+ git -C repo pack-refs &&
+ git -C wt1 pack-refs &&
+ git -C wt2 pack-refs &&
+ test_line_count = 1 repo/.git/worktrees/wt1/reftable/tables.list &&
+ test_line_count = 1 repo/.git/worktrees/wt2/reftable/tables.list &&
+ test_line_count = 1 repo/.git/reftable/tables.list &&
+
+ git -C wt1 update-ref worktrees/wt2/refs/bisect/per-worktree HEAD &&
+ test_line_count = 1 repo/.git/worktrees/wt1/reftable/tables.list &&
+ test_line_count = 2 repo/.git/worktrees/wt2/reftable/tables.list &&
+ test_line_count = 1 repo/.git/reftable/tables.list
+'
+
+test_expect_success 'worktree: can create shared and per-worktree ref in one transaction' '
+ test_when_finished "rm -rf repo worktree" &&
+ git init repo &&
+ test_commit -C repo A &&
+
+ git -C repo worktree add ../worktree &&
+ git -C repo pack-refs &&
+ git -C worktree pack-refs &&
+ test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
+ test_line_count = 1 repo/.git/reftable/tables.list &&
+
+ cat >stdin <<-EOF &&
+ create worktrees/worktree/refs/bisect/per-worktree HEAD
+ create refs/branches/shared HEAD
+ EOF
+ git -C repo update-ref --stdin <stdin &&
+ test_line_count = 2 repo/.git/worktrees/worktree/reftable/tables.list &&
+ test_line_count = 2 repo/.git/reftable/tables.list
+'
+
+test_expect_success 'worktree: can access common refs' '
+ test_when_finished "rm -rf repo worktree" &&
+ git init repo &&
+ test_commit -C repo file1 &&
+ git -C repo branch branch1 &&
+ git -C repo worktree add ../worktree &&
+
+ echo refs/heads/worktree >expect &&
+ git -C worktree symbolic-ref HEAD >actual &&
+ test_cmp expect actual &&
+ git -C worktree checkout branch1
+'
+
+test_expect_success 'worktree: adds worktree with detached HEAD' '
+ test_when_finished "rm -rf repo worktree" &&
+
+ git init repo &&
+ test_commit -C repo A &&
+ git -C repo rev-parse main >expect &&
+
+ git -C repo worktree add --detach ../worktree main &&
+ git -C worktree rev-parse HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'fetch: accessing FETCH_HEAD special ref works' '
+ test_when_finished "rm -rf repo sub" &&
+
+ git init sub &&
+ test_commit -C sub two &&
+ git -C sub rev-parse HEAD >expect &&
+
+ git init repo &&
+ test_commit -C repo one &&
+ git -C repo fetch ../sub &&
+ git -C repo rev-parse FETCH_HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t0611-reftable-httpd.sh b/t/t0611-reftable-httpd.sh
new file mode 100755
index 0000000..5e05b9c
--- /dev/null
+++ b/t/t0611-reftable-httpd.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+test_description='reftable HTTPD tests'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-httpd.sh
+
+start_httpd
+
+REPO="$HTTPD_DOCUMENT_ROOT_PATH/repo"
+
+test_expect_success 'serving ls-remote' '
+ git init --ref-format=reftable -b main "$REPO" &&
+ cd "$REPO" &&
+ test_commit m1 &&
+ >.git/git-daemon-export-ok &&
+ git ls-remote "http://127.0.0.1:$LIB_HTTPD_PORT/smart/repo" | cut -f 2-2 -d " " >actual &&
+ cat >expect <<-EOF &&
+ HEAD
+ refs/heads/main
+ refs/tags/m1
+ EOF
+ test_cmp actual expect
+'
+
+test_done
diff --git a/t/t1000-read-tree-m-3way.sh b/t/t1000-read-tree-m-3way.sh
index 013c5a7..0e8c0df 100755
--- a/t/t1000-read-tree-m-3way.sh
+++ b/t/t1000-read-tree-m-3way.sh
@@ -71,6 +71,8 @@ In addition:
DF: a special case, where A makes a directory and B makes a file.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-read-tree.sh
. "$TEST_DIRECTORY"/lib-read-tree-m-3way.sh
diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh
index 1057a96..88c524f 100755
--- a/t/t1001-read-tree-m-2way.sh
+++ b/t/t1001-read-tree-m-2way.sh
@@ -20,11 +20,13 @@ In the test, these paths are used:
rezrov - in H, deleted in M
yomin - not in H or M
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-read-tree.sh
read_tree_twoway () {
- git read-tree -m "$1" "$2" && git ls-files --stage
+ git read-tree -m "$1" "$2" && git ls-files --stage
}
compare_change () {
@@ -36,11 +38,12 @@ compare_change () {
}
check_cache_at () {
- clean_if_empty=$(git diff-files -- "$1")
+ git diff-files -- "$1" >out &&
+ clean_if_empty=$(cat out) &&
case "$clean_if_empty" in
'') echo "$1: clean" ;;
?*) echo "$1: dirty" ;;
- esac
+ esac &&
case "$2,$clean_if_empty" in
clean,) : ;;
clean,?*) false ;;
@@ -367,7 +370,7 @@ test_expect_success 'read-tree supports the super-prefix' '
cat <<-EOF >expect &&
error: Updating '\''fictional/a'\'' would lose untracked files in it
EOF
- test_must_fail git --super-prefix fictional/ read-tree -u -m "$treeH" "$treeM" 2>actual &&
+ test_must_fail git read-tree --super-prefix fictional/ -u -m "$treeH" "$treeM" 2>actual &&
test_cmp expect actual
'
diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh
index 9c05f5e..a7c2ed0 100755
--- a/t/t1002-read-tree-m-u-2way.sh
+++ b/t/t1002-read-tree-m-u-2way.sh
@@ -8,6 +8,8 @@ test_description='Two way merge with read-tree -m -u $H $M
This is identical to t1001, but uses -u to update the work tree as well.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-read-tree.sh
@@ -21,11 +23,12 @@ compare_change () {
}
check_cache_at () {
- clean_if_empty=$(git diff-files -- "$1")
+ git diff-files -- "$1" >out &&
+ clean_if_empty=$(cat out) &&
case "$clean_if_empty" in
'') echo "$1: clean" ;;
?*) echo "$1: dirty" ;;
- esac
+ esac &&
case "$2,$clean_if_empty" in
clean,) : ;;
clean,?*) false ;;
@@ -34,315 +37,312 @@ check_cache_at () {
esac
}
-test_expect_success \
- setup \
- 'echo frotz >frotz &&
- echo nitfol >nitfol &&
- echo bozbar >bozbar &&
- echo rezrov >rezrov &&
- git update-index --add nitfol bozbar rezrov &&
- treeH=$(git write-tree) &&
- echo treeH $treeH &&
- git ls-tree $treeH &&
-
- echo gnusto >bozbar &&
- git update-index --add frotz bozbar --force-remove rezrov &&
- git ls-files --stage >M.out &&
- treeM=$(git write-tree) &&
- echo treeM $treeM &&
- git ls-tree $treeM &&
- cp bozbar bozbar.M &&
- cp frotz frotz.M &&
- cp nitfol nitfol.M &&
- git diff-tree $treeH $treeM'
-
-test_expect_success \
- '1, 2, 3 - no carry forward' \
- 'rm -f .git/index nitfol bozbar rezrov frotz &&
- read_tree_u_must_succeed --reset -u $treeH &&
- read_tree_u_must_succeed -m -u $treeH $treeM &&
- git ls-files --stage >1-3.out &&
- cmp M.out 1-3.out &&
- test_cmp bozbar.M bozbar &&
- test_cmp frotz.M frotz &&
- test_cmp nitfol.M nitfol &&
- check_cache_at bozbar clean &&
- check_cache_at frotz clean &&
- check_cache_at nitfol clean'
-
-test_expect_success \
- '4 - carry forward local addition.' \
- 'rm -f .git/index nitfol bozbar rezrov frotz &&
- read_tree_u_must_succeed --reset -u $treeH &&
- echo "+100644 X 0 yomin" >expected &&
- echo yomin >yomin &&
- git update-index --add yomin &&
- read_tree_u_must_succeed -m -u $treeH $treeM &&
- git ls-files --stage >4.out &&
- test_might_fail git diff -U0 --no-index M.out 4.out >4diff.out &&
- compare_change 4diff.out expected &&
- check_cache_at yomin clean &&
- test_cmp bozbar.M bozbar &&
- test_cmp frotz.M frotz &&
- test_cmp nitfol.M nitfol &&
- echo yomin >yomin1 &&
- diff yomin yomin1 &&
- rm -f yomin1'
-
-test_expect_success \
- '5 - carry forward local addition.' \
- 'rm -f .git/index nitfol bozbar rezrov frotz &&
- read_tree_u_must_succeed --reset -u $treeH &&
- read_tree_u_must_succeed -m -u $treeH &&
- echo yomin >yomin &&
- git update-index --add yomin &&
- echo yomin yomin >yomin &&
- read_tree_u_must_succeed -m -u $treeH $treeM &&
- git ls-files --stage >5.out &&
- test_might_fail git diff -U0 --no-index M.out 5.out >5diff.out &&
- compare_change 5diff.out expected &&
- check_cache_at yomin dirty &&
- test_cmp bozbar.M bozbar &&
- test_cmp frotz.M frotz &&
- test_cmp nitfol.M nitfol &&
- : dirty index should have prevented -u from checking it out. &&
- echo yomin yomin >yomin1 &&
- diff yomin yomin1 &&
- rm -f yomin1'
-
-test_expect_success \
- '6 - local addition already has the same.' \
- 'rm -f .git/index nitfol bozbar rezrov frotz &&
- read_tree_u_must_succeed --reset -u $treeH &&
- echo frotz >frotz &&
- git update-index --add frotz &&
- read_tree_u_must_succeed -m -u $treeH $treeM &&
- git ls-files --stage >6.out &&
- test_cmp M.out 6.out &&
- check_cache_at frotz clean &&
- test_cmp bozbar.M bozbar &&
- test_cmp frotz.M frotz &&
- test_cmp nitfol.M nitfol &&
- echo frotz >frotz1 &&
- diff frotz frotz1 &&
- rm -f frotz1'
-
-test_expect_success \
- '7 - local addition already has the same.' \
- 'rm -f .git/index nitfol bozbar rezrov frotz &&
- read_tree_u_must_succeed --reset -u $treeH &&
- echo frotz >frotz &&
- git update-index --add frotz &&
- echo frotz frotz >frotz &&
- read_tree_u_must_succeed -m -u $treeH $treeM &&
- git ls-files --stage >7.out &&
- test_cmp M.out 7.out &&
- check_cache_at frotz dirty &&
- test_cmp bozbar.M bozbar &&
- test_cmp nitfol.M nitfol &&
- : dirty index should have prevented -u from checking it out. &&
- echo frotz frotz >frotz1 &&
- diff frotz frotz1 &&
- rm -f frotz1'
-
-test_expect_success \
- '8 - conflicting addition.' \
- 'rm -f .git/index nitfol bozbar rezrov frotz &&
- read_tree_u_must_succeed --reset -u $treeH &&
- echo frotz frotz >frotz &&
- git update-index --add frotz &&
- if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
-
-test_expect_success \
- '9 - conflicting addition.' \
- 'rm -f .git/index nitfol bozbar rezrov frotz &&
- read_tree_u_must_succeed --reset -u $treeH &&
- echo frotz frotz >frotz &&
- git update-index --add frotz &&
- echo frotz >frotz &&
- if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
-
-test_expect_success \
- '10 - path removed.' \
- 'rm -f .git/index nitfol bozbar rezrov frotz &&
- read_tree_u_must_succeed --reset -u $treeH &&
- echo rezrov >rezrov &&
- git update-index --add rezrov &&
- read_tree_u_must_succeed -m -u $treeH $treeM &&
- git ls-files --stage >10.out &&
- cmp M.out 10.out &&
- test_cmp bozbar.M bozbar &&
- test_cmp frotz.M frotz &&
- test_cmp nitfol.M nitfol
+test_expect_success setup '
+ echo frotz >frotz &&
+ echo nitfol >nitfol &&
+ echo bozbar >bozbar &&
+ echo rezrov >rezrov &&
+ git update-index --add nitfol bozbar rezrov &&
+ treeH=$(git write-tree) &&
+ echo treeH $treeH &&
+ git ls-tree $treeH &&
+
+ echo gnusto >bozbar &&
+ git update-index --add frotz bozbar --force-remove rezrov &&
+ git ls-files --stage >M.out &&
+ treeM=$(git write-tree) &&
+ echo treeM $treeM &&
+ git ls-tree $treeM &&
+ cp bozbar bozbar.M &&
+ cp frotz frotz.M &&
+ cp nitfol nitfol.M &&
+ git diff-tree $treeH $treeM
+'
+
+test_expect_success '1, 2, 3 - no carry forward' '
+ rm -f .git/index nitfol bozbar rezrov frotz &&
+ read_tree_u_must_succeed --reset -u $treeH &&
+ read_tree_u_must_succeed -m -u $treeH $treeM &&
+ git ls-files --stage >1-3.out &&
+ cmp M.out 1-3.out &&
+ test_cmp bozbar.M bozbar &&
+ test_cmp frotz.M frotz &&
+ test_cmp nitfol.M nitfol &&
+ check_cache_at bozbar clean &&
+ check_cache_at frotz clean &&
+ check_cache_at nitfol clean
+'
+
+test_expect_success '4 - carry forward local addition.' '
+ rm -f .git/index nitfol bozbar rezrov frotz &&
+ read_tree_u_must_succeed --reset -u $treeH &&
+ echo "+100644 X 0 yomin" >expected &&
+ echo yomin >yomin &&
+ git update-index --add yomin &&
+ read_tree_u_must_succeed -m -u $treeH $treeM &&
+ git ls-files --stage >4.out &&
+ test_might_fail git diff -U0 --no-index M.out 4.out >4diff.out &&
+ compare_change 4diff.out expected &&
+ check_cache_at yomin clean &&
+ test_cmp bozbar.M bozbar &&
+ test_cmp frotz.M frotz &&
+ test_cmp nitfol.M nitfol &&
+ echo yomin >yomin1 &&
+ diff yomin yomin1 &&
+ rm -f yomin1
+'
+
+test_expect_success '5 - carry forward local addition.' '
+ rm -f .git/index nitfol bozbar rezrov frotz &&
+ read_tree_u_must_succeed --reset -u $treeH &&
+ read_tree_u_must_succeed -m -u $treeH &&
+ echo yomin >yomin &&
+ git update-index --add yomin &&
+ echo yomin yomin >yomin &&
+ read_tree_u_must_succeed -m -u $treeH $treeM &&
+ git ls-files --stage >5.out &&
+ test_might_fail git diff -U0 --no-index M.out 5.out >5diff.out &&
+ compare_change 5diff.out expected &&
+ check_cache_at yomin dirty &&
+ test_cmp bozbar.M bozbar &&
+ test_cmp frotz.M frotz &&
+ test_cmp nitfol.M nitfol &&
+ : dirty index should have prevented -u from checking it out. &&
+ echo yomin yomin >yomin1 &&
+ diff yomin yomin1 &&
+ rm -f yomin1
+'
+
+test_expect_success '6 - local addition already has the same.' '
+ rm -f .git/index nitfol bozbar rezrov frotz &&
+ read_tree_u_must_succeed --reset -u $treeH &&
+ echo frotz >frotz &&
+ git update-index --add frotz &&
+ read_tree_u_must_succeed -m -u $treeH $treeM &&
+ git ls-files --stage >6.out &&
+ test_cmp M.out 6.out &&
+ check_cache_at frotz clean &&
+ test_cmp bozbar.M bozbar &&
+ test_cmp frotz.M frotz &&
+ test_cmp nitfol.M nitfol &&
+ echo frotz >frotz1 &&
+ diff frotz frotz1 &&
+ rm -f frotz1
+'
+
+test_expect_success '7 - local addition already has the same.' '
+ rm -f .git/index nitfol bozbar rezrov frotz &&
+ read_tree_u_must_succeed --reset -u $treeH &&
+ echo frotz >frotz &&
+ git update-index --add frotz &&
+ echo frotz frotz >frotz &&
+ read_tree_u_must_succeed -m -u $treeH $treeM &&
+ git ls-files --stage >7.out &&
+ test_cmp M.out 7.out &&
+ check_cache_at frotz dirty &&
+ test_cmp bozbar.M bozbar &&
+ test_cmp nitfol.M nitfol &&
+ : dirty index should have prevented -u from checking it out. &&
+ echo frotz frotz >frotz1 &&
+ diff frotz frotz1 &&
+ rm -f frotz1
+'
+
+test_expect_success '8 - conflicting addition.' '
+ rm -f .git/index nitfol bozbar rezrov frotz &&
+ read_tree_u_must_succeed --reset -u $treeH &&
+ echo frotz frotz >frotz &&
+ git update-index --add frotz &&
+ ! read_tree_u_must_succeed -m -u $treeH $treeM
'
-test_expect_success \
- '11 - dirty path removed.' \
- 'rm -f .git/index nitfol bozbar rezrov frotz &&
- read_tree_u_must_succeed --reset -u $treeH &&
- echo rezrov >rezrov &&
- git update-index --add rezrov &&
- echo rezrov rezrov >rezrov &&
- if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
-
-test_expect_success \
- '12 - unmatching local changes being removed.' \
- 'rm -f .git/index nitfol bozbar rezrov frotz &&
- read_tree_u_must_succeed --reset -u $treeH &&
- echo rezrov rezrov >rezrov &&
- git update-index --add rezrov &&
- if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
-
-test_expect_success \
- '13 - unmatching local changes being removed.' \
- 'rm -f .git/index nitfol bozbar rezrov frotz &&
- read_tree_u_must_succeed --reset -u $treeH &&
- echo rezrov rezrov >rezrov &&
- git update-index --add rezrov &&
- echo rezrov >rezrov &&
- if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
+test_expect_success '9 - conflicting addition.' '
+ rm -f .git/index nitfol bozbar rezrov frotz &&
+ read_tree_u_must_succeed --reset -u $treeH &&
+ echo frotz frotz >frotz &&
+ git update-index --add frotz &&
+ echo frotz >frotz &&
+ ! read_tree_u_must_succeed -m -u $treeH $treeM
+'
+
+test_expect_success '10 - path removed.' '
+ rm -f .git/index nitfol bozbar rezrov frotz &&
+ read_tree_u_must_succeed --reset -u $treeH &&
+ echo rezrov >rezrov &&
+ git update-index --add rezrov &&
+ read_tree_u_must_succeed -m -u $treeH $treeM &&
+ git ls-files --stage >10.out &&
+ cmp M.out 10.out &&
+ test_cmp bozbar.M bozbar &&
+ test_cmp frotz.M frotz &&
+ test_cmp nitfol.M nitfol
+'
+
+test_expect_success '11 - dirty path removed.' '
+ rm -f .git/index nitfol bozbar rezrov frotz &&
+ read_tree_u_must_succeed --reset -u $treeH &&
+ echo rezrov >rezrov &&
+ git update-index --add rezrov &&
+ echo rezrov rezrov >rezrov &&
+ ! read_tree_u_must_succeed -m -u $treeH $treeM
+'
+
+test_expect_success '12 - unmatching local changes being removed.' '
+ rm -f .git/index nitfol bozbar rezrov frotz &&
+ read_tree_u_must_succeed --reset -u $treeH &&
+ echo rezrov rezrov >rezrov &&
+ git update-index --add rezrov &&
+ ! read_tree_u_must_succeed -m -u $treeH $treeM
+'
+
+test_expect_success '13 - unmatching local changes being removed.' '
+ rm -f .git/index nitfol bozbar rezrov frotz &&
+ read_tree_u_must_succeed --reset -u $treeH &&
+ echo rezrov rezrov >rezrov &&
+ git update-index --add rezrov &&
+ echo rezrov >rezrov &&
+ ! read_tree_u_must_succeed -m -u $treeH $treeM
+'
cat >expected <<EOF
-100644 X 0 nitfol
+100644 X 0 nitfol
EOF
-test_expect_success \
- '14 - unchanged in two heads.' \
- 'rm -f .git/index nitfol bozbar rezrov frotz &&
- read_tree_u_must_succeed --reset -u $treeH &&
- echo nitfol nitfol >nitfol &&
- git update-index --add nitfol &&
- read_tree_u_must_succeed -m -u $treeH $treeM &&
- git ls-files --stage >14.out &&
- test_must_fail git diff -U0 --no-index M.out 14.out >14diff.out &&
- compare_change 14diff.out expected &&
- test_cmp bozbar.M bozbar &&
- test_cmp frotz.M frotz &&
- check_cache_at nitfol clean &&
- echo nitfol nitfol >nitfol1 &&
- diff nitfol nitfol1 &&
- rm -f nitfol1'
-
-test_expect_success \
- '15 - unchanged in two heads.' \
- 'rm -f .git/index nitfol bozbar rezrov frotz &&
- read_tree_u_must_succeed --reset -u $treeH &&
- echo nitfol nitfol >nitfol &&
- git update-index --add nitfol &&
- echo nitfol nitfol nitfol >nitfol &&
- read_tree_u_must_succeed -m -u $treeH $treeM &&
- git ls-files --stage >15.out &&
- test_must_fail git diff -U0 --no-index M.out 15.out >15diff.out &&
- compare_change 15diff.out expected &&
- check_cache_at nitfol dirty &&
- test_cmp bozbar.M bozbar &&
- test_cmp frotz.M frotz &&
- echo nitfol nitfol nitfol >nitfol1 &&
- diff nitfol nitfol1 &&
- rm -f nitfol1'
-
-test_expect_success \
- '16 - conflicting local change.' \
- 'rm -f .git/index nitfol bozbar rezrov frotz &&
- read_tree_u_must_succeed --reset -u $treeH &&
- echo bozbar bozbar >bozbar &&
- git update-index --add bozbar &&
- if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
-
-test_expect_success \
- '17 - conflicting local change.' \
- 'rm -f .git/index nitfol bozbar rezrov frotz &&
- read_tree_u_must_succeed --reset -u $treeH &&
- echo bozbar bozbar >bozbar &&
- git update-index --add bozbar &&
- echo bozbar bozbar bozbar >bozbar &&
- if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
-
-test_expect_success \
- '18 - local change already having a good result.' \
- 'rm -f .git/index nitfol bozbar rezrov frotz &&
- read_tree_u_must_succeed --reset -u $treeH &&
- echo gnusto >bozbar &&
- git update-index --add bozbar &&
- read_tree_u_must_succeed -m -u $treeH $treeM &&
- git ls-files --stage >18.out &&
- test_cmp M.out 18.out &&
- check_cache_at bozbar clean &&
- test_cmp bozbar.M bozbar &&
- test_cmp frotz.M frotz &&
- test_cmp nitfol.M nitfol
+test_expect_success '14 - unchanged in two heads.' '
+ rm -f .git/index nitfol bozbar rezrov frotz &&
+ read_tree_u_must_succeed --reset -u $treeH &&
+ echo nitfol nitfol >nitfol &&
+ git update-index --add nitfol &&
+ read_tree_u_must_succeed -m -u $treeH $treeM &&
+ git ls-files --stage >14.out &&
+ test_must_fail git diff -U0 --no-index M.out 14.out >14diff.out &&
+ compare_change 14diff.out expected &&
+ test_cmp bozbar.M bozbar &&
+ test_cmp frotz.M frotz &&
+ check_cache_at nitfol clean &&
+ echo nitfol nitfol >nitfol1 &&
+ diff nitfol nitfol1 &&
+ rm -f nitfol1
+'
+
+test_expect_success '15 - unchanged in two heads.' '
+ rm -f .git/index nitfol bozbar rezrov frotz &&
+ read_tree_u_must_succeed --reset -u $treeH &&
+ echo nitfol nitfol >nitfol &&
+ git update-index --add nitfol &&
+ echo nitfol nitfol nitfol >nitfol &&
+ read_tree_u_must_succeed -m -u $treeH $treeM &&
+ git ls-files --stage >15.out &&
+ test_must_fail git diff -U0 --no-index M.out 15.out >15diff.out &&
+ compare_change 15diff.out expected &&
+ check_cache_at nitfol dirty &&
+ test_cmp bozbar.M bozbar &&
+ test_cmp frotz.M frotz &&
+ echo nitfol nitfol nitfol >nitfol1 &&
+ diff nitfol nitfol1 &&
+ rm -f nitfol1
'
-test_expect_success \
- '19 - local change already having a good result, further modified.' \
- 'rm -f .git/index nitfol bozbar rezrov frotz &&
- read_tree_u_must_succeed --reset -u $treeH &&
- echo gnusto >bozbar &&
- git update-index --add bozbar &&
- echo gnusto gnusto >bozbar &&
- read_tree_u_must_succeed -m -u $treeH $treeM &&
- git ls-files --stage >19.out &&
- test_cmp M.out 19.out &&
- check_cache_at bozbar dirty &&
- test_cmp frotz.M frotz &&
- test_cmp nitfol.M nitfol &&
- echo gnusto gnusto >bozbar1 &&
- diff bozbar bozbar1 &&
- rm -f bozbar1'
-
-test_expect_success \
- '20 - no local change, use new tree.' \
- 'rm -f .git/index nitfol bozbar rezrov frotz &&
- read_tree_u_must_succeed --reset -u $treeH &&
- echo bozbar >bozbar &&
- git update-index --add bozbar &&
- read_tree_u_must_succeed -m -u $treeH $treeM &&
- git ls-files --stage >20.out &&
- test_cmp M.out 20.out &&
- check_cache_at bozbar clean &&
- test_cmp bozbar.M bozbar &&
- test_cmp frotz.M frotz &&
- test_cmp nitfol.M nitfol
+test_expect_success '16 - conflicting local change.' '
+ rm -f .git/index nitfol bozbar rezrov frotz &&
+ read_tree_u_must_succeed --reset -u $treeH &&
+ echo bozbar bozbar >bozbar &&
+ git update-index --add bozbar &&
+ ! read_tree_u_must_succeed -m -u $treeH $treeM
'
-test_expect_success \
- '21 - no local change, dirty cache.' \
- 'rm -f .git/index nitfol bozbar rezrov frotz &&
- read_tree_u_must_succeed --reset -u $treeH &&
- echo bozbar >bozbar &&
- git update-index --add bozbar &&
- echo gnusto gnusto >bozbar &&
- if read_tree_u_must_succeed -m -u $treeH $treeM; then false; else :; fi'
+test_expect_success '17 - conflicting local change.' '
+ rm -f .git/index nitfol bozbar rezrov frotz &&
+ read_tree_u_must_succeed --reset -u $treeH &&
+ echo bozbar bozbar >bozbar &&
+ git update-index --add bozbar &&
+ echo bozbar bozbar bozbar >bozbar &&
+ ! read_tree_u_must_succeed -m -u $treeH $treeM
+'
+
+test_expect_success '18 - local change already having a good result.' '
+ rm -f .git/index nitfol bozbar rezrov frotz &&
+ read_tree_u_must_succeed --reset -u $treeH &&
+ echo gnusto >bozbar &&
+ git update-index --add bozbar &&
+ read_tree_u_must_succeed -m -u $treeH $treeM &&
+ git ls-files --stage >18.out &&
+ test_cmp M.out 18.out &&
+ check_cache_at bozbar clean &&
+ test_cmp bozbar.M bozbar &&
+ test_cmp frotz.M frotz &&
+ test_cmp nitfol.M nitfol
+'
+
+test_expect_success '19 - local change already having a good result, further modified.' '
+ rm -f .git/index nitfol bozbar rezrov frotz &&
+ read_tree_u_must_succeed --reset -u $treeH &&
+ echo gnusto >bozbar &&
+ git update-index --add bozbar &&
+ echo gnusto gnusto >bozbar &&
+ read_tree_u_must_succeed -m -u $treeH $treeM &&
+ git ls-files --stage >19.out &&
+ test_cmp M.out 19.out &&
+ check_cache_at bozbar dirty &&
+ test_cmp frotz.M frotz &&
+ test_cmp nitfol.M nitfol &&
+ echo gnusto gnusto >bozbar1 &&
+ diff bozbar bozbar1 &&
+ rm -f bozbar1
+'
+
+test_expect_success '20 - no local change, use new tree.' '
+ rm -f .git/index nitfol bozbar rezrov frotz &&
+ read_tree_u_must_succeed --reset -u $treeH &&
+ echo bozbar >bozbar &&
+ git update-index --add bozbar &&
+ read_tree_u_must_succeed -m -u $treeH $treeM &&
+ git ls-files --stage >20.out &&
+ test_cmp M.out 20.out &&
+ check_cache_at bozbar clean &&
+ test_cmp bozbar.M bozbar &&
+ test_cmp frotz.M frotz &&
+ test_cmp nitfol.M nitfol
+'
+
+test_expect_success '21 - no local change, dirty cache.' '
+ rm -f .git/index nitfol bozbar rezrov frotz &&
+ read_tree_u_must_succeed --reset -u $treeH &&
+ echo bozbar >bozbar &&
+ git update-index --add bozbar &&
+ echo gnusto gnusto >bozbar &&
+ ! read_tree_u_must_succeed -m -u $treeH $treeM
+'
# Also make sure we did not break DF vs DF/DF case.
-test_expect_success \
- 'DF vs DF/DF case setup.' \
- 'rm -f .git/index &&
- echo DF >DF &&
- git update-index --add DF &&
- treeDF=$(git write-tree) &&
- echo treeDF $treeDF &&
- git ls-tree $treeDF &&
-
- rm -f DF &&
- mkdir DF &&
- echo DF/DF >DF/DF &&
- git update-index --add --remove DF DF/DF &&
- treeDFDF=$(git write-tree) &&
- echo treeDFDF $treeDFDF &&
- git ls-tree $treeDFDF &&
- git ls-files --stage >DFDF.out'
-
-test_expect_success \
- 'DF vs DF/DF case test.' \
- 'rm -f .git/index &&
- rm -fr DF &&
- echo DF >DF &&
- git update-index --add DF &&
- read_tree_u_must_succeed -m -u $treeDF $treeDFDF &&
- git ls-files --stage >DFDFcheck.out &&
- test_cmp DFDF.out DFDFcheck.out &&
- check_cache_at DF/DF clean'
+test_expect_success 'DF vs DF/DF case setup.' '
+ rm -f .git/index &&
+ echo DF >DF &&
+ git update-index --add DF &&
+ treeDF=$(git write-tree) &&
+ echo treeDF $treeDF &&
+ git ls-tree $treeDF &&
+
+ rm -f DF &&
+ mkdir DF &&
+ echo DF/DF >DF/DF &&
+ git update-index --add --remove DF DF/DF &&
+ treeDFDF=$(git write-tree) &&
+ echo treeDFDF $treeDFDF &&
+ git ls-tree $treeDFDF &&
+ git ls-files --stage >DFDF.out
+'
+
+test_expect_success 'DF vs DF/DF case test.' '
+ rm -f .git/index &&
+ rm -fr DF &&
+ echo DF >DF &&
+ git update-index --add DF &&
+ read_tree_u_must_succeed -m -u $treeDF $treeDFDF &&
+ git ls-files --stage >DFDFcheck.out &&
+ test_cmp DFDF.out DFDFcheck.out &&
+ check_cache_at DF/DF clean
+'
test_done
diff --git a/t/t1003-read-tree-prefix.sh b/t/t1003-read-tree-prefix.sh
index b6111cd..c860c08 100755
--- a/t/t1003-read-tree-prefix.sh
+++ b/t/t1003-read-tree-prefix.sh
@@ -6,6 +6,7 @@
test_description='git read-tree --prefix test.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -24,4 +25,14 @@ test_expect_success 'read-tree --prefix' '
cmp expect actual
'
+test_expect_success 'read-tree --prefix with leading slash exits with error' '
+ git rm -rf . &&
+ test_must_fail git read-tree --prefix=/two/ $tree &&
+ git read-tree --prefix=two/ $tree &&
+
+ git rm -rf . &&
+ test_must_fail git read-tree --prefix=/ $tree &&
+ git read-tree --prefix= $tree
+'
+
test_done
diff --git a/t/t1005-read-tree-reset.sh b/t/t1005-read-tree-reset.sh
index 83b09e1..26be4a2 100755
--- a/t/t1005-read-tree-reset.sh
+++ b/t/t1005-read-tree-reset.sh
@@ -2,6 +2,7 @@
test_description='read-tree -u --reset'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-read-tree.sh
@@ -40,7 +41,8 @@ test_expect_success 'reset should remove remnants from a failed merge' '
git ls-files -s &&
read_tree_u_must_succeed --reset -u HEAD &&
git ls-files -s >actual &&
- ! test -f old
+ ! test -f old &&
+ test_cmp expect actual
'
test_expect_success 'two-way reset should remove remnants too' '
@@ -55,7 +57,8 @@ test_expect_success 'two-way reset should remove remnants too' '
git ls-files -s &&
read_tree_u_must_succeed --reset -u HEAD HEAD &&
git ls-files -s >actual &&
- ! test -f old
+ ! test -f old &&
+ test_cmp expect actual
'
test_expect_success 'Porcelain reset should remove remnants too' '
@@ -70,7 +73,8 @@ test_expect_success 'Porcelain reset should remove remnants too' '
git ls-files -s &&
git reset --hard &&
git ls-files -s >actual &&
- ! test -f old
+ ! test -f old &&
+ test_cmp expect actual
'
test_expect_success 'Porcelain checkout -f should remove remnants too' '
@@ -85,7 +89,8 @@ test_expect_success 'Porcelain checkout -f should remove remnants too' '
git ls-files -s &&
git checkout -f &&
git ls-files -s >actual &&
- ! test -f old
+ ! test -f old &&
+ test_cmp expect actual
'
test_expect_success 'Porcelain checkout -f HEAD should remove remnants too' '
@@ -100,7 +105,8 @@ test_expect_success 'Porcelain checkout -f HEAD should remove remnants too' '
git ls-files -s &&
git checkout -f HEAD &&
git ls-files -s >actual &&
- ! test -f old
+ ! test -f old &&
+ test_cmp expect actual
'
test_done
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index 18b3779..e12b221 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -4,96 +4,208 @@ test_description='git cat-file'
. ./test-lib.sh
+test_cmdmode_usage () {
+ test_expect_code 129 "$@" 2>err &&
+ grep "^error: .* cannot be used together" err
+}
+
+for switches in \
+ '-e -p' \
+ '-p -t' \
+ '-t -s' \
+ '-s --textconv' \
+ '--textconv --filters' \
+ '--batch-all-objects -e'
+do
+ test_expect_success "usage: cmdmode $switches" '
+ test_cmdmode_usage git cat-file $switches
+ '
+done
+
+test_incompatible_usage () {
+ test_expect_code 129 "$@" 2>err &&
+ grep -E "^(fatal|error):.*(requires|incompatible with|needs)" err
+}
+
+for opt in --batch --batch-check
+do
+ test_expect_success "usage: incompatible options: --path with $opt" '
+ test_incompatible_usage git cat-file --path=foo $opt
+ '
+done
+
+test_missing_usage () {
+ test_expect_code 129 "$@" 2>err &&
+ grep -E "^fatal:.*required" err
+}
+
+short_modes="-e -p -t -s"
+cw_modes="--textconv --filters"
+
+for opt in $cw_modes
+do
+ test_expect_success "usage: $opt requires another option" '
+ test_missing_usage git cat-file $opt
+ '
+done
+
+for opt in $short_modes
+do
+ test_expect_success "usage: $opt requires another option" '
+ test_missing_usage git cat-file $opt
+ '
+
+ for opt2 in --batch \
+ --batch-check \
+ --follow-symlinks \
+ "--path=foo HEAD:some-path.txt"
+ do
+ test_expect_success "usage: incompatible options: $opt and $opt2" '
+ test_incompatible_usage git cat-file $opt $opt2
+ '
+ done
+done
+
+test_too_many_arguments () {
+ test_expect_code 129 "$@" 2>err &&
+ grep -E "^fatal: too many arguments$" err
+}
+
+for opt in $short_modes $cw_modes
+do
+ args="one two three"
+ test_expect_success "usage: too many arguments: $opt $args" '
+ test_too_many_arguments git cat-file $opt $args
+ '
+
+ for opt2 in --buffer --follow-symlinks
+ do
+ test_expect_success "usage: incompatible arguments: $opt with batch option $opt2" '
+ test_incompatible_usage git cat-file $opt $opt2
+ '
+ done
+done
+
+for opt in --buffer \
+ --follow-symlinks \
+ --batch-all-objects \
+ -z \
+ -Z
+do
+ test_expect_success "usage: bad option combination: $opt without batch mode" '
+ test_incompatible_usage git cat-file $opt &&
+ test_incompatible_usage git cat-file $opt commit HEAD
+ '
+done
+
echo_without_newline () {
printf '%s' "$*"
}
-strlen () {
- echo_without_newline "$1" | wc -c | sed -e 's/^ *//'
+echo_without_newline_nul () {
+ echo_without_newline "$@" | tr '\n' '\0'
}
-maybe_remove_timestamp () {
- if test -z "$2"; then
- echo_without_newline "$1"
- else
- echo_without_newline "$(printf '%s\n' "$1" | sed -e 's/ [0-9][0-9]* [-+][0-9][0-9][0-9][0-9]$//')"
- fi
+strlen () {
+ echo_without_newline "$1" | wc -c | sed -e 's/^ *//'
}
run_tests () {
type=$1
- sha1=$2
+ oid=$2
size=$3
content=$4
pretty_content=$5
- no_ts=$6
- batch_output="$sha1 $type $size
+ batch_output="$oid $type $size
$content"
test_expect_success "$type exists" '
- git cat-file -e $sha1
+ git cat-file -e $oid
'
test_expect_success "Type of $type is correct" '
echo $type >expect &&
- git cat-file -t $sha1 >actual &&
+ git cat-file -t $oid >actual &&
test_cmp expect actual
'
test_expect_success "Size of $type is correct" '
echo $size >expect &&
- git cat-file -s $sha1 >actual &&
+ git cat-file -s $oid >actual &&
test_cmp expect actual
'
test_expect_success "Type of $type is correct using --allow-unknown-type" '
echo $type >expect &&
- git cat-file -t --allow-unknown-type $sha1 >actual &&
+ git cat-file -t --allow-unknown-type $oid >actual &&
test_cmp expect actual
'
test_expect_success "Size of $type is correct using --allow-unknown-type" '
echo $size >expect &&
- git cat-file -s --allow-unknown-type $sha1 >actual &&
+ git cat-file -s --allow-unknown-type $oid >actual &&
test_cmp expect actual
'
test -z "$content" ||
test_expect_success "Content of $type is correct" '
- maybe_remove_timestamp "$content" $no_ts >expect &&
- maybe_remove_timestamp "$(git cat-file $type $sha1)" $no_ts >actual &&
+ echo_without_newline "$content" >expect &&
+ git cat-file $type $oid >actual &&
test_cmp expect actual
'
test_expect_success "Pretty content of $type is correct" '
- maybe_remove_timestamp "$pretty_content" $no_ts >expect &&
- maybe_remove_timestamp "$(git cat-file -p $sha1)" $no_ts >actual &&
+ echo_without_newline "$pretty_content" >expect &&
+ git cat-file -p $oid >actual &&
test_cmp expect actual
'
test -z "$content" ||
test_expect_success "--batch output of $type is correct" '
- maybe_remove_timestamp "$batch_output" $no_ts >expect &&
- maybe_remove_timestamp "$(echo $sha1 | git cat-file --batch)" $no_ts >actual &&
+ echo "$batch_output" >expect &&
+ echo $oid | git cat-file --batch >actual &&
test_cmp expect actual
'
test_expect_success "--batch-check output of $type is correct" '
- echo "$sha1 $type $size" >expect &&
- echo_without_newline $sha1 | git cat-file --batch-check >actual &&
+ echo "$oid $type $size" >expect &&
+ echo_without_newline $oid | git cat-file --batch-check >actual &&
test_cmp expect actual
'
+ for opt in --buffer --no-buffer
+ do
+ test -z "$content" ||
+ test_expect_success "--batch-command $opt output of $type content is correct" '
+ echo "$batch_output" >expect &&
+ test_write_lines "contents $oid" | git cat-file --batch-command $opt >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "--batch-command $opt output of $type info is correct" '
+ echo "$oid $type $size" >expect &&
+ test_write_lines "info $oid" |
+ git cat-file --batch-command $opt >actual &&
+ test_cmp expect actual
+ '
+ done
+
test_expect_success "custom --batch-check format" '
- echo "$type $sha1" >expect &&
- echo $sha1 | git cat-file --batch-check="%(objecttype) %(objectname)" >actual &&
+ echo "$type $oid" >expect &&
+ echo $oid | git cat-file --batch-check="%(objecttype) %(objectname)" >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "custom --batch-command format" '
+ echo "$type $oid" >expect &&
+ echo "info $oid" | git cat-file --batch-command="%(objecttype) %(objectname)" >actual &&
test_cmp expect actual
'
test_expect_success '--batch-check with %(rest)' '
echo "$type this is some extra content" >expect &&
- echo "$sha1 this is some extra content" |
+ echo "$oid this is some extra content" |
git cat-file --batch-check="%(objecttype) %(rest)" >actual &&
test_cmp expect actual
'
@@ -102,10 +214,9 @@ $content"
test_expect_success "--batch without type ($type)" '
{
echo "$size" &&
- maybe_remove_timestamp "$content" $no_ts
+ echo "$content"
} >expect &&
- echo $sha1 | git cat-file --batch="%(objectsize)" >actual.full &&
- maybe_remove_timestamp "$(cat actual.full)" $no_ts >actual &&
+ echo $oid | git cat-file --batch="%(objectsize)" >actual &&
test_cmp expect actual
'
@@ -113,126 +224,200 @@ $content"
test_expect_success "--batch without size ($type)" '
{
echo "$type" &&
- maybe_remove_timestamp "$content" $no_ts
+ echo "$content"
} >expect &&
- echo $sha1 | git cat-file --batch="%(objecttype)" >actual.full &&
- maybe_remove_timestamp "$(cat actual.full)" $no_ts >actual &&
+ echo $oid | git cat-file --batch="%(objecttype)" >actual &&
test_cmp expect actual
'
}
hello_content="Hello World"
hello_size=$(strlen "$hello_content")
-hello_sha1=$(echo_without_newline "$hello_content" | git hash-object --stdin)
+hello_oid=$(echo_without_newline "$hello_content" | git hash-object --stdin)
test_expect_success "setup" '
+ git config core.repositoryformatversion 1 &&
+ git config extensions.objectformat $test_hash_algo &&
+ git config extensions.compatobjectformat $test_compat_hash_algo &&
echo_without_newline "$hello_content" > hello &&
git update-index --add hello
'
-run_tests 'blob' $hello_sha1 $hello_size "$hello_content" "$hello_content"
+run_blob_tests () {
+ oid=$1
+
+ run_tests 'blob' $oid $hello_size "$hello_content" "$hello_content"
+
+ test_expect_success '--batch-command --buffer with flush for blob info' '
+ echo "$oid blob $hello_size" >expect &&
+ test_write_lines "info $oid" "flush" |
+ GIT_TEST_CAT_FILE_NO_FLUSH_ON_EXIT=1 \
+ git cat-file --batch-command --buffer >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success '--batch-command --buffer without flush for blob info' '
+ touch output &&
+ test_write_lines "info $oid" |
+ GIT_TEST_CAT_FILE_NO_FLUSH_ON_EXIT=1 \
+ git cat-file --batch-command --buffer >>output &&
+ test_must_be_empty output
+ '
+}
+
+hello_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $hello_oid)
+run_blob_tests $hello_oid
+run_blob_tests $hello_compat_oid
test_expect_success '--batch-check without %(rest) considers whole line' '
- echo "$hello_sha1 blob $hello_size" >expect &&
- git update-index --add --cacheinfo 100644 $hello_sha1 "white space" &&
+ echo "$hello_oid blob $hello_size" >expect &&
+ git update-index --add --cacheinfo 100644 $hello_oid "white space" &&
test_when_finished "git update-index --remove \"white space\"" &&
echo ":white space" | git cat-file --batch-check >actual &&
test_cmp expect actual
'
-tree_sha1=$(git write-tree)
+tree_oid=$(git write-tree)
+tree_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $tree_oid)
tree_size=$(($(test_oid rawsz) + 13))
-tree_pretty_content="100644 blob $hello_sha1 hello"
+tree_compat_size=$(($(test_oid --hash=compat rawsz) + 13))
+tree_pretty_content="100644 blob $hello_oid hello${LF}"
+tree_compat_pretty_content="100644 blob $hello_compat_oid hello${LF}"
-run_tests 'tree' $tree_sha1 $tree_size "" "$tree_pretty_content"
+run_tests 'tree' $tree_oid $tree_size "" "$tree_pretty_content"
+run_tests 'tree' $tree_compat_oid $tree_compat_size "" "$tree_compat_pretty_content"
commit_message="Initial commit"
-commit_sha1=$(echo_without_newline "$commit_message" | git commit-tree $tree_sha1)
+commit_oid=$(echo_without_newline "$commit_message" | git commit-tree $tree_oid)
+commit_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $commit_oid)
commit_size=$(($(test_oid hexsz) + 137))
-commit_content="tree $tree_sha1
-author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> 0000000000 +0000
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 0000000000 +0000
+commit_compat_size=$(($(test_oid --hash=compat hexsz) + 137))
+commit_content="tree $tree_oid
+author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+$commit_message"
+
+commit_compat_content="tree $tree_compat_oid
+author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
$commit_message"
-run_tests 'commit' $commit_sha1 $commit_size "$commit_content" "$commit_content" 1
+run_tests 'commit' $commit_oid $commit_size "$commit_content" "$commit_content"
+run_tests 'commit' $commit_compat_oid $commit_compat_size "$commit_compat_content" "$commit_compat_content"
-tag_header_without_timestamp="object $hello_sha1
-type blob
+tag_header_without_oid="type blob
tag hellotag
tagger $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
+tag_header_without_timestamp="object $hello_oid
+$tag_header_without_oid"
+tag_compat_header_without_timestamp="object $hello_compat_oid
+$tag_header_without_oid"
tag_description="This is a tag"
-tag_content="$tag_header_without_timestamp 0000000000 +0000
+tag_content="$tag_header_without_timestamp 0 +0000
+
+$tag_description"
+tag_compat_content="$tag_compat_header_without_timestamp 0 +0000
$tag_description"
-tag_sha1=$(echo_without_newline "$tag_content" | git hash-object -t tag --stdin -w)
+tag_oid=$(echo_without_newline "$tag_content" | git hash-object -t tag --stdin -w)
tag_size=$(strlen "$tag_content")
-run_tests 'tag' $tag_sha1 $tag_size "$tag_content" "$tag_content" 1
+tag_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $tag_oid)
+tag_compat_size=$(strlen "$tag_compat_content")
-test_expect_success \
- "Reach a blob from a tag pointing to it" \
- "test '$hello_content' = \"\$(git cat-file blob $tag_sha1)\""
+run_tests 'tag' $tag_oid $tag_size "$tag_content" "$tag_content"
+run_tests 'tag' $tag_compat_oid $tag_compat_size "$tag_compat_content" "$tag_compat_content"
-for batch in batch batch-check
+test_expect_success "Reach a blob from a tag pointing to it" '
+ echo_without_newline "$hello_content" >expect &&
+ git cat-file blob $tag_oid >actual &&
+ test_cmp expect actual
+'
+
+for oid in $hello_oid $hello_compat_oid
do
- for opt in t s e p
+ for batch in batch batch-check batch-command
do
+ for opt in t s e p
+ do
test_expect_success "Passing -$opt with --$batch fails" '
- test_must_fail git cat-file --$batch -$opt $hello_sha1
+ test_must_fail git cat-file --$batch -$opt $oid
'
test_expect_success "Passing --$batch with -$opt fails" '
- test_must_fail git cat-file -$opt --$batch $hello_sha1
+ test_must_fail git cat-file -$opt --$batch $oid
'
- done
+ done
- test_expect_success "Passing <type> with --$batch fails" '
- test_must_fail git cat-file --$batch blob $hello_sha1
- '
+ test_expect_success "Passing <type> with --$batch fails" '
+ test_must_fail git cat-file --$batch blob $oid
+ '
- test_expect_success "Passing --$batch with <type> fails" '
- test_must_fail git cat-file blob --$batch $hello_sha1
- '
+ test_expect_success "Passing --$batch with <type> fails" '
+ test_must_fail git cat-file blob --$batch $oid
+ '
- test_expect_success "Passing sha1 with --$batch fails" '
- test_must_fail git cat-file --$batch $hello_sha1
- '
+ test_expect_success "Passing oid with --$batch fails" '
+ test_must_fail git cat-file --$batch $oid
+ '
+ done
done
-for opt in t s e p
+for oid in $hello_oid $hello_compat_oid
do
- test_expect_success "Passing -$opt with --follow-symlinks fails" '
- test_must_fail git cat-file --follow-symlinks -$opt $hello_sha1
+ for opt in t s e p
+ do
+ test_expect_success "Passing -$opt with --follow-symlinks fails" '
+ test_must_fail git cat-file --follow-symlinks -$opt $oid
'
+ done
done
test_expect_success "--batch-check for a non-existent named object" '
- test "foobar42 missing
-foobar84 missing" = \
- "$( ( echo foobar42; echo_without_newline foobar84; ) | git cat-file --batch-check)"
+ cat >expect <<-EOF &&
+ foobar42 missing
+ foobar84 missing
+ EOF
+
+ printf "foobar42\nfoobar84" >in &&
+ git cat-file --batch-check <in >actual &&
+ test_cmp expect actual
'
test_expect_success "--batch-check for a non-existent hash" '
- test "0000000000000000000000000000000000000042 missing
-0000000000000000000000000000000000000084 missing" = \
- "$( ( echo 0000000000000000000000000000000000000042;
- echo_without_newline 0000000000000000000000000000000000000084; ) |
- git cat-file --batch-check)"
+ cat >expect <<-EOF &&
+ 0000000000000000000000000000000000000042 missing
+ 0000000000000000000000000000000000000084 missing
+ EOF
+
+ printf "0000000000000000000000000000000000000042\n0000000000000000000000000000000000000084" >in &&
+ git cat-file --batch-check <in >actual &&
+ test_cmp expect actual
'
test_expect_success "--batch for an existent and a non-existent hash" '
- test "$tag_sha1 tag $tag_size
-$tag_content
-0000000000000000000000000000000000000000 missing" = \
- "$( ( echo $tag_sha1;
- echo_without_newline 0000000000000000000000000000000000000000; ) |
- git cat-file --batch)"
+ cat >expect <<-EOF &&
+ $tag_oid tag $tag_size
+ $tag_content
+ 0000000000000000000000000000000000000000 missing
+ EOF
+
+ printf "$tag_oid\n0000000000000000000000000000000000000000" >in &&
+ git cat-file --batch <in >actual &&
+ test_cmp expect actual
'
test_expect_success "--batch-check for an empty line" '
- test " missing" = "$(echo | git cat-file --batch-check)"
+ cat >expect <<-EOF &&
+ missing
+ EOF
+
+ echo >in &&
+ git cat-file --batch-check <in >actual &&
+ test_cmp expect actual
'
test_expect_success 'empty --batch-check notices missing object' '
@@ -241,49 +426,185 @@ test_expect_success 'empty --batch-check notices missing object' '
test_cmp expect actual
'
-batch_input="$hello_sha1
-$commit_sha1
-$tag_sha1
+batch_tests () {
+ boid=$1
+ loid=$2
+ lsize=$3
+ coid=$4
+ csize=$5
+ ccontent=$6
+ toid=$7
+ tsize=$8
+ tcontent=$9
+
+ batch_input="$boid
+$coid
+$toid
deadbeef
"
-batch_output="$hello_sha1 blob $hello_size
-$hello_content
-$commit_sha1 commit $commit_size
-$commit_content
-$tag_sha1 tag $tag_size
-$tag_content
-deadbeef missing
- missing"
+ printf "%s\0" \
+ "$boid blob $hello_size" \
+ "$hello_content" \
+ "$coid commit $csize" \
+ "$ccontent" \
+ "$toid tag $tsize" \
+ "$tcontent" \
+ "deadbeef missing" \
+ " missing" >batch_output
+
+ test_expect_success '--batch with multiple oids gives correct format' '
+ tr "\0" "\n" <batch_output >expect &&
+ echo_without_newline "$batch_input" >in &&
+ git cat-file --batch <in >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success '--batch, -z with multiple oids gives correct format' '
+ echo_without_newline_nul "$batch_input" >in &&
+ tr "\0" "\n" <batch_output >expect &&
+ git cat-file --batch -z <in >actual &&
+ test_cmp expect actual
+ '
-test_expect_success '--batch with multiple sha1s gives correct format' '
- test "$(maybe_remove_timestamp "$batch_output" 1)" = "$(maybe_remove_timestamp "$(echo_without_newline "$batch_input" | git cat-file --batch)" 1)"
-'
+ test_expect_success '--batch, -Z with multiple oids gives correct format' '
+ echo_without_newline_nul "$batch_input" >in &&
+ git cat-file --batch -Z <in >actual &&
+ test_cmp batch_output actual
+ '
-batch_check_input="$hello_sha1
-$tree_sha1
-$commit_sha1
-$tag_sha1
+batch_check_input="$boid
+$loid
+$coid
+$toid
deadbeef
"
-batch_check_output="$hello_sha1 blob $hello_size
-$tree_sha1 tree $tree_size
-$commit_sha1 commit $commit_size
-$tag_sha1 tag $tag_size
-deadbeef missing
- missing"
+ printf "%s\0" \
+ "$boid blob $hello_size" \
+ "$loid tree $lsize" \
+ "$coid commit $csize" \
+ "$toid tag $tsize" \
+ "deadbeef missing" \
+ " missing" >batch_check_output
+
+ test_expect_success "--batch-check with multiple oids gives correct format" '
+ tr "\0" "\n" <batch_check_output >expect &&
+ echo_without_newline "$batch_check_input" >in &&
+ git cat-file --batch-check <in >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "--batch-check, -z with multiple oids gives correct format" '
+ tr "\0" "\n" <batch_check_output >expect &&
+ echo_without_newline_nul "$batch_check_input" >in &&
+ git cat-file --batch-check -z <in >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "--batch-check, -Z with multiple oids gives correct format" '
+ echo_without_newline_nul "$batch_check_input" >in &&
+ git cat-file --batch-check -Z <in >actual &&
+ test_cmp batch_check_output actual
+ '
+
+batch_command_multiple_info="info $boid
+info $loid
+info $coid
+info $toid
+info deadbeef"
+
+ test_expect_success '--batch-command with multiple info calls gives correct format' '
+ cat >expect <<-EOF &&
+ $boid blob $hello_size
+ $loid tree $lsize
+ $coid commit $csize
+ $toid tag $tsize
+ deadbeef missing
+ EOF
+
+ echo "$batch_command_multiple_info" >in &&
+ git cat-file --batch-command --buffer <in >actual &&
+
+ test_cmp expect actual &&
+
+ echo "$batch_command_multiple_info" | tr "\n" "\0" >in &&
+ git cat-file --batch-command --buffer -z <in >actual &&
-test_expect_success "--batch-check with multiple sha1s gives correct format" '
- test "$batch_check_output" = \
- "$(echo_without_newline "$batch_check_input" | git cat-file --batch-check)"
+ test_cmp expect actual &&
+
+ echo "$batch_command_multiple_info" | tr "\n" "\0" >in &&
+ tr "\n" "\0" <expect >expect_nul &&
+ git cat-file --batch-command --buffer -Z <in >actual &&
+
+ test_cmp expect_nul actual
+ '
+
+batch_command_multiple_contents="contents $boid
+contents $coid
+contents $toid
+contents deadbeef
+flush"
+
+ test_expect_success '--batch-command with multiple command calls gives correct format' '
+ printf "%s\0" \
+ "$boid blob $hello_size" \
+ "$hello_content" \
+ "$coid commit $csize" \
+ "$ccontent" \
+ "$toid tag $tsize" \
+ "$tcontent" \
+ "deadbeef missing" >expect_nul &&
+ tr "\0" "\n" <expect_nul >expect &&
+
+ echo "$batch_command_multiple_contents" >in &&
+ git cat-file --batch-command --buffer <in >actual &&
+
+ test_cmp expect actual &&
+
+ echo "$batch_command_multiple_contents" | tr "\n" "\0" >in &&
+ git cat-file --batch-command --buffer -z <in >actual &&
+
+ test_cmp expect actual &&
+
+ echo "$batch_command_multiple_contents" | tr "\n" "\0" >in &&
+ git cat-file --batch-command --buffer -Z <in >actual &&
+
+ test_cmp expect_nul actual
+ '
+
+}
+
+batch_tests $hello_oid $tree_oid $tree_size $commit_oid $commit_size "$commit_content" $tag_oid $tag_size "$tag_content"
+batch_tests $hello_compat_oid $tree_compat_oid $tree_compat_size $commit_compat_oid $commit_compat_size "$commit_compat_content" $tag_compat_oid $tag_compat_size "$tag_compat_content"
+
+
+test_expect_success FUNNYNAMES 'setup with newline in input' '
+ touch -- "newline${LF}embedded" &&
+ git add -- "newline${LF}embedded" &&
+ git commit -m "file with newline embedded" &&
+ test_tick &&
+
+ printf "HEAD:newline${LF}embedded" >in
+'
+
+test_expect_success FUNNYNAMES '--batch-check, -z with newline in input' '
+ git cat-file --batch-check -z <in >actual &&
+ echo "$(git rev-parse "HEAD:newline${LF}embedded") blob 0" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success FUNNYNAMES '--batch-check, -Z with newline in input' '
+ git cat-file --batch-check -Z <in >actual &&
+ printf "%s\0" "$(git rev-parse "HEAD:newline${LF}embedded") blob 0" >expect &&
+ test_cmp expect actual
'
test_expect_success 'setup blobs which are likely to delta' '
test-tool genrandom foo 10240 >foo &&
- { cat foo; echo plus; } >foo-plus &&
+ { cat foo && echo plus; } >foo-plus &&
git add foo foo-plus &&
git commit -m foo &&
cat >blobs <<-\EOF
@@ -305,7 +626,7 @@ test_expect_success 'confirm that neither loose blob is a delta' '
# we will check only that one of the two objects is a delta
# against the other, but not the order. We can do so by just
# asking for the base of both, and checking whether either
-# sha1 appears in the output.
+# oid appears in the output.
test_expect_success '%(deltabase) reports packed delta bases' '
git repack -ad &&
git cat-file --batch-check="%(deltabase)" <blobs >actual &&
@@ -315,39 +636,244 @@ test_expect_success '%(deltabase) reports packed delta bases' '
}
'
-bogus_type="bogus"
-bogus_content="bogus"
-bogus_size=$(strlen "$bogus_content")
-bogus_sha1=$(echo_without_newline "$bogus_content" | git hash-object -t $bogus_type --literally -w --stdin)
+test_expect_success 'setup bogus data' '
+ bogus_short_type="bogus" &&
+ bogus_short_content="bogus" &&
+ bogus_short_size=$(strlen "$bogus_short_content") &&
+ bogus_short_oid=$(echo_without_newline "$bogus_short_content" | git hash-object -t $bogus_short_type --literally -w --stdin) &&
+
+ bogus_long_type="abcdefghijklmnopqrstuvwxyz1234679" &&
+ bogus_long_content="bogus" &&
+ bogus_long_size=$(strlen "$bogus_long_content") &&
+ bogus_long_oid=$(echo_without_newline "$bogus_long_content" | git hash-object -t $bogus_long_type --literally -w --stdin)
+'
+
+for arg1 in '' --allow-unknown-type
+do
+ for arg2 in -s -t -p
+ do
+ if test "$arg1" = "--allow-unknown-type" && test "$arg2" = "-p"
+ then
+ continue
+ fi
+
+
+ test_expect_success "cat-file $arg1 $arg2 error on bogus short OID" '
+ cat >expect <<-\EOF &&
+ fatal: invalid object type
+ EOF
+
+ if test "$arg1" = "--allow-unknown-type"
+ then
+ git cat-file $arg1 $arg2 $bogus_short_oid
+ else
+ test_must_fail git cat-file $arg1 $arg2 $bogus_short_oid >out 2>actual &&
+ test_must_be_empty out &&
+ test_cmp expect actual
+ fi
+ '
+
+ test_expect_success "cat-file $arg1 $arg2 error on bogus full OID" '
+ if test "$arg2" = "-p"
+ then
+ cat >expect <<-EOF
+ error: header for $bogus_long_oid too long, exceeds 32 bytes
+ fatal: Not a valid object name $bogus_long_oid
+ EOF
+ else
+ cat >expect <<-EOF
+ error: header for $bogus_long_oid too long, exceeds 32 bytes
+ fatal: git cat-file: could not get object info
+ EOF
+ fi &&
+
+ if test "$arg1" = "--allow-unknown-type"
+ then
+ git cat-file $arg1 $arg2 $bogus_short_oid
+ else
+ test_must_fail git cat-file $arg1 $arg2 $bogus_long_oid >out 2>actual &&
+ test_must_be_empty out &&
+ test_cmp expect actual
+ fi
+ '
+
+ test_expect_success "cat-file $arg1 $arg2 error on missing short OID" '
+ cat >expect.err <<-EOF &&
+ fatal: Not a valid object name $(test_oid deadbeef_short)
+ EOF
+ test_must_fail git cat-file $arg1 $arg2 $(test_oid deadbeef_short) >out 2>err.actual &&
+ test_must_be_empty out &&
+ test_cmp expect.err err.actual
+ '
+
+ test_expect_success "cat-file $arg1 $arg2 error on missing full OID" '
+ if test "$arg2" = "-p"
+ then
+ cat >expect.err <<-EOF
+ fatal: Not a valid object name $(test_oid deadbeef)
+ EOF
+ else
+ cat >expect.err <<-\EOF
+ fatal: git cat-file: could not get object info
+ EOF
+ fi &&
+ test_must_fail git cat-file $arg1 $arg2 $(test_oid deadbeef) >out 2>err.actual &&
+ test_must_be_empty out &&
+ test_cmp expect.err err.actual
+ '
+ done
+done
+
+test_expect_success '-e is OK with a broken object without --allow-unknown-type' '
+ git cat-file -e $bogus_short_oid
+'
+
+test_expect_success '-e can not be combined with --allow-unknown-type' '
+ test_expect_code 128 git cat-file -e --allow-unknown-type $bogus_short_oid
+'
+
+test_expect_success '-p cannot print a broken object even with --allow-unknown-type' '
+ test_must_fail git cat-file -p $bogus_short_oid &&
+ test_expect_code 128 git cat-file -p --allow-unknown-type $bogus_short_oid
+'
+
+test_expect_success '<type> <hash> does not work with objects of broken types' '
+ cat >err.expect <<-\EOF &&
+ fatal: invalid object type "bogus"
+ EOF
+ test_must_fail git cat-file $bogus_short_type $bogus_short_oid 2>err.actual &&
+ test_cmp err.expect err.actual
+'
+
+test_expect_success 'broken types combined with --batch and --batch-check' '
+ echo $bogus_short_oid >bogus-oid &&
+
+ cat >err.expect <<-\EOF &&
+ fatal: invalid object type
+ EOF
+
+ test_must_fail git cat-file --batch <bogus-oid 2>err.actual &&
+ test_cmp err.expect err.actual &&
+
+ test_must_fail git cat-file --batch-check <bogus-oid 2>err.actual &&
+ test_cmp err.expect err.actual
+'
+
+test_expect_success 'the --batch and --batch-check options do not combine with --allow-unknown-type' '
+ test_expect_code 128 git cat-file --batch --allow-unknown-type <bogus-oid &&
+ test_expect_code 128 git cat-file --batch-check --allow-unknown-type <bogus-oid
+'
+
+test_expect_success 'the --allow-unknown-type option does not consider replacement refs' '
+ cat >expect <<-EOF &&
+ $bogus_short_type
+ EOF
+ git cat-file -t --allow-unknown-type $bogus_short_oid >actual &&
+ test_cmp expect actual &&
+
+ # Create it manually, as "git replace" will die on bogus
+ # types.
+ head=$(git rev-parse --verify HEAD) &&
+ test_when_finished "test-tool ref-store main delete-refs 0 msg refs/replace/$bogus_short_oid" &&
+ test-tool ref-store main update-ref msg "refs/replace/$bogus_short_oid" $head $ZERO_OID REF_SKIP_OID_VERIFICATION &&
+
+ cat >expect <<-EOF &&
+ commit
+ EOF
+ git cat-file -t --allow-unknown-type $bogus_short_oid >actual &&
+ test_cmp expect actual
+'
test_expect_success "Type of broken object is correct" '
- echo $bogus_type >expect &&
- git cat-file -t --allow-unknown-type $bogus_sha1 >actual &&
+ echo $bogus_short_type >expect &&
+ git cat-file -t --allow-unknown-type $bogus_short_oid >actual &&
test_cmp expect actual
'
test_expect_success "Size of broken object is correct" '
- echo $bogus_size >expect &&
- git cat-file -s --allow-unknown-type $bogus_sha1 >actual &&
+ echo $bogus_short_size >expect &&
+ git cat-file -s --allow-unknown-type $bogus_short_oid >actual &&
test_cmp expect actual
'
-bogus_type="abcdefghijklmnopqrstuvwxyz1234679"
-bogus_content="bogus"
-bogus_size=$(strlen "$bogus_content")
-bogus_sha1=$(echo_without_newline "$bogus_content" | git hash-object -t $bogus_type --literally -w --stdin)
+
+test_expect_success 'clean up broken object' '
+ rm .git/objects/$(test_oid_to_path $bogus_short_oid)
+'
test_expect_success "Type of broken object is correct when type is large" '
- echo $bogus_type >expect &&
- git cat-file -t --allow-unknown-type $bogus_sha1 >actual &&
+ echo $bogus_long_type >expect &&
+ git cat-file -t --allow-unknown-type $bogus_long_oid >actual &&
test_cmp expect actual
'
test_expect_success "Size of large broken object is correct when type is large" '
- echo $bogus_size >expect &&
- git cat-file -s --allow-unknown-type $bogus_sha1 >actual &&
+ echo $bogus_long_size >expect &&
+ git cat-file -s --allow-unknown-type $bogus_long_oid >actual &&
test_cmp expect actual
'
+test_expect_success 'clean up broken object' '
+ rm .git/objects/$(test_oid_to_path $bogus_long_oid)
+'
+
+test_expect_success 'cat-file -t and -s on corrupt loose object' '
+ git init --bare corrupt-loose.git &&
+ (
+ cd corrupt-loose.git &&
+
+ # Setup and create the empty blob and its path
+ empty_path=$(git rev-parse --git-path objects/$(test_oid_to_path "$EMPTY_BLOB")) &&
+ empty_blob=$(git hash-object -w --stdin </dev/null) &&
+
+ # Create another blob and its path
+ echo other >other.blob &&
+ other_blob=$(git hash-object -w --stdin <other.blob) &&
+ other_path=$(git rev-parse --git-path objects/$(test_oid_to_path "$other_blob")) &&
+
+ # Before the swap the size is 0
+ cat >out.expect <<-EOF &&
+ 0
+ EOF
+ git cat-file -s "$EMPTY_BLOB" >out.actual 2>err.actual &&
+ test_must_be_empty err.actual &&
+ test_cmp out.expect out.actual &&
+
+ # Swap the two to corrupt the repository
+ mv -f "$other_path" "$empty_path" &&
+ test_must_fail git fsck 2>err.fsck &&
+ grep "hash-path mismatch" err.fsck &&
+
+ # confirm that cat-file is reading the new swapped-in
+ # blob...
+ cat >out.expect <<-EOF &&
+ blob
+ EOF
+ git cat-file -t "$EMPTY_BLOB" >out.actual 2>err.actual &&
+ test_must_be_empty err.actual &&
+ test_cmp out.expect out.actual &&
+
+ # ... since it has a different size now.
+ cat >out.expect <<-EOF &&
+ 6
+ EOF
+ git cat-file -s "$EMPTY_BLOB" >out.actual 2>err.actual &&
+ test_must_be_empty err.actual &&
+ test_cmp out.expect out.actual &&
+
+ # So far "cat-file" has been happy to spew the found
+ # content out as-is. Try to make it zlib-invalid.
+ mv -f other.blob "$empty_path" &&
+ test_must_fail git fsck 2>err.fsck &&
+ cat >expect <<-EOF &&
+ error: inflate: data stream error (incorrect header check)
+ error: unable to unpack header of ./$empty_path
+ error: $empty_blob: object corrupt or missing: ./$empty_path
+ EOF
+ grep "^error: " err.fsck >actual &&
+ test_cmp expect actual
+ )
+'
+
# Tests for git cat-file --follow-symlinks
test_expect_success 'prep for symlink tests' '
echo_without_newline "$hello_content" >morx &&
@@ -384,7 +910,7 @@ test_expect_success 'prep for symlink tests' '
test_ln_s_add loop2 loop1 &&
git add morx dir/subdir/ind2 dir/ind1 &&
git commit -am "test" &&
- echo $hello_sha1 blob $hello_size >found
+ echo $hello_oid blob $hello_size >found
'
test_expect_success 'git cat-file --batch-check --follow-symlinks works for non-links' '
@@ -413,6 +939,13 @@ test_expect_success 'git cat-file --batch-check --follow-symlinks works for brok
test_cmp expect actual
'
+test_expect_success 'git cat-file --batch-check --follow-symlinks -Z works for broken in-repo, same-dir links' '
+ printf "HEAD:broken-same-dir-link\0" >in &&
+ printf "dangling 25\0HEAD:broken-same-dir-link\0" >expect &&
+ git cat-file --batch-check --follow-symlinks -Z <in >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'git cat-file --batch-check --follow-symlinks works for same-dir links-to-links' '
echo HEAD:link-to-link | git cat-file --batch-check --follow-symlinks >actual &&
test_cmp found actual
@@ -427,6 +960,15 @@ test_expect_success 'git cat-file --batch-check --follow-symlinks works for pare
test_cmp expect actual
'
+test_expect_success 'git cat-file --batch-check --follow-symlinks -Z works for parent-dir links' '
+ echo HEAD:dir/parent-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ printf "notdir 29\0HEAD:dir/parent-dir-link/nope\0" >expect &&
+ printf "HEAD:dir/parent-dir-link/nope\0" >in &&
+ git cat-file --batch-check --follow-symlinks -Z <in >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'git cat-file --batch-check --follow-symlinks works for .. links' '
echo dangling 22 >expect &&
echo HEAD:dir/link-dir/nope >>expect &&
@@ -456,7 +998,7 @@ test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir/
echo HEAD:dirlink/morx >>expect &&
echo HEAD:dirlink/morx | git cat-file --batch-check --follow-symlinks >actual &&
test_cmp expect actual &&
- echo $hello_sha1 blob $hello_size >expect &&
+ echo $hello_oid blob $hello_size >expect &&
echo HEAD:dirlink/ind1 | git cat-file --batch-check --follow-symlinks >actual &&
test_cmp expect actual
'
@@ -541,6 +1083,13 @@ test_expect_success 'git cat-file --batch-check --follow-symlink breaks loops' '
test_cmp expect actual
'
+test_expect_success 'git cat-file --batch-check --follow-symlink -Z breaks loops' '
+ printf "loop 10\0HEAD:loop1\0" >expect &&
+ printf "HEAD:loop1\0" >in &&
+ git cat-file --batch-check --follow-symlinks -Z <in >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'git cat-file --batch --follow-symlink returns correct sha and mode' '
echo HEAD:morx | git cat-file --batch >expect &&
echo HEAD:morx | git cat-file --batch --follow-symlinks >actual &&
@@ -608,4 +1157,141 @@ test_expect_success 'cat-file --batch="batman" with --batch-all-objects will wor
cmp expect actual
'
+test_expect_success 'cat-file %(objectsize:disk) with --batch-all-objects' '
+ # our state has both loose and packed objects,
+ # so find both for our expected output
+ {
+ find .git/objects/?? -type f |
+ awk -F/ "{ print \$0, \$3\$4 }" |
+ while read path oid
+ do
+ size=$(test_file_size "$path") &&
+ echo "$oid $size" ||
+ return 1
+ done &&
+ rawsz=$(test_oid rawsz) &&
+ find .git/objects/pack -name "*.idx" |
+ while read idx
+ do
+ git show-index <"$idx" >idx.raw &&
+ sort -nr <idx.raw >idx.sorted &&
+ packsz=$(test_file_size "${idx%.idx}.pack") &&
+ end=$((packsz - rawsz)) &&
+ while read start oid rest
+ do
+ size=$((end - start)) &&
+ end=$start &&
+ echo "$oid $size" ||
+ return 1
+ done <idx.sorted ||
+ return 1
+ done
+ } >expect.raw &&
+ sort <expect.raw >expect &&
+ git cat-file --batch-all-objects \
+ --batch-check="%(objectname) %(objectsize:disk)" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'set up replacement object' '
+ orig=$(git rev-parse HEAD) &&
+ git cat-file commit $orig >orig &&
+ {
+ cat orig &&
+ echo extra
+ } >fake &&
+ fake=$(git hash-object -t commit -w fake) &&
+ orig_size=$(git cat-file -s $orig) &&
+ fake_size=$(git cat-file -s $fake) &&
+ git replace $orig $fake
+'
+
+test_expect_success 'cat-file --batch respects replace objects' '
+ git cat-file --batch >actual <<-EOF &&
+ $orig
+ EOF
+ {
+ echo "$orig commit $fake_size" &&
+ cat fake &&
+ echo
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cat-file --batch-check respects replace objects' '
+ git cat-file --batch-check >actual <<-EOF &&
+ $orig
+ EOF
+ echo "$orig commit $fake_size" >expect &&
+ test_cmp expect actual
+'
+
+# Pull the entry for object with oid "$1" out of the output of
+# "cat-file --batch", including its object content (which requires
+# parsing and reading a set amount of bytes, hence perl).
+extract_batch_output () {
+ perl -ne '
+ BEGIN { $oid = shift }
+ if (/^$oid \S+ (\d+)$/) {
+ print;
+ read STDIN, my $buf, $1;
+ print $buf;
+ print "\n";
+ }
+ ' "$@"
+}
+
+test_expect_success 'cat-file --batch-all-objects --batch ignores replace' '
+ git cat-file --batch-all-objects --batch >actual.raw &&
+ extract_batch_output $orig <actual.raw >actual &&
+ {
+ echo "$orig commit $orig_size" &&
+ cat orig &&
+ echo
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cat-file --batch-all-objects --batch-check ignores replace' '
+ git cat-file --batch-all-objects --batch-check >actual.raw &&
+ grep ^$orig actual.raw >actual &&
+ echo "$orig commit $orig_size" >expect &&
+ test_cmp expect actual
+'
+test_expect_success 'batch-command empty command' '
+ echo "" >cmd &&
+ test_expect_code 128 git cat-file --batch-command <cmd 2>err &&
+ grep "^fatal:.*empty command in input.*" err
+'
+
+test_expect_success 'batch-command whitespace before command' '
+ echo " info deadbeef" >cmd &&
+ test_expect_code 128 git cat-file --batch-command <cmd 2>err &&
+ grep "^fatal:.*whitespace before command.*" err
+'
+
+test_expect_success 'batch-command unknown command' '
+ echo unknown_command >cmd &&
+ test_expect_code 128 git cat-file --batch-command <cmd 2>err &&
+ grep "^fatal:.*unknown command.*" err
+'
+
+test_expect_success 'batch-command missing arguments' '
+ echo "info" >cmd &&
+ test_expect_code 128 git cat-file --batch-command <cmd 2>err &&
+ grep "^fatal:.*info requires arguments.*" err
+'
+
+test_expect_success 'batch-command flush with arguments' '
+ echo "flush arg" >cmd &&
+ test_expect_code 128 git cat-file --batch-command --buffer <cmd 2>err &&
+ grep "^fatal:.*flush takes no arguments.*" err
+'
+
+test_expect_success 'batch-command flush without --buffer' '
+ echo "flush" >cmd &&
+ test_expect_code 128 git cat-file --batch-command <cmd 2>err &&
+ grep "^fatal:.*flush is only for --buffer mode.*" err
+'
+
test_done
diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh
index 64b340f..64aea38 100755
--- a/t/t1007-hash-object.sh
+++ b/t/t1007-hash-object.sh
@@ -2,6 +2,7 @@
test_description="git hash-object"
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
echo_without_newline() {
@@ -123,8 +124,8 @@ test_expect_success 'check that appropriate filter is invoke when --path is used
path0_sha=$(git hash-object --path=file0 file1) &&
test "$file0_sha" = "$path0_sha" &&
test "$file1_sha" = "$path1_sha" &&
- path1_sha=$(cat file0 | git hash-object --path=file1 --stdin) &&
- path0_sha=$(cat file1 | git hash-object --path=file0 --stdin) &&
+ path1_sha=$(git hash-object --path=file1 --stdin <file0) &&
+ path0_sha=$(git hash-object --path=file0 --stdin <file1) &&
test "$file0_sha" = "$path0_sha" &&
test "$file1_sha" = "$path1_sha"
'
@@ -153,7 +154,7 @@ test_expect_success '--path works in a subdirectory' '
test_expect_success 'check that --no-filters option works' '
nofilters_file1=$(git hash-object --no-filters file1) &&
test "$file0_sha" = "$nofilters_file1" &&
- nofilters_file1=$(cat file1 | git hash-object --stdin) &&
+ nofilters_file1=$(git hash-object --stdin <file1) &&
test "$file0_sha" = "$nofilters_file1"
'
@@ -202,23 +203,34 @@ done
test_expect_success 'too-short tree' '
echo abc >malformed-tree &&
test_must_fail git hash-object -t tree malformed-tree 2>err &&
- test_i18ngrep "too-short tree object" err
+ grep "too-short tree object" err
'
test_expect_success 'malformed mode in tree' '
- hex_sha1=$(echo foo | git hash-object --stdin -w) &&
- bin_sha1=$(echo $hex_sha1 | hex2oct) &&
- printf "9100644 \0$bin_sha1" >tree-with-malformed-mode &&
+ hex_oid=$(echo foo | git hash-object --stdin -w) &&
+ bin_oid=$(echo $hex_oid | hex2oct) &&
+ printf "9100644 \0$bin_oid" >tree-with-malformed-mode &&
test_must_fail git hash-object -t tree tree-with-malformed-mode 2>err &&
- test_i18ngrep "malformed mode in tree entry" err
+ grep "malformed mode in tree entry" err
'
test_expect_success 'empty filename in tree' '
- hex_sha1=$(echo foo | git hash-object --stdin -w) &&
- bin_sha1=$(echo $hex_sha1 | hex2oct) &&
- printf "100644 \0$bin_sha1" >tree-with-empty-filename &&
+ hex_oid=$(echo foo | git hash-object --stdin -w) &&
+ bin_oid=$(echo $hex_oid | hex2oct) &&
+ printf "100644 \0$bin_oid" >tree-with-empty-filename &&
test_must_fail git hash-object -t tree tree-with-empty-filename 2>err &&
- test_i18ngrep "empty filename in tree entry" err
+ grep "empty filename in tree entry" err
+'
+
+test_expect_success 'duplicate filename in tree' '
+ hex_oid=$(echo foo | git hash-object --stdin -w) &&
+ bin_oid=$(echo $hex_oid | hex2oct) &&
+ {
+ printf "100644 file\0$bin_oid" &&
+ printf "100644 file\0$bin_oid"
+ } >tree-with-duplicate-filename &&
+ test_must_fail git hash-object -t tree tree-with-duplicate-filename 2>err &&
+ grep "duplicateEntries" err
'
test_expect_success 'corrupt commit' '
diff --git a/t/t1008-read-tree-overlay.sh b/t/t1008-read-tree-overlay.sh
index 4512fb0b..ad5936e 100755
--- a/t/t1008-read-tree-overlay.sh
+++ b/t/t1008-read-tree-overlay.sh
@@ -5,6 +5,7 @@ test_description='test multi-tree read-tree without merging'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-read-tree.sh
diff --git a/t/t1009-read-tree-new-index.sh b/t/t1009-read-tree-new-index.sh
index 2935f68..fc179ac 100755
--- a/t/t1009-read-tree-new-index.sh
+++ b/t/t1009-read-tree-new-index.sh
@@ -5,6 +5,7 @@ test_description='test read-tree into a fresh index file'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t1010-mktree.sh b/t/t1010-mktree.sh
index b946f87..22875ba 100755
--- a/t/t1010-mktree.sh
+++ b/t/t1010-mktree.sh
@@ -2,13 +2,14 @@
test_description='git mktree'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
- for d in a a. a0
+ for d in a a- a0
do
mkdir "$d" && echo "$d/one" >"$d/one" &&
- git add "$d"
+ git add "$d" || return 1
done &&
echo zero >one &&
git update-index --add --info-only one &&
@@ -59,11 +60,11 @@ test_expect_success 'allow missing object with --missing' '
'
test_expect_success 'mktree refuses to read ls-tree -r output (1)' '
- test_must_fail git mktree <all >actual
+ test_must_fail git mktree <all
'
test_expect_success 'mktree refuses to read ls-tree -r output (2)' '
- test_must_fail git mktree <all.withsub >actual
+ test_must_fail git mktree <all.withsub
'
test_done
diff --git a/t/t1011-read-tree-sparse-checkout.sh b/t/t1011-read-tree-sparse-checkout.sh
index 24092c0..595b24c 100755
--- a/t/t1011-read-tree-sparse-checkout.sh
+++ b/t/t1011-read-tree-sparse-checkout.sh
@@ -11,6 +11,8 @@ test_description='sparse checkout tests
A init.t
'
+TEST_CREATE_REPO_NO_TEMPLATE=1
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-read-tree.sh
@@ -53,12 +55,13 @@ test_expect_success 'read-tree without .git/info/sparse-checkout' '
'
test_expect_success 'read-tree with .git/info/sparse-checkout but disabled' '
+ mkdir .git/info &&
echo >.git/info/sparse-checkout &&
read_tree_u_must_succeed -m -u HEAD &&
git ls-files -t >result &&
test_cmp expected.swt result &&
- test -f init.t &&
- test -f sub/added
+ test_path_is_file init.t &&
+ test_path_is_file sub/added
'
test_expect_success 'read-tree --no-sparse-checkout with empty .git/info/sparse-checkout and enabled' '
@@ -67,8 +70,8 @@ test_expect_success 'read-tree --no-sparse-checkout with empty .git/info/sparse-
read_tree_u_must_succeed --no-sparse-checkout -m -u HEAD &&
git ls-files -t >result &&
test_cmp expected.swt result &&
- test -f init.t &&
- test -f sub/added
+ test_path_is_file init.t &&
+ test_path_is_file sub/added
'
test_expect_success 'read-tree with empty .git/info/sparse-checkout' '
@@ -85,8 +88,8 @@ test_expect_success 'read-tree with empty .git/info/sparse-checkout' '
S subsub/added
EOF
test_cmp expected.swt result &&
- ! test -f init.t &&
- ! test -f sub/added
+ test_path_is_missing init.t &&
+ test_path_is_missing sub/added
'
test_expect_success 'match directories with trailing slash' '
@@ -101,8 +104,8 @@ test_expect_success 'match directories with trailing slash' '
read_tree_u_must_succeed -m -u HEAD &&
git ls-files -t > result &&
test_cmp expected.swt-noinit result &&
- test ! -f init.t &&
- test -f sub/added
+ test_path_is_missing init.t &&
+ test_path_is_file sub/added
'
test_expect_success 'match directories without trailing slash' '
@@ -110,8 +113,8 @@ test_expect_success 'match directories without trailing slash' '
read_tree_u_must_succeed -m -u HEAD &&
git ls-files -t >result &&
test_cmp expected.swt-noinit result &&
- test ! -f init.t &&
- test -f sub/added
+ test_path_is_missing init.t &&
+ test_path_is_file sub/added
'
test_expect_success 'match directories with negated patterns' '
@@ -129,9 +132,9 @@ EOF
git read-tree -m -u HEAD &&
git ls-files -t >result &&
test_cmp expected.swt-negation result &&
- test ! -f init.t &&
- test ! -f sub/added &&
- test -f sub/addedtoo
+ test_path_is_missing init.t &&
+ test_path_is_missing sub/added &&
+ test_path_is_file sub/addedtoo
'
test_expect_success 'match directories with negated patterns (2)' '
@@ -150,9 +153,9 @@ EOF
git read-tree -m -u HEAD &&
git ls-files -t >result &&
test_cmp expected.swt-negation2 result &&
- test -f init.t &&
- test -f sub/added &&
- test ! -f sub/addedtoo
+ test_path_is_file init.t &&
+ test_path_is_file sub/added &&
+ test_path_is_missing sub/addedtoo
'
test_expect_success 'match directory pattern' '
@@ -160,8 +163,8 @@ test_expect_success 'match directory pattern' '
read_tree_u_must_succeed -m -u HEAD &&
git ls-files -t >result &&
test_cmp expected.swt-noinit result &&
- test ! -f init.t &&
- test -f sub/added
+ test_path_is_missing init.t &&
+ test_path_is_file sub/added
'
test_expect_success 'checkout area changes' '
@@ -176,22 +179,43 @@ test_expect_success 'checkout area changes' '
read_tree_u_must_succeed -m -u HEAD &&
git ls-files -t >result &&
test_cmp expected.swt-nosub result &&
- test -f init.t &&
- test ! -f sub/added
+ test_path_is_file init.t &&
+ test_path_is_missing sub/added
'
test_expect_success 'read-tree updates worktree, absent case' '
echo sub/added >.git/info/sparse-checkout &&
git checkout -f top &&
read_tree_u_must_succeed -m -u HEAD^ &&
- test ! -f init.t
+ test_path_is_missing init.t
+'
+
+test_expect_success 'read-tree will not throw away dirty changes, non-sparse' '
+ echo "/*" >.git/info/sparse-checkout &&
+ read_tree_u_must_succeed -m -u HEAD &&
+
+ echo dirty >init.t &&
+ read_tree_u_must_fail -m -u HEAD^ &&
+ test_path_is_file init.t &&
+ grep -q dirty init.t
+'
+
+test_expect_success 'read-tree will not throw away dirty changes, sparse' '
+ echo "/*" >.git/info/sparse-checkout &&
+ read_tree_u_must_succeed -m -u HEAD &&
+
+ echo dirty >init.t &&
+ echo sub/added >.git/info/sparse-checkout &&
+ read_tree_u_must_fail -m -u HEAD^ &&
+ test_path_is_file init.t &&
+ grep -q dirty init.t
'
test_expect_success 'read-tree updates worktree, dirty case' '
echo sub/added >.git/info/sparse-checkout &&
git checkout -f top &&
echo dirty >init.t &&
- read_tree_u_must_succeed -m -u HEAD^ &&
+ read_tree_u_must_fail -m -u HEAD^ &&
grep -q dirty init.t &&
rm init.t
'
@@ -208,7 +232,7 @@ test_expect_success 'read-tree adds to worktree, absent case' '
echo init.t >.git/info/sparse-checkout &&
git checkout -f removed &&
read_tree_u_must_succeed -u -m HEAD^ &&
- test ! -f sub/added
+ test_path_is_missing sub/added
'
test_expect_success 'read-tree adds to worktree, dirty case' '
@@ -227,7 +251,7 @@ test_expect_success 'index removal and worktree narrowing at the same time' '
echo init.t >.git/info/sparse-checkout &&
git checkout removed &&
git ls-files sub/added >result &&
- test ! -f sub/added &&
+ test_path_is_missing sub/added &&
test_must_be_empty result
'
diff --git a/t/t1012-read-tree-df.sh b/t/t1012-read-tree-df.sh
index 57f0770..cde93d2 100755
--- a/t/t1012-read-tree-df.sh
+++ b/t/t1012-read-tree-df.sh
@@ -2,6 +2,7 @@
test_description='read-tree D/F conflict corner cases'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-read-tree.sh
diff --git a/t/t1013-read-tree-submodule.sh b/t/t1013-read-tree-submodule.sh
index b6df744..bfc90d4 100755
--- a/t/t1013-read-tree-submodule.sh
+++ b/t/t1013-read-tree-submodule.sh
@@ -6,7 +6,6 @@ test_description='read-tree can handle submodules'
. "$TEST_DIRECTORY"/lib-submodule-update.sh
KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS=1
-KNOWN_FAILURE_SUBMODULE_OVERWRITE_IGNORED_UNTRACKED=1
test_submodule_switch_recursing_with_args "read-tree -u -m"
diff --git a/t/t1014-read-tree-confusing.sh b/t/t1014-read-tree-confusing.sh
index da3376b..8ea8d36 100755
--- a/t/t1014-read-tree-confusing.sh
+++ b/t/t1014-read-tree-confusing.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='check that read-tree rejects confusing paths'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'create base tree' '
diff --git a/t/t1016-compatObjectFormat.sh b/t/t1016-compatObjectFormat.sh
new file mode 100755
index 0000000..be3206a
--- /dev/null
+++ b/t/t1016-compatObjectFormat.sh
@@ -0,0 +1,281 @@
+#!/bin/sh
+#
+# Copyright (c) 2023 Eric Biederman
+#
+
+test_description='Test how well compatObjectFormat works'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-gpg.sh
+
+# All of the follow variables must be defined in the environment:
+# GIT_AUTHOR_NAME
+# GIT_AUTHOR_EMAIL
+# GIT_AUTHOR_DATE
+# GIT_COMMITTER_NAME
+# GIT_COMMITTER_EMAIL
+# GIT_COMMITTER_DATE
+#
+# The test relies on these variables being set so that the two
+# different commits in two different repositories encoded with two
+# different hash functions result in the same content in the commits.
+# This means that when the commit is translated between hash functions
+# the commit is identical to the commit in the other repository.
+
+compat_hash () {
+ case "$1" in
+ "sha1")
+ echo "sha256"
+ ;;
+ "sha256")
+ echo "sha1"
+ ;;
+ esac
+}
+
+hello_oid () {
+ case "$1" in
+ "sha1")
+ echo "$hello_sha1_oid"
+ ;;
+ "sha256")
+ echo "$hello_sha256_oid"
+ ;;
+ esac
+}
+
+tree_oid () {
+ case "$1" in
+ "sha1")
+ echo "$tree_sha1_oid"
+ ;;
+ "sha256")
+ echo "$tree_sha256_oid"
+ ;;
+ esac
+}
+
+commit_oid () {
+ case "$1" in
+ "sha1")
+ echo "$commit_sha1_oid"
+ ;;
+ "sha256")
+ echo "$commit_sha256_oid"
+ ;;
+ esac
+}
+
+commit2_oid () {
+ case "$1" in
+ "sha1")
+ echo "$commit2_sha1_oid"
+ ;;
+ "sha256")
+ echo "$commit2_sha256_oid"
+ ;;
+ esac
+}
+
+del_sigcommit () {
+ local delete="$1"
+
+ if test "$delete" = "sha256" ; then
+ local pattern="gpgsig-sha256"
+ else
+ local pattern="gpgsig"
+ fi
+ test-tool delete-gpgsig "$pattern"
+}
+
+
+del_sigtag () {
+ local storage="$1"
+ local delete="$2"
+
+ if test "$storage" = "$delete" ; then
+ local pattern="trailer"
+ elif test "$storage" = "sha256" ; then
+ local pattern="gpgsig"
+ else
+ local pattern="gpgsig-sha256"
+ fi
+ test-tool delete-gpgsig "$pattern"
+}
+
+base=$(pwd)
+for hash in sha1 sha256
+do
+ cd "$base"
+ mkdir -p repo-$hash
+ cd repo-$hash
+
+ test_expect_success "setup $hash repository" '
+ git init --object-format=$hash &&
+ git config core.repositoryformatversion 1 &&
+ git config extensions.objectformat $hash &&
+ git config extensions.compatobjectformat $(compat_hash $hash) &&
+ git config gpg.program $TEST_DIRECTORY/t1016/gpg &&
+ echo "Hellow World!" > hello &&
+ eval hello_${hash}_oid=$(git hash-object hello) &&
+ git update-index --add hello &&
+ git commit -m "Initial commit" &&
+ eval commit_${hash}_oid=$(git rev-parse HEAD) &&
+ eval tree_${hash}_oid=$(git rev-parse HEAD^{tree})
+ '
+ test_expect_success "create a $hash tagged blob" '
+ git tag --no-sign -m "This is a tag" hellotag $(hello_oid $hash) &&
+ eval hellotag_${hash}_oid=$(git rev-parse hellotag)
+ '
+ test_expect_success "create a $hash tagged tree" '
+ git tag --no-sign -m "This is a tag" treetag $(tree_oid $hash) &&
+ eval treetag_${hash}_oid=$(git rev-parse treetag)
+ '
+ test_expect_success "create a $hash tagged commit" '
+ git tag --no-sign -m "This is a tag" committag $(commit_oid $hash) &&
+ eval committag_${hash}_oid=$(git rev-parse committag)
+ '
+ test_expect_success GPG2 "create a $hash signed commit" '
+ git commit --gpg-sign --allow-empty -m "This is a signed commit" &&
+ eval signedcommit_${hash}_oid=$(git rev-parse HEAD)
+ '
+ test_expect_success GPG2 "create a $hash signed tag" '
+ git tag -s -m "This is a signed tag" signedtag HEAD &&
+ eval signedtag_${hash}_oid=$(git rev-parse signedtag)
+ '
+ test_expect_success "create a $hash branch" '
+ git checkout -b branch $(commit_oid $hash) &&
+ echo "More more more give me more!" > more &&
+ eval more_${hash}_oid=$(git hash-object more) &&
+ echo "Another and another and another" > another &&
+ eval another_${hash}_oid=$(git hash-object another) &&
+ git update-index --add more another &&
+ git commit -m "Add more files!" &&
+ eval commit2_${hash}_oid=$(git rev-parse HEAD) &&
+ eval tree2_${hash}_oid=$(git rev-parse HEAD^{tree})
+ '
+ test_expect_success GPG2 "create another $hash signed tag" '
+ git tag -s -m "This is another signed tag" signedtag2 $(commit2_oid $hash) &&
+ eval signedtag2_${hash}_oid=$(git rev-parse signedtag2)
+ '
+ test_expect_success GPG2 "merge the $hash branches together" '
+ git merge -S -m "merge some signed tags together" signedtag signedtag2 &&
+ eval signedcommit2_${hash}_oid=$(git rev-parse HEAD)
+ '
+ test_expect_success GPG2 "create additional $hash signed commits" '
+ git commit --gpg-sign --allow-empty -m "This is an additional signed commit" &&
+ git cat-file commit HEAD | del_sigcommit sha256 > "../${hash}_signedcommit3" &&
+ git cat-file commit HEAD | del_sigcommit sha1 > "../${hash}_signedcommit4" &&
+ eval signedcommit3_${hash}_oid=$(git hash-object -t commit -w ../${hash}_signedcommit3) &&
+ eval signedcommit4_${hash}_oid=$(git hash-object -t commit -w ../${hash}_signedcommit4)
+ '
+ test_expect_success GPG2 "create additional $hash signed tags" '
+ git tag -s -m "This is an additional signed tag" signedtag34 HEAD &&
+ git cat-file tag signedtag34 | del_sigtag "${hash}" sha256 > ../${hash}_signedtag3 &&
+ git cat-file tag signedtag34 | del_sigtag "${hash}" sha1 > ../${hash}_signedtag4 &&
+ eval signedtag3_${hash}_oid=$(git hash-object -t tag -w ../${hash}_signedtag3) &&
+ eval signedtag4_${hash}_oid=$(git hash-object -t tag -w ../${hash}_signedtag4)
+ '
+done
+cd "$base"
+
+compare_oids () {
+ test "$#" = 5 && { local PREREQ="$1"; shift; } || PREREQ=
+ local type="$1"
+ local name="$2"
+ local sha1_oid="$3"
+ local sha256_oid="$4"
+
+ echo ${sha1_oid} > ${name}_sha1_expected
+ echo ${sha256_oid} > ${name}_sha256_expected
+ echo ${type} > ${name}_type_expected
+
+ git --git-dir=repo-sha1/.git rev-parse --output-object-format=sha256 ${sha1_oid} > ${name}_sha1_sha256_found
+ git --git-dir=repo-sha256/.git rev-parse --output-object-format=sha1 ${sha256_oid} > ${name}_sha256_sha1_found
+ local sha1_sha256_oid="$(cat ${name}_sha1_sha256_found)"
+ local sha256_sha1_oid="$(cat ${name}_sha256_sha1_found)"
+
+ test_expect_success $PREREQ "Verify ${type} ${name}'s sha1 oid" '
+ git --git-dir=repo-sha256/.git rev-parse --output-object-format=sha1 ${sha256_oid} > ${name}_sha1 &&
+ test_cmp ${name}_sha1 ${name}_sha1_expected
+'
+
+ test_expect_success $PREREQ "Verify ${type} ${name}'s sha256 oid" '
+ git --git-dir=repo-sha1/.git rev-parse --output-object-format=sha256 ${sha1_oid} > ${name}_sha256 &&
+ test_cmp ${name}_sha256 ${name}_sha256_expected
+'
+
+ test_expect_success $PREREQ "Verify ${name}'s sha1 type" '
+ git --git-dir=repo-sha1/.git cat-file -t ${sha1_oid} > ${name}_type1 &&
+ git --git-dir=repo-sha256/.git cat-file -t ${sha256_sha1_oid} > ${name}_type2 &&
+ test_cmp ${name}_type1 ${name}_type2 &&
+ test_cmp ${name}_type1 ${name}_type_expected
+'
+
+ test_expect_success $PREREQ "Verify ${name}'s sha256 type" '
+ git --git-dir=repo-sha256/.git cat-file -t ${sha256_oid} > ${name}_type3 &&
+ git --git-dir=repo-sha1/.git cat-file -t ${sha1_sha256_oid} > ${name}_type4 &&
+ test_cmp ${name}_type3 ${name}_type4 &&
+ test_cmp ${name}_type3 ${name}_type_expected
+'
+
+ test_expect_success $PREREQ "Verify ${name}'s sha1 size" '
+ git --git-dir=repo-sha1/.git cat-file -s ${sha1_oid} > ${name}_size1 &&
+ git --git-dir=repo-sha256/.git cat-file -s ${sha256_sha1_oid} > ${name}_size2 &&
+ test_cmp ${name}_size1 ${name}_size2
+'
+
+ test_expect_success $PREREQ "Verify ${name}'s sha256 size" '
+ git --git-dir=repo-sha256/.git cat-file -s ${sha256_oid} > ${name}_size3 &&
+ git --git-dir=repo-sha1/.git cat-file -s ${sha1_sha256_oid} > ${name}_size4 &&
+ test_cmp ${name}_size3 ${name}_size4
+'
+
+ test_expect_success $PREREQ "Verify ${name}'s sha1 pretty content" '
+ git --git-dir=repo-sha1/.git cat-file -p ${sha1_oid} > ${name}_content1 &&
+ git --git-dir=repo-sha256/.git cat-file -p ${sha256_sha1_oid} > ${name}_content2 &&
+ test_cmp ${name}_content1 ${name}_content2
+'
+
+ test_expect_success $PREREQ "Verify ${name}'s sha256 pretty content" '
+ git --git-dir=repo-sha256/.git cat-file -p ${sha256_oid} > ${name}_content3 &&
+ git --git-dir=repo-sha1/.git cat-file -p ${sha1_sha256_oid} > ${name}_content4 &&
+ test_cmp ${name}_content3 ${name}_content4
+'
+
+ test_expect_success $PREREQ "Verify ${name}'s sha1 content" '
+ git --git-dir=repo-sha1/.git cat-file ${type} ${sha1_oid} > ${name}_content5 &&
+ git --git-dir=repo-sha256/.git cat-file ${type} ${sha256_sha1_oid} > ${name}_content6 &&
+ test_cmp ${name}_content5 ${name}_content6
+'
+
+ test_expect_success $PREREQ "Verify ${name}'s sha256 content" '
+ git --git-dir=repo-sha256/.git cat-file ${type} ${sha256_oid} > ${name}_content7 &&
+ git --git-dir=repo-sha1/.git cat-file ${type} ${sha1_sha256_oid} > ${name}_content8 &&
+ test_cmp ${name}_content7 ${name}_content8
+'
+
+}
+
+compare_oids 'blob' hello "$hello_sha1_oid" "$hello_sha256_oid"
+compare_oids 'tree' tree "$tree_sha1_oid" "$tree_sha256_oid"
+compare_oids 'commit' commit "$commit_sha1_oid" "$commit_sha256_oid"
+compare_oids GPG2 'commit' signedcommit "$signedcommit_sha1_oid" "$signedcommit_sha256_oid"
+compare_oids 'tag' hellotag "$hellotag_sha1_oid" "$hellotag_sha256_oid"
+compare_oids 'tag' treetag "$treetag_sha1_oid" "$treetag_sha256_oid"
+compare_oids 'tag' committag "$committag_sha1_oid" "$committag_sha256_oid"
+compare_oids GPG2 'tag' signedtag "$signedtag_sha1_oid" "$signedtag_sha256_oid"
+
+compare_oids 'blob' more "$more_sha1_oid" "$more_sha256_oid"
+compare_oids 'blob' another "$another_sha1_oid" "$another_sha256_oid"
+compare_oids 'tree' tree2 "$tree2_sha1_oid" "$tree2_sha256_oid"
+compare_oids 'commit' commit2 "$commit2_sha1_oid" "$commit2_sha256_oid"
+compare_oids GPG2 'tag' signedtag2 "$signedtag2_sha1_oid" "$signedtag2_sha256_oid"
+compare_oids GPG2 'commit' signedcommit2 "$signedcommit2_sha1_oid" "$signedcommit2_sha256_oid"
+compare_oids GPG2 'commit' signedcommit3 "$signedcommit3_sha1_oid" "$signedcommit3_sha256_oid"
+compare_oids GPG2 'commit' signedcommit4 "$signedcommit4_sha1_oid" "$signedcommit4_sha256_oid"
+compare_oids GPG2 'tag' signedtag3 "$signedtag3_sha1_oid" "$signedtag3_sha256_oid"
+compare_oids GPG2 'tag' signedtag4 "$signedtag4_sha1_oid" "$signedtag4_sha256_oid"
+
+test_done
diff --git a/t/t1016/gpg b/t/t1016/gpg
new file mode 100755
index 0000000..2601cb1
--- /dev/null
+++ b/t/t1016/gpg
@@ -0,0 +1,2 @@
+#!/bin/sh
+exec gpg --faked-system-time "20230918T154812" "$@"
diff --git a/t/t1020-subdirectory.sh b/t/t1020-subdirectory.sh
index c2df75e..45eef94 100755
--- a/t/t1020-subdirectory.sh
+++ b/t/t1020-subdirectory.sh
@@ -6,14 +6,15 @@
test_description='Try various core-level commands in subdirectory.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-read-tree.sh
test_expect_success setup '
long="a b c d e f g h i j k l m n o p q r s t u v w x y z" &&
- for c in $long; do echo $c; done >one &&
+ test_write_lines $long >one &&
mkdir dir &&
- for c in x y z $long a b c; do echo $c; done >dir/two &&
+ test_write_lines x y z $long a b c >dir/two &&
cp one original.one &&
cp dir/two original.two
'
@@ -22,7 +23,7 @@ test_expect_success 'update-index and ls-files' '
git update-index --add one &&
case "$(git ls-files)" in
one) echo pass one ;;
- *) echo bad one; exit 1 ;;
+ *) echo bad one; return 1 ;;
esac &&
(
cd dir &&
@@ -34,7 +35,7 @@ test_expect_success 'update-index and ls-files' '
) &&
case "$(git ls-files)" in
dir/two"$LF"one) echo pass both ;;
- *) echo bad; exit 1 ;;
+ *) echo bad; return 1 ;;
esac
'
@@ -57,7 +58,7 @@ test_expect_success 'diff-files' '
echo d >>dir/two &&
case "$(git diff-files --name-only)" in
dir/two"$LF"one) echo pass top ;;
- *) echo bad top; exit 1 ;;
+ *) echo bad top; return 1 ;;
esac &&
# diff should not omit leading paths
(
diff --git a/t/t1022-read-tree-partial-clone.sh b/t/t1022-read-tree-partial-clone.sh
index a763e27..cca4380 100755
--- a/t/t1022-read-tree-partial-clone.sh
+++ b/t/t1022-read-tree-partial-clone.sh
@@ -3,10 +3,7 @@
test_description='git read-tree in partial clones'
TEST_NO_CREATE_REPO=1
-
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'read-tree in partial clone prefetches in one batch' '
@@ -22,7 +19,7 @@ test_expect_success 'read-tree in partial clone prefetches in one batch' '
git -C server config uploadpack.allowfilter 1 &&
git -C server config uploadpack.allowanysha1inwant 1 &&
git clone --bare --filter=blob:none "file://$(pwd)/server" client &&
- GIT_TRACE_PACKET="$(pwd)/trace" git -C client read-tree $TREE &&
+ GIT_TRACE_PACKET="$(pwd)/trace" git -C client read-tree $TREE $TREE &&
# "done" marks the end of negotiation (once per fetch). Expect that
# only one fetch occurs.
diff --git a/t/t1050-large.sh b/t/t1050-large.sh
index 4bab6a5..c71932b 100755
--- a/t/t1050-large.sh
+++ b/t/t1050-large.sh
@@ -5,6 +5,12 @@ test_description='adding and checking out large blobs'
. ./test-lib.sh
+test_expect_success 'core.bigFileThreshold must be non-negative' '
+ test_must_fail git -c core.bigFileThreshold=-1 rev-parse >out 2>err &&
+ grep "bad numeric config value" err &&
+ test_must_be_empty out
+'
+
test_expect_success setup '
# clone does not allow us to pass core.bigfilethreshold to
# new repos, so set core.bigfilethreshold globally
@@ -17,6 +23,14 @@ test_expect_success setup '
export GIT_ALLOC_LIMIT
'
+test_expect_success 'enter "large" codepath, with small core.bigFileThreshold' '
+ test_when_finished "rm -rf repo" &&
+
+ git init --bare repo &&
+ echo large | git -C repo hash-object -w --stdin &&
+ git -C repo -c core.bigfilethreshold=4 fsck
+'
+
# add a large file with different settings
while read expect config
do
@@ -43,42 +57,32 @@ EOF
test_expect_success 'add a large file or two' '
git add large1 huge large2 &&
# make sure we got a single packfile and no loose objects
- bad= count=0 idx= &&
+ count=0 idx= &&
for p in .git/objects/pack/pack-*.pack
do
- count=$(( $count + 1 ))
- if test_path_is_file "$p" &&
- idx=${p%.pack}.idx && test_path_is_file "$idx"
- then
- continue
- fi
- bad=t
+ count=$(( $count + 1 )) &&
+ test_path_is_file "$p" &&
+ idx=${p%.pack}.idx &&
+ test_path_is_file "$idx" || return 1
done &&
- test -z "$bad" &&
test $count = 1 &&
cnt=$(git show-index <"$idx" | wc -l) &&
test $cnt = 2 &&
for l in .git/objects/$OIDPATH_REGEX
do
- test_path_is_file "$l" || continue
- bad=t
+ test_path_is_missing "$l" || return 1
done &&
- test -z "$bad" &&
# attempt to add another copy of the same
git add large3 &&
bad= count=0 &&
for p in .git/objects/pack/pack-*.pack
do
- count=$(( $count + 1 ))
- if test_path_is_file "$p" &&
- idx=${p%.pack}.idx && test_path_is_file "$idx"
- then
- continue
- fi
- bad=t
+ count=$(( $count + 1 )) &&
+ test_path_is_file "$p" &&
+ idx=${p%.pack}.idx &&
+ test_path_is_file "$idx" || return 1
done &&
- test -z "$bad" &&
test $count = 1
'
@@ -107,7 +111,7 @@ test_expect_success 'packsize limit' '
count=0 &&
for pi in .git/objects/pack/pack-*.idx
do
- test_path_is_file "$pi" && count=$(( $count + 1 ))
+ test_path_is_file "$pi" && count=$(( $count + 1 )) || return 1
done &&
test $count = 2 &&
@@ -120,7 +124,7 @@ test_expect_success 'packsize limit' '
for pi in .git/objects/pack/pack-*.idx
do
- git show-index <"$pi"
+ git show-index <"$pi" || return 1
done |
sed -e "s/^[0-9]* \([0-9a-f]*\) .*/\1/" |
sort >actual &&
diff --git a/t/t1051-large-conversion.sh b/t/t1051-large-conversion.sh
index 8b7640b..f6709c9 100755
--- a/t/t1051-large-conversion.sh
+++ b/t/t1051-large-conversion.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test conversion filters on large files'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
set_attr() {
@@ -83,4 +85,30 @@ test_expect_success 'ident converts on output' '
test_cmp small.clean large.clean
'
+# This smudge filter prepends 5GB of zeros to the file it checks out. This
+# ensures that smudging doesn't mangle large files on 64-bit Windows.
+test_expect_success EXPENSIVE,SIZE_T_IS_64BIT,!LONG_IS_64BIT \
+ 'files over 4GB convert on output' '
+ test_commit test small "a small file" &&
+ small_size=$(test_file_size small) &&
+ test_config filter.makelarge.smudge \
+ "test-tool genzeros $((5*1024*1024*1024)) && cat" &&
+ echo "small filter=makelarge" >.gitattributes &&
+ rm small &&
+ git checkout -- small &&
+ size=$(test_file_size small) &&
+ test "$size" -eq $((5 * 1024 * 1024 * 1024 + $small_size))
+'
+
+# This clean filter writes down the size of input it receives. By checking against
+# the actual size, we ensure that cleaning doesn't mangle large files on 64-bit Windows.
+test_expect_success EXPENSIVE,SIZE_T_IS_64BIT,!LONG_IS_64BIT \
+ 'files over 4GB convert on input' '
+ test-tool genzeros $((5*1024*1024*1024)) >big &&
+ test_config filter.checklarge.clean "wc -c >big.size" &&
+ echo "big filter=checklarge" >.gitattributes &&
+ git add big &&
+ test $(test_file_size big) -eq $(cat big.size)
+'
+
test_done
diff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh
index bc89371..5e0f0a3 100755
--- a/t/t1060-object-corruption.sh
+++ b/t/t1060-object-corruption.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='see how we handle various forms of corruption'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# convert "1234abcd" to ".git/objects/12/34abcd"
@@ -123,7 +125,7 @@ test_expect_success 'fetch into corrupted repo with index-pack' '
cd bit-error-cp &&
test_must_fail git -c transfer.unpackLimit=1 \
fetch ../no-bit-error 2>stderr &&
- test_i18ngrep ! -i collision stderr
+ test_grep ! -i collision stderr
)
'
@@ -137,4 +139,11 @@ test_expect_success 'internal tree objects are not "missing"' '
)
'
+test_expect_success 'partial clone of corrupted repository' '
+ test_config -C misnamed uploadpack.allowFilter true &&
+ git clone --no-local --no-checkout --filter=blob:none \
+ misnamed corrupt-partial && \
+ test_must_fail git -C corrupt-partial checkout --force
+'
+
test_done
diff --git a/t/t1090-sparse-checkout-scope.sh b/t/t1090-sparse-checkout-scope.sh
index 3deb490..3a14218 100755
--- a/t/t1090-sparse-checkout-scope.sh
+++ b/t/t1090-sparse-checkout-scope.sh
@@ -5,6 +5,7 @@ test_description='sparse checkout scope tests'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_CREATE_REPO_NO_TEMPLATE=1
. ./test-lib.sh
test_expect_success 'setup' '
@@ -25,6 +26,7 @@ test_expect_success 'create feature branch' '
test_expect_success 'perform sparse checkout of main' '
git config --local --bool core.sparsecheckout true &&
+ mkdir .git/info &&
echo "!/*" >.git/info/sparse-checkout &&
echo "/a" >>.git/info/sparse-checkout &&
echo "/c" >>.git/info/sparse-checkout &&
@@ -52,9 +54,28 @@ test_expect_success 'return to full checkout of main' '
test "$(cat b)" = "modified"
'
+test_expect_success 'skip-worktree on files outside sparse patterns' '
+ git sparse-checkout disable &&
+ git sparse-checkout set --no-cone "a*" &&
+ git checkout-index --all --ignore-skip-worktree-bits &&
+
+ git ls-files -t >output &&
+ ! grep ^S output >actual &&
+ test_must_be_empty actual &&
+
+ test_config sparse.expectFilesOutsideOfPatterns true &&
+ cat <<-\EOF >expect &&
+ S b
+ S c
+ EOF
+ git ls-files -t >output &&
+ grep ^S output >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'in partial clone, sparse checkout only fetches needed blobs' '
test_create_repo server &&
- git clone "file://$(pwd)/server" client &&
+ git clone --template= "file://$(pwd)/server" client &&
test_config -C server uploadpack.allowfilter 1 &&
test_config -C server uploadpack.allowanysha1inwant 1 &&
@@ -66,6 +87,7 @@ test_expect_success 'in partial clone, sparse checkout only fetches needed blobs
git -C server commit -m message &&
test_config -C client core.sparsecheckout 1 &&
+ mkdir client/.git/info &&
echo "!/*" >client/.git/info/sparse-checkout &&
echo "/a" >>client/.git/info/sparse-checkout &&
git -C client fetch --filter=blob:none origin &&
diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh
index 38fc834..ab3a105 100755
--- a/t/t1091-sparse-checkout-builtin.sh
+++ b/t/t1091-sparse-checkout-builtin.sh
@@ -5,6 +5,9 @@ test_description='sparse checkout builtin tests'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+GIT_TEST_SPLIT_INDEX=false
+export GIT_TEST_SPLIT_INDEX
+
. ./test-lib.sh
list_files() {
@@ -41,10 +44,18 @@ test_expect_success 'setup' '
)
'
-test_expect_success 'git sparse-checkout list (empty)' '
+test_expect_success 'git sparse-checkout list (not sparse)' '
+ test_must_fail git -C repo sparse-checkout list >list 2>err &&
+ test_must_be_empty list &&
+ test_grep "this worktree is not sparse" err
+'
+
+test_expect_success 'git sparse-checkout list (not sparse)' '
+ git -C repo sparse-checkout set &&
+ rm repo/.git/info/sparse-checkout &&
git -C repo sparse-checkout list >list 2>err &&
test_must_be_empty list &&
- test_i18ngrep "this worktree is not sparse (sparse-checkout file may not exist)" err
+ test_grep "this worktree is not sparse (sparse-checkout file may not exist)" err
'
test_expect_success 'git sparse-checkout list (populated)' '
@@ -61,7 +72,7 @@ test_expect_success 'git sparse-checkout list (populated)' '
'
test_expect_success 'git sparse-checkout init' '
- git -C repo sparse-checkout init &&
+ git -C repo sparse-checkout init --no-cone &&
cat >expect <<-\EOF &&
/*
!/*/
@@ -71,6 +82,12 @@ test_expect_success 'git sparse-checkout init' '
check_files repo a
'
+test_expect_success 'git sparse-checkout init in empty repo' '
+ test_when_finished rm -rf empty-repo blank-template &&
+ git init --template= empty-repo &&
+ git -C empty-repo sparse-checkout init
+'
+
test_expect_success 'git sparse-checkout list after init' '
git -C repo sparse-checkout list >actual &&
cat >expect <<-\EOF &&
@@ -94,6 +111,7 @@ test_expect_success 'init with existing sparse-checkout' '
test_expect_success 'clone --sparse' '
git clone --sparse "file://$(pwd)/repo" clone &&
+ git -C clone sparse-checkout reapply --no-cone &&
git -C clone sparse-checkout list >actual &&
cat >expect <<-\EOF &&
/*
@@ -103,6 +121,18 @@ test_expect_success 'clone --sparse' '
check_files clone a
'
+test_expect_success 'switching to cone mode with non-cone mode patterns' '
+ git init bad-patterns &&
+ (
+ cd bad-patterns &&
+ git sparse-checkout init --no-cone &&
+ git sparse-checkout add dir &&
+ git config --worktree core.sparseCheckoutCone true &&
+ test_must_fail git sparse-checkout add dir 2>err &&
+ grep "existing sparse-checkout patterns do not use cone mode" err
+ )
+'
+
test_expect_success 'interaction with clone --no-checkout (unborn index)' '
git clone --no-checkout "file://$(pwd)/repo" clone_no_checkout &&
git -C clone_no_checkout sparse-checkout init --cone &&
@@ -126,9 +156,9 @@ test_expect_success 'interaction with clone --no-checkout (unborn index)' '
'
test_expect_success 'set enables config' '
- git init empty-config &&
+ git init worktree-config &&
(
- cd empty-config &&
+ cd worktree-config &&
test_commit test file &&
test_path_is_missing .git/config.worktree &&
git sparse-checkout set nothing &&
@@ -165,12 +195,14 @@ test_expect_success 'set sparse-checkout using --stdin' '
'
test_expect_success 'add to sparse-checkout' '
- cat repo/.git/info/sparse-checkout >expect &&
+ cat repo/.git/info/sparse-checkout >old &&
+ test_when_finished cp old repo/.git/info/sparse-checkout &&
cat >add <<-\EOF &&
pattern1
/folder1/
pattern2
EOF
+ cat old >expect &&
cat add >>expect &&
git -C repo sparse-checkout add --stdin <add &&
git -C repo sparse-checkout list >actual &&
@@ -179,11 +211,26 @@ test_expect_success 'add to sparse-checkout' '
check_files repo "a folder1 folder2"
'
+test_expect_success 'worktree: add copies sparse-checkout patterns' '
+ cat repo/.git/info/sparse-checkout >old &&
+ test_when_finished cp old repo/.git/info/sparse-checkout &&
+ test_when_finished git -C repo worktree remove ../worktree &&
+ git -C repo sparse-checkout set --no-cone "/*" &&
+ git -C repo worktree add --quiet ../worktree 2>err &&
+ test_must_be_empty err &&
+ new="$(git -C worktree rev-parse --git-path info/sparse-checkout)" &&
+ test_path_is_file "$new" &&
+ test_cmp repo/.git/info/sparse-checkout "$new" &&
+ git -C worktree sparse-checkout set --cone &&
+ test_cmp_config -C worktree true core.sparseCheckoutCone &&
+ test_must_fail git -C repo core.sparseCheckoutCone
+'
+
test_expect_success 'cone mode: match patterns' '
git -C repo config --worktree core.sparseCheckoutCone true &&
rm -rf repo/a repo/folder1 repo/folder2 &&
git -C repo read-tree -mu HEAD 2>err &&
- test_i18ngrep ! "disabling cone patterns" err &&
+ test_grep ! "disabling cone patterns" err &&
git -C repo reset --hard &&
check_files repo a folder1 folder2
'
@@ -191,9 +238,9 @@ test_expect_success 'cone mode: match patterns' '
test_expect_success 'cone mode: warn on bad pattern' '
test_when_finished mv sparse-checkout repo/.git/info/ &&
cp repo/.git/info/sparse-checkout . &&
- echo "!/deep/deeper/*" >>repo/.git/info/sparse-checkout &&
+ echo "!/deep/deeper/*/" >>repo/.git/info/sparse-checkout &&
git -C repo read-tree -mu HEAD 2>err &&
- test_i18ngrep "unrecognized negative pattern" err
+ test_grep "unrecognized negative pattern" err
'
test_expect_success 'sparse-checkout disable' '
@@ -208,20 +255,35 @@ test_expect_success 'sparse-checkout disable' '
test_expect_success 'sparse-index enabled and disabled' '
git -C repo sparse-checkout init --cone --sparse-index &&
test_cmp_config -C repo true index.sparse &&
- test-tool -C repo read-cache --table >cache &&
- grep " tree " cache &&
-
+ git -C repo ls-files --sparse >sparse &&
git -C repo sparse-checkout disable &&
- test-tool -C repo read-cache --table >cache &&
- ! grep " tree " cache &&
+ git -C repo ls-files --sparse >full &&
+
+ cat >expect <<-\EOF &&
+ @@ -1,4 +1,7 @@
+ a
+ -deep/
+ -folder1/
+ -folder2/
+ +deep/a
+ +deep/deeper1/a
+ +deep/deeper1/deepest/a
+ +deep/deeper2/a
+ +folder1/a
+ +folder2/a
+ EOF
+
+ diff -u sparse full | tail -n +3 >actual &&
+ test_cmp expect actual &&
+
git -C repo config --list >config &&
- ! grep index.sparse config
+ test_cmp_config -C repo false index.sparse
'
test_expect_success 'cone mode: init and set' '
git -C repo sparse-checkout init --cone &&
git -C repo config --list >config &&
- test_i18ngrep "core.sparsecheckoutcone=true" config &&
+ test_grep "core.sparsecheckoutcone=true" config &&
list_files repo >dir &&
echo a >expect &&
test_cmp expect dir &&
@@ -272,7 +334,7 @@ test_expect_success 'cone mode: set with nested folders' '
test_expect_success 'cone mode: add independent path' '
git -C repo sparse-checkout set deep/deeper1 &&
- git -C repo sparse-checkout add folder1 &&
+ git -C repo sparse-checkout add --end-of-options folder1 &&
cat >expect <<-\EOF &&
/*
!/*/
@@ -324,7 +386,7 @@ test_expect_success 'not-up-to-date does not block rest of sparsification' '
git -C repo sparse-checkout set deep/deeper1 2>err &&
- test_i18ngrep "The following paths are not up to date" err &&
+ test_grep "The following paths are not up to date" err &&
test_cmp expect repo/.git/info/sparse-checkout &&
check_files repo/deep a deeper1 deeper2 &&
check_files repo/deep/deeper1 a deepest &&
@@ -339,9 +401,9 @@ test_expect_success 'revert to old sparse-checkout on empty update' '
git add file &&
git commit -m "test" &&
git sparse-checkout set nothing 2>err &&
- test_i18ngrep ! "Sparse checkout leaves no entry on working directory" err &&
- test_i18ngrep ! ".git/index.lock" err &&
- git sparse-checkout set file
+ test_grep ! "Sparse checkout leaves no entry on working directory" err &&
+ test_grep ! ".git/index.lock" err &&
+ git sparse-checkout set --no-cone file
)
'
@@ -349,32 +411,32 @@ test_expect_success 'fail when lock is taken' '
test_when_finished rm -rf repo/.git/info/sparse-checkout.lock &&
touch repo/.git/info/sparse-checkout.lock &&
test_must_fail git -C repo sparse-checkout set deep 2>err &&
- test_i18ngrep "Unable to create .*\.lock" err
+ test_grep "Unable to create .*\.lock" err
'
test_expect_success '.gitignore should not warn about cone mode' '
git -C repo config --worktree core.sparseCheckoutCone true &&
echo "**/bin/*" >repo/.gitignore &&
git -C repo reset --hard 2>err &&
- test_i18ngrep ! "disabling cone patterns" err
+ test_grep ! "disabling cone patterns" err
'
test_expect_success 'sparse-checkout (init|set|disable) warns with dirty status' '
git clone repo dirty &&
echo dirty >dirty/folder1/a &&
- git -C dirty sparse-checkout init 2>err &&
- test_i18ngrep "warning.*The following paths are not up to date" err &&
+ git -C dirty sparse-checkout init --no-cone 2>err &&
+ test_grep "warning.*The following paths are not up to date" err &&
git -C dirty sparse-checkout set /folder2/* /deep/deeper1/* 2>err &&
- test_i18ngrep "warning.*The following paths are not up to date" err &&
+ test_grep "warning.*The following paths are not up to date" err &&
test_path_is_file dirty/folder1/a &&
git -C dirty sparse-checkout disable 2>err &&
test_must_be_empty err &&
git -C dirty reset --hard &&
- git -C dirty sparse-checkout init &&
+ git -C dirty sparse-checkout init --no-cone &&
git -C dirty sparse-checkout set /folder2/* /deep/deeper1/* &&
test_path_is_missing dirty/folder1/a &&
git -C dirty sparse-checkout disable &&
@@ -390,23 +452,23 @@ test_expect_success 'sparse-checkout (init|set|disable) warns with unmerged stat
EOF
git -C unmerged update-index --index-info <input &&
- git -C unmerged sparse-checkout init 2>err &&
- test_i18ngrep "warning.*The following paths are unmerged" err &&
+ git -C unmerged sparse-checkout init --no-cone 2>err &&
+ test_grep "warning.*The following paths are unmerged" err &&
git -C unmerged sparse-checkout set /folder2/* /deep/deeper1/* 2>err &&
- test_i18ngrep "warning.*The following paths are unmerged" err &&
+ test_grep "warning.*The following paths are unmerged" err &&
test_path_is_file dirty/folder1/a &&
git -C unmerged sparse-checkout disable 2>err &&
- test_i18ngrep "warning.*The following paths are unmerged" err &&
+ test_grep "warning.*The following paths are unmerged" err &&
git -C unmerged reset --hard &&
- git -C unmerged sparse-checkout init &&
+ git -C unmerged sparse-checkout init --no-cone &&
git -C unmerged sparse-checkout set /folder2/* /deep/deeper1/* &&
git -C unmerged sparse-checkout disable
'
-test_expect_success 'sparse-checkout reapply' '
+test_expect_failure 'sparse-checkout reapply' '
git clone repo tweak &&
echo dirty >tweak/deep/deeper2/a &&
@@ -418,26 +480,28 @@ test_expect_success 'sparse-checkout reapply' '
git -C tweak update-index --index-info <input &&
git -C tweak sparse-checkout init --cone 2>err &&
- test_i18ngrep "warning.*The following paths are not up to date" err &&
- test_i18ngrep "warning.*The following paths are unmerged" err &&
+ test_grep "warning.*The following paths are not up to date" err &&
+ test_grep "warning.*The following paths are unmerged" err &&
git -C tweak sparse-checkout set folder2 deep/deeper1 2>err &&
- test_i18ngrep "warning.*The following paths are not up to date" err &&
- test_i18ngrep "warning.*The following paths are unmerged" err &&
+ test_grep "warning.*The following paths are not up to date" err &&
+ test_grep "warning.*The following paths are unmerged" err &&
git -C tweak sparse-checkout reapply 2>err &&
- test_i18ngrep "warning.*The following paths are not up to date" err &&
+ test_grep "warning.*The following paths are not up to date" err &&
test_path_is_file tweak/deep/deeper2/a &&
- test_i18ngrep "warning.*The following paths are unmerged" err &&
+ test_grep "warning.*The following paths are unmerged" err &&
test_path_is_file tweak/folder1/a &&
git -C tweak checkout HEAD deep/deeper2/a &&
git -C tweak sparse-checkout reapply 2>err &&
- test_i18ngrep ! "warning.*The following paths are not up to date" err &&
+ test_grep ! "warning.*The following paths are not up to date" err &&
test_path_is_missing tweak/deep/deeper2/a &&
- test_i18ngrep "warning.*The following paths are unmerged" err &&
+ test_grep "warning.*The following paths are unmerged" err &&
test_path_is_file tweak/folder1/a &&
+ # NEEDSWORK: We are asking to update a file outside of the
+ # sparse-checkout cone, but this is no longer allowed.
git -C tweak add folder1/a &&
git -C tweak sparse-checkout reapply 2>err &&
test_must_be_empty err &&
@@ -447,6 +511,37 @@ test_expect_success 'sparse-checkout reapply' '
git -C tweak sparse-checkout disable
'
+test_expect_success 'reapply can handle config options' '
+ git -C repo sparse-checkout init --cone --no-sparse-index &&
+ git -C repo config --worktree --list >actual &&
+ cat >expect <<-\EOF &&
+ core.sparsecheckout=true
+ core.sparsecheckoutcone=true
+ index.sparse=false
+ EOF
+ test_cmp expect actual &&
+
+ git -C repo sparse-checkout reapply --no-cone --no-sparse-index &&
+ git -C repo config --worktree --list >actual &&
+ cat >expect <<-\EOF &&
+ core.sparsecheckout=true
+ core.sparsecheckoutcone=false
+ index.sparse=false
+ EOF
+ test_cmp expect actual &&
+
+ git -C repo sparse-checkout reapply --cone --sparse-index &&
+ git -C repo config --worktree --list >actual &&
+ cat >expect <<-\EOF &&
+ core.sparsecheckout=true
+ core.sparsecheckoutcone=true
+ index.sparse=true
+ EOF
+ test_cmp expect actual &&
+
+ git -C repo sparse-checkout disable
+'
+
test_expect_success 'cone mode: set with core.ignoreCase=true' '
rm repo/.git/info/sparse-checkout &&
git -C repo sparse-checkout init --cone &&
@@ -460,33 +555,45 @@ test_expect_success 'cone mode: set with core.ignoreCase=true' '
check_files repo a folder1
'
-test_expect_success 'interaction with submodules' '
+test_expect_success 'setup submodules' '
git clone repo super &&
(
cd super &&
mkdir modules &&
- git submodule add ../repo modules/child &&
+ git -c protocol.file.allow=always \
+ submodule add ../repo modules/child &&
git add . &&
git commit -m "add submodule" &&
git sparse-checkout init --cone &&
git sparse-checkout set folder1
- ) &&
+ )
+'
+
+test_expect_success 'interaction with submodules' '
check_files super a folder1 modules &&
check_files super/modules/child a deep folder1 folder2
'
+test_expect_success 'check-rules interaction with submodules' '
+ git -C super ls-tree --name-only -r HEAD >all-files &&
+ git -C super sparse-checkout check-rules >check-rules-matches <all-files &&
+
+ test_grep ! "modules/" check-rules-matches &&
+ test_grep "folder1/" check-rules-matches
+'
+
test_expect_success 'different sparse-checkouts with worktrees' '
+ git -C repo sparse-checkout set --cone deep folder1 &&
git -C repo worktree add --detach ../worktree &&
- check_files worktree "a deep folder1 folder2" &&
- git -C worktree sparse-checkout init --cone &&
- git -C repo sparse-checkout set folder1 &&
- git -C worktree sparse-checkout set deep/deeper1 &&
- check_files repo a folder1 &&
- check_files worktree a deep
+ check_files worktree "a deep folder1" &&
+ git -C repo sparse-checkout set --cone folder1 &&
+ git -C worktree sparse-checkout set --cone deep/deeper1 &&
+ check_files repo "a folder1" &&
+ check_files worktree "a deep"
'
test_expect_success 'set using filename keeps file on-disk' '
- git -C repo sparse-checkout set a deep &&
+ git -C repo sparse-checkout set --skip-checks a deep &&
cat >expect <<-\EOF &&
/*
!/*/
@@ -509,7 +616,7 @@ check_read_tree_errors () {
then
test_must_be_empty err
else
- test_i18ngrep "$ERRORS" err
+ test_grep "$ERRORS" err
fi &&
check_files $REPO $FILES
}
@@ -571,6 +678,15 @@ test_expect_success 'pattern-checks: starting "*"' '
check_read_tree_errors repo "a deep" "disabling cone pattern matching"
'
+test_expect_success 'pattern-checks: non directory pattern' '
+ cat >repo/.git/info/sparse-checkout <<-\EOF &&
+ /deep/deeper1/a
+ EOF
+ check_read_tree_errors repo deep "disabling cone pattern matching" &&
+ check_files repo/deep deeper1 &&
+ check_files repo/deep/deeper1 a
+'
+
test_expect_success 'pattern-checks: contained glob characters' '
for c in "[a]" "\\" "?" "*"
do
@@ -579,7 +695,7 @@ test_expect_success 'pattern-checks: contained glob characters' '
!/*/
something$c-else/
EOF
- check_read_tree_errors repo "a" "disabling cone pattern matching"
+ check_read_tree_errors repo "a" "disabling cone pattern matching" || return 1
done
'
@@ -597,7 +713,7 @@ test_expect_success BSLASHPSPEC 'pattern-checks: escaped characters' '
git -C escaped reset --hard $COMMIT &&
check_files escaped "a deep folder1 folder2 zbad\\dir zdoes*exist" zglob[!a]? &&
git -C escaped sparse-checkout init --cone &&
- git -C escaped sparse-checkout set zbad\\dir/bogus "zdoes*not*exist" "zdoes*exist" "zglob[!a]?" &&
+ git -C escaped sparse-checkout set --skip-checks zbad\\dir/bogus "zdoes*not*exist" "zdoes*exist" "zglob[!a]?" &&
cat >expect <<-\EOF &&
/*
!/*/
@@ -642,4 +758,297 @@ test_expect_success MINGW 'cone mode replaces backslashes with slashes' '
check_files repo/deep a deeper1
'
+test_expect_success 'cone mode clears ignored subdirectories' '
+ rm repo/.git/info/sparse-checkout &&
+
+ git -C repo sparse-checkout init --cone &&
+ git -C repo sparse-checkout set deep/deeper1 &&
+
+ cat >repo/.gitignore <<-\EOF &&
+ obj/
+ *.o
+ EOF
+
+ git -C repo add .gitignore &&
+ git -C repo commit -m ".gitignore" &&
+
+ mkdir -p repo/obj repo/folder1/obj repo/deep/deeper2/obj &&
+ for file in folder1/obj/a obj/a folder1/file.o folder1.o \
+ deep/deeper2/obj/a deep/deeper2/file.o file.o
+ do
+ echo ignored >repo/$file || return 1
+ done &&
+
+ git -C repo status --porcelain=v2 >out &&
+ test_must_be_empty out &&
+
+ git -C repo sparse-checkout reapply &&
+ test_path_is_missing repo/folder1 &&
+ test_path_is_missing repo/deep/deeper2 &&
+ test_path_is_dir repo/obj &&
+ test_path_is_file repo/file.o &&
+
+ git -C repo status --porcelain=v2 >out &&
+ test_must_be_empty out &&
+
+ git -C repo sparse-checkout set deep/deeper2 &&
+ test_path_is_missing repo/deep/deeper1 &&
+ test_path_is_dir repo/deep/deeper2 &&
+ test_path_is_dir repo/obj &&
+ test_path_is_file repo/file.o &&
+
+ >repo/deep/deeper2/ignored.o &&
+ >repo/deep/deeper2/untracked &&
+
+ # When an untracked file is in the way, all untracked files
+ # (even ignored files) are preserved.
+ git -C repo sparse-checkout set folder1 2>err &&
+ grep "contains untracked files" err &&
+ test_path_is_file repo/deep/deeper2/ignored.o &&
+ test_path_is_file repo/deep/deeper2/untracked &&
+
+ # The rest of the cone matches expectation
+ test_path_is_missing repo/deep/deeper1 &&
+ test_path_is_dir repo/obj &&
+ test_path_is_file repo/file.o &&
+
+ git -C repo status --porcelain=v2 >out &&
+ echo "? deep/deeper2/untracked" >expect &&
+ test_cmp expect out
+'
+
+test_expect_success 'malformed cone-mode patterns' '
+ git -C repo sparse-checkout init --cone &&
+ mkdir -p repo/foo/bar &&
+ touch repo/foo/bar/x repo/foo/y &&
+ cat >repo/.git/info/sparse-checkout <<-\EOF &&
+ /*
+ !/*/
+ /foo/
+ !/foo/*/
+ /foo/\*/
+ EOF
+
+ # Listing the patterns will notice the duplicate pattern and
+ # emit a warning. It will list the patterns directly instead
+ # of using the cone-mode translation to a set of directories.
+ git -C repo sparse-checkout list >actual 2>err &&
+ test_cmp repo/.git/info/sparse-checkout actual &&
+ grep "warning: your sparse-checkout file may have issues: pattern .* is repeated" err &&
+ grep "warning: disabling cone pattern matching" err
+'
+
+test_expect_success 'set from subdir pays attention to prefix' '
+ git -C repo sparse-checkout disable &&
+ git -C repo/deep sparse-checkout set --cone deeper2 ../folder1 &&
+
+ git -C repo sparse-checkout list >actual &&
+
+ cat >expect <<-\EOF &&
+ deep/deeper2
+ folder1
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'add from subdir pays attention to prefix' '
+ git -C repo sparse-checkout set --cone deep/deeper2 &&
+ git -C repo/deep sparse-checkout add deeper1/deepest ../folder1 &&
+
+ git -C repo sparse-checkout list >actual &&
+
+ cat >expect <<-\EOF &&
+ deep/deeper1/deepest
+ deep/deeper2
+ folder1
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'set from subdir in non-cone mode throws an error' '
+ git -C repo sparse-checkout disable &&
+ test_must_fail git -C repo/deep sparse-checkout set --no-cone deeper2 ../folder1 2>error &&
+
+ grep "run from the toplevel directory in non-cone mode" error
+'
+
+test_expect_success 'set from subdir in non-cone mode throws an error' '
+ git -C repo sparse-checkout set --no-cone deep/deeper2 &&
+ test_must_fail git -C repo/deep sparse-checkout add deeper1/deepest ../folder1 2>error &&
+
+ grep "run from the toplevel directory in non-cone mode" error
+'
+
+test_expect_success 'by default, cone mode will error out when passed files' '
+ git -C repo sparse-checkout reapply --cone &&
+ test_must_fail git -C repo sparse-checkout add .gitignore 2>error &&
+
+ grep ".gitignore.*is not a directory" error
+'
+
+test_expect_success 'error on mistyped command line options' '
+ test_must_fail git -C repo sparse-checkout add --sikp-checks .gitignore 2>error &&
+
+ grep "unknown option.*sikp-checks" error
+'
+
+test_expect_success 'by default, non-cone mode will warn on individual files' '
+ git -C repo sparse-checkout reapply --no-cone &&
+ git -C repo sparse-checkout add .gitignore 2>warning &&
+
+ grep "pass a leading slash before paths.*if you want a single file" warning
+'
+
+test_expect_success 'setup bare repo' '
+ git clone --bare "file://$(pwd)/repo" bare
+'
+test_expect_success 'list fails outside work tree' '
+ test_must_fail git -C bare sparse-checkout list 2>err &&
+ test_grep "this operation must be run in a work tree" err
+'
+
+test_expect_success 'add fails outside work tree' '
+ test_must_fail git -C bare sparse-checkout add deeper 2>err &&
+ test_grep "this operation must be run in a work tree" err
+'
+
+test_expect_success 'set fails outside work tree' '
+ test_must_fail git -C bare sparse-checkout set deeper 2>err &&
+ test_grep "this operation must be run in a work tree" err
+'
+
+test_expect_success 'init fails outside work tree' '
+ test_must_fail git -C bare sparse-checkout init 2>err &&
+ test_grep "this operation must be run in a work tree" err
+'
+
+test_expect_success 'reapply fails outside work tree' '
+ test_must_fail git -C bare sparse-checkout reapply 2>err &&
+ test_grep "this operation must be run in a work tree" err
+'
+
+test_expect_success 'disable fails outside work tree' '
+ test_must_fail git -C bare sparse-checkout disable 2>err &&
+ test_grep "this operation must be run in a work tree" err
+'
+
+test_expect_success 'setup clean' '
+ git -C repo clean -fdx
+'
+
+test_expect_success 'check-rules cone mode' '
+ cat >rules <<-\EOF &&
+ folder1
+ deep/deeper1/deepest
+ EOF
+
+ git -C bare ls-tree -r --name-only HEAD >all-files &&
+ git -C bare sparse-checkout check-rules --cone \
+ --rules-file ../rules >check-rules-file <all-files &&
+
+ git -C repo sparse-checkout set --cone --stdin <rules&&
+ git -C repo ls-files -t >out &&
+ sed -n "/^S /!s/^. //p" out >ls-files &&
+
+ git -C repo sparse-checkout check-rules >check-rules-default <all-files &&
+
+ test_grep "deep/deeper1/deepest/a" check-rules-file &&
+ test_grep ! "deep/deeper2" check-rules-file &&
+
+ test_cmp check-rules-file ls-files &&
+ test_cmp check-rules-file check-rules-default
+'
+
+test_expect_success 'check-rules non-cone mode' '
+ cat >rules <<-\EOF &&
+ deep/deeper1/deepest/a
+ EOF
+
+ git -C bare ls-tree -r --name-only HEAD >all-files &&
+ git -C bare sparse-checkout check-rules --no-cone --rules-file ../rules\
+ >check-rules-file <all-files &&
+
+ git -C repo sparse-checkout set --no-cone --stdin <rules &&
+ git -C repo ls-files -t >out &&
+ sed -n "/^S /!s/^. //p" out >ls-files &&
+
+ git -C repo sparse-checkout check-rules >check-rules-default <all-files &&
+
+ cat >expect <<-\EOF &&
+ deep/deeper1/deepest/a
+ EOF
+
+ test_cmp expect check-rules-file &&
+ test_cmp check-rules-file ls-files &&
+ test_cmp check-rules-file check-rules-default
+'
+
+test_expect_success 'check-rules cone mode is default' '
+ cat >rules <<-\EOF &&
+ folder1
+ EOF
+
+ cat >all-files <<-\EOF &&
+ toplevel
+ folder2/file
+ folder1/file
+ EOF
+
+ cat >expect <<-\EOF &&
+ toplevel
+ folder1/file
+ EOF
+
+ git -C repo sparse-checkout set --no-cone &&
+ git -C repo sparse-checkout check-rules \
+ --rules-file ../rules >actual <all-files &&
+
+ git -C bare sparse-checkout check-rules \
+ --rules-file ../rules >actual-bare <all-files &&
+
+ test_cmp expect actual &&
+ test_cmp expect actual-bare
+'
+
+test_expect_success 'check-rules quoting' '
+ cat >rules <<-EOF &&
+ "folder\" a"
+ EOF
+ cat >files <<-EOF &&
+ "folder\" a/file"
+ "folder\" b/file"
+ EOF
+ cat >expect <<-EOF &&
+ "folder\" a/file"
+ EOF
+ git sparse-checkout check-rules --cone \
+ --rules-file rules >actual <files &&
+
+ test_cmp expect actual
+'
+
+test_expect_success 'check-rules null termination' '
+ cat >rules <<-EOF &&
+ "folder\" a"
+ EOF
+
+ lf_to_nul >files <<-EOF &&
+ folder" a/a
+ folder" a/b
+ folder" b/fileQ
+ EOF
+
+ cat >expect <<-EOF &&
+ folder" a/aQfolder" a/bQ
+ EOF
+
+ git sparse-checkout check-rules --cone -z \
+ --rules-file rules >actual.nul <files &&
+ nul_to_q <actual.nul >actual &&
+ echo >>actual &&
+
+ test_cmp expect actual
+'
+
+
test_done
diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh
index ddc86bb..2f1ae5f 100755
--- a/t/t1092-sparse-checkout-compatibility.sh
+++ b/t/t1092-sparse-checkout-compatibility.sh
@@ -16,9 +16,13 @@ test_expect_success 'setup' '
echo "after deep" >e &&
echo "after folder1" >g &&
echo "after x" >z &&
- mkdir folder1 folder2 deep x &&
+ mkdir folder1 folder2 deep before x &&
+ echo "before deep" >before/a &&
+ echo "before deep again" >before/b &&
mkdir deep/deeper1 deep/deeper2 deep/before deep/later &&
mkdir deep/deeper1/deepest &&
+ mkdir deep/deeper1/deepest2 &&
+ mkdir deep/deeper1/deepest3 &&
echo "after deeper1" >deep/e &&
echo "after deepest" >deep/deeper1/e &&
cp a folder1 &&
@@ -30,7 +34,9 @@ test_expect_success 'setup' '
cp a deep/deeper2 &&
cp a deep/later &&
cp a deep/deeper1/deepest &&
- cp -r deep/deeper1/deepest deep/deeper2 &&
+ cp a deep/deeper1/deepest2 &&
+ cp a deep/deeper1/deepest3 &&
+ cp -r deep/deeper1/ deep/deeper2 &&
mkdir deep/deeper1/0 &&
mkdir deep/deeper1/0/0 &&
touch deep/deeper1/0/1 &&
@@ -47,7 +53,7 @@ test_expect_success 'setup' '
git checkout -b base &&
for dir in folder1 folder2 deep
do
- git checkout -b update-$dir &&
+ git checkout -b update-$dir base &&
echo "updated $dir" >$dir/a &&
git commit -a -m "update $dir" || return 1
done &&
@@ -126,6 +132,8 @@ test_expect_success 'setup' '
git checkout -b deepest base &&
echo "updated deepest" >deep/deeper1/deepest/a &&
+ echo "updated deepest2" >deep/deeper1/deepest2/a &&
+ echo "updated deepest3" >deep/deeper1/deepest3/a &&
git commit -a -m "update deepest" &&
git checkout -f base &&
@@ -154,6 +162,19 @@ init_repos () {
git -C sparse-index sparse-checkout set deep
}
+init_repos_as_submodules () {
+ git reset --hard &&
+ init_repos &&
+ git submodule add ./full-checkout &&
+ git submodule add ./sparse-checkout &&
+ git submodule add ./sparse-index &&
+
+ git submodule status >actual &&
+ grep full-checkout actual &&
+ grep sparse-checkout actual &&
+ grep sparse-index actual
+}
+
run_on_sparse () {
(
cd sparse-checkout &&
@@ -187,48 +208,106 @@ test_sparse_match () {
test_cmp sparse-checkout-err sparse-index-err
}
-test_expect_success 'sparse-index contents' '
- init_repos &&
-
- test-tool -C sparse-index read-cache --table >cache &&
- for dir in folder1 folder2 x
+test_sparse_unstaged () {
+ file=$1 &&
+ for repo in sparse-checkout sparse-index
do
- TREE=$(git -C sparse-index rev-parse HEAD:$dir) &&
- grep "040000 tree $TREE $dir/" cache \
- || return 1
- done &&
-
- git -C sparse-index sparse-checkout set folder1 &&
+ # Skip "unmerged" paths
+ git -C $repo diff --staged --diff-filter=u -- "$file" >diff &&
+ test_must_be_empty diff || return 1
+ done
+}
- test-tool -C sparse-index read-cache --table >cache &&
- for dir in deep folder2 x
+# Usage: test_sprase_checkout_set "<c1> ... <cN>" "<s1> ... <sM>"
+# Verifies that "git sparse-checkout set <c1> ... <cN>" succeeds and
+# leaves the sparse index in a state where <s1> ... <sM> are sparse
+# directories (and <c1> ... <cN> are not).
+test_sparse_checkout_set () {
+ CONE_DIRS=$1 &&
+ SPARSE_DIRS=$2 &&
+ git -C sparse-index sparse-checkout set --skip-checks $CONE_DIRS &&
+ git -C sparse-index ls-files --sparse --stage >cache &&
+
+ # Check that the directories outside of the sparse-checkout cone
+ # have sparse directory entries.
+ for dir in $SPARSE_DIRS
do
TREE=$(git -C sparse-index rev-parse HEAD:$dir) &&
- grep "040000 tree $TREE $dir/" cache \
+ grep "040000 $TREE 0 $dir/" cache \
|| return 1
done &&
- git -C sparse-index sparse-checkout set deep/deeper1 &&
-
- test-tool -C sparse-index read-cache --table >cache &&
- for dir in deep/deeper2 folder1 folder2 x
+ # Check that the directories in the sparse-checkout cone
+ # are not sparse directory entries.
+ for dir in $CONE_DIRS
do
- TREE=$(git -C sparse-index rev-parse HEAD:$dir) &&
- grep "040000 tree $TREE $dir/" cache \
+ # Allow TREE to not exist because
+ # $dir does not exist at HEAD.
+ TREE=$(git -C sparse-index rev-parse HEAD:$dir) ||
+ ! grep "040000 $TREE 0 $dir/" cache \
|| return 1
- done &&
+ done
+}
- # Disabling the sparse-index removes tree entries with full ones
- git -C sparse-index sparse-checkout init --no-sparse-index &&
+test_expect_success 'sparse-index contents' '
+ init_repos &&
- test-tool -C sparse-index read-cache --table >cache &&
- ! grep "040000 tree" cache &&
- test_sparse_match test-tool read-cache --table
+ # Remove deep, add three other directories.
+ test_sparse_checkout_set \
+ "folder1 folder2 x" \
+ "before deep" &&
+
+ # Remove folder1, add deep
+ test_sparse_checkout_set \
+ "deep folder2 x" \
+ "before folder1" &&
+
+ # Replace deep with deep/deeper2 (dropping deep/deeper1)
+ # Add folder1
+ test_sparse_checkout_set \
+ "deep/deeper2 folder1 folder2 x" \
+ "before deep/deeper1" &&
+
+ # Replace deep/deeper2 with deep/deeper1
+ # Replace folder1 with folder1/0/0
+ # Replace folder2 with non-existent folder2/2/3
+ # Add non-existent "bogus"
+ test_sparse_checkout_set \
+ "bogus deep/deeper1 folder1/0/0 folder2/2/3 x" \
+ "before deep/deeper2 folder2/0" &&
+
+ # Drop down to only files at root
+ test_sparse_checkout_set \
+ "" \
+ "before deep folder1 folder2 x" &&
+
+ # Disabling the sparse-index replaces tree entries with full ones
+ git -C sparse-index sparse-checkout init --no-sparse-index &&
+ test_sparse_match git ls-files --stage --sparse
'
test_expect_success 'expanded in-memory index matches full index' '
init_repos &&
- test_sparse_match test-tool read-cache --expand --table
+ test_sparse_match git ls-files --stage
+'
+
+test_expect_success 'root directory cannot be sparse' '
+ init_repos &&
+
+ # Remove all in-cone files and directories from the index, collapse index
+ # with `git sparse-checkout reapply`
+ git -C sparse-index rm -r . &&
+ git -C sparse-index sparse-checkout reapply &&
+
+ # Verify sparse directories still present, root directory is not sparse
+ cat >expect <<-EOF &&
+ before/
+ folder1/
+ folder2/
+ x/
+ EOF
+ git -C sparse-index ls-files --sparse >actual &&
+ test_cmp expect actual
'
test_expect_success 'status with options' '
@@ -247,12 +326,19 @@ test_expect_success 'status with options' '
test_all_match git status --porcelain=v2 -uno
'
+test_expect_success 'status with diff in unexpanded sparse directory' '
+ init_repos &&
+ test_all_match git checkout rename-base &&
+ test_all_match git reset --soft rename-out-to-out &&
+ test_all_match git status --porcelain=v2
+'
+
test_expect_success 'status reports sparse-checkout' '
init_repos &&
git -C sparse-checkout status >full &&
git -C sparse-index status >sparse &&
- test_i18ngrep "You are in a sparse checkout with " full &&
- test_i18ngrep "You are in a sparse checkout." sparse
+ test_grep "You are in a sparse checkout with " full &&
+ test_grep "You are in a sparse checkout." sparse
'
test_expect_success 'add, commit, checkout' '
@@ -291,6 +377,45 @@ test_expect_success 'add, commit, checkout' '
test_all_match git checkout -
'
+test_expect_success 'deep changes during checkout' '
+ init_repos &&
+
+ test_sparse_match git sparse-checkout set deep/deeper1/deepest &&
+ test_all_match git checkout deepest &&
+ test_all_match git checkout base
+'
+
+test_expect_success 'checkout with modified sparse directory' '
+ init_repos &&
+
+ test_all_match git checkout rename-in-to-out -- . &&
+ test_sparse_match git sparse-checkout reapply &&
+ test_all_match git checkout base
+'
+
+test_expect_success 'checkout orphan then non-orphan' '
+ init_repos &&
+
+ test_all_match git checkout --orphan test-orphan &&
+ test_all_match git status --porcelain=v2 &&
+ test_all_match git checkout base &&
+ test_all_match git status --porcelain=v2
+'
+
+test_expect_success 'add outside sparse cone' '
+ init_repos &&
+
+ run_on_sparse mkdir folder1 &&
+ run_on_sparse ../edit-contents folder1/a &&
+ run_on_sparse ../edit-contents folder1/newfile &&
+ test_sparse_match test_must_fail git add folder1/a &&
+ grep "Disable or modify the sparsity rules" sparse-checkout-err &&
+ test_sparse_unstaged folder1/a &&
+ test_sparse_match test_must_fail git add folder1/newfile &&
+ grep "Disable or modify the sparsity rules" sparse-checkout-err &&
+ test_sparse_unstaged folder1/newfile
+'
+
test_expect_success 'commit including unstaged changes' '
init_repos &&
@@ -332,25 +457,31 @@ test_expect_success 'status/add: outside sparse cone' '
write_script edit-contents <<-\EOF &&
echo text >>$1
EOF
- run_on_sparse ../edit-contents folder1/a &&
+ run_on_all ../edit-contents folder1/a &&
run_on_all ../edit-contents folder1/new &&
test_sparse_match git status --porcelain=v2 &&
# Adding the path outside of the sparse-checkout cone should fail.
test_sparse_match test_must_fail git add folder1/a &&
- test_sparse_match test_must_fail git add --refresh folder1/a &&
-
- # NEEDSWORK: Adding a newly-tracked file outside the cone succeeds
- test_sparse_match git add folder1/new &&
-
- test_all_match git add . &&
+ grep "Disable or modify the sparsity rules" sparse-checkout-err &&
+ test_sparse_unstaged folder1/a &&
+ test_all_match git add --refresh folder1/a &&
+ test_must_be_empty sparse-checkout-err &&
+ test_sparse_unstaged folder1/a &&
+ test_sparse_match test_must_fail git add folder1/new &&
+ grep "Disable or modify the sparsity rules" sparse-checkout-err &&
+ test_sparse_unstaged folder1/new &&
+ test_sparse_match git add --sparse folder1/a &&
+ test_sparse_match git add --sparse folder1/new &&
+
+ test_all_match git add --sparse . &&
test_all_match git status --porcelain=v2 &&
test_all_match git commit -m folder1/new &&
test_all_match git rev-parse HEAD^{tree} &&
run_on_all ../edit-contents folder1/newer &&
- test_all_match git add folder1/ &&
+ test_all_match git add --sparse folder1/ &&
test_all_match git status --porcelain=v2 &&
test_all_match git commit -m folder1/newer &&
test_all_match git rev-parse HEAD^{tree}
@@ -371,7 +502,7 @@ test_expect_success 'checkout and reset --hard' '
test_all_match git reset --hard update-folder2
'
-test_expect_success 'diff --staged' '
+test_expect_success 'diff --cached' '
init_repos &&
write_script edit-contents <<-\EOF &&
@@ -380,10 +511,10 @@ test_expect_success 'diff --staged' '
run_on_all ../edit-contents &&
test_all_match git diff &&
- test_all_match git diff --staged &&
+ test_all_match git diff --cached &&
test_all_match git add README.md &&
test_all_match git diff &&
- test_all_match git diff --staged
+ test_all_match git diff --cached
'
# NEEDSWORK: sparse-checkout behaves differently from full-checkout when
@@ -400,8 +531,8 @@ test_expect_success 'diff with renames and conflicts' '
test_all_match git checkout rename-base &&
test_all_match git checkout $branch -- . &&
test_all_match git status --porcelain=v2 &&
- test_all_match git diff --staged --no-renames &&
- test_all_match git diff --staged --find-renames || return 1
+ test_all_match git diff --cached --no-renames &&
+ test_all_match git diff --cached --find-renames || return 1
done
'
@@ -420,8 +551,8 @@ test_expect_success 'diff with directory/file conflicts' '
test_all_match git checkout $branch &&
test_all_match git checkout rename-base -- . &&
test_all_match git status --porcelain=v2 &&
- test_all_match git diff --staged --no-renames &&
- test_all_match git diff --staged --find-renames || return 1
+ test_all_match git diff --cached --no-renames &&
+ test_all_match git diff --cached --find-renames || return 1
done
'
@@ -442,60 +573,434 @@ test_expect_success 'log with pathspec outside sparse definition' '
test_expect_success 'blame with pathspec inside sparse definition' '
init_repos &&
- test_all_match git blame a &&
- test_all_match git blame deep/a &&
- test_all_match git blame deep/deeper1/a &&
- test_all_match git blame deep/deeper1/deepest/a
+ for file in a \
+ deep/a \
+ deep/deeper1/a \
+ deep/deeper1/deepest/a
+ do
+ test_all_match git blame $file || return 1
+ done
'
-# TODO: blame currently does not support blaming files outside of the
-# sparse definition. It complains that the file doesn't exist locally.
-test_expect_failure 'blame with pathspec outside sparse definition' '
+# Without a revision specified, blame will error if passed any file that
+# is not present in the working directory (even if the file is tracked).
+# Here we just verify that this is also true with sparse checkouts.
+test_expect_success 'blame with pathspec outside sparse definition' '
init_repos &&
+ test_sparse_match git sparse-checkout set &&
- test_all_match git blame folder1/a &&
- test_all_match git blame folder2/a &&
- test_all_match git blame deep/deeper2/a &&
- test_all_match git blame deep/deeper2/deepest/a
+ for file in \
+ deep/a \
+ deep/deeper1/a \
+ deep/deeper1/deepest/a
+ do
+ test_sparse_match test_must_fail git blame $file &&
+ cat >expect <<-EOF &&
+ fatal: Cannot lstat '"'"'$file'"'"': No such file or directory
+ EOF
+ # We compare sparse-checkout-err and sparse-index-err in
+ # `test_sparse_match`. Given we know they are the same, we
+ # only check the content of sparse-index-err here.
+ test_cmp expect sparse-index-err || return 1
+ done
'
-# NEEDSWORK: a sparse-checkout behaves differently from a full checkout
-# in this scenario, but it shouldn't.
-test_expect_failure 'checkout and reset (mixed)' '
+test_expect_success 'checkout and reset (mixed)' '
init_repos &&
test_all_match git checkout -b reset-test update-deep &&
test_all_match git reset deepest &&
- test_all_match git reset update-folder1 &&
- test_all_match git reset update-folder2
+
+ # Because skip-worktree is preserved, resetting to update-folder1
+ # will show worktree changes for folder1/a in full-checkout, but not
+ # in sparse-checkout or sparse-index.
+ git -C full-checkout reset update-folder1 >full-checkout-out &&
+ test_sparse_match git reset update-folder1 &&
+ grep "M folder1/a" full-checkout-out &&
+ ! grep "M folder1/a" sparse-checkout-out &&
+ run_on_sparse test_path_is_missing folder1
'
-# NEEDSWORK: a sparse-checkout behaves differently from a full checkout
-# in this scenario, but it shouldn't.
-test_expect_success 'checkout and reset (mixed) [sparse]' '
+test_expect_success 'checkout and reset (merge)' '
init_repos &&
- test_sparse_match git checkout -b reset-test update-deep &&
- test_sparse_match git reset deepest &&
- test_sparse_match git reset update-folder1 &&
- test_sparse_match git reset update-folder2
+ write_script edit-contents <<-\EOF &&
+ echo text >>$1
+ EOF
+
+ test_all_match git checkout -b reset-test update-deep &&
+ run_on_all ../edit-contents a &&
+ test_all_match git reset --merge deepest &&
+ test_all_match git status --porcelain=v2 &&
+
+ test_all_match git reset --hard update-deep &&
+ run_on_all ../edit-contents deep/a &&
+ test_all_match test_must_fail git reset --merge deepest
'
-test_expect_success 'merge' '
+test_expect_success 'checkout and reset (keep)' '
init_repos &&
- test_all_match git checkout -b merge update-deep &&
- test_all_match git merge -m "folder1" update-folder1 &&
- test_all_match git rev-parse HEAD^{tree} &&
- test_all_match git merge -m "folder2" update-folder2 &&
- test_all_match git rev-parse HEAD^{tree}
+ write_script edit-contents <<-\EOF &&
+ echo text >>$1
+ EOF
+
+ test_all_match git checkout -b reset-test update-deep &&
+ run_on_all ../edit-contents a &&
+ test_all_match git reset --keep deepest &&
+ test_all_match git status --porcelain=v2 &&
+
+ test_all_match git reset --hard update-deep &&
+ run_on_all ../edit-contents deep/a &&
+ test_all_match test_must_fail git reset --keep deepest
+'
+
+test_expect_success 'reset with pathspecs inside sparse definition' '
+ init_repos &&
+
+ write_script edit-contents <<-\EOF &&
+ echo text >>$1
+ EOF
+
+ test_all_match git checkout -b reset-test update-deep &&
+ run_on_all ../edit-contents deep/a &&
+
+ test_all_match git reset base -- deep/a &&
+ test_all_match git status --porcelain=v2 &&
+
+ test_all_match git reset base -- nonexistent-file &&
+ test_all_match git status --porcelain=v2 &&
+
+ test_all_match git reset deepest -- deep &&
+ test_all_match git status --porcelain=v2
+'
+
+# Although the working tree differs between full and sparse checkouts after
+# reset, the state of the index is the same.
+test_expect_success 'reset with pathspecs outside sparse definition' '
+ init_repos &&
+ test_all_match git checkout -b reset-test base &&
+
+ test_sparse_match git reset update-folder1 -- folder1 &&
+ git -C full-checkout reset update-folder1 -- folder1 &&
+ test_all_match git ls-files -s -- folder1 &&
+
+ test_sparse_match git reset update-folder2 -- folder2/a &&
+ git -C full-checkout reset update-folder2 -- folder2/a &&
+ test_all_match git ls-files -s -- folder2/a
+'
+
+test_expect_success 'reset with wildcard pathspec' '
+ init_repos &&
+
+ test_all_match git reset update-deep -- deep\* &&
+ test_all_match git ls-files -s -- deep &&
+
+ test_all_match git reset deepest -- deep\*\*\* &&
+ test_all_match git ls-files -s -- deep &&
+
+ # The following `git reset`s result in updating the index on files with
+ # `skip-worktree` enabled. To avoid failing due to discrepencies in reported
+ # "modified" files, `test_sparse_match` reset is performed separately from
+ # "full-checkout" reset, then the index contents of all repos are verified.
+
+ test_sparse_match git reset update-folder1 -- \*/a &&
+ git -C full-checkout reset update-folder1 -- \*/a &&
+ test_all_match git ls-files -s -- deep/a folder1/a &&
+
+ test_sparse_match git reset update-folder2 -- folder\* &&
+ git -C full-checkout reset update-folder2 -- folder\* &&
+ test_all_match git ls-files -s -- folder10 folder1 folder2 &&
+
+ test_sparse_match git reset base -- folder1/\* &&
+ git -C full-checkout reset base -- folder1/\* &&
+ test_all_match git ls-files -s -- folder1
+'
+
+test_expect_success 'reset hard with removed sparse dir' '
+ init_repos &&
+
+ run_on_all git rm -r --sparse folder1 &&
+ test_all_match git status --porcelain=v2 &&
+
+ test_all_match git reset --hard &&
+ test_all_match git status --porcelain=v2 &&
+
+ cat >expect <<-\EOF &&
+ folder1/
+ EOF
+
+ git -C sparse-index ls-files --sparse folder1 >out &&
+ test_cmp expect out
+'
+
+test_expect_success 'update-index modify outside sparse definition' '
+ init_repos &&
+
+ write_script edit-contents <<-\EOF &&
+ echo text >>$1
+ EOF
+
+ # Create & modify folder1/a
+ # Note that this setup is a manual way of reaching the erroneous
+ # condition in which a `skip-worktree` enabled, outside-of-cone file
+ # exists on disk. It is used here to ensure `update-index` is stable
+ # and behaves predictably if such a condition occurs.
+ run_on_sparse mkdir -p folder1 &&
+ run_on_sparse cp ../initial-repo/folder1/a folder1/a &&
+ run_on_all ../edit-contents folder1/a &&
+
+ # If file has skip-worktree enabled, but the file is present, it is
+ # treated the same as if skip-worktree is disabled
+ test_all_match git status --porcelain=v2 &&
+ test_all_match git update-index folder1/a &&
+ test_all_match git status --porcelain=v2 &&
+
+ # When skip-worktree is disabled (even on files outside sparse cone), file
+ # is updated in the index
+ test_sparse_match git update-index --no-skip-worktree folder1/a &&
+ test_all_match git status --porcelain=v2 &&
+ test_all_match git update-index folder1/a &&
+ test_all_match git status --porcelain=v2
+'
+
+test_expect_success 'update-index --add outside sparse definition' '
+ init_repos &&
+
+ write_script edit-contents <<-\EOF &&
+ echo text >>$1
+ EOF
+
+ # Create folder1, add new file
+ run_on_sparse mkdir -p folder1 &&
+ run_on_all ../edit-contents folder1/b &&
+
+ # The *untracked* out-of-cone file is added to the index because it does
+ # not have a `skip-worktree` bit to signal that it should be ignored
+ # (unlike in `git add`, which will fail due to the file being outside
+ # the sparse checkout definition).
+ test_all_match git update-index --add folder1/b &&
+ test_all_match git status --porcelain=v2
+'
+
+# NEEDSWORK: `--remove`, unlike the rest of `update-index`, does not ignore
+# `skip-worktree` entries by default and will remove them from the index.
+# The `--ignore-skip-worktree-entries` flag must be used in conjunction with
+# `--remove` to ignore the `skip-worktree` entries and prevent their removal
+# from the index.
+test_expect_success 'update-index --remove outside sparse definition' '
+ init_repos &&
+
+ # When --ignore-skip-worktree-entries is _not_ specified:
+ # out-of-cone, not-on-disk files are removed from the index
+ test_sparse_match git update-index --remove folder1/a &&
+ cat >expect <<-EOF &&
+ D folder1/a
+ EOF
+ test_sparse_match git diff --cached --name-status &&
+ test_cmp expect sparse-checkout-out &&
+
+ # Reset the state
+ test_all_match git reset --hard &&
+
+ # When --ignore-skip-worktree-entries is specified, out-of-cone
+ # (skip-worktree) files are ignored
+ test_sparse_match git update-index --remove --ignore-skip-worktree-entries folder1/a &&
+ test_sparse_match git diff --cached --name-status &&
+ test_must_be_empty sparse-checkout-out &&
+
+ # Reset the state
+ test_all_match git reset --hard &&
+
+ # --force-remove supercedes --ignore-skip-worktree-entries, removing
+ # a skip-worktree file from the index (and disk) when both are specified
+ # with --remove
+ test_sparse_match git update-index --force-remove --ignore-skip-worktree-entries folder1/a &&
+ cat >expect <<-EOF &&
+ D folder1/a
+ EOF
+ test_sparse_match git diff --cached --name-status &&
+ test_cmp expect sparse-checkout-out
+'
+
+test_expect_success 'update-index with directories' '
+ init_repos &&
+
+ # update-index will exit silently when provided with a directory name
+ # containing a trailing slash
+ test_all_match git update-index deep/ folder1/ &&
+ grep "Ignoring path deep/" sparse-checkout-err &&
+ grep "Ignoring path folder1/" sparse-checkout-err &&
+
+ # When update-index is given a directory name WITHOUT a trailing slash, it will
+ # behave in different ways depending on the status of the directory on disk:
+ # * if it exists, the command exits with an error ("add individual files instead")
+ # * if it does NOT exist (e.g., in a sparse-checkout), it is assumed to be a
+ # file and either triggers an error ("does not exist and --remove not passed")
+ # or is ignored completely (when using --remove)
+ test_all_match test_must_fail git update-index deep &&
+ run_on_all test_must_fail git update-index folder1 &&
+ test_must_fail git -C full-checkout update-index --remove folder1 &&
+ test_sparse_match git update-index --remove folder1 &&
+ test_all_match git status --porcelain=v2
+'
+
+test_expect_success 'update-index --again file outside sparse definition' '
+ init_repos &&
+
+ test_all_match git checkout -b test-reupdate &&
+
+ # Update HEAD without modifying the index to introduce a difference in
+ # folder1/a
+ test_sparse_match git reset --soft update-folder1 &&
+
+ # Because folder1/a differs in the index vs HEAD,
+ # `git update-index --no-skip-worktree --again` will effectively perform
+ # `git update-index --no-skip-worktree folder1/a` and remove the skip-worktree
+ # flag from folder1/a
+ test_sparse_match git update-index --no-skip-worktree --again &&
+ test_sparse_match git status --porcelain=v2 &&
+
+ cat >expect <<-EOF &&
+ D folder1/a
+ EOF
+ test_sparse_match git diff --name-status &&
+ test_cmp expect sparse-checkout-out
+'
+
+test_expect_success 'update-index --cacheinfo' '
+ init_repos &&
+
+ deep_a_oid=$(git -C full-checkout rev-parse update-deep:deep/a) &&
+ folder2_oid=$(git -C full-checkout rev-parse update-folder2:folder2) &&
+ folder1_a_oid=$(git -C full-checkout rev-parse update-folder1:folder1/a) &&
+
+ test_all_match git update-index --cacheinfo 100644 $deep_a_oid deep/a &&
+ test_all_match git status --porcelain=v2 &&
+
+ # Cannot add sparse directory, even in sparse index case
+ test_all_match test_must_fail git update-index --add --cacheinfo 040000 $folder2_oid folder2/ &&
+
+ # Sparse match only: the new outside-of-cone entry is added *without* skip-worktree,
+ # so `git status` reports it as "deleted" in the worktree
+ test_sparse_match git update-index --add --cacheinfo 100644 $folder1_a_oid folder1/a &&
+ test_sparse_match git status --porcelain=v2 &&
+ cat >expect <<-EOF &&
+ MD folder1/a
+ EOF
+ test_sparse_match git status --short -- folder1/a &&
+ test_cmp expect sparse-checkout-out &&
+
+ # To return folder1/a to "normal" for a sparse checkout (ignored &
+ # outside-of-cone), add the skip-worktree flag.
+ test_sparse_match git update-index --skip-worktree folder1/a &&
+ cat >expect <<-EOF &&
+ S folder1/a
+ EOF
+ test_sparse_match git ls-files -t -- folder1/a &&
+ test_cmp expect sparse-checkout-out
+'
+
+for MERGE_TREES in "base HEAD update-folder2" \
+ "update-folder1 update-folder2" \
+ "update-folder2"
+do
+ test_expect_success "'read-tree -mu $MERGE_TREES' with files outside sparse definition" '
+ init_repos &&
+
+ # Although the index matches, without --no-sparse-checkout, outside-of-
+ # definition files will not exist on disk for sparse checkouts
+ test_all_match git read-tree -mu $MERGE_TREES &&
+ test_all_match git status --porcelain=v2 &&
+ test_path_is_missing sparse-checkout/folder2 &&
+ test_path_is_missing sparse-index/folder2 &&
+
+ test_all_match git read-tree --reset -u HEAD &&
+ test_all_match git status --porcelain=v2 &&
+
+ test_all_match git read-tree -mu --no-sparse-checkout $MERGE_TREES &&
+ test_all_match git status --porcelain=v2 &&
+ test_cmp sparse-checkout/folder2/a sparse-index/folder2/a &&
+ test_cmp sparse-checkout/folder2/a full-checkout/folder2/a
+
+ '
+done
+
+test_expect_success 'read-tree --merge with edit/edit conflicts in sparse directories' '
+ init_repos &&
+
+ # Merge of multiple changes to same directory (but not same files) should
+ # succeed
+ test_all_match git read-tree -mu base rename-base update-folder1 &&
+ test_all_match git status --porcelain=v2 &&
+
+ test_all_match git reset --hard &&
+
+ test_all_match git read-tree -mu rename-base update-folder2 &&
+ test_all_match git status --porcelain=v2 &&
+
+ test_all_match git reset --hard &&
+
+ test_all_match test_must_fail git read-tree -mu base update-folder1 rename-out-to-in &&
+ test_all_match test_must_fail git read-tree -mu rename-out-to-in update-folder1
+'
+
+test_expect_success 'read-tree --prefix' '
+ init_repos &&
+
+ # If files differing between the index and target <commit-ish> exist
+ # inside the prefix, `read-tree --prefix` should fail
+ test_all_match test_must_fail git read-tree --prefix=deep/ deepest &&
+ test_all_match test_must_fail git read-tree --prefix=folder1/ update-folder1 &&
+
+ # If no differing index entries exist matching the prefix,
+ # `read-tree --prefix` updates the index successfully
+ test_all_match git rm -rf deep/deeper1/deepest/ &&
+ test_all_match git read-tree --prefix=deep/deeper1/deepest -u deepest &&
+ test_all_match git status --porcelain=v2 &&
+
+ run_on_all git rm -rf --sparse folder1/ &&
+ test_all_match git read-tree --prefix=folder1/ -u update-folder1 &&
+ test_all_match git status --porcelain=v2 &&
+
+ test_all_match git rm -rf --sparse folder2/0 &&
+ test_all_match git read-tree --prefix=folder2/0/ -u rename-out-to-out &&
+ test_all_match git status --porcelain=v2
+'
+
+test_expect_success 'read-tree --merge with directory-file conflicts' '
+ init_repos &&
+
+ test_all_match git checkout -b test-branch rename-base &&
+
+ # Although the index matches, without --no-sparse-checkout, outside-of-
+ # definition files will not exist on disk for sparse checkouts
+ test_sparse_match git read-tree -mu rename-out-to-out &&
+ test_sparse_match git status --porcelain=v2 &&
+ test_path_is_missing sparse-checkout/folder2 &&
+ test_path_is_missing sparse-index/folder2 &&
+
+ test_sparse_match git read-tree --reset -u HEAD &&
+ test_sparse_match git status --porcelain=v2 &&
+
+ test_sparse_match git read-tree -mu --no-sparse-checkout rename-out-to-out &&
+ test_sparse_match git status --porcelain=v2 &&
+ test_cmp sparse-checkout/folder2/0/1 sparse-index/folder2/0/1
+'
+
+test_expect_success 'merge, cherry-pick, and rebase' '
+ init_repos &&
+
+ for OPERATION in "merge -m merge" cherry-pick "rebase --apply" "rebase --merge"
+ do
+ test_all_match git checkout -B temp update-deep &&
+ test_all_match git $OPERATION update-folder1 &&
+ test_all_match git rev-parse HEAD^{tree} &&
+ test_all_match git $OPERATION update-folder2 &&
+ test_all_match git rev-parse HEAD^{tree} || return 1
+ done
'
-# NEEDSWORK: This test is documenting current behavior, but that
-# behavior can be confusing to users so there is desire to change it.
-# Right now, users might be using this flow to work through conflicts,
-# so any solution should present advice to users who try this sequence
-# of commands to follow whatever new method we create.
test_expect_success 'merge with conflict outside cone' '
init_repos &&
@@ -510,13 +1015,19 @@ test_expect_success 'merge with conflict outside cone' '
test_all_match git status --porcelain=v2 &&
# 2. Add the file with conflict markers
- test_all_match git add folder1/a &&
+ test_sparse_match test_must_fail git add folder1/a &&
+ grep "Disable or modify the sparsity rules" sparse-checkout-err &&
+ test_sparse_unstaged folder1/a &&
+ test_all_match git add --sparse folder1/a &&
test_all_match git status --porcelain=v2 &&
# 3. Rename the file to another sparse filename and
# accept conflict markers as resolved content.
run_on_all mv folder2/a folder2/z &&
- test_all_match git add folder2 &&
+ test_sparse_match test_must_fail git add folder2 &&
+ grep "Disable or modify the sparsity rules" sparse-checkout-err &&
+ test_sparse_unstaged folder2/z &&
+ test_all_match git add --sparse folder2 &&
test_all_match git status --porcelain=v2 &&
test_all_match git merge --continue &&
@@ -524,6 +1035,50 @@ test_expect_success 'merge with conflict outside cone' '
test_all_match git rev-parse HEAD^{tree}
'
+test_expect_success 'cherry-pick/rebase with conflict outside cone' '
+ init_repos &&
+
+ for OPERATION in cherry-pick rebase
+ do
+ test_all_match git checkout -B tip &&
+ test_all_match git reset --hard merge-left &&
+ test_all_match git status --porcelain=v2 &&
+ test_all_match test_must_fail git $OPERATION merge-right &&
+ test_all_match git status --porcelain=v2 &&
+
+ # Resolve the conflict in different ways:
+ # 1. Revert to the base
+ test_all_match git checkout base -- deep/deeper2/a &&
+ test_all_match git status --porcelain=v2 &&
+
+ # 2. Add the file with conflict markers
+ # NEEDSWORK: Even though the merge conflict removed the
+ # SKIP_WORKTREE bit from the index entry for folder1/a, we should
+ # warn that this is a problematic add.
+ test_sparse_match test_must_fail git add folder1/a &&
+ grep "Disable or modify the sparsity rules" sparse-checkout-err &&
+ test_sparse_unstaged folder1/a &&
+ test_all_match git add --sparse folder1/a &&
+ test_all_match git status --porcelain=v2 &&
+
+ # 3. Rename the file to another sparse filename and
+ # accept conflict markers as resolved content.
+ # NEEDSWORK: This mode now fails, because folder2/z is
+ # outside of the sparse-checkout cone and does not match an
+ # existing index entry with the SKIP_WORKTREE bit cleared.
+ run_on_all mv folder2/a folder2/z &&
+ test_sparse_match test_must_fail git add folder2 &&
+ grep "Disable or modify the sparsity rules" sparse-checkout-err &&
+ test_sparse_unstaged folder2/z &&
+ test_all_match git add --sparse folder2 &&
+ test_all_match git status --porcelain=v2 &&
+
+ test_all_match git $OPERATION --continue &&
+ test_all_match git status --porcelain=v2 &&
+ test_all_match git rev-parse HEAD^{tree} || return 1
+ done
+'
+
test_expect_success 'merge with outside renames' '
init_repos &&
@@ -558,6 +1113,123 @@ test_expect_success 'cherry-pick with conflicts' '
test_all_match test_must_fail git cherry-pick to-cherry-pick
'
+test_expect_success 'stash' '
+ init_repos &&
+
+ write_script edit-contents <<-\EOF &&
+ echo text >>$1
+ EOF
+
+ # Stash a sparse directory (folder1)
+ test_all_match git checkout -b test-branch rename-base &&
+ test_all_match git reset --soft rename-out-to-out &&
+ test_all_match git stash &&
+ test_all_match git status --porcelain=v2 &&
+
+ # Apply the sparse directory stash without reinstating the index
+ test_all_match git stash apply -q &&
+ test_all_match git status --porcelain=v2 &&
+
+ # Reset to state where stash can be applied
+ test_sparse_match git sparse-checkout reapply &&
+ test_all_match git reset --hard rename-out-to-out &&
+
+ # Apply the sparse directory stash *with* reinstating the index
+ test_all_match git stash apply --index -q &&
+ test_all_match git status --porcelain=v2 &&
+
+ # Reset to state where we will get a conflict applying the stash
+ test_sparse_match git sparse-checkout reapply &&
+ test_all_match git reset --hard update-folder1 &&
+
+ # Apply the sparse directory stash with conflicts
+ test_all_match test_must_fail git stash apply --index -q &&
+ test_all_match test_must_fail git stash apply -q &&
+ test_all_match git status --porcelain=v2 &&
+
+ # Reset to base branch
+ test_sparse_match git sparse-checkout reapply &&
+ test_all_match git reset --hard base &&
+
+ # Stash & unstash an untracked file outside of the sparse checkout
+ # definition.
+ run_on_sparse mkdir -p folder1 &&
+ run_on_all ../edit-contents folder1/new &&
+ test_all_match git stash -u &&
+ test_all_match git status --porcelain=v2 &&
+
+ test_all_match git stash pop -q &&
+ test_all_match git status --porcelain=v2
+'
+
+test_expect_success 'checkout-index inside sparse definition' '
+ init_repos &&
+
+ run_on_all rm -f deep/a &&
+ test_all_match git checkout-index -- deep/a &&
+ test_all_match git status --porcelain=v2 &&
+
+ echo test >>new-a &&
+ run_on_all cp ../new-a a &&
+ test_all_match test_must_fail git checkout-index -- a &&
+ test_all_match git checkout-index -f -- a &&
+ test_all_match git status --porcelain=v2
+'
+
+test_expect_success 'checkout-index outside sparse definition' '
+ init_repos &&
+
+ # Without --ignore-skip-worktree-bits, outside-of-cone files will trigger
+ # an error
+ test_sparse_match test_must_fail git checkout-index -- folder1/a &&
+ test_grep "folder1/a has skip-worktree enabled" sparse-checkout-err &&
+ test_path_is_missing folder1/a &&
+
+ # With --ignore-skip-worktree-bits, outside-of-cone files are checked out
+ test_sparse_match git checkout-index --ignore-skip-worktree-bits -- folder1/a &&
+ test_cmp sparse-checkout/folder1/a sparse-index/folder1/a &&
+ test_cmp sparse-checkout/folder1/a full-checkout/folder1/a &&
+
+ run_on_sparse rm -rf folder1 &&
+ echo test >new-a &&
+ run_on_sparse mkdir -p folder1 &&
+ run_on_all cp ../new-a folder1/a &&
+
+ test_all_match test_must_fail git checkout-index --ignore-skip-worktree-bits -- folder1/a &&
+ test_all_match git checkout-index -f --ignore-skip-worktree-bits -- folder1/a &&
+ test_cmp sparse-checkout/folder1/a sparse-index/folder1/a &&
+ test_cmp sparse-checkout/folder1/a full-checkout/folder1/a
+'
+
+test_expect_success 'checkout-index with folders' '
+ init_repos &&
+
+ # Inside checkout definition
+ test_all_match test_must_fail git checkout-index -f -- deep/ &&
+
+ # Outside checkout definition
+ # Note: although all tests fail (as expected), the messaging differs. For
+ # non-sparse index checkouts, the error is that the "file" does not appear
+ # in the index; for sparse checkouts, the error is explicitly that the
+ # entry is a sparse directory.
+ run_on_all test_must_fail git checkout-index -f -- folder1/ &&
+ test_cmp full-checkout-err sparse-checkout-err &&
+ ! test_cmp full-checkout-err sparse-index-err &&
+ grep "is a sparse directory" sparse-index-err
+'
+
+test_expect_success 'checkout-index --all' '
+ init_repos &&
+
+ test_all_match git checkout-index --all &&
+ test_sparse_match test_path_is_missing folder1 &&
+
+ # --ignore-skip-worktree-bits will cause `skip-worktree` files to be
+ # checked out, causing the outside-of-cone `folder1` to exist on-disk
+ test_all_match git checkout-index --ignore-skip-worktree-bits --all &&
+ test_all_match test_path_exists folder1
+'
+
test_expect_success 'clean' '
init_repos &&
@@ -567,58 +1239,174 @@ test_expect_success 'clean' '
test_all_match git commit -m "ignore bogus files" &&
run_on_sparse mkdir folder1 &&
+ run_on_all mkdir -p deep/untracked-deep &&
run_on_all touch folder1/bogus &&
+ run_on_all touch folder1/untracked &&
+ run_on_all touch deep/untracked-deep/bogus &&
+ run_on_all touch deep/untracked-deep/untracked &&
test_all_match git status --porcelain=v2 &&
test_all_match git clean -f &&
test_all_match git status --porcelain=v2 &&
test_sparse_match ls &&
test_sparse_match ls folder1 &&
+ run_on_all test_path_exists folder1/bogus &&
+ run_on_all test_path_is_missing folder1/untracked &&
+ run_on_all test_path_exists deep/untracked-deep/bogus &&
+ run_on_all test_path_exists deep/untracked-deep/untracked &&
+
+ test_all_match git clean -fd &&
+ test_all_match git status --porcelain=v2 &&
+ test_sparse_match ls &&
+ test_sparse_match ls folder1 &&
+ run_on_all test_path_exists folder1/bogus &&
+ run_on_all test_path_exists deep/untracked-deep/bogus &&
+ run_on_all test_path_is_missing deep/untracked-deep/untracked &&
test_all_match git clean -xf &&
test_all_match git status --porcelain=v2 &&
test_sparse_match ls &&
test_sparse_match ls folder1 &&
+ run_on_all test_path_is_missing folder1/bogus &&
+ run_on_all test_path_exists deep/untracked-deep/bogus &&
test_all_match git clean -xdf &&
test_all_match git status --porcelain=v2 &&
test_sparse_match ls &&
test_sparse_match ls folder1 &&
+ run_on_all test_path_is_missing deep/untracked-deep/bogus &&
test_sparse_match test_path_is_dir folder1
'
+for builtin in show rev-parse
+do
+ test_expect_success "$builtin (cached blobs/trees)" "
+ init_repos &&
+
+ test_all_match git $builtin :a &&
+ test_all_match git $builtin :deep/a &&
+ test_sparse_match git $builtin :folder1/a &&
+
+ # The error message differs depending on whether
+ # the directory exists in the worktree.
+ test_all_match test_must_fail git $builtin :deep/ &&
+ test_must_fail git -C full-checkout $builtin :folder1/ &&
+ test_sparse_match test_must_fail git $builtin :folder1/ &&
+
+ # Change the sparse cone for an extra case:
+ run_on_sparse git sparse-checkout set deep/deeper1 &&
+
+ # deep/deeper2 is a sparse directory in the sparse index.
+ test_sparse_match test_must_fail git $builtin :deep/deeper2/ &&
+
+ # deep/deeper2/deepest is not in the sparse index, but
+ # will trigger an index expansion.
+ test_sparse_match test_must_fail git $builtin :deep/deeper2/deepest/
+ "
+done
+
test_expect_success 'submodule handling' '
init_repos &&
+ test_sparse_match git sparse-checkout add modules &&
test_all_match mkdir modules &&
test_all_match touch modules/a &&
test_all_match git add modules &&
test_all_match git commit -m "add modules directory" &&
+ test_config_global protocol.file.allow always &&
+
run_on_all git submodule add "$(pwd)/initial-repo" modules/sub &&
test_all_match git commit -m "add submodule" &&
# having a submodule prevents "modules" from collapse
- test-tool -C sparse-index read-cache --table >cache &&
- grep "100644 blob .* modules/a" cache &&
- grep "160000 commit $(git -C initial-repo rev-parse HEAD) modules/sub" cache
+ test_sparse_match git sparse-checkout set deep/deeper1 &&
+ git -C sparse-index ls-files --sparse --stage >cache &&
+ grep "100644 .* modules/a" cache &&
+ grep "160000 $(git -C initial-repo rev-parse HEAD) 0 modules/sub" cache
'
+# When working with a sparse index, some commands will need to expand the
+# index to operate properly. If those commands also write the index back
+# to disk, they need to convert the index to sparse before writing.
+# This test verifies that both of these events are logged in trace2 logs.
test_expect_success 'sparse-index is expanded and converted back' '
init_repos &&
- GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
- git -C sparse-index -c core.fsmonitor="" reset --hard &&
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+ git -C sparse-index reset -- folder1/a &&
test_region index convert_to_sparse trace2.txt &&
+ test_region index ensure_full_index trace2.txt &&
+
+ # ls-files expands on read, but does not write.
+ rm trace2.txt &&
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
+ git -C sparse-index ls-files &&
test_region index ensure_full_index trace2.txt
'
-ensure_not_expanded () {
+test_expect_success 'index.sparse disabled inline uses full index' '
+ init_repos &&
+
+ # When index.sparse is disabled inline with `git status`, the
+ # index is expanded at the beginning of the execution then never
+ # converted back to sparse. It is then written to disk as a full index.
+ rm -f trace2.txt &&
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
+ git -C sparse-index -c index.sparse=false status &&
+ ! test_region index convert_to_sparse trace2.txt &&
+ test_region index ensure_full_index trace2.txt &&
+
+ # Since index.sparse is set to true at a repo level, the index
+ # is converted from full to sparse when read, then never expanded
+ # over the course of `git status`. It is written to disk as a sparse
+ # index.
+ rm -f trace2.txt &&
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
+ git -C sparse-index status &&
+ test_region index convert_to_sparse trace2.txt &&
+ ! test_region index ensure_full_index trace2.txt &&
+
+ # Now that the index has been written to disk as sparse, it is not
+ # converted to sparse (or expanded to full) when read by `git status`.
rm -f trace2.txt &&
- echo >>sparse-index/untracked.txt &&
GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
- git -C sparse-index "$@" &&
+ git -C sparse-index status &&
+ ! test_region index convert_to_sparse trace2.txt &&
+ ! test_region index ensure_full_index trace2.txt
+'
+
+run_sparse_index_trace2 () {
+ rm -f trace2.txt &&
+ if test -z "$WITHOUT_UNTRACKED_TXT"
+ then
+ echo >>sparse-index/untracked.txt
+ fi &&
+
+ if test "$1" = "!"
+ then
+ shift &&
+ test_must_fail env \
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+ git -C sparse-index "$@" \
+ >sparse-index-out \
+ 2>sparse-index-error || return 1
+ else
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+ git -C sparse-index "$@" \
+ >sparse-index-out \
+ 2>sparse-index-error || return 1
+ fi
+}
+
+ensure_expanded () {
+ run_sparse_index_trace2 "$@" &&
+ test_region index ensure_full_index trace2.txt
+}
+
+ensure_not_expanded () {
+ run_sparse_index_trace2 "$@" &&
test_region ! index ensure_full_index trace2.txt
}
@@ -626,6 +1414,7 @@ test_expect_success 'sparse-index is not expanded' '
init_repos &&
ensure_not_expanded status &&
+ ensure_not_expanded ls-files --sparse &&
ensure_not_expanded commit --allow-empty -m empty &&
echo >>sparse-index/a &&
ensure_not_expanded commit -a -m a &&
@@ -637,9 +1426,9 @@ test_expect_success 'sparse-index is not expanded' '
ensure_not_expanded checkout - &&
ensure_not_expanded switch rename-out-to-out &&
ensure_not_expanded switch - &&
- git -C sparse-index reset --hard &&
+ ensure_not_expanded reset --hard &&
ensure_not_expanded checkout rename-out-to-out -- deep/deeper1 &&
- git -C sparse-index reset --hard &&
+ ensure_not_expanded reset --hard &&
ensure_not_expanded restore -s rename-out-to-out -- deep/deeper1 &&
echo >>sparse-index/README.md &&
@@ -647,7 +1436,340 @@ test_expect_success 'sparse-index is not expanded' '
echo >>sparse-index/extra.txt &&
ensure_not_expanded add extra.txt &&
echo >>sparse-index/untracked.txt &&
- ensure_not_expanded add .
+ ensure_not_expanded add . &&
+
+ ensure_not_expanded checkout-index -f a &&
+ ensure_not_expanded checkout-index -f --all &&
+ for ref in update-deep update-folder1 update-folder2 update-deep
+ do
+ echo >>sparse-index/README.md &&
+ ensure_not_expanded reset --hard $ref || return 1
+ done &&
+
+ ensure_not_expanded reset --mixed base &&
+ ensure_not_expanded reset --hard update-deep &&
+ ensure_not_expanded reset --keep base &&
+ ensure_not_expanded reset --merge update-deep &&
+ ensure_not_expanded reset --hard &&
+
+ ensure_not_expanded reset base -- deep/a &&
+ ensure_not_expanded reset base -- nonexistent-file &&
+ ensure_not_expanded reset deepest -- deep &&
+
+ # Although folder1 is outside the sparse definition, it exists as a
+ # directory entry in the index, so the pathspec will not force the
+ # index to be expanded.
+ ensure_not_expanded reset deepest -- folder1 &&
+ ensure_not_expanded reset deepest -- folder1/ &&
+
+ # Wildcard identifies only in-cone files, no index expansion
+ ensure_not_expanded reset deepest -- deep/\* &&
+
+ # Wildcard identifies only full sparse directories, no index expansion
+ ensure_not_expanded reset deepest -- folder\* &&
+
+ ensure_not_expanded clean -fd &&
+
+ ensure_not_expanded checkout -f update-deep &&
+ test_config -C sparse-index pull.twohead ort &&
+ (
+ sane_unset GIT_TEST_MERGE_ALGORITHM &&
+ for OPERATION in "merge -m merge" cherry-pick rebase
+ do
+ ensure_not_expanded merge -m merge update-folder1 &&
+ ensure_not_expanded merge -m merge update-folder2 || return 1
+ done
+ )
+'
+
+test_expect_success 'sparse-index is not expanded: merge conflict in cone' '
+ init_repos &&
+
+ for side in right left
+ do
+ git -C sparse-index checkout -b expand-$side base &&
+ echo $side >sparse-index/deep/a &&
+ git -C sparse-index commit -a -m "$side" || return 1
+ done &&
+
+ (
+ sane_unset GIT_TEST_MERGE_ALGORITHM &&
+ git -C sparse-index config pull.twohead ort &&
+ ensure_not_expanded ! merge -m merged expand-right
+ )
+'
+
+test_expect_success 'sparse-index is not expanded: stash' '
+ init_repos &&
+
+ echo >>sparse-index/a &&
+ ensure_not_expanded stash &&
+ ensure_not_expanded stash list &&
+ ensure_not_expanded stash show stash@{0} &&
+ ensure_not_expanded stash apply stash@{0} &&
+ ensure_not_expanded stash drop stash@{0} &&
+
+ echo >>sparse-index/deep/new &&
+ ensure_not_expanded stash -u &&
+ (
+ WITHOUT_UNTRACKED_TXT=1 &&
+ ensure_not_expanded stash pop
+ ) &&
+
+ ensure_not_expanded stash create &&
+ oid=$(git -C sparse-index stash create) &&
+ ensure_not_expanded stash store -m "test" $oid &&
+ ensure_not_expanded reset --hard &&
+ ensure_not_expanded stash pop
+'
+
+test_expect_success 'describe tested on all' '
+ init_repos &&
+
+ # Add tag to be read by describe
+
+ run_on_all git tag -a v1.0 -m "Version 1" &&
+ test_all_match git describe --dirty &&
+ run_on_all rm g &&
+ test_all_match git describe --dirty
+'
+
+
+test_expect_success 'sparse-index is not expanded: describe' '
+ init_repos &&
+
+ # Add tag to be read by describe
+
+ git -C sparse-index tag -a v1.0 -m "Version 1" &&
+
+ ensure_not_expanded describe --dirty &&
+ echo "test" >>sparse-index/g &&
+ ensure_not_expanded describe --dirty &&
+ ensure_not_expanded describe
+'
+
+test_expect_success 'sparse index is not expanded: diff' '
+ init_repos &&
+
+ write_script edit-contents <<-\EOF &&
+ echo text >>$1
+ EOF
+
+ # Add file within cone
+ test_sparse_match git sparse-checkout set deep &&
+ run_on_all ../edit-contents deep/testfile &&
+ test_all_match git add deep/testfile &&
+ run_on_all ../edit-contents deep/testfile &&
+
+ test_all_match git diff &&
+ test_all_match git diff --cached &&
+ ensure_not_expanded diff &&
+ ensure_not_expanded diff --cached &&
+
+ # Add file outside cone
+ test_all_match git reset --hard &&
+ run_on_all mkdir newdirectory &&
+ run_on_all ../edit-contents newdirectory/testfile &&
+ test_sparse_match git sparse-checkout set newdirectory &&
+ test_all_match git add newdirectory/testfile &&
+ run_on_all ../edit-contents newdirectory/testfile &&
+ test_sparse_match git sparse-checkout set &&
+
+ test_all_match git diff &&
+ test_all_match git diff --cached &&
+ ensure_not_expanded diff &&
+ ensure_not_expanded diff --cached &&
+
+ # Merge conflict outside cone
+ # The sparse checkout will report a warning that is not in the
+ # full checkout, so we use `run_on_all` instead of
+ # `test_all_match`
+ run_on_all git reset --hard &&
+ test_all_match git checkout merge-left &&
+ test_all_match test_must_fail git merge merge-right &&
+
+ test_all_match git diff &&
+ test_all_match git diff --cached &&
+ ensure_not_expanded diff &&
+ ensure_not_expanded diff --cached
+'
+
+test_expect_success 'sparse index is not expanded: show and rev-parse' '
+ init_repos &&
+
+ ensure_not_expanded show :a &&
+ ensure_not_expanded show :deep/a &&
+ ensure_not_expanded rev-parse :a &&
+ ensure_not_expanded rev-parse :deep/a
+'
+
+test_expect_success 'sparse index is not expanded: update-index' '
+ init_repos &&
+
+ deep_a_oid=$(git -C full-checkout rev-parse update-deep:deep/a) &&
+ ensure_not_expanded update-index --cacheinfo 100644 $deep_a_oid deep/a &&
+
+ echo "test" >sparse-index/README.md &&
+ echo "test2" >sparse-index/a &&
+ rm -f sparse-index/deep/a &&
+
+ ensure_not_expanded update-index --add README.md &&
+ ensure_not_expanded update-index a &&
+ ensure_not_expanded update-index --remove deep/a &&
+
+ ensure_not_expanded reset --soft update-deep &&
+ ensure_not_expanded update-index --add --remove --again
+'
+
+test_expect_success 'sparse index is not expanded: blame' '
+ init_repos &&
+
+ for file in a \
+ deep/a \
+ deep/deeper1/a \
+ deep/deeper1/deepest/a
+ do
+ ensure_not_expanded blame $file || return 1
+ done
+'
+
+test_expect_success 'sparse index is not expanded: fetch/pull' '
+ init_repos &&
+
+ git -C sparse-index remote add full "file://$(pwd)/full-checkout" &&
+ ensure_not_expanded fetch full &&
+ git -C full-checkout commit --allow-empty -m "for pull merge" &&
+ git -C sparse-index commit --allow-empty -m "for pull merge" &&
+ ensure_not_expanded pull full base
+'
+
+test_expect_success 'sparse index is not expanded: read-tree' '
+ init_repos &&
+
+ ensure_not_expanded checkout -b test-branch update-folder1 &&
+ for MERGE_TREES in "base HEAD update-folder2" \
+ "base HEAD rename-base" \
+ "base update-folder2" \
+ "base rename-base" \
+ "update-folder2"
+ do
+ ensure_not_expanded read-tree -mu $MERGE_TREES &&
+ ensure_not_expanded reset --hard || return 1
+ done &&
+
+ rm -rf sparse-index/deep/deeper2 &&
+ ensure_not_expanded add . &&
+ ensure_not_expanded commit -m "test" &&
+
+ ensure_not_expanded read-tree --prefix=deep/deeper2 -u deepest
+'
+
+test_expect_success 'ls-files' '
+ init_repos &&
+
+ # Use a smaller sparse-checkout for reduced output
+ test_sparse_match git sparse-checkout set &&
+
+ # Behavior agrees by default. Sparse index is expanded.
+ test_all_match git ls-files &&
+
+ # With --sparse, the sparse index data changes behavior.
+ git -C sparse-index ls-files --sparse >actual &&
+
+ cat >expect <<-\EOF &&
+ a
+ before/
+ deep/
+ e
+ folder1-
+ folder1.x
+ folder1/
+ folder10
+ folder2/
+ g
+ x/
+ z
+ EOF
+
+ test_cmp expect actual &&
+
+ # With --sparse and no sparse index, nothing changes.
+ git -C sparse-checkout ls-files >dense &&
+ git -C sparse-checkout ls-files --sparse >sparse &&
+ test_cmp dense sparse &&
+
+ # Set up a strange condition of having a file edit
+ # outside of the sparse-checkout cone. We want to verify
+ # that all modes handle this the same, and detect the
+ # modification.
+ write_script edit-content <<-\EOF &&
+ mkdir -p folder1 &&
+ echo content >>folder1/a
+ EOF
+ run_on_all ../edit-content &&
+
+ test_all_match git ls-files --modified &&
+
+ git -C sparse-index ls-files --sparse --modified >sparse-index-out &&
+ cat >expect <<-\EOF &&
+ folder1/a
+ EOF
+ test_cmp expect sparse-index-out &&
+
+ # Add folder1 to the sparse-checkout cone and
+ # check that ls-files shows the expanded files.
+ test_sparse_match git sparse-checkout add folder1 &&
+ test_all_match git ls-files --modified &&
+
+ test_all_match git ls-files &&
+ git -C sparse-index ls-files --sparse >actual &&
+
+ cat >expect <<-\EOF &&
+ a
+ before/
+ deep/
+ e
+ folder1-
+ folder1.x
+ folder1/0/0/0
+ folder1/0/1
+ folder1/a
+ folder10
+ folder2/
+ g
+ x/
+ z
+ EOF
+
+ test_cmp expect actual &&
+
+ # Double-check index expansion is avoided
+ ensure_not_expanded ls-files --sparse
+'
+
+test_expect_success 'sparse index is not expanded: sparse-checkout' '
+ init_repos &&
+
+ ensure_not_expanded sparse-checkout set deep/deeper2 &&
+ ensure_not_expanded sparse-checkout set deep/deeper1 &&
+ ensure_not_expanded sparse-checkout set deep &&
+ ensure_not_expanded sparse-checkout add folder1 &&
+ ensure_not_expanded sparse-checkout set deep/deeper1 &&
+ ensure_not_expanded sparse-checkout set folder2 &&
+
+ # Demonstrate that the checks that "folder1/a" is a file
+ # do not cause a sparse-index expansion (since it is in the
+ # sparse-checkout cone).
+ echo >>sparse-index/folder2/a &&
+ git -C sparse-index add folder2/a &&
+
+ ensure_not_expanded sparse-checkout add folder1 &&
+
+ # Skip checks here, since deep/deeper1 is inside a sparse directory
+ # that must be expanded to check whether `deep/deeper1` is a file
+ # or not.
+ ensure_not_expanded sparse-checkout set --skip-checks deep/deeper1 &&
+ ensure_not_expanded sparse-checkout set
'
# NEEDSWORK: a sparse-checkout behaves differently from a full checkout
@@ -665,13 +1787,13 @@ test_expect_success 'reset mixed and checkout orphan' '
# the sparse checkouts skip "adding" the other side of
# the conflict.
test_sparse_match git reset --mixed HEAD~1 &&
- test_sparse_match test-tool read-cache --table --expand &&
+ test_sparse_match git ls-files --stage &&
test_sparse_match git status --porcelain=v2 &&
# At this point, sparse-checkouts behave differently
# from the full-checkout.
test_sparse_match git checkout --orphan new-branch &&
- test_sparse_match test-tool read-cache --table --expand &&
+ test_sparse_match git ls-files --stage &&
test_sparse_match git status --porcelain=v2
'
@@ -793,4 +1915,420 @@ test_expect_success 'checkout behaves oddly with df-conflict-2' '
test_cmp full-checkout-err sparse-index-err
'
+test_expect_success 'mv directory from out-of-cone to in-cone' '
+ init_repos &&
+
+ # <source> as a sparse directory (or SKIP_WORKTREE_DIR without enabling
+ # sparse index).
+ test_all_match git mv --sparse folder1 deep &&
+ test_all_match git status --porcelain=v2 &&
+ test_sparse_match git ls-files -t &&
+ git -C sparse-checkout ls-files -t >actual &&
+ grep -e "H deep/folder1/0/0/0" actual &&
+ grep -e "H deep/folder1/0/1" actual &&
+ grep -e "H deep/folder1/a" actual &&
+
+ test_all_match git reset --hard &&
+
+ # <source> as a directory deeper than sparse index boundary (where
+ # sparse index will expand).
+ test_sparse_match git mv --sparse folder1/0 deep &&
+ test_sparse_match git status --porcelain=v2 &&
+ test_sparse_match git ls-files -t &&
+ git -C sparse-checkout ls-files -t >actual &&
+ grep -e "H deep/0/0/0" actual &&
+ grep -e "H deep/0/1" actual
+'
+
+test_expect_success 'rm pathspec inside sparse definition' '
+ init_repos &&
+
+ test_all_match git rm deep/a &&
+ test_all_match git status --porcelain=v2 &&
+
+ # test wildcard
+ run_on_all git reset --hard &&
+ test_all_match git rm deep/* &&
+ test_all_match git status --porcelain=v2 &&
+
+ # test recursive rm
+ run_on_all git reset --hard &&
+ test_all_match git rm -r deep &&
+ test_all_match git status --porcelain=v2
+'
+
+test_expect_success 'rm pathspec outside sparse definition' '
+ init_repos &&
+
+ for file in folder1/a folder1/0/1
+ do
+ test_sparse_match test_must_fail git rm $file &&
+ test_sparse_match test_must_fail git rm --cached $file &&
+ test_sparse_match git rm --sparse $file &&
+ test_sparse_match git status --porcelain=v2 || return 1
+ done &&
+
+ cat >folder1-full <<-EOF &&
+ rm ${SQ}folder1/0/0/0${SQ}
+ rm ${SQ}folder1/0/1${SQ}
+ rm ${SQ}folder1/a${SQ}
+ EOF
+
+ cat >folder1-sparse <<-EOF &&
+ rm ${SQ}folder1/${SQ}
+ EOF
+
+ # test wildcard
+ run_on_sparse git reset --hard &&
+ run_on_sparse git sparse-checkout reapply &&
+ test_sparse_match test_must_fail git rm folder1/* &&
+ run_on_sparse git rm --sparse folder1/* &&
+ test_cmp folder1-full sparse-checkout-out &&
+ test_cmp folder1-sparse sparse-index-out &&
+ test_sparse_match git status --porcelain=v2 &&
+
+ # test recursive rm
+ run_on_sparse git reset --hard &&
+ run_on_sparse git sparse-checkout reapply &&
+ test_sparse_match test_must_fail git rm --sparse folder1 &&
+ run_on_sparse git rm --sparse -r folder1 &&
+ test_cmp folder1-full sparse-checkout-out &&
+ test_cmp folder1-sparse sparse-index-out &&
+ test_sparse_match git status --porcelain=v2
+'
+
+test_expect_success 'rm pathspec expands index when necessary' '
+ init_repos &&
+
+ # in-cone pathspec (do not expand)
+ ensure_not_expanded rm "deep/deep*" &&
+ test_must_be_empty sparse-index-err &&
+
+ # out-of-cone pathspec (expand)
+ ! ensure_not_expanded rm --sparse "folder1/a*" &&
+ test_must_be_empty sparse-index-err &&
+
+ # pathspec that should expand index
+ ! ensure_not_expanded rm "*/a" &&
+ test_must_be_empty sparse-index-err &&
+
+ ! ensure_not_expanded rm "**a" &&
+ test_must_be_empty sparse-index-err
+'
+
+test_expect_success 'sparse index is not expanded: rm' '
+ init_repos &&
+
+ ensure_not_expanded rm deep/a &&
+
+ # test in-cone wildcard
+ git -C sparse-index reset --hard &&
+ ensure_not_expanded rm deep/* &&
+
+ # test recursive rm
+ git -C sparse-index reset --hard &&
+ ensure_not_expanded rm -r deep
+'
+
+test_expect_success 'grep with and --cached' '
+ init_repos &&
+
+ test_all_match git grep --cached a &&
+ test_all_match git grep --cached a -- "folder1/*"
+'
+
+test_expect_success 'grep is not expanded' '
+ init_repos &&
+
+ ensure_not_expanded grep a &&
+ ensure_not_expanded grep a -- deep/* &&
+
+ # All files within the folder1/* pathspec are sparse,
+ # so this command does not find any matches
+ ensure_not_expanded ! grep a -- folder1/* &&
+
+ # test out-of-cone pathspec with or without wildcard
+ ensure_not_expanded grep --cached a -- "folder1/a" &&
+ ensure_not_expanded grep --cached a -- "folder1/*" &&
+
+ # test in-cone pathspec with or without wildcard
+ ensure_not_expanded grep --cached a -- "deep/a" &&
+ ensure_not_expanded grep --cached a -- "deep/*"
+'
+
+# NEEDSWORK: when running `grep` in the superproject with --recurse-submodules,
+# Git expands the index of the submodules unexpectedly. Even though `grep`
+# builtin is marked as "command_requires_full_index = 0", this config is only
+# useful for the superproject. Namely, the submodules have their own configs,
+# which are _not_ populated by the one-time sparse-index feature switch.
+test_expect_failure 'grep within submodules is not expanded' '
+ init_repos_as_submodules &&
+
+ # do not use ensure_not_expanded() here, becasue `grep` should be
+ # run in the superproject, not in "./sparse-index"
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+ git grep --cached --recurse-submodules a -- "*/folder1/*" &&
+ test_region ! index ensure_full_index trace2.txt
+'
+
+# NEEDSWORK: this test is not actually testing the code. The design purpose
+# of this test is to verify the grep result when the submodules are using a
+# sparse-index. Namely, we want "folder1/" as a tree (a sparse directory); but
+# because of the index expansion, we are now grepping the "folder1/a" blob.
+# Because of the problem stated above 'grep within submodules is not expanded',
+# we don't have the ideal test environment yet.
+test_expect_success 'grep sparse directory within submodules' '
+ init_repos_as_submodules &&
+
+ cat >expect <<-\EOF &&
+ full-checkout/folder1/a:a
+ sparse-checkout/folder1/a:a
+ sparse-index/folder1/a:a
+ EOF
+ git grep --cached --recurse-submodules a -- "*/folder1/*" >actual &&
+ test_cmp actual expect
+'
+
+test_expect_success 'write-tree' '
+ init_repos &&
+
+ test_all_match git write-tree &&
+
+ write_script edit-contents <<-\EOF &&
+ echo text >>"$1"
+ EOF
+
+ # make a change inside the sparse cone
+ run_on_all ../edit-contents deep/a &&
+ test_all_match git update-index deep/a &&
+ test_all_match git write-tree &&
+ test_all_match git status --porcelain=v2 &&
+
+ # make a change outside the sparse cone
+ run_on_all mkdir -p folder1 &&
+ run_on_all cp a folder1/a &&
+ run_on_all ../edit-contents folder1/a &&
+ test_all_match git update-index folder1/a &&
+ test_all_match git write-tree &&
+ test_all_match git status --porcelain=v2 &&
+
+ # check that SKIP_WORKTREE files are not materialized
+ test_path_is_missing sparse-checkout/folder2/a &&
+ test_path_is_missing sparse-index/folder2/a
+'
+
+test_expect_success 'sparse-index is not expanded: write-tree' '
+ init_repos &&
+
+ ensure_not_expanded write-tree &&
+
+ echo "test1" >>sparse-index/a &&
+ git -C sparse-index update-index a &&
+ ensure_not_expanded write-tree
+'
+
+test_expect_success 'diff-files with pathspec inside sparse definition' '
+ init_repos &&
+
+ write_script edit-contents <<-\EOF &&
+ echo text >>"$1"
+ EOF
+
+ run_on_all ../edit-contents deep/a &&
+
+ test_all_match git diff-files &&
+
+ test_all_match git diff-files -- deep/a &&
+
+ # test wildcard
+ test_all_match git diff-files -- "deep/*"
+'
+
+test_expect_success 'diff-files with pathspec outside sparse definition' '
+ init_repos &&
+
+ test_sparse_match git diff-files -- folder2/a &&
+
+ write_script edit-contents <<-\EOF &&
+ echo text >>"$1"
+ EOF
+
+ # The directory "folder1" is outside the cone of interest
+ # and will not exist in the sparse checkout repositories.
+ # Create it as needed, add file "folder1/a" there with
+ # contents that is different from the staged version.
+ run_on_all mkdir -p folder1 &&
+ run_on_all cp a folder1/a &&
+
+ run_on_all ../edit-contents folder1/a &&
+ test_all_match git diff-files &&
+ test_all_match git diff-files -- folder1/a &&
+ test_all_match git diff-files -- "folder*/a"
+'
+
+test_expect_success 'sparse index is not expanded: diff-files' '
+ init_repos &&
+
+ write_script edit-contents <<-\EOF &&
+ echo text >>"$1"
+ EOF
+
+ run_on_all ../edit-contents deep/a &&
+
+ ensure_not_expanded diff-files &&
+ ensure_not_expanded diff-files -- deep/a &&
+ ensure_not_expanded diff-files -- "deep/*"
+'
+
+test_expect_success 'diff-tree' '
+ init_repos &&
+
+ # Test change inside sparse cone
+ tree1=$(git -C sparse-index rev-parse HEAD^{tree}) &&
+ tree2=$(git -C sparse-index rev-parse update-deep^{tree}) &&
+ test_all_match git diff-tree $tree1 $tree2 &&
+ test_all_match git diff-tree $tree1 $tree2 -- deep/a &&
+ test_all_match git diff-tree HEAD update-deep &&
+ test_all_match git diff-tree HEAD update-deep -- deep/a &&
+
+ # Test change outside sparse cone
+ tree3=$(git -C sparse-index rev-parse update-folder1^{tree}) &&
+ test_all_match git diff-tree $tree1 $tree3 &&
+ test_all_match git diff-tree $tree1 $tree3 -- folder1/a &&
+ test_all_match git diff-tree HEAD update-folder1 &&
+ test_all_match git diff-tree HEAD update-folder1 -- folder1/a &&
+
+ # Check that SKIP_WORKTREE files are not materialized
+ test_path_is_missing sparse-checkout/folder1/a &&
+ test_path_is_missing sparse-index/folder1/a &&
+ test_path_is_missing sparse-checkout/folder2/a &&
+ test_path_is_missing sparse-index/folder2/a
+'
+
+test_expect_success 'sparse-index is not expanded: diff-tree' '
+ init_repos &&
+
+ tree1=$(git -C sparse-index rev-parse HEAD^{tree}) &&
+ tree2=$(git -C sparse-index rev-parse update-deep^{tree}) &&
+ tree3=$(git -C sparse-index rev-parse update-folder1^{tree}) &&
+
+ ensure_not_expanded diff-tree $tree1 $tree2 &&
+ ensure_not_expanded diff-tree $tree1 $tree2 -- deep/a &&
+ ensure_not_expanded diff-tree HEAD update-deep &&
+ ensure_not_expanded diff-tree HEAD update-deep -- deep/a &&
+ ensure_not_expanded diff-tree $tree1 $tree3 &&
+ ensure_not_expanded diff-tree $tree1 $tree3 -- folder1/a &&
+ ensure_not_expanded diff-tree HEAD update-folder1 &&
+ ensure_not_expanded diff-tree HEAD update-folder1 -- folder1/a
+'
+
+test_expect_success 'worktree' '
+ init_repos &&
+
+ write_script edit-contents <<-\EOF &&
+ echo text >>"$1"
+ EOF
+
+ for repo in full-checkout sparse-checkout sparse-index
+ do
+ worktree=${repo}-wt &&
+ git -C $repo worktree add ../$worktree &&
+
+ # Compare worktree content with "ls"
+ (cd $repo && ls) >worktree_contents &&
+ (cd $worktree && ls) >new_worktree_contents &&
+ test_cmp worktree_contents new_worktree_contents &&
+
+ # Compare index content with "ls-files --sparse"
+ git -C $repo ls-files --sparse >index_contents &&
+ git -C $worktree ls-files --sparse >new_index_contents &&
+ test_cmp index_contents new_index_contents &&
+
+ git -C $repo worktree remove ../$worktree || return 1
+ done &&
+
+ test_all_match git worktree add .worktrees/hotfix &&
+ run_on_all ../edit-contents .worktrees/hotfix/deep/a &&
+ test_all_match test_must_fail git worktree remove .worktrees/hotfix
+'
+
+test_expect_success 'worktree is not expanded' '
+ init_repos &&
+
+ ensure_not_expanded worktree add .worktrees/hotfix &&
+ ensure_not_expanded worktree remove .worktrees/hotfix
+'
+
+test_expect_success 'check-attr with pathspec inside sparse definition' '
+ init_repos &&
+
+ echo "a -crlf myAttr" >>.gitattributes &&
+ run_on_all cp ../.gitattributes ./deep &&
+
+ test_all_match git check-attr -a -- deep/a &&
+
+ test_all_match git add deep/.gitattributes &&
+ test_all_match git check-attr -a --cached -- deep/a
+'
+
+test_expect_success 'check-attr with pathspec outside sparse definition' '
+ init_repos &&
+
+ echo "a -crlf myAttr" >>.gitattributes &&
+ run_on_sparse mkdir folder1 &&
+ run_on_all cp ../.gitattributes ./folder1 &&
+ run_on_all cp a folder1/a &&
+
+ test_all_match git check-attr -a -- folder1/a &&
+
+ git -C full-checkout add folder1/.gitattributes &&
+ test_sparse_match git add --sparse folder1/.gitattributes &&
+ test_all_match git commit -m "add .gitattributes" &&
+ test_sparse_match git sparse-checkout reapply &&
+ test_all_match git check-attr -a --cached -- folder1/a
+'
+
+# NEEDSWORK: The 'diff --check' test is left as 'test_expect_failure' due
+# to an underlying issue in oneway_diff() within diff-lib.c.
+# 'do_oneway_diff()' is not called as expected for paths that could match
+# inside of a sparse directory. Specifically, the 'ce_path_match()' function
+# fails to recognize files inside a sparse directory (e.g., when 'folder1/'
+# is a sparse directory, 'folder1/a' cannot be recognized). The goal is to
+# proceed with 'do_oneway_diff()' if the pathspec could match inside of a
+# sparse directory.
+test_expect_failure 'diff --check with pathspec outside sparse definition' '
+ init_repos &&
+
+ write_script edit-contents <<-\EOF &&
+ echo "a " >"$1"
+ EOF
+
+ test_all_match git config core.whitespace -trailing-space,-space-before-tab &&
+
+ echo "a whitespace=trailing-space,space-before-tab" >>.gitattributes &&
+ run_on_all mkdir -p folder1 &&
+ run_on_all cp ../.gitattributes ./folder1 &&
+ test_all_match git add --sparse folder1/.gitattributes &&
+ run_on_all ../edit-contents folder1/a &&
+ test_all_match git add --sparse folder1/a &&
+
+ test_sparse_match git sparse-checkout reapply &&
+ test_all_match test_must_fail git diff --check --cached -- folder1/a
+'
+
+test_expect_success 'sparse-index is not expanded: check-attr' '
+ init_repos &&
+
+ echo "a -crlf myAttr" >>.gitattributes &&
+ mkdir ./sparse-index/folder1 &&
+ cp ./sparse-index/a ./sparse-index/folder1/a &&
+ cp .gitattributes ./sparse-index/deep &&
+ cp .gitattributes ./sparse-index/folder1 &&
+
+ git -C sparse-index add deep/.gitattributes &&
+ git -C sparse-index add --sparse folder1/.gitattributes &&
+ ensure_not_expanded check-attr -a --cached -- deep/a &&
+ ensure_not_expanded check-attr -a --cached -- folder1/a
+'
+
test_done
diff --git a/t/t1100-commit-tree-options.sh b/t/t1100-commit-tree-options.sh
index ae66ba5..0f37a43 100755
--- a/t/t1100-commit-tree-options.sh
+++ b/t/t1100-commit-tree-options.sh
@@ -12,6 +12,7 @@ Also make sure that command line parser understands the normal
"flags first and then non flag arguments" command line.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
cat >expected <<EOF
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index 9ff46f3..9b65d9e 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -8,8 +8,101 @@ test_description='Test git config in different settings'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
+test_expect_success 'setup whitespace config' '
+ sed -e "s/^|//" \
+ -e "s/[$]$//" \
+ -e "s/X/ /g" >.git/config <<-\EOF
+ [section]
+ | solid = rock
+ | sparse = big XX blue
+ | sparseAndTail = big XX blue $
+ | sparseAndTailQuoted = "big XX blue "
+ | sparseAndBiggerTail = big XX blue X X
+ | sparseAndBiggerTailQuoted = "big XX blue X X"
+ | sparseAndBiggerTailQuotedPlus = "big XX blue X X"X $
+ | headAndTail = Xbig blue $
+ | headAndTailQuoted = "Xbig blue "
+ | headAndTailQuotedPlus = "Xbig blue " $
+ | annotated = big blueX# to be discarded
+ | annotatedQuoted = "big blue"X# to be discarded
+ EOF
+'
+
+test_expect_success 'no internal whitespace' '
+ echo "rock" >expect &&
+ git config --get section.solid >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'internal whitespace' '
+ echo "big QQ blue" | q_to_tab >expect &&
+ git config --get section.sparse >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'internal and trailing whitespace' '
+ echo "big QQ blue" | q_to_tab >expect &&
+ git config --get section.sparseAndTail >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'internal and trailing whitespace, all quoted' '
+ echo "big QQ blue " | q_to_tab >expect &&
+ git config --get section.sparseAndTailQuoted >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'internal and more trailing whitespace' '
+ echo "big QQ blue" | q_to_tab >expect &&
+ git config --get section.sparseAndBiggerTail >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'internal and more trailing whitespace, all quoted' '
+ echo "big QQ blue Q Q" | q_to_tab >expect &&
+ git config --get section.sparseAndBiggerTailQuoted >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'internal and more trailing whitespace, not all quoted' '
+ echo "big QQ blue Q Q" | q_to_tab >expect &&
+ git config --get section.sparseAndBiggerTailQuotedPlus >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'leading and trailing whitespace' '
+ echo "big blue" >expect &&
+ git config --get section.headAndTail >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'leading and trailing whitespace, all quoted' '
+ echo "Qbig blue " | q_to_tab >expect &&
+ git config --get section.headAndTailQuoted >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'leading and trailing whitespace, not all quoted' '
+ echo "Qbig blue " | q_to_tab >expect &&
+ git config --get section.headAndTailQuotedPlus >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'inline comment' '
+ echo "big blue" >expect &&
+ git config --get section.annotated >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'inline comment, quoted' '
+ echo "big blue" >expect &&
+ git config --get section.annotatedQuoted >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'clear default config' '
rm -f .git/config
'
@@ -68,14 +161,32 @@ test_expect_success 'replace with non-match (actually matching)' '
cat > expect << EOF
[section]
- penguin = very blue
Movie = BadPhysics
UPPERCASE = true
- penguin = kingpin
+ penguin = gentoo # Pygoscelis papua
+ disposition = peckish # find fish
+ foo = bar #abc
+ spsp = value # and comment
+ htsp = value # and comment
[Sections]
WhatEver = Second
EOF
+test_expect_success 'append comments' '
+ git config --replace-all --comment="Pygoscelis papua" section.penguin gentoo &&
+ git config --comment="find fish" section.disposition peckish &&
+ git config --comment="#abc" section.foo bar &&
+
+ git config --comment="and comment" section.spsp value &&
+ git config --comment=" # and comment" section.htsp value &&
+
+ test_cmp expect .git/config
+'
+
+test_expect_success 'Prohibited LF in comment' '
+ test_must_fail git config --comment="a${LF}b" section.k v
+'
+
test_expect_success 'non-match result' 'test_cmp expect .git/config'
test_expect_success 'find mixed-case key by canonical name' '
@@ -97,6 +208,23 @@ test_expect_success 'subsections are not canonicalized by git-config' '
test_cmp_config two section.SubSection.key
'
+test_missing_key () {
+ local key="$1" &&
+ local title="$2" &&
+ test_expect_success "value for $title is not printed" '
+ test_must_fail git config "$key" >out 2>err &&
+ test_must_be_empty out &&
+ test_must_be_empty err
+ '
+}
+
+test_missing_key 'missingsection.missingkey' 'missing section and missing key'
+test_missing_key 'missingsection.penguin' 'missing section and existing key'
+test_missing_key 'section.missingkey' 'existing section and missing key'
+test_missing_key 'section.MissingSubSection.missingkey' 'missing subsection and missing key'
+test_missing_key 'section.SubSection.missingkey' 'existing subsection and missing key'
+test_missing_key 'section.MissingSubSection.key' 'missing subsection and existing key'
+
cat > .git/config <<\EOF
[alpha]
bar = foo
@@ -435,7 +563,7 @@ test_expect_success 'get bool variable with empty value' '
test_expect_success 'no arguments, but no crash' '
test_must_fail git config >output 2>&1 &&
- test_i18ngrep usage output
+ test_grep usage output
'
cat > .git/config << EOF
@@ -616,6 +744,36 @@ test_expect_success 'renaming to bogus section is rejected' '
test_must_fail git config --rename-section branch.zwei "bogus name"
'
+test_expect_success 'renaming a section with a long line' '
+ {
+ printf "[b]\\n" &&
+ printf " c = d %1024s [a] e = f\\n" " " &&
+ printf "[a] g = h\\n"
+ } >y &&
+ git config -f y --rename-section a xyz &&
+ test_must_fail git config -f y b.e
+'
+
+test_expect_success 'renaming an embedded section with a long line' '
+ {
+ printf "[b]\\n" &&
+ printf " c = d %1024s [a] [foo] e = f\\n" " " &&
+ printf "[a] g = h\\n"
+ } >y &&
+ git config -f y --rename-section a xyz &&
+ test_must_fail git config -f y foo.e
+'
+
+test_expect_success 'renaming a section with an overly-long line' '
+ {
+ printf "[b]\\n" &&
+ printf " c = d %525000s e" " " &&
+ printf "[a] g = h\\n"
+ } >y &&
+ test_must_fail git config -f y --rename-section a xyz 2>err &&
+ grep "refusing to work with overly long line in .y. on line 2" err
+'
+
cat >> .git/config << EOF
[branch "zwei"] a = 1 [branch "vier"]
EOF
@@ -672,25 +830,25 @@ test_expect_success 'invalid unit' '
git config aninvalid.unit "1auto" &&
test_cmp_config 1auto aninvalid.unit &&
test_must_fail git config --int --get aninvalid.unit 2>actual &&
- test_i18ngrep "bad numeric config value .1auto. for .aninvalid.unit. in file .git/config: invalid unit" actual
+ test_grep "bad numeric config value .1auto. for .aninvalid.unit. in file .git/config: invalid unit" actual
'
test_expect_success 'invalid unit boolean' '
git config commit.gpgsign "1true" &&
test_cmp_config 1true commit.gpgsign &&
test_must_fail git config --bool --get commit.gpgsign 2>actual &&
- test_i18ngrep "bad boolean config value .1true. for .commit.gpgsign." actual
+ test_grep "bad boolean config value .1true. for .commit.gpgsign." actual
'
test_expect_success 'line number is reported correctly' '
printf "[bool]\n\tvar\n" >invalid &&
test_must_fail git config -f invalid --path bool.var 2>actual &&
- test_i18ngrep "line 2" actual
+ test_grep "line 2" actual
'
test_expect_success 'invalid stdin config' '
echo "[broken" | test_must_fail git config --list --file - >output 2>&1 &&
- test_i18ngrep "bad config line 1 in standard input" output
+ test_grep "bad config line 1 in standard input" output
'
cat > expect << EOF
@@ -717,8 +875,8 @@ test_expect_success bool '
rm -f result &&
for i in 1 2 3 4
do
- git config --bool --get bool.true$i >>result
- git config --bool --get bool.false$i >>result
+ git config --bool --get bool.true$i >>result &&
+ git config --bool --get bool.false$i >>result || return 1
done &&
test_cmp expect result'
@@ -871,7 +1029,7 @@ test_expect_success !MINGW 'get --path copes with unset $HOME' '
git config --get --path path.normal >>result &&
git config --get --path path.trailingtilde >>result
) &&
- test_i18ngrep "[Ff]ailed to expand.*~/" msg &&
+ test_grep "[Ff]ailed to expand.*~/" msg &&
test_cmp expect result
'
@@ -901,7 +1059,7 @@ test_expect_success 'get --expiry-date' '
EOF
: "work around heredoc parsing bug fixed in dash 0.5.7 (in ec2c84d)" &&
{
- echo "$rel_out $(git config --expiry-date date.valid1)"
+ echo "$rel_out $(git config --expiry-date date.valid1)" &&
git config --expiry-date date.valid2 &&
git config --expiry-date date.valid3 &&
git config --expiry-date date.valid4 &&
@@ -938,7 +1096,7 @@ test_expect_success 'get --type=color barfs on non-color' '
test_expect_success 'set --type=color barfs on non-color' '
test_must_fail git config --type=color foo.color "not-a-color" 2>error &&
- test_i18ngrep "cannot parse color" error
+ test_grep "cannot parse color" error
'
cat > expect << EOF
@@ -1018,9 +1176,25 @@ test_expect_success '--null --get-regexp' '
test_cmp expect result
'
-test_expect_success 'inner whitespace kept verbatim' '
- git config section.val "foo bar" &&
- test_cmp_config "foo bar" section.val
+test_expect_success 'inner whitespace kept verbatim, spaces only' '
+ echo "foo bar" >expect &&
+ git config section.val "foo bar" &&
+ git config --get section.val >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'inner whitespace kept verbatim, horizontal tabs only' '
+ echo "fooQQbar" | q_to_tab >expect &&
+ git config section.val "$(cat expect)" &&
+ git config --get section.val >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'inner whitespace kept verbatim, horizontal tabs and spaces' '
+ echo "foo Q bar" | q_to_tab >expect &&
+ git config section.val "$(cat expect)" &&
+ git config --get section.val >actual &&
+ test_cmp expect actual
'
test_expect_success SYMLINKS 'symlinked configuration' '
@@ -1050,15 +1224,20 @@ test_expect_success SYMLINKS 'symlink to nonexistent configuration' '
test_must_fail git config --file=linktolinktonada --list
'
-test_expect_success 'check split_cmdline return' "
- git config alias.split-cmdline-fix 'echo \"' &&
- test_must_fail git split-cmdline-fix &&
- echo foo > foo &&
- git add foo &&
- git commit -m 'initial commit' &&
- git config branch.main.mergeoptions 'echo \"' &&
- test_must_fail git merge main
-"
+test_expect_success 'check split_cmdline return' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ git config alias.split-cmdline-fix "echo \"" &&
+ test_must_fail git split-cmdline-fix &&
+ echo foo >foo &&
+ git add foo &&
+ git commit -m "initial commit" &&
+ git config branch.main.mergeoptions "echo \"" &&
+ test_must_fail git merge main
+ )
+'
test_expect_success 'git -c "key=value" support' '
cat >expect <<-\EOF &&
@@ -1109,10 +1288,16 @@ test_expect_success 'git -c works with aliases of builtins' '
'
test_expect_success 'aliases can be CamelCased' '
- test_config alias.CamelCased "rev-parse HEAD" &&
- git CamelCased >out &&
- git rev-parse HEAD >expect &&
- test_cmp expect out
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ git config alias.CamelCased "rev-parse HEAD" &&
+ git CamelCased >out &&
+ git rev-parse HEAD >expect &&
+ test_cmp expect out
+ )
'
test_expect_success 'git -c does not split values on equals' '
@@ -1399,12 +1584,12 @@ test_expect_success 'git --config-env with missing value' '
test_expect_success 'git --config-env fails with invalid parameters' '
test_must_fail git --config-env=foo.flag config --bool foo.flag 2>error &&
- test_i18ngrep "invalid config format: foo.flag" error &&
+ test_grep "invalid config format: foo.flag" error &&
test_must_fail git --config-env=foo.flag= config --bool foo.flag 2>error &&
- test_i18ngrep "missing environment variable name for configuration ${SQ}foo.flag${SQ}" error &&
+ test_grep "missing environment variable name for configuration ${SQ}foo.flag${SQ}" error &&
sane_unset NONEXISTENT &&
test_must_fail git --config-env=foo.flag=NONEXISTENT config --bool foo.flag 2>error &&
- test_i18ngrep "missing environment variable ${SQ}NONEXISTENT${SQ} for configuration ${SQ}foo.flag${SQ}" error
+ test_grep "missing environment variable ${SQ}NONEXISTENT${SQ} for configuration ${SQ}foo.flag${SQ}" error
'
test_expect_success 'git -c and --config-env work together' '
@@ -1457,55 +1642,49 @@ test_expect_success 'git config ignores pairs without count' '
test_must_be_empty error
'
-test_expect_success 'git config ignores pairs with zero count' '
- test_must_fail env \
- GIT_CONFIG_COUNT=0 \
- GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
- git config pair.one
-'
-
test_expect_success 'git config ignores pairs exceeding count' '
GIT_CONFIG_COUNT=1 \
GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
GIT_CONFIG_KEY_1="pair.two" GIT_CONFIG_VALUE_1="value" \
- git config --get-regexp "pair.*" >actual &&
+ git config --get-regexp "pair.*" >actual 2>error &&
cat >expect <<-EOF &&
pair.one value
EOF
- test_cmp expect actual
+ test_cmp expect actual &&
+ test_must_be_empty error
'
test_expect_success 'git config ignores pairs with zero count' '
test_must_fail env \
GIT_CONFIG_COUNT=0 GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
- git config pair.one >error &&
+ git config pair.one 2>error &&
test_must_be_empty error
'
test_expect_success 'git config ignores pairs with empty count' '
test_must_fail env \
GIT_CONFIG_COUNT= GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
- git config pair.one >error &&
+ git config pair.one 2>error &&
test_must_be_empty error
'
test_expect_success 'git config fails with invalid count' '
test_must_fail env GIT_CONFIG_COUNT=10a git config --list 2>error &&
- test_i18ngrep "bogus count" error &&
+ test_grep "bogus count" error &&
test_must_fail env GIT_CONFIG_COUNT=9999999999999999 git config --list 2>error &&
- test_i18ngrep "too many entries" error
+ test_grep "too many entries" error
'
test_expect_success 'git config fails with missing config key' '
test_must_fail env GIT_CONFIG_COUNT=1 GIT_CONFIG_VALUE_0="value" \
git config --list 2>error &&
- test_i18ngrep "missing config key" error
+ test_grep "missing config key" error
'
test_expect_success 'git config fails with missing config value' '
test_must_fail env GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0="pair.one" \
git config --list 2>error &&
- test_i18ngrep "missing config value" error
+ test_grep "missing config value" error
'
test_expect_success 'git config fails with invalid config pair key' '
@@ -1570,12 +1749,12 @@ test_expect_success 'git config --edit respects core.editor' '
# malformed configuration files
test_expect_success 'barf on syntax error' '
cat >.git/config <<-\EOF &&
- # broken section line
+ # broken key=value
[section]
key garbage
EOF
- test_must_fail git config --get section.key >actual 2>error &&
- test_i18ngrep " line 3 " error
+ test_must_fail git config --get section.key 2>error &&
+ test_grep " line 3 " error
'
test_expect_success 'barf on incomplete section header' '
@@ -1584,18 +1763,18 @@ test_expect_success 'barf on incomplete section header' '
[section
key = value
EOF
- test_must_fail git config --get section.key >actual 2>error &&
- test_i18ngrep " line 2 " error
+ test_must_fail git config --get section.key 2>error &&
+ test_grep " line 2 " error
'
test_expect_success 'barf on incomplete string' '
cat >.git/config <<-\EOF &&
- # broken section line
+ # broken value string
[section]
key = "value string
EOF
- test_must_fail git config --get section.key >actual 2>error &&
- test_i18ngrep " line 3 " error
+ test_must_fail git config --get section.key 2>error &&
+ test_grep " line 3 " error
'
test_expect_success 'urlmatch' '
@@ -1626,6 +1805,21 @@ test_expect_success 'urlmatch' '
test_cmp expect actual
'
+test_expect_success 'urlmatch with --show-scope' '
+ cat >.git/config <<-\EOF &&
+ [http "https://weak.example.com"]
+ sslVerify = false
+ cookieFile = /tmp/cookie.txt
+ EOF
+
+ cat >expect <<-EOF &&
+ local http.cookiefile /tmp/cookie.txt
+ local http.sslverify false
+ EOF
+ git config --get-urlmatch --show-scope HTTP https://weak.example.com >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'urlmatch favors more specific URLs' '
cat >.git/config <<-\EOF &&
[http "https://example.com/"]
@@ -1952,11 +2146,11 @@ test_expect_success '--show-origin getting a single key' '
'
test_expect_success 'set up custom config file' '
- CUSTOM_CONFIG_FILE="custom.conf" &&
- cat >"$CUSTOM_CONFIG_FILE" <<-\EOF
+ cat >"custom.conf" <<-\EOF &&
[user]
custom = true
EOF
+ CUSTOM_CONFIG_FILE="$(test-tool path-utils real_path custom.conf)"
'
test_expect_success !MINGW 'set up custom config file with special name characters' '
@@ -1995,22 +2189,39 @@ test_expect_success '--show-origin stdin with file include' '
'
test_expect_success '--show-origin blob' '
- blob=$(git hash-object -w "$CUSTOM_CONFIG_FILE") &&
- cat >expect <<-EOF &&
- blob:$blob user.custom=true
- EOF
- git config --blob=$blob --show-origin --list >output &&
- test_cmp expect output
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ blob=$(git hash-object -w "$CUSTOM_CONFIG_FILE") &&
+ cat >expect <<-EOF &&
+ blob:$blob user.custom=true
+ EOF
+ git config --blob=$blob --show-origin --list >output &&
+ test_cmp expect output
+ )
'
test_expect_success '--show-origin blob ref' '
- cat >expect <<-\EOF &&
- blob:main:custom.conf user.custom=true
- EOF
- git add "$CUSTOM_CONFIG_FILE" &&
- git commit -m "new config file" &&
- git config --blob=main:"$CUSTOM_CONFIG_FILE" --show-origin --list >output &&
- test_cmp expect output
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ cat >expect <<-\EOF &&
+ blob:main:custom.conf user.custom=true
+ EOF
+ cp "$CUSTOM_CONFIG_FILE" custom.conf &&
+ git add custom.conf &&
+ git commit -m "new config file" &&
+ git config --blob=main:custom.conf --show-origin --list >output &&
+ test_cmp expect output
+ )
+'
+
+test_expect_success '--show-origin with --default' '
+ git config --show-origin --default foo some.key >actual &&
+ echo "command line: foo" >expect &&
+ test_cmp expect actual
'
test_expect_success '--show-scope with --list' '
@@ -2023,8 +2234,17 @@ test_expect_success '--show-scope with --list' '
local user.override=local
local include.path=../include/relative.include
local user.relative=include
+ local core.repositoryformatversion=1
+ local extensions.worktreeconfig=true
+ worktree user.worktree=true
command user.cmdline=true
EOF
+ git worktree add wt1 &&
+ # We need these to test for worktree scope, but outside of this
+ # test, this is just noise
+ test_config core.repositoryformatversion 1 &&
+ test_config extensions.worktreeConfig true &&
+ git config --worktree user.worktree true &&
git -c user.cmdline=true config --list --show-scope >output &&
test_cmp expect output
'
@@ -2072,13 +2292,20 @@ test_expect_success '--show-scope with --show-origin' '
test_cmp expect output
'
-test_expect_success 'override global and system config' '
- test_when_finished rm -f "$HOME"/.config/git &&
+test_expect_success '--show-scope with --default' '
+ git config --show-scope --default foo some.key >actual &&
+ echo "command foo" >expect &&
+ test_cmp expect actual
+'
+test_expect_success 'override global and system config' '
+ test_when_finished rm -f \"\$HOME\"/.gitconfig &&
cat >"$HOME"/.gitconfig <<-EOF &&
[home]
config = true
EOF
+
+ test_when_finished rm -rf \"\$HOME\"/.config/git &&
mkdir -p "$HOME"/.config/git &&
cat >"$HOME"/.config/git/config <<-EOF &&
[xdg]
@@ -2187,17 +2414,17 @@ test_expect_success 'identical mixed --type specifiers are allowed' '
test_expect_success 'non-identical modern --type specifiers are not allowed' '
test_must_fail git config --type=int --type=bool section.big 2>error &&
- test_i18ngrep "only one type at a time" error
+ test_grep "only one type at a time" error
'
test_expect_success 'non-identical legacy --type specifiers are not allowed' '
test_must_fail git config --int --bool section.big 2>error &&
- test_i18ngrep "only one type at a time" error
+ test_grep "only one type at a time" error
'
test_expect_success 'non-identical mixed --type specifiers are not allowed' '
test_must_fail git config --type=int --bool section.big 2>error &&
- test_i18ngrep "only one type at a time" error
+ test_grep "only one type at a time" error
'
test_expect_success '--type allows valid type specifiers' '
@@ -2214,7 +2441,13 @@ test_expect_success 'unset type specifiers may be reset to conflicting ones' '
test_expect_success '--type rejects unknown specifiers' '
test_must_fail git config --type=nonsense section.foo 2>error &&
- test_i18ngrep "unrecognized --type argument" error
+ test_grep "unrecognized --type argument" error
+'
+
+test_expect_success '--type=int requires at least one digit' '
+ test_must_fail git config --type int --default m some.key >out 2>error &&
+ grep "bad numeric config value" error &&
+ test_must_be_empty out
'
test_expect_success '--replace-all does not invent newlines' '
@@ -2254,7 +2487,7 @@ test_expect_success 'set all config with value-pattern' '
# multiple matches => failure
test_must_fail git config --file=config abc.key three o+ 2>err &&
- test_i18ngrep "has multiple values" err &&
+ test_grep "has multiple values" err &&
# multiple values, no match => add
git config --file=config abc.key three a+ &&
@@ -2387,4 +2620,122 @@ test_expect_success '--get and --get-all with --fixed-value' '
test_must_fail git config --file=config --get-regexp --fixed-value fixed+ non-existent
'
+test_expect_success 'includeIf.hasconfig:remote.*.url' '
+ git init hasremoteurlTest &&
+ test_when_finished "rm -rf hasremoteurlTest" &&
+
+ cat >include-this <<-\EOF &&
+ [user]
+ this = this-is-included
+ EOF
+ cat >dont-include-that <<-\EOF &&
+ [user]
+ that = that-is-not-included
+ EOF
+ cat >>hasremoteurlTest/.git/config <<-EOF &&
+ [includeIf "hasconfig:remote.*.url:foourl"]
+ path = "$(pwd)/include-this"
+ [includeIf "hasconfig:remote.*.url:barurl"]
+ path = "$(pwd)/dont-include-that"
+ [remote "foo"]
+ url = foourl
+ EOF
+
+ echo this-is-included >expect-this &&
+ git -C hasremoteurlTest config --get user.this >actual-this &&
+ test_cmp expect-this actual-this &&
+
+ test_must_fail git -C hasremoteurlTest config --get user.that
+'
+
+test_expect_success 'includeIf.hasconfig:remote.*.url respects last-config-wins' '
+ git init hasremoteurlTest &&
+ test_when_finished "rm -rf hasremoteurlTest" &&
+
+ cat >include-two-three <<-\EOF &&
+ [user]
+ two = included-config
+ three = included-config
+ EOF
+ cat >>hasremoteurlTest/.git/config <<-EOF &&
+ [remote "foo"]
+ url = foourl
+ [user]
+ one = main-config
+ two = main-config
+ [includeIf "hasconfig:remote.*.url:foourl"]
+ path = "$(pwd)/include-two-three"
+ [user]
+ three = main-config
+ EOF
+
+ echo main-config >expect-main-config &&
+ echo included-config >expect-included-config &&
+
+ git -C hasremoteurlTest config --get user.one >actual &&
+ test_cmp expect-main-config actual &&
+
+ git -C hasremoteurlTest config --get user.two >actual &&
+ test_cmp expect-included-config actual &&
+
+ git -C hasremoteurlTest config --get user.three >actual &&
+ test_cmp expect-main-config actual
+'
+
+test_expect_success 'includeIf.hasconfig:remote.*.url globs' '
+ git init hasremoteurlTest &&
+ test_when_finished "rm -rf hasremoteurlTest" &&
+
+ printf "[user]\ndss = yes\n" >double-star-start &&
+ printf "[user]\ndse = yes\n" >double-star-end &&
+ printf "[user]\ndsm = yes\n" >double-star-middle &&
+ printf "[user]\nssm = yes\n" >single-star-middle &&
+ printf "[user]\nno = no\n" >no &&
+
+ cat >>hasremoteurlTest/.git/config <<-EOF &&
+ [remote "foo"]
+ url = https://foo/bar/baz
+ [includeIf "hasconfig:remote.*.url:**/baz"]
+ path = "$(pwd)/double-star-start"
+ [includeIf "hasconfig:remote.*.url:**/nomatch"]
+ path = "$(pwd)/no"
+ [includeIf "hasconfig:remote.*.url:https:/**"]
+ path = "$(pwd)/double-star-end"
+ [includeIf "hasconfig:remote.*.url:nomatch:/**"]
+ path = "$(pwd)/no"
+ [includeIf "hasconfig:remote.*.url:https:/**/baz"]
+ path = "$(pwd)/double-star-middle"
+ [includeIf "hasconfig:remote.*.url:https:/**/nomatch"]
+ path = "$(pwd)/no"
+ [includeIf "hasconfig:remote.*.url:https://*/bar/baz"]
+ path = "$(pwd)/single-star-middle"
+ [includeIf "hasconfig:remote.*.url:https://*/baz"]
+ path = "$(pwd)/no"
+ EOF
+
+ git -C hasremoteurlTest config --get user.dss &&
+ git -C hasremoteurlTest config --get user.dse &&
+ git -C hasremoteurlTest config --get user.dsm &&
+ git -C hasremoteurlTest config --get user.ssm &&
+ test_must_fail git -C hasremoteurlTest config --get user.no
+'
+
+test_expect_success 'includeIf.hasconfig:remote.*.url forbids remote url in such included files' '
+ git init hasremoteurlTest &&
+ test_when_finished "rm -rf hasremoteurlTest" &&
+
+ cat >include-with-url <<-\EOF &&
+ [remote "bar"]
+ url = barurl
+ EOF
+ cat >>hasremoteurlTest/.git/config <<-EOF &&
+ [includeIf "hasconfig:remote.*.url:foourl"]
+ path = "$(pwd)/include-with-url"
+ EOF
+
+ # test with any Git command
+ test_must_fail git -C hasremoteurlTest status 2>err &&
+ grep "fatal: remote URLs cannot be configured in file directly or indirectly included by includeIf.hasconfig:remote.*.url" err
+'
+
test_done
diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh
index 84bf197..29cf8a9 100755
--- a/t/t1301-shared-repo.sh
+++ b/t/t1301-shared-repo.sh
@@ -8,6 +8,8 @@ test_description='Test shared repository initialization'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_CREATE_REPO_NO_TEMPLATE=1
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Remove a default ACL from the test dir if possible.
@@ -25,6 +27,7 @@ test_expect_success 'shared = 0400 (faulty permission u-w)' '
for u in 002 022
do
test_expect_success POSIXPERM "shared=1 does not clear bits preset by umask $u" '
+ test_when_finished "rm -rf sub" &&
mkdir sub && (
cd sub &&
umask $u &&
@@ -42,21 +45,30 @@ do
;;
esac
'
- rm -rf sub
done
test_expect_success 'shared=all' '
- mkdir sub &&
- cd sub &&
- git init --shared=all &&
+ git init --template= --shared=all &&
test 2 = $(git config core.sharedrepository)
'
+test_expect_success 'template cannot set core.bare' '
+ test_when_finished "rm -rf subdir" &&
+ test_when_finished "rm -rf templates" &&
+ test_config core.bare true &&
+ umask 0022 &&
+ mkdir -p templates/ &&
+ cp .git/config templates/config &&
+ git init --template=templates subdir &&
+ test_path_is_missing subdir/HEAD
+'
+
test_expect_success POSIXPERM 'update-server-info honors core.sharedRepository' '
: > a1 &&
git add a1 &&
test_tick &&
git commit -m a1 &&
+ mkdir .git/info &&
umask 0277 &&
git update-server-info &&
actual="$(ls -l .git/info/refs)" &&
@@ -88,7 +100,7 @@ do
rm -f .git/info/refs &&
git update-server-info &&
actual="$(test_modebits .git/info/refs)" &&
- verbose test "x$actual" = "x-$y"
+ test "x$actual" = "x-$y"
'
@@ -98,7 +110,7 @@ do
rm -f .git/info/refs &&
git update-server-info &&
actual="$(test_modebits .git/info/refs)" &&
- verbose test "x$actual" = "x-$x"
+ test "x$actual" = "x-$x"
'
@@ -114,23 +126,8 @@ test_expect_success POSIXPERM 'info/refs respects umask in unshared repo' '
test_cmp expect actual
'
-test_expect_success POSIXPERM 'git reflog expire honors core.sharedRepository' '
- umask 077 &&
- git config core.sharedRepository group &&
- git reflog expire --all &&
- actual="$(ls -l .git/logs/refs/heads/main)" &&
- case "$actual" in
- -rw-rw-*)
- : happy
- ;;
- *)
- echo Ooops, .git/logs/refs/heads/main is not 066x [$actual]
- false
- ;;
- esac
-'
-
test_expect_success POSIXPERM 'forced modes' '
+ test_when_finished "rm -rf new" &&
mkdir -p templates/hooks &&
echo update-server-info >templates/hooks/post-update &&
chmod +x templates/hooks/post-update &&
@@ -139,7 +136,8 @@ test_expect_success POSIXPERM 'forced modes' '
(
cd new &&
umask 002 &&
- git init --shared=0660 --template=templates &&
+ git init --shared=0660 --template=../templates &&
+ test_path_is_file .git/hooks/post-update &&
>frotz &&
git add frotz &&
git commit -a -m initial &&
@@ -172,6 +170,7 @@ test_expect_success POSIXPERM 'forced modes' '
'
test_expect_success POSIXPERM 'remote init does not use config from cwd' '
+ test_when_finished "rm -rf child.git" &&
git config core.sharedrepository 0666 &&
umask 0022 &&
git init --bare child.git &&
@@ -191,7 +190,7 @@ test_expect_success POSIXPERM 're-init respects core.sharedrepository (local)' '
'
test_expect_success POSIXPERM 're-init respects core.sharedrepository (remote)' '
- rm -rf child.git &&
+ test_when_finished "rm -rf child.git" &&
umask 0022 &&
git init --bare --shared=0666 child.git &&
test_path_is_missing child.git/foo &&
@@ -202,7 +201,7 @@ test_expect_success POSIXPERM 're-init respects core.sharedrepository (remote)'
'
test_expect_success POSIXPERM 'template can set core.sharedrepository' '
- rm -rf child.git &&
+ test_when_finished "rm -rf child.git" &&
umask 0022 &&
git config core.sharedrepository 0666 &&
cp .git/config templates/config &&
diff --git a/t/t1302-repo-version.sh b/t/t1302-repo-version.sh
index 0acabb6..42caa0d 100755
--- a/t/t1302-repo-version.sh
+++ b/t/t1302-repo-version.sh
@@ -5,13 +5,10 @@
test_description='Test repository version check'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
- test_oid_cache <<-\EOF &&
- version sha1:0
- version sha256:1
- EOF
cat >test.patch <<-\EOF &&
diff --git a/test.txt b/test.txt
new file mode 100644
@@ -27,7 +24,12 @@ test_expect_success 'setup' '
'
test_expect_success 'gitdir selection on normal repos' '
- echo $(test_oid version) >expect &&
+ if test_have_prereq DEFAULT_REPO_FORMAT
+ then
+ echo 0
+ else
+ echo 1
+ fi >expect &&
git config core.repositoryformatversion >actual &&
git -C test config core.repositoryformatversion >actual2 &&
test_cmp expect actual &&
@@ -36,7 +38,7 @@ test_expect_success 'gitdir selection on normal repos' '
test_expect_success 'gitdir selection on unsupported repo' '
# Make sure it would stop at test2, not trash
- test_expect_code 1 git -C test2 config core.repositoryformatversion >actual
+ test_expect_code 1 git -C test2 config core.repositoryformatversion
'
test_expect_success 'gitdir not required mode' '
@@ -78,8 +80,13 @@ mkconfig () {
while read outcome version extensions; do
test_expect_success "$outcome version=$version $extensions" "
- mkconfig $version $extensions >.git/config &&
- check_${outcome}
+ test_when_finished 'rm -rf extensions' &&
+ git init extensions &&
+ (
+ cd extensions &&
+ mkconfig $version $extensions >.git/config &&
+ check_${outcome}
+ )
"
done <<\EOF
allow 0
@@ -93,7 +100,8 @@ allow 1 noop-v1
EOF
test_expect_success 'precious-objects allowed' '
- mkconfig 1 preciousObjects >.git/config &&
+ git config core.repositoryFormatVersion 1 &&
+ git config extensions.preciousObjects 1 &&
check_allow
'
diff --git a/t/t1303-wacky-config.sh b/t/t1303-wacky-config.sh
index 0000e66..0506f3d 100755
--- a/t/t1303-wacky-config.sh
+++ b/t/t1303-wacky-config.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='Test wacky input to git config'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Leaving off the newline is intentional!
diff --git a/t/t1304-default-acl.sh b/t/t1304-default-acl.sh
index 335d3f3..31b89dd 100755
--- a/t/t1304-default-acl.sh
+++ b/t/t1304-default-acl.sh
@@ -9,6 +9,7 @@ test_description='Test repository with default ACL'
# => this must come before . ./test-lib.sh
umask 077
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# We need an arbitrary other user give permission to using ACLs. root
@@ -18,7 +19,7 @@ test_expect_success 'checking for a working acl setup' '
if setfacl -m d:m:rwx -m u:root:rwx . &&
getfacl . | grep user:root:rwx &&
touch should-have-readable-acl &&
- getfacl should-have-readable-acl | egrep "mask::?rw-"
+ getfacl should-have-readable-acl | grep -E "mask::?rw-"
then
test_set_prereq SETFACL
fi
@@ -34,7 +35,7 @@ check_perms_and_acl () {
getfacl "$1" > actual &&
grep -q "user:root:rwx" actual &&
grep -q "user:${LOGNAME}:rwx" actual &&
- egrep "mask::?r--" actual > /dev/null 2>&1 &&
+ grep -E "mask::?r--" actual > /dev/null 2>&1 &&
grep -q "group::---" actual || false
}
diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh
index ccbb116..5cde79e 100755
--- a/t/t1305-config-include.sh
+++ b/t/t1305-config-include.sh
@@ -1,6 +1,7 @@
#!/bin/sh
test_description='test config file include directives'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Force setup_explicit_git_dir() to run until the end. This is needed
diff --git a/t/t1307-config-blob.sh b/t/t1307-config-blob.sh
index 930dce0..b9852fe 100755
--- a/t/t1307-config-blob.sh
+++ b/t/t1307-config-blob.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='support for reading config from a blob'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'create config blob' '
@@ -61,7 +63,7 @@ test_expect_success 'parse errors in blobs are properly attributed' '
git commit -m broken &&
test_must_fail git config --blob=HEAD:config some.value 2>err &&
- test_i18ngrep "HEAD:config" err
+ test_grep "HEAD:config" err
'
test_expect_success 'can parse blob ending with CR' '
diff --git a/t/t1308-config-set.sh b/t/t1308-config-set.sh
index 88b119a..3bfec07 100755
--- a/t/t1308-config-set.sh
+++ b/t/t1308-config-set.sh
@@ -2,6 +2,7 @@
test_description='Test git config-set API in different settings'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# 'check_config get_* section.key value' verifies that the entry for
@@ -57,6 +58,8 @@ test_expect_success 'setup default config' '
skin = false
nose = 1
horns
+ [value]
+ less
EOF
'
@@ -115,10 +118,53 @@ test_expect_success 'find value with the highest priority' '
check_config get_value case.baz "hask"
'
+test_expect_success 'return value for an existing key' '
+ test-tool config get lamb.chop >out 2>err &&
+ test_must_be_empty out &&
+ test_must_be_empty err
+'
+
+test_expect_success 'return value for value-less key' '
+ test-tool config get value.less >out 2>err &&
+ test_must_be_empty out &&
+ test_must_be_empty err
+'
+
+test_expect_success 'return value for a missing key' '
+ cat >expect <<-\EOF &&
+ Value not found for "missing.key"
+ EOF
+ test_expect_code 1 test-tool config get missing.key >actual 2>err &&
+ test_cmp actual expect &&
+ test_must_be_empty err
+'
+
+test_expect_success 'return value for a bad key: CONFIG_INVALID_KEY' '
+ cat >expect <<-\EOF &&
+ Key "fails.iskeychar.-" is invalid
+ EOF
+ test_expect_code 1 test-tool config get fails.iskeychar.- >actual 2>err &&
+ test_cmp actual expect &&
+ test_must_be_empty out
+'
+
+test_expect_success 'return value for a bad key: CONFIG_NO_SECTION_OR_NAME' '
+ cat >expect <<-\EOF &&
+ Key "keynosection" has no section
+ EOF
+ test_expect_code 1 test-tool config get keynosection >actual 2>err &&
+ test_cmp actual expect &&
+ test_must_be_empty out
+'
+
test_expect_success 'find integer value for a key' '
check_config get_int lamb.chop 65
'
+test_expect_success 'parse integer value during iteration' '
+ check_config git_config_int lamb.chop 65
+'
+
test_expect_success 'find string value for a key' '
check_config get_string case.baz hask &&
check_config expect_code 1 get_string case.ba "Value not found for \"case.ba\""
@@ -126,13 +172,18 @@ test_expect_success 'find string value for a key' '
test_expect_success 'check line error when NULL string is queried' '
test_expect_code 128 test-tool config get_string case.foo 2>result &&
- test_i18ngrep "fatal: .*case\.foo.*\.git/config.*line 7" result
+ test_grep "fatal: .*case\.foo.*\.git/config.*line 7" result
'
test_expect_success 'find integer if value is non parse-able' '
check_config expect_code 128 get_int lamb.head
'
+test_expect_success 'non parse-able integer value during iteration' '
+ check_config expect_code 128 git_config_int lamb.head 2>result &&
+ grep "fatal: bad numeric config value .* in file \.git/config" result
+'
+
test_expect_success 'find bool value for the entered key' '
check_config get_bool goat.head 1 &&
check_config get_bool goat.skin 0 &&
@@ -145,6 +196,71 @@ test_expect_success 'find multiple values' '
check_config get_value_multi case.baz sam bat hask
'
+test_NULL_in_multi () {
+ local op="$1" &&
+ local file="$2" &&
+
+ test_expect_success "$op: NULL value in config${file:+ in $file}" '
+ config="$file" &&
+ if test -z "$config"
+ then
+ config=.git/config &&
+ test_when_finished "mv $config.old $config" &&
+ mv "$config" "$config".old
+ fi &&
+
+ # Value-less in the middle of a list
+ cat >"$config" <<-\EOF &&
+ [a]key=x
+ [a]key
+ [a]key=y
+ EOF
+ case "$op" in
+ *_multi)
+ cat >expect <<-\EOF
+ x
+ (NULL)
+ y
+ EOF
+ ;;
+ *)
+ cat >expect <<-\EOF
+ y
+ EOF
+ ;;
+ esac &&
+ test-tool config "$op" a.key $file >actual &&
+ test_cmp expect actual &&
+
+ # Value-less at the end of a least
+ cat >"$config" <<-\EOF &&
+ [a]key=x
+ [a]key=y
+ [a]key
+ EOF
+ case "$op" in
+ *_multi)
+ cat >expect <<-\EOF
+ x
+ y
+ (NULL)
+ EOF
+ ;;
+ *)
+ cat >expect <<-\EOF
+ (NULL)
+ EOF
+ ;;
+ esac &&
+ test-tool config "$op" a.key $file >actual &&
+ test_cmp expect actual
+ '
+}
+
+test_NULL_in_multi "get_value_multi"
+test_NULL_in_multi "configset_get_value" "my.config"
+test_NULL_in_multi "configset_get_value_multi" "my.config"
+
test_expect_success 'find value from a configset' '
cat >config2 <<-\EOF &&
[case]
@@ -206,7 +322,7 @@ test_expect_success 'proper error on error in default config files' '
cp .git/config .git/config.old &&
test_when_finished "mv .git/config.old .git/config" &&
echo "[" >>.git/config &&
- echo "fatal: bad config line 34 in file .git/config" >expect &&
+ echo "fatal: bad config line 36 in file .git/config" >expect &&
test_expect_code 128 test-tool config get_value foo.bar 2>actual &&
test_cmp expect actual
'
@@ -226,14 +342,14 @@ test_expect_success 'check line errors for malformed values' '
br
EOF
test_expect_code 128 git br 2>result &&
- test_i18ngrep "missing value for .alias\.br" result &&
- test_i18ngrep "fatal: .*\.git/config" result &&
- test_i18ngrep "fatal: .*line 2" result
+ test_grep "missing value for .alias\.br" result &&
+ test_grep "fatal: .*\.git/config" result &&
+ test_grep "fatal: .*line 2" result
'
test_expect_success 'error on modifying repo config without repo' '
nongit test_must_fail git config a.b c 2>err &&
- test_i18ngrep "not in a git directory" err
+ test_grep "not in a git directory" err
'
cmdline_config="'foo.bar=from-cmdline'"
diff --git a/t/t1309-early-config.sh b/t/t1309-early-config.sh
index b4a9158..523aa99 100755
--- a/t/t1309-early-config.sh
+++ b/t/t1309-early-config.sh
@@ -2,6 +2,7 @@
test_description='Test read_early_config()'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'read early config' '
@@ -77,7 +78,7 @@ test_with_config () {
test_expect_success 'ignore .git/ with incompatible repository version' '
test_with_config "[core]repositoryformatversion = 999999" 2>err &&
- test_i18ngrep "warning:.* Expected git repo version <= [1-9]" err
+ test_grep "warning:.* Expected git repo version <= [1-9]" err
'
test_expect_failure 'ignore .git/ with invalid repository version' '
diff --git a/t/t1310-config-default.sh b/t/t1310-config-default.sh
index 6049d91..1a90d31 100755
--- a/t/t1310-config-default.sh
+++ b/t/t1310-config-default.sh
@@ -2,6 +2,7 @@
test_description='Test git config in different settings (with --default)'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'uses --default when entry missing' '
@@ -25,12 +26,12 @@ test_expect_success 'canonicalizes --default with appropriate type' '
test_expect_success 'dies when --default cannot be parsed' '
test_must_fail git config -f config --type=expiry-date --default=x --get \
not.a.section 2>error &&
- test_i18ngrep "failed to format default config value" error
+ test_grep "failed to format default config value" error
'
test_expect_success 'does not allow --default without --get' '
test_must_fail git config --default=quux --unset a.section >output 2>&1 &&
- test_i18ngrep "\-\-default is only applicable to" output
+ test_grep "\-\-default is only applicable to" output
'
test_done
diff --git a/t/t1350-config-hooks-path.sh b/t/t1350-config-hooks-path.sh
index fa9647a..f6dc83e 100755
--- a/t/t1350-config-hooks-path.sh
+++ b/t/t1350-config-hooks-path.sh
@@ -6,11 +6,11 @@ test_description='Test the core.hooksPath configuration variable'
test_expect_success 'set up a pre-commit hook in core.hooksPath' '
>actual &&
- mkdir -p .git/custom-hooks .git/hooks &&
+ mkdir -p .git/custom-hooks &&
write_script .git/custom-hooks/pre-commit <<-\EOF &&
echo CUSTOM >>actual
EOF
- write_script .git/hooks/pre-commit <<-\EOF
+ test_hook --setup pre-commit <<-\EOF
echo NORMAL >>actual
EOF
'
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 4506cd4..ec3443c 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -4,16 +4,11 @@
#
test_description='Test git update-ref and basic ref logging'
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
. ./test-lib.sh
Z=$ZERO_OID
m=refs/heads/main
-n_dir=refs/heads/gu
-n=$n_dir/fixes
outside=refs/foo
bare=bare-repo
@@ -65,10 +60,10 @@ test_expect_success "delete $m without oldvalue verification" '
test_must_fail git show-ref --verify -q $m
'
-test_expect_success "fail to create $n" '
- test_when_finished "rm -f .git/$n_dir" &&
- touch .git/$n_dir &&
- test_must_fail git update-ref $n $A
+test_expect_success "fail to create $n due to file/directory conflict" '
+ test_when_finished "git update-ref -d refs/heads/gu" &&
+ git update-ref refs/heads/gu $A &&
+ test_must_fail git update-ref refs/heads/gu/fixes $A
'
test_expect_success "create $m (by HEAD)" '
@@ -95,7 +90,8 @@ test_expect_success "deleting current branch adds message to HEAD's log" '
git symbolic-ref HEAD $m &&
git update-ref -m delete-$m -d $m &&
test_must_fail git show-ref --verify -q $m &&
- grep "delete-$m$" .git/logs/HEAD
+ test-tool ref-store main for-each-reflog-ent HEAD >actual &&
+ grep "delete-$m$" actual
'
test_expect_success "deleting by HEAD adds message to HEAD's log" '
@@ -104,7 +100,8 @@ test_expect_success "deleting by HEAD adds message to HEAD's log" '
git symbolic-ref HEAD $m &&
git update-ref -m delete-by-head -d HEAD &&
test_must_fail git show-ref --verify -q $m &&
- grep "delete-by-head$" .git/logs/HEAD
+ test-tool ref-store main for-each-reflog-ent HEAD >actual &&
+ grep "delete-by-head$" actual
'
test_expect_success 'update-ref does not create reflogs by default' '
@@ -135,7 +132,7 @@ test_expect_success 'creates no reflog in bare repository' '
test_expect_success 'core.logAllRefUpdates=true creates reflog in bare repository' '
test_when_finished "git -C $bare config --unset core.logAllRefUpdates && \
- rm $bare/logs/$m" &&
+ test-tool ref-store main delete-reflog $m" &&
git -C $bare config core.logAllRefUpdates true &&
git -C $bare update-ref $m $bareB &&
git -C $bare rev-parse $bareB >expect &&
@@ -224,27 +221,27 @@ test_expect_success 'delete symref without dereference when the referred ref is
'
test_expect_success 'update-ref -d is not confused by self-reference' '
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self" &&
git symbolic-ref refs/heads/self refs/heads/self &&
- test_when_finished "rm -f .git/refs/heads/self" &&
- test_path_is_file .git/refs/heads/self &&
+ git symbolic-ref --no-recurse refs/heads/self &&
test_must_fail git update-ref -d refs/heads/self &&
- test_path_is_file .git/refs/heads/self
+ git symbolic-ref --no-recurse refs/heads/self
'
test_expect_success 'update-ref --no-deref -d can delete self-reference' '
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self" &&
git symbolic-ref refs/heads/self refs/heads/self &&
- test_when_finished "rm -f .git/refs/heads/self" &&
- test_path_is_file .git/refs/heads/self &&
+ git symbolic-ref --no-recurse refs/heads/self &&
git update-ref --no-deref -d refs/heads/self &&
test_must_fail git show-ref --verify -q refs/heads/self
'
-test_expect_success 'update-ref --no-deref -d can delete reference to bad ref' '
+test_expect_success REFFILES 'update-ref --no-deref -d can delete reference to bad ref' '
>.git/refs/heads/bad &&
test_when_finished "rm -f .git/refs/heads/bad" &&
git symbolic-ref refs/heads/ref-to-bad refs/heads/bad &&
test_when_finished "git update-ref -d refs/heads/ref-to-bad" &&
- test_path_is_file .git/refs/heads/ref-to-bad &&
+ git symbolic-ref --no-recurse refs/heads/ref-to-bad &&
git update-ref --no-deref -d refs/heads/ref-to-bad &&
test_must_fail git show-ref --verify -q refs/heads/ref-to-bad
'
@@ -268,7 +265,10 @@ test_expect_success "(not) changed .git/$m" '
! test $B = $(git show-ref -s --verify $m)
'
-rm -f .git/logs/refs/heads/main
+test_expect_success "clean up reflog" '
+ test-tool ref-store main delete-reflog $m
+'
+
test_expect_success "create $m (logged by touch)" '
test_config core.logAllRefUpdates false &&
GIT_COMMITTER_DATE="2005-05-26 23:30" \
@@ -288,41 +288,15 @@ test_expect_success "set $m (logged by touch)" '
test $A = $(git show-ref -s --verify $m)
'
-test_expect_success 'empty directory removal' '
- git branch d1/d2/r1 HEAD &&
- git branch d1/r2 HEAD &&
- test_path_is_file .git/refs/heads/d1/d2/r1 &&
- test_path_is_file .git/logs/refs/heads/d1/d2/r1 &&
- git branch -d d1/d2/r1 &&
- test_must_fail git show-ref --verify -q refs/heads/d1/d2 &&
- test_must_fail git show-ref --verify -q logs/refs/heads/d1/d2 &&
- test_path_is_file .git/refs/heads/d1/r2 &&
- test_path_is_file .git/logs/refs/heads/d1/r2
-'
-
-test_expect_success 'symref empty directory removal' '
- git branch e1/e2/r1 HEAD &&
- git branch e1/r2 HEAD &&
- git checkout e1/e2/r1 &&
- test_when_finished "git checkout main" &&
- test_path_is_file .git/refs/heads/e1/e2/r1 &&
- test_path_is_file .git/logs/refs/heads/e1/e2/r1 &&
- git update-ref -d HEAD &&
- test_must_fail git show-ref --verify -q refs/heads/e1/e2 &&
- test_must_fail git show-ref --verify -q logs/refs/heads/e1/e2 &&
- test_path_is_file .git/refs/heads/e1/r2 &&
- test_path_is_file .git/logs/refs/heads/e1/r2 &&
- test_path_is_file .git/logs/HEAD
-'
-
cat >expect <<EOF
$Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 Initial Creation
$A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150260 +0000 Switch
$B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000
EOF
test_expect_success "verifying $m's log (logged by touch)" '
- test_when_finished "rm -rf .git/$m .git/logs expect" &&
- test_cmp expect .git/logs/$m
+ test_when_finished "git update-ref -d $m && git reflog expire --expire=all --all && rm -rf actual expect" &&
+ test-tool ref-store main for-each-reflog-ent $m >actual &&
+ test_cmp actual expect
'
test_expect_success "create $m (logged by config)" '
@@ -350,19 +324,34 @@ $A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 +0000 Switch
$B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 +0000
EOF
test_expect_success "verifying $m's log (logged by config)" '
- test_when_finished "rm -f .git/$m .git/logs/$m expect" &&
- test_cmp expect .git/logs/$m
+ test_when_finished "git update-ref -d $m && git reflog expire --expire=all --all && rm -rf actual expect" &&
+ test-tool ref-store main for-each-reflog-ent $m >actual &&
+ test_cmp actual expect
'
test_expect_success 'set up for querying the reflog' '
+ git update-ref -d $m &&
+ test-tool ref-store main delete-reflog $m &&
+
+ GIT_COMMITTER_DATE="1117150320 -0500" git update-ref $m $C &&
+ GIT_COMMITTER_DATE="1117150350 -0500" git update-ref $m $A &&
+ GIT_COMMITTER_DATE="1117150380 -0500" git update-ref $m $B &&
+ GIT_COMMITTER_DATE="1117150680 -0500" git update-ref $m $F &&
+ GIT_COMMITTER_DATE="1117150980 -0500" git update-ref $m $E &&
git update-ref $m $D &&
- cat >.git/logs/$m <<-EOF
+ # Delete the last reflog entry so that the tip of m and the reflog for
+ # it disagree.
+ git reflog delete $m@{0} &&
+
+ cat >expect <<-EOF &&
$Z $C $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150320 -0500
$C $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150350 -0500
$A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 -0500
- $F $Z $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150680 -0500
- $Z $E $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 -0500
+ $B $F $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150680 -0500
+ $F $E $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 -0500
EOF
+ test-tool ref-store main for-each-reflog-ent $m >actual &&
+ test_cmp expect actual
'
ed="Thu, 26 May 2005 18:32:00 -0500"
@@ -410,13 +399,12 @@ test_expect_success 'Query "main@{2005-05-26 23:33:01}" (middle of history with
test_when_finished "rm -f o e" &&
git rev-parse --verify "main@{2005-05-26 23:33:01}" >o 2>e &&
echo "$B" >expect &&
- test_cmp expect o &&
- test_i18ngrep -F "warning: log for ref $m has gap after $gd" e
+ test_cmp expect o
'
test_expect_success 'Query "main@{2005-05-26 23:38:00}" (middle of history)' '
test_when_finished "rm -f o e" &&
git rev-parse --verify "main@{2005-05-26 23:38:00}" >o 2>e &&
- echo "$Z" >expect &&
+ echo "$F" >expect &&
test_cmp expect o &&
test_must_be_empty e
'
@@ -432,10 +420,27 @@ test_expect_success 'Query "main@{2005-05-28}" (past end of history)' '
git rev-parse --verify "main@{2005-05-28}" >o 2>e &&
echo "$D" >expect &&
test_cmp expect o &&
- test_i18ngrep -F "warning: log for ref $m unexpectedly ended on $ld" e
+ test_grep -F "warning: log for ref $m unexpectedly ended on $ld" e
'
-rm -f .git/$m .git/logs/$m expect
+rm -f expect
+git update-ref -d $m
+
+test_expect_success 'query reflog with gap' '
+ test_when_finished "git update-ref -d $m" &&
+
+ GIT_COMMITTER_DATE="1117150320 -0500" git update-ref $m $A &&
+ GIT_COMMITTER_DATE="1117150380 -0500" git update-ref $m $B &&
+ GIT_COMMITTER_DATE="1117150480 -0500" git update-ref $m $C &&
+ GIT_COMMITTER_DATE="1117150580 -0500" git update-ref $m $D &&
+ GIT_COMMITTER_DATE="1117150680 -0500" git update-ref $m $F &&
+ git reflog delete $m@{2} &&
+
+ git rev-parse --verify "main@{2005-05-26 23:33:01}" >actual 2>stderr &&
+ echo "$B" >expect &&
+ test_cmp expect actual &&
+ test_grep -F "warning: log for ref $m has gap after $gd" stderr
+'
test_expect_success 'creating initial files' '
test_when_finished rm -f M &&
@@ -467,7 +472,8 @@ $h_OTHER $h_FIXED $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117151040 +0000 co
$h_FIXED $h_MERGED $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117151100 +0000 commit (merge): Merged initial commit and a later commit.
EOF
test_expect_success 'git commit logged updates' '
- test_cmp expect .git/logs/$m
+ test-tool ref-store main for-each-reflog-ent $m >actual &&
+ test_cmp expect actual
'
unset h_TEST h_OTHER h_FIXED h_MERGED
@@ -486,57 +492,57 @@ test_expect_success 'git cat-file blob main@{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_must_fail git rev-parse PSEUDOREF &&
- test_i18ngrep "unable to resolve reference" err
+ test_grep "unable to resolve reference" err
'
test_expect_success 'create pseudoref' '
git update-ref PSEUDOREF $A &&
- test $A = $(git rev-parse PSEUDOREF)
+ test $A = $(git show-ref -s --verify PSEUDOREF)
'
test_expect_success 'overwrite pseudoref with no old value given' '
git update-ref PSEUDOREF $B &&
- test $B = $(git rev-parse PSEUDOREF)
+ test $B = $(git show-ref -s --verify PSEUDOREF)
'
test_expect_success 'overwrite pseudoref with correct old value' '
git update-ref PSEUDOREF $C $B &&
- test $C = $(git rev-parse PSEUDOREF)
+ test $C = $(git show-ref -s --verify PSEUDOREF)
'
test_expect_success 'do not overwrite pseudoref with wrong old value' '
test_must_fail git update-ref PSEUDOREF $D $E 2>err &&
- test $C = $(git rev-parse PSEUDOREF) &&
- test_i18ngrep "cannot lock ref.*expected" err
+ test $C = $(git show-ref -s --verify PSEUDOREF) &&
+ test_grep "cannot lock ref.*expected" err
'
test_expect_success 'delete pseudoref' '
git update-ref -d PSEUDOREF &&
- test_must_fail git rev-parse PSEUDOREF
+ test_must_fail git show-ref -s --verify PSEUDOREF
'
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 = $(git rev-parse PSEUDOREF) &&
- test_i18ngrep "cannot lock ref.*expected" err
+ test $A = $(git show-ref -s --verify PSEUDOREF) &&
+ test_grep "cannot lock ref.*expected" err
'
test_expect_success 'delete pseudoref with correct old value' '
git update-ref -d PSEUDOREF $A &&
- test_must_fail git rev-parse PSEUDOREF
+ test_must_fail git show-ref -s --verify PSEUDOREF
'
test_expect_success 'create pseudoref with old OID zero' '
git update-ref PSEUDOREF $A $Z &&
- test $A = $(git rev-parse PSEUDOREF)
+ test $A = $(git show-ref -s --verify PSEUDOREF)
'
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 = $(git rev-parse PSEUDOREF) &&
- test_i18ngrep "already exists" err
+ test $A = $(git show-ref -s --verify PSEUDOREF) &&
+ test_grep "already exists" err
'
# Test --stdin
@@ -556,7 +562,7 @@ test_expect_success 'stdin test setup' '
test_expect_success '-z fails without --stdin' '
test_must_fail git update-ref -z $m $m $m 2>err &&
- test_i18ngrep "usage: git update-ref" err
+ test_grep "usage: git update-ref" err
'
test_expect_success 'stdin works with no input' '
@@ -616,7 +622,7 @@ test_expect_success 'stdin fails create with no ref' '
test_expect_success 'stdin fails create with no new value' '
echo "create $a" >stdin &&
test_must_fail git update-ref --stdin <stdin 2>err &&
- grep "fatal: create $a: missing <newvalue>" err
+ grep "fatal: create $a: missing <new-oid>" err
'
test_expect_success 'stdin fails create with too many arguments' '
@@ -634,7 +640,7 @@ test_expect_success 'stdin fails update with no ref' '
test_expect_success 'stdin fails update with no new value' '
echo "update $a" >stdin &&
test_must_fail git update-ref --stdin <stdin 2>err &&
- grep "fatal: update $a: missing <newvalue>" err
+ grep "fatal: update $a: missing <new-oid>" err
'
test_expect_success 'stdin fails update with too many arguments' '
@@ -674,7 +680,7 @@ test_expect_success 'stdin fails with duplicate refs' '
create $a $m
EOF
test_must_fail git update-ref --stdin <stdin 2>err &&
- test_i18ngrep "fatal: multiple updates for ref '"'"'$a'"'"' not allowed" err
+ test_grep "fatal: multiple updates for ref '"'"'$a'"'"' not allowed" err
'
test_expect_success 'stdin create ref works' '
@@ -759,21 +765,21 @@ test_expect_success 'stdin update ref fails with wrong old value' '
test_expect_success 'stdin update ref fails with bad old value' '
echo "update $c $m does-not-exist" >stdin &&
test_must_fail git update-ref --stdin <stdin 2>err &&
- grep "fatal: update $c: invalid <oldvalue>: does-not-exist" err &&
+ grep "fatal: update $c: invalid <old-oid>: does-not-exist" err &&
test_must_fail git rev-parse --verify -q $c
'
test_expect_success 'stdin create ref fails with bad new value' '
echo "create $c does-not-exist" >stdin &&
test_must_fail git update-ref --stdin <stdin 2>err &&
- grep "fatal: create $c: invalid <newvalue>: does-not-exist" err &&
+ grep "fatal: create $c: invalid <new-oid>: does-not-exist" err &&
test_must_fail git rev-parse --verify -q $c
'
test_expect_success 'stdin create ref fails with zero new value' '
echo "create $c " >stdin &&
test_must_fail git update-ref --stdin <stdin 2>err &&
- grep "fatal: create $c: zero <newvalue>" err &&
+ grep "fatal: create $c: zero <new-oid>" err &&
test_must_fail git rev-parse --verify -q $c
'
@@ -797,7 +803,7 @@ test_expect_success 'stdin delete ref fails with wrong old value' '
test_expect_success 'stdin delete ref fails with zero old value' '
echo "delete $a " >stdin &&
test_must_fail git update-ref --stdin <stdin 2>err &&
- grep "fatal: delete $a: zero <oldvalue>" err &&
+ grep "fatal: delete $a: zero <old-oid>" err &&
git rev-parse $m >expect &&
git rev-parse $a >actual &&
test_cmp expect actual
@@ -1021,7 +1027,7 @@ test_expect_success 'stdin -z fails create with no ref' '
test_expect_success 'stdin -z fails create with no new value' '
printf $F "create $a" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: create $a: unexpected end of input when reading <newvalue>" err
+ grep "fatal: create $a: unexpected end of input when reading <new-oid>" err
'
test_expect_success 'stdin -z fails create with too many arguments' '
@@ -1039,27 +1045,27 @@ test_expect_success 'stdin -z fails update with no ref' '
test_expect_success 'stdin -z fails update with too few args' '
printf $F "update $a" "$m" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: update $a: unexpected end of input when reading <oldvalue>" err
+ grep "fatal: update $a: unexpected end of input when reading <old-oid>" err
'
test_expect_success 'stdin -z emits warning with empty new value' '
git update-ref $a $m &&
printf $F "update $a" "" "" >stdin &&
git update-ref -z --stdin <stdin 2>err &&
- grep "warning: update $a: missing <newvalue>, treating as zero" err &&
+ grep "warning: update $a: missing <new-oid>, treating as zero" err &&
test_must_fail git rev-parse --verify -q $a
'
test_expect_success 'stdin -z fails update with no new value' '
printf $F "update $a" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: update $a: unexpected end of input when reading <newvalue>" err
+ grep "fatal: update $a: unexpected end of input when reading <new-oid>" err
'
test_expect_success 'stdin -z fails update with no old value' '
printf $F "update $a" "$m" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: update $a: unexpected end of input when reading <oldvalue>" err
+ grep "fatal: update $a: unexpected end of input when reading <old-oid>" err
'
test_expect_success 'stdin -z fails update with too many arguments' '
@@ -1077,7 +1083,7 @@ test_expect_success 'stdin -z fails delete with no ref' '
test_expect_success 'stdin -z fails delete with no old value' '
printf $F "delete $a" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: delete $a: unexpected end of input when reading <oldvalue>" err
+ grep "fatal: delete $a: unexpected end of input when reading <old-oid>" err
'
test_expect_success 'stdin -z fails delete with too many arguments' '
@@ -1095,7 +1101,7 @@ test_expect_success 'stdin -z fails verify with too many arguments' '
test_expect_success 'stdin -z fails verify with no old value' '
printf $F "verify $a" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: verify $a: unexpected end of input when reading <oldvalue>" err
+ grep "fatal: verify $a: unexpected end of input when reading <old-oid>" err
'
test_expect_success 'stdin -z fails option with unknown name' '
@@ -1107,7 +1113,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 &&
- test_i18ngrep "fatal: multiple updates for ref '"'"'$a'"'"' not allowed" err
+ test_grep "fatal: multiple updates for ref '"'"'$a'"'"' not allowed" err
'
test_expect_success 'stdin -z create ref works' '
@@ -1154,7 +1160,7 @@ test_expect_success 'stdin -z update ref fails with wrong old value' '
test_expect_success 'stdin -z update ref fails with bad old value' '
printf $F "update $c" "$m" "does-not-exist" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: update $c: invalid <oldvalue>: does-not-exist" err &&
+ grep "fatal: update $c: invalid <old-oid>: does-not-exist" err &&
test_must_fail git rev-parse --verify -q $c
'
@@ -1172,14 +1178,14 @@ test_expect_success 'stdin -z create ref fails with bad new value' '
git update-ref -d "$c" &&
printf $F "create $c" "does-not-exist" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: create $c: invalid <newvalue>: does-not-exist" err &&
+ grep "fatal: create $c: invalid <new-oid>: does-not-exist" err &&
test_must_fail git rev-parse --verify -q $c
'
test_expect_success 'stdin -z create ref fails with empty new value' '
printf $F "create $c" "" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: create $c: missing <newvalue>" err &&
+ grep "fatal: create $c: missing <new-oid>" err &&
test_must_fail git rev-parse --verify -q $c
'
@@ -1203,7 +1209,7 @@ test_expect_success 'stdin -z delete ref fails with wrong old value' '
test_expect_success 'stdin -z delete ref fails with zero old value' '
printf $F "delete $a" "$Z" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: delete $a: zero <oldvalue>" err &&
+ grep "fatal: delete $a: zero <old-oid>" err &&
git rev-parse $m >expect &&
git rev-parse $a >actual &&
test_cmp expect actual
@@ -1338,7 +1344,7 @@ test_expect_success 'fails with duplicate HEAD update' '
update HEAD $B
EOF
test_must_fail git update-ref --stdin <stdin 2>err &&
- test_i18ngrep "fatal: multiple updates for '\''HEAD'\'' (including one via its referent .refs/heads/target1.) are not allowed" err &&
+ test_grep "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 &&
@@ -1355,7 +1361,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 &&
- test_i18ngrep "fatal: multiple updates for '\''refs/heads/target2'\'' (including one via symref .refs/heads/symref2.) are not allowed" err &&
+ test_grep "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 &&
@@ -1368,7 +1374,7 @@ test_expect_success ULIMIT_FILE_DESCRIPTORS 'large transaction creating branches
(
for i in $(test_seq 33)
do
- echo "create refs/heads/$i HEAD"
+ echo "create refs/heads/$i HEAD" || exit 1
done >large_input &&
run_with_limited_open_files git update-ref --stdin <large_input &&
git rev-parse --verify -q refs/heads/33
@@ -1379,7 +1385,7 @@ test_expect_success ULIMIT_FILE_DESCRIPTORS 'large transaction deleting branches
(
for i in $(test_seq 33)
do
- echo "delete refs/heads/$i HEAD"
+ echo "delete refs/heads/$i HEAD" || exit 1
done >large_input &&
run_with_limited_open_files git update-ref --stdin <large_input &&
test_must_fail git rev-parse --verify -q refs/heads/33
@@ -1568,6 +1574,7 @@ test_expect_success 'transaction can create and delete' '
EOF
git update-ref --stdin <stdin >actual &&
printf "%s: ok\n" start commit start commit >expect &&
+ test_cmp expect actual &&
test_must_fail git show-ref --verify refs/heads/create-and-delete
'
@@ -1595,16 +1602,43 @@ test_expect_success 'transaction cannot restart ongoing transaction' '
commit
EOF
test_must_fail git update-ref --stdin <stdin >actual &&
+ printf "%s: ok\n" start >expect &&
+ test_cmp expect actual &&
test_must_fail git show-ref --verify refs/heads/restart
'
-test_expect_success 'directory not created deleting packed ref' '
- git branch d1/d2/r1 HEAD &&
- git pack-refs --all &&
- test_path_is_missing .git/refs/heads/d1/d2 &&
- git update-ref -d refs/heads/d1/d2/r1 &&
- test_path_is_missing .git/refs/heads/d1/d2 &&
- test_path_is_missing .git/refs/heads/d1
+test_expect_success PIPE 'transaction flushes status updates' '
+ mkfifo in out &&
+ (git update-ref --stdin <in >out &) &&
+
+ exec 9>in &&
+ exec 8<out &&
+ test_when_finished "exec 9>&-" &&
+ test_when_finished "exec 8<&-" &&
+
+ echo "start" >&9 &&
+ echo "start: ok" >expected &&
+ read line <&8 &&
+ echo "$line" >actual &&
+ test_cmp expected actual &&
+
+ echo "create refs/heads/flush $A" >&9 &&
+
+ echo prepare >&9 &&
+ echo "prepare: ok" >expected &&
+ read line <&8 &&
+ echo "$line" >actual &&
+ test_cmp expected actual &&
+
+ # This must now fail given that we have locked the ref.
+ test_must_fail git update-ref refs/heads/flush $B 2>stderr &&
+ grep "fatal: update_ref failed for ref ${SQ}refs/heads/flush${SQ}: cannot lock ref" stderr &&
+
+ echo commit >&9 &&
+ echo "commit: ok" >expected &&
+ read line <&8 &&
+ echo "$line" >actual &&
+ test_cmp expected actual
'
test_done
diff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh
index 132a1b8..5c60d6f 100755
--- a/t/t1401-symbolic-ref.sh
+++ b/t/t1401-symbolic-ref.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='basic symbolic-ref tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# If the tests munging HEAD fail, they can break detection of
@@ -31,7 +33,8 @@ test_expect_success 'symbolic-ref refuses non-ref for HEAD' '
reset_to_sane
test_expect_success 'symbolic-ref refuses bare sha1' '
- test_must_fail git symbolic-ref HEAD $(git rev-parse HEAD)
+ rev=$(git rev-parse HEAD) &&
+ test_must_fail git symbolic-ref HEAD "$rev"
'
reset_to_sane
@@ -103,9 +106,8 @@ test_expect_success LONG_REF 'we can parse long symbolic ref' '
'
test_expect_success 'symbolic-ref reports failure in exit code' '
- test_when_finished "rm -f .git/HEAD.lock" &&
- >.git/HEAD.lock &&
- test_must_fail git symbolic-ref HEAD refs/heads/whatever
+ # Create d/f conflict to simulate failure.
+ test_must_fail git symbolic-ref refs/heads refs/heads/foo
'
test_expect_success 'symbolic-ref writes reflog entry' '
@@ -163,4 +165,62 @@ test_expect_success 'symbolic-ref can resolve d/f name (ENOTDIR)' '
test_cmp expect actual
'
+test_expect_success 'symbolic-ref refuses invalid target for non-HEAD' '
+ test_must_fail git symbolic-ref refs/heads/invalid foo..bar
+'
+
+test_expect_success 'symbolic-ref allows top-level target for non-HEAD' '
+ git symbolic-ref refs/heads/top-level ORIG_HEAD &&
+ git update-ref ORIG_HEAD HEAD &&
+ test_cmp_rev top-level HEAD
+'
+
+test_expect_success 'symbolic-ref pointing at another' '
+ git update-ref refs/heads/maint-2.37 HEAD &&
+ git symbolic-ref refs/heads/maint refs/heads/maint-2.37 &&
+ git checkout maint &&
+
+ git symbolic-ref HEAD >actual &&
+ echo refs/heads/maint-2.37 >expect &&
+ test_cmp expect actual &&
+
+ git symbolic-ref --no-recurse HEAD >actual &&
+ echo refs/heads/maint >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'symbolic-ref --short handles complex utf8 case' '
+ name="测试-加-增加-加-增加" &&
+ git symbolic-ref TEST_SYMREF "refs/heads/$name" &&
+ # In the real world, we saw problems with this case only
+ # when the locale includes UTF-8. Set it here to try to make things as
+ # hard as possible for us to pass, but in practice we should do the
+ # right thing regardless (and of course some platforms may not even
+ # have this locale).
+ LC_ALL=en_US.UTF-8 git symbolic-ref --short TEST_SYMREF >actual &&
+ echo "$name" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'symbolic-ref --short handles name with suffix' '
+ git symbolic-ref TEST_SYMREF "refs/remotes/origin/HEAD" &&
+ git symbolic-ref --short TEST_SYMREF >actual &&
+ echo "origin" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'symbolic-ref --short handles almost-matching name' '
+ git symbolic-ref TEST_SYMREF "refs/headsXfoo" &&
+ git symbolic-ref --short TEST_SYMREF >actual &&
+ echo "headsXfoo" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'symbolic-ref --short handles name with percent' '
+ git symbolic-ref TEST_SYMREF "refs/heads/%foo" &&
+ git symbolic-ref --short TEST_SYMREF >actual &&
+ echo "%foo" >expect &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t1402-check-ref-format.sh b/t/t1402-check-ref-format.sh
index cabc516..5ed9d73 100755
--- a/t/t1402-check-ref-format.sh
+++ b/t/t1402-check-ref-format.sh
@@ -2,6 +2,7 @@
test_description='Test git check-ref-format'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
valid_ref() {
diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh
index 17d3cc1..33fb7a3 100755
--- a/t/t1403-show-ref.sh
+++ b/t/t1403-show-ref.sh
@@ -4,6 +4,7 @@ test_description='show-ref'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -78,7 +79,7 @@ test_expect_success 'show-ref --verify -q' '
test_expect_success 'show-ref -d' '
{
echo $(git rev-parse refs/tags/A) refs/tags/A &&
- echo $(git rev-parse refs/tags/A^0) "refs/tags/A^{}"
+ echo $(git rev-parse refs/tags/A^0) "refs/tags/A^{}" &&
echo $(git rev-parse refs/tags/C) refs/tags/C
} >expect &&
git show-ref -d A C >actual &&
@@ -123,14 +124,14 @@ test_expect_success 'show-ref -d' '
test_expect_success 'show-ref --heads, --tags, --head, pattern' '
for branch in B main side
do
- echo $(git rev-parse refs/heads/$branch) refs/heads/$branch
+ echo $(git rev-parse refs/heads/$branch) refs/heads/$branch || return 1
done >expect.heads &&
git show-ref --heads >actual &&
test_cmp expect.heads actual &&
for tag in A B C
do
- echo $(git rev-parse refs/tags/$tag) refs/tags/$tag
+ echo $(git rev-parse refs/tags/$tag) refs/tags/$tag || return 1
done >expect.tags &&
git show-ref --tags >actual &&
test_cmp expect.tags actual &&
@@ -148,7 +149,7 @@ test_expect_success 'show-ref --heads, --tags, --head, pattern' '
{
echo $(git rev-parse HEAD) HEAD &&
- echo $(git rev-parse refs/heads/B) refs/heads/B
+ echo $(git rev-parse refs/heads/B) refs/heads/B &&
echo $(git rev-parse refs/tags/B) refs/tags/B
} >expect &&
git show-ref --head B >actual &&
@@ -156,8 +157,8 @@ test_expect_success 'show-ref --heads, --tags, --head, pattern' '
{
echo $(git rev-parse HEAD) HEAD &&
- echo $(git rev-parse refs/heads/B) refs/heads/B
- echo $(git rev-parse refs/tags/B) refs/tags/B
+ echo $(git rev-parse refs/heads/B) refs/heads/B &&
+ echo $(git rev-parse refs/tags/B) refs/tags/B &&
echo $(git rev-parse refs/tags/B^0) "refs/tags/B^{}"
} >expect &&
git show-ref --head -d B >actual &&
@@ -173,6 +174,14 @@ test_expect_success 'show-ref --verify HEAD' '
test_must_be_empty actual
'
+test_expect_success 'show-ref --verify pseudorefs' '
+ git update-ref CHERRY_PICK_HEAD HEAD $ZERO_OID &&
+ test_when_finished "git update-ref -d CHERRY_PICK_HEAD" &&
+ git show-ref -s --verify HEAD >actual &&
+ git show-ref -s --verify CHERRY_PICK_HEAD >expect &&
+ test_cmp actual expect
+'
+
test_expect_success 'show-ref --verify with dangling ref' '
sha1_file() {
echo "$*" | sed "s#..#.git/objects/&/#"
@@ -195,4 +204,86 @@ test_expect_success 'show-ref --verify with dangling ref' '
)
'
+test_expect_success 'show-ref sub-modes are mutually exclusive' '
+ test_must_fail git show-ref --verify --exclude-existing 2>err &&
+ grep "verify" err &&
+ grep "exclude-existing" err &&
+ grep "cannot be used together" err &&
+
+ test_must_fail git show-ref --verify --exists 2>err &&
+ grep "verify" err &&
+ grep "exists" err &&
+ grep "cannot be used together" err &&
+
+ test_must_fail git show-ref --exclude-existing --exists 2>err &&
+ grep "exclude-existing" err &&
+ grep "exists" err &&
+ grep "cannot be used together" err
+'
+
+test_expect_success '--exists with existing reference' '
+ git show-ref --exists refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+'
+
+test_expect_success '--exists with missing reference' '
+ test_expect_code 2 git show-ref --exists refs/heads/does-not-exist
+'
+
+test_expect_success '--exists does not use DWIM' '
+ test_expect_code 2 git show-ref --exists $GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME 2>err &&
+ grep "reference does not exist" err
+'
+
+test_expect_success '--exists with HEAD' '
+ git show-ref --exists HEAD
+'
+
+test_expect_success '--exists with bad reference name' '
+ test_when_finished "git update-ref -d refs/heads/bad...name" &&
+ new_oid=$(git rev-parse HEAD) &&
+ test-tool ref-store main update-ref msg refs/heads/bad...name $new_oid $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+ git show-ref --exists refs/heads/bad...name
+'
+
+test_expect_success '--exists with arbitrary symref' '
+ test_when_finished "git symbolic-ref -d refs/symref" &&
+ git symbolic-ref refs/symref refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME &&
+ git show-ref --exists refs/symref
+'
+
+test_expect_success '--exists with dangling symref' '
+ test_when_finished "git symbolic-ref -d refs/heads/dangling" &&
+ git symbolic-ref refs/heads/dangling refs/heads/does-not-exist &&
+ git show-ref --exists refs/heads/dangling
+'
+
+test_expect_success '--exists with nonexistent object ID' '
+ test-tool ref-store main update-ref msg refs/heads/missing-oid $(test_oid 001) $ZERO_OID REF_SKIP_OID_VERIFICATION &&
+ git show-ref --exists refs/heads/missing-oid
+'
+
+test_expect_success '--exists with non-commit object' '
+ tree_oid=$(git rev-parse HEAD^{tree}) &&
+ test-tool ref-store main update-ref msg refs/heads/tree ${tree_oid} $ZERO_OID REF_SKIP_OID_VERIFICATION &&
+ git show-ref --exists refs/heads/tree
+'
+
+test_expect_success '--exists with directory fails with generic error' '
+ cat >expect <<-EOF &&
+ error: reference does not exist
+ EOF
+ test_expect_code 2 git show-ref --exists refs/heads 2>err &&
+ test_cmp expect err
+'
+
+test_expect_success '--exists with non-existent special ref' '
+ test_expect_code 2 git show-ref --exists FETCH_HEAD
+'
+
+test_expect_success '--exists with existing special ref' '
+ test_when_finished "rm .git/FETCH_HEAD" &&
+ git rev-parse HEAD >.git/FETCH_HEAD &&
+ git show-ref --exists FETCH_HEAD
+'
+
test_done
diff --git a/t/t1404-update-ref-errors.sh b/t/t1404-update-ref-errors.sh
index b729c1f..98e9158 100755
--- a/t/t1404-update-ref-errors.sh
+++ b/t/t1404-update-ref-errors.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='Test git update-ref error handling'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Create some references, perhaps run pack-refs --all, then try to
@@ -27,7 +29,7 @@ test_update_rejected () {
fi &&
printf "create $prefix/%s $C\n" $create >input &&
test_must_fail git update-ref --stdin <input 2>output.err &&
- test_i18ngrep -F "$error" output.err &&
+ test_grep -F "$error" output.err &&
git for-each-ref $prefix >actual &&
test_cmp unchanged actual
}
@@ -90,9 +92,6 @@ df_test() {
else
delname="$delref"
fi &&
- cat >expected-err <<-EOF &&
- fatal: cannot lock ref $SQ$addname$SQ: $SQ$delref$SQ exists; cannot create $SQ$addref$SQ
- EOF
$pack &&
if $add_del
then
@@ -101,7 +100,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 &&
+ grep "fatal:\( cannot lock ref $SQ$addname$SQ:\)\? $SQ$delref$SQ exists; cannot create $SQ$addref$SQ" 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
@@ -189,78 +188,6 @@ test_expect_success 'one new ref is a simple prefix of another' '
'
-test_expect_success REFFILES 'empty directory should not fool rev-parse' '
- prefix=refs/e-rev-parse &&
- git update-ref $prefix/foo $C &&
- git pack-refs --all &&
- mkdir -p .git/$prefix/foo/bar/baz &&
- echo "$C" >expected &&
- git rev-parse $prefix/foo >actual &&
- test_cmp expected actual
-'
-
-test_expect_success REFFILES 'empty directory should not fool for-each-ref' '
- prefix=refs/e-for-each-ref &&
- git update-ref $prefix/foo $C &&
- git for-each-ref $prefix >expected &&
- git pack-refs --all &&
- mkdir -p .git/$prefix/foo/bar/baz &&
- git for-each-ref $prefix >actual &&
- test_cmp expected actual
-'
-
-test_expect_success REFFILES 'empty directory should not fool create' '
- prefix=refs/e-create &&
- mkdir -p .git/$prefix/foo/bar/baz &&
- printf "create %s $C\n" $prefix/foo |
- git update-ref --stdin
-'
-
-test_expect_success REFFILES 'empty directory should not fool verify' '
- prefix=refs/e-verify &&
- git update-ref $prefix/foo $C &&
- git pack-refs --all &&
- mkdir -p .git/$prefix/foo/bar/baz &&
- printf "verify %s $C\n" $prefix/foo |
- git update-ref --stdin
-'
-
-test_expect_success REFFILES 'empty directory should not fool 1-arg update' '
- prefix=refs/e-update-1 &&
- git update-ref $prefix/foo $C &&
- git pack-refs --all &&
- mkdir -p .git/$prefix/foo/bar/baz &&
- printf "update %s $D\n" $prefix/foo |
- git update-ref --stdin
-'
-
-test_expect_success REFFILES 'empty directory should not fool 2-arg update' '
- prefix=refs/e-update-2 &&
- git update-ref $prefix/foo $C &&
- git pack-refs --all &&
- mkdir -p .git/$prefix/foo/bar/baz &&
- printf "update %s $D $C\n" $prefix/foo |
- git update-ref --stdin
-'
-
-test_expect_success REFFILES 'empty directory should not fool 0-arg delete' '
- prefix=refs/e-delete-0 &&
- git update-ref $prefix/foo $C &&
- git pack-refs --all &&
- mkdir -p .git/$prefix/foo/bar/baz &&
- printf "delete %s\n" $prefix/foo |
- git update-ref --stdin
-'
-
-test_expect_success REFFILES 'empty directory should not fool 1-arg delete' '
- prefix=refs/e-delete-1 &&
- git update-ref $prefix/foo $C &&
- git pack-refs --all &&
- mkdir -p .git/$prefix/foo/bar/baz &&
- printf "delete %s $C\n" $prefix/foo |
- git update-ref --stdin
-'
-
test_expect_success 'D/F conflict prevents add long + delete short' '
df_test refs/df-al-ds --add-del foo/bar foo
'
@@ -466,170 +393,4 @@ test_expect_success 'incorrect old value blocks indirect no-deref delete' '
test_cmp expected output.err
'
-test_expect_success REFFILES 'non-empty directory blocks create' '
- prefix=refs/ne-create &&
- mkdir -p .git/$prefix/foo/bar &&
- : >.git/$prefix/foo/bar/baz.lock &&
- test_when_finished "rm -f .git/$prefix/foo/bar/baz.lock" &&
- cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/foo$SQ: there is a non-empty directory $SQ.git/$prefix/foo$SQ blocking reference $SQ$prefix/foo$SQ
- EOF
- printf "%s\n" "update $prefix/foo $C" |
- test_must_fail git update-ref --stdin 2>output.err &&
- test_cmp expected output.err &&
- cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ
- EOF
- printf "%s\n" "update $prefix/foo $D $C" |
- test_must_fail git update-ref --stdin 2>output.err &&
- test_cmp expected output.err
-'
-
-test_expect_success REFFILES 'broken reference blocks create' '
- prefix=refs/broken-create &&
- mkdir -p .git/$prefix &&
- echo "gobbledigook" >.git/$prefix/foo &&
- test_when_finished "rm -f .git/$prefix/foo" &&
- cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
- EOF
- printf "%s\n" "update $prefix/foo $C" |
- test_must_fail git update-ref --stdin 2>output.err &&
- test_cmp expected output.err &&
- cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
- EOF
- printf "%s\n" "update $prefix/foo $D $C" |
- test_must_fail git update-ref --stdin 2>output.err &&
- test_cmp expected output.err
-'
-
-test_expect_success REFFILES 'non-empty directory blocks indirect create' '
- prefix=refs/ne-indirect-create &&
- git symbolic-ref $prefix/symref $prefix/foo &&
- mkdir -p .git/$prefix/foo/bar &&
- : >.git/$prefix/foo/bar/baz.lock &&
- test_when_finished "rm -f .git/$prefix/foo/bar/baz.lock" &&
- cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/symref$SQ: there is a non-empty directory $SQ.git/$prefix/foo$SQ blocking reference $SQ$prefix/foo$SQ
- EOF
- printf "%s\n" "update $prefix/symref $C" |
- test_must_fail git update-ref --stdin 2>output.err &&
- test_cmp expected output.err &&
- cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ
- EOF
- printf "%s\n" "update $prefix/symref $D $C" |
- test_must_fail git update-ref --stdin 2>output.err &&
- test_cmp expected output.err
-'
-
-test_expect_success REFFILES 'broken reference blocks indirect create' '
- prefix=refs/broken-indirect-create &&
- git symbolic-ref $prefix/symref $prefix/foo &&
- echo "gobbledigook" >.git/$prefix/foo &&
- test_when_finished "rm -f .git/$prefix/foo" &&
- cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
- EOF
- printf "%s\n" "update $prefix/symref $C" |
- test_must_fail git update-ref --stdin 2>output.err &&
- test_cmp expected output.err &&
- cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
- EOF
- printf "%s\n" "update $prefix/symref $D $C" |
- test_must_fail git update-ref --stdin 2>output.err &&
- test_cmp expected output.err
-'
-
-test_expect_success REFFILES 'no bogus intermediate values during delete' '
- prefix=refs/slow-transaction &&
- # Set up a reference with differing loose and packed versions:
- git update-ref $prefix/foo $C &&
- git pack-refs --all &&
- git update-ref $prefix/foo $D &&
- git for-each-ref $prefix >unchanged &&
- # Now try to update the reference, but hold the `packed-refs` lock
- # for a while to see what happens while the process is blocked:
- : >.git/packed-refs.lock &&
- test_when_finished "rm -f .git/packed-refs.lock" &&
- {
- # Note: the following command is intentionally run in the
- # background. We increase the timeout so that `update-ref`
- # attempts to acquire the `packed-refs` lock for much longer
- # than it takes for us to do the check then delete it:
- git -c core.packedrefstimeout=30000 update-ref -d $prefix/foo &
- } &&
- pid2=$! &&
- # Give update-ref plenty of time to get to the point where it tries
- # to lock packed-refs:
- sleep 1 &&
- # Make sure that update-ref did not complete despite the lock:
- kill -0 $pid2 &&
- # Verify that the reference still has its old value:
- sha1=$(git rev-parse --verify --quiet $prefix/foo || echo undefined) &&
- case "$sha1" in
- $D)
- # This is what we hope for; it means that nothing
- # user-visible has changed yet.
- : ;;
- undefined)
- # This is not correct; it means the deletion has happened
- # already even though update-ref should not have been
- # able to acquire the lock yet.
- echo "$prefix/foo deleted prematurely" &&
- break
- ;;
- $C)
- # This value should never be seen. Probably the loose
- # reference has been deleted but the packed reference
- # is still there:
- echo "$prefix/foo incorrectly observed to be C" &&
- break
- ;;
- *)
- # WTF?
- echo "unexpected value observed for $prefix/foo: $sha1" &&
- break
- ;;
- esac >out &&
- rm -f .git/packed-refs.lock &&
- wait $pid2 &&
- test_must_be_empty out &&
- test_must_fail git rev-parse --verify --quiet $prefix/foo
-'
-
-test_expect_success REFFILES 'delete fails cleanly if packed-refs file is locked' '
- prefix=refs/locked-packed-refs &&
- # Set up a reference with differing loose and packed versions:
- git update-ref $prefix/foo $C &&
- git pack-refs --all &&
- git update-ref $prefix/foo $D &&
- git for-each-ref $prefix >unchanged &&
- # Now try to delete it while the `packed-refs` lock is held:
- : >.git/packed-refs.lock &&
- test_when_finished "rm -f .git/packed-refs.lock" &&
- test_must_fail git update-ref -d $prefix/foo >out 2>err &&
- git for-each-ref $prefix >actual &&
- test_i18ngrep "Unable to create $SQ.*packed-refs.lock$SQ: " err &&
- test_cmp unchanged actual
-'
-
-test_expect_success REFFILES 'delete fails cleanly if packed-refs.new write fails' '
- # Setup and expectations are similar to the test above.
- prefix=refs/failed-packed-refs &&
- git update-ref $prefix/foo $C &&
- git pack-refs --all &&
- git update-ref $prefix/foo $D &&
- git for-each-ref $prefix >unchanged &&
- # This should not happen in practice, but it is an easy way to get a
- # reliable error (we open with create_tempfile(), which uses O_EXCL).
- : >.git/packed-refs.new &&
- test_when_finished "rm -f .git/packed-refs.new" &&
- test_must_fail git update-ref -d $prefix/foo &&
- git for-each-ref $prefix >actual &&
- test_cmp unchanged actual
-'
-
test_done
diff --git a/t/t1405-main-ref-store.sh b/t/t1405-main-ref-store.sh
index 49718b7..a6bcd62 100755
--- a/t/t1405-main-ref-store.sh
+++ b/t/t1405-main-ref-store.sh
@@ -5,6 +5,7 @@ test_description='test main ref store api'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
RUN="test-tool ref-store main"
@@ -14,15 +15,6 @@ test_expect_success 'setup' '
test_commit one
'
-test_expect_success REFFILES 'pack_refs(PACK_REFS_ALL | PACK_REFS_PRUNE)' '
- N=`find .git/refs -type f | wc -l` &&
- test "$N" != 0 &&
- ALL_OR_PRUNE_FLAG=3 &&
- $RUN pack-refs ${ALL_OR_PRUNE_FLAG} &&
- N=`find .git/refs -type f` &&
- test -z "$N"
-'
-
test_expect_success 'create_symref(FOO, refs/heads/main)' '
$RUN create-symref FOO refs/heads/main nothing &&
echo refs/heads/main >expected &&
@@ -35,8 +27,7 @@ test_expect_success 'delete_refs(FOO, refs/tags/new-tag)' '
git rev-parse FOO -- &&
git rev-parse refs/tags/new-tag -- &&
m=$(git rev-parse main) &&
- REF_NO_DEREF=1 &&
- $RUN delete-refs $REF_NO_DEREF nothing FOO refs/tags/new-tag &&
+ $RUN delete-refs REF_NO_DEREF nothing FOO refs/tags/new-tag &&
test_must_fail git rev-parse --symbolic-full-name FOO &&
test_must_fail git rev-parse FOO -- &&
test_must_fail git rev-parse refs/tags/new-tag --
@@ -77,11 +68,11 @@ test_expect_success 'verify_ref(new-main)' '
'
test_expect_success 'for_each_reflog()' '
- $RUN for-each-reflog | sort -k2 | cut -d" " -f 2- >actual &&
+ $RUN for-each-reflog >actual &&
cat >expected <<-\EOF &&
- HEAD 0x1
- refs/heads/main 0x0
- refs/heads/new-main 0x0
+ HEAD
+ refs/heads/main
+ refs/heads/new-main
EOF
test_cmp expected actual
'
@@ -89,13 +80,13 @@ test_expect_success 'for_each_reflog()' '
test_expect_success 'for_each_reflog_ent()' '
$RUN for-each-reflog-ent HEAD >actual &&
head -n1 actual | grep one &&
- tail -n2 actual | head -n1 | grep recreate-main
+ tail -n1 actual | grep recreate-main
'
test_expect_success 'for_each_reflog_ent_reverse()' '
$RUN for-each-reflog-ent-reverse HEAD >actual &&
head -n1 actual | grep recreate-main &&
- tail -n2 actual | head -n1 | grep one
+ tail -n1 actual | grep one
'
test_expect_success 'reflog_exists(HEAD)' '
@@ -108,7 +99,7 @@ test_expect_success 'delete_reflog(HEAD)' '
'
test_expect_success 'create-reflog(HEAD)' '
- $RUN create-reflog HEAD 1 &&
+ $RUN create-reflog HEAD &&
git reflog exists HEAD
'
diff --git a/t/t1406-submodule-ref-store.sh b/t/t1406-submodule-ref-store.sh
index 0a87058..c01f0f1 100755
--- a/t/t1406-submodule-ref-store.sh
+++ b/t/t1406-submodule-ref-store.sh
@@ -5,6 +5,7 @@ test_description='test submodule ref store api'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
RUN="test-tool ref-store submodule:sub"
@@ -62,11 +63,11 @@ test_expect_success 'verify_ref(new-main)' '
'
test_expect_success 'for_each_reflog()' '
- $RUN for-each-reflog | sort | cut -d" " -f 2- >actual &&
+ $RUN for-each-reflog >actual &&
cat >expected <<-\EOF &&
- HEAD 0x1
- refs/heads/main 0x0
- refs/heads/new-main 0x0
+ HEAD
+ refs/heads/main
+ refs/heads/new-main
EOF
test_cmp expected actual
'
@@ -74,13 +75,13 @@ test_expect_success 'for_each_reflog()' '
test_expect_success 'for_each_reflog_ent()' '
$RUN for-each-reflog-ent HEAD >actual &&
head -n1 actual | grep first &&
- tail -n2 actual | head -n1 | grep main.to.new
+ tail -n1 actual | grep main.to.new
'
test_expect_success 'for_each_reflog_ent_reverse()' '
$RUN for-each-reflog-ent-reverse HEAD >actual &&
head -n1 actual | grep main.to.new &&
- tail -n2 actual | head -n1 | grep first
+ tail -n1 actual | grep first
'
test_expect_success 'reflog_exists(HEAD)' '
@@ -92,7 +93,7 @@ test_expect_success 'delete_reflog() not allowed' '
'
test_expect_success 'create-reflog() not allowed' '
- test_must_fail $RUN create-reflog HEAD 1
+ test_must_fail $RUN create-reflog HEAD
'
test_done
diff --git a/t/t1407-worktree-ref-store.sh b/t/t1407-worktree-ref-store.sh
index ad8006c..48b1c92 100755
--- a/t/t1407-worktree-ref-store.sh
+++ b/t/t1407-worktree-ref-store.sh
@@ -5,6 +5,7 @@ test_description='test worktree ref store api'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
RWT="test-tool ref-store worktree:wt"
@@ -52,41 +53,4 @@ test_expect_success 'create_symref(FOO, refs/heads/main)' '
test_cmp expected actual
'
-# Some refs (refs/bisect/*, pseudorefs) are kept per worktree, so they should
-# only appear in the for-each-reflog output if it is called from the correct
-# worktree, which is exercised in this test. This test is poorly written (and
-# therefore marked REFFILES) for mulitple reasons: 1) it creates invalidly
-# formatted log entres. 2) it uses direct FS access for creating the reflogs. 3)
-# PSEUDO-WT and refs/bisect/random do not create reflogs by default, so it is
-# not testing a realistic scenario.
-test_expect_success REFFILES 'for_each_reflog()' '
- echo $ZERO_OID > .git/logs/PSEUDO-MAIN &&
- mkdir -p .git/logs/refs/bisect &&
- echo $ZERO_OID > .git/logs/refs/bisect/random &&
-
- echo $ZERO_OID > .git/worktrees/wt/logs/PSEUDO-WT &&
- mkdir -p .git/worktrees/wt/logs/refs/bisect &&
- echo $ZERO_OID > .git/worktrees/wt/logs/refs/bisect/wt-random &&
-
- $RWT for-each-reflog | cut -d" " -f 2- | sort >actual &&
- cat >expected <<-\EOF &&
- HEAD 0x1
- PSEUDO-WT 0x0
- refs/bisect/wt-random 0x0
- refs/heads/main 0x0
- refs/heads/wt-main 0x0
- EOF
- test_cmp expected actual &&
-
- $RMAIN for-each-reflog | cut -d" " -f 2- | sort >actual &&
- cat >expected <<-\EOF &&
- HEAD 0x1
- PSEUDO-MAIN 0x0
- refs/bisect/random 0x0
- refs/heads/main 0x0
- refs/heads/wt-main 0x0
- EOF
- test_cmp expected actual
-'
-
test_done
diff --git a/t/t1408-packed-refs.sh b/t/t1408-packed-refs.sh
index 41ba1f1..9469c79 100755
--- a/t/t1408-packed-refs.sh
+++ b/t/t1408-packed-refs.sh
@@ -5,6 +5,7 @@ test_description='packed-refs entries are covered by loose refs'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t1409-avoid-packing-refs.sh b/t/t1409-avoid-packing-refs.sh
index be12fb6..7748973 100755
--- a/t/t1409-avoid-packing-refs.sh
+++ b/t/t1409-avoid-packing-refs.sh
@@ -2,8 +2,15 @@
test_description='avoid rewriting packed-refs unnecessarily'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
+if test_have_prereq !REFFILES
+then
+ skip_all='skipping files-backend specific pack-refs tests'
+ test_done
+fi
+
# Add an identifying mark to the packed-refs file header line. This
# shouldn't upset readers, and it should be omitted if the file is
# ever rewritten.
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
index d42f067..5bf883f 100755
--- a/t/t1410-reflog.sh
+++ b/t/t1410-reflog.sh
@@ -7,6 +7,7 @@ test_description='Test prune and reflog expiration'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
check_have () {
@@ -28,7 +29,7 @@ check_fsck () {
'')
test_must_be_empty fsck.output ;;
*)
- test_i18ngrep "$1" fsck.output ;;
+ test_grep "$1" fsck.output ;;
esac
}
@@ -106,6 +107,28 @@ test_expect_success setup '
test_line_count = 4 output
'
+test_expect_success 'correct usage on sub-command -h' '
+ test_expect_code 129 git reflog expire -h >err &&
+ grep "git reflog expire" err
+'
+
+test_expect_success 'correct usage on "git reflog show -h"' '
+ test_expect_code 129 git reflog show -h >err &&
+ grep -F "git reflog [show]" err
+'
+
+test_expect_success 'pass through -- to sub-command' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_commit -C repo message --a-file contents dash-tag &&
+
+ git -C repo reflog show -- --does-not-exist >out &&
+ test_must_be_empty out &&
+ git -C repo reflog show >expect &&
+ git -C repo reflog show -- --a-file >actual &&
+ test_cmp expect actual
+'
+
test_expect_success rewind '
test_tick && git reset --hard HEAD~2 &&
test -f C &&
@@ -285,9 +308,9 @@ test_expect_success 'git reflog expire unknown reference' '
test_config gc.reflogexpireunreachable never &&
test_must_fail git reflog expire main@{123} 2>stderr &&
- test_i18ngrep "points nowhere" stderr &&
+ test_grep "points nowhere" stderr &&
test_must_fail git reflog expire does-not-exist 2>stderr &&
- test_i18ngrep "points nowhere" stderr
+ test_grep "points nowhere" stderr
'
test_expect_success 'checkout should not delete log for packed ref' '
@@ -331,36 +354,6 @@ test_expect_success 'stale dirs do not cause d/f conflicts (reflogs off)' '
test_must_be_empty actual
'
-# Triggering the bug detected by this test requires a newline to fall
-# exactly BUFSIZ-1 bytes from the end of the file. We don't know
-# what that value is, since it's platform dependent. However, if
-# we choose some value N, we also catch any D which divides N evenly
-# (since we will read backwards in chunks of D). So we choose 8K,
-# which catches glibc (with an 8K BUFSIZ) and *BSD (1K).
-#
-# Each line is 114 characters, so we need 75 to still have a few before the
-# last 8K. The 89-character padding on the final entry lines up our
-# newline exactly.
-test_expect_success SHA1 'parsing reverse reflogs at BUFSIZ boundaries' '
- git checkout -b reflogskip &&
- zf=$(test_oid zero_2) &&
- ident="abc <xyz> 0000000001 +0000" &&
- for i in $(test_seq 1 75); do
- printf "$zf%02d $zf%02d %s\t" $i $(($i+1)) "$ident" &&
- if test $i = 75; then
- for j in $(test_seq 1 89); do
- printf X
- done
- else
- printf X
- fi &&
- printf "\n"
- done >.git/logs/refs/heads/reflogskip &&
- git rev-parse reflogskip@{73} >actual &&
- echo ${zf}03 >expect &&
- test_cmp expect actual
-'
-
test_expect_success 'no segfaults for reflog containing non-commit sha1s' '
git update-ref --create-reflog -m "Creating ref" \
refs/tests/tree-in-reflog HEAD &&
@@ -374,18 +367,6 @@ test_expect_failure 'reflog with non-commit entries displays all entries' '
test_line_count = 3 actual
'
-# This test takes a lock on an individual ref; this is not supported in
-# reftable.
-test_expect_success REFFILES 'reflog expire operates on symref not 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" &&
- touch .git/refs/heads/referrent.lock &&
- git reflog expire --expire=all the_symref
-'
-
test_expect_success 'continue walking past root commits' '
git init orphanage &&
(
@@ -418,7 +399,148 @@ test_expect_success 'expire with multiple worktrees' '
test_commit -C link-wt foobar &&
test_tick &&
git reflog expire --verbose --all --expire=$test_tick &&
- test_must_be_empty .git/worktrees/link-wt/logs/HEAD
+ test-tool ref-store worktree:link-wt for-each-reflog-ent HEAD >actual &&
+ test_must_be_empty actual
+ )
+'
+
+test_expect_success 'expire one of multiple worktrees' '
+ git init main-wt2 &&
+ (
+ cd main-wt2 &&
+ test_tick &&
+ test_commit foo &&
+ git worktree add link-wt &&
+ test_tick &&
+ test_commit -C link-wt foobar &&
+ test_tick &&
+ test-tool ref-store worktree:link-wt for-each-reflog-ent HEAD \
+ >expect-link-wt &&
+ git reflog expire --verbose --all --expire=$test_tick \
+ --single-worktree &&
+ test-tool ref-store worktree:main for-each-reflog-ent HEAD \
+ >actual-main &&
+ test-tool ref-store worktree:link-wt for-each-reflog-ent HEAD \
+ >actual-link-wt &&
+ test_must_be_empty actual-main &&
+ test_cmp expect-link-wt actual-link-wt
+ )
+'
+
+test_expect_success 'empty reflog' '
+ test_when_finished "rm -rf empty" &&
+ git init empty &&
+ test_commit -C empty A &&
+ test-tool ref-store main create-reflog refs/heads/foo &&
+ git -C empty reflog expire --all 2>err &&
+ test_must_be_empty err
+'
+
+test_expect_success 'list reflogs' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ git reflog list >actual &&
+ test_must_be_empty actual &&
+
+ test_commit A &&
+ cat >expect <<-EOF &&
+ HEAD
+ refs/heads/main
+ EOF
+ git reflog list >actual &&
+ test_cmp expect actual &&
+
+ git branch b &&
+ cat >expect <<-EOF &&
+ HEAD
+ refs/heads/b
+ refs/heads/main
+ EOF
+ git reflog list >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'list reflogs with worktree' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ test_commit A &&
+ git worktree add wt &&
+ git -c core.logAllRefUpdates=always \
+ update-ref refs/worktree/main HEAD &&
+ git -c core.logAllRefUpdates=always \
+ update-ref refs/worktree/per-worktree HEAD &&
+ git -c core.logAllRefUpdates=always -C wt \
+ update-ref refs/worktree/per-worktree HEAD &&
+ git -c core.logAllRefUpdates=always -C wt \
+ update-ref refs/worktree/worktree HEAD &&
+
+ cat >expect <<-EOF &&
+ HEAD
+ refs/heads/main
+ refs/heads/wt
+ refs/worktree/main
+ refs/worktree/per-worktree
+ EOF
+ git reflog list >actual &&
+ test_cmp expect actual &&
+
+ cat >expect <<-EOF &&
+ HEAD
+ refs/heads/main
+ refs/heads/wt
+ refs/worktree/per-worktree
+ refs/worktree/worktree
+ EOF
+ git -C wt reflog list >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'reflog list returns error with additional args' '
+ cat >expect <<-EOF &&
+ error: list does not accept arguments: ${SQ}bogus${SQ}
+ EOF
+ test_must_fail git reflog list bogus 2>err &&
+ test_cmp expect err
+'
+
+test_expect_success 'reflog for symref with unborn target can be listed' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ git symbolic-ref HEAD refs/heads/unborn &&
+ cat >expect <<-EOF &&
+ HEAD
+ refs/heads/main
+ EOF
+ git reflog list >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'reflog with invalid object ID can be listed' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ test-tool ref-store main update-ref msg refs/heads/missing \
+ $(test_oid deadbeef) "$ZERO_OID" REF_SKIP_OID_VERIFICATION &&
+ cat >expect <<-EOF &&
+ HEAD
+ refs/heads/main
+ refs/heads/missing
+ EOF
+ git reflog list >actual &&
+ test_cmp expect actual
)
'
diff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh
index 0bb319b..da581ec 100755
--- a/t/t1411-reflog-show.sh
+++ b/t/t1411-reflog-show.sh
@@ -4,6 +4,7 @@ test_description='Test reflog display routines'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -169,9 +170,4 @@ test_expect_success 'git log -g -p shows diffs vs. parents' '
test_cmp expect actual
'
-test_expect_success 'reflog exists works' '
- git reflog exists refs/heads/main &&
- ! git reflog exists refs/heads/nonexistent
-'
-
test_done
diff --git a/t/t1412-reflog-loop.sh b/t/t1412-reflog-loop.sh
index 977603f..ff30874 100755
--- a/t/t1412-reflog-loop.sh
+++ b/t/t1412-reflog-loop.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='reflog walk shows repeated commits again'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup commits' '
diff --git a/t/t1413-reflog-detach.sh b/t/t1413-reflog-detach.sh
index 934688a..d2a4822 100755
--- a/t/t1413-reflog-detach.sh
+++ b/t/t1413-reflog-detach.sh
@@ -4,6 +4,7 @@ test_description='Test reflog interaction with detached HEAD'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
reset_state () {
diff --git a/t/t1414-reflog-walk.sh b/t/t1414-reflog-walk.sh
index ea64cec..be6c3f4 100755
--- a/t/t1414-reflog-walk.sh
+++ b/t/t1414-reflog-walk.sh
@@ -121,13 +121,12 @@ test_expect_success 'min/max age uses entry date to limit' '
# Create a situation where the reflog and ref database disagree about the latest
# state of HEAD.
-test_expect_success REFFILES 'walk prefers reflog to ref tip' '
+test_expect_success 'walk prefers reflog to ref tip' '
+ test_commit A &&
+ test_commit B &&
+ git reflog delete HEAD@{0} &&
head=$(git rev-parse HEAD) &&
- one=$(git rev-parse one) &&
- ident="$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE" &&
- echo "$head $one $ident broken reflog entry" >>.git/logs/HEAD &&
-
- echo $one >expect &&
+ git rev-parse A >expect &&
git log -g --format=%H -1 >actual &&
test_cmp expect actual
'
diff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh
index a3e6ea0..eb4eec8 100755
--- a/t/t1415-worktree-refs.sh
+++ b/t/t1415-worktree-refs.sh
@@ -2,6 +2,7 @@
test_description='per-worktree refs'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -16,17 +17,6 @@ test_expect_success 'setup' '
git -C wt2 update-ref refs/worktree/foo HEAD
'
-# The 'packed-refs' file is stored directly in .git/. This means it is global
-# to the repository, and can only contain refs that are shared across all
-# worktrees.
-test_expect_success REFFILES 'refs/worktree must not be packed' '
- git pack-refs --all &&
- test_path_is_missing .git/refs/tags/wt1 &&
- test_path_is_file .git/refs/worktree/foo &&
- test_path_is_file .git/worktrees/wt1/refs/worktree/foo &&
- test_path_is_file .git/worktrees/wt2/refs/worktree/foo
-'
-
test_expect_success 'refs/worktree are per-worktree' '
test_cmp_rev worktree/foo initial &&
( cd wt1 && test_cmp_rev worktree/foo wt1 ) &&
diff --git a/t/t1416-ref-transaction-hooks.sh b/t/t1416-ref-transaction-hooks.sh
index 6c94102..2092488 100755
--- a/t/t1416-ref-transaction-hooks.sh
+++ b/t/t1416-ref-transaction-hooks.sh
@@ -5,10 +5,10 @@ test_description='reference transaction hooks'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
- mkdir -p .git/hooks &&
test_commit PRE &&
PRE_OID=$(git rev-parse PRE) &&
test_commit POST &&
@@ -16,9 +16,8 @@ test_expect_success setup '
'
test_expect_success 'hook allows updating ref if successful' '
- test_when_finished "rm .git/hooks/reference-transaction" &&
git reset --hard PRE &&
- write_script .git/hooks/reference-transaction <<-\EOF &&
+ test_hook reference-transaction <<-\EOF &&
echo "$*" >>actual
EOF
cat >expect <<-EOF &&
@@ -30,22 +29,21 @@ test_expect_success 'hook allows updating ref if successful' '
'
test_expect_success 'hook aborts updating ref in prepared state' '
- test_when_finished "rm .git/hooks/reference-transaction" &&
git reset --hard PRE &&
- write_script .git/hooks/reference-transaction <<-\EOF &&
+ test_hook reference-transaction <<-\EOF &&
if test "$1" = prepared
then
exit 1
fi
EOF
test_must_fail git update-ref HEAD POST 2>err &&
- test_i18ngrep "ref updates aborted by hook" err
+ test_grep "ref updates aborted by hook" err
'
test_expect_success 'hook gets all queued updates in prepared state' '
- test_when_finished "rm .git/hooks/reference-transaction actual" &&
+ test_when_finished "rm actual" &&
git reset --hard PRE &&
- write_script .git/hooks/reference-transaction <<-\EOF &&
+ test_hook reference-transaction <<-\EOF &&
if test "$1" = prepared
then
while read -r line
@@ -66,9 +64,9 @@ test_expect_success 'hook gets all queued updates in prepared state' '
'
test_expect_success 'hook gets all queued updates in committed state' '
- test_when_finished "rm .git/hooks/reference-transaction actual" &&
+ test_when_finished "rm actual" &&
git reset --hard PRE &&
- write_script .git/hooks/reference-transaction <<-\EOF &&
+ test_hook reference-transaction <<-\EOF &&
if test "$1" = committed
then
while read -r line
@@ -86,9 +84,9 @@ test_expect_success 'hook gets all queued updates in committed state' '
'
test_expect_success 'hook gets all queued updates in aborted state' '
- test_when_finished "rm .git/hooks/reference-transaction actual" &&
+ test_when_finished "rm actual" &&
git reset --hard PRE &&
- write_script .git/hooks/reference-transaction <<-\EOF &&
+ test_hook reference-transaction <<-\EOF &&
if test "$1" = aborted
then
while read -r line
@@ -115,11 +113,11 @@ test_expect_success 'interleaving hook calls succeed' '
git init --bare target-repo.git &&
- write_script target-repo.git/hooks/reference-transaction <<-\EOF &&
+ test_hook -C target-repo.git reference-transaction <<-\EOF &&
echo $0 "$@" >>actual
EOF
- write_script target-repo.git/hooks/update <<-\EOF &&
+ test_hook -C target-repo.git update <<-\EOF &&
echo $0 "$@" >>actual
EOF
diff --git a/t/t1417-reflog-updateref.sh b/t/t1417-reflog-updateref.sh
new file mode 100755
index 0000000..0eb5e67
--- /dev/null
+++ b/t/t1417-reflog-updateref.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+test_description='git reflog --updateref'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ git init -b main repo &&
+ (
+ cd repo &&
+
+ test_commit A &&
+ test_commit B &&
+ test_commit C &&
+
+ git reflog HEAD >expect &&
+ git reset --hard HEAD~ &&
+ # Make sure that the reflog does not point to the same commit
+ # as HEAD.
+ git reflog delete HEAD@{0} &&
+ git reflog HEAD >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_reflog_updateref () {
+ exp=$1
+ shift
+ args="$@"
+
+ test_expect_success "get '$exp' with '$args'" '
+ test_when_finished "rm -rf copy" &&
+ cp -R repo copy &&
+
+ (
+ cd copy &&
+
+ $args &&
+ git rev-parse $exp >expect &&
+ git rev-parse HEAD >actual &&
+
+ test_cmp expect actual
+ )
+ '
+}
+
+test_reflog_updateref B git reflog delete --updateref HEAD@{0}
+test_reflog_updateref B git reflog delete --updateref HEAD@{1}
+test_reflog_updateref C git reflog delete --updateref main@{0}
+test_reflog_updateref B git reflog delete --updateref main@{1}
+test_reflog_updateref B git reflog delete --updateref --rewrite HEAD@{0}
+test_reflog_updateref B git reflog delete --updateref --rewrite HEAD@{1}
+test_reflog_updateref C git reflog delete --updateref --rewrite main@{0}
+test_reflog_updateref B git reflog delete --updateref --rewrite main@{1}
+test_reflog_updateref B test_must_fail git reflog expire HEAD@{0}
+test_reflog_updateref B test_must_fail git reflog expire HEAD@{1}
+test_reflog_updateref B test_must_fail git reflog expire main@{0}
+test_reflog_updateref B test_must_fail git reflog expire main@{1}
+test_reflog_updateref B test_must_fail git reflog expire --updateref HEAD@{0}
+test_reflog_updateref B test_must_fail git reflog expire --updateref HEAD@{1}
+test_reflog_updateref B test_must_fail git reflog expire --updateref main@{0}
+test_reflog_updateref B test_must_fail git reflog expire --updateref main@{1}
+test_reflog_updateref B test_must_fail git reflog expire --updateref --rewrite HEAD@{0}
+test_reflog_updateref B test_must_fail git reflog expire --updateref --rewrite HEAD@{1}
+test_reflog_updateref B test_must_fail git reflog expire --updateref --rewrite main@{0}
+test_reflog_updateref B test_must_fail git reflog expire --updateref --rewrite main@{1}
+
+test_done
diff --git a/t/t1418-reflog-exists.sh b/t/t1418-reflog-exists.sh
new file mode 100755
index 0000000..2268bca
--- /dev/null
+++ b/t/t1418-reflog-exists.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+test_description='Test reflog display routines'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit A
+'
+
+test_expect_success 'usage' '
+ test_expect_code 129 git reflog exists &&
+ test_expect_code 129 git reflog exists -h
+'
+
+test_expect_success 'usage: unknown option' '
+ test_expect_code 129 git reflog exists --unknown-option
+'
+
+test_expect_success 'reflog exists works' '
+ git reflog exists refs/heads/main &&
+ test_must_fail git reflog exists refs/heads/nonexistent
+'
+
+test_expect_success 'reflog exists works with a "--" delimiter' '
+ git reflog exists -- refs/heads/main &&
+ test_must_fail git reflog exists -- refs/heads/nonexistent
+'
+
+test_expect_success 'reflog exists works with a "--end-of-options" delimiter' '
+ git reflog exists --end-of-options refs/heads/main &&
+ test_must_fail git reflog exists --end-of-options refs/heads/nonexistent
+'
+
+test_done
diff --git a/t/t1419-exclude-refs.sh b/t/t1419-exclude-refs.sh
new file mode 100755
index 0000000..1359574
--- /dev/null
+++ b/t/t1419-exclude-refs.sh
@@ -0,0 +1,128 @@
+#!/bin/sh
+
+test_description='test exclude_patterns functionality in main ref store'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+if test_have_prereq !REFFILES
+then
+ skip_all='skipping `git for-each-ref --exclude` tests; need files backend'
+ test_done
+fi
+
+for_each_ref__exclude () {
+ GIT_TRACE2_PERF=1 test-tool ref-store main \
+ for-each-ref--exclude "$@" >actual.raw
+ cut -d ' ' -f 2 actual.raw
+}
+
+for_each_ref () {
+ git for-each-ref --format='%(refname)' "$@"
+}
+
+assert_jumps () {
+ local nr="$1"
+ local trace="$2"
+
+ grep -q "name:jumps_made value:$nr$" $trace
+}
+
+assert_no_jumps () {
+ ! assert_jumps ".*" "$1"
+}
+
+test_expect_success 'setup' '
+ test_commit --no-tag base &&
+ base="$(git rev-parse HEAD)" &&
+
+ for name in foo bar baz quux
+ do
+ for i in 1 2 3
+ do
+ echo "create refs/heads/$name/$i $base" || return 1
+ done || return 1
+ done >in &&
+ echo "delete refs/heads/main" >>in &&
+
+ git update-ref --stdin <in &&
+ git pack-refs --all
+'
+
+test_expect_success 'excluded region in middle' '
+ for_each_ref__exclude refs/heads refs/heads/foo >actual 2>perf &&
+ for_each_ref refs/heads/bar refs/heads/baz refs/heads/quux >expect &&
+
+ test_cmp expect actual &&
+ assert_jumps 1 perf
+'
+
+test_expect_success 'excluded region at beginning' '
+ for_each_ref__exclude refs/heads refs/heads/bar >actual 2>perf &&
+ for_each_ref refs/heads/baz refs/heads/foo refs/heads/quux >expect &&
+
+ test_cmp expect actual &&
+ assert_jumps 1 perf
+'
+
+test_expect_success 'excluded region at end' '
+ for_each_ref__exclude refs/heads refs/heads/quux >actual 2>perf &&
+ for_each_ref refs/heads/foo refs/heads/bar refs/heads/baz >expect &&
+
+ test_cmp expect actual &&
+ assert_jumps 1 perf
+'
+
+test_expect_success 'disjoint excluded regions' '
+ for_each_ref__exclude refs/heads refs/heads/bar refs/heads/quux >actual 2>perf &&
+ for_each_ref refs/heads/baz refs/heads/foo >expect &&
+
+ test_cmp expect actual &&
+ assert_jumps 2 perf
+'
+
+test_expect_success 'adjacent, non-overlapping excluded regions' '
+ for_each_ref__exclude refs/heads refs/heads/bar refs/heads/baz >actual 2>perf &&
+ for_each_ref refs/heads/foo refs/heads/quux >expect &&
+
+ test_cmp expect actual &&
+ assert_jumps 1 perf
+'
+
+test_expect_success 'overlapping excluded regions' '
+ for_each_ref__exclude refs/heads refs/heads/ba refs/heads/baz >actual 2>perf &&
+ for_each_ref refs/heads/foo refs/heads/quux >expect &&
+
+ test_cmp expect actual &&
+ assert_jumps 1 perf
+'
+
+test_expect_success 'several overlapping excluded regions' '
+ for_each_ref__exclude refs/heads \
+ refs/heads/bar refs/heads/baz refs/heads/foo >actual 2>perf &&
+ for_each_ref refs/heads/quux >expect &&
+
+ test_cmp expect actual &&
+ assert_jumps 1 perf
+'
+
+test_expect_success 'non-matching excluded section' '
+ for_each_ref__exclude refs/heads refs/heads/does/not/exist >actual 2>perf &&
+ for_each_ref >expect &&
+
+ test_cmp expect actual &&
+ assert_no_jumps perf
+'
+
+test_expect_success 'meta-characters are discarded' '
+ for_each_ref__exclude refs/heads "refs/heads/ba*" >actual 2>perf &&
+ for_each_ref >expect &&
+
+ test_cmp expect actual &&
+ assert_no_jumps perf
+'
+
+test_done
diff --git a/t/t1420-lost-found.sh b/t/t1420-lost-found.sh
index dc9e402..dbe15a0 100755
--- a/t/t1420-lost-found.sh
+++ b/t/t1420-lost-found.sh
@@ -4,6 +4,8 @@
#
test_description='Test fsck --lost-found'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh
index b1839e0..0c00118 100755
--- a/t/t1430-bad-ref-name.sh
+++ b/t/t1430-bad-ref-name.sh
@@ -4,11 +4,13 @@ test_description='Test handling of ref names that check-ref-format rejects'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
test_commit one &&
- test_commit two
+ test_commit two &&
+ main_sha1=$(git rev-parse refs/heads/main)
'
test_expect_success 'fast-import: fail on invalid branch name ".badbranchname"' '
@@ -42,16 +44,16 @@ test_expect_success 'fast-import: fail on invalid branch name "bad[branch]name"'
'
test_expect_success 'git branch shows badly named ref as warning' '
- cp .git/refs/heads/main .git/refs/heads/broken...ref &&
- test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
git branch >output 2>error &&
- test_i18ngrep -e "ignoring ref with broken name refs/heads/broken\.\.\.ref" error &&
+ test_grep -e "ignoring ref with broken name refs/heads/broken\.\.\.ref" error &&
! grep -e "broken\.\.\.ref" output
'
test_expect_success 'branch -d can delete badly named ref' '
- cp .git/refs/heads/main .git/refs/heads/broken...ref &&
- test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
git branch -d broken...ref &&
git branch >output 2>error &&
! grep -e "broken\.\.\.ref" error &&
@@ -59,8 +61,8 @@ test_expect_success 'branch -d can delete badly named ref' '
'
test_expect_success 'branch -D can delete badly named ref' '
- cp .git/refs/heads/main .git/refs/heads/broken...ref &&
- test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
git branch -D broken...ref &&
git branch >output 2>error &&
! grep -e "broken\.\.\.ref" error &&
@@ -89,7 +91,7 @@ test_expect_success 'branch -D cannot delete absolute path' '
'
test_expect_success 'git branch cannot create a badly named ref' '
- test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
test_must_fail git branch broken...ref &&
git branch >output 2>error &&
! grep -e "broken\.\.\.ref" error &&
@@ -97,7 +99,7 @@ test_expect_success 'git branch cannot create a badly named ref' '
'
test_expect_success 'branch -m cannot rename to a bad ref name' '
- test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
test_might_fail git branch -D goodref &&
git branch goodref &&
test_must_fail git branch -m goodref broken...ref &&
@@ -108,8 +110,9 @@ test_expect_success 'branch -m cannot rename to a bad ref name' '
'
test_expect_failure 'branch -m can rename from a bad ref name' '
- cp .git/refs/heads/main .git/refs/heads/broken...ref &&
- test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
git branch -m broken...ref renamed &&
test_cmp_rev main renamed &&
git branch >output 2>error &&
@@ -118,7 +121,7 @@ test_expect_failure 'branch -m can rename from a bad ref name' '
'
test_expect_success 'push cannot create a badly named ref' '
- test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
test_must_fail git push "file://$(pwd)" HEAD:refs/heads/broken...ref &&
git branch >output 2>error &&
! grep -e "broken\.\.\.ref" error &&
@@ -138,7 +141,7 @@ test_expect_failure 'push --mirror can delete badly named ref' '
cd dest &&
test_commit two &&
git checkout --detach &&
- cp .git/refs/heads/main .git/refs/heads/broken...ref
+ test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION
) &&
git -C src push --mirror "file://$top/dest" &&
git -C dest branch >output 2>error &&
@@ -147,36 +150,36 @@ test_expect_failure 'push --mirror can delete badly named ref' '
'
test_expect_success 'rev-parse skips symref pointing to broken name' '
- test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
git branch shadow one &&
- cp .git/refs/heads/main .git/refs/heads/broken...ref &&
- printf "ref: refs/heads/broken...ref\n" >.git/refs/tags/shadow &&
- test_when_finished "rm -f .git/refs/tags/shadow" &&
+ test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+ test-tool ref-store main create-symref refs/tags/shadow refs/heads/broken...ref msg &&
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/tags/shadow" &&
git rev-parse --verify one >expect &&
git rev-parse --verify shadow >actual 2>err &&
test_cmp expect actual &&
- test_i18ngrep "ignoring dangling symref refs/tags/shadow" err
+ test_grep "ignoring dangling symref refs/tags/shadow" err
'
test_expect_success 'for-each-ref emits warnings for broken names' '
- cp .git/refs/heads/main .git/refs/heads/broken...ref &&
- test_when_finished "rm -f .git/refs/heads/broken...ref" &&
- printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname &&
- test_when_finished "rm -f .git/refs/heads/badname" &&
- printf "ref: refs/heads/main\n" >.git/refs/heads/broken...symref &&
- test_when_finished "rm -f .git/refs/heads/broken...symref" &&
+ test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
+ test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref &&
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
+ test-tool ref-store main create-symref refs/heads/broken...symref refs/heads/main &&
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
git for-each-ref >output 2>error &&
! grep -e "broken\.\.\.ref" output &&
! grep -e "badname" output &&
! grep -e "broken\.\.\.symref" output &&
- test_i18ngrep "ignoring ref with broken name refs/heads/broken\.\.\.ref" error &&
- test_i18ngrep "ignoring broken ref refs/heads/badname" error &&
- test_i18ngrep "ignoring ref with broken name refs/heads/broken\.\.\.symref" error
+ test_grep "ignoring ref with broken name refs/heads/broken\.\.\.ref" error &&
+ test_grep ! "ignoring broken ref refs/heads/badname" error &&
+ test_grep "ignoring ref with broken name refs/heads/broken\.\.\.symref" error
'
test_expect_success 'update-ref -d can delete broken name' '
- cp .git/refs/heads/main .git/refs/heads/broken...ref &&
- test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
git update-ref -d refs/heads/broken...ref >output 2>error &&
test_must_be_empty output &&
test_must_be_empty error &&
@@ -186,10 +189,10 @@ test_expect_success 'update-ref -d can delete broken name' '
'
test_expect_success 'branch -d can delete broken name' '
- cp .git/refs/heads/main .git/refs/heads/broken...ref &&
- test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
git branch -d broken...ref >output 2>error &&
- test_i18ngrep "Deleted branch broken...ref (was broken)" output &&
+ test_grep "Deleted branch broken...ref (was broken)" output &&
test_must_be_empty error &&
git branch >output 2>error &&
! grep -e "broken\.\.\.ref" error &&
@@ -197,89 +200,99 @@ test_expect_success 'branch -d can delete broken name' '
'
test_expect_success 'update-ref --no-deref -d can delete symref to broken name' '
- cp .git/refs/heads/main .git/refs/heads/broken...ref &&
- test_when_finished "rm -f .git/refs/heads/broken...ref" &&
- printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname &&
- test_when_finished "rm -f .git/refs/heads/badname" &&
+ test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
+ test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
+ test_ref_exists refs/heads/badname &&
git update-ref --no-deref -d refs/heads/badname >output 2>error &&
- test_path_is_missing .git/refs/heads/badname &&
+ test_ref_missing refs/heads/badname &&
test_must_be_empty output &&
test_must_be_empty error
'
test_expect_success 'branch -d can delete symref to broken name' '
- cp .git/refs/heads/main .git/refs/heads/broken...ref &&
- test_when_finished "rm -f .git/refs/heads/broken...ref" &&
- printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname &&
- test_when_finished "rm -f .git/refs/heads/badname" &&
+ test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
+ test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
+ test_ref_exists refs/heads/badname &&
git branch -d badname >output 2>error &&
- test_path_is_missing .git/refs/heads/badname &&
- test_i18ngrep "Deleted branch badname (was refs/heads/broken\.\.\.ref)" output &&
+ test_ref_missing refs/heads/badname &&
+ test_grep "Deleted branch badname (was refs/heads/broken\.\.\.ref)" output &&
test_must_be_empty error
'
test_expect_success 'update-ref --no-deref -d can delete dangling symref to broken name' '
- printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname &&
- test_when_finished "rm -f .git/refs/heads/badname" &&
+ test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
+ test_ref_exists refs/heads/badname &&
git update-ref --no-deref -d refs/heads/badname >output 2>error &&
- test_path_is_missing .git/refs/heads/badname &&
+ test_ref_missing refs/heads/badname &&
test_must_be_empty output &&
test_must_be_empty error
'
test_expect_success 'branch -d can delete dangling symref to broken name' '
- printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname &&
- test_when_finished "rm -f .git/refs/heads/badname" &&
+ test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
+ test_ref_exists refs/heads/badname &&
git branch -d badname >output 2>error &&
- test_path_is_missing .git/refs/heads/badname &&
- test_i18ngrep "Deleted branch badname (was refs/heads/broken\.\.\.ref)" output &&
+ test_ref_missing refs/heads/badname &&
+ test_grep "Deleted branch badname (was refs/heads/broken\.\.\.ref)" output &&
test_must_be_empty error
'
test_expect_success 'update-ref -d can delete broken name through symref' '
- cp .git/refs/heads/main .git/refs/heads/broken...ref &&
- test_when_finished "rm -f .git/refs/heads/broken...ref" &&
- printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname &&
- test_when_finished "rm -f .git/refs/heads/badname" &&
+ test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
+ test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
+ test_ref_exists refs/heads/broken...ref &&
git update-ref -d refs/heads/badname >output 2>error &&
- test_path_is_missing .git/refs/heads/broken...ref &&
+ test_ref_missing refs/heads/broken...ref &&
test_must_be_empty output &&
test_must_be_empty error
'
test_expect_success 'update-ref --no-deref -d can delete symref with broken name' '
- printf "ref: refs/heads/main\n" >.git/refs/heads/broken...symref &&
- test_when_finished "rm -f .git/refs/heads/broken...symref" &&
+ test-tool ref-store main create-symref refs/heads/broken...symref refs/heads/main &&
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
+ test_ref_exists refs/heads/broken...symref &&
git update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&
- test_path_is_missing .git/refs/heads/broken...symref &&
+ test_ref_missing refs/heads/broken...symref &&
test_must_be_empty output &&
test_must_be_empty error
'
test_expect_success 'branch -d can delete symref with broken name' '
- printf "ref: refs/heads/main\n" >.git/refs/heads/broken...symref &&
- test_when_finished "rm -f .git/refs/heads/broken...symref" &&
+ test-tool ref-store main create-symref refs/heads/broken...symref refs/heads/main &&
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
+ test_ref_exists refs/heads/broken...symref &&
git branch -d broken...symref >output 2>error &&
- test_path_is_missing .git/refs/heads/broken...symref &&
- test_i18ngrep "Deleted branch broken...symref (was refs/heads/main)" output &&
+ test_ref_missing refs/heads/broken...symref &&
+ test_grep "Deleted branch broken...symref (was refs/heads/main)" output &&
test_must_be_empty error
'
test_expect_success 'update-ref --no-deref -d can delete dangling symref with broken name' '
- printf "ref: refs/heads/idonotexist\n" >.git/refs/heads/broken...symref &&
- test_when_finished "rm -f .git/refs/heads/broken...symref" &&
+ test-tool ref-store main create-symref refs/heads/broken...symref refs/heads/idonotexist &&
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
+ test_ref_exists refs/heads/broken...symref &&
git update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&
- test_path_is_missing .git/refs/heads/broken...symref &&
+ test_ref_missing refs/heads/broken...symref &&
test_must_be_empty output &&
test_must_be_empty error
'
test_expect_success 'branch -d can delete dangling symref with broken name' '
- printf "ref: refs/heads/idonotexist\n" >.git/refs/heads/broken...symref &&
- test_when_finished "rm -f .git/refs/heads/broken...symref" &&
+ test-tool ref-store main create-symref refs/heads/broken...symref refs/heads/idonotexist &&
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
+ test_ref_exists refs/heads/broken...symref &&
git branch -d broken...symref >output 2>error &&
- test_path_is_missing .git/refs/heads/broken...symref &&
- test_i18ngrep "Deleted branch broken...symref (was refs/heads/idonotexist)" output &&
+ test_ref_missing refs/heads/broken...symref &&
+ test_grep "Deleted branch broken...symref (was refs/heads/idonotexist)" output &&
test_must_be_empty error
'
@@ -288,7 +301,7 @@ test_expect_success 'update-ref -d cannot delete non-ref in .git dir' '
echo precious >expect &&
test_must_fail git update-ref -d my-private-file >output 2>error &&
test_must_be_empty output &&
- test_i18ngrep -e "refusing to update ref with bad name" error &&
+ test_grep -e "refusing to update ref with bad name" error &&
test_cmp expect .git/my-private-file
'
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index 5071ac6..8a456b1 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -15,6 +15,7 @@ test_expect_success setup '
git config --unset i18n.commitencoding &&
git checkout HEAD^0 &&
test_commit B fileB two &&
+ orig_head=$(git rev-parse HEAD) &&
git tag -d A B &&
git reflog expire --expire=now --all
'
@@ -48,84 +49,129 @@ remove_object () {
rm "$(sha1_file "$1")"
}
-test_expect_success 'object with bad sha1' '
- sha=$(echo blob | git hash-object -w --stdin) &&
- old=$(test_oid_to_path "$sha") &&
- new=$(dirname $old)/$(test_oid ff_2) &&
- sha="$(dirname $new)$(basename $new)" &&
- mv .git/objects/$old .git/objects/$new &&
- test_when_finished "remove_object $sha" &&
- git update-index --add --cacheinfo 100644 $sha foo &&
- test_when_finished "git read-tree -u --reset HEAD" &&
- tree=$(git write-tree) &&
- test_when_finished "remove_object $tree" &&
- cmt=$(echo bogus | git commit-tree $tree) &&
- test_when_finished "remove_object $cmt" &&
- git update-ref refs/heads/bogus $cmt &&
- test_when_finished "git update-ref -d refs/heads/bogus" &&
+test_expect_success 'object with hash mismatch' '
+ git init --bare hash-mismatch &&
+ (
+ cd hash-mismatch &&
- test_must_fail git fsck 2>out &&
- test_i18ngrep "$sha.*corrupt" out
+ oid=$(echo blob | git hash-object -w --stdin) &&
+ oldoid=$oid &&
+ old=$(test_oid_to_path "$oid") &&
+ new=$(dirname $old)/$(test_oid ff_2) &&
+ oid="$(dirname $new)$(basename $new)" &&
+
+ mv objects/$old objects/$new &&
+ git update-index --add --cacheinfo 100644 $oid foo &&
+ tree=$(git write-tree) &&
+ cmt=$(echo bogus | git commit-tree $tree) &&
+ git update-ref refs/heads/bogus $cmt &&
+
+ test_must_fail git fsck 2>out &&
+ grep "$oldoid: hash-path mismatch, found at: .*$new" out
+ )
+'
+
+test_expect_success 'object with hash and type mismatch' '
+ git init --bare hash-type-mismatch &&
+ (
+ cd hash-type-mismatch &&
+
+ oid=$(echo blob | git hash-object -w --stdin -t garbage --literally) &&
+ oldoid=$oid &&
+ old=$(test_oid_to_path "$oid") &&
+ new=$(dirname $old)/$(test_oid ff_2) &&
+ oid="$(dirname $new)$(basename $new)" &&
+
+ mv objects/$old objects/$new &&
+ git update-index --add --cacheinfo 100644 $oid foo &&
+ tree=$(git write-tree) &&
+ cmt=$(echo bogus | git commit-tree $tree) &&
+ git update-ref refs/heads/bogus $cmt &&
+
+
+ test_must_fail git fsck 2>out &&
+ grep "^error: $oldoid: hash-path mismatch, found at: .*$new" out &&
+ grep "^error: $oldoid: object is of unknown type '"'"'garbage'"'"'" out
+ )
+'
+
+test_expect_success 'zlib corrupt loose object output ' '
+ git init --bare corrupt-loose-output &&
+ (
+ cd corrupt-loose-output &&
+ oid=$(git hash-object -w --stdin --literally </dev/null) &&
+ oidf=objects/$(test_oid_to_path "$oid") &&
+ chmod +w $oidf &&
+ echo extra garbage >>$oidf &&
+
+ cat >expect.error <<-EOF &&
+ error: garbage at end of loose object '\''$oid'\''
+ error: unable to unpack contents of ./$oidf
+ error: $oid: object corrupt or missing: ./$oidf
+ EOF
+ test_must_fail git fsck 2>actual &&
+ grep ^error: actual >error &&
+ test_cmp expect.error error
+ )
'
test_expect_success 'branch pointing to non-commit' '
- git rev-parse HEAD^{tree} >.git/refs/heads/invalid &&
+ tree_oid=$(git rev-parse --verify HEAD^{tree}) &&
test_when_finished "git update-ref -d refs/heads/invalid" &&
+ test-tool ref-store main update-ref msg refs/heads/invalid $tree_oid $ZERO_OID REF_SKIP_OID_VERIFICATION &&
test_must_fail git fsck 2>out &&
- test_i18ngrep "not a commit" out
+ test_grep "not a commit" out
'
-test_expect_success 'HEAD link pointing at a funny object' '
- test_when_finished "mv .git/SAVED_HEAD .git/HEAD" &&
- mv .git/HEAD .git/SAVED_HEAD &&
+test_expect_success REFFILES 'HEAD link pointing at a funny object' '
+ test_when_finished "git update-ref HEAD $orig_head" &&
echo $ZERO_OID >.git/HEAD &&
# avoid corrupt/broken HEAD from interfering with repo discovery
test_must_fail env GIT_DIR=.git git fsck 2>out &&
- test_i18ngrep "detached HEAD points" out
+ test_grep "detached HEAD points" out
'
test_expect_success 'HEAD link pointing at a funny place' '
- test_when_finished "mv .git/SAVED_HEAD .git/HEAD" &&
- mv .git/HEAD .git/SAVED_HEAD &&
- echo "ref: refs/funny/place" >.git/HEAD &&
+ test_when_finished "git update-ref --no-deref HEAD $orig_head" &&
+ test-tool ref-store main create-symref HEAD refs/funny/place &&
# avoid corrupt/broken HEAD from interfering with repo discovery
test_must_fail env GIT_DIR=.git git fsck 2>out &&
- test_i18ngrep "HEAD points to something strange" out
+ test_grep "HEAD points to something strange" out
'
-test_expect_success 'HEAD link pointing at a funny object (from different wt)' '
- test_when_finished "mv .git/SAVED_HEAD .git/HEAD" &&
- test_when_finished "rm -rf .git/worktrees wt" &&
+test_expect_success REFFILES 'HEAD link pointing at a funny object (from different wt)' '
+ test_when_finished "git update-ref HEAD $orig_head" &&
+ test_when_finished "git worktree remove -f wt" &&
git worktree add wt &&
- mv .git/HEAD .git/SAVED_HEAD &&
echo $ZERO_OID >.git/HEAD &&
# avoid corrupt/broken HEAD from interfering with repo discovery
test_must_fail git -C wt fsck 2>out &&
- test_i18ngrep "main-worktree/HEAD: detached HEAD points" out
+ test_grep "main-worktree/HEAD: detached HEAD points" out
'
-test_expect_success 'other worktree HEAD link pointing at a funny object' '
- test_when_finished "rm -rf .git/worktrees other" &&
+test_expect_success REFFILES 'other worktree HEAD link pointing at a funny object' '
+ test_when_finished "git worktree remove -f other" &&
git worktree add other &&
echo $ZERO_OID >.git/worktrees/other/HEAD &&
test_must_fail git fsck 2>out &&
- test_i18ngrep "worktrees/other/HEAD: detached HEAD points" out
+ test_grep "worktrees/other/HEAD: detached HEAD points" out
'
test_expect_success 'other worktree HEAD link pointing at missing object' '
- test_when_finished "rm -rf .git/worktrees other" &&
+ test_when_finished "git worktree remove -f other" &&
git worktree add other &&
- echo "Contents missing from repo" | git hash-object --stdin >.git/worktrees/other/HEAD &&
+ object_id=$(echo "Contents missing from repo" | git hash-object --stdin) &&
+ test-tool -C other ref-store main update-ref msg HEAD $object_id "" REF_NO_DEREF,REF_SKIP_OID_VERIFICATION &&
test_must_fail git fsck 2>out &&
- test_i18ngrep "worktrees/other/HEAD: invalid sha1 pointer" out
+ test_grep "worktrees/other/HEAD: invalid sha1 pointer" out
'
test_expect_success 'other worktree HEAD link pointing at a funny place' '
- test_when_finished "rm -rf .git/worktrees other" &&
+ test_when_finished "git worktree remove -f other" &&
git worktree add other &&
- echo "ref: refs/funny/place" >.git/worktrees/other/HEAD &&
+ git -C other symbolic-ref HEAD refs/funny/place &&
test_must_fail git fsck 2>out &&
- test_i18ngrep "worktrees/other/HEAD points to something strange" out
+ test_grep "worktrees/other/HEAD points to something strange" out
'
test_expect_success 'commit with multiple signatures is okay' '
@@ -166,45 +212,45 @@ test_expect_success 'email without @ is okay' '
test_expect_success 'email with embedded > is not okay' '
git cat-file commit HEAD >basis &&
sed "s/@[a-z]/&>/" basis >bad-email &&
- new=$(git hash-object -t commit -w --stdin <bad-email) &&
+ new=$(git hash-object --literally -t commit -w --stdin <bad-email) &&
test_when_finished "remove_object $new" &&
git update-ref refs/heads/bogus "$new" &&
test_when_finished "git update-ref -d refs/heads/bogus" &&
test_must_fail git fsck 2>out &&
- test_i18ngrep "error in commit $new" out
+ test_grep "error in commit $new" out
'
test_expect_success 'missing < email delimiter is reported nicely' '
git cat-file commit HEAD >basis &&
sed "s/<//" basis >bad-email-2 &&
- new=$(git hash-object -t commit -w --stdin <bad-email-2) &&
+ new=$(git hash-object --literally -t commit -w --stdin <bad-email-2) &&
test_when_finished "remove_object $new" &&
git update-ref refs/heads/bogus "$new" &&
test_when_finished "git update-ref -d refs/heads/bogus" &&
test_must_fail git fsck 2>out &&
- test_i18ngrep "error in commit $new.* - bad name" out
+ test_grep "error in commit $new.* - bad name" out
'
test_expect_success 'missing email is reported nicely' '
git cat-file commit HEAD >basis &&
sed "s/[a-z]* <[^>]*>//" basis >bad-email-3 &&
- new=$(git hash-object -t commit -w --stdin <bad-email-3) &&
+ new=$(git hash-object --literally -t commit -w --stdin <bad-email-3) &&
test_when_finished "remove_object $new" &&
git update-ref refs/heads/bogus "$new" &&
test_when_finished "git update-ref -d refs/heads/bogus" &&
test_must_fail git fsck 2>out &&
- test_i18ngrep "error in commit $new.* - missing email" out
+ test_grep "error in commit $new.* - missing email" out
'
test_expect_success '> in name is reported' '
git cat-file commit HEAD >basis &&
sed "s/ </> </" basis >bad-email-4 &&
- new=$(git hash-object -t commit -w --stdin <bad-email-4) &&
+ new=$(git hash-object --literally -t commit -w --stdin <bad-email-4) &&
test_when_finished "remove_object $new" &&
git update-ref refs/heads/bogus "$new" &&
test_when_finished "git update-ref -d refs/heads/bogus" &&
test_must_fail git fsck 2>out &&
- test_i18ngrep "error in commit $new" out
+ test_grep "error in commit $new" out
'
# date is 2^64 + 1
@@ -212,23 +258,23 @@ test_expect_success 'integer overflow in timestamps is reported' '
git cat-file commit HEAD >basis &&
sed "s/^\\(author .*>\\) [0-9]*/\\1 18446744073709551617/" \
<basis >bad-timestamp &&
- new=$(git hash-object -t commit -w --stdin <bad-timestamp) &&
+ new=$(git hash-object --literally -t commit -w --stdin <bad-timestamp) &&
test_when_finished "remove_object $new" &&
git update-ref refs/heads/bogus "$new" &&
test_when_finished "git update-ref -d refs/heads/bogus" &&
test_must_fail git fsck 2>out &&
- test_i18ngrep "error in commit $new.*integer overflow" out
+ test_grep "error in commit $new.*integer overflow" out
'
test_expect_success 'commit with NUL in header' '
git cat-file commit HEAD >basis &&
sed "s/author ./author Q/" <basis | q_to_nul >commit-NUL-header &&
- new=$(git hash-object -t commit -w --stdin <commit-NUL-header) &&
+ new=$(git hash-object --literally -t commit -w --stdin <commit-NUL-header) &&
test_when_finished "remove_object $new" &&
git update-ref refs/heads/bogus "$new" &&
test_when_finished "git update-ref -d refs/heads/bogus" &&
test_must_fail git fsck 2>out &&
- test_i18ngrep "error in commit $new.*unterminated header: NUL at offset" out
+ test_grep "error in commit $new.*unterminated header: NUL at offset" out
'
test_expect_success 'tree object with duplicate entries' '
@@ -246,10 +292,10 @@ test_expect_success 'tree object with duplicate entries' '
git cat-file tree $T &&
git cat-file tree $T
) |
- git hash-object -w -t tree --stdin
+ git hash-object --literally -w -t tree --stdin
) &&
test_must_fail git fsck 2>out &&
- test_i18ngrep "error in tree .*contains duplicate file entries" out
+ test_grep "error in tree .*contains duplicate file entries" out
'
check_duplicate_names () {
@@ -272,8 +318,8 @@ check_duplicate_names () {
done >badtree &&
badtree=$(git mktree <badtree) &&
test_must_fail git fsck 2>out &&
- test_i18ngrep "$badtree" out &&
- test_i18ngrep "error in tree .*contains duplicate file entries" out
+ test_grep "$badtree" out &&
+ test_grep "error in tree .*contains duplicate file entries" out
'
}
@@ -295,9 +341,9 @@ test_expect_success 'unparseable tree object' '
commit_sha1=$(git commit-tree $tree_sha1) &&
git update-ref refs/heads/wrong $commit_sha1 &&
test_must_fail git fsck 2>out &&
- test_i18ngrep "error: empty filename in tree entry" out &&
- test_i18ngrep "$tree_sha1" out &&
- test_i18ngrep ! "fatal: empty filename in tree entry" out
+ test_grep "error: empty filename in tree entry" out &&
+ test_grep "$tree_sha1" out &&
+ test_grep ! "fatal: empty filename in tree entry" out
'
test_expect_success 'tree entry with type mismatch' '
@@ -314,8 +360,22 @@ test_expect_success 'tree entry with type mismatch' '
commit=$(git commit-tree $tree) &&
git update-ref refs/heads/type_mismatch $commit &&
test_must_fail git fsck >out 2>&1 &&
- test_i18ngrep "is a blob, not a tree" out &&
- test_i18ngrep ! "dangling blob" out
+ test_grep "is a blob, not a tree" out &&
+ test_grep ! "dangling blob" out
+'
+
+test_expect_success 'tree entry with bogus mode' '
+ test_when_finished "remove_object \$blob" &&
+ test_when_finished "remove_object \$tree" &&
+ blob=$(echo blob | git hash-object -w --stdin) &&
+ blob_oct=$(echo $blob | hex2oct) &&
+ tree=$(printf "100000 foo\0${blob_oct}" |
+ git hash-object -t tree --stdin -w --literally) &&
+ git fsck 2>err &&
+ cat >expect <<-EOF &&
+ warning in tree $tree: badFilemode: contains bad file modes
+ EOF
+ test_cmp expect err
'
test_expect_success 'tag pointing to nonexistent' '
@@ -331,10 +391,10 @@ test_expect_success 'tag pointing to nonexistent' '
tag=$(git hash-object -t tag -w --stdin <invalid-tag) &&
test_when_finished "remove_object $tag" &&
- echo $tag >.git/refs/tags/invalid &&
+ git update-ref refs/tags/invalid $tag &&
test_when_finished "git update-ref -d refs/tags/invalid" &&
test_must_fail git fsck --tags >out &&
- test_i18ngrep "broken link" out
+ test_grep "broken link" out
'
test_expect_success 'tag pointing to something else than its type' '
@@ -351,7 +411,7 @@ test_expect_success 'tag pointing to something else than its type' '
tag=$(git hash-object -t tag -w --stdin <wrong-tag) &&
test_when_finished "remove_object $tag" &&
- echo $tag >.git/refs/tags/wrong &&
+ git update-ref refs/tags/wrong $tag &&
test_when_finished "git update-ref -d refs/tags/wrong" &&
test_must_fail git fsck --tags
'
@@ -366,9 +426,9 @@ test_expect_success 'tag with incorrect tag name & missing tagger' '
This is an invalid tag.
EOF
- tag=$(git hash-object -t tag -w --stdin <wrong-tag) &&
+ tag=$(git hash-object --literally -t tag -w --stdin <wrong-tag) &&
test_when_finished "remove_object $tag" &&
- echo $tag >.git/refs/tags/wrong &&
+ git update-ref refs/tags/wrong $tag &&
test_when_finished "git update-ref -d refs/tags/wrong" &&
git fsck --tags 2>out &&
@@ -392,10 +452,10 @@ test_expect_success 'tag with bad tagger' '
tag=$(git hash-object --literally -t tag -w --stdin <wrong-tag) &&
test_when_finished "remove_object $tag" &&
- echo $tag >.git/refs/tags/wrong &&
+ git update-ref refs/tags/wrong $tag &&
test_when_finished "git update-ref -d refs/tags/wrong" &&
test_must_fail git fsck --tags 2>out &&
- test_i18ngrep "error in tag .*: invalid author/committer" out
+ test_grep "error in tag .*: invalid author/committer" out
'
test_expect_success 'tag with NUL in header' '
@@ -411,10 +471,10 @@ test_expect_success 'tag with NUL in header' '
tag=$(git hash-object --literally -t tag -w --stdin <tag-NUL-header) &&
test_when_finished "remove_object $tag" &&
- echo $tag >.git/refs/tags/wrong &&
+ git update-ref refs/tags/wrong $tag &&
test_when_finished "git update-ref -d refs/tags/wrong" &&
test_must_fail git fsck --tags 2>out &&
- test_i18ngrep "error in tag $tag.*unterminated header: NUL at offset" out
+ test_grep "error in tag $tag.*unterminated header: NUL at offset" out
'
test_expect_success 'cleaned up' '
@@ -444,13 +504,61 @@ test_expect_success 'rev-list --verify-objects with bad sha1' '
test_when_finished "git update-ref -d refs/heads/bogus" &&
test_might_fail git rev-list --verify-objects refs/heads/bogus >/dev/null 2>out &&
- test_i18ngrep -q "error: hash mismatch $(dirname $new)$(test_oid ff_2)" out
+ test_grep -q "error: hash mismatch $(dirname $new)$(test_oid ff_2)" out
+'
+
+# An actual bit corruption is more likely than swapped commits, but
+# this provides an easy way to have commits which don't match their purported
+# hashes, but which aren't so broken we can't read them at all.
+test_expect_success 'rev-list --verify-objects notices swapped commits' '
+ git init swapped-commits &&
+ (
+ cd swapped-commits &&
+ test_commit one &&
+ test_commit two &&
+ one_oid=$(git rev-parse HEAD) &&
+ two_oid=$(git rev-parse HEAD^) &&
+ one=.git/objects/$(test_oid_to_path $one_oid) &&
+ two=.git/objects/$(test_oid_to_path $two_oid) &&
+ mv $one tmp &&
+ mv $two $one &&
+ mv tmp $two &&
+ test_must_fail git rev-list --verify-objects HEAD
+ )
+'
+
+test_expect_success 'set up repository with commit-graph' '
+ git init corrupt-graph &&
+ (
+ cd corrupt-graph &&
+ test_commit one &&
+ test_commit two &&
+ git commit-graph write --reachable
+ )
+'
+
+corrupt_graph_obj () {
+ oid=$(git -C corrupt-graph rev-parse "$1") &&
+ obj=corrupt-graph/.git/objects/$(test_oid_to_path $oid) &&
+ test_when_finished 'mv backup $obj' &&
+ mv $obj backup &&
+ echo garbage >$obj
+}
+
+test_expect_success 'rev-list --verify-objects with commit graph (tip)' '
+ corrupt_graph_obj HEAD &&
+ test_must_fail git -C corrupt-graph rev-list --verify-objects HEAD
+'
+
+test_expect_success 'rev-list --verify-objects with commit graph (parent)' '
+ corrupt_graph_obj HEAD^ &&
+ test_must_fail git -C corrupt-graph rev-list --verify-objects HEAD
'
test_expect_success 'force fsck to ignore double author' '
git cat-file commit HEAD >basis &&
sed "s/^author .*/&,&/" <basis | tr , \\n >multiple-authors &&
- new=$(git hash-object -t commit -w --stdin <multiple-authors) &&
+ new=$(git hash-object --literally -t commit -w --stdin <multiple-authors) &&
test_when_finished "remove_object $new" &&
git update-ref refs/heads/bogus "$new" &&
test_when_finished "git update-ref -d refs/heads/bogus" &&
@@ -465,9 +573,9 @@ test_expect_success 'fsck notices blob entry pointing to null sha1' '
(git init null-blob &&
cd null-blob &&
sha=$(printf "100644 file$_bz$_bzoid" |
- git hash-object -w --stdin -t tree) &&
+ git hash-object --literally -w --stdin -t tree) &&
git fsck 2>out &&
- test_i18ngrep "warning.*null sha1" out
+ test_grep "warning.*null sha1" out
)
'
@@ -475,9 +583,19 @@ test_expect_success 'fsck notices submodule entry pointing to null sha1' '
(git init null-commit &&
cd null-commit &&
sha=$(printf "160000 submodule$_bz$_bzoid" |
- git hash-object -w --stdin -t tree) &&
+ git hash-object --literally -w --stdin -t tree) &&
git fsck 2>out &&
- test_i18ngrep "warning.*null sha1" out
+ test_grep "warning.*null sha1" out
+ )
+'
+
+test_expect_success 'fsck notices excessively large tree entry name' '
+ git init large-name &&
+ (
+ cd large-name &&
+ test_commit a-long-name &&
+ git -c fsck.largePathname=warn:10 fsck 2>out &&
+ grep "warning.*large pathname" out
)
'
@@ -498,7 +616,7 @@ while read name path pretty; do
printf "$mode $type %s\t%s" "$value" "$path" >bad &&
bad_tree=$(git mktree <bad) &&
git fsck 2>out &&
- test_i18ngrep "warning.*tree $bad_tree" out
+ test_grep "warning.*tree $bad_tree" out
)'
done <<-\EOF
100644 blob
@@ -540,13 +658,13 @@ test_expect_success 'NUL in commit' '
git commit --allow-empty -m "initial commitQNUL after message" &&
git cat-file commit HEAD >original &&
q_to_nul <original >munged &&
- git hash-object -w -t commit --stdin <munged >name &&
+ git hash-object --literally -w -t commit --stdin <munged >name &&
git branch bad $(cat name) &&
test_must_fail git -c fsck.nulInCommit=error fsck 2>warn.1 &&
- test_i18ngrep nulInCommit warn.1 &&
+ test_grep nulInCommit warn.1 &&
git fsck 2>warn.2 &&
- test_i18ngrep nulInCommit warn.2
+ test_grep nulInCommit warn.2
)
'
@@ -666,7 +784,7 @@ test_expect_success 'fsck --name-objects' '
tree=$(git rev-parse --verify julius:) &&
git tag -d julius &&
test_must_fail git fsck --name-objects >out &&
- test_i18ngrep "$tree (refs/tags/augustus44\\^:" out
+ test_grep "$tree (refs/tags/augustus44\\^:" out
)
'
@@ -679,15 +797,15 @@ test_expect_success 'alternate objects are correctly blamed' '
mkdir alt.git/objects/$(dirname $path) &&
>alt.git/objects/$(dirname $path)/$(basename $path) &&
test_must_fail git fsck >out 2>&1 &&
- test_i18ngrep alt.git out
+ test_grep alt.git out
'
test_expect_success 'fsck errors in packed objects' '
git cat-file commit HEAD >basis &&
sed "s/</one/" basis >one &&
sed "s/</foo/" basis >two &&
- one=$(git hash-object -t commit -w one) &&
- two=$(git hash-object -t commit -w two) &&
+ one=$(git hash-object --literally -t commit -w one) &&
+ two=$(git hash-object --literally -t commit -w two) &&
pack=$(
{
echo $one &&
@@ -698,8 +816,8 @@ test_expect_success 'fsck errors in packed objects' '
remove_object $one &&
remove_object $two &&
test_must_fail git fsck 2>out &&
- test_i18ngrep "error in commit $one.* - bad name" out &&
- test_i18ngrep "error in commit $two.* - bad name" out &&
+ test_grep "error in commit $one.* - bad name" out &&
+ test_grep "error in commit $two.* - bad name" out &&
! grep corrupt out
'
@@ -716,7 +834,7 @@ test_expect_success 'fsck fails on corrupt packfile' '
test_when_finished "rm -f .git/objects/pack/pack-$pack.*" &&
remove_object $hsh &&
test_must_fail git fsck 2>out &&
- test_i18ngrep "checksum mismatch" out
+ test_grep "checksum mismatch" out
'
test_expect_success 'fsck finds problems in duplicate loose objects' '
@@ -728,10 +846,19 @@ test_expect_success 'fsck finds problems in duplicate loose objects' '
# no "-d" here, so we end up with duplicates
git repack &&
# now corrupt the loose copy
- file=$(sha1_file "$(git rev-parse HEAD)") &&
+ oid="$(git rev-parse HEAD)" &&
+ file=$(sha1_file "$oid") &&
rm "$file" &&
echo broken >"$file" &&
- test_must_fail git fsck
+ test_must_fail git fsck 2>err &&
+
+ cat >expect <<-EOF &&
+ error: inflate: data stream error (incorrect header check)
+ error: unable to unpack header of $file
+ error: $oid: object corrupt or missing: $file
+ EOF
+ grep "^error: " err >actual &&
+ test_cmp expect actual
)
'
@@ -744,7 +871,7 @@ test_expect_success 'fsck detects trailing loose garbage (commit)' '
chmod +w "$file" &&
echo garbage >>"$file" &&
test_must_fail git fsck 2>out &&
- test_i18ngrep "garbage.*$commit" out
+ test_grep "garbage.*$commit" out
'
test_expect_success 'fsck detects trailing loose garbage (large blob)' '
@@ -754,7 +881,7 @@ test_expect_success 'fsck detects trailing loose garbage (large blob)' '
chmod +w "$file" &&
echo garbage >>"$file" &&
test_must_fail git -c core.bigfilethreshold=5 fsck 2>out &&
- test_i18ngrep "garbage.*$blob" out
+ test_grep "garbage.*$blob" out
'
test_expect_success 'fsck detects truncated loose object' '
@@ -770,10 +897,10 @@ test_expect_success 'fsck detects truncated loose object' '
# check both regular and streaming code paths
test_must_fail git fsck 2>out &&
- test_i18ngrep corrupt.*$blob out &&
+ test_grep corrupt.*$blob out &&
test_must_fail git -c core.bigfilethreshold=128 fsck 2>out &&
- test_i18ngrep corrupt.*$blob out
+ test_grep corrupt.*$blob out
'
# for each of type, we have one version which is referenced by another object
@@ -862,7 +989,75 @@ test_expect_success 'detect corrupt index file in fsck' '
test_when_finished "mv .git/index.backup .git/index" &&
corrupt_index_checksum &&
test_must_fail git fsck --cache 2>errors &&
- test_i18ngrep "bad index file" errors
+ test_grep "bad index file" errors
+'
+
+test_expect_success 'fsck error and recovery on invalid object type' '
+ git init --bare garbage-type &&
+ (
+ cd garbage-type &&
+
+ garbage_blob=$(git hash-object --stdin -w -t garbage --literally </dev/null) &&
+
+ test_must_fail git fsck 2>err &&
+ grep -e "^error" -e "^fatal" err >errors &&
+ test_line_count = 1 errors &&
+ grep "$garbage_blob: object is of unknown type '"'"'garbage'"'"':" err
+ )
+'
+
+test_expect_success 'fsck error on gitattributes with excessive line lengths' '
+ blob=$(printf "pattern %02048d" 1 | git hash-object -w --stdin) &&
+ test_when_finished "remove_object $blob" &&
+ tree=$(printf "100644 blob %s\t%s\n" $blob .gitattributes | git mktree) &&
+ test_when_finished "remove_object $tree" &&
+ cat >expected <<-EOF &&
+ error in blob $blob: gitattributesLineLength: .gitattributes has too long lines to parse
+ EOF
+ test_must_fail git fsck --no-dangling >actual 2>&1 &&
+ test_cmp expected actual
+'
+
+test_expect_success 'fsck error on gitattributes with excessive size' '
+ blob=$(test-tool genzeros $((100 * 1024 * 1024 + 1)) | git hash-object -w --stdin) &&
+ test_when_finished "remove_object $blob" &&
+ tree=$(printf "100644 blob %s\t%s\n" $blob .gitattributes | git mktree) &&
+ test_when_finished "remove_object $tree" &&
+ cat >expected <<-EOF &&
+ error in blob $blob: gitattributesLarge: .gitattributes too large to parse
+ EOF
+ test_must_fail git fsck --no-dangling >actual 2>&1 &&
+ test_cmp expected actual
+'
+
+test_expect_success 'fsck detects problems in worktree index' '
+ test_when_finished "git worktree remove -f wt" &&
+ git worktree add wt &&
+
+ echo "this will be removed to break the worktree index" >wt/file &&
+ git -C wt add file &&
+ blob=$(git -C wt rev-parse :file) &&
+ remove_object $blob &&
+
+ test_must_fail git fsck --name-objects >actual 2>&1 &&
+ cat >expect <<-EOF &&
+ missing blob $blob (.git/worktrees/wt/index:file)
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'fsck reports problems in current worktree index without filename' '
+ test_when_finished "rm -f .git/index && git read-tree HEAD" &&
+ echo "this object will be removed to break current worktree index" >file &&
+ git add file &&
+ blob=$(git rev-parse :file) &&
+ remove_object $blob &&
+
+ test_must_fail git fsck --name-objects >actual 2>&1 &&
+ cat >expect <<-EOF &&
+ missing blob $blob (:file)
+ EOF
+ test_cmp expect actual
'
test_done
diff --git a/t/t1451-fsck-buffer.sh b/t/t1451-fsck-buffer.sh
new file mode 100755
index 0000000..3413da4
--- /dev/null
+++ b/t/t1451-fsck-buffer.sh
@@ -0,0 +1,142 @@
+#!/bin/sh
+
+test_description='fsck on buffers without NUL termination
+
+The goal here is to make sure that the various fsck parsers never look
+past the end of the buffer they are given, even when encountering broken
+or truncated objects.
+
+We have to use "hash-object" for this because most code paths that read objects
+append an extra NUL for safety after the buffer. But hash-object, since it is
+reading straight from a file (and possibly even mmap-ing it) cannot always do
+so.
+
+These tests _might_ catch such overruns in normal use, but should be run with
+ASan or valgrind for more confidence.
+'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+# the general idea for tags and commits is to build up the "base" file
+# progressively, and then test new truncations on top of it.
+reset () {
+ test_expect_success 'reset input to empty' '
+ >base
+ '
+}
+
+add () {
+ content="$1"
+ type=${content%% *}
+ test_expect_success "add $type line" '
+ echo "$content" >>base
+ '
+}
+
+check () {
+ type=$1
+ fsck=$2
+ content=$3
+ test_expect_success "truncated $type ($fsck, \"$content\")" '
+ # do not pipe into hash-object here; we want to increase
+ # the chance that it uses a fixed-size buffer or mmap,
+ # and a pipe would be read into a strbuf.
+ {
+ cat base &&
+ echo "$content"
+ } >input &&
+ test_must_fail git hash-object -t "$type" input 2>err &&
+ grep "$fsck" err
+ '
+}
+
+test_expect_success 'create valid objects' '
+ git commit --allow-empty -m foo &&
+ commit=$(git rev-parse --verify HEAD) &&
+ tree=$(git rev-parse --verify HEAD^{tree})
+'
+
+reset
+check commit missingTree ""
+check commit missingTree "tr"
+check commit missingTree "tree"
+check commit badTreeSha1 "tree "
+check commit badTreeSha1 "tree 1234"
+add "tree $tree"
+
+# these expect missingAuthor because "parent" is optional
+check commit missingAuthor ""
+check commit missingAuthor "par"
+check commit missingAuthor "parent"
+check commit badParentSha1 "parent "
+check commit badParentSha1 "parent 1234"
+add "parent $commit"
+
+check commit missingAuthor ""
+check commit missingAuthor "au"
+check commit missingAuthor "author"
+ident_checks () {
+ check $1 missingEmail "$2 "
+ check $1 missingEmail "$2 name"
+ check $1 badEmail "$2 name <"
+ check $1 badEmail "$2 name <email"
+ check $1 missingSpaceBeforeDate "$2 name <email>"
+ check $1 badDate "$2 name <email> "
+ check $1 badDate "$2 name <email> 1234"
+ check $1 badTimezone "$2 name <email> 1234 "
+ check $1 badTimezone "$2 name <email> 1234 +"
+}
+ident_checks commit author
+add "author name <email> 1234 +0000"
+
+check commit missingCommitter ""
+check commit missingCommitter "co"
+check commit missingCommitter "committer"
+ident_checks commit committer
+add "committer name <email> 1234 +0000"
+
+reset
+check tag missingObject ""
+check tag missingObject "obj"
+check tag missingObject "object"
+check tag badObjectSha1 "object "
+check tag badObjectSha1 "object 1234"
+add "object $commit"
+
+check tag missingType ""
+check tag missingType "ty"
+check tag missingType "type"
+check tag badType "type "
+check tag badType "type com"
+add "type commit"
+
+check tag missingTagEntry ""
+check tag missingTagEntry "ta"
+check tag missingTagEntry "tag"
+check tag badTagName "tag "
+add "tag foo"
+
+check tag missingTagger ""
+check tag missingTagger "ta"
+check tag missingTagger "tagger"
+ident_checks tag tagger
+
+# trees are a binary format and can't use our earlier helpers
+test_expect_success 'truncated tree (short hash)' '
+ printf "100644 foo\0\1\1\1\1" >input &&
+ test_must_fail git hash-object -t tree input 2>err &&
+ grep badTree err
+'
+
+test_expect_success 'truncated tree (missing nul)' '
+ # these two things are indistinguishable to the parser. The important
+ # thing about this is example is that there are enough bytes to
+ # make up a hash, and that there is no NUL (and we confirm that the
+ # parser does not walk past the end of the buffer).
+ printf "100644 a long filename, or a hash with missing nul?" >input &&
+ test_must_fail git hash-object -t tree input 2>err &&
+ grep badTree err
+'
+
+test_done
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index 1c2df08..a669e59 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -4,6 +4,7 @@ test_description='test git rev-parse'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_one () {
@@ -194,7 +195,7 @@ test_expect_success 'rev-parse --is-shallow-repository in non-shallow repo' '
'
test_expect_success 'rev-parse --show-object-format in repo' '
- echo "$(test_oid algo)" >expect &&
+ test_oid algo >expect &&
git rev-parse --show-object-format >actual &&
test_cmp expect actual &&
git rev-parse --show-object-format=storage >actual &&
@@ -207,6 +208,23 @@ test_expect_success 'rev-parse --show-object-format in repo' '
grep "unknown mode for --show-object-format: squeamish-ossifrage" err
'
+test_expect_success 'rev-parse --show-ref-format' '
+ test_detect_ref_format >expect &&
+ git rev-parse --show-ref-format >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rev-parse --show-ref-format with invalid storage' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ git config extensions.refstorage broken &&
+ test_must_fail git rev-parse --show-ref-format 2>err &&
+ grep "error: invalid value for ${SQ}extensions.refstorage${SQ}: ${SQ}broken${SQ}" err
+ )
+'
+
test_expect_success '--show-toplevel from subdir of working tree' '
pwd >expect &&
git -C sub/dir rev-parse --show-toplevel >actual &&
@@ -225,7 +243,8 @@ test_expect_success 'showing the superproject correctly' '
test_commit -C super test_commit &&
test_create_repo sub &&
test_commit -C sub test_commit &&
- git -C super submodule add ../sub dir/sub &&
+ git -c protocol.file.allow=always \
+ -C super submodule add ../sub dir/sub &&
echo $(pwd)/super >expect &&
git -C super/dir/sub rev-parse --show-superproject-working-tree >out &&
test_cmp expect out &&
@@ -262,4 +281,27 @@ test_expect_success 'rev-parse --since= unsqueezed ordering' '
test_cmp expect actual
'
+test_expect_success 'rev-parse --bisect includes bad, excludes good' '
+ test_commit_bulk 6 &&
+
+ git update-ref refs/bisect/bad-1 HEAD~1 &&
+ git update-ref refs/bisect/b HEAD~2 &&
+ git update-ref refs/bisect/bad-3 HEAD~3 &&
+ git update-ref refs/bisect/good-3 HEAD~3 &&
+ git update-ref refs/bisect/bad-4 HEAD~4 &&
+ git update-ref refs/bisect/go HEAD~4 &&
+
+ # Note: refs/bisect/b and refs/bisect/go should be ignored because they
+ # do not match the refs/bisect/bad or refs/bisect/good prefixes.
+ cat >expect <<-EOF &&
+ refs/bisect/bad-1
+ refs/bisect/bad-3
+ refs/bisect/bad-4
+ ^refs/bisect/good-3
+ EOF
+
+ git rev-parse --symbolic-full-name --bisect >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t1501-work-tree.sh b/t/t1501-work-tree.sh
index b755580..ae6528a 100755
--- a/t/t1501-work-tree.sh
+++ b/t/t1501-work-tree.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test separate work tree'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh
index b29563f..b754b9f 100755
--- a/t/t1502-rev-parse-parseopt.sh
+++ b/t/t1502-rev-parse-parseopt.sh
@@ -3,13 +3,29 @@
test_description='test git rev-parse --parseopt'
. ./test-lib.sh
+check_invalid_long_option () {
+ spec="$1"
+ opt="$2"
+ test_expect_success "test --parseopt invalid switch $opt help output for $spec" '
+ {
+ cat <<-\EOF &&
+ error: unknown option `'${opt#--}\''
+ EOF
+ sed -e 1d -e \$d <"$TEST_DIRECTORY/t1502/$spec.help"
+ } >expect &&
+ test_expect_code 129 git rev-parse --parseopt -- $opt \
+ 2>output <"$TEST_DIRECTORY/t1502/$spec" &&
+ test_cmp expect output
+ '
+}
+
test_expect_success 'setup optionspec' '
sed -e "s/^|//" >optionspec <<\EOF
|some-command [options] <args>...
|
|some-command does foo and bar!
|--
-|h,help show the help
+|h,help! show the help
|
|foo some nifty option --foo
|bar= some cool option --bar with an argument
@@ -58,44 +74,8 @@ EOF
'
test_expect_success 'test --parseopt help output' '
- sed -e "s/^|//" >expect <<\END_EXPECT &&
-|cat <<\EOF
-|usage: some-command [options] <args>...
-|
-| some-command does foo and bar!
-|
-| -h, --help show the help
-| --foo some nifty option --foo
-| --bar ... some cool option --bar with an argument
-| -b, --baz a short and long option
-|
-|An option group Header
-| -C[...] option C with an optional argument
-| -d, --data[=...] short and long option with an optional argument
-|
-|Argument hints
-| -B <arg> short option required argument
-| --bar2 <arg> long option required argument
-| -e, --fuz <with-space>
-| short and long option required argument
-| -s[<some>] short option optional argument
-| --long[=<data>] long option optional argument
-| -g, --fluf[=<path>] short and long option optional argument
-| --longest <very-long-argument-hint>
-| a very long argument hint
-| --pair <key=value> with an equals sign in the hint
-| --aswitch help te=t contains? fl*g characters!`
-| --bswitch <hint> hint has trailing tab character
-| --cswitch switch has trailing tab character
-| --short-hint <a> with a one symbol hint
-|
-|Extras
-| --extra1 line above used to cause a segfault but no longer does
-|
-|EOF
-END_EXPECT
test_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec &&
- test_cmp expect output
+ test_cmp "$TEST_DIRECTORY/t1502/optionspec.help" output
'
test_expect_success 'test --parseopt help output no switches' '
@@ -131,7 +111,7 @@ test_expect_success 'test --parseopt help-all output hidden switches' '
|
| some-command does foo and bar!
|
-| --hidden1 A hidden switch
+| --[no-]hidden1 A hidden switch
|
|EOF
END_EXPECT
@@ -140,41 +120,12 @@ END_EXPECT
'
test_expect_success 'test --parseopt invalid switch help output' '
- sed -e "s/^|//" >expect <<\END_EXPECT &&
-|error: unknown option `does-not-exist'\''
-|usage: some-command [options] <args>...
-|
-| some-command does foo and bar!
-|
-| -h, --help show the help
-| --foo some nifty option --foo
-| --bar ... some cool option --bar with an argument
-| -b, --baz a short and long option
-|
-|An option group Header
-| -C[...] option C with an optional argument
-| -d, --data[=...] short and long option with an optional argument
-|
-|Argument hints
-| -B <arg> short option required argument
-| --bar2 <arg> long option required argument
-| -e, --fuz <with-space>
-| short and long option required argument
-| -s[<some>] short option optional argument
-| --long[=<data>] long option optional argument
-| -g, --fluf[=<path>] short and long option optional argument
-| --longest <very-long-argument-hint>
-| a very long argument hint
-| --pair <key=value> with an equals sign in the hint
-| --aswitch help te=t contains? fl*g characters!`
-| --bswitch <hint> hint has trailing tab character
-| --cswitch switch has trailing tab character
-| --short-hint <a> with a one symbol hint
-|
-|Extras
-| --extra1 line above used to cause a segfault but no longer does
-|
-END_EXPECT
+ {
+ cat <<-\EOF &&
+ error: unknown option `does-not-exist'\''
+ EOF
+ sed -e 1d -e \$d <"$TEST_DIRECTORY/t1502/optionspec.help"
+ } >expect &&
test_expect_code 129 git rev-parse --parseopt -- --does-not-exist 1>/dev/null 2>output < optionspec &&
test_cmp expect output
'
@@ -282,4 +233,104 @@ test_expect_success 'test --parseopt --stuck-long and short option with unset op
test_cmp expect output
'
+test_expect_success 'test --parseopt help output: "wrapped" options normal "or:" lines' '
+ sed -e "s/^|//" >spec <<-\EOF &&
+ |cmd [--some-option]
+ | [--another-option]
+ |cmd [--yet-another-option]
+ |--
+ |h,help! show the help
+ EOF
+
+ sed -e "s/^|//" >expect <<-\END_EXPECT &&
+ |cat <<\EOF
+ |usage: cmd [--some-option]
+ | or: [--another-option]
+ | or: cmd [--yet-another-option]
+ |
+ | -h, --help show the help
+ |
+ |EOF
+ END_EXPECT
+
+ test_must_fail git rev-parse --parseopt -- -h <spec >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'test --parseopt invalid opt-spec' '
+ test_write_lines x -- "=, x" >spec &&
+ echo "fatal: missing opt-spec before option flags" >expect &&
+ test_must_fail git rev-parse --parseopt -- <spec 2>err &&
+ test_cmp expect err
+'
+
+test_expect_success 'test --parseopt help output: multi-line blurb after empty line' '
+ sed -e "s/^|//" >spec <<-\EOF &&
+ |cmd [--some-option]
+ | [--another-option]
+ |
+ |multi
+ |line
+ |blurb
+ |--
+ |h,help! show the help
+ EOF
+
+ sed -e "s/^|//" >expect <<-\END_EXPECT &&
+ |cat <<\EOF
+ |usage: cmd [--some-option]
+ | or: [--another-option]
+ |
+ | multi
+ | line
+ | blurb
+ |
+ | -h, --help show the help
+ |
+ |EOF
+ END_EXPECT
+
+ test_must_fail git rev-parse --parseopt -- -h <spec >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'test --parseopt help output for optionspec-neg' '
+ test_expect_code 129 git rev-parse --parseopt -- \
+ -h >output <"$TEST_DIRECTORY/t1502/optionspec-neg" &&
+ test_cmp "$TEST_DIRECTORY/t1502/optionspec-neg.help" output
+'
+
+test_expect_success 'test --parseopt valid options for optionspec-neg' '
+ cat >expect <<-\EOF &&
+ set -- --foo --no-foo --no-bar --positive-only --no-negative --
+ EOF
+ git rev-parse --parseopt -- <"$TEST_DIRECTORY/t1502/optionspec-neg" >output \
+ --foo --no-foo --no-bar --positive-only --no-negative &&
+ test_cmp expect output
+'
+
+test_expect_success 'test --parseopt positivated option for optionspec-neg' '
+ cat >expect <<-\EOF &&
+ set -- --no-no-bar --no-no-bar --
+ EOF
+ git rev-parse --parseopt -- <"$TEST_DIRECTORY/t1502/optionspec-neg" >output \
+ --no-no-bar --bar &&
+ test_cmp expect output
+'
+
+check_invalid_long_option optionspec-neg --no-positive-only
+check_invalid_long_option optionspec-neg --negative
+check_invalid_long_option optionspec-neg --no-no-negative
+
+test_expect_success 'ambiguous: --no matches both --noble and --no-noble' '
+ cat >spec <<-\EOF &&
+ some-command [options]
+ --
+ noble The feudal switch.
+ EOF
+ test_expect_code 129 env GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
+ git rev-parse --parseopt -- <spec 2>err --no &&
+ grep "error: ambiguous option: no (could be --noble or --no-noble)" err
+'
+
test_done
diff --git a/t/t1502/.gitattributes b/t/t1502/.gitattributes
new file mode 100644
index 0000000..562b12e
--- /dev/null
+++ b/t/t1502/.gitattributes
@@ -0,0 +1 @@
+* -whitespace
diff --git a/t/t1502/optionspec-neg b/t/t1502/optionspec-neg
new file mode 100644
index 0000000..392f43e
--- /dev/null
+++ b/t/t1502/optionspec-neg
@@ -0,0 +1,8 @@
+some-command [options] <args>...
+
+some-command does foo and bar!
+--
+foo can be negated
+no-bar can be positivated
+positive-only! cannot be negated
+no-negative! cannot be positivated
diff --git a/t/t1502/optionspec-neg.help b/t/t1502/optionspec-neg.help
new file mode 100644
index 0000000..7a29f8c
--- /dev/null
+++ b/t/t1502/optionspec-neg.help
@@ -0,0 +1,12 @@
+cat <<\EOF
+usage: some-command [options] <args>...
+
+ some-command does foo and bar!
+
+ --[no-]foo can be negated
+ --no-bar can be positivated
+ --bar opposite of --no-bar
+ --positive-only cannot be negated
+ --no-negative cannot be positivated
+
+EOF
diff --git a/t/t1502/optionspec.help b/t/t1502/optionspec.help
new file mode 100755
index 0000000..cbdd54d
--- /dev/null
+++ b/t/t1502/optionspec.help
@@ -0,0 +1,36 @@
+cat <<\EOF
+usage: some-command [options] <args>...
+
+ some-command does foo and bar!
+
+ -h, --help show the help
+ --[no-]foo some nifty option --foo
+ --[no-]bar ... some cool option --bar with an argument
+ -b, --[no-]baz a short and long option
+
+An option group Header
+ -C[...] option C with an optional argument
+ -d, --[no-]data[=...] short and long option with an optional argument
+
+Argument hints
+ -B <arg> short option required argument
+ --[no-]bar2 <arg> long option required argument
+ -e, --[no-]fuz <with-space>
+ short and long option required argument
+ -s[<some>] short option optional argument
+ --[no-]long[=<data>] long option optional argument
+ -g, --[no-]fluf[=<path>]
+ short and long option optional argument
+ --[no-]longest <very-long-argument-hint>
+ a very long argument hint
+ --[no-]pair <key=value>
+ with an equals sign in the hint
+ --[no-]aswitch help te=t contains? fl*g characters!`
+ --[no-]bswitch <hint> hint has trailing tab character
+ --[no-]cswitch switch has trailing tab character
+ --[no-]short-hint <a> with a one symbol hint
+
+Extras
+ --[no-]extra1 line above used to cause a segfault but no longer does
+
+EOF
diff --git a/t/t1503-rev-parse-verify.sh b/t/t1503-rev-parse-verify.sh
index 4095861..79df65e 100755
--- a/t/t1503-rev-parse-verify.sh
+++ b/t/t1503-rev-parse-verify.sh
@@ -9,6 +9,7 @@ exec </dev/null
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
add_line_into_file()
@@ -132,7 +133,8 @@ test_expect_success 'use --default' '
'
test_expect_success 'main@{n} for various n' '
- N=$(git reflog | wc -l) &&
+ git reflog >out &&
+ N=$(wc -l <out) &&
Nm1=$(($N-1)) &&
Np1=$(($N+1)) &&
git rev-parse --verify main@{0} &&
@@ -142,11 +144,6 @@ test_expect_success 'main@{n} for various n' '
test_must_fail git rev-parse --verify main@{$Np1}
'
-test_expect_success SYMLINKS,REFFILES 'ref resolution not confused by broken symlinks' '
- ln -s does-not-exist .git/refs/heads/broken &&
- test_must_fail git rev-parse --verify broken
-'
-
test_expect_success 'options can appear after --verify' '
git rev-parse --verify HEAD >expect &&
git rev-parse --verify -q HEAD >actual &&
diff --git a/t/t1504-ceiling-dirs.sh b/t/t1504-ceiling-dirs.sh
index 3d51615..c1679e3 100755
--- a/t/t1504-ceiling-dirs.sh
+++ b/t/t1504-ceiling-dirs.sh
@@ -1,11 +1,17 @@
#!/bin/sh
test_description='test GIT_CEILING_DIRECTORIES'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_prefix() {
- test_expect_success "$1" \
- "test '$2' = \"\$(git rev-parse --show-prefix)\""
+ local expect="$2" &&
+ test_expect_success "$1: git rev-parse --show-prefix is '$2'" '
+ echo "$expect" >expect &&
+ git rev-parse --show-prefix >actual &&
+ test_cmp expect actual
+ '
}
test_fail() {
diff --git a/t/t1505-rev-parse-last.sh b/t/t1505-rev-parse-last.sh
index 2803ca9..4a5758f 100755
--- a/t/t1505-rev-parse-last.sh
+++ b/t/t1505-rev-parse-last.sh
@@ -5,6 +5,7 @@ test_description='test @{-N} syntax'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
diff --git a/t/t1506-rev-parse-diagnosis.sh b/t/t1506-rev-parse-diagnosis.sh
index 65a154a..ef40511 100755
--- a/t/t1506-rev-parse-diagnosis.sh
+++ b/t/t1506-rev-parse-diagnosis.sh
@@ -7,6 +7,7 @@ exec </dev/null
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_did_you_mean ()
@@ -106,16 +107,16 @@ test_expect_success 'correct relative file objects (6)' '
test_expect_success 'incorrect revision id' '
test_must_fail git rev-parse foobar:file.txt 2>error &&
- test_i18ngrep "invalid object name .foobar." error &&
+ test_grep "invalid object name .foobar." error &&
test_must_fail git rev-parse foobar 2>error &&
- test_i18ngrep "unknown revision or path not in the working tree." error
+ test_grep "unknown revision or path not in the working tree." error
'
test_expect_success 'incorrect file in sha1:path' '
test_must_fail git rev-parse HEAD:nothing.txt 2>error &&
- test_i18ngrep "path .nothing.txt. does not exist in .HEAD." error &&
+ test_grep "path .nothing.txt. does not exist in .HEAD." error &&
test_must_fail git rev-parse HEAD:index-only.txt 2>error &&
- test_i18ngrep "path .index-only.txt. exists on disk, but not in .HEAD." error &&
+ test_grep "path .index-only.txt. exists on disk, but not in .HEAD." error &&
(cd subdir &&
test_must_fail git rev-parse HEAD:file2.txt 2>error &&
test_did_you_mean HEAD subdir/ file2.txt exists )
@@ -123,9 +124,9 @@ test_expect_success 'incorrect file in sha1:path' '
test_expect_success 'incorrect file in :path and :N:path' '
test_must_fail git rev-parse :nothing.txt 2>error &&
- test_i18ngrep "path .nothing.txt. does not exist (neither on disk nor in the index)" error &&
+ test_grep "path .nothing.txt. does not exist (neither on disk nor in the index)" error &&
test_must_fail git rev-parse :1:nothing.txt 2>error &&
- test_i18ngrep "path .nothing.txt. does not exist (neither on disk nor in the index)" error &&
+ test_grep "path .nothing.txt. does not exist (neither on disk nor in the index)" error &&
test_must_fail git rev-parse :1:file.txt 2>error &&
test_did_you_mean ":0" "" file.txt "is in the index" "at stage 1" &&
(cd subdir &&
@@ -136,42 +137,42 @@ test_expect_success 'incorrect file in :path and :N:path' '
test_must_fail git rev-parse :2:file2.txt 2>error &&
test_did_you_mean :0 subdir/ file2.txt "is in the index") &&
test_must_fail git rev-parse :disk-only.txt 2>error &&
- test_i18ngrep "path .disk-only.txt. exists on disk, but not in the index" error
+ test_grep "path .disk-only.txt. exists on disk, but not in the index" error
'
test_expect_success 'invalid @{n} reference' '
test_must_fail git rev-parse main@{99999} >output 2>error &&
test_must_be_empty output &&
- test_i18ngrep "log for [^ ]* only has [0-9][0-9]* entries" error &&
+ test_grep "log for [^ ]* only has [0-9][0-9]* entries" error &&
test_must_fail git rev-parse --verify main@{99999} >output 2>error &&
test_must_be_empty output &&
- test_i18ngrep "log for [^ ]* only has [0-9][0-9]* entries" error
+ test_grep "log for [^ ]* only has [0-9][0-9]* entries" error
'
test_expect_success 'relative path not found' '
(
cd subdir &&
test_must_fail git rev-parse HEAD:./nonexistent.txt 2>error &&
- test_i18ngrep subdir/nonexistent.txt error
+ test_grep subdir/nonexistent.txt error
)
'
test_expect_success 'relative path outside worktree' '
test_must_fail git rev-parse HEAD:../file.txt >output 2>error &&
test_must_be_empty output &&
- test_i18ngrep "outside repository" error
+ test_grep "outside repository" error
'
test_expect_success 'relative path when cwd is outside worktree' '
test_must_fail git --git-dir=.git --work-tree=subdir rev-parse HEAD:./file.txt >output 2>error &&
test_must_be_empty output &&
- test_i18ngrep "relative path syntax can.t be used outside working tree" error
+ test_grep "relative path syntax can.t be used outside working tree" error
'
test_expect_success '<commit>:file correctly diagnosed after a pathname' '
test_must_fail git rev-parse file.txt HEAD:file.txt 1>actual 2>error &&
- test_i18ngrep ! "exists on disk" error &&
- test_i18ngrep "no such path in the working tree" error &&
+ test_grep ! "exists on disk" error &&
+ test_grep "no such path in the working tree" error &&
cat >expect <<-\EOF &&
file.txt
HEAD:file.txt
@@ -213,13 +214,13 @@ test_expect_success 'dotdot does not peel endpoints' '
test_expect_success 'arg before dashdash must be a revision (missing)' '
test_must_fail git rev-parse foobar -- 2>stderr &&
- test_i18ngrep "bad revision" stderr
+ test_grep "bad revision" stderr
'
test_expect_success 'arg before dashdash must be a revision (file)' '
>foobar &&
test_must_fail git rev-parse foobar -- 2>stderr &&
- test_i18ngrep "bad revision" stderr
+ test_grep "bad revision" stderr
'
test_expect_success 'arg before dashdash must be a revision (ambiguous)' '
@@ -268,7 +269,7 @@ test_expect_success 'arg after dashdash not interpreted as option' '
test_expect_success 'arg after end-of-options not interpreted as option' '
test_must_fail git rev-parse --end-of-options --not-real -- 2>err &&
- test_i18ngrep bad.revision.*--not-real err
+ test_grep bad.revision.*--not-real err
'
test_expect_success 'end-of-options still allows --' '
diff --git a/t/t1507-rev-parse-upstream.sh b/t/t1507-rev-parse-upstream.sh
index c34714f..b9af6b3 100755
--- a/t/t1507-rev-parse-upstream.sh
+++ b/t/t1507-rev-parse-upstream.sh
@@ -5,6 +5,7 @@ test_description='test <branch>@{upstream} syntax'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
@@ -97,7 +98,8 @@ test_expect_success 'my-side@{u} resolves to correct commit' '
commit_subject my-side >actual &&
test_cmp expect actual &&
echo 5 >expect &&
- commit_subject my-side@{u} >actual
+ commit_subject my-side@{u} >actual &&
+ test_cmp expect actual
'
test_expect_success 'not-tracking@{u} fails' '
@@ -183,6 +185,11 @@ test_expect_success '@{u} error message when no upstream' '
test_cmp expect actual
'
+test_expect_success '@{u} silent error when no upstream' '
+ test_must_fail git rev-parse --verify --quiet @{u} 2>actual &&
+ test_must_be_empty actual
+'
+
test_expect_success 'branch@{u} error message with misspelt branch' '
cat >expect <<-EOF &&
fatal: no such branch: ${SQ}no-such-branch${SQ}
@@ -258,7 +265,8 @@ test_expect_success '@{reflog}-parsing does not look beyond colon' '
git add @{yesterday} &&
git commit -m "funny reflog file" &&
git hash-object @{yesterday} >expect &&
- git rev-parse HEAD:@{yesterday} >actual
+ git rev-parse HEAD:@{yesterday} >actual &&
+ test_cmp expect actual
'
test_expect_success '@{upstream}-parsing does not look beyond colon' '
@@ -266,7 +274,8 @@ test_expect_success '@{upstream}-parsing does not look beyond colon' '
git add @{upstream} &&
git commit -m "funny upstream file" &&
git hash-object @{upstream} >expect &&
- git rev-parse HEAD:@{upstream} >actual
+ git rev-parse HEAD:@{upstream} >actual &&
+ test_cmp expect actual
'
test_done
diff --git a/t/t1508-at-combinations.sh b/t/t1508-at-combinations.sh
index 87a4286..e841309 100755
--- a/t/t1508-at-combinations.sh
+++ b/t/t1508-at-combinations.sh
@@ -4,6 +4,7 @@ test_description='test various @{X} syntax combinations together'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
check() {
diff --git a/t/t1509-root-work-tree.sh b/t/t1509-root-work-tree.sh
index 553a3f6..c799f5b 100755
--- a/t/t1509-root-work-tree.sh
+++ b/t/t1509-root-work-tree.sh
@@ -221,7 +221,8 @@ test_expect_success 'setup' '
rm -rf /.git &&
echo "Initialized empty Git repository in /.git/" > expected &&
git init > result &&
- test_cmp expected result
+ test_cmp expected result &&
+ git config --global --add safe.directory /
'
test_vars 'auto gitdir, root' ".git" "/" ""
@@ -242,7 +243,7 @@ say "auto bare gitdir"
# DESTROYYYYY!!!!!
test_expect_success 'setup' '
rm -rf /refs /objects /info /hooks &&
- rm -f /expected /ls.expected /me /result &&
+ rm -f /HEAD /expected /ls.expected /me /result &&
cd / &&
echo "Initialized empty Git repository in /" > expected &&
git init --bare > result &&
@@ -255,4 +256,9 @@ test_expect_success 'go to /foo' 'cd /foo'
test_vars 'auto gitdir, root' "/" "" ""
+test_expect_success 'cleanup root' '
+ rm -rf /.git /refs /objects /info /hooks /branches /foo &&
+ rm -f /HEAD /config /description /expected /ls.expected /me /result
+'
+
test_done
diff --git a/t/t1509/prepare-chroot.sh b/t/t1509/prepare-chroot.sh
index 6d47e2c..dc997e0 100755
--- a/t/t1509/prepare-chroot.sh
+++ b/t/t1509/prepare-chroot.sh
@@ -43,7 +43,7 @@ rsync --exclude-from t/t1509/excludes -Ha . "$R$(pwd)"
# env might slip through, see test-lib.sh, unset.*PERL_PATH
sed 's|^PERL_PATH=.*|PERL_PATH=/bin/true|' GIT-BUILD-OPTIONS > "$R$(pwd)/GIT-BUILD-OPTIONS"
for cmd in git $BB;do
- ldd $cmd | grep '/' | sed 's,.*\s\(/[^ ]*\).*,\1,' | while read i; do
+ ldd $cmd | sed -n '/\//s,.*\s\(/[^ ]*\).*,\1,p' | while read i; do
mkdir -p "$R$(dirname $i)"
cp "$i" "$R/$i"
done
diff --git a/t/t1510-repo-setup.sh b/t/t1510-repo-setup.sh
index bbfe05b..591505a 100755
--- a/t/t1510-repo-setup.sh
+++ b/t/t1510-repo-setup.sh
@@ -43,6 +43,7 @@ A few rules for repo setup:
# This test heavily relies on the standard error of nested function calls.
test_untraceable=UnfortunatelyYes
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
here=$(pwd)
diff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh
index 7891a6b..70f1e0a 100755
--- a/t/t1512-rev-parse-disambiguation.sh
+++ b/t/t1512-rev-parse-disambiguation.sh
@@ -25,6 +25,87 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
+test_cmp_failed_rev_parse () {
+ dir=$1
+ rev=$2
+
+ cat >expect &&
+ test_must_fail git -C "$dir" rev-parse "$rev" 2>actual.raw &&
+ sed "s/\($rev\)[0-9a-f]*/\1.../" <actual.raw >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'ambiguous blob output' '
+ git init --bare blob.prefix &&
+ (
+ cd blob.prefix &&
+
+ # Both start with "dead..", under both SHA-1 and SHA-256
+ echo brocdnra | git hash-object -w --stdin &&
+ echo brigddsv | git hash-object -w --stdin &&
+
+ # Both start with "beef.."
+ echo 1agllotbh | git hash-object -w --stdin &&
+ echo 1bbfctrkc | git hash-object -w --stdin
+ ) &&
+
+ test_must_fail git -C blob.prefix rev-parse dead &&
+ test_cmp_failed_rev_parse blob.prefix beef <<-\EOF
+ error: short object ID beef... is ambiguous
+ hint: The candidates are:
+ hint: beef... blob
+ hint: beef... blob
+ fatal: ambiguous argument '\''beef...'\'': unknown revision or path not in the working tree.
+ Use '\''--'\'' to separate paths from revisions, like this:
+ '\''git <command> [<revision>...] -- [<file>...]'\''
+ EOF
+'
+
+test_expect_success 'ambiguous loose bad object parsed as OBJ_BAD' '
+ git init --bare blob.bad &&
+ (
+ cd blob.bad &&
+
+ # Both have the prefix "bad0"
+ echo xyzfaowcoh | git hash-object -t bad -w --stdin --literally &&
+ echo xyzhjpyvwl | git hash-object -t bad -w --stdin --literally
+ ) &&
+
+ test_cmp_failed_rev_parse blob.bad bad0 <<-\EOF
+ error: short object ID bad0... is ambiguous
+ fatal: invalid object type
+ EOF
+'
+
+test_expect_success POSIXPERM 'ambigous zlib corrupt loose blob' '
+ git init --bare blob.corrupt &&
+ (
+ cd blob.corrupt &&
+
+ # Both have the prefix "cafe"
+ echo bnkxmdwz | git hash-object -w --stdin &&
+ oid=$(echo bmwsjxzi | git hash-object -w --stdin) &&
+
+ oidf=objects/$(test_oid_to_path "$oid") &&
+ chmod 755 $oidf &&
+ echo broken >$oidf
+ ) &&
+
+ test_cmp_failed_rev_parse blob.corrupt cafe <<-\EOF
+ error: short object ID cafe... is ambiguous
+ error: inflate: data stream error (incorrect header check)
+ error: unable to unpack cafe... header
+ error: inflate: data stream error (incorrect header check)
+ error: unable to unpack cafe... header
+ hint: The candidates are:
+ hint: cafe... [bad object]
+ hint: cafe... blob
+ fatal: ambiguous argument '\''cafe...'\'': unknown revision or path not in the working tree.
+ Use '\''--'\'' to separate paths from revisions, like this:
+ '\''git <command> [<revision>...] -- [<file>...]'\''
+ EOF
+'
+
if ! test_have_prereq SHA1
then
skip_all='not using SHA-1 for objects'
@@ -34,10 +115,7 @@ fi
test_expect_success 'blob and tree' '
test_tick &&
(
- for i in 0 1 2 3 4 5 6 7 8 9
- do
- echo $i
- done &&
+ test_write_lines 0 1 2 3 4 5 6 7 8 9 &&
echo &&
echo b1rwzyc3
) >a0blgqsjc &&
@@ -51,7 +129,7 @@ test_expect_success 'blob and tree' '
test_expect_success 'warn ambiguity when no candidate matches type hint' '
test_must_fail git rev-parse --verify 000000000^{commit} 2>actual &&
- test_i18ngrep "short object ID 000000000 is ambiguous" actual
+ test_grep "short object ID 000000000 is ambiguous" actual
'
test_expect_success 'disambiguate tree-ish' '
@@ -204,10 +282,7 @@ test_expect_success 'more history' '
git checkout v1.0.0^0 &&
git mv a0blgqsjc f5518nwu &&
- for i in h62xsjeu j08bekfvt kg7xflhm
- do
- echo $i
- done >>f5518nwu &&
+ test_write_lines h62xsjeu j08bekfvt kg7xflhm >>f5518nwu &&
git add f5518nwu &&
test_tick &&
@@ -387,7 +462,7 @@ test_expect_success 'ambiguous commits are printed by type first, then hash orde
do
grep $type objects >$type.objects &&
sort $type.objects >$type.objects.sorted &&
- test_cmp $type.objects.sorted $type.objects
+ test_cmp $type.objects.sorted $type.objects || return 1
done
'
@@ -395,10 +470,10 @@ test_expect_success 'cat-file --batch and --batch-check show ambiguous' '
echo "0000 ambiguous" >expect &&
echo 0000 | git cat-file --batch-check >actual 2>err &&
test_cmp expect actual &&
- test_i18ngrep hint: err &&
+ test_grep hint: err &&
echo 0000 | git cat-file --batch >actual 2>err &&
test_cmp expect actual &&
- test_i18ngrep hint: err
+ test_grep hint: err
'
test_done
diff --git a/t/t1513-rev-parse-prefix.sh b/t/t1513-rev-parse-prefix.sh
index 5f437be..ba43387 100755
--- a/t/t1513-rev-parse-prefix.sh
+++ b/t/t1513-rev-parse-prefix.sh
@@ -5,6 +5,7 @@ test_description='Tests for rev-parse --prefix'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t1514-rev-parse-push.sh b/t/t1514-rev-parse-push.sh
index d868a08..a835a19 100755
--- a/t/t1514-rev-parse-push.sh
+++ b/t/t1514-rev-parse-push.sh
@@ -4,6 +4,7 @@ test_description='test <branch>@{push} syntax'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
resolve () {
diff --git a/t/t1515-rev-parse-outside-repo.sh b/t/t1515-rev-parse-outside-repo.sh
index 3ec2971..cdb26a3 100755
--- a/t/t1515-rev-parse-outside-repo.sh
+++ b/t/t1515-rev-parse-outside-repo.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='check that certain rev-parse options work outside repo'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'set up non-repo directory' '
diff --git a/t/t1600-index.sh b/t/t1600-index.sh
index c9b9e71..62e7fd1 100755
--- a/t/t1600-index.sh
+++ b/t/t1600-index.sh
@@ -2,8 +2,11 @@
test_description='index file specific tests'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
+sane_unset GIT_TEST_SPLIT_INDEX
+
test_expect_success 'setup' '
echo 1 >a
'
@@ -13,7 +16,8 @@ test_expect_success 'bogus GIT_INDEX_VERSION issues warning' '
rm -f .git/index &&
GIT_INDEX_VERSION=2bogus &&
export GIT_INDEX_VERSION &&
- git add a 2>&1 | sed "s/[0-9]//" >actual.err &&
+ git add a 2>err &&
+ sed "s/[0-9]//" err >actual.err &&
sed -e "s/ Z$/ /" <<-\EOF >expect.err &&
warning: GIT_INDEX_VERSION set, but the value is invalid.
Using version Z
@@ -27,7 +31,8 @@ test_expect_success 'out of bounds GIT_INDEX_VERSION issues warning' '
rm -f .git/index &&
GIT_INDEX_VERSION=1 &&
export GIT_INDEX_VERSION &&
- git add a 2>&1 | sed "s/[0-9]//" >actual.err &&
+ git add a 2>err &&
+ sed "s/[0-9]//" err >actual.err &&
sed -e "s/ Z$/ /" <<-\EOF >expect.err &&
warning: GIT_INDEX_VERSION set, but the value is invalid.
Using version Z
@@ -50,7 +55,8 @@ test_expect_success 'out of bounds index.version issues warning' '
sane_unset GIT_INDEX_VERSION &&
rm -f .git/index &&
git config --add index.version 1 &&
- git add a 2>&1 | sed "s/[0-9]//" >actual.err &&
+ git add a 2>err &&
+ sed "s/[0-9]//" err >actual.err &&
sed -e "s/ Z$/ /" <<-\EOF >expect.err &&
warning: index.version set, but the value is invalid.
Using version Z
@@ -59,6 +65,37 @@ test_expect_success 'out of bounds index.version issues warning' '
)
'
+test_expect_success 'index.skipHash config option' '
+ rm -f .git/index &&
+ git -c index.skipHash=true add a &&
+ test_trailing_hash .git/index >hash &&
+ echo $(test_oid zero) >expect &&
+ test_cmp expect hash &&
+ git fsck &&
+
+ rm -f .git/index &&
+ git -c feature.manyFiles=true add a &&
+ test_trailing_hash .git/index >hash &&
+ cmp expect hash &&
+
+ rm -f .git/index &&
+ git -c feature.manyFiles=true \
+ -c index.skipHash=false add a &&
+ test_trailing_hash .git/index >hash &&
+ ! cmp expect hash &&
+
+ test_commit start &&
+ git -c protocol.file.allow=always submodule add ./ sub &&
+ git config index.skipHash false &&
+ git -C sub config index.skipHash true &&
+ rm -f .git/modules/sub/index &&
+ >sub/file &&
+ git -C sub add a &&
+ test_trailing_hash .git/modules/sub/index >hash &&
+ test_cmp expect hash &&
+ git -C sub fsck
+'
+
test_index_version () {
INDEX_VERSION_CONFIG=$1 &&
FEATURE_MANY_FILES=$2 &&
@@ -79,9 +116,9 @@ test_index_version () {
else
unset GIT_INDEX_VERSION
fi &&
- git add a 2>&1 &&
+ git add a &&
echo $EXPECTED_OUTPUT_VERSION >expect &&
- test-tool index-version <.git/index >actual &&
+ git update-index --show-index-version >actual &&
test_cmp expect actual
)
}
diff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh
index 986baa6..a7b7263 100755
--- a/t/t1700-split-index.sh
+++ b/t/t1700-split-index.sh
@@ -43,15 +43,15 @@ test_expect_success 'enable split index' '
git config splitIndex.maxPercentChange 100 &&
git update-index --split-index &&
test-tool dump-split-index .git/index >actual &&
- indexversion=$(test-tool index-version <.git/index) &&
+ indexversion=$(git update-index --show-index-version) &&
# NEEDSWORK: Stop hard-coding checksums.
if test "$indexversion" = "4"
then
- own=$(test_oid own_v4)
+ own=$(test_oid own_v4) &&
base=$(test_oid base_v4)
else
- own=$(test_oid own_v3)
+ own=$(test_oid own_v3) &&
base=$(test_oid base_v3)
fi &&
@@ -510,4 +510,38 @@ test_expect_success 'do not refresh null base index' '
)
'
+test_expect_success 'reading split index at alternate location' '
+ git init reading-alternate-location &&
+ (
+ cd reading-alternate-location &&
+ >file-in-alternate &&
+ git update-index --split-index --add file-in-alternate
+ ) &&
+ echo file-in-alternate >expect &&
+
+ # Should be able to find the shared index both right next to
+ # the specified split index file ...
+ GIT_INDEX_FILE=./reading-alternate-location/.git/index \
+ git ls-files --cached >actual &&
+ test_cmp expect actual &&
+
+ # ... and, for backwards compatibility, in the current GIT_DIR
+ # as well.
+ mv -v ./reading-alternate-location/.git/sharedindex.* .git &&
+ GIT_INDEX_FILE=./reading-alternate-location/.git/index \
+ git ls-files --cached >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'GIT_TEST_SPLIT_INDEX works' '
+ git init git-test-split-index &&
+ (
+ cd git-test-split-index &&
+ >file &&
+ GIT_TEST_SPLIT_INDEX=1 git update-index --add file &&
+ ls -l .git/sharedindex.* >actual &&
+ test_line_count = 1 actual
+ )
+'
+
test_done
diff --git a/t/t1701-racy-split-index.sh b/t/t1701-racy-split-index.sh
index 5dc221e..d8fa489 100755
--- a/t/t1701-racy-split-index.sh
+++ b/t/t1701-racy-split-index.sh
@@ -5,6 +5,7 @@
test_description='racy split index'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t1800-hook.sh b/t/t1800-hook.sh
new file mode 100755
index 0000000..8b0234c
--- /dev/null
+++ b/t/t1800-hook.sh
@@ -0,0 +1,188 @@
+#!/bin/sh
+
+test_description='git-hook command'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
+
+test_expect_success 'git hook usage' '
+ test_expect_code 129 git hook &&
+ test_expect_code 129 git hook run &&
+ test_expect_code 129 git hook run -h &&
+ test_expect_code 129 git hook run --unknown 2>err &&
+ grep "unknown option" err
+'
+
+test_expect_success 'git hook run: nonexistent hook' '
+ cat >stderr.expect <<-\EOF &&
+ error: cannot find a hook named test-hook
+ EOF
+ test_expect_code 1 git hook run test-hook 2>stderr.actual &&
+ test_cmp stderr.expect stderr.actual
+'
+
+test_expect_success 'git hook run: nonexistent hook with --ignore-missing' '
+ git hook run --ignore-missing does-not-exist 2>stderr.actual &&
+ test_must_be_empty stderr.actual
+'
+
+test_expect_success 'git hook run: basic' '
+ test_hook test-hook <<-EOF &&
+ echo Test hook
+ EOF
+
+ cat >expect <<-\EOF &&
+ Test hook
+ EOF
+ git hook run test-hook 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git hook run: stdout and stderr both write to our stderr' '
+ test_hook test-hook <<-EOF &&
+ echo >&1 Will end up on stderr
+ echo >&2 Will end up on stderr
+ EOF
+
+ cat >stderr.expect <<-\EOF &&
+ Will end up on stderr
+ Will end up on stderr
+ EOF
+ git hook run test-hook >stdout.actual 2>stderr.actual &&
+ test_cmp stderr.expect stderr.actual &&
+ test_must_be_empty stdout.actual
+'
+
+for code in 1 2 128 129
+do
+ test_expect_success "git hook run: exit code $code is passed along" '
+ test_hook test-hook <<-EOF &&
+ exit $code
+ EOF
+
+ test_expect_code $code git hook run test-hook
+ '
+done
+
+test_expect_success 'git hook run arg u ments without -- is not allowed' '
+ test_expect_code 129 git hook run test-hook arg u ments
+'
+
+test_expect_success 'git hook run -- pass arguments' '
+ test_hook test-hook <<-\EOF &&
+ echo $1
+ echo $2
+ EOF
+
+ cat >expect <<-EOF &&
+ arg
+ u ments
+ EOF
+
+ git hook run test-hook -- arg "u ments" 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git hook run -- out-of-repo runs excluded' '
+ test_hook test-hook <<-EOF &&
+ echo Test hook
+ EOF
+
+ nongit test_must_fail git hook run test-hook
+'
+
+test_expect_success 'git -c core.hooksPath=<PATH> hook run' '
+ mkdir my-hooks &&
+ write_script my-hooks/test-hook <<-\EOF &&
+ echo Hook ran $1
+ EOF
+
+ cat >expect <<-\EOF &&
+ Test hook
+ Hook ran one
+ Hook ran two
+ Hook ran three
+ Hook ran four
+ EOF
+
+ test_hook test-hook <<-EOF &&
+ echo Test hook
+ EOF
+
+ # Test various ways of specifying the path. See also
+ # t1350-config-hooks-path.sh
+ >actual &&
+ git hook run test-hook -- ignored 2>>actual &&
+ git -c core.hooksPath=my-hooks hook run test-hook -- one 2>>actual &&
+ git -c core.hooksPath=my-hooks/ hook run test-hook -- two 2>>actual &&
+ git -c core.hooksPath="$PWD/my-hooks" hook run test-hook -- three 2>>actual &&
+ git -c core.hooksPath="$PWD/my-hooks/" hook run test-hook -- four 2>>actual &&
+ test_cmp expect actual
+'
+
+test_hook_tty () {
+ cat >expect <<-\EOF
+ STDOUT TTY
+ STDERR TTY
+ EOF
+
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+
+ test_commit -C repo A &&
+ test_commit -C repo B &&
+ git -C repo reset --soft HEAD^ &&
+
+ test_hook -C repo pre-commit <<-EOF &&
+ test -t 1 && echo STDOUT TTY >>actual || echo STDOUT NO TTY >>actual &&
+ test -t 2 && echo STDERR TTY >>actual || echo STDERR NO TTY >>actual
+ EOF
+
+ test_terminal git -C repo "$@" &&
+ test_cmp expect repo/actual
+}
+
+test_expect_success TTY 'git hook run: stdout and stderr are connected to a TTY' '
+ test_hook_tty hook run pre-commit
+'
+
+test_expect_success TTY 'git commit: stdout and stderr are connected to a TTY' '
+ test_hook_tty commit -m"B.new"
+'
+
+test_expect_success 'git hook run a hook with a bad shebang' '
+ test_when_finished "rm -rf bad-hooks" &&
+ mkdir bad-hooks &&
+ write_script bad-hooks/test-hook "/bad/path/no/spaces" </dev/null &&
+
+ test_expect_code 1 git \
+ -c core.hooksPath=bad-hooks \
+ hook run test-hook >out 2>err &&
+ test_must_be_empty out &&
+
+ # TODO: We should emit the same (or at least a more similar)
+ # error on MINGW (essentially Git for Windows) and all other
+ # platforms.. See the OS-specific code in start_command()
+ grep -E "^(error|fatal): cannot (exec|spawn) .*bad-hooks/test-hook" err
+'
+
+test_expect_success 'stdin to hooks' '
+ write_script .git/hooks/test-hook <<-\EOF &&
+ echo BEGIN stdin
+ cat
+ echo END stdin
+ EOF
+
+ cat >expect <<-EOF &&
+ BEGIN stdin
+ hello
+ END stdin
+ EOF
+
+ echo hello >input &&
+ git hook run --to-stdin=input test-hook 2>actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t2000-conflict-when-checking-files-out.sh b/t/t2000-conflict-when-checking-files-out.sh
index f18616a..79fc97f 100755
--- a/t/t2000-conflict-when-checking-files-out.sh
+++ b/t/t2000-conflict-when-checking-files-out.sh
@@ -21,6 +21,7 @@ test_description='git conflicts when checking files out test.'
# path1 is occupied by a non-directory. With "-f" flag, it should remove
# the conflicting paths and succeed.
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
show_files() {
diff --git a/t/t2002-checkout-cache-u.sh b/t/t2002-checkout-cache-u.sh
index 70361c8..fc95cf9 100755
--- a/t/t2002-checkout-cache-u.sh
+++ b/t/t2002-checkout-cache-u.sh
@@ -8,6 +8,7 @@ test_description='git checkout-index -u test.
With -u flag, git checkout-index internally runs the equivalent of
git update-index --refresh on the checked out entry.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success \
diff --git a/t/t2003-checkout-cache-mkdir.sh b/t/t2003-checkout-cache-mkdir.sh
index ff163cf..f0fd441 100755
--- a/t/t2003-checkout-cache-mkdir.sh
+++ b/t/t2003-checkout-cache-mkdir.sh
@@ -10,6 +10,7 @@ also verifies that such leading path may contain symlinks, unlike
the GIT controlled paths.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t2004-checkout-cache-temp.sh b/t/t2004-checkout-cache-temp.sh
index a9352b0..98e818f 100755
--- a/t/t2004-checkout-cache-temp.sh
+++ b/t/t2004-checkout-cache-temp.sh
@@ -8,6 +8,7 @@ test_description='git checkout-index --temp test.
With --temp flag, git checkout-index writes to temporary merge files
rather than the tracked path.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -56,7 +57,7 @@ test_expect_success 'checkout all stage 0 to temporary files' '
test $(grep $f actual | cut "-d " -f2) = $f &&
p=$(grep $f actual | cut "-d " -f1) &&
test -f $p &&
- test $(cat $p) = tree1$f
+ test $(cat $p) = tree1$f || return 1
done
'
@@ -84,7 +85,7 @@ test_expect_success 'checkout all stage 2 to temporary files' '
test $(grep $f actual | cut "-d " -f2) = $f &&
p=$(grep $f actual | cut "-d " -f1) &&
test -f $p &&
- test $(cat $p) = tree2$f
+ test $(cat $p) = tree2$f || return 1
done
'
@@ -92,7 +93,7 @@ test_expect_success 'checkout all stages of unknown path' '
rm -f path* .merge_* actual &&
test_must_fail git checkout-index --stage=all --temp \
-- does-not-exist 2>stderr &&
- test_i18ngrep not.in.the.cache stderr
+ test_grep not.in.the.cache stderr
'
test_expect_success 'checkout all stages/one file to nothing' '
@@ -116,6 +117,26 @@ test_expect_success 'checkout all stages/one file to temporary files' '
test $(cat $s3) = tree3path1)
'
+test_expect_success '--stage=all implies --temp' '
+ rm -f path* .merge_* actual &&
+ git checkout-index --stage=all -- path1 &&
+ test_path_is_missing path1
+'
+
+test_expect_success 'overriding --stage=all resets implied --temp' '
+ rm -f path* .merge_* actual &&
+ git checkout-index --stage=all --stage=2 -- path1 &&
+ echo tree2path1 >expect &&
+ test_cmp expect path1
+'
+
+test_expect_success '--stage=all --no-temp is rejected' '
+ rm -f path* .merge_* actual &&
+ test_must_fail git checkout-index --stage=all --no-temp -- path1 2>err &&
+ grep -v "already exists" err &&
+ grep "options .--stage=all. and .--no-temp. cannot be used together" err
+'
+
test_expect_success 'checkout some stages/one file to temporary files' '
rm -f path* .merge_* actual &&
git checkout-index --stage=all --temp -- path2 >actual &&
diff --git a/t/t2005-checkout-index-symlinks.sh b/t/t2005-checkout-index-symlinks.sh
index 9fa5610..67d18cf 100755
--- a/t/t2005-checkout-index-symlinks.sh
+++ b/t/t2005-checkout-index-symlinks.sh
@@ -8,6 +8,7 @@ test_description='git checkout-index on filesystem w/o symlinks test.
This tests that git checkout-index creates a symbolic link as a plain
file if core.symlinks is false.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success \
@@ -21,8 +22,10 @@ test_expect_success \
git checkout-index symlink &&
test -f symlink'
-test_expect_success \
-'the file must be the blob we added during the setup' '
-test "$(git hash-object -t blob symlink)" = $l'
+test_expect_success 'the file must be the blob we added during the setup' '
+ echo "$l" >expect &&
+ git hash-object -t blob symlink >actual &&
+ test_cmp expect actual
+'
test_done
diff --git a/t/t2006-checkout-index-basic.sh b/t/t2006-checkout-index-basic.sh
index 7705e3a..570ba38 100755
--- a/t/t2006-checkout-index-basic.sh
+++ b/t/t2006-checkout-index-basic.sh
@@ -3,11 +3,12 @@
test_description='basic checkout-index tests
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'checkout-index --gobbledegook' '
test_expect_code 129 git checkout-index --gobbledegook 2>err &&
- test_i18ngrep "[Uu]sage" err
+ test_grep "[Uu]sage" err
'
test_expect_success 'checkout-index -h in broken repository' '
@@ -18,18 +19,18 @@ test_expect_success 'checkout-index -h in broken repository' '
>.git/index &&
test_expect_code 129 git checkout-index -h >usage 2>&1
) &&
- test_i18ngrep "[Uu]sage" broken/usage
+ test_grep "[Uu]sage" broken/usage
'
test_expect_success 'checkout-index reports errors (cmdline)' '
test_must_fail git checkout-index -- does-not-exist 2>stderr &&
- test_i18ngrep not.in.the.cache stderr
+ test_grep not.in.the.cache stderr
'
test_expect_success 'checkout-index reports errors (stdin)' '
echo does-not-exist |
test_must_fail git checkout-index --stdin 2>stderr &&
- test_i18ngrep not.in.the.cache stderr
+ test_grep not.in.the.cache stderr
'
for mode in 'case' 'utf-8'
do
@@ -87,8 +88,8 @@ test_expect_success 'checkout-index --temp correctly reports error on missing bl
git update-index --index-info <objs &&
test_must_fail git checkout-index --temp symlink file 2>stderr &&
- test_i18ngrep "unable to read sha1 file of file ($missing_blob)" stderr &&
- test_i18ngrep "unable to read sha1 file of symlink ($missing_blob)" stderr
+ test_grep "unable to read sha1 file of file ($missing_blob)" stderr &&
+ test_grep "unable to read sha1 file of symlink ($missing_blob)" stderr
'
test_expect_success 'checkout-index --temp correctly reports error for submodules' '
@@ -97,7 +98,7 @@ test_expect_success 'checkout-index --temp correctly reports error for submodule
git submodule add ./sub &&
git commit -m sub &&
test_must_fail git checkout-index --temp sub 2>stderr &&
- test_i18ngrep "cannot create temporary submodule sub" stderr
+ test_grep "cannot create temporary submodule sub" stderr
'
test_done
diff --git a/t/t2007-checkout-symlink.sh b/t/t2007-checkout-symlink.sh
index 6f0b90c..bd9e9e7 100755
--- a/t/t2007-checkout-symlink.sh
+++ b/t/t2007-checkout-symlink.sh
@@ -7,6 +7,7 @@ test_description='git checkout to switch between branches with symlink<->dir'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t2008-checkout-subdir.sh b/t/t2008-checkout-subdir.sh
index eadb943..8a518a4 100755
--- a/t/t2008-checkout-subdir.sh
+++ b/t/t2008-checkout-subdir.sh
@@ -4,6 +4,7 @@
test_description='git checkout from subdirectories'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t2009-checkout-statinfo.sh b/t/t2009-checkout-statinfo.sh
index b054063..71195dd 100755
--- a/t/t2009-checkout-statinfo.sh
+++ b/t/t2009-checkout-statinfo.sh
@@ -5,6 +5,7 @@ test_description='checkout should leave clean stat info'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t2010-checkout-ambiguous.sh b/t/t2010-checkout-ambiguous.sh
index 6e87573..82c3bfe 100755
--- a/t/t2010-checkout-ambiguous.sh
+++ b/t/t2010-checkout-ambiguous.sh
@@ -5,6 +5,7 @@ test_description='checkout and pathspecs/refspecs ambiguities'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -61,8 +62,8 @@ test_expect_success 'disambiguate checking out from a tree-ish' '
test_expect_success 'accurate error message with more than one ref' '
test_must_fail git checkout HEAD main -- 2>actual &&
- test_i18ngrep 2 actual &&
- test_i18ngrep "one reference expected, 2 given" actual
+ test_grep 2 actual &&
+ test_grep "one reference expected, 2 given" actual
'
test_done
diff --git a/t/t2011-checkout-invalid-head.sh b/t/t2011-checkout-invalid-head.sh
index e52022e..04f53b1 100755
--- a/t/t2011-checkout-invalid-head.sh
+++ b/t/t2011-checkout-invalid-head.sh
@@ -5,6 +5,7 @@ test_description='checkout switching away from an invalid branch'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -17,12 +18,12 @@ test_expect_success 'checkout should not start branch from a tree' '
test_must_fail git checkout -b newbranch main^{tree}
'
-test_expect_success 'checkout main from invalid HEAD' '
+test_expect_success REFFILES 'checkout main from invalid HEAD' '
echo $ZERO_OID >.git/HEAD &&
git checkout main --
'
-test_expect_success 'checkout notices failure to lock HEAD' '
+test_expect_success REFFILES 'checkout notices failure to lock HEAD' '
test_when_finished "rm -f .git/HEAD.lock" &&
>.git/HEAD.lock &&
test_must_fail git checkout -b other
@@ -30,11 +31,8 @@ test_expect_success 'checkout notices failure to lock HEAD' '
test_expect_success 'create ref directory/file conflict scenario' '
git update-ref refs/heads/outer/inner main &&
-
- # do not rely on symbolic-ref to get a known state,
- # as it may use the same code we are testing
reset_to_df () {
- echo "ref: refs/heads/outer" >.git/HEAD
+ git symbolic-ref HEAD refs/heads/outer
}
'
diff --git a/t/t2012-checkout-last.sh b/t/t2012-checkout-last.sh
index 0e7d47a..4b6372f 100755
--- a/t/t2012-checkout-last.sh
+++ b/t/t2012-checkout-last.sh
@@ -5,6 +5,7 @@ test_description='checkout can switch to last branch and merge base'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -21,14 +22,20 @@ test_expect_success 'first branch switch' '
git checkout other
'
+test_cmp_symbolic_HEAD_ref () {
+ echo refs/heads/"$1" >expect &&
+ git symbolic-ref HEAD >actual &&
+ test_cmp expect actual
+}
+
test_expect_success '"checkout -" switches back' '
git checkout - &&
- test "z$(git symbolic-ref HEAD)" = "zrefs/heads/main"
+ test_cmp_symbolic_HEAD_ref main
'
test_expect_success '"checkout -" switches forth' '
git checkout - &&
- test "z$(git symbolic-ref HEAD)" = "zrefs/heads/other"
+ test_cmp_symbolic_HEAD_ref other
'
test_expect_success 'detach HEAD' '
@@ -37,57 +44,61 @@ test_expect_success 'detach HEAD' '
test_expect_success '"checkout -" attaches again' '
git checkout - &&
- test "z$(git symbolic-ref HEAD)" = "zrefs/heads/other"
+ test_cmp_symbolic_HEAD_ref other
'
test_expect_success '"checkout -" detaches again' '
git checkout - &&
- test "z$(git rev-parse HEAD)" = "z$(git rev-parse other)" &&
+
+ git rev-parse other >expect &&
+ git rev-parse HEAD >actual &&
+ test_cmp expect actual &&
+
test_must_fail git symbolic-ref HEAD
'
test_expect_success 'more switches' '
for i in 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
do
- git checkout -b branch$i
+ git checkout -b branch$i || return 1
done
'
more_switches () {
for i in 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
do
- git checkout branch$i
+ git checkout branch$i || return 1
done
}
test_expect_success 'switch to the last' '
more_switches &&
git checkout @{-1} &&
- test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch2"
+ test_cmp_symbolic_HEAD_ref branch2
'
test_expect_success 'switch to second from the last' '
more_switches &&
git checkout @{-2} &&
- test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch3"
+ test_cmp_symbolic_HEAD_ref branch3
'
test_expect_success 'switch to third from the last' '
more_switches &&
git checkout @{-3} &&
- test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch4"
+ test_cmp_symbolic_HEAD_ref branch4
'
test_expect_success 'switch to fourth from the last' '
more_switches &&
git checkout @{-4} &&
- test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch5"
+ test_cmp_symbolic_HEAD_ref branch5
'
test_expect_success 'switch to twelfth from the last' '
more_switches &&
git checkout @{-12} &&
- test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch13"
+ test_cmp_symbolic_HEAD_ref branch13
'
test_expect_success 'merge base test setup' '
@@ -98,19 +109,28 @@ test_expect_success 'merge base test setup' '
test_expect_success 'another...main' '
git checkout another &&
git checkout another...main &&
- test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify main^)"
+
+ git rev-parse --verify main^ >expect &&
+ git rev-parse --verify HEAD >actual &&
+ test_cmp expect actual
'
test_expect_success '...main' '
git checkout another &&
git checkout ...main &&
- test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify main^)"
+
+ git rev-parse --verify main^ >expect &&
+ git rev-parse --verify HEAD >actual &&
+ test_cmp expect actual
'
test_expect_success 'main...' '
git checkout another &&
git checkout main... &&
- test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify main^)"
+
+ git rev-parse --verify main^ >expect &&
+ git rev-parse --verify HEAD >actual &&
+ test_cmp expect actual
'
test_expect_success '"checkout -" works after a rebase A' '
@@ -118,7 +138,7 @@ test_expect_success '"checkout -" works after a rebase A' '
git checkout other &&
git rebase main &&
git checkout - &&
- test "z$(git symbolic-ref HEAD)" = "zrefs/heads/main"
+ test_cmp_symbolic_HEAD_ref main
'
test_expect_success '"checkout -" works after a rebase A B' '
@@ -127,7 +147,7 @@ test_expect_success '"checkout -" works after a rebase A B' '
git checkout other &&
git rebase main moodle &&
git checkout - &&
- test "z$(git symbolic-ref HEAD)" = "zrefs/heads/main"
+ test_cmp_symbolic_HEAD_ref main
'
test_expect_success '"checkout -" works after a rebase -i A' '
@@ -135,7 +155,7 @@ test_expect_success '"checkout -" works after a rebase -i A' '
git checkout other &&
git rebase -i main &&
git checkout - &&
- test "z$(git symbolic-ref HEAD)" = "zrefs/heads/main"
+ test_cmp_symbolic_HEAD_ref main
'
test_expect_success '"checkout -" works after a rebase -i A B' '
@@ -144,7 +164,7 @@ test_expect_success '"checkout -" works after a rebase -i A B' '
git checkout other &&
git rebase main foodle &&
git checkout - &&
- test "z$(git symbolic-ref HEAD)" = "zrefs/heads/main"
+ test_cmp_symbolic_HEAD_ref main
'
test_done
diff --git a/t/t2014-checkout-switch.sh b/t/t2014-checkout-switch.sh
index ccfb147..c138bdd 100755
--- a/t/t2014-checkout-switch.sh
+++ b/t/t2014-checkout-switch.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='Peter MacMillan'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t2015-checkout-unborn.sh b/t/t2015-checkout-unborn.sh
index a972121..fb0e138 100755
--- a/t/t2015-checkout-unborn.sh
+++ b/t/t2015-checkout-unborn.sh
@@ -4,15 +4,17 @@ test_description='checkout from unborn branch'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
mkdir parent &&
- (cd parent &&
- git init &&
- echo content >file &&
- git add file &&
- git commit -m base
+ (
+ cd parent &&
+ git init &&
+ echo content >file &&
+ git add file &&
+ git commit -m base
) &&
git fetch parent main:origin
'
diff --git a/t/t2016-checkout-patch.sh b/t/t2016-checkout-patch.sh
index abfd586..c40b661 100755
--- a/t/t2016-checkout-patch.sh
+++ b/t/t2016-checkout-patch.sh
@@ -2,9 +2,10 @@
test_description='git checkout --patch'
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-patch-mode.sh
-test_expect_success PERL 'setup' '
+test_expect_success 'setup' '
mkdir dir &&
echo parent > dir/foo &&
echo dummy > bar &&
@@ -18,66 +19,68 @@ test_expect_success PERL 'setup' '
# note: bar sorts before dir/foo, so the first 'n' is always to skip 'bar'
-# NEEDSWORK: Since the builtin add-p is used when $GIT_TEST_ADD_I_USE_BUILTIN
-# is given, we should replace the PERL prerequisite with an ADD_I prerequisite
-# which first checks if $GIT_TEST_ADD_I_USE_BUILTIN is defined before checking
-# PERL.
-test_expect_success PERL 'saying "n" does nothing' '
+test_expect_success 'saying "n" does nothing' '
set_and_save_state dir/foo work head &&
test_write_lines n n | git checkout -p &&
verify_saved_state bar &&
verify_saved_state dir/foo
'
-test_expect_success PERL 'git checkout -p' '
+test_expect_success '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' '
+test_expect_success 'git checkout -p with staged changes' '
set_state dir/foo work index &&
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 &&
- 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' '
- test_write_lines n y y | git checkout -p HEAD &&
- verify_saved_state bar &&
- verify_state dir/foo head head
-'
-
-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
- 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^...' '
+for opt in "HEAD" "@"
+do
+ test_expect_success "git checkout -p $opt with NO staged changes: abort" '
+ set_and_save_state dir/foo work head &&
+ test_write_lines n y n | git checkout -p $opt >output &&
+ verify_saved_state bar &&
+ verify_saved_state dir/foo &&
+ test_grep "Discard" output
+ '
+
+ test_expect_success "git checkout -p $opt with NO staged changes: apply" '
+ test_write_lines n y y | git checkout -p $opt >output &&
+ verify_saved_state bar &&
+ verify_state dir/foo head head &&
+ test_grep "Discard" output
+ '
+
+ test_expect_success "git checkout -p $opt with change already staged" '
+ set_state dir/foo index index &&
+ # the third n is to get out in case it mistakenly does not apply
+ test_write_lines n y n | git checkout -p $opt >output &&
+ verify_saved_state bar &&
+ verify_state dir/foo head head &&
+ test_grep "Discard" output
+ '
+done
+
+test_expect_success 'git checkout -p HEAD^...' '
# the third n is to get out in case it mistakenly does not apply
test_write_lines n y n | git checkout -p HEAD^... &&
verify_saved_state bar &&
verify_state dir/foo parent parent
'
-test_expect_success PERL 'git checkout -p HEAD^' '
+test_expect_success 'git checkout -p HEAD^' '
# the third n is to get out in case it mistakenly does not apply
test_write_lines n y n | git checkout -p HEAD^ &&
verify_saved_state bar &&
verify_state dir/foo parent parent
'
-test_expect_success PERL 'git checkout -p handles deletion' '
+test_expect_success 'git checkout -p handles deletion' '
set_state dir/foo work index &&
rm dir/foo &&
test_write_lines n y | git checkout -p &&
@@ -90,28 +93,28 @@ test_expect_success PERL 'git checkout -p handles deletion' '
# dir/foo. There's always an extra 'n' to reject edits to dir/foo in
# the failure case (and thus get out of the loop).
-test_expect_success PERL 'path limiting works: dir' '
+test_expect_success 'path limiting works: dir' '
set_state dir/foo work head &&
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' '
+test_expect_success 'path limiting works: -- dir' '
set_state dir/foo work head &&
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' '
+test_expect_success 'path limiting works: HEAD^ -- dir' '
# the third n is to get out in case it mistakenly does not apply
test_write_lines y n n | git checkout -p HEAD^ -- dir &&
verify_saved_state bar &&
verify_state dir/foo parent parent
'
-test_expect_success PERL 'path limiting works: foo inside dir' '
+test_expect_success '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
test_write_lines y n n | (cd dir && git checkout -p foo) &&
@@ -119,11 +122,11 @@ test_expect_success PERL 'path limiting works: foo inside dir' '
verify_state dir/foo head head
'
-test_expect_success PERL 'none of this moved HEAD' '
+test_expect_success 'none of this moved HEAD' '
verify_saved_head
'
-test_expect_success PERL 'empty tree can be handled' '
+test_expect_success 'empty tree can be handled' '
test_when_finished "git reset --hard" &&
git checkout -p $(test_oid empty_tree) --
'
diff --git a/t/t2017-checkout-orphan.sh b/t/t2017-checkout-orphan.sh
index 88d6992..a5c7358 100755
--- a/t/t2017-checkout-orphan.sh
+++ b/t/t2017-checkout-orphan.sh
@@ -10,6 +10,7 @@ Main Tests for --orphan functionality.'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
TEST_FILE=foo
@@ -62,8 +63,17 @@ test_expect_success '--orphan ignores branch.autosetupmerge' '
git checkout main &&
git config branch.autosetupmerge always &&
git checkout --orphan gamma &&
- test -z "$(git config branch.gamma.merge)" &&
+ test_cmp_config "" --default "" branch.gamma.merge &&
test refs/heads/gamma = "$(git symbolic-ref HEAD)" &&
+ test_must_fail git rev-parse --verify HEAD^ &&
+ git checkout main &&
+ git config branch.autosetupmerge inherit &&
+ git checkout --orphan eta &&
+ test_cmp_config "" --default "" branch.eta.merge &&
+ test_cmp_config "" --default "" branch.eta.remote &&
+ echo refs/heads/eta >expected &&
+ git symbolic-ref HEAD >actual &&
+ test_cmp expected actual &&
test_must_fail git rev-parse --verify HEAD^
'
@@ -76,7 +86,7 @@ test_expect_success '--orphan makes reflog by default' '
git rev-parse --verify delta@{0}
'
-test_expect_success REFFILES '--orphan does not make reflog when core.logAllRefUpdates = false' '
+test_expect_success '--orphan does not make reflog when core.logAllRefUpdates = false' '
git checkout main &&
git config core.logAllRefUpdates false &&
git checkout --orphan epsilon &&
diff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh
index 93be1c0..43551cc 100755
--- a/t/t2018-checkout-branch.sh
+++ b/t/t2018-checkout-branch.sh
@@ -2,6 +2,8 @@
test_description='checkout'
+TEST_CREATE_REPO_NO_TEMPLATE=1
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Arguments: [!] <branch> <oid> [<checkout options>]
@@ -85,6 +87,19 @@ test_expect_success 'setup' '
git branch -m branch1
'
+test_expect_success 'checkout a branch without refs/heads/* prefix' '
+ git clone --no-tags . repo-odd-prefix &&
+ (
+ cd repo-odd-prefix &&
+
+ origin=$(git symbolic-ref refs/remotes/origin/HEAD) &&
+ git symbolic-ref refs/heads/a-branch "$origin" &&
+
+ git checkout -f a-branch &&
+ git checkout -f a-branch
+ )
+'
+
test_expect_success 'checkout -b to a new branch, set to HEAD' '
test_when_finished "
git checkout branch1 &&
@@ -148,7 +163,7 @@ test_expect_success 'checkout -b to an existing branch fails' '
test_expect_success 'checkout -b to @{-1} fails with the right branch name' '
git checkout branch1 &&
git checkout branch2 &&
- echo >expect "fatal: A branch named '\''branch1'\'' already exists." &&
+ echo >expect "fatal: a branch named '\''branch1'\'' already exists" &&
test_must_fail git checkout -b @{-1} 2>actual &&
test_cmp expect actual
'
@@ -244,11 +259,12 @@ test_expect_success 'checkout -b to a new branch preserves mergeable changes des
git checkout branch1-scratch &&
test_might_fail git branch -D branch3 &&
git config core.sparseCheckout false &&
- rm .git/info/sparse-checkout" &&
+ rm -rf .git/info" &&
test_commit file2 &&
echo stuff >>file1 &&
+ mkdir .git/info &&
echo file2 >.git/info/sparse-checkout &&
git config core.sparseCheckout true &&
@@ -262,12 +278,12 @@ test_expect_success 'checkout -b to a new branch preserves mergeable changes des
test_expect_success 'checkout -b rejects an invalid start point' '
test_must_fail git checkout -b branch4 file1 2>err &&
- test_i18ngrep "is not a commit" err
+ test_grep "is not a commit" err
'
test_expect_success 'checkout -b rejects an extra path argument' '
test_must_fail git checkout -b branch5 branch1 file1 2>err &&
- test_i18ngrep "Cannot update paths and switch to branch" err
+ test_grep "Cannot update paths and switch to branch" err
'
test_done
diff --git a/t/t2019-checkout-ambiguous-ref.sh b/t/t2019-checkout-ambiguous-ref.sh
index b99d519..c67261e 100755
--- a/t/t2019-checkout-ambiguous-ref.sh
+++ b/t/t2019-checkout-ambiguous-ref.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='checkout handling of ambiguous (branch/tag) refs'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup ambiguous refs' '
@@ -14,7 +16,7 @@ test_expect_success 'setup ambiguous refs' '
'
test_expect_success 'checkout ambiguous ref succeeds' '
- git checkout ambiguity >stdout 2>stderr
+ git checkout ambiguity 2>stderr
'
test_expect_success 'checkout produces ambiguity warning' '
@@ -30,12 +32,12 @@ test_expect_success 'checkout chooses branch over tag' '
'
test_expect_success 'checkout reports switch to branch' '
- test_i18ngrep "Switched to branch" stderr &&
- test_i18ngrep ! "^HEAD is now at" stderr
+ test_grep "Switched to branch" stderr &&
+ test_grep ! "^HEAD is now at" stderr
'
test_expect_success 'checkout vague ref succeeds' '
- git checkout vagueness >stdout 2>stderr &&
+ git checkout vagueness 2>stderr &&
test_set_prereq VAGUENESS_SUCCESS
'
@@ -52,8 +54,8 @@ test_expect_success VAGUENESS_SUCCESS 'checkout chooses branch over tag' '
'
test_expect_success VAGUENESS_SUCCESS 'checkout reports switch to branch' '
- test_i18ngrep "Switched to branch" stderr &&
- test_i18ngrep ! "^HEAD is now at" stderr
+ test_grep "Switched to branch" stderr &&
+ test_grep ! "^HEAD is now at" stderr
'
test_done
diff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh
index bc46713..8d90d02 100755
--- a/t/t2020-checkout-detach.sh
+++ b/t/t2020-checkout-detach.sh
@@ -4,6 +4,7 @@ test_description='checkout into detached HEAD state'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
check_detached () {
@@ -16,12 +17,12 @@ check_not_detached () {
PREV_HEAD_DESC='Previous HEAD position was'
check_orphan_warning() {
- test_i18ngrep "you are leaving $2 behind" "$1" &&
- test_i18ngrep ! "$PREV_HEAD_DESC" "$1"
+ test_grep "you are leaving $2 behind" "$1" &&
+ test_grep ! "$PREV_HEAD_DESC" "$1"
}
check_no_orphan_warning() {
- test_i18ngrep ! "you are leaving .* commit.*behind" "$1" &&
- test_i18ngrep "$PREV_HEAD_DESC" "$1"
+ test_grep ! "you are leaving .* commit.*behind" "$1" &&
+ test_grep "$PREV_HEAD_DESC" "$1"
}
reset () {
@@ -44,6 +45,18 @@ test_expect_success 'checkout branch does not detach' '
check_not_detached
'
+for opt in "HEAD" "@"
+do
+ test_expect_success "checkout $opt no-op/don't detach" '
+ reset &&
+ cat .git/HEAD >expect &&
+ git checkout $opt &&
+ cat .git/HEAD >actual &&
+ check_not_detached &&
+ test_cmp expect actual
+ '
+done
+
test_expect_success 'checkout tag detaches' '
reset &&
git checkout tag &&
@@ -163,7 +176,10 @@ test_expect_success 'tracking count is accurate after orphan check' '
git config branch.child.merge refs/heads/main &&
git checkout child^ &&
git checkout child >stdout &&
- test_cmp expect stdout
+ test_cmp expect stdout &&
+
+ git checkout --detach child >stdout &&
+ test_grep ! "can be fast-forwarded\." stdout
'
test_expect_success 'no advice given for explicit detached head state' '
diff --git a/t/t2021-checkout-overwrite.sh b/t/t2021-checkout-overwrite.sh
index 70d6926..ecfacf0 100755
--- a/t/t2021-checkout-overwrite.sh
+++ b/t/t2021-checkout-overwrite.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='checkout must not overwrite an untracked objects'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -51,6 +53,10 @@ test_expect_success SYMLINKS 'the symlink remained' '
test -h a/b
'
+test_expect_success 'cleanup after previous symlink tests' '
+ rm a/b
+'
+
test_expect_success SYMLINKS 'checkout -f must not follow symlinks when removing entries' '
git checkout -f start &&
mkdir dir &&
@@ -63,4 +69,15 @@ test_expect_success SYMLINKS 'checkout -f must not follow symlinks when removing
test_path_is_file untracked/f
'
+test_expect_success 'checkout --overwrite-ignore should succeed if only ignored files in the way' '
+ git checkout -b df_conflict &&
+ test_commit contents some_dir &&
+ git checkout start &&
+ mkdir some_dir &&
+ echo autogenerated information >some_dir/ignore &&
+ echo ignore >.git/info/exclude &&
+ git checkout --overwrite-ignore df_conflict &&
+ test_path_is_file some_dir
+'
+
test_done
diff --git a/t/t2022-checkout-paths.sh b/t/t2022-checkout-paths.sh
index c49ba7f..f1b709d 100755
--- a/t/t2022-checkout-paths.sh
+++ b/t/t2022-checkout-paths.sh
@@ -4,6 +4,7 @@ test_description='checkout $tree -- $paths'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t2023-checkout-m.sh b/t/t2023-checkout-m.sh
index 7b327b7..81e772f 100755
--- a/t/t2023-checkout-m.sh
+++ b/t/t2023-checkout-m.sh
@@ -7,6 +7,7 @@ Ensures that checkout -m on a resolved file restores the conflicted file'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh
index 4a1c901..a3b1449 100755
--- a/t/t2024-checkout-dwim.sh
+++ b/t/t2024-checkout-dwim.sh
@@ -93,7 +93,7 @@ test_expect_success 'when arg matches multiple remotes, do not fallback to inter
test_must_fail git checkout ambiguous_branch_and_file 2>err &&
- test_i18ngrep "matched multiple (2) remote tracking branches" err &&
+ test_grep "matched multiple (2) remote tracking branches" err &&
# file must not be altered
test_cmp expect ambiguous_branch_and_file
@@ -105,20 +105,20 @@ test_expect_success 'checkout of branch from multiple remotes fails with advice'
test_must_fail git checkout foo 2>stderr &&
test_branch main &&
status_uno_is_clean &&
- test_i18ngrep "^hint: " stderr &&
+ test_grep "^hint: " stderr &&
test_must_fail git -c advice.checkoutAmbiguousRemoteBranchName=false \
checkout foo 2>stderr &&
test_branch main &&
status_uno_is_clean &&
- test_i18ngrep ! "^hint: " stderr
+ test_grep ! "^hint: " stderr
'
-test_expect_success PERL 'checkout -p with multiple remotes does not print advice' '
+test_expect_success 'checkout -p with multiple remotes does not print advice' '
git checkout -B main &&
test_might_fail git branch -D foo &&
git checkout -p foo 2>stderr &&
- test_i18ngrep ! "^hint: " stderr &&
+ test_grep ! "^hint: " stderr &&
status_uno_is_clean
'
@@ -305,10 +305,13 @@ test_expect_success 'loosely defined local base branch is reported correctly' '
test_config branch.strict.merge refs/heads/main &&
test_config branch.loose.merge main &&
- git checkout strict | sed -e "s/strict/BRANCHNAME/g" >expect &&
+ git checkout strict >expect.raw 2>&1 &&
+ sed -e "s/strict/BRANCHNAME/g" <expect.raw >expect &&
status_uno_is_clean &&
- git checkout loose | sed -e "s/loose/BRANCHNAME/g" >actual &&
+ git checkout loose >actual.raw 2>&1 &&
+ sed -e "s/loose/BRANCHNAME/g" <actual.raw >actual &&
status_uno_is_clean &&
+ grep BRANCHNAME actual &&
test_cmp expect actual
'
diff --git a/t/t2025-checkout-no-overlay.sh b/t/t2025-checkout-no-overlay.sh
index fa9e098..246609d 100755
--- a/t/t2025-checkout-no-overlay.sh
+++ b/t/t2025-checkout-no-overlay.sh
@@ -2,6 +2,7 @@
test_description='checkout --no-overlay <tree-ish> -- <pathspec>'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -25,7 +26,7 @@ test_expect_success 'checkout --no-overlay removing last file from directory' '
test_expect_success 'checkout -p --overlay is disallowed' '
test_must_fail git checkout -p --overlay HEAD 2>actual &&
- test_i18ngrep "fatal: -p and --overlay are mutually exclusive" actual
+ test_grep "fatal: options .-p. and .--overlay. cannot be used together" actual
'
test_expect_success '--no-overlay --theirs with D/F conflict deletes file' '
diff --git a/t/t2026-checkout-pathspec-file.sh b/t/t2026-checkout-pathspec-file.sh
index 43d31d7..acd5521 100755
--- a/t/t2026-checkout-pathspec-file.sh
+++ b/t/t2026-checkout-pathspec-file.sh
@@ -2,6 +2,7 @@
test_description='checkout --pathspec-from-file'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_tick
@@ -148,16 +149,16 @@ test_expect_success 'error conditions' '
echo fileA.t >list &&
test_must_fail git checkout --pathspec-from-file=list --detach 2>err &&
- test_i18ngrep -e "--pathspec-from-file is incompatible with --detach" err &&
+ test_grep -e "options .--pathspec-from-file. and .--detach. cannot be used together" err &&
test_must_fail git checkout --pathspec-from-file=list --patch 2>err &&
- test_i18ngrep -e "--pathspec-from-file is incompatible with --patch" err &&
+ test_grep -e "options .--pathspec-from-file. and .--patch. cannot be used together" err &&
test_must_fail git checkout --pathspec-from-file=list -- fileA.t 2>err &&
- test_i18ngrep -e "--pathspec-from-file is incompatible with pathspec arguments" err &&
+ test_grep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
test_must_fail git checkout --pathspec-file-nul 2>err &&
- test_i18ngrep -e "--pathspec-file-nul requires --pathspec-from-file" err
+ test_grep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err
'
test_done
diff --git a/t/t2027-checkout-track.sh b/t/t2027-checkout-track.sh
index 4453741..98f16c7 100755
--- a/t/t2027-checkout-track.sh
+++ b/t/t2027-checkout-track.sh
@@ -5,6 +5,7 @@ test_description='tests for git branch --track'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -21,7 +22,30 @@ test_expect_success 'checkout --track -b creates a new tracking branch' '
test_expect_success 'checkout --track -b rejects an extra path argument' '
test_must_fail git checkout --track -b branch2 main one.t 2>err &&
- test_i18ngrep "cannot be used with updating paths" err
+ test_grep "cannot be used with updating paths" err
+'
+
+test_expect_success 'checkout --track -b overrides autoSetupMerge=inherit' '
+ # Set up tracking config on main
+ test_config branch.main.remote origin &&
+ test_config branch.main.merge refs/heads/some-branch &&
+ test_config branch.autoSetupMerge inherit &&
+ # With --track=inherit, we copy the tracking config from main
+ git checkout --track=inherit -b b1 main &&
+ test_cmp_config origin branch.b1.remote &&
+ test_cmp_config refs/heads/some-branch branch.b1.merge &&
+ # With branch.autoSetupMerge=inherit, we do the same
+ git checkout -b b2 main &&
+ test_cmp_config origin branch.b2.remote &&
+ test_cmp_config refs/heads/some-branch branch.b2.merge &&
+ # But --track overrides this
+ git checkout --track -b b3 main &&
+ test_cmp_config . branch.b3.remote &&
+ test_cmp_config refs/heads/main branch.b3.merge &&
+ # And --track=direct does as well
+ git checkout --track=direct -b b4 main &&
+ test_cmp_config . branch.b4.remote &&
+ test_cmp_config refs/heads/main branch.b4.merge
'
test_done
diff --git a/t/t2030-unresolve-info.sh b/t/t2030-unresolve-info.sh
index f691e6d..be3fcdd 100755
--- a/t/t2030-unresolve-info.sh
+++ b/t/t2030-unresolve-info.sh
@@ -37,11 +37,17 @@ prime_resolve_undo () {
git checkout second^0 &&
test_tick &&
test_must_fail git merge third^0 &&
- echo merge does not leave anything &&
check_resolve_undo empty &&
- echo different >fi/le &&
- git add fi/le &&
- echo resolving records &&
+
+ # how should the conflict be resolved?
+ case "$1" in
+ remove)
+ rm -f file/le && git rm fi/le
+ ;;
+ *) # modify
+ echo different >fi/le && git add fi/le
+ ;;
+ esac
check_resolve_undo recorded fi/le initial:fi/le second:fi/le third:fi/le
}
@@ -122,6 +128,37 @@ test_expect_success 'add records checkout -m undoes' '
test_expect_success 'unmerge with plumbing' '
prime_resolve_undo &&
git update-index --unresolve fi/le &&
+ git ls-files --resolve-undo fi/le >actual &&
+ test_must_be_empty actual &&
+ git ls-files -u >actual &&
+ test_line_count = 3 actual
+'
+
+test_expect_success 'unmerge can be done even after committing' '
+ prime_resolve_undo &&
+ git commit -m "record to nuke MERGE_HEAD" &&
+ git update-index --unresolve fi/le &&
+ git ls-files --resolve-undo fi/le >actual &&
+ test_must_be_empty actual &&
+ git ls-files -u >actual &&
+ test_line_count = 3 actual
+'
+
+test_expect_success 'unmerge removal' '
+ prime_resolve_undo remove &&
+ git update-index --unresolve fi/le &&
+ git ls-files --resolve-undo fi/le >actual &&
+ test_must_be_empty actual &&
+ git ls-files -u >actual &&
+ test_line_count = 3 actual
+'
+
+test_expect_success 'unmerge removal after committing' '
+ prime_resolve_undo remove &&
+ git commit -m "record to nuke MERGE_HEAD" &&
+ git update-index --unresolve fi/le &&
+ git ls-files --resolve-undo fi/le >actual &&
+ test_must_be_empty actual &&
git ls-files -u >actual &&
test_line_count = 3 actual
'
@@ -191,7 +228,78 @@ test_expect_success 'rerere forget (add-add conflict)' '
git commit -m "add differently" &&
test_must_fail git merge fifth &&
git rerere forget add-differently 2>actual &&
- test_i18ngrep "no remembered" actual
+ test_grep "no remembered" actual
+'
+
+test_expect_success 'resolve-undo keeps blobs from gc' '
+ git checkout -f main &&
+
+ # First make sure we do not have any cruft left in the object store
+ git repack -a -d &&
+ git prune --expire=now &&
+ git prune-packed &&
+ git gc --prune=now &&
+ git fsck --unreachable >cruft &&
+ test_must_be_empty cruft &&
+
+ # Now add three otherwise unreferenced blob objects to the index
+ git reset --hard &&
+ B1=$(echo "resolve undo test data 1" | git hash-object -w --stdin) &&
+ B2=$(echo "resolve undo test data 2" | git hash-object -w --stdin) &&
+ B3=$(echo "resolve undo test data 3" | git hash-object -w --stdin) &&
+ git update-index --add --index-info <<-EOF &&
+ 100644 $B1 1 frotz
+ 100644 $B2 2 frotz
+ 100644 $B3 3 frotz
+ EOF
+
+ # These three blob objects are reachable (only) from the index
+ git fsck --unreachable >cruft &&
+ test_must_be_empty cruft &&
+ # and they should be protected from GC
+ git gc --prune=now &&
+ git cat-file -e $B1 &&
+ git cat-file -e $B2 &&
+ git cat-file -e $B3 &&
+
+ # Now resolve the conflicted path
+ B0=$(echo "resolve undo test data 0" | git hash-object -w --stdin) &&
+ git update-index --add --cacheinfo 100644,$B0,frotz &&
+
+ # These three blob objects are now reachable only from the resolve-undo
+ git fsck --unreachable >cruft &&
+ test_must_be_empty cruft &&
+
+ # and they should survive GC
+ git gc --prune=now &&
+ git cat-file -e $B0 &&
+ git cat-file -e $B1 &&
+ git cat-file -e $B2 &&
+ git cat-file -e $B3 &&
+
+ # Now we switch away, which nukes resolve-undo, and
+ # blobs B0..B3 would become dangling. fsck should
+ # notice that they are now unreachable.
+ git checkout -f side &&
+ git fsck --unreachable >cruft &&
+ sort cruft >actual &&
+ sort <<-EOF >expect &&
+ unreachable blob $B0
+ unreachable blob $B1
+ unreachable blob $B2
+ unreachable blob $B3
+ EOF
+ test_cmp expect actual &&
+
+ # And they should go away when gc runs.
+ git gc --prune=now &&
+ git fsck --unreachable >cruft &&
+ test_must_be_empty cruft &&
+
+ test_must_fail git cat-file -e $B0 &&
+ test_must_fail git cat-file -e $B1 &&
+ test_must_fail git cat-file -e $B2 &&
+ test_must_fail git cat-file -e $B3
'
test_done
diff --git a/t/t2050-git-dir-relative.sh b/t/t2050-git-dir-relative.sh
index 21f4659..1f193cd 100755
--- a/t/t2050-git-dir-relative.sh
+++ b/t/t2050-git-dir-relative.sh
@@ -12,6 +12,7 @@ into the subdir while keeping the worktree location,
and tries commits from the top and the subdir, checking
that the commit-hook still gets called.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
COMMIT_FILE="$(pwd)/output"
diff --git a/t/t2060-switch.sh b/t/t2060-switch.sh
index 9bc6a3a..c91c4db 100755
--- a/t/t2060-switch.sh
+++ b/t/t2060-switch.sh
@@ -32,6 +32,17 @@ test_expect_success 'switch and detach' '
test_must_fail git symbolic-ref HEAD
'
+test_expect_success 'suggestion to detach' '
+ test_must_fail git switch main^{commit} 2>stderr &&
+ grep "try again with the --detach option" stderr
+'
+
+test_expect_success 'suggestion to detach is suppressed with advice.suggestDetachingHead=false' '
+ test_config advice.suggestDetachingHead false &&
+ test_must_fail git switch main^{commit} 2>stderr &&
+ ! grep "try again with the --detach option" stderr
+'
+
test_expect_success 'switch and detach current branch' '
test_when_finished git switch main &&
git switch main &&
@@ -107,4 +118,63 @@ test_expect_success 'not switching when something is in progress' '
test_must_fail git switch -d @^
'
+test_expect_success 'tracking info copied with autoSetupMerge=inherit' '
+ # default config does not copy tracking info
+ git switch -c foo-no-inherit foo &&
+ test_cmp_config "" --default "" branch.foo-no-inherit.remote &&
+ test_cmp_config "" --default "" branch.foo-no-inherit.merge &&
+ # with --track=inherit, we copy tracking info from foo
+ git switch --track=inherit -c foo2 foo &&
+ test_cmp_config origin branch.foo2.remote &&
+ test_cmp_config refs/heads/foo branch.foo2.merge &&
+ # with autoSetupMerge=inherit, we do the same
+ test_config branch.autoSetupMerge inherit &&
+ git switch -c foo3 foo &&
+ test_cmp_config origin branch.foo3.remote &&
+ test_cmp_config refs/heads/foo branch.foo3.merge &&
+ # with --track, we override autoSetupMerge
+ git switch --track -c foo4 foo &&
+ test_cmp_config . branch.foo4.remote &&
+ test_cmp_config refs/heads/foo branch.foo4.merge &&
+ # and --track=direct does as well
+ git switch --track=direct -c foo5 foo &&
+ test_cmp_config . branch.foo5.remote &&
+ test_cmp_config refs/heads/foo branch.foo5.merge &&
+ # no tracking info to inherit from main
+ git switch -c main2 main &&
+ test_cmp_config "" --default "" branch.main2.remote &&
+ test_cmp_config "" --default "" branch.main2.merge
+'
+
+test_expect_success 'switch back when temporarily detached and checked out elsewhere ' '
+ test_when_finished "
+ git worktree remove wt1 ||:
+ git worktree remove wt2 ||:
+ git checkout - ||:
+ git branch -D shared ||:
+ " &&
+ git checkout -b shared &&
+ test_commit shared-first &&
+ HASH1=$(git rev-parse --verify HEAD) &&
+ test_commit shared-second &&
+ test_commit shared-third &&
+ HASH2=$(git rev-parse --verify HEAD) &&
+ git worktree add wt1 -f shared &&
+ git -C wt1 bisect start &&
+ git -C wt1 bisect good $HASH1 &&
+ git -C wt1 bisect bad $HASH2 &&
+ git worktree add wt2 -f shared &&
+ git -C wt2 bisect start &&
+ git -C wt2 bisect good $HASH1 &&
+ git -C wt2 bisect bad $HASH2 &&
+ # we test in both worktrees to ensure that works
+ # as expected with "first" and "next" worktrees
+ test_must_fail git -C wt1 switch shared &&
+ test_must_fail git -C wt1 switch -C shared &&
+ git -C wt1 switch --ignore-other-worktrees shared &&
+ test_must_fail git -C wt2 switch shared &&
+ test_must_fail git -C wt2 switch -C shared &&
+ git -C wt2 switch --ignore-other-worktrees shared
+'
+
test_done
diff --git a/t/t2070-restore.sh b/t/t2070-restore.sh
index 7c43ddf..ac40494 100755
--- a/t/t2070-restore.sh
+++ b/t/t2070-restore.sh
@@ -5,6 +5,7 @@ test_description='restore basic functionality'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -137,4 +138,87 @@ test_expect_success 'restore --staged invalidates cache tree for deletions' '
test_must_fail git rev-parse HEAD:new1
'
+test_expect_success 'restore --merge to unresolve' '
+ O=$(echo original | git hash-object -w --stdin) &&
+ A=$(echo ourside | git hash-object -w --stdin) &&
+ B=$(echo theirside | git hash-object -w --stdin) &&
+ {
+ echo "100644 $O 1 file" &&
+ echo "100644 $A 2 file" &&
+ echo "100644 $B 3 file"
+ } | git update-index --index-info &&
+ echo nothing >file &&
+ git restore --worktree --merge file &&
+ cat >expect <<-\EOF &&
+ <<<<<<< ours
+ ourside
+ =======
+ theirside
+ >>>>>>> theirs
+ EOF
+ test_cmp expect file
+'
+
+test_expect_success 'restore --merge to unresolve after (mistaken) resolution' '
+ O=$(echo original | git hash-object -w --stdin) &&
+ A=$(echo ourside | git hash-object -w --stdin) &&
+ B=$(echo theirside | git hash-object -w --stdin) &&
+ {
+ echo "100644 $O 1 file" &&
+ echo "100644 $A 2 file" &&
+ echo "100644 $B 3 file"
+ } | git update-index --index-info &&
+ echo nothing >file &&
+ git add file &&
+ git restore --worktree --merge file &&
+ cat >expect <<-\EOF &&
+ <<<<<<< ours
+ ourside
+ =======
+ theirside
+ >>>>>>> theirs
+ EOF
+ test_cmp expect file
+'
+
+test_expect_success 'restore --merge to unresolve after (mistaken) resolution' '
+ O=$(echo original | git hash-object -w --stdin) &&
+ A=$(echo ourside | git hash-object -w --stdin) &&
+ B=$(echo theirside | git hash-object -w --stdin) &&
+ {
+ echo "100644 $O 1 file" &&
+ echo "100644 $A 2 file" &&
+ echo "100644 $B 3 file"
+ } | git update-index --index-info &&
+ git rm -f file &&
+ git restore --worktree --merge file &&
+ cat >expect <<-\EOF &&
+ <<<<<<< ours
+ ourside
+ =======
+ theirside
+ >>>>>>> theirs
+ EOF
+ test_cmp expect file
+'
+
+test_expect_success 'restore with merge options are incompatible with certain options' '
+ for opts in \
+ "--staged --ours" \
+ "--staged --theirs" \
+ "--staged --merge" \
+ "--source=HEAD --ours" \
+ "--source=HEAD --theirs" \
+ "--source=HEAD --merge" \
+ "--staged --conflict=diff3" \
+ "--staged --worktree --ours" \
+ "--staged --worktree --theirs" \
+ "--staged --worktree --merge" \
+ "--staged --worktree --conflict=zdiff3"
+ do
+ test_must_fail git restore $opts . 2>err &&
+ grep "cannot be used" err || return
+ done
+'
+
test_done
diff --git a/t/t2071-restore-patch.sh b/t/t2071-restore-patch.sh
index b5c5c0f..42d5522 100755
--- a/t/t2071-restore-patch.sh
+++ b/t/t2071-restore-patch.sh
@@ -2,9 +2,10 @@
test_description='git restore --patch'
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-patch-mode.sh
-test_expect_success PERL 'setup' '
+test_expect_success 'setup' '
mkdir dir &&
echo parent >dir/foo &&
echo dummy >bar &&
@@ -16,43 +17,47 @@ test_expect_success PERL 'setup' '
save_head
'
-test_expect_success PERL 'restore -p without pathspec is fine' '
+test_expect_success 'restore -p without pathspec is fine' '
echo q >cmd &&
git restore -p <cmd
'
# note: bar sorts before dir/foo, so the first 'n' is always to skip 'bar'
-test_expect_success PERL 'saying "n" does nothing' '
+test_expect_success 'saying "n" does nothing' '
set_and_save_state dir/foo work head &&
test_write_lines n n | git restore -p &&
verify_saved_state bar &&
verify_saved_state dir/foo
'
-test_expect_success PERL 'git restore -p' '
+test_expect_success 'git restore -p' '
set_and_save_state dir/foo work head &&
test_write_lines n y | git restore -p &&
verify_saved_state bar &&
verify_state dir/foo head head
'
-test_expect_success PERL 'git restore -p with staged changes' '
+test_expect_success 'git restore -p with staged changes' '
set_state dir/foo work index &&
test_write_lines n y | git restore -p &&
verify_saved_state bar &&
verify_state dir/foo index index
'
-test_expect_success PERL 'git restore -p --source=HEAD' '
- set_state dir/foo work index &&
- # the third n is to get out in case it mistakenly does not apply
- test_write_lines n y n | git restore -p --source=HEAD &&
- verify_saved_state bar &&
- verify_state dir/foo head index
-'
-
-test_expect_success PERL 'git restore -p --source=HEAD^' '
+for opt in "HEAD" "@"
+do
+ test_expect_success "git restore -p --source=$opt" '
+ set_state dir/foo work index &&
+ # the third n is to get out in case it mistakenly does not apply
+ test_write_lines n y n | git restore -p --source=$opt >output &&
+ verify_saved_state bar &&
+ verify_state dir/foo head index &&
+ test_grep "Discard" output
+ '
+done
+
+test_expect_success 'git restore -p --source=HEAD^' '
set_state dir/foo work index &&
# the third n is to get out in case it mistakenly does not apply
test_write_lines n y n | git restore -p --source=HEAD^ &&
@@ -60,7 +65,7 @@ test_expect_success PERL 'git restore -p --source=HEAD^' '
verify_state dir/foo parent index
'
-test_expect_success PERL 'git restore -p --source=HEAD^...' '
+test_expect_success 'git restore -p --source=HEAD^...' '
set_state dir/foo work index &&
# the third n is to get out in case it mistakenly does not apply
test_write_lines n y n | git restore -p --source=HEAD^... &&
@@ -68,7 +73,7 @@ test_expect_success PERL 'git restore -p --source=HEAD^...' '
verify_state dir/foo parent index
'
-test_expect_success PERL 'git restore -p handles deletion' '
+test_expect_success 'git restore -p handles deletion' '
set_state dir/foo work index &&
rm dir/foo &&
test_write_lines n y | git restore -p &&
@@ -81,21 +86,21 @@ test_expect_success PERL 'git restore -p handles deletion' '
# dir/foo. There's always an extra 'n' to reject edits to dir/foo in
# the failure case (and thus get out of the loop).
-test_expect_success PERL 'path limiting works: dir' '
+test_expect_success 'path limiting works: dir' '
set_state dir/foo work head &&
test_write_lines y n | git restore -p dir &&
verify_saved_state bar &&
verify_state dir/foo head head
'
-test_expect_success PERL 'path limiting works: -- dir' '
+test_expect_success 'path limiting works: -- dir' '
set_state dir/foo work head &&
test_write_lines y n | git restore -p -- dir &&
verify_saved_state bar &&
verify_state dir/foo head head
'
-test_expect_success PERL 'path limiting works: HEAD^ -- dir' '
+test_expect_success 'path limiting works: HEAD^ -- dir' '
set_state dir/foo work head &&
# the third n is to get out in case it mistakenly does not apply
test_write_lines y n n | git restore -p --source=HEAD^ -- dir &&
@@ -103,7 +108,7 @@ test_expect_success PERL 'path limiting works: HEAD^ -- dir' '
verify_state dir/foo parent head
'
-test_expect_success PERL 'path limiting works: foo inside dir' '
+test_expect_success '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
test_write_lines y n n | (cd dir && git restore -p foo) &&
@@ -111,7 +116,7 @@ test_expect_success PERL 'path limiting works: foo inside dir' '
verify_state dir/foo head head
'
-test_expect_success PERL 'none of this moved HEAD' '
+test_expect_success 'none of this moved HEAD' '
verify_saved_head
'
diff --git a/t/t2072-restore-pathspec-file.sh b/t/t2072-restore-pathspec-file.sh
index b48345b..86c9c88 100755
--- a/t/t2072-restore-pathspec-file.sh
+++ b/t/t2072-restore-pathspec-file.sh
@@ -2,6 +2,7 @@
test_description='restore --pathspec-from-file'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_tick
@@ -152,16 +153,16 @@ test_expect_success 'error conditions' '
>empty_list &&
test_must_fail git restore --pathspec-from-file=list --patch --source=HEAD^1 2>err &&
- test_i18ngrep -e "--pathspec-from-file is incompatible with --patch" err &&
+ test_grep -e "options .--pathspec-from-file. and .--patch. cannot be used together" err &&
test_must_fail git restore --pathspec-from-file=list --source=HEAD^1 -- fileA.t 2>err &&
- test_i18ngrep -e "--pathspec-from-file is incompatible with pathspec arguments" err &&
+ test_grep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
test_must_fail git restore --pathspec-file-nul --source=HEAD^1 2>err &&
- test_i18ngrep -e "--pathspec-file-nul requires --pathspec-from-file" err &&
+ test_grep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
test_must_fail git restore --pathspec-from-file=empty_list --source=HEAD^1 2>err &&
- test_i18ngrep -e "you must specify path(s) to restore" err
+ test_grep -e "you must specify path(s) to restore" err
'
test_expect_success 'wildcard pathspec matches file in subdirectory' '
diff --git a/t/t2080-parallel-checkout-basics.sh b/t/t2080-parallel-checkout-basics.sh
index 3e0f8c6..5ffe1a4 100755
--- a/t/t2080-parallel-checkout-basics.sh
+++ b/t/t2080-parallel-checkout-basics.sh
@@ -41,6 +41,8 @@ TEST_NO_CREATE_REPO=1
# - m/m (file)
#
test_expect_success 'setup repo for checkout with various types of changes' '
+ test_config_global protocol.file.allow always &&
+
git init sub &&
(
cd sub &&
@@ -140,6 +142,7 @@ do
esac
test_expect_success "$mode checkout on clone" '
+ test_config_global protocol.file.allow always &&
repo=various_${mode}_clone &&
set_checkout_config $workers $threshold &&
test_checkout_workers $expected_workers \
@@ -226,4 +229,49 @@ test_expect_success SYMLINKS 'parallel checkout checks for symlinks in leading d
)
'
+# This test is here (and not in e.g. t2022-checkout-paths.sh), because we
+# check the final report including sequential, parallel, and delayed entries
+# all at the same time. So we must have finer control of the parallel checkout
+# variables.
+test_expect_success '"git checkout ." report should not include failed entries' '
+ test_config_global filter.delay.process \
+ "test-tool rot13-filter --always-delay --log=delayed.log clean smudge delay" &&
+ test_config_global filter.delay.required true &&
+ test_config_global filter.cat.clean cat &&
+ test_config_global filter.cat.smudge cat &&
+ test_config_global filter.cat.required true &&
+
+ set_checkout_config 2 0 &&
+ git init failed_entries &&
+ (
+ cd failed_entries &&
+ cat >.gitattributes <<-EOF &&
+ *delay* filter=delay
+ parallel-ineligible* filter=cat
+ EOF
+ echo a >missing-delay.a &&
+ echo a >parallel-ineligible.a &&
+ echo a >parallel-eligible.a &&
+ echo b >success-delay.b &&
+ echo b >parallel-ineligible.b &&
+ echo b >parallel-eligible.b &&
+ git add -A &&
+ git commit -m files &&
+
+ a_blob="$(git rev-parse :parallel-ineligible.a)" &&
+ rm .git/objects/$(test_oid_to_path $a_blob) &&
+ rm *.a *.b &&
+
+ test_checkout_workers 2 test_must_fail git checkout . 2>err &&
+
+ # All *.b entries should succeed and all *.a entries should fail:
+ # - missing-delay.a: the delay filter will drop this path
+ # - parallel-*.a: the blob will be missing
+ #
+ grep "Updated 3 paths from the index" err &&
+ test_stdout_line_count = 3 ls *.b &&
+ ! ls *.a
+ )
+'
+
test_done
diff --git a/t/t2081-parallel-checkout-collisions.sh b/t/t2081-parallel-checkout-collisions.sh
index f6fcfc0..6acdb89 100755
--- a/t/t2081-parallel-checkout-collisions.sh
+++ b/t/t2081-parallel-checkout-collisions.sh
@@ -11,6 +11,7 @@ The tests in this file exercise parallel checkout's collision detection code in
both these mechanics.
"
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY/lib-parallel-checkout.sh"
diff --git a/t/t2082-parallel-checkout-attributes.sh b/t/t2082-parallel-checkout-attributes.sh
index 2525457..f3511cd 100755
--- a/t/t2082-parallel-checkout-attributes.sh
+++ b/t/t2082-parallel-checkout-attributes.sh
@@ -138,12 +138,9 @@ test_expect_success 'parallel-checkout and external filter' '
# The delayed queue is independent from the parallel queue, and they should be
# able to work together in the same checkout process.
#
-test_expect_success PERL 'parallel-checkout and delayed checkout' '
- write_script rot13-filter.pl "$PERL_PATH" \
- <"$TEST_DIRECTORY"/t0021/rot13-filter.pl &&
-
+test_expect_success 'parallel-checkout and delayed checkout' '
test_config_global filter.delay.process \
- "\"$(pwd)/rot13-filter.pl\" --always-delay \"$(pwd)/delayed.log\" clean smudge delay" &&
+ "test-tool rot13-filter --always-delay --log=\"$(pwd)/delayed.log\" clean smudge delay" &&
test_config_global filter.delay.required true &&
echo "abcd" >original &&
diff --git a/t/t2100-update-cache-badpath.sh b/t/t2100-update-cache-badpath.sh
index 2df3fdd..7915e7b 100755
--- a/t/t2100-update-cache-badpath.sh
+++ b/t/t2100-update-cache-badpath.sh
@@ -22,6 +22,7 @@ and tries to git update-index --add the following:
All of the attempts should fail.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
mkdir path2 path3
diff --git a/t/t2101-update-index-reupdate.sh b/t/t2101-update-index-reupdate.sh
index 6c32d42..e3c7acd 100755
--- a/t/t2101-update-index-reupdate.sh
+++ b/t/t2101-update-index-reupdate.sh
@@ -6,6 +6,7 @@
test_description='git update-index --again test.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'update-index --add' '
diff --git a/t/t2102-update-index-symlinks.sh b/t/t2102-update-index-symlinks.sh
index 22f2c73..c49cdfb 100755
--- a/t/t2102-update-index-symlinks.sh
+++ b/t/t2102-update-index-symlinks.sh
@@ -8,6 +8,7 @@ test_description='git update-index on filesystem w/o symlinks test.
This tests that git update-index keeps the symbolic link property
even if a plain file is in the working tree if core.symlinks is false.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success \
@@ -25,7 +26,7 @@ test_expect_success \
'the index entry must still be a symbolic link' '
case "$(git ls-files --stage --cached symlink)" in
120000" "*symlink) echo pass;;
-*) echo fail; git ls-files --stage --cached symlink; (exit 1);;
+*) echo fail; git ls-files --stage --cached symlink; false;;
esac'
test_done
diff --git a/t/t2103-update-index-ignore-missing.sh b/t/t2103-update-index-ignore-missing.sh
index 0114f05..e9451cd 100755
--- a/t/t2103-update-index-ignore-missing.sh
+++ b/t/t2103-update-index-ignore-missing.sh
@@ -2,6 +2,7 @@
test_description='update-index with options'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success basics '
@@ -23,7 +24,7 @@ test_expect_success basics '
test_cmp expect actual &&
git update-index --add one two three &&
- for i in one three two; do echo $i; done >expect &&
+ test_write_lines one three two >expect &&
git ls-files >actual &&
test_cmp expect actual &&
diff --git a/t/t2104-update-index-skip-worktree.sh b/t/t2104-update-index-skip-worktree.sh
index 30666fc..7ec7f30 100755
--- a/t/t2104-update-index-skip-worktree.sh
+++ b/t/t2104-update-index-skip-worktree.sh
@@ -5,32 +5,33 @@
test_description='skip-worktree bit test'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
sane_unset GIT_TEST_SPLIT_INDEX
test_set_index_version () {
- GIT_INDEX_VERSION="$1"
- export GIT_INDEX_VERSION
+ GIT_INDEX_VERSION="$1"
+ export GIT_INDEX_VERSION
}
test_set_index_version 3
-cat >expect.full <<EOF
-H 1
-H 2
-H sub/1
-H sub/2
-EOF
+test_expect_success 'setup' '
+ cat >expect.full <<-\EOF &&
+ H 1
+ H 2
+ H sub/1
+ H sub/2
+ EOF
-cat >expect.skip <<EOF
-S 1
-H 2
-S sub/1
-H sub/2
-EOF
+ cat >expect.skip <<-\EOF &&
+ S 1
+ H 2
+ S sub/1
+ H sub/2
+ EOF
-test_expect_success 'setup' '
mkdir sub &&
touch ./1 ./2 sub/1 sub/2 &&
git add 1 2 sub/1 sub/2 &&
@@ -38,7 +39,7 @@ test_expect_success 'setup' '
'
test_expect_success 'index is at version 2' '
- test "$(test-tool index-version < .git/index)" = 2
+ test "$(git update-index --show-index-version)" = 2
'
test_expect_success 'update-index --skip-worktree' '
@@ -47,7 +48,7 @@ test_expect_success 'update-index --skip-worktree' '
'
test_expect_success 'index is at version 3 after having some skip-worktree entries' '
- test "$(test-tool index-version < .git/index)" = 3
+ test "$(git update-index --show-index-version)" = 3
'
test_expect_success 'ls-files -t' '
@@ -60,7 +61,7 @@ test_expect_success 'update-index --no-skip-worktree' '
'
test_expect_success 'index version is back to 2 when there is no skip-worktree entry' '
- test "$(test-tool index-version < .git/index)" = 2
+ test "$(git update-index --show-index-version)" = 2
'
test_done
diff --git a/t/t2105-update-index-gitfile.sh b/t/t2105-update-index-gitfile.sh
index a7f3d47..963ebe7 100755
--- a/t/t2105-update-index-gitfile.sh
+++ b/t/t2105-update-index-gitfile.sh
@@ -6,6 +6,7 @@
test_description='git update-index for gitlink to .git file.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'submodule with absolute .git file' '
diff --git a/t/t2106-update-index-assume-unchanged.sh b/t/t2106-update-index-assume-unchanged.sh
index 2d450da..95c004d 100755
--- a/t/t2106-update-index-assume-unchanged.sh
+++ b/t/t2106-update-index-assume-unchanged.sh
@@ -3,6 +3,7 @@
test_description='git update-index --assume-unchanged test.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -21,7 +22,7 @@ test_expect_success 'do not switch branches with dirty file' '
echo dirt >file &&
git update-index --assume-unchanged file &&
test_must_fail git checkout - 2>err &&
- test_i18ngrep overwritten err
+ test_grep overwritten err
'
test_done
diff --git a/t/t2107-update-index-basic.sh b/t/t2107-update-index-basic.sh
index a30b7ca..cc72ead 100755
--- a/t/t2107-update-index-basic.sh
+++ b/t/t2107-update-index-basic.sh
@@ -14,7 +14,7 @@ test_expect_success 'update-index --nonsense fails' '
test_expect_success 'update-index --nonsense dumps usage' '
test_expect_code 129 git update-index --nonsense 2>err &&
- test_i18ngrep "[Uu]sage: git update-index" err
+ test_grep "[Uu]sage: git update-index" err
'
test_expect_success 'update-index -h with corrupt index' '
@@ -25,7 +25,7 @@ test_expect_success 'update-index -h with corrupt index' '
>.git/index &&
test_expect_code 129 git update-index -h >usage 2>&1
) &&
- test_i18ngrep "[Uu]sage: git update-index" broken/usage
+ test_grep "[Uu]sage: git update-index" broken/usage
'
test_expect_success '--cacheinfo complains of missing arguments' '
@@ -36,9 +36,14 @@ test_expect_success '--cacheinfo does not accept blob null sha1' '
echo content >file &&
git add file &&
git rev-parse :file >expect &&
- test_must_fail git update-index --cacheinfo 100644 $ZERO_OID file &&
+ test_must_fail git update-index --verbose --cacheinfo 100644 $ZERO_OID file >out &&
git rev-parse :file >actual &&
- test_cmp expect actual
+ test_cmp expect actual &&
+
+ cat >expect <<-\EOF &&
+ add '\''file'\''
+ EOF
+ test_cmp expect out
'
test_expect_success '--cacheinfo does not accept gitlink null sha1' '
@@ -59,9 +64,14 @@ test_expect_success '--cacheinfo mode,sha1,path (new syntax)' '
git rev-parse :file >actual &&
test_cmp expect actual &&
- git update-index --add --cacheinfo "100644,$(cat expect),elif" &&
+ git update-index --add --verbose --cacheinfo "100644,$(cat expect),elif" >out &&
git rev-parse :elif >actual &&
- test_cmp expect actual
+ test_cmp expect actual &&
+
+ cat >expect <<-\EOF &&
+ add '\''elif'\''
+ EOF
+ test_cmp expect out
'
test_expect_success '.lock files cleaned up' '
@@ -73,8 +83,9 @@ test_expect_success '.lock files cleaned up' '
cd repo &&
git config core.worktree ../../worktree &&
# --refresh triggers late setup_work_tree,
- # active_cache_changed is zero, rollback_lock_file fails
- git update-index --refresh &&
+ # the_index.cache_changed is zero, rollback_lock_file fails
+ git update-index --refresh --verbose >out &&
+ test_must_be_empty out &&
! test -f .git/index.lock
)
'
@@ -83,7 +94,15 @@ test_expect_success '--chmod=+x and chmod=-x in the same argument list' '
>A &&
>B &&
git add A B &&
- git update-index --chmod=+x A --chmod=-x B &&
+ git update-index --verbose --chmod=+x A --chmod=-x B >out &&
+ cat >expect <<-\EOF &&
+ add '\''A'\''
+ chmod +x '\''A'\''
+ add '\''B'\''
+ chmod -x '\''B'\''
+ EOF
+ test_cmp expect out &&
+
cat >expect <<-EOF &&
100755 $EMPTY_BLOB 0 A
100644 $EMPTY_BLOB 0 B
@@ -92,4 +111,35 @@ test_expect_success '--chmod=+x and chmod=-x in the same argument list' '
test_cmp expect actual
'
+test_expect_success '--index-version' '
+ git commit --allow-empty -m snap &&
+ git reset --hard &&
+ git rm -f -r --cached . &&
+
+ # The default index version is 2 --- update this test
+ # when you change it in the code
+ git update-index --show-index-version >actual &&
+ echo 2 >expect &&
+ test_cmp expect actual &&
+
+ # The next test wants us to be using version 2
+ git update-index --index-version 2 &&
+
+ git update-index --index-version 4 --verbose >actual &&
+ echo "index-version: was 2, set to 4" >expect &&
+ test_cmp expect actual &&
+
+ git update-index --index-version 4 --verbose >actual &&
+ echo "index-version: was 4, set to 4" >expect &&
+ test_cmp expect actual &&
+
+ git update-index --index-version 2 --verbose >actual &&
+ echo "index-version: was 4, set to 2" >expect &&
+ test_cmp expect actual &&
+
+ # non-verbose should be silent
+ git update-index --index-version 4 >actual &&
+ test_must_be_empty actual
+'
+
test_done
diff --git a/t/t2108-update-index-refresh-racy.sh b/t/t2108-update-index-refresh-racy.sh
new file mode 100755
index 0000000..bc5f288
--- /dev/null
+++ b/t/t2108-update-index-refresh-racy.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+
+test_description='update-index refresh tests related to racy timestamps'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+reset_files () {
+ echo content >file &&
+ echo content >other &&
+ test_set_magic_mtime file &&
+ test_set_magic_mtime other
+}
+
+update_assert_changed () {
+ test_set_magic_mtime .git/index &&
+ test_might_fail git update-index "$1" &&
+ ! test_is_magic_mtime .git/index
+}
+
+test_expect_success 'setup' '
+ reset_files &&
+ # we are calling reset_files() a couple of times during tests;
+ # test-tool chmtime does not change the ctime; to not weaken
+ # or even break our tests, disable ctime-checks entirely
+ git config core.trustctime false &&
+ git add file other &&
+ git commit -m "initial import"
+'
+
+test_expect_success '--refresh has no racy timestamps to fix' '
+ reset_files &&
+ # set the index time far enough to the future;
+ # it must be at least 3 seconds for VFAT
+ test_set_magic_mtime .git/index +60 &&
+ git update-index --refresh &&
+ test_is_magic_mtime .git/index +60
+'
+
+test_expect_success '--refresh should fix racy timestamp' '
+ reset_files &&
+ update_assert_changed --refresh
+'
+
+test_expect_success '--really-refresh should fix racy timestamp' '
+ reset_files &&
+ update_assert_changed --really-refresh
+'
+
+test_expect_success '--refresh should fix racy timestamp if other file needs update' '
+ reset_files &&
+ echo content2 >other &&
+ test_set_magic_mtime other &&
+ update_assert_changed --refresh
+'
+
+test_expect_success '--refresh should fix racy timestamp if racy file needs update' '
+ reset_files &&
+ echo content2 >file &&
+ test_set_magic_mtime file &&
+ update_assert_changed --refresh
+'
+
+test_done
diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh
index 45ca35d..df235ac 100755
--- a/t/t2200-add-update.sh
+++ b/t/t2200-add-update.sh
@@ -14,6 +14,7 @@ only the updates to dir/sub.
Also tested are "git add -u" without limiting, and "git add -u"
without contents changes, and other conditions'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -40,20 +41,38 @@ test_expect_success update '
'
test_expect_success 'update noticed a removal' '
- test "$(git ls-files dir1/sub1)" = ""
+ git ls-files dir1/sub1 >out &&
+ test_must_be_empty out
'
test_expect_success 'update touched correct path' '
- test "$(git diff-files --name-status dir2/sub3)" = ""
+ git diff-files --name-status dir2/sub3 >out &&
+ test_must_be_empty out
'
test_expect_success 'update did not touch other tracked files' '
- test "$(git diff-files --name-status check)" = "M check" &&
- test "$(git diff-files --name-status top)" = "M top"
+ echo "M check" >expect &&
+ git diff-files --name-status check >actual &&
+ test_cmp expect actual &&
+
+ echo "M top" >expect &&
+ git diff-files --name-status top >actual &&
+ test_cmp expect actual
'
test_expect_success 'update did not touch untracked files' '
- test "$(git ls-files dir2/other)" = ""
+ git ls-files dir2/other >out &&
+ test_must_be_empty out
+'
+
+test_expect_success 'error out when passing untracked path' '
+ git reset --hard &&
+ echo content >>baz &&
+ echo content >>top &&
+ test_must_fail git add -u baz top 2>err &&
+ test_grep -e "error: pathspec .baz. did not match any file(s) known to git" err &&
+ git diff --cached --name-only >actual &&
+ test_must_be_empty actual
'
test_expect_success 'cache tree has not been corrupted' '
@@ -75,9 +94,8 @@ test_expect_success 'update from a subdirectory' '
'
test_expect_success 'change gets noticed' '
-
- test "$(git diff-files --name-status dir1)" = ""
-
+ git diff-files --name-status dir1 >out &&
+ test_must_be_empty out
'
test_expect_success 'non-qualified update in subdir updates from the root' '
@@ -102,7 +120,8 @@ test_expect_success 'replace a file with a symlink' '
test_expect_success 'add everything changed' '
git add -u &&
- test -z "$(git diff-files)"
+ git diff-files >out &&
+ test_must_be_empty out
'
@@ -110,7 +129,8 @@ test_expect_success 'touch and then add -u' '
touch check &&
git add -u &&
- test -z "$(git diff-files)"
+ git diff-files >out &&
+ test_must_be_empty out
'
@@ -118,7 +138,8 @@ test_expect_success 'touch and then add explicitly' '
touch check &&
git add check &&
- test -z "$(git diff-files)"
+ git diff-files >out &&
+ test_must_be_empty out
'
@@ -129,12 +150,15 @@ test_expect_success 'add -n -u should not add but just report' '
echo "remove '\''top'\''"
) >expect &&
before=$(git ls-files -s check top) &&
+ git count-objects -v >objects_before &&
echo changed >>check &&
rm -f top &&
git add -n -u >actual &&
after=$(git ls-files -s check top) &&
+ git count-objects -v >objects_after &&
test "$before" = "$after" &&
+ test_cmp objects_before objects_after &&
test_cmp expect actual
'
@@ -147,13 +171,13 @@ test_expect_success 'add -u resolves unmerged paths' '
{
for path in path1 path2
do
- echo "100644 $one 1 $path"
- echo "100644 $two 2 $path"
- echo "100644 $three 3 $path"
- done
- echo "100644 $one 1 path3"
- echo "100644 $one 1 path4"
- echo "100644 $one 3 path5"
+ echo "100644 $one 1 $path" &&
+ echo "100644 $two 2 $path" &&
+ echo "100644 $three 3 $path" || return 1
+ done &&
+ echo "100644 $one 1 path3" &&
+ echo "100644 $one 1 path4" &&
+ echo "100644 $one 3 path5" &&
echo "100644 $one 3 path6"
} |
git update-index --index-info &&
@@ -170,8 +194,8 @@ test_expect_success 'add -u resolves unmerged paths' '
git add -u &&
git ls-files -s path1 path2 path3 path4 path5 path6 >actual &&
{
- echo "100644 $three 0 path1"
- echo "100644 $two 0 path3"
+ echo "100644 $three 0 path1" &&
+ echo "100644 $two 0 path3" &&
echo "100644 $two 0 path5"
} >expect &&
test_cmp expect actual
@@ -183,4 +207,15 @@ test_expect_success '"add -u non-existent" should fail' '
! grep "non-existent" actual
'
+test_expect_success '"commit -a" implies "add -u" if index becomes empty' '
+ git rm -rf \* &&
+ git commit -m clean-slate &&
+ test_commit file1 &&
+ rm file1.t &&
+ test_tick &&
+ git commit -a -m remove &&
+ git ls-tree HEAD: >out &&
+ test_must_be_empty out
+'
+
test_done
diff --git a/t/t2201-add-update-typechange.sh b/t/t2201-add-update-typechange.sh
index a4eec0a..dba62d6 100755
--- a/t/t2201-add-update-typechange.sh
+++ b/t/t2201-add-update-typechange.sh
@@ -2,6 +2,7 @@
test_description='more git add -u'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -97,17 +98,17 @@ test_expect_success modify '
"
} >expect &&
{
- cat expect
- echo ":100644 160000 $_empty $ZERO_OID T yonk"
+ cat expect &&
+ echo ":100644 160000 $_empty $ZERO_OID T yonk" &&
echo ":100644 000000 $_empty $ZERO_OID D zifmia"
} >expect-files &&
{
- cat expect
+ cat expect &&
echo ":000000 160000 $ZERO_OID $ZERO_OID A yonk"
} >expect-index &&
{
- echo "100644 $_empty 0 nitfol"
- echo "160000 $yomin 0 yomin"
+ echo "100644 $_empty 0 nitfol" &&
+ echo "160000 $yomin 0 yomin" &&
echo "160000 $yonk 0 yonk"
} >expect-final
'
diff --git a/t/t2202-add-addremove.sh b/t/t2202-add-addremove.sh
index 9ee6590..24c60bf 100755
--- a/t/t2202-add-addremove.sh
+++ b/t/t2202-add-addremove.sh
@@ -2,6 +2,7 @@
test_description='git add --all'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t2203-add-intent.sh b/t/t2203-add-intent.sh
index cf0175a..8fa44a9 100755
--- a/t/t2203-add-intent.sh
+++ b/t/t2203-add-intent.sh
@@ -2,6 +2,7 @@
test_description='Intent to add'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'intent to add' '
@@ -116,7 +117,7 @@ test_expect_success 'cache-tree does not ignore dir that has i-t-a entries' '
mkdir 2 &&
for f in 1 2/1 2/2 3
do
- echo "$f" >"$f"
+ echo "$f" >"$f" || return 1
done &&
git add 1 2/2 3 &&
git add -N 2/1 &&
@@ -172,7 +173,7 @@ test_expect_success 'rename detection finds the right names' '
git add -N third &&
git status | grep -v "^?" >actual.1 &&
- test_i18ngrep "renamed: *first -> third" actual.1 &&
+ test_grep "renamed: *first -> third" actual.1 &&
git status --porcelain | grep -v "^?" >actual.2 &&
cat >expected.2 <<-\EOF &&
@@ -212,8 +213,8 @@ test_expect_success 'double rename detection in status' '
git add -N third &&
git status | grep -v "^?" >actual.1 &&
- test_i18ngrep "renamed: *first -> second" actual.1 &&
- test_i18ngrep "renamed: *second -> third" actual.1 &&
+ test_grep "renamed: *first -> second" actual.1 &&
+ test_grep "renamed: *second -> third" actual.1 &&
git status --porcelain | grep -v "^?" >actual.2 &&
cat >expected.2 <<-\EOF &&
diff --git a/t/t2204-add-ignored.sh b/t/t2204-add-ignored.sh
index 2e07365..b7cf1e4 100755
--- a/t/t2204-add-ignored.sh
+++ b/t/t2204-add-ignored.sh
@@ -2,6 +2,7 @@
test_description='giving ignored paths to git add'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -35,7 +36,7 @@ do
'
test_expect_success "complaints for ignored $i output" '
- test_i18ngrep -e "Use -f if" err
+ test_grep -e "Use -f if" err
'
test_expect_success "complaints for ignored $i with unignored file" '
@@ -45,7 +46,7 @@ do
test_must_be_empty out
'
test_expect_success "complaints for ignored $i with unignored file output" '
- test_i18ngrep -e "Use -f if" err
+ test_grep -e "Use -f if" err
'
done
@@ -64,7 +65,7 @@ do
test_expect_success "complaints for ignored $i in dir output" '
(
cd dir &&
- test_i18ngrep -e "Use -f if" err
+ test_grep -e "Use -f if" err
)
'
done
@@ -84,7 +85,7 @@ do
test_expect_success "complaints for ignored $i in sub output" '
(
cd sub &&
- test_i18ngrep -e "Use -f if" err
+ test_grep -e "Use -f if" err
)
'
done
diff --git a/t/t2205-add-worktree-config.sh b/t/t2205-add-worktree-config.sh
new file mode 100755
index 0000000..98265ba
--- /dev/null
+++ b/t/t2205-add-worktree-config.sh
@@ -0,0 +1,266 @@
+#!/bin/sh
+
+test_description='directory traversal respects user config
+
+This test verifies the traversal of the directory tree when the traversal begins
+outside the repository. Two instances for which this can occur are tested:
+
+ 1) The user manually sets the worktree. For this instance, the test sets
+ the worktree two levels above the `.git` directory and checks whether we
+ are able to add to the index those files that are in either (1) the
+ manually configured worktree directory or (2) the standard worktree
+ location with respect to the `.git` directory (i.e. ensuring that the
+ encountered `.git` directory is not treated as belonging to a foreign
+ nested repository).
+ 2) The user manually sets the `git_dir` while the working directory is
+ outside the repository. The test checks that files inside the
+ repository can be added to the index.
+ '
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success '1a: setup--config worktree' '
+ mkdir test1 &&
+ (
+ cd test1 &&
+ test_create_repo repo &&
+ git --git-dir="repo/.git" config core.worktree "$(pwd)" &&
+
+ mkdir -p outside-tracked outside-untracked &&
+ mkdir -p repo/inside-tracked repo/inside-untracked &&
+ >file-tracked &&
+ >file-untracked &&
+ >outside-tracked/file &&
+ >outside-untracked/file &&
+ >repo/file-tracked &&
+ >repo/file-untracked &&
+ >repo/inside-tracked/file &&
+ >repo/inside-untracked/file &&
+
+ cat >expect-tracked-unsorted <<-EOF &&
+ ../file-tracked
+ ../outside-tracked/file
+ file-tracked
+ inside-tracked/file
+ EOF
+
+ cat >expect-untracked-unsorted <<-EOF &&
+ ../file-untracked
+ ../outside-untracked/file
+ file-untracked
+ inside-untracked/file
+ EOF
+
+ cat >expect-all-dir-unsorted <<-EOF &&
+ ../file-untracked
+ ../file-tracked
+ ../outside-untracked/
+ ../outside-tracked/
+ ./
+ EOF
+
+ cat expect-tracked-unsorted expect-untracked-unsorted >expect-all-unsorted &&
+
+ cat >.gitignore <<-EOF
+ .gitignore
+ actual-*
+ expect-*
+ EOF
+ )
+'
+
+test_expect_success '1b: pre-add all' '
+ (
+ cd test1 &&
+ local parent_dir="$(pwd)" &&
+ git -C repo ls-files -o --exclude-standard "$parent_dir" >actual-all-unsorted &&
+ sort actual-all-unsorted >actual-all &&
+ sort expect-all-unsorted >expect-all &&
+ test_cmp expect-all actual-all
+ )
+'
+
+test_expect_success '1c: pre-add dir all' '
+ (
+ cd test1 &&
+ local parent_dir="$(pwd)" &&
+ git -C repo ls-files -o --directory --exclude-standard "$parent_dir" >actual-all-dir-unsorted &&
+ sort actual-all-dir-unsorted >actual-all &&
+ sort expect-all-dir-unsorted >expect-all &&
+ test_cmp expect-all actual-all
+ )
+'
+
+test_expect_success '1d: post-add tracked' '
+ (
+ cd test1 &&
+ local parent_dir="$(pwd)" &&
+ (
+ cd repo &&
+ git add file-tracked &&
+ git add inside-tracked &&
+ git add ../outside-tracked &&
+ git add "$parent_dir/file-tracked" &&
+ git ls-files "$parent_dir" >../actual-tracked-unsorted
+ ) &&
+ sort actual-tracked-unsorted >actual-tracked &&
+ sort expect-tracked-unsorted >expect-tracked &&
+ test_cmp expect-tracked actual-tracked
+ )
+'
+
+test_expect_success '1e: post-add untracked' '
+ (
+ cd test1 &&
+ local parent_dir="$(pwd)" &&
+ git -C repo ls-files -o --exclude-standard "$parent_dir" >actual-untracked-unsorted &&
+ sort actual-untracked-unsorted >actual-untracked &&
+ sort expect-untracked-unsorted >expect-untracked &&
+ test_cmp expect-untracked actual-untracked
+ )
+'
+
+test_expect_success '2a: setup--set git-dir' '
+ mkdir test2 &&
+ (
+ cd test2 &&
+ test_create_repo repo &&
+ # create two foreign repositories that should remain untracked
+ test_create_repo repo-outside &&
+ test_create_repo repo/repo-inside &&
+
+ mkdir -p repo/inside-tracked repo/inside-untracked &&
+ >repo/file-tracked &&
+ >repo/file-untracked &&
+ >repo/inside-tracked/file &&
+ >repo/inside-untracked/file &&
+ >repo-outside/file &&
+ >repo/repo-inside/file &&
+
+ cat >expect-tracked-unsorted <<-EOF &&
+ repo/file-tracked
+ repo/inside-tracked/file
+ EOF
+
+ cat >expect-untracked-unsorted <<-EOF &&
+ repo/file-untracked
+ repo/inside-untracked/file
+ repo/repo-inside/
+ repo-outside/
+ EOF
+
+ cat >expect-all-dir-unsorted <<-EOF &&
+ repo/
+ repo-outside/
+ EOF
+
+ cat expect-tracked-unsorted expect-untracked-unsorted >expect-all-unsorted &&
+
+ cat >.gitignore <<-EOF
+ .gitignore
+ actual-*
+ expect-*
+ EOF
+ )
+'
+
+test_expect_success '2b: pre-add all' '
+ (
+ cd test2 &&
+ git --git-dir=repo/.git ls-files -o --exclude-standard >actual-all-unsorted &&
+ sort actual-all-unsorted >actual-all &&
+ sort expect-all-unsorted >expect-all &&
+ test_cmp expect-all actual-all
+ )
+'
+
+test_expect_success '2c: pre-add dir all' '
+ (
+ cd test2 &&
+ git --git-dir=repo/.git ls-files -o --directory --exclude-standard >actual-all-dir-unsorted &&
+ sort actual-all-dir-unsorted >actual-all &&
+ sort expect-all-dir-unsorted >expect-all &&
+ test_cmp expect-all actual-all
+ )
+'
+
+test_expect_success '2d: post-add tracked' '
+ (
+ cd test2 &&
+ git --git-dir=repo/.git add repo/file-tracked &&
+ git --git-dir=repo/.git add repo/inside-tracked &&
+ git --git-dir=repo/.git ls-files >actual-tracked-unsorted &&
+ sort actual-tracked-unsorted >actual-tracked &&
+ sort expect-tracked-unsorted >expect-tracked &&
+ test_cmp expect-tracked actual-tracked
+ )
+'
+
+test_expect_success '2e: post-add untracked' '
+ (
+ cd test2 &&
+ git --git-dir=repo/.git ls-files -o --exclude-standard >actual-untracked-unsorted &&
+ sort actual-untracked-unsorted >actual-untracked &&
+ sort expect-untracked-unsorted >expect-untracked &&
+ test_cmp expect-untracked actual-untracked
+ )
+'
+
+test_expect_success '3a: setup--add repo dir' '
+ mkdir test3 &&
+ (
+ cd test3 &&
+ test_create_repo repo &&
+
+ mkdir -p repo/inside-tracked repo/inside-ignored &&
+ >repo/file-tracked &&
+ >repo/file-ignored &&
+ >repo/inside-tracked/file &&
+ >repo/inside-ignored/file &&
+
+ cat >.gitignore <<-EOF &&
+ .gitignore
+ actual-*
+ expect-*
+ *ignored
+ EOF
+
+ cat >expect-tracked-unsorted <<-EOF &&
+ repo/file-tracked
+ repo/inside-tracked/file
+ EOF
+
+ cat >expect-ignored-unsorted <<-EOF
+ repo/file-ignored
+ repo/inside-ignored/
+ .gitignore
+ actual-ignored-unsorted
+ expect-ignored-unsorted
+ expect-tracked-unsorted
+ EOF
+ )
+'
+
+test_expect_success '3b: ignored' '
+ (
+ cd test3 &&
+ git --git-dir=repo/.git ls-files -io --directory --exclude-standard >actual-ignored-unsorted &&
+ sort actual-ignored-unsorted >actual-ignored &&
+ sort expect-ignored-unsorted >expect-ignored &&
+ test_cmp expect-ignored actual-ignored
+ )
+'
+
+test_expect_success '3c: add repo' '
+ (
+ cd test3 &&
+ git --git-dir=repo/.git add repo &&
+ git --git-dir=repo/.git ls-files >actual-tracked-unsorted &&
+ sort actual-tracked-unsorted >actual-tracked &&
+ sort expect-tracked-unsorted >expect-tracked &&
+ test_cmp expect-tracked actual-tracked
+ )
+'
+
+test_done
diff --git a/t/t2300-cd-to-toplevel.sh b/t/t2300-cd-to-toplevel.sh
index c8de6d8..b40eeb2 100755
--- a/t/t2300-cd-to-toplevel.sh
+++ b/t/t2300-cd-to-toplevel.sh
@@ -2,6 +2,7 @@
test_description='cd_to_toplevel'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
EXEC_PATH="$(git --exec-path)"
diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh
index 37ad794..ba320dc 100755
--- a/t/t2400-worktree-add.sh
+++ b/t/t2400-worktree-add.sh
@@ -5,6 +5,7 @@ test_description='test git worktree add'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_CREATE_REPO_NO_TEMPLATE=1
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-rebase.sh
@@ -120,7 +121,30 @@ test_expect_success '"add" worktree creating new branch' '
test_expect_success 'die the same branch is already checked out' '
(
cd here &&
- test_must_fail git checkout newmain
+ test_must_fail git checkout newmain 2>actual &&
+ grep "already used by worktree at" actual
+ )
+'
+
+test_expect_success 'refuse to reset a branch in use elsewhere' '
+ (
+ cd here &&
+
+ # we know we are on detached HEAD but just in case ...
+ git checkout --detach HEAD &&
+ git rev-parse --verify HEAD >old.head &&
+
+ git rev-parse --verify refs/heads/newmain >old.branch &&
+ test_must_fail git checkout -B newmain 2>error &&
+ git rev-parse --verify refs/heads/newmain >new.branch &&
+ git rev-parse --verify HEAD >new.head &&
+
+ grep "already used by worktree at" error &&
+ test_cmp old.branch new.branch &&
+ test_cmp old.head new.head &&
+
+ # and we must be still on the same detached HEAD state
+ test_must_fail git symbolic-ref HEAD
)
'
@@ -165,8 +189,62 @@ test_expect_success '"add" default branch of a bare repo' '
(
git clone --bare . bare2 &&
cd bare2 &&
- git worktree add ../there3 main
- )
+ git worktree add ../there3 main &&
+ cd ../there3 &&
+ # Simple check that a Git command does not
+ # immediately fail with the current setup
+ git status
+ ) &&
+ cat >expect <<-EOF &&
+ init.t
+ EOF
+ ls there3 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '"add" to bare repo with worktree config' '
+ (
+ git clone --bare . bare3 &&
+ cd bare3 &&
+ git config extensions.worktreeconfig true &&
+
+ # Add config values that are erroneous to have in
+ # a config.worktree file outside of the main
+ # working tree, to check that Git filters them out
+ # when copying config during "git worktree add".
+ git config --worktree core.bare true &&
+ git config --worktree core.worktree "$(pwd)" &&
+
+ # We want to check that bogus.key is copied
+ git config --worktree bogus.key value &&
+ git config --unset core.bare &&
+ git worktree add ../there4 main &&
+ cd ../there4 &&
+
+ # Simple check that a Git command does not
+ # immediately fail with the current setup
+ git status &&
+ git worktree add --detach ../there5 &&
+ cd ../there5 &&
+ git status
+ ) &&
+
+ # the worktree has the arbitrary value copied.
+ test_cmp_config -C there4 value bogus.key &&
+ test_cmp_config -C there5 value bogus.key &&
+
+ # however, core.bare and core.worktree were removed.
+ test_must_fail git -C there4 config core.bare &&
+ test_must_fail git -C there4 config core.worktree &&
+
+ cat >expect <<-EOF &&
+ init.t
+ EOF
+
+ ls there4 >actual &&
+ test_cmp expect actual &&
+ ls there5 >actual &&
+ test_cmp expect actual
'
test_expect_success 'checkout with grafts' '
@@ -175,6 +253,7 @@ test_expect_success 'checkout with grafts' '
SHA1=$(git rev-parse HEAD) &&
test_commit def &&
test_commit xyz &&
+ mkdir .git/info &&
echo "$(git rev-parse HEAD) $SHA1" >.git/info/grafts &&
cat >expected <<-\EOF &&
xyz
@@ -242,17 +321,24 @@ test_expect_success '"add" no auto-vivify with --detach and <branch> omitted' '
test_must_fail git -C mish/mash symbolic-ref HEAD
'
-test_expect_success '"add" -b/-B mutually exclusive' '
- test_must_fail git worktree add -b poodle -B poodle bamboo main
-'
-
-test_expect_success '"add" -b/--detach mutually exclusive' '
- test_must_fail git worktree add -b poodle --detach bamboo main
-'
+# Helper function to test mutually exclusive options.
+#
+# Note: Quoted arguments containing spaces are not supported.
+test_wt_add_excl () {
+ local opts="$*" &&
+ test_expect_success "'worktree add' with '$opts' has mutually exclusive options" '
+ test_must_fail git worktree add $opts 2>actual &&
+ grep -E "fatal:( options)? .* cannot be used together" actual
+ '
+}
-test_expect_success '"add" -B/--detach mutually exclusive' '
- test_must_fail git worktree add -B poodle --detach bamboo main
-'
+test_wt_add_excl -b poodle -B poodle bamboo main
+test_wt_add_excl -b poodle --detach bamboo main
+test_wt_add_excl -B poodle --detach bamboo main
+test_wt_add_excl --orphan --detach bamboo
+test_wt_add_excl --orphan --no-checkout bamboo
+test_wt_add_excl --orphan bamboo main
+test_wt_add_excl --orphan -b bamboo wtdir/ main
test_expect_success '"add -B" fails if the branch is checked out' '
git rev-parse newmain >before &&
@@ -270,10 +356,111 @@ test_expect_success 'add -B' '
'
test_expect_success 'add --quiet' '
+ test_when_finished "git worktree remove -f -f another-worktree" &&
git worktree add --quiet another-worktree main 2>actual &&
test_must_be_empty actual
'
+test_expect_success 'add --quiet -b' '
+ test_when_finished "git branch -D quietnewbranch" &&
+ test_when_finished "git worktree remove -f -f another-worktree" &&
+ git worktree add --quiet -b quietnewbranch another-worktree 2>actual &&
+ test_must_be_empty actual
+'
+
+test_expect_success '"add --orphan"' '
+ test_when_finished "git worktree remove -f -f orphandir" &&
+ git worktree add --orphan -b neworphan orphandir &&
+ echo refs/heads/neworphan >expected &&
+ git -C orphandir symbolic-ref HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '"add --orphan (no -b)"' '
+ test_when_finished "git worktree remove -f -f neworphan" &&
+ git worktree add --orphan neworphan &&
+ echo refs/heads/neworphan >expected &&
+ git -C neworphan symbolic-ref HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '"add --orphan --quiet"' '
+ test_when_finished "git worktree remove -f -f orphandir" &&
+ git worktree add --quiet --orphan -b neworphan orphandir 2>log.actual &&
+ test_must_be_empty log.actual &&
+ echo refs/heads/neworphan >expected &&
+ git -C orphandir symbolic-ref HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '"add --orphan" fails if the branch already exists' '
+ test_when_finished "git branch -D existingbranch" &&
+ git worktree add -b existingbranch orphandir main &&
+ git worktree remove orphandir &&
+ test_must_fail git worktree add --orphan -b existingbranch orphandir
+'
+
+test_expect_success '"add --orphan" with empty repository' '
+ test_when_finished "rm -rf empty_repo" &&
+ echo refs/heads/newbranch >expected &&
+ GIT_DIR="empty_repo" git init --bare &&
+ git -C empty_repo worktree add --orphan -b newbranch worktreedir &&
+ git -C empty_repo/worktreedir symbolic-ref HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '"add" worktree with orphan branch and lock' '
+ git worktree add --lock --orphan -b orphanbr orphan-with-lock &&
+ test_when_finished "git worktree unlock orphan-with-lock || :" &&
+ test -f .git/worktrees/orphan-with-lock/locked
+'
+
+test_expect_success '"add" worktree with orphan branch, lock, and reason' '
+ lock_reason="why not" &&
+ git worktree add --detach --lock --reason "$lock_reason" orphan-with-lock-reason main &&
+ test_when_finished "git worktree unlock orphan-with-lock-reason || :" &&
+ test -f .git/worktrees/orphan-with-lock-reason/locked &&
+ echo "$lock_reason" >expect &&
+ test_cmp expect .git/worktrees/orphan-with-lock-reason/locked
+'
+
+# Note: Quoted arguments containing spaces are not supported.
+test_wt_add_orphan_hint () {
+ local context="$1" &&
+ local use_branch="$2" &&
+ shift 2 &&
+ local opts="$*" &&
+ test_expect_success "'worktree add' show orphan hint in bad/orphan HEAD w/ $context" '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (cd repo && test_commit commit) &&
+ git -C repo switch --orphan noref &&
+ test_must_fail git -C repo worktree add $opts foobar/ 2>actual &&
+ ! grep "error: unknown switch" actual &&
+ grep "hint: If you meant to create a worktree containing a new unborn branch" actual &&
+ if [ $use_branch -eq 1 ]
+ then
+ grep -E "^hint: +git worktree add --orphan -b [^ ]+ [^ ]+$" actual
+ else
+ grep -E "^hint: +git worktree add --orphan [^ ]+$" actual
+ fi
+
+ '
+}
+
+test_wt_add_orphan_hint 'no opts' 0
+test_wt_add_orphan_hint '-b' 1 -b foobar_branch
+test_wt_add_orphan_hint '-B' 1 -B foobar_branch
+
+test_expect_success "'worktree add' doesn't show orphan hint in bad/orphan HEAD w/ --quiet" '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (cd repo && test_commit commit) &&
+ test_must_fail git -C repo worktree add --quiet foobar_branch foobar/ 2>actual &&
+ ! grep "error: unknown switch" actual &&
+ ! grep "hint: If you meant to create a worktree containing a new unborn branch" actual
+'
+
test_expect_success 'local clone from linked checkout' '
git clone --local here here-clone &&
( cd here-clone && git fsck )
@@ -303,7 +490,8 @@ test_expect_success 'put a worktree under rebase' '
cd under-rebase &&
set_fake_editor &&
FAKE_LINES="edit 1" git rebase -i HEAD^ &&
- git worktree list | grep "under-rebase.*detached HEAD"
+ git worktree list >actual &&
+ grep "under-rebase.*detached HEAD" actual
)
'
@@ -344,7 +532,8 @@ test_expect_success 'checkout a branch under bisect' '
git bisect start &&
git bisect bad &&
git bisect good HEAD~2 &&
- git worktree list | grep "under-bisect.*detached HEAD" &&
+ git worktree list >actual &&
+ grep "under-bisect.*detached HEAD" actual &&
test_must_fail git worktree add new-bisect under-bisect &&
! test -d new-bisect
)
@@ -390,6 +579,14 @@ setup_remote_repo () {
)
}
+test_expect_success '"add" <path> <remote/branch> w/ no HEAD' '
+ test_when_finished rm -rf repo_upstream repo_local foo &&
+ setup_remote_repo repo_upstream repo_local &&
+ git -C repo_local config --bool core.bare true &&
+ git -C repo_local branch -D main &&
+ git -C repo_local worktree add ./foo repo_upstream/foo
+'
+
test_expect_success '--no-track avoids setting up tracking' '
test_when_finished rm -rf repo_upstream repo_local foo &&
setup_remote_repo repo_upstream repo_local &&
@@ -472,6 +669,35 @@ test_expect_success 'git worktree add --guess-remote sets up tracking' '
test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
)
'
+test_expect_success 'git worktree add --guess-remote sets up tracking (quiet)' '
+ test_when_finished rm -rf repo_a repo_b foo &&
+ setup_remote_repo repo_a repo_b &&
+ (
+ cd repo_b &&
+ git worktree add --quiet --guess-remote ../foo 2>actual &&
+ test_must_be_empty actual
+ ) &&
+ (
+ cd foo &&
+ test_branch_upstream foo repo_a foo &&
+ test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
+ )
+'
+
+test_expect_success 'git worktree --no-guess-remote (quiet)' '
+ test_when_finished rm -rf repo_a repo_b foo &&
+ setup_remote_repo repo_a repo_b &&
+ (
+ cd repo_b &&
+ git worktree add --quiet --no-guess-remote ../foo
+ ) &&
+ (
+ cd foo &&
+ test_must_fail git config "branch.foo.remote" &&
+ test_must_fail git config "branch.foo.merge" &&
+ test_cmp_rev ! refs/remotes/repo_a/foo refs/heads/foo
+ )
+'
test_expect_success 'git worktree add with worktree.guessRemote sets up tracking' '
test_when_finished rm -rf repo_a repo_b foo &&
@@ -504,11 +730,352 @@ test_expect_success 'git worktree --no-guess-remote option overrides config' '
)
'
+test_dwim_orphan () {
+ local info_text="No possible source branch, inferring '--orphan'" &&
+ local fetch_error_text="fatal: No local or remote refs exist despite at least one remote" &&
+ local orphan_hint="hint: If you meant to create a worktree containing a new unborn branch" &&
+ local invalid_ref_regex="^fatal: invalid reference: " &&
+ local bad_combo_regex="^fatal: options '[-a-z]*' and '[-a-z]*' cannot be used together" &&
+
+ local git_ns="repo" &&
+ local dashc_args="-C $git_ns" &&
+ local use_cd=0 &&
+
+ local bad_head=0 &&
+ local empty_repo=1 &&
+ local local_ref=0 &&
+ local use_quiet=0 &&
+ local remote=0 &&
+ local remote_ref=0 &&
+ local use_detach=0 &&
+ local use_new_branch=0 &&
+
+ local outcome="$1" &&
+ local outcome_text &&
+ local success &&
+ shift &&
+ local args="" &&
+ local context="" &&
+ case "$outcome" in
+ "infer")
+ success=1 &&
+ outcome_text='"add" DWIM infer --orphan'
+ ;;
+ "no_infer")
+ success=1 &&
+ outcome_text='"add" DWIM doesnt infer --orphan'
+ ;;
+ "fetch_error")
+ success=0 &&
+ outcome_text='"add" error need fetch'
+ ;;
+ "fatal_orphan_bad_combo")
+ success=0 &&
+ outcome_text='"add" error inferred "--orphan" gives illegal opts combo'
+ ;;
+ "warn_bad_head")
+ success=0 &&
+ outcome_text='"add" error, warn on bad HEAD, hint use orphan'
+ ;;
+ *)
+ echo "test_dwim_orphan(): invalid outcome: '$outcome'" >&2 &&
+ return 1
+ ;;
+ esac &&
+ while [ $# -gt 0 ]
+ do
+ case "$1" in
+ # How and from where to create the worktree
+ "-C_repo")
+ use_cd=0 &&
+ git_ns="repo" &&
+ dashc_args="-C $git_ns" &&
+ context="$context, 'git -C repo'"
+ ;;
+ "-C_wt")
+ use_cd=0 &&
+ git_ns="wt" &&
+ dashc_args="-C $git_ns" &&
+ context="$context, 'git -C wt'"
+ ;;
+ "cd_repo")
+ use_cd=1 &&
+ git_ns="repo" &&
+ dashc_args="" &&
+ context="$context, 'cd repo && git'"
+ ;;
+ "cd_wt")
+ use_cd=1 &&
+ git_ns="wt" &&
+ dashc_args="" &&
+ context="$context, 'cd wt && git'"
+ ;;
+
+ # Bypass the "pull first" warning
+ "force")
+ args="$args --force" &&
+ context="$context, --force"
+ ;;
+
+ # Try to use remote refs when DWIM
+ "guess_remote")
+ args="$args --guess-remote" &&
+ context="$context, --guess-remote"
+ ;;
+ "no_guess_remote")
+ args="$args --no-guess-remote" &&
+ context="$context, --no-guess-remote"
+ ;;
+
+ # Whether there is at least one local branch present
+ "local_ref")
+ empty_repo=0 &&
+ local_ref=1 &&
+ context="$context, >=1 local branches"
+ ;;
+ "no_local_ref")
+ empty_repo=0 &&
+ context="$context, 0 local branches"
+ ;;
+
+ # Whether the HEAD points at a valid ref (skip this opt when no refs)
+ "good_head")
+ # requires: local_ref
+ context="$context, valid HEAD"
+ ;;
+ "bad_head")
+ bad_head=1 &&
+ context="$context, invalid (or orphan) HEAD"
+ ;;
+
+ # Whether the code path is tested with the base add command, -b, or --detach
+ "no_-b")
+ use_new_branch=0 &&
+ context="$context, no --branch"
+ ;;
+ "-b")
+ use_new_branch=1 &&
+ context="$context, --branch"
+ ;;
+ "detach")
+ use_detach=1 &&
+ context="$context, --detach"
+ ;;
+
+ # Whether to check that all output is suppressed (except errors)
+ # or that the output is as expected
+ "quiet")
+ use_quiet=1 &&
+ args="$args --quiet" &&
+ context="$context, --quiet"
+ ;;
+ "no_quiet")
+ use_quiet=0 &&
+ context="$context, no --quiet (expect output)"
+ ;;
+
+ # Whether there is at least one remote attached to the repo
+ "remote")
+ empty_repo=0 &&
+ remote=1 &&
+ context="$context, >=1 remotes"
+ ;;
+ "no_remote")
+ empty_repo=0 &&
+ remote=0 &&
+ context="$context, 0 remotes"
+ ;;
+
+ # Whether there is at least one valid remote ref
+ "remote_ref")
+ # requires: remote
+ empty_repo=0 &&
+ remote_ref=1 &&
+ context="$context, >=1 fetched remote branches"
+ ;;
+ "no_remote_ref")
+ empty_repo=0 &&
+ remote_ref=0 &&
+ context="$context, 0 fetched remote branches"
+ ;;
+
+ # Options or flags that become illegal when --orphan is inferred
+ "no_checkout")
+ args="$args --no-checkout" &&
+ context="$context, --no-checkout"
+ ;;
+ "track")
+ args="$args --track" &&
+ context="$context, --track"
+ ;;
+
+ # All other options are illegal
+ *)
+ echo "test_dwim_orphan(): invalid arg: '$1'" >&2 &&
+ return 1
+ ;;
+ esac &&
+ shift
+ done &&
+ context="${context#', '}" &&
+ if [ $use_new_branch -eq 1 ]
+ then
+ args="$args -b foo"
+ elif [ $use_detach -eq 1 ]
+ then
+ args="$args --detach"
+ else
+ context="DWIM (no --branch), $context"
+ fi &&
+ if [ $empty_repo -eq 1 ]
+ then
+ context="empty repo, $context"
+ fi &&
+ args="$args ../foo" &&
+ context="${context%', '}" &&
+ test_expect_success "$outcome_text w/ $context" '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ if [ $local_ref -eq 1 ] && [ "$git_ns" = "repo" ]
+ then
+ (cd repo && test_commit commit) &&
+ if [ $bad_head -eq 1 ]
+ then
+ git -C repo symbolic-ref HEAD refs/heads/badbranch
+ fi
+ elif [ $local_ref -eq 1 ] && [ "$git_ns" = "wt" ]
+ then
+ test_when_finished "git -C repo worktree remove -f ../wt" &&
+ git -C repo worktree add --orphan -b main ../wt &&
+ (cd wt && test_commit commit) &&
+ if [ $bad_head -eq 1 ]
+ then
+ git -C wt symbolic-ref HEAD refs/heads/badbranch
+ fi
+ elif [ $local_ref -eq 0 ] && [ "$git_ns" = "wt" ]
+ then
+ test_when_finished "git -C repo worktree remove -f ../wt" &&
+ git -C repo worktree add --orphan -b orphanbranch ../wt
+ fi &&
+
+ if [ $remote -eq 1 ]
+ then
+ test_when_finished "rm -rf upstream" &&
+ git init upstream &&
+ (cd upstream && test_commit commit) &&
+ git -C upstream switch -c foo &&
+ git -C repo remote add upstream ../upstream
+ fi &&
+
+ if [ $remote_ref -eq 1 ]
+ then
+ git -C repo fetch
+ fi &&
+ if [ $success -eq 1 ]
+ then
+ test_when_finished git -C repo worktree remove ../foo
+ fi &&
+ (
+ if [ $use_cd -eq 1 ]
+ then
+ cd $git_ns
+ fi &&
+ if [ "$outcome" = "infer" ]
+ then
+ git $dashc_args worktree add $args 2>actual &&
+ if [ $use_quiet -eq 1 ]
+ then
+ test_must_be_empty actual
+ else
+ grep "$info_text" actual
+ fi
+ elif [ "$outcome" = "no_infer" ]
+ then
+ git $dashc_args worktree add $args 2>actual &&
+ if [ $use_quiet -eq 1 ]
+ then
+ test_must_be_empty actual
+ else
+ ! grep "$info_text" actual
+ fi
+ elif [ "$outcome" = "fetch_error" ]
+ then
+ test_must_fail git $dashc_args worktree add $args 2>actual &&
+ grep "$fetch_error_text" actual
+ elif [ "$outcome" = "fatal_orphan_bad_combo" ]
+ then
+ test_must_fail git $dashc_args worktree add $args 2>actual &&
+ if [ $use_quiet -eq 1 ]
+ then
+ ! grep "$info_text" actual
+ else
+ grep "$info_text" actual
+ fi &&
+ grep "$bad_combo_regex" actual
+ elif [ "$outcome" = "warn_bad_head" ]
+ then
+ test_must_fail git $dashc_args worktree add $args 2>actual &&
+ if [ $use_quiet -eq 1 ]
+ then
+ grep "$invalid_ref_regex" actual &&
+ ! grep "$orphan_hint" actual
+ else
+ headpath=$(git $dashc_args rev-parse --path-format=absolute --git-path HEAD) &&
+ headcontents=$(cat "$headpath") &&
+ grep "HEAD points to an invalid (or orphaned) reference" actual &&
+ grep "HEAD path: .$headpath." actual &&
+ grep "HEAD contents: .$headcontents." actual &&
+ grep "$orphan_hint" actual &&
+ ! grep "$info_text" actual
+ fi &&
+ grep "$invalid_ref_regex" actual
+ else
+ # Unreachable
+ false
+ fi
+ ) &&
+ if [ $success -ne 1 ]
+ then
+ test_path_is_missing foo
+ fi
+ '
+}
+
+for quiet_mode in "no_quiet" "quiet"
+do
+ for changedir_type in "cd_repo" "cd_wt" "-C_repo" "-C_wt"
+ do
+ dwim_test_args="$quiet_mode $changedir_type"
+ test_dwim_orphan 'infer' $dwim_test_args no_-b
+ test_dwim_orphan 'no_infer' $dwim_test_args no_-b local_ref good_head
+ test_dwim_orphan 'infer' $dwim_test_args no_-b no_local_ref no_remote no_remote_ref no_guess_remote
+ test_dwim_orphan 'infer' $dwim_test_args no_-b no_local_ref remote no_remote_ref no_guess_remote
+ test_dwim_orphan 'fetch_error' $dwim_test_args no_-b no_local_ref remote no_remote_ref guess_remote
+ test_dwim_orphan 'infer' $dwim_test_args no_-b no_local_ref remote no_remote_ref guess_remote force
+ test_dwim_orphan 'no_infer' $dwim_test_args no_-b no_local_ref remote remote_ref guess_remote
+
+ test_dwim_orphan 'infer' $dwim_test_args -b
+ test_dwim_orphan 'no_infer' $dwim_test_args -b local_ref good_head
+ test_dwim_orphan 'infer' $dwim_test_args -b no_local_ref no_remote no_remote_ref no_guess_remote
+ test_dwim_orphan 'infer' $dwim_test_args -b no_local_ref remote no_remote_ref no_guess_remote
+ test_dwim_orphan 'infer' $dwim_test_args -b no_local_ref remote no_remote_ref guess_remote
+ test_dwim_orphan 'infer' $dwim_test_args -b no_local_ref remote remote_ref guess_remote
+
+ test_dwim_orphan 'warn_bad_head' $dwim_test_args no_-b local_ref bad_head
+ test_dwim_orphan 'warn_bad_head' $dwim_test_args -b local_ref bad_head
+ test_dwim_orphan 'warn_bad_head' $dwim_test_args detach local_ref bad_head
+ done
+
+ test_dwim_orphan 'fatal_orphan_bad_combo' $quiet_mode no_-b no_checkout
+ test_dwim_orphan 'fatal_orphan_bad_combo' $quiet_mode no_-b track
+ test_dwim_orphan 'fatal_orphan_bad_combo' $quiet_mode -b no_checkout
+ test_dwim_orphan 'fatal_orphan_bad_combo' $quiet_mode -b track
+done
+
post_checkout_hook () {
- gitdir=${1:-.git}
- test_when_finished "rm -f $gitdir/hooks/post-checkout" &&
- mkdir -p $gitdir/hooks &&
- write_script $gitdir/hooks/post-checkout <<-\EOF
+ test_when_finished "rm -rf .git/hooks" &&
+ mkdir .git/hooks &&
+ test_hook -C "$1" post-checkout <<-\EOF
{
echo $*
git rev-parse --git-dir --show-toplevel
@@ -614,6 +1181,7 @@ test_expect_success '"add" should not fail because of another bad worktree' '
'
test_expect_success '"add" with uninitialized submodule, with submodule.recurse unset' '
+ test_config_global protocol.file.allow always &&
test_create_repo submodule &&
test_commit -C submodule first &&
test_create_repo project &&
@@ -629,6 +1197,7 @@ test_expect_success '"add" with uninitialized submodule, with submodule.recurse
'
test_expect_success '"add" with initialized submodule, with submodule.recurse unset' '
+ test_config_global protocol.file.allow always &&
git -C project-clone submodule update --init &&
git -C project-clone worktree add ../project-4
'
diff --git a/t/t2401-worktree-prune.sh b/t/t2401-worktree-prune.sh
index a615d3b..71aa9bc 100755
--- a/t/t2401-worktree-prune.sh
+++ b/t/t2401-worktree-prune.sh
@@ -5,6 +5,7 @@ test_description='prune $GIT_DIR/worktrees'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success initialize '
@@ -19,7 +20,7 @@ test_expect_success 'worktree prune on normal repo' '
test_expect_success 'prune files inside $GIT_DIR/worktrees' '
mkdir .git/worktrees &&
: >.git/worktrees/abc &&
- git worktree prune --verbose >actual &&
+ git worktree prune --verbose 2>actual &&
cat >expect <<EOF &&
Removing worktrees/abc: not a valid directory
EOF
@@ -34,7 +35,7 @@ test_expect_success 'prune directories without gitdir' '
cat >expect <<EOF &&
Removing worktrees/def: gitdir file does not exist
EOF
- git worktree prune --verbose >actual &&
+ git worktree prune --verbose 2>actual &&
test_cmp expect actual &&
! test -d .git/worktrees/def &&
! test -d .git/worktrees
@@ -45,8 +46,8 @@ test_expect_success SANITY 'prune directories with unreadable gitdir' '
: >.git/worktrees/def/def &&
: >.git/worktrees/def/gitdir &&
chmod u-r .git/worktrees/def/gitdir &&
- git worktree prune --verbose >actual &&
- test_i18ngrep "Removing worktrees/def: unable to read gitdir file" actual &&
+ git worktree prune --verbose 2>actual &&
+ test_grep "Removing worktrees/def: unable to read gitdir file" actual &&
! test -d .git/worktrees/def &&
! test -d .git/worktrees
'
@@ -55,8 +56,8 @@ test_expect_success 'prune directories with invalid gitdir' '
mkdir -p .git/worktrees/def/abc &&
: >.git/worktrees/def/def &&
: >.git/worktrees/def/gitdir &&
- git worktree prune --verbose >actual &&
- test_i18ngrep "Removing worktrees/def: invalid gitdir file" actual &&
+ git worktree prune --verbose 2>actual &&
+ test_grep "Removing worktrees/def: invalid gitdir file" actual &&
! test -d .git/worktrees/def &&
! test -d .git/worktrees
'
@@ -65,8 +66,8 @@ test_expect_success 'prune directories with gitdir pointing to nowhere' '
mkdir -p .git/worktrees/def/abc &&
: >.git/worktrees/def/def &&
echo "$(pwd)"/nowhere >.git/worktrees/def/gitdir &&
- git worktree prune --verbose >actual &&
- test_i18ngrep "Removing worktrees/def: gitdir file points to non-existent location" actual &&
+ git worktree prune --verbose 2>actual &&
+ test_grep "Removing worktrees/def: gitdir file points to non-existent location" actual &&
! test -d .git/worktrees/def &&
! test -d .git/worktrees
'
@@ -101,8 +102,8 @@ test_expect_success 'prune duplicate (linked/linked)' '
git worktree add --detach w2 &&
sed "s/w2/w1/" .git/worktrees/w2/gitdir >.git/worktrees/w2/gitdir.new &&
mv .git/worktrees/w2/gitdir.new .git/worktrees/w2/gitdir &&
- git worktree prune --verbose >actual &&
- test_i18ngrep "duplicate entry" actual &&
+ git worktree prune --verbose 2>actual &&
+ test_grep "duplicate entry" actual &&
test -d .git/worktrees/w1 &&
! test -d .git/worktrees/w2
'
@@ -114,8 +115,8 @@ test_expect_success 'prune duplicate (main/linked)' '
git -C repo worktree add --detach ../wt &&
rm -fr wt &&
mv repo wt &&
- git -C wt worktree prune --verbose >actual &&
- test_i18ngrep "duplicate entry" actual &&
+ git -C wt worktree prune --verbose 2>actual &&
+ test_grep "duplicate entry" actual &&
! test -d .git/worktrees/wt
'
diff --git a/t/t2402-worktree-list.sh b/t/t2402-worktree-list.sh
index 4012bd6..33ea9cb 100755
--- a/t/t2402-worktree-list.sh
+++ b/t/t2402-worktree-list.sh
@@ -5,6 +5,7 @@ test_description='test git worktree list'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -64,6 +65,25 @@ test_expect_success '"list" all worktrees --porcelain' '
test_cmp expect actual
'
+test_expect_success '"list" all worktrees --porcelain -z' '
+ test_when_finished "rm -rf here _actual actual expect &&
+ git worktree prune" &&
+ printf "worktree %sQHEAD %sQbranch %sQQ" \
+ "$(git rev-parse --show-toplevel)" \
+ $(git rev-parse HEAD --symbolic-full-name HEAD) >expect &&
+ git worktree add --detach here main &&
+ printf "worktree %sQHEAD %sQdetachedQQ" \
+ "$(git -C here rev-parse --show-toplevel)" \
+ "$(git rev-parse HEAD)" >>expect &&
+ git worktree list --porcelain -z >_actual &&
+ nul_to_q <_actual >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '"list" -z fails without --porcelain' '
+ test_must_fail git worktree list -z
+'
+
test_expect_success '"list" all worktrees with locked annotation' '
test_when_finished "rm -rf locked unlocked out && git worktree prune" &&
git worktree add --detach locked main &&
@@ -123,7 +143,7 @@ test_expect_success '"list" all worktrees --porcelain with prunable' '
rm -rf prunable &&
git worktree list --porcelain >out &&
sed -n "/^worktree .*\/prunable$/,/^$/p" <out >only_prunable &&
- test_i18ngrep "^prunable gitdir file points to non-existent location$" only_prunable
+ test_grep "^prunable gitdir file points to non-existent location$" only_prunable
'
test_expect_success '"list" all worktrees with prunable consistent with "prune"' '
@@ -134,9 +154,9 @@ test_expect_success '"list" all worktrees with prunable consistent with "prune"'
git worktree list >out &&
grep "/prunable *[0-9a-f].* prunable$" out &&
! grep "/unprunable *[0-9a-f].* unprunable$" out &&
- git worktree prune --verbose >out &&
- test_i18ngrep "^Removing worktrees/prunable" out &&
- test_i18ngrep ! "^Removing worktrees/unprunable" out
+ git worktree prune --verbose 2>out &&
+ test_grep "^Removing worktrees/prunable" out &&
+ test_grep ! "^Removing worktrees/unprunable" out
'
test_expect_success '"list" --verbose and --porcelain mutually exclusive' '
diff --git a/t/t2403-worktree-move.sh b/t/t2403-worktree-move.sh
index a4e1a17..901342e 100755
--- a/t/t2403-worktree-move.sh
+++ b/t/t2403-worktree-move.sh
@@ -2,6 +2,7 @@
test_description='test git worktree move, remove, lock and unlock'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -138,7 +139,8 @@ test_expect_success 'move a repo with uninitialized submodule' '
(
cd withsub &&
test_commit initial &&
- git submodule add "$PWD"/.git sub &&
+ git -c protocol.file.allow=always \
+ submodule add "$PWD"/.git sub &&
git commit -m withsub &&
git worktree add second HEAD &&
git worktree move second third
@@ -148,7 +150,7 @@ test_expect_success 'move a repo with uninitialized submodule' '
test_expect_success 'not move a repo with initialized submodule' '
(
cd withsub &&
- git -C third submodule update &&
+ git -c protocol.file.allow=always -C third submodule update &&
test_must_fail git worktree move third forth
)
'
@@ -200,7 +202,7 @@ test_expect_success 'proper error when worktree not found' '
for i in noodle noodle/bork
do
test_must_fail git worktree lock $i 2>err &&
- test_i18ngrep "not a working tree" err || return 1
+ test_grep "not a working tree" err || return 1
done
'
@@ -227,6 +229,7 @@ test_expect_success 'remove cleans up .git/worktrees when empty' '
'
test_expect_success 'remove a repo with uninitialized submodule' '
+ test_config_global protocol.file.allow always &&
(
cd withsub &&
git worktree add to-remove HEAD &&
@@ -235,6 +238,7 @@ test_expect_success 'remove a repo with uninitialized submodule' '
'
test_expect_success 'not remove a repo with initialized submodule' '
+ test_config_global protocol.file.allow always &&
(
cd withsub &&
git worktree add to-remove HEAD &&
diff --git a/t/t2404-worktree-config.sh b/t/t2404-worktree-config.sh
index 9536d10..842937b 100755
--- a/t/t2404-worktree-config.sh
+++ b/t/t2404-worktree-config.sh
@@ -2,6 +2,7 @@
test_description="config file in multi worktree"
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t2405-worktree-submodule.sh b/t/t2405-worktree-submodule.sh
index b172c26..11018f3 100755
--- a/t/t2405-worktree-submodule.sh
+++ b/t/t2405-worktree-submodule.sh
@@ -10,6 +10,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
base_path=$(pwd -P)
test_expect_success 'setup: create origin repos' '
+ git config --global protocol.file.allow always &&
git init origin/sub &&
test_commit -C origin/sub file1 &&
git init origin/main &&
diff --git a/t/t2406-worktree-repair.sh b/t/t2406-worktree-repair.sh
index f737418..edbf502 100755
--- a/t/t2406-worktree-repair.sh
+++ b/t/t2406-worktree-repair.sh
@@ -2,6 +2,7 @@
test_description='test git worktree repair'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -24,7 +25,7 @@ test_expect_success 'worktree path not directory' '
>notdir &&
test_must_fail git worktree repair >out 2>err &&
test_must_be_empty out &&
- test_i18ngrep "not a directory" err
+ test_grep "not a directory" err
'
test_expect_success "don't clobber .git repo" '
@@ -34,7 +35,7 @@ test_expect_success "don't clobber .git repo" '
test_create_repo repo &&
test_must_fail git worktree repair >out 2>err &&
test_must_be_empty out &&
- test_i18ngrep ".git is not a file" err
+ test_grep ".git is not a file" err
'
test_corrupt_gitfile () {
@@ -45,9 +46,8 @@ test_corrupt_gitfile () {
git worktree add --detach corrupt &&
git -C corrupt rev-parse --absolute-git-dir >expect &&
eval "$butcher" &&
- git -C "$repairdir" worktree repair >out 2>err &&
- test_i18ngrep "$problem" out &&
- test_must_be_empty err &&
+ git -C "$repairdir" worktree repair 2>err &&
+ test_grep "$problem" err &&
git -C corrupt rev-parse --absolute-git-dir >actual &&
test_cmp expect actual
}
@@ -93,7 +93,7 @@ test_expect_success 'repair .git file from bare.git' '
test_expect_success 'invalid worktree path' '
test_must_fail git worktree repair /notvalid >out 2>err &&
test_must_be_empty out &&
- test_i18ngrep "not a valid path" err
+ test_grep "not a valid path" err
'
test_expect_success 'repo not found; .git not file' '
@@ -101,7 +101,7 @@ test_expect_success 'repo not found; .git not file' '
test_create_repo not-a-worktree &&
test_must_fail git worktree repair not-a-worktree >out 2>err &&
test_must_be_empty out &&
- test_i18ngrep ".git is not a file" err
+ test_grep ".git is not a file" err
'
test_expect_success 'repo not found; .git not referencing repo' '
@@ -111,7 +111,7 @@ test_expect_success 'repo not found; .git not referencing repo' '
mv side/.newgit side/.git &&
mkdir not-a-repo &&
test_must_fail git worktree repair side 2>err &&
- test_i18ngrep ".git file does not reference a repository" err
+ test_grep ".git file does not reference a repository" err
'
test_expect_success 'repo not found; .git file broken' '
@@ -121,7 +121,7 @@ test_expect_success 'repo not found; .git file broken' '
mv orig moved &&
test_must_fail git worktree repair moved >out 2>err &&
test_must_be_empty out &&
- test_i18ngrep ".git file broken" err
+ test_grep ".git file broken" err
'
test_expect_success 'repair broken gitdir' '
@@ -130,10 +130,9 @@ test_expect_success 'repair broken gitdir' '
sed s,orig/\.git$,moved/.git, .git/worktrees/orig/gitdir >expect &&
rm .git/worktrees/orig/gitdir &&
mv orig moved &&
- git worktree repair moved >out 2>err &&
+ git worktree repair moved 2>err &&
test_cmp expect .git/worktrees/orig/gitdir &&
- test_i18ngrep "gitdir unreadable" out &&
- test_must_be_empty err
+ test_grep "gitdir unreadable" err
'
test_expect_success 'repair incorrect gitdir' '
@@ -141,10 +140,9 @@ test_expect_success 'repair incorrect gitdir' '
git worktree add --detach orig &&
sed s,orig/\.git$,moved/.git, .git/worktrees/orig/gitdir >expect &&
mv orig moved &&
- git worktree repair moved >out 2>err &&
+ git worktree repair moved 2>err &&
test_cmp expect .git/worktrees/orig/gitdir &&
- test_i18ngrep "gitdir incorrect" out &&
- test_must_be_empty err
+ test_grep "gitdir incorrect" err
'
test_expect_success 'repair gitdir (implicit) from linked worktree' '
@@ -152,10 +150,9 @@ test_expect_success 'repair gitdir (implicit) from linked worktree' '
git worktree add --detach orig &&
sed s,orig/\.git$,moved/.git, .git/worktrees/orig/gitdir >expect &&
mv orig moved &&
- git -C moved worktree repair >out 2>err &&
+ git -C moved worktree repair 2>err &&
test_cmp expect .git/worktrees/orig/gitdir &&
- test_i18ngrep "gitdir incorrect" out &&
- test_must_be_empty err
+ test_grep "gitdir incorrect" err
'
test_expect_success 'unable to repair gitdir (implicit) from main worktree' '
@@ -163,9 +160,8 @@ test_expect_success 'unable to repair gitdir (implicit) from main worktree' '
git worktree add --detach orig &&
cat .git/worktrees/orig/gitdir >expect &&
mv orig moved &&
- git worktree repair >out 2>err &&
+ git worktree repair 2>err &&
test_cmp expect .git/worktrees/orig/gitdir &&
- test_must_be_empty out &&
test_must_be_empty err
'
@@ -178,12 +174,11 @@ test_expect_success 'repair multiple gitdir files' '
sed s,orig2/\.git$,moved2/.git, .git/worktrees/orig2/gitdir >expect2 &&
mv orig1 moved1 &&
mv orig2 moved2 &&
- git worktree repair moved1 moved2 >out 2>err &&
+ git worktree repair moved1 moved2 2>err &&
test_cmp expect1 .git/worktrees/orig1/gitdir &&
test_cmp expect2 .git/worktrees/orig2/gitdir &&
- test_i18ngrep "gitdir incorrect:.*orig1/gitdir$" out &&
- test_i18ngrep "gitdir incorrect:.*orig2/gitdir$" out &&
- test_must_be_empty err
+ test_grep "gitdir incorrect:.*orig1/gitdir$" err &&
+ test_grep "gitdir incorrect:.*orig2/gitdir$" err
'
test_expect_success 'repair moved main and linked worktrees' '
diff --git a/t/t2407-worktree-heads.sh b/t/t2407-worktree-heads.sh
new file mode 100755
index 0000000..f6835c9
--- /dev/null
+++ b/t/t2407-worktree-heads.sh
@@ -0,0 +1,180 @@
+#!/bin/sh
+
+test_description='test operations trying to overwrite refs at worktree HEAD'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit init &&
+
+ for i in 1 2 3 4
+ do
+ git checkout -b conflict-$i &&
+ echo "not I" >$i.t &&
+ git add $i.t &&
+ git commit -m "will conflict" &&
+
+ git checkout - &&
+ test_commit $i &&
+ git branch wt-$i &&
+ git branch fake-$i &&
+ git worktree add wt-$i wt-$i || return 1
+ done &&
+
+ # Create a server that updates each branch by one commit
+ git init server &&
+ test_commit -C server initial &&
+ git remote add server ./server &&
+ for i in 1 2 3 4
+ do
+ git -C server checkout -b wt-$i &&
+ test_commit -C server A-$i || return 1
+ done &&
+ for i in 1 2
+ do
+ git -C server checkout -b fake-$i &&
+ test_commit -C server f-$i || return 1
+ done
+'
+
+test_expect_success 'refuse to overwrite: checked out in worktree' '
+ for i in 1 2 3 4
+ do
+ test_must_fail git branch -f wt-$i HEAD 2>err &&
+ grep "cannot force update the branch" err &&
+
+ test_must_fail git branch -D wt-$i 2>err &&
+ grep "cannot delete branch" err || return 1
+ done
+'
+
+test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in bisect' '
+ test_when_finished git -C wt-4 bisect reset &&
+
+ # Set up a bisect so HEAD no longer points to wt-4.
+ git -C wt-4 bisect start &&
+ git -C wt-4 bisect bad wt-4 &&
+ git -C wt-4 bisect good wt-1 &&
+
+ test_must_fail git branch -f wt-4 HEAD 2>err &&
+ grep "cannot force update the branch '\''wt-4'\'' used by worktree at.*wt-4" err
+'
+
+test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase (apply)' '
+ test_when_finished git -C wt-2 rebase --abort &&
+
+ # This will fail part-way through due to a conflict.
+ test_must_fail git -C wt-2 rebase --apply conflict-2 &&
+
+ test_must_fail git branch -f wt-2 HEAD 2>err &&
+ grep "cannot force update the branch '\''wt-2'\'' used by worktree at.*wt-2" err
+'
+
+test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase (merge)' '
+ test_when_finished git -C wt-2 rebase --abort &&
+
+ # This will fail part-way through due to a conflict.
+ test_must_fail git -C wt-2 rebase conflict-2 &&
+
+ test_must_fail git branch -f wt-2 HEAD 2>err &&
+ grep "cannot force update the branch '\''wt-2'\'' used by worktree at.*wt-2" err
+'
+
+test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase with --update-refs' '
+ test_when_finished git -C wt-3 rebase --abort &&
+
+ git branch -f can-be-updated wt-3 &&
+ test_must_fail git -C wt-3 rebase --update-refs conflict-3 &&
+
+ for i in 3 4
+ do
+ test_must_fail git branch -f can-be-updated HEAD 2>err &&
+ grep "cannot force update the branch '\''can-be-updated'\'' used by worktree at.*wt-3" err ||
+ return 1
+ done
+'
+
+test_expect_success !SANITIZE_LEAK 'refuse to fetch over ref: checked out' '
+ test_must_fail git fetch server +refs/heads/wt-3:refs/heads/wt-3 2>err &&
+ grep "refusing to fetch into branch '\''refs/heads/wt-3'\''" err &&
+
+ # General fetch into refs/heads/ will fail on first ref,
+ # so use a generic error message check.
+ test_must_fail git fetch server +refs/heads/*:refs/heads/* 2>err &&
+ grep "refusing to fetch into branch" err
+'
+
+test_expect_success !SANITIZE_LEAK 'refuse to fetch over ref: worktree in bisect' '
+ test_when_finished git -C wt-4 bisect reset &&
+
+ # Set up a bisect so HEAD no longer points to wt-4.
+ git -C wt-4 bisect start &&
+ git -C wt-4 bisect bad wt-4 &&
+ git -C wt-4 bisect good wt-1 &&
+
+ test_must_fail git fetch server +refs/heads/wt-4:refs/heads/wt-4 2>err &&
+ grep "refusing to fetch into branch" err
+'
+
+test_expect_success !SANITIZE_LEAK 'refuse to fetch over ref: worktree in rebase' '
+ test_when_finished git -C wt-3 rebase --abort &&
+
+ # This will fail part-way through due to a conflict.
+ test_must_fail git -C wt-3 rebase conflict-3 &&
+
+ test_must_fail git fetch server +refs/heads/wt-3:refs/heads/wt-3 2>err &&
+ grep "refusing to fetch into branch" err
+'
+
+test_expect_success 'refuse to overwrite when in error states' '
+ test_when_finished rm -rf .git/worktrees/wt-*/rebase-merge &&
+ test_when_finished rm -rf .git/worktrees/wt-*/BISECT_* &&
+
+ # Both branches are currently under rebase.
+ mkdir -p .git/worktrees/wt-3/rebase-merge &&
+ touch .git/worktrees/wt-3/rebase-merge/interactive &&
+ echo refs/heads/fake-1 >.git/worktrees/wt-3/rebase-merge/head-name &&
+ echo refs/heads/fake-2 >.git/worktrees/wt-3/rebase-merge/onto &&
+ mkdir -p .git/worktrees/wt-4/rebase-merge &&
+ touch .git/worktrees/wt-4/rebase-merge/interactive &&
+ echo refs/heads/fake-2 >.git/worktrees/wt-4/rebase-merge/head-name &&
+ echo refs/heads/fake-1 >.git/worktrees/wt-4/rebase-merge/onto &&
+
+ # Both branches are currently under bisect.
+ touch .git/worktrees/wt-4/BISECT_LOG &&
+ echo refs/heads/fake-2 >.git/worktrees/wt-4/BISECT_START &&
+ touch .git/worktrees/wt-1/BISECT_LOG &&
+ echo refs/heads/fake-1 >.git/worktrees/wt-1/BISECT_START &&
+
+ for i in 1 2
+ do
+ test_must_fail git branch -f fake-$i HEAD 2>err &&
+ grep "cannot force update the branch '\''fake-$i'\'' used by worktree at" err ||
+ return 1
+ done
+'
+
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+test_expect_success !SANITIZE_LEAK 'refuse to overwrite during rebase with --update-refs' '
+ git commit --fixup HEAD~2 --allow-empty &&
+ (
+ set_cat_todo_editor &&
+ test_must_fail git rebase -i --update-refs HEAD~3 >todo &&
+ ! grep "update-refs" todo
+ ) &&
+ git branch -f allow-update HEAD~2 &&
+ (
+ set_cat_todo_editor &&
+ test_must_fail git rebase -i --update-refs HEAD~3 >todo &&
+ grep "update-ref refs/heads/allow-update" todo
+ )
+'
+
+# This must be the last test in this file
+test_expect_success '$EDITOR and friends are unchanged' '
+ test_editor_unchanged
+'
+
+test_done
diff --git a/t/t2500-untracked-overwriting.sh b/t/t2500-untracked-overwriting.sh
new file mode 100755
index 0000000..5c0bf4d
--- /dev/null
+++ b/t/t2500-untracked-overwriting.sh
@@ -0,0 +1,244 @@
+#!/bin/sh
+
+test_description='Test handling of overwriting untracked files'
+
+. ./test-lib.sh
+
+test_setup_reset () {
+ git init reset_$1 &&
+ (
+ cd reset_$1 &&
+ test_commit init &&
+
+ git branch stable &&
+ git branch work &&
+
+ git checkout work &&
+ test_commit foo &&
+
+ git checkout stable
+ )
+}
+
+test_expect_success 'reset --hard will nuke untracked files/dirs' '
+ test_setup_reset hard &&
+ (
+ cd reset_hard &&
+ git ls-tree -r stable &&
+ git log --all --name-status --oneline &&
+ git ls-tree -r work &&
+
+ mkdir foo.t &&
+ echo precious >foo.t/file &&
+ echo foo >expect &&
+
+ git reset --hard work &&
+
+ # check that untracked directory foo.t/ was nuked
+ test_path_is_file foo.t &&
+ test_cmp expect foo.t
+ )
+'
+
+test_expect_success 'reset --merge will preserve untracked files/dirs' '
+ test_setup_reset merge &&
+ (
+ cd reset_merge &&
+
+ mkdir foo.t &&
+ echo precious >foo.t/file &&
+ cp foo.t/file expect &&
+
+ test_must_fail git reset --merge work 2>error &&
+ test_cmp expect foo.t/file &&
+ grep "Updating .foo.t. would lose untracked files" error
+ )
+'
+
+test_expect_success 'reset --keep will preserve untracked files/dirs' '
+ test_setup_reset keep &&
+ (
+ cd reset_keep &&
+
+ mkdir foo.t &&
+ echo precious >foo.t/file &&
+ cp foo.t/file expect &&
+
+ test_must_fail git reset --merge work 2>error &&
+ test_cmp expect foo.t/file &&
+ grep "Updating.*foo.t.*would lose untracked files" error
+ )
+'
+
+test_setup_checkout_m () {
+ git init checkout &&
+ (
+ cd checkout &&
+ test_commit init &&
+
+ test_write_lines file has some >filler &&
+ git add filler &&
+ git commit -m filler &&
+
+ git branch stable &&
+
+ git switch -c work &&
+ echo stuff >notes.txt &&
+ test_write_lines file has some words >filler &&
+ git add notes.txt filler &&
+ git commit -m filler &&
+
+ git checkout stable
+ )
+}
+
+test_expect_success 'checkout -m does not nuke untracked file' '
+ test_setup_checkout_m &&
+ (
+ cd checkout &&
+
+ # Tweak filler
+ test_write_lines this file has some >filler &&
+ # Make an untracked file, save its contents in "expect"
+ echo precious >notes.txt &&
+ cp notes.txt expect &&
+
+ test_must_fail git checkout -m work &&
+ test_cmp expect notes.txt
+ )
+'
+
+test_setup_sequencing () {
+ git init sequencing_$1 &&
+ (
+ cd sequencing_$1 &&
+ test_commit init &&
+
+ test_write_lines this file has some words >filler &&
+ git add filler &&
+ git commit -m filler &&
+
+ mkdir -p foo/bar &&
+ test_commit foo/bar/baz &&
+
+ git branch simple &&
+ git branch fooey &&
+
+ git checkout fooey &&
+ git rm foo/bar/baz.t &&
+ echo stuff >>filler &&
+ git add -u &&
+ git commit -m "changes" &&
+
+ git checkout simple &&
+ echo items >>filler &&
+ echo newstuff >>newfile &&
+ git add filler newfile &&
+ git commit -m another
+ )
+}
+
+test_expect_success 'git rebase --abort and untracked files' '
+ test_setup_sequencing rebase_abort_and_untracked &&
+ (
+ cd sequencing_rebase_abort_and_untracked &&
+ git checkout fooey &&
+ test_must_fail git rebase simple &&
+
+ cat init.t &&
+ git rm init.t &&
+ echo precious >init.t &&
+ cp init.t expect &&
+ git status --porcelain &&
+ test_must_fail git rebase --abort &&
+ test_cmp expect init.t
+ )
+'
+
+test_expect_success 'git rebase fast forwarding and untracked files' '
+ test_setup_sequencing rebase_fast_forward_and_untracked &&
+ (
+ cd sequencing_rebase_fast_forward_and_untracked &&
+ git checkout init &&
+ echo precious >filler &&
+ cp filler expect &&
+ test_must_fail git rebase init simple &&
+ test_cmp expect filler
+ )
+'
+
+test_expect_failure 'git rebase --autostash and untracked files' '
+ test_setup_sequencing rebase_autostash_and_untracked &&
+ (
+ cd sequencing_rebase_autostash_and_untracked &&
+ git checkout simple &&
+ git rm filler &&
+ mkdir filler &&
+ echo precious >filler/file &&
+ cp filler/file expect &&
+ git rebase --autostash init &&
+ test_path_is_file filler/file
+ )
+'
+
+test_expect_failure 'git stash and untracked files' '
+ test_setup_sequencing stash_and_untracked_files &&
+ (
+ cd sequencing_stash_and_untracked_files &&
+ git checkout simple &&
+ git rm filler &&
+ mkdir filler &&
+ echo precious >filler/file &&
+ cp filler/file expect &&
+ git status --porcelain &&
+ git stash push &&
+ git status --porcelain &&
+ test_path_is_file filler/file
+ )
+'
+
+test_expect_success 'git am --abort and untracked dir vs. unmerged file' '
+ test_setup_sequencing am_abort_and_untracked &&
+ (
+ cd sequencing_am_abort_and_untracked &&
+ git format-patch -1 --stdout fooey >changes.mbox &&
+ test_must_fail git am --3way changes.mbox &&
+
+ # Delete the conflicted file; we will stage and commit it later
+ rm filler &&
+
+ # Put an unrelated untracked directory there
+ mkdir filler &&
+ echo foo >filler/file1 &&
+ echo bar >filler/file2 &&
+
+ test_must_fail git am --abort 2>errors &&
+ test_path_is_dir filler &&
+ grep "Updating .filler. would lose untracked files in it" errors
+ )
+'
+
+test_expect_success 'git am --skip and untracked dir vs deleted file' '
+ test_setup_sequencing am_skip_and_untracked &&
+ (
+ cd sequencing_am_skip_and_untracked &&
+ git checkout fooey &&
+ git format-patch -1 --stdout simple >changes.mbox &&
+ test_must_fail git am --3way changes.mbox &&
+
+ # Delete newfile
+ rm newfile &&
+
+ # Put an unrelated untracked directory there
+ mkdir newfile &&
+ echo foo >newfile/file1 &&
+ echo bar >newfile/file2 &&
+
+ # Change our mind about resolutions, just skip this patch
+ test_must_fail git am --skip 2>errors &&
+ test_path_is_dir newfile &&
+ grep "Updating .newfile. would lose untracked files in it" errors
+ )
+'
+
+test_done
diff --git a/t/t2501-cwd-empty.sh b/t/t2501-cwd-empty.sh
new file mode 100755
index 0000000..f6d8d7d
--- /dev/null
+++ b/t/t2501-cwd-empty.sh
@@ -0,0 +1,277 @@
+#!/bin/sh
+
+test_description='Test handling of the current working directory becoming empty'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ test_commit init &&
+
+ git branch fd_conflict &&
+
+ mkdir -p foo/bar &&
+ test_commit foo/bar/baz &&
+
+ git revert HEAD &&
+ git tag reverted &&
+
+ git checkout fd_conflict &&
+ mkdir dirORfile &&
+ test_commit dirORfile/foo &&
+
+ git rm -r dirORfile &&
+ echo not-a-directory >dirORfile &&
+ git add dirORfile &&
+ git commit -m dirORfile &&
+
+ git switch -c df_conflict HEAD~1 &&
+ test_commit random_file &&
+
+ git switch -c undo_fd_conflict fd_conflict &&
+ git revert HEAD
+'
+
+test_incidental_dir_removal () {
+ test_when_finished "git reset --hard" &&
+
+ git checkout foo/bar/baz^{commit} &&
+ test_path_is_dir foo/bar &&
+
+ (
+ cd foo &&
+ "$@" &&
+
+ # Make sure foo still exists, and commands needing it work
+ test-tool getcwd &&
+ git status --porcelain
+ ) &&
+ test_path_is_missing foo/bar/baz &&
+ test_path_is_missing foo/bar &&
+
+ test_path_is_dir foo
+}
+
+test_required_dir_removal () {
+ git checkout df_conflict^{commit} &&
+ test_when_finished "git clean -fdx" &&
+
+ (
+ cd dirORfile &&
+
+ # Ensure command refuses to run
+ test_must_fail "$@" 2>../error &&
+ grep "Refusing to remove.*current working directory" ../error &&
+
+ # ...and that the index and working tree are left clean
+ git diff --exit-code HEAD &&
+
+ # Ensure that getcwd and git status do not error out (which
+ # they might if the current working directory had been removed)
+ test-tool getcwd &&
+ git status --porcelain
+ ) &&
+
+ test_path_is_dir dirORfile
+}
+
+test_expect_success 'checkout does not clean cwd incidentally' '
+ test_incidental_dir_removal git checkout init
+'
+
+test_expect_success 'checkout fails if cwd needs to be removed' '
+ test_required_dir_removal git checkout fd_conflict
+'
+
+test_expect_success 'reset --hard does not clean cwd incidentally' '
+ test_incidental_dir_removal git reset --hard init
+'
+
+test_expect_success 'reset --hard fails if cwd needs to be removed' '
+ test_required_dir_removal git reset --hard fd_conflict
+'
+
+test_expect_success 'merge does not clean cwd incidentally' '
+ test_incidental_dir_removal git merge reverted
+'
+
+# This file uses some simple merges where
+# Base: 'dirORfile/' exists
+# Side1: random other file changed
+# Side2: 'dirORfile/' removed, 'dirORfile' added
+# this should resolve cleanly, but merge-recursive throws merge conflicts
+# because it's dumb. Add a special test for checking merge-recursive (and
+# merge-ort), then after this just hard require ort for all remaining tests.
+#
+test_expect_success 'merge fails if cwd needs to be removed; recursive friendly' '
+ git checkout foo/bar/baz &&
+ test_when_finished "git clean -fdx" &&
+
+ mkdir dirORfile &&
+ (
+ cd dirORfile &&
+
+ test_must_fail git merge fd_conflict 2>../error
+ ) &&
+
+ test_path_is_dir dirORfile &&
+ grep "Refusing to remove the current working directory" error
+'
+
+GIT_TEST_MERGE_ALGORITHM=ort
+
+test_expect_success 'merge fails if cwd needs to be removed' '
+ test_required_dir_removal git merge fd_conflict
+'
+
+test_expect_success 'cherry-pick does not clean cwd incidentally' '
+ test_incidental_dir_removal git cherry-pick reverted
+'
+
+test_expect_success 'cherry-pick fails if cwd needs to be removed' '
+ test_required_dir_removal git cherry-pick fd_conflict
+'
+
+test_expect_success 'rebase does not clean cwd incidentally' '
+ test_incidental_dir_removal git rebase reverted
+'
+
+test_expect_success 'rebase fails if cwd needs to be removed' '
+ test_required_dir_removal git rebase fd_conflict
+'
+
+test_expect_success 'revert does not clean cwd incidentally' '
+ test_incidental_dir_removal git revert HEAD
+'
+
+test_expect_success 'revert fails if cwd needs to be removed' '
+ test_required_dir_removal git revert undo_fd_conflict
+'
+
+test_expect_success 'rm does not clean cwd incidentally' '
+ test_incidental_dir_removal git rm bar/baz.t
+'
+
+test_expect_success 'apply does not remove cwd incidentally' '
+ git diff HEAD HEAD~1 >patch &&
+ test_incidental_dir_removal git apply ../patch
+'
+
+test_incidental_untracked_dir_removal () {
+ test_when_finished "git reset --hard" &&
+
+ git checkout foo/bar/baz^{commit} &&
+ mkdir -p untracked &&
+ mkdir empty
+ >untracked/random &&
+
+ (
+ cd untracked &&
+ "$@" &&
+
+ # Make sure untracked still exists, and commands needing it work
+ test-tool getcwd &&
+ git status --porcelain
+ ) &&
+ test_path_is_missing empty &&
+ test_path_is_missing untracked/random &&
+
+ test_path_is_dir untracked
+}
+
+test_expect_success 'clean does not remove cwd incidentally' '
+ test_incidental_untracked_dir_removal \
+ git -C .. clean -fd -e warnings . >warnings &&
+ grep "Refusing to remove current working directory" warnings
+'
+
+test_expect_success 'stash does not remove cwd incidentally' '
+ test_incidental_untracked_dir_removal \
+ git stash --include-untracked
+'
+
+test_expect_success '`rm -rf dir` only removes a subset of dir' '
+ test_when_finished "rm -rf a/" &&
+
+ mkdir -p a/b/c &&
+ >a/b/c/untracked &&
+ >a/b/c/tracked &&
+ git add a/b/c/tracked &&
+
+ (
+ cd a/b &&
+ git rm -rf ../b
+ ) &&
+
+ test_path_is_dir a/b &&
+ test_path_is_missing a/b/c/tracked &&
+ test_path_is_file a/b/c/untracked
+'
+
+test_expect_success '`rm -rf dir` even with only tracked files will remove something else' '
+ test_when_finished "rm -rf a/" &&
+
+ mkdir -p a/b/c &&
+ >a/b/c/tracked &&
+ git add a/b/c/tracked &&
+
+ (
+ cd a/b &&
+ git rm -rf ../b
+ ) &&
+
+ test_path_is_missing a/b/c/tracked &&
+ test_path_is_missing a/b/c &&
+ test_path_is_dir a/b
+'
+
+test_expect_success 'git version continues working from a deleted dir' '
+ mkdir tmp &&
+ (
+ cd tmp &&
+ rm -rf ../tmp &&
+ git version
+ )
+'
+
+test_submodule_removal () {
+ path_status=$1 &&
+ shift &&
+
+ test_status=
+ test "$path_status" = dir && test_status=test_must_fail
+
+ test_when_finished "git reset --hard HEAD~1" &&
+ test_when_finished "rm -rf .git/modules/my_submodule" &&
+
+ git checkout foo/bar/baz &&
+
+ git init my_submodule &&
+ touch my_submodule/file &&
+ git -C my_submodule add file &&
+ git -C my_submodule commit -m "initial commit" &&
+ git submodule add ./my_submodule &&
+ git commit -m "Add the submodule" &&
+
+ (
+ cd my_submodule &&
+ $test_status "$@"
+ ) &&
+
+ test_path_is_${path_status} my_submodule
+}
+
+test_expect_success 'rm -r with -C leaves submodule if cwd inside' '
+ test_submodule_removal dir git -C .. rm -r my_submodule/
+'
+
+test_expect_success 'rm -r leaves submodule if cwd inside' '
+ test_submodule_removal dir \
+ git --git-dir=../.git --work-tree=.. rm -r ../my_submodule/
+'
+
+test_expect_success 'rm -rf removes submodule even if cwd inside' '
+ test_submodule_removal missing \
+ git --git-dir=../.git --work-tree=.. rm -rf ../my_submodule/
+'
+
+test_done
diff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh
index 740ce56..11af455 100755
--- a/t/t3000-ls-files-others.sh
+++ b/t/t3000-ls-files-others.sh
@@ -15,6 +15,8 @@ filesystem.
path3/file3 - a file in a directory
path4 - an empty directory
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup ' '
diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh
index 516c95e..1ed0aa9 100755
--- a/t/t3001-ls-files-others-exclude.sh
+++ b/t/t3001-ls-files-others-exclude.sh
@@ -8,6 +8,7 @@ test_description='git ls-files --others --exclude
This test runs git ls-files --others and tests --exclude patterns.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
rm -fr one three
@@ -66,26 +67,26 @@ echo '!*.2
allignores='.gitignore one/.gitignore one/two/.gitignore'
-test_expect_success \
- 'git ls-files --others with various exclude options.' \
- 'git ls-files --others \
+test_expect_success 'git ls-files --others with various exclude options.' '
+ git ls-files --others \
--exclude=\*.6 \
--exclude-per-directory=.gitignore \
--exclude-from=.git/ignore \
- >output &&
- test_cmp expect output'
+ >output &&
+ test_cmp expect output
+'
# Test \r\n (MSDOS-like systems)
printf '*.1\r\n/*.3\r\n!*.6\r\n' >.gitignore
-test_expect_success \
- 'git ls-files --others with \r\n line endings.' \
- 'git ls-files --others \
+test_expect_success 'git ls-files --others with \r\n line endings.' '
+ git ls-files --others \
--exclude=\*.6 \
--exclude-per-directory=.gitignore \
--exclude-from=.git/ignore \
- >output &&
- test_cmp expect output'
+ >output &&
+ test_cmp expect output
+'
test_expect_success 'setup skip-worktree gitignore' '
git add $allignores &&
@@ -93,14 +94,14 @@ test_expect_success 'setup skip-worktree gitignore' '
rm $allignores
'
-test_expect_success \
- 'git ls-files --others with various exclude options.' \
- 'git ls-files --others \
+test_expect_success 'git ls-files --others with various exclude options.' '
+ git ls-files --others \
--exclude=\*.6 \
--exclude-per-directory=.gitignore \
--exclude-from=.git/ignore \
- >output &&
- test_cmp expect output'
+ >output &&
+ test_cmp expect output
+'
test_expect_success 'restore gitignore' '
git checkout --ignore-skip-worktree-bits $allignores &&
@@ -282,12 +283,12 @@ test_expect_success 'pattern matches prefix completely' '
'
test_expect_success 'ls-files with "**" patterns' '
- cat <<\EOF >expect &&
-a.1
-one/a.1
-one/two/a.1
-three/a.1
-EOF
+ cat <<-\EOF >expect &&
+ a.1
+ one/a.1
+ one/two/a.1
+ three/a.1
+ EOF
git ls-files -o -i --exclude "**/a.1" >actual &&
test_cmp expect actual
'
diff --git a/t/t3002-ls-files-dashpath.sh b/t/t3002-ls-files-dashpath.sh
index 8704b04..4dd2455 100755
--- a/t/t3002-ls-files-dashpath.sh
+++ b/t/t3002-ls-files-dashpath.sh
@@ -12,58 +12,66 @@ filesystem.
-foo - a file with a funny name.
-- - another file with a funny name.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
-test_expect_success \
- setup \
- 'echo frotz >path0 &&
+test_expect_success 'setup' '
+ echo frotz >path0 &&
echo frotz >./-foo &&
- echo frotz >./--'
+ echo frotz >./--
+'
-test_expect_success \
- 'git ls-files without path restriction.' \
- 'git ls-files --others >output &&
- test_cmp output - <<EOF
---
--foo
-output
-path0
-EOF
+test_expect_success 'git ls-files without path restriction.' '
+ test_when_finished "rm -f expect" &&
+ git ls-files --others >output &&
+ cat >expect <<-\EOF &&
+ --
+ -foo
+ output
+ path0
+ EOF
+ test_cmp output expect
'
-test_expect_success \
- 'git ls-files with path restriction.' \
- 'git ls-files --others path0 >output &&
- test_cmp output - <<EOF
-path0
-EOF
+test_expect_success 'git ls-files with path restriction.' '
+ test_when_finished "rm -f expect" &&
+ git ls-files --others path0 >output &&
+ cat >expect <<-\EOF &&
+ path0
+ EOF
+ test_cmp output expect
'
-test_expect_success \
- 'git ls-files with path restriction with --.' \
- 'git ls-files --others -- path0 >output &&
- test_cmp output - <<EOF
-path0
-EOF
+test_expect_success 'git ls-files with path restriction with --.' '
+ test_when_finished "rm -f expect" &&
+ git ls-files --others -- path0 >output &&
+ cat >expect <<-\EOF &&
+ path0
+ EOF
+ test_cmp output expect
'
-test_expect_success \
- 'git ls-files with path restriction with -- --.' \
- 'git ls-files --others -- -- >output &&
- test_cmp output - <<EOF
---
-EOF
+test_expect_success 'git ls-files with path restriction with -- --.' '
+ test_when_finished "rm -f expect" &&
+ git ls-files --others -- -- >output &&
+ cat >expect <<-\EOF &&
+ --
+ EOF
+ test_cmp output expect
'
-test_expect_success \
- 'git ls-files with no path restriction.' \
- 'git ls-files --others -- >output &&
- test_cmp output - <<EOF
---
--foo
-output
-path0
-EOF
+test_expect_success 'git ls-files with no path restriction.' '
+ test_when_finished "rm -f expect" &&
+ git ls-files --others -- >output &&
+ cat >expect <<-\EOF &&
+ --
+ -foo
+ output
+ path0
+ EOF
+ test_cmp output expect
+
'
test_done
diff --git a/t/t3003-ls-files-exclude.sh b/t/t3003-ls-files-exclude.sh
index c41c4f0..7933dff 100755
--- a/t/t3003-ls-files-exclude.sh
+++ b/t/t3003-ls-files-exclude.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='ls-files --exclude does not affect index files'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'create repo with file' '
diff --git a/t/t3004-ls-files-basic.sh b/t/t3004-ls-files-basic.sh
index 9fd5a1f..12e41a7 100755
--- a/t/t3004-ls-files-basic.sh
+++ b/t/t3004-ls-files-basic.sh
@@ -6,6 +6,7 @@ This test runs git ls-files with various unusual or malformed
command-line arguments.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'ls-files in empty repository' '
@@ -20,7 +21,7 @@ test_expect_success 'ls-files with nonexistent path' '
test_expect_success 'ls-files with nonsense option' '
test_expect_code 129 git ls-files --nonsense 2>actual &&
- test_i18ngrep "[Uu]sage: git ls-files" actual
+ test_grep "[Uu]sage: git ls-files" actual
'
test_expect_success 'ls-files -h in corrupt repository' '
@@ -31,7 +32,7 @@ test_expect_success 'ls-files -h in corrupt repository' '
>.git/index &&
test_expect_code 129 git ls-files -h >usage 2>&1
) &&
- test_i18ngrep "[Uu]sage: git ls-files " broken/usage
+ test_grep "[Uu]sage: git ls-files " broken/usage
'
test_expect_success SYMLINKS 'ls-files with absolute paths to symlinks' '
diff --git a/t/t3005-ls-files-relative.sh b/t/t3005-ls-files-relative.sh
index 727e9ae..fbfa210 100755
--- a/t/t3005-ls-files-relative.sh
+++ b/t/t3005-ls-files-relative.sh
@@ -5,6 +5,7 @@ test_description='ls-files tests with relative paths
This test runs git ls-files with various relative path arguments.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'prepare' '
@@ -38,10 +39,7 @@ test_expect_success 'ls-files with mixed levels' '
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"
- done >expect.err &&
+ printf "error: pathspec $SQ%s$SQ did not match any file(s) known to git\n" ../y* >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 &&
@@ -53,10 +51,7 @@ test_expect_success 'ls-files -c' '
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"
- done >expect.err &&
+ printf "error: pathspec $SQ%s$SQ did not match any file(s) known to git\n" ../x* >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 &&
diff --git a/t/t3006-ls-files-long.sh b/t/t3006-ls-files-long.sh
index e109c3f..2aaf91e 100755
--- a/t/t3006-ls-files-long.sh
+++ b/t/t3006-ls-files-long.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='overly long paths'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t3007-ls-files-recurse-submodules.sh b/t/t3007-ls-files-recurse-submodules.sh
index 4a08000..61771ee 100755
--- a/t/t3007-ls-files-recurse-submodules.sh
+++ b/t/t3007-ls-files-recurse-submodules.sh
@@ -34,6 +34,23 @@ test_expect_success 'ls-files correctly outputs files in submodule' '
test_cmp expect actual
'
+test_expect_success '--stage' '
+ GITMODULES_HASH=$(git rev-parse HEAD:.gitmodules) &&
+ A_HASH=$(git rev-parse HEAD:a) &&
+ B_HASH=$(git rev-parse HEAD:b/b) &&
+ C_HASH=$(git -C submodule rev-parse HEAD:c) &&
+
+ cat >expect <<-EOF &&
+ 100644 $GITMODULES_HASH 0 .gitmodules
+ 100644 $A_HASH 0 a
+ 100644 $B_HASH 0 b/b
+ 100644 $C_HASH 0 submodule/c
+ EOF
+
+ git ls-files --stage --recurse-submodules >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'ls-files correctly outputs files in submodule with -z' '
lf_to_nul >expect <<-\EOF &&
.gitmodules
@@ -279,20 +296,52 @@ test_expect_success '--recurse-submodules and relative paths' '
test_expect_success '--recurse-submodules does not support --error-unmatch' '
test_must_fail git ls-files --recurse-submodules --error-unmatch 2>actual &&
- test_i18ngrep "does not support --error-unmatch" actual
+ test_grep "does not support --error-unmatch" actual
+'
+
+test_expect_success '--recurse-submodules parses submodule repo config' '
+ test_config -C submodule index.sparse "invalid non-boolean value" &&
+ test_must_fail git ls-files --recurse-submodules 2>err &&
+ grep "bad boolean config value" err
+'
+
+test_expect_success '--recurse-submodules parses submodule worktree config' '
+ test_config -C submodule extensions.worktreeConfig true &&
+ test_config -C submodule --worktree index.sparse "invalid non-boolean value" &&
+
+ test_must_fail git ls-files --recurse-submodules 2>err &&
+ grep "bad boolean config value" err
+'
+
+test_expect_success '--recurse-submodules submodules ignore super project worktreeConfig extension' '
+ # Enable worktree config in both super project & submodule, set an
+ # invalid config in the submodule worktree config
+ test_config extensions.worktreeConfig true &&
+ test_config -C submodule extensions.worktreeConfig true &&
+ test_config -C submodule --worktree index.sparse "invalid non-boolean value" &&
+
+ # Now, disable the worktree config in the submodule. Note that we need
+ # to manually re-enable extensions.worktreeConfig when the test is
+ # finished, otherwise the test_unconfig of index.sparse will not work.
+ test_unconfig -C submodule extensions.worktreeConfig &&
+ test_when_finished "git -C submodule config extensions.worktreeConfig true" &&
+
+ # With extensions.worktreeConfig disabled in the submodule, the invalid
+ # worktree config is not picked up.
+ git ls-files --recurse-submodules 2>err &&
+ ! grep "bad boolean config value" err
'
test_incompatible_with_recurse_submodules () {
test_expect_success "--recurse-submodules and $1 are incompatible" "
test_must_fail git ls-files --recurse-submodules $1 2>actual &&
- test_i18ngrep 'unsupported mode' actual
+ test_grep 'unsupported mode' actual
"
}
test_incompatible_with_recurse_submodules --deleted
test_incompatible_with_recurse_submodules --modified
test_incompatible_with_recurse_submodules --others
-test_incompatible_with_recurse_submodules --stage
test_incompatible_with_recurse_submodules --killed
test_incompatible_with_recurse_submodules --unmerged
diff --git a/t/t3008-ls-files-lazy-init-name-hash.sh b/t/t3008-ls-files-lazy-init-name-hash.sh
index 85f3704..51d3dff 100755
--- a/t/t3008-ls-files-lazy-init-name-hash.sh
+++ b/t/t3008-ls-files-lazy-init-name-hash.sh
@@ -2,6 +2,7 @@
test_description='Test the lazy init name hash with various folder structures'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
if test 1 -eq $(test-tool online-cpus)
diff --git a/t/t3009-ls-files-others-nonsubmodule.sh b/t/t3009-ls-files-others-nonsubmodule.sh
index 963f346..14218b3 100755
--- a/t/t3009-ls-files-others-nonsubmodule.sh
+++ b/t/t3009-ls-files-others-nonsubmodule.sh
@@ -18,6 +18,7 @@ This test runs git ls-files --others with the following working tree:
git repository with a commit and an untracked file
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup: directories' '
diff --git a/t/t3010-ls-files-killed-modified.sh b/t/t3010-ls-files-killed-modified.sh
index 580e158..0541787 100755
--- a/t/t3010-ls-files-killed-modified.sh
+++ b/t/t3010-ls-files-killed-modified.sh
@@ -41,6 +41,8 @@ Also for modification test, the cache and working tree have:
We should report path0, path1, path2/file2, path3/file3, path7 and path8
modified without reporting path9 and path10. submod1 is also modified.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'git update-index --add to add various paths.' '
diff --git a/t/t3012-ls-files-dedup.sh b/t/t3012-ls-files-dedup.sh
index 2682b1f..190e2f6 100755
--- a/t/t3012-ls-files-dedup.sh
+++ b/t/t3012-ls-files-dedup.sh
@@ -2,6 +2,7 @@
test_description='git ls-files --deduplicate test'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t3013-ls-files-format.sh b/t/t3013-ls-files-format.sh
new file mode 100755
index 0000000..6e6ea0b
--- /dev/null
+++ b/t/t3013-ls-files-format.sh
@@ -0,0 +1,146 @@
+#!/bin/sh
+
+test_description='git ls-files --format test'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+for flag in -s -o -k -t --resolve-undo --deduplicate --eol
+do
+ test_expect_success "usage: --format is incompatible with $flag" '
+ test_expect_code 129 git ls-files --format="%(objectname)" $flag
+ '
+done
+
+test_expect_success 'setup' '
+ printf "LINEONE\nLINETWO\nLINETHREE\n" >o1.txt &&
+ printf "LINEONE\r\nLINETWO\r\nLINETHREE\r\n" >o2.txt &&
+ printf "LINEONE\r\nLINETWO\nLINETHREE\n" >o3.txt &&
+ git add o?.txt &&
+ oid=$(git hash-object o1.txt) &&
+ git update-index --add --cacheinfo 120000 $oid o4.txt &&
+ git update-index --add --cacheinfo 160000 $oid o5.txt &&
+ git update-index --add --cacheinfo 100755 $oid o6.txt &&
+ git commit -m base
+'
+
+test_expect_success 'git ls-files --format objectmode v.s. -s' '
+ git ls-files -s >files &&
+ cut -d" " -f1 files >expect &&
+ git ls-files --format="%(objectmode)" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git ls-files --format objectname v.s. -s' '
+ git ls-files -s >files &&
+ cut -d" " -f2 files >expect &&
+ git ls-files --format="%(objectname)" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git ls-files --format objecttype' '
+ git ls-files --format="%(objectname)" o1.txt o4.txt o6.txt >objectname &&
+ git cat-file --batch-check="%(objecttype)" >expect <objectname &&
+ git ls-files --format="%(objecttype)" o1.txt o4.txt o6.txt >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git ls-files --format objectsize' '
+ cat>expect <<-\EOF &&
+26
+29
+27
+26
+-
+26
+ EOF
+ git ls-files --format="%(objectsize)" >actual &&
+
+ test_cmp expect actual
+'
+
+test_expect_success 'git ls-files --format objectsize:padded' '
+ cat>expect <<-\EOF &&
+ 26
+ 29
+ 27
+ 26
+ -
+ 26
+ EOF
+ git ls-files --format="%(objectsize:padded)" >actual &&
+
+ test_cmp expect actual
+'
+
+test_expect_success 'git ls-files --format v.s. --eol' '
+ git ls-files --eol >tmp &&
+ sed -e "s/ / /g" -e "s/ */ /g" tmp >expect 2>err &&
+ test_must_be_empty err &&
+ git ls-files --format="i/%(eolinfo:index) w/%(eolinfo:worktree) attr/%(eolattr) %(path)" >actual 2>err &&
+ test_must_be_empty err &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git ls-files --format path v.s. -s' '
+ git ls-files -s >files &&
+ cut -f2 files >expect &&
+ git ls-files --format="%(path)" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git ls-files --format with relative path' '
+ cat >expect <<-\EOF &&
+ ../o1.txt
+ ../o2.txt
+ ../o3.txt
+ ../o4.txt
+ ../o5.txt
+ ../o6.txt
+ EOF
+ mkdir sub &&
+ cd sub &&
+ git ls-files --format="%(path)" ":/" >../actual &&
+ cd .. &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git ls-files --format with -m' '
+ echo change >o1.txt &&
+ cat >expect <<-\EOF &&
+ o1.txt
+ o4.txt
+ o5.txt
+ o6.txt
+ EOF
+ git ls-files --format="%(path)" -m >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git ls-files --format with -d' '
+ echo o7 >o7.txt &&
+ git add o7.txt &&
+ rm o7.txt &&
+ cat >expect <<-\EOF &&
+ o4.txt
+ o5.txt
+ o6.txt
+ o7.txt
+ EOF
+ git ls-files --format="%(path)" -d >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git ls-files --format v.s -s' '
+ git ls-files --stage >expect &&
+ git ls-files --format="%(objectmode) %(objectname) %(stage)%x09%(path)" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git ls-files --format with --debug' '
+ git ls-files --debug >expect &&
+ git ls-files --format="%(path)" --debug >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t3020-ls-files-error-unmatch.sh b/t/t3020-ls-files-error-unmatch.sh
index 124e73b..133593d 100755
--- a/t/t3020-ls-files-error-unmatch.sh
+++ b/t/t3020-ls-files-error-unmatch.sh
@@ -9,6 +9,8 @@ This test runs git ls-files --error-unmatch to ensure it correctly
returns an error when a non-existent path is provided on the command
line.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -17,12 +19,12 @@ test_expect_success 'setup' '
git commit -m "add foo bar"
'
-test_expect_success \
- 'git ls-files --error-unmatch should fail with unmatched path.' \
- 'test_must_fail git ls-files --error-unmatch foo bar-does-not-match'
+test_expect_success 'git ls-files --error-unmatch should fail with unmatched path.' '
+ test_must_fail git ls-files --error-unmatch foo bar-does-not-match
+'
-test_expect_success \
- 'git ls-files --error-unmatch should succeed with matched paths.' \
- 'git ls-files --error-unmatch foo bar'
+test_expect_success 'git ls-files --error-unmatch should succeed with matched paths.' '
+ git ls-files --error-unmatch foo bar
+'
test_done
diff --git a/t/t3040-subprojects-basic.sh b/t/t3040-subprojects-basic.sh
index 6abdcbb..bd65dfc 100755
--- a/t/t3040-subprojects-basic.sh
+++ b/t/t3040-subprojects-basic.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='Basic subproject functionality'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup: create superproject' '
diff --git a/t/t3050-subprojects-fetch.sh b/t/t3050-subprojects-fetch.sh
index f1f09ab..3884694 100755
--- a/t/t3050-subprojects-fetch.sh
+++ b/t/t3050-subprojects-fetch.sh
@@ -2,6 +2,7 @@
test_description='fetching and pushing project with subproject'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t3060-ls-files-with-tree.sh b/t/t3060-ls-files-with-tree.sh
index b257c79..5a06732 100755
--- a/t/t3060-ls-files-with-tree.sh
+++ b/t/t3060-ls-files-with-tree.sh
@@ -8,9 +8,11 @@ test_description='git ls-files test (--with-tree).
This test runs git ls-files --with-tree and in particular in
a scenario known to trigger a crash with some versions of git.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
-test_expect_success setup '
+test_expect_success 'setup' '
# The bug we are exercising requires a fair number of entries
# in a sub-directory so that add_index_entry will trigger a
@@ -38,7 +40,7 @@ test_expect_success setup '
git commit -a -m "remove them all" &&
# The bug also requires some entry before our directory so that
- # prune_path will modify the_index.cache
+ # prune_index will modify the_repository->index.cache
mkdir a_directory_that_sorts_before_sub &&
>a_directory_that_sorts_before_sub/file &&
@@ -54,7 +56,7 @@ test_expect_success 'usage' '
'
test_expect_success 'git ls-files --with-tree should succeed from subdir' '
- # We have to run from a sub-directory to trigger prune_path
+ # We have to run from a sub-directory to trigger prune_index
# Then we finally get to run our --with-tree test
(
cd sub &&
@@ -62,9 +64,9 @@ test_expect_success 'git ls-files --with-tree should succeed from subdir' '
)
'
-test_expect_success \
- 'git ls-files --with-tree should add entries from named tree.' \
- 'test_cmp expected output'
+test_expect_success 'git ls-files --with-tree should add entries from named tree.' '
+ test_cmp expected output
+'
test_expect_success 'no duplicates in --with-tree output' '
git ls-files --with-tree=HEAD >actual &&
diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh
index 56ea4bd..4dd42df 100755
--- a/t/t3070-wildmatch.sh
+++ b/t/t3070-wildmatch.sh
@@ -2,13 +2,9 @@
test_description='wildmatch tests'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
-# Disable expensive chain-lint tests; all of the tests in this script
-# are variants of a few trivial test-tool invocations, and there are a lot of
-# them.
-GIT_TEST_CHAIN_LINT_HARDER_DEFAULT=0
-
should_create_test_file() {
file=$1
@@ -192,7 +188,7 @@ match() {
file=$(cat .git/expected_test_file) &&
if should_create_test_file "$file"
then
- dirs=${file%/*}
+ dirs=${file%/*} &&
if test "$file" != "$dirs"
then
mkdir -p -- "$dirs" &&
@@ -435,4 +431,15 @@ match 1 1 1 1 'a' '[B-a]'
match 0 1 0 1 'z' '[Z-y]'
match 1 1 1 1 'Z' '[Z-y]'
+test_expect_success 'matching does not exhibit exponential behavior' '
+ {
+ test-tool wildmatch wildmatch \
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab \
+ "*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a" &
+ pid=$!
+ } &&
+ sleep 2 &&
+ ! kill $!
+'
+
test_done
diff --git a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh
index 18baf49..436de44 100755
--- a/t/t3100-ls-tree-restrict.sh
+++ b/t/t3100-ls-tree-restrict.sh
@@ -16,6 +16,8 @@ This test runs git ls-tree with the following in a tree.
The new path restriction code should do the right thing for path2 and
path2/baz. Also path0/ should snow nothing.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success \
diff --git a/t/t3101-ls-tree-dirname.sh b/t/t3101-ls-tree-dirname.sh
index 12bf310..5af2dac 100755
--- a/t/t3101-ls-tree-dirname.sh
+++ b/t/t3101-ls-tree-dirname.sh
@@ -19,6 +19,8 @@ This test runs git ls-tree with the following in a tree.
Test the handling of multiple directories which have matching file
entries. Also test odd filename and missing entries handling.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -152,6 +154,14 @@ EOF
test_output
'
+test_expect_success 'ls-tree --no-full-name' '
+ git -C path0 ls-tree --no-full-name $tree a >current &&
+ cat >expected <<-EOF &&
+ 040000 tree X a
+ EOF
+ test_output
+'
+
test_expect_success 'ls-tree --full-tree' '
(
cd path1/b/c &&
@@ -199,31 +209,34 @@ EOF
test_cmp expected check
'
-test_expect_success 'ls-tree --name-only' '
- git ls-tree --name-only $tree >current &&
- cat >expected <<\EOF &&
-1.txt
-2.txt
-path0
-path1
-path2
-path3
-EOF
- test_output
-'
-
-test_expect_success 'ls-tree --name-only -r' '
- git ls-tree --name-only -r $tree >current &&
- cat >expected <<\EOF &&
-1.txt
-2.txt
-path0/a/b/c/1.txt
-path1/b/c/1.txt
-path2/1.txt
-path3/1.txt
-path3/2.txt
-EOF
- test_output
-'
+for opt in --name-only --name-status
+do
+ test_expect_success "ls-tree $opt" '
+ git ls-tree $opt $tree >current &&
+ cat >expected <<-\EOF &&
+ 1.txt
+ 2.txt
+ path0
+ path1
+ path2
+ path3
+ EOF
+ test_output
+ '
+
+ test_expect_success "ls-tree $opt -r" '
+ git ls-tree $opt -r $tree >current &&
+ cat >expected <<-\EOF &&
+ 1.txt
+ 2.txt
+ path0/a/b/c/1.txt
+ path1/b/c/1.txt
+ path2/1.txt
+ path3/1.txt
+ path3/2.txt
+ EOF
+ test_output
+ '
+done
test_done
diff --git a/t/t3102-ls-tree-wildcards.sh b/t/t3102-ls-tree-wildcards.sh
index 1e16c6b..3942db2 100755
--- a/t/t3102-ls-tree-wildcards.sh
+++ b/t/t3102-ls-tree-wildcards.sh
@@ -2,6 +2,7 @@
test_description='ls-tree with(out) globs'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t3103-ls-tree-misc.sh b/t/t3103-ls-tree-misc.sh
index 1452091..81c6343 100755
--- a/t/t3103-ls-tree-misc.sh
+++ b/t/t3103-ls-tree-misc.sh
@@ -7,6 +7,7 @@ Miscellaneous tests for git ls-tree.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -22,4 +23,19 @@ test_expect_success 'ls-tree fails with non-zero exit code on broken tree' '
test_must_fail git ls-tree -r HEAD
'
+for opts in \
+ "--long --name-only" \
+ "--name-only --name-status" \
+ "--name-status --object-only" \
+ "--object-only --long"
+do
+ test_expect_success "usage: incompatible options: $opts" '
+ test_expect_code 129 git ls-tree $opts $tree
+ '
+
+ one_opt=$(echo "$opts" | cut -d' ' -f1)
+ test_expect_success "usage: incompatible options: $one_opt and --format" '
+ test_expect_code 129 git ls-tree $one_opt --format=fmt $tree
+ '
+done
test_done
diff --git a/t/t3104-ls-tree-format.sh b/t/t3104-ls-tree-format.sh
new file mode 100755
index 0000000..3adb206
--- /dev/null
+++ b/t/t3104-ls-tree-format.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+
+test_description='ls-tree --format'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-t3100.sh
+
+test_expect_success 'ls-tree --format usage' '
+ test_expect_code 129 git ls-tree --format=fmt -l HEAD &&
+ test_expect_code 129 git ls-tree --format=fmt --name-only HEAD &&
+ test_expect_code 129 git ls-tree --format=fmt --name-status HEAD
+'
+
+test_expect_success 'setup' '
+ setup_basic_ls_tree_data
+'
+
+test_ls_tree_format () {
+ format=$1 &&
+ opts=$2 &&
+ fmtopts=$3 &&
+
+ test_expect_success "ls-tree '--format=<$format>' is like options '$opts $fmtopts'" '
+ git ls-tree $opts -r HEAD >expect &&
+ git ls-tree --format="$format" -r $fmtopts HEAD >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "ls-tree '--format=<$format>' on optimized v.s. non-optimized path" '
+ git ls-tree --format="$format" -r $fmtopts HEAD >expect &&
+ git ls-tree --format="> $format" -r $fmtopts HEAD >actual.raw &&
+ sed "s/^> //" >actual <actual.raw &&
+ test_cmp expect actual
+ '
+}
+
+test_expect_success "ls-tree --format='%(path) %(path) %(path)' HEAD top-file" '
+ git ls-tree --format="%(path) %(path) %(path)" HEAD top-file.t >actual &&
+ echo top-file.t top-file.t top-file.t >expect &&
+ test_cmp expect actual
+'
+
+test_ls_tree_format \
+ "%(objectmode) %(objecttype) %(objectname)%x09%(path)" \
+ ""
+
+test_ls_tree_format \
+ "%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)" \
+ "--long"
+
+test_ls_tree_format \
+ "%(path)" \
+ "--name-only"
+
+test_ls_tree_format \
+ "%(objectname)" \
+ "--object-only"
+
+test_ls_tree_format \
+ "%(objectname)" \
+ "--object-only --abbrev" \
+ "--abbrev"
+
+test_ls_tree_format \
+ "%(objectmode) %(objecttype) %(objectname)%x09%(path)" \
+ "-t" \
+ "-t"
+
+test_ls_tree_format \
+ "%(objectmode) %(objecttype) %(objectname)%x09%(path)" \
+ "--full-name" \
+ "--full-name"
+
+test_ls_tree_format \
+ "%(objectmode) %(objecttype) %(objectname)%x09%(path)" \
+ "--full-tree" \
+ "--full-tree"
+
+test_done
diff --git a/t/t3105-ls-tree-output.sh b/t/t3105-ls-tree-output.sh
new file mode 100755
index 0000000..ce2391e
--- /dev/null
+++ b/t/t3105-ls-tree-output.sh
@@ -0,0 +1,192 @@
+#!/bin/sh
+
+test_description='ls-tree output'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-t3100.sh
+
+test_expect_success 'ls-tree --format usage' '
+ test_expect_code 129 git ls-tree --format=fmt -l HEAD &&
+ test_expect_code 129 git ls-tree --format=fmt --name-only HEAD &&
+ test_expect_code 129 git ls-tree --format=fmt --name-status HEAD
+'
+
+test_expect_success 'setup' '
+ setup_basic_ls_tree_data
+'
+
+test_ls_tree_format_mode_output () {
+ local opts="$1" &&
+ shift &&
+ cat >expect &&
+
+ while test $# -gt 0
+ do
+ local mode="$1" &&
+ shift &&
+
+ test_expect_success "'ls-tree $opts${mode:+ $mode}' output" '
+ git ls-tree ${mode:+$mode }$opts HEAD >actual &&
+ test_cmp expect actual
+ '
+
+ case "$opts" in
+ --full-tree)
+ test_expect_success "'ls-tree $opts${mode:+ $mode}' output (via subdir, fails)" '
+ test_must_fail git -C dir ls-tree --full-name ${mode:+$mode }$opts HEAD -- ../
+ '
+ ;;
+ *)
+ test_expect_success "'ls-tree $opts${mode:+ $mode}' output (via subdir)" '
+ git -C dir ls-tree --full-name ${mode:+$mode }$opts HEAD -- ../ >actual &&
+ test_cmp expect actual
+ '
+ ;;
+ esac
+ done
+}
+
+# test exact output of option (none, --long, ...) and mode (none and
+# -d, -r -t) and combinations
+test_expect_success 'setup: HEAD_* variables' '
+ HEAD_gitmodules=$(git rev-parse HEAD:.gitmodules) &&
+ HEAD_dir=$(git rev-parse HEAD:dir) &&
+ HEAD_top_file=$(git rev-parse HEAD:top-file.t) &&
+ HEAD_submodule=$(git rev-parse HEAD:submodule) &&
+ HEAD_dir_sub_file=$(git rev-parse HEAD:dir/sub-file.t)
+'
+## opt =
+test_ls_tree_format_mode_output "" "" "-t" <<-EOF
+ 100644 blob $HEAD_gitmodules .gitmodules
+ 040000 tree $HEAD_dir dir
+ 160000 commit $HEAD_submodule submodule
+ 100644 blob $HEAD_top_file top-file.t
+ EOF
+test_ls_tree_format_mode_output "" "-d" <<-EOF
+ 040000 tree $HEAD_dir dir
+ 160000 commit $HEAD_submodule submodule
+ EOF
+test_ls_tree_format_mode_output "" "-r" <<-EOF
+ 100644 blob $HEAD_gitmodules .gitmodules
+ 100644 blob $HEAD_dir_sub_file dir/sub-file.t
+ 160000 commit $HEAD_submodule submodule
+ 100644 blob $HEAD_top_file top-file.t
+ EOF
+## opt = --long
+test_ls_tree_format_mode_output "--long" "" "-t" <<-EOF
+ 100644 blob $HEAD_gitmodules 61 .gitmodules
+ 040000 tree $HEAD_dir - dir
+ 160000 commit $HEAD_submodule - submodule
+ 100644 blob $HEAD_top_file 9 top-file.t
+ EOF
+test_ls_tree_format_mode_output "--long" "-d" <<-EOF
+ 040000 tree $HEAD_dir - dir
+ 160000 commit $HEAD_submodule - submodule
+ EOF
+test_ls_tree_format_mode_output "--long" "-r" <<-EOF
+ 100644 blob $HEAD_gitmodules 61 .gitmodules
+ 100644 blob $HEAD_dir_sub_file 13 dir/sub-file.t
+ 160000 commit $HEAD_submodule - submodule
+ 100644 blob $HEAD_top_file 9 top-file.t
+ EOF
+## opt = --name-only
+test_ls_tree_format_mode_output "--name-only" "" "-t" <<-EOF
+ .gitmodules
+ dir
+ submodule
+ top-file.t
+ EOF
+test_ls_tree_format_mode_output "--name-only" "-d" <<-EOF
+ dir
+ submodule
+ EOF
+test_ls_tree_format_mode_output "--name-only" "-r" <<-EOF
+ .gitmodules
+ dir/sub-file.t
+ submodule
+ top-file.t
+ EOF
+## opt = --object-only
+test_ls_tree_format_mode_output "--object-only" "" "-t" <<-EOF
+ $HEAD_gitmodules
+ $HEAD_dir
+ $HEAD_submodule
+ $HEAD_top_file
+ EOF
+test_ls_tree_format_mode_output "--object-only" "-d" <<-EOF
+ $HEAD_dir
+ $HEAD_submodule
+ EOF
+test_ls_tree_format_mode_output "--object-only" "-r" <<-EOF
+ $HEAD_gitmodules
+ $HEAD_dir_sub_file
+ $HEAD_submodule
+ $HEAD_top_file
+ EOF
+## opt = --object-only --abbrev
+test_expect_success 'setup: HEAD_short_* variables' '
+ HEAD_short_gitmodules=$(git rev-parse --short HEAD:.gitmodules) &&
+ HEAD_short_dir=$(git rev-parse --short HEAD:dir) &&
+ HEAD_short_top_file=$(git rev-parse --short HEAD:top-file.t) &&
+ HEAD_short_submodule=$(git rev-parse --short HEAD:submodule) &&
+ HEAD_short_dir_sub_file=$(git rev-parse --short HEAD:dir/sub-file.t)
+'
+test_ls_tree_format_mode_output "--object-only --abbrev" "" "-t" <<-EOF
+ $HEAD_short_gitmodules
+ $HEAD_short_dir
+ $HEAD_short_submodule
+ $HEAD_short_top_file
+ EOF
+test_ls_tree_format_mode_output "--object-only --abbrev" "-d" <<-EOF
+ $HEAD_short_dir
+ $HEAD_short_submodule
+ EOF
+test_ls_tree_format_mode_output "--object-only --abbrev" "-r" <<-EOF
+ $HEAD_short_gitmodules
+ $HEAD_short_dir_sub_file
+ $HEAD_short_submodule
+ $HEAD_short_top_file
+ EOF
+## opt = --full-name
+test_ls_tree_format_mode_output "--full-name" "" <<-EOF
+ 100644 blob $HEAD_gitmodules .gitmodules
+ 040000 tree $HEAD_dir dir
+ 160000 commit $HEAD_submodule submodule
+ 100644 blob $HEAD_top_file top-file.t
+ EOF
+test_ls_tree_format_mode_output "--full-name" "-d" <<-EOF
+ 040000 tree $HEAD_dir dir
+ 160000 commit $HEAD_submodule submodule
+ EOF
+test_ls_tree_format_mode_output "--full-name" "-r" <<-EOF
+ 100644 blob $HEAD_gitmodules .gitmodules
+ 100644 blob $HEAD_dir_sub_file dir/sub-file.t
+ 160000 commit $HEAD_submodule submodule
+ 100644 blob $HEAD_top_file top-file.t
+ EOF
+test_ls_tree_format_mode_output "--full-name" "-t" <<-EOF
+ 100644 blob $HEAD_gitmodules .gitmodules
+ 040000 tree $HEAD_dir dir
+ 160000 commit $HEAD_submodule submodule
+ 100644 blob $HEAD_top_file top-file.t
+ EOF
+## opt = --full-tree
+test_ls_tree_format_mode_output "--full-tree" "" "-t" <<-EOF
+ 100644 blob $HEAD_gitmodules .gitmodules
+ 040000 tree $HEAD_dir dir
+ 160000 commit $HEAD_submodule submodule
+ 100644 blob $HEAD_top_file top-file.t
+ EOF
+test_ls_tree_format_mode_output "--full-tree" "-d" <<-EOF
+ 040000 tree $HEAD_dir dir
+ 160000 commit $HEAD_submodule submodule
+ EOF
+test_ls_tree_format_mode_output "--full-tree" "-r" <<-EOF
+ 100644 blob $HEAD_gitmodules .gitmodules
+ 100644 blob $HEAD_dir_sub_file dir/sub-file.t
+ 160000 commit $HEAD_submodule submodule
+ 100644 blob $HEAD_top_file top-file.t
+ EOF
+
+test_done
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index e575ffb..ccfa6a7 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -8,6 +8,7 @@ test_description='git branch assorted tests'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-rebase.sh
@@ -24,10 +25,10 @@ test_expect_success 'prepare a trivial repository' '
test_expect_success 'git branch --help should not have created a bogus branch' '
test_might_fail git branch --man --help </dev/null >/dev/null 2>&1 &&
- test_path_is_missing .git/refs/heads/--help
+ test_ref_missing refs/heads/--help
'
-test_expect_success 'branch -h in broken repository' '
+test_expect_success REFFILES 'branch -h in broken repository' '
mkdir broken &&
(
cd broken &&
@@ -35,39 +36,59 @@ test_expect_success 'branch -h in broken repository' '
>.git/refs/heads/main &&
test_expect_code 129 git branch -h >usage 2>&1
) &&
- test_i18ngrep "[Uu]sage" broken/usage
+ test_grep "[Uu]sage" broken/usage
'
test_expect_success 'git branch abc should create a branch' '
- git branch abc && test_path_is_file .git/refs/heads/abc
+ git branch abc &&
+ test_ref_exists refs/heads/abc
+'
+
+test_expect_success 'git branch abc should fail when abc exists' '
+ test_must_fail git branch abc
+'
+
+test_expect_success 'git branch --force abc should fail when abc is checked out' '
+ test_when_finished git switch main &&
+ git switch abc &&
+ test_must_fail git branch --force abc HEAD~1
+'
+
+test_expect_success 'git branch --force abc should succeed when abc exists' '
+ git rev-parse HEAD~1 >expect &&
+ git branch --force abc HEAD~1 &&
+ git rev-parse abc >actual &&
+ test_cmp expect actual
'
test_expect_success 'git branch a/b/c should create a branch' '
- git branch a/b/c && test_path_is_file .git/refs/heads/a/b/c
+ git branch a/b/c &&
+ test_ref_exists refs/heads/a/b/c
'
test_expect_success 'git branch mb main... should create a branch' '
- git branch mb main... && test_path_is_file .git/refs/heads/mb
+ git branch mb main... &&
+ test_ref_exists refs/heads/mb
'
test_expect_success 'git branch HEAD should fail' '
test_must_fail git branch HEAD
'
-cat >expect <<EOF
-$ZERO_OID $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from main
-EOF
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 -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
+ test_ref_exists refs/heads/d/e/f &&
+ cat >expect <<-EOF &&
+ $HEAD refs/heads/d/e/f@{0}: branch: Created from main
+ EOF
+ git reflog show --no-abbrev-commit refs/heads/d/e/f >actual &&
+ test_cmp expect actual
'
test_expect_success 'git branch -d d/e/f should delete a branch and a log' '
git branch -d d/e/f &&
- test_path_is_missing .git/refs/heads/d/e/f &&
+ test_ref_missing refs/heads/d/e/f &&
test_must_fail git reflog exists refs/heads/d/e/f
'
@@ -85,7 +106,7 @@ test_expect_success 'git branch l should work after branch l/m has been deleted'
test_expect_success 'git branch -m dumps usage' '
test_expect_code 128 git branch -m 2>err &&
- test_i18ngrep "branch name required" err
+ test_grep "branch name required" err
'
test_expect_success 'git branch -m m broken_symref should work' '
@@ -168,6 +189,13 @@ test_expect_success 'git branch -M foo bar should fail when bar is checked out'
test_must_fail git branch -M bar foo
'
+test_expect_success 'git branch -M foo bar should fail when bar is checked out in worktree' '
+ git branch -f bar &&
+ test_when_finished "git worktree remove wt && git branch -D wt" &&
+ git worktree add wt &&
+ test_must_fail git branch -M bar wt
+'
+
test_expect_success 'git branch -M baz bam should succeed when baz is checked out' '
git checkout -b baz &&
git branch bam &&
@@ -175,10 +203,9 @@ test_expect_success 'git branch -M baz bam should succeed when baz is checked ou
test $(git rev-parse --abbrev-ref HEAD) = bam
'
-test_expect_success 'git branch -M baz bam should add entries to .git/logs/HEAD' '
- msg="Branch: renamed refs/heads/baz to refs/heads/bam" &&
- grep " 0\{40\}.*$msg$" .git/logs/HEAD &&
- grep "^0\{40\}.*$msg$" .git/logs/HEAD
+test_expect_success 'git branch -M baz bam should add entries to HEAD reflog' '
+ git reflog show HEAD >actual &&
+ grep "HEAD@{0}: Branch: renamed refs/heads/baz to refs/heads/bam" actual
'
test_expect_success 'git branch -M should leave orphaned HEAD alone' '
@@ -187,17 +214,20 @@ test_expect_success 'git branch -M should leave orphaned HEAD alone' '
cd orphan &&
test_commit initial &&
git checkout --orphan lonely &&
- grep lonely .git/HEAD &&
- test_path_is_missing .git/refs/head/lonely &&
+ git symbolic-ref HEAD >expect &&
+ echo refs/heads/lonely >actual &&
+ test_cmp expect actual &&
+ test_ref_missing refs/head/lonely &&
git branch -M main mistress &&
- grep lonely .git/HEAD
+ git symbolic-ref HEAD >expect &&
+ test_cmp expect actual
)
'
test_expect_success 'resulting reflog can be shown by log -g' '
oid=$(git rev-parse HEAD) &&
cat >expect <<-EOF &&
- HEAD@{0} $oid $msg
+ HEAD@{0} $oid Branch: renamed refs/heads/baz to refs/heads/bam
HEAD@{2} $oid checkout: moving from foo to baz
EOF
git log -g --format="%gd %H %gs" -2 HEAD >actual &&
@@ -215,15 +245,34 @@ test_expect_success 'git branch -M baz bam should succeed when baz is checked ou
git worktree prune
'
+test_expect_success REFFILES 'git branch -M fails if updating any linked working tree fails' '
+ git worktree add -b baz bazdir1 &&
+ git worktree add -f bazdir2 baz &&
+ touch .git/worktrees/bazdir1/HEAD.lock &&
+ test_must_fail git branch -M baz bam &&
+ test $(git -C bazdir2 rev-parse --abbrev-ref HEAD) = bam &&
+ git branch -M bam baz &&
+ rm .git/worktrees/bazdir1/HEAD.lock &&
+ touch .git/worktrees/bazdir2/HEAD.lock &&
+ test_must_fail git branch -M baz bam &&
+ test $(git -C bazdir1 rev-parse --abbrev-ref HEAD) = bam &&
+ rm -rf bazdir1 bazdir2 &&
+ git worktree prune
+'
+
test_expect_success 'git branch -M baz bam should succeed within a worktree in which baz is checked out' '
git checkout -b baz &&
git worktree add -f bazdir baz &&
(
cd bazdir &&
git branch -M baz bam &&
- test $(git rev-parse --abbrev-ref HEAD) = bam
+ echo bam >expect &&
+ git rev-parse --abbrev-ref HEAD >actual &&
+ test_cmp expect actual
) &&
- test $(git rev-parse --abbrev-ref HEAD) = bam &&
+ echo bam >expect &&
+ git rev-parse --abbrev-ref HEAD >actual &&
+ test_cmp expect actual &&
rm -r bazdir &&
git worktree prune
'
@@ -244,6 +293,67 @@ test_expect_success 'git branch -M topic topic should work when main is checked
git branch -M topic topic
'
+test_expect_success 'git branch -M and -C fail on detached HEAD' '
+ git checkout HEAD^{} &&
+ test_when_finished git checkout - &&
+ echo "fatal: cannot rename the current branch while not on any" >expect &&
+ test_must_fail git branch -M must-fail 2>err &&
+ test_cmp expect err &&
+ echo "fatal: cannot copy the current branch while not on any" >expect &&
+ test_must_fail git branch -C must-fail 2>err &&
+ test_cmp expect err
+'
+
+test_expect_success 'git branch -m should work with orphan branches' '
+ test_when_finished git checkout - &&
+ test_when_finished git worktree remove -f wt &&
+ git worktree add wt --detach &&
+ # rename orphan in another worktreee
+ git -C wt checkout --orphan orphan-foo-wt &&
+ git branch -m orphan-foo-wt orphan-bar-wt &&
+ test orphan-bar-wt=$(git -C orphan-worktree branch --show-current) &&
+ # rename orphan in the current worktree
+ git checkout --orphan orphan-foo &&
+ git branch -m orphan-foo orphan-bar &&
+ test orphan-bar=$(git branch --show-current)
+'
+
+test_expect_success 'git branch -d on orphan HEAD (merged)' '
+ test_when_finished git checkout main &&
+ git checkout --orphan orphan &&
+ test_when_finished "rm -rf .git/objects/commit-graph*" &&
+ git commit-graph write --reachable &&
+ git branch --track to-delete main &&
+ git branch -d to-delete
+'
+
+test_expect_success 'git branch -d on orphan HEAD (merged, graph)' '
+ test_when_finished git checkout main &&
+ git checkout --orphan orphan &&
+ git branch --track to-delete main &&
+ git branch -d to-delete
+'
+
+test_expect_success 'git branch -d on orphan HEAD (unmerged)' '
+ test_when_finished git checkout main &&
+ git checkout --orphan orphan &&
+ test_when_finished "git branch -D to-delete" &&
+ git branch to-delete main &&
+ test_must_fail git branch -d to-delete 2>err &&
+ grep "not fully merged" err
+'
+
+test_expect_success 'git branch -d on orphan HEAD (unmerged, graph)' '
+ test_when_finished git checkout main &&
+ git checkout --orphan orphan &&
+ test_when_finished "git branch -D to-delete" &&
+ git branch to-delete main &&
+ test_when_finished "rm -rf .git/objects/commit-graph*" &&
+ git commit-graph write --reachable &&
+ test_must_fail git branch -d to-delete 2>err &&
+ grep "not fully merged" err
+'
+
test_expect_success 'git branch -v -d t should work' '
git branch t &&
git rev-parse --verify refs/heads/t &&
@@ -282,6 +392,7 @@ test_expect_success 'deleting checked-out branch from repo that is a submodule'
git init repo1 &&
git init repo1/sub &&
test_commit -C repo1/sub x &&
+ test_config_global protocol.file.allow always &&
git -C repo1 submodule add ./sub &&
git -C repo1 commit -m "adding sub" &&
@@ -329,10 +440,10 @@ test_expect_success 'git branch --list -v with --abbrev' '
test_expect_success 'git branch --column' '
COLUMNS=81 git branch --column=column >actual &&
- cat >expect <<\EOF &&
- a/b/c bam foo l * main n o/p r
- abc bar j/k m/m mb o/o q topic
-EOF
+ cat >expect <<-\EOF &&
+ a/b/c bam foo l * main n o/p r
+ abc bar j/k m/m mb o/o q topic
+ EOF
test_cmp expect actual
'
@@ -342,25 +453,25 @@ test_expect_success 'git branch --column with an extremely long branch name' '
test_when_finished "git branch -d $long" &&
git branch $long &&
COLUMNS=80 git branch --column=column >actual &&
- cat >expect <<EOF &&
- a/b/c
- abc
- bam
- bar
- foo
- j/k
- l
- m/m
-* main
- mb
- n
- o/o
- o/p
- q
- r
- topic
- $long
-EOF
+ cat >expect <<-EOF &&
+ a/b/c
+ abc
+ bam
+ bar
+ foo
+ j/k
+ l
+ m/m
+ * main
+ mb
+ n
+ o/o
+ o/p
+ q
+ r
+ topic
+ $long
+ EOF
test_cmp expect actual
'
@@ -370,10 +481,10 @@ test_expect_success 'git branch with column.*' '
COLUMNS=80 git branch >actual &&
git config --unset column.branch &&
git config --unset column.ui &&
- cat >expect <<\EOF &&
- a/b/c bam foo l * main n o/p r
- abc bar j/k m/m mb o/o q topic
-EOF
+ cat >expect <<-\EOF &&
+ a/b/c bam foo l * main n o/p r
+ abc bar j/k m/m mb o/o q topic
+ EOF
test_cmp expect actual
'
@@ -385,39 +496,36 @@ test_expect_success 'git branch -v with column.ui ignored' '
git config column.ui column &&
COLUMNS=80 git branch -v | cut -c -8 | sed "s/ *$//" >actual &&
git config --unset column.ui &&
- cat >expect <<\EOF &&
- a/b/c
- abc
- bam
- bar
- foo
- j/k
- l
- m/m
-* main
- mb
- n
- o/o
- o/p
- q
- r
- topic
-EOF
+ cat >expect <<-\EOF &&
+ a/b/c
+ abc
+ bam
+ bar
+ foo
+ j/k
+ l
+ m/m
+ * main
+ mb
+ n
+ o/o
+ o/p
+ q
+ r
+ topic
+ EOF
test_cmp expect actual
'
-mv .git/config .git/config-saved
-
-test_expect_success SHA1 'git branch -m q q2 without config should succeed' '
+test_expect_success DEFAULT_REPO_FORMAT 'git branch -m q q2 without config should succeed' '
+ test_when_finished mv .git/config-saved .git/config &&
+ mv .git/config .git/config-saved &&
git branch -m q q2 &&
git branch -m q2 q
'
-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 config branch.s/s.dummy Hello &&
git branch --create-reflog s/s &&
git reflog exists refs/heads/s/s &&
git branch --create-reflog s/t &&
@@ -468,19 +576,19 @@ EOF
# ...and that the comments for those sections are also
# preserved.
- cat config.branch | sed "s/\"source\"/\"dest\"/" >expect &&
+ sed "s/\"source\"/\"dest\"/" config.branch >expect &&
sed -n -e "/Note the lack/,\$p" .git/config >actual &&
test_cmp expect actual
'
test_expect_success 'git branch -c dumps usage' '
test_expect_code 128 git branch -c 2>err &&
- test_i18ngrep "branch name required" err
+ test_grep "branch name required" err
'
test_expect_success 'git branch --copy dumps usage' '
test_expect_code 128 git branch --copy 2>err &&
- test_i18ngrep "branch name required" err
+ test_grep "branch name required" err
'
test_expect_success 'git branch -c d e should work' '
@@ -590,7 +698,8 @@ test_expect_success 'git branch -C c1 c2 should succeed when c1 is checked out'
test_expect_success 'git branch -C c1 c2 should never touch HEAD' '
msg="Branch: copied refs/heads/c1 to refs/heads/c2" &&
- ! grep "$msg$" .git/logs/HEAD
+ git reflog HEAD >actual &&
+ ! grep "$msg$" actual
'
test_expect_success 'git branch -C main should work when main is checked out' '
@@ -693,26 +802,26 @@ test_expect_success 'deleting a symref' '
git symbolic-ref refs/heads/symref refs/heads/target &&
echo "Deleted branch symref (was refs/heads/target)." >expect &&
git branch -d symref >actual &&
- test_path_is_file .git/refs/heads/target &&
- test_path_is_missing .git/refs/heads/symref &&
+ test_ref_exists refs/heads/target &&
+ test_ref_missing refs/heads/symref &&
test_cmp expect actual
'
test_expect_success 'deleting a dangling symref' '
git symbolic-ref refs/heads/dangling-symref nowhere &&
- test_path_is_file .git/refs/heads/dangling-symref &&
+ git symbolic-ref --no-recurse refs/heads/dangling-symref &&
echo "Deleted branch dangling-symref (was nowhere)." >expect &&
git branch -d dangling-symref >actual &&
- test_path_is_missing .git/refs/heads/dangling-symref &&
+ test_ref_missing refs/heads/dangling-symref &&
test_cmp expect actual
'
test_expect_success 'deleting a self-referential symref' '
git symbolic-ref refs/heads/self-reference refs/heads/self-reference &&
- test_path_is_file .git/refs/heads/self-reference &&
+ test_ref_exists refs/heads/self-reference &&
echo "Deleted branch self-reference (was refs/heads/self-reference)." >expect &&
git branch -d self-reference >actual &&
- test_path_is_missing .git/refs/heads/self-reference &&
+ test_ref_missing refs/heads/self-reference &&
test_cmp expect actual
'
@@ -720,15 +829,8 @@ test_expect_success 'renaming a symref is not allowed' '
git symbolic-ref refs/heads/topic refs/heads/main &&
test_must_fail git branch -m topic new-topic &&
git symbolic-ref refs/heads/topic &&
- test_path_is_file .git/refs/heads/main &&
- test_path_is_missing .git/refs/heads/new-topic
-'
-
-test_expect_success SYMLINKS 'git branch -m u v should fail when the reflog for u is a symlink' '
- 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
+ test_ref_exists refs/heads/main &&
+ test_ref_missing refs/heads/new-topic
'
test_expect_success 'test tracking setup via --track' '
@@ -814,7 +916,19 @@ test_expect_success 'test deleting branch without config' '
test_expect_success 'deleting currently checked out branch fails' '
git worktree add -b my7 my7 &&
test_must_fail git -C my7 branch -d my7 &&
- test_must_fail git branch -d my7 &&
+ test_must_fail git branch -d my7 2>actual &&
+ grep "^error: cannot delete branch .my7. used by worktree at " actual &&
+ rm -r my7 &&
+ git worktree prune
+'
+
+test_expect_success 'deleting in-use branch fails' '
+ git worktree add my7 &&
+ test_commit -C my7 bt7 &&
+ git -C my7 bisect start HEAD HEAD~2 &&
+ test_must_fail git -C my7 branch -d my7 &&
+ test_must_fail git branch -d my7 2>actual &&
+ grep "^error: cannot delete branch .my7. used by worktree at " actual &&
rm -r my7 &&
git worktree prune
'
@@ -840,6 +954,41 @@ test_expect_success 'branch from tag w/--track causes failure' '
test_must_fail git branch --track my11 foobar
'
+test_expect_success 'simple tracking works when remote branch name matches' '
+ test_when_finished "rm -rf otherserver" &&
+ git init otherserver &&
+ test_commit -C otherserver my_commit 1 &&
+ git -C otherserver branch feature &&
+ test_config branch.autosetupmerge simple &&
+ test_config remote.otherserver.url otherserver &&
+ test_config remote.otherserver.fetch refs/heads/*:refs/remotes/otherserver/* &&
+ git fetch otherserver &&
+ git branch feature otherserver/feature &&
+ test_cmp_config otherserver branch.feature.remote &&
+ test_cmp_config refs/heads/feature branch.feature.merge
+'
+
+test_expect_success 'simple tracking skips when remote branch name does not match' '
+ test_config branch.autosetupmerge simple &&
+ test_config remote.local.url . &&
+ test_config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ git fetch local &&
+ git branch my-other local/main &&
+ test_cmp_config "" --default "" branch.my-other.remote &&
+ test_cmp_config "" --default "" branch.my-other.merge
+'
+
+test_expect_success 'simple tracking skips when remote ref is not a branch' '
+ test_config branch.autosetupmerge simple &&
+ test_config remote.localtags.url . &&
+ test_config remote.localtags.fetch refs/tags/*:refs/remotes/localtags/* &&
+ git tag mytag12 main &&
+ git fetch localtags &&
+ git branch mytag12 localtags/mytag12 &&
+ test_cmp_config "" --default "" branch.mytag12.remote &&
+ test_cmp_config "" --default "" branch.mytag12.merge
+'
+
test_expect_success '--set-upstream-to fails on multiple branches' '
echo "fatal: too many arguments to set new upstream" >expect &&
test_must_fail git branch --set-upstream-to main a b c 2>err &&
@@ -849,7 +998,7 @@ test_expect_success '--set-upstream-to fails on multiple branches' '
test_expect_success '--set-upstream-to fails on detached HEAD' '
git checkout HEAD^{} &&
test_when_finished git checkout - &&
- echo "fatal: could not set upstream of HEAD to main when it does not point to any branch." >expect &&
+ echo "fatal: could not set upstream of HEAD to main when it does not point to any branch" >expect &&
test_must_fail git branch --set-upstream-to main 2>err &&
test_cmp expect err
'
@@ -862,11 +1011,11 @@ test_expect_success '--set-upstream-to fails on a missing dst branch' '
test_expect_success '--set-upstream-to fails on a missing src branch' '
test_must_fail git branch --set-upstream-to does-not-exist main 2>err &&
- test_i18ngrep "the requested upstream branch '"'"'does-not-exist'"'"' does not exist" err
+ test_grep "the requested upstream branch '"'"'does-not-exist'"'"' does not exist" err
'
test_expect_success '--set-upstream-to fails on a non-ref' '
- echo "fatal: Cannot setup tracking information; starting point '"'"'HEAD^{}'"'"' is not a branch." >expect &&
+ echo "fatal: cannot set up tracking information; starting point '"'"'HEAD^{}'"'"' is not a branch" >expect &&
test_must_fail git branch --set-upstream-to HEAD^{} 2>err &&
test_cmp expect err
'
@@ -876,7 +1025,7 @@ test_expect_success '--set-upstream-to fails on locked config' '
>.git/config.lock &&
git branch locked &&
test_must_fail git branch --set-upstream-to locked 2>err &&
- test_i18ngrep "could not lock config file .git/config" err
+ test_grep "could not lock config file .git/config" err
'
test_expect_success 'use --set-upstream-to modify HEAD' '
@@ -897,7 +1046,7 @@ test_expect_success 'use --set-upstream-to modify a particular branch' '
'
test_expect_success '--unset-upstream should fail if given a non-existent branch' '
- echo "fatal: Branch '"'"'i-dont-exist'"'"' has no upstream information" >expect &&
+ echo "fatal: branch '"'"'i-dont-exist'"'"' has no upstream information" >expect &&
test_must_fail git branch --unset-upstream i-dont-exist 2>err &&
test_cmp expect err
'
@@ -907,7 +1056,7 @@ test_expect_success '--unset-upstream should fail if config is locked' '
git branch --set-upstream-to locked &&
>.git/config.lock &&
test_must_fail git branch --unset-upstream 2>err &&
- test_i18ngrep "could not lock config file .git/config" err
+ test_grep "could not lock config file .git/config" err
'
test_expect_success 'test --unset-upstream on HEAD' '
@@ -919,7 +1068,7 @@ test_expect_success 'test --unset-upstream on HEAD' '
test_must_fail git config branch.main.remote &&
test_must_fail git config branch.main.merge &&
# fail for a branch without upstream set
- echo "fatal: Branch '"'"'main'"'"' has no upstream information" >expect &&
+ echo "fatal: branch '"'"'main'"'"' has no upstream information" >expect &&
test_must_fail git branch --unset-upstream 2>err &&
test_cmp expect err
'
@@ -933,7 +1082,7 @@ test_expect_success '--unset-upstream should fail on multiple branches' '
test_expect_success '--unset-upstream should fail on detached HEAD' '
git checkout HEAD^{} &&
test_when_finished git checkout - &&
- echo "fatal: could not unset upstream of HEAD when it does not point to any branch." >expect &&
+ echo "fatal: could not unset upstream of HEAD when it does not point to any branch" >expect &&
test_must_fail git branch --unset-upstream 2>err &&
test_cmp expect err
'
@@ -950,26 +1099,26 @@ test_expect_success 'disabled option --set-upstream fails' '
test_must_fail git branch --set-upstream origin/main
'
-test_expect_success '--set-upstream-to notices an error to set branch as own upstream' '
+test_expect_success '--set-upstream-to notices an error to set branch as own upstream' "
git branch --set-upstream-to refs/heads/my13 my13 2>actual &&
cat >expect <<-\EOF &&
- warning: Not setting branch my13 as its own upstream.
+ warning: not setting branch 'my13' as its own upstream
EOF
test_expect_code 1 git config branch.my13.remote &&
test_expect_code 1 git config branch.my13.merge &&
test_cmp expect actual
-'
+"
-# Keep this test last, as it changes the current branch
-cat >expect <<EOF
-$ZERO_OID $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from main
-EOF
test_expect_success 'git checkout -b g/h/i -l should create a branch and a log' '
+ test_when_finished git checkout main &&
GIT_COMMITTER_DATE="2005-05-26 23:30" \
git checkout -b g/h/i -l main &&
- test_path_is_file .git/refs/heads/g/h/i &&
- test_path_is_file .git/logs/refs/heads/g/h/i &&
- test_cmp expect .git/logs/refs/heads/g/h/i
+ test_ref_exists refs/heads/g/h/i &&
+ cat >expect <<-EOF &&
+ $HEAD refs/heads/g/h/i@{0}: branch: Created from main
+ EOF
+ git reflog show --no-abbrev-commit refs/heads/g/h/i >actual &&
+ test_cmp expect actual
'
test_expect_success 'checkout -b makes reflog by default' '
@@ -993,13 +1142,27 @@ test_expect_success 'checkout -b with -l makes reflog when core.logAllRefUpdates
git rev-parse --verify gamma@{0}
'
-test_expect_success 'avoid ambiguous track' '
+test_expect_success 'avoid ambiguous track and advise' '
git config branch.autosetupmerge true &&
git config remote.ambi1.url lalala &&
git config remote.ambi1.fetch refs/heads/lalala:refs/heads/main &&
git config remote.ambi2.url lilili &&
git config remote.ambi2.fetch refs/heads/lilili:refs/heads/main &&
- test_must_fail git branch all1 main &&
+ cat <<-EOF >expected &&
+ fatal: not tracking: ambiguous information for ref '\''refs/heads/main'\''
+ hint: There are multiple remotes whose fetch refspecs map to the remote
+ hint: tracking ref '\''refs/heads/main'\'':
+ hint: ambi1
+ hint: ambi2
+ hint:
+ hint: This is typically a configuration error.
+ hint:
+ hint: To support setting up tracking branches, ensure that
+ hint: different remotes'\'' fetch refspecs map into different
+ hint: tracking namespaces.
+ EOF
+ test_must_fail git branch all1 main 2>actual &&
+ test_cmp expected actual &&
test -z "$(git config branch.all1.merge)"
'
@@ -1286,6 +1449,9 @@ test_expect_success 'branch --delete --force removes dangling branch' '
'
test_expect_success 'use --edit-description' '
+ EDITOR=: git branch --edit-description &&
+ test_expect_code 1 git config branch.main.description &&
+
write_script editor <<-\EOF &&
echo "New contents" >"$1"
EOF
@@ -1326,7 +1492,7 @@ test_expect_success '--list during rebase' '
set_fake_editor &&
git rebase -i HEAD~2 &&
git branch --list >actual &&
- test_i18ngrep "rebasing main" actual
+ test_grep "rebasing main" actual
'
test_expect_success '--list during rebase from detached HEAD' '
@@ -1338,7 +1504,7 @@ test_expect_success '--list during rebase from detached HEAD' '
set_fake_editor &&
git rebase -i HEAD~2 &&
git branch --list >actual &&
- test_i18ngrep "rebasing detached HEAD $oid" actual
+ test_grep "rebasing detached HEAD $oid" actual
'
test_expect_success 'tracking with unexpected .fetch refspec' '
@@ -1378,9 +1544,10 @@ test_expect_success 'tracking with unexpected .fetch refspec' '
test_expect_success 'configured committerdate sort' '
git init -b main sort &&
+ test_config -C sort branch.sort "committerdate" &&
+
(
cd sort &&
- git config branch.sort committerdate &&
test_commit initial &&
git checkout -b a &&
test_commit a &&
@@ -1400,9 +1567,10 @@ test_expect_success 'configured committerdate sort' '
'
test_expect_success 'option override configured sort' '
+ test_config -C sort branch.sort "committerdate" &&
+
(
cd sort &&
- git config branch.sort committerdate &&
git branch --sort=refname >actual &&
cat >expect <<-\EOF &&
a
@@ -1414,12 +1582,125 @@ test_expect_success 'option override configured sort' '
)
'
+test_expect_success '--no-sort cancels config sort keys' '
+ test_config -C sort branch.sort "-refname" &&
+
+ (
+ cd sort &&
+
+ # objecttype is identical for all of them, so sort falls back on
+ # default (ascending refname)
+ git branch \
+ --no-sort \
+ --sort="objecttype" >actual &&
+ cat >expect <<-\EOF &&
+ a
+ * b
+ c
+ main
+ EOF
+ test_cmp expect actual
+ )
+
+'
+
+test_expect_success '--no-sort cancels command line sort keys' '
+ (
+ cd sort &&
+
+ # objecttype is identical for all of them, so sort falls back on
+ # default (ascending refname)
+ git branch \
+ --sort="-refname" \
+ --no-sort \
+ --sort="objecttype" >actual &&
+ cat >expect <<-\EOF &&
+ a
+ * b
+ c
+ main
+ EOF
+ test_cmp expect actual
+ )
+'
+
+test_expect_success '--no-sort without subsequent --sort prints expected branches' '
+ (
+ cd sort &&
+
+ # Sort the results with `sort` for a consistent comparison
+ # against expected
+ git branch --no-sort | sort >actual &&
+ cat >expect <<-\EOF &&
+ a
+ c
+ main
+ * b
+ EOF
+ test_cmp expect actual
+ )
+'
+
test_expect_success 'invalid sort parameter in configuration' '
+ test_config -C sort branch.sort "v:notvalid" &&
+
(
cd sort &&
- git config branch.sort "v:notvalid" &&
- test_must_fail git branch
+
+ # this works in the "listing" mode, so bad sort key
+ # is a dying offence.
+ test_must_fail git branch &&
+
+ # these do not need to use sorting, and should all
+ # succeed
+ git branch newone main &&
+ git branch -c newone newerone &&
+ git branch -m newone newestone &&
+ git branch -d newerone newestone
)
'
+test_expect_success 'tracking info copied with --track=inherit' '
+ git branch --track=inherit foo2 my1 &&
+ test_cmp_config local branch.foo2.remote &&
+ test_cmp_config refs/heads/main branch.foo2.merge
+'
+
+test_expect_success 'tracking info copied with autoSetupMerge=inherit' '
+ test_unconfig branch.autoSetupMerge &&
+ # default config does not copy tracking info
+ git branch foo-no-inherit my1 &&
+ test_cmp_config "" --default "" branch.foo-no-inherit.remote &&
+ test_cmp_config "" --default "" branch.foo-no-inherit.merge &&
+ # with autoSetupMerge=inherit, we copy tracking info from my1
+ test_config branch.autoSetupMerge inherit &&
+ git branch foo3 my1 &&
+ test_cmp_config local branch.foo3.remote &&
+ test_cmp_config refs/heads/main branch.foo3.merge &&
+ # no tracking info to inherit from main
+ git branch main2 main &&
+ test_cmp_config "" --default "" branch.main2.remote &&
+ test_cmp_config "" --default "" branch.main2.merge
+'
+
+test_expect_success '--track overrides branch.autoSetupMerge' '
+ test_config branch.autoSetupMerge inherit &&
+ git branch --track=direct foo4 my1 &&
+ test_cmp_config . branch.foo4.remote &&
+ test_cmp_config refs/heads/my1 branch.foo4.merge &&
+ git branch --no-track foo5 my1 &&
+ test_cmp_config "" --default "" branch.foo5.remote &&
+ test_cmp_config "" --default "" branch.foo5.merge
+'
+
+test_expect_success 'errors if given a bad branch name' '
+ cat <<-\EOF >expect &&
+ fatal: '\''foo..bar'\'' is not a valid branch name
+ hint: See `man git check-ref-format`
+ hint: Disable this message with "git config advice.refSyntax false"
+ EOF
+ test_must_fail git branch foo..bar >actual 2>&1 &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t3201-branch-contains.sh b/t/t3201-branch-contains.sh
index 349a810..800fc33 100755
--- a/t/t3201-branch-contains.sh
+++ b/t/t3201-branch-contains.sh
@@ -2,9 +2,6 @@
test_description='branch --contains <commit>, --no-contains <commit> --merged, and --no-merged'
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t3202-show-branch.sh b/t/t3202-show-branch.sh
index ad9902a..a1139f7 100755
--- a/t/t3202-show-branch.sh
+++ b/t/t3202-show-branch.sh
@@ -4,12 +4,34 @@ test_description='test show-branch'
. ./test-lib.sh
+test_expect_success 'error descriptions on empty repository' '
+ current=$(git branch --show-current) &&
+ cat >expect <<-EOF &&
+ error: no commit on branch '\''$current'\'' yet
+ EOF
+ test_must_fail git branch --edit-description 2>actual &&
+ test_cmp expect actual &&
+ test_must_fail git branch --edit-description $current 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'fatal descriptions on empty repository' '
+ current=$(git branch --show-current) &&
+ cat >expect <<-EOF &&
+ fatal: no commit on branch '\''$current'\'' yet
+ EOF
+ test_must_fail git branch --set-upstream-to=non-existent 2>actual &&
+ test_cmp expect actual &&
+ test_must_fail git branch -c new-branch 2>actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'setup' '
test_commit initial &&
for i in $(test_seq 1 10)
do
git checkout -b branch$i initial &&
- test_commit --no-tag branch$i
+ test_commit --no-tag branch$i || return 1
done &&
git for-each-ref \
--sort=version:refname \
@@ -49,7 +71,7 @@ test_expect_success 'show-branch with more than 8 branches' '
test_expect_success 'show-branch with showbranch.default' '
for branch in $(cat branches.sorted)
do
- test_config showbranch.default $branch --add
+ test_config showbranch.default $branch --add || return 1
done &&
git show-branch >actual &&
test_cmp expect actual
@@ -94,6 +116,22 @@ test_expect_success 'show branch --remotes' '
test_must_be_empty actual.out
'
+test_expect_success 'show-branch --sparse' '
+ test_when_finished "git checkout branch10 && git branch -D branchA" &&
+ git checkout -b branchA branch10 &&
+ git merge -s ours -m "merge 1 and 10 to make A" branch1 &&
+ git commit --allow-empty -m "another" &&
+
+ git show-branch --sparse >out &&
+ grep "merge 1 and 10 to make A" out &&
+
+ git show-branch >out &&
+ ! grep "merge 1 and 10 to make A" out &&
+
+ git show-branch --no-sparse >out &&
+ ! grep "merge 1 and 10 to make A" out
+'
+
test_expect_success 'setup show branch --list' '
sed "s/^> //" >expect <<-\EOF
> [branch1] branch1
@@ -124,7 +162,7 @@ test_expect_success 'show branch --merge-base with one argument' '
do
git rev-parse $branch >expect &&
git show-branch --merge-base $branch >actual &&
- test_cmp expect actual
+ test_cmp expect actual || return 1
done
'
@@ -133,7 +171,7 @@ test_expect_success 'show branch --merge-base with two arguments' '
do
git rev-parse initial >expect &&
git show-branch --merge-base initial $branch >actual &&
- test_cmp expect actual
+ test_cmp expect actual || return 1
done
'
@@ -146,4 +184,103 @@ test_expect_success 'show branch --merge-base with N arguments' '
test_cmp expect actual
'
+# incompatible options
+while read combo
+do
+ test_expect_success "show-branch $combo (should fail)" '
+ test_must_fail git show-branch $combo 2>error &&
+ grep -e "cannot be used together" -e "usage:" error
+ '
+done <<\EOF
+--all --reflog
+--merge-base --reflog
+--list --merge-base
+--reflog --current
+EOF
+
+# unnegatable options
+for opt in topo-order date-order reflog
+do
+ test_expect_success "show-branch --no-$opt (should fail)" '
+ test_must_fail git show-branch --no-$opt 2>err &&
+ grep "unknown option .no-$opt." err
+ '
+done
+
+test_expect_success 'error descriptions on non-existent branch' '
+ cat >expect <<-EOF &&
+ error: no branch named '\''non-existent'\''
+ EOF
+ test_must_fail git branch --edit-description non-existent 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'fatal descriptions on non-existent branch' '
+ cat >expect <<-EOF &&
+ fatal: branch '\''non-existent'\'' does not exist
+ EOF
+ test_must_fail git branch --set-upstream-to=non-existent non-existent 2>actual &&
+ test_cmp expect actual &&
+
+ cat >expect <<-EOF &&
+ fatal: no branch named '\''non-existent'\''
+ EOF
+ test_must_fail git branch -c non-existent new-branch 2>actual &&
+ test_cmp expect actual &&
+ test_must_fail git branch -m non-existent new-branch 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'error descriptions on orphan branch' '
+ test_when_finished git worktree remove -f wt &&
+ git worktree add wt --detach &&
+ git -C wt checkout --orphan orphan-branch &&
+ test_branch_op_in_wt() {
+ test_orphan_error() {
+ test_must_fail git $* 2>actual &&
+ test_grep "no commit on branch .orphan-branch. yet$" actual
+ } &&
+ test_orphan_error -C wt branch $1 $2 && # implicit branch
+ test_orphan_error -C wt branch $1 orphan-branch $2 && # explicit branch
+ test_orphan_error branch $1 orphan-branch $2 # different worktree
+ } &&
+ test_branch_op_in_wt --edit-description &&
+ test_branch_op_in_wt --set-upstream-to=ne &&
+ test_branch_op_in_wt -c new-branch
+'
+
+test_expect_success 'setup reflogs' '
+ test_commit base &&
+ git checkout -b branch &&
+ test_commit one &&
+ git reset --hard HEAD^ &&
+ test_commit two &&
+ test_commit three
+'
+
+test_expect_success '--reflog shows reflog entries' '
+ cat >expect <<-\EOF &&
+ ! [branch@{0}] (0 seconds ago) commit: three
+ ! [branch@{1}] (60 seconds ago) commit: two
+ ! [branch@{2}] (2 minutes ago) reset: moving to HEAD^
+ ! [branch@{3}] (2 minutes ago) commit: one
+ ----
+ + [branch@{0}] three
+ ++ [branch@{1}] two
+ + [branch@{3}] one
+ ++++ [branch@{2}] base
+ EOF
+ # the output always contains relative timestamps; use
+ # a known time to get deterministic results
+ GIT_TEST_DATE_NOW=$test_tick \
+ git show-branch --reflog branch >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--reflog handles missing reflog' '
+ git reflog expire --expire=now branch &&
+ git show-branch --reflog branch >actual &&
+ test_must_be_empty actual
+'
+
test_done
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index 6e94c6d..758963b 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -1,9 +1,6 @@
#!/bin/sh
test_description='git branch display tests'
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
@@ -58,9 +55,17 @@ cat >expect <<'EOF'
EOF
test_expect_success 'git branch -r shows remote branches' '
git branch -r >actual &&
+ test_cmp expect actual &&
+
+ git branch --remotes >actual &&
test_cmp expect actual
'
+test_expect_success 'git branch --no-remotes is rejected' '
+ test_must_fail git branch --no-remotes 2>err &&
+ grep "unknown option .no-remotes." err
+'
+
cat >expect <<'EOF'
branch-one
branch-two
@@ -71,9 +76,17 @@ cat >expect <<'EOF'
EOF
test_expect_success 'git branch -a shows local and remote branches' '
git branch -a >actual &&
+ test_cmp expect actual &&
+
+ git branch --all >actual &&
test_cmp expect actual
'
+test_expect_success 'git branch --no-all is rejected' '
+ test_must_fail git branch --no-all 2>err &&
+ grep "unknown option .no-all." err
+'
+
cat >expect <<'EOF'
two
one
@@ -340,10 +353,48 @@ test_expect_success 'git branch --format option' '
test_cmp expect actual
'
+test_expect_success 'git branch --format with ahead-behind' '
+ cat >expect <<-\EOF &&
+ (HEAD detached from fromtag) 0 0
+ refs/heads/ambiguous 0 0
+ refs/heads/branch-one 1 0
+ refs/heads/branch-two 0 0
+ refs/heads/main 1 0
+ refs/heads/ref-to-branch 1 0
+ refs/heads/ref-to-remote 1 0
+ EOF
+ git branch --format="%(refname) %(ahead-behind:HEAD)" >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'git branch with --format=%(rest) must fail' '
test_must_fail git branch --format="%(rest)" >actual
'
+test_expect_success 'git branch --format --omit-empty' '
+ cat >expect <<-\EOF &&
+ Refname is (HEAD detached from fromtag)
+ Refname is refs/heads/ambiguous
+ Refname is refs/heads/branch-one
+ Refname is refs/heads/branch-two
+
+ Refname is refs/heads/ref-to-branch
+ Refname is refs/heads/ref-to-remote
+ EOF
+ git branch --format="%(if:notequals=refs/heads/main)%(refname)%(then)Refname is %(refname)%(end)" >actual &&
+ test_cmp expect actual &&
+ cat >expect <<-\EOF &&
+ Refname is (HEAD detached from fromtag)
+ Refname is refs/heads/ambiguous
+ Refname is refs/heads/branch-one
+ Refname is refs/heads/branch-two
+ Refname is refs/heads/ref-to-branch
+ Refname is refs/heads/ref-to-remote
+ EOF
+ git branch --omit-empty --format="%(if:notequals=refs/heads/main)%(refname)%(then)Refname is %(refname)%(end)" >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'worktree colors correct' '
cat >expect <<-EOF &&
* <GREEN>(HEAD detached from fromtag)<RESET>
diff --git a/t/t3204-branch-name-interpretation.sh b/t/t3204-branch-name-interpretation.sh
index 993a6b5..594e3e4 100755
--- a/t/t3204-branch-name-interpretation.sh
+++ b/t/t3204-branch-name-interpretation.sh
@@ -9,6 +9,7 @@ This script aims to check the behavior of those corner cases.
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
expect_branch() {
@@ -57,6 +58,16 @@ test_expect_success 'create branch with pseudo-qualified name' '
expect_branch refs/heads/refs/heads/qualified two
'
+test_expect_success 'force-copy a branch to itself via @{-1} is no-op' '
+ git branch -t copiable main &&
+ git checkout copiable &&
+ git checkout - &&
+ git branch -C @{-1} copiable &&
+ git config --get-all branch.copiable.merge >actual &&
+ echo refs/heads/main >expect &&
+ test_cmp expect actual
+'
+
test_expect_success 'delete branch via @{-1}' '
git branch previous-del &&
@@ -133,4 +144,28 @@ test_expect_success 'checkout does not treat remote @{upstream} as a branch' '
expect_branch HEAD one
'
+test_expect_success 'edit-description via @{-1}' '
+ git checkout -b desc-branch &&
+ git checkout -b non-desc-branch &&
+ write_script editor <<-\EOF &&
+ echo "Branch description" >"$1"
+ EOF
+ EDITOR=./editor git branch --edit-description @{-1} &&
+ test_must_fail git config branch.non-desc-branch.description &&
+ git config branch.desc-branch.description >actual &&
+ printf "Branch description\n\n" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'modify branch upstream via "@{-1}" and "@{-1}@{upstream}"' '
+ git checkout -b upstream-branch &&
+ git checkout -b upstream-other -t upstream-branch &&
+ git branch --set-upstream-to upstream-other @{-1} &&
+ git config branch.upstream-branch.merge >actual &&
+ echo "refs/heads/upstream-other" >expect &&
+ test_cmp expect actual &&
+ git branch --unset-upstream @{-1}@{upstream} &&
+ test_must_fail git config branch.upstream-other.merge
+'
+
test_done
diff --git a/t/t3205-branch-color.sh b/t/t3205-branch-color.sh
index 08bd906..0b61da9 100755
--- a/t/t3205-branch-color.sh
+++ b/t/t3205-branch-color.sh
@@ -1,9 +1,7 @@
#!/bin/sh
test_description='basic branch output coloring'
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'set up some sample branches' '
diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh
index e30bc48..7b05bf3 100755
--- a/t/t3206-range-diff.sh
+++ b/t/t3206-range-diff.sh
@@ -33,6 +33,26 @@ test_expect_success 'setup' '
u3 sha256:736c4bc
u4 sha256:673e77d
+ # topic (abbrev=10)
+ t1_abbrev sha1:4de457d2c0
+ t2_abbrev sha1:fccce22f8c
+ t3_abbrev sha1:147e64ef53
+ t4_abbrev sha1:a63e992599
+ t1_abbrev sha256:b89f8b9092
+ t2_abbrev sha256:5f12aadf34
+ t3_abbrev sha256:ea8b273a6c
+ t4_abbrev sha256:14b73361fc
+
+ # unmodified (abbrev=10)
+ u1_abbrev sha1:35b9b25f76
+ u2_abbrev sha1:de345ab3de
+ u3_abbrev sha1:9af6654000
+ u4_abbrev sha1:2901f773f3
+ u1_abbrev sha256:e3731be242
+ u2_abbrev sha256:14fadf8cee
+ u3_abbrev sha256:736c4bcb44
+ u4_abbrev sha256:673e77d589
+
# reordered
r1 sha1:aca177a
r2 sha1:14ad629
@@ -153,6 +173,18 @@ test_expect_success 'simple A B C (unmodified)' '
test_cmp expect actual
'
+test_expect_success 'simple A..B A..C (unmodified) with --abbrev' '
+ git range-diff --no-color --abbrev=10 main..topic main..unmodified \
+ >actual &&
+ cat >expect <<-EOF &&
+ 1: $(test_oid t1_abbrev) = 1: $(test_oid u1_abbrev) s/5/A/
+ 2: $(test_oid t2_abbrev) = 2: $(test_oid u2_abbrev) s/4/A/
+ 3: $(test_oid t3_abbrev) = 3: $(test_oid u3_abbrev) s/11/B/
+ 4: $(test_oid t4_abbrev) = 4: $(test_oid u4_abbrev) s/12/B/
+ EOF
+ test_cmp expect actual
+'
+
test_expect_success 'A^! and A^-<n> (unmodified)' '
git range-diff --no-color topic^! unmodified^-1 >actual &&
cat >expect <<-EOF &&
@@ -162,8 +194,8 @@ test_expect_success 'A^! and A^-<n> (unmodified)' '
'
test_expect_success 'A^{/..} is not mistaken for a range' '
- test_must_fail git range-diff topic^.. topic^{/..} 2>error &&
- test_i18ngrep "not a commit range" error
+ test_must_fail git range-diff topic^.. topic^{/..} -- 2>error &&
+ test_grep "not a commit range" error
'
test_expect_success 'trivial reordering' '
@@ -505,7 +537,7 @@ do
main..unmodified >actual &&
test_when_finished "rm 000?-*" &&
test_line_count = 5 actual &&
- test_i18ngrep "^Range-diff:$" 0000-* &&
+ test_grep "^Range-diff:$" 0000-* &&
grep "= 1: .* s/5/A" 0000-* &&
grep "= 2: .* s/4/A" 0000-* &&
grep "= 3: .* s/11/B" 0000-* &&
@@ -517,7 +549,7 @@ test_expect_success 'format-patch --range-diff as commentary' '
git format-patch --range-diff=HEAD~1 HEAD~1 >actual &&
test_when_finished "rm 0001-*" &&
test_line_count = 1 actual &&
- test_i18ngrep "^Range-diff:$" 0001-* &&
+ test_grep "^Range-diff:$" 0001-* &&
grep "> 1: .* new message" 0001-*
'
@@ -525,7 +557,7 @@ test_expect_success 'format-patch --range-diff reroll-count with a non-integer'
git format-patch --range-diff=HEAD~1 -v2.9 HEAD~1 >actual &&
test_when_finished "rm v2.9-0001-*" &&
test_line_count = 1 actual &&
- test_i18ngrep "^Range-diff:$" v2.9-0001-* &&
+ test_grep "^Range-diff:$" v2.9-0001-* &&
grep "> 1: .* new message" v2.9-0001-*
'
@@ -533,7 +565,7 @@ test_expect_success 'format-patch --range-diff reroll-count with a integer' '
git format-patch --range-diff=HEAD~1 -v2 HEAD~1 >actual &&
test_when_finished "rm v2-0001-*" &&
test_line_count = 1 actual &&
- test_i18ngrep "^Range-diff ..* v1:$" v2-0001-* &&
+ test_grep "^Range-diff ..* v1:$" v2-0001-* &&
grep "> 1: .* new message" v2-0001-*
'
@@ -541,7 +573,7 @@ test_expect_success 'format-patch --range-diff with v0' '
git format-patch --range-diff=HEAD~1 -v0 HEAD~1 >actual &&
test_when_finished "rm v0-0001-*" &&
test_line_count = 1 actual &&
- test_i18ngrep "^Range-diff:$" v0-0001-* &&
+ test_grep "^Range-diff:$" v0-0001-* &&
grep "> 1: .* new message" v0-0001-*
'
@@ -630,6 +662,20 @@ test_expect_success 'range-diff with multiple --notes' '
test_cmp expect actual
'
+# `range-diff` should act like `log` with regards to notes
+test_expect_success 'range-diff with --notes=custom does not show default notes' '
+ git notes add -m "topic note" topic &&
+ git notes add -m "unmodified note" unmodified &&
+ git notes --ref=custom add -m "topic note" topic &&
+ git notes --ref=custom add -m "unmodified note" unmodified &&
+ test_when_finished git notes remove topic unmodified &&
+ test_when_finished git notes --ref=custom remove topic unmodified &&
+ git range-diff --notes=custom main..topic main..unmodified \
+ >actual &&
+ ! grep "## Notes ##" actual &&
+ grep "## Notes (custom) ##" actual
+'
+
test_expect_success 'format-patch --range-diff does not compare notes by default' '
git notes add -m "topic note" topic &&
git notes add -m "unmodified note" unmodified &&
@@ -638,7 +684,7 @@ test_expect_success 'format-patch --range-diff does not compare notes by default
main..unmodified >actual &&
test_when_finished "rm 000?-*" &&
test_line_count = 5 actual &&
- test_i18ngrep "^Range-diff:$" 0000-* &&
+ test_grep "^Range-diff:$" 0000-* &&
grep "= 1: .* s/5/A" 0000-* &&
grep "= 2: .* s/4/A" 0000-* &&
grep "= 3: .* s/11/B" 0000-* &&
@@ -647,6 +693,20 @@ test_expect_success 'format-patch --range-diff does not compare notes by default
! grep "note" 0000-*
'
+test_expect_success 'format-patch --notes=custom --range-diff only compares custom notes' '
+ git notes add -m "topic note" topic &&
+ git notes --ref=custom add -m "topic note (custom)" topic &&
+ git notes add -m "unmodified note" unmodified &&
+ git notes --ref=custom add -m "unmodified note (custom)" unmodified &&
+ test_when_finished git notes remove topic unmodified &&
+ test_when_finished git notes --ref=custom remove topic unmodified &&
+ git format-patch --notes=custom --cover-letter --range-diff=$prev \
+ main..unmodified >actual &&
+ test_when_finished "rm 000?-*" &&
+ grep "## Notes (custom) ##" 0000-* &&
+ ! grep "## Notes ##" 0000-*
+'
+
test_expect_success 'format-patch --range-diff with --no-notes' '
git notes add -m "topic note" topic &&
git notes add -m "unmodified note" unmodified &&
@@ -655,7 +715,7 @@ test_expect_success 'format-patch --range-diff with --no-notes' '
main..unmodified >actual &&
test_when_finished "rm 000?-*" &&
test_line_count = 5 actual &&
- test_i18ngrep "^Range-diff:$" 0000-* &&
+ test_grep "^Range-diff:$" 0000-* &&
grep "= 1: .* s/5/A" 0000-* &&
grep "= 2: .* s/4/A" 0000-* &&
grep "= 3: .* s/11/B" 0000-* &&
@@ -672,7 +732,7 @@ test_expect_success 'format-patch --range-diff with --notes' '
main..unmodified >actual &&
test_when_finished "rm 000?-*" &&
test_line_count = 5 actual &&
- test_i18ngrep "^Range-diff:$" 0000-* &&
+ test_grep "^Range-diff:$" 0000-* &&
grep "= 1: .* s/5/A" 0000-* &&
grep "= 2: .* s/4/A" 0000-* &&
grep "= 3: .* s/11/B" 0000-* &&
@@ -701,7 +761,7 @@ test_expect_success 'format-patch --range-diff with format.notes config' '
main..unmodified >actual &&
test_when_finished "rm 000?-*" &&
test_line_count = 5 actual &&
- test_i18ngrep "^Range-diff:$" 0000-* &&
+ test_grep "^Range-diff:$" 0000-* &&
grep "= 1: .* s/5/A" 0000-* &&
grep "= 2: .* s/4/A" 0000-* &&
grep "= 3: .* s/11/B" 0000-* &&
@@ -732,7 +792,7 @@ test_expect_success 'format-patch --range-diff with multiple notes' '
main..unmodified >actual &&
test_when_finished "rm 000?-*" &&
test_line_count = 5 actual &&
- test_i18ngrep "^Range-diff:$" 0000-* &&
+ test_grep "^Range-diff:$" 0000-* &&
grep "= 1: .* s/5/A" 0000-* &&
grep "= 2: .* s/4/A" 0000-* &&
grep "= 3: .* s/11/B" 0000-* &&
@@ -772,4 +832,66 @@ test_expect_success '--left-only/--right-only' '
test_cmp expect actual
'
+test_expect_success 'ranges with pathspecs' '
+ git range-diff topic...mode-only-change -- other-file >actual &&
+ test_line_count = 2 actual &&
+ topic_oid=$(git rev-parse --short topic) &&
+ mode_change_oid=$(git rev-parse --short mode-only-change^) &&
+ file_change_oid=$(git rev-parse --short mode-only-change) &&
+ grep "$mode_change_oid" actual &&
+ ! grep "$file_change_oid" actual &&
+ ! grep "$topic_oid" actual
+'
+
+test_expect_success 'submodule changes are shown irrespective of diff.submodule' '
+ git init sub-repo &&
+ test_commit -C sub-repo sub-first &&
+ sub_oid1=$(git -C sub-repo rev-parse HEAD) &&
+ test_commit -C sub-repo sub-second &&
+ sub_oid2=$(git -C sub-repo rev-parse HEAD) &&
+ test_commit -C sub-repo sub-third &&
+ sub_oid3=$(git -C sub-repo rev-parse HEAD) &&
+
+ git checkout -b main-sub topic &&
+ git -c protocol.file.allow=always submodule add ./sub-repo sub &&
+ git -C sub checkout --detach sub-first &&
+ git commit -m "add sub" sub &&
+ sup_oid1=$(git rev-parse --short HEAD) &&
+ git checkout -b topic-sub &&
+ git -C sub checkout sub-second &&
+ git commit -m "change sub" sub &&
+ sup_oid2=$(git rev-parse --short HEAD) &&
+ git checkout -b modified-sub main-sub &&
+ git -C sub checkout sub-third &&
+ git commit -m "change sub" sub &&
+ sup_oid3=$(git rev-parse --short HEAD) &&
+ sup_oid0=$(test_oid __) &&
+
+ test_config diff.submodule log &&
+ git range-diff topic topic-sub modified-sub >actual &&
+ cat >expect <<-EOF &&
+ 1: $sup_oid1 = 1: $sup_oid1 add sub
+ 2: $sup_oid2 < -: $sup_oid0 change sub
+ -: $sup_oid0 > 2: $sup_oid3 change sub
+ EOF
+ test_cmp expect actual &&
+ test_config diff.submodule diff &&
+ git range-diff topic topic-sub modified-sub >actual &&
+ git range-diff --creation-factor=100 topic topic-sub modified-sub >actual &&
+ cat >expect <<-EOF &&
+ 1: $sup_oid1 = 1: $sup_oid1 add sub
+ 2: $sup_oid2 ! 2: $sup_oid3 change sub
+ @@ Commit message
+ ## sub ##
+ @@
+ -Subproject commit $sub_oid1
+ -+Subproject commit $sub_oid2
+ ++Subproject commit $sub_oid3
+ EOF
+ test_cmp expect actual &&
+ test_config diff.submodule diff &&
+ git range-diff --creation-factor=100 topic topic-sub modified-sub >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t3207-branch-submodule.sh b/t/t3207-branch-submodule.sh
new file mode 100755
index 0000000..fe72b24
--- /dev/null
+++ b/t/t3207-branch-submodule.sh
@@ -0,0 +1,329 @@
+#!/bin/sh
+
+test_description='git branch submodule tests'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+pwd=$(pwd)
+
+# Creates a clean test environment in "pwd" by copying the repo setup
+# from test_dirs.
+reset_test () {
+ rm -fr super &&
+ rm -fr sub-sub-upstream &&
+ rm -fr sub-upstream &&
+ cp -r test_dirs/* .
+}
+
+# Tests that the expected branch does not exist
+test_no_branch () {
+ DIR=$1 &&
+ BRANCH_NAME=$2 &&
+ test_must_fail git -C "$DIR" rev-parse "$BRANCH_NAME" 2>err &&
+ grep "ambiguous argument .$BRANCH_NAME." err
+}
+
+test_expect_success 'setup superproject and submodule' '
+ git config --global protocol.file.allow always &&
+ mkdir test_dirs &&
+ (
+ cd test_dirs &&
+ git init super &&
+ test_commit -C super foo &&
+ git init sub-sub-upstream &&
+ test_commit -C sub-sub-upstream foo &&
+ git init sub-upstream &&
+ # Submodule in a submodule
+ git -C sub-upstream submodule add "${pwd}/test_dirs/sub-sub-upstream" sub-sub &&
+ git -C sub-upstream commit -m "add submodule" &&
+ # Regular submodule
+ git -C super submodule add "${pwd}/test_dirs/sub-upstream" sub &&
+ # Submodule in a subdirectory
+ git -C super submodule add "${pwd}/test_dirs/sub-sub-upstream" second/sub &&
+ git -C super commit -m "add submodule" &&
+ git -C super config submodule.propagateBranches true &&
+ git -C super/sub submodule update --init
+ ) &&
+ reset_test
+'
+
+# Test the argument parsing
+test_expect_success '--recurse-submodules should create branches' '
+ test_when_finished "reset_test" &&
+ (
+ cd super &&
+ git branch --recurse-submodules branch-a &&
+ git rev-parse branch-a &&
+ git -C sub rev-parse branch-a &&
+ git -C sub/sub-sub rev-parse branch-a &&
+ git -C second/sub rev-parse branch-a
+ )
+'
+
+test_expect_success '--recurse-submodules should die if submodule.propagateBranches is false' '
+ test_when_finished "reset_test" &&
+ (
+ cd super &&
+ echo "fatal: branch with --recurse-submodules can only be used if submodule.propagateBranches is enabled" >expected &&
+ test_must_fail git -c submodule.propagateBranches=false branch --recurse-submodules branch-a 2>actual &&
+ test_cmp expected actual
+ )
+'
+
+test_expect_success '--recurse-submodules should fail when not creating branches' '
+ test_when_finished "reset_test" &&
+ (
+ cd super &&
+ git branch --recurse-submodules branch-a &&
+ echo "fatal: --recurse-submodules can only be used to create branches" >expected &&
+ test_must_fail git branch --recurse-submodules -D branch-a 2>actual &&
+ test_cmp expected actual &&
+ # Assert that the branches were not deleted
+ git rev-parse branch-a &&
+ git -C sub rev-parse branch-a
+ )
+'
+
+test_expect_success 'should respect submodule.recurse when creating branches' '
+ test_when_finished "reset_test" &&
+ (
+ cd super &&
+ git -c submodule.recurse=true branch branch-a &&
+ git rev-parse branch-a &&
+ git -C sub rev-parse branch-a
+ )
+'
+
+test_expect_success 'should ignore submodule.recurse when not creating branches' '
+ test_when_finished "reset_test" &&
+ (
+ cd super &&
+ git branch --recurse-submodules branch-a &&
+ git -c submodule.recurse=true branch -D branch-a &&
+ test_no_branch . branch-a &&
+ git -C sub rev-parse branch-a
+ )
+'
+
+# Test branch creation behavior
+test_expect_success 'should create branches based off commit id in superproject' '
+ test_when_finished "reset_test" &&
+ (
+ cd super &&
+ git branch --recurse-submodules branch-a &&
+ git checkout --recurse-submodules branch-a &&
+ git -C sub rev-parse HEAD >expected &&
+ # Move the tip of sub:branch-a so that it no longer matches the commit in super:branch-a
+ git -C sub checkout branch-a &&
+ test_commit -C sub bar &&
+ # Create a new branch-b branch with start-point=branch-a
+ git branch --recurse-submodules branch-b branch-a &&
+ git rev-parse branch-b &&
+ git -C sub rev-parse branch-b >actual &&
+ # Assert that the commit id of sub:second-branch matches super:branch-a and not sub:branch-a
+ test_cmp expected actual
+ )
+'
+
+test_expect_success 'should not create any branches if branch is not valid for all repos' '
+ test_when_finished "reset_test" &&
+ (
+ cd super &&
+ git -C sub branch branch-a &&
+ test_must_fail git branch --recurse-submodules branch-a 2>actual &&
+ test_no_branch . branch-a &&
+ grep "submodule .sub.: fatal: a branch named .branch-a. already exists" actual
+ )
+'
+
+test_expect_success 'should create branches if branch exists and --force is given' '
+ test_when_finished "reset_test" &&
+ (
+ cd super &&
+ git -C sub rev-parse HEAD >expected &&
+ test_commit -C sub baz &&
+ # branch-a in sub now points to a newer commit.
+ git -C sub branch branch-a HEAD &&
+ git -C sub rev-parse branch-a >actual-old-branch-a &&
+ git branch --recurse-submodules --force branch-a &&
+ git rev-parse branch-a &&
+ git -C sub rev-parse branch-a >actual-new-branch-a &&
+ test_cmp expected actual-new-branch-a &&
+ # assert that branch --force actually moved the sub
+ # branch
+ ! test_cmp expected actual-old-branch-a
+ )
+'
+
+test_expect_success 'should create branch when submodule is not in HEAD:.gitmodules' '
+ test_when_finished "reset_test" &&
+ (
+ cd super &&
+ git branch branch-a &&
+ git checkout -b branch-b &&
+ git submodule add ../sub-upstream sub2 &&
+ git -C sub2 submodule update --init &&
+ # branch-b now has a committed submodule not in branch-a
+ git commit -m "add second submodule" &&
+ git checkout branch-a &&
+ git branch --recurse-submodules branch-c branch-b &&
+ git checkout --recurse-submodules branch-c &&
+ git -C sub2 rev-parse branch-c &&
+ git -C sub2/sub-sub rev-parse branch-c
+ )
+'
+
+test_expect_success 'should not create branches in inactive submodules' '
+ test_when_finished "reset_test" &&
+ test_config -C super submodule.sub.active false &&
+ (
+ cd super &&
+ git branch --recurse-submodules branch-a &&
+ git rev-parse branch-a &&
+ test_no_branch sub branch-a
+ )
+'
+
+test_expect_success 'should set up tracking of local branches with track=always' '
+ test_when_finished "reset_test" &&
+ (
+ cd super &&
+ git -c branch.autoSetupMerge=always branch --recurse-submodules branch-a main &&
+ git -C sub rev-parse main &&
+ test_cmp_config -C sub . branch.branch-a.remote &&
+ test_cmp_config -C sub refs/heads/main branch.branch-a.merge
+ )
+'
+
+test_expect_success 'should set up tracking of local branches with explicit track' '
+ test_when_finished "reset_test" &&
+ (
+ cd super &&
+ git branch --track --recurse-submodules branch-a main &&
+ git -C sub rev-parse main &&
+ test_cmp_config -C sub . branch.branch-a.remote &&
+ test_cmp_config -C sub refs/heads/main branch.branch-a.merge
+ )
+'
+
+test_expect_success 'should not set up unnecessary tracking of local branches' '
+ test_when_finished "reset_test" &&
+ (
+ cd super &&
+ git branch --recurse-submodules branch-a main &&
+ git -C sub rev-parse main &&
+ test_cmp_config -C sub "" --default "" branch.branch-a.remote &&
+ test_cmp_config -C sub "" --default "" branch.branch-a.merge
+ )
+'
+
+reset_remote_test () {
+ rm -fr super-clone &&
+ reset_test
+}
+
+test_expect_success 'setup tests with remotes' '
+ (
+ cd test_dirs &&
+ (
+ cd super &&
+ git branch branch-a &&
+ git checkout -b branch-b &&
+ git submodule add ../sub-upstream sub2 &&
+ # branch-b now has a committed submodule not in branch-a
+ git commit -m "add second submodule"
+ ) &&
+ git clone --branch main --recurse-submodules super super-clone &&
+ git -C super-clone config submodule.propagateBranches true
+ ) &&
+ reset_remote_test
+'
+
+test_expect_success 'should get fatal error upon branch creation when submodule is not in .git/modules' '
+ test_when_finished "reset_remote_test" &&
+ (
+ cd super-clone &&
+ # This should succeed because super-clone has sub in .git/modules
+ git branch --recurse-submodules branch-a origin/branch-a &&
+ # This should fail because super-clone does not have sub2 .git/modules
+ test_must_fail git branch --recurse-submodules branch-b origin/branch-b 2>actual &&
+ grep "fatal: submodule .sub2.: unable to find submodule" actual &&
+ test_no_branch . branch-b &&
+ test_no_branch sub branch-b &&
+ # User can fix themselves by initializing the submodule
+ git checkout origin/branch-b &&
+ git submodule update --init --recursive &&
+ git branch --recurse-submodules branch-b origin/branch-b
+ )
+'
+
+test_expect_success 'should set up tracking of remote-tracking branches by default' '
+ test_when_finished "reset_remote_test" &&
+ (
+ cd super-clone &&
+ git branch --recurse-submodules branch-a origin/branch-a &&
+ test_cmp_config origin branch.branch-a.remote &&
+ test_cmp_config refs/heads/branch-a branch.branch-a.merge &&
+ # "origin/branch-a" does not exist for "sub", but it matches the refspec
+ # so tracking should be set up
+ test_cmp_config -C sub origin branch.branch-a.remote &&
+ test_cmp_config -C sub refs/heads/branch-a branch.branch-a.merge &&
+ test_cmp_config -C sub/sub-sub origin branch.branch-a.remote &&
+ test_cmp_config -C sub/sub-sub refs/heads/branch-a branch.branch-a.merge
+ )
+'
+
+test_expect_success 'should not fail when unable to set up tracking in submodule' '
+ test_when_finished "reset_remote_test" &&
+ (
+ cd super-clone &&
+ git remote rename origin ex-origin &&
+ git branch --recurse-submodules branch-a ex-origin/branch-a &&
+ test_cmp_config ex-origin branch.branch-a.remote &&
+ test_cmp_config refs/heads/branch-a branch.branch-a.merge &&
+ test_cmp_config -C sub "" --default "" branch.branch-a.remote &&
+ test_cmp_config -C sub "" --default "" branch.branch-a.merge
+ )
+'
+
+test_expect_success '--track=inherit should set up tracking correctly' '
+ test_when_finished "reset_remote_test" &&
+ (
+ cd super-clone &&
+ git branch --recurse-submodules branch-a origin/branch-a &&
+ # Set this manually instead of using branch --set-upstream-to
+ # to circumvent the "nonexistent upstream" check.
+ git -C sub config branch.branch-a.remote origin &&
+ git -C sub config branch.branch-a.merge refs/heads/sub-branch-a &&
+ git -C sub/sub-sub config branch.branch-a.remote other &&
+ git -C sub/sub-sub config branch.branch-a.merge refs/heads/sub-sub-branch-a &&
+
+ git branch --recurse-submodules --track=inherit branch-b branch-a &&
+ test_cmp_config origin branch.branch-b.remote &&
+ test_cmp_config refs/heads/branch-a branch.branch-b.merge &&
+ test_cmp_config -C sub origin branch.branch-b.remote &&
+ test_cmp_config -C sub refs/heads/sub-branch-a branch.branch-b.merge &&
+ test_cmp_config -C sub/sub-sub other branch.branch-b.remote &&
+ test_cmp_config -C sub/sub-sub refs/heads/sub-sub-branch-a branch.branch-b.merge
+ )
+'
+
+test_expect_success '--no-track should not set up tracking' '
+ test_when_finished "reset_remote_test" &&
+ (
+ cd super-clone &&
+ git branch --recurse-submodules --no-track branch-a origin/branch-a &&
+ test_cmp_config "" --default "" branch.branch-a.remote &&
+ test_cmp_config "" --default "" branch.branch-a.merge &&
+ test_cmp_config -C sub "" --default "" branch.branch-a.remote &&
+ test_cmp_config -C sub "" --default "" branch.branch-a.merge &&
+ test_cmp_config -C sub/sub-sub "" --default "" branch.branch-a.remote &&
+ test_cmp_config -C sub/sub-sub "" --default "" branch.branch-a.merge
+ )
+'
+
+test_done
diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh
deleted file mode 100755
index 577f32d..0000000
--- a/t/t3210-pack-refs.sh
+++ /dev/null
@@ -1,259 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Amos Waterland
-# Copyright (c) 2006 Christian Couder
-#
-
-test_description='git pack-refs should not change the branch semantic
-
-This test runs git pack-refs and git show-ref and checks that the branch
-semantic is still the same.
-'
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
-. ./test-lib.sh
-
-test_expect_success 'enable reflogs' '
- git config core.logallrefupdates true
-'
-
-test_expect_success \
- 'prepare a trivial repository' \
- 'echo Hello > A &&
- git update-index --add A &&
- git commit -m "Initial commit." &&
- HEAD=$(git rev-parse --verify HEAD)'
-
-SHA1=
-
-test_expect_success \
- 'see if git show-ref works as expected' \
- 'git branch a &&
- SHA1=$(cat .git/refs/heads/a) &&
- echo "$SHA1 refs/heads/a" >expect &&
- git show-ref a >result &&
- test_cmp expect result'
-
-test_expect_success \
- 'see if a branch still exists when packed' \
- 'git branch b &&
- git pack-refs --all &&
- rm -f .git/refs/heads/b &&
- echo "$SHA1 refs/heads/b" >expect &&
- git show-ref b >result &&
- test_cmp expect result'
-
-test_expect_success 'git branch c/d should barf if branch c exists' '
- git branch c &&
- git pack-refs --all &&
- rm -f .git/refs/heads/c &&
- test_must_fail git branch c/d
-'
-
-test_expect_success \
- 'see if a branch still exists after git pack-refs --prune' \
- 'git branch e &&
- git pack-refs --all --prune &&
- echo "$SHA1 refs/heads/e" >expect &&
- git show-ref e >result &&
- test_cmp expect result'
-
-test_expect_success 'see if git pack-refs --prune remove ref files' '
- git branch f &&
- git pack-refs --all --prune &&
- ! test -f .git/refs/heads/f
-'
-
-test_expect_success 'see if git pack-refs --prune removes empty dirs' '
- git branch r/s/t &&
- git pack-refs --all --prune &&
- ! test -e .git/refs/heads/r
-'
-
-test_expect_success \
- 'git branch g should work when git branch g/h has been deleted' \
- 'git branch g/h &&
- git pack-refs --all --prune &&
- git branch -d g/h &&
- git branch g &&
- git pack-refs --all &&
- git branch -d g'
-
-test_expect_success 'git branch i/j/k should barf if branch i exists' '
- git branch i &&
- git pack-refs --all --prune &&
- test_must_fail git branch i/j/k
-'
-
-test_expect_success \
- 'test git branch k after branch k/l/m and k/lm have been deleted' \
- 'git branch k/l &&
- git branch k/lm &&
- git branch -d k/l &&
- git branch k/l/m &&
- git branch -d k/l/m &&
- git branch -d k/lm &&
- git branch k'
-
-test_expect_success \
- 'test git branch n after some branch deletion and pruning' \
- 'git branch n/o &&
- git branch n/op &&
- git branch -d n/o &&
- git branch n/o/p &&
- git branch -d n/op &&
- git pack-refs --all --prune &&
- git branch -d n/o/p &&
- git branch n'
-
-test_expect_success \
- 'see if up-to-date packed refs are preserved' \
- 'git branch q &&
- git pack-refs --all --prune &&
- git update-ref refs/heads/q refs/heads/q &&
- ! test -f .git/refs/heads/q'
-
-test_expect_success 'pack, prune and repack' '
- git tag foo &&
- git pack-refs --all --prune &&
- git show-ref >all-of-them &&
- git pack-refs &&
- git show-ref >again &&
- test_cmp all-of-them again
-'
-
-test_expect_success 'explicit pack-refs with dangling packed reference' '
- git commit --allow-empty -m "soon to be garbage-collected" &&
- git pack-refs --all &&
- git reset --hard HEAD^ &&
- git reflog expire --expire=all --all &&
- git prune --expire=all &&
- git pack-refs --all 2>result &&
- test_must_be_empty result
-'
-
-test_expect_success 'delete ref with dangling packed version' '
- git checkout -b lamb &&
- git commit --allow-empty -m "future garbage" &&
- git pack-refs --all &&
- git reset --hard HEAD^ &&
- git checkout main &&
- git reflog expire --expire=all --all &&
- git prune --expire=all &&
- git branch -d lamb 2>result &&
- test_must_be_empty result
-'
-
-test_expect_success 'delete ref while another dangling packed ref' '
- git branch lamb &&
- git commit --allow-empty -m "future garbage" &&
- git pack-refs --all &&
- git reset --hard HEAD^ &&
- git reflog expire --expire=all --all &&
- git prune --expire=all &&
- git branch -d lamb 2>result &&
- test_must_be_empty result
-'
-
-test_expect_success 'pack ref directly below refs/' '
- git update-ref refs/top HEAD &&
- git pack-refs --all --prune &&
- grep refs/top .git/packed-refs &&
- test_path_is_missing .git/refs/top
-'
-
-test_expect_success 'do not pack ref in refs/bisect' '
- git update-ref refs/bisect/local HEAD &&
- git pack-refs --all --prune &&
- ! grep refs/bisect/local .git/packed-refs >/dev/null &&
- test_path_is_file .git/refs/bisect/local
-'
-
-test_expect_success 'disable reflogs' '
- git config core.logallrefupdates false &&
- rm -rf .git/logs
-'
-
-test_expect_success 'create packed foo/bar/baz branch' '
- git branch foo/bar/baz &&
- git pack-refs --all --prune &&
- test_path_is_missing .git/refs/heads/foo/bar/baz &&
- test_must_fail git reflog exists refs/heads/foo/bar/baz
-'
-
-test_expect_success 'notice d/f conflict with existing directory' '
- test_must_fail git branch foo &&
- test_must_fail git branch foo/bar
-'
-
-test_expect_success 'existing directory reports concrete ref' '
- test_must_fail git branch foo 2>stderr &&
- test_i18ngrep refs/heads/foo/bar/baz stderr
-'
-
-test_expect_success 'notice d/f conflict with existing ref' '
- test_must_fail git branch foo/bar/baz/extra &&
- test_must_fail git branch foo/bar/baz/lots/of/extra/components
-'
-
-test_expect_success 'reject packed-refs with unterminated line' '
- cp .git/packed-refs .git/packed-refs.bak &&
- test_when_finished "mv .git/packed-refs.bak .git/packed-refs" &&
- printf "%s" "$HEAD refs/zzzzz" >>.git/packed-refs &&
- echo "fatal: unterminated line in .git/packed-refs: $HEAD refs/zzzzz" >expected_err &&
- test_must_fail git for-each-ref >out 2>err &&
- test_cmp expected_err err
-'
-
-test_expect_success 'reject packed-refs containing junk' '
- cp .git/packed-refs .git/packed-refs.bak &&
- test_when_finished "mv .git/packed-refs.bak .git/packed-refs" &&
- printf "%s\n" "bogus content" >>.git/packed-refs &&
- echo "fatal: unexpected line in .git/packed-refs: bogus content" >expected_err &&
- test_must_fail git for-each-ref >out 2>err &&
- test_cmp expected_err err
-'
-
-test_expect_success 'reject packed-refs with a short SHA-1' '
- cp .git/packed-refs .git/packed-refs.bak &&
- test_when_finished "mv .git/packed-refs.bak .git/packed-refs" &&
- printf "%.7s %s\n" $HEAD refs/zzzzz >>.git/packed-refs &&
- printf "fatal: unexpected line in .git/packed-refs: %.7s %s\n" $HEAD refs/zzzzz >expected_err &&
- test_must_fail git for-each-ref >out 2>err &&
- test_cmp expected_err err
-'
-
-test_expect_success 'timeout if packed-refs.lock exists' '
- LOCK=.git/packed-refs.lock &&
- >"$LOCK" &&
- test_when_finished "rm -f $LOCK" &&
- test_must_fail git pack-refs --all --prune
-'
-
-test_expect_success 'retry acquiring packed-refs.lock' '
- LOCK=.git/packed-refs.lock &&
- >"$LOCK" &&
- test_when_finished "wait && rm -f $LOCK" &&
- {
- ( sleep 1 && rm -f $LOCK ) &
- } &&
- git -c core.packedrefstimeout=3000 pack-refs --all --prune
-'
-
-test_expect_success SYMLINKS 'pack symlinked packed-refs' '
- # First make sure that symlinking works when reading:
- git update-ref refs/heads/lossy refs/heads/main &&
- git for-each-ref >all-refs-before &&
- mv .git/packed-refs .git/my-deviant-packed-refs &&
- ln -s my-deviant-packed-refs .git/packed-refs &&
- git for-each-ref >all-refs-linked &&
- test_cmp all-refs-before all-refs-linked &&
- git pack-refs --all --prune &&
- git for-each-ref >all-refs-packed &&
- test_cmp all-refs-before all-refs-packed &&
- test -h .git/packed-refs &&
- test "$(test_readlink .git/packed-refs)" = "my-deviant-packed-refs"
-'
-
-test_done
diff --git a/t/t3211-peel-ref.sh b/t/t3211-peel-ref.sh
index 37b9d26..9cbc34f 100755
--- a/t/t3211-peel-ref.sh
+++ b/t/t3211-peel-ref.sh
@@ -4,6 +4,7 @@ test_description='tests for the peel_ref optimization of packed-refs'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'create annotated tag in refs/tags' '
diff --git a/t/t3300-funny-names.sh b/t/t3300-funny-names.sh
index f5bf16a..d3ac826 100755
--- a/t/t3300-funny-names.sh
+++ b/t/t3300-funny-names.sh
@@ -9,6 +9,7 @@ This test tries pathnames with funny characters in the working
tree, index, and tree objects.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
HT=' '
diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh
index d742be8..cf23c06 100755
--- a/t/t3301-notes.sh
+++ b/t/t3301-notes.sh
@@ -362,6 +362,7 @@ test_expect_success 'do not create empty note with -m ""' '
'
test_expect_success 'create note with combination of -m and -F' '
+ test_when_finished git notes remove HEAD &&
cat >expect-combine_m_and_F <<-EOF &&
foo
@@ -380,6 +381,41 @@ test_expect_success 'create note with combination of -m and -F' '
test_cmp expect-combine_m_and_F actual
'
+test_expect_success 'create note with combination of -m and -F and --separator' '
+ test_when_finished git notes remove HEAD &&
+ cat >expect-combine_m_and_F <<-\EOF &&
+ foo
+ -------
+ xyzzy
+ -------
+ bar
+ -------
+ zyxxy
+ -------
+ baz
+ EOF
+ echo "xyzzy" >note_a &&
+ echo "zyxxy" >note_b &&
+ git notes add -m "foo" -F note_a -m "bar" -F note_b -m "baz" --separator="-------" &&
+ git notes show >actual &&
+ test_cmp expect-combine_m_and_F actual
+'
+
+test_expect_success 'create note with combination of -m and -F and --no-separator' '
+ cat >expect-combine_m_and_F <<-\EOF &&
+ foo
+ xyzzy
+ bar
+ zyxxy
+ baz
+ EOF
+ echo "xyzzy" >note_a &&
+ echo "zyxxy" >note_b &&
+ git notes add -m "foo" -F note_a -m "bar" -F note_b -m "baz" --no-separator &&
+ git notes show >actual &&
+ test_cmp expect-combine_m_and_F actual
+'
+
test_expect_success 'remove note with "git notes remove"' '
git notes remove HEAD^ &&
git notes remove &&
@@ -505,6 +541,11 @@ test_expect_success 'list notes with "git notes"' '
test_cmp expect actual
'
+test_expect_success '"git notes" without subcommand does not take arguments' '
+ test_expect_code 129 git notes HEAD^^ 2>err &&
+ grep "^error: unknown subcommand" err
+'
+
test_expect_success 'list specific note with "git notes list <object>"' '
git rev-parse refs/notes/commits:$commit_3 >expect &&
git notes list HEAD^^ >actual &&
@@ -516,6 +557,112 @@ test_expect_success 'listing non-existing notes fails' '
test_must_be_empty actual
'
+test_expect_success 'append: specify a separator with an empty arg' '
+ test_when_finished git notes remove HEAD &&
+ cat >expect <<-\EOF &&
+ notes-1
+
+ notes-2
+ EOF
+
+ git notes add -m "notes-1" &&
+ git notes append --separator="" -m "notes-2" &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'append: specify a separator without arg' '
+ test_when_finished git notes remove HEAD &&
+ cat >expect <<-\EOF &&
+ notes-1
+
+ notes-2
+ EOF
+
+ git notes add -m "notes-1" &&
+ git notes append --separator -m "notes-2" &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'append: specify as --no-separator' '
+ test_when_finished git notes remove HEAD &&
+ cat >expect <<-\EOF &&
+ notes-1
+ notes-2
+ EOF
+
+ git notes add -m "notes-1" &&
+ git notes append --no-separator -m "notes-2" &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'append: specify separator with line break' '
+ test_when_finished git notes remove HEAD &&
+ cat >expect <<-\EOF &&
+ notes-1
+ -------
+ notes-2
+ EOF
+
+ git notes add -m "notes-1" &&
+ git notes append --separator="-------$LF" -m "notes-2" &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'append: specify separator without line break' '
+ test_when_finished git notes remove HEAD &&
+ cat >expect <<-\EOF &&
+ notes-1
+ -------
+ notes-2
+ EOF
+
+ git notes add -m "notes-1" &&
+ git notes append --separator="-------" -m "notes-2" &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'append: specify separator with multiple messages' '
+ test_when_finished git notes remove HEAD &&
+ cat >expect <<-\EOF &&
+ notes-1
+ -------
+ notes-2
+ -------
+ notes-3
+ EOF
+
+ git notes add -m "notes-1" &&
+ git notes append --separator="-------" -m "notes-2" -m "notes-3" &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'append note with combination of -m and -F and --separator' '
+ test_when_finished git notes remove HEAD &&
+ cat >expect-combine_m_and_F <<-\EOF &&
+ m-notes-1
+ -------
+ f-notes-1
+ -------
+ m-notes-2
+ -------
+ f-notes-2
+ -------
+ m-notes-3
+ EOF
+
+ echo "f-notes-1" >note_a &&
+ echo "f-notes-2" >note_b &&
+ git notes append -m "m-notes-1" -F note_a -m "m-notes-2" -F note_b -m "m-notes-3" --separator="-------" &&
+ git notes show >actual &&
+ test_cmp expect-combine_m_and_F actual
+'
+
test_expect_success 'append to existing note with "git notes append"' '
cat >expect <<-EOF &&
Initial set of notes
@@ -813,6 +960,33 @@ test_expect_success 'create note from blob with "git notes add -C" reuses blob i
test_cmp blob actual
'
+test_expect_success 'create note from blob with "-C", also specify "-m", "-F" and "--separator"' '
+ # 8th will be reuseed in following tests, so rollback when the test is done
+ test_when_finished "git notes remove && git notes add -C $(cat blob)" &&
+ commit=$(git rev-parse HEAD) &&
+ cat >expect <<-EOF &&
+ commit $commit
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:20:13 2005 -0700
+
+ ${indent}8th
+
+ Notes:
+ ${indent}This is a blob object
+ ${indent}-------
+ ${indent}This is created by -m
+ ${indent}-------
+ ${indent}This is created by -F
+ EOF
+
+ git notes remove &&
+ echo "This is a blob object" | git hash-object -w --stdin >blob &&
+ echo "This is created by -F" >note_a &&
+ git notes add -C $(cat blob) -m "This is created by -m" -F note_a --separator="-------" &&
+ git log -1 >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'create note from other note with "git notes add -c"' '
test_commit 9th &&
commit=$(git rev-parse HEAD) &&
@@ -1295,9 +1469,9 @@ test_expect_success 'GIT_NOTES_REWRITE_REF overrides config' '
test_expect_success 'git notes copy diagnoses too many or too few arguments' '
test_must_fail git notes copy 2>error &&
- test_i18ngrep "too few arguments" error &&
+ test_grep "too few arguments" error &&
test_must_fail git notes copy one two three 2>error &&
- test_i18ngrep "too many arguments" error
+ test_grep "too many arguments" error
'
test_expect_success 'git notes get-ref expands refs/heads/main to refs/notes/refs/heads/main' '
diff --git a/t/t3302-notes-index-expensive.sh b/t/t3302-notes-index-expensive.sh
index ef8b639..d0c4d38 100755
--- a/t/t3302-notes-index-expensive.sh
+++ b/t/t3302-notes-index-expensive.sh
@@ -8,6 +8,7 @@ test_description='Test commit notes index (expensive!)'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
create_repo () {
@@ -64,7 +65,8 @@ create_repo () {
test_notes () {
count=$1 &&
git config core.notesRef refs/notes/commits &&
- git log | grep "^ " >output &&
+ git log >tmp &&
+ grep "^ " tmp >output &&
i=$count &&
while test $i -gt 0
do
@@ -89,7 +91,7 @@ write_script time_notes <<\EOF
unset GIT_NOTES_REF
;;
esac
- git log
+ git log || exit $?
i=$(($i+1))
done >/dev/null
EOF
diff --git a/t/t3303-notes-subtrees.sh b/t/t3303-notes-subtrees.sh
index d47ce00..bc9b791 100755
--- a/t/t3303-notes-subtrees.sh
+++ b/t/t3303-notes-subtrees.sh
@@ -5,6 +5,7 @@ test_description='Test commit notes organized in subtrees'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
number_of_commits=100
@@ -30,7 +31,7 @@ verify_notes () {
while [ $i -gt 0 ]; do
echo " commit #$i" &&
echo " note for commit #$i" &&
- i=$(($i-1));
+ i=$(($i-1)) || return 1
done > expect &&
test_cmp expect output
}
@@ -42,7 +43,7 @@ test_expect_success "setup: create $number_of_commits commits" '
while [ $nr -lt $number_of_commits ]; do
nr=$(($nr+1)) &&
test_tick &&
- cat <<INPUT_END
+ cat <<INPUT_END || return 1
commit refs/heads/main
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
data <<COMMIT
@@ -78,7 +79,7 @@ test_sha1_based () {
(
start_note_commit &&
nr=$number_of_commits &&
- git rev-list refs/heads/main |
+ git rev-list refs/heads/main >out &&
while read sha1; do
note_path=$(echo "$sha1" | sed "$1")
cat <<INPUT_END &&
@@ -90,9 +91,9 @@ EOF
INPUT_END
nr=$(($nr-1))
- done
- ) |
- git fast-import --quiet
+ done <out
+ ) >gfi &&
+ git fast-import --quiet <gfi
}
test_expect_success 'test notes in 2/38-fanout' 'test_sha1_based "s|^..|&/|"'
@@ -178,7 +179,7 @@ verify_concatenated_notes () {
echo " first note for commit #$i" &&
echo " " &&
echo " second note for commit #$i" &&
- i=$(($i-1));
+ i=$(($i-1)) || return 1
done > expect &&
test_cmp expect output
}
diff --git a/t/t3304-notes-mixed.sh b/t/t3304-notes-mixed.sh
index 03dfcd3..2c3a245 100755
--- a/t/t3304-notes-mixed.sh
+++ b/t/t3304-notes-mixed.sh
@@ -5,6 +5,7 @@ test_description='Test notes trees that also contain non-notes'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
number_of_commits=100
diff --git a/t/t3305-notes-fanout.sh b/t/t3305-notes-fanout.sh
index 94c1b02..1ec1fb6 100755
--- a/t/t3305-notes-fanout.sh
+++ b/t/t3305-notes-fanout.sh
@@ -2,13 +2,14 @@
test_description='Test that adding/removing many notes triggers automatic fanout restructuring'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
path_has_fanout() {
path=$1 &&
fanout=$2 &&
after_last_slash=$(($(test_oid hexsz) - $fanout * 2)) &&
- echo $path | grep -q "^\([0-9a-f]\{2\}/\)\{$fanout\}[0-9a-f]\{$after_last_slash\}$"
+ echo $path | grep -q -E "^([0-9a-f]{2}/){$fanout}[0-9a-f]{$after_last_slash}$"
}
touched_one_note_with_fanout() {
@@ -23,7 +24,7 @@ touched_one_note_with_fanout() {
all_notes_have_fanout() {
notes_commit=$1 &&
fanout=$2 &&
- git ls-tree -r --name-only $notes_commit 2>/dev/null |
+ git ls-tree -r --name-only $notes_commit |
while read path
do
path_has_fanout $path $fanout || return 1
@@ -51,13 +52,14 @@ test_expect_success 'creating many notes with git-notes' '
'
test_expect_success 'many notes created correctly with git-notes' '
- git log | grep "^ " > output &&
+ git log >output.raw &&
+ grep "^ " output.raw >output &&
i=$num_notes &&
while test $i -gt 0
do
echo " commit #$i" &&
echo " note #$i" &&
- i=$(($i - 1));
+ i=$(($i - 1)) || return 1
done > expect &&
test_cmp expect output
'
@@ -90,13 +92,13 @@ test_expect_success 'stable fanout 0 is followed by stable fanout 1' '
test_expect_success 'deleting most notes with git-notes' '
remove_notes=285 &&
i=0 &&
- git rev-list HEAD |
+ git rev-list HEAD >revs &&
while test $i -lt $remove_notes && read sha1
do
i=$(($i + 1)) &&
test_tick &&
- git notes remove "$sha1" 2>/dev/null || return 1
- done
+ git notes remove "$sha1" || return 1
+ done <revs
'
test_expect_success 'most notes deleted correctly with git-notes' '
@@ -106,7 +108,7 @@ test_expect_success 'most notes deleted correctly with git-notes' '
do
echo " commit #$i" &&
echo " note #$i" &&
- i=$(($i - 1));
+ i=$(($i - 1)) || return 1
done > expect &&
test_cmp expect output
'
diff --git a/t/t3307-notes-man.sh b/t/t3307-notes-man.sh
index 1aa366a..ae31650 100755
--- a/t/t3307-notes-man.sh
+++ b/t/t3307-notes-man.sh
@@ -4,6 +4,7 @@ test_description='Examples from the git-notes man page
Make sure the manual is not full of lies.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t3309-notes-merge-auto-resolve.sh b/t/t3309-notes-merge-auto-resolve.sh
index 141d3e4..9bd5dbf 100755
--- a/t/t3309-notes-merge-auto-resolve.sh
+++ b/t/t3309-notes-merge-auto-resolve.sh
@@ -360,7 +360,12 @@ test_expect_success 'merge z into y with invalid strategy => Fail/No changes' '
test_expect_success 'merge z into y with invalid configuration option => Fail/No changes' '
git config core.notesRef refs/notes/y &&
- test_must_fail git -c notes.mergeStrategy="foo" notes merge z &&
+ cat >expect <<-\EOF &&
+ error: unknown notes merge strategy foo
+ fatal: unable to parse '\''notes.mergeStrategy'\'' from command-line config
+ EOF
+ test_must_fail git -c notes.mergeStrategy="foo" notes merge z 2>actual &&
+ test_cmp expect actual &&
# Verify no changes (y)
verify_notes y y
'
diff --git a/t/t3310-notes-merge-manual-resolve.sh b/t/t3310-notes-merge-manual-resolve.sh
index d3d72e2..597df5e 100755
--- a/t/t3310-notes-merge-manual-resolve.sh
+++ b/t/t3310-notes-merge-manual-resolve.sh
@@ -216,7 +216,7 @@ test_expect_success 'merge z into m (== y) with default ("manual") resolver => C
git config core.notesRef refs/notes/m &&
test_must_fail git notes merge z >output 2>&1 &&
# Output should point to where to resolve conflicts
- test_i18ngrep "\\.git/NOTES_MERGE_WORKTREE" output &&
+ test_grep "\\.git/NOTES_MERGE_WORKTREE" output &&
# Inspect merge conflicts
ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
test_cmp expect_conflicts output_conflicts &&
@@ -263,7 +263,7 @@ test_expect_success 'cannot do merge w/conflicts when previous merge is unfinish
test -d .git/NOTES_MERGE_WORKTREE &&
test_must_fail git notes merge z >output 2>&1 &&
# Output should indicate what is wrong
- test_i18ngrep -q "\\.git/NOTES_MERGE_\\* exists" output
+ test_grep -q "\\.git/NOTES_MERGE_\\* exists" output
'
# Setup non-conflicting merge between x and new notes ref w
@@ -417,7 +417,7 @@ test_expect_success 'redo merge of z into m (== y) with default ("manual") resol
git config core.notesRef refs/notes/m &&
test_must_fail git notes merge z >output 2>&1 &&
# Output should point to where to resolve conflicts
- test_i18ngrep "\\.git/NOTES_MERGE_WORKTREE" output &&
+ test_grep "\\.git/NOTES_MERGE_WORKTREE" output &&
# Inspect merge conflicts
ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
test_cmp expect_conflicts output_conflicts &&
@@ -449,7 +449,7 @@ git rev-parse refs/notes/z > pre_merge_z
test_expect_success 'redo merge of z into m (== y) with default ("manual") resolver => Conflicting 3-way merge' '
test_must_fail git notes merge z >output 2>&1 &&
# Output should point to where to resolve conflicts
- test_i18ngrep "\\.git/NOTES_MERGE_WORKTREE" output &&
+ test_grep "\\.git/NOTES_MERGE_WORKTREE" output &&
# Inspect merge conflicts
ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
test_cmp expect_conflicts output_conflicts &&
@@ -528,7 +528,7 @@ test_expect_success 'redo merge of z into m (== y) with default ("manual") resol
git update-ref refs/notes/m refs/notes/y &&
test_must_fail git notes merge z >output 2>&1 &&
# Output should point to where to resolve conflicts
- test_i18ngrep "\\.git/NOTES_MERGE_WORKTREE" output &&
+ test_grep "\\.git/NOTES_MERGE_WORKTREE" output &&
# Inspect merge conflicts
ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
test_cmp expect_conflicts output_conflicts &&
@@ -561,9 +561,9 @@ y and z notes on 4th commit
EOF
# Fail to finalize merge
test_must_fail git notes merge --commit >output 2>&1 &&
- # .git/NOTES_MERGE_* must remain
- test -f .git/NOTES_MERGE_PARTIAL &&
- test -f .git/NOTES_MERGE_REF &&
+ # NOTES_MERGE_* refs and .git/NOTES_MERGE_* state files must remain
+ git rev-parse --verify NOTES_MERGE_PARTIAL &&
+ git rev-parse --verify NOTES_MERGE_REF &&
test -f .git/NOTES_MERGE_WORKTREE/$commit_sha1 &&
test -f .git/NOTES_MERGE_WORKTREE/$commit_sha2 &&
test -f .git/NOTES_MERGE_WORKTREE/$commit_sha3 &&
@@ -573,9 +573,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
- 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 &&
+ test_grep -q "refs/notes/m" output &&
+ test_grep -q "$(git rev-parse refs/notes/m)" output &&
+ test_grep -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/t3320-notes-merge-worktrees.sh b/t/t3320-notes-merge-worktrees.sh
index 6b2d507..0fd3328 100755
--- a/t/t3320-notes-merge-worktrees.sh
+++ b/t/t3320-notes-merge-worktrees.sh
@@ -8,6 +8,7 @@ test_description='Test merging of notes trees in multiple worktrees'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup commit' '
@@ -56,7 +57,7 @@ test_expect_success 'merge z into y while mid-merge in another workdir fails' '
cd worktree &&
git config core.notesRef refs/notes/y &&
test_must_fail git notes merge z 2>err &&
- test_i18ngrep "a notes merge into refs/notes/y is already in-progress at" err
+ test_grep "a notes merge into refs/notes/y is already in-progress at" err
) &&
test_must_fail git -C worktree symbolic-ref NOTES_MERGE_REF
'
@@ -66,7 +67,7 @@ test_expect_success 'merge z into x while mid-merge on y succeeds' '
cd worktree2 &&
git config core.notesRef refs/notes/x &&
test_must_fail git notes merge z >out 2>&1 &&
- test_i18ngrep "Automatic notes merge failed" out &&
+ test_grep "Automatic notes merge failed" out &&
grep -v "A notes merge into refs/notes/x is already in-progress in" out
) &&
echo "refs/notes/x" >expect &&
diff --git a/t/t3321-notes-stripspace.sh b/t/t3321-notes-stripspace.sh
new file mode 100755
index 0000000..beca346
--- /dev/null
+++ b/t/t3321-notes-stripspace.sh
@@ -0,0 +1,578 @@
+#!/bin/sh
+#
+# Copyright (c) 2023 Teng Long
+#
+
+test_description='Test commit notes with stripspace behavior'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+MULTI_LF="$LF$LF$LF"
+write_script fake_editor <<\EOF
+echo "$MSG" >"$1"
+echo "$MSG" >&2
+EOF
+GIT_EDITOR=./fake_editor
+export GIT_EDITOR
+
+test_expect_success 'setup the commit' '
+ test_commit 1st
+'
+
+test_expect_success 'add note by editor' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ first-line
+
+ second-line
+ EOF
+
+ MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes add &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying single "-m", "--stripspace" is the default behavior' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ first-line
+
+ second-line
+ EOF
+
+ git notes add -m "${LF}first-line${MULTI_LF}second-line${LF}" &&
+ git notes show >actual &&
+ test_cmp expect actual &&
+ git notes remove &&
+ git notes add --stripspace -m "${LF}first-line${MULTI_LF}second-line${LF}" &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying single "-m" and "--no-stripspace" ' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ ${LF}first-line${MULTI_LF}second-line
+ EOF
+
+ git notes add --no-stripspace \
+ -m "${LF}first-line${MULTI_LF}second-line${LF}" &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying multiple "-m", "--stripspace" is the default behavior' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ first-line
+
+ second-line
+ EOF
+
+ git notes add -m "${LF}" \
+ -m "first-line" \
+ -m "${MULTI_LF}" \
+ -m "second-line" \
+ -m "${LF}" &&
+ git notes show >actual &&
+ test_cmp expect actual &&
+ git notes remove &&
+ git notes add --stripspace -m "${LF}" \
+ -m "first-line" \
+ -m "${MULTI_LF}" \
+ -m "second-line" \
+ -m "${LF}" &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'add notes by specifying multiple "-m" and "--no-stripspace"' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ ${LF}
+ first-line
+ ${MULTI_LF}
+ second-line${LF}
+ EOF
+
+ git notes add --no-stripspace \
+ -m "${LF}" \
+ -m "first-line" \
+ -m "${MULTI_LF}" \
+ -m "second-line" \
+ -m "${LF}" &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying single "-F", "--stripspace" is the default behavior' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ first-line
+
+ second-line
+ EOF
+
+ cat >note-file <<-EOF &&
+ ${LF}
+ first-line
+ ${MULTI_LF}
+ second-line
+ ${LF}
+ EOF
+
+ git notes add -F note-file &&
+ git notes show >actual &&
+ test_cmp expect actual &&
+ git notes remove &&
+ git notes add --stripspace -F note-file &&
+ git notes show >actual
+'
+
+test_expect_success 'add note by specifying single "-F" and "--no-stripspace"' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ ${LF}
+ first-line
+ ${MULTI_LF}
+ second-line
+ ${LF}
+ EOF
+
+ cat >note-file <<-EOF &&
+ ${LF}
+ first-line
+ ${MULTI_LF}
+ second-line
+ ${LF}
+ EOF
+
+ git notes add --no-stripspace -F note-file &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying multiple "-F", "--stripspace" is the default behavior' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ file-1-first-line
+
+ file-1-second-line
+
+ file-2-first-line
+
+ file-2-second-line
+ EOF
+
+ cat >note-file-1 <<-EOF &&
+ ${LF}
+ file-1-first-line
+ ${MULTI_LF}
+ file-1-second-line
+ ${LF}
+ EOF
+
+ cat >note-file-2 <<-EOF &&
+ ${LF}
+ file-2-first-line
+ ${MULTI_LF}
+ file-2-second-line
+ ${LF}
+ EOF
+
+ git notes add -F note-file-1 -F note-file-2 &&
+ git notes show >actual &&
+ test_cmp expect actual &&
+ git notes remove &&
+ git notes add --stripspace -F note-file-1 -F note-file-2 &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying multiple "-F" with "--no-stripspace"' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ ${LF}
+ file-1-first-line
+ ${MULTI_LF}
+ file-1-second-line
+ ${LF}
+
+ ${LF}
+ file-2-first-line
+ ${MULTI_LF}
+ file-2-second-line
+ ${LF}
+ EOF
+
+ cat >note-file-1 <<-EOF &&
+ ${LF}
+ file-1-first-line
+ ${MULTI_LF}
+ file-1-second-line
+ ${LF}
+ EOF
+
+ cat >note-file-2 <<-EOF &&
+ ${LF}
+ file-2-first-line
+ ${MULTI_LF}
+ file-2-second-line
+ ${LF}
+ EOF
+
+ git notes add --no-stripspace -F note-file-1 -F note-file-2 &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'append note by editor' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ first-line
+
+ second-line
+ EOF
+
+ git notes add -m "first-line" &&
+ MSG="${MULTI_LF}second-line${LF}" git notes append &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'append note by specifying single "-m"' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ first-line
+
+ second-line
+ EOF
+
+ git notes add -m "${LF}first-line" &&
+ git notes append -m "${MULTI_LF}second-line${LF}" &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'append note by specifying multiple "-m"' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ first-line
+
+ second-line
+ EOF
+
+ git notes add -m "${LF}first-line" &&
+ git notes append -m "${MULTI_LF}" \
+ -m "second-line" \
+ -m "${LF}" &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying single "-F"' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ first-line
+
+ second-line
+ EOF
+
+ cat >note-file <<-EOF &&
+ ${LF}
+ first-line
+ ${MULTI_LF}
+ second-line
+ ${LF}
+ EOF
+
+ git notes add -F note-file &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'add notes by specifying multiple "-F"' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ file-1-first-line
+
+ file-1-second-line
+
+ file-2-first-line
+
+ file-2-second-line
+ EOF
+
+ cat >note-file-1 <<-EOF &&
+ ${LF}
+ file-1-first-line
+ ${MULTI_LF}
+ file-1-second-line
+ ${LF}
+ EOF
+
+ cat >note-file-2 <<-EOF &&
+ ${LF}
+ file-2-first-line
+ ${MULTI_LF}
+ file-2-second-line
+ ${LF}
+ EOF
+
+ git notes add -F note-file-1 -F note-file-2 &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'append note by specifying single "-F"' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ initial-line
+
+ first-line
+
+ second-line
+ EOF
+
+ cat >note-file <<-EOF &&
+ ${LF}
+ first-line
+ ${MULTI_LF}
+ second-line
+ ${LF}
+ EOF
+
+ git notes add -m "initial-line" &&
+ git notes append -F note-file &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'append notes by specifying multiple "-F"' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ initial-line
+
+ file-1-first-line
+
+ file-1-second-line
+
+ file-2-first-line
+
+ file-2-second-line
+ EOF
+
+ cat >note-file-1 <<-EOF &&
+ ${LF}
+ file-1-first-line
+ ${MULTI_LF}
+ file-1-second-line
+ ${LF}
+ EOF
+
+ cat >note-file-2 <<-EOF &&
+ ${LF}
+ file-2-first-line
+ ${MULTI_LF}
+ file-2-second-line
+ ${LF}
+ EOF
+
+ git notes add -m "initial-line" &&
+ git notes append -F note-file-1 -F note-file-2 &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'append note by specifying multiple "-F" with "--no-stripspace"' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ initial-line
+ ${LF}${LF}
+ file-1-first-line
+ ${MULTI_LF}
+ file-1-second-line
+ ${LF}
+
+ ${LF}
+ file-2-first-line
+ ${MULTI_LF}
+ file-2-second-line
+ ${LF}
+ EOF
+
+ cat >note-file-1 <<-EOF &&
+ ${LF}
+ file-1-first-line
+ ${MULTI_LF}
+ file-1-second-line
+ ${LF}
+ EOF
+
+ cat >note-file-2 <<-EOF &&
+ ${LF}
+ file-2-first-line
+ ${MULTI_LF}
+ file-2-second-line
+ ${LF}
+ EOF
+
+ git notes add -m "initial-line" &&
+ git notes append --no-stripspace -F note-file-1 -F note-file-2 &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'add notes with empty messages' '
+ rev=$(git rev-parse HEAD) &&
+ git notes add -m "${LF}" \
+ -m "${MULTI_LF}" \
+ -m "${LF}" >actual 2>&1 &&
+ test_grep "Removing note for object" actual
+'
+
+test_expect_success 'add note by specifying "-C", "--no-stripspace" is the default behavior' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ ${LF}
+ first-line
+ ${MULTI_LF}
+ second-line
+ ${LF}
+ EOF
+
+ git hash-object -w --stdin <expect >blob &&
+ git notes add -C $(cat blob) &&
+ git notes show >actual &&
+ test_cmp expect actual &&
+ git notes remove &&
+ git notes add --no-stripspace -C $(cat blob) &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'reuse note by specifying "-C" and "--stripspace"' '
+ test_when_finished "git notes remove" &&
+ cat >data <<-EOF &&
+ ${LF}
+ first-line
+ ${MULTI_LF}
+ second-line
+ ${LF}
+ EOF
+
+ cat >expect <<-EOF &&
+ first-line
+
+ second-line
+ EOF
+
+ git hash-object -w --stdin <data >blob &&
+ git notes add --stripspace -C $(cat blob) &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'reuse with "-C" and add note with "-m", "-m" will stripspace all together' '
+ test_when_finished "git notes remove" &&
+ cat >data <<-EOF &&
+ ${LF}
+ first-line
+ ${MULTI_LF}
+ second-line
+ ${LF}
+ EOF
+
+ cat >expect <<-EOF &&
+ first-line
+
+ second-line
+
+ third-line
+ EOF
+
+ git hash-object -w --stdin <data >blob &&
+ git notes add -C $(cat blob) -m "third-line" &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'add note with "-m" and reuse note with "-C", "-C" will not stripspace all together' '
+ test_when_finished "git notes remove" &&
+ cat >data <<-EOF &&
+
+ second-line
+ EOF
+
+ cat >expect <<-EOF &&
+ first-line
+ ${LF}
+ second-line
+ EOF
+
+ git hash-object -w --stdin <data >blob &&
+ git notes add -m "first-line" -C $(cat blob) &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying "-c", "--stripspace" is the default behavior' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ first-line
+
+ second-line
+ EOF
+
+ echo "initial-line" | git hash-object -w --stdin >blob &&
+ MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes add -c $(cat blob) &&
+ git notes show >actual &&
+ test_cmp expect actual &&
+ git notes remove &&
+ MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes add --stripspace -c $(cat blob) &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying "-c" with "--no-stripspace"' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ ${LF}first-line${MULTI_LF}second-line${LF}
+ EOF
+
+ echo "initial-line" | git hash-object -w --stdin >blob &&
+ MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes add --no-stripspace -c $(cat blob) &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'edit note by specifying "-c", "--stripspace" is the default behavior' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ first-line
+
+ second-line
+ EOF
+
+ MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes edit &&
+ git notes show >actual &&
+ test_cmp expect actual &&
+ git notes remove &&
+ MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes edit --stripspace &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'edit note by specifying "-c" with "--no-stripspace"' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ ${LF}first-line${MULTI_LF}second-line${LF}
+ EOF
+
+ MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes add --no-stripspace &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 23dbd3c..e1c8c5f 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -18,10 +18,7 @@ GIT_AUTHOR_EMAIL=bogus@email@address
export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
test_expect_success 'prepare repository with topic branches' '
- git config core.logAllRefUpdates true &&
- echo First >A &&
- git update-index --add A &&
- git commit -m "Add A." &&
+ test_commit "Add A." A First First &&
git checkout -b force-3way &&
echo Dummy >Y &&
git update-index --add Y &&
@@ -32,9 +29,7 @@ test_expect_success 'prepare repository with topic branches' '
git mv A D/A &&
git commit -m "Move A." &&
git checkout -b my-topic-branch main &&
- echo Second >B &&
- git update-index --add B &&
- git commit -m "Add B." &&
+ test_commit "Add B." B Second Second &&
git checkout -f main &&
echo Third >>A &&
git update-index A &&
@@ -148,8 +143,8 @@ test_expect_success 'Show verbose error when HEAD could not be detached' '
>B &&
test_when_finished "rm -f B" &&
test_must_fail git rebase topic 2>output.err >output.out &&
- test_i18ngrep "The following untracked working tree files would be overwritten by checkout:" output.err &&
- test_i18ngrep B output.err
+ test_grep "The following untracked working tree files would be overwritten by checkout:" output.err &&
+ test_grep B output.err
'
test_expect_success 'fail when upstream arg is missing and not on branch' '
@@ -393,27 +388,61 @@ test_expect_success 'switch to branch checked out here' '
git rebase main main
'
+test_expect_success 'switch to branch checked out elsewhere fails' '
+ test_when_finished "
+ git worktree remove wt1 &&
+ git worktree remove wt2 &&
+ git branch -d shared
+ " &&
+ git worktree add wt1 -b shared &&
+ git worktree add wt2 -f shared &&
+ # we test in both worktrees to ensure that works
+ # as expected with "first" and "next" worktrees
+ test_must_fail git -C wt1 rebase shared shared &&
+ test_must_fail git -C wt2 rebase shared shared
+'
+
test_expect_success 'switch to branch not checked out' '
git checkout main &&
git branch other &&
git rebase main other
'
+test_expect_success 'switch to non-branch detaches HEAD' '
+ git checkout main &&
+ old_main=$(git rev-parse HEAD) &&
+ git rebase First Second^0 &&
+ test_cmp_rev HEAD Second &&
+ test_cmp_rev main $old_main &&
+ test_must_fail git symbolic-ref HEAD
+'
+
test_expect_success 'refuse to switch to branch checked out elsewhere' '
git checkout main &&
git worktree add wt &&
test_must_fail git -C wt rebase main main 2>err &&
- test_i18ngrep "already checked out" err
+ test_grep "already used by worktree at" err
'
-test_expect_success MINGW,SYMLINKS_WINDOWS 'rebase when .git/logs is a symlink' '
- git checkout main &&
- mv .git/logs actual_logs &&
- cmd //c "mklink /D .git\logs ..\actual_logs" &&
- git rebase -f HEAD^ &&
- test -L .git/logs &&
- rm .git/logs &&
- mv actual_logs .git/logs
+test_expect_success 'rebase when inside worktree subdirectory' '
+ git init main-wt &&
+ (
+ cd main-wt &&
+ git commit --allow-empty -m "initial" &&
+ mkdir -p foo/bar &&
+ test_commit foo/bar/baz &&
+ mkdir -p a/b &&
+ test_commit a/b/c &&
+ # create another branch for our other worktree
+ git branch other &&
+ git worktree add ../other-wt other &&
+ cd ../other-wt &&
+ # create and cd into a subdirectory
+ mkdir -p random/dir &&
+ cd random/dir &&
+ # now do the rebase
+ git rebase --onto HEAD^^ HEAD^ # drops the HEAD^ commit
+ )
'
test_done
diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh
index cfde68f..5c67d07 100755
--- a/t/t3402-rebase-merge.sh
+++ b/t/t3402-rebase-merge.sh
@@ -8,6 +8,7 @@ test_description='git rebase --merge test'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
T="A quick brown fox
@@ -68,7 +69,7 @@ test_expect_success 'merge and rebase should match' '
if test -s difference
then
cat difference
- (exit 1)
+ false
else
echo happy
fi
@@ -102,7 +103,7 @@ test_expect_success 'merge and rebase should match' '
if test -s difference
then
cat difference
- (exit 1)
+ false
else
echo happy
fi
@@ -117,7 +118,7 @@ test_expect_success 'picking rebase' '
echo happy
else
git show-branch
- (exit 1)
+ false
fi &&
f=$(git diff-tree --name-only HEAD^ HEAD) &&
g=$(git diff-tree --name-only HEAD^^ HEAD^) &&
@@ -127,31 +128,10 @@ test_expect_success 'picking rebase' '
*)
echo "$f"
echo "$g"
- (exit 1)
+ false
esac
'
-test_expect_success 'rebase -s funny -Xopt' '
- test_when_finished "rm -fr test-bin funny.was.run" &&
- mkdir test-bin &&
- cat >test-bin/git-merge-funny <<-EOF &&
- #!$SHELL_PATH
- case "\$1" in --opt) ;; *) exit 2 ;; esac
- shift &&
- >funny.was.run &&
- exec git merge-recursive "\$@"
- EOF
- chmod +x test-bin/git-merge-funny &&
- git reset --hard &&
- git checkout -b test-funny main^ &&
- test_commit funny &&
- (
- PATH=./test-bin:$PATH &&
- git rebase -s funny -Xopt main
- ) &&
- test -f funny.was.run
-'
-
test_expect_success 'rebase --skip works with two conflicts in a row' '
git checkout second-side &&
tr "[A-Z]" "[a-z]" <newfile >tmp &&
@@ -191,7 +171,7 @@ test_expect_success '--reapply-cherry-picks' '
# Regular rebase fails, because the 1-11 commit is deduplicated
test_must_fail git -C repo rebase --merge main 2> err &&
- test_i18ngrep "error: could not apply.*add 12 in another branch" err &&
+ test_grep "error: could not apply.*add 12 in another branch" err &&
git -C repo rebase --abort &&
# With --reapply-cherry-picks, it works
diff --git a/t/t3403-rebase-skip.sh b/t/t3403-rebase-skip.sh
index f6e4864..a1911c4 100755
--- a/t/t3403-rebase-skip.sh
+++ b/t/t3403-rebase-skip.sh
@@ -108,10 +108,10 @@ test_expect_success 'correct advice upon picking empty commit' '
test_when_finished "git rebase --abort" &&
test_must_fail git rebase -i --onto goodbye \
amended-goodbye^ amended-goodbye 2>err &&
- test_i18ngrep "previous cherry-pick is now empty" err &&
- test_i18ngrep "git rebase --skip" err &&
+ test_grep "previous cherry-pick is now empty" err &&
+ test_grep "git rebase --skip" err &&
test_must_fail git commit &&
- test_i18ngrep "git rebase --skip" err
+ test_grep "git rebase --skip" err
'
test_expect_success 'correct authorship when committing empty pick' '
@@ -131,10 +131,10 @@ test_expect_success 'correct advice upon rewording empty commit' '
test_must_fail env FAKE_LINES="reword 1" git rebase -i \
--onto goodbye amended-goodbye^ amended-goodbye 2>err
) &&
- test_i18ngrep "previous cherry-pick is now empty" err &&
- test_i18ngrep "git rebase --skip" err &&
+ test_grep "previous cherry-pick is now empty" err &&
+ test_grep "git rebase --skip" err &&
test_must_fail git commit &&
- test_i18ngrep "git rebase --skip" err
+ test_grep "git rebase --skip" err
'
test_expect_success 'correct advice upon editing empty commit' '
@@ -144,10 +144,10 @@ test_expect_success 'correct advice upon editing empty commit' '
test_must_fail env FAKE_LINES="edit 1" git rebase -i \
--onto goodbye amended-goodbye^ amended-goodbye 2>err
) &&
- test_i18ngrep "previous cherry-pick is now empty" err &&
- test_i18ngrep "git rebase --skip" err &&
+ test_grep "previous cherry-pick is now empty" err &&
+ test_grep "git rebase --skip" err &&
test_must_fail git commit &&
- test_i18ngrep "git rebase --skip" err
+ test_grep "git rebase --skip" err
'
test_expect_success 'correct advice upon cherry-picking an empty commit during a rebase' '
@@ -157,10 +157,10 @@ test_expect_success 'correct advice upon cherry-picking an empty commit during a
test_must_fail env FAKE_LINES="1 exec_git_cherry-pick_amended-goodbye" \
git rebase -i goodbye^ goodbye 2>err
) &&
- test_i18ngrep "previous cherry-pick is now empty" err &&
- test_i18ngrep "git cherry-pick --skip" err &&
+ test_grep "previous cherry-pick is now empty" err &&
+ test_grep "git cherry-pick --skip" err &&
test_must_fail git commit 2>err &&
- test_i18ngrep "git cherry-pick --skip" err
+ test_grep "git cherry-pick --skip" err
'
test_expect_success 'correct advice upon multi cherry-pick picking an empty commit during a rebase' '
@@ -170,10 +170,10 @@ test_expect_success 'correct advice upon multi cherry-pick picking an empty comm
test_must_fail env FAKE_LINES="1 exec_git_cherry-pick_goodbye_amended-goodbye" \
git rebase -i goodbye^^ goodbye 2>err
) &&
- test_i18ngrep "previous cherry-pick is now empty" err &&
- test_i18ngrep "git cherry-pick --skip" err &&
+ test_grep "previous cherry-pick is now empty" err &&
+ test_grep "git cherry-pick --skip" err &&
test_must_fail git commit 2>err &&
- test_i18ngrep "git cherry-pick --skip" err
+ test_grep "git cherry-pick --skip" err
'
test_expect_success 'fixup that empties commit fails' '
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index d877872..d1bead6 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -25,8 +25,6 @@ Initial setup:
where A, B, D and G all touch file1, and one, two, three, four all
touch file "conflict".
'
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
@@ -155,6 +153,18 @@ test_expect_success 'rebase -i with the exec command checks tree cleanness' '
git rebase --continue
'
+test_expect_success 'cherry-pick works with rebase --exec' '
+ test_when_finished "git cherry-pick --abort; \
+ git rebase --abort; \
+ git checkout primary" &&
+ echo "exec git cherry-pick G" >todo &&
+ (
+ set_replace_editor todo &&
+ test_must_fail git rebase -i D D
+ ) &&
+ test_cmp_rev G CHERRY_PICK_HEAD
+'
+
test_expect_success 'rebase -x with empty command fails' '
test_when_finished "git rebase --abort ||:" &&
test_must_fail env git rebase -x "" @ 2>actual &&
@@ -293,10 +303,11 @@ test_expect_success 'abort with error when new base cannot be checked out' '
git rm --cached file1 &&
git commit -m "remove file in base" &&
test_must_fail git rebase -i primary > output 2>&1 &&
- test_i18ngrep "The following untracked working tree files would be overwritten by checkout:" \
+ test_grep "The following untracked working tree files would be overwritten by checkout:" \
output &&
- test_i18ngrep "file1" output &&
+ test_grep "file1" output &&
test_path_is_missing .git/rebase-merge &&
+ rm file1 &&
git reset --hard HEAD^
'
@@ -351,82 +362,6 @@ test_expect_success 'retain authorship when squashing' '
git show HEAD | grep "^Author: Twerp Snog"
'
-test_expect_success REBASE_P '-p handles "no changes" gracefully' '
- HEAD=$(git rev-parse HEAD) &&
- git rebase -i -p HEAD^ &&
- git update-index --refresh &&
- git diff-files --quiet &&
- git diff-index --quiet --cached HEAD -- &&
- test $HEAD = $(git rev-parse HEAD)
-'
-
-test_expect_failure REBASE_P 'exchange two commits with -p' '
- git checkout H &&
- (
- set_fake_editor &&
- FAKE_LINES="2 1" git rebase -i -p HEAD~2
- ) &&
- test H = $(git cat-file commit HEAD^ | sed -ne \$p) &&
- test G = $(git cat-file commit HEAD | sed -ne \$p)
-'
-
-test_expect_success REBASE_P 'preserve merges with -p' '
- git checkout -b to-be-preserved primary^ &&
- : > unrelated-file &&
- git add unrelated-file &&
- test_tick &&
- git commit -m "unrelated" &&
- git checkout -b another-branch primary &&
- echo B > file1 &&
- test_tick &&
- git commit -m J file1 &&
- test_tick &&
- git merge to-be-preserved &&
- echo C > file1 &&
- test_tick &&
- git commit -m K file1 &&
- echo D > file1 &&
- test_tick &&
- git commit -m L1 file1 &&
- git checkout HEAD^ &&
- echo 1 > unrelated-file &&
- test_tick &&
- git commit -m L2 unrelated-file &&
- test_tick &&
- git merge another-branch &&
- echo E > file1 &&
- test_tick &&
- git commit -m M file1 &&
- git checkout -b to-be-rebased &&
- test_tick &&
- git rebase -i -p --onto branch1 primary &&
- git update-index --refresh &&
- git diff-files --quiet &&
- git diff-index --quiet --cached HEAD -- &&
- test_cmp_rev HEAD~6 branch1 &&
- test_cmp_rev HEAD~4^2 to-be-preserved &&
- test_cmp_rev HEAD^^2^ HEAD^^^ &&
- test $(git show HEAD~5:file1) = B &&
- test $(git show HEAD~3:file1) = C &&
- test $(git show HEAD:file1) = E &&
- test $(git show HEAD:unrelated-file) = 1
-'
-
-test_expect_success REBASE_P 'edit ancestor with -p' '
- (
- set_fake_editor &&
- FAKE_LINES="1 2 edit 3 4" git rebase -i -p HEAD~3
- ) &&
- echo 2 > unrelated-file &&
- test_tick &&
- git commit -m L2-modified --amend unrelated-file &&
- git rebase --continue &&
- git update-index --refresh &&
- git diff-files --quiet &&
- git diff-index --quiet --cached HEAD -- &&
- test $(git show HEAD:unrelated-file) = 2
-'
-
test_expect_success '--continue tries to commit' '
git reset --hard D &&
test_tick &&
@@ -681,7 +616,8 @@ test_expect_success 'clean error after failed "exec"' '
echo "edited again" > file7 &&
git add file7 &&
test_must_fail git rebase --continue 2>error &&
- test_i18ngrep "you have staged changes in your working tree" error
+ test_grep "you have staged changes in your working tree" error &&
+ test_grep ! "could not open.*for reading" error
'
test_expect_success 'rebase a detached HEAD' '
@@ -696,9 +632,7 @@ test_expect_success 'rebase a detached HEAD' '
'
test_expect_success 'rebase a commit violating pre-commit' '
-
- mkdir -p .git/hooks &&
- write_script .git/hooks/pre-commit <<-\EOF &&
+ test_hook pre-commit <<-\EOF &&
test -z "$(git diff --cached --check)"
EOF
echo "monde! " >> file1 &&
@@ -713,8 +647,6 @@ test_expect_success 'rebase a commit violating pre-commit' '
'
test_expect_success 'rebase with a file named HEAD in worktree' '
-
- rm -fr .git/hooks &&
git reset --hard &&
git checkout -b branch3 A &&
@@ -839,7 +771,7 @@ test_expect_success 'reword' '
git show HEAD~2 | grep "C changed"
'
-test_expect_success 'no uncommited changes when rewording the todo list is reloaded' '
+test_expect_success 'no uncommitted changes when rewording and the todo list is reloaded' '
git checkout E &&
test_when_finished "git checkout @{-1}" &&
(
@@ -901,7 +833,7 @@ test_expect_success 'always cherry-pick with --no-ff' '
do
test ! $(git rev-parse HEAD~$p) = $(git rev-parse original-no-ff-branch~$p) &&
git diff HEAD~$p original-no-ff-branch~$p > out &&
- test_must_be_empty out
+ test_must_be_empty out || return 1
done &&
test_cmp_rev HEAD~3 original-no-ff-branch~3 &&
git diff HEAD~3 original-no-ff-branch~3 > out &&
@@ -1036,7 +968,7 @@ test_expect_success 'rebase --exec works without -i ' '
git reset --hard execute &&
rm -rf exec_output &&
EDITOR="echo >invoked_editor" git rebase --exec "echo a line >>exec_output" HEAD~2 2>actual &&
- test_i18ngrep "Successfully rebased and updated" actual &&
+ test_grep "Successfully rebased and updated" actual &&
test_line_count = 2 exec_output &&
test_path_is_missing invoked_editor
'
@@ -1044,7 +976,7 @@ test_expect_success 'rebase --exec works without -i ' '
test_expect_success 'rebase -i --exec without <CMD>' '
git reset --hard execute &&
test_must_fail git rebase -i --exec 2>actual &&
- test_i18ngrep "requires a value" actual &&
+ test_grep "requires a value" actual &&
git checkout primary
'
@@ -1325,9 +1257,9 @@ test_expect_success 'short commit ID collide' '
test $colliding_id = "$(git rev-parse HEAD | cut -c 1-4)" &&
grep "^pick $colliding_id " \
.git/rebase-merge/git-rebase-todo.tmp &&
- grep "^pick [0-9a-f]\{$hexsz\}" \
+ grep -E "^pick [0-9a-f]{$hexsz}" \
.git/rebase-merge/git-rebase-todo &&
- grep "^pick [0-9a-f]\{$hexsz\}" \
+ grep -E "^pick [0-9a-f]{$hexsz}" \
.git/rebase-merge/git-rebase-todo.backup &&
git rebase --continue
) &&
@@ -1342,7 +1274,7 @@ test_expect_success 'respect core.abbrev' '
set_cat_todo_editor &&
test_must_fail git rebase -i HEAD~4 >todo-list
) &&
- test 4 = $(grep -c "pick [0-9a-f]\{12,\}" todo-list)
+ test 4 = $(grep -c -E "pick [0-9a-f]{12,}" todo-list)
'
test_expect_success 'todo count' '
@@ -1353,24 +1285,38 @@ test_expect_success 'todo count' '
test_set_editor "$(pwd)/dump-raw.sh" &&
git rebase -i HEAD~4 >actual
) &&
- test_i18ngrep "^# Rebase ..* onto ..* ([0-9]" actual
+ test_grep "^# Rebase ..* onto ..* ([0-9]" actual
'
test_expect_success 'rebase -i commits that overwrite untracked files (pick)' '
- git checkout --force branch2 &&
+ git checkout --force A &&
git clean -f &&
+ cat >todo <<-EOF &&
+ exec >file2
+ pick $(git rev-parse B) B
+ pick $(git rev-parse C) C
+ pick $(git rev-parse D) D
+ exec cat .git/rebase-merge/done >actual
+ EOF
(
- set_fake_editor &&
- FAKE_LINES="edit 1 2" git rebase -i A
+ set_replace_editor todo &&
+ test_must_fail git rebase -i A
) &&
- test_cmp_rev HEAD F &&
- test_path_is_missing file6 &&
- >file6 &&
- test_must_fail git rebase --continue &&
- test_cmp_rev HEAD F &&
- rm file6 &&
+ test_cmp_rev HEAD B &&
+ test_cmp_rev REBASE_HEAD C &&
+ head -n3 todo >expect &&
+ test_cmp expect .git/rebase-merge/done &&
+ rm file2 &&
+ test_path_is_missing .git/rebase-merge/patch &&
+ echo changed >file1 &&
+ git add file1 &&
+ test_must_fail git rebase --continue 2>err &&
+ grep "error: you have staged changes in your working tree" err &&
+ git reset --hard HEAD &&
git rebase --continue &&
- test_cmp_rev HEAD I
+ test_cmp_rev HEAD D &&
+ tail -n3 todo >>expect &&
+ test_cmp expect actual
'
test_expect_success 'rebase -i commits that overwrite untracked files (squash)' '
@@ -1386,7 +1332,14 @@ test_expect_success 'rebase -i commits that overwrite untracked files (squash)'
>file6 &&
test_must_fail git rebase --continue &&
test_cmp_rev HEAD F &&
+ test_cmp_rev REBASE_HEAD I &&
rm file6 &&
+ test_path_is_missing .git/rebase-merge/patch &&
+ echo changed >file1 &&
+ git add file1 &&
+ test_must_fail git rebase --continue 2>err &&
+ grep "error: you have staged changes in your working tree" err &&
+ git reset --hard HEAD &&
git rebase --continue &&
test $(git cat-file commit HEAD | sed -ne \$p) = I &&
git reset --hard original-branch2
@@ -1404,7 +1357,14 @@ test_expect_success 'rebase -i commits that overwrite untracked files (no ff)' '
>file6 &&
test_must_fail git rebase --continue &&
test $(git cat-file commit HEAD | sed -ne \$p) = F &&
+ test_cmp_rev REBASE_HEAD I &&
rm file6 &&
+ test_path_is_missing .git/rebase-merge/patch &&
+ echo changed >file1 &&
+ git add file1 &&
+ test_must_fail git rebase --continue 2>err &&
+ grep "error: you have staged changes in your working tree" err &&
+ git reset --hard HEAD &&
git rebase --continue &&
test $(git cat-file commit HEAD | sed -ne \$p) = I
'
@@ -1416,7 +1376,7 @@ test_expect_success 'rebase --continue removes CHERRY_PICK_HEAD' '
test_seq 5 | sed "s/$double/&&/" >seq &&
git add seq &&
test_tick &&
- git commit -m seq-$double
+ git commit -m seq-$double || return 1
done &&
git tag seq-onto &&
git reset --hard HEAD~2 &&
@@ -1460,7 +1420,7 @@ test_expect_success 'rebase -i respects rebase.missingCommitsCheck = ignore' '
FAKE_LINES="1 2 3 4" git rebase -i --root 2>actual
) &&
test D = $(git cat-file commit HEAD | sed -ne \$p) &&
- test_i18ngrep \
+ test_grep \
"Successfully rebased and updated refs/heads/missing-commit" \
actual
'
@@ -1523,21 +1483,22 @@ test_expect_success 'rebase --edit-todo respects rebase.missingCommitsCheck = ig
git rebase --continue 2>actual
) &&
test D = $(git cat-file commit HEAD | sed -ne \$p) &&
- test_i18ngrep \
+ test_grep \
"Successfully rebased and updated refs/heads/missing-commit" \
actual
'
test_expect_success 'rebase --edit-todo respects rebase.missingCommitsCheck = warn' '
cat >expect <<-EOF &&
- error: invalid line 1: badcmd $(git rev-list --pretty=oneline --abbrev-commit -1 primary~4)
+ error: invalid command '\''pickled'\''
+ error: invalid line 1: pickled $(git rev-list --pretty=oneline --abbrev-commit -1 primary~4)
Warning: some commits may have been dropped accidentally.
Dropped commits (newer to older):
- $(git rev-list --pretty=oneline --abbrev-commit -1 primary)
- $(git rev-list --pretty=oneline --abbrev-commit -1 primary~4)
To avoid this message, use "drop" to explicitly remove a commit.
EOF
- head -n4 expect >expect.2 &&
+ head -n5 expect >expect.2 &&
tail -n1 expect >>expect.2 &&
tail -n4 expect.2 >expect.3 &&
test_config rebase.missingCommitsCheck warn &&
@@ -1548,7 +1509,7 @@ test_expect_success 'rebase --edit-todo respects rebase.missingCommitsCheck = wa
git rebase -i --root &&
cp .git/rebase-merge/git-rebase-todo.backup orig &&
FAKE_LINES="2 3 4" git rebase --edit-todo 2>actual.2 &&
- head -n6 actual.2 >actual &&
+ head -n7 actual.2 >actual &&
test_cmp expect actual &&
cp orig .git/rebase-merge/git-rebase-todo &&
FAKE_LINES="1 2 3 4" git rebase --edit-todo 2>actual.2 &&
@@ -1557,14 +1518,15 @@ test_expect_success 'rebase --edit-todo respects rebase.missingCommitsCheck = wa
git rebase --continue 2>actual
) &&
test D = $(git cat-file commit HEAD | sed -ne \$p) &&
- test_i18ngrep \
+ test_grep \
"Successfully rebased and updated refs/heads/missing-commit" \
actual
'
test_expect_success 'rebase --edit-todo respects rebase.missingCommitsCheck = error' '
cat >expect <<-EOF &&
- error: invalid line 1: badcmd $(git rev-list --pretty=oneline --abbrev-commit -1 primary~4)
+ error: invalid command '\''pickled'\''
+ error: invalid line 1: pickled $(git rev-list --pretty=oneline --abbrev-commit -1 primary~4)
Warning: some commits may have been dropped accidentally.
Dropped commits (newer to older):
- $(git rev-list --pretty=oneline --abbrev-commit -1 primary)
@@ -1604,7 +1566,7 @@ test_expect_success 'rebase --edit-todo respects rebase.missingCommitsCheck = er
git rebase --continue 2>actual
) &&
test D = $(git cat-file commit HEAD | sed -ne \$p) &&
- test_i18ngrep \
+ test_grep \
"Successfully rebased and updated refs/heads/missing-commit" \
actual
'
@@ -1664,9 +1626,9 @@ test_expect_success 'static check of bad command' '
set_fake_editor &&
test_must_fail env FAKE_LINES="1 2 3 bad 4 5" \
git rebase -i --root 2>actual &&
- test_i18ngrep "badcmd $(git rev-list --oneline -1 primary~1)" \
+ test_grep "pickled $(git rev-list --oneline -1 primary~1)" \
actual &&
- test_i18ngrep "You can fix this with .git rebase --edit-todo.." \
+ test_grep "You can fix this with .git rebase --edit-todo.." \
actual &&
FAKE_LINES="1 2 3 drop 4 5" git rebase --edit-todo
) &&
@@ -1675,6 +1637,32 @@ test_expect_success 'static check of bad command' '
test C = $(git cat-file commit HEAD^ | sed -ne \$p)
'
+test_expect_success 'the first command cannot be a fixup' '
+ rebase_setup_and_clean fixup-first &&
+
+ cat >orig <<-EOF &&
+ fixup $(git log -1 --format="%h %s" B)
+ pick $(git log -1 --format="%h %s" C)
+ EOF
+
+ (
+ set_replace_editor orig &&
+ test_must_fail git rebase -i A 2>actual
+ ) &&
+ grep "cannot .fixup. without a previous commit" actual &&
+ grep "You can fix this with .git rebase --edit-todo.." actual &&
+ # verify that the todo list has not been truncated
+ grep -v "^#" .git/rebase-merge/git-rebase-todo >actual &&
+ test_cmp orig actual &&
+
+ test_must_fail git rebase --edit-todo 2>actual &&
+ grep "cannot .fixup. without a previous commit" actual &&
+ grep "You can fix this with .git rebase --edit-todo.." actual &&
+ # verify that the todo list has not been truncated
+ grep -v "^#" .git/rebase-merge/git-rebase-todo >actual &&
+ test_cmp orig actual
+'
+
test_expect_success 'tabs and spaces are accepted in the todolist' '
rebase_setup_and_clean indented-comment &&
write_script add-indent.sh <<-\EOF &&
@@ -1698,8 +1686,8 @@ test_expect_success 'static check of bad SHA-1' '
set_fake_editor &&
test_must_fail env FAKE_LINES="1 2 edit fakesha 3 4 5 #" \
git rebase -i --root 2>actual &&
- test_i18ngrep "edit XXXXXXX False commit" actual &&
- test_i18ngrep "You can fix this with .git rebase --edit-todo.." \
+ test_grep "edit XXXXXXX False commit" actual &&
+ test_grep "You can fix this with .git rebase --edit-todo.." \
actual &&
FAKE_LINES="1 2 4 5 6" git rebase --edit-todo
) &&
@@ -1726,7 +1714,7 @@ test_expect_success 'rebase -i --gpg-sign=<key-id>' '
FAKE_LINES="edit 1" git rebase -i --gpg-sign="\"S I Gner\"" \
HEAD^ >out 2>err
) &&
- test_i18ngrep "$SQ-S\"S I Gner\"$SQ" err
+ test_grep "$SQ-S\"S I Gner\"$SQ" err
'
test_expect_success 'rebase -i --gpg-sign=<key-id> overrides commit.gpgSign' '
@@ -1737,7 +1725,7 @@ test_expect_success 'rebase -i --gpg-sign=<key-id> overrides commit.gpgSign' '
FAKE_LINES="edit 1" git rebase -i --gpg-sign="\"S I Gner\"" \
HEAD^ >out 2>err
) &&
- test_i18ngrep "$SQ-S\"S I Gner\"$SQ" err
+ test_grep "$SQ-S\"S I Gner\"$SQ" err
'
test_expect_success 'valid author header after --root swap' '
@@ -1765,10 +1753,8 @@ test_expect_success 'valid author header when author contains single quote' '
'
test_expect_success 'post-commit hook is called' '
- test_when_finished "rm -f .git/hooks/post-commit" &&
>actual &&
- mkdir -p .git/hooks &&
- write_script .git/hooks/post-commit <<-\EOS &&
+ test_hook post-commit <<-\EOS &&
git rev-parse HEAD >>actual
EOS
(
@@ -1793,7 +1779,7 @@ test_expect_success 'correct error message for partial commit after empty pick'
) &&
echo x >file1 &&
test_must_fail git commit file1 2>err &&
- test_i18ngrep "cannot do a partial commit during a rebase." err
+ test_grep "cannot do a partial commit during a rebase." err
'
test_expect_success 'correct error message for commit --amend after empty pick' '
@@ -1806,13 +1792,13 @@ test_expect_success 'correct error message for commit --amend after empty pick'
) &&
echo x>file1 &&
test_must_fail git commit -a --amend 2>err &&
- test_i18ngrep "middle of a rebase -- cannot amend." err
+ test_grep "middle of a rebase -- cannot amend." err
'
test_expect_success 'todo has correct onto hash' '
GIT_SEQUENCE_EDITOR=cat git rebase -i no-conflict-branch~4 no-conflict-branch >actual &&
onto=$(git rev-parse --short HEAD~4) &&
- test_i18ngrep "^# Rebase ..* onto $onto" actual
+ test_grep "^# Rebase ..* onto $onto" actual
'
test_expect_success 'ORIG_HEAD is updated correctly' '
@@ -1826,6 +1812,409 @@ test_expect_success 'ORIG_HEAD is updated correctly' '
test_cmp_rev ORIG_HEAD test-orig-head@{1}
'
+test_expect_success '--update-refs adds label and update-ref commands' '
+ git checkout -b update-refs no-conflict-branch &&
+ git branch -f base HEAD~4 &&
+ git branch -f first HEAD~3 &&
+ git branch -f second HEAD~3 &&
+ git branch -f third HEAD~1 &&
+ git commit --allow-empty --fixup=third &&
+ git branch -f is-not-reordered &&
+ git commit --allow-empty --fixup=HEAD~4 &&
+ git branch -f shared-tip &&
+ (
+ set_cat_todo_editor &&
+
+ cat >expect <<-EOF &&
+ pick $(git log -1 --format=%h J) J
+ fixup $(git log -1 --format=%h update-refs) fixup! J # empty
+ update-ref refs/heads/second
+ update-ref refs/heads/first
+ pick $(git log -1 --format=%h K) K
+ pick $(git log -1 --format=%h L) L
+ fixup $(git log -1 --format=%h is-not-reordered) fixup! L # empty
+ update-ref refs/heads/third
+ pick $(git log -1 --format=%h M) M
+ update-ref refs/heads/no-conflict-branch
+ update-ref refs/heads/is-not-reordered
+ update-ref refs/heads/shared-tip
+ EOF
+
+ test_must_fail git rebase -i --autosquash --update-refs primary >todo &&
+ test_cmp expect todo &&
+
+ test_must_fail git -c rebase.autosquash=true \
+ -c rebase.updaterefs=true \
+ rebase -i primary >todo &&
+
+ test_cmp expect todo
+ )
+'
+
+test_expect_success '--update-refs adds commands with --rebase-merges' '
+ git checkout -b update-refs-with-merge no-conflict-branch &&
+ git branch -f base HEAD~4 &&
+ git branch -f first HEAD~3 &&
+ git branch -f second HEAD~3 &&
+ git branch -f third HEAD~1 &&
+ git merge -m merge branch2 &&
+ git branch -f merge-branch &&
+ git commit --fixup=third --allow-empty &&
+ (
+ set_cat_todo_editor &&
+
+ cat >expect <<-EOF &&
+ label onto
+ reset onto
+ pick $(git log -1 --format=%h branch2~1) F
+ pick $(git log -1 --format=%h branch2) I
+ update-ref refs/heads/branch2
+ label merge
+ reset onto
+ pick $(git log -1 --format=%h refs/heads/second) J
+ update-ref refs/heads/second
+ update-ref refs/heads/first
+ pick $(git log -1 --format=%h refs/heads/third~1) K
+ pick $(git log -1 --format=%h refs/heads/third) L
+ fixup $(git log -1 --format=%h update-refs-with-merge) fixup! L # empty
+ update-ref refs/heads/third
+ pick $(git log -1 --format=%h HEAD~2) M
+ update-ref refs/heads/no-conflict-branch
+ merge -C $(git log -1 --format=%h HEAD~1) merge # merge
+ update-ref refs/heads/merge-branch
+ EOF
+
+ test_must_fail git rebase -i --autosquash \
+ --rebase-merges=rebase-cousins \
+ --update-refs primary >todo &&
+
+ test_cmp expect todo &&
+
+ test_must_fail git -c rebase.autosquash=true \
+ -c rebase.updaterefs=true \
+ rebase -i \
+ --rebase-merges=rebase-cousins \
+ primary >todo &&
+
+ test_cmp expect todo
+ )
+'
+
+test_expect_success '--update-refs updates refs correctly' '
+ git checkout -B update-refs no-conflict-branch &&
+ git branch -f base HEAD~4 &&
+ git branch -f first HEAD~3 &&
+ git branch -f second HEAD~3 &&
+ git branch -f third HEAD~1 &&
+ test_commit extra2 fileX &&
+ git commit --amend --fixup=L &&
+
+ git rebase -i --autosquash --update-refs primary 2>err &&
+
+ test_cmp_rev HEAD~3 refs/heads/first &&
+ test_cmp_rev HEAD~3 refs/heads/second &&
+ test_cmp_rev HEAD~1 refs/heads/third &&
+ test_cmp_rev HEAD refs/heads/no-conflict-branch &&
+
+ cat >expect <<-\EOF &&
+ Successfully rebased and updated refs/heads/update-refs.
+ Updated the following refs with --update-refs:
+ refs/heads/first
+ refs/heads/no-conflict-branch
+ refs/heads/second
+ refs/heads/third
+ EOF
+
+ # Clear "Rebasing (X/Y)" progress lines and drop leading tabs.
+ sed -e "s/Rebasing.*Successfully/Successfully/g" -e "s/^\t//g" \
+ <err >err.trimmed &&
+ test_cmp expect err.trimmed
+'
+
+test_expect_success 'respect user edits to update-ref steps' '
+ git checkout -B update-refs-break no-conflict-branch &&
+ git branch -f base HEAD~4 &&
+ git branch -f first HEAD~3 &&
+ git branch -f second HEAD~3 &&
+ git branch -f third HEAD~1 &&
+ git branch -f unseen base &&
+
+ # First, we will add breaks to the expected todo file
+ cat >fake-todo-1 <<-EOF &&
+ pick $(git rev-parse HEAD~3)
+ break
+ update-ref refs/heads/second
+ update-ref refs/heads/first
+
+ pick $(git rev-parse HEAD~2)
+ pick $(git rev-parse HEAD~1)
+ update-ref refs/heads/third
+
+ pick $(git rev-parse HEAD)
+ update-ref refs/heads/no-conflict-branch
+ EOF
+
+ # Second, we will drop some update-refs commands (and move one)
+ cat >fake-todo-2 <<-EOF &&
+ update-ref refs/heads/second
+
+ pick $(git rev-parse HEAD~2)
+ update-ref refs/heads/third
+ pick $(git rev-parse HEAD~1)
+ break
+
+ pick $(git rev-parse HEAD)
+ EOF
+
+ # Third, we will:
+ # * insert a new one (new-branch),
+ # * re-add an old one (first), and
+ # * add a second instance of a previously-stored one (second)
+ cat >fake-todo-3 <<-EOF &&
+ update-ref refs/heads/unseen
+ update-ref refs/heads/new-branch
+ pick $(git rev-parse HEAD)
+ update-ref refs/heads/first
+ update-ref refs/heads/second
+ EOF
+
+ (
+ set_replace_editor fake-todo-1 &&
+ git rebase -i --update-refs primary &&
+
+ # These branches are currently locked.
+ for b in first second third no-conflict-branch
+ do
+ test_must_fail git branch -f $b base || return 1
+ done &&
+
+ set_replace_editor fake-todo-2 &&
+ git rebase --edit-todo &&
+
+ # These branches are currently locked.
+ for b in second third
+ do
+ test_must_fail git branch -f $b base || return 1
+ done &&
+
+ # These branches are currently unlocked for checkout.
+ for b in first no-conflict-branch
+ do
+ git worktree add wt-$b $b &&
+ git worktree remove wt-$b || return 1
+ done &&
+
+ git rebase --continue &&
+
+ set_replace_editor fake-todo-3 &&
+ git rebase --edit-todo &&
+
+ # These branches are currently locked.
+ for b in second third first unseen
+ do
+ test_must_fail git branch -f $b base || return 1
+ done &&
+
+ # These branches are currently unlocked for checkout.
+ for b in no-conflict-branch
+ do
+ git worktree add wt-$b $b &&
+ git worktree remove wt-$b || return 1
+ done &&
+
+ git rebase --continue
+ ) &&
+
+ test_cmp_rev HEAD~2 refs/heads/third &&
+ test_cmp_rev HEAD~1 refs/heads/unseen &&
+ test_cmp_rev HEAD~1 refs/heads/new-branch &&
+ test_cmp_rev HEAD refs/heads/first &&
+ test_cmp_rev HEAD refs/heads/second &&
+ test_cmp_rev HEAD refs/heads/no-conflict-branch
+'
+
+test_expect_success '--update-refs: all update-ref lines removed' '
+ git checkout -b test-refs-not-removed no-conflict-branch &&
+ git branch -f base HEAD~4 &&
+ git branch -f first HEAD~3 &&
+ git branch -f second HEAD~3 &&
+ git branch -f third HEAD~1 &&
+ git branch -f tip &&
+
+ test_commit test-refs-not-removed &&
+ git commit --amend --fixup first &&
+
+ git rev-parse first second third tip no-conflict-branch >expect-oids &&
+
+ (
+ set_cat_todo_editor &&
+ test_must_fail git rebase -i --update-refs base >todo.raw &&
+ sed -e "/^update-ref/d" <todo.raw >todo
+ ) &&
+ (
+ set_replace_editor todo &&
+ git rebase -i --update-refs base
+ ) &&
+
+ # Ensure refs are not deleted and their OIDs have not changed
+ git rev-parse first second third tip no-conflict-branch >actual-oids &&
+ test_cmp expect-oids actual-oids
+'
+
+test_expect_success '--update-refs: all update-ref lines removed, then some re-added' '
+ git checkout -b test-refs-not-removed2 no-conflict-branch &&
+ git branch -f base HEAD~4 &&
+ git branch -f first HEAD~3 &&
+ git branch -f second HEAD~3 &&
+ git branch -f third HEAD~1 &&
+ git branch -f tip &&
+
+ test_commit test-refs-not-removed2 &&
+ git commit --amend --fixup first &&
+
+ git rev-parse first second third >expect-oids &&
+
+ (
+ set_cat_todo_editor &&
+ test_must_fail git rebase -i \
+ --autosquash --update-refs \
+ base >todo.raw &&
+ sed -e "/^update-ref/d" <todo.raw >todo
+ ) &&
+
+ # Add a break to the end of the todo so we can edit later
+ echo "break" >>todo &&
+
+ (
+ set_replace_editor todo &&
+ git rebase -i --autosquash --update-refs base &&
+ echo "update-ref refs/heads/tip" >todo &&
+ git rebase --edit-todo &&
+ git rebase --continue
+ ) &&
+
+ # Ensure first/second/third are unchanged, but tip is updated
+ git rev-parse first second third >actual-oids &&
+ test_cmp expect-oids actual-oids &&
+ test_cmp_rev HEAD tip
+'
+
+test_expect_success '--update-refs: --edit-todo with no update-ref lines' '
+ git checkout -b test-refs-not-removed3 no-conflict-branch &&
+ git branch -f base HEAD~4 &&
+ git branch -f first HEAD~3 &&
+ git branch -f second HEAD~3 &&
+ git branch -f third HEAD~1 &&
+ git branch -f tip &&
+
+ test_commit test-refs-not-removed3 &&
+ git commit --amend --fixup first &&
+
+ git rev-parse first second third tip no-conflict-branch >expect-oids &&
+
+ (
+ set_cat_todo_editor &&
+ test_must_fail git rebase -i \
+ --autosquash --update-refs \
+ base >todo.raw &&
+ sed -e "/^update-ref/d" <todo.raw >todo
+ ) &&
+
+ # Add a break to the beginning of the todo so we can resume with no
+ # update-ref lines
+ echo "break" >todo.new &&
+ cat todo >>todo.new &&
+
+ (
+ set_replace_editor todo.new &&
+ git rebase -i --autosquash --update-refs base &&
+
+ # Make no changes when editing so update-refs is still empty
+ cat todo >todo.new &&
+ git rebase --edit-todo &&
+ git rebase --continue
+ ) &&
+
+ # Ensure refs are not deleted and their OIDs have not changed
+ git rev-parse first second third tip no-conflict-branch >actual-oids &&
+ test_cmp expect-oids actual-oids
+'
+
+test_expect_success '--update-refs: check failed ref update' '
+ test_when_finished "test_might_fail git rebase --abort" &&
+ git checkout -B update-refs-error no-conflict-branch &&
+ git branch -f base HEAD~4 &&
+ git branch -f first HEAD~3 &&
+ git branch -f second HEAD~2 &&
+ git branch -f third HEAD~1 &&
+
+ cat >fake-todo <<-EOF &&
+ pick $(git rev-parse HEAD~3)
+ break
+ update-ref refs/heads/first
+
+ pick $(git rev-parse HEAD~2)
+ update-ref refs/heads/second
+
+ pick $(git rev-parse HEAD~1)
+ update-ref refs/heads/third
+
+ pick $(git rev-parse HEAD)
+ update-ref refs/heads/no-conflict-branch
+ EOF
+
+ (
+ set_replace_editor fake-todo &&
+ git rebase -i --update-refs base
+ ) &&
+
+ # At this point, the values of first, second, and third are
+ # recorded in the update-refs file. We will force-update the
+ # "second" ref, but "git branch -f" will not work because of
+ # the lock in the update-refs file.
+ git update-ref refs/heads/second third &&
+
+ test_must_fail git rebase --continue 2>err &&
+ grep "update_ref failed for ref '\''refs/heads/second'\''" err &&
+
+ cat >expect <<-\EOF &&
+ Updated the following refs with --update-refs:
+ refs/heads/first
+ refs/heads/no-conflict-branch
+ refs/heads/third
+ Failed to update the following refs with --update-refs:
+ refs/heads/second
+ EOF
+
+ # Clear "Rebasing (X/Y)" progress lines and drop leading tabs.
+ tail -n 6 err >err.last &&
+ sed -e "s/Rebasing.*Successfully/Successfully/g" -e "s/^\t//g" \
+ <err.last >err.trimmed &&
+ test_cmp expect err.trimmed
+'
+
+test_expect_success 'bad labels and refs rejected when parsing todo list' '
+ test_when_finished "test_might_fail git rebase --abort" &&
+ cat >todo <<-\EOF &&
+ exec >execed
+ label #
+ label :invalid
+ update-ref :bad
+ update-ref topic
+ EOF
+ rm -f execed &&
+ (
+ set_replace_editor todo &&
+ test_must_fail git rebase -i HEAD 2>err
+ ) &&
+ grep "'\''#'\'' is not a valid label" err &&
+ grep "'\'':invalid'\'' is not a valid label" err &&
+ grep "'\'':bad'\'' is not a valid refname" err &&
+ grep "update-ref requires a fully qualified refname e.g. refs/heads/topic" \
+ err &&
+ test_path_is_missing execed
+'
+
# This must be the last test in this file
test_expect_success '$EDITOR and friends are unchanged' '
test_editor_unchanged
diff --git a/t/t3405-rebase-malformed.sh b/t/t3405-rebase-malformed.sh
index 2524331..8979bc3 100755
--- a/t/t3405-rebase-malformed.sh
+++ b/t/t3405-rebase-malformed.sh
@@ -5,6 +5,7 @@ test_description='rebase should handle arbitrary git message'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-rebase.sh
diff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh
index 77a313f..a1d7fa7 100755
--- a/t/t3406-rebase-message.sh
+++ b/t/t3406-rebase-message.sh
@@ -10,10 +10,16 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
test_expect_success 'setup' '
test_commit O fileO &&
test_commit X fileX &&
+ git branch fast-forward &&
test_commit A fileA &&
test_commit B fileB &&
test_commit Y fileY &&
+ git checkout -b conflicts O &&
+ test_commit P &&
+ test_commit conflict-X fileX &&
+ test_commit Q &&
+
git checkout -b topic O &&
git cherry-pick A B &&
test_commit Z fileZ &&
@@ -27,24 +33,24 @@ test_expect_success 'rebase -m' '
test_expect_success 'rebase against main twice' '
git rebase --apply main >out &&
- test_i18ngrep "Current branch topic is up to date" out
+ test_grep "Current branch topic is up to date" out
'
test_expect_success 'rebase against main twice with --force' '
git rebase --force-rebase --apply main >out &&
- test_i18ngrep "Current branch topic is up to date, rebase forced" out
+ test_grep "Current branch topic is up to date, rebase forced" out
'
test_expect_success 'rebase against main twice from another branch' '
git checkout topic^ &&
git rebase --apply main topic >out &&
- test_i18ngrep "Current branch topic is up to date" out
+ test_grep "Current branch topic is up to date" out
'
test_expect_success 'rebase fast-forward to main' '
git checkout topic^ &&
git rebase --apply topic >out &&
- test_i18ngrep "Fast-forwarded HEAD to topic" out
+ test_grep "Fast-forwarded HEAD to topic" out
'
test_expect_success 'rebase --stat' '
@@ -69,41 +75,175 @@ test_expect_success 'rebase -n overrides config rebase.stat config' '
test_expect_success 'rebase --onto outputs the invalid ref' '
test_must_fail git rebase --onto invalid-ref HEAD HEAD 2>err &&
- test_i18ngrep "invalid-ref" err
+ test_grep "invalid-ref" err
'
test_expect_success 'error out early upon -C<n> or --whitespace=<bad>' '
test_must_fail git rebase -Cnot-a-number HEAD 2>err &&
- test_i18ngrep "numerical value" err &&
+ test_grep "numerical value" err &&
test_must_fail git rebase --whitespace=bad HEAD 2>err &&
- test_i18ngrep "Invalid whitespace option" err
+ test_grep "Invalid whitespace option" err
'
-test_expect_success 'GIT_REFLOG_ACTION' '
- git checkout start &&
- test_commit reflog-onto &&
- git checkout -b reflog-topic start &&
- test_commit reflog-to-rebase &&
+write_reflog_expect () {
+ if test $mode = --apply
+ then
+ sed 's/(continue)/(pick)/'
+ else
+ cat
+ fi >expect
+}
+
+test_reflog () {
+ mode=$1
+ reflog_action="$2"
+
+ test_expect_success "rebase $mode reflog${reflog_action:+ GIT_REFLOG_ACTION=$reflog_action}" '
+ git checkout conflicts &&
+ test_when_finished "git reset --hard Q" &&
+
+ (
+ if test -n "$reflog_action"
+ then
+ GIT_REFLOG_ACTION="$reflog_action" &&
+ export GIT_REFLOG_ACTION
+ fi &&
+ test_must_fail git rebase $mode main &&
+ echo resolved >fileX &&
+ git add fileX &&
+ git rebase --continue
+ ) &&
+
+ git log -g --format=%gs -5 >actual &&
+ write_reflog_expect <<-EOF &&
+ ${reflog_action:-rebase} (finish): returning to refs/heads/conflicts
+ ${reflog_action:-rebase} (pick): Q
+ ${reflog_action:-rebase} (continue): conflict-X
+ ${reflog_action:-rebase} (pick): P
+ ${reflog_action:-rebase} (start): checkout main
+ EOF
+ test_cmp expect actual &&
+
+ git log -g --format=%gs -1 conflicts >actual &&
+ write_reflog_expect <<-EOF &&
+ ${reflog_action:-rebase} (finish): refs/heads/conflicts onto $(git rev-parse main)
+ EOF
+ test_cmp expect actual &&
+
+ # check there is only one new entry in the branch reflog
+ test_cmp_rev conflicts@{1} Q
+ '
+
+ test_expect_success "rebase $mode fast-forward reflog${reflog_action:+ GIT_REFLOG_ACTION=$reflog_action}" '
+ git checkout fast-forward &&
+ test_when_finished "git reset --hard X" &&
+
+ (
+ if test -n "$reflog_action"
+ then
+ GIT_REFLOG_ACTION="$reflog_action" &&
+ export GIT_REFLOG_ACTION
+ fi &&
+ git rebase $mode main
+ ) &&
+
+ git log -g --format=%gs -2 >actual &&
+ write_reflog_expect <<-EOF &&
+ ${reflog_action:-rebase} (finish): returning to refs/heads/fast-forward
+ ${reflog_action:-rebase} (start): checkout main
+ EOF
+ test_cmp expect actual &&
+
+ git log -g --format=%gs -1 fast-forward >actual &&
+ write_reflog_expect <<-EOF &&
+ ${reflog_action:-rebase} (finish): refs/heads/fast-forward onto $(git rev-parse main)
+ EOF
+ test_cmp expect actual &&
+
+ # check there is only one new entry in the branch reflog
+ test_cmp_rev fast-forward@{1} X
+ '
+
+ test_expect_success "rebase $mode --skip reflog${reflog_action:+ GIT_REFLOG_ACTION=$reflog_action}" '
+ git checkout conflicts &&
+ test_when_finished "git reset --hard Q" &&
+
+ (
+ if test -n "$reflog_action"
+ then
+ GIT_REFLOG_ACTION="$reflog_action" &&
+ export GIT_REFLOG_ACTION
+ fi &&
+ test_must_fail git rebase $mode main &&
+ git rebase --skip
+ ) &&
+
+ git log -g --format=%gs -4 >actual &&
+ write_reflog_expect <<-EOF &&
+ ${reflog_action:-rebase} (finish): returning to refs/heads/conflicts
+ ${reflog_action:-rebase} (pick): Q
+ ${reflog_action:-rebase} (pick): P
+ ${reflog_action:-rebase} (start): checkout main
+ EOF
+ test_cmp expect actual
+ '
+
+ test_expect_success "rebase $mode --abort reflog${reflog_action:+ GIT_REFLOG_ACTION=$reflog_action}" '
+ git checkout conflicts &&
+ test_when_finished "git reset --hard Q" &&
+
+ git log -g -1 conflicts >branch-expect &&
+ (
+ if test -n "$reflog_action"
+ then
+ GIT_REFLOG_ACTION="$reflog_action" &&
+ export GIT_REFLOG_ACTION
+ fi &&
+ test_must_fail git rebase $mode main &&
+ git rebase --abort
+ ) &&
- git rebase reflog-onto &&
git log -g --format=%gs -3 >actual &&
- cat >expect <<-\EOF &&
- rebase (finish): returning to refs/heads/reflog-topic
- rebase (pick): reflog-to-rebase
- rebase (start): checkout reflog-onto
+ write_reflog_expect <<-EOF &&
+ ${reflog_action:-rebase} (abort): returning to refs/heads/conflicts
+ ${reflog_action:-rebase} (pick): P
+ ${reflog_action:-rebase} (start): checkout main
EOF
test_cmp expect actual &&
- git checkout -b reflog-prefix reflog-to-rebase &&
- GIT_REFLOG_ACTION=change-the-reflog git rebase reflog-onto &&
+ # check branch reflog is unchanged
+ git log -g -1 conflicts >branch-actual &&
+ test_cmp branch-expect branch-actual
+ '
+
+ test_expect_success "rebase $mode --abort detached HEAD reflog${reflog_action:+ GIT_REFLOG_ACTION=$reflog_action}" '
+ git checkout Q &&
+ test_when_finished "git reset --hard Q" &&
+
+ (
+ if test -n "$reflog_action"
+ then
+ GIT_REFLOG_ACTION="$reflog_action" &&
+ export GIT_REFLOG_ACTION
+ fi &&
+ test_must_fail git rebase $mode main &&
+ git rebase --abort
+ ) &&
+
git log -g --format=%gs -3 >actual &&
- cat >expect <<-\EOF &&
- change-the-reflog (finish): returning to refs/heads/reflog-prefix
- change-the-reflog (pick): reflog-to-rebase
- change-the-reflog (start): checkout reflog-onto
+ write_reflog_expect <<-EOF &&
+ ${reflog_action:-rebase} (abort): returning to $(git rev-parse Q)
+ ${reflog_action:-rebase} (pick): P
+ ${reflog_action:-rebase} (start): checkout main
EOF
test_cmp expect actual
-'
+ '
+}
+
+test_reflog --merge
+test_reflog --merge my-reflog-action
+test_reflog --apply
+test_reflog --apply my-reflog-action
test_expect_success 'rebase -i onto unrelated history' '
git init unrelated &&
@@ -111,8 +251,8 @@ test_expect_success 'rebase -i onto unrelated history' '
git -C unrelated remote add -f origin "$PWD" &&
git -C unrelated branch --set-upstream-to=origin/main &&
git -C unrelated -c core.editor=true rebase -i -v --stat >actual &&
- test_i18ngrep "Changes to " actual &&
- test_i18ngrep "5 files changed" actual
+ test_grep "Changes to " actual &&
+ test_grep "5 files changed" actual
'
test_done
diff --git a/t/t3407-rebase-abort.sh b/t/t3407-rebase-abort.sh
index 7c381fb..9f49c42 100755
--- a/t/t3407-rebase-abort.sh
+++ b/t/t3407-rebase-abort.sh
@@ -7,77 +7,92 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
-### Test that we handle space characters properly
-work_dir="$(pwd)/test dir"
-
test_expect_success setup '
- mkdir -p "$work_dir" &&
- cd "$work_dir" &&
- git init &&
- echo a > a &&
- git add a &&
- git commit -m a &&
+ test_commit a a a &&
git branch to-rebase &&
- echo b > a &&
- git commit -a -m b &&
- echo c > a &&
- git commit -a -m c &&
+ test_commit --annotate b a b &&
+ test_commit --annotate c a c &&
git checkout to-rebase &&
- echo d > a &&
- git commit -a -m "merge should fail on this" &&
- echo e > a &&
- git commit -a -m "merge should fail on this, too" &&
- git branch pre-rebase
+ test_commit "merge should fail on this" a d d &&
+ test_commit --annotate "merge should fail on this, too" a e pre-rebase
'
+# Check that HEAD is equal to "pre-rebase" and the current branch is
+# "to-rebase"
+check_head() {
+ test_cmp_rev HEAD pre-rebase^{commit} &&
+ test "$(git symbolic-ref HEAD)" = refs/heads/to-rebase
+}
+
testrebase() {
type=$1
- dotest=$2
+ state_dir=$2
test_expect_success "rebase$type --abort" '
- cd "$work_dir" &&
# Clean up the state from the previous one
git reset --hard pre-rebase &&
test_must_fail git rebase$type main &&
- test_path_is_dir "$dotest" &&
+ test_path_is_dir "$state_dir" &&
git rebase --abort &&
- test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
- test ! -d "$dotest"
+ check_head &&
+ test_path_is_missing "$state_dir"
+ '
+
+ test_expect_success "pre rebase$type head is marked as reachable" '
+ # Clean up the state from the previous one
+ git checkout -f --detach pre-rebase &&
+ test_tick &&
+ git commit --amend --only -m "reworded" &&
+ orig_head=$(git rev-parse HEAD) &&
+ test_must_fail git rebase$type main &&
+ # Stop ORIG_HEAD marking $state_dir/orig-head as reachable
+ git update-ref -d ORIG_HEAD &&
+ git reflog expire --expire="$GIT_COMMITTER_DATE" --all &&
+ git prune --expire=now &&
+ git rebase --abort &&
+ test_cmp_rev $orig_head HEAD
'
test_expect_success "rebase$type --abort after --skip" '
- cd "$work_dir" &&
# Clean up the state from the previous one
- git reset --hard pre-rebase &&
+ git checkout -B to-rebase pre-rebase &&
test_must_fail git rebase$type main &&
- test_path_is_dir "$dotest" &&
+ test_path_is_dir "$state_dir" &&
test_must_fail git rebase --skip &&
- test $(git rev-parse HEAD) = $(git rev-parse main) &&
+ test_cmp_rev HEAD main &&
git rebase --abort &&
- test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
- test ! -d "$dotest"
+ check_head &&
+ test_path_is_missing "$state_dir"
'
test_expect_success "rebase$type --abort after --continue" '
- cd "$work_dir" &&
# Clean up the state from the previous one
git reset --hard pre-rebase &&
test_must_fail git rebase$type main &&
- test_path_is_dir "$dotest" &&
+ test_path_is_dir "$state_dir" &&
echo c > a &&
echo d >> a &&
git add a &&
test_must_fail git rebase --continue &&
- test $(git rev-parse HEAD) != $(git rev-parse main) &&
+ test_cmp_rev ! HEAD main &&
git rebase --abort &&
- test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
- test ! -d "$dotest"
+ check_head &&
+ test_path_is_missing "$state_dir"
+ '
+
+ test_expect_success "rebase$type --abort when checking out a tag" '
+ test_when_finished "git symbolic-ref HEAD refs/heads/to-rebase" &&
+ git reset --hard a -- &&
+ test_must_fail git rebase$type --onto b c pre-rebase &&
+ test_cmp_rev HEAD b^{commit} &&
+ git rebase --abort &&
+ test_cmp_rev HEAD pre-rebase^{commit} &&
+ ! git symbolic-ref HEAD
'
test_expect_success "rebase$type --abort does not update reflog" '
- cd "$work_dir" &&
# Clean up the state from the previous one
git reset --hard pre-rebase &&
git reflog show to-rebase > reflog_before &&
@@ -89,7 +104,6 @@ testrebase() {
'
test_expect_success 'rebase --abort can not be used with other options' '
- cd "$work_dir" &&
# Clean up the state from the previous one
git reset --hard pre-rebase &&
test_must_fail git rebase$type main &&
@@ -97,33 +111,21 @@ testrebase() {
test_must_fail git rebase --abort -v &&
git rebase --abort
'
+
+ test_expect_success "rebase$type --quit" '
+ test_when_finished "git symbolic-ref HEAD refs/heads/to-rebase" &&
+ # Clean up the state from the previous one
+ git reset --hard pre-rebase &&
+ test_must_fail git rebase$type main &&
+ test_path_is_dir $state_dir &&
+ head_before=$(git rev-parse HEAD) &&
+ git rebase --quit &&
+ test_cmp_rev HEAD $head_before &&
+ test_path_is_missing .git/rebase-apply
+ '
}
testrebase " --apply" .git/rebase-apply
testrebase " --merge" .git/rebase-merge
-test_expect_success 'rebase --apply --quit' '
- cd "$work_dir" &&
- # Clean up the state from the previous one
- git reset --hard pre-rebase &&
- test_must_fail git rebase --apply main &&
- test_path_is_dir .git/rebase-apply &&
- head_before=$(git rev-parse HEAD) &&
- git rebase --quit &&
- test $(git rev-parse HEAD) = $head_before &&
- test ! -d .git/rebase-apply
-'
-
-test_expect_success 'rebase --merge --quit' '
- cd "$work_dir" &&
- # Clean up the state from the previous one
- git reset --hard pre-rebase &&
- test_must_fail git rebase --merge main &&
- test_path_is_dir .git/rebase-merge &&
- head_before=$(git rev-parse HEAD) &&
- git rebase --quit &&
- test $(git rev-parse HEAD) = $head_before &&
- test ! -d .git/rebase-merge
-'
-
test_done
diff --git a/t/t3408-rebase-multi-line.sh b/t/t3408-rebase-multi-line.sh
index ab0960e..7b4607d 100755
--- a/t/t3408-rebase-multi-line.sh
+++ b/t/t3408-rebase-multi-line.sh
@@ -5,6 +5,7 @@ test_description='rebasing a commit with multi-line first paragraph.'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -55,14 +56,4 @@ test_expect_success rebase '
test_cmp expect actual
'
-test_expect_success REBASE_P rebasep '
-
- git checkout side-merge &&
- git rebase -p side &&
- git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
- git cat-file commit side-merge-original | sed -e "1,/^\$/d" >expect &&
- test_cmp expect actual
-
-'
-
test_done
diff --git a/t/t3409-rebase-environ.sh b/t/t3409-rebase-environ.sh
new file mode 100755
index 0000000..acaf555
--- /dev/null
+++ b/t/t3409-rebase-environ.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+test_description='git rebase interactive environment'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit one &&
+ test_commit two &&
+ test_commit three
+'
+
+test_expect_success 'rebase --exec does not muck with GIT_DIR' '
+ git rebase --exec "printf %s \$GIT_DIR >environ" HEAD~1 &&
+ test_must_be_empty environ
+'
+
+test_expect_success 'rebase --exec does not muck with GIT_WORK_TREE' '
+ git rebase --exec "printf %s \$GIT_WORK_TREE >environ" HEAD~1 &&
+ test_must_be_empty environ
+'
+
+test_done
diff --git a/t/t3409-rebase-preserve-merges.sh b/t/t3409-rebase-preserve-merges.sh
deleted file mode 100755
index ec8062a..0000000
--- a/t/t3409-rebase-preserve-merges.sh
+++ /dev/null
@@ -1,130 +0,0 @@
-#!/bin/sh
-#
-# Copyright(C) 2008 Stephen Habermann & Andreas Ericsson
-#
-test_description='git rebase -p should preserve merges
-
-Run "git rebase -p" and check that merges are properly carried along
-'
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
-. ./test-lib.sh
-
-if ! test_have_prereq REBASE_P; then
- skip_all='skipping git rebase -p tests, as asked for'
- test_done
-fi
-
-GIT_AUTHOR_EMAIL=bogus_email_address
-export GIT_AUTHOR_EMAIL
-
-# Clone 2 (conflicting merge):
-#
-# A1--A2--B3 <-- origin/main
-# \ \
-# B1------M <-- topic
-# \
-# B2 <-- origin/topic
-#
-# Clone 3 (no-ff merge):
-#
-# A1--A2--B3 <-- origin/main
-# \
-# B1------M <-- topic
-# \ /
-# \--A3 <-- topic2
-# \
-# B2 <-- origin/topic
-#
-# Clone 4 (same as Clone 3)
-
-test_expect_success 'setup for merge-preserving rebase' \
- 'echo First > A &&
- git add A &&
- git commit -m "Add A1" &&
- git checkout -b topic &&
- echo Second > B &&
- git add B &&
- git commit -m "Add B1" &&
- git checkout -f main &&
- echo Third >> A &&
- git commit -a -m "Modify A2" &&
- echo Fifth > B &&
- git add B &&
- git commit -m "Add different B" &&
-
- git clone ./. clone2 &&
- (
- cd clone2 &&
- git checkout -b topic origin/topic &&
- test_must_fail git merge origin/main &&
- echo Resolved >B &&
- git add B &&
- git commit -m "Merge origin/main into topic"
- ) &&
-
- git clone ./. clone3 &&
- (
- cd clone3 &&
- git checkout -b topic2 origin/topic &&
- echo Sixth > A &&
- git commit -a -m "Modify A3" &&
- git checkout -b topic origin/topic &&
- git merge --no-ff topic2
- ) &&
-
- git clone ./. clone4 &&
- (
- cd clone4 &&
- git checkout -b topic2 origin/topic &&
- echo Sixth > A &&
- git commit -a -m "Modify A3" &&
- git checkout -b topic origin/topic &&
- git merge --no-ff topic2
- ) &&
-
- git checkout topic &&
- echo Fourth >> B &&
- git commit -a -m "Modify B2"
-'
-
-test_expect_success '--continue works after a conflict' '
- (
- cd clone2 &&
- git fetch &&
- test_must_fail git rebase -p origin/topic &&
- test 2 = $(git ls-files B | wc -l) &&
- echo Resolved again > B &&
- test_must_fail git rebase --continue &&
- grep "^@@@ " .git/rebase-merge/patch &&
- git add B &&
- git rebase --continue &&
- test 1 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) &&
- test 1 = $(git rev-list --all --pretty=oneline | grep "Add different" | wc -l) &&
- test 1 = $(git rev-list --all --pretty=oneline | grep "Merge origin" | wc -l)
- )
-'
-
-test_expect_success 'rebase -p preserves no-ff merges' '
- (
- cd clone3 &&
- git fetch &&
- git rebase -p origin/topic &&
- test 3 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) &&
- test 1 = $(git rev-list --all --pretty=oneline | grep "Merge branch" | wc -l)
- )
-'
-
-test_expect_success 'rebase -p ignores merge.log config' '
- (
- cd clone4 &&
- git fetch &&
- git -c merge.log=1 rebase -p origin/topic &&
- echo >expected &&
- git log --format="%b" -1 >current &&
- test_cmp expected current
- )
-'
-
-test_done
diff --git a/t/t3410-rebase-preserve-dropped-merges.sh b/t/t3410-rebase-preserve-dropped-merges.sh
deleted file mode 100755
index 2e29866..0000000
--- a/t/t3410-rebase-preserve-dropped-merges.sh
+++ /dev/null
@@ -1,90 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2008 Stephen Haberman
-#
-
-test_description='git rebase preserve merges
-
-This test runs git rebase with preserve merges and ensures commits
-dropped by the --cherry-pick flag have their childrens parents
-rewritten.
-'
-. ./test-lib.sh
-
-if ! test_have_prereq REBASE_P; then
- skip_all='skipping git rebase -p tests, as asked for'
- test_done
-fi
-
-# set up two branches like this:
-#
-# A - B - C - D - E
-# \
-# F - G - H
-# \
-# I
-#
-# where B, D and G touch the same file.
-
-test_expect_success 'setup' '
- test_commit A file1 &&
- test_commit B file1 1 &&
- test_commit C file2 &&
- test_commit D file1 2 &&
- test_commit E file3 &&
- git checkout A &&
- test_commit F file4 &&
- test_commit G file1 3 &&
- test_commit H file5 &&
- git checkout F &&
- test_commit I file6
-'
-
-# A - B - C - D - E
-# \ \ \
-# F - G - H -- L \ --> L
-# \ | \
-# I -- G2 -- J -- K I -- K
-# G2 = same changes as G
-test_expect_success 'skip same-resolution merges with -p' '
- git checkout H &&
- test_must_fail git merge E &&
- test_commit L file1 23 &&
- git checkout I &&
- test_commit G2 file1 3 &&
- test_must_fail git merge E &&
- test_commit J file1 23 &&
- test_commit K file7 file7 &&
- git rebase -i -p L &&
- test $(git rev-parse HEAD^^) = $(git rev-parse L) &&
- test "23" = "$(cat file1)" &&
- test "I" = "$(cat file6)" &&
- test "file7" = "$(cat file7)"
-'
-
-# A - B - C - D - E
-# \ \ \
-# F - G - H -- L2 \ --> L2
-# \ | \
-# I -- G3 --- J2 -- K2 I -- G3 -- K2
-# G2 = different changes as G
-test_expect_success 'keep different-resolution merges with -p' '
- git checkout H &&
- test_must_fail git merge E &&
- test_commit L2 file1 23 &&
- git checkout I &&
- test_commit G3 file1 4 &&
- test_must_fail git merge E &&
- test_commit J2 file1 24 &&
- test_commit K2 file7 file7 &&
- test_must_fail git rebase -i -p L2 &&
- echo 234 > file1 &&
- git add file1 &&
- git rebase --continue &&
- test $(git rev-parse HEAD^^^) = $(git rev-parse L2) &&
- test "234" = "$(cat file1)" &&
- test "I" = "$(cat file6)" &&
- test "file7" = "$(cat file7)"
-'
-
-test_done
diff --git a/t/t3411-rebase-preserve-around-merges.sh b/t/t3411-rebase-preserve-around-merges.sh
deleted file mode 100755
index fb45e7b..0000000
--- a/t/t3411-rebase-preserve-around-merges.sh
+++ /dev/null
@@ -1,80 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2008 Stephen Haberman
-#
-
-test_description='git rebase preserve merges
-
-This test runs git rebase with -p and tries to squash a commit from after
-a merge to before the merge.
-'
-. ./test-lib.sh
-
-if ! test_have_prereq REBASE_P; then
- skip_all='skipping git rebase -p tests, as asked for'
- test_done
-fi
-
-. "$TEST_DIRECTORY"/lib-rebase.sh
-
-set_fake_editor
-
-# set up two branches like this:
-#
-# A1 - B1 - D1 - E1 - F1
-# \ /
-# -- C1 --
-
-test_expect_success 'setup' '
- test_commit A1 &&
- test_commit B1 &&
- test_commit C1 &&
- git reset --hard B1 &&
- test_commit D1 &&
- test_merge E1 C1 &&
- test_commit F1
-'
-
-# Should result in:
-#
-# A1 - B1 - D2 - E2
-# \ /
-# -- C1 --
-#
-test_expect_success 'squash F1 into D1' '
- FAKE_LINES="1 squash 4 2 3" git rebase -i -p B1 &&
- test "$(git rev-parse HEAD^2)" = "$(git rev-parse C1)" &&
- test "$(git rev-parse HEAD~2)" = "$(git rev-parse B1)" &&
- git tag E2
-'
-
-# Start with:
-#
-# A1 - B1 - D2 - E2
-# \
-# G1 ---- L1 ---- M1
-# \ /
-# H1 -- J1 -- K1
-# \ /
-# -- I1 --
-#
-# And rebase G1..M1 onto E2
-
-test_expect_success 'rebase two levels of merge' '
- git checkout A1 &&
- test_commit G1 &&
- test_commit H1 &&
- test_commit I1 &&
- git checkout -b branch3 H1 &&
- test_commit J1 &&
- test_merge K1 I1 &&
- git checkout -b branch2 G1 &&
- test_commit L1 &&
- test_merge M1 K1 &&
- GIT_EDITOR=: git rebase -i -p E2 &&
- test "$(git rev-parse HEAD~3)" = "$(git rev-parse E2)" &&
- test "$(git rev-parse HEAD~2)" = "$(git rev-parse HEAD^2^2~2)" &&
- test "$(git rev-parse HEAD^2^1^1)" = "$(git rev-parse HEAD^2^2^1)"
-'
-
-test_done
diff --git a/t/t3412-rebase-root.sh b/t/t3412-rebase-root.sh
index fda62c6..e75b3d0 100755
--- a/t/t3412-rebase-root.sh
+++ b/t/t3412-rebase-root.sh
@@ -7,11 +7,12 @@ Tests if git rebase --root --onto <newparent> can rebase the root commit.
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
log_with_names () {
git rev-list --topo-order --parents --pretty="tformat:%s" HEAD |
- git name-rev --stdin --name-only --refs=refs/heads/$1
+ git name-rev --annotate-stdin --name-only --refs=refs/heads/$1
}
@@ -31,12 +32,9 @@ test_expect_success 'rebase --root fails with too many args' '
'
test_expect_success 'setup pre-rebase hook' '
- mkdir -p .git/hooks &&
- cat >.git/hooks/pre-rebase <<EOF &&
-#!$SHELL_PATH
-echo "\$1,\$2" >.git/PRE-REBASE-INPUT
-EOF
- chmod +x .git/hooks/pre-rebase
+ test_hook --setup pre-rebase <<-\EOF
+ echo "$1,$2" >.git/PRE-REBASE-INPUT
+ EOF
'
cat > expect <<EOF
4
@@ -89,17 +87,6 @@ test_expect_success 'pre-rebase got correct input (4)' '
test "z$(cat .git/PRE-REBASE-INPUT)" = z--root,work4
'
-test_expect_success REBASE_P 'rebase -i -p with linear history' '
- git checkout -b work5 other &&
- git rebase -i -p --root --onto main &&
- git log --pretty=tformat:"%s" > rebased5 &&
- test_cmp expect rebased5
-'
-
-test_expect_success REBASE_P 'pre-rebase got correct input (5)' '
- test "z$(cat .git/PRE-REBASE-INPUT)" = z--root,
-'
-
test_expect_success 'set up merge history' '
git checkout other^ &&
git checkout -b side &&
@@ -123,13 +110,6 @@ commit work6~4
1
EOF
-test_expect_success REBASE_P 'rebase -i -p with merge' '
- git checkout -b work6 other &&
- git rebase -i -p --root --onto main &&
- log_with_names work6 > rebased6 &&
- test_cmp expect-side rebased6
-'
-
test_expect_success 'set up second root and merge' '
git symbolic-ref HEAD refs/heads/third &&
rm .git/index &&
@@ -158,20 +138,10 @@ commit work7~5
1
EOF
-test_expect_success REBASE_P 'rebase -i -p with two roots' '
- git checkout -b work7 other &&
- git rebase -i -p --root --onto main &&
- log_with_names work7 > rebased7 &&
- test_cmp expect-third rebased7
-'
-
test_expect_success 'setup pre-rebase hook that fails' '
- mkdir -p .git/hooks &&
- cat >.git/hooks/pre-rebase <<EOF &&
-#!$SHELL_PATH
-false
-EOF
- chmod +x .git/hooks/pre-rebase
+ test_hook --setup --clobber pre-rebase <<-\EOF
+ false
+ EOF
'
test_expect_success 'pre-rebase hook stops rebase' '
@@ -264,21 +234,9 @@ commit conflict3~6
1
EOF
-test_expect_success REBASE_P 'rebase -i -p --root with conflict (first part)' '
- git checkout -b conflict3 other &&
- test_must_fail git rebase -i -p --root --onto main &&
- git ls-files -u | grep "B$"
-'
-
test_expect_success 'fix the conflict' '
echo 3 > B &&
git add B
'
-test_expect_success REBASE_P 'rebase -i -p --root with conflict (second part)' '
- git rebase --continue &&
- log_with_names conflict3 >out &&
- test_cmp expect-conflict-p out
-'
-
test_done
diff --git a/t/t3413-rebase-hook.sh b/t/t3413-rebase-hook.sh
index b4acb3b..e845683 100755
--- a/t/t3413-rebase-hook.sh
+++ b/t/t3413-rebase-hook.sh
@@ -5,6 +5,7 @@ test_description='git rebase with its hook(s)'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -41,12 +42,9 @@ test_expect_success 'rebase -i' '
'
test_expect_success 'setup pre-rebase hook' '
- mkdir -p .git/hooks &&
- cat >.git/hooks/pre-rebase <<EOF &&
-#!$SHELL_PATH
-echo "\$1,\$2" >.git/PRE-REBASE-INPUT
-EOF
- chmod +x .git/hooks/pre-rebase
+ test_hook --setup pre-rebase <<-\EOF
+ echo "$1,$2" >.git/PRE-REBASE-INPUT
+ EOF
'
test_expect_success 'pre-rebase hook gets correct input (1)' '
@@ -102,12 +100,9 @@ test_expect_success 'pre-rebase hook gets correct input (6)' '
'
test_expect_success 'setup pre-rebase hook that fails' '
- mkdir -p .git/hooks &&
- cat >.git/hooks/pre-rebase <<EOF &&
-#!$SHELL_PATH
-false
-EOF
- chmod +x .git/hooks/pre-rebase
+ test_hook --setup --clobber pre-rebase <<-\EOF
+ false
+ EOF
'
test_expect_success 'pre-rebase hook stops rebase (1)' '
diff --git a/t/t3414-rebase-preserve-onto.sh b/t/t3414-rebase-preserve-onto.sh
deleted file mode 100755
index 72e04b5..0000000
--- a/t/t3414-rebase-preserve-onto.sh
+++ /dev/null
@@ -1,85 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2009 Greg Price
-#
-
-test_description='git rebase -p should respect --onto
-
-In a rebase with --onto, we should rewrite all the commits that
-aren'"'"'t on top of $ONTO, even if they are on top of $UPSTREAM.
-'
-. ./test-lib.sh
-
-if ! test_have_prereq REBASE_P; then
- skip_all='skipping git rebase -p tests, as asked for'
- test_done
-fi
-
-. "$TEST_DIRECTORY"/lib-rebase.sh
-
-# Set up branches like this:
-# A1---B1---E1---F1---G1
-# \ \ /
-# \ \--C1---D1--/
-# H1
-
-test_expect_success 'setup' '
- test_commit A1 &&
- test_commit B1 &&
- test_commit C1 &&
- test_commit D1 &&
- git reset --hard B1 &&
- test_commit E1 &&
- test_commit F1 &&
- test_merge G1 D1 &&
- git reset --hard A1 &&
- test_commit H1
-'
-
-# Now rebase merge G1 from both branches' base B1, both should move:
-# A1---B1---E1---F1---G1
-# \ \ /
-# \ \--C1---D1--/
-# \
-# H1---E2---F2---G2
-# \ /
-# \--C2---D2--/
-
-test_expect_success 'rebase from B1 onto H1' '
- git checkout G1 &&
- git rebase -p --onto H1 B1 &&
- test "$(git rev-parse HEAD^1^1^1)" = "$(git rev-parse H1)" &&
- test "$(git rev-parse HEAD^2^1^1)" = "$(git rev-parse H1)"
-'
-
-# On the other hand if rebase from E1 which is within one branch,
-# then the other branch stays:
-# A1---B1---E1---F1---G1
-# \ \ /
-# \ \--C1---D1--/
-# \ \
-# H1-----F3-----G3
-
-test_expect_success 'rebase from E1 onto H1' '
- git checkout G1 &&
- git rebase -p --onto H1 E1 &&
- test "$(git rev-parse HEAD^1^1)" = "$(git rev-parse H1)" &&
- test "$(git rev-parse HEAD^2)" = "$(git rev-parse D1)"
-'
-
-# And the same if we rebase from a commit in the second-parent branch.
-# A1---B1---E1---F1----G1
-# \ \ \ /
-# \ \--C1---D1-\-/
-# \ \
-# H1------D3------G4
-
-test_expect_success 'rebase from C1 onto H1' '
- git checkout G1 &&
- git rev-list --first-parent --pretty=oneline C1..G1 &&
- git rebase -p --onto H1 C1 &&
- test "$(git rev-parse HEAD^2^1)" = "$(git rev-parse H1)" &&
- test "$(git rev-parse HEAD^1)" = "$(git rev-parse F1)"
-'
-
-test_done
diff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh
index 78c2749..fcc40d6 100755
--- a/t/t3415-rebase-autosquash.sh
+++ b/t/t3415-rebase-autosquash.sh
@@ -43,7 +43,7 @@ test_auto_fixup () {
git tag $1 &&
test_tick &&
- git rebase $2 -i HEAD^^^ &&
+ git rebase $2 HEAD^^^ &&
git log --oneline >actual &&
if test -n "$no_squash"
then
@@ -61,15 +61,24 @@ test_auto_fixup () {
}
test_expect_success 'auto fixup (option)' '
- test_auto_fixup final-fixup-option --autosquash
+ test_auto_fixup fixup-option --autosquash &&
+ test_auto_fixup fixup-option-i "--autosquash -i"
'
-test_expect_success 'auto fixup (config)' '
+test_expect_success 'auto fixup (config true)' '
git config rebase.autosquash true &&
- test_auto_fixup final-fixup-config-true &&
+ test_auto_fixup ! fixup-config-true &&
+ test_auto_fixup fixup-config-true-i -i &&
test_auto_fixup ! fixup-config-true-no --no-autosquash &&
+ test_auto_fixup ! fixup-config-true-i-no "-i --no-autosquash"
+'
+
+test_expect_success 'auto fixup (config false)' '
git config rebase.autosquash false &&
- test_auto_fixup ! final-fixup-config-false
+ test_auto_fixup ! fixup-config-false &&
+ test_auto_fixup ! fixup-config-false-i -i &&
+ test_auto_fixup fixup-config-false-yes --autosquash &&
+ test_auto_fixup fixup-config-false-i-yes "-i --autosquash"
'
test_auto_squash () {
@@ -87,7 +96,7 @@ test_auto_squash () {
git commit -m "squash! first" -m "extra para for first" &&
git tag $1 &&
test_tick &&
- git rebase $2 -i HEAD^^^ &&
+ git rebase $2 HEAD^^^ &&
git log --oneline >actual &&
if test -n "$no_squash"
then
@@ -105,15 +114,24 @@ test_auto_squash () {
}
test_expect_success 'auto squash (option)' '
- test_auto_squash final-squash --autosquash
+ test_auto_squash squash-option --autosquash &&
+ test_auto_squash squash-option-i "--autosquash -i"
'
-test_expect_success 'auto squash (config)' '
+test_expect_success 'auto squash (config true)' '
git config rebase.autosquash true &&
- test_auto_squash final-squash-config-true &&
+ test_auto_squash ! squash-config-true &&
+ test_auto_squash squash-config-true-i -i &&
test_auto_squash ! squash-config-true-no --no-autosquash &&
+ test_auto_squash ! squash-config-true-i-no "-i --no-autosquash"
+'
+
+test_expect_success 'auto squash (config false)' '
git config rebase.autosquash false &&
- test_auto_squash ! final-squash-config-false
+ test_auto_squash ! squash-config-false &&
+ test_auto_squash ! squash-config-false-i -i &&
+ test_auto_squash squash-config-false-yes --autosquash &&
+ test_auto_squash squash-config-false-i-yes "-i --autosquash"
'
test_expect_success 'misspelled auto squash' '
@@ -232,6 +250,19 @@ test_expect_success 'auto squash that matches longer sha1' '
test_line_count = 1 actual
'
+test_expect_success 'auto squash of fixup commit that matches branch name which points back to fixup commit' '
+ git reset --hard base &&
+ git commit --allow-empty -m "fixup! self-cycle" &&
+ git branch self-cycle &&
+ GIT_SEQUENCE_EDITOR="cat >tmp" git rebase --autosquash -i HEAD^^ &&
+ sed -ne "/^[^#]/{s/[0-9a-f]\{7,\}/HASH/g;p;}" tmp >actual &&
+ cat <<-EOF >expect &&
+ pick HASH second commit
+ pick HASH fixup! self-cycle # empty
+ EOF
+ test_cmp expect actual
+'
+
test_auto_commit_flags () {
git reset --hard base &&
echo 1 >file1 &&
diff --git a/t/t3416-rebase-onto-threedots.sh b/t/t3416-rebase-onto-threedots.sh
index 3716a42..f8c4ed7 100755
--- a/t/t3416-rebase-onto-threedots.sh
+++ b/t/t3416-rebase-onto-threedots.sh
@@ -5,6 +5,7 @@ test_description='git rebase --onto A...B'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY/lib-rebase.sh"
@@ -79,8 +80,10 @@ test_expect_success 'rebase -i --onto main...topic' '
git reset --hard &&
git checkout topic &&
git reset --hard G &&
- set_fake_editor &&
- EXPECT_COUNT=1 git rebase -i --onto main...topic F &&
+ (
+ set_fake_editor &&
+ EXPECT_COUNT=1 git rebase -i --onto main...topic F
+ ) &&
git rev-parse HEAD^1 >actual &&
git rev-parse C^0 >expect &&
test_cmp expect actual
@@ -90,20 +93,22 @@ test_expect_success 'rebase -i --onto main...' '
git reset --hard &&
git checkout topic &&
git reset --hard G &&
- set_fake_editor &&
- EXPECT_COUNT=1 git rebase -i --onto main... F &&
+ (
+ set_fake_editor &&
+ EXPECT_COUNT=1 git rebase -i --onto main... F
+ ) &&
git rev-parse HEAD^1 >actual &&
git rev-parse C^0 >expect &&
test_cmp expect actual
'
-test_expect_success 'rebase -i --onto main...side' '
+test_expect_success 'rebase --onto main...side requires a single merge-base' '
git reset --hard &&
git checkout side &&
git reset --hard K &&
- set_fake_editor &&
- test_must_fail git rebase -i --onto main...side J
+ test_must_fail git rebase -i --onto main...side J 2>err &&
+ grep "need exactly one merge base" err
'
test_expect_success 'rebase --keep-base --onto incompatible' '
@@ -129,6 +134,20 @@ test_expect_success 'rebase --keep-base main from topic' '
test_cmp expect actual
'
+test_expect_success 'rebase --keep-base main topic from main' '
+ git checkout main &&
+ git branch -f topic G &&
+
+ git rebase --keep-base main topic &&
+ git rev-parse C >base.expect &&
+ git merge-base main HEAD >base.actual &&
+ test_cmp base.expect base.actual &&
+
+ git rev-parse HEAD~2 >actual &&
+ git rev-parse C^0 >expect &&
+ test_cmp expect actual
+'
+
test_expect_success 'rebase --keep-base main from side' '
git reset --hard &&
git checkout side &&
@@ -142,8 +161,27 @@ test_expect_success 'rebase -i --keep-base main from topic' '
git checkout topic &&
git reset --hard G &&
- set_fake_editor &&
- EXPECT_COUNT=2 git rebase -i --keep-base main &&
+ (
+ set_fake_editor &&
+ EXPECT_COUNT=2 git rebase -i --keep-base main
+ ) &&
+ git rev-parse C >base.expect &&
+ git merge-base main HEAD >base.actual &&
+ test_cmp base.expect base.actual &&
+
+ git rev-parse HEAD~2 >actual &&
+ git rev-parse C^0 >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rebase -i --keep-base main topic from main' '
+ git checkout main &&
+ git branch -f topic G &&
+
+ (
+ set_fake_editor &&
+ EXPECT_COUNT=2 git rebase -i --keep-base main topic
+ ) &&
git rev-parse C >base.expect &&
git merge-base main HEAD >base.actual &&
test_cmp base.expect base.actual &&
@@ -153,13 +191,39 @@ test_expect_success 'rebase -i --keep-base main from topic' '
test_cmp expect actual
'
-test_expect_success 'rebase -i --keep-base main from side' '
+test_expect_success 'rebase --keep-base requires a single merge base' '
git reset --hard &&
git checkout side &&
git reset --hard K &&
- set_fake_editor &&
- test_must_fail git rebase -i --keep-base main
+ test_must_fail git rebase -i --keep-base main 2>err &&
+ grep "need exactly one merge base with branch" err
+'
+
+test_expect_success 'rebase --keep-base keeps cherry picks' '
+ git checkout -f -B main E &&
+ git cherry-pick F &&
+ (
+ set_fake_editor &&
+ EXPECT_COUNT=2 git rebase -i --keep-base HEAD G
+ ) &&
+ test_cmp_rev HEAD G
+'
+
+test_expect_success 'rebase --keep-base --no-reapply-cherry-picks' '
+ git checkout -f -B main E &&
+ git cherry-pick F &&
+ (
+ set_fake_editor &&
+ EXPECT_COUNT=1 git rebase -i --keep-base \
+ --no-reapply-cherry-picks HEAD G
+ ) &&
+ test_cmp_rev HEAD^ C
+'
+
+# This must be the last test in this file
+test_expect_success '$EDITOR and friends are unchanged' '
+ test_editor_unchanged
'
test_done
diff --git a/t/t3417-rebase-whitespace-fix.sh b/t/t3417-rebase-whitespace-fix.sh
index 946e92f..96f2cf2 100755
--- a/t/t3417-rebase-whitespace-fix.sh
+++ b/t/t3417-rebase-whitespace-fix.sh
@@ -115,9 +115,7 @@ test_expect_success 'at beginning of file' '
git config core.whitespace "blank-at-eol" &&
cp beginning file &&
git commit -m beginning file &&
- for i in 1 2 3 4 5; do
- echo $i
- done >> file &&
+ test_write_lines 1 2 3 4 5 >>file &&
git commit -m more file &&
git rebase --whitespace=fix HEAD^^ &&
test_cmp expect-beginning file
diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh
index 738fbae..127216f 100755
--- a/t/t3418-rebase-continue.sh
+++ b/t/t3418-rebase-continue.sh
@@ -62,75 +62,39 @@ test_expect_success 'rebase --continue remembers 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" F3 32 &&
- test_when_finished "rm -fr test-bin funny.was.run" &&
+ test_when_finished "rm -fr test-bin" &&
mkdir test-bin &&
- cat >test-bin/git-merge-funny <<-EOF &&
- #!$SHELL_PATH
- case "\$1" in --opt) ;; *) exit 2 ;; esac
- shift &&
- >funny.was.run &&
- exec git merge-recursive "\$@"
+
+ write_script test-bin/git-merge-funny <<-\EOF &&
+ printf "[%s]\n" $# "$1" "$2" "$3" "$5" >actual
+ shift 3 &&
+ exec git merge-recursive "$@"
EOF
- chmod +x test-bin/git-merge-funny &&
- (
- PATH=./test-bin:$PATH &&
- test_must_fail git rebase -s funny -Xopt main 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
-'
-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 "\$@"
+ cat >expect <<-\EOF &&
+ [7]
+ [--option=arg with space]
+ [--op"tion\]
+ [--new
+ line ]
+ [--]
EOF
- chmod +x test-bin/git-merge-funny &&
+
+ rm -f actual &&
(
PATH=./test-bin:$PATH &&
- test_must_fail git rebase -i -s funny -Xopt -Xfoo main topic
+ test_must_fail git rebase -s funny -X"option=arg with space" \
+ -Xop\"tion\\ -X"new${LF}line " main topic
) &&
- test -f funny.was.run &&
- rm funny.was.run &&
+ test_cmp expect actual &&
+ rm actual &&
echo "Resolved" >F2 &&
git add F2 &&
(
PATH=./test-bin:$PATH &&
git rebase --continue
) &&
- test -f funny.was.run
-'
-
-test_expect_success REBASE_P 'rebase passes merge strategy options correctly' '
- rm -fr .git/rebase-* &&
- git reset --hard commit-new-file-F3-on-topic-branch &&
- test_commit theirs-to-merge &&
- git reset --hard HEAD^ &&
- test_commit some-commit &&
- test_tick &&
- git merge --no-ff theirs-to-merge &&
- FAKE_LINES="1 edit 2 3" git rebase -i -f -p -m \
- -s recursive --strategy-option=theirs HEAD~2 &&
- test_commit force-change &&
- git rebase --continue
+ test_cmp expect actual
'
test_expect_success 'rebase -r passes merge strategy options correctly' '
@@ -151,15 +115,23 @@ test_expect_success '--skip after failed fixup cleans commit message' '
test_when_finished "test_might_fail git rebase --abort" &&
git checkout -b with-conflicting-fixup &&
test_commit wants-fixup &&
- test_commit "fixup! wants-fixup" wants-fixup.t 1 wants-fixup-1 &&
- test_commit "fixup! wants-fixup" wants-fixup.t 2 wants-fixup-2 &&
- test_commit "fixup! wants-fixup" wants-fixup.t 3 wants-fixup-3 &&
+ test_commit "fixup 1" wants-fixup.t 1 wants-fixup-1 &&
+ test_commit "fixup 2" wants-fixup.t 2 wants-fixup-2 &&
+ test_commit "fixup 3" wants-fixup.t 3 wants-fixup-3 &&
test_must_fail env FAKE_LINES="1 fixup 2 squash 4" \
git rebase -i HEAD~4 &&
: now there is a conflict, and comments in the commit message &&
- git show HEAD >out &&
- grep "fixup! wants-fixup" out &&
+ test_commit_message HEAD <<-\EOF &&
+ # This is a combination of 2 commits.
+ # This is the 1st commit message:
+
+ wants-fixup
+
+ # The commit message #2 will be skipped:
+
+ # fixup 1
+ EOF
: skip and continue &&
echo "cp \"\$1\" .git/copy.txt" | write_script copy-editor.sh &&
@@ -169,33 +141,49 @@ test_expect_success '--skip after failed fixup cleans commit message' '
test_path_is_missing .git/copy.txt &&
: now the comments in the commit message should have been cleaned up &&
- git show HEAD >out &&
- ! grep "fixup! wants-fixup" out &&
+ test_commit_message HEAD -m wants-fixup &&
: now, let us ensure that "squash" is handled correctly &&
git reset --hard wants-fixup-3 &&
- test_must_fail env FAKE_LINES="1 squash 4 squash 2 squash 4" \
+ test_must_fail env FAKE_LINES="1 squash 2 squash 1 squash 3 squash 1" \
git rebase -i HEAD~4 &&
- : the first squash failed, but there are two more in the chain &&
+ : the second squash failed, but there are two more in the chain &&
(test_set_editor "$PWD/copy-editor.sh" &&
test_must_fail git rebase --skip) &&
: not the final squash, no need to edit the commit message &&
test_path_is_missing .git/copy.txt &&
- : 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 &&
+ : The first and third squashes succeeded, therefore: &&
+ test_commit_message HEAD <<-\EOF &&
+ # This is a combination of 3 commits.
+ # This is the 1st commit message:
+
+ wants-fixup
+
+ # This is the commit message #2:
+
+ fixup 1
+
+ # This is the commit message #3:
+
+ fixup 2
+ EOF
(test_set_editor "$PWD/copy-editor.sh" && git rebase --skip) &&
- git show HEAD >out &&
- test_i18ngrep ! "# This is a combination" out &&
+ test_commit_message HEAD <<-\EOF &&
+ wants-fixup
+
+ fixup 1
+
+ fixup 2
+ EOF
: 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 the commit message #2:" .git/copy.txt
+ head -n1 .git/copy.txt >first-line &&
+ test_grep "# This is a combination of 3 commits" first-line &&
+ test_grep "# This is the commit message #3:" .git/copy.txt
'
test_expect_success 'setup rerere database' '
@@ -268,7 +256,6 @@ test_rerere_autoupdate --apply
test_rerere_autoupdate -m
GIT_SEQUENCE_EDITOR=: && export GIT_SEQUENCE_EDITOR
test_rerere_autoupdate -i
-test_have_prereq !REBASE_P || test_rerere_autoupdate --preserve-merges
unset GIT_SEQUENCE_EDITOR
test_expect_success 'the todo command "break" works' '
@@ -281,6 +268,24 @@ test_expect_success 'the todo command "break" works' '
test_path_is_file execed
'
+test_expect_success 'patch file is removed before break command' '
+ test_when_finished "git rebase --abort" &&
+ cat >todo <<-\EOF &&
+ pick commit-new-file-F2-on-topic-branch
+ break
+ EOF
+
+ (
+ set_replace_editor todo &&
+ test_must_fail git rebase -i --onto commit-new-file-F2 HEAD
+ ) &&
+ test_path_is_file .git/rebase-merge/patch &&
+ echo 22>F2 &&
+ git add F2 &&
+ git rebase --continue &&
+ test_path_is_missing .git/rebase-merge/patch
+'
+
test_expect_success '--reschedule-failed-exec' '
test_when_finished "git rebase --abort" &&
test_must_fail git rebase -x false --reschedule-failed-exec HEAD^ &&
@@ -289,7 +294,7 @@ test_expect_success '--reschedule-failed-exec' '
test_must_fail git -c rebase.rescheduleFailedExec=true \
rebase -x false HEAD^ 2>err &&
grep "^exec false" .git/rebase-merge/git-rebase-todo &&
- test_i18ngrep "has been rescheduled" err
+ test_grep "has been rescheduled" err
'
test_expect_success 'rebase.rescheduleFailedExec only affects `rebase -i`' '
@@ -323,4 +328,30 @@ test_expect_success 'there is no --no-reschedule-failed-exec in an ongoing rebas
test_expect_code 129 git rebase --edit-todo --no-reschedule-failed-exec
'
+test_orig_head_helper () {
+ test_when_finished 'git rebase --abort &&
+ git checkout topic &&
+ git reset --hard commit-new-file-F2-on-topic-branch' &&
+ git update-ref -d ORIG_HEAD &&
+ test_must_fail git rebase "$@" &&
+ test_cmp_rev ORIG_HEAD commit-new-file-F2-on-topic-branch
+}
+
+test_orig_head () {
+ type=$1
+ test_expect_success "rebase $type sets ORIG_HEAD correctly" '
+ git checkout topic &&
+ git reset --hard commit-new-file-F2-on-topic-branch &&
+ test_orig_head_helper $type main
+ '
+
+ test_expect_success "rebase $type <upstream> <branch> sets ORIG_HEAD correctly" '
+ git checkout main &&
+ test_orig_head_helper $type main topic
+ '
+}
+
+test_orig_head --apply
+test_orig_head --merge
+
test_done
diff --git a/t/t3419-rebase-patch-id.sh b/t/t3419-rebase-patch-id.sh
index 295040f..6c61f24 100755
--- a/t/t3419-rebase-patch-id.sh
+++ b/t/t3419-rebase-patch-id.sh
@@ -5,6 +5,7 @@ test_description='git rebase - test patch id computation'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
scramble () {
@@ -43,15 +44,26 @@ test_expect_success 'setup: 500 lines' '
git add newfile &&
git commit -q -m "add small file" &&
- git cherry-pick main >/dev/null 2>&1
-'
+ git cherry-pick main >/dev/null 2>&1 &&
+
+ git branch -f squashed main &&
+ git checkout -q -f squashed &&
+ git reset -q --soft HEAD~2 &&
+ git commit -q -m squashed &&
+
+ git branch -f mode main &&
+ git checkout -q -f mode &&
+ test_chmod +x file &&
+ git commit -q -a --amend &&
-test_expect_success 'setup attributes' '
- echo "file binary" >.gitattributes
+ git branch -f modeother other &&
+ git checkout -q -f modeother &&
+ test_chmod +x file &&
+ git commit -q -a --amend
'
test_expect_success 'detect upstream patch' '
- git checkout -q main &&
+ git checkout -q main^{} &&
scramble file &&
git add file &&
git commit -q -m "change big file again" &&
@@ -61,14 +73,46 @@ test_expect_success 'detect upstream patch' '
test_must_be_empty revs
'
+test_expect_success 'detect upstream patch binary' '
+ echo "file binary" >.gitattributes &&
+ git checkout -q other^{} &&
+ git rebase main &&
+ git rev-list main...HEAD~ >revs &&
+ test_must_be_empty revs &&
+ test_when_finished "rm .gitattributes"
+'
+
+test_expect_success 'detect upstream patch modechange' '
+ git checkout -q modeother^{} &&
+ git rebase mode &&
+ git rev-list mode...HEAD~ >revs &&
+ test_must_be_empty revs
+'
+
test_expect_success 'do not drop patch' '
- git branch -f squashed main &&
- git checkout -q -f squashed &&
- git reset -q --soft HEAD~2 &&
- git commit -q -m squashed &&
git checkout -q other^{} &&
test_must_fail git rebase squashed &&
- git rebase --quit
+ test_when_finished "git rebase --abort"
+'
+
+test_expect_success 'do not drop patch binary' '
+ echo "file binary" >.gitattributes &&
+ git checkout -q other^{} &&
+ test_must_fail git rebase squashed &&
+ test_when_finished "git rebase --abort" &&
+ test_when_finished "rm .gitattributes"
+'
+
+test_expect_success 'do not drop patch modechange' '
+ git checkout -q modeother^{} &&
+ git rebase other &&
+ cat >expected <<-\EOF &&
+ diff --git a/file b/file
+ old mode 100644
+ new mode 100755
+ EOF
+ git diff HEAD~ >modediff &&
+ test_cmp expected modediff
'
test_done
diff --git a/t/t3420-rebase-autostash.sh b/t/t3420-rebase-autostash.sh
index 43fcb68..1a820f1 100755
--- a/t/t3420-rebase-autostash.sh
+++ b/t/t3420-rebase-autostash.sh
@@ -310,7 +310,7 @@ test_expect_success 'autostash is saved on editor failure with conflict' '
test_expect_success 'autostash with dirty submodules' '
test_when_finished "git reset --hard && git checkout main" &&
git checkout -b with-submodule &&
- git submodule add ./ sub &&
+ git -c protocol.file.allow=always submodule add ./ sub &&
test_tick &&
git commit -m add-submodule &&
echo changed >sub/file0 &&
@@ -333,4 +333,14 @@ test_expect_success 'never change active branch' '
test_cmp_rev not-the-feature-branch unrelated-onto-branch
'
+test_expect_success 'autostash commit is marked as reachable' '
+ echo changed >file0 &&
+ git rebase --autostash --exec "git prune --expire=now" \
+ feature-branch^ feature-branch &&
+ # git rebase succeeds if the stash cannot be applied so we need to check
+ # the contents of file0
+ echo changed >expect &&
+ test_cmp expect file0
+'
+
test_done
diff --git a/t/t3421-rebase-topology-linear.sh b/t/t3421-rebase-topology-linear.sh
index 4a9204b..62d86d5 100755
--- a/t/t3421-rebase-topology-linear.sh
+++ b/t/t3421-rebase-topology-linear.sh
@@ -29,7 +29,6 @@ test_run_rebase () {
test_run_rebase success --apply
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase success -p
test_expect_success 'setup branches and remote tracking' '
git tag -l >tags &&
@@ -53,7 +52,6 @@ test_run_rebase () {
test_run_rebase success --apply
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase () {
result=$1
@@ -70,7 +68,6 @@ test_run_rebase success --apply
test_run_rebase success --fork-point
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase failure -p
test_run_rebase () {
result=$1
@@ -87,7 +84,6 @@ test_run_rebase success --apply
test_run_rebase success --fork-point
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase () {
result=$1
@@ -102,7 +98,6 @@ test_run_rebase success --apply
test_run_rebase success --fork-point
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase success -p
# f
# /
@@ -142,7 +137,6 @@ test_run_rebase () {
test_run_rebase success --apply
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase () {
result=$1
@@ -157,7 +151,6 @@ test_run_rebase () {
test_run_rebase success --apply
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase () {
result=$1
@@ -172,7 +165,6 @@ test_run_rebase () {
test_run_rebase success --apply
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase () {
result=$1
@@ -187,7 +179,6 @@ test_run_rebase () {
test_run_rebase success --apply
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase success -p
# a---b---c---j!
# \
@@ -215,7 +206,6 @@ test_run_rebase () {
test_run_rebase failure --apply
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase failure -p
test_run_rebase () {
result=$1
@@ -229,7 +219,6 @@ test_run_rebase () {
}
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase () {
result=$1
@@ -243,7 +232,6 @@ test_run_rebase () {
}
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase success --rebase-merges
# m
@@ -283,7 +271,6 @@ test_run_rebase () {
test_run_rebase success --apply
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase () {
result=$1
@@ -298,7 +285,6 @@ test_run_rebase () {
test_run_rebase success --apply
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase failure -p
test_run_rebase () {
result=$1
@@ -313,7 +299,6 @@ test_run_rebase () {
test_run_rebase success --apply
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase () {
result=$1
@@ -329,7 +314,6 @@ test_run_rebase () {
test_run_rebase success --apply
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase failure -p
test_run_rebase () {
result=$1
@@ -344,7 +328,6 @@ test_run_rebase () {
test_run_rebase success --apply
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase failure -p
test_run_rebase () {
result=$1
@@ -358,7 +341,6 @@ test_run_rebase () {
test_run_rebase success ''
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase failure -p
test_run_rebase () {
result=$1
@@ -373,6 +355,5 @@ test_run_rebase () {
test_run_rebase success ''
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase success -p
test_done
diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh
index c823406..b40f262 100755
--- a/t/t3422-rebase-incompatible-options.sh
+++ b/t/t3422-rebase-incompatible-options.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test if rebase detects and aborts on incompatible options'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -23,11 +25,11 @@ test_expect_success 'setup' '
'
#
-# 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.
+# Rebase has a couple options which are specific to the apply backend,
+# and several options which are specific to the merge backend. Flags
+# from the different sets cannot work together, and we do not want to
+# just ignore one of the sets of flags. Make sure rebase warns the
+# user and aborts instead.
#
test_rebase_am_only () {
@@ -48,6 +50,11 @@ test_rebase_am_only () {
test_must_fail git rebase $opt --strategy-option=ours A
"
+ test_expect_success "$opt incompatible with --autosquash" "
+ git checkout B^0 &&
+ test_must_fail git rebase $opt --autosquash A
+ "
+
test_expect_success "$opt incompatible with --interactive" "
git checkout B^0 &&
test_must_fail git rebase $opt --interactive A
@@ -58,20 +65,70 @@ test_rebase_am_only () {
test_must_fail git rebase $opt --exec 'true' A
"
+ test_expect_success "$opt incompatible with --keep-empty" "
+ git checkout B^0 &&
+ test_must_fail git rebase $opt --keep-empty A
+ "
+
+ test_expect_success "$opt incompatible with --empty=..." "
+ git checkout B^0 &&
+ test_must_fail git rebase $opt --empty=ask A
+ "
+
+ test_expect_success "$opt incompatible with --no-reapply-cherry-picks" "
+ git checkout B^0 &&
+ test_must_fail git rebase $opt --no-reapply-cherry-picks A
+ "
+
+ test_expect_success "$opt incompatible with --reapply-cherry-picks" "
+ git checkout B^0 &&
+ test_must_fail git rebase $opt --reapply-cherry-picks A
+ "
+
+ test_expect_success "$opt incompatible with --rebase-merges" "
+ git checkout B^0 &&
+ test_must_fail git rebase $opt --rebase-merges A
+ "
+
+ test_expect_success "$opt incompatible with --update-refs" "
+ git checkout B^0 &&
+ test_must_fail git rebase $opt --update-refs A
+ "
+
+ test_expect_success "$opt incompatible with --root without --onto" "
+ git checkout B^0 &&
+ test_must_fail git rebase $opt --root A
+ "
+
+ test_expect_success "$opt incompatible with rebase.rebaseMerges" "
+ git checkout B^0 &&
+ test_must_fail git -c rebase.rebaseMerges=true rebase $opt A 2>err &&
+ grep -e --no-rebase-merges err
+ "
+
+ test_expect_success "$opt incompatible with rebase.updateRefs" "
+ git checkout B^0 &&
+ test_must_fail git -c rebase.updateRefs=true rebase $opt A 2>err &&
+ grep -e --no-update-refs err
+ "
+
+ test_expect_success "$opt okay with overridden rebase.rebaseMerges" "
+ test_when_finished \"git reset --hard B^0\" &&
+ git checkout B^0 &&
+ git -c rebase.rebaseMerges=true rebase --no-rebase-merges $opt A
+ "
+
+ test_expect_success "$opt okay with overridden rebase.updateRefs" "
+ test_when_finished \"git reset --hard B^0\" &&
+ git checkout B^0 &&
+ git -c rebase.updateRefs=true rebase --no-update-refs $opt A
+ "
}
+# Check options which imply --apply
test_rebase_am_only --whitespace=fix
test_rebase_am_only -C4
-
-test_expect_success REBASE_P '--preserve-merges incompatible with --signoff' '
- git checkout B^0 &&
- test_must_fail git rebase --preserve-merges --signoff A
-'
-
-test_expect_success REBASE_P \
- '--preserve-merges incompatible with --rebase-merges' '
- git checkout B^0 &&
- test_must_fail git rebase --preserve-merges --rebase-merges A
-'
+# Also check an explicit --apply
+test_rebase_am_only --apply
test_done
diff --git a/t/t3423-rebase-reword.sh b/t/t3423-rebase-reword.sh
index 4859bb8..2fab703 100755
--- a/t/t3423-rebase-reword.sh
+++ b/t/t3423-rebase-reword.sh
@@ -2,6 +2,7 @@
test_description='git rebase interactive with rewording'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-rebase.sh
diff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh
index 5e1045a..1ee6b00 100755
--- a/t/t3424-rebase-empty.sh
+++ b/t/t3424-rebase-empty.sh
@@ -72,6 +72,17 @@ test_expect_success 'rebase --merge --empty=keep' '
test_cmp expect actual
'
+test_expect_success 'rebase --merge --empty=stop' '
+ git checkout -B testing localmods &&
+ test_must_fail git rebase --merge --empty=stop upstream &&
+
+ git rebase --skip &&
+
+ test_write_lines D C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'rebase --merge --empty=ask' '
git checkout -B testing localmods &&
test_must_fail git rebase --merge --empty=ask upstream &&
@@ -101,9 +112,9 @@ test_expect_success 'rebase --interactive --empty=keep' '
test_cmp expect actual
'
-test_expect_success 'rebase --interactive --empty=ask' '
+test_expect_success 'rebase --interactive --empty=stop' '
git checkout -B testing localmods &&
- test_must_fail git rebase --interactive --empty=ask upstream &&
+ test_must_fail git rebase --interactive --empty=stop upstream &&
git rebase --skip &&
@@ -112,7 +123,7 @@ test_expect_success 'rebase --interactive --empty=ask' '
test_cmp expect actual
'
-test_expect_success 'rebase --interactive uses default of --empty=ask' '
+test_expect_success 'rebase --interactive uses default of --empty=stop' '
git checkout -B testing localmods &&
test_must_fail git rebase --interactive upstream &&
@@ -167,4 +178,42 @@ test_expect_success 'rebase --merge does not leave state laying around' '
test_path_is_missing .git/MERGE_MSG
'
+test_expect_success 'rebase --exec --empty=drop' '
+ git checkout -B testing localmods &&
+ git rebase --exec "true" --empty=drop upstream &&
+
+ test_write_lines D C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rebase --exec --empty=keep' '
+ git checkout -B testing localmods &&
+ git rebase --exec "true" --empty=keep upstream &&
+
+ test_write_lines D C2 C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rebase --exec uses default of --empty=keep' '
+ git checkout -B testing localmods &&
+ git rebase --exec "true" upstream &&
+
+ test_write_lines D C2 C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rebase --exec --empty=stop' '
+ git checkout -B testing localmods &&
+ test_must_fail git rebase --exec "true" --empty=stop upstream &&
+
+ git rebase --skip &&
+
+ test_write_lines D C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t3425-rebase-topology-merges.sh b/t/t3425-rebase-topology-merges.sh
index e42faa4..a16428b 100755
--- a/t/t3425-rebase-topology-merges.sh
+++ b/t/t3425-rebase-topology-merges.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='rebase topology tests with merges'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-rebase.sh
@@ -106,155 +108,4 @@ test_run_rebase success 'd n o e' --apply
test_run_rebase success 'd n o e' -m
test_run_rebase success 'd n o e' -i
-if ! test_have_prereq REBASE_P; then
- skip_all='skipping git rebase -p tests, as asked for'
- test_done
-fi
-
-test_expect_success "rebase -p is no-op in non-linear history" "
- reset_rebase &&
- git rebase -p d w &&
- test_cmp_rev w HEAD
-"
-
-test_expect_success "rebase -p is no-op when base inside second parent" "
- reset_rebase &&
- git rebase -p e w &&
- test_cmp_rev w HEAD
-"
-
-test_expect_failure "rebase -p --root on non-linear history is a no-op" "
- reset_rebase &&
- git rebase -p --root w &&
- test_cmp_rev w HEAD
-"
-
-test_expect_success "rebase -p re-creates merge from side branch" "
- reset_rebase &&
- git rebase -p z w &&
- test_cmp_rev z HEAD^ &&
- test_cmp_rev w^2 HEAD^2
-"
-
-test_expect_success "rebase -p re-creates internal merge" "
- reset_rebase &&
- git rebase -p c w &&
- test_cmp_rev c HEAD~4 &&
- test_cmp_rev HEAD^2^ HEAD~3 &&
- test_revision_subjects 'd n e o w' HEAD~3 HEAD~2 HEAD^2 HEAD^ HEAD
-"
-
-test_expect_success "rebase -p can re-create two branches on onto" "
- reset_rebase &&
- git rebase -p --onto c d w &&
- test_cmp_rev c HEAD~3 &&
- test_cmp_rev c HEAD^2^ &&
- test_revision_subjects 'n e o w' HEAD~2 HEAD^2 HEAD^ HEAD
-"
-
-# f
-# /
-# a---b---c---g---h
-# \
-# d---gp--i
-# \ \
-# e-------u
-#
-# gp = cherry-picked g
-# h = reverted g
-test_expect_success 'setup of non-linear-history for patch-equivalence tests' '
- git checkout e &&
- test_merge u i
-'
-
-test_expect_success "rebase -p re-creates history around dropped commit matching upstream" "
- reset_rebase &&
- git rebase -p h u &&
- test_cmp_rev h HEAD~3 &&
- test_cmp_rev HEAD^2^ HEAD~2 &&
- test_revision_subjects 'd i e u' HEAD~2 HEAD^2 HEAD^ HEAD
-"
-
-test_expect_success "rebase -p --onto in merged history drops patches in upstream" "
- reset_rebase &&
- git rebase -p --onto f h u &&
- test_cmp_rev f HEAD~3 &&
- test_cmp_rev HEAD^2^ HEAD~2 &&
- test_revision_subjects 'd i e u' HEAD~2 HEAD^2 HEAD^ HEAD
-"
-
-test_expect_success "rebase -p --onto in merged history does not drop patches in onto" "
- reset_rebase &&
- git rebase -p --onto h f u &&
- test_cmp_rev h HEAD~3 &&
- test_cmp_rev HEAD^2~2 HEAD~2 &&
- test_revision_subjects 'd gp i e u' HEAD~2 HEAD^2^ HEAD^2 HEAD^ HEAD
-"
-
-# a---b---c---g---h
-# \
-# d---gp--s
-# \ \ /
-# \ X
-# \ / \
-# e---t
-#
-# gp = cherry-picked g
-# h = reverted g
-test_expect_success 'setup of non-linear-history for dropping whole side' '
- git checkout gp &&
- test_merge s e &&
- git checkout e &&
- test_merge t gp
-'
-
-test_expect_failure "rebase -p drops merge commit when entire first-parent side is dropped" "
- reset_rebase &&
- git rebase -p h s &&
- test_cmp_rev h HEAD~2 &&
- test_linear_range 'd e' h..
-"
-
-test_expect_success "rebase -p drops merge commit when entire second-parent side is dropped" "
- reset_rebase &&
- git rebase -p h t &&
- test_cmp_rev h HEAD~2 &&
- test_linear_range 'd e' h..
-"
-
-# a---b---c
-# \
-# d---e
-# \ \
-# n---r
-# \
-# o
-#
-# r = tree-same with n
-test_expect_success 'setup of non-linear-history for empty commits' '
- git checkout n &&
- git merge --no-commit e &&
- git reset n . &&
- git commit -m r &&
- git reset --hard &&
- git clean -f &&
- git tag r
-'
-
-test_expect_success "rebase -p re-creates empty internal merge commit" "
- reset_rebase &&
- git rebase -p c r &&
- test_cmp_rev c HEAD~3 &&
- test_cmp_rev HEAD^2^ HEAD~2 &&
- test_revision_subjects 'd e n r' HEAD~2 HEAD^2 HEAD^ HEAD
-"
-
-test_expect_success "rebase -p re-creates empty merge commit" "
- reset_rebase &&
- git rebase -p o r &&
- test_cmp_rev e HEAD^2 &&
- test_cmp_rev o HEAD^ &&
- test_revision_subjects 'r' HEAD
-"
-
test_done
diff --git a/t/t3426-rebase-submodule.sh b/t/t3426-rebase-submodule.sh
index 0ad3a07..ba069dc 100755
--- a/t/t3426-rebase-submodule.sh
+++ b/t/t3426-rebase-submodule.sh
@@ -35,6 +35,7 @@ git_rebase_interactive () {
ls -1pR * >>actual &&
test_cmp expect actual &&
set_fake_editor &&
+ mkdir .git/info &&
echo "fake-editor.sh" >.git/info/exclude &&
may_only_be_test_must_fail "$2" &&
$2 git rebase -i "$1"
@@ -47,7 +48,8 @@ test_expect_success 'rebase interactive ignores modified submodules' '
git init sub &&
git -C sub commit --allow-empty -m "Initial commit" &&
git init super &&
- git -C super submodule add ../sub &&
+ git -c protocol.file.allow=always \
+ -C super submodule add ../sub &&
git -C super config submodule.sub.ignore dirty &&
>super/foo &&
git -C super add foo &&
diff --git a/t/t3427-rebase-subtree.sh b/t/t3427-rebase-subtree.sh
index e78c7e3..1b3e97c 100755
--- a/t/t3427-rebase-subtree.sh
+++ b/t/t3427-rebase-subtree.sh
@@ -36,11 +36,10 @@ commit_message() {
# where the root commit adds three files: topic_1.t, topic_2.t and topic_3.t.
#
# This commit history is then rebased onto `topic_3` with the
-# `-Xsubtree=files_subtree` option in three different ways:
+# `-Xsubtree=files_subtree` option in two different ways:
#
-# 1. using `--preserve-merges`
-# 2. using `--preserve-merges` and --keep-empty
-# 3. without specifying a rebase backend
+# 1. without specifying a rebase backend
+# 2. using the `--rebase-merges` backend
test_expect_success 'setup' '
test_commit README &&
@@ -69,34 +68,15 @@ test_expect_success 'setup' '
git commit -m "Empty commit" --allow-empty
'
-# FAILURE: Does not preserve topic_4.
-test_expect_failure REBASE_P 'Rebase -Xsubtree --preserve-merges --onto commit' '
- reset_rebase &&
- git checkout -b rebase-preserve-merges to-rebase &&
- git rebase -Xsubtree=files_subtree --preserve-merges --onto files-main main &&
- verbose test "$(commit_message HEAD~)" = "topic_4" &&
- verbose test "$(commit_message HEAD)" = "files_subtree/topic_5"
-'
-
-# FAILURE: Does not preserve topic_4.
-test_expect_failure REBASE_P 'Rebase -Xsubtree --keep-empty --preserve-merges --onto commit' '
- reset_rebase &&
- git checkout -b rebase-keep-empty to-rebase &&
- git rebase -Xsubtree=files_subtree --keep-empty --preserve-merges --onto files-main main &&
- verbose test "$(commit_message HEAD~2)" = "topic_4" &&
- verbose test "$(commit_message HEAD~)" = "files_subtree/topic_5" &&
- verbose test "$(commit_message HEAD)" = "Empty commit"
-'
-
test_expect_success 'Rebase -Xsubtree --empty=ask --onto commit' '
reset_rebase &&
git checkout -b rebase-onto to-rebase &&
test_must_fail git rebase -Xsubtree=files_subtree --empty=ask --onto files-main main &&
: first pick results in no changes &&
git rebase --skip &&
- verbose test "$(commit_message HEAD~2)" = "topic_4" &&
- verbose test "$(commit_message HEAD~)" = "files_subtree/topic_5" &&
- verbose test "$(commit_message HEAD)" = "Empty commit"
+ test "$(commit_message HEAD~2)" = "topic_4" &&
+ test "$(commit_message HEAD~)" = "files_subtree/topic_5" &&
+ test "$(commit_message HEAD)" = "Empty commit"
'
test_expect_success 'Rebase -Xsubtree --empty=ask --rebase-merges --onto commit' '
@@ -105,9 +85,9 @@ test_expect_success 'Rebase -Xsubtree --empty=ask --rebase-merges --onto commit'
test_must_fail git rebase -Xsubtree=files_subtree --empty=ask --rebase-merges --onto files-main --root &&
: first pick results in no changes &&
git rebase --skip &&
- verbose test "$(commit_message HEAD~2)" = "topic_4" &&
- verbose test "$(commit_message HEAD~)" = "files_subtree/topic_5" &&
- verbose test "$(commit_message HEAD)" = "Empty commit"
+ test "$(commit_message HEAD~2)" = "topic_4" &&
+ test "$(commit_message HEAD~)" = "files_subtree/topic_5" &&
+ test "$(commit_message HEAD)" = "Empty commit"
'
test_done
diff --git a/t/t3428-rebase-signoff.sh b/t/t3428-rebase-signoff.sh
index f6993b7..1bebd1c 100755
--- a/t/t3428-rebase-signoff.sh
+++ b/t/t3428-rebase-signoff.sh
@@ -5,49 +5,48 @@ test_description='git rebase --signoff
This test runs git rebase --signoff and make sure that it works.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
-# A simple file to commit
-cat >file <<EOF
-a
-EOF
+test_expect_success 'setup' '
+ git commit --allow-empty -m "Initial empty commit" &&
+ test_commit first file a &&
+
+ ident="$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" &&
-# Expected commit message for initial commit after rebase --signoff
-cat >expected-initial-signed <<EOF
-Initial empty commit
+ # Expected commit message for initial commit after rebase --signoff
+ cat >expected-initial-signed <<-EOF &&
+ Initial empty commit
-Signed-off-by: $(git var GIT_COMMITTER_IDENT | sed -e "s/>.*/>/")
-EOF
+ Signed-off-by: $ident
+ EOF
-# Expected commit message after rebase --signoff
-cat >expected-signed <<EOF
-first
+ # Expected commit message after rebase --signoff
+ cat >expected-signed <<-EOF &&
+ first
-Signed-off-by: $(git var GIT_COMMITTER_IDENT | sed -e "s/>.*/>/")
-EOF
+ Signed-off-by: $ident
+ EOF
-# Expected commit message after rebase without --signoff (or with --no-signoff)
-cat >expected-unsigned <<EOF
-first
-EOF
+ # Expected commit message after rebase without --signoff (or with --no-signoff)
+ cat >expected-unsigned <<-EOF &&
+ first
+ EOF
+ git config alias.rbs "rebase --signoff"
+'
# We configure an alias to do the rebase --signoff so that
# on the next subtest we can show that --no-signoff overrides the alias
-test_expect_success 'rebase --signoff adds a sign-off line' '
- git commit --allow-empty -m "Initial empty commit" &&
- git add file && git commit -m first &&
- git config alias.rbs "rebase --signoff" &&
- git rbs HEAD^ &&
- git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
- test_cmp expected-signed actual
+test_expect_success 'rebase --apply --signoff adds a sign-off line' '
+ git rbs --apply HEAD^ &&
+ test_commit_message HEAD expected-signed
'
test_expect_success 'rebase --no-signoff does not add a sign-off line' '
git commit --amend -m "first" &&
git rbs --no-signoff HEAD^ &&
- git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
- test_cmp expected-unsigned actual
+ test_commit_message HEAD expected-unsigned
'
test_expect_success 'rebase --exec --signoff adds a sign-off line' '
@@ -55,30 +54,25 @@ test_expect_success 'rebase --exec --signoff adds a sign-off line' '
git commit --amend -m "first" &&
git rebase --exec "touch exec" --signoff HEAD^ &&
test_path_is_file exec &&
- git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
- test_cmp expected-signed actual
+ test_commit_message HEAD expected-signed
'
test_expect_success 'rebase --root --signoff adds a sign-off line' '
git commit --amend -m "first" &&
git rebase --root --keep-empty --signoff &&
- git cat-file commit HEAD^ | sed -e "1,/^\$/d" >actual &&
- test_cmp expected-initial-signed actual &&
- git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
- test_cmp expected-signed actual
+ test_commit_message HEAD^ expected-initial-signed &&
+ test_commit_message HEAD expected-signed
'
test_expect_success 'rebase -i --signoff fails' '
git commit --amend -m "first" &&
git rebase -i --signoff HEAD^ &&
- git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
- test_cmp expected-signed actual
+ test_commit_message HEAD expected-signed
'
test_expect_success 'rebase -m --signoff fails' '
git commit --amend -m "first" &&
git rebase -m --signoff HEAD^ &&
- git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
- test_cmp expected-signed actual
+ test_commit_message HEAD expected-signed
'
test_done
diff --git a/t/t3429-rebase-edit-todo.sh b/t/t3429-rebase-edit-todo.sh
index 7024d49..8e0d039 100755
--- a/t/t3429-rebase-edit-todo.sh
+++ b/t/t3429-rebase-edit-todo.sh
@@ -2,6 +2,7 @@
test_description='rebase should reread the todo file if an exec modifies it'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-rebase.sh
@@ -13,10 +14,15 @@ test_expect_success 'setup' '
test_expect_success 'rebase exec modifies rebase-todo' '
todo=.git/rebase-merge/git-rebase-todo &&
- git rebase HEAD -x "echo exec touch F >>$todo" &&
+ git rebase HEAD~1 -x "echo exec touch F >>$todo" &&
test -e F
'
+test_expect_success 'rebase exec with an empty list does not exec anything' '
+ git rebase HEAD -x "true" 2>output &&
+ ! grep "Executing: true" output
+'
+
test_expect_success 'loose object cache vs re-reading todo list' '
GIT_REBASE_TODO=.git/rebase-merge/git-rebase-todo &&
export GIT_REBASE_TODO &&
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index 43c82d9..59b5d6b 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -128,14 +128,41 @@ test_expect_success 'generate correct todo list' '
'
test_expect_success '`reset` refuses to overwrite untracked files' '
- git checkout -b refuse-to-reset &&
+ git checkout B &&
test_commit dont-overwrite-untracked &&
- git checkout @{-1} &&
- : >dont-overwrite-untracked.t &&
- echo "reset refs/tags/dont-overwrite-untracked" >script-from-scratch &&
+ cat >script-from-scratch <<-EOF &&
+ exec >dont-overwrite-untracked.t
+ pick $(git rev-parse B) B
+ reset refs/tags/dont-overwrite-untracked
+ pick $(git rev-parse C) C
+ exec cat .git/rebase-merge/done >actual
+ EOF
test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
- test_must_fail git rebase -ir HEAD &&
- git rebase --abort
+ test_must_fail git rebase -ir A &&
+ test_cmp_rev HEAD B &&
+ head -n3 script-from-scratch >expect &&
+ test_cmp expect .git/rebase-merge/done &&
+ rm dont-overwrite-untracked.t &&
+ git rebase --continue &&
+ tail -n3 script-from-scratch >>expect &&
+ test_cmp expect actual
+'
+
+test_expect_success '`reset` rejects trees' '
+ test_when_finished "test_might_fail git rebase --abort" &&
+ test_must_fail env GIT_SEQUENCE_EDITOR="echo reset A^{tree} >" \
+ git rebase -i B C >out 2>err &&
+ grep "object .* is a tree" err &&
+ test_must_be_empty out
+'
+
+test_expect_success '`reset` only looks for labels under refs/rewritten/' '
+ test_when_finished "test_might_fail git rebase --abort" &&
+ git branch refs/rewritten/my-label A &&
+ test_must_fail env GIT_SEQUENCE_EDITOR="echo reset my-label >" \
+ git rebase -i B C >out 2>err &&
+ grep "could not resolve ${SQ}my-label${SQ}" err &&
+ test_must_be_empty out
'
test_expect_success 'failed `merge -C` writes patch (may be rescheduled, too)' '
@@ -148,12 +175,16 @@ test_expect_success 'failed `merge -C` writes patch (may be rescheduled, too)' '
test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
test_tick &&
test_must_fail git rebase -ir HEAD &&
+ test_cmp_rev REBASE_HEAD H^0 &&
grep "^merge -C .* G$" .git/rebase-merge/done &&
grep "^merge -C .* G$" .git/rebase-merge/git-rebase-todo &&
- test_path_is_file .git/rebase-merge/patch &&
+ test_path_is_missing .git/rebase-merge/patch &&
+ echo changed >file1 &&
+ git add file1 &&
+ test_must_fail git rebase --continue 2>err &&
+ grep "error: you have staged changes in your working tree" err &&
: fail because of merge conflict &&
- rm G.t .git/rebase-merge/patch &&
git reset --hard conflicting-G &&
test_must_fail git rebase --continue &&
! grep "^merge -C .* G$" .git/rebase-merge/git-rebase-todo &&
@@ -233,6 +264,16 @@ test_expect_success 'with a branch tip that was cherry-picked already' '
EOF
'
+test_expect_success '--no-rebase-merges countermands --rebase-merges' '
+ git checkout -b no-rebase-merges E &&
+ git rebase --rebase-merges --no-rebase-merges C &&
+ test_cmp_graph C.. <<-\EOF
+ * B
+ * D
+ o C
+ EOF
+'
+
test_expect_success 'do not rebase cousins unless asked for' '
git checkout -b cousins main &&
before="$(git rev-parse --verify HEAD)" &&
@@ -251,6 +292,40 @@ test_expect_success 'do not rebase cousins unless asked for' '
EOF
'
+test_expect_success 'rebase.rebaseMerges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '
+ test_config rebase.rebaseMerges rebase-cousins &&
+ git checkout -b config-rebase-cousins main &&
+ git rebase HEAD^ &&
+ test_cmp_graph HEAD^.. <<-\EOF
+ * Merge the topic branch '\''onebranch'\''
+ |\
+ | * D
+ | * G
+ |/
+ o H
+ EOF
+'
+
+test_expect_success '--no-rebase-merges overrides rebase.rebaseMerges=no-rebase-cousins' '
+ test_config rebase.rebaseMerges no-rebase-cousins &&
+ git checkout -b override-config-no-rebase-cousins E &&
+ git rebase --no-rebase-merges C &&
+ test_cmp_graph C.. <<-\EOF
+ * B
+ * D
+ o C
+ EOF
+'
+
+test_expect_success '--rebase-merges overrides rebase.rebaseMerges=rebase-cousins' '
+ test_config rebase.rebaseMerges rebase-cousins &&
+ git checkout -b override-config-rebase-cousins E &&
+ before="$(git rev-parse --verify HEAD)" &&
+ test_tick &&
+ git rebase --rebase-merges C &&
+ test_cmp_rev HEAD $before
+'
+
test_expect_success 'refs/rewritten/* is worktree-local' '
git worktree add wt &&
cat >wt/script-from-scratch <<-\EOF &&
@@ -292,9 +367,9 @@ test_expect_success 'post-rewrite hook and fixups work for merges' '
git commit --fixup HEAD same2.t &&
fixup="$(git rev-parse HEAD)" &&
- mkdir -p .git/hooks &&
- test_when_finished "rm .git/hooks/post-rewrite" &&
- echo "cat >actual" | write_script .git/hooks/post-rewrite &&
+ test_hook post-rewrite <<-\EOF &&
+ cat >actual
+ EOF
test_tick &&
git rebase -i --autosquash -r HEAD^^^ &&
@@ -517,4 +592,23 @@ test_expect_success '--rebase-merges with message matched with onto label' '
EOF
'
+test_expect_success 'progress shows the correct total' '
+ git checkout -b progress H &&
+ git rebase --rebase-merges --force-rebase --verbose A 2> err &&
+ # Expecting "Rebasing (N/14)" here, no bogus total number
+ grep "^Rebasing.*/14.$" err >progress &&
+ test_line_count = 14 progress
+'
+
+test_expect_success 'truncate label names' '
+ commit=$(git commit-tree -p HEAD^ -p HEAD -m "0123456789 我 123" HEAD^{tree}) &&
+ git merge --ff-only $commit &&
+
+ done="$(git rev-parse --git-path rebase-merge/done)" &&
+ git -c rebase.maxLabelLength=14 rebase --rebase-merges -x "cp \"$done\" out" --root &&
+ grep "label 0123456789-我$" out &&
+ git -c rebase.maxLabelLength=13 rebase --rebase-merges -x "cp \"$done\" out" --root &&
+ grep "label 0123456789-$" out
+'
+
test_done
diff --git a/t/t3431-rebase-fork-point.sh b/t/t3431-rebase-fork-point.sh
index 4c98d99..0bb284d 100755
--- a/t/t3431-rebase-fork-point.sh
+++ b/t/t3431-rebase-fork-point.sh
@@ -8,6 +8,7 @@ test_description='git rebase --fork-point test'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# A---B---D---E (main)
@@ -50,7 +51,7 @@ test_rebase () {
test_rebase 'G F E D B A'
test_rebase 'G F D B A' --onto D
-test_rebase 'G F B A' --keep-base
+test_rebase 'G F C B A' --keep-base
test_rebase 'G F C E D B A' --no-fork-point
test_rebase 'G F C D B A' --no-fork-point --onto D
test_rebase 'G F C B A' --no-fork-point --keep-base
@@ -83,7 +84,7 @@ test_expect_success 'git rebase --fork-point with ambigous refname' '
test_expect_success '--fork-point and --root both given' '
test_must_fail git rebase --fork-point --root 2>err &&
- test_i18ngrep "cannot combine" err
+ test_grep "cannot be used together" err
'
test_expect_success 'rebase.forkPoint set to false' '
diff --git a/t/t3432-rebase-fast-forward.sh b/t/t3432-rebase-fast-forward.sh
index 5086e14..7f1a5dd 100755
--- a/t/t3432-rebase-fast-forward.sh
+++ b/t/t3432-rebase-fast-forward.sh
@@ -8,6 +8,7 @@ test_description='ensure rebase fast-forwards commits when possible'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t3433-rebase-across-mode-change.sh b/t/t3433-rebase-across-mode-change.sh
index 05df964..c8172b0 100755
--- a/t/t3433-rebase-across-mode-change.sh
+++ b/t/t3433-rebase-across-mode-change.sh
@@ -2,6 +2,7 @@
test_description='git rebase across mode change'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t3435-rebase-gpg-sign.sh b/t/t3435-rebase-gpg-sign.sh
index ec10766..6aa2aeb 100755
--- a/t/t3435-rebase-gpg-sign.sh
+++ b/t/t3435-rebase-gpg-sign.sh
@@ -64,13 +64,6 @@ test_rebase_gpg_sign ! true -i --no-gpg-sign
test_rebase_gpg_sign ! true -i --gpg-sign --no-gpg-sign
test_rebase_gpg_sign false -i --no-gpg-sign --gpg-sign
-test_expect_failure 'rebase -p --no-gpg-sign override commit.gpgsign' '
- git reset --hard merged &&
- git config commit.gpgsign true &&
- git rebase -p --no-gpg-sign --onto=one fork-point main &&
- test_must_fail git verify-commit HEAD
-'
-
test_expect_success 'rebase -r, merge strategy, --gpg-sign will sign commit' '
git reset --hard merged &&
test_unconfig commit.gpgsign &&
diff --git a/t/t3436-rebase-more-options.sh b/t/t3436-rebase-more-options.sh
index 4d10664..94671d3 100755
--- a/t/t3436-rebase-more-options.sh
+++ b/t/t3436-rebase-more-options.sh
@@ -82,6 +82,20 @@ test_expect_success '--committer-date-is-author-date works with merge backend' '
test_ctime_is_atime -1
'
+test_expect_success '--committer-date-is-author-date works when rewording' '
+ GIT_AUTHOR_DATE="@1234 +0300" git commit --amend --reset-author &&
+ (
+ set_fake_editor &&
+ FAKE_COMMIT_MESSAGE=edited \
+ FAKE_LINES="reword 1" \
+ git rebase -i --committer-date-is-author-date HEAD^
+ ) &&
+ test_write_lines edited "" >expect &&
+ git log --format="%B" -1 >actual &&
+ test_cmp expect actual &&
+ test_ctime_is_atime -1
+'
+
test_expect_success '--committer-date-is-author-date works with rebase -r' '
git checkout side &&
GIT_AUTHOR_DATE="@1234 +0300" git merge --no-ff commit3 &&
@@ -155,6 +169,21 @@ test_expect_success '--reset-author-date with --committer-date-is-author-date wo
test_atime_is_ignored -2
'
+test_expect_success 'reset-author-date with --committer-date-is-author-date works when rewording' '
+ GIT_AUTHOR_DATE="@1234 +0300" git commit --amend --reset-author &&
+ (
+ set_fake_editor &&
+ FAKE_COMMIT_MESSAGE=edited \
+ FAKE_LINES="reword 1" \
+ git rebase -i --committer-date-is-author-date \
+ --reset-author-date HEAD^
+ ) &&
+ test_write_lines edited "" >expect &&
+ git log --format="%B" -1 >actual &&
+ test_cmp expect actual &&
+ test_atime_is_ignored -1
+'
+
test_expect_success '--reset-author-date --committer-date-is-author-date works when forking merge' '
GIT_SEQUENCE_EDITOR="echo \"merge -C $(git rev-parse HEAD) commit3\">" \
PATH="./test-bin:$PATH" git rebase -i --strategy=test \
diff --git a/t/t3437-rebase-fixup-options.sh b/t/t3437-rebase-fixup-options.sh
index c023fef..7929e2e 100755
--- a/t/t3437-rebase-fixup-options.sh
+++ b/t/t3437-rebase-fixup-options.sh
@@ -14,27 +14,13 @@ to the "fixup" command that works with "fixup!", "fixup -C" works with
"amend!" upon --autosquash.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-rebase.sh
EMPTY=""
-# test_commit_message <rev> -m <msg>
-# test_commit_message <rev> <path>
-# Verify that the commit message of <rev> matches
-# <msg> or the content of <path>.
-test_commit_message () {
- git show --no-patch --pretty=format:%B "$1" >actual &&
- case "$2" in
- -m)
- echo "$3" >expect &&
- test_cmp expect actual ;;
- *)
- test_cmp "$2" actual ;;
- esac
-}
-
get_author () {
rev="$1" &&
git log -1 --pretty=format:"%an %ae %at" "$rev"
@@ -50,6 +36,7 @@ test_expect_success 'setup' '
body
EOF
+ test_commit initial &&
test_commit A A &&
test_commit B B &&
get_author HEAD >expected-author &&
@@ -208,4 +195,29 @@ test_expect_success 'fixup -C works upon --autosquash with amend!' '
actual-squash-message
'
+test_expect_success 'fixup -[Cc]<commit> works' '
+ test_when_finished "test_might_fail git rebase --abort" &&
+ cat >todo <<-\EOF &&
+ pick A
+ fixup -CA1
+ pick B
+ fixup -cA2
+ EOF
+ (
+ set_replace_editor todo &&
+ FAKE_COMMIT_MESSAGE="edited and fixed up" \
+ git rebase -i initial initial
+ ) &&
+ git log --pretty=format:%B initial.. >actual &&
+ cat >expect <<-EOF &&
+ edited and fixed up
+ $EMPTY
+ new subject
+ $EMPTY
+ new
+ body
+ EOF
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t3438-rebase-broken-files.sh b/t/t3438-rebase-broken-files.sh
new file mode 100755
index 0000000..821f08e
--- /dev/null
+++ b/t/t3438-rebase-broken-files.sh
@@ -0,0 +1,70 @@
+#!/bin/sh
+
+test_description='rebase behavior when on-disk files are broken'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'set up conflicting branches' '
+ test_commit base file &&
+ git checkout -b branch1 &&
+ test_commit one file &&
+ git checkout -b branch2 HEAD^ &&
+ test_commit two file
+'
+
+create_conflict () {
+ test_when_finished "git rebase --abort" &&
+ git checkout -B tmp branch2 &&
+ test_must_fail git rebase branch1
+}
+
+check_resolve_fails () {
+ echo resolved >file &&
+ git add file &&
+ test_must_fail git rebase --continue
+}
+
+for item in NAME EMAIL DATE
+do
+ test_expect_success "detect missing GIT_AUTHOR_$item" '
+ create_conflict &&
+
+ grep -v $item .git/rebase-merge/author-script >tmp &&
+ mv tmp .git/rebase-merge/author-script &&
+
+ check_resolve_fails
+ '
+done
+
+for item in NAME EMAIL DATE
+do
+ test_expect_success "detect duplicate GIT_AUTHOR_$item" '
+ create_conflict &&
+
+ grep -i $item .git/rebase-merge/author-script >tmp &&
+ cat tmp >>.git/rebase-merge/author-script &&
+
+ check_resolve_fails
+ '
+done
+
+test_expect_success 'unknown key in author-script' '
+ create_conflict &&
+
+ echo "GIT_AUTHOR_BOGUS=${SQ}whatever${SQ}" \
+ >>.git/rebase-merge/author-script &&
+
+ check_resolve_fails
+'
+
+test_expect_success POSIXPERM,SANITY 'unwritable rebased-patches does not leak' '
+ >.git/rebased-patches &&
+ chmod a-w .git/rebased-patches &&
+
+ git checkout -b side HEAD^ &&
+ test_commit unrelated &&
+ test_must_fail git rebase --apply --onto tmp HEAD^
+'
+
+test_done
diff --git a/t/t3500-cherry.sh b/t/t3500-cherry.sh
index 0458a58..78c3eac 100755
--- a/t/t3500-cherry.sh
+++ b/t/t3500-cherry.sh
@@ -16,46 +16,43 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
GIT_AUTHOR_EMAIL=bogus_email_address
export GIT_AUTHOR_EMAIL
-test_expect_success \
- 'prepare repository with topic branch, and check cherry finds the 2 patches from there' \
- 'echo First > A &&
- git update-index --add A &&
- test_tick &&
- git commit -m "Add A." &&
-
- git checkout -b my-topic-branch &&
-
- echo Second > B &&
- git update-index --add B &&
- test_tick &&
- git commit -m "Add B." &&
-
- echo AnotherSecond > C &&
- git update-index --add C &&
- test_tick &&
- git commit -m "Add C." &&
-
- git checkout -f main &&
- rm -f B C &&
-
- echo Third >> A &&
- git update-index A &&
- test_tick &&
- git commit -m "Modify A." &&
-
- expr "$(echo $(git cherry main my-topic-branch) )" : "+ [^ ]* + .*"
+test_expect_success 'prepare repository with topic branch, and check cherry finds the 2 patches from there' '
+ echo First > A &&
+ git update-index --add A &&
+ test_tick &&
+ git commit -m "Add A." &&
+
+ git checkout -b my-topic-branch &&
+
+ echo Second > B &&
+ git update-index --add B &&
+ test_tick &&
+ git commit -m "Add B." &&
+
+ echo AnotherSecond > C &&
+ git update-index --add C &&
+ test_tick &&
+ git commit -m "Add C." &&
+
+ git checkout -f main &&
+ rm -f B C &&
+
+ echo Third >> A &&
+ git update-index A &&
+ test_tick &&
+ git commit -m "Modify A." &&
+
+ expr "$(echo $(git cherry main my-topic-branch) )" : "+ [^ ]* + .*"
'
-test_expect_success \
- 'check that cherry with limit returns only the top patch'\
- 'expr "$(echo $(git cherry main my-topic-branch my-topic-branch^1) )" : "+ [^ ]*"
+test_expect_success 'check that cherry with limit returns only the top patch' '
+ expr "$(echo $(git cherry main my-topic-branch my-topic-branch^1) )" : "+ [^ ]*"
'
-test_expect_success \
- 'cherry-pick one of the 2 patches, and check cherry recognized one and only one as new' \
- 'git cherry-pick my-topic-branch^0 &&
- echo $(git cherry main my-topic-branch) &&
- expr "$(echo $(git cherry main my-topic-branch) )" : "+ [^ ]* - .*"
+test_expect_success 'cherry-pick one of the 2 patches, and check cherry recognized one and only one as new' '
+ git cherry-pick my-topic-branch^0 &&
+ echo $(git cherry main my-topic-branch) &&
+ expr "$(echo $(git cherry main my-topic-branch) )" : "+ [^ ]* - .*"
'
test_expect_success 'cherry ignores whitespace' '
diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh
index 4b5b607..411027f 100755
--- a/t/t3501-revert-cherry-pick.sh
+++ b/t/t3501-revert-cherry-pick.sh
@@ -1,25 +1,18 @@
#!/bin/sh
-test_description='test cherry-pick and revert with renames
-
- --
- + rename2: renames oops to opos
- + rename1: renames oops to spoo
- + added: adds extra line to oops
- ++ initial: has lines in oops
-
-'
+test_description='miscellaneous basic tests for cherry-pick and revert'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
for l in a b c d e f g h i j k l m n o
do
- echo $l$l$l$l$l$l$l$l$l
+ echo $l$l$l$l$l$l$l$l$l || return 1
done >oops &&
test_tick &&
@@ -50,7 +43,7 @@ test_expect_success 'cherry-pick --nonsense' '
git diff --exit-code HEAD &&
test_must_fail git cherry-pick --nonsense 2>msg &&
git diff --exit-code HEAD "$pos" &&
- test_i18ngrep "[Uu]sage:" msg
+ test_grep "[Uu]sage:" msg
'
test_expect_success 'revert --nonsense' '
@@ -59,15 +52,22 @@ test_expect_success 'revert --nonsense' '
git diff --exit-code HEAD &&
test_must_fail git revert --nonsense 2>msg &&
git diff --exit-code HEAD "$pos" &&
- test_i18ngrep "[Uu]sage:" msg
+ test_grep "[Uu]sage:" msg
'
+# the following two test cherry-pick and revert with renames
+#
+# --
+# + rename2: renames oops to opos
+# + rename1: renames oops to spoo
+# + added: adds extra line to oops
+# ++ initial: has lines in oops
+
test_expect_success 'cherry-pick after renaming branch' '
git checkout rename2 &&
git cherry-pick added &&
- test $(git rev-parse HEAD^) = $(git rev-parse rename2) &&
- test -f opos &&
+ test_cmp_rev rename2 HEAD^ &&
grep "Add extra line at the end" opos &&
git reflog -1 | grep cherry-pick
@@ -77,9 +77,9 @@ test_expect_success 'revert after renaming branch' '
git checkout rename1 &&
git revert added &&
- test $(git rev-parse HEAD^) = $(git rev-parse rename1) &&
- test -f spoo &&
- ! grep "Add extra line at the end" spoo &&
+ test_cmp_rev rename1 HEAD^ &&
+ test_path_is_file spoo &&
+ test_cmp_rev initial:oops HEAD:spoo &&
git reflog -1 | grep revert
'
@@ -99,16 +99,24 @@ test_expect_success 'revert forbidden on dirty working tree' '
echo content >extra_file &&
git add extra_file &&
test_must_fail git revert HEAD 2>errors &&
- test_i18ngrep "your local changes would be overwritten by " errors
+ test_grep "your local changes would be overwritten by " errors
'
test_expect_success 'cherry-pick on unborn branch' '
- git checkout --orphan unborn &&
+ git switch --orphan unborn &&
git rm --cached -r . &&
- rm -rf * &&
git cherry-pick initial &&
- git diff --quiet initial &&
+ git diff --exit-code initial &&
+ test_cmp_rev ! initial HEAD
+'
+
+test_expect_success 'cherry-pick on unborn branch with --allow-empty' '
+ git checkout --detach &&
+ git branch -D unborn &&
+ git switch --orphan unborn &&
+ git cherry-pick initial --allow-empty &&
+ git diff --exit-code initial &&
test_cmp_rev ! initial HEAD
'
@@ -159,6 +167,7 @@ test_expect_success 'cherry-pick works with dirty renamed file' '
'
test_expect_success 'advice from failed revert' '
+ test_when_finished "git reset --hard" &&
test_commit --no-tag "add dream" dream dream &&
dream_oid=$(git rev-parse --short HEAD) &&
cat <<-EOF >expected &&
@@ -169,9 +178,69 @@ test_expect_success 'advice from failed revert' '
hint: You can instead skip this commit with "git revert --skip".
hint: To abort and get back to the state before "git revert",
hint: run "git revert --abort".
+ hint: Disable this message with "git config advice.mergeConflict false"
EOF
test_commit --append --no-tag "double-add dream" dream dream &&
test_must_fail git revert HEAD^ 2>actual &&
test_cmp expected actual
'
+
+test_expect_subject () {
+ echo "$1" >expect &&
+ git log -1 --pretty=%s >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'titles of fresh reverts' '
+ test_commit --no-tag A file1 &&
+ test_commit --no-tag B file1 &&
+ git revert --no-edit HEAD &&
+ test_expect_subject "Revert \"B\"" &&
+ git revert --no-edit HEAD &&
+ test_expect_subject "Reapply \"B\"" &&
+ git revert --no-edit HEAD &&
+ test_expect_subject "Revert \"Reapply \"B\"\""
+'
+
+test_expect_success 'title of legacy double revert' '
+ test_commit --no-tag "Revert \"Revert \"B\"\"" file1 &&
+ git revert --no-edit HEAD &&
+ test_expect_subject "Revert \"Revert \"Revert \"B\"\"\""
+'
+
+test_expect_success 'identification of reverted commit (default)' '
+ test_commit to-ident &&
+ test_when_finished "git reset --hard to-ident" &&
+ git checkout --detach to-ident &&
+ git revert --no-edit HEAD &&
+ git cat-file commit HEAD >actual.raw &&
+ grep "^This reverts " actual.raw >actual &&
+ echo "This reverts commit $(git rev-parse HEAD^)." >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'identification of reverted commit (--reference)' '
+ git checkout --detach to-ident &&
+ git revert --reference --no-edit HEAD &&
+ git cat-file commit HEAD >actual.raw &&
+ grep "^This reverts " actual.raw >actual &&
+ echo "This reverts commit $(git show -s --pretty=reference HEAD^)." >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'identification of reverted commit (revert.reference)' '
+ git checkout --detach to-ident &&
+ git -c revert.reference=true revert --no-edit HEAD &&
+ git cat-file commit HEAD >actual.raw &&
+ grep "^This reverts " actual.raw >actual &&
+ echo "This reverts commit $(git show -s --pretty=reference HEAD^)." >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick is unaware of --reference (for now)' '
+ test_when_finished "git reset --hard" &&
+ test_must_fail git cherry-pick --reference HEAD 2>actual &&
+ grep "^usage: git cherry-pick" actual
+'
+
test_done
diff --git a/t/t3502-cherry-pick-merge.sh b/t/t3502-cherry-pick-merge.sh
index 5495eac..1b2c0d6 100755
--- a/t/t3502-cherry-pick-merge.sh
+++ b/t/t3502-cherry-pick-merge.sh
@@ -11,6 +11,7 @@ test_description='cherry picking and reverting a merge
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t3503-cherry-pick-root.sh b/t/t3503-cherry-pick-root.sh
index 95fe4fe..76d393d 100755
--- a/t/t3503-cherry-pick-root.sh
+++ b/t/t3503-cherry-pick-root.sh
@@ -5,6 +5,7 @@ test_description='test cherry-picking (and reverting) a root commit'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh
index eba3c38..9748443 100755
--- a/t/t3505-cherry-pick-empty.sh
+++ b/t/t3505-cherry-pick-empty.sh
@@ -84,7 +84,7 @@ test_expect_success 'cherry-pick a commit that becomes no-op (prep)' '
git commit -m "add file2 on the side"
'
-test_expect_success 'cherry-pick a no-op without --keep-redundant' '
+test_expect_success 'cherry-pick a no-op with neither --keep-redundant nor --empty' '
git reset --hard &&
git checkout fork^0 &&
test_must_fail git cherry-pick main
@@ -99,4 +99,53 @@ test_expect_success 'cherry-pick a no-op with --keep-redundant' '
test_cmp expect actual
'
+test_expect_success '--keep-redundant-commits is incompatible with operations' '
+ test_must_fail git cherry-pick HEAD 2>output &&
+ test_grep "The previous cherry-pick is now empty" output &&
+ test_must_fail git cherry-pick --keep-redundant-commits --continue 2>output &&
+ test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --continue" output &&
+ test_must_fail git cherry-pick --keep-redundant-commits --skip 2>output &&
+ test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --skip" output &&
+ test_must_fail git cherry-pick --keep-redundant-commits --abort 2>output &&
+ test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --abort" output &&
+ test_must_fail git cherry-pick --keep-redundant-commits --quit 2>output &&
+ test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --quit" output &&
+ git cherry-pick --abort
+'
+
+test_expect_success '--empty is incompatible with operations' '
+ test_must_fail git cherry-pick HEAD 2>output &&
+ test_grep "The previous cherry-pick is now empty" output &&
+ test_must_fail git cherry-pick --empty=stop --continue 2>output &&
+ test_grep "fatal: cherry-pick: --empty cannot be used with --continue" output &&
+ test_must_fail git cherry-pick --empty=stop --skip 2>output &&
+ test_grep "fatal: cherry-pick: --empty cannot be used with --skip" output &&
+ test_must_fail git cherry-pick --empty=stop --abort 2>output &&
+ test_grep "fatal: cherry-pick: --empty cannot be used with --abort" output &&
+ test_must_fail git cherry-pick --empty=stop --quit 2>output &&
+ test_grep "fatal: cherry-pick: --empty cannot be used with --quit" output &&
+ git cherry-pick --abort
+'
+
+test_expect_success 'cherry-pick a no-op with --empty=stop' '
+ git reset --hard &&
+ git checkout fork^0 &&
+ test_must_fail git cherry-pick --empty=stop main 2>output &&
+ test_grep "The previous cherry-pick is now empty" output
+'
+
+test_expect_success 'cherry-pick a no-op with --empty=drop' '
+ git reset --hard &&
+ git checkout fork^0 &&
+ git cherry-pick --empty=drop main &&
+ test_commit_message HEAD -m "add file2 on the side"
+'
+
+test_expect_success 'cherry-pick a no-op with --empty=keep' '
+ git reset --hard &&
+ git checkout fork^0 &&
+ git cherry-pick --empty=keep main &&
+ test_commit_message HEAD -m "add file2 on main"
+'
+
test_done
diff --git a/t/t3506-cherry-pick-ff.sh b/t/t3506-cherry-pick-ff.sh
index 7e11bd4..b71bad1 100755
--- a/t/t3506-cherry-pick-ff.sh
+++ b/t/t3506-cherry-pick-ff.sh
@@ -5,6 +5,7 @@ test_description='test cherry-picking with --ff option'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh
index 979e843..f3947b4 100755
--- a/t/t3507-cherry-pick-conflict.sh
+++ b/t/t3507-cherry-pick-conflict.sh
@@ -12,6 +12,7 @@ test_description='test cherry-pick and revert with conflicts
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_CREATE_REPO_NO_TEMPLATE=1
. ./test-lib.sh
pristine_detach () {
@@ -59,6 +60,7 @@ test_expect_success 'advice from failed cherry-pick' '
hint: You can instead skip this commit with "git cherry-pick --skip".
hint: To abort and get back to the state before "git cherry-pick",
hint: run "git cherry-pick --abort".
+ hint: Disable this message with "git config advice.mergeConflict false"
EOF
test_must_fail git cherry-pick picked 2>actual &&
@@ -73,6 +75,7 @@ test_expect_success 'advice from failed cherry-pick --no-commit' "
error: could not apply \$picked... picked
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
+ hint: Disable this message with \"git config advice.mergeConflict false\"
EOF
test_must_fail git cherry-pick --no-commit picked 2>actual &&
@@ -176,7 +179,7 @@ test_expect_success 'partial commit of cherry-pick fails' '
git add foo &&
test_must_fail git commit foo 2>err &&
- test_i18ngrep "cannot do a partial commit during a cherry-pick." err
+ test_grep "cannot do a partial commit during a cherry-pick." err
'
test_expect_success 'commit --amend of cherry-pick fails' '
@@ -187,7 +190,7 @@ test_expect_success 'commit --amend of cherry-pick fails' '
git add foo &&
test_must_fail git commit --amend 2>err &&
- test_i18ngrep "in the middle of a cherry-pick -- cannot amend." err
+ test_grep "in the middle of a cherry-pick -- cannot amend." err
'
test_expect_success 'successful final commit clears cherry-pick state' '
@@ -497,7 +500,7 @@ test_expect_success \
test_expect_success 'failed cherry-pick does not forget -s' '
pristine_detach initial &&
test_must_fail git cherry-pick -s picked &&
- test_i18ngrep -e "Signed-off-by" .git/MERGE_MSG
+ test_grep -e "Signed-off-by" .git/MERGE_MSG
'
test_expect_success 'commit after failed cherry-pick does not add duplicated -s' '
@@ -558,10 +561,11 @@ test_expect_success 'cherry-pick preserves sparse-checkout' '
echo \"/*\" >.git/info/sparse-checkout
git read-tree --reset -u HEAD
rm .git/info/sparse-checkout" &&
+ mkdir .git/info &&
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_grep ! "Changes not staged for commit:" actual
'
test_expect_success 'cherry-pick --continue remembers --keep-redundant-commits' '
diff --git a/t/t3508-cherry-pick-many-commits.sh b/t/t3508-cherry-pick-many-commits.sh
index e8375d1..2d53ce7 100755
--- a/t/t3508-cherry-pick-many-commits.sh
+++ b/t/t3508-cherry-pick-many-commits.sh
@@ -29,7 +29,7 @@ test_expect_success setup '
git add file1 &&
test_tick &&
git commit -m "$val" &&
- git tag $val
+ git tag $val || return 1
done
'
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
index 49010aa..7eb52b1 100755
--- a/t/t3510-cherry-pick-sequence.sh
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -90,6 +90,38 @@ test_expect_success 'cherry-pick persists opts correctly' '
test_cmp expect actual
'
+test_expect_success 'cherry-pick persists --empty=stop correctly' '
+ pristine_detach yetanotherpick &&
+ # Picking `anotherpick` forces a conflict so that we stop. That
+ # commit is then skipped, after which we pick `yetanotherpick`
+ # while already on `yetanotherpick` to cause an empty commit
+ test_must_fail git cherry-pick --empty=stop anotherpick yetanotherpick &&
+ test_must_fail git cherry-pick --skip 2>msg &&
+ test_grep "The previous cherry-pick is now empty" msg &&
+ rm msg &&
+ git cherry-pick --abort
+'
+
+test_expect_success 'cherry-pick persists --empty=drop correctly' '
+ pristine_detach yetanotherpick &&
+ # Picking `anotherpick` forces a conflict so that we stop. That
+ # commit is then skipped, after which we pick `yetanotherpick`
+ # while already on `yetanotherpick` to cause an empty commit
+ test_must_fail git cherry-pick --empty=drop anotherpick yetanotherpick &&
+ git cherry-pick --skip &&
+ test_cmp_rev yetanotherpick HEAD
+'
+
+test_expect_success 'cherry-pick persists --empty=keep correctly' '
+ pristine_detach yetanotherpick &&
+ # Picking `anotherpick` forces a conflict so that we stop. That
+ # commit is then skipped, after which we pick `yetanotherpick`
+ # while already on `yetanotherpick` to cause an empty commit
+ test_must_fail git cherry-pick --empty=keep anotherpick yetanotherpick &&
+ git cherry-pick --skip &&
+ test_cmp_rev yetanotherpick HEAD^
+'
+
test_expect_success 'revert persists opts correctly' '
pristine_detach initial &&
# to make sure that the session to revert a sequence
@@ -154,7 +186,7 @@ test_expect_success 'skip "empty" commit' '
pristine_detach picked &&
test_commit dummy foo d &&
test_must_fail git cherry-pick anotherpick 2>err &&
- test_i18ngrep "git cherry-pick --skip" err &&
+ test_grep "git cherry-pick --skip" err &&
git cherry-pick --skip &&
test_cmp_rev dummy HEAD
'
@@ -238,6 +270,7 @@ test_expect_success 'allow skipping commit but not abort for a new history' '
'
test_expect_success 'allow skipping stopped cherry-pick because of untracked file modifications' '
+ test_when_finished "rm unrelated" &&
pristine_detach initial &&
git rm --cached unrelated &&
git commit -m "untrack unrelated" &&
@@ -313,7 +346,7 @@ test_expect_success '--abort does not unsafely change HEAD' '
git reset --hard base &&
test_must_fail git cherry-pick picked anotherpick &&
git cherry-pick --abort 2>actual &&
- test_i18ngrep "You seem to have moved HEAD" actual &&
+ test_grep "You seem to have moved HEAD" actual &&
test_cmp_rev base HEAD
'
@@ -519,7 +552,7 @@ test_expect_success '--continue asks for help after resolving patch to nil' '
test_cmp_rev unrelatedpick CHERRY_PICK_HEAD &&
git checkout HEAD -- unrelated &&
test_must_fail git cherry-pick --continue 2>msg &&
- test_i18ngrep "The previous cherry-pick is now empty" msg
+ test_grep "The previous cherry-pick is now empty" msg
'
test_expect_success 'follow advice and skip nil patch' '
diff --git a/t/t3511-cherry-pick-x.sh b/t/t3511-cherry-pick-x.sh
index 84a587d..dd5d92e 100755
--- a/t/t3511-cherry-pick-x.sh
+++ b/t/t3511-cherry-pick-x.sh
@@ -2,6 +2,7 @@
test_description='Test cherry-pick -x and -s'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
pristine_detach () {
diff --git a/t/t3512-cherry-pick-submodule.sh b/t/t3512-cherry-pick-submodule.sh
index c657840..f22d1dd 100755
--- a/t/t3512-cherry-pick-submodule.sh
+++ b/t/t3512-cherry-pick-submodule.sh
@@ -16,6 +16,8 @@ fi
test_submodule_switch "cherry-pick"
test_expect_success 'unrelated submodule/file conflict is ignored' '
+ test_config_global protocol.file.allow always &&
+
test_create_repo sub &&
touch sub/file &&
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index bb9ef35..98259e2 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -265,7 +265,7 @@ test_expect_success 'choking "git rm" should not let it die with cruft (induce S
test_expect_success !MINGW 'choking "git rm" should not let it die with cruft (induce and check SIGPIPE)' '
choke_git_rm_setup &&
- OUT=$( ((trap "" PIPE; git rm -n "some-file-*"; echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=$( ((trap "" PIPE && git rm -n "some-file-*"; echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT" &&
test_path_is_missing .git/index.lock
'
@@ -274,12 +274,9 @@ test_expect_success 'Resolving by removal is not a warning-worthy event' '
git reset -q --hard &&
test_when_finished "rm -f .git/index.lock msg && git reset -q --hard" &&
blob=$(echo blob | git hash-object -w --stdin) &&
- for stage in 1 2 3
- do
- echo "100644 $blob $stage blob"
- done | git update-index --index-info &&
+ printf "100644 $blob %d\tblob\n" 1 2 3 | git update-index --index-info &&
git rm blob >msg 2>&1 &&
- test_i18ngrep ! "needs merge" msg &&
+ test_grep ! "needs merge" msg &&
test_must_fail git ls-files -s --error-unmatch blob
'
@@ -336,7 +333,7 @@ test_expect_success 'rm removes empty submodules from work tree' '
test_expect_success 'rm removes removed submodule from index and .gitmodules' '
git reset --hard &&
- git submodule update &&
+ git -c protocol.file.allow=always submodule update &&
rm -rf submod &&
git rm submod &&
git status -s -uno --ignore-submodules=none >actual &&
@@ -634,7 +631,7 @@ test_expect_success 'rm of a populated submodule with a .git directory migrates
test_path_is_missing submod/.git &&
git status -s -uno --ignore-submodules=none >actual &&
test_file_not_empty actual &&
- test_i18ngrep Migrating output.err
+ test_grep Migrating output.err
'
cat >expect.deepmodified <<EOF
@@ -642,6 +639,7 @@ cat >expect.deepmodified <<EOF
EOF
test_expect_success 'setup subsubmodule' '
+ test_config_global protocol.file.allow always &&
git reset --hard &&
git submodule update &&
(
@@ -724,7 +722,7 @@ test_expect_success "rm absorbs submodule's nested .git directory" '
test_path_is_missing submod/subsubmod/.git &&
git status -s -uno --ignore-submodules=none >actual &&
test_file_not_empty actual &&
- test_i18ngrep Migrating output.err
+ test_grep Migrating output.err
'
test_expect_success 'checking out a commit after submodule removal needs manual updates' '
@@ -733,7 +731,7 @@ test_expect_success 'checking out a commit after submodule removal needs manual
git submodule update &&
git checkout -q HEAD^ &&
git checkout -q main 2>actual &&
- test_i18ngrep "^warning: unable to rmdir '\''submod'\'':" actual &&
+ test_grep "^warning: unable to rmdir '\''submod'\'':" actual &&
git status -s submod >actual &&
echo "?? submod/" >expected &&
test_cmp expected actual &&
diff --git a/t/t3601-rm-pathspec-file.sh b/t/t3601-rm-pathspec-file.sh
index 7de21f8..7cef129 100755
--- a/t/t3601-rm-pathspec-file.sh
+++ b/t/t3601-rm-pathspec-file.sh
@@ -2,6 +2,7 @@
test_description='rm --pathspec-from-file'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_tick
@@ -66,14 +67,14 @@ test_expect_success 'error conditions' '
echo fileA.t >list &&
test_must_fail git rm --pathspec-from-file=list -- fileA.t 2>err &&
- test_i18ngrep -e "--pathspec-from-file is incompatible with pathspec arguments" err &&
+ test_grep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
test_must_fail git rm --pathspec-file-nul 2>err &&
- test_i18ngrep -e "--pathspec-file-nul requires --pathspec-from-file" err &&
+ test_grep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
>empty_list &&
test_must_fail git rm --pathspec-from-file=empty_list 2>err &&
- test_i18ngrep -e "No pathspec was given. Which files should I remove?" err
+ test_grep -e "No pathspec was given. Which files should I remove?" err
'
test_done
diff --git a/t/t3602-rm-sparse-checkout.sh b/t/t3602-rm-sparse-checkout.sh
index e9e9a15..08580fd 100755
--- a/t/t3602-rm-sparse-checkout.sh
+++ b/t/t3602-rm-sparse-checkout.sh
@@ -11,12 +11,15 @@ test_expect_success 'setup' "
git commit -m files &&
cat >sparse_error_header <<-EOF &&
- The following pathspecs didn't match any eligible path, but they do match index
- entries outside the current sparse checkout:
+ The following paths and/or pathspecs matched paths that exist
+ outside of your sparse-checkout definition, so will not be
+ updated in the index:
EOF
cat >sparse_hint <<-EOF &&
- hint: Disable or modify the sparsity rules if you intend to update such entries.
+ hint: If you intend to update such entries, try one of the following:
+ hint: * Use the --sparse option.
+ hint: * Disable or modify the sparsity rules.
hint: Disable this message with \"git config advice.updateSparsePath false\"
EOF
@@ -27,7 +30,7 @@ test_expect_success 'setup' "
for opt in "" -f --dry-run
do
test_expect_success "rm${opt:+ $opt} does not remove sparse entries" '
- git sparse-checkout set a &&
+ git sparse-checkout set --no-cone a &&
test_must_fail git rm $opt b 2>stderr &&
test_cmp b_error_and_hint stderr &&
git ls-files --error-unmatch b
@@ -39,7 +42,29 @@ test_expect_success 'recursive rm does not remove sparse entries' '
git sparse-checkout set sub/dir &&
git rm -r sub &&
git status --porcelain -uno >actual &&
- echo "D sub/dir/e" >expected &&
+ cat >expected <<-\EOF &&
+ D sub/dir/e
+ EOF
+ test_cmp expected actual &&
+
+ git rm --sparse -r sub &&
+ git status --porcelain -uno >actual2 &&
+ cat >expected2 <<-\EOF &&
+ D sub/d
+ D sub/dir/e
+ EOF
+ test_cmp expected2 actual2
+'
+
+test_expect_success 'recursive rm --sparse removes sparse entries' '
+ git reset --hard &&
+ git sparse-checkout set "sub/dir" &&
+ git rm --sparse -r sub &&
+ git status --porcelain -uno >actual &&
+ cat >expected <<-\EOF &&
+ D sub/d
+ D sub/dir/e
+ EOF
test_cmp expected actual
'
@@ -75,4 +100,40 @@ test_expect_success 'do not warn about sparse entries with --ignore-unmatch' '
git ls-files --error-unmatch b
'
+test_expect_success 'refuse to rm a non-skip-worktree path outside sparse cone' '
+ git reset --hard &&
+ git sparse-checkout set a &&
+ git update-index --no-skip-worktree b &&
+ test_must_fail git rm b 2>stderr &&
+ test_cmp b_error_and_hint stderr &&
+ git rm --sparse b 2>stderr &&
+ test_must_be_empty stderr &&
+ test_path_is_missing b
+'
+
+test_expect_success 'can remove files from non-sparse dir' '
+ git reset --hard &&
+ git sparse-checkout disable &&
+ mkdir -p w x/y &&
+ test_commit w/f &&
+ test_commit x/y/f &&
+
+ git sparse-checkout set --no-cone w !/x y/ &&
+ git rm w/f.t x/y/f.t 2>stderr &&
+ test_must_be_empty stderr
+'
+
+test_expect_success 'refuse to remove non-skip-worktree file from sparse dir' '
+ git reset --hard &&
+ git sparse-checkout disable &&
+ mkdir -p x/y/z &&
+ test_commit x/y/z/f &&
+ git sparse-checkout set --no-cone !/x y/ !x/y/z &&
+
+ git update-index --no-skip-worktree x/y/z/f.t &&
+ test_must_fail git rm x/y/z/f.t 2>stderr &&
+ echo x/y/z/f.t | cat sparse_error_header - sparse_hint >expect &&
+ test_cmp expect stderr
+'
+
test_done
diff --git a/t/t3650-replay-basics.sh b/t/t3650-replay-basics.sh
new file mode 100755
index 0000000..3896702
--- /dev/null
+++ b/t/t3650-replay-basics.sh
@@ -0,0 +1,198 @@
+#!/bin/sh
+
+test_description='basic git replay tests'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+GIT_AUTHOR_NAME=author@name
+GIT_AUTHOR_EMAIL=bogus@email@address
+export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
+
+test_expect_success 'setup' '
+ test_commit A &&
+ test_commit B &&
+
+ git switch -c topic1 &&
+ test_commit C &&
+ git switch -c topic2 &&
+ test_commit D &&
+ test_commit E &&
+ git switch topic1 &&
+ test_commit F &&
+ git switch -c topic3 &&
+ test_commit G &&
+ test_commit H &&
+ git switch -c topic4 main &&
+ test_commit I &&
+ test_commit J &&
+
+ git switch -c next main &&
+ test_commit K &&
+ git merge -m "Merge topic1" topic1 &&
+ git merge -m "Merge topic2" topic2 &&
+ git merge -m "Merge topic3" topic3 &&
+ >evil &&
+ git add evil &&
+ git commit --amend &&
+ git merge -m "Merge topic4" topic4 &&
+
+ git switch main &&
+ test_commit L &&
+ test_commit M &&
+
+ git switch -c conflict B &&
+ test_commit C.conflict C.t conflict
+'
+
+test_expect_success 'setup bare' '
+ git clone --bare . bare
+'
+
+test_expect_success 'using replay to rebase two branches, one on top of other' '
+ git replay --onto main topic1..topic2 >result &&
+
+ test_line_count = 1 result &&
+
+ git log --format=%s $(cut -f 3 -d " " result) >actual &&
+ test_write_lines E D M L B A >expect &&
+ test_cmp expect actual &&
+
+ printf "update refs/heads/topic2 " >expect &&
+ printf "%s " $(cut -f 3 -d " " result) >>expect &&
+ git rev-parse topic2 >>expect &&
+
+ test_cmp expect result
+'
+
+test_expect_success 'using replay on bare repo to rebase two branches, one on top of other' '
+ git -C bare replay --onto main topic1..topic2 >result-bare &&
+ test_cmp expect result-bare
+'
+
+test_expect_success 'using replay to rebase with a conflict' '
+ test_expect_code 1 git replay --onto topic1 B..conflict
+'
+
+test_expect_success 'using replay on bare repo to rebase with a conflict' '
+ test_expect_code 1 git -C bare replay --onto topic1 B..conflict
+'
+
+test_expect_success 'using replay to perform basic cherry-pick' '
+ # The differences between this test and previous ones are:
+ # --advance vs --onto
+ # 2nd field of result is refs/heads/main vs. refs/heads/topic2
+ # 4th field of result is hash for main instead of hash for topic2
+
+ git replay --advance main topic1..topic2 >result &&
+
+ test_line_count = 1 result &&
+
+ git log --format=%s $(cut -f 3 -d " " result) >actual &&
+ test_write_lines E D M L B A >expect &&
+ test_cmp expect actual &&
+
+ printf "update refs/heads/main " >expect &&
+ printf "%s " $(cut -f 3 -d " " result) >>expect &&
+ git rev-parse main >>expect &&
+
+ test_cmp expect result
+'
+
+test_expect_success 'using replay on bare repo to perform basic cherry-pick' '
+ git -C bare replay --advance main topic1..topic2 >result-bare &&
+ test_cmp expect result-bare
+'
+
+test_expect_success 'replay on bare repo fails with both --advance and --onto' '
+ test_must_fail git -C bare replay --advance main --onto main topic1..topic2 >result-bare
+'
+
+test_expect_success 'replay fails when both --advance and --onto are omitted' '
+ test_must_fail git replay topic1..topic2 >result
+'
+
+test_expect_success 'using replay to also rebase a contained branch' '
+ git replay --contained --onto main main..topic3 >result &&
+
+ test_line_count = 2 result &&
+ cut -f 3 -d " " result >new-branch-tips &&
+
+ git log --format=%s $(head -n 1 new-branch-tips) >actual &&
+ test_write_lines F C M L B A >expect &&
+ test_cmp expect actual &&
+
+ git log --format=%s $(tail -n 1 new-branch-tips) >actual &&
+ test_write_lines H G F C M L B A >expect &&
+ test_cmp expect actual &&
+
+ printf "update refs/heads/topic1 " >expect &&
+ printf "%s " $(head -n 1 new-branch-tips) >>expect &&
+ git rev-parse topic1 >>expect &&
+ printf "update refs/heads/topic3 " >>expect &&
+ printf "%s " $(tail -n 1 new-branch-tips) >>expect &&
+ git rev-parse topic3 >>expect &&
+
+ test_cmp expect result
+'
+
+test_expect_success 'using replay on bare repo to also rebase a contained branch' '
+ git -C bare replay --contained --onto main main..topic3 >result-bare &&
+ test_cmp expect result-bare
+'
+
+test_expect_success 'using replay to rebase multiple divergent branches' '
+ git replay --onto main ^topic1 topic2 topic4 >result &&
+
+ test_line_count = 2 result &&
+ cut -f 3 -d " " result >new-branch-tips &&
+
+ git log --format=%s $(head -n 1 new-branch-tips) >actual &&
+ test_write_lines E D M L B A >expect &&
+ test_cmp expect actual &&
+
+ git log --format=%s $(tail -n 1 new-branch-tips) >actual &&
+ test_write_lines J I M L B A >expect &&
+ test_cmp expect actual &&
+
+ printf "update refs/heads/topic2 " >expect &&
+ printf "%s " $(head -n 1 new-branch-tips) >>expect &&
+ git rev-parse topic2 >>expect &&
+ printf "update refs/heads/topic4 " >>expect &&
+ printf "%s " $(tail -n 1 new-branch-tips) >>expect &&
+ git rev-parse topic4 >>expect &&
+
+ test_cmp expect result
+'
+
+test_expect_success 'using replay on bare repo to rebase multiple divergent branches, including contained ones' '
+ git -C bare replay --contained --onto main ^main topic2 topic3 topic4 >result &&
+
+ test_line_count = 4 result &&
+ cut -f 3 -d " " result >new-branch-tips &&
+
+ >expect &&
+ for i in 2 1 3 4
+ do
+ printf "update refs/heads/topic$i " >>expect &&
+ printf "%s " $(grep topic$i result | cut -f 3 -d " ") >>expect &&
+ git -C bare rev-parse topic$i >>expect || return 1
+ done &&
+
+ test_cmp expect result &&
+
+ test_write_lines F C M L B A >expect1 &&
+ test_write_lines E D C M L B A >expect2 &&
+ test_write_lines H G F C M L B A >expect3 &&
+ test_write_lines J I M L B A >expect4 &&
+
+ for i in 1 2 3 4
+ do
+ git -C bare log --format=%s $(grep topic$i result | cut -f 3 -d " ") >actual &&
+ test_cmp expect$i actual || return 1
+ done
+'
+
+test_done
diff --git a/t/t3700-add.sh b/t/t3700-add.sh
index 4086e1e..839c904 100755
--- a/t/t3700-add.sh
+++ b/t/t3700-add.sh
@@ -5,8 +5,11 @@
test_description='Test of git add, including the -- option.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-unique-files.sh
+
# Test the file mode "$1" of the file "$2" in the index.
test_mode_in_index () {
case "$(git ls-files -s "$2")" in
@@ -21,17 +24,53 @@ test_mode_in_index () {
esac
}
-test_expect_success \
- 'Test of git add' \
- 'touch foo && git add foo'
+test_expect_success 'Test of git add' '
+ touch foo && git add foo
+'
-test_expect_success \
- 'Post-check that foo is in the index' \
- 'git ls-files foo | grep foo'
+test_expect_success 'Test with no pathspecs' '
+ cat >expect <<-EOF &&
+ Nothing specified, nothing added.
+ hint: Maybe you wanted to say ${SQ}git add .${SQ}?
+ hint: Disable this message with "git config advice.addEmptyPathspec false"
+ EOF
+ git add 2>actual &&
+ test_cmp expect actual
+'
-test_expect_success \
- 'Test that "git add -- -q" works' \
- 'touch -- -q && git add -- -q'
+test_expect_success 'Post-check that foo is in the index' '
+ git ls-files foo | grep foo
+'
+
+test_expect_success 'Test that "git add -- -q" works' '
+ touch -- -q && git add -- -q
+'
+
+BATCH_CONFIGURATION='-c core.fsync=loose-object -c core.fsyncmethod=batch'
+
+test_expect_success 'git add: core.fsyncmethod=batch' "
+ test_create_unique_files 2 4 files_base_dir1 &&
+ GIT_TEST_FSYNC=1 git $BATCH_CONFIGURATION add -- ./files_base_dir1/ &&
+ git ls-files --stage files_base_dir1/ |
+ test_parse_ls_files_stage_oids >added_files_oids &&
+
+ # We created 2 subdirs with 4 files each (8 files total) above
+ test_line_count = 8 added_files_oids &&
+ git cat-file --batch-check='%(objectname)' <added_files_oids >added_files_actual &&
+ test_cmp added_files_oids added_files_actual
+"
+
+test_expect_success 'git update-index: core.fsyncmethod=batch' "
+ test_create_unique_files 2 4 files_base_dir2 &&
+ find files_base_dir2 ! -type d -print | xargs git $BATCH_CONFIGURATION update-index --add -- &&
+ git ls-files --stage files_base_dir2 |
+ test_parse_ls_files_stage_oids >added_files2_oids &&
+
+ # We created 2 subdirs with 4 files each (8 files total) above
+ test_line_count = 8 added_files2_oids &&
+ git cat-file --batch-check='%(objectname)' <added_files2_oids >added_files2_actual &&
+ test_cmp added_files2_oids added_files2_actual
+"
test_expect_success \
'git add: Test that executable bit is not used if core.filemode=0' \
@@ -77,24 +116,32 @@ test_expect_success '.gitignore test setup' '
test_expect_success '.gitignore is honored' '
git add . &&
- ! (git ls-files | grep "\\.ig")
+ git ls-files >files &&
+ sed -n "/\\.ig/p" <files >actual &&
+ test_must_be_empty actual
'
test_expect_success 'error out when attempting to add ignored ones without -f' '
test_must_fail git add a.?? &&
- ! (git ls-files | grep "\\.ig")
+ git ls-files >files &&
+ sed -n "/\\.ig/p" <files >actual &&
+ test_must_be_empty actual
'
test_expect_success 'error out when attempting to add ignored ones without -f' '
test_must_fail git add d.?? &&
- ! (git ls-files | grep "\\.ig")
+ git ls-files >files &&
+ sed -n "/\\.ig/p" <files >actual &&
+ test_must_be_empty actual
'
test_expect_success 'error out when attempting to add ignored ones but add others' '
touch a.if &&
test_must_fail git add a.?? &&
- ! (git ls-files | grep "\\.ig") &&
- (git ls-files | grep a.if)
+ git ls-files >files &&
+ sed -n "/\\.ig/p" <files >actual &&
+ test_must_be_empty actual &&
+ grep a.if files
'
test_expect_success 'add ignored ones with -f' '
@@ -140,9 +187,9 @@ test_expect_success 'check correct prefix detection' '
test_expect_success 'git add with filemode=0, symlinks=0, and unmerged entries' '
for s in 1 2 3
do
- echo $s > stage$s
- echo "100755 $(git hash-object -w stage$s) $s file"
- echo "120000 $(printf $s | git hash-object -w -t blob --stdin) $s symlink"
+ echo $s > stage$s &&
+ echo "100755 $(git hash-object -w stage$s) $s file" &&
+ echo "120000 $(printf $s | git hash-object -w -t blob --stdin) $s symlink" || return 1
done | git update-index --index-info &&
git config core.filemode 0 &&
git config core.symlinks 0 &&
@@ -176,7 +223,7 @@ test_expect_success 'git add --refresh' '
git read-tree HEAD &&
case "$(git diff-index HEAD -- foo)" in
:100644" "*"M foo") echo pass;;
- *) echo fail; (exit 1);;
+ *) echo fail; false;;
esac &&
git add --refresh -- foo &&
test -z "$(git diff-index HEAD -- foo)"
@@ -247,14 +294,14 @@ test_expect_success POSIXPERM,SANITY 'git add (add.ignore-errors = false)' '
rm -f foo2
test_expect_success POSIXPERM,SANITY '--no-ignore-errors overrides config' '
- git config add.ignore-errors 1 &&
- git reset --hard &&
- date >foo1 &&
- date >foo2 &&
- chmod 0 foo2 &&
- test_must_fail git add --verbose --no-ignore-errors . &&
- ! ( git ls-files foo1 | grep foo1 ) &&
- git config add.ignore-errors 0
+ git config add.ignore-errors 1 &&
+ git reset --hard &&
+ date >foo1 &&
+ date >foo2 &&
+ chmod 0 foo2 &&
+ test_must_fail git add --verbose --no-ignore-errors . &&
+ ! ( git ls-files foo1 | grep foo1 ) &&
+ git config add.ignore-errors 0
'
rm -f foo2
@@ -262,7 +309,7 @@ test_expect_success BSLASHPSPEC "git add 'fo\\[ou\\]bar' ignores foobar" '
git reset --hard &&
touch fo\[ou\]bar foobar &&
git add '\''fo\[ou\]bar'\'' &&
- git ls-files fo\[ou\]bar | fgrep fo\[ou\]bar &&
+ git ls-files fo\[ou\]bar | grep -F fo\[ou\]bar &&
! ( git ls-files foobar | grep foobar )
'
@@ -302,6 +349,40 @@ test_expect_success '"git add ." in empty repo' '
)
'
+test_expect_success '"git add" a embedded repository' '
+ rm -fr outer && git init outer &&
+ (
+ cd outer &&
+ for i in 1 2
+ do
+ name=inner$i &&
+ git init $name &&
+ git -C $name commit --allow-empty -m $name ||
+ return 1
+ done &&
+ git add . 2>actual &&
+ cat >expect <<-EOF &&
+ warning: adding embedded git repository: inner1
+ hint: You${SQ}ve added another git repository inside your current repository.
+ hint: Clones of the outer repository will not contain the contents of
+ hint: the embedded repository and will not know how to obtain it.
+ hint: If you meant to add a submodule, use:
+ hint:
+ hint: git submodule add <url> inner1
+ hint:
+ hint: If you added this path by mistake, you can remove it from the
+ hint: index with:
+ hint:
+ hint: git rm --cached inner1
+ hint:
+ hint: See "git help submodule" for more information.
+ hint: Disable this message with "git config advice.addEmbeddedRepo false"
+ warning: adding embedded git repository: inner2
+ EOF
+ test_cmp expect actual
+ )
+'
+
test_expect_success 'error on a repository with no commits' '
rm -fr empty &&
git init empty &&
@@ -333,8 +414,7 @@ cat >expect.err <<\EOF
The following paths are ignored by one of your .gitignore files:
ignored-file
hint: Use -f if you really want to add them.
-hint: Turn this message off by running
-hint: "git config advice.addIgnoredFile false"
+hint: Disable this message with "git config advice.addIgnoredFile false"
EOF
cat >expect.out <<\EOF
add 'track-this'
@@ -401,7 +481,7 @@ test_expect_success 'git add --chmod fails with non regular files (but updates t
test_ln_s_add foo foo3 &&
touch foo4 &&
test_must_fail git add --chmod=+x foo3 foo4 2>stderr &&
- test_i18ngrep "cannot chmod +x .foo3." stderr &&
+ test_grep "cannot chmod +x .foo3." stderr &&
test_mode_in_index 120000 foo3 &&
test_mode_in_index 100755 foo4
'
@@ -418,12 +498,12 @@ test_expect_success 'git add --chmod --dry-run reports error for non regular fil
git reset --hard &&
test_ln_s_add foo foo4 &&
test_must_fail git add --chmod=+x --dry-run foo4 2>stderr &&
- test_i18ngrep "cannot chmod +x .foo4." stderr
+ test_grep "cannot chmod +x .foo4." stderr
'
test_expect_success 'git add --chmod --dry-run reports error for unmatched pathspec' '
test_must_fail git add --chmod=+x --dry-run nonexistent 2>stderr &&
- test_i18ngrep "pathspec .nonexistent. did not match any files" stderr
+ test_grep "pathspec .nonexistent. did not match any files" stderr
'
test_expect_success 'no file status change if no pathspec is given' '
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index 2077146..04d8333 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -4,15 +4,10 @@ test_description='add -i basic tests'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
-if ! test_have_prereq PERL
-then
- skip_all='skipping add -i tests, perl not available'
- test_done
-fi
-
diff_cmp () {
for x
do
@@ -46,6 +41,21 @@ force_color () {
)
}
+test_expect_success 'warn about add.interactive.useBuiltin' '
+ cat >expect <<-\EOF &&
+ warning: the add.interactive.useBuiltin setting has been removed!
+ See its entry in '\''git help config'\'' for details.
+ No changes.
+ EOF
+
+ for v in = =true =false
+ do
+ git -c "add.interactive.useBuiltin$v" add -p >out 2>actual &&
+ test_must_be_empty out &&
+ test_cmp expect actual || return 1
+ done
+'
+
test_expect_success 'setup (initial)' '
echo content >file &&
git add file &&
@@ -103,6 +113,15 @@ test_expect_success 'status works (commit)' '
grep "+1/-0 *+2/-0 file" output
'
+test_expect_success 'update can stage deletions' '
+ >to-delete &&
+ git add to-delete &&
+ rm to-delete &&
+ test_write_lines u t "" | git add -i &&
+ git ls-files to-delete >output &&
+ test_must_be_empty output
+'
+
test_expect_success 'setup expected' '
cat >expected <<-\EOF
index 180b47c..b6f2c08 100644
@@ -287,9 +306,11 @@ test_expect_success FILEMODE 'stage mode and hunk' '
echo content >>file &&
chmod +x file &&
printf "y\\ny\\n" | git add -p &&
- git diff --cached file | grep "new mode" &&
- git diff --cached file | grep "+content" &&
- test -z "$(git diff file)"
+ git diff --cached file >out &&
+ grep "new mode" out &&
+ grep "+content" out &&
+ git diff file >out &&
+ test_must_be_empty out
'
# end of tests disabled when filemode is not usable
@@ -305,9 +326,9 @@ test_expect_success 'different prompts for mode change/deleted' '
git -c core.filemode=true add -p >actual &&
sed -n "s/^\(([0-9/]*) Stage .*?\).*/\1/p" actual >actual.filtered &&
cat >expect <<-\EOF &&
- (1/1) Stage deletion [y,n,q,a,d,?]?
- (1/2) Stage mode change [y,n,q,a,d,j,J,g,/,?]?
- (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]?
+ (1/1) Stage deletion [y,n,q,a,d,p,?]?
+ (1/2) Stage mode change [y,n,q,a,d,j,J,g,/,p,?]?
+ (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]?
EOF
test_cmp expect actual.filtered
'
@@ -315,18 +336,20 @@ test_expect_success 'different prompts for mode change/deleted' '
test_expect_success 'correct message when there is nothing to do' '
git reset --hard &&
git add -p 2>err &&
- test_i18ngrep "No changes" err &&
+ test_grep "No changes" err &&
printf "\\0123" >binary &&
git add binary &&
printf "\\0abc" >binary &&
git add -p 2>err &&
- test_i18ngrep "Only binary files changed" err
+ test_grep "Only binary files changed" err
'
test_expect_success 'setup again' '
git reset --hard &&
test_chmod +x file &&
- echo content >>file
+ echo content >>file &&
+ test_write_lines A B C D>file2 &&
+ git add file2
'
# Write the patch file with a new line at the top and bottom
@@ -341,13 +364,27 @@ test_expect_success 'setup patch' '
content
+lastline
\ No newline at end of file
+ diff --git a/file2 b/file2
+ index 8422d40..35b930a 100644
+ --- a/file2
+ +++ b/file2
+ @@ -1,4 +1,5 @@
+ -A
+ +Z
+ B
+ +Y
+ C
+ -D
+ +X
EOF
'
# Expected output, diff is similar to the patch but w/ diff at the top
test_expect_success 'setup expected' '
echo diff --git a/file b/file >expected &&
- cat patch |sed "/^index/s/ 100644/ 100755/" >>expected &&
+ sed -e "/^index 180b47c/s/ 100644/ 100755/" \
+ -e /1,5/s//1,4/ \
+ -e /Y/d patch >>expected &&
cat >expected-output <<-\EOF
--- a/file
+++ b/file
@@ -366,6 +403,28 @@ test_expect_success 'setup expected' '
content
+lastline
\ No newline at end of file
+ --- a/file2
+ +++ b/file2
+ @@ -1,4 +1,5 @@
+ -A
+ +Z
+ B
+ +Y
+ C
+ -D
+ +X
+ @@ -1,2 +1,2 @@
+ -A
+ +Z
+ B
+ @@ -2,2 +2,3 @@
+ B
+ +Y
+ C
+ @@ -3,2 +4,2 @@
+ C
+ -D
+ +X
EOF
'
@@ -373,9 +432,9 @@ test_expect_success 'setup expected' '
test_expect_success 'add first line works' '
git commit -am "clear local changes" &&
git apply patch &&
- printf "%s\n" s y y | git add -p file 2>error |
- sed -n -e "s/^([1-2]\/[1-2]) Stage this hunk[^@]*\(@@ .*\)/\1/" \
- -e "/^[-+@ \\\\]"/p >output &&
+ test_write_lines s y y s y n y | git add -p 2>error >raw-output &&
+ sed -n -e "s/^([1-9]\/[1-9]) Stage this hunk[^@]*\(@@ .*\)/\1/" \
+ -e "/^[-+@ \\\\]"/p raw-output >output &&
test_must_be_empty error &&
git diff --cached >diff &&
diff_cmp expected diff &&
@@ -439,7 +498,7 @@ test_expect_success 'adding an empty file' '
echo y | git checkout -p added-file -- >actual &&
test_path_is_file empty &&
- test_i18ngrep "Apply addition to index and worktree" actual
+ test_grep "Apply addition to index and worktree" actual
)
'
@@ -456,13 +515,13 @@ test_expect_success 'split hunk setup' '
test_expect_success 'goto hunk' '
test_when_finished "git reset" &&
tr _ " " >expect <<-EOF &&
- (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]? + 1: -1,2 +1,3 +15
+ (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? + 1: -1,2 +1,3 +15
_ 2: -2,4 +3,8 +21
go to which hunk? @@ -1,2 +1,3 @@
_10
+15
_20
- (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]?_
+ (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
EOF
test_write_lines s y g 1 | git add -p >actual &&
tail -n 7 <actual >actual.trimmed &&
@@ -472,11 +531,11 @@ test_expect_success 'goto hunk' '
test_expect_success 'navigate to hunk via regex' '
test_when_finished "git reset" &&
tr _ " " >expect <<-EOF &&
- (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]? @@ -1,2 +1,3 @@
+ (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? @@ -1,2 +1,3 @@
_10
+15
_20
- (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]?_
+ (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
EOF
test_write_lines s y /1,2 | git add -p >actual &&
tail -n 5 <actual >actual.trimmed &&
@@ -500,7 +559,7 @@ test_expect_success 'split hunk "add -p (edit)"' '
! grep "^+15" actual
'
-test_expect_failure 'split hunk "add -p (no, yes, edit)"' '
+test_expect_success 'split hunk "add -p (no, yes, edit)"' '
test_write_lines 5 10 20 21 30 31 40 50 60 >test &&
git reset &&
# test sequence is s(plit), n(o), y(es), e(dit)
@@ -524,7 +583,7 @@ test_expect_success 'split hunk with incomplete line at end' '
test_must_fail git grep --cached before
'
-test_expect_failure 'edit, adding lines to the first hunk' '
+test_expect_success 'edit, adding lines to the first hunk' '
test_write_lines 10 11 20 30 40 50 51 60 >test &&
git reset &&
tr _ " " >patch <<-EOF &&
@@ -657,25 +716,63 @@ test_expect_success 'colors can be overridden' '
<BLUE>+<RESET><BLUE>new<RESET>
<CYAN> more-context<RESET>
<BLUE>+<RESET><BLUE>another-one<RESET>
- <YELLOW>(1/1) Stage this hunk [y,n,q,a,d,s,e,?]? <RESET><BOLD>Split into 2 hunks.<RESET>
+ <YELLOW>(1/1) Stage this hunk [y,n,q,a,d,s,e,p,?]? <RESET><BOLD>Split into 2 hunks.<RESET>
<MAGENTA>@@ -1,3 +1,3 @@<RESET>
<CYAN> context<RESET>
<BOLD>-old<RESET>
<BLUE>+<RESET><BLUE>new<RESET>
<CYAN> more-context<RESET>
- <YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]? <RESET><MAGENTA>@@ -3 +3,2 @@<RESET>
+ <YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET><MAGENTA>@@ -3 +3,2 @@<RESET>
<CYAN> more-context<RESET>
<BLUE>+<RESET><BLUE>another-one<RESET>
- <YELLOW>(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]? <RESET><MAGENTA>@@ -1,3 +1,3 @@<RESET>
+ <YELLOW>(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? <RESET><MAGENTA>@@ -1,3 +1,3 @@<RESET>
<CYAN> context<RESET>
<BOLD>-old<RESET>
<BLUE>+new<RESET>
<CYAN> more-context<RESET>
- <YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]? <RESET>
+ <YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET>
EOF
test_cmp expect actual
'
+test_expect_success 'brackets appear without color' '
+ git reset --hard &&
+ test_when_finished "git rm -f bracket-test" &&
+ test_write_lines context old more-context >bracket-test &&
+ git add bracket-test &&
+ test_write_lines context new more-context another-one >bracket-test &&
+
+ test_write_lines quit >input &&
+ git add -i >actual <input &&
+
+ sed "s/^|//" >expect <<-\EOF &&
+ | staged unstaged path
+ | 1: +3/-0 +2/-1 bracket-test
+ |
+ |*** Commands ***
+ | 1: [s]tatus 2: [u]pdate 3: [r]evert 4: [a]dd untracked
+ | 5: [p]atch 6: [d]iff 7: [q]uit 8: [h]elp
+ |What now> Bye.
+ EOF
+
+ test_cmp expect actual
+'
+
+test_expect_success 'colors can be skipped with color.ui=false' '
+ git reset --hard &&
+ test_when_finished "git rm -f color-test" &&
+ test_write_lines context old more-context >color-test &&
+ git add color-test &&
+ test_write_lines context new more-context another-one >color-test &&
+
+ test_write_lines help quit >input &&
+ force_color git \
+ -c color.ui=false \
+ add -i >actual.raw <input &&
+ test_decode_color <actual.raw >actual &&
+ test_cmp actual.raw actual
+'
+
test_expect_success 'colorized diffs respect diff.wsErrorHighlight' '
git reset --hard &&
@@ -706,9 +803,33 @@ test_expect_success 'detect bogus diffFilter output' '
git reset --hard &&
echo content >test &&
- test_config interactive.diffFilter "sed 1d" &&
+ test_config interactive.diffFilter "sed 6d" &&
+ printf y >y &&
+ force_color test_must_fail git add -p <y >output 2>&1 &&
+ grep "mismatched output" output
+'
+
+test_expect_success 'handle iffy colored hunk headers' '
+ git reset --hard &&
+
+ echo content >test &&
+ printf n >n &&
+ force_color git -c interactive.diffFilter="sed s/.*@@.*/XX/" \
+ add -p >output 2>&1 <n &&
+ grep "^XX$" output
+'
+
+test_expect_success 'handle very large filtered diff' '
+ git reset --hard &&
+ # The specific number here is not important, but it must
+ # be large enough that the output of "git diff --color"
+ # fills up the pipe buffer. 10,000 results in ~200k of
+ # colored output.
+ test_seq 10000 >test &&
+ test_config interactive.diffFilter cat &&
printf y >y &&
- force_color test_must_fail git add -p <y
+ force_color git add -p >output 2>&1 <y &&
+ git diff-files --exit-code -- test
'
test_expect_success 'diff.algorithm is passed to `git diff-files`' '
@@ -718,7 +839,7 @@ test_expect_success 'diff.algorithm is passed to `git diff-files`' '
git add file &&
echo changed >file &&
test_must_fail git -c diff.algorithm=bogus add -p 2>err &&
- test_i18ngrep "error: option diff-algorithm accepts " err
+ test_grep "error: option diff-algorithm accepts " err
'
test_expect_success 'patch-mode via -i prompts for files' '
@@ -876,6 +997,18 @@ test_expect_success 'status ignores dirty submodules (except HEAD)' '
! grep dirty-otherwise output
'
+test_expect_success 'handle submodules' '
+ echo 123 >>for-submodules/dirty-otherwise/initial.t &&
+
+ force_color git -C for-submodules add -p dirty-otherwise >output 2>&1 &&
+ grep "No changes" output &&
+
+ force_color git -C for-submodules add -p dirty-head >output 2>&1 <y &&
+ git -C for-submodules ls-files --stage dirty-head >actual &&
+ rev="$(git -C for-submodules/dirty-head rev-parse HEAD)" &&
+ grep "$rev" actual
+'
+
test_expect_success 'set up pathological context' '
git reset --hard &&
test_write_lines a a a a a a a a a a a >a &&
@@ -977,4 +1110,25 @@ test_expect_success 'show help from add--helper' '
test_cmp expect actual
'
+test_expect_success 'reset -p with unmerged files' '
+ test_when_finished "git checkout --force main" &&
+ test_commit one conflict &&
+ git checkout -B side HEAD^ &&
+ test_commit two conflict &&
+ test_must_fail git merge one &&
+
+ # this is a noop with only an unmerged entry
+ git reset -p &&
+
+ # add files that sort before and after unmerged entry
+ echo a >a &&
+ echo z >z &&
+ git add a z &&
+
+ # confirm that we can reset those files
+ printf "%s\n" y y | git reset -p &&
+ git diff-index --cached --diff-filter=u HEAD >staged &&
+ test_must_be_empty staged
+'
+
test_done
diff --git a/t/t3702-add-edit.sh b/t/t3702-add-edit.sh
index 6c67664..82bfb2f 100755
--- a/t/t3702-add-edit.sh
+++ b/t/t3702-add-edit.sh
@@ -4,6 +4,8 @@
#
test_description='add -e basic tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
@@ -98,7 +100,7 @@ EOF
echo "#!$SHELL_PATH" >fake-editor.sh
cat >> fake-editor.sh <<\EOF
-egrep -v '^index' "$1" >orig-patch &&
+grep -E -v '^index' "$1" >orig-patch &&
mv -f patch "$1"
EOF
diff --git a/t/t3703-add-magic-pathspec.sh b/t/t3703-add-magic-pathspec.sh
index 3ef525a..d840710 100755
--- a/t/t3703-add-magic-pathspec.sh
+++ b/t/t3703-add-magic-pathspec.sh
@@ -2,6 +2,7 @@
test_description='magic pathspec tests using git-add'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t3704-add-pathspec-file.sh b/t/t3704-add-pathspec-file.sh
index 9e35c1f..3aa59f6 100755
--- a/t/t3704-add-pathspec-file.sh
+++ b/t/t3704-add-pathspec-file.sh
@@ -2,6 +2,7 @@
test_description='add --pathspec-from-file'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_tick
@@ -137,23 +138,23 @@ test_expect_success 'error conditions' '
>empty_list &&
test_must_fail git add --pathspec-from-file=list --interactive 2>err &&
- test_i18ngrep -e "--pathspec-from-file is incompatible with --interactive/--patch" err &&
+ test_grep -e "options .--pathspec-from-file. and .--interactive/--patch. cannot be used together" err &&
test_must_fail git add --pathspec-from-file=list --patch 2>err &&
- test_i18ngrep -e "--pathspec-from-file is incompatible with --interactive/--patch" err &&
+ test_grep -e "options .--pathspec-from-file. and .--interactive/--patch. cannot be used together" err &&
test_must_fail git add --pathspec-from-file=list --edit 2>err &&
- test_i18ngrep -e "--pathspec-from-file is incompatible with --edit" err &&
+ test_grep -e "options .--pathspec-from-file. and .--edit. cannot be used together" err &&
test_must_fail git add --pathspec-from-file=list -- fileA.t 2>err &&
- test_i18ngrep -e "--pathspec-from-file is incompatible with pathspec arguments" err &&
+ test_grep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
test_must_fail git add --pathspec-file-nul 2>err &&
- test_i18ngrep -e "--pathspec-file-nul requires --pathspec-from-file" err &&
+ test_grep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
# This case succeeds, but still prints to stderr
git add --pathspec-from-file=empty_list 2>err &&
- test_i18ngrep -e "Nothing specified, nothing added." err
+ test_grep -e "Nothing specified, nothing added." err
'
test_done
diff --git a/t/t3705-add-sparse-checkout.sh b/t/t3705-add-sparse-checkout.sh
index 2b1fd0d..2bade9e 100755
--- a/t/t3705-add-sparse-checkout.sh
+++ b/t/t3705-add-sparse-checkout.sh
@@ -19,6 +19,8 @@ setup_sparse_entry () {
fi &&
git add sparse_entry &&
git update-index --skip-worktree sparse_entry &&
+ git config core.sparseCheckout false &&
+ git commit --allow-empty -m "ensure sparse_entry exists at HEAD" &&
SPARSE_ENTRY_BLOB=$(git rev-parse :sparse_entry)
}
@@ -36,14 +38,22 @@ setup_gitignore () {
EOF
}
+test_sparse_entry_unstaged () {
+ git diff --staged -- sparse_entry >diff &&
+ test_must_be_empty diff
+}
+
test_expect_success 'setup' "
cat >sparse_error_header <<-EOF &&
- The following pathspecs didn't match any eligible path, but they do match index
- entries outside the current sparse checkout:
+ The following paths and/or pathspecs matched paths that exist
+ outside of your sparse-checkout definition, so will not be
+ updated in the index:
EOF
cat >sparse_hint <<-EOF &&
- hint: Disable or modify the sparsity rules if you intend to update such entries.
+ hint: If you intend to update such entries, try one of the following:
+ hint: * Use the --sparse option.
+ hint: * Disable or modify the sparsity rules.
hint: Disable this message with \"git config advice.updateSparsePath false\"
EOF
@@ -55,6 +65,7 @@ test_expect_success 'git add does not remove sparse entries' '
setup_sparse_entry &&
rm sparse_entry &&
test_must_fail git add sparse_entry 2>stderr &&
+ test_sparse_entry_unstaged &&
test_cmp error_and_hint stderr &&
test_sparse_entry_unchanged
'
@@ -73,6 +84,7 @@ test_expect_success 'git add . does not remove sparse entries' '
rm sparse_entry &&
setup_gitignore &&
test_must_fail git add . 2>stderr &&
+ test_sparse_entry_unstaged &&
cat sparse_error_header >expect &&
echo . >>expect &&
@@ -88,6 +100,7 @@ do
setup_sparse_entry &&
echo modified >sparse_entry &&
test_must_fail git add $opt sparse_entry 2>stderr &&
+ test_sparse_entry_unstaged &&
test_cmp error_and_hint stderr &&
test_sparse_entry_unchanged
'
@@ -98,6 +111,7 @@ test_expect_success 'git add --refresh does not update sparse entries' '
git ls-files --debug sparse_entry | grep mtime >before &&
test-tool chmtime -60 sparse_entry &&
test_must_fail git add --refresh sparse_entry 2>stderr &&
+ test_sparse_entry_unstaged &&
test_cmp error_and_hint stderr &&
git ls-files --debug sparse_entry | grep mtime >after &&
test_cmp before after
@@ -106,16 +120,19 @@ test_expect_success 'git add --refresh does not update sparse entries' '
test_expect_success 'git add --chmod does not update sparse entries' '
setup_sparse_entry &&
test_must_fail git add --chmod=+x sparse_entry 2>stderr &&
+ test_sparse_entry_unstaged &&
test_cmp error_and_hint stderr &&
test_sparse_entry_unchanged &&
! test -x sparse_entry
'
test_expect_success 'git add --renormalize does not update sparse entries' '
+ test_when_finished rm .gitattributes &&
test_config core.autocrlf false &&
setup_sparse_entry "LINEONE\r\nLINETWO\r\n" &&
echo "sparse_entry text=auto" >.gitattributes &&
test_must_fail git add --renormalize sparse_entry 2>stderr &&
+ test_sparse_entry_unstaged &&
test_cmp error_and_hint stderr &&
test_sparse_entry_unchanged
'
@@ -124,6 +141,7 @@ test_expect_success 'git add --dry-run --ignore-missing warn on sparse path' '
setup_sparse_entry &&
rm sparse_entry &&
test_must_fail git add --dry-run --ignore-missing sparse_entry 2>stderr &&
+ test_sparse_entry_unstaged &&
test_cmp error_and_hint stderr &&
test_sparse_entry_unchanged
'
@@ -145,11 +163,74 @@ test_expect_success 'do not warn when pathspec matches dense entries' '
git ls-files --error-unmatch dense_entry
'
+test_expect_success 'git add fails outside of sparse-checkout definition' '
+ test_when_finished git sparse-checkout disable &&
+ test_commit a &&
+ git sparse-checkout init --no-cone &&
+ git sparse-checkout set a &&
+ echo >>sparse_entry &&
+
+ git update-index --no-skip-worktree sparse_entry &&
+ test_must_fail git add sparse_entry &&
+ test_sparse_entry_unstaged &&
+
+ test_must_fail git add --chmod=+x sparse_entry &&
+ test_sparse_entry_unstaged &&
+
+ test_must_fail git add --renormalize sparse_entry &&
+ test_sparse_entry_unstaged &&
+
+ # Avoid munging CRLFs to avoid an error message
+ git -c core.autocrlf=input add --sparse sparse_entry 2>stderr &&
+ test_must_be_empty stderr &&
+ git ls-files --stage >actual &&
+ grep "^100644 .*sparse_entry\$" actual &&
+
+ git add --sparse --chmod=+x sparse_entry 2>stderr &&
+ test_must_be_empty stderr &&
+ git ls-files --stage >actual &&
+ grep "^100755 .*sparse_entry\$" actual &&
+
+ git reset &&
+
+ # This will print a message over stderr on Windows.
+ git add --sparse --renormalize sparse_entry &&
+ git status --porcelain >actual &&
+ grep "^M sparse_entry\$" actual
+'
+
test_expect_success 'add obeys advice.updateSparsePath' '
setup_sparse_entry &&
test_must_fail git -c advice.updateSparsePath=false add sparse_entry 2>stderr &&
+ test_sparse_entry_unstaged &&
test_cmp sparse_entry_error stderr
'
+test_expect_success 'add allows sparse entries with --sparse' '
+ git sparse-checkout set --no-cone a &&
+ echo modified >sparse_entry &&
+ test_must_fail git add sparse_entry &&
+ test_sparse_entry_unchanged &&
+ git add --sparse sparse_entry 2>stderr &&
+ test_must_be_empty stderr
+'
+
+test_expect_success 'can add files from non-sparse dir' '
+ git sparse-checkout set w !/x y/ &&
+ mkdir -p w x/y &&
+ touch w/f x/y/f &&
+ git add w/f x/y/f 2>stderr &&
+ test_must_be_empty stderr
+'
+
+test_expect_success 'refuse to add non-skip-worktree file from sparse dir' '
+ git sparse-checkout set !/x y/ !x/y/z &&
+ mkdir -p x/y/z &&
+ touch x/y/z/f &&
+ test_must_fail git add x/y/z/f 2>stderr &&
+ echo x/y/z/f | cat sparse_error_header - sparse_hint >expect &&
+ test_cmp expect stderr
+'
+
test_done
diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh
index 0544d58..d3e428f 100755
--- a/t/t3800-mktag.sh
+++ b/t/t3800-mktag.sh
@@ -4,6 +4,7 @@
test_description='git mktag: tag object verify test'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
###########################################################
@@ -72,7 +73,8 @@ check_verify_failure () {
# Manually create the broken, we cannot do it with
# update-ref
- echo "$bad_tag" >"bad-tag/$tag_ref" &&
+ test-tool -C bad-tag ref-store main delete-refs 0 msg "$tag_ref" &&
+ test-tool -C bad-tag ref-store main update-ref msg "$tag_ref" $bad_tag $ZERO_OID REF_SKIP_OID_VERIFICATION &&
# Unlike fsck-ing unreachable content above, this
# will always fail.
@@ -83,7 +85,8 @@ check_verify_failure () {
# Make sure the earlier test created it for us
git rev-parse "$bad_tag" &&
- echo "$bad_tag" >"bad-tag/$tag_ref" &&
+ test-tool -C bad-tag ref-store main delete-refs 0 msg "$tag_ref" &&
+ test-tool -C bad-tag ref-store main update-ref msg "$tag_ref" $bad_tag $ZERO_OID REF_SKIP_OID_VERIFICATION &&
printf "%s tag\t%s\n" "$bad_tag" "$tag_ref" >expected &&
git -C bad-tag for-each-ref "$tag_ref" >actual &&
diff --git a/t/t3900-i18n-commit.sh b/t/t3900-i18n-commit.sh
index bfab245..f27d09c 100755
--- a/t/t3900-i18n-commit.sh
+++ b/t/t3900-i18n-commit.sh
@@ -45,7 +45,7 @@ test_expect_success 'UTF-8 invalid characters refused' '
printf "Commit message\n\nInvalid surrogate:\355\240\200\n" \
>"$HOME/invalid" &&
git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
- test_i18ngrep "did not conform" "$HOME"/stderr
+ test_grep "did not conform" "$HOME"/stderr
'
test_expect_success 'UTF-8 overlong sequences rejected' '
@@ -55,7 +55,7 @@ test_expect_success 'UTF-8 overlong sequences rejected' '
printf "\340\202\251ommit message\n\nThis is not a space:\300\240\n" \
>"$HOME/invalid" &&
git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
- test_i18ngrep "did not conform" "$HOME"/stderr
+ test_grep "did not conform" "$HOME"/stderr
'
test_expect_success 'UTF-8 non-characters refused' '
@@ -64,7 +64,7 @@ test_expect_success 'UTF-8 non-characters refused' '
printf "Commit message\n\nNon-character:\364\217\277\276\n" \
>"$HOME/invalid" &&
git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
- test_i18ngrep "did not conform" "$HOME"/stderr
+ test_grep "did not conform" "$HOME"/stderr
'
test_expect_success 'UTF-8 non-characters refused' '
@@ -73,7 +73,7 @@ test_expect_success 'UTF-8 non-characters refused' '
printf "Commit message\n\nNon-character:\357\267\220\n" \
>"$HOME/invalid" &&
git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
- test_i18ngrep "did not conform" "$HOME"/stderr
+ test_grep "did not conform" "$HOME"/stderr
'
for H in ISO8859-1 eucJP ISO-2022-JP
diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh
index 4f16a73..4b37f78 100755
--- a/t/t3901-i18n-patch.sh
+++ b/t/t3901-i18n-patch.sh
@@ -298,7 +298,7 @@ test_expect_success 'am --no-utf8 (U/L)' '
# commit-tree will warn that the commit message does not contain valid UTF-8
# as mailinfo did not convert it
- test_i18ngrep "did not conform" err &&
+ test_grep "did not conform" err &&
check_encoding 2
'
diff --git a/t/t3902-quoted.sh b/t/t3902-quoted.sh
index f528008..72a5a56 100755
--- a/t/t3902-quoted.sh
+++ b/t/t3902-quoted.sh
@@ -5,6 +5,7 @@
test_description='quoted output'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
FN='濱野'
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 873aa56..00db82f 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -9,6 +9,26 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-unique-files.sh
+
+test_expect_success 'usage on cmd and subcommand invalid option' '
+ test_expect_code 129 git stash --invalid-option 2>usage &&
+ grep "or: git stash" usage &&
+
+ test_expect_code 129 git stash push --invalid-option 2>usage &&
+ ! grep "or: git stash" usage
+'
+
+test_expect_success 'usage on main command -h emits a summary of subcommands' '
+ test_expect_code 129 git stash -h >usage &&
+ grep -F "usage: git stash list" usage &&
+ grep -F "or: git stash show" usage
+'
+
+test_expect_success 'usage for subcommands should emit subcommand usage' '
+ test_expect_code 129 git stash push -h >usage &&
+ grep -F "usage: git stash [push" usage
+'
diff_cmp () {
for i in "$1" "$2"
@@ -22,7 +42,7 @@ diff_cmp () {
rm -f "$1.compare" "$2.compare"
}
-test_expect_success 'stash some dirty working directory' '
+setup_stash() {
echo 1 >file &&
git add file &&
echo unrelated >other-file &&
@@ -36,6 +56,10 @@ test_expect_success 'stash some dirty working directory' '
git stash &&
git diff-files --quiet &&
git diff-index --cached --quiet HEAD
+}
+
+test_expect_success 'stash some dirty working directory' '
+ setup_stash
'
cat >expect <<EOF
@@ -166,6 +190,43 @@ test_expect_success 'drop middle stash by index' '
test 1 = $(git show HEAD:file)
'
+test_expect_success 'drop stash reflog updates refs/stash' '
+ git reset --hard &&
+ git rev-parse refs/stash >expect &&
+ echo 9 >file &&
+ git stash &&
+ git stash drop stash@{0} &&
+ git rev-parse refs/stash >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'drop stash reflog updates refs/stash with rewrite' '
+ git init repo &&
+ (
+ cd repo &&
+ setup_stash
+ ) &&
+ echo 9 >repo/file &&
+
+ old_oid="$(git -C repo rev-parse stash@{0})" &&
+ git -C repo stash &&
+ new_oid="$(git -C repo rev-parse stash@{0})" &&
+
+ cat >expect <<-EOF &&
+ $new_oid
+ $old_oid
+ EOF
+ git -C repo reflog show refs/stash --format=%H >actual &&
+ test_cmp expect actual &&
+
+ git -C repo stash drop stash@{1} &&
+ git -C repo reflog show refs/stash --format=%H >actual &&
+ cat >expect <<-EOF &&
+ $new_oid
+ EOF
+ test_cmp expect actual
+'
+
test_expect_success 'stash pop' '
git reset --hard &&
git stash pop &&
@@ -242,6 +303,18 @@ test_expect_success 'apply -q is quiet' '
test_must_be_empty output.out
'
+test_expect_success 'apply --index -q is quiet' '
+ # Added file, deleted file, modified file all staged for commit
+ echo foo >new-file &&
+ echo test >file &&
+ git add new-file file &&
+ git rm other-file &&
+
+ git stash &&
+ git stash apply --index -q >output.out 2>&1 &&
+ test_must_be_empty output.out
+'
+
test_expect_success 'save -q is quiet' '
git stash save --quiet >output.out 2>&1 &&
test_must_be_empty output.out
@@ -272,6 +345,27 @@ test_expect_success 'drop -q is quiet' '
test_must_be_empty output.out
'
+test_expect_success 'stash push -q --staged refreshes the index' '
+ git reset --hard &&
+ echo test >file &&
+ git add file &&
+ git stash push -q --staged &&
+ git diff-files >output.out &&
+ test_must_be_empty output.out
+'
+
+test_expect_success 'stash apply -q --index refreshes the index' '
+ echo test >other-file &&
+ git add other-file &&
+ echo another-change >other-file &&
+ git diff-files >expect &&
+ git stash &&
+
+ git stash apply -q --index &&
+ git diff-files >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'stash -k' '
echo bar3 >file &&
echo bar4 >file2 &&
@@ -288,9 +382,20 @@ test_expect_success 'stash --no-keep-index' '
test bar,bar2 = $(cat file),$(cat file2)
'
+test_expect_success 'stash --staged' '
+ echo bar3 >file &&
+ echo bar4 >file2 &&
+ git add file2 &&
+ git stash --staged &&
+ test bar3,bar2 = $(cat file),$(cat file2) &&
+ git reset --hard &&
+ git stash pop &&
+ test bar,bar4 = $(cat file),$(cat file2)
+'
+
test_expect_success 'dont assume push with non-option args' '
test_must_fail git stash -q drop 2>err &&
- test_i18ngrep -e "subcommand wasn'\''t specified; '\''push'\'' can'\''t be assumed due to unexpected token '\''drop'\''" err
+ test_grep -e "subcommand wasn'\''t specified; '\''push'\'' can'\''t be assumed due to unexpected token '\''drop'\''" err
'
test_expect_success 'stash --invalid-option' '
@@ -360,10 +465,11 @@ test_expect_success SYMLINKS 'stash file to symlink' '
rm file &&
ln -s file2 file &&
git stash save "file to symlink" &&
- test -f file &&
+ test_path_is_file_not_symlink file &&
test bar = "$(cat file)" &&
git stash apply &&
- case "$(ls -l file)" in *" file -> file2") :;; *) false;; esac
+ test_path_is_symlink file &&
+ test "$(test_readlink file)" = file2
'
test_expect_success SYMLINKS 'stash file to symlink (stage rm)' '
@@ -371,10 +477,11 @@ test_expect_success SYMLINKS 'stash file to symlink (stage rm)' '
git rm file &&
ln -s file2 file &&
git stash save "file to symlink (stage rm)" &&
- test -f file &&
+ test_path_is_file_not_symlink file &&
test bar = "$(cat file)" &&
git stash apply &&
- case "$(ls -l file)" in *" file -> file2") :;; *) false;; esac
+ test_path_is_symlink file &&
+ test "$(test_readlink file)" = file2
'
test_expect_success SYMLINKS 'stash file to symlink (full stage)' '
@@ -383,10 +490,11 @@ test_expect_success SYMLINKS 'stash file to symlink (full stage)' '
ln -s file2 file &&
git add file &&
git stash save "file to symlink (full stage)" &&
- test -f file &&
+ test_path_is_file_not_symlink file &&
test bar = "$(cat file)" &&
git stash apply &&
- case "$(ls -l file)" in *" file -> file2") :;; *) false;; esac
+ test_path_is_symlink file &&
+ test "$(test_readlink file)" = file2
'
# This test creates a commit with a symlink used for the following tests
@@ -457,7 +565,7 @@ test_expect_failure 'stash directory to file' '
rm -fr dir &&
echo bar >dir &&
git stash save "directory to file" &&
- test -d dir &&
+ test_path_is_dir dir &&
test foo = "$(cat dir/file)" &&
test_must_fail git stash apply &&
test bar = "$(cat dir)" &&
@@ -470,10 +578,10 @@ test_expect_failure 'stash file to directory' '
mkdir file &&
echo foo >file/file &&
git stash save "file to directory" &&
- test -f file &&
+ test_path_is_file file &&
test bar = "$(cat file)" &&
git stash apply &&
- test -f file/file &&
+ test_path_is_file file/file &&
test foo = "$(cat file/file)"
'
@@ -488,7 +596,7 @@ test_expect_success 'giving too many ref arguments does not modify files' '
for type in apply pop "branch stash-branch"
do
test_must_fail git stash $type stash@{0} stash@{1} 2>err &&
- test_i18ngrep "Too many revisions" err &&
+ test_grep "Too many revisions" err &&
test 123456789 = $(test-tool chmtime -g file2) || return 1
done
'
@@ -496,14 +604,14 @@ test_expect_success 'giving too many ref arguments does not modify files' '
test_expect_success 'drop: too many arguments errors out (does nothing)' '
git stash list >expect &&
test_must_fail git stash drop stash@{0} stash@{1} 2>err &&
- test_i18ngrep "Too many revisions" err &&
+ test_grep "Too many revisions" err &&
git stash list >actual &&
test_cmp expect actual
'
test_expect_success 'show: too many arguments errors out (does nothing)' '
test_must_fail git stash show stash@{0} stash@{1} 2>err 1>out &&
- test_i18ngrep "Too many revisions" err &&
+ test_grep "Too many revisions" err &&
test_must_be_empty out
'
@@ -546,7 +654,7 @@ test_expect_success 'stash branch - stashes on stack, stash-like argument' '
test_expect_success 'stash branch complains with no arguments' '
test_must_fail git stash branch 2>err &&
- test_i18ngrep "No branch name specified" err
+ test_grep "No branch name specified" err
'
test_expect_success 'stash show format defaults to --stat' '
@@ -823,6 +931,10 @@ test_expect_success 'store called with invalid commit' '
test_must_fail git stash store foo
'
+test_expect_success 'store called with non-stash commit' '
+ test_must_fail git stash store HEAD
+'
+
test_expect_success 'store updates stash ref and reflog' '
git stash clear &&
git reset --hard &&
@@ -1012,6 +1124,17 @@ test_expect_success 'create stores correct message' '
test_cmp expect actual
'
+test_expect_success 'create when branch name has /' '
+ test_when_finished "git checkout main" &&
+ git checkout -b some/topic &&
+ >foo &&
+ git add foo &&
+ STASH_ID=$(git stash create "create test message") &&
+ echo "On some/topic: create test message" >expect &&
+ git show --pretty=%s -s ${STASH_ID} >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'create with multiple arguments for the message' '
>foo &&
git add foo &&
@@ -1092,19 +1215,19 @@ test_expect_success 'stash with file including $IFS character' '
'
test_expect_success 'stash with pathspec matching multiple paths' '
- echo original >file &&
- echo original >other-file &&
- git commit -m "two" file other-file &&
- echo modified >file &&
- echo modified >other-file &&
- git stash push -- "*file" &&
- echo original >expect &&
- test_cmp expect file &&
- test_cmp expect other-file &&
- git stash pop &&
- echo modified >expect &&
- test_cmp expect file &&
- test_cmp expect other-file
+ echo original >file &&
+ echo original >other-file &&
+ git commit -m "two" file other-file &&
+ echo modified >file &&
+ echo modified >other-file &&
+ git stash push -- "*file" &&
+ echo original >expect &&
+ test_cmp expect file &&
+ test_cmp expect other-file &&
+ git stash pop &&
+ echo modified >expect &&
+ test_cmp expect file &&
+ test_cmp expect other-file
'
test_expect_success 'stash push -p with pathspec shows no changes only once' '
@@ -1242,7 +1365,6 @@ test_expect_success 'stash works when user.name and user.email are not set' '
>2 &&
git add 2 &&
test_config user.useconfigonly true &&
- test_config stash.usebuiltin true &&
(
sane_unset GIT_AUTHOR_NAME &&
sane_unset GIT_AUTHOR_EMAIL &&
@@ -1293,18 +1415,157 @@ test_expect_success 'stash handles skip-worktree entries nicely' '
git rev-parse --verify refs/stash:A.t
'
-test_expect_success 'stash -c stash.useBuiltin=false warning ' '
- expected="stash.useBuiltin support has been removed" &&
- git -c stash.useBuiltin=false stash 2>err &&
- test_i18ngrep "$expected" err &&
- env GIT_TEST_STASH_USE_BUILTIN=false git stash 2>err &&
- test_i18ngrep "$expected" err &&
+BATCH_CONFIGURATION='-c core.fsync=loose-object -c core.fsyncmethod=batch'
+
+test_expect_success 'stash with core.fsyncmethod=batch' "
+ test_create_unique_files 2 4 files_base_dir &&
+ GIT_TEST_FSYNC=1 git $BATCH_CONFIGURATION stash push -u -- ./files_base_dir/ &&
+
+ # The files were untracked, so use the third parent,
+ # which contains the untracked files
+ git ls-tree -r stash^3 -- ./files_base_dir/ |
+ test_parse_ls_tree_oids >stashed_files_oids &&
+
+ # We created 2 dirs with 4 files each (8 files total) above
+ test_line_count = 8 stashed_files_oids &&
+ git cat-file --batch-check='%(objectname)' <stashed_files_oids >stashed_files_actual &&
+ test_cmp stashed_files_oids stashed_files_actual
+"
+
+
+test_expect_success 'git stash succeeds despite directory/file change' '
+ test_create_repo directory_file_switch_v1 &&
+ (
+ cd directory_file_switch_v1 &&
+ test_commit init &&
- git -c stash.useBuiltin=true stash 2>err &&
- test_must_be_empty err &&
- env GIT_TEST_STASH_USE_BUILTIN=true git stash 2>err &&
- test_must_be_empty err
+ test_write_lines this file has some words >filler &&
+ git add filler &&
+ git commit -m filler &&
+
+ git rm filler &&
+ mkdir filler &&
+ echo contents >filler/file &&
+ git stash push
+ )
+'
+
+test_expect_success 'git stash can pop file -> directory saved changes' '
+ test_create_repo directory_file_switch_v2 &&
+ (
+ cd directory_file_switch_v2 &&
+ test_commit init &&
+
+ test_write_lines this file has some words >filler &&
+ git add filler &&
+ git commit -m filler &&
+
+ git rm filler &&
+ mkdir filler &&
+ echo contents >filler/file &&
+ cp filler/file expect &&
+ git stash push --include-untracked &&
+ git stash apply --index &&
+ test_cmp expect filler/file
+ )
+'
+
+test_expect_success 'git stash can pop directory -> file saved changes' '
+ test_create_repo directory_file_switch_v3 &&
+ (
+ cd directory_file_switch_v3 &&
+ test_commit init &&
+
+ mkdir filler &&
+ test_write_lines some words >filler/file1 &&
+ test_write_lines and stuff >filler/file2 &&
+ git add filler &&
+ git commit -m filler &&
+
+ git rm -rf filler &&
+ echo contents >filler &&
+ cp filler expect &&
+ git stash push --include-untracked &&
+ git stash apply --index &&
+ test_cmp expect filler
+ )
+'
+
+test_expect_success 'restore untracked files even when we hit conflicts' '
+ git init restore_untracked_after_conflict &&
+ (
+ cd restore_untracked_after_conflict &&
+
+ echo hi >a &&
+ echo there >b &&
+ git add . &&
+ git commit -m first &&
+ echo hello >a &&
+ echo something >c &&
+
+ git stash push --include-untracked &&
+
+ echo conflict >a &&
+ git add a &&
+ git commit -m second &&
+
+ test_must_fail git stash pop &&
+
+ test_path_is_file c
+ )
+'
+
+test_expect_success 'stash create reports a locked index' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A A.file &&
+ echo change >A.file &&
+ touch .git/index.lock &&
+
+ cat >expect <<-EOF &&
+ error: could not write index
+ EOF
+ test_must_fail git stash create 2>err &&
+ test_cmp expect err
+ )
+'
+
+test_expect_success 'stash push reports a locked index' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A A.file &&
+ echo change >A.file &&
+ touch .git/index.lock &&
+
+ cat >expect <<-EOF &&
+ error: could not write index
+ EOF
+ test_must_fail git stash push 2>err &&
+ test_cmp expect err
+ )
+'
+
+test_expect_success 'stash apply reports a locked index' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A A.file &&
+ echo change >A.file &&
+ git stash push &&
+ touch .git/index.lock &&
+
+ cat >expect <<-EOF &&
+ error: could not write index
+ EOF
+ test_must_fail git stash apply 2>err &&
+ test_cmp expect err
+ )
'
test_done
diff --git a/t/t3904-stash-patch.sh b/t/t3904-stash-patch.sh
index accfe38..368fc2a 100755
--- a/t/t3904-stash-patch.sh
+++ b/t/t3904-stash-patch.sh
@@ -3,12 +3,6 @@
test_description='stash -p'
. ./lib-patch-mode.sh
-if ! test_have_prereq PERL
-then
- skip_all='skipping stash -p tests, perl not available'
- test_done
-fi
-
test_expect_success 'setup' '
mkdir dir &&
echo parent > dir/foo &&
diff --git a/t/t3905-stash-include-untracked.sh b/t/t3905-stash-include-untracked.sh
index dd2cdcc..1289ae3 100755
--- a/t/t3905-stash-include-untracked.sh
+++ b/t/t3905-stash-include-untracked.sh
@@ -404,7 +404,7 @@ test_expect_success 'stash show --include-untracked errors on duplicate files' '
) &&
w_commit=$(git commit-tree -p HEAD -p "$i_commit" -p "$u_commit" -m "WIP on any-branch" "$tree") &&
test_must_fail git stash show --include-untracked "$w_commit" 2>err &&
- test_i18ngrep "worktree and untracked commit have duplicate entries: tracked" err
+ test_grep "worktree and untracked commit have duplicate entries: tracked" err
'
test_expect_success 'stash show --{include,only}-untracked on stashes without untracked entries' '
@@ -422,4 +422,10 @@ test_expect_success 'stash show --{include,only}-untracked on stashes without un
test_must_be_empty actual
'
+test_expect_success 'stash -u ignores sub-repository' '
+ test_when_finished "rm -rf sub-repo" &&
+ git init sub-repo &&
+ git stash -u
+'
+
test_done
diff --git a/t/t3906-stash-submodule.sh b/t/t3906-stash-submodule.sh
index a52e53d..0f7348e 100755
--- a/t/t3906-stash-submodule.sh
+++ b/t/t3906-stash-submodule.sh
@@ -36,7 +36,7 @@ setup_basic () {
git init main &&
(
cd main &&
- git submodule add ../sub &&
+ git -c protocol.file.allow=always submodule add ../sub &&
test_commit main_file
)
}
diff --git a/t/t3908-stash-in-worktree.sh b/t/t3908-stash-in-worktree.sh
index 2b2b366..347a89b 100755
--- a/t/t3908-stash-in-worktree.sh
+++ b/t/t3908-stash-in-worktree.sh
@@ -5,6 +5,7 @@
test_description='Test git stash in a worktree'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t3909-stash-pathspec-file.sh b/t/t3909-stash-pathspec-file.sh
index 55e050c..73f2dbd 100755
--- a/t/t3909-stash-pathspec-file.sh
+++ b/t/t3909-stash-pathspec-file.sh
@@ -88,13 +88,13 @@ test_expect_success 'error conditions' '
echo fileA.t >list &&
test_must_fail git stash push --pathspec-from-file=list --patch 2>err &&
- test_i18ngrep -e "--pathspec-from-file is incompatible with --patch" err &&
+ test_grep -e "options .--pathspec-from-file. and .--patch. cannot be used together" err &&
test_must_fail git stash push --pathspec-from-file=list -- fileA.t 2>err &&
- test_i18ngrep -e "--pathspec-from-file is incompatible with pathspec arguments" err &&
+ test_grep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
test_must_fail git stash push --pathspec-file-nul 2>err &&
- test_i18ngrep -e "--pathspec-file-nul requires --pathspec-from-file" err
+ test_grep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err
'
test_done
diff --git a/t/t3920-crlf-messages.sh b/t/t3920-crlf-messages.sh
index a8ad546..50ae222 100755
--- a/t/t3920-crlf-messages.sh
+++ b/t/t3920-crlf-messages.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='Test ref-filter and pretty APIs for commit and tag messages using CRLF'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
LIB_CRLF_BRANCHES=""
@@ -8,9 +10,9 @@ LIB_CRLF_BRANCHES=""
create_crlf_ref () {
branch="$1" &&
cat >.crlf-orig-$branch.txt &&
- cat .crlf-orig-$branch.txt | append_cr >.crlf-message-$branch.txt &&
+ append_cr <.crlf-orig-$branch.txt >.crlf-message-$branch.txt &&
grep 'Subject' .crlf-orig-$branch.txt | tr '\n' ' ' | sed 's/[ ]*$//' | tr -d '\n' >.crlf-subject-$branch.txt &&
- grep 'Body' .crlf-message-$branch.txt >.crlf-body-$branch.txt || true &&
+ grep 'Body' .crlf-orig-$branch.txt | append_cr >.crlf-body-$branch.txt &&
LIB_CRLF_BRANCHES="${LIB_CRLF_BRANCHES} ${branch}" &&
test_tick &&
hash=$(git commit-tree HEAD^{tree} -p HEAD -F .crlf-message-${branch}.txt) &&
@@ -70,7 +72,7 @@ test_crlf_subject_body_and_contents() {
for ref in ${LIB_CRLF_BRANCHES}
do
cat .crlf-${file}-\"\${ref}\".txt >>expect &&
- printf \"\n\" >>expect
+ printf \"\n\" >>expect || return 1
done &&
git $command_and_args --format=\"%${atom}\" >actual &&
test_cmp expect actual
@@ -90,12 +92,12 @@ test_expect_success 'branch: --verbose works with messages using CRLF' '
do
printf " " >>expect &&
cat .crlf-subject-${branch}.txt >>expect &&
- printf "\n" >>expect
+ printf "\n" >>expect || return 1
done &&
git branch -v >tmp &&
# Remove first two columns, and the line for the currently checked out branch
current=$(git branch --show-current) &&
- grep -v $current <tmp | awk "{\$1=\$2=\"\"}1" >actual &&
+ awk "/$current/ { next } { \$1 = \$2 = \"\" } 1" <tmp >actual &&
test_cmp expect actual
'
diff --git a/t/t4000-diff-format.sh b/t/t4000-diff-format.sh
index cce3349..8d50331 100755
--- a/t/t4000-diff-format.sh
+++ b/t/t4000-diff-format.sh
@@ -5,7 +5,12 @@
test_description='Test built-in diff output engine.
+We happen to know that all diff plumbing and diff Porcelain share the
+same command line parser, so testing one should be sufficient; pick
+diff-files as a representative.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-diff.sh
@@ -14,9 +19,11 @@ Line 2
line 3'
cat path0 >path1
chmod +x path1
+mkdir path2
+>path2/path3
test_expect_success 'update-index --add two files with and without +x.' '
- git update-index --add path0 path1
+ git update-index --add path0 path1 path2/path3
'
mv path0 path0-
@@ -89,4 +96,31 @@ test_expect_success 'git diff-files --patch --no-patch does not show the patch'
test_must_be_empty err
'
+
+# Smudge path2/path3 so that dirstat has something to show
+date >path2/path3
+
+for format in stat raw numstat shortstat summary \
+ dirstat cumulative dirstat-by-file \
+ patch-with-raw patch-with-stat compact-summary
+do
+ test_expect_success "--no-patch in 'git diff-files --no-patch --$format' is a no-op" '
+ git diff-files --no-patch "--$format" >actual &&
+ git diff-files "--$format" >expect &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "--no-patch clears all previous ones" '
+ git diff-files --$format -s -p >actual &&
+ git diff-files -p >expect &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "--no-patch in 'git diff --no-patch --$format' is a no-op" '
+ git diff --no-patch "--$format" >actual &&
+ git diff "--$format" >expect &&
+ test_cmp expect actual
+ '
+done
+
test_done
diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh
index 68f2ebc..49c042a 100755
--- a/t/t4001-diff-rename.sh
+++ b/t/t4001-diff-rename.sh
@@ -135,25 +135,25 @@ test_expect_success 'favour same basenames over different ones' '
mkdir subdir &&
git mv another-path subdir/path1 &&
git status >out &&
- test_i18ngrep "renamed: .*path1 -> subdir/path1" out
+ test_grep "renamed: .*path1 -> subdir/path1" out
'
test_expect_success 'test diff.renames=true for git status' '
git -c diff.renames=true status >out &&
- test_i18ngrep "renamed: .*path1 -> subdir/path1" out
+ test_grep "renamed: .*path1 -> subdir/path1" out
'
test_expect_success 'test diff.renames=false for git status' '
git -c diff.renames=false status >out &&
- test_i18ngrep ! "renamed: .*path1 -> subdir/path1" out &&
- test_i18ngrep "new file: .*subdir/path1" out &&
- test_i18ngrep "deleted: .*[^/]path1" out
+ test_grep ! "renamed: .*path1 -> subdir/path1" out &&
+ test_grep "new file: .*subdir/path1" out &&
+ test_grep "deleted: .*[^/]path1" out
'
test_expect_success 'favour same basenames even with minor differences' '
git show HEAD:path1 | sed "s/15/16/" > subdir/path1 &&
git status >out &&
- test_i18ngrep "renamed: .*path1 -> subdir/path1" out
+ test_grep "renamed: .*path1 -> subdir/path1" out
'
test_expect_success 'two files with same basename and same content' '
@@ -165,7 +165,7 @@ test_expect_success 'two files with same basename and same content' '
git commit -m 2 &&
git mv dir other-dir &&
git status >out &&
- test_i18ngrep "renamed: .*dir/A/file -> other-dir/A/file" out
+ test_grep "renamed: .*dir/A/file -> other-dir/A/file" out
'
test_expect_success 'setup for many rename source candidates' '
@@ -174,7 +174,7 @@ test_expect_success 'setup for many rename source candidates' '
do
for j in 0 1 2 3 4 5 6 7 8 9;
do
- echo "$i$j" >"path$i$j"
+ echo "$i$j" >"path$i$j" || return 1
done
done &&
git add "path??" &&
@@ -202,9 +202,9 @@ test_expect_success 'rename pretty print with nothing in common' '
git mv a/b/c c/b/a &&
git commit -m "a/b/c -> c/b/a" &&
git diff -M --summary HEAD^ HEAD >output &&
- test_i18ngrep " a/b/c => c/b/a " output &&
+ test_grep " a/b/c => c/b/a " output &&
git diff -M --stat HEAD^ HEAD >output &&
- test_i18ngrep " a/b/c => c/b/a " output
+ test_grep " a/b/c => c/b/a " output
'
test_expect_success 'rename pretty print with common prefix' '
@@ -212,9 +212,9 @@ test_expect_success 'rename pretty print with common prefix' '
git mv c/b/a c/d/e &&
git commit -m "c/b/a -> c/d/e" &&
git diff -M --summary HEAD^ HEAD >output &&
- test_i18ngrep " c/{b/a => d/e} " output &&
+ test_grep " c/{b/a => d/e} " output &&
git diff -M --stat HEAD^ HEAD >output &&
- test_i18ngrep " c/{b/a => d/e} " output
+ test_grep " c/{b/a => d/e} " output
'
test_expect_success 'rename pretty print with common suffix' '
@@ -222,9 +222,9 @@ test_expect_success 'rename pretty print with common suffix' '
git mv c/d/e d/e &&
git commit -m "c/d/e -> d/e" &&
git diff -M --summary HEAD^ HEAD >output &&
- test_i18ngrep " {c/d => d}/e " output &&
+ test_grep " {c/d => d}/e " output &&
git diff -M --stat HEAD^ HEAD >output &&
- test_i18ngrep " {c/d => d}/e " output
+ test_grep " {c/d => d}/e " output
'
test_expect_success 'rename pretty print with common prefix and suffix' '
@@ -232,9 +232,9 @@ test_expect_success 'rename pretty print with common prefix and suffix' '
git mv d/e d/f/e &&
git commit -m "d/e -> d/f/e" &&
git diff -M --summary HEAD^ HEAD >output &&
- test_i18ngrep " d/{ => f}/e " output &&
+ test_grep " d/{ => f}/e " output &&
git diff -M --stat HEAD^ HEAD >output &&
- test_i18ngrep " d/{ => f}/e " output
+ test_grep " d/{ => f}/e " output
'
test_expect_success 'rename pretty print common prefix and suffix overlap' '
@@ -242,9 +242,9 @@ test_expect_success 'rename pretty print common prefix and suffix overlap' '
git mv d/f/e d/f/f/e &&
git commit -m "d/f/e d/f/f/e" &&
git diff -M --summary HEAD^ HEAD >output &&
- test_i18ngrep " d/f/{ => f}/e " output &&
+ test_grep " d/f/{ => f}/e " output &&
git diff -M --stat HEAD^ HEAD >output &&
- test_i18ngrep " d/f/{ => f}/e " output
+ test_grep " d/f/{ => f}/e " output
'
test_expect_success 'diff-tree -l0 defaults to a big rename limit, not zero' '
@@ -286,4 +286,28 @@ test_expect_success 'basename similarity vs best similarity' '
test_cmp expected actual
'
+test_expect_success 'last line matters too' '
+ {
+ test_write_lines a 0 1 2 3 4 5 6 7 8 9 &&
+ printf "git ignores final up to 63 characters if not newline terminated"
+ } >no-final-lf &&
+ git add no-final-lf &&
+ git commit -m "original version of file with no final newline" &&
+
+ # Change ONLY the first character of the whole file
+ {
+ test_write_lines b 0 1 2 3 4 5 6 7 8 9 &&
+ printf "git ignores final up to 63 characters if not newline terminated"
+ } >no-final-lf &&
+ git add no-final-lf &&
+ git mv no-final-lf still-absent-final-lf &&
+ git commit -a -m "rename no-final-lf -> still-absent-final-lf" &&
+ git diff-tree -r -M --name-status HEAD^ HEAD >actual &&
+ sed -e "s/^R[0-9]* /R /" actual >actual.munged &&
+ cat >expected <<-\EOF &&
+ R no-final-lf still-absent-final-lf
+ EOF
+ test_cmp expected actual.munged
+'
+
test_done
diff --git a/t/t4002-diff-basic.sh b/t/t4002-diff-basic.sh
index 6a9f010..cb33070 100755
--- a/t/t4002-diff-basic.sh
+++ b/t/t4002-diff-basic.sh
@@ -6,6 +6,8 @@
test_description='Test diff raw-output.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-read-tree-m-3way.sh
@@ -282,132 +284,131 @@ cmp_diff_files_output () {
test_cmp "$1" .test-tmp
}
-test_expect_success \
- 'diff-tree of known trees.' \
- 'git diff-tree $tree_O $tree_A >.test-a &&
- cmp -s .test-a .test-plain-OA'
-
-test_expect_success \
- 'diff-tree of known trees.' \
- 'git diff-tree -r $tree_O $tree_A >.test-a &&
- cmp -s .test-a .test-recursive-OA'
-
-test_expect_success \
- 'diff-tree of known trees.' \
- 'git diff-tree $tree_O $tree_B >.test-a &&
- cmp -s .test-a .test-plain-OB'
-
-test_expect_success \
- 'diff-tree of known trees.' \
- 'git diff-tree -r $tree_O $tree_B >.test-a &&
- cmp -s .test-a .test-recursive-OB'
-
-test_expect_success \
- 'diff-tree of known trees.' \
- 'git diff-tree $tree_A $tree_B >.test-a &&
- cmp -s .test-a .test-plain-AB'
-
-test_expect_success \
- 'diff-tree of known trees.' \
- 'git diff-tree -r $tree_A $tree_B >.test-a &&
- cmp -s .test-a .test-recursive-AB'
-
-test_expect_success \
- 'diff-tree --stdin of known trees.' \
- 'echo $tree_A $tree_B | git diff-tree --stdin > .test-a &&
- echo $tree_A $tree_B > .test-plain-ABx &&
- cat .test-plain-AB >> .test-plain-ABx &&
- cmp -s .test-a .test-plain-ABx'
-
-test_expect_success \
- 'diff-tree --stdin of known trees.' \
- 'echo $tree_A $tree_B | git diff-tree -r --stdin > .test-a &&
- echo $tree_A $tree_B > .test-recursive-ABx &&
- cat .test-recursive-AB >> .test-recursive-ABx &&
- cmp -s .test-a .test-recursive-ABx'
-
-test_expect_success \
- 'diff-cache O with A in cache' \
- 'git read-tree $tree_A &&
- git diff-index --cached $tree_O >.test-a &&
- cmp -s .test-a .test-recursive-OA'
-
-test_expect_success \
- 'diff-cache O with B in cache' \
- 'git read-tree $tree_B &&
- git diff-index --cached $tree_O >.test-a &&
- cmp -s .test-a .test-recursive-OB'
-
-test_expect_success \
- 'diff-cache A with B in cache' \
- 'git read-tree $tree_B &&
- git diff-index --cached $tree_A >.test-a &&
- cmp -s .test-a .test-recursive-AB'
-
-test_expect_success \
- 'diff-files with O in cache and A checked out' \
- 'rm -fr Z [A-Z][A-Z] &&
- git read-tree $tree_A &&
- git checkout-index -f -a &&
- git read-tree --reset $tree_O &&
- test_must_fail git update-index --refresh -q &&
- git diff-files >.test-a &&
- cmp_diff_files_output .test-a .test-recursive-OA'
-
-test_expect_success \
- 'diff-files with O in cache and B checked out' \
- 'rm -fr Z [A-Z][A-Z] &&
- git read-tree $tree_B &&
- git checkout-index -f -a &&
- git read-tree --reset $tree_O &&
- test_must_fail git update-index --refresh -q &&
- git diff-files >.test-a &&
- cmp_diff_files_output .test-a .test-recursive-OB'
-
-test_expect_success \
- 'diff-files with A in cache and B checked out' \
- 'rm -fr Z [A-Z][A-Z] &&
- git read-tree $tree_B &&
- git checkout-index -f -a &&
- git read-tree --reset $tree_A &&
- test_must_fail git update-index --refresh -q &&
- git diff-files >.test-a &&
- cmp_diff_files_output .test-a .test-recursive-AB'
+test_expect_success 'diff-tree of known trees.' '
+ git diff-tree $tree_O $tree_A >.test-a &&
+ cmp -s .test-a .test-plain-OA
+'
+
+test_expect_success 'diff-tree of known trees.' '
+ git diff-tree -r $tree_O $tree_A >.test-a &&
+ cmp -s .test-a .test-recursive-OA
+'
+
+test_expect_success 'diff-tree of known trees.' '
+ git diff-tree $tree_O $tree_B >.test-a &&
+ cmp -s .test-a .test-plain-OB
+'
+
+test_expect_success 'diff-tree of known trees.' '
+ git diff-tree -r $tree_O $tree_B >.test-a &&
+ cmp -s .test-a .test-recursive-OB
+'
+
+test_expect_success 'diff-tree of known trees.' '
+ git diff-tree $tree_A $tree_B >.test-a &&
+ cmp -s .test-a .test-plain-AB
+'
+
+test_expect_success 'diff-tree of known trees.' '
+ git diff-tree -r $tree_A $tree_B >.test-a &&
+ cmp -s .test-a .test-recursive-AB
+'
+
+test_expect_success 'diff-tree --stdin of known trees.' '
+ echo $tree_A $tree_B | git diff-tree --stdin > .test-a &&
+ echo $tree_A $tree_B > .test-plain-ABx &&
+ cat .test-plain-AB >> .test-plain-ABx &&
+ cmp -s .test-a .test-plain-ABx
+'
+
+test_expect_success 'diff-tree --stdin of known trees.' '
+ echo $tree_A $tree_B | git diff-tree -r --stdin > .test-a &&
+ echo $tree_A $tree_B > .test-recursive-ABx &&
+ cat .test-recursive-AB >> .test-recursive-ABx &&
+ cmp -s .test-a .test-recursive-ABx
+'
+
+test_expect_success 'diff-cache O with A in cache' '
+ git read-tree $tree_A &&
+ git diff-index --cached $tree_O >.test-a &&
+ cmp -s .test-a .test-recursive-OA
+'
+
+test_expect_success 'diff-cache O with B in cache' '
+ git read-tree $tree_B &&
+ git diff-index --cached $tree_O >.test-a &&
+ cmp -s .test-a .test-recursive-OB
+'
+
+test_expect_success 'diff-cache A with B in cache' '
+ git read-tree $tree_B &&
+ git diff-index --cached $tree_A >.test-a &&
+ cmp -s .test-a .test-recursive-AB
+'
+
+test_expect_success 'diff-files with O in cache and A checked out' '
+ rm -fr Z [A-Z][A-Z] &&
+ git read-tree $tree_A &&
+ git checkout-index -f -a &&
+ git read-tree --reset $tree_O &&
+ test_must_fail git update-index --refresh -q &&
+ git diff-files >.test-a &&
+ cmp_diff_files_output .test-a .test-recursive-OA
+'
+
+test_expect_success 'diff-files with O in cache and B checked out' '
+ rm -fr Z [A-Z][A-Z] &&
+ git read-tree $tree_B &&
+ git checkout-index -f -a &&
+ git read-tree --reset $tree_O &&
+ test_must_fail git update-index --refresh -q &&
+ git diff-files >.test-a &&
+ cmp_diff_files_output .test-a .test-recursive-OB
+'
+
+test_expect_success 'diff-files with A in cache and B checked out' '
+ rm -fr Z [A-Z][A-Z] &&
+ git read-tree $tree_B &&
+ git checkout-index -f -a &&
+ git read-tree --reset $tree_A &&
+ test_must_fail git update-index --refresh -q &&
+ git diff-files >.test-a &&
+ cmp_diff_files_output .test-a .test-recursive-AB
+'
################################################################
# Now we have established the baseline, we do not have to
# rely on individual object ID values that much.
-test_expect_success \
- 'diff-tree O A == diff-tree -R A O' \
- 'git diff-tree $tree_O $tree_A >.test-a &&
- git diff-tree -R $tree_A $tree_O >.test-b &&
- cmp -s .test-a .test-b'
-
-test_expect_success \
- 'diff-tree -r O A == diff-tree -r -R A O' \
- 'git diff-tree -r $tree_O $tree_A >.test-a &&
- git diff-tree -r -R $tree_A $tree_O >.test-b &&
- cmp -s .test-a .test-b'
-
-test_expect_success \
- 'diff-tree B A == diff-tree -R A B' \
- 'git diff-tree $tree_B $tree_A >.test-a &&
- git diff-tree -R $tree_A $tree_B >.test-b &&
- cmp -s .test-a .test-b'
-
-test_expect_success \
- 'diff-tree -r B A == diff-tree -r -R A B' \
- 'git diff-tree -r $tree_B $tree_A >.test-a &&
- git diff-tree -r -R $tree_A $tree_B >.test-b &&
- cmp -s .test-a .test-b'
-
-test_expect_success \
- 'diff can read from stdin' \
- 'test_must_fail git diff --no-index -- MN - < NN |
- grep -v "^index" | sed "s#/-#/NN#" >.test-a &&
- test_must_fail git diff --no-index -- MN NN |
- grep -v "^index" >.test-b &&
- test_cmp .test-a .test-b'
+test_expect_success 'diff-tree O A == diff-tree -R A O' '
+ git diff-tree $tree_O $tree_A >.test-a &&
+ git diff-tree -R $tree_A $tree_O >.test-b &&
+ cmp -s .test-a .test-b
+'
+
+test_expect_success 'diff-tree -r O A == diff-tree -r -R A O' '
+ git diff-tree -r $tree_O $tree_A >.test-a &&
+ git diff-tree -r -R $tree_A $tree_O >.test-b &&
+ cmp -s .test-a .test-b
+'
+
+test_expect_success 'diff-tree B A == diff-tree -R A B' '
+ git diff-tree $tree_B $tree_A >.test-a &&
+ git diff-tree -R $tree_A $tree_B >.test-b &&
+ cmp -s .test-a .test-b
+'
+
+test_expect_success 'diff-tree -r B A == diff-tree -r -R A B' '
+ git diff-tree -r $tree_B $tree_A >.test-a &&
+ git diff-tree -r -R $tree_A $tree_B >.test-b &&
+ cmp -s .test-a .test-b'
+
+test_expect_success 'diff can read from stdin' '
+ test_must_fail git diff --no-index -- MN - < NN |
+ sed "/^index/d; s#/-#/NN#" >.test-a &&
+ test_must_fail git diff --no-index -- MN NN |
+ grep -v "^index" >.test-b &&
+ test_cmp .test-a .test-b
+'
test_done
diff --git a/t/t4003-diff-rename-1.sh b/t/t4003-diff-rename-1.sh
index db07ff3..ebe0918 100755
--- a/t/t4003-diff-rename-1.sh
+++ b/t/t4003-diff-rename-1.sh
@@ -6,23 +6,25 @@
test_description='More rename detection
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
-test_expect_success \
- 'prepare reference tree' \
- 'cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
- echo frotz >rezrov &&
- git update-index --add COPYING rezrov &&
- tree=$(git write-tree) &&
- echo $tree'
-
-test_expect_success \
- 'prepare work tree' \
- 'sed -e 's/HOWEVER/However/' <COPYING >COPYING.1 &&
- sed -e 's/GPL/G.P.L/g' <COPYING >COPYING.2 &&
- rm -f COPYING &&
- git update-index --add --remove COPYING COPYING.?'
+test_expect_success 'prepare reference tree' '
+ COPYING_test_data >COPYING &&
+ echo frotz >rezrov &&
+ git update-index --add COPYING rezrov &&
+ tree=$(git write-tree) &&
+ echo $tree
+'
+
+test_expect_success 'prepare work tree' '
+ sed -e 's/HOWEVER/However/' <COPYING >COPYING.1 &&
+ sed -e 's/GPL/G.P.L/g' <COPYING >COPYING.2 &&
+ rm -f COPYING &&
+ git update-index --add --remove COPYING COPYING.?
+'
# tree has COPYING and rezrov. work tree has COPYING.1 and COPYING.2,
# both are slightly edited, and unchanged rezrov. So we say you
@@ -55,14 +57,14 @@ rename to COPYING.2
+ This file is licensed under the G.P.L v2, or a later version
EOF
-test_expect_success \
- 'validate output from rename/copy detection (#1)' \
- 'compare_diff_patch current expected'
+test_expect_success 'validate output from rename/copy detection (#1)' '
+ compare_diff_patch current expected
+'
-test_expect_success \
- 'prepare work tree again' \
- 'mv COPYING.2 COPYING &&
- git update-index --add --remove COPYING COPYING.1 COPYING.2'
+test_expect_success 'prepare work tree again' '
+ mv COPYING.2 COPYING &&
+ git update-index --add --remove COPYING COPYING.1 COPYING.2
+'
# tree has COPYING and rezrov. work tree has COPYING and COPYING.1,
# both are slightly edited, and unchanged rezrov. So we say you
@@ -93,14 +95,14 @@ copy to COPYING.1
+ However, in order to allow a migration to GPLv3 if that seems like
EOF
-test_expect_success \
- 'validate output from rename/copy detection (#2)' \
- 'compare_diff_patch current expected'
+test_expect_success 'validate output from rename/copy detection (#2)' '
+ compare_diff_patch current expected
+'
-test_expect_success \
- 'prepare work tree once again' \
- 'cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
- git update-index --add --remove COPYING COPYING.1'
+test_expect_success 'prepare work tree once again' '
+ COPYING_test_data >COPYING &&
+ git update-index --add --remove COPYING COPYING.1
+'
# tree has COPYING and rezrov. work tree has COPYING and COPYING.1,
# but COPYING is not edited. We say you copy-and-edit COPYING.1; this
@@ -121,8 +123,8 @@ copy to COPYING.1
+ However, in order to allow a migration to GPLv3 if that seems like
EOF
-test_expect_success \
- 'validate output from rename/copy detection (#3)' \
- 'compare_diff_patch current expected'
+test_expect_success 'validate output from rename/copy detection (#3)' '
+ compare_diff_patch current expected
+'
test_done
diff --git a/t/t4004-diff-rename-symlink.sh b/t/t4004-diff-rename-symlink.sh
index 3d495e3..1d70d4d 100755
--- a/t/t4004-diff-rename-symlink.sh
+++ b/t/t4004-diff-rename-symlink.sh
@@ -9,24 +9,26 @@ The rename detection logic should be able to detect pure rename or
copy of symbolic links, but should not produce rename/copy followed
by an edit for them.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-diff.sh
-test_expect_success SYMLINKS \
- 'prepare reference tree' \
- 'echo xyzzy | tr -d '\\\\'012 >yomin &&
- ln -s xyzzy frotz &&
- git update-index --add frotz yomin &&
- tree=$(git write-tree) &&
- echo $tree'
+test_expect_success SYMLINKS 'prepare reference tree' '
+ echo xyzzy | tr -d '\\\\'012 >yomin &&
+ ln -s xyzzy frotz &&
+ git update-index --add frotz yomin &&
+ tree=$(git write-tree) &&
+ echo $tree
+'
-test_expect_success SYMLINKS \
- 'prepare work tree' \
- 'mv frotz rezrov &&
- rm -f yomin &&
- ln -s xyzzy nitfol &&
- ln -s xzzzy bozbar &&
- git update-index --add --remove frotz rezrov nitfol bozbar yomin'
+test_expect_success SYMLINKS 'prepare work tree' '
+ mv frotz rezrov &&
+ rm -f yomin &&
+ ln -s xyzzy nitfol &&
+ ln -s xzzzy bozbar &&
+ git update-index --add --remove frotz rezrov nitfol bozbar yomin
+'
# tree has frotz pointing at xyzzy, and yomin that contains xyzzy to
# confuse things. work tree has rezrov (xyzzy) nitfol (xyzzy) and
@@ -34,9 +36,9 @@ test_expect_success SYMLINKS \
# rezrov and nitfol are rename/copy of frotz and bozbar should be
# a new creation.
-test_expect_success SYMLINKS 'setup diff output' "
- GIT_DIFF_OPTS=--unified=0 git diff-index -C -p $tree >current &&
- cat >expected <<\EOF
+test_expect_success SYMLINKS 'setup diff output' '
+ GIT_DIFF_OPTS=--unified=0 git diff-index -C -p $tree >current &&
+ cat >expected <<\EOF
diff --git a/bozbar b/bozbar
new file mode 120000
--- /dev/null
@@ -60,10 +62,10 @@ deleted file mode 100644
-xyzzy
\ No newline at end of file
EOF
-"
+'
-test_expect_success SYMLINKS \
- 'validate diff output' \
- 'compare_diff_patch current expected'
+test_expect_success SYMLINKS 'validate diff output' '
+ compare_diff_patch current expected
+'
test_done
diff --git a/t/t4005-diff-rename-2.sh b/t/t4005-diff-rename-2.sh
index 8647906..5c756dc 100755
--- a/t/t4005-diff-rename-2.sh
+++ b/t/t4005-diff-rename-2.sh
@@ -5,11 +5,13 @@
test_description='Same rename detection as t4003 but testing diff-raw.'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
test_expect_success 'setup reference tree' '
- cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
+ COPYING_test_data >COPYING &&
echo frotz >rezrov &&
git update-index --add COPYING rezrov &&
tree=$(git write-tree) &&
@@ -64,7 +66,7 @@ test_expect_success 'validate output from rename/copy detection (#2)' '
# nows how to say Copy.
test_expect_success 'validate output from rename/copy detection (#3)' '
- cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
+ COPYING_test_data >COPYING &&
git update-index --add --remove COPYING COPYING.1 &&
cat <<-EOF >expected &&
diff --git a/t/t4006-diff-mode.sh b/t/t4006-diff-mode.sh
index 6cdee2a..dbd4c0d 100755
--- a/t/t4006-diff-mode.sh
+++ b/t/t4006-diff-mode.sh
@@ -6,6 +6,8 @@
test_description='Test mode change diffs.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
sed_script='s/\(:100644 100755\) \('"$OID_REGEX"'\) \2 /\1 X X /'
diff --git a/t/t4007-rename-3.sh b/t/t4007-rename-3.sh
index cbb9c62..b86165c 100755
--- a/t/t4007-rename-3.sh
+++ b/t/t4007-rename-3.sh
@@ -6,18 +6,19 @@
test_description='Rename interaction with pathspec.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
test_expect_success 'prepare reference tree' '
mkdir path0 path1 &&
- cp "$TEST_DIRECTORY"/lib-diff/COPYING path0/COPYING &&
+ COPYING_test_data >path0/COPYING &&
git update-index --add path0/COPYING &&
tree=$(git write-tree) &&
- echo $tree
+ blob=$(git rev-parse :path0/COPYING)
'
-blob=$(git hash-object "$TEST_DIRECTORY/lib-diff/COPYING")
test_expect_success 'prepare work tree' '
cp path0/COPYING path1/COPYING &&
git update-index --add --remove path0/COPYING path1/COPYING
diff --git a/t/t4008-diff-break-rewrite.sh b/t/t4008-diff-break-rewrite.sh
index 2299f27..562aaf3 100755
--- a/t/t4008-diff-break-rewrite.sh
+++ b/t/t4008-diff-break-rewrite.sh
@@ -25,8 +25,8 @@ Further, with -B and -M together, these should turn into two renames.
. "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
test_expect_success setup '
- cat "$TEST_DIRECTORY"/lib-diff/README >file0 &&
- cat "$TEST_DIRECTORY"/lib-diff/COPYING >file1 &&
+ echo some dissimilar content >file0 &&
+ COPYING_test_data >file1 &&
blob0_id=$(git hash-object file0) &&
blob1_id=$(git hash-object file1) &&
git update-index --add file0 file1 &&
diff --git a/t/t4009-diff-rename-4.sh b/t/t4009-diff-rename-4.sh
index b1da807..3480781 100755
--- a/t/t4009-diff-rename-4.sh
+++ b/t/t4009-diff-rename-4.sh
@@ -6,12 +6,14 @@
test_description='Same rename detection as t4003 but testing diff-raw -z.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
test_expect_success \
'prepare reference tree' \
- 'cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
+ 'COPYING_test_data >COPYING &&
echo frotz >rezrov &&
git update-index --add COPYING rezrov &&
orig=$(git hash-object COPYING) &&
@@ -81,7 +83,7 @@ test_expect_success \
test_expect_success \
'prepare work tree once again' \
- 'cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
+ 'COPYING_test_data >COPYING &&
git update-index --add --remove COPYING COPYING.1'
git diff-index -z -C --find-copies-harder $tree >current
diff --git a/t/t4010-diff-pathspec.sh b/t/t4010-diff-pathspec.sh
index 1bbced7..9d9650e 100755
--- a/t/t4010-diff-pathspec.sh
+++ b/t/t4010-diff-pathspec.sh
@@ -9,6 +9,8 @@ Prepare:
file0
path1/file1
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
diff --git a/t/t4011-diff-symlink.sh b/t/t4011-diff-symlink.sh
index 5a25c25..bc8ba88 100755
--- a/t/t4011-diff-symlink.sh
+++ b/t/t4011-diff-symlink.sh
@@ -6,18 +6,20 @@
test_description='Test diff of symlinks.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-diff.sh
# Print the short OID of a symlink with the given name.
symlink_oid () {
- local oid=$(printf "%s" "$1" | git hash-object --stdin) &&
+ local oid="$(printf "%s" "$1" | git hash-object --stdin)" &&
git rev-parse --short "$oid"
}
# Print the short OID of the given file.
short_oid () {
- local oid=$(git hash-object "$1") &&
+ local oid="$(git hash-object "$1")" &&
git rev-parse --short "$oid"
}
diff --git a/t/t4012-diff-binary.sh b/t/t4012-diff-binary.sh
index 33ff588..c64d9d2 100755
--- a/t/t4012-diff-binary.sh
+++ b/t/t4012-diff-binary.sh
@@ -6,6 +6,7 @@
test_description='Binary diff and apply
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
cat >expect.binary-numstat <<\EOF
@@ -112,20 +113,20 @@ test_expect_success 'diff --no-index with binary creation' '
'
cat >expect <<EOF
- binfile | Bin 0 -> 1026 bytes
- textfile | 10000 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ binfilë | Bin 0 -> 1026 bytes
+ tëxtfilë | 10000 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
EOF
test_expect_success 'diff --stat with binary files and big change count' '
- printf "\01\00%1024d" 1 >binfile &&
- git add binfile &&
+ printf "\01\00%1024d" 1 >binfilë &&
+ git add binfilë &&
i=0 &&
while test $i -lt 10000; do
echo $i &&
- i=$(($i + 1))
- done >textfile &&
- git add textfile &&
- git diff --cached --stat binfile textfile >output &&
+ i=$(($i + 1)) || return 1
+ done >tëxtfilë &&
+ git add tëxtfilë &&
+ git -c core.quotepath=false diff --cached --stat binfilë tëxtfilë >output &&
grep " | " output >actual &&
test_cmp expect actual
'
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index 28683d0..3855d68 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -19,8 +19,8 @@ test_expect_success setup '
mkdir dir &&
mkdir dir2 &&
- for i in 1 2 3; do echo $i; done >file0 &&
- for i in A B; do echo $i; done >dir/sub &&
+ test_write_lines 1 2 3 >file0 &&
+ test_write_lines A B >dir/sub &&
cat file0 >file2 &&
git add file0 file2 dir/sub &&
git commit -m Initial &&
@@ -32,8 +32,8 @@ test_expect_success setup '
GIT_COMMITTER_DATE="2006-06-26 00:01:00 +0000" &&
export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
- for i in 4 5 6; do echo $i; done >>file0 &&
- for i in C D; do echo $i; done >>dir/sub &&
+ test_write_lines 4 5 6 >>file0 &&
+ test_write_lines C D >>dir/sub &&
rm -f file2 &&
git update-index --remove file0 file2 dir/sub &&
git commit -m "Second${LF}${LF}This is the second commit." &&
@@ -42,9 +42,9 @@ test_expect_success setup '
GIT_COMMITTER_DATE="2006-06-26 00:02:00 +0000" &&
export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
- for i in A B C; do echo $i; done >file1 &&
+ test_write_lines A B C >file1 &&
git add file1 &&
- for i in E F; do echo $i; done >>dir/sub &&
+ test_write_lines E F >>dir/sub &&
git update-index dir/sub &&
git commit -m Third &&
@@ -53,8 +53,8 @@ test_expect_success setup '
export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
git checkout side &&
- for i in A B C; do echo $i; done >>file0 &&
- for i in 1 2; do echo $i; done >>dir/sub &&
+ test_write_lines A B C >>file0 &&
+ test_write_lines 1 2 >>dir/sub &&
cat dir/sub >file3 &&
git add file3 &&
git update-index file0 dir/sub &&
@@ -71,8 +71,8 @@ test_expect_success setup '
GIT_COMMITTER_DATE="2006-06-26 00:05:00 +0000" &&
export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
- for i in A B C; do echo $i; done >>file0 &&
- for i in 1 2; do echo $i; done >>dir/sub &&
+ test_write_lines A B C >>file0 &&
+ test_write_lines 1 2 >>dir/sub &&
git update-index file0 dir/sub &&
mkdir dir3 &&
@@ -86,7 +86,7 @@ test_expect_success setup '
GIT_COMMITTER_DATE="2006-06-26 00:06:00 +0000" &&
export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
git checkout -b rearrange initial &&
- for i in B A; do echo $i; done >dir/sub &&
+ test_write_lines B A >dir/sub &&
git add dir/sub &&
git commit -m "Rearranged lines in dir/sub" &&
git checkout master &&
@@ -178,32 +178,29 @@ process_diffs () {
V=$(git version | sed -e 's/^git version //' -e 's/\./\\./g')
while read magic cmd
do
- status=success
case "$magic" in
'' | '#'*)
continue ;;
- :*)
- magic=${magic#:}
+ :noellipses)
+ magic=noellipses
label="$magic-$cmd"
- case "$magic" in
- noellipses) ;;
- failure)
- status=failure
- magic=
- label="$cmd" ;;
- *)
- BUG "unknown magic $magic" ;;
- esac ;;
+ ;;
+ :*)
+ BUG "unknown magic $magic"
+ ;;
*)
- cmd="$magic $cmd" magic=
- label="$cmd" ;;
+ cmd="$magic $cmd"
+ magic=
+ label="$cmd"
+ ;;
esac
+
test=$(echo "$label" | sed -e 's|[/ ][/ ]*|_|g')
pfx=$(printf "%04d" $test_count)
expect="$TEST_DIRECTORY/t4013/diff.$test"
actual="$pfx-diff.$test"
- test_expect_$status "git $cmd # magic is ${magic:-(not used)}" '
+ test_expect_success "git $cmd # magic is ${magic:-(not used)}" '
{
echo "$ git $cmd"
case "$magic" in
@@ -352,6 +349,8 @@ log -GF -p --pickaxe-all master
log -IA -IB -I1 -I2 -p master
log --decorate --all
log --decorate=full --all
+log --decorate --clear-decorations --all
+log --decorate=full --clear-decorations --all
rev-list --parents HEAD
rev-list --children HEAD
@@ -471,6 +470,14 @@ test_expect_success 'log --diff-merges=on matches --diff-merges=separate' '
test_cmp expected actual
'
+test_expect_success 'log --dd matches --diff-merges=1 -p' '
+ git log --diff-merges=1 -p master >result &&
+ process_diffs result >expected &&
+ git log --dd master >result &&
+ process_diffs result >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'deny wrong log.diffMerges config' '
test_config log.diffMerges wrong-value &&
test_expect_code 128 git log
@@ -512,7 +519,7 @@ test_expect_success 'log -S requires an argument' '
'
test_expect_success 'diff --cached on unborn branch' '
- echo ref: refs/heads/unborn >.git/HEAD &&
+ git symbolic-ref HEAD refs/heads/unborn &&
git diff --cached >result &&
process_diffs result >actual &&
process_diffs "$TEST_DIRECTORY/t4013/diff.diff_--cached" >expected &&
@@ -542,6 +549,39 @@ test_expect_success 'diff-tree --stdin with log formatting' '
test_cmp expect actual
'
+test_expect_success 'diff-tree --stdin with pathspec' '
+ cat >expect <<-EOF &&
+ Third
+
+ dir/sub
+ Second
+
+ dir/sub
+ EOF
+ git rev-list master^ |
+ git diff-tree -r --stdin --name-only --format=%s dir >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'show A B ... -- <pathspec>' '
+ # side touches dir/sub, file0, and file3
+ # master^ touches dir/sub, and file1
+ # master^^ touches dir/sub, file0, and file2
+ git show --name-only --format="<%s>" side master^ master^^ -- dir >actual &&
+ cat >expect <<-\EOF &&
+ <Side>
+
+ dir/sub
+ <Third>
+
+ dir/sub
+ <Second>
+
+ dir/sub
+ EOF
+ test_cmp expect actual
+'
+
test_expect_success 'diff -I<regex>: setup' '
git checkout master &&
test_seq 50 >file0 &&
@@ -578,7 +618,90 @@ test_expect_success 'diff -I<regex> --stat' '
test_expect_success 'diff -I<regex>: detect malformed regex' '
test_expect_code 129 git diff --ignore-matching-lines="^[124-9" 2>error &&
- test_i18ngrep "invalid regex given to -I: " error
+ test_grep "invalid regex given to -I: " error
+'
+
+# check_prefix <patch> <src> <dst>
+# check only lines with paths to avoid dependency on exact oid/contents
+check_prefix () {
+ grep -E '^(diff|---|\+\+\+) ' "$1" >actual.paths &&
+ cat >expect <<-EOF &&
+ diff --git $2 $3
+ --- $2
+ +++ $3
+ EOF
+ test_cmp expect actual.paths
+}
+
+test_expect_success 'diff-files does not respect diff.noPrefix' '
+ git -c diff.noPrefix diff-files -p >actual &&
+ check_prefix actual a/file0 b/file0
+'
+
+test_expect_success 'diff-files respects --no-prefix' '
+ git diff-files -p --no-prefix >actual &&
+ check_prefix actual file0 file0
+'
+
+test_expect_success 'diff respects diff.noPrefix' '
+ git -c diff.noPrefix diff >actual &&
+ check_prefix actual file0 file0
+'
+
+test_expect_success 'diff --default-prefix overrides diff.noPrefix' '
+ git -c diff.noPrefix diff --default-prefix >actual &&
+ check_prefix actual a/file0 b/file0
+'
+
+test_expect_success 'diff respects diff.mnemonicPrefix' '
+ git -c diff.mnemonicPrefix diff >actual &&
+ check_prefix actual i/file0 w/file0
+'
+
+test_expect_success 'diff --default-prefix overrides diff.mnemonicPrefix' '
+ git -c diff.mnemonicPrefix diff --default-prefix >actual &&
+ check_prefix actual a/file0 b/file0
+'
+
+test_expect_success 'diff respects diff.srcPrefix' '
+ git -c diff.srcPrefix=x/ diff >actual &&
+ check_prefix actual x/file0 b/file0
+'
+
+test_expect_success 'diff respects diff.dstPrefix' '
+ git -c diff.dstPrefix=y/ diff >actual &&
+ check_prefix actual a/file0 y/file0
+'
+
+test_expect_success 'diff --src-prefix overrides diff.srcPrefix' '
+ git -c diff.srcPrefix=y/ diff --src-prefix=z/ >actual &&
+ check_prefix actual z/file0 b/file0
+'
+
+test_expect_success 'diff --dst-prefix overrides diff.dstPrefix' '
+ git -c diff.dstPrefix=y/ diff --dst-prefix=z/ >actual &&
+ check_prefix actual a/file0 z/file0
+'
+
+test_expect_success 'diff.{src,dst}Prefix ignored with diff.noPrefix' '
+ git -c diff.dstPrefix=y/ -c diff.srcPrefix=x/ -c diff.noPrefix diff >actual &&
+ check_prefix actual file0 file0
+'
+
+test_expect_success 'diff.{src,dst}Prefix ignored with diff.mnemonicPrefix' '
+ git -c diff.dstPrefix=x/ -c diff.srcPrefix=y/ -c diff.mnemonicPrefix diff >actual &&
+ check_prefix actual i/file0 w/file0
+'
+
+test_expect_success 'diff.{src,dst}Prefix ignored with --default-prefix' '
+ git -c diff.dstPrefix=x/ -c diff.srcPrefix=y/ diff --default-prefix >actual &&
+ check_prefix actual a/file0 b/file0
+'
+
+test_expect_success 'diff --no-renames cannot be abbreviated' '
+ test_expect_code 129 git diff --no-rename >actual 2>error &&
+ test_must_be_empty actual &&
+ grep "invalid option: --no-rename" error
'
test_done
diff --git a/t/t4013/diff.log_--decorate=full_--all b/t/t4013/diff.log_--decorate=full_--all
index 3f9b872..6b0b334 100644
--- a/t/t4013/diff.log_--decorate=full_--all
+++ b/t/t4013/diff.log_--decorate=full_--all
@@ -20,7 +20,7 @@ Date: Mon Jun 26 00:06:00 2006 +0000
Rearranged lines in dir/sub
-commit cbacedd14cb8b89255a2c02b59e77a2e9a8021a0 (refs/notes/commits)
+commit cbacedd14cb8b89255a2c02b59e77a2e9a8021a0
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:06:00 2006 +0000
diff --git a/t/t4013/diff.log_--decorate=full_--clear-decorations_--all b/t/t4013/diff.log_--decorate=full_--clear-decorations_--all
new file mode 100644
index 0000000..1c030a6
--- /dev/null
+++ b/t/t4013/diff.log_--decorate=full_--clear-decorations_--all
@@ -0,0 +1,61 @@
+$ git log --decorate=full --clear-decorations --all
+commit b7e0bc69303b488b47deca799a7d723971dfa6cd (refs/heads/mode)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ update mode
+
+commit a6f364368ca320bc5a92e18912e16fa6b3dff598 (refs/heads/note)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ update mode (file2)
+
+Notes:
+ note
+
+commit cd4e72fd96faed3f0ba949dc42967430374e2290 (refs/heads/rearrange)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ Rearranged lines in dir/sub
+
+commit cbacedd14cb8b89255a2c02b59e77a2e9a8021a0 (refs/notes/commits)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ Notes added by 'git notes add'
+
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> refs/heads/master)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a (refs/heads/side)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a (refs/heads/initial)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+$
diff --git a/t/t4013/diff.log_--decorate=full_--decorate-all_--all b/t/t4013/diff.log_--decorate=full_--decorate-all_--all
new file mode 100644
index 0000000..d6e7928
--- /dev/null
+++ b/t/t4013/diff.log_--decorate=full_--decorate-all_--all
@@ -0,0 +1,61 @@
+$ git log --decorate=full --decorate-all --all
+commit b7e0bc69303b488b47deca799a7d723971dfa6cd (refs/heads/mode)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ update mode
+
+commit a6f364368ca320bc5a92e18912e16fa6b3dff598 (refs/heads/note)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ update mode (file2)
+
+Notes:
+ note
+
+commit cd4e72fd96faed3f0ba949dc42967430374e2290 (refs/heads/rearrange)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ Rearranged lines in dir/sub
+
+commit cbacedd14cb8b89255a2c02b59e77a2e9a8021a0 (refs/notes/commits)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ Notes added by 'git notes add'
+
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> refs/heads/master)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a (refs/heads/side)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a (refs/heads/initial)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+$
diff --git a/t/t4013/diff.log_--decorate_--all b/t/t4013/diff.log_--decorate_--all
index f5e20e1..c7df1f5 100644
--- a/t/t4013/diff.log_--decorate_--all
+++ b/t/t4013/diff.log_--decorate_--all
@@ -20,7 +20,7 @@ Date: Mon Jun 26 00:06:00 2006 +0000
Rearranged lines in dir/sub
-commit cbacedd14cb8b89255a2c02b59e77a2e9a8021a0 (refs/notes/commits)
+commit cbacedd14cb8b89255a2c02b59e77a2e9a8021a0
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:06:00 2006 +0000
diff --git a/t/t4013/diff.log_--decorate_--clear-decorations_--all b/t/t4013/diff.log_--decorate_--clear-decorations_--all
new file mode 100644
index 0000000..88be82c
--- /dev/null
+++ b/t/t4013/diff.log_--decorate_--clear-decorations_--all
@@ -0,0 +1,61 @@
+$ git log --decorate --clear-decorations --all
+commit b7e0bc69303b488b47deca799a7d723971dfa6cd (mode)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ update mode
+
+commit a6f364368ca320bc5a92e18912e16fa6b3dff598 (note)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ update mode (file2)
+
+Notes:
+ note
+
+commit cd4e72fd96faed3f0ba949dc42967430374e2290 (rearrange)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ Rearranged lines in dir/sub
+
+commit cbacedd14cb8b89255a2c02b59e77a2e9a8021a0 (refs/notes/commits)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ Notes added by 'git notes add'
+
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> master)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a (side)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a (initial)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+$
diff --git a/t/t4013/diff.log_--decorate_--decorate-all_--all b/t/t4013/diff.log_--decorate_--decorate-all_--all
new file mode 100644
index 0000000..5d22618
--- /dev/null
+++ b/t/t4013/diff.log_--decorate_--decorate-all_--all
@@ -0,0 +1,61 @@
+$ git log --decorate --decorate-all --all
+commit b7e0bc69303b488b47deca799a7d723971dfa6cd (mode)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ update mode
+
+commit a6f364368ca320bc5a92e18912e16fa6b3dff598 (note)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ update mode (file2)
+
+Notes:
+ note
+
+commit cd4e72fd96faed3f0ba949dc42967430374e2290 (rearrange)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ Rearranged lines in dir/sub
+
+commit cbacedd14cb8b89255a2c02b59e77a2e9a8021a0 (refs/notes/commits)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:06:00 2006 +0000
+
+ Notes added by 'git notes add'
+
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> master)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a (side)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a (initial)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+$
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 712d4b5..e37a141 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -12,25 +12,25 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. "$TEST_DIRECTORY"/lib-terminal.sh
test_expect_success setup '
- for i in 1 2 3 4 5 6 7 8 9 10; do echo "$i"; done >file &&
+ test_write_lines 1 2 3 4 5 6 7 8 9 10 >file &&
cat file >elif &&
git add file elif &&
test_tick &&
git commit -m Initial &&
git checkout -b side &&
- for i in 1 2 5 6 A B C 7 8 9 10; do echo "$i"; done >file &&
+ test_write_lines 1 2 5 6 A B C 7 8 9 10 >file &&
test_chmod +x elif &&
test_tick &&
git commit -m "Side changes #1" &&
- for i in D E F; do echo "$i"; done >>file &&
+ test_write_lines D E F >>file &&
git update-index file &&
test_tick &&
git commit -m "Side changes #2" &&
git tag C2 &&
- for i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo "$i"; done >file &&
+ test_write_lines 5 6 1 2 3 A 4 B C 7 8 9 10 D E F >file &&
git update-index file &&
test_tick &&
git commit -m "Side changes #3 with \\n backslash-n in it." &&
@@ -43,22 +43,26 @@ test_expect_success setup '
git checkout side &&
git checkout -b patchid &&
- for i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo "$i"; done >file2 &&
- for i in 1 2 3 A 4 B C 7 8 9 10 D E F 5 6; do echo "$i"; done >file3 &&
- for i in 8 9 10; do echo "$i"; done >file &&
+ test_write_lines 5 6 1 2 3 A 4 B C 7 8 9 10 D E F >file2 &&
+ test_write_lines 1 2 3 A 4 B C 7 8 9 10 D E F 5 6 >file3 &&
+ test_write_lines 8 9 10 >file &&
git add file file2 file3 &&
test_tick &&
git commit -m "patchid 1" &&
- for i in 4 A B 7 8 9 10; do echo "$i"; done >file2 &&
- for i in 8 9 10 5 6; do echo "$i"; done >file3 &&
+ test_write_lines 4 A B 7 8 9 10 >file2 &&
+ test_write_lines 8 9 10 5 6 >file3 &&
git add file2 file3 &&
test_tick &&
git commit -m "patchid 2" &&
- for i in 10 5 6; do echo "$i"; done >file &&
+ test_write_lines 10 5 6 >file &&
git add file &&
test_tick &&
git commit -m "patchid 3" &&
+ git checkout -b empty main &&
+ test_tick &&
+ git commit --allow-empty -m "empty commit" &&
+
git checkout main
'
@@ -128,6 +132,12 @@ test_expect_success 'replay did not screw up the log message' '
grep "^Side .* with .* backslash-n" actual
'
+test_expect_success 'format-patch empty commit' '
+ git format-patch --stdout main..empty >empty &&
+ grep "^From " empty >from &&
+ test_line_count = 1 from
+'
+
test_expect_success 'extra headers' '
git config format.headers "To: R E Cipient <rcipient@example.com>
" &&
@@ -325,7 +335,7 @@ test_expect_success 'filename length limit' '
max=$(
for patch in 000[1-9]-*.patch
do
- echo "$patch" | wc -c
+ echo "$patch" | wc -c || exit 1
done |
sort -nr |
head -n 1
@@ -343,7 +353,7 @@ test_expect_success 'filename length limit from config' '
max=$(
for patch in 000[1-9]-*.patch
do
- echo "$patch" | wc -c
+ echo "$patch" | wc -c || exit 1
done |
sort -nr |
head -n 1
@@ -361,7 +371,7 @@ test_expect_success 'filename limit applies only to basename' '
max=$(
for patch in patches/000[1-9]-*.patch
do
- echo "${patch#patches/}" | wc -c
+ echo "${patch#patches/}" | wc -c || exit 1
done |
sort -nr |
head -n 1
@@ -445,13 +455,13 @@ test_expect_success 'no threading' '
cat >expect.thread <<EOF
---
-Message-Id: <0>
+Message-ID: <0>
---
-Message-Id: <1>
+Message-ID: <1>
In-Reply-To: <0>
References: <0>
---
-Message-Id: <2>
+Message-ID: <2>
In-Reply-To: <0>
References: <0>
EOF
@@ -460,17 +470,22 @@ test_expect_success 'thread' '
check_threading expect.thread --thread main
'
+test_expect_success '--thread overrides format.thread=deep' '
+ test_config format.thread deep &&
+ check_threading expect.thread --thread main
+'
+
cat >expect.in-reply-to <<EOF
---
-Message-Id: <0>
+Message-ID: <0>
In-Reply-To: <1>
References: <1>
---
-Message-Id: <2>
+Message-ID: <2>
In-Reply-To: <1>
References: <1>
---
-Message-Id: <3>
+Message-ID: <3>
In-Reply-To: <1>
References: <1>
EOF
@@ -482,17 +497,17 @@ test_expect_success 'thread in-reply-to' '
cat >expect.cover-letter <<EOF
---
-Message-Id: <0>
+Message-ID: <0>
---
-Message-Id: <1>
+Message-ID: <1>
In-Reply-To: <0>
References: <0>
---
-Message-Id: <2>
+Message-ID: <2>
In-Reply-To: <0>
References: <0>
---
-Message-Id: <3>
+Message-ID: <3>
In-Reply-To: <0>
References: <0>
EOF
@@ -503,21 +518,21 @@ test_expect_success 'thread cover-letter' '
cat >expect.cl-irt <<EOF
---
-Message-Id: <0>
+Message-ID: <0>
In-Reply-To: <1>
References: <1>
---
-Message-Id: <2>
+Message-ID: <2>
In-Reply-To: <0>
References: <1>
<0>
---
-Message-Id: <3>
+Message-ID: <3>
In-Reply-To: <0>
References: <1>
<0>
---
-Message-Id: <4>
+Message-ID: <4>
In-Reply-To: <0>
References: <1>
<0>
@@ -535,13 +550,13 @@ test_expect_success 'thread explicit shallow' '
cat >expect.deep <<EOF
---
-Message-Id: <0>
+Message-ID: <0>
---
-Message-Id: <1>
+Message-ID: <1>
In-Reply-To: <0>
References: <0>
---
-Message-Id: <2>
+Message-ID: <2>
In-Reply-To: <1>
References: <0>
<1>
@@ -553,16 +568,16 @@ test_expect_success 'thread deep' '
cat >expect.deep-irt <<EOF
---
-Message-Id: <0>
+Message-ID: <0>
In-Reply-To: <1>
References: <1>
---
-Message-Id: <2>
+Message-ID: <2>
In-Reply-To: <0>
References: <1>
<0>
---
-Message-Id: <3>
+Message-ID: <3>
In-Reply-To: <2>
References: <1>
<0>
@@ -576,18 +591,18 @@ test_expect_success 'thread deep in-reply-to' '
cat >expect.deep-cl <<EOF
---
-Message-Id: <0>
+Message-ID: <0>
---
-Message-Id: <1>
+Message-ID: <1>
In-Reply-To: <0>
References: <0>
---
-Message-Id: <2>
+Message-ID: <2>
In-Reply-To: <1>
References: <0>
<1>
---
-Message-Id: <3>
+Message-ID: <3>
In-Reply-To: <2>
References: <0>
<1>
@@ -600,22 +615,22 @@ test_expect_success 'thread deep cover-letter' '
cat >expect.deep-cl-irt <<EOF
---
-Message-Id: <0>
+Message-ID: <0>
In-Reply-To: <1>
References: <1>
---
-Message-Id: <2>
+Message-ID: <2>
In-Reply-To: <0>
References: <1>
<0>
---
-Message-Id: <3>
+Message-ID: <3>
In-Reply-To: <2>
References: <1>
<0>
<2>
---
-Message-Id: <4>
+Message-ID: <4>
In-Reply-To: <3>
References: <1>
<0>
@@ -653,7 +668,7 @@ test_expect_success 'excessive subject' '
git checkout side &&
before=$(git hash-object file) &&
before=$(git rev-parse --short $before) &&
- for i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo "$i"; done >>file &&
+ test_write_lines 5 6 1 2 3 A 4 B C 7 8 9 10 D E F >>file &&
after=$(git hash-object file) &&
after=$(git rev-parse --short $after) &&
git update-index file &&
@@ -926,11 +941,40 @@ test_expect_success 'format-patch --numstat should produce a patch' '
'
test_expect_success 'format-patch -- <path>' '
- git format-patch main..side -- file 2>error &&
- ! grep "Use .--" error
+ rm -f *.patch &&
+ git checkout -b pathspec main &&
+
+ echo file_a 1 >file_a &&
+ echo file_b 1 >file_b &&
+ git add file_a file_b &&
+ git commit -m pathspec_initial &&
+
+ echo file_a 2 >>file_a &&
+ git add file_a &&
+ git commit -m pathspec_a &&
+
+ echo file_b 2 >>file_b &&
+ git add file_b &&
+ git commit -m pathspec_b &&
+
+ echo file_a 3 >>file_a &&
+ echo file_b 3 >>file_b &&
+ git add file_a file_b &&
+ git commit -m pathspec_ab &&
+
+ cat >expect <<-\EOF &&
+ 0001-pathspec_initial.patch
+ 0002-pathspec_a.patch
+ 0003-pathspec_ab.patch
+ EOF
+
+ git format-patch main..pathspec -- file_a >output &&
+ test_cmp expect output &&
+ ! grep file_b *.patch
'
test_expect_success 'format-patch --ignore-if-in-upstream HEAD' '
+ git checkout side &&
git format-patch --ignore-if-in-upstream HEAD
'
@@ -1086,7 +1130,7 @@ test_expect_success TTY 'format-patch --stdout paginates' '
test_expect_success 'format-patch handles multi-line subjects' '
rm -rf patches/ &&
echo content >>file &&
- for i in one two three; do echo $i; done >msg &&
+ test_write_lines one two three >msg &&
git add file &&
git commit -F msg &&
git format-patch -o patches -1 &&
@@ -1098,7 +1142,7 @@ test_expect_success 'format-patch handles multi-line subjects' '
test_expect_success 'format-patch handles multi-line encoded subjects' '
rm -rf patches/ &&
echo content >>file &&
- for i in en två tre; do echo $i; done >msg &&
+ test_write_lines en två tre >msg &&
git add file &&
git commit -F msg &&
git format-patch -o patches -1 &&
@@ -1329,7 +1373,27 @@ test_expect_success '--rfc' '
Subject: [RFC PATCH 1/1] header with . in it
EOF
git format-patch -n -1 --stdout --rfc >patch &&
- grep ^Subject: patch >actual &&
+ grep "^Subject:" patch >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--rfc does not overwrite prefix' '
+ cat >expect <<-\EOF &&
+ Subject: [RFC PATCH foobar 1/1] header with . in it
+ EOF
+ git -c format.subjectPrefix="PATCH foobar" \
+ format-patch -n -1 --stdout --rfc >patch &&
+ grep "^Subject:" patch >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--rfc is argument order independent' '
+ cat >expect <<-\EOF &&
+ Subject: [RFC PATCH foobar 1/1] header with . in it
+ EOF
+ git format-patch -n -1 --stdout --rfc \
+ --subject-prefix="PATCH foobar" >patch &&
+ grep "^Subject:" patch >actual &&
test_cmp expect actual
'
@@ -1371,6 +1435,43 @@ test_expect_success '--from omits redundant in-body header' '
test_cmp expect patch.head
'
+test_expect_success 'with --force-in-body-from, redundant in-body from is kept' '
+ git format-patch --force-in-body-from \
+ -1 --stdout --from="A U Thor <author@example.com>" >patch &&
+ cat >expect <<-\EOF &&
+ From: A U Thor <author@example.com>
+
+ From: A U Thor <author@example.com>
+
+ EOF
+ sed -ne "/^From:/p; /^$/p; /^---$/q" patch >patch.head &&
+ test_cmp expect patch.head
+'
+
+test_expect_success 'format.forceInBodyFrom, equivalent to --force-in-body-from' '
+ git -c format.forceInBodyFrom=yes format-patch \
+ -1 --stdout --from="A U Thor <author@example.com>" >patch &&
+ cat >expect <<-\EOF &&
+ From: A U Thor <author@example.com>
+
+ From: A U Thor <author@example.com>
+
+ EOF
+ sed -ne "/^From:/p; /^$/p; /^---$/q" patch >patch.head &&
+ test_cmp expect patch.head
+'
+
+test_expect_success 'format.forceInBodyFrom, equivalent to --force-in-body-from' '
+ git -c format.forceInBodyFrom=yes format-patch --no-force-in-body-from \
+ -1 --stdout --from="A U Thor <author@example.com>" >patch &&
+ cat >expect <<-\EOF &&
+ From: A U Thor <author@example.com>
+
+ EOF
+ sed -ne "/^From:/p; /^$/p; /^---$/q" patch >patch.head &&
+ test_cmp expect patch.head
+'
+
test_expect_success 'in-body headers trigger content encoding' '
test_env GIT_AUTHOR_NAME="éxötìc" test_commit exotic &&
test_when_finished "git reset --hard HEAD^" &&
@@ -1391,7 +1492,7 @@ append_signoff()
C=$(git commit-tree HEAD^^{tree} -p HEAD) &&
git format-patch --stdout --signoff $C^..$C >append_signoff.patch &&
sed -n -e "1,/^---$/p" append_signoff.patch |
- egrep -n "^Subject|Sign|^$"
+ grep -E -n "^Subject|Sign|^$"
}
test_expect_success 'signoff: commit with no body' '
@@ -1805,6 +1906,16 @@ body" &&
grep "^body$" actual
'
+test_expect_success 'cover letter with --cover-from-description subject (UTF-8 subject line)' '
+ test_config branch.rebuild-1.description "Café?
+
+body" &&
+ git checkout rebuild-1 &&
+ git format-patch --stdout --cover-letter --cover-from-description subject --encode-email-headers main >actual &&
+ grep "^Subject: \[PATCH 0/2\] =?UTF-8?q?Caf=C3=A9=3F?=$" actual &&
+ ! grep "Café" actual
+'
+
test_expect_success 'cover letter with format.coverFromDescription = auto (short subject line)' '
test_config branch.rebuild-1.description "config subject
@@ -1910,6 +2021,20 @@ test_expect_success 'cover letter using branch description (6)' '
grep hello actual
'
+test_expect_success 'cover letter with --description-file' '
+ test_when_finished "rm -f description.txt" &&
+ cat >description.txt <<-\EOF &&
+ subject from file
+
+ body from file
+ EOF
+ git checkout rebuild-1 &&
+ git format-patch --stdout --cover-letter --cover-from-description auto \
+ --description-file description.txt main >actual &&
+ grep "^Subject: \[PATCH 0/2\] subject from file$" actual &&
+ grep "^body from file$" actual
+'
+
test_expect_success 'cover letter with nothing' '
git format-patch --stdout --cover-letter >actual &&
test_line_count = 0 actual
@@ -2208,14 +2333,32 @@ test_expect_success 'format-patch --base with --attach' '
test_expect_success 'format-patch --attach cover-letter only is non-multipart' '
test_when_finished "rm -fr patches" &&
git format-patch -o patches --cover-letter --attach=mimemime --base=HEAD~ -1 &&
- ! egrep "^--+mimemime" patches/0000*.patch &&
- egrep "^--+mimemime$" patches/0001*.patch >output &&
+ ! grep -E "^--+mimemime" patches/0000*.patch &&
+ grep -E "^--+mimemime$" patches/0001*.patch >output &&
test_line_count = 2 output &&
- egrep "^--+mimemime--$" patches/0001*.patch >output &&
+ grep -E "^--+mimemime--$" patches/0001*.patch >output &&
test_line_count = 1 output
'
-test_expect_success 'format-patch --pretty=mboxrd' '
+test_expect_success 'format-patch with format.attach' '
+ test_when_finished "rm -fr patches" &&
+ separator=attachment-separator &&
+ test_config format.attach "$separator" &&
+ filename=$(git format-patch -o patches -1) &&
+ grep "^Content-Type: multipart/.*$separator" "$filename"
+'
+
+test_expect_success 'format-patch with format.attach=disabled' '
+ test_when_finished "rm -fr patches" &&
+ separator=attachment-separator &&
+ test_config_global format.attach "$separator" &&
+ test_config format.attach "" &&
+ filename=$(git format-patch -o patches -1) &&
+ # The output should not even declare content type for text/plain.
+ ! grep "^Content-Type: multipart/" "$filename"
+'
+
+test_expect_success '-c format.mboxrd format-patch' '
sp=" " &&
cat >msg <<-INPUT_END &&
mboxrd should escape the body
@@ -2250,7 +2393,9 @@ test_expect_success 'format-patch --pretty=mboxrd' '
INPUT_END
C=$(git commit-tree HEAD^^{tree} -p HEAD <msg) &&
- git format-patch --pretty=mboxrd --stdout -1 $C~1..$C >patch &&
+ git -c format.mboxrd format-patch --stdout -1 $C~1..$C >patch &&
+ git format-patch --pretty=mboxrd --stdout -1 $C~1..$C >compat &&
+ test_cmp patch compat &&
git grep -h --no-index -A11 \
"^>From could trip up a loose mbox parser" patch >actual &&
test_cmp expect actual
@@ -2268,25 +2413,25 @@ test_expect_success 'interdiff: cover-letter' '
--q
EOF
git format-patch --cover-letter --interdiff=boop~2 -1 boop &&
- test_i18ngrep "^Interdiff:$" 0000-cover-letter.patch &&
- test_i18ngrep ! "^Interdiff:$" 0001-fleep.patch &&
+ test_grep "^Interdiff:$" 0000-cover-letter.patch &&
+ test_grep ! "^Interdiff:$" 0001-fleep.patch &&
sed "1,/^@@ /d; /^-- $/q" 0000-cover-letter.patch >actual &&
test_cmp expect actual
'
test_expect_success 'interdiff: reroll-count' '
git format-patch --cover-letter --interdiff=boop~2 -v2 -1 boop &&
- test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
+ test_grep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
'
test_expect_success 'interdiff: reroll-count with a non-integer' '
git format-patch --cover-letter --interdiff=boop~2 -v2.2 -1 boop &&
- test_i18ngrep "^Interdiff:$" v2.2-0000-cover-letter.patch
+ test_grep "^Interdiff:$" v2.2-0000-cover-letter.patch
'
test_expect_success 'interdiff: reroll-count with a integer' '
git format-patch --cover-letter --interdiff=boop~2 -v2 -1 boop &&
- test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
+ test_grep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
'
test_expect_success 'interdiff: solo-patch' '
@@ -2295,9 +2440,25 @@ test_expect_success 'interdiff: solo-patch' '
EOF
git format-patch --interdiff=boop~2 -1 boop &&
- test_i18ngrep "^Interdiff:$" 0001-fleep.patch &&
+ test_grep "^Interdiff:$" 0001-fleep.patch &&
sed "1,/^ @@ /d; /^$/q" 0001-fleep.patch >actual &&
test_cmp expect actual
'
+test_expect_success 'format-patch does not respect diff.noprefix' '
+ git -c diff.noprefix format-patch -1 --stdout >actual &&
+ grep "^--- a/blorp" actual
+'
+
+test_expect_success 'format-patch respects format.noprefix' '
+ git -c format.noprefix format-patch -1 --stdout >actual &&
+ grep "^--- blorp" actual
+'
+
+test_expect_success 'format-patch --default-prefix overrides format.noprefix' '
+ git -c format.noprefix \
+ format-patch -1 --default-prefix --stdout >actual &&
+ grep "^--- a/blorp" actual
+'
+
test_done
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index 2c13b62..b443626 100755
--- a/t/t4015-diff-whitespace.sh
+++ b/t/t4015-diff-whitespace.sh
@@ -1,14 +1,53 @@
#!/bin/sh
#
# Copyright (c) 2006 Johannes E. Schindelin
-#
+# Copyright (c) 2023 Google LLC
test_description='Test special whitespace in diff engine.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-diff.sh
+for opt_res in --patch --quiet -s --stat --shortstat --dirstat=lines \
+ --raw! --name-only! --name-status!
+do
+ opts=${opt_res%!} expect_failure=
+ test "$opts" = "$opt_res" ||
+ expect_failure="test_expect_code 1"
+
+ test_expect_success "status with $opts (different)" '
+ echo foo >x &&
+ git add x &&
+ echo bar >x &&
+ test_expect_code 1 git diff -w $opts --exit-code x
+ '
+
+ test_expect_success POSIXPERM "status with $opts (mode differs)" '
+ test_when_finished "git update-index --chmod=-x x" &&
+ echo foo >x &&
+ git add x &&
+ git update-index --chmod=+x x &&
+ test_expect_code 1 git diff -w $opts --exit-code x
+ '
+
+ test_expect_success "status with $opts (removing an empty file)" '
+ : >x &&
+ git add x &&
+ rm x &&
+ test_expect_code 1 git diff -w $opts --exit-code -- x
+ '
+
+ test_expect_success "status with $opts (different but equivalent)" '
+ echo foo >x &&
+ git add x &&
+ echo " foo" >x &&
+ $expect_failure git diff -w $opts --exit-code x
+ '
+done
+
test_expect_success "Ray Lehtiniemi's example" '
cat <<-\EOF >x &&
do {
@@ -843,7 +882,7 @@ test_expect_success 'whitespace changes with modification reported (diffstat)' '
test_expect_success 'whitespace-only changes reported across renames (diffstat)' '
git reset --hard &&
- for i in 1 2 3 4 5 6 7 8 9; do echo "$i$i$i$i$i$i"; done >x &&
+ for i in 1 2 3 4 5 6 7 8 9; do echo "$i$i$i$i$i$i" || return 1; done >x &&
git add x &&
git commit -m "base" &&
sed -e "5s/^/ /" x >z &&
@@ -859,7 +898,7 @@ test_expect_success 'whitespace-only changes reported across renames (diffstat)'
test_expect_success 'whitespace-only changes reported across renames' '
git reset --hard HEAD~1 &&
- for i in 1 2 3 4 5 6 7 8 9; do echo "$i$i$i$i$i$i"; done >x &&
+ for i in 1 2 3 4 5 6 7 8 9; do echo "$i$i$i$i$i$i" || return 1; done >x &&
git add x &&
hash_x=$(git hash-object x) &&
before=$(git rev-parse --short "$hash_x") &&
@@ -907,7 +946,7 @@ test_expect_success 'combined diff with autocrlf conversion' '
git commit -m "the other side" x &&
git config core.autocrlf true &&
test_must_fail git merge one-side >actual &&
- test_i18ngrep "Automatic merge failed" actual &&
+ test_grep "Automatic merge failed" actual &&
git diff >actual.raw &&
sed -e "1,/^@@@/d" actual.raw >actual &&
@@ -1442,6 +1481,143 @@ test_expect_success 'detect permutations inside moved code -- dimmed-zebra' '
test_cmp expected actual
'
+test_expect_success 'zebra alternate color is only used when necessary' '
+ cat >old.txt <<-\EOF &&
+ line 1A should be marked as oldMoved newMovedAlternate
+ line 1B should be marked as oldMoved newMovedAlternate
+ unchanged
+ line 2A should be marked as oldMoved newMovedAlternate
+ line 2B should be marked as oldMoved newMovedAlternate
+ line 3A should be marked as oldMovedAlternate newMoved
+ line 3B should be marked as oldMovedAlternate newMoved
+ unchanged
+ line 4A should be marked as oldMoved newMovedAlternate
+ line 4B should be marked as oldMoved newMovedAlternate
+ line 5A should be marked as oldMovedAlternate newMoved
+ line 5B should be marked as oldMovedAlternate newMoved
+ line 6A should be marked as oldMoved newMoved
+ line 6B should be marked as oldMoved newMoved
+ EOF
+ cat >new.txt <<-\EOF &&
+ line 1A should be marked as oldMoved newMovedAlternate
+ line 1B should be marked as oldMoved newMovedAlternate
+ unchanged
+ line 3A should be marked as oldMovedAlternate newMoved
+ line 3B should be marked as oldMovedAlternate newMoved
+ line 2A should be marked as oldMoved newMovedAlternate
+ line 2B should be marked as oldMoved newMovedAlternate
+ unchanged
+ line 6A should be marked as oldMoved newMoved
+ line 6B should be marked as oldMoved newMoved
+ line 4A should be marked as oldMoved newMovedAlternate
+ line 4B should be marked as oldMoved newMovedAlternate
+ line 5A should be marked as oldMovedAlternate newMoved
+ line 5B should be marked as oldMovedAlternate newMoved
+ EOF
+ test_expect_code 1 git diff --no-index --color --color-moved=zebra \
+ --color-moved-ws=allow-indentation-change \
+ old.txt new.txt >output &&
+ grep -v index output | test_decode_color >actual &&
+ cat >expected <<-\EOF &&
+ <BOLD>diff --git a/old.txt b/new.txt<RESET>
+ <BOLD>--- a/old.txt<RESET>
+ <BOLD>+++ b/new.txt<RESET>
+ <CYAN>@@ -1,14 +1,14 @@<RESET>
+ <BOLD;MAGENTA>-line 1A should be marked as oldMoved newMovedAlternate<RESET>
+ <BOLD;MAGENTA>-line 1B should be marked as oldMoved newMovedAlternate<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN> line 1A should be marked as oldMoved newMovedAlternate<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN> line 1B should be marked as oldMoved newMovedAlternate<RESET>
+ unchanged<RESET>
+ <BOLD;MAGENTA>-line 2A should be marked as oldMoved newMovedAlternate<RESET>
+ <BOLD;MAGENTA>-line 2B should be marked as oldMoved newMovedAlternate<RESET>
+ <BOLD;BLUE>-line 3A should be marked as oldMovedAlternate newMoved<RESET>
+ <BOLD;BLUE>-line 3B should be marked as oldMovedAlternate newMoved<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN> line 3A should be marked as oldMovedAlternate newMoved<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN> line 3B should be marked as oldMovedAlternate newMoved<RESET>
+ <BOLD;YELLOW>+<RESET><BOLD;YELLOW> line 2A should be marked as oldMoved newMovedAlternate<RESET>
+ <BOLD;YELLOW>+<RESET><BOLD;YELLOW> line 2B should be marked as oldMoved newMovedAlternate<RESET>
+ unchanged<RESET>
+ <BOLD;MAGENTA>-line 4A should be marked as oldMoved newMovedAlternate<RESET>
+ <BOLD;MAGENTA>-line 4B should be marked as oldMoved newMovedAlternate<RESET>
+ <BOLD;BLUE>-line 5A should be marked as oldMovedAlternate newMoved<RESET>
+ <BOLD;BLUE>-line 5B should be marked as oldMovedAlternate newMoved<RESET>
+ <BOLD;MAGENTA>-line 6A should be marked as oldMoved newMoved<RESET>
+ <BOLD;MAGENTA>-line 6B should be marked as oldMoved newMoved<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN> line 6A should be marked as oldMoved newMoved<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN> line 6B should be marked as oldMoved newMoved<RESET>
+ <BOLD;YELLOW>+<RESET><BOLD;YELLOW> line 4A should be marked as oldMoved newMovedAlternate<RESET>
+ <BOLD;YELLOW>+<RESET><BOLD;YELLOW> line 4B should be marked as oldMoved newMovedAlternate<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN> line 5A should be marked as oldMovedAlternate newMoved<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN> line 5B should be marked as oldMovedAlternate newMoved<RESET>
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'short lines of opposite sign do not get marked as moved' '
+ cat >old.txt <<-\EOF &&
+ this line should be marked as moved
+ unchanged
+ unchanged
+ unchanged
+ unchanged
+ too short
+ this line should be marked as oldMoved newMoved
+ this line should be marked as oldMovedAlternate newMoved
+ unchanged 1
+ unchanged 2
+ unchanged 3
+ unchanged 4
+ this line should be marked as oldMoved newMoved/newMovedAlternate
+ EOF
+ cat >new.txt <<-\EOF &&
+ too short
+ unchanged
+ unchanged
+ this line should be marked as moved
+ too short
+ unchanged
+ unchanged
+ this line should be marked as oldMoved newMoved/newMovedAlternate
+ unchanged 1
+ unchanged 2
+ this line should be marked as oldMovedAlternate newMoved
+ this line should be marked as oldMoved newMoved/newMovedAlternate
+ unchanged 3
+ this line should be marked as oldMoved newMoved
+ unchanged 4
+ EOF
+ test_expect_code 1 git diff --no-index --color --color-moved=zebra \
+ old.txt new.txt >output && cat output &&
+ grep -v index output | test_decode_color >actual &&
+ cat >expect <<-\EOF &&
+ <BOLD>diff --git a/old.txt b/new.txt<RESET>
+ <BOLD>--- a/old.txt<RESET>
+ <BOLD>+++ b/new.txt<RESET>
+ <CYAN>@@ -1,13 +1,15 @@<RESET>
+ <BOLD;MAGENTA>-this line should be marked as moved<RESET>
+ <GREEN>+<RESET><GREEN>too short<RESET>
+ unchanged<RESET>
+ unchanged<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN>this line should be marked as moved<RESET>
+ <GREEN>+<RESET><GREEN>too short<RESET>
+ unchanged<RESET>
+ unchanged<RESET>
+ <RED>-too short<RESET>
+ <BOLD;MAGENTA>-this line should be marked as oldMoved newMoved<RESET>
+ <BOLD;BLUE>-this line should be marked as oldMovedAlternate newMoved<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN>this line should be marked as oldMoved newMoved/newMovedAlternate<RESET>
+ unchanged 1<RESET>
+ unchanged 2<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN>this line should be marked as oldMovedAlternate newMoved<RESET>
+ <BOLD;YELLOW>+<RESET><BOLD;YELLOW>this line should be marked as oldMoved newMoved/newMovedAlternate<RESET>
+ unchanged 3<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN>this line should be marked as oldMoved newMoved<RESET>
+ unchanged 4<RESET>
+ <BOLD;MAGENTA>-this line should be marked as oldMoved newMoved/newMovedAlternate<RESET>
+ EOF
+ test_cmp expect actual
+'
+
test_expect_success 'cmd option assumes configured colored-moved' '
test_config color.diff.oldMoved "magenta" &&
test_config color.diff.newMoved "cyan" &&
@@ -1485,7 +1661,7 @@ test_expect_success 'cmd option assumes configured colored-moved' '
test_cmp expected actual
'
-test_expect_success 'no effect from --color-moved with --word-diff' '
+test_expect_success 'no effect on diff from --color-moved with --word-diff' '
cat <<-\EOF >text.txt &&
Lorem Ipsum is simply dummy text of the printing and typesetting industry.
EOF
@@ -1499,6 +1675,12 @@ test_expect_success 'no effect from --color-moved with --word-diff' '
test_cmp expect actual
'
+test_expect_success 'no effect on show from --color-moved with --word-diff' '
+ git show --color-moved --word-diff >actual &&
+ git show --word-diff >expect &&
+ test_cmp expect actual
+'
+
test_expect_success 'set up whitespace tests' '
git reset --hard &&
# Note that these lines have no leading or trailing whitespace.
@@ -1833,6 +2015,52 @@ test_expect_success '--color-moved treats adjacent blocks as separate for MIN_AL
test_cmp expected actual
'
+test_expect_success '--color-moved rewinds for MIN_ALNUM_COUNT' '
+ git reset --hard &&
+ test_write_lines >file \
+ A B C one two three four five six seven D E F G H I J &&
+ git add file &&
+ test_write_lines >file \
+ one two A B C D E F G H I J two three four five six seven &&
+ git diff --color-moved=zebra -- file &&
+
+ git diff --color-moved=zebra --color -- file >actual.raw &&
+ grep -v "index" actual.raw | test_decode_color >actual &&
+ cat >expected <<-\EOF &&
+ <BOLD>diff --git a/file b/file<RESET>
+ <BOLD>--- a/file<RESET>
+ <BOLD>+++ b/file<RESET>
+ <CYAN>@@ -1,13 +1,8 @@<RESET>
+ <GREEN>+<RESET><GREEN>one<RESET>
+ <GREEN>+<RESET><GREEN>two<RESET>
+ A<RESET>
+ B<RESET>
+ C<RESET>
+ <RED>-one<RESET>
+ <BOLD;MAGENTA>-two<RESET>
+ <BOLD;MAGENTA>-three<RESET>
+ <BOLD;MAGENTA>-four<RESET>
+ <BOLD;MAGENTA>-five<RESET>
+ <BOLD;MAGENTA>-six<RESET>
+ <BOLD;MAGENTA>-seven<RESET>
+ D<RESET>
+ E<RESET>
+ F<RESET>
+ <CYAN>@@ -15,3 +10,9 @@<RESET> <RESET>G<RESET>
+ H<RESET>
+ I<RESET>
+ J<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN>two<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN>three<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN>four<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN>five<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN>six<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN>seven<RESET>
+ EOF
+
+ test_cmp expected actual
+'
+
test_expect_success 'move detection with submodules' '
test_create_repo bananas &&
echo ripe >bananas/recipe &&
@@ -1996,37 +2224,37 @@ test_expect_success 'compare whitespace delta across moved blocks' '
test_expect_success 'bogus settings in move detection erroring out' '
test_must_fail git diff --color-moved=bogus 2>err &&
- test_i18ngrep "must be one of" err &&
- test_i18ngrep bogus err &&
+ test_grep "must be one of" err &&
+ test_grep bogus err &&
test_must_fail git -c diff.colormoved=bogus diff 2>err &&
- test_i18ngrep "must be one of" err &&
- test_i18ngrep "from command-line config" err &&
+ test_grep "must be one of" err &&
+ test_grep "from command-line config" err &&
test_must_fail git diff --color-moved-ws=bogus 2>err &&
- test_i18ngrep "possible values" err &&
- test_i18ngrep bogus err &&
+ test_grep "possible values" err &&
+ test_grep bogus err &&
test_must_fail git -c diff.colormovedws=bogus diff 2>err &&
- test_i18ngrep "possible values" err &&
- test_i18ngrep "from command-line config" err
+ test_grep "possible values" err &&
+ test_grep "from command-line config" err
'
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_grep allow-indentation-change err
'
EMPTY=''
test_expect_success 'compare mixed whitespace delta across moved blocks' '
git reset --hard &&
- tr Q_ "\t " <<-EOF >text.txt &&
- ${EMPTY}
- ____too short without
- ${EMPTY}
+ tr "^|Q_" "\f\v\t " <<-EOF >text.txt &&
+ ^__
+ |____too short without
+ ^
___being grouped across blank line
${EMPTY}
context
@@ -2045,7 +2273,7 @@ test_expect_success 'compare mixed whitespace delta across moved blocks' '
git add text.txt &&
git commit -m "add text.txt" &&
- tr Q_ "\t " <<-EOF >text.txt &&
+ tr "^|Q_" "\f\v\t " <<-EOF >text.txt &&
context
lines
to
@@ -2056,7 +2284,7 @@ test_expect_success 'compare mixed whitespace delta across moved blocks' '
${EMPTY}
QQtoo short without
${EMPTY}
- Q_______being grouped across blank line
+ ^Q_______being grouped across blank line
${EMPTY}
Q_QThese two lines have had their
indentation reduced by four spaces
@@ -2068,16 +2296,16 @@ test_expect_success 'compare mixed whitespace delta across moved blocks' '
-c core.whitespace=space-before-tab \
diff --color --color-moved --ws-error-highlight=all \
--color-moved-ws=allow-indentation-change >actual.raw &&
- grep -v "index" actual.raw | test_decode_color >actual &&
+ grep -v "index" actual.raw | tr "\f\v" "^|" | test_decode_color >actual &&
cat <<-\EOF >expected &&
<BOLD>diff --git a/text.txt b/text.txt<RESET>
<BOLD>--- a/text.txt<RESET>
<BOLD>+++ b/text.txt<RESET>
<CYAN>@@ -1,16 +1,16 @@<RESET>
- <BOLD;MAGENTA>-<RESET>
- <BOLD;MAGENTA>-<RESET><BOLD;MAGENTA> too short without<RESET>
- <BOLD;MAGENTA>-<RESET>
+ <BOLD;MAGENTA>-<RESET><BOLD;MAGENTA>^<RESET><BRED> <RESET>
+ <BOLD;MAGENTA>-<RESET><BOLD;MAGENTA>| too short without<RESET>
+ <BOLD;MAGENTA>-<RESET><BOLD;MAGENTA>^<RESET>
<BOLD;MAGENTA>-<RESET><BOLD;MAGENTA> being grouped across blank line<RESET>
<BOLD;MAGENTA>-<RESET>
<RESET>context<RESET>
@@ -2097,7 +2325,7 @@ test_expect_success 'compare mixed whitespace delta across moved blocks' '
<BOLD;YELLOW>+<RESET>
<BOLD;YELLOW>+<RESET> <BOLD;YELLOW>too short without<RESET>
<BOLD;YELLOW>+<RESET>
- <BOLD;YELLOW>+<RESET> <BOLD;YELLOW> being grouped across blank line<RESET>
+ <BOLD;YELLOW>+<RESET><BOLD;YELLOW>^ being grouped across blank line<RESET>
<BOLD;YELLOW>+<RESET>
<BOLD;CYAN>+<RESET> <BRED> <RESET> <BOLD;CYAN>These two lines have had their<RESET>
<BOLD;CYAN>+<RESET><BOLD;CYAN>indentation reduced by four spaces<RESET>
diff --git a/t/t4016-diff-quote.sh b/t/t4016-diff-quote.sh
index 876271d..5a8d887 100755
--- a/t/t4016-diff-quote.sh
+++ b/t/t4016-diff-quote.sh
@@ -6,6 +6,7 @@
test_description='Quoting paths in diff output.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
P0='pathname'
diff --git a/t/t4017-diff-retval.sh b/t/t4017-diff-retval.sh
index ed461f4..f439f46 100755
--- a/t/t4017-diff-retval.sh
+++ b/t/t4017-diff-retval.sh
@@ -5,6 +5,7 @@ test_description='Return value of diffs'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -137,4 +138,9 @@ test_expect_success 'check honors conflict marker length' '
git reset --hard
'
+test_expect_success 'option errors are not confused by --exit-code' '
+ test_must_fail git diff --exit-code --nonsense 2>err &&
+ grep '^usage:' err
+'
+
test_done
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 740696c..e026fac 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -53,15 +53,34 @@ do
echo "*.java diff=$p" >.gitattributes &&
test_expect_code 1 git diff --no-index \
A.java B.java 2>msg &&
- test_i18ngrep ! fatal msg &&
- test_i18ngrep ! error msg
+ test_grep ! fatal msg &&
+ test_grep ! error msg
'
test_expect_success "builtin $p wordRegex pattern compiles" '
echo "*.java diff=$p" >.gitattributes &&
test_expect_code 1 git diff --no-index --word-diff \
A.java B.java 2>msg &&
- test_i18ngrep ! fatal msg &&
- test_i18ngrep ! error msg
+ test_grep ! fatal msg &&
+ test_grep ! error msg
+ '
+
+ test_expect_success "builtin $p pattern compiles on bare repo with --attr-source" '
+ test_when_finished "rm -rf bare.git" &&
+ git checkout -B master &&
+ git add . &&
+ echo "*.java diff=notexist" >.gitattributes &&
+ git add .gitattributes &&
+ git commit -am "changing gitattributes" &&
+ git checkout -B branchA &&
+ echo "*.java diff=$p" >.gitattributes &&
+ git add .gitattributes &&
+ git commit -am "changing gitattributes" &&
+ git clone --bare --no-local . bare.git &&
+ git -C bare.git symbolic-ref HEAD refs/heads/master &&
+ test_expect_code 1 git -C bare.git --attr-source=branchA \
+ diff --exit-code HEAD:A.java HEAD:B.java 2>msg &&
+ test_grep ! fatal msg &&
+ test_grep ! error msg
'
done
@@ -69,13 +88,13 @@ test_expect_success 'last regexp must not be negated' '
echo "*.java diff=java" >.gitattributes &&
test_config diff.java.funcname "!static" &&
test_expect_code 128 git diff --no-index A.java B.java 2>msg &&
- test_i18ngrep ": Last expression must not be negated:" msg
+ test_grep ": Last expression must not be negated:" msg
'
test_expect_success 'setup hunk header tests' '
for i in $diffpatterns
do
- echo "$i-* diff=$i"
+ echo "$i-* diff=$i" || return 1
done > .gitattributes &&
# add all test files to the index
diff --git a/t/t4018/csharp-exclude-assignments b/t/t4018/csharp-exclude-assignments
new file mode 100644
index 0000000..239f312
--- /dev/null
+++ b/t/t4018/csharp-exclude-assignments
@@ -0,0 +1,20 @@
+class Example
+{
+ string Method(int RIGHT)
+ {
+ var constantAssignment = "test";
+ var methodAssignment = MethodCall();
+ var multiLineMethodAssignment = MethodCall(
+ );
+ var multiLine = "first"
+ + MethodCall()
+ +
+ ( MethodCall()
+ )
+ + MethodCall();
+
+ return "ChangeMe";
+ }
+
+ string MethodCall(int a = 0, int b = 0) => "test";
+}
diff --git a/t/t4018/csharp-exclude-control-statements b/t/t4018/csharp-exclude-control-statements
new file mode 100644
index 0000000..3a0f404
--- /dev/null
+++ b/t/t4018/csharp-exclude-control-statements
@@ -0,0 +1,34 @@
+class Example
+{
+ string Method(int RIGHT)
+ {
+ if (false)
+ {
+ return "out";
+ }
+ else { }
+ if (true) MethodCall(
+ );
+ else MethodCall(
+ );
+ switch ("test")
+ {
+ case "one":
+ return MethodCall(
+ );
+ case "two":
+ break;
+ }
+ (int, int) tuple = (1, 4);
+ switch (tuple)
+ {
+ case (1, 4):
+ MethodCall();
+ break;
+ }
+
+ return "ChangeMe";
+ }
+
+ string MethodCall(int a = 0, int b = 0) => "test";
+}
diff --git a/t/t4018/csharp-exclude-exceptions b/t/t4018/csharp-exclude-exceptions
new file mode 100644
index 0000000..b1e6425
--- /dev/null
+++ b/t/t4018/csharp-exclude-exceptions
@@ -0,0 +1,29 @@
+using System;
+
+class Example
+{
+ string Method(int RIGHT)
+ {
+ try
+ {
+ throw new Exception("fail");
+ }
+ catch (Exception)
+ {
+ }
+ finally
+ {
+ }
+ try { } catch (Exception) {}
+ try
+ {
+ throw GetException(
+ );
+ }
+ catch (Exception) { }
+
+ return "ChangeMe";
+ }
+
+ Exception GetException() => new Exception("fail");
+}
diff --git a/t/t4018/csharp-exclude-generic-method-calls b/t/t4018/csharp-exclude-generic-method-calls
new file mode 100644
index 0000000..31af546
--- /dev/null
+++ b/t/t4018/csharp-exclude-generic-method-calls
@@ -0,0 +1,12 @@
+class Example
+{
+ string Method(int RIGHT)
+ {
+ GenericMethodCall<int, int>(
+ );
+
+ return "ChangeMe";
+ }
+
+ string GenericMethodCall<T, T2>() => "test";
+}
diff --git a/t/t4018/csharp-exclude-init-dispose b/t/t4018/csharp-exclude-init-dispose
new file mode 100644
index 0000000..2bc8e19
--- /dev/null
+++ b/t/t4018/csharp-exclude-init-dispose
@@ -0,0 +1,22 @@
+using System;
+
+class Example : IDisposable
+{
+ string Method(int RIGHT)
+ {
+ new Example();
+ new Example(
+ );
+ new Example { };
+ using (this)
+ {
+ }
+ var def =
+ this is default(
+ Example);
+
+ return "ChangeMe";
+ }
+
+ public void Dispose() {}
+}
diff --git a/t/t4018/csharp-exclude-iterations b/t/t4018/csharp-exclude-iterations
new file mode 100644
index 0000000..960aa18
--- /dev/null
+++ b/t/t4018/csharp-exclude-iterations
@@ -0,0 +1,26 @@
+using System.Linq;
+
+class Example
+{
+ string Method(int RIGHT)
+ {
+ do { } while (true);
+ do MethodCall(
+ ); while (true);
+ while (true);
+ while (true) {
+ break;
+ }
+ for (int i = 0; i < 10; ++i)
+ {
+ }
+ foreach (int i in Enumerable.Range(0, 10))
+ {
+ }
+ int[] numbers = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0];
+
+ return "ChangeMe";
+ }
+
+ string MethodCall(int a = 0, int b = 0) => "test";
+}
diff --git a/t/t4018/csharp-exclude-method-calls b/t/t4018/csharp-exclude-method-calls
new file mode 100644
index 0000000..51e2dc2
--- /dev/null
+++ b/t/t4018/csharp-exclude-method-calls
@@ -0,0 +1,20 @@
+class Example
+{
+ string Method(int RIGHT)
+ {
+ MethodCall();
+ MethodCall(1, 2);
+ MethodCall(
+ 1, 2);
+ MethodCall(
+ 1, 2,
+ 3);
+ MethodCall(
+ 1, MethodCall(),
+ 2);
+
+ return "ChangeMe";
+ }
+
+ int MethodCall(int a = 0, int b = 0, int c = 0) => 42;
+}
diff --git a/t/t4018/csharp-exclude-other b/t/t4018/csharp-exclude-other
new file mode 100644
index 0000000..4d5581c
--- /dev/null
+++ b/t/t4018/csharp-exclude-other
@@ -0,0 +1,18 @@
+class Example
+{
+ string Method(int RIGHT)
+ {
+ lock (this)
+ {
+ }
+ unsafe
+ {
+ byte[] bytes = [1, 2, 3];
+ fixed (byte* pointerToFirst = bytes)
+ {
+ }
+ }
+
+ return "ChangeMe";
+ }
+}
diff --git a/t/t4018/csharp-method b/t/t4018/csharp-method
new file mode 100644
index 0000000..16b367a
--- /dev/null
+++ b/t/t4018/csharp-method
@@ -0,0 +1,10 @@
+class Example
+{
+ string Method(int RIGHT)
+ {
+ // Filler
+ // Filler
+
+ return "ChangeMe";
+ }
+}
diff --git a/t/t4018/csharp-method-array b/t/t4018/csharp-method-array
new file mode 100644
index 0000000..1126de8
--- /dev/null
+++ b/t/t4018/csharp-method-array
@@ -0,0 +1,10 @@
+class Example
+{
+ string[] Method(int RIGHT)
+ {
+ // Filler
+ // Filler
+
+ return ["ChangeMe"];
+ }
+}
diff --git a/t/t4018/csharp-method-explicit b/t/t4018/csharp-method-explicit
new file mode 100644
index 0000000..5a71011
--- /dev/null
+++ b/t/t4018/csharp-method-explicit
@@ -0,0 +1,12 @@
+using System;
+
+class Example : IDisposable
+{
+ void IDisposable.Dispose() // RIGHT
+ {
+ // Filler
+ // Filler
+
+ // ChangeMe
+ }
+}
diff --git a/t/t4018/csharp-method-generics b/t/t4018/csharp-method-generics
new file mode 100644
index 0000000..b3216bf
--- /dev/null
+++ b/t/t4018/csharp-method-generics
@@ -0,0 +1,11 @@
+class Example<T1, T2>
+{
+ Example<int, string> Method<TA, TB>(TA RIGHT, TB b)
+ {
+ // Filler
+ // Filler
+
+ // ChangeMe
+ return null;
+ }
+}
diff --git a/t/t4018/csharp-method-generics-alternate-spaces b/t/t4018/csharp-method-generics-alternate-spaces
new file mode 100644
index 0000000..9583621
--- /dev/null
+++ b/t/t4018/csharp-method-generics-alternate-spaces
@@ -0,0 +1,11 @@
+class Example<T1, T2>
+{
+ Example<int,string> Method<TA ,TB>(TA RIGHT, TB b)
+ {
+ // Filler
+ // Filler
+
+ // ChangeMe
+ return null;
+ }
+}
diff --git a/t/t4018/csharp-method-modifiers b/t/t4018/csharp-method-modifiers
new file mode 100644
index 0000000..caefa8e
--- /dev/null
+++ b/t/t4018/csharp-method-modifiers
@@ -0,0 +1,13 @@
+using System.Threading.Tasks;
+
+class Example
+{
+ static internal async Task Method(int RIGHT)
+ {
+ // Filler
+ // Filler
+
+ // ChangeMe
+ await Task.Delay(1);
+ }
+}
diff --git a/t/t4018/csharp-method-multiline b/t/t4018/csharp-method-multiline
new file mode 100644
index 0000000..3983ff4
--- /dev/null
+++ b/t/t4018/csharp-method-multiline
@@ -0,0 +1,10 @@
+class Example
+{
+ string Method_RIGHT(
+ int a,
+ int b,
+ int c)
+ {
+ return "ChangeMe";
+ }
+}
diff --git a/t/t4018/csharp-method-params b/t/t4018/csharp-method-params
new file mode 100644
index 0000000..3f00410
--- /dev/null
+++ b/t/t4018/csharp-method-params
@@ -0,0 +1,10 @@
+class Example
+{
+ string Method(int RIGHT, int b, int c = 42)
+ {
+ // Filler
+ // Filler
+
+ return "ChangeMe";
+ }
+}
diff --git a/t/t4018/csharp-method-special-chars b/t/t4018/csharp-method-special-chars
new file mode 100644
index 0000000..e6c7bc0
--- /dev/null
+++ b/t/t4018/csharp-method-special-chars
@@ -0,0 +1,11 @@
+class @Some_Type
+{
+ @Some_Type @Method_With_Underscore(int RIGHT)
+ {
+ // Filler
+ // Filler
+
+ // ChangeMe
+ return new @Some_Type();
+ }
+}
diff --git a/t/t4018/csharp-method-with-spacing b/t/t4018/csharp-method-with-spacing
new file mode 100644
index 0000000..233bb97
--- /dev/null
+++ b/t/t4018/csharp-method-with-spacing
@@ -0,0 +1,10 @@
+class Example
+{
+ string Method ( int RIGHT )
+ {
+ // Filler
+ // Filler
+
+ return "ChangeMe";
+ }
+}
diff --git a/t/t4018/csharp-property b/t/t4018/csharp-property
new file mode 100644
index 0000000..e56dfce3
--- /dev/null
+++ b/t/t4018/csharp-property
@@ -0,0 +1,11 @@
+class Example
+{
+ public bool RIGHT
+ {
+ get { return true; }
+ set
+ {
+ // ChangeMe
+ }
+ }
+}
diff --git a/t/t4018/csharp-property-braces-same-line b/t/t4018/csharp-property-braces-same-line
new file mode 100644
index 0000000..608131d
--- /dev/null
+++ b/t/t4018/csharp-property-braces-same-line
@@ -0,0 +1,10 @@
+class Example
+{
+ public bool RIGHT {
+ get { return true; }
+ set
+ {
+ // ChangeMe
+ }
+ }
+}
diff --git a/t/t4018/java-class-brace-on-separate-line b/t/t4018/java-class-brace-on-separate-line
new file mode 100644
index 0000000..8795acd
--- /dev/null
+++ b/t/t4018/java-class-brace-on-separate-line
@@ -0,0 +1,6 @@
+class RIGHT
+{
+ static int ONE;
+ static int TWO;
+ static int ChangeMe;
+}
diff --git a/t/t4018/java-class-space-before-type-parameters b/t/t4018/java-class-space-before-type-parameters
new file mode 100644
index 0000000..0bdef1d
--- /dev/null
+++ b/t/t4018/java-class-space-before-type-parameters
@@ -0,0 +1,6 @@
+class RIGHT <TYPE, PARAMS, AFTER, SPACE> {
+ static int ONE;
+ static int TWO;
+ static int THREE;
+ private A ChangeMe;
+}
diff --git a/t/t4018/java-class-type-parameters b/t/t4018/java-class-type-parameters
new file mode 100644
index 0000000..579aa7a
--- /dev/null
+++ b/t/t4018/java-class-type-parameters
@@ -0,0 +1,6 @@
+class RIGHT<A, B> {
+ static int ONE;
+ static int TWO;
+ static int THREE;
+ private A ChangeMe;
+}
diff --git a/t/t4018/java-class-type-parameters-implements b/t/t4018/java-class-type-parameters-implements
new file mode 100644
index 0000000..b8038b1
--- /dev/null
+++ b/t/t4018/java-class-type-parameters-implements
@@ -0,0 +1,6 @@
+class RIGHT<A, B> implements List<A> {
+ static int ONE;
+ static int TWO;
+ static int THREE;
+ private A ChangeMe;
+}
diff --git a/t/t4018/java-interface-type-parameters b/t/t4018/java-interface-type-parameters
new file mode 100644
index 0000000..a4baa1a
--- /dev/null
+++ b/t/t4018/java-interface-type-parameters
@@ -0,0 +1,6 @@
+interface RIGHT<A, B> {
+ static int ONE;
+ static int TWO;
+ static int THREE;
+ public B foo(A ChangeMe);
+}
diff --git a/t/t4018/java-interface-type-parameters-extends b/t/t4018/java-interface-type-parameters-extends
new file mode 100644
index 0000000..31d7fb3
--- /dev/null
+++ b/t/t4018/java-interface-type-parameters-extends
@@ -0,0 +1,6 @@
+interface RIGHT<A, B> extends Function<A, B> {
+ static int ONE;
+ static int TWO;
+ static int THREE;
+ public B foo(A ChangeMe);
+}
diff --git a/t/t4018/java-non-sealed b/t/t4018/java-non-sealed
new file mode 100644
index 0000000..069087c
--- /dev/null
+++ b/t/t4018/java-non-sealed
@@ -0,0 +1,8 @@
+public abstract sealed class SealedClass {
+ public static non-sealed class RIGHT extends SealedClass {
+ static int ONE;
+ static int TWO;
+ static int THREE;
+ private int ChangeMe;
+ }
+}
diff --git a/t/t4018/java-record b/t/t4018/java-record
new file mode 100644
index 0000000..97aa819
--- /dev/null
+++ b/t/t4018/java-record
@@ -0,0 +1,6 @@
+public record RIGHT(int comp1, double comp2, String comp3) {
+ static int ONE;
+ static int TWO;
+ static int THREE;
+ static int ChangeMe;
+}
diff --git a/t/t4018/java-record-space-before-components b/t/t4018/java-record-space-before-components
new file mode 100644
index 0000000..9827f22
--- /dev/null
+++ b/t/t4018/java-record-space-before-components
@@ -0,0 +1,6 @@
+public record RIGHT (String components, String after, String space) {
+ static int ONE;
+ static int TWO;
+ static int THREE;
+ static int ChangeMe;
+}
diff --git a/t/t4018/java-record-type-parameters b/t/t4018/java-record-type-parameters
new file mode 100644
index 0000000..f62a035
--- /dev/null
+++ b/t/t4018/java-record-type-parameters
@@ -0,0 +1,6 @@
+public record RIGHT<A, N extends Number>(A comp1, N comp2, int comp3) {
+ static int ONE;
+ static int TWO;
+ static int THREE;
+ static int ChangeMe;
+}
diff --git a/t/t4018/java-sealed b/t/t4018/java-sealed
new file mode 100644
index 0000000..785fbc6
--- /dev/null
+++ b/t/t4018/java-sealed
@@ -0,0 +1,7 @@
+public abstract sealed class Sealed { // RIGHT
+ static int ONE;
+ static int TWO;
+ static int THREE;
+ public final class ChangeMe extends Sealed {
+ }
+}
diff --git a/t/t4018/java-sealed-permits b/t/t4018/java-sealed-permits
new file mode 100644
index 0000000..18dd489
--- /dev/null
+++ b/t/t4018/java-sealed-permits
@@ -0,0 +1,6 @@
+public abstract sealed class RIGHT permits PermittedA, PermittedB {
+ static int ONE;
+ static int TWO;
+ static int THREE;
+ private int ChangeMe;
+}
diff --git a/t/t4018/java-sealed-type-parameters b/t/t4018/java-sealed-type-parameters
new file mode 100644
index 0000000..e6530c4
--- /dev/null
+++ b/t/t4018/java-sealed-type-parameters
@@ -0,0 +1,6 @@
+public abstract sealed class RIGHT<A, B> {
+ static int ONE;
+ static int TWO;
+ static int THREE;
+ private int ChangeMe;
+}
diff --git a/t/t4018/java-sealed-type-parameters-implements-permits b/t/t4018/java-sealed-type-parameters-implements-permits
new file mode 100644
index 0000000..bd6e6d3
--- /dev/null
+++ b/t/t4018/java-sealed-type-parameters-implements-permits
@@ -0,0 +1,6 @@
+public abstract sealed class RIGHT<A, B> implements List<A> permits PermittedA, PermittedB {
+ static int ONE;
+ static int TWO;
+ static int THREE;
+ private int ChangeMe;
+}
diff --git a/t/t4018/java-sealed-type-parameters-permits b/t/t4018/java-sealed-type-parameters-permits
new file mode 100644
index 0000000..25a0da6
--- /dev/null
+++ b/t/t4018/java-sealed-type-parameters-permits
@@ -0,0 +1,6 @@
+public abstract sealed class RIGHT<A, B> permits PermittedA, PermittedB {
+ static int ONE;
+ static int TWO;
+ static int THREE;
+ private int ChangeMe;
+}
diff --git a/t/t4018/kotlin-class b/t/t4018/kotlin-class
new file mode 100644
index 0000000..bb864f2
--- /dev/null
+++ b/t/t4018/kotlin-class
@@ -0,0 +1,5 @@
+class RIGHT {
+ //comment
+ //comment
+ return ChangeMe
+}
diff --git a/t/t4018/kotlin-enum-class b/t/t4018/kotlin-enum-class
new file mode 100644
index 0000000..8885f90
--- /dev/null
+++ b/t/t4018/kotlin-enum-class
@@ -0,0 +1,5 @@
+enum class RIGHT{
+ // Left
+ // a comment
+ ChangeMe
+}
diff --git a/t/t4018/kotlin-fun b/t/t4018/kotlin-fun
new file mode 100644
index 0000000..2a60280
--- /dev/null
+++ b/t/t4018/kotlin-fun
@@ -0,0 +1,5 @@
+fun RIGHT(){
+ //a comment
+ //b comment
+ return ChangeMe()
+}
diff --git a/t/t4018/kotlin-inheritace-class b/t/t4018/kotlin-inheritace-class
new file mode 100644
index 0000000..77376c1
--- /dev/null
+++ b/t/t4018/kotlin-inheritace-class
@@ -0,0 +1,5 @@
+open class RIGHT{
+ // a comment
+ // b comment
+ // ChangeMe
+}
diff --git a/t/t4018/kotlin-inline-class b/t/t4018/kotlin-inline-class
new file mode 100644
index 0000000..7bf46dd
--- /dev/null
+++ b/t/t4018/kotlin-inline-class
@@ -0,0 +1,5 @@
+value class RIGHT(Args){
+ // a comment
+ // b comment
+ ChangeMe
+}
diff --git a/t/t4018/kotlin-interface b/t/t4018/kotlin-interface
new file mode 100644
index 0000000..f686ba7
--- /dev/null
+++ b/t/t4018/kotlin-interface
@@ -0,0 +1,5 @@
+interface RIGHT{
+ //another comment
+ //another comment
+ //ChangeMe
+}
diff --git a/t/t4018/kotlin-nested-fun b/t/t4018/kotlin-nested-fun
new file mode 100644
index 0000000..1218685
--- /dev/null
+++ b/t/t4018/kotlin-nested-fun
@@ -0,0 +1,9 @@
+class LEFT{
+ class CENTER{
+ fun RIGHT( a:Int){
+ //comment
+ //comment
+ ChangeMe
+ }
+ }
+}
diff --git a/t/t4018/kotlin-public-class b/t/t4018/kotlin-public-class
new file mode 100644
index 0000000..9433fcc
--- /dev/null
+++ b/t/t4018/kotlin-public-class
@@ -0,0 +1,5 @@
+public class RIGHT{
+ //comment1
+ //comment2
+ ChangeMe
+}
diff --git a/t/t4018/kotlin-sealed-class b/t/t4018/kotlin-sealed-class
new file mode 100644
index 0000000..0efa4a4
--- /dev/null
+++ b/t/t4018/kotlin-sealed-class
@@ -0,0 +1,5 @@
+sealed class RIGHT {
+ // a comment
+ // b comment
+ ChangeMe
+}
diff --git a/t/t4019-diff-wserror.sh b/t/t4019-diff-wserror.sh
index c6135c7..d2b3109 100755
--- a/t/t4019-diff-wserror.sh
+++ b/t/t4019-diff-wserror.sh
@@ -2,6 +2,7 @@
test_description='diff whitespace error detection'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -286,9 +287,9 @@ test_expect_success 'do not color trailing cr in context' '
'
test_expect_success 'color new trailing blank lines' '
- { echo a; echo b; echo; echo; } >x &&
+ test_write_lines a b "" "" >x &&
git add x &&
- { echo a; echo; echo; echo; echo c; echo; echo; echo; echo; } >x &&
+ test_write_lines a "" "" "" c "" "" "" "" >x &&
git diff --color x >output &&
cnt=$($grep_a "${blue_grep}" output | wc -l) &&
test $cnt = 2
diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh
index e009826..fdd865f 100755
--- a/t/t4020-diff-external.sh
+++ b/t/t4020-diff-external.sh
@@ -2,6 +2,7 @@
test_description='external diff interface test'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -23,45 +24,38 @@ test_expect_success setup '
'
test_expect_success 'GIT_EXTERNAL_DIFF environment' '
-
- GIT_EXTERNAL_DIFF=echo git diff | {
- read path oldfile oldhex oldmode newfile newhex newmode &&
- test "z$path" = zfile &&
- test "z$oldmode" = z100644 &&
- test "z$newhex" = "z$ZERO_OID" &&
- test "z$newmode" = z100644 &&
- oh=$(git rev-parse --verify HEAD:file) &&
- test "z$oh" = "z$oldhex"
- }
+ cat >expect <<-EOF &&
+ file $(git rev-parse --verify HEAD:file) 100644 file $(test_oid zero) 100644
+ EOF
+ GIT_EXTERNAL_DIFF=echo git diff >out &&
+ cut -d" " -f1,3- <out >actual &&
+ test_cmp expect actual
'
test_expect_success 'GIT_EXTERNAL_DIFF environment should apply only to diff' '
-
- GIT_EXTERNAL_DIFF=echo git log -p -1 HEAD |
- grep "^diff --git a/file b/file"
+ GIT_EXTERNAL_DIFF=echo git log -p -1 HEAD >out &&
+ grep "^diff --git a/file b/file" out
'
test_expect_success 'GIT_EXTERNAL_DIFF environment and --no-ext-diff' '
-
- GIT_EXTERNAL_DIFF=echo git diff --no-ext-diff |
- grep "^diff --git a/file b/file"
+ GIT_EXTERNAL_DIFF=echo git diff --no-ext-diff >out &&
+ grep "^diff --git a/file b/file" out
'
test_expect_success SYMLINKS 'typechange diff' '
rm -f file &&
ln -s elif file &&
- GIT_EXTERNAL_DIFF=echo git diff | {
- read path oldfile oldhex oldmode newfile newhex newmode &&
- test "z$path" = zfile &&
- test "z$oldmode" = z100644 &&
- test "z$newhex" = "z$ZERO_OID" &&
- test "z$newmode" = z120000 &&
- oh=$(git rev-parse --verify HEAD:file) &&
- test "z$oh" = "z$oldhex"
- } &&
+
+ cat >expect <<-EOF &&
+ file $(git rev-parse --verify HEAD:file) 100644 $(test_oid zero) 120000
+ EOF
+ GIT_EXTERNAL_DIFF=echo git diff >out &&
+ cut -d" " -f1,3-4,6- <out >actual &&
+ test_cmp expect actual &&
+
GIT_EXTERNAL_DIFF=echo git diff --no-ext-diff >actual &&
git diff >expect &&
test_cmp expect actual
@@ -71,27 +65,25 @@ test_expect_success 'diff.external' '
git reset --hard &&
echo third >file &&
test_config diff.external echo &&
- git diff | {
- read path oldfile oldhex oldmode newfile newhex newmode &&
- test "z$path" = zfile &&
- test "z$oldmode" = z100644 &&
- test "z$newhex" = "z$ZERO_OID" &&
- test "z$newmode" = z100644 &&
- oh=$(git rev-parse --verify HEAD:file) &&
- test "z$oh" = "z$oldhex"
- }
+
+ cat >expect <<-EOF &&
+ file $(git rev-parse --verify HEAD:file) 100644 $(test_oid zero) 100644
+ EOF
+ git diff >out &&
+ cut -d" " -f1,3-4,6- <out >actual &&
+ test_cmp expect actual
'
test_expect_success 'diff.external should apply only to diff' '
test_config diff.external echo &&
- git log -p -1 HEAD |
- grep "^diff --git a/file b/file"
+ git log -p -1 HEAD >out &&
+ grep "^diff --git a/file b/file" out
'
test_expect_success 'diff.external and --no-ext-diff' '
test_config diff.external echo &&
- git diff --no-ext-diff |
- grep "^diff --git a/file b/file"
+ git diff --no-ext-diff >out &&
+ grep "^diff --git a/file b/file" out
'
test_expect_success 'diff attribute' '
@@ -102,29 +94,23 @@ test_expect_success 'diff attribute' '
echo >.gitattributes "file diff=parrot" &&
- git diff | {
- read path oldfile oldhex oldmode newfile newhex newmode &&
- test "z$path" = zfile &&
- test "z$oldmode" = z100644 &&
- test "z$newhex" = "z$ZERO_OID" &&
- test "z$newmode" = z100644 &&
- oh=$(git rev-parse --verify HEAD:file) &&
- test "z$oh" = "z$oldhex"
- }
-
+ cat >expect <<-EOF &&
+ file $(git rev-parse --verify HEAD:file) 100644 $(test_oid zero) 100644
+ EOF
+ git diff >out &&
+ cut -d" " -f1,3-4,6- <out >actual &&
+ test_cmp expect actual
'
-test_expect_success 'diff attribute should apply only to diff' '
-
- git log -p -1 HEAD |
- grep "^diff --git a/file b/file"
+test_expect_success !SANITIZE_LEAK 'diff attribute should apply only to diff' '
+ git log -p -1 HEAD >out &&
+ grep "^diff --git a/file b/file" out
'
test_expect_success 'diff attribute and --no-ext-diff' '
-
- git diff --no-ext-diff |
- grep "^diff --git a/file b/file"
+ git diff --no-ext-diff >out &&
+ grep "^diff --git a/file b/file" out
'
@@ -135,48 +121,55 @@ test_expect_success 'diff attribute' '
echo >.gitattributes "file diff=color" &&
- git diff | {
- read path oldfile oldhex oldmode newfile newhex newmode &&
- test "z$path" = zfile &&
- test "z$oldmode" = z100644 &&
- test "z$newhex" = "z$ZERO_OID" &&
- test "z$newmode" = z100644 &&
- oh=$(git rev-parse --verify HEAD:file) &&
- test "z$oh" = "z$oldhex"
- }
-
+ cat >expect <<-EOF &&
+ file $(git rev-parse --verify HEAD:file) 100644 $(test_oid zero) 100644
+ EOF
+ git diff >out &&
+ cut -d" " -f1,3-4,6- <out >actual &&
+ test_cmp expect actual
'
-test_expect_success 'diff attribute should apply only to diff' '
-
- git log -p -1 HEAD |
- grep "^diff --git a/file b/file"
+test_expect_success !SANITIZE_LEAK 'diff attribute should apply only to diff' '
+ git log -p -1 HEAD >out &&
+ grep "^diff --git a/file b/file" out
'
test_expect_success 'diff attribute and --no-ext-diff' '
-
- git diff --no-ext-diff |
- grep "^diff --git a/file b/file"
+ git diff --no-ext-diff >out &&
+ grep "^diff --git a/file b/file" out
'
test_expect_success 'GIT_EXTERNAL_DIFF trumps diff.external' '
>.gitattributes &&
test_config diff.external "echo ext-global" &&
- GIT_EXTERNAL_DIFF="echo ext-env" git diff | grep ext-env
+
+ cat >expect <<-EOF &&
+ ext-env file $(git rev-parse --verify HEAD:file) 100644 file $(test_oid zero) 100644
+ EOF
+ GIT_EXTERNAL_DIFF="echo ext-env" git diff >out &&
+ cut -d" " -f1-2,4- <out >actual &&
+ test_cmp expect actual
'
test_expect_success 'attributes trump GIT_EXTERNAL_DIFF and diff.external' '
test_config diff.foo.command "echo ext-attribute" &&
test_config diff.external "echo ext-global" &&
echo "file diff=foo" >.gitattributes &&
- GIT_EXTERNAL_DIFF="echo ext-env" git diff | grep ext-attribute
+
+ cat >expect <<-EOF &&
+ ext-attribute file $(git rev-parse --verify HEAD:file) 100644 file $(test_oid zero) 100644
+ EOF
+ GIT_EXTERNAL_DIFF="echo ext-env" git diff >out &&
+ cut -d" " -f1-2,4- <out >actual &&
+ test_cmp expect actual
'
test_expect_success 'no diff with -diff' '
echo >.gitattributes "file -diff" &&
- git diff | grep Binary
+ git diff >out &&
+ grep Binary out
'
echo NULZbetweenZwords | perl -pe 'y/Z/\000/' > file
@@ -213,12 +206,17 @@ test_expect_success 'GIT_EXTERNAL_DIFF path counter/total' '
'
test_expect_success 'GIT_EXTERNAL_DIFF generates pretty paths' '
+ test_when_finished "git rm -f file.ext" &&
touch file.ext &&
git add file.ext &&
echo with extension > file.ext &&
- GIT_EXTERNAL_DIFF=echo git diff file.ext | grep ......_file\.ext &&
- git update-index --force-remove file.ext &&
- rm file.ext
+
+ cat >expect <<-EOF &&
+ file.ext
+ EOF
+ GIT_EXTERNAL_DIFF=echo git diff file.ext >out &&
+ basename $(cut -d" " -f2 <out) >actual &&
+ test_cmp expect actual
'
echo "#!$SHELL_PATH" >fake-diff.sh
@@ -234,7 +232,7 @@ keep_only_cr () {
test_expect_success 'external diff with autocrlf = true' '
test_config core.autocrlf true &&
GIT_EXTERNAL_DIFF=./fake-diff.sh git diff &&
- test $(wc -l < crlfed.txt) = $(cat crlfed.txt | keep_only_cr | wc -c)
+ test $(wc -l <crlfed.txt) = $(keep_only_cr <crlfed.txt | wc -c)
'
test_expect_success 'diff --cached' '
diff --git a/t/t4021-format-patch-numbered.sh b/t/t4021-format-patch-numbered.sh
index 9be65fd..1219aa2 100755
--- a/t/t4021-format-patch-numbered.sh
+++ b/t/t4021-format-patch-numbered.sh
@@ -5,6 +5,7 @@
test_description='Format-patch numbering options'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t4022-diff-rewrite.sh b/t/t4022-diff-rewrite.sh
index 6d1c3d9..6fed993 100755
--- a/t/t4022-diff-rewrite.sh
+++ b/t/t4022-diff-rewrite.sh
@@ -3,15 +3,17 @@
test_description='rewrite diff'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-diff-data.sh
test_expect_success setup '
- cat "$TEST_DIRECTORY"/../COPYING >test &&
+ COPYING_test_data >test.data &&
+ cp test.data test &&
git add test &&
tr \
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" \
"nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM" \
- <"$TEST_DIRECTORY"/../COPYING >test &&
+ <test.data >test &&
echo "to be deleted" >test2 &&
blob=$(git hash-object test2) &&
blob=$(git rev-parse --short $blob) &&
@@ -22,7 +24,7 @@ test_expect_success setup '
test_expect_success 'detect rewrite' '
actual=$(git diff-files -B --summary test) &&
- verbose expr "$actual" : " rewrite test ([0-9]*%)$"
+ expr "$actual" : " rewrite test ([0-9]*%)$"
'
diff --git a/t/t4023-diff-rename-typechange.sh b/t/t4023-diff-rename-typechange.sh
index 8c98237..787605c 100755
--- a/t/t4023-diff-rename-typechange.sh
+++ b/t/t4023-diff-rename-typechange.sh
@@ -3,25 +3,26 @@
test_description='typechange rename detection'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-diff.sh
test_expect_success setup '
rm -f foo bar &&
- cat "$TEST_DIRECTORY"/../COPYING >foo &&
+ COPYING_test_data >foo &&
test_ln_s_add linklink bar &&
git add foo &&
git commit -a -m Initial &&
git tag one &&
git rm -f foo bar &&
- cat "$TEST_DIRECTORY"/../COPYING >bar &&
+ COPYING_test_data >bar &&
test_ln_s_add linklink foo &&
git add bar &&
git commit -a -m Second &&
git tag two &&
git rm -f foo bar &&
- cat "$TEST_DIRECTORY"/../COPYING >foo &&
+ COPYING_test_data >foo &&
git add foo &&
git commit -a -m Third &&
git tag three &&
@@ -35,7 +36,7 @@ test_expect_success setup '
# This is purely for sanity check
git rm -f foo bar &&
- cat "$TEST_DIRECTORY"/../COPYING >foo &&
+ COPYING_test_data >foo &&
cat "$TEST_DIRECTORY"/../Makefile >bar &&
git add foo bar &&
git commit -a -m Fifth &&
@@ -43,7 +44,7 @@ test_expect_success setup '
git rm -f foo bar &&
cat "$TEST_DIRECTORY"/../Makefile >foo &&
- cat "$TEST_DIRECTORY"/../COPYING >bar &&
+ COPYING_test_data >bar &&
git add foo bar &&
git commit -a -m Sixth &&
git tag six
@@ -51,10 +52,10 @@ test_expect_success setup '
'
test_expect_success 'cross renames to be detected for regular files' '
-
- git diff-tree five six -r --name-status -B -M | sort >actual &&
+ git diff-tree five six -r --name-status -B -M >out &&
+ sort out >actual &&
{
- echo "R100 foo bar"
+ echo "R100 foo bar" &&
echo "R100 bar foo"
} | sort >expect &&
test_cmp expect actual
@@ -62,10 +63,10 @@ test_expect_success 'cross renames to be detected for regular files' '
'
test_expect_success 'cross renames to be detected for typechange' '
-
- git diff-tree one two -r --name-status -B -M | sort >actual &&
+ git diff-tree one two -r --name-status -B -M >out &&
+ sort out >actual &&
{
- echo "R100 foo bar"
+ echo "R100 foo bar" &&
echo "R100 bar foo"
} | sort >expect &&
test_cmp expect actual
@@ -73,11 +74,11 @@ test_expect_success 'cross renames to be detected for typechange' '
'
test_expect_success 'moves and renames' '
-
- git diff-tree three four -r --name-status -B -M | sort >actual &&
+ git diff-tree three four -r --name-status -B -M >out &&
+ sort out >actual &&
{
# see -B -M (#6) in t4008
- echo "C100 foo bar"
+ echo "C100 foo bar" &&
echo "T100 foo"
} | sort >expect &&
test_cmp expect actual
diff --git a/t/t4024-diff-optimize-common.sh b/t/t4024-diff-optimize-common.sh
index 6b44ce1..e2f0eca 100755
--- a/t/t4024-diff-optimize-common.sh
+++ b/t/t4024-diff-optimize-common.sh
@@ -2,6 +2,7 @@
test_description='common tail optimization'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
z=zzzzzzzz ;# 8
@@ -148,7 +149,7 @@ test_expect_success 'diff -U0' '
for n in $sample
do
- git diff -U0 file-?$n
+ git diff -U0 file-?$n || return 1
done | zc >actual &&
test_cmp expect actual
diff --git a/t/t4025-hunk-header.sh b/t/t4025-hunk-header.sh
index 35578f2..5397cb7 100755
--- a/t/t4025-hunk-header.sh
+++ b/t/t4025-hunk-header.sh
@@ -2,6 +2,7 @@
test_description='diff hunk header truncation'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
N='日本語'
@@ -13,15 +14,9 @@ test_expect_success setup '
(
echo "A $NS" &&
- for c in B C D E F G H I J K
- do
- echo " $c"
- done &&
+ printf " %s\n" B C D E F G H I J K &&
echo "L $NS" &&
- for c in M N O P Q R S T U V
- do
- echo " $c"
- done
+ printf " %s\n" M N O P Q R S T U V
) >file &&
git add file &&
diff --git a/t/t4026-color.sh b/t/t4026-color.sh
index c0b642c..cc3f60d 100755
--- a/t/t4026-color.sh
+++ b/t/t4026-color.sh
@@ -4,6 +4,8 @@
#
test_description='Test diff/status color escape codes'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
ESC=$(printf '\033')
@@ -58,6 +60,10 @@ test_expect_success 'fg bg attr...' '
color "blue bold dim ul blink reverse" "[1;2;4;5;7;34m"
'
+test_expect_success 'reset fg bg attr...' '
+ color "reset blue bold dim ul blink reverse" "[;1;2;4;5;7;34m"
+'
+
# note that nobold and nodim are the same code (22)
test_expect_success 'attr negation' '
color "nobold nodim noul noblink noreverse" "[22;24;25;27m"
@@ -94,6 +100,18 @@ test_expect_success '24-bit colors' '
color "#ff00ff black" "[38;2;255;0;255;40m"
'
+test_expect_success '"default" foreground' '
+ color "default" "[39m"
+'
+
+test_expect_success '"normal default" to clear background' '
+ color "normal default" "[49m"
+'
+
+test_expect_success '"default" can be combined with attributes' '
+ color "default default no-reverse bold" "[1;27;39;49m"
+'
+
test_expect_success '"normal" yields no color at all"' '
color "normal black" "[40m"
'
diff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh
index 94ef77e..40164ae 100755
--- a/t/t4027-diff-submodule.sh
+++ b/t/t4027-diff-submodule.sh
@@ -2,6 +2,7 @@
test_description='difference in submodules'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-diff.sh
@@ -27,10 +28,8 @@ test_expect_success setup '
git commit -m "submodule #2"
) &&
- set x $(
- cd sub &&
- git rev-list HEAD
- ) &&
+ git -C sub rev-list HEAD >revs &&
+ set x $(cat revs) &&
echo ":160000 160000 $3 $ZERO_OID M sub" >expect &&
subtip=$3 subprev=$2
'
diff --git a/t/t4028-format-patch-mime-headers.sh b/t/t4028-format-patch-mime-headers.sh
index 204ba67..60cb819 100755
--- a/t/t4028-format-patch-mime-headers.sh
+++ b/t/t4028-format-patch-mime-headers.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='format-patch mime headers and extra headers do not conflict'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'create commit with utf-8 body' '
diff --git a/t/t4029-diff-trailing-space.sh b/t/t4029-diff-trailing-space.sh
index 32b6e9a..5f8ffef 100755
--- a/t/t4029-diff-trailing-space.sh
+++ b/t/t4029-diff-trailing-space.sh
@@ -4,6 +4,7 @@
#
test_description='diff honors config option, diff.suppressBlankEmpty'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
cat <<\EOF >expected ||
diff --git a/t/t4031-diff-rewrite-binary.sh b/t/t4031-diff-rewrite-binary.sh
index eacc669..c4394a2 100755
--- a/t/t4031-diff-rewrite-binary.sh
+++ b/t/t4031-diff-rewrite-binary.sh
@@ -53,7 +53,7 @@ test_expect_success 'rewrite diff --numstat shows binary changes' '
test_expect_success 'diff --stat counts binary rewrite as 0 lines' '
git diff -B --stat --summary >diff &&
grep "Bin" diff &&
- test_i18ngrep "0 insertions.*0 deletions" diff &&
+ test_grep "0 insertions.*0 deletions" diff &&
grep " rewrite file" diff
'
diff --git a/t/t4032-diff-inter-hunk-context.sh b/t/t4032-diff-inter-hunk-context.sh
index bada0cb..7db92d0 100755
--- a/t/t4032-diff-inter-hunk-context.sh
+++ b/t/t4032-diff-inter-hunk-context.sh
@@ -2,6 +2,7 @@
test_description='diff hunk fusing'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
f() {
diff --git a/t/t4033-diff-patience.sh b/t/t4033-diff-patience.sh
index 113304d..f7be7f5 100755
--- a/t/t4033-diff-patience.sh
+++ b/t/t4033-diff-patience.sh
@@ -2,6 +2,7 @@
test_description='patience diff algorithm'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-diff-alternative.sh
diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh
index 561c582..74586f3 100755
--- a/t/t4034-diff-words.sh
+++ b/t/t4034-diff-words.sh
@@ -2,6 +2,7 @@
test_description='word diff colors'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-diff.sh
@@ -68,6 +69,10 @@ test_language_driver () {
echo "* diff='"$lang"'" >.gitattributes &&
word_diff --color-words
'
+ test_expect_success "diff driver '$lang' in Islandic" '
+ LANG=is_IS.UTF-8 LANGUAGE=is LC_ALL="$is_IS_locale" \
+ word_diff --color-words
+ '
}
test_expect_success setup '
@@ -323,6 +328,7 @@ test_language_driver dts
test_language_driver fortran
test_language_driver html
test_language_driver java
+test_language_driver kotlin
test_language_driver matlab
test_language_driver objc
test_language_driver pascal
diff --git a/t/t4034/cpp/expect b/t/t4034/cpp/expect
index 37d1ea2..dc500ae 100644
--- a/t/t4034/cpp/expect
+++ b/t/t4034/cpp/expect
@@ -1,36 +1,35 @@
<BOLD>diff --git a/pre b/post<RESET>
-<BOLD>index 23d5c8a..7e8c026 100644<RESET>
+<BOLD>index a1a09b7..f1b6f3c 100644<RESET>
<BOLD>--- a/pre<RESET>
<BOLD>+++ b/post<RESET>
-<CYAN>@@ -1,19 +1,19 @@<RESET>
-Foo() : x(0<RED>&&1<RESET><GREEN>&42<RESET>) { <GREEN>bar(x);<RESET> }
+<CYAN>@@ -1,30 +1,30 @@<RESET>
+Foo() : x(0<RED>&&1<RESET><GREEN>&42<RESET>) { <RED>foo0<RESET><GREEN>bar<RESET>(x.<RED>find<RESET><GREEN>Find<RESET>); }
cout<<"Hello World<RED>!<RESET><GREEN>?<RESET>\n"<<endl;
-<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>y<RESET>'
-[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
-!<RED>a<RESET><GREEN>x<RESET> ~<RED>a a<RESET><GREEN>x x<RESET>++ <RED>a<RESET><GREEN>x<RESET>-- <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET><<<RED>b a<RESET><GREEN>y x<RESET>>><RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>&<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>^<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>|<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>&&<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>||<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>?<RED>b<RESET><GREEN>y<RESET>:z
-<RED>a<RESET><GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>,y
-<RED>a<RESET><GREEN>x<RESET>::<RED>b<RESET><GREEN>y<RESET>
+<GREEN>(<RESET>1 <RED>-<RESET><GREEN>+<RESET>1e10 0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>2<RESET>'
+// long double<RESET>
+<RED>3.141592653e-10l<RESET><GREEN>3.141592654e+10l<RESET>
+// float<RESET>
+<RED>120E5f<RESET><GREEN>120E6f<RESET>
+// hex<RESET>
+<RED>0xdead<RESET><GREEN>0xdeaf<RESET>'1<RED>eaF<RESET><GREEN>eaf<RESET>+<RED>8ULL<RESET><GREEN>7ULL<RESET>
+// octal<RESET>
+<RED>01234567<RESET><GREEN>01234560<RESET>
+// binary<RESET>
+<RED>0b1000<RESET><GREEN>0b1100<RESET>+e1
+// expression<RESET>
+1.5-e+<RED>2<RESET><GREEN>3<RESET>+f
+// another one<RESET>
+str.e+<RED>65<RESET><GREEN>75<RESET>
+[a] b<RED>-><RESET><GREEN>->*<RESET>v d<RED>.<RESET><GREEN>.*<RESET>e
+<GREEN>~<RESET>!a <GREEN>!<RESET>~b c<RED>++<RESET><GREEN>+<RESET> d<RED>--<RESET><GREEN>-<RESET> e*<GREEN>*<RESET>f g<RED>&<RESET><GREEN>&&<RESET>h
+a<RED>*<RESET><GREEN>*=<RESET>b c<RED>/<RESET><GREEN>/=<RESET>d e<RED>%<RESET><GREEN>%=<RESET>f
+a<RED>+<RESET><GREEN>++<RESET>b c<RED>-<RESET><GREEN>--<RESET>d
+a<RED><<<RESET><GREEN><<=<RESET>b c<RED>>><RESET><GREEN>>>=<RESET>d
+a<RED><<RESET><GREEN><=<RESET>b c<RED><=<RESET><GREEN><<RESET>d e<RED>><RESET><GREEN>>=<RESET>f g<RED>>=<RESET><GREEN>><RESET>h i<RED><=<RESET><GREEN><=><RESET>j
+a<RED>==<RESET><GREEN>!=<RESET>b c<RED>!=<RESET><GREEN>=<RESET>d
+a<RED>^<RESET><GREEN>^=<RESET>b c<RED>|<RESET><GREEN>|=<RESET>d e<RED>&&<RESET><GREEN>&=<RESET>f
+a<RED>||<RESET><GREEN>|<RESET>b
+a?<GREEN>:<RESET>b
+a<RED>=<RESET><GREEN>==<RESET>b c<RED>+=<RESET><GREEN>+<RESET>d e<RED>-=<RESET><GREEN>-<RESET>f g<RED>*=<RESET><GREEN>*<RESET>h i<RED>/=<RESET><GREEN>/<RESET>j k<RED>%=<RESET><GREEN>%<RESET>l m<RED><<=<RESET><GREEN><<<RESET>n o<RED>>>=<RESET><GREEN>>><RESET>p q<RED>&=<RESET><GREEN>&<RESET>r s<RED>^=<RESET><GREEN>^<RESET>t u<RED>|=<RESET><GREEN>|<RESET>v
+a,b<RESET>
+a<RED>::<RESET><GREEN>:<RESET>b
diff --git a/t/t4034/cpp/post b/t/t4034/cpp/post
index 7e8c026..f1b6f3c 100644
--- a/t/t4034/cpp/post
+++ b/t/t4034/cpp/post
@@ -1,19 +1,30 @@
-Foo() : x(0&42) { bar(x); }
+Foo() : x(0&42) { bar(x.Find); }
cout<<"Hello World?\n"<<endl;
-(1) (-1e10) (0xabcdef) 'y'
-[x] x->y x.y
-!x ~x x++ x-- x*y x&y
-x*y x/y x%y
-x+y x-y
-x<<y x>>y
-x<y x<=y x>y x>=y
-x==y x!=y
-x&y
-x^y
-x|y
-x&&y
-x||y
-x?y:z
-x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
-x,y
-x::y
+(1 +1e10 0xabcdef) '2'
+// long double
+3.141592654e+10l
+// float
+120E6f
+// hex
+0xdeaf'1eaf+7ULL
+// octal
+01234560
+// binary
+0b1100+e1
+// expression
+1.5-e+3+f
+// another one
+str.e+75
+[a] b->*v d.*e
+~!a !~b c+ d- e**f g&&h
+a*=b c/=d e%=f
+a++b c--d
+a<<=b c>>=d
+a<=b c<d e>=f g>h i<=>j
+a!=b c=d
+a^=b c|=d e&=f
+a|b
+a?:b
+a==b c+d e-f g*h i/j k%l m<<n o>>p q&r s^t u|v
+a,b
+a:b
diff --git a/t/t4034/cpp/pre b/t/t4034/cpp/pre
index 23d5c8a..a1a09b7 100644
--- a/t/t4034/cpp/pre
+++ b/t/t4034/cpp/pre
@@ -1,19 +1,30 @@
-Foo():x(0&&1){}
+Foo():x(0&&1){ foo0( x.find); }
cout<<"Hello World!\n"<<endl;
1 -1e10 0xabcdef 'x'
-[a] a->b a.b
-!a ~a a++ a-- a*b a&b
-a*b a/b a%b
-a+b a-b
-a<<b a>>b
-a<b a<=b a>b a>=b
-a==b a!=b
-a&b
-a^b
-a|b
-a&&b
+// long double
+3.141592653e-10l
+// float
+120E5f
+// hex
+0xdead'1eaF+8ULL
+// octal
+01234567
+// binary
+0b1000+e1
+// expression
+1.5-e+2+f
+// another one
+str.e+65
+[a] b->v d.e
+!a ~b c++ d-- e*f g&h
+a*b c/d e%f
+a+b c-d
+a<<b c>>d
+a<b c<=d e>f g>=h i<=j
+a==b c!=d
+a^b c|d e&&f
a||b
-a?b:z
-a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
-a,y
+a?b
+a=b c+=d e-=f g*=h i/=j k%=l m<<=n o>>=p q&=r s^=t u|=v
+a,b
a::b
diff --git a/t/t4034/kotlin/expect b/t/t4034/kotlin/expect
new file mode 100644
index 0000000..7f76f75
--- /dev/null
+++ b/t/t4034/kotlin/expect
@@ -0,0 +1,43 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 11ea3de..2e1df4c 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,30 +1,30 @@<RESET>
+println("Hello World<RED>!\n<RESET><GREEN>?<RESET>")
+<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>y<RESET>'
+[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
+!<RED>a a<RESET><GREEN>x x<RESET>.inv() <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET><GREEN>y<RESET>
+a <RED>shr<RESET><GREEN>shl<RESET> b
+<RED>a<RESET><GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b a<RESET><GREEN>y x<RESET>===<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET> and <RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>^<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET> or <RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&&<RED>b a<RESET><GREEN>y x<RESET>||<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET><GREEN>y<RESET>
+a<RED>=<RESET><GREEN>+=<RESET>b c<RED>+=<RESET><GREEN>=<RESET>d e<RED>-=<RESET><GREEN><=<RESET>f g<RED>*=<RESET><GREEN>>=<RESET>h i<RED>/=<RESET><GREEN>/<RESET>j k<RED>%=<RESET><GREEN>%<RESET>l m<RED><<=<RESET><GREEN><<<RESET>n o<RED>>>=<RESET><GREEN>>><RESET>p q<RED>&=<RESET><GREEN>&<RESET>r s<RED>^=<RESET><GREEN>^<RESET>t u<RED>|=<RESET><GREEN>|<RESET>v
+a<RED><<=<RESET><GREEN><=<RESET>b
+a<RED>||<RESET><GREEN>|<RESET>b a<RED>&&<RESET><GREEN>&<RESET>b
+<RED>a<RESET><GREEN>x<RESET>,y
+--a<RED>==<RESET><GREEN>!=<RESET>--b
+a++<RED>==<RESET><GREEN>!=<RESET>++b
+<RED>0xFF_EC_DE_5E 0b100_000 100_000<RESET><GREEN>0xFF_E1_DE_5E 0b100_100 200_000<RESET>
+a<RED>==<RESET><GREEN>===<RESET>b
+a<RED>!!<RESET><GREEN>!=<RESET>b
+<RED>_32<RESET><GREEN>_33<RESET>.find(arr)
+X.<RED>fill<RESET><GREEN>find<RESET>()
+X.<RED>u<RESET><GREEN>f<RESET>+1
+X.u<RED>-<RESET><GREEN>+<RESET>2
+a<RED>.<RESET><GREEN>..<RESET>b
+a<RED>?.<RESET><GREEN>?:<RESET>b
+<RED>.32_00_456<RESET><GREEN>.32_00_446<RESET>
diff --git a/t/t4034/kotlin/post b/t/t4034/kotlin/post
new file mode 100644
index 0000000..2e1df4c
--- /dev/null
+++ b/t/t4034/kotlin/post
@@ -0,0 +1,30 @@
+println("Hello World?")
+(1) (-1e10) (0xabcdef) 'y'
+[x] x->y x.y
+!x x.inv() x*y x&y
+x*y x/y x%y
+x+y x-y
+a shl b
+x<y x<=y x>y x>=y
+x==y x!=y x===y
+x and y
+x^y
+x or y
+x&&y x||y
+x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
+a+=b c=d e<=f g>=h i/j k%l m<<n o>>p q&r s^t u|v
+a<=b
+a|b a&b
+x,y
+--a!=--b
+a++!=++b
+0xFF_E1_DE_5E 0b100_100 200_000
+a===b
+a!=b
+_33.find(arr)
+X.find()
+X.f+1
+X.u+2
+a..b
+a?:b
+.32_00_446
diff --git a/t/t4034/kotlin/pre b/t/t4034/kotlin/pre
new file mode 100644
index 0000000..11ea3de
--- /dev/null
+++ b/t/t4034/kotlin/pre
@@ -0,0 +1,30 @@
+println("Hello World!\n")
+1 -1e10 0xabcdef 'x'
+[a] a->b a.b
+!a a.inv() a*b a&b
+a*b a/b a%b
+a+b a-b
+a shr b
+a<b a<=b a>b a>=b
+a==b a!=b a===b
+a and b
+a^b
+a or b
+a&&b a||b
+a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
+a=b c+=d e-=f g*=h i/=j k%=l m<<=n o>>=p q&=r s^=t u|=v
+a<<=b
+a||b a&&b
+a,y
+--a==--b
+a++==++b
+0xFF_EC_DE_5E 0b100_000 100_000
+a==b
+a!!b
+_32.find(arr)
+X.fill()
+X.u+1
+X.u-2
+a.b
+a?.b
+.32_00_456
diff --git a/t/t4035-diff-quiet.sh b/t/t4035-diff-quiet.sh
index 0352bf8..76f8034 100755
--- a/t/t4035-diff-quiet.sh
+++ b/t/t4035-diff-quiet.sh
@@ -2,6 +2,7 @@
test_description='Return value of diffs'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t4036-format-patch-signer-mime.sh b/t/t4036-format-patch-signer-mime.sh
index 98d9713..48655bc 100755
--- a/t/t4036-format-patch-signer-mime.sh
+++ b/t/t4036-format-patch-signer-mime.sh
@@ -2,6 +2,7 @@
test_description='format-patch -s should force MIME encoding as needed'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t4037-diff-r-t-dirs.sh b/t/t4037-diff-r-t-dirs.sh
index f5ce3b2..b5f96fe 100755
--- a/t/t4037-diff-r-t-dirs.sh
+++ b/t/t4037-diff-r-t-dirs.sh
@@ -2,6 +2,7 @@
test_description='diff -r -t shows directory additions and deletions'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t4038-diff-combined.sh b/t/t4038-diff-combined.sh
index aeac203..2ce26e5 100755
--- a/t/t4038-diff-combined.sh
+++ b/t/t4038-diff-combined.sh
@@ -80,11 +80,21 @@ test_expect_success 'check combined output (1)' '
verify_helper sidewithone
'
+test_expect_success 'check combined output (1) with git diff <rev>^!' '
+ git diff sidewithone^! -- >sidewithone &&
+ verify_helper sidewithone
+'
+
test_expect_success 'check combined output (2)' '
git show sidesansone -- >sidesansone &&
verify_helper sidesansone
'
+test_expect_success 'check combined output (2) with git diff <rev>^!' '
+ git diff sidesansone^! -- >sidesansone &&
+ verify_helper sidesansone
+'
+
test_expect_success 'diagnose truncated file' '
>file &&
git add file &&
@@ -100,7 +110,7 @@ test_expect_success 'setup for --cc --raw' '
for i in $(test_seq 1 40)
do
blob=$(echo file$i | git hash-object --stdin -w) &&
- trees="$trees$(echo "100644 blob $blob file" | git mktree)$LF"
+ trees="$trees$(echo "100644 blob $blob file" | git mktree)$LF" || return 1
done
'
diff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh
index 0eb0314..78090e6 100755
--- a/t/t4039-diff-assume-unchanged.sh
+++ b/t/t4039-diff-assume-unchanged.sh
@@ -2,6 +2,7 @@
test_description='diff with assume-unchanged entries'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# external diff has been tested in t4020-diff-external.sh
diff --git a/t/t4040-whitespace-status.sh b/t/t4040-whitespace-status.sh
index 3c728a3..eec3d73 100755
--- a/t/t4040-whitespace-status.sh
+++ b/t/t4040-whitespace-status.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='diff --exit-code with whitespace'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -26,8 +28,7 @@ test_expect_success 'diff-tree --exit-code' '
test_expect_success 'diff-tree -b --exit-code' '
git diff -b --exit-code HEAD^ HEAD &&
- git diff-tree -b -p --exit-code HEAD^ HEAD &&
- git diff-tree -b --exit-code HEAD^ HEAD
+ git diff-tree -b -p --exit-code HEAD^ HEAD
'
test_expect_success 'diff-index --cached --exit-code' '
diff --git a/t/t4042-diff-textconv-caching.sh b/t/t4042-diff-textconv-caching.sh
index bf33aed..8ebfa3c 100755
--- a/t/t4042-diff-textconv-caching.sh
+++ b/t/t4042-diff-textconv-caching.sh
@@ -118,4 +118,26 @@ test_expect_success 'log notes cache and still use cache for -p' '
git log --no-walk -p refs/notes/textconv/magic HEAD
'
+test_expect_success 'caching is silently ignored outside repo' '
+ mkdir -p non-repo &&
+ echo one >non-repo/one &&
+ echo two >non-repo/two &&
+ echo "* diff=test" >attr &&
+ test_expect_code 1 \
+ nongit git -c core.attributesFile="$PWD/attr" \
+ -c diff.test.textconv="tr a-z A-Z <" \
+ -c diff.test.cachetextconv=true \
+ diff --no-index one two >actual &&
+ cat >expect <<-\EOF &&
+ diff --git a/one b/two
+ index 5626abf..f719efd 100644
+ --- a/one
+ +++ b/two
+ @@ -1 +1 @@
+ -ONE
+ +TWO
+ EOF
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t4044-diff-index-unique-abbrev.sh b/t/t4044-diff-index-unique-abbrev.sh
index 4701796..9f6043d 100755
--- a/t/t4044-diff-index-unique-abbrev.sh
+++ b/t/t4044-diff-index-unique-abbrev.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test unique sha1 abbreviation on "index from..to" line'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -32,12 +34,12 @@ test_expect_success 'setup' '
100644 blob $(test_oid hash2) foo
EOF
- echo "$(test_oid val1)" > foo &&
+ test_oid val1 > foo &&
git add foo &&
git commit -m "initial" &&
git cat-file -p HEAD: > actual &&
test_cmp expect_initial actual &&
- echo "$(test_oid val2)" > foo &&
+ test_oid val2 > foo &&
git commit -a -m "update" &&
git cat-file -p HEAD: > actual &&
test_cmp expect_update actual
diff --git a/t/t4045-diff-relative.sh b/t/t4045-diff-relative.sh
index fab351b..9b46c4c 100755
--- a/t/t4045-diff-relative.sh
+++ b/t/t4045-diff-relative.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='diff --relative tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -162,6 +164,35 @@ check_diff_relative_option subdir file2 true --no-relative --relative
check_diff_relative_option . file2 false --no-relative --relative=subdir
check_diff_relative_option . file2 true --no-relative --relative=subdir
+test_expect_success 'external diff with --relative' '
+ test_when_finished "git reset --hard" &&
+ echo changed >file1 &&
+ echo changed >subdir/file2 &&
+
+ write_script mydiff <<-\EOF &&
+ # hacky pretend diff; the goal here is just to make sure we got
+ # passed sensible input that we _could_ diff, without relying on
+ # the specific output of a system diff tool.
+ echo "diff a/$1 b/$1" &&
+ echo "--- a/$1" &&
+ echo "+++ b/$1" &&
+ echo "@@ -1 +0,0 @@" &&
+ sed "s/^/-/" "$2" &&
+ sed "s/^/+/" "$5"
+ EOF
+
+ cat >expect <<-\EOF &&
+ diff a/file2 b/file2
+ --- a/file2
+ +++ b/file2
+ @@ -1 +0,0 @@
+ -other content
+ +changed
+ EOF
+ GIT_EXTERNAL_DIFF=./mydiff git diff --relative=subdir >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'setup diff --relative unmerged' '
test_commit zero file0 &&
test_commit base subdir/file0 &&
diff --git a/t/t4046-diff-unmerged.sh b/t/t4046-diff-unmerged.sh
index ff7cfd8..ffaf693 100755
--- a/t/t4046-diff-unmerged.sh
+++ b/t/t4046-diff-unmerged.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='diff with unmerged index entries'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -18,7 +20,7 @@ test_expect_success setup '
for t in o x
do
path="$b$o$t" &&
- case "$path" in ooo) continue ;; esac
+ case "$path" in ooo) continue ;; esac &&
paths="$paths$path " &&
p=" $path" &&
case "$b" in x) echo "$m1$p" ;; esac &&
@@ -37,7 +39,7 @@ test_expect_success 'diff-files -0' '
for path in $paths
do
>"$path" &&
- echo ":000000 100644 $ZERO_OID $ZERO_OID U $path"
+ echo ":000000 100644 $ZERO_OID $ZERO_OID U $path" || return 1
done >diff-files-0.expect &&
git diff-files -0 >diff-files-0.actual &&
test_cmp diff-files-0.expect diff-files-0.actual
@@ -50,7 +52,7 @@ test_expect_success 'diff-files -1' '
echo ":000000 100644 $ZERO_OID $ZERO_OID U $path" &&
case "$path" in
x??) echo ":100644 100644 $blob1 $ZERO_OID M $path"
- esac
+ esac || return 1
done >diff-files-1.expect &&
git diff-files -1 >diff-files-1.actual &&
test_cmp diff-files-1.expect diff-files-1.actual
@@ -63,7 +65,7 @@ test_expect_success 'diff-files -2' '
echo ":000000 100644 $ZERO_OID $ZERO_OID U $path" &&
case "$path" in
?x?) echo ":100644 100644 $blob2 $ZERO_OID M $path"
- esac
+ esac || return 1
done >diff-files-2.expect &&
git diff-files -2 >diff-files-2.actual &&
test_cmp diff-files-2.expect diff-files-2.actual &&
@@ -78,10 +80,20 @@ test_expect_success 'diff-files -3' '
echo ":000000 100644 $ZERO_OID $ZERO_OID U $path" &&
case "$path" in
??x) echo ":100644 100644 $blob3 $ZERO_OID M $path"
- esac
+ esac || return 1
done >diff-files-3.expect &&
git diff-files -3 >diff-files-3.actual &&
test_cmp diff-files-3.expect diff-files-3.actual
'
+test_expect_success 'diff --stat' '
+ for path in $paths
+ do
+ echo " $path | Unmerged" || return 1
+ done >diff-stat.expect &&
+ echo " 0 files changed" >>diff-stat.expect &&
+ git diff --cached --stat >diff-stat.actual &&
+ test_cmp diff-stat.expect diff-stat.actual
+'
+
test_done
diff --git a/t/t4047-diff-dirstat.sh b/t/t4047-diff-dirstat.sh
index 7fec2cb..7b73462 100755
--- a/t/t4047-diff-dirstat.sh
+++ b/t/t4047-diff-dirstat.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='diff --dirstat tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# set up two commits where the second commit has these files
@@ -941,37 +943,37 @@ test_expect_success '--dirstat=future_param,lines,0 should fail loudly' '
test_must_fail git diff --dirstat=future_param,lines,0 HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&
test_debug "cat actual_error" &&
test_must_be_empty actual_diff_dirstat &&
- test_i18ngrep -q "future_param" actual_error &&
- test_i18ngrep -q "\--dirstat" actual_error
+ test_grep -q "future_param" actual_error &&
+ test_grep -q "\--dirstat" actual_error
'
test_expect_success '--dirstat=dummy1,cumulative,2dummy should report both unrecognized parameters' '
test_must_fail git diff --dirstat=dummy1,cumulative,2dummy HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&
test_debug "cat actual_error" &&
test_must_be_empty actual_diff_dirstat &&
- test_i18ngrep -q "dummy1" actual_error &&
- test_i18ngrep -q "2dummy" actual_error &&
- test_i18ngrep -q "\--dirstat" actual_error
+ test_grep -q "dummy1" actual_error &&
+ test_grep -q "2dummy" actual_error &&
+ test_grep -q "\--dirstat" actual_error
'
test_expect_success 'diff.dirstat=future_param,0,lines should warn, but still work' '
git -c diff.dirstat=future_param,0,lines diff --dirstat HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&
test_debug "cat actual_error" &&
test_cmp expect_diff_dirstat actual_diff_dirstat &&
- test_i18ngrep -q "future_param" actual_error &&
- test_i18ngrep -q "diff\\.dirstat" actual_error &&
+ test_grep -q "future_param" actual_error &&
+ test_grep -q "diff\\.dirstat" actual_error &&
git -c diff.dirstat=future_param,0,lines diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M 2>actual_error &&
test_debug "cat actual_error" &&
test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
- test_i18ngrep -q "future_param" actual_error &&
- test_i18ngrep -q "diff\\.dirstat" actual_error &&
+ test_grep -q "future_param" actual_error &&
+ test_grep -q "diff\\.dirstat" actual_error &&
git -c diff.dirstat=future_param,0,lines diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC 2>actual_error &&
test_debug "cat actual_error" &&
test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC &&
- test_i18ngrep -q "future_param" actual_error &&
- test_i18ngrep -q "diff\\.dirstat" actual_error
+ test_grep -q "future_param" actual_error &&
+ test_grep -q "diff\\.dirstat" actual_error
'
test_expect_success '--shortstat --dirstat should output only one dirstat' '
diff --git a/t/t4049-diff-stat-count.sh b/t/t4049-diff-stat-count.sh
index 53061b1..0a4fc73 100755
--- a/t/t4049-diff-stat-count.sh
+++ b/t/t4049-diff-stat-count.sh
@@ -2,6 +2,8 @@
# Copyright (c) 2011, Google Inc.
test_description='diff --stat-count'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -51,7 +53,7 @@ test_expect_success 'exclude unmerged entries from total file count' '
git rm -f d &&
for stage in 1 2 3
do
- sed -e "s/ 0 a/ $stage d/" x
+ sed -e "s/ 0 a/ $stage d/" x || return 1
done |
git update-index --index-info &&
echo d >d &&
diff --git a/t/t4050-diff-histogram.sh b/t/t4050-diff-histogram.sh
index fd3e86a..c61b30f 100755
--- a/t/t4050-diff-histogram.sh
+++ b/t/t4050-diff-histogram.sh
@@ -2,6 +2,7 @@
test_description='histogram diff algorithm'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-diff-alternative.sh
diff --git a/t/t4051-diff-function-context.sh b/t/t4051-diff-function-context.sh
index 4838a1d..725278a 100755
--- a/t/t4051-diff-function-context.sh
+++ b/t/t4051-diff-function-context.sh
@@ -2,6 +2,7 @@
test_description='diff function context'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
dir="$TEST_DIRECTORY/t4051"
diff --git a/t/t4052-stat-output.sh b/t/t4052-stat-output.sh
index 9eba436..7badd72 100755
--- a/t/t4052-stat-output.sh
+++ b/t/t4052-stat-output.sh
@@ -8,10 +8,11 @@ test_description='test --stat output of various commands'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
-# 120 character name
+# 120-character name
name=aaaaaaaaaa
name=$name$name$name$name$name$name$name$name$name$name$name$name
test_expect_success 'preparation' '
@@ -48,12 +49,41 @@ log -1 --stat
EOF
cat >expect.60 <<-'EOF'
- ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
+ ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
EOF
cat >expect.6030 <<-'EOF'
...aaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
EOF
-cat >expect2.60 <<-'EOF'
+while read verb expect cmd args
+do
+ # No width limit applied when statNameWidth is ignored
+ case "$expect" in expect72|expect.6030)
+ test_expect_success "$cmd $verb diff.statNameWidth with long name" '
+ git -c diff.statNameWidth=30 $cmd $args >output &&
+ grep " | " output >actual &&
+ test_cmp $expect actual
+ ';;
+ esac
+ # Maximum width limit still applied when statNameWidth is ignored
+ case "$expect" in expect.60|expect.6030)
+ test_expect_success "$cmd --stat=width $verb diff.statNameWidth with long name" '
+ git -c diff.statNameWidth=30 $cmd $args --stat=60 >output &&
+ grep " | " output >actual &&
+ test_cmp $expect actual
+ ';;
+ esac
+done <<\EOF
+ignores expect72 format-patch -1 --stdout
+ignores expect.60 format-patch -1 --stdout
+respects expect.6030 diff HEAD^ HEAD --stat
+respects expect.6030 show --stat
+respects expect.6030 log -1 --stat
+EOF
+
+cat >expect.40 <<-'EOF'
+ ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
+EOF
+cat >expect2.40 <<-'EOF'
...aaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
...aaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
EOF
@@ -66,22 +96,22 @@ do
test_expect_success "$cmd --stat=width: a long name is given more room when the bar is short" '
git $cmd $args --stat=40 >output &&
grep " | " output >actual &&
- test_cmp $expect.60 actual
+ test_cmp $expect.40 actual
'
test_expect_success "$cmd --stat-width=width with long name" '
git $cmd $args --stat-width=40 >output &&
grep " | " output >actual &&
- test_cmp $expect.60 actual
+ test_cmp $expect.40 actual
'
- test_expect_success "$cmd --stat=...,name-width with long name" '
+ test_expect_success "$cmd --stat=width,name-width with long name" '
git $cmd $args --stat=60,30 >output &&
grep " | " output >actual &&
test_cmp $expect.6030 actual
'
- test_expect_success "$cmd --stat-name-width with long name" '
+ test_expect_success "$cmd --stat-name-width=width with long name" '
git $cmd $args --stat-name-width=30 >output &&
grep " | " output >actual &&
test_cmp $expect.6030 actual
@@ -93,15 +123,14 @@ expect show --stat
expect log -1 --stat
EOF
-
-test_expect_success 'preparation for big change tests' '
+test_expect_success 'preparation for big-change tests' '
>abcd &&
git add abcd &&
git commit -m message &&
i=0 &&
while test $i -lt 1000
do
- echo $i && i=$(($i + 1))
+ echo $i && i=$(($i + 1)) || return 1
done >abcd &&
git commit -m message abcd
'
@@ -110,7 +139,7 @@ cat >expect72 <<'EOF'
abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
EOF
-test_expect_success "format-patch --cover-letter ignores COLUMNS (big change)" '
+test_expect_success "format-patch --cover-letter ignores COLUMNS with big change" '
COLUMNS=200 git format-patch -1 --stdout --cover-letter >output &&
grep " | " output >actual &&
test_cmp expect72 actual
@@ -130,7 +159,7 @@ cat >expect200-graph <<'EOF'
EOF
while read verb expect cmd args
do
- test_expect_success "$cmd $verb COLUMNS (big change)" '
+ test_expect_success "$cmd $verb COLUMNS with big change" '
COLUMNS=200 git $cmd $args >output &&
grep " | " output >actual &&
test_cmp "$expect" actual
@@ -138,7 +167,7 @@ do
case "$cmd" in diff|show) continue;; esac
- test_expect_success "$cmd --graph $verb COLUMNS (big change)" '
+ test_expect_success "$cmd --graph $verb COLUMNS with big change" '
COLUMNS=200 git $cmd $args --graph >output &&
grep " | " output >actual &&
test_cmp "$expect-graph" actual
@@ -158,7 +187,7 @@ cat >expect40-graph <<'EOF'
EOF
while read verb expect cmd args
do
- test_expect_success "$cmd $verb not enough COLUMNS (big change)" '
+ test_expect_success "$cmd $verb not enough COLUMNS with big change" '
COLUMNS=40 git $cmd $args >output &&
grep " | " output >actual &&
test_cmp "$expect" actual
@@ -166,7 +195,7 @@ do
case "$cmd" in diff|show) continue;; esac
- test_expect_success "$cmd --graph $verb not enough COLUMNS (big change)" '
+ test_expect_success "$cmd --graph $verb not enough COLUMNS with big change" '
COLUMNS=40 git $cmd $args --graph >output &&
grep " | " output >actual &&
test_cmp "$expect-graph" actual
@@ -186,7 +215,7 @@ cat >expect40-graph <<'EOF'
EOF
while read verb expect cmd args
do
- test_expect_success "$cmd $verb statGraphWidth config" '
+ test_expect_success "$cmd $verb diff.statGraphWidth" '
git -c diff.statGraphWidth=26 $cmd $args >output &&
grep " | " output >actual &&
test_cmp "$expect" actual
@@ -194,7 +223,7 @@ do
case "$cmd" in diff|show) continue;; esac
- test_expect_success "$cmd --graph $verb statGraphWidth config" '
+ test_expect_success "$cmd --graph $verb diff.statGraphWidth" '
git -c diff.statGraphWidth=26 $cmd $args --graph >output &&
grep " | " output >actual &&
test_cmp "$expect-graph" actual
@@ -206,7 +235,6 @@ respects expect40 show --stat
respects expect40 log -1 --stat
EOF
-
cat >expect <<'EOF'
abcd | 1000 ++++++++++++++++++++++++++
EOF
@@ -227,7 +255,7 @@ do
test_cmp expect actual
'
- test_expect_success "$cmd --stat-graph-width with big change" '
+ test_expect_success "$cmd --stat-graph-width=width with big change" '
git $cmd $args --stat-graph-width=26 >output &&
grep " | " output >actual &&
test_cmp expect actual
@@ -241,7 +269,7 @@ do
test_cmp expect-graph actual
'
- test_expect_success "$cmd --stat-graph-width --graph with big change" '
+ test_expect_success "$cmd --stat-graph-width=width --graph with big change" '
git $cmd $args --stat-graph-width=26 --graph >output &&
grep " | " output >actual &&
test_cmp expect-graph actual
@@ -253,7 +281,7 @@ show --stat
log -1 --stat
EOF
-test_expect_success 'preparation for long filename tests' '
+test_expect_success 'preparation for long-name tests' '
cp abcd aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&
git add aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&
git commit -m message
@@ -301,7 +329,7 @@ cat >expect200-graph <<'EOF'
EOF
while read verb expect cmd args
do
- test_expect_success "$cmd $verb COLUMNS (long filename)" '
+ test_expect_success "$cmd $verb COLUMNS with long name" '
COLUMNS=200 git $cmd $args >output &&
grep " | " output >actual &&
test_cmp "$expect" actual
@@ -309,7 +337,7 @@ do
case "$cmd" in diff|show) continue;; esac
- test_expect_success "$cmd --graph $verb COLUMNS (long filename)" '
+ test_expect_success "$cmd --graph $verb COLUMNS with long name" '
COLUMNS=200 git $cmd $args --graph >output &&
grep " | " output >actual &&
test_cmp "$expect-graph" actual
@@ -330,7 +358,7 @@ EOF
while read verb expect cmd args
do
test_expect_success COLUMNS_CAN_BE_1 \
- "$cmd $verb prefix greater than COLUMNS (big change)" '
+ "$cmd $verb prefix greater than COLUMNS with big change" '
COLUMNS=1 git $cmd $args >output &&
grep " | " output >actual &&
test_cmp "$expect" actual
@@ -339,7 +367,7 @@ do
case "$cmd" in diff|show) continue;; esac
test_expect_success COLUMNS_CAN_BE_1 \
- "$cmd --graph $verb prefix greater than COLUMNS (big change)" '
+ "$cmd --graph $verb prefix greater than COLUMNS with big change" '
COLUMNS=1 git $cmd $args --graph >output &&
grep " | " output >actual &&
test_cmp "$expect-graph" actual
@@ -354,8 +382,14 @@ EOF
cat >expect <<'EOF'
abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
EOF
-test_expect_success 'merge --stat respects COLUMNS (big change)' '
- git checkout -b branch HEAD^^ &&
+test_expect_success 'merge --stat respects diff.statGraphWidth with big change' '
+ git checkout -b branch1 HEAD^^ &&
+ git -c diff.statGraphWidth=26 merge --stat --no-ff main^ >output &&
+ grep " | " output >actual &&
+ test_cmp expect40 actual
+'
+test_expect_success 'merge --stat respects COLUMNS with big change' '
+ git checkout -b branch2 HEAD^^ &&
COLUMNS=100 git merge --stat --no-ff main^ >output &&
grep " | " output >actual &&
test_cmp expect actual
@@ -364,7 +398,17 @@ test_expect_success 'merge --stat respects COLUMNS (big change)' '
cat >expect <<'EOF'
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 +++++++++++++++++++++++++++++++++++++++
EOF
-test_expect_success 'merge --stat respects COLUMNS (long filename)' '
+cat >expect.30 <<'EOF'
+ ...aaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++++++++++++++++++++++++++++++
+EOF
+test_expect_success 'merge --stat respects diff.statNameWidth with long name' '
+ git switch branch1 &&
+ git -c diff.statNameWidth=30 merge --stat --no-ff main >output &&
+ grep " | " output >actual &&
+ test_cmp expect.30 actual
+'
+test_expect_success 'merge --stat respects COLUMNS with long name' '
+ git switch branch2 &&
COLUMNS=100 git merge --stat --no-ff main >output &&
grep " | " output >actual &&
test_cmp expect actual
diff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh
index 3feadf0..651ec77 100755
--- a/t/t4053-diff-no-index.sh
+++ b/t/t4053-diff-no-index.sh
@@ -2,6 +2,7 @@
test_description='diff --no-index'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -55,7 +56,7 @@ test_expect_success 'git diff --no-index executed outside repo gives correct err
export GIT_CEILING_DIRECTORIES &&
cd non/git &&
test_must_fail git diff --no-index a 2>actual.err &&
- test_i18ngrep "usage: git diff --no-index" actual.err
+ test_grep "usage: git diff --no-index" actual.err
)
'
@@ -204,4 +205,95 @@ test_expect_success POSIXPERM,SYMLINKS 'diff --no-index normalizes: mode not lik
test_cmp expected actual
'
+test_expect_success POSIXPERM 'external diff with mode-only change' '
+ echo content >not-executable &&
+ echo content >executable &&
+ chmod +x executable &&
+ echo executable executable $(test_oid zero) 100755 \
+ not-executable $(test_oid zero) 100644 not-executable \
+ >expect &&
+ test_expect_code 1 git -c diff.external=echo diff \
+ --no-index executable not-executable >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success "diff --no-index treats '-' as stdin" '
+ cat >expect <<-EOF &&
+ diff --git a/- b/a/1
+ index $ZERO_OID..$(git hash-object --stdin <a/1) 100644
+ --- a/-
+ +++ b/a/1
+ @@ -1 +1 @@
+ -x
+ +1
+ EOF
+
+ test_write_lines x | test_expect_code 1 \
+ git -c core.abbrev=no diff --no-index -- - a/1 >actual &&
+ test_cmp expect actual &&
+
+ test_write_lines 1 | git diff --no-index -- a/1 - >actual &&
+ test_must_be_empty actual
+'
+
+test_expect_success "diff --no-index -R treats '-' as stdin" '
+ cat >expect <<-EOF &&
+ diff --git b/a/1 a/-
+ index $(git hash-object --stdin <a/1)..$ZERO_OID 100644
+ --- b/a/1
+ +++ a/-
+ @@ -1 +1 @@
+ -1
+ +x
+ EOF
+
+ test_write_lines x | test_expect_code 1 \
+ git -c core.abbrev=no diff --no-index -R -- - a/1 >actual &&
+ test_cmp expect actual &&
+
+ test_write_lines 1 | git diff --no-index -R -- a/1 - >actual &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'diff --no-index refuses to diff stdin and a directory' '
+ test_must_fail git diff --no-index -- - a </dev/null 2>err &&
+ grep "fatal: cannot compare stdin to a directory" err
+'
+
+test_expect_success PIPE 'diff --no-index refuses to diff a named pipe and a directory' '
+ test_when_finished "rm -f pipe" &&
+ mkfifo pipe &&
+ test_must_fail git diff --no-index -- pipe a 2>err &&
+ grep "fatal: cannot compare a named pipe to a directory" err
+'
+
+test_expect_success PIPE,SYMLINKS 'diff --no-index reads from pipes' '
+ test_when_finished "rm -f old new new-link" &&
+ mkfifo old &&
+ mkfifo new &&
+ ln -s new new-link &&
+ {
+ (test_write_lines a b c >old) &
+ } &&
+ test_when_finished "kill $! || :" &&
+ {
+ (test_write_lines a x c >new) &
+ } &&
+ test_when_finished "kill $! || :" &&
+
+ cat >expect <<-EOF &&
+ diff --git a/old b/new-link
+ --- a/old
+ +++ b/new-link
+ @@ -1,3 +1,3 @@
+ a
+ -b
+ +x
+ c
+ EOF
+
+ test_expect_code 1 git diff --no-index old new-link >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t4054-diff-bogus-tree.sh b/t/t4054-diff-bogus-tree.sh
index 8c95f15..05c88f8 100755
--- a/t/t4054-diff-bogus-tree.sh
+++ b/t/t4054-diff-bogus-tree.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test diff with a bogus tree containing the null sha1'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'create bogus tree' '
@@ -8,7 +10,7 @@ test_expect_success 'create bogus tree' '
bogus_tree=$(
printf "100644 fooQ$name" |
q_to_nul |
- git hash-object -w --stdin -t tree
+ git hash-object --literally -w --stdin -t tree
)
'
diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh
index 741e080..3ea9ae9 100755
--- a/t/t4055-diff-context.sh
+++ b/t/t4055-diff-context.sh
@@ -5,6 +5,7 @@
test_description='diff.context configuration'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -73,13 +74,13 @@ test_expect_success 'plumbing not affected' '
test_expect_success 'non-integer config parsing' '
git config diff.context no &&
test_must_fail git diff 2>output &&
- test_i18ngrep "bad numeric config value" output
+ test_grep "bad numeric config value" output
'
test_expect_success 'negative integer config parsing' '
git config diff.context -1 &&
test_must_fail git diff 2>output &&
- test_i18ngrep "bad config variable" output
+ test_grep "bad config variable" output
'
test_expect_success '-U0 is valid, so is diff.context=0' '
diff --git a/t/t4057-diff-combined-paths.sh b/t/t4057-diff-combined-paths.sh
index 7e5b74f..9a7505c 100755
--- a/t/t4057-diff-combined-paths.sh
+++ b/t/t4057-diff-combined-paths.sh
@@ -5,6 +5,7 @@ test_description='combined diff show only paths that are different to all parent
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# verify that diffc.expect matches output of
@@ -18,13 +19,13 @@ test_expect_success 'trivial merge - combine-diff empty' '
for i in $(test_seq 1 9)
do
echo $i >$i.txt &&
- git add $i.txt
+ git add $i.txt || return 1
done &&
git commit -m "init" &&
git checkout -b side &&
for i in $(test_seq 2 9)
do
- echo $i/2 >>$i.txt
+ echo $i/2 >>$i.txt || return 1
done &&
git commit -a -m "side 2-9" &&
git checkout main &&
@@ -40,14 +41,14 @@ test_expect_success 'only one truly conflicting path' '
git checkout side &&
for i in $(test_seq 2 9)
do
- echo $i/3 >>$i.txt
+ echo $i/3 >>$i.txt || return 1
done &&
echo "4side" >>4.txt &&
git commit -a -m "side 2-9 +4" &&
git checkout main &&
for i in $(test_seq 1 9)
do
- echo $i/3 >>$i.txt
+ echo $i/3 >>$i.txt || return 1
done &&
echo "4main" >>4.txt &&
git commit -a -m "main 1-9 +4" &&
@@ -69,13 +70,13 @@ test_expect_success 'merge introduces new file' '
git checkout side &&
for i in $(test_seq 5 9)
do
- echo $i/4 >>$i.txt
+ echo $i/4 >>$i.txt || return 1
done &&
git commit -a -m "side 5-9" &&
git checkout main &&
for i in $(test_seq 1 3)
do
- echo $i/4 >>$i.txt
+ echo $i/4 >>$i.txt || return 1
done &&
git commit -a -m "main 1-3 +4hello" &&
git merge side &&
@@ -90,13 +91,13 @@ test_expect_success 'merge removed a file' '
git checkout side &&
for i in $(test_seq 5 9)
do
- echo $i/5 >>$i.txt
+ echo $i/5 >>$i.txt || return 1
done &&
git commit -a -m "side 5-9" &&
git checkout main &&
for i in $(test_seq 1 3)
do
- echo $i/4 >>$i.txt
+ echo $i/4 >>$i.txt || return 1
done &&
git commit -a -m "main 1-3" &&
git merge side &&
diff --git a/t/t4058-diff-duplicates.sh b/t/t4058-diff-duplicates.sh
index 54614b8..2501c89 100755
--- a/t/t4058-diff-duplicates.sh
+++ b/t/t4058-diff-duplicates.sh
@@ -29,7 +29,7 @@ make_tree () {
make_tree_entry "$1" "$2" "$3"
shift; shift; shift
done |
- git hash-object -w -t tree --stdin
+ git hash-object --literally -w -t tree --stdin
}
# this is kind of a convoluted setup, but matches
diff --git a/t/t4059-diff-submodule-not-initialized.sh b/t/t4059-diff-submodule-not-initialized.sh
index 49bca7b..d489230 100755
--- a/t/t4059-diff-submodule-not-initialized.sh
+++ b/t/t4059-diff-submodule-not-initialized.sh
@@ -49,7 +49,7 @@ test_expect_success 'setup - submodules' '
'
test_expect_success 'setup - git submodule add' '
- git submodule add ./sm2 sm1 &&
+ git -c protocol.file.allow=always submodule add ./sm2 sm1 &&
commit_file sm1 .gitmodules &&
git diff-tree -p --no-commit-id --submodule=log HEAD -- sm1 >actual &&
cat >expected <<-EOF &&
diff --git a/t/t4060-diff-submodule-option-diff-format.sh b/t/t4060-diff-submodule-option-diff-format.sh
index dc7b242..97c6424 100755
--- a/t/t4060-diff-submodule-option-diff-format.sh
+++ b/t/t4060-diff-submodule-option-diff-format.sh
@@ -361,7 +361,6 @@ test_expect_success 'typechanged submodule(submodule->blob)' '
rm -f sm1 &&
test_create_repo sm1 &&
head6=$(add_file sm1 foo6 foo7)
-fullhead6=$(cd sm1; git rev-parse --verify HEAD)
test_expect_success 'nonexistent commit' '
git diff-index -p --submodule=diff HEAD >actual &&
cat >expected <<-EOF &&
@@ -704,10 +703,26 @@ test_expect_success 'path filter' '
diff_cmp expected actual
'
-commit_file sm2
+cat >.gitmodules <<-EOF
+[submodule "sm2"]
+ path = sm2
+ url = bogus_url
+EOF
+git add .gitmodules
+commit_file sm2 .gitmodules
+
test_expect_success 'given commit' '
git diff-index -p --submodule=diff HEAD^ >actual &&
cat >expected <<-EOF &&
+ diff --git a/.gitmodules b/.gitmodules
+ new file mode 100644
+ index 1234567..89abcde
+ --- /dev/null
+ +++ b/.gitmodules
+ @@ -0,0 +1,3 @@
+ +[submodule "sm2"]
+ +path = sm2
+ +url = bogus_url
Submodule sm1 $head7...0000000 (submodule deleted)
Submodule sm2 0000000...$head9 (new submodule)
diff --git a/sm2/foo8 b/sm2/foo8
@@ -729,15 +744,21 @@ test_expect_success 'given commit' '
'
test_expect_success 'setup .git file for sm2' '
- (cd sm2 &&
- REAL="$(pwd)/../.real" &&
- mv .git "$REAL" &&
- echo "gitdir: $REAL" >.git)
+ git submodule absorbgitdirs sm2
'
test_expect_success 'diff --submodule=diff with .git file' '
git diff --submodule=diff HEAD^ >actual &&
cat >expected <<-EOF &&
+ diff --git a/.gitmodules b/.gitmodules
+ new file mode 100644
+ index 1234567..89abcde
+ --- /dev/null
+ +++ b/.gitmodules
+ @@ -0,0 +1,3 @@
+ +[submodule "sm2"]
+ +path = sm2
+ +url = bogus_url
Submodule sm1 $head7...0000000 (submodule deleted)
Submodule sm2 0000000...$head9 (new submodule)
diff --git a/sm2/foo8 b/sm2/foo8
@@ -758,10 +779,68 @@ test_expect_success 'diff --submodule=diff with .git file' '
diff_cmp expected actual
'
+mv sm2 sm2-bak
+
+test_expect_success 'deleted submodule with .git file' '
+ git diff-index -p --submodule=diff HEAD >actual &&
+ cat >expected <<-EOF &&
+ Submodule sm1 $head7...0000000 (submodule deleted)
+ Submodule sm2 $head9...0000000 (submodule deleted)
+ diff --git a/sm2/foo8 b/sm2/foo8
+ deleted file mode 100644
+ index 1234567..89abcde
+ --- a/sm2/foo8
+ +++ /dev/null
+ @@ -1 +0,0 @@
+ -foo8
+ diff --git a/sm2/foo9 b/sm2/foo9
+ deleted file mode 100644
+ index 1234567..89abcde
+ --- a/sm2/foo9
+ +++ /dev/null
+ @@ -1 +0,0 @@
+ -foo9
+ EOF
+ diff_cmp expected actual
+'
+
+echo submodule-to-blob>sm2
+
+test_expect_success 'typechanged(submodule->blob) submodule with .git file' '
+ git diff-index -p --submodule=diff HEAD >actual &&
+ cat >expected <<-EOF &&
+ Submodule sm1 $head7...0000000 (submodule deleted)
+ Submodule sm2 $head9...0000000 (submodule deleted)
+ diff --git a/sm2/foo8 b/sm2/foo8
+ deleted file mode 100644
+ index 1234567..89abcde
+ --- a/sm2/foo8
+ +++ /dev/null
+ @@ -1 +0,0 @@
+ -foo8
+ diff --git a/sm2/foo9 b/sm2/foo9
+ deleted file mode 100644
+ index 1234567..89abcde
+ --- a/sm2/foo9
+ +++ /dev/null
+ @@ -1 +0,0 @@
+ -foo9
+ diff --git a/sm2 b/sm2
+ new file mode 100644
+ index 1234567..89abcde
+ --- /dev/null
+ +++ b/sm2
+ @@ -0,0 +1 @@
+ +submodule-to-blob
+ EOF
+ diff_cmp expected actual
+'
+
+rm sm2
+mv sm2-bak sm2
+
test_expect_success 'setup nested submodule' '
- git submodule add -f ./sm2 &&
- git commit -a -m "add sm2" &&
- git -C sm2 submodule add ../sm2 nested &&
+ git -c protocol.file.allow=always -C sm2 submodule add ../sm2 nested &&
git -C sm2 commit -a -m "nested sub" &&
head10=$(git -C sm2 rev-parse --short --verify HEAD)
'
@@ -791,6 +870,7 @@ test_expect_success 'diff --submodule=diff with moved nested submodule HEAD' '
test_expect_success 'diff --submodule=diff recurses into nested submodules' '
cat >expected <<-EOF &&
+ Submodule sm1 $head7...0000000 (submodule deleted)
Submodule sm2 contains modified content
Submodule sm2 $head9..$head10:
diff --git a/sm2/.gitmodules b/sm2/.gitmodules
@@ -830,4 +910,67 @@ test_expect_success 'diff --submodule=diff recurses into nested submodules' '
diff_cmp expected actual
'
+(cd sm2; commit_file nested)
+commit_file sm2
+head12=$(cd sm2; git rev-parse --short --verify HEAD)
+
+mv sm2 sm2-bak
+
+test_expect_success 'diff --submodule=diff recurses into deleted nested submodules' '
+ cat >expected <<-EOF &&
+ Submodule sm1 $head7...0000000 (submodule deleted)
+ Submodule sm2 $head12...0000000 (submodule deleted)
+ diff --git a/sm2/.gitmodules b/sm2/.gitmodules
+ deleted file mode 100644
+ index 3a816b8..0000000
+ --- a/sm2/.gitmodules
+ +++ /dev/null
+ @@ -1,3 +0,0 @@
+ -[submodule "nested"]
+ - path = nested
+ - url = ../sm2
+ diff --git a/sm2/foo8 b/sm2/foo8
+ deleted file mode 100644
+ index db9916b..0000000
+ --- a/sm2/foo8
+ +++ /dev/null
+ @@ -1 +0,0 @@
+ -foo8
+ diff --git a/sm2/foo9 b/sm2/foo9
+ deleted file mode 100644
+ index 9c3b4f6..0000000
+ --- a/sm2/foo9
+ +++ /dev/null
+ @@ -1 +0,0 @@
+ -foo9
+ Submodule nested $head11...0000000 (submodule deleted)
+ diff --git a/sm2/nested/file b/sm2/nested/file
+ deleted file mode 100644
+ index ca281f5..0000000
+ --- a/sm2/nested/file
+ +++ /dev/null
+ @@ -1 +0,0 @@
+ -nested content
+ diff --git a/sm2/nested/foo8 b/sm2/nested/foo8
+ deleted file mode 100644
+ index db9916b..0000000
+ --- a/sm2/nested/foo8
+ +++ /dev/null
+ @@ -1 +0,0 @@
+ -foo8
+ diff --git a/sm2/nested/foo9 b/sm2/nested/foo9
+ deleted file mode 100644
+ index 9c3b4f6..0000000
+ --- a/sm2/nested/foo9
+ +++ /dev/null
+ @@ -1 +0,0 @@
+ -foo9
+ EOF
+ git diff --submodule=diff >actual 2>err &&
+ test_must_be_empty err &&
+ diff_cmp expected actual
+'
+
+mv sm2-bak sm2
+
test_done
diff --git a/t/t4062-diff-pickaxe.sh b/t/t4062-diff-pickaxe.sh
index 1130c80..a90b46b 100755
--- a/t/t4062-diff-pickaxe.sh
+++ b/t/t4062-diff-pickaxe.sh
@@ -5,6 +5,7 @@
test_description='Pickaxe options'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -23,7 +24,7 @@ test_expect_success '-G matches' '
test_expect_success '-S --pickaxe-regex' '
git diff --name-only -S0 --pickaxe-regex HEAD^ >out &&
- verbose test 4096-zeroes.txt = "$(cat out)"
+ test 4096-zeroes.txt = "$(cat out)"
'
test_done
diff --git a/t/t4063-diff-blobs.sh b/t/t4063-diff-blobs.sh
index bc69e26..7e6c9d6 100755
--- a/t/t4063-diff-blobs.sh
+++ b/t/t4063-diff-blobs.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test direct comparison of blobs via git-diff'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
run_diff () {
diff --git a/t/t4066-diff-emit-delay.sh b/t/t4066-diff-emit-delay.sh
index a1de63b..0ecb391 100755
--- a/t/t4066-diff-emit-delay.sh
+++ b/t/t4066-diff-emit-delay.sh
@@ -4,6 +4,7 @@ test_description='test combined/stat/moved interaction'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# This test covers a weird 3-way interaction between "--cc -p", which will run
diff --git a/t/t4067-diff-partial-clone.sh b/t/t4067-diff-partial-clone.sh
index 804f2a8..7af3a08 100755
--- a/t/t4067-diff-partial-clone.sh
+++ b/t/t4067-diff-partial-clone.sh
@@ -2,6 +2,7 @@
test_description='behavior of diff when reading objects in a partial clone'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'git show batches blobs' '
@@ -77,6 +78,7 @@ test_expect_success 'diff skips same-OID blobs' '
test_expect_success 'when fetching missing objects, diff skips GITLINKs' '
test_when_finished "rm -rf sub server client trace" &&
+ test_config_global protocol.file.allow always &&
test_create_repo sub &&
test_commit -C sub first &&
@@ -149,7 +151,7 @@ test_expect_success 'diff does not fetch anything if inexact rename detection is
# Ensure no fetches.
GIT_TRACE_PACKET="$(pwd)/trace" git -C client diff --raw -M HEAD^ HEAD &&
- ! test_path_exists trace
+ test_path_is_missing trace
'
test_expect_success 'diff --break-rewrites fetches only if necessary, and batches blobs if it does' '
@@ -169,7 +171,7 @@ test_expect_success 'diff --break-rewrites fetches only if necessary, and batche
# Ensure no fetches.
GIT_TRACE_PACKET="$(pwd)/trace" git -C client diff --raw -M HEAD^ HEAD &&
- ! test_path_exists trace &&
+ test_path_is_missing trace &&
# But with --break-rewrites, ensure that there is exactly 1 negotiation
# by checking that there is only 1 "done" line sent. ("done" marks the
diff --git a/t/t4068-diff-symmetric-merge-base.sh b/t/t4068-diff-symmetric-merge-base.sh
index 2d650d8..eff63c1 100755
--- a/t/t4068-diff-symmetric-merge-base.sh
+++ b/t/t4068-diff-symmetric-merge-base.sh
@@ -34,7 +34,7 @@ test_expect_success setup '
echo c >c &&
git add c &&
git commit -m C &&
- git tag commit-C &&
+ git tag -m commit-C commit-C &&
git merge -m D main &&
git tag commit-D &&
git checkout main &&
@@ -68,27 +68,27 @@ test_expect_success 'diff with two merge bases' '
test_expect_success 'diff with no merge bases' '
test_must_fail git diff br2...br3 2>err &&
- test_i18ngrep "fatal: br2...br3: no merge base" err
+ test_grep "fatal: br2...br3: no merge base" err
'
test_expect_success 'diff with too many symmetric differences' '
test_must_fail git diff br1...main br2...br3 2>err &&
- test_i18ngrep "usage" err
+ test_grep "usage" err
'
test_expect_success 'diff with symmetric difference and extraneous arg' '
test_must_fail git diff main br1...main 2>err &&
- test_i18ngrep "usage" err
+ test_grep "usage" err
'
test_expect_success 'diff with two ranges' '
test_must_fail git diff main br1..main br2..br3 2>err &&
- test_i18ngrep "usage" err
+ test_grep "usage" err
'
test_expect_success 'diff with ranges and extra arg' '
test_must_fail git diff main br1..main commit-D 2>err &&
- test_i18ngrep "usage" err
+ test_grep "usage" err
'
test_expect_success 'diff --merge-base with no commits' '
@@ -97,7 +97,7 @@ test_expect_success 'diff --merge-base with no commits' '
test_expect_success 'diff --merge-base with three commits' '
test_must_fail git diff --merge-base br1 br2 main 2>err &&
- test_i18ngrep "usage" err
+ test_grep "usage" err
'
for cmd in diff-index diff
@@ -109,6 +109,13 @@ do
test_cmp expect actual
'
+ test_expect_success "$cmd --merge-base with annotated tag" '
+ git checkout main &&
+ git $cmd commit-C >expect &&
+ git $cmd --merge-base commit-C >actual &&
+ test_cmp expect actual
+ '
+
test_expect_success "$cmd --merge-base with one commit and unstaged changes" '
git checkout main &&
test_when_finished git reset --hard &&
@@ -143,19 +150,19 @@ do
test_expect_success "$cmd --merge-base with non-commit" '
git checkout main &&
test_must_fail git $cmd --merge-base main^{tree} 2>err &&
- test_i18ngrep "fatal: --merge-base only works with commits" err
+ test_grep "is a tree, not a commit" err
'
test_expect_success "$cmd --merge-base with no merge bases and one commit" '
git checkout main &&
test_must_fail git $cmd --merge-base br3 2>err &&
- test_i18ngrep "fatal: no merge base found" err
+ test_grep "fatal: no merge base found" err
'
test_expect_success "$cmd --merge-base with multiple merge bases and one commit" '
git checkout main &&
test_must_fail git $cmd --merge-base br1 2>err &&
- test_i18ngrep "fatal: multiple merge bases found" err
+ test_grep "fatal: multiple merge bases found" err
'
done
@@ -169,28 +176,28 @@ do
test_expect_success "$cmd --merge-base commit and non-commit" '
test_must_fail git $cmd --merge-base br2 main^{tree} 2>err &&
- test_i18ngrep "fatal: --merge-base only works with commits" err
+ test_grep "is a tree, not a commit" err
'
test_expect_success "$cmd --merge-base with no merge bases and two commits" '
test_must_fail git $cmd --merge-base br2 br3 2>err &&
- test_i18ngrep "fatal: no merge base found" err
+ test_grep "fatal: no merge base found" err
'
test_expect_success "$cmd --merge-base with multiple merge bases and two commits" '
test_must_fail git $cmd --merge-base main br1 2>err &&
- test_i18ngrep "fatal: multiple merge bases found" err
+ test_grep "fatal: multiple merge bases found" err
'
done
test_expect_success 'diff-tree --merge-base with one commit' '
test_must_fail git diff-tree --merge-base main 2>err &&
- test_i18ngrep "fatal: --merge-base only works with two commits" err
+ test_grep "fatal: --merge-base only works with two commits" err
'
test_expect_success 'diff --merge-base with range' '
test_must_fail git diff --merge-base br2..br3 2>err &&
- test_i18ngrep "fatal: --merge-base does not work with ranges" err
+ test_grep "fatal: --merge-base does not work with ranges" err
'
test_done
diff --git a/t/t4069-remerge-diff.sh b/t/t4069-remerge-diff.sh
new file mode 100755
index 0000000..07323eb
--- /dev/null
+++ b/t/t4069-remerge-diff.sh
@@ -0,0 +1,320 @@
+#!/bin/sh
+
+test_description='remerge-diff handling'
+
+. ./test-lib.sh
+
+# This test is ort-specific
+if test "${GIT_TEST_MERGE_ALGORITHM}" != ort
+then
+ skip_all="GIT_TEST_MERGE_ALGORITHM != ort"
+ test_done
+fi
+
+test_expect_success 'setup basic merges' '
+ test_write_lines 1 2 3 4 5 6 7 8 9 >numbers &&
+ git add numbers &&
+ git commit -m base &&
+
+ git branch feature_a &&
+ git branch feature_b &&
+ git branch feature_c &&
+
+ git branch ab_resolution &&
+ git branch bc_resolution &&
+
+ git checkout feature_a &&
+ test_write_lines 1 2 three 4 5 6 7 eight 9 >numbers &&
+ git commit -a -m change_a &&
+
+ git checkout feature_b &&
+ test_write_lines 1 2 tres 4 5 6 7 8 9 >numbers &&
+ git commit -a -m change_b &&
+
+ git checkout feature_c &&
+ test_write_lines 1 2 3 4 5 6 7 8 9 10 >numbers &&
+ git commit -a -m change_c &&
+
+ git checkout bc_resolution &&
+ git merge --ff-only feature_b &&
+ # no conflict
+ git merge feature_c &&
+
+ git checkout ab_resolution &&
+ git merge --ff-only feature_a &&
+ # conflicts!
+ test_must_fail git merge feature_b &&
+ # Resolve conflict...and make another change elsewhere
+ test_write_lines 1 2 drei 4 5 6 7 acht 9 >numbers &&
+ git add numbers &&
+ git merge --continue
+'
+
+test_expect_success 'remerge-diff on a clean merge' '
+ git log -1 --oneline bc_resolution >expect &&
+ git show --oneline --remerge-diff bc_resolution >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'remerge-diff on a clean merge with a filter' '
+ git show --oneline --remerge-diff --diff-filter=U bc_resolution >actual &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'remerge-diff with both a resolved conflict and an unrelated change' '
+ git log -1 --oneline ab_resolution >tmp &&
+ cat <<-EOF >>tmp &&
+ diff --git a/numbers b/numbers
+ remerge CONFLICT (content): Merge conflict in numbers
+ index a1fb731..6875544 100644
+ --- a/numbers
+ +++ b/numbers
+ @@ -1,13 +1,9 @@
+ 1
+ 2
+ -<<<<<<< b0ed5cb (change_a)
+ -three
+ -=======
+ -tres
+ ->>>>>>> 6cd3f82 (change_b)
+ +drei
+ 4
+ 5
+ 6
+ 7
+ -eight
+ +acht
+ 9
+ EOF
+ # Hashes above are sha1; rip them out so test works with sha256
+ sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >expect &&
+
+ git show --oneline --remerge-diff ab_resolution >tmp &&
+ sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'pickaxe still includes additional headers for relevant changes' '
+ # reuses "expect" from the previous testcase
+
+ git log --oneline --remerge-diff -Sacht ab_resolution >tmp &&
+ sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'can filter out additional headers with pickaxe' '
+ git show --remerge-diff --submodule=log --find-object=HEAD ab_resolution >actual &&
+ test_must_be_empty actual &&
+
+ git show --remerge-diff -S"not present" --all >actual &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'setup non-content conflicts' '
+ git switch --orphan base &&
+
+ test_write_lines 1 2 3 4 5 6 7 8 9 >numbers &&
+ test_write_lines a b c d e f g h i >letters &&
+ test_write_lines in the way >content &&
+ git add numbers letters content &&
+ git commit -m base &&
+
+ git branch side1 &&
+ git branch side2 &&
+
+ git checkout side1 &&
+ test_write_lines 1 2 three 4 5 6 7 8 9 >numbers &&
+ git mv letters letters_side1 &&
+ git mv content file_or_directory &&
+ git add numbers &&
+ git commit -m side1 &&
+
+ git checkout side2 &&
+ git rm numbers &&
+ git mv letters letters_side2 &&
+ mkdir file_or_directory &&
+ echo hello >file_or_directory/world &&
+ git add file_or_directory/world &&
+ git commit -m side2 &&
+
+ git checkout -b resolution side1 &&
+ test_must_fail git merge side2 &&
+ test_write_lines 1 2 three 4 5 6 7 8 9 >numbers &&
+ git add numbers &&
+ git add letters_side1 &&
+ git rm letters &&
+ git rm letters_side2 &&
+ git add file_or_directory~HEAD &&
+ git mv file_or_directory~HEAD wanted_content &&
+ git commit -m resolved
+'
+
+test_expect_success 'remerge-diff with non-content conflicts' '
+ git log -1 --oneline resolution >tmp &&
+ cat <<-EOF >>tmp &&
+ diff --git a/file_or_directory~HASH (side1) b/wanted_content
+ similarity index 100%
+ rename from file_or_directory~HASH (side1)
+ rename to wanted_content
+ remerge CONFLICT (file/directory): directory in the way of file_or_directory from HASH (side1); moving it to file_or_directory~HASH (side1) instead.
+ diff --git a/letters b/letters
+ remerge CONFLICT (rename/rename): letters renamed to letters_side1 in HASH (side1) and to letters_side2 in HASH (side2).
+ diff --git a/letters_side2 b/letters_side2
+ deleted file mode 100644
+ index b236ae5..0000000
+ --- a/letters_side2
+ +++ /dev/null
+ @@ -1,9 +0,0 @@
+ -a
+ -b
+ -c
+ -d
+ -e
+ -f
+ -g
+ -h
+ -i
+ diff --git a/numbers b/numbers
+ remerge CONFLICT (modify/delete): numbers deleted in HASH (side2) and modified in HASH (side1). Version HASH (side1) of numbers left in tree.
+ EOF
+ # We still have some sha1 hashes above; rip them out so test works
+ # with sha256
+ sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >expect &&
+
+ git show --oneline --remerge-diff resolution >tmp &&
+ sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'remerge-diff w/ diff-filter=U: all conflict headers, no diff content' '
+ git log -1 --oneline resolution >tmp &&
+ cat <<-EOF >>tmp &&
+ diff --git a/file_or_directory~HASH (side1) b/file_or_directory~HASH (side1)
+ remerge CONFLICT (file/directory): directory in the way of file_or_directory from HASH (side1); moving it to file_or_directory~HASH (side1) instead.
+ diff --git a/letters b/letters
+ remerge CONFLICT (rename/rename): letters renamed to letters_side1 in HASH (side1) and to letters_side2 in HASH (side2).
+ diff --git a/numbers b/numbers
+ remerge CONFLICT (modify/delete): numbers deleted in HASH (side2) and modified in HASH (side1). Version HASH (side1) of numbers left in tree.
+ EOF
+ # We still have some sha1 hashes above; rip them out so test works
+ # with sha256
+ sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >expect &&
+
+ git show --oneline --remerge-diff --diff-filter=U resolution >tmp &&
+ sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'submodule formatting ignores additional headers' '
+ # Reuses "expect" from last testcase
+
+ git show --oneline --remerge-diff --diff-filter=U --submodule=log >tmp &&
+ sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'remerge-diff w/ diff-filter=R: relevant file + conflict header' '
+ git log -1 --oneline resolution >tmp &&
+ cat <<-EOF >>tmp &&
+ diff --git a/file_or_directory~HASH (side1) b/wanted_content
+ similarity index 100%
+ rename from file_or_directory~HASH (side1)
+ rename to wanted_content
+ remerge CONFLICT (file/directory): directory in the way of file_or_directory from HASH (side1); moving it to file_or_directory~HASH (side1) instead.
+ EOF
+ # We still have some sha1 hashes above; rip them out so test works
+ # with sha256
+ sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >expect &&
+
+ git show --oneline --remerge-diff --diff-filter=R resolution >tmp &&
+ sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'remerge-diff w/ pathspec: limits to relevant file including conflict header' '
+ git log -1 --oneline resolution >tmp &&
+ cat <<-EOF >>tmp &&
+ diff --git a/letters b/letters
+ remerge CONFLICT (rename/rename): letters renamed to letters_side1 in HASH (side1) and to letters_side2 in HASH (side2).
+ diff --git a/letters_side2 b/letters_side2
+ deleted file mode 100644
+ index b236ae5..0000000
+ --- a/letters_side2
+ +++ /dev/null
+ @@ -1,9 +0,0 @@
+ -a
+ -b
+ -c
+ -d
+ -e
+ -f
+ -g
+ -h
+ -i
+ EOF
+ # We still have some sha1 hashes above; rip them out so test works
+ # with sha256
+ sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >expect &&
+
+ git show --oneline --remerge-diff resolution -- "letters*" >tmp &&
+ sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'setup non-content conflicts' '
+ git switch --orphan newbase &&
+
+ test_write_lines 1 2 3 4 5 6 7 8 9 >numbers &&
+ git add numbers &&
+ git commit -m base &&
+
+ git branch newside1 &&
+ git branch newside2 &&
+
+ git checkout newside1 &&
+ test_write_lines 1 2 three 4 5 6 7 8 9 >numbers &&
+ git add numbers &&
+ git commit -m side1 &&
+
+ git checkout newside2 &&
+ test_write_lines 1 2 drei 4 5 6 7 8 9 >numbers &&
+ git add numbers &&
+ git commit -m side2 &&
+
+ git checkout -b newresolution newside1 &&
+ test_must_fail git merge newside2 &&
+ git checkout --theirs numbers &&
+ git add -u numbers &&
+ git commit -m resolved
+'
+
+test_expect_success 'remerge-diff turns off history simplification' '
+ git log -1 --oneline newresolution >tmp &&
+ cat <<-EOF >>tmp &&
+ diff --git a/numbers b/numbers
+ remerge CONFLICT (content): Merge conflict in numbers
+ index 070e9e7..5335e78 100644
+ --- a/numbers
+ +++ b/numbers
+ @@ -1,10 +1,6 @@
+ 1
+ 2
+ -<<<<<<< 96f1e45 (side1)
+ -three
+ -=======
+ drei
+ ->>>>>>> 4fd522f (side2)
+ 4
+ 5
+ 6
+ EOF
+ # We still have some sha1 hashes above; rip them out so test works
+ # with sha256
+ sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >expect &&
+
+ git show --oneline --remerge-diff newresolution -- numbers >tmp &&
+ sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t4100-apply-stat.sh b/t/t4100-apply-stat.sh
index 9b433de..d503547 100755
--- a/t/t4100-apply-stat.sh
+++ b/t/t4100-apply-stat.sh
@@ -6,6 +6,8 @@
test_description='git apply --stat --summary test, with --recount
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
UNC='s/^\(@@ -[1-9][0-9]*\),[0-9]* \(+[1-9][0-9]*\),[0-9]* @@/\1,999 \2,999 @@/'
diff --git a/t/t4101-apply-nonl.sh b/t/t4101-apply-nonl.sh
index e3443d0..b116919 100755
--- a/t/t4101-apply-nonl.sh
+++ b/t/t4101-apply-nonl.sh
@@ -6,6 +6,8 @@
test_description='git apply should handle files with incomplete lines.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# setup
diff --git a/t/t4102-apply-rename.sh b/t/t4102-apply-rename.sh
index fae3059..d1e06fc 100755
--- a/t/t4102-apply-rename.sh
+++ b/t/t4102-apply-rename.sh
@@ -6,6 +6,8 @@
test_description='git apply handling copy/rename patch.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# setup
diff --git a/t/t4103-apply-binary.sh b/t/t4103-apply-binary.sh
index d370ecf..144619a 100755
--- a/t/t4103-apply-binary.sh
+++ b/t/t4103-apply-binary.sh
@@ -9,6 +9,7 @@ test_description='git apply handling binary patches
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t4104-apply-boundary.sh b/t/t4104-apply-boundary.sh
index 71ef413..dc501aa 100755
--- a/t/t4104-apply-boundary.sh
+++ b/t/t4104-apply-boundary.sh
@@ -5,6 +5,7 @@
test_description='git apply boundary tests'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
L="c d e f g h i j k l m n o p q r s t u v w x"
diff --git a/t/t4105-apply-fuzz.sh b/t/t4105-apply-fuzz.sh
index 3266e39..ed814a8 100755
--- a/t/t4105-apply-fuzz.sh
+++ b/t/t4105-apply-fuzz.sh
@@ -2,6 +2,8 @@
test_description='apply with fuzz and offset'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
dotest () {
@@ -15,15 +17,9 @@ dotest () {
test_expect_success setup '
- for i in 1 2 3 4 5 6 7 8 9 10 11 12
- do
- echo $i
- done >file &&
+ test_write_lines 1 2 3 4 5 6 7 8 9 10 11 12 >file &&
git update-index --add file &&
- for i in 1 2 3 4 5 6 7 a b c d e 8 9 10 11 12
- do
- echo $i
- done >file &&
+ test_write_lines 1 2 3 4 5 6 7 a b c d e 8 9 10 11 12 >file &&
cat file >expect &&
git diff >O0.diff &&
diff --git a/t/t4106-apply-stdin.sh b/t/t4106-apply-stdin.sh
index 72467a1..5c150f3 100755
--- a/t/t4106-apply-stdin.sh
+++ b/t/t4106-apply-stdin.sh
@@ -2,6 +2,8 @@
test_description='git apply --numstat - <patch'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -18,7 +20,10 @@ test_expect_success 'git apply --numstat - < patch' '
'
test_expect_success 'git apply --numstat - < patch patch' '
- for i in 1 2; do echo "1 1 text"; done >expect &&
+ cat >expect <<-\EOF &&
+ 1 1 text
+ 1 1 text
+ EOF
git apply --numstat - < patch patch >actual &&
test_cmp expect actual
'
diff --git a/t/t4108-apply-threeway.sh b/t/t4108-apply-threeway.sh
index cc3aa33..c558282 100755
--- a/t/t4108-apply-threeway.sh
+++ b/t/t4108-apply-threeway.sh
@@ -275,4 +275,22 @@ test_expect_success 'apply full-index patch with 3way' '
git apply --3way --index bin.diff
'
+test_expect_success 'apply delete then new patch with 3way' '
+ git reset --hard main &&
+ test_write_lines 2 > delnew &&
+ git add delnew &&
+ git diff --cached >> new.patch &&
+ git reset --hard &&
+ test_write_lines 1 > delnew &&
+ git add delnew &&
+ git commit -m "delnew" &&
+ rm delnew &&
+ git diff >> delete-then-new.patch &&
+ cat new.patch >> delete-then-new.patch &&
+
+ git checkout -- . &&
+ # Apply must succeed.
+ git apply --3way delete-then-new.patch
+'
+
test_done
diff --git a/t/t4109-apply-multifrag.sh b/t/t4109-apply-multifrag.sh
index ac58083..4dc6d8e 100755
--- a/t/t4109-apply-multifrag.sh
+++ b/t/t4109-apply-multifrag.sh
@@ -6,6 +6,8 @@
test_description='git apply test patches with multiple fragments.'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
cp "$TEST_DIRECTORY/t4109/patch1.patch" .
diff --git a/t/t4110-apply-scan.sh b/t/t4110-apply-scan.sh
index 09f5811..266302a 100755
--- a/t/t4110-apply-scan.sh
+++ b/t/t4110-apply-scan.sh
@@ -7,6 +7,8 @@
test_description='git apply test for patches which require scanning forwards and backwards.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'git apply scan' '
diff --git a/t/t4111-apply-subdir.sh b/t/t4111-apply-subdir.sh
index 1618a6d..e9a87d7 100755
--- a/t/t4111-apply-subdir.sh
+++ b/t/t4111-apply-subdir.sh
@@ -2,6 +2,7 @@
test_description='patching from inconvenient places'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t4112-apply-renames.sh b/t/t4112-apply-renames.sh
index f9ad183..d53aa42 100755
--- a/t/t4112-apply-renames.sh
+++ b/t/t4112-apply-renames.sh
@@ -7,6 +7,8 @@ test_description='git apply should not get confused with rename/copy.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# setup
diff --git a/t/t4113-apply-ending.sh b/t/t4113-apply-ending.sh
index 66fa515..2c65c6a 100755
--- a/t/t4113-apply-ending.sh
+++ b/t/t4113-apply-ending.sh
@@ -6,6 +6,7 @@
test_description='git apply trying to add an ending line.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# setup
diff --git a/t/t4114-apply-typechange.sh b/t/t4114-apply-typechange.sh
index da3e64f..8ff3640 100755
--- a/t/t4114-apply-typechange.sh
+++ b/t/t4114-apply-typechange.sh
@@ -7,6 +7,7 @@ test_description='git apply should not get confused with type changes.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup repository and commits' '
diff --git a/t/t4115-apply-symlink.sh b/t/t4115-apply-symlink.sh
index 872fcda..cbef0a5 100755
--- a/t/t4115-apply-symlink.sh
+++ b/t/t4115-apply-symlink.sh
@@ -7,6 +7,7 @@ test_description='git apply symlinks and partial files
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -44,4 +45,100 @@ test_expect_success 'apply --index symlink patch' '
'
+test_expect_success 'symlink setup' '
+ ln -s .git symlink &&
+ git add symlink &&
+ git commit -m "add symlink"
+'
+
+test_expect_success SYMLINKS 'symlink escape when creating new files' '
+ test_when_finished "git reset --hard && git clean -dfx" &&
+
+ cat >patch <<-EOF &&
+ diff --git a/symlink b/renamed-symlink
+ similarity index 100%
+ rename from symlink
+ rename to renamed-symlink
+ --
+ diff --git /dev/null b/renamed-symlink/create-me
+ new file mode 100644
+ index 0000000..039727e
+ --- /dev/null
+ +++ b/renamed-symlink/create-me
+ @@ -0,0 +1,1 @@
+ +busted
+ EOF
+
+ test_must_fail git apply patch 2>stderr &&
+ cat >expected_stderr <<-EOF &&
+ error: affected file ${SQ}renamed-symlink/create-me${SQ} is beyond a symbolic link
+ EOF
+ test_cmp expected_stderr stderr &&
+ test_path_is_missing .git/create-me
+'
+
+test_expect_success SYMLINKS 'symlink escape when modifying file' '
+ test_when_finished "git reset --hard && git clean -dfx" &&
+ touch .git/modify-me &&
+
+ cat >patch <<-EOF &&
+ diff --git a/symlink b/renamed-symlink
+ similarity index 100%
+ rename from symlink
+ rename to renamed-symlink
+ --
+ diff --git a/renamed-symlink/modify-me b/renamed-symlink/modify-me
+ index 1111111..2222222 100644
+ --- a/renamed-symlink/modify-me
+ +++ b/renamed-symlink/modify-me
+ @@ -0,0 +1,1 @@
+ +busted
+ EOF
+
+ test_must_fail git apply patch 2>stderr &&
+ cat >expected_stderr <<-EOF &&
+ error: renamed-symlink/modify-me: No such file or directory
+ EOF
+ test_cmp expected_stderr stderr &&
+ test_must_be_empty .git/modify-me
+'
+
+test_expect_success SYMLINKS 'symlink escape when deleting file' '
+ test_when_finished "git reset --hard && git clean -dfx && rm .git/delete-me" &&
+ touch .git/delete-me &&
+
+ cat >patch <<-EOF &&
+ diff --git a/symlink b/renamed-symlink
+ similarity index 100%
+ rename from symlink
+ rename to renamed-symlink
+ --
+ diff --git a/renamed-symlink/delete-me b/renamed-symlink/delete-me
+ deleted file mode 100644
+ index 1111111..0000000 100644
+ EOF
+
+ test_must_fail git apply patch 2>stderr &&
+ cat >expected_stderr <<-EOF &&
+ error: renamed-symlink/delete-me: No such file or directory
+ EOF
+ test_cmp expected_stderr stderr &&
+ test_path_is_file .git/delete-me
+'
+
+test_expect_success SYMLINKS '--reject removes .rej symlink if it exists' '
+ test_when_finished "git reset --hard && git clean -dfx" &&
+
+ test_commit file &&
+ echo modified >file.t &&
+ git diff -- file.t >patch &&
+ echo modified-again >file.t &&
+
+ ln -s foo file.t.rej &&
+ test_must_fail git apply patch --reject 2>err &&
+ test_grep "Rejected hunk" err &&
+ test_path_is_missing foo &&
+ test_path_is_file file.t.rej
+'
+
test_done
diff --git a/t/t4116-apply-reverse.sh b/t/t4116-apply-reverse.sh
index b99e65c..a9f4ddd 100755
--- a/t/t4116-apply-reverse.sh
+++ b/t/t4116-apply-reverse.sh
@@ -7,18 +7,20 @@ test_description='git apply in reverse
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
- for i in a b c d e f g h i j k l m n; do echo $i; done >file1 &&
+ test_write_lines a b c d e f g h i j k l m n >file1 &&
perl -pe "y/ijk/\\000\\001\\002/" <file1 >file2 &&
git add file1 file2 &&
git commit -m initial &&
git tag initial &&
- for i in a b c g h i J K L m o n p q; do echo $i; done >file1 &&
+ test_write_lines a b c g h i J K L m o n p q >file1 &&
perl -pe "y/mon/\\000\\001\\002/" <file1 >file2 &&
git commit -a -m second &&
diff --git a/t/t4117-apply-reject.sh b/t/t4117-apply-reject.sh
index 0ee93fe..4d15ccd 100755
--- a/t/t4117-apply-reject.sh
+++ b/t/t4117-apply-reject.sh
@@ -7,28 +7,20 @@ test_description='git apply with rejects
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
- for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
- do
- echo $i
- done >file1 &&
+ test_write_lines 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 >file1 &&
cat file1 >saved.file1 &&
git update-index --add file1 &&
git commit -m initial &&
- for i in 1 2 A B 4 5 6 7 8 9 10 11 12 C 13 14 15 16 17 18 19 20 D 21
- do
- echo $i
- done >file1 &&
+ test_write_lines 1 2 A B 4 5 6 7 8 9 10 11 12 C 13 14 15 16 17 18 19 20 D 21 >file1 &&
git diff >patch.1 &&
cat file1 >clean &&
- for i in 1 E 2 3 4 5 6 7 8 9 10 11 12 C 13 14 15 16 17 18 19 20 F 21
- do
- echo $i
- done >expected &&
+ test_write_lines 1 E 2 3 4 5 6 7 8 9 10 11 12 C 13 14 15 16 17 18 19 20 F 21 >expected &&
mv file1 file2 &&
git update-index --add --remove file1 file2 &&
@@ -38,10 +30,7 @@ test_expect_success setup '
mv saved.file1 file1 &&
git update-index --add --remove file1 file2 &&
- for i in 1 E 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 F 21
- do
- echo $i
- done >file1 &&
+ test_write_lines 1 E 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 F 21 >file1 &&
cat file1 >saved.file1
'
diff --git a/t/t4118-apply-empty-context.sh b/t/t4118-apply-empty-context.sh
index 65f2e4c..69c9c48 100755
--- a/t/t4118-apply-empty-context.sh
+++ b/t/t4118-apply-empty-context.sh
@@ -7,14 +7,12 @@ test_description='git apply with new style GNU diff with empty context
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
- {
- echo; echo;
- echo A; echo B; echo C;
- echo;
- } >file1 &&
+ test_write_lines "" "" A B C "" >file1 &&
cat file1 >file1.orig &&
{
cat file1 &&
diff --git a/t/t4119-apply-config.sh b/t/t4119-apply-config.sh
index a9a0583..208c961 100755
--- a/t/t4119-apply-config.sh
+++ b/t/t4119-apply-config.sh
@@ -7,6 +7,8 @@ test_description='git apply --whitespace=strip and configuration file.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t4120-apply-popt.sh b/t/t4120-apply-popt.sh
index 497b628..697e86c 100755
--- a/t/t4120-apply-popt.sh
+++ b/t/t4120-apply-popt.sh
@@ -31,7 +31,7 @@ test_expect_success 'apply git diff with -p2' '
test_expect_success 'apply with too large -p' '
cp file1.saved file1 &&
test_must_fail git apply --stat -p3 patch.file 2>err &&
- test_i18ngrep "removing 3 leading" err
+ test_grep "removing 3 leading" err
'
test_expect_success 'apply (-p2) traditional diff with funny filenames' '
@@ -53,7 +53,7 @@ test_expect_success 'apply (-p2) traditional diff with funny filenames' '
test_expect_success 'apply with too large -p and fancy filename' '
cp file1.saved file1 &&
test_must_fail git apply --stat -p3 patch.escaped 2>err &&
- test_i18ngrep "removing 3 leading" err
+ test_grep "removing 3 leading" err
'
test_expect_success 'apply (-p2) diff, mode change only' '
diff --git a/t/t4121-apply-diffs.sh b/t/t4121-apply-diffs.sh
index b45454a..a80cec9 100755
--- a/t/t4121-apply-diffs.sh
+++ b/t/t4121-apply-diffs.sh
@@ -4,6 +4,7 @@ test_description='git apply for contextually independent diffs'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
echo '1
diff --git a/t/t4122-apply-symlink-inside.sh b/t/t4122-apply-symlink-inside.sh
index aa52de4..2089d84 100755
--- a/t/t4122-apply-symlink-inside.sh
+++ b/t/t4122-apply-symlink-inside.sh
@@ -4,6 +4,7 @@ test_description='apply to deeper directory without getting fooled with symlink'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -94,19 +95,19 @@ test_expect_success SYMLINKS 'do not follow symbolic link (same input)' '
# same input creates a confusing symbolic link
test_must_fail git apply patch 2>error-wt &&
- test_i18ngrep "beyond a symbolic link" error-wt &&
+ test_grep "beyond a symbolic link" error-wt &&
test_path_is_missing arch/x86_64/dir &&
test_path_is_missing arch/i386/dir/file &&
test_must_fail git apply --index patch 2>error-ix &&
- test_i18ngrep "beyond a symbolic link" error-ix &&
+ test_grep "beyond a symbolic link" error-ix &&
test_path_is_missing arch/x86_64/dir &&
test_path_is_missing arch/i386/dir/file &&
test_must_fail git ls-files --error-unmatch arch/x86_64/dir &&
test_must_fail git ls-files --error-unmatch arch/i386/dir &&
test_must_fail git apply --cached patch 2>error-ct &&
- test_i18ngrep "beyond a symbolic link" error-ct &&
+ test_grep "beyond a symbolic link" error-ct &&
test_must_fail git ls-files --error-unmatch arch/x86_64/dir &&
test_must_fail git ls-files --error-unmatch arch/i386/dir &&
@@ -134,23 +135,23 @@ test_expect_success SYMLINKS 'do not follow symbolic link (existing)' '
git add arch/x86_64/dir &&
test_must_fail git apply add_file.patch 2>error-wt-add &&
- test_i18ngrep "beyond a symbolic link" error-wt-add &&
+ test_grep "beyond a symbolic link" error-wt-add &&
test_path_is_missing arch/i386/dir/file &&
mkdir arch/i386/dir &&
>arch/i386/dir/file &&
test_must_fail git apply del_file.patch 2>error-wt-del &&
- test_i18ngrep "beyond a symbolic link" error-wt-del &&
+ test_grep "beyond a symbolic link" error-wt-del &&
test_path_is_file arch/i386/dir/file &&
rm arch/i386/dir/file &&
test_must_fail git apply --index add_file.patch 2>error-ix-add &&
- test_i18ngrep "beyond a symbolic link" error-ix-add &&
+ test_grep "beyond a symbolic link" error-ix-add &&
test_path_is_missing arch/i386/dir/file &&
test_must_fail git ls-files --error-unmatch arch/i386/dir &&
test_must_fail git apply --cached add_file.patch 2>error-ct-file &&
- test_i18ngrep "beyond a symbolic link" error-ct-file &&
+ test_grep "beyond a symbolic link" error-ct-file &&
test_must_fail git ls-files --error-unmatch arch/i386/dir
'
diff --git a/t/t4123-apply-shrink.sh b/t/t4123-apply-shrink.sh
index 984157f..3601c0c 100755
--- a/t/t4123-apply-shrink.sh
+++ b/t/t4123-apply-shrink.sh
@@ -2,6 +2,7 @@
test_description='apply a patch that is larger than the preimage'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
cat >F <<\EOF
@@ -39,20 +40,8 @@ test_expect_success setup '
'
test_expect_success 'apply should fail gracefully' '
-
- if git apply --index patch
- then
- echo Oops, should not have succeeded
- false
- else
- status=$?
- echo "Status was $status"
- if test -f .git/index.lock
- then
- echo Oops, should not have crashed
- false
- fi
- fi
+ test_must_fail git apply --index patch &&
+ test_path_is_missing .git/index.lock
'
test_done
diff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh
index 0ca2982..485c7d2 100755
--- a/t/t4124-apply-ws-rule.sh
+++ b/t/t4124-apply-ws-rule.sh
@@ -230,10 +230,10 @@ test_expect_success 'blank at EOF with --whitespace=fix (1)' '
test_might_fail git config --unset core.whitespace &&
rm -f .gitattributes &&
- { echo a; echo b; echo c; } >one &&
+ test_write_lines a b c >one &&
git add one &&
- { echo a; echo b; echo c; } >expect &&
- { cat expect; echo; } >one &&
+ test_write_lines a b c >expect &&
+ { cat expect && echo; } >one &&
git diff -- one >patch &&
git checkout one &&
@@ -242,10 +242,10 @@ test_expect_success 'blank at EOF with --whitespace=fix (1)' '
'
test_expect_success 'blank at EOF with --whitespace=fix (2)' '
- { echo a; echo b; echo c; } >one &&
+ test_write_lines a b c >one &&
git add one &&
- { echo a; echo c; } >expect &&
- { cat expect; echo; echo; } >one &&
+ test_write_lines a b >expect &&
+ { cat expect && test_write_lines "" ""; } >one &&
git diff -- one >patch &&
git checkout one &&
@@ -254,10 +254,10 @@ test_expect_success 'blank at EOF with --whitespace=fix (2)' '
'
test_expect_success 'blank at EOF with --whitespace=fix (3)' '
- { echo a; echo b; echo; } >one &&
+ test_write_lines a b "" >one &&
git add one &&
- { echo a; echo c; echo; } >expect &&
- { cat expect; echo; echo; } >one &&
+ test_write_lines a c "" >expect &&
+ { cat expect && test_write_lines "" ""; } >one &&
git diff -- one >patch &&
git checkout one &&
@@ -266,9 +266,9 @@ test_expect_success 'blank at EOF with --whitespace=fix (3)' '
'
test_expect_success 'blank at end of hunk, not at EOF with --whitespace=fix' '
- { echo a; echo b; echo; echo; echo; echo; echo; echo d; } >one &&
+ test_write_lines a b "" "" "" "" "" d >one &&
git add one &&
- { echo a; echo c; echo; echo; echo; echo; echo; echo; echo d; } >expect &&
+ test_write_lines a b "" "" "" "" "" "" d >expect &&
cp expect one &&
git diff -- one >patch &&
@@ -278,7 +278,7 @@ test_expect_success 'blank at end of hunk, not at EOF with --whitespace=fix' '
'
test_expect_success 'blank at EOF with --whitespace=warn' '
- { echo a; echo b; echo c; } >one &&
+ test_write_lines a b c >one &&
git add one &&
echo >>one &&
cat one >expect &&
@@ -291,7 +291,7 @@ test_expect_success 'blank at EOF with --whitespace=warn' '
'
test_expect_success 'blank at EOF with --whitespace=error' '
- { echo a; echo b; echo c; } >one &&
+ test_write_lines a b c >one &&
git add one &&
cat one >expect &&
echo >>one &&
@@ -304,7 +304,7 @@ test_expect_success 'blank at EOF with --whitespace=error' '
'
test_expect_success 'blank but not empty at EOF' '
- { echo a; echo b; echo c; } >one &&
+ test_write_lines a b c >one &&
git add one &&
echo " " >>one &&
cat one >expect &&
@@ -317,13 +317,13 @@ test_expect_success 'blank but not empty at EOF' '
'
test_expect_success 'applying beyond EOF requires one non-blank context line' '
- { echo; echo; echo; echo; } >one &&
+ test_write_lines "" "" "" "" >one &&
git add one &&
- { echo b; } >>one &&
+ echo b >>one &&
git diff -- one >patch &&
git checkout one &&
- { echo a; echo; } >one &&
+ test_write_lines a "" >one &&
cp one expect &&
test_must_fail git apply --whitespace=fix patch &&
test_cmp expect one &&
@@ -333,7 +333,7 @@ test_expect_success 'applying beyond EOF requires one non-blank context line' '
test_expect_success 'tons of blanks at EOF should not apply' '
for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16; do
- echo; echo; echo; echo;
+ test_write_lines "" "" "" "" || return 1
done >one &&
git add one &&
echo a >>one &&
@@ -362,9 +362,9 @@ test_expect_success 'missing blank line at end with --whitespace=fix' '
'
test_expect_success 'two missing blank lines at end with --whitespace=fix' '
- { echo a; echo; echo b; echo c; } >one &&
+ test_write_lines a "" b c >one &&
cp one no-blank-lines &&
- { echo; echo; } >>one &&
+ test_write_lines "" "" >>one &&
git add one &&
echo d >>one &&
cp one expect &&
@@ -381,9 +381,9 @@ test_expect_success 'two missing blank lines at end with --whitespace=fix' '
'
test_expect_success 'missing blank line at end, insert before end, --whitespace=fix' '
- { echo a; echo; } >one &&
+ test_write_lines a "" >one &&
git add one &&
- { echo b; echo a; echo; } >one &&
+ test_write_lines b a "" >one &&
cp one expect &&
git diff -- one >patch &&
echo a >one &&
@@ -393,10 +393,10 @@ test_expect_success 'missing blank line at end, insert before end, --whitespace=
'
test_expect_success 'shrink file with tons of missing blanks at end of file' '
- { echo a; echo b; echo c; } >one &&
+ test_write_lines a b c >one &&
cp one no-blank-lines &&
for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16; do
- echo; echo; echo; echo;
+ test_write_lines "" "" "" "" || return 1
done >>one &&
git add one &&
echo a >one &&
@@ -412,9 +412,9 @@ test_expect_success 'shrink file with tons of missing blanks at end of file' '
'
test_expect_success 'missing blanks at EOF must only match blank lines' '
- { echo a; echo b; } >one &&
+ test_write_lines a b >one &&
git add one &&
- { echo c; echo d; } >>one &&
+ test_write_lines c d >>one &&
git diff -- one >patch &&
echo a >one &&
@@ -434,9 +434,9 @@ test_expect_success 'missing blank line should match context line with spaces' '
git add one &&
echo d >>one &&
git diff -- one >patch &&
- { echo a; echo b; echo c; } >one &&
+ test_write_lines a b c >one &&
cp one expect &&
- { echo; echo d; } >>expect &&
+ test_write_lines "" d >>expect &&
git add one &&
git apply --whitespace=fix patch &&
@@ -455,7 +455,7 @@ test_expect_success 'same, but with the --ignore-space-option' '
echo d >>one &&
cp one expect &&
git diff -- one >patch &&
- { echo a; echo b; echo c; } >one &&
+ test_write_lines a b c >one &&
git add one &&
git checkout-index -f one &&
diff --git a/t/t4125-apply-ws-fuzz.sh b/t/t4125-apply-ws-fuzz.sh
index 9671de7..090987c 100755
--- a/t/t4125-apply-ws-fuzz.sh
+++ b/t/t4125-apply-ws-fuzz.sh
@@ -10,10 +10,7 @@ test_expect_success setup '
git add file &&
# file-0 is full of whitespace breakages
- for l in a bb c d eeee f ggg h
- do
- echo "$l "
- done >file-0 &&
+ printf "%s \n" a bb c d eeee f ggg h >file-0 &&
# patch-0 creates a whitespace broken file
cat file-0 >file &&
diff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh
index ceb6a79..56210b5 100755
--- a/t/t4126-apply-empty.sh
+++ b/t/t4126-apply-empty.sh
@@ -2,6 +2,7 @@
test_description='apply empty'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -9,10 +10,9 @@ test_expect_success setup '
git add empty &&
test_tick &&
git commit -m initial &&
- for i in a b c d e
- do
- echo $i
- done >empty &&
+ git commit --allow-empty -m "empty commit" &&
+ git format-patch --always HEAD~ >empty.patch &&
+ test_write_lines a b c d e >empty &&
cat empty >expect &&
git diff |
sed -e "/^diff --git/d" \
@@ -25,33 +25,69 @@ test_expect_success setup '
'
test_expect_success 'apply empty' '
- git reset --hard &&
rm -f missing &&
+ test_when_finished "git reset --hard" &&
git apply patch0 &&
test_cmp expect empty
'
+test_expect_success 'apply empty patch fails' '
+ test_when_finished "git reset --hard" &&
+ test_must_fail git apply empty.patch &&
+ test_must_fail git apply - </dev/null
+'
+
+test_expect_success 'apply with --allow-empty succeeds' '
+ test_when_finished "git reset --hard" &&
+ git apply --allow-empty empty.patch &&
+ git apply --allow-empty - </dev/null
+'
+
test_expect_success 'apply --index empty' '
- git reset --hard &&
rm -f missing &&
+ test_when_finished "git reset --hard" &&
git apply --index patch0 &&
test_cmp expect empty &&
git diff --exit-code
'
test_expect_success 'apply create' '
- git reset --hard &&
rm -f missing &&
+ test_when_finished "git reset --hard" &&
git apply patch1 &&
test_cmp expect missing
'
test_expect_success 'apply --index create' '
- git reset --hard &&
rm -f missing &&
+ test_when_finished "git reset --hard" &&
git apply --index patch1 &&
test_cmp expect missing &&
git diff --exit-code
'
+test_expect_success !MINGW 'apply with no-contents and a funny pathname' '
+ test_when_finished "rm -fr \"funny \"; git reset --hard" &&
+
+ mkdir "funny " &&
+ >"funny /empty" &&
+ git add "funny /empty" &&
+ git diff HEAD -- "funny /" >sample.patch &&
+ git diff -R HEAD -- "funny /" >elpmas.patch &&
+
+ git reset --hard &&
+
+ git apply --stat --check --apply sample.patch &&
+ test_must_be_empty "funny /empty" &&
+
+ git apply --stat --check --apply elpmas.patch &&
+ test_path_is_missing "funny /empty" &&
+
+ git apply -R --stat --check --apply elpmas.patch &&
+ test_must_be_empty "funny /empty" &&
+
+ git apply -R --stat --check --apply sample.patch &&
+ test_path_is_missing "funny /empty"
+'
+
test_done
diff --git a/t/t4127-apply-same-fn.sh b/t/t4127-apply-same-fn.sh
index 305b7e6..aa5cfae 100755
--- a/t/t4127-apply-same-fn.sh
+++ b/t/t4127-apply-same-fn.sh
@@ -2,6 +2,8 @@
test_description='apply same filename'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
modify () {
@@ -10,10 +12,7 @@ modify () {
}
test_expect_success setup '
- for i in a b c d e f g h i j k l m
- do
- echo $i
- done >same_fn &&
+ test_write_lines a b c d e f g h i j k l m >same_fn &&
cp same_fn other_fn &&
git add same_fn other_fn &&
git commit -m initial
diff --git a/t/t4128-apply-root.sh b/t/t4128-apply-root.sh
index 6cc741a..ed94c90 100755
--- a/t/t4128-apply-root.sh
+++ b/t/t4128-apply-root.sh
@@ -2,6 +2,7 @@
test_description='apply same filename'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -24,10 +25,11 @@ diff a/bla/blub/dir/file b/bla/blub/dir/file
EOF
test_expect_success 'apply --directory -p (1)' '
-
git apply --directory=some/sub -p3 --index patch &&
- test Bello = $(git show :some/sub/dir/file) &&
- test Bello = $(cat some/sub/dir/file)
+ echo Bello >expect &&
+ git show :some/sub/dir/file >actual &&
+ test_cmp expect actual &&
+ test_cmp expect some/sub/dir/file
'
@@ -35,8 +37,10 @@ test_expect_success 'apply --directory -p (2) ' '
git reset --hard initial &&
git apply --directory=some/sub/ -p3 --index patch &&
- test Bello = $(git show :some/sub/dir/file) &&
- test Bello = $(cat some/sub/dir/file)
+ echo Bello >expect &&
+ git show :some/sub/dir/file >actual &&
+ test_cmp expect actual &&
+ test_cmp expect some/sub/dir/file
'
@@ -53,8 +57,10 @@ EOF
test_expect_success 'apply --directory (new file)' '
git reset --hard initial &&
git apply --directory=some/sub/dir/ --index patch &&
- test content = $(git show :some/sub/dir/newfile) &&
- test content = $(cat some/sub/dir/newfile)
+ echo content >expect &&
+ git show :some/sub/dir/newfile >actual &&
+ test_cmp expect actual &&
+ test_cmp expect some/sub/dir/newfile
'
cat > patch << EOF
@@ -70,8 +76,10 @@ EOF
test_expect_success 'apply --directory -p (new file)' '
git reset --hard initial &&
git apply -p2 --directory=some/sub/dir/ --index patch &&
- test content = $(git show :some/sub/dir/newfile2) &&
- test content = $(cat some/sub/dir/newfile2)
+ echo content >expect &&
+ git show :some/sub/dir/newfile2 >actual &&
+ test_cmp expect actual &&
+ test_cmp expect some/sub/dir/newfile2
'
cat > patch << EOF
@@ -89,7 +97,8 @@ test_expect_success 'apply --directory (delete file)' '
echo content >some/sub/dir/delfile &&
git add some/sub/dir/delfile &&
git apply --directory=some/sub/dir/ --index patch &&
- ! (git ls-files | grep delfile)
+ git ls-files >out &&
+ ! grep delfile out
'
cat > patch << 'EOF'
@@ -105,8 +114,10 @@ EOF
test_expect_success 'apply --directory (quoted filename)' '
git reset --hard initial &&
git apply --directory=some/sub/dir/ --index patch &&
- test content = $(git show :some/sub/dir/quotefile) &&
- test content = $(cat some/sub/dir/quotefile)
+ echo content >expect &&
+ git show :some/sub/dir/quotefile >actual &&
+ test_cmp expect actual &&
+ test_cmp expect some/sub/dir/quotefile
'
test_done
diff --git a/t/t4129-apply-samemode.sh b/t/t4129-apply-samemode.sh
index 576632f..4eb8444 100755
--- a/t/t4129-apply-samemode.sh
+++ b/t/t4129-apply-samemode.sh
@@ -2,6 +2,8 @@
test_description='applying patch with mode bits'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -39,7 +41,8 @@ test_expect_success FILEMODE 'same mode (index only)' '
chmod +x file &&
git add file &&
git apply --cached patch-0.txt &&
- git ls-files -s file | grep "^100755"
+ git ls-files -s file >ls-files-output &&
+ test_grep "^100755" ls-files-output
'
test_expect_success FILEMODE 'mode update (no index)' '
@@ -58,19 +61,20 @@ test_expect_success FILEMODE 'mode update (with index)' '
test_expect_success FILEMODE 'mode update (index only)' '
git reset --hard &&
git apply --cached patch-1.txt &&
- git ls-files -s file | grep "^100755"
+ git ls-files -s file >ls-files-output &&
+ test_grep "^100755" ls-files-output
'
test_expect_success FILEMODE 'empty mode is rejected' '
git reset --hard &&
test_must_fail git apply patch-empty-mode.txt 2>err &&
- test_i18ngrep "invalid mode" err
+ test_grep "invalid mode" err
'
test_expect_success FILEMODE 'bogus mode is rejected' '
git reset --hard &&
test_must_fail git apply patch-bogus-mode.txt 2>err &&
- test_i18ngrep "invalid mode" err
+ test_grep "invalid mode" err
'
test_expect_success POSIXPERM 'do not use core.sharedRepository for working tree files' '
@@ -99,4 +103,31 @@ test_expect_success POSIXPERM 'do not use core.sharedRepository for working tree
)
'
+test_expect_success 'git apply respects core.fileMode' '
+ test_config core.fileMode false &&
+ echo true >script.sh &&
+ git add --chmod=+x script.sh &&
+ git ls-files -s script.sh >ls-files-output &&
+ test_grep "^100755" ls-files-output &&
+ test_tick && git commit -m "Add script" &&
+ git ls-tree -r HEAD script.sh >ls-tree-output &&
+ test_grep "^100755" ls-tree-output &&
+
+ echo true >>script.sh &&
+ test_tick && git commit -m "Modify script" script.sh &&
+ git format-patch -1 --stdout >patch &&
+ test_grep "^index.*100755$" patch &&
+
+ git switch -c branch HEAD^ &&
+ git apply --index patch 2>err &&
+ test_grep ! "has type 100644, expected 100755" err &&
+ git reset --hard &&
+
+ git apply patch 2>err &&
+ test_grep ! "has type 100644, expected 100755" err &&
+
+ git apply --cached patch 2>err &&
+ test_grep ! "has type 100644, expected 100755" err
+'
+
test_done
diff --git a/t/t4130-apply-criss-cross-rename.sh b/t/t4130-apply-criss-cross-rename.sh
index f8a313b..f3ea632 100755
--- a/t/t4130-apply-criss-cross-rename.sh
+++ b/t/t4130-apply-criss-cross-rename.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='git apply handling criss-cross rename patch.'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
create_file() {
diff --git a/t/t4132-apply-removal.sh b/t/t4132-apply-removal.sh
index fec1d6f..c1e3049 100755
--- a/t/t4132-apply-removal.sh
+++ b/t/t4132-apply-removal.sh
@@ -4,6 +4,8 @@
test_description='git-apply notices removal patches generated by GNU diff'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t4133-apply-filenames.sh b/t/t4133-apply-filenames.sh
index c5ed3b1..c21ddb2 100755
--- a/t/t4133-apply-filenames.sh
+++ b/t/t4133-apply-filenames.sh
@@ -5,6 +5,8 @@
test_description='git apply filename consistency check'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -30,9 +32,9 @@ EOF
test_expect_success 'apply diff with inconsistent filenames in headers' '
test_must_fail git apply bad1.patch 2>err &&
- test_i18ngrep "inconsistent new filename" err &&
+ test_grep "inconsistent new filename" err &&
test_must_fail git apply bad2.patch 2>err &&
- test_i18ngrep "inconsistent old filename" err
+ test_grep "inconsistent old filename" err
'
test_expect_success 'apply diff with new filename missing from headers' '
@@ -44,7 +46,7 @@ test_expect_success 'apply diff with new filename missing from headers' '
+1
EOF
test_must_fail git apply missing_new_filename.diff 2>err &&
- test_i18ngrep "lacks filename information" err
+ test_grep "lacks filename information" err
'
test_expect_success 'apply diff with old filename missing from headers' '
@@ -56,7 +58,7 @@ test_expect_success 'apply diff with old filename missing from headers' '
-1
EOF
test_must_fail git apply missing_old_filename.diff 2>err &&
- test_i18ngrep "lacks filename information" err
+ test_grep "lacks filename information" err
'
test_done
diff --git a/t/t4134-apply-submodule.sh b/t/t4134-apply-submodule.sh
index d1c16ba..aceb4c4 100755
--- a/t/t4134-apply-submodule.sh
+++ b/t/t4134-apply-submodule.sh
@@ -5,6 +5,8 @@
test_description='git apply submodule tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t4135-apply-weird-filenames.sh b/t/t4135-apply-weird-filenames.sh
index 6bc3fb9..d3502c6 100755
--- a/t/t4135-apply-weird-filenames.sh
+++ b/t/t4135-apply-weird-filenames.sh
@@ -2,6 +2,7 @@
test_description='git apply with weird postimage filenames'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t4136-apply-check.sh b/t/t4136-apply-check.sh
index 4c3f264..dfec1c5 100755
--- a/t/t4136-apply-check.sh
+++ b/t/t4136-apply-check.sh
@@ -2,6 +2,8 @@
test_description='git apply should exit non-zero with unrecognized input.'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t4138-apply-ws-expansion.sh b/t/t4138-apply-ws-expansion.sh
index b19faeb..8bbf826 100755
--- a/t/t4138-apply-ws-expansion.sh
+++ b/t/t4138-apply-ws-expansion.sh
@@ -29,8 +29,8 @@ test_expect_success setup '
x=1 &&
while test $x -lt $n
do
- printf "%63s%d\n" "" $x >>after
- x=$(( $x + 1 ))
+ printf "%63s%d\n" "" $x >>after &&
+ x=$(( $x + 1 )) || return 1
done &&
printf "\t%s\n" d e f >>after &&
test_expect_code 1 git diff --no-index before after >patch2.patch.raw &&
@@ -40,8 +40,8 @@ test_expect_success setup '
x=1 &&
while test $x -lt $n
do
- printf "%63s%d\n" "" $x >>expect-2
- x=$(( $x + 1 ))
+ printf "%63s%d\n" "" $x >>expect-2 &&
+ x=$(( $x + 1 )) || return 1
done &&
printf "%64s\n" d e f >>expect-2 &&
@@ -52,8 +52,8 @@ test_expect_success setup '
x=0 &&
while test $x -lt $n
do
- printf "%63s%02d\n" "" $x >>after
- x=$(( $x + 1 ))
+ printf "%63s%02d\n" "" $x >>after &&
+ x=$(( $x + 1 )) || return 1
done &&
printf "\t%s\n" d e f >>after &&
test_expect_code 1 git diff --no-index before after >patch3.patch.raw &&
@@ -63,8 +63,8 @@ test_expect_success setup '
x=0 &&
while test $x -lt $n
do
- printf "%63s%02d\n" "" $x >>expect-3
- x=$(( $x + 1 ))
+ printf "%63s%02d\n" "" $x >>expect-3 &&
+ x=$(( $x + 1 )) || return 1
done &&
printf "%64s\n" d e f >>expect-3 &&
@@ -73,16 +73,16 @@ test_expect_success setup '
x=0 &&
while test $x -lt 50
do
- printf "\t%02d\n" $x >>before
- x=$(( $x + 1 ))
+ printf "\t%02d\n" $x >>before &&
+ x=$(( $x + 1 )) || return 1
done &&
cat before >after &&
printf "%64s\n" a b c >>after &&
while test $x -lt 100
do
- printf "\t%02d\n" $x >>before
- printf "\t%02d\n" $x >>after
- x=$(( $x + 1 ))
+ printf "\t%02d\n" $x >>before &&
+ printf "\t%02d\n" $x >>after &&
+ x=$(( $x + 1 )) || return 1
done &&
test_expect_code 1 git diff --no-index before after >patch4.patch.raw &&
sed -e "s/before/test-4/" -e "s/after/test-4/" patch4.patch.raw >patch4.patch &&
@@ -90,16 +90,16 @@ test_expect_success setup '
x=0 &&
while test $x -lt 50
do
- printf "%63s%02d\n" "" $x >>test-4
- x=$(( $x + 1 ))
+ printf "%63s%02d\n" "" $x >>test-4 &&
+ x=$(( $x + 1 )) || return 1
done &&
cat test-4 >expect-4 &&
printf "%64s\n" a b c >>expect-4 &&
while test $x -lt 100
do
- printf "%63s%02d\n" "" $x >>test-4
- printf "%63s%02d\n" "" $x >>expect-4
- x=$(( $x + 1 ))
+ printf "%63s%02d\n" "" $x >>test-4 &&
+ printf "%63s%02d\n" "" $x >>expect-4 &&
+ x=$(( $x + 1 )) || return 1
done &&
git config core.whitespace tab-in-indent,tabwidth=63 &&
diff --git a/t/t4139-apply-escape.sh b/t/t4139-apply-escape.sh
index 45b5660..e5c7439 100755
--- a/t/t4139-apply-escape.sh
+++ b/t/t4139-apply-escape.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='paths written by git-apply cannot escape the working tree'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# tests will try to write to ../foo, and we do not
diff --git a/t/t4140-apply-ita.sh b/t/t4140-apply-ita.sh
index c614eaf..b375aca 100755
--- a/t/t4140-apply-ita.sh
+++ b/t/t4140-apply-ita.sh
@@ -2,6 +2,7 @@
test_description='git apply of i-t-a file'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t4141-apply-too-large.sh b/t/t4141-apply-too-large.sh
new file mode 100755
index 0000000..20cc120
--- /dev/null
+++ b/t/t4141-apply-too-large.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+test_description='git apply with too-large patch'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success EXPENSIVE 'git apply rejects patches that are too large' '
+ sz=$((1024 * 1024 * 1023)) &&
+ {
+ cat <<-\EOF &&
+ diff --git a/file b/file
+ new file mode 100644
+ --- /dev/null
+ +++ b/file
+ @@ -0,0 +1 @@
+ EOF
+ test-tool genzeros
+ } | test_copy_bytes $sz | test_must_fail git apply 2>err &&
+ grep "patch too large" err
+'
+
+test_done
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 2aaaa0d..5e2b6c8 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -103,7 +103,7 @@ test_expect_success setup '
git format-patch --stdout first >patch1 &&
{
- echo "Message-Id: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>" &&
+ echo "Message-ID: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>" &&
echo "X-Fake-Field: Line One" &&
echo "X-Fake-Field: Line Two" &&
echo "X-Fake-Field: Line Three" &&
@@ -116,7 +116,7 @@ test_expect_success setup '
git format-patch --stdout first | sed -e "1d"
} | append_cr >patch1-crlf.eml &&
{
- printf "%255s\\n" ""
+ printf "%255s\\n" "" &&
echo "X-Fake-Field: Line One" &&
echo "X-Fake-Field: Line Two" &&
echo "X-Fake-Field: Line Three" &&
@@ -196,6 +196,12 @@ test_expect_success setup '
git format-patch -M --stdout lorem^ >rename-add.patch &&
+ git checkout -b empty-commit &&
+ git commit -m "empty commit" --allow-empty &&
+
+ : >empty.patch &&
+ git format-patch --always --stdout empty-commit^ >empty-commit.patch &&
+
# reset time
sane_unset test_tick &&
test_tick
@@ -309,12 +315,10 @@ test_expect_success 'am --patch-format=hg applies hg patch' '
'
test_expect_success 'am with applypatch-msg hook' '
- test_when_finished "rm -f .git/hooks/applypatch-msg" &&
rm -fr .git/rebase-apply &&
git reset --hard &&
git checkout first &&
- mkdir -p .git/hooks &&
- write_script .git/hooks/applypatch-msg <<-\EOF &&
+ test_hook applypatch-msg <<-\EOF &&
cat "$1" >actual-msg &&
echo hook-message >"$1"
EOF
@@ -329,12 +333,10 @@ test_expect_success 'am with applypatch-msg hook' '
'
test_expect_success 'am with failing applypatch-msg hook' '
- test_when_finished "rm -f .git/hooks/applypatch-msg" &&
rm -fr .git/rebase-apply &&
git reset --hard &&
git checkout first &&
- mkdir -p .git/hooks &&
- write_script .git/hooks/applypatch-msg <<-\EOF &&
+ test_hook applypatch-msg <<-\EOF &&
exit 1
EOF
test_must_fail git am patch1 &&
@@ -343,13 +345,26 @@ test_expect_success 'am with failing applypatch-msg hook' '
test_cmp_rev first HEAD
'
+test_expect_success 'am with failing applypatch-msg hook (no verify)' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout first &&
+ test_hook applypatch-msg <<-\EOF &&
+ echo hook-message >"$1"
+ exit 1
+ EOF
+ git am --no-verify patch1 &&
+ test_path_is_missing .git/rebase-apply &&
+ git diff --exit-code second &&
+ git log -1 --format=format:%B >actual &&
+ test_cmp msg actual
+'
+
test_expect_success 'am with pre-applypatch hook' '
- test_when_finished "rm -f .git/hooks/pre-applypatch" &&
rm -fr .git/rebase-apply &&
git reset --hard &&
git checkout first &&
- mkdir -p .git/hooks &&
- write_script .git/hooks/pre-applypatch <<-\EOF &&
+ test_hook pre-applypatch <<-\EOF &&
git diff first >diff.actual
exit 0
EOF
@@ -362,12 +377,10 @@ test_expect_success 'am with pre-applypatch hook' '
'
test_expect_success 'am with failing pre-applypatch hook' '
- test_when_finished "rm -f .git/hooks/pre-applypatch" &&
rm -fr .git/rebase-apply &&
git reset --hard &&
git checkout first &&
- mkdir -p .git/hooks &&
- write_script .git/hooks/pre-applypatch <<-\EOF &&
+ test_hook pre-applypatch <<-\EOF &&
exit 1
EOF
test_must_fail git am patch1 &&
@@ -376,13 +389,28 @@ test_expect_success 'am with failing pre-applypatch hook' '
test_cmp_rev first HEAD
'
+test_expect_success 'am with failing pre-applypatch hook (no verify)' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout first &&
+ touch empty-file &&
+ test_hook pre-applypatch <<-\EOF &&
+ rm empty-file
+ exit 1
+ EOF
+ git am --no-verify patch1 &&
+ test_path_is_missing .git/rebase-apply &&
+ test_path_is_file empty-file &&
+ git diff --exit-code second &&
+ git log -1 --format=format:%B >actual &&
+ test_cmp msg actual
+'
+
test_expect_success 'am with post-applypatch hook' '
- test_when_finished "rm -f .git/hooks/post-applypatch" &&
rm -fr .git/rebase-apply &&
git reset --hard &&
git checkout first &&
- mkdir -p .git/hooks &&
- write_script .git/hooks/post-applypatch <<-\EOF &&
+ test_hook post-applypatch <<-\EOF &&
git rev-parse HEAD >head.actual
git diff second >diff.actual
exit 0
@@ -397,12 +425,10 @@ test_expect_success 'am with post-applypatch hook' '
'
test_expect_success 'am with failing post-applypatch hook' '
- test_when_finished "rm -f .git/hooks/post-applypatch" &&
rm -fr .git/rebase-apply &&
git reset --hard &&
git checkout first &&
- mkdir -p .git/hooks &&
- write_script .git/hooks/post-applypatch <<-\EOF &&
+ test_hook post-applypatch <<-\EOF &&
git rev-parse HEAD >head.actual
exit 1
EOF
@@ -753,7 +779,7 @@ test_expect_success 'am --resolved fails if index has unmerged entries' '
test_must_fail git am --resolved >err &&
test_path_is_dir .git/rebase-apply &&
test_cmp_rev second HEAD &&
- test_i18ngrep "still have unmerged paths" err
+ test_grep "still have unmerged paths" err
'
test_expect_success 'am takes patches from a Pine mailbox' '
@@ -887,7 +913,7 @@ test_expect_success 'am newline in subject' '
test_tick &&
sed -e "s/second/second \\\n foo/" patch1 >patchnl &&
git am <patchnl >output.out 2>&1 &&
- test_i18ngrep "^Applying: second \\\n foo$" output.out
+ test_grep "^Applying: second \\\n foo$" output.out
'
test_expect_success 'am -q is quiet' '
@@ -916,7 +942,7 @@ test_expect_success 'am --message-id really adds the message id' '
git am --message-id patch1.eml &&
test_path_is_missing .git/rebase-apply &&
git cat-file commit HEAD | tail -n1 >actual &&
- grep Message-Id patch1.eml >expected &&
+ grep Message-ID patch1.eml >expected &&
test_cmp expected actual
'
@@ -928,7 +954,7 @@ test_expect_success 'am.messageid really adds the message id' '
git am patch1.eml &&
test_path_is_missing .git/rebase-apply &&
git cat-file commit HEAD | tail -n1 >actual &&
- grep Message-Id patch1.eml >expected &&
+ grep Message-ID patch1.eml >expected &&
test_cmp expected actual
'
@@ -939,7 +965,7 @@ test_expect_success 'am --message-id -s signs off after the message id' '
git am -s --message-id patch1.eml &&
test_path_is_missing .git/rebase-apply &&
git cat-file commit HEAD | tail -n2 | head -n1 >actual &&
- grep Message-Id patch1.eml >expected &&
+ grep Message-ID patch1.eml >expected &&
test_cmp expected actual
'
@@ -1039,7 +1065,7 @@ test_expect_success 'am --patch-format=mboxrd handles mboxrd' '
>From extra escape for reversibility
INPUT_END
git commit -F msg &&
- git format-patch --pretty=mboxrd --stdout -1 >mboxrd1 &&
+ git -c format.mboxrd format-patch --stdout -1 >mboxrd1 &&
grep "^>From could trip up a loose mbox parser" mboxrd1 &&
git checkout -f first &&
git am --patch-format=mboxrd mboxrd1 &&
@@ -1152,4 +1178,105 @@ test_expect_success 'apply binary blob in partial clone' '
git -C client am ../patch
'
+test_expect_success 'an empty input file is error regardless of --empty option' '
+ test_when_finished "git am --abort || :" &&
+ test_must_fail git am --empty=drop empty.patch 2>actual &&
+ echo "Patch format detection failed." >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'invalid when passing the --empty option alone' '
+ test_when_finished "git am --abort || :" &&
+ git checkout empty-commit^ &&
+ test_must_fail git am --empty empty-commit.patch 2>err &&
+ echo "error: invalid value for '\''--empty'\'': '\''empty-commit.patch'\''" >expected &&
+ test_cmp expected err
+'
+
+test_expect_success 'a message without a patch is an error (default)' '
+ test_when_finished "git am --abort || :" &&
+ test_must_fail git am empty-commit.patch >err &&
+ grep "Patch is empty" err
+'
+
+test_expect_success 'a message without a patch is an error where an explicit "--empty=stop" is given' '
+ test_when_finished "git am --abort || :" &&
+ test_must_fail git am --empty=stop empty-commit.patch >err &&
+ grep "Patch is empty." err
+'
+
+test_expect_success 'a message without a patch will be skipped when "--empty=drop" is given' '
+ git am --empty=drop empty-commit.patch >output &&
+ git rev-parse empty-commit^ >expected &&
+ git rev-parse HEAD >actual &&
+ test_cmp expected actual &&
+ grep "Skipping: empty commit" output
+'
+
+test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
+ git am --empty=keep empty-commit.patch >output &&
+ test_path_is_missing .git/rebase-apply &&
+ git show empty-commit --format="%B" >expected &&
+ git show HEAD --format="%B" >actual &&
+ grep -f actual expected &&
+ grep "Creating an empty commit: empty commit" output
+'
+
+test_expect_success 'skip an empty patch in the middle of an am session' '
+ git checkout empty-commit^ &&
+ test_must_fail git am empty-commit.patch >out 2>err &&
+ grep "Patch is empty." out &&
+ grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
+ git am --skip &&
+ test_path_is_missing .git/rebase-apply &&
+ git rev-parse empty-commit^ >expected &&
+ git rev-parse HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'record an empty patch as an empty commit in the middle of an am session' '
+ git checkout empty-commit^ &&
+ test_must_fail git am empty-commit.patch >out 2>err &&
+ grep "Patch is empty." out &&
+ grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
+ git am --allow-empty >output &&
+ grep "No changes - recorded it as an empty commit." output &&
+ test_path_is_missing .git/rebase-apply &&
+ git show empty-commit --format="%B" >expected &&
+ git show HEAD --format="%B" >actual &&
+ grep -f actual expected
+'
+
+test_expect_success 'create an non-empty commit when the index IS changed though "--allow-empty" is given' '
+ git checkout empty-commit^ &&
+ test_must_fail git am empty-commit.patch >err &&
+ : >empty-file &&
+ git add empty-file &&
+ git am --allow-empty &&
+ git show empty-commit --format="%B" >expected &&
+ git show HEAD --format="%B" >actual &&
+ grep -f actual expected &&
+ git diff HEAD^..HEAD --name-only
+'
+
+test_expect_success 'cannot create empty commits when there is a clean index due to merge conflicts' '
+ test_when_finished "git am --abort || :" &&
+ git rev-parse HEAD >expected &&
+ test_must_fail git am seq.patch &&
+ test_must_fail git am --allow-empty >err &&
+ ! grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
+ git rev-parse HEAD >actual &&
+ test_cmp actual expected
+'
+
+test_expect_success 'cannot create empty commits when there is unmerged index due to merge conflicts' '
+ test_when_finished "git am --abort || :" &&
+ git rev-parse HEAD >expected &&
+ test_must_fail git am -3 seq.patch &&
+ test_must_fail git am --allow-empty >err &&
+ ! grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
+ git rev-parse HEAD >actual &&
+ test_cmp actual expected
+'
+
test_done
diff --git a/t/t4151-am-abort.sh b/t/t4151-am-abort.sh
index 9d8d3c7..edb38da 100755
--- a/t/t4151-am-abort.sh
+++ b/t/t4151-am-abort.sh
@@ -5,10 +5,7 @@ test_description='am --abort'
. ./test-lib.sh
test_expect_success setup '
- for i in a b c d e f g
- do
- echo $i
- done >file-1 &&
+ test_write_lines a b c d e f g >file-1 &&
cp file-1 file-2 &&
test_tick &&
git add file-1 file-2 &&
@@ -23,7 +20,13 @@ test_expect_success setup '
test_tick &&
git commit -a -m $i || return 1
done &&
+ git branch changes &&
git format-patch --no-numbered initial &&
+ git checkout -b conflicting initial &&
+ echo different >>file-1 &&
+ echo whatever >new-file &&
+ git add file-1 new-file &&
+ git commit -m different &&
git checkout -b side initial &&
echo local change >file-2-expect
'
@@ -37,16 +40,13 @@ do
test_must_fail git am$with3 000[1245]-*.patch &&
git log --pretty=tformat:%s >actual &&
- for i in 3 2 initial
- do
- echo $i
- done >expect &&
+ test_write_lines 3 2 initial >expect &&
test_cmp expect actual
'
test_expect_success "am$with3 --skip continue after failed am$with3" '
test_must_fail git am$with3 --skip >output &&
- test_i18ngrep "^Applying: 6$" output &&
+ test_grep "^Applying: 6$" output &&
test_cmp file-2-expect file-2 &&
test ! -f .git/MERGE_RR
'
@@ -191,4 +191,37 @@ test_expect_success 'am --abort leaves index stat info alone' '
git diff-files --exit-code --quiet
'
+test_expect_success 'git am --abort return failed exit status when it fails' '
+ test_when_finished "rm -rf file-2/ && git reset --hard && git am --abort" &&
+ git checkout changes &&
+ git format-patch -1 --stdout conflicting >changes.mbox &&
+ test_must_fail git am --3way changes.mbox &&
+
+ git rm file-2 &&
+ mkdir file-2 &&
+ echo precious >file-2/somefile &&
+ test_must_fail git am --abort &&
+ test_path_is_dir file-2/
+'
+
+test_expect_success 'git am --abort cleans relevant files' '
+ git checkout changes &&
+ git format-patch -1 --stdout conflicting >changes.mbox &&
+ test_must_fail git am --3way changes.mbox &&
+
+ test_path_is_file new-file &&
+ echo further changes >>file-1 &&
+ echo change other file >>file-2 &&
+
+ # Abort, and expect the files touched by am to be reverted
+ git am --abort &&
+
+ test_path_is_missing new-file &&
+
+ # Files not involved in am operation are left modified
+ git diff --name-only changes >actual &&
+ test_write_lines file-2 >expect &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t4152-am-subjects.sh b/t/t4152-am-subjects.sh
index 4c68245..9f2edba 100755
--- a/t/t4152-am-subjects.sh
+++ b/t/t4152-am-subjects.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test subject preservation with format-patch | am'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
make_patches() {
diff --git a/t/t4153-am-resume-override-opts.sh b/t/t4153-am-resume-override-opts.sh
index b7c3861..4add7c7 100755
--- a/t/t4153-am-resume-override-opts.sh
+++ b/t/t4153-am-resume-override-opts.sh
@@ -53,7 +53,7 @@ test_expect_success '--no-quiet overrides --quiet' '
# Applying side1 will be quiet.
test_must_fail git am --quiet side[123].eml >out &&
test_path_is_dir .git/rebase-apply &&
- test_i18ngrep ! "^Applying: " out &&
+ test_grep ! "^Applying: " out &&
echo side1 >file &&
git add file &&
diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh
index 9f8c76d..b0a3e84 100755
--- a/t/t4200-rerere.sh
+++ b/t/t4200-rerere.sh
@@ -365,9 +365,6 @@ test_expect_success 'set up an unresolved merge' '
test_might_fail git config --unset rerere.autoupdate &&
git reset --hard &&
git checkout version2 &&
- fifth=$(git rev-parse fifth) &&
- echo "$fifth branch fifth of ." |
- git fmt-merge-msg >msg &&
ancestor=$(git merge-base version2 fifth) &&
test_must_fail git merge-recursive "$ancestor" -- HEAD fifth &&
@@ -436,13 +433,13 @@ test_expect_success 'rerere --no-no-rerere-autoupdate' '
git update-index --index-info <failedmerge &&
cp file3.conflict file3 &&
test_must_fail git rerere --no-no-rerere-autoupdate 2>err &&
- test_i18ngrep [Uu]sage err &&
+ test_grep [Uu]sage err &&
test_must_fail git update-index --refresh
'
test_expect_success 'rerere -h' '
test_must_fail git rerere -h >help &&
- test_i18ngrep [Uu]sage help
+ test_grep [Uu]sage help
'
concat_insert () {
@@ -674,4 +671,67 @@ test_expect_success 'test simple stage 1 handling' '
)
'
+test_expect_success 'rerere does not crash with missing preimage' '
+ git config rerere.enabled true &&
+
+ echo bar >test &&
+ git add test &&
+ git commit -m "one" &&
+ git branch rerere_no_crash &&
+
+ echo foo >>test &&
+ git add test &&
+ git commit -m "two" &&
+
+ git checkout rerere_no_crash &&
+ echo "bar" >>test &&
+ git add test &&
+ git commit -m "three" &&
+
+ test_must_fail git rebase main &&
+ rm .git/rr-cache/*/preimage &&
+ git rebase --abort
+'
+
+test_expect_success 'rerere does not crash with unmatched conflict marker' '
+ git config rerere.enabled true &&
+
+ echo bar >test &&
+ git add test &&
+ git commit -m "one" &&
+ git branch rerere_no_preimage &&
+
+ cat >test <<-EOF &&
+ test
+ bar
+ foobar
+ EOF
+ git add test &&
+ git commit -m "two" &&
+
+ git checkout rerere_no_preimage &&
+ echo "bar" >>test &&
+ git add test &&
+ git commit -m "three" &&
+
+ cat >test <<-EOF &&
+ foobar
+ bar
+ bar
+ EOF
+ git add test &&
+ git commit -m "four" &&
+
+ test_must_fail git rebase main &&
+ cat >test <<-EOF &&
+ test
+ bar
+ <<<<<<< HEAD
+ foobar
+ bar
+ EOF
+ git add test &&
+ test_must_fail git rebase --continue
+'
+
test_done
diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh
index 3095b1b..f698d0c 100755
--- a/t/t4201-shortlog.sh
+++ b/t/t4201-shortlog.sh
@@ -83,6 +83,13 @@ test_expect_success 'pretty format' '
test_cmp expect log.predictable
'
+test_expect_success 'pretty format (with --date)' '
+ sed "s/SUBJECT/2005-04-07 OBJECT_NAME/" expect.template >expect &&
+ git shortlog --format="%ad %H" --date=short HEAD >log &&
+ fuzz log >log.predictable &&
+ test_cmp expect log.predictable
+'
+
test_expect_success '--abbrev' '
sed s/SUBJECT/OBJID/ expect.template >expect &&
git shortlog --format="%h" --abbrev=35 HEAD >log &&
@@ -132,7 +139,7 @@ test_expect_success !MINGW 'shortlog can read --format=raw output' '
test_expect_success 'shortlog from non-git directory refuses extra arguments' '
test_must_fail env GIT_DIR=non-existing git shortlog foo 2>out &&
- test_i18ngrep "too many arguments" out
+ test_grep "too many arguments" out
'
test_expect_success 'shortlog should add newline when input line matches wraplen' '
@@ -237,6 +244,26 @@ test_expect_success 'shortlog --group=trailer:signed-off-by' '
test_cmp expect actual
'
+test_expect_success 'shortlog --group=format' '
+ git shortlog -s --date="format:%Y" --group="format:%cN (%cd)" \
+ HEAD >actual &&
+ cat >expect <<-\EOF &&
+ 4 C O Mitter (2005)
+ 1 Sin Nombre (2005)
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'shortlog --group=<format> DWIM' '
+ git shortlog -s --date="format:%Y" --group="%cN (%cd)" HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'shortlog bogus --group' '
+ test_must_fail git shortlog --group=bogus HEAD 2>err &&
+ grep "unknown group type" err
+'
+
test_expect_success 'trailer idents are split' '
cat >expect <<-\EOF &&
2 C O Mitter
@@ -285,6 +312,38 @@ test_expect_success 'shortlog de-duplicates trailers in a single commit' '
test_cmp expect actual
'
+# Trailers that have unfolded (single line) and folded (multiline) values which
+# are otherwise identical are treated as the same trailer for de-duplication.
+test_expect_success 'shortlog de-duplicates trailers in a single commit (folded/unfolded values)' '
+ git commit --allow-empty -F - <<-\EOF &&
+ subject one
+
+ this message has two distinct values, plus a repeat (folded)
+
+ Repeated-trailer: Foo foo foo
+ Repeated-trailer: Bar
+ Repeated-trailer: Foo
+ foo foo
+ EOF
+
+ git commit --allow-empty -F - <<-\EOF &&
+ subject two
+
+ similar to the previous, but without the second distinct value
+
+ Repeated-trailer: Foo foo foo
+ Repeated-trailer: Foo
+ foo foo
+ EOF
+
+ cat >expect <<-\EOF &&
+ 2 Foo foo foo
+ 1 Bar
+ EOF
+ git shortlog -ns --group=trailer:repeated-trailer -2 HEAD >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'shortlog can match multiple groups' '
git commit --allow-empty -F - <<-\EOF &&
subject one
@@ -319,6 +378,18 @@ test_expect_success 'shortlog can match multiple groups' '
test_cmp expect actual
'
+test_expect_success 'shortlog can match multiple format groups' '
+ GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME" \
+ git commit --allow-empty -m "identical names" &&
+ test_tick &&
+ cat >expect <<-\EOF &&
+ 2 A U Thor
+ 1 C O Mitter
+ EOF
+ git shortlog -ns --group="%cn" --group="%an" -2 HEAD >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'set up option selection tests' '
git commit --allow-empty -F - <<-\EOF
subject
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index 9dfead9..60fe60d 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -120,48 +120,61 @@ test_expect_success 'diff-filter=A' '
test_expect_success 'diff-filter=M' '
- actual=$(git log --pretty="format:%s" --diff-filter=M HEAD) &&
- expect=$(echo second) &&
- verbose test "$actual" = "$expect"
+ git log --pretty="format:%s" --diff-filter=M HEAD >actual &&
+ printf "second" >expect &&
+ test_cmp expect actual
'
test_expect_success 'diff-filter=D' '
- actual=$(git log --no-renames --pretty="format:%s" --diff-filter=D HEAD) &&
- expect=$(echo sixth ; echo third) &&
- verbose test "$actual" = "$expect"
+ git log --no-renames --pretty="format:%s" --diff-filter=D HEAD >actual &&
+ printf "sixth\nthird" >expect &&
+ test_cmp expect actual
'
test_expect_success 'diff-filter=R' '
- actual=$(git log -M --pretty="format:%s" --diff-filter=R HEAD) &&
- expect=$(echo third) &&
- verbose test "$actual" = "$expect"
+ git log -M --pretty="format:%s" --diff-filter=R HEAD >actual &&
+ printf "third" >expect &&
+ test_cmp expect actual
+
+'
+
+test_expect_success 'multiple --diff-filter bits' '
+
+ git log -M --pretty="format:%s" --diff-filter=R HEAD >expect &&
+ git log -M --pretty="format:%s" --diff-filter=Ra HEAD >actual &&
+ test_cmp expect actual &&
+ git log -M --pretty="format:%s" --diff-filter=aR HEAD >actual &&
+ test_cmp expect actual &&
+ git log -M --pretty="format:%s" \
+ --diff-filter=a --diff-filter=R HEAD >actual &&
+ test_cmp expect actual
'
test_expect_success 'diff-filter=C' '
- actual=$(git log -C -C --pretty="format:%s" --diff-filter=C HEAD) &&
- expect=$(echo fourth) &&
- verbose test "$actual" = "$expect"
+ git log -C -C --pretty="format:%s" --diff-filter=C HEAD >actual &&
+ printf "fourth" >expect &&
+ test_cmp expect actual
'
test_expect_success 'git log --follow' '
- actual=$(git log --follow --pretty="format:%s" ichi) &&
- expect=$(echo third ; echo second ; echo initial) &&
- verbose test "$actual" = "$expect"
+ git log --follow --pretty="format:%s" ichi >actual &&
+ printf "third\nsecond\ninitial" >expect &&
+ test_cmp expect actual
'
test_expect_success 'git config log.follow works like --follow' '
test_config log.follow true &&
- actual=$(git log --pretty="format:%s" ichi) &&
- expect=$(echo third ; echo second ; echo initial) &&
- verbose test "$actual" = "$expect"
+ git log --pretty="format:%s" ichi >actual &&
+ printf "third\nsecond\ninitial" >expect &&
+ test_cmp expect actual
'
test_expect_success 'git config log.follow does not die with multiple paths' '
@@ -174,11 +187,26 @@ test_expect_success 'git config log.follow does not die with no paths' '
git log --
'
+test_expect_success 'git log --follow rejects unsupported pathspec magic' '
+ test_must_fail git log --follow ":(top,glob,icase)ichi" 2>stderr &&
+ # check full error message; we want to be sure we mention both
+ # of the rejected types (glob,icase), but not the allowed one (top)
+ echo "fatal: pathspec magic not supported by --follow: ${SQ}glob${SQ}, ${SQ}icase${SQ}" >expect &&
+ test_cmp expect stderr
+'
+
+test_expect_success 'log.follow disabled with unsupported pathspec magic' '
+ test_config log.follow true &&
+ git log --format=%s ":(glob,icase)ichi" >actual &&
+ echo third >expect &&
+ test_cmp expect actual
+'
+
test_expect_success 'git config log.follow is overridden by --no-follow' '
test_config log.follow true &&
- actual=$(git log --no-follow --pretty="format:%s" ichi) &&
- expect="third" &&
- verbose test "$actual" = "$expect"
+ git log --no-follow --pretty="format:%s" ichi >actual &&
+ printf "third" >expect &&
+ test_cmp expect actual
'
# Note that these commits are intentionally listed out of order.
@@ -236,6 +264,15 @@ test_expect_success 'log --grep' '
test_cmp expect actual
'
+for noop_opt in --invert-grep --all-match
+do
+ test_expect_success "log $noop_opt without --grep is a NOOP" '
+ git log >expect &&
+ git log $noop_opt >actual &&
+ test_cmp expect actual
+ '
+done
+
cat > expect << EOF
second
initial
@@ -250,7 +287,7 @@ test_expect_success 'log --invert-grep --grep' '
test_cmp expect actual &&
# POSIX extended
- git -c grep.patternType=basic log --pretty="tformat:%s" --invert-grep --grep=t[h] --grep=S[e]c >actual &&
+ git -c grep.patternType=extended log --pretty="tformat:%s" --invert-grep --grep=t[h] --grep=S[e]c >actual &&
test_cmp expect actual &&
# PCRE
@@ -449,6 +486,80 @@ test_expect_success !FAIL_PREREQS 'log with various grep.patternType configurati
)
'
+for cmd in show whatchanged reflog format-patch
+do
+ case "$cmd" in
+ format-patch) myarg="HEAD~.." ;;
+ *) myarg= ;;
+ esac
+
+ test_expect_success "$cmd: understands grep.patternType, like 'log'" '
+ git init "pattern-type-$cmd" &&
+ (
+ cd "pattern-type-$cmd" &&
+ test_commit 1 file A &&
+ test_commit "(1|2)" file B 2 &&
+
+ git -c grep.patternType=fixed $cmd --grep="..." $myarg >actual &&
+ test_must_be_empty actual &&
+
+ git -c grep.patternType=basic $cmd --grep="..." $myarg >actual &&
+ test_file_not_empty actual
+ )
+ '
+done
+
+test_expect_success 'log --author' '
+ cat >expect <<-\EOF &&
+ Author: <BOLD;RED>A U<RESET> Thor <author@example.com>
+ EOF
+ git log -1 --color=always --author="A U" >log &&
+ grep Author log >actual.raw &&
+ test_decode_color <actual.raw >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log --committer' '
+ cat >expect <<-\EOF &&
+ Commit: C O Mitter <committer@<BOLD;RED>example<RESET>.com>
+ EOF
+ git log -1 --color=always --pretty=fuller --committer="example" >log &&
+ grep "Commit:" log >actual.raw &&
+ test_decode_color <actual.raw >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log -i --grep with color' '
+ cat >expect <<-\EOF &&
+ <BOLD;RED>Sec<RESET>ond
+ <BOLD;RED>sec<RESET>ond
+ EOF
+ git log --color=always -i --grep=^sec >log &&
+ grep -i sec log >actual.raw &&
+ test_decode_color <actual.raw >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '-c color.grep.selected log --grep' '
+ cat >expect <<-\EOF &&
+ <GREEN>th<RESET><BOLD;RED>ir<RESET><GREEN>d<RESET>
+ EOF
+ git -c color.grep.selected="green" log --color=always --grep=ir >log &&
+ grep ir log >actual.raw &&
+ test_decode_color <actual.raw >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '-c color.grep.matchSelected log --grep' '
+ cat >expect <<-\EOF &&
+ <BLUE>i<RESET>n<BLUE>i<RESET>t<BLUE>i<RESET>al
+ EOF
+ git -c color.grep.matchSelected="blue" log --color=always --grep=i >log &&
+ grep al log >actual.raw &&
+ test_decode_color <actual.raw >actual &&
+ test_cmp expect actual
+'
+
cat > expect <<EOF
* Second
* sixth
@@ -608,7 +719,7 @@ EOF
test_expect_success 'log --graph with full output' '
git log --graph --date-order --pretty=short |
- git name-rev --name-only --stdin |
+ git name-rev --name-only --annotate-stdin |
sed "s/Merge:.*/Merge: A B/;s/ *\$//" >actual &&
test_cmp expect actual
'
@@ -617,9 +728,12 @@ test_expect_success 'set up more tangled history' '
git checkout -b tangle HEAD~6 &&
test_commit tangle-a tangle-a a &&
git merge main~3 &&
+ git update-ref refs/prefetch/merge HEAD &&
git merge side~1 &&
+ git update-ref refs/rewritten/merge HEAD &&
git checkout main &&
git merge tangle &&
+ git update-ref refs/hidden/tangle HEAD &&
git checkout -b reach &&
test_commit reach &&
git checkout main &&
@@ -736,6 +850,21 @@ test_expect_success 'log.decorate configuration' '
'
+test_expect_success 'parse log.excludeDecoration with no value' '
+ cp .git/config .git/config.orig &&
+ test_when_finished mv .git/config.orig .git/config &&
+
+ cat >>.git/config <<-\EOF &&
+ [log]
+ excludeDecoration
+ EOF
+ cat >expect <<-\EOF &&
+ error: missing value for '\''log.excludeDecoration'\''
+ EOF
+ git log --decorate=short 2>actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'decorate-refs with glob' '
cat >expect.decorate <<-\EOF &&
Merge-tag-reach
@@ -887,9 +1016,9 @@ test_expect_success 'decorate-refs-exclude and simplify-by-decoration' '
Merge-tag-reach (HEAD -> main)
reach (tag: reach, reach)
seventh (tag: seventh)
- Merge-branch-tangle
- Merge-branch-side-early-part-into-tangle (tangle)
- tangle-a (tag: tangle-a)
+ Merge-branch-tangle (refs/hidden/tangle)
+ Merge-branch-side-early-part-into-tangle (refs/rewritten/merge, tangle)
+ Merge-branch-main-early-part-into-tangle (refs/prefetch/merge)
EOF
git log -n6 --decorate=short --pretty="tformat:%f%d" \
--decorate-refs-exclude="*octopus*" \
@@ -901,6 +1030,152 @@ test_expect_success 'decorate-refs-exclude and simplify-by-decoration' '
test_cmp expect.decorate actual
'
+test_expect_success 'decorate-refs with implied decorate from format' '
+ cat >expect <<-\EOF &&
+ side-2 (tag: side-2)
+ side-1
+ EOF
+ git log --no-walk --format="%s%d" \
+ --decorate-refs="*side-2" side-1 side-2 \
+ >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'implied decorate does not override option' '
+ cat >expect <<-\EOF &&
+ side-2 (tag: refs/tags/side-2, refs/heads/side)
+ side-1 (tag: refs/tags/side-1)
+ EOF
+ git log --no-walk --format="%s%d" \
+ --decorate=full side-1 side-2 \
+ >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'decorate-refs and simplify-by-decoration without output' '
+ cat >expect <<-\EOF &&
+ side-2
+ initial
+ EOF
+ # Do not just use a --format without %d here; we want to
+ # make sure that we did not accidentally turn on displaying
+ # the decorations, too. And that requires one of the regular
+ # formats.
+ git log --decorate-refs="*side-2" --oneline \
+ --simplify-by-decoration >actual.raw &&
+ sed "s/^[0-9a-f]* //" <actual.raw >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'decorate-refs-exclude HEAD' '
+ git log --decorate=full --oneline \
+ --decorate-refs-exclude="HEAD" >actual &&
+ ! grep HEAD actual
+'
+
+test_expect_success 'decorate-refs focus from default' '
+ git log --decorate=full --oneline \
+ --decorate-refs="refs/heads" >actual &&
+ ! grep HEAD actual
+'
+
+test_expect_success '--clear-decorations overrides defaults' '
+ cat >expect.default <<-\EOF &&
+ Merge-tag-reach (HEAD -> refs/heads/main)
+ Merge-tags-octopus-a-and-octopus-b
+ seventh (tag: refs/tags/seventh)
+ octopus-b (tag: refs/tags/octopus-b, refs/heads/octopus-b)
+ octopus-a (tag: refs/tags/octopus-a, refs/heads/octopus-a)
+ reach (tag: refs/tags/reach, refs/heads/reach)
+ Merge-branch-tangle
+ Merge-branch-side-early-part-into-tangle (refs/heads/tangle)
+ Merge-branch-main-early-part-into-tangle
+ tangle-a (tag: refs/tags/tangle-a)
+ Merge-branch-side
+ side-2 (tag: refs/tags/side-2, refs/heads/side)
+ side-1 (tag: refs/tags/side-1)
+ Second
+ sixth
+ fifth
+ fourth
+ third
+ second
+ initial
+ EOF
+ git log --decorate=full --pretty="tformat:%f%d" >actual &&
+ test_cmp expect.default actual &&
+
+ cat >expect.all <<-\EOF &&
+ Merge-tag-reach (HEAD -> refs/heads/main)
+ Merge-tags-octopus-a-and-octopus-b
+ seventh (tag: refs/tags/seventh)
+ octopus-b (tag: refs/tags/octopus-b, refs/heads/octopus-b)
+ octopus-a (tag: refs/tags/octopus-a, refs/heads/octopus-a)
+ reach (tag: refs/tags/reach, refs/heads/reach)
+ Merge-branch-tangle (refs/hidden/tangle)
+ Merge-branch-side-early-part-into-tangle (refs/rewritten/merge, refs/heads/tangle)
+ Merge-branch-main-early-part-into-tangle (refs/prefetch/merge)
+ tangle-a (tag: refs/tags/tangle-a)
+ Merge-branch-side
+ side-2 (tag: refs/tags/side-2, refs/heads/side)
+ side-1 (tag: refs/tags/side-1)
+ Second
+ sixth
+ fifth
+ fourth
+ third
+ second
+ initial
+ EOF
+ git log --decorate=full --pretty="tformat:%f%d" \
+ --clear-decorations >actual &&
+ test_cmp expect.all actual &&
+ git -c log.initialDecorationSet=all log \
+ --decorate=full --pretty="tformat:%f%d" >actual &&
+ test_cmp expect.all actual
+'
+
+test_expect_success '--clear-decorations clears previous exclusions' '
+ cat >expect.all <<-\EOF &&
+ Merge-tag-reach (HEAD -> refs/heads/main)
+ reach (tag: refs/tags/reach, refs/heads/reach)
+ Merge-tags-octopus-a-and-octopus-b
+ octopus-b (tag: refs/tags/octopus-b, refs/heads/octopus-b)
+ octopus-a (tag: refs/tags/octopus-a, refs/heads/octopus-a)
+ seventh (tag: refs/tags/seventh)
+ Merge-branch-tangle (refs/hidden/tangle)
+ Merge-branch-side-early-part-into-tangle (refs/rewritten/merge, refs/heads/tangle)
+ Merge-branch-main-early-part-into-tangle (refs/prefetch/merge)
+ tangle-a (tag: refs/tags/tangle-a)
+ side-2 (tag: refs/tags/side-2, refs/heads/side)
+ side-1 (tag: refs/tags/side-1)
+ initial
+ EOF
+
+ git log --decorate=full --pretty="tformat:%f%d" \
+ --simplify-by-decoration \
+ --decorate-refs-exclude="heads/octopus*" \
+ --decorate-refs="heads" \
+ --clear-decorations >actual &&
+ test_cmp expect.all actual &&
+
+ cat >expect.filtered <<-\EOF &&
+ Merge-tags-octopus-a-and-octopus-b
+ octopus-b (refs/heads/octopus-b)
+ octopus-a (refs/heads/octopus-a)
+ initial
+ EOF
+
+ git log --decorate=full --pretty="tformat:%f%d" \
+ --simplify-by-decoration \
+ --decorate-refs-exclude="heads/octopus" \
+ --decorate-refs="heads" \
+ --clear-decorations \
+ --decorate-refs-exclude="tags/" \
+ --decorate-refs="heads/octopus*" >actual &&
+ test_cmp expect.filtered actual
+'
+
test_expect_success 'log.decorate config parsing' '
git log --oneline --decorate=full >expect.full &&
git log --oneline --decorate=short >expect.short &&
@@ -1583,6 +1858,75 @@ test_expect_success 'log --graph with --name-only' '
test_cmp_graph --name-only tangle..reach
'
+test_expect_success '--no-graph countermands --graph' '
+ git log >expect &&
+ git log --graph --no-graph >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--graph countermands --no-graph' '
+ git log --graph >expect &&
+ git log --no-graph --graph >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--no-graph does not unset --topo-order' '
+ git log --topo-order >expect &&
+ git log --topo-order --no-graph >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--no-graph does not unset --parents' '
+ git log --parents >expect &&
+ git log --parents --no-graph >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--reverse and --graph conflict' '
+ test_must_fail git log --reverse --graph 2>stderr &&
+ test_grep "cannot be used together" stderr
+'
+
+test_expect_success '--reverse --graph --no-graph works' '
+ git log --reverse >expect &&
+ git log --reverse --graph --no-graph >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--show-linear-break and --graph conflict' '
+ test_must_fail git log --show-linear-break --graph 2>stderr &&
+ test_grep "cannot be used together" stderr
+'
+
+test_expect_success '--show-linear-break --graph --no-graph works' '
+ git log --show-linear-break >expect &&
+ git log --show-linear-break --graph --no-graph >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--no-walk and --graph conflict' '
+ test_must_fail git log --no-walk --graph 2>stderr &&
+ test_grep "cannot be used together" stderr
+'
+
+test_expect_success '--no-walk --graph --no-graph works' '
+ git log --no-walk >expect &&
+ git log --no-walk --graph --no-graph >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--walk-reflogs and --graph conflict' '
+ test_must_fail git log --walk-reflogs --graph 2>stderr &&
+ (test_grep "cannot combine" stderr ||
+ test_grep "cannot be used together" stderr)
+'
+
+test_expect_success '--walk-reflogs --graph --no-graph works' '
+ git log --walk-reflogs >expect &&
+ git log --walk-reflogs --graph --no-graph >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'dotdot is a parent directory' '
mkdir -p a/b &&
( echo sixth && echo fifth ) >expect &&
@@ -1616,6 +1960,34 @@ test_expect_success GPGSM 'setup signed branch x509' '
git commit -S -m signed_commit
'
+test_expect_success GPGSSH 'setup sshkey signed branch' '
+ test_config gpg.format ssh &&
+ test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+ test_when_finished "git reset --hard && git checkout main" &&
+ git checkout -b signed-ssh main &&
+ echo foo >foo &&
+ git add foo &&
+ git commit -S -m signed_commit
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'create signed commits with keys having defined lifetimes' '
+ test_config gpg.format ssh &&
+ touch file &&
+ git add file &&
+
+ echo expired >file && test_tick && git commit -a -m expired -S"${GPGSSH_KEY_EXPIRED}" &&
+ git tag expired-signed &&
+
+ echo notyetvalid >file && test_tick && git commit -a -m notyetvalid -S"${GPGSSH_KEY_NOTYETVALID}" &&
+ git tag notyetvalid-signed &&
+
+ echo timeboxedvalid >file && test_tick && git commit -a -m timeboxedvalid -S"${GPGSSH_KEY_TIMEBOXEDVALID}" &&
+ git tag timeboxedvalid-signed &&
+
+ echo timeboxedinvalid >file && test_tick && git commit -a -m timeboxedinvalid -S"${GPGSSH_KEY_TIMEBOXEDINVALID}" &&
+ git tag timeboxedinvalid-signed
+'
+
test_expect_success GPGSM 'log x509 fingerprint' '
echo "F8BF62E0693D0694816377099909C779FA23FD65 | " >expect &&
git log -n1 --format="%GF | %GP" signed-x509 >actual &&
@@ -1628,6 +2000,13 @@ test_expect_success GPGSM 'log OpenPGP fingerprint' '
test_cmp expect actual
'
+test_expect_success GPGSSH 'log ssh key fingerprint' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2\" | \"}" >expect &&
+ git log -n1 --format="%GF | %GP" signed-ssh >actual &&
+ test_cmp expect actual
+'
+
test_expect_success GPG 'log --graph --show-signature' '
git log --graph --show-signature -n1 signed >actual &&
grep "^| gpg: Signature made" actual &&
@@ -1640,6 +2019,37 @@ test_expect_success GPGSM 'log --graph --show-signature x509' '
grep "^| gpgsm: Good signature" actual
'
+test_expect_success GPGSSH 'log --graph --show-signature ssh' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git log --graph --show-signature -n1 signed-ssh >actual &&
+ grep "${GOOD_SIGNATURE_TRUSTED}" actual
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'log shows failure on expired signature key' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git log --graph --show-signature -n1 expired-signed >actual &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'log shows failure on not yet valid signature key' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git log --graph --show-signature -n1 notyetvalid-signed >actual &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'log show success with commit date and key validity matching' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git log --graph --show-signature -n1 timeboxedvalid-signed >actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'log shows failure with commit date outside of key validity' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git log --graph --show-signature -n1 timeboxedinvalid-signed >actual &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
+'
+
test_expect_success GPG 'log --graph --show-signature for merged tag' '
test_when_finished "git reset --hard && git checkout main" &&
git checkout -b plain main &&
@@ -1733,10 +2143,13 @@ test_expect_success GPG 'log --show-signature for merged tag with GPG failure' '
git tag -s -m signed_tag_msg signed_tag_fail &&
git checkout plain-fail &&
git merge --no-ff -m msg signed_tag_fail &&
- TMPDIR="$(pwd)/bogus" git log --show-signature -n1 plain-fail >actual &&
- grep "^merged tag" actual &&
- grep "^No signature" actual &&
- ! grep "^gpg: Signature made" actual
+ if ! test_have_prereq VALGRIND
+ then
+ TMPDIR="$(pwd)/bogus" git log --show-signature -n1 plain-fail >actual &&
+ grep "^merged tag" actual &&
+ grep "^No signature" actual &&
+ ! grep "^gpg: Signature made" actual
+ fi
'
test_expect_success GPGSM 'log --graph --show-signature for merged tag x509' '
@@ -1777,7 +2190,8 @@ test_expect_success GPGSM 'log --graph --show-signature for merged tag x509 miss
git merge --no-ff -m msg signed_tag_x509_nokey &&
GNUPGHOME=. git log --graph --show-signature -n1 plain-x509-nokey >actual &&
grep "^|\\\ merged tag" actual &&
- grep "^| | gpgsm: certificate not found" actual
+ grep -e "^| | gpgsm: certificate not found" \
+ -e "^| | gpgsm: failed to find the certificate: Not found" actual
'
test_expect_success GPGSM 'log --graph --show-signature for merged tag x509 bad signature' '
@@ -1838,24 +2252,7 @@ test_expect_success 'log on empty repo fails' '
git init empty &&
test_when_finished "rm -rf empty" &&
test_must_fail git -C empty log 2>stderr &&
- test_i18ngrep does.not.have.any.commits stderr
-'
-
-test_expect_success REFFILES 'log diagnoses bogus HEAD hash' '
- git init empty &&
- test_when_finished "rm -rf empty" &&
- echo 1234abcd >empty/.git/refs/heads/main &&
- test_must_fail git -C empty log 2>stderr &&
- test_i18ngrep broken stderr
-'
-
-test_expect_success 'log diagnoses bogus HEAD symref' '
- git init empty &&
- git --git-dir empty/.git symbolic-ref HEAD refs/heads/invalid.lock &&
- test_must_fail git -C empty log 2>stderr &&
- test_i18ngrep broken stderr &&
- test_must_fail git -C empty log --default totally-bogus 2>stderr &&
- test_i18ngrep broken stderr
+ test_grep does.not.have.any.commits stderr
'
test_expect_success 'log does not default to HEAD when rev input is given' '
@@ -1929,11 +2326,44 @@ test_expect_success 'log --decorate includes all levels of tag annotated tags' '
test_cmp expect actual
'
+test_expect_success 'log --decorate does not include things outside filter' '
+ reflist="refs/prefetch refs/rebase-merge refs/bundle" &&
+
+ for ref in $reflist
+ do
+ git update-ref $ref/fake HEAD || return 1
+ done &&
+
+ git log --decorate=full --oneline >actual &&
+
+ # None of the refs are visible:
+ ! grep /fake actual
+'
+
test_expect_success 'log --end-of-options' '
- git update-ref refs/heads/--source HEAD &&
- git log --end-of-options --source >actual &&
- git log >expect &&
- test_cmp expect actual
+ git update-ref refs/heads/--source HEAD &&
+ git log --end-of-options --source >actual &&
+ git log >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'set up commits with different authors' '
+ git checkout --orphan authors &&
+ test_commit --author "Jim <jim@example.com>" jim_1 &&
+ test_commit --author "Val <val@example.com>" val_1 &&
+ test_commit --author "Val <val@example.com>" val_2 &&
+ test_commit --author "Jim <jim@example.com>" jim_2 &&
+ test_commit --author "Val <val@example.com>" val_3 &&
+ test_commit --author "Jim <jim@example.com>" jim_3
+'
+
+test_expect_success 'log --invert-grep --grep --author' '
+ cat >expect <<-\EOF &&
+ val_3
+ val_1
+ EOF
+ git log --format=%s --author=Val --grep 2 --invert-grep >actual &&
+ test_cmp expect actual
'
test_done
diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh
index 0b2d21e..8a88dd7 100755
--- a/t/t4203-mailmap.sh
+++ b/t/t4203-mailmap.sh
@@ -360,7 +360,7 @@ test_expect_success 'mailmap.blob might be the wrong type' '
cp default.map .mailmap &&
git -c mailmap.blob=HEAD: shortlog HEAD >actual 2>err &&
- test_i18ngrep "mailmap is not a blob" err &&
+ test_grep "mailmap is not a blob" err &&
test_cmp expect actual
'
@@ -466,7 +466,7 @@ test_expect_success 'gitmailmap(5) example output: example #1' '
Author Jane Doe <jane@laptop.(none)> maps to Jane Doe <jane@laptop.(none)>
Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
- Author Jane D <jane@desktop.(none)> maps to Jane Doe <jane@desktop.(none)>
+ Author Jane D. <jane@desktop.(none)> maps to Jane Doe <jane@desktop.(none)>
Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
EOF
git -C doc log --reverse --pretty=format:"Author %an <%ae> maps to %aN <%aE>%nCommitter %cn <%ce> maps to %cN <%cE>%n" >actual &&
@@ -494,7 +494,7 @@ test_expect_success 'gitmailmap(5) example output: example #2' '
Author Jane Doe <jane@laptop.(none)> maps to Jane Doe <jane@example.com>
Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
- Author Jane D <jane@desktop.(none)> maps to Jane Doe <jane@example.com>
+ Author Jane D. <jane@desktop.(none)> maps to Jane Doe <jane@example.com>
Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
EOF
git -C doc log --reverse --pretty=format:"Author %an <%ae> maps to %aN <%aE>%nCommitter %cn <%ce> maps to %cN <%cE>%n" >actual &&
@@ -963,4 +963,128 @@ test_expect_success SYMLINKS 'symlinks not respected in-tree' '
test_cmp expect actual
'
+test_expect_success 'prepare for cat-file --mailmap' '
+ rm -f .mailmap &&
+ git commit --allow-empty -m foo --author="Orig <orig@example.com>"
+'
+
+test_expect_success '--no-use-mailmap disables mailmap in cat-file' '
+ test_when_finished "rm .mailmap" &&
+ cat >.mailmap <<-EOF &&
+ A U Thor <author@example.com> Orig <orig@example.com>
+ EOF
+ cat >expect <<-EOF &&
+ author Orig <orig@example.com>
+ EOF
+ git cat-file --no-use-mailmap commit HEAD >log &&
+ sed -n "/^author /s/\([^>]*>\).*/\1/p" log >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--use-mailmap enables mailmap in cat-file' '
+ test_when_finished "rm .mailmap" &&
+ cat >.mailmap <<-EOF &&
+ A U Thor <author@example.com> Orig <orig@example.com>
+ EOF
+ cat >expect <<-EOF &&
+ author A U Thor <author@example.com>
+ EOF
+ git cat-file --use-mailmap commit HEAD >log &&
+ sed -n "/^author /s/\([^>]*>\).*/\1/p" log >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--no-mailmap disables mailmap in cat-file for annotated tag objects' '
+ test_when_finished "rm .mailmap" &&
+ cat >.mailmap <<-EOF &&
+ Orig <orig@example.com> C O Mitter <committer@example.com>
+ EOF
+ cat >expect <<-EOF &&
+ tagger C O Mitter <committer@example.com>
+ EOF
+ git tag -a -m "annotated tag" v1 &&
+ git cat-file --no-mailmap -p v1 >log &&
+ sed -n "/^tagger /s/\([^>]*>\).*/\1/p" log >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--mailmap enables mailmap in cat-file for annotated tag objects' '
+ test_when_finished "rm .mailmap" &&
+ cat >.mailmap <<-EOF &&
+ Orig <orig@example.com> C O Mitter <committer@example.com>
+ EOF
+ cat >expect <<-EOF &&
+ tagger Orig <orig@example.com>
+ EOF
+ git tag -a -m "annotated tag" v2 &&
+ git cat-file --mailmap -p v2 >log &&
+ sed -n "/^tagger /s/\([^>]*>\).*/\1/p" log >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file -s returns correct size with --use-mailmap' '
+ test_when_finished "rm .mailmap" &&
+ cat >.mailmap <<-\EOF &&
+ C O Mitter <committer@example.com> Orig <orig@example.com>
+ EOF
+ git cat-file commit HEAD >commit.out &&
+ echo $(wc -c <commit.out) >expect &&
+ git cat-file --use-mailmap commit HEAD >commit.out &&
+ echo $(wc -c <commit.out) >>expect &&
+ git cat-file -s HEAD >actual &&
+ git cat-file --use-mailmap -s HEAD >>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file -s returns correct size with --use-mailmap for tag objects' '
+ test_when_finished "rm .mailmap" &&
+ cat >.mailmap <<-\EOF &&
+ Orig <orig@example.com> C O Mitter <committer@example.com>
+ EOF
+ git tag -a -m "annotated tag" v3 &&
+ git cat-file tag v3 >tag.out &&
+ echo $(wc -c <tag.out) >expect &&
+ git cat-file --use-mailmap tag v3 >tag.out &&
+ echo $(wc -c <tag.out) >>expect &&
+ git cat-file -s v3 >actual &&
+ git cat-file --use-mailmap -s v3 >>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check returns correct size with --use-mailmap' '
+ test_when_finished "rm .mailmap" &&
+ cat >.mailmap <<-\EOF &&
+ C O Mitter <committer@example.com> Orig <orig@example.com>
+ EOF
+ git cat-file commit HEAD >commit.out &&
+ commit_size=$(wc -c <commit.out) &&
+ commit_sha=$(git rev-parse HEAD) &&
+ echo $commit_sha commit $commit_size >expect &&
+ git cat-file --use-mailmap commit HEAD >commit.out &&
+ commit_size=$(wc -c <commit.out) &&
+ echo $commit_sha commit $commit_size >>expect &&
+ echo "HEAD" >in &&
+ git cat-file --batch-check <in >actual &&
+ git cat-file --use-mailmap --batch-check <in >>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-command returns correct size with --use-mailmap' '
+ test_when_finished "rm .mailmap" &&
+ cat >.mailmap <<-\EOF &&
+ C O Mitter <committer@example.com> Orig <orig@example.com>
+ EOF
+ git cat-file commit HEAD >commit.out &&
+ commit_size=$(wc -c <commit.out) &&
+ commit_sha=$(git rev-parse HEAD) &&
+ echo $commit_sha commit $commit_size >expect &&
+ git cat-file --use-mailmap commit HEAD >commit.out &&
+ commit_size=$(wc -c <commit.out) &&
+ echo $commit_sha commit $commit_size >>expect &&
+ echo "info HEAD" >in &&
+ git cat-file --batch-command <in >actual &&
+ git cat-file --use-mailmap --batch-command <in >>actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t4204-patch-id.sh b/t/t4204-patch-id.sh
index f120857..a7fa94c 100755
--- a/t/t4204-patch-id.sh
+++ b/t/t4204-patch-id.sh
@@ -8,13 +8,13 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
test_expect_success 'setup' '
- as="a a a a a a a a" && # eight a
- test_write_lines $as >foo &&
- test_write_lines $as >bar &&
+ str="ab cd ef gh ij kl mn op" &&
+ test_write_lines $str >foo &&
+ test_write_lines $str >bar &&
git add foo bar &&
git commit -a -m initial &&
- test_write_lines $as b >foo &&
- test_write_lines $as b >bar &&
+ test_write_lines $str b >foo &&
+ test_write_lines $str b >bar &&
git commit -a -m first &&
git checkout -b same main &&
git commit --amend -m same-msg &&
@@ -22,12 +22,28 @@ test_expect_success 'setup' '
echo c >foo &&
echo c >bar &&
git commit --amend -a -m notsame-msg &&
+ git checkout -b with_space main~ &&
+ cat >foo <<-\EOF &&
+ a b
+ c d
+ e f
+ g h
+ i j
+ k l
+ m n
+ op
+ EOF
+ cp foo bar &&
+ git add foo bar &&
+ git commit --amend -m "with spaces" &&
test_write_lines bar foo >bar-then-foo &&
test_write_lines foo bar >foo-then-bar
+
'
test_expect_success 'patch-id output is well-formed' '
- git log -p -1 | git patch-id >output &&
+ git log -p -1 >log.output &&
+ git patch-id <log.output >output &&
grep "^$OID_REGEX $(git rev-parse HEAD)$" output
'
@@ -35,17 +51,18 @@ test_expect_success 'patch-id output is well-formed' '
calc_patch_id () {
patch_name="$1"
shift
- git patch-id "$@" |
- sed "s/ .*//" >patch-id_"$patch_name" &&
- test_line_count -gt 0 patch-id_"$patch_name"
+ git patch-id "$@" >patch-id.output &&
+ sed "s/ .*//" patch-id.output >patch-id_"$patch_name" &&
+ test_line_count -eq 1 patch-id_"$patch_name"
}
get_top_diff () {
- git log -p -1 "$@" -O bar-then-foo --
+ git log -p -1 "$@" -O bar-then-foo --full-index --
}
get_patch_id () {
- get_top_diff "$1" | calc_patch_id "$@"
+ get_top_diff "$1" >top-diff.output &&
+ calc_patch_id <top-diff.output "$@"
}
test_expect_success 'patch-id detects equality' '
@@ -59,20 +76,49 @@ test_expect_success 'patch-id detects inequality' '
get_patch_id notsame &&
! test_cmp patch-id_main patch-id_notsame
'
+test_expect_success 'patch-id detects equality binary' '
+ cat >.gitattributes <<-\EOF &&
+ foo binary
+ bar binary
+ EOF
+ get_patch_id main &&
+ get_patch_id same &&
+ git log -p -1 --binary main >top-diff.output &&
+ calc_patch_id <top-diff.output main_binpatch &&
+ git log -p -1 --binary same >top-diff.output &&
+ calc_patch_id <top-diff.output same_binpatch &&
+ test_cmp patch-id_main patch-id_main_binpatch &&
+ test_cmp patch-id_same patch-id_same_binpatch &&
+ test_cmp patch-id_main patch-id_same &&
+ test_when_finished "rm .gitattributes"
+'
+
+test_expect_success 'patch-id detects inequality binary' '
+ cat >.gitattributes <<-\EOF &&
+ foo binary
+ bar binary
+ EOF
+ get_patch_id main &&
+ get_patch_id notsame &&
+ ! test_cmp patch-id_main patch-id_notsame &&
+ test_when_finished "rm .gitattributes"
+'
test_expect_success 'patch-id supports git-format-patch output' '
get_patch_id main &&
git checkout same &&
- git format-patch -1 --stdout | calc_patch_id same &&
+ git format-patch -1 --stdout >format-patch.output &&
+ calc_patch_id same <format-patch.output &&
test_cmp patch-id_main patch-id_same &&
- set $(git format-patch -1 --stdout | git patch-id) &&
+ set $(git patch-id <format-patch.output) &&
test "$2" = $(git rev-parse HEAD)
'
test_expect_success 'whitespace is irrelevant in footer' '
get_patch_id main &&
git checkout same &&
- git format-patch -1 --stdout | sed "s/ \$//" | calc_patch_id same &&
+ git format-patch -1 --stdout >format-patch.output &&
+ sed "s/ \$//" format-patch.output | calc_patch_id same &&
test_cmp patch-id_main patch-id_same
'
@@ -91,14 +137,27 @@ test_patch_id_file_order () {
shift
name="order-${1}-$relevant"
shift
- get_top_diff "main" | calc_patch_id "$name" "$@" &&
+ get_top_diff "main" >top-diff.output &&
+ calc_patch_id <top-diff.output "$name" "$@" &&
git checkout same &&
- git format-patch -1 --stdout -O foo-then-bar |
- calc_patch_id "ordered-$name" "$@" &&
+ git format-patch -1 --stdout -O foo-then-bar >format-patch.output &&
+ calc_patch_id <format-patch.output "ordered-$name" "$@" &&
cmp_patch_id $relevant "$name" "ordered-$name"
+}
+test_patch_id_whitespace () {
+ relevant="$1"
+ shift
+ name="ws-${1}-$relevant"
+ shift
+ get_top_diff "main~" >top-diff.output &&
+ calc_patch_id <top-diff.output "$name" "$@" &&
+ get_top_diff "with_space" >top-diff.output &&
+ calc_patch_id <top-diff.output "ws-$name" "$@" &&
+ cmp_patch_id $relevant "$name" "ws-$name"
}
+
# combined test for options: add more tests here to make them
# run with all options
test_patch_id () {
@@ -114,6 +173,14 @@ test_expect_success 'file order is relevant with --unstable' '
test_patch_id_file_order relevant --unstable --unstable
'
+test_expect_success 'whitespace is relevant with --verbatim' '
+ test_patch_id_whitespace relevant --verbatim --verbatim
+'
+
+test_expect_success 'whitespace is irrelevant without --verbatim' '
+ test_patch_id_whitespace irrelevant --stable --stable
+'
+
#Now test various option combinations.
test_expect_success 'default is unstable' '
test_patch_id relevant default
@@ -129,6 +196,17 @@ test_expect_success 'patchid.stable = false is unstable' '
test_patch_id relevant patchid.stable=false
'
+test_expect_success 'patchid.verbatim = true is correct and stable' '
+ test_config patchid.verbatim true &&
+ test_patch_id_whitespace relevant patchid.verbatim=true &&
+ test_patch_id irrelevant patchid.verbatim=true
+'
+
+test_expect_success 'patchid.verbatim = false is unstable' '
+ test_config patchid.verbatim false &&
+ test_patch_id relevant patchid.verbatim=false
+'
+
test_expect_success '--unstable overrides patchid.stable = true' '
test_config patchid.stable true &&
test_patch_id relevant patchid.stable=true--unstable --unstable
@@ -139,10 +217,16 @@ test_expect_success '--stable overrides patchid.stable = false' '
test_patch_id irrelevant patchid.stable=false--stable --stable
'
+test_expect_success '--verbatim overrides patchid.stable = false' '
+ test_config patchid.stable false &&
+ test_patch_id_whitespace relevant stable=false--verbatim --verbatim
+'
+
test_expect_success 'patch-id supports git-format-patch MIME output' '
get_patch_id main &&
git checkout same &&
- git format-patch -1 --attach --stdout | calc_patch_id same &&
+ git format-patch -1 --attach --stdout >format-patch.output &&
+ calc_patch_id <format-patch.output same &&
test_cmp patch-id_main patch-id_same
'
@@ -160,40 +244,70 @@ test_expect_success 'patch-id respects config from subdir' '
)
'
-cat >nonl <<\EOF
-diff --git i/a w/a
-index e69de29..2e65efe 100644
---- i/a
-+++ w/a
-@@ -0,0 +1 @@
-+a
-\ No newline at end of file
-diff --git i/b w/b
-index e69de29..6178079 100644
---- i/b
-+++ w/b
-@@ -0,0 +1 @@
-+b
-EOF
-
-cat >withnl <<\EOF
-diff --git i/a w/a
-index e69de29..7898192 100644
---- i/a
-+++ w/a
-@@ -0,0 +1 @@
-+a
-diff --git i/b w/b
-index e69de29..6178079 100644
---- i/b
-+++ w/b
-@@ -0,0 +1 @@
-+b
-EOF
-
test_expect_success 'patch-id handles no-nl-at-eof markers' '
- cat nonl | calc_patch_id nonl &&
- cat withnl | calc_patch_id withnl &&
- test_cmp patch-id_nonl patch-id_withnl
+ cat >nonl <<-\EOF &&
+ diff --git i/a w/a
+ index e69de29..2e65efe 100644
+ --- i/a
+ +++ w/a
+ @@ -0,0 +1 @@
+ +a
+ \ No newline at end of file
+ diff --git i/b w/b
+ index e69de29..6178079 100644
+ --- i/b
+ +++ w/b
+ @@ -0,0 +1 @@
+ +b
+ EOF
+ cat >withnl <<-\EOF &&
+ diff --git i/a w/a
+ index e69de29..7898192 100644
+ --- i/a
+ +++ w/a
+ @@ -0,0 +1 @@
+ +a
+ diff --git i/b w/b
+ index e69de29..6178079 100644
+ --- i/b
+ +++ w/b
+ @@ -0,0 +1 @@
+ +b
+ EOF
+ calc_patch_id nonl <nonl &&
+ calc_patch_id withnl <withnl &&
+ test_cmp patch-id_nonl patch-id_withnl &&
+ calc_patch_id nonl-inc-ws --verbatim <nonl &&
+ calc_patch_id withnl-inc-ws --verbatim <withnl &&
+ ! test_cmp patch-id_nonl-inc-ws patch-id_withnl-inc-ws
+'
+
+test_expect_success 'patch-id handles diffs with one line of before/after' '
+ cat >diffu1 <<-\EOF &&
+ diff --git a/bar b/bar
+ index bdaf90f..31051f6 100644
+ --- a/bar
+ +++ b/bar
+ @@ -2 +2,2 @@
+ b
+ +c
+ diff --git a/car b/car
+ index 00750ed..2ae5e34 100644
+ --- a/car
+ +++ b/car
+ @@ -1 +1,2 @@
+ 3
+ +d
+ diff --git a/foo b/foo
+ index e439850..7146eb8 100644
+ --- a/foo
+ +++ b/foo
+ @@ -2 +2,2 @@
+ a
+ +e
+ EOF
+ calc_patch_id diffu1 <diffu1 &&
+ test_config patchid.stable true &&
+ calc_patch_id diffu1stable <diffu1
'
test_done
diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh
index 5865daa..158b49d 100755
--- a/t/t4205-log-pretty-formats.sh
+++ b/t/t4205-log-pretty-formats.sh
@@ -30,40 +30,46 @@ test_expect_success 'set up basic repos' '
>bar &&
git add foo &&
test_tick &&
- git config i18n.commitEncoding $test_encoding &&
+ test_config i18n.commitEncoding $test_encoding &&
commit_msg $test_encoding | git commit -F - &&
git add bar &&
test_tick &&
- git commit -m "add bar" &&
- git config --unset i18n.commitEncoding
+ git commit -m "add bar"
'
test_expect_success 'alias builtin format' '
git log --pretty=oneline >expected &&
- git config pretty.test-alias oneline &&
+ test_config pretty.test-alias oneline &&
git log --pretty=test-alias >actual &&
test_cmp expected actual
'
test_expect_success 'alias masking builtin format' '
git log --pretty=oneline >expected &&
- git config pretty.oneline "%H" &&
+ test_config pretty.oneline "%H" &&
git log --pretty=oneline >actual &&
test_cmp expected actual
'
test_expect_success 'alias user-defined format' '
git log --pretty="format:%h" >expected &&
- git config pretty.test-alias "format:%h" &&
+ test_config pretty.test-alias "format:%h" &&
git log --pretty=test-alias >actual &&
test_cmp expected actual
'
+test_expect_success 'alias user-defined format is matched case-insensitively' '
+ git log --pretty="format:%h" >expected &&
+ test_config pretty.testone "format:%h" &&
+ test_config pretty.testtwo testOne &&
+ git log --pretty=testTwo >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'alias user-defined tformat with %s (ISO8859-1 encoding)' '
- git config i18n.logOutputEncoding $test_encoding &&
+ test_config i18n.logOutputEncoding $test_encoding &&
git log --oneline >expected-s &&
git log --pretty="tformat:%h %s" >actual-s &&
- git config --unset i18n.logOutputEncoding &&
test_cmp expected-s actual-s
'
@@ -75,34 +81,34 @@ test_expect_success 'alias user-defined tformat with %s (utf-8 encoding)' '
test_expect_success 'alias user-defined tformat' '
git log --pretty="tformat:%h" >expected &&
- git config pretty.test-alias "tformat:%h" &&
+ test_config pretty.test-alias "tformat:%h" &&
git log --pretty=test-alias >actual &&
test_cmp expected actual
'
test_expect_success 'alias non-existent format' '
- git config pretty.test-alias format-that-will-never-exist &&
+ test_config pretty.test-alias format-that-will-never-exist &&
test_must_fail git log --pretty=test-alias
'
test_expect_success 'alias of an alias' '
git log --pretty="tformat:%h" >expected &&
- git config pretty.test-foo "tformat:%h" &&
- git config pretty.test-bar test-foo &&
+ test_config pretty.test-foo "tformat:%h" &&
+ test_config pretty.test-bar test-foo &&
git log --pretty=test-bar >actual && test_cmp expected actual
'
test_expect_success 'alias masking an alias' '
git log --pretty=format:"Two %H" >expected &&
- git config pretty.duplicate "format:One %H" &&
- git config --add pretty.duplicate "format:Two %H" &&
+ test_config pretty.duplicate "format:One %H" &&
+ test_config pretty.duplicate "format:Two %H" --add &&
git log --pretty=duplicate >actual &&
test_cmp expected actual
'
test_expect_success 'alias loop' '
- git config pretty.test-foo test-bar &&
- git config pretty.test-bar test-foo &&
+ test_config pretty.test-foo test-bar &&
+ test_config pretty.test-bar test-foo &&
test_must_fail git log --pretty=test-foo
'
@@ -156,7 +162,7 @@ test_expect_success 'NUL termination with --reflog --pretty=oneline' '
for r in $revs
do
git show -s --pretty=oneline "$r" >raw &&
- cat raw | lf_to_nul || exit 1
+ lf_to_nul <raw || return 1
done >expect &&
# the trailing NUL is already produced so we do not need to
# output another one
@@ -576,6 +582,38 @@ test_expect_success 'clean log decoration' '
test_cmp expected actual1
'
+test_expect_success 'pretty format %decorate' '
+ git checkout -b foo &&
+ git commit --allow-empty -m "new commit" &&
+ git tag bar &&
+ git branch qux &&
+
+ echo " (HEAD -> foo, tag: bar, qux)" >expect1 &&
+ git log --format="%(decorate)" -1 >actual1 &&
+ test_cmp expect1 actual1 &&
+
+ echo "HEAD -> foo, tag: bar, qux" >expect2 &&
+ git log --format="%(decorate:prefix=,suffix=)" -1 >actual2 &&
+ test_cmp expect2 actual2 &&
+
+ echo "[ bar; qux; foo ]" >expect3 &&
+ git log --format="%(decorate:prefix=[ ,suffix= ],separator=%x3B ,tag=)" \
+ --decorate-refs=refs/ -1 >actual3 &&
+ test_cmp expect3 actual3 &&
+
+ # Try with a typo (in "separator"), in which case the placeholder should
+ # not be replaced.
+ echo "%(decorate:prefix=[ ,suffix= ],separater=; )" >expect4 &&
+ git log --format="%(decorate:prefix=[ ,suffix= ],separater=%x3B )" \
+ -1 >actual4 &&
+ test_cmp expect4 actual4 &&
+
+ echo "HEAD->foo bar qux" >expect5 &&
+ git log --format="%(decorate:prefix=,suffix=,separator= ,tag=,pointer=->)" \
+ -1 >actual5 &&
+ test_cmp expect5 actual5
+'
+
cat >trailers <<EOF
Signed-off-by: A U Thor <author@example.com>
Acked-by: A U Thor <author@example.com>
@@ -924,6 +962,36 @@ test_expect_success '%S in git log --format works with other placeholders (part
test_cmp expect actual
'
+test_expect_success 'setup more commits for %S with --bisect' '
+ test_commit four &&
+ test_commit five &&
+
+ head1=$(git rev-parse --verify HEAD~0) &&
+ head2=$(git rev-parse --verify HEAD~1) &&
+ head3=$(git rev-parse --verify HEAD~2) &&
+ head4=$(git rev-parse --verify HEAD~3)
+'
+
+test_expect_success '%S with --bisect labels commits with refs/bisect/bad ref' '
+ git update-ref refs/bisect/bad-$head1 $head1 &&
+ git update-ref refs/bisect/go $head1 &&
+ git update-ref refs/bisect/bad-$head2 $head2 &&
+ git update-ref refs/bisect/b $head3 &&
+ git update-ref refs/bisect/bad-$head4 $head4 &&
+ git update-ref refs/bisect/good-$head4 $head4 &&
+
+ # We expect to see the range of commits betwee refs/bisect/good-$head4
+ # and refs/bisect/bad-$head1. The "source" ref is the nearest bisect ref
+ # from which the commit is reachable.
+ cat >expect <<-EOF &&
+ $head1 refs/bisect/bad-$head1
+ $head2 refs/bisect/bad-$head2
+ $head3 refs/bisect/bad-$head2
+ EOF
+ git log --bisect --format="%H %S" >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'log --pretty=reference' '
git log --pretty="tformat:%h (%s, %as)" >expect &&
git log --pretty=reference >actual &&
@@ -976,7 +1044,7 @@ test_expect_success '%(describe) vs git describe' '
else
: >expect-contains-bad
fi &&
- echo "$hash $desc"
+ echo "$hash $desc" || return 1
done >expect &&
test_path_exists expect-contains-good &&
test_path_exists expect-contains-bad &&
@@ -1002,4 +1070,138 @@ test_expect_success '%(describe:exclude=...) vs git describe --exclude ...' '
test_cmp expect actual
'
+test_expect_success '%(describe:tags) vs git describe --tags' '
+ test_when_finished "git tag -d tagname" &&
+ git tag tagname &&
+ git describe --tags >expect &&
+ git log -1 --format="%(describe:tags)" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '%(describe:abbrev=...) vs git describe --abbrev=...' '
+ test_when_finished "git tag -d tagname" &&
+
+ # Case 1: We have commits between HEAD and the most recent tag
+ # reachable from it
+ test_commit --no-tag file &&
+ git describe --abbrev=15 >expect &&
+ git log -1 --format="%(describe:abbrev=15)" >actual &&
+ test_cmp expect actual &&
+
+ # Make sure the hash used is at least 15 digits long
+ sed -e "s/^.*-g\([0-9a-f]*\)$/\1/" <actual >hexpart &&
+ test 16 -le $(wc -c <hexpart) &&
+
+ # Case 2: We have a tag at HEAD, describe directly gives the
+ # name of the tag
+ git tag -a -m tagged tagname &&
+ git describe --abbrev=15 >expect &&
+ git log -1 --format="%(describe:abbrev=15)" >actual &&
+ test_cmp expect actual &&
+ test tagname = $(cat actual)
+'
+
+test_expect_success 'log --pretty with space stealing' '
+ printf mm0 >expect &&
+ git log -1 --pretty="format:mm%>>|(1)%x30" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log --pretty with invalid padding format' '
+ printf "%s%%<(20" "$(git rev-parse HEAD)" >expect &&
+ git log -1 --pretty="format:%H%<(20" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log --pretty with magical wrapping directives' '
+ commit_id=$(git commit-tree HEAD^{tree} -m "describe me") &&
+ git tag describe-me $commit_id &&
+ printf "\n(tag:\ndescribe-me)%%+w(2)" >expect &&
+ git log -1 --pretty="format:%w(1)%+d%+w(2)" $commit_id >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success SIZE_T_IS_64BIT 'log --pretty with overflowing wrapping directive' '
+ printf "%%w(2147483649,1,1)0" >expect &&
+ git log -1 --pretty="format:%w(2147483649,1,1)%x30" >actual &&
+ test_cmp expect actual &&
+ printf "%%w(1,2147483649,1)0" >expect &&
+ git log -1 --pretty="format:%w(1,2147483649,1)%x30" >actual &&
+ test_cmp expect actual &&
+ printf "%%w(1,1,2147483649)0" >expect &&
+ git log -1 --pretty="format:%w(1,1,2147483649)%x30" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success SIZE_T_IS_64BIT 'log --pretty with overflowing padding directive' '
+ printf "%%<(2147483649)0" >expect &&
+ git log -1 --pretty="format:%<(2147483649)%x30" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log --pretty with padding and preceding control chars' '
+ printf "\20\20 0" >expect &&
+ git log -1 --pretty="format:%x10%x10%>|(4)%x30" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log --pretty truncation with control chars' '
+ test_commit "$(printf "\20\20\20\20xxxx")" file contents commit-with-control-chars &&
+ printf "\20\20\20\20x.." >expect &&
+ git log -1 --pretty="format:%<(3,trunc)%s" commit-with-control-chars >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success EXPENSIVE,SIZE_T_IS_64BIT 'log --pretty with huge commit message' '
+ # We only assert that this command does not crash. This needs to be
+ # executed with the address sanitizer to demonstrate failure.
+ git log -1 --pretty="format:%>(2147483646)%x41%41%>(2147483646)%x41" >/dev/null
+'
+
+test_expect_success EXPENSIVE,SIZE_T_IS_64BIT 'set up huge commit' '
+ test-tool genzeros 2147483649 | tr "\000" "1" >expect &&
+ huge_commit=$(git commit-tree -F expect HEAD^{tree})
+'
+
+test_expect_success EXPENSIVE,SIZE_T_IS_64BIT 'log --pretty with huge commit message' '
+ git log -1 --format="%B%<(1)%x30" $huge_commit >actual &&
+ echo 0 >>expect &&
+ test_cmp expect actual
+'
+
+test_expect_success EXPENSIVE,SIZE_T_IS_64BIT 'log --pretty with huge commit message does not cause allocation failure' '
+ test_must_fail git log -1 --format="%<(1)%B" $huge_commit 2>error &&
+ cat >expect <<-EOF &&
+ fatal: number too large to represent as int on this platform: 2147483649
+ EOF
+ test_cmp expect error
+'
+
+# pretty-formats note wide char limitations, and add tests
+test_expect_failure 'wide and decomposed characters column counting' '
+
+# from t/lib-unicode-nfc-nfd.sh hex values converted to octal
+ utf8_nfc=$(printf "\303\251") && # e acute combined.
+ utf8_nfd=$(printf "\145\314\201") && # e with a combining acute (i.e. decomposed)
+ utf8_emoji=$(printf "\360\237\221\250") &&
+
+# replacement character when requesting a wide char fits in a single display colum.
+# "half wide" alternative could be a plain ASCII dot `.`
+ utf8_vert_ell=$(printf "\342\213\256") &&
+
+# use ${xxx} here!
+ nfc10="${utf8_nfc}${utf8_nfc}${utf8_nfc}${utf8_nfc}${utf8_nfc}${utf8_nfc}${utf8_nfc}${utf8_nfc}${utf8_nfc}${utf8_nfc}" &&
+ nfd10="${utf8_nfd}${utf8_nfd}${utf8_nfd}${utf8_nfd}${utf8_nfd}${utf8_nfd}${utf8_nfd}${utf8_nfd}${utf8_nfd}${utf8_nfd}" &&
+ emoji5="${utf8_emoji}${utf8_emoji}${utf8_emoji}${utf8_emoji}${utf8_emoji}" &&
+# emoji5 uses 10 display columns
+
+ test_commit "abcdefghij" &&
+ test_commit --no-tag "${nfc10}" &&
+ test_commit --no-tag "${nfd10}" &&
+ test_commit --no-tag "${emoji5}" &&
+ printf "${utf8_emoji}..${utf8_emoji}${utf8_vert_ell}\n${utf8_nfd}..${utf8_nfd}${utf8_nfd}\n${utf8_nfc}..${utf8_nfc}${utf8_nfc}\na..ij\n" >expected &&
+ git log --format="%<(5,mtrunc)%s" -4 >actual &&
+ test_cmp expected actual
+'
+
test_done
diff --git a/t/t4206-log-follow-harder-copies.sh b/t/t4206-log-follow-harder-copies.sh
index 4871a5d..9167b03 100755
--- a/t/t4206-log-follow-harder-copies.sh
+++ b/t/t4206-log-follow-harder-copies.sh
@@ -6,6 +6,8 @@
test_description='Test --follow should always find copies hard in git log.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-diff.sh
@@ -14,29 +16,29 @@ Line 2
Line 3
'
-test_expect_success \
- 'add a file path0 and commit.' \
- 'git add path0 &&
- git commit -m "Add path0"'
+test_expect_success 'add a file path0 and commit.' '
+ git add path0 &&
+ git commit -m "Add path0"
+'
echo >path0 'New line 1
New line 2
New line 3
'
-test_expect_success \
- 'Change path0.' \
- 'git add path0 &&
- git commit -m "Change path0"'
+test_expect_success 'Change path0.' '
+ git add path0 &&
+ git commit -m "Change path0"
+'
cat <path0 >path1
-test_expect_success \
- 'copy path0 to path1.' \
- 'git add path1 &&
- git commit -m "Copy path1 from path0"'
+test_expect_success 'copy path0 to path1.' '
+ git add path1 &&
+ git commit -m "Copy path1 from path0"
+'
-test_expect_success \
- 'find the copy path0 -> path1 harder' \
- 'git log --follow --name-status --pretty="format:%s" path1 > current'
+test_expect_success 'find the copy path0 -> path1 harder' '
+ git log --follow --name-status --pretty="format:%s" path1 > current
+'
cat >expected <<\EOF
Copy path1 from path0
@@ -49,8 +51,8 @@ Add path0
A path0
EOF
-test_expect_success \
- 'validate the output.' \
- 'compare_diff_patch current expected'
+test_expect_success 'validate the output.' '
+ compare_diff_patch current expected
+'
test_done
diff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh
index b870942..73ea9e5 100755
--- a/t/t4207-log-decoration-colors.sh
+++ b/t/t4207-log-decoration-colors.sh
@@ -3,11 +3,12 @@
# Copyright (c) 2010 Nazri Ramliy
#
-test_description='Test for "git log --decorate" colors'
+test_description='test "git log --decorate" colors'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -16,6 +17,7 @@ test_expect_success setup '
git config color.decorate.remoteBranch red &&
git config color.decorate.tag "reverse bold yellow" &&
git config color.decorate.stash magenta &&
+ git config color.decorate.grafted black &&
git config color.decorate.HEAD cyan &&
c_reset="<RESET>" &&
@@ -26,6 +28,7 @@ test_expect_success setup '
c_tag="<BOLD;REVERSE;YELLOW>" &&
c_stash="<MAGENTA>" &&
c_HEAD="<CYAN>" &&
+ c_grafted="<BLACK>" &&
test_commit A &&
git clone . other &&
@@ -41,25 +44,91 @@ test_expect_success setup '
git stash save Changes to A.t
'
-cat >expected <<EOF
-${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD ->\
- ${c_reset}${c_branch}main${c_reset}${c_commit},\
- ${c_reset}${c_tag}tag: v1.0${c_reset}${c_commit},\
- ${c_reset}${c_tag}tag: B${c_reset}${c_commit})${c_reset} B
-${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A1${c_reset}${c_commit},\
- ${c_reset}${c_remoteBranch}other/main${c_reset}${c_commit})${c_reset} A1
-${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_stash}refs/stash${c_reset}${c_commit})${c_reset}\
- On main: Changes to A.t
-${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A${c_reset}${c_commit})${c_reset} A
-EOF
+cmp_filtered_decorations () {
+ sed "s/$OID_REGEX/COMMIT_ID/" actual | test_decode_color >filtered &&
+ test_cmp expect filtered
+}
# We want log to show all, but the second parent to refs/stash is irrelevant
# to this test since it does not contain any decoration, hence --first-parent
-test_expect_success 'Commit Decorations Colored Correctly' '
- git log --first-parent --abbrev=10 --all --decorate --oneline --color=always |
- sed "s/[0-9a-f]\{10,10\}/COMMIT_ID/" |
- test_decode_color >out &&
- test_cmp expected out
+test_expect_success 'commit decorations colored correctly' '
+ cat >expect <<-EOF &&
+ ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD${c_reset}\
+${c_commit} -> ${c_reset}${c_branch}main${c_reset}${c_commit}, \
+${c_reset}${c_tag}tag: ${c_reset}${c_tag}v1.0${c_reset}${c_commit}, \
+${c_reset}${c_tag}tag: ${c_reset}${c_tag}B${c_reset}${c_commit})${c_reset} B
+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
+${c_tag}tag: ${c_reset}${c_tag}A1${c_reset}${c_commit}, \
+${c_reset}${c_remoteBranch}other/main${c_reset}${c_commit})${c_reset} A1
+ ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
+${c_stash}refs/stash${c_reset}${c_commit})${c_reset} On main: Changes to A.t
+ ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
+${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_commit})${c_reset} A
+ EOF
+
+ git log --first-parent --no-abbrev --decorate --oneline --color=always --all >actual &&
+ cmp_filtered_decorations
+'
+
+remove_replace_refs () {
+ git for-each-ref 'refs/replace*/**' --format='delete %(refname)' >in &&
+ git update-ref --stdin <in &&
+ rm in
+}
+
+test_expect_success 'test coloring with replace-objects' '
+ test_when_finished remove_replace_refs &&
+ test_commit C &&
+ test_commit D &&
+
+ git replace HEAD~1 HEAD~2 &&
+
+ cat >expect <<-EOF &&
+ ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD${c_reset}\
+${c_commit} -> ${c_reset}${c_branch}main${c_reset}${c_commit}, \
+${c_reset}${c_tag}tag: ${c_reset}${c_tag}D${c_reset}${c_commit})${c_reset} D
+ ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
+${c_tag}tag: ${c_reset}${c_tag}C${c_reset}${c_commit}, \
+${c_reset}${c_grafted}replaced${c_reset}${c_commit})${c_reset} B
+ ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
+${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_commit})${c_reset} A
+EOF
+
+ git log --first-parent --no-abbrev --decorate --oneline --color=always HEAD >actual &&
+ cmp_filtered_decorations &&
+ git replace -d HEAD~1 &&
+
+ GIT_REPLACE_REF_BASE=refs/replace2/ git replace HEAD~1 HEAD~2 &&
+ GIT_REPLACE_REF_BASE=refs/replace2/ git log --first-parent \
+ --no-abbrev --decorate --oneline --color=always HEAD >actual &&
+ cmp_filtered_decorations
+'
+
+test_expect_success 'test coloring with grafted commit' '
+ test_when_finished remove_replace_refs &&
+
+ git replace --graft HEAD HEAD~2 &&
+
+ cat >expect <<-EOF &&
+ ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD${c_reset}\
+${c_commit} -> ${c_reset}${c_branch}main${c_reset}${c_commit}, \
+${c_reset}${c_tag}tag: ${c_reset}${c_tag}D${c_reset}${c_commit}, \
+${c_reset}${c_grafted}replaced${c_reset}${c_commit})${c_reset} D
+ ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
+${c_tag}tag: ${c_reset}${c_tag}v1.0${c_reset}${c_commit}, \
+${c_reset}${c_tag}tag: ${c_reset}${c_tag}B${c_reset}${c_commit})${c_reset} B
+ ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
+${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_commit})${c_reset} A
+ EOF
+
+ git log --first-parent --no-abbrev --decorate --oneline --color=always HEAD >actual &&
+ cmp_filtered_decorations &&
+ git replace -d HEAD &&
+
+ GIT_REPLACE_REF_BASE=refs/replace2/ git replace --graft HEAD HEAD~2 &&
+ GIT_REPLACE_REF_BASE=refs/replace2/ git log --first-parent \
+ --no-abbrev --decorate --oneline --color=always HEAD >actual &&
+ cmp_filtered_decorations
'
test_done
diff --git a/t/t4208-log-magic-pathspec.sh b/t/t4208-log-magic-pathspec.sh
index 7f0c1dc..806b280 100755
--- a/t/t4208-log-magic-pathspec.sh
+++ b/t/t4208-log-magic-pathspec.sh
@@ -21,7 +21,7 @@ test_expect_success '"git log :/" should not be ambiguous' '
test_expect_success '"git log :/a" should be ambiguous (applied both rev and worktree)' '
: >a &&
test_must_fail git log :/a 2>error &&
- test_i18ngrep ambiguous error
+ test_grep ambiguous error
'
test_expect_success '"git log :/a -- " should not be ambiguous' '
@@ -65,7 +65,7 @@ test_expect_success '"git log :/in" should not be ambiguous' '
test_expect_success '"git log :" should be ambiguous' '
test_must_fail git log : 2>error &&
- test_i18ngrep ambiguous error
+ test_grep ambiguous error
'
test_expect_success 'git log -- :' '
@@ -104,7 +104,7 @@ test_expect_success '"git log :(exclude)sub --" must resolve as an object' '
test_expect_success '"git log :(unknown-magic) complains of bogus magic' '
test_must_fail git log ":(unknown-magic)" 2>error &&
- test_i18ngrep pathspec.magic error
+ test_grep pathspec.magic error
'
test_expect_success 'command line pathspec parsing for "git log"' '
@@ -124,6 +124,7 @@ test_expect_success 'command line pathspec parsing for "git log"' '
test_expect_success 'tree_entry_interesting does not match past submodule boundaries' '
test_when_finished "rm -rf repo submodule" &&
+ test_config_global protocol.file.allow always &&
git init submodule &&
test_commit -C submodule initial &&
git init repo &&
diff --git a/t/t4209-log-pickaxe.sh b/t/t4209-log-pickaxe.sh
index 75795d0..64e1623 100755
--- a/t/t4209-log-pickaxe.sh
+++ b/t/t4209-log-pickaxe.sh
@@ -57,27 +57,27 @@ test_expect_success setup '
test_expect_success 'usage' '
test_expect_code 129 git log -S 2>err &&
- test_i18ngrep "switch.*requires a value" err &&
+ test_grep "switch.*requires a value" err &&
test_expect_code 129 git log -G 2>err &&
- test_i18ngrep "switch.*requires a value" err &&
+ test_grep "switch.*requires a value" err &&
test_expect_code 128 git log -Gregex -Sstring 2>err &&
- grep "mutually exclusive" err &&
+ grep "cannot be used together" err &&
test_expect_code 128 git log -Gregex --find-object=HEAD 2>err &&
- grep "mutually exclusive" err &&
+ grep "cannot be used together" err &&
test_expect_code 128 git log -Sstring --find-object=HEAD 2>err &&
- grep "mutually exclusive" err &&
+ grep "cannot be used together" err &&
test_expect_code 128 git log --pickaxe-all --find-object=HEAD 2>err &&
- grep "mutually exclusive" err
+ grep "cannot be used together" err
'
test_expect_success 'usage: --pickaxe-regex' '
test_expect_code 128 git log -Gregex --pickaxe-regex 2>err &&
- grep "mutually exclusive" err
+ grep "cannot be used together" err
'
test_expect_success 'usage: --no-pickaxe-regex' '
diff --git a/t/t4210-log-i18n.sh b/t/t4210-log-i18n.sh
index 0141f36..75216f1 100755
--- a/t/t4210-log-i18n.sh
+++ b/t/t4210-log-i18n.sh
@@ -64,7 +64,7 @@ test_expect_success 'log --grep does not find non-reencoded values (latin1)' '
'
triggers_undefined_behaviour () {
- local engine=$1
+ local engine="$1"
case $engine in
fixed)
@@ -85,7 +85,7 @@ triggers_undefined_behaviour () {
}
mismatched_git_log () {
- local pattern=$1
+ local pattern="$1"
LC_ALL=$is_IS_locale git log --encoding=ISO-8859-1 --format=%s \
--grep=$pattern
@@ -131,11 +131,4 @@ do
fi
done
-test_expect_success 'log shows warning when conversion fails' '
- enc=this-encoding-does-not-exist &&
- git log -1 --encoding=$enc 2>err &&
- echo "warning: unable to reencode commit to ${SQ}${enc}${SQ}" >expect &&
- test_cmp expect err
-'
-
test_done
diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh
index 560127c..02d76dc 100755
--- a/t/t4211-line-log.sh
+++ b/t/t4211-line-log.sh
@@ -19,7 +19,7 @@ test_expect_success 'basic command line parsing' '
# -L requires there is no pathspec
test_must_fail git log -L1,1:b.c -- b.c 2>error &&
- test_i18ngrep "cannot be used with pathspec" error &&
+ test_grep "cannot be used with pathspec" error &&
# This would fail because --follow wants a single path, but
# we may fail due to incompatibility between -L/--follow in
@@ -50,7 +50,7 @@ canned_test_failure () {
test_bad_opts () {
test_expect_success "invalid args: $1" "
test_must_fail git log $1 2>errors &&
- test_i18ngrep '$2' errors
+ test_grep '$2' errors
"
}
@@ -137,7 +137,7 @@ test_expect_success 'range_set_union' '
test_seq 1000 > c.c &&
git add c.c &&
git commit -m "modify many lines" &&
- git log $(for x in $(test_seq 200); do echo -L $((2*x)),+1:c.c; done)
+ git log $(for x in $(test_seq 200); do echo -L $((2*x)),+1:c.c || return 1; done)
'
test_expect_success '-s shows only line-log commits' '
@@ -315,4 +315,26 @@ test_expect_success 'line-log with --before' '
test_cmp expect actual
'
+test_expect_success 'setup tests for zero-width regular expressions' '
+ cat >expect <<-EOF
+ Modify func1() in file.c
+ Add func1() and func2() in file.c
+ EOF
+'
+
+test_expect_success 'zero-width regex $ matches any function name' '
+ git log --format="%s" --no-patch "-L:$:file.c" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'zero-width regex ^ matches any function name' '
+ git log --format="%s" --no-patch "-L:^:file.c" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'zero-width regex .* matches any function name' '
+ git log --format="%s" --no-patch "-L:.*:file.c" >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh
index 03b952c..e6b5912 100755
--- a/t/t4212-log-corrupt.sh
+++ b/t/t4212-log-corrupt.sh
@@ -2,28 +2,30 @@
test_description='git log with invalid commit headers'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
test_commit foo &&
- git cat-file commit HEAD |
- sed "/^author /s/>/>-<>/" >broken_email.commit &&
- git hash-object -w -t commit broken_email.commit >broken_email.hash &&
+ git cat-file commit HEAD >ok.commit &&
+ sed "s/>/>-<>/" <ok.commit >broken_email.commit &&
+
+ git hash-object --literally -w -t commit broken_email.commit >broken_email.hash &&
git update-ref refs/heads/broken_email $(cat broken_email.hash)
'
test_expect_success 'fsck notices broken commit' '
test_must_fail git fsck 2>actual &&
- test_i18ngrep invalid.author actual
+ test_grep invalid.author actual
'
test_expect_success 'git log with broken author email' '
{
- echo commit $(cat broken_email.hash)
- echo "Author: A U Thor <author@example.com>"
- echo "Date: Thu Apr 7 15:13:13 2005 -0700"
- echo
+ echo commit $(cat broken_email.hash) &&
+ echo "Author: A U Thor <author@example.com>" &&
+ echo "Date: Thu Apr 7 15:13:13 2005 -0700" &&
+ echo &&
echo " foo"
} >expect.out &&
@@ -42,10 +44,15 @@ test_expect_success 'git log --format with broken author email' '
test_must_be_empty actual.err
'
+test_expect_success '--until handles broken email' '
+ git rev-list --until=1980-01-01 broken_email >actual &&
+ test_must_be_empty actual
+'
+
munge_author_date () {
git cat-file commit "$1" >commit.orig &&
sed "s/^\(author .*>\) [0-9]*/\1 $2/" <commit.orig >commit.munge &&
- git hash-object -w -t commit commit.munge
+ git hash-object --literally -w -t commit commit.munge
}
test_expect_success 'unparsable dates produce sentinel value' '
@@ -85,4 +92,45 @@ test_expect_success 'absurdly far-in-future date' '
git log -1 --format=%ad $commit
'
+test_expect_success 'create commits with whitespace committer dates' '
+ # It is important that this subject line is numeric, since we want to
+ # be sure we are not confused by skipping whitespace and accidentally
+ # parsing the subject as a timestamp.
+ #
+ # Do not use munge_author_date here. Besides not hitting the committer
+ # line, it leaves the timezone intact, and we want nothing but
+ # whitespace.
+ #
+ # We will make two munged commits here. The first, ws_commit, will
+ # be purely spaces. The second contains a vertical tab, which is
+ # considered a space by strtoumax(), but not by our isspace().
+ test_commit 1234567890 &&
+ git cat-file commit HEAD >commit.orig &&
+ sed "s/>.*/> /" <commit.orig >commit.munge &&
+ ws_commit=$(git hash-object --literally -w -t commit commit.munge) &&
+ sed "s/>.*/> $(printf "\013")/" <commit.orig >commit.munge &&
+ vt_commit=$(git hash-object --literally -w -t commit commit.munge)
+'
+
+test_expect_success '--until treats whitespace date as sentinel' '
+ echo $ws_commit >expect &&
+ git rev-list --until=1980-01-01 $ws_commit >actual &&
+ test_cmp expect actual &&
+
+ echo $vt_commit >expect &&
+ git rev-list --until=1980-01-01 $vt_commit >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'pretty-printer handles whitespace date' '
+ # as with the %ad test above, we will show these as the empty string,
+ # not the 1970 epoch date. This is intentional; see 7d9a281941 (t4212:
+ # test bogus timestamps with git-log, 2014-02-24) for more discussion.
+ echo : >expect &&
+ git log -1 --format="%at:%ct" $ws_commit >actual &&
+ test_cmp expect actual &&
+ git log -1 --format="%at:%ct" $vt_commit >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t4213-log-tabexpand.sh b/t/t4213-log-tabexpand.sh
index 53a4af3..590fce9 100755
--- a/t/t4213-log-tabexpand.sh
+++ b/t/t4213-log-tabexpand.sh
@@ -2,6 +2,7 @@
test_description='log/show --expand-tabs'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
HT=" "
diff --git a/t/t4214-log-graph-octopus.sh b/t/t4214-log-graph-octopus.sh
index f70c46b..7905597 100755
--- a/t/t4214-log-graph-octopus.sh
+++ b/t/t4214-log-graph-octopus.sh
@@ -5,6 +5,7 @@ test_description='git log --graph of skewed left octopus merge.'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-log-graph.sh
diff --git a/t/t4215-log-skewed-merges.sh b/t/t4215-log-skewed-merges.sh
index 28d0779..b877ac7 100755
--- a/t/t4215-log-skewed-merges.sh
+++ b/t/t4215-log-skewed-merges.sh
@@ -2,6 +2,7 @@
test_description='git log --graph of skewed merges'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-log-graph.sh
diff --git a/t/t4216-log-bloom.sh b/t/t4216-log-bloom.sh
index 50f206d..2ba0324 100755
--- a/t/t4216-log-bloom.sh
+++ b/t/t4216-log-bloom.sh
@@ -5,6 +5,7 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-chunk.sh
GIT_TEST_COMMIT_GRAPH=0
GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=0
@@ -48,6 +49,7 @@ graph_read_expect () {
header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0
num_commits: $1
chunks: oid_fanout oid_lookup commit_metadata generation_data bloom_indexes bloom_data
+ options: bloom(1,10,7) read_generation_data
EOF
test-tool read-graph >actual &&
test_cmp expect actual
@@ -175,13 +177,11 @@ test_expect_success 'persist filter settings' '
test_when_finished rm -rf .git/objects/info/commit-graph* &&
rm -rf .git/objects/info/commit-graph* &&
GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
- GIT_TRACE2_EVENT_NESTING=5 \
GIT_TEST_BLOOM_SETTINGS_NUM_HASHES=9 \
GIT_TEST_BLOOM_SETTINGS_BITS_PER_ENTRY=15 \
git commit-graph write --reachable --changed-paths &&
grep "{\"hash_version\":1,\"num_hashes\":9,\"bits_per_entry\":15,\"max_changed_paths\":512" trace2.txt &&
GIT_TRACE2_EVENT="$(pwd)/trace2-auto.txt" \
- GIT_TRACE2_EVENT_NESTING=5 \
git commit-graph write --reachable --changed-paths &&
grep "{\"hash_version\":1,\"num_hashes\":9,\"bits_per_entry\":15,\"max_changed_paths\":512" trace2-auto.txt
'
@@ -376,7 +376,7 @@ test_expect_success 'Bloom generation backfills empty commits' '
cd empty &&
for i in $(test_seq 1 6)
do
- git commit --allow-empty -m "$i"
+ git commit --allow-empty -m "$i" || return 1
done &&
# Generate Bloom filters for empty commits 1-6, two at a time.
@@ -389,7 +389,7 @@ test_expect_success 'Bloom generation backfills empty commits' '
test_filter_computed 2 trace.event &&
test_filter_not_computed 4 trace.event &&
test_filter_trunc_empty 2 trace.event &&
- test_filter_trunc_large 0 trace.event
+ test_filter_trunc_large 0 trace.event || return 1
done &&
# Finally, make sure that once all commits have filters, that
@@ -405,4 +405,53 @@ test_expect_success 'Bloom generation backfills empty commits' '
)
'
+corrupt_graph () {
+ graph=.git/objects/info/commit-graph &&
+ test_when_finished "rm -rf $graph" &&
+ git commit-graph write --reachable --changed-paths &&
+ corrupt_chunk_file $graph "$@"
+}
+
+check_corrupt_graph () {
+ corrupt_graph "$@" &&
+ git -c core.commitGraph=false log -- A/B/file2 >expect.out &&
+ git -c core.commitGraph=true log -- A/B/file2 >out 2>err &&
+ test_cmp expect.out out
+}
+
+test_expect_success 'Bloom reader notices too-small data chunk' '
+ check_corrupt_graph BDAT clear 00000000 &&
+ echo "warning: ignoring too-small changed-path chunk" \
+ "(4 < 12) in commit-graph file" >expect.err &&
+ test_cmp expect.err err
+'
+
+test_expect_success 'Bloom reader notices out-of-bounds filter offsets' '
+ check_corrupt_graph BIDX 12 FFFFFFFF &&
+ # use grep to avoid depending on exact chunk size
+ grep "warning: ignoring out-of-range offset (4294967295) for changed-path filter at pos 3 of .git/objects/info/commit-graph" err
+'
+
+test_expect_success 'Bloom reader notices too-small index chunk' '
+ # replace the index with a single entry, making most
+ # lookups out-of-bounds
+ check_corrupt_graph BIDX clear 00000000 &&
+ echo "warning: commit-graph changed-path index chunk" \
+ "is too small" >expect.err &&
+ test_cmp expect.err err
+'
+
+test_expect_success 'Bloom reader notices out-of-order index offsets' '
+ # we do not know any real offsets, but we can pick
+ # something plausible; we should not get to the point of
+ # actually reading from the bogus offsets anyway.
+ corrupt_graph BIDX 4 0000000c00000005 &&
+ echo "warning: ignoring decreasing changed-path index offsets" \
+ "(12 > 5) for positions 1 and 2 of .git/objects/info/commit-graph" >expect.err &&
+ git -c core.commitGraph=false log -- A/B/file2 >expect.out &&
+ git -c core.commitGraph=true log -- A/B/file2 >out 2>err &&
+ test_cmp expect.out out &&
+ test_cmp expect.err err
+'
+
test_done
diff --git a/t/t4217-log-limit.sh b/t/t4217-log-limit.sh
new file mode 100755
index 0000000..613f071
--- /dev/null
+++ b/t/t4217-log-limit.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+test_description='git log with filter options limiting the output'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'setup test' '
+ git init &&
+ echo a >file &&
+ git add file &&
+ GIT_COMMITTER_DATE="2021-02-01 00:00" git commit -m init &&
+ echo a >>file &&
+ git add file &&
+ GIT_COMMITTER_DATE="2022-02-01 00:00" git commit -m first &&
+ echo a >>file &&
+ git add file &&
+ GIT_COMMITTER_DATE="2021-03-01 00:00" git commit -m second &&
+ echo a >>file &&
+ git add file &&
+ GIT_COMMITTER_DATE="2022-03-01 00:00" git commit -m third
+'
+
+test_expect_success 'git log --since-as-filter=...' '
+ git log --since-as-filter="2022-01-01" --format=%s >actual &&
+ cat >expect <<-\EOF &&
+ third
+ first
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'git log --children --since-as-filter=...' '
+ git log --children --since-as-filter="2022-01-01" --format=%s >actual &&
+ cat >expect <<-\EOF &&
+ third
+ first
+ EOF
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t4252-am-options.sh b/t/t4252-am-options.sh
index e758e63..5b680dc 100755
--- a/t/t4252-am-options.sh
+++ b/t/t4252-am-options.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='git am with options and not losing them'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
tm="$TEST_DIRECTORY/t4252"
diff --git a/t/t4254-am-corrupt.sh b/t/t4254-am-corrupt.sh
index 54be7da..661feb6 100755
--- a/t/t4254-am-corrupt.sh
+++ b/t/t4254-am-corrupt.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='git am with corrupt input'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
make_mbox_with_nul () {
@@ -57,7 +59,7 @@ test_expect_success setup '
# Also, it had the unwanted side-effect of deleting f.
test_expect_success 'try to apply corrupted patch' '
test_when_finished "git am --abort" &&
- test_must_fail git -c advice.amWorkDir=false am bad-patch.diff 2>actual &&
+ test_must_fail git -c advice.amWorkDir=false -c advice.mergeConflict=false am bad-patch.diff 2>actual &&
echo "error: git diff header lacks filename information (line 4)" >expected &&
test_path_is_file f &&
test_cmp expected actual
diff --git a/t/t4256-am-format-flowed.sh b/t/t4256-am-format-flowed.sh
index 2369c4e..92d8c8b 100755
--- a/t/t4256-am-format-flowed.sh
+++ b/t/t4256-am-format-flowed.sh
@@ -2,6 +2,7 @@
test_description='test format=flowed support of git am'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -12,7 +13,7 @@ test_expect_success 'setup' '
test_expect_success 'am with format=flowed' '
git am <"$TEST_DIRECTORY/t4256/1/patch" 2>stderr &&
- test_i18ngrep "warning: Patch sent with format=flowed" stderr &&
+ test_grep "warning: Patch sent with format=flowed" stderr &&
test_cmp "$TEST_DIRECTORY/t4256/1/mailinfo.c" mailinfo.c
'
diff --git a/t/t4257-am-interactive.sh b/t/t4257-am-interactive.sh
index aed8f4d..f26d7fd 100755
--- a/t/t4257-am-interactive.sh
+++ b/t/t4257-am-interactive.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='am --interactive tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'set up patches to apply' '
diff --git a/t/t4258-am-quoted-cr.sh b/t/t4258-am-quoted-cr.sh
index 201915b..3573c91 100755
--- a/t/t4258-am-quoted-cr.sh
+++ b/t/t4258-am-quoted-cr.sh
@@ -2,6 +2,7 @@
test_description='test am --quoted-cr=<action>'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
DATA="$TEST_DIRECTORY/t4258"
diff --git a/t/t4258/mbox b/t/t4258/mbox
index c62819f..1ae528b 100644
--- a/t/t4258/mbox
+++ b/t/t4258/mbox
@@ -2,7 +2,7 @@ From: A U Thor <mail@example.com>
To: list@example.org
Subject: [PATCH v2] sample
Date: Mon, 3 Aug 2020 22:40:55 +0700
-Message-Id: <msg-id@example.com>
+Message-ID: <msg-id@example.com>
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: base64
diff --git a/t/t4300-merge-tree.sh b/t/t4300-merge-tree.sh
index e59601e..9c19726 100755
--- a/t/t4300-merge-tree.sh
+++ b/t/t4300-merge-tree.sh
@@ -4,6 +4,8 @@
#
test_description='git merge-tree'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -84,6 +86,33 @@ EXPECTED
test_cmp expected actual
'
+test_expect_success '3-way merge with --attr-source' '
+ test_when_finished rm -rf 3-way &&
+ git init 3-way &&
+ (
+ cd 3-way &&
+ test_commit initial file1 foo &&
+ base=$(git rev-parse HEAD) &&
+ git checkout -b brancha &&
+ echo bar >>file1 &&
+ git commit -am "adding bar" &&
+ source=$(git rev-parse HEAD) &&
+ git checkout @{-1} &&
+ git checkout -b branchb &&
+ echo baz >>file1 &&
+ git commit -am "adding baz" &&
+ merge=$(git rev-parse HEAD) &&
+ git checkout -b gitattributes &&
+ test_commit "gitattributes" .gitattributes "file1 merge=union" &&
+ git checkout @{-1} &&
+ tree=$(git --attr-source=gitattributes merge-tree --write-tree \
+ --merge-base "$base" --end-of-options "$source" "$merge") &&
+ test_write_lines foo bar baz >expect &&
+ git cat-file -p "$tree:file1" >actual &&
+ test_cmp expect actual
+ )
+'
+
test_expect_success 'file change A, B (same)' '
git reset --hard initial &&
test_commit "change-a-b-same-A" "initial-file" "AAA" &&
@@ -332,4 +361,22 @@ test_expect_success 'turn tree to file' '
test_cmp expect actual
'
+test_expect_success 'merge-tree respects core.useReplaceRefs=false' '
+ test_commit merge-to &&
+ test_commit valid base &&
+ git reset --hard HEAD^ &&
+ test_commit malicious base &&
+
+ test_when_finished "git replace -d $(git rev-parse valid^0)" &&
+ git replace valid^0 malicious^0 &&
+
+ tree=$(git -c core.useReplaceRefs=true merge-tree --write-tree merge-to valid) &&
+ merged=$(git cat-file -p $tree:base) &&
+ test malicious = $merged &&
+
+ tree=$(git -c core.useReplaceRefs=false merge-tree --write-tree merge-to valid) &&
+ merged=$(git cat-file -p $tree:base) &&
+ test valid = $merged
+'
+
test_done
diff --git a/t/t4301-merge-tree-write-tree.sh b/t/t4301-merge-tree-write-tree.sh
new file mode 100755
index 0000000..eea1990
--- /dev/null
+++ b/t/t4301-merge-tree-write-tree.sh
@@ -0,0 +1,993 @@
+#!/bin/sh
+
+test_description='git merge-tree --write-tree'
+
+. ./test-lib.sh
+
+# This test is ort-specific
+if test "$GIT_TEST_MERGE_ALGORITHM" != "ort"
+then
+ skip_all="GIT_TEST_MERGE_ALGORITHM != ort"
+ test_done
+fi
+
+test_expect_success setup '
+ test_write_lines 1 2 3 4 5 >numbers &&
+ echo hello >greeting &&
+ echo foo >whatever &&
+ git add numbers greeting whatever &&
+ test_tick &&
+ git commit -m initial &&
+
+ git branch side1 &&
+ git branch side2 &&
+ git branch side3 &&
+ git branch side4 &&
+
+ git checkout side1 &&
+ test_write_lines 1 2 3 4 5 6 >numbers &&
+ echo hi >greeting &&
+ echo bar >whatever &&
+ git add numbers greeting whatever &&
+ test_tick &&
+ git commit -m modify-stuff &&
+
+ git checkout side2 &&
+ test_write_lines 0 1 2 3 4 5 >numbers &&
+ echo yo >greeting &&
+ git rm whatever &&
+ mkdir whatever &&
+ >whatever/empty &&
+ git add numbers greeting whatever/empty &&
+ test_tick &&
+ git commit -m other-modifications &&
+
+ git checkout side3 &&
+ git mv numbers sequence &&
+ test_tick &&
+ git commit -m rename-numbers &&
+
+ git checkout side4 &&
+ test_write_lines 0 1 2 3 4 5 >numbers &&
+ echo yo >greeting &&
+ git add numbers greeting &&
+ test_tick &&
+ git commit -m other-content-modifications &&
+
+ git switch --orphan unrelated &&
+ >something-else &&
+ git add something-else &&
+ test_tick &&
+ git commit -m first-commit
+'
+
+test_expect_success 'Clean merge' '
+ TREE_OID=$(git merge-tree --write-tree side1 side3) &&
+ q_to_tab <<-EOF >expect &&
+ 100644 blob $(git rev-parse side1:greeting)Qgreeting
+ 100644 blob $(git rev-parse side1:numbers)Qsequence
+ 100644 blob $(git rev-parse side1:whatever)Qwhatever
+ EOF
+
+ git ls-tree $TREE_OID >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Content merge and a few conflicts' '
+ git checkout side1^0 &&
+ test_must_fail git merge side2 &&
+ expected_tree=$(git rev-parse AUTO_MERGE) &&
+
+ # We will redo the merge, while we are still in a conflicted state!
+ git ls-files -u >conflicted-file-info &&
+ test_when_finished "git reset --hard" &&
+
+ test_expect_code 1 git merge-tree --write-tree side1 side2 >RESULT &&
+ actual_tree=$(head -n 1 RESULT) &&
+
+ # Due to differences of e.g. "HEAD" vs "side1", the results will not
+ # exactly match. Dig into individual files.
+
+ # Numbers should have three-way merged cleanly
+ test_write_lines 0 1 2 3 4 5 6 >expect &&
+ git show ${actual_tree}:numbers >actual &&
+ test_cmp expect actual &&
+
+ # whatever and whatever~<branch> should have same HASHES
+ git rev-parse ${expected_tree}:whatever ${expected_tree}:whatever~HEAD >expect &&
+ git rev-parse ${actual_tree}:whatever ${actual_tree}:whatever~side1 >actual &&
+ test_cmp expect actual &&
+
+ # greeting should have a merge conflict
+ git show ${expected_tree}:greeting >tmp &&
+ sed -e s/HEAD/side1/ tmp >expect &&
+ git show ${actual_tree}:greeting >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Auto resolve conflicts by "ours" strategy option' '
+ git checkout side1^0 &&
+
+ # make sure merge conflict exists
+ test_must_fail git merge side4 &&
+ git merge --abort &&
+
+ git merge -X ours side4 &&
+ git rev-parse HEAD^{tree} >expected &&
+
+ git merge-tree -X ours side1 side4 >actual &&
+
+ test_cmp expected actual
+'
+
+test_expect_success 'Barf on misspelled option, with exit code other than 0 or 1' '
+ # Mis-spell with single "s" instead of double "s"
+ test_expect_code 129 git merge-tree --write-tree --mesages FOOBAR side1 side2 2>expect &&
+
+ grep "error: unknown option.*mesages" expect
+'
+
+test_expect_success 'Barf on too many arguments' '
+ test_expect_code 129 git merge-tree --write-tree side1 side2 invalid 2>expect &&
+
+ grep "^usage: git merge-tree" expect
+'
+
+anonymize_hash() {
+ sed -e "s/[0-9a-f]\{40,\}/HASH/g" "$@"
+}
+
+test_expect_success 'test conflict notices and such' '
+ test_expect_code 1 git merge-tree --write-tree --name-only side1 side2 >out &&
+ anonymize_hash out >actual &&
+
+ # Expected results:
+ # "greeting" should merge with conflicts
+ # "numbers" should merge cleanly
+ # "whatever" has *both* a modify/delete and a file/directory conflict
+ cat <<-EOF >expect &&
+ HASH
+ greeting
+ whatever~side1
+
+ Auto-merging greeting
+ CONFLICT (content): Merge conflict in greeting
+ Auto-merging numbers
+ CONFLICT (file/directory): directory in the way of whatever from side1; moving it to whatever~side1 instead.
+ CONFLICT (modify/delete): whatever~side1 deleted in side2 and modified in side1. Version side1 of whatever~side1 left in tree.
+ EOF
+
+ test_cmp expect actual
+'
+
+# directory rename + content conflict
+# Commit O: foo, olddir/{a,b,c}
+# Commit A: modify foo, newdir/{a,b,c}
+# Commit B: modify foo differently & rename foo -> olddir/bar
+# Expected: CONFLICT(content) for newdir/bar (not olddir/bar or foo)
+
+test_expect_success 'directory rename + content conflict' '
+ # Setup
+ git init dir-rename-and-content &&
+ (
+ cd dir-rename-and-content &&
+ test_write_lines 1 2 3 4 5 >foo &&
+ mkdir olddir &&
+ for i in a b c; do echo $i >olddir/$i || exit 1; done &&
+ git add foo olddir &&
+ git commit -m "original" &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git checkout A &&
+ test_write_lines 1 2 3 4 5 6 >foo &&
+ git add foo &&
+ git mv olddir newdir &&
+ git commit -m "Modify foo, rename olddir to newdir" &&
+
+ git checkout B &&
+ test_write_lines 1 2 3 4 5 six >foo &&
+ git add foo &&
+ git mv foo olddir/bar &&
+ git commit -m "Modify foo & rename foo -> olddir/bar"
+ ) &&
+ # Testing
+ (
+ cd dir-rename-and-content &&
+
+ test_expect_code 1 \
+ git merge-tree -z A^0 B^0 >out &&
+ echo >>out &&
+ anonymize_hash out >actual &&
+ q_to_tab <<-\EOF | lf_to_nul >expect &&
+ HASH
+ 100644 HASH 1Qnewdir/bar
+ 100644 HASH 2Qnewdir/bar
+ 100644 HASH 3Qnewdir/bar
+ EOF
+
+ q_to_nul <<-EOF >>expect &&
+ Q2Qnewdir/barQolddir/barQCONFLICT (directory rename suggested)QCONFLICT (file location): foo renamed to olddir/bar in B^0, inside a directory that was renamed in A^0, suggesting it should perhaps be moved to newdir/bar.
+ Q1Qnewdir/barQAuto-mergingQAuto-merging newdir/bar
+ Q1Qnewdir/barQCONFLICT (contents)QCONFLICT (content): Merge conflict in newdir/bar
+ Q
+ EOF
+ test_cmp expect actual
+ )
+'
+
+# rename/delete + modify/delete handling
+# Commit O: foo
+# Commit A: modify foo + rename to bar
+# Commit B: delete foo
+# Expected: CONFLICT(rename/delete) + CONFLICT(modify/delete)
+
+test_expect_success 'rename/delete handling' '
+ # Setup
+ git init rename-delete &&
+ (
+ cd rename-delete &&
+ test_write_lines 1 2 3 4 5 >foo &&
+ git add foo &&
+ git commit -m "original" &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git checkout A &&
+ test_write_lines 1 2 3 4 5 6 >foo &&
+ git add foo &&
+ git mv foo bar &&
+ git commit -m "Modify foo, rename to bar" &&
+
+ git checkout B &&
+ git rm foo &&
+ git commit -m "remove foo"
+ ) &&
+ # Testing
+ (
+ cd rename-delete &&
+
+ test_expect_code 1 \
+ git merge-tree -z A^0 B^0 >out &&
+ echo >>out &&
+ anonymize_hash out >actual &&
+ q_to_tab <<-\EOF | lf_to_nul >expect &&
+ HASH
+ 100644 HASH 1Qbar
+ 100644 HASH 2Qbar
+ EOF
+
+ q_to_nul <<-EOF >>expect &&
+ Q2QbarQfooQCONFLICT (rename/delete)QCONFLICT (rename/delete): foo renamed to bar in A^0, but deleted in B^0.
+ Q1QbarQCONFLICT (modify/delete)QCONFLICT (modify/delete): bar deleted in B^0 and modified in A^0. Version A^0 of bar left in tree.
+ Q
+ EOF
+ test_cmp expect actual
+ )
+'
+
+# rename/add handling
+# Commit O: foo
+# Commit A: modify foo, add different bar
+# Commit B: modify & rename foo->bar
+# Expected: CONFLICT(add/add) [via rename collide] for bar
+
+test_expect_success 'rename/add handling' '
+ # Setup
+ git init rename-add &&
+ (
+ cd rename-add &&
+ test_write_lines original 1 2 3 4 5 >foo &&
+ git add foo &&
+ git commit -m "original" &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git checkout A &&
+ test_write_lines 1 2 3 4 5 >foo &&
+ echo "different file" >bar &&
+ git add foo bar &&
+ git commit -m "Modify foo, add bar" &&
+
+ git checkout B &&
+ test_write_lines original 1 2 3 4 5 6 >foo &&
+ git add foo &&
+ git mv foo bar &&
+ git commit -m "rename foo to bar"
+ ) &&
+ # Testing
+ (
+ cd rename-add &&
+
+ test_expect_code 1 \
+ git merge-tree -z A^0 B^0 >out &&
+ echo >>out &&
+
+ #
+ # First, check that the bar that appears at stage 3 does not
+ # correspond to an individual blob anywhere in history
+ #
+ hash=$(tr "\0" "\n" <out | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
+ git rev-list --objects --all >all_blobs &&
+ ! grep $hash all_blobs &&
+
+ #
+ # Second, check anonymized hash output against expectation
+ #
+ anonymize_hash out >actual &&
+ q_to_tab <<-\EOF | lf_to_nul >expect &&
+ HASH
+ 100644 HASH 2Qbar
+ 100644 HASH 3Qbar
+ EOF
+
+ q_to_nul <<-EOF >>expect &&
+ Q1QbarQAuto-mergingQAuto-merging bar
+ Q1QbarQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in bar
+ Q1QfooQAuto-mergingQAuto-merging foo
+ Q
+ EOF
+ test_cmp expect actual
+ )
+'
+
+# rename/add, where add is a mode conflict
+# Commit O: foo
+# Commit A: modify foo, add symlink bar
+# Commit B: modify & rename foo->bar
+# Expected: CONFLICT(distinct modes) for bar
+
+test_expect_success SYMLINKS 'rename/add, where add is a mode conflict' '
+ # Setup
+ git init rename-add-symlink &&
+ (
+ cd rename-add-symlink &&
+ test_write_lines original 1 2 3 4 5 >foo &&
+ git add foo &&
+ git commit -m "original" &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git checkout A &&
+ test_write_lines 1 2 3 4 5 >foo &&
+ ln -s foo bar &&
+ git add foo bar &&
+ git commit -m "Modify foo, add symlink bar" &&
+
+ git checkout B &&
+ test_write_lines original 1 2 3 4 5 6 >foo &&
+ git add foo &&
+ git mv foo bar &&
+ git commit -m "rename foo to bar"
+ ) &&
+ # Testing
+ (
+ cd rename-add-symlink &&
+
+ test_expect_code 1 \
+ git merge-tree -z A^0 B^0 >out &&
+ echo >>out &&
+
+ #
+ # First, check that the bar that appears at stage 3 does not
+ # correspond to an individual blob anywhere in history
+ #
+ hash=$(tr "\0" "\n" <out | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
+ git rev-list --objects --all >all_blobs &&
+ ! grep $hash all_blobs &&
+
+ #
+ # Second, check anonymized hash output against expectation
+ #
+ anonymize_hash out >actual &&
+ q_to_tab <<-\EOF | lf_to_nul >expect &&
+ HASH
+ 120000 HASH 2Qbar
+ 100644 HASH 3Qbar~B^0
+ EOF
+
+ q_to_nul <<-EOF >>expect &&
+ Q2QbarQbar~B^0QCONFLICT (distinct modes)QCONFLICT (distinct types): bar had different types on each side; renamed one of them so each can be recorded somewhere.
+ Q1QfooQAuto-mergingQAuto-merging foo
+ Q
+ EOF
+ test_cmp expect actual
+ )
+'
+
+# rename/rename(1to2) + content conflict handling
+# Commit O: foo
+# Commit A: modify foo & rename to bar
+# Commit B: modify foo & rename to baz
+# Expected: CONFLICT(rename/rename)
+
+test_expect_success 'rename/rename + content conflict' '
+ # Setup
+ git init rr-plus-content &&
+ (
+ cd rr-plus-content &&
+ test_write_lines 1 2 3 4 5 >foo &&
+ git add foo &&
+ git commit -m "original" &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git checkout A &&
+ test_write_lines 1 2 3 4 5 six >foo &&
+ git add foo &&
+ git mv foo bar &&
+ git commit -m "Modify foo + rename to bar" &&
+
+ git checkout B &&
+ test_write_lines 1 2 3 4 5 6 >foo &&
+ git add foo &&
+ git mv foo baz &&
+ git commit -m "Modify foo + rename to baz"
+ ) &&
+ # Testing
+ (
+ cd rr-plus-content &&
+
+ test_expect_code 1 \
+ git merge-tree -z A^0 B^0 >out &&
+ echo >>out &&
+ anonymize_hash out >actual &&
+ q_to_tab <<-\EOF | lf_to_nul >expect &&
+ HASH
+ 100644 HASH 2Qbar
+ 100644 HASH 3Qbaz
+ 100644 HASH 1Qfoo
+ EOF
+
+ q_to_nul <<-EOF >>expect &&
+ Q1QfooQAuto-mergingQAuto-merging foo
+ Q3QfooQbarQbazQCONFLICT (rename/rename)QCONFLICT (rename/rename): foo renamed to bar in A^0 and to baz in B^0.
+ Q
+ EOF
+ test_cmp expect actual
+ )
+'
+
+# rename/add/delete
+# Commit O: foo
+# Commit A: rm foo, add different bar
+# Commit B: rename foo->bar
+# Expected: CONFLICT (rename/delete), CONFLICT(add/add) [via rename collide]
+# for bar
+
+test_expect_success 'rename/add/delete conflict' '
+ # Setup
+ git init rad &&
+ (
+ cd rad &&
+ echo "original file" >foo &&
+ git add foo &&
+ git commit -m "original" &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git checkout A &&
+ git rm foo &&
+ echo "different file" >bar &&
+ git add bar &&
+ git commit -m "Remove foo, add bar" &&
+
+ git checkout B &&
+ git mv foo bar &&
+ git commit -m "rename foo to bar"
+ ) &&
+ # Testing
+ (
+ cd rad &&
+
+ test_expect_code 1 \
+ git merge-tree -z B^0 A^0 >out &&
+ echo >>out &&
+ anonymize_hash out >actual &&
+
+ q_to_tab <<-\EOF | lf_to_nul >expect &&
+ HASH
+ 100644 HASH 2Qbar
+ 100644 HASH 3Qbar
+
+ EOF
+
+ q_to_nul <<-EOF >>expect &&
+ 2QbarQfooQCONFLICT (rename/delete)QCONFLICT (rename/delete): foo renamed to bar in B^0, but deleted in A^0.
+ Q1QbarQAuto-mergingQAuto-merging bar
+ Q1QbarQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in bar
+ Q
+ EOF
+ test_cmp expect actual
+ )
+'
+
+# rename/rename(2to1)/delete/delete
+# Commit O: foo, bar
+# Commit A: rename foo->baz, rm bar
+# Commit B: rename bar->baz, rm foo
+# Expected: 2x CONFLICT (rename/delete), CONFLICT (add/add) via colliding
+# renames for baz
+
+test_expect_success 'rename/rename(2to1)/delete/delete conflict' '
+ # Setup
+ git init rrdd &&
+ (
+ cd rrdd &&
+ echo foo >foo &&
+ echo bar >bar &&
+ git add foo bar &&
+ git commit -m O &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git checkout A &&
+ git mv foo baz &&
+ git rm bar &&
+ git commit -m "Rename foo, remove bar" &&
+
+ git checkout B &&
+ git mv bar baz &&
+ git rm foo &&
+ git commit -m "Rename bar, remove foo"
+ ) &&
+ # Testing
+ (
+ cd rrdd &&
+
+ test_expect_code 1 \
+ git merge-tree -z A^0 B^0 >out &&
+ echo >>out &&
+ anonymize_hash out >actual &&
+
+ q_to_tab <<-\EOF | lf_to_nul >expect &&
+ HASH
+ 100644 HASH 2Qbaz
+ 100644 HASH 3Qbaz
+
+ EOF
+
+ q_to_nul <<-EOF >>expect &&
+ 2QbazQbarQCONFLICT (rename/delete)QCONFLICT (rename/delete): bar renamed to baz in B^0, but deleted in A^0.
+ Q2QbazQfooQCONFLICT (rename/delete)QCONFLICT (rename/delete): foo renamed to baz in A^0, but deleted in B^0.
+ Q1QbazQAuto-mergingQAuto-merging baz
+ Q1QbazQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in baz
+ Q
+ EOF
+ test_cmp expect actual
+ )
+'
+
+# mod6: chains of rename/rename(1to2) + add/add via colliding renames
+# Commit O: one, three, five
+# Commit A: one->two, three->four, five->six
+# Commit B: one->six, three->two, five->four
+# Expected: three CONFLICT(rename/rename) messages + three CONFLICT(add/add)
+# messages; each path in two of the multi-way merged contents
+# found in two, four, six
+
+test_expect_success 'mod6: chains of rename/rename(1to2) and add/add via colliding renames' '
+ # Setup
+ git init mod6 &&
+ (
+ cd mod6 &&
+ test_seq 11 19 >one &&
+ test_seq 31 39 >three &&
+ test_seq 51 59 >five &&
+ git add . &&
+ test_tick &&
+ git commit -m "O" &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git checkout A &&
+ test_seq 10 19 >one &&
+ echo 40 >>three &&
+ git add one three &&
+ git mv one two &&
+ git mv three four &&
+ git mv five six &&
+ test_tick &&
+ git commit -m "A" &&
+
+ git checkout B &&
+ echo 20 >>one &&
+ echo forty >>three &&
+ echo 60 >>five &&
+ git add one three five &&
+ git mv one six &&
+ git mv three two &&
+ git mv five four &&
+ test_tick &&
+ git commit -m "B"
+ ) &&
+ # Testing
+ (
+ cd mod6 &&
+
+ test_expect_code 1 \
+ git merge-tree -z A^0 B^0 >out &&
+ echo >>out &&
+
+ #
+ # First, check that some of the hashes that appear as stage
+ # conflict entries do not appear as individual blobs anywhere
+ # in history.
+ #
+ hash1=$(tr "\0" "\n" <out | head | grep 2.four | cut -f 2 -d " ") &&
+ hash2=$(tr "\0" "\n" <out | head | grep 3.two | cut -f 2 -d " ") &&
+ git rev-list --objects --all >all_blobs &&
+ ! grep $hash1 all_blobs &&
+ ! grep $hash2 all_blobs &&
+
+ #
+ # Now compare anonymized hash output with expectation
+ #
+ anonymize_hash out >actual &&
+ q_to_tab <<-\EOF | lf_to_nul >expect &&
+ HASH
+ 100644 HASH 1Qfive
+ 100644 HASH 2Qfour
+ 100644 HASH 3Qfour
+ 100644 HASH 1Qone
+ 100644 HASH 2Qsix
+ 100644 HASH 3Qsix
+ 100644 HASH 1Qthree
+ 100644 HASH 2Qtwo
+ 100644 HASH 3Qtwo
+
+ EOF
+
+ q_to_nul <<-EOF >>expect &&
+ 3QfiveQsixQfourQCONFLICT (rename/rename)QCONFLICT (rename/rename): five renamed to six in A^0 and to four in B^0.
+ Q1QfourQAuto-mergingQAuto-merging four
+ Q1QfourQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in four
+ Q1QoneQAuto-mergingQAuto-merging one
+ Q3QoneQtwoQsixQCONFLICT (rename/rename)QCONFLICT (rename/rename): one renamed to two in A^0 and to six in B^0.
+ Q1QsixQAuto-mergingQAuto-merging six
+ Q1QsixQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in six
+ Q1QthreeQAuto-mergingQAuto-merging three
+ Q3QthreeQfourQtwoQCONFLICT (rename/rename)QCONFLICT (rename/rename): three renamed to four in A^0 and to two in B^0.
+ Q1QtwoQAuto-mergingQAuto-merging two
+ Q1QtwoQCONFLICT (contents)QCONFLICT (add/add): Merge conflict in two
+ Q
+ EOF
+ test_cmp expect actual
+ )
+'
+
+# directory rename + rename/delete + modify/delete + directory/file conflict
+# Commit O: foo, olddir/{a,b,c}
+# Commit A: delete foo, rename olddir/ -> newdir/, add newdir/bar/file
+# Commit B: modify foo & rename foo -> olddir/bar
+# Expected: CONFLICT(content) for newdir/bar (not olddir/bar or foo)
+
+test_expect_success 'directory rename + rename/delete + modify/delete + directory/file conflict' '
+ # Setup
+ git init 4-stacked-conflict &&
+ (
+ cd 4-stacked-conflict &&
+ test_write_lines 1 2 3 4 5 >foo &&
+ mkdir olddir &&
+ for i in a b c; do echo $i >olddir/$i || exit 1; done &&
+ git add foo olddir &&
+ git commit -m "original" &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git checkout A &&
+ git rm foo &&
+ git mv olddir newdir &&
+ mkdir newdir/bar &&
+ >newdir/bar/file &&
+ git add newdir/bar/file &&
+ git commit -m "rm foo, olddir/ -> newdir/, + newdir/bar/file" &&
+
+ git checkout B &&
+ test_write_lines 1 2 3 4 5 6 >foo &&
+ git add foo &&
+ git mv foo olddir/bar &&
+ git commit -m "Modify foo & rename foo -> olddir/bar"
+ ) &&
+ # Testing
+ (
+ cd 4-stacked-conflict &&
+
+ test_expect_code 1 \
+ git merge-tree -z A^0 B^0 >out &&
+ echo >>out &&
+ anonymize_hash out >actual &&
+
+ q_to_tab <<-\EOF | lf_to_nul >expect &&
+ HASH
+ 100644 HASH 1Qnewdir/bar~B^0
+ 100644 HASH 3Qnewdir/bar~B^0
+ EOF
+
+ q_to_nul <<-EOF >>expect &&
+ Q2Qnewdir/barQolddir/barQCONFLICT (directory rename suggested)QCONFLICT (file location): foo renamed to olddir/bar in B^0, inside a directory that was renamed in A^0, suggesting it should perhaps be moved to newdir/bar.
+ Q2Qnewdir/barQfooQCONFLICT (rename/delete)QCONFLICT (rename/delete): foo renamed to newdir/bar in B^0, but deleted in A^0.
+ Q2Qnewdir/bar~B^0Qnewdir/barQCONFLICT (file/directory)QCONFLICT (file/directory): directory in the way of newdir/bar from B^0; moving it to newdir/bar~B^0 instead.
+ Q1Qnewdir/bar~B^0QCONFLICT (modify/delete)QCONFLICT (modify/delete): newdir/bar~B^0 deleted in A^0 and modified in B^0. Version B^0 of newdir/bar~B^0 left in tree.
+ Q
+ EOF
+ test_cmp expect actual
+ )
+'
+
+for opt in $(git merge-tree --git-completion-helper-all)
+do
+ if test $opt = "--trivial-merge" || test $opt = "--write-tree"
+ then
+ continue
+ fi
+
+ test_expect_success "usage: --trivial-merge is incompatible with $opt" '
+ test_expect_code 128 git merge-tree --trivial-merge $opt side1 side2 side3
+ '
+done
+
+test_expect_success 'Just the conflicted files without the messages' '
+ test_expect_code 1 git merge-tree --write-tree --no-messages --name-only side1 side2 >out &&
+ anonymize_hash out >actual &&
+
+ test_write_lines HASH greeting whatever~side1 >expect &&
+
+ test_cmp expect actual
+'
+
+test_expect_success 'Check conflicted oids and modes without messages' '
+ test_expect_code 1 git merge-tree --write-tree --no-messages side1 side2 >out &&
+ anonymize_hash out >actual &&
+
+ # Compare the basic output format
+ q_to_tab >expect <<-\EOF &&
+ HASH
+ 100644 HASH 1Qgreeting
+ 100644 HASH 2Qgreeting
+ 100644 HASH 3Qgreeting
+ 100644 HASH 1Qwhatever~side1
+ 100644 HASH 2Qwhatever~side1
+ EOF
+
+ test_cmp expect actual &&
+
+ # Check the actual hashes against the `ls-files -u` output too
+ tail -n +2 out | sed -e s/side1/HEAD/ >actual &&
+ test_cmp conflicted-file-info actual
+'
+
+test_expect_success 'NUL terminated conflicted file "lines"' '
+ git checkout -b tweak1 side1 &&
+ test_write_lines zero 1 2 3 4 5 6 >numbers &&
+ git add numbers &&
+ git mv numbers "Αυτά μου φαίνονται κινέζικα" &&
+ git commit -m "Renamed numbers" &&
+
+ test_expect_code 1 git merge-tree --write-tree -z tweak1 side2 >out &&
+ echo >>out &&
+ anonymize_hash out >actual &&
+
+ # Expected results:
+ # "greeting" should merge with conflicts
+ # "whatever" has *both* a modify/delete and a file/directory conflict
+ # "Αυτά μου φαίνονται κινέζικα" should have a conflict
+ echo HASH | lf_to_nul >expect &&
+
+ q_to_tab <<-EOF | lf_to_nul >>expect &&
+ 100644 HASH 1Qgreeting
+ 100644 HASH 2Qgreeting
+ 100644 HASH 3Qgreeting
+ 100644 HASH 1Qwhatever~tweak1
+ 100644 HASH 2Qwhatever~tweak1
+ 100644 HASH 1QΑυτά μου φαίνονται κινέζικα
+ 100644 HASH 2QΑυτά μου φαίνονται κινέζικα
+ 100644 HASH 3QΑυτά μου φαίνονται κινέζικα
+
+ EOF
+
+ q_to_nul <<-EOF >>expect &&
+ 1QgreetingQAuto-mergingQAuto-merging greeting
+ Q1QgreetingQCONFLICT (contents)QCONFLICT (content): Merge conflict in greeting
+ Q2Qwhatever~tweak1QwhateverQCONFLICT (file/directory)QCONFLICT (file/directory): directory in the way of whatever from tweak1; moving it to whatever~tweak1 instead.
+ Q1Qwhatever~tweak1QCONFLICT (modify/delete)QCONFLICT (modify/delete): whatever~tweak1 deleted in side2 and modified in tweak1. Version tweak1 of whatever~tweak1 left in tree.
+ Q1QΑυτά μου φαίνονται κινέζικαQAuto-mergingQAuto-merging Αυτά μου φαίνονται κινέζικα
+ Q1QΑυτά μου φαίνονται κινέζικαQCONFLICT (contents)QCONFLICT (content): Merge conflict in Αυτά μου φαίνονται κινέζικα
+ Q
+ EOF
+
+ test_cmp expect actual
+'
+
+test_expect_success 'error out by default for unrelated histories' '
+ test_expect_code 128 git merge-tree --write-tree side1 unrelated 2>error &&
+
+ grep "refusing to merge unrelated histories" error
+'
+
+test_expect_success 'can override merge of unrelated histories' '
+ git merge-tree --write-tree --allow-unrelated-histories side1 unrelated >tree &&
+ TREE=$(cat tree) &&
+
+ git rev-parse side1:numbers side1:greeting side1:whatever unrelated:something-else >expect &&
+ git rev-parse $TREE:numbers $TREE:greeting $TREE:whatever $TREE:something-else >actual &&
+
+ test_cmp expect actual
+'
+
+test_expect_success SANITY 'merge-ort fails gracefully in a read-only repository' '
+ git init --bare read-only &&
+ git push read-only side1 side2 side3 &&
+ test_when_finished "chmod -R u+w read-only" &&
+ chmod -R a-w read-only &&
+ test_must_fail git -C read-only merge-tree side1 side3 &&
+ test_must_fail git -C read-only merge-tree side1 side2
+'
+
+test_expect_success '--stdin with both a successful and a conflicted merge' '
+ printf "side1 side3\nside1 side2" | git merge-tree --stdin >actual &&
+
+ git checkout side1^0 &&
+ git merge side3 &&
+
+ printf "1\0" >expect &&
+ git rev-parse HEAD^{tree} | lf_to_nul >>expect &&
+ printf "\0" >>expect &&
+
+ git checkout side1^0 &&
+ test_must_fail git merge side2 &&
+ sed s/HEAD/side1/ greeting >tmp &&
+ mv tmp greeting &&
+ git add -u &&
+ git mv whatever~HEAD whatever~side1 &&
+
+ printf "0\0" >>expect &&
+ git write-tree | lf_to_nul >>expect &&
+
+ cat <<-EOF | q_to_tab | lf_to_nul >>expect &&
+ 100644 $(git rev-parse side1~1:greeting) 1Qgreeting
+ 100644 $(git rev-parse side1:greeting) 2Qgreeting
+ 100644 $(git rev-parse side2:greeting) 3Qgreeting
+ 100644 $(git rev-parse side1~1:whatever) 1Qwhatever~side1
+ 100644 $(git rev-parse side1:whatever) 2Qwhatever~side1
+ EOF
+
+ q_to_nul <<-EOF >>expect &&
+ Q1QgreetingQAuto-mergingQAuto-merging greeting
+ Q1QgreetingQCONFLICT (contents)QCONFLICT (content): Merge conflict in greeting
+ Q1QnumbersQAuto-mergingQAuto-merging numbers
+ Q2Qwhatever~side1QwhateverQCONFLICT (file/directory)QCONFLICT (file/directory): directory in the way of whatever from side1; moving it to whatever~side1 instead.
+ Q1Qwhatever~side1QCONFLICT (modify/delete)QCONFLICT (modify/delete): whatever~side1 deleted in side2 and modified in side1. Version side1 of whatever~side1 left in tree.
+ EOF
+
+ printf "\0\0" >>expect &&
+
+ test_cmp expect actual
+'
+
+
+test_expect_success '--merge-base is incompatible with --stdin' '
+ test_must_fail git merge-tree --merge-base=side1 --stdin 2>expect &&
+
+ grep "^fatal: .*merge-base.*stdin.* cannot be used together" expect
+'
+
+# specify merge-base as parent of branch2
+# git merge-tree --write-tree --merge-base=c2 c1 c3
+# Commit c1: add file1
+# Commit c2: add file2 after c1
+# Commit c3: add file3 after c2
+# Expected: add file3, and file2 does NOT appear
+
+test_expect_success 'specify merge-base as parent of branch2' '
+ # Setup
+ test_when_finished "rm -rf base-b2-p" &&
+ git init base-b2-p &&
+ test_commit -C base-b2-p c1 file1 &&
+ test_commit -C base-b2-p c2 file2 &&
+ test_commit -C base-b2-p c3 file3 &&
+
+ # Testing
+ TREE_OID=$(git -C base-b2-p merge-tree --write-tree --merge-base=c2 c1 c3) &&
+
+ q_to_tab <<-EOF >expect &&
+ 100644 blob $(git -C base-b2-p rev-parse c1:file1)Qfile1
+ 100644 blob $(git -C base-b2-p rev-parse c3:file3)Qfile3
+ EOF
+
+ git -C base-b2-p ls-tree $TREE_OID >actual &&
+ test_cmp expect actual
+'
+
+# Since the earlier tests have verified that individual merge-tree calls
+# are doing the right thing, this test case is only used to verify that
+# we can also trigger merges via --stdin, and that when we do we get
+# the same answer as running a bunch of separate merges.
+
+test_expect_success 'check the input format when --stdin is passed' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_commit -C repo c1 &&
+ test_commit -C repo c2 &&
+ test_commit -C repo c3 &&
+ printf "c1 c3\nc2 -- c1 c3\nc2 c3" | git -C repo merge-tree --stdin >actual &&
+
+ printf "1\0" >expect &&
+ git -C repo merge-tree --write-tree -z c1 c3 >>expect &&
+ printf "\0" >>expect &&
+
+ printf "1\0" >>expect &&
+ git -C repo merge-tree --write-tree -z --merge-base=c2 c1 c3 >>expect &&
+ printf "\0" >>expect &&
+
+ printf "1\0" >>expect &&
+ git -C repo merge-tree --write-tree -z c2 c3 >>expect &&
+ printf "\0" >>expect &&
+
+ test_cmp expect actual
+'
+
+test_expect_success '--merge-base with tree OIDs' '
+ git merge-tree --merge-base=side1^ side1 side3 >with-commits &&
+ git merge-tree --merge-base=side1^^{tree} side1^{tree} side3^{tree} >with-trees &&
+ test_cmp with-commits with-trees
+'
+
+test_expect_success 'error out on missing tree objects' '
+ git init --bare missing-tree.git &&
+ git rev-list side3 >list &&
+ git rev-parse side3^: >>list &&
+ git pack-objects missing-tree.git/objects/pack/side3-tree-is-missing <list &&
+ side3=$(git rev-parse side3) &&
+ test_must_fail git --git-dir=missing-tree.git merge-tree $side3^ $side3 >actual 2>err &&
+ test_grep "Could not read $(git rev-parse $side3:)" err &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'error out on missing blob objects' '
+ echo 1 | git hash-object -w --stdin >blob1 &&
+ echo 2 | git hash-object -w --stdin >blob2 &&
+ echo 3 | git hash-object -w --stdin >blob3 &&
+ printf "100644 blob $(cat blob1)\tblob\n" | git mktree >tree1 &&
+ printf "100644 blob $(cat blob2)\tblob\n" | git mktree >tree2 &&
+ printf "100644 blob $(cat blob3)\tblob\n" | git mktree >tree3 &&
+ git init --bare missing-blob.git &&
+ cat blob1 blob3 tree1 tree2 tree3 |
+ git pack-objects missing-blob.git/objects/pack/side1-whatever-is-missing &&
+ test_must_fail git --git-dir=missing-blob.git >actual 2>err \
+ merge-tree --merge-base=$(cat tree1) $(cat tree2) $(cat tree3) &&
+ test_grep "unable to read blob object $(cat blob2)" err &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'error out on missing commits as well' '
+ git init --bare missing-commit.git &&
+ git rev-list --objects side1 side3 >list-including-initial &&
+ grep -v ^$(git rev-parse side1^) <list-including-initial >list &&
+ git pack-objects missing-commit.git/objects/pack/missing-initial <list &&
+ side1=$(git rev-parse side1) &&
+ side3=$(git rev-parse side3) &&
+ test_must_fail git --git-dir=missing-commit.git \
+ merge-tree --allow-unrelated-histories $side1 $side3 >actual &&
+ test_must_be_empty actual
+'
+
+test_done
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 2c88d1c..72b8d0f 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -24,6 +24,7 @@ commit id embedding:
'
+TEST_CREATE_REPO_NO_TEMPLATE=1
. ./test-lib.sh
SUBSTFORMAT=%H%n
@@ -77,7 +78,7 @@ check_tar() {
path=$(get_pax_header $header path) &&
if test -n "$path"
then
- mv "$data" "$path"
+ mv "$data" "$path" || exit 1
fi
fi
done
@@ -104,6 +105,18 @@ check_added() {
'
}
+check_mtime() {
+ dir=$1
+ path_in_archive=$2
+ mtime=$3
+
+ test_expect_success " validate mtime of $path_in_archive" '
+ test-tool chmtime --get $dir/$path_in_archive >actual.mtime &&
+ echo $mtime >expect.mtime &&
+ test_cmp expect.mtime actual.mtime
+ '
+}
+
test_expect_success 'setup' '
test_oid_cache <<-EOF
obj sha1:19f9c8273ec45a8938e6999cb59b3ff66739902a
@@ -111,6 +124,16 @@ test_expect_success 'setup' '
EOF
'
+test_expect_success '--list notices extra parameters' '
+ test_must_fail git archive --list blah &&
+ test_must_fail git archive --remote=. --list blah
+'
+
+test_expect_success 'end-of-options is correctly eaten' '
+ git archive --list --end-of-options &&
+ git archive --remote=. --list --end-of-options
+'
+
test_expect_success 'populate workdir' '
mkdir a &&
echo simple textfile >a/a &&
@@ -133,7 +156,7 @@ test_expect_success 'populate workdir' '
for depth in 1 2 3 4 5
do
mkdir $p &&
- cd $p
+ cd $p || exit 1
done &&
echo text >file_with_long_path
) &&
@@ -143,6 +166,7 @@ test_expect_success 'populate workdir' '
test_expect_success \
'add ignored file' \
'echo ignore me >a/ignored &&
+ mkdir .git/info &&
echo ignored export-ignore >.git/info/attributes'
test_expect_success 'add files to repository' '
@@ -157,7 +181,8 @@ test_expect_success 'setup export-subst' '
'
test_expect_success 'create bare clone' '
- git clone --bare . bare.git &&
+ git clone --template= --bare . bare.git &&
+ mkdir bare.git/info &&
cp .git/info/attributes bare.git/info/attributes
'
@@ -170,6 +195,14 @@ test_expect_success 'git archive' '
'
check_tar b
+check_mtime b a/a 1117231200
+
+test_expect_success 'git archive --mtime' '
+ git archive --mtime=2002-02-02T02:02:02-0200 HEAD >with_mtime.tar
+'
+
+check_tar with_mtime
+check_mtime with_mtime a/a 1012622522
test_expect_success 'git archive --prefix=prefix/' '
git archive --prefix=prefix/ HEAD >with_prefix.tar
@@ -235,14 +268,6 @@ test_expect_success 'git archive --remote with configured remote' '
test_cmp_bin b.tar b5-nick.tar
'
-test_expect_success 'validate file modification time' '
- mkdir extract &&
- "$TAR" xf b.tar -C extract a/a &&
- test-tool chmtime --get extract/a/a >b.mtime &&
- echo "1117231200" >expected.mtime &&
- test_cmp expected.mtime b.mtime
-'
-
test_expect_success 'git get-tar-commit-id' '
git get-tar-commit-id <b.tar >actual &&
git rev-parse HEAD >expect &&
@@ -339,21 +364,28 @@ test_expect_success 'only enabled filters are available remotely' '
test_cmp_bin remote.bar config.bar
'
-test_expect_success GZIP 'git archive --format=tgz' '
+test_expect_success 'invalid filter is reported only once' '
+ test_must_fail git -c tar.invalid.command= archive --format=invalid \
+ HEAD >out 2>err &&
+ test_must_be_empty out &&
+ test_line_count = 1 err
+'
+
+test_expect_success 'git archive --format=tgz' '
git archive --format=tgz HEAD >j.tgz
'
-test_expect_success GZIP 'git archive --format=tar.gz' '
+test_expect_success 'git archive --format=tar.gz' '
git archive --format=tar.gz HEAD >j1.tar.gz &&
test_cmp_bin j.tgz j1.tar.gz
'
-test_expect_success GZIP 'infer tgz from .tgz filename' '
+test_expect_success 'infer tgz from .tgz filename' '
git archive --output=j2.tgz HEAD &&
test_cmp_bin j.tgz j2.tgz
'
-test_expect_success GZIP 'infer tgz from .tar.gz filename' '
+test_expect_success 'infer tgz from .tar.gz filename' '
git archive --output=j3.tar.gz HEAD &&
test_cmp_bin j.tgz j3.tar.gz
'
@@ -363,24 +395,40 @@ test_expect_success GZIP 'extract tgz file' '
test_cmp_bin b.tar j.tar
'
-test_expect_success GZIP 'remote tar.gz is allowed by default' '
+test_expect_success 'remote tar.gz is allowed by default' '
git archive --remote=. --format=tar.gz HEAD >remote.tar.gz &&
test_cmp_bin j.tgz remote.tar.gz
'
-test_expect_success GZIP 'remote tar.gz can be disabled' '
+test_expect_success 'remote tar.gz can be disabled' '
git config tar.tar.gz.remote false &&
test_must_fail git archive --remote=. --format=tar.gz HEAD \
>remote.tar.gz
'
+test_expect_success GZIP 'git archive --format=tgz (external gzip)' '
+ test_config tar.tgz.command "gzip -cn" &&
+ git archive --format=tgz HEAD >external_gzip.tgz
+'
+
+test_expect_success GZIP 'git archive --format=tar.gz (external gzip)' '
+ test_config tar.tar.gz.command "gzip -cn" &&
+ git archive --format=tar.gz HEAD >external_gzip.tar.gz &&
+ test_cmp_bin external_gzip.tgz external_gzip.tar.gz
+'
+
+test_expect_success GZIP 'extract tgz file (external gzip)' '
+ gzip -d -c <external_gzip.tgz >external_gzip.tar &&
+ test_cmp_bin b.tar external_gzip.tar
+'
+
test_expect_success 'archive and :(glob)' '
git archive -v HEAD -- ":(glob)**/sh" >/dev/null 2>actual &&
- cat >expect <<EOF &&
-a/
-a/bin/
-a/bin/sh
-EOF
+ cat >expect <<-\EOF &&
+ a/
+ a/bin/
+ a/bin/sh
+ EOF
test_cmp expect actual
'
@@ -388,6 +436,19 @@ test_expect_success 'catch non-matching pathspec' '
test_must_fail git archive -v HEAD -- "*.abc" >/dev/null
'
+test_expect_success 'reject paths outside the current directory' '
+ test_must_fail git -C a/bin archive HEAD .. >/dev/null 2>err &&
+ grep "outside the current directory" err
+'
+
+test_expect_success 'allow pathspecs that resolve to the current directory' '
+ git -C a/bin archive -v HEAD ../bin >/dev/null 2>actual &&
+ cat >expect <<-\EOF &&
+ sh
+ EOF
+ test_cmp expect actual
+'
+
# Pull the size and date of each entry in a tarfile using the system tar.
#
# We'll pull out only the year from the date; that avoids any question of
diff --git a/t/t5001-archive-attr.sh b/t/t5001-archive-attr.sh
index 712ae52..eaf959d 100755
--- a/t/t5001-archive-attr.sh
+++ b/t/t5001-archive-attr.sh
@@ -2,6 +2,8 @@
test_description='git archive attribute tests'
+TEST_CREATE_REPO_NO_TEMPLATE=1
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
SUBSTFORMAT='%H (%h)%n'
@@ -20,6 +22,7 @@ extract_tar_to_dir () {
test_expect_success 'setup' '
echo ignored >ignored &&
+ mkdir .git/info &&
echo ignored export-ignore >>.git/info/attributes &&
git add ignored &&
@@ -30,6 +33,13 @@ test_expect_success 'setup' '
echo ignored-by-tree.d export-ignore >>.gitattributes &&
git add ignored-by-tree ignored-by-tree.d .gitattributes &&
+ mkdir subdir &&
+ >subdir/included &&
+ >subdir/ignored-by-subtree &&
+ >subdir/ignored-by-tree &&
+ echo ignored-by-subtree export-ignore >subdir/.gitattributes &&
+ git add subdir &&
+
echo ignored by worktree >ignored-by-worktree &&
echo ignored-by-worktree export-ignore >.gitattributes &&
git add ignored-by-worktree &&
@@ -46,7 +56,8 @@ test_expect_success 'setup' '
git commit -m. &&
- git clone --bare . bare &&
+ git clone --template= --bare . bare &&
+ mkdir bare/info &&
cp .git/info/attributes bare/info/attributes
'
@@ -89,6 +100,15 @@ test_expect_exists archive-pathspec-wildcard/ignored-by-worktree
test_expect_missing archive-pathspec-wildcard/excluded-by-pathspec.d
test_expect_missing archive-pathspec-wildcard/excluded-by-pathspec.d/file
+test_expect_success 'git -C subdir archive' '
+ git -C subdir archive HEAD >archive-subdir.tar &&
+ extract_tar_to_dir archive-subdir
+'
+
+test_expect_exists archive-subdir/included
+test_expect_missing archive-subdir/ignored-by-subtree
+test_expect_missing archive-subdir/ignored-by-tree
+
test_expect_success 'git archive with worktree attributes' '
git archive --worktree-attributes HEAD >worktree.tar &&
(mkdir worktree && cd worktree && "$TAR" xf -) <worktree.tar
@@ -118,7 +138,7 @@ test_expect_success 'git archive with worktree attributes, bare' '
'
test_expect_missing bare-worktree/ignored
-test_expect_exists bare-worktree/ignored-by-tree
+test_expect_missing bare-worktree/ignored-by-tree
test_expect_exists bare-worktree/ignored-by-worktree
test_expect_success 'export-subst' '
diff --git a/t/t5002-archive-attr-pattern.sh b/t/t5002-archive-attr-pattern.sh
index bda6d7d..78ab75f 100755
--- a/t/t5002-archive-attr-pattern.sh
+++ b/t/t5002-archive-attr-pattern.sh
@@ -2,6 +2,8 @@
test_description='git archive attribute pattern tests'
+TEST_PASSES_SANITIZE_LEAK=true
+TEST_CREATE_REPO_NO_TEMPLATE=1
. ./test-lib.sh
test_expect_exists() {
@@ -14,6 +16,7 @@ test_expect_missing() {
test_expect_success 'setup' '
echo ignored >ignored &&
+ mkdir .git/info &&
echo ignored export-ignore >>.git/info/attributes &&
git add ignored &&
@@ -53,7 +56,8 @@ test_expect_success 'setup' '
git commit -m. &&
- git clone --bare . bare &&
+ git clone --template= --bare . bare &&
+ mkdir bare/info &&
cp .git/info/attributes bare/info/attributes
'
diff --git a/t/t5003-archive-zip.sh b/t/t5003-archive-zip.sh
index 1e6d18b..961c6aa 100755
--- a/t/t5003-archive-zip.sh
+++ b/t/t5003-archive-zip.sh
@@ -2,6 +2,7 @@
test_description='git archive --format=zip test'
+TEST_CREATE_REPO_NO_TEMPLATE=1
. ./test-lib.sh
SUBSTFORMAT=%H%n
@@ -106,7 +107,7 @@ test_expect_success \
printf "A\$Format:%s\$O" "$SUBSTFORMAT" >a/substfile1 &&
printf "A not substituted O" >a/substfile2 &&
(p=long_path_to_a_file && cd a &&
- for depth in 1 2 3 4 5; do mkdir $p && cd $p; done &&
+ for depth in 1 2 3 4 5; do mkdir $p && cd $p || exit 1; done &&
echo text >file_with_long_path)
'
@@ -121,6 +122,7 @@ test_expect_success 'prepare file list' '
test_expect_success \
'add ignored file' \
'echo ignore me >a/ignored &&
+ mkdir .git/info &&
echo ignored export-ignore >.git/info/attributes'
test_expect_success 'add files to repository' '
@@ -139,7 +141,8 @@ test_expect_success 'setup export-subst and diff attributes' '
'
test_expect_success 'create bare clone' '
- git clone --bare . bare.git &&
+ git clone --template= --bare . bare.git &&
+ mkdir bare.git/info &&
cp .git/info/attributes bare.git/info/attributes &&
# Recreate our changes to .git/config rather than just copying it, as
# we do not want to clobber core.bare or other settings.
@@ -206,6 +209,26 @@ test_expect_success 'git archive --format=zip --add-file' '
check_zip with_untracked
check_added with_untracked untracked untracked
+test_expect_success UNZIP 'git archive --format=zip --add-virtual-file' '
+ if test_have_prereq FUNNYNAMES
+ then
+ PATHNAME="pathname with : colon"
+ else
+ PATHNAME="pathname without colon"
+ fi &&
+ git archive --format=zip >with_file_with_content.zip \
+ --add-virtual-file=\""$PATHNAME"\": \
+ --add-virtual-file=hello:world $EMPTY_TREE &&
+ test_when_finished "rm -rf tmp-unpack" &&
+ mkdir tmp-unpack && (
+ cd tmp-unpack &&
+ "$GIT_UNZIP" ../with_file_with_content.zip &&
+ test_path_is_file hello &&
+ test_path_is_file "$PATHNAME" &&
+ test world = $(cat hello)
+ )
+'
+
test_expect_success 'git archive --format=zip --add-file twice' '
echo untracked >untracked &&
git archive --format=zip --prefix=one/ --add-file=untracked \
@@ -216,4 +239,38 @@ check_zip with_untracked2
check_added with_untracked2 untracked one/untracked
check_added with_untracked2 untracked two/untracked
+# Test remote archive over HTTP protocol.
+#
+# Note: this should be the last part of this test suite, because
+# by including lib-httpd.sh, the test may end early if httpd tests
+# should not be run.
+#
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success "setup for HTTP protocol" '
+ cp -R bare.git "$HTTPD_DOCUMENT_ROOT_PATH/bare.git" &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/bare.git" \
+ config http.uploadpack true &&
+ set_askpass user@host pass@host
+'
+
+setup_askpass_helper
+
+test_expect_success 'remote archive does not work with protocol v1' '
+ test_must_fail git -c protocol.version=1 archive \
+ --remote="$HTTPD_URL/auth/smart/bare.git" \
+ --output=remote-http.zip HEAD >actual 2>&1 &&
+ cat >expect <<-EOF &&
+ fatal: can${SQ}t connect to subservice git-upload-archive
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'archive remote http repository' '
+ git archive --remote="$HTTPD_URL/auth/smart/bare.git" \
+ --output=remote-http.zip HEAD &&
+ test_cmp_bin d.zip remote-http.zip
+'
+
test_done
diff --git a/t/t5004-archive-corner-cases.sh b/t/t5004-archive-corner-cases.sh
index 2d32d0e..9f2c6da 100755
--- a/t/t5004-archive-corner-cases.sh
+++ b/t/t5004-archive-corner-cases.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test corner cases of git-archive'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# the 10knuls.tar file is used to test for an empty git generated tar
@@ -131,7 +133,7 @@ test_expect_success ZIPINFO 'zip archive with many entries' '
do
for b in 0 1 2 3 4 5 6 7 8 9 a b c d e f
do
- : >00/$a$b
+ : >00/$a$b || return 1
done
done &&
git add 00 &&
@@ -143,7 +145,7 @@ test_expect_success ZIPINFO 'zip archive with many entries' '
do
for d in 0 1 2 3 4 5 6 7 8 9 a b c d e f
do
- echo "040000 tree $subtree $c$d"
+ echo "040000 tree $subtree $c$d" || return 1
done
done >tree &&
tree=$(git mktree <tree) &&
@@ -171,7 +173,7 @@ test_expect_success EXPENSIVE,UNZIP,UNZIP_ZIP64_SUPPORT \
# create tree containing 65500 entries of that blob
for i in $(test_seq 1 65500)
do
- echo "100644 blob $blob $i"
+ echo "100644 blob $blob $i" || return 1
done >tree &&
tree=$(git mktree <tree) &&
diff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh
index 141b29f..c8d0655 100755
--- a/t/t5100-mailinfo.sh
+++ b/t/t5100-mailinfo.sh
@@ -70,7 +70,7 @@ test_expect_success 'respect NULs' '
git mailsplit -d3 -o. "$DATA/nul-plain" &&
test_cmp "$DATA/nul-plain" 001 &&
- (cat 001 | git mailinfo msg patch) &&
+ git mailinfo msg patch <001 &&
test_line_count = 4 patch
'
@@ -122,7 +122,7 @@ test_expect_success 'mailinfo unescapes with --mboxrd' '
do
git mailinfo mboxrd/msg mboxrd/patch \
<mboxrd/$i >mboxrd/out &&
- test_cmp "$DATA/${i}mboxrd" mboxrd/msg
+ test_cmp "$DATA/${i}mboxrd" mboxrd/msg || return 1
done &&
sp=" " &&
echo "From " >expect &&
@@ -201,13 +201,13 @@ test_expect_success 'mailinfo -b double [PATCH]' '
test z"$subj" = z"Subject: message"
'
-test_expect_failure 'mailinfo -b trailing [PATCH]' '
+test_expect_success 'mailinfo -b trailing [PATCH]' '
subj="$(echo "Subject: [other] [PATCH] message" |
git mailinfo -b /dev/null /dev/null)" &&
test z"$subj" = z"Subject: [other] message"
'
-test_expect_failure 'mailinfo -b separated double [PATCH]' '
+test_expect_success 'mailinfo -b separated double [PATCH]' '
subj="$(echo "Subject: [PATCH] [other] [PATCH] message" |
git mailinfo -b /dev/null /dev/null)" &&
test z"$subj" = z"Subject: [other] message"
@@ -268,4 +268,26 @@ test_expect_success 'mailinfo warn CR in base64 encoded email' '
test_must_be_empty quoted-cr/0002.err
'
+test_expect_success 'from line with unterminated quoted string' '
+ echo "From: bob \"unterminated string smith <bob@example.com>" >in &&
+ git mailinfo /dev/null /dev/null <in >actual &&
+ cat >expect <<-\EOF &&
+ Author: bob unterminated string smith
+ Email: bob@example.com
+
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'from line with unterminated comment' '
+ echo "From: bob (unterminated comment smith <bob@example.com>" >in &&
+ git mailinfo /dev/null /dev/null <in >actual &&
+ cat >expect <<-\EOF &&
+ Author: bob (unterminated comment smith
+ Email: bob@example.com
+
+ EOF
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t5100/comment.expect b/t/t5100/comment.expect
index 7228177..bd71956 100644
--- a/t/t5100/comment.expect
+++ b/t/t5100/comment.expect
@@ -1,4 +1,4 @@
-Author: A U Thor (this is (really) a comment (honestly))
+Author: (this is (really) a "comment" (honestly)) A U Thor
Email: somebody@example.com
Subject: testing comments
Date: Sun, 25 May 2008 00:38:18 -0700
diff --git a/t/t5100/comment.in b/t/t5100/comment.in
index c53a192..0b7e903 100644
--- a/t/t5100/comment.in
+++ b/t/t5100/comment.in
@@ -1,5 +1,5 @@
From 1234567890123456789012345678901234567890 Mon Sep 17 00:00:00 2001
-From: "A U Thor" <somebody@example.com> (this is \(really\) a comment (honestly))
+From: (this is \(really\) a "comment" (honestly)) "A U Thor" <somebody@example.com>
Date: Sun, 25 May 2008 00:38:18 -0700
Subject: [PATCH] testing comments
diff --git a/t/t5100/msg0002 b/t/t5100/msg0002
index e2546ec..1089382 100644
--- a/t/t5100/msg0002
+++ b/t/t5100/msg0002
@@ -3,7 +3,7 @@ message:
From: Nit Picker <nit.picker@example.net>
Subject: foo is too old
-Message-Id: <nitpicker.12121212@example.net>
+Message-ID: <nitpicker.12121212@example.net>
Hopefully this would fix the problem stated there.
diff --git a/t/t5100/msg0003 b/t/t5100/msg0003
index 1ac6810..3402b53 100644
--- a/t/t5100/msg0003
+++ b/t/t5100/msg0003
@@ -3,7 +3,7 @@ message:
From: Nit Picker <nit.picker@example.net>
Subject: foo is too old
-Message-Id: <nitpicker.12121212@example.net>
+Message-ID: <nitpicker.12121212@example.net>
Hopefully this would fix the problem stated there.
diff --git a/t/t5100/msg0012--message-id b/t/t5100/msg0012--message-id
index 376e26e..4448295 100644
--- a/t/t5100/msg0012--message-id
+++ b/t/t5100/msg0012--message-id
@@ -5,4 +5,4 @@ docutils заменён на python-docutils
python-docutils. В то время как сам rest2web не нужен.
Signed-off-by: Dmitriy Blinov <bda@mnsspb.ru>
-Message-Id: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>
+Message-ID: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>
diff --git a/t/t5100/quoted-cr.mbox b/t/t5100/quoted-cr.mbox
index 909021b..a529d4d 100644
--- a/t/t5100/quoted-cr.mbox
+++ b/t/t5100/quoted-cr.mbox
@@ -3,7 +3,7 @@ From: A U Thor <mail@example.com>
To: list@example.org
Subject: [PATCH v2] sample
Date: Mon, 3 Aug 2020 22:40:55 +0700
-Message-Id: <msg-id@example.com>
+Message-ID: <msg-id@example.com>
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: base64
@@ -27,7 +27,7 @@ From: A U Thor <mail@example.com>
To: list@example.org
Subject: [PATCH v2] sample
Date: Mon, 3 Aug 2020 22:40:55 +0700
-Message-Id: <msg-id2@example.com>
+Message-ID: <msg-id2@example.com>
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: base64
diff --git a/t/t5100/sample.mbox b/t/t5100/sample.mbox
index 6d4d0e4..4a54ee5 100644
--- a/t/t5100/sample.mbox
+++ b/t/t5100/sample.mbox
@@ -35,7 +35,7 @@ message:
From: Nit Picker <nit.picker@example.net>
Subject: foo is too old
-Message-Id: <nitpicker.12121212@example.net>
+Message-ID: <nitpicker.12121212@example.net>
Hopefully this would fix the problem stated there.
@@ -78,7 +78,7 @@ message:
From: Nit Picker <nit.picker@example.net>
Subject: foo is too old
-Message-Id: <nitpicker.12121212@example.net>
+Message-ID: <nitpicker.12121212@example.net>
Hopefully this would fix the problem stated there.
@@ -508,7 +508,7 @@ From bda@mnsspb.ru Wed Nov 12 17:54:41 2008
From: Dmitriy Blinov <bda@mnsspb.ru>
To: navy-patches@dinar.mns.mnsspb.ru
Date: Wed, 12 Nov 2008 17:54:41 +0300
-Message-Id: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>
+Message-ID: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>
X-Mailer: git-send-email 1.5.6.5
MIME-Version: 1.0
Content-Type: text/plain;
diff --git a/t/t5200-update-server-info.sh b/t/t5200-update-server-info.sh
index 21a58ee..ed9dfd6 100755
--- a/t/t5200-update-server-info.sh
+++ b/t/t5200-update-server-info.sh
@@ -2,6 +2,7 @@
test_description='Test git update-server-info'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' 'test_commit file'
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
index e13a884..61e2be2 100755
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
@@ -161,22 +161,27 @@ test_expect_success 'pack-objects with bogus arguments' '
'
check_unpack () {
+ local packname="$1" &&
+ local object_list="$2" &&
+ local git_config="$3" &&
test_when_finished "rm -rf git2" &&
- git init --bare git2 &&
- git -C git2 unpack-objects -n <"$1".pack &&
- git -C git2 unpack-objects <"$1".pack &&
- (cd .git && find objects -type f -print) |
- while read path
- do
- cmp git2/$path .git/$path || {
- echo $path differs.
- return 1
- }
- done
+ git $git_config init --bare git2 &&
+ (
+ git $git_config -C git2 unpack-objects -n <"$packname".pack &&
+ git $git_config -C git2 unpack-objects <"$packname".pack &&
+ git $git_config -C git2 cat-file --batch-check="%(objectname)"
+ ) <"$object_list" >current &&
+ cmp "$object_list" current
}
test_expect_success 'unpack without delta' '
- check_unpack test-1-${packname_1}
+ check_unpack test-1-${packname_1} obj-list
+'
+
+BATCH_CONFIGURATION='-c core.fsync=loose-object -c core.fsyncmethod=batch'
+
+test_expect_success 'unpack without delta (core.fsyncmethod=batch)' '
+ check_unpack test-1-${packname_1} obj-list "$BATCH_CONFIGURATION"
'
test_expect_success 'pack with REF_DELTA' '
@@ -185,7 +190,11 @@ test_expect_success 'pack with REF_DELTA' '
'
test_expect_success 'unpack with REF_DELTA' '
- check_unpack test-2-${packname_2}
+ check_unpack test-2-${packname_2} obj-list
+'
+
+test_expect_success 'unpack with REF_DELTA (core.fsyncmethod=batch)' '
+ check_unpack test-2-${packname_2} obj-list "$BATCH_CONFIGURATION"
'
test_expect_success 'pack with OFS_DELTA' '
@@ -195,7 +204,11 @@ test_expect_success 'pack with OFS_DELTA' '
'
test_expect_success 'unpack with OFS_DELTA' '
- check_unpack test-3-${packname_3}
+ check_unpack test-3-${packname_3} obj-list
+'
+
+test_expect_success 'unpack with OFS_DELTA (core.fsyncmethod=batch)' '
+ check_unpack test-3-${packname_3} obj-list "$BATCH_CONFIGURATION"
'
test_expect_success 'compare delta flavors' '
@@ -250,95 +263,97 @@ test_expect_success 'survive missing objects/pack directory' '
)
'
-test_expect_success \
- 'verify pack' \
- 'git verify-pack test-1-${packname_1}.idx \
- test-2-${packname_2}.idx \
- test-3-${packname_3}.idx'
-
-test_expect_success \
- 'verify pack -v' \
- 'git verify-pack -v test-1-${packname_1}.idx \
- test-2-${packname_2}.idx \
- test-3-${packname_3}.idx'
-
-test_expect_success \
- 'verify-pack catches mismatched .idx and .pack files' \
- 'cat test-1-${packname_1}.idx >test-3.idx &&
- cat test-2-${packname_2}.pack >test-3.pack &&
- if git verify-pack test-3.idx
- then false
- else :;
- fi'
-
-test_expect_success \
- 'verify-pack catches a corrupted pack signature' \
- 'cat test-1-${packname_1}.pack >test-3.pack &&
- echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=2 &&
- if git verify-pack test-3.idx
- then false
- else :;
- fi'
-
-test_expect_success \
- 'verify-pack catches a corrupted pack version' \
- 'cat test-1-${packname_1}.pack >test-3.pack &&
- echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=7 &&
- if git verify-pack test-3.idx
- then false
- else :;
- fi'
-
-test_expect_success \
- 'verify-pack catches a corrupted type/size of the 1st packed object data' \
- 'cat test-1-${packname_1}.pack >test-3.pack &&
- echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=12 &&
- if git verify-pack test-3.idx
- then false
- else :;
- fi'
-
-test_expect_success \
- 'verify-pack catches a corrupted sum of the index file itself' \
- 'l=$(wc -c <test-3.idx) &&
- l=$(expr $l - 20) &&
- cat test-1-${packname_1}.pack >test-3.pack &&
- printf "%20s" "" | dd of=test-3.idx count=20 bs=1 conv=notrunc seek=$l &&
- if git verify-pack test-3.pack
- then false
- else :;
- fi'
-
-test_expect_success \
- 'build pack index for an existing pack' \
- 'cat test-1-${packname_1}.pack >test-3.pack &&
- git index-pack -o tmp.idx test-3.pack &&
- cmp tmp.idx test-1-${packname_1}.idx &&
-
- git index-pack test-3.pack &&
- cmp test-3.idx test-1-${packname_1}.idx &&
-
- cat test-2-${packname_2}.pack >test-3.pack &&
- git index-pack -o tmp.idx test-2-${packname_2}.pack &&
- cmp tmp.idx test-2-${packname_2}.idx &&
-
- git index-pack test-3.pack &&
- cmp test-3.idx test-2-${packname_2}.idx &&
-
- cat test-3-${packname_3}.pack >test-3.pack &&
- git index-pack -o tmp.idx test-3-${packname_3}.pack &&
- cmp tmp.idx test-3-${packname_3}.idx &&
-
- git index-pack test-3.pack &&
- cmp test-3.idx test-3-${packname_3}.idx &&
-
- cat test-1-${packname_1}.pack >test-4.pack &&
- rm -f test-4.keep &&
- git index-pack --keep=why test-4.pack &&
- cmp test-1-${packname_1}.idx test-4.idx &&
- test -f test-4.keep &&
-
- :'
+test_expect_success 'verify pack' '
+ git verify-pack test-1-${packname_1}.idx \
+ test-2-${packname_2}.idx \
+ test-3-${packname_3}.idx
+'
+
+test_expect_success 'verify pack -v' '
+ git verify-pack -v test-1-${packname_1}.idx \
+ test-2-${packname_2}.idx \
+ test-3-${packname_3}.idx
+'
+
+test_expect_success 'verify-pack catches mismatched .idx and .pack files' '
+ cat test-1-${packname_1}.idx >test-3.idx &&
+ cat test-2-${packname_2}.pack >test-3.pack &&
+ if git verify-pack test-3.idx
+ then false
+ else :;
+ fi
+'
+
+test_expect_success 'verify-pack catches a corrupted pack signature' '
+ cat test-1-${packname_1}.pack >test-3.pack &&
+ echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=2 &&
+ if git verify-pack test-3.idx
+ then false
+ else :;
+ fi
+'
+
+test_expect_success 'verify-pack catches a corrupted pack version' '
+ cat test-1-${packname_1}.pack >test-3.pack &&
+ echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=7 &&
+ if git verify-pack test-3.idx
+ then false
+ else :;
+ fi
+'
+
+test_expect_success 'verify-pack catches a corrupted type/size of the 1st packed object data' '
+ cat test-1-${packname_1}.pack >test-3.pack &&
+ echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=12 &&
+ if git verify-pack test-3.idx
+ then false
+ else :;
+ fi
+'
+
+test_expect_success 'verify-pack catches a corrupted sum of the index file itself' '
+ l=$(wc -c <test-3.idx) &&
+ l=$(expr $l - 20) &&
+ cat test-1-${packname_1}.pack >test-3.pack &&
+ printf "%20s" "" | dd of=test-3.idx count=20 bs=1 conv=notrunc seek=$l &&
+ if git verify-pack test-3.pack
+ then false
+ else :;
+ fi
+'
+
+test_expect_success 'build pack index for an existing pack' '
+ cat test-1-${packname_1}.pack >test-3.pack &&
+ git index-pack -o tmp.idx test-3.pack &&
+ cmp tmp.idx test-1-${packname_1}.idx &&
+
+ git index-pack --promisor=message test-3.pack &&
+ cmp test-3.idx test-1-${packname_1}.idx &&
+ echo message >expect &&
+ test_cmp expect test-3.promisor &&
+
+ cat test-2-${packname_2}.pack >test-3.pack &&
+ git index-pack -o tmp.idx test-2-${packname_2}.pack &&
+ cmp tmp.idx test-2-${packname_2}.idx &&
+
+ git index-pack test-3.pack &&
+ cmp test-3.idx test-2-${packname_2}.idx &&
+
+ cat test-3-${packname_3}.pack >test-3.pack &&
+ git index-pack -o tmp.idx test-3-${packname_3}.pack &&
+ cmp tmp.idx test-3-${packname_3}.idx &&
+
+ git index-pack test-3.pack &&
+ cmp test-3.idx test-3-${packname_3}.idx &&
+
+ cat test-1-${packname_1}.pack >test-4.pack &&
+ rm -f test-4.keep &&
+ git index-pack --keep=why test-4.pack &&
+ cmp test-1-${packname_1}.idx test-4.idx &&
+ test -f test-4.keep &&
+
+ :
+'
test_expect_success 'unpacking with --strict' '
@@ -347,7 +362,7 @@ test_expect_success 'unpacking with --strict' '
for i in 0 1 2 3 4 5 6 7 8 9
do
o=$(echo $j$i | git hash-object -w --stdin) &&
- echo "100644 $o 0 $j$i"
+ echo "100644 $o 0 $j$i" || return 1
done
done >LIST &&
rm -f .git/index &&
@@ -361,11 +376,7 @@ test_expect_success 'unpacking with --strict' '
ST=$(git write-tree) &&
git rev-list --objects "$LIST" "$LI" "$ST" >actual &&
PACK5=$( git pack-objects test-5 <actual ) &&
- PACK6=$( (
- echo "$LIST"
- echo "$LI"
- echo "$ST"
- ) | git pack-objects test-6 ) &&
+ PACK6=$( test_write_lines "$LIST" "$LI" "$ST" | git pack-objects test-6 ) &&
test_create_repo test-5 &&
(
cd test-5 &&
@@ -394,7 +405,7 @@ test_expect_success 'index-pack with --strict' '
for i in 0 1 2 3 4 5 6 7 8 9
do
o=$(echo $j$i | git hash-object -w --stdin) &&
- echo "100644 $o 0 $j$i"
+ echo "100644 $o 0 $j$i" || return 1
done
done >LIST &&
rm -f .git/index &&
@@ -408,11 +419,7 @@ test_expect_success 'index-pack with --strict' '
ST=$(git write-tree) &&
git rev-list --objects "$LIST" "$LI" "$ST" >actual &&
PACK5=$( git pack-objects test-5 <actual ) &&
- PACK6=$( (
- echo "$LIST"
- echo "$LI"
- echo "$ST"
- ) | git pack-objects test-6 ) &&
+ PACK6=$( test_write_lines "$LIST" "$LI" "$ST" | git pack-objects test-6 ) &&
test_create_repo test-7 &&
(
cd test-7 &&
@@ -434,6 +441,47 @@ test_expect_success 'index-pack with --strict' '
)
'
+test_expect_success 'setup for --strict and --fsck-objects downgrading fsck msgs' '
+ git init strict &&
+ (
+ cd strict &&
+ test_commit first hello &&
+ cat >commit <<-EOF &&
+ tree $(git rev-parse HEAD^{tree})
+ parent $(git rev-parse HEAD)
+ author A U Thor
+ committer A U Thor
+
+ commit: this is a commit with bad emails
+
+ EOF
+ git hash-object --literally -t commit -w --stdin <commit >commit_list &&
+ git pack-objects test <commit_list >pack-name
+ )
+'
+
+test_with_bad_commit () {
+ must_fail_arg="$1" &&
+ must_pass_arg="$2" &&
+ (
+ cd strict &&
+ test_must_fail git index-pack "$must_fail_arg" "test-$(cat pack-name).pack" &&
+ git index-pack "$must_pass_arg" "test-$(cat pack-name).pack"
+ )
+}
+
+test_expect_success 'index-pack with --strict downgrading fsck msgs' '
+ test_with_bad_commit --strict --strict="missingEmail=ignore"
+'
+
+test_expect_success 'index-pack with --fsck-objects downgrading fsck msgs' '
+ test_with_bad_commit --fsck-objects --fsck-objects="missingEmail=ignore"
+'
+
+test_expect_success 'cleanup for --strict and --fsck-objects downgrading fsck msgs' '
+ rm -rf strict
+'
+
test_expect_success 'honor pack.packSizeLimit' '
git config pack.packSizeLimit 3m &&
packname_10=$(git pack-objects test-10 <obj-list) &&
@@ -534,7 +582,7 @@ test_expect_success 'make sure index-pack detects the SHA1 collision' '
(
cd corrupt &&
test_must_fail git index-pack -o ../bad.idx ../test-3.pack 2>msg &&
- test_i18ngrep "SHA1 COLLISION FOUND" msg
+ test_grep "SHA1 COLLISION FOUND" msg
)
'
@@ -542,7 +590,7 @@ test_expect_success 'make sure index-pack detects the SHA1 collision (large blob
(
cd corrupt &&
test_must_fail git -c core.bigfilethreshold=1 index-pack -o ../bad.idx ../test-3.pack 2>msg &&
- test_i18ngrep "SHA1 COLLISION FOUND" msg
+ test_grep "SHA1 COLLISION FOUND" msg
)
'
@@ -582,141 +630,6 @@ test_expect_success 'prefetch objects' '
test_line_count = 1 donelines
'
-test_expect_success 'setup for --stdin-packs tests' '
- git init stdin-packs &&
- (
- cd stdin-packs &&
-
- test_commit A &&
- test_commit B &&
- test_commit C &&
-
- for id in A B C
- do
- git pack-objects .git/objects/pack/pack-$id \
- --incremental --revs <<-EOF
- refs/tags/$id
- EOF
- done &&
-
- ls -la .git/objects/pack
- )
-'
-
-test_expect_success '--stdin-packs with excluded packs' '
- (
- cd stdin-packs &&
-
- PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" &&
- PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" &&
- PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" &&
-
- git pack-objects test --stdin-packs <<-EOF &&
- $PACK_A
- ^$PACK_B
- $PACK_C
- EOF
-
- (
- git show-index <$(ls .git/objects/pack/pack-A-*.idx) &&
- git show-index <$(ls .git/objects/pack/pack-C-*.idx)
- ) >expect.raw &&
- git show-index <$(ls test-*.idx) >actual.raw &&
-
- cut -d" " -f2 <expect.raw | sort >expect &&
- cut -d" " -f2 <actual.raw | sort >actual &&
- test_cmp expect actual
- )
-'
-
-test_expect_success '--stdin-packs is incompatible with --filter' '
- (
- cd stdin-packs &&
- test_must_fail git pack-objects --stdin-packs --stdout \
- --filter=blob:none </dev/null 2>err &&
- test_i18ngrep "cannot use --filter with --stdin-packs" err
- )
-'
-
-test_expect_success '--stdin-packs is incompatible with --revs' '
- (
- cd stdin-packs &&
- test_must_fail git pack-objects --stdin-packs --revs out \
- </dev/null 2>err &&
- test_i18ngrep "cannot use internal rev list with --stdin-packs" err
- )
-'
-
-test_expect_success '--stdin-packs with loose objects' '
- (
- cd stdin-packs &&
-
- PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" &&
- PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" &&
- PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" &&
-
- test_commit D && # loose
-
- git pack-objects test2 --stdin-packs --unpacked <<-EOF &&
- $PACK_A
- ^$PACK_B
- $PACK_C
- EOF
-
- (
- git show-index <$(ls .git/objects/pack/pack-A-*.idx) &&
- git show-index <$(ls .git/objects/pack/pack-C-*.idx) &&
- git rev-list --objects --no-object-names \
- refs/tags/C..refs/tags/D
-
- ) >expect.raw &&
- ls -la . &&
- git show-index <$(ls test2-*.idx) >actual.raw &&
-
- cut -d" " -f2 <expect.raw | sort >expect &&
- cut -d" " -f2 <actual.raw | sort >actual &&
- test_cmp expect actual
- )
-'
-
-test_expect_success '--stdin-packs with broken links' '
- (
- cd stdin-packs &&
-
- # make an unreachable object with a bogus parent
- git cat-file -p HEAD >commit &&
- sed "s/$(git rev-parse HEAD^)/$(test_oid zero)/" <commit |
- git hash-object -w -t commit --stdin >in &&
-
- git pack-objects .git/objects/pack/pack-D <in &&
-
- PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" &&
- PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" &&
- PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" &&
- PACK_D="$(basename .git/objects/pack/pack-D-*.pack)" &&
-
- git pack-objects test3 --stdin-packs --unpacked <<-EOF &&
- $PACK_A
- ^$PACK_B
- $PACK_C
- $PACK_D
- EOF
-
- (
- git show-index <$(ls .git/objects/pack/pack-A-*.idx) &&
- git show-index <$(ls .git/objects/pack/pack-C-*.idx) &&
- git show-index <$(ls .git/objects/pack/pack-D-*.idx) &&
- git rev-list --objects --no-object-names \
- refs/tags/C..refs/tags/D
- ) >expect.raw &&
- git show-index <$(ls test3-*.idx) >actual.raw &&
-
- cut -d" " -f2 <expect.raw | sort >expect &&
- cut -d" " -f2 <actual.raw | sort >actual &&
- test_cmp expect actual
- )
-'
-
test_expect_success 'negative window clamps to 0' '
git pack-objects --progress --window=-1 neg-window <obj-list 2>stderr &&
check_deltas stderr = 0
diff --git a/t/t5301-sliding-window.sh b/t/t5301-sliding-window.sh
index 76f9798..226490d 100755
--- a/t/t5301-sliding-window.sh
+++ b/t/t5301-sliding-window.sh
@@ -4,57 +4,59 @@
#
test_description='mmap sliding window tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
-test_expect_success \
- 'setup' \
- 'rm -f .git/index* &&
- for i in a b c
- do
- echo $i >$i &&
- test-tool genrandom "$i" 32768 >>$i &&
- git update-index --add $i || return 1
- done &&
- echo d >d && cat c >>d && git update-index --add d &&
- tree=$(git write-tree) &&
- commit1=$(git commit-tree $tree </dev/null) &&
- git update-ref HEAD $commit1 &&
- git repack -a -d &&
- test "$(git count-objects)" = "0 objects, 0 kilobytes" &&
- pack1=$(ls .git/objects/pack/*.pack) &&
- test -f "$pack1"'
-
-test_expect_success \
- 'verify-pack -v, defaults' \
- 'git verify-pack -v "$pack1"'
-
-test_expect_success \
- 'verify-pack -v, packedGitWindowSize == 1 page' \
- 'git config core.packedGitWindowSize 512 &&
- git verify-pack -v "$pack1"'
-
-test_expect_success \
- 'verify-pack -v, packedGit{WindowSize,Limit} == 1 page' \
- 'git config core.packedGitWindowSize 512 &&
- git config core.packedGitLimit 512 &&
- git verify-pack -v "$pack1"'
-
-test_expect_success \
- 'repack -a -d, packedGit{WindowSize,Limit} == 1 page' \
- 'git config core.packedGitWindowSize 512 &&
- git config core.packedGitLimit 512 &&
- commit2=$(git commit-tree $tree -p $commit1 </dev/null) &&
- git update-ref HEAD $commit2 &&
- git repack -a -d &&
- test "$(git count-objects)" = "0 objects, 0 kilobytes" &&
- pack2=$(ls .git/objects/pack/*.pack) &&
- test -f "$pack2" &&
- test "$pack1" \!= "$pack2"'
-
-test_expect_success \
- 'verify-pack -v, defaults' \
- 'git config --unset core.packedGitWindowSize &&
- git config --unset core.packedGitLimit &&
- git verify-pack -v "$pack2"'
+test_expect_success 'setup' '
+ rm -f .git/index* &&
+ for i in a b c
+ do
+ echo $i >$i &&
+ test-tool genrandom "$i" 32768 >>$i &&
+ git update-index --add $i || return 1
+ done &&
+ echo d >d && cat c >>d && git update-index --add d &&
+ tree=$(git write-tree) &&
+ commit1=$(git commit-tree $tree </dev/null) &&
+ git update-ref HEAD $commit1 &&
+ git repack -a -d &&
+ test "$(git count-objects)" = "0 objects, 0 kilobytes" &&
+ pack1=$(ls .git/objects/pack/*.pack) &&
+ test -f "$pack1"
+'
+
+test_expect_success 'verify-pack -v, defaults' '
+ git verify-pack -v "$pack1"
+'
+
+test_expect_success 'verify-pack -v, packedGitWindowSize == 1 page' '
+ git config core.packedGitWindowSize 512 &&
+ git verify-pack -v "$pack1"
+'
+
+test_expect_success 'verify-pack -v, packedGit{WindowSize,Limit} == 1 page' '
+ git config core.packedGitWindowSize 512 &&
+ git config core.packedGitLimit 512 &&
+ git verify-pack -v "$pack1"
+'
+
+test_expect_success 'repack -a -d, packedGit{WindowSize,Limit} == 1 page' '
+ git config core.packedGitWindowSize 512 &&
+ git config core.packedGitLimit 512 &&
+ commit2=$(git commit-tree $tree -p $commit1 </dev/null) &&
+ git update-ref HEAD $commit2 &&
+ git repack -a -d &&
+ test "$(git count-objects)" = "0 objects, 0 kilobytes" &&
+ pack2=$(ls .git/objects/pack/*.pack) &&
+ test -f "$pack2" &&
+ test "$pack1" \!= "$pack2"
+'
+
+test_expect_success 'verify-pack -v, defaults' '
+ git config --unset core.packedGitWindowSize &&
+ git config --unset core.packedGitLimit &&
+ git verify-pack -v "$pack2"
+'
test_done
diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh
index 7c9d687..d88e6f1 100755
--- a/t/t5302-pack-index.sh
+++ b/t/t5302-pack-index.sh
@@ -4,6 +4,8 @@
#
test_description='pack index with 64-bit offsets and object CRC'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -14,7 +16,7 @@ test_expect_success 'setup' '
i=1 &&
while test $i -le 100
do
- iii=$(printf "%03i" $i)
+ iii=$(printf "%03i" $i) &&
test-tool genrandom "bar" 200 > wide_delta_$iii &&
test-tool genrandom "baz $iii" 50 >> wide_delta_$iii &&
test-tool genrandom "foo"$i 100 > deep_delta_$iii &&
@@ -263,7 +265,7 @@ tag guten tag
This is an invalid tag.
EOF
- tag=$(git hash-object -t tag -w --stdin <wrong-tag) &&
+ tag=$(git hash-object -t tag -w --stdin --literally <wrong-tag) &&
pack1=$(echo $tag $sha | git pack-objects tag-test) &&
echo remove tag object &&
thirtyeight=${tag#??} &&
@@ -280,8 +282,16 @@ test_expect_success 'index-pack --fsck-objects also warns upon missing tagger in
test_expect_success 'index-pack -v --stdin produces progress for both phases' '
pack=$(git pack-objects --all pack </dev/null) &&
GIT_PROGRESS_DELAY=0 git index-pack -v --stdin <pack-$pack.pack 2>err &&
- test_i18ngrep "Receiving objects" err &&
- test_i18ngrep "Resolving deltas" err
+ test_grep "Receiving objects" err &&
+ test_grep "Resolving deltas" err
+'
+
+test_expect_success 'too-large packs report the breach' '
+ pack=$(git pack-objects --all pack </dev/null) &&
+ sz="$(test_file_size pack-$pack.pack)" &&
+ test "$sz" -gt 20 &&
+ test_must_fail git index-pack --max-input-size=20 pack-$pack.pack 2>err &&
+ grep "maximum allowed size (20 bytes)" err
'
test_done
diff --git a/t/t5303-pack-corruption-resilience.sh b/t/t5303-pack-corruption-resilience.sh
index 41e6dc4..61469ef 100755
--- a/t/t5303-pack-corruption-resilience.sh
+++ b/t/t5303-pack-corruption-resilience.sh
@@ -4,6 +4,8 @@
#
test_description='resilience to pack corruptions with redundant objects'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Note: the test objects are created with knowledge of their pack encoding
@@ -57,304 +59,304 @@ do_corrupt_object() {
printf '\0' > zero
-test_expect_success \
- 'initial setup validation' \
- 'create_test_files &&
- create_new_pack &&
- git prune-packed &&
- git cat-file blob $blob_1 > /dev/null &&
- git cat-file blob $blob_2 > /dev/null &&
- git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
- 'create corruption in header of first object' \
- 'do_corrupt_object $blob_1 0 < zero &&
- test_must_fail git cat-file blob $blob_1 > /dev/null &&
- test_must_fail git cat-file blob $blob_2 > /dev/null &&
- test_must_fail git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
- '... but having a loose copy allows for full recovery' \
- 'mv ${pack}.idx tmp &&
- git hash-object -t blob -w file_1 &&
- mv tmp ${pack}.idx &&
- git cat-file blob $blob_1 > /dev/null &&
- git cat-file blob $blob_2 > /dev/null &&
- git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
- '... and loose copy of first delta allows for partial recovery' \
- 'git prune-packed &&
- test_must_fail git cat-file blob $blob_2 > /dev/null &&
- mv ${pack}.idx tmp &&
- git hash-object -t blob -w file_2 &&
- mv tmp ${pack}.idx &&
- test_must_fail git cat-file blob $blob_1 > /dev/null &&
- git cat-file blob $blob_2 > /dev/null &&
- git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
- 'create corruption in data of first object' \
- 'create_new_pack &&
- git prune-packed &&
- chmod +w ${pack}.pack &&
- perl -i.bak -pe "s/ base /abcdef/" ${pack}.pack &&
- test_must_fail git cat-file blob $blob_1 > /dev/null &&
- test_must_fail git cat-file blob $blob_2 > /dev/null &&
- test_must_fail git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
- '... but having a loose copy allows for full recovery' \
- 'mv ${pack}.idx tmp &&
- git hash-object -t blob -w file_1 &&
- mv tmp ${pack}.idx &&
- git cat-file blob $blob_1 > /dev/null &&
- git cat-file blob $blob_2 > /dev/null &&
- git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
- '... and loose copy of second object allows for partial recovery' \
- 'git prune-packed &&
- test_must_fail git cat-file blob $blob_2 > /dev/null &&
- mv ${pack}.idx tmp &&
- git hash-object -t blob -w file_2 &&
- mv tmp ${pack}.idx &&
- test_must_fail git cat-file blob $blob_1 > /dev/null &&
- git cat-file blob $blob_2 > /dev/null &&
- git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
- 'create corruption in header of first delta' \
- 'create_new_pack &&
- git prune-packed &&
- do_corrupt_object $blob_2 0 < zero &&
- git cat-file blob $blob_1 > /dev/null &&
- test_must_fail git cat-file blob $blob_2 > /dev/null &&
- test_must_fail git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
- '... but having a loose copy allows for full recovery' \
- 'mv ${pack}.idx tmp &&
- git hash-object -t blob -w file_2 &&
- mv tmp ${pack}.idx &&
- git cat-file blob $blob_1 > /dev/null &&
- git cat-file blob $blob_2 > /dev/null &&
- git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
- '... and then a repack "clears" the corruption' \
- 'do_repack &&
- git prune-packed &&
- git verify-pack ${pack}.pack &&
- git cat-file blob $blob_1 > /dev/null &&
- git cat-file blob $blob_2 > /dev/null &&
- git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
- 'create corruption in data of first delta' \
- 'create_new_pack &&
- git prune-packed &&
- chmod +w ${pack}.pack &&
- perl -i.bak -pe "s/ delta1 /abcdefgh/" ${pack}.pack &&
- git cat-file blob $blob_1 > /dev/null &&
- test_must_fail git cat-file blob $blob_2 > /dev/null &&
- test_must_fail git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
- '... but having a loose copy allows for full recovery' \
- 'mv ${pack}.idx tmp &&
- git hash-object -t blob -w file_2 &&
- mv tmp ${pack}.idx &&
- git cat-file blob $blob_1 > /dev/null &&
- git cat-file blob $blob_2 > /dev/null &&
- git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
- '... and then a repack "clears" the corruption' \
- 'do_repack &&
- git prune-packed &&
- git verify-pack ${pack}.pack &&
- git cat-file blob $blob_1 > /dev/null &&
- git cat-file blob $blob_2 > /dev/null &&
- git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
- 'corruption in delta base reference of first delta (OBJ_REF_DELTA)' \
- 'create_new_pack &&
- git prune-packed &&
- do_corrupt_object $blob_2 2 < zero &&
- git cat-file blob $blob_1 > /dev/null &&
- test_must_fail git cat-file blob $blob_2 > /dev/null &&
- test_must_fail git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
- '... but having a loose copy allows for full recovery' \
- 'mv ${pack}.idx tmp &&
- git hash-object -t blob -w file_2 &&
- mv tmp ${pack}.idx &&
- git cat-file blob $blob_1 > /dev/null &&
- git cat-file blob $blob_2 > /dev/null &&
- git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
- '... and then a repack "clears" the corruption' \
- 'do_repack &&
- git prune-packed &&
- git verify-pack ${pack}.pack &&
- git cat-file blob $blob_1 > /dev/null &&
- git cat-file blob $blob_2 > /dev/null &&
- git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
- 'corruption #0 in delta base reference of first delta (OBJ_OFS_DELTA)' \
- 'create_new_pack --delta-base-offset &&
- git prune-packed &&
- do_corrupt_object $blob_2 2 < zero &&
- git cat-file blob $blob_1 > /dev/null &&
- test_must_fail git cat-file blob $blob_2 > /dev/null &&
- test_must_fail git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
- '... but having a loose copy allows for full recovery' \
- 'mv ${pack}.idx tmp &&
- git hash-object -t blob -w file_2 &&
- mv tmp ${pack}.idx &&
- git cat-file blob $blob_1 > /dev/null &&
- git cat-file blob $blob_2 > /dev/null &&
- git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
- '... and then a repack "clears" the corruption' \
- 'do_repack --delta-base-offset &&
- git prune-packed &&
- git verify-pack ${pack}.pack &&
- git cat-file blob $blob_1 > /dev/null &&
- git cat-file blob $blob_2 > /dev/null &&
- git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
- 'corruption #1 in delta base reference of first delta (OBJ_OFS_DELTA)' \
- 'create_new_pack --delta-base-offset &&
- git prune-packed &&
- printf "\001" | do_corrupt_object $blob_2 2 &&
- git cat-file blob $blob_1 > /dev/null &&
- test_must_fail git cat-file blob $blob_2 > /dev/null &&
- test_must_fail git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
- '... but having a loose copy allows for full recovery' \
- 'mv ${pack}.idx tmp &&
- git hash-object -t blob -w file_2 &&
- mv tmp ${pack}.idx &&
- git cat-file blob $blob_1 > /dev/null &&
- git cat-file blob $blob_2 > /dev/null &&
- git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
- '... and then a repack "clears" the corruption' \
- 'do_repack --delta-base-offset &&
- git prune-packed &&
- git verify-pack ${pack}.pack &&
- git cat-file blob $blob_1 > /dev/null &&
- git cat-file blob $blob_2 > /dev/null &&
- git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
- '... and a redundant pack allows for full recovery too' \
- 'do_corrupt_object $blob_2 2 < zero &&
- git cat-file blob $blob_1 > /dev/null &&
- test_must_fail git cat-file blob $blob_2 > /dev/null &&
- test_must_fail git cat-file blob $blob_3 > /dev/null &&
- mv ${pack}.idx tmp &&
- git hash-object -t blob -w file_1 &&
- git hash-object -t blob -w file_2 &&
- printf "$blob_1\n$blob_2\n" | git pack-objects .git/objects/pack/pack &&
- git prune-packed &&
- mv tmp ${pack}.idx &&
- git cat-file blob $blob_1 > /dev/null &&
- git cat-file blob $blob_2 > /dev/null &&
- git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
- 'corruption of delta base reference pointing to wrong object' \
- 'create_new_pack --delta-base-offset &&
- git prune-packed &&
- printf "\220\033" | do_corrupt_object $blob_3 2 &&
- git cat-file blob $blob_1 >/dev/null &&
- git cat-file blob $blob_2 >/dev/null &&
- test_must_fail git cat-file blob $blob_3 >/dev/null'
-
-test_expect_success \
- '... but having a loose copy allows for full recovery' \
- 'mv ${pack}.idx tmp &&
- git hash-object -t blob -w file_3 &&
- mv tmp ${pack}.idx &&
- git cat-file blob $blob_1 > /dev/null &&
- git cat-file blob $blob_2 > /dev/null &&
- git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
- '... and then a repack "clears" the corruption' \
- 'do_repack --delta-base-offset --no-reuse-delta &&
- git prune-packed &&
- git verify-pack ${pack}.pack &&
- git cat-file blob $blob_1 > /dev/null &&
- git cat-file blob $blob_2 > /dev/null &&
- git cat-file blob $blob_3 > /dev/null'
-
-test_expect_success \
- 'corrupting header to have too small output buffer fails unpack' \
- 'create_new_pack &&
- git prune-packed &&
- printf "\262\001" | do_corrupt_object $blob_1 0 &&
- test_must_fail git cat-file blob $blob_1 > /dev/null &&
- test_must_fail git cat-file blob $blob_2 > /dev/null &&
- test_must_fail git cat-file blob $blob_3 > /dev/null'
+test_expect_success 'initial setup validation' '
+ create_test_files &&
+ create_new_pack &&
+ git prune-packed &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success 'create corruption in header of first object' '
+ do_corrupt_object $blob_1 0 < zero &&
+ test_must_fail git cat-file blob $blob_1 > /dev/null &&
+ test_must_fail git cat-file blob $blob_2 > /dev/null &&
+ test_must_fail git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... but having a loose copy allows for full recovery' '
+ mv ${pack}.idx tmp &&
+ git hash-object -t blob -w file_1 &&
+ mv tmp ${pack}.idx &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... and loose copy of first delta allows for partial recovery' '
+ git prune-packed &&
+ test_must_fail git cat-file blob $blob_2 > /dev/null &&
+ mv ${pack}.idx tmp &&
+ git hash-object -t blob -w file_2 &&
+ mv tmp ${pack}.idx &&
+ test_must_fail git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success 'create corruption in data of first object' '
+ create_new_pack &&
+ git prune-packed &&
+ chmod +w ${pack}.pack &&
+ perl -i.bak -pe "s/ base /abcdef/" ${pack}.pack &&
+ test_must_fail git cat-file blob $blob_1 > /dev/null &&
+ test_must_fail git cat-file blob $blob_2 > /dev/null &&
+ test_must_fail git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... but having a loose copy allows for full recovery' '
+ mv ${pack}.idx tmp &&
+ git hash-object -t blob -w file_1 &&
+ mv tmp ${pack}.idx &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... and loose copy of second object allows for partial recovery' '
+ git prune-packed &&
+ test_must_fail git cat-file blob $blob_2 > /dev/null &&
+ mv ${pack}.idx tmp &&
+ git hash-object -t blob -w file_2 &&
+ mv tmp ${pack}.idx &&
+ test_must_fail git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success 'create corruption in header of first delta' '
+ create_new_pack &&
+ git prune-packed &&
+ do_corrupt_object $blob_2 0 < zero &&
+ git cat-file blob $blob_1 > /dev/null &&
+ test_must_fail git cat-file blob $blob_2 > /dev/null &&
+ test_must_fail git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... but having a loose copy allows for full recovery' '
+ mv ${pack}.idx tmp &&
+ git hash-object -t blob -w file_2 &&
+ mv tmp ${pack}.idx &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... and then a repack "clears" the corruption' '
+ do_repack &&
+ git prune-packed &&
+ git verify-pack ${pack}.pack &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success 'create corruption in data of first delta' '
+ create_new_pack &&
+ git prune-packed &&
+ chmod +w ${pack}.pack &&
+ perl -i.bak -pe "s/ delta1 /abcdefgh/" ${pack}.pack &&
+ git cat-file blob $blob_1 > /dev/null &&
+ test_must_fail git cat-file blob $blob_2 > /dev/null &&
+ test_must_fail git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... but having a loose copy allows for full recovery' '
+ mv ${pack}.idx tmp &&
+ git hash-object -t blob -w file_2 &&
+ mv tmp ${pack}.idx &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... and then a repack "clears" the corruption' '
+ do_repack &&
+ git prune-packed &&
+ git verify-pack ${pack}.pack &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success 'corruption in delta base reference of first delta (OBJ_REF_DELTA)' '
+ create_new_pack &&
+ git prune-packed &&
+ do_corrupt_object $blob_2 2 < zero &&
+ git cat-file blob $blob_1 > /dev/null &&
+ test_must_fail git cat-file blob $blob_2 > /dev/null &&
+ test_must_fail git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... but having a loose copy allows for full recovery' '
+ mv ${pack}.idx tmp &&
+ git hash-object -t blob -w file_2 &&
+ mv tmp ${pack}.idx &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... and then a repack "clears" the corruption' '
+ do_repack &&
+ git prune-packed &&
+ git verify-pack ${pack}.pack &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success 'corruption #0 in delta base reference of first delta (OBJ_OFS_DELTA)' '
+ create_new_pack --delta-base-offset &&
+ git prune-packed &&
+ do_corrupt_object $blob_2 2 < zero &&
+ git cat-file blob $blob_1 > /dev/null &&
+ test_must_fail git cat-file blob $blob_2 > /dev/null &&
+ test_must_fail git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... but having a loose copy allows for full recovery' '
+ mv ${pack}.idx tmp &&
+ git hash-object -t blob -w file_2 &&
+ mv tmp ${pack}.idx &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... and then a repack "clears" the corruption' '
+ do_repack --delta-base-offset &&
+ git prune-packed &&
+ git verify-pack ${pack}.pack &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success 'corruption #1 in delta base reference of first delta (OBJ_OFS_DELTA)' '
+ create_new_pack --delta-base-offset &&
+ git prune-packed &&
+ printf "\001" | do_corrupt_object $blob_2 2 &&
+ git cat-file blob $blob_1 > /dev/null &&
+ test_must_fail git cat-file blob $blob_2 > /dev/null &&
+ test_must_fail git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... but having a loose copy allows for full recovery' '
+ mv ${pack}.idx tmp &&
+ git hash-object -t blob -w file_2 &&
+ mv tmp ${pack}.idx &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... and then a repack "clears" the corruption' '
+ do_repack --delta-base-offset &&
+ git prune-packed &&
+ git verify-pack ${pack}.pack &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... and a redundant pack allows for full recovery too' '
+ do_corrupt_object $blob_2 2 < zero &&
+ git cat-file blob $blob_1 > /dev/null &&
+ test_must_fail git cat-file blob $blob_2 > /dev/null &&
+ test_must_fail git cat-file blob $blob_3 > /dev/null &&
+ mv ${pack}.idx tmp &&
+ git hash-object -t blob -w file_1 &&
+ git hash-object -t blob -w file_2 &&
+ printf "$blob_1\n$blob_2\n" | git pack-objects .git/objects/pack/pack &&
+ git prune-packed &&
+ mv tmp ${pack}.idx &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success 'corruption of delta base reference pointing to wrong object' '
+ create_new_pack --delta-base-offset &&
+ git prune-packed &&
+ printf "\220\033" | do_corrupt_object $blob_3 2 &&
+ git cat-file blob $blob_1 >/dev/null &&
+ git cat-file blob $blob_2 >/dev/null &&
+ test_must_fail git cat-file blob $blob_3 >/dev/null
+'
+
+test_expect_success '... but having a loose copy allows for full recovery' '
+ mv ${pack}.idx tmp &&
+ git hash-object -t blob -w file_3 &&
+ mv tmp ${pack}.idx &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success '... and then a repack "clears" the corruption' '
+ do_repack --delta-base-offset --no-reuse-delta &&
+ git prune-packed &&
+ git verify-pack ${pack}.pack &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null
+'
+
+test_expect_success 'corrupting header to have too small output buffer fails unpack' '
+ create_new_pack &&
+ git prune-packed &&
+ printf "\262\001" | do_corrupt_object $blob_1 0 &&
+ test_must_fail git cat-file blob $blob_1 > /dev/null &&
+ test_must_fail git cat-file blob $blob_2 > /dev/null &&
+ test_must_fail git cat-file blob $blob_3 > /dev/null
+'
# \0 - empty base
# \1 - one byte in result
# \1 - one literal byte (X)
-test_expect_success \
- 'apply good minimal delta' \
- 'printf "\0\1\1X" > minimal_delta &&
- test-tool delta -p /dev/null minimal_delta /dev/null'
+test_expect_success 'apply good minimal delta' '
+ printf "\0\1\1X" > minimal_delta &&
+ test-tool delta -p /dev/null minimal_delta /dev/null
+'
# \0 - empty base
# \1 - 1 byte in result
# \2 - two literal bytes (one too many)
-test_expect_success \
- 'apply delta with too many literal bytes' \
- 'printf "\0\1\2XX" > too_big_literal &&
- test_must_fail test-tool delta -p /dev/null too_big_literal /dev/null'
+test_expect_success 'apply delta with too many literal bytes' '
+ printf "\0\1\2XX" > too_big_literal &&
+ test_must_fail test-tool delta -p /dev/null too_big_literal /dev/null
+'
# \4 - four bytes in base
# \1 - one byte in result
# \221 - copy, one byte offset, one byte size
# \0 - copy from offset 0
# \2 - copy two bytes (one too many)
-test_expect_success \
- 'apply delta with too many copied bytes' \
- 'printf "\4\1\221\0\2" > too_big_copy &&
- printf base >base &&
- test_must_fail test-tool delta -p base too_big_copy /dev/null'
+test_expect_success 'apply delta with too many copied bytes' '
+ printf "\4\1\221\0\2" > too_big_copy &&
+ printf base >base &&
+ test_must_fail test-tool delta -p base too_big_copy /dev/null
+'
# \0 - empty base
# \2 - two bytes in result
# \2 - two literal bytes (we are short one)
-test_expect_success \
- 'apply delta with too few literal bytes' \
- 'printf "\0\2\2X" > truncated_delta &&
- test_must_fail test-tool delta -p /dev/null truncated_delta /dev/null'
+test_expect_success 'apply delta with too few literal bytes' '
+ printf "\0\2\2X" > truncated_delta &&
+ test_must_fail test-tool delta -p /dev/null truncated_delta /dev/null
+'
# \0 - empty base
# \1 - one byte in result
# \221 - copy, one byte offset, one byte size
# \0 - copy from offset 0
# \1 - copy one byte (we are short one)
-test_expect_success \
- 'apply delta with too few bytes in base' \
- 'printf "\0\1\221\0\1" > truncated_base &&
- test_must_fail test-tool delta -p /dev/null truncated_base /dev/null'
+test_expect_success 'apply delta with too few bytes in base' '
+ printf "\0\1\221\0\1" > truncated_base &&
+ test_must_fail test-tool delta -p /dev/null truncated_base /dev/null
+'
# \4 - four bytes in base
# \2 - two bytes in result
@@ -364,20 +366,20 @@ test_expect_success \
#
# Note that the literal byte is necessary to get past the uninteresting minimum
# delta size check.
-test_expect_success \
- 'apply delta with truncated copy parameters' \
- 'printf "\4\2\1X\221" > truncated_copy_delta &&
- printf base >base &&
- test_must_fail test-tool delta -p base truncated_copy_delta /dev/null'
+test_expect_success 'apply delta with truncated copy parameters' '
+ printf "\4\2\1X\221" > truncated_copy_delta &&
+ printf base >base &&
+ test_must_fail test-tool delta -p base truncated_copy_delta /dev/null
+'
# \0 - empty base
# \1 - one byte in result
# \1 - one literal byte (X)
# \1 - trailing garbage command
-test_expect_success \
- 'apply delta with trailing garbage literal' \
- 'printf "\0\1\1X\1" > tail_garbage_literal &&
- test_must_fail test-tool delta -p /dev/null tail_garbage_literal /dev/null'
+test_expect_success 'apply delta with trailing garbage literal' '
+ printf "\0\1\1X\1" > tail_garbage_literal &&
+ test_must_fail test-tool delta -p /dev/null tail_garbage_literal /dev/null
+'
# \4 - four bytes in base
# \1 - one byte in result
@@ -385,19 +387,19 @@ test_expect_success \
# \221 - copy, one byte offset, one byte size
# \0 - copy from offset 0
# \1 - copy 1 byte
-test_expect_success \
- 'apply delta with trailing garbage copy' \
- 'printf "\4\1\1X\221\0\1" > tail_garbage_copy &&
- printf base >base &&
- test_must_fail test-tool delta -p /dev/null tail_garbage_copy /dev/null'
+test_expect_success 'apply delta with trailing garbage copy' '
+ printf "\4\1\1X\221\0\1" > tail_garbage_copy &&
+ printf base >base &&
+ test_must_fail test-tool delta -p /dev/null tail_garbage_copy /dev/null
+'
# \0 - empty base
# \1 - one byte in result
# \1 - one literal byte (X)
# \0 - bogus opcode
-test_expect_success \
- 'apply delta with trailing garbage opcode' \
- 'printf "\0\1\1X\0" > tail_garbage_opcode &&
- test_must_fail test-tool delta -p /dev/null tail_garbage_opcode /dev/null'
+test_expect_success 'apply delta with trailing garbage opcode' '
+ printf "\0\1\1X\0" > tail_garbage_opcode &&
+ test_must_fail test-tool delta -p /dev/null tail_garbage_opcode /dev/null
+'
test_done
diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh
index 7cabb85..1f1f664 100755
--- a/t/t5304-prune.sh
+++ b/t/t5304-prune.sh
@@ -16,7 +16,7 @@ add_blob() {
before=$(git count-objects | sed "s/ .*//") &&
BLOB=$(echo aleph_0 | git hash-object -w --stdin) &&
BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
- verbose test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+ test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
test_path_is_file $BLOB_FILE &&
test-tool chmtime =+0 $BLOB_FILE
}
@@ -29,6 +29,14 @@ test_expect_success setup '
git gc
'
+test_expect_success 'bare repo prune is quiet without $GIT_DIR/objects/pack' '
+ git clone -q --shared --template= --bare . bare.git &&
+ rmdir bare.git/objects/pack &&
+ git --git-dir=bare.git prune --no-progress 2>prune.err &&
+ test_must_be_empty prune.err &&
+ rm -r bare.git prune.err
+'
+
test_expect_success 'prune stale packs' '
orig_pack=$(echo .git/objects/pack/*.pack) &&
>.git/objects/tmp_1.pack &&
@@ -43,34 +51,42 @@ test_expect_success 'prune stale packs' '
test_expect_success 'prune --expire' '
add_blob &&
git prune --expire=1.hour.ago &&
- verbose test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+ test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
test_path_is_file $BLOB_FILE &&
test-tool chmtime =-86500 $BLOB_FILE &&
git prune --expire 1.day &&
- verbose test $before = $(git count-objects | sed "s/ .*//") &&
+ test $before = $(git count-objects | sed "s/ .*//") &&
test_path_is_missing $BLOB_FILE
'
test_expect_success 'gc: implicit prune --expire' '
add_blob &&
test-tool chmtime =-$((2*$week-30)) $BLOB_FILE &&
- git gc &&
- verbose test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+ git gc --no-cruft &&
+ test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
test_path_is_file $BLOB_FILE &&
test-tool chmtime =-$((2*$week+1)) $BLOB_FILE &&
- git gc &&
- verbose test $before = $(git count-objects | sed "s/ .*//") &&
+ git gc --no-cruft &&
+ test $before = $(git count-objects | sed "s/ .*//") &&
test_path_is_missing $BLOB_FILE
'
test_expect_success 'gc: refuse to start with invalid gc.pruneExpire' '
- git config gc.pruneExpire invalid &&
- test_must_fail git gc
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ >repo/.git/config &&
+ git -C repo config gc.pruneExpire invalid &&
+ cat >expect <<-\EOF &&
+ error: Invalid gc.pruneexpire: '\''invalid'\''
+ fatal: bad config variable '\''gc.pruneexpire'\'' in file '\''.git/config'\'' at line 2
+ EOF
+ test_must_fail git -C repo gc 2>actual &&
+ test_cmp expect actual
'
test_expect_success 'gc: start with ok gc.pruneExpire' '
git config gc.pruneExpire 2.days.ago &&
- git gc
+ git gc --no-cruft
'
test_expect_success 'prune: prune nonsense parameters' '
@@ -121,44 +137,44 @@ test_expect_success 'gc --no-prune' '
add_blob &&
test-tool chmtime =-$((5001*$day)) $BLOB_FILE &&
git config gc.pruneExpire 2.days.ago &&
- git gc --no-prune &&
- verbose test 1 = $(git count-objects | sed "s/ .*//") &&
+ git gc --no-prune --no-cruft &&
+ test 1 = $(git count-objects | sed "s/ .*//") &&
test_path_is_file $BLOB_FILE
'
test_expect_success 'gc respects gc.pruneExpire' '
git config gc.pruneExpire 5002.days.ago &&
- git gc &&
+ git gc --no-cruft &&
test_path_is_file $BLOB_FILE &&
git config gc.pruneExpire 5000.days.ago &&
- git gc &&
+ git gc --no-cruft &&
test_path_is_missing $BLOB_FILE
'
test_expect_success 'gc --prune=<date>' '
add_blob &&
test-tool chmtime =-$((5001*$day)) $BLOB_FILE &&
- git gc --prune=5002.days.ago &&
+ git gc --prune=5002.days.ago --no-cruft &&
test_path_is_file $BLOB_FILE &&
- git gc --prune=5000.days.ago &&
+ git gc --prune=5000.days.ago --no-cruft &&
test_path_is_missing $BLOB_FILE
'
test_expect_success 'gc --prune=never' '
add_blob &&
- git gc --prune=never &&
+ git gc --prune=never --no-cruft &&
test_path_is_file $BLOB_FILE &&
- git gc --prune=now &&
+ git gc --prune=now --no-cruft &&
test_path_is_missing $BLOB_FILE
'
test_expect_success 'gc respects gc.pruneExpire=never' '
git config gc.pruneExpire never &&
add_blob &&
- git gc &&
+ git gc --no-cruft &&
test_path_is_file $BLOB_FILE &&
git config gc.pruneExpire now &&
- git gc &&
+ git gc --no-cruft &&
test_path_is_missing $BLOB_FILE
'
@@ -176,10 +192,10 @@ test_expect_success 'gc: prune old objects after local clone' '
git clone --no-hardlinks . aclone &&
(
cd aclone &&
- verbose test 1 = $(git count-objects | sed "s/ .*//") &&
+ test 1 = $(git count-objects | sed "s/ .*//") &&
test_path_is_file $BLOB_FILE &&
- git gc --prune &&
- verbose test 0 = $(git count-objects | sed "s/ .*//") &&
+ git gc --prune --no-cruft &&
+ test 0 = $(git count-objects | sed "s/ .*//") &&
test_path_is_missing $BLOB_FILE
)
'
@@ -221,7 +237,7 @@ test_expect_success 'clean pack garbage with gc' '
>.git/objects/pack/fake2.keep &&
>.git/objects/pack/fake2.idx &&
>.git/objects/pack/fake3.keep &&
- git gc &&
+ git gc --no-cruft &&
git count-objects -v 2>stderr &&
grep "^warning:" stderr | sort >actual &&
cat >expected <<\EOF &&
@@ -291,6 +307,7 @@ test_expect_success 'prune: handle HEAD reflog in multiple worktrees' '
cat ../expected >blob &&
git add blob &&
git commit -m "second commit in third" &&
+ git clean -f && # Remove untracked left behind by deleting index
git reset --hard HEAD^
) &&
git prune --expire=now &&
@@ -301,10 +318,10 @@ test_expect_success 'prune: handle HEAD reflog in multiple worktrees' '
test_expect_success 'prune: handle expire option correctly' '
test_must_fail git prune --expire 2>error &&
- test_i18ngrep "requires a value" error &&
+ test_grep "requires a value" error &&
test_must_fail git prune --expire=nyah 2>error &&
- test_i18ngrep "malformed expiration" error &&
+ test_grep "malformed expiration" error &&
git prune --no-expire
'
@@ -333,4 +350,18 @@ test_expect_success 'old reachable-from-recent retained with bitmaps' '
test_must_fail git cat-file -e $to_drop
'
+test_expect_success 'gc.recentObjectsHook' '
+ add_blob &&
+ test-tool chmtime =-86500 $BLOB_FILE &&
+
+ write_script precious-objects <<-EOF &&
+ echo $BLOB
+ EOF
+ test_config gc.recentObjectsHook ./precious-objects &&
+
+ git prune --expire=now &&
+
+ git cat-file -p $BLOB
+'
+
test_done
diff --git a/t/t5306-pack-nobase.sh b/t/t5306-pack-nobase.sh
index f4931c0..0d50c6b 100755
--- a/t/t5306-pack-nobase.sh
+++ b/t/t5306-pack-nobase.sh
@@ -6,22 +6,23 @@
test_description='git-pack-object with missing base
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Create A-B chain
#
-test_expect_success \
- 'setup base' \
- 'for a in a b c d e f g h i; do echo $a >>text; done &&
- echo side >side &&
- git update-index --add text side &&
- A=$(echo A | git commit-tree $(git write-tree)) &&
+test_expect_success 'setup base' '
+ test_write_lines a b c d e f g h i >text &&
+ echo side >side &&
+ git update-index --add text side &&
+ A=$(echo A | git commit-tree $(git write-tree)) &&
- echo m >>text &&
- git update-index text &&
- B=$(echo B | git commit-tree $(git write-tree) -p $A) &&
- git update-ref HEAD $B
- '
+ echo m >>text &&
+ git update-index text &&
+ B=$(echo B | git commit-tree $(git write-tree) -p $A) &&
+ git update-ref HEAD $B
+'
# Create repository with C whose parent is B.
# Repository contains C, C^{tree}, C:text, B, B^{tree}.
@@ -29,52 +30,49 @@ test_expect_success \
# Repository is missing A (parent of B).
# Repository is missing A:side.
#
-test_expect_success \
- 'setup patch_clone' \
- 'base_objects=$(pwd)/.git/objects &&
- (mkdir patch_clone &&
- cd patch_clone &&
- git init &&
- echo "$base_objects" >.git/objects/info/alternates &&
- echo q >>text &&
- git read-tree $B &&
- git update-index text &&
- git update-ref HEAD $(echo C | git commit-tree $(git write-tree) -p $B) &&
- rm .git/objects/info/alternates &&
+test_expect_success 'setup patch_clone' '
+ base_objects=$(pwd)/.git/objects &&
+ (mkdir patch_clone &&
+ cd patch_clone &&
+ git init &&
+ echo "$base_objects" >.git/objects/info/alternates &&
+ echo q >>text &&
+ git read-tree $B &&
+ git update-index text &&
+ git update-ref HEAD $(echo C | git commit-tree $(git write-tree) -p $B) &&
+ rm .git/objects/info/alternates &&
- git --git-dir=../.git cat-file commit $B |
- git hash-object -t commit -w --stdin &&
+ git --git-dir=../.git cat-file commit $B |
+ git hash-object -t commit -w --stdin &&
- git --git-dir=../.git cat-file tree "$B^{tree}" |
- git hash-object -t tree -w --stdin
- ) &&
- C=$(git --git-dir=patch_clone/.git rev-parse HEAD)
- '
+ git --git-dir=../.git cat-file tree "$B^{tree}" |
+ git hash-object -t tree -w --stdin
+ ) &&
+ C=$(git --git-dir=patch_clone/.git rev-parse HEAD)
+'
# Clone patch_clone indirectly by cloning base and fetching.
#
-test_expect_success \
- 'indirectly clone patch_clone' \
- '(mkdir user_clone &&
- cd user_clone &&
- git init &&
- git pull ../.git &&
- test $(git rev-parse HEAD) = $B &&
+test_expect_success 'indirectly clone patch_clone' '
+ (mkdir user_clone &&
+ cd user_clone &&
+ git init &&
+ git pull ../.git &&
+ test $(git rev-parse HEAD) = $B &&
- git pull ../patch_clone/.git &&
- test $(git rev-parse HEAD) = $C
- )
- '
+ git pull ../patch_clone/.git &&
+ test $(git rev-parse HEAD) = $C
+ )
+'
# Cloning the patch_clone directly should fail.
#
-test_expect_success \
- 'clone of patch_clone is incomplete' \
- '(mkdir user_direct &&
- cd user_direct &&
- git init &&
- test_must_fail git fetch ../patch_clone/.git
- )
- '
+test_expect_success 'clone of patch_clone is incomplete' '
+ (mkdir user_direct &&
+ cd user_direct &&
+ git init &&
+ test_must_fail git fetch ../patch_clone/.git
+ )
+'
test_done
diff --git a/t/t5307-pack-missing-commit.sh b/t/t5307-pack-missing-commit.sh
index f4338ab..1e02c30 100755
--- a/t/t5307-pack-missing-commit.sh
+++ b/t/t5307-pack-missing-commit.sh
@@ -2,6 +2,7 @@
test_description='pack should notice missing commit objects'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -11,7 +12,7 @@ test_expect_success setup '
git add "file$i" &&
test_tick &&
git commit -m "$i" &&
- git tag "tag$i"
+ git tag "tag$i" || return 1
done &&
obj=$(git rev-parse --verify tag3) &&
fanout=$(expr "$obj" : "\(..\)") &&
diff --git a/t/t5308-pack-detect-duplicates.sh b/t/t5308-pack-detect-duplicates.sh
index 693b241..655cafa 100755
--- a/t/t5308-pack-detect-duplicates.sh
+++ b/t/t5308-pack-detect-duplicates.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='handling of duplicate objects in incoming packfiles'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-pack.sh
diff --git a/t/t5309-pack-delta-cycles.sh b/t/t5309-pack-delta-cycles.sh
index 55b7876..4e910c5 100755
--- a/t/t5309-pack-delta-cycles.sh
+++ b/t/t5309-pack-delta-cycles.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test index-pack handling of delta cycles in packfiles'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-pack.sh
diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh
index b028387..d7fd713 100755
--- a/t/t5310-pack-bitmaps.sh
+++ b/t/t5310-pack-bitmaps.sh
@@ -1,13 +1,18 @@
#!/bin/sh
test_description='exercise basic bitmap functionality'
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
-. "$TEST_DIRECTORY"/lib-bundle.sh
. "$TEST_DIRECTORY"/lib-bitmap.sh
+# t5310 deals only with single-pack bitmaps, so don't write MIDX bitmaps in
+# their place.
+GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0
+
+# Likewise, allow individual tests to control whether or not they use
+# the boundary-based traversal.
+sane_unset GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL
+
objpath () {
echo ".git/objects/$(echo "$1" | sed -e 's|\(..\)|\1/|')"
}
@@ -25,594 +30,501 @@ has_any () {
grep -Ff "$1" "$2"
}
-# To ensure the logic for "maximal commits" is exercised, make
-# the repository a bit more complicated.
-#
-# other second
-# * *
-# (99 commits) (99 commits)
-# * *
-# |\ /|
-# | * octo-other octo-second * |
-# |/|\_________ ____________/|\|
-# | \ \/ __________/ |
-# | | ________/\ / |
-# * |/ * merge-right *
-# | _|__________/ \____________ |
-# |/ | \|
-# (l1) * * merge-left * (r1)
-# | / \________________________ |
-# |/ \|
-# (l2) * * (r2)
-# \___________________________ |
-# \|
-# * (base)
-#
-# We only push bits down the first-parent history, which
-# makes some of these commits unimportant!
-#
-# The important part for the maximal commit algorithm is how
-# the bitmasks are extended. Assuming starting bit positions
-# for second (bit 0) and other (bit 1), the bitmasks at the
-# end should be:
-#
-# second: 1 (maximal, selected)
-# other: 01 (maximal, selected)
-# (base): 11 (maximal)
-#
-# This complicated history was important for a previous
-# version of the walk that guarantees never walking a
-# commit multiple times. That goal might be important
-# again, so preserve this complicated case. For now, this
-# test will guarantee that the bitmaps are computed
-# correctly, even with the repeat calculations.
-
-test_expect_success 'setup repo with moderate-sized history' '
- test_commit_bulk --id=file 10 &&
- git branch -M second &&
- git checkout -b other HEAD~5 &&
- test_commit_bulk --id=side 10 &&
-
- # add complicated history setup, including merges and
- # ambiguous merge-bases
-
- git checkout -b merge-left other~2 &&
- git merge second~2 -m "merge-left" &&
-
- git checkout -b merge-right second~1 &&
- git merge other~1 -m "merge-right" &&
-
- git checkout -b octo-second second &&
- git merge merge-left merge-right -m "octopus-second" &&
-
- git checkout -b octo-other other &&
- git merge merge-left merge-right -m "octopus-other" &&
-
- git checkout other &&
- git merge octo-other -m "pull octopus" &&
-
- git checkout second &&
- git merge octo-second -m "pull octopus" &&
-
- # Remove these branches so they are not selected
- # as bitmap tips
- git branch -D merge-left &&
- git branch -D merge-right &&
- git branch -D octo-other &&
- git branch -D octo-second &&
-
- # add padding to make these merges less interesting
- # and avoid having them selected for bitmaps
- test_commit_bulk --id=file 100 &&
- git checkout other &&
- test_commit_bulk --id=side 100 &&
- git checkout second &&
-
- bitmaptip=$(git rev-parse second) &&
- blob=$(echo tagged-blob | git hash-object -w --stdin) &&
- git tag tagged-blob $blob &&
- git config repack.writebitmaps true
-'
-
-test_expect_success 'full repack creates bitmaps' '
- GIT_TRACE2_EVENT_NESTING=4 GIT_TRACE2_EVENT="$(pwd)/trace" \
- git repack -ad &&
- ls .git/objects/pack/ | grep bitmap >output &&
- test_line_count = 1 output &&
- grep "\"key\":\"num_selected_commits\",\"value\":\"106\"" trace &&
- grep "\"key\":\"num_maximal_commits\",\"value\":\"107\"" trace
-'
+test_bitmap_cases () {
+ writeLookupTable=false
+ for i in "$@"
+ do
+ case "$i" in
+ "pack.writeBitmapLookupTable") writeLookupTable=true;;
+ esac
+ done
-test_expect_success 'rev-list --test-bitmap verifies bitmaps' '
- git rev-list --test-bitmap HEAD
-'
+ test_expect_success 'setup test repository' '
+ rm -fr * .git &&
+ git init &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"'
+ '
+ setup_bitmap_history
-rev_list_tests_head () {
- test_expect_success "counting commits via bitmap ($state, $branch)" '
- git rev-list --count $branch >expect &&
- git rev-list --use-bitmap-index --count $branch >actual &&
- test_cmp expect actual
+ test_expect_success 'setup writing bitmaps during repack' '
+ git config repack.writeBitmaps true
'
- test_expect_success "counting partial commits via bitmap ($state, $branch)" '
- git rev-list --count $branch~5..$branch >expect &&
- git rev-list --use-bitmap-index --count $branch~5..$branch >actual &&
- test_cmp expect actual
+ test_expect_success 'full repack creates bitmaps' '
+ GIT_TRACE2_EVENT="$(pwd)/trace" \
+ git repack -ad &&
+ ls .git/objects/pack/ | grep bitmap >output &&
+ test_line_count = 1 output &&
+ grep "\"key\":\"num_selected_commits\",\"value\":\"106\"" trace &&
+ grep "\"key\":\"num_maximal_commits\",\"value\":\"107\"" trace
'
- test_expect_success "counting commits with limit ($state, $branch)" '
- git rev-list --count -n 1 $branch >expect &&
- git rev-list --use-bitmap-index --count -n 1 $branch >actual &&
- test_cmp expect actual
+ basic_bitmap_tests
+
+ test_expect_success 'pack-objects respects --local (non-local loose)' '
+ git init --bare alt.git &&
+ echo $(pwd)/alt.git/objects >.git/objects/info/alternates &&
+ echo content1 >file1 &&
+ # non-local loose object which is not present in bitmapped pack
+ altblob=$(GIT_DIR=alt.git git hash-object -w file1) &&
+ # non-local loose object which is also present in bitmapped pack
+ git cat-file blob $blob | GIT_DIR=alt.git git hash-object -w --stdin &&
+ git add file1 &&
+ test_tick &&
+ git commit -m commit_file1 &&
+ echo HEAD | git pack-objects --local --stdout --revs >1.pack &&
+ git index-pack 1.pack &&
+ list_packed_objects 1.idx >1.objects &&
+ printf "%s\n" "$altblob" "$blob" >nonlocal-loose &&
+ ! has_any nonlocal-loose 1.objects
'
- test_expect_success "counting non-linear history ($state, $branch)" '
- git rev-list --count other...second >expect &&
- git rev-list --use-bitmap-index --count other...second >actual &&
- test_cmp expect actual
+ test_expect_success 'pack-objects respects --honor-pack-keep (local non-bitmapped pack)' '
+ echo content2 >file2 &&
+ blob2=$(git hash-object -w file2) &&
+ git add file2 &&
+ test_tick &&
+ git commit -m commit_file2 &&
+ printf "%s\n" "$blob2" "$bitmaptip" >keepobjects &&
+ pack2=$(git pack-objects pack2 <keepobjects) &&
+ mv pack2-$pack2.* .git/objects/pack/ &&
+ >.git/objects/pack/pack2-$pack2.keep &&
+ rm $(objpath $blob2) &&
+ echo HEAD | git pack-objects --honor-pack-keep --stdout --revs >2a.pack &&
+ git index-pack 2a.pack &&
+ list_packed_objects 2a.idx >2a.objects &&
+ ! has_any keepobjects 2a.objects
'
- test_expect_success "counting commits with limiting ($state, $branch)" '
- git rev-list --count $branch -- 1.t >expect &&
- git rev-list --use-bitmap-index --count $branch -- 1.t >actual &&
- test_cmp expect actual
+ test_expect_success 'pack-objects respects --local (non-local pack)' '
+ mv .git/objects/pack/pack2-$pack2.* alt.git/objects/pack/ &&
+ echo HEAD | git pack-objects --local --stdout --revs >2b.pack &&
+ git index-pack 2b.pack &&
+ list_packed_objects 2b.idx >2b.objects &&
+ ! has_any keepobjects 2b.objects
'
- test_expect_success "counting objects via bitmap ($state, $branch)" '
- git rev-list --count --objects $branch >expect &&
- git rev-list --use-bitmap-index --count --objects $branch >actual &&
- test_cmp expect actual
+ test_expect_success 'pack-objects respects --honor-pack-keep (local bitmapped pack)' '
+ ls .git/objects/pack/ | grep bitmap >output &&
+ test_line_count = 1 output &&
+ packbitmap=$(basename $(cat output) .bitmap) &&
+ list_packed_objects .git/objects/pack/$packbitmap.idx >packbitmap.objects &&
+ test_when_finished "rm -f .git/objects/pack/$packbitmap.keep" &&
+ >.git/objects/pack/$packbitmap.keep &&
+ echo HEAD | git pack-objects --honor-pack-keep --stdout --revs >3a.pack &&
+ git index-pack 3a.pack &&
+ list_packed_objects 3a.idx >3a.objects &&
+ ! has_any packbitmap.objects 3a.objects
'
- test_expect_success "enumerate commits ($state, $branch)" '
- git rev-list --use-bitmap-index $branch >actual &&
- git rev-list $branch >expect &&
- test_bitmap_traversal --no-confirm-bitmaps expect actual
+ test_expect_success 'pack-objects respects --local (non-local bitmapped pack)' '
+ mv .git/objects/pack/$packbitmap.* alt.git/objects/pack/ &&
+ rm -f .git/objects/pack/multi-pack-index &&
+ test_when_finished "mv alt.git/objects/pack/$packbitmap.* .git/objects/pack/" &&
+ echo HEAD | git pack-objects --local --stdout --revs >3b.pack &&
+ git index-pack 3b.pack &&
+ list_packed_objects 3b.idx >3b.objects &&
+ ! has_any packbitmap.objects 3b.objects
'
- test_expect_success "enumerate --objects ($state, $branch)" '
- git rev-list --objects --use-bitmap-index $branch >actual &&
- git rev-list --objects $branch >expect &&
- test_bitmap_traversal expect actual
+ test_expect_success 'pack-objects to file can use bitmap' '
+ # make sure we still have 1 bitmap index from previous tests
+ ls .git/objects/pack/ | grep bitmap >output &&
+ test_line_count = 1 output &&
+ # verify equivalent packs are generated with/without using bitmap index
+ packasha1=$(git pack-objects --no-use-bitmap-index --all packa </dev/null) &&
+ packbsha1=$(git pack-objects --use-bitmap-index --all packb </dev/null) &&
+ list_packed_objects packa-$packasha1.idx >packa.objects &&
+ list_packed_objects packb-$packbsha1.idx >packb.objects &&
+ test_cmp packa.objects packb.objects
'
- test_expect_success "bitmap --objects handles non-commit objects ($state, $branch)" '
- git rev-list --objects --use-bitmap-index $branch tagged-blob >actual &&
- grep $blob actual
+ test_expect_success 'full repack, reusing previous bitmaps' '
+ git repack -ad &&
+ ls .git/objects/pack/ | grep bitmap >output &&
+ test_line_count = 1 output
'
-}
-rev_list_tests () {
- state=$1
+ test_expect_success 'fetch (full bitmap)' '
+ git --git-dir=clone.git fetch origin second:second &&
+ git rev-parse HEAD >expect &&
+ git --git-dir=clone.git rev-parse HEAD >actual &&
+ test_cmp expect actual
+ '
- for branch in "second" "other"
- do
- rev_list_tests_head
- done
-}
+ test_expect_success 'create objects for missing-HAVE tests' '
+ blob=$(echo "missing have" | git hash-object -w --stdin) &&
+ tree=$(printf "100644 blob $blob\tfile\n" | git mktree) &&
+ parent=$(echo parent | git commit-tree $tree) &&
+ commit=$(echo commit | git commit-tree $tree -p $parent) &&
+ cat >revs <<-EOF
+ HEAD
+ ^HEAD^
+ ^$commit
+ EOF
+ '
-rev_list_tests 'full bitmap'
+ test_expect_success 'pack-objects respects --incremental' '
+ cat >revs2 <<-EOF &&
+ HEAD
+ $commit
+ EOF
+ git pack-objects --incremental --stdout --revs <revs2 >4.pack &&
+ git index-pack 4.pack &&
+ list_packed_objects 4.idx >4.objects &&
+ test_line_count = 4 4.objects &&
+ git rev-list --objects $commit >revlist &&
+ cut -d" " -f1 revlist |sort >objects &&
+ test_cmp 4.objects objects
+ '
-test_expect_success 'clone from bitmapped repository' '
- git clone --no-local --bare . clone.git &&
- git rev-parse HEAD >expect &&
- git --git-dir=clone.git rev-parse HEAD >actual &&
- test_cmp expect actual
-'
+ test_expect_success 'pack with missing blob' '
+ rm $(objpath $blob) &&
+ git pack-objects --stdout --revs <revs >/dev/null
+ '
-test_expect_success 'partial clone from bitmapped repository' '
- test_config uploadpack.allowfilter true &&
- git clone --no-local --bare --filter=blob:none . partial-clone.git &&
- (
- cd partial-clone.git &&
- pack=$(echo objects/pack/*.pack) &&
- git verify-pack -v "$pack" >have &&
- awk "/blob/ { print \$1 }" <have >blobs &&
- # we expect this single blob because of the direct ref
- git rev-parse refs/tags/tagged-blob >expect &&
- test_cmp expect blobs
- )
-'
+ test_expect_success 'pack with missing tree' '
+ rm $(objpath $tree) &&
+ git pack-objects --stdout --revs <revs >/dev/null
+ '
-test_expect_success 'setup further non-bitmapped commits' '
- test_commit_bulk --id=further 10
-'
+ test_expect_success 'pack with missing parent' '
+ rm $(objpath $parent) &&
+ git pack-objects --stdout --revs <revs >/dev/null
+ '
-rev_list_tests 'partial bitmap'
+ test_expect_success JGIT,SHA1 'we can read jgit bitmaps' '
+ git clone --bare . compat-jgit.git &&
+ (
+ cd compat-jgit.git &&
+ rm -f objects/pack/*.bitmap &&
+ jgit gc &&
+ git rev-list --test-bitmap HEAD
+ )
+ '
-test_expect_success 'fetch (partial bitmap)' '
- git --git-dir=clone.git fetch origin second:second &&
- git rev-parse HEAD >expect &&
- git --git-dir=clone.git rev-parse HEAD >actual &&
- test_cmp expect actual
-'
+ test_expect_success JGIT,SHA1 'jgit can read our bitmaps' '
+ git clone --bare . compat-us.git &&
+ (
+ cd compat-us.git &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
+ git repack -adb &&
+ # jgit gc will barf if it does not like our bitmaps
+ jgit gc
+ )
+ '
-test_expect_success 'incremental repack fails when bitmaps are requested' '
- test_commit more-1 &&
- test_must_fail git repack -d 2>err &&
- test_i18ngrep "Incremental repacks are incompatible with bitmap" err
-'
+ test_expect_success 'splitting packs does not generate bogus bitmaps' '
+ test-tool genrandom foo $((1024 * 1024)) >rand &&
+ git add rand &&
+ git commit -m "commit with big file" &&
+ git -c pack.packSizeLimit=500k repack -adb &&
+ git init --bare no-bitmaps.git &&
+ git -C no-bitmaps.git fetch .. HEAD
+ '
-test_expect_success 'incremental repack can disable bitmaps' '
- test_commit more-2 &&
- git repack -d --no-write-bitmap-index
-'
+ test_expect_success 'set up reusable pack' '
+ rm -f .git/objects/pack/*.keep &&
+ git repack -adb &&
+ reusable_pack () {
+ git for-each-ref --format="%(objectname)" |
+ git pack-objects --delta-base-offset --revs --stdout "$@"
+ }
+ '
-test_expect_success 'pack-objects respects --local (non-local loose)' '
- git init --bare alt.git &&
- echo $(pwd)/alt.git/objects >.git/objects/info/alternates &&
- echo content1 >file1 &&
- # non-local loose object which is not present in bitmapped pack
- altblob=$(GIT_DIR=alt.git git hash-object -w file1) &&
- # non-local loose object which is also present in bitmapped pack
- git cat-file blob $blob | GIT_DIR=alt.git git hash-object -w --stdin &&
- git add file1 &&
- test_tick &&
- git commit -m commit_file1 &&
- echo HEAD | git pack-objects --local --stdout --revs >1.pack &&
- git index-pack 1.pack &&
- list_packed_objects 1.idx >1.objects &&
- printf "%s\n" "$altblob" "$blob" >nonlocal-loose &&
- ! has_any nonlocal-loose 1.objects
-'
+ test_expect_success 'pack reuse respects --honor-pack-keep' '
+ test_when_finished "rm -f .git/objects/pack/*.keep" &&
+ for i in .git/objects/pack/*.pack
+ do
+ >${i%.pack}.keep || return 1
+ done &&
+ reusable_pack --honor-pack-keep >empty.pack &&
+ git index-pack empty.pack &&
+ git show-index <empty.idx >actual &&
+ test_must_be_empty actual
+ '
-test_expect_success 'pack-objects respects --honor-pack-keep (local non-bitmapped pack)' '
- echo content2 >file2 &&
- blob2=$(git hash-object -w file2) &&
- git add file2 &&
- test_tick &&
- git commit -m commit_file2 &&
- printf "%s\n" "$blob2" "$bitmaptip" >keepobjects &&
- pack2=$(git pack-objects pack2 <keepobjects) &&
- mv pack2-$pack2.* .git/objects/pack/ &&
- >.git/objects/pack/pack2-$pack2.keep &&
- rm $(objpath $blob2) &&
- echo HEAD | git pack-objects --honor-pack-keep --stdout --revs >2a.pack &&
- git index-pack 2a.pack &&
- list_packed_objects 2a.idx >2a.objects &&
- ! has_any keepobjects 2a.objects
-'
+ test_expect_success 'pack reuse respects --local' '
+ mv .git/objects/pack/* alt.git/objects/pack/ &&
+ test_when_finished "mv alt.git/objects/pack/* .git/objects/pack/" &&
+ reusable_pack --local >empty.pack &&
+ git index-pack empty.pack &&
+ git show-index <empty.idx >actual &&
+ test_must_be_empty actual
+ '
-test_expect_success 'pack-objects respects --local (non-local pack)' '
- mv .git/objects/pack/pack2-$pack2.* alt.git/objects/pack/ &&
- echo HEAD | git pack-objects --local --stdout --revs >2b.pack &&
- git index-pack 2b.pack &&
- list_packed_objects 2b.idx >2b.objects &&
- ! has_any keepobjects 2b.objects
-'
+ test_expect_success 'pack reuse respects --incremental' '
+ reusable_pack --incremental >empty.pack &&
+ git index-pack empty.pack &&
+ git show-index <empty.idx >actual &&
+ test_must_be_empty actual
+ '
-test_expect_success 'pack-objects respects --honor-pack-keep (local bitmapped pack)' '
- ls .git/objects/pack/ | grep bitmap >output &&
- test_line_count = 1 output &&
- packbitmap=$(basename $(cat output) .bitmap) &&
- list_packed_objects .git/objects/pack/$packbitmap.idx >packbitmap.objects &&
- test_when_finished "rm -f .git/objects/pack/$packbitmap.keep" &&
- >.git/objects/pack/$packbitmap.keep &&
- echo HEAD | git pack-objects --honor-pack-keep --stdout --revs >3a.pack &&
- git index-pack 3a.pack &&
- list_packed_objects 3a.idx >3a.objects &&
- ! has_any packbitmap.objects 3a.objects
-'
+ test_expect_success 'truncated bitmap fails gracefully (ewah)' '
+ test_config pack.writebitmaphashcache false &&
+ test_config pack.writebitmaplookuptable false &&
+ git repack -ad &&
+ git rev-list --use-bitmap-index --count --all >expect &&
+ bitmap=$(ls .git/objects/pack/*.bitmap) &&
+ test_when_finished "rm -f $bitmap" &&
+ test_copy_bytes 256 <$bitmap >$bitmap.tmp &&
+ mv -f $bitmap.tmp $bitmap &&
+ git rev-list --use-bitmap-index --count --all >actual 2>stderr &&
+ test_cmp expect actual &&
+ test_grep corrupt.ewah.bitmap stderr
+ '
-test_expect_success 'pack-objects respects --local (non-local bitmapped pack)' '
- mv .git/objects/pack/$packbitmap.* alt.git/objects/pack/ &&
- rm -f .git/objects/pack/multi-pack-index &&
- test_when_finished "mv alt.git/objects/pack/$packbitmap.* .git/objects/pack/" &&
- echo HEAD | git pack-objects --local --stdout --revs >3b.pack &&
- git index-pack 3b.pack &&
- list_packed_objects 3b.idx >3b.objects &&
- ! has_any packbitmap.objects 3b.objects
-'
+ test_expect_success 'truncated bitmap fails gracefully (cache)' '
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
+ git repack -ad &&
+ git rev-list --use-bitmap-index --count --all >expect &&
+ bitmap=$(ls .git/objects/pack/*.bitmap) &&
+ test_when_finished "rm -f $bitmap" &&
+ test_copy_bytes 512 <$bitmap >$bitmap.tmp &&
+ mv -f $bitmap.tmp $bitmap &&
+ git rev-list --use-bitmap-index --count --all >actual 2>stderr &&
+ test_cmp expect actual &&
+ test_grep corrupted.bitmap.index stderr
+ '
-test_expect_success 'pack-objects to file can use bitmap' '
- # make sure we still have 1 bitmap index from previous tests
- ls .git/objects/pack/ | grep bitmap >output &&
- test_line_count = 1 output &&
- # verify equivalent packs are generated with/without using bitmap index
- packasha1=$(git pack-objects --no-use-bitmap-index --all packa </dev/null) &&
- packbsha1=$(git pack-objects --use-bitmap-index --all packb </dev/null) &&
- list_packed_objects packa-$packasha1.idx >packa.objects &&
- list_packed_objects packb-$packbsha1.idx >packb.objects &&
- test_cmp packa.objects packb.objects
-'
+ # Create a state of history with these properties:
+ #
+ # - refs that allow a client to fetch some new history, while sharing some old
+ # history with the server; we use branches delta-reuse-old and
+ # delta-reuse-new here
+ #
+ # - the new history contains an object that is stored on the server as a delta
+ # against a base that is in the old history
+ #
+ # - the base object is not immediately reachable from the tip of the old
+ # history; finding it would involve digging down through history we know the
+ # other side has
+ #
+ # This should result in a state where fetching from old->new would not
+ # traditionally reuse the on-disk delta (because we'd have to dig to realize
+ # that the client has it), but we will do so if bitmaps can tell us cheaply
+ # that the other side has it.
+ test_expect_success 'set up thin delta-reuse parent' '
+ # This first commit contains the buried base object.
+ test-tool genrandom delta 16384 >file &&
+ git add file &&
+ git commit -m "delta base" &&
+ base=$(git rev-parse --verify HEAD:file) &&
+
+ # These intermediate commits bury the base back in history.
+ # This becomes the "old" state.
+ for i in 1 2 3 4 5
+ do
+ echo $i >file &&
+ git commit -am "intermediate $i" || return 1
+ done &&
+ git branch delta-reuse-old &&
+
+ # And now our new history has a delta against the buried base. Note
+ # that this must be smaller than the original file, since pack-objects
+ # prefers to create deltas from smaller objects to larger.
+ test-tool genrandom delta 16300 >file &&
+ git commit -am "delta result" &&
+ delta=$(git rev-parse --verify HEAD:file) &&
+ git branch delta-reuse-new &&
+
+ # Repack with bitmaps and double check that we have the expected delta
+ # relationship.
+ git repack -adb &&
+ have_delta $delta $base
+ '
-test_expect_success 'full repack, reusing previous bitmaps' '
- git repack -ad &&
- ls .git/objects/pack/ | grep bitmap >output &&
- test_line_count = 1 output
-'
+ # Now we can sanity-check the non-bitmap behavior (that the server is not able
+ # to reuse the delta). This isn't strictly something we care about, so this
+ # test could be scrapped in the future. But it makes sure that the next test is
+ # actually triggering the feature we want.
+ #
+ # Note that our tools for working with on-the-wire "thin" packs are limited. So
+ # we actually perform the fetch, retain the resulting pack, and inspect the
+ # result.
+ test_expect_success 'fetch without bitmaps ignores delta against old base' '
+ test_config pack.usebitmaps false &&
+ test_when_finished "rm -rf client.git" &&
+ git init --bare client.git &&
+ (
+ cd client.git &&
+ git config transfer.unpackLimit 1 &&
+ git fetch .. delta-reuse-old:delta-reuse-old &&
+ git fetch .. delta-reuse-new:delta-reuse-new &&
+ have_delta $delta $ZERO_OID
+ )
+ '
-test_expect_success 'fetch (full bitmap)' '
- git --git-dir=clone.git fetch origin second:second &&
- git rev-parse HEAD >expect &&
- git --git-dir=clone.git rev-parse HEAD >actual &&
- test_cmp expect actual
-'
+ # And do the same for the bitmap case, where we do expect to find the delta.
+ test_expect_success 'fetch with bitmaps can reuse old base' '
+ test_config pack.usebitmaps true &&
+ test_when_finished "rm -rf client.git" &&
+ git init --bare client.git &&
+ (
+ cd client.git &&
+ git config transfer.unpackLimit 1 &&
+ git fetch .. delta-reuse-old:delta-reuse-old &&
+ git fetch .. delta-reuse-new:delta-reuse-new &&
+ have_delta $delta $base
+ )
+ '
-test_expect_success 'create objects for missing-HAVE tests' '
- blob=$(echo "missing have" | git hash-object -w --stdin) &&
- tree=$(printf "100644 blob $blob\tfile\n" | git mktree) &&
- parent=$(echo parent | git commit-tree $tree) &&
- commit=$(echo commit | git commit-tree $tree -p $parent) &&
- cat >revs <<-EOF
- HEAD
- ^HEAD^
- ^$commit
- EOF
-'
+ test_expect_success 'pack.preferBitmapTips' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
-test_expect_success 'pack-objects respects --incremental' '
- cat >revs2 <<-EOF &&
- HEAD
- $commit
- EOF
- git pack-objects --incremental --stdout --revs <revs2 >4.pack &&
- git index-pack 4.pack &&
- list_packed_objects 4.idx >4.objects &&
- test_line_count = 4 4.objects &&
- git rev-list --objects $commit >revlist &&
- cut -d" " -f1 revlist |sort >objects &&
- test_cmp 4.objects objects
-'
+ # create enough commits that not all are receive bitmap
+ # coverage even if they are all at the tip of some reference.
+ test_commit_bulk --message="%s" 103 &&
-test_expect_success 'pack with missing blob' '
- rm $(objpath $blob) &&
- git pack-objects --stdout --revs <revs >/dev/null
-'
+ git rev-list HEAD >commits.raw &&
+ sort <commits.raw >commits &&
-test_expect_success 'pack with missing tree' '
- rm $(objpath $tree) &&
- git pack-objects --stdout --revs <revs >/dev/null
-'
+ git log --format="create refs/tags/%s %H" HEAD >refs &&
+ git update-ref --stdin <refs &&
-test_expect_success 'pack with missing parent' '
- rm $(objpath $parent) &&
- git pack-objects --stdout --revs <revs >/dev/null
-'
+ git repack -adb &&
+ test-tool bitmap list-commits | sort >bitmaps &&
-test_expect_success JGIT,SHA1 'we can read jgit bitmaps' '
- git clone --bare . compat-jgit.git &&
- (
- cd compat-jgit.git &&
- rm -f objects/pack/*.bitmap &&
- jgit gc &&
- git rev-list --test-bitmap HEAD
- )
-'
+ # remember which commits did not receive bitmaps
+ comm -13 bitmaps commits >before &&
+ test_file_not_empty before &&
-test_expect_success JGIT,SHA1 'jgit can read our bitmaps' '
- git clone --bare . compat-us.git &&
- (
- cd compat-us.git &&
- git repack -adb &&
- # jgit gc will barf if it does not like our bitmaps
- jgit gc
- )
-'
+ # mark the commits which did not receive bitmaps as preferred,
+ # and generate the bitmap again
+ perl -pe "s{^}{create refs/tags/include/$. }" <before |
+ git update-ref --stdin &&
+ git -c pack.preferBitmapTips=refs/tags/include repack -adb &&
-test_expect_success 'splitting packs does not generate bogus bitmaps' '
- test-tool genrandom foo $((1024 * 1024)) >rand &&
- git add rand &&
- git commit -m "commit with big file" &&
- git -c pack.packSizeLimit=500k repack -adb &&
- git init --bare no-bitmaps.git &&
- git -C no-bitmaps.git fetch .. HEAD
-'
+ # finally, check that the commit(s) without bitmap coverage
+ # are not the same ones as before
+ test-tool bitmap list-commits | sort >bitmaps &&
+ comm -13 bitmaps commits >after &&
-test_expect_success 'set up reusable pack' '
- rm -f .git/objects/pack/*.keep &&
- git repack -adb &&
- reusable_pack () {
- git for-each-ref --format="%(objectname)" |
- git pack-objects --delta-base-offset --revs --stdout "$@"
- }
-'
+ ! test_cmp before after
+ )
+ '
-test_expect_success 'pack reuse respects --honor-pack-keep' '
- test_when_finished "rm -f .git/objects/pack/*.keep" &&
- for i in .git/objects/pack/*.pack
- do
- >${i%.pack}.keep
- done &&
- reusable_pack --honor-pack-keep >empty.pack &&
- git index-pack empty.pack &&
- git show-index <empty.idx >actual &&
- test_must_be_empty actual
-'
+ test_expect_success 'pack.preferBitmapTips' '
+ git init repo &&
+ test_when_finished "rm -rf repo" &&
+ (
+ cd repo &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
+ test_commit_bulk --message="%s" 103 &&
+
+ cat >>.git/config <<-\EOF &&
+ [pack]
+ preferBitmapTips
+ EOF
+ cat >expect <<-\EOF &&
+ error: missing value for '\''pack.preferbitmaptips'\''
+ EOF
+ git repack -adb 2>actual &&
+ test_cmp expect actual
+ )
+ '
-test_expect_success 'pack reuse respects --local' '
- mv .git/objects/pack/* alt.git/objects/pack/ &&
- test_when_finished "mv alt.git/objects/pack/* .git/objects/pack/" &&
- reusable_pack --local >empty.pack &&
- git index-pack empty.pack &&
- git show-index <empty.idx >actual &&
- test_must_be_empty actual
-'
+ test_expect_success 'complains about multiple pack bitmaps' '
+ rm -fr repo &&
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
-test_expect_success 'pack reuse respects --incremental' '
- reusable_pack --incremental >empty.pack &&
- git index-pack empty.pack &&
- git show-index <empty.idx >actual &&
- test_must_be_empty actual
-'
+ test_commit base &&
-test_expect_success 'truncated bitmap fails gracefully (ewah)' '
- test_config pack.writebitmaphashcache false &&
- git repack -ad &&
- git rev-list --use-bitmap-index --count --all >expect &&
- bitmap=$(ls .git/objects/pack/*.bitmap) &&
- test_when_finished "rm -f $bitmap" &&
- test_copy_bytes 256 <$bitmap >$bitmap.tmp &&
- mv -f $bitmap.tmp $bitmap &&
- git rev-list --use-bitmap-index --count --all >actual 2>stderr &&
- test_cmp expect actual &&
- test_i18ngrep corrupt.ewah.bitmap stderr
-'
+ git repack -adb &&
+ bitmap="$(ls .git/objects/pack/pack-*.bitmap)" &&
+ mv "$bitmap" "$bitmap.bak" &&
-test_expect_success 'truncated bitmap fails gracefully (cache)' '
- git repack -ad &&
- git rev-list --use-bitmap-index --count --all >expect &&
- bitmap=$(ls .git/objects/pack/*.bitmap) &&
- test_when_finished "rm -f $bitmap" &&
- test_copy_bytes 512 <$bitmap >$bitmap.tmp &&
- mv -f $bitmap.tmp $bitmap &&
- git rev-list --use-bitmap-index --count --all >actual 2>stderr &&
- test_cmp expect actual &&
- test_i18ngrep corrupted.bitmap.index stderr
-'
+ test_commit other &&
+ git repack -ab &&
-test_expect_success 'enumerating progress counts pack-reused objects' '
- count=$(git rev-list --objects --all --count) &&
- git repack -adb &&
+ mv "$bitmap.bak" "$bitmap" &&
- # check first with only reused objects; confirm that our progress
- # showed the right number, and also that we did pack-reuse as expected.
- # Check only the final "done" line of the meter (there may be an
- # arbitrary number of intermediate lines ending with CR).
- GIT_PROGRESS_DELAY=0 \
- git pack-objects --all --stdout --progress \
- </dev/null >/dev/null 2>stderr &&
- grep "Enumerating objects: $count, done" stderr &&
- grep "pack-reused $count" stderr &&
-
- # now the same but with one non-reused object
- git commit --allow-empty -m "an extra commit object" &&
- GIT_PROGRESS_DELAY=0 \
- git pack-objects --all --stdout --progress \
- </dev/null >/dev/null 2>stderr &&
- grep "Enumerating objects: $((count+1)), done" stderr &&
- grep "pack-reused $count" stderr
-'
+ find .git/objects/pack -type f -name "*.pack" >packs &&
+ find .git/objects/pack -type f -name "*.bitmap" >bitmaps &&
+ test_line_count = 2 packs &&
+ test_line_count = 2 bitmaps &&
-# have_delta <obj> <expected_base>
-#
-# Note that because this relies on cat-file, it might find _any_ copy of an
-# object in the repository. The caller is responsible for making sure
-# there's only one (e.g., via "repack -ad", or having just fetched a copy).
-have_delta () {
- echo $2 >expect &&
- echo $1 | git cat-file --batch-check="%(deltabase)" >actual &&
- test_cmp expect actual
+ GIT_TRACE2_EVENT=$(pwd)/trace2.txt git rev-list --use-bitmap-index HEAD &&
+ grep "opened bitmap" trace2.txt &&
+ grep "ignoring extra bitmap" trace2.txt
+ )
+ '
}
-# Create a state of history with these properties:
-#
-# - refs that allow a client to fetch some new history, while sharing some old
-# history with the server; we use branches delta-reuse-old and
-# delta-reuse-new here
-#
-# - the new history contains an object that is stored on the server as a delta
-# against a base that is in the old history
-#
-# - the base object is not immediately reachable from the tip of the old
-# history; finding it would involve digging down through history we know the
-# other side has
-#
-# This should result in a state where fetching from old->new would not
-# traditionally reuse the on-disk delta (because we'd have to dig to realize
-# that the client has it), but we will do so if bitmaps can tell us cheaply
-# that the other side has it.
-test_expect_success 'set up thin delta-reuse parent' '
- # This first commit contains the buried base object.
- test-tool genrandom delta 16384 >file &&
- git add file &&
- git commit -m "delta base" &&
- base=$(git rev-parse --verify HEAD:file) &&
-
- # These intermediate commits bury the base back in history.
- # This becomes the "old" state.
- for i in 1 2 3 4 5
- do
- echo $i >file &&
- git commit -am "intermediate $i" || return 1
- done &&
- git branch delta-reuse-old &&
-
- # And now our new history has a delta against the buried base. Note
- # that this must be smaller than the original file, since pack-objects
- # prefers to create deltas from smaller objects to larger.
- test-tool genrandom delta 16300 >file &&
- git commit -am "delta result" &&
- delta=$(git rev-parse --verify HEAD:file) &&
- git branch delta-reuse-new &&
-
- # Repack with bitmaps and double check that we have the expected delta
- # relationship.
- git repack -adb &&
- have_delta $delta $base
-'
+test_bitmap_cases
-# Now we can sanity-check the non-bitmap behavior (that the server is not able
-# to reuse the delta). This isn't strictly something we care about, so this
-# test could be scrapped in the future. But it makes sure that the next test is
-# actually triggering the feature we want.
-#
-# Note that our tools for working with on-the-wire "thin" packs are limited. So
-# we actually perform the fetch, retain the resulting pack, and inspect the
-# result.
-test_expect_success 'fetch without bitmaps ignores delta against old base' '
- test_config pack.usebitmaps false &&
- test_when_finished "rm -rf client.git" &&
- git init --bare client.git &&
- (
- cd client.git &&
- git config transfer.unpackLimit 1 &&
- git fetch .. delta-reuse-old:delta-reuse-old &&
- git fetch .. delta-reuse-new:delta-reuse-new &&
- have_delta $delta $ZERO_OID
- )
-'
+GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1
+export GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL
-# And do the same for the bitmap case, where we do expect to find the delta.
-test_expect_success 'fetch with bitmaps can reuse old base' '
- test_config pack.usebitmaps true &&
- test_when_finished "rm -rf client.git" &&
- git init --bare client.git &&
- (
- cd client.git &&
- git config transfer.unpackLimit 1 &&
- git fetch .. delta-reuse-old:delta-reuse-old &&
- git fetch .. delta-reuse-new:delta-reuse-new &&
- have_delta $delta $base
- )
-'
+test_bitmap_cases
-test_expect_success 'pack.preferBitmapTips' '
- git init repo &&
- test_when_finished "rm -fr repo" &&
- (
- cd repo &&
+sane_unset GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL
- # create enough commits that not all are receive bitmap
- # coverage even if they are all at the tip of some reference.
- test_commit_bulk --message="%s" 103 &&
+test_expect_success 'incremental repack fails when bitmaps are requested' '
+ test_commit more-1 &&
+ test_must_fail git repack -d 2>err &&
+ test_grep "Incremental repacks are incompatible with bitmap" err
+'
- git rev-list HEAD >commits.raw &&
- sort <commits.raw >commits &&
+test_expect_success 'incremental repack can disable bitmaps' '
+ test_commit more-2 &&
+ git repack -d --no-write-bitmap-index
+'
- git log --format="create refs/tags/%s %H" HEAD >refs &&
- git update-ref --stdin <refs &&
+test_expect_success 'boundary-based traversal is used when requested' '
+ git repack -a -d --write-bitmap-index &&
- git repack -adb &&
- test-tool bitmap list-commits | sort >bitmaps &&
+ for argv in \
+ "git -c pack.useBitmapBoundaryTraversal=true" \
+ "git -c feature.experimental=true" \
+ "GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1 git"
+ do
+ eval "GIT_TRACE2_EVENT=1 $argv rev-list --objects \
+ --use-bitmap-index second..other 2>perf" &&
+ grep "\"region_enter\".*\"label\":\"haves/boundary\"" perf ||
+ return 1
+ done &&
- # remember which commits did not receive bitmaps
- comm -13 bitmaps commits >before &&
- test_file_not_empty before &&
+ for argv in \
+ "git -c pack.useBitmapBoundaryTraversal=false" \
+ "git -c feature.experimental=true -c pack.useBitmapBoundaryTraversal=false" \
+ "GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=0 git -c pack.useBitmapBoundaryTraversal=true" \
+ "GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=0 git -c feature.experimental=true"
+ do
+ eval "GIT_TRACE2_EVENT=1 $argv rev-list --objects \
+ --use-bitmap-index second..other 2>perf" &&
+ grep "\"region_enter\".*\"label\":\"haves/classic\"" perf ||
+ return 1
+ done
+'
- # mark the commits which did not receive bitmaps as preferred,
- # and generate the bitmap again
- perl -pe "s{^}{create refs/tags/include/$. }" <before |
- git update-ref --stdin &&
- git -c pack.preferBitmapTips=refs/tags/include repack -adb &&
+test_bitmap_cases "pack.writeBitmapLookupTable"
- # finally, check that the commit(s) without bitmap coverage
- # are not the same ones as before
- test-tool bitmap list-commits | sort >bitmaps &&
- comm -13 bitmaps commits >after &&
+test_expect_success 'verify writing bitmap lookup table when enabled' '
+ GIT_TRACE2_EVENT="$(pwd)/trace2" \
+ git repack -ad &&
+ grep "\"label\":\"writing_lookup_table\"" trace2
+'
- ! test_cmp before after
- )
+test_expect_success 'truncated bitmap fails gracefully (lookup table)' '
+ test_config pack.writebitmaphashcache false &&
+ git repack -adb &&
+ git rev-list --use-bitmap-index --count --all >expect &&
+ bitmap=$(ls .git/objects/pack/*.bitmap) &&
+ test_when_finished "rm -f $bitmap" &&
+ test_copy_bytes 512 <$bitmap >$bitmap.tmp &&
+ mv -f $bitmap.tmp $bitmap &&
+ git rev-list --use-bitmap-index --count --all >actual 2>stderr &&
+ test_cmp expect actual &&
+ test_grep corrupted.bitmap.index stderr
'
test_done
diff --git a/t/t5311-pack-bitmaps-shallow.sh b/t/t5311-pack-bitmaps-shallow.sh
index 872a95d..4fe71fe 100755
--- a/t/t5311-pack-bitmaps-shallow.sh
+++ b/t/t5311-pack-bitmaps-shallow.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='check bitmap operation with shallow repositories'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# We want to create a situation where the shallow, grafted
@@ -17,23 +19,40 @@ test_description='check bitmap operation with shallow repositories'
# the tree for A. But in a shallow one, we've grafted away
# A, and fetching A to B requires that the other side send
# us the tree for file=1.
-test_expect_success 'setup shallow repo' '
- echo 1 >file &&
- git add file &&
- git commit -m orig &&
- echo 2 >file &&
- git commit -a -m update &&
- git clone --no-local --bare --depth=1 . shallow.git &&
- echo 1 >file &&
- git commit -a -m repeat
-'
-
-test_expect_success 'turn on bitmaps in the parent' '
- git repack -adb
-'
-
-test_expect_success 'shallow fetch from bitmapped repo' '
- (cd shallow.git && git fetch)
-'
+test_shallow_bitmaps () {
+ writeLookupTable=false
+
+ for i in "$@"
+ do
+ case $i in
+ "pack.writeBitmapLookupTable") writeLookupTable=true;;
+ esac
+ done
+
+ test_expect_success 'setup shallow repo' '
+ rm -rf * .git &&
+ git init &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
+ echo 1 >file &&
+ git add file &&
+ git commit -m orig &&
+ echo 2 >file &&
+ git commit -a -m update &&
+ git clone --no-local --bare --depth=1 . shallow.git &&
+ echo 1 >file &&
+ git commit -a -m repeat
+ '
+
+ test_expect_success 'turn on bitmaps in the parent' '
+ git repack -adb
+ '
+
+ test_expect_success 'shallow fetch from bitmapped repo' '
+ (cd shallow.git && git fetch)
+ '
+}
+
+test_shallow_bitmaps
+test_shallow_bitmaps "pack.writeBitmapLookupTable"
test_done
diff --git a/t/t5312-prune-corruption.sh b/t/t5312-prune-corruption.sh
index 11423b3..d8d2e30 100755
--- a/t/t5312-prune-corruption.sh
+++ b/t/t5312-prune-corruption.sh
@@ -7,10 +7,14 @@ if we see, for example, a ref with a bogus name, it is OK either to
bail out or to proceed using it as a reachable tip, but it is _not_
OK to proceed as if it did not exist. Otherwise we might silently
delete objects that cannot be recovered.
+
+Note that we do assert command failure in these cases, because that is
+what currently happens. If that changes, these tests should be revisited.
'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'disable reflogs' '
@@ -18,39 +22,58 @@ test_expect_success 'disable reflogs' '
git reflog expire --expire=all --all
'
+create_bogus_ref () {
+ test-tool ref-store main update-ref msg "refs/heads/bogus..name" $bogus $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+ test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/bogus..name"
+}
+
test_expect_success 'create history reachable only from a bogus-named ref' '
test_tick && git commit --allow-empty -m main &&
base=$(git rev-parse HEAD) &&
test_tick && git commit --allow-empty -m bogus &&
bogus=$(git rev-parse HEAD) &&
git cat-file commit $bogus >saved &&
- echo $bogus >.git/refs/heads/bogus..name &&
git reset --hard HEAD^
'
test_expect_success 'pruning does not drop bogus object' '
test_when_finished "git hash-object -w -t commit saved" &&
- test_might_fail git prune --expire=now &&
- verbose git cat-file -e $bogus
+ create_bogus_ref &&
+ test_must_fail git prune --expire=now &&
+ git cat-file -e $bogus
'
test_expect_success 'put bogus object into pack' '
git tag reachable $bogus &&
git repack -ad &&
git tag -d reachable &&
- verbose git cat-file -e $bogus
+ git cat-file -e $bogus
+'
+
+test_expect_success 'non-destructive repack bails on bogus ref' '
+ create_bogus_ref &&
+ test_must_fail git repack -adk
+'
+
+test_expect_success 'GIT_REF_PARANOIA=0 overrides safety' '
+ create_bogus_ref &&
+ GIT_REF_PARANOIA=0 git repack -adk
'
+
test_expect_success 'destructive repack keeps packed object' '
- test_might_fail git repack -Ad --unpack-unreachable=now &&
- verbose git cat-file -e $bogus &&
- test_might_fail git repack -ad &&
- verbose git cat-file -e $bogus
+ create_bogus_ref &&
+ test_must_fail git repack -Ad --unpack-unreachable=now &&
+ git cat-file -e $bogus &&
+ test_must_fail git repack -ad &&
+ git cat-file -e $bogus
'
-# subsequent tests will have different corruptions
-test_expect_success 'clean up bogus ref' '
- rm .git/refs/heads/bogus..name
+test_expect_success 'destructive repack not confused by dangling symref' '
+ test_when_finished "git symbolic-ref -d refs/heads/dangling" &&
+ git symbolic-ref refs/heads/dangling refs/heads/does-not-exist &&
+ git repack -ad &&
+ test_must_fail git cat-file -e $bogus
'
# We create two new objects here, "one" and "two". Our
@@ -77,8 +100,8 @@ test_expect_success 'create history with missing tip commit' '
test_expect_success 'pruning with a corrupted tip does not drop history' '
test_when_finished "git hash-object -w -t commit saved" &&
- test_might_fail git prune --expire=now &&
- verbose git cat-file -e $recoverable
+ test_must_fail git prune --expire=now &&
+ git cat-file -e $recoverable
'
test_expect_success 'pack-refs does not silently delete broken loose ref' '
@@ -88,30 +111,4 @@ test_expect_success 'pack-refs does not silently delete broken loose ref' '
test_cmp expect actual
'
-# we do not want to count on running pack-refs to
-# actually pack it, as it is perfectly reasonable to
-# skip processing a broken ref
-test_expect_success 'create packed-refs file with broken ref' '
- rm -f .git/refs/heads/main &&
- cat >.git/packed-refs <<-EOF &&
- $missing refs/heads/main
- $recoverable refs/heads/other
- EOF
- echo $missing >expect &&
- git rev-parse refs/heads/main >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'pack-refs does not silently delete broken packed ref' '
- git pack-refs --all --prune &&
- git rev-parse refs/heads/main >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'pack-refs does not drop broken refs during deletion' '
- git update-ref -d refs/heads/other &&
- git rev-parse refs/heads/main >actual &&
- test_cmp expect actual
-'
-
test_done
diff --git a/t/t5313-pack-bounds-checks.sh b/t/t5313-pack-bounds-checks.sh
index 535313e..ceaa670 100755
--- a/t/t5313-pack-bounds-checks.sh
+++ b/t/t5313-pack-bounds-checks.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='bounds-checking of access to mmapped on-disk file formats'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
clear_base () {
@@ -57,7 +59,7 @@ test_expect_success 'setup' '
test_expect_success 'set up base packfile and variables' '
# the hash of this content starts with ff, which
# makes some later computations much simpler
- echo $(test_oid oidfff) >file &&
+ test_oid oidfff >file &&
git add file &&
git commit -m base &&
git repack -ad &&
diff --git a/t/t5314-pack-cycle-detection.sh b/t/t5314-pack-cycle-detection.sh
index 0aec861..82734b9 100755
--- a/t/t5314-pack-cycle-detection.sh
+++ b/t/t5314-pack-cycle-detection.sh
@@ -49,9 +49,9 @@ Then no matter which order we start looking at the packs in, we know that we
will always find a delta for "file", because its lookup will always come
immediately after the lookup for "dummy".
'
-. ./test-lib.sh
-
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
# Create a pack containing the tree $1 and blob $1:file, with
# the latter stored as a delta against $2:file.
@@ -63,13 +63,16 @@ immediately after the lookup for "dummy".
# Note that the two variants of "file" must be similar enough to convince git
# to create the delta.
make_pack () {
- {
- printf '%s\n' "-$(git rev-parse $2)"
- printf '%s dummy\n' "$(git rev-parse $1:dummy)"
- printf '%s file\n' "$(git rev-parse $1:file)"
- } |
- git pack-objects --stdout |
- git index-pack --stdin --fix-thin
+ ln1=$(git rev-parse "$2") &&
+ ln2=$(git rev-parse "$1:dummy") &&
+ ln3=$(git rev-parse "$1:file") &&
+ cat >list <<-EOF
+ -$ln1
+ $ln2 dummy
+ $ln3 file
+ EOF
+ git pack-objects --stdout <list >pack &&
+ git index-pack --stdin --fix-thin <pack
}
test_expect_success 'setup' '
diff --git a/t/t5315-pack-objects-compression.sh b/t/t5315-pack-objects-compression.sh
index 8bacd96..c80ea9e 100755
--- a/t/t5315-pack-objects-compression.sh
+++ b/t/t5315-pack-objects-compression.sh
@@ -2,6 +2,7 @@
test_description='pack-object compression configuration'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh
index 759169d..eb4ef3d 100755
--- a/t/t5316-pack-delta-depth.sh
+++ b/t/t5316-pack-delta-depth.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='pack-objects breaks long cross-pack delta chains'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# This mirrors a repeated push setup:
@@ -57,11 +59,18 @@ test_expect_success 'create series of packs' '
git commit -m $i &&
cur=$(git rev-parse HEAD^{tree}) &&
{
- test -n "$prev" && echo "-$prev"
- echo $cur
+ if test -n "$prev"
+ then
+ echo "-$prev"
+ fi &&
+ echo $cur &&
echo "$(git rev-parse :file) file"
} | git pack-objects --stdout >tmp &&
- git index-pack --stdin --fix-thin <tmp || return 1
+ GIT_TRACE2_EVENT=$PWD/trace \
+ git index-pack -v --stdin --fix-thin <tmp || return 1 &&
+ grep -c region_enter.*progress trace >enter &&
+ grep -c region_leave.*progress trace >leave &&
+ test_cmp enter leave &&
prev=$cur
done
'
diff --git a/t/t5317-pack-objects-filter-objects.sh b/t/t5317-pack-objects-filter-objects.sh
index 13ed3eb..79552d6 100755
--- a/t/t5317-pack-objects-filter-objects.sh
+++ b/t/t5317-pack-objects-filter-objects.sh
@@ -5,27 +5,29 @@ test_description='git pack-objects using object filtering'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Test blob:none filter.
test_expect_success 'setup r1' '
- echo "{print \$1}" >print_1.awk &&
- echo "{print \$2}" >print_2.awk &&
-
git init r1 &&
for n in 1 2 3 4 5
do
- echo "This is file: $n" > r1/file.$n
- git -C r1 add file.$n
- git -C r1 commit -m "$n"
+ echo "This is file: $n" > r1/file.$n &&
+ git -C r1 add file.$n &&
+ git -C r1 commit -m "$n" || return 1
done
'
+parse_verify_pack_blob_oid () {
+ awk '{print $1}' -
+}
+
test_expect_success 'verify blob count in normal packfile' '
git -C r1 ls-files -s file.1 file.2 file.3 file.4 file.5 \
>ls_files_result &&
- awk -f print_2.awk ls_files_result |
+ test_parse_ls_files_stage_oids <ls_files_result |
sort >expected &&
git -C r1 pack-objects --revs --stdout >all.pack <<-EOF &&
@@ -35,7 +37,7 @@ test_expect_success 'verify blob count in normal packfile' '
git -C r1 verify-pack -v ../all.pack >verify_result &&
grep blob verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >observed &&
test_cmp expected observed
@@ -51,15 +53,23 @@ test_expect_success 'verify blob:none packfile has no blobs' '
! grep blob verify_result
'
+test_expect_success 'verify blob:none packfile without --stdout' '
+ git -C r1 pack-objects --revs --filter=blob:none mypackname >packhash <<-EOF &&
+ HEAD
+ EOF
+ git -C r1 verify-pack -v "mypackname-$(cat packhash).pack" >verify_result &&
+ ! grep blob verify_result
+'
+
test_expect_success 'verify normal and blob:none packfiles have same commits/trees' '
git -C r1 verify-pack -v ../all.pack >verify_result &&
grep -E "commit|tree" verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >expected &&
git -C r1 verify-pack -v ../filter.pack >verify_result &&
grep -E "commit|tree" verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >observed &&
test_cmp expected observed
@@ -116,15 +126,15 @@ test_expect_success 'setup r2' '
git init r2 &&
for n in 1000 10000
do
- printf "%"$n"s" X > r2/large.$n
- git -C r2 add large.$n
- git -C r2 commit -m "$n"
+ printf "%"$n"s" X > r2/large.$n &&
+ git -C r2 add large.$n &&
+ git -C r2 commit -m "$n" || return 1
done
'
test_expect_success 'verify blob count in normal packfile' '
git -C r2 ls-files -s large.1000 large.10000 >ls_files_result &&
- awk -f print_2.awk ls_files_result |
+ test_parse_ls_files_stage_oids <ls_files_result |
sort >expected &&
git -C r2 pack-objects --revs --stdout >all.pack <<-EOF &&
@@ -134,7 +144,7 @@ test_expect_success 'verify blob count in normal packfile' '
git -C r2 verify-pack -v ../all.pack >verify_result &&
grep blob verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >observed &&
test_cmp expected observed
@@ -162,7 +172,7 @@ test_expect_success 'verify blob:limit=1000' '
test_expect_success 'verify blob:limit=1001' '
git -C r2 ls-files -s large.1000 >ls_files_result &&
- awk -f print_2.awk ls_files_result |
+ test_parse_ls_files_stage_oids <ls_files_result |
sort >expected &&
git -C r2 pack-objects --revs --stdout --filter=blob:limit=1001 >filter.pack <<-EOF &&
@@ -172,7 +182,7 @@ test_expect_success 'verify blob:limit=1001' '
git -C r2 verify-pack -v ../filter.pack >verify_result &&
grep blob verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >observed &&
test_cmp expected observed
@@ -180,7 +190,7 @@ test_expect_success 'verify blob:limit=1001' '
test_expect_success 'verify blob:limit=10001' '
git -C r2 ls-files -s large.1000 large.10000 >ls_files_result &&
- awk -f print_2.awk ls_files_result |
+ test_parse_ls_files_stage_oids <ls_files_result |
sort >expected &&
git -C r2 pack-objects --revs --stdout --filter=blob:limit=10001 >filter.pack <<-EOF &&
@@ -190,7 +200,7 @@ test_expect_success 'verify blob:limit=10001' '
git -C r2 verify-pack -v ../filter.pack >verify_result &&
grep blob verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >observed &&
test_cmp expected observed
@@ -198,7 +208,7 @@ test_expect_success 'verify blob:limit=10001' '
test_expect_success 'verify blob:limit=1k' '
git -C r2 ls-files -s large.1000 >ls_files_result &&
- awk -f print_2.awk ls_files_result |
+ test_parse_ls_files_stage_oids <ls_files_result |
sort >expected &&
git -C r2 pack-objects --revs --stdout --filter=blob:limit=1k >filter.pack <<-EOF &&
@@ -208,7 +218,7 @@ test_expect_success 'verify blob:limit=1k' '
git -C r2 verify-pack -v ../filter.pack >verify_result &&
grep blob verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >observed &&
test_cmp expected observed
@@ -216,7 +226,7 @@ test_expect_success 'verify blob:limit=1k' '
test_expect_success 'verify explicitly specifying oversized blob in input' '
git -C r2 ls-files -s large.1000 large.10000 >ls_files_result &&
- awk -f print_2.awk ls_files_result |
+ test_parse_ls_files_stage_oids <ls_files_result |
sort >expected &&
echo HEAD >objects &&
@@ -226,7 +236,7 @@ test_expect_success 'verify explicitly specifying oversized blob in input' '
git -C r2 verify-pack -v ../filter.pack >verify_result &&
grep blob verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >observed &&
test_cmp expected observed
@@ -234,7 +244,7 @@ test_expect_success 'verify explicitly specifying oversized blob in input' '
test_expect_success 'verify blob:limit=1m' '
git -C r2 ls-files -s large.1000 large.10000 >ls_files_result &&
- awk -f print_2.awk ls_files_result |
+ test_parse_ls_files_stage_oids <ls_files_result |
sort >expected &&
git -C r2 pack-objects --revs --stdout --filter=blob:limit=1m >filter.pack <<-EOF &&
@@ -244,7 +254,7 @@ test_expect_success 'verify blob:limit=1m' '
git -C r2 verify-pack -v ../filter.pack >verify_result &&
grep blob verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >observed &&
test_cmp expected observed
@@ -253,12 +263,50 @@ test_expect_success 'verify blob:limit=1m' '
test_expect_success 'verify normal and blob:limit packfiles have same commits/trees' '
git -C r2 verify-pack -v ../all.pack >verify_result &&
grep -E "commit|tree" verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >expected &&
git -C r2 verify-pack -v ../filter.pack >verify_result &&
grep -E "commit|tree" verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
+ sort >observed &&
+
+ test_cmp expected observed
+'
+
+test_expect_success 'verify small limit and big limit results in small limit' '
+ git -C r2 ls-files -s large.1000 >ls_files_result &&
+ test_parse_ls_files_stage_oids <ls_files_result |
+ sort >expected &&
+
+ git -C r2 pack-objects --revs --stdout --filter=blob:limit=1001 \
+ --filter=blob:limit=10001 >filter.pack <<-EOF &&
+ HEAD
+ EOF
+ git -C r2 index-pack ../filter.pack &&
+
+ git -C r2 verify-pack -v ../filter.pack >verify_result &&
+ grep blob verify_result |
+ parse_verify_pack_blob_oid |
+ sort >observed &&
+
+ test_cmp expected observed
+'
+
+test_expect_success 'verify big limit and small limit results in small limit' '
+ git -C r2 ls-files -s large.1000 >ls_files_result &&
+ test_parse_ls_files_stage_oids <ls_files_result |
+ sort >expected &&
+
+ git -C r2 pack-objects --revs --stdout --filter=blob:limit=10001 \
+ --filter=blob:limit=1001 >filter.pack <<-EOF &&
+ HEAD
+ EOF
+ git -C r2 index-pack ../filter.pack &&
+
+ git -C r2 verify-pack -v ../filter.pack >verify_result &&
+ grep blob verify_result |
+ parse_verify_pack_blob_oid |
sort >observed &&
test_cmp expected observed
@@ -278,10 +326,10 @@ test_expect_success 'setup r3' '
mkdir r3/dir1 &&
for n in sparse1 sparse2
do
- echo "This is file: $n" > r3/$n
- git -C r3 add $n
- echo "This is file: dir1/$n" > r3/dir1/$n
- git -C r3 add dir1/$n
+ echo "This is file: $n" > r3/$n &&
+ git -C r3 add $n &&
+ echo "This is file: dir1/$n" > r3/dir1/$n &&
+ git -C r3 add dir1/$n || return 1
done &&
git -C r3 commit -m "sparse" &&
echo dir1/ >pattern1 &&
@@ -291,7 +339,7 @@ test_expect_success 'setup r3' '
test_expect_success 'verify blob count in normal packfile' '
git -C r3 ls-files -s sparse1 sparse2 dir1/sparse1 dir1/sparse2 \
>ls_files_result &&
- awk -f print_2.awk ls_files_result |
+ test_parse_ls_files_stage_oids <ls_files_result |
sort >expected &&
git -C r3 pack-objects --revs --stdout >all.pack <<-EOF &&
@@ -301,7 +349,7 @@ test_expect_success 'verify blob count in normal packfile' '
git -C r3 verify-pack -v ../all.pack >verify_result &&
grep blob verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >observed &&
test_cmp expected observed
@@ -331,10 +379,10 @@ test_expect_success 'setup r4' '
mkdir r4/dir1 &&
for n in sparse1 sparse2
do
- echo "This is file: $n" > r4/$n
- git -C r4 add $n
- echo "This is file: dir1/$n" > r4/dir1/$n
- git -C r4 add dir1/$n
+ echo "This is file: $n" > r4/$n &&
+ git -C r4 add $n &&
+ echo "This is file: dir1/$n" > r4/dir1/$n &&
+ git -C r4 add dir1/$n || return 1
done &&
echo dir1/ >r4/pattern &&
git -C r4 add pattern &&
@@ -344,7 +392,7 @@ test_expect_success 'setup r4' '
test_expect_success 'verify blob count in normal packfile' '
git -C r4 ls-files -s pattern sparse1 sparse2 dir1/sparse1 dir1/sparse2 \
>ls_files_result &&
- awk -f print_2.awk ls_files_result |
+ test_parse_ls_files_stage_oids <ls_files_result |
sort >expected &&
git -C r4 pack-objects --revs --stdout >all.pack <<-EOF &&
@@ -354,7 +402,7 @@ test_expect_success 'verify blob count in normal packfile' '
git -C r4 verify-pack -v ../all.pack >verify_result &&
grep blob verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >observed &&
test_cmp expected observed
@@ -362,11 +410,11 @@ test_expect_success 'verify blob count in normal packfile' '
test_expect_success 'verify sparse:oid=OID' '
git -C r4 ls-files -s dir1/sparse1 dir1/sparse2 >ls_files_result &&
- awk -f print_2.awk ls_files_result |
+ test_parse_ls_files_stage_oids <ls_files_result |
sort >expected &&
git -C r4 ls-files -s pattern >staged &&
- oid=$(awk -f print_2.awk staged) &&
+ oid=$(test_parse_ls_files_stage_oids <staged) &&
git -C r4 pack-objects --revs --stdout --filter=sparse:oid=$oid >filter.pack <<-EOF &&
HEAD
EOF
@@ -374,7 +422,7 @@ test_expect_success 'verify sparse:oid=OID' '
git -C r4 verify-pack -v ../filter.pack >verify_result &&
grep blob verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >observed &&
test_cmp expected observed
@@ -382,7 +430,7 @@ test_expect_success 'verify sparse:oid=OID' '
test_expect_success 'verify sparse:oid=oid-ish' '
git -C r4 ls-files -s dir1/sparse1 dir1/sparse2 >ls_files_result &&
- awk -f print_2.awk ls_files_result |
+ test_parse_ls_files_stage_oids <ls_files_result |
sort >expected &&
git -C r4 pack-objects --revs --stdout --filter=sparse:oid=main:pattern >filter.pack <<-EOF &&
@@ -392,7 +440,7 @@ test_expect_success 'verify sparse:oid=oid-ish' '
git -C r4 verify-pack -v ../filter.pack >verify_result &&
grep blob verify_result |
- awk -f print_1.awk |
+ parse_verify_pack_blob_oid |
sort >observed &&
test_cmp expected observed
@@ -404,12 +452,12 @@ test_expect_success 'verify sparse:oid=oid-ish' '
test_expect_success 'setup r1 - delete loose blobs' '
git -C r1 ls-files -s file.1 file.2 file.3 file.4 file.5 \
>ls_files_result &&
- awk -f print_2.awk ls_files_result |
+ test_parse_ls_files_stage_oids <ls_files_result |
sort >expected &&
- for id in `cat expected | sed "s|..|&/|"`
+ for id in `sed "s|..|&/|" expected`
do
- rm r1/.git/objects/$id
+ rm r1/.git/objects/$id || return 1
done
'
diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh
index 295c5bd..a2b4442 100755
--- a/t/t5318-commit-graph.sh
+++ b/t/t5318-commit-graph.sh
@@ -2,6 +2,7 @@
test_description='commit graph'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-chunk.sh
GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=0
@@ -12,29 +13,22 @@ test_expect_success 'usage' '
test_expect_success 'usage shown without sub-command' '
test_expect_code 129 git commit-graph 2>err &&
- ! grep error: err
+ grep usage: err
'
test_expect_success 'usage shown with an error on unknown sub-command' '
cat >expect <<-\EOF &&
- error: unrecognized subcommand: unknown
+ error: unknown subcommand: `unknown'\''
EOF
test_expect_code 129 git commit-graph unknown 2>stderr &&
grep error stderr >actual &&
test_cmp expect actual
'
+objdir=".git/objects"
+
test_expect_success 'setup full repo' '
- mkdir full &&
- cd "$TRASH_DIRECTORY/full" &&
- git init &&
- git config core.commitGraph true &&
- objdir=".git/objects" &&
-
- test_oid_cache <<-EOF
- oid_version sha1:1
- oid_version sha256:2
- EOF
+ git init full
'
test_expect_success POSIXPERM 'tweak umask for modebit tests' '
@@ -42,185 +36,135 @@ test_expect_success POSIXPERM 'tweak umask for modebit tests' '
'
test_expect_success 'verify graph with no graph file' '
- cd "$TRASH_DIRECTORY/full" &&
- git commit-graph verify
+ git -C full commit-graph verify
'
test_expect_success 'write graph with no packs' '
- cd "$TRASH_DIRECTORY/full" &&
- git commit-graph write --object-dir $objdir &&
- test_path_is_missing $objdir/info/commit-graph
+ git -C full commit-graph write --object-dir $objdir &&
+ test_path_is_missing full/$objdir/info/commit-graph
'
test_expect_success 'exit with correct error on bad input to --stdin-packs' '
- cd "$TRASH_DIRECTORY/full" &&
echo doesnotexist >in &&
- test_expect_code 1 git commit-graph write --stdin-packs <in 2>stderr &&
- test_i18ngrep "error adding pack" stderr
+ test_expect_code 1 git -C full commit-graph write --stdin-packs \
+ <in 2>stderr &&
+ test_grep "error adding pack" stderr
'
test_expect_success 'create commits and repack' '
- cd "$TRASH_DIRECTORY/full" &&
for i in $(test_seq 3)
do
- test_commit $i &&
- git branch commits/$i
+ test_commit -C full $i &&
+ git -C full branch commits/$i || return 1
done &&
- git repack
+ git -C full repack
'
-graph_git_two_modes() {
- git -c core.commitGraph=true $1 >output
- git -c core.commitGraph=false $1 >expect
- test_cmp expect output
-}
-
-graph_git_behavior() {
- MSG=$1
- DIR=$2
- BRANCH=$3
- COMPARE=$4
- test_expect_success "check normal git operations: $MSG" '
- cd "$TRASH_DIRECTORY/$DIR" &&
- graph_git_two_modes "log --oneline $BRANCH" &&
- graph_git_two_modes "log --topo-order $BRANCH" &&
- graph_git_two_modes "log --graph $COMPARE..$BRANCH" &&
- graph_git_two_modes "branch -vv" &&
- graph_git_two_modes "merge-base -a $BRANCH $COMPARE"
- '
-}
+. "$TEST_DIRECTORY"/lib-commit-graph.sh
graph_git_behavior 'no graph' full commits/3 commits/1
-graph_read_expect() {
- OPTIONAL=""
- NUM_CHUNKS=3
- if test ! -z "$2"
- then
- OPTIONAL=" $2"
- NUM_CHUNKS=$((3 + $(echo "$2" | wc -w)))
- fi
- cat >expect <<- EOF
- header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0
- num_commits: $1
- chunks: oid_fanout oid_lookup commit_metadata$OPTIONAL
- EOF
- test-tool read-graph >output &&
- test_cmp expect output
-}
-
test_expect_success 'exit with correct error on bad input to --stdin-commits' '
- cd "$TRASH_DIRECTORY/full" &&
# invalid, non-hex OID
- echo HEAD >in &&
- test_expect_code 1 git commit-graph write --stdin-commits <in 2>stderr &&
- test_i18ngrep "unexpected non-hex object ID: HEAD" stderr &&
+ echo HEAD | test_expect_code 1 git -C full commit-graph write \
+ --stdin-commits 2>stderr &&
+ test_grep "unexpected non-hex object ID: HEAD" stderr &&
# non-existent OID
- echo $ZERO_OID >in &&
- test_expect_code 1 git commit-graph write --stdin-commits <in 2>stderr &&
- test_i18ngrep "invalid object" stderr &&
+ echo $ZERO_OID | test_expect_code 1 git -C full commit-graph write \
+ --stdin-commits 2>stderr &&
+ test_grep "invalid object" stderr &&
# valid commit and tree OID
- git rev-parse HEAD HEAD^{tree} >in &&
- git commit-graph write --stdin-commits <in &&
- graph_read_expect 3 generation_data
+ git -C full rev-parse HEAD HEAD^{tree} >in &&
+ git -C full commit-graph write --stdin-commits <in &&
+ graph_read_expect -C full 3 generation_data
'
test_expect_success 'write graph' '
- cd "$TRASH_DIRECTORY/full" &&
- git commit-graph write &&
- test_path_is_file $objdir/info/commit-graph &&
- graph_read_expect "3" generation_data
+ git -C full commit-graph write &&
+ test_path_is_file full/$objdir/info/commit-graph &&
+ graph_read_expect -C full 3 generation_data
'
test_expect_success POSIXPERM 'write graph has correct permissions' '
- test_path_is_file $objdir/info/commit-graph &&
+ test_path_is_file full/$objdir/info/commit-graph &&
echo "-r--r--r--" >expect &&
- test_modebits $objdir/info/commit-graph >actual &&
+ test_modebits full/$objdir/info/commit-graph >actual &&
test_cmp expect actual
'
graph_git_behavior 'graph exists' full commits/3 commits/1
test_expect_success 'Add more commits' '
- cd "$TRASH_DIRECTORY/full" &&
- git reset --hard commits/1 &&
+ git -C full reset --hard commits/1 &&
for i in $(test_seq 4 5)
do
- test_commit $i &&
- git branch commits/$i
+ test_commit -C full $i &&
+ git -C full branch commits/$i || return 1
done &&
- git reset --hard commits/2 &&
+ git -C full reset --hard commits/2 &&
for i in $(test_seq 6 7)
do
- test_commit $i &&
- git branch commits/$i
+ test_commit -C full $i &&
+ git -C full branch commits/$i || return 1
done &&
- git reset --hard commits/2 &&
- git merge commits/4 &&
- git branch merge/1 &&
- git reset --hard commits/4 &&
- git merge commits/6 &&
- git branch merge/2 &&
- git reset --hard commits/3 &&
- git merge commits/5 commits/7 &&
- git branch merge/3 &&
- git repack
+ git -C full reset --hard commits/2 &&
+ git -C full merge commits/4 &&
+ git -C full branch merge/1 &&
+ git -C full reset --hard commits/4 &&
+ git -C full merge commits/6 &&
+ git -C full branch merge/2 &&
+ git -C full reset --hard commits/3 &&
+ git -C full merge commits/5 commits/7 &&
+ git -C full branch merge/3 &&
+ git -C full repack
'
test_expect_success 'commit-graph write progress off for redirected stderr' '
- cd "$TRASH_DIRECTORY/full" &&
- git commit-graph write 2>err &&
+ git -C full commit-graph write 2>err &&
test_must_be_empty err
'
test_expect_success 'commit-graph write force progress on for stderr' '
- cd "$TRASH_DIRECTORY/full" &&
- GIT_PROGRESS_DELAY=0 git commit-graph write --progress 2>err &&
+ GIT_PROGRESS_DELAY=0 git -C full commit-graph write --progress 2>err &&
test_file_not_empty err
'
test_expect_success 'commit-graph write with the --no-progress option' '
- cd "$TRASH_DIRECTORY/full" &&
- git commit-graph write --no-progress 2>err &&
+ git -C full commit-graph write --no-progress 2>err &&
test_must_be_empty err
'
test_expect_success 'commit-graph write --stdin-commits progress off for redirected stderr' '
- cd "$TRASH_DIRECTORY/full" &&
- git rev-parse commits/5 >in &&
- git commit-graph write --stdin-commits <in 2>err &&
+ git -C full rev-parse commits/5 >in &&
+ git -C full commit-graph write --stdin-commits <in 2>err &&
test_must_be_empty err
'
test_expect_success 'commit-graph write --stdin-commits force progress on for stderr' '
- cd "$TRASH_DIRECTORY/full" &&
- git rev-parse commits/5 >in &&
- GIT_PROGRESS_DELAY=0 git commit-graph write --stdin-commits --progress <in 2>err &&
- test_i18ngrep "Collecting commits from input" err
+ git -C full rev-parse commits/5 >in &&
+ GIT_PROGRESS_DELAY=0 git -C full commit-graph write --stdin-commits \
+ --progress <in 2>err &&
+ test_grep "Collecting commits from input" err
'
test_expect_success 'commit-graph write --stdin-commits with the --no-progress option' '
- cd "$TRASH_DIRECTORY/full" &&
- git rev-parse commits/5 >in &&
- git commit-graph write --stdin-commits --no-progress <in 2>err &&
+ git -C full rev-parse commits/5 >in &&
+ git -C full commit-graph write --stdin-commits --no-progress <in 2>err &&
test_must_be_empty err
'
test_expect_success 'commit-graph verify progress off for redirected stderr' '
- cd "$TRASH_DIRECTORY/full" &&
- git commit-graph verify 2>err &&
+ git -C full commit-graph verify 2>err &&
test_must_be_empty err
'
test_expect_success 'commit-graph verify force progress on for stderr' '
- cd "$TRASH_DIRECTORY/full" &&
- GIT_PROGRESS_DELAY=0 git commit-graph verify --progress 2>err &&
+ GIT_PROGRESS_DELAY=0 git -C full commit-graph verify --progress 2>err &&
test_file_not_empty err
'
test_expect_success 'commit-graph verify with the --no-progress option' '
- cd "$TRASH_DIRECTORY/full" &&
- git commit-graph verify --no-progress 2>err &&
+ git -C full commit-graph verify --no-progress 2>err &&
test_must_be_empty err
'
@@ -235,10 +179,9 @@ test_expect_success 'commit-graph verify with the --no-progress option' '
# 1
test_expect_success 'write graph with merges' '
- cd "$TRASH_DIRECTORY/full" &&
- git commit-graph write &&
- test_path_is_file $objdir/info/commit-graph &&
- graph_read_expect "10" "generation_data extra_edges"
+ git -C full commit-graph write &&
+ test_path_is_file full/$objdir/info/commit-graph &&
+ graph_read_expect -C full 10 "generation_data extra_edges"
'
graph_git_behavior 'merge 1 vs 2' full merge/1 merge/2
@@ -246,12 +189,11 @@ graph_git_behavior 'merge 1 vs 3' full merge/1 merge/3
graph_git_behavior 'merge 2 vs 3' full merge/2 merge/3
test_expect_success 'Add one more commit' '
- cd "$TRASH_DIRECTORY/full" &&
- test_commit 8 &&
- git branch commits/8 &&
- ls $objdir/pack | grep idx >existing-idx &&
- git repack &&
- ls $objdir/pack| grep idx | grep -v -f existing-idx >new-idx
+ test_commit -C full 8 &&
+ git -C full branch commits/8 &&
+ ls full/$objdir/pack | grep idx >existing-idx &&
+ git -C full repack &&
+ ls full/$objdir/pack| grep idx | grep -v -f existing-idx >new-idx
'
# Current graph structure:
@@ -270,114 +212,101 @@ graph_git_behavior 'mixed mode, commit 8 vs merge 1' full commits/8 merge/1
graph_git_behavior 'mixed mode, commit 8 vs merge 2' full commits/8 merge/2
test_expect_success 'write graph with new commit' '
- cd "$TRASH_DIRECTORY/full" &&
- git commit-graph write &&
- test_path_is_file $objdir/info/commit-graph &&
- graph_read_expect "11" "generation_data extra_edges"
+ git -C full commit-graph write &&
+ test_path_is_file full/$objdir/info/commit-graph &&
+ graph_read_expect -C full 11 "generation_data extra_edges"
'
graph_git_behavior 'full graph, commit 8 vs merge 1' full commits/8 merge/1
graph_git_behavior 'full graph, commit 8 vs merge 2' full commits/8 merge/2
test_expect_success 'write graph with nothing new' '
- cd "$TRASH_DIRECTORY/full" &&
- git commit-graph write &&
- test_path_is_file $objdir/info/commit-graph &&
- graph_read_expect "11" "generation_data extra_edges"
+ git -C full commit-graph write &&
+ test_path_is_file full/$objdir/info/commit-graph &&
+ graph_read_expect -C full 11 "generation_data extra_edges"
'
graph_git_behavior 'cleared graph, commit 8 vs merge 1' full commits/8 merge/1
graph_git_behavior 'cleared graph, commit 8 vs merge 2' full commits/8 merge/2
test_expect_success 'build graph from latest pack with closure' '
- cd "$TRASH_DIRECTORY/full" &&
- cat new-idx | git commit-graph write --stdin-packs &&
- test_path_is_file $objdir/info/commit-graph &&
- graph_read_expect "9" "generation_data extra_edges"
+ git -C full commit-graph write --stdin-packs <new-idx &&
+ test_path_is_file full/$objdir/info/commit-graph &&
+ graph_read_expect -C full 9 "generation_data extra_edges"
'
graph_git_behavior 'graph from pack, commit 8 vs merge 1' full commits/8 merge/1
graph_git_behavior 'graph from pack, commit 8 vs merge 2' full commits/8 merge/2
test_expect_success 'build graph from commits with closure' '
- cd "$TRASH_DIRECTORY/full" &&
- git tag -a -m "merge" tag/merge merge/2 &&
- git rev-parse tag/merge >commits-in &&
- git rev-parse merge/1 >>commits-in &&
- cat commits-in | git commit-graph write --stdin-commits &&
- test_path_is_file $objdir/info/commit-graph &&
- graph_read_expect "6" "generation_data"
+ git -C full tag -a -m "merge" tag/merge merge/2 &&
+ git -C full rev-parse tag/merge >commits-in &&
+ git -C full rev-parse merge/1 >>commits-in &&
+ git -C full commit-graph write --stdin-commits <commits-in &&
+ test_path_is_file full/$objdir/info/commit-graph &&
+ graph_read_expect -C full 6 "generation_data"
'
graph_git_behavior 'graph from commits, commit 8 vs merge 1' full commits/8 merge/1
graph_git_behavior 'graph from commits, commit 8 vs merge 2' full commits/8 merge/2
test_expect_success 'build graph from commits with append' '
- cd "$TRASH_DIRECTORY/full" &&
- git rev-parse merge/3 | git commit-graph write --stdin-commits --append &&
- test_path_is_file $objdir/info/commit-graph &&
- graph_read_expect "10" "generation_data extra_edges"
+ git -C full rev-parse merge/3 >in &&
+ git -C full commit-graph write --stdin-commits --append <in &&
+ test_path_is_file full/$objdir/info/commit-graph &&
+ graph_read_expect -C full 10 "generation_data extra_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 '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" "generation_data extra_edges"
+ git -C full commit-graph write --reachable &&
+ test_path_is_file full/$objdir/info/commit-graph &&
+ graph_read_expect -C full 11 "generation_data extra_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 &&
- cd bare &&
- git config core.commitGraph true &&
- baredir="./objects"
+ git clone --bare --no-local full bare
'
graph_git_behavior 'bare repo, commit 8 vs merge 1' bare commits/8 merge/1
graph_git_behavior 'bare repo, commit 8 vs merge 2' bare commits/8 merge/2
test_expect_success 'write graph in bare repo' '
- cd "$TRASH_DIRECTORY/bare" &&
- git commit-graph write &&
- test_path_is_file $baredir/info/commit-graph &&
- graph_read_expect "11" "generation_data extra_edges"
+ git -C bare commit-graph write &&
+ test_path_is_file bare/objects/info/commit-graph &&
+ graph_read_expect -C bare 11 "generation_data extra_edges"
'
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 &&
+ git -C full checkout -b merge-5-to-8 commits/5 &&
+ git -C full merge commits/8 &&
+ git -C full show-ref -s merge-5-to-8 >output &&
+ git -C full 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_commit -C full --no-tag blank &&
+ git -C full commit-graph write --reachable &&
+ cp full/$objdir/info/commit-graph commit-graph-before-gc &&
+ git -C full reset --hard HEAD~1 &&
+ test_config -C full gc.writeCommitGraph true &&
+ git -C full gc &&
+ cp full/$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
+ git -C full commit-graph write --reachable &&
+ test_cmp_bin commit-graph-after-gc full/$objdir/info/commit-graph
'
test_expect_success 'replace-objects invalidates commit-graph' '
- cd "$TRASH_DIRECTORY" &&
test_when_finished rm -rf replace &&
git clone full replace &&
(
@@ -385,6 +314,7 @@ test_expect_success 'replace-objects invalidates commit-graph' '
git commit-graph write --reachable &&
test_path_is_file .git/objects/info/commit-graph &&
git replace HEAD~1 HEAD~2 &&
+ graph_git_two_modes "commit-graph verify" &&
git -c core.commitGraph=false log >expect &&
git -c core.commitGraph=true log >actual &&
test_cmp expect actual &&
@@ -399,15 +329,15 @@ test_expect_success 'replace-objects invalidates commit-graph' '
'
test_expect_success 'commit grafts invalidate commit-graph' '
- cd "$TRASH_DIRECTORY" &&
test_when_finished rm -rf graft &&
- git clone full graft &&
+ git clone --template= full graft &&
(
cd graft &&
git commit-graph write --reachable &&
test_path_is_file .git/objects/info/commit-graph &&
H1=$(git rev-parse --verify HEAD~1) &&
H3=$(git rev-parse --verify HEAD~3) &&
+ mkdir .git/info &&
echo "$H1 $H3" >.git/info/grafts &&
git -c core.commitGraph=false log >expect &&
git -c core.commitGraph=true log >actual &&
@@ -423,7 +353,6 @@ test_expect_success 'commit grafts invalidate commit-graph' '
'
test_expect_success 'replace-objects invalidates commit-graph' '
- cd "$TRASH_DIRECTORY" &&
test_when_finished rm -rf shallow &&
git clone --depth 2 "file://$TRASH_DIRECTORY/full" shallow &&
(
@@ -455,35 +384,36 @@ test_expect_success 'warn on improper hash version' '
cd sha1 &&
mv ../cg-sha256 .git/objects/info/commit-graph &&
git log -1 2>err &&
- test_i18ngrep "commit-graph hash version 2 does not match version 1" err
+ test_grep "commit-graph hash version 2 does not match version 1" err
) &&
(
cd sha256 &&
mv ../cg-sha1 .git/objects/info/commit-graph &&
git log -1 2>err &&
- test_i18ngrep "commit-graph hash version 1 does not match version 2" err
+ test_grep "commit-graph hash version 1 does not match version 2" err
)
'
-test_expect_success 'lower layers have overflow chunk' '
- cd "$TRASH_DIRECTORY/full" &&
+test_expect_success TIME_IS_64BIT,TIME_T_IS_64BIT 'lower layers have overflow chunk' '
UNIX_EPOCH_ZERO="@0 +0000" &&
- FUTURE_DATE="@2147483646 +0000" &&
- rm -f .git/objects/info/commit-graph &&
- test_commit --date "$FUTURE_DATE" future-1 &&
- test_commit --date "$UNIX_EPOCH_ZERO" old-1 &&
- git commit-graph write --reachable &&
- test_commit --date "$FUTURE_DATE" future-2 &&
- test_commit --date "$UNIX_EPOCH_ZERO" old-2 &&
- git commit-graph write --reachable --split=no-merge &&
- test_commit extra &&
- git commit-graph write --reachable --split=no-merge &&
- git commit-graph write --reachable &&
- graph_read_expect 16 "generation_data generation_data_overflow extra_edges" &&
- mv .git/objects/info/commit-graph commit-graph-upgraded &&
- git commit-graph write --reachable &&
- graph_read_expect 16 "generation_data generation_data_overflow extra_edges" &&
- test_cmp .git/objects/info/commit-graph commit-graph-upgraded
+ FUTURE_DATE="@4147483646 +0000" &&
+ rm -f full/.git/objects/info/commit-graph &&
+ test_commit -C full --date "$FUTURE_DATE" future-1 &&
+ test_commit -C full --date "$UNIX_EPOCH_ZERO" old-1 &&
+ git -C full commit-graph write --reachable &&
+ test_commit -C full --date "$FUTURE_DATE" future-2 &&
+ test_commit -C full --date "$UNIX_EPOCH_ZERO" old-2 &&
+ git -C full commit-graph write --reachable --split=no-merge &&
+ test_commit -C full extra &&
+ git -C full commit-graph write --reachable --split=no-merge &&
+ git -C full commit-graph write --reachable &&
+ graph_read_expect -C full 16 \
+ "generation_data generation_data_overflow extra_edges" &&
+ mv full/.git/objects/info/commit-graph commit-graph-upgraded &&
+ git -C full commit-graph write --reachable &&
+ graph_read_expect -C full 16 \
+ "generation_data generation_data_overflow extra_edges" &&
+ test_cmp full/.git/objects/info/commit-graph commit-graph-upgraded
'
# the verify tests below expect the commit-graph to contain
@@ -493,10 +423,11 @@ test_expect_success 'lower layers have overflow chunk' '
# and the tests will likely break.
test_expect_success 'git commit-graph verify' '
- cd "$TRASH_DIRECTORY/full" &&
- git rev-parse commits/8 | git -c commitGraph.generationVersion=1 commit-graph write --stdin-commits &&
- git commit-graph verify >output &&
- graph_read_expect 9 extra_edges
+ git -C full rev-parse commits/8 >in &&
+ git -C full -c commitGraph.generationVersion=1 commit-graph write \
+ --stdin-commits <in &&
+ git -C full commit-graph verify >output &&
+ graph_read_expect -C full 9 extra_edges 1
'
NUM_COMMITS=9
@@ -520,39 +451,39 @@ 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_WIDTH=$(($HASH_LEN + 16))
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_GENERATION_LAST=$(($GRAPH_BYTE_COMMIT_GENERATION + $(($NUM_COMMITS - 1)) * $GRAPH_COMMIT_DATA_WIDTH))
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))
corrupt_graph_setup() {
- cd "$TRASH_DIRECTORY/full" &&
- test_when_finished mv commit-graph-backup $objdir/info/commit-graph &&
- cp $objdir/info/commit-graph commit-graph-backup &&
- chmod u+w $objdir/info/commit-graph
+ test_when_finished mv commit-graph-backup full/$objdir/info/commit-graph &&
+ cp full/$objdir/info/commit-graph commit-graph-backup &&
+ chmod u+w full/$objdir/info/commit-graph
}
corrupt_graph_verify() {
grepstr=$1
- test_must_fail git commit-graph verify 2>test_err &&
+ test_must_fail git -C full commit-graph verify 2>test_err &&
grep -v "^+" test_err >err &&
- test_i18ngrep "$grepstr" err &&
+ test_grep "$grepstr" err &&
if test "$2" != "no-copy"
then
- cp $objdir/info/commit-graph commit-graph-pre-write-test
+ cp full/$objdir/info/commit-graph commit-graph-pre-write-test
fi &&
- git status --short &&
- GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE=true git commit-graph write &&
- chmod u+w $objdir/info/commit-graph &&
- git commit-graph verify
+ git -C full status --short &&
+ GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE=true git -C full commit-graph write &&
+ chmod u+w full/$objdir/info/commit-graph &&
+ git -C full commit-graph verify
}
# usage: corrupt_graph_and_verify <position> <data> <string> [<zero_pos>]
@@ -566,24 +497,24 @@ corrupt_graph_and_verify() {
data="${2:-\0}"
grepstr=$3
corrupt_graph_setup &&
- orig_size=$(wc -c < $objdir/info/commit-graph) &&
+ orig_size=$(wc -c <full/$objdir/info/commit-graph) &&
zero_pos=${4:-${orig_size}} &&
- printf "$data" | dd of="$objdir/info/commit-graph" bs=1 seek="$pos" conv=notrunc &&
- dd of="$objdir/info/commit-graph" bs=1 seek="$zero_pos" if=/dev/null &&
- test-tool genzeros $(($orig_size - $zero_pos)) >>"$objdir/info/commit-graph" &&
+ printf "$data" | dd of="full/$objdir/info/commit-graph" bs=1 seek="$pos" conv=notrunc &&
+ dd of="full/$objdir/info/commit-graph" bs=1 seek="$zero_pos" if=/dev/null &&
+ test-tool genzeros $(($orig_size - $zero_pos)) >>"full/$objdir/info/commit-graph" &&
corrupt_graph_verify "$grepstr"
}
test_expect_success POSIXPERM,SANITY 'detect permission problem' '
corrupt_graph_setup &&
- chmod 000 $objdir/info/commit-graph &&
+ chmod 000 full/$objdir/info/commit-graph &&
corrupt_graph_verify "Could not open" "no-copy"
'
test_expect_success 'detect too small' '
corrupt_graph_setup &&
- echo "a small graph" >$objdir/info/commit-graph &&
+ echo "a small graph" >full/$objdir/info/commit-graph &&
corrupt_graph_verify "too small"
'
@@ -609,17 +540,17 @@ test_expect_success 'detect low chunk count' '
test_expect_success 'detect missing OID fanout chunk' '
corrupt_graph_and_verify $GRAPH_BYTE_OID_FANOUT_ID "\0" \
- "missing the OID Fanout chunk"
+ "commit-graph required OID fanout chunk missing or corrupted"
'
test_expect_success 'detect missing OID lookup chunk' '
corrupt_graph_and_verify $GRAPH_BYTE_OID_LOOKUP_ID "\0" \
- "missing the OID Lookup chunk"
+ "commit-graph required OID lookup chunk missing or corrupted"
'
test_expect_success 'detect missing commit data chunk' '
corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_DATA_ID "\0" \
- "missing the Commit Data chunk"
+ "commit-graph required commit data chunk missing or corrupted"
'
test_expect_success 'detect incorrect fanout' '
@@ -629,7 +560,7 @@ test_expect_success 'detect incorrect fanout' '
test_expect_success 'detect incorrect fanout final value' '
corrupt_graph_and_verify $GRAPH_BYTE_FANOUT2 "\01" \
- "fanout value"
+ "OID lookup chunk is the wrong size"
'
test_expect_success 'detect incorrect OID order' '
@@ -667,11 +598,6 @@ test_expect_success 'detect incorrect generation number' '
"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"
@@ -693,13 +619,51 @@ test_expect_success 'detect incorrect chunk count' '
$GRAPH_CHUNK_LOOKUP_OFFSET
'
-test_expect_success 'git fsck (checks commit-graph)' '
- cd "$TRASH_DIRECTORY/full" &&
- git fsck &&
+test_expect_success 'detect mixed generation numbers (non-zero to zero)' '
+ corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_GENERATION_LAST "\0\0\0\0" \
+ "both zero and non-zero generations"
+'
+
+test_expect_success 'detect mixed generation numbers (zero to non-zero)' '
+ corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_GENERATION "\0\0\0\0" \
+ "both zero and non-zero generations"
+'
+
+test_expect_success 'git fsck (checks commit-graph when config set to true)' '
+ git -C full fsck &&
corrupt_graph_and_verify $GRAPH_BYTE_FOOTER "\00" \
"incorrect checksum" &&
- cp commit-graph-pre-write-test $objdir/info/commit-graph &&
- test_must_fail git fsck
+ cp commit-graph-pre-write-test full/$objdir/info/commit-graph &&
+ test_must_fail git -C full -c core.commitGraph=true fsck
+'
+
+test_expect_success 'git fsck (ignores commit-graph when config set to false)' '
+ git -C full fsck &&
+ corrupt_graph_and_verify $GRAPH_BYTE_FOOTER "\00" \
+ "incorrect checksum" &&
+ cp commit-graph-pre-write-test full/$objdir/info/commit-graph &&
+ git -C full -c core.commitGraph=false fsck
+'
+
+test_expect_success 'git fsck (checks commit-graph when config unset)' '
+ test_when_finished "git -C full config core.commitGraph true" &&
+
+ git -C full fsck &&
+ corrupt_graph_and_verify $GRAPH_BYTE_FOOTER "\00" \
+ "incorrect checksum" &&
+ test_unconfig -C full core.commitGraph &&
+ cp commit-graph-pre-write-test full/$objdir/info/commit-graph &&
+ test_must_fail git -C full fsck
+'
+
+test_expect_success 'git fsck shows commit-graph output with --progress' '
+ git -C "$TRASH_DIRECTORY/full" fsck --progress 2>err &&
+ grep "Verifying commits in commit graph" err
+'
+
+test_expect_success 'git fsck suppresses commit-graph output with --no-progress' '
+ git -C "$TRASH_DIRECTORY/full" fsck --no-progress 2>err &&
+ ! grep "Verifying commits in commit graph" err
'
test_expect_success 'setup non-the_repository tests' '
@@ -757,7 +721,7 @@ test_expect_success 'corrupt commit-graph write (broken parent)' '
git commit-tree -p "$broken" -m "good commit" "$empty" >good &&
test_must_fail git commit-graph write --stdin-commits \
<good 2>test_err &&
- test_i18ngrep "unable to parse commit" test_err
+ test_grep "unable to parse commit" test_err
)
'
@@ -778,7 +742,7 @@ test_expect_success 'corrupt commit-graph write (missing tree)' '
git commit-tree -p "$broken" -m "good" "$tree" >good &&
test_must_fail git commit-graph write --stdin-commits \
<good 2>test_err &&
- test_i18ngrep "unable to parse commit" test_err
+ test_grep "unable to parse commit" test_err
)
'
@@ -800,38 +764,185 @@ test_expect_success 'corrupt commit-graph write (missing tree)' '
#
test_expect_success 'set up and verify repo with generation data overflow chunk' '
- objdir=".git/objects" &&
UNIX_EPOCH_ZERO="@0 +0000" &&
FUTURE_DATE="@2147483646 +0000" &&
- test_oid_cache <<-EOF &&
- oid_version sha1:1
- oid_version sha256:2
- EOF
- cd "$TRASH_DIRECTORY" &&
- mkdir repo &&
- cd repo &&
- git init &&
- test_commit --date "$UNIX_EPOCH_ZERO" 1 &&
- test_commit 2 &&
- test_commit --date "$UNIX_EPOCH_ZERO" 3 &&
- git commit-graph write --reachable &&
- graph_read_expect 3 generation_data &&
- test_commit --date "$FUTURE_DATE" 4 &&
- test_commit 5 &&
- test_commit --date "$UNIX_EPOCH_ZERO" 6 &&
- git branch left &&
- git reset --hard 3 &&
- test_commit 7 &&
- test_commit --date "$FUTURE_DATE" 8 &&
- test_commit 9 &&
- git branch right &&
- git reset --hard 3 &&
- test_merge M left right &&
- git commit-graph write --reachable &&
- graph_read_expect 10 "generation_data generation_data_overflow" &&
- git commit-graph verify
+
+ git init repo &&
+ (
+ cd repo &&
+
+ test_commit --date "$UNIX_EPOCH_ZERO" 1 &&
+ test_commit 2 &&
+ test_commit --date "$UNIX_EPOCH_ZERO" 3 &&
+ git commit-graph write --reachable &&
+ graph_read_expect 3 generation_data &&
+ test_commit --date "$FUTURE_DATE" 4 &&
+ test_commit 5 &&
+ test_commit --date "$UNIX_EPOCH_ZERO" 6 &&
+ git branch left &&
+ git reset --hard 3 &&
+ test_commit 7 &&
+ test_commit --date "$FUTURE_DATE" 8 &&
+ test_commit 9 &&
+ git branch right &&
+ git reset --hard 3 &&
+ test_merge M left right &&
+ git commit-graph write --reachable &&
+ graph_read_expect 10 "generation_data generation_data_overflow" &&
+ git commit-graph verify
+ )
'
graph_git_behavior 'generation data overflow chunk repo' repo left right
+test_expect_success 'overflow during generation version upgrade' '
+ git init overflow-v2-upgrade &&
+ (
+ cd overflow-v2-upgrade &&
+
+ # This commit will have a date at two seconds past the Epoch,
+ # and a (v1) generation number of 1, since it is a root commit.
+ #
+ # The offset will then be computed as 1-2, which will underflow
+ # to 2^31, which is greater than the v2 offset small limit of
+ # 2^31-1.
+ #
+ # This is sufficient to need a large offset table for the v2
+ # generation numbers.
+ test_commit --date "@2 +0000" base &&
+ git repack -d &&
+
+ # Test that upgrading from generation v1 to v2 correctly
+ # produces the overflow table.
+ git -c commitGraph.generationVersion=1 commit-graph write &&
+ git -c commitGraph.generationVersion=2 commit-graph write \
+ --changed-paths &&
+
+ git rev-list --all
+ )
+'
+
+corrupt_chunk () {
+ graph=full/.git/objects/info/commit-graph &&
+ test_when_finished "rm -rf $graph" &&
+ git -C full commit-graph write --reachable &&
+ corrupt_chunk_file $graph "$@"
+}
+
+check_corrupt_chunk () {
+ corrupt_chunk "$@" &&
+ git -C full -c core.commitGraph=false log >expect.out &&
+ git -C full -c core.commitGraph=true log >out 2>err &&
+ test_cmp expect.out out
+}
+
+test_expect_success 'reader notices too-small oid fanout chunk' '
+ # make it big enough that the graph file is plausible,
+ # otherwise we hit an earlier check
+ check_corrupt_chunk OIDF clear $(printf "000000%02x" $(test_seq 250)) &&
+ cat >expect.err <<-\EOF &&
+ error: commit-graph oid fanout chunk is wrong size
+ error: commit-graph required OID fanout chunk missing or corrupted
+ EOF
+ test_cmp expect.err err
+'
+
+test_expect_success 'reader notices fanout/lookup table mismatch' '
+ check_corrupt_chunk OIDF 1020 "FFFFFFFF" &&
+ cat >expect.err <<-\EOF &&
+ error: commit-graph OID lookup chunk is the wrong size
+ error: commit-graph required OID lookup chunk missing or corrupted
+ EOF
+ test_cmp expect.err err
+'
+
+test_expect_success 'reader notices out-of-bounds fanout' '
+ # Rather than try to corrupt a specific hash, we will just
+ # wreck them all. But we cannot just set them all to 0xFFFFFFFF or
+ # similar, as they are used for hi/lo starts in a binary search (so if
+ # they are identical, that indicates that the search should abort
+ # immediately). Instead, we will give them high values that differ by
+ # 2^24, ensuring that any that are used would cause an out-of-bounds
+ # read.
+ check_corrupt_chunk OIDF 0 $(printf "%02x000000" $(test_seq 0 254)) &&
+ cat >expect.err <<-\EOF &&
+ error: commit-graph fanout values out of order
+ error: commit-graph required OID fanout chunk missing or corrupted
+ EOF
+ test_cmp expect.err err
+'
+
+test_expect_success 'reader notices too-small commit data chunk' '
+ check_corrupt_chunk CDAT clear 00000000 &&
+ cat >expect.err <<-\EOF &&
+ error: commit-graph commit data chunk is wrong size
+ error: commit-graph required commit data chunk missing or corrupted
+ EOF
+ test_cmp expect.err err
+'
+
+test_expect_success 'reader notices out-of-bounds extra edge' '
+ check_corrupt_chunk EDGE clear &&
+ cat >expect.err <<-\EOF &&
+ error: commit-graph extra-edges pointer out of bounds
+ EOF
+ test_cmp expect.err err
+'
+
+test_expect_success 'reader notices too-small generations chunk' '
+ check_corrupt_chunk GDA2 clear 00000000 &&
+ cat >expect.err <<-\EOF &&
+ error: commit-graph generations chunk is wrong size
+ EOF
+ test_cmp expect.err err
+'
+
+test_expect_success 'stale commit cannot be parsed when given directly' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ test_commit B &&
+ git commit-graph write --reachable &&
+
+ oid=$(git rev-parse B) &&
+ rm .git/objects/"$(test_oid_to_path "$oid")" &&
+
+ # Verify that it is possible to read the commit from the
+ # commit graph when not being paranoid, ...
+ git rev-list B &&
+ # ... but parsing the commit when double checking that
+ # it actually exists in the object database should fail.
+ test_must_fail env GIT_COMMIT_GRAPH_PARANOIA=true git rev-list -1 B
+ )
+'
+
+test_expect_success 'stale commit cannot be parsed when traversing graph' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ test_commit A &&
+ test_commit B &&
+ test_commit C &&
+ git commit-graph write --reachable &&
+
+ # Corrupt the repository by deleting the intermediate commit
+ # object. Commands should notice that this object is absent and
+ # thus that the repository is corrupt even if the commit graph
+ # exists.
+ oid=$(git rev-parse B) &&
+ rm .git/objects/"$(test_oid_to_path "$oid")" &&
+
+ # Again, we should be able to parse the commit when not
+ # being paranoid about commit graph staleness...
+ git rev-parse HEAD~2 &&
+ # ... but fail when we are paranoid.
+ test_must_fail env GIT_COMMIT_GRAPH_PARANOIA=true git rev-parse HEAD~2 2>error &&
+ grep "error: commit $oid exists in commit-graph but not in the object database" error
+ )
+'
+
test_done
diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh
index 3d4d9f1..dd09134 100755
--- a/t/t5319-multi-pack-index.sh
+++ b/t/t5319-multi-pack-index.sh
@@ -2,6 +2,7 @@
test_description='multi-pack-indexes'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-chunk.sh
GIT_TEST_MULTI_PACK_INDEX=0
objdir=.git/objects
@@ -93,7 +94,7 @@ test_expect_success 'create objects' '
test_commit initial &&
for i in $(test_seq 1 5)
do
- generate_objects $i
+ generate_objects $i || return 1
done &&
commit_and_list_objects
'
@@ -155,7 +156,7 @@ test_expect_success 'corrupt idx reports errors' '
test_expect_success 'add more objects' '
for i in $(test_seq 6 10)
do
- generate_objects $i
+ generate_objects $i || return 1
done &&
commit_and_list_objects
'
@@ -168,18 +169,45 @@ test_expect_success 'write midx with two packs' '
compare_results_with_midx "two packs"
+test_expect_success 'write midx with --stdin-packs' '
+ rm -fr $objdir/pack/multi-pack-index &&
+
+ idx="$(find $objdir/pack -name "test-2-*.idx")" &&
+ basename "$idx" >in &&
+
+ git multi-pack-index write --stdin-packs <in &&
+
+ test-tool read-midx $objdir | grep "\.idx$" >packs &&
+
+ test_cmp packs in
+'
+
+compare_results_with_midx "mixed mode (one pack + extra)"
+
+test_expect_success 'write with no objects and preferred pack' '
+ test_when_finished "rm -rf empty" &&
+ git init empty &&
+ test_must_fail git -C empty multi-pack-index write \
+ --stdin-packs --preferred-pack=does-not-exist </dev/null 2>err &&
+ cat >expect <<-EOF &&
+ warning: unknown preferred pack: ${SQ}does-not-exist${SQ}
+ error: no pack files to index.
+ EOF
+ test_cmp expect err
+'
+
test_expect_success 'write progress off for redirected stderr' '
git multi-pack-index --object-dir=$objdir write 2>err &&
test_line_count = 0 err
'
test_expect_success 'write force progress on for stderr' '
- GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir --progress write 2>err &&
+ GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir write --progress 2>err &&
test_file_not_empty err
'
test_expect_success 'write with the --no-progress option' '
- GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir --no-progress write 2>err &&
+ GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir write --no-progress 2>err &&
test_line_count = 0 err
'
@@ -188,7 +216,7 @@ test_expect_success 'add more packs' '
do
generate_objects $j &&
commit_and_list_objects &&
- git pack-objects --index-version=2 $objdir/pack/test-pack <obj-list
+ git pack-objects --index-version=2 $objdir/pack/test-pack <obj-list || return 1
done
'
@@ -201,6 +229,34 @@ test_expect_success 'write midx with twelve packs' '
compare_results_with_midx "twelve packs"
+test_expect_success 'multi-pack-index *.rev cleanup with --object-dir' '
+ git init repo &&
+ git clone -s repo alternate &&
+
+ test_when_finished "rm -rf repo alternate" &&
+
+ (
+ cd repo &&
+ test_commit base &&
+ git repack -d
+ ) &&
+
+ ours="alternate/.git/objects/pack/multi-pack-index-123.rev" &&
+ theirs="repo/.git/objects/pack/multi-pack-index-abc.rev" &&
+ touch "$ours" "$theirs" &&
+
+ (
+ cd alternate &&
+ git multi-pack-index --object-dir ../repo/.git/objects write
+ ) &&
+
+ # writing a midx in "repo" should not remove the .rev file in the
+ # alternate
+ test_path_is_file repo/.git/objects/pack/multi-pack-index &&
+ test_path_is_file $ours &&
+ test_path_is_missing $theirs
+'
+
test_expect_success 'warn on improper hash version' '
git init --object-format=sha1 sha1 &&
(
@@ -224,13 +280,13 @@ test_expect_success 'warn on improper hash version' '
cd sha1 &&
mv ../mpi-sha256 .git/objects/pack/multi-pack-index &&
git log -1 2>err &&
- test_i18ngrep "multi-pack-index hash version 2 does not match version 1" err
+ test_grep "multi-pack-index hash version 2 does not match version 1" err
) &&
(
cd sha256 &&
mv ../mpi-sha1 .git/objects/pack/multi-pack-index &&
git log -1 2>err &&
- test_i18ngrep "multi-pack-index hash version 1 does not match version 2" err
+ test_grep "multi-pack-index hash version 1 does not match version 2" err
)
'
@@ -277,6 +333,23 @@ test_expect_success 'midx picks objects from preferred pack' '
)
'
+test_expect_success 'preferred packs must be non-empty' '
+ test_when_finished rm -rf preferred.git &&
+ git init preferred.git &&
+ (
+ cd preferred.git &&
+
+ test_commit base &&
+ git repack -ad &&
+
+ empty="$(git pack-objects $objdir/pack/pack </dev/null)" &&
+
+ test_must_fail git multi-pack-index write \
+ --preferred-pack=pack-$empty.pack 2>err &&
+ grep "with no objects" err
+ )
+'
+
test_expect_success 'verify multi-pack-index success' '
git multi-pack-index verify --object-dir=$objdir
'
@@ -314,7 +387,7 @@ corrupt_midx_and_verify() {
printf "$DATA" | dd of="$FILE" bs=1 seek="$POS" conv=notrunc &&
test_must_fail $COMMAND 2>test_err &&
grep -v "^+" test_err >err &&
- test_i18ngrep "$GREPSTR" err
+ test_grep "$GREPSTR" err
}
test_expect_success 'verify bad signature' '
@@ -366,7 +439,7 @@ test_expect_success 'verify extended chunk count' '
test_expect_success 'verify missing required chunk' '
corrupt_midx_and_verify $MIDX_BYTE_CHUNK_ID "\01" $objdir \
- "missing required"
+ "required pack-name chunk missing"
'
test_expect_success 'verify invalid chunk offset' '
@@ -407,20 +480,37 @@ test_expect_success 'verify incorrect offset' '
test_expect_success 'git-fsck incorrect offset' '
corrupt_midx_and_verify $MIDX_BYTE_OFFSET "\377" $objdir \
"incorrect object offset" \
- "git -c core.multipackindex=true fsck"
+ "git -c core.multiPackIndex=true fsck" &&
+ test_unconfig core.multiPackIndex &&
+ test_must_fail git fsck &&
+ git -c core.multiPackIndex=false fsck
+'
+
+test_expect_success 'git fsck shows MIDX output with --progress' '
+ git fsck --progress 2>err &&
+ grep "Verifying OID order in multi-pack-index" err &&
+ grep "Verifying object offsets" err
+'
+
+test_expect_success 'git fsck suppresses MIDX output with --no-progress' '
+ git fsck --no-progress 2>err &&
+ ! grep "Verifying OID order in multi-pack-index" err &&
+ ! grep "Verifying object offsets" err
'
test_expect_success 'corrupt MIDX is not reused' '
corrupt_midx_and_verify $MIDX_BYTE_OFFSET "\377" $objdir \
"incorrect object offset" &&
git multi-pack-index write 2>err &&
- test_i18ngrep checksum.mismatch err &&
+ test_grep checksum.mismatch err &&
git multi-pack-index verify
'
test_expect_success 'verify incorrect checksum' '
- pos=$(($(wc -c <$objdir/pack/multi-pack-index) - 1)) &&
- corrupt_midx_and_verify $pos "\377" $objdir "incorrect checksum"
+ pos=$(($(wc -c <$objdir/pack/multi-pack-index) - 10)) &&
+ corrupt_midx_and_verify $pos \
+ "\377\377\377\377\377\377\377\377\377\377" \
+ $objdir "incorrect checksum"
'
test_expect_success 'repack progress off for redirected stderr' '
@@ -429,12 +519,12 @@ test_expect_success 'repack progress off for redirected stderr' '
'
test_expect_success 'repack force progress on for stderr' '
- GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir --progress repack 2>err &&
+ GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir repack --progress 2>err &&
test_file_not_empty err
'
test_expect_success 'repack with the --no-progress option' '
- GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir --no-progress repack 2>err &&
+ GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir repack --no-progress 2>err &&
test_line_count = 0 err
'
@@ -487,7 +577,8 @@ test_expect_success 'repack preserves multi-pack-index when creating packs' '
compare_results_with_midx "after repack"
test_expect_success 'multi-pack-index and pack-bitmap' '
- git -c repack.writeBitmaps=true repack -ad &&
+ GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0 \
+ git -c repack.writeBitmaps=true repack -ad &&
git multi-pack-index write &&
git rev-list --test-bitmap HEAD
'
@@ -530,14 +621,22 @@ test_expect_success 'force some 64-bit offsets with pack-objects' '
mkdir objects64/pack &&
for i in $(test_seq 1 11)
do
- generate_objects 11
+ generate_objects 11 || return 1
done &&
commit_and_list_objects &&
pack64=$(git pack-objects --index-version=2,0x40 objects64/pack/test-64 <obj-list) &&
idx64=objects64/pack/test-64-$pack64.idx &&
chmod u+w $idx64 &&
corrupt_data $idx64 $(test_oid idxoff) "\02" &&
- midx64=$(git multi-pack-index --object-dir=objects64 write) &&
+ # objects64 is not a real repository, but can serve as an alternate
+ # anyway so we can write a MIDX into it
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ ( cd ../objects64 && pwd ) >.git/objects/info/alternates &&
+ midx64=$(git multi-pack-index --object-dir=../objects64 write)
+ ) &&
midx_read_expect 1 63 5 objects64 " large-offsets"
'
@@ -566,7 +665,7 @@ test_expect_success 'setup expire tests' '
git update-index --add large_file.txt &&
for i in $(test_seq 1 20)
do
- test_commit $i
+ test_commit $i || exit 1
done &&
git branch A HEAD &&
git branch B HEAD~8 &&
@@ -618,7 +717,7 @@ test_expect_success 'expire progress off for redirected stderr' '
test_expect_success 'expire force progress on for stderr' '
(
cd dup &&
- GIT_PROGRESS_DELAY=0 git multi-pack-index --progress expire 2>err &&
+ GIT_PROGRESS_DELAY=0 git multi-pack-index expire --progress 2>err &&
test_file_not_empty err
)
'
@@ -626,7 +725,7 @@ test_expect_success 'expire force progress on for stderr' '
test_expect_success 'expire with the --no-progress option' '
(
cd dup &&
- GIT_PROGRESS_DELAY=0 git multi-pack-index --no-progress expire 2>err &&
+ GIT_PROGRESS_DELAY=0 git multi-pack-index expire --no-progress 2>err &&
test_line_count = 0 err
)
'
@@ -710,6 +809,70 @@ test_expect_success 'repack creates a new pack' '
)
'
+test_expect_success 'repack (all) ignores cruft pack' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit base &&
+ test_commit --no-tag unreachable &&
+
+ git reset --hard base &&
+ git reflog expire --all --expire=all &&
+ git repack --cruft -d &&
+
+ git multi-pack-index write &&
+
+ find $objdir/pack | sort >before &&
+ git multi-pack-index repack --batch-size=0 &&
+ find $objdir/pack | sort >after &&
+
+ test_cmp before after
+ )
+'
+
+test_expect_success 'repack (--batch-size) ignores cruft pack' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit_bulk 5 &&
+ test_commit --no-tag unreachable &&
+
+ git reset --hard HEAD^ &&
+ git reflog expire --all --expire=all &&
+ git repack --cruft -d &&
+
+ test_commit four &&
+
+ find $objdir/pack -type f -name "*.pack" | sort >before &&
+ git repack -d &&
+ find $objdir/pack -type f -name "*.pack" | sort >after &&
+
+ pack="$(comm -13 before after)" &&
+ test_file_size "$pack" >sz &&
+ # Set --batch-size to twice the size of the pack created
+ # in the previous step, since this is enough to
+ # accommodate it and the cruft pack.
+ #
+ # This means that the MIDX machinery *could* combine the
+ # new and cruft packs together.
+ #
+ # We ensure that it does not below.
+ batch="$((($(cat sz) * 2)))" &&
+
+ git multi-pack-index write &&
+
+ find $objdir/pack | sort >before &&
+ git multi-pack-index repack --batch-size=$batch &&
+ find $objdir/pack | sort >after &&
+
+ test_cmp before after
+ )
+'
+
test_expect_success 'expire removes repacked packs' '
(
cd dup &&
@@ -773,6 +936,36 @@ test_expect_success 'expire respects .keep files' '
)
'
+test_expect_success 'expiring unreferenced cruft pack retains pack' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit base &&
+ test_commit --no-tag unreachable &&
+ unreachable=$(git rev-parse HEAD) &&
+
+ git reset --hard base &&
+ git reflog expire --all --expire=all &&
+ git repack --cruft -d &&
+ mtimes="$(ls $objdir/pack/pack-*.mtimes)" &&
+
+ echo "base..$unreachable" >in &&
+ pack="$(git pack-objects --revs --delta-base-offset \
+ $objdir/pack/pack <in)" &&
+
+ # Preferring the contents of "$pack" will leave the
+ # cruft pack unreferenced (ie., none of the objects
+ # contained in the cruft pack will have their MIDX copy
+ # selected from the cruft pack).
+ git multi-pack-index write --preferred-pack="pack-$pack.pack" &&
+ git multi-pack-index expire &&
+
+ test_path_is_file "$mtimes"
+ )
+'
+
test_expect_success 'repack --batch-size=0 repacks everything' '
cp -r dup dup2 &&
(
@@ -839,7 +1032,178 @@ test_expect_success 'load reverse index when missing .idx, .pack' '
test_expect_success 'usage shown without sub-command' '
test_expect_code 129 git multi-pack-index 2>err &&
- ! test_i18ngrep "unrecognized subcommand" err
+ ! test_grep "unrecognized subcommand" err
+'
+
+test_expect_success 'complains when run outside of a repository' '
+ nongit test_must_fail git multi-pack-index write 2>err &&
+ grep "not a git repository" err
+'
+
+test_expect_success 'repack with delta islands' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit first &&
+ git repack &&
+ test_commit second &&
+ git repack &&
+
+ git multi-pack-index write &&
+ git -c repack.useDeltaIslands=true multi-pack-index repack
+ )
+'
+
+corrupt_chunk () {
+ midx=.git/objects/pack/multi-pack-index &&
+ test_when_finished "rm -rf $midx" &&
+ git repack -ad --write-midx &&
+ corrupt_chunk_file $midx "$@"
+}
+
+test_expect_success 'reader notices too-small oid fanout chunk' '
+ corrupt_chunk OIDF clear 00000000 &&
+ test_must_fail git log 2>err &&
+ cat >expect <<-\EOF &&
+ error: multi-pack-index OID fanout is of the wrong size
+ fatal: multi-pack-index required OID fanout chunk missing or corrupted
+ EOF
+ test_cmp expect err
+'
+
+test_expect_success 'reader notices too-small oid lookup chunk' '
+ corrupt_chunk OIDL clear 00000000 &&
+ test_must_fail git log 2>err &&
+ cat >expect <<-\EOF &&
+ error: multi-pack-index OID lookup chunk is the wrong size
+ fatal: multi-pack-index required OID lookup chunk missing or corrupted
+ EOF
+ test_cmp expect err
+'
+
+test_expect_success 'reader notices too-small pack names chunk' '
+ # There is no NUL to terminate the name here, so the
+ # chunk is too short.
+ corrupt_chunk PNAM clear 70656666 &&
+ test_must_fail git log 2>err &&
+ cat >expect <<-\EOF &&
+ fatal: multi-pack-index pack-name chunk is too short
+ EOF
+ test_cmp expect err
+'
+
+test_expect_success 'reader handles unaligned chunks' '
+ # A 9-byte PNAM means all of the subsequent chunks
+ # will no longer be 4-byte aligned, but it is still
+ # a valid one-pack chunk on its own (it is "foo.pack\0").
+ corrupt_chunk PNAM clear 666f6f2e7061636b00 &&
+ git -c core.multipackindex=false log >expect.out &&
+ git -c core.multipackindex=true log >out 2>err &&
+ test_cmp expect.out out &&
+ cat >expect.err <<-\EOF &&
+ error: chunk id 4f494446 not 4-byte aligned
+ EOF
+ test_cmp expect.err err
+'
+
+test_expect_success 'reader notices too-small object offset chunk' '
+ corrupt_chunk OOFF clear 00000000 &&
+ test_must_fail git log 2>err &&
+ cat >expect <<-\EOF &&
+ error: multi-pack-index object offset chunk is the wrong size
+ fatal: multi-pack-index required object offsets chunk missing or corrupted
+ EOF
+ test_cmp expect err
+'
+
+test_expect_success 'reader bounds-checks large offset table' '
+ # re-use the objects64 dir here to cheaply get access to a midx
+ # with large offsets.
+ git init repo &&
+ test_when_finished "rm -rf repo" &&
+ (
+ cd repo &&
+ (cd ../objects64 && pwd) >.git/objects/info/alternates &&
+ git multi-pack-index --object-dir=../objects64 write &&
+ midx=../objects64/pack/multi-pack-index &&
+ corrupt_chunk_file $midx LOFF clear &&
+ # using only %(objectsize) is important here; see the commit
+ # message for more details
+ test_must_fail git cat-file --batch-all-objects \
+ --batch-check="%(objectsize)" 2>err &&
+ cat >expect <<-\EOF &&
+ fatal: multi-pack-index large offset out of bounds
+ EOF
+ test_cmp expect err
+ )
+'
+
+test_expect_success 'reader notices too-small revindex chunk' '
+ # We only get a revindex with bitmaps (and likewise only
+ # load it when they are asked for).
+ test_config repack.writeBitmaps true &&
+ corrupt_chunk RIDX clear 00000000 &&
+ git -c core.multipackIndex=false rev-list \
+ --all --use-bitmap-index >expect.out &&
+ git -c core.multipackIndex=true rev-list \
+ --all --use-bitmap-index >out 2>err &&
+ test_cmp expect.out out &&
+ cat >expect.err <<-\EOF &&
+ error: multi-pack-index reverse-index chunk is the wrong size
+ warning: multi-pack bitmap is missing required reverse index
+ EOF
+ test_cmp expect.err err
+'
+
+test_expect_success 'reader notices out-of-bounds fanout' '
+ # This is similar to the out-of-bounds fanout test in t5318. The values
+ # in adjacent entries should be large but not identical (they
+ # are used as hi/lo starts for a binary search, which would then abort
+ # immediately).
+ corrupt_chunk OIDF 0 $(printf "%02x000000" $(test_seq 0 254)) &&
+ test_must_fail git log 2>err &&
+ cat >expect <<-\EOF &&
+ error: oid fanout out of order: fanout[254] = fe000000 > 5c = fanout[255]
+ fatal: multi-pack-index required OID fanout chunk missing or corrupted
+ EOF
+ test_cmp expect err
+'
+
+test_expect_success 'bitmapped packs are stored via the BTMP chunk' '
+ test_when_finished "rm -fr repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ for i in 1 2 3 4 5
+ do
+ test_commit "$i" &&
+ git repack -d || return 1
+ done &&
+
+ find $objdir/pack -type f -name "*.idx" | xargs -n 1 basename |
+ sort >packs &&
+
+ git multi-pack-index write --stdin-packs <packs &&
+ test_must_fail test-tool read-midx --bitmap $objdir 2>err &&
+ cat >expect <<-\EOF &&
+ error: MIDX does not contain the BTMP chunk
+ EOF
+ test_cmp expect err &&
+
+ git multi-pack-index write --stdin-packs --bitmap \
+ --preferred-pack="$(head -n1 <packs)" <packs &&
+ test-tool read-midx --bitmap $objdir >actual &&
+ for i in $(test_seq $(wc -l <packs))
+ do
+ sed -ne "${i}s/\.idx$/\.pack/p" packs &&
+ echo " bitmap_pos: $((($i - 1) * 3))" &&
+ echo " bitmap_nr: 3" || return 1
+ done >expect &&
+ test_cmp expect actual
+ )
'
test_done
diff --git a/t/t5320-delta-islands.sh b/t/t5320-delta-islands.sh
index fea92a5..4063633 100755
--- a/t/t5320-delta-islands.sh
+++ b/t/t5320-delta-islands.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='exercise delta islands'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# returns true iff $1 is a delta based on $2
@@ -132,7 +134,7 @@ test_expect_success 'island core places core objects first' '
repack -adfi &&
git verify-pack -v .git/objects/pack/*.pack |
cut -d" " -f1 |
- egrep "$root|$two" >actual &&
+ grep -E "$root|$two" >actual &&
test_cmp expect actual
'
diff --git a/t/t5321-pack-large-objects.sh b/t/t5321-pack-large-objects.sh
index 8a56d98..70770fe 100755
--- a/t/t5321-pack-large-objects.sh
+++ b/t/t5321-pack-large-objects.sh
@@ -6,6 +6,8 @@
test_description='git pack-object with "large" deltas
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-pack.sh
diff --git a/t/t5322-pack-objects-sparse.sh b/t/t5322-pack-objects-sparse.sh
index 61cb907..770695c 100755
--- a/t/t5322-pack-objects-sparse.sh
+++ b/t/t5322-pack-objects-sparse.sh
@@ -4,6 +4,7 @@ test_description='pack-objects object selection using sparse algorithm'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup repo' '
@@ -14,7 +15,7 @@ test_expect_success 'setup repo' '
for j in $(test_seq 1 3)
do
mkdir f$i/f$j &&
- echo $j >f$i/f$j/data.txt
+ echo $j >f$i/f$j/data.txt || return 1
done
done &&
git add . &&
@@ -23,7 +24,7 @@ test_expect_success 'setup repo' '
do
git checkout -b topic$i main &&
echo change-$i >f$i/f$i/data.txt &&
- git commit -a -m "Changed f$i/f$i/data.txt"
+ git commit -a -m "Changed f$i/f$i/data.txt" || return 1
done &&
cat >packinput.txt <<-EOF &&
topic1
diff --git a/t/t5324-split-commit-graph.sh b/t/t5324-split-commit-graph.sh
index 587226e..281266f 100755
--- a/t/t5324-split-commit-graph.sh
+++ b/t/t5324-split-commit-graph.sh
@@ -1,7 +1,10 @@
#!/bin/sh
test_description='split commit graph'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-chunk.sh
GIT_TEST_COMMIT_GRAPH=0
GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=0
@@ -30,10 +33,16 @@ graph_read_expect() {
then
NUM_BASE=$2
fi
+ OPTIONS=
+ if test -z "$3"
+ then
+ OPTIONS=" read_generation_data"
+ fi
cat >expect <<- EOF
header: 43475048 1 $(test_oid oid_version) 4 $NUM_BASE
num_commits: $1
chunks: oid_fanout oid_lookup commit_metadata generation_data
+ options:$OPTIONS
EOF
test-tool read-graph >output &&
test_cmp expect output
@@ -55,8 +64,8 @@ test_expect_success 'create commits and write commit-graph' '
'
graph_git_two_modes() {
- git -c core.commitGraph=true $1 >output
- git -c core.commitGraph=false $1 >expect
+ git ${2:+ -C "$2"} -c core.commitGraph=true $1 >output &&
+ git ${2:+ -C "$2"} -c core.commitGraph=false $1 >expect &&
test_cmp expect output
}
@@ -64,12 +73,13 @@ graph_git_behavior() {
MSG=$1
BRANCH=$2
COMPARE=$3
+ DIR=$4
test_expect_success "check normal git operations: $MSG" '
- graph_git_two_modes "log --oneline $BRANCH" &&
- graph_git_two_modes "log --topo-order $BRANCH" &&
- graph_git_two_modes "log --graph $COMPARE..$BRANCH" &&
- graph_git_two_modes "branch -vv" &&
- graph_git_two_modes "merge-base -a $BRANCH $COMPARE"
+ graph_git_two_modes "log --oneline $BRANCH" "$DIR" &&
+ graph_git_two_modes "log --topo-order $BRANCH" "$DIR" &&
+ graph_git_two_modes "log --graph $COMPARE..$BRANCH" "$DIR" &&
+ graph_git_two_modes "branch -vv" "$DIR" &&
+ graph_git_two_modes "merge-base -a $BRANCH $COMPARE" "$DIR"
'
}
@@ -187,7 +197,10 @@ test_expect_success 'create fork and chain across alternate' '
)
'
-graph_git_behavior 'alternate: commit 13 vs 6' commits/13 commits/6
+if test -d fork
+then
+ graph_git_behavior 'alternate: commit 13 vs 6' commits/13 origin/commits/6 "fork"
+fi
test_expect_success 'test merge stragety constants' '
git clone . merge-2 &&
@@ -271,7 +284,33 @@ test_expect_success 'verify hashes along chain, even in shallow' '
corrupt_file "$base_file" $(test_oid shallow) "\01" &&
test_must_fail git commit-graph verify --shallow 2>test_err &&
grep -v "^+" test_err >err &&
- test_i18ngrep "incorrect checksum" err
+ test_grep "incorrect checksum" err
+ )
+'
+
+test_expect_success 'verify notices chain slice which is bogus (base)' '
+ git clone --no-hardlinks . verify-chain-bogus-base &&
+ (
+ cd verify-chain-bogus-base &&
+ git commit-graph verify &&
+ base_file=$graphdir/graph-$(sed -n 1p $graphdir/commit-graph-chain).graph &&
+ echo "garbage" >$base_file &&
+ test_must_fail git commit-graph verify 2>test_err &&
+ grep -v "^+" test_err >err &&
+ grep "commit-graph file is too small" err
+ )
+'
+
+test_expect_success 'verify notices chain slice which is bogus (tip)' '
+ git clone --no-hardlinks . verify-chain-bogus-tip &&
+ (
+ cd verify-chain-bogus-tip &&
+ git commit-graph verify &&
+ tip_file=$graphdir/graph-$(sed -n 2p $graphdir/commit-graph-chain).graph &&
+ echo "garbage" >$tip_file &&
+ test_must_fail git commit-graph verify 2>test_err &&
+ grep -v "^+" test_err >err &&
+ grep "commit-graph file is too small" err
)
'
@@ -281,11 +320,11 @@ test_expect_success 'verify --shallow does not check base contents' '
cd verify-shallow &&
git commit-graph verify &&
base_file=$graphdir/graph-$(head -n 1 $graphdir/commit-graph-chain).graph &&
- corrupt_file "$base_file" 1000 "\01" &&
+ corrupt_file "$base_file" 1500 "\01" &&
git commit-graph verify --shallow &&
test_must_fail git commit-graph verify 2>test_err &&
grep -v "^+" test_err >err &&
- test_i18ngrep "incorrect checksum" err
+ test_grep "incorrect checksum" err
)
'
@@ -296,24 +335,51 @@ test_expect_success 'warn on base graph chunk incorrect' '
git commit-graph verify &&
base_file=$graphdir/graph-$(tail -n 1 $graphdir/commit-graph-chain).graph &&
corrupt_file "$base_file" $(test_oid base) "\01" &&
- git commit-graph verify --shallow 2>test_err &&
+ test_must_fail git commit-graph verify --shallow 2>test_err &&
+ grep -v "^+" test_err >err &&
+ test_grep "commit-graph chain does not match" err
+ )
+'
+
+test_expect_success 'verify after commit-graph-chain corruption (base)' '
+ git clone --no-hardlinks . verify-chain-base &&
+ (
+ cd verify-chain-base &&
+ corrupt_file "$graphdir/commit-graph-chain" 30 "G" &&
+ test_must_fail git commit-graph verify 2>test_err &&
+ grep -v "^+" test_err >err &&
+ test_grep "invalid commit-graph chain" err &&
+ corrupt_file "$graphdir/commit-graph-chain" 30 "A" &&
+ test_must_fail git commit-graph verify 2>test_err &&
grep -v "^+" test_err >err &&
- test_i18ngrep "commit-graph chain does not match" err
+ test_grep "unable to find all commit-graph files" err
)
'
-test_expect_success 'verify after commit-graph-chain corruption' '
- git clone --no-hardlinks . verify-chain &&
+test_expect_success 'verify after commit-graph-chain corruption (tip)' '
+ git clone --no-hardlinks . verify-chain-tip &&
(
- cd verify-chain &&
- corrupt_file "$graphdir/commit-graph-chain" 60 "G" &&
- git commit-graph verify 2>test_err &&
+ cd verify-chain-tip &&
+ corrupt_file "$graphdir/commit-graph-chain" 70 "G" &&
+ test_must_fail git commit-graph verify 2>test_err &&
grep -v "^+" test_err >err &&
- test_i18ngrep "invalid commit-graph chain" err &&
- corrupt_file "$graphdir/commit-graph-chain" 60 "A" &&
- git commit-graph verify 2>test_err &&
+ test_grep "invalid commit-graph chain" err &&
+ corrupt_file "$graphdir/commit-graph-chain" 70 "A" &&
+ test_must_fail git commit-graph verify 2>test_err &&
+ grep -v "^+" test_err >err &&
+ test_grep "unable to find all commit-graph files" err
+ )
+'
+
+test_expect_success 'verify notices too-short chain file' '
+ git clone --no-hardlinks . verify-chain-short &&
+ (
+ cd verify-chain-short &&
+ git commit-graph verify &&
+ echo "garbage" >$graphdir/commit-graph-chain &&
+ test_must_fail git commit-graph verify 2>test_err &&
grep -v "^+" test_err >err &&
- test_i18ngrep "unable to find all commit-graph files" err
+ grep "commit-graph chain file too small" err
)
'
@@ -328,10 +394,23 @@ test_expect_success 'verify across alternates' '
test_commit extra &&
git commit-graph write --reachable --split &&
tip_file=$graphdir/graph-$(tail -n 1 $graphdir/commit-graph-chain).graph &&
- corrupt_file "$tip_file" 100 "\01" &&
+ corrupt_file "$tip_file" 1500 "\01" &&
test_must_fail git commit-graph verify --shallow 2>test_err &&
grep -v "^+" test_err >err &&
- test_i18ngrep "commit-graph has incorrect fanout value" err
+ test_grep "incorrect checksum" err
+ )
+'
+
+test_expect_success 'reader bounds-checks base-graph chunk' '
+ git clone --no-hardlinks . corrupt-base-chunk &&
+ (
+ cd corrupt-base-chunk &&
+ tip_file=$graphdir/graph-$(tail -n 1 $graphdir/commit-graph-chain).graph &&
+ corrupt_chunk_file "$tip_file" BASE clear 01020304 &&
+ git -c core.commitGraph=false log >expect.out &&
+ git -c core.commitGraph=true log >out 2>err &&
+ test_cmp expect.out out &&
+ grep "commit-graph base graphs chunk is too small" err
)
'
@@ -341,8 +420,9 @@ test_expect_success 'add octopus merge' '
git branch merge/octopus &&
git commit-graph write --reachable --split &&
git commit-graph verify --progress 2>err &&
- test_line_count = 3 err &&
- test_i18ngrep ! warning err &&
+ test_line_count = 1 err &&
+ grep "Verifying commits in commit graph: 100% (18/18)" err &&
+ test_grep ! warning err &&
test_line_count = 3 $graphdir/commit-graph-chain
'
@@ -444,7 +524,7 @@ test_expect_success 'prevent regression for duplicate commits across layers' '
git init dup &&
git -C dup commit --allow-empty -m one &&
git -C dup -c core.commitGraph=false commit-graph write --split=no-merge --reachable 2>err &&
- test_i18ngrep "attempting to write a commit-graph" err &&
+ test_grep "attempting to write a commit-graph" err &&
git -C dup commit-graph write --split=no-merge --reachable &&
git -C dup commit --allow-empty -m two &&
git -C dup commit-graph write --split=no-merge --reachable &&
@@ -504,6 +584,7 @@ test_expect_success 'setup repo for mixed generation commit-graph-chain' '
header: 43475048 1 $(test_oid oid_version) 4 1
num_commits: $NUM_SECOND_LAYER_COMMITS
chunks: oid_fanout oid_lookup commit_metadata
+ options:
EOF
test_cmp expect output &&
git commit-graph verify &&
@@ -536,6 +617,7 @@ test_expect_success 'do not write generation data chunk if not present on existi
header: 43475048 1 $(test_oid oid_version) 4 2
num_commits: $NUM_THIRD_LAYER_COMMITS
chunks: oid_fanout oid_lookup commit_metadata
+ options:
EOF
test_cmp expect output &&
git commit-graph verify
@@ -577,6 +659,7 @@ test_expect_success 'do not write generation data chunk if the topmost remaining
header: 43475048 1 $(test_oid oid_version) 4 2
num_commits: $(($NUM_THIRD_LAYER_COMMITS + $NUM_FOURTH_LAYER_COMMITS))
chunks: oid_fanout oid_lookup commit_metadata
+ options:
EOF
test_cmp expect output &&
git commit-graph verify
@@ -616,6 +699,7 @@ test_expect_success 'write generation data chunk if topmost remaining layer has
header: 43475048 1 $(test_oid oid_version) 5 1
num_commits: $(($NUM_SECOND_LAYER_COMMITS + $NUM_THIRD_LAYER_COMMITS + $NUM_FOURTH_LAYER_COMMITS + $NUM_FIFTH_LAYER_COMMITS))
chunks: oid_fanout oid_lookup commit_metadata generation_data
+ options: read_generation_data
EOF
test_cmp expect output
)
diff --git a/t/t5325-reverse-index.sh b/t/t5325-reverse-index.sh
index da453f6..431a603 100755
--- a/t/t5325-reverse-index.sh
+++ b/t/t5325-reverse-index.sh
@@ -1,17 +1,20 @@
#!/bin/sh
test_description='on-disk reverse index'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# The below tests want control over the 'pack.writeReverseIndex' setting
# themselves to assert various combinations of it with other options.
-sane_unset GIT_TEST_WRITE_REV_INDEX
+sane_unset GIT_TEST_NO_WRITE_REV_INDEX
packdir=.git/objects/pack
test_expect_success 'setup' '
test_commit base &&
+ test_config pack.writeReverseIndex false &&
pack=$(git pack-objects --all $packdir/pack) &&
rev=$packdir/pack-$pack.rev &&
@@ -46,7 +49,7 @@ test_expect_success 'index-pack with --[no-]rev-index' '
test_path_exists $rev &&
test_index_pack "$conf" --no-rev-index &&
- test_path_is_missing $rev
+ test_path_is_missing $rev || return 1
done
'
@@ -94,6 +97,17 @@ test_expect_success 'reverse index is not generated when available on disk' '
--batch-check="%(objectsize:disk)" <tip
'
+test_expect_success 'reverse index is ignored when pack.readReverseIndex is false' '
+ test_index_pack true &&
+ test_path_is_file $rev &&
+
+ test_config pack.readReverseIndex false &&
+
+ git rev-parse HEAD >tip &&
+ GIT_TEST_REV_INDEX_DIE_ON_DISK=1 git cat-file \
+ --batch-check="%(objectsize:disk)" <tip
+'
+
test_expect_success 'revindex in-memory vs on-disk' '
git init repo &&
test_when_finished "rm -fr repo" &&
@@ -117,4 +131,78 @@ test_expect_success 'revindex in-memory vs on-disk' '
test_cmp on-disk in-core
)
'
+
+test_expect_success 'fsck succeeds on good rev-index' '
+ test_when_finished rm -fr repo &&
+ git init repo &&
+ (
+ cd repo &&
+
+ test_commit commit &&
+ git -c pack.writeReverseIndex=true repack -ad &&
+ git fsck 2>err &&
+ test_must_be_empty err
+ )
+'
+
+test_expect_success 'set up rev-index corruption tests' '
+ git init corrupt &&
+ (
+ cd corrupt &&
+
+ test_commit commit &&
+ git -c pack.writeReverseIndex=true repack -ad &&
+
+ revfile=$(ls .git/objects/pack/pack-*.rev) &&
+ chmod a+w $revfile &&
+ cp $revfile $revfile.bak
+ )
+'
+
+corrupt_rev_and_verify () {
+ (
+ pos="$1" &&
+ value="$2" &&
+ error="$3" &&
+
+ cd corrupt &&
+ revfile=$(ls .git/objects/pack/pack-*.rev) &&
+
+ # Reset to original rev-file.
+ cp $revfile.bak $revfile &&
+
+ printf "$value" | dd of=$revfile bs=1 seek="$pos" conv=notrunc &&
+ test_must_fail git fsck 2>err &&
+ grep "$error" err
+ )
+}
+
+test_expect_success 'fsck catches invalid checksum' '
+ revfile=$(ls corrupt/.git/objects/pack/pack-*.rev) &&
+ orig_size=$(wc -c <$revfile) &&
+ hashpos=$((orig_size - 10)) &&
+ corrupt_rev_and_verify $hashpos bogus \
+ "invalid checksum"
+'
+
+test_expect_success 'fsck catches invalid row position' '
+ corrupt_rev_and_verify 14 "\07" \
+ "invalid rev-index position"
+'
+
+test_expect_success 'fsck catches invalid header: magic number' '
+ corrupt_rev_and_verify 1 "\07" \
+ "reverse-index file .* has unknown signature"
+'
+
+test_expect_success 'fsck catches invalid header: version' '
+ corrupt_rev_and_verify 7 "\02" \
+ "reverse-index file .* has unsupported version"
+'
+
+test_expect_success 'fsck catches invalid header: hash function' '
+ corrupt_rev_and_verify 11 "\03" \
+ "reverse-index file .* has unsupported hash id"
+'
+
test_done
diff --git a/t/t5326-multi-pack-bitmaps.sh b/t/t5326-multi-pack-bitmaps.sh
new file mode 100755
index 0000000..5d7d321
--- /dev/null
+++ b/t/t5326-multi-pack-bitmaps.sh
@@ -0,0 +1,533 @@
+#!/bin/sh
+
+test_description='exercise basic multi-pack bitmap functionality'
+. ./test-lib.sh
+. "${TEST_DIRECTORY}/lib-bitmap.sh"
+
+# We'll be writing our own midx and bitmaps, so avoid getting confused by the
+# automatic ones.
+GIT_TEST_MULTI_PACK_INDEX=0
+GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0
+
+# This test exercise multi-pack bitmap functionality where the object order is
+# stored and read from a special chunk within the MIDX, so use the default
+# behavior here.
+sane_unset GIT_TEST_MIDX_WRITE_REV
+sane_unset GIT_TEST_MIDX_READ_RIDX
+
+bitmap_reuse_tests() {
+ from=$1
+ to=$2
+ writeLookupTable=false
+
+ for i in $3-${$#}
+ do
+ case $i in
+ "pack.writeBitmapLookupTable") writeLookupTable=true;;
+ esac
+ done
+
+ test_expect_success "setup pack reuse tests ($from -> $to)" '
+ rm -fr repo &&
+ git init repo &&
+ (
+ cd repo &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
+ test_commit_bulk 16 &&
+ git tag old-tip &&
+
+ git config core.multiPackIndex true &&
+ if test "MIDX" = "$from"
+ then
+ git repack -Ad &&
+ git multi-pack-index write --bitmap
+ else
+ git repack -Adb
+ fi
+ )
+ '
+
+ test_expect_success "build bitmap from existing ($from -> $to)" '
+ (
+ cd repo &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
+ test_commit_bulk --id=further 16 &&
+ git tag new-tip &&
+
+ if test "MIDX" = "$to"
+ then
+ git repack -d &&
+ git multi-pack-index write --bitmap
+ else
+ git repack -Adb
+ fi
+ )
+ '
+
+ test_expect_success "verify resulting bitmaps ($from -> $to)" '
+ (
+ cd repo &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
+ git for-each-ref &&
+ git rev-list --test-bitmap refs/tags/old-tip &&
+ git rev-list --test-bitmap refs/tags/new-tip
+ )
+ '
+}
+
+test_midx_bitmap_cases () {
+ writeLookupTable=false
+ writeBitmapLookupTable=
+
+ for i in "$@"
+ do
+ case $i in
+ "pack.writeBitmapLookupTable")
+ writeLookupTable=true
+ writeBitmapLookupTable="$i"
+ ;;
+ esac
+ done
+
+ test_expect_success 'setup test_repository' '
+ rm -rf * .git &&
+ git init &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"'
+ '
+
+ midx_bitmap_core
+
+ bitmap_reuse_tests 'pack' 'MIDX' "$writeBitmapLookupTable"
+ bitmap_reuse_tests 'MIDX' 'pack' "$writeBitmapLookupTable"
+ bitmap_reuse_tests 'MIDX' 'MIDX' "$writeBitmapLookupTable"
+
+ test_expect_success 'missing object closure fails gracefully' '
+ rm -fr repo &&
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
+
+ test_commit loose &&
+ test_commit packed &&
+
+ # Do not pass "--revs"; we want a pack without the "loose"
+ # commit.
+ git pack-objects $objdir/pack/pack <<-EOF &&
+ $(git rev-parse packed)
+ EOF
+
+ test_must_fail git multi-pack-index write --bitmap 2>err &&
+ grep "doesn.t have full closure" err &&
+ test_path_is_missing $midx
+ )
+ '
+
+ midx_bitmap_partial_tests
+
+ test_expect_success 'removing a MIDX clears stale bitmaps' '
+ rm -fr repo &&
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
+ test_commit base &&
+ git repack &&
+ git multi-pack-index write --bitmap &&
+
+ # Write a MIDX and bitmap; remove the MIDX but leave the bitmap.
+ stale_bitmap=$midx-$(midx_checksum $objdir).bitmap &&
+ rm $midx &&
+
+ # Then write a new MIDX.
+ test_commit new &&
+ git repack &&
+ git multi-pack-index write --bitmap &&
+
+ test_path_is_file $midx &&
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
+ test_path_is_missing $stale_bitmap
+ )
+ '
+
+ test_expect_success 'pack.preferBitmapTips' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
+
+ test_commit_bulk --message="%s" 103 &&
+
+ git log --format="%H" >commits.raw &&
+ sort <commits.raw >commits &&
+
+ git log --format="create refs/tags/%s %H" HEAD >refs &&
+ git update-ref --stdin <refs &&
+
+ git multi-pack-index write --bitmap &&
+ test_path_is_file $midx &&
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
+
+ test-tool bitmap list-commits | sort >bitmaps &&
+ comm -13 bitmaps commits >before &&
+ test_line_count = 1 before &&
+
+ perl -ne "printf(\"create refs/tags/include/%d \", $.); print" \
+ <before | git update-ref --stdin &&
+
+ rm -fr $midx-$(midx_checksum $objdir).bitmap &&
+ rm -fr $midx &&
+
+ git -c pack.preferBitmapTips=refs/tags/include \
+ multi-pack-index write --bitmap &&
+ test-tool bitmap list-commits | sort >bitmaps &&
+ comm -13 bitmaps commits >after &&
+
+ ! test_cmp before after
+ )
+ '
+
+ test_expect_success 'writing a bitmap with --refs-snapshot' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
+
+ test_commit one &&
+ test_commit two &&
+
+ git rev-parse one >snapshot &&
+
+ git repack -ad &&
+
+ # First, write a MIDX which see both refs/tags/one and
+ # refs/tags/two (causing both of those commits to receive
+ # bitmaps).
+ git multi-pack-index write --bitmap &&
+
+ test_path_is_file $midx &&
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
+
+ test-tool bitmap list-commits | sort >bitmaps &&
+ grep "$(git rev-parse one)" bitmaps &&
+ grep "$(git rev-parse two)" bitmaps &&
+
+ rm -fr $midx-$(midx_checksum $objdir).bitmap &&
+ rm -fr $midx &&
+
+ # Then again, but with a refs snapshot which only sees
+ # refs/tags/one.
+ git multi-pack-index write --bitmap --refs-snapshot=snapshot &&
+
+ test_path_is_file $midx &&
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
+
+ test-tool bitmap list-commits | sort >bitmaps &&
+ grep "$(git rev-parse one)" bitmaps &&
+ ! grep "$(git rev-parse two)" bitmaps
+ )
+ '
+
+ test_expect_success 'write a bitmap with --refs-snapshot (preferred tips)' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
+
+ test_commit_bulk --message="%s" 103 &&
+
+ git log --format="%H" >commits.raw &&
+ sort <commits.raw >commits &&
+
+ git log --format="create refs/tags/%s %H" HEAD >refs &&
+ git update-ref --stdin <refs &&
+
+ git multi-pack-index write --bitmap &&
+ test_path_is_file $midx &&
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
+
+ test-tool bitmap list-commits | sort >bitmaps &&
+ comm -13 bitmaps commits >before &&
+ test_line_count = 1 before &&
+
+ (
+ grep -vf before commits.raw &&
+ # mark missing commits as preferred
+ sed "s/^/+/" before
+ ) >snapshot &&
+
+ rm -fr $midx-$(midx_checksum $objdir).bitmap &&
+ rm -fr $midx &&
+
+ git multi-pack-index write --bitmap --refs-snapshot=snapshot &&
+ test-tool bitmap list-commits | sort >bitmaps &&
+ comm -13 bitmaps commits >after &&
+
+ ! test_cmp before after
+ )
+ '
+
+ test_expect_success 'hash-cache values are propagated from pack bitmaps' '
+ rm -fr repo &&
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
+
+ test_commit base &&
+ test_commit base2 &&
+ git repack -adb &&
+
+ test-tool bitmap dump-hashes >pack.raw &&
+ test_file_not_empty pack.raw &&
+ sort pack.raw >pack.hashes &&
+
+ test_commit new &&
+ git repack &&
+ git multi-pack-index write --bitmap &&
+
+ test-tool bitmap dump-hashes >midx.raw &&
+ sort midx.raw >midx.hashes &&
+
+ # ensure that every namehash in the pack bitmap can be found in
+ # the midx bitmap (i.e., that there are no oid-namehash pairs
+ # unique to the pack bitmap).
+ comm -23 pack.hashes midx.hashes >dropped.hashes &&
+ test_must_be_empty dropped.hashes
+ )
+ '
+
+ test_expect_success 'no .bitmap is written without any objects' '
+ rm -fr repo &&
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
+
+ empty="$(git pack-objects $objdir/pack/pack </dev/null)" &&
+ cat >packs <<-EOF &&
+ pack-$empty.idx
+ EOF
+
+ git multi-pack-index write --bitmap --stdin-packs \
+ <packs 2>err &&
+
+ grep "bitmap without any objects" err &&
+
+ test_path_is_file $midx &&
+ test_path_is_missing $midx-$(midx_checksum $objdir).bitmap
+ )
+ '
+
+ test_expect_success 'graceful fallback when missing reverse index' '
+ rm -fr repo &&
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
+
+ test_commit base &&
+
+ # write a pack and MIDX bitmap containing base
+ git repack -adb &&
+ git multi-pack-index write --bitmap &&
+
+ GIT_TEST_MIDX_READ_RIDX=0 \
+ git rev-list --use-bitmap-index HEAD 2>err &&
+ ! grep "ignoring extra bitmap file" err
+ )
+ '
+}
+
+test_midx_bitmap_cases
+
+test_midx_bitmap_cases "pack.writeBitmapLookupTable"
+
+test_expect_success 'multi-pack-index write writes lookup table if enabled' '
+ rm -fr repo &&
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ test_commit base &&
+ git config pack.writeBitmapLookupTable true &&
+ git repack -ad &&
+ GIT_TRACE2_EVENT="$(pwd)/trace" \
+ git multi-pack-index write --bitmap &&
+ grep "\"label\":\"writing_lookup_table\"" trace
+ )
+'
+
+test_expect_success 'preferred pack change with existing MIDX bitmap' '
+ git init preferred-pack-with-existing &&
+ (
+ cd preferred-pack-with-existing &&
+
+ test_commit base &&
+ test_commit other &&
+
+ git rev-list --objects --no-object-names base >p1.objects &&
+ git rev-list --objects --no-object-names other >p2.objects &&
+
+ p1="$(git pack-objects "$objdir/pack/pack" \
+ --delta-base-offset <p1.objects)" &&
+ p2="$(git pack-objects "$objdir/pack/pack" \
+ --delta-base-offset <p2.objects)" &&
+
+ # Generate a MIDX containing the first two packs,
+ # marking p1 as preferred, and ensure that it can be
+ # successfully cloned.
+ git multi-pack-index write --bitmap \
+ --preferred-pack="pack-$p1.pack" &&
+ test_path_is_file $midx &&
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
+ git clone --no-local . clone1 &&
+
+ # Then generate a new pack which sorts ahead of any
+ # existing pack (by tweaking the pack prefix).
+ test_commit foo &&
+ git pack-objects --all --unpacked $objdir/pack/pack0 &&
+
+ # Generate a new MIDX which changes the preferred pack
+ # to a pack contained in the existing MIDX.
+ git multi-pack-index write --bitmap \
+ --preferred-pack="pack-$p2.pack" &&
+ test_path_is_file $midx &&
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
+
+ # When the above circumstances are met, the preferred
+ # pack should change appropriately and clones should
+ # (still) succeed.
+ git clone --no-local . clone2
+ )
+'
+
+test_expect_success 'tagged commits are selected for bitmapping' '
+ rm -fr repo &&
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit --annotate base &&
+ git repack -d &&
+
+ # Remove refs/heads/main which points at the commit directly,
+ # leaving only a reference to the annotated tag.
+ git branch -M main &&
+ git checkout base &&
+ git branch -d main &&
+
+ git multi-pack-index write --bitmap &&
+
+ git rev-parse HEAD >want &&
+ test-tool bitmap list-commits >actual &&
+ grep $(cat want) actual
+ )
+'
+
+corrupt_file () {
+ chmod a+w "$1" &&
+ printf "bogus" | dd of="$1" bs=1 seek="12" conv=notrunc
+}
+
+test_expect_success 'git fsck correctly identifies good and bad bitmaps' '
+ git init valid &&
+ test_when_finished rm -rf valid &&
+
+ test_commit_bulk 20 &&
+ git repack -adbf &&
+
+ # Move pack-bitmap aside so it is not deleted
+ # in next repack.
+ packbitmap=$(ls .git/objects/pack/pack-*.bitmap) &&
+ mv "$packbitmap" "$packbitmap.bak" &&
+
+ test_commit_bulk 10 &&
+ git repack -b --write-midx &&
+ midxbitmap=$(ls .git/objects/pack/multi-pack-index-*.bitmap) &&
+
+ # Copy MIDX bitmap to backup. Copy pack bitmap from backup.
+ cp "$midxbitmap" "$midxbitmap.bak" &&
+ cp "$packbitmap.bak" "$packbitmap" &&
+
+ # fsck works at first
+ git fsck 2>err &&
+ test_must_be_empty err &&
+
+ corrupt_file "$packbitmap" &&
+ test_must_fail git fsck 2>err &&
+ grep "bitmap file '\''$packbitmap'\'' has invalid checksum" err &&
+
+ cp "$packbitmap.bak" "$packbitmap" &&
+ corrupt_file "$midxbitmap" &&
+ test_must_fail git fsck 2>err &&
+ grep "bitmap file '\''$midxbitmap'\'' has invalid checksum" err &&
+
+ corrupt_file "$packbitmap" &&
+ test_must_fail git fsck 2>err &&
+ grep "bitmap file '\''$midxbitmap'\'' has invalid checksum" err &&
+ grep "bitmap file '\''$packbitmap'\'' has invalid checksum" err
+'
+
+test_expect_success 'corrupt MIDX with bitmap causes fallback' '
+ git init corrupt-midx-bitmap &&
+ (
+ cd corrupt-midx-bitmap &&
+
+ test_commit first &&
+ git repack -d &&
+ test_commit second &&
+ git repack -d &&
+
+ git multi-pack-index write --bitmap &&
+ checksum=$(midx_checksum $objdir) &&
+ for f in $midx $midx-$checksum.bitmap
+ do
+ mv $f $f.bak || return 1
+ done &&
+
+ # pack everything together, invalidating the MIDX
+ git repack -ad &&
+ # then restore the now-stale MIDX
+ for f in $midx $midx-$checksum.bitmap
+ do
+ mv $f.bak $f || return 1
+ done &&
+
+ git rev-list --count --objects --use-bitmap-index HEAD >out 2>err &&
+ # should attempt opening the broken pack twice (once
+ # from the attempt to load it via the stale bitmap, and
+ # again when attempting to load it from the stale MIDX)
+ # before falling back to the non-MIDX case
+ test 2 -eq $(grep -c "could not open pack" err) &&
+ test 6 -eq $(cat out)
+ )
+'
+
+for allow_pack_reuse in single multi
+do
+ test_expect_success "reading MIDX without BTMP chunk does not complain with $allow_pack_reuse pack reuse" '
+ test_when_finished "rm -rf midx-without-btmp" &&
+ git init midx-without-btmp &&
+ (
+ cd midx-without-btmp &&
+ test_commit initial &&
+
+ git repack -Adbl --write-bitmap-index --write-midx &&
+ GIT_TEST_MIDX_READ_BTMP=false git -c pack.allowPackReuse=$allow_pack_reuse \
+ pack-objects --all --use-bitmap-index --stdout </dev/null >/dev/null 2>err &&
+ test_must_be_empty err
+ )
+ '
+done
+
+test_done
diff --git a/t/t5327-multi-pack-bitmaps-rev.sh b/t/t5327-multi-pack-bitmaps-rev.sh
new file mode 100755
index 0000000..e65e311
--- /dev/null
+++ b/t/t5327-multi-pack-bitmaps-rev.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+test_description='exercise basic multi-pack bitmap functionality (.rev files)'
+
+. ./test-lib.sh
+. "${TEST_DIRECTORY}/lib-bitmap.sh"
+
+# We'll be writing our own midx and bitmaps, so avoid getting confused by the
+# automatic ones.
+GIT_TEST_MULTI_PACK_INDEX=0
+GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0
+
+# Unlike t5326, this test exercise multi-pack bitmap functionality where the
+# object order is stored in a separate .rev file.
+GIT_TEST_MIDX_WRITE_REV=1
+GIT_TEST_MIDX_READ_RIDX=0
+export GIT_TEST_MIDX_WRITE_REV
+export GIT_TEST_MIDX_READ_RIDX
+
+test_midx_bitmap_rev () {
+ writeLookupTable=false
+
+ for i in "$@"
+ do
+ case $i in
+ "pack.writeBitmapLookupTable") writeLookupTable=true;;
+ esac
+ done
+
+ test_expect_success 'setup bitmap config' '
+ rm -rf * .git &&
+ git init &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"'
+ '
+
+ midx_bitmap_core rev
+ midx_bitmap_partial_tests rev
+}
+
+test_midx_bitmap_rev
+test_midx_bitmap_rev "pack.writeBitmapLookupTable"
+
+test_done
diff --git a/t/t5328-commit-graph-64bit-time.sh b/t/t5328-commit-graph-64bit-time.sh
new file mode 100755
index 0000000..fc6a242
--- /dev/null
+++ b/t/t5328-commit-graph-64bit-time.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+test_description='commit graph with 64-bit timestamps'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+if ! test_have_prereq TIME_IS_64BIT || ! test_have_prereq TIME_T_IS_64BIT
+then
+ skip_all='skipping 64-bit timestamp tests'
+ test_done
+fi
+
+. "$TEST_DIRECTORY"/lib-commit-graph.sh
+. "$TEST_DIRECTORY/lib-chunk.sh"
+
+UNIX_EPOCH_ZERO="@0 +0000"
+FUTURE_DATE="@4147483646 +0000"
+
+GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=0
+
+test_expect_success 'lower layers have overflow chunk' '
+ rm -f .git/objects/info/commit-graph &&
+ test_commit --date "$FUTURE_DATE" future-1 &&
+ test_commit --date "$UNIX_EPOCH_ZERO" old-1 &&
+ git commit-graph write --reachable &&
+ test_commit --date "$FUTURE_DATE" future-2 &&
+ test_commit --date "$UNIX_EPOCH_ZERO" old-2 &&
+ git commit-graph write --reachable --split=no-merge &&
+ test_commit extra &&
+ git commit-graph write --reachable --split=no-merge &&
+ git commit-graph write --reachable &&
+ graph_read_expect 5 "generation_data generation_data_overflow" &&
+ mv .git/objects/info/commit-graph commit-graph-upgraded &&
+ git commit-graph write --reachable &&
+ graph_read_expect 5 "generation_data generation_data_overflow" &&
+ test_cmp .git/objects/info/commit-graph commit-graph-upgraded
+'
+
+graph_git_behavior 'overflow' '' HEAD~2 HEAD
+
+test_expect_success 'set up and verify repo with generation data overflow chunk' '
+ git init repo &&
+ (
+ cd repo &&
+ test_commit --date "$UNIX_EPOCH_ZERO" 1 &&
+ test_commit 2 &&
+ test_commit --date "$UNIX_EPOCH_ZERO" 3 &&
+ git commit-graph write --reachable &&
+ graph_read_expect 3 generation_data &&
+ test_commit --date "$FUTURE_DATE" 4 &&
+ test_commit 5 &&
+ test_commit --date "$UNIX_EPOCH_ZERO" 6 &&
+ git branch left &&
+ git reset --hard 3 &&
+ test_commit 7 &&
+ test_commit --date "$FUTURE_DATE" 8 &&
+ test_commit 9 &&
+ git branch right &&
+ git reset --hard 3 &&
+ test_merge M left right &&
+ git commit-graph write --reachable &&
+ graph_read_expect 10 "generation_data generation_data_overflow" &&
+ git commit-graph verify
+ )
+'
+
+graph_git_behavior 'overflow 2' repo left right
+
+test_expect_success 'single commit with generation data exceeding UINT32_MAX' '
+ git init repo-uint32-max &&
+ test_commit -C repo-uint32-max --date "@4294967297 +0000" 1 &&
+ git -C repo-uint32-max commit-graph write --reachable &&
+ graph_read_expect -C repo-uint32-max 1 "generation_data" &&
+ git -C repo-uint32-max commit-graph verify
+'
+
+test_expect_success 'reader notices out-of-bounds generation overflow' '
+ graph=.git/objects/info/commit-graph &&
+ test_when_finished "rm -rf $graph" &&
+ git commit-graph write --reachable &&
+ corrupt_chunk_file $graph GDO2 clear &&
+ test_must_fail git log 2>err &&
+ grep "commit-graph overflow generation data is too small" err
+'
+
+test_done
diff --git a/t/t5329-pack-objects-cruft.sh b/t/t5329-pack-objects-cruft.sh
new file mode 100755
index 0000000..fc5fedb
--- /dev/null
+++ b/t/t5329-pack-objects-cruft.sh
@@ -0,0 +1,947 @@
+#!/bin/sh
+
+test_description='cruft pack related pack-objects tests'
+. ./test-lib.sh
+
+objdir=.git/objects
+packdir=$objdir/pack
+
+basic_cruft_pack_tests () {
+ expire="$1"
+
+ test_expect_success "unreachable loose objects are packed (expire $expire)" '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit base &&
+ git repack -Ad &&
+ test_commit loose &&
+
+ test-tool chmtime +2000 "$objdir/$(test_oid_to_path \
+ $(git rev-parse loose:loose.t))" &&
+ test-tool chmtime +1000 "$objdir/$(test_oid_to_path \
+ $(git rev-parse loose^{tree}))" &&
+
+ (
+ git rev-list --objects --no-object-names base..loose |
+ while read oid
+ do
+ path="$objdir/$(test_oid_to_path "$oid")" &&
+ printf "%s %d\n" "$oid" "$(test-tool chmtime --get "$path")" ||
+ echo "object list generation failed for $oid"
+ done |
+ sort -k1
+ ) >expect &&
+
+ keep="$(basename "$(ls $packdir/pack-*.pack)")" &&
+ cruft="$(echo $keep | git pack-objects --cruft \
+ --cruft-expiration="$expire" $packdir/pack)" &&
+ test-tool pack-mtimes "pack-$cruft.mtimes" >actual &&
+
+ test_cmp expect actual
+ )
+ '
+
+ test_expect_success "unreachable packed objects are packed (expire $expire)" '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit packed &&
+ git repack -Ad &&
+ test_commit other &&
+
+ git rev-list --objects --no-object-names packed.. >objects &&
+ keep="$(basename "$(ls $packdir/pack-*.pack)")" &&
+ other="$(git pack-objects --delta-base-offset \
+ $packdir/pack <objects)" &&
+ git prune-packed &&
+
+ test-tool chmtime --get -100 "$packdir/pack-$other.pack" >expect &&
+
+ cruft="$(git pack-objects --cruft --cruft-expiration="$expire" $packdir/pack <<-EOF
+ $keep
+ -pack-$other.pack
+ EOF
+ )" &&
+ test-tool pack-mtimes "pack-$cruft.mtimes" >actual.raw &&
+
+ cut -d" " -f2 <actual.raw | sort -u >actual &&
+
+ test_cmp expect actual
+ )
+ '
+
+ test_expect_success "unreachable cruft objects are repacked (expire $expire)" '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit packed &&
+ git repack -Ad &&
+ test_commit other &&
+
+ git rev-list --objects --no-object-names packed.. >objects &&
+ keep="$(basename "$(ls $packdir/pack-*.pack)")" &&
+
+ cruft_a="$(echo $keep | git pack-objects --cruft --cruft-expiration="$expire" $packdir/pack)" &&
+ git prune-packed &&
+ cruft_b="$(git pack-objects --cruft --cruft-expiration="$expire" $packdir/pack <<-EOF
+ $keep
+ -pack-$cruft_a.pack
+ EOF
+ )" &&
+
+ test-tool pack-mtimes "pack-$cruft_a.mtimes" >expect.raw &&
+ test-tool pack-mtimes "pack-$cruft_b.mtimes" >actual.raw &&
+
+ sort <expect.raw >expect &&
+ sort <actual.raw >actual &&
+
+ test_cmp expect actual
+ )
+ '
+
+ test_expect_success "multiple cruft packs (expire $expire)" '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit reachable &&
+ git repack -Ad &&
+ keep="$(basename "$(ls $packdir/pack-*.pack)")" &&
+
+ test_commit cruft &&
+ loose="$objdir/$(test_oid_to_path $(git rev-parse cruft))" &&
+
+ # generate three copies of the cruft object in different
+ # cruft packs, each with a unique mtime:
+ # - one expired (1000 seconds ago)
+ # - two non-expired (one 1000 seconds in the future,
+ # one 1500 seconds in the future)
+ test-tool chmtime =-1000 "$loose" &&
+ git pack-objects --cruft $packdir/pack-A <<-EOF &&
+ $keep
+ EOF
+ test-tool chmtime =+1000 "$loose" &&
+ git pack-objects --cruft $packdir/pack-B <<-EOF &&
+ $keep
+ -$(basename $(ls $packdir/pack-A-*.pack))
+ EOF
+ test-tool chmtime =+1500 "$loose" &&
+ git pack-objects --cruft $packdir/pack-C <<-EOF &&
+ $keep
+ -$(basename $(ls $packdir/pack-A-*.pack))
+ -$(basename $(ls $packdir/pack-B-*.pack))
+ EOF
+
+ # ensure the resulting cruft pack takes the most recent
+ # mtime among all copies
+ cruft="$(git pack-objects --cruft \
+ --cruft-expiration="$expire" \
+ $packdir/pack <<-EOF
+ $keep
+ -$(basename $(ls $packdir/pack-A-*.pack))
+ -$(basename $(ls $packdir/pack-B-*.pack))
+ -$(basename $(ls $packdir/pack-C-*.pack))
+ EOF
+ )" &&
+
+ test-tool pack-mtimes "$(basename $(ls $packdir/pack-C-*.mtimes))" >expect.raw &&
+ test-tool pack-mtimes "pack-$cruft.mtimes" >actual.raw &&
+
+ sort expect.raw >expect &&
+ sort actual.raw >actual &&
+ test_cmp expect actual
+ )
+ '
+
+ test_expect_success "cruft packs tolerate missing trees (expire $expire)" '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit reachable &&
+ test_commit cruft &&
+
+ tree="$(git rev-parse cruft^{tree})" &&
+
+ git reset --hard reachable &&
+ git tag -d cruft &&
+ git reflog expire --all --expire=all &&
+
+ # remove the unreachable tree, but leave the commit
+ # which has it as its root tree intact
+ rm -fr "$objdir/$(test_oid_to_path "$tree")" &&
+
+ git repack -Ad &&
+ basename $(ls $packdir/pack-*.pack) >in &&
+ git pack-objects --cruft --cruft-expiration="$expire" \
+ $packdir/pack <in
+ )
+ '
+
+ test_expect_success "cruft packs tolerate missing blobs (expire $expire)" '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit reachable &&
+ test_commit cruft &&
+
+ blob="$(git rev-parse cruft:cruft.t)" &&
+
+ git reset --hard reachable &&
+ git tag -d cruft &&
+ git reflog expire --all --expire=all &&
+
+ # remove the unreachable blob, but leave the commit (and
+ # the root tree of that commit) intact
+ rm -fr "$objdir/$(test_oid_to_path "$blob")" &&
+
+ git repack -Ad &&
+ basename $(ls $packdir/pack-*.pack) >in &&
+ git pack-objects --cruft --cruft-expiration="$expire" \
+ $packdir/pack <in
+ )
+ '
+}
+
+basic_cruft_pack_tests never
+basic_cruft_pack_tests 2.weeks.ago
+
+test_expect_success 'cruft tags rescue tagged objects' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit packed &&
+ git repack -Ad &&
+
+ test_commit tagged &&
+ git tag -a annotated -m tag &&
+
+ git rev-list --objects --no-object-names packed.. >objects &&
+ while read oid
+ do
+ test-tool chmtime -1000 \
+ "$objdir/$(test_oid_to_path $oid)" || exit 1
+ done <objects &&
+
+ test-tool chmtime -500 \
+ "$objdir/$(test_oid_to_path $(git rev-parse annotated))" &&
+
+ keep="$(basename "$(ls $packdir/pack-*.pack)")" &&
+ cruft="$(echo $keep | git pack-objects --cruft \
+ --cruft-expiration=750.seconds.ago \
+ $packdir/pack)" &&
+ test-tool pack-mtimes "pack-$cruft.mtimes" >actual.raw &&
+ cut -f1 -d" " <actual.raw | sort >actual &&
+
+ (
+ cat objects &&
+ git rev-parse annotated
+ ) >expect.raw &&
+ sort <expect.raw >expect &&
+
+ test_cmp expect actual &&
+ cat actual
+ )
+'
+
+test_expect_success 'cruft commits rescue parents, trees' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit packed &&
+ git repack -Ad &&
+
+ test_commit old &&
+ test_commit new &&
+
+ git rev-list --objects --no-object-names packed..new >objects &&
+ while read object
+ do
+ test-tool chmtime -1000 \
+ "$objdir/$(test_oid_to_path $object)" || exit 1
+ done <objects &&
+ test-tool chmtime +500 "$objdir/$(test_oid_to_path \
+ $(git rev-parse HEAD))" &&
+
+ keep="$(basename "$(ls $packdir/pack-*.pack)")" &&
+ cruft="$(echo $keep | git pack-objects --cruft \
+ --cruft-expiration=750.seconds.ago \
+ $packdir/pack)" &&
+ test-tool pack-mtimes "pack-$cruft.mtimes" >actual.raw &&
+
+ cut -d" " -f1 <actual.raw | sort >actual &&
+ sort <objects >expect &&
+
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'cruft trees rescue sub-trees, blobs' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit packed &&
+ git repack -Ad &&
+
+ mkdir -p dir/sub &&
+ echo foo >foo &&
+ echo bar >dir/bar &&
+ echo baz >dir/sub/baz &&
+
+ test_tick &&
+ git add . &&
+ git commit -m "pruned" &&
+
+ test-tool chmtime -1000 "$objdir/$(test_oid_to_path $(git rev-parse HEAD))" &&
+ test-tool chmtime -1000 "$objdir/$(test_oid_to_path $(git rev-parse HEAD^{tree}))" &&
+ test-tool chmtime -1000 "$objdir/$(test_oid_to_path $(git rev-parse HEAD:foo))" &&
+ test-tool chmtime -500 "$objdir/$(test_oid_to_path $(git rev-parse HEAD:dir))" &&
+ test-tool chmtime -1000 "$objdir/$(test_oid_to_path $(git rev-parse HEAD:dir/bar))" &&
+ test-tool chmtime -1000 "$objdir/$(test_oid_to_path $(git rev-parse HEAD:dir/sub))" &&
+ test-tool chmtime -1000 "$objdir/$(test_oid_to_path $(git rev-parse HEAD:dir/sub/baz))" &&
+
+ keep="$(basename "$(ls $packdir/pack-*.pack)")" &&
+ cruft="$(echo $keep | git pack-objects --cruft \
+ --cruft-expiration=750.seconds.ago \
+ $packdir/pack)" &&
+ test-tool pack-mtimes "pack-$cruft.mtimes" >actual.raw &&
+ cut -f1 -d" " <actual.raw | sort >actual &&
+
+ git rev-parse HEAD:dir HEAD:dir/bar HEAD:dir/sub HEAD:dir/sub/baz >expect.raw &&
+ sort <expect.raw >expect &&
+
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'expired objects are pruned' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit packed &&
+ git repack -Ad &&
+
+ test_commit pruned &&
+
+ git rev-list --objects --no-object-names packed..pruned >objects &&
+ while read object
+ do
+ test-tool chmtime -1000 \
+ "$objdir/$(test_oid_to_path $object)" || exit 1
+ done <objects &&
+
+ keep="$(basename "$(ls $packdir/pack-*.pack)")" &&
+ cruft="$(echo $keep | git pack-objects --cruft \
+ --cruft-expiration=750.seconds.ago \
+ $packdir/pack)" &&
+
+ test-tool pack-mtimes "pack-$cruft.mtimes" >actual &&
+ test_must_be_empty actual
+ )
+'
+
+test_expect_success 'repack --cruft generates a cruft pack' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit reachable &&
+ git branch -M main &&
+ git checkout --orphan other &&
+ test_commit unreachable &&
+
+ git checkout main &&
+ git branch -D other &&
+ git tag -d unreachable &&
+ # objects are not cruft if they are contained in the reflogs
+ git reflog expire --all --expire=all &&
+
+ git rev-list --objects --all --no-object-names >reachable.raw &&
+ git cat-file --batch-all-objects --batch-check="%(objectname)" >objects &&
+ sort <reachable.raw >reachable &&
+ comm -13 reachable objects >unreachable &&
+
+ git repack --cruft -d &&
+
+ cruft=$(basename $(ls $packdir/pack-*.mtimes) .mtimes) &&
+ pack=$(basename $(ls $packdir/pack-*.pack | grep -v $cruft) .pack) &&
+
+ git show-index <$packdir/$pack.idx >actual.raw &&
+ cut -f2 -d" " actual.raw | sort >actual &&
+ test_cmp reachable actual &&
+
+ git show-index <$packdir/$cruft.idx >actual.raw &&
+ cut -f2 -d" " actual.raw | sort >actual &&
+ test_cmp unreachable actual
+ )
+'
+
+test_expect_success 'loose objects mtimes upsert others' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit reachable &&
+ git repack -Ad &&
+ git branch -M main &&
+
+ git checkout --orphan other &&
+ test_commit cruft &&
+ # incremental repack, leaving existing objects loose (so
+ # they can be "freshened")
+ git repack &&
+
+ tip="$(git rev-parse cruft)" &&
+ path="$objdir/$(test_oid_to_path "$tip")" &&
+ test-tool chmtime --get +1000 "$path" >expect &&
+
+ git checkout main &&
+ git branch -D other &&
+ git tag -d cruft &&
+ git reflog expire --all --expire=all &&
+
+ git repack --cruft -d &&
+
+ mtimes="$(basename $(ls $packdir/pack-*.mtimes))" &&
+ test-tool pack-mtimes "$mtimes" >actual.raw &&
+ grep "$tip" actual.raw | cut -d" " -f2 >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'expiring cruft objects with git gc' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit reachable &&
+ git branch -M main &&
+ git checkout --orphan other &&
+ test_commit unreachable &&
+
+ git checkout main &&
+ git branch -D other &&
+ git tag -d unreachable &&
+ # objects are not cruft if they are contained in the reflogs
+ git reflog expire --all --expire=all &&
+
+ git rev-list --objects --all --no-object-names >reachable.raw &&
+ git cat-file --batch-all-objects --batch-check="%(objectname)" >objects &&
+ sort <reachable.raw >reachable &&
+ comm -13 reachable objects >unreachable &&
+
+ # Write a cruft pack containing all unreachable objects.
+ git gc --cruft --prune="01-01-1980" &&
+
+ mtimes=$(ls .git/objects/pack/pack-*.mtimes) &&
+ test_path_is_file $mtimes &&
+
+ # Prune all unreachable objects from the cruft pack.
+ git gc --cruft --prune=now &&
+
+ git cat-file --batch-all-objects --batch-check="%(objectname)" >objects &&
+
+ comm -23 unreachable objects >removed &&
+ test_cmp unreachable removed &&
+ test_path_is_missing $mtimes
+ )
+'
+
+test_expect_success 'cruft packs are not included in geometric repack' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit reachable &&
+ git repack -Ad &&
+ git branch -M main &&
+
+ git checkout --orphan other &&
+ test_commit cruft &&
+ git repack -d &&
+
+ git checkout main &&
+ git branch -D other &&
+ git tag -d cruft &&
+ git reflog expire --all --expire=all &&
+
+ git repack --cruft &&
+
+ find $packdir -type f | sort >before &&
+ git repack --geometric=2 -d &&
+ find $packdir -type f | sort >after &&
+
+ test_cmp before after
+ )
+'
+
+test_expect_success 'repack --geometric collects once-cruft objects' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit reachable &&
+ git repack -Ad &&
+ git branch -M main &&
+
+ git checkout --orphan other &&
+ git rm -rf . &&
+ test_commit --no-tag cruft &&
+ cruft="$(git rev-parse HEAD)" &&
+
+ git checkout main &&
+ git branch -D other &&
+ git reflog expire --all --expire=all &&
+
+ # Pack the objects created in the previous step into a cruft
+ # pack. Intentionally leave loose copies of those objects
+ # around so we can pick them up in a subsequent --geometric
+ # reapack.
+ git repack --cruft &&
+
+ # Now make those objects reachable, and ensure that they are
+ # packed into the new pack created via a --geometric repack.
+ git update-ref refs/heads/other $cruft &&
+
+ # Without this object, the set of unpacked objects is exactly
+ # the set of objects already in the cruft pack. Tweak that set
+ # to ensure we do not overwrite the cruft pack entirely.
+ test_commit reachable2 &&
+
+ find $packdir -name "pack-*.idx" | sort >before &&
+ git repack --geometric=2 -d &&
+ find $packdir -name "pack-*.idx" | sort >after &&
+
+ {
+ git rev-list --objects --no-object-names $cruft &&
+ git rev-list --objects --no-object-names reachable..reachable2
+ } >want.raw &&
+ sort want.raw >want &&
+
+ pack=$(comm -13 before after) &&
+ git show-index <$pack >objects.raw &&
+
+ cut -d" " -f2 objects.raw | sort >got &&
+
+ test_cmp want got
+ )
+'
+
+test_expect_success 'cruft repack with no reachable objects' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit base &&
+ git repack -ad &&
+
+ base="$(git rev-parse base)" &&
+
+ git for-each-ref --format="delete %(refname)" >in &&
+ git update-ref --stdin <in &&
+ git reflog expire --all --expire=all &&
+ rm -fr .git/index &&
+
+ git repack --cruft -d &&
+
+ git cat-file -t $base
+ )
+'
+
+write_blob () {
+ test-tool genrandom "$@" >in &&
+ git hash-object -w -t blob in
+}
+
+find_pack () {
+ for idx in $(ls $packdir/pack-*.idx)
+ do
+ git show-index <$idx >out &&
+ if grep -q "$1" out
+ then
+ echo $idx
+ fi || return 1
+ done
+}
+
+test_expect_success 'cruft repack with --max-pack-size' '
+ git init max-pack-size &&
+ (
+ cd max-pack-size &&
+ test_commit base &&
+
+ # two cruft objects which exceed the maximum pack size
+ foo=$(write_blob foo 1048576) &&
+ bar=$(write_blob bar 1048576) &&
+ test-tool chmtime --get -1000 \
+ "$objdir/$(test_oid_to_path $foo)" >foo.mtime &&
+ test-tool chmtime --get -2000 \
+ "$objdir/$(test_oid_to_path $bar)" >bar.mtime &&
+ git repack --cruft --max-pack-size=1M &&
+ find $packdir -name "*.mtimes" >cruft &&
+ test_line_count = 2 cruft &&
+
+ foo_mtimes="$(basename $(find_pack $foo) .idx).mtimes" &&
+ bar_mtimes="$(basename $(find_pack $bar) .idx).mtimes" &&
+ test-tool pack-mtimes $foo_mtimes >foo.actual &&
+ test-tool pack-mtimes $bar_mtimes >bar.actual &&
+
+ echo "$foo $(cat foo.mtime)" >foo.expect &&
+ echo "$bar $(cat bar.mtime)" >bar.expect &&
+
+ test_cmp foo.expect foo.actual &&
+ test_cmp bar.expect bar.actual &&
+ test "$foo_mtimes" != "$bar_mtimes"
+ )
+'
+
+test_expect_success 'cruft repack with pack.packSizeLimit' '
+ (
+ cd max-pack-size &&
+ # repack everything back together to remove the existing cruft
+ # pack (but to keep its objects)
+ git repack -adk &&
+ git -c pack.packSizeLimit=1M repack --cruft &&
+ # ensure the same post condition is met when --max-pack-size
+ # would otherwise be inferred from the configuration
+ find $packdir -name "*.mtimes" >cruft &&
+ test_line_count = 2 cruft &&
+ for pack in $(cat cruft)
+ do
+ test-tool pack-mtimes "$(basename $pack)" >objects &&
+ test_line_count = 1 objects || return 1
+ done
+ )
+'
+
+test_expect_success 'cruft repack respects repack.cruftWindow' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit base &&
+
+ GIT_TRACE2_EVENT=$(pwd)/event.trace \
+ git -c pack.window=1 -c repack.cruftWindow=2 repack \
+ --cruft --window=3 &&
+
+ grep "pack-objects.*--window=2.*--cruft" event.trace
+ )
+'
+
+test_expect_success 'cruft repack respects --window by default' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit base &&
+
+ GIT_TRACE2_EVENT=$(pwd)/event.trace \
+ git -c pack.window=2 repack --cruft --window=3 &&
+
+ grep "pack-objects.*--window=3.*--cruft" event.trace
+ )
+'
+
+test_expect_success 'cruft repack respects --quiet' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit base &&
+ GIT_PROGRESS_DELAY=0 git repack --cruft --quiet 2>err &&
+ test_must_be_empty err
+ )
+'
+
+test_expect_success 'cruft --local drops unreachable objects' '
+ git init alternate &&
+ git init repo &&
+ test_when_finished "rm -fr alternate repo" &&
+
+ test_commit -C alternate base &&
+ # Pack all objects in alterate so that the cruft repack in "repo" sees
+ # the object it dropped due to `--local` as packed. Otherwise this
+ # object would not appear packed anywhere (since it is not packed in
+ # alternate and likewise not part of the cruft pack in the other repo
+ # because of `--local`).
+ git -C alternate repack -ad &&
+
+ (
+ cd repo &&
+
+ object="$(git -C ../alternate rev-parse HEAD:base.t)" &&
+ git -C ../alternate cat-file -p $object >contents &&
+
+ # Write some reachable objects and two unreachable ones: one
+ # that the alternate has and another that is unique.
+ test_commit other &&
+ git hash-object -w -t blob contents &&
+ cruft="$(echo cruft | git hash-object -w -t blob --stdin)" &&
+
+ ( cd ../alternate/.git/objects && pwd ) \
+ >.git/objects/info/alternates &&
+
+ test_path_is_file $objdir/$(test_oid_to_path $cruft) &&
+ test_path_is_file $objdir/$(test_oid_to_path $object) &&
+
+ git repack -d --cruft --local &&
+
+ test-tool pack-mtimes "$(basename $(ls $packdir/pack-*.mtimes))" \
+ >objects &&
+ ! grep $object objects &&
+ grep $cruft objects
+ )
+'
+
+test_expect_success 'MIDX bitmaps tolerate reachable cruft objects' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit reachable &&
+ test_commit cruft &&
+ unreachable="$(git rev-parse cruft)" &&
+
+ git reset --hard $unreachable^ &&
+ git tag -d cruft &&
+ git reflog expire --all --expire=all &&
+
+ git repack --cruft -d &&
+
+ # resurrect the unreachable object via a new commit. the
+ # new commit will get selected for a bitmap, but be
+ # missing one of its parents from the selected packs.
+ git reset --hard $unreachable &&
+ test_commit resurrect &&
+
+ git repack --write-midx --write-bitmap-index --geometric=2 -d
+ )
+'
+
+test_expect_success 'cruft objects are freshend via loose' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ echo "cruft" >contents &&
+ blob="$(git hash-object -w -t blob contents)" &&
+ loose="$objdir/$(test_oid_to_path $blob)" &&
+
+ test_commit base &&
+
+ git repack --cruft -d &&
+
+ test_path_is_missing "$loose" &&
+ test-tool pack-mtimes "$(basename "$(ls $packdir/pack-*.mtimes)")" >cruft &&
+ grep "$blob" cruft &&
+
+ # write the same object again
+ git hash-object -w -t blob contents &&
+
+ test_path_is_file "$loose"
+ )
+'
+
+test_expect_success 'gc.recentObjectsHook' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ # Create a handful of objects.
+ #
+ # - one reachable commit, "base", designated for the reachable
+ # pack
+ # - one unreachable commit, "cruft.discard", which is marked
+ # for deletion
+ # - one unreachable commit, "cruft.old", which would be marked
+ # for deletion, but is rescued as an extra cruft tip
+ # - one unreachable commit, "cruft.new", which is not marked
+ # for deletion
+ test_commit base &&
+ git branch -M main &&
+
+ git checkout --orphan discard &&
+ git rm -fr . &&
+ test_commit --no-tag cruft.discard &&
+
+ git checkout --orphan old &&
+ git rm -fr . &&
+ test_commit --no-tag cruft.old &&
+ cruft_old="$(git rev-parse HEAD)" &&
+
+ git checkout --orphan new &&
+ git rm -fr . &&
+ test_commit --no-tag cruft.new &&
+ cruft_new="$(git rev-parse HEAD)" &&
+
+ git checkout main &&
+ git branch -D discard old new &&
+ git reflog expire --all --expire=all &&
+
+ # mark cruft.old with an mtime that is many minutes
+ # older than the expiration period, and mark cruft.new
+ # with an mtime that is in the future (and thus not
+ # eligible for pruning).
+ test-tool chmtime -2000 "$objdir/$(test_oid_to_path $cruft_old)" &&
+ test-tool chmtime +1000 "$objdir/$(test_oid_to_path $cruft_new)" &&
+
+ # Write the list of cruft objects we expect to
+ # accumulate, which is comprised of everything reachable
+ # from cruft.old and cruft.new, but not cruft.discard.
+ git rev-list --objects --no-object-names \
+ $cruft_old $cruft_new >cruft.raw &&
+ sort cruft.raw >cruft.expect &&
+
+ # Write the script to list extra tips, which are limited
+ # to cruft.old, in this case.
+ write_script extra-tips <<-EOF &&
+ echo $cruft_old
+ EOF
+ git config gc.recentObjectsHook ./extra-tips &&
+
+ git repack --cruft --cruft-expiration=now -d &&
+
+ mtimes="$(ls .git/objects/pack/pack-*.mtimes)" &&
+ git show-index <${mtimes%.mtimes}.idx >cruft &&
+ cut -d" " -f2 cruft | sort >cruft.actual &&
+ test_cmp cruft.expect cruft.actual &&
+
+ # Ensure that the "old" objects are removed after
+ # dropping the gc.recentObjectsHook hook.
+ git config --unset gc.recentObjectsHook &&
+ git repack --cruft --cruft-expiration=now -d &&
+
+ mtimes="$(ls .git/objects/pack/pack-*.mtimes)" &&
+ git show-index <${mtimes%.mtimes}.idx >cruft &&
+ cut -d" " -f2 cruft | sort >cruft.actual &&
+
+ git rev-list --objects --no-object-names $cruft_new >cruft.raw &&
+ cp cruft.expect cruft.old &&
+ sort cruft.raw >cruft.expect &&
+ test_cmp cruft.expect cruft.actual &&
+
+ # ensure objects which are no longer in the cruft pack were
+ # removed from the repository
+ for object in $(comm -13 cruft.expect cruft.old)
+ do
+ test_must_fail git cat-file -t $object || return 1
+ done
+ )
+'
+
+test_expect_success 'multi-valued gc.recentObjectsHook' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit base &&
+ git branch -M main &&
+
+ git checkout --orphan cruft.a &&
+ git rm -fr . &&
+ test_commit --no-tag cruft.a &&
+ cruft_a="$(git rev-parse HEAD)" &&
+
+ git checkout --orphan cruft.b &&
+ git rm -fr . &&
+ test_commit --no-tag cruft.b &&
+ cruft_b="$(git rev-parse HEAD)" &&
+
+ git checkout main &&
+ git branch -D cruft.a cruft.b &&
+ git reflog expire --all --expire=all &&
+
+ echo "echo $cruft_a" | write_script extra-tips.a &&
+ echo "echo $cruft_b" | write_script extra-tips.b &&
+ echo "false" | write_script extra-tips.c &&
+
+ git rev-list --objects --no-object-names $cruft_a $cruft_b \
+ >cruft.raw &&
+ sort cruft.raw >cruft.expect &&
+
+ # ensure that each extra cruft tip is saved by its
+ # respective hook
+ git config --add gc.recentObjectsHook ./extra-tips.a &&
+ git config --add gc.recentObjectsHook ./extra-tips.b &&
+ git repack --cruft --cruft-expiration=now -d &&
+
+ mtimes="$(ls .git/objects/pack/pack-*.mtimes)" &&
+ git show-index <${mtimes%.mtimes}.idx >cruft &&
+ cut -d" " -f2 cruft | sort >cruft.actual &&
+ test_cmp cruft.expect cruft.actual &&
+
+ # ensure that a dirty exit halts cruft pack generation
+ git config --add gc.recentObjectsHook ./extra-tips.c &&
+ test_must_fail git repack --cruft --cruft-expiration=now -d 2>err &&
+ grep "unable to enumerate additional recent objects" err &&
+
+ # and that the existing cruft pack is left alone
+ test_path_is_file "$mtimes"
+ )
+'
+
+test_expect_success 'additional cruft blobs via gc.recentObjectsHook' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit base &&
+
+ blob=$(echo "unreachable" | git hash-object -w --stdin) &&
+
+ # mark the unreachable blob we wrote above as having
+ # aged out of the retention period
+ test-tool chmtime -2000 "$objdir/$(test_oid_to_path $blob)" &&
+
+ # Write the script to list extra tips, which is just the
+ # extra blob as above.
+ write_script extra-tips <<-EOF &&
+ echo $blob
+ EOF
+ git config gc.recentObjectsHook ./extra-tips &&
+
+ git repack --cruft --cruft-expiration=now -d &&
+
+ mtimes="$(ls .git/objects/pack/pack-*.mtimes)" &&
+ git show-index <${mtimes%.mtimes}.idx >cruft &&
+ cut -d" " -f2 cruft >actual &&
+ echo $blob >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_done
diff --git a/t/t5330-no-lazy-fetch-with-commit-graph.sh b/t/t5330-no-lazy-fetch-with-commit-graph.sh
new file mode 100755
index 0000000..5eb28f0
--- /dev/null
+++ b/t/t5330-no-lazy-fetch-with-commit-graph.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+test_description='test for no lazy fetch with the commit-graph'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'setup: prepare a repository with a commit' '
+ git init with-commit &&
+ test_commit -C with-commit the-commit &&
+ oid=$(git -C with-commit rev-parse HEAD)
+'
+
+test_expect_success 'setup: prepare a repository with commit-graph contains the commit' '
+ git init with-commit-graph &&
+ echo "$(pwd)/with-commit/.git/objects" \
+ >with-commit-graph/.git/objects/info/alternates &&
+ # create a ref that points to the commit in alternates
+ git -C with-commit-graph update-ref refs/ref_to_the_commit "$oid" &&
+ # prepare some other objects to commit-graph
+ test_commit -C with-commit-graph something &&
+ git -c gc.writeCommitGraph=true -C with-commit-graph gc &&
+ test_path_is_file with-commit-graph/.git/objects/info/commit-graph
+'
+
+test_expect_success 'setup: change the alternates to what without the commit' '
+ git init --bare without-commit &&
+ git -C with-commit-graph cat-file -e $oid &&
+ echo "$(pwd)/without-commit/objects" \
+ >with-commit-graph/.git/objects/info/alternates &&
+ test_must_fail git -C with-commit-graph cat-file -e $oid
+'
+
+test_expect_success 'fetch any commit from promisor with the usage of the commit graph' '
+ # setup promisor and prepare any commit to fetch
+ git -C with-commit-graph remote add origin "$(pwd)/with-commit" &&
+ git -C with-commit-graph config remote.origin.promisor true &&
+ git -C with-commit-graph config remote.origin.partialclonefilter blob:none &&
+ test_commit -C with-commit any-commit &&
+ anycommit=$(git -C with-commit rev-parse HEAD) &&
+ GIT_TRACE="$(pwd)/trace.txt" \
+ git -C with-commit-graph fetch origin $anycommit 2>err &&
+ ! grep "fatal: promisor-remote: unable to fork off fetch subprocess" err &&
+ grep "git fetch origin" trace.txt >actual &&
+ test_line_count = 1 actual
+'
+
+test_done
diff --git a/t/t5331-pack-objects-stdin.sh b/t/t5331-pack-objects-stdin.sh
new file mode 100755
index 0000000..2dcf1ee
--- /dev/null
+++ b/t/t5331-pack-objects-stdin.sh
@@ -0,0 +1,240 @@
+#!/bin/sh
+
+test_description='pack-objects --stdin'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+packed_objects () {
+ git show-index <"$1" >tmp-object-list &&
+ cut -d' ' -f2 tmp-object-list | sort &&
+ rm tmp-object-list
+ }
+
+test_expect_success 'setup for --stdin-packs tests' '
+ git init stdin-packs &&
+ (
+ cd stdin-packs &&
+
+ test_commit A &&
+ test_commit B &&
+ test_commit C &&
+
+ for id in A B C
+ do
+ git pack-objects .git/objects/pack/pack-$id \
+ --incremental --revs <<-EOF || exit 1
+ refs/tags/$id
+ EOF
+ done &&
+
+ ls -la .git/objects/pack
+ )
+'
+
+test_expect_success '--stdin-packs with excluded packs' '
+ (
+ cd stdin-packs &&
+
+ PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" &&
+ PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" &&
+ PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" &&
+
+ git pack-objects test --stdin-packs <<-EOF &&
+ $PACK_A
+ ^$PACK_B
+ $PACK_C
+ EOF
+
+ (
+ git show-index <$(ls .git/objects/pack/pack-A-*.idx) &&
+ git show-index <$(ls .git/objects/pack/pack-C-*.idx)
+ ) >expect.raw &&
+ git show-index <$(ls test-*.idx) >actual.raw &&
+
+ cut -d" " -f2 <expect.raw | sort >expect &&
+ cut -d" " -f2 <actual.raw | sort >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success '--stdin-packs is incompatible with --filter' '
+ (
+ cd stdin-packs &&
+ test_must_fail git pack-objects --stdin-packs --stdout \
+ --filter=blob:none </dev/null 2>err &&
+ test_grep "cannot use --filter with --stdin-packs" err
+ )
+'
+
+test_expect_success '--stdin-packs is incompatible with --revs' '
+ (
+ cd stdin-packs &&
+ test_must_fail git pack-objects --stdin-packs --revs out \
+ </dev/null 2>err &&
+ test_grep "cannot use internal rev list with --stdin-packs" err
+ )
+'
+
+test_expect_success '--stdin-packs with loose objects' '
+ (
+ cd stdin-packs &&
+
+ PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" &&
+ PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" &&
+ PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" &&
+
+ test_commit D && # loose
+
+ git pack-objects test2 --stdin-packs --unpacked <<-EOF &&
+ $PACK_A
+ ^$PACK_B
+ $PACK_C
+ EOF
+
+ (
+ git show-index <$(ls .git/objects/pack/pack-A-*.idx) &&
+ git show-index <$(ls .git/objects/pack/pack-C-*.idx) &&
+ git rev-list --objects --no-object-names \
+ refs/tags/C..refs/tags/D
+
+ ) >expect.raw &&
+ ls -la . &&
+ git show-index <$(ls test2-*.idx) >actual.raw &&
+
+ cut -d" " -f2 <expect.raw | sort >expect &&
+ cut -d" " -f2 <actual.raw | sort >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success '--stdin-packs with broken links' '
+ (
+ cd stdin-packs &&
+
+ # make an unreachable object with a bogus parent
+ git cat-file -p HEAD >commit &&
+ sed "s/$(git rev-parse HEAD^)/$(test_oid zero)/" <commit |
+ git hash-object -w -t commit --stdin >in &&
+
+ git pack-objects .git/objects/pack/pack-D <in &&
+
+ PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" &&
+ PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" &&
+ PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" &&
+ PACK_D="$(basename .git/objects/pack/pack-D-*.pack)" &&
+
+ git pack-objects test3 --stdin-packs --unpacked <<-EOF &&
+ $PACK_A
+ ^$PACK_B
+ $PACK_C
+ $PACK_D
+ EOF
+
+ (
+ git show-index <$(ls .git/objects/pack/pack-A-*.idx) &&
+ git show-index <$(ls .git/objects/pack/pack-C-*.idx) &&
+ git show-index <$(ls .git/objects/pack/pack-D-*.idx) &&
+ git rev-list --objects --no-object-names \
+ refs/tags/C..refs/tags/D
+ ) >expect.raw &&
+ git show-index <$(ls test3-*.idx) >actual.raw &&
+
+ cut -d" " -f2 <expect.raw | sort >expect &&
+ cut -d" " -f2 <actual.raw | sort >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'pack-objects --stdin with duplicate packfile' '
+ test_when_finished "rm -fr repo" &&
+
+ git init repo &&
+ (
+ cd repo &&
+ test_commit "commit" &&
+ git repack -ad &&
+
+ {
+ basename .git/objects/pack/pack-*.pack &&
+ basename .git/objects/pack/pack-*.pack
+ } >packfiles &&
+
+ git pack-objects --stdin-packs generated-pack <packfiles &&
+ packed_objects .git/objects/pack/pack-*.idx >expect &&
+ packed_objects generated-pack-*.idx >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'pack-objects --stdin with same packfile excluded and included' '
+ test_when_finished "rm -fr repo" &&
+
+ git init repo &&
+ (
+ cd repo &&
+ test_commit "commit" &&
+ git repack -ad &&
+
+ {
+ basename .git/objects/pack/pack-*.pack &&
+ printf "^%s\n" "$(basename .git/objects/pack/pack-*.pack)"
+ } >packfiles &&
+
+ git pack-objects --stdin-packs generated-pack <packfiles &&
+ packed_objects generated-pack-*.idx >packed-objects &&
+ test_must_be_empty packed-objects
+ )
+'
+
+test_expect_success 'pack-objects --stdin with packfiles from alternate object database' '
+ test_when_finished "rm -fr shared member" &&
+
+ # Set up a shared repository with a single packfile.
+ git init shared &&
+ test_commit -C shared "shared-objects" &&
+ git -C shared repack -ad &&
+ basename shared/.git/objects/pack/pack-*.pack >packfile &&
+
+ # Set up a repository that is connected to the shared repository. This
+ # repository has no objects on its own, but we still expect to be able
+ # to pack objects from its alternate.
+ git clone --shared shared member &&
+ git -C member pack-objects --stdin-packs generated-pack <packfile &&
+ test_cmp shared/.git/objects/pack/pack-*.pack member/generated-pack-*.pack
+'
+
+test_expect_success 'pack-objects --stdin with packfiles from main and alternate object database' '
+ test_when_finished "rm -fr shared member" &&
+
+ # Set up a shared repository with a single packfile.
+ git init shared &&
+ test_commit -C shared "shared-commit" &&
+ git -C shared repack -ad &&
+
+ # Set up a repository that is connected to the shared repository. This
+ # repository has a second packfile so that we can verify that it is
+ # possible to write packs that include packfiles from different object
+ # databases.
+ git clone --shared shared member &&
+ test_commit -C member "local-commit" &&
+ git -C member repack -dl &&
+
+ {
+ basename shared/.git/objects/pack/pack-*.pack &&
+ basename member/.git/objects/pack/pack-*.pack
+ } >packfiles &&
+
+ {
+ packed_objects shared/.git/objects/pack/pack-*.idx &&
+ packed_objects member/.git/objects/pack/pack-*.idx
+ } | sort >expected-objects &&
+
+ git -C member pack-objects --stdin-packs generated-pack <packfiles &&
+ packed_objects member/generated-pack-*.idx >actual-objects &&
+ test_cmp expected-objects actual-objects
+'
+
+test_done
diff --git a/t/t5332-multi-pack-reuse.sh b/t/t5332-multi-pack-reuse.sh
new file mode 100755
index 0000000..3c20738
--- /dev/null
+++ b/t/t5332-multi-pack-reuse.sh
@@ -0,0 +1,207 @@
+#!/bin/sh
+
+test_description='pack-objects multi-pack reuse'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-bitmap.sh
+
+objdir=.git/objects
+packdir=$objdir/pack
+
+test_pack_reused () {
+ test_trace2_data pack-objects pack-reused "$1"
+}
+
+test_packs_reused () {
+ test_trace2_data pack-objects packs-reused "$1"
+}
+
+
+# pack_position <object> </path/to/pack.idx
+pack_position () {
+ git show-index >objects &&
+ grep "$1" objects | cut -d" " -f1
+}
+
+# test_pack_objects_reused_all <pack-reused> <packs-reused>
+test_pack_objects_reused_all () {
+ : >trace2.txt &&
+ GIT_TRACE2_EVENT="$PWD/trace2.txt" \
+ git pack-objects --stdout --revs --all --delta-base-offset \
+ >/dev/null &&
+
+ test_pack_reused "$1" <trace2.txt &&
+ test_packs_reused "$2" <trace2.txt
+}
+
+# test_pack_objects_reused <pack-reused> <packs-reused>
+test_pack_objects_reused () {
+ : >trace2.txt &&
+ GIT_TRACE2_EVENT="$PWD/trace2.txt" \
+ git pack-objects --stdout --revs >/dev/null &&
+
+ test_pack_reused "$1" <trace2.txt &&
+ test_packs_reused "$2" <trace2.txt
+}
+
+test_expect_success 'preferred pack is reused for single-pack reuse' '
+ test_config pack.allowPackReuse single &&
+
+ for i in A B
+ do
+ test_commit "$i" &&
+ git repack -d || return 1
+ done &&
+
+ git multi-pack-index write --bitmap &&
+
+ test_pack_objects_reused_all 3 1
+'
+
+test_expect_success 'multi-pack reuse is disabled by default' '
+ test_pack_objects_reused_all 3 1
+'
+
+test_expect_success 'feature.experimental implies multi-pack reuse' '
+ test_config feature.experimental true &&
+
+ test_pack_objects_reused_all 6 2
+'
+
+test_expect_success 'multi-pack reuse can be disabled with feature.experimental' '
+ test_config feature.experimental true &&
+ test_config pack.allowPackReuse single &&
+
+ test_pack_objects_reused_all 3 1
+'
+
+test_expect_success 'enable multi-pack reuse' '
+ git config pack.allowPackReuse multi
+'
+
+test_expect_success 'reuse all objects from subset of bitmapped packs' '
+ test_commit C &&
+ git repack -d &&
+
+ git multi-pack-index write --bitmap &&
+
+ cat >in <<-EOF &&
+ $(git rev-parse C)
+ ^$(git rev-parse A)
+ EOF
+
+ test_pack_objects_reused 6 2 <in
+'
+
+test_expect_success 'reuse all objects from all packs' '
+ test_pack_objects_reused_all 9 3
+'
+
+test_expect_success 'reuse objects from first pack with middle gap' '
+ for i in D E F
+ do
+ test_commit "$i" || return 1
+ done &&
+
+ # Set "pack.window" to zero to ensure that we do not create any
+ # deltas, which could alter the amount of pack reuse we perform
+ # (if, for e.g., we are not sending one or more bases).
+ D="$(git -c pack.window=0 pack-objects --all --unpacked $packdir/pack)" &&
+
+ d_pos="$(pack_position $(git rev-parse D) <$packdir/pack-$D.idx)" &&
+ e_pos="$(pack_position $(git rev-parse E) <$packdir/pack-$D.idx)" &&
+ f_pos="$(pack_position $(git rev-parse F) <$packdir/pack-$D.idx)" &&
+
+ # commits F, E, and D, should appear in that order at the
+ # beginning of the pack
+ test $f_pos -lt $e_pos &&
+ test $e_pos -lt $d_pos &&
+
+ # Ensure that the pack we are constructing sorts ahead of any
+ # other packs in lexical/bitmap order by choosing it as the
+ # preferred pack.
+ git multi-pack-index write --bitmap --preferred-pack="pack-$D.idx" &&
+
+ cat >in <<-EOF &&
+ $(git rev-parse E)
+ ^$(git rev-parse D)
+ EOF
+
+ test_pack_objects_reused 3 1 <in
+'
+
+test_expect_success 'reuse objects from middle pack with middle gap' '
+ rm -fr $packdir/multi-pack-index* &&
+
+ # Ensure that the pack we are constructing sort into any
+ # position *but* the first one, by choosing a different pack as
+ # the preferred one.
+ git multi-pack-index write --bitmap --preferred-pack="pack-$A.idx" &&
+
+ cat >in <<-EOF &&
+ $(git rev-parse E)
+ ^$(git rev-parse D)
+ EOF
+
+ test_pack_objects_reused 3 1 <in
+'
+
+test_expect_success 'omit delta with uninteresting base (same pack)' '
+ git repack -adk &&
+
+ test_seq 32 >f &&
+ git add f &&
+ test_tick &&
+ git commit -m "delta" &&
+ delta="$(git rev-parse HEAD)" &&
+
+ test_seq 64 >f &&
+ test_tick &&
+ git commit -a -m "base" &&
+ base="$(git rev-parse HEAD)" &&
+
+ test_commit other &&
+
+ git repack -d &&
+
+ have_delta "$(git rev-parse $delta:f)" "$(git rev-parse $base:f)" &&
+
+ git multi-pack-index write --bitmap &&
+
+ cat >in <<-EOF &&
+ $(git rev-parse other)
+ ^$base
+ EOF
+
+ # We can only reuse the 3 objects corresponding to "other" from
+ # the latest pack.
+ #
+ # This is because even though we want "delta", we do not want
+ # "base", meaning that we have to inflate the delta/base-pair
+ # corresponding to the blob in commit "delta", which bypasses
+ # the pack-reuse mechanism.
+ #
+ # The remaining objects from the other pack are similarly not
+ # reused because their objects are on the uninteresting side of
+ # the query.
+ test_pack_objects_reused 3 1 <in
+'
+
+test_expect_success 'omit delta from uninteresting base (cross pack)' '
+ cat >in <<-EOF &&
+ $(git rev-parse $base)
+ ^$(git rev-parse $delta)
+ EOF
+
+ P="$(git pack-objects --revs $packdir/pack <in)" &&
+
+ git multi-pack-index write --bitmap --preferred-pack="pack-$P.idx" &&
+
+ packs_nr="$(find $packdir -type f -name "pack-*.pack" | wc -l)" &&
+ objects_nr="$(git rev-list --count --all --objects)" &&
+
+ test_pack_objects_reused_all $(($objects_nr - 1)) $packs_nr
+'
+
+test_done
diff --git a/t/t5351-unpack-large-objects.sh b/t/t5351-unpack-large-objects.sh
new file mode 100755
index 0000000..43cbcd5
--- /dev/null
+++ b/t/t5351-unpack-large-objects.sh
@@ -0,0 +1,103 @@
+#!/bin/sh
+#
+# Copyright (c) 2022 Han Xin
+#
+
+test_description='git unpack-objects with large objects'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+prepare_dest () {
+ test_when_finished "rm -rf dest.git" &&
+ git init --bare dest.git &&
+ git -C dest.git config core.bigFileThreshold "$1"
+}
+
+test_expect_success "create large objects (1.5 MB) and PACK" '
+ test-tool genrandom foo 1500000 >big-blob &&
+ test_commit --append foo big-blob &&
+ test-tool genrandom bar 1500000 >big-blob &&
+ test_commit --append bar big-blob &&
+ PACK=$(echo HEAD | git pack-objects --revs pack) &&
+ git verify-pack -v pack-$PACK.pack >out &&
+ sed -n -e "s/^\([0-9a-f][0-9a-f]*\).*\(commit\|tree\|blob\).*/\1/p" \
+ <out >obj-list
+'
+
+test_expect_success 'set memory limitation to 1MB' '
+ GIT_ALLOC_LIMIT=1m &&
+ export GIT_ALLOC_LIMIT
+'
+
+test_expect_success 'unpack-objects failed under memory limitation' '
+ prepare_dest 2m &&
+ test_must_fail git -C dest.git unpack-objects <pack-$PACK.pack 2>err &&
+ grep "fatal: attempting to allocate" err
+'
+
+test_expect_success 'unpack-objects works with memory limitation in dry-run mode' '
+ prepare_dest 2m &&
+ git -C dest.git unpack-objects -n <pack-$PACK.pack &&
+ test_stdout_line_count = 0 find dest.git/objects -type f &&
+ test_dir_is_empty dest.git/objects/pack
+'
+
+test_expect_success 'unpack big object in stream' '
+ prepare_dest 1m &&
+ git -C dest.git unpack-objects <pack-$PACK.pack &&
+ test_dir_is_empty dest.git/objects/pack
+'
+
+check_fsync_events () {
+ local trace="$1" &&
+ shift &&
+
+ cat >expect &&
+ sed -n \
+ -e '/^{"event":"counter",.*"category":"fsync",/ {
+ s/.*"category":"fsync",//;
+ s/}$//;
+ p;
+ }' \
+ <"$trace" >actual &&
+ test_cmp expect actual
+}
+
+BATCH_CONFIGURATION='-c core.fsync=loose-object -c core.fsyncmethod=batch'
+
+test_expect_success 'unpack big object in stream (core.fsyncmethod=batch)' '
+ prepare_dest 1m &&
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+ GIT_TEST_FSYNC=true \
+ git -C dest.git $BATCH_CONFIGURATION unpack-objects <pack-$PACK.pack &&
+ if grep "core.fsyncMethod = batch is unsupported" trace2.txt
+ then
+ flush_count=7
+ else
+ flush_count=1
+ fi &&
+ check_fsync_events trace2.txt <<-EOF &&
+ "name":"writeout-only","count":6
+ "name":"hardware-flush","count":$flush_count
+ EOF
+
+ test_dir_is_empty dest.git/objects/pack &&
+ git -C dest.git cat-file --batch-check="%(objectname)" <obj-list >current &&
+ cmp obj-list current
+'
+
+test_expect_success 'do not unpack existing large objects' '
+ prepare_dest 1m &&
+ git -C dest.git index-pack --stdin <pack-$PACK.pack &&
+ git -C dest.git unpack-objects <pack-$PACK.pack &&
+
+ # The destination came up with the exact same pack...
+ DEST_PACK=$(echo dest.git/objects/pack/pack-*.pack) &&
+ cmp pack-$PACK.pack $DEST_PACK &&
+
+ # ...and wrote no loose objects
+ test_stdout_line_count = 0 find dest.git/objects -type f ! -name "pack-*"
+'
+
+test_done
diff --git a/t/t5401-update-hooks.sh b/t/t5401-update-hooks.sh
index 6012cc8..d8cadee 100755
--- a/t/t5401-update-hooks.sh
+++ b/t/t5401-update-hooks.sh
@@ -20,45 +20,37 @@ test_expect_success setup '
git clone --bare ./. victim.git &&
GIT_DIR=victim.git git update-ref refs/heads/tofail $commit1 &&
git update-ref refs/heads/main $commit1 &&
- git update-ref refs/heads/tofail $commit0
-'
+ git update-ref refs/heads/tofail $commit0 &&
-cat >victim.git/hooks/pre-receive <<'EOF'
-#!/bin/sh
-printf %s "$@" >>$GIT_DIR/pre-receive.args
-cat - >$GIT_DIR/pre-receive.stdin
-echo STDOUT pre-receive
-echo STDERR pre-receive >&2
-EOF
-chmod u+x victim.git/hooks/pre-receive
+ test_hook --setup -C victim.git pre-receive <<-\EOF &&
+ printf %s "$@" >>$GIT_DIR/pre-receive.args
+ cat - >$GIT_DIR/pre-receive.stdin
+ echo STDOUT pre-receive
+ echo STDERR pre-receive >&2
+ EOF
-cat >victim.git/hooks/update <<'EOF'
-#!/bin/sh
-echo "$@" >>$GIT_DIR/update.args
-read x; printf %s "$x" >$GIT_DIR/update.stdin
-echo STDOUT update $1
-echo STDERR update $1 >&2
-test "$1" = refs/heads/main || exit
-EOF
-chmod u+x victim.git/hooks/update
+ test_hook --setup -C victim.git update <<-\EOF &&
+ echo "$@" >>$GIT_DIR/update.args
+ read x; printf %s "$x" >$GIT_DIR/update.stdin
+ echo STDOUT update $1
+ echo STDERR update $1 >&2
+ test "$1" = refs/heads/main || exit
+ EOF
-cat >victim.git/hooks/post-receive <<'EOF'
-#!/bin/sh
-printf %s "$@" >>$GIT_DIR/post-receive.args
-cat - >$GIT_DIR/post-receive.stdin
-echo STDOUT post-receive
-echo STDERR post-receive >&2
-EOF
-chmod u+x victim.git/hooks/post-receive
+ test_hook --setup -C victim.git post-receive <<-\EOF &&
+ printf %s "$@" >>$GIT_DIR/post-receive.args
+ cat - >$GIT_DIR/post-receive.stdin
+ echo STDOUT post-receive
+ echo STDERR post-receive >&2
+ EOF
-cat >victim.git/hooks/post-update <<'EOF'
-#!/bin/sh
-echo "$@" >>$GIT_DIR/post-update.args
-read x; printf %s "$x" >$GIT_DIR/post-update.stdin
-echo STDOUT post-update
-echo STDERR post-update >&2
-EOF
-chmod u+x victim.git/hooks/post-update
+ test_hook --setup -C victim.git post-update <<-\EOF
+ echo "$@" >>$GIT_DIR/post-update.args
+ read x; printf %s "$x" >$GIT_DIR/post-update.stdin
+ echo STDOUT post-update
+ echo STDERR post-update >&2
+ EOF
+'
test_expect_success push '
test_must_fail git send-pack --force ./victim.git \
@@ -131,20 +123,18 @@ remote: STDOUT post-update
remote: STDERR post-update
EOF
test_expect_success 'send-pack stderr contains hook messages' '
- grep ^remote: send.err | sed "s/ *\$//" >actual &&
+ sed -n "/^remote:/s/ *\$//p" send.err >actual &&
test_cmp expect actual
'
test_expect_success 'pre-receive hook that forgets to read its input' '
- write_script victim.git/hooks/pre-receive <<-\EOF &&
+ test_hook --clobber -C victim.git pre-receive <<-\EOF &&
exit 0
EOF
rm -f victim.git/hooks/update victim.git/hooks/post-update &&
- for v in $(test_seq 100 999)
- do
- git branch branch_$v main || return
- done &&
+ printf "create refs/heads/branch_%d main\n" $(test_seq 100 999) >input &&
+ git update-ref --stdin <input &&
git push ./victim.git "+refs/heads/*:refs/heads/*"
'
diff --git a/t/t5402-post-merge-hook.sh b/t/t5402-post-merge-hook.sh
index 3e5e19c..46ebdfb 100755
--- a/t/t5402-post-merge-hook.sh
+++ b/t/t5402-post-merge-hook.sh
@@ -7,6 +7,7 @@ test_description='Test the post-merge hook.'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -25,13 +26,15 @@ test_expect_success setup '
GIT_DIR=clone2/.git git update-index --add a
'
-for clone in 1 2; do
- cat >clone${clone}/.git/hooks/post-merge <<'EOF'
-#!/bin/sh
-echo $@ >> $GIT_DIR/post-merge.args
-EOF
- chmod u+x clone${clone}/.git/hooks/post-merge
-done
+test_expect_success 'setup clone hooks' '
+ test_when_finished "rm -f hook" &&
+ cat >hook <<-\EOF &&
+ echo $@ >>$GIT_DIR/post-merge.args
+ EOF
+
+ test_hook --setup -C clone1 post-merge <hook &&
+ test_hook --setup -C clone2 post-merge <hook
+'
test_expect_success 'post-merge does not run for up-to-date ' '
GIT_DIR=clone1/.git git merge $commit0 &&
diff --git a/t/t5403-post-checkout-hook.sh b/t/t5403-post-checkout-hook.sh
index 1ec9e23..cfaae54 100755
--- a/t/t5403-post-checkout-hook.sh
+++ b/t/t5403-post-checkout-hook.sh
@@ -7,11 +7,11 @@ test_description='Test the post-checkout hook.'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
- mkdir -p .git/hooks &&
- write_script .git/hooks/post-checkout <<-\EOF &&
+ test_hook --setup post-checkout <<-\EOF &&
echo "$@" >.git/post-checkout.args
EOF
test_commit one &&
@@ -49,23 +49,60 @@ test_expect_success 'post-checkout receives the right args when not switching br
test $old = $new && test $flag = 0
'
-test_expect_success 'post-checkout is triggered on rebase' '
- test_when_finished "rm -f .git/post-checkout.args" &&
- git checkout -b rebase-test main &&
- rm -f .git/post-checkout.args &&
- git rebase rebase-on-me &&
- read old new flag <.git/post-checkout.args &&
- test $old != $new && test $flag = 1
-'
+test_rebase () {
+ args="$*" &&
+ test_expect_success "post-checkout is triggered on rebase $args" '
+ test_when_finished "rm -f .git/post-checkout.args" &&
+ git checkout -B rebase-test main &&
+ rm -f .git/post-checkout.args &&
+ git rebase $args rebase-on-me &&
+ read old new flag <.git/post-checkout.args &&
+ test_cmp_rev main $old &&
+ test_cmp_rev rebase-on-me $new &&
+ test $flag = 1
+ '
+
+ test_expect_success "post-checkout is triggered on rebase $args with fast-forward" '
+ test_when_finished "rm -f .git/post-checkout.args" &&
+ git checkout -B ff-rebase-test rebase-on-me^ &&
+ rm -f .git/post-checkout.args &&
+ git rebase $args rebase-on-me &&
+ read old new flag <.git/post-checkout.args &&
+ test_cmp_rev rebase-on-me^ $old &&
+ test_cmp_rev rebase-on-me $new &&
+ test $flag = 1
+ '
+
+ test_expect_success "rebase $args fast-forward branch checkout runs post-checkout hook" '
+ test_when_finished "test_might_fail git rebase --abort" &&
+ test_when_finished "rm -f .git/post-checkout.args" &&
+ git update-ref refs/heads/rebase-fast-forward three &&
+ git checkout two &&
+ rm -f .git/post-checkout.args &&
+ git rebase $args HEAD rebase-fast-forward &&
+ read old new flag <.git/post-checkout.args &&
+ test_cmp_rev two $old &&
+ test_cmp_rev three $new &&
+ test $flag = 1
+ '
+
+ test_expect_success "rebase $args checkout does not remove untracked files" '
+ test_when_finished "test_might_fail git rebase --abort" &&
+ test_when_finished "rm -f .git/post-checkout.args" &&
+ git update-ref refs/heads/rebase-fast-forward three &&
+ git checkout two &&
+ rm -f .git/post-checkout.args &&
+ echo untracked >three.t &&
+ test_when_finished "rm three.t" &&
+ test_must_fail git rebase $args HEAD rebase-fast-forward 2>err &&
+ grep "untracked working tree files would be overwritten by checkout" err &&
+ test_path_is_missing .git/post-checkout.args
-test_expect_success 'post-checkout is triggered on rebase with fast-forward' '
- test_when_finished "rm -f .git/post-checkout.args" &&
- git checkout -b ff-rebase-test rebase-on-me^ &&
- rm -f .git/post-checkout.args &&
- git rebase rebase-on-me &&
- read old new flag <.git/post-checkout.args &&
- test $old != $new && test $flag = 1
'
+}
+
+test_rebase --apply &&
+test_rebase --merge
test_expect_success 'post-checkout hook is triggered by clone' '
mkdir -p templates/hooks &&
diff --git a/t/t5404-tracking-branches.sh b/t/t5404-tracking-branches.sh
index cc07889..51737ee 100755
--- a/t/t5404-tracking-branches.sh
+++ b/t/t5404-tracking-branches.sh
@@ -5,6 +5,7 @@ test_description='tracking branch update checks for git push'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t5405-send-pack-rewind.sh b/t/t5405-send-pack-rewind.sh
index 11f0323..1686ac1 100755
--- a/t/t5405-send-pack-rewind.sh
+++ b/t/t5405-send-pack-rewind.sh
@@ -5,6 +5,7 @@ test_description='forced push to replace commit we do not have'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t5406-remote-rejects.sh b/t/t5406-remote-rejects.sh
index 5c509db..d6a9946 100755
--- a/t/t5406-remote-rejects.sh
+++ b/t/t5406-remote-rejects.sh
@@ -2,10 +2,11 @@
test_description='remote push rejects are reported by client'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
- write_script .git/hooks/update <<-\EOF &&
+ test_hook update <<-\EOF &&
exit 1
EOF
echo 1 >file &&
diff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh
index 6da8d76..ad7f8c6 100755
--- a/t/t5407-post-rewrite-hook.sh
+++ b/t/t5407-post-rewrite-hook.sh
@@ -17,15 +17,19 @@ test_expect_success 'setup' '
git checkout A^0 &&
test_commit E bar E &&
test_commit F foo F &&
- git checkout main
-'
+ git checkout B &&
+ git merge E &&
+ git tag merge-E &&
+ test_commit G G &&
+ test_commit H H &&
+ test_commit I I &&
+ git checkout main &&
-cat >.git/hooks/post-rewrite <<EOF
-#!/bin/sh
-echo \$@ > "$TRASH_DIRECTORY"/post-rewrite.args
-cat > "$TRASH_DIRECTORY"/post-rewrite.data
-EOF
-chmod u+x .git/hooks/post-rewrite
+ test_hook --setup post-rewrite <<-EOF
+ echo \$@ > "$TRASH_DIRECTORY"/post-rewrite.args
+ cat > "$TRASH_DIRECTORY"/post-rewrite.data
+ EOF
+'
clear_hook_input () {
rm -f post-rewrite.args post-rewrite.data
@@ -175,6 +179,48 @@ test_fail_interactive_rebase () {
)
}
+test_expect_success 'git rebase with failed pick' '
+ clear_hook_input &&
+ cat >todo <<-\EOF &&
+ exec >bar
+ merge -C merge-E E
+ exec >G
+ pick G
+ exec >H 2>I
+ pick H
+ fixup I
+ EOF
+
+ (
+ set_replace_editor todo &&
+ test_must_fail git rebase -i D D 2>err
+ ) &&
+ grep "would be overwritten" err &&
+ rm bar &&
+
+ test_must_fail git rebase --continue 2>err &&
+ grep "would be overwritten" err &&
+ rm G &&
+
+ test_must_fail git rebase --continue 2>err &&
+ grep "would be overwritten" err &&
+ rm H &&
+
+ test_must_fail git rebase --continue 2>err &&
+ grep "would be overwritten" err &&
+ rm I &&
+
+ git rebase --continue &&
+ echo rebase >expected.args &&
+ cat >expected.data <<-EOF &&
+ $(git rev-parse merge-E) $(git rev-parse HEAD~2)
+ $(git rev-parse G) $(git rev-parse HEAD~1)
+ $(git rev-parse H) $(git rev-parse HEAD)
+ $(git rev-parse I) $(git rev-parse HEAD)
+ EOF
+ verify_hook_input
+'
+
test_expect_success 'git rebase -i (unchanged)' '
git reset --hard D &&
clear_hook_input &&
diff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh
index 9f1a483..fa5de45 100755
--- a/t/t5409-colorize-remote-messages.sh
+++ b/t/t5409-colorize-remote-messages.sh
@@ -5,7 +5,7 @@ test_description='remote messages are colorized on the client'
. ./test-lib.sh
test_expect_success 'setup' '
- write_script .git/hooks/update <<-\EOF &&
+ test_hook --setup update <<-\EOF &&
echo error: error
echo ERROR: also highlighted
echo hint: hint
diff --git a/t/t5410-receive-pack-alternates.sh b/t/t5410-receive-pack-alternates.sh
index 0b28e4e..7a45d4c 100755
--- a/t/t5410-receive-pack-alternates.sh
+++ b/t/t5410-receive-pack-alternates.sh
@@ -5,6 +5,7 @@ test_description='git receive-pack with alternate ref filtering'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t5411-proc-receive-hook.sh b/t/t5411-proc-receive-hook.sh
index 98b0e81..92cf52c 100755
--- a/t/t5411-proc-receive-hook.sh
+++ b/t/t5411-proc-receive-hook.sh
@@ -36,7 +36,7 @@ setup_upstream_and_workbench () {
TAG=$(git -C workbench rev-parse v123) &&
# setup pre-receive hook
- write_script upstream.git/hooks/pre-receive <<-\EOF &&
+ test_hook --setup -C upstream.git pre-receive <<-\EOF &&
exec >&2
echo "# pre-receive hook"
while read old new ref
@@ -46,7 +46,7 @@ setup_upstream_and_workbench () {
EOF
# setup post-receive hook
- write_script upstream.git/hooks/post-receive <<-\EOF &&
+ test_hook --setup -C upstream.git post-receive <<-\EOF &&
exec >&2
echo "# post-receive hook"
while read old new ref
diff --git a/t/t5411/once-0010-report-status-v1.sh b/t/t5411/once-0010-report-status-v1.sh
index 297b109..f9ffb01 100644
--- a/t/t5411/once-0010-report-status-v1.sh
+++ b/t/t5411/once-0010-report-status-v1.sh
@@ -3,7 +3,7 @@ test_expect_success "setup receive.procReceiveRefs" '
'
test_expect_success "setup proc-receive hook" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/main/topic1" \
diff --git a/t/t5411/test-0002-pre-receive-declined.sh b/t/t5411/test-0002-pre-receive-declined.sh
index 0c3490c..98a9d13 100644
--- a/t/t5411/test-0002-pre-receive-declined.sh
+++ b/t/t5411/test-0002-pre-receive-declined.sh
@@ -1,6 +1,6 @@
test_expect_success "setup pre-receive hook ($PROTOCOL)" '
mv "$upstream/hooks/pre-receive" "$upstream/hooks/pre-receive.ok" &&
- write_script "$upstream/hooks/pre-receive" <<-EOF
+ test_hook -C "$upstream" --clobber pre-receive <<-\EOF
exit 1
EOF
'
@@ -21,7 +21,7 @@ test_expect_success "git-push is declined ($PROTOCOL)" '
EOF
test_cmp expect actual &&
- test_cmp_refs -C "$upstream" <<-EOF
+ test_cmp_refs -C "$upstream" <<-\EOF
<COMMIT-A> refs/heads/main
EOF
'
diff --git a/t/t5411/test-0003-pre-receive-declined--porcelain.sh b/t/t5411/test-0003-pre-receive-declined--porcelain.sh
index 2393b04..67ca6dc 100644
--- a/t/t5411/test-0003-pre-receive-declined--porcelain.sh
+++ b/t/t5411/test-0003-pre-receive-declined--porcelain.sh
@@ -1,6 +1,6 @@
test_expect_success "setup pre-receive hook ($PROTOCOL/porcelain)" '
mv "$upstream/hooks/pre-receive" "$upstream/hooks/pre-receive.ok" &&
- write_script "$upstream/hooks/pre-receive" <<-EOF
+ test_hook -C "$upstream" --clobber pre-receive <<-\EOF
exit 1
EOF
'
diff --git a/t/t5411/test-0013-bad-protocol.sh b/t/t5411/test-0013-bad-protocol.sh
index c08a00d..8d22e17 100644
--- a/t/t5411/test-0013-bad-protocol.sh
+++ b/t/t5411/test-0013-bad-protocol.sh
@@ -1,5 +1,5 @@
test_expect_success "setup proc-receive hook (unknown version, $PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v --version 2
EOF
@@ -40,7 +40,7 @@ test_expect_success "proc-receive: bad protocol (unknown version, $PROTOCOL)" '
'
test_expect_success "setup proc-receive hook (hook --die-read-version, $PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v --die-read-version
EOF
@@ -65,13 +65,13 @@ test_expect_success "proc-receive: bad protocol (hook --die-read-version, $PROTO
grep "remote: fatal: die with the --die-read-version option" out-$test_count &&
grep "remote: error: fail to negotiate version with proc-receive hook" out-$test_count &&
- test_cmp_refs -C "$upstream" <<-EOF
+ test_cmp_refs -C "$upstream" <<-\EOF
<COMMIT-A> refs/heads/main
EOF
'
test_expect_success "setup proc-receive hook (hook --die-write-version, $PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v --die-write-version
EOF
@@ -102,7 +102,7 @@ test_expect_success "proc-receive: bad protocol (hook --die-write-version, $PROT
'
test_expect_success "setup proc-receive hook (hook --die-read-commands, $PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v --die-read-commands
EOF
@@ -132,7 +132,7 @@ test_expect_success "proc-receive: bad protocol (hook --die-read-commands, $PROT
'
test_expect_success "setup proc-receive hook (hook --die-read-push-options, $PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v --die-read-push-options
EOF
@@ -164,7 +164,7 @@ test_expect_success "proc-receive: bad protocol (hook --die-read-push-options, $
'
test_expect_success "setup proc-receive hook (hook --die-write-report, $PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v --die-write-report
EOF
@@ -194,7 +194,7 @@ test_expect_success "proc-receive: bad protocol (hook --die-write-report, $PROTO
'
test_expect_success "setup proc-receive hook (no report, $PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v
EOF
@@ -236,7 +236,7 @@ test_expect_success "cleanup ($PROTOCOL)" '
'
test_expect_success "setup proc-receive hook (no ref, $PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok"
@@ -269,7 +269,7 @@ test_expect_success "proc-receive: bad protocol (no ref, $PROTOCOL)" '
'
test_expect_success "setup proc-receive hook (unknown status, $PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "xx refs/for/main/topic"
diff --git a/t/t5411/test-0014-bad-protocol--porcelain.sh b/t/t5411/test-0014-bad-protocol--porcelain.sh
index 3eaa597..298a3d1 100644
--- a/t/t5411/test-0014-bad-protocol--porcelain.sh
+++ b/t/t5411/test-0014-bad-protocol--porcelain.sh
@@ -1,5 +1,5 @@
test_expect_success "setup proc-receive hook (unknown version, $PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v --version 2
EOF
@@ -40,7 +40,7 @@ test_expect_success "proc-receive: bad protocol (unknown version, $PROTOCOL/porc
'
test_expect_success "setup proc-receive hook (hook --die-read-version, $PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v --die-read-version
EOF
@@ -71,7 +71,7 @@ test_expect_success "proc-receive: bad protocol (hook --die-read-version, $PROTO
'
test_expect_success "setup proc-receive hook (hook --die-write-version, $PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v --die-write-version
EOF
@@ -102,7 +102,7 @@ test_expect_success "proc-receive: bad protocol (hook --die-write-version, $PROT
'
test_expect_success "setup proc-receive hook (hook --die-read-commands, $PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v --die-read-commands
EOF
@@ -132,7 +132,7 @@ test_expect_success "proc-receive: bad protocol (hook --die-read-commands, $PROT
'
test_expect_success "setup proc-receive hook (hook --die-read-push-options, $PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v --die-read-push-options
EOF
@@ -164,7 +164,7 @@ test_expect_success "proc-receive: bad protocol (hook --die-read-push-options, $
'
test_expect_success "setup proc-receive hook (hook --die-write-report, $PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v --die-write-report
EOF
@@ -194,7 +194,7 @@ test_expect_success "proc-receive: bad protocol (hook --die-write-report, $PROTO
'
test_expect_success "setup proc-receive hook (no report, $PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v
EOF
@@ -236,7 +236,7 @@ test_expect_success "cleanup ($PROTOCOL/porcelain)" '
'
test_expect_success "setup proc-receive hook (no ref, $PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok"
@@ -270,7 +270,7 @@ test_expect_success "proc-receive: bad protocol (no ref, $PROTOCOL/porcelain)" '
'
test_expect_success "setup proc-receive hook (unknown status, $PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "xx refs/for/main/topic"
diff --git a/t/t5411/test-0020-report-ng.sh b/t/t5411/test-0020-report-ng.sh
index e915dbc..6347c96 100644
--- a/t/t5411/test-0020-report-ng.sh
+++ b/t/t5411/test-0020-report-ng.sh
@@ -1,5 +1,5 @@
test_expect_success "setup proc-receive hook (ng, no message, $PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ng refs/for/main/topic"
@@ -31,7 +31,7 @@ test_expect_success "proc-receive: fail to update (ng, no message, $PROTOCOL)" '
'
test_expect_success "setup proc-receive hook (ng message, $PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ng refs/for/main/topic error msg"
diff --git a/t/t5411/test-0021-report-ng--porcelain.sh b/t/t5411/test-0021-report-ng--porcelain.sh
index 2a392e0..502b34f 100644
--- a/t/t5411/test-0021-report-ng--porcelain.sh
+++ b/t/t5411/test-0021-report-ng--porcelain.sh
@@ -1,5 +1,5 @@
test_expect_success "setup proc-receive hook (ng, no message, $PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ng refs/for/main/topic"
@@ -32,7 +32,7 @@ test_expect_success "proc-receive: fail to update (ng, no message, $PROTOCOL/por
'
test_expect_success "setup proc-receive hook (ng message, $PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ng refs/for/main/topic error msg"
diff --git a/t/t5411/test-0022-report-unexpect-ref.sh b/t/t5411/test-0022-report-unexpect-ref.sh
index f7a494b..7744392 100644
--- a/t/t5411/test-0022-report-unexpect-ref.sh
+++ b/t/t5411/test-0022-report-unexpect-ref.sh
@@ -1,5 +1,5 @@
test_expect_success "setup proc-receive hook (unexpected ref, $PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/heads/main"
diff --git a/t/t5411/test-0023-report-unexpect-ref--porcelain.sh b/t/t5411/test-0023-report-unexpect-ref--porcelain.sh
index 63c479e..6d116ef 100644
--- a/t/t5411/test-0023-report-unexpect-ref--porcelain.sh
+++ b/t/t5411/test-0023-report-unexpect-ref--porcelain.sh
@@ -1,5 +1,5 @@
test_expect_success "setup proc-receive hook (unexpected ref, $PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/heads/main"
diff --git a/t/t5411/test-0024-report-unknown-ref.sh b/t/t5411/test-0024-report-unknown-ref.sh
index af055aa..619ca2f 100644
--- a/t/t5411/test-0024-report-unknown-ref.sh
+++ b/t/t5411/test-0024-report-unknown-ref.sh
@@ -1,5 +1,5 @@
test_expect_success "setup proc-receive hook (unexpected ref, $PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/main/topic"
diff --git a/t/t5411/test-0025-report-unknown-ref--porcelain.sh b/t/t5411/test-0025-report-unknown-ref--porcelain.sh
index 99601ca..8b3f5d0 100644
--- a/t/t5411/test-0025-report-unknown-ref--porcelain.sh
+++ b/t/t5411/test-0025-report-unknown-ref--porcelain.sh
@@ -1,5 +1,5 @@
test_expect_success "setup proc-receive hook (unexpected ref, $PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/main/topic"
diff --git a/t/t5411/test-0026-push-options.sh b/t/t5411/test-0026-push-options.sh
index fec5f95..510fff3 100644
--- a/t/t5411/test-0026-push-options.sh
+++ b/t/t5411/test-0026-push-options.sh
@@ -1,6 +1,6 @@
test_expect_success "setup proc-receive hook and disable push-options ($PROTOCOL)" '
git -C "$upstream" config receive.advertisePushOptions false &&
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/main/topic"
@@ -18,7 +18,7 @@ test_expect_success "proc-receive: not support push options ($PROTOCOL)" '
HEAD:refs/for/main/topic \
>out-$test_count 2>&1 &&
make_user_friendly_and_stable_output <out-$test_count >actual &&
- test_i18ngrep "fatal: the receiving end does not support push options" \
+ test_grep "fatal: the receiving end does not support push options" \
actual &&
test_cmp_refs -C "$upstream" <<-EOF
@@ -31,7 +31,7 @@ test_expect_success "enable push options ($PROTOCOL)" '
'
test_expect_success "setup version=0 for proc-receive hook ($PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
--version 0 \
@@ -75,7 +75,7 @@ test_expect_success "proc-receive: ignore push-options for version 0 ($PROTOCOL)
'
test_expect_success "restore proc-receive hook ($PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/main/topic"
diff --git a/t/t5411/test-0027-push-options--porcelain.sh b/t/t5411/test-0027-push-options--porcelain.sh
index 8fb75a8..9435457 100644
--- a/t/t5411/test-0027-push-options--porcelain.sh
+++ b/t/t5411/test-0027-push-options--porcelain.sh
@@ -1,6 +1,6 @@
test_expect_success "setup proc-receive hook and disable push-options ($PROTOCOL/porcelain)" '
git -C "$upstream" config receive.advertisePushOptions false &&
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/main/topic"
@@ -19,7 +19,7 @@ test_expect_success "proc-receive: not support push options ($PROTOCOL/porcelain
HEAD:refs/for/main/topic \
>out-$test_count 2>&1 &&
make_user_friendly_and_stable_output <out-$test_count >actual &&
- test_i18ngrep "fatal: the receiving end does not support push options" \
+ test_grep "fatal: the receiving end does not support push options" \
actual &&
test_cmp_refs -C "$upstream" <<-EOF
@@ -32,7 +32,7 @@ test_expect_success "enable push options ($PROTOCOL/porcelain)" '
'
test_expect_success "setup version=0 for proc-receive hook ($PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
--version 0 \
@@ -78,7 +78,7 @@ test_expect_success "proc-receive: ignore push-options for version 0 ($PROTOCOL/
'
test_expect_success "restore proc-receive hook ($PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/main/topic"
diff --git a/t/t5411/test-0030-report-ok.sh b/t/t5411/test-0030-report-ok.sh
index a3a6278..0f190a6 100644
--- a/t/t5411/test-0030-report-ok.sh
+++ b/t/t5411/test-0030-report-ok.sh
@@ -1,5 +1,5 @@
test_expect_success "setup proc-receive hook (ok, $PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/main/topic"
diff --git a/t/t5411/test-0031-report-ok--porcelain.sh b/t/t5411/test-0031-report-ok--porcelain.sh
index 0e17538..7ec3981 100644
--- a/t/t5411/test-0031-report-ok--porcelain.sh
+++ b/t/t5411/test-0031-report-ok--porcelain.sh
@@ -1,5 +1,5 @@
test_expect_success "setup proc-receive hook (ok, $PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/main/topic"
diff --git a/t/t5411/test-0032-report-with-options.sh b/t/t5411/test-0032-report-with-options.sh
index 988a430..07733b9 100644
--- a/t/t5411/test-0032-report-with-options.sh
+++ b/t/t5411/test-0032-report-with-options.sh
@@ -1,5 +1,5 @@
test_expect_success "setup proc-receive hook (option without matching ok, $PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "option refname refs/pull/123/head" \
@@ -30,7 +30,7 @@ test_expect_success "proc-receive: report option without matching ok ($PROTOCOL)
'
test_expect_success "setup proc-receive hook (option refname, $PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/main/topic" \
@@ -62,7 +62,7 @@ test_expect_success "proc-receive: report option refname ($PROTOCOL)" '
'
test_expect_success "setup proc-receive hook (option refname and forced-update, $PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/main/topic" \
@@ -95,7 +95,7 @@ test_expect_success "proc-receive: report option refname and forced-update ($PRO
'
test_expect_success "setup proc-receive hook (option refname and old-oid, $PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/main/topic" \
@@ -129,7 +129,7 @@ test_expect_success "proc-receive: report option refname and old-oid ($PROTOCOL)
'
test_expect_success "setup proc-receive hook (option old-oid, $PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/main/topic" \
@@ -161,7 +161,7 @@ test_expect_success "proc-receive: report option old-oid ($PROTOCOL)" '
'
test_expect_success "setup proc-receive hook (option old-oid and new-oid, $PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/main/topic" \
@@ -195,7 +195,7 @@ test_expect_success "proc-receive: report option old-oid and new-oid ($PROTOCOL)
'
test_expect_success "setup proc-receive hook (report with multiple rewrites, $PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/a/b/c/topic" \
diff --git a/t/t5411/test-0033-report-with-options--porcelain.sh b/t/t5411/test-0033-report-with-options--porcelain.sh
index daacb3d..2e1831b 100644
--- a/t/t5411/test-0033-report-with-options--porcelain.sh
+++ b/t/t5411/test-0033-report-with-options--porcelain.sh
@@ -1,5 +1,5 @@
test_expect_success "setup proc-receive hook (option without matching ok, $PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "option refname refs/pull/123/head" \
@@ -31,7 +31,7 @@ test_expect_success "proc-receive: report option without matching ok ($PROTOCOL/
'
test_expect_success "setup proc-receive hook (option refname, $PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/main/topic" \
@@ -64,7 +64,7 @@ test_expect_success "proc-receive: report option refname ($PROTOCOL/porcelain)"
'
test_expect_success "setup proc-receive hook (option refname and forced-update, $PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/main/topic" \
@@ -99,7 +99,7 @@ test_expect_success "proc-receive: report option refname and forced-update ($PRO
'
test_expect_success "setup proc-receive hook (option refname and old-oid, $PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/main/topic" \
@@ -134,7 +134,7 @@ test_expect_success "proc-receive: report option refname and old-oid ($PROTOCOL/
'
test_expect_success "setup proc-receive hook (option old-oid, $PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/main/topic" \
@@ -167,7 +167,7 @@ test_expect_success "proc-receive: report option old-oid ($PROTOCOL/porcelain)"
'
test_expect_success "setup proc-receive hook (option old-oid and new-oid, $PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/main/topic" \
@@ -202,7 +202,7 @@ test_expect_success "proc-receive: report option old-oid and new-oid ($PROTOCOL/
'
test_expect_success "setup proc-receive hook (report with multiple rewrites, $PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/a/b/c/topic" \
diff --git a/t/t5411/test-0034-report-ft.sh b/t/t5411/test-0034-report-ft.sh
index 73a47d1..0e37535 100644
--- a/t/t5411/test-0034-report-ft.sh
+++ b/t/t5411/test-0034-report-ft.sh
@@ -1,5 +1,5 @@
test_expect_success "setup proc-receive hook (ft, $PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/main/topic" \
diff --git a/t/t5411/test-0035-report-ft--porcelain.sh b/t/t5411/test-0035-report-ft--porcelain.sh
index c350201..b9a0518 100644
--- a/t/t5411/test-0035-report-ft--porcelain.sh
+++ b/t/t5411/test-0035-report-ft--porcelain.sh
@@ -1,5 +1,5 @@
test_expect_success "setup proc-receive hook (fall-through, $PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-\EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/main/topic" \
diff --git a/t/t5411/test-0036-report-multi-rewrite-for-one-ref.sh b/t/t5411/test-0036-report-multi-rewrite-for-one-ref.sh
index 8c8a6c1..889e970 100644
--- a/t/t5411/test-0036-report-multi-rewrite-for-one-ref.sh
+++ b/t/t5411/test-0036-report-multi-rewrite-for-one-ref.sh
@@ -14,7 +14,7 @@ test_expect_success "setup git config for remote-tracking of special refs" '
'
test_expect_success "setup proc-receive hook (multiple rewrites for one ref, no refname for the 1st rewrite, $PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/main/topic" \
@@ -87,7 +87,7 @@ test_expect_success "proc-receive: check remote-tracking #1 ($PROTOCOL)" '
'
test_expect_success "setup proc-receive hook (multiple rewrites for one ref, no refname for the 2nd rewrite, $PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/main/topic" \
@@ -162,7 +162,7 @@ test_expect_success "proc-receive: check remote-tracking #2 ($PROTOCOL)" '
'
test_expect_success "setup proc-receive hook (multiple rewrites for one ref, $PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/main/topic" \
diff --git a/t/t5411/test-0037-report-multi-rewrite-for-one-ref--porcelain.sh b/t/t5411/test-0037-report-multi-rewrite-for-one-ref--porcelain.sh
index bc44810..1e523b1 100644
--- a/t/t5411/test-0037-report-multi-rewrite-for-one-ref--porcelain.sh
+++ b/t/t5411/test-0037-report-multi-rewrite-for-one-ref--porcelain.sh
@@ -1,5 +1,5 @@
test_expect_success "setup proc-receive hook (multiple rewrites for one ref, no refname for the 1st rewrite, $PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/main/topic" \
@@ -58,7 +58,7 @@ test_expect_success "proc-receive: multiple rewrite for one ref, no refname for
'
test_expect_success "setup proc-receive hook (multiple rewrites for one ref, no refname for the 2nd rewrite, $PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/main/topic" \
@@ -119,7 +119,7 @@ test_expect_success "proc-receive: multiple rewrites for one ref, no refname for
'
test_expect_success "setup proc-receive hook (multiple rewrites for one ref, $PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/main/topic" \
diff --git a/t/t5411/test-0038-report-mixed-refs.sh b/t/t5411/test-0038-report-mixed-refs.sh
index e63fe7b..4c70e84 100644
--- a/t/t5411/test-0038-report-mixed-refs.sh
+++ b/t/t5411/test-0038-report-mixed-refs.sh
@@ -1,5 +1,5 @@
test_expect_success "setup proc-receive hook ($PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/next/topic2" \
diff --git a/t/t5411/test-0039-report-mixed-refs--porcelain.sh b/t/t5411/test-0039-report-mixed-refs--porcelain.sh
index 99d17b7..40f4c5b 100644
--- a/t/t5411/test-0039-report-mixed-refs--porcelain.sh
+++ b/t/t5411/test-0039-report-mixed-refs--porcelain.sh
@@ -1,5 +1,5 @@
test_expect_success "setup proc-receive hook ($PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/for/next/topic2" \
diff --git a/t/t5411/test-0040-process-all-refs.sh b/t/t5411/test-0040-process-all-refs.sh
index 2f405ad..7ae3851 100644
--- a/t/t5411/test-0040-process-all-refs.sh
+++ b/t/t5411/test-0040-process-all-refs.sh
@@ -17,7 +17,7 @@ test_expect_success "setup upstream branches ($PROTOCOL)" '
'
test_expect_success "setup proc-receive hook ($PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/heads/main" \
diff --git a/t/t5411/test-0041-process-all-refs--porcelain.sh b/t/t5411/test-0041-process-all-refs--porcelain.sh
index c884057..02e1e08 100644
--- a/t/t5411/test-0041-process-all-refs--porcelain.sh
+++ b/t/t5411/test-0041-process-all-refs--porcelain.sh
@@ -17,7 +17,7 @@ test_expect_success "setup upstream branches ($PROTOCOL/porcelain)" '
'
test_expect_success "setup proc-receive hook ($PROTOCOL/porcelain)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/heads/main" \
diff --git a/t/t5411/test-0050-proc-receive-refs-with-modifiers.sh b/t/t5411/test-0050-proc-receive-refs-with-modifiers.sh
index 31989f0..7efdfe5 100644
--- a/t/t5411/test-0050-proc-receive-refs-with-modifiers.sh
+++ b/t/t5411/test-0050-proc-receive-refs-with-modifiers.sh
@@ -9,7 +9,7 @@ test_expect_success "config receive.procReceiveRefs with modifiers ($PROTOCOL)"
'
test_expect_success "setup proc-receive hook ($PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/heads/main" \
@@ -70,7 +70,7 @@ test_expect_success "setup upstream: create tags/v123 ($PROTOCOL)" '
'
test_expect_success "setup proc-receive hook ($PROTOCOL)" '
- write_script "$upstream/hooks/proc-receive" <<-EOF
+ test_hook -C "$upstream" --clobber proc-receive <<-EOF
printf >&2 "# proc-receive hook\n"
test-tool proc-receive -v \
-r "ok refs/heads/main" \
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index 8a5d349..1bc15a3 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -95,7 +95,7 @@ test_expect_success 'setup' '
while [ $cur -le 10 ]; do
add A$cur $(eval echo \$A$prev) &&
prev=$cur &&
- cur=$(($cur+1))
+ cur=$(($cur+1)) || return 1
done &&
add B1 $A1 &&
git update-ref refs/heads/A "$ATIP" &&
@@ -112,7 +112,7 @@ test_expect_success 'post 1st pull setup' '
while [ $cur -le 65 ]; do
add B$cur $(eval echo \$B$prev) &&
prev=$cur &&
- cur=$(($cur+1))
+ cur=$(($cur+1)) || return 1
done
'
@@ -132,13 +132,18 @@ test_expect_success 'single branch object count' '
'
test_expect_success 'single given branch clone' '
- git clone --single-branch --branch A "file://$(pwd)/." branch-a &&
- test_must_fail git --git-dir=branch-a/.git rev-parse origin/B
+ GIT_TRACE2_EVENT="$(pwd)/branch-a/trace2_event" \
+ git clone --single-branch --branch A "file://$(pwd)/." branch-a &&
+ test_must_fail git --git-dir=branch-a/.git rev-parse origin/B &&
+ grep \"fetch-info\".*\"haves\":0 branch-a/trace2_event &&
+ grep \"fetch-info\".*\"wants\":1 branch-a/trace2_event
'
test_expect_success 'clone shallow depth 1' '
- git clone --no-single-branch --depth 1 "file://$(pwd)/." shallow0 &&
- test "$(git --git-dir=shallow0/.git rev-list --count HEAD)" = 1
+ GIT_TRACE2_EVENT="$(pwd)/shallow0/trace2_event" \
+ git clone --no-single-branch --depth 1 "file://$(pwd)/." shallow0 &&
+ test "$(git --git-dir=shallow0/.git rev-list --count HEAD)" = 1 &&
+ grep \"fetch-info\".*\"depth\":1 shallow0/trace2_event
'
test_expect_success 'clone shallow depth 1 with fsck' '
@@ -235,7 +240,10 @@ test_expect_success 'add two more (part 2)' '
test_expect_success 'deepening pull in shallow repo' '
(
cd shallow &&
- git pull --depth 4 .. B
+ GIT_TRACE2_EVENT="$(pwd)/trace2_event" \
+ git pull --depth 4 .. B &&
+ grep \"fetch-info\".*\"depth\":4 trace2_event &&
+ grep \"fetch-info\".*\"shallows\":2 trace2_event
)
'
@@ -306,9 +314,12 @@ test_expect_success 'fetch --depth --no-shallow' '
test_expect_success 'turn shallow to complete repository' '
(
cd shallow &&
- git fetch --unshallow &&
+ GIT_TRACE2_EVENT="$(pwd)/trace2_event" \
+ git fetch --unshallow &&
! test -f .git/shallow &&
- git fsck --full
+ git fsck --full &&
+ grep \"fetch-info\".*\"shallows\":2 trace2_event &&
+ grep \"fetch-info\".*\"depth\":2147483647 trace2_event
)
'
@@ -403,10 +414,11 @@ test_expect_success 'in_vain not triggered before first ACK' '
test_commit -C myserver bar &&
git -C myclient fetch --progress origin 2>log &&
- test_i18ngrep "remote: Total 3 " log
+ test_grep "remote: Total 3 " log
'
test_expect_success 'in_vain resetted upon ACK' '
+ test_when_finished rm -f log trace2 &&
rm -rf myserver myclient &&
git init myserver &&
@@ -432,8 +444,9 @@ test_expect_success 'in_vain resetted upon ACK' '
# first. The 256th commit is common between the client and the server,
# and should reset in_vain. This allows negotiation to continue until
# the client reports that first_anotherbranch_commit is common.
- git -C myclient fetch --progress origin main 2>log &&
- test_i18ngrep "Total 3 " log
+ GIT_TRACE2_EVENT="$(pwd)/trace2" git -C myclient fetch --progress origin main 2>log &&
+ grep \"key\":\"total_rounds\",\"value\":\"6\" trace2 &&
+ test_grep "Total 3 " log
'
test_expect_success 'fetch in shallow repo unreachable shallow objects' '
@@ -457,18 +470,18 @@ 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
- test_i18ngrep "remote: Total 1" actual
+ test_grep "remote: Total 1" actual
)
'
test_expect_success 'setup tests for the --stdin parameter' '
for head in C D E F
do
- add $head
+ add $head || return 1
done &&
for head in A B C D E F
do
- git tag $head $head
+ git tag $head $head || return 1
done &&
cat >input <<-\EOF &&
refs/heads/C
@@ -692,7 +705,7 @@ test_expect_success 'fetch-pack cannot fetch a raw sha1 that is not advertised a
# unadvertised objects, so restrict this test to v0.
test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 git -C client fetch-pack ../server \
$(git -C server rev-parse refs/heads/main^) 2>err &&
- test_i18ngrep "Server does not allow request for unadvertised object" err
+ test_grep "Server does not allow request for unadvertised object" err
'
check_prot_path () {
@@ -824,13 +837,15 @@ test_expect_success 'clone shallow since ...' '
'
test_expect_success 'fetch shallow since ...' '
- git -C shallow11 fetch --shallow-since "200000000 +0700" origin &&
+ GIT_TRACE2_EVENT=$(pwd)/shallow11/trace2_event \
+ git -C shallow11 fetch --shallow-since "200000000 +0700" origin &&
git -C shallow11 log --pretty=tformat:%s origin/main >actual &&
cat >expected <<-\EOF &&
three
two
EOF
- test_cmp expected actual
+ test_cmp expected actual &&
+ grep \"fetch-info\".*\"deepen-since\":true shallow11/trace2_event
'
test_expect_success 'clone shallow since selects no commits' '
@@ -927,7 +942,8 @@ test_expect_success 'fetching deepen' '
)
'
-test_expect_success 'use ref advertisement to prune "have" lines sent' '
+test_negotiation_algorithm_default () {
+ test_when_finished rm -rf clientv0 clientv2 &&
rm -rf server client &&
git init server &&
test_commit -C server both_have_1 &&
@@ -946,7 +962,7 @@ test_expect_success 'use ref advertisement to prune "have" lines sent' '
rm -f trace &&
cp -r client clientv0 &&
GIT_TRACE_PACKET="$(pwd)/trace" git -C clientv0 \
- fetch origin server_has both_have_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 &&
@@ -954,10 +970,27 @@ test_expect_success 'use ref advertisement to prune "have" lines sent' '
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 &&
+ "$@" 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 'use ref advertisement to prune "have" lines sent' '
+ test_negotiation_algorithm_default
+'
+
+test_expect_success 'same as last but with config overrides' '
+ test_negotiation_algorithm_default \
+ -c feature.experimental=true \
+ -c fetch.negotiationAlgorithm=consecutive
+'
+
+test_expect_success 'ensure bogus fetch.negotiationAlgorithm yields error' '
+ test_when_finished rm -rf clientv0 &&
+ cp -r client clientv0 &&
+ test_must_fail git -C clientv0 --fetch.negotiationAlgorithm=bogus \
+ fetch origin server_has both_have_2
'
test_expect_success 'filtering by size' '
@@ -967,13 +1000,16 @@ test_expect_success 'filtering by size' '
test_config -C server uploadpack.allowfilter 1 &&
test_create_repo client &&
- git -C client fetch-pack --filter=blob:limit=0 ../server HEAD &&
+ GIT_TRACE2_EVENT=$(pwd)/client/trace2_event \
+ git -C client fetch-pack --filter=blob:limit=0 ../server HEAD &&
# Ensure that object is not inadvertently fetched
commit=$(git -C server rev-parse HEAD) &&
blob=$(git hash-object server/one.t) &&
git -C client rev-list --objects --missing=allow-any "$commit" >oids &&
- ! grep "$blob" oids
+ ! grep "$blob" oids &&
+
+ grep \"fetch-info\".*\"filter\":\"blob:limit\" client/trace2_event
'
test_expect_success 'filtering by size has no effect if support for it is not advertised' '
@@ -990,7 +1026,7 @@ test_expect_success 'filtering by size has no effect if support for it is not ad
git -C client rev-list --objects --missing=allow-any "$commit" >oids &&
grep "$blob" oids &&
- test_i18ngrep "filtering not recognized by server" err
+ test_grep "filtering not recognized by server" err
'
fetch_filter_blob_limit_zero () {
diff --git a/t/t5502-quickfetch.sh b/t/t5502-quickfetch.sh
index 8c05c77..7b3ff21 100755
--- a/t/t5502-quickfetch.sh
+++ b/t/t5502-quickfetch.sh
@@ -5,6 +5,7 @@ test_description='test quickfetch from local'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -130,7 +131,7 @@ test_expect_success 'quickfetch should handle ~1000 refs (on Windows)' '
for i in 0 1 2 3 4 5 6 7 8 9; do
for j in 0 1 2 3 4 5 6 7 8 9; do
for k in 0 1 2 3 4 5 6 7 8 9; do
- echo "$branchprefix$i$j$k" >> .git/packed-refs
+ echo "$branchprefix$i$j$k" >> .git/packed-refs || return 1
done
done
done &&
diff --git a/t/t5503-tagfollow.sh b/t/t5503-tagfollow.sh
index 195fc64..5ebbaa4 100755
--- a/t/t5503-tagfollow.sh
+++ b/t/t5503-tagfollow.sh
@@ -5,6 +5,7 @@ test_description='test automatic tag following'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# End state of the repository:
diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh
index 6e5a9c2..138e677 100755
--- a/t/t5504-fetch-receive-strict.sh
+++ b/t/t5504-fetch-receive-strict.sh
@@ -4,6 +4,7 @@ test_description='fetch/receive strict mode'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup and inject "corrupt or missing" object' '
@@ -138,12 +139,12 @@ This commit object intentionally broken
EOF
test_expect_success 'setup bogus commit' '
- commit="$(git hash-object -t commit -w --stdin <bogus-commit)"
+ commit="$(git hash-object --literally -t commit -w --stdin <bogus-commit)"
'
test_expect_success 'fsck with no skipList input' '
test_must_fail git fsck 2>err &&
- test_i18ngrep "missingEmail" err
+ test_grep "missingEmail" err
'
test_expect_success 'setup sorted and unsorted skipLists' '
@@ -168,9 +169,9 @@ test_expect_success 'fsck with unsorted skipList' '
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.*: does-not-exist" err &&
+ test_grep "could not open.*: does-not-exist" err &&
test_must_fail git -c fsck.skipList=.git/config -c fsck.missingEmail=ignore fsck 2>err &&
- test_i18ngrep "invalid object name: \[core\]" err
+ test_grep "invalid object name: \[core\]" err
'
test_expect_success 'fsck with other accepted skipList input (comments & empty lines)' '
@@ -179,14 +180,14 @@ test_expect_success 'fsck with other accepted skipList input (comments & empty l
$(test_oid 001)
EOF
test_must_fail git -c fsck.skipList=SKIP.with-comment fsck 2>err-with-comment &&
- test_i18ngrep "missingEmail" err-with-comment &&
+ test_grep "missingEmail" err-with-comment &&
cat >SKIP.with-empty-line <<-EOF &&
$(test_oid 001)
$(test_oid 002)
EOF
test_must_fail git -c fsck.skipList=SKIP.with-empty-line fsck 2>err-with-empty-line &&
- test_i18ngrep "missingEmail" err-with-empty-line
+ test_grep "missingEmail" err-with-empty-line
'
test_expect_success 'fsck no garbage output from comments & empty lines errors' '
@@ -197,7 +198,7 @@ test_expect_success 'fsck no garbage output from comments & empty lines errors'
test_expect_success 'fsck with invalid abbreviated skipList input' '
echo $commit | test_copy_bytes 20 >SKIP.abbreviated &&
test_must_fail git -c fsck.skipList=SKIP.abbreviated fsck 2>err-abbreviated &&
- test_i18ngrep "^fatal: invalid object name: " err-abbreviated
+ test_grep "^fatal: invalid object name: " err-abbreviated
'
test_expect_success 'fsck with exhaustive accepted skipList input (various types of comments etc.)' '
@@ -230,10 +231,10 @@ test_expect_success 'push with receive.fsck.skipList' '
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.*: does-not-exist" err &&
+ test_grep "could not open.*: 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 object name: \[core\]" err &&
+ test_grep "invalid object name: \[core\]" err &&
git --git-dir=dst/.git config receive.fsck.skipList SKIP &&
git push --porcelain dst bogus
@@ -259,10 +260,10 @@ test_expect_success 'fetch with fetch.fsck.skipList' '
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.*: does-not-exist" err &&
+ test_grep "could not open.*: 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 object name: \[core\]" err &&
+ test_grep "invalid object name: \[core\]" err &&
git --git-dir=dst/.git config fetch.fsck.skipList dst/.git/SKIP &&
git --git-dir=dst/.git fetch "file://$(pwd)" $refspec
@@ -270,7 +271,7 @@ test_expect_success 'fetch with fetch.fsck.skipList' '
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_grep "Unhandled message id: whatever" err
'
test_expect_success 'push with receive.fsck.missingEmail=warn' '
@@ -292,7 +293,7 @@ test_expect_success 'push with receive.fsck.missingEmail=warn' '
receive.fsck.missingEmail warn &&
git push --porcelain dst bogus >act 2>&1 &&
grep "missingEmail" act &&
- test_i18ngrep "Skipping unknown msg id.*whatever" act &&
+ test_grep "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 &&
@@ -320,7 +321,7 @@ test_expect_success 'fetch with fetch.fsck.missingEmail=warn' '
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 &&
+ test_grep "Skipping unknown msg id.*whatever" act &&
rm -rf dst &&
git init dst &&
git --git-dir=dst/.git config fetch.fsckobjects true &&
@@ -352,4 +353,21 @@ test_expect_success \
grep "Cannot demote unterminatedheader" act
'
+test_expect_success 'badFilemode is not a strict error' '
+ git init --bare badmode.git &&
+ tree=$(
+ cd badmode.git &&
+ blob=$(echo blob | git hash-object -w --stdin | hex2oct) &&
+ printf "123456 foo\0${blob}" |
+ git hash-object -t tree --stdin -w --literally
+ ) &&
+
+ rm -rf dst.git &&
+ git init --bare dst.git &&
+ git -C dst.git config transfer.fsckObjects true &&
+
+ git -C badmode.git push ../dst.git $tree:refs/tags/tree 2>err &&
+ grep "$tree: badFilemode" err
+'
+
test_done
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index e6e3c8f..7789ff1 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -2,9 +2,6 @@
test_description='git remote porcelain-ish'
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
. ./test-lib.sh
setup_repository () {
@@ -81,6 +78,40 @@ test_expect_success 'add another remote' '
)
'
+test_expect_success 'setup bare clone for server' '
+ git clone --bare "file://$(pwd)/one" srv.bare &&
+ git -C srv.bare config --local uploadpack.allowfilter 1 &&
+ git -C srv.bare config --local uploadpack.allowanysha1inwant 1
+'
+
+test_expect_success 'filters for promisor remotes are listed by git remote -v' '
+ test_when_finished "rm -rf pc" &&
+ git clone --filter=blob:none "file://$(pwd)/srv.bare" pc &&
+ git -C pc remote -v >out &&
+ grep "srv.bare (fetch) \[blob:none\]" out &&
+
+ git -C pc config remote.origin.partialCloneFilter object:type=commit &&
+ git -C pc remote -v >out &&
+ grep "srv.bare (fetch) \[object:type=commit\]" out
+'
+
+test_expect_success 'filters should not be listed for non promisor remotes (remote -v)' '
+ test_when_finished "rm -rf pc" &&
+ git clone one pc &&
+ git -C pc remote -v >out &&
+ ! grep "(fetch) \[.*\]" out
+'
+
+test_expect_success 'filters are listed by git remote -v only' '
+ test_when_finished "rm -rf pc" &&
+ git clone --filter=blob:none "file://$(pwd)/srv.bare" pc &&
+ git -C pc remote >out &&
+ ! grep "\[blob:none\]" out &&
+
+ git -C pc remote show >out &&
+ ! grep "\[blob:none\]" out
+'
+
test_expect_success 'check remote-tracking' '
(
cd test &&
@@ -210,6 +241,26 @@ test_expect_success 'add invalid foreign_vcs remote' '
test_cmp expect actual
'
+test_expect_success 'without subcommand' '
+ echo origin >expect &&
+ git -C test remote >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'without subcommand accepts -v' '
+ cat >expect <<-EOF &&
+ origin $(pwd)/one (fetch)
+ origin $(pwd)/one (push)
+ EOF
+ git -C test remote -v >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'without subcommand does not take arguments' '
+ test_expect_code 129 git -C test remote origin 2>err &&
+ grep "^error: unknown subcommand:" err
+'
+
cat >test/expect <<EOF
* remote origin
Fetch URL: $(pwd)/one
@@ -271,6 +322,52 @@ test_expect_success 'show' '
)
'
+cat >expect <<EOF
+* remote origin
+ Fetch URL: $(pwd)/one
+ Push URL: $(pwd)/one
+ HEAD branch: main
+ Remote branches:
+ main skipped
+ side tracked
+ Local branches configured for 'git pull':
+ ahead merges with remote main
+ main merges with remote main
+ Local refs configured for 'git push':
+ main pushes to main (local out of date)
+ main pushes to upstream (create)
+EOF
+
+test_expect_success 'show with negative refspecs' '
+ test_when_finished "git -C test config --unset-all --fixed-value remote.origin.fetch ^refs/heads/main" &&
+ git -C test config --add remote.origin.fetch ^refs/heads/main &&
+ git -C test remote show origin >output &&
+ test_cmp expect output
+'
+
+cat >expect <<EOF
+* remote origin
+ Fetch URL: $(pwd)/one
+ Push URL: $(pwd)/one
+ HEAD branch: main
+ Remote branches:
+ main new (next fetch will store in remotes/origin)
+ side stale (use 'git remote prune' to remove)
+ Local branches configured for 'git pull':
+ ahead merges with remote main
+ main merges with remote main
+ Local refs configured for 'git push':
+ main pushes to main (local out of date)
+ main pushes to upstream (create)
+EOF
+
+test_expect_failure 'show stale with negative refspecs' '
+ test_when_finished "git -C test config --unset-all --fixed-value remote.origin.fetch ^refs/heads/side" &&
+ git -C test config --add remote.origin.fetch ^refs/heads/side &&
+ git -C test remote show origin >output &&
+ test_cmp expect output
+'
+
cat >test/expect <<EOF
* remote origin
Fetch URL: $(pwd)/one
@@ -756,7 +853,9 @@ test_expect_success 'rename a remote' '
(
cd four &&
git config branch.main.pushRemote origin &&
- git remote rename origin upstream &&
+ GIT_TRACE2_EVENT=$(pwd)/trace \
+ git remote rename --progress origin upstream &&
+ test_region progress "Renaming remote references" trace &&
grep "pushRemote" .git/config &&
test -z "$(git for-each-ref refs/remotes/origin)" &&
test "$(git symbolic-ref refs/remotes/upstream/HEAD)" = "refs/remotes/upstream/main" &&
@@ -803,6 +902,17 @@ test_expect_success 'rename a remote renames repo remote.pushDefault but keeps g
)
'
+test_expect_success 'rename handles remote without fetch refspec' '
+ git clone --bare one no-refspec.git &&
+ # confirm assumption that bare clone does not create refspec
+ test_expect_code 5 \
+ git -C no-refspec.git config --unset-all remote.origin.fetch &&
+ git -C no-refspec.git config remote.origin.url >expect &&
+ git -C no-refspec.git remote rename origin foo &&
+ git -C no-refspec.git config remote.foo.url >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'rename does not update a non-default fetch refspec' '
git clone one four.one &&
(
@@ -924,11 +1034,12 @@ test_expect_success 'migrate a remote from named file in $GIT_DIR/remotes' '
'
test_expect_success 'migrate a remote from named file in $GIT_DIR/branches' '
- git clone one six &&
+ git clone --template= one six &&
origin_url=$(pwd)/one &&
(
cd six &&
git remote rm origin &&
+ mkdir .git/branches &&
echo "$origin_url#main" >.git/branches/origin &&
git remote rename origin origin &&
test_path_is_missing .git/branches/origin &&
@@ -939,10 +1050,11 @@ test_expect_success 'migrate a remote from named file in $GIT_DIR/branches' '
'
test_expect_success 'migrate a remote from named file in $GIT_DIR/branches (2)' '
- git clone one seven &&
+ git clone --template= one seven &&
(
cd seven &&
git remote rm origin &&
+ mkdir .git/branches &&
echo "quux#foom" > .git/branches/origin &&
git remote rename origin origin &&
test_path_is_missing .git/branches/origin &&
@@ -963,7 +1075,7 @@ test_expect_success 'remote prune to cause a dangling symref' '
cd eight &&
git remote prune origin
) >err 2>&1 &&
- test_i18ngrep "has become dangling" err &&
+ test_grep "has become dangling" err &&
: And the dangling symref will not cause other annoying errors &&
(
@@ -975,7 +1087,7 @@ test_expect_success 'remote prune to cause a dangling symref' '
cd eight &&
test_must_fail git branch nomore origin
) 2>err &&
- test_i18ngrep "dangling symref" err
+ test_grep "dangling symref" err
'
test_expect_success 'show empty remote' '
@@ -1307,7 +1419,7 @@ test_expect_success 'extra args: setup' '
test_extra_arg () {
test_expect_success "extra args: $*" "
test_must_fail git remote $* bogus_extra_arg 2>actual &&
- test_i18ngrep '^usage:' actual
+ test_grep '^usage:' actual
"
}
@@ -1332,7 +1444,6 @@ test_expect_success 'unqualified <dst> refspec DWIM and advice' '
(
cd test &&
git tag -a -m "Some tag" some-tag main &&
- exit_with=true &&
for type in commit tag tree blob
do
if test "$type" = "blob"
@@ -1342,15 +1453,14 @@ test_expect_success 'unqualified <dst> refspec DWIM and advice' '
oid=$(git rev-parse some-tag^{$type})
fi &&
test_must_fail git push origin $oid:dst 2>err &&
- test_i18ngrep "error: The destination you" err &&
- test_i18ngrep "hint: Did you mean" err &&
+ test_grep "error: The destination you" err &&
+ test_grep "hint: Did you mean" err &&
test_must_fail git -c advice.pushUnqualifiedRefName=false \
push origin $oid:dst 2>err &&
- test_i18ngrep "error: The destination you" err &&
- test_i18ngrep ! "hint: Did you mean" err ||
- exit_with=false
- done &&
- $exit_with
+ test_grep "error: The destination you" err &&
+ test_grep ! "hint: Did you mean" err ||
+ exit 1
+ done
)
'
@@ -1369,16 +1479,16 @@ test_expect_success 'refs/remotes/* <src> refspec and unqualified <dst> DWIM and
git fetch --no-tags two &&
test_must_fail git push origin refs/remotes/two/another:dst 2>err &&
- test_i18ngrep "error: The destination you" err &&
+ test_grep "error: The destination you" err &&
test_must_fail git push origin refs/remotes/tags-from-two/my-tag:dst-tag 2>err &&
- test_i18ngrep "error: The destination you" err &&
+ test_grep "error: The destination you" err &&
test_must_fail git push origin refs/remotes/trees-from-two/my-head-tree:dst-tree 2>err &&
- test_i18ngrep "error: The destination you" err &&
+ test_grep "error: The destination you" err &&
test_must_fail git push origin refs/remotes/blobs-from-two/my-file-blob:dst-blob 2>err &&
- test_i18ngrep "error: The destination you" err
+ test_grep "error: The destination you" err
)
'
diff --git a/t/t5506-remote-groups.sh b/t/t5506-remote-groups.sh
index 8f150c0..0e17617 100755
--- a/t/t5506-remote-groups.sh
+++ b/t/t5506-remote-groups.sh
@@ -4,6 +4,7 @@ test_description='git remote group handling'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
mark() {
@@ -98,4 +99,13 @@ test_expect_success 'updating remote name updates that remote' '
! repo_fetched two
'
+test_expect_success 'updating group in parallel with a duplicate remote does not fail (fetch)' '
+ mark fetch-group-duplicate &&
+ update_repo one &&
+ git config --add remotes.duplicate one &&
+ git config --add remotes.duplicate one &&
+ git -c fetch.parallel=2 remote update duplicate &&
+ repo_fetched one
+'
+
test_done
diff --git a/t/t5507-remote-environment.sh b/t/t5507-remote-environment.sh
index e614929..c6a6957 100755
--- a/t/t5507-remote-environment.sh
+++ b/t/t5507-remote-environment.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='check environment showed to remote side of transports'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'set up "remote" push situation' '
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index a0faf0d..33d34d5 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -5,9 +5,6 @@ test_description='Per branch config variables affects "git fetch".
'
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-bundle.sh
@@ -40,11 +37,11 @@ test_expect_success "clone and setup child repos" '
git config branch.main.remote two &&
git config branch.main.merge refs/heads/one &&
mkdir -p .git/remotes &&
- {
- echo "URL: ../two/.git/"
- echo "Pull: refs/heads/main:refs/heads/two"
- echo "Pull: refs/heads/one:refs/heads/one"
- } >.git/remotes/two
+ cat >.git/remotes/two <<-\EOF
+ URL: ../two/.git/
+ Pull: refs/heads/main:refs/heads/two
+ Pull: refs/heads/one:refs/heads/one
+ EOF
) &&
git clone . bundle &&
git clone . seven
@@ -71,7 +68,7 @@ test_expect_success "fetch test for-merge" '
main_in_two=$(cd ../two && git rev-parse main) &&
one_in_two=$(cd ../two && git rev-parse one) &&
{
- echo "$one_in_two "
+ echo "$one_in_two " &&
echo "$main_in_two not-for-merge"
} >expected &&
cut -f -2 .git/FETCH_HEAD >actual &&
@@ -167,6 +164,18 @@ test_expect_success 'fetch --prune --tags with refspec prunes based on refspec'
git rev-parse sometag
'
+test_expect_success REFFILES 'fetch --prune fails to delete branches' '
+ cd "$D" &&
+ git clone . prune-fail &&
+ cd prune-fail &&
+ git update-ref refs/remotes/origin/extrabranch main &&
+ git pack-refs --all &&
+ : this will prevent --prune from locking packed-refs for deleting refs, but adding loose refs still succeeds &&
+ >.git/packed-refs.new &&
+
+ test_must_fail git fetch --prune origin
+'
+
test_expect_success 'fetch --atomic works with a single branch' '
test_when_finished "rm -rf \"$D\"/atomic" &&
@@ -265,7 +274,7 @@ test_expect_success 'fetch --atomic executes a single reference transaction only
EOF
rm -f atomic/actual &&
- write_script atomic/.git/hooks/reference-transaction <<-\EOF &&
+ test_hook -C atomic reference-transaction <<-\EOF &&
( echo "$*" && cat ) >>actual
EOF
@@ -298,7 +307,7 @@ test_expect_success 'fetch --atomic aborts all reference updates if hook aborts'
EOF
rm -f atomic/actual &&
- write_script atomic/.git/hooks/reference-transaction <<-\EOF &&
+ test_hook -C atomic/.git reference-transaction <<-\EOF &&
( echo "$*" && cat ) >>actual
exit 1
EOF
@@ -326,7 +335,7 @@ test_expect_success 'fetch --atomic --append appends to FETCH_HEAD' '
test_line_count = 2 atomic/.git/FETCH_HEAD &&
cp atomic/.git/FETCH_HEAD expected &&
- write_script atomic/.git/hooks/reference-transaction <<-\EOF &&
+ test_hook -C atomic reference-transaction <<-\EOF &&
exit 1
EOF
@@ -407,9 +416,9 @@ test_expect_success 'fetch uses remote ref names to describe new refs' '
(
cd descriptive &&
git fetch o 2>actual &&
- test_i18ngrep "new branch.* -> refs/crazyheads/descriptive-branch$" actual &&
- test_i18ngrep "new tag.* -> descriptive-tag$" actual &&
- test_i18ngrep "new ref.* -> crazy$" actual
+ test_grep "new branch.* -> refs/crazyheads/descriptive-branch$" actual &&
+ test_grep "new tag.* -> descriptive-tag$" actual &&
+ test_grep "new ref.* -> crazy$" actual
) &&
git checkout main
'
@@ -550,7 +559,7 @@ test_expect_success 'bundle should record HEAD correctly' '
git bundle list-heads bundle5 >actual &&
for h in HEAD refs/heads/main
do
- echo "$(git rev-parse --verify $h) $h"
+ echo "$(git rev-parse --verify $h) $h" || return 1
done >expect &&
test_cmp expect actual
@@ -782,6 +791,7 @@ test_expect_success 'fetch.writeCommitGraph' '
'
test_expect_success 'fetch.writeCommitGraph with submodules' '
+ test_config_global protocol.file.allow always &&
git clone dups super &&
(
cd super &&
@@ -793,10 +803,19 @@ test_expect_success 'fetch.writeCommitGraph with submodules' '
cd super-clone &&
rm -rf .git/objects/info &&
git -c fetch.writeCommitGraph=true fetch origin &&
- test_path_is_file .git/objects/info/commit-graphs/commit-graph-chain
+ test_path_is_file .git/objects/info/commit-graphs/commit-graph-chain &&
+ git -c fetch.writeCommitGraph=true fetch --recurse-submodules origin
)
'
+# fetches from first configured url
+test_expect_success 'fetch from multiple configured URLs in single remote' '
+ git init url1 &&
+ git remote add multipleurls url1 &&
+ git remote set-url --add multipleurls url2 &&
+ git fetch multipleurls
+'
+
# configured prune tests
set_config_tristate () {
@@ -845,7 +864,11 @@ test_configured_prune_type () {
then
new_cmdline=$cmdline_setup
else
- new_cmdline=$(printf "%s" "$cmdline" | perl -pe 's[origin(?!/)]["'"$remote_url"'"]g')
+ new_cmdline=$(perl -e '
+ my ($cmdline, $url) = @ARGV;
+ $cmdline =~ s[origin(?!/)][quotemeta($url)]ge;
+ print $cmdline;
+ ' -- "$cmdline" "$remote_url")
fi
if test "$fetch_prune_tags" = 'true' ||
@@ -1092,63 +1115,65 @@ test_expect_success 'fetching with auto-gc does not lock up' '
git config gc.autoPackLimit 1 &&
git config gc.autoDetach false &&
GIT_ASK_YESNO="$D/askyesno" git fetch --verbose >fetch.out 2>&1 &&
- test_i18ngrep "Auto packing the repository" fetch.out &&
+ test_grep "Auto packing the repository" fetch.out &&
! grep "Should I try again" fetch.out
)
'
-test_expect_success 'fetch aligned output' '
- git clone . full-output &&
- test_commit looooooooooooong-tag &&
- (
- cd full-output &&
- git -c fetch.output=full fetch origin >actual 2>&1 &&
- grep -e "->" actual | cut -c 22- >../actual
- ) &&
- cat >expect <<-\EOF &&
- main -> origin/main
- looooooooooooong-tag -> looooooooooooong-tag
- EOF
- test_cmp expect actual
-'
+for section in fetch transfer
+do
+ test_expect_success "$section.hideRefs affects connectivity check" '
+ GIT_TRACE="$PWD"/trace git -c $section.hideRefs=refs -c \
+ $section.hideRefs="!refs/tags/" fetch &&
+ grep "git rev-list .*--exclude-hidden=fetch" trace
+ '
+done
+
+test_expect_success 'prepare source branch' '
+ echo one >onebranch &&
+ git checkout --orphan onebranch &&
+ git rm --cached -r . &&
+ git add onebranch &&
+ git commit -m onebranch &&
+ git rev-list --objects onebranch -- >actual &&
+ # 3 objects should be created, at least ...
+ test 3 -le $(wc -l <actual)
+'
+
+validate_store_type () {
+ git -C dest count-objects -v >actual &&
+ case "$store_type" in
+ packed)
+ grep "^count: 0$" actual ;;
+ loose)
+ grep "^packs: 0$" actual ;;
+ esac || {
+ echo "store_type is $store_type"
+ cat actual
+ false
+ }
+}
-test_expect_success 'fetch compact output' '
- git clone . compact &&
- test_commit extraaa &&
- (
- cd compact &&
- git -c fetch.output=compact fetch origin >actual 2>&1 &&
- grep -e "->" actual | cut -c 22- >../actual
- ) &&
- cat >expect <<-\EOF &&
- main -> origin/*
- extraaa -> *
- EOF
- test_cmp expect actual
-'
+test_unpack_limit () {
+ store_type=$1
-test_expect_success '--no-show-forced-updates' '
- mkdir forced-updates &&
- (
- cd forced-updates &&
- git init &&
- test_commit 1 &&
- test_commit 2
- ) &&
- git clone forced-updates forced-update-clone &&
- git clone forced-updates no-forced-update-clone &&
- git -C forced-updates reset --hard HEAD~1 &&
- (
- cd forced-update-clone &&
- git fetch --show-forced-updates origin 2>output &&
- test_i18ngrep "(forced update)" output
- ) &&
- (
- cd no-forced-update-clone &&
- git fetch --no-show-forced-updates origin 2>output &&
- test_i18ngrep ! "(forced update)" output
- )
-'
+ case "$store_type" in
+ packed) fetch_limit=1 transfer_limit=10000 ;;
+ loose) fetch_limit=10000 transfer_limit=1 ;;
+ esac
+
+ test_expect_success "fetch trumps transfer limit" '
+ rm -fr dest &&
+ git --bare init dest &&
+ git -C dest config fetch.unpacklimit $fetch_limit &&
+ git -C dest config transfer.unpacklimit $transfer_limit &&
+ git -C dest fetch .. onebranch &&
+ validate_store_type
+ '
+}
+
+test_unpack_limit packed
+test_unpack_limit loose
setup_negotiation_tip () {
SERVER="$1"
diff --git a/t/t5511-refspec.sh b/t/t5511-refspec.sh
index be025b9..fc55681 100755
--- a/t/t5511-refspec.sh
+++ b/t/t5511-refspec.sh
@@ -2,6 +2,7 @@
test_description='refspec parsing'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_refspec () {
diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh
index f53f588..5dbe107 100755
--- a/t/t5512-ls-remote.sh
+++ b/t/t5512-ls-remote.sh
@@ -15,6 +15,23 @@ generate_references () {
done
}
+test_expect_success 'set up fake upload-pack' '
+ # This can be used to simulate an upload-pack that just shows the
+ # contents of the "input" file (prepared with the test-tool pkt-line
+ # helper), and does not do any negotiation (since ls-remote does not
+ # need it).
+ write_script cat-input <<-\EOF
+ # send our initial advertisement/response
+ cat input
+ # soak up the flush packet from the client
+ cat
+ EOF
+'
+
+test_expect_success 'dies when no remote found' '
+ test_must_fail git ls-remote
+'
+
test_expect_success setup '
>file &&
git add file &&
@@ -30,7 +47,8 @@ test_expect_success setup '
git show-ref -d >refs &&
sed -e "s/ / /" refs >>expected.all &&
- git remote add self "$(pwd)/.git"
+ git remote add self "$(pwd)/.git" &&
+ git remote add self2 "."
'
test_expect_success 'ls-remote --tags .git' '
@@ -83,11 +101,17 @@ test_expect_success 'ls-remote --sort="-refname" --tags self' '
test_cmp expect actual
'
-test_expect_success 'dies when no remote specified and no default remotes found' '
+test_expect_success 'dies when no remote specified, multiple remotes found, and no default specified' '
test_must_fail git ls-remote
'
-test_expect_success 'use "origin" when no remote specified' '
+test_expect_success 'succeeds when no remote specified but only one found' '
+ test_when_finished git remote add self2 "." &&
+ git remote remove self2 &&
+ git ls-remote
+'
+
+test_expect_success 'use "origin" when no remote specified and multiple found' '
URL="$(pwd)/.git" &&
echo "From $URL" >exp_err &&
@@ -220,22 +244,25 @@ test_expect_success 'protocol v2 supports hiderefs' '
test_expect_success 'ls-remote --symref' '
git fetch origin &&
- echo "ref: refs/heads/main HEAD" >expect &&
+ echo "ref: refs/heads/main HEAD" >expect.v2 &&
generate_references \
HEAD \
- refs/heads/main >>expect &&
+ refs/heads/main >>expect.v2 &&
+ echo "ref: refs/remotes/origin/main refs/remotes/origin/HEAD" >>expect.v2 &&
oid=$(git rev-parse HEAD) &&
- echo "$oid refs/remotes/origin/HEAD" >>expect &&
+ echo "$oid refs/remotes/origin/HEAD" >>expect.v2 &&
generate_references \
refs/remotes/origin/main \
refs/tags/mark \
refs/tags/mark1.1 \
refs/tags/mark1.10 \
- refs/tags/mark1.2 >>expect &&
- # Protocol v2 supports sending symrefs for refs other than HEAD, so use
- # protocol v0 here.
- GIT_TEST_PROTOCOL_VERSION=0 git ls-remote --symref >actual &&
- test_cmp expect actual
+ refs/tags/mark1.2 >>expect.v2 &&
+ # v0 does not show non-HEAD symrefs
+ grep -v "ref: refs/remotes" <expect.v2 >expect.v0 &&
+ git -c protocol.version=0 ls-remote --symref >actual.v0 &&
+ test_cmp expect.v0 actual.v0 &&
+ git -c protocol.version=2 ls-remote --symref >actual.v2 &&
+ test_cmp expect.v2 actual.v2
'
test_expect_success 'ls-remote with filtered symref (refname)' '
@@ -244,76 +271,41 @@ test_expect_success 'ls-remote with filtered symref (refname)' '
ref: refs/heads/main HEAD
$rev HEAD
EOF
- # Protocol v2 supports sending symrefs for refs other than HEAD, so use
- # protocol v0 here.
- GIT_TEST_PROTOCOL_VERSION=0 git ls-remote --symref . HEAD >actual &&
+ git ls-remote --symref . HEAD >actual &&
test_cmp expect actual
'
-test_expect_failure 'ls-remote with filtered symref (--heads)' '
+test_expect_success 'ls-remote with filtered symref (--heads)' '
git symbolic-ref refs/heads/foo refs/tags/mark &&
- cat >expect <<-EOF &&
+ cat >expect.v2 <<-EOF &&
ref: refs/tags/mark refs/heads/foo
$rev refs/heads/foo
$rev refs/heads/main
EOF
- # Protocol v2 supports sending symrefs for refs other than HEAD, so use
- # protocol v0 here.
- GIT_TEST_PROTOCOL_VERSION=0 git ls-remote --symref --heads . >actual &&
- test_cmp expect actual
+ grep -v "^ref: refs/tags/" <expect.v2 >expect.v0 &&
+ git -c protocol.version=0 ls-remote --symref --heads . >actual.v0 &&
+ test_cmp expect.v0 actual.v0 &&
+ git -c protocol.version=2 ls-remote --symref --heads . >actual.v2 &&
+ test_cmp expect.v2 actual.v2
'
-test_expect_success 'ls-remote --symref omits filtered-out matches' '
- cat >expect <<-EOF &&
- $rev refs/heads/foo
- $rev refs/heads/main
+test_expect_success 'indicate no refs in v0 standards-compliant empty remote' '
+ # Git does not produce an output like this, but it does match the
+ # standard and is produced by other implementations like JGit. So
+ # hard-code the case we care about.
+ #
+ # The actual capabilities do not matter; there are none that would
+ # change how ls-remote behaves.
+ oid=0000000000000000000000000000000000000000 &&
+ test-tool pkt-line pack >input.q <<-EOF &&
+ $oid capabilities^{}Qcaps-go-here
+ 0000
EOF
- # Protocol v2 supports sending symrefs for refs other than HEAD, so use
- # protocol v0 here.
- GIT_TEST_PROTOCOL_VERSION=0 git ls-remote --symref --heads . >actual &&
- test_cmp expect actual &&
- GIT_TEST_PROTOCOL_VERSION=0 git ls-remote --symref . "refs/heads/*" >actual &&
- test_cmp expect actual
-'
-
-test_lazy_prereq GIT_DAEMON '
- test_bool_env GIT_TEST_GIT_DAEMON true
-'
+ q_to_nul <input.q >input &&
-# This test spawns a daemon, so run it only if the user would be OK with
-# testing with git-daemon.
-test_expect_success PIPE,JGIT,GIT_DAEMON 'indicate no refs in standards-compliant empty remote' '
- test_set_port JGIT_DAEMON_PORT &&
- JGIT_DAEMON_PID= &&
- git init --bare empty.git &&
- >empty.git/git-daemon-export-ok &&
- mkfifo jgit_daemon_output &&
- {
- jgit daemon --port="$JGIT_DAEMON_PORT" . >jgit_daemon_output &
- JGIT_DAEMON_PID=$!
- } &&
- test_when_finished kill "$JGIT_DAEMON_PID" &&
- {
- read line &&
- case $line in
- Exporting*)
- ;;
- *)
- echo "Expected: Exporting" &&
- false;;
- esac &&
- read line &&
- case $line in
- "Listening on"*)
- ;;
- *)
- echo "Expected: Listening on" &&
- false;;
- esac
- } <jgit_daemon_output &&
# --exit-code asks the command to exit with 2 when no
# matching refs are found.
- test_expect_code 2 git ls-remote --exit-code git://localhost:$JGIT_DAEMON_PORT/empty.git
+ test_expect_code 2 git ls-remote --exit-code --upload-pack=./cat-input .
'
test_expect_success 'ls-remote works outside repository' '
@@ -328,14 +320,14 @@ test_expect_success 'ls-remote works outside repository' '
test_expect_success 'ls-remote --sort fails gracefully outside repository' '
# Use a sort key that requires access to the referenced objects.
nongit test_must_fail git ls-remote --sort=authordate "$TRASH_DIRECTORY" 2>err &&
- test_i18ngrep "^fatal: not a git repository, but the field '\''authordate'\'' requires access to object data" err
+ test_grep "^fatal: not a git repository, but the field '\''authordate'\'' requires access to object data" err
'
test_expect_success 'ls-remote patterns work with all protocol versions' '
git for-each-ref --format="%(objectname) %(refname)" \
refs/heads/main refs/remotes/origin/main >expect &&
- git -c protocol.version=1 ls-remote . main >actual.v1 &&
- test_cmp expect actual.v1 &&
+ git -c protocol.version=0 ls-remote . main >actual.v0 &&
+ test_cmp expect actual.v0 &&
git -c protocol.version=2 ls-remote . main >actual.v2 &&
test_cmp expect actual.v2
'
@@ -343,10 +335,49 @@ test_expect_success 'ls-remote patterns work with all protocol versions' '
test_expect_success 'ls-remote prefixes work with all protocol versions' '
git for-each-ref --format="%(objectname) %(refname)" \
refs/heads/ refs/tags/ >expect &&
- git -c protocol.version=1 ls-remote --heads --tags . >actual.v1 &&
- test_cmp expect actual.v1 &&
+ git -c protocol.version=0 ls-remote --heads --tags . >actual.v0 &&
+ test_cmp expect actual.v0 &&
git -c protocol.version=2 ls-remote --heads --tags . >actual.v2 &&
test_cmp expect actual.v2
'
+test_expect_success 'v0 clients can handle multiple symrefs' '
+ # Modern versions of Git will not return multiple symref capabilities
+ # for v0, so we have to hard-code the response. Note that we will
+ # always use both v0 and object-format=sha1 here, as the hard-coded
+ # response reflects a server that only supports those.
+ oid=1234567890123456789012345678901234567890 &&
+ symrefs="symref=refs/remotes/origin/HEAD:refs/remotes/origin/main" &&
+ symrefs="$symrefs symref=HEAD:refs/heads/main" &&
+
+ # Likewise we want to make sure our parser is not fooled by the string
+ # "symref" appearing as part of an earlier cap. But there is no way to
+ # do that via upload-pack, as arbitrary strings can appear only in a
+ # "symref" value itself (where we skip past the values as a whole)
+ # and "agent" (which always appears after "symref", so putting our
+ # parser in a confused state is less interesting).
+ caps="some other caps including a-fake-symref-cap" &&
+
+ test-tool pkt-line pack >input.q <<-EOF &&
+ $oid HEADQ$caps $symrefs
+ $oid refs/heads/main
+ $oid refs/remotes/origin/HEAD
+ $oid refs/remotes/origin/main
+ 0000
+ EOF
+ q_to_nul <input.q >input &&
+
+ cat >expect <<-EOF &&
+ ref: refs/heads/main HEAD
+ $oid HEAD
+ $oid refs/heads/main
+ ref: refs/remotes/origin/main refs/remotes/origin/HEAD
+ $oid refs/remotes/origin/HEAD
+ $oid refs/remotes/origin/main
+ EOF
+
+ git ls-remote --symref --upload-pack=./cat-input . >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t5513-fetch-track.sh b/t/t5513-fetch-track.sh
index 65d1e05..c46c4db 100755
--- a/t/t5513-fetch-track.sh
+++ b/t/t5513-fetch-track.sh
@@ -2,6 +2,7 @@
test_description='fetch follows remote-tracking branches correctly'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t5514-fetch-multiple.sh b/t/t5514-fetch-multiple.sh
index 511ba3b..25772c8 100755
--- a/t/t5514-fetch-multiple.sh
+++ b/t/t5514-fetch-multiple.sh
@@ -24,6 +24,15 @@ setup_repository () {
)
}
+setup_test_clone () {
+ test_dir="$1" &&
+ git clone one "$test_dir" &&
+ for r in one two three
+ do
+ git -C "$test_dir" remote add "$r" "../$r" || return 1
+ done
+}
+
test_expect_success setup '
setup_repository one &&
setup_repository two &&
@@ -58,6 +67,13 @@ test_expect_success 'git fetch --all' '
test_cmp expect output)
'
+test_expect_success 'git fetch --all --no-write-fetch-head' '
+ (cd test &&
+ rm -f .git/FETCH_HEAD &&
+ git fetch --all --no-write-fetch-head &&
+ test_path_is_missing .git/FETCH_HEAD)
+'
+
test_expect_success 'git fetch --all should continue if a remote has errors' '
(git clone one test2 &&
cd test2 &&
@@ -193,8 +209,165 @@ test_expect_success 'parallel' '
test_must_fail env GIT_TRACE="$PWD/trace" \
git fetch --jobs=2 --multiple one two 2>err &&
grep "preparing to run up to 2 tasks" trace &&
- test_i18ngrep "could not fetch .one.*128" err &&
- test_i18ngrep "could not fetch .two.*128" err
+ test_grep "could not fetch .one.*128" err &&
+ test_grep "could not fetch .two.*128" err
+'
+
+test_expect_success 'git fetch --multiple --jobs=0 picks a default' '
+ (cd test &&
+ git fetch --multiple --jobs=0)
+'
+
+create_fetch_all_expect () {
+ cat >expect <<-\EOF
+ one/main
+ one/side
+ origin/HEAD -> origin/main
+ origin/main
+ origin/side
+ three/another
+ three/main
+ three/side
+ two/another
+ two/main
+ two/side
+ EOF
+}
+
+for fetch_all in true false
+do
+ test_expect_success "git fetch --all (works with fetch.all = $fetch_all)" '
+ test_dir="test_fetch_all_$fetch_all" &&
+ setup_test_clone "$test_dir" &&
+ (
+ cd "$test_dir" &&
+ git config fetch.all $fetch_all &&
+ git fetch --all &&
+ create_fetch_all_expect &&
+ git branch -r >actual &&
+ test_cmp expect actual
+ )
+ '
+done
+
+test_expect_success 'git fetch (fetch all remotes with fetch.all = true)' '
+ setup_test_clone test9 &&
+ (
+ cd test9 &&
+ git config fetch.all true &&
+ git fetch &&
+ git branch -r >actual &&
+ create_fetch_all_expect &&
+ test_cmp expect actual
+ )
+'
+
+create_fetch_one_expect () {
+ cat >expect <<-\EOF
+ one/main
+ one/side
+ origin/HEAD -> origin/main
+ origin/main
+ origin/side
+ EOF
+}
+
+test_expect_success 'git fetch one (explicit remote overrides fetch.all)' '
+ setup_test_clone test10 &&
+ (
+ cd test10 &&
+ git config fetch.all true &&
+ git fetch one &&
+ create_fetch_one_expect &&
+ git branch -r >actual &&
+ test_cmp expect actual
+ )
+'
+
+create_fetch_two_as_origin_expect () {
+ cat >expect <<-\EOF
+ origin/HEAD -> origin/main
+ origin/another
+ origin/main
+ origin/side
+ EOF
+}
+
+test_expect_success 'git config fetch.all false (fetch only default remote)' '
+ setup_test_clone test11 &&
+ (
+ cd test11 &&
+ git config fetch.all false &&
+ git remote set-url origin ../two &&
+ git fetch &&
+ create_fetch_two_as_origin_expect &&
+ git branch -r >actual &&
+ test_cmp expect actual
+ )
+'
+
+for fetch_all in true false
+do
+ test_expect_success "git fetch --no-all (fetch only default remote with fetch.all = $fetch_all)" '
+ test_dir="test_no_all_fetch_all_$fetch_all" &&
+ setup_test_clone "$test_dir" &&
+ (
+ cd "$test_dir" &&
+ git config fetch.all $fetch_all &&
+ git remote set-url origin ../two &&
+ git fetch --no-all &&
+ create_fetch_two_as_origin_expect &&
+ git branch -r >actual &&
+ test_cmp expect actual
+ )
+ '
+done
+
+test_expect_success 'git fetch --no-all (fetch only default remote without fetch.all)' '
+ setup_test_clone test12 &&
+ (
+ cd test12 &&
+ git config --unset-all fetch.all || true &&
+ git remote set-url origin ../two &&
+ git fetch --no-all &&
+ create_fetch_two_as_origin_expect &&
+ git branch -r >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'git fetch --all --no-all (fetch only default remote)' '
+ setup_test_clone test13 &&
+ (
+ cd test13 &&
+ git remote set-url origin ../two &&
+ git fetch --all --no-all &&
+ create_fetch_two_as_origin_expect &&
+ git branch -r >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'git fetch --no-all one (fetch only explicit remote)' '
+ setup_test_clone test14 &&
+ (
+ cd test14 &&
+ git fetch --no-all one &&
+ create_fetch_one_expect &&
+ git branch -r >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'git fetch --no-all --all (fetch all remotes)' '
+ setup_test_clone test15 &&
+ (
+ cd test15 &&
+ git fetch --no-all --all &&
+ create_fetch_all_expect &&
+ git branch -r >actual &&
+ test_cmp expect actual
+ )
'
test_done
diff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh
index 50f1410..c100a80 100755
--- a/t/t5515-fetch-merge-logic.sh
+++ b/t/t5515-fetch-merge-logic.sh
@@ -14,6 +14,7 @@ export GIT_TEST_PROTOCOL_VERSION
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
build_script () {
@@ -105,19 +106,19 @@ test_expect_success setup '
remotes="$remotes config-glob" &&
mkdir -p .git/remotes &&
- {
- echo "URL: ../.git/"
- echo "Pull: refs/heads/main:remotes/rem/main"
- echo "Pull: refs/heads/one:remotes/rem/one"
- echo "Pull: two:remotes/rem/two"
- echo "Pull: refs/heads/three:remotes/rem/three"
- } >.git/remotes/remote-explicit &&
+ cat >.git/remotes/remote-explicit <<-\EOF &&
+ URL: ../.git/
+ Pull: refs/heads/main:remotes/rem/main
+ Pull: refs/heads/one:remotes/rem/one
+ Pull: two:remotes/rem/two
+ Pull: refs/heads/three:remotes/rem/three
+ EOF
remotes="$remotes remote-explicit" &&
- {
- echo "URL: ../.git/"
- echo "Pull: refs/heads/*:refs/remotes/rem/*"
- } >.git/remotes/remote-glob &&
+ cat >.git/remotes/remote-glob <<-\EOF &&
+ URL: ../.git/
+ Pull: refs/heads/*:refs/remotes/rem/*
+ EOF
remotes="$remotes remote-glob" &&
mkdir -p .git/branches &&
@@ -133,7 +134,7 @@ test_expect_success setup '
git config branch.br-$remote-merge.merge refs/heads/three &&
git config branch.br-$remote-octopus.remote $remote &&
git config branch.br-$remote-octopus.merge refs/heads/one &&
- git config --add branch.br-$remote-octopus.merge two
+ git config --add branch.br-$remote-octopus.merge two || return 1
done &&
build_script sed_script
'
@@ -191,17 +192,17 @@ do
cp "$expect_r" expect_r &&
convert_expected expect_r sed_script &&
{
- echo "# $cmd"
- set x $cmd; shift
- git symbolic-ref HEAD refs/heads/$1 ; shift
- rm -f .git/FETCH_HEAD
+ echo "# $cmd" &&
+ set x $cmd && shift &&
+ git symbolic-ref HEAD refs/heads/$1 && shift &&
+ rm -f .git/FETCH_HEAD &&
git for-each-ref \
refs/heads refs/remotes/rem refs/tags |
while read val type refname
do
- git update-ref -d "$refname" "$val"
- done
- git fetch "$@" >/dev/null
+ git update-ref -d "$refname" "$val" || return 1
+ done &&
+ git fetch "$@" >/dev/null &&
cat .git/FETCH_HEAD
} >"$actual_f" &&
git show-ref >"$actual_r" &&
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 4db8edd..2e7c0e1 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -12,25 +12,24 @@ This test checks the following functionality:
* --porcelain output format
* hiderefs
* reflogs
+* URL validation
'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_CREATE_REPO_NO_TEMPLATE=1
. ./test-lib.sh
D=$(pwd)
mk_empty () {
repo_name="$1"
- rm -fr "$repo_name" &&
- mkdir "$repo_name" &&
- (
- cd "$repo_name" &&
- git init &&
- git config receive.denyCurrentBranch warn &&
- mv .git/hooks .git/hooks-disabled
- )
+ test_when_finished "rm -rf \"$repo_name\"" &&
+ test_path_is_missing "$repo_name" &&
+ git init --template= "$repo_name" &&
+ mkdir "$repo_name"/.git/hooks &&
+ git -C "$repo_name" config receive.denyCurrentBranch warn
}
mk_test () {
@@ -59,41 +58,29 @@ mk_test () {
mk_test_with_hooks() {
repo_name=$1
mk_test "$@" &&
- (
- cd "$repo_name" &&
- mkdir .git/hooks &&
- cd .git/hooks &&
-
- cat >pre-receive <<-'EOF' &&
- #!/bin/sh
- cat - >>pre-receive.actual
- EOF
-
- cat >update <<-'EOF' &&
- #!/bin/sh
- printf "%s %s %s\n" "$@" >>update.actual
- EOF
-
- cat >post-receive <<-'EOF' &&
- #!/bin/sh
- cat - >>post-receive.actual
- EOF
-
- cat >post-update <<-'EOF' &&
- #!/bin/sh
- for ref in "$@"
- do
- printf "%s\n" "$ref" >>post-update.actual
- done
- EOF
-
- chmod +x pre-receive update post-receive post-update
- )
+ test_hook -C "$repo_name" pre-receive <<-'EOF' &&
+ cat - >>pre-receive.actual
+ EOF
+
+ test_hook -C "$repo_name" update <<-'EOF' &&
+ printf "%s %s %s\n" "$@" >>update.actual
+ EOF
+
+ test_hook -C "$repo_name" post-receive <<-'EOF' &&
+ cat - >>post-receive.actual
+ EOF
+
+ test_hook -C "$repo_name" post-update <<-'EOF'
+ for ref in "$@"
+ do
+ printf "%s\n" "$ref" >>post-update.actual
+ done
+ EOF
}
mk_child() {
- rm -rf "$2" &&
- git clone "$1" "$2"
+ test_when_finished "rm -rf \"$2\"" &&
+ git clone --template= "$1" "$2"
}
check_push_result () {
@@ -133,6 +120,17 @@ test_expect_success setup '
'
+for cmd in push fetch
+do
+ for opt in ipv4 ipv6
+ do
+ test_expect_success "reject 'git $cmd --no-$opt'" '
+ test_must_fail git $cmd --no-$opt 2>err &&
+ grep "unknown option .no-$opt" err
+ '
+ done
+done
+
test_expect_success 'fetch without wildcard' '
mk_empty testrepo &&
(
@@ -197,36 +195,55 @@ grep_wrote () {
grep 'write_pack_file/wrote.*"value":"'$1'"' $2
}
-test_expect_success 'push with negotiation' '
- # Without negotiation
+test_expect_success 'push without negotiation' '
mk_empty testrepo &&
git push testrepo $the_first_commit:refs/remotes/origin/first_commit &&
test_commit -C testrepo unrelated_commit &&
git -C testrepo config receive.hideRefs refs/remotes/origin/first_commit &&
- echo now pushing without negotiation &&
+ test_when_finished "rm event" &&
GIT_TRACE2_EVENT="$(pwd)/event" git -c protocol.version=2 push testrepo refs/heads/main:refs/remotes/origin/main &&
- grep_wrote 5 event && # 2 commits, 2 trees, 1 blob
+ grep_wrote 5 event # 2 commits, 2 trees, 1 blob
+'
- # Same commands, but with negotiation
- rm event &&
+test_expect_success 'push with negotiation' '
mk_empty testrepo &&
git push testrepo $the_first_commit:refs/remotes/origin/first_commit &&
test_commit -C testrepo unrelated_commit &&
git -C testrepo config receive.hideRefs refs/remotes/origin/first_commit &&
- GIT_TRACE2_EVENT="$(pwd)/event" git -c protocol.version=2 -c push.negotiate=1 push testrepo refs/heads/main:refs/remotes/origin/main &&
+ test_when_finished "rm event" &&
+ GIT_TRACE2_EVENT="$(pwd)/event" \
+ git -c protocol.version=2 -c push.negotiate=1 \
+ push testrepo refs/heads/main:refs/remotes/origin/main &&
+ grep \"key\":\"total_rounds\",\"value\":\"1\" event &&
grep_wrote 2 event # 1 commit, 1 tree
'
test_expect_success 'push with negotiation proceeds anyway even if negotiation fails' '
- rm event &&
mk_empty testrepo &&
git push testrepo $the_first_commit:refs/remotes/origin/first_commit &&
test_commit -C testrepo unrelated_commit &&
git -C testrepo config receive.hideRefs refs/remotes/origin/first_commit &&
+ test_when_finished "rm event" &&
GIT_TEST_PROTOCOL_VERSION=0 GIT_TRACE2_EVENT="$(pwd)/event" \
git -c push.negotiate=1 push testrepo refs/heads/main:refs/remotes/origin/main 2>err &&
grep_wrote 5 event && # 2 commits, 2 trees, 1 blob
- test_i18ngrep "push negotiation failed" err
+ test_grep "push negotiation failed" err
+'
+
+test_expect_success 'push with negotiation does not attempt to fetch submodules' '
+ mk_empty submodule_upstream &&
+ test_commit -C submodule_upstream submodule_commit &&
+ test_config_global protocol.file.allow always &&
+ git submodule add ./submodule_upstream submodule &&
+ mk_empty testrepo &&
+ git push testrepo $the_first_commit:refs/remotes/origin/first_commit &&
+ test_commit -C testrepo unrelated_commit &&
+ git -C testrepo config receive.hideRefs refs/remotes/origin/first_commit &&
+ GIT_TRACE2_EVENT="$(pwd)/event" git -c submodule.recurse=true \
+ -c protocol.version=2 -c push.negotiate=1 \
+ push testrepo refs/heads/main:refs/remotes/origin/main 2>err &&
+ grep \"key\":\"total_rounds\",\"value\":\"1\" event &&
+ ! grep "Fetching submodule" err
'
test_expect_success 'push without wildcard' '
@@ -395,6 +412,11 @@ test_expect_success 'push with ambiguity' '
'
+test_expect_success 'push with onelevel ref' '
+ mk_test testrepo heads/main &&
+ test_must_fail git push testrepo HEAD:refs/onelevel
+'
+
test_expect_success 'push with colon-less refspec (1)' '
mk_test testrepo heads/frotz tags/frotz &&
@@ -541,6 +563,15 @@ do
done
+test_expect_success "push to remote with no explicit refspec and config remote.*.push = src:dest" '
+ mk_test testrepo heads/main &&
+ git checkout $the_first_commit &&
+ test_config remote.there.url testrepo &&
+ test_config remote.there.push refs/heads/main:refs/heads/main &&
+ git push there &&
+ check_push_result testrepo $the_commit heads/main
+'
+
test_expect_success 'push with remote.pushdefault' '
mk_test up_repo heads/main &&
mk_test down_repo heads/main &&
@@ -593,6 +624,26 @@ test_expect_success 'branch.*.pushremote config order is irrelevant' '
check_push_result two_repo $the_commit heads/main
'
+test_expect_success 'push rejects empty branch name entries' '
+ mk_test one_repo heads/main &&
+ test_config remote.one.url one_repo &&
+ test_config branch..remote one &&
+ test_config branch..merge refs/heads/ &&
+ test_config branch.main.remote one &&
+ test_config branch.main.merge refs/heads/main &&
+ test_must_fail git push 2>err &&
+ grep "bad config variable .branch\.\." err
+'
+
+test_expect_success 'push ignores "branch." config without subsection' '
+ mk_test one_repo heads/main &&
+ test_config remote.one.url one_repo &&
+ test_config branch.autoSetupMerge true &&
+ test_config branch.main.remote one &&
+ test_config branch.main.merge refs/heads/main &&
+ git push
+'
+
test_expect_success 'push with dry-run' '
mk_test testrepo heads/main &&
@@ -647,7 +698,6 @@ test_expect_success 'push does not update local refs on failure' '
mk_test testrepo heads/main &&
mk_child testrepo child &&
- mkdir testrepo/.git/hooks &&
echo "#!/no/frobnication/today" >testrepo/.git/hooks/pre-receive &&
chmod +x testrepo/.git/hooks/pre-receive &&
(
@@ -662,10 +712,10 @@ test_expect_success 'push does not update local refs on failure' '
test_expect_success 'allow deleting an invalid remote ref' '
- mk_test testrepo heads/main &&
+ mk_test testrepo heads/branch &&
rm -f testrepo/.git/objects/??/* &&
- git push testrepo :refs/heads/main &&
- (cd testrepo && test_must_fail git rev-parse --verify refs/heads/main)
+ git push testrepo :refs/heads/branch &&
+ (cd testrepo && test_must_fail git rev-parse --verify refs/heads/branch)
'
@@ -706,25 +756,26 @@ test_expect_success 'pushing valid refs triggers post-receive and post-update ho
'
test_expect_success 'deleting dangling ref triggers hooks with correct args' '
- mk_test_with_hooks testrepo heads/main &&
+ mk_test_with_hooks testrepo heads/branch &&
+ orig=$(git -C testrepo rev-parse refs/heads/branch) &&
rm -f testrepo/.git/objects/??/* &&
- git push testrepo :refs/heads/main &&
+ git push testrepo :refs/heads/branch &&
(
cd testrepo/.git &&
cat >pre-receive.expect <<-EOF &&
- $ZERO_OID $ZERO_OID refs/heads/main
+ $orig $ZERO_OID refs/heads/branch
EOF
cat >update.expect <<-EOF &&
- refs/heads/main $ZERO_OID $ZERO_OID
+ refs/heads/branch $orig $ZERO_OID
EOF
cat >post-receive.expect <<-EOF &&
- $ZERO_OID $ZERO_OID refs/heads/main
+ $orig $ZERO_OID refs/heads/branch
EOF
cat >post-update.expect <<-EOF &&
- refs/heads/main
+ refs/heads/branch
EOF
test_cmp pre-receive.expect pre-receive.actual &&
@@ -863,6 +914,13 @@ test_expect_success 'push --delete refuses empty string' '
test_must_fail git push testrepo --delete ""
'
+test_expect_success 'push --delete onelevel refspecs' '
+ mk_test testrepo heads/main &&
+ git -C testrepo update-ref refs/onelevel refs/heads/main &&
+ git push testrepo --delete refs/onelevel &&
+ test_must_fail git -C testrepo rev-parse --verify refs/onelevel
+'
+
test_expect_success 'warn on push to HEAD of non-bare repository' '
mk_test testrepo heads/main &&
(
@@ -911,6 +969,7 @@ test_expect_success 'fetch with branches' '
mk_empty testrepo &&
git branch second $the_first_commit &&
git checkout second &&
+ mkdir testrepo/.git/branches &&
echo ".." > testrepo/.git/branches/branch1 &&
(
cd testrepo &&
@@ -924,6 +983,7 @@ test_expect_success 'fetch with branches' '
test_expect_success 'fetch with branches containing #' '
mk_empty testrepo &&
+ mkdir testrepo/.git/branches &&
echo "..#second" > testrepo/.git/branches/branch2 &&
(
cd testrepo &&
@@ -938,7 +998,11 @@ test_expect_success 'fetch with branches containing #' '
test_expect_success 'push with branches' '
mk_empty testrepo &&
git checkout second &&
+
+ test_when_finished "rm -rf .git/branches" &&
+ mkdir .git/branches &&
echo "testrepo" > .git/branches/branch1 &&
+
git push branch1 &&
(
cd testrepo &&
@@ -950,7 +1014,11 @@ test_expect_success 'push with branches' '
test_expect_success 'push with branches containing #' '
mk_empty testrepo &&
+
+ test_when_finished "rm -rf .git/branches" &&
+ mkdir .git/branches &&
echo "testrepo#branch3" > .git/branches/branch2 &&
+
git push branch2 &&
(
cd testrepo &&
@@ -1199,7 +1267,7 @@ test_expect_success 'fetch exact SHA1' '
# fetching the hidden object should fail by default
test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 \
git fetch -v ../testrepo $the_commit:refs/heads/copy 2>err &&
- test_i18ngrep "Server does not allow request for unadvertised object" err &&
+ test_grep "Server does not allow request for unadvertised object" err &&
test_must_fail git rev-parse --verify refs/heads/copy &&
# the server side can allow it to succeed
@@ -1301,26 +1369,24 @@ do
git fetch ../testrepo/.git $SHA1_3 2>err &&
# ideally we would insist this be on a "remote error:"
# line, but it is racy; see the commit message
- test_i18ngrep "not our ref.*$SHA1_3\$" err
+ test_grep "not our ref.*$SHA1_3\$" err
)
'
done
test_expect_success 'fetch follows tags by default' '
mk_test testrepo heads/main &&
- rm -fr src dst &&
+ test_when_finished "rm -rf src" &&
git init src &&
(
cd src &&
git pull ../testrepo main &&
git tag -m "annotated" tag &&
git for-each-ref >tmp1 &&
- (
- cat tmp1
- sed -n "s|refs/heads/main$|refs/remotes/origin/main|p" tmp1
- ) |
+ sed -n "p; s|refs/heads/main$|refs/remotes/origin/main|p" tmp1 |
sort -k 3 >../expect
) &&
+ test_when_finished "rm -rf dst" &&
git init dst &&
(
cd dst &&
@@ -1341,13 +1407,14 @@ test_expect_success 'peeled advertisements are not considered ref tips' '
oid=$(git -C testrepo rev-parse mytag^{commit}) &&
test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 \
git fetch testrepo $oid 2>err &&
- test_i18ngrep "Server does not allow request for unadvertised object" err
+ test_grep "Server does not allow request for unadvertised object" err
'
test_expect_success 'pushing a specific ref applies remote.$name.push as refmap' '
mk_test testrepo heads/main &&
- rm -fr src dst &&
+ test_when_finished "rm -rf src" &&
git init src &&
+ test_when_finished "rm -rf dst" &&
git init --bare dst &&
(
cd src &&
@@ -1370,8 +1437,9 @@ test_expect_success 'pushing a specific ref applies remote.$name.push as refmap'
test_expect_success 'with no remote.$name.push, it is not used as refmap' '
mk_test testrepo heads/main &&
- rm -fr src dst &&
+ test_when_finished "rm -rf src" &&
git init src &&
+ test_when_finished "rm -rf dst" &&
git init --bare dst &&
(
cd src &&
@@ -1392,8 +1460,9 @@ test_expect_success 'with no remote.$name.push, it is not used as refmap' '
test_expect_success 'with no remote.$name.push, upstream mapping is used' '
mk_test testrepo heads/main &&
- rm -fr src dst &&
+ test_when_finished "rm -rf src" &&
git init src &&
+ test_when_finished "rm -rf dst" &&
git init --bare dst &&
(
cd src &&
@@ -1421,8 +1490,9 @@ test_expect_success 'with no remote.$name.push, upstream mapping is used' '
test_expect_success 'push does not follow tags by default' '
mk_test testrepo heads/main &&
- rm -fr src dst &&
+ test_when_finished "rm -rf src" &&
git init src &&
+ test_when_finished "rm -rf dst" &&
git init --bare dst &&
(
cd src &&
@@ -1444,8 +1514,9 @@ test_expect_success 'push does not follow tags by default' '
test_expect_success 'push --follow-tags only pushes relevant tags' '
mk_test testrepo heads/main &&
- rm -fr src dst &&
+ test_when_finished "rm -rf src" &&
git init src &&
+ test_when_finished "rm -rf dst" &&
git init --bare dst &&
(
cd src &&
@@ -1483,9 +1554,9 @@ EOF
'
test_expect_success 'pushing a tag pushes the tagged object' '
- rm -rf dst.git &&
blob=$(echo unreferenced | git hash-object -w --stdin) &&
git tag -m foo tag-of-blob $blob &&
+ test_when_finished "rm -rf dst.git" &&
git init --bare dst.git &&
git push dst.git tag-of-blob &&
# the receiving index-pack should have noticed
@@ -1496,7 +1567,7 @@ test_expect_success 'pushing a tag pushes the tagged object' '
'
test_expect_success 'push into bare respects core.logallrefupdates' '
- rm -rf dst.git &&
+ test_when_finished "rm -rf dst.git" &&
git init --bare dst.git &&
git -C dst.git config core.logallrefupdates true &&
@@ -1514,7 +1585,7 @@ test_expect_success 'push into bare respects core.logallrefupdates' '
'
test_expect_success 'fetch into bare respects core.logallrefupdates' '
- rm -rf dst.git &&
+ test_when_finished "rm -rf dst.git" &&
git init --bare dst.git &&
(
cd dst.git &&
@@ -1535,6 +1606,7 @@ test_expect_success 'fetch into bare respects core.logallrefupdates' '
'
test_expect_success 'receive.denyCurrentBranch = updateInstead' '
+ mk_empty testrepo &&
git push testrepo main &&
(
cd testrepo &&
@@ -1637,7 +1709,7 @@ test_expect_success 'receive.denyCurrentBranch = updateInstead' '
) &&
# (5) push into void
- rm -fr void &&
+ test_when_finished "rm -rf void" &&
git init void &&
(
cd void &&
@@ -1659,26 +1731,23 @@ test_expect_success 'receive.denyCurrentBranch = updateInstead' '
'
test_expect_success 'updateInstead with push-to-checkout hook' '
- rm -fr testrepo &&
+ test_when_finished "rm -rf testrepo" &&
git init testrepo &&
- (
- cd testrepo &&
- git pull .. main &&
- git reset --hard HEAD^^ &&
- git tag initial &&
- git config receive.denyCurrentBranch updateInstead &&
- write_script .git/hooks/push-to-checkout <<-\EOF
- echo >&2 updating from $(git rev-parse HEAD)
- echo >&2 updating to "$1"
-
- git update-index -q --refresh &&
- git read-tree -u -m HEAD "$1" || {
- status=$?
- echo >&2 read-tree failed
- exit $status
- }
- EOF
- ) &&
+ git -C testrepo pull .. main &&
+ git -C testrepo reset --hard HEAD^^ &&
+ git -C testrepo tag initial &&
+ git -C testrepo config receive.denyCurrentBranch updateInstead &&
+ test_hook -C testrepo push-to-checkout <<-\EOF &&
+ echo >&2 updating from $(git rev-parse HEAD)
+ echo >&2 updating to "$1"
+
+ git update-index -q --refresh &&
+ git read-tree -u -m HEAD "$1" || {
+ status=$?
+ echo >&2 read-tree failed
+ exit $status
+ }
+ EOF
# Try pushing into a pristine
git push testrepo main &&
@@ -1721,35 +1790,32 @@ test_expect_success 'updateInstead with push-to-checkout hook' '
) &&
# push into void
- rm -fr void &&
+ test_when_finished "rm -rf void" &&
git init void &&
- (
- cd void &&
- git config receive.denyCurrentBranch updateInstead &&
- write_script .git/hooks/push-to-checkout <<-\EOF
- if git rev-parse --quiet --verify HEAD
- then
- has_head=yes
- echo >&2 updating from $(git rev-parse HEAD)
- else
- has_head=no
- echo >&2 pushing into void
- fi
- echo >&2 updating to "$1"
-
- git update-index -q --refresh &&
- case "$has_head" in
- yes)
- git read-tree -u -m HEAD "$1" ;;
- no)
- git read-tree -u -m "$1" ;;
- esac || {
- status=$?
- echo >&2 read-tree failed
- exit $status
- }
- EOF
- ) &&
+ git -C void config receive.denyCurrentBranch updateInstead &&
+ test_hook -C void push-to-checkout <<-\EOF &&
+ if git rev-parse --quiet --verify HEAD
+ then
+ has_head=yes
+ echo >&2 updating from $(git rev-parse HEAD)
+ else
+ has_head=no
+ echo >&2 pushing into void
+ fi
+ echo >&2 updating to "$1"
+
+ git update-index -q --refresh &&
+ case "$has_head" in
+ yes)
+ git read-tree -u -m HEAD "$1" ;;
+ no)
+ git read-tree -u -m "$1" ;;
+ esac || {
+ status=$?
+ echo >&2 read-tree failed
+ exit $status
+ }
+ EOF
git push void main &&
(
@@ -1768,6 +1834,68 @@ test_expect_success 'denyCurrentBranch and worktrees' '
test_must_fail git -C cloned push origin HEAD:new-wt &&
test_config receive.denyCurrentBranch updateInstead &&
git -C cloned push origin HEAD:new-wt &&
+ test_path_exists new-wt/first.t &&
test_must_fail git -C cloned push --delete origin new-wt
'
+
+test_expect_success 'denyCurrentBranch and bare repository worktrees' '
+ test_when_finished "rm -fr bare.git" &&
+ git clone --bare . bare.git &&
+ git -C bare.git worktree add wt &&
+ test_commit grape &&
+ git -C bare.git config receive.denyCurrentBranch refuse &&
+ test_must_fail git push bare.git HEAD:wt &&
+ git -C bare.git config receive.denyCurrentBranch updateInstead &&
+ git push bare.git HEAD:wt &&
+ test_path_exists bare.git/wt/grape.t &&
+ test_must_fail git push --delete bare.git wt
+'
+
+test_expect_success 'refuse fetch to current branch of worktree' '
+ test_when_finished "git worktree remove --force wt && git branch -D wt" &&
+ git worktree add wt &&
+ test_commit apple &&
+ test_must_fail git fetch . HEAD:wt &&
+ git fetch -u . HEAD:wt
+'
+
+test_expect_success 'refuse fetch to current branch of bare repository worktree' '
+ test_when_finished "rm -fr bare.git" &&
+ git clone --bare . bare.git &&
+ git -C bare.git worktree add wt &&
+ test_commit banana &&
+ test_must_fail git -C bare.git fetch .. HEAD:wt &&
+ git -C bare.git fetch -u .. HEAD:wt
+'
+
+test_expect_success 'refuse to push a hidden ref, and make sure do not pollute the repository' '
+ mk_empty testrepo &&
+ git -C testrepo config receive.hiderefs refs/hidden &&
+ git -C testrepo config receive.unpackLimit 1 &&
+ test_must_fail git push testrepo HEAD:refs/hidden/foo &&
+ test_dir_is_empty testrepo/.git/objects/pack
+'
+
+test_expect_success 'push with config push.useBitmaps' '
+ mk_test testrepo heads/main &&
+ git checkout main &&
+ test_unconfig push.useBitmaps &&
+ GIT_TRACE2_EVENT="$PWD/default" \
+ git push --quiet testrepo main:test &&
+ test_subcommand git pack-objects --all-progress-implied --revs --stdout \
+ --thin --delta-base-offset -q <default &&
+
+ test_config push.useBitmaps true &&
+ GIT_TRACE2_EVENT="$PWD/true" \
+ git push --quiet testrepo main:test2 &&
+ test_subcommand git pack-objects --all-progress-implied --revs --stdout \
+ --thin --delta-base-offset -q <true &&
+
+ test_config push.useBitmaps false &&
+ GIT_TRACE2_EVENT="$PWD/false" \
+ git push --quiet testrepo main:test3 &&
+ test_subcommand git pack-objects --all-progress-implied --revs --stdout \
+ --thin --delta-base-offset -q --no-use-bitmap-index <false
+'
+
test_done
diff --git a/t/t5517-push-mirror.sh b/t/t5517-push-mirror.sh
index a448e16..6d4944a 100755
--- a/t/t5517-push-mirror.sh
+++ b/t/t5517-push-mirror.sh
@@ -5,6 +5,7 @@ test_description='pushing to a mirror repository'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
D=$(pwd)
diff --git a/t/t5518-fetch-exit-status.sh b/t/t5518-fetch-exit-status.sh
index 5c4ac25..c131200 100755
--- a/t/t5518-fetch-exit-status.sh
+++ b/t/t5518-fetch-exit-status.sh
@@ -8,6 +8,7 @@ test_description='fetch exit status test'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index 672001a..47534f1 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -31,7 +31,7 @@ test_pull_autostash_fail () {
echo dirty >new_file &&
git add new_file &&
test_must_fail git pull "$@" . copy 2>err &&
- test_i18ngrep -E "uncommitted changes.|overwritten by merge:" err
+ test_grep -E "uncommitted changes.|overwritten by merge:" err
}
test_expect_success setup '
@@ -151,7 +151,7 @@ test_expect_success 'fail if wildcard spec does not match any refs' '
echo file >expect &&
test_cmp expect file &&
test_must_fail git pull . "refs/nonexisting1/*:refs/nonexisting2/*" 2>err &&
- test_i18ngrep "no candidates for merging" err &&
+ test_grep "no candidates for merging" err &&
test_cmp expect file
'
@@ -164,7 +164,7 @@ test_expect_success 'fail if no branches specified with non-default remote' '
test_cmp expect file &&
test_config branch.test.remote origin &&
test_must_fail git pull test_remote 2>err &&
- test_i18ngrep "specify a branch on the command line" err &&
+ test_grep "specify a branch on the command line" err &&
test_cmp expect file
'
@@ -176,7 +176,7 @@ test_expect_success 'fail if not on a branch' '
echo file >expect &&
test_cmp expect file &&
test_must_fail git pull 2>err &&
- test_i18ngrep "not currently on a branch" err &&
+ test_grep "not currently on a branch" err &&
test_cmp expect file
'
@@ -189,7 +189,7 @@ test_expect_success 'fail if no configuration for current branch' '
echo file >expect &&
test_cmp expect file &&
test_must_fail git pull 2>err &&
- test_i18ngrep "no tracking information" err &&
+ test_grep "no tracking information" err &&
test_cmp expect file
'
@@ -202,7 +202,7 @@ test_expect_success 'pull --all: fail if no configuration for current branch' '
echo file >expect &&
test_cmp expect file &&
test_must_fail git pull --all 2>err &&
- test_i18ngrep "There is no tracking information" err &&
+ test_grep "There is no tracking information" err &&
test_cmp expect file
'
@@ -214,10 +214,27 @@ test_expect_success 'fail if upstream branch does not exist' '
echo file >expect &&
test_cmp expect file &&
test_must_fail git pull 2>err &&
- test_i18ngrep "no such ref was fetched" err &&
+ test_grep "no such ref was fetched" err &&
test_cmp expect file
'
+test_expect_success 'fetch upstream branch even if refspec excludes it' '
+ # the branch names are not important here except that
+ # the first one must not be a prefix of the second,
+ # since otherwise the ref-prefix protocol extension
+ # would match both
+ git branch in-refspec HEAD^ &&
+ git branch not-in-refspec HEAD &&
+ git init -b in-refspec downstream &&
+ git -C downstream remote add -t in-refspec origin "file://$(pwd)/.git" &&
+ git -C downstream config branch.in-refspec.remote origin &&
+ git -C downstream config branch.in-refspec.merge refs/heads/not-in-refspec &&
+ git -C downstream pull &&
+ git rev-parse --verify not-in-refspec >expect &&
+ git -C downstream rev-parse --verify HEAD >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'fail if the index has unresolved entries' '
git checkout -b third second^ &&
test_when_finished "git checkout -f copy && git branch -D third" &&
@@ -231,13 +248,13 @@ test_expect_success 'fail if the index has unresolved entries' '
test_file_not_empty unmerged &&
cp file expected &&
test_must_fail git pull . second 2>err &&
- test_i18ngrep "Pulling is not possible because you have unmerged files." err &&
+ test_grep "Pulling is not possible because you have unmerged files." err &&
test_cmp expected file &&
git add file &&
git ls-files -u >unmerged &&
test_must_be_empty unmerged &&
test_must_fail git pull . second 2>err &&
- test_i18ngrep "You have not concluded your merge" err &&
+ test_grep "You have not concluded your merge" err &&
test_cmp expected file
'
@@ -247,7 +264,7 @@ test_expect_success 'fast-forwards working tree if branch head is updated' '
echo file >expect &&
test_cmp expect file &&
git pull . second:third 2>err &&
- test_i18ngrep "fetch updated the current branch head" err &&
+ test_grep "fetch updated the current branch head" err &&
echo modified >expect &&
test_cmp expect file &&
test_cmp_rev third second
@@ -260,7 +277,7 @@ test_expect_success 'fast-forward fails with conflicting work tree' '
test_cmp expect file &&
echo conflict >file &&
test_must_fail git pull . second:third 2>err &&
- test_i18ngrep "Cannot fast-forward your working tree" err &&
+ test_grep "Cannot fast-forward your working tree" err &&
echo conflict >expect &&
test_cmp expect file &&
test_cmp_rev third second
@@ -330,6 +347,19 @@ test_expect_success '--rebase --autostash fast forward' '
test_cmp_rev HEAD to-rebase-ff
'
+test_expect_success '--rebase with rebase.autostash succeeds on ff' '
+ test_when_finished "rm -fr src dst actual" &&
+ git init src &&
+ test_commit -C src "initial" file "content" &&
+ git clone src dst &&
+ test_commit -C src --printf "more_content" file "more content\ncontent\n" &&
+ echo "dirty" >>dst/file &&
+ test_config -C dst rebase.autostash true &&
+ git -C dst pull --rebase >actual 2>&1 &&
+ grep -q "Fast-forward" actual &&
+ grep -q "Applied autostash." actual
+'
+
test_expect_success '--rebase with conflicts shows advice' '
test_when_finished "git rebase --abort; git checkout -f to-rebase" &&
git checkout -b seq &&
@@ -345,7 +375,7 @@ test_expect_success '--rebase with conflicts shows advice' '
test_tick &&
git commit -m "Create conflict" seq.txt &&
test_must_fail git pull --rebase . seq 2>err >out &&
- test_i18ngrep "Resolve all conflicts manually" err
+ test_grep "Resolve all conflicts manually" err
'
test_expect_success 'failed --rebase shows advice' '
@@ -359,14 +389,14 @@ test_expect_success 'failed --rebase shows advice' '
git checkout -f -b fails-to-rebase HEAD^ &&
test_commit v2-without-cr file "2" file2-lf &&
test_must_fail git pull --rebase . diverging 2>err >out &&
- test_i18ngrep "Resolve all conflicts manually" err
+ test_grep "Resolve all conflicts manually" err
'
test_expect_success '--rebase fails with multiple branches' '
git reset --hard before-rebase &&
test_must_fail git pull --rebase . copy main 2>err &&
test_cmp_rev HEAD before-rebase &&
- test_i18ngrep "Cannot rebase onto multiple branches" err &&
+ test_grep "Cannot rebase onto multiple branches" err &&
echo modified >expect &&
git show HEAD:file >actual &&
test_cmp expect actual
@@ -490,7 +520,7 @@ test_expect_success 'pull --rebase warns on --verify-signatures' '
echo new >expect &&
git show HEAD:file2 >actual &&
test_cmp expect actual &&
- test_i18ngrep "ignoring --verify-signatures for rebase" err
+ test_grep "ignoring --verify-signatures for rebase" err
'
test_expect_success 'pull --rebase does not warn on --no-verify-signatures' '
@@ -500,7 +530,7 @@ test_expect_success 'pull --rebase does not warn on --no-verify-signatures' '
echo new >expect &&
git show HEAD:file2 >actual &&
test_cmp expect actual &&
- test_i18ngrep ! "verify-signatures" err
+ test_grep ! "verify-signatures" err
'
# add a feature branch, keep-merge, that is merged into main, so the
@@ -546,15 +576,6 @@ test_expect_success 'pull.rebase=1 is treated as true and flattens keep-merge' '
test_cmp expect actual
'
-test_expect_success REBASE_P \
- 'pull.rebase=preserve rebases and merges keep-merge' '
- git reset --hard before-preserve-rebase &&
- test_config pull.rebase preserve &&
- git pull . copy &&
- test_cmp_rev HEAD^^ copy &&
- test_cmp_rev HEAD^2 keep-merge
-'
-
test_expect_success 'pull.rebase=interactive' '
write_script "$TRASH_DIRECTORY/fake-editor" <<-\EOF &&
echo I was here >fake.out &&
@@ -598,7 +619,7 @@ test_expect_success '--rebase=false create a new merge commit' '
test_expect_success '--rebase=true rebases and flattens keep-merge' '
git reset --hard before-preserve-rebase &&
- test_config pull.rebase preserve &&
+ test_config pull.rebase merges &&
git pull --rebase=true . copy &&
test_cmp_rev HEAD^^ copy &&
echo file3 >expect &&
@@ -606,23 +627,14 @@ test_expect_success '--rebase=true rebases and flattens keep-merge' '
test_cmp expect actual
'
-test_expect_success REBASE_P \
- '--rebase=preserve rebases and merges keep-merge' '
- git reset --hard before-preserve-rebase &&
- test_config pull.rebase true &&
- git pull --rebase=preserve . copy &&
- test_cmp_rev HEAD^^ copy &&
- test_cmp_rev HEAD^2 keep-merge
-'
-
test_expect_success '--rebase=invalid fails' '
git reset --hard before-preserve-rebase &&
test_must_fail git pull --rebase=invalid . copy
'
-test_expect_success '--rebase overrides pull.rebase=preserve and flattens keep-merge' '
+test_expect_success '--rebase overrides pull.rebase=merges and flattens keep-merge' '
git reset --hard before-preserve-rebase &&
- test_config pull.rebase preserve &&
+ test_config pull.rebase merges &&
git pull --rebase . copy &&
test_cmp_rev HEAD^^ copy &&
echo file3 >expect &&
@@ -728,7 +740,7 @@ test_expect_success 'pull --rebase fails on unborn branch with staged changes' '
test_cmp expect actual &&
git show :staged-file >actual &&
test_cmp expect actual &&
- test_i18ngrep "unborn branch with changes added to the index" err
+ test_grep "unborn branch with changes added to the index" err
)
'
diff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh
index 7601c91..db00c43 100755
--- a/t/t5521-pull-options.sh
+++ b/t/t5521-pull-options.sh
@@ -5,6 +5,7 @@ test_description='pull options'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -93,7 +94,7 @@ test_expect_success 'git pull --no-write-fetch-head fails' '
(cd clonedwfh && git init &&
test_expect_code 129 git pull --no-write-fetch-head "../parent" >out 2>err &&
test_must_be_empty out &&
- test_i18ngrep "no-write-fetch-head" err)
+ test_grep "no-write-fetch-head" err)
'
test_expect_success 'git pull --force' '
@@ -142,7 +143,7 @@ test_expect_success 'git pull --dry-run' '
cd clonedry &&
git pull --dry-run ../parent &&
test_path_is_missing .git/FETCH_HEAD &&
- test_path_is_missing .git/refs/heads/main &&
+ test_ref_missing refs/heads/main &&
test_path_is_missing .git/index &&
test_path_is_missing file
)
@@ -156,7 +157,7 @@ test_expect_success 'git pull --all --dry-run' '
git remote add origin ../parent &&
git pull --all --dry-run &&
test_path_is_missing .git/FETCH_HEAD &&
- test_path_is_missing .git/refs/remotes/origin/main &&
+ test_ref_missing refs/remotes/origin/main &&
test_path_is_missing .git/index &&
test_path_is_missing file
)
@@ -228,4 +229,28 @@ test_expect_success 'git pull --no-signoff flag cancels --signoff flag' '
test_must_be_empty actual
'
+test_expect_success 'git pull --no-verify flag passed to merge' '
+ test_when_finished "rm -fr src dst actual" &&
+ git init src &&
+ test_commit -C src one &&
+ git clone src dst &&
+ test_hook -C dst commit-msg <<-\EOF &&
+ false
+ EOF
+ test_commit -C src two &&
+ git -C dst pull --no-ff --no-verify
+'
+
+test_expect_success 'git pull --no-verify --verify passed to merge' '
+ test_when_finished "rm -fr src dst actual" &&
+ git init src &&
+ test_commit -C src one &&
+ git clone src dst &&
+ test_hook -C dst commit-msg <<-\EOF &&
+ false
+ EOF
+ test_commit -C src two &&
+ test_must_fail git -C dst pull --no-ff --no-verify --verify
+'
+
test_done
diff --git a/t/t5522-pull-symlink.sh b/t/t5522-pull-symlink.sh
index bcff460..cc5496e 100755
--- a/t/t5522-pull-symlink.sh
+++ b/t/t5522-pull-symlink.sh
@@ -2,6 +2,7 @@
test_description='pulling from symlinked subdir'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# The scenario we are building:
@@ -78,7 +79,9 @@ test_expect_success SYMLINKS 'pushing from symlinked subdir' '
git commit -m push ./file &&
git push
) &&
- test push = $(git show HEAD:subdir/file)
+ echo push >expect &&
+ git show HEAD:subdir/file >actual &&
+ test_cmp expect actual
'
test_done
diff --git a/t/t5523-push-upstream.sh b/t/t5523-push-upstream.sh
index fdb4292..1f859ad 100755
--- a/t/t5523-push-upstream.sh
+++ b/t/t5523-push-upstream.sh
@@ -4,6 +4,7 @@ test_description='push with --set-upstream'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
@@ -60,12 +61,20 @@ test_expect_success 'push -u :topic_2' '
check_config topic_2 upstream refs/heads/other2
'
-test_expect_success 'push -u --all' '
+test_expect_success 'push -u --all(the same behavior with--branches)' '
git branch all1 &&
git branch all2 &&
git push -u --all &&
check_config all1 upstream refs/heads/all1 &&
- check_config all2 upstream refs/heads/all2
+ check_config all2 upstream refs/heads/all2 &&
+ git config --get-regexp branch.all* > expect &&
+ git config --remove-section branch.all1 &&
+ git config --remove-section branch.all2 &&
+ git push -u --branches &&
+ check_config all1 upstream refs/heads/all1 &&
+ check_config all2 upstream refs/heads/all2 &&
+ git config --get-regexp branch.all* > actual &&
+ test_cmp expect actual
'
test_expect_success 'push -u HEAD' '
@@ -78,7 +87,7 @@ test_expect_success TTY 'progress messages go to tty' '
ensure_fresh_upstream &&
test_terminal git push -u upstream main >out 2>err &&
- test_i18ngrep "Writing objects" err
+ test_grep "Writing objects" err
'
test_expect_success 'progress messages do not go to non-tty' '
@@ -86,7 +95,7 @@ test_expect_success 'progress messages do not go to non-tty' '
# skip progress messages, since stderr is non-tty
git push -u upstream main >out 2>err &&
- test_i18ngrep ! "Writing objects" err
+ test_grep ! "Writing objects" err
'
test_expect_success 'progress messages go to non-tty (forced)' '
@@ -94,22 +103,22 @@ test_expect_success 'progress messages go to non-tty (forced)' '
# force progress messages to stderr, even though it is non-tty
git push -u --progress upstream main >out 2>err &&
- test_i18ngrep "Writing objects" err
+ test_grep "Writing objects" err
'
test_expect_success TTY 'push -q suppresses progress' '
ensure_fresh_upstream &&
test_terminal git push -u -q upstream main >out 2>err &&
- test_i18ngrep ! "Writing objects" err
+ test_grep ! "Writing objects" err
'
test_expect_success TTY 'push --no-progress suppresses progress' '
ensure_fresh_upstream &&
test_terminal git push -u --no-progress upstream main >out 2>err &&
- test_i18ngrep ! "Unpacking objects" err &&
- test_i18ngrep ! "Writing objects" err
+ test_grep ! "Unpacking objects" err &&
+ test_grep ! "Writing objects" err
'
test_expect_success TTY 'quiet push' '
diff --git a/t/t5524-pull-msg.sh b/t/t5524-pull-msg.sh
index b2be360..56716e2 100755
--- a/t/t5524-pull-msg.sh
+++ b/t/t5524-pull-msg.sh
@@ -2,6 +2,7 @@
test_description='git pull message generation'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
dollar='$Dollar'
diff --git a/t/t5525-fetch-tagopt.sh b/t/t5525-fetch-tagopt.sh
index 45815f7..3a28f1d 100755
--- a/t/t5525-fetch-tagopt.sh
+++ b/t/t5525-fetch-tagopt.sh
@@ -2,6 +2,7 @@
test_description='tagopt variable affects "git fetch" and is overridden by commandline.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
setup_clone () {
diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh
index ed11569..5e56620 100755
--- a/t/t5526-fetch-submodules.sh
+++ b/t/t5526-fetch-submodules.sh
@@ -3,41 +3,131 @@
test_description='Recursive "git fetch" for submodules'
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1
+export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
. ./test-lib.sh
pwd=$(pwd)
-add_upstream_commit() {
+write_expected_sub () {
+ NEW_HEAD=$1 &&
+ SUPER_HEAD=$2 &&
+ cat >"$pwd/expect.err.sub" <<-EOF
+ Fetching submodule submodule${SUPER_HEAD:+ at commit $SUPER_HEAD}
+ From $pwd/submodule
+ OLD_HEAD..$NEW_HEAD sub -> origin/sub
+ EOF
+}
+
+write_expected_sub2 () {
+ NEW_HEAD=$1 &&
+ SUPER_HEAD=$2 &&
+ cat >"$pwd/expect.err.sub2" <<-EOF
+ Fetching submodule submodule2${SUPER_HEAD:+ at commit $SUPER_HEAD}
+ From $pwd/submodule2
+ OLD_HEAD..$NEW_HEAD sub2 -> origin/sub2
+ EOF
+}
+
+write_expected_deep () {
+ NEW_HEAD=$1 &&
+ SUB_HEAD=$2 &&
+ cat >"$pwd/expect.err.deep" <<-EOF
+ Fetching submodule submodule/subdir/deepsubmodule${SUB_HEAD:+ at commit $SUB_HEAD}
+ From $pwd/deepsubmodule
+ OLD_HEAD..$NEW_HEAD deep -> origin/deep
+ EOF
+}
+
+write_expected_super () {
+ NEW_HEAD=$1 &&
+ cat >"$pwd/expect.err.super" <<-EOF
+ From $pwd/.
+ OLD_HEAD..$NEW_HEAD super -> origin/super
+ EOF
+}
+
+# For each submodule in the test setup, this creates a commit and writes
+# a file that contains the expected err if that new commit were fetched.
+# These output files get concatenated in the right order by
+# verify_fetch_result().
+add_submodule_commits () {
(
cd submodule &&
- head1=$(git rev-parse --short HEAD) &&
echo new >> subfile &&
test_tick &&
git add subfile &&
git commit -m new subfile &&
- head2=$(git rev-parse --short HEAD) &&
- echo "Fetching submodule submodule" > ../expect.err &&
- echo "From $pwd/submodule" >> ../expect.err &&
- echo " $head1..$head2 sub -> origin/sub" >> ../expect.err
+ new_head=$(git rev-parse --short HEAD) &&
+ write_expected_sub $new_head
) &&
(
cd deepsubmodule &&
- head1=$(git rev-parse --short HEAD) &&
echo new >> deepsubfile &&
test_tick &&
git add deepsubfile &&
git commit -m new deepsubfile &&
- head2=$(git rev-parse --short HEAD) &&
- echo "Fetching submodule submodule/subdir/deepsubmodule" >> ../expect.err
- echo "From $pwd/deepsubmodule" >> ../expect.err &&
- echo " $head1..$head2 deep -> origin/deep" >> ../expect.err
+ new_head=$(git rev-parse --short HEAD) &&
+ write_expected_deep $new_head
)
}
+# For each superproject in the test setup, update its submodule, add the
+# submodule and create a new commit with the submodule change.
+#
+# This requires add_submodule_commits() to be called first, otherwise
+# the submodules will not have changed and cannot be "git add"-ed.
+add_superproject_commits () {
+ (
+ cd submodule &&
+ (
+ cd subdir/deepsubmodule &&
+ git fetch &&
+ git checkout -q FETCH_HEAD
+ ) &&
+ git add subdir/deepsubmodule &&
+ git commit -m "new deep submodule"
+ ) &&
+ git add submodule &&
+ git commit -m "new submodule" &&
+ super_head=$(git rev-parse --short HEAD) &&
+ sub_head=$(git -C submodule rev-parse --short HEAD) &&
+ write_expected_super $super_head &&
+ write_expected_sub $sub_head
+}
+
+# Verifies that the expected repositories were fetched. This is done by
+# concatenating the files expect.err.[super|sub|deep] in the correct
+# order and comparing it to the actual stderr.
+#
+# If a repo should not be fetched in the test, its corresponding
+# expect.err file should be rm-ed.
+verify_fetch_result () {
+ ACTUAL_ERR=$1 &&
+ rm -f expect.err.combined &&
+ if test -f expect.err.super
+ then
+ cat expect.err.super >>expect.err.combined
+ fi &&
+ if test -f expect.err.sub
+ then
+ cat expect.err.sub >>expect.err.combined
+ fi &&
+ if test -f expect.err.deep
+ then
+ cat expect.err.deep >>expect.err.combined
+ fi &&
+ if test -f expect.err.sub2
+ then
+ cat expect.err.sub2 >>expect.err.combined
+ fi &&
+ sed -e 's/[0-9a-f][0-9a-f]*\.\./OLD_HEAD\.\./' "$ACTUAL_ERR" >actual.err.cmp &&
+ test_cmp expect.err.combined actual.err.cmp
+}
+
test_expect_success setup '
+ git config --global protocol.file.allow always &&
mkdir deepsubmodule &&
(
cd deepsubmodule &&
@@ -68,38 +158,52 @@ test_expect_success setup '
'
test_expect_success "fetch --recurse-submodules recurses into submodules" '
- add_upstream_commit &&
+ add_submodule_commits &&
(
cd downstream &&
git fetch --recurse-submodules >../actual.out 2>../actual.err
) &&
test_must_be_empty actual.out &&
- test_cmp expect.err actual.err
+ verify_fetch_result actual.err
+'
+
+test_expect_success "fetch --recurse-submodules honors --no-write-fetch-head" '
+ (
+ cd downstream &&
+ git submodule foreach --recursive \
+ sh -c "cd \"\$(git rev-parse --git-dir)\" && rm -f FETCH_HEAD" &&
+
+ git fetch --recurse-submodules --no-write-fetch-head &&
+
+ git submodule foreach --recursive \
+ sh -c "cd \"\$(git rev-parse --git-dir)\" && ! test -f FETCH_HEAD"
+ )
'
test_expect_success "submodule.recurse option triggers recursive fetch" '
- add_upstream_commit &&
+ add_submodule_commits &&
(
cd downstream &&
git -c submodule.recurse fetch >../actual.out 2>../actual.err
) &&
test_must_be_empty actual.out &&
- test_cmp expect.err actual.err
+ verify_fetch_result actual.err
'
test_expect_success "fetch --recurse-submodules -j2 has the same output behaviour" '
- add_upstream_commit &&
+ test_when_finished "rm -f trace.out" &&
+ add_submodule_commits &&
(
cd downstream &&
GIT_TRACE="$TRASH_DIRECTORY/trace.out" git fetch --recurse-submodules -j2 2>../actual.err
) &&
test_must_be_empty actual.out &&
- test_cmp expect.err actual.err &&
+ verify_fetch_result actual.err &&
grep "2 tasks" trace.out
'
test_expect_success "fetch alone only fetches superproject" '
- add_upstream_commit &&
+ add_submodule_commits &&
(
cd downstream &&
git fetch >../actual.out 2>../actual.err
@@ -124,11 +228,11 @@ test_expect_success "using fetchRecurseSubmodules=true in .gitmodules recurses i
git fetch >../actual.out 2>../actual.err
) &&
test_must_be_empty actual.out &&
- test_cmp expect.err actual.err
+ verify_fetch_result actual.err
'
test_expect_success "--no-recurse-submodules overrides .gitmodules config" '
- add_upstream_commit &&
+ add_submodule_commits &&
(
cd downstream &&
git fetch --no-recurse-submodules >../actual.out 2>../actual.err
@@ -155,7 +259,7 @@ test_expect_success "--recurse-submodules overrides fetchRecurseSubmodules setti
git config --unset submodule.submodule.fetchRecurseSubmodules
) &&
test_must_be_empty actual.out &&
- test_cmp expect.err actual.err
+ verify_fetch_result actual.err
'
test_expect_success "--quiet propagates to submodules" '
@@ -177,13 +281,13 @@ test_expect_success "--quiet propagates to parallel submodules" '
'
test_expect_success "--dry-run propagates to submodules" '
- add_upstream_commit &&
+ add_submodule_commits &&
(
cd downstream &&
git fetch --recurse-submodules --dry-run >../actual.out 2>../actual.err
) &&
test_must_be_empty actual.out &&
- test_cmp expect.err actual.err
+ verify_fetch_result actual.err
'
test_expect_success "Without --dry-run propagates to submodules" '
@@ -192,22 +296,22 @@ test_expect_success "Without --dry-run propagates to submodules" '
git fetch --recurse-submodules >../actual.out 2>../actual.err
) &&
test_must_be_empty actual.out &&
- test_cmp expect.err actual.err
+ verify_fetch_result actual.err
'
test_expect_success "recurseSubmodules=true propagates into submodules" '
- add_upstream_commit &&
+ add_submodule_commits &&
(
cd downstream &&
git config fetch.recurseSubmodules true &&
git fetch >../actual.out 2>../actual.err
) &&
test_must_be_empty actual.out &&
- test_cmp expect.err actual.err
+ verify_fetch_result actual.err
'
test_expect_success "--recurse-submodules overrides config in submodule" '
- add_upstream_commit &&
+ add_submodule_commits &&
(
cd downstream &&
(
@@ -217,11 +321,11 @@ test_expect_success "--recurse-submodules overrides config in submodule" '
git fetch --recurse-submodules >../actual.out 2>../actual.err
) &&
test_must_be_empty actual.out &&
- test_cmp expect.err actual.err
+ verify_fetch_result actual.err
'
test_expect_success "--no-recurse-submodules overrides config setting" '
- add_upstream_commit &&
+ add_submodule_commits &&
(
cd downstream &&
git config fetch.recurseSubmodules true &&
@@ -246,36 +350,34 @@ test_expect_success "Recursion doesn't happen when no new commits are fetched in
'
test_expect_success "Recursion stops when no new submodule commits are fetched" '
- head1=$(git rev-parse --short HEAD) &&
git add submodule &&
git commit -m "new submodule" &&
- head2=$(git rev-parse --short HEAD) &&
- echo "From $pwd/." > expect.err.sub &&
- echo " $head1..$head2 super -> origin/super" >>expect.err.sub &&
- head -3 expect.err >> expect.err.sub &&
+ new_head=$(git rev-parse --short HEAD) &&
+ write_expected_super $new_head &&
+ rm expect.err.deep &&
(
cd downstream &&
git fetch >../actual.out 2>../actual.err
) &&
- test_cmp expect.err.sub actual.err &&
+ verify_fetch_result actual.err &&
test_must_be_empty actual.out
'
test_expect_success "Recursion doesn't happen when new superproject commits don't change any submodules" '
- add_upstream_commit &&
- head1=$(git rev-parse --short HEAD) &&
+ add_submodule_commits &&
echo a > file &&
git add file &&
git commit -m "new file" &&
- head2=$(git rev-parse --short HEAD) &&
- echo "From $pwd/." > expect.err.file &&
- echo " $head1..$head2 super -> origin/super" >> expect.err.file &&
+ new_head=$(git rev-parse --short HEAD) &&
+ write_expected_super $new_head &&
+ rm expect.err.sub &&
+ rm expect.err.deep &&
(
cd downstream &&
git fetch >../actual.out 2>../actual.err
) &&
test_must_be_empty actual.out &&
- test_cmp expect.err.file actual.err
+ verify_fetch_result actual.err
'
test_expect_success "Recursion picks up config in submodule" '
@@ -287,14 +389,11 @@ test_expect_success "Recursion picks up config in submodule" '
git config fetch.recurseSubmodules true
)
) &&
- add_upstream_commit &&
- head1=$(git rev-parse --short HEAD) &&
+ add_submodule_commits &&
git add submodule &&
git commit -m "new submodule" &&
- head2=$(git rev-parse --short HEAD) &&
- echo "From $pwd/." > expect.err.sub &&
- echo " $head1..$head2 super -> origin/super" >> expect.err.sub &&
- cat expect.err >> expect.err.sub &&
+ new_head=$(git rev-parse --short HEAD) &&
+ write_expected_super $new_head &&
(
cd downstream &&
git fetch >../actual.out 2>../actual.err &&
@@ -303,60 +402,23 @@ test_expect_success "Recursion picks up config in submodule" '
git config --unset fetch.recurseSubmodules
)
) &&
- test_cmp expect.err.sub actual.err &&
+ verify_fetch_result actual.err &&
test_must_be_empty actual.out
'
test_expect_success "Recursion picks up all submodules when necessary" '
- add_upstream_commit &&
- (
- cd submodule &&
- (
- cd subdir/deepsubmodule &&
- git fetch &&
- git checkout -q FETCH_HEAD
- ) &&
- head1=$(git rev-parse --short HEAD^) &&
- git add subdir/deepsubmodule &&
- git commit -m "new deepsubmodule" &&
- head2=$(git rev-parse --short HEAD) &&
- echo "Fetching submodule submodule" > ../expect.err.sub &&
- echo "From $pwd/submodule" >> ../expect.err.sub &&
- echo " $head1..$head2 sub -> origin/sub" >> ../expect.err.sub
- ) &&
- head1=$(git rev-parse --short HEAD) &&
- git add submodule &&
- git commit -m "new submodule" &&
- head2=$(git rev-parse --short HEAD) &&
- echo "From $pwd/." > expect.err.2 &&
- echo " $head1..$head2 super -> origin/super" >> expect.err.2 &&
- cat expect.err.sub >> expect.err.2 &&
- tail -3 expect.err >> expect.err.2 &&
+ add_submodule_commits &&
+ add_superproject_commits &&
(
cd downstream &&
git fetch >../actual.out 2>../actual.err
) &&
- test_cmp expect.err.2 actual.err &&
+ verify_fetch_result actual.err &&
test_must_be_empty actual.out
'
test_expect_success "'--recurse-submodules=on-demand' doesn't recurse when no new commits are fetched in the superproject (and ignores config)" '
- add_upstream_commit &&
- (
- cd submodule &&
- (
- cd subdir/deepsubmodule &&
- git fetch &&
- git checkout -q FETCH_HEAD
- ) &&
- head1=$(git rev-parse --short HEAD^) &&
- git add subdir/deepsubmodule &&
- git commit -m "new deepsubmodule" &&
- head2=$(git rev-parse --short HEAD) &&
- echo Fetching submodule submodule > ../expect.err.sub &&
- echo "From $pwd/submodule" >> ../expect.err.sub &&
- echo " $head1..$head2 sub -> origin/sub" >> ../expect.err.sub
- ) &&
+ add_submodule_commits &&
(
cd downstream &&
git config fetch.recurseSubmodules true &&
@@ -368,15 +430,8 @@ test_expect_success "'--recurse-submodules=on-demand' doesn't recurse when no ne
'
test_expect_success "'--recurse-submodules=on-demand' recurses as deep as necessary (and ignores config)" '
- head1=$(git rev-parse --short HEAD) &&
- git add submodule &&
- git commit -m "new submodule" &&
- head2=$(git rev-parse --short HEAD) &&
- tail -3 expect.err > expect.err.deepsub &&
- echo "From $pwd/." > expect.err &&
- echo " $head1..$head2 super -> origin/super" >>expect.err &&
- cat expect.err.sub >> expect.err &&
- cat expect.err.deepsub >> expect.err &&
+ add_submodule_commits &&
+ add_superproject_commits &&
(
cd downstream &&
git config fetch.recurseSubmodules false &&
@@ -392,24 +447,165 @@ test_expect_success "'--recurse-submodules=on-demand' recurses as deep as necess
)
) &&
test_must_be_empty actual.out &&
- test_cmp expect.err actual.err
+ verify_fetch_result actual.err
+'
+
+# These tests verify that we can fetch submodules that aren't in the
+# index.
+#
+# First, test the simple case where the index is empty and we only fetch
+# submodules that are not in the index.
+test_expect_success 'setup downstream branch without submodules' '
+ (
+ cd downstream &&
+ git checkout --recurse-submodules -b no-submodules &&
+ git rm .gitmodules &&
+ git rm submodule &&
+ git commit -m "no submodules" &&
+ git checkout --recurse-submodules super
+ )
+'
+
+test_expect_success "'--recurse-submodules=on-demand' should fetch submodule commits if the submodule is changed but the index has no submodules" '
+ add_submodule_commits &&
+ add_superproject_commits &&
+ # Fetch the new superproject commit
+ (
+ cd downstream &&
+ git switch --recurse-submodules no-submodules &&
+ git fetch --recurse-submodules=on-demand >../actual.out 2>../actual.err
+ ) &&
+ super_head=$(git rev-parse --short HEAD) &&
+ sub_head=$(git -C submodule rev-parse --short HEAD) &&
+ deep_head=$(git -C submodule/subdir/deepsubmodule rev-parse --short HEAD) &&
+
+ # assert that these are fetched from commits, not the index
+ write_expected_sub $sub_head $super_head &&
+ write_expected_deep $deep_head $sub_head &&
+
+ test_must_be_empty actual.out &&
+ verify_fetch_result actual.err
+'
+
+test_expect_success "'--recurse-submodules' should fetch submodule commits if the submodule is changed but the index has no submodules" '
+ add_submodule_commits &&
+ add_superproject_commits &&
+ # Fetch the new superproject commit
+ (
+ cd downstream &&
+ git switch --recurse-submodules no-submodules &&
+ git fetch --recurse-submodules >../actual.out 2>../actual.err
+ ) &&
+ super_head=$(git rev-parse --short HEAD) &&
+ sub_head=$(git -C submodule rev-parse --short HEAD) &&
+ deep_head=$(git -C submodule/subdir/deepsubmodule rev-parse --short HEAD) &&
+
+ # assert that these are fetched from commits, not the index
+ write_expected_sub $sub_head $super_head &&
+ write_expected_deep $deep_head $sub_head &&
+
+ test_must_be_empty actual.out &&
+ verify_fetch_result actual.err
+'
+
+test_expect_success "'--recurse-submodules' should ignore changed, inactive submodules" '
+ add_submodule_commits &&
+ add_superproject_commits &&
+
+ # Fetch the new superproject commit
+ (
+ cd downstream &&
+ git switch --recurse-submodules no-submodules &&
+ git -c submodule.submodule.active=false fetch --recurse-submodules >../actual.out 2>../actual.err
+ ) &&
+ test_must_be_empty actual.out &&
+ super_head=$(git rev-parse --short HEAD) &&
+ write_expected_super $super_head &&
+ # Neither should be fetched because the submodule is inactive
+ rm expect.err.sub &&
+ rm expect.err.deep &&
+ verify_fetch_result actual.err
+'
+
+# Now that we know we can fetch submodules that are not in the index,
+# test that we can fetch index and non-index submodules in the same
+# operation.
+test_expect_success 'setup downstream branch with other submodule' '
+ mkdir submodule2 &&
+ (
+ cd submodule2 &&
+ git init &&
+ echo sub2content >sub2file &&
+ git add sub2file &&
+ git commit -a -m new &&
+ git branch -M sub2
+ ) &&
+ git checkout -b super-sub2-only &&
+ git submodule add "$pwd/submodule2" submodule2 &&
+ git commit -m "add sub2" &&
+ git checkout super &&
+ (
+ cd downstream &&
+ git fetch --recurse-submodules origin &&
+ git checkout super-sub2-only &&
+ # Explicitly run "git submodule update" because sub2 is new
+ # and has not been cloned.
+ git submodule update --init &&
+ git checkout --recurse-submodules super
+ )
+'
+
+test_expect_success "'--recurse-submodules' should fetch submodule commits in changed submodules and the index" '
+ test_when_finished "rm expect.err.sub2" &&
+ # Create new commit in origin/super
+ add_submodule_commits &&
+ add_superproject_commits &&
+
+ # Create new commit in origin/super-sub2-only
+ git checkout super-sub2-only &&
+ (
+ cd submodule2 &&
+ test_commit --no-tag foo
+ ) &&
+ git add submodule2 &&
+ git commit -m "new submodule2" &&
+
+ git checkout super &&
+ (
+ cd downstream &&
+ git fetch --recurse-submodules >../actual.out 2>../actual.err
+ ) &&
+ test_must_be_empty actual.out &&
+ sub2_head=$(git -C submodule2 rev-parse --short HEAD) &&
+ super_head=$(git rev-parse --short super) &&
+ super_sub2_only_head=$(git rev-parse --short super-sub2-only) &&
+ write_expected_sub2 $sub2_head $super_sub2_only_head &&
+
+ # write_expected_super cannot handle >1 branch. Since this is a
+ # one-off, construct expect.err.super manually.
+ cat >"$pwd/expect.err.super" <<-EOF &&
+ From $pwd/.
+ OLD_HEAD..$super_head super -> origin/super
+ OLD_HEAD..$super_sub2_only_head super-sub2-only -> origin/super-sub2-only
+ EOF
+ verify_fetch_result actual.err
'
test_expect_success "'--recurse-submodules=on-demand' stops when no new submodule commits are found in the superproject (and ignores config)" '
- add_upstream_commit &&
- head1=$(git rev-parse --short HEAD) &&
+ add_submodule_commits &&
echo a >> file &&
git add file &&
git commit -m "new file" &&
- head2=$(git rev-parse --short HEAD) &&
- echo "From $pwd/." > expect.err.file &&
- echo " $head1..$head2 super -> origin/super" >> expect.err.file &&
+ new_head=$(git rev-parse --short HEAD) &&
+ write_expected_super $new_head &&
+ rm expect.err.sub &&
+ rm expect.err.deep &&
(
cd downstream &&
git fetch --recurse-submodules=on-demand >../actual.out 2>../actual.err
) &&
test_must_be_empty actual.out &&
- test_cmp expect.err.file actual.err
+ verify_fetch_result actual.err
'
test_expect_success "'fetch.recurseSubmodules=on-demand' overrides global config" '
@@ -417,15 +613,13 @@ test_expect_success "'fetch.recurseSubmodules=on-demand' overrides global config
cd downstream &&
git fetch --recurse-submodules
) &&
- add_upstream_commit &&
+ add_submodule_commits &&
git config --global fetch.recurseSubmodules false &&
- head1=$(git rev-parse --short HEAD) &&
git add submodule &&
git commit -m "new submodule" &&
- head2=$(git rev-parse --short HEAD) &&
- echo "From $pwd/." > expect.err.2 &&
- echo " $head1..$head2 super -> origin/super" >>expect.err.2 &&
- head -3 expect.err >> expect.err.2 &&
+ new_head=$(git rev-parse --short HEAD) &&
+ write_expected_super $new_head &&
+ rm expect.err.deep &&
(
cd downstream &&
git config fetch.recurseSubmodules on-demand &&
@@ -437,7 +631,7 @@ test_expect_success "'fetch.recurseSubmodules=on-demand' overrides global config
git config --unset fetch.recurseSubmodules
) &&
test_must_be_empty actual.out &&
- test_cmp expect.err.2 actual.err
+ verify_fetch_result actual.err
'
test_expect_success "'submodule.<sub>.fetchRecurseSubmodules=on-demand' overrides fetch.recurseSubmodules" '
@@ -445,15 +639,13 @@ test_expect_success "'submodule.<sub>.fetchRecurseSubmodules=on-demand' override
cd downstream &&
git fetch --recurse-submodules
) &&
- add_upstream_commit &&
+ add_submodule_commits &&
git config fetch.recurseSubmodules false &&
- head1=$(git rev-parse --short HEAD) &&
git add submodule &&
git commit -m "new submodule" &&
- head2=$(git rev-parse --short HEAD) &&
- echo "From $pwd/." > expect.err.2 &&
- echo " $head1..$head2 super -> origin/super" >>expect.err.2 &&
- head -3 expect.err >> expect.err.2 &&
+ new_head=$(git rev-parse --short HEAD) &&
+ write_expected_super $new_head &&
+ rm expect.err.deep &&
(
cd downstream &&
git config submodule.submodule.fetchRecurseSubmodules on-demand &&
@@ -465,7 +657,7 @@ test_expect_success "'submodule.<sub>.fetchRecurseSubmodules=on-demand' override
git config --unset submodule.submodule.fetchRecurseSubmodules
) &&
test_must_be_empty actual.out &&
- test_cmp expect.err.2 actual.err
+ verify_fetch_result actual.err
'
test_expect_success "don't fetch submodule when newly recorded commits are already present" '
@@ -473,18 +665,19 @@ test_expect_success "don't fetch submodule when newly recorded commits are alrea
cd submodule &&
git checkout -q HEAD^^
) &&
- head1=$(git rev-parse --short HEAD) &&
git add submodule &&
git commit -m "submodule rewound" &&
- head2=$(git rev-parse --short HEAD) &&
- echo "From $pwd/." > expect.err &&
- echo " $head1..$head2 super -> origin/super" >> expect.err &&
+ new_head=$(git rev-parse --short HEAD) &&
+ write_expected_super $new_head &&
+ rm expect.err.sub &&
+ # This file does not exist, but rm -f for readability
+ rm -f expect.err.deep &&
(
cd downstream &&
git fetch >../actual.out 2>../actual.err
) &&
test_must_be_empty actual.out &&
- test_cmp expect.err actual.err &&
+ verify_fetch_result actual.err &&
(
cd submodule &&
git checkout -q sub
@@ -496,15 +689,13 @@ test_expect_success "'fetch.recurseSubmodules=on-demand' works also without .git
cd downstream &&
git fetch --recurse-submodules
) &&
- add_upstream_commit &&
- head1=$(git rev-parse --short HEAD) &&
+ add_submodule_commits &&
git add submodule &&
git rm .gitmodules &&
git commit -m "new submodule without .gitmodules" &&
- head2=$(git rev-parse --short HEAD) &&
- echo "From $pwd/." >expect.err.2 &&
- echo " $head1..$head2 super -> origin/super" >>expect.err.2 &&
- head -3 expect.err >>expect.err.2 &&
+ new_head=$(git rev-parse --short HEAD) &&
+ write_expected_super $new_head &&
+ rm expect.err.deep &&
(
cd downstream &&
rm .gitmodules &&
@@ -520,7 +711,7 @@ test_expect_success "'fetch.recurseSubmodules=on-demand' works also without .git
git reset --hard
) &&
test_must_be_empty actual.out &&
- test_cmp expect.err.2 actual.err &&
+ verify_fetch_result actual.err &&
git checkout HEAD^ -- .gitmodules &&
git add .gitmodules &&
git commit -m "new submodule restored .gitmodules"
@@ -528,17 +719,30 @@ test_expect_success "'fetch.recurseSubmodules=on-demand' works also without .git
test_expect_success 'fetching submodules respects parallel settings' '
git config fetch.recurseSubmodules true &&
+ test_when_finished "rm -f downstream/trace.out" &&
(
cd downstream &&
GIT_TRACE=$(pwd)/trace.out git fetch &&
grep "1 tasks" trace.out &&
+ >trace.out &&
+
GIT_TRACE=$(pwd)/trace.out git fetch --jobs 7 &&
grep "7 tasks" trace.out &&
+ >trace.out &&
+
git config submodule.fetchJobs 8 &&
GIT_TRACE=$(pwd)/trace.out git fetch &&
grep "8 tasks" trace.out &&
+ >trace.out &&
+
GIT_TRACE=$(pwd)/trace.out git fetch --jobs 9 &&
- grep "9 tasks" trace.out
+ grep "9 tasks" trace.out &&
+ >trace.out &&
+
+ GIT_TRACE=$(pwd)/trace.out git -c submodule.fetchJobs=0 fetch &&
+ grep "preparing to run up to [0-9]* tasks" trace.out &&
+ ! grep "up to 0 tasks" trace.out &&
+ >trace.out
)
'
@@ -567,7 +771,7 @@ test_expect_success 'fetching submodule into a broken repository' '
git -C dst fetch --recurse-submodules &&
# Break the receiving submodule
- rm -f dst/sub/.git/HEAD &&
+ rm -r dst/sub/.git/objects &&
# NOTE: without the fix the following tests will recurse forever!
# They should terminate with an error.
@@ -842,4 +1046,151 @@ test_expect_success 'recursive fetch after deinit a submodule' '
test_cmp expect actual
'
+test_expect_success 'setup repo with upstreams that share a submodule name' '
+ mkdir same-name-1 &&
+ (
+ cd same-name-1 &&
+ git init -b main &&
+ test_commit --no-tag a
+ ) &&
+ git clone same-name-1 same-name-2 &&
+ # same-name-1 and same-name-2 both add a submodule with the
+ # name "submodule"
+ (
+ cd same-name-1 &&
+ mkdir submodule &&
+ git -C submodule init -b main &&
+ test_commit -C submodule --no-tag a1 &&
+ git submodule add "$pwd/same-name-1/submodule" &&
+ git add submodule &&
+ git commit -m "super-a1"
+ ) &&
+ (
+ cd same-name-2 &&
+ mkdir submodule &&
+ git -C submodule init -b main &&
+ test_commit -C submodule --no-tag a2 &&
+ git submodule add "$pwd/same-name-2/submodule" &&
+ git add submodule &&
+ git commit -m "super-a2"
+ ) &&
+ git clone same-name-1 -o same-name-1 same-name-downstream &&
+ (
+ cd same-name-downstream &&
+ git remote add same-name-2 ../same-name-2 &&
+ git fetch --all &&
+ # init downstream with same-name-1
+ git submodule update --init
+ )
+'
+
+test_expect_success 'fetch --recurse-submodules updates name-conflicted, populated submodule' '
+ test_when_finished "git -C same-name-downstream checkout main" &&
+ (
+ cd same-name-1 &&
+ test_commit -C submodule --no-tag b1 &&
+ git add submodule &&
+ git commit -m "super-b1"
+ ) &&
+ (
+ cd same-name-2 &&
+ test_commit -C submodule --no-tag b2 &&
+ git add submodule &&
+ git commit -m "super-b2"
+ ) &&
+ (
+ cd same-name-downstream &&
+ # even though the .gitmodules is correct, we cannot
+ # fetch from same-name-2
+ git checkout same-name-2/main &&
+ git fetch --recurse-submodules same-name-1 &&
+ test_must_fail git fetch --recurse-submodules same-name-2
+ ) &&
+ super_head1=$(git -C same-name-1 rev-parse HEAD) &&
+ git -C same-name-downstream cat-file -e $super_head1 &&
+
+ super_head2=$(git -C same-name-2 rev-parse HEAD) &&
+ git -C same-name-downstream cat-file -e $super_head2 &&
+
+ sub_head1=$(git -C same-name-1/submodule rev-parse HEAD) &&
+ git -C same-name-downstream/submodule cat-file -e $sub_head1 &&
+
+ sub_head2=$(git -C same-name-2/submodule rev-parse HEAD) &&
+ test_must_fail git -C same-name-downstream/submodule cat-file -e $sub_head2
+'
+
+test_expect_success 'fetch --recurse-submodules updates name-conflicted, unpopulated submodule' '
+ (
+ cd same-name-1 &&
+ test_commit -C submodule --no-tag c1 &&
+ git add submodule &&
+ git commit -m "super-c1"
+ ) &&
+ (
+ cd same-name-2 &&
+ test_commit -C submodule --no-tag c2 &&
+ git add submodule &&
+ git commit -m "super-c2"
+ ) &&
+ (
+ cd same-name-downstream &&
+ git checkout main &&
+ git rm .gitmodules &&
+ git rm submodule &&
+ git commit -m "no submodules" &&
+ git fetch --recurse-submodules same-name-1
+ ) &&
+ head1=$(git -C same-name-1/submodule rev-parse HEAD) &&
+ head2=$(git -C same-name-2/submodule rev-parse HEAD) &&
+ (
+ cd same-name-downstream/.git/modules/submodule &&
+ # The submodule has core.worktree pointing to the "git
+ # rm"-ed directory, overwrite the invalid value. See
+ # comment in get_fetch_task_from_changed() for more
+ # information.
+ git --work-tree=. cat-file -e $head1 &&
+ test_must_fail git --work-tree=. cat-file -e $head2
+ )
+'
+
+test_expect_success 'fetch --all with --recurse-submodules' '
+ test_when_finished "rm -fr src_clone" &&
+ git clone --recurse-submodules src src_clone &&
+ (
+ cd src_clone &&
+ git config submodule.recurse true &&
+ git config fetch.parallel 0 &&
+ git fetch --all 2>../fetch-log
+ ) &&
+ grep "^Fetching submodule sub$" fetch-log >fetch-subs &&
+ test_line_count = 1 fetch-subs
+'
+
+test_expect_success 'fetch --all with --recurse-submodules with multiple' '
+ test_when_finished "rm -fr src_clone" &&
+ git clone --recurse-submodules src src_clone &&
+ (
+ cd src_clone &&
+ git remote add secondary ../src &&
+ git config submodule.recurse true &&
+ git config fetch.parallel 0 &&
+ git fetch --all 2>../fetch-log
+ ) &&
+ grep "Fetching submodule sub" fetch-log >fetch-subs &&
+ test_line_count = 2 fetch-subs
+'
+
+test_expect_success "fetch --all with --no-recurse-submodules only fetches superproject" '
+ test_when_finished "rm -rf src_clone" &&
+
+ git clone --recurse-submodules src src_clone &&
+ (
+ cd src_clone &&
+ git remote add secondary ../src &&
+ git config submodule.recurse true &&
+ git fetch --all --no-recurse-submodules 2>../fetch-log
+ ) &&
+ ! grep "Fetching submodule" fetch-log
+'
+
test_done
diff --git a/t/t5527-fetch-odd-refs.sh b/t/t5527-fetch-odd-refs.sh
index e2770e4..98ece27 100755
--- a/t/t5527-fetch-odd-refs.sh
+++ b/t/t5527-fetch-odd-refs.sh
@@ -4,6 +4,7 @@ test_description='test fetching of oddly-named refs'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# afterwards we will have:
diff --git a/t/t5528-push-default.sh b/t/t5528-push-default.sh
index f280e00..14f7ece 100755
--- a/t/t5528-push-default.sh
+++ b/t/t5528-push-default.sh
@@ -94,17 +94,92 @@ test_expect_success '"upstream" does not push when remotes do not match' '
test_must_fail git push parent2
'
-test_expect_success 'push from/to new branch with upstream, matching and simple' '
+test_expect_success '"current" does not push when multiple remotes and none origin' '
+ git checkout main &&
+ test_config push.default current &&
+ test_commit current-multi &&
+ test_must_fail git push
+'
+
+test_expect_success '"current" pushes when remote explicitly specified' '
+ git checkout main &&
+ test_config push.default current &&
+ test_commit current-specified &&
+ git push parent1
+'
+
+test_expect_success '"current" pushes to origin when no remote specified among multiple' '
+ git checkout main &&
+ test_config remote.origin.url repo1 &&
+ test_config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" &&
+ test_commit current-origin &&
+ test_push_success current main
+'
+
+test_expect_success '"current" pushes to single remote even when not specified' '
+ git checkout main &&
+ test_when_finished git remote add parent1 repo1 &&
+ git remote remove parent1 &&
+ test_commit current-implied &&
+ test_push_success current main repo2
+'
+
+test_expect_success 'push from/to new branch with non-defaulted remote fails with upstream, matching, current and simple ' '
git checkout -b new-branch &&
test_push_failure simple &&
test_push_failure matching &&
+ test_push_failure upstream &&
+ test_push_failure current
+'
+
+test_expect_success 'push from/to new branch fails with upstream and simple ' '
+ git checkout -b new-branch-1 &&
+ test_config branch.new-branch-1.remote parent1 &&
+ test_push_failure simple &&
test_push_failure upstream
'
+# The behavior here is surprising but not entirely wrong:
+# - the current branch is used to determine the target remote
+# - the "matching" push default pushes matching branches, *ignoring* the
+# current new branch as it does not have upstream tracking
+# - the default push succeeds
+#
+# A previous test expected this to fail, but for the wrong reasons:
+# it expected a fail becaause the branch is new and cannot be pushed, but
+# in fact it was failing because of an ambiguous remote
+#
+test_expect_failure 'push from/to new branch fails with matching ' '
+ git checkout -b new-branch-2 &&
+ test_config branch.new-branch-2.remote parent1 &&
+ test_push_failure matching
+'
+
+test_expect_success 'push from/to branch with tracking fails with nothing ' '
+ git checkout -b tracked-branch &&
+ test_config branch.tracked-branch.remote parent1 &&
+ test_config branch.tracked-branch.merge refs/heads/tracked-branch &&
+ test_push_failure nothing
+'
+
+test_expect_success 'push from/to new branch succeeds with upstream if push.autoSetupRemote' '
+ git checkout -b new-branch-a &&
+ test_config push.autoSetupRemote true &&
+ test_config branch.new-branch-a.remote parent1 &&
+ test_push_success upstream new-branch-a
+'
+
+test_expect_success 'push from/to new branch succeeds with simple if push.autoSetupRemote' '
+ git checkout -b new-branch-c &&
+ test_config push.autoSetupRemote true &&
+ test_config branch.new-branch-c.remote parent1 &&
+ test_push_success simple new-branch-c
+'
+
test_expect_success '"matching" fails if none match' '
git init --bare empty &&
test_must_fail git push empty : 2>actual &&
- test_i18ngrep "Perhaps you should specify a branch" actual
+ test_grep "Perhaps you should specify a branch" actual
'
test_expect_success 'push ambiguously named branch with upstream, matching and simple' '
diff --git a/t/t5529-push-errors.sh b/t/t5529-push-errors.sh
index ce85fd3..0247137 100755
--- a/t/t5529-push-errors.sh
+++ b/t/t5529-push-errors.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='detect some push errors early (before contacting remote)'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup commits' '
diff --git a/t/t5530-upload-pack-error.sh b/t/t5530-upload-pack-error.sh
index 7c1460e..7172780 100755
--- a/t/t5530-upload-pack-error.sh
+++ b/t/t5530-upload-pack-error.sh
@@ -2,6 +2,7 @@
test_description='errors in upload-pack'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
D=$(pwd)
@@ -35,8 +36,8 @@ test_expect_success 'upload-pack fails due to error in pack-objects packing' '
printf "%04xwant %s\n00000009done\n0000" \
$(($hexsz + 10)) $head >input &&
test_must_fail git upload-pack . <input >/dev/null 2>output.err &&
- test_i18ngrep "unable to read" output.err &&
- test_i18ngrep "pack-objects died" output.err
+ test_grep "unable to read" output.err &&
+ test_grep "pack-objects died" output.err
'
test_expect_success 'corrupt repo differently' '
diff --git a/t/t5531-deep-submodule-push.sh b/t/t5531-deep-submodule-push.sh
index d573ca4..f3fff55 100755
--- a/t/t5531-deep-submodule-push.sh
+++ b/t/t5531-deep-submodule-push.sh
@@ -5,6 +5,9 @@ test_description='test push with submodules'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1
+export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
+
. ./test-lib.sh
test_expect_success setup '
@@ -308,7 +311,7 @@ test_expect_success 'submodule entry pointing at a tag is error' '
git -C work commit -m "bad commit" &&
test_when_finished "git -C work reset --hard HEAD^" &&
test_must_fail git -C work push --recurse-submodules=on-demand ../pub.git main 2>err &&
- test_i18ngrep "is a tag, not a commit" err
+ test_grep "is a tag, not a commit" err
'
test_expect_success 'push fails if recurse submodules option passed as yes' '
@@ -509,6 +512,56 @@ test_expect_success 'push only unpushed submodules recursively' '
test_cmp expected_pub actual_pub
'
+setup_subsub () {
+ git init upstream &&
+ git init upstream/sub &&
+ git init upstream/sub/deepsub &&
+ test_commit -C upstream/sub/deepsub innermost &&
+ git -C upstream/sub submodule add ./deepsub deepsub &&
+ git -C upstream/sub commit -m middle &&
+ git -C upstream submodule add ./sub sub &&
+ git -C upstream commit -m outermost &&
+
+ git -c protocol.file.allow=always clone --recurse-submodules upstream downstream &&
+ git -C downstream/sub/deepsub checkout -b downstream-branch &&
+ git -C downstream/sub checkout -b downstream-branch &&
+ git -C downstream checkout -b downstream-branch
+}
+
+new_downstream_commits () {
+ test_commit -C downstream/sub/deepsub new-innermost &&
+ git -C downstream/sub add deepsub &&
+ git -C downstream/sub commit -m new-middle &&
+ git -C downstream add sub &&
+ git -C downstream commit -m new-outermost
+}
+
+test_expect_success 'push with push.recurseSubmodules=only on superproject' '
+ test_when_finished rm -rf upstream downstream &&
+ setup_subsub &&
+ new_downstream_commits &&
+ git -C downstream config push.recurseSubmodules only &&
+ git -C downstream push origin downstream-branch &&
+
+ test_must_fail git -C upstream rev-parse refs/heads/downstream-branch &&
+ git -C upstream/sub rev-parse refs/heads/downstream-branch &&
+ test_must_fail git -C upstream/sub/deepsub rev-parse refs/heads/downstream-branch
+'
+
+test_expect_success 'push with push.recurseSubmodules=only on superproject and top-level submodule' '
+ test_when_finished rm -rf upstream downstream &&
+ setup_subsub &&
+ new_downstream_commits &&
+ git -C downstream config push.recurseSubmodules only &&
+ git -C downstream/sub config push.recurseSubmodules only &&
+ git -C downstream push origin downstream-branch 2> err &&
+
+ test_must_fail git -C upstream rev-parse refs/heads/downstream-branch &&
+ git -C upstream/sub rev-parse refs/heads/downstream-branch &&
+ git -C upstream/sub/deepsub rev-parse refs/heads/downstream-branch &&
+ grep "recursing into submodule with push.recurseSubmodules=only; using on-demand instead" err
+'
+
test_expect_success 'push propagating the remotes name to a submodule' '
git -C work remote add origin ../pub.git &&
git -C work remote add pub ../pub.git &&
diff --git a/t/t5532-fetch-proxy.sh b/t/t5532-fetch-proxy.sh
index 9c27986..d664912 100755
--- a/t/t5532-fetch-proxy.sh
+++ b/t/t5532-fetch-proxy.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='fetching via git:// using core.gitproxy'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup remote repo' '
diff --git a/t/t5534-push-signed.sh b/t/t5534-push-signed.sh
index bba768f..c91a62b 100755
--- a/t/t5534-push-signed.sh
+++ b/t/t5534-push-signed.sh
@@ -35,8 +35,7 @@ test_expect_success setup '
test_expect_success 'unsigned push does not send push certificate' '
prepare_dst &&
- mkdir -p dst/.git/hooks &&
- write_script dst/.git/hooks/post-receive <<-\EOF &&
+ test_hook -C dst post-receive <<-\EOF &&
# discard the update list
cat >/dev/null
# record the push certificate
@@ -52,8 +51,7 @@ test_expect_success 'unsigned push does not send push certificate' '
test_expect_success 'talking with a receiver without push certificate support' '
prepare_dst &&
- mkdir -p dst/.git/hooks &&
- write_script dst/.git/hooks/post-receive <<-\EOF &&
+ test_hook -C dst post-receive <<-\EOF &&
# discard the update list
cat >/dev/null
# record the push certificate
@@ -69,22 +67,19 @@ test_expect_success 'talking with a receiver without push certificate support' '
test_expect_success 'push --signed fails with a receiver without push certificate support' '
prepare_dst &&
- mkdir -p dst/.git/hooks &&
test_must_fail git push --signed dst noop ff +noff 2>err &&
- test_i18ngrep "the receiving end does not support" err
+ test_grep "the receiving end does not support" err
'
test_expect_success 'push --signed=1 is accepted' '
prepare_dst &&
- mkdir -p dst/.git/hooks &&
test_must_fail git push --signed=1 dst noop ff +noff 2>err &&
- test_i18ngrep "the receiving end does not support" err
+ test_grep "the receiving end does not support" err
'
test_expect_success GPG 'no certificate for a signed push with no update' '
prepare_dst &&
- mkdir -p dst/.git/hooks &&
- write_script dst/.git/hooks/post-receive <<-\EOF &&
+ test_hook -C dst post-receive <<-\EOF &&
if test -n "${GIT_PUSH_CERT-}"
then
git cat-file blob $GIT_PUSH_CERT >../push-cert
@@ -96,9 +91,8 @@ test_expect_success GPG 'no certificate for a signed push with no update' '
test_expect_success GPG 'signed push sends push certificate' '
prepare_dst &&
- mkdir -p dst/.git/hooks &&
git -C dst config receive.certnonceseed sekrit &&
- write_script dst/.git/hooks/post-receive <<-\EOF &&
+ test_hook -C dst post-receive <<-\EOF &&
# discard the update list
cat >/dev/null
# record the push certificate
@@ -137,6 +131,52 @@ test_expect_success GPG 'signed push sends push certificate' '
test_cmp expect dst/push-cert-status
'
+test_expect_success GPGSSH 'ssh signed push sends push certificate' '
+ prepare_dst &&
+ git -C dst config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git -C dst config receive.certnonceseed sekrit &&
+ test_hook -C dst 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 gpg.format ssh &&
+ test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+ FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") &&
+ git push --signed dst noop ff +noff &&
+
+ (
+ cat <<-\EOF &&
+ SIGNER=principal with number 1
+ KEY=FINGERPRINT
+ STATUS=G
+ NONCE_STATUS=OK
+ EOF
+ sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
+ ) | sed -e "s|FINGERPRINT|$FINGERPRINT|" >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_expect_success GPG 'inconsistent push options in signed push not allowed' '
# First, invoke receive-pack with dummy input to obtain its preamble.
prepare_dst &&
@@ -176,9 +216,8 @@ test_expect_success GPG 'inconsistent push options in signed push not allowed' '
test_expect_success GPG 'fail without key and heed user.signingkey' '
prepare_dst &&
- mkdir -p dst/.git/hooks &&
git -C dst config receive.certnonceseed sekrit &&
- write_script dst/.git/hooks/post-receive <<-\EOF &&
+ test_hook -C dst post-receive <<-\EOF &&
# discard the update list
cat >/dev/null
# record the push certificate
@@ -226,9 +265,8 @@ test_expect_success GPG 'fail without key and heed user.signingkey' '
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 &&
+ test_hook -C dst post-receive <<-\EOF &&
# discard the update list
cat >/dev/null
# record the push certificate
@@ -265,7 +303,7 @@ test_expect_success GPGSM 'fail without key and heed user.signingkey x509' '
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 ":") &&
+ key=$(cut -d" " -f1 <"${GNUPGHOME}/trustlist.txt" | tr -d ":") &&
sed -e "s/^KEY=/KEY=${key}/" expect.in >expect &&
noop=$(git rev-parse noop) &&
@@ -276,6 +314,59 @@ test_expect_success GPGSM 'fail without key and heed user.signingkey x509' '
test_cmp expect dst/push-cert-status
'
+test_expect_success GPGSSH 'fail without key and heed user.signingkey ssh' '
+ test_config gpg.format ssh &&
+ prepare_dst &&
+ git -C dst config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git -C dst config receive.certnonceseed sekrit &&
+ test_hook -C dst 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 gpg.format ssh &&
+ test_config user.signingkey "" &&
+ (
+ sane_unset GIT_COMMITTER_EMAIL &&
+ test_must_fail git push --signed dst noop ff +noff
+ ) &&
+ test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+ FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") &&
+ git push --signed dst noop ff +noff &&
+
+ (
+ cat <<-\EOF &&
+ SIGNER=principal with number 1
+ KEY=FINGERPRINT
+ STATUS=G
+ NONCE_STATUS=OK
+ EOF
+ sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
+ ) | sed -e "s|FINGERPRINT|$FINGERPRINT|" >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_expect_success GPG 'failed atomic push does not execute GPG' '
prepare_dst &&
git -C dst config receive.certnonceseed sekrit &&
@@ -287,7 +378,7 @@ test_expect_success GPG 'failed atomic push does not execute GPG' '
--signed --atomic --porcelain \
dst noop ff noff >out 2>err &&
- test_i18ngrep ! "gpg failed to sign" err &&
+ test_grep ! "gpg failed to sign" err &&
cat >expect <<-EOF &&
To dst
= refs/heads/noop:refs/heads/noop [up to date]
diff --git a/t/t5536-fetch-conflicts.sh b/t/t5536-fetch-conflicts.sh
index 91f28c2..23bf696 100755
--- a/t/t5536-fetch-conflicts.sh
+++ b/t/t5536-fetch-conflicts.sh
@@ -40,7 +40,7 @@ test_expect_success 'fetch conflict: config vs. config' '
"+refs/heads/branch2:refs/remotes/origin/branch1" && (
cd ccc &&
test_must_fail git fetch origin 2>error &&
- test_i18ngrep "fatal: Cannot fetch both refs/heads/branch1 and refs/heads/branch2 to refs/remotes/origin/branch1" error
+ test_grep "fatal: Cannot fetch both refs/heads/branch1 and refs/heads/branch2 to refs/remotes/origin/branch1" error
)
'
@@ -67,7 +67,7 @@ test_expect_success 'fetch conflict: arg vs. arg' '
test_must_fail git fetch origin \
refs/heads/*:refs/remotes/origin/* \
refs/heads/branch2:refs/remotes/origin/branch1 2>error &&
- test_i18ngrep "fatal: Cannot fetch both refs/heads/branch1 and refs/heads/branch2 to refs/remotes/origin/branch1" error
+ test_grep "fatal: Cannot fetch both refs/heads/branch1 and refs/heads/branch2 to refs/remotes/origin/branch1" error
)
'
@@ -78,8 +78,8 @@ test_expect_success 'fetch conflict: criss-cross args' '
git fetch origin \
refs/heads/branch1:refs/remotes/origin/branch2 \
refs/heads/branch2:refs/remotes/origin/branch1 2>error &&
- test_i18ngrep "warning: refs/remotes/origin/branch1 usually tracks refs/heads/branch1, not refs/heads/branch2" error &&
- test_i18ngrep "warning: refs/remotes/origin/branch2 usually tracks refs/heads/branch2, not refs/heads/branch1" error
+ test_grep "warning: refs/remotes/origin/branch1 usually tracks refs/heads/branch1, not refs/heads/branch2" error &&
+ test_grep "warning: refs/remotes/origin/branch2 usually tracks refs/heads/branch2, not refs/heads/branch1" error
)
'
diff --git a/t/t5537-fetch-shallow.sh b/t/t5537-fetch-shallow.sh
index 11d5ea5..37f7547 100755
--- a/t/t5537-fetch-shallow.sh
+++ b/t/t5537-fetch-shallow.sh
@@ -161,6 +161,30 @@ test_expect_success 'fetch --update-shallow' '
)
'
+test_expect_success 'fetch --update-shallow into a repo with submodules' '
+ test_config_global protocol.file.allow always &&
+
+ git init a-submodule &&
+ test_commit -C a-submodule foo &&
+
+ test_when_finished "rm -rf repo-with-sub" &&
+ git init repo-with-sub &&
+ git -C repo-with-sub submodule add ../a-submodule a-submodule &&
+ git -C repo-with-sub commit -m "added submodule" &&
+ git -C repo-with-sub fetch --update-shallow ../shallow/.git refs/heads/*:refs/remotes/shallow/*
+'
+
+test_expect_success 'fetch --update-shallow a commit that is also a shallow point into a repo with submodules' '
+ test_when_finished "rm -rf repo-with-sub" &&
+ git init repo-with-sub &&
+ git -c protocol.file.allow=always -C repo-with-sub \
+ submodule add ../a-submodule a-submodule &&
+ git -C repo-with-sub commit -m "added submodule" &&
+
+ SHALLOW=$(cat shallow/.git/shallow) &&
+ git -C repo-with-sub fetch --update-shallow ../shallow/.git "$SHALLOW":refs/heads/a-shallow
+'
+
test_expect_success 'fetch --update-shallow (with fetch.writeCommitGraph)' '
(
cd shallow &&
diff --git a/t/t5540-http-push-webdav.sh b/t/t5540-http-push-webdav.sh
index 8b68bb3..37db3de 100755
--- a/t/t5540-http-push-webdav.sh
+++ b/t/t5540-http-push-webdav.sh
@@ -18,6 +18,12 @@ then
test_done
fi
+if test_have_prereq !REFFILES
+then
+ skip_all='skipping test; dumb HTTP protocol not supported with reftable.'
+ test_done
+fi
+
LIB_HTTPD_DAV=t
. "$TEST_DIRECTORY"/lib-httpd.sh
ROOT_PATH="$PWD"
@@ -36,7 +42,9 @@ test_expect_success 'setup remote repository' '
git clone --bare test_repo test_repo.git &&
cd test_repo.git &&
git --bare update-server-info &&
- mv hooks/post-update.sample hooks/post-update &&
+ test_hook --setup post-update <<-\EOF &&
+ exec git update-server-info
+ EOF
ORIG_HEAD=$(git rev-parse --verify HEAD) &&
cd - &&
mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH"
diff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh
index c024fa2..71428f3 100755
--- a/t/t5541-http-push-smart.sh
+++ b/t/t5541-http-push-smart.sh
@@ -36,28 +36,6 @@ test_expect_success 'setup remote repository' '
setup_askpass_helper
-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
-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 &&
-
- # NEEDSWORK: If the overspecification of the expected result is reduced, we
- # might be able to run this test in all protocol versions.
- if test "$GIT_TEST_PROTOCOL_VERSION" = 0
- then
- check_access_log exp
- fi
-'
-
test_expect_success 'clone remote repository' '
rm -rf test_repo_clone &&
git clone $HTTPD_URL/smart/test_repo.git test_repo_clone &&
@@ -67,6 +45,10 @@ test_expect_success 'clone remote repository' '
'
test_expect_success 'push to remote repository (standard)' '
+ # Clear the log, so that the "used receive-pack service" test below
+ # sees just what we did here.
+ >"$HTTPD_ROOT_PATH"/access.log &&
+
cd "$ROOT_PATH"/test_repo_clone &&
: >path2 &&
git add path2 &&
@@ -80,6 +62,34 @@ test_expect_success 'push to remote repository (standard)' '
test $HEAD = $(git rev-parse --verify HEAD))
'
+test_expect_success 'used receive-pack service' '
+ cat >exp <<-\EOF &&
+ 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
+
+ check_access_log exp
+'
+
+test_expect_success 'push to remote repository (standard) with sending Accept-Language' '
+ cat >exp <<-\EOF &&
+ => Send header: Accept-Language: ko-KR, *;q=0.9
+ => Send header: Accept-Language: ko-KR, *;q=0.9
+ EOF
+
+ cd "$ROOT_PATH"/test_repo_clone &&
+ : >path_lang &&
+ git add path_lang &&
+ test_tick &&
+ git commit -m path_lang &&
+ HEAD=$(git rev-parse --verify HEAD) &&
+ GIT_TRACE_CURL=true LANGUAGE="ko_KR.UTF-8" git push -v -v 2>err &&
+ ! grep "Expect: 100-continue" err &&
+
+ grep "=> Send header: Accept-Language:" err >err.language &&
+ test_cmp exp err.language
+'
+
test_expect_success 'push already up-to-date' '
git push
'
@@ -96,18 +106,18 @@ test_expect_success 'create and delete remote branch' '
test_must_fail git show-ref --verify refs/remotes/origin/dev
'
-cat >"$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update" <<EOF
-#!/bin/sh
-exit 1
-EOF
-chmod a+x "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update"
+test_expect_success 'setup rejected update hook' '
+ test_hook --setup -C "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" update <<-\EOF &&
+ exit 1
+ EOF
-cat >exp <<EOF
-remote: error: hook declined to update refs/heads/dev2
-To http://127.0.0.1:$LIB_HTTPD_PORT/smart/test_repo.git
- ! [remote rejected] dev2 -> dev2 (hook declined)
-error: failed to push some refs to 'http://127.0.0.1:$LIB_HTTPD_PORT/smart/test_repo.git'
-EOF
+ cat >exp <<-EOF
+ remote: error: hook declined to update refs/heads/dev2
+ To http://127.0.0.1:$LIB_HTTPD_PORT/smart/test_repo.git
+ ! [remote rejected] dev2 -> dev2 (hook declined)
+ error: failed to push some refs to '\''http://127.0.0.1:$LIB_HTTPD_PORT/smart/test_repo.git'\''
+ EOF
+'
test_expect_success 'rejected update prints status' '
cd "$ROOT_PATH"/test_repo_clone &&
@@ -122,28 +132,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
-POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
-GET /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
-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
-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
-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' '
- # NEEDSWORK: If the overspecification of the expected result is reduced, we
- # might be able to run this test in all protocol versions.
- if test "$GIT_TEST_PROTOCOL_VERSION" = 0
- then
- check_access_log exp
- fi
-'
-
test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
"$ROOT_PATH"/test_repo_clone main success
@@ -165,7 +153,7 @@ test_expect_success 'push fails for non-fast-forward refs unmatched by remote he
'
test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper: our output' '
- test_i18ngrep "Updates were rejected because" \
+ test_grep "Updates were rejected because" \
output
'
@@ -244,8 +232,9 @@ test_expect_success 'push --atomic fails on server-side errors' '
test_config -C "$d" http.receivepack true &&
up="$HTTPD_URL"/smart/atomic-branches.git &&
- # break ref updates for other on the remote site
- mkdir "$d/refs/heads/other.lock" &&
+ # Create d/f conflict to break ref updates for other on the remote site.
+ git -C "$d" update-ref -d refs/heads/other &&
+ git -C "$d" update-ref refs/heads/other/conflict HEAD &&
# add the new commit to other
git branch -f other collateral &&
@@ -253,18 +242,9 @@ test_expect_success 'push --atomic fails on server-side errors' '
# --atomic should cause entire push to be rejected
test_must_fail git push --atomic "$up" atomic other 2>output &&
- # the new branch should not have been created upstream
- test_must_fail git -C "$d" show-ref --verify refs/heads/atomic &&
-
- # upstream should still reflect atomic2, the last thing we pushed
- # successfully
- git rev-parse atomic2 >expected &&
- # ...to other.
- git -C "$d" rev-parse refs/heads/other >actual &&
- test_cmp expected actual &&
-
- # the new branch should not have been created upstream
+ # The atomic and other branches should not be created upstream.
test_must_fail git -C "$d" show-ref --verify refs/heads/atomic &&
+ test_must_fail git -C "$d" show-ref --verify refs/heads/other &&
# the failed refs should be indicated to the user
grep "^ ! .*rejected.* other -> other .*atomic transaction failed" output &&
@@ -309,7 +289,7 @@ test_expect_success TTY 'push shows progress when stderr is a tty' '
cd "$ROOT_PATH"/test_repo_clone &&
test_commit noisy &&
test_terminal git push >output 2>&1 &&
- test_i18ngrep "^Writing objects" output
+ test_grep "^Writing objects" output
'
test_expect_success TTY 'push --quiet silences status and progress' '
@@ -323,16 +303,16 @@ test_expect_success TTY 'push --no-progress silences progress but not status' '
cd "$ROOT_PATH"/test_repo_clone &&
test_commit no-progress &&
test_terminal git push --no-progress >output 2>&1 &&
- test_i18ngrep "^To http" output &&
- test_i18ngrep ! "^Writing objects" output
+ test_grep "^To http" output &&
+ test_grep ! "^Writing objects" output
'
test_expect_success 'push --progress shows progress to non-tty' '
cd "$ROOT_PATH"/test_repo_clone &&
test_commit progress &&
git push --progress >output 2>&1 &&
- test_i18ngrep "^To http" output &&
- test_i18ngrep "^Writing objects" output
+ test_grep "^To http" output &&
+ test_grep "^Writing objects" output
'
test_expect_success 'http push gives sane defaults to reflog' '
@@ -419,10 +399,7 @@ test_expect_success CMDLINE_LIMIT 'push 2000 tags over http' '
'
test_expect_success GPG 'push with post-receive to inspect certificate' '
- (
- cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
- mkdir -p hooks &&
- write_script hooks/post-receive <<-\EOF &&
+ test_hook -C "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git post-receive <<-\EOF &&
# discard the update list
cat >/dev/null
# record the push certificate
@@ -437,8 +414,9 @@ test_expect_success GPG 'push with post-receive to inspect certificate' '
NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus}
NONCE=${GIT_PUSH_CERT_NONCE-nononce}
E_O_F
- EOF
-
+ EOF
+ (
+ cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
git config receive.certnonceseed sekrit &&
git config receive.certnonceslop 30
) &&
@@ -503,10 +481,26 @@ test_expect_success 'colorize errors/hints' '
-c color.push=always \
push origin origin/main^:main 2>act &&
test_decode_color <act >decoded &&
- test_i18ngrep "<RED>.*rejected.*<RESET>" decoded &&
- test_i18ngrep "<RED>error: failed to push some refs" decoded &&
- test_i18ngrep "<YELLOW>hint: " decoded &&
- test_i18ngrep ! "^hint: " decoded
+ test_grep "<RED>.*rejected.*<RESET>" decoded &&
+ test_grep "<RED>error: failed to push some refs" decoded &&
+ test_grep "<YELLOW>hint: " decoded &&
+ test_grep ! "^hint: " decoded
+'
+
+test_expect_success 'report error server does not provide ref status' '
+ git init "$HTTPD_DOCUMENT_ROOT_PATH/no_report" &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/no_report" config http.receivepack true &&
+ test_must_fail git push --porcelain \
+ $HTTPD_URL_USER_PASS/smart/no_report \
+ HEAD:refs/tags/will-fail >actual &&
+ test_must_fail git -C "$HTTPD_DOCUMENT_ROOT_PATH/no_report" \
+ rev-parse --verify refs/tags/will-fail &&
+ cat >expect <<-EOF &&
+ To $HTTPD_URL/smart/no_report
+ ! HEAD:refs/tags/will-fail [remote failure] (remote failed to report status)
+ Done
+ EOF
+ test_cmp expect actual
'
test_done
diff --git a/t/t5543-atomic-push.sh b/t/t5543-atomic-push.sh
index bfee461..04b47ad 100755
--- a/t/t5543-atomic-push.sh
+++ b/t/t5543-atomic-push.sh
@@ -117,7 +117,10 @@ test_expect_success 'atomic push fails if one branch fails' '
test_commit five &&
git checkout main &&
test_commit six &&
- test_must_fail git push --atomic --all up
+ test_must_fail git push --atomic --all up >output-all 2>&1 &&
+ # --all and --branches have the same behavior when be combined with --atomic
+ test_must_fail git push --atomic --branches up >output-branches 2>&1 &&
+ test_cmp output-all output-branches
) &&
test_refs main HEAD@{7} &&
test_refs second HEAD@{4}
@@ -162,16 +165,10 @@ test_expect_success 'atomic push obeys update hook preventing a branch to be pus
test_commit two &&
git push --mirror up
) &&
- (
- cd upstream &&
- HOOKDIR="$(git rev-parse --git-dir)/hooks" &&
- HOOK="$HOOKDIR/update" &&
- mkdir -p "$HOOKDIR" &&
- write_script "$HOOK" <<-\EOF
- # only allow update to main from now on
- test "$1" = "refs/heads/main"
- EOF
- ) &&
+ test_hook -C upstream update <<-\EOF &&
+ # only allow update to main from now on
+ test "$1" = "refs/heads/main"
+ EOF
(
cd workbench &&
git checkout main &&
diff --git a/t/t5544-pack-objects-hook.sh b/t/t5544-pack-objects-hook.sh
index dd5f44d..1a9e14b 100755
--- a/t/t5544-pack-objects-hook.sh
+++ b/t/t5544-pack-objects-hook.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test custom script in place of pack-objects'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'create some history to fetch' '
@@ -56,7 +58,12 @@ test_expect_success 'hook does not run from repo config' '
! grep "hook running" stderr &&
test_path_is_missing .git/hook.args &&
test_path_is_missing .git/hook.stdin &&
- test_path_is_missing .git/hook.stdout
+ test_path_is_missing .git/hook.stdout &&
+
+ # check that global config is used instead
+ test_config_global uploadpack.packObjectsHook ./hook &&
+ git clone --no-local . dst2.git 2>stderr &&
+ grep "hook running" stderr
'
test_expect_success 'hook works with partial clone' '
diff --git a/t/t5545-push-options.sh b/t/t5545-push-options.sh
index 58c7add..fb13549 100755
--- a/t/t5545-push-options.sh
+++ b/t/t5545-push-options.sh
@@ -5,6 +5,9 @@ test_description='pushing to a repository using push options'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1
+export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
+
. ./test-lib.sh
mk_repo_pair () {
@@ -116,6 +119,7 @@ test_expect_success 'push options and submodules' '
test_commit -C parent one &&
git -C parent push --mirror up &&
+ test_config_global protocol.file.allow always &&
git -C parent submodule add ../upstream workbench &&
git -C parent/workbench remote add up ../../upstream &&
git -C parent commit -m "add submodule" &&
@@ -248,7 +252,7 @@ test_expect_success 'push option denied properly by http server' '
mk_http_pair false &&
test_commit -C test_http_clone one &&
test_must_fail git -C test_http_clone push --push-option=asdf origin main 2>actual &&
- test_i18ngrep "the receiving end does not support push options" actual &&
+ test_grep "the receiving end does not support push options" actual &&
git -C test_http_clone push origin main
'
diff --git a/t/t5546-receive-limits.sh b/t/t5546-receive-limits.sh
index 0b0e987..9fc9ba5 100755
--- a/t/t5546-receive-limits.sh
+++ b/t/t5546-receive-limits.sh
@@ -1,16 +1,34 @@
#!/bin/sh
test_description='check receive input limits'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Let's run tests with different unpack limits: 1 and 10000
# When the limit is 1, `git receive-pack` will call `git index-pack`.
# When the limit is 10000, `git receive-pack` will call `git unpack-objects`.
+validate_store_type () {
+ git -C dest count-objects -v >actual &&
+ case "$store_type" in
+ index)
+ grep "^count: 0$" actual ;;
+ unpack)
+ grep "^packs: 0$" actual ;;
+ esac || {
+ echo "store_type is $store_type"
+ cat actual
+ false;
+ }
+}
+
test_pack_input_limit () {
- case "$1" in
- index) unpack_limit=1 ;;
- unpack) unpack_limit=10000 ;;
+ store_type=$1
+
+ case "$store_type" in
+ index) unpack_limit=1 other_limit=10000 ;;
+ unpack) unpack_limit=10000 other_limit=1 ;;
esac
test_expect_success 'prepare destination repository' '
@@ -41,6 +59,19 @@ test_pack_input_limit () {
git --git-dir=dest config receive.maxInputSize 0 &&
git push dest HEAD
'
+
+ test_expect_success 'prepare destination repository (once more)' '
+ rm -fr dest &&
+ git --bare init dest
+ '
+
+ test_expect_success 'receive trumps transfer' '
+ git --git-dir=dest config receive.unpacklimit "$unpack_limit" &&
+ git --git-dir=dest config transfer.unpacklimit "$other_limit" &&
+ git push dest HEAD &&
+ validate_store_type
+ '
+
}
test_expect_success "create known-size (1024 bytes) commit" '
diff --git a/t/t5547-push-quarantine.sh b/t/t5547-push-quarantine.sh
index faaa51c..9f899b8 100755
--- a/t/t5547-push-quarantine.sh
+++ b/t/t5547-push-quarantine.sh
@@ -1,11 +1,13 @@
#!/bin/sh
test_description='check quarantine of objects during push'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'create picky dest repo' '
git init --bare dest.git &&
- write_script dest.git/hooks/pre-receive <<-\EOF
+ test_hook --setup -C dest.git pre-receive <<-\EOF
while read old new ref; do
test "$(git log -1 --format=%s $new)" = reject && exit 1
done
@@ -60,7 +62,7 @@ test_expect_success 'push to repo path with path separator (colon)' '
test_expect_success 'updating a ref from quarantine is forbidden' '
git init --bare update.git &&
- write_script update.git/hooks/pre-receive <<-\EOF &&
+ test_hook -C update.git pre-receive <<-\EOF &&
read old new refname
git update-ref refs/heads/unrelated $new
exit 1
diff --git a/t/t5548-push-porcelain.sh b/t/t5548-push-porcelain.sh
index f11ff57..6282728 100755
--- a/t/t5548-push-porcelain.sh
+++ b/t/t5548-push-porcelain.sh
@@ -168,7 +168,7 @@ run_git_push_porcelain_output_test() {
'
test_expect_success "prepare pre-receive hook ($PROTOCOL)" '
- write_script "$upstream/hooks/pre-receive" <<-EOF
+ test_hook --setup -C "$upstream" pre-receive <<-EOF
exit 1
EOF
'
diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh
index 6d9142a..4c3b327 100755
--- a/t/t5550-http-fetch-dumb.sh
+++ b/t/t5550-http-fetch-dumb.sh
@@ -5,6 +5,13 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
+
+if test_have_prereq !REFFILES
+then
+ skip_all='skipping test; dumb HTTP protocol not supported with reftable.'
+ test_done
+fi
+
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
@@ -18,16 +25,17 @@ test_expect_success 'setup repository' '
git commit -m two
'
+setup_post_update_server_info_hook () {
+ test_hook --setup -C "$1" post-update <<-\EOF &&
+ exec git update-server-info
+ EOF
+ git -C "$1" update-server-info
+}
+
test_expect_success 'create http-accessible bare repository with loose objects' '
cp -R .git "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
- (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
- git config core.bare true &&
- mkdir -p hooks &&
- write_script "hooks/post-update" <<-\EOF &&
- exec git update-server-info
- EOF
- hooks/post-update
- ) &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" config core.bare true &&
+ setup_post_update_server_info_hook "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
git push public main:main
'
@@ -55,20 +63,14 @@ test_expect_success 'create password-protected repository' '
test_expect_success 'create empty remote repository' '
git init --bare "$HTTPD_DOCUMENT_ROOT_PATH/empty.git" &&
- (cd "$HTTPD_DOCUMENT_ROOT_PATH/empty.git" &&
- mkdir -p hooks &&
- write_script "hooks/post-update" <<-\EOF &&
- exec git update-server-info
- EOF
- hooks/post-update
- )
+ setup_post_update_server_info_hook "$HTTPD_DOCUMENT_ROOT_PATH/empty.git"
'
-test_expect_success 'empty dumb HTTP repository has default hash algorithm' '
+test_expect_success 'empty dumb HTTP repository falls back to SHA1' '
test_when_finished "rm -fr clone-empty" &&
git clone $HTTPD_URL/dumb/empty.git clone-empty &&
git -C clone-empty rev-parse --show-object-format >empty-format &&
- test "$(cat empty-format)" = "$(test_oid algo)"
+ test "$(cat empty-format)" = sha1
'
setup_askpass_helper
@@ -232,7 +234,7 @@ test_expect_success 'http-fetch --packfile' '
--index-pack-arg=--keep \
"$HTTPD_URL"/dumb/repo_pack.git/$p >out &&
- grep "^keep.[0-9a-f]\{16,\}$" out &&
+ grep -E "^keep.[0-9a-f]{16,}$" out &&
cut -c6- out >packhash &&
# Ensure that the expected files are generated
@@ -367,14 +369,14 @@ ja;q=0.95, zh;q=0.94, sv;q=0.93, pt;q=0.92, nb;q=0.91, *;q=0.90" \
ko_KR.EUC-KR:en_US.UTF-8:fr_CA:de.UTF-8@euro:sr@latin:ja:zh:sv:pt:nb
'
-test_expect_success 'git client does not send an empty Accept-Language' '
+test_expect_success 'git client send an empty Accept-Language' '
GIT_TRACE_CURL=true LANGUAGE= git ls-remote "$HTTPD_URL/dumb/repo.git" 2>stderr &&
! grep "^=> Send header: Accept-Language:" stderr
'
test_expect_success 'remote-http complains cleanly about malformed urls' '
test_must_fail git remote-http http::/example.com/repo.git 2>stderr &&
- test_i18ngrep "url has no scheme" stderr
+ test_grep "url has no scheme" stderr
'
# NEEDSWORK: Writing commands to git-remote-curl can race against the latter
@@ -383,7 +385,7 @@ test_expect_success 'remote-http complains cleanly about malformed urls' '
test_expect_success 'remote-http complains cleanly about empty scheme' '
test_must_fail ok=sigpipe git ls-remote \
http::${HTTPD_URL#http}/dumb/repo.git 2>stderr &&
- test_i18ngrep "url has no scheme" stderr
+ test_grep "url has no scheme" stderr
'
test_expect_success 'redirects can be forbidden/allowed' '
@@ -395,7 +397,7 @@ test_expect_success 'redirects can be forbidden/allowed' '
test_expect_success 'redirects are reported to stderr' '
# just look for a snippet of the redirected-to URL
- test_i18ngrep /dumb/ stderr
+ test_grep /dumb/ stderr
'
test_expect_success 'non-initial redirects can be forbidden' '
@@ -420,7 +422,8 @@ test_expect_success 'set up evil alternates scheme' '
sha1=$(git -C "$victim" rev-parse HEAD) &&
evil=$HTTPD_DOCUMENT_ROOT_PATH/evil.git &&
- git init --bare "$evil" &&
+ git init --template= --bare "$evil" &&
+ mkdir "$evil/info" &&
# do this by hand to avoid object existence check
printf "%s\\t%s\\n" $sha1 refs/heads/main >"$evil/info/refs"
'
@@ -463,7 +466,7 @@ test_expect_success 'can redirect through non-"info/refs?service=git-upload-pack
test_expect_success 'print HTTP error when any intermediate redirect throws error' '
test_must_fail git clone "$HTTPD_URL/redir-to/502" 2> stderr &&
- test_i18ngrep "unable to access.*/redir-to/502" stderr
+ test_grep "unable to access.*/redir-to/502" stderr
'
test_expect_success 'fetching via http alternates works' '
diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh
index 4f87d90..a623a10 100755
--- a/t/t5551-http-fetch-smart.sh
+++ b/t/t5551-http-fetch-smart.sh
@@ -1,13 +1,19 @@
#!/bin/sh
-test_description='test smart fetching over http via http-backend'
+: ${HTTP_PROTO:=HTTP/1.1}
+test_description="test smart fetching over http via http-backend ($HTTP_PROTO)"
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-httpd.sh
+test "$HTTP_PROTO" = "HTTP/2" && enable_http2
start_httpd
+test_expect_success HTTP2 'enable client-side http/2' '
+ git config --global http.version HTTP/2
+'
+
test_expect_success 'setup repository' '
git config push.default matching &&
echo content >file &&
@@ -27,32 +33,71 @@ test_expect_success 'create http-accessible bare repository' '
setup_askpass_helper
test_expect_success 'clone http repository' '
- cat >exp <<-\EOF &&
- > GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1
- > Accept: */*
- > Accept-Encoding: ENCODINGS
- > Pragma: no-cache
- < HTTP/1.1 200 OK
- < Pragma: no-cache
- < Cache-Control: no-cache, max-age=0, must-revalidate
- < Content-Type: application/x-git-upload-pack-advertisement
- > POST /smart/repo.git/git-upload-pack HTTP/1.1
- > Accept-Encoding: ENCODINGS
- > Content-Type: application/x-git-upload-pack-request
- > Accept: application/x-git-upload-pack-result
- > Content-Length: xxx
- < HTTP/1.1 200 OK
- < Pragma: no-cache
- < Cache-Control: no-cache, max-age=0, must-revalidate
- < Content-Type: application/x-git-upload-pack-result
+ if test_have_prereq HTTP2 && test "$HTTPD_PROTO" = "https"
+ then
+ # ALPN lets us immediately use HTTP/2; likewise, POSTs with
+ # bodies can use it because they do not need to upgrade
+ INITIAL_PROTO=HTTP/2
+ else
+ # either we are not using HTTP/2, or the initial
+ # request is sent via HTTP/1.1 and asks for upgrade
+ INITIAL_PROTO=HTTP/1.1
+ fi &&
+
+ cat >exp.raw <<-EOF &&
+ > GET /smart/repo.git/info/refs?service=git-upload-pack $INITIAL_PROTO
+ > accept: */*
+ > accept-encoding: ENCODINGS
+ > accept-language: ko-KR, *;q=0.9
+ > pragma: no-cache
+ {V2} > git-protocol: version=2
+ < $HTTP_PROTO 200 OK
+ < pragma: no-cache
+ < cache-control: no-cache, max-age=0, must-revalidate
+ < content-type: application/x-git-upload-pack-advertisement
+ > POST /smart/repo.git/git-upload-pack $INITIAL_PROTO
+ > accept-encoding: ENCODINGS
+ > content-type: application/x-git-upload-pack-request
+ > accept: application/x-git-upload-pack-result
+ > accept-language: ko-KR, *;q=0.9
+ {V2} > git-protocol: version=2
+ > content-length: xxx
+ < $INITIAL_PROTO 200 OK
+ < pragma: no-cache
+ < cache-control: no-cache, max-age=0, must-revalidate
+ < content-type: application/x-git-upload-pack-result
+ {V2} > POST /smart/repo.git/git-upload-pack $INITIAL_PROTO
+ {V2} > accept-encoding: ENCODINGS
+ {V2} > content-type: application/x-git-upload-pack-request
+ {V2} > accept: application/x-git-upload-pack-result
+ {V2} > accept-language: ko-KR, *;q=0.9
+ {V2} > git-protocol: version=2
+ {V2} > content-length: xxx
+ {V2} < $INITIAL_PROTO 200 OK
+ {V2} < pragma: no-cache
+ {V2} < cache-control: no-cache, max-age=0, must-revalidate
+ {V2} < content-type: application/x-git-upload-pack-result
EOF
- GIT_TRACE_CURL=true GIT_TEST_PROTOCOL_VERSION=0 \
+
+ if test "$GIT_TEST_PROTOCOL_VERSION" = 0
+ then
+ sed "/^{V2}/d" <exp.raw >exp
+ else
+ sed "s/^{V2} //" <exp.raw >exp
+ fi &&
+
+ GIT_TRACE_CURL=true LANGUAGE="ko_KR.UTF-8" \
git clone --quiet $HTTPD_URL/smart/repo.git clone 2>err &&
test_cmp file clone/file &&
tr '\''\015'\'' Q <err |
+ perl -pe '\''
+ s/(Send|Recv) header: ([A-Za-z0-9-]+):/
+ "$1 header: " . lc($2) . ":"
+ /e;
+ '\'' |
sed -e "
s/Q\$//
- /^[*] /d
+ /^[^<=]/d
/^== Info:/d
/^=> Send header, /d
/^=> Send header:$/d
@@ -62,6 +107,8 @@ test_expect_success 'clone http repository' '
s/= Recv header://
/^<= Recv data/d
/^=> Send data/d
+ /^<= Recv SSL data/d
+ /^=> Send SSL data/d
/^$/d
/^< $/d
@@ -69,33 +116,35 @@ test_expect_success 'clone http repository' '
s/^/> /
}
- /^> User-Agent: /d
- /^> Host: /d
+ /^< HTTP/ {
+ s/200$/200 OK/
+ }
+ /^< HTTP\\/1.1 101/d
+ /^[><] connection: /d
+ /^[><] upgrade: /d
+ /^> http2-settings: /d
+
+ /^> user-agent: /d
+ /^> host: /d
/^> POST /,$ {
/^> Accept: [*]\\/[*]/d
}
- s/^> Content-Length: .*/> Content-Length: xxx/
+ s/^> content-length: .*/> content-length: xxx/
/^> 00..want /d
/^> 00.*done/d
- /^< Server: /d
- /^< Expires: /d
- /^< Date: /d
- /^< Content-Length: /d
- /^< Transfer-Encoding: /d
+ /^< server: /d
+ /^< expires: /d
+ /^< date: /d
+ /^< content-length: /d
+ /^< transfer-encoding: /d
" >actual &&
- # NEEDSWORK: If the overspecification of the expected result is reduced, we
- # might be able to run this test in all protocol versions.
- if test "$GIT_TEST_PROTOCOL_VERSION" = 0
- then
- sed -e "s/^> Accept-Encoding: .*/> Accept-Encoding: ENCODINGS/" \
- actual >actual.smudged &&
- test_cmp exp actual.smudged &&
+ sed -e "s/^> accept-encoding: .*/> accept-encoding: ENCODINGS/" \
+ actual >actual.smudged &&
+ test_cmp exp actual.smudged &&
- grep "Accept-Encoding:.*gzip" actual >actual.gzip &&
- test_line_count = 2 actual.gzip
- fi
+ grep "accept-encoding:.*gzip" actual >actual.gzip
'
test_expect_success 'fetch changes via http' '
@@ -107,19 +156,9 @@ test_expect_success 'fetch changes via http' '
'
test_expect_success 'used upload-pack service' '
- cat >exp <<-\EOF &&
- 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
- 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
-
- # NEEDSWORK: If the overspecification of the expected result is reduced, we
- # might be able to run this test in all protocol versions.
- if test "$GIT_TEST_PROTOCOL_VERSION" = 0
- then
- check_access_log exp
- fi
+ strip_access_log >log &&
+ grep "GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/[0-9.]* 200" log &&
+ grep "POST /smart/repo.git/git-upload-pack HTTP/[0-9.]* 200" log
'
test_expect_success 'follow redirects (301)' '
@@ -175,8 +214,8 @@ test_expect_success 'no-op half-auth fetch does not require a password' '
# This is not possible with protocol v2, since both objects and refs
# are obtained from the "git-upload-pack" path. A solution to this is
# to teach the server and client to be able to inline ls-refs requests
- # as an Extra Parameter (see pack-protocol.txt), so that "info/refs"
- # can serve refs, just like it does in protocol v0.
+ # as an Extra Parameter (see "git help gitformat-pack-protocol"), so that
+ # "info/refs" can serve refs, just like it does in protocol v0.
GIT_TEST_PROTOCOL_VERSION=0 git --git-dir=half-auth fetch &&
expect_askpass none
'
@@ -196,8 +235,8 @@ test_expect_success 'GIT_TRACE_CURL redacts auth details' '
# Ensure that there is no "Basic" followed by a base64 string, but that
# the auth details are redacted
- ! grep "Authorization: Basic [0-9a-zA-Z+/]" trace &&
- grep "Authorization: Basic <redacted>" trace
+ ! grep -i "Authorization: Basic [0-9a-zA-Z+/]" trace &&
+ grep -i "Authorization: Basic <redacted>" trace
'
test_expect_success 'GIT_CURL_VERBOSE redacts auth details' '
@@ -208,8 +247,8 @@ test_expect_success 'GIT_CURL_VERBOSE redacts auth details' '
# Ensure that there is no "Basic" followed by a base64 string, but that
# the auth details are redacted
- ! grep "Authorization: Basic [0-9a-zA-Z+/]" trace &&
- grep "Authorization: Basic <redacted>" trace
+ ! grep -i "Authorization: Basic [0-9a-zA-Z+/]" trace &&
+ grep -i "Authorization: Basic <redacted>" trace
'
test_expect_success 'GIT_TRACE_CURL does not redact auth details if GIT_TRACE_REDACT=0' '
@@ -219,7 +258,7 @@ test_expect_success 'GIT_TRACE_CURL does not redact auth details if GIT_TRACE_RE
git clone --bare "$HTTPD_URL/auth/smart/repo.git" redact-auth &&
expect_askpass both user@host &&
- grep "Authorization: Basic [0-9a-zA-Z+/]" trace
+ grep -i "Authorization: Basic [0-9a-zA-Z+/]" trace
'
test_expect_success 'disable dumb http on server' '
@@ -236,7 +275,7 @@ test_expect_success 'GIT_SMART_HTTP can disable smart http' '
test_expect_success 'invalid Content-Type rejected' '
test_must_fail git clone $HTTPD_URL/broken_smart/repo.git 2>actual &&
- test_i18ngrep "not valid:" actual
+ test_grep "not valid:" actual
'
test_expect_success 'create namespaced refs' '
@@ -268,21 +307,23 @@ test_expect_success 'cookies stored in http.cookiefile when http.savecookies set
127.0.0.1 FALSE /smart_cookies/ FALSE 0 othername othervalue
EOF
sort >expect_cookies.txt <<-\EOF &&
-
127.0.0.1 FALSE /smart_cookies/ FALSE 0 othername othervalue
+ 127.0.0.1 FALSE /smart_cookies/repo.git/ FALSE 0 name value
127.0.0.1 FALSE /smart_cookies/repo.git/info/ FALSE 0 name value
EOF
git config http.cookiefile cookies.txt &&
git config http.savecookies true &&
- git ls-remote $HTTPD_URL/smart_cookies/repo.git main &&
- # NEEDSWORK: If the overspecification of the expected result is reduced, we
- # might be able to run this test in all protocol versions.
- if test "$GIT_TEST_PROTOCOL_VERSION" = 0
- then
- tail -3 cookies.txt | sort >cookies_tail.txt &&
- test_cmp expect_cookies.txt cookies_tail.txt
- fi
+ test_when_finished "
+ git --git-dir=\"\$HTTPD_DOCUMENT_ROOT_PATH/repo.git\" \
+ tag -d cookie-tag
+ " &&
+ git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
+ tag -m "foo" cookie-tag &&
+ git fetch $HTTPD_URL/smart_cookies/repo.git cookie-tag &&
+
+ grep "^[^#]" cookies.txt | sort >cookies_stripped.txt &&
+ test_cmp expect_cookies.txt cookies_stripped.txt
'
test_expect_success 'transfer.hiderefs works over smart-http' '
@@ -318,7 +359,9 @@ create_tags () {
# now assign tags to all the dangling commits we created above
tag=$(perl -e "print \"bla\" x 30") &&
- sed -e "s|^:\([^ ]*\) \(.*\)$|\2 refs/tags/$tag-\1|" <marks >>packed-refs
+ sed -e "s|^:\([^ ]*\) \(.*\)$|create refs/tags/$tag-\1 \2|" <marks >input &&
+ git update-ref --stdin <input &&
+ rm input
}
test_expect_success 'create 2,000 tags in the repo' '
@@ -341,7 +384,10 @@ test_expect_success CMDLINE_LIMIT \
test_expect_success 'large fetch-pack requests can be sent using chunked encoding' '
GIT_TRACE_CURL=true git -c http.postbuffer=65536 \
clone --bare "$HTTPD_URL/smart/repo.git" split.git 2>err &&
- grep "^=> Send header: Transfer-Encoding: chunked" err
+ {
+ test_have_prereq HTTP2 ||
+ grep "^=> Send header: Transfer-Encoding: chunked" err
+ }
'
test_expect_success 'test allowreachablesha1inwant' '
@@ -474,10 +520,10 @@ test_expect_success 'cookies are redacted by default' '
GIT_TRACE_CURL=true \
git -c "http.cookieFile=$(pwd)/cookies" clone \
$HTTPD_URL/smart/repo.git clone 2>err &&
- grep "Cookie:.*Foo=<redacted>" err &&
- grep "Cookie:.*Bar=<redacted>" err &&
- ! grep "Cookie:.*Foo=1" err &&
- ! grep "Cookie:.*Bar=2" err
+ grep -i "Cookie:.*Foo=<redacted>" err &&
+ grep -i "Cookie:.*Bar=<redacted>" err &&
+ ! grep -i "Cookie:.*Foo=1" err &&
+ ! grep -i "Cookie:.*Bar=2" err
'
test_expect_success 'empty values of cookies are also redacted' '
@@ -486,7 +532,7 @@ test_expect_success 'empty values of cookies are also redacted' '
GIT_TRACE_CURL=true \
git -c "http.cookieFile=$(pwd)/cookies" clone \
$HTTPD_URL/smart/repo.git clone 2>err &&
- grep "Cookie:.*Foo=<redacted>" err
+ grep -i "Cookie:.*Foo=<redacted>" err
'
test_expect_success 'GIT_TRACE_REDACT=0 disables cookie redaction' '
@@ -496,8 +542,8 @@ test_expect_success 'GIT_TRACE_REDACT=0 disables cookie redaction' '
GIT_TRACE_REDACT=0 GIT_TRACE_CURL=true \
git -c "http.cookieFile=$(pwd)/cookies" clone \
$HTTPD_URL/smart/repo.git clone 2>err &&
- grep "Cookie:.*Foo=1" err &&
- grep "Cookie:.*Bar=2" err
+ grep -i "Cookie:.*Foo=1" err &&
+ grep -i "Cookie:.*Bar=2" err
'
test_expect_success 'GIT_TRACE_CURL_NO_DATA prevents data from being traced' '
@@ -514,7 +560,7 @@ test_expect_success 'GIT_TRACE_CURL_NO_DATA prevents data from being traced' '
test_expect_success 'server-side error detected' '
test_must_fail git clone $HTTPD_URL/error_smart/repo.git 2>actual &&
- test_i18ngrep "server-side error" actual
+ test_grep "server-side error" actual
'
test_expect_success 'http auth remembers successful credentials' '
@@ -558,4 +604,151 @@ test_expect_success 'http auth forgets bogus credentials' '
expect_askpass both user@host
'
+test_expect_success 'client falls back from v2 to v0 to match server' '
+ GIT_TRACE_PACKET=$PWD/trace \
+ GIT_TEST_PROTOCOL_VERSION=2 \
+ git clone $HTTPD_URL/smart_v0/repo.git repo-v0 &&
+ # check for v0; there the HEAD symref is communicated in the capability
+ # line; v2 uses a different syntax on each ref advertisement line
+ grep symref=HEAD:refs/heads/ trace
+'
+
+test_expect_success 'create empty http-accessible SHA-256 repository' '
+ mkdir "$HTTPD_DOCUMENT_ROOT_PATH/sha256.git" &&
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH/sha256.git" &&
+ git --bare init --object-format=sha256
+ )
+'
+
+test_expect_success 'clone empty SHA-256 repository with protocol v2' '
+ rm -fr sha256 &&
+ echo sha256 >expected &&
+ git -c protocol.version=2 clone "$HTTPD_URL/smart/sha256.git" &&
+ git -C sha256 rev-parse --show-object-format >actual &&
+ test_cmp actual expected &&
+ git ls-remote "$HTTPD_URL/smart/sha256.git" >actual &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'clone empty SHA-256 repository with protocol v0' '
+ rm -fr sha256 &&
+ echo sha256 >expected &&
+ GIT_TRACE=1 GIT_TRACE_PACKET=1 git -c protocol.version=0 clone "$HTTPD_URL/smart/sha256.git" &&
+ git -C sha256 rev-parse --show-object-format >actual &&
+ test_cmp actual expected &&
+ git ls-remote "$HTTPD_URL/smart/sha256.git" >actual &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'passing hostname resolution information works' '
+ BOGUS_HOST=gitbogusexamplehost.invalid &&
+ BOGUS_HTTPD_URL=$HTTPD_PROTO://$BOGUS_HOST:$LIB_HTTPD_PORT &&
+ test_must_fail git ls-remote "$BOGUS_HTTPD_URL/smart/repo.git" >/dev/null &&
+ git -c "http.curloptResolve=$BOGUS_HOST:$LIB_HTTPD_PORT:127.0.0.1" ls-remote "$BOGUS_HTTPD_URL/smart/repo.git" >/dev/null
+'
+
+# here user%40host is the URL-encoded version of user@host,
+# which is our intentionally-odd username to catch parsing errors
+url_user=$HTTPD_URL_USER/auth/smart/repo.git
+url_userpass=$HTTPD_URL_USER_PASS/auth/smart/repo.git
+url_userblank=$HTTPD_PROTO://user%40host:@$HTTPD_DEST/auth/smart/repo.git
+message="URL .*:<redacted>@.* uses plaintext credentials"
+
+test_expect_success 'clone warns or fails when using username:password' '
+ test_when_finished "rm -rf attempt*" &&
+
+ git -c transfer.credentialsInUrl=allow \
+ clone $url_userpass attempt1 2>err &&
+ ! grep "$message" err &&
+
+ git -c transfer.credentialsInUrl=warn \
+ clone $url_userpass attempt2 2>err &&
+ grep "warning: $message" err >warnings &&
+ test_line_count -ge 1 warnings &&
+
+ test_must_fail git -c transfer.credentialsInUrl=die \
+ clone $url_userpass attempt3 2>err &&
+ grep "fatal: $message" err >warnings &&
+ test_line_count -ge 1 warnings &&
+
+ test_must_fail git -c transfer.credentialsInUrl=die \
+ clone $url_userblank attempt4 2>err &&
+ grep "fatal: $message" err >warnings &&
+ test_line_count -ge 1 warnings
+'
+
+test_expect_success 'clone does not detect username:password when it is https://username@domain:port/' '
+ test_when_finished "rm -rf attempt1" &&
+
+ # we are relying on lib-httpd for url construction, so document our
+ # assumptions
+ case "$HTTPD_URL_USER" in
+ *:[0-9]*) : ok ;;
+ *) BUG "httpd url does not have port: $HTTPD_URL_USER"
+ esac &&
+
+ git -c transfer.credentialsInUrl=warn clone $url_user attempt1 2>err &&
+ ! grep "uses plaintext credentials" err
+'
+
+test_expect_success 'fetch warns or fails when using username:password' '
+ git -c transfer.credentialsInUrl=allow fetch $url_userpass 2>err &&
+ ! grep "$message" err &&
+
+ git -c transfer.credentialsInUrl=warn fetch $url_userpass 2>err &&
+ grep "warning: $message" err >warnings &&
+ test_line_count -ge 1 warnings &&
+
+ test_must_fail git -c transfer.credentialsInUrl=die \
+ fetch $url_userpass 2>err &&
+ grep "fatal: $message" err >warnings &&
+ test_line_count -ge 1 warnings &&
+
+ test_must_fail git -c transfer.credentialsInUrl=die \
+ fetch $url_userblank 2>err &&
+ grep "fatal: $message" err >warnings &&
+ test_line_count -ge 1 warnings
+'
+
+
+test_expect_success 'push warns or fails when using username:password' '
+ git -c transfer.credentialsInUrl=allow push $url_userpass 2>err &&
+ ! grep "$message" err &&
+
+ git -c transfer.credentialsInUrl=warn push $url_userpass 2>err &&
+ grep "warning: $message" err >warnings &&
+
+ test_must_fail git -c transfer.credentialsInUrl=die \
+ push $url_userpass 2>err &&
+ grep "fatal: $message" err >warnings &&
+ test_line_count -ge 1 warnings
+'
+
+test_expect_success 'no empty path components' '
+ # In the URL, add a trailing slash, and see if git appends yet another
+ # slash.
+ git clone $HTTPD_URL/smart/repo.git/ clone-with-slash &&
+
+ strip_access_log >log &&
+ ! grep "//" log
+'
+
+test_expect_success 'tag following always works over v0 http' '
+ upstream=$HTTPD_DOCUMENT_ROOT_PATH/tags &&
+ git init "$upstream" &&
+ (
+ cd "$upstream" &&
+ git commit --allow-empty -m base &&
+ git tag not-annotated &&
+ git tag -m foo annotated
+ ) &&
+ git init tags &&
+ git -C tags -c protocol.version=0 \
+ fetch --depth 1 $HTTPD_URL/smart/tags \
+ refs/tags/annotated:refs/tags/annotated &&
+ git -C "$upstream" for-each-ref refs/tags >expect &&
+ git -C tags for-each-ref >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t5552-skipping-fetch-negotiator.sh b/t/t5552-skipping-fetch-negotiator.sh
index 7b9fb4f..b55a9f6 100755
--- a/t/t5552-skipping-fetch-negotiator.sh
+++ b/t/t5552-skipping-fetch-negotiator.sh
@@ -3,6 +3,22 @@
test_description='test skipping fetch negotiator'
. ./test-lib.sh
+test_expect_success 'fetch.negotiationalgorithm config' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ cat >repo/.git/config <<-\EOF &&
+ [fetch]
+ negotiationAlgorithm
+ EOF
+ cat >expect <<-\EOF &&
+ error: missing value for '\''fetch.negotiationalgorithm'\''
+ fatal: bad config variable '\''fetch.negotiationalgorithm'\'' in file '\''.git/config'\'' at line 2
+ EOF
+ test_expect_code 128 git -C repo fetch >out 2>actual &&
+ test_must_be_empty out &&
+ test_cmp expect actual
+'
+
have_sent () {
while test "$#" -ne 0
do
@@ -48,7 +64,7 @@ test_expect_success 'commits with no parents are sent regardless of skip distanc
git init client &&
for i in $(test_seq 7)
do
- test_commit -C client c$i
+ test_commit -C client c$i || return 1
done &&
# We send: "c7" (skip 1) "c5" (skip 2) "c2" (skip 4). After that, since
@@ -68,7 +84,7 @@ test_expect_success 'when two skips collide, favor the larger one' '
git init client &&
for i in $(test_seq 11)
do
- test_commit -C client c$i
+ test_commit -C client c$i || return 1
done &&
git -C client checkout c5 &&
test_commit -C client c5side &&
@@ -155,14 +171,14 @@ test_expect_success 'do not send "have" with ancestors of commits that server AC
for i in $(test_seq 8)
do
git -C client checkout --orphan b$i &&
- test_commit -C client b$i.c0
+ test_commit -C client b$i.c0 || return 1
done &&
for j in $(test_seq 19)
do
for i in $(test_seq 8)
do
git -C client checkout b$i &&
- test_commit -C client b$i.c$j
+ test_commit -C client b$i.c$j || return 1
done
done &&
@@ -201,7 +217,7 @@ test_expect_success 'do not send "have" with ancestors of commits that server AC
# should still send the others (in this test, just check b2).
for i in $(test_seq 0 8)
do
- have_not_sent b1.c$i
+ have_not_sent b1.c$i || return 1
done &&
have_sent b2.c1 b2.c0
'
diff --git a/t/t5553-set-upstream.sh b/t/t5553-set-upstream.sh
index 9c12c0f..4805016 100755
--- a/t/t5553-set-upstream.sh
+++ b/t/t5553-set-upstream.sh
@@ -91,6 +91,17 @@ test_expect_success 'fetch --set-upstream with valid URL sets upstream to URL' '
check_config_missing other2
'
+test_expect_success 'fetch --set-upstream with a detached HEAD' '
+ git checkout HEAD^0 &&
+ test_when_finished "git checkout -" &&
+ cat >expect <<-\EOF &&
+ warning: could not set upstream of HEAD to '"'"'main'"'"' from '"'"'upstream'"'"' when it does not point to any branch.
+ EOF
+ git fetch --set-upstream upstream main 2>actual.raw &&
+ grep ^warning: actual.raw >actual &&
+ test_cmp expect actual
+'
+
# tests for pull --set-upstream
test_expect_success 'setup bare parent pull' '
@@ -178,4 +189,15 @@ test_expect_success 'pull --set-upstream with valid URL and branch sets branch'
check_config_missing other2
'
+test_expect_success 'pull --set-upstream with a detached HEAD' '
+ git checkout HEAD^0 &&
+ test_when_finished "git checkout -" &&
+ cat >expect <<-\EOF &&
+ warning: could not set upstream of HEAD to '"'"'main'"'"' from '"'"'upstream'"'"' when it does not point to any branch.
+ EOF
+ git pull --no-rebase --set-upstream upstream main 2>actual.raw &&
+ grep ^warning: actual.raw >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t5554-noop-fetch-negotiator.sh b/t/t5554-noop-fetch-negotiator.sh
index 2ac7b58..06991e8 100755
--- a/t/t5554-noop-fetch-negotiator.sh
+++ b/t/t5554-noop-fetch-negotiator.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test noop fetch negotiator'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'noop negotiator does not emit any "have"' '
diff --git a/t/t5555-http-smart-common.sh b/t/t5555-http-smart-common.sh
new file mode 100755
index 0000000..3dcb334
--- /dev/null
+++ b/t/t5555-http-smart-common.sh
@@ -0,0 +1,161 @@
+#!/bin/sh
+
+test_description='test functionality common to smart fetch & push'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit --no-tag initial
+'
+
+test_expect_success 'git upload-pack --http-backend-info-refs and --advertise-refs are aliased' '
+ git upload-pack --http-backend-info-refs . >expected 2>err.expected &&
+ git upload-pack --advertise-refs . >actual 2>err.actual &&
+ test_cmp err.expected err.actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'git receive-pack --http-backend-info-refs and --advertise-refs are aliased' '
+ git receive-pack --http-backend-info-refs . >expected 2>err.expected &&
+ git receive-pack --advertise-refs . >actual 2>err.actual &&
+ test_cmp err.expected err.actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'git upload-pack --advertise-refs' '
+ cat >expect <<-EOF &&
+ $(git rev-parse HEAD) HEAD
+ $(git rev-parse HEAD) $(git symbolic-ref HEAD)
+ 0000
+ EOF
+
+ # We only care about GIT_PROTOCOL, not GIT_TEST_PROTOCOL_VERSION
+ sane_unset GIT_PROTOCOL &&
+ GIT_TEST_PROTOCOL_VERSION=2 \
+ git upload-pack --advertise-refs . >out 2>err &&
+
+ test-tool pkt-line unpack <out >actual &&
+ test_must_be_empty err &&
+ test_cmp actual expect &&
+
+ # The --advertise-refs alias works
+ git upload-pack --advertise-refs . >out 2>err &&
+
+ test-tool pkt-line unpack <out >actual &&
+ test_must_be_empty err &&
+ test_cmp actual expect
+'
+
+test_expect_success 'git upload-pack --advertise-refs: v0' '
+ # With no specified protocol
+ cat >expect <<-EOF &&
+ $(git rev-parse HEAD) HEAD
+ $(git rev-parse HEAD) $(git symbolic-ref HEAD)
+ 0000
+ EOF
+
+ git upload-pack --advertise-refs . >out 2>err &&
+ test-tool pkt-line unpack <out >actual &&
+ test_must_be_empty err &&
+ test_cmp actual expect &&
+
+ # With explicit v0
+ GIT_PROTOCOL=version=0 \
+ git upload-pack --advertise-refs . >out 2>err &&
+ test-tool pkt-line unpack <out >actual 2>err &&
+ test_must_be_empty err &&
+ test_cmp actual expect
+
+'
+
+test_expect_success 'git receive-pack --advertise-refs: v0' '
+ # With no specified protocol
+ cat >expect <<-EOF &&
+ $(git rev-parse HEAD) $(git symbolic-ref HEAD)
+ 0000
+ EOF
+
+ git receive-pack --advertise-refs . >out 2>err &&
+ test-tool pkt-line unpack <out >actual &&
+ test_must_be_empty err &&
+ test_cmp actual expect &&
+
+ # With explicit v0
+ GIT_PROTOCOL=version=0 \
+ git receive-pack --advertise-refs . >out 2>err &&
+ test-tool pkt-line unpack <out >actual 2>err &&
+ test_must_be_empty err &&
+ test_cmp actual expect
+
+'
+
+test_expect_success 'git upload-pack --advertise-refs: v1' '
+ # With no specified protocol
+ cat >expect <<-EOF &&
+ version 1
+ $(git rev-parse HEAD) HEAD
+ $(git rev-parse HEAD) $(git symbolic-ref HEAD)
+ 0000
+ EOF
+
+ GIT_PROTOCOL=version=1 \
+ git upload-pack --advertise-refs . >out &&
+
+ test-tool pkt-line unpack <out >actual 2>err &&
+ test_must_be_empty err &&
+ test_cmp actual expect
+'
+
+test_expect_success 'git receive-pack --advertise-refs: v1' '
+ # With no specified protocol
+ cat >expect <<-EOF &&
+ version 1
+ $(git rev-parse HEAD) $(git symbolic-ref HEAD)
+ 0000
+ EOF
+
+ GIT_PROTOCOL=version=1 \
+ git receive-pack --advertise-refs . >out &&
+
+ test-tool pkt-line unpack <out >actual 2>err &&
+ test_must_be_empty err &&
+ test_cmp actual expect
+'
+
+test_expect_success 'git upload-pack --advertise-refs: v2' '
+ cat >expect <<-EOF &&
+ version 2
+ agent=FAKE
+ ls-refs=unborn
+ fetch=shallow wait-for-done
+ server-option
+ object-format=$(test_oid algo)
+ 0000
+ EOF
+
+ GIT_PROTOCOL=version=2 \
+ GIT_USER_AGENT=FAKE \
+ git upload-pack --advertise-refs . >out 2>err &&
+
+ test-tool pkt-line unpack <out >actual &&
+ test_must_be_empty err &&
+ test_cmp actual expect
+'
+
+test_expect_success 'git receive-pack --advertise-refs: v2' '
+ # There is no v2 yet for receive-pack, implicit v0
+ cat >expect <<-EOF &&
+ $(git rev-parse HEAD) $(git symbolic-ref HEAD)
+ 0000
+ EOF
+
+ GIT_PROTOCOL=version=2 \
+ git receive-pack --advertise-refs . >out 2>err &&
+
+ test-tool pkt-line unpack <out >actual &&
+ test_must_be_empty err &&
+ test_cmp actual expect
+'
+
+test_done
diff --git a/t/t5557-http-get.sh b/t/t5557-http-get.sh
new file mode 100755
index 0000000..76a4bbd
--- /dev/null
+++ b/t/t5557-http-get.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+test_description='test downloading a file by URL'
+
+TEST_PASSES_SANITIZE_LEAK=true
+
+. ./test-lib.sh
+
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success 'get by URL: 404' '
+ test_when_finished "rm -f file.temp" &&
+ url="$HTTPD_URL/none.txt" &&
+ cat >input <<-EOF &&
+ capabilities
+ get $url file1
+ EOF
+
+ test_must_fail git remote-http $url <input 2>err &&
+ test_path_is_missing file1 &&
+ grep "failed to download file at URL" err
+'
+
+test_expect_success 'get by URL: 200' '
+ echo data >"$HTTPD_DOCUMENT_ROOT_PATH/exists.txt" &&
+
+ url="$HTTPD_URL/exists.txt" &&
+ cat >input <<-EOF &&
+ capabilities
+ get $url file2
+
+ EOF
+
+ git remote-http $url <input &&
+ test_cmp "$HTTPD_DOCUMENT_ROOT_PATH/exists.txt" file2
+'
+
+test_done
diff --git a/t/t5558-clone-bundle-uri.sh b/t/t5558-clone-bundle-uri.sh
new file mode 100755
index 0000000..1ca5f74
--- /dev/null
+++ b/t/t5558-clone-bundle-uri.sh
@@ -0,0 +1,1076 @@
+#!/bin/sh
+
+test_description='test fetching bundles with --bundle-uri'
+
+. ./test-lib.sh
+
+test_expect_success 'fail to clone from non-existent file' '
+ test_when_finished rm -rf test &&
+ git clone --bundle-uri="$(pwd)/does-not-exist" . test 2>err &&
+ grep "failed to download bundle from URI" err
+'
+
+test_expect_success 'fail to clone from non-bundle file' '
+ test_when_finished rm -rf test &&
+ echo bogus >bogus &&
+ git clone --bundle-uri="$(pwd)/bogus" . test 2>err &&
+ grep "is not a bundle" err
+'
+
+test_expect_success 'create bundle' '
+ git init clone-from &&
+ git -C clone-from checkout -b topic &&
+ test_commit -C clone-from A &&
+ test_commit -C clone-from B &&
+ git -C clone-from bundle create B.bundle topic
+'
+
+test_expect_success 'clone with path bundle' '
+ git clone --bundle-uri="clone-from/B.bundle" \
+ clone-from clone-path &&
+ git -C clone-path rev-parse refs/bundles/topic >actual &&
+ git -C clone-from rev-parse topic >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'clone with path bundle and non-default hash' '
+ test_when_finished "rm -rf clone-path-non-default-hash" &&
+ GIT_DEFAULT_HASH=sha256 git clone --bundle-uri="clone-from/B.bundle" \
+ clone-from clone-path-non-default-hash &&
+ git -C clone-path-non-default-hash rev-parse refs/bundles/topic >actual &&
+ git -C clone-from rev-parse topic >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'clone with file:// bundle' '
+ git clone --bundle-uri="file://$(pwd)/clone-from/B.bundle" \
+ clone-from clone-file &&
+ git -C clone-file rev-parse refs/bundles/topic >actual &&
+ git -C clone-from rev-parse topic >expect &&
+ test_cmp expect actual
+'
+
+# To get interesting tests for bundle lists, we need to construct a
+# somewhat-interesting commit history.
+#
+# ---------------- bundle-4
+#
+# 4
+# / \
+# ----|---|------- bundle-3
+# | |
+# | 3
+# | |
+# ----|---|------- bundle-2
+# | |
+# 2 |
+# | |
+# ----|---|------- bundle-1
+# \ /
+# 1
+# |
+# (previous commits)
+test_expect_success 'construct incremental bundle list' '
+ (
+ cd clone-from &&
+ git checkout -b base &&
+ test_commit 1 &&
+ git checkout -b left &&
+ test_commit 2 &&
+ git checkout -b right base &&
+ test_commit 3 &&
+ git checkout -b merge left &&
+ git merge right -m "4" &&
+
+ git bundle create bundle-1.bundle base &&
+ git bundle create bundle-2.bundle base..left &&
+ git bundle create bundle-3.bundle base..right &&
+ git bundle create bundle-4.bundle merge --not left right
+ )
+'
+
+test_expect_success 'clone bundle list (file, no heuristic)' '
+ cat >bundle-list <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+
+ [bundle "bundle-1"]
+ uri = file://$(pwd)/clone-from/bundle-1.bundle
+
+ [bundle "bundle-2"]
+ uri = file://$(pwd)/clone-from/bundle-2.bundle
+
+ [bundle "bundle-3"]
+ uri = file://$(pwd)/clone-from/bundle-3.bundle
+
+ [bundle "bundle-4"]
+ uri = file://$(pwd)/clone-from/bundle-4.bundle
+ EOF
+
+ git clone --bundle-uri="file://$(pwd)/bundle-list" \
+ clone-from clone-list-file 2>err &&
+ ! grep "Repository lacks these prerequisite commits" err &&
+
+ git -C clone-from for-each-ref --format="%(objectname)" >oids &&
+ git -C clone-list-file cat-file --batch-check <oids &&
+
+ git -C clone-list-file for-each-ref --format="%(refname)" >refs &&
+ grep "refs/bundles/" refs >actual &&
+ cat >expect <<-\EOF &&
+ refs/bundles/base
+ refs/bundles/left
+ refs/bundles/merge
+ refs/bundles/right
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'clone bundle list (file, all mode, some failures)' '
+ cat >bundle-list <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+
+ # Does not exist. Should be skipped.
+ [bundle "bundle-0"]
+ uri = file://$(pwd)/clone-from/bundle-0.bundle
+
+ [bundle "bundle-1"]
+ uri = file://$(pwd)/clone-from/bundle-1.bundle
+
+ [bundle "bundle-2"]
+ uri = file://$(pwd)/clone-from/bundle-2.bundle
+
+ # No bundle-3 means bundle-4 will not apply.
+
+ [bundle "bundle-4"]
+ uri = file://$(pwd)/clone-from/bundle-4.bundle
+
+ # Does not exist. Should be skipped.
+ [bundle "bundle-5"]
+ uri = file://$(pwd)/clone-from/bundle-5.bundle
+ EOF
+
+ GIT_TRACE2_PERF=1 \
+ git clone --bundle-uri="file://$(pwd)/bundle-list" \
+ clone-from clone-all-some 2>err &&
+ ! grep "Repository lacks these prerequisite commits" err &&
+ ! grep "fatal" err &&
+ grep "warning: failed to download bundle from URI" err &&
+
+ git -C clone-from for-each-ref --format="%(objectname)" >oids &&
+ git -C clone-all-some cat-file --batch-check <oids &&
+
+ git -C clone-all-some for-each-ref --format="%(refname)" >refs &&
+ grep "refs/bundles/" refs >actual &&
+ cat >expect <<-\EOF &&
+ refs/bundles/base
+ refs/bundles/left
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'clone bundle list (file, all mode, all failures)' '
+ cat >bundle-list <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+
+ # Does not exist. Should be skipped.
+ [bundle "bundle-0"]
+ uri = file://$(pwd)/clone-from/bundle-0.bundle
+
+ # Does not exist. Should be skipped.
+ [bundle "bundle-5"]
+ uri = file://$(pwd)/clone-from/bundle-5.bundle
+ EOF
+
+ git clone --bundle-uri="file://$(pwd)/bundle-list" \
+ clone-from clone-all-fail 2>err &&
+ ! grep "Repository lacks these prerequisite commits" err &&
+ ! grep "fatal" err &&
+ grep "warning: failed to download bundle from URI" err &&
+
+ git -C clone-from for-each-ref --format="%(objectname)" >oids &&
+ git -C clone-all-fail cat-file --batch-check <oids &&
+
+ git -C clone-all-fail for-each-ref --format="%(refname)" >refs &&
+ ! grep "refs/bundles/" refs
+'
+
+test_expect_success 'clone bundle list (file, any mode)' '
+ cat >bundle-list <<-EOF &&
+ [bundle]
+ version = 1
+ mode = any
+
+ # Does not exist. Should be skipped.
+ [bundle "bundle-0"]
+ uri = file://$(pwd)/clone-from/bundle-0.bundle
+
+ [bundle "bundle-1"]
+ uri = file://$(pwd)/clone-from/bundle-1.bundle
+
+ # Does not exist. Should be skipped.
+ [bundle "bundle-5"]
+ uri = file://$(pwd)/clone-from/bundle-5.bundle
+ EOF
+
+ git clone --bundle-uri="file://$(pwd)/bundle-list" \
+ clone-from clone-any-file 2>err &&
+ ! grep "Repository lacks these prerequisite commits" err &&
+
+ git -C clone-from for-each-ref --format="%(objectname)" >oids &&
+ git -C clone-any-file cat-file --batch-check <oids &&
+
+ git -C clone-any-file for-each-ref --format="%(refname)" >refs &&
+ grep "refs/bundles/" refs >actual &&
+ cat >expect <<-\EOF &&
+ refs/bundles/base
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'clone bundle list (file, any mode, all failures)' '
+ cat >bundle-list <<-EOF &&
+ [bundle]
+ version = 1
+ mode = any
+
+ # Does not exist. Should be skipped.
+ [bundle "bundle-0"]
+ uri = $HTTPD_URL/bundle-0.bundle
+
+ # Does not exist. Should be skipped.
+ [bundle "bundle-5"]
+ uri = $HTTPD_URL/bundle-5.bundle
+ EOF
+
+ git clone --bundle-uri="file://$(pwd)/bundle-list" \
+ clone-from clone-any-fail 2>err &&
+ ! grep "fatal" err &&
+ grep "warning: failed to download bundle from URI" err &&
+
+ git -C clone-from for-each-ref --format="%(objectname)" >oids &&
+ git -C clone-any-fail cat-file --batch-check <oids &&
+
+ git -C clone-any-fail for-each-ref --format="%(refname)" >refs &&
+ ! grep "refs/bundles/" refs
+'
+
+#########################################################################
+# HTTP tests begin here
+
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success 'fail to fetch from non-existent HTTP URL' '
+ test_when_finished rm -rf test &&
+ git clone --bundle-uri="$HTTPD_URL/does-not-exist" . test 2>err &&
+ grep "failed to download bundle from URI" err
+'
+
+test_expect_success 'fail to fetch from non-bundle HTTP URL' '
+ test_when_finished rm -rf test &&
+ echo bogus >"$HTTPD_DOCUMENT_ROOT_PATH/bogus" &&
+ git clone --bundle-uri="$HTTPD_URL/bogus" . test 2>err &&
+ grep "is not a bundle" err
+'
+
+test_expect_success 'clone HTTP bundle' '
+ cp clone-from/B.bundle "$HTTPD_DOCUMENT_ROOT_PATH/B.bundle" &&
+
+ git clone --no-local --mirror clone-from \
+ "$HTTPD_DOCUMENT_ROOT_PATH/fetch.git" &&
+
+ git clone --bundle-uri="$HTTPD_URL/B.bundle" \
+ "$HTTPD_URL/smart/fetch.git" clone-http &&
+ git -C clone-http rev-parse refs/bundles/topic >actual &&
+ git -C clone-from rev-parse topic >expect &&
+ test_cmp expect actual &&
+
+ test_config -C clone-http log.excludedecoration refs/bundle/
+'
+
+test_expect_success 'clone HTTP bundle with non-default hash' '
+ test_when_finished "rm -rf clone-http-non-default-hash" &&
+ GIT_DEFAULT_HASH=sha256 git clone --bundle-uri="$HTTPD_URL/B.bundle" \
+ "$HTTPD_URL/smart/fetch.git" clone-http-non-default-hash &&
+ git -C clone-http-non-default-hash rev-parse refs/bundles/topic >actual &&
+ git -C clone-from rev-parse topic >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'clone bundle list (HTTP, no heuristic)' '
+ test_when_finished rm -f trace*.txt &&
+
+ cp clone-from/bundle-*.bundle "$HTTPD_DOCUMENT_ROOT_PATH/" &&
+ cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+
+ [bundle "bundle-1"]
+ uri = $HTTPD_URL/bundle-1.bundle
+
+ [bundle "bundle-2"]
+ uri = $HTTPD_URL/bundle-2.bundle
+
+ [bundle "bundle-3"]
+ uri = $HTTPD_URL/bundle-3.bundle
+
+ [bundle "bundle-4"]
+ uri = $HTTPD_URL/bundle-4.bundle
+ EOF
+
+ GIT_TRACE2_EVENT="$(pwd)/trace-clone.txt" \
+ git clone --bundle-uri="$HTTPD_URL/bundle-list" \
+ clone-from clone-list-http 2>err &&
+ ! grep "Repository lacks these prerequisite commits" err &&
+
+ git -C clone-from for-each-ref --format="%(objectname)" >oids &&
+ git -C clone-list-http cat-file --batch-check <oids &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-1.bundle
+ $HTTPD_URL/bundle-2.bundle
+ $HTTPD_URL/bundle-3.bundle
+ $HTTPD_URL/bundle-4.bundle
+ $HTTPD_URL/bundle-list
+ EOF
+
+ # Sort the list, since the order is not well-defined
+ # without a heuristic.
+ test_remote_https_urls <trace-clone.txt | sort >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'clone bundle list (HTTP, any mode)' '
+ cp clone-from/bundle-*.bundle "$HTTPD_DOCUMENT_ROOT_PATH/" &&
+ cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle]
+ version = 1
+ mode = any
+
+ # Does not exist. Should be skipped.
+ [bundle "bundle-0"]
+ uri = $HTTPD_URL/bundle-0.bundle
+
+ [bundle "bundle-1"]
+ uri = $HTTPD_URL/bundle-1.bundle
+
+ # Does not exist. Should be skipped.
+ [bundle "bundle-5"]
+ uri = $HTTPD_URL/bundle-5.bundle
+ EOF
+
+ git clone --bundle-uri="$HTTPD_URL/bundle-list" \
+ clone-from clone-any-http 2>err &&
+ ! grep "fatal" err &&
+ grep "warning: failed to download bundle from URI" err &&
+
+ git -C clone-from for-each-ref --format="%(objectname)" >oids &&
+ git -C clone-any-http cat-file --batch-check <oids &&
+
+ git -C clone-list-file for-each-ref --format="%(refname)" >refs &&
+ grep "refs/bundles/" refs >actual &&
+ cat >expect <<-\EOF &&
+ refs/bundles/base
+ refs/bundles/left
+ refs/bundles/merge
+ refs/bundles/right
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'clone bundle list (http, creationToken)' '
+ test_when_finished rm -f trace*.txt &&
+
+ cp clone-from/bundle-*.bundle "$HTTPD_DOCUMENT_ROOT_PATH/" &&
+ cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+
+ [bundle "bundle-1"]
+ uri = bundle-1.bundle
+ creationToken = 1
+
+ [bundle "bundle-2"]
+ uri = bundle-2.bundle
+ creationToken = 2
+
+ [bundle "bundle-3"]
+ uri = bundle-3.bundle
+ creationToken = 3
+
+ [bundle "bundle-4"]
+ uri = bundle-4.bundle
+ creationToken = 4
+ EOF
+
+ GIT_TRACE2_EVENT="$(pwd)/trace-clone.txt" git \
+ clone --bundle-uri="$HTTPD_URL/bundle-list" \
+ "$HTTPD_URL/smart/fetch.git" clone-list-http-2 &&
+
+ git -C clone-from for-each-ref --format="%(objectname)" >oids &&
+ git -C clone-list-http-2 cat-file --batch-check <oids &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-list
+ $HTTPD_URL/bundle-4.bundle
+ $HTTPD_URL/bundle-3.bundle
+ $HTTPD_URL/bundle-2.bundle
+ $HTTPD_URL/bundle-1.bundle
+ EOF
+
+ test_remote_https_urls <trace-clone.txt >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'clone incomplete bundle list (http, creationToken)' '
+ test_when_finished rm -f trace*.txt &&
+
+ cp clone-from/bundle-*.bundle "$HTTPD_DOCUMENT_ROOT_PATH/" &&
+ cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+
+ [bundle "bundle-1"]
+ uri = bundle-1.bundle
+ creationToken = 1
+ EOF
+
+ GIT_TRACE2_EVENT=$(pwd)/trace-clone.txt \
+ git clone --bundle-uri="$HTTPD_URL/bundle-list" \
+ --single-branch --branch=base --no-tags \
+ "$HTTPD_URL/smart/fetch.git" clone-token-http &&
+
+ test_cmp_config -C clone-token-http "$HTTPD_URL/bundle-list" fetch.bundleuri &&
+ test_cmp_config -C clone-token-http 1 fetch.bundlecreationtoken &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-list
+ $HTTPD_URL/bundle-1.bundle
+ EOF
+
+ test_remote_https_urls <trace-clone.txt >actual &&
+ test_cmp expect actual &&
+
+ # We now have only one bundle ref.
+ git -C clone-token-http for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+ cat >expect <<-\EOF &&
+ refs/bundles/base
+ EOF
+ test_cmp expect refs &&
+
+ # Add remaining bundles, exercising the "deepening" strategy
+ # for downloading via the creationToken heurisitc.
+ cat >>"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle "bundle-2"]
+ uri = bundle-2.bundle
+ creationToken = 2
+
+ [bundle "bundle-3"]
+ uri = bundle-3.bundle
+ creationToken = 3
+
+ [bundle "bundle-4"]
+ uri = bundle-4.bundle
+ creationToken = 4
+ EOF
+
+ GIT_TRACE2_EVENT="$(pwd)/trace1.txt" \
+ git -C clone-token-http fetch origin --no-tags \
+ refs/heads/merge:refs/heads/merge &&
+ test_cmp_config -C clone-token-http 4 fetch.bundlecreationtoken &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-list
+ $HTTPD_URL/bundle-4.bundle
+ $HTTPD_URL/bundle-3.bundle
+ $HTTPD_URL/bundle-2.bundle
+ EOF
+
+ test_remote_https_urls <trace1.txt >actual &&
+ test_cmp expect actual &&
+
+ # We now have all bundle refs.
+ git -C clone-token-http for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+
+ cat >expect <<-\EOF &&
+ refs/bundles/base
+ refs/bundles/left
+ refs/bundles/merge
+ refs/bundles/right
+ EOF
+ test_cmp expect refs
+'
+
+test_expect_success 'http clone with bundle.heuristic creates fetch.bundleURI' '
+ test_when_finished rm -rf fetch-http-4 trace*.txt &&
+
+ cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+
+ [bundle "bundle-1"]
+ uri = bundle-1.bundle
+ creationToken = 1
+ EOF
+
+ GIT_TRACE2_EVENT="$(pwd)/trace-clone.txt" \
+ git clone --single-branch --branch=base \
+ --bundle-uri="$HTTPD_URL/bundle-list" \
+ "$HTTPD_URL/smart/fetch.git" fetch-http-4 &&
+
+ test_cmp_config -C fetch-http-4 "$HTTPD_URL/bundle-list" fetch.bundleuri &&
+ test_cmp_config -C fetch-http-4 1 fetch.bundlecreationtoken &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-list
+ $HTTPD_URL/bundle-1.bundle
+ EOF
+
+ test_remote_https_urls <trace-clone.txt >actual &&
+ test_cmp expect actual &&
+
+ # only received base ref from bundle-1
+ git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+ cat >expect <<-\EOF &&
+ refs/bundles/base
+ EOF
+ test_cmp expect refs &&
+
+ cat >>"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle "bundle-2"]
+ uri = bundle-2.bundle
+ creationToken = 2
+ EOF
+
+ # Fetch the objects for bundle-2 _and_ bundle-3.
+ GIT_TRACE2_EVENT="$(pwd)/trace1.txt" \
+ git -C fetch-http-4 fetch origin --no-tags \
+ refs/heads/left:refs/heads/left \
+ refs/heads/right:refs/heads/right &&
+ test_cmp_config -C fetch-http-4 2 fetch.bundlecreationtoken &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-list
+ $HTTPD_URL/bundle-2.bundle
+ EOF
+
+ test_remote_https_urls <trace1.txt >actual &&
+ test_cmp expect actual &&
+
+ # received left from bundle-2
+ git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+ cat >expect <<-\EOF &&
+ refs/bundles/base
+ refs/bundles/left
+ EOF
+ test_cmp expect refs &&
+
+ # No-op fetch
+ GIT_TRACE2_EVENT="$(pwd)/trace1b.txt" \
+ git -C fetch-http-4 fetch origin --no-tags \
+ refs/heads/left:refs/heads/left \
+ refs/heads/right:refs/heads/right &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-list
+ EOF
+ test_remote_https_urls <trace1b.txt >actual &&
+ test_cmp expect actual &&
+
+ cat >>"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle "bundle-3"]
+ uri = bundle-3.bundle
+ creationToken = 3
+
+ [bundle "bundle-4"]
+ uri = bundle-4.bundle
+ creationToken = 4
+ EOF
+
+ # This fetch should skip bundle-3.bundle, since its objects are
+ # already local (we have the requisite commits for bundle-4.bundle).
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+ git -C fetch-http-4 fetch origin --no-tags \
+ refs/heads/merge:refs/heads/merge &&
+ test_cmp_config -C fetch-http-4 4 fetch.bundlecreationtoken &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-list
+ $HTTPD_URL/bundle-4.bundle
+ EOF
+
+ test_remote_https_urls <trace2.txt >actual &&
+ test_cmp expect actual &&
+
+ # received merge ref from bundle-4, but right is missing
+ # because we did not download bundle-3.
+ git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+
+ cat >expect <<-\EOF &&
+ refs/bundles/base
+ refs/bundles/left
+ refs/bundles/merge
+ EOF
+ test_cmp expect refs &&
+
+ # No-op fetch
+ GIT_TRACE2_EVENT="$(pwd)/trace2b.txt" \
+ git -C fetch-http-4 fetch origin &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-list
+ EOF
+ test_remote_https_urls <trace2b.txt >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'creationToken heuristic with failed downloads (clone)' '
+ test_when_finished rm -rf download-* trace*.txt &&
+
+ # Case 1: base bundle does not exist, nothing can unbundle
+ cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+
+ [bundle "bundle-1"]
+ uri = fake.bundle
+ creationToken = 1
+
+ [bundle "bundle-2"]
+ uri = bundle-2.bundle
+ creationToken = 2
+
+ [bundle "bundle-3"]
+ uri = bundle-3.bundle
+ creationToken = 3
+
+ [bundle "bundle-4"]
+ uri = bundle-4.bundle
+ creationToken = 4
+ EOF
+
+ GIT_TRACE2_EVENT="$(pwd)/trace-clone-1.txt" \
+ git clone --single-branch --branch=base \
+ --bundle-uri="$HTTPD_URL/bundle-list" \
+ "$HTTPD_URL/smart/fetch.git" download-1 &&
+
+ # Bundle failure does not set these configs.
+ test_must_fail git -C download-1 config fetch.bundleuri &&
+ test_must_fail git -C download-1 config fetch.bundlecreationtoken &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-list
+ $HTTPD_URL/bundle-4.bundle
+ $HTTPD_URL/bundle-3.bundle
+ $HTTPD_URL/bundle-2.bundle
+ $HTTPD_URL/fake.bundle
+ EOF
+ test_remote_https_urls <trace-clone-1.txt >actual &&
+ test_cmp expect actual &&
+
+ # All bundles failed to unbundle
+ git -C download-1 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+ test_must_be_empty refs &&
+
+ # Case 2: middle bundle does not exist, only two bundles can unbundle
+ cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+
+ [bundle "bundle-1"]
+ uri = bundle-1.bundle
+ creationToken = 1
+
+ [bundle "bundle-2"]
+ uri = fake.bundle
+ creationToken = 2
+
+ [bundle "bundle-3"]
+ uri = bundle-3.bundle
+ creationToken = 3
+
+ [bundle "bundle-4"]
+ uri = bundle-4.bundle
+ creationToken = 4
+ EOF
+
+ GIT_TRACE2_EVENT="$(pwd)/trace-clone-2.txt" \
+ git clone --single-branch --branch=base \
+ --bundle-uri="$HTTPD_URL/bundle-list" \
+ "$HTTPD_URL/smart/fetch.git" download-2 &&
+
+ # Bundle failure does not set these configs.
+ test_must_fail git -C download-2 config fetch.bundleuri &&
+ test_must_fail git -C download-2 config fetch.bundlecreationtoken &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-list
+ $HTTPD_URL/bundle-4.bundle
+ $HTTPD_URL/bundle-3.bundle
+ $HTTPD_URL/fake.bundle
+ $HTTPD_URL/bundle-1.bundle
+ EOF
+ test_remote_https_urls <trace-clone-2.txt >actual &&
+ test_cmp expect actual &&
+
+ # bundle-1 and bundle-3 could unbundle, but bundle-4 could not
+ git -C download-2 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+ cat >expect <<-EOF &&
+ refs/bundles/base
+ refs/bundles/right
+ EOF
+ test_cmp expect refs &&
+
+ # Case 3: top bundle does not exist, rest unbundle fine.
+ cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+
+ [bundle "bundle-1"]
+ uri = bundle-1.bundle
+ creationToken = 1
+
+ [bundle "bundle-2"]
+ uri = bundle-2.bundle
+ creationToken = 2
+
+ [bundle "bundle-3"]
+ uri = bundle-3.bundle
+ creationToken = 3
+
+ [bundle "bundle-4"]
+ uri = fake.bundle
+ creationToken = 4
+ EOF
+
+ GIT_TRACE2_EVENT="$(pwd)/trace-clone-3.txt" \
+ git clone --single-branch --branch=base \
+ --bundle-uri="$HTTPD_URL/bundle-list" \
+ "$HTTPD_URL/smart/fetch.git" download-3 &&
+
+ # As long as we have continguous successful downloads,
+ # we _do_ set these configs.
+ test_cmp_config -C download-3 "$HTTPD_URL/bundle-list" fetch.bundleuri &&
+ test_cmp_config -C download-3 3 fetch.bundlecreationtoken &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-list
+ $HTTPD_URL/fake.bundle
+ $HTTPD_URL/bundle-3.bundle
+ $HTTPD_URL/bundle-2.bundle
+ $HTTPD_URL/bundle-1.bundle
+ EOF
+ test_remote_https_urls <trace-clone-3.txt >actual &&
+ test_cmp expect actual &&
+
+ # fake.bundle did not unbundle, but the others did.
+ git -C download-3 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+ cat >expect <<-EOF &&
+ refs/bundles/base
+ refs/bundles/left
+ refs/bundles/right
+ EOF
+ test_cmp expect refs
+'
+
+# Expand the bundle list to include other interesting shapes, specifically
+# interesting for use when fetching from a previous state.
+#
+# ---------------- bundle-7
+# 7
+# _/|\_
+# ---/--|--\------ bundle-6
+# 5 | 6
+# --|---|---|----- bundle-4
+# | 4 |
+# | / \ /
+# --|-|---|/------ bundle-3 (the client will be caught up to this point.)
+# \ | 3
+# ---\|---|------- bundle-2
+# 2 |
+# ----|---|------- bundle-1
+# \ /
+# 1
+# |
+# (previous commits)
+test_expect_success 'expand incremental bundle list' '
+ (
+ cd clone-from &&
+ git checkout -b lefter left &&
+ test_commit 5 &&
+ git checkout -b righter right &&
+ test_commit 6 &&
+ git checkout -b top lefter &&
+ git merge -m "7" merge righter &&
+
+ git bundle create bundle-6.bundle lefter righter --not left right &&
+ git bundle create bundle-7.bundle top --not lefter merge righter &&
+
+ cp bundle-*.bundle "$HTTPD_DOCUMENT_ROOT_PATH/"
+ ) &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/fetch.git" fetch origin +refs/heads/*:refs/heads/*
+'
+
+test_expect_success 'creationToken heuristic with failed downloads (fetch)' '
+ test_when_finished rm -rf download-* trace*.txt &&
+
+ cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+
+ [bundle "bundle-1"]
+ uri = bundle-1.bundle
+ creationToken = 1
+
+ [bundle "bundle-2"]
+ uri = bundle-2.bundle
+ creationToken = 2
+
+ [bundle "bundle-3"]
+ uri = bundle-3.bundle
+ creationToken = 3
+ EOF
+
+ git clone --single-branch --branch=left \
+ --bundle-uri="$HTTPD_URL/bundle-list" \
+ "$HTTPD_URL/smart/fetch.git" fetch-base &&
+ test_cmp_config -C fetch-base "$HTTPD_URL/bundle-list" fetch.bundleURI &&
+ test_cmp_config -C fetch-base 3 fetch.bundleCreationToken &&
+
+ # Case 1: all bundles exist: successful unbundling of all bundles
+ cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+
+ [bundle "bundle-1"]
+ uri = bundle-1.bundle
+ creationToken = 1
+
+ [bundle "bundle-2"]
+ uri = bundle-2.bundle
+ creationToken = 2
+
+ [bundle "bundle-3"]
+ uri = bundle-3.bundle
+ creationToken = 3
+
+ [bundle "bundle-4"]
+ uri = bundle-4.bundle
+ creationToken = 4
+
+ [bundle "bundle-6"]
+ uri = bundle-6.bundle
+ creationToken = 6
+
+ [bundle "bundle-7"]
+ uri = bundle-7.bundle
+ creationToken = 7
+ EOF
+
+ cp -r fetch-base fetch-1 &&
+ GIT_TRACE2_EVENT="$(pwd)/trace-fetch-1.txt" \
+ git -C fetch-1 fetch origin &&
+ test_cmp_config -C fetch-1 7 fetch.bundlecreationtoken &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-list
+ $HTTPD_URL/bundle-7.bundle
+ $HTTPD_URL/bundle-6.bundle
+ $HTTPD_URL/bundle-4.bundle
+ EOF
+ test_remote_https_urls <trace-fetch-1.txt >actual &&
+ test_cmp expect actual &&
+
+ # Check which bundles have unbundled by refs
+ git -C fetch-1 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+ cat >expect <<-EOF &&
+ refs/bundles/base
+ refs/bundles/left
+ refs/bundles/lefter
+ refs/bundles/merge
+ refs/bundles/right
+ refs/bundles/righter
+ refs/bundles/top
+ EOF
+ test_cmp expect refs &&
+
+ # Case 2: middle bundle does not exist, only bundle-4 can unbundle
+ cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+
+ [bundle "bundle-1"]
+ uri = bundle-1.bundle
+ creationToken = 1
+
+ [bundle "bundle-2"]
+ uri = bundle-2.bundle
+ creationToken = 2
+
+ [bundle "bundle-3"]
+ uri = bundle-3.bundle
+ creationToken = 3
+
+ [bundle "bundle-4"]
+ uri = bundle-4.bundle
+ creationToken = 4
+
+ [bundle "bundle-6"]
+ uri = fake.bundle
+ creationToken = 6
+
+ [bundle "bundle-7"]
+ uri = bundle-7.bundle
+ creationToken = 7
+ EOF
+
+ cp -r fetch-base fetch-2 &&
+ GIT_TRACE2_EVENT="$(pwd)/trace-fetch-2.txt" \
+ git -C fetch-2 fetch origin &&
+
+ # Since bundle-7 fails to unbundle, do not update creation token.
+ test_cmp_config -C fetch-2 3 fetch.bundlecreationtoken &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-list
+ $HTTPD_URL/bundle-7.bundle
+ $HTTPD_URL/fake.bundle
+ $HTTPD_URL/bundle-4.bundle
+ EOF
+ test_remote_https_urls <trace-fetch-2.txt >actual &&
+ test_cmp expect actual &&
+
+ # Check which bundles have unbundled by refs
+ git -C fetch-2 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+ cat >expect <<-EOF &&
+ refs/bundles/base
+ refs/bundles/left
+ refs/bundles/merge
+ refs/bundles/right
+ EOF
+ test_cmp expect refs &&
+
+ # Case 3: top bundle does not exist, rest unbundle fine.
+ cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+
+ [bundle "bundle-1"]
+ uri = bundle-1.bundle
+ creationToken = 1
+
+ [bundle "bundle-2"]
+ uri = bundle-2.bundle
+ creationToken = 2
+
+ [bundle "bundle-3"]
+ uri = bundle-3.bundle
+ creationToken = 3
+
+ [bundle "bundle-4"]
+ uri = bundle-4.bundle
+ creationToken = 4
+
+ [bundle "bundle-6"]
+ uri = bundle-6.bundle
+ creationToken = 6
+
+ [bundle "bundle-7"]
+ uri = fake.bundle
+ creationToken = 7
+ EOF
+
+ cp -r fetch-base fetch-3 &&
+ GIT_TRACE2_EVENT="$(pwd)/trace-fetch-3.txt" \
+ git -C fetch-3 fetch origin &&
+
+ # As long as we have continguous successful downloads,
+ # we _do_ set the maximum creation token.
+ test_cmp_config -C fetch-3 6 fetch.bundlecreationtoken &&
+
+ # NOTE: the fetch skips bundle-4 since bundle-6 successfully
+ # unbundles itself and bundle-7 failed to download.
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-list
+ $HTTPD_URL/fake.bundle
+ $HTTPD_URL/bundle-6.bundle
+ EOF
+ test_remote_https_urls <trace-fetch-3.txt >actual &&
+ test_cmp expect actual &&
+
+ # Check which bundles have unbundled by refs
+ git -C fetch-3 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+ cat >expect <<-EOF &&
+ refs/bundles/base
+ refs/bundles/left
+ refs/bundles/lefter
+ refs/bundles/right
+ refs/bundles/righter
+ EOF
+ test_cmp expect refs
+'
+
+test_expect_success 'bundles are downloaded once during fetch --all' '
+ test_when_finished rm -rf download-* trace*.txt fetch-mult &&
+
+ cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+
+ [bundle "bundle-1"]
+ uri = bundle-1.bundle
+ creationToken = 1
+
+ [bundle "bundle-2"]
+ uri = bundle-2.bundle
+ creationToken = 2
+
+ [bundle "bundle-3"]
+ uri = bundle-3.bundle
+ creationToken = 3
+ EOF
+
+ git clone --single-branch --branch=left \
+ --bundle-uri="$HTTPD_URL/bundle-list" \
+ "$HTTPD_URL/smart/fetch.git" fetch-mult &&
+ git -C fetch-mult remote add dup1 "$HTTPD_URL/smart/fetch.git" &&
+ git -C fetch-mult remote add dup2 "$HTTPD_URL/smart/fetch.git" &&
+
+ GIT_TRACE2_EVENT="$(pwd)/trace-mult.txt" \
+ git -C fetch-mult fetch --all &&
+ grep "\"child_start\".*\"git-remote-https\",\"$HTTPD_URL/bundle-list\"" \
+ trace-mult.txt >bundle-fetches &&
+ test_line_count = 1 bundle-fetches
+'
+# Do not add tests here unless they use the HTTP server, as they will
+# not run unless the HTTP dependencies exist.
+
+test_done
diff --git a/t/t5559-http-fetch-smart-http2.sh b/t/t5559-http-fetch-smart-http2.sh
new file mode 100755
index 0000000..54aa9d3
--- /dev/null
+++ b/t/t5559-http-fetch-smart-http2.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+HTTP_PROTO=HTTP/2
+LIB_HTTPD_SSL=1
+. ./t5551-http-fetch-smart.sh
diff --git a/t/t5560-http-backend-noserver.sh b/t/t5560-http-backend-noserver.sh
index d30cf4f..f75068d 100755
--- a/t/t5560-http-backend-noserver.sh
+++ b/t/t5560-http-backend-noserver.sh
@@ -4,6 +4,7 @@ test_description='test git-http-backend-noserver'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
HTTPD_DOCUMENT_ROOT_PATH="$TRASH_DIRECTORY"
diff --git a/t/t5561-http-backend.sh b/t/t5561-http-backend.sh
index 9c57d84..e1d3b8c 100755
--- a/t/t5561-http-backend.sh
+++ b/t/t5561-http-backend.sh
@@ -4,6 +4,7 @@ test_description='test git-http-backend'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-httpd.sh
diff --git a/t/t5562-http-backend-content-length.sh b/t/t5562-http-backend-content-length.sh
index 05a5806..7ee9858 100755
--- a/t/t5562-http-backend-content-length.sh
+++ b/t/t5562-http-backend-content-length.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test git-http-backend respects CONTENT_LENGTH'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_lazy_prereq GZIP 'gzip --version'
@@ -63,7 +65,7 @@ test_expect_success 'setup' '
hash_next=$(git commit-tree -p HEAD -m next HEAD^{tree}) &&
{
printf "%s %s refs/heads/newbranch\\0report-status object-format=%s\\n" \
- "$ZERO_OID" "$hash_next" "$(test_oid algo)" | packetize_raw
+ "$ZERO_OID" "$hash_next" "$(test_oid algo)" | packetize_raw &&
printf 0000 &&
echo "$hash_next" | git pack-objects --stdout
} >push_body &&
diff --git a/t/t5562/invoke-with-content-length.pl b/t/t5562/invoke-with-content-length.pl
index 0943474..9babb9a 100644
--- a/t/t5562/invoke-with-content-length.pl
+++ b/t/t5562/invoke-with-content-length.pl
@@ -1,4 +1,4 @@
-use 5.008;
+use 5.008001;
use strict;
use warnings;
@@ -13,11 +13,6 @@ 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);
{
@@ -29,8 +24,13 @@ my $pid = open(my $out, "|-", @command);
}
print $out $body_data or die "Cannot write data: $!";
-sleep 60; # is interrupted by SIGCHLD
-if (!$exited) {
- close($out);
+$SIG{ALRM} = sub {
+ kill 'KILL', $pid;
die "Command did not exit after reading whole body";
+};
+alarm 60;
+
+my $ret = waitpid($pid, 0);
+if ($ret != $pid) {
+ die "confusing return from waitpid: $ret";
}
diff --git a/t/t5563-simple-http-auth.sh b/t/t5563-simple-http-auth.sh
new file mode 100755
index 0000000..ab8a721
--- /dev/null
+++ b/t/t5563-simple-http-auth.sh
@@ -0,0 +1,329 @@
+#!/bin/sh
+
+test_description='test http auth header and credential helper interop'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-httpd.sh
+
+enable_cgipassauth
+if ! test_have_prereq CGIPASSAUTH
+then
+ skip_all="no CGIPassAuth support"
+ test_done
+fi
+start_httpd
+
+test_expect_success 'setup_credential_helper' '
+ mkdir "$TRASH_DIRECTORY/bin" &&
+ PATH=$PATH:"$TRASH_DIRECTORY/bin" &&
+ export PATH &&
+
+ CREDENTIAL_HELPER="$TRASH_DIRECTORY/bin/git-credential-test-helper" &&
+ write_script "$CREDENTIAL_HELPER" <<-\EOF
+ cmd=$1
+ teefile=$cmd-query.cred
+ catfile=$cmd-reply.cred
+ sed -n -e "/^$/q" -e "p" >>$teefile
+ if test "$cmd" = "get"
+ then
+ cat $catfile
+ fi
+ EOF
+'
+
+set_credential_reply () {
+ cat >"$TRASH_DIRECTORY/$1-reply.cred"
+}
+
+expect_credential_query () {
+ cat >"$TRASH_DIRECTORY/$1-expect.cred" &&
+ test_cmp "$TRASH_DIRECTORY/$1-expect.cred" \
+ "$TRASH_DIRECTORY/$1-query.cred"
+}
+
+per_test_cleanup () {
+ rm -f *.cred &&
+ rm -f "$HTTPD_ROOT_PATH"/custom-auth.valid \
+ "$HTTPD_ROOT_PATH"/custom-auth.challenge
+}
+
+test_expect_success 'setup repository' '
+ test_commit foo &&
+ git init --bare "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ git push --mirror "$HTTPD_DOCUMENT_ROOT_PATH/repo.git"
+'
+
+test_expect_success 'access using basic auth' '
+ test_when_finished "per_test_cleanup" &&
+
+ set_credential_reply get <<-EOF &&
+ username=alice
+ password=secret-passwd
+ EOF
+
+ # Basic base64(alice:secret-passwd)
+ cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+ Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ EOF
+
+ cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+ WWW-Authenticate: Basic realm="example.com"
+ EOF
+
+ test_config_global credential.helper test-helper &&
+ git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+ expect_credential_query get <<-EOF &&
+ protocol=http
+ host=$HTTPD_DEST
+ wwwauth[]=Basic realm="example.com"
+ EOF
+
+ expect_credential_query store <<-EOF
+ protocol=http
+ host=$HTTPD_DEST
+ username=alice
+ password=secret-passwd
+ EOF
+'
+
+test_expect_success 'access using basic auth invalid credentials' '
+ test_when_finished "per_test_cleanup" &&
+
+ set_credential_reply get <<-EOF &&
+ username=baduser
+ password=wrong-passwd
+ EOF
+
+ # Basic base64(alice:secret-passwd)
+ cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+ Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ EOF
+
+ cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+ WWW-Authenticate: Basic realm="example.com"
+ EOF
+
+ test_config_global credential.helper test-helper &&
+ test_must_fail git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+ expect_credential_query get <<-EOF &&
+ protocol=http
+ host=$HTTPD_DEST
+ wwwauth[]=Basic realm="example.com"
+ EOF
+
+ expect_credential_query erase <<-EOF
+ protocol=http
+ host=$HTTPD_DEST
+ username=baduser
+ password=wrong-passwd
+ wwwauth[]=Basic realm="example.com"
+ EOF
+'
+
+test_expect_success 'access using basic auth with extra challenges' '
+ test_when_finished "per_test_cleanup" &&
+
+ set_credential_reply get <<-EOF &&
+ username=alice
+ password=secret-passwd
+ EOF
+
+ # Basic base64(alice:secret-passwd)
+ cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+ Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ EOF
+
+ cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+ WWW-Authenticate: FooBar param1="value1" param2="value2"
+ WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
+ WWW-Authenticate: Basic realm="example.com"
+ EOF
+
+ test_config_global credential.helper test-helper &&
+ git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+ expect_credential_query get <<-EOF &&
+ protocol=http
+ host=$HTTPD_DEST
+ wwwauth[]=FooBar param1="value1" param2="value2"
+ wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
+ wwwauth[]=Basic realm="example.com"
+ EOF
+
+ expect_credential_query store <<-EOF
+ protocol=http
+ host=$HTTPD_DEST
+ username=alice
+ password=secret-passwd
+ EOF
+'
+
+test_expect_success 'access using basic auth mixed-case wwwauth header name' '
+ test_when_finished "per_test_cleanup" &&
+
+ set_credential_reply get <<-EOF &&
+ username=alice
+ password=secret-passwd
+ EOF
+
+ # Basic base64(alice:secret-passwd)
+ cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+ Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ EOF
+
+ cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+ www-authenticate: foobar param1="value1" param2="value2"
+ WWW-AUTHENTICATE: BEARER authorize_uri="id.example.com" p=1 q=0
+ WwW-aUtHeNtIcAtE: baSiC realm="example.com"
+ EOF
+
+ test_config_global credential.helper test-helper &&
+ git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+ expect_credential_query get <<-EOF &&
+ protocol=http
+ host=$HTTPD_DEST
+ wwwauth[]=foobar param1="value1" param2="value2"
+ wwwauth[]=BEARER authorize_uri="id.example.com" p=1 q=0
+ wwwauth[]=baSiC realm="example.com"
+ EOF
+
+ expect_credential_query store <<-EOF
+ protocol=http
+ host=$HTTPD_DEST
+ username=alice
+ password=secret-passwd
+ EOF
+'
+
+test_expect_success 'access using basic auth with wwwauth header continuations' '
+ test_when_finished "per_test_cleanup" &&
+
+ set_credential_reply get <<-EOF &&
+ username=alice
+ password=secret-passwd
+ EOF
+
+ # Basic base64(alice:secret-passwd)
+ cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+ Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ EOF
+
+ # Note that leading and trailing whitespace is important to correctly
+ # simulate a continuation/folded header.
+ cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+ WWW-Authenticate: FooBar param1="value1"
+ param2="value2"
+ WWW-Authenticate: Bearer authorize_uri="id.example.com"
+ p=1
+ q=0
+ WWW-Authenticate: Basic realm="example.com"
+ EOF
+
+ test_config_global credential.helper test-helper &&
+ git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+ expect_credential_query get <<-EOF &&
+ protocol=http
+ host=$HTTPD_DEST
+ wwwauth[]=FooBar param1="value1" param2="value2"
+ wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
+ wwwauth[]=Basic realm="example.com"
+ EOF
+
+ expect_credential_query store <<-EOF
+ protocol=http
+ host=$HTTPD_DEST
+ username=alice
+ password=secret-passwd
+ EOF
+'
+
+test_expect_success 'access using basic auth with wwwauth header empty continuations' '
+ test_when_finished "per_test_cleanup" &&
+
+ set_credential_reply get <<-EOF &&
+ username=alice
+ password=secret-passwd
+ EOF
+
+ # Basic base64(alice:secret-passwd)
+ cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+ Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ EOF
+
+ CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
+
+ # Note that leading and trailing whitespace is important to correctly
+ # simulate a continuation/folded header.
+ printf "WWW-Authenticate: FooBar param1=\"value1\"\r\n" >"$CHALLENGE" &&
+ printf " \r\n" >>"$CHALLENGE" &&
+ printf " param2=\"value2\"\r\n" >>"$CHALLENGE" &&
+ printf "WWW-Authenticate: Bearer authorize_uri=\"id.example.com\"\r\n" >>"$CHALLENGE" &&
+ printf " p=1\r\n" >>"$CHALLENGE" &&
+ printf " \r\n" >>"$CHALLENGE" &&
+ printf " q=0\r\n" >>"$CHALLENGE" &&
+ printf "WWW-Authenticate: Basic realm=\"example.com\"\r\n" >>"$CHALLENGE" &&
+
+ test_config_global credential.helper test-helper &&
+ git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+ expect_credential_query get <<-EOF &&
+ protocol=http
+ host=$HTTPD_DEST
+ wwwauth[]=FooBar param1="value1" param2="value2"
+ wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
+ wwwauth[]=Basic realm="example.com"
+ EOF
+
+ expect_credential_query store <<-EOF
+ protocol=http
+ host=$HTTPD_DEST
+ username=alice
+ password=secret-passwd
+ EOF
+'
+
+test_expect_success 'access using basic auth with wwwauth header mixed line-endings' '
+ test_when_finished "per_test_cleanup" &&
+
+ set_credential_reply get <<-EOF &&
+ username=alice
+ password=secret-passwd
+ EOF
+
+ # Basic base64(alice:secret-passwd)
+ cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+ Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+ EOF
+
+ CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
+
+ # Note that leading and trailing whitespace is important to correctly
+ # simulate a continuation/folded header.
+ printf "WWW-Authenticate: FooBar param1=\"value1\"\r\n" >"$CHALLENGE" &&
+ printf " \r\n" >>"$CHALLENGE" &&
+ printf "\tparam2=\"value2\"\r\n" >>"$CHALLENGE" &&
+ printf "WWW-Authenticate: Basic realm=\"example.com\"" >>"$CHALLENGE" &&
+
+ test_config_global credential.helper test-helper &&
+ git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+ expect_credential_query get <<-EOF &&
+ protocol=http
+ host=$HTTPD_DEST
+ wwwauth[]=FooBar param1="value1" param2="value2"
+ wwwauth[]=Basic realm="example.com"
+ EOF
+
+ expect_credential_query store <<-EOF
+ protocol=http
+ host=$HTTPD_DEST
+ username=alice
+ password=secret-passwd
+ EOF
+'
+
+test_done
diff --git a/t/t5564-http-proxy.sh b/t/t5564-http-proxy.sh
new file mode 100755
index 0000000..9da5134
--- /dev/null
+++ b/t/t5564-http-proxy.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+test_description="test fetching through http proxy"
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-httpd.sh
+
+LIB_HTTPD_PROXY=1
+start_httpd
+
+test_expect_success 'setup repository' '
+ test_commit foo &&
+ git init --bare "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ git push --mirror "$HTTPD_DOCUMENT_ROOT_PATH/repo.git"
+'
+
+setup_askpass_helper
+
+# sanity check that our test setup is correctly using proxy
+test_expect_success 'proxy requires password' '
+ test_config_global http.proxy $HTTPD_DEST &&
+ test_must_fail git clone $HTTPD_URL/smart/repo.git 2>err &&
+ grep "error.*407" err
+'
+
+test_expect_success 'clone through proxy with auth' '
+ test_when_finished "rm -rf clone" &&
+ test_config_global http.proxy http://proxuser:proxpass@$HTTPD_DEST &&
+ GIT_TRACE_CURL=$PWD/trace git clone $HTTPD_URL/smart/repo.git clone &&
+ grep -i "Proxy-Authorization: Basic <redacted>" trace
+'
+
+test_expect_success 'clone can prompt for proxy password' '
+ test_when_finished "rm -rf clone" &&
+ test_config_global http.proxy http://proxuser@$HTTPD_DEST &&
+ set_askpass nobody proxpass &&
+ GIT_TRACE_CURL=$PWD/trace git clone $HTTPD_URL/smart/repo.git clone &&
+ expect_askpass pass proxuser
+'
+
+test_done
diff --git a/t/t5570-git-daemon.sh b/t/t5570-git-daemon.sh
index b87ca06..f9a9bf9 100755
--- a/t/t5570-git-daemon.sh
+++ b/t/t5570-git-daemon.sh
@@ -10,9 +10,9 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
start_git_daemon
check_verbose_connect () {
- 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_grep -F "Looking up 127.0.0.1 ..." stderr &&
+ test_grep -F "Connecting to 127.0.0.1 (port " stderr &&
+ test_grep -F "done." stderr
}
test_expect_success 'setup repository' '
@@ -108,7 +108,7 @@ test_expect_success 'fetch notices corrupt idx' '
test_expect_success 'client refuses to ask for repo with newline' '
test_must_fail git clone "$GIT_DAEMON_URL/repo$LF.git" dst 2>stderr &&
- test_i18ngrep newline.is.forbidden stderr
+ test_grep newline.is.forbidden stderr
'
test_remote_error()
@@ -148,7 +148,7 @@ test_remote_error()
fi
test_must_fail git "$cmd" "$GIT_DAEMON_URL/$repo" "$@" 2>output &&
- test_i18ngrep "fatal: remote error: $msg: /$repo" output &&
+ test_grep "fatal: remote error: $msg: /$repo" output &&
ret=$?
chmod +x "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git"
(exit $ret)
@@ -194,7 +194,7 @@ test_expect_success 'hostname cannot break out of directory' '
test_expect_success FAKENC 'hostname interpolation works after LF-stripping' '
{
- printf "git-upload-pack /interp.git\n\0host=localhost" | packetize_raw
+ printf "git-upload-pack /interp.git\n\0host=localhost" | packetize_raw &&
printf "0000"
} >input &&
fake_nc "$GIT_DAEMON_HOST_PORT" <input >output &&
diff --git a/t/t5571-pre-push-hook.sh b/t/t5571-pre-push-hook.sh
index ad8d580..448134c 100755
--- a/t/t5571-pre-push-hook.sh
+++ b/t/t5571-pre-push-hook.sh
@@ -4,59 +4,69 @@ test_description='check pre-push hooks'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
-# Setup hook that always succeeds
-HOOKDIR="$(git rev-parse --git-dir)/hooks"
-HOOK="$HOOKDIR/pre-push"
-mkdir -p "$HOOKDIR"
-write_script "$HOOK" <<EOF
-cat >/dev/null
-exit 0
-EOF
-
test_expect_success 'setup' '
+ test_hook pre-push <<-\EOF &&
+ cat >actual
+ EOF
+
git config push.default upstream &&
git init --bare repo1 &&
git remote add parent1 repo1 &&
test_commit one &&
- git push parent1 HEAD:foreign
+ cat >expect <<-EOF &&
+ HEAD $(git rev-parse HEAD) refs/heads/foreign $(test_oid zero)
+ EOF
+
+ test_when_finished "rm actual" &&
+ git push parent1 HEAD:foreign &&
+ test_cmp expect actual
'
-write_script "$HOOK" <<EOF
-cat >/dev/null
-exit 1
-EOF
COMMIT1="$(git rev-parse HEAD)"
export COMMIT1
test_expect_success 'push with failing hook' '
+ test_hook pre-push <<-\EOF &&
+ cat >actual &&
+ exit 1
+ EOF
+
test_commit two &&
- test_must_fail git push parent1 HEAD
+ cat >expect <<-EOF &&
+ HEAD $(git rev-parse HEAD) refs/heads/main $(test_oid zero)
+ EOF
+
+ test_when_finished "rm actual" &&
+ test_must_fail git push parent1 HEAD &&
+ test_cmp expect actual
'
test_expect_success '--no-verify bypasses hook' '
- git push --no-verify parent1 HEAD
+ git push --no-verify parent1 HEAD &&
+ test_path_is_missing actual
'
COMMIT2="$(git rev-parse HEAD)"
export COMMIT2
-write_script "$HOOK" <<'EOF'
-echo "$1" >actual
-echo "$2" >>actual
-cat >>actual
-EOF
-
-cat >expected <<EOF
-parent1
-repo1
-refs/heads/main $COMMIT2 refs/heads/foreign $COMMIT1
-EOF
-
test_expect_success 'push with hook' '
+ test_hook --setup pre-push <<-\EOF &&
+ echo "$1" >actual
+ echo "$2" >>actual
+ cat >>actual
+ EOF
+
+ cat >expect <<-EOF &&
+ parent1
+ repo1
+ refs/heads/main $COMMIT2 refs/heads/foreign $COMMIT1
+ EOF
+
git push parent1 main:foreign &&
- diff expected actual
+ test_cmp expect actual
'
test_expect_success 'add a branch' '
@@ -67,64 +77,65 @@ test_expect_success 'add a branch' '
COMMIT3="$(git rev-parse HEAD)"
export COMMIT3
-cat >expected <<EOF
-parent1
-repo1
-refs/heads/other $COMMIT3 refs/heads/foreign $COMMIT2
-EOF
-
test_expect_success 'push to default' '
+ cat >expect <<-EOF &&
+ parent1
+ repo1
+ refs/heads/other $COMMIT3 refs/heads/foreign $COMMIT2
+ EOF
git push &&
- diff expected actual
+ test_cmp expect actual
'
-cat >expected <<EOF
-parent1
-repo1
-refs/tags/one $COMMIT1 refs/tags/tag1 $ZERO_OID
-HEAD~ $COMMIT2 refs/heads/prev $ZERO_OID
-EOF
-
test_expect_success 'push non-branches' '
+ cat >expect <<-EOF &&
+ parent1
+ repo1
+ refs/tags/one $COMMIT1 refs/tags/tag1 $ZERO_OID
+ HEAD~ $COMMIT2 refs/heads/prev $ZERO_OID
+ EOF
+
git push parent1 one:tag1 HEAD~:refs/heads/prev &&
- diff expected actual
+ test_cmp expect actual
'
-cat >expected <<EOF
-parent1
-repo1
-(delete) $ZERO_OID refs/heads/prev $COMMIT2
-EOF
-
test_expect_success 'push delete' '
+ cat >expect <<-EOF &&
+ parent1
+ repo1
+ (delete) $ZERO_OID refs/heads/prev $COMMIT2
+ EOF
+
git push parent1 :prev &&
- diff expected actual
+ test_cmp expect actual
'
-cat >expected <<EOF
-repo1
-repo1
-HEAD $COMMIT3 refs/heads/other $ZERO_OID
-EOF
-
test_expect_success 'push to URL' '
+ cat >expect <<-EOF &&
+ repo1
+ repo1
+ HEAD $COMMIT3 refs/heads/other $ZERO_OID
+ EOF
+
git push repo1 HEAD &&
- diff expected actual
+ test_cmp expect actual
'
test_expect_success 'set up many-ref tests' '
{
- nr=1000
+ nr=1000 &&
while test $nr -lt 2000
do
- nr=$(( $nr + 1 ))
- echo "create refs/heads/b/$nr $COMMIT3"
+ nr=$(( $nr + 1 )) &&
+ echo "create refs/heads/b/$nr $COMMIT3" || return 1
done
} | git update-ref --stdin
'
test_expect_success 'sigpipe does not cause pre-push hook failure' '
- echo "exit 0" | write_script "$HOOK" &&
+ test_hook --clobber pre-push <<-\EOF &&
+ exit 0
+ EOF
git push parent1 "refs/heads/b/*:refs/heads/b/*"
'
diff --git a/t/t5572-pull-submodule.sh b/t/t5572-pull-submodule.sh
index 4f92a11..5174452 100755
--- a/t/t5572-pull-submodule.sh
+++ b/t/t5572-pull-submodule.sh
@@ -2,6 +2,9 @@
test_description='pull can handle submodules'
+GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1
+export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
+
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-submodule-update.sh
@@ -49,6 +52,10 @@ then
fi
test_submodule_switch_func "git_pull_noff"
+test_expect_success 'setup' '
+ git config --global protocol.file.allow always
+'
+
test_expect_success 'pull --recurse-submodule setup' '
test_create_repo child &&
test_commit -C child bar &&
@@ -104,6 +111,32 @@ test_expect_success " --[no-]recurse-submodule and submodule.recurse" '
test_path_is_file super/sub/merge_strategy_4.t
'
+test_expect_success "fetch.recurseSubmodules option triggers recursive fetch (but not recursive update)" '
+ test_commit -C child merge_strategy_5 &&
+ # Omit the parent commit, otherwise this passes with the
+ # default "pull" behavior.
+
+ git -C super -c fetch.recursesubmodules=true pull --no-rebase &&
+ # Check that the submodule commit was fetched
+ sub_oid=$(git -C child rev-parse HEAD) &&
+ git -C super/sub cat-file -e $sub_oid &&
+ # Check that the submodule worktree did not update
+ test_path_is_missing super/sub/merge_strategy_5.t
+'
+
+test_expect_success "fetch.recurseSubmodules takes precedence over submodule.recurse" '
+ test_commit -C child merge_strategy_6 &&
+ # Omit the parent commit, otherwise this passes with the
+ # default "pull" behavior.
+
+ git -C super -c submodule.recurse=false -c fetch.recursesubmodules=true pull --no-rebase &&
+ # Check that the submodule commit was fetched
+ sub_oid=$(git -C child rev-parse HEAD) &&
+ git -C super/sub cat-file -e $sub_oid &&
+ # Check that the submodule worktree did not update
+ test_path_is_missing super/sub/merge_strategy_6.t
+'
+
test_expect_success 'pull --rebase --recurse-submodules (remote superproject submodule changes, local submodule changes)' '
# This tests the following scenario :
# - local submodule has new commits
@@ -144,7 +177,7 @@ test_expect_success 'pull --rebase --recurse-submodules fails if both sides reco
# submodule itself, but the merge strategy in submodules
# does not support rebase:
test_must_fail git -C super pull --rebase --recurse-submodules 2>err &&
- test_i18ngrep "locally recorded submodule modifications" err
+ test_grep "locally recorded submodule modifications" err
'
test_expect_success 'pull --rebase --recurse-submodules (no submodule changes, no fork-point)' '
diff --git a/t/t5573-pull-verify-signatures.sh b/t/t5573-pull-verify-signatures.sh
index a53dd85..ab05f38 100755
--- a/t/t5573-pull-verify-signatures.sh
+++ b/t/t5573-pull-verify-signatures.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='pull signature verification tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY/lib-gpg.sh"
@@ -45,46 +47,46 @@ test_expect_success GPG 'create repositories with signed commits' '
test_expect_success GPG 'pull unsigned commit with --verify-signatures' '
test_when_finished "git reset --hard && git checkout initial" &&
test_must_fail git pull --ff-only --verify-signatures unsigned 2>pullerror &&
- test_i18ngrep "does not have a GPG signature" pullerror
+ test_grep "does not have a GPG signature" pullerror
'
test_expect_success GPG 'pull commit with bad signature with --verify-signatures' '
test_when_finished "git reset --hard && git checkout initial" &&
test_must_fail git pull --ff-only --verify-signatures bad 2>pullerror &&
- test_i18ngrep "has a bad GPG signature" pullerror
+ test_grep "has a bad GPG signature" pullerror
'
test_expect_success GPG 'pull commit with untrusted signature with --verify-signatures' '
test_when_finished "git reset --hard && git checkout initial" &&
test_must_fail git pull --ff-only --verify-signatures untrusted 2>pullerror &&
- test_i18ngrep "has an untrusted GPG signature" pullerror
+ test_grep "has an untrusted GPG signature" pullerror
'
test_expect_success GPG 'pull commit with untrusted signature with --verify-signatures and minTrustLevel=ultimate' '
test_when_finished "git reset --hard && git checkout initial" &&
test_config gpg.minTrustLevel ultimate &&
test_must_fail git pull --ff-only --verify-signatures untrusted 2>pullerror &&
- test_i18ngrep "has an untrusted GPG signature" pullerror
+ test_grep "has an untrusted GPG signature" pullerror
'
test_expect_success GPG 'pull commit with untrusted signature with --verify-signatures and minTrustLevel=marginal' '
test_when_finished "git reset --hard && git checkout initial" &&
test_config gpg.minTrustLevel marginal &&
test_must_fail git pull --ff-only --verify-signatures untrusted 2>pullerror &&
- test_i18ngrep "has an untrusted GPG signature" pullerror
+ test_grep "has an untrusted GPG signature" pullerror
'
test_expect_success GPG 'pull commit with untrusted signature with --verify-signatures and minTrustLevel=undefined' '
test_when_finished "git reset --hard && git checkout initial" &&
test_config gpg.minTrustLevel undefined &&
git pull --ff-only --verify-signatures untrusted >pulloutput &&
- test_i18ngrep "has a good GPG signature" pulloutput
+ test_grep "has a good GPG signature" pulloutput
'
test_expect_success GPG 'pull signed commit with --verify-signatures' '
test_when_finished "git reset --hard && git checkout initial" &&
git pull --verify-signatures signed >pulloutput &&
- test_i18ngrep "has a good GPG signature" pulloutput
+ test_grep "has a good GPG signature" pulloutput
'
test_expect_success GPG 'pull commit with bad signature without verification' '
@@ -104,7 +106,7 @@ test_expect_success GPG 'pull unsigned commit into unborn branch' '
git init empty-repo &&
test_must_fail \
git -C empty-repo pull --verify-signatures .. 2>pullerror &&
- test_i18ngrep "does not have a GPG signature" pullerror
+ test_grep "does not have a GPG signature" pullerror
'
test_expect_success GPG 'pull commit into unborn branch with bad signature and --verify-signatures' '
@@ -112,7 +114,7 @@ test_expect_success GPG 'pull commit into unborn branch with bad signature and -
git init empty-repo &&
test_must_fail \
git -C empty-repo pull --ff-only --verify-signatures ../bad 2>pullerror &&
- test_i18ngrep "has a bad GPG signature" pullerror
+ test_grep "has a bad GPG signature" pullerror
'
test_expect_success GPG 'pull commit into unborn branch with untrusted signature and --verify-signatures' '
@@ -120,7 +122,7 @@ test_expect_success GPG 'pull commit into unborn branch with untrusted signature
git init empty-repo &&
test_must_fail \
git -C empty-repo pull --ff-only --verify-signatures ../untrusted 2>pullerror &&
- test_i18ngrep "has an untrusted GPG signature" pullerror
+ test_grep "has an untrusted GPG signature" pullerror
'
test_expect_success GPG 'pull commit into unborn branch with untrusted signature and --verify-signatures and minTrustLevel=ultimate' '
@@ -129,7 +131,7 @@ test_expect_success GPG 'pull commit into unborn branch with untrusted signature
test_config_global gpg.minTrustLevel ultimate &&
test_must_fail \
git -C empty-repo pull --ff-only --verify-signatures ../untrusted 2>pullerror &&
- test_i18ngrep "has an untrusted GPG signature" pullerror
+ test_grep "has an untrusted GPG signature" pullerror
'
test_expect_success GPG 'pull commit into unborn branch with untrusted signature and --verify-signatures and minTrustLevel=marginal' '
@@ -138,7 +140,7 @@ test_expect_success GPG 'pull commit into unborn branch with untrusted signature
test_config_global gpg.minTrustLevel marginal &&
test_must_fail \
git -C empty-repo pull --ff-only --verify-signatures ../untrusted 2>pullerror &&
- test_i18ngrep "has an untrusted GPG signature" pullerror
+ test_grep "has an untrusted GPG signature" pullerror
'
test_expect_success GPG 'pull commit into unborn branch with untrusted signature and --verify-signatures and minTrustLevel=undefined' '
@@ -146,7 +148,7 @@ test_expect_success GPG 'pull commit into unborn branch with untrusted signature
git init empty-repo &&
test_config_global gpg.minTrustLevel undefined &&
git -C empty-repo pull --ff-only --verify-signatures ../untrusted >pulloutput &&
- test_i18ngrep "has a good GPG signature" pulloutput
+ test_grep "has a good GPG signature" pulloutput
'
test_done
diff --git a/t/t5574-fetch-output.sh b/t/t5574-fetch-output.sh
new file mode 100755
index 0000000..5883839
--- /dev/null
+++ b/t/t5574-fetch-output.sh
@@ -0,0 +1,304 @@
+#!/bin/sh
+
+test_description='git fetch output format'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+test_expect_success 'fetch with invalid output format configuration' '
+ test_when_finished "rm -rf clone" &&
+ git clone . clone &&
+
+ test_must_fail git -C clone -c fetch.output fetch origin 2>actual.err &&
+ cat >expect <<-EOF &&
+ error: missing value for ${SQ}fetch.output${SQ}
+ fatal: unable to parse ${SQ}fetch.output${SQ} from command-line config
+ EOF
+ test_cmp expect actual.err &&
+
+ test_must_fail git -C clone -c fetch.output= fetch origin 2>actual.err &&
+ cat >expect <<-EOF &&
+ fatal: invalid value for ${SQ}fetch.output${SQ}: ${SQ}${SQ}
+ EOF
+ test_cmp expect actual.err &&
+
+ test_must_fail git -C clone -c fetch.output=garbage fetch origin 2>actual.err &&
+ cat >expect <<-EOF &&
+ fatal: invalid value for ${SQ}fetch.output${SQ}: ${SQ}garbage${SQ}
+ EOF
+ test_cmp expect actual.err
+'
+
+test_expect_success 'fetch aligned output' '
+ git clone . full-output &&
+ test_commit looooooooooooong-tag &&
+ (
+ cd full-output &&
+ git -c fetch.output=full fetch origin >actual 2>&1 &&
+ grep -e "->" actual | cut -c 22- >../actual
+ ) &&
+ cat >expect <<-\EOF &&
+ main -> origin/main
+ looooooooooooong-tag -> looooooooooooong-tag
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'fetch compact output' '
+ git clone . compact &&
+ test_commit extraaa &&
+ (
+ cd compact &&
+ git -c fetch.output=compact fetch origin >actual 2>&1 &&
+ grep -e "->" actual | cut -c 22- >../actual
+ ) &&
+ cat >expect <<-\EOF &&
+ main -> origin/*
+ extraaa -> *
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'setup for fetch porcelain output' '
+ # Set up a bunch of references that we can use to demonstrate different
+ # kinds of flag symbols in the output format.
+ test_commit commit-for-porcelain-output &&
+ MAIN_OLD=$(git rev-parse HEAD) &&
+ git branch "fast-forward" &&
+ git branch "deleted-branch" &&
+ git checkout -b force-updated &&
+ test_commit --no-tag force-update-old &&
+ FORCE_UPDATED_OLD=$(git rev-parse HEAD) &&
+ git checkout main &&
+
+ # Backup to preseed.git
+ git clone --mirror . preseed.git &&
+
+ # Continue changing our local references.
+ git branch new-branch &&
+ git branch -d deleted-branch &&
+ git checkout fast-forward &&
+ test_commit --no-tag fast-forward-new &&
+ FAST_FORWARD_NEW=$(git rev-parse HEAD) &&
+ git checkout force-updated &&
+ git reset --hard HEAD~ &&
+ test_commit --no-tag force-update-new &&
+ FORCE_UPDATED_NEW=$(git rev-parse HEAD)
+'
+
+for opt in "" "--atomic"
+do
+ test_expect_success "fetch porcelain output ${opt:+(atomic)}" '
+ test_when_finished "rm -rf porcelain" &&
+
+ # Clone and pre-seed the repositories. We fetch references into two
+ # namespaces so that we can test that rejected and force-updated
+ # references are reported properly.
+ refspecs="refs/heads/*:refs/unforced/* +refs/heads/*:refs/forced/*" &&
+ git clone preseed.git porcelain &&
+ git -C porcelain fetch origin $opt $refspecs &&
+
+ cat >expect <<-EOF &&
+ - $MAIN_OLD $ZERO_OID refs/forced/deleted-branch
+ - $MAIN_OLD $ZERO_OID refs/unforced/deleted-branch
+ $MAIN_OLD $FAST_FORWARD_NEW refs/unforced/fast-forward
+ ! $FORCE_UPDATED_OLD $FORCE_UPDATED_NEW refs/unforced/force-updated
+ * $ZERO_OID $MAIN_OLD refs/unforced/new-branch
+ $MAIN_OLD $FAST_FORWARD_NEW refs/forced/fast-forward
+ + $FORCE_UPDATED_OLD $FORCE_UPDATED_NEW refs/forced/force-updated
+ * $ZERO_OID $MAIN_OLD refs/forced/new-branch
+ $MAIN_OLD $FAST_FORWARD_NEW refs/remotes/origin/fast-forward
+ + $FORCE_UPDATED_OLD $FORCE_UPDATED_NEW refs/remotes/origin/force-updated
+ * $ZERO_OID $MAIN_OLD refs/remotes/origin/new-branch
+ EOF
+
+ # Change the URL of the repository to fetch different references.
+ git -C porcelain remote set-url origin .. &&
+
+ # Execute a dry-run fetch first. We do this to assert that the dry-run
+ # and non-dry-run fetches produces the same output. Execution of the
+ # fetch is expected to fail as we have a rejected reference update.
+ test_must_fail git -C porcelain fetch $opt \
+ --porcelain --dry-run --prune origin $refspecs >actual &&
+ test_cmp expect actual &&
+
+ # And now we perform a non-dry-run fetch.
+ test_must_fail git -C porcelain fetch $opt \
+ --porcelain --prune origin $refspecs >actual 2>stderr &&
+ test_cmp expect actual &&
+ test_must_be_empty stderr
+ '
+done
+
+test_expect_success 'fetch porcelain with multiple remotes' '
+ test_when_finished "rm -rf porcelain" &&
+
+ git switch --create multiple-remotes &&
+ git clone . porcelain &&
+ git -C porcelain remote add second-remote "$PWD" &&
+ git -C porcelain fetch second-remote &&
+
+ test_commit --no-tag multi-commit &&
+ old_commit=$(git rev-parse HEAD~) &&
+ new_commit=$(git rev-parse HEAD) &&
+
+ cat >expect <<-EOF &&
+ $old_commit $new_commit refs/remotes/origin/multiple-remotes
+ $old_commit $new_commit refs/remotes/second-remote/multiple-remotes
+ EOF
+
+ git -C porcelain fetch --porcelain --all >actual 2>stderr &&
+ test_cmp expect actual &&
+ test_must_be_empty stderr
+'
+
+test_expect_success 'fetch porcelain refuses to work with submodules' '
+ test_when_finished "rm -rf porcelain" &&
+
+ cat >expect <<-EOF &&
+ fatal: options ${SQ}--porcelain${SQ} and ${SQ}--recurse-submodules${SQ} cannot be used together
+ EOF
+
+ git init porcelain &&
+ test_must_fail git -C porcelain fetch --porcelain --recurse-submodules=yes 2>stderr &&
+ test_cmp expect stderr &&
+
+ test_must_fail git -C porcelain fetch --porcelain --recurse-submodules=on-demand 2>stderr &&
+ test_cmp expect stderr
+'
+
+test_expect_success 'fetch porcelain overrides fetch.output config' '
+ test_when_finished "rm -rf porcelain" &&
+
+ git switch --create config-override &&
+ git clone . porcelain &&
+ test_commit new-commit &&
+ old_commit=$(git rev-parse HEAD~) &&
+ new_commit=$(git rev-parse HEAD) &&
+
+ cat >expect <<-EOF &&
+ $old_commit $new_commit refs/remotes/origin/config-override
+ * $ZERO_OID $new_commit refs/tags/new-commit
+ EOF
+
+ git -C porcelain -c fetch.output=compact fetch --porcelain >stdout 2>stderr &&
+ test_must_be_empty stderr &&
+ test_cmp expect stdout
+'
+
+test_expect_success 'fetch --no-porcelain overrides previous --porcelain' '
+ test_when_finished "rm -rf no-porcelain" &&
+
+ git switch --create no-porcelain &&
+ git clone . no-porcelain &&
+ test_commit --no-tag no-porcelain &&
+ old_commit=$(git rev-parse --short HEAD~) &&
+ new_commit=$(git rev-parse --short HEAD) &&
+
+ cat >expect <<-EOF &&
+ From $(test-tool path-utils real_path .)/.
+ $old_commit..$new_commit no-porcelain -> origin/no-porcelain
+ EOF
+
+ git -C no-porcelain fetch --porcelain --no-porcelain >stdout 2>stderr &&
+ test_cmp expect stderr &&
+ test_must_be_empty stdout
+'
+
+test_expect_success 'fetch output with HEAD' '
+ test_when_finished "rm -rf head" &&
+ git clone . head &&
+
+ git -C head fetch --dry-run origin HEAD >actual.out 2>actual.err &&
+ cat >expect <<-EOF &&
+ From $(test-tool path-utils real_path .)/.
+ * branch HEAD -> FETCH_HEAD
+ EOF
+ test_must_be_empty actual.out &&
+ test_cmp expect actual.err &&
+
+ git -C head fetch origin HEAD >actual.out 2>actual.err &&
+ test_must_be_empty actual.out &&
+ test_cmp expect actual.err &&
+
+ git -C head fetch --dry-run origin HEAD:foo >actual.out 2>actual.err &&
+ cat >expect <<-EOF &&
+ From $(test-tool path-utils real_path .)/.
+ * [new ref] HEAD -> foo
+ EOF
+ test_must_be_empty actual.out &&
+ test_cmp expect actual.err &&
+
+ git -C head fetch origin HEAD:foo >actual.out 2>actual.err &&
+ test_must_be_empty actual.out &&
+ test_cmp expect actual.err
+'
+
+test_expect_success 'fetch porcelain output with HEAD' '
+ test_when_finished "rm -rf head" &&
+ git clone . head &&
+ COMMIT_ID=$(git rev-parse HEAD) &&
+
+ git -C head fetch --porcelain --dry-run origin HEAD >actual &&
+ cat >expect <<-EOF &&
+ * $ZERO_OID $COMMIT_ID FETCH_HEAD
+ EOF
+ test_cmp expect actual &&
+
+ git -C head fetch --porcelain origin HEAD >actual &&
+ test_cmp expect actual &&
+
+ git -C head fetch --porcelain --dry-run origin HEAD:foo >actual &&
+ cat >expect <<-EOF &&
+ * $ZERO_OID $COMMIT_ID refs/heads/foo
+ EOF
+ test_cmp expect actual &&
+
+ git -C head fetch --porcelain origin HEAD:foo >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'fetch output with object ID' '
+ test_when_finished "rm -rf object-id" &&
+ git clone . object-id &&
+ commit=$(git rev-parse HEAD) &&
+
+ git -C object-id fetch --dry-run origin $commit:object-id >actual.out 2>actual.err &&
+ cat >expect <<-EOF &&
+ From $(test-tool path-utils real_path .)/.
+ * [new ref] $commit -> object-id
+ EOF
+ test_must_be_empty actual.out &&
+ test_cmp expect actual.err &&
+
+ git -C object-id fetch origin $commit:object-id >actual.out 2>actual.err &&
+ test_must_be_empty actual.out &&
+ test_cmp expect actual.err
+'
+
+test_expect_success '--no-show-forced-updates' '
+ mkdir forced-updates &&
+ (
+ cd forced-updates &&
+ git init &&
+ test_commit 1 &&
+ test_commit 2
+ ) &&
+ git clone forced-updates forced-update-clone &&
+ git clone forced-updates no-forced-update-clone &&
+ git -C forced-updates reset --hard HEAD~1 &&
+ (
+ cd forced-update-clone &&
+ git fetch --show-forced-updates origin 2>output &&
+ test_grep "(forced update)" output
+ ) &&
+ (
+ cd no-forced-update-clone &&
+ git fetch --no-show-forced-updates origin 2>output &&
+ test_grep ! "(forced update)" output
+ )
+'
+
+test_done
diff --git a/t/t5580-unc-paths.sh b/t/t5580-unc-paths.sh
index cd803ae..d7537a1 100755
--- a/t/t5580-unc-paths.sh
+++ b/t/t5580-unc-paths.sh
@@ -4,6 +4,7 @@ test_description='various Windows-only path tests'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
if test_have_prereq CYGWIN
@@ -74,7 +75,7 @@ test_expect_success push '
test_expect_success MINGW 'remote nick cannot contain backslashes' '
BACKSLASHED="$(winpwd | tr / \\\\)" &&
git ls-remote "$BACKSLASHED" 2>err &&
- test_i18ngrep ! "unable to access" err
+ test_grep ! "unable to access" err
'
test_expect_success 'unc alternates' '
diff --git a/t/t5583-push-branches.sh b/t/t5583-push-branches.sh
new file mode 100755
index 0000000..320f49c
--- /dev/null
+++ b/t/t5583-push-branches.sh
@@ -0,0 +1,116 @@
+#!/bin/sh
+
+test_description='check the consisitency of behavior of --all and --branches'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+delete_refs() {
+ dir=$1
+ shift
+ rm -rf deletes
+ for arg in $*
+ do
+ echo "delete ${arg}" >>deletes
+ done
+ git -C $dir update-ref --stdin < deletes
+}
+
+test_expect_success 'setup bare remote' '
+ git init --bare remote-1 &&
+ git -C remote-1 config gc.auto 0 &&
+ test_commit one &&
+ git push remote-1 HEAD
+'
+
+test_expect_success 'setup different types of references' '
+ cat >refs <<-EOF &&
+ update refs/heads/branch-1 HEAD
+ update refs/heads/branch-2 HEAD
+ EOF
+
+ git tag -a -m "annotated" annotated-1 HEAD &&
+ git tag -a -m "annotated" annotated-2 HEAD &&
+ git update-ref --stdin < refs
+'
+
+test_expect_success '--all and --branches have the same behavior' '
+ test_when_finished "delete_refs remote-1 \
+ refs/heads/branch-1 \
+ refs/heads/branch-2" &&
+ git push remote-1 --all &&
+ commit=$(git rev-parse HEAD) &&
+ cat >expect <<-EOF &&
+ $commit refs/heads/branch-1
+ $commit refs/heads/branch-2
+ $commit refs/heads/main
+ EOF
+
+ git -C remote-1 show-ref --heads >actual.all &&
+ delete_refs remote-1 refs/heads/branch-1 refs/heads/branch-2 &&
+ git push remote-1 --branches &&
+ git -C remote-1 show-ref --heads >actual.branches &&
+ test_cmp actual.all actual.branches &&
+ test_cmp expect actual.all
+'
+
+test_expect_success '--all or --branches can not be combined with refspecs' '
+ test_must_fail git push remote-1 --all main >actual.all 2>&1 &&
+ test_must_fail git push remote-1 --branches main >actual.branches 2>&1 &&
+ test_cmp actual.all actual.branches &&
+ grep "be combined with refspecs" actual.all
+'
+
+test_expect_success '--all or --branches can not be combined with --mirror' '
+ test_must_fail git push remote-1 --all --mirror >actual.all 2>&1 &&
+ test_must_fail git push remote-1 --branches --mirror >actual.branches 2>&1 &&
+ test_cmp actual.all actual.branches &&
+ grep "cannot be used together" actual.all
+'
+
+test_expect_success '--all or --branches can not be combined with --tags' '
+ test_must_fail git push remote-1 --all --tags >actual.all 2>&1 &&
+ test_must_fail git push remote-1 --branches --tags >actual.branches 2>&1 &&
+ test_cmp actual.all actual.branches &&
+ grep "cannot be used together" actual.all
+'
+
+
+test_expect_success '--all or --branches can not be combined with --delete' '
+ test_must_fail git push remote-1 --all --delete >actual.all 2>&1 &&
+ test_must_fail git push remote-1 --branches --delete >actual.branches 2>&1 &&
+ test_cmp actual.all actual.branches &&
+ grep "cannot be used together" actual.all
+'
+
+test_expect_success '--all or --branches combines with --follow-tags have same behavior' '
+ test_when_finished "delete_refs remote-1 \
+ refs/heads/branch-1 \
+ refs/heads/branch-2 \
+ refs/tags/annotated-1 \
+ refs/tags/annotated-2" &&
+ git push remote-1 --all --follow-tags &&
+ git -C remote-1 show-ref > actual.all &&
+ cat >expect <<-EOF &&
+ $commit refs/heads/branch-1
+ $commit refs/heads/branch-2
+ $commit refs/heads/main
+ $(git rev-parse annotated-1) refs/tags/annotated-1
+ $(git rev-parse annotated-2) refs/tags/annotated-2
+ EOF
+
+ delete_refs remote-1 \
+ refs/heads/branch-1 \
+ refs/heads/branch-2 \
+ refs/tags/annotated-1 \
+ refs/tags/annotated-2 &&
+ git push remote-1 --branches --follow-tags &&
+ git -C remote-1 show-ref >actual.branches &&
+ test_cmp actual.all actual.branches &&
+ test_cmp expect actual.all
+'
+
+test_done
diff --git a/t/t5600-clone-fail-cleanup.sh b/t/t5600-clone-fail-cleanup.sh
index 5bf1026..c814afa 100755
--- a/t/t5600-clone-fail-cleanup.sh
+++ b/t/t5600-clone-fail-cleanup.sh
@@ -13,6 +13,7 @@ Unless the directory already exists, in which case we clean up only what we
wrote.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
corrupt_repo () {
@@ -35,7 +36,9 @@ test_expect_success 'create a repo to clone' '
'
test_expect_success 'create objects in repo for later corruption' '
- test_commit -C foo file
+ test_commit -C foo file &&
+ git -C foo checkout --detach &&
+ test_commit -C foo detached
'
# source repository given to git clone should be relative to the
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index 83c24fc..ca43185 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -79,12 +79,10 @@ test_expect_success 'clone from hooks' '
cd .. &&
git init r1 &&
cd r1 &&
- cat >.git/hooks/pre-commit <<-\EOF &&
- #!/bin/sh
+ test_hook pre-commit <<-\EOF &&
git clone ../r0 ../r2
exit 1
EOF
- chmod u+x .git/hooks/pre-commit &&
: >file &&
git add file &&
test_must_fail git commit -m invoke-hook &&
@@ -159,6 +157,23 @@ test_expect_success 'clone --mirror does not repeat tags' '
'
+test_expect_success 'clone with files ref format' '
+ test_when_finished "rm -rf ref-storage" &&
+ git clone --ref-format=files --mirror src ref-storage &&
+ echo files >expect &&
+ git -C ref-storage rev-parse --show-ref-format >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'clone with garbage ref format' '
+ cat >expect <<-EOF &&
+ fatal: unknown ref storage format ${SQ}garbage${SQ}
+ EOF
+ test_must_fail git clone --ref-format=garbage --mirror src ref-storage 2>err &&
+ test_cmp expect err &&
+ test_path_is_missing ref-storage
+'
+
test_expect_success 'clone to destination with trailing /' '
git clone src target-1/ &&
@@ -632,7 +647,7 @@ test_expect_success 'clone on case-insensitive fs' '
test_expect_success CASE_INSENSITIVE_FS 'colliding file detection' '
grep X icasefs/warning &&
grep x icasefs/warning &&
- test_i18ngrep "the following paths have collided" icasefs/warning
+ test_grep "the following paths have collided" icasefs/warning
'
test_expect_success 'clone with GIT_DEFAULT_HASH' '
@@ -698,7 +713,7 @@ test_expect_success 'partial clone: warn if server does not support object filte
git clone --filter=blob:limit=0 "file://$(pwd)/server" client 2> err &&
- test_i18ngrep "filtering not recognized by server" err
+ test_grep "filtering not recognized by server" err
'
test_expect_success 'batch missing blob request during checkout' '
@@ -722,7 +737,11 @@ test_expect_success 'batch missing blob request during checkout' '
# Ensure that there is only one negotiation by checking that there is
# only "done" line sent. ("done" marks the end of negotiation.)
- GIT_TRACE_PACKET="$(pwd)/trace" git -C client checkout HEAD^ &&
+ GIT_TRACE_PACKET="$(pwd)/trace" \
+ GIT_TRACE2_EVENT="$(pwd)/trace2_event" \
+ git -C client -c trace2.eventNesting=5 checkout HEAD^ &&
+ grep \"key\":\"total_rounds\",\"value\":\"1\" trace2_event >trace_lines &&
+ test_line_count = 1 trace_lines &&
grep "fetch> done" trace >done_lines &&
test_line_count = 1 done_lines
'
@@ -742,6 +761,7 @@ test_expect_success 'batch missing blob request does not inadvertently try to fe
echo aa >server/a &&
echo bb >server/b &&
# Also add a gitlink pointing to an arbitrary repository
+ test_config_global protocol.file.allow always &&
git -C server submodule add "$(pwd)/repo_for_submodule" c &&
git -C server add a b c &&
git -C server commit -m x &&
@@ -756,6 +776,18 @@ test_expect_success 'batch missing blob request does not inadvertently try to fe
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
+test_expect_success 'clone with includeIf' '
+ test_when_finished "rm -rf repo \"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\"" &&
+ git clone --bare --no-local src "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+
+ test_when_finished "rm \"$HOME\"/.gitconfig" &&
+ cat >"$HOME"/.gitconfig <<-EOF &&
+ [includeIf "onbranch:something"]
+ path = /does/not/exist.inc
+ EOF
+ git clone $HTTPD_URL/smart/repo.git repo
+'
+
test_expect_success 'partial clone using HTTP' '
partial_clone "$HTTPD_DOCUMENT_ROOT_PATH/server" "$HTTPD_URL/smart/server"
'
@@ -764,11 +796,116 @@ test_expect_success 'reject cloning shallow repository using HTTP' '
test_when_finished "rm -rf repo" &&
git clone --bare --no-local --depth=1 src "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
test_must_fail git -c protocol.version=2 clone --reject-shallow $HTTPD_URL/smart/repo.git repo 2>err &&
- test_i18ngrep -e "source repository is shallow, reject to clone." err &&
+ test_grep -e "source repository is shallow, reject to clone." err &&
git clone --no-reject-shallow $HTTPD_URL/smart/repo.git repo
'
+test_expect_success 'auto-discover bundle URI from HTTP clone' '
+ test_when_finished rm -rf trace.txt repo2 "$HTTPD_DOCUMENT_ROOT_PATH/repo2.git" &&
+ git -C src bundle create "$HTTPD_DOCUMENT_ROOT_PATH/everything.bundle" --all &&
+ git clone --bare --no-local src "$HTTPD_DOCUMENT_ROOT_PATH/repo2.git" &&
+
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo2.git" config \
+ uploadpack.advertiseBundleURIs true &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo2.git" config \
+ bundle.version 1 &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo2.git" config \
+ bundle.mode all &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo2.git" config \
+ bundle.everything.uri "$HTTPD_URL/everything.bundle" &&
+
+ GIT_TRACE2_EVENT="$(pwd)/trace.txt" \
+ git -c protocol.version=2 \
+ -c transfer.bundleURI=true clone \
+ $HTTPD_URL/smart/repo2.git repo2 &&
+ cat >pattern <<-EOF &&
+ "event":"child_start".*"argv":\["git-remote-https","$HTTPD_URL/everything.bundle"\]
+ EOF
+ grep -f pattern trace.txt
+'
+
+test_expect_success 'auto-discover multiple bundles from HTTP clone' '
+ test_when_finished rm -rf trace.txt repo3 "$HTTPD_DOCUMENT_ROOT_PATH/repo3.git" &&
+
+ test_commit -C src new &&
+ git -C src bundle create "$HTTPD_DOCUMENT_ROOT_PATH/new.bundle" HEAD~1..HEAD &&
+ git clone --bare --no-local src "$HTTPD_DOCUMENT_ROOT_PATH/repo3.git" &&
+
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo3.git" config \
+ uploadpack.advertiseBundleURIs true &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo3.git" config \
+ bundle.version 1 &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo3.git" config \
+ bundle.mode all &&
+
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo3.git" config \
+ bundle.everything.uri "$HTTPD_URL/everything.bundle" &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo3.git" config \
+ bundle.new.uri "$HTTPD_URL/new.bundle" &&
+
+ GIT_TRACE2_EVENT="$(pwd)/trace.txt" \
+ git -c protocol.version=2 \
+ -c transfer.bundleURI=true clone \
+ $HTTPD_URL/smart/repo3.git repo3 &&
+
+ # We should fetch _both_ bundles
+ cat >pattern <<-EOF &&
+ "event":"child_start".*"argv":\["git-remote-https","$HTTPD_URL/everything.bundle"\]
+ EOF
+ grep -f pattern trace.txt &&
+ cat >pattern <<-EOF &&
+ "event":"child_start".*"argv":\["git-remote-https","$HTTPD_URL/new.bundle"\]
+ EOF
+ grep -f pattern trace.txt
+'
+
+test_expect_success 'auto-discover multiple bundles from HTTP clone: creationToken heuristic' '
+ test_when_finished rm -rf "$HTTPD_DOCUMENT_ROOT_PATH/repo4.git" &&
+ test_when_finished rm -rf clone-heuristic trace*.txt &&
+
+ test_commit -C src newest &&
+ git -C src bundle create "$HTTPD_DOCUMENT_ROOT_PATH/newest.bundle" HEAD~1..HEAD &&
+ git clone --bare --no-local src "$HTTPD_DOCUMENT_ROOT_PATH/repo4.git" &&
+
+ cat >>"$HTTPD_DOCUMENT_ROOT_PATH/repo4.git/config" <<-EOF &&
+ [uploadPack]
+ advertiseBundleURIs = true
+
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+
+ [bundle "everything"]
+ uri = $HTTPD_URL/everything.bundle
+ creationtoken = 1
+
+ [bundle "new"]
+ uri = $HTTPD_URL/new.bundle
+ creationtoken = 2
+
+ [bundle "newest"]
+ uri = $HTTPD_URL/newest.bundle
+ creationtoken = 3
+ EOF
+
+ GIT_TRACE2_EVENT="$(pwd)/trace-clone.txt" \
+ git -c protocol.version=2 \
+ -c transfer.bundleURI=true clone \
+ "$HTTPD_URL/smart/repo4.git" clone-heuristic &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/newest.bundle
+ $HTTPD_URL/new.bundle
+ $HTTPD_URL/everything.bundle
+ EOF
+
+ # We should fetch all bundles in the expected order.
+ test_remote_https_urls <trace-clone.txt >actual &&
+ test_cmp expect actual
+'
+
# DO NOT add non-httpd-specific tests here, because the last part of this
# test script is only executed when httpd is available and enabled.
diff --git a/t/t5602-clone-remote-exec.sh b/t/t5602-clone-remote-exec.sh
index cbcceab..56329aa 100755
--- a/t/t5602-clone-remote-exec.sh
+++ b/t/t5602-clone-remote-exec.sh
@@ -2,6 +2,7 @@
test_description=clone
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t5603-clone-dirname.sh b/t/t5603-clone-dirname.sh
index 13b5e5e..8ca1f09 100755
--- a/t/t5603-clone-dirname.sh
+++ b/t/t5603-clone-dirname.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='check output directory names used by git-clone'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# we use a fake ssh wrapper that ignores the arguments
diff --git a/t/t5604-clone-reference.sh b/t/t5604-clone-reference.sh
index 24340e6..9b32db8 100755
--- a/t/t5604-clone-reference.sh
+++ b/t/t5604-clone-reference.sh
@@ -7,6 +7,7 @@ test_description='test clone --reference'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
base_dir=$(pwd)
@@ -303,8 +304,6 @@ test_expect_success SYMLINKS 'setup repo with manually symlinked or unknown file
ln -s ../an-object $obj &&
cd ../ &&
- find . -type f | sort >../../../T.objects-files.raw &&
- find . -type l | sort >../../../T.objects-symlinks.raw &&
echo unknown_content >unknown_file
) &&
git -C T fsck &&
@@ -313,19 +312,27 @@ test_expect_success SYMLINKS 'setup repo with manually symlinked or unknown file
test_expect_success SYMLINKS 'clone repo with symlinked or unknown files at objects/' '
- for option in --local --no-hardlinks --shared --dissociate
+ # None of these options work when cloning locally, since T has
+ # symlinks in its `$GIT_DIR/objects` directory
+ for option in --local --no-hardlinks --dissociate
do
- git clone $option T T$option || return 1 &&
- git -C T$option fsck || return 1 &&
- git -C T$option rev-list --all --objects >T$option.objects &&
- test_cmp T.objects T$option.objects &&
- (
- cd T$option/.git/objects &&
- find . -type f | sort >../../../T$option.objects-files.raw &&
- find . -type l | sort >../../../T$option.objects-symlinks.raw
- )
+ test_must_fail git clone $option T T$option 2>err || return 1 &&
+ test_grep "symlink.*exists" err || return 1
done &&
+ # But `--shared` clones should still work, even when specifying
+ # a local path *and* that repository has symlinks present in its
+ # `$GIT_DIR/objects` directory.
+ git clone --shared T T--shared &&
+ git -C T--shared fsck &&
+ git -C T--shared rev-list --all --objects >T--shared.objects &&
+ test_cmp T.objects T--shared.objects &&
+ (
+ cd T--shared/.git/objects &&
+ find . -type f | sort >../../../T--shared.objects-files.raw &&
+ find . -type l | sort >../../../T--shared.objects-symlinks.raw
+ ) &&
+
for raw in $(ls T*.raw)
do
sed -e "s!/../!/Y/!; s![0-9a-f]\{38,\}!Z!" -e "/commit-graph/d" \
@@ -333,29 +340,25 @@ test_expect_success SYMLINKS 'clone repo with symlinked or unknown files at obje
sort $raw.de-sha-1 >$raw.de-sha || return 1
done &&
- cat >expected-files <<-EOF &&
- ./Y/Z
- ./Y/Z
- ./Y/Z
- ./a-loose-dir/Z
- ./an-object
- ./info/packs
- ./pack/pack-Z.idx
- ./pack/pack-Z.pack
- ./packs/pack-Z.idx
- ./packs/pack-Z.pack
- ./unknown_file
- EOF
-
- for option in --local --no-hardlinks --dissociate
- do
- test_cmp expected-files T$option.objects-files.raw.de-sha || return 1 &&
- test_must_be_empty T$option.objects-symlinks.raw.de-sha || return 1
- done &&
-
echo ./info/alternates >expected-files &&
test_cmp expected-files T--shared.objects-files.raw &&
test_must_be_empty T--shared.objects-symlinks.raw
'
+test_expect_success SYMLINKS 'clone repo with symlinked objects directory' '
+ test_when_finished "rm -fr sensitive malicious" &&
+
+ mkdir -p sensitive &&
+ echo "secret" >sensitive/file &&
+
+ git init malicious &&
+ rm -fr malicious/.git/objects &&
+ ln -s "$(pwd)/sensitive" ./malicious/.git/objects &&
+
+ test_must_fail git clone --local malicious clone 2>err &&
+
+ test_path_is_missing clone &&
+ grep "is a symlink, refusing to clone with --local" err
+'
+
test_done
diff --git a/t/t5605-clone-local.sh b/t/t5605-clone-local.sh
index 7d63365..a305586 100755
--- a/t/t5605-clone-local.sh
+++ b/t/t5605-clone-local.sh
@@ -15,19 +15,27 @@ test_expect_success 'preparing origin repository' '
: >file && git add . && git commit -m1 &&
git clone --bare . a.git &&
git clone --bare . x &&
- test "$(cd a.git && git config --bool core.bare)" = true &&
- test "$(cd x && git config --bool core.bare)" = true &&
+ echo true >expect &&
+ git -C a.git config --bool core.bare >actual &&
+ test_cmp expect actual &&
+ echo true >expect &&
+ git -C x config --bool core.bare >actual &&
+ test_cmp expect actual &&
git bundle create b1.bundle --all &&
git bundle create b2.bundle main &&
mkdir dir &&
cp b1.bundle dir/b3 &&
- cp b1.bundle b4
+ cp b1.bundle b4 &&
+ git branch not-main main &&
+ git bundle create b5.bundle not-main
'
test_expect_success 'local clone without .git suffix' '
git clone -l -s a b &&
(cd b &&
- test "$(git config --bool core.bare)" = false &&
+ echo false >expect &&
+ git config --bool core.bare >actual &&
+ test_cmp expect actual &&
git fetch)
'
@@ -57,11 +65,11 @@ test_expect_success 'Even without -l, local will make a hardlink' '
'
test_expect_success 'local clone of repo with nonexistent ref in HEAD' '
- echo "ref: refs/heads/nonexistent" > a.git/HEAD &&
+ git -C a.git symbolic-ref HEAD refs/heads/nonexistent &&
git clone a d &&
(cd d &&
git fetch &&
- test ! -e .git/refs/remotes/origin/HEAD)
+ test_ref_missing refs/remotes/origin/HEAD)
'
test_expect_success 'bundle clone without .bundle suffix' '
@@ -83,11 +91,19 @@ test_expect_success 'bundle clone from b4.bundle that does not exist' '
test_must_fail git clone b4.bundle bb
'
-test_expect_success 'bundle clone with nonexistent HEAD' '
+test_expect_success 'bundle clone with nonexistent HEAD (match default)' '
git clone b2.bundle b2 &&
(cd b2 &&
git fetch &&
- test_must_fail git rev-parse --verify refs/heads/main)
+ git rev-parse --verify refs/heads/main)
+'
+
+test_expect_success 'bundle clone with nonexistent HEAD (no match default)' '
+ git clone b5.bundle b5 &&
+ (cd b5 &&
+ git fetch &&
+ test_must_fail git rev-parse --verify refs/heads/main &&
+ test_must_fail git rev-parse --verify refs/heads/not-main)
'
test_expect_success 'clone empty repository' '
@@ -141,4 +157,13 @@ test_expect_success 'cloning locally respects "-u" for fetching refs' '
test_must_fail git clone --bare -u false a should_not_work.git
'
+test_expect_success REFFILES 'local clone from repo with corrupt refs fails gracefully' '
+ git init corrupt &&
+ test_commit -C corrupt one &&
+ echo a >corrupt/.git/refs/heads/topic &&
+
+ test_must_fail git clone corrupt working 2>err &&
+ grep "has a null OID" err
+'
+
test_done
diff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh
index d822153..e93e0d0 100755
--- a/t/t5606-clone-options.sh
+++ b/t/t5606-clone-options.sh
@@ -4,6 +4,7 @@ test_description='basic clone options'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -38,15 +39,16 @@ test_expect_success 'clone -o' '
test_expect_success 'rejects invalid -o/--origin' '
test_must_fail git clone -o "bad...name" parent clone-bad-name 2>err &&
- test_i18ngrep "'\''bad...name'\'' is not a valid remote name" err
+ test_grep "'\''bad...name'\'' is not a valid remote name" err
'
-test_expect_success 'disallows --bare with --origin' '
+test_expect_success 'clone --bare -o' '
- test_must_fail git clone -o foo --bare parent clone-bare-o 2>err &&
- test_debug "cat err" &&
- test_i18ngrep -e "--bare and --origin foo options are incompatible" err
+ git clone -o foo --bare parent clone-bare-o &&
+ (cd parent && pwd) >expect &&
+ git -C clone-bare-o config remote.foo.url >actual &&
+ test_cmp expect actual
'
@@ -54,14 +56,22 @@ test_expect_success 'disallows --bare with --separate-git-dir' '
test_must_fail git clone --bare --separate-git-dir dot-git-destiation parent clone-bare-sgd 2>err &&
test_debug "cat err" &&
- test_i18ngrep -e "--bare and --separate-git-dir are incompatible" err
+ test_grep -e "options .--bare. and .--separate-git-dir. cannot be used together" err
+
+'
+test_expect_success 'disallows --bundle-uri with shallow options' '
+ for option in --depth=1 --shallow-since=01-01-2000 --shallow-exclude=HEAD
+ do
+ test_must_fail git clone --bundle-uri=bundle $option from to 2>err &&
+ grep "bundle-uri.* cannot be used together" err || return 1
+ done
'
test_expect_success 'reject cloning shallow repository' '
test_when_finished "rm -rf repo" &&
test_must_fail git clone --reject-shallow shallow-repo out 2>err &&
- test_i18ngrep -e "source repository is shallow, reject to clone." err &&
+ test_grep -e "source repository is shallow, reject to clone." err &&
git clone --no-reject-shallow shallow-repo repo
'
@@ -69,7 +79,7 @@ test_expect_success 'reject cloning shallow repository' '
test_expect_success 'reject cloning non-local shallow repository' '
test_when_finished "rm -rf repo" &&
test_must_fail git clone --reject-shallow --no-local shallow-repo out 2>err &&
- test_i18ngrep -e "source repository is shallow, reject to clone." err &&
+ test_grep -e "source repository is shallow, reject to clone." err &&
git clone --no-reject-shallow --no-local shallow-repo repo
'
@@ -110,6 +120,16 @@ test_expect_success 'prefers -c config over --template config' '
'
+test_expect_success 'ignore --template config for core.bare' '
+
+ template="$TRASH_DIRECTORY/template-with-bare-config" &&
+ mkdir "$template" &&
+ git config --file "$template/config" core.bare true &&
+ git clone "--template=$template" parent clone-bare-config &&
+ test "$(git -C clone-bare-config config --local core.bare)" = "false" &&
+ test_path_is_missing clone-bare-config/HEAD
+'
+
test_expect_success 'prefers config "clone.defaultRemoteName" over default' '
test_config_global clone.defaultRemoteName from_config &&
@@ -129,7 +149,7 @@ test_expect_success 'redirected clone does not show progress' '
git clone "file://$(pwd)/parent" clone-redirected >out 2>err &&
! grep % err &&
- test_i18ngrep ! "Checking connectivity" err
+ test_grep ! "Checking connectivity" err
'
diff --git a/t/t5607-clone-bundle.sh b/t/t5607-clone-bundle.sh
index 51705aa..0d1e92d 100755
--- a/t/t5607-clone-bundle.sh
+++ b/t/t5607-clone-bundle.sh
@@ -24,7 +24,7 @@ test_expect_success 'setup' '
test_expect_success '"verify" needs a worktree' '
git bundle create tip.bundle -1 main &&
nongit test_must_fail git bundle verify ../tip.bundle 2>err &&
- test_i18ngrep "need a repository" err
+ test_grep "need a repository" err
'
test_expect_success 'annotated tags can be excluded by rev-list options' '
@@ -166,7 +166,7 @@ test_expect_success 'git bundle v3 rejects unknown capabilities' '
@unknown=silly
EOF
test_must_fail git bundle verify new 2>output &&
- test_i18ngrep "unknown capability .unknown=silly." output
+ test_grep "unknown capability .unknown=silly." output
'
test_done
diff --git a/t/t5609-clone-branch.sh b/t/t5609-clone-branch.sh
index f86a674..252e1f7 100755
--- a/t/t5609-clone-branch.sh
+++ b/t/t5609-clone-branch.sh
@@ -4,6 +4,7 @@ test_description='clone --branch option'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
check_HEAD() {
diff --git a/t/t5610-clone-detached.sh b/t/t5610-clone-detached.sh
index a7ec21e..022ed3d 100755
--- a/t/t5610-clone-detached.sh
+++ b/t/t5610-clone-detached.sh
@@ -4,6 +4,7 @@ test_description='test cloning a repository with detached HEAD'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
head_is_detached() {
diff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh
index f8625f9..298d4be 100755
--- a/t/t5611-clone-config.sh
+++ b/t/t5611-clone-config.sh
@@ -4,6 +4,7 @@ test_description='tests for git clone -c key=value'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'clone -c sets config in cloned repo' '
@@ -17,7 +18,7 @@ test_expect_success 'clone -c sets config in cloned repo' '
test_expect_success 'clone -c can set multi-keys' '
rm -rf child &&
git clone -c core.foo=bar -c core.foo=baz . child &&
- { echo bar; echo baz; } >expect &&
+ test_write_lines bar baz >expect &&
git --git-dir=child/.git config --get-all core.foo >actual &&
test_cmp expect actual
'
@@ -102,7 +103,7 @@ test_expect_success 'set up shallow repository' '
test_expect_success 'clone.rejectshallow=true should reject cloning shallow repo' '
test_when_finished "rm -rf out" &&
test_must_fail git -c clone.rejectshallow=true clone --no-local shallow-repo out 2>err &&
- test_i18ngrep -e "source repository is shallow, reject to clone." err &&
+ test_grep -e "source repository is shallow, reject to clone." err &&
git -c clone.rejectshallow=false clone --no-local shallow-repo out
'
@@ -110,7 +111,7 @@ test_expect_success 'clone.rejectshallow=true should reject cloning shallow repo
test_expect_success 'option --[no-]reject-shallow override clone.rejectshallow config' '
test_when_finished "rm -rf out" &&
test_must_fail git -c clone.rejectshallow=false clone --reject-shallow --no-local shallow-repo out 2>err &&
- test_i18ngrep -e "source repository is shallow, reject to clone." err &&
+ test_grep -e "source repository is shallow, reject to clone." err &&
git -c clone.rejectshallow=true clone --no-reject-shallow --no-local shallow-repo out
'
diff --git a/t/t5613-info-alternate.sh b/t/t5613-info-alternate.sh
index 895f46b..7708cba 100755
--- a/t/t5613-info-alternate.sh
+++ b/t/t5613-info-alternate.sh
@@ -4,6 +4,8 @@
#
test_description='test transitive info/alternate entries'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'preparing first repository' '
diff --git a/t/t5614-clone-submodules-shallow.sh b/t/t5614-clone-submodules-shallow.sh
index 5504b51..c2a2bb4 100755
--- a/t/t5614-clone-submodules-shallow.sh
+++ b/t/t5614-clone-submodules-shallow.sh
@@ -2,6 +2,7 @@
test_description='Test shallow cloning of repos with submodules'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
pwd=$(pwd)
@@ -24,6 +25,7 @@ test_expect_success 'setup' '
test_expect_success 'nonshallow clone implies nonshallow submodule' '
test_when_finished "rm -rf super_clone" &&
+ test_config_global protocol.file.allow always &&
git clone --recurse-submodules "file://$pwd/." super_clone &&
git -C super_clone log --oneline >lines &&
test_line_count = 3 lines &&
@@ -33,6 +35,7 @@ test_expect_success 'nonshallow clone implies nonshallow submodule' '
test_expect_success 'shallow clone with shallow submodule' '
test_when_finished "rm -rf super_clone" &&
+ test_config_global protocol.file.allow always &&
git clone --recurse-submodules --depth 2 --shallow-submodules "file://$pwd/." super_clone &&
git -C super_clone log --oneline >lines &&
test_line_count = 2 lines &&
@@ -42,6 +45,7 @@ test_expect_success 'shallow clone with shallow submodule' '
test_expect_success 'shallow clone does not imply shallow submodule' '
test_when_finished "rm -rf super_clone" &&
+ test_config_global protocol.file.allow always &&
git clone --recurse-submodules --depth 2 "file://$pwd/." super_clone &&
git -C super_clone log --oneline >lines &&
test_line_count = 2 lines &&
@@ -51,6 +55,7 @@ test_expect_success 'shallow clone does not imply shallow submodule' '
test_expect_success 'shallow clone with non shallow submodule' '
test_when_finished "rm -rf super_clone" &&
+ test_config_global protocol.file.allow always &&
git clone --recurse-submodules --depth 2 --no-shallow-submodules "file://$pwd/." super_clone &&
git -C super_clone log --oneline >lines &&
test_line_count = 2 lines &&
@@ -60,6 +65,7 @@ test_expect_success 'shallow clone with non shallow submodule' '
test_expect_success 'non shallow clone with shallow submodule' '
test_when_finished "rm -rf super_clone" &&
+ test_config_global protocol.file.allow always &&
git clone --recurse-submodules --no-local --shallow-submodules "file://$pwd/." super_clone &&
git -C super_clone log --oneline >lines &&
test_line_count = 3 lines &&
@@ -69,6 +75,7 @@ test_expect_success 'non shallow clone with shallow submodule' '
test_expect_success 'clone follows shallow recommendation' '
test_when_finished "rm -rf super_clone" &&
+ test_config_global protocol.file.allow always &&
git config -f .gitmodules submodule.sub.shallow true &&
git add .gitmodules &&
git commit -m "recommend shallow for sub" &&
@@ -87,6 +94,7 @@ test_expect_success 'clone follows shallow recommendation' '
test_expect_success 'get unshallow recommended shallow submodule' '
test_when_finished "rm -rf super_clone" &&
+ test_config_global protocol.file.allow always &&
git clone --no-local "file://$pwd/." super_clone &&
(
cd super_clone &&
@@ -103,6 +111,7 @@ test_expect_success 'get unshallow recommended shallow submodule' '
test_expect_success 'clone follows non shallow recommendation' '
test_when_finished "rm -rf super_clone" &&
+ test_config_global protocol.file.allow always &&
git config -f .gitmodules submodule.sub.shallow false &&
git add .gitmodules &&
git commit -m "recommend non shallow for sub" &&
diff --git a/t/t5615-alternate-env.sh b/t/t5615-alternate-env.sh
index b4905b8..83513e4 100755
--- a/t/t5615-alternate-env.sh
+++ b/t/t5615-alternate-env.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='handling of alternates in environment variables'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
check_obj () {
diff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh
index cf3e82b..2da7291 100755
--- a/t/t5616-partial-clone.sh
+++ b/t/t5616-partial-clone.sh
@@ -16,10 +16,10 @@ test_expect_success 'setup normal src repo' '
git init src &&
for n in 1 2 3 4
do
- echo "This is file: $n" > src/file.$n.txt
- git -C src add file.$n.txt
- git -C src commit -m "file $n"
- git -C src ls-files -s file.$n.txt >>temp
+ echo "This is file: $n" > src/file.$n.txt &&
+ git -C src add file.$n.txt &&
+ git -C src commit -m "file $n" &&
+ git -C src ls-files -s file.$n.txt >>temp || return 1
done &&
awk -f print_2.awk <temp | sort >expect_1.oids &&
test_line_count = 4 expect_1.oids
@@ -49,6 +49,13 @@ test_expect_success 'do partial clone 1' '
test "$(git -C pc1 config --local remote.origin.partialclonefilter)" = "blob:none"
'
+test_expect_success 'rev-list --missing=allow-promisor on partial clone' '
+ git -C pc1 rev-list --objects --missing=allow-promisor HEAD >actual &&
+ git -C pc1 rev-list --objects --missing=print HEAD >expect.raw &&
+ grep -v "^?" expect.raw >expect &&
+ test_cmp expect actual
+'
+
test_expect_success 'verify that .promisor file contains refs fetched' '
ls pc1/.git/objects/pack/pack-*.promisor >promisorlist &&
test_line_count = 1 promisorlist &&
@@ -72,9 +79,9 @@ test_expect_success 'push new commits to server' '
git -C src remote add srv "file://$(pwd)/srv.bare" &&
for x in a b c d e
do
- echo "Mod file.1.txt $x" >>src/file.1.txt
- git -C src add file.1.txt
- git -C src commit -m "mod $x"
+ echo "Mod file.1.txt $x" >>src/file.1.txt &&
+ git -C src add file.1.txt &&
+ git -C src commit -m "mod $x" || return 1
done &&
git -C src blame main -- file.1.txt >expect.blame &&
git -C src push -u srv main
@@ -114,9 +121,9 @@ test_expect_success 'verify blame causes dynamic object fetch' '
test_expect_success 'push new commits to server for file.2.txt' '
for x in a b c d e f
do
- echo "Mod file.2.txt $x" >>src/file.2.txt
- git -C src add file.2.txt
- git -C src commit -m "mod $x"
+ echo "Mod file.2.txt $x" >>src/file.2.txt &&
+ git -C src add file.2.txt &&
+ git -C src commit -m "mod $x" || return 1
done &&
git -C src push -u srv main
'
@@ -135,9 +142,9 @@ test_expect_success 'override inherited filter-spec using --no-filter' '
test_expect_success 'push new commits to server for file.3.txt' '
for x in a b c d e f
do
- echo "Mod file.3.txt $x" >>src/file.3.txt
- git -C src add file.3.txt
- git -C src commit -m "mod $x"
+ echo "Mod file.3.txt $x" >>src/file.3.txt &&
+ git -C src add file.3.txt &&
+ git -C src commit -m "mod $x" || return 1
done &&
git -C src push -u srv main
'
@@ -166,13 +173,94 @@ test_expect_success 'manual prefetch of missing objects' '
test_line_count = 0 observed.oids
'
+# create new commits in "src" repo to establish a history on file.4.txt
+# and push to "srv.bare".
+test_expect_success 'push new commits to server for file.4.txt' '
+ for x in a b c d e f
+ do
+ echo "Mod file.4.txt $x" >src/file.4.txt &&
+ if list_contains "a,b" "$x"; then
+ printf "%10000s" X >>src/file.4.txt
+ fi &&
+ if list_contains "c,d" "$x"; then
+ printf "%20000s" X >>src/file.4.txt
+ fi &&
+ git -C src add file.4.txt &&
+ git -C src commit -m "mod $x" || return 1
+ done &&
+ git -C src push -u srv main
+'
+
+# Do partial fetch to fetch smaller files; then verify that without --refetch
+# applying a new filter does not refetch missing large objects. Then use
+# --refetch to apply the new filter on existing commits. Test it under both
+# protocol v2 & v0.
+test_expect_success 'apply a different filter using --refetch' '
+ git -C pc1 fetch --filter=blob:limit=999 origin &&
+ git -C pc1 rev-list --quiet --objects --missing=print \
+ main..origin/main >observed &&
+ test_line_count = 4 observed &&
+
+ git -C pc1 fetch --filter=blob:limit=19999 --refetch origin &&
+ git -C pc1 rev-list --quiet --objects --missing=print \
+ main..origin/main >observed &&
+ test_line_count = 2 observed &&
+
+ git -c protocol.version=0 -C pc1 fetch --filter=blob:limit=29999 \
+ --refetch origin &&
+ git -C pc1 rev-list --quiet --objects --missing=print \
+ main..origin/main >observed &&
+ test_line_count = 0 observed
+'
+
+test_expect_success 'fetch --refetch works with a shallow clone' '
+ git clone --no-checkout --depth=1 --filter=blob:none "file://$(pwd)/srv.bare" pc1s &&
+ git -C pc1s rev-list --objects --missing=print HEAD >observed &&
+ test_line_count = 6 observed &&
+
+ GIT_TRACE=1 git -C pc1s fetch --filter=blob:limit=999 --refetch origin &&
+ git -C pc1s rev-list --objects --missing=print HEAD >observed &&
+ test_line_count = 6 observed
+'
+
+test_expect_success 'fetch --refetch triggers repacking' '
+ GIT_TRACE2_CONFIG_PARAMS=gc.autoPackLimit,maintenance.incremental-repack.auto &&
+ export GIT_TRACE2_CONFIG_PARAMS &&
+
+ GIT_TRACE2_EVENT="$PWD/trace1.event" \
+ git -C pc1 fetch --refetch origin &&
+ test_subcommand git maintenance run --auto --no-quiet <trace1.event &&
+ grep \"param\":\"gc.autopacklimit\",\"value\":\"1\" trace1.event &&
+ grep \"param\":\"maintenance.incremental-repack.auto\",\"value\":\"-1\" trace1.event &&
+
+ GIT_TRACE2_EVENT="$PWD/trace2.event" \
+ git -c protocol.version=0 \
+ -c gc.autoPackLimit=0 \
+ -c maintenance.incremental-repack.auto=1234 \
+ -C pc1 fetch --refetch origin &&
+ test_subcommand git maintenance run --auto --no-quiet <trace2.event &&
+ grep \"param\":\"gc.autopacklimit\",\"value\":\"0\" trace2.event &&
+ grep \"param\":\"maintenance.incremental-repack.auto\",\"value\":\"-1\" trace2.event &&
+
+ GIT_TRACE2_EVENT="$PWD/trace3.event" \
+ git -c protocol.version=0 \
+ -c gc.autoPackLimit=1234 \
+ -c maintenance.incremental-repack.auto=0 \
+ -C pc1 fetch --refetch origin &&
+ test_subcommand git maintenance run --auto --no-quiet <trace3.event &&
+ grep \"param\":\"gc.autopacklimit\",\"value\":\"1\" trace3.event &&
+ grep \"param\":\"maintenance.incremental-repack.auto\",\"value\":\"0\" trace3.event
+'
+
test_expect_success 'partial clone with transfer.fsckobjects=1 works with submodules' '
test_create_repo submodule &&
test_commit -C submodule mycommit &&
test_create_repo src_with_sub &&
- test_config -C src_with_sub uploadpack.allowfilter 1 &&
- test_config -C src_with_sub uploadpack.allowanysha1inwant 1 &&
+ git -C src_with_sub config uploadpack.allowfilter 1 &&
+ git -C src_with_sub config uploadpack.allowanysha1inwant 1 &&
+
+ test_config_global protocol.file.allow always &&
git -C src_with_sub submodule add "file://$(pwd)/submodule" mysub &&
git -C src_with_sub commit -m "commit with submodule" &&
@@ -182,6 +270,12 @@ test_expect_success 'partial clone with transfer.fsckobjects=1 works with submod
test_when_finished rm -rf dst
'
+test_expect_success 'lazily fetched .gitmodules works' '
+ git clone --filter="blob:none" --no-checkout "file://$(pwd)/src_with_sub" dst &&
+ git -C dst fetch &&
+ test_when_finished rm -rf dst
+'
+
test_expect_success 'partial clone with transfer.fsckobjects=1 uses index-pack --fsck-objects' '
git init src &&
test_commit -C src x &&
@@ -225,7 +319,7 @@ test_expect_success 'use fsck before and after manually fetching a missing subtr
# Auto-fetch all remaining trees and blobs with --missing=error
git -C dst rev-list --missing=error --objects main >fetched_objects &&
- test_line_count = 70 fetched_objects &&
+ test_line_count = 88 fetched_objects &&
awk -f print_1.awk fetched_objects |
xargs -n1 git -C dst cat-file -t >fetched_types &&
@@ -259,14 +353,14 @@ test_expect_success 'upload-pack complains of bogus filter config' '
test_must_fail git \
-c uploadpackfilter.tree.maxdepth \
upload-pack . >/dev/null 2>err &&
- test_i18ngrep "unable to parse.*tree.maxdepth" err
+ test_grep "unable to parse.*tree.maxdepth" err
'
test_expect_success 'upload-pack fails banned object filters' '
test_config -C srv.bare uploadpackfilter.blob:none.allow false &&
test_must_fail ok=sigpipe git clone --no-checkout --filter=blob:none \
"file://$(pwd)/srv.bare" pc3 2>err &&
- test_i18ngrep "filter '\''blob:none'\'' not supported" err
+ test_grep "filter '\''blob:none'\'' not supported" err
'
test_expect_success 'upload-pack fails banned combine object filters' '
@@ -276,14 +370,14 @@ test_expect_success 'upload-pack fails banned combine object filters' '
test_config -C srv.bare uploadpackfilter.blob:none.allow false &&
test_must_fail ok=sigpipe git clone --no-checkout --filter=tree:1 \
--filter=blob:none "file://$(pwd)/srv.bare" pc3 2>err &&
- test_i18ngrep "filter '\''blob:none'\'' not supported" err
+ test_grep "filter '\''blob:none'\'' not supported" err
'
test_expect_success 'upload-pack fails banned object filters with fallback' '
test_config -C srv.bare uploadpackfilter.allow false &&
test_must_fail ok=sigpipe git clone --no-checkout --filter=blob:none \
"file://$(pwd)/srv.bare" pc3 2>err &&
- test_i18ngrep "filter '\''blob:none'\'' not supported" err
+ test_grep "filter '\''blob:none'\'' not supported" err
'
test_expect_success 'upload-pack limits tree depth filters' '
@@ -292,7 +386,7 @@ test_expect_success 'upload-pack limits tree depth filters' '
test_config -C srv.bare uploadpackfilter.tree.maxDepth 0 &&
test_must_fail ok=sigpipe git clone --no-checkout --filter=tree:1 \
"file://$(pwd)/srv.bare" pc3 2>err &&
- test_i18ngrep "tree filter allows max depth 0, but got 1" err &&
+ test_grep "tree filter allows max depth 0, but got 1" err &&
git clone --no-checkout --filter=tree:0 "file://$(pwd)/srv.bare" pc4 &&
@@ -300,7 +394,7 @@ test_expect_success 'upload-pack limits tree depth filters' '
git clone --no-checkout --filter=tree:5 "file://$(pwd)/srv.bare" pc5 &&
test_must_fail ok=sigpipe git clone --no-checkout --filter=tree:6 \
"file://$(pwd)/srv.bare" pc6 2>err &&
- test_i18ngrep "tree filter allows max depth 5, but got 6" err
+ test_grep "tree filter allows max depth 5, but got 6" err
'
test_expect_success 'partial clone fetches blobs pointed to by refs even if normally filtered out' '
@@ -365,11 +459,11 @@ test_expect_success 'partial clone with unresolvable sparse filter fails cleanly
test_must_fail git clone --no-local --bare \
--filter=sparse:oid=main:no-such-name \
sparse-src dst.git 2>err &&
- test_i18ngrep "unable to access sparse blob in .main:no-such-name" err &&
+ test_grep "unable to access sparse blob in .main:no-such-name" err &&
test_must_fail git clone --no-local --bare \
--filter=sparse:oid=main \
sparse-src dst.git 2>err &&
- test_i18ngrep "unable to parse sparse filter data in" err
+ test_grep "unable to parse sparse filter data in" err
'
setup_triangle () {
@@ -385,7 +479,7 @@ setup_triangle () {
for i in $(test_seq 1 100)
do
echo "make the tree big" >server/file$i &&
- git -C server add file$i
+ git -C server add file$i || return 1
done &&
git -C server commit -m "initial" &&
git clone --bare --filter=tree:0 "file://$(pwd)/server" client &&
@@ -399,8 +493,8 @@ setup_triangle () {
TREE_HASH=$(git -C server rev-parse HEAD~1^{tree}) &&
git -C promisor-remote fetch --keep "file://$(pwd)/server" "$TREE_HASH" &&
git -C promisor-remote count-objects -v >object-count &&
- test_i18ngrep "count: 0" object-count &&
- test_i18ngrep "in-pack: 2" object-count &&
+ test_grep "count: 0" object-count &&
+ test_grep "in-pack: 2" object-count &&
# Set it as the promisor remote of client. Thus, whenever
# the client lazy fetches, the lazy fetch will succeed only if it is
@@ -556,6 +650,49 @@ test_expect_success 'repack does not loosen promisor objects' '
grep "loosen_unused_packed_objects/loosened:0" trace
'
+test_expect_success 'lazy-fetch in submodule succeeds' '
+ # setup
+ test_config_global protocol.file.allow always &&
+
+ test_when_finished "rm -rf src-sub" &&
+ git init src-sub &&
+ git -C src-sub config uploadpack.allowfilter 1 &&
+ git -C src-sub config uploadpack.allowanysha1inwant 1 &&
+
+ # This blob must be missing in the subsequent commit.
+ echo foo >src-sub/file &&
+ git -C src-sub add file &&
+ git -C src-sub commit -m "submodule one" &&
+ SUB_ONE=$(git -C src-sub rev-parse HEAD) &&
+
+ echo bar >src-sub/file &&
+ git -C src-sub add file &&
+ git -C src-sub commit -m "submodule two" &&
+ SUB_TWO=$(git -C src-sub rev-parse HEAD) &&
+
+ test_when_finished "rm -rf src-super" &&
+ git init src-super &&
+ git -C src-super config uploadpack.allowfilter 1 &&
+ git -C src-super config uploadpack.allowanysha1inwant 1 &&
+ git -C src-super submodule add ../src-sub src-sub &&
+
+ git -C src-super/src-sub checkout $SUB_ONE &&
+ git -C src-super add src-sub &&
+ git -C src-super commit -m "superproject one" &&
+
+ git -C src-super/src-sub checkout $SUB_TWO &&
+ git -C src-super add src-sub &&
+ git -C src-super commit -m "superproject two" &&
+
+ # the fetch
+ test_when_finished "rm -rf client" &&
+ git clone --filter=blob:none --also-filter-submodules \
+ --recurse-submodules "file://$(pwd)/src-super" client &&
+
+ # Trigger lazy-fetch from the superproject
+ git -C client restore --recurse-submodules --source=HEAD^ :/
+'
+
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
@@ -611,7 +748,7 @@ test_expect_success 'upon cloning, check that all refs point to objects' '
test_must_fail git -c protocol.version=2 clone \
--filter=blob:none $HTTPD_URL/one_time_perl/server repo 2>err &&
- test_i18ngrep "did not send all necessary objects" err &&
+ test_grep "did not send all necessary objects" err &&
# Ensure that the one-time-perl script was used.
! test -e "$HTTPD_ROOT_PATH/one-time-perl"
@@ -669,7 +806,7 @@ test_expect_success 'tolerate server sending REF_DELTA against missing promisor
for i in $(test_seq 10)
do
echo "this is a line" >>"$SERVER/foo.txt" &&
- echo "this is another line" >>"$SERVER/have.txt"
+ echo "this is another line" >>"$SERVER/have.txt" || return 1
done &&
git -C "$SERVER" add foo.txt have.txt &&
git -C "$SERVER" commit -m bar &&
diff --git a/t/t5617-clone-submodules-remote.sh b/t/t5617-clone-submodules-remote.sh
index e2dbb4e..5a4d793 100755
--- a/t/t5617-clone-submodules-remote.sh
+++ b/t/t5617-clone-submodules-remote.sh
@@ -5,11 +5,13 @@ test_description='Test cloning repos with submodules using remote-tracking branc
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
pwd=$(pwd)
test_expect_success 'setup' '
+ git config --global protocol.file.allow always &&
git checkout -b main &&
test_commit commit1 &&
mkdir sub &&
@@ -28,6 +30,13 @@ test_expect_success 'setup' '
)
'
+# bare clone giving "srv.bare" for use as our server.
+test_expect_success 'setup bare clone for server' '
+ git clone --bare "file://$(pwd)/." srv.bare &&
+ git -C srv.bare config --local uploadpack.allowfilter 1 &&
+ git -C srv.bare config --local uploadpack.allowanysha1inwant 1
+'
+
test_expect_success 'clone with --no-remote-submodules' '
test_when_finished "rm -rf super_clone" &&
git clone --recurse-submodules --no-remote-submodules "file://$pwd/." super_clone &&
@@ -65,4 +74,38 @@ test_expect_success 'clone with --single-branch' '
)
'
+# do basic partial clone from "srv.bare"
+# confirm partial clone was registered in the local config for super and sub.
+test_expect_success 'clone with --filter' '
+ git clone --recurse-submodules \
+ --filter blob:none --also-filter-submodules \
+ "file://$pwd/srv.bare" super_clone &&
+ test_cmp_config -C super_clone true remote.origin.promisor &&
+ test_cmp_config -C super_clone blob:none remote.origin.partialclonefilter &&
+ test_cmp_config -C super_clone/sub true remote.origin.promisor &&
+ test_cmp_config -C super_clone/sub blob:none remote.origin.partialclonefilter
+'
+
+# check that clone.filterSubmodules works (--also-filter-submodules can be
+# omitted)
+test_expect_success 'filters applied with clone.filterSubmodules' '
+ test_config_global clone.filterSubmodules true &&
+ git clone --recurse-submodules --filter blob:none \
+ "file://$pwd/srv.bare" super_clone2 &&
+ test_cmp_config -C super_clone2 true remote.origin.promisor &&
+ test_cmp_config -C super_clone2 blob:none remote.origin.partialclonefilter &&
+ test_cmp_config -C super_clone2/sub true remote.origin.promisor &&
+ test_cmp_config -C super_clone2/sub blob:none remote.origin.partialclonefilter
+'
+
+test_expect_success '--no-also-filter-submodules overrides clone.filterSubmodules=true' '
+ test_config_global clone.filterSubmodules true &&
+ git clone --recurse-submodules --filter blob:none \
+ --no-also-filter-submodules \
+ "file://$pwd/srv.bare" super_clone3 &&
+ test_cmp_config -C super_clone3 true remote.origin.promisor &&
+ test_cmp_config -C super_clone3 blob:none remote.origin.partialclonefilter &&
+ test_cmp_config -C super_clone3/sub false --default false remote.origin.promisor
+'
+
test_done
diff --git a/t/t5618-alternate-refs.sh b/t/t5618-alternate-refs.sh
index 3353216..f905db0 100755
--- a/t/t5618-alternate-refs.sh
+++ b/t/t5618-alternate-refs.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test handling of --alternate-refs traversal'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Avoid test_commit because we want a specific and known set of refs:
diff --git a/t/t5619-clone-local-ambiguous-transport.sh b/t/t5619-clone-local-ambiguous-transport.sh
new file mode 100755
index 0000000..cce62bf
--- /dev/null
+++ b/t/t5619-clone-local-ambiguous-transport.sh
@@ -0,0 +1,70 @@
+#!/bin/sh
+
+test_description='test local clone with ambiguous transport'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-httpd.sh"
+
+if ! test_have_prereq SYMLINKS
+then
+ skip_all='skipping test, symlink support unavailable'
+ test_done
+fi
+
+start_httpd
+
+REPO="$HTTPD_DOCUMENT_ROOT_PATH/sub.git"
+URI="$HTTPD_URL/dumb/sub.git"
+
+test_expect_success 'setup' '
+ mkdir -p sensitive &&
+ echo "secret" >sensitive/secret &&
+
+ git init --bare "$REPO" &&
+ test_commit_bulk -C "$REPO" --ref=main 1 &&
+
+ git -C "$REPO" update-ref HEAD main &&
+ git -C "$REPO" update-server-info &&
+
+ git init malicious &&
+ (
+ cd malicious &&
+
+ git submodule add "$URI" &&
+
+ mkdir -p repo/refs &&
+ touch repo/refs/.gitkeep &&
+ printf "ref: refs/heads/a" >repo/HEAD &&
+ ln -s "$(cd .. && pwd)/sensitive" repo/objects &&
+
+ mkdir -p "$HTTPD_URL/dumb" &&
+ ln -s "../../../.git/modules/sub/../../../repo/" "$URI" &&
+
+ git add . &&
+ git commit -m "initial commit"
+ ) &&
+
+ # Delete all of the references in our malicious submodule to
+ # avoid the client attempting to checkout any objects (which
+ # will be missing, and thus will cause the clone to fail before
+ # we can trigger the exploit).
+ git -C "$REPO" for-each-ref --format="delete %(refname)" >in &&
+ git -C "$REPO" update-ref --stdin <in &&
+ git -C "$REPO" update-server-info
+'
+
+test_expect_success 'ambiguous transport does not lead to arbitrary file-inclusion' '
+ git clone malicious clone &&
+ test_must_fail git -C clone submodule update --init 2>err &&
+
+ test_path_is_missing clone/.git/modules/sub/objects/secret &&
+ # We would actually expect "transport .file. not allowed" here,
+ # but due to quirks of the URL detection in Git, we mis-parse
+ # the absolute path as a bogus URL and die before that step.
+ #
+ # This works for now, and if we ever fix the URL detection, it
+ # is OK to change this to detect the transport error.
+ grep "protocol .* is not supported" err
+'
+
+test_done
diff --git a/t/t5700-protocol-v1.sh b/t/t5700-protocol-v1.sh
index 468bd3e..a73b4d4 100755
--- a/t/t5700-protocol-v1.sh
+++ b/t/t5700-protocol-v1.sh
@@ -149,6 +149,21 @@ test_expect_success 'push with file:// using protocol v1' '
grep "push< version 1" log
'
+test_expect_success 'cloning branchless tagless but not refless remote' '
+ rm -rf server client &&
+
+ git -c init.defaultbranch=main init server &&
+ echo foo >server/foo.txt &&
+ git -C server add foo.txt &&
+ git -C server commit -m "message" &&
+ git -C server update-ref refs/notbranch/alsonottag HEAD &&
+ git -C server checkout --detach &&
+ git -C server branch -D main &&
+ git -C server symbolic-ref HEAD refs/heads/nonexistentbranch &&
+
+ git -c protocol.version=1 clone "file://$(pwd)/server" client
+'
+
# Test protocol v1 with 'ssh://' transport
#
test_expect_success 'setup ssh wrapper' '
@@ -229,15 +244,28 @@ test_expect_success 'push with ssh:// using protocol v1' '
grep "push< version 1" log
'
+test_expect_success 'clone propagates object-format from empty repo' '
+ test_when_finished "rm -fr src256 dst256" &&
+
+ echo sha256 >expect &&
+ git init --object-format=sha256 src256 &&
+ git clone --no-local src256 dst256 &&
+ git -C dst256 rev-parse --show-object-format >actual &&
+
+ test_cmp expect actual
+'
+
# Test protocol v1 with 'http://' transport
#
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
-test_expect_success 'create repo to be served by http:// transport' '
+test_expect_success 'create repos to be served by http:// transport' '
git init "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
git -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" config http.receivepack true &&
- test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" one
+ test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" one &&
+ git init --object-format=sha256 "$HTTPD_DOCUMENT_ROOT_PATH/sha256" &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/sha256" config http.receivepack true
'
test_expect_success 'clone with http:// using protocol v1' '
@@ -254,6 +282,20 @@ test_expect_success 'clone with http:// using protocol v1' '
grep "git< version 1" log
'
+test_expect_success 'clone with http:// using protocol v1 with empty SHA-256 repo' '
+ GIT_TRACE_PACKET=1 GIT_TRACE_CURL=1 git -c protocol.version=1 \
+ clone "$HTTPD_URL/smart/sha256" sha256 2>log &&
+
+ echo sha256 >expect &&
+ git -C sha256 rev-parse --show-object-format >actual &&
+ test_cmp expect actual &&
+
+ # Client requested to use protocol v1
+ grep "Git-Protocol: version=1" log &&
+ # Server responded using protocol v1
+ grep "git< version 1" log
+'
+
test_expect_success 'fetch with http:// using protocol v1' '
test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" two &&
diff --git a/t/t5701-git-serve.sh b/t/t5701-git-serve.sh
index 930721f..c48830d 100755
--- a/t/t5701-git-serve.sh
+++ b/t/t5701-git-serve.sh
@@ -5,6 +5,7 @@ test_description='test protocol v2 server commands'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'test capability advertisement' '
@@ -12,16 +13,18 @@ test_expect_success 'test capability advertisement' '
wrong_algo sha1:sha256
wrong_algo sha256:sha1
EOF
- cat >expect <<-EOF &&
+ cat >expect.base <<-EOF &&
version 2
agent=git/$(git version | cut -d" " -f3)
ls-refs=unborn
fetch=shallow wait-for-done
server-option
object-format=$(test_oid algo)
- object-info
+ EOF
+ cat >expect.trailer <<-EOF &&
0000
EOF
+ cat expect.base expect.trailer >expect &&
GIT_TEST_SIDEBAND_ALL=0 test-tool serve-v2 \
--advertise-capabilities >out &&
@@ -48,7 +51,7 @@ test_expect_success 'request invalid capability' '
0000
EOF
test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in &&
- test_i18ngrep "unknown capability" err
+ test_grep "unknown capability" err
'
test_expect_success 'request with no command' '
@@ -58,7 +61,7 @@ test_expect_success 'request with no command' '
0000
EOF
test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in &&
- test_i18ngrep "no command requested" err
+ test_grep "no command requested" err
'
test_expect_success 'request invalid command' '
@@ -69,7 +72,38 @@ test_expect_success 'request invalid command' '
0000
EOF
test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in &&
- test_i18ngrep "invalid command" err
+ test_grep "invalid command" err
+'
+
+test_expect_success 'request capability as command' '
+ test-tool pkt-line pack >in <<-EOF &&
+ command=agent
+ object-format=$(test_oid algo)
+ 0000
+ EOF
+ test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in &&
+ grep invalid.command.*agent err
+'
+
+test_expect_success 'request command as capability' '
+ test-tool pkt-line pack >in <<-EOF &&
+ command=ls-refs
+ object-format=$(test_oid algo)
+ fetch
+ 0000
+ EOF
+ test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in &&
+ grep unknown.capability err
+'
+
+test_expect_success 'requested command is command=value' '
+ test-tool pkt-line pack >in <<-EOF &&
+ command=ls-refs=whatever
+ object-format=$(test_oid algo)
+ 0000
+ EOF
+ test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in &&
+ grep invalid.command.*ls-refs=whatever err
'
test_expect_success 'wrong object-format' '
@@ -80,7 +114,7 @@ test_expect_success 'wrong object-format' '
0000
EOF
test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in &&
- test_i18ngrep "mismatched object format" err
+ test_grep "mismatched object format" err
'
# Test the basics of ls-refs
@@ -116,6 +150,19 @@ test_expect_success 'basics of ls-refs' '
test_cmp expect actual
'
+test_expect_success 'ls-refs complains about unknown options' '
+ test-tool pkt-line pack >in <<-EOF &&
+ command=ls-refs
+ object-format=$(test_oid algo)
+ 0001
+ no-such-arg
+ 0000
+ EOF
+
+ test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in &&
+ grep unexpected.line.*no-such-arg err
+'
+
test_expect_success 'basic ref-prefixes' '
test-tool pkt-line pack >in <<-EOF &&
command=ls-refs
@@ -158,6 +205,37 @@ test_expect_success 'refs/heads prefix' '
test_cmp expect actual
'
+test_expect_success 'ignore very large set of prefixes' '
+ # generate a large number of ref-prefixes that we expect
+ # to match nothing; the value here exceeds TOO_MANY_PREFIXES
+ # from ls-refs.c.
+ {
+ echo command=ls-refs &&
+ echo object-format=$(test_oid algo) &&
+ echo 0001 &&
+ perl -le "print \"ref-prefix refs/heads/\$_\" for (1..65536)" &&
+ echo 0000
+ } |
+ test-tool pkt-line pack >in &&
+
+ # and then confirm that we see unmatched prefixes anyway (i.e.,
+ # that the prefix was not applied).
+ cat >expect <<-EOF &&
+ $(git rev-parse HEAD) HEAD
+ $(git rev-parse refs/heads/dev) refs/heads/dev
+ $(git rev-parse refs/heads/main) refs/heads/main
+ $(git rev-parse refs/heads/release) refs/heads/release
+ $(git rev-parse refs/tags/annotated-tag) refs/tags/annotated-tag
+ $(git rev-parse refs/tags/one) refs/tags/one
+ $(git rev-parse refs/tags/two) refs/tags/two
+ 0000
+ EOF
+
+ test-tool serve-v2 --stateless-rpc <in >out &&
+ test-tool pkt-line unpack <out >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'peel parameter' '
test-tool pkt-line pack >in <<-EOF &&
command=ls-refs
@@ -244,6 +322,8 @@ test_expect_success 'unexpected lines are not allowed in fetch request' '
# Test the basics of object-info
#
test_expect_success 'basics of object-info' '
+ test_config transfer.advertiseObjectInfo true &&
+
test-tool pkt-line pack >in <<-EOF &&
command=object-info
object-format=$(test_oid algo)
@@ -266,4 +346,60 @@ test_expect_success 'basics of object-info' '
test_cmp expect actual
'
+test_expect_success 'test capability advertisement with uploadpack.advertiseBundleURIs' '
+ test_config uploadpack.advertiseBundleURIs true &&
+
+ cat >expect.extra <<-EOF &&
+ bundle-uri
+ EOF
+ cat expect.base \
+ expect.extra \
+ expect.trailer >expect &&
+
+ GIT_TEST_SIDEBAND_ALL=0 test-tool serve-v2 \
+ --advertise-capabilities >out &&
+ test-tool pkt-line unpack <out >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'basics of bundle-uri: dies if not enabled' '
+ test-tool pkt-line pack >in <<-EOF &&
+ command=bundle-uri
+ 0000
+ EOF
+
+ cat >err.expect <<-\EOF &&
+ fatal: invalid command '"'"'bundle-uri'"'"'
+ EOF
+
+ cat >expect <<-\EOF &&
+ ERR serve: invalid command '"'"'bundle-uri'"'"'
+ EOF
+
+ test_must_fail test-tool serve-v2 --stateless-rpc <in >out 2>err.actual &&
+ test_cmp err.expect err.actual &&
+ test_must_be_empty out
+'
+
+test_expect_success 'object-info missing from capabilities when disabled' '
+ test_config transfer.advertiseObjectInfo false &&
+
+ GIT_TEST_SIDEBAND_ALL=0 test-tool serve-v2 \
+ --advertise-capabilities >out &&
+ test-tool pkt-line unpack <out >actual &&
+
+ ! grep object.info actual
+'
+
+test_expect_success 'object-info commands rejected when disabled' '
+ test_config transfer.advertiseObjectInfo false &&
+
+ test-tool pkt-line pack >in <<-EOF &&
+ command=object-info
+ EOF
+
+ test_must_fail test-tool serve-v2 --stateless-rpc <in 2>err &&
+ grep invalid.command err
+'
+
test_done
diff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh
index d3687b1..1ef540f 100755
--- a/t/t5702-protocol-v2.sh
+++ b/t/t5702-protocol-v2.sh
@@ -189,8 +189,8 @@ test_expect_success 'warn if using server-option with ls-remote with legacy prot
test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 git -c protocol.version=0 \
ls-remote -o hello -o world "file://$(pwd)/file_parent" main 2>err &&
- test_i18ngrep "see protocol.version in" err &&
- test_i18ngrep "server options require protocol version 2 or later" err
+ test_grep "see protocol.version in" err &&
+ test_grep "server options require protocol version 2 or later" err
'
test_expect_success 'clone with file:// using protocol v2' '
@@ -221,7 +221,9 @@ test_expect_success 'clone of empty repo propagates name of default branch' '
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
git -c init.defaultBranch=main -c protocol.version=2 \
clone "file://$(pwd)/file_empty_parent" file_empty_child &&
- grep "refs/heads/mydefaultbranch" file_empty_child/.git/HEAD
+ echo refs/heads/mydefaultbranch >expect &&
+ git -C file_empty_child symbolic-ref HEAD >actual &&
+ test_cmp expect actual
'
test_expect_success '...but not if explicitly forbidden by config' '
@@ -234,7 +236,100 @@ test_expect_success '...but not if explicitly forbidden by config' '
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
git -c init.defaultBranch=main -c protocol.version=2 \
clone "file://$(pwd)/file_empty_parent" file_empty_child &&
- ! grep "refs/heads/mydefaultbranch" file_empty_child/.git/HEAD
+ echo refs/heads/main >expect &&
+ git -C file_empty_child symbolic-ref HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'bare clone propagates empty default branch' '
+ test_when_finished "rm -rf file_empty_parent file_empty_child.git" &&
+
+ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
+ git -c init.defaultBranch=mydefaultbranch init file_empty_parent &&
+
+ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
+ git -c init.defaultBranch=main -c protocol.version=2 \
+ clone --bare \
+ "file://$(pwd)/file_empty_parent" file_empty_child.git &&
+ echo "refs/heads/mydefaultbranch" >expect &&
+ git -C file_empty_child.git symbolic-ref HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'clone propagates unborn HEAD from non-empty repo' '
+ test_when_finished "rm -rf file_unborn_parent file_unborn_child" &&
+
+ git init file_unborn_parent &&
+ (
+ cd file_unborn_parent &&
+ git checkout -b branchwithstuff &&
+ test_commit --no-tag stuff &&
+ git symbolic-ref HEAD refs/heads/mydefaultbranch
+ ) &&
+
+ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
+ git -c init.defaultBranch=main -c protocol.version=2 \
+ clone "file://$(pwd)/file_unborn_parent" \
+ file_unborn_child 2>stderr &&
+ echo "refs/heads/mydefaultbranch" >expect &&
+ git -C file_unborn_child symbolic-ref HEAD >actual &&
+ test_cmp expect actual &&
+ grep "warning: remote HEAD refers to nonexistent ref" stderr
+'
+
+test_expect_success 'clone propagates object-format from empty repo' '
+ test_when_finished "rm -fr src256 dst256" &&
+
+ echo sha256 >expect &&
+ git init --object-format=sha256 src256 &&
+ git clone src256 dst256 &&
+ git -C dst256 rev-parse --show-object-format >actual &&
+
+ test_cmp expect actual
+'
+
+test_expect_success 'bare clone propagates unborn HEAD from non-empty repo' '
+ test_when_finished "rm -rf file_unborn_parent file_unborn_child.git" &&
+
+ git init file_unborn_parent &&
+ (
+ cd file_unborn_parent &&
+ git checkout -b branchwithstuff &&
+ test_commit --no-tag stuff &&
+ git symbolic-ref HEAD refs/heads/mydefaultbranch
+ ) &&
+
+ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
+ git -c init.defaultBranch=main -c protocol.version=2 \
+ clone --bare "file://$(pwd)/file_unborn_parent" \
+ file_unborn_child.git 2>stderr &&
+ echo "refs/heads/mydefaultbranch" >expect &&
+ git -C file_unborn_child.git symbolic-ref HEAD >actual &&
+ test_cmp expect actual &&
+ ! grep "warning:" stderr
+'
+
+test_expect_success 'defaulted HEAD uses remote branch if available' '
+ test_when_finished "rm -rf file_unborn_parent file_unborn_child" &&
+
+ git init file_unborn_parent &&
+ (
+ cd file_unborn_parent &&
+ git config lsrefs.unborn ignore &&
+ git checkout -b branchwithstuff &&
+ test_commit --no-tag stuff &&
+ git symbolic-ref HEAD refs/heads/mydefaultbranch
+ ) &&
+
+ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
+ git -c init.defaultBranch=branchwithstuff -c protocol.version=2 \
+ clone "file://$(pwd)/file_unborn_parent" \
+ file_unborn_child 2>stderr &&
+ echo "refs/heads/branchwithstuff" >expect &&
+ git -C file_unborn_child symbolic-ref HEAD >actual &&
+ test_cmp expect actual &&
+ test_path_is_file file_unborn_child/stuff.t &&
+ ! grep "warning:" stderr
'
test_expect_success 'fetch with file:// using protocol v2' '
@@ -294,8 +389,8 @@ test_expect_success 'warn if using server-option with fetch with legacy protocol
test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 git -C temp_child -c protocol.version=0 \
fetch -o hello -o world "file://$(pwd)/file_parent" main 2>err &&
- test_i18ngrep "see protocol.version in" err &&
- test_i18ngrep "server options require protocol version 2 or later" err
+ test_grep "see protocol.version in" err &&
+ test_grep "server options require protocol version 2 or later" err
'
test_expect_success 'server-options are sent when cloning' '
@@ -316,8 +411,8 @@ test_expect_success 'warn if using server-option with clone with legacy protocol
clone --server-option=hello --server-option=world \
"file://$(pwd)/file_parent" myclone 2>err &&
- test_i18ngrep "see protocol.version in" err &&
- test_i18ngrep "server options require protocol version 2 or later" err
+ test_grep "see protocol.version in" err &&
+ test_grep "server options require protocol version 2 or later" err
'
test_expect_success 'upload-pack respects config using protocol v2' '
@@ -412,7 +507,7 @@ test_expect_success 'partial clone warns if filter is not advertised' '
git -C server config uploadpack.allowfilter 0 &&
git -c protocol.version=2 \
clone --filter=blob:none "file://$(pwd)/server" client 2>err &&
- test_i18ngrep "filtering not recognized by server, ignoring" err
+ test_grep "filtering not recognized by server, ignoring" err
'
test_expect_success 'even with handcrafted request, filter does not work if not advertised' '
@@ -606,7 +701,7 @@ test_expect_success 'usage: --negotiate-only without --negotiation-tip' '
setup_negotiate_only "$SERVER" "$URI" &&
cat >err.expect <<-\EOF &&
- fatal: --negotiate-only needs one or more --negotiate-tip=*
+ fatal: --negotiate-only needs one or more --negotiation-tip=*
EOF
test_must_fail git -c protocol.version=2 -C client fetch \
@@ -615,6 +710,18 @@ test_expect_success 'usage: --negotiate-only without --negotiation-tip' '
test_cmp err.expect err.actual
'
+test_expect_success 'usage: --negotiate-only with --recurse-submodules' '
+ cat >err.expect <<-\EOF &&
+ fatal: options '\''--negotiate-only'\'' and '\''--recurse-submodules'\'' cannot be used together
+ EOF
+
+ test_must_fail git -c protocol.version=2 -C client fetch \
+ --negotiate-only \
+ --recurse-submodules \
+ origin 2>err.actual &&
+ test_cmp err.expect err.actual
+'
+
test_expect_success 'file:// --negotiate-only' '
SERVER="server" &&
URI="file://$(pwd)/server" &&
@@ -641,7 +748,53 @@ test_expect_success 'file:// --negotiate-only with protocol v0' '
--negotiate-only \
--negotiation-tip=$(git -C client rev-parse HEAD) \
origin 2>err &&
- test_i18ngrep "negotiate-only requires protocol v2" err
+ test_grep "negotiate-only requires protocol v2" err
+'
+
+test_expect_success 'push with custom path does not request v2' '
+ rm -f env.trace &&
+ git -C client push \
+ --receive-pack="env >../env.trace; git-receive-pack" \
+ origin HEAD:refs/heads/custom-push-test &&
+ test_path_is_file env.trace &&
+ ! grep ^GIT_PROTOCOL env.trace
+'
+
+test_expect_success 'fetch with custom path does request v2' '
+ rm -f env.trace &&
+ git -C client fetch \
+ --upload-pack="env >../env.trace; git-upload-pack" \
+ origin HEAD &&
+ grep ^GIT_PROTOCOL=version=2 env.trace
+'
+
+test_expect_success 'archive with custom path does not request v2' '
+ rm -f env.trace &&
+ git -C client archive \
+ --exec="env >../env.trace; git-upload-archive" \
+ --remote=origin \
+ HEAD >/dev/null &&
+ test_path_is_file env.trace &&
+ ! grep ^GIT_PROTOCOL env.trace
+'
+
+test_expect_success 'reject client packfile-uris if not advertised' '
+ {
+ packetize command=fetch &&
+ packetize object-format=$(test_oid algo) &&
+ printf 0001 &&
+ packetize packfile-uris https &&
+ packetize done &&
+ printf 0000
+ } >input &&
+ test_must_fail env GIT_PROTOCOL=version=2 \
+ git upload-pack client <input &&
+ test_must_fail env GIT_PROTOCOL=version=2 \
+ git -c uploadpack.blobpackfileuri \
+ upload-pack client <input &&
+ GIT_PROTOCOL=version=2 \
+ git -c uploadpack.blobpackfileuri=anything \
+ upload-pack client <input
'
# Test protocol v2 with 'http://' transport
@@ -687,7 +840,7 @@ test_expect_success 'clone repository with http:// using protocol v2 with incomp
# Server responded using protocol v2
grep "git< version 2" log &&
# Client reported appropriate failure
- test_i18ngrep "bytes of length header were received" err
+ test_grep "bytes of length header were received" err
'
test_expect_success 'clone repository with http:// using protocol v2 with incomplete pktline body' '
@@ -704,7 +857,7 @@ test_expect_success 'clone repository with http:// using protocol v2 with incomp
# Server responded using protocol v2
grep "git< version 2" log &&
# Client reported appropriate failure
- test_i18ngrep "bytes of body are still expected" err
+ test_grep "bytes of body are still expected" err
'
test_expect_success 'clone with http:// using protocol v2 and invalid parameters' '
@@ -734,7 +887,7 @@ test_expect_success 'clone big repository with http:// using protocol v2' '
echo "data 0" &&
echo "M 644 inline bla.txt" &&
echo "data 4" &&
- echo "bla"
+ echo "bla" || return 1
done | git -C "$HTTPD_DOCUMENT_ROOT_PATH/big" fast-import &&
GIT_TRACE_PACKET="$(pwd)/log" GIT_TRACE_CURL="$(pwd)/log" git \
@@ -851,7 +1004,7 @@ test_expect_success 'when server sends "ready", expect DELIM' '
test_must_fail git -C http_child -c protocol.version=2 \
fetch "$HTTPD_URL/one_time_perl/http_parent" 2> err &&
- test_i18ngrep "expected packfile to be sent after .ready." err
+ test_grep "expected packfile to be sent after .ready." err
'
test_expect_success 'when server does not send "ready", expect FLUSH' '
@@ -879,7 +1032,7 @@ test_expect_success 'when server does not send "ready", expect FLUSH' '
fetch "$HTTPD_URL/one_time_perl/http_parent" 2> err &&
grep "fetch< .*acknowledgments" log &&
! grep "fetch< .*ready" log &&
- test_i18ngrep "expected no other sections to be sent after no .ready." err
+ test_grep "expected no other sections to be sent after no .ready." err
'
configure_exclusion () {
@@ -917,7 +1070,7 @@ test_expect_success 'part of packfile response provided as URI' '
do
git verify-pack --object-format=$(test_oid algo) --verbose $idx >out &&
{
- grep "^[0-9a-f]\{16,\} " out || :
+ grep -E "^[0-9a-f]{16,} " out || :
} >out.objectlist &&
if test_line_count = 1 out.objectlist
then
@@ -929,7 +1082,7 @@ test_expect_success 'part of packfile response provided as URI' '
then
>h2found
fi
- fi
+ fi || return 1
done &&
test -f hfound &&
test -f h2found &&
@@ -989,7 +1142,7 @@ test_expect_success 'fetching with valid packfile URI but invalid hash fails' '
git -c protocol.version=2 \
-c fetch.uriprotocols=http,https \
clone "$HTTPD_URL/smart/http_parent" http_child 2>err &&
- test_i18ngrep "pack downloaded from.*does not match expected hash" err
+ test_grep "pack downloaded from.*does not match expected hash" err
'
test_expect_success 'packfile-uri with transfer.fsckobjects' '
@@ -1030,7 +1183,7 @@ test_expect_success 'packfile-uri with transfer.fsckobjects fails on bad object'
This commit object intentionally broken
EOF
- BOGUS=$(git -C "$P" hash-object -t commit -w --stdin <bogus-commit) &&
+ BOGUS=$(git -C "$P" hash-object -t commit -w --stdin --literally <bogus-commit) &&
git -C "$P" branch bogus-branch "$BOGUS" &&
echo my-blob >"$P/my-blob" &&
@@ -1043,7 +1196,7 @@ test_expect_success 'packfile-uri with transfer.fsckobjects fails on bad object'
test_must_fail git -c protocol.version=2 -c transfer.fsckobjects=1 \
-c fetch.uriprotocols=http,https \
clone "$HTTPD_URL/smart/http_parent" http_child 2>error &&
- test_i18ngrep "invalid author/committer line - missing email" error
+ test_grep "invalid author/committer line - missing email" error
'
test_expect_success 'packfile-uri with transfer.fsckobjects succeeds when .gitmodules is separate from tree' '
@@ -1091,7 +1244,58 @@ test_expect_success 'packfile-uri with transfer.fsckobjects fails when .gitmodul
test_must_fail git -c protocol.version=2 -c transfer.fsckobjects=1 \
-c fetch.uriprotocols=http,https \
clone "$HTTPD_URL/smart/http_parent" http_child 2>err &&
- test_i18ngrep "disallowed submodule name" err
+ test_grep "disallowed submodule name" err
+'
+
+test_expect_success 'packfile-uri path redacted in trace' '
+ P="$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
+ rm -rf "$P" http_child log &&
+
+ git init "$P" &&
+ git -C "$P" config "uploadpack.allowsidebandall" "true" &&
+
+ echo my-blob >"$P/my-blob" &&
+ git -C "$P" add my-blob &&
+ git -C "$P" commit -m x &&
+
+ git -C "$P" hash-object my-blob >objh &&
+ git -C "$P" pack-objects "$HTTPD_DOCUMENT_ROOT_PATH/mypack" <objh >packh &&
+ git -C "$P" config --add \
+ "uploadpack.blobpackfileuri" \
+ "$(cat objh) $(cat packh) $HTTPD_URL/dumb/mypack-$(cat packh).pack" &&
+
+ GIT_TRACE_PACKET="$(pwd)/log" \
+ git -c protocol.version=2 \
+ -c fetch.uriprotocols=http,https \
+ clone "$HTTPD_URL/smart/http_parent" http_child &&
+
+ grep -F "clone< \\1$(cat packh) $HTTPD_URL/<redacted>" log
+'
+
+test_expect_success 'packfile-uri path not redacted in trace when GIT_TRACE_REDACT=0' '
+ P="$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
+ rm -rf "$P" http_child log &&
+
+ git init "$P" &&
+ git -C "$P" config "uploadpack.allowsidebandall" "true" &&
+
+ echo my-blob >"$P/my-blob" &&
+ git -C "$P" add my-blob &&
+ git -C "$P" commit -m x &&
+
+ git -C "$P" hash-object my-blob >objh &&
+ git -C "$P" pack-objects "$HTTPD_DOCUMENT_ROOT_PATH/mypack" <objh >packh &&
+ git -C "$P" config --add \
+ "uploadpack.blobpackfileuri" \
+ "$(cat objh) $(cat packh) $HTTPD_URL/dumb/mypack-$(cat packh).pack" &&
+
+ GIT_TRACE_PACKET="$(pwd)/log" \
+ GIT_TRACE_REDACT=0 \
+ git -c protocol.version=2 \
+ -c fetch.uriprotocols=http,https \
+ clone "$HTTPD_URL/smart/http_parent" http_child &&
+
+ grep -F "clone< \\1$(cat packh) $HTTPD_URL/dumb/mypack-$(cat packh).pack" log
'
test_expect_success 'http:// --negotiate-only' '
@@ -1123,7 +1327,7 @@ test_expect_success 'http:// --negotiate-only without wait-for-done support' '
--negotiate-only \
--negotiation-tip=$(git -C client rev-parse HEAD) \
origin 2>err &&
- test_i18ngrep "server does not support wait-for-done" err
+ test_grep "server does not support wait-for-done" err
'
test_expect_success 'http:// --negotiate-only with protocol v0' '
@@ -1137,7 +1341,7 @@ test_expect_success 'http:// --negotiate-only with protocol v0' '
--negotiate-only \
--negotiation-tip=$(git -C client rev-parse HEAD) \
origin 2>err &&
- test_i18ngrep "negotiate-only requires protocol v2" err
+ test_grep "negotiate-only requires protocol v2" err
'
# DO NOT add non-httpd-specific tests here, because the last part of this
diff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh
index 2200985..1910971 100755
--- a/t/t5703-upload-pack-ref-in-want.sh
+++ b/t/t5703-upload-pack-ref-in-want.sh
@@ -2,9 +2,6 @@
test_description='upload-pack ref-in-want'
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
. ./test-lib.sh
get_actual_refs () {
@@ -232,14 +229,16 @@ test_expect_success 'setup repos for fetching with ref-in-want tests' '
'
test_expect_success 'fetching with exact OID' '
- test_when_finished "rm -f log" &&
+ test_when_finished "rm -f log trace2" &&
rm -rf local &&
cp -r "$LOCAL_PRISTINE" local &&
oid=$(git -C "$REPO" rev-parse d) &&
- GIT_TRACE_PACKET="$(pwd)/log" git -C local fetch origin \
+ GIT_TRACE_PACKET="$(pwd)/log" GIT_TRACE2_EVENT="$(pwd)/trace2" \
+ git -C local fetch origin \
"$oid":refs/heads/actual &&
+ grep \"key\":\"total_rounds\",\"value\":\"2\" trace2 &&
git -C "$REPO" rev-parse "d" >expected &&
git -C local rev-parse refs/heads/actual >actual &&
test_cmp expected actual &&
@@ -485,7 +484,7 @@ test_expect_success 'server is initially ahead - no ref in want' '
cp -r "$LOCAL_PRISTINE" local &&
inconsistency main $(test_oid numeric) &&
test_must_fail git -C local fetch 2>err &&
- test_i18ngrep "fatal: remote error: upload-pack: not our ref" err
+ test_grep "fatal: remote error: upload-pack: not our ref" err
'
test_expect_success 'server is initially ahead - ref in want' '
@@ -531,7 +530,7 @@ test_expect_success 'server loses a ref - ref in want' '
echo "s/main/rain/" >"$HTTPD_ROOT_PATH/one-time-perl" &&
test_must_fail git -C local fetch 2>err &&
- test_i18ngrep "fatal: remote error: unknown ref refs/heads/rain" err
+ test_grep "fatal: remote error: unknown ref refs/heads/rain" err
'
# DO NOT add non-httpd-specific tests here, because the last part of this
diff --git a/t/t5704-protocol-violations.sh b/t/t5704-protocol-violations.sh
index 5c94194..11be64f 100755
--- a/t/t5704-protocol-violations.sh
+++ b/t/t5704-protocol-violations.sh
@@ -4,6 +4,8 @@ test_description='Test responses to violations of the network protocol. In most
of these cases it will generally be acceptable for one side to break off
communications if the other side says something unexpected. We are mostly
making sure that we do not segfault or otherwise behave badly.'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'extra delim packet in v2 ls-refs args' '
@@ -16,7 +18,7 @@ test_expect_success 'extra delim packet in v2 ls-refs args' '
} >input &&
test_must_fail env GIT_PROTOCOL=version=2 \
git upload-pack . <input 2>err &&
- test_i18ngrep "expected flush after ls-refs arguments" err
+ test_grep "expected flush after ls-refs arguments" err
'
test_expect_success 'extra delim packet in v2 fetch args' '
@@ -29,7 +31,22 @@ test_expect_success 'extra delim packet in v2 fetch args' '
} >input &&
test_must_fail env GIT_PROTOCOL=version=2 \
git upload-pack . <input 2>err &&
- test_i18ngrep "expected flush after fetch arguments" err
+ test_grep "expected flush after fetch arguments" err
+'
+
+test_expect_success 'bogus symref in v0 capabilities' '
+ test_commit foo &&
+ oid=$(git rev-parse HEAD) &&
+ dst=refs/heads/foo &&
+ {
+ printf "%s HEAD\0symref object-format=%s symref=HEAD:%s\n" \
+ "$oid" "$GIT_DEFAULT_HASH" "$dst" |
+ test-tool pkt-line pack-raw-stdin &&
+ printf "0000"
+ } >input &&
+ git ls-remote --symref --upload-pack="cat input; read junk;:" . >actual &&
+ printf "ref: %s\tHEAD\n%s\tHEAD\n" "$dst" "$oid" >expect &&
+ test_cmp expect actual
'
test_done
diff --git a/t/t5705-session-id-in-capabilities.sh b/t/t5705-session-id-in-capabilities.sh
index eb8c79a..b8a722e 100755
--- a/t/t5705-session-id-in-capabilities.sh
+++ b/t/t5705-session-id-in-capabilities.sh
@@ -2,6 +2,7 @@
test_description='session ID in capabilities'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
REPO="$(pwd)/repo"
@@ -32,7 +33,6 @@ do
test_when_finished "git -C local push --delete origin new-branch" &&
cp -r "$LOCAL_PRISTINE" local &&
git -C local pull --no-rebase origin &&
- GIT_TRACE2_EVENT_NESTING=5 \
GIT_TRACE2_EVENT="$(pwd)/tr2-client-events" \
git -c protocol.version=$PROTO -C local push \
--receive-pack "GIT_TRACE2_EVENT=\"$(pwd)/tr2-server-events\" git-receive-pack" \
@@ -65,7 +65,6 @@ do
test_when_finished "git -C local push --delete origin new-branch" &&
cp -r "$LOCAL_PRISTINE" local &&
git -C local pull --no-rebase origin &&
- GIT_TRACE2_EVENT_NESTING=5 \
GIT_TRACE2_EVENT="$(pwd)/tr2-client-events" \
git -c protocol.version=$PROTO -C local push \
--receive-pack "GIT_TRACE2_EVENT=\"$(pwd)/tr2-server-events\" git-receive-pack" \
diff --git a/t/t5730-protocol-v2-bundle-uri-file.sh b/t/t5730-protocol-v2-bundle-uri-file.sh
new file mode 100755
index 0000000..37bdb72
--- /dev/null
+++ b/t/t5730-protocol-v2-bundle-uri-file.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+test_description="Test bundle-uri with protocol v2 and 'file://' transport"
+
+TEST_NO_CREATE_REPO=1
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+# Test protocol v2 with 'file://' transport
+#
+BUNDLE_URI_PROTOCOL=file
+. "$TEST_DIRECTORY"/lib-bundle-uri-protocol.sh
+
+test_done
diff --git a/t/t5731-protocol-v2-bundle-uri-git.sh b/t/t5731-protocol-v2-bundle-uri-git.sh
new file mode 100755
index 0000000..8add1b3
--- /dev/null
+++ b/t/t5731-protocol-v2-bundle-uri-git.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+test_description="Test bundle-uri with protocol v2 and 'git://' transport"
+
+TEST_NO_CREATE_REPO=1
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+# Test protocol v2 with 'git://' transport
+#
+BUNDLE_URI_PROTOCOL=git
+. "$TEST_DIRECTORY"/lib-bundle-uri-protocol.sh
+
+test_done
diff --git a/t/t5732-protocol-v2-bundle-uri-http.sh b/t/t5732-protocol-v2-bundle-uri-http.sh
new file mode 100755
index 0000000..129daa0
--- /dev/null
+++ b/t/t5732-protocol-v2-bundle-uri-http.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+test_description="Test bundle-uri with protocol v2 and 'http://' transport"
+
+TEST_NO_CREATE_REPO=1
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+# Test protocol v2 with 'http://' transport
+#
+BUNDLE_URI_PROTOCOL=http
+. "$TEST_DIRECTORY"/lib-bundle-uri-protocol.sh
+
+test_done
diff --git a/t/t5750-bundle-uri-parse.sh b/t/t5750-bundle-uri-parse.sh
new file mode 100755
index 0000000..81bdf58
--- /dev/null
+++ b/t/t5750-bundle-uri-parse.sh
@@ -0,0 +1,290 @@
+#!/bin/sh
+
+test_description="Test bundle-uri bundle_uri_parse_line()"
+
+TEST_NO_CREATE_REPO=1
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'bundle_uri_parse_line() just URIs' '
+ cat >in <<-\EOF &&
+ bundle.one.uri=http://example.com/bundle.bdl
+ bundle.two.uri=https://example.com/bundle.bdl
+ bundle.three.uri=file:///usr/share/git/bundle.bdl
+ EOF
+
+ cat >expect <<-\EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ [bundle "one"]
+ uri = http://example.com/bundle.bdl
+ [bundle "two"]
+ uri = https://example.com/bundle.bdl
+ [bundle "three"]
+ uri = file:///usr/share/git/bundle.bdl
+ EOF
+
+ test-tool bundle-uri parse-key-values in >actual 2>err &&
+ test_must_be_empty err &&
+ test_cmp_config_output expect actual
+'
+
+test_expect_success 'bundle_uri_parse_line(): relative URIs' '
+ cat >in <<-\EOF &&
+ bundle.one.uri=bundle.bdl
+ bundle.two.uri=../bundle.bdl
+ bundle.three.uri=sub/dir/bundle.bdl
+ EOF
+
+ cat >expect <<-\EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ [bundle "one"]
+ uri = <uri>/bundle.bdl
+ [bundle "two"]
+ uri = bundle.bdl
+ [bundle "three"]
+ uri = <uri>/sub/dir/bundle.bdl
+ EOF
+
+ test-tool bundle-uri parse-key-values in >actual 2>err &&
+ test_must_be_empty err &&
+ test_cmp_config_output expect actual
+'
+
+test_expect_success 'bundle_uri_parse_line(): relative URIs and parent paths' '
+ cat >in <<-\EOF &&
+ bundle.one.uri=bundle.bdl
+ bundle.two.uri=../bundle.bdl
+ bundle.three.uri=../../bundle.bdl
+ EOF
+
+ cat >expect <<-\EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ [bundle "one"]
+ uri = <uri>/bundle.bdl
+ [bundle "two"]
+ uri = bundle.bdl
+ [bundle "three"]
+ uri = <uri>/../bundle.bdl
+ EOF
+
+ # TODO: We would prefer if parsing a bundle list would not cause
+ # a die() and instead would give a warning and allow the rest of
+ # a Git command to continue. This test_must_fail is necessary for
+ # now until the interface for relative_url() allows for reporting
+ # an error instead of die()ing.
+ test_must_fail test-tool bundle-uri parse-key-values in >actual 2>err &&
+ grep "fatal: cannot strip one component off url" err
+'
+
+test_expect_success 'bundle_uri_parse_line() parsing edge cases: empty key or value' '
+ cat >in <<-\EOF &&
+ =bogus-value
+ bogus-key=
+ EOF
+
+ cat >err.expect <<-EOF &&
+ error: bundle-uri: line has empty key or value
+ error: bad line: '\''=bogus-value'\''
+ error: bundle-uri: line has empty key or value
+ error: bad line: '\''bogus-key='\''
+ EOF
+
+ cat >expect <<-\EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ EOF
+
+ test_must_fail test-tool bundle-uri parse-key-values in >actual 2>err &&
+ test_cmp err.expect err &&
+ test_cmp_config_output expect actual
+'
+
+test_expect_success 'bundle_uri_parse_line() parsing edge cases: empty lines' '
+ cat >in <<-\EOF &&
+ bundle.one.uri=http://example.com/bundle.bdl
+
+ bundle.two.uri=https://example.com/bundle.bdl
+
+ bundle.three.uri=file:///usr/share/git/bundle.bdl
+ EOF
+
+ cat >err.expect <<-\EOF &&
+ error: bundle-uri: got an empty line
+ error: bad line: '\'''\''
+ error: bundle-uri: got an empty line
+ error: bad line: '\'''\''
+ EOF
+
+ # We fail, but try to continue parsing regardless
+ cat >expect <<-\EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ [bundle "one"]
+ uri = http://example.com/bundle.bdl
+ [bundle "two"]
+ uri = https://example.com/bundle.bdl
+ [bundle "three"]
+ uri = file:///usr/share/git/bundle.bdl
+ EOF
+
+ test_must_fail test-tool bundle-uri parse-key-values in >actual 2>err &&
+ test_cmp err.expect err &&
+ test_cmp_config_output expect actual
+'
+
+test_expect_success 'bundle_uri_parse_line() parsing edge cases: duplicate lines' '
+ cat >in <<-\EOF &&
+ bundle.one.uri=http://example.com/bundle.bdl
+ bundle.two.uri=https://example.com/bundle.bdl
+ bundle.one.uri=https://example.com/bundle-2.bdl
+ bundle.three.uri=file:///usr/share/git/bundle.bdl
+ EOF
+
+ cat >err.expect <<-\EOF &&
+ error: bad line: '\''bundle.one.uri=https://example.com/bundle-2.bdl'\''
+ EOF
+
+ # We fail, but try to continue parsing regardless
+ cat >expect <<-\EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ [bundle "one"]
+ uri = http://example.com/bundle.bdl
+ [bundle "two"]
+ uri = https://example.com/bundle.bdl
+ [bundle "three"]
+ uri = file:///usr/share/git/bundle.bdl
+ EOF
+
+ test_must_fail test-tool bundle-uri parse-key-values in >actual 2>err &&
+ test_cmp err.expect err &&
+ test_cmp_config_output expect actual
+'
+
+test_expect_success 'parse config format: just URIs' '
+ cat >expect <<-\EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ [bundle "one"]
+ uri = http://example.com/bundle.bdl
+ [bundle "two"]
+ uri = https://example.com/bundle.bdl
+ [bundle "three"]
+ uri = file:///usr/share/git/bundle.bdl
+ EOF
+
+ test-tool bundle-uri parse-config expect >actual 2>err &&
+ test_must_be_empty err &&
+ test_cmp_config_output expect actual
+'
+
+test_expect_success 'parse config format: relative URIs' '
+ cat >in <<-\EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ [bundle "one"]
+ uri = bundle.bdl
+ [bundle "two"]
+ uri = ../bundle.bdl
+ [bundle "three"]
+ uri = sub/dir/bundle.bdl
+ EOF
+
+ cat >expect <<-\EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ [bundle "one"]
+ uri = <uri>/bundle.bdl
+ [bundle "two"]
+ uri = bundle.bdl
+ [bundle "three"]
+ uri = <uri>/sub/dir/bundle.bdl
+ EOF
+
+ test-tool bundle-uri parse-config in >actual 2>err &&
+ test_must_be_empty err &&
+ test_cmp_config_output expect actual
+'
+
+test_expect_success 'parse config format edge cases: empty key or value' '
+ cat >in1 <<-\EOF &&
+ = bogus-value
+ EOF
+
+ cat >err1 <<-EOF &&
+ error: bad config line 1 in file in1
+ EOF
+
+ cat >expect <<-\EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ EOF
+
+ test_must_fail test-tool bundle-uri parse-config in1 >actual 2>err &&
+ test_cmp err1 err &&
+ test_cmp_config_output expect actual &&
+
+ cat >in2 <<-\EOF &&
+ bogus-key =
+ EOF
+
+ cat >err2 <<-EOF &&
+ error: bad config line 1 in file in2
+ EOF
+
+ test_must_fail test-tool bundle-uri parse-config in2 >actual 2>err &&
+ test_cmp err2 err &&
+ test_cmp_config_output expect actual
+'
+
+test_expect_success 'parse config format: creationToken heuristic' '
+ cat >expect <<-\EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+ [bundle "one"]
+ uri = http://example.com/bundle.bdl
+ creationToken = 123456
+ [bundle "two"]
+ uri = https://example.com/bundle.bdl
+ creationToken = 12345678901234567890
+ [bundle "three"]
+ uri = file:///usr/share/git/bundle.bdl
+ creationToken = 1
+ EOF
+
+ test-tool bundle-uri parse-config expect >actual 2>err &&
+ test_must_be_empty err &&
+ test_cmp_config_output expect actual
+'
+
+test_expect_success 'parse config format edge cases: creationToken heuristic' '
+ cat >expect <<-\EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+ [bundle "one"]
+ uri = http://example.com/bundle.bdl
+ creationToken = bogus
+ EOF
+
+ test-tool bundle-uri parse-config expect >actual 2>err &&
+ grep "could not parse bundle list key creationToken with value '\''bogus'\''" err
+'
+
+test_done
diff --git a/t/t5801-remote-helpers.sh b/t/t5801-remote-helpers.sh
index d386076..4e0a77f 100755
--- a/t/t5801-remote-helpers.sh
+++ b/t/t5801-remote-helpers.sh
@@ -137,7 +137,7 @@ test_expect_success 'forced push' '
test_expect_success 'cloning without refspec' '
GIT_REMOTE_TESTGIT_NOREFSPEC=1 \
git clone "testgit::${PWD}/server" local2 2>error &&
- test_i18ngrep "this remote helper should implement refspec capability" error &&
+ test_grep "this remote helper should implement refspec capability" error &&
compare_refs local2 HEAD server HEAD
'
@@ -145,7 +145,7 @@ test_expect_success 'pulling without refspecs' '
(cd local2 &&
git reset --hard &&
GIT_REMOTE_TESTGIT_NOREFSPEC=1 git pull 2>../error) &&
- test_i18ngrep "this remote helper should implement refspec capability" error &&
+ test_grep "this remote helper should implement refspec capability" error &&
compare_refs local2 HEAD server HEAD
'
@@ -157,7 +157,7 @@ test_expect_success 'pushing without refspecs' '
GIT_REMOTE_TESTGIT_NOREFSPEC=1 &&
export GIT_REMOTE_TESTGIT_NOREFSPEC &&
test_must_fail git push 2>../error) &&
- test_i18ngrep "remote-helper doesn.t support push; refspec needed" error
+ test_grep "remote-helper doesn.t support push; refspec needed" error
'
test_expect_success 'pulling without marks' '
@@ -256,7 +256,7 @@ clean_mark () {
test_expect_success 'proper failure checks for fetching' '
(cd local &&
test_must_fail env GIT_REMOTE_TESTGIT_FAILURE=1 git fetch 2>error &&
- test_i18ngrep -q "error while running fast-import" error
+ test_grep -q "error while running fast-import" error
)
'
diff --git a/t/t5801/git-remote-testgit b/t/t5801/git-remote-testgit
index 1544d6d..c5b10f5 100755
--- a/t/t5801/git-remote-testgit
+++ b/t/t5801/git-remote-testgit
@@ -12,6 +12,11 @@ url=$2
dir="$GIT_DIR/testgit/$alias"
+if ! git rev-parse --is-inside-git-dir
+then
+ exit 1
+fi
+
h_refspec="refs/heads/*:refs/testgit/$alias/heads/*"
t_refspec="refs/tags/*:refs/testgit/$alias/tags/*"
@@ -25,6 +30,7 @@ GIT_DIR="$url/.git"
export GIT_DIR
force=
+object_format=
mkdir -p "$dir"
@@ -56,7 +62,8 @@ do
echo
;;
list)
- echo ":object-format $(git rev-parse --show-object-format=storage)"
+ test -n "$object_format" &&
+ echo ":object-format $(git rev-parse --show-object-format=storage)"
git for-each-ref --format='? %(refname)' 'refs/heads/' 'refs/tags/'
head=$(git symbolic-ref HEAD)
echo "@$head HEAD"
diff --git a/t/t5810-proto-disable-local.sh b/t/t5810-proto-disable-local.sh
index c1ef99b..8626102 100755
--- a/t/t5810-proto-disable-local.sh
+++ b/t/t5810-proto-disable-local.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test disabling of local paths in clone/fetch'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY/lib-proto-disable.sh"
diff --git a/t/t5811-proto-disable-git.sh b/t/t5811-proto-disable-git.sh
index 8ac6b2a..ed773e7 100755
--- a/t/t5811-proto-disable-git.sh
+++ b/t/t5811-proto-disable-git.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test disabling of git-over-tcp in clone/fetch'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY/lib-proto-disable.sh"
. "$TEST_DIRECTORY/lib-git-daemon.sh"
diff --git a/t/t5812-proto-disable-http.sh b/t/t5812-proto-disable-http.sh
index af8772f..769c717 100755
--- a/t/t5812-proto-disable-http.sh
+++ b/t/t5812-proto-disable-http.sh
@@ -16,11 +16,11 @@ test_expect_success 'create git-accessible repo' '
test_proto "smart http" http "$HTTPD_URL/smart/repo.git"
-test_expect_success 'curl redirects respect whitelist' '
+test_expect_success 'http(s) transport respects GIT_ALLOW_PROTOCOL' '
test_must_fail env GIT_ALLOW_PROTOCOL=http:https \
GIT_SMART_HTTP=0 \
git clone "$HTTPD_URL/ftp-redir/repo.git" 2>stderr &&
- test_i18ngrep -E "(ftp.*disabled|your curl version is too old)" stderr
+ test_grep -E "(ftp.*disabled|your curl version is too old)" stderr
'
test_expect_success 'curl limits redirects' '
diff --git a/t/t5813-proto-disable-ssh.sh b/t/t5813-proto-disable-ssh.sh
index 3f084ee..2e975dc 100755
--- a/t/t5813-proto-disable-ssh.sh
+++ b/t/t5813-proto-disable-ssh.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test disabling of git-over-ssh in clone/fetch'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY/lib-proto-disable.sh"
diff --git a/t/t5815-submodule-protos.sh b/t/t5815-submodule-protos.sh
index 06f55a1..4d5956c 100755
--- a/t/t5815-submodule-protos.sh
+++ b/t/t5815-submodule-protos.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-test_description='test protocol whitelisting with submodules'
+test_description='test protocol filtering with submodules'
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-proto-disable.sh
@@ -36,7 +36,7 @@ test_expect_success 'update of ext not allowed' '
test_must_fail git -C dst submodule update ext-module
'
-test_expect_success 'user can override whitelist' '
+test_expect_success 'user can filter protocols with GIT_ALLOW_PROTOCOL' '
GIT_ALLOW_PROTOCOL=ext git -C dst submodule update ext-module
'
diff --git a/t/t5900-repo-selection.sh b/t/t5900-repo-selection.sh
index 14e59c5..a84faac 100755
--- a/t/t5900-repo-selection.sh
+++ b/t/t5900-repo-selection.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='selecting remote repo in ambiguous cases'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
reset() {
diff --git a/t/t6000-rev-list-misc.sh b/t/t6000-rev-list-misc.sh
index ef849e5..6289a2e 100755
--- a/t/t6000-rev-list-misc.sh
+++ b/t/t6000-rev-list-misc.sh
@@ -169,35 +169,17 @@ test_expect_success 'rev-list --count --objects' '
test_line_count = $count actual
'
-test_expect_success 'rev-list --unsorted-input results in different sorting' '
- git rev-list --unsorted-input HEAD HEAD~ >first &&
- git rev-list --unsorted-input HEAD~ HEAD >second &&
- ! test_cmp first second &&
- sort first >first.sorted &&
- sort second >second.sorted &&
- test_cmp first.sorted second.sorted
-'
+test_expect_success 'rev-list --unpacked' '
+ git repack -ad &&
+ test_commit unpacked &&
-test_expect_success 'rev-list --unsorted-input incompatible with --no-walk' '
- cat >expect <<-EOF &&
- fatal: --no-walk is incompatible with --unsorted-input
- EOF
- test_must_fail git rev-list --unsorted-input --no-walk HEAD 2>error &&
- test_cmp expect error &&
- test_must_fail git rev-list --unsorted-input --no-walk=sorted HEAD 2>error &&
- test_cmp expect error &&
- test_must_fail git rev-list --unsorted-input --no-walk=unsorted HEAD 2>error &&
- test_cmp expect error &&
+ git rev-list --objects --no-object-names unpacked^.. >expect.raw &&
+ sort expect.raw >expect &&
- cat >expect <<-EOF &&
- fatal: --unsorted-input is incompatible with --no-walk
- EOF
- test_must_fail git rev-list --no-walk --unsorted-input HEAD 2>error &&
- test_cmp expect error &&
- test_must_fail git rev-list --no-walk=sorted --unsorted-input HEAD 2>error &&
- test_cmp expect error &&
- test_must_fail git rev-list --no-walk=unsorted --unsorted-input HEAD 2>error &&
- test_cmp expect error
+ git rev-list --all --objects --unpacked --no-object-names >actual.raw &&
+ sort actual.raw >actual &&
+
+ test_cmp expect actual
'
test_done
diff --git a/t/t6001-rev-list-graft.sh b/t/t6001-rev-list-graft.sh
index 7294147..73a2465 100755
--- a/t/t6001-rev-list-graft.sh
+++ b/t/t6001-rev-list-graft.sh
@@ -99,6 +99,7 @@ do
"
test_expect_success 'with grafts' "
+ mkdir -p .git/info &&
echo '$B0 $A2' >.git/info/grafts &&
check $type $B2 -- $B2 $B1 $B0 $A2 $A1 $A0
"
@@ -117,10 +118,10 @@ done
test_expect_success 'show advice that grafts are deprecated' '
git show HEAD 2>err &&
- test_i18ngrep "git replace" err &&
+ test_grep "git replace" err &&
test_config advice.graftFileDeprecated false &&
git show HEAD 2>err &&
- test_i18ngrep ! "git replace" err
+ test_grep ! "git replace" err
'
test_done
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
index b95a021..162cf50 100755
--- a/t/t6002-rev-list-bisect.sh
+++ b/t/t6002-rev-list-bisect.sh
@@ -4,6 +4,7 @@
#
test_description='Tests git rev-list --bisect functionality'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh
index 24d1836..5cf2cee 100755
--- a/t/t6003-rev-list-topo-order.sh
+++ b/t/t6003-rev-list-topo-order.sh
@@ -5,6 +5,7 @@
test_description='Tests git rev-list --topo-order functionality'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
@@ -325,19 +326,16 @@ a2
c3
EOF
-#
-# this test fails on --topo-order - a fix is required
-#
-#test_output_expect_success '--max-age=c3, --topo-order' "git rev-list --topo-order --max-age=$(commit_date c3) l5" <<EOF
-#l5
-#l4
-#l3
-#a4
-#c3
-#b4
-#a3
-#a2
-#EOF
+test_output_expect_success '--max-age=c3, --topo-order' "git rev-list --topo-order --max-age=$(commit_date c3) l5" <<EOF
+l5
+l4
+l3
+a4
+c3
+b4
+a3
+a2
+EOF
test_output_expect_success 'one specified head reachable from another a4, c3, --topo-order' "list_duplicates git rev-list --topo-order a4 c3" <<EOF
EOF
diff --git a/t/t6005-rev-list-count.sh b/t/t6005-rev-list-count.sh
index 0b64822..ee0306a 100755
--- a/t/t6005-rev-list-count.sh
+++ b/t/t6005-rev-list-count.sh
@@ -2,50 +2,65 @@
test_description='git rev-list --max-count and --skip test'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
- for n in 1 2 3 4 5 ; do \
- echo $n > a ; \
- git add a ; \
- git commit -m "$n" ; \
+ for n in 1 2 3 4 5 ; do
+ echo $n > a &&
+ git add a &&
+ git commit -m "$n" || return 1
done
'
test_expect_success 'no options' '
- test $(git rev-list HEAD | wc -l) = 5
+ test_stdout_line_count = 5 git rev-list HEAD
'
test_expect_success '--max-count' '
- test $(git rev-list HEAD --max-count=0 | wc -l) = 0 &&
- test $(git rev-list HEAD --max-count=3 | wc -l) = 3 &&
- test $(git rev-list HEAD --max-count=5 | wc -l) = 5 &&
- test $(git rev-list HEAD --max-count=10 | wc -l) = 5
+ test_must_fail git rev-list --max-count=1q HEAD 2>error &&
+ grep "not an integer" error &&
+
+ test_stdout_line_count = 0 git rev-list HEAD --max-count=0 &&
+ test_stdout_line_count = 3 git rev-list HEAD --max-count=3 &&
+ test_stdout_line_count = 5 git rev-list HEAD --max-count=5 &&
+ test_stdout_line_count = 5 git rev-list HEAD --max-count=10 &&
+ test_stdout_line_count = 5 git rev-list HEAD --max-count=-1
'
test_expect_success '--max-count all forms' '
- test $(git rev-list HEAD --max-count=1 | wc -l) = 1 &&
- test $(git rev-list HEAD -1 | wc -l) = 1 &&
- test $(git rev-list HEAD -n1 | wc -l) = 1 &&
- test $(git rev-list HEAD -n 1 | wc -l) = 1
+ test_must_fail git rev-list -1q HEAD 2>error &&
+ grep "not an integer" error &&
+ test_must_fail git rev-list --1 HEAD &&
+ test_must_fail git rev-list -n 1q HEAD 2>error &&
+ grep "not an integer" error &&
+
+ test_stdout_line_count = 1 git rev-list HEAD --max-count=1 &&
+ test_stdout_line_count = 1 git rev-list HEAD -1 &&
+ test_stdout_line_count = 1 git rev-list HEAD -n1 &&
+ test_stdout_line_count = 1 git rev-list HEAD -n 1 &&
+ test_stdout_line_count = 5 git rev-list HEAD -n -1
'
test_expect_success '--skip' '
- test $(git rev-list HEAD --skip=0 | wc -l) = 5 &&
- test $(git rev-list HEAD --skip=3 | wc -l) = 2 &&
- test $(git rev-list HEAD --skip=5 | wc -l) = 0 &&
- test $(git rev-list HEAD --skip=10 | wc -l) = 0
+ test_must_fail git rev-list --skip 1q HEAD 2>error &&
+ grep "not an integer" error &&
+
+ test_stdout_line_count = 5 git rev-list HEAD --skip=0 &&
+ test_stdout_line_count = 2 git rev-list HEAD --skip=3 &&
+ test_stdout_line_count = 0 git rev-list HEAD --skip=5 &&
+ test_stdout_line_count = 0 git rev-list HEAD --skip=10
'
test_expect_success '--skip --max-count' '
- test $(git rev-list HEAD --skip=0 --max-count=0 | wc -l) = 0 &&
- test $(git rev-list HEAD --skip=0 --max-count=10 | wc -l) = 5 &&
- test $(git rev-list HEAD --skip=3 --max-count=0 | wc -l) = 0 &&
- test $(git rev-list HEAD --skip=3 --max-count=1 | wc -l) = 1 &&
- test $(git rev-list HEAD --skip=3 --max-count=2 | wc -l) = 2 &&
- test $(git rev-list HEAD --skip=3 --max-count=10 | wc -l) = 2 &&
- test $(git rev-list HEAD --skip=5 --max-count=10 | wc -l) = 0 &&
- test $(git rev-list HEAD --skip=10 --max-count=10 | wc -l) = 0
+ test_stdout_line_count = 0 git rev-list HEAD --skip=0 --max-count=0 &&
+ test_stdout_line_count = 5 git rev-list HEAD --skip=0 --max-count=10 &&
+ test_stdout_line_count = 0 git rev-list HEAD --skip=3 --max-count=0 &&
+ test_stdout_line_count = 1 git rev-list HEAD --skip=3 --max-count=1 &&
+ test_stdout_line_count = 2 git rev-list HEAD --skip=3 --max-count=2 &&
+ test_stdout_line_count = 2 git rev-list HEAD --skip=3 --max-count=10 &&
+ test_stdout_line_count = 0 git rev-list HEAD --skip=5 --max-count=10 &&
+ test_stdout_line_count = 0 git rev-list HEAD --skip=10 --max-count=10
'
test_done
diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh
index 41d0ca0..573eb97 100755
--- a/t/t6006-rev-list-format.sh
+++ b/t/t6006-rev-list-format.sh
@@ -493,7 +493,7 @@ test_expect_success 'empty email' '
test_tick &&
C=$(GIT_AUTHOR_EMAIL= git commit-tree HEAD^{tree} </dev/null) &&
A=$(git show --pretty=format:%an,%ae,%ad%n -s $C) &&
- verbose test "$A" = "$GIT_AUTHOR_NAME,,Thu Apr 7 15:14:13 2005 -0700"
+ test "$A" = "$GIT_AUTHOR_NAME,,Thu Apr 7 15:14:13 2005 -0700"
'
test_expect_success 'del LF before empty (1)' '
diff --git a/t/t6007-rev-list-cherry-pick-file.sh b/t/t6007-rev-list-cherry-pick-file.sh
index aebe4b6..6f3e543 100755
--- a/t/t6007-rev-list-cherry-pick-file.sh
+++ b/t/t6007-rev-list-cherry-pick-file.sh
@@ -58,7 +58,7 @@ EOF
test_expect_success '--left-right' '
git rev-list --left-right B...C > actual &&
- git name-rev --stdin --name-only --refs="*tags/*" \
+ git name-rev --annotate-stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
test_cmp expect actual.named
'
@@ -78,14 +78,14 @@ EOF
test_expect_success '--cherry-pick bar does not come up empty' '
git rev-list --left-right --cherry-pick B...C -- bar > actual &&
- git name-rev --stdin --name-only --refs="*tags/*" \
+ git name-rev --annotate-stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
test_cmp expect actual.named
'
test_expect_success 'bar does not come up empty' '
git rev-list --left-right B...C -- bar > actual &&
- git name-rev --stdin --name-only --refs="*tags/*" \
+ git name-rev --annotate-stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
test_cmp expect actual.named
'
@@ -97,14 +97,14 @@ EOF
test_expect_success '--cherry-pick bar does not come up empty (II)' '
git rev-list --left-right --cherry-pick F...E -- bar > actual &&
- git name-rev --stdin --name-only --refs="*tags/*" \
+ git name-rev --annotate-stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
test_cmp expect actual.named
'
test_expect_success 'name-rev multiple --refs combine inclusive' '
git rev-list --left-right --cherry-pick F...E -- bar >actual &&
- git name-rev --stdin --name-only --refs="*tags/F" --refs="*tags/E" \
+ git name-rev --annotate-stdin --name-only --refs="*tags/F" --refs="*tags/E" \
<actual >actual.named &&
test_cmp expect actual.named
'
@@ -116,7 +116,7 @@ EOF
test_expect_success 'name-rev --refs excludes non-matched patterns' '
git rev-list --left-right --right-only --cherry-pick F...E -- bar >>expect &&
git rev-list --left-right --cherry-pick F...E -- bar >actual &&
- git name-rev --stdin --name-only --refs="*tags/F" \
+ git name-rev --annotate-stdin --name-only --refs="*tags/F" \
<actual >actual.named &&
test_cmp expect actual.named
'
@@ -128,14 +128,14 @@ EOF
test_expect_success 'name-rev --exclude excludes matched patterns' '
git rev-list --left-right --right-only --cherry-pick F...E -- bar >>expect &&
git rev-list --left-right --cherry-pick F...E -- bar >actual &&
- git name-rev --stdin --name-only --refs="*tags/*" --exclude="*E" \
+ git name-rev --annotate-stdin --name-only --refs="*tags/*" --exclude="*E" \
<actual >actual.named &&
test_cmp expect actual.named
'
test_expect_success 'name-rev --no-refs clears the refs list' '
git rev-list --left-right --cherry-pick F...E -- bar >expect &&
- git name-rev --stdin --name-only --refs="*tags/F" --refs="*tags/E" --no-refs --refs="*tags/G" \
+ git name-rev --annotate-stdin --name-only --refs="*tags/F" --refs="*tags/E" --no-refs --refs="*tags/G" \
<expect >actual &&
test_cmp expect actual
'
@@ -149,7 +149,7 @@ EOF
test_expect_success '--cherry-mark' '
git rev-list --cherry-mark F...E -- bar > actual &&
- git name-rev --stdin --name-only --refs="*tags/*" \
+ git name-rev --annotate-stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
test_cmp expect actual.named
'
@@ -163,7 +163,7 @@ EOF
test_expect_success '--cherry-mark --left-right' '
git rev-list --cherry-mark --left-right F...E -- bar > actual &&
- git name-rev --stdin --name-only --refs="*tags/*" \
+ git name-rev --annotate-stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
test_cmp expect actual.named
'
@@ -174,14 +174,14 @@ EOF
test_expect_success '--cherry-pick --right-only' '
git rev-list --cherry-pick --right-only F...E -- bar > actual &&
- git name-rev --stdin --name-only --refs="*tags/*" \
+ git name-rev --annotate-stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
test_cmp expect actual.named
'
test_expect_success '--cherry-pick --left-only' '
git rev-list --cherry-pick --left-only E...F -- bar > actual &&
- git name-rev --stdin --name-only --refs="*tags/*" \
+ git name-rev --annotate-stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
test_cmp expect actual.named
'
@@ -193,7 +193,7 @@ EOF
test_expect_success '--cherry' '
git rev-list --cherry F...E -- bar > actual &&
- git name-rev --stdin --name-only --refs="*tags/*" \
+ git name-rev --annotate-stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
test_cmp expect actual.named
'
diff --git a/t/t6008-rev-list-submodule.sh b/t/t6008-rev-list-submodule.sh
index 3153a0d..2cdef6f 100755
--- a/t/t6008-rev-list-submodule.sh
+++ b/t/t6008-rev-list-submodule.sh
@@ -8,6 +8,7 @@ test_description='git rev-list involving submodules that this repo has'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -26,7 +27,7 @@ test_expect_success 'setup' '
: > super-file &&
git add super-file &&
- git submodule add "$(pwd)" sub &&
+ git -c protocol.file.allow=always submodule add "$(pwd)" sub &&
git symbolic-ref HEAD refs/heads/super &&
test_tick &&
git commit -m super-initial &&
diff --git a/t/t6009-rev-list-parent.sh b/t/t6009-rev-list-parent.sh
index 63fa7c8..91db8fa 100755
--- a/t/t6009-rev-list-parent.sh
+++ b/t/t6009-rev-list-parent.sh
@@ -5,6 +5,7 @@ test_description='ancestor culling and limiting by parent number'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
check_revlist () {
@@ -62,6 +63,17 @@ test_expect_success 'setup roots, merges and octopuses' '
git checkout main
'
+test_expect_success 'parse --max-parents & --min-parents' '
+ test_must_fail git rev-list --max-parents=1q HEAD 2>error &&
+ grep "not an integer" error &&
+
+ test_must_fail git rev-list --min-parents=1q HEAD 2>error &&
+ grep "not an integer" error &&
+
+ git rev-list --max-parents=1 --min-parents=1 HEAD &&
+ git rev-list --max-parents=-1 --min-parents=-1 HEAD
+'
+
test_expect_success 'rev-list roots' '
check_revlist "--max-parents=0" one five
@@ -124,7 +136,7 @@ test_expect_success 'dodecapus' '
git checkout -b root$i five &&
test_commit $i &&
roots="$roots root$i" ||
- return
+ return 1
done &&
git checkout main &&
test_tick &&
@@ -142,8 +154,8 @@ test_expect_success 'ancestors with the same commit time' '
test_tick_keep=$test_tick &&
for i in 1 2 3 4 5 6 7 8; do
- test_tick=$test_tick_keep
- test_commit t$i
+ test_tick=$test_tick_keep &&
+ test_commit t$i || return 1
done &&
git rev-list t1^! --not t$i >result &&
test_must_be_empty result
diff --git a/t/t6011-rev-list-with-bad-commit.sh b/t/t6011-rev-list-with-bad-commit.sh
index bad02cf..b2e422c 100755
--- a/t/t6011-rev-list-with-bad-commit.sh
+++ b/t/t6011-rev-list-with-bad-commit.sh
@@ -2,6 +2,7 @@
test_description='git rev-list should notice bad commits'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Note:
diff --git a/t/t6012-rev-list-simplify.sh b/t/t6012-rev-list-simplify.sh
index 4f7fa8b..de1e87f 100755
--- a/t/t6012-rev-list-simplify.sh
+++ b/t/t6012-rev-list-simplify.sh
@@ -12,17 +12,18 @@ note () {
}
unnote () {
- git name-rev --tags --stdin | sed -e "s|$OID_REGEX (tags/\([^)]*\)) |\1 |g"
+ test_when_finished "rm -f tmp" &&
+ git name-rev --tags --annotate-stdin >tmp &&
+ sed -e "s|$OID_REGEX (tags/\([^)]*\)) |\1 |g" <tmp
}
#
-# Create a test repo with interesting commit graph:
+# Create a test repo with an interesting commit graph:
#
-# A--B----------G--H--I--K--L
-# \ \ / /
-# \ \ / /
-# C------E---F J
-# \_/
+# A-----B-----G--H--I--K--L
+# \ \ / /
+# \ \ / /
+# C--D--E--F J
#
# The commits are laid out from left-to-right starting with
# the root commit A and terminating at the tip commit L.
@@ -112,8 +113,8 @@ check_outcome () {
shift &&
param="$*" &&
test_expect_$outcome "log $param" '
- git log --pretty="$FMT" --parents $param |
- unnote >actual &&
+ git log --pretty="$FMT" --parents $param >out &&
+ unnote >actual <out &&
sed -e "s/^.* \([^ ]*\) .*/\1/" >check <actual &&
test_cmp expect check
'
@@ -142,11 +143,18 @@ check_result 'I B A' --author-date-order -- file
check_result 'H' --first-parent -- another-file
check_result 'H' --first-parent --topo-order -- another-file
+check_result 'L K I H G B A' --first-parent L
+check_result 'F E D C' --exclude-first-parent-only F ^L
+check_result '' F ^L
+check_result 'L K I H G J' L ^F
+check_result 'L K I H G B J' --exclude-first-parent-only L ^F
+check_result 'L K I H G B' --exclude-first-parent-only --first-parent L ^F
+
check_result 'E C B A' --full-history E -- lost
test_expect_success 'full history simplification without parent' '
printf "%s\n" E C B A >expect &&
- git log --pretty="$FMT" --full-history E -- lost |
- unnote >actual &&
+ git log --pretty="$FMT" --full-history E -- lost >out &&
+ unnote >actual <out &&
sed -e "s/^.* \([^ ]*\) .*/\1/" >check <actual &&
test_cmp expect check
'
diff --git a/t/t6014-rev-list-all.sh b/t/t6014-rev-list-all.sh
index c9bedd2..16b8bd1 100755
--- a/t/t6014-rev-list-all.sh
+++ b/t/t6014-rev-list-all.sh
@@ -2,6 +2,7 @@
test_description='--all includes detached HEADs'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
diff --git a/t/t6017-rev-list-stdin.sh b/t/t6017-rev-list-stdin.sh
index 0516251..4821b90 100755
--- a/t/t6017-rev-list-stdin.sh
+++ b/t/t6017-rev-list-stdin.sh
@@ -48,7 +48,9 @@ test_expect_success setup '
git add file-$i &&
test_tick &&
git commit -m side-$i || exit
- done
+ done &&
+
+ git update-ref refs/heads/-dashed-branch HEAD
)
'
@@ -60,6 +62,13 @@ check side-1 ^side-7 -- file-2
check side-3 ^side-4 -- file-3
check side-3 ^side-2
check side-3 ^side-2 -- file-1
+check --all
+check --all --not --branches
+check --glob=refs/heads
+check --glob=refs/heads --
+check --glob=refs/heads -- file-1
+check --end-of-options -dashed-branch
+check --all --not refs/heads/main
test_expect_success 'not only --stdin' '
cat >expect <<-EOF &&
@@ -78,4 +87,65 @@ test_expect_success 'not only --stdin' '
test_cmp expect actual
'
+test_expect_success 'pseudo-opt with missing value' '
+ cat >input <<-EOF &&
+ --glob
+ refs/heads
+ EOF
+
+ cat >expect <<-EOF &&
+ fatal: Option ${SQ}--glob${SQ} requires a value
+ EOF
+
+ test_must_fail git rev-list --stdin <input 2>error &&
+ test_cmp expect error
+'
+
+test_expect_success 'pseudo-opt with invalid value' '
+ cat >input <<-EOF &&
+ --no-walk=garbage
+ EOF
+
+ cat >expect <<-EOF &&
+ error: invalid argument to --no-walk
+ fatal: invalid option ${SQ}--no-walk=garbage${SQ} in --stdin mode
+ EOF
+
+ test_must_fail git rev-list --stdin <input 2>error &&
+ test_cmp expect error
+'
+
+test_expect_success 'unknown option without --end-of-options' '
+ cat >input <<-EOF &&
+ -dashed-branch
+ EOF
+
+ cat >expect <<-EOF &&
+ fatal: invalid option ${SQ}-dashed-branch${SQ} in --stdin mode
+ EOF
+
+ test_must_fail git rev-list --stdin <input 2>error &&
+ test_cmp expect error
+'
+
+test_expect_success '--not on command line does not influence revisions read via --stdin' '
+ cat >input <<-EOF &&
+ refs/heads/main
+ EOF
+ git rev-list refs/heads/main >expect &&
+
+ git rev-list refs/heads/main --not --stdin <input >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--not via stdin does not influence revisions from command line' '
+ cat >input <<-EOF &&
+ --not
+ EOF
+ git rev-list refs/heads/main >expect &&
+
+ git rev-list refs/heads/main --stdin refs/heads/main <input >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh
index 24b34ad..3b181f7 100755
--- a/t/t6018-rev-list-glob.sh
+++ b/t/t6018-rev-list-glob.sh
@@ -5,6 +5,7 @@ test_description='rev-list/rev-parse --glob'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
commit () {
@@ -186,6 +187,44 @@ test_expect_success 'rev-parse --exclude=ref with --remotes=glob' '
compare rev-parse "--exclude=upstream/x --remotes=upstream/*" "upstream/one upstream/two"
'
+for section in fetch receive uploadpack
+do
+ test_expect_success "rev-parse --exclude-hidden=$section with --all" '
+ compare "-c transfer.hideRefs=refs/remotes/ rev-parse" "--branches --tags" "--exclude-hidden=$section --all"
+ '
+
+ test_expect_success "rev-parse --exclude-hidden=$section with --all" '
+ compare "-c transfer.hideRefs=refs/heads/subspace/ rev-parse" "--exclude=refs/heads/subspace/* --all" "--exclude-hidden=$section --all"
+ '
+
+ test_expect_success "rev-parse --exclude-hidden=$section with --glob" '
+ compare "-c transfer.hideRefs=refs/heads/subspace/ rev-parse" "--exclude=refs/heads/subspace/* --glob=refs/heads/*" "--exclude-hidden=$section --glob=refs/heads/*"
+ '
+
+ test_expect_success "rev-parse --exclude-hidden=$section can be passed once per pseudo-ref" '
+ compare "-c transfer.hideRefs=refs/remotes/ rev-parse" "--branches --tags --branches --tags" "--exclude-hidden=$section --all --exclude-hidden=$section --all"
+ '
+
+ test_expect_success "rev-parse --exclude-hidden=$section can only be passed once per pseudo-ref" '
+ echo "fatal: --exclude-hidden= passed more than once" >expected &&
+ test_must_fail git rev-parse --exclude-hidden=$section --exclude-hidden=$section 2>err &&
+ test_cmp expected err
+ '
+
+ for pseudoopt in branches tags remotes
+ do
+ test_expect_success "rev-parse --exclude-hidden=$section fails with --$pseudoopt" '
+ test_must_fail git rev-parse --exclude-hidden=$section --$pseudoopt 2>err &&
+ test_grep "error: options .--exclude-hidden. and .--$pseudoopt. cannot be used together" err
+ '
+
+ test_expect_success "rev-parse --exclude-hidden=$section fails with --$pseudoopt=pattern" '
+ test_must_fail git rev-parse --exclude-hidden=$section --$pseudoopt=pattern 2>err &&
+ test_grep "error: options .--exclude-hidden. and .--$pseudoopt. cannot be used together" err
+ '
+ done
+done
+
test_expect_success 'rev-list --exclude=glob with --branches=glob' '
compare rev-list "--exclude=subspace-* --branches=sub*" "subspace/one subspace/two"
'
diff --git a/t/t6019-rev-list-ancestry-path.sh b/t/t6019-rev-list-ancestry-path.sh
index 20adbec..738da23 100755
--- a/t/t6019-rev-list-ancestry-path.sh
+++ b/t/t6019-rev-list-ancestry-path.sh
@@ -8,8 +8,13 @@ test_description='--ancestry-path'
# / \
# A-------K---------------L--M
#
-# D..M == E F G H I J K L M
-# --ancestry-path D..M == E F H I J L M
+# D..M == E F G H I J K L M
+# --ancestry-path D..M == E F H I J L M
+# --ancestry-path=F D..M == E F J L M
+# --ancestry-path=G D..M == G H I J L M
+# --ancestry-path=H D..M == E G H I J L M
+# --ancestry-path=K D..M == K L M
+# --ancestry-path=K --ancestry-path=F D..M == E F J K L M
#
# D..M -- M.t == M
# --ancestry-path D..M -- M.t == M
@@ -50,73 +55,41 @@ test_expect_success setup '
test_commit M
'
-test_expect_success 'rev-list D..M' '
- for c in E F G H I J K L M; do echo $c; done >expect &&
- git rev-list --format=%s D..M |
- sed -e "/^commit /d" |
- sort >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'rev-list --ancestry-path D..M' '
- for c in E F H I J L M; do echo $c; done >expect &&
- git rev-list --ancestry-path --format=%s D..M |
- sed -e "/^commit /d" |
- sort >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'rev-list D..M -- M.t' '
- echo M >expect &&
- git rev-list --format=%s D..M -- M.t |
- sed -e "/^commit /d" >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'rev-list --ancestry-path D..M -- M.t' '
- echo M >expect &&
- git rev-list --ancestry-path --format=%s D..M -- M.t |
- sed -e "/^commit /d" >actual &&
- test_cmp expect actual
-'
+test_ancestry () {
+ args=$1
+ expected=$2
+ test_expect_success "log $args" "
+ test_write_lines $expected >expect &&
+ git log --format=%s $args >raw &&
+
+ if test -n \"$expected\"
+ then
+ sort raw >actual &&
+ test_cmp expect actual
+ else
+ test_must_be_empty raw
+ fi
+ "
+}
-test_expect_success 'rev-list F...I' '
- for c in F G H I; do echo $c; done >expect &&
- git rev-list --format=%s F...I |
- sed -e "/^commit /d" |
- sort >actual &&
- test_cmp expect actual
-'
+test_ancestry "D..M" "E F G H I J K L M"
-test_expect_success 'rev-list --ancestry-path F...I' '
- for c in F H I; do echo $c; done >expect &&
- git rev-list --ancestry-path --format=%s F...I |
- sed -e "/^commit /d" |
- sort >actual &&
- test_cmp expect actual
-'
+test_ancestry "--ancestry-path D..M" "E F H I J L M"
+test_ancestry "--ancestry-path=F D..M" "E F J L M"
+test_ancestry "--ancestry-path=G D..M" "G H I J L M"
+test_ancestry "--ancestry-path=H D..M" "E G H I J L M"
+test_ancestry "--ancestry-path=K D..M" "K L M"
+test_ancestry "--ancestry-path=F --ancestry-path=K D..M" "E F J K L M"
-# G.t is dropped in an "-s ours" merge
-test_expect_success 'rev-list G..M -- G.t' '
- git rev-list --format=%s G..M -- G.t |
- sed -e "/^commit /d" >actual &&
- test_must_be_empty actual
-'
+test_ancestry "D..M -- M.t" "M"
+test_ancestry "--ancestry-path D..M -- M.t" "M"
-test_expect_success 'rev-list --ancestry-path G..M -- G.t' '
- echo L >expect &&
- git rev-list --ancestry-path --format=%s G..M -- G.t |
- sed -e "/^commit /d" >actual &&
- test_cmp expect actual
-'
+test_ancestry "F...I" "F G H I"
+test_ancestry "--ancestry-path F...I" "F H I"
-test_expect_success 'rev-list --ancestry-path --simplify-merges G^..M -- G.t' '
- for c in G L; do echo $c; done >expect &&
- git rev-list --ancestry-path --simplify-merges --format=%s G^..M -- G.t |
- sed -e "/^commit /d" |
- sort >actual &&
- test_cmp expect actual
-'
+test_ancestry "G..M -- G.t" ""
+test_ancestry "--ancestry-path G..M -- G.t" "L"
+test_ancestry "--ancestry-path --simplify-merges G^..M -- G.t" "G L"
# b---bc
# / \ /
diff --git a/t/t6020-bundle-misc.sh b/t/t6020-bundle-misc.sh
index b13e8a5..3e6bcbf 100755
--- a/t/t6020-bundle-misc.sh
+++ b/t/t6020-bundle-misc.sh
@@ -10,6 +10,14 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-bundle.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
+
+for cmd in create verify list-heads unbundle
+do
+ test_expect_success "usage: git bundle $cmd needs an argument" '
+ test_expect_code 129 git bundle $cmd
+ '
+done
# Create a commit or tag and set the variable with the object ID.
test_commit_setvar () {
@@ -122,6 +130,8 @@ format_and_save_expect () {
sed -e 's/Z$//' >expect
}
+HASH_MESSAGE="The bundle uses this hash algorithm: $GIT_DEFAULT_HASH"
+
# (C) (D, pull/1/head, topic/1)
# o --- o
# / \ (L)
@@ -194,11 +204,12 @@ test_expect_success 'create bundle from special rev: main^!' '
git bundle verify special-rev.bdl |
make_user_friendly_and_stable_output >actual &&
- format_and_save_expect <<-\EOF &&
+ format_and_save_expect <<-EOF &&
The bundle contains this ref:
<COMMIT-P> refs/heads/main
The bundle requires this ref:
<COMMIT-O> Z
+ $HASH_MESSAGE
EOF
test_cmp expect actual &&
@@ -215,12 +226,13 @@ test_expect_success 'create bundle with --max-count option' '
git bundle verify max-count.bdl |
make_user_friendly_and_stable_output >actual &&
- format_and_save_expect <<-\EOF &&
+ format_and_save_expect <<-EOF &&
The bundle contains these 2 refs:
<COMMIT-P> refs/heads/main
<TAG-1> refs/tags/v1
The bundle requires this ref:
<COMMIT-O> Z
+ $HASH_MESSAGE
EOF
test_cmp expect actual &&
@@ -240,7 +252,7 @@ test_expect_success 'create bundle with --since option' '
git bundle verify since.bdl |
make_user_friendly_and_stable_output >actual &&
- format_and_save_expect <<-\EOF &&
+ format_and_save_expect <<-EOF &&
The bundle contains these 5 refs:
<COMMIT-P> refs/heads/main
<COMMIT-N> refs/heads/release
@@ -250,6 +262,7 @@ test_expect_success 'create bundle with --since option' '
The bundle requires these 2 refs:
<COMMIT-M> Z
<COMMIT-K> Z
+ $HASH_MESSAGE
EOF
test_cmp expect actual &&
@@ -267,11 +280,12 @@ test_expect_success 'create bundle 1 - no prerequisites' '
EOF
git bundle create stdin-1.bdl --stdin <input &&
- cat >expect <<-\EOF &&
+ format_and_save_expect <<-EOF &&
The bundle contains these 2 refs:
<COMMIT-D> refs/heads/topic/1
<COMMIT-H> refs/heads/topic/2
The bundle records a complete history.
+ $HASH_MESSAGE
EOF
# verify bundle, which has no prerequisites
@@ -308,13 +322,14 @@ test_expect_success 'create bundle 2 - has prerequisites' '
--stdin \
release <input &&
- format_and_save_expect <<-\EOF &&
+ format_and_save_expect <<-EOF &&
The bundle contains this ref:
<COMMIT-N> refs/heads/release
The bundle requires these 3 refs:
<COMMIT-D> Z
<COMMIT-E> Z
<COMMIT-G> Z
+ $HASH_MESSAGE
EOF
git bundle verify 2.bdl |
@@ -367,13 +382,14 @@ test_expect_success 'create bundle 3 - two refs, same object' '
--stdin \
main HEAD <input &&
- format_and_save_expect <<-\EOF &&
+ format_and_save_expect <<-EOF &&
The bundle contains these 2 refs:
<COMMIT-P> refs/heads/main
<COMMIT-P> HEAD
The bundle requires these 2 refs:
<COMMIT-M> Z
<COMMIT-K> Z
+ $HASH_MESSAGE
EOF
git bundle verify 3.bdl |
@@ -409,12 +425,13 @@ test_expect_success 'create bundle 4 - with tags' '
--stdin \
--all <input &&
- cat >expect <<-\EOF &&
+ cat >expect <<-EOF &&
The bundle contains these 3 refs:
<TAG-1> refs/tags/v1
<TAG-2> refs/tags/v2
<TAG-3> refs/tags/v3
The bundle records a complete history.
+ $HASH_MESSAGE
EOF
git bundle verify 4.bdl |
@@ -475,4 +492,163 @@ test_expect_success 'clone from bundle' '
test_cmp expect actual
'
+test_expect_success 'unfiltered bundle with --objects' '
+ git bundle create all-objects.bdl \
+ --all --objects &&
+ git bundle create all.bdl \
+ --all &&
+
+ # Compare the headers of these files.
+ sed -n -e "/^$/q" -e "p" all.bdl >expect &&
+ sed -n -e "/^$/q" -e "p" all-objects.bdl >actual &&
+ test_cmp expect actual
+'
+
+for filter in "blob:none" "tree:0" "tree:1" "blob:limit=100"
+do
+ test_expect_success "filtered bundle: $filter" '
+ test_when_finished rm -rf .git/objects/pack cloned unbundled &&
+ git bundle create partial.bdl \
+ --all \
+ --filter=$filter &&
+
+ git bundle verify partial.bdl >unfiltered &&
+ make_user_friendly_and_stable_output <unfiltered >actual &&
+
+ cat >expect <<-EOF &&
+ The bundle contains these 10 refs:
+ <COMMIT-P> refs/heads/main
+ <COMMIT-N> refs/heads/release
+ <COMMIT-D> refs/heads/topic/1
+ <COMMIT-H> refs/heads/topic/2
+ <COMMIT-D> refs/pull/1/head
+ <COMMIT-G> refs/pull/2/head
+ <TAG-1> refs/tags/v1
+ <TAG-2> refs/tags/v2
+ <TAG-3> refs/tags/v3
+ <COMMIT-P> HEAD
+ The bundle records a complete history.
+ $HASH_MESSAGE
+ The bundle uses this filter: $filter
+ EOF
+ test_cmp expect actual &&
+
+ test_config uploadpack.allowfilter 1 &&
+ test_config uploadpack.allowanysha1inwant 1 &&
+ git clone --no-local --filter=$filter --bare "file://$(pwd)" cloned &&
+
+ git init unbundled &&
+ git -C unbundled bundle unbundle ../partial.bdl >ref-list.txt &&
+ ls unbundled/.git/objects/pack/pack-*.promisor >promisor &&
+ test_line_count = 1 promisor &&
+
+ # Count the same number of reachable objects.
+ reflist=$(git for-each-ref --format="%(objectname)") &&
+ git rev-list --objects --filter=$filter --missing=allow-any \
+ $reflist >expect &&
+ for repo in cloned unbundled
+ do
+ git -C $repo rev-list --objects --missing=allow-any \
+ $reflist >actual &&
+ test_cmp expect actual || return 1
+ done
+ '
+done
+
+# NEEDSWORK: 'git clone --bare' should be able to clone from a filtered
+# bundle, but that requires a change to promisor/filter config options.
+# For now, we fail gracefully with a helpful error. This behavior can be
+# changed in the future to succeed as much as possible.
+test_expect_success 'cloning from filtered bundle has useful error' '
+ git bundle create partial.bdl \
+ --all \
+ --filter=blob:none &&
+ test_must_fail git clone --bare partial.bdl partial 2>err &&
+ grep "cannot clone from filtered bundle" err
+'
+
+test_expect_success 'verify catches unreachable, broken prerequisites' '
+ test_when_finished rm -rf clone-from clone-to &&
+ git init clone-from &&
+ (
+ cd clone-from &&
+ git checkout -b base &&
+ test_commit A &&
+ git checkout -b tip &&
+ git commit --allow-empty -m "will drop by shallow" &&
+ git commit --allow-empty -m "will keep by shallow" &&
+ git commit --allow-empty -m "for bundle, not clone" &&
+ git bundle create tip.bundle tip~1..tip &&
+ git reset --hard HEAD~1 &&
+ git checkout base
+ ) &&
+ BAD_OID=$(git -C clone-from rev-parse tip~1) &&
+ TIP_OID=$(git -C clone-from rev-parse tip) &&
+ git clone --depth=1 --no-single-branch \
+ "file://$(pwd)/clone-from" clone-to &&
+ (
+ cd clone-to &&
+
+ # Set up broken history by removing shallow markers
+ git update-ref -d refs/remotes/origin/tip &&
+ rm .git/shallow &&
+
+ # Verify should fail
+ test_must_fail git bundle verify \
+ ../clone-from/tip.bundle 2>err &&
+ grep "some prerequisite commits .* are not connected" err &&
+ test_line_count = 1 err &&
+
+ # Unbundling should fail
+ test_must_fail git bundle unbundle \
+ ../clone-from/tip.bundle 2>err &&
+ grep "some prerequisite commits .* are not connected" err &&
+ test_line_count = 1 err
+ )
+'
+
+test_expect_success 'bundle progress includes write phase' '
+ GIT_PROGRESS_DELAY=0 \
+ git bundle create --progress out.bundle --all 2>err &&
+ grep 'Writing' err
+'
+
+test_expect_success TTY 'create --quiet disables all bundle progress' '
+ test_terminal env GIT_PROGRESS_DELAY=0 \
+ git bundle create --quiet out.bundle --all 2>err &&
+ test_must_be_empty err
+'
+
+test_expect_success 'bundle progress with --no-quiet' '
+ GIT_PROGRESS_DELAY=0 \
+ git bundle create --no-quiet out.bundle --all 2>err &&
+ grep "%" err
+'
+
+test_expect_success 'read bundle over stdin' '
+ git bundle create some.bundle HEAD &&
+
+ git bundle verify - <some.bundle 2>err &&
+ grep "<stdin> is okay" err &&
+
+ git bundle list-heads some.bundle >expect &&
+ git bundle list-heads - <some.bundle >actual &&
+ test_cmp expect actual &&
+
+ git bundle unbundle some.bundle >expect &&
+ git bundle unbundle - <some.bundle >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'send a bundle to standard output' '
+ git bundle create - --all HEAD >bundle-one &&
+ mkdir -p down &&
+ git -C down bundle create - --all HEAD >bundle-two &&
+ git bundle verify bundle-one &&
+ git bundle verify bundle-two &&
+ git ls-remote bundle-one >expect &&
+ git ls-remote bundle-two >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t6021-rev-list-exclude-hidden.sh b/t/t6021-rev-list-exclude-hidden.sh
new file mode 100755
index 0000000..51df021
--- /dev/null
+++ b/t/t6021-rev-list-exclude-hidden.sh
@@ -0,0 +1,164 @@
+#!/bin/sh
+
+test_description='git rev-list --exclude-hidden test'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit_bulk --id=commit --ref=refs/heads/branch 1 &&
+ COMMIT=$(git rev-parse refs/heads/branch) &&
+ test_commit_bulk --id=tag --ref=refs/tags/lightweight 1 &&
+ TAG=$(git rev-parse refs/tags/lightweight) &&
+ test_commit_bulk --id=hidden --ref=refs/hidden/commit 1 &&
+ HIDDEN=$(git rev-parse refs/hidden/commit) &&
+ test_commit_bulk --id=namespace --ref=refs/namespaces/namespace/refs/namespaced/commit 1 &&
+ NAMESPACE=$(git rev-parse refs/namespaces/namespace/refs/namespaced/commit)
+'
+
+test_expect_success 'invalid section' '
+ echo "fatal: unsupported section for hidden refs: unsupported" >expected &&
+ test_must_fail git rev-list --exclude-hidden=unsupported 2>err &&
+ test_cmp expected err
+'
+
+for section in fetch receive uploadpack
+do
+ test_expect_success "$section: passed multiple times" '
+ echo "fatal: --exclude-hidden= passed more than once" >expected &&
+ test_must_fail git rev-list --exclude-hidden=$section --exclude-hidden=$section 2>err &&
+ test_cmp expected err
+ '
+
+ test_expect_success "$section: without hiddenRefs" '
+ git rev-list --exclude-hidden=$section --all >out &&
+ cat >expected <<-EOF &&
+ $NAMESPACE
+ $HIDDEN
+ $TAG
+ $COMMIT
+ EOF
+ test_cmp expected out
+ '
+
+ test_expect_success "$section: hidden via transfer.hideRefs" '
+ git -c transfer.hideRefs=refs/hidden/ rev-list --exclude-hidden=$section --all >out &&
+ cat >expected <<-EOF &&
+ $NAMESPACE
+ $TAG
+ $COMMIT
+ EOF
+ test_cmp expected out
+ '
+
+ test_expect_success "$section: hidden via $section.hideRefs" '
+ git -c $section.hideRefs=refs/hidden/ rev-list --exclude-hidden=$section --all >out &&
+ cat >expected <<-EOF &&
+ $NAMESPACE
+ $TAG
+ $COMMIT
+ EOF
+ test_cmp expected out
+ '
+
+ test_expect_success "$section: respects both transfer.hideRefs and $section.hideRefs" '
+ git -c transfer.hideRefs=refs/tags/ -c $section.hideRefs=refs/hidden/ rev-list --exclude-hidden=$section --all >out &&
+ cat >expected <<-EOF &&
+ $NAMESPACE
+ $COMMIT
+ EOF
+ test_cmp expected out
+ '
+
+ test_expect_success "$section: negation without hidden refs marks everything as uninteresting" '
+ git rev-list --all --exclude-hidden=$section --not --all >out &&
+ test_must_be_empty out
+ '
+
+ test_expect_success "$section: negation with hidden refs marks them as interesting" '
+ git -c transfer.hideRefs=refs/hidden/ rev-list --all --exclude-hidden=$section --not --all >out &&
+ cat >expected <<-EOF &&
+ $HIDDEN
+ EOF
+ test_cmp expected out
+ '
+
+ test_expect_success "$section: hidden refs and excludes work together" '
+ git -c transfer.hideRefs=refs/hidden/ rev-list --exclude=refs/tags/* --exclude-hidden=$section --all >out &&
+ cat >expected <<-EOF &&
+ $NAMESPACE
+ $COMMIT
+ EOF
+ test_cmp expected out
+ '
+
+ test_expect_success "$section: excluded hidden refs get reset" '
+ git -c transfer.hideRefs=refs/ rev-list --exclude-hidden=$section --all --all >out &&
+ cat >expected <<-EOF &&
+ $NAMESPACE
+ $HIDDEN
+ $TAG
+ $COMMIT
+ EOF
+ test_cmp expected out
+ '
+
+ test_expect_success "$section: excluded hidden refs can be used with multiple pseudo-refs" '
+ git -c transfer.hideRefs=refs/ rev-list --exclude-hidden=$section --all --exclude-hidden=$section --all >out &&
+ test_must_be_empty out
+ '
+
+ test_expect_success "$section: works with --glob" '
+ git -c transfer.hideRefs=refs/hidden/ rev-list --exclude-hidden=$section --glob=refs/h* >out &&
+ cat >expected <<-EOF &&
+ $COMMIT
+ EOF
+ test_cmp expected out
+ '
+
+ test_expect_success "$section: operates on stripped refs by default" '
+ GIT_NAMESPACE=namespace git -c transfer.hideRefs=refs/namespaced/ rev-list --exclude-hidden=$section --all >out &&
+ cat >expected <<-EOF &&
+ $HIDDEN
+ $TAG
+ $COMMIT
+ EOF
+ test_cmp expected out
+ '
+
+ test_expect_success "$section: does not hide namespace by default" '
+ GIT_NAMESPACE=namespace git -c transfer.hideRefs=refs/namespaces/namespace/ rev-list --exclude-hidden=$section --all >out &&
+ cat >expected <<-EOF &&
+ $NAMESPACE
+ $HIDDEN
+ $TAG
+ $COMMIT
+ EOF
+ test_cmp expected out
+ '
+
+ test_expect_success "$section: can operate on unstripped refs" '
+ GIT_NAMESPACE=namespace git -c transfer.hideRefs=^refs/namespaces/namespace/ rev-list --exclude-hidden=$section --all >out &&
+ cat >expected <<-EOF &&
+ $HIDDEN
+ $TAG
+ $COMMIT
+ EOF
+ test_cmp expected out
+ '
+
+ for pseudoopt in remotes branches tags
+ do
+ test_expect_success "$section: fails with --$pseudoopt" '
+ test_must_fail git rev-list --exclude-hidden=$section --$pseudoopt 2>err &&
+ test_grep "error: options .--exclude-hidden. and .--$pseudoopt. cannot be used together" err
+ '
+
+ test_expect_success "$section: fails with --$pseudoopt=pattern" '
+ test_must_fail git rev-list --exclude-hidden=$section --$pseudoopt=pattern 2>err &&
+ test_grep "error: options .--exclude-hidden. and .--$pseudoopt. cannot be used together" err
+ '
+ done
+done
+
+test_done
diff --git a/t/t6022-rev-list-missing.sh b/t/t6022-rev-list-missing.sh
new file mode 100755
index 0000000..127180e
--- /dev/null
+++ b/t/t6022-rev-list-missing.sh
@@ -0,0 +1,149 @@
+#!/bin/sh
+
+test_description='handling of missing objects in rev-list'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+# We setup the repository with two commits, this way HEAD is always
+# available and we can hide commit 1.
+test_expect_success 'create repository and alternate directory' '
+ test_commit 1 &&
+ test_commit 2 &&
+ test_commit 3 &&
+ git tag -m "tag message" annot_tag HEAD~1 &&
+ git tag regul_tag HEAD~1 &&
+ git branch a_branch HEAD~1
+'
+
+# We manually corrupt the repository, which means that the commit-graph may
+# contain references to already-deleted objects. We thus need to enable
+# commit-graph paranoia to not returned these deleted commits from the graph.
+GIT_COMMIT_GRAPH_PARANOIA=true
+export GIT_COMMIT_GRAPH_PARANOIA
+
+for obj in "HEAD~1" "HEAD~1^{tree}" "HEAD:1.t"
+do
+ test_expect_success "rev-list --missing=error fails with missing object $obj" '
+ oid="$(git rev-parse $obj)" &&
+ path=".git/objects/$(test_oid_to_path $oid)" &&
+
+ mv "$path" "$path.hidden" &&
+ test_when_finished "mv $path.hidden $path" &&
+
+ test_must_fail git rev-list --missing=error --objects \
+ --no-object-names HEAD
+ '
+done
+
+for obj in "HEAD~1" "HEAD~1^{tree}" "HEAD:1.t"
+do
+ for action in "allow-any" "print"
+ do
+ test_expect_success "rev-list --missing=$action with missing $obj" '
+ oid="$(git rev-parse $obj)" &&
+ path=".git/objects/$(test_oid_to_path $oid)" &&
+
+ # Before the object is made missing, we use rev-list to
+ # get the expected oids.
+ git rev-list --objects --no-object-names \
+ HEAD ^$obj >expect.raw &&
+
+ # Blobs are shared by all commits, so even though a commit/tree
+ # might be skipped, its blob must be accounted for.
+ if test $obj != "HEAD:1.t"
+ then
+ echo $(git rev-parse HEAD:1.t) >>expect.raw &&
+ echo $(git rev-parse HEAD:2.t) >>expect.raw
+ fi &&
+
+ mv "$path" "$path.hidden" &&
+ test_when_finished "mv $path.hidden $path" &&
+
+ git rev-list --missing=$action --objects --no-object-names \
+ HEAD >actual.raw &&
+
+ # When the action is to print, we should also add the missing
+ # oid to the expect list.
+ case $action in
+ allow-any)
+ ;;
+ print)
+ grep ?$oid actual.raw &&
+ echo ?$oid >>expect.raw
+ ;;
+ esac &&
+
+ sort actual.raw >actual &&
+ sort expect.raw >expect &&
+ test_cmp expect actual
+ '
+ done
+done
+
+for missing_tip in "annot_tag" "regul_tag" "a_branch" "HEAD~1" "HEAD~1^{tree}" "HEAD:1.t"
+do
+ # We want to check that things work when both
+ # - all the tips passed are missing (case existing_tip = ""), and
+ # - there is one missing tip and one existing tip (case existing_tip = "HEAD")
+ for existing_tip in "" "HEAD"
+ do
+ for action in "allow-any" "print"
+ do
+ test_expect_success "--missing=$action with tip '$missing_tip' missing and tip '$existing_tip'" '
+ # Before the object is made missing, we use rev-list to
+ # get the expected oids.
+ if test "$existing_tip" = "HEAD"
+ then
+ git rev-list --objects --no-object-names \
+ HEAD ^$missing_tip >expect.raw
+ else
+ >expect.raw
+ fi &&
+
+ # Blobs are shared by all commits, so even though a commit/tree
+ # might be skipped, its blob must be accounted for.
+ if test "$existing_tip" = "HEAD" && test $missing_tip != "HEAD:1.t"
+ then
+ echo $(git rev-parse HEAD:1.t) >>expect.raw &&
+ echo $(git rev-parse HEAD:2.t) >>expect.raw
+ fi &&
+
+ missing_oid="$(git rev-parse $missing_tip)" &&
+
+ if test "$missing_tip" = "annot_tag"
+ then
+ oid="$(git rev-parse $missing_tip^{commit})" &&
+ echo "$missing_oid" >>expect.raw
+ else
+ oid="$missing_oid"
+ fi &&
+
+ path=".git/objects/$(test_oid_to_path $oid)" &&
+
+ mv "$path" "$path.hidden" &&
+ test_when_finished "mv $path.hidden $path" &&
+
+ git rev-list --missing=$action --objects --no-object-names \
+ $missing_oid $existing_tip >actual.raw &&
+
+ # When the action is to print, we should also add the missing
+ # oid to the expect list.
+ case $action in
+ allow-any)
+ ;;
+ print)
+ grep ?$oid actual.raw &&
+ echo ?$oid >>expect.raw
+ ;;
+ esac &&
+
+ sort actual.raw >actual &&
+ sort expect.raw >expect &&
+ test_cmp expect actual
+ '
+ done
+ done
+done
+
+test_done
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index a1baf4e..cdc0270 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -34,6 +34,36 @@ HASH2=
HASH3=
HASH4=
+test_bisect_usage () {
+ local code="$1" &&
+ shift &&
+ cat >expect &&
+ test_expect_code $code "$@" >out 2>actual &&
+ test_must_be_empty out &&
+ test_cmp expect actual
+}
+
+test_expect_success 'bisect usage' "
+ test_bisect_usage 1 git bisect reset extra1 extra2 <<-\EOF &&
+ error: 'git bisect reset' requires either no argument or a commit
+ EOF
+ test_bisect_usage 1 git bisect terms extra1 extra2 <<-\EOF &&
+ error: 'git bisect terms' requires 0 or 1 argument
+ EOF
+ test_bisect_usage 1 git bisect next extra1 <<-\EOF &&
+ error: 'git bisect next' requires 0 arguments
+ EOF
+ test_bisect_usage 1 git bisect log extra1 <<-\EOF &&
+ error: We are not bisecting.
+ EOF
+ test_bisect_usage 1 git bisect replay <<-\EOF &&
+ error: no logfile given
+ EOF
+ test_bisect_usage 1 git bisect run <<-\EOF
+ error: 'git bisect run' failed: no command provided.
+ EOF
+"
+
test_expect_success 'set up basic repo with 1 file (hello) and 4 commits' '
add_line_into_file "1: Hello World" hello &&
HASH1=$(git rev-parse --verify HEAD) &&
@@ -92,6 +122,29 @@ test_expect_success 'bisect start without -- takes unknown arg as pathspec' '
grep bar ".git/BISECT_NAMES"
'
+test_expect_success 'bisect reset: back in a branch checked out also elsewhere' '
+ echo "shared" > branch.expect &&
+ test_bisect_reset() {
+ git -C $1 bisect start &&
+ git -C $1 bisect good $HASH1 &&
+ git -C $1 bisect bad $HASH3 &&
+ git -C $1 bisect reset &&
+ git -C $1 branch --show-current > branch.output &&
+ cmp branch.expect branch.output
+ } &&
+ test_when_finished "
+ git worktree remove wt1 &&
+ git worktree remove wt2 &&
+ git branch -d shared
+ " &&
+ git worktree add wt1 -b shared &&
+ git worktree add wt2 -f shared &&
+ # we test in both worktrees to ensure that works
+ # as expected with "first" and "next" worktrees
+ test_bisect_reset wt1 &&
+ test_bisect_reset wt2
+'
+
test_expect_success 'bisect reset: back in the main branch' '
git bisect reset &&
echo "* main" > branch.expect &&
@@ -117,6 +170,12 @@ test_expect_success 'bisect reset when not bisecting' '
cmp branch.expect branch.output
'
+test_expect_success 'bisect reset cleans up even when not bisecting' '
+ echo garbage >.git/BISECT_LOG &&
+ git bisect reset &&
+ test_path_is_missing .git/BISECT_LOG
+'
+
test_expect_success 'bisect reset removes packed refs' '
git bisect reset &&
git bisect start &&
@@ -167,7 +226,7 @@ test_expect_success 'bisect start: existing ".git/BISECT_START" not modified if
cp .git/BISECT_START saved &&
test_must_fail git bisect start $HASH4 foo -- &&
git branch > branch.output &&
- test_i18ngrep "* (no branch, bisect started on other)" branch.output > /dev/null &&
+ test_grep "* (no branch, bisect started on other)" branch.output > /dev/null &&
test_cmp saved .git/BISECT_START
'
test_expect_success 'bisect start: no ".git/BISECT_START" if mistaken rev' '
@@ -252,6 +311,124 @@ test_expect_success 'bisect skip: with commit both bad and skipped' '
grep $HASH4 my_bisect_log.txt
'
+test_bisect_run_args () {
+ test_when_finished "rm -f run.sh actual" &&
+ >actual &&
+ cat >expect.args &&
+ cat <&6 >expect.out &&
+ cat <&7 >expect.err &&
+ write_script run.sh <<-\EOF &&
+ while test $# != 0
+ do
+ echo "<$1>" &&
+ shift
+ done >actual.args
+ EOF
+
+ test_when_finished "git bisect reset" &&
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ git bisect run ./run.sh $@ >actual.out.raw 2>actual.err &&
+ # Prune just the log output
+ sed -n \
+ -e '/^Author:/d' \
+ -e '/^Date:/d' \
+ -e '/^$/d' \
+ -e '/^commit /d' \
+ -e '/^ /d' \
+ -e 'p' \
+ <actual.out.raw >actual.out &&
+ test_cmp expect.out actual.out &&
+ test_cmp expect.err actual.err &&
+ test_cmp expect.args actual.args
+}
+
+test_expect_success 'git bisect run: args, stdout and stderr with no arguments' "
+ test_bisect_run_args <<-'EOF_ARGS' 6<<-EOF_OUT 7<<-'EOF_ERR'
+ EOF_ARGS
+ running './run.sh'
+ $HASH4 is the first bad commit
+ bisect found first bad commit
+ EOF_OUT
+ EOF_ERR
+"
+
+test_expect_success 'git bisect run: args, stdout and stderr: "--" argument' "
+ test_bisect_run_args -- <<-'EOF_ARGS' 6<<-EOF_OUT 7<<-'EOF_ERR'
+ <-->
+ EOF_ARGS
+ running './run.sh' '--'
+ $HASH4 is the first bad commit
+ bisect found first bad commit
+ EOF_OUT
+ EOF_ERR
+"
+
+test_expect_success 'git bisect run: args, stdout and stderr: "--log foo --no-log bar" arguments' "
+ test_bisect_run_args --log foo --no-log bar <<-'EOF_ARGS' 6<<-EOF_OUT 7<<-'EOF_ERR'
+ <--log>
+ <foo>
+ <--no-log>
+ <bar>
+ EOF_ARGS
+ running './run.sh' '--log' 'foo' '--no-log' 'bar'
+ $HASH4 is the first bad commit
+ bisect found first bad commit
+ EOF_OUT
+ EOF_ERR
+"
+
+test_expect_success 'git bisect run: args, stdout and stderr: "--bisect-start" argument' "
+ test_bisect_run_args --bisect-start <<-'EOF_ARGS' 6<<-EOF_OUT 7<<-'EOF_ERR'
+ <--bisect-start>
+ EOF_ARGS
+ running './run.sh' '--bisect-start'
+ $HASH4 is the first bad commit
+ bisect found first bad commit
+ EOF_OUT
+ EOF_ERR
+"
+
+test_expect_success 'git bisect run: negative exit code' "
+ write_script fail.sh <<-'EOF' &&
+ exit 255
+ EOF
+ cat <<-'EOF' >expect &&
+ bisect run failed: exit code -1 from './fail.sh' is < 0 or >= 128
+ EOF
+ test_when_finished 'git bisect reset' &&
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ ! git bisect run ./fail.sh 2>err &&
+ sed -En 's/.*(bisect.*code) (-?[0-9]+) (from.*)/\1 -1 \3/p' err >actual &&
+ test_cmp expect actual
+"
+
+test_expect_success 'git bisect run: unable to verify on good' "
+ write_script fail.sh <<-'EOF' &&
+ head=\$(git rev-parse --verify HEAD)
+ good=\$(git rev-parse --verify $HASH1)
+ if test "\$head" = "\$good"
+ then
+ exit 255
+ else
+ exit 127
+ fi
+ EOF
+ cat <<-'EOF' >expect &&
+ unable to verify './fail.sh' on good revision
+ EOF
+ test_when_finished 'git bisect reset' &&
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ ! git bisect run ./fail.sh 2>err &&
+ sed -n 's/.*\(unable to verify.*\)/\1/p' err >actual &&
+ test_cmp expect actual
+"
+
# We want to automatically find the commit that
# added "Another" into hello.
test_expect_success '"git bisect run" simple case' '
@@ -266,6 +443,16 @@ test_expect_success '"git bisect run" simple case' '
git bisect reset
'
+# We want to make sure no arguments has been eaten
+test_expect_success '"git bisect run" simple case' '
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ git bisect run printf "%s %s\n" reset --bisect-skip >my_bisect_log.txt &&
+ grep -e "reset --bisect-skip" my_bisect_log.txt &&
+ git bisect reset
+'
+
# We want to automatically find the commit that
# added "Ciao" into hello.
test_expect_success '"git bisect run" with more complex "git bisect start"' '
@@ -278,6 +465,51 @@ test_expect_success '"git bisect run" with more complex "git bisect start"' '
git bisect reset
'
+test_expect_success 'bisect run accepts exit code 126 as bad' '
+ test_when_finished "git bisect reset" &&
+ write_script test_script.sh <<-\EOF &&
+ ! grep Another hello || exit 126 >/dev/null
+ EOF
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ git bisect run ./test_script.sh >my_bisect_log.txt &&
+ grep "$HASH3 is the first bad commit" my_bisect_log.txt
+'
+
+test_expect_success POSIXPERM 'bisect run fails with non-executable test script' '
+ test_when_finished "git bisect reset" &&
+ >not-executable.sh &&
+ chmod -x not-executable.sh &&
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ test_must_fail git bisect run ./not-executable.sh >my_bisect_log.txt &&
+ ! grep "is the first bad commit" my_bisect_log.txt
+'
+
+test_expect_success 'bisect run accepts exit code 127 as bad' '
+ test_when_finished "git bisect reset" &&
+ write_script test_script.sh <<-\EOF &&
+ ! grep Another hello || exit 127 >/dev/null
+ EOF
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ git bisect run ./test_script.sh >my_bisect_log.txt &&
+ grep "$HASH3 is the first bad commit" my_bisect_log.txt
+'
+
+test_expect_success 'bisect run fails with missing test script' '
+ test_when_finished "git bisect reset" &&
+ rm -f does-not-exist.sh &&
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ test_must_fail git bisect run ./does-not-exist.sh >my_bisect_log.txt &&
+ ! grep "is the first bad commit" my_bisect_log.txt
+'
+
# $HASH1 is good, $HASH5 is bad, we skip $HASH3
# but $HASH4 is good,
# so we should find $HASH5 as the first bad commit
@@ -362,7 +594,7 @@ test_expect_success 'bisect starting with a detached HEAD' '
test_expect_success 'bisect errors out if bad and good are mistaken' '
git bisect reset &&
test_must_fail git bisect start $HASH2 $HASH4 2> rev_list_error &&
- test_i18ngrep "mistook good and bad" rev_list_error &&
+ test_grep "mistook good and bad" rev_list_error &&
git bisect reset
'
@@ -404,7 +636,7 @@ test_expect_success 'side branch creation' '
test_expect_success 'good merge base when good and bad are siblings' '
git bisect start "$HASH7" "$SIDE_HASH7" > my_bisect_log.txt &&
- test_i18ngrep "merge base must be tested" my_bisect_log.txt &&
+ test_grep "merge base must be tested" my_bisect_log.txt &&
grep $HASH4 my_bisect_log.txt &&
git bisect good > my_bisect_log.txt &&
! grep "merge base must be tested" my_bisect_log.txt &&
@@ -413,7 +645,7 @@ test_expect_success 'good merge base when good and bad are siblings' '
'
test_expect_success 'skipped merge base when good and bad are siblings' '
git bisect start "$SIDE_HASH7" "$HASH7" > my_bisect_log.txt &&
- test_i18ngrep "merge base must be tested" my_bisect_log.txt &&
+ test_grep "merge base must be tested" my_bisect_log.txt &&
grep $HASH4 my_bisect_log.txt &&
git bisect skip > my_bisect_log.txt 2>&1 &&
grep "warning" my_bisect_log.txt &&
@@ -423,11 +655,11 @@ test_expect_success 'skipped merge base when good and bad are siblings' '
test_expect_success 'bad merge base when good and bad are siblings' '
git bisect start "$HASH7" HEAD > my_bisect_log.txt &&
- test_i18ngrep "merge base must be tested" my_bisect_log.txt &&
+ test_grep "merge base must be tested" my_bisect_log.txt &&
grep $HASH4 my_bisect_log.txt &&
test_must_fail git bisect bad > my_bisect_log.txt 2>&1 &&
- test_i18ngrep "merge base $HASH4 is bad" my_bisect_log.txt &&
- test_i18ngrep "fixed between $HASH4 and \[$SIDE_HASH7\]" my_bisect_log.txt &&
+ test_grep "merge base $HASH4 is bad" my_bisect_log.txt &&
+ test_grep "fixed between $HASH4 and \[$SIDE_HASH7\]" my_bisect_log.txt &&
git bisect reset
'
@@ -478,9 +710,9 @@ test_expect_success '"git bisect run --first-parent" simple case' '
test_expect_success 'good merge bases when good and bad are siblings' '
git bisect start "$B_HASH" "$A_HASH" > my_bisect_log.txt &&
- test_i18ngrep "merge base must be tested" my_bisect_log.txt &&
+ test_grep "merge base must be tested" my_bisect_log.txt &&
git bisect good > my_bisect_log2.txt &&
- test_i18ngrep "merge base must be tested" my_bisect_log2.txt &&
+ test_grep "merge base must be tested" my_bisect_log2.txt &&
{
{
grep "$SIDE_HASH5" my_bisect_log.txt &&
@@ -495,14 +727,14 @@ test_expect_success 'good merge bases when good and bad are siblings' '
test_expect_success 'optimized merge base checks' '
git bisect start "$HASH7" "$SIDE_HASH7" > my_bisect_log.txt &&
- test_i18ngrep "merge base must be tested" my_bisect_log.txt &&
+ test_grep "merge base must be tested" my_bisect_log.txt &&
grep "$HASH4" my_bisect_log.txt &&
git bisect good > my_bisect_log2.txt &&
test -f ".git/BISECT_ANCESTORS_OK" &&
test "$HASH6" = $(git rev-parse --verify HEAD) &&
git bisect bad &&
git bisect good "$A_HASH" > my_bisect_log4.txt &&
- test_i18ngrep "merge base must be tested" my_bisect_log4.txt &&
+ test_grep "merge base must be tested" my_bisect_log4.txt &&
test_path_is_missing ".git/BISECT_ANCESTORS_OK"
'
@@ -580,7 +812,7 @@ test_expect_success 'skipping away from skipped commit' '
test_expect_success 'erroring out when using bad path arguments' '
test_must_fail git bisect start $PARA_HASH7 $HASH1 -- foobar 2> error.txt &&
- test_i18ngrep "bad path arguments" error.txt
+ test_grep "bad path arguments" error.txt
'
test_expect_success 'test bisection on bare repo - --no-checkout specified' '
@@ -646,7 +878,7 @@ test_expect_success 'broken branch creation' '
echo "" > expected.ok
cat > expected.missing-tree.default <<EOF
-fatal: unable to read tree $deleted
+fatal: unable to read tree ($deleted)
EOF
test_expect_success 'bisect fails if tree is broken on start commit' '
@@ -855,6 +1087,16 @@ test_expect_success 'bisect start with one term1 and term2' '
git bisect reset
'
+test_expect_success 'bogus command does not start bisect' '
+ git bisect reset &&
+ test_must_fail git bisect --bisect-terms 1 2 2>out &&
+ ! grep "You need to start" out &&
+ test_must_fail git bisect --bisect-terms 2>out &&
+ ! grep "You need to start" out &&
+ grep "git bisect.*visualize" out &&
+ git bisect reset
+'
+
test_expect_success 'bisect replay with term1 and term2' '
git bisect replay log_to_replay.txt >bisect_result &&
grep "$HASH2 is the first term1 commit" bisect_result &&
@@ -940,12 +1182,11 @@ test_expect_success 'git bisect reset cleans bisection state properly' '
git bisect bad $HASH4 &&
git bisect reset &&
test -z "$(git for-each-ref "refs/bisect/*")" &&
- test_path_is_missing ".git/BISECT_EXPECTED_REV" &&
+ test_ref_missing BISECT_EXPECTED_REV &&
test_path_is_missing ".git/BISECT_ANCESTORS_OK" &&
test_path_is_missing ".git/BISECT_LOG" &&
test_path_is_missing ".git/BISECT_RUN" &&
test_path_is_missing ".git/BISECT_TERMS" &&
- test_path_is_missing ".git/head-name" &&
test_path_is_missing ".git/BISECT_HEAD" &&
test_path_is_missing ".git/BISECT_START"
'
@@ -962,4 +1203,60 @@ test_expect_success 'bisect handles annotated tags' '
grep "$bad is the first bad commit" output
'
+test_expect_success 'bisect run fails with exit code equals or greater than 128' '
+ write_script test_script.sh <<-\EOF &&
+ exit 128
+ EOF
+ test_must_fail git bisect run ./test_script.sh &&
+ write_script test_script.sh <<-\EOF &&
+ exit 255
+ EOF
+ test_must_fail git bisect run ./test_script.sh
+'
+
+test_expect_success 'bisect visualize with a filename with dash and space' '
+ echo "My test line" >>"./-hello 2" &&
+ git add -- "./-hello 2" &&
+ git commit --quiet -m "Add test line" -- "./-hello 2" &&
+ git bisect visualize -p -- "-hello 2"
+'
+
+test_expect_success 'bisect state output with multiple good commits' '
+ git bisect reset &&
+ git bisect start >output &&
+ grep "waiting for both good and bad commits" output &&
+ git bisect log >output &&
+ grep "waiting for both good and bad commits" output &&
+ git bisect good "$HASH1" >output &&
+ grep "waiting for bad commit, 1 good commit known" output &&
+ git bisect log >output &&
+ grep "waiting for bad commit, 1 good commit known" output &&
+ git bisect good "$HASH2" >output &&
+ grep "waiting for bad commit, 2 good commits known" output &&
+ git bisect log >output &&
+ grep "waiting for bad commit, 2 good commits known" output
+'
+
+test_expect_success 'bisect state output with bad commit' '
+ git bisect reset &&
+ git bisect start >output &&
+ grep "waiting for both good and bad commits" output &&
+ git bisect log >output &&
+ grep "waiting for both good and bad commits" output &&
+ git bisect bad "$HASH4" >output &&
+ grep -F "waiting for good commit(s), bad commit known" output &&
+ git bisect log >output &&
+ grep -F "waiting for good commit(s), bad commit known" output
+'
+
+test_expect_success 'verify correct error message' '
+ git bisect reset &&
+ git bisect start $HASH4 $HASH1 &&
+ write_script test_script.sh <<-\EOF &&
+ rm .git/BISECT*
+ EOF
+ test_must_fail git bisect run ./test_script.sh 2>error &&
+ grep "git bisect good.*exited with error code" error
+'
+
test_done
diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh
index a313849..acc281c 100755
--- a/t/t6040-tracking-info.sh
+++ b/t/t6040-tracking-info.sh
@@ -5,6 +5,7 @@ test_description='remote tracking stats'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
advance () {
@@ -82,13 +83,13 @@ test_expect_success 'checkout (diverged from upstream)' '
(
cd test && git checkout b1
) >actual &&
- test_i18ngrep "have 1 and 1 different" actual
+ test_grep "have 1 and 1 different" actual
'
test_expect_success 'checkout with local tracked branch' '
git checkout main &&
git checkout follower >actual &&
- test_i18ngrep "is ahead of" actual
+ test_grep "is ahead of" actual
'
test_expect_success 'checkout (upstream is gone)' '
@@ -96,14 +97,14 @@ test_expect_success 'checkout (upstream is gone)' '
cd test &&
git checkout b5
) >actual &&
- test_i18ngrep "is based on .*, but the upstream is gone." actual
+ test_grep "is based on .*, but the upstream is gone." actual
'
test_expect_success 'checkout (up-to-date with upstream)' '
(
cd test && git checkout b6
) >actual &&
- test_i18ngrep "Your branch is up to date with .origin/main" actual
+ test_grep "Your branch is up to date with .origin/main" actual
'
test_expect_success 'status (diverged from upstream)' '
@@ -113,7 +114,7 @@ test_expect_success 'status (diverged from upstream)' '
# reports nothing to commit
test_must_fail git commit --dry-run
) >actual &&
- test_i18ngrep "have 1 and 1 different" actual
+ test_grep "have 1 and 1 different" actual
'
test_expect_success 'status (upstream is gone)' '
@@ -123,7 +124,7 @@ test_expect_success 'status (upstream is gone)' '
# reports nothing to commit
test_must_fail git commit --dry-run
) >actual &&
- test_i18ngrep "is based on .*, but the upstream is gone." actual
+ test_grep "is based on .*, but the upstream is gone." actual
'
test_expect_success 'status (up-to-date with upstream)' '
@@ -133,7 +134,7 @@ test_expect_success 'status (up-to-date with upstream)' '
# reports nothing to commit
test_must_fail git commit --dry-run
) >actual &&
- test_i18ngrep "Your branch is up to date with .origin/main" actual
+ test_grep "Your branch is up to date with .origin/main" actual
'
cat >expect <<\EOF
@@ -252,7 +253,7 @@ test_expect_success 'fail to track lightweight tags' '
git checkout main &&
git tag light &&
test_must_fail git branch --track lighttrack light >actual &&
- test_i18ngrep ! "set up to track" actual &&
+ test_grep ! "set up to track" actual &&
test_must_fail git checkout lighttrack
'
@@ -260,7 +261,7 @@ test_expect_success 'fail to track annotated tags' '
git checkout main &&
git tag -m heavy heavy &&
test_must_fail git branch --track heavytrack heavy >actual &&
- test_i18ngrep ! "set up to track" actual &&
+ test_grep ! "set up to track" actual &&
test_must_fail git checkout heavytrack
'
diff --git a/t/t6050-replace.sh b/t/t6050-replace.sh
index 2500acc..c6e9b33 100755
--- a/t/t6050-replace.sh
+++ b/t/t6050-replace.sh
@@ -44,7 +44,7 @@ commit_peeling_shows_parents ()
_parent_number=$(( $_parent_number + 1 ))
done &&
test_must_fail git rev-parse --verify $_commit^$_parent_number 2>err &&
- test_i18ngrep "Needed a single revision" err
+ test_grep "Needed a single revision" err
}
commit_has_parents ()
@@ -62,59 +62,59 @@ HASH6=
HASH7=
test_expect_success 'set up buggy branch' '
- echo "line 1" >>hello &&
- echo "line 2" >>hello &&
- echo "line 3" >>hello &&
- echo "line 4" >>hello &&
- add_and_commit_file hello "4 lines" &&
- HASH1=$(git rev-parse --verify HEAD) &&
- echo "line BUG" >>hello &&
- echo "line 6" >>hello &&
- echo "line 7" >>hello &&
- echo "line 8" >>hello &&
- add_and_commit_file hello "4 more lines with a BUG" &&
- HASH2=$(git rev-parse --verify HEAD) &&
- echo "line 9" >>hello &&
- echo "line 10" >>hello &&
- add_and_commit_file hello "2 more lines" &&
- HASH3=$(git rev-parse --verify HEAD) &&
- echo "line 11" >>hello &&
- add_and_commit_file hello "1 more line" &&
- HASH4=$(git rev-parse --verify HEAD) &&
- sed -e "s/BUG/5/" hello >hello.new &&
- mv hello.new hello &&
- add_and_commit_file hello "BUG fixed" &&
- HASH5=$(git rev-parse --verify HEAD) &&
- echo "line 12" >>hello &&
- echo "line 13" >>hello &&
- add_and_commit_file hello "2 more lines" &&
- HASH6=$(git rev-parse --verify HEAD) &&
- echo "line 14" >>hello &&
- echo "line 15" >>hello &&
- echo "line 16" >>hello &&
- add_and_commit_file hello "again 3 more lines" &&
- HASH7=$(git rev-parse --verify HEAD)
+ echo "line 1" >>hello &&
+ echo "line 2" >>hello &&
+ echo "line 3" >>hello &&
+ echo "line 4" >>hello &&
+ add_and_commit_file hello "4 lines" &&
+ HASH1=$(git rev-parse --verify HEAD) &&
+ echo "line BUG" >>hello &&
+ echo "line 6" >>hello &&
+ echo "line 7" >>hello &&
+ echo "line 8" >>hello &&
+ add_and_commit_file hello "4 more lines with a BUG" &&
+ HASH2=$(git rev-parse --verify HEAD) &&
+ echo "line 9" >>hello &&
+ echo "line 10" >>hello &&
+ add_and_commit_file hello "2 more lines" &&
+ HASH3=$(git rev-parse --verify HEAD) &&
+ echo "line 11" >>hello &&
+ add_and_commit_file hello "1 more line" &&
+ HASH4=$(git rev-parse --verify HEAD) &&
+ sed -e "s/BUG/5/" hello >hello.new &&
+ mv hello.new hello &&
+ add_and_commit_file hello "BUG fixed" &&
+ HASH5=$(git rev-parse --verify HEAD) &&
+ echo "line 12" >>hello &&
+ echo "line 13" >>hello &&
+ add_and_commit_file hello "2 more lines" &&
+ HASH6=$(git rev-parse --verify HEAD) &&
+ echo "line 14" >>hello &&
+ echo "line 15" >>hello &&
+ echo "line 16" >>hello &&
+ add_and_commit_file hello "again 3 more lines" &&
+ HASH7=$(git rev-parse --verify HEAD)
'
test_expect_success 'replace the author' '
- git cat-file commit $HASH2 | grep "author A U Thor" &&
- R=$(git cat-file commit $HASH2 | sed -e "s/A U/O/" | git hash-object -t commit --stdin -w) &&
- git cat-file commit $R | grep "author O Thor" &&
- git update-ref refs/replace/$HASH2 $R &&
- git show HEAD~5 | grep "O Thor" &&
- git show $HASH2 | grep "O Thor"
+ git cat-file commit $HASH2 | grep "author A U Thor" &&
+ R=$(git cat-file commit $HASH2 | sed -e "s/A U/O/" | git hash-object -t commit --stdin -w) &&
+ git cat-file commit $R | grep "author O Thor" &&
+ git update-ref refs/replace/$HASH2 $R &&
+ git show HEAD~5 | grep "O Thor" &&
+ git show $HASH2 | grep "O Thor"
'
test_expect_success 'test --no-replace-objects option' '
- git cat-file commit $HASH2 | grep "author O Thor" &&
- git --no-replace-objects cat-file commit $HASH2 | grep "author A U Thor" &&
- git show $HASH2 | grep "O Thor" &&
- git --no-replace-objects show $HASH2 | grep "A U Thor"
+ git cat-file commit $HASH2 | grep "author O Thor" &&
+ git --no-replace-objects cat-file commit $HASH2 | grep "author A U Thor" &&
+ git show $HASH2 | grep "O Thor" &&
+ git --no-replace-objects show $HASH2 | grep "A U Thor"
'
test_expect_success 'test GIT_NO_REPLACE_OBJECTS env variable' '
- GIT_NO_REPLACE_OBJECTS=1 git cat-file commit $HASH2 | grep "author A U Thor" &&
- GIT_NO_REPLACE_OBJECTS=1 git show $HASH2 | grep "A U Thor"
+ GIT_NO_REPLACE_OBJECTS=1 git cat-file commit $HASH2 | grep "author A U Thor" &&
+ GIT_NO_REPLACE_OBJECTS=1 git show $HASH2 | grep "A U Thor"
'
test_expect_success 'test core.usereplacerefs config option' '
@@ -132,64 +132,64 @@ tagger T A Gger <> 0 +0000
EOF
test_expect_success 'tag replaced commit' '
- git update-ref refs/tags/mytag $(git mktag <tag.sig)
+ git update-ref refs/tags/mytag $(git mktag <tag.sig)
'
test_expect_success '"git fsck" works' '
- git fsck main >fsck_main.out &&
- test_i18ngrep "dangling commit $R" fsck_main.out &&
- test_i18ngrep "dangling tag $(git show-ref -s refs/tags/mytag)" fsck_main.out &&
- test -z "$(git fsck)"
+ git fsck main >fsck_main.out &&
+ test_grep "dangling commit $R" fsck_main.out &&
+ test_grep "dangling tag $(git show-ref -s refs/tags/mytag)" fsck_main.out &&
+ test -z "$(git fsck)"
'
test_expect_success 'repack, clone and fetch work' '
- git repack -a -d &&
- git clone --no-hardlinks . clone_dir &&
- (
- cd clone_dir &&
- git show HEAD~5 | grep "A U Thor" &&
- git show $HASH2 | grep "A U Thor" &&
- git cat-file commit $R &&
- git repack -a -d &&
- test_must_fail git cat-file commit $R &&
- git fetch ../ "refs/replace/*:refs/replace/*" &&
- git show HEAD~5 | grep "O Thor" &&
- git show $HASH2 | grep "O Thor" &&
- git cat-file commit $R
- )
+ git repack -a -d &&
+ git clone --no-hardlinks . clone_dir &&
+ (
+ cd clone_dir &&
+ git show HEAD~5 | grep "A U Thor" &&
+ git show $HASH2 | grep "A U Thor" &&
+ git cat-file commit $R &&
+ git repack -a -d &&
+ test_must_fail git cat-file commit $R &&
+ git fetch ../ "refs/replace/*:refs/replace/*" &&
+ git show HEAD~5 | grep "O Thor" &&
+ git show $HASH2 | grep "O Thor" &&
+ git cat-file commit $R
+ )
'
test_expect_success '"git replace" listing and deleting' '
- test "$HASH2" = "$(git replace -l)" &&
- test "$HASH2" = "$(git replace)" &&
- aa=${HASH2%??????????????????????????????????????} &&
- test "$HASH2" = "$(git replace --list "$aa*")" &&
- test_must_fail git replace -d $R &&
- test_must_fail git replace --delete &&
- test_must_fail git replace -l -d $HASH2 &&
- git replace -d $HASH2 &&
- git show $HASH2 | grep "A U Thor" &&
- test -z "$(git replace -l)"
+ test "$HASH2" = "$(git replace -l)" &&
+ test "$HASH2" = "$(git replace)" &&
+ aa=${HASH2%??????????????????????????????????????} &&
+ test "$HASH2" = "$(git replace --list "$aa*")" &&
+ test_must_fail git replace -d $R &&
+ test_must_fail git replace --delete &&
+ test_must_fail git replace -l -d $HASH2 &&
+ git replace -d $HASH2 &&
+ git show $HASH2 | grep "A U Thor" &&
+ test -z "$(git replace -l)"
'
test_expect_success '"git replace" replacing' '
- git replace $HASH2 $R &&
- git show $HASH2 | grep "O Thor" &&
- test_must_fail git replace $HASH2 $R &&
- git replace -f $HASH2 $R &&
- test_must_fail git replace -f &&
- test "$HASH2" = "$(git replace)"
+ git replace $HASH2 $R &&
+ git show $HASH2 | grep "O Thor" &&
+ test_must_fail git replace $HASH2 $R &&
+ git replace -f $HASH2 $R &&
+ test_must_fail git replace -f &&
+ test "$HASH2" = "$(git replace)"
'
test_expect_success '"git replace" resolves sha1' '
- SHORTHASH2=$(git rev-parse --short=8 $HASH2) &&
- git replace -d $SHORTHASH2 &&
- git replace $SHORTHASH2 $R &&
- git show $HASH2 | grep "O Thor" &&
- test_must_fail git replace $HASH2 $R &&
- git replace -f $HASH2 $R &&
- test_must_fail git replace --force &&
- test "$HASH2" = "$(git replace)"
+ SHORTHASH2=$(git rev-parse --short=8 $HASH2) &&
+ git replace -d $SHORTHASH2 &&
+ git replace $SHORTHASH2 $R &&
+ git show $HASH2 | grep "O Thor" &&
+ test_must_fail git replace $HASH2 $R &&
+ git replace -f $HASH2 $R &&
+ test_must_fail git replace --force &&
+ test "$HASH2" = "$(git replace)"
'
# This creates a side branch where the bug in H2
@@ -207,79 +207,79 @@ test_expect_success '"git replace" resolves sha1' '
# Then we replace H6 with P6.
#
test_expect_success 'create parallel branch without the bug' '
- git replace -d $HASH2 &&
- git show $HASH2 | grep "A U Thor" &&
- git checkout $HASH1 &&
- git cherry-pick $HASH2 &&
- git show $HASH5 | git apply &&
- git commit --amend -m "hello: 4 more lines WITHOUT the bug" hello &&
- PARA2=$(git rev-parse --verify HEAD) &&
- git cherry-pick $HASH3 &&
- PARA3=$(git rev-parse --verify HEAD) &&
- git cherry-pick $HASH4 &&
- PARA4=$(git rev-parse --verify HEAD) &&
- git cherry-pick $HASH6 &&
- PARA6=$(git rev-parse --verify HEAD) &&
- git replace $HASH6 $PARA6 &&
- git checkout main &&
- cur=$(git rev-parse --verify HEAD) &&
- test "$cur" = "$HASH7" &&
- git log --pretty=oneline | grep $PARA2 &&
- git remote add cloned ./clone_dir
+ git replace -d $HASH2 &&
+ git show $HASH2 | grep "A U Thor" &&
+ git checkout $HASH1 &&
+ git cherry-pick $HASH2 &&
+ git show $HASH5 | git apply &&
+ git commit --amend -m "hello: 4 more lines WITHOUT the bug" hello &&
+ PARA2=$(git rev-parse --verify HEAD) &&
+ git cherry-pick $HASH3 &&
+ PARA3=$(git rev-parse --verify HEAD) &&
+ git cherry-pick $HASH4 &&
+ PARA4=$(git rev-parse --verify HEAD) &&
+ git cherry-pick $HASH6 &&
+ PARA6=$(git rev-parse --verify HEAD) &&
+ git replace $HASH6 $PARA6 &&
+ git checkout main &&
+ cur=$(git rev-parse --verify HEAD) &&
+ test "$cur" = "$HASH7" &&
+ git log --pretty=oneline | grep $PARA2 &&
+ git remote add cloned ./clone_dir
'
test_expect_success 'push to cloned repo' '
- git push cloned $HASH6^:refs/heads/parallel &&
- (
- cd clone_dir &&
- git checkout parallel &&
- git log --pretty=oneline | grep $PARA2
- )
+ git push cloned $HASH6^:refs/heads/parallel &&
+ (
+ cd clone_dir &&
+ git checkout parallel &&
+ git log --pretty=oneline | grep $PARA2
+ )
'
test_expect_success 'push branch with replacement' '
- git cat-file commit $PARA3 | grep "author A U Thor" &&
- S=$(git cat-file commit $PARA3 | sed -e "s/A U/O/" | git hash-object -t commit --stdin -w) &&
- git cat-file commit $S | grep "author O Thor" &&
- git replace $PARA3 $S &&
- git show $HASH6~2 | grep "O Thor" &&
- git show $PARA3 | grep "O Thor" &&
- git push cloned $HASH6^:refs/heads/parallel2 &&
- (
- cd clone_dir &&
- git checkout parallel2 &&
- git log --pretty=oneline | grep $PARA3 &&
- git show $PARA3 | grep "A U Thor"
- )
+ git cat-file commit $PARA3 | grep "author A U Thor" &&
+ S=$(git cat-file commit $PARA3 | sed -e "s/A U/O/" | git hash-object -t commit --stdin -w) &&
+ git cat-file commit $S | grep "author O Thor" &&
+ git replace $PARA3 $S &&
+ git show $HASH6~2 | grep "O Thor" &&
+ git show $PARA3 | grep "O Thor" &&
+ git push cloned $HASH6^:refs/heads/parallel2 &&
+ (
+ cd clone_dir &&
+ git checkout parallel2 &&
+ git log --pretty=oneline | grep $PARA3 &&
+ git show $PARA3 | grep "A U Thor"
+ )
'
test_expect_success 'fetch branch with replacement' '
- git branch tofetch $HASH6 &&
- (
- cd clone_dir &&
- git fetch origin refs/heads/tofetch:refs/heads/parallel3 &&
- git log --pretty=oneline parallel3 >output.txt &&
- ! grep $PARA3 output.txt &&
- git show $PARA3 >para3.txt &&
- grep "A U Thor" para3.txt &&
- git fetch origin "refs/replace/*:refs/replace/*" &&
- git log --pretty=oneline parallel3 >output.txt &&
- grep $PARA3 output.txt &&
- git show $PARA3 >para3.txt &&
- grep "O Thor" para3.txt
- )
+ git branch tofetch $HASH6 &&
+ (
+ cd clone_dir &&
+ git fetch origin refs/heads/tofetch:refs/heads/parallel3 &&
+ git log --pretty=oneline parallel3 >output.txt &&
+ ! grep $PARA3 output.txt &&
+ git show $PARA3 >para3.txt &&
+ grep "A U Thor" para3.txt &&
+ git fetch origin "refs/replace/*:refs/replace/*" &&
+ git log --pretty=oneline parallel3 >output.txt &&
+ grep $PARA3 output.txt &&
+ git show $PARA3 >para3.txt &&
+ grep "O Thor" para3.txt
+ )
'
test_expect_success 'bisect and replacements' '
- git bisect start $HASH7 $HASH1 &&
- test "$PARA3" = "$(git rev-parse --verify HEAD)" &&
- git bisect reset &&
- GIT_NO_REPLACE_OBJECTS=1 git bisect start $HASH7 $HASH1 &&
- test "$HASH4" = "$(git rev-parse --verify HEAD)" &&
- git bisect reset &&
- git --no-replace-objects bisect start $HASH7 $HASH1 &&
- test "$HASH4" = "$(git rev-parse --verify HEAD)" &&
- git bisect reset
+ git bisect start $HASH7 $HASH1 &&
+ test "$PARA3" = "$(git rev-parse --verify HEAD)" &&
+ git bisect reset &&
+ GIT_NO_REPLACE_OBJECTS=1 git bisect start $HASH7 $HASH1 &&
+ test "$HASH4" = "$(git rev-parse --verify HEAD)" &&
+ git bisect reset &&
+ git --no-replace-objects bisect start $HASH7 $HASH1 &&
+ test "$HASH4" = "$(git rev-parse --verify HEAD)" &&
+ git bisect reset
'
test_expect_success 'index-pack and replacements' '
@@ -490,9 +490,9 @@ test_expect_success '--convert-graft-file' '
$(git rev-parse HEAD^^ HEAD^ HEAD^^ HEAD^2) \
>.git/info/grafts &&
git status 2>stderr &&
- test_i18ngrep "hint:.*grafts is deprecated" stderr &&
+ test_grep "hint:.*grafts is deprecated" stderr &&
git replace --convert-graft-file 2>stderr &&
- test_i18ngrep ! "hint:.*grafts is deprecated" stderr &&
+ test_grep ! "hint:.*grafts is deprecated" stderr &&
test_path_is_missing .git/info/grafts &&
: verify that the history is now "grafted" &&
@@ -503,8 +503,8 @@ test_expect_success '--convert-graft-file' '
test_when_finished "rm -f .git/info/grafts" &&
echo $EMPTY_BLOB $EMPTY_TREE >.git/info/grafts &&
test_must_fail git replace --convert-graft-file 2>err &&
- test_i18ngrep "$EMPTY_BLOB $EMPTY_TREE" err &&
- test_i18ngrep "$EMPTY_BLOB $EMPTY_TREE" .git/info/grafts
+ test_grep "$EMPTY_BLOB $EMPTY_TREE" err &&
+ test_grep "$EMPTY_BLOB $EMPTY_TREE" .git/info/grafts
'
test_done
diff --git a/t/t6060-merge-index.sh b/t/t6060-merge-index.sh
index ddf34f0..1a8b64c 100755
--- a/t/t6060-merge-index.sh
+++ b/t/t6060-merge-index.sh
@@ -1,12 +1,12 @@
#!/bin/sh
test_description='basic git merge-index / git-merge-one-file tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup diverging branches' '
- for i in 1 2 3 4 5 6 7 8 9 10; do
- echo $i
- done >file &&
+ test_write_lines 1 2 3 4 5 6 7 8 9 10 >file &&
git add file &&
git commit -m base &&
git tag base &&
diff --git a/t/t6100-rev-list-in-order.sh b/t/t6100-rev-list-in-order.sh
index e934bc2..88ed7bd 100755
--- a/t/t6100-rev-list-in-order.sh
+++ b/t/t6100-rev-list-in-order.sh
@@ -2,6 +2,7 @@
test_description='rev-list testing in-commit-order'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup a commit history with trees, blobs' '
diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh
index 78b5851..d20723d 100755
--- a/t/t6101-rev-parse-parents.sh
+++ b/t/t6101-rev-parse-parents.sh
@@ -8,6 +8,8 @@ test_description='Test git rev-parse with different parent options'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
+TEST_CREATE_REPO_NO_TEMPLATE=1
. ./test-lib.sh
test_cmp_rev_output () {
@@ -25,6 +27,7 @@ test_expect_success 'setup' '
git merge -m next --allow-unrelated-histories start2 &&
test_commit final &&
+ mkdir .git/info &&
test_seq 40 |
while read i
do
@@ -32,7 +35,7 @@ test_expect_success 'setup' '
test_tick &&
git commit --allow-empty -m "$i" &&
commit=$(git rev-parse --verify HEAD) &&
- printf "$commit " >>.git/info/grafts
+ printf "$commit " >>.git/info/grafts || return 1
done
'
diff --git a/t/t6102-rev-list-unexpected-objects.sh b/t/t6102-rev-list-unexpected-objects.sh
index 52cde09..5d28507 100755
--- a/t/t6102-rev-list-unexpected-objects.sh
+++ b/t/t6102-rev-list-unexpected-objects.sh
@@ -2,6 +2,7 @@
test_description='git rev-list should handle unexpected object types'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup well-formed objects' '
@@ -16,13 +17,18 @@ test_expect_success 'setup unexpected non-blob entry' '
broken_tree="$(git hash-object -w --literally -t tree broken-tree)"
'
-test_expect_failure 'traverse unexpected non-blob entry (lone)' '
- test_must_fail git rev-list --objects $broken_tree
+test_expect_success 'TODO (should fail!): traverse unexpected non-blob entry (lone)' '
+ sed "s/Z$//" >expect <<-EOF &&
+ $broken_tree Z
+ $tree foo
+ EOF
+ git rev-list --objects $broken_tree >actual &&
+ test_cmp expect actual
'
test_expect_success 'traverse unexpected non-blob entry (seen)' '
test_must_fail git rev-list --objects $tree $broken_tree >output 2>&1 &&
- test_i18ngrep "is not a blob" output
+ test_grep "is not a blob" output
'
test_expect_success 'setup unexpected non-tree entry' '
@@ -36,7 +42,7 @@ test_expect_success 'traverse unexpected non-tree entry (lone)' '
test_expect_success 'traverse unexpected non-tree entry (seen)' '
test_must_fail git rev-list --objects $blob $broken_tree >output 2>&1 &&
- test_i18ngrep "is not a tree" output
+ test_grep "is not a tree" output
'
test_expect_success 'setup unexpected non-commit parent' '
@@ -48,13 +54,13 @@ test_expect_success 'setup unexpected non-commit parent' '
test_expect_success 'traverse unexpected non-commit parent (lone)' '
test_must_fail git rev-list --objects $broken_commit >output 2>&1 &&
- test_i18ngrep "not a commit" output
+ test_grep "not a commit" output
'
test_expect_success 'traverse unexpected non-commit parent (seen)' '
test_must_fail git rev-list --objects $blob $broken_commit \
>output 2>&1 &&
- test_i18ngrep "not a commit" output
+ test_grep "not a commit" output
'
test_expect_success 'setup unexpected non-tree root' '
@@ -70,7 +76,7 @@ test_expect_success 'traverse unexpected non-tree root (lone)' '
test_expect_success 'traverse unexpected non-tree root (seen)' '
test_must_fail git rev-list --objects $blob $broken_commit \
>output 2>&1 &&
- test_i18ngrep "not a tree" output
+ test_grep "not a tree" output
'
test_expect_success 'setup unexpected non-commit tag' '
@@ -87,7 +93,7 @@ test_expect_success 'traverse unexpected non-commit tag (lone)' '
test_expect_success 'traverse unexpected non-commit tag (seen)' '
test_must_fail git rev-list --objects $blob $tag >output 2>&1 &&
- test_i18ngrep "not a commit" output
+ test_grep "not a commit" output
'
test_expect_success 'setup unexpected non-tree tag' '
@@ -104,7 +110,7 @@ test_expect_success 'traverse unexpected non-tree tag (lone)' '
test_expect_success 'traverse unexpected non-tree tag (seen)' '
test_must_fail git rev-list --objects $blob $tag >output 2>&1 &&
- test_i18ngrep "not a tree" output
+ test_grep "not a tree" output
'
test_expect_success 'setup unexpected non-blob tag' '
@@ -115,13 +121,13 @@ test_expect_success 'setup unexpected non-blob tag' '
tag=$(git hash-object -w --literally -t tag broken-tag)
'
-test_expect_failure 'traverse unexpected non-blob tag (lone)' '
+test_expect_success 'traverse unexpected non-blob tag (lone)' '
test_must_fail git rev-list --objects $tag
'
test_expect_success 'traverse unexpected non-blob tag (seen)' '
test_must_fail git rev-list --objects $commit $tag >output 2>&1 &&
- test_i18ngrep "not a blob" output
+ test_grep "not a blob" output
'
test_done
diff --git a/t/t6110-rev-list-sparse.sh b/t/t6110-rev-list-sparse.sh
index 13c1da5..ddefc7f 100755
--- a/t/t6110-rev-list-sparse.sh
+++ b/t/t6110-rev-list-sparse.sh
@@ -4,6 +4,7 @@ test_description='operations that cull histories in unusual ways'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t6111-rev-list-treesame.sh b/t/t6111-rev-list-treesame.sh
index e07b607..90ff141 100755
--- a/t/t6111-rev-list-treesame.sh
+++ b/t/t6111-rev-list-treesame.sh
@@ -23,7 +23,8 @@ note () {
}
unnote () {
- git name-rev --tags --stdin | sed -e "s|$OID_REGEX (tags/\([^)]*\))\([ ]\)|\1\2|g"
+ git name-rev --tags --annotate-stdin | \
+ sed -e "s|$OID_REGEX (tags/\([^)]*\))\([ ]\)|\1\2|g"
}
test_expect_success setup '
diff --git a/t/t6112-rev-list-filters-objects.sh b/t/t6112-rev-list-filters-objects.sh
index 4ade105..43e1afd 100755
--- a/t/t6112-rev-list-filters-objects.sh
+++ b/t/t6112-rev-list-filters-objects.sh
@@ -16,9 +16,9 @@ test_expect_success 'setup r1' '
git init r1 &&
for n in 1 2 3 4 5
do
- echo "This is file: $n" > r1/file.$n
- git -C r1 add file.$n
- git -C r1 commit -m "$n"
+ echo "This is file: $n" > r1/file.$n &&
+ git -C r1 add file.$n &&
+ git -C r1 commit -m "$n" || return 1
done
'
@@ -73,9 +73,9 @@ test_expect_success 'setup r2' '
git init r2 &&
for n in 1000 10000
do
- printf "%"$n"s" X > r2/large.$n
- git -C r2 add large.$n
- git -C r2 commit -m "$n"
+ printf "%"$n"s" X > r2/large.$n &&
+ git -C r2 add large.$n &&
+ git -C r2 commit -m "$n" || return 1
done
'
@@ -245,10 +245,10 @@ test_expect_success 'setup r3' '
mkdir r3/dir1 &&
for n in sparse1 sparse2
do
- echo "This is file: $n" > r3/$n
- git -C r3 add $n
- echo "This is file: dir1/$n" > r3/dir1/$n
- git -C r3 add dir1/$n
+ echo "This is file: $n" > r3/$n &&
+ git -C r3 add $n &&
+ echo "This is file: dir1/$n" > r3/dir1/$n &&
+ git -C r3 add dir1/$n || return 1
done &&
git -C r3 commit -m "sparse" &&
echo dir1/ >pattern1 &&
@@ -457,7 +457,7 @@ expect_invalid_filter_spec () {
test_must_fail git -C r3 rev-list --objects --filter="$spec" HEAD \
>actual 2>actual_stderr &&
test_must_be_empty actual &&
- test_i18ngrep "$err" actual_stderr
+ test_grep "$err" actual_stderr
}
test_expect_success 'combine:... while URL-encoding things that should not be' '
@@ -670,9 +670,9 @@ test_expect_success 'rev-list W/ --missing=print' '
awk -f print_2.awk ls_files_result |
sort >expected &&
- for id in `cat expected | sed "s|..|&/|"`
+ for id in `sed "s|..|&/|" expected`
do
- rm r1/.git/objects/$id
+ rm r1/.git/objects/$id || return 1
done &&
git -C r1 rev-list --quiet --missing=print --objects HEAD >revs &&
diff --git a/t/t6113-rev-list-bitmap-filters.sh b/t/t6113-rev-list-bitmap-filters.sh
index 4d8e091..a9656a1 100755
--- a/t/t6113-rev-list-bitmap-filters.sh
+++ b/t/t6113-rev-list-bitmap-filters.sh
@@ -1,9 +1,12 @@
#!/bin/sh
test_description='rev-list combining bitmaps and filters'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-bitmap.sh
+
test_expect_success 'set up bitmapped repo' '
# one commit will have bitmaps, the other will not
test_commit one &&
@@ -141,4 +144,17 @@ test_expect_success 'combine filter with --filter-provided-objects' '
done <objects
'
+test_expect_success 'bitmap traversal with --unpacked' '
+ git repack -adb &&
+ test_commit unpacked &&
+
+ git rev-list --objects --no-object-names unpacked^.. >expect.raw &&
+ sort expect.raw >expect &&
+
+ git rev-list --use-bitmap-index --objects --all --unpacked >actual.raw &&
+ sort actual.raw >actual &&
+
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t6114-keep-packs.sh b/t/t6114-keep-packs.sh
index 9239d8a..44246f8 100755
--- a/t/t6114-keep-packs.sh
+++ b/t/t6114-keep-packs.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='rev-list with .keep packs'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t6115-rev-list-du.sh b/t/t6115-rev-list-du.sh
index b4aef32..c0cfda6 100755
--- a/t/t6115-rev-list-du.sh
+++ b/t/t6115-rev-list-du.sh
@@ -48,4 +48,33 @@ check_du HEAD
check_du --objects HEAD
check_du --objects HEAD^..HEAD
+test_expect_success 'setup for --unpacked tests' '
+ git repack -adb &&
+ test_commit unpacked
+'
+
+check_du --all --objects --unpacked
+
+# As mentioned above, don't use hardcode sizes as actual size, but use the
+# output from git cat-file.
+test_expect_success 'rev-list --disk-usage=human' '
+ git rev-list --objects HEAD --disk-usage=human >actual &&
+ disk_usage_slow --objects HEAD >actual_size &&
+ grep "$(cat actual_size) bytes" actual
+'
+
+test_expect_success 'rev-list --disk-usage=human with bitmaps' '
+ git rev-list --objects HEAD --use-bitmap-index --disk-usage=human >actual &&
+ disk_usage_slow --objects HEAD >actual_size &&
+ grep "$(cat actual_size) bytes" actual
+'
+
+test_expect_success 'rev-list use --disk-usage unproperly' '
+ test_must_fail git rev-list --objects HEAD --disk-usage=typo 2>err &&
+ cat >expect <<-\EOF &&
+ fatal: invalid value for '\''--disk-usage=<format>'\'': '\''typo'\'', the only allowed format is '\''human'\''
+ EOF
+ test_cmp err expect
+'
+
test_done
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index bae2419..e78315d 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -85,6 +85,7 @@ check_describe e-1-gHASH --tags HEAD^^
check_describe c-2-gHASH --tags HEAD^^2
check_describe B --tags HEAD^^2^
check_describe e --tags HEAD^^^
+check_describe e --tags --exact-match HEAD^^^
check_describe heads/main --all HEAD
check_describe tags/c-6-gHASH --all HEAD^
@@ -96,6 +97,13 @@ check_describe A-3-gHASH --long HEAD^^2
check_describe c-7-gHASH --tags
check_describe e-3-gHASH --first-parent --tags
+check_describe c-7-gHASH --tags --no-exact-match HEAD
+check_describe e-3-gHASH --first-parent --tags --no-exact-match HEAD
+
+test_expect_success '--exact-match failure' '
+ test_must_fail git describe --exact-match HEAD 2>err
+'
+
test_expect_success 'describe --contains defaults to HEAD without commit-ish' '
echo "A^0" >expect &&
git checkout A &&
@@ -262,7 +270,7 @@ test_expect_success 'name-rev --all' '
>expect.unsorted &&
for rev in $(git rev-list --all)
do
- git name-rev $rev >>expect.unsorted
+ git name-rev $rev >>expect.unsorted || return 1
done &&
sort <expect.unsorted >expect &&
git name-rev --all >actual.unsorted &&
@@ -270,19 +278,24 @@ test_expect_success 'name-rev --all' '
test_cmp expect actual
'
-test_expect_success 'name-rev --stdin' '
+test_expect_success 'name-rev --annotate-stdin' '
>expect.unsorted &&
for rev in $(git rev-list --all)
do
name=$(git name-rev --name-only $rev) &&
- echo "$rev ($name)" >>expect.unsorted
+ echo "$rev ($name)" >>expect.unsorted || return 1
done &&
sort <expect.unsorted >expect &&
- git rev-list --all | git name-rev --stdin >actual.unsorted &&
+ git rev-list --all | git name-rev --annotate-stdin >actual.unsorted &&
sort <actual.unsorted >actual &&
test_cmp expect actual
'
+test_expect_success 'name-rev --stdin deprecated' "
+ git rev-list --all | git name-rev --stdin 2>actual &&
+ grep -E 'warning: --stdin is deprecated' actual
+"
+
test_expect_success 'describe --contains with the exact tags' '
echo "A^0" >expect &&
tag_object=$(git rev-parse refs/tags/A) &&
@@ -379,7 +392,7 @@ test_expect_success 'describe directly tagged blob' '
test_expect_success 'describe tag object' '
git tag test-blob-1 -a -m msg unique-file:file &&
test_must_fail git describe test-blob-1 2>actual &&
- test_i18ngrep "fatal: test-blob-1 is neither a commit nor blob" actual
+ test_grep "fatal: test-blob-1 is neither a commit nor blob" actual
'
test_expect_success ULIMIT_STACK_SIZE 'name-rev works in a deep repo' '
@@ -390,9 +403,12 @@ test_expect_success ULIMIT_STACK_SIZE 'name-rev works in a deep repo' '
committer A U Thor <author@example.com> $((1000000000 + $i * 100)) +0200
data <<EOF
commit #$i
-EOF"
- test $i = 1 && echo "from refs/heads/main^0"
- i=$(($i + 1))
+EOF" &&
+ if test $i = 1
+ then
+ echo "from refs/heads/main^0"
+ fi &&
+ i=$(($i + 1)) || return 1
done | git fast-import &&
git checkout main &&
git tag far-far-away HEAD^ &&
@@ -480,6 +496,124 @@ test_expect_success 'name-rev covers all conditions while looking at parents' '
)
'
+# A-B-C-D-E-main
+#
+# Where C has a non-monotonically increasing commit timestamp w.r.t. other
+# commits
+test_expect_success 'non-monotonic commit dates setup' '
+ UNIX_EPOCH_ZERO="@0 +0000" &&
+ git init non-monotonic &&
+ test_commit -C non-monotonic A &&
+ test_commit -C non-monotonic --no-tag B &&
+ test_commit -C non-monotonic --no-tag --date "$UNIX_EPOCH_ZERO" C &&
+ test_commit -C non-monotonic D &&
+ test_commit -C non-monotonic E
+'
+
+test_expect_success 'name-rev with commitGraph handles non-monotonic timestamps' '
+ test_config -C non-monotonic core.commitGraph true &&
+ (
+ cd non-monotonic &&
+
+ git commit-graph write --reachable &&
+
+ echo "main~3 tags/D~2" >expect &&
+ git name-rev --tags main~3 >actual &&
+
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'name-rev --all works with non-monotonic timestamps' '
+ test_config -C non-monotonic core.commitGraph false &&
+ (
+ cd non-monotonic &&
+
+ rm -rf .git/info/commit-graph* &&
+
+ cat >tags <<-\EOF &&
+ tags/E
+ tags/D
+ tags/D~1
+ tags/D~2
+ tags/A
+ EOF
+
+ git log --pretty=%H >revs &&
+
+ paste -d" " revs tags | sort >expect &&
+
+ git name-rev --tags --all | sort >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'name-rev --annotate-stdin works with non-monotonic timestamps' '
+ test_config -C non-monotonic core.commitGraph false &&
+ (
+ cd non-monotonic &&
+
+ rm -rf .git/info/commit-graph* &&
+
+ cat >expect <<-\EOF &&
+ E
+ D
+ D~1
+ D~2
+ A
+ EOF
+
+ git log --pretty=%H >revs &&
+ git name-rev --tags --annotate-stdin --name-only <revs >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'name-rev --all works with commitGraph' '
+ test_config -C non-monotonic core.commitGraph true &&
+ (
+ cd non-monotonic &&
+
+ git commit-graph write --reachable &&
+
+ cat >tags <<-\EOF &&
+ tags/E
+ tags/D
+ tags/D~1
+ tags/D~2
+ tags/A
+ EOF
+
+ git log --pretty=%H >revs &&
+
+ paste -d" " revs tags | sort >expect &&
+
+ git name-rev --tags --all | sort >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'name-rev --annotate-stdin works with commitGraph' '
+ test_config -C non-monotonic core.commitGraph true &&
+ (
+ cd non-monotonic &&
+
+ git commit-graph write --reachable &&
+
+ cat >expect <<-\EOF &&
+ E
+ D
+ D~1
+ D~2
+ A
+ EOF
+
+ git log --pretty=%H >revs &&
+ git name-rev --tags --annotate-stdin --name-only <revs >actual &&
+ test_cmp expect actual
+ )
+'
+
# B
# o
# \
@@ -531,4 +665,10 @@ test_expect_success 'setup: describe commits with disjoint bases 2' '
check_describe -C disjoint2 "B-3-gHASH" HEAD
+test_expect_success 'setup misleading taggerdates' '
+ GIT_COMMITTER_DATE="2006-12-12 12:31" git tag -a -m "another tag" newer-tag-older-commit unique-file~1
+'
+
+check_describe newer-tag-older-commit~1 --contains unique-file~2
+
test_done
diff --git a/t/t6131-pathspec-icase.sh b/t/t6131-pathspec-icase.sh
index 39fc3f6..770cce0 100755
--- a/t/t6131-pathspec-icase.sh
+++ b/t/t6131-pathspec-icase.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test case insensitive pathspec limiting'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
if test_have_prereq CASE_INSENSITIVE_FS
diff --git a/t/t6132-pathspec-exclude.sh b/t/t6132-pathspec-exclude.sh
index 30328b8..9fdafeb 100755
--- a/t/t6132-pathspec-exclude.sh
+++ b/t/t6132-pathspec-exclude.sh
@@ -11,7 +11,7 @@ test_expect_success 'setup' '
fi &&
: >$p &&
git add $p &&
- git commit -m $p
+ git commit -m $p || return 1
done &&
git log --oneline --format=%s >actual &&
cat <<EOF >expect &&
@@ -195,6 +195,7 @@ test_expect_success 'multiple exclusions' '
'
test_expect_success 't_e_i() exclude case #8' '
+ test_when_finished "rm -fr case8" &&
git init case8 &&
(
cd case8 &&
@@ -244,4 +245,184 @@ test_expect_success 'grep --untracked PATTERN :(exclude)*FILE' '
test_cmp expect-grep actual-grep
'
+# Depending on the command, all negative pathspec needs to subtract
+# either from the full tree, or from the current directory.
+#
+# The sample tree checked out at this point has:
+# file
+# sub/file
+# sub/file2
+# sub/sub/file
+# sub/sub/sub/file
+# sub2/file
+#
+# but there may also be some cruft that interferes with "git clean"
+# and "git add" tests.
+
+test_expect_success 'archive with all negative' '
+ git reset --hard &&
+ git clean -f &&
+ git -C sub archive --format=tar HEAD -- ":!sub/" >archive &&
+ "$TAR" tf archive >actual &&
+ cat >expect <<-\EOF &&
+ file
+ file2
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'add with all negative' '
+ H=$(git rev-parse HEAD) &&
+ git reset --hard $H &&
+ git clean -f &&
+ test_when_finished "git reset --hard $H" &&
+ for path in file sub/file sub/sub/file sub2/file
+ do
+ echo smudge >>"$path" || return 1
+ done &&
+ git -C sub add -- ":!sub/" &&
+ git diff --name-only --no-renames --cached >actual &&
+ cat >expect <<-\EOF &&
+ file
+ sub/file
+ sub2/file
+ EOF
+ test_cmp expect actual &&
+ git diff --name-only --no-renames >actual &&
+ echo sub/sub/file >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'add -p with all negative' '
+ H=$(git rev-parse HEAD) &&
+ git reset --hard $H &&
+ git clean -f &&
+ test_when_finished "git reset --hard $H" &&
+ for path in file sub/file sub/sub/file sub2/file
+ do
+ echo smudge >>"$path" || return 1
+ done &&
+ yes | git -C sub add -p -- ":!sub/" &&
+ git diff --name-only --no-renames --cached >actual &&
+ cat >expect <<-\EOF &&
+ file
+ sub/file
+ sub2/file
+ EOF
+ test_cmp expect actual &&
+ git diff --name-only --no-renames >actual &&
+ echo sub/sub/file >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'clean with all negative' '
+ H=$(git rev-parse HEAD) &&
+ git reset --hard $H &&
+ test_when_finished "git reset --hard $H && git clean -f" &&
+ git clean -f &&
+ for path in file9 sub/file9 sub/sub/file9 sub2/file9
+ do
+ echo cruft >"$path" || return 1
+ done &&
+ git -C sub clean -f -- ":!sub" &&
+ test_path_is_file file9 &&
+ test_path_is_missing sub/file9 &&
+ test_path_is_file sub/sub/file9 &&
+ test_path_is_file sub2/file9
+'
+
+test_expect_success 'commit with all negative' '
+ H=$(git rev-parse HEAD) &&
+ git reset --hard $H &&
+ test_when_finished "git reset --hard $H" &&
+ for path in file sub/file sub/sub/file sub2/file
+ do
+ echo smudge >>"$path" || return 1
+ done &&
+ git -C sub commit -m sample -- ":!sub/" &&
+ git diff --name-only --no-renames HEAD^ HEAD >actual &&
+ cat >expect <<-\EOF &&
+ file
+ sub/file
+ sub2/file
+ EOF
+ test_cmp expect actual &&
+ git diff --name-only --no-renames HEAD >actual &&
+ echo sub/sub/file >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'reset with all negative' '
+ H=$(git rev-parse HEAD) &&
+ git reset --hard $H &&
+ test_when_finished "git reset --hard $H" &&
+ for path in file sub/file sub/sub/file sub2/file
+ do
+ echo smudge >>"$path" &&
+ git add "$path" || return 1
+ done &&
+ git -C sub reset --quiet -- ":!sub/" &&
+ git diff --name-only --no-renames --cached >actual &&
+ echo sub/sub/file >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'grep with all negative' '
+ H=$(git rev-parse HEAD) &&
+ git reset --hard $H &&
+ test_when_finished "git reset --hard $H" &&
+ for path in file sub/file sub/sub/file sub2/file
+ do
+ echo "needle $path" >>"$path" || return 1
+ done &&
+ git -C sub grep -h needle -- ":!sub/" >actual &&
+ cat >expect <<-\EOF &&
+ needle sub/file
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'ls-files with all negative' '
+ git reset --hard &&
+ git -C sub ls-files -- ":!sub/" >actual &&
+ cat >expect <<-\EOF &&
+ file
+ file2
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'rm with all negative' '
+ git reset --hard &&
+ test_when_finished "git reset --hard" &&
+ git -C sub rm -r --cached -- ":!sub/" >actual &&
+ git diff --name-only --no-renames --diff-filter=D --cached >actual &&
+ cat >expect <<-\EOF &&
+ sub/file
+ sub/file2
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'stash with all negative' '
+ H=$(git rev-parse HEAD) &&
+ git reset --hard $H &&
+ test_when_finished "git reset --hard $H" &&
+ for path in file sub/file sub/sub/file sub2/file
+ do
+ echo smudge >>"$path" || return 1
+ done &&
+ git -C sub stash push -m sample -- ":!sub/" &&
+ git diff --name-only --no-renames HEAD >actual &&
+ echo sub/sub/file >expect &&
+ test_cmp expect actual &&
+ git stash show --name-only >actual &&
+ cat >expect <<-\EOF &&
+ file
+ sub/file
+ sub2/file
+ EOF
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t6134-pathspec-in-submodule.sh b/t/t6134-pathspec-in-submodule.sh
index 0f1cb49..16ce4cf 100755
--- a/t/t6134-pathspec-in-submodule.sh
+++ b/t/t6134-pathspec-in-submodule.sh
@@ -2,6 +2,7 @@
test_description='test case exclude pathspec'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup a submodule' '
@@ -9,7 +10,7 @@ test_expect_success 'setup a submodule' '
: >pretzel/a &&
git -C pretzel add a &&
git -C pretzel commit -m "add a file" -- a &&
- git submodule add ./pretzel sub &&
+ git -c protocol.file.allow=always submodule add ./pretzel sub &&
git commit -a -m "add submodule" &&
git submodule deinit --all
'
@@ -26,7 +27,7 @@ test_expect_success 'error message for path inside submodule' '
test_expect_success 'error message for path inside submodule from within submodule' '
test_must_fail git -C sub add . 2>actual &&
- test_i18ngrep "in unpopulated submodule" actual
+ test_grep "in unpopulated submodule" actual
'
test_done
diff --git a/t/t6135-pathspec-with-attrs.sh b/t/t6135-pathspec-with-attrs.sh
index 457cc16..120dcd7 100755
--- a/t/t6135-pathspec-with-attrs.sh
+++ b/t/t6135-pathspec-with-attrs.sh
@@ -64,11 +64,24 @@ test_expect_success 'setup .gitattributes' '
fileSetLabel label
fileValue label=foo
fileWrongLabel label☺
+ newFileA* labelA
+ newFileB* labelB
EOF
- git add .gitattributes &&
+ echo fileSetLabel label1 >sub/.gitattributes &&
+ git add .gitattributes sub/.gitattributes &&
git commit -m "add attributes"
'
+test_expect_success 'setup .gitignore' '
+ cat <<-\EOF >.gitignore &&
+ actual
+ expect
+ pathspec_file
+ EOF
+ git add .gitignore &&
+ git commit -m "add gitignore"
+'
+
test_expect_success 'check specific set attr' '
cat <<-\EOF >expect &&
fileSetLabel
@@ -78,7 +91,17 @@ test_expect_success 'check specific set attr' '
test_cmp expect actual
'
-test_expect_success 'check specific set attr (2)' '
+test_expect_success 'check set attr with pathspec pattern' '
+ echo sub/fileSetLabel >expect &&
+
+ git ls-files ":(attr:label)sub" >actual &&
+ test_cmp expect actual &&
+
+ git ls-files ":(attr:label)sub/" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'check specific set attr in tree-ish' '
cat <<-\EOF >expect &&
HEAD:fileSetLabel
HEAD:sub/fileSetLabel
@@ -87,6 +110,16 @@ test_expect_success 'check specific set attr (2)' '
test_cmp expect actual
'
+test_expect_success 'check specific set attr with pathspec pattern in tree-ish' '
+ echo HEAD:sub/fileSetLabel >expect &&
+
+ git grep -l content HEAD ":(attr:label)sub" >actual &&
+ test_cmp expect actual &&
+
+ git grep -l content HEAD ":(attr:label)sub/" >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'check specific unset attr' '
cat <<-\EOF >expect &&
fileUnsetLabel
@@ -129,6 +162,7 @@ test_expect_success 'check specific value attr (2)' '
test_expect_success 'check unspecified attr' '
cat <<-\EOF >expect &&
.gitattributes
+ .gitignore
fileA
fileAB
fileAC
@@ -137,6 +171,7 @@ test_expect_success 'check unspecified attr' '
fileC
fileNoLabel
fileWrongLabel
+ sub/.gitattributes
sub/fileA
sub/fileAB
sub/fileAC
@@ -153,6 +188,7 @@ test_expect_success 'check unspecified attr' '
test_expect_success 'check unspecified attr (2)' '
cat <<-\EOF >expect &&
HEAD:.gitattributes
+ HEAD:.gitignore
HEAD:fileA
HEAD:fileAB
HEAD:fileAC
@@ -161,6 +197,7 @@ test_expect_success 'check unspecified attr (2)' '
HEAD:fileC
HEAD:fileNoLabel
HEAD:fileWrongLabel
+ HEAD:sub/.gitattributes
HEAD:sub/fileA
HEAD:sub/fileAB
HEAD:sub/fileAC
@@ -177,9 +214,11 @@ test_expect_success 'check unspecified attr (2)' '
test_expect_success 'check multiple unspecified attr' '
cat <<-\EOF >expect &&
.gitattributes
+ .gitignore
fileC
fileNoLabel
fileWrongLabel
+ sub/.gitattributes
sub/fileC
sub/fileNoLabel
sub/fileWrongLabel
@@ -212,17 +251,100 @@ test_expect_success 'check label excluding other labels' '
test_expect_success 'fail on multiple attr specifiers in one pathspec item' '
test_must_fail git ls-files . ":(attr:labelB,attr:labelC)" 2>actual &&
- test_i18ngrep "Only one" actual
+ test_grep "Only one" actual
'
-test_expect_success 'fail if attr magic is used places not implemented' '
+test_expect_success 'fail if attr magic is used in places not implemented' '
# The main purpose of this test is to check that we actually fail
# when you attempt to use attr magic in commands that do not implement
- # attr magic. This test does not advocate git-add to stay that way,
- # though, but git-add is convenient as it has its own internal pathspec
- # parsing.
- test_must_fail git add ":(attr:labelB)" 2>actual &&
- test_i18ngrep "magic not supported" actual
+ # attr magic. This test does not advocate check-ignore to stay that way.
+ # When you teach the command to grok the pathspec, you need to find
+ # another command to replace it for the test.
+ test_must_fail git check-ignore ":(attr:labelB)" 2>actual &&
+ test_grep "magic not supported" actual
+'
+
+test_expect_success 'check that attr magic works for git stash push' '
+ cat <<-\EOF >expect &&
+ A sub/newFileA-foo
+ EOF
+ >sub/newFileA-foo &&
+ >sub/newFileB-foo &&
+ git stash push --include-untracked -- ":(exclude,attr:labelB)" &&
+ git stash show --include-untracked --name-status >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'check that attr magic works for git add --all' '
+ cat <<-\EOF >expect &&
+ sub/newFileA-foo
+ EOF
+ >sub/newFileA-foo &&
+ >sub/newFileB-foo &&
+ git add --all ":(exclude,attr:labelB)" &&
+ git diff --name-only --cached >actual &&
+ git restore -W -S . &&
+ test_cmp expect actual
+'
+
+test_expect_success 'check that attr magic works for git add -u' '
+ cat <<-\EOF >expect &&
+ sub/fileA
+ EOF
+ >sub/newFileA-foo &&
+ >sub/newFileB-foo &&
+ >sub/fileA &&
+ >sub/fileB &&
+ git add -u ":(exclude,attr:labelB)" &&
+ git diff --name-only --cached >actual &&
+ git restore -S -W . && rm sub/new* &&
+ test_cmp expect actual
+'
+
+test_expect_success 'check that attr magic works for git add <path>' '
+ cat <<-\EOF >expect &&
+ fileA
+ fileB
+ sub/fileA
+ EOF
+ >fileA &&
+ >fileB &&
+ >sub/fileA &&
+ >sub/fileB &&
+ git add ":(exclude,attr:labelB)sub/*" &&
+ git diff --name-only --cached >actual &&
+ git restore -S -W . &&
+ test_cmp expect actual
+'
+
+test_expect_success 'check that attr magic works for git -add .' '
+ cat <<-\EOF >expect &&
+ sub/fileA
+ EOF
+ >fileA &&
+ >fileB &&
+ >sub/fileA &&
+ >sub/fileB &&
+ cd sub &&
+ git add . ":(exclude,attr:labelB)" &&
+ cd .. &&
+ git diff --name-only --cached >actual &&
+ git restore -S -W . &&
+ test_cmp expect actual
+'
+
+test_expect_success 'check that attr magic works for git add --pathspec-from-file' '
+ cat <<-\EOF >pathspec_file &&
+ :(exclude,attr:labelB)
+ EOF
+ cat <<-\EOF >expect &&
+ sub/newFileA-foo
+ EOF
+ >sub/newFileA-foo &&
+ >sub/newFileB-foo &&
+ git add --all --pathspec-from-file=pathspec_file &&
+ git diff --name-only --cached >actual &&
+ test_cmp expect actual
'
test_expect_success 'abort on giving invalid label on the command line' '
@@ -245,12 +367,57 @@ test_expect_success 'check attribute list' '
test_expect_success 'backslash cannot be the last character' '
test_must_fail git ls-files ":(attr:label=foo\\ labelA=bar)" 2>actual &&
- test_i18ngrep "not allowed as last character in attr value" actual
+ test_grep "not allowed as last character in attr value" actual
'
test_expect_success 'backslash cannot be used as a value' '
test_must_fail git ls-files ":(attr:label=f\\\oo)" 2>actual &&
- test_i18ngrep "for value matching" actual
+ test_grep "for value matching" actual
+'
+
+test_expect_success 'reading from .gitattributes in a subdirectory (1)' '
+ git ls-files ":(attr:label1)" >actual &&
+ test_write_lines "sub/fileSetLabel" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'reading from .gitattributes in a subdirectory (2)' '
+ git ls-files ":(attr:label1)sub" >actual &&
+ test_write_lines "sub/fileSetLabel" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'reading from .gitattributes in a subdirectory (3)' '
+ git ls-files ":(attr:label1)sub/" >actual &&
+ test_write_lines "sub/fileSetLabel" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success POSIXPERM 'pathspec with builtin_objectmode attr can be used' '
+ >mode_exec_file_1 &&
+
+ git status -s ":(attr:builtin_objectmode=100644)mode_exec_*" >actual &&
+ echo ?? mode_exec_file_1 >expect &&
+ test_cmp expect actual &&
+
+ git add mode_exec_file_1 &&
+ chmod +x mode_exec_file_1 &&
+ git status -s ":(attr:builtin_objectmode=100755)mode_exec_*" >actual &&
+ echo AM mode_exec_file_1 >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success POSIXPERM 'builtin_objectmode attr can be excluded' '
+ >mode_1_regular &&
+ >mode_1_exec &&
+ chmod +x mode_1_exec &&
+ git status -s ":(exclude,attr:builtin_objectmode=100644)" "mode_1_*" >actual &&
+ echo ?? mode_1_exec >expect &&
+ test_cmp expect actual &&
+
+ git status -s ":(exclude,attr:builtin_objectmode=100755)" "mode_1_*" >actual &&
+ echo ?? mode_1_regular >expect &&
+ test_cmp expect actual
'
test_done
diff --git a/t/t6136-pathspec-in-bare.sh b/t/t6136-pathspec-in-bare.sh
index b117251..2db37a6 100755
--- a/t/t6136-pathspec-in-bare.sh
+++ b/t/t6136-pathspec-in-bare.sh
@@ -2,6 +2,7 @@
test_description='diagnosing out-of-scope pathspec'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup a bare and non-bare repository' '
@@ -14,11 +15,11 @@ test_expect_success 'log and ls-files in a bare repository' '
cd bare &&
test_must_fail git log -- .. >out 2>err &&
test_must_be_empty out &&
- test_i18ngrep "outside repository" err &&
+ test_grep "outside repository" err &&
test_must_fail git ls-files -- .. >out 2>err &&
test_must_be_empty out &&
- test_i18ngrep "outside repository" err
+ test_grep "outside repository" err
)
'
@@ -27,11 +28,11 @@ test_expect_success 'log and ls-files in .git directory' '
cd .git &&
test_must_fail git log -- .. >out 2>err &&
test_must_be_empty out &&
- test_i18ngrep "outside repository" err &&
+ test_grep "outside repository" err &&
test_must_fail git ls-files -- .. >out 2>err &&
test_must_be_empty out &&
- test_i18ngrep "outside repository" err
+ test_grep "outside repository" err
)
'
diff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh
index 44f55d9..5a221f8 100755
--- a/t/t6200-fmt-merge-msg.sh
+++ b/t/t6200-fmt-merge-msg.sh
@@ -81,6 +81,36 @@ test_expect_success GPG 'set up a signed tag' '
git tag -s -m signed-tag-msg signed-good-tag left
'
+test_expect_success GPGSSH 'created ssh signed commit and tag' '
+ test_config gpg.format ssh &&
+ git checkout -b signed-ssh &&
+ touch file &&
+ git add file &&
+ git commit -m "ssh signed" -S"${GPGSSH_KEY_PRIMARY}" &&
+ git tag -s -u"${GPGSSH_KEY_PRIMARY}" -m signed-ssh-tag-msg signed-good-ssh-tag left &&
+ git tag -s -u"${GPGSSH_KEY_UNTRUSTED}" -m signed-ssh-tag-msg-untrusted signed-untrusted-ssh-tag left
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'create signed tags with keys having defined lifetimes' '
+ test_when_finished "test_unconfig commit.gpgsign" &&
+ test_config gpg.format ssh &&
+ git checkout -b signed-expiry-ssh &&
+ touch file &&
+ git add file &&
+
+ echo expired >file && test_tick && git commit -a -m expired -S"${GPGSSH_KEY_EXPIRED}" &&
+ git tag -s -u "${GPGSSH_KEY_EXPIRED}" -m expired-signed expired-signed &&
+
+ echo notyetvalid >file && test_tick && git commit -a -m notyetvalid -S"${GPGSSH_KEY_NOTYETVALID}" &&
+ git tag -s -u "${GPGSSH_KEY_NOTYETVALID}" -m notyetvalid-signed notyetvalid-signed &&
+
+ echo timeboxedvalid >file && test_tick && git commit -a -m timeboxedvalid -S"${GPGSSH_KEY_TIMEBOXEDVALID}" &&
+ git tag -s -u "${GPGSSH_KEY_TIMEBOXEDVALID}" -m timeboxedvalid-signed timeboxedvalid-signed &&
+
+ echo timeboxedinvalid >file && test_tick && git commit -a -m timeboxedinvalid -S"${GPGSSH_KEY_TIMEBOXEDINVALID}" &&
+ git tag -s -u "${GPGSSH_KEY_TIMEBOXEDINVALID}" -m timeboxedinvalid-signed timeboxedinvalid-signed
+'
+
test_expect_success 'message for merging local branch' '
echo "Merge branch ${apos}left${apos}" >expected &&
@@ -94,8 +124,9 @@ test_expect_success 'message for merging local branch' '
test_expect_success GPG 'message for merging local tag signed by good key' '
git checkout main &&
git fetch . signed-good-tag &&
- git fmt-merge-msg <.git/FETCH_HEAD >actual 2>&1 &&
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
grep "^Merge tag ${apos}signed-good-tag${apos}" actual &&
+ grep "^signed-tag-msg" actual &&
grep "^# gpg: Signature made" actual &&
grep "^# gpg: Good signature from" actual
'
@@ -103,12 +134,77 @@ test_expect_success GPG 'message for merging local tag signed by good key' '
test_expect_success GPG 'message for merging local tag signed by unknown key' '
git checkout main &&
git fetch . signed-good-tag &&
- GNUPGHOME=. git fmt-merge-msg <.git/FETCH_HEAD >actual 2>&1 &&
+ GNUPGHOME=. git fmt-merge-msg <.git/FETCH_HEAD >actual &&
grep "^Merge tag ${apos}signed-good-tag${apos}" actual &&
+ grep "^signed-tag-msg" actual &&
grep "^# gpg: Signature made" actual &&
grep -E "^# gpg: Can${apos}t check signature: (public key not found|No public key)" actual
'
+test_expect_success GPGSSH 'message for merging local tag signed by good ssh key' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git checkout main &&
+ git fetch . signed-good-ssh-tag &&
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ grep "^Merge tag ${apos}signed-good-ssh-tag${apos}" actual &&
+ grep "^signed-ssh-tag-msg" actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual
+'
+
+test_expect_success GPGSSH 'message for merging local tag signed by unknown ssh key' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git checkout main &&
+ git fetch . signed-untrusted-ssh-tag &&
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ grep "^Merge tag ${apos}signed-untrusted-ssh-tag${apos}" actual &&
+ grep "^signed-ssh-tag-msg-untrusted" actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ grep "${GPGSSH_KEY_NOT_TRUSTED}" actual
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'message for merging local tag signed by expired ssh key' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git checkout main &&
+ git fetch . expired-signed &&
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ grep "^Merge tag ${apos}expired-signed${apos}" actual &&
+ grep "^expired-signed" actual &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'message for merging local tag signed by not yet valid ssh key' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git checkout main &&
+ git fetch . notyetvalid-signed &&
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ grep "^Merge tag ${apos}notyetvalid-signed${apos}" actual &&
+ grep "^notyetvalid-signed" actual &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'message for merging local tag signed by valid timeboxed ssh key' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git checkout main &&
+ git fetch . timeboxedvalid-signed &&
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ grep "^Merge tag ${apos}timeboxedvalid-signed${apos}" actual &&
+ grep "^timeboxedvalid-signed" actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'message for merging local tag signed by invalid timeboxed ssh key' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git checkout main &&
+ git fetch . timeboxedinvalid-signed &&
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ grep "^Merge tag ${apos}timeboxedinvalid-signed${apos}" actual &&
+ grep "^timeboxedinvalid-signed" actual &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
+'
+
test_expect_success 'message for merging external branch' '
echo "Merge branch ${apos}left${apos} of $(pwd)" >expected &&
@@ -491,7 +587,7 @@ test_expect_success 'merge-msg lots of commits' '
while test $i -gt 9
do
echo " $i" &&
- i=$(($i-1))
+ i=$(($i-1)) || return 1
done &&
echo " ..."
} >expected &&
@@ -545,7 +641,35 @@ test_expect_success 'merge-msg with "merging" an annotated tag' '
test_cmp expected .git/MERGE_MSG
'
+test_expect_success 'merge --into-name=<name>' '
+ test_when_finished "git checkout main" &&
+ git checkout -B side main &&
+ git commit --allow-empty -m "One step ahead" &&
+
+ git checkout --detach main &&
+ git merge --no-ff side &&
+ git show -s --format="%s" >full.0 &&
+ head -n1 full.0 >actual &&
+ # expect that HEAD is shown as-is
+ grep -e "Merge branch .side. into HEAD$" actual &&
+
+ git reset --hard main &&
+ git merge --no-ff --into-name=main side &&
+ git show -s --format="%s" >full.1 &&
+ head -n1 full.1 >actual &&
+ # expect that we pretend to be merging to main, that is suppressed
+ grep -e "Merge branch .side.$" actual &&
+
+ git checkout -b throwaway main &&
+ git merge --no-ff --into-name=main side &&
+ git show -s --format="%s" >full.2 &&
+ head -n1 full.2 >actual &&
+ # expect that we pretend to be merging to main, that is suppressed
+ grep -e "Merge branch .side.$" actual
+'
+
test_expect_success 'merge.suppressDest configuration' '
+ test_when_finished "git checkout main" &&
git checkout -B side main &&
git commit --allow-empty -m "One step ahead" &&
git checkout main &&
@@ -562,7 +686,19 @@ test_expect_success 'merge.suppressDest configuration' '
git -c merge.suppressDest="ma?*[rn]" fmt-merge-msg <.git/FETCH_HEAD >full.3 &&
head -n1 full.3 >actual &&
grep -e "Merge branch .side." actual &&
- ! grep -e " into main$" actual
+ ! grep -e " into main$" actual &&
+
+ git checkout --detach HEAD &&
+ git -c merge.suppressDest="main" fmt-merge-msg <.git/FETCH_HEAD >full.4 &&
+ head -n1 full.4 >actual &&
+ grep -e "Merge branch .side. into HEAD$" actual &&
+
+ git -c merge.suppressDest="main" fmt-merge-msg \
+ --into-name=main <.git/FETCH_HEAD >full.5 &&
+ head -n1 full.5 >actual &&
+ grep -e "Merge branch .side." actual &&
+ ! grep -e " into main$" actual &&
+ ! grep -e " into HEAD$" actual
'
test_done
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index 80679d5..eb6c820 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -5,10 +5,8 @@
test_description='for-each-ref test'
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
. ./test-lib.sh
+GNUPGHOME_NOT_USED=$GNUPGHOME
. "$TEST_DIRECTORY"/lib-gpg.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
@@ -22,11 +20,19 @@ setdate_and_increment () {
export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
}
+test_object_file_size () {
+ oid=$(git rev-parse "$1")
+ path=".git/objects/$(test_oid_to_path $oid)"
+ test_file_size "$path"
+}
+
test_expect_success setup '
- test_oid_cache <<-EOF &&
- disklen sha1:138
- disklen sha256:154
+ # setup .mailmap
+ cat >.mailmap <<-EOF &&
+ A Thor <athor@example.com> A U Thor <author@example.com>
+ C Mitter <cmitter@example.com> C O Mitter <committer@example.com>
EOF
+
setdate_and_increment &&
echo "Using $datestamp" > one &&
git add one &&
@@ -43,25 +49,29 @@ test_expect_success setup '
git config push.default current
'
-test_atom() {
+test_atom () {
case "$1" in
head) ref=refs/heads/main ;;
tag) ref=refs/tags/testtag ;;
sym) ref=refs/heads/sym ;;
*) ref=$1 ;;
esac
+ format=$2
+ test_do=test_expect_${4:-success}
+
printf '%s\n' "$3" >expected
- test_expect_${4:-success} $PREREQ "basic atom: $1 $2" "
- git for-each-ref --format='%($2)' $ref >actual &&
+ $test_do $PREREQ "basic atom: $ref $format" '
+ git for-each-ref --format="%($format)" "$ref" >actual &&
sanitize_pgp <actual >actual.clean &&
test_cmp expected actual.clean
- "
+ '
+
# Automatically test "contents:size" atom after testing "contents"
- if test "$2" = "contents"
+ if test "$format" = "contents"
then
# for commit leg, $3 is changed there
expect=$(printf '%s' "$3" | wc -c)
- test_expect_${4:-success} $PREREQ "basic atom: $1 contents:size" '
+ $test_do $PREREQ "basic atom: $ref contents:size" '
type=$(git cat-file -t "$ref") &&
case $type in
tag)
@@ -85,7 +95,6 @@ test_atom() {
}
hexlen=$(test_oid hexsz)
-disklen=$(test_oid disklen)
test_atom head refname refs/heads/main
test_atom head refname: refs/heads/main
@@ -120,7 +129,7 @@ test_atom head push:strip=1 remotes/myfork/main
test_atom head push:strip=-1 main
test_atom head objecttype commit
test_atom head objectsize $((131 + hexlen))
-test_atom head objectsize:disk $disklen
+test_atom head objectsize:disk $(test_object_file_size refs/heads/main)
test_atom head deltabase $ZERO_OID
test_atom head objectname $(git rev-parse refs/heads/main)
test_atom head objectname:short $(git rev-parse --short refs/heads/main)
@@ -143,15 +152,31 @@ test_atom head '*objectname' ''
test_atom head '*objecttype' ''
test_atom head author 'A U Thor <author@example.com> 1151968724 +0200'
test_atom head authorname 'A U Thor'
+test_atom head authorname:mailmap 'A Thor'
test_atom head authoremail '<author@example.com>'
test_atom head authoremail:trim 'author@example.com'
test_atom head authoremail:localpart 'author'
+test_atom head authoremail:trim,localpart 'author'
+test_atom head authoremail:mailmap '<athor@example.com>'
+test_atom head authoremail:mailmap,trim 'athor@example.com'
+test_atom head authoremail:trim,mailmap 'athor@example.com'
+test_atom head authoremail:mailmap,localpart 'athor'
+test_atom head authoremail:localpart,mailmap 'athor'
+test_atom head authoremail:mailmap,trim,localpart,mailmap,trim 'athor'
test_atom head authordate 'Tue Jul 4 01:18:44 2006 +0200'
test_atom head committer 'C O Mitter <committer@example.com> 1151968723 +0200'
test_atom head committername 'C O Mitter'
+test_atom head committername:mailmap 'C Mitter'
test_atom head committeremail '<committer@example.com>'
test_atom head committeremail:trim 'committer@example.com'
test_atom head committeremail:localpart 'committer'
+test_atom head committeremail:localpart,trim 'committer'
+test_atom head committeremail:mailmap '<cmitter@example.com>'
+test_atom head committeremail:mailmap,trim 'cmitter@example.com'
+test_atom head committeremail:trim,mailmap 'cmitter@example.com'
+test_atom head committeremail:mailmap,localpart 'cmitter'
+test_atom head committeremail:localpart,mailmap 'cmitter'
+test_atom head committeremail:trim,mailmap,trim,trim,localpart 'cmitter'
test_atom head committerdate 'Tue Jul 4 01:18:43 2006 +0200'
test_atom head tag ''
test_atom head tagger ''
@@ -178,8 +203,8 @@ test_atom tag upstream ''
test_atom tag push ''
test_atom tag objecttype tag
test_atom tag objectsize $((114 + hexlen))
-test_atom tag objectsize:disk $disklen
-test_atom tag '*objectsize:disk' $disklen
+test_atom tag objectsize:disk $(test_object_file_size refs/tags/testtag)
+test_atom tag '*objectsize:disk' $(test_object_file_size refs/heads/main)
test_atom tag deltabase $ZERO_OID
test_atom tag '*deltabase' $ZERO_OID
test_atom tag objectname $(git rev-parse refs/tags/testtag)
@@ -201,22 +226,46 @@ test_atom tag '*objectname' $(git rev-parse refs/tags/testtag^{})
test_atom tag '*objecttype' 'commit'
test_atom tag author ''
test_atom tag authorname ''
+test_atom tag authorname:mailmap ''
test_atom tag authoremail ''
test_atom tag authoremail:trim ''
test_atom tag authoremail:localpart ''
+test_atom tag authoremail:trim,localpart ''
+test_atom tag authoremail:mailmap ''
+test_atom tag authoremail:mailmap,trim ''
+test_atom tag authoremail:trim,mailmap ''
+test_atom tag authoremail:mailmap,localpart ''
+test_atom tag authoremail:localpart,mailmap ''
+test_atom tag authoremail:mailmap,trim,localpart,mailmap,trim ''
test_atom tag authordate ''
test_atom tag committer ''
test_atom tag committername ''
+test_atom tag committername:mailmap ''
test_atom tag committeremail ''
test_atom tag committeremail:trim ''
test_atom tag committeremail:localpart ''
+test_atom tag committeremail:localpart,trim ''
+test_atom tag committeremail:mailmap ''
+test_atom tag committeremail:mailmap,trim ''
+test_atom tag committeremail:trim,mailmap ''
+test_atom tag committeremail:mailmap,localpart ''
+test_atom tag committeremail:localpart,mailmap ''
+test_atom tag committeremail:trim,mailmap,trim,trim,localpart ''
test_atom tag committerdate ''
test_atom tag tag 'testtag'
test_atom tag tagger 'C O Mitter <committer@example.com> 1151968725 +0200'
test_atom tag taggername 'C O Mitter'
+test_atom tag taggername:mailmap 'C Mitter'
test_atom tag taggeremail '<committer@example.com>'
test_atom tag taggeremail:trim 'committer@example.com'
test_atom tag taggeremail:localpart 'committer'
+test_atom tag taggeremail:trim,localpart 'committer'
+test_atom tag taggeremail:mailmap '<cmitter@example.com>'
+test_atom tag taggeremail:mailmap,trim 'cmitter@example.com'
+test_atom tag taggeremail:trim,mailmap 'cmitter@example.com'
+test_atom tag taggeremail:mailmap,localpart 'cmitter'
+test_atom tag taggeremail:localpart,mailmap 'cmitter'
+test_atom tag taggeremail:trim,mailmap,trim,localpart,localpart 'cmitter'
test_atom tag taggerdate 'Tue Jul 4 01:18:45 2006 +0200'
test_atom tag creator 'C O Mitter <committer@example.com> 1151968725 +0200'
test_atom tag creatordate 'Tue Jul 4 01:18:45 2006 +0200'
@@ -269,6 +318,66 @@ test_expect_success 'arguments to %(objectname:short=) must be positive integers
test_must_fail git for-each-ref --format="%(objectname:short=foo)"
'
+test_bad_atom () {
+ case "$1" in
+ head) ref=refs/heads/main ;;
+ tag) ref=refs/tags/testtag ;;
+ sym) ref=refs/heads/sym ;;
+ *) ref=$1 ;;
+ esac
+ format=$2
+ test_do=test_expect_${4:-success}
+
+ printf '%s\n' "$3" >expect
+ $test_do $PREREQ "err basic atom: $ref $format" '
+ test_must_fail git for-each-ref \
+ --format="%($format)" "$ref" 2>error &&
+ test_cmp expect error
+ '
+}
+
+test_bad_atom head 'authoremail:foo' \
+ 'fatal: unrecognized %(authoremail) argument: foo'
+
+test_bad_atom head 'authoremail:mailmap,trim,bar' \
+ 'fatal: unrecognized %(authoremail) argument: bar'
+
+test_bad_atom head 'authoremail:trim,' \
+ 'fatal: unrecognized %(authoremail) argument: '
+
+test_bad_atom head 'authoremail:mailmaptrim' \
+ 'fatal: unrecognized %(authoremail) argument: trim'
+
+test_bad_atom head 'committeremail: ' \
+ 'fatal: unrecognized %(committeremail) argument: '
+
+test_bad_atom head 'committeremail: trim,foo' \
+ 'fatal: unrecognized %(committeremail) argument: trim,foo'
+
+test_bad_atom head 'committeremail:mailmap,localpart ' \
+ 'fatal: unrecognized %(committeremail) argument: '
+
+test_bad_atom head 'committeremail:trim_localpart' \
+ 'fatal: unrecognized %(committeremail) argument: _localpart'
+
+test_bad_atom head 'committeremail:localpart,,,trim' \
+ 'fatal: unrecognized %(committeremail) argument: ,,trim'
+
+test_bad_atom tag 'taggeremail:mailmap,trim, foo ' \
+ 'fatal: unrecognized %(taggeremail) argument: foo '
+
+test_bad_atom tag 'taggeremail:trim,localpart,' \
+ 'fatal: unrecognized %(taggeremail) argument: '
+
+test_bad_atom tag 'taggeremail:mailmap;localpart trim' \
+ 'fatal: unrecognized %(taggeremail) argument: ;localpart trim'
+
+test_bad_atom tag 'taggeremail:localpart trim' \
+ 'fatal: unrecognized %(taggeremail) argument: trim'
+
+test_bad_atom tag 'taggeremail:mailmap,mailmap,trim,qux,localpart,trim' \
+ 'fatal: unrecognized %(taggeremail) argument: qux,localpart,trim'
+
test_date () {
f=$1 &&
committer_date=$2 &&
@@ -419,6 +528,11 @@ test_expect_success 'Verify descending sort' '
test_cmp expected actual
'
+test_expect_success 'Give help even with invalid sort atoms' '
+ test_expect_code 129 git for-each-ref --sort=bogus -h >actual 2>&1 &&
+ grep "^usage: git for-each-ref" actual
+'
+
cat >expected <<\EOF
refs/tags/testtag
refs/tags/testtag-2
@@ -446,6 +560,41 @@ test_expect_success 'exercise glob patterns with prefixes' '
'
cat >expected <<\EOF
+refs/tags/bar
+refs/tags/baz
+refs/tags/testtag
+EOF
+
+test_expect_success 'exercise patterns with prefix exclusions' '
+ for tag in foo/one foo/two foo/three bar baz
+ do
+ git tag "$tag" || return 1
+ done &&
+ test_when_finished "git tag -d foo/one foo/two foo/three bar baz" &&
+ git for-each-ref --format="%(refname)" \
+ refs/tags/ --exclude=refs/tags/foo >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+refs/tags/bar
+refs/tags/baz
+refs/tags/foo/one
+refs/tags/testtag
+EOF
+
+test_expect_success 'exercise patterns with pattern exclusions' '
+ for tag in foo/one foo/two foo/three bar baz
+ do
+ git tag "$tag" || return 1
+ done &&
+ test_when_finished "git tag -d foo/one foo/two foo/three bar baz" &&
+ git for-each-ref --format="%(refname)" \
+ refs/tags/ --exclude="refs/tags/foo/t*" >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
'refs/heads/main'
'refs/remotes/origin/main'
'refs/tags/testtag'
@@ -559,6 +708,144 @@ test_expect_success 'color.ui=always does not override tty check' '
test_cmp expected.bare actual
'
+test_expect_success 'setup for describe atom tests' '
+ git init -b master describe-repo &&
+ (
+ cd describe-repo &&
+
+ test_commit --no-tag one &&
+ git tag tagone &&
+
+ test_commit --no-tag two &&
+ git tag -a -m "tag two" tagtwo
+ )
+'
+
+test_expect_success 'describe atom vs git describe' '
+ (
+ cd describe-repo &&
+
+ git for-each-ref --format="%(objectname)" \
+ refs/tags/ >obj &&
+ while read hash
+ do
+ if desc=$(git describe $hash)
+ then
+ : >expect-contains-good
+ else
+ : >expect-contains-bad
+ fi &&
+ echo "$hash $desc" || return 1
+ done <obj >expect &&
+ test_path_exists expect-contains-good &&
+ test_path_exists expect-contains-bad &&
+
+ git for-each-ref --format="%(objectname) %(describe)" \
+ refs/tags/ >actual 2>err &&
+ test_cmp expect actual &&
+ test_must_be_empty err
+ )
+'
+
+test_expect_success 'describe:tags vs describe --tags' '
+ (
+ cd describe-repo &&
+ git describe --tags >expect &&
+ git for-each-ref --format="%(describe:tags)" \
+ refs/heads/master >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'describe:abbrev=... vs describe --abbrev=...' '
+ (
+ cd describe-repo &&
+
+ # Case 1: We have commits between HEAD and the most
+ # recent tag reachable from it
+ test_commit --no-tag file &&
+ git describe --abbrev=14 >expect &&
+ git for-each-ref --format="%(describe:abbrev=14)" \
+ refs/heads/master >actual &&
+ test_cmp expect actual &&
+
+ # Make sure the hash used is atleast 14 digits long
+ sed -e "s/^.*-g\([0-9a-f]*\)$/\1/" <actual >hexpart &&
+ test 15 -le $(wc -c <hexpart) &&
+
+ # Case 2: We have a tag at HEAD, describe directly gives
+ # the name of the tag
+ git tag -a -m tagged tagname &&
+ git describe --abbrev=14 >expect &&
+ git for-each-ref --format="%(describe:abbrev=14)" \
+ refs/heads/master >actual &&
+ test_cmp expect actual &&
+ test tagname = $(cat actual)
+ )
+'
+
+test_expect_success 'describe:match=... vs describe --match ...' '
+ (
+ cd describe-repo &&
+ git tag -a -m "tag foo" tag-foo &&
+ git describe --match "*-foo" >expect &&
+ git for-each-ref --format="%(describe:match="*-foo")" \
+ refs/heads/master >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'describe:exclude:... vs describe --exclude ...' '
+ (
+ cd describe-repo &&
+ git tag -a -m "tag bar" tag-bar &&
+ git describe --exclude "*-bar" >expect &&
+ git for-each-ref --format="%(describe:exclude="*-bar")" \
+ refs/heads/master >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'deref with describe atom' '
+ (
+ cd describe-repo &&
+ cat >expect <<-\EOF &&
+
+ tagname
+ tagname
+ tagname
+
+ tagtwo
+ EOF
+ git for-each-ref --format="%(*describe)" >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'err on bad describe atom arg' '
+ (
+ cd describe-repo &&
+
+ # The bad arg is the only arg passed to describe atom
+ cat >expect <<-\EOF &&
+ fatal: unrecognized %(describe) argument: baz
+ EOF
+ test_must_fail git for-each-ref --format="%(describe:baz)" \
+ refs/heads/master 2>actual &&
+ test_cmp expect actual &&
+
+ # The bad arg is in the middle of the option string
+ # passed to the describe atom
+ cat >expect <<-\EOF &&
+ fatal: unrecognized %(describe) argument: qux=1,abbrev=14
+ EOF
+ test_must_fail git for-each-ref \
+ --format="%(describe:tags,qux=1,abbrev=14)" \
+ ref/heads/master 2>actual &&
+ test_cmp expect actual
+ )
+'
+
cat >expected <<\EOF
heads/main
tags/main
@@ -604,7 +891,7 @@ test_expect_success 'create tag without tagger' '
git tag -a -m "Broken tag" taggerless &&
git tag -f taggerless $(git cat-file tag taggerless |
sed -e "/^tagger /d" |
- git hash-object --stdin -w -t tag)
+ git hash-object --literally --stdin -w -t tag)
'
test_atom refs/tags/taggerless type 'commit'
@@ -841,16 +1128,16 @@ test_expect_success 'Verify sorts with raw' '
test_expect_success 'Verify sorts with raw:size' '
cat >expected <<-EOF &&
refs/myblobs/blob8
- refs/myblobs/first
refs/myblobs/blob7
- refs/heads/main
refs/myblobs/blob4
refs/myblobs/blob1
refs/myblobs/blob2
refs/myblobs/blob3
refs/myblobs/blob5
refs/myblobs/blob6
+ refs/myblobs/first
refs/mytrees/first
+ refs/heads/main
EOF
git for-each-ref --format="%(refname)" --sort=raw:size \
refs/heads/main refs/myblobs/ refs/mytrees/first >actual &&
@@ -950,10 +1237,7 @@ test_expect_success '%(raw) with --shell and --sort=raw must fail' '
'
test_expect_success '%(raw:size) with --shell' '
- git for-each-ref --format="%(raw:size)" | while read line
- do
- echo "'\''$line'\''" >>expect
- done &&
+ git for-each-ref --format="%(raw:size)" | sed "s/^/$SQ/;s/$/$SQ/" >expect &&
git for-each-ref --format="%(raw:size)" --shell >actual &&
test_cmp expect actual
'
@@ -965,6 +1249,17 @@ test_expect_success 'for-each-ref --format compare with cat-file --batch' '
test_cmp expected actual
'
+test_expect_success 'verify sorts with contents:size' '
+ cat >expect <<-\EOF &&
+ refs/heads/main
+ refs/heads/newtag
+ refs/heads/ambiguous
+ EOF
+ git for-each-ref --format="%(refname)" \
+ --sort=contents:size refs/heads/ >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'set up multiple-sort tags' '
for when in 100000 200000
do
@@ -1019,6 +1314,94 @@ test_expect_success 'equivalent sorts fall back on refname' '
test_cmp expected actual
'
+test_expect_success '--no-sort cancels the previous sort keys' '
+ cat >expected <<-\EOF &&
+ 100000 <user1@example.com> refs/tags/multi-ref1-100000-user1
+ 100000 <user2@example.com> refs/tags/multi-ref1-100000-user2
+ 100000 <user1@example.com> refs/tags/multi-ref2-100000-user1
+ 100000 <user2@example.com> refs/tags/multi-ref2-100000-user2
+ 200000 <user1@example.com> refs/tags/multi-ref1-200000-user1
+ 200000 <user2@example.com> refs/tags/multi-ref1-200000-user2
+ 200000 <user1@example.com> refs/tags/multi-ref2-200000-user1
+ 200000 <user2@example.com> refs/tags/multi-ref2-200000-user2
+ EOF
+ git for-each-ref \
+ --format="%(taggerdate:unix) %(taggeremail) %(refname)" \
+ --sort=-refname \
+ --sort=taggeremail \
+ --no-sort \
+ --sort=taggerdate \
+ "refs/tags/multi-*" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '--no-sort without subsequent --sort prints expected refs' '
+ cat >expected <<-\EOF &&
+ refs/tags/multi-ref1-100000-user1
+ refs/tags/multi-ref1-100000-user2
+ refs/tags/multi-ref1-200000-user1
+ refs/tags/multi-ref1-200000-user2
+ refs/tags/multi-ref2-100000-user1
+ refs/tags/multi-ref2-100000-user2
+ refs/tags/multi-ref2-200000-user1
+ refs/tags/multi-ref2-200000-user2
+ EOF
+
+ # Sort the results with `sort` for a consistent comparison against
+ # expected
+ git for-each-ref \
+ --format="%(refname)" \
+ --no-sort \
+ "refs/tags/multi-*" | sort >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'set up custom date sorting' '
+ # Dates:
+ # - Wed Feb 07 2024 21:34:20 +0000
+ # - Tue Dec 14 1999 00:05:22 +0000
+ # - Fri Jun 04 2021 11:26:51 +0000
+ # - Mon Jan 22 2007 16:44:01 GMT+0000
+ i=1 &&
+ for when in 1707341660 945129922 1622806011 1169484241
+ do
+ GIT_COMMITTER_DATE="@$when +0000" \
+ GIT_COMMITTER_EMAIL="user@example.com" \
+ git tag -m "tag $when" custom-dates-$i &&
+ i=$(($i+1)) || return 1
+ done
+'
+
+test_expect_success 'sort by date defaults to full timestamp' '
+ cat >expected <<-\EOF &&
+ 945129922 refs/tags/custom-dates-2
+ 1169484241 refs/tags/custom-dates-4
+ 1622806011 refs/tags/custom-dates-3
+ 1707341660 refs/tags/custom-dates-1
+ EOF
+
+ git for-each-ref \
+ --format="%(creatordate:unix) %(refname)" \
+ --sort=creatordate \
+ "refs/tags/custom-dates-*" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'sort by custom date format' '
+ cat >expected <<-\EOF &&
+ 00:05:22 refs/tags/custom-dates-2
+ 11:26:51 refs/tags/custom-dates-3
+ 16:44:01 refs/tags/custom-dates-4
+ 21:34:20 refs/tags/custom-dates-1
+ EOF
+
+ git for-each-ref \
+ --format="%(creatordate:format:%H:%M:%S) %(refname)" \
+ --sort="creatordate:format:%H:%M:%S" \
+ "refs/tags/custom-dates-*" >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '
test_when_finished "git checkout main" &&
git for-each-ref --format="%(HEAD) %(refname:short)" refs/heads/ >actual &&
@@ -1222,6 +1605,24 @@ test_expect_success 'basic atom: rest must fail' '
test_must_fail git for-each-ref --format="%(rest)" refs/heads/main
'
+test_expect_success 'HEAD atom does not take arguments' '
+ test_must_fail git for-each-ref --format="%(HEAD:foo)" 2>err &&
+ echo "fatal: %(HEAD) does not take arguments" >expect &&
+ test_cmp expect err
+'
+
+test_expect_success 'subject atom rejects unknown arguments' '
+ test_must_fail git for-each-ref --format="%(subject:foo)" 2>err &&
+ echo "fatal: unrecognized %(subject) argument: foo" >expect &&
+ test_cmp expect err
+'
+
+test_expect_success 'refname atom rejects unknown arguments' '
+ test_must_fail git for-each-ref --format="%(refname:foo)" 2>err &&
+ echo "fatal: unrecognized %(refname) argument: foo" >expect &&
+ test_cmp expect err
+'
+
test_expect_success 'trailer parsing not fooled by --- line' '
git commit --allow-empty -F - <<-\EOF &&
this is the subject
@@ -1315,7 +1716,7 @@ test_expect_success ':remotename and :remoteref' '
echo "${pair#*=}" >expect &&
git for-each-ref --format="${pair%=*}" \
refs/heads/main >actual &&
- test_cmp expect actual
+ test_cmp expect actual || exit 1
done &&
git branch push-simple &&
git config branch.push-simple.pushRemote from &&
@@ -1336,6 +1737,14 @@ test_expect_success 'for-each-ref --ignore-case ignores case' '
test_cmp expect actual
'
+test_expect_success 'for-each-ref --omit-empty works' '
+ git for-each-ref --format="%(refname)" >actual &&
+ test_line_count -gt 1 actual &&
+ git for-each-ref --format="%(if:equals=refs/heads/main)%(refname)%(then)%(refname)%(end)" --omit-empty >actual &&
+ echo refs/heads/main >expect &&
+ test_cmp expect actual
+'
+
test_expect_success 'for-each-ref --ignore-case works on multiple sort keys' '
# name refs numerically to avoid case-insensitive filesystem conflicts
nr=0 &&
@@ -1386,4 +1795,304 @@ test_expect_success 'for-each-ref reports broken tags' '
refs/tags/broken-tag-*
'
+test_expect_success 'set up tag with signature and no blank lines' '
+ git tag -F - fake-sig-no-blanks <<-\EOF
+ this is the subject
+ -----BEGIN PGP SIGNATURE-----
+ not a real signature, but we just care about the
+ subject/body parsing. It is important here that
+ there are no blank lines in the signature.
+ -----END PGP SIGNATURE-----
+ EOF
+'
+
+test_atom refs/tags/fake-sig-no-blanks contents:subject 'this is the subject'
+test_atom refs/tags/fake-sig-no-blanks contents:body ''
+test_atom refs/tags/fake-sig-no-blanks contents:signature "$sig"
+
+test_expect_success 'set up tag with CRLF signature' '
+ append_cr <<-\EOF |
+ this is the subject
+ -----BEGIN PGP SIGNATURE-----
+
+ not a real signature, but we just care about
+ the subject/body parsing. It is important here
+ that there is a blank line separating this
+ from the signature header.
+ -----END PGP SIGNATURE-----
+ EOF
+ git tag -F - --cleanup=verbatim fake-sig-crlf
+'
+
+test_atom refs/tags/fake-sig-crlf contents:subject 'this is the subject'
+test_atom refs/tags/fake-sig-crlf contents:body ''
+
+# CRLF is retained in the signature, so we have to pass our expected value
+# through append_cr. But test_atom requires a shell string, which means command
+# substitution, and the shell will strip trailing newlines from the output of
+# the substitution. Hack around it by adding and then removing a dummy line.
+sig_crlf="$(printf "%s" "$sig" | append_cr; echo dummy)"
+sig_crlf=${sig_crlf%dummy}
+test_atom refs/tags/fake-sig-crlf contents:signature "$sig_crlf"
+
+test_expect_success 'git for-each-ref --stdin: empty' '
+ >in &&
+ git for-each-ref --format="%(refname)" --stdin <in >actual &&
+ git for-each-ref --format="%(refname)" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git for-each-ref --stdin: fails if extra args' '
+ >in &&
+ test_must_fail git for-each-ref --format="%(refname)" \
+ --stdin refs/heads/extra <in 2>err &&
+ grep "unknown arguments supplied with --stdin" err
+'
+
+test_expect_success 'git for-each-ref --stdin: matches' '
+ cat >in <<-EOF &&
+ refs/tags/multi*
+ refs/heads/amb*
+ EOF
+
+ cat >expect <<-EOF &&
+ refs/heads/ambiguous
+ refs/tags/multi-ref1-100000-user1
+ refs/tags/multi-ref1-100000-user2
+ refs/tags/multi-ref1-200000-user1
+ refs/tags/multi-ref1-200000-user2
+ refs/tags/multi-ref2-100000-user1
+ refs/tags/multi-ref2-100000-user2
+ refs/tags/multi-ref2-200000-user1
+ refs/tags/multi-ref2-200000-user2
+ refs/tags/multiline
+ EOF
+
+ git for-each-ref --format="%(refname)" --stdin <in >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git for-each-ref with non-existing refs' '
+ cat >in <<-EOF &&
+ refs/heads/this-ref-does-not-exist
+ refs/tags/bogus
+ EOF
+
+ git for-each-ref --format="%(refname)" --stdin <in >actual &&
+ test_must_be_empty actual &&
+
+ xargs git for-each-ref --format="%(refname)" <in >actual &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'git for-each-ref with nested tags' '
+ git tag -am "Normal tag" nested/base HEAD &&
+ git tag -am "Nested tag" nested/nest1 refs/tags/nested/base &&
+ git tag -am "Double nested tag" nested/nest2 refs/tags/nested/nest1 &&
+
+ head_oid="$(git rev-parse HEAD)" &&
+ base_tag_oid="$(git rev-parse refs/tags/nested/base)" &&
+ nest1_tag_oid="$(git rev-parse refs/tags/nested/nest1)" &&
+ nest2_tag_oid="$(git rev-parse refs/tags/nested/nest2)" &&
+
+ cat >expect <<-EOF &&
+ refs/tags/nested/base $base_tag_oid tag $head_oid commit
+ refs/tags/nested/nest1 $nest1_tag_oid tag $head_oid commit
+ refs/tags/nested/nest2 $nest2_tag_oid tag $head_oid commit
+ EOF
+
+ git for-each-ref \
+ --format="%(refname) %(objectname) %(objecttype) %(*objectname) %(*objecttype)" \
+ refs/tags/nested/ >actual &&
+ test_cmp expect actual
+'
+
+GRADE_FORMAT="%(signature:grade)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)"
+TRUSTLEVEL_FORMAT="%(signature:trustlevel)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)"
+
+test_expect_success GPG 'setup for signature atom using gpg' '
+ git checkout -b signed &&
+
+ test_when_finished "test_unconfig commit.gpgSign" &&
+
+ echo "1" >file &&
+ git add file &&
+ test_tick &&
+ git commit -S -m "file: 1" &&
+ git tag first-signed &&
+
+ echo "2" >file &&
+ test_tick &&
+ git commit -a -m "file: 2" &&
+ git tag second-unsigned &&
+
+ git config commit.gpgSign 1 &&
+ echo "3" >file &&
+ test_tick &&
+ git commit -a --no-gpg-sign -m "file: 3" &&
+ git tag third-unsigned &&
+
+ test_tick &&
+ git rebase -f HEAD^^ && git tag second-signed HEAD^ &&
+ git tag third-signed &&
+
+ echo "4" >file &&
+ test_tick &&
+ git commit -a -SB7227189 -m "file: 4" &&
+ git tag fourth-signed &&
+
+ echo "5" >file &&
+ test_tick &&
+ git commit -a --no-gpg-sign -m "file: 5" &&
+ git tag fifth-unsigned &&
+
+ echo "6" >file &&
+ test_tick &&
+ git commit -a --no-gpg-sign -m "file: 6" &&
+
+ test_tick &&
+ git rebase -f HEAD^^ &&
+ git tag fifth-signed HEAD^ &&
+ git tag sixth-signed &&
+
+ echo "7" >file &&
+ test_tick &&
+ git commit -a --no-gpg-sign -m "file: 7" &&
+ git tag seventh-unsigned
+'
+
+test_expect_success GPGSSH 'setup for signature atom using ssh' '
+ test_when_finished "test_unconfig gpg.format user.signingkey" &&
+
+ test_config gpg.format ssh &&
+ test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+ echo "8" >file &&
+ test_tick &&
+ git add file &&
+ git commit -S -m "file: 8" &&
+ git tag eighth-signed-ssh
+'
+
+test_expect_success GPG2 'bare signature atom' '
+ git verify-commit first-signed 2>expect &&
+ echo >>expect &&
+ git for-each-ref refs/tags/first-signed \
+ --format="%(signature)" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG 'show good signature with custom format' '
+ git verify-commit first-signed &&
+ cat >expect <<-\EOF &&
+ G
+ 13B6F51ECDDE430D
+ C O Mitter <committer@example.com>
+ 73D758744BE721698EC54E8713B6F51ECDDE430D
+ 73D758744BE721698EC54E8713B6F51ECDDE430D
+ EOF
+ git for-each-ref refs/tags/first-signed \
+ --format="$GRADE_FORMAT" >actual &&
+ test_cmp expect actual
+'
+test_expect_success GPGSSH 'show good signature with custom format
+ with ssh' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") &&
+ cat >expect.tmpl <<-\EOF &&
+ G
+ FINGERPRINT
+ principal with number 1
+ FINGERPRINT
+
+ EOF
+ sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect &&
+ git for-each-ref refs/tags/eighth-signed-ssh \
+ --format="$GRADE_FORMAT" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG 'signature atom with grade option and bad signature' '
+ git cat-file commit third-signed >raw &&
+ sed -e "s/^file: 3/file: 3 forged/" raw >forged1 &&
+ FORGED1=$(git hash-object -w -t commit forged1) &&
+ git update-ref refs/tags/third-signed "$FORGED1" &&
+ test_must_fail git verify-commit "$FORGED1" &&
+
+ cat >expect <<-\EOF &&
+ B
+ 13B6F51ECDDE430D
+ C O Mitter <committer@example.com>
+
+
+ EOF
+ git for-each-ref refs/tags/third-signed \
+ --format="$GRADE_FORMAT" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG 'show untrusted signature with custom format' '
+ cat >expect <<-\EOF &&
+ U
+ 65A0EEA02E30CAD7
+ Eris Discordia <discord@example.net>
+ F8364A59E07FFE9F4D63005A65A0EEA02E30CAD7
+ D4BE22311AD3131E5EDA29A461092E85B7227189
+ EOF
+ git for-each-ref refs/tags/fourth-signed \
+ --format="$GRADE_FORMAT" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG 'show untrusted signature with undefined trust level' '
+ cat >expect <<-\EOF &&
+ undefined
+ 65A0EEA02E30CAD7
+ Eris Discordia <discord@example.net>
+ F8364A59E07FFE9F4D63005A65A0EEA02E30CAD7
+ D4BE22311AD3131E5EDA29A461092E85B7227189
+ EOF
+ git for-each-ref refs/tags/fourth-signed \
+ --format="$TRUSTLEVEL_FORMAT" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG 'show untrusted signature with ultimate trust level' '
+ cat >expect <<-\EOF &&
+ ultimate
+ 13B6F51ECDDE430D
+ C O Mitter <committer@example.com>
+ 73D758744BE721698EC54E8713B6F51ECDDE430D
+ 73D758744BE721698EC54E8713B6F51ECDDE430D
+ EOF
+ git for-each-ref refs/tags/sixth-signed \
+ --format="$TRUSTLEVEL_FORMAT" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG 'show unknown signature with custom format' '
+ cat >expect <<-\EOF &&
+ E
+ 13B6F51ECDDE430D
+
+
+
+ EOF
+ GNUPGHOME="$GNUPGHOME_NOT_USED" git for-each-ref \
+ refs/tags/sixth-signed --format="$GRADE_FORMAT" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG 'show lack of signature with custom format' '
+ cat >expect <<-\EOF &&
+ N
+
+
+
+
+ EOF
+ git for-each-ref refs/tags/seventh-unsigned \
+ --format="$GRADE_FORMAT" >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t6301-for-each-ref-errors.sh b/t/t6301-for-each-ref-errors.sh
index 40edf9d..83b8a19 100755
--- a/t/t6301-for-each-ref-errors.sh
+++ b/t/t6301-for-each-ref-errors.sh
@@ -2,6 +2,7 @@
test_description='for-each-ref errors for broken refs'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
ZEROS=$ZERO_OID
@@ -14,7 +15,7 @@ test_expect_success setup '
git for-each-ref --format="%(objectname) %(refname)" >brief-list
'
-test_expect_success 'Broken refs are reported correctly' '
+test_expect_success REFFILES 'Broken refs are reported correctly' '
r=refs/heads/bogus &&
: >.git/$r &&
test_when_finished "rm -f .git/$r" &&
@@ -24,7 +25,7 @@ test_expect_success 'Broken refs are reported correctly' '
test_cmp broken-err err
'
-test_expect_success 'NULL_SHA1 refs are reported correctly' '
+test_expect_success REFFILES 'NULL_SHA1 refs are reported correctly' '
r=refs/heads/zeros &&
echo $ZEROS >.git/$r &&
test_when_finished "rm -f .git/$r" &&
@@ -38,19 +39,32 @@ test_expect_success 'NULL_SHA1 refs are reported correctly' '
'
test_expect_success 'Missing objects are reported correctly' '
- r=refs/heads/missing &&
- echo $MISSING >.git/$r &&
- test_when_finished "rm -f .git/$r" &&
- echo "fatal: missing object $MISSING for $r" >missing-err &&
+ test_when_finished "git update-ref -d refs/heads/missing" &&
+ test-tool ref-store main update-ref msg refs/heads/missing "$MISSING" "$ZERO_OID" REF_SKIP_OID_VERIFICATION &&
+ echo "fatal: missing object $MISSING for refs/heads/missing" >missing-err &&
test_must_fail git for-each-ref 2>err &&
test_cmp missing-err err &&
(
cat brief-list &&
- echo "$MISSING $r"
+ echo "$MISSING refs/heads/missing"
) | sort -k 2 >missing-brief-expected &&
git for-each-ref --format="%(objectname) %(refname)" >brief-out 2>brief-err &&
test_cmp missing-brief-expected brief-out &&
test_must_be_empty brief-err
'
+test_expect_success 'ahead-behind requires an argument' '
+ test_must_fail git for-each-ref \
+ --format="%(ahead-behind)" 2>err &&
+ echo "fatal: expected format: %(ahead-behind:<committish>)" >expect &&
+ test_cmp expect err
+'
+
+test_expect_success 'missing ahead-behind base' '
+ test_must_fail git for-each-ref \
+ --format="%(ahead-behind:refs/heads/missing)" 2>err &&
+ echo "fatal: failed to find '\''refs/heads/missing'\''" >expect &&
+ test_cmp expect err
+'
+
test_done
diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
index 1537aa2..948f1bb 100755
--- a/t/t6302-for-each-ref-filter.sh
+++ b/t/t6302-for-each-ref-filter.sh
@@ -2,9 +2,6 @@
test_description='test for-each-refs usage of ref-filter APIs'
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-gpg.sh
@@ -34,6 +31,37 @@ test_expect_success 'setup some history and refs' '
git update-ref refs/odd/spot main
'
+test_expect_success '--include-root-refs pattern prints pseudorefs' '
+ cat >expect <<-\EOF &&
+ HEAD
+ ORIG_HEAD
+ refs/heads/main
+ refs/heads/side
+ refs/odd/spot
+ refs/tags/annotated-tag
+ refs/tags/doubly-annotated-tag
+ refs/tags/doubly-signed-tag
+ refs/tags/four
+ refs/tags/one
+ refs/tags/signed-tag
+ refs/tags/three
+ refs/tags/two
+ EOF
+ git update-ref ORIG_HEAD main &&
+ git for-each-ref --format="%(refname)" --include-root-refs >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--include-root-refs with other patterns' '
+ cat >expect <<-\EOF &&
+ HEAD
+ ORIG_HEAD
+ EOF
+ git update-ref ORIG_HEAD main &&
+ git for-each-ref --format="%(refname)" --include-root-refs "*HEAD" >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'filtering with --points-at' '
cat >expect <<-\EOF &&
refs/heads/main
@@ -48,6 +76,8 @@ test_expect_success 'check signed tags with --points-at' '
sed -e "s/Z$//" >expect <<-\EOF &&
refs/heads/side Z
refs/tags/annotated-tag four
+ refs/tags/doubly-annotated-tag four
+ refs/tags/doubly-signed-tag four
refs/tags/four Z
refs/tags/signed-tag four
EOF
diff --git a/t/t6400-merge-df.sh b/t/t6400-merge-df.sh
index 57a67cf..3de4ef6 100755
--- a/t/t6400-merge-df.sh
+++ b/t/t6400-merge-df.sh
@@ -126,7 +126,7 @@ test_expect_success 'Simple merge in repo with interesting pathnames' '
# foo/bar-2/baz
# The fact that foo/bar-2 appears between foo/bar and foo/bar/baz
# can trip up some codepaths, and is the point of this test.
- test_create_repo name-ordering &&
+ git init name-ordering &&
(
cd name-ordering &&
diff --git a/t/t6401-merge-criss-cross.sh b/t/t6401-merge-criss-cross.sh
index 9d5e992..1962310 100755
--- a/t/t6401-merge-criss-cross.sh
+++ b/t/t6401-merge-criss-cross.sh
@@ -8,6 +8,8 @@
test_description='Test criss-cross merge'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'prepare repository' '
diff --git a/t/t6402-merge-rename.sh b/t/t6402-merge-rename.sh
index 3a32b1a..2738b50 100755
--- a/t/t6402-merge-rename.sh
+++ b/t/t6402-merge-rename.sh
@@ -210,7 +210,7 @@ test_expect_success 'updated working tree file should prevent the merge' '
echo >>M one line addition &&
cat M >M.saved &&
git update-index M &&
- test_expect_code 128 git pull --no-rebase . yellow &&
+ test_expect_code 2 git pull --no-rebase . yellow &&
test_cmp M M.saved &&
rm -f M.saved
'
@@ -311,13 +311,13 @@ test_expect_success 'Rename+D/F conflict; renamed file merges but dir in way' '
git checkout -q renamed-file-has-no-conflicts^0 &&
test_must_fail git merge --strategy=recursive dir-in-way >output &&
- test_i18ngrep "CONFLICT (modify/delete): dir/file-in-the-way" output &&
- test_i18ngrep "Auto-merging dir" output &&
+ test_grep "CONFLICT (modify/delete): dir/file-in-the-way" output &&
+ test_grep "Auto-merging dir" output &&
if test "$GIT_TEST_MERGE_ALGORITHM" = ort
then
- test_i18ngrep "moving it to dir~HEAD instead" output
+ test_grep "moving it to dir~HEAD instead" output
else
- test_i18ngrep "Adding as dir~HEAD instead" output
+ test_grep "Adding as dir~HEAD instead" output
fi &&
test_stdout_line_count = 3 git ls-files -u &&
@@ -338,13 +338,13 @@ test_expect_success 'Same as previous, but merged other way' '
test_must_fail git merge --strategy=recursive renamed-file-has-no-conflicts >output 2>errors &&
! grep "error: refusing to lose untracked file at" errors &&
- test_i18ngrep "CONFLICT (modify/delete): dir/file-in-the-way" output &&
- test_i18ngrep "Auto-merging dir" output &&
+ test_grep "CONFLICT (modify/delete): dir/file-in-the-way" output &&
+ test_grep "Auto-merging dir" output &&
if test "$GIT_TEST_MERGE_ALGORITHM" = ort
then
- test_i18ngrep "moving it to dir~renamed-file-has-no-conflicts instead" output
+ test_grep "moving it to dir~renamed-file-has-no-conflicts instead" output
else
- test_i18ngrep "Adding as dir~renamed-file-has-no-conflicts instead" output
+ test_grep "Adding as dir~renamed-file-has-no-conflicts instead" output
fi &&
test_stdout_line_count = 3 git ls-files -u &&
diff --git a/t/t6403-merge-file.sh b/t/t6403-merge-file.sh
index 2f421d9..fb872c5 100755
--- a/t/t6403-merge-file.sh
+++ b/t/t6403-merge-file.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='RCS merge replacement: merge-file'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -54,7 +56,67 @@ test_expect_success 'setup' '
deduxit me super semitas jusitiae,
EOF
- printf "propter nomen suum." >>new4.txt
+ printf "propter nomen suum." >>new4.txt &&
+
+ cat >base.c <<-\EOF &&
+ int f(int x, int y)
+ {
+ if (x == 0)
+ {
+ return y;
+ }
+ return x;
+ }
+
+ int g(size_t u)
+ {
+ while (u < 30)
+ {
+ u++;
+ }
+ return u;
+ }
+ EOF
+
+ cat >ours.c <<-\EOF &&
+ int g(size_t u)
+ {
+ while (u < 30)
+ {
+ u++;
+ }
+ return u;
+ }
+
+ int h(int x, int y, int z)
+ {
+ if (z == 0)
+ {
+ return x;
+ }
+ return y;
+ }
+ EOF
+
+ cat >theirs.c <<-\EOF
+ int f(int x, int y)
+ {
+ if (x == 0)
+ {
+ return y;
+ }
+ return x;
+ }
+
+ int g(size_t u)
+ {
+ while (u > 34)
+ {
+ u--;
+ }
+ return u;
+ }
+ EOF
'
test_expect_success 'merge with no changes' '
@@ -63,11 +125,30 @@ test_expect_success 'merge with no changes' '
test_cmp test.txt orig.txt
'
+test_expect_success 'merge with no changes with --object-id' '
+ git add orig.txt &&
+ git merge-file -p --object-id :orig.txt :orig.txt :orig.txt >actual &&
+ test_cmp actual orig.txt
+'
+
test_expect_success "merge without conflict" '
cp new1.txt test.txt &&
git merge-file test.txt orig.txt new2.txt
'
+test_expect_success 'merge without conflict with --object-id' '
+ git add orig.txt new2.txt &&
+ git merge-file --object-id :orig.txt :orig.txt :new2.txt >actual &&
+ git rev-parse :new2.txt >expected &&
+ test_cmp actual expected
+'
+
+test_expect_success 'can accept object ID with --object-id' '
+ git merge-file --object-id $(test_oid empty_blob) $(test_oid empty_blob) :new2.txt >actual &&
+ git rev-parse :new2.txt >expected &&
+ test_cmp actual expected
+'
+
test_expect_success 'works in subdirectory' '
mkdir dir &&
cp new1.txt dir/a.txt &&
@@ -136,6 +217,31 @@ test_expect_success "expected conflict markers" '
test_cmp expect.txt test.txt
'
+test_expect_success "merge with conflicts with --object-id" '
+ git add backup.txt orig.txt new3.txt &&
+ test_must_fail git merge-file -p --object-id :backup.txt :orig.txt :new3.txt >actual &&
+ sed -e "s/<< test.txt/<< :backup.txt/" \
+ -e "s/>> new3.txt/>> :new3.txt/" \
+ expect.txt >expect &&
+ test_cmp expect actual &&
+ test_must_fail git merge-file --object-id :backup.txt :orig.txt :new3.txt >oid &&
+ git cat-file blob "$(cat oid)" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success "merge with conflicts with --object-id with labels" '
+ git add backup.txt orig.txt new3.txt &&
+ test_must_fail git merge-file -p --object-id \
+ -L test.txt -L orig.txt -L new3.txt \
+ :backup.txt :orig.txt :new3.txt >actual &&
+ test_cmp expect.txt actual &&
+ test_must_fail git merge-file --object-id \
+ -L test.txt -L orig.txt -L new3.txt \
+ :backup.txt :orig.txt :new3.txt >oid &&
+ git cat-file blob "$(cat oid)" >actual &&
+ test_cmp expect.txt actual
+'
+
test_expect_success "merge conflicting with --ours" '
cp backup.txt test.txt &&
@@ -254,6 +360,14 @@ test_expect_success 'binary files cannot be merged' '
grep "Cannot merge binary files" merge.err
'
+test_expect_success 'binary files cannot be merged with --object-id' '
+ cp "$TEST_DIRECTORY"/test-binary-1.png . &&
+ git add orig.txt new1.txt test-binary-1.png &&
+ test_must_fail git merge-file --object-id \
+ :orig.txt :test-binary-1.png :new1.txt 2> merge.err &&
+ grep "Cannot merge binary files" merge.err
+'
+
test_expect_success 'MERGE_ZEALOUS simplifies non-conflicts' '
sed -e "s/deerit.\$/deerit;/" -e "s/me;\$/me./" <new5.txt >new6.txt &&
sed -e "s/deerit.\$/deerit,/" -e "s/me;\$/me,/" <new5.txt >new7.txt &&
@@ -387,4 +501,72 @@ test_expect_success 'conflict sections match existing line endings' '
test $(tr "\015" Q <nolf.txt | grep "^[<=>].*Q$" | wc -l) = 0
'
+test_expect_success '--object-id fails without repository' '
+ empty="$(test_oid empty_blob)" &&
+ nongit test_must_fail git merge-file --object-id $empty $empty $empty 2>err &&
+ grep "not a git repository" err
+'
+
+test_expect_success 'merging C files with "myers" diff algorithm creates some spurious conflicts' '
+ cat >expect.c <<-\EOF &&
+ int g(size_t u)
+ {
+ while (u < 30)
+ {
+ u++;
+ }
+ return u;
+ }
+
+ int h(int x, int y, int z)
+ {
+ <<<<<<< ours.c
+ if (z == 0)
+ ||||||| base.c
+ while (u < 30)
+ =======
+ while (u > 34)
+ >>>>>>> theirs.c
+ {
+ <<<<<<< ours.c
+ return x;
+ ||||||| base.c
+ u++;
+ =======
+ u--;
+ >>>>>>> theirs.c
+ }
+ return y;
+ }
+ EOF
+
+ test_must_fail git merge-file -p --diff3 --diff-algorithm myers ours.c base.c theirs.c >myers_output.c &&
+ test_cmp expect.c myers_output.c
+'
+
+test_expect_success 'merging C files with "histogram" diff algorithm avoids some spurious conflicts' '
+ cat >expect.c <<-\EOF &&
+ int g(size_t u)
+ {
+ while (u > 34)
+ {
+ u--;
+ }
+ return u;
+ }
+
+ int h(int x, int y, int z)
+ {
+ if (z == 0)
+ {
+ return x;
+ }
+ return y;
+ }
+ EOF
+
+ git merge-file -p --diff3 --diff-algorithm histogram ours.c base.c theirs.c >histogram_output.c &&
+ test_cmp expect.c histogram_output.c
+'
+
test_done
diff --git a/t/t6404-recursive-merge.sh b/t/t6404-recursive-merge.sh
index eaf48e9..3621551 100755
--- a/t/t6404-recursive-merge.sh
+++ b/t/t6404-recursive-merge.sh
@@ -4,6 +4,7 @@ test_description='Test merge without common ancestors'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# This scenario is based on a real-world repository of Shawn Pearce.
@@ -108,8 +109,13 @@ test_expect_success 'refuse to merge binary files' '
printf "\0\0" >binary-file &&
git add binary-file &&
git commit -m binary2 &&
- test_must_fail git merge F >merge.out 2>merge.err &&
- grep "Cannot merge binary files: binary-file (HEAD vs. F)" merge.err
+ if test "$GIT_TEST_MERGE_ALGORITHM" = ort
+ then
+ test_must_fail git merge F >merge_output
+ else
+ test_must_fail git merge F 2>merge_output
+ fi &&
+ grep "Cannot merge binary files: binary-file (HEAD vs. F)" merge_output
'
test_expect_success 'mark rename/delete as unmerged' '
diff --git a/t/t6405-merge-symlinks.sh b/t/t6405-merge-symlinks.sh
index 7435fce..29e2b25 100755
--- a/t/t6405-merge-symlinks.sh
+++ b/t/t6405-merge-symlinks.sh
@@ -11,6 +11,7 @@ if core.symlinks is false.'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t6406-merge-attr.sh b/t/t6406-merge-attr.sh
index 8494645..156a1ef 100755
--- a/t/t6406-merge-attr.sh
+++ b/t/t6406-merge-attr.sh
@@ -8,6 +8,7 @@ test_description='per path merge controlled by merge attribute'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -41,11 +42,15 @@ test_expect_success setup '
#!/bin/sh
orig="$1" ours="$2" theirs="$3" exit="$4" path=$5
+ orig_name="$6" our_name="$7" their_name="$8"
(
echo "orig is $orig"
echo "ours is $ours"
echo "theirs is $theirs"
echo "path is $path"
+ echo "orig_name is $orig_name"
+ echo "our_name is $our_name"
+ echo "their_name is $their_name"
echo "=== orig ==="
cat "$orig"
echo "=== ours ==="
@@ -55,6 +60,12 @@ test_expect_success setup '
) >"$ours+"
cat "$ours+" >"$ours"
rm -f "$ours+"
+
+ if test -f ./please-abort
+ then
+ echo >>./please-abort killing myself
+ kill -9 $$
+ fi
exit "$exit"
EOF
chmod +x ./custom-merge
@@ -62,10 +73,10 @@ test_expect_success setup '
test_expect_success merge '
- {
- echo "binary -merge"
- echo "union merge=union"
- } >.gitattributes &&
+ cat >.gitattributes <<-\EOF &&
+ binary -merge
+ union merge=union
+ EOF
if git merge main
then
@@ -114,7 +125,7 @@ test_expect_success 'custom merge backend' '
git reset --hard anchor &&
git config --replace-all \
- merge.custom.driver "./custom-merge %O %A %B 0 %P" &&
+ merge.custom.driver "./custom-merge %O %A %B 0 %P %S %X %Y" &&
git config --replace-all \
merge.custom.name "custom merge driver for testing" &&
@@ -125,7 +136,8 @@ test_expect_success 'custom merge backend' '
o=$(git unpack-file main^:text) &&
a=$(git unpack-file side^:text) &&
b=$(git unpack-file main:text) &&
- sh -c "./custom-merge $o $a $b 0 text" &&
+ base_revid=$(git rev-parse --short main^) &&
+ sh -c "./custom-merge $o $a $b 0 text $base_revid HEAD main" &&
sed -e 1,3d $a >check-2 &&
cmp check-1 check-2 &&
rm -f $o $a $b
@@ -135,7 +147,7 @@ test_expect_success 'custom merge backend' '
git reset --hard anchor &&
git config --replace-all \
- merge.custom.driver "./custom-merge %O %A %B 1 %P" &&
+ merge.custom.driver "./custom-merge %O %A %B 1 %P %S %X %Y" &&
git config --replace-all \
merge.custom.name "custom merge driver for testing" &&
@@ -152,7 +164,8 @@ test_expect_success 'custom merge backend' '
o=$(git unpack-file main^:text) &&
a=$(git unpack-file anchor:text) &&
b=$(git unpack-file main:text) &&
- sh -c "./custom-merge $o $a $b 0 text" &&
+ base_revid=$(git rev-parse --short main^) &&
+ sh -c "./custom-merge $o $a $b 0 text $base_revid HEAD main" &&
sed -e 1,3d $a >check-2 &&
cmp check-1 check-2 &&
sed -e 1,3d -e 4q $a >check-3 &&
@@ -161,9 +174,27 @@ test_expect_success 'custom merge backend' '
rm -f $o $a $b
'
+test_expect_success !WINDOWS 'custom merge driver that is killed with a signal' '
+ test_when_finished "rm -f output please-abort" &&
+
+ git reset --hard anchor &&
+ git config --replace-all \
+ merge.custom.driver "./custom-merge %O %A %B 0 %P %S %X %Y" &&
+ git config --replace-all \
+ merge.custom.name "custom merge driver for testing" &&
+
+ >./please-abort &&
+ echo "* merge=custom" >.gitattributes &&
+ test_must_fail git merge main 2>err &&
+ grep "^error: failed to execute internal merge" err &&
+ git ls-files -u >output &&
+ git diff --name-only HEAD >>output &&
+ test_must_be_empty output
+'
+
test_expect_success 'up-to-date merge without common ancestor' '
- test_create_repo repo1 &&
- test_create_repo repo2 &&
+ git init repo1 &&
+ git init repo2 &&
test_tick &&
(
cd repo1 &&
@@ -221,8 +252,13 @@ test_expect_success 'binary files with union attribute' '
printf "two\0" >bin.txt &&
git commit -am two &&
- test_must_fail git merge bin-main 2>stderr &&
- grep -i "warning.*cannot merge.*HEAD vs. bin-main" stderr
+ if test "$GIT_TEST_MERGE_ALGORITHM" = ort
+ then
+ test_must_fail git merge bin-main >output
+ else
+ test_must_fail git merge bin-main 2>output
+ fi &&
+ grep -i "warning.*cannot merge.*HEAD vs. bin-main" output
'
test_done
diff --git a/t/t6407-merge-binary.sh b/t/t6407-merge-binary.sh
index d4273f2..0753fc9 100755
--- a/t/t6407-merge-binary.sh
+++ b/t/t6407-merge-binary.sh
@@ -5,6 +5,7 @@ test_description='ask merge-recursive to merge binary files'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -42,14 +43,9 @@ test_expect_success resolve '
rm -f a* m* &&
git reset --hard anchor &&
- if git merge -s resolve main
- then
- echo Oops, should not have succeeded
- false
- else
- git ls-files -s >current
- test_cmp expect current
- fi
+ test_must_fail git merge -s resolve main &&
+ git ls-files -s >current &&
+ test_cmp expect current
'
test_expect_success recursive '
@@ -57,14 +53,9 @@ test_expect_success recursive '
rm -f a* m* &&
git reset --hard anchor &&
- if git merge -s recursive main
- then
- echo Oops, should not have succeeded
- false
- else
- git ls-files -s >current
- test_cmp expect current
- fi
+ test_must_fail git merge -s recursive main &&
+ git ls-files -s >current &&
+ test_cmp expect current
'
test_done
diff --git a/t/t6408-merge-up-to-date.sh b/t/t6408-merge-up-to-date.sh
index 7763c1b..8a1ba6d 100755
--- a/t/t6408-merge-up-to-date.sh
+++ b/t/t6408-merge-up-to-date.sh
@@ -2,6 +2,7 @@
test_description='merge fast-forward and up to date'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t6409-merge-subtree.sh b/t/t6409-merge-subtree.sh
index ba7890e..e9ba6f1 100755
--- a/t/t6409-merge-subtree.sh
+++ b/t/t6409-merge-subtree.sh
@@ -10,7 +10,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
test_expect_success setup '
s="1 2 3 4 5 6 7 8" &&
- for i in $s; do echo $i; done >hello &&
+ test_write_lines $s >hello &&
git add hello &&
git commit -m initial &&
git checkout -b side &&
@@ -18,7 +18,7 @@ test_expect_success setup '
git add hello &&
git commit -m second &&
git checkout main &&
- for i in mundo $s; do echo $i; done >hello &&
+ test_write_lines mundo $s >hello &&
git add hello &&
git commit -m main
@@ -27,7 +27,7 @@ test_expect_success setup '
test_expect_success 'subtree available and works like recursive' '
git merge -s subtree side &&
- for i in mundo $s world; do echo $i; done >expect &&
+ test_write_lines mundo $s world >expect &&
test_cmp expect hello
'
diff --git a/t/t6411-merge-filemode.sh b/t/t6411-merge-filemode.sh
index f54c915..b618272 100755
--- a/t/t6411-merge-filemode.sh
+++ b/t/t6411-merge-filemode.sh
@@ -4,6 +4,7 @@ test_description='merge: handle file mode'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'set up mode change in one branch' '
@@ -51,10 +52,10 @@ test_expect_success 'set up mode change in both branches' '
: >file2 &&
git add file2 &&
git commit -m b2 &&
- {
- echo "100755 $H 2 file2"
- echo "100644 $H 3 file2"
- } >expect
+ cat >expect <<-EOF
+ 100755 $H 2 file2
+ 100644 $H 3 file2
+ EOF
'
do_both_modes () {
diff --git a/t/t6412-merge-large-rename.sh b/t/t6412-merge-large-rename.sh
index c50d315..ca018d1 100755
--- a/t/t6412-merge-large-rename.sh
+++ b/t/t6412-merge-large-rename.sh
@@ -37,18 +37,18 @@ test_rename() {
test_might_fail git branch -D test$n &&
git reset --hard initial &&
for i in $(count $n); do
- make_text $i initial initial >$i
+ make_text $i initial initial >$i || return 1
done &&
git add . &&
git commit -m add=$n &&
for i in $(count $n); do
- make_text $i changed initial >$i
+ make_text $i changed initial >$i || return 1
done &&
git commit -a -m change=$n &&
git checkout -b test$n HEAD^ &&
for i in $(count $n); do
- git rm $i
- make_text $i initial changed >$i.moved
+ git rm $i &&
+ make_text $i initial changed >$i.moved || return 1
done &&
git add . &&
git commit -m change+rename=$n &&
@@ -79,7 +79,7 @@ test_expect_success 'setup large simple rename' '
git reset --hard initial &&
for i in $(count 200); do
- make_text foo bar baz >$i
+ make_text foo bar baz >$i || return 1
done &&
git add . &&
git commit -m create-files &&
diff --git a/t/t6413-merge-crlf.sh b/t/t6413-merge-crlf.sh
index affea25..647ea1e 100755
--- a/t/t6413-merge-crlf.sh
+++ b/t/t6413-merge-crlf.sh
@@ -11,6 +11,7 @@ test_description='merge conflict in crlf repo
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -33,14 +34,14 @@ test_expect_success setup '
test_expect_success 'Check "ours" is CRLF' '
git reset --hard initial &&
git merge side -s ours &&
- cat file | remove_cr | append_cr >file.temp &&
+ remove_cr <file | append_cr >file.temp &&
test_cmp file file.temp
'
test_expect_success 'Check that conflict file is CRLF' '
git reset --hard a &&
test_must_fail git merge side &&
- cat file | remove_cr | append_cr >file.temp &&
+ remove_cr <file | append_cr >file.temp &&
test_cmp file file.temp
'
diff --git a/t/t6414-merge-rename-nocruft.sh b/t/t6414-merge-rename-nocruft.sh
index d7e3c1f..69fc1c9 100755
--- a/t/t6414-merge-rename-nocruft.sh
+++ b/t/t6414-merge-rename-nocruft.sh
@@ -4,6 +4,7 @@ test_description='Merge-recursive merging renames'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t6415-merge-dir-to-symlink.sh b/t/t6415-merge-dir-to-symlink.sh
index 2ce104a..ae00492 100755
--- a/t/t6415-merge-dir-to-symlink.sh
+++ b/t/t6415-merge-dir-to-symlink.sh
@@ -4,6 +4,7 @@ test_description='merging when a directory was replaced with a symlink'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'create a commit where dir a/b changed to symlink' '
@@ -25,7 +26,8 @@ test_expect_success 'checkout does not clobber untracked symlink' '
git reset --hard main &&
git rm --cached a/b &&
git commit -m "untracked symlink remains" &&
- test_must_fail git checkout start^0
+ test_must_fail git checkout start^0 &&
+ git clean -fd # Do not leave the untracked symlink in the way
'
test_expect_success 'a/b-2/c/d is kept when clobbering symlink b' '
@@ -34,7 +36,8 @@ test_expect_success 'a/b-2/c/d is kept when clobbering symlink b' '
git rm --cached a/b &&
git commit -m "untracked symlink remains" &&
git checkout -f start^0 &&
- test_path_is_file a/b-2/c/d
+ test_path_is_file a/b-2/c/d &&
+ git clean -fd # Do not leave the untracked symlink in the way
'
test_expect_success 'checkout should not have deleted a/b-2/c/d' '
diff --git a/t/t6416-recursive-corner-cases.sh b/t/t6416-recursive-corner-cases.sh
index 84f5082..5f414ab 100755
--- a/t/t6416-recursive-corner-cases.sh
+++ b/t/t6416-recursive-corner-cases.sh
@@ -5,6 +5,7 @@ test_description='recursive merge corner cases involving criss-cross merges'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-merge.sh
@@ -19,19 +20,13 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
#
test_expect_success 'setup basic criss-cross + rename with no modifications' '
- test_create_repo basic-rename &&
+ git init basic-rename &&
(
cd basic-rename &&
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 &&
+ printf "line %d in a sample file\n" $ten >one &&
+ printf "line %d in another sample file\n" $ten >two &&
git add one two &&
test_tick && git commit -m initial &&
@@ -91,19 +86,13 @@ test_expect_success 'merge simple rename+criss-cross with no modifications' '
#
test_expect_success 'setup criss-cross + rename merges with basic modification' '
- test_create_repo rename-modify &&
+ git init rename-modify &&
(
cd rename-modify &&
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 &&
+ printf "line %d in a sample file\n" $ten >one &&
+ printf "line %d in another sample file\n" $ten >two &&
git add one two &&
test_tick && git commit -m initial &&
@@ -172,7 +161,7 @@ test_expect_success 'merge criss-cross + rename merges with basic modification'
#
test_expect_success 'setup differently handled merges of rename/add conflict' '
- test_create_repo rename-add &&
+ git init rename-add &&
(
cd rename-add &&
@@ -336,7 +325,7 @@ test_expect_success 'git detects differently handled merges conflict, swapped' '
# Merging commits D & E should result in modify/delete conflict.
test_expect_success 'setup criss-cross + modify/delete resolved differently' '
- test_create_repo modify-delete &&
+ git init modify-delete &&
(
cd modify-delete &&
@@ -511,7 +500,7 @@ test_expect_success 'git detects conflict merging criss-cross+modify/delete, rev
#
test_expect_success 'setup differently handled merges of directory/file conflict' '
- test_create_repo directory-file &&
+ git init directory-file &&
(
cd directory-file &&
@@ -879,7 +868,7 @@ test_expect_failure 'merge of D2 & E4 merges a2s & reports conflict for a/file'
# but that may cancel out at the final merge stage".
test_expect_success 'setup rename/rename(1to2)/modify followed by what looks like rename/rename(2to1)/modify' '
- test_create_repo rename-squared-squared &&
+ git init rename-squared-squared &&
(
cd rename-squared-squared &&
@@ -956,7 +945,7 @@ test_expect_success 'handle rename/rename(1to2)/modify followed by what looks li
# content merge handled.
test_expect_success 'setup criss-cross + rename/rename/add-source + modify/modify' '
- test_create_repo rename-rename-add-source &&
+ git init rename-rename-add-source &&
(
cd rename-rename-add-source &&
@@ -1044,7 +1033,7 @@ test_expect_failure 'detect rename/rename/add-source for virtual merge-base' '
# base of B & C needs to not delete B:c for that to work, though...
test_expect_success 'setup criss-cross+rename/rename/add-dest + simple modify' '
- test_create_repo rename-rename-add-dest &&
+ git init rename-rename-add-dest &&
(
cd rename-rename-add-dest &&
@@ -1123,7 +1112,7 @@ test_expect_success 'virtual merge base handles rename/rename(1to2)/add-dest' '
# git detect it?
test_expect_success 'setup symlink modify/modify' '
- test_create_repo symlink-modify-modify &&
+ git init symlink-modify-modify &&
(
cd symlink-modify-modify &&
@@ -1190,7 +1179,7 @@ test_expect_merge_algorithm failure success 'check symlink modify/modify' '
# git detect it?
test_expect_success 'setup symlink add/add' '
- test_create_repo symlink-add-add &&
+ git init symlink-add-add &&
(
cd symlink-add-add &&
@@ -1256,11 +1245,11 @@ test_expect_merge_algorithm failure success 'check symlink add/add' '
# git detect it?
test_expect_success 'setup submodule modify/modify' '
- test_create_repo submodule-modify-modify &&
+ git init submodule-modify-modify &&
(
cd submodule-modify-modify &&
- test_create_repo submod &&
+ git init submod &&
(
cd submod &&
touch file-A &&
@@ -1344,11 +1333,11 @@ test_expect_merge_algorithm failure success 'check submodule modify/modify' '
# git detect it?
test_expect_success 'setup submodule add/add' '
- test_create_repo submodule-add-add &&
+ git init submodule-add-add &&
(
cd submodule-add-add &&
- test_create_repo submod &&
+ git init submod &&
(
cd submod &&
touch file-A &&
@@ -1431,11 +1420,11 @@ test_expect_merge_algorithm failure success 'check submodule add/add' '
# This is an obvious add/add conflict for 'path'. Can git detect it?
test_expect_success 'setup conflicting entry types (submodule vs symlink)' '
- test_create_repo submodule-symlink-add-add &&
+ git init submodule-symlink-add-add &&
(
cd submodule-symlink-add-add &&
- test_create_repo path &&
+ git init path &&
(
cd path &&
touch file-B &&
@@ -1506,7 +1495,7 @@ test_expect_merge_algorithm failure success 'check conflicting entry types (subm
# This is an obvious add/add mode conflict. Can git detect it?
test_expect_success 'setup conflicting modes for regular file' '
- test_create_repo regular-file-mode-conflict &&
+ git init regular-file-mode-conflict &&
(
cd regular-file-mode-conflict &&
@@ -1583,15 +1572,12 @@ test_expect_failure 'check conflicting modes for regular file' '
# to ensure that we handle it as well as practical.
test_expect_success 'setup nested conflicts' '
- test_create_repo nested_conflicts &&
+ git init nested_conflicts &&
(
cd nested_conflicts &&
# Create some related files now
- for i in $(test_seq 1 10)
- do
- echo Random base content line $i
- done >initial &&
+ printf "Random base content line %d\n" $(test_seq 1 10) >initial &&
cp initial b_L1 &&
cp initial b_R1 &&
@@ -1772,15 +1758,12 @@ test_expect_success 'check nested conflicts' '
# have three levels of conflict markers. Can we distinguish all three?
test_expect_success 'setup virtual merge base with nested conflicts' '
- test_create_repo virtual_merge_base_has_nested_conflicts &&
+ git init virtual_merge_base_has_nested_conflicts &&
(
cd virtual_merge_base_has_nested_conflicts &&
# Create some related files now
- for i in $(test_seq 1 10)
- do
- echo Random base content line $i
- done >content &&
+ printf "Random base content line %d\n" $(test_seq 1 10) >content &&
# Setup original commit
git add content &&
diff --git a/t/t6417-merge-ours-theirs.sh b/t/t6417-merge-ours-theirs.sh
index ec065d6..482b73a 100755
--- a/t/t6417-merge-ours-theirs.sh
+++ b/t/t6417-merge-ours-theirs.sh
@@ -4,13 +4,11 @@ test_description='Merge-recursive ours and theirs variants'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
- for i in 1 2 3 4 5 6 7 8 9
- do
- echo "$i"
- done >file &&
+ test_write_lines 1 2 3 4 5 6 7 8 9 >file &&
git add file &&
cp file elif &&
git commit -m initial &&
diff --git a/t/t6418-merge-text-auto.sh b/t/t6418-merge-text-auto.sh
index 1e0296d..48a62cb 100755
--- a/t/t6418-merge-text-auto.sh
+++ b/t/t6418-merge-text-auto.sh
@@ -15,6 +15,7 @@ test_description='CRLF merge conflict across text=auto change
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_have_prereq SED_STRIPS_CR && SED_OPTIONS=-b
@@ -204,4 +205,30 @@ test_expect_success 'Test delete/normalize conflict' '
test_path_is_missing file
'
+test_expect_success 'rename/delete vs. renormalization' '
+ git init subrepo &&
+ (
+ cd subrepo &&
+ echo foo >oldfile &&
+ git add oldfile &&
+ git commit -m original &&
+
+ git branch rename &&
+ git branch nuke &&
+
+ git checkout rename &&
+ git mv oldfile newfile &&
+ git commit -m renamed &&
+
+ git checkout nuke &&
+ git rm oldfile &&
+ git commit -m deleted &&
+
+ git checkout rename^0 &&
+ test_must_fail git -c merge.renormalize=true merge nuke >out &&
+
+ grep "rename/delete" out
+ )
+'
+
test_done
diff --git a/t/t6421-merge-partial-clone.sh b/t/t6421-merge-partial-clone.sh
index 36bcd7c3..711b709 100755
--- a/t/t6421-merge-partial-clone.sh
+++ b/t/t6421-merge-partial-clone.sh
@@ -31,7 +31,7 @@ test_description="limiting blob downloads when merging with partial clones"
test_setup_repo () {
test -d server && return
- test_create_repo server &&
+ git init server &&
(
cd server &&
@@ -155,7 +155,7 @@ test_setup_repo () {
# Commit A:
# (Rename leap->jump, rename basename/ -> basename/subdir/, rename dir/
# -> folder/, move e into newsubdir, add newfile.rs, remove f, modify
-# both both Makefiles and jumps)
+# both Makefiles and jumps)
# general/{jump1_A, jump2_A}
# basename/subdir/{numbers_A, sequence_A, values_A}
# folder/subdir/{a,b,c,d,Makefile_TOP_A}
@@ -343,7 +343,7 @@ test_expect_merge_algorithm failure success 'Objects downloaded when a directory
# Commit A:
# (Rename leap->jump, rename basename/ -> basename/subdir/, rename dir/
# -> folder/, move e into newsubdir, add newfile.rs, remove f, modify
-# both both Makefiles and jumps)
+# both Makefiles and jumps)
# general/{jump1_A, jump2_A}
# basename/subdir/{numbers_A, sequence_A, values_A}
# folder/subdir/{a,b,c,d,Makefile_TOP_A}
diff --git a/t/t6422-merge-rename-corner-cases.sh b/t/t6422-merge-rename-corner-cases.sh
index bf4ce3c..80d7b5e 100755
--- a/t/t6422-merge-rename-corner-cases.sh
+++ b/t/t6422-merge-rename-corner-cases.sh
@@ -6,11 +6,12 @@ test_description="recursive merge corner cases w/ renames but not criss-crosses"
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-merge.sh
test_setup_rename_delete_untracked () {
- test_create_repo rename-delete-untracked &&
+ git init rename-delete-untracked &&
(
cd rename-delete-untracked &&
@@ -55,7 +56,7 @@ test_expect_success "Does git preserve Gollum's precious artifact?" '
# We should be able to merge B & C cleanly
test_setup_rename_modify_add_source () {
- test_create_repo rename-modify-add-source &&
+ git init rename-modify-add-source &&
(
cd rename-modify-add-source &&
@@ -95,7 +96,7 @@ test_expect_failure 'rename/modify/add-source conflict resolvable' '
'
test_setup_break_detection_1 () {
- test_create_repo break-detection-1 &&
+ git init break-detection-1 &&
(
cd break-detection-1 &&
@@ -143,7 +144,7 @@ test_expect_failure 'conflict caused if rename not detected' '
'
test_setup_break_detection_2 () {
- test_create_repo break-detection-2 &&
+ git init break-detection-2 &&
(
cd break-detection-2 &&
@@ -191,7 +192,7 @@ test_expect_failure 'missed conflict if rename not detected' '
# Commit C: rename a->b, add unrelated a
test_setup_break_detection_3 () {
- test_create_repo break-detection-3 &&
+ git init break-detection-3 &&
(
cd break-detection-3 &&
@@ -267,7 +268,7 @@ test_expect_failure 'detect rename/add-source and preserve all data, merge other
'
test_setup_rename_directory () {
- test_create_repo rename-directory-$1 &&
+ git init rename-directory-$1 &&
(
cd rename-directory-$1 &&
@@ -385,7 +386,7 @@ test_expect_success 'rename/directory conflict + content merge conflict' '
'
test_setup_rename_directory_2 () {
- test_create_repo rename-directory-2 &&
+ git init rename-directory-2 &&
(
cd rename-directory-2 &&
@@ -444,7 +445,7 @@ test_expect_success 'disappearing dir in rename/directory conflict handled' '
# Commit B: modify a, add different b
test_setup_rename_with_content_merge_and_add () {
- test_create_repo rename-with-content-merge-and-add-$1 &&
+ git init rename-with-content-merge-and-add-$1 &&
(
cd rename-with-content-merge-and-add-$1 &&
@@ -475,7 +476,7 @@ test_expect_success 'handle rename-with-content-merge vs. add' '
git checkout A^0 &&
test_must_fail git merge -s recursive B^0 >out &&
- test_i18ngrep "CONFLICT (.*/add)" out &&
+ test_grep "CONFLICT (.*/add)" out &&
git ls-files -s >out &&
test_line_count = 2 out &&
@@ -521,7 +522,7 @@ test_expect_success 'handle rename-with-content-merge vs. add, merge other way'
git checkout B^0 &&
test_must_fail git merge -s recursive A^0 >out &&
- test_i18ngrep "CONFLICT (.*/add)" out &&
+ test_grep "CONFLICT (.*/add)" out &&
git ls-files -s >out &&
test_line_count = 2 out &&
@@ -569,7 +570,7 @@ test_expect_success 'handle rename-with-content-merge vs. add, merge other way'
# * Nothing else should be present. Is anything?
test_setup_rename_rename_2to1 () {
- test_create_repo rename-rename-2to1 &&
+ git init rename-rename-2to1 &&
(
cd rename-rename-2to1 &&
@@ -601,7 +602,7 @@ 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 (\(.*\)/\1)" out &&
+ test_grep "CONFLICT (\(.*\)/\1)" out &&
git ls-files -s >out &&
test_line_count = 2 out &&
@@ -641,7 +642,7 @@ test_expect_success 'handle rename/rename (2to1) conflict correctly' '
# Commit B: rename a->b
# Commit C: rename a->c
test_setup_rename_rename_1to2 () {
- test_create_repo rename-rename-1to2 &&
+ git init rename-rename-1to2 &&
(
cd rename-rename-1to2 &&
@@ -699,7 +700,7 @@ test_expect_success 'merge has correct working tree contents' '
# Merging of B & C should NOT be clean; there's a rename/rename conflict
test_setup_rename_rename_1to2_add_source_1 () {
- test_create_repo rename-rename-1to2-add-source-1 &&
+ git init rename-rename-1to2-add-source-1 &&
(
cd rename-rename-1to2-add-source-1 &&
@@ -747,7 +748,7 @@ test_expect_failure 'detect conflict with rename/rename(1to2)/add-source merge'
'
test_setup_rename_rename_1to2_add_source_2 () {
- test_create_repo rename-rename-1to2-add-source-2 &&
+ git init rename-rename-1to2-add-source-2 &&
(
cd rename-rename-1to2-add-source-2 &&
@@ -793,7 +794,7 @@ test_expect_failure 'rename/rename/add-source still tracks new a file' '
'
test_setup_rename_rename_1to2_add_dest () {
- test_create_repo rename-rename-1to2-add-dest &&
+ git init rename-rename-1to2-add-dest &&
(
cd rename-rename-1to2-add-dest &&
@@ -873,7 +874,7 @@ test_expect_success 'rename/rename/add-dest merge still knows about conflicting
# Expected: CONFLICT (rename/add/delete), two-way merged bar
test_setup_rad () {
- test_create_repo rad &&
+ git init rad &&
(
cd rad &&
echo "original file" >foo &&
@@ -913,8 +914,8 @@ test_expect_merge_algorithm failure success 'rad-check: rename/add/delete confli
# be flexible in the type of console output message(s) reported
# for this particular case; we will be more stringent about the
# contents of the index and working directory.
- test_i18ngrep "CONFLICT (.*/add)" out &&
- test_i18ngrep "CONFLICT (rename.*/delete)" out &&
+ test_grep "CONFLICT (.*/add)" out &&
+ test_grep "CONFLICT (rename.*/delete)" out &&
test_must_be_empty err &&
git ls-files -s >file_count &&
@@ -945,7 +946,7 @@ test_expect_merge_algorithm failure success 'rad-check: rename/add/delete confli
# Expected: CONFLICT (rename/rename/delete/delete), two-way merged baz
test_setup_rrdd () {
- test_create_repo rrdd &&
+ git init rrdd &&
(
cd rrdd &&
echo foo >foo &&
@@ -987,8 +988,8 @@ test_expect_merge_algorithm failure success 'rrdd-check: rename/rename(2to1)/del
# be flexible in the type of console output message(s) reported
# for this particular case; we will be more stringent about the
# contents of the index and working directory.
- test_i18ngrep "CONFLICT (\(.*\)/\1)" out &&
- test_i18ngrep "CONFLICT (rename.*delete)" out &&
+ test_grep "CONFLICT (\(.*\)/\1)" out &&
+ test_grep "CONFLICT (rename.*delete)" out &&
test_must_be_empty err &&
git ls-files -s >file_count &&
@@ -1021,7 +1022,7 @@ test_expect_merge_algorithm failure success 'rrdd-check: rename/rename(2to1)/del
# multi-way merged contents found in two, four, six
test_setup_mod6 () {
- test_create_repo mod6 &&
+ git init mod6 &&
(
cd mod6 &&
test_seq 11 19 >one &&
@@ -1067,7 +1068,7 @@ test_expect_merge_algorithm failure success 'mod6-check: chains of rename/rename
test_must_fail git merge -s recursive B^0 >out 2>err &&
- test_i18ngrep "CONFLICT (rename/rename)" out &&
+ test_grep "CONFLICT (rename/rename)" out &&
test_must_be_empty err &&
git ls-files -s >file_count &&
@@ -1158,8 +1159,7 @@ test_conflicts_with_adds_and_renames() {
# 4) There should not be any three~* files in the working
# tree
test_setup_collision_conflict () {
- #test_expect_success "setup simple $sideL/$sideR conflict" '
- test_create_repo simple_${sideL}_${sideR} &&
+ git init simple_${sideL}_${sideR} &&
(
cd simple_${sideL}_${sideR} &&
@@ -1235,7 +1235,6 @@ test_conflicts_with_adds_and_renames() {
fi &&
test_tick && git commit -m R
)
- #'
}
test_expect_success "check simple $sideL/$sideR conflict" '
@@ -1307,7 +1306,7 @@ test_conflicts_with_adds_and_renames add add
# So, we have four different conflicting files that all end up at path
# 'three'.
test_setup_nested_conflicts_from_rename_rename () {
- test_create_repo nested_conflicts_from_rename_rename &&
+ git init nested_conflicts_from_rename_rename &&
(
cd nested_conflicts_from_rename_rename &&
@@ -1416,7 +1415,7 @@ test_expect_success 'check nested conflicts from rename/rename(2to1)' '
# Expected: CONFLICT(rename/rename) message, three unstaged entries in the
# index, and contents of orig-[AB] at path orig-[AB]
test_setup_rename_rename_1_to_2_binary () {
- test_create_repo rename_rename_1_to_2_binary &&
+ git init rename_rename_1_to_2_binary &&
(
cd rename_rename_1_to_2_binary &&
diff --git a/t/t6423-merge-rename-directories.sh b/t/t6423-merge-rename-directories.sh
index 5b81a13..88d1cf2 100755
--- a/t/t6423-merge-rename-directories.sh
+++ b/t/t6423-merge-rename-directories.sh
@@ -40,7 +40,7 @@ test_description="recursive merge with directory renames"
# Expected: y/{b,c,d,e/f}
test_setup_1a () {
- test_create_repo 1a &&
+ git init 1a &&
(
cd 1a &&
@@ -106,7 +106,7 @@ test_expect_success '1a: Simple directory rename detection' '
# Expected: y/{b,c,d,e}
test_setup_1b () {
- test_create_repo 1b &&
+ git init 1b &&
(
cd 1b &&
@@ -169,7 +169,7 @@ test_expect_success '1b: Merge a directory with another' '
# Expected: y/{b,c,d} (because x/d -> z/d -> y/d)
test_setup_1c () {
- test_create_repo 1c &&
+ git init 1c &&
(
cd 1c &&
@@ -232,7 +232,7 @@ test_expect_success '1c: Transitive renaming' '
# y/wham_1 & z/wham_2 should too...giving us a conflict.
test_setup_1d () {
- test_create_repo 1d &&
+ git init 1d &&
(
cd 1d &&
@@ -276,7 +276,7 @@ test_expect_success '1d: Directory renames cause a rename/rename(2to1) conflict'
git checkout A^0 &&
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
- test_i18ngrep "CONFLICT (\(.*\)/\1)" out &&
+ test_grep "CONFLICT (\(.*\)/\1)" out &&
git ls-files -s >out &&
test_line_count = 8 out &&
@@ -328,7 +328,7 @@ test_expect_success '1d: Directory renames cause a rename/rename(2to1) conflict'
# Expected: y/{newb,newc,d}
test_setup_1e () {
- test_create_repo 1e &&
+ git init 1e &&
(
cd 1e &&
@@ -387,7 +387,7 @@ test_expect_success '1e: Renamed directory, with all files being renamed too' '
# Expected: y/{b,c}, x/{d,e,f,g}
test_setup_1f () {
- test_create_repo 1f &&
+ git init 1f &&
(
cd 1f &&
@@ -476,7 +476,7 @@ test_expect_success '1f: Split a directory into two other directories' '
# Commit B: z/{b,c,d}
# Expected: y/b, w/c, z/d, with warning about z/ -> (y/ vs. w/) conflict
test_setup_2a () {
- test_create_repo 2a &&
+ git init 2a &&
(
cd 2a &&
@@ -515,7 +515,7 @@ test_expect_success '2a: Directory split into two on one side, with equal number
git checkout A^0 &&
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
- test_i18ngrep "CONFLICT.*directory rename split" out &&
+ test_grep "CONFLICT.*directory rename split" out &&
git ls-files -s >out &&
test_line_count = 3 out &&
@@ -538,7 +538,7 @@ test_expect_success '2a: Directory split into two on one side, with equal number
# Commit B: z/{b,c}, x/d
# Expected: y/b, w/c, x/d; No warning about z/ -> (y/ vs. w/) conflict
test_setup_2b () {
- test_create_repo 2b &&
+ git init 2b &&
(
cd 2b &&
@@ -591,7 +591,7 @@ test_expect_success '2b: Directory split into two on one side, with equal number
git rev-parse >expect \
O:z/b O:z/c B:x/d &&
test_cmp expect actual &&
- test_i18ngrep ! "CONFLICT.*directory rename split" out
+ test_grep ! "CONFLICT.*directory rename split" out
)
'
@@ -620,7 +620,7 @@ test_expect_success '2b: Directory split into two on one side, with equal number
# Commit B: y/{b,c}, x/d
# Expected: y/{b,c}, x/d
test_setup_3a () {
- test_create_repo 3a &&
+ git init 3a &&
(
cd 3a &&
@@ -684,7 +684,7 @@ test_expect_success '3a: Avoid implicit rename if involved as source on other si
# end up with CONFLICT:(z/d -> y/d vs. x/d vs. w/d), i.e. a
# rename/rename/rename(1to3) conflict, which is just weird.
test_setup_3b () {
- test_create_repo 3b &&
+ git init 3b &&
(
cd 3b &&
@@ -726,8 +726,8 @@ test_expect_success '3b: Avoid implicit rename if involved as source on current
git checkout A^0 &&
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
- test_i18ngrep CONFLICT.*rename/rename.*z/d.*x/d.*w/d out &&
- test_i18ngrep ! CONFLICT.*rename/rename.*y/d out &&
+ test_grep CONFLICT.*rename/rename.*z/d.*x/d.*w/d out &&
+ test_grep ! CONFLICT.*rename/rename.*y/d out &&
git ls-files -s >out &&
test_line_count = 5 out &&
@@ -807,7 +807,7 @@ test_expect_success '3b: Avoid implicit rename if involved as source on current
# NOTE: Even though most files from z moved to y, we don't want f to follow.
test_setup_4a () {
- test_create_repo 4a &&
+ git init 4a &&
(
cd 4a &&
@@ -896,7 +896,7 @@ test_expect_success '4a: Directory split, with original directory still present'
# index.
test_setup_5a () {
- test_create_repo 5a &&
+ git init 5a &&
(
cd 5a &&
@@ -938,7 +938,7 @@ test_expect_success '5a: Merge directories, other side adds files to original an
git checkout A^0 &&
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
- test_i18ngrep "CONFLICT.*implicit dir rename" out &&
+ test_grep "CONFLICT.*implicit dir rename" out &&
git ls-files -s >out &&
test_line_count = 6 out &&
@@ -971,7 +971,7 @@ test_expect_success '5a: Merge directories, other side adds files to original an
# back to git behavior without the directory rename detection.
test_setup_5b () {
- test_create_repo 5b &&
+ git init 5b &&
(
cd 5b &&
@@ -1013,7 +1013,7 @@ test_expect_success '5b: Rename/delete in order to get add/add/add conflict' '
git checkout A^0 &&
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
- test_i18ngrep "CONFLICT (add/add).* y/d" out &&
+ test_grep "CONFLICT (add/add).* y/d" out &&
git ls-files -s >out &&
test_line_count = 5 out &&
@@ -1048,7 +1048,7 @@ test_expect_success '5b: Rename/delete in order to get add/add/add conflict' '
# though, because it doesn't have anything in the way.
test_setup_5c () {
- test_create_repo 5c &&
+ git init 5c &&
(
cd 5c &&
@@ -1094,8 +1094,8 @@ test_expect_success '5c: Transitive rename would cause rename/rename/rename/add/
git checkout A^0 &&
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
- test_i18ngrep "CONFLICT (rename/rename).*x/d.*w/d.*z/d" out &&
- test_i18ngrep "CONFLICT (add/add).* y/d" out &&
+ test_grep "CONFLICT (rename/rename).*x/d.*w/d.*z/d" out &&
+ test_grep "CONFLICT (add/add).* y/d" out &&
git ls-files -s >out &&
test_line_count = 9 out &&
@@ -1138,7 +1138,7 @@ test_expect_success '5c: Transitive rename would cause rename/rename/rename/add/
# directory rename detection for z/f -> y/f.
test_setup_5d () {
- test_create_repo 5d &&
+ git init 5d &&
(
cd 5d &&
@@ -1179,7 +1179,7 @@ test_expect_success '5d: Directory/file/file conflict due to directory rename' '
git checkout A^0 &&
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
- test_i18ngrep "CONFLICT (file/directory).*y/d" out &&
+ test_grep "CONFLICT (file/directory).*y/d" out &&
git ls-files -s >out &&
test_line_count = 6 out &&
@@ -1239,7 +1239,7 @@ test_expect_success '5d: Directory/file/file conflict due to directory rename' '
# it is also involved in a rename/delete conflict.
test_setup_6a () {
- test_create_repo 6a &&
+ git init 6a &&
(
cd 6a &&
@@ -1278,7 +1278,7 @@ test_expect_success '6a: Tricky rename/delete' '
git checkout A^0 &&
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
- test_i18ngrep "CONFLICT (rename/delete).*z/c.*y/c" out &&
+ test_grep "CONFLICT (rename/delete).*z/c.*y/c" out &&
if test "$GIT_TEST_MERGE_ALGORITHM" = ort
then
@@ -1337,7 +1337,7 @@ test_expect_success '6a: Tricky rename/delete' '
# the behavior on testcases 6b2 and 8e, and introduced this 6b1 testcase.
test_setup_6b1 () {
- test_create_repo 6b1 &&
+ git init 6b1 &&
(
cd 6b1 &&
@@ -1415,7 +1415,7 @@ test_expect_merge_algorithm failure success '6b1: Same renames done on both side
# the z/ -> y/ rename.
test_setup_6b2 () {
- test_create_repo 6b2 &&
+ git init 6b2 &&
(
cd 6b2 &&
@@ -1479,7 +1479,7 @@ test_expect_merge_algorithm failure success '6b2: Same rename done on both sides
# "accidentally detect a rename" and give us y/{b,c,d}.
test_setup_6c () {
- test_create_repo 6c &&
+ git init 6c &&
(
cd 6c &&
@@ -1542,7 +1542,7 @@ test_expect_success '6c: Rename only done on same side' '
# doesn't "accidentally detect a rename" and give us y/{b,c,d}.
test_setup_6d () {
- test_create_repo 6d &&
+ git init 6d &&
(
cd 6d &&
@@ -1605,7 +1605,7 @@ test_expect_success '6d: We do not always want transitive renaming' '
# add/add conflict on y/d_1 vs y/d_2.
test_setup_6e () {
- test_create_repo 6e &&
+ git init 6e &&
(
cd 6e &&
@@ -1700,7 +1700,7 @@ test_expect_success '6e: Add/add from one side' '
# NOTE: There's a rename of z/ here, y/ has more renames, so z/d -> y/d.
test_setup_7a () {
- test_create_repo 7a &&
+ git init 7a &&
(
cd 7a &&
@@ -1740,8 +1740,8 @@ test_expect_success '7a: rename-dir vs. rename-dir (NOT split evenly) PLUS add-o
git checkout A^0 &&
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
- test_i18ngrep "CONFLICT (rename/rename).*z/b.*y/b.*w/b" out &&
- test_i18ngrep "CONFLICT (rename/rename).*z/c.*y/c.*x/c" out &&
+ test_grep "CONFLICT (rename/rename).*z/b.*y/b.*w/b" out &&
+ test_grep "CONFLICT (rename/rename).*z/c.*y/c.*x/c" out &&
git ls-files -s >out &&
test_line_count = 7 out &&
@@ -1772,7 +1772,7 @@ test_expect_success '7a: rename-dir vs. rename-dir (NOT split evenly) PLUS add-o
# Expected: y/{b,c}, CONFLICT(rename/rename(2to1): x/d_1, w/d_2 -> y_d)
test_setup_7b () {
- test_create_repo 7b &&
+ git init 7b &&
(
cd 7b &&
@@ -1813,7 +1813,7 @@ test_expect_success '7b: rename/rename(2to1), but only due to transitive rename'
git checkout A^0 &&
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
- test_i18ngrep "CONFLICT (\(.*\)/\1)" out &&
+ test_grep "CONFLICT (\(.*\)/\1)" out &&
git ls-files -s >out &&
test_line_count = 4 out &&
@@ -1861,7 +1861,7 @@ test_expect_success '7b: rename/rename(2to1), but only due to transitive rename'
# nor CONFLiCT x/d -> w/d vs. y/d vs. z/d)
test_setup_7c () {
- test_create_repo 7c &&
+ git init 7c &&
(
cd 7c &&
@@ -1900,7 +1900,7 @@ test_expect_success '7c: rename/rename(1to...2or3); transitive rename may add co
git checkout A^0 &&
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
- test_i18ngrep "CONFLICT (rename/rename).*x/d.*w/d.*y/d" out &&
+ test_grep "CONFLICT (rename/rename).*x/d.*w/d.*y/d" out &&
git ls-files -s >out &&
test_line_count = 5 out &&
@@ -1926,7 +1926,7 @@ test_expect_success '7c: rename/rename(1to...2or3); transitive rename may add co
# NOTE: z->y so NOT CONFLICT(delete x/d vs rename to z/d)
test_setup_7d () {
- test_create_repo 7d &&
+ git init 7d &&
(
cd 7d &&
@@ -1965,7 +1965,7 @@ test_expect_success '7d: transitive rename involved in rename/delete; how is it
git checkout A^0 &&
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
- test_i18ngrep "CONFLICT (rename/delete).*x/d.*y/d" out &&
+ test_grep "CONFLICT (rename/delete).*x/d.*y/d" out &&
if test "$GIT_TEST_MERGE_ALGORITHM" = ort
then
@@ -2027,7 +2027,7 @@ test_expect_success '7d: transitive rename involved in rename/delete; how is it
# how it's resolved.
test_setup_7e () {
- test_create_repo 7e &&
+ git init 7e &&
(
cd 7e &&
@@ -2071,7 +2071,7 @@ test_expect_success '7e: transitive rename in rename/delete AND dirs in the way'
git checkout A^0 &&
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
- test_i18ngrep "CONFLICT (rename/delete).*x/d.*y/d" out &&
+ test_grep "CONFLICT (rename/delete).*x/d.*y/d" out &&
if test "$GIT_TEST_MERGE_ALGORITHM" = ort
then
@@ -2137,7 +2137,7 @@ test_expect_success '7e: transitive rename in rename/delete AND dirs in the way'
# we potentially could.
test_setup_8a () {
- test_create_repo 8a &&
+ git init 8a &&
(
cd 8a &&
@@ -2216,7 +2216,7 @@ test_expect_success '8a: Dual-directory rename, one into the others way' '
# e_1 and e_2.
test_setup_8b () {
- test_create_repo 8b &&
+ git init 8b &&
(
cd 8b &&
@@ -2290,7 +2290,7 @@ test_expect_success '8b: Dual-directory rename, one into the others way, with co
# notes in 8d.
test_setup_8c () {
- test_create_repo 8c &&
+ git init 8c &&
(
cd 8c &&
@@ -2330,7 +2330,7 @@ test_expect_success '8c: modify/delete or rename+modify/delete' '
git checkout A^0 &&
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
- test_i18ngrep "CONFLICT (modify/delete).* z/d" out &&
+ test_grep "CONFLICT (modify/delete).* z/d" out &&
git ls-files -s >out &&
test_line_count = 5 out &&
@@ -2370,7 +2370,7 @@ test_expect_success '8c: modify/delete or rename+modify/delete' '
# differently.
test_setup_8d () {
- test_create_repo 8d &&
+ git init 8d &&
(
cd 8d &&
@@ -2453,7 +2453,7 @@ test_expect_success '8d: rename/delete...or not?' '
# the behavior, and predict it without computing as many details.
test_setup_8e () {
- test_create_repo 8e &&
+ git init 8e &&
(
cd 8e &&
@@ -2491,8 +2491,8 @@ test_expect_success '8e: Both sides rename, one side adds to original directory'
git checkout A^0 &&
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
- test_i18ngrep CONFLICT.*rename/rename.*z/c.*y/c.*w/c out &&
- test_i18ngrep CONFLICT.*rename/rename.*z/b.*y/b.*w/b out &&
+ test_grep CONFLICT.*rename/rename.*z/c.*y/c.*w/c out &&
+ test_grep CONFLICT.*rename/rename.*z/b.*y/b.*w/b out &&
git ls-files -s >out &&
test_line_count = 7 out &&
@@ -2537,7 +2537,7 @@ test_expect_success '8e: Both sides rename, one side adds to original directory'
# of that could take the new file in commit B at z/i to x/w/i or x/i.
test_setup_9a () {
- test_create_repo 9a &&
+ git init 9a &&
(
cd 9a &&
@@ -2609,7 +2609,7 @@ test_expect_success '9a: Inner renamed directory within outer renamed directory'
# Expected: y/{b,c,d_merged}
test_setup_9b () {
- test_create_repo 9b &&
+ git init 9b &&
(
cd 9b &&
@@ -2697,7 +2697,7 @@ test_expect_success '9b: Transitive rename with content merge' '
# history for any implicit directory renames.
test_setup_9c () {
- test_create_repo 9c &&
+ git init 9c &&
(
cd 9c &&
@@ -2741,7 +2741,7 @@ test_expect_success '9c: Doubly transitive rename?' '
git checkout A^0 &&
git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
- test_i18ngrep "WARNING: Avoiding applying x -> z rename to x/f" out &&
+ test_grep "WARNING: Avoiding applying x -> z rename to x/f" out &&
git ls-files -s >out &&
test_line_count = 6 out &&
@@ -2786,7 +2786,7 @@ test_expect_success '9c: Doubly transitive rename?' '
# testcases and simplifies things for the user.
test_setup_9d () {
- test_create_repo 9d &&
+ git init 9d &&
(
cd 9d &&
@@ -2830,10 +2830,10 @@ test_expect_success '9d: N-way transitive rename?' '
git checkout A^0 &&
git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
- test_i18ngrep "WARNING: Avoiding applying z -> y rename to z/t" out &&
- test_i18ngrep "WARNING: Avoiding applying y -> x rename to y/a" out &&
- test_i18ngrep "WARNING: Avoiding applying x -> w rename to x/b" out &&
- test_i18ngrep "WARNING: Avoiding applying w -> v rename to w/c" out &&
+ test_grep "WARNING: Avoiding applying z -> y rename to z/t" out &&
+ test_grep "WARNING: Avoiding applying y -> x rename to y/a" out &&
+ test_grep "WARNING: Avoiding applying x -> w rename to x/b" out &&
+ test_grep "WARNING: Avoiding applying w -> v rename to w/c" out &&
git ls-files -s >out &&
test_line_count = 7 out &&
@@ -2861,7 +2861,7 @@ test_expect_success '9d: N-way transitive rename?' '
# dir1/yo, dir2/yo, dir3/yo, dirN/yo
test_setup_9e () {
- test_create_repo 9e &&
+ git init 9e &&
(
cd 9e &&
@@ -2954,7 +2954,7 @@ test_expect_success '9e: N-to-1 whammo' '
# Expected: priority/{a,b}/$more_files, priority/c
test_setup_9f () {
- test_create_repo 9f &&
+ git init 9f &&
(
cd 9f &&
@@ -3027,7 +3027,7 @@ test_expect_success '9f: Renamed directory that only contained immediate subdirs
# viewpoint...
test_setup_9g () {
- test_create_repo 9g &&
+ git init 9g &&
(
cd 9g &&
@@ -3096,7 +3096,7 @@ test_expect_failure '9g: Renamed directory that only contained immediate subdirs
# NOTE: If we applied the z/ -> y/ rename to z/d, then we'd end up with
# a rename/rename(1to2) conflict (z/d -> y/d vs. x/d)
test_setup_9h () {
- test_create_repo 9h &&
+ git init 9h &&
(
cd 9h &&
@@ -3177,7 +3177,7 @@ test_expect_success '9h: Avoid dir rename on merely modified path' '
# ERROR_MSG(untracked working tree files would be overwritten by merge)
test_setup_10a () {
- test_create_repo 10a &&
+ git init 10a &&
(
cd 10a &&
@@ -3215,7 +3215,7 @@ test_expect_success '10a: Overwrite untracked with normal rename/delete' '
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
test_path_is_missing .git/MERGE_HEAD &&
- test_i18ngrep "The following untracked working tree files would be overwritten by merge" err &&
+ test_grep "The following untracked working tree files would be overwritten by merge" err &&
git ls-files -s >out &&
test_line_count = 1 out &&
@@ -3243,7 +3243,7 @@ test_expect_success '10a: Overwrite untracked with normal rename/delete' '
# ERROR_MSG(refusing to lose untracked file at 'y/d')
test_setup_10b () {
- test_create_repo 10b &&
+ git init 10b &&
(
cd 10b &&
@@ -3287,7 +3287,7 @@ test_expect_success '10b: Overwrite untracked with dir rename + delete' '
if test "$GIT_TEST_MERGE_ALGORITHM" = ort
then
test_path_is_missing .git/MERGE_HEAD &&
- test_i18ngrep "error: The following untracked working tree files would be overwritten by merge" err &&
+ test_grep "error: The following untracked working tree files would be overwritten by merge" err &&
git ls-files -s >out &&
test_line_count = 1 out &&
@@ -3296,8 +3296,8 @@ test_expect_success '10b: Overwrite untracked with dir rename + delete' '
git ls-files -o >out &&
test_line_count = 5 out
else
- test_i18ngrep "CONFLICT (rename/delete).*Version B\^0 of y/d left in tree at y/d~B\^0" out &&
- test_i18ngrep "Error: Refusing to lose untracked file at y/e; writing to y/e~B\^0 instead" out &&
+ test_grep "CONFLICT (rename/delete).*Version B\^0 of y/d left in tree at y/d~B\^0" out &&
+ test_grep "Error: Refusing to lose untracked file at y/e; writing to y/e~B\^0 instead" out &&
git ls-files -s >out &&
test_line_count = 3 out &&
@@ -3334,7 +3334,7 @@ test_expect_success '10b: Overwrite untracked with dir rename + delete' '
# ERROR_MSG(Refusing to lose untracked file at y/c)
test_setup_10c () {
- test_create_repo 10c_$1 &&
+ git init 10c_$1 &&
(
cd 10c_$1 &&
@@ -3377,7 +3377,7 @@ test_expect_success '10c1: Overwrite untracked with dir rename/rename(1to2)' '
if test "$GIT_TEST_MERGE_ALGORITHM" = ort
then
test_path_is_missing .git/MERGE_HEAD &&
- test_i18ngrep "error: The following untracked working tree files would be overwritten by merge" err &&
+ test_grep "error: The following untracked working tree files would be overwritten by merge" err &&
git ls-files -s >out &&
test_line_count = 4 out &&
@@ -3386,8 +3386,8 @@ test_expect_success '10c1: Overwrite untracked with dir rename/rename(1to2)' '
git ls-files -o >out &&
test_line_count = 3 out
else
- test_i18ngrep "CONFLICT (rename/rename)" out &&
- test_i18ngrep "Refusing to lose untracked file at y/c; adding as y/c~B\^0 instead" out &&
+ test_grep "CONFLICT (rename/rename)" out &&
+ test_grep "Refusing to lose untracked file at y/c; adding as y/c~B\^0 instead" out &&
git ls-files -s >out &&
test_line_count = 6 out &&
@@ -3428,7 +3428,7 @@ test_expect_success '10c2: Overwrite untracked with dir rename/rename(1to2), oth
if test "$GIT_TEST_MERGE_ALGORITHM" = ort
then
test_path_is_missing .git/MERGE_HEAD &&
- test_i18ngrep "error: The following untracked working tree files would be overwritten by merge" err &&
+ test_grep "error: The following untracked working tree files would be overwritten by merge" err &&
git ls-files -s >out &&
test_line_count = 4 out &&
@@ -3437,8 +3437,8 @@ test_expect_success '10c2: Overwrite untracked with dir rename/rename(1to2), oth
git ls-files -o >out &&
test_line_count = 3 out
else
- test_i18ngrep "CONFLICT (rename/rename)" out &&
- test_i18ngrep "Refusing to lose untracked file at y/c; adding as y/c~HEAD instead" out &&
+ test_grep "CONFLICT (rename/rename)" out &&
+ test_grep "Refusing to lose untracked file at y/c; adding as y/c~HEAD instead" out &&
git ls-files -s >out &&
test_line_count = 6 out &&
@@ -3472,7 +3472,7 @@ test_expect_success '10c2: Overwrite untracked with dir rename/rename(1to2), oth
# ERROR_MSG(Refusing to lose untracked file at y/wham)
test_setup_10d () {
- test_create_repo 10d &&
+ git init 10d &&
(
cd 10d &&
@@ -3517,7 +3517,7 @@ test_expect_success '10d: Delete untracked with dir rename/rename(2to1)' '
if test "$GIT_TEST_MERGE_ALGORITHM" = ort
then
test_path_is_missing .git/MERGE_HEAD &&
- test_i18ngrep "error: The following untracked working tree files would be overwritten by merge" err &&
+ test_grep "error: The following untracked working tree files would be overwritten by merge" err &&
git ls-files -s >out &&
test_line_count = 6 out &&
@@ -3526,8 +3526,8 @@ test_expect_success '10d: Delete untracked with dir rename/rename(2to1)' '
git ls-files -o >out &&
test_line_count = 3 out
else
- test_i18ngrep "CONFLICT (rename/rename)" out &&
- test_i18ngrep "Refusing to lose untracked file at y/wham" out &&
+ test_grep "CONFLICT (rename/rename)" out &&
+ test_grep "Refusing to lose untracked file at y/wham" out &&
git ls-files -s >out &&
test_line_count = 6 out &&
@@ -3568,7 +3568,7 @@ test_expect_success '10d: Delete untracked with dir rename/rename(2to1)' '
# Expected: y/{a,b,c} + untracked z/c
test_setup_10e () {
- test_create_repo 10e &&
+ git init 10e &&
(
cd 10e &&
@@ -3606,7 +3606,7 @@ test_expect_merge_algorithm failure success '10e: Does git complain about untrac
echo random >z/c &&
git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
- test_i18ngrep ! "following untracked working tree files would be overwritten by merge" err &&
+ test_grep ! "following untracked working tree files would be overwritten by merge" err &&
git ls-files -s >out &&
test_line_count = 3 out &&
@@ -3650,7 +3650,7 @@ test_expect_merge_algorithm failure success '10e: Does git complain about untrac
# z/c with uncommitted mods on top of A:z/c_v1
test_setup_11a () {
- test_create_repo 11a &&
+ git init 11a &&
(
cd 11a &&
@@ -3690,9 +3690,9 @@ test_expect_success '11a: Avoid losing dirty contents with simple rename' '
if test "$GIT_TEST_MERGE_ALGORITHM" = ort
then
test_path_is_missing .git/MERGE_HEAD &&
- test_i18ngrep "error: Your local changes to the following files would be overwritten by merge" err
+ test_grep "error: Your local changes to the following files would be overwritten by merge" err
else
- test_i18ngrep "Refusing to lose dirty file at z/c" out &&
+ test_grep "Refusing to lose dirty file at z/c" out &&
git ls-files -s >out &&
test_line_count = 2 out &&
@@ -3728,7 +3728,7 @@ test_expect_success '11a: Avoid losing dirty contents with simple rename' '
test_setup_11b () {
- test_create_repo 11b &&
+ git init 11b &&
(
cd 11b &&
@@ -3770,10 +3770,10 @@ test_expect_success '11b: Avoid losing dirty file involved in directory rename'
then
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
test_path_is_missing .git/MERGE_HEAD &&
- test_i18ngrep "error: Your local changes to the following files would be overwritten by merge" err
+ test_grep "error: Your local changes to the following files would be overwritten by merge" err
else
git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
- test_i18ngrep "Refusing to lose dirty file at z/c" out &&
+ test_grep "Refusing to lose dirty file at z/c" out &&
git ls-files -s >out &&
test_line_count = 3 out &&
@@ -3810,7 +3810,7 @@ test_expect_success '11b: Avoid losing dirty file involved in directory rename'
# y/c left untouched (still has uncommitted mods)
test_setup_11c () {
- test_create_repo 11c &&
+ git init 11c &&
(
cd 11c &&
@@ -3853,9 +3853,9 @@ test_expect_success '11c: Avoid losing not-uptodate with rename + D/F conflict'
if test "$GIT_TEST_MERGE_ALGORITHM" = ort
then
test_path_is_missing .git/MERGE_HEAD &&
- test_i18ngrep "error: Your local changes to the following files would be overwritten by merge" err
+ test_grep "error: Your local changes to the following files would be overwritten by merge" err
else
- test_i18ngrep "following files would be overwritten by merge" err
+ test_grep "following files would be overwritten by merge" err
fi &&
grep -q stuff y/c &&
@@ -3883,7 +3883,7 @@ test_expect_success '11c: Avoid losing not-uptodate with rename + D/F conflict'
# y/{a,c~HEAD,c/d}, x/b, now-untracked z/c_v1 with uncommitted mods
test_setup_11d () {
- test_create_repo 11d &&
+ git init 11d &&
(
cd 11d &&
@@ -3927,9 +3927,9 @@ test_expect_success '11d: Avoid losing not-uptodate with rename + D/F conflict'
if test "$GIT_TEST_MERGE_ALGORITHM" = ort
then
test_path_is_missing .git/MERGE_HEAD &&
- test_i18ngrep "error: Your local changes to the following files would be overwritten by merge" err
+ test_grep "error: Your local changes to the following files would be overwritten by merge" err
else
- test_i18ngrep "Refusing to lose dirty file at z/c" out &&
+ test_grep "Refusing to lose dirty file at z/c" out &&
git ls-files -s >out &&
test_line_count = 4 out &&
@@ -3968,7 +3968,7 @@ test_expect_success '11d: Avoid losing not-uptodate with rename + D/F conflict'
# y/c has dirty file from before merge
test_setup_11e () {
- test_create_repo 11e &&
+ git init 11e &&
(
cd 11e &&
@@ -4013,10 +4013,10 @@ test_expect_success '11e: Avoid deleting not-uptodate with dir rename/rename(1to
if test "$GIT_TEST_MERGE_ALGORITHM" = ort
then
test_path_is_missing .git/MERGE_HEAD &&
- test_i18ngrep "error: Your local changes to the following files would be overwritten by merge" err
+ test_grep "error: Your local changes to the following files would be overwritten by merge" err
else
- test_i18ngrep "CONFLICT (rename/rename)" out &&
- test_i18ngrep "Refusing to lose dirty file at y/c" out &&
+ test_grep "CONFLICT (rename/rename)" out &&
+ test_grep "Refusing to lose dirty file at y/c" out &&
git ls-files -s >out &&
test_line_count = 7 out &&
@@ -4060,7 +4060,7 @@ test_expect_success '11e: Avoid deleting not-uptodate with dir rename/rename(1to
# ERROR_MSG(Refusing to lose dirty file at y/wham)
test_setup_11f () {
- test_create_repo 11f &&
+ git init 11f &&
(
cd 11f &&
@@ -4102,10 +4102,10 @@ test_expect_success '11f: Avoid deleting not-uptodate with dir rename/rename(2to
if test "$GIT_TEST_MERGE_ALGORITHM" = ort
then
test_path_is_missing .git/MERGE_HEAD &&
- test_i18ngrep "error: Your local changes to the following files would be overwritten by merge" err
+ test_grep "error: Your local changes to the following files would be overwritten by merge" err
else
- test_i18ngrep "CONFLICT (rename/rename)" out &&
- test_i18ngrep "Refusing to lose dirty file at y/wham" out &&
+ test_grep "CONFLICT (rename/rename)" out &&
+ test_grep "Refusing to lose dirty file at y/wham" out &&
git ls-files -s >out &&
test_line_count = 4 out &&
@@ -4155,7 +4155,7 @@ test_expect_success '11f: Avoid deleting not-uptodate with dir rename/rename(2to
# Expected: node1/{leaf1,leaf2,leaf5,node2/{leaf3,leaf4,leaf6}}
test_setup_12a () {
- test_create_repo 12a &&
+ git init 12a &&
(
cd 12a &&
@@ -4238,7 +4238,7 @@ test_expect_success '12a: Moving one directory hierarchy into another' '
# node2/node1/{leaf1, leaf2}
test_setup_12b1 () {
- test_create_repo 12b1 &&
+ git init 12b1 &&
(
cd 12b1 &&
@@ -4327,7 +4327,7 @@ test_expect_merge_algorithm failure success '12b1: Moving two directory hierarch
# even simple rules give weird results when given weird inputs.
test_setup_12b2 () {
- test_create_repo 12b2 &&
+ git init 12b2 &&
(
cd 12b2 &&
@@ -4402,7 +4402,7 @@ test_expect_success '12b2: Moving two directory hierarchies into each other' '
# each side of the merge.
test_setup_12c1 () {
- test_create_repo 12c1 &&
+ git init 12c1 &&
(
cd 12c1 &&
@@ -4421,14 +4421,14 @@ test_setup_12c1 () {
git checkout A &&
git mv node2/ node1/ &&
- for i in `git ls-files`; do echo side A >>$i; done &&
+ for i in $(git ls-files); do echo side A >>$i; done &&
git add -u &&
test_tick &&
git commit -m "A" &&
git checkout B &&
git mv node1/ node2/ &&
- for i in `git ls-files`; do echo side B >>$i; done &&
+ for i in $(git ls-files); do echo side B >>$i; done &&
git add -u &&
test_tick &&
git commit -m "B"
@@ -4492,7 +4492,7 @@ test_expect_merge_algorithm failure success '12c1: Moving one directory hierarch
# on each side of the merge.
test_setup_12c2 () {
- test_create_repo 12c2 &&
+ git init 12c2 &&
(
cd 12c2 &&
@@ -4511,7 +4511,7 @@ test_setup_12c2 () {
git checkout A &&
git mv node2/ node1/ &&
- for i in `git ls-files`; do echo side A >>$i; done &&
+ for i in $(git ls-files); do echo side A >>$i; done &&
git add -u &&
echo leaf5 >node1/leaf5 &&
git add node1/leaf5 &&
@@ -4520,7 +4520,7 @@ test_setup_12c2 () {
git checkout B &&
git mv node1/ node2/ &&
- for i in `git ls-files`; do echo side B >>$i; done &&
+ for i in $(git ls-files); do echo side B >>$i; done &&
git add -u &&
echo leaf6 >node2/leaf6 &&
git add node2/leaf6 &&
@@ -4584,7 +4584,7 @@ test_expect_success '12c2: Moving one directory hierarchy into another w/ conten
# Expected: subdir/foo, bar
test_setup_12d () {
- test_create_repo 12d &&
+ git init 12d &&
(
cd 12d &&
@@ -4642,7 +4642,7 @@ test_expect_success '12d: Rename/merge subdir into the root, variant 1' '
# Expected: foo, bar
test_setup_12e () {
- test_create_repo 12e &&
+ git init 12e &&
(
cd 12e &&
@@ -4743,7 +4743,7 @@ test_expect_success '12e: Rename/merge subdir into the root, variant 2' '
# pick and re-applying them in the subsequent one.
test_setup_12f () {
- test_create_repo 12f &&
+ git init 12f &&
(
cd 12f &&
@@ -4759,7 +4759,7 @@ test_setup_12f () {
echo g >dir/subdir/tweaked/g &&
echo h >dir/subdir/tweaked/h &&
test_seq 20 30 >dir/subdir/tweaked/Makefile &&
- for i in `test_seq 1 88`; do
+ for i in $(test_seq 1 88); do
echo content $i >dir/unchanged/file_$i
done &&
git add . &&
@@ -4902,7 +4902,7 @@ test_expect_merge_algorithm failure success '12f: Trivial directory resolve, cac
# Expected: newfile_{merged}, newdir/{a_B,b_B,c_A}
test_setup_12g () {
- test_create_repo 12g &&
+ git init 12g &&
(
cd 12g &&
@@ -4973,7 +4973,7 @@ test_expect_success '12g: Testcase with two kinds of "relevant" renames' '
# Expected: newdir/{alpha_2, b}
test_setup_12h () {
- test_create_repo 12h &&
+ git init 12h &&
(
cd 12h &&
@@ -5032,7 +5032,7 @@ test_expect_failure '12h: renaming a file within a renamed directory' '
# source/bar vs. source/subdir/bar
test_setup_12i () {
- test_create_repo 12i &&
+ git init 12i &&
(
cd 12i &&
@@ -5090,7 +5090,7 @@ test_expect_success '12i: Directory rename causes rename-to-self' '
# Expected: {foo, bar, baz_2}, with conflicts on bar vs. subdir/bar
test_setup_12j () {
- test_create_repo 12j &&
+ git init 12j &&
(
cd 12j &&
@@ -5148,7 +5148,7 @@ test_expect_success '12j: Directory rename to root causes rename-to-self' '
# Expected: dirA/{foo, bar, baz_2}, with conflicts on dirA/bar vs. dirB/bar
test_setup_12k () {
- test_create_repo 12k &&
+ git init 12k &&
(
cd 12k &&
@@ -5199,6 +5199,167 @@ test_expect_success '12k: Directory rename with sibling causes rename-to-self' '
)
'
+# Testcase 12l, Both sides rename a directory into the other side, both add
+# a file which after directory renames are the same filename
+# Commit O: sub1/file, sub2/other
+# Commit A: sub3/file, sub2/{other, new_add_add_file_1}
+# Commit B: sub1/{file, newfile}, sub1/sub2/{other, new_add_add_file_2}
+#
+# In words:
+# A: sub1/ -> sub3/, add sub2/new_add_add_file_1
+# B: sub2/ -> sub1/sub2, add sub1/newfile, add sub1/sub2/new_add_add_file_2
+#
+# Expected: sub3/{file, newfile, sub2/other}
+# CONFLICT (add/add): sub1/sub2/new_add_add_file
+#
+# Note that sub1/newfile is not extraneous. Directory renames are only
+# detected if they are needed, and they are only needed if the old directory
+# had a new file added on the opposite side of history. So sub1/newfile
+# is needed for there to be a sub1/ -> sub3/ rename.
+
+test_setup_12l () {
+ git init 12l_$1 &&
+ (
+ cd 12l_$1 &&
+
+ mkdir sub1 sub2
+ echo file >sub1/file &&
+ echo other >sub2/other &&
+ git add sub1 sub2 &&
+ git commit -m "O" &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git checkout A &&
+ git mv sub1 sub3 &&
+ echo conflicting >sub2/new_add_add_file &&
+ git add sub2 &&
+ test_tick &&
+ git add -u &&
+ git commit -m "A" &&
+
+ git checkout B &&
+ echo dissimilar >sub2/new_add_add_file &&
+ echo brand >sub1/newfile &&
+ git add sub1 sub2 &&
+ git mv sub2 sub1 &&
+ test_tick &&
+ git commit -m "B"
+ )
+}
+
+test_expect_merge_algorithm failure success '12l (B into A): Rename into each other + add/add conflict' '
+ test_setup_12l BintoA &&
+ (
+ cd 12l_BintoA &&
+
+ git checkout -q A^0 &&
+
+ test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 &&
+
+ test_stdout_line_count = 5 git ls-files -s &&
+
+ git rev-parse >actual \
+ :0:sub3/file :0:sub3/newfile :0:sub3/sub2/other \
+ :2:sub1/sub2/new_add_add_file \
+ :3:sub1/sub2/new_add_add_file &&
+ git rev-parse >expect \
+ O:sub1/file B:sub1/newfile O:sub2/other \
+ A:sub2/new_add_add_file \
+ B:sub1/sub2/new_add_add_file &&
+ test_cmp expect actual &&
+
+ git ls-files -o >actual &&
+ test_write_lines actual expect >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_merge_algorithm failure success '12l (A into B): Rename into each other + add/add conflict' '
+ test_setup_12l AintoB &&
+ (
+ cd 12l_AintoB &&
+
+ git checkout -q B^0 &&
+
+ test_must_fail git -c merge.directoryRenames=true merge -s recursive A^0 &&
+
+ test_stdout_line_count = 5 git ls-files -s &&
+
+ git rev-parse >actual \
+ :0:sub3/file :0:sub3/newfile :0:sub3/sub2/other \
+ :2:sub1/sub2/new_add_add_file \
+ :3:sub1/sub2/new_add_add_file &&
+ git rev-parse >expect \
+ O:sub1/file B:sub1/newfile O:sub2/other \
+ B:sub1/sub2/new_add_add_file \
+ A:sub2/new_add_add_file &&
+ test_cmp expect actual &&
+
+ git ls-files -o >actual &&
+ test_write_lines actual expect >expect &&
+ test_cmp expect actual
+ )
+'
+
+# Testcase 12m, Directory rename, plus change of parent dir to symlink
+# Commit O: dir/subdir/file
+# Commit A: renamed-dir/subdir/file
+# Commit B: dir/subdir
+# In words:
+# A: dir/subdir/ -> renamed-dir/subdir
+# B: delete dir/subdir/file, add dir/subdir as symlink
+#
+# Expected: CONFLICT (rename/delete): renamed-dir/subdir/file,
+# CONFLICT (file location): renamed-dir/subdir vs. dir/subdir
+# CONFLICT (directory/file): renamed-dir/subdir symlink has
+# renamed-dir/subdir in the way
+
+test_setup_12m () {
+ git init 12m &&
+ (
+ cd 12m &&
+
+ mkdir -p dir/subdir &&
+ echo 1 >dir/subdir/file &&
+ git add . &&
+ git commit -m "O" &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git switch A &&
+ git mv dir/ renamed-dir/ &&
+ git add . &&
+ git commit -m "A" &&
+
+ git switch B &&
+ git rm dir/subdir/file &&
+ mkdir dir &&
+ ln -s /dev/null dir/subdir &&
+ git add . &&
+ git commit -m "B"
+ )
+}
+
+test_expect_merge_algorithm failure success '12m: Change parent of renamed-dir to symlink on other side' '
+ test_setup_12m &&
+ (
+ cd 12m &&
+
+ git checkout -q A^0 &&
+
+ test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 &&
+
+ test_stdout_line_count = 3 git ls-files -s &&
+ test_stdout_line_count = 2 ls -1 renamed-dir &&
+ test_path_is_missing dir
+ )
+'
+
###########################################################################
# SECTION 13: Checking informational and conflict messages
#
@@ -5217,7 +5378,7 @@ test_expect_success '12k: Directory rename with sibling causes rename-to-self' '
# Expected: y/{b,c,d,e/f}, with notices/conflicts for both y/d and y/e/f
test_setup_13a () {
- test_create_repo 13a_$1 &&
+ git init 13a_$1 &&
(
cd 13a_$1 &&
@@ -5256,8 +5417,8 @@ test_expect_success '13a(conflict): messages for newly added files' '
test_must_fail git merge -s recursive B^0 >out 2>err &&
- test_i18ngrep CONFLICT..file.location.*z/e/f.added.in.B^0.*y/e/f out &&
- test_i18ngrep CONFLICT..file.location.*z/d.added.in.B^0.*y/d out &&
+ test_grep CONFLICT..file.location.*z/e/f.added.in.B^0.*y/e/f out &&
+ test_grep CONFLICT..file.location.*z/d.added.in.B^0.*y/d out &&
git ls-files >paths &&
! grep z/ paths &&
@@ -5280,8 +5441,8 @@ test_expect_success '13a(info): messages for newly added files' '
git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
- test_i18ngrep Path.updated:.*z/e/f.added.in.B^0.*y/e/f out &&
- test_i18ngrep Path.updated:.*z/d.added.in.B^0.*y/d out &&
+ test_grep Path.updated:.*z/e/f.added.in.B^0.*y/e/f out &&
+ test_grep Path.updated:.*z/d.added.in.B^0.*y/d out &&
git ls-files >paths &&
! grep z/ paths &&
@@ -5304,7 +5465,7 @@ test_expect_success '13a(info): messages for newly added files' '
# one about content, and one about file location
test_setup_13b () {
- test_create_repo 13b_$1 &&
+ git init 13b_$1 &&
(
cd 13b_$1 &&
@@ -5346,8 +5507,8 @@ test_expect_success '13b(conflict): messages for transitive rename with conflict
test_must_fail git merge -s recursive B^0 >out 2>err &&
- test_i18ngrep CONFLICT.*content.*Merge.conflict.in.y/d out &&
- test_i18ngrep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out &&
+ test_grep CONFLICT.*content.*Merge.conflict.in.y/d out &&
+ test_grep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out &&
git ls-files >paths &&
! grep z/ paths &&
@@ -5368,8 +5529,8 @@ test_expect_success '13b(info): messages for transitive rename with conflicted c
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
- test_i18ngrep CONFLICT.*content.*Merge.conflict.in.y/d out &&
- test_i18ngrep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out &&
+ test_grep CONFLICT.*content.*Merge.conflict.in.y/d out &&
+ test_grep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out &&
git ls-files >paths &&
! grep z/ paths &&
@@ -5391,7 +5552,7 @@ test_expect_success '13b(info): messages for transitive rename with conflicted c
# shown in testcase 13d.
test_setup_13c () {
- test_create_repo 13c_$1 &&
+ git init 13c_$1 &&
(
cd 13c_$1 &&
@@ -5432,7 +5593,7 @@ test_expect_success '13c(conflict): messages for rename/rename(1to1) via transit
test_must_fail git merge -s recursive B^0 >out 2>err &&
- test_i18ngrep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out &&
+ test_grep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out &&
git ls-files >paths &&
! grep z/ paths &&
@@ -5453,7 +5614,7 @@ test_expect_success '13c(info): messages for rename/rename(1to1) via transitive
git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
- test_i18ngrep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out &&
+ test_grep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out &&
git ls-files >paths &&
! grep z/ paths &&
@@ -5479,7 +5640,7 @@ test_expect_success '13c(info): messages for rename/rename(1to1) via transitive
# No conflict in where a/y ends up, so put it in d/y.
test_setup_13d () {
- test_create_repo 13d_$1 &&
+ git init 13d_$1 &&
(
cd 13d_$1 &&
@@ -5521,8 +5682,8 @@ test_expect_success '13d(conflict): messages for rename/rename(1to1) via dual tr
test_must_fail git merge -s recursive B^0 >out 2>err &&
- test_i18ngrep CONFLICT..file.location.*a/y.renamed.to.b/y.*moved.to.d/y out &&
- test_i18ngrep CONFLICT..file.location.*a/y.renamed.to.c/y.*moved.to.d/y out &&
+ test_grep CONFLICT..file.location.*a/y.renamed.to.b/y.*moved.to.d/y out &&
+ test_grep CONFLICT..file.location.*a/y.renamed.to.c/y.*moved.to.d/y out &&
git ls-files >paths &&
! grep b/ paths &&
@@ -5545,8 +5706,8 @@ test_expect_success '13d(info): messages for rename/rename(1to1) via dual transi
git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
- test_i18ngrep Path.updated.*a/y.renamed.to.b/y.*moving.it.to.d/y out &&
- test_i18ngrep Path.updated.*a/y.renamed.to.c/y.*moving.it.to.d/y out &&
+ test_grep Path.updated.*a/y.renamed.to.b/y.*moving.it.to.d/y out &&
+ test_grep Path.updated.*a/y.renamed.to.c/y.*moving.it.to.d/y out &&
git ls-files >paths &&
! grep b/ paths &&
@@ -5605,7 +5766,7 @@ test_expect_success '13d(info): messages for rename/rename(1to1) via dual transi
# least avoids hitting a BUG().
#
test_setup_13e () {
- test_create_repo 13e &&
+ git init 13e &&
(
cd 13e &&
@@ -5660,9 +5821,9 @@ test_expect_success '13e: directory rename detection in recursive case' '
git -c merge.directoryRenames=conflict merge -s recursive C^0 >out 2>err &&
- test_i18ngrep ! CONFLICT out &&
- test_i18ngrep ! BUG: err &&
- test_i18ngrep ! core.dumped err &&
+ test_grep ! CONFLICT out &&
+ test_grep ! BUG: err &&
+ test_grep ! core.dumped err &&
test_must_be_empty err &&
git ls-files >paths &&
diff --git a/t/t6424-merge-unrelated-index-changes.sh b/t/t6424-merge-unrelated-index-changes.sh
index 5e3779e..7677c5f 100755
--- a/t/t6424-merge-unrelated-index-changes.sh
+++ b/t/t6424-merge-unrelated-index-changes.sh
@@ -71,7 +71,9 @@ test_expect_success 'ff update' '
git merge E^0 &&
test_must_fail git rev-parse HEAD:random_file &&
- test "$(git diff --name-only --cached E)" = "random_file"
+ test "$(git diff --name-only --cached E)" = "random_file" &&
+ test_path_is_file random_file &&
+ git rev-parse --verify :random_file
'
test_expect_success 'ff update, important file modified' '
@@ -83,6 +85,8 @@ test_expect_success 'ff update, important file modified' '
git add subdir/e &&
test_must_fail git merge E^0 &&
+ test_path_is_file subdir/e &&
+ git rev-parse --verify :subdir/e &&
test_path_is_missing .git/MERGE_HEAD
'
@@ -93,6 +97,8 @@ test_expect_success 'resolve, trivial' '
touch random_file && git add random_file &&
test_must_fail git merge -s resolve C^0 &&
+ test_path_is_file random_file &&
+ git rev-parse --verify :random_file &&
test_path_is_missing .git/MERGE_HEAD
'
@@ -103,6 +109,41 @@ test_expect_success 'resolve, non-trivial' '
touch random_file && git add random_file &&
test_must_fail git merge -s resolve D^0 &&
+ test_path_is_file random_file &&
+ git rev-parse --verify :random_file &&
+ test_path_is_missing .git/MERGE_HEAD
+'
+
+test_expect_success 'resolve, trivial, related file removed' '
+ git reset --hard &&
+ git checkout B^0 &&
+
+ git rm a &&
+ test_path_is_missing a &&
+
+ test_must_fail git merge -s resolve C^0 &&
+
+ test_path_is_missing a &&
+ test_path_is_missing .git/MERGE_HEAD
+'
+
+test_expect_success 'resolve, non-trivial, related file removed' '
+ git reset --hard &&
+ git checkout B^0 &&
+
+ git rm a &&
+ test_path_is_missing a &&
+
+ # We also ask for recursive in order to turn off the "allow_trivial"
+ # setting in builtin/merge.c, and ensure that resolve really does
+ # correctly fail the merge (I guess this also tests that recursive
+ # correctly fails the merge, but the main thing we are attempting
+ # to test here is resolve and are just using the side effect of
+ # adding recursive to ensure that resolve is actually tested rather
+ # than the trivial merge codepath)
+ test_must_fail git merge -s resolve -s recursive D^0 &&
+
+ test_path_is_missing a &&
test_path_is_missing .git/MERGE_HEAD
'
@@ -113,6 +154,8 @@ test_expect_success 'recursive' '
touch random_file && git add random_file &&
test_must_fail git merge -s recursive C^0 &&
+ test_path_is_file random_file &&
+ git rev-parse --verify :random_file &&
test_path_is_missing .git/MERGE_HEAD
'
@@ -132,9 +175,10 @@ test_expect_success 'merge-recursive, when index==head but head!=HEAD' '
# Make index match B
git diff C B -- | git apply --cached &&
+ test_when_finished "git clean -fd" && # Do not leave untracked around
# Merge B & F, with B as "head"
git merge-recursive A -- B F > out &&
- test_i18ngrep "Already up to date" out
+ test_grep "Already up to date" out
'
test_expect_success 'recursive, when file has staged changes not matching HEAD nor what a merge would give' '
@@ -144,10 +188,13 @@ test_expect_success 'recursive, when file has staged changes not matching HEAD n
mkdir subdir &&
test_seq 1 10 >subdir/a &&
git add subdir/a &&
+ git rev-parse --verify :subdir/a >expect &&
# 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
+ git rev-parse --verify :subdir/a >actual &&
+ test_cmp expect actual &&
+ test_grep "changes to the following files would be overwritten" err
'
test_expect_success 'recursive, when file has staged changes matching what a merge would give' '
@@ -157,10 +204,13 @@ test_expect_success 'recursive, when file has staged changes matching what a mer
mkdir subdir &&
test_seq 1 11 >subdir/a &&
git add subdir/a &&
+ git rev-parse --verify :subdir/a >expect &&
# 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
+ git rev-parse --verify :subdir/a >actual &&
+ test_cmp expect actual &&
+ test_grep "changes to the following files would be overwritten" err
'
test_expect_success 'octopus, unrelated file touched' '
@@ -170,7 +220,9 @@ test_expect_success 'octopus, unrelated file touched' '
touch random_file && git add random_file &&
test_must_fail git merge C^0 D^0 &&
- test_path_is_missing .git/MERGE_HEAD
+ test_path_is_missing .git/MERGE_HEAD &&
+ git rev-parse --verify :random_file &&
+ test_path_exists random_file
'
test_expect_success 'octopus, related file removed' '
@@ -180,6 +232,8 @@ test_expect_success 'octopus, related file removed' '
git rm b &&
test_must_fail git merge C^0 D^0 &&
+ test_path_is_missing b &&
+ test_must_fail git rev-parse --verify :b &&
test_path_is_missing .git/MERGE_HEAD
'
@@ -188,8 +242,12 @@ test_expect_success 'octopus, related file modified' '
git checkout B^0 &&
echo 12 >>a && git add a &&
+ git rev-parse --verify :a >expect &&
test_must_fail git merge C^0 D^0 &&
+ test_path_is_file a &&
+ git rev-parse --verify :a >actual &&
+ test_cmp expect actual &&
test_path_is_missing .git/MERGE_HEAD
'
@@ -200,6 +258,8 @@ test_expect_success 'ours' '
touch random_file && git add random_file &&
test_must_fail git merge -s ours C^0 &&
+ test_path_is_file random_file &&
+ git rev-parse --verify :random_file &&
test_path_is_missing .git/MERGE_HEAD
'
@@ -210,7 +270,41 @@ test_expect_success 'subtree' '
touch random_file && git add random_file &&
test_must_fail git merge -s subtree E^0 &&
+ test_path_is_file random_file &&
+ git rev-parse --verify :random_file &&
test_path_is_missing .git/MERGE_HEAD
'
+test_expect_success 'avoid failure due to stat-dirty files' '
+ git reset --hard &&
+ git checkout B^0 &&
+
+ # Make "a" be stat-dirty
+ test-tool chmtime =+1 a &&
+
+ # stat-dirty file should not prevent stash creation in builtin/merge.c
+ git merge -s resolve -s recursive D^0
+'
+
+test_expect_success 'with multiple strategies, recursive or ort failure do not early abort' '
+ git reset --hard &&
+ git checkout B^0 &&
+
+ test_seq 0 10 >a &&
+ git add a &&
+ git rev-parse :a >expect &&
+
+ sane_unset GIT_TEST_MERGE_ALGORITHM &&
+ test_must_fail git merge -s recursive -s ort -s octopus C^0 >output 2>&1 &&
+
+ grep "Trying merge strategy recursive..." output &&
+ grep "Trying merge strategy ort..." output &&
+ grep "Trying merge strategy octopus..." output &&
+ grep "No merge strategy handled the merge." output &&
+
+ # Changes to "a" should remain staged
+ git rev-parse :a >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t6425-merge-rename-delete.sh b/t/t6425-merge-rename-delete.sh
index 459b431..b95b064 100755
--- a/t/t6425-merge-rename-delete.sh
+++ b/t/t6425-merge-rename-delete.sh
@@ -4,6 +4,7 @@ test_description='Merge-recursive rename/delete conflict message'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'rename/delete' '
@@ -20,8 +21,8 @@ test_expect_success 'rename/delete' '
git commit -m "delete" &&
test_must_fail git merge --strategy=recursive rename >output &&
- test_i18ngrep "CONFLICT (rename/delete): A.* renamed .*to B.* in rename" output &&
- test_i18ngrep "CONFLICT (rename/delete): A.*deleted in HEAD." output
+ test_grep "CONFLICT (rename/delete): A.* renamed .*to B.* in rename" output &&
+ test_grep "CONFLICT (rename/delete): A.*deleted in HEAD." output
'
test_done
diff --git a/t/t6426-merge-skip-unneeded-updates.sh b/t/t6426-merge-skip-unneeded-updates.sh
index 7b5f1c1..b059475 100755
--- a/t/t6426-merge-skip-unneeded-updates.sh
+++ b/t/t6426-merge-skip-unneeded-updates.sh
@@ -38,7 +38,7 @@ test_description="merge cases"
# Expected: b_2
test_setup_1a () {
- test_create_repo 1a_$1 &&
+ git init 1a_$1 &&
(
cd 1a_$1 &&
@@ -136,7 +136,7 @@ test_expect_success '1a-R: Modify(A)/Modify(B), change on B subset of A' '
# Expected: c_2
test_setup_2a () {
- test_create_repo 2a_$1 &&
+ git init 2a_$1 &&
(
cd 2a_$1 &&
@@ -229,7 +229,7 @@ test_expect_success '2a-R: Modify/rename, merge into rename side' '
# Expected: c_2
test_setup_2b () {
- test_create_repo 2b_$1 &&
+ git init 2b_$1 &&
(
cd 2b_$1 &&
@@ -336,7 +336,7 @@ test_expect_success '2b-R: Rename+Mod(A)/Mod(B), B mods subset of A' '
# not make that particular mistake.
test_setup_2c () {
- test_create_repo 2c &&
+ git init 2c &&
(
cd 2c &&
@@ -375,45 +375,33 @@ test_expect_success '2c: Modify b & add c VS rename b->c' '
export GIT_MERGE_VERBOSITY &&
test_must_fail git merge -s recursive B^0 >out 2>err &&
- test_i18ngrep "CONFLICT (.*/add):" out &&
+ test_grep "CONFLICT (.*/add):" out &&
test_must_be_empty err &&
- # Make sure c WAS updated
+ git ls-files -s >index_files &&
+ test_line_count = 2 index_files &&
+
+ # Ensure b was removed
+ test_path_is_missing b &&
+
+ # Make sure c WAS updated...
test-tool chmtime --get c >new-mtime &&
- test $(cat old-mtime) -lt $(cat new-mtime)
-
- # FIXME: rename/add conflicts are horribly broken right now;
- # when I get back to my patch series fixing it and
- # rename/rename(2to1) conflicts to bring them in line with
- # how add/add conflicts behave, then checks like the below
- # could be added. But that patch series is waiting until
- # the rename-directory-detection series lands, which this
- # is part of. And in the mean time, I do not want to further
- # enforce broken behavior. So for now, the main test is the
- # one above that err is an empty file.
-
- #git ls-files -s >index_files &&
- #test_line_count = 2 index_files &&
-
- #git rev-parse >actual :2:c :3:c &&
- #git rev-parse >expect A:b A:c &&
- #test_cmp expect actual &&
-
- #git cat-file -p A:b >>merged &&
- #git cat-file -p A:c >>merge-me &&
- #>empty &&
- #test_must_fail git merge-file \
- # -L "Temporary merge branch 1" \
- # -L "" \
- # -L "Temporary merge branch 2" \
- # merged empty merge-me &&
- #sed -e "s/^\([<=>]\)/\1\1\1/" merged >merged-internal &&
-
- #git hash-object c >actual &&
- #git hash-object merged-internal >expect &&
- #test_cmp expect actual &&
-
- #test_path_is_missing b
+ test $(cat old-mtime) -lt $(cat new-mtime) &&
+
+ # ...and has correct index entries and working tree contents
+ git rev-parse >actual :2:c :3:c &&
+ git rev-parse >expect A:c A:b &&
+ test_cmp expect actual &&
+
+ git cat-file -p A:b >>merge-me &&
+ git cat-file -p A:c >>merged &&
+ >empty &&
+ test_must_fail git merge-file \
+ -L "HEAD" \
+ -L "" \
+ -L "B^0" \
+ merged empty merge-me &&
+ test_cmp merged c
)
'
@@ -437,7 +425,7 @@ test_expect_success '2c: Modify b & add c VS rename b->c' '
# Expected: bar/{bq_2, whatever}
test_setup_3a () {
- test_create_repo 3a_$1 &&
+ git init 3a_$1 &&
(
cd 3a_$1 &&
@@ -537,7 +525,7 @@ test_expect_success '3a-R: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
# Expected: bar/{bq_2, whatever}
test_setup_3b () {
- test_create_repo 3b_$1 &&
+ git init 3b_$1 &&
(
cd 3b_$1 &&
@@ -642,7 +630,7 @@ test_expect_success '3b-R: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
# Expected: b_2 for merge, b_4 in working copy
test_setup_4a () {
- test_create_repo 4a &&
+ git init 4a &&
(
cd 4a &&
@@ -714,7 +702,7 @@ test_expect_merge_algorithm failure success '4a: Change on A, change on B subset
# Expected: c_2
test_setup_4b () {
- test_create_repo 4b &&
+ git init 4b &&
(
cd 4b &&
diff --git a/t/t6427-diff3-conflict-markers.sh b/t/t6427-diff3-conflict-markers.sh
index 25c4b72..dd5fe6a 100755
--- a/t/t6427-diff3-conflict-markers.sh
+++ b/t/t6427-diff3-conflict-markers.sh
@@ -19,7 +19,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
#
test_expect_success 'setup no merge base' '
- test_create_repo no_merge_base &&
+ git init no_merge_base &&
(
cd no_merge_base &&
@@ -55,7 +55,7 @@ test_expect_success 'check no merge base' '
#
test_expect_success 'setup unique merge base' '
- test_create_repo unique_merge_base &&
+ git init unique_merge_base &&
(
cd unique_merge_base &&
@@ -116,7 +116,7 @@ test_expect_success 'check unique merge base' '
#
test_expect_success 'setup multiple merge bases' '
- test_create_repo multiple_merge_bases &&
+ git init multiple_merge_bases &&
(
cd multiple_merge_bases &&
@@ -190,7 +190,7 @@ test_expect_success 'check multiple merge bases' '
'
test_expect_success 'rebase --merge describes parent of commit being picked' '
- test_create_repo rebase &&
+ git init rebase &&
(
cd rebase &&
test_commit base file &&
@@ -211,4 +211,94 @@ test_expect_success 'rebase --apply describes fake ancestor base' '
)
'
+test_setup_zdiff3 () {
+ git init zdiff3 &&
+ (
+ cd zdiff3 &&
+
+ test_write_lines 1 2 3 4 5 6 7 8 9 >basic &&
+ test_write_lines 1 2 3 AA 4 5 BB 6 7 8 >middle-common &&
+ test_write_lines 1 2 3 4 5 6 7 8 9 >interesting &&
+ test_write_lines 1 2 3 4 5 6 7 8 9 >evil &&
+
+ git add basic middle-common interesting evil &&
+ git commit -m base &&
+
+ git branch left &&
+ git branch right &&
+
+ git checkout left &&
+ test_write_lines 1 2 3 4 A B C D E 7 8 9 >basic &&
+ test_write_lines 1 2 3 CC 4 5 DD 6 7 8 >middle-common &&
+ test_write_lines 1 2 3 4 A B C D E F G H I J 7 8 9 >interesting &&
+ test_write_lines 1 2 3 4 X A B C 7 8 9 >evil &&
+ git add -u &&
+ git commit -m letters &&
+
+ git checkout right &&
+ test_write_lines 1 2 3 4 A X C Y E 7 8 9 >basic &&
+ test_write_lines 1 2 3 EE 4 5 FF 6 7 8 >middle-common &&
+ test_write_lines 1 2 3 4 A B C 5 6 G H I J 7 8 9 >interesting &&
+ test_write_lines 1 2 3 4 Y A B C B C 7 8 9 >evil &&
+ git add -u &&
+ git commit -m permuted
+ )
+}
+
+test_expect_success 'check zdiff3 markers' '
+ test_setup_zdiff3 &&
+ (
+ cd zdiff3 &&
+
+ git checkout left^0 &&
+
+ base=$(git rev-parse --short HEAD^1) &&
+ test_must_fail git -c merge.conflictstyle=zdiff3 merge -s recursive right^0 &&
+
+ test_write_lines 1 2 3 4 A \
+ "<<<<<<< HEAD" B C D \
+ "||||||| $base" 5 6 \
+ ======= X C Y \
+ ">>>>>>> right^0" \
+ E 7 8 9 \
+ >expect &&
+ test_cmp expect basic &&
+
+ test_write_lines 1 2 3 \
+ "<<<<<<< HEAD" CC \
+ "||||||| $base" AA \
+ ======= EE \
+ ">>>>>>> right^0" \
+ 4 5 \
+ "<<<<<<< HEAD" DD \
+ "||||||| $base" BB \
+ ======= FF \
+ ">>>>>>> right^0" \
+ 6 7 8 \
+ >expect &&
+ test_cmp expect middle-common &&
+
+ test_write_lines 1 2 3 4 A B C \
+ "<<<<<<< HEAD" D E F \
+ "||||||| $base" 5 6 \
+ ======= 5 6 \
+ ">>>>>>> right^0" \
+ G H I J 7 8 9 \
+ >expect &&
+ test_cmp expect interesting &&
+
+ # Not passing this one yet; the common "B C" lines is still
+ # being left in the conflict blocks on the left and right
+ # sides.
+ test_write_lines 1 2 3 4 \
+ "<<<<<<< HEAD" X A \
+ "||||||| $base" 5 6 \
+ ======= Y A B C \
+ ">>>>>>> right^0" \
+ B C 7 8 9 \
+ >expect &&
+ test_cmp expect evil
+ )
+'
+
test_done
diff --git a/t/t6428-merge-conflicts-sparse.sh b/t/t6428-merge-conflicts-sparse.sh
index 7e8bf49..9919c3f 100755
--- a/t/t6428-merge-conflicts-sparse.sh
+++ b/t/t6428-merge-conflicts-sparse.sh
@@ -29,7 +29,7 @@ test_description="merge cases"
# Testcase basic, conflicting changes in 'numerals'
test_setup_numerals () {
- test_create_repo numerals_$1 &&
+ git init numerals_$1 &&
(
cd numerals_$1 &&
@@ -87,7 +87,7 @@ test_expect_success 'conflicting entries written to worktree even if sparse' '
test_path_is_file numerals &&
git sparse-checkout init &&
- git sparse-checkout set README &&
+ git sparse-checkout set --no-cone README &&
test_path_is_file README &&
test_path_is_missing numerals &&
@@ -112,7 +112,7 @@ test_expect_success 'conflicting entries written to worktree even if sparse' '
)
'
-test_expect_merge_algorithm failure success 'present-despite-SKIP_WORKTREE handled reasonably' '
+test_expect_success 'present-despite-SKIP_WORKTREE handled reasonably' '
test_setup_numerals in_the_way &&
(
cd numerals_in_the_way &&
@@ -123,7 +123,7 @@ test_expect_merge_algorithm failure success 'present-despite-SKIP_WORKTREE handl
test_path_is_file numerals &&
git sparse-checkout init &&
- git sparse-checkout set README &&
+ git sparse-checkout set --no-cone README &&
test_path_is_file README &&
test_path_is_missing numerals &&
@@ -132,26 +132,13 @@ test_expect_merge_algorithm failure success 'present-despite-SKIP_WORKTREE handl
test_must_fail git merge -s recursive B^0 &&
- git ls-files -t >index_files &&
- test_cmp expected-index index_files &&
+ test_path_is_missing .git/MERGE_HEAD &&
- test_path_is_file README &&
test_path_is_file numerals &&
- test_cmp expected-merge numerals &&
-
- # There should still be a file with "foobar" in it
- grep foobar * &&
-
- # 5 other files:
- # * expected-merge
- # * expected-index
- # * index_files
- # * others
- # * whatever name was given to the numerals file that had
- # "foobar" in it
- git ls-files -o >others &&
- test_line_count = 5 others
+ # numerals should still have "foobar" in it
+ echo foobar >expect &&
+ test_cmp expect numerals
)
'
diff --git a/t/t6429-merge-sequence-rename-caching.sh b/t/t6429-merge-sequence-rename-caching.sh
index 035edc4..0f39ed0 100755
--- a/t/t6429-merge-sequence-rename-caching.sh
+++ b/t/t6429-merge-sequence-rename-caching.sh
@@ -35,7 +35,7 @@ test_description="remember regular & dir renames in sequence of merges"
# preventing us from finding new renames.
#
test_expect_success 'caching renames does not preclude finding new ones' '
- test_create_repo caching-renames-and-new-renames &&
+ git init caching-renames-and-new-renames &&
(
cd caching-renames-and-new-renames &&
@@ -71,8 +71,9 @@ test_expect_success 'caching renames does not preclude finding new ones' '
git switch upstream &&
- test-tool fast-rebase --onto HEAD upstream~1 topic &&
- #git cherry-pick upstream~1..topic
+ git replay --onto HEAD upstream~1..topic >out &&
+ git update-ref --stdin <out &&
+ git checkout topic &&
git ls-files >tracked-files &&
test_line_count = 2 tracked-files &&
@@ -106,7 +107,7 @@ test_expect_success 'caching renames does not preclude finding new ones' '
# should be able to only run rename detection on the upstream side one
# time.)
test_expect_success 'cherry-pick both a commit and its immediate revert' '
- test_create_repo pick-commit-and-its-immediate-revert &&
+ git init pick-commit-and-its-immediate-revert &&
(
cd pick-commit-and-its-immediate-revert &&
@@ -140,8 +141,9 @@ test_expect_success 'cherry-pick both a commit and its immediate revert' '
GIT_TRACE2_PERF="$(pwd)/trace.output" &&
export GIT_TRACE2_PERF &&
- test-tool fast-rebase --onto HEAD upstream~1 topic &&
- #git cherry-pick upstream~1..topic &&
+ git replay --onto HEAD upstream~1..topic >out &&
+ git update-ref --stdin <out &&
+ git checkout topic &&
grep region_enter.*diffcore_rename trace.output >calls &&
test_line_count = 1 calls
@@ -162,7 +164,7 @@ test_expect_success 'cherry-pick both a commit and its immediate revert' '
# could cause a spurious rename/add conflict.
#
test_expect_success 'rename same file identically, then reintroduce it' '
- test_create_repo rename-rename-1to1-then-add-old-filename &&
+ git init rename-rename-1to1-then-add-old-filename &&
(
cd rename-rename-1to1-then-add-old-filename &&
@@ -199,8 +201,9 @@ test_expect_success 'rename same file identically, then reintroduce it' '
GIT_TRACE2_PERF="$(pwd)/trace.output" &&
export GIT_TRACE2_PERF &&
- test-tool fast-rebase --onto HEAD upstream~1 topic &&
- #git cherry-pick upstream~1..topic &&
+ git replay --onto HEAD upstream~1..topic >out &&
+ git update-ref --stdin <out &&
+ git checkout topic &&
git ls-files >tracked &&
test_line_count = 2 tracked &&
@@ -229,7 +232,7 @@ test_expect_success 'rename same file identically, then reintroduce it' '
# cached, the directory rename could put newfile in the wrong directory.
#
test_expect_success 'rename same file identically, then add file to old dir' '
- test_create_repo rename-rename-1to1-then-add-file-to-old-dir &&
+ git init rename-rename-1to1-then-add-file-to-old-dir &&
(
cd rename-rename-1to1-then-add-file-to-old-dir &&
@@ -276,8 +279,9 @@ test_expect_success 'rename same file identically, then add file to old dir' '
GIT_TRACE2_PERF="$(pwd)/trace.output" &&
export GIT_TRACE2_PERF &&
- test-tool fast-rebase --onto HEAD upstream~1 topic &&
- #git cherry-pick upstream~1..topic &&
+ git replay --onto HEAD upstream~1..topic >out &&
+ git update-ref --stdin <out &&
+ git checkout topic &&
git ls-files >tracked &&
test_line_count = 4 tracked &&
@@ -311,7 +315,7 @@ test_expect_success 'rename same file identically, then add file to old dir' '
# should avoid the need to re-detect upstream renames.)
#
test_expect_success 'cached dir rename does not prevent noticing later conflict' '
- test_create_repo dir-rename-cache-not-occluding-later-conflict &&
+ git init dir-rename-cache-not-occluding-later-conflict &&
(
cd dir-rename-cache-not-occluding-later-conflict &&
@@ -353,10 +357,7 @@ test_expect_success 'cached dir rename does not prevent noticing later conflict'
GIT_TRACE2_PERF="$(pwd)/trace.output" &&
export GIT_TRACE2_PERF &&
- test_must_fail test-tool fast-rebase --onto HEAD upstream~1 topic >output &&
- #git cherry-pick upstream..topic &&
-
- grep CONFLICT..rename/rename output &&
+ test_must_fail git replay --onto HEAD upstream~1..topic >output &&
grep region_enter.*diffcore_rename trace.output >calls &&
test_line_count = 2 calls
@@ -365,7 +366,7 @@ test_expect_success 'cached dir rename does not prevent noticing later conflict'
# Helper for the next two tests
test_setup_upstream_rename () {
- test_create_repo $1 &&
+ git init $1 &&
(
cd $1 &&
@@ -455,8 +456,9 @@ test_expect_success 'dir rename unneeded, then add new file to old dir' '
GIT_TRACE2_PERF="$(pwd)/trace.output" &&
export GIT_TRACE2_PERF &&
- test-tool fast-rebase --onto HEAD upstream~1 topic &&
- #git cherry-pick upstream..topic &&
+ git replay --onto HEAD upstream~1..topic >out &&
+ git update-ref --stdin <out &&
+ git checkout topic &&
grep region_enter.*diffcore_rename trace.output >calls &&
test_line_count = 2 calls &&
@@ -521,8 +523,9 @@ test_expect_success 'dir rename unneeded, then rename existing file into old dir
GIT_TRACE2_PERF="$(pwd)/trace.output" &&
export GIT_TRACE2_PERF &&
- test-tool fast-rebase --onto HEAD upstream~1 topic &&
- #git cherry-pick upstream..topic &&
+ git replay --onto HEAD upstream~1..topic >out &&
+ git update-ref --stdin <out &&
+ git checkout topic &&
grep region_enter.*diffcore_rename trace.output >calls &&
test_line_count = 3 calls &&
@@ -537,7 +540,7 @@ test_expect_success 'dir rename unneeded, then rename existing file into old dir
# Helper for the next two tests
test_setup_topic_rename () {
- test_create_repo $1 &&
+ git init $1 &&
(
cd $1 &&
@@ -623,8 +626,9 @@ test_expect_success 'caching renames only on upstream side, part 1' '
GIT_TRACE2_PERF="$(pwd)/trace.output" &&
export GIT_TRACE2_PERF &&
- test-tool fast-rebase --onto HEAD upstream~1 topic &&
- #git cherry-pick upstream..topic &&
+ git replay --onto HEAD upstream~1..topic >out &&
+ git update-ref --stdin <out &&
+ git checkout topic &&
grep region_enter.*diffcore_rename trace.output >calls &&
test_line_count = 1 calls &&
@@ -681,8 +685,9 @@ test_expect_success 'caching renames only on upstream side, part 2' '
GIT_TRACE2_PERF="$(pwd)/trace.output" &&
export GIT_TRACE2_PERF &&
- test-tool fast-rebase --onto HEAD upstream~1 topic &&
- #git cherry-pick upstream..topic &&
+ git replay --onto HEAD upstream~1..topic >out &&
+ git update-ref --stdin <out &&
+ git checkout topic &&
grep region_enter.*diffcore_rename trace.output >calls &&
test_line_count = 2 calls &&
@@ -697,4 +702,71 @@ test_expect_success 'caching renames only on upstream side, part 2' '
)
'
+#
+# The following testcase just creates two simple renames (slightly modified
+# on both sides but without conflicting changes), and a directory full of
+# files that are otherwise uninteresting. The setup is as follows:
+#
+# base: unrelated/<BUNCH OF FILES>
+# numbers
+# values
+# upstream: modify: numbers
+# modify: values
+# topic: add: unrelated/foo
+# modify: numbers
+# modify: values
+# rename: numbers -> sequence
+# rename: values -> progression
+#
+# This is a trivial rename case, but we're curious what happens with a very
+# low renameLimit interacting with the restart optimization trying to notice
+# that unrelated/ looks like a trivial merge candidate.
+#
+test_expect_success 'avoid assuming we detected renames' '
+ git init redo-weirdness &&
+ (
+ cd redo-weirdness &&
+
+ mkdir unrelated &&
+ for i in $(test_seq 1 10)
+ do
+ >unrelated/$i || exit 1
+ done &&
+ test_seq 2 10 >numbers &&
+ test_seq 12 20 >values &&
+ git add numbers values unrelated/ &&
+ git commit -m orig &&
+
+ git branch upstream &&
+ git branch topic &&
+
+ git switch upstream &&
+ test_seq 1 10 >numbers &&
+ test_seq 11 20 >values &&
+ git add numbers &&
+ git commit -m "Some tweaks" &&
+
+ git switch topic &&
+
+ >unrelated/foo &&
+ test_seq 2 12 >numbers &&
+ test_seq 12 22 >values &&
+ git add numbers values unrelated/ &&
+ git mv numbers sequence &&
+ git mv values progression &&
+ git commit -m A &&
+
+ #
+ # Actual testing
+ #
+
+ git switch --detach topic^0 &&
+
+ test_must_fail git -c merge.renameLimit=1 rebase upstream &&
+
+ git ls-files -u >actual &&
+ test_line_count = 2 actual
+ )
+'
+
test_done
diff --git a/t/t6430-merge-recursive.sh b/t/t6430-merge-recursive.sh
index ffcc01f..ca15e6d 100755
--- a/t/t6430-merge-recursive.sh
+++ b/t/t6430-merge-recursive.sh
@@ -308,13 +308,13 @@ test_expect_success 'fail if the index has unresolved entries' '
test_must_fail git merge "$c5" &&
test_must_fail git merge "$c5" 2> out &&
- test_i18ngrep "not possible because you have unmerged files" out &&
+ test_grep "not possible because you have unmerged files" out &&
git add -u &&
test_must_fail git merge "$c5" 2> out &&
- test_i18ngrep "You have not concluded your merge" out &&
+ test_grep "You have not concluded your merge" out &&
rm -f .git/MERGE_HEAD &&
test_must_fail git merge "$c5" 2> out &&
- test_i18ngrep "Your local changes to the following files would be overwritten by merge:" out
+ test_grep "Your local changes to the following files would be overwritten by merge:" out
'
test_expect_success 'merge-recursive remove conflict' '
@@ -706,19 +706,21 @@ test_expect_success 'merge-recursive remembers the names of all base trees' '
# more trees than static slots used by oid_to_hex()
for commit in $c0 $c2 $c4 $c5 $c6 $c7
do
- git rev-parse "$commit^{tree}"
+ git rev-parse "$commit^{tree}" || return 1
done >trees &&
# ignore the return code; it only fails because the input is weird...
test_must_fail git -c merge.verbosity=5 merge-recursive $(cat trees) -- $c1 $c3 >out &&
# ...but make sure it fails in the expected way
- test_i18ngrep CONFLICT.*rename/rename out &&
+ test_grep CONFLICT.*rename/rename out &&
# merge-recursive prints in reverse order, but we do not care
sort <trees >expect &&
sed -n "s/^virtual //p" out | sort >actual &&
- test_cmp expect actual
+ test_cmp expect actual &&
+
+ git clean -fd
'
test_expect_success 'merge-recursive internal merge resolves to the sameness' '
diff --git a/t/t6431-merge-criscross.sh b/t/t6431-merge-criscross.sh
index 3824756..3fe14cd 100755
--- a/t/t6431-merge-criscross.sh
+++ b/t/t6431-merge-criscross.sh
@@ -2,6 +2,7 @@
test_description='merge-recursive backend test'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# A <- create some files
diff --git a/t/t6433-merge-toplevel.sh b/t/t6433-merge-toplevel.sh
index b160314..ed7866d 100755
--- a/t/t6433-merge-toplevel.sh
+++ b/t/t6433-merge-toplevel.sh
@@ -5,6 +5,7 @@ test_description='"git merge" top-level frontend'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
t3033_reset () {
@@ -151,7 +152,7 @@ test_expect_success 'refuse two-project merge by default, quit before --autostas
echo change >>one.t &&
git diff >expect &&
test_must_fail git merge --autostash five 2>err &&
- test_i18ngrep ! "stash" err &&
+ test_grep ! "stash" err &&
git diff >actual &&
test_cmp expect actual
'
@@ -169,7 +170,7 @@ test_expect_success 'two-project merge with --allow-unrelated-histories with --a
echo change >>one.t &&
git diff one.t >expect &&
git merge --allow-unrelated-histories --autostash five 2>err &&
- test_i18ngrep "Applied autostash." err &&
+ test_grep "Applied autostash." err &&
git diff one.t >actual &&
test_cmp expect actual
'
diff --git a/t/t6435-merge-sparse.sh b/t/t6435-merge-sparse.sh
index 74562e1..78628fb 100755
--- a/t/t6435-merge-sparse.sh
+++ b/t/t6435-merge-sparse.sh
@@ -2,6 +2,8 @@
test_description='merge with sparse files'
+TEST_CREATE_REPO_NO_TEMPLATE=1
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# test_file $filename $content
@@ -26,6 +28,7 @@ test_expect_success 'setup' '
git rm modify_delete &&
test_commit_this ours &&
git config core.sparseCheckout true &&
+ mkdir .git/info &&
echo "/checked-out" >.git/info/sparse-checkout &&
git reset --hard &&
test_must_fail git merge theirs
diff --git a/t/t6436-merge-overwrite.sh b/t/t6436-merge-overwrite.sh
index 84b4aac..4f43764 100755
--- a/t/t6436-merge-overwrite.sh
+++ b/t/t6436-merge-overwrite.sh
@@ -68,7 +68,8 @@ test_expect_success 'will not overwrite removed file' '
git commit -m "rm c1.c" &&
cp important c1.c &&
test_must_fail git merge c1a &&
- test_cmp important c1.c
+ test_cmp important c1.c &&
+ rm c1.c # Do not leave untracked file in way of future tests
'
test_expect_success 'will not overwrite re-added file' '
@@ -103,12 +104,12 @@ test_expect_success 'will not overwrite unstaged changes in renamed file' '
if test "$GIT_TEST_MERGE_ALGORITHM" = ort
then
test_must_fail git merge c1a >out 2>err &&
- test_i18ngrep "would be overwritten by merge" err &&
+ test_grep "would be overwritten by merge" err &&
test_cmp important other.c &&
test_path_is_missing .git/MERGE_HEAD
else
test_must_fail git merge c1a >out &&
- test_i18ngrep "Refusing to lose dirty file at other.c" out &&
+ test_grep "Refusing to lose dirty file at other.c" out &&
test_path_is_file other.c~HEAD &&
test $(git hash-object other.c~HEAD) = $(git rev-parse c1a:c1.c) &&
test_cmp important other.c
diff --git a/t/t6437-submodule-merge.sh b/t/t6437-submodule-merge.sh
index e5e89c2..7a3f1cb 100755
--- a/t/t6437-submodule-merge.sh
+++ b/t/t6437-submodule-merge.sh
@@ -5,6 +5,10 @@ test_description='merging with submodules'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1
+export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-merge.sh
@@ -100,8 +104,25 @@ test_expect_success 'setup for merge search' '
echo "file-c" > file-c &&
git add file-c &&
git commit -m "sub-c") &&
- git commit -a -m "c" &&
+ git commit -a -m "c")
+'
+
+test_expect_success 'merging should conflict for non fast-forward' '
+ test_when_finished "git -C merge-search reset --hard" &&
+ (cd merge-search &&
+ git checkout -b test-nonforward-a b &&
+ if test "$GIT_TEST_MERGE_ALGORITHM" = ort
+ then
+ test_must_fail git merge c 2>actual &&
+ sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-c)" &&
+ grep "$sub_expect" actual
+ else
+ test_must_fail git merge c 2> actual
+ fi)
+'
+test_expect_success 'finish setup for merge-search' '
+ (cd merge-search &&
git checkout -b d a &&
(cd sub &&
git checkout -b sub-d sub-b &&
@@ -126,14 +147,16 @@ test_expect_success 'merge with one side as a fast-forward of the other' '
test_cmp expect actual)
'
-test_expect_success 'merging should conflict for non fast-forward' '
+test_expect_success 'merging should conflict for non fast-forward (resolution exists)' '
(cd merge-search &&
- git checkout -b test-nonforward b &&
+ git checkout -b test-nonforward-b b &&
(cd sub &&
- git rev-parse sub-d > ../expect) &&
+ git rev-parse --short sub-d > ../expect) &&
if test "$GIT_TEST_MERGE_ALGORITHM" = ort
then
- test_must_fail git merge c >actual
+ test_must_fail git merge c >actual 2>sub-actual &&
+ sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-c)" &&
+ grep "$sub_expect" sub-actual
else
test_must_fail git merge c 2> actual
fi &&
@@ -158,7 +181,9 @@ test_expect_success 'merging should fail for ambiguous common parent' '
) &&
if test "$GIT_TEST_MERGE_ALGORITHM" = ort
then
- test_must_fail git merge c >actual
+ test_must_fail git merge c >actual 2>sub-actual &&
+ sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-c)" &&
+ grep "$sub_expect" sub-actual
else
test_must_fail git merge c 2> actual
fi &&
@@ -202,7 +227,12 @@ test_expect_success 'merging should fail for changes that are backwards' '
git commit -a -m "f" &&
git checkout -b test-backward e &&
- test_must_fail git merge f)
+ test_must_fail git merge f 2>actual &&
+ if test "$GIT_TEST_MERGE_ALGORITHM" = ort
+ then
+ sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-d)" &&
+ grep "$sub_expect" actual
+ fi)
'
@@ -307,7 +337,7 @@ test_expect_success 'recursive merge with submodule' '
# Expected: path/ is submodule and file contents for B's path are somewhere
test_expect_success 'setup file/submodule conflict' '
- test_create_repo file-submodule &&
+ git init file-submodule &&
(
cd file-submodule &&
@@ -322,7 +352,7 @@ test_expect_success 'setup file/submodule conflict' '
git commit -m B &&
git checkout A &&
- test_create_repo path &&
+ git init path &&
test_commit -C path world &&
git submodule add ./path &&
git commit -m A
@@ -382,7 +412,7 @@ test_expect_success 'file/submodule conflict; merge --abort works afterward' '
# under the submodule to be treated as untracked or in the way.
test_expect_success 'setup directory/submodule conflict' '
- test_create_repo directory-submodule &&
+ git init directory-submodule &&
(
cd directory-submodule &&
@@ -405,7 +435,7 @@ test_expect_success 'setup directory/submodule conflict' '
git commit -m B2 &&
git checkout A &&
- test_create_repo path &&
+ git init path &&
test_commit -C path hello world &&
git submodule add ./path &&
git commit -m A
@@ -450,7 +480,7 @@ test_expect_merge_algorithm failure success !FAIL_PREREQS 'directory/submodule c
# We do not want files within the submodule to prevent the
# merge from starting; we should not be writing to such paths
# anyway.
- test_i18ngrep ! "refusing to lose untracked file at" err
+ test_grep ! "refusing to lose untracked file at" err
)
'
@@ -473,4 +503,44 @@ test_expect_failure 'directory/submodule conflict; merge --abort works afterward
)
'
+# Setup:
+# - Submodule has 2 commits: a and b
+# - Superproject branch 'a' adds and commits submodule pointing to 'commit a'
+# - Superproject branch 'b' adds and commits submodule pointing to 'commit b'
+# If these two branches are now merged, there is no merge base
+test_expect_success 'setup for null merge base' '
+ mkdir no-merge-base &&
+ (cd no-merge-base &&
+ git init &&
+ mkdir sub &&
+ (cd sub &&
+ git init &&
+ echo "file-a" > file-a &&
+ git add file-a &&
+ git commit -m "commit a") &&
+ git commit --allow-empty -m init &&
+ git branch init &&
+ git checkout -b a init &&
+ git add sub &&
+ git commit -m "a" &&
+ git switch main &&
+ (cd sub &&
+ echo "file-b" > file-b &&
+ git add file-b &&
+ git commit -m "commit b"))
+'
+
+test_expect_success 'merging should fail with no merge base' '
+ (cd no-merge-base &&
+ git checkout -b b init &&
+ git add sub &&
+ git commit -m "b" &&
+ test_must_fail git merge a 2>actual &&
+ if test "$GIT_TEST_MERGE_ALGORITHM" = ort
+ then
+ sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short HEAD^1)" &&
+ grep "$sub_expect" actual
+ fi)
+'
+
test_done
diff --git a/t/t6439-merge-co-error-msgs.sh b/t/t6439-merge-co-error-msgs.sh
index 5bfb027..0cbec57 100755
--- a/t/t6439-merge-co-error-msgs.sh
+++ b/t/t6439-merge-co-error-msgs.sh
@@ -5,6 +5,7 @@ test_description='unpack-trees error messages'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
@@ -47,6 +48,7 @@ test_expect_success 'untracked files overwritten by merge (fast and non-fast for
export GIT_MERGE_VERBOSITY &&
test_must_fail git merge branch 2>out2
) &&
+ echo "Merge with strategy ${GIT_TEST_MERGE_ALGORITHM:-ort} failed." >>expect &&
test_cmp out2 expect &&
git reset --hard HEAD^
'
diff --git a/t/t6500-gc.sh b/t/t6500-gc.sh
index c202126..43d4017 100755
--- a/t/t6500-gc.sh
+++ b/t/t6500-gc.sh
@@ -11,23 +11,7 @@ test_expect_success 'setup' '
# behavior, make sure we always pack everything to one pack by
# default
git config gc.bigPackThreshold 2g &&
-
- # These are simply values which, when hashed as a blob with a newline,
- # produce a hash where the first byte is 0x17 in their respective
- # algorithms.
- test_oid_cache <<-EOF
- obj1 sha1:263
- obj1 sha256:34
-
- obj2 sha1:410
- obj2 sha256:174
-
- obj3 sha1:523
- obj3 sha256:313
-
- obj4 sha1:790
- obj4 sha256:481
- EOF
+ test_oid_init
'
test_expect_success 'gc empty repository' '
@@ -41,7 +25,7 @@ test_expect_success 'gc does not leave behind pid file' '
test_expect_success 'gc --gobbledegook' '
test_expect_code 129 git gc --nonsense 2>err &&
- test_i18ngrep "[Uu]sage: git gc" err
+ test_grep "[Uu]sage: git gc" err
'
test_expect_success 'gc -h with invalid configuration' '
@@ -52,7 +36,7 @@ test_expect_success 'gc -h with invalid configuration' '
echo "[gc] pruneexpire = CORRUPT" >>.git/config &&
test_expect_code 129 git gc -h >usage 2>&1
) &&
- test_i18ngrep "[Uu]sage" broken/usage
+ test_grep "[Uu]sage" broken/usage
'
test_expect_success 'gc is not aborted due to a stale symref' '
@@ -101,12 +85,12 @@ test_expect_success 'pre-auto-gc hook can stop auto gc' '
EOF
git init pre-auto-gc-hook &&
+ test_hook -C pre-auto-gc-hook pre-auto-gc <<-\EOF &&
+ echo >&2 no gc for you &&
+ exit 1
+ EOF
(
cd pre-auto-gc-hook &&
- write_script ".git/hooks/pre-auto-gc" <<-\EOF &&
- echo >&2 no gc for you &&
- exit 1
- EOF
git config gc.auto 3 &&
git config gc.autoDetach false &&
@@ -114,8 +98,8 @@ test_expect_success 'pre-auto-gc hook can stop auto gc' '
# We need to create two object whose sha1s start with 17
# since this is what git gc counts. As it happens, these
# two blobs will do so.
- test_commit "$(test_oid obj1)" &&
- test_commit "$(test_oid obj2)" &&
+ test_commit "$(test_oid blob17_1)" &&
+ test_commit "$(test_oid blob17_2)" &&
git gc --auto >../out.actual 2>../err.actual
) &&
@@ -128,14 +112,12 @@ test_expect_success 'pre-auto-gc hook can stop auto gc' '
See "git help gc" for manual housekeeping.
EOF
- (
- cd pre-auto-gc-hook &&
- write_script ".git/hooks/pre-auto-gc" <<-\EOF &&
- echo >&2 will gc for you &&
- exit 0
- EOF
- git gc --auto >../out.actual 2>../err.actual
- ) &&
+ test_hook -C pre-auto-gc-hook --clobber pre-auto-gc <<-\EOF &&
+ echo >&2 will gc for you &&
+ exit 0
+ EOF
+
+ git -C pre-auto-gc-hook gc --auto >out.actual 2>err.actual &&
test_must_be_empty out.actual &&
test_cmp err.expect err.actual
@@ -148,16 +130,16 @@ test_expect_success 'auto gc with too many loose objects does not attempt to cre
# We need to create two object whose sha1s start with 17
# since this is what git gc counts. As it happens, these
# two blobs will do so.
- test_commit "$(test_oid obj1)" &&
- test_commit "$(test_oid obj2)" &&
+ test_commit "$(test_oid blob17_1)" &&
+ test_commit "$(test_oid blob17_2)" &&
# Our first gc will create a pack; our second will create a second pack
git gc --auto &&
ls .git/objects/pack/pack-*.pack | sort >existing_packs &&
- test_commit "$(test_oid obj3)" &&
- test_commit "$(test_oid obj4)" &&
+ test_commit "$(test_oid blob17_3)" &&
+ test_commit "$(test_oid blob17_4)" &&
git gc --auto 2>err &&
- test_i18ngrep ! "^warning:" err &&
+ test_grep ! "^warning:" err &&
ls .git/objects/pack/pack-*.pack | sort >post_packs &&
comm -1 -3 existing_packs post_packs >new &&
comm -2 -3 existing_packs post_packs >del &&
@@ -168,15 +150,15 @@ test_expect_success 'auto gc with too many loose objects does not attempt to cre
test_expect_success 'gc --no-quiet' '
GIT_PROGRESS_DELAY=0 git -c gc.writeCommitGraph=true gc --no-quiet >stdout 2>stderr &&
test_must_be_empty stdout &&
- test_i18ngrep "Computing commit graph generation numbers" stderr
+ test_grep "Computing commit graph generation numbers" stderr
'
test_expect_success TTY 'with TTY: gc --no-quiet' '
test_terminal env GIT_PROGRESS_DELAY=0 \
git -c gc.writeCommitGraph=true gc --no-quiet >stdout 2>stderr &&
test_must_be_empty stdout &&
- test_i18ngrep "Enumerating objects" stderr &&
- test_i18ngrep "Computing commit graph generation numbers" stderr
+ test_grep "Enumerating objects" stderr &&
+ test_grep "Computing commit graph generation numbers" stderr
'
test_expect_success 'gc --quiet' '
@@ -204,6 +186,158 @@ test_expect_success 'one of gc.reflogExpire{Unreachable,}=never does not skip "e
grep -E "^trace: (built-in|exec|run_command): git reflog expire --" trace.out
'
+test_expect_success 'gc.repackFilter launches repack with a filter' '
+ git clone --no-local --bare . bare.git &&
+
+ git -C bare.git -c gc.cruftPacks=false gc &&
+ test_stdout_line_count = 1 ls bare.git/objects/pack/*.pack &&
+
+ GIT_TRACE=$(pwd)/trace.out git -C bare.git -c gc.repackFilter=blob:none \
+ -c repack.writeBitmaps=false -c gc.cruftPacks=false gc &&
+ test_stdout_line_count = 2 ls bare.git/objects/pack/*.pack &&
+ grep -E "^trace: (built-in|exec|run_command): git repack .* --filter=blob:none ?.*" trace.out
+'
+
+test_expect_success 'gc.repackFilterTo store filtered out objects' '
+ test_when_finished "rm -rf bare.git filtered.git" &&
+
+ git init --bare filtered.git &&
+ git -C bare.git -c gc.repackFilter=blob:none \
+ -c gc.repackFilterTo=../filtered.git/objects/pack/pack \
+ -c repack.writeBitmaps=false -c gc.cruftPacks=false gc &&
+
+ test_stdout_line_count = 1 ls bare.git/objects/pack/*.pack &&
+ test_stdout_line_count = 1 ls filtered.git/objects/pack/*.pack
+'
+
+prepare_cruft_history () {
+ test_commit base &&
+
+ test_commit --no-tag foo &&
+ test_commit --no-tag bar &&
+ git reset HEAD^^
+}
+
+assert_no_cruft_packs () {
+ find .git/objects/pack -name "*.mtimes" >mtimes &&
+ test_must_be_empty mtimes
+}
+
+for argv in \
+ "gc" \
+ "-c gc.cruftPacks=true gc" \
+ "-c gc.cruftPacks=false gc --cruft"
+do
+ test_expect_success "git $argv generates a cruft pack" '
+ test_when_finished "rm -fr repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ prepare_cruft_history &&
+ git $argv &&
+
+ find .git/objects/pack -name "*.mtimes" >mtimes &&
+ sed -e 's/\.mtimes$/\.pack/g' mtimes >packs &&
+
+ test_file_not_empty packs &&
+ while read pack
+ do
+ test_path_is_file "$pack" || return 1
+ done <packs
+ )
+ '
+done
+
+for argv in \
+ "gc --no-cruft" \
+ "-c gc.cruftPacks=false gc" \
+ "-c gc.cruftPacks=true gc --no-cruft"
+do
+ test_expect_success "git $argv does not generate a cruft pack" '
+ test_when_finished "rm -fr repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ prepare_cruft_history &&
+ git $argv &&
+
+ assert_no_cruft_packs
+ )
+ '
+done
+
+test_expect_success '--keep-largest-pack ignores cruft packs' '
+ test_when_finished "rm -fr repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ # Generate a pack for reachable objects (of which there
+ # are 3), and one for unreachable objects (of which
+ # there are 6).
+ prepare_cruft_history &&
+ git gc --cruft &&
+
+ mtimes="$(find .git/objects/pack -type f -name "pack-*.mtimes")" &&
+ sz="$(test_file_size "${mtimes%.mtimes}.pack")" &&
+
+ # Ensure that the cruft pack gets removed (due to
+ # `--prune=now`) despite it being the largest pack.
+ git -c gc.bigPackThreshold=$sz gc --cruft --prune=now &&
+
+ assert_no_cruft_packs
+ )
+'
+
+test_expect_success 'gc.bigPackThreshold ignores cruft packs' '
+ test_when_finished "rm -fr repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ # Generate a pack for reachable objects (of which there
+ # are 3), and one for unreachable objects (of which
+ # there are 6).
+ prepare_cruft_history &&
+ git gc --cruft &&
+
+ # Ensure that the cruft pack gets removed (due to
+ # `--prune=now`) despite it being the largest pack.
+ git gc --cruft --prune=now --keep-largest-pack &&
+
+ assert_no_cruft_packs
+ )
+'
+
+cruft_max_size_opts="git repack -d -l --cruft --cruft-expiration=2.weeks.ago"
+
+test_expect_success 'setup for --max-cruft-size tests' '
+ git init cruft--max-size &&
+ (
+ cd cruft--max-size &&
+ prepare_cruft_history
+ )
+'
+
+test_expect_success '--max-cruft-size sets appropriate repack options' '
+ GIT_TRACE2_EVENT=$(pwd)/trace2.txt git -C cruft--max-size \
+ gc --cruft --max-cruft-size=1M &&
+ test_subcommand $cruft_max_size_opts --max-cruft-size=1048576 <trace2.txt
+'
+
+test_expect_success 'gc.maxCruftSize sets appropriate repack options' '
+ GIT_TRACE2_EVENT=$(pwd)/trace2.txt \
+ git -C cruft--max-size -c gc.maxCruftSize=2M gc --cruft &&
+ test_subcommand $cruft_max_size_opts --max-cruft-size=2097152 <trace2.txt &&
+
+ GIT_TRACE2_EVENT=$(pwd)/trace2.txt \
+ git -C cruft--max-size -c gc.maxCruftSize=2M gc --cruft \
+ --max-cruft-size=3M &&
+ test_subcommand $cruft_max_size_opts --max-cruft-size=3145728 <trace2.txt
+'
+
run_and_wait_for_auto_gc () {
# We read stdout from gc for the side effect of waiting until the
# background gc process exits, closing its fd 9. Furthermore, the
@@ -222,7 +356,7 @@ test_expect_success 'background auto gc does not run if gc.log is present and re
test_config gc.autodetach true &&
echo fleem >.git/gc.log &&
git gc --auto 2>err &&
- test_i18ngrep "^warning:" err &&
+ test_grep "^warning:" err &&
test_config gc.logexpiry 5.days &&
test-tool chmtime =-345600 .git/gc.log &&
git gc --auto &&
diff --git a/t/t6501-freshen-objects.sh b/t/t6501-freshen-objects.sh
index 1066245..4521508 100755
--- a/t/t6501-freshen-objects.sh
+++ b/t/t6501-freshen-objects.sh
@@ -28,6 +28,7 @@ test_description='check pruning of dependent objects'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# We care about reachability, so we do not want to use
@@ -100,7 +101,7 @@ do
'
test_expect_success "simultaneous gc ($title)" '
- git gc --prune=12.hours.ago
+ git gc --no-cruft --prune=12.hours.ago
'
test_expect_success "finish writing out commit ($title)" '
@@ -130,7 +131,7 @@ do
'
test_expect_success "simultaneous gc ($title)" '
- git gc --prune=12.hours.ago
+ git gc --no-cruft --prune=12.hours.ago
'
# tree should have been refreshed by write-tree
@@ -150,8 +151,8 @@ test_expect_success 'do not complain about existing broken links (commit)' '
some message
EOF
commit=$(git hash-object -t commit -w broken-commit) &&
- git gc -q 2>stderr &&
- verbose git cat-file -e $commit &&
+ git gc --no-cruft -q 2>stderr &&
+ git cat-file -e $commit &&
test_must_be_empty stderr
'
@@ -160,7 +161,7 @@ test_expect_success 'do not complain about existing broken links (tree)' '
100644 blob $(test_oid 003) foo
EOF
tree=$(git mktree --missing <broken-tree) &&
- git gc -q 2>stderr &&
+ git gc --no-cruft -q 2>stderr &&
git cat-file -e $tree &&
test_must_be_empty stderr
'
@@ -175,7 +176,7 @@ test_expect_success 'do not complain about existing broken links (tag)' '
this is a broken tag
EOF
tag=$(git hash-object -t tag -w broken-tag) &&
- git gc -q 2>stderr &&
+ git gc --no-cruft -q 2>stderr &&
git cat-file -e $tag &&
test_must_be_empty stderr
'
diff --git a/t/t6600-test-reach.sh b/t/t6600-test-reach.sh
index 3d7a62d..b330945 100755
--- a/t/t6600-test-reach.sh
+++ b/t/t6600-test-reach.sh
@@ -32,7 +32,7 @@ test_expect_success 'setup' '
do
test_commit "1-$i" &&
git branch -f commit-1-$i &&
- git tag -a -m "1-$i" tag-1-$i commit-1-$i
+ git tag -a -m "1-$i" tag-1-$i commit-1-$i || return 1
done &&
for j in $(test_seq 1 9)
do
@@ -46,7 +46,7 @@ test_expect_success 'setup' '
do
git merge commit-$j-$i -m "$x-$i" &&
git branch -f commit-$x-$i &&
- git tag -a -m "$x-$i" tag-$x-$i commit-$x-$i
+ git tag -a -m "$x-$i" tag-$x-$i commit-$x-$i || return 1
done
done &&
git commit-graph write --reachable &&
@@ -443,4 +443,173 @@ test_expect_success 'get_reachable_subset:none' '
test_all_modes get_reachable_subset
'
+test_expect_success 'for-each-ref ahead-behind:linear' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-1-3
+ refs/heads/commit-1-5
+ refs/heads/commit-1-8
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1 0 8
+ refs/heads/commit-1-3 0 6
+ refs/heads/commit-1-5 0 4
+ refs/heads/commit-1-8 0 1
+ EOF
+ run_all_modes git for-each-ref \
+ --format="%(refname) %(ahead-behind:commit-1-9)" --stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:all' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-2-4
+ refs/heads/commit-4-2
+ refs/heads/commit-4-4
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1 0 24
+ refs/heads/commit-2-4 0 17
+ refs/heads/commit-4-2 0 17
+ refs/heads/commit-4-4 0 9
+ EOF
+ run_all_modes git for-each-ref \
+ --format="%(refname) %(ahead-behind:commit-5-5)" --stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:some' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-5-3
+ refs/heads/commit-4-8
+ refs/heads/commit-9-9
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1 0 53
+ refs/heads/commit-4-8 8 30
+ refs/heads/commit-5-3 0 39
+ refs/heads/commit-9-9 27 0
+ EOF
+ run_all_modes git for-each-ref \
+ --format="%(refname) %(ahead-behind:commit-9-6)" --stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:some, multibase' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-5-3
+ refs/heads/commit-7-8
+ refs/heads/commit-4-8
+ refs/heads/commit-9-9
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1 0 53 0 53
+ refs/heads/commit-4-8 8 30 0 22
+ refs/heads/commit-5-3 0 39 0 39
+ refs/heads/commit-7-8 14 12 8 6
+ refs/heads/commit-9-9 27 0 27 0
+ EOF
+ run_all_modes git for-each-ref \
+ --format="%(refname) %(ahead-behind:commit-9-6) %(ahead-behind:commit-6-9)" \
+ --stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:none' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-7-5
+ refs/heads/commit-4-8
+ refs/heads/commit-9-9
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-4-8 16 16
+ refs/heads/commit-7-5 7 4
+ refs/heads/commit-9-9 49 0
+ EOF
+ run_all_modes git for-each-ref \
+ --format="%(refname) %(ahead-behind:commit-8-4)" --stdin
+'
+
+test_expect_success 'for-each-ref merged:linear' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-1-3
+ refs/heads/commit-1-5
+ refs/heads/commit-1-8
+ refs/heads/commit-2-1
+ refs/heads/commit-5-1
+ refs/heads/commit-9-1
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-1-3
+ refs/heads/commit-1-5
+ refs/heads/commit-1-8
+ EOF
+ run_all_modes git for-each-ref --merged=commit-1-9 \
+ --format="%(refname)" --stdin
+'
+
+test_expect_success 'for-each-ref merged:all' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-2-4
+ refs/heads/commit-4-2
+ refs/heads/commit-4-4
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-2-4
+ refs/heads/commit-4-2
+ refs/heads/commit-4-4
+ EOF
+ run_all_modes git for-each-ref --merged=commit-5-5 \
+ --format="%(refname)" --stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:some' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-5-3
+ refs/heads/commit-4-8
+ refs/heads/commit-9-9
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-5-3
+ EOF
+ run_all_modes git for-each-ref --merged=commit-9-6 \
+ --format="%(refname)" --stdin
+'
+
+test_expect_success 'for-each-ref merged:some, multibase' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-5-3
+ refs/heads/commit-7-8
+ refs/heads/commit-4-8
+ refs/heads/commit-9-9
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-4-8
+ refs/heads/commit-5-3
+ EOF
+ run_all_modes git for-each-ref \
+ --merged=commit-5-8 \
+ --merged=commit-8-5 \
+ --format="%(refname)" \
+ --stdin
+'
+
+test_expect_success 'for-each-ref merged:none' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-7-5
+ refs/heads/commit-4-8
+ refs/heads/commit-9-9
+ EOF
+ >expect &&
+ run_all_modes git for-each-ref --merged=commit-8-4 \
+ --format="%(refname)" --stdin
+'
+
test_done
diff --git a/t/t6700-tree-depth.sh b/t/t6700-tree-depth.sh
new file mode 100755
index 0000000..9e70a7c
--- /dev/null
+++ b/t/t6700-tree-depth.sh
@@ -0,0 +1,95 @@
+#!/bin/sh
+
+test_description='handling of deep trees in various commands'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+# We'll test against two depths here: a small one that will let us check the
+# behavior of the config setting easily, and a large one that should be
+# forbidden by default. Testing the default depth will let us know whether our
+# default is enough to prevent segfaults on systems that run the tests.
+small_depth=50
+big_depth=4100
+
+small_ok="-c core.maxtreedepth=$small_depth"
+small_no="-c core.maxtreedepth=$((small_depth-1))"
+
+# usage: mkdeep <name> <depth>
+# Create a tag <name> containing a file whose path has depth <depth>.
+#
+# We'll use fast-import here for two reasons:
+#
+# 1. It's faster than creating $big_depth tree objects.
+#
+# 2. As we tighten tree limits, it's more likely to allow large sizes
+# than trying to stuff a deep path into the index.
+mkdeep () {
+ {
+ echo "commit refs/tags/$1" &&
+ echo "committer foo <foo@example.com> 1234 -0000" &&
+ echo "data <<EOF" &&
+ echo "the commit message" &&
+ echo "EOF" &&
+
+ printf 'M 100644 inline ' &&
+ i=0 &&
+ while test $i -lt $2
+ do
+ printf 'a/'
+ i=$((i+1))
+ done &&
+ echo "file" &&
+
+ echo "data <<EOF" &&
+ echo "the file contents" &&
+ echo "EOF" &&
+ echo
+ } | git fast-import
+}
+
+test_expect_success 'create small tree' '
+ mkdeep small $small_depth
+'
+
+test_expect_success 'create big tree' '
+ mkdeep big $big_depth
+'
+
+test_expect_success 'limit recursion of git-archive' '
+ git $small_ok archive small >/dev/null &&
+ test_must_fail git $small_no archive small >/dev/null
+'
+
+test_expect_success 'default limit for git-archive fails gracefully' '
+ test_must_fail git archive big >/dev/null
+'
+
+test_expect_success 'limit recursion of ls-tree -r' '
+ git $small_ok ls-tree -r small &&
+ test_must_fail git $small_no ls-tree -r small
+'
+
+test_expect_success 'default limit for ls-tree fails gracefully' '
+ test_must_fail git ls-tree -r big >/dev/null
+'
+
+test_expect_success 'limit recursion of rev-list --objects' '
+ git $small_ok rev-list --objects small >/dev/null &&
+ test_must_fail git $small_no rev-list --objects small >/dev/null
+'
+
+test_expect_success 'default limit for rev-list fails gracefully' '
+ test_must_fail git rev-list --objects big >/dev/null
+'
+
+test_expect_success 'limit recursion of diff-tree -r' '
+ git $small_ok diff-tree -r $EMPTY_TREE small &&
+ test_must_fail git $small_no diff-tree -r $EMPTY_TREE small
+'
+
+test_expect_success 'default limit for diff-tree fails gracefully' '
+ test_must_fail git diff-tree -r $EMPTY_TREE big
+'
+
+test_done
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index 25bb9bb..879a6dc 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -2,10 +2,34 @@
test_description='git mv in subdirs'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-diff-data.sh
+
+index_at_path () {
+ git ls-files --format='%(objectmode) %(objectname) %(stage)' "$@"
+}
+
+test_expect_success 'mv -f refreshes updated index entry' '
+ echo test >bar &&
+ git add bar &&
+ git commit -m test &&
+
+ echo foo >foo &&
+ git add foo &&
+
+ # Wait one second to ensure ctime of rename will differ from original
+ # file creation ctime.
+ sleep 1 &&
+ git mv -f foo bar &&
+ git reset --merge HEAD &&
+
+ # Verify the index has been reset
+ git diff-files >out &&
+ test_must_be_empty out
+'
test_expect_success 'prepare reference tree' '
mkdir path0 path1 &&
- cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
+ COPYING_test_data >path0/COPYING &&
git add path0/COPYING &&
git commit -m add -a
'
@@ -40,8 +64,8 @@ test_expect_success 'checking the commit' '
test_expect_success 'mv --dry-run does not move file' '
git mv -n path0/COPYING MOVED &&
- test -f path0/COPYING &&
- test ! -f MOVED
+ test_path_is_file path0/COPYING &&
+ test_path_is_missing MOVED
'
test_expect_success 'checking -k on non-existing file' '
@@ -51,25 +75,25 @@ test_expect_success 'checking -k on non-existing file' '
test_expect_success 'checking -k on untracked file' '
>untracked1 &&
git mv -k untracked1 path0 &&
- test -f untracked1 &&
- test ! -f path0/untracked1
+ test_path_is_file untracked1 &&
+ test_path_is_missing path0/untracked1
'
test_expect_success 'checking -k on multiple untracked files' '
>untracked2 &&
git mv -k untracked1 untracked2 path0 &&
- test -f untracked1 &&
- test -f untracked2 &&
- test ! -f path0/untracked1 &&
- test ! -f path0/untracked2
+ test_path_is_file untracked1 &&
+ test_path_is_file untracked2 &&
+ test_path_is_missing path0/untracked1 &&
+ test_path_is_missing path0/untracked2
'
test_expect_success 'checking -f on untracked file with existing target' '
>path0/untracked1 &&
test_must_fail git mv -f untracked1 path0 &&
- test ! -f .git/index.lock &&
- test -f untracked1 &&
- test -f path0/untracked1
+ test_path_is_missing .git/index.lock &&
+ test_path_is_file untracked1 &&
+ test_path_is_file path0/untracked1
'
# clean up the mess in case bad things happen
@@ -107,7 +131,7 @@ test_expect_success 'clean up' '
'
test_expect_success 'adding another file' '
- cp "$TEST_DIRECTORY"/../README.md path0/README &&
+ COPYING_test_data | tr A-Za-z N-ZA-Mn-za-m >path0/README &&
git add path0/README &&
git commit -m add2 -a
'
@@ -150,6 +174,13 @@ test_expect_success 'do not move directory over existing directory' '
test_must_fail git mv path2 path0
'
+test_expect_success 'rename directory to non-existing directory' '
+ mkdir dir-a &&
+ >dir-a/f &&
+ git add dir-a &&
+ git mv dir-a non-existing-dir
+'
+
test_expect_success 'move into "."' '
git mv path1/path2/ .
'
@@ -167,7 +198,8 @@ test_expect_success "Michael Cassar's test case" '
git mv papers/unsorted/Thesis.pdf papers/all-papers/moo-blah.pdf &&
T=$(git write-tree) &&
- git ls-tree -r $T | verbose grep partA/outline.txt
+ git ls-tree -r $T >out &&
+ grep partA/outline.txt out
'
rm -fr papers partA path?
@@ -195,8 +227,8 @@ test_expect_success 'absolute pathname' '
git add sub/file &&
git mv sub "$(pwd)/in" &&
- ! test -d sub &&
- test -d in &&
+ test_path_is_missing sub &&
+ test_path_is_dir in &&
git ls-files --error-unmatch in/file
)
'
@@ -214,8 +246,8 @@ test_expect_success 'absolute pathname outside should fail' '
git add sub/file &&
test_must_fail git mv sub "$out/out" &&
- test -d sub &&
- ! test -d ../in &&
+ test_path_is_dir sub &&
+ test_path_is_missing ../in &&
git ls-files --error-unmatch sub/file
)
'
@@ -240,12 +272,12 @@ test_expect_success 'git mv should not change sha1 of moved cache entry' '
git init &&
echo 1 >dirty &&
git add dirty &&
- entry="$(git ls-files --stage dirty | cut -f 1)" &&
+ entry="$(index_at_path dirty)" &&
git mv dirty dirty2 &&
- test "$entry" = "$(git ls-files --stage dirty2 | cut -f 1)" &&
+ test "$entry" = "$(index_at_path dirty2)" &&
echo 2 >dirty2 &&
git mv dirty2 dirty &&
- test "$entry" = "$(git ls-files --stage dirty | cut -f 1)"
+ test "$entry" = "$(index_at_path dirty)"
'
rm -f dirty dirty2
@@ -264,7 +296,7 @@ test_expect_success 'git mv error on conflicted file' '
EOF
test_must_fail git mv conflict newname 2>actual &&
- test_i18ngrep "conflicted" actual
+ test_grep "conflicted" actual
'
test_expect_success 'git mv should overwrite symlink to a file' '
@@ -275,8 +307,8 @@ test_expect_success 'git mv should overwrite symlink to a file' '
git add moved &&
test_must_fail git mv moved symlink &&
git mv -f moved symlink &&
- ! test -e moved &&
- test -f symlink &&
+ test_path_is_missing moved &&
+ test_path_is_file symlink &&
test "$(cat symlink)" = 1 &&
git update-index --refresh &&
git diff-files --quiet
@@ -292,18 +324,19 @@ test_expect_success 'git mv should overwrite file with a symlink' '
git add moved &&
test_must_fail git mv symlink moved &&
git mv -f symlink moved &&
- ! test -e symlink &&
+ test_path_is_missing symlink &&
git update-index --refresh &&
git diff-files --quiet
'
test_expect_success SYMLINKS 'check moved symlink' '
- test -h moved
+ test_path_is_symlink moved
'
rm -f moved symlink
test_expect_success 'setup submodule' '
+ test_config_global protocol.file.allow always &&
git commit -m initial &&
git reset --hard &&
git submodule add ./. sub &&
@@ -321,7 +354,7 @@ test_expect_success 'git mv cannot move a submodule in a file' '
'
test_expect_success 'git mv moves a submodule with a .git directory and no .gitmodules' '
- entry="$(git ls-files --stage sub | cut -f 1)" &&
+ entry="$(index_at_path sub)" &&
git rm .gitmodules &&
(
cd sub &&
@@ -331,8 +364,8 @@ test_expect_success 'git mv moves a submodule with a .git directory and no .gitm
) &&
mkdir mod &&
git mv sub mod/sub &&
- ! test -e sub &&
- test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+ test_path_is_missing sub &&
+ test "$entry" = "$(index_at_path mod/sub)" &&
git -C mod/sub status &&
git update-index --refresh &&
git diff-files --quiet
@@ -342,7 +375,7 @@ test_expect_success 'git mv moves a submodule with a .git directory and .gitmodu
rm -rf mod &&
git reset --hard &&
git submodule update &&
- entry="$(git ls-files --stage sub | cut -f 1)" &&
+ entry="$(index_at_path sub)" &&
(
cd sub &&
rm -f .git &&
@@ -351,8 +384,8 @@ test_expect_success 'git mv moves a submodule with a .git directory and .gitmodu
) &&
mkdir mod &&
git mv sub mod/sub &&
- ! test -e sub &&
- test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+ test_path_is_missing sub &&
+ test "$entry" = "$(index_at_path mod/sub)" &&
git -C mod/sub status &&
echo mod/sub >expected &&
git config -f .gitmodules submodule.sub.path >actual &&
@@ -365,11 +398,11 @@ test_expect_success 'git mv moves a submodule with gitfile' '
rm -rf mod &&
git reset --hard &&
git submodule update &&
- entry="$(git ls-files --stage sub | cut -f 1)" &&
+ entry="$(index_at_path sub)" &&
mkdir mod &&
git -C mod mv ../sub/ . &&
- ! test -e sub &&
- test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+ test_path_is_missing sub &&
+ test "$entry" = "$(index_at_path mod/sub)" &&
git -C mod/sub status &&
echo mod/sub >expected &&
git config -f .gitmodules submodule.sub.path >actual &&
@@ -383,12 +416,12 @@ test_expect_success 'mv does not complain when no .gitmodules file is found' '
git reset --hard &&
git submodule update &&
git rm .gitmodules &&
- entry="$(git ls-files --stage sub | cut -f 1)" &&
+ entry="$(index_at_path sub)" &&
mkdir mod &&
git mv sub mod/sub 2>actual.err &&
test_must_be_empty actual.err &&
- ! test -e sub &&
- test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+ test_path_is_missing sub &&
+ test "$entry" = "$(index_at_path mod/sub)" &&
git -C mod/sub status &&
git update-index --refresh &&
git diff-files --quiet
@@ -399,17 +432,17 @@ test_expect_success 'mv will error out on a modified .gitmodules file unless sta
git reset --hard &&
git submodule update &&
git config -f .gitmodules foo.bar true &&
- entry="$(git ls-files --stage sub | cut -f 1)" &&
+ entry="$(index_at_path sub)" &&
mkdir mod &&
test_must_fail git mv sub mod/sub 2>actual.err &&
- test -s actual.err &&
- test -e sub &&
+ test_file_not_empty actual.err &&
+ test_path_exists sub &&
git diff-files --quiet -- sub &&
git add .gitmodules &&
git mv sub mod/sub 2>actual.err &&
test_must_be_empty actual.err &&
- ! test -e sub &&
- test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+ test_path_is_missing sub &&
+ test "$entry" = "$(index_at_path mod/sub)" &&
git -C mod/sub status &&
git update-index --refresh &&
git diff-files --quiet
@@ -421,13 +454,13 @@ test_expect_success 'mv issues a warning when section is not found in .gitmodule
git submodule update &&
git config -f .gitmodules --remove-section submodule.sub &&
git add .gitmodules &&
- entry="$(git ls-files --stage sub | cut -f 1)" &&
+ entry="$(index_at_path sub)" &&
echo "warning: Could not find section in .gitmodules where path=sub" >expect.err &&
mkdir mod &&
git mv sub mod/sub 2>actual.err &&
test_cmp expect.err actual.err &&
- ! test -e sub &&
- test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+ test_path_is_missing sub &&
+ test "$entry" = "$(index_at_path mod/sub)" &&
git -C mod/sub status &&
git update-index --refresh &&
git diff-files --quiet
@@ -439,7 +472,7 @@ test_expect_success 'mv --dry-run does not touch the submodule or .gitmodules' '
git submodule update &&
mkdir mod &&
git mv -n sub mod/sub 2>actual.err &&
- test -f sub/.git &&
+ test_path_is_file sub/.git &&
git diff-index --exit-code HEAD &&
git update-index --refresh &&
git diff-files --quiet -- sub .gitmodules
@@ -449,14 +482,14 @@ test_expect_success 'checking out a commit before submodule moved needs manual u
git mv sub sub2 &&
git commit -m "moved sub to sub2" &&
git checkout -q HEAD^ 2>actual &&
- test_i18ngrep "^warning: unable to rmdir '\''sub2'\'':" actual &&
+ test_grep "^warning: unable to rmdir '\''sub2'\'':" actual &&
git status -s sub2 >actual &&
echo "?? sub2/" >expected &&
test_cmp expected actual &&
- ! test -f sub/.git &&
- test -f sub2/.git &&
+ test_path_is_missing sub/.git &&
+ test_path_is_file sub2/.git &&
git submodule update &&
- test -f sub/.git &&
+ test_path_is_file sub/.git &&
rm -rf sub2 &&
git diff-index --exit-code HEAD &&
git update-index --refresh &&
@@ -489,6 +522,7 @@ test_expect_success 'moving a submodule in nested directories' '
'
test_expect_success 'moving nested submodules' '
+ test_config_global protocol.file.allow always &&
git commit -am "cleanup commit" &&
mkdir sub_nested_nested &&
(
diff --git a/t/t7002-mv-sparse-checkout.sh b/t/t7002-mv-sparse-checkout.sh
new file mode 100755
index 0000000..26582ae
--- /dev/null
+++ b/t/t7002-mv-sparse-checkout.sh
@@ -0,0 +1,517 @@
+#!/bin/sh
+
+test_description='git mv in sparse working trees'
+
+. ./test-lib.sh
+
+setup_sparse_checkout () {
+ mkdir folder1 &&
+ touch folder1/file1 &&
+ git add folder1 &&
+ git sparse-checkout set --cone sub
+}
+
+cleanup_sparse_checkout () {
+ git sparse-checkout disable &&
+ git reset --hard
+}
+
+test_expect_success 'setup' "
+ mkdir -p sub/dir sub/dir2 &&
+ touch a b c sub/d sub/dir/e sub/dir2/e &&
+ git add -A &&
+ git commit -m files &&
+
+ cat >sparse_error_header <<-EOF &&
+ The following paths and/or pathspecs matched paths that exist
+ outside of your sparse-checkout definition, so will not be
+ updated in the index:
+ EOF
+
+ cat >sparse_hint <<-EOF &&
+ hint: If you intend to update such entries, try one of the following:
+ hint: * Use the --sparse option.
+ hint: * Disable or modify the sparsity rules.
+ hint: Disable this message with \"git config advice.updateSparsePath false\"
+ EOF
+
+ cat >dirty_error_header <<-EOF &&
+ The following paths have been moved outside the
+ sparse-checkout definition but are not sparse due to local
+ modifications.
+ EOF
+
+ cat >dirty_hint <<-EOF
+ hint: To correct the sparsity of these paths, do the following:
+ hint: * Use \"git add --sparse <paths>\" to update the index
+ hint: * Use \"git sparse-checkout reapply\" to apply the sparsity rules
+ hint: Disable this message with \"git config advice.updateSparsePath false\"
+ EOF
+"
+
+test_expect_success 'mv refuses to move sparse-to-sparse' '
+ test_when_finished rm -f e &&
+ git reset --hard &&
+ git sparse-checkout set --no-cone a &&
+ touch b &&
+ test_must_fail git mv b e 2>stderr &&
+ cat sparse_error_header >expect &&
+ echo b >>expect &&
+ echo e >>expect &&
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+ git mv --sparse b e 2>stderr &&
+ test_must_be_empty stderr
+'
+
+test_expect_success 'mv refuses to move sparse-to-sparse, ignores failure' '
+ test_when_finished rm -f b c e &&
+ git reset --hard &&
+ git sparse-checkout set a &&
+
+ # tracked-to-untracked
+ touch b &&
+ git mv -k b e 2>stderr &&
+ test_path_exists b &&
+ test_path_is_missing e &&
+ cat sparse_error_header >expect &&
+ echo b >>expect &&
+ echo e >>expect &&
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+
+ git mv --sparse b e 2>stderr &&
+ test_must_be_empty stderr &&
+ test_path_is_missing b &&
+ test_path_exists e &&
+
+ # tracked-to-tracked
+ git reset --hard &&
+ touch b &&
+ git mv -k b c 2>stderr &&
+ test_path_exists b &&
+ test_path_is_missing c &&
+ cat sparse_error_header >expect &&
+ echo b >>expect &&
+ echo c >>expect &&
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+
+ git mv --sparse b c 2>stderr &&
+ test_must_be_empty stderr &&
+ test_path_is_missing b &&
+ test_path_exists c
+'
+
+test_expect_success 'mv refuses to move non-sparse-to-sparse' '
+ test_when_finished rm -f b c e &&
+ git reset --hard &&
+ git sparse-checkout set a &&
+
+ # tracked-to-untracked
+ test_must_fail git mv a e 2>stderr &&
+ test_path_exists a &&
+ test_path_is_missing e &&
+ cat sparse_error_header >expect &&
+ echo e >>expect &&
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+ git mv --sparse a e 2>stderr &&
+ test_must_be_empty stderr &&
+ test_path_is_missing a &&
+ test_path_exists e &&
+
+ # tracked-to-tracked
+ rm e &&
+ git reset --hard &&
+ test_must_fail git mv a c 2>stderr &&
+ test_path_exists a &&
+ test_path_is_missing c &&
+ cat sparse_error_header >expect &&
+ echo c >>expect &&
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+ git mv --sparse a c 2>stderr &&
+ test_must_be_empty stderr &&
+ test_path_is_missing a &&
+ test_path_exists c
+'
+
+test_expect_success 'mv refuses to move sparse-to-non-sparse' '
+ test_when_finished rm -f b c e &&
+ git reset --hard &&
+ git sparse-checkout set a e &&
+
+ # tracked-to-untracked
+ touch b &&
+ test_must_fail git mv b e 2>stderr &&
+ cat sparse_error_header >expect &&
+ echo b >>expect &&
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+ git mv --sparse b e 2>stderr &&
+ test_must_be_empty stderr
+'
+
+test_expect_success 'recursive mv refuses to move (possible) sparse' '
+ test_when_finished rm -rf b c e sub2 &&
+ git reset --hard &&
+ # Without cone mode, "sub" and "sub2" do not match
+ git sparse-checkout set sub/dir sub2/dir &&
+
+ # Add contained contents to ensure we avoid non-existence errors
+ mkdir sub/dir2 &&
+ touch sub/d sub/dir2/e &&
+
+ test_must_fail git mv sub sub2 2>stderr &&
+ cat sparse_error_header >expect &&
+ cat >>expect <<-\EOF &&
+ sub/d
+ sub2/d
+ sub/dir2/e
+ sub2/dir2/e
+ EOF
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+ git mv --sparse sub sub2 2>stderr &&
+ test_must_be_empty stderr &&
+ git commit -m "moved sub to sub2" &&
+ git rev-parse HEAD~1:sub >expect &&
+ git rev-parse HEAD:sub2 >actual &&
+ test_cmp expect actual &&
+ git reset --hard HEAD~1
+'
+
+test_expect_success 'recursive mv refuses to move sparse' '
+ git reset --hard &&
+ # Use cone mode so "sub/" matches the sparse-checkout patterns
+ git sparse-checkout init --cone &&
+ git sparse-checkout set sub/dir sub2/dir &&
+
+ # Add contained contents to ensure we avoid non-existence errors
+ mkdir sub/dir2 &&
+ touch sub/dir2/e &&
+
+ test_must_fail git mv sub sub2 2>stderr &&
+ cat sparse_error_header >expect &&
+ cat >>expect <<-\EOF &&
+ sub/dir2/e
+ sub2/dir2/e
+ EOF
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+ git mv --sparse sub sub2 2>stderr &&
+ test_must_be_empty stderr &&
+ git commit -m "moved sub to sub2" &&
+ git rev-parse HEAD~1:sub >expect &&
+ git rev-parse HEAD:sub2 >actual &&
+ test_cmp expect actual &&
+ git reset --hard HEAD~1
+'
+
+test_expect_success 'can move files to non-sparse dir' '
+ git reset --hard &&
+ git sparse-checkout init --no-cone &&
+ git sparse-checkout set a b c w !/x y/ &&
+ mkdir -p w x/y &&
+
+ git mv a w/new-a 2>stderr &&
+ git mv b x/y/new-b 2>stderr &&
+ test_must_be_empty stderr
+'
+
+test_expect_success 'refuse to move file to non-skip-worktree sparse path' '
+ test_when_finished "cleanup_sparse_checkout" &&
+ git reset --hard &&
+ git sparse-checkout init --no-cone &&
+ git sparse-checkout set a !/x y/ !x/y/z &&
+ mkdir -p x/y/z &&
+
+ test_must_fail git mv a x/y/z/new-a 2>stderr &&
+ echo x/y/z/new-a | cat sparse_error_header - sparse_hint >expect &&
+ test_cmp expect stderr
+'
+
+test_expect_success 'refuse to move out-of-cone directory without --sparse' '
+ test_when_finished "cleanup_sparse_checkout" &&
+ setup_sparse_checkout &&
+
+ test_must_fail git mv folder1 sub 2>stderr &&
+ cat sparse_error_header >expect &&
+ echo folder1/file1 >>expect &&
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr
+'
+
+test_expect_success 'can move out-of-cone directory with --sparse' '
+ test_when_finished "cleanup_sparse_checkout" &&
+ setup_sparse_checkout &&
+
+ git mv --sparse folder1 sub 2>stderr &&
+ test_must_be_empty stderr &&
+
+ test_path_is_dir sub/folder1 &&
+ test_path_is_file sub/folder1/file1
+'
+
+test_expect_success 'refuse to move out-of-cone file without --sparse' '
+ test_when_finished "cleanup_sparse_checkout" &&
+ setup_sparse_checkout &&
+
+ test_must_fail git mv folder1/file1 sub 2>stderr &&
+ cat sparse_error_header >expect &&
+ echo folder1/file1 >>expect &&
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr
+'
+
+test_expect_success 'can move out-of-cone file with --sparse' '
+ test_when_finished "cleanup_sparse_checkout" &&
+ setup_sparse_checkout &&
+
+ git mv --sparse folder1/file1 sub 2>stderr &&
+ test_must_be_empty stderr &&
+
+ test_path_is_file sub/file1
+'
+
+test_expect_success 'refuse to move sparse file to existing destination' '
+ test_when_finished "cleanup_sparse_checkout" &&
+ mkdir folder1 &&
+ touch folder1/file1 &&
+ touch sub/file1 &&
+ git add folder1 sub/file1 &&
+ git sparse-checkout set --cone sub &&
+
+ test_must_fail git mv --sparse folder1/file1 sub 2>stderr &&
+ echo "fatal: destination exists, source=folder1/file1, destination=sub/file1" >expect &&
+ test_cmp expect stderr
+'
+
+test_expect_success 'move sparse file to existing destination with --force and --sparse' '
+ test_when_finished "cleanup_sparse_checkout" &&
+ mkdir folder1 &&
+ touch folder1/file1 &&
+ touch sub/file1 &&
+ echo "overwrite" >folder1/file1 &&
+ git add folder1 sub/file1 &&
+ git sparse-checkout set --cone sub &&
+
+ git mv --sparse --force folder1/file1 sub 2>stderr &&
+ test_must_be_empty stderr &&
+ echo "overwrite" >expect &&
+ test_cmp expect sub/file1
+'
+
+test_expect_success 'move clean path from in-cone to out-of-cone' '
+ test_when_finished "cleanup_sparse_checkout" &&
+ setup_sparse_checkout &&
+
+ test_must_fail git mv sub/d folder1 2>stderr &&
+ cat sparse_error_header >expect &&
+ echo "folder1/d" >>expect &&
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+
+ git mv --sparse sub/d folder1 2>stderr &&
+ test_must_be_empty stderr &&
+
+ test_path_is_missing sub/d &&
+ test_path_is_missing folder1/d &&
+ git ls-files -t >actual &&
+ ! grep "^H sub/d\$" actual &&
+ grep "S folder1/d" actual
+'
+
+test_expect_success 'move clean path from in-cone to out-of-cone overwrite' '
+ test_when_finished "cleanup_sparse_checkout" &&
+ setup_sparse_checkout &&
+ echo "sub/file1 overwrite" >sub/file1 &&
+ git add sub/file1 &&
+
+ test_must_fail git mv sub/file1 folder1 2>stderr &&
+ cat sparse_error_header >expect &&
+ echo "folder1/file1" >>expect &&
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+
+ test_must_fail git mv --sparse sub/file1 folder1 2>stderr &&
+ echo "fatal: destination exists in the index, source=sub/file1, destination=folder1/file1" \
+ >expect &&
+ test_cmp expect stderr &&
+
+ git mv --sparse -f sub/file1 folder1 2>stderr &&
+ test_must_be_empty stderr &&
+
+ test_path_is_missing sub/file1 &&
+ test_path_is_missing folder1/file1 &&
+ git ls-files -t >actual &&
+ ! grep "H sub/file1" actual &&
+ grep "S folder1/file1" actual &&
+
+ # compare file content before move and after move
+ echo "sub/file1 overwrite" >expect &&
+ git ls-files -s -- folder1/file1 | awk "{print \$2}" >oid &&
+ git cat-file blob $(cat oid) >actual &&
+ test_cmp expect actual
+'
+
+# This test is testing the same behavior as the
+# "move clean path from in-cone to out-of-cone overwrite" above.
+# The only difference is the <destination> changes from "folder1" to "folder1/file1"
+test_expect_success 'move clean path from in-cone to out-of-cone file overwrite' '
+ test_when_finished "cleanup_sparse_checkout" &&
+ setup_sparse_checkout &&
+ echo "sub/file1 overwrite" >sub/file1 &&
+ git add sub/file1 &&
+
+ test_must_fail git mv sub/file1 folder1/file1 2>stderr &&
+ cat sparse_error_header >expect &&
+ echo "folder1/file1" >>expect &&
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+
+ test_must_fail git mv --sparse sub/file1 folder1/file1 2>stderr &&
+ echo "fatal: destination exists in the index, source=sub/file1, destination=folder1/file1" \
+ >expect &&
+ test_cmp expect stderr &&
+
+ git mv --sparse -f sub/file1 folder1/file1 2>stderr &&
+ test_must_be_empty stderr &&
+
+ test_path_is_missing sub/file1 &&
+ test_path_is_missing folder1/file1 &&
+ git ls-files -t >actual &&
+ ! grep "H sub/file1" actual &&
+ grep "S folder1/file1" actual &&
+
+ # compare file content before move and after move
+ echo "sub/file1 overwrite" >expect &&
+ git ls-files -s -- folder1/file1 | awk "{print \$2}" >oid &&
+ git cat-file blob $(cat oid) >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'move directory with one of the files overwrite' '
+ test_when_finished "cleanup_sparse_checkout" &&
+ mkdir -p folder1/dir &&
+ touch folder1/dir/file1 &&
+ git add folder1 &&
+ git sparse-checkout set --cone sub &&
+
+ echo test >sub/dir/file1 &&
+ git add sub/dir/file1 &&
+
+ test_must_fail git mv sub/dir folder1 2>stderr &&
+ cat sparse_error_header >expect &&
+ echo "folder1/dir/e" >>expect &&
+ echo "folder1/dir/file1" >>expect &&
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+
+ test_must_fail git mv --sparse sub/dir folder1 2>stderr &&
+ echo "fatal: destination exists in the index, source=sub/dir/file1, destination=folder1/dir/file1" \
+ >expect &&
+ test_cmp expect stderr &&
+
+ git mv --sparse -f sub/dir folder1 2>stderr &&
+ test_must_be_empty stderr &&
+
+ test_path_is_missing sub/dir/file1 &&
+ test_path_is_missing sub/dir/e &&
+ test_path_is_missing folder1/file1 &&
+ git ls-files -t >actual &&
+ ! grep "H sub/dir/file1" actual &&
+ ! grep "H sub/dir/e" actual &&
+ grep "S folder1/dir/file1" actual &&
+
+ # compare file content before move and after move
+ echo test >expect &&
+ git ls-files -s -- folder1/dir/file1 | awk "{print \$2}" >oid &&
+ git cat-file blob $(cat oid) >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'move dirty path from in-cone to out-of-cone' '
+ test_when_finished "cleanup_sparse_checkout" &&
+ setup_sparse_checkout &&
+ echo "modified" >>sub/d &&
+
+ test_must_fail git mv sub/d folder1 2>stderr &&
+ cat sparse_error_header >expect &&
+ echo "folder1/d" >>expect &&
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+
+ git mv --sparse sub/d folder1 2>stderr &&
+ cat dirty_error_header >expect &&
+ echo "folder1/d" >>expect &&
+ cat dirty_hint >>expect &&
+ test_cmp expect stderr &&
+
+ test_path_is_missing sub/d &&
+ test_path_is_file folder1/d &&
+ git ls-files -t >actual &&
+ ! grep "^H sub/d\$" actual &&
+ grep "H folder1/d" actual
+'
+
+test_expect_success 'move dir from in-cone to out-of-cone' '
+ test_when_finished "cleanup_sparse_checkout" &&
+ setup_sparse_checkout &&
+ mkdir sub/dir/deep &&
+
+ test_must_fail git mv sub/dir folder1 2>stderr &&
+ cat sparse_error_header >expect &&
+ echo "folder1/dir/e" >>expect &&
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+
+ git mv --sparse sub/dir folder1 2>stderr &&
+ test_must_be_empty stderr &&
+
+ test_path_is_missing sub/dir &&
+ test_path_is_missing folder1 &&
+ git ls-files -t >actual &&
+ ! grep "H sub/dir/e" actual &&
+ grep "S folder1/dir/e" actual
+'
+
+test_expect_success 'move partially-dirty dir from in-cone to out-of-cone' '
+ test_when_finished "cleanup_sparse_checkout" &&
+ setup_sparse_checkout &&
+ mkdir sub/dir/deep &&
+ touch sub/dir/e2 sub/dir/e3 &&
+ git add sub/dir/e2 sub/dir/e3 &&
+ echo "modified" >>sub/dir/e2 &&
+ echo "modified" >>sub/dir/e3 &&
+
+ test_must_fail git mv sub/dir folder1 2>stderr &&
+ cat sparse_error_header >expect &&
+ echo "folder1/dir/e" >>expect &&
+ echo "folder1/dir/e2" >>expect &&
+ echo "folder1/dir/e3" >>expect &&
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+
+ git mv --sparse sub/dir folder1 2>stderr &&
+ cat dirty_error_header >expect &&
+ echo "folder1/dir/e2" >>expect &&
+ echo "folder1/dir/e3" >>expect &&
+ cat dirty_hint >>expect &&
+ test_cmp expect stderr &&
+
+ test_path_is_missing sub/dir &&
+ test_path_is_missing folder1/dir/e &&
+ test_path_is_file folder1/dir/e2 &&
+ test_path_is_file folder1/dir/e3 &&
+ git ls-files -t >actual &&
+ ! grep "H sub/dir/e" actual &&
+ ! grep "H sub/dir/e2" actual &&
+ ! grep "H sub/dir/e3" actual &&
+ grep "S folder1/dir/e" actual &&
+ grep "H folder1/dir/e2" actual &&
+ grep "H folder1/dir/e3" actual
+'
+
+test_done
diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh
index e18a218..5ab4d41 100755
--- a/t/t7003-filter-branch.sh
+++ b/t/t7003-filter-branch.sh
@@ -49,7 +49,7 @@ test_expect_success 'result is really identical' '
test_expect_success 'rewrite bare repository identically' '
(git config core.bare true && cd .git &&
git filter-branch branch > filter-output 2>&1 &&
- ! fgrep fatal filter-output)
+ ! grep fatal filter-output)
'
git config core.bare false
test_expect_success 'result is really identical' '
@@ -396,10 +396,7 @@ test_expect_success '--prune-empty is able to prune entire branch' '
git branch prune-entire B &&
git filter-branch -f --prune-empty --index-filter "git update-index --remove A.t B.t" prune-entire &&
test_must_fail git rev-parse refs/heads/prune-entire &&
- if test_have_prereq REFFILES
- then
- test_must_fail git reflog exists refs/heads/prune-entire
- fi
+ test_must_fail git reflog exists refs/heads/prune-entire
'
test_expect_success '--remap-to-ancestor with filename filters' '
@@ -506,7 +503,7 @@ test_expect_success 'rewrite repository including refs that point at non-commit
git tag -a -m "tag to a tree" treetag $new_tree &&
git reset --hard HEAD &&
git filter-branch -f -- --all >filter-output 2>&1 &&
- ! fgrep fatal filter-output
+ ! grep fatal filter-output
'
test_expect_success 'filter-branch handles ref deletion' '
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 082be85..696866d 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -94,10 +94,10 @@ test_expect_success 'creating a tag with --create-reflog should create reflog' '
git log -1 \
--format="format:tag: tagging %h (%s, %cd)%n" \
--date=format:%Y-%m-%d >expected &&
- test_when_finished "git tag -d tag_with_reflog" &&
- git tag --create-reflog tag_with_reflog &&
- git reflog exists refs/tags/tag_with_reflog &&
- sed -e "s/^.* //" .git/logs/refs/tags/tag_with_reflog >actual &&
+ test_when_finished "git tag -d tag_with_reflog1" &&
+ git tag --create-reflog tag_with_reflog1 &&
+ git reflog exists refs/tags/tag_with_reflog1 &&
+ test-tool ref-store main for-each-reflog-ent refs/tags/tag_with_reflog1 | sed -e "s/^.* //" >actual &&
test_cmp expected actual
'
@@ -105,10 +105,10 @@ test_expect_success 'annotated tag with --create-reflog has correct message' '
git log -1 \
--format="format:tag: tagging %h (%s, %cd)%n" \
--date=format:%Y-%m-%d >expected &&
- test_when_finished "git tag -d tag_with_reflog" &&
- git tag -m "annotated tag" --create-reflog tag_with_reflog &&
- git reflog exists refs/tags/tag_with_reflog &&
- sed -e "s/^.* //" .git/logs/refs/tags/tag_with_reflog >actual &&
+ test_when_finished "git tag -d tag_with_reflog2" &&
+ git tag -m "annotated tag" --create-reflog tag_with_reflog2 &&
+ git reflog exists refs/tags/tag_with_reflog2 &&
+ test-tool ref-store main for-each-reflog-ent refs/tags/tag_with_reflog2 | sed -e "s/^.* //" >actual &&
test_cmp expected actual
'
@@ -118,10 +118,10 @@ test_expect_success '--create-reflog does not create reflog on failure' '
'
test_expect_success 'option core.logAllRefUpdates=always creates reflog' '
- test_when_finished "git tag -d tag_with_reflog" &&
+ test_when_finished "git tag -d tag_with_reflog3" &&
test_config core.logAllRefUpdates always &&
- git tag tag_with_reflog &&
- git reflog exists refs/tags/tag_with_reflog
+ git tag tag_with_reflog3 &&
+ git reflog exists refs/tags/tag_with_reflog3
'
test_expect_success 'listing all tags if one exists should succeed' '
@@ -792,6 +792,34 @@ test_expect_success 'annotations for blobs are empty' '
test_cmp expect actual
'
+# Run this before doing any signing, so the test has the same results
+# regardless of the GPG prereq.
+test_expect_success 'git tag --format with ahead-behind' '
+ test_when_finished git reset --hard tag-one-line &&
+ git commit --allow-empty -m "left" &&
+ git tag -a -m left tag-left &&
+ git reset --hard HEAD~1 &&
+ git commit --allow-empty -m "right" &&
+ git tag -a -m left tag-right &&
+
+ # Use " !" at the end to demonstrate whitespace
+ # around empty ahead-behind token for tag-blob.
+ cat >expect <<-EOF &&
+ refs/tags/tag-blob !
+ refs/tags/tag-left 1 1 !
+ refs/tags/tag-lines 0 1 !
+ refs/tags/tag-one-line 0 1 !
+ refs/tags/tag-right 0 0 !
+ refs/tags/tag-zero-lines 0 1 !
+ EOF
+ git tag -l --format="%(refname) %(ahead-behind:HEAD) !" >actual 2>err &&
+ grep "refs/tags/tag" actual >actual.focus &&
+ test_cmp expect actual.focus &&
+
+ # Error reported for tags that point to non-commits.
+ grep "error: object [0-9a-f]* is a blob, not a commit" err
+'
+
# trying to verify annotated non-signed tags:
test_expect_success GPG \
@@ -1749,10 +1777,10 @@ test_expect_success '--points-at finds annotated tags of tags' '
'
test_expect_success 'recursive tagging should give advice' '
- sed -e "s/|$//" <<-EOF >expect &&
+ cat >expect <<-EOF &&
hint: You have created a nested tag. The object referred to by your new tag is
hint: already a tag. If you meant to tag the object that it points to, use:
- hint: |
+ hint:
hint: git tag -f nested annotated-v4.0^{}
hint: Disable this message with "git config advice.nestedTag false"
EOF
@@ -1834,6 +1862,51 @@ test_expect_success 'option override configured sort' '
test_cmp expect actual
'
+test_expect_success '--no-sort cancels config sort keys' '
+ test_config tag.sort "-refname" &&
+
+ # objecttype is identical for all of them, so sort falls back on
+ # default (ascending refname)
+ git tag -l \
+ --no-sort \
+ --sort="objecttype" \
+ "foo*" >actual &&
+ cat >expect <<-\EOF &&
+ foo1.10
+ foo1.3
+ foo1.6
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success '--no-sort cancels command line sort keys' '
+ # objecttype is identical for all of them, so sort falls back on
+ # default (ascending refname)
+ git tag -l \
+ --sort="-refname" \
+ --no-sort \
+ --sort="objecttype" \
+ "foo*" >actual &&
+ cat >expect <<-\EOF &&
+ foo1.10
+ foo1.3
+ foo1.6
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success '--no-sort without subsequent --sort prints expected tags' '
+ # Sort the results with `sort` for a consistent comparison against
+ # expected
+ git tag -l --no-sort "foo*" | sort >actual &&
+ cat >expect <<-\EOF &&
+ foo1.10
+ foo1.3
+ foo1.6
+ EOF
+ test_cmp expect actual
+'
+
test_expect_success 'invalid sort parameter on command line' '
test_must_fail git tag -l --sort=notvalid "foo*" >actual
'
@@ -1843,6 +1916,23 @@ test_expect_success 'invalid sort parameter in configuratoin' '
test_must_fail git tag -l "foo*"
'
+test_expect_success 'version sort handles empty value for versionsort.{prereleaseSuffix,suffix}' '
+ cp .git/config .git/config.orig &&
+ test_when_finished mv .git/config.orig .git/config &&
+
+ cat >>.git/config <<-\EOF &&
+ [versionsort]
+ prereleaseSuffix
+ suffix
+ EOF
+ cat >expect <<-\EOF &&
+ error: missing value for '\''versionsort.suffix'\''
+ error: missing value for '\''versionsort.prereleasesuffix'\''
+ EOF
+ git tag -l --sort=version:refname 2>actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'version sort with prerelease reordering' '
test_config versionsort.prereleaseSuffix -rc &&
git tag foo1.6-rc1 &&
@@ -1976,9 +2066,12 @@ test_expect_success ULIMIT_STACK_SIZE '--contains and --no-contains work in a de
committer A U Thor <author@example.com> $((1000000000 + $i * 100)) +0200
data <<EOF
commit #$i
-EOF"
- test $i = 1 && echo "from refs/heads/main^0"
- i=$(($i + 1))
+EOF" &&
+ if test $i = 1
+ then
+ echo "from refs/heads/main^0"
+ fi &&
+ i=$(($i + 1)) || return 1
done | git fast-import &&
git checkout main &&
git tag far-far-away HEAD^ &&
@@ -1998,6 +2091,22 @@ test_expect_success '--format should list tags as per format given' '
test_cmp expect actual
'
+test_expect_success '--format --omit-empty works' '
+ cat >expect <<-\EOF &&
+ refname : refs/tags/v1.0
+
+ refname : refs/tags/v1.1.3
+ EOF
+ git tag -l --format="%(if:notequals=refs/tags/v1.0.1)%(refname)%(then)refname : %(refname)%(end)" "v1*" >actual &&
+ test_cmp expect actual &&
+ cat >expect <<-\EOF &&
+ refname : refs/tags/v1.0
+ refname : refs/tags/v1.1.3
+ EOF
+ git tag -l --omit-empty --format="%(if:notequals=refs/tags/v1.0.1)%(refname)%(then)refname : %(refname)%(end)" "v1*" >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'git tag -l with --format="%(rest)" must fail' '
test_must_fail git tag -l --format="%(rest)" "v1*"
'
@@ -2124,4 +2233,23 @@ test_expect_success 'Does --[no-]contains stop at commits? Yes!' '
test_cmp expected actual
'
+test_expect_success 'If tag is created then tag message file is unlinked' '
+ test_when_finished "git tag -d foo" &&
+ write_script fakeeditor <<-\EOF &&
+ echo Message >.git/TAG_EDITMSG
+ EOF
+ GIT_EDITOR=./fakeeditor git tag -a foo &&
+ test_path_is_missing .git/TAG_EDITMSG
+'
+
+test_expect_success 'If tag cannot be created then tag message file is not unlinked' '
+ test_when_finished "git tag -d foo/bar && rm .git/TAG_EDITMSG" &&
+ write_script fakeeditor <<-\EOF &&
+ echo Message >.git/TAG_EDITMSG
+ EOF
+ git tag foo/bar &&
+ test_must_fail env GIT_EDITOR=./fakeeditor git tag -a foo &&
+ test_path_exists .git/TAG_EDITMSG
+'
+
test_done
diff --git a/t/t7006-pager.sh b/t/t7006-pager.sh
index 0e7cf75..e56ca5b 100755
--- a/t/t7006-pager.sh
+++ b/t/t7006-pager.sh
@@ -661,6 +661,13 @@ test_expect_success 'setup trace2' '
export GIT_TRACE2_BRIEF
'
+test_expect_success 'setup large log output' '
+ perl -e "
+ print \"this is a long commit message\" x 50000
+ " >commit-msg &&
+ git commit --allow-empty -F commit-msg
+'
+
test_expect_success TTY 'git returns SIGPIPE on early pager exit' '
test_when_finished "rm pager-used trace.normal" &&
test_config core.pager ">pager-used; head -n 1; exit 0" &&
@@ -670,7 +677,7 @@ test_expect_success TTY 'git returns SIGPIPE on early pager exit' '
if test_have_prereq !MINGW
then
- OUT=$( ((test_terminal git log; echo $? 1>&3) | :) 3>&1 ) &&
+ { test_terminal git log >/dev/null; OUT=$?; } &&
test_match_signal 13 "$OUT"
else
test_terminal git log
@@ -691,7 +698,7 @@ test_expect_success TTY 'git returns SIGPIPE on early pager non-zero exit' '
if test_have_prereq !MINGW
then
- OUT=$( ((test_terminal git log; echo $? 1>&3) | :) 3>&1 ) &&
+ { test_terminal git log >/dev/null; OUT=$?; } &&
test_match_signal 13 "$OUT"
else
test_terminal git log
@@ -710,13 +717,7 @@ test_expect_success TTY 'git discards pager non-zero exit without SIGPIPE' '
export GIT_TRACE2 &&
test_when_finished "unset GIT_TRACE2" &&
- if test_have_prereq !MINGW
- then
- OUT=$( ((test_terminal git log; echo $? 1>&3) | :) 3>&1 ) &&
- test "$OUT" -eq 0
- else
- test_terminal git log
- fi &&
+ test_terminal git log &&
grep child_exit trace.normal >child-exits &&
test_line_count = 1 child-exits &&
@@ -724,41 +725,14 @@ test_expect_success TTY 'git discards pager non-zero exit without SIGPIPE' '
test_path_is_file pager-used
'
-test_expect_success TTY 'git discards nonexisting pager without SIGPIPE' '
- test_when_finished "rm pager-used trace.normal" &&
- test_config core.pager "wc >pager-used; does-not-exist" &&
- GIT_TRACE2="$(pwd)/trace.normal" &&
- export GIT_TRACE2 &&
- test_when_finished "unset GIT_TRACE2" &&
-
- if test_have_prereq !MINGW
- then
- OUT=$( ((test_terminal git log; echo $? 1>&3) | :) 3>&1 ) &&
- test "$OUT" -eq 0
- else
- test_terminal git log
- fi &&
-
- grep child_exit trace.normal >child-exits &&
- test_line_count = 1 child-exits &&
- grep " code:127 " child-exits &&
- test_path_is_file pager-used
-'
-
-test_expect_success TTY 'git attempts to page to nonexisting pager command, gets SIGPIPE' '
+test_expect_success TTY 'git skips paging nonexisting command' '
test_when_finished "rm trace.normal" &&
test_config core.pager "does-not-exist" &&
GIT_TRACE2="$(pwd)/trace.normal" &&
export GIT_TRACE2 &&
test_when_finished "unset GIT_TRACE2" &&
- if test_have_prereq !MINGW
- then
- OUT=$( ((test_terminal git log; echo $? 1>&3) | :) 3>&1 ) &&
- test_match_signal 13 "$OUT"
- else
- test_terminal git log
- fi &&
+ test_terminal git log &&
grep child_exit trace.normal >child-exits &&
test_line_count = 1 child-exits &&
@@ -767,14 +741,14 @@ test_expect_success TTY 'git attempts to page to nonexisting pager command, gets
test_expect_success TTY 'git returns SIGPIPE on propagated signals from pager' '
test_when_finished "rm pager-used trace.normal" &&
- test_config core.pager ">pager-used; test-tool sigchain" &&
+ test_config core.pager ">pager-used; exec test-tool sigchain" &&
GIT_TRACE2="$(pwd)/trace.normal" &&
export GIT_TRACE2 &&
test_when_finished "unset GIT_TRACE2" &&
if test_have_prereq !MINGW
then
- OUT=$( ((test_terminal git log; echo $? 1>&3) | :) 3>&1 ) &&
+ { test_terminal git log >/dev/null; OUT=$?; } &&
test_match_signal 13 "$OUT"
else
test_terminal git log
@@ -786,4 +760,9 @@ test_expect_success TTY 'git returns SIGPIPE on propagated signals from pager' '
test_path_is_file pager-used
'
+test_expect_success TTY 'non-existent pager doesnt cause crash' '
+ test_config pager.show invalid-pager &&
+ test_terminal git show
+'
+
test_done
diff --git a/t/t7007-show.sh b/t/t7007-show.sh
index d6cc69e..f908a4d 100755
--- a/t/t7007-show.sh
+++ b/t/t7007-show.sh
@@ -2,6 +2,7 @@
test_description='git show'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t7008-filter-branch-null-sha1.sh b/t/t7008-filter-branch-null-sha1.sh
index 9ba9f24..93fbc92 100755
--- a/t/t7008-filter-branch-null-sha1.sh
+++ b/t/t7008-filter-branch-null-sha1.sh
@@ -1,6 +1,7 @@
#!/bin/sh
test_description='filter-branch removal of trees with null sha1'
+
. ./test-lib.sh
test_expect_success 'setup: base commits' '
diff --git a/t/t7010-setup.sh b/t/t7010-setup.sh
index 0335a9a..520f96d 100755
--- a/t/t7010-setup.sh
+++ b/t/t7010-setup.sh
@@ -137,7 +137,7 @@ test_expect_success 'setup deeper work tree' '
test_expect_success 'add a directory outside the work tree' '(
cd tester &&
- d1="$(cd .. ; pwd)" &&
+ d1="$(cd .. && pwd)" &&
test_must_fail git add "$d1"
)'
diff --git a/t/t7011-skip-worktree-reading.sh b/t/t7011-skip-worktree-reading.sh
index 1761a2b..4adac5a 100755
--- a/t/t7011-skip-worktree-reading.sh
+++ b/t/t7011-skip-worktree-reading.sh
@@ -5,6 +5,7 @@
test_description='skip-worktree bit test'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
cat >expect.full <<EOF
diff --git a/t/t7012-skip-worktree-writing.sh b/t/t7012-skip-worktree-writing.sh
index a1080b9..cd5c20f 100755
--- a/t/t7012-skip-worktree-writing.sh
+++ b/t/t7012-skip-worktree-writing.sh
@@ -151,7 +151,7 @@ test_expect_success 'stash restore in sparse checkout' '
git stash push &&
- git sparse-checkout set subdir &&
+ git sparse-checkout set --no-cone subdir &&
# Ensure after sparse-checkout we only have expected files
cat >expect <<-EOF &&
@@ -171,50 +171,20 @@ test_expect_success 'stash restore in sparse checkout' '
# Put a file in the working directory in the way
echo in the way >modified &&
- git stash apply &&
+ test_must_fail git stash apply 2>error&&
- # Ensure stash vivifies modifies paths...
- cat >expect <<-EOF &&
- H addme
- H modified
- H removeme
- H subdir/A
- S untouched
- EOF
- git ls-files -t >actual &&
- test_cmp expect actual &&
+ grep "changes.*would be overwritten by merge" error &&
- # ...and that the paths show up in status as changed...
- cat >expect <<-EOF &&
- A addme
- M modified
- D removeme
- M subdir/A
- ?? actual
- ?? expect
- ?? modified.stash.XXXXXX
- EOF
- git status --porcelain | \
- sed -e s/stash......./stash.XXXXXX/ >actual &&
- test_cmp expect actual &&
+ echo in the way >expect &&
+ test_cmp expect modified &&
+ git diff --quiet HEAD ":!modified" &&
# ...and that working directory reflects the files correctly
- test_path_is_file addme &&
+ test_path_is_missing addme &&
test_path_is_file modified &&
test_path_is_missing removeme &&
test_path_is_file subdir/A &&
- test_path_is_missing untouched &&
-
- # ...including that we have the expected "modified" file...
- cat >expect <<-EOF &&
- modified
- tweaked
- EOF
- test_cmp expect modified &&
-
- # ...and that the other "modified" file is still present...
- echo in the way >expect &&
- test_cmp expect modified.stash.*
+ test_path_is_missing untouched
)
'
diff --git a/t/t7030-verify-tag.sh b/t/t7030-verify-tag.sh
index 10faa64..6f526c3 100755
--- a/t/t7030-verify-tag.sh
+++ b/t/t7030-verify-tag.sh
@@ -115,7 +115,7 @@ test_expect_success GPGSM 'verify and show signatures x509 with high minTrustLev
test_expect_success GPG 'detect fudged signature' '
git cat-file tag seventh-signed >raw &&
- sed -e "/^tag / 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 &&
diff --git a/t/t7031-verify-tag-signed-ssh.sh b/t/t7031-verify-tag-signed-ssh.sh
new file mode 100755
index 0000000..20913b3
--- /dev/null
+++ b/t/t7031-verify-tag-signed-ssh.sh
@@ -0,0 +1,213 @@
+#!/bin/sh
+
+test_description='signed tag tests'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-gpg.sh"
+
+test_expect_success GPGSSH 'create signed tags ssh' '
+ test_when_finished "test_unconfig commit.gpgsign" &&
+ test_config gpg.format ssh &&
+ test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+
+ echo 1 >file && git add file &&
+ test_tick && git commit -m initial &&
+ git tag -s -m initial initial &&
+ git branch side &&
+
+ echo 2 >file && test_tick && git commit -a -m second &&
+ git tag -s -m second second &&
+
+ git checkout side &&
+ echo 3 >elif && git add elif &&
+ test_tick && git commit -m "third on side" &&
+
+ git checkout main &&
+ test_tick && git merge -S side &&
+ git tag -s -m merge merge &&
+
+ echo 4 >file && test_tick && git commit -a -S -m "fourth unsigned" &&
+ git tag -a -m fourth-unsigned fourth-unsigned &&
+
+ test_tick && git commit --amend -S -m "fourth signed" &&
+ git tag -s -m fourth fourth-signed &&
+
+ echo 5 >file && test_tick && git commit -a -m "fifth" &&
+ git tag fifth-unsigned &&
+
+ git config commit.gpgsign true &&
+ echo 6 >file && test_tick && git commit -a -m "sixth" &&
+ git tag -a -m sixth sixth-unsigned &&
+
+ test_tick && git rebase -f HEAD^^ && git tag -s -m 6th sixth-signed HEAD^ &&
+ git tag -m seventh -s seventh-signed &&
+
+ echo 8 >file && test_tick && git commit -a -m eighth &&
+ git tag -u"${GPGSSH_KEY_UNTRUSTED}" -m eighth eighth-signed-alt
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'create signed tags with keys having defined lifetimes' '
+ test_when_finished "test_unconfig commit.gpgsign" &&
+ test_config gpg.format ssh &&
+
+ echo expired >file && test_tick && git commit -a -m expired -S"${GPGSSH_KEY_EXPIRED}" &&
+ git tag -s -u "${GPGSSH_KEY_EXPIRED}" -m expired-signed expired-signed &&
+
+ echo notyetvalid >file && test_tick && git commit -a -m notyetvalid -S"${GPGSSH_KEY_NOTYETVALID}" &&
+ git tag -s -u "${GPGSSH_KEY_NOTYETVALID}" -m notyetvalid-signed notyetvalid-signed &&
+
+ echo timeboxedvalid >file && test_tick && git commit -a -m timeboxedvalid -S"${GPGSSH_KEY_TIMEBOXEDVALID}" &&
+ git tag -s -u "${GPGSSH_KEY_TIMEBOXEDVALID}" -m timeboxedvalid-signed timeboxedvalid-signed &&
+
+ echo timeboxedinvalid >file && test_tick && git commit -a -m timeboxedinvalid -S"${GPGSSH_KEY_TIMEBOXEDINVALID}" &&
+ git tag -s -u "${GPGSSH_KEY_TIMEBOXEDINVALID}" -m timeboxedinvalid-signed timeboxedinvalid-signed
+'
+
+test_expect_success GPGSSH 'verify and show ssh signatures' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ (
+ for tag in initial second merge fourth-signed sixth-signed seventh-signed
+ do
+ git verify-tag $tag 2>actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ echo $tag OK || exit 1
+ done
+ ) &&
+ (
+ for tag in fourth-unsigned fifth-unsigned sixth-unsigned
+ do
+ test_must_fail git verify-tag $tag 2>actual &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ echo $tag OK || exit 1
+ done
+ ) &&
+ (
+ for tag in eighth-signed-alt
+ do
+ test_must_fail git verify-tag $tag 2>actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ grep "${GPGSSH_KEY_NOT_TRUSTED}" actual &&
+ echo $tag OK || exit 1
+ done
+ )
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-tag exits failure on expired signature key' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ test_must_fail git verify-tag expired-signed 2>actual &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-tag exits failure on not yet valid signature key' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ test_must_fail git verify-tag notyetvalid-signed 2>actual &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-tag succeeds with tag date and key validity matching' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git verify-tag timeboxedvalid-signed 2>actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-tag failes with tag date outside of key validity' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ test_must_fail git verify-tag timeboxedinvalid-signed 2>actual &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
+'
+
+test_expect_success GPGSSH 'detect fudged ssh signature' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git cat-file tag seventh-signed >raw &&
+ 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 "${GPGSSH_BAD_SIGNATURE}" actual1 &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual1 &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual1
+'
+
+test_expect_success GPGSSH 'verify ssh signatures with --raw' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ (
+ for tag in initial second merge fourth-signed sixth-signed seventh-signed
+ do
+ git verify-tag --raw $tag 2>actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ echo $tag OK || exit 1
+ done
+ ) &&
+ (
+ for tag in fourth-unsigned fifth-unsigned sixth-unsigned
+ do
+ test_must_fail git verify-tag --raw $tag 2>actual &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ echo $tag OK || exit 1
+ done
+ ) &&
+ (
+ for tag in eighth-signed-alt
+ do
+ test_must_fail git verify-tag --raw $tag 2>actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ echo $tag OK || exit 1
+ done
+ )
+'
+
+test_expect_success GPGSSH 'verify signatures with --raw ssh' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git verify-tag --raw sixth-signed 2>actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ echo sixth-signed OK
+'
+
+test_expect_success GPGSSH 'verify multiple tags ssh' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ tags="seventh-signed sixth-signed" &&
+ for i in $tags
+ do
+ git verify-tag -v --raw $i || return 1
+ done >expect.stdout 2>expect.stderr.1 &&
+ grep "^${GPGSSH_GOOD_SIGNATURE_TRUSTED}" <expect.stderr.1 >expect.stderr &&
+ git verify-tag -v --raw $tags >actual.stdout 2>actual.stderr.1 &&
+ grep "^${GPGSSH_GOOD_SIGNATURE_TRUSTED}" <actual.stderr.1 >actual.stderr &&
+ test_cmp expect.stdout actual.stdout &&
+ test_cmp expect.stderr actual.stderr
+'
+
+test_expect_success GPGSSH 'verifying tag with --format - ssh' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ cat >expect <<-\EOF &&
+ tagname : fourth-signed
+ EOF
+ git verify-tag --format="tagname : %(tag)" "fourth-signed" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPGSSH 'verifying a forged tag with --format should fail silently - ssh' '
+ test_must_fail git verify-tag --format="tagname : %(tag)" $(cat forged1.tag) >actual-forged &&
+ test_must_be_empty actual-forged
+'
+
+test_expect_success GPGSSH 'rev-list --format=%G' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git rev-list -1 --format="%G? %H" sixth-signed >actual &&
+ cat >expect <<-EOF &&
+ commit $(git rev-parse sixth-signed^0)
+ G $(git rev-parse sixth-signed^0)
+ EOF
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t7060-wtstatus.sh b/t/t7060-wtstatus.sh
index 0f4344c..aaeb4a5 100755
--- a/t/t7060-wtstatus.sh
+++ b/t/t7060-wtstatus.sh
@@ -5,6 +5,7 @@ test_description='basic work tree status reporting'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t7062-wtstatus-ignorecase.sh b/t/t7062-wtstatus-ignorecase.sh
index 73709db..caf372a 100755
--- a/t/t7062-wtstatus-ignorecase.sh
+++ b/t/t7062-wtstatus-ignorecase.sh
@@ -2,6 +2,7 @@
test_description='git-status with core.ignorecase=true'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'status with hash collisions' '
diff --git a/t/t7063-status-untracked-cache.sh b/t/t7063-status-untracked-cache.sh
index a0c123b..8929ef4 100755
--- a/t/t7063-status-untracked-cache.sh
+++ b/t/t7063-status-untracked-cache.sh
@@ -86,11 +86,15 @@ test_expect_success 'core.untrackedCache is unset' '
'
test_expect_success 'setup' '
- git init worktree &&
+ git init --template= worktree &&
cd worktree &&
mkdir done dtwo dthree &&
touch one two three done/one dtwo/two dthree/three &&
+ test-tool chmtime =-300 one two three done/one dtwo/two dthree/three &&
+ test-tool chmtime =-300 done dtwo dthree &&
+ test-tool chmtime =-300 . &&
git add one two done/one &&
+ mkdir .git/info &&
: >.git/info/exclude &&
git update-index --untracked-cache &&
test_oid_cache <<-EOF
@@ -142,7 +146,6 @@ two
EOF
test_expect_success 'status first time (empty cache)' '
- avoid_racy &&
: >../trace.output &&
GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../actual &&
@@ -166,7 +169,6 @@ test_expect_success 'untracked cache after first status' '
'
test_expect_success 'status second time (fully populated cache)' '
- avoid_racy &&
: >../trace.output &&
GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../actual &&
@@ -189,9 +191,122 @@ test_expect_success 'untracked cache after second status' '
test_cmp ../dump.expect ../actual
'
+cat >../status_uall.expect <<EOF &&
+A done/one
+A one
+A two
+?? dthree/three
+?? dtwo/two
+?? three
+EOF
+
+# Bypassing the untracked cache here is not desirable from an
+# end-user perspective, but is expected in the current design.
+# The untracked cache data stored for a -unormal run cannot be
+# correctly used in a -uall run - it would yield incorrect output.
+test_expect_success 'untracked cache is bypassed with -uall' '
+ : >../trace.output &&
+ GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
+ git status -uall --porcelain >../actual &&
+ iuc status -uall --porcelain >../status.iuc &&
+ test_cmp ../status_uall.expect ../status.iuc &&
+ test_cmp ../status_uall.expect ../actual &&
+ get_relevant_traces ../trace.output ../trace.relevant &&
+ cat >../trace.expect <<EOF &&
+ ....path:
+EOF
+ test_cmp ../trace.expect ../trace.relevant
+'
+
+test_expect_success 'untracked cache remains after bypass' '
+ test-tool dump-untracked-cache >../actual &&
+ test_cmp ../dump.expect ../actual
+'
+
+test_expect_success 'if -uall is configured, untracked cache gets populated by default' '
+ test_config status.showuntrackedfiles all &&
+ : >../trace.output &&
+ GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
+ git status --porcelain >../actual &&
+ iuc status --porcelain >../status.iuc &&
+ test_cmp ../status_uall.expect ../status.iuc &&
+ test_cmp ../status_uall.expect ../actual &&
+ get_relevant_traces ../trace.output ../trace.relevant &&
+ cat >../trace.expect <<EOF &&
+ ....path:
+ ....node-creation:3
+ ....gitignore-invalidation:1
+ ....directory-invalidation:0
+ ....opendir:4
+EOF
+ test_cmp ../trace.expect ../trace.relevant
+'
+
+cat >../dump_uall.expect <<EOF &&
+info/exclude $EMPTY_BLOB
+core.excludesfile $ZERO_OID
+exclude_per_dir .gitignore
+flags 00000000
+/ $ZERO_OID recurse valid
+three
+/done/ $ZERO_OID recurse valid
+/dthree/ $ZERO_OID recurse valid
+three
+/dtwo/ $ZERO_OID recurse valid
+two
+EOF
+
+test_expect_success 'if -uall was configured, untracked cache is populated' '
+ test-tool dump-untracked-cache >../actual &&
+ test_cmp ../dump_uall.expect ../actual
+'
+
+test_expect_success 'if -uall is configured, untracked cache is used by default' '
+ test_config status.showuntrackedfiles all &&
+ : >../trace.output &&
+ GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
+ git status --porcelain >../actual &&
+ iuc status --porcelain >../status.iuc &&
+ test_cmp ../status_uall.expect ../status.iuc &&
+ test_cmp ../status_uall.expect ../actual &&
+ get_relevant_traces ../trace.output ../trace.relevant &&
+ cat >../trace.expect <<EOF &&
+ ....path:
+ ....node-creation:0
+ ....gitignore-invalidation:0
+ ....directory-invalidation:0
+ ....opendir:0
+EOF
+ test_cmp ../trace.expect ../trace.relevant
+'
+
+# Bypassing the untracked cache here is not desirable from an
+# end-user perspective, but is expected in the current design.
+# The untracked cache data stored for a -all run cannot be
+# correctly used in a -unormal run - it would yield incorrect
+# output.
+test_expect_success 'if -uall is configured, untracked cache is bypassed with -unormal' '
+ test_config status.showuntrackedfiles all &&
+ : >../trace.output &&
+ GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
+ git status -unormal --porcelain >../actual &&
+ iuc status -unormal --porcelain >../status.iuc &&
+ test_cmp ../status.expect ../status.iuc &&
+ test_cmp ../status.expect ../actual &&
+ get_relevant_traces ../trace.output ../trace.relevant &&
+ cat >../trace.expect <<EOF &&
+ ....path:
+EOF
+ test_cmp ../trace.expect ../trace.relevant
+'
+
+test_expect_success 'repopulate untracked cache for -unormal' '
+ git status --porcelain
+'
+
test_expect_success 'modify in root directory, one dir invalidation' '
- avoid_racy &&
: >four &&
+ test-tool chmtime =-240 four &&
: >../trace.output &&
GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../actual &&
@@ -241,7 +356,6 @@ EOF
'
test_expect_success 'new .gitignore invalidates recursively' '
- avoid_racy &&
echo four >.gitignore &&
: >../trace.output &&
GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
@@ -292,7 +406,6 @@ EOF
'
test_expect_success 'new info/exclude invalidates everything' '
- avoid_racy &&
echo three >>.git/info/exclude &&
: >../trace.output &&
GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
@@ -520,14 +633,14 @@ test_expect_success 'create/modify files, some of which are gitignored' '
echo three >done/three && # three is gitignored
echo four >done/four && # four is gitignored at a higher level
echo five >done/five && # five is not gitignored
- echo test >base && #we need to ensure that the root dir is touched
- rm base &&
+ test-tool chmtime =-180 done/two done/three done/four done/five done &&
+ # we need to ensure that the root dir is touched (in the past);
+ test-tool chmtime =-180 . &&
sync_mtime
'
test_expect_success 'test sparse status with untracked cache' '
: >../trace.output &&
- avoid_racy &&
GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../status.actual &&
iuc status --porcelain >../status.iuc &&
@@ -570,7 +683,6 @@ EOF
'
test_expect_success 'test sparse status again with untracked cache' '
- avoid_racy &&
: >../trace.output &&
GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../status.actual &&
@@ -597,11 +709,11 @@ EOF
test_expect_success 'set up for test of subdir and sparse checkouts' '
mkdir done/sub &&
mkdir done/sub/sub &&
- echo "sub" > done/sub/sub/file
+ echo "sub" > done/sub/sub/file &&
+ test-tool chmtime =-120 done/sub/sub/file done/sub/sub done/sub done
'
test_expect_success 'test sparse status with untracked cache and subdir' '
- avoid_racy &&
: >../trace.output &&
GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../status.actual &&
@@ -651,7 +763,6 @@ EOF
'
test_expect_success 'test sparse status again with untracked cache and subdir' '
- avoid_racy &&
: >../trace.output &&
GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../status.actual &&
@@ -875,4 +986,9 @@ test_expect_success '"status" after file replacement should be clean with UC=fal
status_is_clean
'
+test_expect_success 'empty repo (no index) and core.untrackedCache' '
+ git init emptyrepo &&
+ git -C emptyrepo -c core.untrackedCache=true write-tree
+'
+
test_done
diff --git a/t/t7064-wtstatus-pv2.sh b/t/t7064-wtstatus-pv2.sh
index eeb0534..11884d2 100755
--- a/t/t7064-wtstatus-pv2.sh
+++ b/t/t7064-wtstatus-pv2.sh
@@ -4,10 +4,6 @@ test_description='git status --porcelain=v2
This test exercises porcelain V2 output for git status.'
-
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
. ./test-lib.sh
@@ -113,6 +109,21 @@ test_expect_success 'after first commit, create unstaged changes' '
test_cmp expect actual
'
+test_expect_success 'after first commit, stash existing changes' '
+ cat >expect <<-EOF &&
+ # branch.oid $H0
+ # branch.head initial-branch
+ # stash 2
+ EOF
+
+ test_when_finished "git stash pop && git stash pop" &&
+
+ git stash -- file_x &&
+ git stash &&
+ git status --porcelain=v2 --branch --show-stash --untracked-files=no >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'after first commit but omit untracked files and branch' '
cat >expect <<-EOF &&
1 .M N... 100644 100644 100644 $OID_X $OID_X file_x
@@ -462,6 +473,7 @@ test_expect_success 'create and add submodule, submodule appears clean (A. S...)
git checkout initial-branch &&
git clone . sub_repo &&
git clone . super_repo &&
+ test_config_global protocol.file.allow always &&
( cd super_repo &&
git submodule add ../sub_repo sub1 &&
diff --git a/t/t7101-reset-empty-subdirs.sh b/t/t7101-reset-empty-subdirs.sh
index bfce05a..89cf98b 100755
--- a/t/t7101-reset-empty-subdirs.sh
+++ b/t/t7101-reset-empty-subdirs.sh
@@ -4,60 +4,63 @@
#
test_description='git reset should cull empty subdirs'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-diff-data.sh
test_expect_success 'creating initial files' '
- mkdir path0 &&
- cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
- git add path0/COPYING &&
- git commit -m add -a
+ mkdir path0 &&
+ COPYING_test_data >path0/COPYING &&
+ git add path0/COPYING &&
+ git commit -m add -a
'
test_expect_success 'creating second files' '
- mkdir path1 &&
- mkdir path1/path2 &&
- cp "$TEST_DIRECTORY"/../COPYING path1/path2/COPYING &&
- cp "$TEST_DIRECTORY"/../COPYING path1/COPYING &&
- cp "$TEST_DIRECTORY"/../COPYING COPYING &&
- cp "$TEST_DIRECTORY"/../COPYING path0/COPYING-TOO &&
- git add path1/path2/COPYING &&
- git add path1/COPYING &&
- git add COPYING &&
- git add path0/COPYING-TOO &&
- git commit -m change -a
+ mkdir path1 &&
+ mkdir path1/path2 &&
+ COPYING_test_data >path1/path2/COPYING &&
+ COPYING_test_data >path1/COPYING &&
+ COPYING_test_data >COPYING &&
+ COPYING_test_data >path0/COPYING-TOO &&
+ git add path1/path2/COPYING &&
+ git add path1/COPYING &&
+ git add COPYING &&
+ git add path0/COPYING-TOO &&
+ git commit -m change -a
'
test_expect_success 'resetting tree HEAD^' '
- git reset --hard HEAD^
+ git reset --hard HEAD^
'
test_expect_success 'checking initial files exist after rewind' '
- test -d path0 &&
- test -f path0/COPYING
+ test -d path0 &&
+ test -f path0/COPYING
'
test_expect_success 'checking lack of path1/path2/COPYING' '
- ! test -f path1/path2/COPYING
+ ! test -f path1/path2/COPYING
'
test_expect_success 'checking lack of path1/COPYING' '
- ! test -f path1/COPYING
+ ! test -f path1/COPYING
'
test_expect_success 'checking lack of COPYING' '
- ! test -f COPYING
+ ! test -f COPYING
'
test_expect_success 'checking checking lack of path1/COPYING-TOO' '
- ! test -f path0/COPYING-TOO
+ ! test -f path0/COPYING-TOO
'
test_expect_success 'checking lack of path1/path2' '
- ! test -d path1/path2
+ ! test -d path1/path2
'
test_expect_success 'checking lack of path1' '
- ! test -d path1
+ ! test -d path1
'
test_done
diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh
index 601b2bf..62d9f84 100755
--- a/t/t7102-reset.sh
+++ b/t/t7102-reset.sh
@@ -71,6 +71,16 @@ check_changes () {
done | test_cmp .cat_expect -
}
+# no negated form for various type of resets
+for opt in soft mixed hard merge keep
+do
+ test_expect_success "no 'git reset --no-$opt'" '
+ test_when_finished "rm -f err" &&
+ test_must_fail git reset --no-$opt 2>err &&
+ grep "error: unknown option .no-$opt." err
+ '
+done
+
test_expect_success 'reset --hard message' '
hex=$(git log -1 --format="%h") &&
git reset --hard >.actual &&
@@ -462,12 +472,55 @@ test_expect_success 'resetting an unmodified path is a no-op' '
git diff-index --cached --exit-code HEAD
'
+test_reset_refreshes_index () {
+
+ # To test whether the index is refreshed in `git reset --mixed` with
+ # the given options, create a scenario where we clearly see different
+ # results depending on whether the refresh occurred or not.
+
+ # Step 0: start with a clean index
+ git reset --hard HEAD &&
+
+ # Step 1: remove file2, but only in the index (no change to worktree)
+ git rm --cached file2 &&
+
+ # Step 2: reset index & leave worktree unchanged from HEAD
+ git $1 reset $2 --mixed HEAD &&
+
+ # Step 3: verify whether the index is refreshed by checking whether
+ # file2 still has staged changes in the index differing from HEAD (if
+ # the refresh occurred, there should be no such changes)
+ git diff-files >output.log &&
+ test_must_be_empty output.log
+}
+
test_expect_success '--mixed refreshes the index' '
+ # Verify default behavior (without --[no-]refresh or reset.refresh)
+ test_reset_refreshes_index &&
+
+ # With --quiet
+ test_reset_refreshes_index "" --quiet
+'
+
+test_expect_success '--mixed --[no-]refresh sets refresh behavior' '
+ # Verify that --[no-]refresh controls index refresh
+ test_reset_refreshes_index "" --refresh &&
+ ! test_reset_refreshes_index "" --no-refresh
+'
+
+test_expect_success '--mixed preserves skip-worktree' '
+ echo 123 >>file2 &&
+ git add file2 &&
+ git update-index --skip-worktree file2 &&
+ git reset --mixed HEAD >output &&
+ test_must_be_empty output &&
+
cat >expect <<-\EOF &&
Unstaged changes after reset:
M file2
EOF
- echo 123 >>file2 &&
+ git update-index --no-skip-worktree file2 &&
+ git add file2 &&
git reset --mixed HEAD >output &&
test_cmp expect output
'
@@ -563,4 +616,12 @@ test_expect_success 'reset --mixed sets up work tree' '
test_must_be_empty actual
'
+test_expect_success 'reset handles --end-of-options' '
+ git update-ref refs/heads/--foo HEAD^ &&
+ git log -1 --format=%s refs/heads/--foo >expect &&
+ git reset --hard --end-of-options --foo &&
+ git log -1 --format=%s HEAD >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t7103-reset-bare.sh b/t/t7103-reset-bare.sh
index afe36a5..18bbd99 100755
--- a/t/t7103-reset-bare.sh
+++ b/t/t7103-reset-bare.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='git reset in a bare repository'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup non-bare' '
@@ -63,7 +65,10 @@ test_expect_success '"mixed" reset is not allowed in bare' '
test_expect_success '"soft" reset is allowed in bare' '
git reset --soft HEAD^ &&
- test "$(git show --pretty=format:%s | head -n 1)" = "one"
+ git show --pretty=format:%s >out &&
+ echo one >expect &&
+ head -n 1 out >actual &&
+ test_cmp expect actual
'
test_done
diff --git a/t/t7104-reset-hard.sh b/t/t7104-reset-hard.sh
index 7948ec3..cf9697e 100755
--- a/t/t7104-reset-hard.sh
+++ b/t/t7104-reset-hard.sh
@@ -2,6 +2,7 @@
test_description='reset --hard unmerged'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t7105-reset-patch.sh b/t/t7105-reset-patch.sh
index fc2a6cf..f4f3b7a 100755
--- a/t/t7105-reset-patch.sh
+++ b/t/t7105-reset-patch.sh
@@ -1,9 +1,11 @@
#!/bin/sh
test_description='git reset --patch'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-patch-mode.sh
-test_expect_success PERL 'setup' '
+test_expect_success 'setup' '
mkdir dir &&
echo parent > dir/foo &&
echo dummy > bar &&
@@ -17,42 +19,46 @@ test_expect_success PERL 'setup' '
# note: bar sorts before foo, so the first 'n' is always to skip 'bar'
-test_expect_success PERL 'saying "n" does nothing' '
+test_expect_success 'saying "n" does nothing' '
set_and_save_state dir/foo work work &&
test_write_lines n n | git reset -p &&
verify_saved_state dir/foo &&
verify_saved_state bar
'
-test_expect_success PERL 'git reset -p' '
- 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^' '
+for opt in "HEAD" "@" ""
+do
+ test_expect_success "git reset -p $opt" '
+ set_and_save_state dir/foo work work &&
+ test_write_lines n y | git reset -p $opt >output &&
+ verify_state dir/foo work head &&
+ verify_saved_state bar &&
+ test_grep "Unstage" output
+ '
+done
+
+test_expect_success 'git reset -p HEAD^' '
test_write_lines n y | git reset -p HEAD^ >output &&
verify_state dir/foo work parent &&
verify_saved_state bar &&
- test_i18ngrep "Apply" output
+ test_grep "Apply" output
'
-test_expect_success PERL 'git reset -p HEAD^^{tree}' '
+test_expect_success 'git reset -p HEAD^^{tree}' '
test_write_lines n y | git reset -p HEAD^^{tree} >output &&
verify_state dir/foo work parent &&
verify_saved_state bar &&
- test_i18ngrep "Apply" output
+ test_grep "Apply" output
'
-test_expect_success PERL 'git reset -p HEAD^:dir/foo (blob fails)' '
+test_expect_success 'git reset -p HEAD^:dir/foo (blob fails)' '
set_and_save_state dir/foo work work &&
test_must_fail git reset -p HEAD^:dir/foo &&
verify_saved_state dir/foo &&
verify_saved_state bar
'
-test_expect_success PERL 'git reset -p aaaaaaaa (unknown fails)' '
+test_expect_success 'git reset -p aaaaaaaa (unknown fails)' '
set_and_save_state dir/foo work work &&
test_must_fail git reset -p aaaaaaaa &&
verify_saved_state dir/foo &&
@@ -64,27 +70,27 @@ test_expect_success PERL 'git reset -p aaaaaaaa (unknown fails)' '
# dir/foo. There's always an extra 'n' to reject edits to dir/foo in
# the failure case (and thus get out of the loop).
-test_expect_success PERL 'git reset -p dir' '
+test_expect_success 'git reset -p dir' '
set_state dir/foo work work &&
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)' '
+test_expect_success 'git reset -p -- foo (inside dir)' '
set_state dir/foo work work &&
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' '
+test_expect_success '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
'
-test_expect_success PERL 'none of this moved HEAD' '
+test_expect_success 'none of this moved HEAD' '
verify_saved_head
'
diff --git a/t/t7106-reset-unborn-branch.sh b/t/t7106-reset-unborn-branch.sh
index ecb85c3..88d1c8a 100755
--- a/t/t7106-reset-unborn-branch.sh
+++ b/t/t7106-reset-unborn-branch.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='git reset should work on unborn branch'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -32,7 +34,7 @@ test_expect_success 'reset $file' '
test_cmp expect actual
'
-test_expect_success PERL 'reset -p' '
+test_expect_success 'reset -p' '
rm .git/index &&
git add a &&
echo y >yes &&
@@ -40,7 +42,7 @@ test_expect_success PERL 'reset -p' '
git ls-files >actual &&
test_must_be_empty actual &&
- test_i18ngrep "Unstage" output
+ test_grep "Unstage" output
'
test_expect_success 'reset --soft is a no-op' '
diff --git a/t/t7107-reset-pathspec-file.sh b/t/t7107-reset-pathspec-file.sh
index 15ccb14..020db20 100755
--- a/t/t7107-reset-pathspec-file.sh
+++ b/t/t7107-reset-pathspec-file.sh
@@ -2,6 +2,7 @@
test_description='reset --pathspec-from-file'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_tick
@@ -160,19 +161,19 @@ test_expect_success 'error conditions' '
git rm fileA.t &&
test_must_fail git reset --pathspec-from-file=list --patch 2>err &&
- test_i18ngrep -e "--pathspec-from-file is incompatible with --patch" err &&
+ test_grep -e "options .--pathspec-from-file. and .--patch. cannot be used together" err &&
test_must_fail git reset --pathspec-from-file=list -- fileA.t 2>err &&
- test_i18ngrep -e "--pathspec-from-file is incompatible with pathspec arguments" err &&
+ test_grep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
test_must_fail git reset --pathspec-file-nul 2>err &&
- test_i18ngrep -e "--pathspec-file-nul requires --pathspec-from-file" err &&
+ test_grep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
test_must_fail git reset --soft --pathspec-from-file=list 2>err &&
- test_i18ngrep -e "fatal: Cannot do soft reset with paths" err &&
+ test_grep -e "fatal: Cannot do soft reset with paths" err &&
test_must_fail git reset --hard --pathspec-from-file=list 2>err &&
- test_i18ngrep -e "fatal: Cannot do hard reset with paths" err
+ test_grep -e "fatal: Cannot do hard reset with paths" err
'
test_done
diff --git a/t/t7110-reset-merge.sh b/t/t7110-reset-merge.sh
index a82a07a..7ee180f 100755
--- a/t/t7110-reset-merge.sh
+++ b/t/t7110-reset-merge.sh
@@ -5,20 +5,21 @@
test_description='Tests for "git reset" with "--merge" and "--keep" options'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
- for i in 1 2 3; do echo line $i; done >file1 &&
- cat file1 >file2 &&
- git add file1 file2 &&
- test_tick &&
- git commit -m "Initial commit" &&
- git tag initial &&
- echo line 4 >>file1 &&
- cat file1 >file2 &&
- test_tick &&
- git commit -m "add line 4 to file1" file1 &&
- git tag second
+ printf "line %d\n" 1 2 3 >file1 &&
+ cat file1 >file2 &&
+ git add file1 file2 &&
+ test_tick &&
+ git commit -m "Initial commit" &&
+ git tag initial &&
+ echo line 4 >>file1 &&
+ cat file1 >file2 &&
+ test_tick &&
+ git commit -m "add line 4 to file1" file1 &&
+ git tag second
'
# The next test will test the following:
@@ -28,19 +29,19 @@ test_expect_success setup '
# file1: C C C D --merge D D D
# file2: C D D D --merge C D D
test_expect_success 'reset --merge is ok with changes in file it does not touch' '
- git reset --merge HEAD^ &&
- ! grep 4 file1 &&
- grep 4 file2 &&
- test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
- test -z "$(git diff --cached)"
+ git reset --merge HEAD^ &&
+ ! grep 4 file1 &&
+ grep 4 file2 &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+ test -z "$(git diff --cached)"
'
test_expect_success 'reset --merge is ok when switching back' '
- git reset --merge second &&
- grep 4 file1 &&
- grep 4 file2 &&
- test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
- test -z "$(git diff --cached)"
+ git reset --merge second &&
+ grep 4 file1 &&
+ grep 4 file2 &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+ test -z "$(git diff --cached)"
'
# The next test will test the following:
@@ -50,21 +51,21 @@ test_expect_success 'reset --merge is ok when switching back' '
# file1: C C C D --keep D D D
# file2: C D D D --keep C D D
test_expect_success 'reset --keep is ok with changes in file it does not touch' '
- git reset --hard second &&
- cat file1 >file2 &&
- git reset --keep HEAD^ &&
- ! grep 4 file1 &&
- grep 4 file2 &&
- test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
- test -z "$(git diff --cached)"
+ git reset --hard second &&
+ cat file1 >file2 &&
+ git reset --keep HEAD^ &&
+ ! grep 4 file1 &&
+ grep 4 file2 &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+ test -z "$(git diff --cached)"
'
test_expect_success 'reset --keep is ok when switching back' '
- git reset --keep second &&
- grep 4 file1 &&
- grep 4 file2 &&
- test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
- test -z "$(git diff --cached)"
+ git reset --keep second &&
+ grep 4 file1 &&
+ grep 4 file2 &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+ test -z "$(git diff --cached)"
'
# The next test will test the following:
@@ -74,28 +75,28 @@ test_expect_success 'reset --keep is ok when switching back' '
# file1: B B C D --merge D D D
# file2: C D D D --merge C D D
test_expect_success 'reset --merge discards changes added to index (1)' '
- git reset --hard second &&
- cat file1 >file2 &&
- echo "line 5" >> file1 &&
- git add file1 &&
- git reset --merge HEAD^ &&
- ! grep 4 file1 &&
- ! grep 5 file1 &&
- grep 4 file2 &&
- test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
- test -z "$(git diff --cached)"
+ git reset --hard second &&
+ cat file1 >file2 &&
+ echo "line 5" >> file1 &&
+ git add file1 &&
+ git reset --merge HEAD^ &&
+ ! grep 4 file1 &&
+ ! grep 5 file1 &&
+ grep 4 file2 &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+ test -z "$(git diff --cached)"
'
test_expect_success 'reset --merge is ok again when switching back (1)' '
- git reset --hard initial &&
- echo "line 5" >> file2 &&
- git add file2 &&
- git reset --merge second &&
- ! grep 4 file2 &&
- ! grep 5 file1 &&
- grep 4 file1 &&
- test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
- test -z "$(git diff --cached)"
+ git reset --hard initial &&
+ echo "line 5" >> file2 &&
+ git add file2 &&
+ git reset --merge second &&
+ ! grep 4 file2 &&
+ ! grep 5 file1 &&
+ grep 4 file1 &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+ test -z "$(git diff --cached)"
'
# The next test will test the following:
@@ -104,10 +105,10 @@ test_expect_success 'reset --merge is ok again when switching back (1)' '
# ----------------------------------------------------
# file1: B B C D --keep (disallowed)
test_expect_success 'reset --keep fails with changes in index in files it touches' '
- git reset --hard second &&
- echo "line 5" >> file1 &&
- git add file1 &&
- test_must_fail git reset --keep HEAD^
+ git reset --hard second &&
+ echo "line 5" >> file1 &&
+ git add file1 &&
+ test_must_fail git reset --keep HEAD^
'
# The next test will test the following:
@@ -117,23 +118,23 @@ test_expect_success 'reset --keep fails with changes in index in files it touche
# file1: C C C D --merge D D D
# file2: C C D D --merge D D D
test_expect_success 'reset --merge discards changes added to index (2)' '
- git reset --hard second &&
- echo "line 4" >> file2 &&
- git add file2 &&
- git reset --merge HEAD^ &&
- ! grep 4 file2 &&
- test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
- test -z "$(git diff)" &&
- test -z "$(git diff --cached)"
+ git reset --hard second &&
+ echo "line 4" >> file2 &&
+ git add file2 &&
+ git reset --merge HEAD^ &&
+ ! grep 4 file2 &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+ test -z "$(git diff)" &&
+ test -z "$(git diff --cached)"
'
test_expect_success 'reset --merge is ok again when switching back (2)' '
- git reset --hard initial &&
- git reset --merge second &&
- ! grep 4 file2 &&
- grep 4 file1 &&
- test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
- test -z "$(git diff --cached)"
+ git reset --hard initial &&
+ git reset --merge second &&
+ ! grep 4 file2 &&
+ grep 4 file1 &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+ test -z "$(git diff --cached)"
'
# The next test will test the following:
@@ -143,21 +144,21 @@ test_expect_success 'reset --merge is ok again when switching back (2)' '
# file1: C C C D --keep D D D
# file2: C C D D --keep C D D
test_expect_success 'reset --keep keeps changes it does not touch' '
- git reset --hard second &&
- echo "line 4" >> file2 &&
- git add file2 &&
- git reset --keep HEAD^ &&
- grep 4 file2 &&
- test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
- test -z "$(git diff --cached)"
+ git reset --hard second &&
+ echo "line 4" >> file2 &&
+ git add file2 &&
+ git reset --keep HEAD^ &&
+ grep 4 file2 &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+ test -z "$(git diff --cached)"
'
test_expect_success 'reset --keep keeps changes when switching back' '
- git reset --keep second &&
- grep 4 file2 &&
- grep 4 file1 &&
- test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
- test -z "$(git diff --cached)"
+ git reset --keep second &&
+ grep 4 file2 &&
+ grep 4 file1 &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+ test -z "$(git diff --cached)"
'
# The next test will test the following:
@@ -166,14 +167,14 @@ test_expect_success 'reset --keep keeps changes when switching back' '
# ----------------------------------------------------
# file1: A B B C --merge (disallowed)
test_expect_success 'reset --merge fails with changes in file it touches' '
- git reset --hard second &&
- echo "line 5" >> file1 &&
- test_tick &&
- git commit -m "add line 5" file1 &&
- sed -e "s/line 1/changed line 1/" <file1 >file3 &&
- mv file3 file1 &&
- test_must_fail git reset --merge HEAD^ 2>err.log &&
- grep file1 err.log | grep "not uptodate"
+ git reset --hard second &&
+ echo "line 5" >> file1 &&
+ test_tick &&
+ git commit -m "add line 5" file1 &&
+ sed -e "s/line 1/changed line 1/" <file1 >file3 &&
+ mv file3 file1 &&
+ test_must_fail git reset --merge HEAD^ 2>err.log &&
+ grep file1 err.log | grep "not uptodate"
'
# The next test will test the following:
@@ -182,36 +183,36 @@ test_expect_success 'reset --merge fails with changes in file it touches' '
# ----------------------------------------------------
# file1: A B B C --keep (disallowed)
test_expect_success 'reset --keep fails with changes in file it touches' '
- git reset --hard second &&
- echo "line 5" >> file1 &&
- test_tick &&
- git commit -m "add line 5" file1 &&
- sed -e "s/line 1/changed line 1/" <file1 >file3 &&
- mv file3 file1 &&
- test_must_fail git reset --keep HEAD^ 2>err.log &&
- grep file1 err.log | grep "not uptodate"
+ git reset --hard second &&
+ echo "line 5" >> file1 &&
+ test_tick &&
+ git commit -m "add line 5" file1 &&
+ sed -e "s/line 1/changed line 1/" <file1 >file3 &&
+ mv file3 file1 &&
+ test_must_fail git reset --keep HEAD^ 2>err.log &&
+ grep file1 err.log | grep "not uptodate"
'
test_expect_success 'setup 3 different branches' '
- git reset --hard second &&
- git branch branch1 &&
- git branch branch2 &&
- git branch branch3 &&
- git checkout branch1 &&
- echo "line 5 in branch1" >> file1 &&
- test_tick &&
- git commit -a -m "change in branch1" &&
- git checkout branch2 &&
- echo "line 5 in branch2" >> file1 &&
- test_tick &&
- git commit -a -m "change in branch2" &&
- git tag third &&
- git checkout branch3 &&
- echo a new file >file3 &&
- rm -f file1 &&
- git add file3 &&
- test_tick &&
- git commit -a -m "change in branch3"
+ git reset --hard second &&
+ git branch branch1 &&
+ git branch branch2 &&
+ git branch branch3 &&
+ git checkout branch1 &&
+ echo "line 5 in branch1" >> file1 &&
+ test_tick &&
+ git commit -a -m "change in branch1" &&
+ git checkout branch2 &&
+ echo "line 5 in branch2" >> file1 &&
+ test_tick &&
+ git commit -a -m "change in branch2" &&
+ git tag third &&
+ git checkout branch3 &&
+ echo a new file >file3 &&
+ rm -f file1 &&
+ git add file3 &&
+ test_tick &&
+ git commit -a -m "change in branch3"
'
# The next test will test the following:
@@ -220,12 +221,12 @@ test_expect_success 'setup 3 different branches' '
# ----------------------------------------------------
# file1: X U B C --merge C C C
test_expect_success '"reset --merge HEAD^" is ok with pending merge' '
- git checkout third &&
- test_must_fail git merge branch1 &&
- git reset --merge HEAD^ &&
- test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
- test -z "$(git diff --cached)" &&
- test -z "$(git diff)"
+ git checkout third &&
+ test_must_fail git merge branch1 &&
+ git reset --merge HEAD^ &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+ test -z "$(git diff --cached)" &&
+ test -z "$(git diff)"
'
# The next test will test the following:
@@ -234,10 +235,10 @@ test_expect_success '"reset --merge HEAD^" is ok with pending merge' '
# ----------------------------------------------------
# file1: X U B C --keep (disallowed)
test_expect_success '"reset --keep HEAD^" fails with pending merge' '
- git reset --hard third &&
- test_must_fail git merge branch1 &&
- test_must_fail git reset --keep HEAD^ 2>err.log &&
- test_i18ngrep "middle of a merge" err.log
+ git reset --hard third &&
+ test_must_fail git merge branch1 &&
+ test_must_fail git reset --keep HEAD^ 2>err.log &&
+ test_grep "middle of a merge" err.log
'
# The next test will test the following:
@@ -246,12 +247,12 @@ test_expect_success '"reset --keep HEAD^" fails with pending merge' '
# ----------------------------------------------------
# file1: X U B B --merge B B B
test_expect_success '"reset --merge HEAD" is ok with pending merge' '
- git reset --hard third &&
- test_must_fail git merge branch1 &&
- git reset --merge HEAD &&
- test "$(git rev-parse HEAD)" = "$(git rev-parse third)" &&
- test -z "$(git diff --cached)" &&
- test -z "$(git diff)"
+ git reset --hard third &&
+ test_must_fail git merge branch1 &&
+ git reset --merge HEAD &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse third)" &&
+ test -z "$(git diff --cached)" &&
+ test -z "$(git diff)"
'
# The next test will test the following:
@@ -260,36 +261,36 @@ test_expect_success '"reset --merge HEAD" is ok with pending merge' '
# ----------------------------------------------------
# file1: X U B B --keep (disallowed)
test_expect_success '"reset --keep HEAD" fails with pending merge' '
- git reset --hard third &&
- test_must_fail git merge branch1 &&
- test_must_fail git reset --keep HEAD 2>err.log &&
- test_i18ngrep "middle of a merge" err.log
+ git reset --hard third &&
+ test_must_fail git merge branch1 &&
+ test_must_fail git reset --keep HEAD 2>err.log &&
+ test_grep "middle of a merge" err.log
'
test_expect_success '--merge is ok with added/deleted merge' '
- git reset --hard third &&
- rm -f file2 &&
- test_must_fail git merge branch3 &&
- ! test -f file2 &&
- test -f file3 &&
- git diff --exit-code file3 &&
- git diff --exit-code branch3 file3 &&
- git reset --merge HEAD &&
- ! test -f file3 &&
- ! test -f file2 &&
- git diff --exit-code --cached
+ git reset --hard third &&
+ rm -f file2 &&
+ test_must_fail git merge branch3 &&
+ ! test -f file2 &&
+ test -f file3 &&
+ git diff --exit-code file3 &&
+ git diff --exit-code branch3 file3 &&
+ git reset --merge HEAD &&
+ ! test -f file3 &&
+ ! test -f file2 &&
+ git diff --exit-code --cached
'
test_expect_success '--keep fails with added/deleted merge' '
- git reset --hard third &&
- rm -f file2 &&
- test_must_fail git merge branch3 &&
- ! test -f file2 &&
- test -f file3 &&
- git diff --exit-code file3 &&
- git diff --exit-code branch3 file3 &&
- test_must_fail git reset --keep HEAD 2>err.log &&
- test_i18ngrep "middle of a merge" err.log
+ git reset --hard third &&
+ rm -f file2 &&
+ test_must_fail git merge branch3 &&
+ ! test -f file2 &&
+ test -f file3 &&
+ git diff --exit-code file3 &&
+ git diff --exit-code branch3 file3 &&
+ test_must_fail git reset --keep HEAD 2>err.log &&
+ test_grep "middle of a merge" err.log
'
test_done
diff --git a/t/t7111-reset-table.sh b/t/t7111-reset-table.sh
index ce421ad..01b7c35 100755
--- a/t/t7111-reset-table.sh
+++ b/t/t7111-reset-table.sh
@@ -5,13 +5,14 @@
test_description='Tests to check that "reset" options follow a known table'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'creating initial commits' '
- test_commit E file1 &&
- test_commit D file1 &&
- test_commit C file1
+ test_commit E file1 &&
+ test_commit D file1 &&
+ test_commit C file1
'
while read W1 I1 H1 T opt W2 I2 H2
@@ -73,13 +74,13 @@ B C C C keep B C C
EOF
test_expect_success 'setting up branches to test with unmerged entries' '
- git reset --hard C &&
- git branch branch1 &&
- git branch branch2 &&
- git checkout branch1 &&
- test_commit B1 file1 &&
- git checkout branch2 &&
- test_commit B file1
+ git reset --hard C &&
+ git branch branch1 &&
+ git branch branch2 &&
+ git checkout branch1 &&
+ test_commit B1 file1 &&
+ git checkout branch2 &&
+ test_commit B file1
'
while read W1 I1 H1 T opt W2 I2 H2
diff --git a/t/t7112-reset-submodule.sh b/t/t7112-reset-submodule.sh
index 19830d9..a3e2413 100755
--- a/t/t7112-reset-submodule.sh
+++ b/t/t7112-reset-submodule.sh
@@ -6,7 +6,6 @@ test_description='reset can handle submodules'
. "$TEST_DIRECTORY"/lib-submodule-update.sh
KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS=1
-KNOWN_FAILURE_SUBMODULE_OVERWRITE_IGNORED_UNTRACKED=1
test_submodule_switch_recursing_with_args "reset --keep"
diff --git a/t/t7113-post-index-change-hook.sh b/t/t7113-post-index-change-hook.sh
index 688fa99..58e55a7 100755
--- a/t/t7113-post-index-change-hook.sh
+++ b/t/t7113-post-index-change-hook.sh
@@ -5,6 +5,7 @@ test_description='post index change hook'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -16,8 +17,7 @@ test_expect_success 'setup' '
'
test_expect_success 'test status, add, commit, others trigger hook without flags set' '
- mkdir -p .git/hooks &&
- write_script .git/hooks/post-index-change <<-\EOF &&
+ test_hook post-index-change <<-\EOF &&
if test "$1" -eq 1; then
echo "Invalid combination of flags passed to hook; updated_workdir is set." >testfailure
exit 1
@@ -62,7 +62,7 @@ test_expect_success 'test status, add, commit, others trigger hook without flags
'
test_expect_success 'test checkout and reset trigger the hook' '
- write_script .git/hooks/post-index-change <<-\EOF &&
+ test_hook post-index-change <<-\EOF &&
if test "$1" -eq 1 && test "$2" -eq 1; then
echo "Invalid combination of flags passed to hook; updated_workdir and updated_skipworktree are both set." >testfailure
exit 1
@@ -105,7 +105,7 @@ test_expect_success 'test checkout and reset trigger the hook' '
'
test_expect_success 'test reset --mixed and update-index triggers the hook' '
- write_script .git/hooks/post-index-change <<-\EOF &&
+ test_hook post-index-change <<-\EOF &&
if test "$1" -eq 1 && test "$2" -eq 1; then
echo "Invalid combination of flags passed to hook; updated_workdir and updated_skipworktree are both set." >testfailure
exit 1
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index 7f6e23a..42352dc 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -217,7 +217,7 @@ test_expect_success 'switch to another branch while carrying a deletion' '
git rm two &&
test_must_fail git checkout simple 2>errs &&
- test_i18ngrep overwritten errs &&
+ test_grep overwritten errs &&
test_must_fail git read-tree --quiet -m -u HEAD simple 2>errs &&
test_must_be_empty errs
@@ -229,7 +229,7 @@ test_expect_success 'checkout to detach HEAD (with advice declined)' '
git checkout -f renamer &&
git clean -f &&
git checkout renamer^ 2>messages &&
- test_i18ngrep "HEAD is now at $rev" messages &&
+ test_grep "HEAD is now at $rev" messages &&
test_line_count = 1 messages &&
H=$(git rev-parse --verify HEAD) &&
M=$(git show-ref -s --verify refs/heads/main) &&
@@ -372,75 +372,75 @@ test_expect_success 'checkout specific path while in subdirectory' '
'
test_expect_success 'checkout w/--track sets up tracking' '
- git config branch.autosetupmerge false &&
- git checkout main &&
- git checkout --track -b track1 &&
- test "$(git config branch.track1.remote)" &&
- test "$(git config branch.track1.merge)"
+ git config branch.autosetupmerge false &&
+ git checkout main &&
+ git checkout --track -b track1 &&
+ test "$(git config branch.track1.remote)" &&
+ test "$(git config branch.track1.merge)"
'
test_expect_success 'checkout w/autosetupmerge=always sets up tracking' '
- test_when_finished git config branch.autosetupmerge false &&
- git config branch.autosetupmerge always &&
- git checkout main &&
- git checkout -b track2 &&
- test "$(git config branch.track2.remote)" &&
- test "$(git config branch.track2.merge)"
+ test_when_finished git config branch.autosetupmerge false &&
+ git config branch.autosetupmerge always &&
+ git checkout main &&
+ git checkout -b track2 &&
+ test "$(git config branch.track2.remote)" &&
+ test "$(git config branch.track2.merge)"
'
test_expect_success 'checkout w/--track from non-branch HEAD fails' '
- git checkout main^0 &&
- test_must_fail git symbolic-ref HEAD &&
- test_must_fail git checkout --track -b track &&
- test_must_fail git rev-parse --verify track &&
- test_must_fail git symbolic-ref HEAD &&
- test "z$(git rev-parse main^0)" = "z$(git rev-parse HEAD)"
+ git checkout main^0 &&
+ test_must_fail git symbolic-ref HEAD &&
+ test_must_fail git checkout --track -b track &&
+ test_must_fail git rev-parse --verify track &&
+ test_must_fail git symbolic-ref HEAD &&
+ test "z$(git rev-parse main^0)" = "z$(git rev-parse HEAD)"
'
test_expect_success 'checkout w/--track from tag fails' '
- git checkout main^0 &&
- test_must_fail git symbolic-ref HEAD &&
- test_must_fail git checkout --track -b track frotz &&
- test_must_fail git rev-parse --verify track &&
- test_must_fail git symbolic-ref HEAD &&
- test "z$(git rev-parse main^0)" = "z$(git rev-parse HEAD)"
+ git checkout main^0 &&
+ test_must_fail git symbolic-ref HEAD &&
+ test_must_fail git checkout --track -b track frotz &&
+ test_must_fail git rev-parse --verify track &&
+ test_must_fail git symbolic-ref HEAD &&
+ test "z$(git rev-parse main^0)" = "z$(git rev-parse HEAD)"
'
test_expect_success 'detach a symbolic link HEAD' '
- git checkout main &&
- git config --bool core.prefersymlinkrefs yes &&
- git checkout side &&
- git checkout main &&
- it=$(git symbolic-ref HEAD) &&
- test "z$it" = zrefs/heads/main &&
- here=$(git rev-parse --verify refs/heads/main) &&
- git checkout side^ &&
- test "z$(git rev-parse --verify refs/heads/main)" = "z$here"
+ git checkout main &&
+ git config --bool core.prefersymlinkrefs yes &&
+ git checkout side &&
+ git checkout main &&
+ it=$(git symbolic-ref HEAD) &&
+ test "z$it" = zrefs/heads/main &&
+ here=$(git rev-parse --verify refs/heads/main) &&
+ git checkout side^ &&
+ test "z$(git rev-parse --verify refs/heads/main)" = "z$here"
'
test_expect_success 'checkout with --track fakes a sensible -b <name>' '
- git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" &&
- git update-ref refs/remotes/origin/koala/bear renamer &&
+ git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" &&
+ git update-ref refs/remotes/origin/koala/bear renamer &&
- git checkout --track origin/koala/bear &&
- test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
- test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
+ git checkout --track origin/koala/bear &&
+ test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
- git checkout main && git branch -D koala/bear &&
+ git checkout main && git branch -D koala/bear &&
- git checkout --track refs/remotes/origin/koala/bear &&
- test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
- test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
+ git checkout --track refs/remotes/origin/koala/bear &&
+ test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
- git checkout main && git branch -D koala/bear &&
+ git checkout main && git branch -D koala/bear &&
- git checkout --track remotes/origin/koala/bear &&
- test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
- test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)"
+ git checkout --track remotes/origin/koala/bear &&
+ test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)"
'
test_expect_success 'checkout with --track, but without -b, fails with too short tracked name' '
- test_must_fail git checkout --track renamer
+ test_must_fail git checkout --track renamer
'
setup_conflicting_index () {
@@ -497,6 +497,11 @@ test_expect_success 'checkout unmerged stage' '
test ztheirside = "z$(cat file)"
'
+test_expect_success 'checkout path with --merge from tree-ish is a no-no' '
+ setup_conflicting_index &&
+ test_must_fail git checkout -m HEAD -- file
+'
+
test_expect_success 'checkout with --merge' '
setup_conflicting_index &&
echo "none of the above" >sample &&
@@ -517,6 +522,48 @@ test_expect_success 'checkout with --merge' '
test_cmp merged file
'
+test_expect_success 'checkout -m works after (mistaken) resolution' '
+ setup_conflicting_index &&
+ echo "none of the above" >sample &&
+ cat sample >fild &&
+ cat sample >file &&
+ cat sample >filf &&
+ # resolve to something
+ git add file &&
+ git checkout --merge -- fild file filf &&
+ {
+ echo "<<<<<<< ours" &&
+ echo ourside &&
+ echo "=======" &&
+ echo theirside &&
+ echo ">>>>>>> theirs"
+ } >merged &&
+ test_cmp expect fild &&
+ test_cmp expect filf &&
+ test_cmp merged file
+'
+
+test_expect_success 'checkout -m works after (mistaken) resolution to remove' '
+ setup_conflicting_index &&
+ echo "none of the above" >sample &&
+ cat sample >fild &&
+ cat sample >file &&
+ cat sample >filf &&
+ # resolve to remove
+ git rm file &&
+ git checkout --merge -- fild file filf &&
+ {
+ echo "<<<<<<< ours" &&
+ echo ourside &&
+ echo "=======" &&
+ echo theirside &&
+ echo ">>>>>>> theirs"
+ } >merged &&
+ test_cmp expect fild &&
+ test_cmp expect filf &&
+ test_cmp merged file
+'
+
test_expect_success 'checkout with --merge, in diff3 -m style' '
git config merge.conflictstyle diff3 &&
setup_conflicting_index &&
@@ -584,7 +631,74 @@ test_expect_success 'checkout --conflict=diff3' '
test_cmp merged file
'
+test_expect_success 'checkout --conflict=diff3 --no-conflict does not merge' '
+ setup_conflicting_index &&
+ echo "none of the above" >expect &&
+ cat expect >fild &&
+ cat expect >file &&
+ test_must_fail git checkout --conflict=diff3 --no-conflict -- fild file 2>err &&
+ test_cmp expect file &&
+ test_cmp expect fild &&
+ echo "error: path ${SQ}file${SQ} is unmerged" >expect &&
+ test_cmp expect err
+'
+
+test_expect_success 'checkout --conflict=diff3 --no-merge does not merge' '
+ setup_conflicting_index &&
+ echo "none of the above" >expect &&
+ cat expect >fild &&
+ cat expect >file &&
+ test_must_fail git checkout --conflict=diff3 --no-merge -- fild file 2>err &&
+ test_cmp expect file &&
+ test_cmp expect fild &&
+ echo "error: path ${SQ}file${SQ} is unmerged" >expect &&
+ test_cmp expect err
+'
+
+test_expect_success 'checkout --no-merge --conflict=diff3 does merge' '
+ setup_conflicting_index &&
+ echo "none of the above" >fild &&
+ echo "none of the above" >file &&
+ git checkout --no-merge --conflict=diff3 -- fild file &&
+ echo "ourside" >expect &&
+ test_cmp expect fild &&
+ cat >expect <<-\EOF &&
+ <<<<<<< ours
+ ourside
+ ||||||| base
+ original
+ =======
+ theirside
+ >>>>>>> theirs
+ EOF
+ test_cmp expect file
+'
+
+test_expect_success 'checkout --merge --conflict=diff3 --no-conflict does merge' '
+ setup_conflicting_index &&
+ echo "none of the above" >fild &&
+ echo "none of the above" >file &&
+ git checkout --merge --conflict=diff3 --no-conflict -- fild file &&
+ echo "ourside" >expect &&
+ test_cmp expect fild &&
+ cat >expect <<-\EOF &&
+ <<<<<<< ours
+ ourside
+ =======
+ theirside
+ >>>>>>> theirs
+ EOF
+ test_cmp expect file
+'
+
+test_expect_success 'checkout with invalid conflict style' '
+ test_must_fail git checkout --conflict=bad 2>actual -- file &&
+ echo "error: unknown conflict style ${SQ}bad${SQ}" >expect &&
+ test_cmp expect actual
+'
+
test_expect_success 'failing checkout -b should not break working tree' '
+ git clean -fd && # Remove untracked files in the way
git reset --hard main &&
git symbolic-ref HEAD refs/heads/main &&
test_must_fail git checkout -b renamer side^ &&
@@ -657,4 +771,21 @@ test_expect_success 'custom merge driver with checkout -m' '
test_cmp expect arm
'
+test_expect_success 'tracking info copied with autoSetupMerge=inherit' '
+ git reset --hard main &&
+ # default config does not copy tracking info
+ git checkout -b foo-no-inherit koala/bear &&
+ test_cmp_config "" --default "" branch.foo-no-inherit.remote &&
+ test_cmp_config "" --default "" branch.foo-no-inherit.merge &&
+ # with autoSetupMerge=inherit, we copy tracking info from koala/bear
+ test_config branch.autoSetupMerge inherit &&
+ git checkout -b foo koala/bear &&
+ test_cmp_config origin branch.foo.remote &&
+ test_cmp_config refs/heads/koala/bear branch.foo.merge &&
+ # no tracking info to inherit from main
+ git checkout -b main2 main &&
+ test_cmp_config "" --default "" branch.main2.remote &&
+ test_cmp_config "" --default "" branch.main2.merge
+'
+
test_done
diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh
index 0399701..0aae0de 100755
--- a/t/t7300-clean.sh
+++ b/t/t7300-clean.sh
@@ -5,6 +5,7 @@
test_description='git clean basic tests'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
git config clean.requireForce no
@@ -120,7 +121,7 @@ test_expect_success 'git clean with relative prefix' '
grep part3 |
sed -n -e "s|^Would remove ||p"
) &&
- verbose test "$would_clean" = ../src/part3.c
+ test "$would_clean" = ../src/part3.c
'
test_expect_success 'git clean with absolute path' '
@@ -133,7 +134,7 @@ test_expect_success 'git clean with absolute path' '
grep part3 |
sed -n -e "s|^Would remove ||p"
) &&
- verbose test "$would_clean" = ../src/part3.c
+ test "$would_clean" = ../src/part3.c
'
test_expect_success 'git clean with out of work tree relative path' '
@@ -407,6 +408,12 @@ test_expect_success 'clean.requireForce and -f' '
'
+test_expect_success 'clean.requireForce and --interactive' '
+ git clean --interactive </dev/null >output 2>error &&
+ test_grep ! "requireForce is true and" error &&
+ test_grep "\*\*\* Commands \*\*\*" output
+'
+
test_expect_success 'core.excludesfile' '
echo excludes >excludes &&
@@ -480,6 +487,7 @@ test_expect_success 'should not clean submodules' '
git init &&
test_commit msg hello.world
) &&
+ test_config_global protocol.file.allow always &&
git submodule add ./repo/.git sub1 &&
git commit -m "sub1" &&
git branch before_sub2 &&
@@ -516,8 +524,12 @@ test_expect_success 'nested (empty) git should be kept' '
git init empty_repo &&
mkdir to_clean &&
>to_clean/should_clean.this &&
+ # Note that we put the expect file in the .git directory so that it
+ # does not get cleaned.
+ find empty_repo | sort >.git/expect &&
git clean -f -d &&
- test_path_is_file empty_repo/.git/HEAD &&
+ find empty_repo | sort >actual &&
+ test_cmp .git/expect actual &&
test_path_is_missing to_clean
'
@@ -558,10 +570,10 @@ test_expect_success 'giving path in nested git work tree will NOT remove it' '
mkdir -p bar/baz &&
test_commit msg bar/baz/hello.world
) &&
+ find repo | sort >expect &&
git clean -f -d repo/bar/baz &&
- test_path_is_file repo/.git/HEAD &&
- test_path_is_dir repo/bar/ &&
- test_path_is_file repo/bar/baz/hello.world
+ find repo | sort >actual &&
+ test_cmp expect actual
'
test_expect_success 'giving path to nested .git will not remove it' '
@@ -572,10 +584,10 @@ test_expect_success 'giving path to nested .git will not remove it' '
git init &&
test_commit msg hello.world
) &&
+ find repo | sort >expect &&
git clean -f -d repo/.git &&
- test_path_is_file repo/.git/HEAD &&
- test_path_is_dir repo/.git/refs &&
- test_path_is_dir repo/.git/objects &&
+ find repo | sort >actual &&
+ test_cmp expect actual &&
test_path_is_dir untracked/
'
@@ -587,9 +599,10 @@ test_expect_success 'giving path to nested .git/ will NOT remove contents' '
git init &&
test_commit msg hello.world
) &&
+ find repo | sort >expect &&
git clean -f -d repo/.git/ &&
- test_path_is_dir repo/.git &&
- test_path_is_file repo/.git/HEAD &&
+ find repo | sort >actual &&
+ test_cmp expect actual &&
test_path_is_dir untracked/
'
@@ -734,7 +747,7 @@ test_expect_success MINGW 'handle clean & core.longpaths = false nicely' '
test_must_fail git clean -xdf 2>.git/err &&
# grepping for a strerror string is unportable but it is OK here with
# MINGW prereq
- test_i18ngrep "too long" .git/err
+ test_grep "too long" .git/err
'
test_expect_success 'clean untracked paths by pathspec' '
diff --git a/t/t7301-clean-interactive.sh b/t/t7301-clean-interactive.sh
index a07e8b8..4afe53c 100755
--- a/t/t7301-clean-interactive.sh
+++ b/t/t7301-clean-interactive.sh
@@ -2,6 +2,7 @@
test_description='git clean -i basic tests'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
@@ -24,18 +25,18 @@ test_expect_success 'git clean -i (c: clean hotkey)' '
touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
docs/manual.txt obj.o build/lib.so &&
echo c | git clean -i &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test ! -f a.out &&
- test -f docs/manual.txt &&
- test ! -f src/part3.c &&
- test ! -f src/part3.h &&
- test ! -f src/part4.c &&
- test ! -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_missing a.out &&
+ test_path_is_file docs/manual.txt &&
+ test_path_is_missing src/part3.c &&
+ test_path_is_missing src/part3.h &&
+ test_path_is_missing src/part4.c &&
+ test_path_is_missing src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -45,18 +46,18 @@ test_expect_success 'git clean -i (cl: clean prefix)' '
touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
docs/manual.txt obj.o build/lib.so &&
echo cl | git clean -i &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test ! -f a.out &&
- test -f docs/manual.txt &&
- test ! -f src/part3.c &&
- test ! -f src/part3.h &&
- test ! -f src/part4.c &&
- test ! -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_missing a.out &&
+ test_path_is_file docs/manual.txt &&
+ test_path_is_missing src/part3.c &&
+ test_path_is_missing src/part3.h &&
+ test_path_is_missing src/part4.c &&
+ test_path_is_missing src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -66,18 +67,18 @@ test_expect_success 'git clean -i (quit)' '
touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
docs/manual.txt obj.o build/lib.so &&
echo quit | git clean -i &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test -f a.out &&
- test -f docs/manual.txt &&
- test -f src/part3.c &&
- test -f src/part3.h &&
- test -f src/part4.c &&
- test -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_file a.out &&
+ test_path_is_file docs/manual.txt &&
+ test_path_is_file src/part3.c &&
+ test_path_is_file src/part3.h &&
+ test_path_is_file src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -87,18 +88,18 @@ test_expect_success 'git clean -i (Ctrl+D)' '
touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
docs/manual.txt obj.o build/lib.so &&
echo "\04" | git clean -i &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test -f a.out &&
- test -f docs/manual.txt &&
- test -f src/part3.c &&
- test -f src/part3.h &&
- test -f src/part4.c &&
- test -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_file a.out &&
+ test_path_is_file docs/manual.txt &&
+ test_path_is_file src/part3.c &&
+ test_path_is_file src/part3.h &&
+ test_path_is_file src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -109,18 +110,18 @@ test_expect_success 'git clean -id (filter all)' '
docs/manual.txt obj.o build/lib.so &&
test_write_lines f "*" "" c |
git clean -id &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test -f a.out &&
- test -f docs/manual.txt &&
- test -f src/part3.c &&
- test -f src/part3.h &&
- test -f src/part4.c &&
- test -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_file a.out &&
+ test_path_is_file docs/manual.txt &&
+ test_path_is_file src/part3.c &&
+ test_path_is_file src/part3.h &&
+ test_path_is_file src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -131,18 +132,18 @@ test_expect_success 'git clean -id (filter patterns)' '
docs/manual.txt obj.o build/lib.so &&
test_write_lines f "part3.* *.out" "" c |
git clean -id &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test -f a.out &&
- test ! -f docs/manual.txt &&
- test -f src/part3.c &&
- test -f src/part3.h &&
- test ! -f src/part4.c &&
- test ! -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_file a.out &&
+ test_path_is_missing docs/manual.txt &&
+ test_path_is_file src/part3.c &&
+ test_path_is_file src/part3.h &&
+ test_path_is_missing src/part4.c &&
+ test_path_is_missing src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -153,18 +154,18 @@ test_expect_success 'git clean -id (filter patterns 2)' '
docs/manual.txt obj.o build/lib.so &&
test_write_lines f "* !*.out" "" c |
git clean -id &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test ! -f a.out &&
- test -f docs/manual.txt &&
- test -f src/part3.c &&
- test -f src/part3.h &&
- test -f src/part4.c &&
- test -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_missing a.out &&
+ test_path_is_file docs/manual.txt &&
+ test_path_is_file src/part3.c &&
+ test_path_is_file src/part3.h &&
+ test_path_is_file src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -175,18 +176,18 @@ test_expect_success 'git clean -id (select - all)' '
docs/manual.txt obj.o build/lib.so &&
test_write_lines s "*" "" c |
git clean -id &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test ! -f a.out &&
- test ! -f docs/manual.txt &&
- test ! -f src/part3.c &&
- test ! -f src/part3.h &&
- test ! -f src/part4.c &&
- test ! -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_missing a.out &&
+ test_path_is_missing docs/manual.txt &&
+ test_path_is_missing src/part3.c &&
+ test_path_is_missing src/part3.h &&
+ test_path_is_missing src/part4.c &&
+ test_path_is_missing src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -197,18 +198,18 @@ test_expect_success 'git clean -id (select - none)' '
docs/manual.txt obj.o build/lib.so &&
test_write_lines s "" c |
git clean -id &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test -f a.out &&
- test -f docs/manual.txt &&
- test -f src/part3.c &&
- test -f src/part3.h &&
- test -f src/part4.c &&
- test -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_file a.out &&
+ test_path_is_file docs/manual.txt &&
+ test_path_is_file src/part3.c &&
+ test_path_is_file src/part3.h &&
+ test_path_is_file src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -219,18 +220,18 @@ test_expect_success 'git clean -id (select - number)' '
docs/manual.txt obj.o build/lib.so &&
test_write_lines s 3 "" c |
git clean -id &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test -f a.out &&
- test -f docs/manual.txt &&
- test ! -f src/part3.c &&
- test -f src/part3.h &&
- test -f src/part4.c &&
- test -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_file a.out &&
+ test_path_is_file docs/manual.txt &&
+ test_path_is_missing src/part3.c &&
+ test_path_is_file src/part3.h &&
+ test_path_is_file src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -241,18 +242,18 @@ test_expect_success 'git clean -id (select - number 2)' '
docs/manual.txt obj.o build/lib.so &&
test_write_lines s "2 3" 5 "" c |
git clean -id &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test -f a.out &&
- test ! -f docs/manual.txt &&
- test ! -f src/part3.c &&
- test -f src/part3.h &&
- test ! -f src/part4.c &&
- test -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_file a.out &&
+ test_path_is_missing docs/manual.txt &&
+ test_path_is_missing src/part3.c &&
+ test_path_is_file src/part3.h &&
+ test_path_is_missing src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -263,18 +264,18 @@ test_expect_success 'git clean -id (select - number 3)' '
docs/manual.txt obj.o build/lib.so &&
test_write_lines s "3,4 5" "" c |
git clean -id &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test -f a.out &&
- test -f docs/manual.txt &&
- test ! -f src/part3.c &&
- test ! -f src/part3.h &&
- test ! -f src/part4.c &&
- test -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_file a.out &&
+ test_path_is_file docs/manual.txt &&
+ test_path_is_missing src/part3.c &&
+ test_path_is_missing src/part3.h &&
+ test_path_is_missing src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -284,11 +285,11 @@ test_expect_success 'git clean -id (select - filenames)' '
touch a.out foo.txt bar.txt baz.txt &&
test_write_lines s "a.out fo ba bar" "" c |
git clean -id &&
- test -f Makefile &&
- test ! -f a.out &&
- test ! -f foo.txt &&
- test ! -f bar.txt &&
- test -f baz.txt &&
+ test_path_is_file Makefile &&
+ test_path_is_missing a.out &&
+ test_path_is_missing foo.txt &&
+ test_path_is_missing bar.txt &&
+ test_path_is_file baz.txt &&
rm baz.txt
'
@@ -300,18 +301,18 @@ test_expect_success 'git clean -id (select - range)' '
docs/manual.txt obj.o build/lib.so &&
test_write_lines s "1,3-4" 2 "" c |
git clean -id &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test ! -f a.out &&
- test ! -f src/part3.c &&
- test ! -f src/part3.h &&
- test -f src/part4.c &&
- test -f src/part4.h &&
- test ! -f docs/manual.txt &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_missing a.out &&
+ test_path_is_missing src/part3.c &&
+ test_path_is_missing src/part3.h &&
+ test_path_is_file src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_missing docs/manual.txt &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -322,18 +323,18 @@ test_expect_success 'git clean -id (select - range 2)' '
docs/manual.txt obj.o build/lib.so &&
test_write_lines s "4- 1" "" c |
git clean -id &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test ! -f a.out &&
- test -f docs/manual.txt &&
- test -f src/part3.c &&
- test ! -f src/part3.h &&
- test ! -f src/part4.c &&
- test ! -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_missing a.out &&
+ test_path_is_file docs/manual.txt &&
+ test_path_is_file src/part3.c &&
+ test_path_is_missing src/part3.h &&
+ test_path_is_missing src/part4.c &&
+ test_path_is_missing src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -344,18 +345,18 @@ test_expect_success 'git clean -id (inverse select)' '
docs/manual.txt obj.o build/lib.so &&
test_write_lines s "*" "-5- 1 -2" "" c |
git clean -id &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test ! -f a.out &&
- test -f docs/manual.txt &&
- test ! -f src/part3.c &&
- test ! -f src/part3.h &&
- test -f src/part4.c &&
- test -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_missing a.out &&
+ test_path_is_file docs/manual.txt &&
+ test_path_is_missing src/part3.c &&
+ test_path_is_missing src/part3.h &&
+ test_path_is_file src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -366,18 +367,18 @@ test_expect_success 'git clean -id (ask)' '
docs/manual.txt obj.o build/lib.so &&
test_write_lines a Y y no yes bad "" |
git clean -id &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test ! -f a.out &&
- test ! -f docs/manual.txt &&
- test -f src/part3.c &&
- test ! -f src/part3.h &&
- test -f src/part4.c &&
- test -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_missing a.out &&
+ test_path_is_missing docs/manual.txt &&
+ test_path_is_file src/part3.c &&
+ test_path_is_missing src/part3.h &&
+ test_path_is_file src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -388,18 +389,18 @@ test_expect_success 'git clean -id (ask - Ctrl+D)' '
docs/manual.txt obj.o build/lib.so &&
test_write_lines a Y no yes "\04" |
git clean -id &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test ! -f a.out &&
- test -f docs/manual.txt &&
- test ! -f src/part3.c &&
- test -f src/part3.h &&
- test -f src/part4.c &&
- test -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_missing a.out &&
+ test_path_is_file docs/manual.txt &&
+ test_path_is_missing src/part3.c &&
+ test_path_is_file src/part3.h &&
+ test_path_is_file src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -411,18 +412,18 @@ test_expect_success 'git clean -id with prefix and path (filter)' '
(cd build/ &&
test_write_lines f docs "*.h" "" c |
git clean -id ..) &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test ! -f a.out &&
- test -f docs/manual.txt &&
- test ! -f src/part3.c &&
- test -f src/part3.h &&
- test ! -f src/part4.c &&
- test -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_missing a.out &&
+ test_path_is_file docs/manual.txt &&
+ test_path_is_missing src/part3.c &&
+ test_path_is_file src/part3.h &&
+ test_path_is_missing src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -434,18 +435,18 @@ test_expect_success 'git clean -id with prefix and path (select by name)' '
(cd build/ &&
test_write_lines s ../docs/ ../src/part3.c ../src/part4.c "" c |
git clean -id ..) &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test -f a.out &&
- test ! -f docs/manual.txt &&
- test ! -f src/part3.c &&
- test -f src/part3.h &&
- test ! -f src/part4.c &&
- test -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_file a.out &&
+ test_path_is_missing docs/manual.txt &&
+ test_path_is_missing src/part3.c &&
+ test_path_is_file src/part3.h &&
+ test_path_is_missing src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -457,18 +458,18 @@ test_expect_success 'git clean -id with prefix and path (ask)' '
(cd build/ &&
test_write_lines a Y y no yes bad "" |
git clean -id ..) &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test ! -f a.out &&
- test ! -f docs/manual.txt &&
- test -f src/part3.c &&
- test ! -f src/part3.h &&
- test -f src/part4.c &&
- test -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_missing a.out &&
+ test_path_is_missing docs/manual.txt &&
+ test_path_is_file src/part3.c &&
+ test_path_is_missing src/part3.h &&
+ test_path_is_file src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index cb1b8e3..5c4a89d 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -14,6 +14,36 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
+test_expect_success 'setup - enable local submodules' '
+ git config --global protocol.file.allow always
+'
+
+test_expect_success 'submodule usage: -h' '
+ git submodule -h >out 2>err &&
+ grep "^usage: git submodule" out &&
+ test_must_be_empty err
+'
+
+test_expect_success 'submodule usage: --recursive' '
+ test_expect_code 1 git submodule --recursive >out 2>err &&
+ grep "^usage: git submodule" err &&
+ test_must_be_empty out
+'
+
+test_expect_success 'submodule usage: status --' '
+ test_expect_code 1 git submodule -- &&
+ test_expect_code 1 git submodule --end-of-options
+'
+
+for opt in '--quiet' '--cached'
+do
+ test_expect_success "submodule usage: status $opt" '
+ git submodule $opt &&
+ git submodule status $opt &&
+ git submodule $opt status
+ '
+done
+
test_expect_success 'submodule deinit works on empty repository' '
git submodule deinit --all
'
@@ -30,7 +60,7 @@ test_expect_success 'submodule init aborts on missing .gitmodules file' '
git update-index --add --cacheinfo 160000,$(git rev-parse HEAD),sub &&
# missing the .gitmodules file here
test_must_fail git submodule init 2>actual &&
- test_i18ngrep "No url found for submodule path" actual
+ test_grep "No url found for submodule path" actual
'
test_expect_success 'submodule update aborts on missing .gitmodules file' '
@@ -38,7 +68,7 @@ test_expect_success 'submodule update aborts on missing .gitmodules file' '
git update-index --add --cacheinfo 160000,$(git rev-parse HEAD),sub &&
# missing the .gitmodules file here
git submodule update sub 2>actual &&
- test_i18ngrep "Submodule path .sub. not initialized" actual
+ test_grep "Submodule path .sub. not initialized" actual
'
test_expect_success 'submodule update aborts on missing gitmodules url' '
@@ -70,7 +100,7 @@ test_expect_success 'status should ignore inner git repo when not added' '
) &&
test_must_fail git submodule status inner 2>output.err &&
rm -fr inner &&
- test_i18ngrep "^error: .*did not match any file(s) known to git" output.err
+ test_grep "^error: .*did not match any file(s) known to git" output.err
'
test_expect_success 'setup - repository in init subdirectory' '
@@ -152,6 +182,11 @@ test_expect_success 'submodule add' '
test_must_be_empty untracked
'
+test_expect_success !WINDOWS 'submodule add (absolute path)' '
+ test_when_finished "git reset --hard" &&
+ git submodule add "$submodurl" "$submodurl/add-abs"
+'
+
test_expect_success 'setup parent and one repository' '
test_create_repo parent &&
test_commit -C parent one
@@ -161,7 +196,7 @@ test_expect_success 'redirected submodule add does not show progress' '
git -C addtest submodule add "file://$submodurl/parent" submod-redirected \
2>err &&
! grep % err &&
- test_i18ngrep ! "Checking connectivity" err
+ test_grep ! "Checking connectivity" err
'
test_expect_success 'redirected submodule add --progress does show progress' '
@@ -177,8 +212,7 @@ test_expect_success 'submodule add to .gitignored path fails' '
The following paths are ignored by one of your .gitignore files:
submod
hint: Use -f if you really want to add them.
- hint: Turn this message off by running
- hint: "git config advice.addIgnoredFile false"
+ hint: Disable this message with "git config advice.addIgnoredFile false"
EOF
# Does not use test_commit due to the ignore
echo "*" > .gitignore &&
@@ -228,7 +262,7 @@ test_expect_success 'submodule add relays add --dry-run stderr' '
cd addtest &&
: >.git/index.lock &&
! git submodule add "$submodurl" sub-while-locked 2>output.err &&
- test_i18ngrep "^fatal: .*index\.lock" output.err &&
+ test_grep "^fatal: .*index\.lock" output.err &&
test_path_is_missing sub-while-locked
)
'
@@ -370,7 +404,7 @@ test_expect_success 'submodule add in subdirectory with relative path should fai
cd addtest/sub &&
test_must_fail git submodule add ../../ submod3 2>../../output.err
) &&
- test_i18ngrep toplevel output.err
+ test_grep toplevel output.err
'
test_expect_success 'setup - add an example entry to .gitmodules' '
@@ -451,7 +485,7 @@ test_expect_success 'status should still be "missing" after initializing' '
test_failure_with_unknown_submodule () {
test_must_fail git submodule $1 no-such-submodule 2>output.err &&
- test_i18ngrep "^error: .*no-such-submodule" output.err
+ test_grep "^error: .*no-such-submodule" output.err
}
test_expect_success 'init should fail with unknown submodule' '
@@ -544,6 +578,16 @@ test_expect_success 'status should be "modified" after submodule commit' '
grep "^+$rev2" list
'
+test_expect_success '"submodule --cached" command forms should be identical' '
+ git submodule status --cached >expect &&
+
+ git submodule --cached >actual &&
+ test_cmp expect actual &&
+
+ git submodule --cached status >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'the --cached sha1 should be rev1' '
git submodule --cached status >list &&
grep "^+$rev1" list
@@ -599,7 +643,7 @@ test_expect_success 'update --init' '
test_must_fail git config submodule.example.url &&
git submodule update init 2> update.out &&
- test_i18ngrep "not initialized" update.out &&
+ test_grep "not initialized" update.out &&
test_must_fail git rev-parse --resolve-git-dir init/.git &&
git submodule update --init init &&
@@ -616,7 +660,7 @@ test_expect_success 'update --init from subdirectory' '
(
cd sub &&
git submodule update ../init 2>update.out &&
- test_i18ngrep "not initialized" update.out &&
+ test_grep "not initialized" update.out &&
test_must_fail git rev-parse --resolve-git-dir ../init/.git &&
git submodule update --init ../init
@@ -1076,7 +1120,7 @@ test_expect_success 'submodule deinit from subdirectory' '
cd sub &&
git submodule deinit ../init >../output
) &&
- test_i18ngrep "\\.\\./init" output &&
+ test_grep "\\.\\./init" output &&
test -z "$(git config --get-regexp "submodule\.example\.")" &&
test -n "$(git config --get-regexp "submodule\.example2\.")" &&
test -f example2/.git &&
@@ -1091,8 +1135,8 @@ test_expect_success 'submodule deinit . deinits all initialized submodules' '
git submodule deinit . >actual &&
test -z "$(git config --get-regexp "submodule\.example\.")" &&
test -z "$(git config --get-regexp "submodule\.example2\.")" &&
- test_i18ngrep "Cleared directory .init" actual &&
- test_i18ngrep "Cleared directory .example2" actual &&
+ test_grep "Cleared directory .init" actual &&
+ test_grep "Cleared directory .example2" actual &&
rmdir init example2
'
@@ -1104,8 +1148,8 @@ test_expect_success 'submodule deinit --all deinits all initialized submodules'
git submodule deinit --all >actual &&
test -z "$(git config --get-regexp "submodule\.example\.")" &&
test -z "$(git config --get-regexp "submodule\.example2\.")" &&
- test_i18ngrep "Cleared directory .init" actual &&
- test_i18ngrep "Cleared directory .example2" actual &&
+ test_grep "Cleared directory .init" actual &&
+ test_grep "Cleared directory .example2" actual &&
rmdir init example2
'
@@ -1115,8 +1159,8 @@ test_expect_success 'submodule deinit deinits a submodule when its work tree is
git submodule deinit init example2 >actual &&
test -z "$(git config --get-regexp "submodule\.example\.")" &&
test -z "$(git config --get-regexp "submodule\.example2\.")" &&
- test_i18ngrep ! "Cleared directory .init" actual &&
- test_i18ngrep "Cleared directory .example2" actual &&
+ test_grep ! "Cleared directory .init" actual &&
+ test_grep "Cleared directory .example2" actual &&
rmdir init
'
@@ -1128,7 +1172,7 @@ test_expect_success 'submodule deinit fails when the submodule contains modifica
test -f example2/.git &&
git submodule deinit -f init >actual &&
test -z "$(git config --get-regexp "submodule\.example\.")" &&
- test_i18ngrep "Cleared directory .init" actual &&
+ test_grep "Cleared directory .init" actual &&
rmdir init
'
@@ -1140,7 +1184,7 @@ test_expect_success 'submodule deinit fails when the submodule contains untracke
test -f example2/.git &&
git submodule deinit -f init >actual &&
test -z "$(git config --get-regexp "submodule\.example\.")" &&
- test_i18ngrep "Cleared directory .init" actual &&
+ test_grep "Cleared directory .init" actual &&
rmdir init
'
@@ -1155,45 +1199,44 @@ test_expect_success 'submodule deinit fails when the submodule HEAD does not mat
test -f example2/.git &&
git submodule deinit -f init >actual &&
test -z "$(git config --get-regexp "submodule\.example\.")" &&
- test_i18ngrep "Cleared directory .init" actual &&
+ test_grep "Cleared directory .init" actual &&
rmdir init
'
test_expect_success 'submodule deinit is silent when used on an uninitialized submodule' '
git submodule update --init &&
git submodule deinit init >actual &&
- test_i18ngrep "Submodule .example. (.*) unregistered for path .init" actual &&
- test_i18ngrep "Cleared directory .init" actual &&
+ test_grep "Submodule .example. (.*) unregistered for path .init" actual &&
+ test_grep "Cleared directory .init" actual &&
git submodule deinit init >actual &&
- test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
- test_i18ngrep "Cleared directory .init" actual &&
+ test_grep ! "Submodule .example. (.*) unregistered for path .init" actual &&
+ test_grep "Cleared directory .init" actual &&
git submodule deinit . >actual &&
- test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
- test_i18ngrep "Submodule .example2. (.*) unregistered for path .example2" actual &&
- test_i18ngrep "Cleared directory .init" actual &&
+ test_grep ! "Submodule .example. (.*) unregistered for path .init" actual &&
+ test_grep "Submodule .example2. (.*) unregistered for path .example2" actual &&
+ test_grep "Cleared directory .init" actual &&
git submodule deinit . >actual &&
- test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
- test_i18ngrep ! "Submodule .example2. (.*) unregistered for path .example2" actual &&
- test_i18ngrep "Cleared directory .init" actual &&
+ test_grep ! "Submodule .example. (.*) unregistered for path .init" actual &&
+ test_grep ! "Submodule .example2. (.*) unregistered for path .example2" actual &&
+ test_grep "Cleared directory .init" actual &&
git submodule deinit --all >actual &&
- test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
- test_i18ngrep ! "Submodule .example2. (.*) unregistered for path .example2" actual &&
- test_i18ngrep "Cleared directory .init" actual &&
+ test_grep ! "Submodule .example. (.*) unregistered for path .init" actual &&
+ test_grep ! "Submodule .example2. (.*) unregistered for path .example2" actual &&
+ test_grep "Cleared directory .init" actual &&
rmdir init example2
'
-test_expect_success 'submodule deinit fails when submodule has a .git directory even when forced' '
+test_expect_success 'submodule deinit absorbs .git directory if .git is a directory' '
git submodule update --init &&
(
cd init &&
rm .git &&
- cp -R ../.git/modules/example .git &&
+ mv ../.git/modules/example .git &&
GIT_WORK_TREE=. git config --unset core.worktree
) &&
- test_must_fail git submodule deinit init &&
- test_must_fail git submodule deinit -f init &&
- test -d init/.git &&
- test -n "$(git config --get-regexp "submodule\.example\.")"
+ git submodule deinit init &&
+ test_path_is_missing init/.git &&
+ test -z "$(git config --get-regexp "submodule\.example\.")"
'
test_expect_success 'submodule with UTF-8 name' '
@@ -1225,31 +1268,6 @@ test_expect_success 'submodule add clone shallow submodule' '
)
'
-test_expect_success 'submodule helper list is not confused by common prefixes' '
- mkdir -p dir1/b &&
- (
- cd dir1/b &&
- git init &&
- echo hi >testfile2 &&
- git add . &&
- git commit -m "test1"
- ) &&
- mkdir -p dir2/b &&
- (
- cd dir2/b &&
- git init &&
- echo hello >testfile1 &&
- git add . &&
- git commit -m "test2"
- ) &&
- git submodule add /dir1/b dir1/b &&
- git submodule add /dir2/b dir2/b &&
- git commit -m "first submodule commit" &&
- git submodule--helper list dir1/b | cut -f 2 >actual &&
- echo "dir1/b" >expect &&
- test_cmp expect actual
-'
-
test_expect_success 'setup superproject with submodules' '
git init sub1 &&
test_commit -C sub1 test &&
@@ -1332,6 +1350,22 @@ test_expect_success 'clone active submodule without submodule url set' '
)
'
+test_expect_success 'update submodules without url set in .gitconfig' '
+ test_when_finished "rm -rf multisuper_clone" &&
+ git clone file://"$pwd"/multisuper multisuper_clone &&
+
+ git -C multisuper_clone submodule init &&
+ for s in sub0 sub1 sub2 sub3
+ do
+ key=submodule.$s.url &&
+ git -C multisuper_clone config --local --unset $key &&
+ git -C multisuper_clone config --file .gitmodules --unset $key || return 1
+ done &&
+
+ test_must_fail git -C multisuper_clone submodule update 2>err &&
+ grep "cannot clone submodule .sub[0-3]. without a URL" err
+'
+
test_expect_success 'clone --recurse-submodules with a pathspec works' '
test_when_finished "rm -rf multisuper_clone" &&
cat >expected <<-\EOF &&
diff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh
index 9c3cc4c..542b333 100755
--- a/t/t7401-submodule-summary.sh
+++ b/t/t7401-submodule-summary.sh
@@ -17,6 +17,7 @@ This test script tries to verify the sanity of summary subcommand of git submodu
# various reasons, one of them being that there are lots of commands taking place
# outside of 'test_expect_success' block, which is no longer in good-style.
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
add_file () {
diff --git a/t/t7402-submodule-rebase.sh b/t/t7402-submodule-rebase.sh
index 8e32f19..aa2fdc3 100755
--- a/t/t7402-submodule-rebase.sh
+++ b/t/t7402-submodule-rebase.sh
@@ -5,6 +5,7 @@
test_description='Test rebasing, stashing, etc. with submodules'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -55,12 +56,15 @@ chmod a+x fake-editor.sh
test_expect_success 'interactive rebase with a dirty submodule' '
- test submodule = $(git diff --name-only) &&
+ echo submodule >expect &&
+ git diff --name-only >actual &&
+ test_cmp expect actual &&
HEAD=$(git rev-parse HEAD) &&
GIT_EDITOR="\"$(pwd)/fake-editor.sh\"" EDITOR_TEXT="pick $HEAD" \
git rebase -i HEAD^ &&
- test submodule = $(git diff --name-only)
-
+ echo submodule >expect &&
+ git diff --name-only >actual &&
+ test_cmp expect actual
'
test_expect_success 'rebase with dirty file and submodule fails' '
@@ -82,11 +86,19 @@ test_expect_success 'stash with a dirty submodule' '
CURRENT=$(cd submodule && git rev-parse HEAD) &&
git stash &&
test new != $(cat file) &&
- test submodule = $(git diff --name-only) &&
- test $CURRENT = $(cd submodule && git rev-parse HEAD) &&
+ echo submodule >expect &&
+ git diff --name-only >actual &&
+ test_cmp expect actual &&
+
+ echo "$CURRENT" >expect &&
+ git -C submodule rev-parse HEAD >actual &&
+ test_cmp expect actual &&
+
git stash apply &&
test new = $(cat file) &&
- test $CURRENT = $(cd submodule && git rev-parse HEAD)
+ echo "$CURRENT" >expect &&
+ git -C submodule rev-parse HEAD >actual &&
+ test_cmp expect actual
'
@@ -104,7 +116,7 @@ test_expect_success 'rebasing submodule that should conflict' '
test_tick &&
git commit -m fourth &&
- test_must_fail git rebase --onto HEAD^^ HEAD^ HEAD^0 &&
+ test_must_fail git rebase --onto HEAD^^ HEAD^ HEAD^0 2>actual_output &&
git ls-files -s submodule >actual &&
(
cd submodule &&
@@ -112,7 +124,12 @@ test_expect_success 'rebasing submodule that should conflict' '
echo "160000 $(git rev-parse HEAD^^) 2 submodule" &&
echo "160000 $(git rev-parse HEAD) 3 submodule"
) >expect &&
- test_cmp expect actual
+ test_cmp expect actual &&
+ if test "$GIT_TEST_MERGE_ALGORITHM" = ort
+ then
+ sub_expect="go to submodule (submodule), and either merge commit $(git -C submodule rev-parse --short HEAD^0)" &&
+ grep "$sub_expect" actual_output
+ fi
'
test_done
diff --git a/t/t7403-submodule-sync.sh b/t/t7403-submodule-sync.sh
index 7d2ac33..19b6135 100755
--- a/t/t7403-submodule-sync.sh
+++ b/t/t7403-submodule-sync.sh
@@ -11,9 +11,12 @@ These tests exercise the "git submodule sync" subcommand.
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
+ git config --global protocol.file.allow always &&
+
echo file >file &&
git add file &&
test_tick &&
@@ -160,7 +163,7 @@ test_expect_success '"git submodule sync" should update submodule URLs - subdire
cd sub &&
git submodule sync >../../output
) &&
- test_i18ngrep "\\.\\./submodule" output &&
+ test_grep "\\.\\./submodule" output &&
test -d "$(
cd super-clone/submodule &&
git config remote.origin.url
@@ -191,7 +194,7 @@ test_expect_success '"git submodule sync --recursive" should update all submodul
cd sub &&
git submodule sync --recursive >../../output
) &&
- test_i18ngrep "\\.\\./submodule/sub-submodule" output &&
+ test_grep "\\.\\./submodule/sub-submodule" output &&
test -d "$(
cd super-clone/submodule &&
git config remote.origin.url
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index 11cccbb..8491b8c 100755
--- a/t/t7406-submodule-update.sh
+++ b/t/t7406-submodule-update.sh
@@ -25,6 +25,7 @@ compare_head()
test_expect_success 'setup a submodule tree' '
+ git config --global protocol.file.allow always &&
echo file > file &&
git add file &&
test_tick &&
@@ -205,8 +206,18 @@ test_expect_success 'submodule update should fail due to local changes' '
(cd submodule &&
compare_head
) &&
- test_must_fail git submodule update submodule
- )
+ test_must_fail git submodule update submodule 2>../actual.raw
+ ) &&
+ sed "s/^> //" >expect <<-\EOF &&
+ > error: Your local changes to the following files would be overwritten by checkout:
+ > file
+ > Please commit your changes or stash them before you switch branches.
+ > Aborting
+ > fatal: Unable to checkout OID in submodule path '\''submodule'\''
+ EOF
+ sed -e "s/checkout $SQ[^$SQ]*$SQ/checkout OID/" <actual.raw >actual &&
+ test_cmp expect actual
+
'
test_expect_success 'submodule update should throw away changes with --force ' '
(cd super &&
@@ -660,6 +671,39 @@ test_expect_success 'submodule update --init skips submodule with update=none' '
)
'
+test_expect_success 'submodule update with pathspec warns against uninitialized ones' '
+ test_when_finished "rm -fr selective" &&
+ git clone super selective &&
+ (
+ cd selective &&
+ git submodule init submodule &&
+
+ git submodule update submodule 2>err &&
+ ! grep "Submodule path .* not initialized" err &&
+
+ git submodule update rebasing 2>err &&
+ grep "Submodule path .rebasing. not initialized" err &&
+
+ test_path_exists submodule/.git &&
+ test_path_is_missing rebasing/.git
+ )
+
+'
+
+test_expect_success 'submodule update without pathspec updates only initialized ones' '
+ test_when_finished "rm -fr selective" &&
+ git clone super selective &&
+ (
+ cd selective &&
+ git submodule init submodule &&
+ git submodule update 2>err &&
+ test_path_exists submodule/.git &&
+ test_path_is_missing rebasing/.git &&
+ ! grep "Submodule path .* not initialized" err
+ )
+
+'
+
test_expect_success 'submodule update continues after checkout error' '
(cd super &&
git reset --hard HEAD &&
@@ -726,7 +770,7 @@ test_expect_success 'submodule update continues after recursive checkout error'
echo "" > file
)
) &&
- test_must_fail git submodule update --recursive &&
+ test_expect_code 1 git submodule update --recursive &&
(cd submodule2 &&
git rev-parse --verify HEAD >../actual
) &&
@@ -901,7 +945,7 @@ test_expect_success 'submodule update places git-dir in superprojects git-dir re
git clone super_update_r super_update_r2 &&
(cd super_update_r2 &&
git submodule update --init --recursive >actual &&
- test_i18ngrep "Submodule path .submodule/subsubmodule.: checked out" actual &&
+ test_grep "Submodule path .submodule/subsubmodule.: checked out" actual &&
(cd submodule/subsubmodule &&
git log > ../../expected
) &&
@@ -981,7 +1025,7 @@ test_expect_success 'submodule update clone shallow submodule outside of depth'
# unadvertised objects, so restrict this test to v0.
test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 \
git submodule update --init --depth=1 2>actual &&
- test_i18ngrep "Direct fetching of that commit failed." actual &&
+ test_grep "Direct fetching of that commit failed." actual &&
git -C ../submodule config uploadpack.allowReachableSHA1InWant true &&
git submodule update --init --depth=1 >actual &&
git -C submodule log --oneline >out &&
@@ -995,7 +1039,7 @@ test_expect_success 'submodule update --recursive drops module name before recur
git checkout HEAD^
) &&
git submodule update --recursive deeper/submodule >actual &&
- test_i18ngrep "Submodule path .deeper/submodule/subsubmodule.: checked out" actual
+ test_grep "Submodule path .deeper/submodule/subsubmodule.: checked out" actual
)
'
@@ -1031,7 +1075,7 @@ test_expect_success 'submodule update --quiet passes quietness to merge/rebase'
git submodule update --rebase --quiet >out 2>err &&
test_must_be_empty out &&
test_must_be_empty err &&
- git submodule update --rebase -v >out 2>err &&
+ git submodule update --rebase >out 2>err &&
test_file_not_empty out &&
test_must_be_empty err
)
@@ -1061,4 +1105,101 @@ test_expect_success 'submodule update --quiet passes quietness to fetch with a s
)
'
+test_expect_success 'submodule update --filter requires --init' '
+ test_expect_code 129 git -C super submodule update --filter blob:none
+'
+
+test_expect_success 'submodule update --filter sets partial clone settings' '
+ test_when_finished "rm -rf super-filter" &&
+ git clone cloned super-filter &&
+ git -C super-filter submodule update --init --filter blob:none &&
+ test_cmp_config -C super-filter/submodule true remote.origin.promisor &&
+ test_cmp_config -C super-filter/submodule blob:none remote.origin.partialclonefilter
+'
+
+# NEEDSWORK: Clean up the tests so that we can reuse the test setup.
+# Don't reuse the existing repos because the earlier tests have
+# intentionally disruptive configurations.
+test_expect_success 'setup clean recursive superproject' '
+ git init bottom &&
+ test_commit -C bottom "bottom" &&
+ git init middle &&
+ git -C middle submodule add ../bottom bottom &&
+ git -C middle commit -m "middle" &&
+ git init top &&
+ git -C top submodule add ../middle middle &&
+ git -C top commit -m "top" &&
+ git clone --recurse-submodules top top-clean
+'
+
+test_expect_success 'submodule update should skip unmerged submodules' '
+ test_when_finished "rm -fr top-cloned" &&
+ cp -r top-clean top-cloned &&
+
+ # Create an upstream commit in each repo, starting with bottom
+ test_commit -C bottom upstream_commit &&
+ # Create middle commit
+ git -C middle/bottom fetch &&
+ git -C middle/bottom checkout -f FETCH_HEAD &&
+ git -C middle add bottom &&
+ git -C middle commit -m "upstream_commit" &&
+ # Create top commit
+ git -C top/middle fetch &&
+ git -C top/middle checkout -f FETCH_HEAD &&
+ git -C top add middle &&
+ git -C top commit -m "upstream_commit" &&
+
+ # Create a downstream conflict
+ test_commit -C top-cloned/middle/bottom downstream_commit &&
+ git -C top-cloned/middle add bottom &&
+ git -C top-cloned/middle commit -m "downstream_commit" &&
+ git -C top-cloned/middle fetch --recurse-submodules origin &&
+ test_must_fail git -C top-cloned/middle merge origin/main &&
+
+ # Make the update of "middle" a no-op, otherwise we error out
+ # because of its unmerged state
+ test_config -C top-cloned submodule.middle.update !true &&
+ git -C top-cloned submodule update --recursive 2>actual.err &&
+ cat >expect.err <<-\EOF &&
+ Skipping unmerged submodule middle/bottom
+ EOF
+ test_cmp expect.err actual.err
+'
+
+test_expect_success 'submodule update --recursive skip submodules with strategy=none' '
+ test_when_finished "rm -fr top-cloned" &&
+ cp -r top-clean top-cloned &&
+
+ test_commit -C top-cloned/middle/bottom downstream_commit &&
+ git -C top-cloned/middle config submodule.bottom.update none &&
+ git -C top-cloned submodule update --recursive 2>actual.err &&
+ cat >expect.err <<-\EOF &&
+ Skipping submodule '\''middle/bottom'\''
+ EOF
+ test_cmp expect.err actual.err
+'
+
+add_submodule_commit_and_validate () {
+ HASH=$(git rev-parse HEAD) &&
+ git update-index --add --cacheinfo 160000,$HASH,sub &&
+ git commit -m "create submodule" &&
+ echo "160000 commit $HASH sub" >expect &&
+ git ls-tree HEAD -- sub >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'commit with staged submodule change' '
+ add_submodule_commit_and_validate
+'
+
+test_expect_success 'commit with staged submodule change with ignoreSubmodules dirty' '
+ test_config diff.ignoreSubmodules dirty &&
+ add_submodule_commit_and_validate
+'
+
+test_expect_success 'commit with staged submodule change with ignoreSubmodules all' '
+ test_config diff.ignoreSubmodules all &&
+ add_submodule_commit_and_validate
+'
+
test_done
diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh
index e2f110b..8d7b234 100755
--- a/t/t7407-submodule-foreach.sh
+++ b/t/t7407-submodule-foreach.sh
@@ -16,6 +16,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
test_expect_success 'setup a submodule tree' '
+ git config --global protocol.file.allow always &&
echo file > file &&
git add file &&
test_tick &&
@@ -153,6 +154,11 @@ test_expect_success 'use "submodule foreach" to checkout 2nd level submodule' '
)
'
+test_expect_success 'usage: foreach -- --not-an-option' '
+ test_expect_code 1 git submodule foreach -- --not-an-option &&
+ test_expect_code 1 git -C clone2 submodule foreach -- --not-an-option
+'
+
test_expect_success 'use "foreach --recursive" to checkout all submodules' '
(
cd clone2 &&
diff --git a/t/t7408-submodule-reference.sh b/t/t7408-submodule-reference.sh
index a3892f4..d6040e0 100755
--- a/t/t7408-submodule-reference.sh
+++ b/t/t7408-submodule-reference.sh
@@ -17,6 +17,10 @@ test_alternate_is_used () {
test_cmp expect actual
}
+test_expect_success 'setup' '
+ git config --global protocol.file.allow always
+'
+
test_expect_success 'preparing first repository' '
test_create_repo A &&
(
@@ -193,7 +197,19 @@ test_expect_success 'missing nested submodule alternate fails clone and submodul
cd supersuper-clone &&
check_that_two_of_three_alternates_are_used &&
# update of the submodule fails
- test_must_fail git submodule update --init --recursive
+ cat >expect <<-\EOF &&
+ fatal: submodule '\''sub'\'' cannot add alternate: path ... does not exist
+ Failed to clone '\''sub'\''. Retry scheduled
+ fatal: submodule '\''sub-dissociate'\'' cannot add alternate: path ... does not exist
+ Failed to clone '\''sub-dissociate'\''. Retry scheduled
+ fatal: submodule '\''sub'\'' cannot add alternate: path ... does not exist
+ Failed to clone '\''sub'\'' a second time, aborting
+ fatal: Failed to recurse into submodule path ...
+ EOF
+ test_must_fail git submodule update --init --recursive 2>err &&
+ grep -e fatal: -e ^Failed err >actual.raw &&
+ sed -e "s/path $SQ[^$SQ]*$SQ/path .../" <actual.raw >actual &&
+ test_cmp expect actual
)
'
diff --git a/t/t7409-submodule-detached-work-tree.sh b/t/t7409-submodule-detached-work-tree.sh
index e17ac81..574a6fc 100755
--- a/t/t7409-submodule-detached-work-tree.sh
+++ b/t/t7409-submodule-detached-work-tree.sh
@@ -13,8 +13,13 @@ TEST_NO_CREATE_REPO=1
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
+test_expect_success 'setup' '
+ git config --global protocol.file.allow always
+'
+
test_expect_success 'submodule on detached working tree' '
git init --bare remote &&
test_create_repo bundle1 &&
diff --git a/t/t7411-submodule-config.sh b/t/t7411-submodule-config.sh
index ad28e93..31271f8 100755
--- a/t/t7411-submodule-config.sh
+++ b/t/t7411-submodule-config.sh
@@ -12,6 +12,9 @@ from the database and from the worktree works.
TEST_NO_CREATE_REPO=1
. ./test-lib.sh
+test_expect_success 'setup' '
+ git config --global protocol.file.allow always
+'
test_expect_success 'submodule config cache setup' '
mkdir submodule &&
(cd submodule &&
@@ -42,7 +45,7 @@ test_expect_success 'configuration parsing with error' '
(
cd repo &&
test_must_fail test-tool submodule-config "" s 2>actual &&
- test_i18ngrep "bad config" actual
+ test_grep "bad config" actual
)
'
@@ -98,7 +101,7 @@ test_expect_success 'error in history of one submodule config lets continue, std
>actual \
2>actual_stderr &&
test_cmp expect_error actual &&
- test_i18ngrep "submodule-blob $sha1:.gitmodules" actual_stderr >/dev/null
+ test_grep "submodule-blob $sha1:.gitmodules" actual_stderr >/dev/null
)
'
@@ -134,44 +137,44 @@ test_expect_success 'error in history in fetchrecursesubmodule lets continue' '
)
'
-test_expect_success 'reading submodules config from the working tree with "submodule--helper config"' '
+test_expect_success 'reading submodules config from the working tree' '
(cd super &&
echo "../submodule" >expect &&
- git submodule--helper config submodule.submodule.url >actual &&
+ test-tool submodule config-list submodule.submodule.url >actual &&
test_cmp expect actual
)
'
-test_expect_success 'unsetting submodules config from the working tree with "submodule--helper config --unset"' '
+test_expect_success 'unsetting submodules config from the working tree' '
(cd super &&
- git submodule--helper config --unset submodule.submodule.url &&
- git submodule--helper config submodule.submodule.url >actual &&
+ test-tool submodule config-unset submodule.submodule.url &&
+ test-tool submodule config-list submodule.submodule.url >actual &&
test_must_be_empty actual
)
'
-test_expect_success 'writing submodules config with "submodule--helper config"' '
+test_expect_success 'writing submodules config' '
(cd super &&
echo "new_url" >expect &&
- git submodule--helper config submodule.submodule.url "new_url" &&
- git submodule--helper config submodule.submodule.url >actual &&
+ test-tool submodule config-set submodule.submodule.url "new_url" &&
+ test-tool submodule config-list submodule.submodule.url >actual &&
test_cmp expect actual
)
'
-test_expect_success 'overwriting unstaged submodules config with "submodule--helper config"' '
+test_expect_success 'overwriting unstaged submodules config' '
test_when_finished "git -C super checkout .gitmodules" &&
(cd super &&
echo "newer_url" >expect &&
- git submodule--helper config submodule.submodule.url "newer_url" &&
- git submodule--helper config submodule.submodule.url >actual &&
+ test-tool submodule config-set submodule.submodule.url "newer_url" &&
+ test-tool submodule config-list submodule.submodule.url >actual &&
test_cmp expect actual
)
'
test_expect_success 'writeable .gitmodules when it is in the working tree' '
- git -C super submodule--helper config --check-writeable
+ test-tool -C super submodule config-writeable
'
test_expect_success 'writeable .gitmodules when it is nowhere in the repository' '
@@ -180,7 +183,7 @@ test_expect_success 'writeable .gitmodules when it is nowhere in the repository'
(cd super &&
git rm .gitmodules &&
git commit -m "remove .gitmodules from the current branch" &&
- git submodule--helper config --check-writeable
+ test-tool submodule config-writeable
)
'
@@ -188,7 +191,7 @@ test_expect_success 'non-writeable .gitmodules when it is in the index but not i
test_when_finished "git -C super checkout .gitmodules" &&
(cd super &&
rm -f .gitmodules &&
- test_must_fail git submodule--helper config --check-writeable
+ test_must_fail test-tool submodule config-writeable
)
'
@@ -197,7 +200,7 @@ test_expect_success 'non-writeable .gitmodules when it is in the current branch
test_when_finished "git -C super reset --hard $ORIG" &&
(cd super &&
git rm .gitmodules &&
- test_must_fail git submodule--helper config --check-writeable
+ test_must_fail test-tool submodule config-writeable
)
'
@@ -205,11 +208,11 @@ test_expect_success 'reading submodules config from the index when .gitmodules i
ORIG=$(git -C super rev-parse HEAD) &&
test_when_finished "git -C super reset --hard $ORIG" &&
(cd super &&
- git submodule--helper config submodule.submodule.url "staged_url" &&
+ test-tool submodule config-set submodule.submodule.url "staged_url" &&
git add .gitmodules &&
rm -f .gitmodules &&
echo "staged_url" >expect &&
- git submodule--helper config submodule.submodule.url >actual &&
+ test-tool submodule config-list submodule.submodule.url >actual &&
test_cmp expect actual
)
'
@@ -220,7 +223,7 @@ test_expect_success 'reading submodules config from the current branch when .git
(cd super &&
git rm .gitmodules &&
echo "../submodule" >expect &&
- git submodule--helper config submodule.submodule.url >actual &&
+ test-tool submodule config-list submodule.submodule.url >actual &&
test_cmp expect actual
)
'
diff --git a/t/t7412-submodule-absorbgitdirs.sh b/t/t7412-submodule-absorbgitdirs.sh
index 1cfa150..f778321 100755
--- a/t/t7412-submodule-absorbgitdirs.sh
+++ b/t/t7412-submodule-absorbgitdirs.sh
@@ -6,9 +6,11 @@ This test verifies that `git submodue absorbgitdirs` moves a submodules git
directory into the superproject.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup a real submodule' '
+ cwd="$(pwd)" &&
git init sub1 &&
test_commit -C sub1 first &&
git submodule add ./sub1 &&
@@ -17,13 +19,21 @@ test_expect_success 'setup a real submodule' '
'
test_expect_success 'absorb the git dir' '
+ >expect &&
+ >actual &&
>expect.1 &&
>expect.2 &&
>actual.1 &&
>actual.2 &&
git status >expect.1 &&
git -C sub1 rev-parse HEAD >expect.2 &&
- git submodule absorbgitdirs &&
+ cat >expect <<-EOF &&
+ Migrating git directory of '\''sub1'\'' from
+ '\''$cwd/sub1/.git'\'' to
+ '\''$cwd/.git/modules/sub1'\''
+ EOF
+ git submodule absorbgitdirs 2>actual &&
+ test_cmp expect actual &&
git fsck &&
test -f sub1/.git &&
test -d .git/modules/sub1 &&
@@ -36,7 +46,8 @@ test_expect_success 'absorb the git dir' '
test_expect_success 'absorbing does not fail for deinitialized submodules' '
test_when_finished "git submodule update --init" &&
git submodule deinit --all &&
- git submodule absorbgitdirs &&
+ git submodule absorbgitdirs 2>err &&
+ test_must_be_empty err &&
test -d .git/modules/sub1 &&
test -d sub1 &&
! test -e sub1/.git
@@ -55,7 +66,13 @@ test_expect_success 'setup nested submodule' '
test_expect_success 'absorb the git dir in a nested submodule' '
git status >expect.1 &&
git -C sub1/nested rev-parse HEAD >expect.2 &&
- git submodule absorbgitdirs &&
+ cat >expect <<-EOF &&
+ Migrating git directory of '\''sub1/nested'\'' from
+ '\''$cwd/sub1/nested/.git'\'' to
+ '\''$cwd/.git/modules/sub1/modules/nested'\''
+ EOF
+ git submodule absorbgitdirs 2>actual &&
+ test_cmp expect actual &&
test -f sub1/nested/.git &&
test -d .git/modules/sub1/modules/nested &&
git status >actual.1 &&
@@ -86,7 +103,13 @@ test_expect_success 're-setup nested submodule' '
test_expect_success 'absorb the git dir in a nested submodule' '
git status >expect.1 &&
git -C sub1/nested rev-parse HEAD >expect.2 &&
- git submodule absorbgitdirs &&
+ cat >expect <<-EOF &&
+ Migrating git directory of '\''sub1'\'' from
+ '\''$cwd/sub1/.git'\'' to
+ '\''$cwd/.git/modules/sub1'\''
+ EOF
+ git submodule absorbgitdirs 2>actual &&
+ test_cmp expect actual &&
test -f sub1/.git &&
test -f sub1/nested/.git &&
test -d .git/modules/sub1/modules/nested &&
@@ -96,6 +119,27 @@ test_expect_success 'absorb the git dir in a nested submodule' '
test_cmp expect.2 actual.2
'
+test_expect_success 'absorb the git dir outside of primary worktree' '
+ test_when_finished "rm -rf repo-bare.git" &&
+ git clone --bare . repo-bare.git &&
+ test_when_finished "rm -rf repo-wt" &&
+ git -C repo-bare.git worktree add ../repo-wt &&
+
+ test_when_finished "rm -f .gitconfig" &&
+ test_config_global protocol.file.allow always &&
+ git -C repo-wt submodule update --init &&
+ git init repo-wt/sub2 &&
+ test_commit -C repo-wt/sub2 A &&
+ git -C repo-wt submodule add ./sub2 sub2 &&
+ cat >expect <<-EOF &&
+ Migrating git directory of '\''sub2'\'' from
+ '\''$cwd/repo-wt/sub2/.git'\'' to
+ '\''$cwd/repo-bare.git/worktrees/repo-wt/modules/sub2'\''
+ EOF
+ git -C repo-wt submodule absorbgitdirs 2>actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'setup a gitlink with missing .gitmodules entry' '
git init sub2 &&
test_commit -C sub2 first &&
@@ -106,7 +150,11 @@ test_expect_success 'setup a gitlink with missing .gitmodules entry' '
test_expect_success 'absorbing the git dir fails for incomplete submodules' '
git status >expect.1 &&
git -C sub2 rev-parse HEAD >expect.2 &&
- test_must_fail git submodule absorbgitdirs &&
+ cat >expect <<-\EOF &&
+ fatal: could not lookup name for submodule '\''sub2'\''
+ EOF
+ test_must_fail git submodule absorbgitdirs 2>actual &&
+ test_cmp expect actual &&
git -C sub2 fsck &&
test -d sub2/.git &&
git status >actual &&
@@ -126,8 +174,11 @@ test_expect_success 'setup a submodule with multiple worktrees' '
'
test_expect_success 'absorbing fails for a submodule with multiple worktrees' '
- test_must_fail git submodule absorbgitdirs sub3 2>error &&
- test_i18ngrep "not supported" error
+ cat >expect <<-\EOF &&
+ fatal: could not lookup name for submodule '\''sub2'\''
+ EOF
+ test_must_fail git submodule absorbgitdirs 2>actual &&
+ test_cmp expect actual
'
test_done
diff --git a/t/t7413-submodule-is-active.sh b/t/t7413-submodule-is-active.sh
index c8e7e98..887d181 100755
--- a/t/t7413-submodule-is-active.sh
+++ b/t/t7413-submodule-is-active.sh
@@ -1,14 +1,19 @@
#!/bin/sh
-test_description='Test submodule--helper is-active
+test_description='Test with test-tool submodule is-active
-This test verifies that `git submodue--helper is-active` correctly identifies
+This test verifies that `test-tool submodule is-active` correctly identifies
submodules which are "active" and interesting to the user.
+
+This is a unit test of the submodule.c is_submodule_active() function,
+which is also indirectly tested elsewhere.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
+ git config --global protocol.file.allow always &&
git init sub &&
test_commit -C sub initial &&
git init super &&
@@ -25,13 +30,13 @@ test_expect_success 'setup' '
'
test_expect_success 'is-active works with urls' '
- git -C super submodule--helper is-active sub1 &&
- git -C super submodule--helper is-active sub2 &&
+ test-tool -C super submodule is-active sub1 &&
+ test-tool -C super submodule is-active sub2 &&
git -C super config --unset submodule.sub1.URL &&
- test_must_fail git -C super submodule--helper is-active sub1 &&
+ test_must_fail test-tool -C super submodule is-active sub1 &&
git -C super config submodule.sub1.URL ../sub &&
- git -C super submodule--helper is-active sub1
+ test-tool -C super submodule is-active sub1
'
test_expect_success 'is-active works with submodule.<name>.active config' '
@@ -39,11 +44,27 @@ test_expect_success 'is-active works with submodule.<name>.active config' '
test_when_finished "git -C super config submodule.sub1.URL ../sub" &&
git -C super config --bool submodule.sub1.active "false" &&
- test_must_fail git -C super submodule--helper is-active sub1 &&
+ test_must_fail test-tool -C super submodule is-active sub1 &&
git -C super config --bool submodule.sub1.active "true" &&
git -C super config --unset submodule.sub1.URL &&
- git -C super submodule--helper is-active sub1
+ test-tool -C super submodule is-active sub1
+'
+
+test_expect_success 'is-active handles submodule.active config missing a value' '
+ cp super/.git/config super/.git/config.orig &&
+ test_when_finished mv super/.git/config.orig super/.git/config &&
+
+ cat >>super/.git/config <<-\EOF &&
+ [submodule]
+ active
+ EOF
+
+ cat >expect <<-\EOF &&
+ error: missing value for '\''submodule.active'\''
+ EOF
+ test-tool -C super submodule is-active sub1 2>actual &&
+ test_cmp expect actual
'
test_expect_success 'is-active works with basic submodule.active config' '
@@ -53,17 +74,17 @@ test_expect_success 'is-active works with basic submodule.active config' '
git -C super config --add submodule.active "." &&
git -C super config --unset submodule.sub1.URL &&
- git -C super submodule--helper is-active sub1 &&
- git -C super submodule--helper is-active sub2
+ test-tool -C super submodule is-active sub1 &&
+ test-tool -C super submodule is-active sub2
'
test_expect_success 'is-active correctly works with paths that are not submodules' '
test_when_finished "git -C super config --unset-all submodule.active" &&
- test_must_fail git -C super submodule--helper is-active not-a-submodule &&
+ test_must_fail test-tool -C super submodule is-active not-a-submodule &&
git -C super config --add submodule.active "." &&
- test_must_fail git -C super submodule--helper is-active not-a-submodule
+ test_must_fail test-tool -C super submodule is-active not-a-submodule
'
test_expect_success 'is-active works with exclusions in submodule.active config' '
@@ -72,8 +93,8 @@ test_expect_success 'is-active works with exclusions in submodule.active config'
git -C super config --add submodule.active "." &&
git -C super config --add submodule.active ":(exclude)sub1" &&
- test_must_fail git -C super submodule--helper is-active sub1 &&
- git -C super submodule--helper is-active sub2
+ test_must_fail test-tool -C super submodule is-active sub1 &&
+ test-tool -C super submodule is-active sub2
'
test_expect_success 'is-active with submodule.active and submodule.<name>.active' '
@@ -85,8 +106,8 @@ test_expect_success 'is-active with submodule.active and submodule.<name>.active
git -C super config --bool submodule.sub1.active "false" &&
git -C super config --bool submodule.sub2.active "true" &&
- test_must_fail git -C super submodule--helper is-active sub1 &&
- git -C super submodule--helper is-active sub2
+ test_must_fail test-tool -C super submodule is-active sub1 &&
+ test-tool -C super submodule is-active sub2
'
test_expect_success 'is-active, submodule.active and submodule add' '
diff --git a/t/t7414-submodule-mistakes.sh b/t/t7414-submodule-mistakes.sh
index f2e7df5..24f30e3 100755
--- a/t/t7414-submodule-mistakes.sh
+++ b/t/t7414-submodule-mistakes.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='handling of common mistakes people may make with submodules'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'create embedded repository' '
@@ -11,13 +13,13 @@ test_expect_success 'create embedded repository' '
test_expect_success 'git-add on embedded repository warns' '
test_when_finished "git rm --cached -f embed" &&
git add embed 2>stderr &&
- test_i18ngrep warning stderr
+ test_grep warning stderr
'
test_expect_success '--no-warn-embedded-repo suppresses warning' '
test_when_finished "git rm --cached -f embed" &&
git add --no-warn-embedded-repo embed 2>stderr &&
- test_i18ngrep ! warning stderr
+ test_grep ! warning stderr
'
test_expect_success 'no warning when updating entry' '
@@ -25,13 +27,14 @@ test_expect_success 'no warning when updating entry' '
git add embed &&
git -C embed commit --allow-empty -m two &&
git add embed 2>stderr &&
- test_i18ngrep ! warning stderr
+ test_grep ! warning stderr
'
test_expect_success 'submodule add does not warn' '
test_when_finished "git rm -rf submodule .gitmodules" &&
- git submodule add ./embed submodule 2>stderr &&
- test_i18ngrep ! warning stderr
+ git -c protocol.file.allow=always \
+ submodule add ./embed submodule 2>stderr &&
+ test_grep ! warning stderr
'
test_done
diff --git a/t/t7416-submodule-dash-url.sh b/t/t7416-submodule-dash-url.sh
index d21dc8b..2ab566e 100755
--- a/t/t7416-submodule-dash-url.sh
+++ b/t/t7416-submodule-dash-url.sh
@@ -1,8 +1,14 @@
#!/bin/sh
test_description='check handling of disallowed .gitmodule urls'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
+test_expect_success 'setup' '
+ git config --global protocol.file.allow always
+'
+
test_expect_success 'create submodule with protected dash in url' '
git init upstream &&
git -C upstream commit --allow-empty -m base &&
@@ -35,7 +41,7 @@ test_expect_success 'remove ./ protection from .gitmodules url' '
test_expect_success 'clone rejects unprotected dash' '
test_when_finished "rm -rf dst" &&
test_must_fail git clone --recurse-submodules . dst 2>err &&
- test_i18ngrep ignoring err
+ test_grep ignoring err
'
test_expect_success 'fsck rejects unprotected dash' '
@@ -57,7 +63,7 @@ test_expect_success 'trailing backslash is handled correctly' '
mv .new .gitmodules &&
git commit -am "Add testmodule" &&
test_must_fail git clone --verbose --recurse-submodules . dolly 2>err &&
- test_i18ngrep ! "unknown option" err
+ test_grep ! "unknown option" err
'
test_expect_success 'fsck rejects missing URL scheme' '
diff --git a/t/t7417-submodule-path-url.sh b/t/t7417-submodule-path-url.sh
index f0f6b9f..5e3051d 100755
--- a/t/t7417-submodule-path-url.sh
+++ b/t/t7417-submodule-path-url.sh
@@ -6,6 +6,10 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
+test_expect_success 'setup' '
+ git config --global protocol.file.allow always
+'
+
test_expect_success 'create submodule with dash in path' '
git init upstream &&
git -C upstream commit --allow-empty -m base &&
@@ -17,7 +21,7 @@ test_expect_success 'create submodule with dash in path' '
test_expect_success 'clone rejects unprotected dash' '
test_when_finished "rm -rf dst" &&
git clone --recurse-submodules . dst 2>err &&
- test_i18ngrep ignoring err
+ test_grep ignoring err
'
test_expect_success 'fsck rejects unprotected dash' '
@@ -42,7 +46,7 @@ test_expect_success MINGW 'submodule paths disallows trailing spaces' '
git -C super update-ref refs/heads/main $commit &&
test_must_fail git clone --recurse-submodules super dst 2>err &&
- test_i18ngrep "sub " err
+ test_grep "sub " err
'
test_done
diff --git a/t/t7418-submodule-sparse-gitmodules.sh b/t/t7418-submodule-sparse-gitmodules.sh
index 3f7f2718..dde11ec 100755
--- a/t/t7418-submodule-sparse-gitmodules.sh
+++ b/t/t7418-submodule-sparse-gitmodules.sh
@@ -12,8 +12,15 @@ The test setup uses a sparse checkout, however the same scenario can be set up
also by committing .gitmodules and then just removing it from the filesystem.
'
+GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1
+export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
+
. ./test-lib.sh
+test_expect_success 'setup' '
+ git config --global protocol.file.allow always
+'
+
test_expect_success 'sparse checkout setup which hides .gitmodules' '
git init upstream &&
git init submodule &&
@@ -28,8 +35,9 @@ test_expect_success 'sparse checkout setup which hides .gitmodules' '
test_tick &&
git commit -m "Add submodule"
) &&
- git clone upstream super &&
+ git clone --template= upstream super &&
(cd super &&
+ mkdir .git/info &&
cat >.git/info/sparse-checkout <<-\EOF &&
/*
!/.gitmodules
@@ -42,12 +50,12 @@ test_expect_success 'sparse checkout setup which hides .gitmodules' '
test_expect_success 'reading gitmodules config file when it is not checked out' '
echo "../submodule" >expect &&
- git -C super submodule--helper config submodule.submodule.url >actual &&
+ test-tool -C super submodule config-list submodule.submodule.url >actual &&
test_cmp expect actual
'
test_expect_success 'not writing gitmodules config file when it is not checked out' '
- test_must_fail git -C super submodule--helper config submodule.submodule.url newurl &&
+ test_must_fail test-tool -C super submodule config-set submodule.submodule.url newurl &&
test_path_is_missing super/.gitmodules
'
diff --git a/t/t7419-submodule-set-branch.sh b/t/t7419-submodule-set-branch.sh
index 3b925c3..a5d1bc5 100755
--- a/t/t7419-submodule-set-branch.sh
+++ b/t/t7419-submodule-set-branch.sh
@@ -9,9 +9,18 @@ This test verifies that the set-branch subcommand of git-submodule is working
as expected.
'
+TEST_PASSES_SANITIZE_LEAK=true
TEST_NO_CREATE_REPO=1
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
. ./test-lib.sh
+test_expect_success 'setup' '
+ git config --global protocol.file.allow always
+'
+
test_expect_success 'submodule config cache setup' '
mkdir submodule &&
(cd submodule &&
@@ -22,26 +31,28 @@ test_expect_success 'submodule config cache setup' '
git checkout -b topic &&
echo b >a &&
git add . &&
- git commit -mb
+ git commit -mb &&
+ git checkout main
) &&
mkdir super &&
(cd super &&
git init &&
git submodule add ../submodule &&
- git commit -m "add submodule"
+ git submodule add --name thename ../submodule thepath &&
+ git commit -m "add submodules"
)
'
test_expect_success 'ensure submodule branch is unset' '
(cd super &&
- ! grep branch .gitmodules
+ test_cmp_config "" -f .gitmodules --default "" submodule.submodule.branch
)
'
test_expect_success 'test submodule set-branch --branch' '
(cd super &&
git submodule set-branch --branch topic submodule &&
- grep "branch = topic" .gitmodules &&
+ test_cmp_config topic -f .gitmodules submodule.submodule.branch &&
git submodule update --remote &&
cat <<-\EOF >expect &&
b
@@ -52,13 +63,12 @@ test_expect_success 'test submodule set-branch --branch' '
'
test_expect_success 'test submodule set-branch --default' '
- test_commit -C submodule c &&
(cd super &&
git submodule set-branch --default submodule &&
- ! grep branch .gitmodules &&
+ test_cmp_config "" -f .gitmodules --default "" submodule.submodule.branch &&
git submodule update --remote &&
cat <<-\EOF >expect &&
- c
+ a
EOF
git -C submodule show -s --pretty=%s >actual &&
test_cmp expect actual
@@ -66,10 +76,9 @@ test_expect_success 'test submodule set-branch --default' '
'
test_expect_success 'test submodule set-branch -b' '
- test_commit -C submodule b &&
(cd super &&
git submodule set-branch -b topic submodule &&
- grep "branch = topic" .gitmodules &&
+ test_cmp_config topic -f .gitmodules submodule.submodule.branch &&
git submodule update --remote &&
cat <<-\EOF >expect &&
b
@@ -80,17 +89,43 @@ test_expect_success 'test submodule set-branch -b' '
'
test_expect_success 'test submodule set-branch -d' '
- test_commit -C submodule d &&
(cd super &&
git submodule set-branch -d submodule &&
- ! grep branch .gitmodules &&
+ test_cmp_config "" -f .gitmodules --default "" submodule.submodule.branch &&
git submodule update --remote &&
cat <<-\EOF >expect &&
- d
+ a
EOF
git -C submodule show -s --pretty=%s >actual &&
test_cmp expect actual
)
'
+test_expect_success 'test submodule set-branch --branch with named submodule' '
+ (cd super &&
+ git submodule set-branch --branch topic thepath &&
+ test_cmp_config topic -f .gitmodules submodule.thename.branch &&
+ test_cmp_config "" -f .gitmodules --default "" submodule.thepath.branch &&
+ git submodule update --remote &&
+ cat <<-\EOF >expect &&
+ b
+ EOF
+ git -C thepath show -s --pretty=%s >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'test submodule set-branch --default with named submodule' '
+ (cd super &&
+ git submodule set-branch --default thepath &&
+ test_cmp_config "" -f .gitmodules --default "" submodule.thename.branch &&
+ git submodule update --remote &&
+ cat <<-\EOF >expect &&
+ a
+ EOF
+ git -C thepath show -s --pretty=%s >actual &&
+ test_cmp expect actual
+ )
+'
+
test_done
diff --git a/t/t7420-submodule-set-url.sh b/t/t7420-submodule-set-url.sh
index ef0cb6e..bf7f15e 100755
--- a/t/t7420-submodule-set-url.sh
+++ b/t/t7420-submodule-set-url.sh
@@ -12,6 +12,10 @@ as expected.
TEST_NO_CREATE_REPO=1
. ./test-lib.sh
+test_expect_success 'setup' '
+ git config --global protocol.file.allow always
+'
+
test_expect_success 'submodule config cache setup' '
mkdir submodule &&
(
@@ -21,17 +25,26 @@ test_expect_success 'submodule config cache setup' '
git add file &&
git commit -ma
) &&
+ mkdir namedsubmodule &&
+ (
+ cd namedsubmodule &&
+ git init &&
+ echo 1 >file &&
+ git add file &&
+ git commit -m1
+ ) &&
mkdir super &&
(
cd super &&
git init &&
git submodule add ../submodule &&
- git commit -m "add submodule"
+ git submodule add --name thename ../namedsubmodule thepath &&
+ git commit -m "add submodules"
)
'
test_expect_success 'test submodule set-url' '
- # add a commit and move the submodule (change the url)
+ # add commits and move the submodules (change the urls)
(
cd submodule &&
echo b >>file &&
@@ -40,15 +53,28 @@ test_expect_success 'test submodule set-url' '
) &&
mv submodule newsubmodule &&
+ (
+ cd namedsubmodule &&
+ echo 2 >>file &&
+ git add file &&
+ git commit -m2
+ ) &&
+ mv namedsubmodule newnamedsubmodule &&
+
git -C newsubmodule show >expect &&
+ git -C newnamedsubmodule show >>expect &&
(
cd super &&
test_must_fail git submodule update --remote &&
git submodule set-url submodule ../newsubmodule &&
- grep -F "url = ../newsubmodule" .gitmodules &&
+ test_cmp_config ../newsubmodule -f .gitmodules submodule.submodule.url &&
+ git submodule set-url thepath ../newnamedsubmodule &&
+ test_cmp_config ../newnamedsubmodule -f .gitmodules submodule.thename.url &&
+ test_cmp_config "" -f .gitmodules --default "" submodule.thepath.url &&
git submodule update --remote
) &&
git -C super/submodule show >actual &&
+ git -C super/thepath show >>actual &&
test_cmp expect actual
'
diff --git a/t/t7421-submodule-summary-add.sh b/t/t7421-submodule-summary-add.sh
index b070f13..ce64d8b 100755
--- a/t/t7421-submodule-summary-add.sh
+++ b/t/t7421-submodule-summary-add.sh
@@ -12,6 +12,10 @@ while making sure to add submodules using `git submodule add` instead of
. ./test-lib.sh
+test_expect_success 'setup' '
+ git config --global protocol.file.allow always
+'
+
test_expect_success 'summary test environment setup' '
git init sm &&
test_commit -C sm "add file" file file-content file-tag &&
diff --git a/t/t7422-submodule-output.sh b/t/t7422-submodule-output.sh
new file mode 100755
index 0000000..ab946ec
--- /dev/null
+++ b/t/t7422-submodule-output.sh
@@ -0,0 +1,170 @@
+#!/bin/sh
+
+test_description='submodule --cached, --quiet etc. output'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-t3100.sh
+
+setup_sub () {
+ local d="$1" &&
+ shift &&
+ git $@ clone . "$d" &&
+ git $@ submodule add ./"$d"
+}
+
+normalize_status () {
+ sed -e 's/-g[0-9a-f]*/-gHASH/'
+}
+
+test_expect_success 'setup' '
+ test_commit A &&
+ test_commit B &&
+ setup_sub S &&
+ setup_sub S.D &&
+ setup_sub S.C &&
+ setup_sub S.C.D &&
+ setup_sub X &&
+ git add S* &&
+ test_commit C &&
+
+ # recursive in X/
+ git -C X pull &&
+ GIT_ALLOW_PROTOCOL=file git -C X submodule update --init &&
+
+ # dirty
+ for d in S.D X/S.D
+ do
+ echo dirty >"$d"/A.t || return 1
+ done &&
+
+ # commit (for --cached)
+ for d in S.C* X/S.C*
+ do
+ git -C "$d" reset --hard A || return 1
+ done &&
+
+ # dirty
+ for d in S*.D X/S*.D
+ do
+ echo dirty >"$d/C2.t" || return 1
+ done &&
+
+ for ref in A B C
+ do
+ # Not different with SHA-1 and SHA-256, just (ab)using
+ # test_oid_cache as a variable bag to avoid using
+ # $(git rev-parse ...).
+ oid=$(git rev-parse $ref) &&
+ test_oid_cache <<-EOF || return 1
+ $ref sha1:$oid
+ $ref sha256:$oid
+ EOF
+ done
+'
+
+for opts in "" "status"
+do
+ test_expect_success "git submodule $opts" '
+ sed -e "s/^>//" >expect <<-EOF &&
+ > $(test_oid B) S (B)
+ >+$(test_oid A) S.C (A)
+ >+$(test_oid A) S.C.D (A)
+ > $(test_oid B) S.D (B)
+ >+$(test_oid C) X (C)
+ EOF
+ git submodule $opts >actual.raw &&
+ normalize_status <actual.raw >actual &&
+ test_cmp expect actual
+ '
+done
+
+for opts in \
+ "status --recursive"
+do
+ test_expect_success "git submodule $opts" '
+ sed -e "s/^>//" >expect <<-EOF &&
+ > $(test_oid B) S (B)
+ >+$(test_oid A) S.C (A)
+ >+$(test_oid A) S.C.D (A)
+ > $(test_oid B) S.D (B)
+ >+$(test_oid C) X (C)
+ > $(test_oid B) X/S (B)
+ >+$(test_oid A) X/S.C (A)
+ >+$(test_oid A) X/S.C.D (A)
+ > $(test_oid B) X/S.D (B)
+ > $(test_oid B) X/X (B)
+ EOF
+ git submodule $opts >actual.raw &&
+ normalize_status <actual.raw >actual &&
+ test_cmp expect actual
+ '
+done
+
+for opts in \
+ "--quiet" \
+ "--quiet status" \
+ "status --quiet"
+do
+ test_expect_success "git submodule $opts" '
+ git submodule $opts >out &&
+ test_must_be_empty out
+ '
+done
+
+for opts in \
+ "--cached" \
+ "--cached status" \
+ "status --cached"
+do
+ test_expect_success "git submodule $opts" '
+ sed -e "s/^>//" >expect <<-EOF &&
+ > $(test_oid B) S (B)
+ >+$(test_oid B) S.C (B)
+ >+$(test_oid B) S.C.D (B)
+ > $(test_oid B) S.D (B)
+ >+$(test_oid B) X (B)
+ EOF
+ git submodule $opts >actual.raw &&
+ normalize_status <actual.raw >actual &&
+ test_cmp expect actual
+ '
+done
+
+for opts in \
+ "--cached --quiet" \
+ "--cached --quiet status" \
+ "--cached status --quiet" \
+ "--quiet status --cached" \
+ "status --cached --quiet"
+do
+ test_expect_success "git submodule $opts" '
+ git submodule $opts >out &&
+ test_must_be_empty out
+ '
+done
+
+for opts in \
+ "status --cached --recursive" \
+ "--cached status --recursive"
+do
+ test_expect_success "git submodule $opts" '
+ sed -e "s/^>//" >expect <<-EOF &&
+ > $(test_oid B) S (B)
+ >+$(test_oid B) S.C (B)
+ >+$(test_oid B) S.C.D (B)
+ > $(test_oid B) S.D (B)
+ >+$(test_oid B) X (B)
+ > $(test_oid B) X/S (B)
+ >+$(test_oid B) X/S.C (B)
+ >+$(test_oid B) X/S.C.D (B)
+ > $(test_oid B) X/S.D (B)
+ > $(test_oid B) X/X (B)
+ EOF
+ git submodule $opts >actual.raw &&
+ normalize_status <actual.raw >actual &&
+ test_cmp expect actual
+ '
+done
+
+test_done
diff --git a/t/t7450-bad-git-dotfiles.sh b/t/t7450-bad-git-dotfiles.sh
index 41706c1..46d4fb0 100755
--- a/t/t7450-bad-git-dotfiles.sh
+++ b/t/t7450-bad-git-dotfiles.sh
@@ -12,16 +12,22 @@ Such as:
- symlinked .gitmodules, etc
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-pack.sh
+test_expect_success 'setup' '
+ git config --global protocol.file.allow always
+'
+
test_expect_success 'check names' '
cat >expect <<-\EOF &&
valid
valid/with/paths
EOF
- git submodule--helper check-name >actual <<-\EOF &&
+ test-tool submodule check-name >actual <<-\EOF &&
valid
valid/with/paths
@@ -39,6 +45,32 @@ test_expect_success 'check names' '
test_cmp expect actual
'
+test_expect_success 'check urls' '
+ cat >expect <<-\EOF &&
+ ./bar/baz/foo.git
+ https://example.com/foo.git
+ http://example.com:80/deeper/foo.git
+ EOF
+
+ test-tool submodule check-url >actual <<-\EOF &&
+ ./bar/baz/foo.git
+ https://example.com/foo.git
+ http://example.com:80/deeper/foo.git
+ -a./foo
+ ../../..//test/foo.git
+ ../../../../../:localhost:8080/foo.git
+ ..\../.\../:example.com/foo.git
+ ./%0ahost=example.com/foo.git
+ https://one.example.com/evil?%0ahost=two.example.com
+ https:///example.com/foo.git
+ http://example.com:test/foo.git
+ https::example.com/foo.git
+ http:::example.com/foo.git
+ EOF
+
+ test_cmp expect actual
+'
+
test_expect_success 'create innocent subrepo' '
git init innocent &&
git -C innocent commit --allow-empty -m foo
@@ -232,7 +264,7 @@ test_expect_success 'fsck detects non-blob .gitmodules' '
git ls-tree HEAD | sed s/subdir/.gitmodules/ | git mktree &&
test_must_fail git fsck 2>output &&
- test_i18ngrep gitmodulesBlob output
+ test_grep gitmodulesBlob output
)
'
@@ -246,8 +278,8 @@ test_expect_success 'fsck detects corrupt .gitmodules' '
git commit -m "broken gitmodules" &&
git fsck 2>output &&
- test_i18ngrep gitmodulesParse output &&
- test_i18ngrep ! "bad config" output
+ test_grep gitmodulesParse output &&
+ test_grep ! "bad config" output
)
'
@@ -269,7 +301,7 @@ test_expect_success WINDOWS 'prevent git~1 squatting on Windows' '
hash="$(echo x | git hash-object -w --stdin)" &&
test_must_fail git update-index --add \
--cacheinfo 160000,$rev,d\\a 2>err &&
- test_i18ngrep "Invalid path" err &&
+ test_grep "Invalid path" err &&
git -c core.protectNTFS=false update-index --add \
--cacheinfo 100644,$modules,.gitmodules \
--cacheinfo 160000,$rev,c \
@@ -283,7 +315,7 @@ test_expect_success WINDOWS 'prevent git~1 squatting on Windows' '
then
test_must_fail git -c core.protectNTFS=false \
clone --recurse-submodules squatting squatting-clone 2>err &&
- test_i18ngrep -e "directory not empty" -e "not an empty directory" err &&
+ test_grep -e "directory not empty" -e "not an empty directory" err &&
! grep gitdir squatting-clone/d/a/git~2
fi
'
@@ -308,7 +340,7 @@ test_expect_success 'git dirs of sibling submodules must not be nested' '
git commit -m nested
) &&
test_must_fail git clone --recurse-submodules nested clone 2>err &&
- test_i18ngrep "is inside git dir" err
+ test_grep "is inside git dir" err
'
test_done
diff --git a/t/t7500-commit-template-squash-signoff.sh b/t/t7500-commit-template-squash-signoff.sh
index 8dd0f98..4dca8d9 100755
--- a/t/t7500-commit-template-squash-signoff.sh
+++ b/t/t7500-commit-template-squash-signoff.sh
@@ -359,14 +359,14 @@ test_expect_success '--fixup=reword: ignores staged changes' '
test_expect_success '--fixup=reword: error out with -m option' '
commit_for_rebase_autosquash_setup &&
- echo "fatal: cannot combine -m with --fixup:reword" >expect &&
+ echo "fatal: options '\''-m'\'' and '\''--fixup:reword'\'' cannot be used together" >expect &&
test_must_fail git commit --fixup=reword:HEAD~ -m "reword commit message" 2>actual &&
test_cmp expect actual
'
test_expect_success '--fixup=amend: error out with -m option' '
commit_for_rebase_autosquash_setup &&
- echo "fatal: cannot combine -m with --fixup:amend" >expect &&
+ echo "fatal: options '\''-m'\'' and '\''--fixup:amend'\'' cannot be used together" >expect &&
test_must_fail git commit --fixup=amend:HEAD~ -m "amend commit message" 2>actual &&
test_cmp expect actual
'
@@ -421,8 +421,9 @@ test_expect_success 'amend! commit allows empty commit msg body with --allow-emp
test_fixup_reword_opt () {
test_expect_success "--fixup=reword: incompatible with $1" "
- echo 'fatal: reword option of --fixup is mutually exclusive with'\
- '--patch/--interactive/--all/--include/--only' >expect &&
+ echo 'fatal: reword option of '\''--fixup'\'' and' \
+ ''\''--patch/--interactive/--all/--include/--only'\' \
+ 'cannot be used together' >expect &&
test_must_fail git commit --fixup=reword:HEAD~ $1 2>actual &&
test_cmp expect actual
"
@@ -435,13 +436,13 @@ done
test_expect_success '--fixup=reword: give error with pathsec' '
commit_for_rebase_autosquash_setup &&
- echo "fatal: cannot combine reword option of --fixup with path '\''foo'\''" >expect &&
+ echo "fatal: reword option of '\''--fixup'\'' and path '\''foo'\'' cannot be used together" >expect &&
test_must_fail git commit --fixup=reword:HEAD~ -- foo 2>actual &&
test_cmp expect actual
'
test_expect_success '--fixup=reword: -F give error message' '
- echo "fatal: Only one of -c/-C/-F/--fixup can be used." >expect &&
+ echo "fatal: options '\''-F'\'' and '\''--fixup'\'' cannot be used together" >expect &&
test_must_fail git commit --fixup=reword:HEAD~ -F msg 2>actual &&
test_cmp expect actual
'
@@ -554,7 +555,7 @@ test_expect_success 'commit without staging files fails and displays hints' '
git commit -m initial &&
echo "changes" >>file &&
test_must_fail git commit -m update >actual &&
- test_i18ngrep "no changes added to commit (use \"git add\" and/or \"git commit -a\")" actual
+ test_grep "no changes added to commit (use \"git add\" and/or \"git commit -a\")" actual
'
test_done
diff --git a/t/t7501-commit-basic-functionality.sh b/t/t7501-commit-basic-functionality.sh
index 512ae27..cc12f99 100755
--- a/t/t7501-commit-basic-functionality.sh
+++ b/t/t7501-commit-basic-functionality.sh
@@ -3,8 +3,7 @@
# Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>
#
-# FIXME: Test the various index usages, -i and -o, test reflog,
-# signoff
+# FIXME: Test the various index usages, test reflog
test_description='git commit'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
@@ -21,7 +20,7 @@ test_expect_success 'initial status' '
echo bongo bongo >file &&
git add file &&
git status >actual &&
- test_i18ngrep "No commits yet" actual
+ test_grep "No commits yet" actual
'
test_expect_success 'fail initial amend' '
@@ -92,6 +91,20 @@ test_expect_success '--long fails with nothing to commit' '
test_must_fail git commit -m initial --long
'
+test_expect_success 'fail to commit untracked file (even with --include/--only)' '
+ echo content >baz &&
+ error="error: pathspec .baz. did not match any file(s) known to git" &&
+
+ test_must_fail git commit -m "baz" baz 2>err &&
+ test_grep -e "$error" err &&
+
+ test_must_fail git commit --only -m "baz" baz 2>err &&
+ test_grep -e "$error" err &&
+
+ test_must_fail git commit --include -m "baz" baz 2>err &&
+ test_grep -e "$error" err
+'
+
test_expect_success 'setup: non-initial commit' '
echo bongo bongo bongo >file &&
git commit -m next -a
@@ -117,6 +130,51 @@ test_expect_success '--long with stuff to commit returns ok' '
git commit -m next -a --long
'
+for opt in "" "-o" "--only"
+do
+ test_expect_success 'exclude additional staged changes when given pathspec' '
+ echo content >>file &&
+ echo content >>baz &&
+ git add baz &&
+ git commit $opt -m "file" file &&
+
+ git diff --name-only >actual &&
+ test_must_be_empty actual &&
+
+ test_write_lines baz >expect &&
+ git diff --name-only --cached >actual &&
+ test_cmp expect actual &&
+
+ test_write_lines file >expect &&
+ git diff --name-only HEAD^ HEAD >actual &&
+ test_cmp expect actual
+ '
+done
+
+test_expect_success '-i/--include includes staged changes' '
+ echo content >>file &&
+ echo content >>baz &&
+ git add file &&
+
+ # baz is in the index, therefore, it will be committed
+ git commit --include -m "file and baz" baz &&
+
+ git diff --name-only HEAD >remaining &&
+ test_must_be_empty remaining &&
+
+ test_write_lines baz file >expect &&
+ git diff --name-only HEAD^ HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--include and --only do not mix' '
+ test_when_finished "git reset --hard" &&
+ echo content >>file &&
+ echo content >>baz &&
+ test_must_fail git commit --include --only -m "file baz" file baz 2>actual &&
+ test_grep -e "fatal: options .-i/--include. and .-o/--only. cannot be used together" actual
+'
+
test_expect_success 'commit message from non-existing file' '
echo more bongo: bongo bongo bongo bongo >file &&
test_must_fail git commit -F gah -a
@@ -141,7 +199,7 @@ test_expect_success 'template "emptyness" check does not kick in with -F' '
test_expect_success 'template "emptyness" check' '
git checkout HEAD file && echo >>file && git add file &&
test_must_fail git commit -t file 2>err &&
- test_i18ngrep "did not edit" err
+ test_grep "did not edit" err
'
test_expect_success 'setup: commit message from file' '
@@ -389,6 +447,28 @@ test_expect_success 'amend commit to fix date' '
'
+test_expect_success 'amend commit to add signoff' '
+
+ test_commit "msg" file content &&
+ git commit --amend --signoff &&
+ test_commit_message HEAD <<-EOF
+ msg
+
+ Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+ EOF
+'
+
+test_expect_success 'amend does not add signoff if it already exists' '
+
+ test_commit --signoff "tenor" file newcontent &&
+ git commit --amend --signoff &&
+ test_commit_message HEAD <<-EOF
+ tenor
+
+ Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+ EOF
+'
+
test_expect_success 'commit mentions forced date in output' '
git commit --amend --date=2010-01-02T03:04:05 >output &&
grep "Date: *Sat Jan 2 03:04:05 2010" output
@@ -667,14 +747,11 @@ test_expect_success 'amend can copy notes' '
test_expect_success 'commit a file whose name is a dash' '
git reset --hard &&
- for i in 1 2 3 4 5
- do
- echo $i
- done >./- &&
+ test_write_lines 1 2 3 4 5 >./- &&
git add ./- &&
test_tick &&
git commit -m "add dash" >output </dev/null &&
- test_i18ngrep " changed, 5 insertions" output
+ test_grep " changed, 5 insertions" output
'
test_expect_success '--only works on to-be-born branch' '
diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
index 38a532d..b37e201 100755
--- a/t/t7502-commit-porcelain.sh
+++ b/t/t7502-commit-porcelain.sh
@@ -466,6 +466,43 @@ test_expect_success 'commit --trailer with -c and command' '
test_cmp expected actual
'
+test_expect_success 'commit --trailer not confused by --- separator' '
+ cat >msg <<-\EOF &&
+ subject
+
+ body with dashes
+ ---
+ in it
+ EOF
+ git commit --allow-empty --trailer="my-trailer: value" -F msg &&
+ {
+ cat msg &&
+ echo &&
+ echo "my-trailer: value"
+ } >expected &&
+ git cat-file commit HEAD >commit.msg &&
+ sed -e "1,/^\$/d" commit.msg >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with --verbose' '
+ cat >msg <<-\EOF &&
+ subject
+
+ body
+ EOF
+ GIT_EDITOR=: git commit --edit -F msg --allow-empty \
+ --trailer="my-trailer: value" --verbose &&
+ {
+ cat msg &&
+ echo &&
+ echo "my-trailer: value"
+ } >expected &&
+ git cat-file commit HEAD >commit.msg &&
+ sed -e "1,/^\$/d" commit.msg >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'multiple -m' '
>negative &&
@@ -687,18 +724,23 @@ test_expect_success 'cleanup commit message (whitespace config, -m)' '
test_expect_success 'message shows author when it is not equal to committer' '
echo >>negative &&
git commit -e -m "sample" -a &&
- test_i18ngrep \
+ test_grep \
"^# Author: *A U Thor <author@example.com>\$" \
.git/COMMIT_EDITMSG
'
test_expect_success 'message shows date when it is explicitly set' '
git commit --allow-empty -e -m foo --date="2010-01-02T03:04:05" &&
- test_i18ngrep \
+ test_grep \
"^# Date: *Sat Jan 2 03:04:05 2010 +0000" \
.git/COMMIT_EDITMSG
'
+test_expect_success 'message does not have multiple scissors lines' '
+ git commit --cleanup=scissors -v --allow-empty -e -m foo &&
+ test $(grep -c -e "--- >8 ---" .git/COMMIT_EDITMSG) -eq 1
+'
+
test_expect_success AUTOIDENT 'message shows committer when it is automatic' '
echo >>negative &&
@@ -709,7 +751,7 @@ test_expect_success AUTOIDENT 'message shows committer when it is automatic' '
) &&
# the ident is calculated from the system, so we cannot
# check the actual value, only that it is there
- test_i18ngrep "^# Committer: " .git/COMMIT_EDITMSG
+ test_grep "^# Committer: " .git/COMMIT_EDITMSG
'
write_script .git/FAKE_EDITOR <<EOF
@@ -841,9 +883,9 @@ try_commit () {
GIT_EDITOR=.git/FAKE_EDITOR git commit -a $* $use_template &&
case "$use_template" in
'')
- test_i18ngrep ! "^## Custom template" .git/COMMIT_EDITMSG ;;
+ test_grep ! "^## Custom template" .git/COMMIT_EDITMSG ;;
*)
- test_i18ngrep "^## Custom template" .git/COMMIT_EDITMSG ;;
+ test_grep "^## Custom template" .git/COMMIT_EDITMSG ;;
esac
}
@@ -851,53 +893,53 @@ try_commit_status_combo () {
test_expect_success 'commit' '
try_commit "" &&
- test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+ test_grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
'
test_expect_success 'commit --status' '
try_commit --status &&
- test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+ test_grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
'
test_expect_success 'commit --no-status' '
try_commit --no-status &&
- test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
+ test_grep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
'
test_expect_success 'commit with commit.status = yes' '
test_config commit.status yes &&
try_commit "" &&
- test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+ test_grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
'
test_expect_success 'commit with commit.status = no' '
test_config commit.status no &&
try_commit "" &&
- test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
+ test_grep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
'
test_expect_success 'commit --status with commit.status = yes' '
test_config commit.status yes &&
try_commit --status &&
- test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+ test_grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
'
test_expect_success 'commit --no-status with commit.status = yes' '
test_config commit.status yes &&
try_commit --no-status &&
- test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
+ test_grep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
'
test_expect_success 'commit --status with commit.status = no' '
test_config commit.status no &&
try_commit --status &&
- test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+ test_grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
'
test_expect_success 'commit --no-status with commit.status = no' '
test_config commit.status no &&
try_commit --no-status &&
- test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
+ test_grep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
'
}
@@ -911,13 +953,13 @@ try_commit_status_combo
test_expect_success 'commit --status with custom comment character' '
test_config core.commentchar ";" &&
try_commit --status &&
- test_i18ngrep "^; Changes to be committed:" .git/COMMIT_EDITMSG
+ test_grep "^; Changes to be committed:" .git/COMMIT_EDITMSG
'
test_expect_success 'switch core.commentchar' '
test_commit "#foo" foo &&
GIT_EDITOR=.git/FAKE_EDITOR git -c core.commentChar=auto commit --amend &&
- test_i18ngrep "^; Changes to be committed:" .git/COMMIT_EDITMSG
+ test_grep "^; Changes to be committed:" .git/COMMIT_EDITMSG
'
test_expect_success 'switch core.commentchar but out of options' '
diff --git a/t/t7503-pre-commit-and-pre-merge-commit-hooks.sh b/t/t7503-pre-commit-and-pre-merge-commit-hooks.sh
index 606d8d0..aa004b7 100755
--- a/t/t7503-pre-commit-and-pre-merge-commit-hooks.sh
+++ b/t/t7503-pre-commit-and-pre-merge-commit-hooks.sh
@@ -5,39 +5,9 @@ test_description='pre-commit and pre-merge-commit hooks'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
-HOOKDIR="$(git rev-parse --git-dir)/hooks"
-PRECOMMIT="$HOOKDIR/pre-commit"
-PREMERGE="$HOOKDIR/pre-merge-commit"
-
-# Prepare sample scripts that write their $0 to actual_hooks
-test_expect_success 'sample script setup' '
- mkdir -p "$HOOKDIR" &&
- write_script "$HOOKDIR/success.sample" <<-\EOF &&
- echo $0 >>actual_hooks
- exit 0
- EOF
- write_script "$HOOKDIR/fail.sample" <<-\EOF &&
- echo $0 >>actual_hooks
- exit 1
- EOF
- write_script "$HOOKDIR/non-exec.sample" <<-\EOF &&
- echo $0 >>actual_hooks
- exit 1
- EOF
- chmod -x "$HOOKDIR/non-exec.sample" &&
- write_script "$HOOKDIR/require-prefix.sample" <<-\EOF &&
- echo $0 >>actual_hooks
- test $GIT_PREFIX = "success/"
- EOF
- write_script "$HOOKDIR/check-author.sample" <<-\EOF
- echo $0 >>actual_hooks
- test "$GIT_AUTHOR_NAME" = "New Author" &&
- test "$GIT_AUTHOR_EMAIL" = "newauthor@example.com"
- EOF
-'
-
test_expect_success 'root commit' '
echo "root" >file &&
git add file &&
@@ -96,10 +66,16 @@ test_expect_success '--no-verify with no hook (merge)' '
test_path_is_missing actual_hooks
'
+setup_success_hook () {
+ test_when_finished "rm -f actual_hooks expected_hooks" &&
+ echo "$1" >expected_hooks &&
+ test_hook "$1" <<-EOF
+ echo $1 >>actual_hooks
+ EOF
+}
+
test_expect_success 'with succeeding hook' '
- test_when_finished "rm -f \"$PRECOMMIT\" expected_hooks actual_hooks" &&
- cp "$HOOKDIR/success.sample" "$PRECOMMIT" &&
- echo "$PRECOMMIT" >expected_hooks &&
+ setup_success_hook "pre-commit" &&
echo "more" >>file &&
git add file &&
git commit -m "more" &&
@@ -107,9 +83,7 @@ test_expect_success 'with succeeding hook' '
'
test_expect_success 'with succeeding hook (merge)' '
- test_when_finished "rm -f \"$PREMERGE\" expected_hooks actual_hooks" &&
- cp "$HOOKDIR/success.sample" "$PREMERGE" &&
- echo "$PREMERGE" >expected_hooks &&
+ setup_success_hook "pre-merge-commit" &&
git checkout side &&
git merge -m "merge main" main &&
git checkout main &&
@@ -117,17 +91,14 @@ test_expect_success 'with succeeding hook (merge)' '
'
test_expect_success 'automatic merge fails; both hooks are available' '
- test_when_finished "rm -f \"$PREMERGE\" \"$PRECOMMIT\"" &&
- test_when_finished "rm -f expected_hooks actual_hooks" &&
- test_when_finished "git checkout main" &&
- cp "$HOOKDIR/success.sample" "$PREMERGE" &&
- cp "$HOOKDIR/success.sample" "$PRECOMMIT" &&
+ setup_success_hook "pre-commit" &&
+ setup_success_hook "pre-merge-commit" &&
git checkout conflicting-a &&
test_must_fail git merge -m "merge conflicting-b" conflicting-b &&
test_path_is_missing actual_hooks &&
- echo "$PRECOMMIT" >expected_hooks &&
+ echo "pre-commit" >expected_hooks &&
echo a+b >conflicting &&
git add conflicting &&
git commit -m "resolve conflict" &&
@@ -135,8 +106,7 @@ test_expect_success 'automatic merge fails; both hooks are available' '
'
test_expect_success '--no-verify with succeeding hook' '
- test_when_finished "rm -f \"$PRECOMMIT\" actual_hooks" &&
- cp "$HOOKDIR/success.sample" "$PRECOMMIT" &&
+ setup_success_hook "pre-commit" &&
echo "even more" >>file &&
git add file &&
git commit --no-verify -m "even more" &&
@@ -144,8 +114,7 @@ test_expect_success '--no-verify with succeeding hook' '
'
test_expect_success '--no-verify with succeeding hook (merge)' '
- test_when_finished "rm -f \"$PREMERGE\" actual_hooks" &&
- cp "$HOOKDIR/success.sample" "$PREMERGE" &&
+ setup_success_hook "pre-merge-commit" &&
git branch -f side side-orig &&
git checkout side &&
git merge --no-verify -m "merge main" main &&
@@ -153,10 +122,19 @@ test_expect_success '--no-verify with succeeding hook (merge)' '
test_path_is_missing actual_hooks
'
+setup_failing_hook () {
+ test_when_finished "rm -f actual_hooks" &&
+ test_hook "$1" <<-EOF
+ echo $1-failing-hook >>actual_hooks
+ exit 1
+ EOF
+}
+
test_expect_success 'with failing hook' '
- test_when_finished "rm -f \"$PRECOMMIT\" expected_hooks actual_hooks" &&
- cp "$HOOKDIR/fail.sample" "$PRECOMMIT" &&
- echo "$PRECOMMIT" >expected_hooks &&
+ setup_failing_hook "pre-commit" &&
+ test_when_finished "rm -f expected_hooks" &&
+ echo "pre-commit-failing-hook" >expected_hooks &&
+
echo "another" >>file &&
git add file &&
test_must_fail git commit -m "another" &&
@@ -164,8 +142,7 @@ test_expect_success 'with failing hook' '
'
test_expect_success '--no-verify with failing hook' '
- test_when_finished "rm -f \"$PRECOMMIT\" actual_hooks" &&
- cp "$HOOKDIR/fail.sample" "$PRECOMMIT" &&
+ setup_failing_hook "pre-commit" &&
echo "stuff" >>file &&
git add file &&
git commit --no-verify -m "stuff" &&
@@ -173,9 +150,8 @@ test_expect_success '--no-verify with failing hook' '
'
test_expect_success 'with failing hook (merge)' '
- test_when_finished "rm -f \"$PREMERGE\" expected_hooks actual_hooks" &&
- cp "$HOOKDIR/fail.sample" "$PREMERGE" &&
- echo "$PREMERGE" >expected_hooks &&
+ setup_failing_hook "pre-merge-commit" &&
+ echo "pre-merge-commit-failing-hook" >expected_hooks &&
git checkout side &&
test_must_fail git merge -m "merge main" main &&
git checkout main &&
@@ -183,8 +159,8 @@ test_expect_success 'with failing hook (merge)' '
'
test_expect_success '--no-verify with failing hook (merge)' '
- test_when_finished "rm -f \"$PREMERGE\" actual_hooks" &&
- cp "$HOOKDIR/fail.sample" "$PREMERGE" &&
+ setup_failing_hook "pre-merge-commit" &&
+
git branch -f side side-orig &&
git checkout side &&
git merge --no-verify -m "merge main" main &&
@@ -192,9 +168,18 @@ test_expect_success '--no-verify with failing hook (merge)' '
test_path_is_missing actual_hooks
'
+setup_non_exec_hook () {
+ test_when_finished "rm -f actual_hooks" &&
+ test_hook "$1" <<-\EOF &&
+ echo non-exec >>actual_hooks
+ exit 1
+ EOF
+ test_hook --disable "$1"
+}
+
+
test_expect_success POSIXPERM 'with non-executable hook' '
- test_when_finished "rm -f \"$PRECOMMIT\" actual_hooks" &&
- cp "$HOOKDIR/non-exec.sample" "$PRECOMMIT" &&
+ setup_non_exec_hook "pre-commit" &&
echo "content" >>file &&
git add file &&
git commit -m "content" &&
@@ -202,8 +187,7 @@ test_expect_success POSIXPERM 'with non-executable hook' '
'
test_expect_success POSIXPERM '--no-verify with non-executable hook' '
- test_when_finished "rm -f \"$PRECOMMIT\" actual_hooks" &&
- cp "$HOOKDIR/non-exec.sample" "$PRECOMMIT" &&
+ setup_non_exec_hook "pre-commit" &&
echo "more content" >>file &&
git add file &&
git commit --no-verify -m "more content" &&
@@ -211,8 +195,7 @@ test_expect_success POSIXPERM '--no-verify with non-executable hook' '
'
test_expect_success POSIXPERM 'with non-executable hook (merge)' '
- test_when_finished "rm -f \"$PREMERGE\" actual_hooks" &&
- cp "$HOOKDIR/non-exec.sample" "$PREMERGE" &&
+ setup_non_exec_hook "pre-merge" &&
git branch -f side side-orig &&
git checkout side &&
git merge -m "merge main" main &&
@@ -221,8 +204,7 @@ test_expect_success POSIXPERM 'with non-executable hook (merge)' '
'
test_expect_success POSIXPERM '--no-verify with non-executable hook (merge)' '
- test_when_finished "rm -f \"$PREMERGE\" actual_hooks" &&
- cp "$HOOKDIR/non-exec.sample" "$PREMERGE" &&
+ setup_non_exec_hook "pre-merge" &&
git branch -f side side-orig &&
git checkout side &&
git merge --no-verify -m "merge main" main &&
@@ -230,10 +212,18 @@ test_expect_success POSIXPERM '--no-verify with non-executable hook (merge)' '
test_path_is_missing actual_hooks
'
+setup_require_prefix_hook () {
+ test_when_finished "rm -f expected_hooks" &&
+ echo require-prefix >expected_hooks &&
+ test_hook pre-commit <<-\EOF
+ echo require-prefix >>actual_hooks
+ test $GIT_PREFIX = "success/"
+ EOF
+}
+
test_expect_success 'with hook requiring GIT_PREFIX' '
- test_when_finished "rm -rf \"$PRECOMMIT\" expected_hooks actual_hooks success" &&
- cp "$HOOKDIR/require-prefix.sample" "$PRECOMMIT" &&
- echo "$PRECOMMIT" >expected_hooks &&
+ test_when_finished "rm -rf actual_hooks success" &&
+ setup_require_prefix_hook &&
echo "more content" >>file &&
git add file &&
mkdir success &&
@@ -245,9 +235,8 @@ test_expect_success 'with hook requiring GIT_PREFIX' '
'
test_expect_success 'with failing hook requiring GIT_PREFIX' '
- test_when_finished "rm -rf \"$PRECOMMIT\" expected_hooks actual_hooks fail" &&
- cp "$HOOKDIR/require-prefix.sample" "$PRECOMMIT" &&
- echo "$PRECOMMIT" >expected_hooks &&
+ test_when_finished "rm -rf actual_hooks fail" &&
+ setup_require_prefix_hook &&
echo "more content" >>file &&
git add file &&
mkdir fail &&
@@ -259,13 +248,23 @@ test_expect_success 'with failing hook requiring GIT_PREFIX' '
test_cmp expected_hooks actual_hooks
'
+setup_require_author_hook () {
+ test_when_finished "rm -f expected_hooks actual_hooks" &&
+ echo check-author >expected_hooks &&
+ test_hook pre-commit <<-\EOF
+ echo check-author >>actual_hooks
+ test "$GIT_AUTHOR_NAME" = "New Author" &&
+ test "$GIT_AUTHOR_EMAIL" = "newauthor@example.com"
+ EOF
+}
+
+
test_expect_success 'check the author in hook' '
- test_when_finished "rm -f \"$PRECOMMIT\" expected_hooks actual_hooks" &&
- cp "$HOOKDIR/check-author.sample" "$PRECOMMIT" &&
+ setup_require_author_hook &&
cat >expected_hooks <<-EOF &&
- $PRECOMMIT
- $PRECOMMIT
- $PRECOMMIT
+ check-author
+ check-author
+ check-author
EOF
test_must_fail git commit --allow-empty -m "by a.u.thor" &&
(
diff --git a/t/t7504-commit-msg-hook.sh b/t/t7504-commit-msg-hook.sh
index 4e75925..d125522 100755
--- a/t/t7504-commit-msg-hook.sh
+++ b/t/t7504-commit-msg-hook.sh
@@ -5,6 +5,7 @@ test_description='commit-msg hook'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'with no hook' '
@@ -54,15 +55,11 @@ test_expect_success '--no-verify with no hook (editor)' '
'
-# now install hook that always succeeds
-HOOKDIR="$(git rev-parse --git-dir)/hooks"
-HOOK="$HOOKDIR/commit-msg"
-mkdir -p "$HOOKDIR"
-cat > "$HOOK" <<EOF
-#!/bin/sh
-exit 0
-EOF
-chmod +x "$HOOK"
+test_expect_success 'setup: commit-msg hook that always succeeds' '
+ test_hook --setup commit-msg <<-\EOF
+ exit 0
+ EOF
+'
test_expect_success 'with succeeding hook' '
@@ -98,14 +95,16 @@ test_expect_success '--no-verify with succeeding hook (editor)' '
'
-# now a hook that fails
-cat > "$HOOK" <<EOF
-#!/bin/sh
-exit 1
-EOF
+test_expect_success 'setup: commit-msg hook that always fails' '
+ test_hook --clobber commit-msg <<-\EOF
+ exit 1
+ EOF
+'
commit_msg_is () {
- test "$(git log --pretty=format:%s%b -1)" = "$1"
+ printf "%s" "$1" >expect &&
+ git log --pretty=format:%s%b -1 >actual &&
+ test_cmp expect actual
}
test_expect_success 'with failing hook' '
@@ -133,6 +132,14 @@ test_expect_success '--no-verify with failing hook' '
'
+test_expect_success '-n followed by --verify with failing hook' '
+
+ echo "even more" >> file &&
+ git add file &&
+ test_must_fail git commit -n --verify -m "even more"
+
+'
+
test_expect_success '--no-verify with failing hook (editor)' '
echo "more stuff" >> file &&
@@ -168,8 +175,12 @@ test_expect_success 'merge bypasses failing hook with --no-verify' '
commit_msg_is "Merge branch '\''main'\'' into newbranch"
'
+test_expect_success 'setup: commit-msg hook made non-executable' '
+ git_dir="$(git rev-parse --git-dir)" &&
+ chmod -x "$git_dir/hooks/commit-msg"
+'
+
-chmod -x "$HOOK"
test_expect_success POSIXPERM 'with non-executable hook' '
echo "content" >file &&
@@ -204,13 +215,12 @@ test_expect_success POSIXPERM '--no-verify with non-executable hook (editor)' '
'
-# now a hook that edits the commit message
-cat > "$HOOK" <<'EOF'
-#!/bin/sh
-echo "new message" > "$1"
-exit 0
-EOF
-chmod +x "$HOOK"
+test_expect_success 'setup: commit-msg hook that edits the commit message' '
+ test_hook --clobber commit-msg <<-\EOF
+ echo "new message" >"$1"
+ exit 0
+ EOF
+'
test_expect_success 'hook edits commit message' '
diff --git a/t/t7505-prepare-commit-msg-hook.sh b/t/t7505-prepare-commit-msg-hook.sh
index 7a8194c..2128142 100755
--- a/t/t7505-prepare-commit-msg-hook.sh
+++ b/t/t7505-prepare-commit-msg-hook.sh
@@ -16,7 +16,7 @@ test_expect_success 'set up commits for rebasing' '
test_commit rebase-b b bb &&
for i in $(test_seq 1 13)
do
- test_commit rebase-$i c $i
+ test_commit rebase-$i c $i || return 1
done &&
git checkout main &&
@@ -47,25 +47,19 @@ test_expect_success 'with no hook' '
'
-# set up fake editor for interactive editing
-cat > fake-editor <<'EOF'
-#!/bin/sh
-exit 0
-EOF
-chmod +x fake-editor
-
-## Not using test_set_editor here so we can easily ensure the editor variable
-## is only set for the editor tests
-FAKE_EDITOR="$(pwd)/fake-editor"
-export FAKE_EDITOR
+test_expect_success 'setup fake editor for interactive editing' '
+ write_script fake-editor <<-\EOF &&
+ exit 0
+ EOF
-# now install hook that always succeeds and adds a message
-HOOKDIR="$(git rev-parse --git-dir)/hooks"
-HOOK="$HOOKDIR/prepare-commit-msg"
-mkdir -p "$HOOKDIR"
-echo "#!$SHELL_PATH" > "$HOOK"
-cat >> "$HOOK" <<'EOF'
+ ## Not using test_set_editor here so we can easily ensure the editor variable
+ ## is only set for the editor tests
+ FAKE_EDITOR="$(pwd)/fake-editor" &&
+ export FAKE_EDITOR
+'
+test_expect_success 'setup prepare-commit-msg hook' '
+ test_hook --setup prepare-commit-msg <<\EOF
GIT_DIR=$(git rev-parse --git-dir)
if test -d "$GIT_DIR/rebase-merge"
then
@@ -103,7 +97,7 @@ else
fi
exit 0
EOF
-chmod +x "$HOOK"
+'
echo dummy template > "$(git rev-parse --git-dir)/template"
@@ -250,7 +244,6 @@ test_rebase () {
}
test_rebase success
-test_have_prereq !REBASE_P || test_rebase success -p
test_expect_success 'with hook (cherry-pick)' '
test_when_finished "git checkout -f main" &&
@@ -266,10 +259,11 @@ test_expect_success 'with hook and editor (cherry-pick)' '
test "$(git log -1 --pretty=format:%s)" = merge
'
-cat > "$HOOK" <<'EOF'
-#!/bin/sh
-exit 1
-EOF
+test_expect_success 'setup: commit-msg hook that always fails' '
+ test_hook --setup --clobber prepare-commit-msg <<-\EOF
+ exit 1
+ EOF
+'
test_expect_success 'with failing hook' '
@@ -297,9 +291,9 @@ test_expect_success 'with failing hook (merge)' '
git checkout -B other HEAD@{1} &&
echo "more" >> file &&
git add file &&
- rm -f "$HOOK" &&
+ test_hook --remove prepare-commit-msg &&
git commit -m other &&
- write_script "$HOOK" <<-EOF &&
+ test_hook --setup prepare-commit-msg <<-\EOF &&
exit 1
EOF
git checkout - &&
diff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh
index 3fcb447..46566d5 100755
--- a/t/t7506-status-submodule.sh
+++ b/t/t7506-status-submodule.sh
@@ -2,6 +2,7 @@
test_description='git status for submodule'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_create_repo_with_commit () {
@@ -36,19 +37,19 @@ test_expect_success 'setup' '
test_expect_success 'status clean' '
git status >output &&
- test_i18ngrep "nothing to commit" output
+ test_grep "nothing to commit" output
'
test_expect_success 'commit --dry-run -a clean' '
test_must_fail git commit --dry-run -a >output &&
- test_i18ngrep "nothing to commit" output
+ test_grep "nothing to commit" output
'
test_expect_success 'status with modified file in submodule' '
(cd sub && git reset --hard) &&
echo "changed" >sub/foo &&
git status >output &&
- test_i18ngrep "modified: sub (modified content)" output
+ test_grep "modified: sub (modified content)" output
'
test_expect_success 'status with modified file in submodule (porcelain)' '
@@ -72,7 +73,7 @@ test_expect_success 'status with modified file in submodule (short)' '
test_expect_success 'status with added file in submodule' '
(cd sub && git reset --hard && echo >foo && git add foo) &&
git status >output &&
- test_i18ngrep "modified: sub (modified content)" output
+ test_grep "modified: sub (modified content)" output
'
test_expect_success 'status with added file in submodule (porcelain)' '
@@ -95,12 +96,12 @@ test_expect_success 'status with untracked file in submodule' '
(cd sub && git reset --hard) &&
echo "content" >sub/new-file &&
git status >output &&
- test_i18ngrep "modified: sub (untracked content)" output
+ test_grep "modified: sub (untracked content)" output
'
test_expect_success 'status -uno with untracked file in submodule' '
git status -uno >output &&
- test_i18ngrep "^nothing to commit" output
+ test_grep "^nothing to commit" output
'
test_expect_success 'status with untracked file in submodule (porcelain)' '
@@ -121,7 +122,7 @@ test_expect_success 'status with added and untracked file in submodule' '
(cd sub && git reset --hard && echo >foo && git add foo) &&
echo "content" >sub/new-file &&
git status >output &&
- test_i18ngrep "modified: sub (modified content, untracked content)" output
+ test_grep "modified: sub (modified content, untracked content)" output
'
test_expect_success 'status with added and untracked file in submodule (porcelain)' '
@@ -139,7 +140,7 @@ test_expect_success 'status with modified file in modified submodule' '
(cd sub && echo "next change" >foo && git commit -m "next change" foo) &&
echo "changed" >sub/foo &&
git status >output &&
- test_i18ngrep "modified: sub (new commits, modified content)" output
+ test_grep "modified: sub (new commits, modified content)" output
'
test_expect_success 'status with modified file in modified submodule (porcelain)' '
@@ -154,7 +155,7 @@ test_expect_success 'status with modified file in modified submodule (porcelain)
test_expect_success 'status with added file in modified submodule' '
(cd sub && git reset --hard && echo >foo && git add foo) &&
git status >output &&
- test_i18ngrep "modified: sub (new commits, modified content)" output
+ test_grep "modified: sub (new commits, modified content)" output
'
test_expect_success 'status with added file in modified submodule (porcelain)' '
@@ -169,7 +170,7 @@ test_expect_success 'status with untracked file in modified submodule' '
(cd sub && git reset --hard) &&
echo "content" >sub/new-file &&
git status >output &&
- test_i18ngrep "modified: sub (new commits, untracked content)" output
+ test_grep "modified: sub (new commits, untracked content)" output
'
test_expect_success 'status with untracked file in modified submodule (porcelain)' '
@@ -183,7 +184,7 @@ test_expect_success 'status with added and untracked file in modified submodule'
(cd sub && git reset --hard && echo >foo && git add foo) &&
echo "content" >sub/new-file &&
git status >output &&
- test_i18ngrep "modified: sub (new commits, modified content, untracked content)" output
+ test_grep "modified: sub (new commits, modified content, untracked content)" output
'
test_expect_success 'status with added and untracked file in modified submodule (porcelain)' '
@@ -208,7 +209,7 @@ test_expect_success 'setup .git file for sub' '
test_expect_success 'status with added file in modified submodule with .git file' '
(cd sub && git reset --hard && echo >foo && git add foo) &&
git status >output &&
- test_i18ngrep "modified: sub (new commits, modified content)" output
+ test_grep "modified: sub (new commits, modified content)" output
'
test_expect_success 'status with a lot of untracked files in the submodule' '
@@ -233,12 +234,12 @@ test_expect_success 'rm submodule contents' '
test_expect_success 'status clean (empty submodule dir)' '
git status >output &&
- test_i18ngrep "nothing to commit" output
+ test_grep "nothing to commit" output
'
test_expect_success 'status -a clean (empty submodule dir)' '
test_must_fail git commit --dry-run -a >output &&
- test_i18ngrep "nothing to commit" output
+ test_grep "nothing to commit" output
'
cat >status_expect <<\EOF
@@ -251,6 +252,7 @@ test_expect_success 'status with merge conflict in .gitmodules' '
test_create_repo_with_commit sub1 &&
test_tick &&
test_create_repo_with_commit sub2 &&
+ test_config_global protocol.file.allow always &&
(
cd super &&
prev=$(git rev-parse HEAD) &&
@@ -326,6 +328,7 @@ test_expect_success 'diff --submodule with merge conflict in .gitmodules' '
# sub2 will have an untracked file
# sub3 will have an untracked repository
test_expect_success 'setup superproject with untracked file in nested submodule' '
+ test_config_global protocol.file.allow always &&
(
cd super &&
git clean -dfx &&
diff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh
index ed2653d..4c7db19 100755
--- a/t/t7507-commit-verbose.sh
+++ b/t/t7507-commit-verbose.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='verbose commit template'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
write_script "check-for-diff" <<\EOF &&
@@ -74,6 +76,7 @@ test_expect_success 'diff in message is retained with -v' '
test_expect_success 'submodule log is stripped out too with -v' '
git config diff.submodule log &&
+ test_config_global protocol.file.allow always &&
git submodule add ./. sub &&
git commit -m "sub added" &&
(
@@ -86,7 +89,7 @@ test_expect_success 'submodule log is stripped out too with -v' '
export GIT_EDITOR &&
test_must_fail git commit -a -v 2>err
) &&
- test_i18ngrep "Aborting commit due to empty commit message." err
+ test_grep "Aborting commit due to empty commit message." err
'
test_expect_success 'verbose diff is stripped out with set core.commentChar' '
@@ -95,7 +98,17 @@ test_expect_success 'verbose diff is stripped out with set core.commentChar' '
export GIT_EDITOR &&
test_must_fail git -c core.commentchar=";" commit -a -v 2>err
) &&
- test_i18ngrep "Aborting commit due to empty commit message." err
+ test_grep "Aborting commit due to empty commit message." err
+'
+
+test_expect_success 'verbose diff is stripped with multi-byte comment char' '
+ (
+ GIT_EDITOR=cat &&
+ export GIT_EDITOR &&
+ test_must_fail git -c core.commentchar="foo>" commit -a -v >out 2>err
+ ) &&
+ grep "^foo> " out &&
+ test_grep "Aborting commit due to empty commit message." err
'
test_expect_success 'status does not verbose without --verbose' '
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index 05c6c02..773383f 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -5,6 +5,7 @@
test_description='git status'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
@@ -18,7 +19,7 @@ test_expect_success 'status -h in broken repository' '
echo "[status] showuntrackedfiles = CORRUPT" >>.git/config &&
test_expect_code 129 git status -h >usage 2>&1
) &&
- test_i18ngrep "[Uu]sage" broken/usage
+ test_grep "[Uu]sage" broken/usage
'
test_expect_success 'commit -h in broken repository' '
@@ -30,7 +31,7 @@ test_expect_success 'commit -h in broken repository' '
echo "[status] showuntrackedfiles = CORRUPT" >>.git/config &&
test_expect_code 129 git commit -h >usage 2>&1
) &&
- test_i18ngrep "[Uu]sage" broken/usage
+ test_grep "[Uu]sage" broken/usage
'
test_expect_success 'create upstream branch' '
@@ -71,7 +72,7 @@ test_expect_success 'setup' '
'
test_expect_success 'status (1)' '
- test_i18ngrep "use \"git rm --cached <file>\.\.\.\" to unstage" output
+ test_grep "use \"git rm --cached <file>\.\.\.\" to unstage" output
'
strip_comments () {
@@ -91,7 +92,7 @@ test_expect_success 'status --column' '
# On branch main
# Your branch and '\''upstream'\'' have diverged,
# and have 1 and 2 different commits each, respectively.
-# (use "git pull" to merge the remote branch into yours)
+# (use "git pull" if you want to integrate the remote branch with yours)
#
# Changes to be committed:
# (use "git restore --staged <file>..." to unstage)
@@ -122,7 +123,7 @@ cat >expect <<\EOF
# On branch main
# Your branch and 'upstream' have diverged,
# and have 1 and 2 different commits each, respectively.
-# (use "git pull" to merge the remote branch into yours)
+# (use "git pull" if you want to integrate the remote branch with yours)
#
# Changes to be committed:
# (use "git restore --staged <file>..." to unstage)
@@ -269,7 +270,7 @@ test_expect_success 'status with gitignore' '
On branch main
Your branch and '\''upstream'\'' have diverged,
and have 1 and 2 different commits each, respectively.
- (use "git pull" to merge the remote branch into yours)
+ (use "git pull" if you want to integrate the remote branch with yours)
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
@@ -334,7 +335,7 @@ test_expect_success 'status with gitignore (nothing untracked)' '
On branch main
Your branch and '\''upstream'\'' have diverged,
and have 1 and 2 different commits each, respectively.
- (use "git pull" to merge the remote branch into yours)
+ (use "git pull" if you want to integrate the remote branch with yours)
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
@@ -404,7 +405,7 @@ test_expect_success 'status -uno' '
On branch main
Your branch and '\''upstream'\'' have diverged,
and have 1 and 2 different commits each, respectively.
- (use "git pull" to merge the remote branch into yours)
+ (use "git pull" if you want to integrate the remote branch with yours)
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
@@ -418,14 +419,19 @@ Changes not staged for commit:
Untracked files not listed (use -u option to show untracked files)
EOF
git status -uno >output &&
+ test_cmp expect output &&
+ git status -ufalse >output &&
test_cmp expect output
'
-test_expect_success 'status (status.showUntrackedFiles no)' '
- test_config status.showuntrackedfiles no &&
- git status >output &&
- test_cmp expect output
-'
+for no in no false 0
+do
+ test_expect_success "status (status.showUntrackedFiles $no)" '
+ test_config status.showuntrackedfiles "$no" &&
+ git status >output &&
+ test_cmp expect output
+ '
+done
test_expect_success 'status -uno (advice.statusHints false)' '
cat >expect <<EOF &&
@@ -466,7 +472,7 @@ test_expect_success 'status -unormal' '
On branch main
Your branch and '\''upstream'\'' have diverged,
and have 1 and 2 different commits each, respectively.
- (use "git pull" to merge the remote branch into yours)
+ (use "git pull" if you want to integrate the remote branch with yours)
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
@@ -487,14 +493,21 @@ Untracked files:
EOF
git status -unormal >output &&
+ test_cmp expect output &&
+ git status -utrue >output &&
+ test_cmp expect output &&
+ git status -uyes >output &&
test_cmp expect output
'
-test_expect_success 'status (status.showUntrackedFiles normal)' '
- test_config status.showuntrackedfiles normal &&
- git status >output &&
- test_cmp expect output
-'
+for normal in normal true 1
+do
+ test_expect_success "status (status.showUntrackedFiles $normal)" '
+ test_config status.showuntrackedfiles $normal &&
+ git status >output &&
+ test_cmp expect output
+ '
+done
cat >expect <<EOF
M dir1/modified
@@ -521,7 +534,7 @@ test_expect_success 'status -uall' '
On branch main
Your branch and '\''upstream'\'' have diverged,
and have 1 and 2 different commits each, respectively.
- (use "git pull" to merge the remote branch into yours)
+ (use "git pull" if you want to integrate the remote branch with yours)
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
@@ -581,7 +594,7 @@ test_expect_success 'status with relative paths' '
On branch main
Your branch and '\''upstream'\'' have diverged,
and have 1 and 2 different commits each, respectively.
- (use "git pull" to merge the remote branch into yours)
+ (use "git pull" if you want to integrate the remote branch with yours)
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
@@ -649,7 +662,7 @@ test_expect_success TTY 'status with color.ui' '
On branch <GREEN>main<RESET>
Your branch and '\''upstream'\'' have diverged,
and have 1 and 2 different commits each, respectively.
- (use "git pull" to merge the remote branch into yours)
+ (use "git pull" if you want to integrate the remote branch with yours)
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
@@ -772,7 +785,7 @@ test_expect_success 'status without relative paths' '
On branch main
Your branch and '\''upstream'\'' have diverged,
and have 1 and 2 different commits each, respectively.
- (use "git pull" to merge the remote branch into yours)
+ (use "git pull" if you want to integrate the remote branch with yours)
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
@@ -846,7 +859,6 @@ test_expect_success 'dry-run of partial commit excluding new file in index' '
On branch main
Your branch and '\''upstream'\'' have diverged,
and have 1 and 2 different commits each, respectively.
- (use "git pull" to merge the remote branch into yours)
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
@@ -900,7 +912,7 @@ test_expect_success 'status submodule summary is disabled by default' '
On branch main
Your branch and '\''upstream'\'' have diverged,
and have 1 and 2 different commits each, respectively.
- (use "git pull" to merge the remote branch into yours)
+ (use "git pull" if you want to integrate the remote branch with yours)
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
@@ -957,7 +969,7 @@ test_expect_success 'status submodule summary' '
On branch main
Your branch and '\''upstream'\'' have diverged,
and have 1 and 2 different commits each, respectively.
- (use "git pull" to merge the remote branch into yours)
+ (use "git pull" if you want to integrate the remote branch with yours)
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
@@ -1012,11 +1024,11 @@ test_expect_success 'status -s submodule summary' '
'
test_expect_success 'status submodule summary (clean submodule): commit' '
- cat >expect <<EOF &&
+ cat >expect-status <<EOF &&
On branch main
Your branch and '\''upstream'\'' have diverged,
and have 2 and 2 different commits each, respectively.
- (use "git pull" to merge the remote branch into yours)
+ (use "git pull" if you want to integrate the remote branch with yours)
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
@@ -1032,12 +1044,13 @@ Untracked files:
no changes added to commit (use "git add" and/or "git commit -a")
EOF
+ sed "/git pull/d" expect-status > expect-commit &&
git commit -m "commit submodule" &&
git config status.submodulesummary 10 &&
test_must_fail git commit --dry-run >output &&
- test_cmp expect output &&
+ test_cmp expect-commit output &&
git status >output &&
- test_cmp expect output
+ test_cmp expect-status output
'
cat >expect <<EOF
@@ -1064,7 +1077,6 @@ test_expect_success 'commit --dry-run submodule summary (--amend)' '
On branch main
Your branch and '\''upstream'\'' have diverged,
and have 2 and 2 different commits each, respectively.
- (use "git pull" to merge the remote branch into yours)
Changes to be committed:
(use "git restore --source=HEAD^1 --staged <file>..." to unstage)
@@ -1116,7 +1128,7 @@ test_expect_success '--ignore-submodules=untracked suppresses submodules with un
On branch main
Your branch and '\''upstream'\'' have diverged,
and have 2 and 2 different commits each, respectively.
- (use "git pull" to merge the remote branch into yours)
+ (use "git pull" if you want to integrate the remote branch with yours)
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
@@ -1225,7 +1237,7 @@ test_expect_success "--ignore-submodules=untracked doesn't suppress submodules w
On branch main
Your branch and '\''upstream'\'' have diverged,
and have 2 and 2 different commits each, respectively.
- (use "git pull" to merge the remote branch into yours)
+ (use "git pull" if you want to integrate the remote branch with yours)
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
@@ -1282,7 +1294,7 @@ test_expect_success "--ignore-submodules=untracked doesn't suppress submodule su
On branch main
Your branch and '\''upstream'\'' have diverged,
and have 2 and 2 different commits each, respectively.
- (use "git pull" to merge the remote branch into yours)
+ (use "git pull" if you want to integrate the remote branch with yours)
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
@@ -1363,7 +1375,7 @@ cat > expect << EOF
; On branch main
; Your branch and 'upstream' have diverged,
; and have 2 and 2 different commits each, respectively.
-; (use "git pull" to merge the remote branch into yours)
+; (use "git pull" if you want to integrate the remote branch with yours)
;
; Changes to be committed:
; (use "git restore --staged <file>..." to unstage)
@@ -1403,7 +1415,9 @@ test_expect_success "status (core.commentchar with submodule summary)" '
test_expect_success "status (core.commentchar with two chars with submodule summary)" '
test_config core.commentchar ";;" &&
- test_must_fail git -c status.displayCommentPrefix=true status
+ sed "s/^/;/" <expect >expect.double &&
+ git -c status.displayCommentPrefix=true status >output &&
+ test_cmp expect.double output
'
test_expect_success "--ignore-submodules=all suppresses submodule summary" '
@@ -1411,7 +1425,7 @@ test_expect_success "--ignore-submodules=all suppresses submodule summary" '
On branch main
Your branch and '\''upstream'\'' have diverged,
and have 2 and 2 different commits each, respectively.
- (use "git pull" to merge the remote branch into yours)
+ (use "git pull" if you want to integrate the remote branch with yours)
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
@@ -1437,7 +1451,7 @@ test_expect_success '.gitmodules ignore=all suppresses unstaged submodule summar
On branch main
Your branch and '\''upstream'\'' have diverged,
and have 2 and 2 different commits each, respectively.
- (use "git pull" to merge the remote branch into yours)
+ (use "git pull" if you want to integrate the remote branch with yours)
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
@@ -1519,8 +1533,8 @@ test_expect_success '"status.branch=true" weaker than "--no-branch"' '
'
test_expect_success '"status.branch=true" weaker than "--porcelain"' '
- git -c status.branch=true status --porcelain >actual &&
- test_cmp expected_nobranch actual
+ git -c status.branch=true status --porcelain >actual &&
+ test_cmp expected_nobranch actual
'
test_expect_success '"status.branch=false" same as "--no-branch"' '
@@ -1542,12 +1556,12 @@ test_expect_success 'git commit will commit a staged but ignored submodule' '
git config --add -f .gitmodules submodule.subname.path sm &&
git config --add submodule.subname.ignore all &&
git status -s --ignore-submodules=dirty >output &&
- test_i18ngrep "^M. sm" output &&
+ test_grep "^M. sm" output &&
GIT_EDITOR="echo hello >>\"\$1\"" &&
export GIT_EDITOR &&
git commit -uno &&
git status -s --ignore-submodules=dirty >output &&
- test_i18ngrep ! "^M. sm" output
+ test_grep ! "^M. sm" output
'
test_expect_success 'git commit --dry-run will show a staged but ignored submodule' '
@@ -1557,7 +1571,6 @@ test_expect_success 'git commit --dry-run will show a staged but ignored submodu
On branch main
Your branch and '\''upstream'\'' have diverged,
and have 2 and 2 different commits each, respectively.
- (use "git pull" to merge the remote branch into yours)
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
@@ -1573,13 +1586,13 @@ EOF
git commit -uno --dry-run >output &&
test_cmp expect output &&
git status -s --ignore-submodules=dirty >output &&
- test_i18ngrep "^M. sm" output
+ test_grep "^M. sm" output
'
test_expect_success 'git commit -m will commit a staged but ignored submodule' '
git commit -uno -m message &&
git status -s --ignore-submodules=dirty >output &&
- test_i18ngrep ! "^M. sm" output &&
+ test_grep ! "^M. sm" output &&
git config --remove-section submodule.subname &&
git config -f .gitmodules --remove-section submodule.subname
'
@@ -1592,7 +1605,7 @@ test_expect_success 'show stash info with "--show-stash"' '
git stash &&
git status >expected_default &&
git status --show-stash >expected_with_stash &&
- test_i18ngrep "^Your stash currently has 1 entry$" expected_with_stash
+ test_grep "^Your stash currently has 1 entry$" expected_with_stash
'
test_expect_success 'no stash info with "--show-stash --no-show-stash"' '
@@ -1619,14 +1632,14 @@ test_expect_success 'no additional info if no stash entries' '
test_expect_success '"No commits yet" should be noted in status output' '
git checkout --orphan empty-branch-1 &&
git status >output &&
- test_i18ngrep "No commits yet" output
+ test_grep "No commits yet" output
'
test_expect_success '"No commits yet" should not be noted in status output' '
git checkout --orphan empty-branch-2 &&
test_commit test-commit-1 &&
git status >output &&
- test_i18ngrep ! "No commits yet" output
+ test_grep ! "No commits yet" output
'
test_expect_success '"Initial commit" should be noted in commit template' '
@@ -1634,7 +1647,7 @@ test_expect_success '"Initial commit" should be noted in commit template' '
touch to_be_committed_1 &&
git add to_be_committed_1 &&
git commit --dry-run >output &&
- test_i18ngrep "Initial commit" output
+ test_grep "Initial commit" output
'
test_expect_success '"Initial commit" should not be noted in commit template' '
@@ -1643,17 +1656,123 @@ test_expect_success '"Initial commit" should not be noted in commit template' '
touch to_be_committed_2 &&
git add to_be_committed_2 &&
git commit --dry-run >output &&
- test_i18ngrep ! "Initial commit" output
+ test_grep ! "Initial commit" output
'
test_expect_success '--no-optional-locks prevents index update' '
- test-tool chmtime =1234567890 .git/index &&
+ test_set_magic_mtime .git/index &&
git --no-optional-locks status &&
- test-tool chmtime --get .git/index >out &&
- grep ^1234567890 out &&
+ test_is_magic_mtime .git/index &&
+ git status &&
+ ! test_is_magic_mtime .git/index
+'
+
+test_expect_success 'racy timestamps will be fixed for clean worktree' '
+ echo content >racy-dirty &&
+ echo content >racy-racy &&
+ git add racy* &&
+ git commit -m "racy test files" &&
+ # let status rewrite the index, if necessary; after that we expect
+ # no more index writes unless caused by racy timestamps; note that
+ # timestamps may already be racy now (depending on previous tests)
+ git status &&
+ test_set_magic_mtime .git/index &&
+ git status &&
+ ! test_is_magic_mtime .git/index
+'
+
+test_expect_success 'racy timestamps will be fixed for dirty worktree' '
+ echo content2 >racy-dirty &&
git status &&
- test-tool chmtime --get .git/index >out &&
- ! grep ^1234567890 out
+ test_set_magic_mtime .git/index &&
+ git status &&
+ ! test_is_magic_mtime .git/index
+'
+
+test_expect_success 'setup slow status advice' '
+ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main git init slowstatus &&
+ (
+ cd slowstatus &&
+ cat >.gitignore <<-\EOF &&
+ /actual
+ /expected
+ /out
+ EOF
+ git add .gitignore &&
+ git commit -m "Add .gitignore" &&
+ git config advice.statusuoption true
+ )
+'
+
+test_expect_success 'slow status advice when core.untrackedCache and fsmonitor are unset' '
+ (
+ cd slowstatus &&
+ git config core.untrackedCache false &&
+ git config core.fsmonitor false &&
+ GIT_TEST_UF_DELAY_WARNING=1 git status >actual &&
+ cat >expected <<-\EOF &&
+ On branch main
+
+ It took 3.25 seconds to enumerate untracked files.
+ See '\''git help status'\'' for information on how to improve this.
+
+ nothing to commit, working tree clean
+ EOF
+ test_cmp expected actual
+ )
+'
+
+test_expect_success 'slow status advice when core.untrackedCache true, but not fsmonitor' '
+ (
+ cd slowstatus &&
+ git config core.untrackedCache true &&
+ git config core.fsmonitor false &&
+ GIT_TEST_UF_DELAY_WARNING=1 git status >actual &&
+ cat >expected <<-\EOF &&
+ On branch main
+
+ It took 3.25 seconds to enumerate untracked files.
+ See '\''git help status'\'' for information on how to improve this.
+
+ nothing to commit, working tree clean
+ EOF
+ test_cmp expected actual
+ )
+'
+
+test_expect_success 'slow status advice when core.untrackedCache true, and fsmonitor' '
+ (
+ cd slowstatus &&
+ git config core.untrackedCache true &&
+ git config core.fsmonitor true &&
+ GIT_TEST_UF_DELAY_WARNING=1 git status >actual &&
+ cat >expected <<-\EOF &&
+ On branch main
+
+ It took 3.25 seconds to enumerate untracked files,
+ but the results were cached, and subsequent runs may be faster.
+ See '\''git help status'\'' for information on how to improve this.
+
+ nothing to commit, working tree clean
+ EOF
+ test_cmp expected actual
+ )
+'
+
+test_expect_success EXPENSIVE 'status does not re-read unchanged 4 or 8 GiB file' '
+ (
+ mkdir large-file &&
+ cd large-file &&
+ # Files are 2 GiB, 4 GiB, and 8 GiB sparse files.
+ test-tool truncate file-a 0x080000000 &&
+ test-tool truncate file-b 0x100000000 &&
+ test-tool truncate file-c 0x200000000 &&
+ # This will be slow.
+ git add file-a file-b file-c &&
+ git commit -m "add large files" &&
+ git diff-index HEAD file-a file-b file-c >actual &&
+ test_must_be_empty actual
+ )
'
test_done
diff --git a/t/t7509-commit-authorship.sh b/t/t7509-commit-authorship.sh
index d568593..fd8c8f8 100755
--- a/t/t7509-commit-authorship.sh
+++ b/t/t7509-commit-authorship.sh
@@ -5,6 +5,7 @@
test_description='commit tests of various authorhip options. '
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
author_header () {
@@ -98,20 +99,20 @@ test_expect_success '--amend option with empty author' '
echo "Empty author test" >>foo &&
test_tick &&
test_must_fail git commit -a -m "empty author" --amend 2>err &&
- test_i18ngrep "empty ident" err
+ test_grep "empty ident" err
'
test_expect_success '--amend option with missing author' '
git cat-file commit Initial >tmp &&
sed "s/author [^<]* </author </" tmp >malformed &&
- sha=$(git hash-object -t commit -w malformed) &&
+ sha=$(git hash-object --literally -t commit -w malformed) &&
test_when_finished "remove_object $sha" &&
git checkout $sha &&
test_when_finished "git checkout Initial" &&
echo "Missing author test" >>foo &&
test_tick &&
test_must_fail git commit -a -m "malformed author" --amend 2>err &&
- test_i18ngrep "empty ident" err
+ test_grep "empty ident" err
'
test_expect_success '--reset-author makes the commit ours even with --amend option' '
diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh
index 8df5a74..0d2dd29 100755
--- a/t/t7510-signed-commit.sh
+++ b/t/t7510-signed-commit.sh
@@ -110,6 +110,13 @@ test_expect_success GPG 'verify and show signatures' '
)
'
+test_expect_success GPG 'verify-commit exits failure on unknown signature' '
+ test_must_fail env GNUPGHOME="$GNUPGHOME_NOT_USED" git verify-commit initial 2>actual &&
+ ! grep "Good signature from" actual &&
+ ! grep "BAD signature from" actual &&
+ grep -q -F -e "No public key" -e "public key not found" actual
+'
+
test_expect_success GPG 'verify-commit exits success on untrusted signature' '
git verify-commit eighth-signed-alt 2>actual &&
grep "Good signature from" actual &&
@@ -195,7 +202,7 @@ test_expect_success GPG 'detect fudged signature with NUL' '
git cat-file commit seventh-signed >raw &&
cat raw >forged2 &&
echo Qwik | tr "Q" "\000" >>forged2 &&
- git hash-object -w -t commit forged2 >forged2.commit &&
+ git hash-object --literally -w -t commit forged2 >forged2.commit &&
test_must_fail git verify-commit $(cat forged2.commit) &&
git show --pretty=short --show-signature $(cat forged2.commit) >actual2 &&
grep "BAD signature from" actual2 &&
@@ -203,7 +210,7 @@ test_expect_success GPG 'detect fudged signature with NUL' '
'
test_expect_success GPG 'amending already signed commit' '
- git checkout fourth-signed^0 &&
+ git checkout -f fourth-signed^0 &&
git commit --amend -S --no-edit &&
git verify-commit HEAD &&
git show -s --show-signature HEAD >actual &&
@@ -211,87 +218,101 @@ test_expect_success GPG 'amending already signed commit' '
! grep "BAD signature from" actual
'
+test_expect_success GPG2 'bare signature' '
+ git verify-commit fifth-signed 2>expect &&
+ echo >>expect &&
+ git log -1 --format="%GG" fifth-signed >actual &&
+ test_cmp expect actual
+'
+
test_expect_success GPG 'show good signature with custom format' '
cat >expect <<-\EOF &&
G
+ ultimate
13B6F51ECDDE430D
C O Mitter <committer@example.com>
73D758744BE721698EC54E8713B6F51ECDDE430D
73D758744BE721698EC54E8713B6F51ECDDE430D
EOF
- git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" sixth-signed >actual &&
+ git log -1 --format="%G?%n%GT%n%GK%n%GS%n%GF%n%GP" sixth-signed >actual &&
test_cmp expect actual
'
test_expect_success GPG 'show bad signature with custom format' '
cat >expect <<-\EOF &&
B
+ undefined
13B6F51ECDDE430D
C O Mitter <committer@example.com>
EOF
- git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" $(cat forged1.commit) >actual &&
+ git log -1 --format="%G?%n%GT%n%GK%n%GS%n%GF%n%GP" $(cat forged1.commit) >actual &&
test_cmp expect actual
'
test_expect_success GPG 'show untrusted signature with custom format' '
cat >expect <<-\EOF &&
U
+ undefined
65A0EEA02E30CAD7
Eris Discordia <discord@example.net>
F8364A59E07FFE9F4D63005A65A0EEA02E30CAD7
D4BE22311AD3131E5EDA29A461092E85B7227189
EOF
- git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
+ git log -1 --format="%G?%n%GT%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
test_cmp expect actual
'
test_expect_success GPG 'show untrusted signature with undefined trust level' '
cat >expect <<-\EOF &&
+ U
undefined
65A0EEA02E30CAD7
Eris Discordia <discord@example.net>
F8364A59E07FFE9F4D63005A65A0EEA02E30CAD7
D4BE22311AD3131E5EDA29A461092E85B7227189
EOF
- git log -1 --format="%GT%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
+ git log -1 --format="%G?%n%GT%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
test_cmp expect actual
'
test_expect_success GPG 'show untrusted signature with ultimate trust level' '
cat >expect <<-\EOF &&
+ G
ultimate
13B6F51ECDDE430D
C O Mitter <committer@example.com>
73D758744BE721698EC54E8713B6F51ECDDE430D
73D758744BE721698EC54E8713B6F51ECDDE430D
EOF
- git log -1 --format="%GT%n%GK%n%GS%n%GF%n%GP" sixth-signed >actual &&
+ git log -1 --format="%G?%n%GT%n%GK%n%GS%n%GF%n%GP" sixth-signed >actual &&
test_cmp expect actual
'
test_expect_success GPG 'show unknown signature with custom format' '
cat >expect <<-\EOF &&
E
+ undefined
65A0EEA02E30CAD7
EOF
- GNUPGHOME="$GNUPGHOME_NOT_USED" git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
+ GNUPGHOME="$GNUPGHOME_NOT_USED" git log -1 --format="%G?%n%GT%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
test_cmp expect actual
'
test_expect_success GPG 'show lack of signature with custom format' '
cat >expect <<-\EOF &&
N
+ undefined
EOF
- git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" seventh-unsigned >actual &&
+ git log -1 --format="%G?%n%GT%n%GK%n%GS%n%GF%n%GP" seventh-unsigned >actual &&
test_cmp expect actual
'
@@ -338,6 +359,8 @@ test_expect_success GPG 'show double signature with custom format' '
'
+# NEEDSWORK: This test relies on the test_tick commit/author dates from the first
+# 'create signed commits' test even though it creates its own
test_expect_success GPG 'verify-commit verifies multiply signed commits' '
git init multiply-signed &&
cd multiply-signed &&
@@ -378,4 +401,48 @@ test_expect_success GPG 'verify-commit verifies multiply signed commits' '
! grep "BAD signature from" actual
'
+test_expect_success 'custom `gpg.program`' '
+ write_script fake-gpg <<-\EOF &&
+ args="$*"
+
+ # skip uninteresting options
+ while case "$1" in
+ --status-fd=*|--keyid-format=*) ;; # skip
+ *) break;;
+ esac; do shift; done
+
+ case "$1" in
+ -bsau)
+ test -z "$LET_GPG_PROGRAM_FAIL" || {
+ echo "zOMG signing failed!" >&2
+ exit 1
+ }
+ cat >sign.file
+ echo "[GNUPG:] SIG_CREATED $args" >&2
+ echo "-----BEGIN PGP MESSAGE-----"
+ echo "$args"
+ echo "-----END PGP MESSAGE-----"
+ ;;
+ --verify)
+ cat "$2" >verify.file
+ exit 0
+ ;;
+ *)
+ echo "Unhandled args: $*" >&2
+ exit 1
+ ;;
+ esac
+ EOF
+
+ test_config gpg.program "$(pwd)/fake-gpg" &&
+ git commit -S --allow-empty -m signed-commit &&
+ test_path_exists sign.file &&
+ git show --show-signature &&
+ test_path_exists verify.file &&
+
+ test_must_fail env LET_GPG_PROGRAM_FAIL=1 \
+ git commit -S --allow-empty -m must-fail 2>err &&
+ grep zOMG err
+'
+
test_done
diff --git a/t/t7511-status-index.sh b/t/t7511-status-index.sh
index b5fdc04..4ffa45a 100755
--- a/t/t7511-status-index.sh
+++ b/t/t7511-status-index.sh
@@ -2,6 +2,7 @@
test_description='git status with certain file name lengths'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
files="0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z"
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index 7f2956d..802f8f7 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -659,6 +659,7 @@ On branch am_empty
You are in the middle of an am session.
The current patch is empty.
(use "git am --skip" to skip this patch)
+ (use "git am --allow-empty" to record this patch as an empty commit)
(use "git am --abort" to restore the original branch)
nothing to commit (use -u to show untracked files)
@@ -691,6 +692,34 @@ EOF
'
+test_expect_success 'status when bisecting while rebasing' '
+ git reset --hard main &&
+ test_when_finished "git rebase --abort" &&
+ ONTO=$(git rev-parse --short HEAD^) &&
+ FAKE_LINES="break" git rebase -i HEAD^ &&
+ test_when_finished "git checkout -" &&
+ git checkout -b bisect_while_rebasing &&
+ test_when_finished "git bisect reset" &&
+ git bisect start &&
+ cat >expected <<EOF &&
+On branch bisect_while_rebasing
+Last command done (1 command done):
+ break
+No commands remaining.
+You are currently editing a commit while rebasing branch '\''bisect'\'' on '\''$ONTO'\''.
+ (use "git commit --amend" to amend the current commit)
+ (use "git rebase --continue" once you are satisfied with your changes)
+
+You are currently bisecting, started from branch '\''bisect_while_rebasing'\''.
+ (use "git bisect reset" to get back to the original branch)
+
+nothing to commit (use -u to show untracked files)
+EOF
+ git status --untracked-files=no >actual &&
+ test_cmp expected actual
+'
+
+
test_expect_success 'status when rebase --apply conflicts with statushints disabled' '
git reset --hard main &&
git checkout -b statushints_disabled &&
@@ -773,6 +802,28 @@ EOF
test_cmp expected actual
'
+test_expect_success 'status when cherry-picking multiple commits' '
+ git reset --hard cherry_branch &&
+ test_when_finished "git cherry-pick --abort" &&
+ test_must_fail git cherry-pick cherry_branch_second one_cherry &&
+ TO_CHERRY_PICK=$(git rev-parse --short CHERRY_PICK_HEAD) &&
+ cat >expected <<EOF &&
+On branch cherry_branch
+You are currently cherry-picking commit $TO_CHERRY_PICK.
+ (fix conflicts and run "git cherry-pick --continue")
+ (use "git cherry-pick --skip" to skip this patch)
+ (use "git cherry-pick --abort" to cancel the cherry-pick operation)
+
+Unmerged paths:
+ (use "git add <file>..." to mark resolution)
+ both modified: main.txt
+
+no changes added to commit (use "git add" and/or "git commit -a")
+EOF
+ git status --untracked-files=no >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'status when cherry-picking after committing conflict resolution' '
git reset --hard cherry_branch &&
test_when_finished "git cherry-pick --abort" &&
diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
index 04885d0..3d3e13c 100755
--- a/t/t7513-interpret-trailers.sh
+++ b/t/t7513-interpret-trailers.sh
@@ -156,7 +156,7 @@ test_expect_success 'with config option on the command line' '
Acked-by: Johan
Reviewed-by: Peff
EOF
- { echo; echo "Acked-by: Johan"; } |
+ { echo && echo "Acked-by: Johan"; } |
git -c "trailer.Acked-by.ifexists=addifdifferent" interpret-trailers \
--trailer "Reviewed-by: Peff" --trailer "Acked-by: Johan" >actual &&
test_cmp expected actual
@@ -489,7 +489,7 @@ test_expect_success 'multiline field treated as atomic for neighbor check' '
'
test_expect_success 'with config setup' '
- git config trailer.ack.key "Acked-by: " &&
+ test_config trailer.ack.key "Acked-by: " &&
cat >expected <<-\EOF &&
Acked-by: Peff
@@ -503,8 +503,8 @@ test_expect_success 'with config setup' '
'
test_expect_success 'with config setup and ":=" as separators' '
- git config trailer.separators ":=" &&
- git config trailer.ack.key "Acked-by= " &&
+ test_config trailer.separators ":=" &&
+ test_config trailer.ack.key "Acked-by= " &&
cat >expected <<-\EOF &&
Acked-by= Peff
@@ -518,7 +518,7 @@ test_expect_success 'with config setup and ":=" as separators' '
'
test_expect_success 'with config setup and "%" as separators' '
- git config trailer.separators "%" &&
+ test_config trailer.separators "%" &&
cat >expected <<-\EOF &&
bug% 42
@@ -532,6 +532,7 @@ test_expect_success 'with config setup and "%" as separators' '
'
test_expect_success 'with "%" as separators and a message with trailers' '
+ test_config trailer.separators "%" &&
cat >special_message <<-\EOF &&
Special Message
@@ -553,8 +554,8 @@ test_expect_success 'with "%" as separators and a message with trailers' '
'
test_expect_success 'with config setup and ":=#" as separators' '
- git config trailer.separators ":=#" &&
- git config trailer.bug.key "Bug #" &&
+ test_config trailer.separators ":=#" &&
+ test_config trailer.bug.key "Bug #" &&
cat >expected <<-\EOF &&
Bug #42
@@ -581,6 +582,8 @@ test_expect_success 'with basic patch' '
'
test_expect_success 'with commit complex message as argument' '
+ test_config trailer.separators ":=" &&
+ test_config trailer.ack.key "Acked-by= " &&
cat complex_message_body complex_message_trailers >complex_message &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
@@ -594,6 +597,8 @@ test_expect_success 'with commit complex message as argument' '
'
test_expect_success 'with 2 files arguments' '
+ test_config trailer.separators ":=" &&
+ test_config trailer.ack.key "Acked-by= " &&
cat basic_message >>expected &&
echo >>expected &&
cat basic_patch >>expected &&
@@ -677,6 +682,9 @@ test_expect_success 'with message that has an old style conflict block' '
'
test_expect_success 'with commit complex message and trailer args' '
+ test_config trailer.separators ":=#" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.bug.key "Bug #" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Fixes: Z
@@ -692,6 +700,9 @@ test_expect_success 'with commit complex message and trailer args' '
'
test_expect_success 'with complex patch, args and --trim-empty' '
+ test_config trailer.separators ":=#" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.bug.key "Bug #" &&
cat complex_message >complex_patch &&
cat basic_patch >>complex_patch &&
cat complex_message_body >expected &&
@@ -746,7 +757,10 @@ test_expect_success POSIXPERM,SANITY "in-place editing doesn't clobber original
'
test_expect_success 'using "where = before"' '
- git config trailer.bug.where "before" &&
+ test_config trailer.separators ":=#" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.bug.key "Bug #" &&
+ test_config trailer.bug.where "before" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Bug #42
@@ -762,7 +776,9 @@ test_expect_success 'using "where = before"' '
'
test_expect_success 'overriding configuration with "--where after"' '
- git config trailer.ack.where "before" &&
+ test_config trailer.separators ":=" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.ack.where "before" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Fixes: Z
@@ -776,7 +792,12 @@ test_expect_success 'overriding configuration with "--where after"' '
test_cmp expected actual
'
-test_expect_success 'using "where = before" with "--no-where"' '
+test_expect_success 'using "--where after" with "--no-where"' '
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.ack.where "before" &&
+ test_config trailer.bug.key "Bug #" &&
+ test_config trailer.bug.where "before" &&
+ test_config trailer.separators ":=#" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Bug #42
@@ -791,8 +812,59 @@ test_expect_success 'using "where = before" with "--no-where"' '
test_cmp expected actual
'
+# Check whether using "--no-where" clears out only the "--where after", such
+# that we still use the configuration in trailer.where (which is different from
+# the hardcoded default (in WHERE_END) assuming the absence of .gitconfig).
+# Here, the "start" setting of trailer.where is respected, so the new "Acked-by"
+# and "Bug" trailers are placed at the beginning, and not at the end which is
+# the harcoded default.
+test_expect_success 'using "--where after" with "--no-where" defaults to configuration' '
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.bug.key "Bug #" &&
+ test_config trailer.separators ":=#" &&
+ test_config trailer.where "start" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Acked-by= Peff
+ Fixes: Z
+ Acked-by= Z
+ Reviewed-by: Z
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --where after --no-where --trailer "ack: Peff" \
+ --trailer "bug: 42" complex_message >actual &&
+ test_cmp expected actual
+'
+
+# The "--where after" will only get respected for the trailer that came
+# immediately after it. For the next trailer (Bug #42), we default to using the
+# hardcoded WHERE_END because we don't have any "trailer.where" or
+# "trailer.bug.where" configured.
+test_expect_success 'using "--no-where" defaults to harcoded default if nothing configured' '
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.bug.key "Bug #" &&
+ test_config trailer.separators ":=#" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Peff
+ Reviewed-by: Z
+ Signed-off-by: Z
+ Bug #42
+ EOF
+ git interpret-trailers --where after --trailer "ack: Peff" --no-where \
+ --trailer "bug: 42" complex_message >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'using "where = after"' '
- git config trailer.ack.where "after" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.ack.where "after" &&
+ test_config trailer.bug.key "Bug #" &&
+ test_config trailer.bug.where "before" &&
+ test_config trailer.separators ":=#" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Bug #42
@@ -808,8 +880,11 @@ test_expect_success 'using "where = after"' '
'
test_expect_success 'using "where = end"' '
- git config trailer.review.key "Reviewed-by" &&
- git config trailer.review.where "end" &&
+ test_config trailer.review.key "Reviewed-by" &&
+ test_config trailer.review.where "end" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.ack.where "after" &&
+ test_config trailer.separators ":=" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Fixes: Z
@@ -827,8 +902,11 @@ test_expect_success 'using "where = end"' '
'
test_expect_success 'using "where = start"' '
- git config trailer.review.key "Reviewed-by" &&
- git config trailer.review.where "start" &&
+ test_config trailer.review.key "Reviewed-by" &&
+ test_config trailer.review.where "start" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.ack.where "after" &&
+ test_config trailer.separators ":=" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Reviewed-by: Johannes
@@ -846,8 +924,13 @@ test_expect_success 'using "where = start"' '
'
test_expect_success 'using "where = before" for a token in the middle of the message' '
- git config trailer.review.key "Reviewed-by:" &&
- git config trailer.review.where "before" &&
+ test_config trailer.review.key "Reviewed-by:" &&
+ test_config trailer.review.where "before" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.ack.where "after" &&
+ test_config trailer.bug.key "Bug #" &&
+ test_config trailer.bug.where "before" &&
+ test_config trailer.separators ":=#" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Bug #42
@@ -864,6 +947,12 @@ test_expect_success 'using "where = before" for a token in the middle of the mes
'
test_expect_success 'using "where = before" and --trim-empty' '
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.ack.where "after" &&
+ test_config trailer.bug.key "Bug #" &&
+ test_config trailer.bug.where "before" &&
+ test_config trailer.review.key "Reviewed-by:" &&
+ test_config trailer.separators ":=#" &&
cat complex_message_body >expected &&
cat >>expected <<-\EOF &&
Bug #46
@@ -878,6 +967,13 @@ test_expect_success 'using "where = before" and --trim-empty' '
'
test_expect_success 'the default is "ifExists = addIfDifferentNeighbor"' '
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.ack.where "after" &&
+ test_config trailer.bug.key "Bug #" &&
+ test_config trailer.bug.where "before" &&
+ test_config trailer.review.key "Reviewed-by:" &&
+ test_config trailer.review.where "before" &&
+ test_config trailer.separators ":=#" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Bug #42
@@ -896,7 +992,13 @@ test_expect_success 'the default is "ifExists = addIfDifferentNeighbor"' '
'
test_expect_success 'default "ifExists" is now "addIfDifferent"' '
- git config trailer.ifexists "addIfDifferent" &&
+ test_config trailer.ifexists "addIfDifferent" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.ack.where "after" &&
+ test_config trailer.bug.key "Bug #" &&
+ test_config trailer.bug.where "before" &&
+ test_config trailer.review.key "Reviewed-by:" &&
+ test_config trailer.separators ":=#" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Bug #42
@@ -914,8 +1016,14 @@ test_expect_success 'default "ifExists" is now "addIfDifferent"' '
'
test_expect_success 'using "ifExists = addIfDifferent" with "where = end"' '
- git config trailer.ack.ifExists "addIfDifferent" &&
- git config trailer.ack.where "end" &&
+ test_config trailer.ack.ifExists "addIfDifferent" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.ack.where "end" &&
+ test_config trailer.bug.key "Bug #" &&
+ test_config trailer.bug.where "before" &&
+ test_config trailer.review.key "Reviewed-by:" &&
+ test_config trailer.ifexists "addIfDifferent" &&
+ test_config trailer.separators ":=#" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Bug #42
@@ -932,8 +1040,14 @@ test_expect_success 'using "ifExists = addIfDifferent" with "where = end"' '
'
test_expect_success 'using "ifExists = addIfDifferent" with "where = before"' '
- git config trailer.ack.ifExists "addIfDifferent" &&
- git config trailer.ack.where "before" &&
+ test_config trailer.ack.ifExists "addIfDifferent" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.ack.where "before" &&
+ test_config trailer.bug.key "Bug #" &&
+ test_config trailer.bug.where "before" &&
+ test_config trailer.review.key "Reviewed-by:" &&
+ test_config trailer.ifexists "addIfDifferent" &&
+ test_config trailer.separators ":=#" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Bug #42
@@ -950,8 +1064,14 @@ test_expect_success 'using "ifExists = addIfDifferent" with "where = before"' '
'
test_expect_success 'using "ifExists = addIfDifferentNeighbor" with "where = end"' '
- git config trailer.ack.ifExists "addIfDifferentNeighbor" &&
- git config trailer.ack.where "end" &&
+ test_config trailer.ack.ifExists "addIfDifferentNeighbor" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.ack.where "end" &&
+ test_config trailer.bug.key "Bug #" &&
+ test_config trailer.bug.where "before" &&
+ test_config trailer.review.key "Reviewed-by:" &&
+ test_config trailer.ifexists "addIfDifferent" &&
+ test_config trailer.separators ":=#" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Bug #42
@@ -973,8 +1093,14 @@ test_expect_success 'using "ifExists = addIfDifferentNeighbor" with "where = end
'
test_expect_success 'using "ifExists = addIfDifferentNeighbor" with "where = after"' '
- git config trailer.ack.ifExists "addIfDifferentNeighbor" &&
- git config trailer.ack.where "after" &&
+ test_config trailer.ack.ifExists "addIfDifferentNeighbor" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.ack.where "after" &&
+ test_config trailer.bug.key "Bug #" &&
+ test_config trailer.bug.where "before" &&
+ test_config trailer.review.key "Reviewed-by:" &&
+ test_config trailer.ifexists "addIfDifferent" &&
+ test_config trailer.separators ":=#" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Bug #42
@@ -995,7 +1121,11 @@ test_expect_success 'using "ifExists = addIfDifferentNeighbor" with "where = af
'
test_expect_success 'using "ifExists = addIfDifferentNeighbor" and --trim-empty' '
- git config trailer.ack.ifExists "addIfDifferentNeighbor" &&
+ test_config trailer.ack.ifExists "addIfDifferentNeighbor" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.bug.key "Bug #" &&
+ test_config trailer.bug.where "before" &&
+ test_config trailer.separators ":=#" &&
cat complex_message_body >expected &&
cat >>expected <<-\EOF &&
Bug #42
@@ -1011,8 +1141,14 @@ test_expect_success 'using "ifExists = addIfDifferentNeighbor" and --trim-empty'
'
test_expect_success 'using "ifExists = add" with "where = end"' '
- git config trailer.ack.ifExists "add" &&
- git config trailer.ack.where "end" &&
+ test_config trailer.ack.ifExists "add" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.ack.where "end" &&
+ test_config trailer.bug.key "Bug #" &&
+ test_config trailer.bug.where "before" &&
+ test_config trailer.review.key "Reviewed-by:" &&
+ test_config trailer.ifexists "addIfDifferent" &&
+ test_config trailer.separators ":=#" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Bug #42
@@ -1036,8 +1172,14 @@ test_expect_success 'using "ifExists = add" with "where = end"' '
'
test_expect_success 'using "ifExists = add" with "where = after"' '
- git config trailer.ack.ifExists "add" &&
- git config trailer.ack.where "after" &&
+ test_config trailer.ack.ifExists "add" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.ack.where "after" &&
+ test_config trailer.bug.key "Bug #" &&
+ test_config trailer.bug.where "before" &&
+ test_config trailer.review.key "Reviewed-by:" &&
+ test_config trailer.ifexists "addIfDifferent" &&
+ test_config trailer.separators ":=#" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Bug #42
@@ -1058,8 +1200,15 @@ test_expect_success 'using "ifExists = add" with "where = after"' '
'
test_expect_success 'overriding configuration with "--if-exists replace"' '
- git config trailer.fix.key "Fixes: " &&
- git config trailer.fix.ifExists "add" &&
+ test_config trailer.fix.key "Fixes: " &&
+ test_config trailer.fix.ifExists "add" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.ack.where "after" &&
+ test_config trailer.bug.key "Bug #" &&
+ test_config trailer.bug.where "before" &&
+ test_config trailer.review.key "Reviewed-by:" &&
+ test_config trailer.review.where "before" &&
+ test_config trailer.separators ":=#" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Bug #42
@@ -1074,9 +1223,66 @@ test_expect_success 'overriding configuration with "--if-exists replace"' '
test_cmp expected actual
'
+# "trailer.ifexists" is set to "doNothing", so using "--no-if-exists" defaults
+# to this "doNothing" behavior. So the "Fixes: 53" trailer does not get added.
+test_expect_success 'using "--if-exists replace" with "--no-if-exists" defaults to configuration' '
+ test_config trailer.ifexists "doNothing" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Fixes: Z
+ Acked-by: Z
+ Reviewed-by: Z
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --if-exists replace --no-if-exists --trailer "Fixes: 53" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+# No "ifexists" configuration is set, so using "--no-if-exists" makes it default
+# to addIfDifferentNeighbor. Because we do have a different neighbor "Fixes: 53"
+# (because it got added by overriding with "--if-exists replace" earlier in the
+# arguments list), we add "Signed-off-by: addme".
+test_expect_success 'using "--no-if-exists" defaults to hardcoded default if nothing configured' '
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Acked-by: Z
+ Reviewed-by: Z
+ Signed-off-by: Z
+ Fixes: 53
+ Signed-off-by: addme
+ EOF
+ git interpret-trailers --if-exists replace --trailer "Fixes: 53" --no-if-exists \
+ --trailer "Signed-off-by: addme" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+# The second "Fixes: 53" trailer is discarded, because the "--no-if-exists" here
+# makes us default to addIfDifferentNeighbor, and we already added the "Fixes:
+# 53" trailer earlier in the argument list.
+test_expect_success 'using "--no-if-exists" defaults to hardcoded default if nothing configured (no addition)' '
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Acked-by: Z
+ Reviewed-by: Z
+ Signed-off-by: Z
+ Fixes: 53
+ EOF
+ git interpret-trailers --if-exists replace --trailer "Fixes: 53" --no-if-exists \
+ --trailer "Fixes: 53" <complex_message >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'using "ifExists = replace"' '
- git config trailer.fix.key "Fixes: " &&
- git config trailer.fix.ifExists "replace" &&
+ test_config trailer.fix.key "Fixes: " &&
+ test_config trailer.fix.ifExists "replace" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.ack.where "after" &&
+ test_config trailer.bug.key "Bug #" &&
+ test_config trailer.bug.where "before" &&
+ test_config trailer.review.key "Reviewed-by:" &&
+ test_config trailer.ifexists "addIfDifferent" &&
+ test_config trailer.separators ":=#" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Bug #42
@@ -1095,7 +1301,16 @@ test_expect_success 'using "ifExists = replace"' '
'
test_expect_success 'using "ifExists = replace" with "where = after"' '
- git config trailer.fix.where "after" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.ack.where "after" &&
+ test_config trailer.bug.key "Bug #" &&
+ test_config trailer.bug.where "before" &&
+ test_config trailer.fix.key "Fixes: " &&
+ test_config trailer.fix.ifExists "replace" &&
+ test_config trailer.fix.where "after" &&
+ test_config trailer.review.key "Reviewed-by:" &&
+ test_config trailer.ifexists "addIfDifferent" &&
+ test_config trailer.separators ":=#" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Bug #42
@@ -1114,7 +1329,15 @@ test_expect_success 'using "ifExists = replace" with "where = after"' '
'
test_expect_success 'using "ifExists = doNothing"' '
- git config trailer.fix.ifExists "doNothing" &&
+ test_config trailer.fix.ifExists "doNothing" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.ack.where "after" &&
+ test_config trailer.bug.key "Bug #" &&
+ test_config trailer.bug.where "before" &&
+ test_config trailer.fix.key "Fixes: " &&
+ test_config trailer.review.key "Reviewed-by:" &&
+ test_config trailer.ifexists "addIfDifferent" &&
+ test_config trailer.separators ":=#" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Bug #42
@@ -1133,8 +1356,17 @@ test_expect_success 'using "ifExists = doNothing"' '
'
test_expect_success 'the default is "ifMissing = add"' '
- git config trailer.cc.key "Cc: " &&
- git config trailer.cc.where "before" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.ack.where "after" &&
+ test_config trailer.bug.key "Bug #" &&
+ test_config trailer.bug.where "before" &&
+ test_config trailer.cc.key "Cc: " &&
+ test_config trailer.cc.where "before" &&
+ test_config trailer.fix.key "Fixes: " &&
+ test_config trailer.fix.ifExists "doNothing" &&
+ test_config trailer.review.key "Reviewed-by:" &&
+ test_config trailer.ifexists "addIfDifferent" &&
+ test_config trailer.separators ":=#" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Bug #42
@@ -1154,7 +1386,14 @@ test_expect_success 'the default is "ifMissing = add"' '
'
test_expect_success 'overriding configuration with "--if-missing doNothing"' '
- git config trailer.ifmissing "add" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.ack.where "after" &&
+ test_config trailer.fix.key "Fixes: " &&
+ test_config trailer.fix.ifExists "doNothing" &&
+ test_config trailer.review.key "Reviewed-by:" &&
+ test_config trailer.ifexists "addIfDifferent" &&
+ test_config trailer.ifmissing "add" &&
+ test_config trailer.separators ":=" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Fixes: Z
@@ -1173,7 +1412,13 @@ test_expect_success 'overriding configuration with "--if-missing doNothing"' '
'
test_expect_success 'when default "ifMissing" is "doNothing"' '
- git config trailer.ifmissing "doNothing" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.ack.where "after" &&
+ test_config trailer.fix.ifExists "doNothing" &&
+ test_config trailer.review.key "Reviewed-by:" &&
+ test_config trailer.ifexists "addIfDifferent" &&
+ test_config trailer.ifmissing "doNothing" &&
+ test_config trailer.separators ":=" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Fixes: Z
@@ -1187,14 +1432,21 @@ test_expect_success 'when default "ifMissing" is "doNothing"' '
--trailer "cc=Linus" --trailer "ack: Junio" \
--trailer "fix=22" --trailer "bug: 42" --trailer "ack: Peff" \
<complex_message >actual &&
- test_cmp expected actual &&
- git config trailer.ifmissing "add"
+ test_cmp expected actual
'
test_expect_success 'using "ifMissing = add" with "where = end"' '
- git config trailer.cc.key "Cc: " &&
- git config trailer.cc.where "end" &&
- git config trailer.cc.ifMissing "add" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.ack.where "after" &&
+ test_config trailer.bug.key "Bug #" &&
+ test_config trailer.bug.where "before" &&
+ test_config trailer.cc.key "Cc: " &&
+ test_config trailer.cc.ifMissing "add" &&
+ test_config trailer.cc.where "end" &&
+ test_config trailer.fix.ifExists "doNothing" &&
+ test_config trailer.review.key "Reviewed-by:" &&
+ test_config trailer.ifexists "addIfDifferent" &&
+ test_config trailer.separators ":=#" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Bug #42
@@ -1214,9 +1466,17 @@ test_expect_success 'using "ifMissing = add" with "where = end"' '
'
test_expect_success 'using "ifMissing = add" with "where = before"' '
- git config trailer.cc.key "Cc: " &&
- git config trailer.cc.where "before" &&
- git config trailer.cc.ifMissing "add" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.ack.where "after" &&
+ test_config trailer.bug.key "Bug #" &&
+ test_config trailer.bug.where "before" &&
+ test_config trailer.cc.key "Cc: " &&
+ test_config trailer.cc.ifMissing "add" &&
+ test_config trailer.cc.where "before" &&
+ test_config trailer.fix.ifExists "doNothing" &&
+ test_config trailer.review.key "Reviewed-by:" &&
+ test_config trailer.ifexists "addIfDifferent" &&
+ test_config trailer.separators ":=#" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Cc: Linus
@@ -1236,7 +1496,15 @@ test_expect_success 'using "ifMissing = add" with "where = before"' '
'
test_expect_success 'using "ifMissing = doNothing"' '
- git config trailer.cc.ifMissing "doNothing" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.ack.where "after" &&
+ test_config trailer.bug.key "Bug #" &&
+ test_config trailer.bug.where "before" &&
+ test_config trailer.cc.ifMissing "doNothing" &&
+ test_config trailer.fix.ifExists "doNothing" &&
+ test_config trailer.review.key "Reviewed-by:" &&
+ test_config trailer.ifexists "addIfDifferent" &&
+ test_config trailer.separators ":=#" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Bug #42
@@ -1254,9 +1522,50 @@ test_expect_success 'using "ifMissing = doNothing"' '
test_cmp expected actual
'
+# Ignore the "IgnoredTrailer" because of "--if-missing doNothing", but also
+# ignore the "StillIgnoredTrailer" because we set "trailer.ifMissing" to
+# "doNothing" in configuration.
+test_expect_success 'using "--no-if-missing" defaults to configuration' '
+ test_config trailer.ifMissing "doNothing" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Fixes: Z
+ Acked-by: Z
+ Reviewed-by: Z
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --if-missing doNothing --trailer "IgnoredTrailer: ignoreme" --no-if-missing \
+ --trailer "StillIgnoredTrailer: ignoreme" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+# Add the "AddedTrailer" because the "--no-if-missing" clears the "--if-missing
+# doNothing" from earlier in the argument list.
+test_expect_success 'using "--no-if-missing" defaults to hardcoded default if nothing configured' '
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Fixes: Z
+ Acked-by: Z
+ Reviewed-by: Z
+ Signed-off-by: Z
+ AddedTrailer: addme
+ EOF
+ git interpret-trailers --if-missing doNothing --trailer "IgnoredTrailer: ignoreme" --no-if-missing \
+ --trailer "AddedTrailer: addme" <complex_message >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'default "where" is now "after"' '
git config trailer.where "after" &&
- git config --unset trailer.ack.where &&
+ test_config trailer.ack.ifExists "add" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.ack.where "after" &&
+ test_config trailer.bug.key "Bug #" &&
+ test_config trailer.bug.where "before" &&
+ test_config trailer.fix.ifExists "doNothing" &&
+ test_config trailer.review.key "Reviewed-by:" &&
+ test_config trailer.ifexists "addIfDifferent" &&
+ test_config trailer.separators ":=#" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Bug #42
@@ -1280,10 +1589,15 @@ test_expect_success 'default "where" is now "after"' '
'
test_expect_success 'with simple command' '
- git config trailer.sign.key "Signed-off-by: " &&
- git config trailer.sign.where "after" &&
- git config trailer.sign.ifExists "addIfDifferentNeighbor" &&
- git config trailer.sign.command "echo \"A U Thor <author@example.com>\"" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.fix.ifExists "doNothing" &&
+ test_config trailer.review.key "Reviewed-by:" &&
+ test_config trailer.sign.command "echo \"A U Thor <author@example.com>\"" &&
+ test_config trailer.sign.key "Signed-off-by: " &&
+ test_config trailer.sign.ifExists "addIfDifferentNeighbor" &&
+ test_config trailer.sign.where "after" &&
+ test_config trailer.ifexists "addIfDifferent" &&
+ test_config trailer.separators ":=" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Fixes: Z
@@ -1298,8 +1612,14 @@ test_expect_success 'with simple command' '
'
test_expect_success 'with command using committer information' '
- git config trailer.sign.ifExists "addIfDifferent" &&
- git config trailer.sign.command "echo \"\$GIT_COMMITTER_NAME <\$GIT_COMMITTER_EMAIL>\"" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.fix.ifExists "doNothing" &&
+ test_config trailer.review.key "Reviewed-by:" &&
+ test_config trailer.sign.command "echo \"\$GIT_COMMITTER_NAME <\$GIT_COMMITTER_EMAIL>\"" &&
+ test_config trailer.sign.key "Signed-off-by: " &&
+ test_config trailer.sign.ifExists "addIfDifferent" &&
+ test_config trailer.ifexists "addIfDifferent" &&
+ test_config trailer.separators ":=" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Fixes: Z
@@ -1314,10 +1634,15 @@ test_expect_success 'with command using committer information' '
'
test_expect_success 'with command using author information' '
- git config trailer.sign.key "Signed-off-by: " &&
- git config trailer.sign.where "after" &&
- git config trailer.sign.ifExists "addIfDifferentNeighbor" &&
- git config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.fix.ifExists "doNothing" &&
+ test_config trailer.review.key "Reviewed-by:" &&
+ test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+ test_config trailer.sign.key "Signed-off-by: " &&
+ test_config trailer.sign.ifExists "addIfDifferentNeighbor" &&
+ test_config trailer.sign.where "after" &&
+ test_config trailer.ifexists "addIfDifferent" &&
+ test_config trailer.separators ":=" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
Fixes: Z
@@ -1338,12 +1663,19 @@ test_expect_success 'setup a commit' '
'
test_expect_success 'cmd takes precedence over command' '
- test_when_finished "git config --unset trailer.fix.cmd" &&
- git config trailer.fix.ifExists "replace" &&
- git config trailer.fix.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%aN)\" \
- --abbrev-commit --abbrev=14 \"\$1\" || true" &&
- git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
--abbrev-commit --abbrev=14 \$ARG" &&
+ test_config trailer.fix.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%aN)\" \
+ --abbrev-commit --abbrev=14 \"\$1\" || true" &&
+ test_config trailer.fix.key "Fixes: " &&
+ test_config trailer.fix.ifExists "replace" &&
+ test_config trailer.fix.where "after" &&
+ test_config trailer.review.key "Reviewed-by:" &&
+ test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+ test_config trailer.sign.key "Signed-off-by: " &&
+ test_config trailer.ifexists "addIfDifferent" &&
+ test_config trailer.separators ":=" &&
FIXED=$(git log -1 --oneline --format="%h (%aN)" --abbrev-commit --abbrev=14 HEAD) &&
cat complex_message_body >expected2 &&
sed -e "s/ Z\$/ /" >>expected2 <<-EOF &&
@@ -1359,8 +1691,16 @@ test_expect_success 'cmd takes precedence over command' '
'
test_expect_success 'with command using $ARG' '
- git config trailer.fix.ifExists "replace" &&
- git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG" &&
+ test_config trailer.fix.key "Fixes: " &&
+ test_config trailer.fix.ifExists "replace" &&
+ test_config trailer.fix.where "after" &&
+ test_config trailer.review.key "Reviewed-by:" &&
+ test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+ test_config trailer.sign.key "Signed-off-by: " &&
+ test_config trailer.ifexists "addIfDifferent" &&
+ test_config trailer.separators ":=" &&
FIXED=$(git log -1 --oneline --format="%h (%s)" --abbrev-commit --abbrev=14 HEAD) &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-EOF &&
@@ -1376,8 +1716,16 @@ test_expect_success 'with command using $ARG' '
'
test_expect_success 'with failing command using $ARG' '
- git config trailer.fix.ifExists "replace" &&
- git config trailer.fix.command "false \$ARG" &&
+ test_config trailer.ack.key "Acked-by= " &&
+ test_config trailer.fix.command "false \$ARG" &&
+ test_config trailer.fix.key "Fixes: " &&
+ test_config trailer.fix.ifExists "replace" &&
+ test_config trailer.fix.where "after" &&
+ test_config trailer.review.key "Reviewed-by:" &&
+ test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+ test_config trailer.sign.key "Signed-off-by: " &&
+ test_config trailer.ifexists "addIfDifferent" &&
+ test_config trailer.separators ":=" &&
cat complex_message_body >expected &&
sed -e "s/ Z\$/ /" >>expected <<-EOF &&
Fixes: Z
@@ -1392,7 +1740,9 @@ test_expect_success 'with failing command using $ARG' '
'
test_expect_success 'with empty tokens' '
- git config --unset trailer.fix.command &&
+ test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+ test_config trailer.sign.key "Signed-off-by: " &&
+ test_config trailer.ifexists "addIfDifferent" &&
cat >expected <<-EOF &&
Signed-off-by: A U Thor <author@example.com>
@@ -1403,7 +1753,8 @@ test_expect_success 'with empty tokens' '
'
test_expect_success 'with command but no key' '
- git config --unset trailer.sign.key &&
+ test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+ test_config trailer.ifexists "addIfDifferent" &&
cat >expected <<-EOF &&
sign: A U Thor <author@example.com>
@@ -1414,7 +1765,9 @@ test_expect_success 'with command but no key' '
'
test_expect_success 'with no command and no key' '
- git config --unset trailer.review.key &&
+ test_config trailer.review.where "before" &&
+ test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+ test_config trailer.ifexists "addIfDifferent" &&
cat >expected <<-EOF &&
review: Junio
@@ -1426,6 +1779,8 @@ test_expect_success 'with no command and no key' '
'
test_expect_success 'with cut line' '
+ test_config trailer.review.where "before" &&
+ test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
cat >expected <<-\EOF &&
my subject
@@ -1443,7 +1798,8 @@ test_expect_success 'with cut line' '
'
test_expect_success 'only trailers' '
- git config trailer.sign.command "echo config-value" &&
+ test_config trailer.sign.command "echo config-value" &&
+ test_config trailer.ifexists "addIfDifferent" &&
cat >expected <<-\EOF &&
existing: existing-value
sign: config-value
@@ -1462,7 +1818,7 @@ test_expect_success 'only trailers' '
'
test_expect_success 'only-trailers omits non-trailer in middle of block' '
- git config trailer.sign.command "echo config-value" &&
+ test_config trailer.sign.command "echo config-value" &&
cat >expected <<-\EOF &&
Signed-off-by: nobody <nobody@nowhere>
Signed-off-by: somebody <somebody@somewhere>
@@ -1482,7 +1838,7 @@ test_expect_success 'only-trailers omits non-trailer in middle of block' '
'
test_expect_success 'only input' '
- git config trailer.sign.command "echo config-value" &&
+ test_config trailer.sign.command "echo config-value" &&
cat >expected <<-\EOF &&
existing: existing-value
EOF
@@ -1560,4 +1916,37 @@ test_expect_success 'suppress --- handling' '
test_cmp expected actual
'
+test_expect_success 'suppressing --- does not disable cut-line handling' '
+ echo "real-trailer: before the cut" >expected &&
+
+ git interpret-trailers --parse --no-divider >actual <<-\EOF &&
+ subject
+
+ This input has a cut-line in it; we should stop parsing when we see it
+ and consider only trailers before that line.
+
+ real-trailer: before the cut
+
+ # ------------------------ >8 ------------------------
+ # Nothing below this line counts as part of the commit message.
+ not-a-trailer: too late
+ EOF
+
+ test_cmp expected actual
+'
+
+test_expect_success 'handling of --- lines in conjunction with cut-lines' '
+ echo "my-trailer: here" >expected &&
+
+ git interpret-trailers --parse >actual <<-\EOF &&
+ subject
+
+ my-trailer: here
+ ---
+ # ------------------------ >8 ------------------------
+ EOF
+
+ test_cmp expected actual
+'
+
test_done
diff --git a/t/t7514-commit-patch.sh b/t/t7514-commit-patch.sh
index 998a210..03ba0c0 100755
--- a/t/t7514-commit-patch.sh
+++ b/t/t7514-commit-patch.sh
@@ -1,13 +1,9 @@
#!/bin/sh
test_description='hunk edit with "commit -p -m"'
-. ./test-lib.sh
-if ! test_have_prereq PERL
-then
- skip_all="skipping '$test_description' tests, perl not available"
- test_done
-fi
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
test_expect_success 'setup (initial)' '
echo line1 >file &&
diff --git a/t/t7515-status-symlinks.sh b/t/t7515-status-symlinks.sh
index 9f989be..e3d6bb6 100755
--- a/t/t7515-status-symlinks.sh
+++ b/t/t7515-status-symlinks.sh
@@ -2,6 +2,7 @@
test_description='git status and symlinks'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t7516-commit-races.sh b/t/t7516-commit-races.sh
index f2ce14e..bb95f09 100755
--- a/t/t7516-commit-races.sh
+++ b/t/t7516-commit-races.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='git commit races'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'race to create orphan commit' '
@@ -10,7 +12,8 @@ test_expect_success 'race to create orphan commit' '
test_must_fail env EDITOR=./hare-editor git commit --allow-empty -m tortoise -e &&
git show -s --pretty=format:%s >subject &&
grep hare subject &&
- test -z "$(git show -s --pretty=format:%P)"
+ git show -s --pretty=format:%P >out &&
+ test_must_be_empty out
'
test_expect_success 'race to create non-orphan commit' '
diff --git a/t/t7517-per-repo-email.sh b/t/t7517-per-repo-email.sh
index 405420a..efc6496 100755
--- a/t/t7517-per-repo-email.sh
+++ b/t/t7517-per-repo-email.sh
@@ -9,6 +9,7 @@ test_description='per-repo forced setting of email address'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup a likely user.useConfigOnly use case' '
@@ -75,19 +76,6 @@ test_expect_success 'noop interactive rebase does not care about ident' '
git rebase -i HEAD^
'
-test_expect_success REBASE_P \
- 'fast-forward rebase does not care about ident (preserve)' '
- git checkout -B tmp side-without-commit &&
- git rebase -p main
-'
-
-test_expect_success REBASE_P \
- 'non-fast-forward rebase refuses to write commits (preserve)' '
- test_when_finished "git rebase --abort || true" &&
- git checkout -B tmp side-with-commit &&
- test_must_fail git rebase -p main
-'
-
test_expect_success 'author.name overrides user.name' '
test_config user.name user &&
test_config user.email user@example.com &&
diff --git a/t/t7518-ident-corner-cases.sh b/t/t7518-ident-corner-cases.sh
index 905957b..b37de0a 100755
--- a/t/t7518-ident-corner-cases.sh
+++ b/t/t7518-ident-corner-cases.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='corner cases in ident strings'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# confirm that we do not segfault _and_ that we do not say "(null)", as
@@ -13,15 +15,24 @@ test_expect_success 'empty name and missing email' '
sane_unset GIT_AUTHOR_EMAIL &&
GIT_AUTHOR_NAME= &&
test_must_fail git commit --allow-empty -m foo 2>err &&
- test_i18ngrep ! "(null)" err
+ test_grep ! "(null)" err
)
'
test_expect_success 'commit rejects all-crud name' '
- test_must_fail env GIT_AUTHOR_NAME=" .;<>" \
+ test_must_fail env GIT_AUTHOR_NAME=" ,;<>" \
git commit --allow-empty -m foo
'
+test_expect_success 'commit does not strip trailing dot' '
+ author_name="Pat Doe Jr." &&
+ env GIT_AUTHOR_NAME="$author_name" \
+ git commit --allow-empty -m foo &&
+ git log -1 --format=%an >actual &&
+ echo "$author_name" >expected &&
+ test_cmp actual expected
+'
+
# We must test the actual error message here, as an unwanted
# auto-detection could fail for other reasons.
test_expect_success 'empty configured name does not auto-detect' '
@@ -29,8 +40,8 @@ test_expect_success 'empty configured name does not auto-detect' '
sane_unset GIT_AUTHOR_NAME &&
test_must_fail \
git -c user.name= commit --allow-empty -m foo 2>err &&
- test_i18ngrep "empty ident name" err &&
- test_i18ngrep "Author identity unknown" err
+ test_grep "empty ident name" err &&
+ test_grep "Author identity unknown" err
)
'
@@ -39,8 +50,8 @@ test_expect_success 'empty configured name does not auto-detect for committer' '
sane_unset GIT_COMMITTER_NAME &&
test_must_fail \
git -c user.name= commit --allow-empty -m foo 2>err &&
- test_i18ngrep "empty ident name" err &&
- test_i18ngrep "Committer identity unknown" err
+ test_grep "empty ident name" err &&
+ test_grep "Committer identity unknown" err
)
'
diff --git a/t/t7519-status-fsmonitor.sh b/t/t7519-status-fsmonitor.sh
index deea88d..7ee69ec 100755
--- a/t/t7519-status-fsmonitor.sh
+++ b/t/t7519-status-fsmonitor.sh
@@ -26,7 +26,7 @@ dirty_repo () {
}
write_integration_script () {
- write_script .git/hooks/fsmonitor-test<<-\EOF
+ test_hook --setup --clobber fsmonitor-test<<-\EOF
if test "$#" -ne 2
then
echo "$0: exactly 2 arguments expected"
@@ -55,8 +55,39 @@ test_lazy_prereq UNTRACKED_CACHE '
test $ret -ne 1
'
+# Test that we detect and disallow repos that are incompatible with FSMonitor.
+test_expect_success 'incompatible bare repo' '
+ test_when_finished "rm -rf ./bare-clone actual expect" &&
+ git init --bare bare-clone &&
+
+ test_must_fail \
+ git -C ./bare-clone -c core.fsmonitor=foo \
+ update-index --fsmonitor 2>actual &&
+ grep "bare repository .* is incompatible with fsmonitor" actual &&
+
+ test_must_fail \
+ git -C ./bare-clone -c core.fsmonitor=true \
+ update-index --fsmonitor 2>actual &&
+ grep "bare repository .* is incompatible with fsmonitor" actual
+'
+
+test_expect_success FSMONITOR_DAEMON 'run fsmonitor-daemon in bare repo' '
+ test_when_finished "rm -rf ./bare-clone actual" &&
+ git init --bare bare-clone &&
+ test_must_fail git -C ./bare-clone fsmonitor--daemon run 2>actual &&
+ grep "bare repository .* is incompatible with fsmonitor" actual
+'
+
+test_expect_success MINGW,FSMONITOR_DAEMON 'run fsmonitor-daemon in virtual repo' '
+ test_when_finished "rm -rf ./fake-virtual-clone actual" &&
+ git init fake-virtual-clone &&
+ test_must_fail git -C ./fake-virtual-clone \
+ -c core.virtualfilesystem=true \
+ fsmonitor--daemon run 2>actual &&
+ grep "virtual repository .* is incompatible with fsmonitor" actual
+'
+
test_expect_success 'setup' '
- mkdir -p .git/hooks &&
: >tracked &&
: >modified &&
mkdir dir1 &&
@@ -108,7 +139,7 @@ EOF
# test that "update-index --fsmonitor-valid" sets the fsmonitor valid bit
test_expect_success 'update-index --fsmonitor-valid" sets the fsmonitor valid bit' '
- write_script .git/hooks/fsmonitor-test<<-\EOF &&
+ test_hook fsmonitor-test<<-\EOF &&
printf "last_update_token\0"
EOF
git update-index --fsmonitor &&
@@ -169,7 +200,7 @@ EOF
# test that newly added files are marked valid
test_expect_success 'newly added files are marked valid' '
- write_script .git/hooks/fsmonitor-test<<-\EOF &&
+ test_hook --setup --clobber fsmonitor-test<<-\EOF &&
printf "last_update_token\0"
EOF
git add new &&
@@ -210,7 +241,7 @@ EOF
# test that *only* files returned by the integration script get flagged as invalid
test_expect_success '*only* files returned by the integration script get flagged as invalid' '
- write_script .git/hooks/fsmonitor-test<<-\EOF &&
+ test_hook --clobber fsmonitor-test<<-\EOF &&
printf "last_update_token\0"
printf "dir1/modified\0"
EOF
@@ -231,7 +262,7 @@ test_expect_success 'refresh_index() invalidates fsmonitor cache' '
dirty_repo &&
write_integration_script &&
git add . &&
- write_script .git/hooks/fsmonitor-test<<-\EOF &&
+ test_hook --clobber fsmonitor-test<<-\EOF &&
EOF
git commit -m "to reset" &&
git reset HEAD~1 &&
@@ -248,7 +279,7 @@ do
git config core.preloadIndex $preload_val &&
if test $preload_val = true
then
- GIT_TEST_PRELOAD_INDEX=$preload_val; export GIT_TEST_PRELOAD_INDEX
+ GIT_TEST_PRELOAD_INDEX=$preload_val && export GIT_TEST_PRELOAD_INDEX
else
sane_unset GIT_TEST_PRELOAD_INDEX
fi
@@ -280,7 +311,7 @@ do
# Make sure it's actually skipping the check for modified and untracked
# (if enabled) files unless it is told about them.
test_expect_success "status doesn't detect unreported modifications" '
- write_script .git/hooks/fsmonitor-test<<-\EOF &&
+ test_hook --clobber fsmonitor-test<<-\EOF &&
printf "last_update_token\0"
:>marker
EOF
@@ -291,14 +322,14 @@ do
rm -f marker &&
git status >actual &&
test_path_is_file marker &&
- test_i18ngrep ! "Changes not staged for commit:" actual &&
+ test_grep ! "Changes not staged for commit:" actual &&
if test $uc_val = true
then
- test_i18ngrep ! "Untracked files:" actual
+ test_grep ! "Untracked files:" actual
fi &&
if test $uc_val = false
then
- test_i18ngrep "Untracked files:" actual
+ test_grep "Untracked files:" actual
fi &&
rm -f marker
'
@@ -322,19 +353,25 @@ test_expect_success UNTRACKED_CACHE 'ignore .git changes when invalidating UNTR'
test_create_repo dot-git &&
(
cd dot-git &&
- mkdir -p .git/hooks &&
: >tracked &&
+ test-tool chmtime =-60 tracked &&
: >modified &&
+ test-tool chmtime =-60 modified &&
mkdir dir1 &&
: >dir1/tracked &&
+ test-tool chmtime =-60 dir1/tracked &&
: >dir1/modified &&
+ test-tool chmtime =-60 dir1/modified &&
mkdir dir2 &&
: >dir2/tracked &&
+ test-tool chmtime =-60 dir2/tracked &&
: >dir2/modified &&
+ test-tool chmtime =-60 dir2/modified &&
write_integration_script &&
git config core.fsmonitor .git/hooks/fsmonitor-test &&
git update-index --untracked-cache &&
git update-index --fsmonitor &&
+ git status &&
GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace-before" \
git status &&
test-tool dump-untracked-cache >../before
@@ -389,47 +426,55 @@ test_expect_success 'status succeeds after staging/unstaging' '
# If "!" is supplied, then we verify that we do not call ensure_full_index
# during a call to 'git status'. Otherwise, we verify that we _do_ call it.
check_sparse_index_behavior () {
- git status --porcelain=v2 >expect &&
- git sparse-checkout init --cone --sparse-index &&
- git sparse-checkout set dir1 dir2 &&
- GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
- git status --porcelain=v2 >actual &&
+ git -C full status --porcelain=v2 >expect &&
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+ git -C sparse status --porcelain=v2 >actual &&
test_region $1 index ensure_full_index trace2.txt &&
test_region fsm_hook query trace2.txt &&
test_cmp expect actual &&
- rm trace2.txt &&
- git sparse-checkout disable
+ rm trace2.txt
}
test_expect_success 'status succeeds with sparse index' '
- git reset --hard &&
-
- test_config core.fsmonitor "$TEST_DIRECTORY/t7519/fsmonitor-all" &&
- check_sparse_index_behavior ! &&
-
- write_script .git/hooks/fsmonitor-test<<-\EOF &&
- printf "last_update_token\0"
- EOF
- git config core.fsmonitor .git/hooks/fsmonitor-test &&
- check_sparse_index_behavior ! &&
+ (
+ sane_unset GIT_TEST_SPLIT_INDEX &&
- write_script .git/hooks/fsmonitor-test<<-\EOF &&
- printf "last_update_token\0"
- printf "dir1/modified\0"
- EOF
- check_sparse_index_behavior ! &&
+ git clone . full &&
+ git clone --sparse . sparse &&
+ git -C sparse sparse-checkout init --cone --sparse-index &&
+ git -C sparse sparse-checkout set dir1 dir2 &&
- cp -r dir1 dir1a &&
- git add dir1a &&
- git commit -m "add dir1a" &&
+ test_hook --clobber fsmonitor-test <<-\EOF &&
+ printf "last_update_token\0"
+ EOF
+ git -C full config core.fsmonitor ../.git/hooks/fsmonitor-test &&
+ git -C sparse config core.fsmonitor ../.git/hooks/fsmonitor-test &&
+ check_sparse_index_behavior ! &&
- # This one modifies outside the sparse-checkout definition
- # and hence we expect to expand the sparse-index.
- write_script .git/hooks/fsmonitor-test<<-\EOF &&
- printf "last_update_token\0"
- printf "dir1a/modified\0"
- EOF
- check_sparse_index_behavior
+ test_hook --clobber fsmonitor-test <<-\EOF &&
+ printf "last_update_token\0"
+ printf "dir1/modified\0"
+ EOF
+ check_sparse_index_behavior ! &&
+
+ git -C sparse sparse-checkout add dir1a &&
+
+ for repo in full sparse
+ do
+ cp -r $repo/dir1 $repo/dir1a &&
+ git -C $repo add dir1a &&
+ git -C $repo commit -m "add dir1a" || return 1
+ done &&
+ git -C sparse sparse-checkout set dir1 dir2 &&
+
+ # This one modifies outside the sparse-checkout definition
+ # and hence we expect to expand the sparse-index.
+ test_hook --clobber fsmonitor-test <<-\EOF &&
+ printf "last_update_token\0"
+ printf "dir1a/modified\0"
+ EOF
+ check_sparse_index_behavior
+ )
'
test_done
diff --git a/t/t7520-ignored-hook-warning.sh b/t/t7520-ignored-hook-warning.sh
index 634fb7f..3b63c34 100755
--- a/t/t7520-ignored-hook-warning.sh
+++ b/t/t7520-ignored-hook-warning.sh
@@ -2,40 +2,38 @@
test_description='ignored hook warning'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
- hookdir="$(git rev-parse --git-dir)/hooks" &&
- hook="$hookdir/pre-commit" &&
- mkdir -p "$hookdir" &&
- write_script "$hook" <<-\EOF
+ test_hook --setup pre-commit <<-\EOF
exit 0
EOF
'
test_expect_success 'no warning if hook is not ignored' '
git commit --allow-empty -m "more" 2>message &&
- test_i18ngrep ! -e "hook was ignored" message
+ test_grep ! -e "hook was ignored" message
'
test_expect_success POSIXPERM 'warning if hook is ignored' '
- chmod -x "$hook" &&
+ test_hook --disable pre-commit &&
git commit --allow-empty -m "even more" 2>message &&
- test_i18ngrep -e "hook was ignored" message
+ test_grep -e "hook was ignored" message
'
test_expect_success POSIXPERM 'no warning if advice.ignoredHook set to false' '
test_config advice.ignoredHook false &&
- chmod -x "$hook" &&
+ test_hook --disable pre-commit &&
git commit --allow-empty -m "even more" 2>message &&
- test_i18ngrep ! -e "hook was ignored" message
+ test_grep ! -e "hook was ignored" message
'
test_expect_success 'no warning if unset advice.ignoredHook and hook removed' '
- rm -f "$hook" &&
+ test_hook --remove pre-commit &&
test_unconfig advice.ignoredHook &&
git commit --allow-empty -m "even more" 2>message &&
- test_i18ngrep ! -e "hook was ignored" message
+ test_grep ! -e "hook was ignored" message
'
test_done
diff --git a/t/t7524-commit-summary.sh b/t/t7524-commit-summary.sh
new file mode 100755
index 0000000..47b2f1d
--- /dev/null
+++ b/t/t7524-commit-summary.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+test_description='git commit summary'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_seq 101 200 >file &&
+ git add file &&
+ git commit -m initial &&
+ git tag initial
+'
+
+test_expect_success 'commit summary ignores rewrites' '
+ git reset --hard initial &&
+ test_seq 200 300 >file &&
+
+ git diff --stat >diffstat &&
+ git diff --stat --break-rewrites >diffstatrewrite &&
+
+ # make sure this scenario is a detectable rewrite
+ ! test_cmp_bin diffstat diffstatrewrite &&
+
+ git add file &&
+ git commit -m second >actual &&
+
+ grep "1 file" <actual >actual.total &&
+ grep "1 file" <diffstat >diffstat.total &&
+ test_cmp diffstat.total actual.total
+'
+
+test_done
diff --git a/t/t7525-status-rename.sh b/t/t7525-status-rename.sh
index a62736d..a9210d3 100755
--- a/t/t7525-status-rename.sh
+++ b/t/t7525-status-rename.sh
@@ -2,6 +2,7 @@
test_description='git status rename detection options'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -20,81 +21,81 @@ test_expect_success 'setup' '
test_expect_success 'status no-options' '
git status >actual &&
- test_i18ngrep "renamed:" actual
+ test_grep "renamed:" actual
'
test_expect_success 'status --no-renames' '
git status --no-renames >actual &&
- test_i18ngrep "deleted:" actual &&
- test_i18ngrep "new file:" actual
+ test_grep "deleted:" actual &&
+ test_grep "new file:" actual
'
test_expect_success 'status.renames inherits from diff.renames false' '
git -c diff.renames=false status >actual &&
- test_i18ngrep "deleted:" actual &&
- test_i18ngrep "new file:" actual
+ test_grep "deleted:" actual &&
+ test_grep "new file:" actual
'
test_expect_success 'status.renames inherits from diff.renames true' '
git -c diff.renames=true status >actual &&
- test_i18ngrep "renamed:" actual
+ test_grep "renamed:" actual
'
test_expect_success 'status.renames overrides diff.renames false' '
git -c diff.renames=true -c status.renames=false status >actual &&
- test_i18ngrep "deleted:" actual &&
- test_i18ngrep "new file:" actual
+ test_grep "deleted:" actual &&
+ test_grep "new file:" actual
'
test_expect_success 'status.renames overrides from diff.renames true' '
git -c diff.renames=false -c status.renames=true status >actual &&
- test_i18ngrep "renamed:" actual
+ test_grep "renamed:" actual
'
test_expect_success 'status status.renames=false' '
git -c status.renames=false status >actual &&
- test_i18ngrep "deleted:" actual &&
- test_i18ngrep "new file:" actual
+ test_grep "deleted:" actual &&
+ test_grep "new file:" actual
'
test_expect_success 'status status.renames=true' '
git -c status.renames=true status >actual &&
- test_i18ngrep "renamed:" actual
+ test_grep "renamed:" actual
'
test_expect_success 'commit honors status.renames=false' '
git -c status.renames=false commit --dry-run >actual &&
- test_i18ngrep "deleted:" actual &&
- test_i18ngrep "new file:" actual
+ test_grep "deleted:" actual &&
+ test_grep "new file:" actual
'
test_expect_success 'commit honors status.renames=true' '
git -c status.renames=true commit --dry-run >actual &&
- test_i18ngrep "renamed:" actual
+ test_grep "renamed:" actual
'
test_expect_success 'status config overridden' '
git -c status.renames=true status --no-renames >actual &&
- test_i18ngrep "deleted:" actual &&
- test_i18ngrep "new file:" actual
+ test_grep "deleted:" actual &&
+ test_grep "new file:" actual
'
test_expect_success 'status score=100%' '
git status -M=100% >actual &&
- test_i18ngrep "deleted:" actual &&
- test_i18ngrep "new file:" actual &&
+ test_grep "deleted:" actual &&
+ test_grep "new file:" actual &&
git status --find-renames=100% >actual &&
- test_i18ngrep "deleted:" actual &&
- test_i18ngrep "new file:" actual
+ test_grep "deleted:" actual &&
+ test_grep "new file:" actual
'
test_expect_success 'status score=01%' '
git status -M=01% >actual &&
- test_i18ngrep "renamed:" actual &&
+ test_grep "renamed:" actual &&
git status --find-renames=01% >actual &&
- test_i18ngrep "renamed:" actual
+ test_grep "renamed:" actual
'
test_expect_success 'copies not overridden by find-renames' '
@@ -102,12 +103,12 @@ test_expect_success 'copies not overridden by find-renames' '
git add copy &&
git -c status.renames=copies status -M=01% >actual &&
- test_i18ngrep "copied:" actual &&
- test_i18ngrep "renamed:" actual &&
+ test_grep "copied:" actual &&
+ test_grep "renamed:" actual &&
git -c status.renames=copies status --find-renames=01% >actual &&
- test_i18ngrep "copied:" actual &&
- test_i18ngrep "renamed:" actual
+ test_grep "copied:" actual &&
+ test_grep "renamed:" actual
'
test_done
diff --git a/t/t7526-commit-pathspec-file.sh b/t/t7526-commit-pathspec-file.sh
index 5fbe47e..c97c550 100755
--- a/t/t7526-commit-pathspec-file.sh
+++ b/t/t7526-commit-pathspec-file.sh
@@ -2,6 +2,7 @@
test_description='commit --pathspec-from-file'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_tick
@@ -140,25 +141,25 @@ test_expect_success 'error conditions' '
>empty_list &&
test_must_fail git commit --pathspec-from-file=list --interactive -m "Commit" 2>err &&
- test_i18ngrep -e "--pathspec-from-file is incompatible with --interactive/--patch" err &&
+ test_grep -e "options .--pathspec-from-file. and .--interactive/--patch. cannot be used together" err &&
test_must_fail git commit --pathspec-from-file=list --patch -m "Commit" 2>err &&
- test_i18ngrep -e "--pathspec-from-file is incompatible with --interactive/--patch" err &&
+ test_grep -e "options .--pathspec-from-file. and .--interactive/--patch. cannot be used together" err &&
test_must_fail git commit --pathspec-from-file=list --all -m "Commit" 2>err &&
- test_i18ngrep -e "--pathspec-from-file with -a does not make sense" err &&
+ test_grep -e "options .--pathspec-from-file. and .-a. cannot be used together" err &&
test_must_fail git commit --pathspec-from-file=list -m "Commit" -- fileA.t 2>err &&
- test_i18ngrep -e "--pathspec-from-file is incompatible with pathspec arguments" err &&
+ test_grep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
test_must_fail git commit --pathspec-file-nul -m "Commit" 2>err &&
- test_i18ngrep -e "--pathspec-file-nul requires --pathspec-from-file" err &&
+ test_grep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
test_must_fail git commit --pathspec-from-file=empty_list --include -m "Commit" 2>err &&
- test_i18ngrep -e "No paths with --include/--only does not make sense." err &&
+ test_grep -e "No paths with --include/--only does not make sense." err &&
test_must_fail git commit --pathspec-from-file=empty_list --only -m "Commit" 2>err &&
- test_i18ngrep -e "No paths with --include/--only does not make sense." err
+ test_grep -e "No paths with --include/--only does not make sense." err
'
test_done
diff --git a/t/t7527-builtin-fsmonitor.sh b/t/t7527-builtin-fsmonitor.sh
new file mode 100755
index 0000000..730f3c7
--- /dev/null
+++ b/t/t7527-builtin-fsmonitor.sh
@@ -0,0 +1,1263 @@
+#!/bin/sh
+
+test_description='built-in file system watcher'
+
+. ./test-lib.sh
+
+if ! test_have_prereq FSMONITOR_DAEMON
+then
+ skip_all="fsmonitor--daemon is not supported on this platform"
+ test_done
+fi
+
+stop_daemon_delete_repo () {
+ r=$1 &&
+ test_might_fail git -C $r fsmonitor--daemon stop &&
+ rm -rf $1
+}
+
+start_daemon () {
+ r= tf= t2= tk= &&
+
+ while test "$#" -ne 0
+ do
+ case "$1" in
+ -C)
+ r="-C ${2?}"
+ shift
+ ;;
+ --tf)
+ tf="${2?}"
+ shift
+ ;;
+ --t2)
+ t2="${2?}"
+ shift
+ ;;
+ --tk)
+ tk="${2?}"
+ shift
+ ;;
+ -*)
+ BUG "error: unknown option: '$1'"
+ ;;
+ *)
+ BUG "error: unbound argument: '$1'"
+ ;;
+ esac
+ shift
+ done &&
+
+ (
+ if test -n "$tf"
+ then
+ GIT_TRACE_FSMONITOR="$tf"
+ export GIT_TRACE_FSMONITOR
+ fi &&
+
+ if test -n "$t2"
+ then
+ GIT_TRACE2_PERF="$t2"
+ export GIT_TRACE2_PERF
+ fi &&
+
+ if test -n "$tk"
+ then
+ GIT_TEST_FSMONITOR_TOKEN="$tk"
+ export GIT_TEST_FSMONITOR_TOKEN
+ fi &&
+
+ git $r fsmonitor--daemon start &&
+ git $r fsmonitor--daemon status
+ )
+}
+
+# Is a Trace2 data event present with the given catetory and key?
+# We do not care what the value is.
+#
+have_t2_data_event () {
+ c=$1 &&
+ k=$2 &&
+
+ grep -e '"event":"data".*"category":"'"$c"'".*"key":"'"$k"'"'
+}
+
+test_expect_success 'explicit daemon start and stop' '
+ test_when_finished "stop_daemon_delete_repo test_explicit" &&
+
+ git init test_explicit &&
+ start_daemon -C test_explicit &&
+
+ git -C test_explicit fsmonitor--daemon stop &&
+ test_must_fail git -C test_explicit fsmonitor--daemon status
+'
+
+test_expect_success 'implicit daemon start' '
+ test_when_finished "stop_daemon_delete_repo test_implicit" &&
+
+ git init test_implicit &&
+ test_must_fail git -C test_implicit fsmonitor--daemon status &&
+
+ # query will implicitly start the daemon.
+ #
+ # for test-script simplicity, we send a V1 timestamp rather than
+ # a V2 token. either way, the daemon response to any query contains
+ # a new V2 token. (the daemon may complain that we sent a V1 request,
+ # but this test case is only concerned with whether the daemon was
+ # implicitly started.)
+
+ GIT_TRACE2_EVENT="$PWD/.git/trace" \
+ test-tool -C test_implicit fsmonitor-client query --token 0 >actual &&
+ nul_to_q <actual >actual.filtered &&
+ grep "builtin:" actual.filtered &&
+
+ # confirm that a daemon was started in the background.
+ #
+ # since the mechanism for starting the background daemon is platform
+ # dependent, just confirm that the foreground command received a
+ # response from the daemon.
+
+ have_t2_data_event fsm_client query/response-length <.git/trace &&
+
+ git -C test_implicit fsmonitor--daemon status &&
+ git -C test_implicit fsmonitor--daemon stop &&
+ test_must_fail git -C test_implicit fsmonitor--daemon status
+'
+
+# Verify that the daemon has shutdown. Spin a few seconds to
+# make the test a little more robust during CI testing.
+#
+# We're looking for an implicit shutdown, such as when we delete or
+# rename the ".git" directory. Our delete/rename will cause a file
+# system event that the daemon will see and the daemon will
+# auto-shutdown as soon as it sees it. But this is racy with our `git
+# fsmonitor--daemon status` commands (and we cannot use a cookie file
+# here to help us). So spin a little and give the daemon a chance to
+# see the event. (This is primarily for underpowered CI build/test
+# machines (where it might take a moment to wake and reschedule the
+# daemon process) to avoid false alarms during test runs.)
+#
+IMPLICIT_TIMEOUT=5
+
+verify_implicit_shutdown () {
+ r=$1 &&
+
+ k=0 &&
+ while test "$k" -lt $IMPLICIT_TIMEOUT
+ do
+ git -C $r fsmonitor--daemon status || return 0
+
+ sleep 1
+ k=$(( $k + 1 ))
+ done &&
+
+ return 1
+}
+
+test_expect_success 'implicit daemon stop (delete .git)' '
+ test_when_finished "stop_daemon_delete_repo test_implicit_1" &&
+
+ git init test_implicit_1 &&
+
+ start_daemon -C test_implicit_1 &&
+
+ # deleting the .git directory will implicitly stop the daemon.
+ rm -rf test_implicit_1/.git &&
+
+ # [1] Create an empty .git directory so that the following Git
+ # command will stay relative to the `-C` directory.
+ #
+ # Without this, the Git command will override the requested
+ # -C argument and crawl out to the containing Git source tree.
+ # This would make the test result dependent upon whether we
+ # were using fsmonitor on our development worktree.
+ #
+ mkdir test_implicit_1/.git &&
+
+ verify_implicit_shutdown test_implicit_1
+'
+
+test_expect_success 'implicit daemon stop (rename .git)' '
+ test_when_finished "stop_daemon_delete_repo test_implicit_2" &&
+
+ git init test_implicit_2 &&
+
+ start_daemon -C test_implicit_2 &&
+
+ # renaming the .git directory will implicitly stop the daemon.
+ mv test_implicit_2/.git test_implicit_2/.xxx &&
+
+ # See [1] above.
+ #
+ mkdir test_implicit_2/.git &&
+
+ verify_implicit_shutdown test_implicit_2
+'
+
+# File systems on Windows may or may not have shortnames.
+# This is a volume-specific setting on modern systems.
+# "C:/" drives are required to have them enabled. Other
+# hard drives default to disabled.
+#
+# This is a crude test to see if shortnames are enabled
+# on the volume containing the test directory. It is
+# crude, but it does not require elevation like `fsutil`.
+#
+test_lazy_prereq SHORTNAMES '
+ mkdir .foo &&
+ test -d "FOO~1"
+'
+
+# Here we assume that the shortname of ".git" is "GIT~1".
+test_expect_success MINGW,SHORTNAMES 'implicit daemon stop (rename GIT~1)' '
+ test_when_finished "stop_daemon_delete_repo test_implicit_1s" &&
+
+ git init test_implicit_1s &&
+
+ start_daemon -C test_implicit_1s &&
+
+ # renaming the .git directory will implicitly stop the daemon.
+ # this moves {.git, GIT~1} to {.gitxyz, GITXYZ~1}.
+ # the rename-from FS Event will contain the shortname.
+ #
+ mv test_implicit_1s/GIT~1 test_implicit_1s/.gitxyz &&
+
+ # See [1] above.
+ # this moves {.gitxyz, GITXYZ~1} to {.git, GIT~1}.
+ mv test_implicit_1s/.gitxyz test_implicit_1s/.git &&
+
+ verify_implicit_shutdown test_implicit_1s
+'
+
+# Here we first create a file with LONGNAME of "GIT~1" before
+# we create the repo. This will cause the shortname of ".git"
+# to be "GIT~2".
+test_expect_success MINGW,SHORTNAMES 'implicit daemon stop (rename GIT~2)' '
+ test_when_finished "stop_daemon_delete_repo test_implicit_1s2" &&
+
+ mkdir test_implicit_1s2 &&
+ echo HELLO >test_implicit_1s2/GIT~1 &&
+ git init test_implicit_1s2 &&
+
+ test_path_is_file test_implicit_1s2/GIT~1 &&
+ test_path_is_dir test_implicit_1s2/GIT~2 &&
+
+ start_daemon -C test_implicit_1s2 &&
+
+ # renaming the .git directory will implicitly stop the daemon.
+ # the rename-from FS Event will contain the shortname.
+ #
+ mv test_implicit_1s2/GIT~2 test_implicit_1s2/.gitxyz &&
+
+ # See [1] above.
+ mv test_implicit_1s2/.gitxyz test_implicit_1s2/.git &&
+
+ verify_implicit_shutdown test_implicit_1s2
+'
+
+test_expect_success 'cannot start multiple daemons' '
+ test_when_finished "stop_daemon_delete_repo test_multiple" &&
+
+ git init test_multiple &&
+
+ start_daemon -C test_multiple &&
+
+ test_must_fail git -C test_multiple fsmonitor--daemon start 2>actual &&
+ grep "fsmonitor--daemon is already running" actual &&
+
+ git -C test_multiple fsmonitor--daemon stop &&
+ test_must_fail git -C test_multiple fsmonitor--daemon status
+'
+
+# These tests use the main repo in the trash directory
+
+test_expect_success 'setup' '
+ >tracked &&
+ >modified &&
+ >delete &&
+ >rename &&
+ mkdir dir1 &&
+ >dir1/tracked &&
+ >dir1/modified &&
+ >dir1/delete &&
+ >dir1/rename &&
+ mkdir dir2 &&
+ >dir2/tracked &&
+ >dir2/modified &&
+ >dir2/delete &&
+ >dir2/rename &&
+ mkdir dirtorename &&
+ >dirtorename/a &&
+ >dirtorename/b &&
+
+ cat >.gitignore <<-\EOF &&
+ .gitignore
+ expect*
+ actual*
+ flush*
+ trace*
+ EOF
+
+ mkdir -p T1/T2/T3/T4 &&
+ echo 1 >T1/F1 &&
+ echo 1 >T1/T2/F1 &&
+ echo 1 >T1/T2/T3/F1 &&
+ echo 1 >T1/T2/T3/T4/F1 &&
+ echo 2 >T1/F2 &&
+ echo 2 >T1/T2/F2 &&
+ echo 2 >T1/T2/T3/F2 &&
+ echo 2 >T1/T2/T3/T4/F2 &&
+
+ git -c core.fsmonitor=false add . &&
+ test_tick &&
+ git -c core.fsmonitor=false commit -m initial &&
+
+ git config core.fsmonitor true
+'
+
+# The test already explicitly stopped (or tried to stop) the daemon.
+# This is here in case something else fails first.
+#
+redundant_stop_daemon () {
+ test_might_fail git fsmonitor--daemon stop
+}
+
+test_expect_success 'update-index implicitly starts daemon' '
+ test_when_finished redundant_stop_daemon &&
+
+ test_must_fail git fsmonitor--daemon status &&
+
+ GIT_TRACE2_EVENT="$PWD/.git/trace_implicit_1" \
+ git update-index --fsmonitor &&
+
+ git fsmonitor--daemon status &&
+ test_might_fail git fsmonitor--daemon stop &&
+
+ # Confirm that the trace2 log contains a record of the
+ # daemon starting.
+ test_subcommand git fsmonitor--daemon start <.git/trace_implicit_1
+'
+
+test_expect_success 'status implicitly starts daemon' '
+ test_when_finished redundant_stop_daemon &&
+
+ test_must_fail git fsmonitor--daemon status &&
+
+ GIT_TRACE2_EVENT="$PWD/.git/trace_implicit_2" \
+ git status >actual &&
+
+ git fsmonitor--daemon status &&
+ test_might_fail git fsmonitor--daemon stop &&
+
+ # Confirm that the trace2 log contains a record of the
+ # daemon starting.
+ test_subcommand git fsmonitor--daemon start <.git/trace_implicit_2
+'
+
+edit_files () {
+ echo 1 >modified &&
+ echo 2 >dir1/modified &&
+ echo 3 >dir2/modified &&
+ >dir1/untracked
+}
+
+delete_files () {
+ rm -f delete &&
+ rm -f dir1/delete &&
+ rm -f dir2/delete
+}
+
+create_files () {
+ echo 1 >new &&
+ echo 2 >dir1/new &&
+ echo 3 >dir2/new
+}
+
+rename_files () {
+ mv rename renamed &&
+ mv dir1/rename dir1/renamed &&
+ mv dir2/rename dir2/renamed
+}
+
+file_to_directory () {
+ rm -f delete &&
+ mkdir delete &&
+ echo 1 >delete/new
+}
+
+directory_to_file () {
+ rm -rf dir1 &&
+ echo 1 >dir1
+}
+
+move_directory_contents_deeper() {
+ mkdir T1/_new_ &&
+ mv T1/[A-Z]* T1/_new_
+}
+
+move_directory_up() {
+ mv T1/T2/T3 T1
+}
+
+move_directory() {
+ mv T1/T2/T3 T1/T2/NewT3
+}
+
+# The next few test cases confirm that our fsmonitor daemon sees each type
+# of OS filesystem notification that we care about. At this layer we just
+# ensure we are getting the OS notifications and do not try to confirm what
+# is reported by `git status`.
+#
+# We run a simple query after modifying the filesystem just to introduce
+# a bit of a delay so that the trace logging from the daemon has time to
+# get flushed to disk.
+#
+# We `reset` and `clean` at the bottom of each test (and before stopping the
+# daemon) because these commands might implicitly restart the daemon.
+
+clean_up_repo_and_stop_daemon () {
+ git reset --hard HEAD &&
+ git clean -fd &&
+ test_might_fail git fsmonitor--daemon stop &&
+ rm -f .git/trace
+}
+
+test_expect_success 'edit some files' '
+ test_when_finished clean_up_repo_and_stop_daemon &&
+
+ start_daemon --tf "$PWD/.git/trace" &&
+
+ edit_files &&
+
+ test-tool fsmonitor-client query --token 0 &&
+
+ grep "^event: dir1/modified$" .git/trace &&
+ grep "^event: dir2/modified$" .git/trace &&
+ grep "^event: modified$" .git/trace &&
+ grep "^event: dir1/untracked$" .git/trace
+'
+
+test_expect_success 'create some files' '
+ test_when_finished clean_up_repo_and_stop_daemon &&
+
+ start_daemon --tf "$PWD/.git/trace" &&
+
+ create_files &&
+
+ test-tool fsmonitor-client query --token 0 &&
+
+ grep "^event: dir1/new$" .git/trace &&
+ grep "^event: dir2/new$" .git/trace &&
+ grep "^event: new$" .git/trace
+'
+
+test_expect_success 'delete some files' '
+ test_when_finished clean_up_repo_and_stop_daemon &&
+
+ start_daemon --tf "$PWD/.git/trace" &&
+
+ delete_files &&
+
+ test-tool fsmonitor-client query --token 0 &&
+
+ grep "^event: dir1/delete$" .git/trace &&
+ grep "^event: dir2/delete$" .git/trace &&
+ grep "^event: delete$" .git/trace
+'
+
+test_expect_success 'rename some files' '
+ test_when_finished clean_up_repo_and_stop_daemon &&
+
+ start_daemon --tf "$PWD/.git/trace" &&
+
+ rename_files &&
+
+ test-tool fsmonitor-client query --token 0 &&
+
+ grep "^event: dir1/rename$" .git/trace &&
+ grep "^event: dir2/rename$" .git/trace &&
+ grep "^event: rename$" .git/trace &&
+ grep "^event: dir1/renamed$" .git/trace &&
+ grep "^event: dir2/renamed$" .git/trace &&
+ grep "^event: renamed$" .git/trace
+'
+
+test_expect_success 'rename directory' '
+ test_when_finished clean_up_repo_and_stop_daemon &&
+
+ start_daemon --tf "$PWD/.git/trace" &&
+
+ mv dirtorename dirrenamed &&
+
+ test-tool fsmonitor-client query --token 0 &&
+
+ grep "^event: dirtorename/*$" .git/trace &&
+ grep "^event: dirrenamed/*$" .git/trace
+'
+
+test_expect_success 'file changes to directory' '
+ test_when_finished clean_up_repo_and_stop_daemon &&
+
+ start_daemon --tf "$PWD/.git/trace" &&
+
+ file_to_directory &&
+
+ test-tool fsmonitor-client query --token 0 &&
+
+ grep "^event: delete$" .git/trace &&
+ grep "^event: delete/new$" .git/trace
+'
+
+test_expect_success 'directory changes to a file' '
+ test_when_finished clean_up_repo_and_stop_daemon &&
+
+ start_daemon --tf "$PWD/.git/trace" &&
+
+ directory_to_file &&
+
+ test-tool fsmonitor-client query --token 0 &&
+
+ grep "^event: dir1$" .git/trace
+'
+
+# The next few test cases exercise the token-resync code. When filesystem
+# drops events (because of filesystem velocity or because the daemon isn't
+# polling fast enough), we need to discard the cached data (relative to the
+# current token) and start collecting events under a new token.
+#
+# the 'test-tool fsmonitor-client flush' command can be used to send a
+# "flush" message to a running daemon and ask it to do a flush/resync.
+
+test_expect_success 'flush cached data' '
+ test_when_finished "stop_daemon_delete_repo test_flush" &&
+
+ git init test_flush &&
+
+ start_daemon -C test_flush --tf "$PWD/.git/trace_daemon" --tk true &&
+
+ # The daemon should have an initial token with no events in _0 and
+ # then a few (probably platform-specific number of) events in _1.
+ # These should both have the same <token_id>.
+
+ test-tool -C test_flush fsmonitor-client query --token "builtin:test_00000001:0" >actual_0 &&
+ nul_to_q <actual_0 >actual_q0 &&
+
+ >test_flush/file_1 &&
+ >test_flush/file_2 &&
+
+ test-tool -C test_flush fsmonitor-client query --token "builtin:test_00000001:0" >actual_1 &&
+ nul_to_q <actual_1 >actual_q1 &&
+
+ grep "file_1" actual_q1 &&
+
+ # Force a flush. This will change the <token_id>, reset the <seq_nr>, and
+ # flush the file data. Then create some events and ensure that the file
+ # again appears in the cache. It should have the new <token_id>.
+
+ test-tool -C test_flush fsmonitor-client flush >flush_0 &&
+ nul_to_q <flush_0 >flush_q0 &&
+ grep "^builtin:test_00000002:0Q/Q$" flush_q0 &&
+
+ test-tool -C test_flush fsmonitor-client query --token "builtin:test_00000002:0" >actual_2 &&
+ nul_to_q <actual_2 >actual_q2 &&
+
+ grep "^builtin:test_00000002:0Q$" actual_q2 &&
+
+ >test_flush/file_3 &&
+
+ test-tool -C test_flush fsmonitor-client query --token "builtin:test_00000002:0" >actual_3 &&
+ nul_to_q <actual_3 >actual_q3 &&
+
+ grep "file_3" actual_q3
+'
+
+# The next few test cases create repos where the .git directory is NOT
+# inside the one of the working directory. That is, where .git is a file
+# that points to a directory elsewhere. This happens for submodules and
+# non-primary worktrees.
+
+test_expect_success 'setup worktree base' '
+ git init wt-base &&
+ echo 1 >wt-base/file1 &&
+ git -C wt-base add file1 &&
+ git -C wt-base commit -m "c1"
+'
+
+test_expect_success 'worktree with .git file' '
+ git -C wt-base worktree add ../wt-secondary &&
+
+ start_daemon -C wt-secondary \
+ --tf "$PWD/trace_wt_secondary" \
+ --t2 "$PWD/trace2_wt_secondary" &&
+
+ git -C wt-secondary fsmonitor--daemon stop &&
+ test_must_fail git -C wt-secondary fsmonitor--daemon status
+'
+
+# NEEDSWORK: Repeat one of the "edit" tests on wt-secondary and
+# confirm that we get the same events and behavior -- that is, that
+# fsmonitor--daemon correctly watches BOTH the working directory and
+# the external GITDIR directory and behaves the same as when ".git"
+# is a directory inside the working directory.
+
+test_expect_success 'cleanup worktrees' '
+ stop_daemon_delete_repo wt-secondary &&
+ stop_daemon_delete_repo wt-base
+'
+
+# The next few tests perform arbitrary/contrived file operations and
+# confirm that status is correct. That is, that the data (or lack of
+# data) from fsmonitor doesn't cause incorrect results. And doesn't
+# cause incorrect results when the untracked-cache is enabled.
+
+test_lazy_prereq UNTRACKED_CACHE '
+ git update-index --test-untracked-cache
+'
+
+test_expect_success 'Matrix: setup for untracked-cache,fsmonitor matrix' '
+ test_unconfig core.fsmonitor &&
+ git update-index --no-fsmonitor &&
+ test_might_fail git fsmonitor--daemon stop
+'
+
+matrix_clean_up_repo () {
+ git reset --hard HEAD &&
+ git clean -fd
+}
+
+matrix_try () {
+ uc=$1 &&
+ fsm=$2 &&
+ fn=$3 &&
+
+ if test $uc = true && test $fsm = false
+ then
+ # The untracked-cache is buggy when FSMonitor is
+ # DISABLED, so skip the tests for this matrix
+ # combination.
+ #
+ # We've observed random, occasional test failures on
+ # Windows and MacOS when the UC is turned on and FSM
+ # is turned off. These are rare, but they do happen
+ # indicating that it is probably a race condition within
+ # the untracked cache itself.
+ #
+ # It usually happens when a test does F/D trickery and
+ # then the NEXT test fails because of extra status
+ # output from stale UC data from the previous test.
+ #
+ # Since FSMonitor is not involved in the error, skip
+ # the tests for this matrix combination.
+ #
+ return 0
+ fi &&
+
+ test_expect_success "Matrix[uc:$uc][fsm:$fsm] $fn" '
+ matrix_clean_up_repo &&
+ $fn &&
+ if test $uc = false && test $fsm = false
+ then
+ git status --porcelain=v1 >.git/expect.$fn
+ else
+ git status --porcelain=v1 >.git/actual.$fn &&
+ test_cmp .git/expect.$fn .git/actual.$fn
+ fi
+ '
+}
+
+uc_values="false"
+test_have_prereq UNTRACKED_CACHE && uc_values="false true"
+for uc_val in $uc_values
+do
+ if test $uc_val = false
+ then
+ test_expect_success "Matrix[uc:$uc_val] disable untracked cache" '
+ git config core.untrackedcache false &&
+ git update-index --no-untracked-cache
+ '
+ else
+ test_expect_success "Matrix[uc:$uc_val] enable untracked cache" '
+ git config core.untrackedcache true &&
+ git update-index --untracked-cache
+ '
+ fi
+
+ fsm_values="false true"
+ for fsm_val in $fsm_values
+ do
+ if test $fsm_val = false
+ then
+ test_expect_success "Matrix[uc:$uc_val][fsm:$fsm_val] disable fsmonitor" '
+ test_unconfig core.fsmonitor &&
+ git update-index --no-fsmonitor &&
+ test_might_fail git fsmonitor--daemon stop
+ '
+ else
+ test_expect_success "Matrix[uc:$uc_val][fsm:$fsm_val] enable fsmonitor" '
+ git config core.fsmonitor true &&
+ git fsmonitor--daemon start &&
+ git update-index --fsmonitor
+ '
+ fi
+
+ matrix_try $uc_val $fsm_val edit_files
+ matrix_try $uc_val $fsm_val delete_files
+ matrix_try $uc_val $fsm_val create_files
+ matrix_try $uc_val $fsm_val rename_files
+ matrix_try $uc_val $fsm_val file_to_directory
+ matrix_try $uc_val $fsm_val directory_to_file
+
+ matrix_try $uc_val $fsm_val move_directory_contents_deeper
+ matrix_try $uc_val $fsm_val move_directory_up
+ matrix_try $uc_val $fsm_val move_directory
+
+ if test $fsm_val = true
+ then
+ test_expect_success "Matrix[uc:$uc_val][fsm:$fsm_val] disable fsmonitor at end" '
+ test_unconfig core.fsmonitor &&
+ git update-index --no-fsmonitor &&
+ test_might_fail git fsmonitor--daemon stop
+ '
+ fi
+ done
+done
+
+# Test Unicode UTF-8 characters in the pathname of the working
+# directory root. Use of "*A()" routines rather than "*W()" routines
+# on Windows can sometimes lead to odd failures.
+#
+u1=$(printf "u_c3_a6__\xC3\xA6")
+u2=$(printf "u_e2_99_ab__\xE2\x99\xAB")
+u_values="$u1 $u2"
+for u in $u_values
+do
+ test_expect_success "unicode in repo root path: $u" '
+ test_when_finished "stop_daemon_delete_repo $u" &&
+
+ git init "$u" &&
+ echo 1 >"$u"/file1 &&
+ git -C "$u" add file1 &&
+ git -C "$u" config core.fsmonitor true &&
+
+ start_daemon -C "$u" &&
+ git -C "$u" status >actual &&
+ grep "new file: file1" actual
+ '
+done
+
+# Test fsmonitor interaction with submodules.
+#
+# If we start the daemon in the super, it will see FS events for
+# everything in the working directory cone and this includes any
+# files/directories contained *within* the submodules.
+#
+# A `git status` at top level will get events for items within the
+# submodule and ignore them, since they aren't named in the index
+# of the super repo. This makes the fsmonitor response a little
+# noisy, but it doesn't alter the correctness of the state of the
+# super-proper.
+#
+# When we have submodules, `git status` normally does a recursive
+# status on each of the submodules and adds a summary row for any
+# dirty submodules. (See the "S..." bits in porcelain V2 output.)
+#
+# It is therefore important that the top level status not be tricked
+# by the FSMonitor response to skip those recursive calls. That is,
+# even if FSMonitor says that the mtime of the submodule directory
+# hasn't changed and it could be implicitly marked valid, we must
+# not take that shortcut. We need to force the recusion into the
+# submodule so that we get a summary of the status *within* the
+# submodule.
+
+create_super () {
+ super="$1" &&
+
+ git init "$super" &&
+ echo x >"$super/file_1" &&
+ echo y >"$super/file_2" &&
+ echo z >"$super/file_3" &&
+ mkdir "$super/dir_1" &&
+ echo a >"$super/dir_1/file_11" &&
+ echo b >"$super/dir_1/file_12" &&
+ mkdir "$super/dir_1/dir_2" &&
+ echo a >"$super/dir_1/dir_2/file_21" &&
+ echo b >"$super/dir_1/dir_2/file_22" &&
+ git -C "$super" add . &&
+ git -C "$super" commit -m "initial $super commit"
+}
+
+create_sub () {
+ sub="$1" &&
+
+ git init "$sub" &&
+ echo x >"$sub/file_x" &&
+ echo y >"$sub/file_y" &&
+ echo z >"$sub/file_z" &&
+ mkdir "$sub/dir_x" &&
+ echo a >"$sub/dir_x/file_a" &&
+ echo b >"$sub/dir_x/file_b" &&
+ mkdir "$sub/dir_x/dir_y" &&
+ echo a >"$sub/dir_x/dir_y/file_a" &&
+ echo b >"$sub/dir_x/dir_y/file_b" &&
+ git -C "$sub" add . &&
+ git -C "$sub" commit -m "initial $sub commit"
+}
+
+my_match_and_clean () {
+ git -C super --no-optional-locks status --porcelain=v2 >actual.with &&
+ git -C super --no-optional-locks -c core.fsmonitor=false \
+ status --porcelain=v2 >actual.without &&
+ test_cmp actual.with actual.without &&
+
+ git -C super --no-optional-locks diff-index --name-status HEAD >actual.with &&
+ git -C super --no-optional-locks -c core.fsmonitor=false \
+ diff-index --name-status HEAD >actual.without &&
+ test_cmp actual.with actual.without &&
+
+ git -C super/dir_1/dir_2/sub reset --hard &&
+ git -C super/dir_1/dir_2/sub clean -d -f
+}
+
+test_expect_success 'submodule setup' '
+ git config --global protocol.file.allow always
+'
+
+test_expect_success 'submodule always visited' '
+ test_when_finished "git -C super fsmonitor--daemon stop; \
+ rm -rf super; \
+ rm -rf sub" &&
+
+ create_super super &&
+ create_sub sub &&
+
+ git -C super submodule add ../sub ./dir_1/dir_2/sub &&
+ git -C super commit -m "add sub" &&
+
+ start_daemon -C super &&
+ git -C super config core.fsmonitor true &&
+ git -C super update-index --fsmonitor &&
+ git -C super status &&
+
+ # Now run pairs of commands w/ and w/o FSMonitor while we make
+ # some dirt in the submodule and confirm matching output.
+
+ # Completely clean status.
+ my_match_and_clean &&
+
+ # .M S..U
+ echo z >super/dir_1/dir_2/sub/dir_x/dir_y/foobar_u &&
+ my_match_and_clean &&
+
+ # .M S.M.
+ echo z >super/dir_1/dir_2/sub/dir_x/dir_y/foobar_m &&
+ git -C super/dir_1/dir_2/sub add . &&
+ my_match_and_clean &&
+
+ # .M S.M.
+ echo z >>super/dir_1/dir_2/sub/dir_x/dir_y/file_a &&
+ git -C super/dir_1/dir_2/sub add . &&
+ my_match_and_clean &&
+
+ # .M SC..
+ echo z >>super/dir_1/dir_2/sub/dir_x/dir_y/file_a &&
+ git -C super/dir_1/dir_2/sub add . &&
+ git -C super/dir_1/dir_2/sub commit -m "SC.." &&
+ my_match_and_clean
+'
+
+# If a submodule has a `sub/.git/` directory (rather than a file
+# pointing to the super's `.git/modules/sub`) and `core.fsmonitor`
+# turned on in the submodule and the daemon is not yet started in
+# the submodule, and someone does a `git submodule absorbgitdirs`
+# in the super, Git will recursively invoke `git submodule--helper`
+# to do the work and this may try to read the index. This will
+# try to start the daemon in the submodule.
+
+test_expect_success "submodule absorbgitdirs implicitly starts daemon" '
+ test_when_finished "rm -rf super; \
+ rm -rf sub; \
+ rm super-sub.trace" &&
+
+ create_super super &&
+ create_sub sub &&
+
+ # Copy rather than submodule add so that we get a .git dir.
+ cp -R ./sub ./super/dir_1/dir_2/sub &&
+
+ git -C super/dir_1/dir_2/sub config core.fsmonitor true &&
+
+ git -C super submodule add ../sub ./dir_1/dir_2/sub &&
+ git -C super commit -m "add sub" &&
+
+ test_path_is_dir super/dir_1/dir_2/sub/.git &&
+
+ cwd="$(cd super && pwd)" &&
+ cat >expect <<-EOF &&
+ Migrating git directory of '\''dir_1/dir_2/sub'\'' from
+ '\''$cwd/dir_1/dir_2/sub/.git'\'' to
+ '\''$cwd/.git/modules/dir_1/dir_2/sub'\''
+ EOF
+ GIT_TRACE2_EVENT="$PWD/super-sub.trace" \
+ git -C super submodule absorbgitdirs >out 2>actual &&
+ test_cmp expect actual &&
+ test_must_be_empty out &&
+
+ # Confirm that the trace2 log contains a record of the
+ # daemon starting.
+ test_subcommand git fsmonitor--daemon start <super-sub.trace
+'
+
+# On a case-insensitive file system, confirm that the daemon
+# notices when the .git directory is moved/renamed/deleted
+# regardless of how it is spelled in the FS event.
+# That is, does the FS event receive the spelling of the
+# operation or does it receive the spelling preserved with
+# the file/directory.
+#
+test_expect_success CASE_INSENSITIVE_FS 'case insensitive+preserving' '
+ test_when_finished "stop_daemon_delete_repo test_insensitive" &&
+
+ git init test_insensitive &&
+
+ start_daemon -C test_insensitive --tf "$PWD/insensitive.trace" &&
+
+ mkdir -p test_insensitive/abc/def &&
+ echo xyz >test_insensitive/ABC/DEF/xyz &&
+
+ test_path_is_dir test_insensitive/.git &&
+ test_path_is_dir test_insensitive/.GIT &&
+
+ # Rename .git using an alternate spelling to verify that
+ # the daemon detects it and automatically shuts down.
+ mv test_insensitive/.GIT test_insensitive/.FOO &&
+
+ # See [1] above.
+ mv test_insensitive/.FOO test_insensitive/.git &&
+
+ verify_implicit_shutdown test_insensitive &&
+
+ # Verify that events were reported using on-disk spellings of the
+ # directories and files that we touched. We may or may not get a
+ # trailing slash on modified directories.
+ #
+ grep -E "^event: abc/?$" ./insensitive.trace &&
+ grep -E "^event: abc/def/?$" ./insensitive.trace &&
+ grep -E "^event: abc/def/xyz$" ./insensitive.trace
+'
+
+# The variable "unicode_debug" is defined in the following library
+# script to dump information about how the (OS, FS) handles Unicode
+# composition. Uncomment the following line if you want to enable it.
+#
+# unicode_debug=true
+
+. "$TEST_DIRECTORY/lib-unicode-nfc-nfd.sh"
+
+# See if the OS or filesystem does NFC/NFD aliasing/munging.
+#
+# The daemon should err on the side of caution and send BOTH the
+# NFC and NFD forms. It does not know the original spelling of
+# the pathname (how the user thinks it should be spelled), so
+# emit both and let the client decide (when necessary). This is
+# similar to "core.precomposeUnicode".
+#
+test_expect_success !UNICODE_COMPOSITION_SENSITIVE 'Unicode nfc/nfd' '
+ test_when_finished "stop_daemon_delete_repo test_unicode" &&
+
+ git init test_unicode &&
+
+ start_daemon -C test_unicode --tf "$PWD/unicode.trace" &&
+
+ # Create a directory using an NFC spelling.
+ #
+ mkdir test_unicode/nfc &&
+ mkdir test_unicode/nfc/c_${utf8_nfc} &&
+
+ # Create a directory using an NFD spelling.
+ #
+ mkdir test_unicode/nfd &&
+ mkdir test_unicode/nfd/d_${utf8_nfd} &&
+
+ test-tool -C test_unicode fsmonitor-client query --token 0 &&
+
+ if test_have_prereq UNICODE_NFC_PRESERVED
+ then
+ # We should have seen NFC event from OS.
+ # We should not have synthesized an NFD event.
+ grep -E "^event: nfc/c_${utf8_nfc}/?$" ./unicode.trace &&
+ grep -E -v "^event: nfc/c_${utf8_nfd}/?$" ./unicode.trace
+ else
+ # We should have seen NFD event from OS.
+ # We should have synthesized an NFC event.
+ grep -E "^event: nfc/c_${utf8_nfd}/?$" ./unicode.trace &&
+ grep -E "^event: nfc/c_${utf8_nfc}/?$" ./unicode.trace
+ fi &&
+
+ # We assume UNICODE_NFD_PRESERVED.
+ # We should have seen explicit NFD from OS.
+ # We should have synthesized an NFC event.
+ grep -E "^event: nfd/d_${utf8_nfd}/?$" ./unicode.trace &&
+ grep -E "^event: nfd/d_${utf8_nfc}/?$" ./unicode.trace
+'
+
+test_expect_success 'split-index and FSMonitor work well together' '
+ git init split-index &&
+ test_when_finished "git -C \"$PWD/split-index\" \
+ fsmonitor--daemon stop" &&
+ (
+ cd split-index &&
+ git config core.splitIndex true &&
+ # force split-index in most cases
+ git config splitIndex.maxPercentChange 99 &&
+ git config core.fsmonitor true &&
+
+ # Create the following commit topology:
+ #
+ # * merge three
+ # |\
+ # | * three
+ # * | merge two
+ # |\|
+ # | * two
+ # * | one
+ # |/
+ # * 5a5efd7 initial
+
+ test_commit initial &&
+ test_commit two &&
+ test_commit three &&
+ git reset --hard initial &&
+ test_commit one &&
+ test_tick &&
+ git merge two &&
+ test_tick &&
+ git merge three &&
+
+ git rebase --force-rebase -r one
+ )
+'
+
+# The FSMonitor daemon reports the OBSERVED pathname of modified files
+# and thus contains the OBSERVED spelling on case-insensitive file
+# systems. The daemon does not (and should not) load the .git/index
+# file and therefore does not know the expected case-spelling. Since
+# it is possible for the user to create files/subdirectories with the
+# incorrect case, a modified file event for a tracked will not have
+# the EXPECTED case. This can cause `index_name_pos()` to incorrectly
+# report that the file is untracked. This causes the client to fail to
+# mark the file as possibly dirty (keeping the CE_FSMONITOR_VALID bit
+# set) so that `git status` will avoid inspecting it and thus not
+# present in the status output.
+#
+# The setup is a little contrived.
+#
+test_expect_success CASE_INSENSITIVE_FS 'fsmonitor subdir case wrong on disk' '
+ test_when_finished "stop_daemon_delete_repo subdir_case_wrong" &&
+
+ git init subdir_case_wrong &&
+ (
+ cd subdir_case_wrong &&
+ echo x >AAA &&
+ echo x >BBB &&
+
+ mkdir dir1 &&
+ echo x >dir1/file1 &&
+ mkdir dir1/dir2 &&
+ echo x >dir1/dir2/file2 &&
+ mkdir dir1/dir2/dir3 &&
+ echo x >dir1/dir2/dir3/file3 &&
+
+ echo x >yyy &&
+ echo x >zzz &&
+ git add . &&
+ git commit -m "data" &&
+
+ # This will cause "dir1/" and everything under it
+ # to be deleted.
+ git sparse-checkout set --cone --sparse-index &&
+
+ # Create dir2 with the wrong case and then let Git
+ # repopulate dir3 -- it will not correct the spelling
+ # of dir2.
+ mkdir dir1 &&
+ mkdir dir1/DIR2 &&
+ git sparse-checkout add dir1/dir2/dir3
+ ) &&
+
+ start_daemon -C subdir_case_wrong --tf "$PWD/subdir_case_wrong.trace" &&
+
+ # Enable FSMonitor in the client. Run enough commands for
+ # the .git/index to sync up with the daemon with everything
+ # marked clean.
+ git -C subdir_case_wrong config core.fsmonitor true &&
+ git -C subdir_case_wrong update-index --fsmonitor &&
+ git -C subdir_case_wrong status &&
+
+ # Make some files dirty so that FSMonitor gets FSEvents for
+ # each of them.
+ echo xx >>subdir_case_wrong/AAA &&
+ echo xx >>subdir_case_wrong/dir1/DIR2/dir3/file3 &&
+ echo xx >>subdir_case_wrong/zzz &&
+
+ GIT_TRACE_FSMONITOR="$PWD/subdir_case_wrong.log" \
+ git -C subdir_case_wrong --no-optional-locks status --short \
+ >"$PWD/subdir_case_wrong.out" &&
+
+ # "git status" should have gotten file events for each of
+ # the 3 files.
+ #
+ # "dir2" should be in the observed case on disk.
+ grep "fsmonitor_refresh_callback" \
+ <"$PWD/subdir_case_wrong.log" \
+ >"$PWD/subdir_case_wrong.log1" &&
+
+ grep -q "AAA.*pos 0" "$PWD/subdir_case_wrong.log1" &&
+ grep -q "zzz.*pos 6" "$PWD/subdir_case_wrong.log1" &&
+
+ grep -q "dir1/DIR2/dir3/file3.*pos -3" "$PWD/subdir_case_wrong.log1" &&
+
+ # Verify that we get a mapping event to correct the case.
+ grep -q "MAP:.*dir1/DIR2/dir3/file3.*dir1/dir2/dir3/file3" \
+ "$PWD/subdir_case_wrong.log1" &&
+
+ # The refresh-callbacks should have caused "git status" to clear
+ # the CE_FSMONITOR_VALID bit on each of those files and caused
+ # the worktree scan to visit them and mark them as modified.
+ grep -q " M AAA" "$PWD/subdir_case_wrong.out" &&
+ grep -q " M zzz" "$PWD/subdir_case_wrong.out" &&
+ grep -q " M dir1/dir2/dir3/file3" "$PWD/subdir_case_wrong.out"
+'
+
+test_expect_success CASE_INSENSITIVE_FS 'fsmonitor file case wrong on disk' '
+ test_when_finished "stop_daemon_delete_repo file_case_wrong" &&
+
+ git init file_case_wrong &&
+ (
+ cd file_case_wrong &&
+ echo x >AAA &&
+ echo x >BBB &&
+
+ mkdir dir1 &&
+ mkdir dir1/dir2 &&
+ mkdir dir1/dir2/dir3 &&
+ echo x >dir1/dir2/dir3/FILE-3-B &&
+ echo x >dir1/dir2/dir3/XXXX-3-X &&
+ echo x >dir1/dir2/dir3/file-3-a &&
+ echo x >dir1/dir2/dir3/yyyy-3-y &&
+ mkdir dir1/dir2/dir4 &&
+ echo x >dir1/dir2/dir4/FILE-4-A &&
+ echo x >dir1/dir2/dir4/XXXX-4-X &&
+ echo x >dir1/dir2/dir4/file-4-b &&
+ echo x >dir1/dir2/dir4/yyyy-4-y &&
+
+ echo x >yyy &&
+ echo x >zzz &&
+ git add . &&
+ git commit -m "data"
+ ) &&
+
+ start_daemon -C file_case_wrong --tf "$PWD/file_case_wrong.trace" &&
+
+ # Enable FSMonitor in the client. Run enough commands for
+ # the .git/index to sync up with the daemon with everything
+ # marked clean.
+ git -C file_case_wrong config core.fsmonitor true &&
+ git -C file_case_wrong update-index --fsmonitor &&
+ git -C file_case_wrong status &&
+
+ # Make some files dirty so that FSMonitor gets FSEvents for
+ # each of them.
+ echo xx >>file_case_wrong/AAA &&
+ echo xx >>file_case_wrong/zzz &&
+
+ # Rename some files so that FSMonitor sees a create and delete
+ # FSEvent for each. (A simple "mv foo FOO" is not portable
+ # between macOS and Windows. It works on both platforms, but makes
+ # the test messy, since (1) one platform updates "ctime" on the
+ # moved file and one does not and (2) it causes a directory event
+ # on one platform and not on the other which causes additional
+ # scanning during "git status" which causes a "H" vs "h" discrepancy
+ # in "git ls-files -f".) So old-school it and move it out of the
+ # way and copy it to the case-incorrect name so that we get fresh
+ # "ctime" and "mtime" values.
+
+ mv file_case_wrong/dir1/dir2/dir3/file-3-a file_case_wrong/dir1/dir2/dir3/ORIG &&
+ cp file_case_wrong/dir1/dir2/dir3/ORIG file_case_wrong/dir1/dir2/dir3/FILE-3-A &&
+ rm file_case_wrong/dir1/dir2/dir3/ORIG &&
+ mv file_case_wrong/dir1/dir2/dir4/FILE-4-A file_case_wrong/dir1/dir2/dir4/ORIG &&
+ cp file_case_wrong/dir1/dir2/dir4/ORIG file_case_wrong/dir1/dir2/dir4/file-4-a &&
+ rm file_case_wrong/dir1/dir2/dir4/ORIG &&
+
+ # Run status enough times to fully sync.
+ #
+ # The first instance should get the create and delete FSEvents
+ # for each pair. Status should update the index with a new FSM
+ # token (so the next invocation will not see data for these
+ # events).
+
+ GIT_TRACE_FSMONITOR="$PWD/file_case_wrong-try1.log" \
+ git -C file_case_wrong status --short \
+ >"$PWD/file_case_wrong-try1.out" &&
+ grep -q "fsmonitor_refresh_callback.*FILE-3-A.*pos -3" "$PWD/file_case_wrong-try1.log" &&
+ grep -q "fsmonitor_refresh_callback.*file-3-a.*pos 4" "$PWD/file_case_wrong-try1.log" &&
+ grep -q "fsmonitor_refresh_callback.*FILE-4-A.*pos 6" "$PWD/file_case_wrong-try1.log" &&
+ grep -q "fsmonitor_refresh_callback.*file-4-a.*pos -9" "$PWD/file_case_wrong-try1.log" &&
+
+ # FSM refresh will have invalidated the FSM bit and cause a regular
+ # (real) scan of these tracked files, so they should have "H" status.
+ # (We will not see a "h" status until the next refresh (on the next
+ # command).)
+
+ git -C file_case_wrong ls-files -f >"$PWD/file_case_wrong-lsf1.out" &&
+ grep -q "H dir1/dir2/dir3/file-3-a" "$PWD/file_case_wrong-lsf1.out" &&
+ grep -q "H dir1/dir2/dir4/FILE-4-A" "$PWD/file_case_wrong-lsf1.out" &&
+
+
+ # Try the status again. We assume that the above status command
+ # advanced the token so that the next one will not see those events.
+
+ GIT_TRACE_FSMONITOR="$PWD/file_case_wrong-try2.log" \
+ git -C file_case_wrong status --short \
+ >"$PWD/file_case_wrong-try2.out" &&
+ ! grep -q "fsmonitor_refresh_callback.*FILE-3-A.*pos" "$PWD/file_case_wrong-try2.log" &&
+ ! grep -q "fsmonitor_refresh_callback.*file-3-a.*pos" "$PWD/file_case_wrong-try2.log" &&
+ ! grep -q "fsmonitor_refresh_callback.*FILE-4-A.*pos" "$PWD/file_case_wrong-try2.log" &&
+ ! grep -q "fsmonitor_refresh_callback.*file-4-a.*pos" "$PWD/file_case_wrong-try2.log" &&
+
+ # FSM refresh saw nothing, so it will mark all files as valid,
+ # so they should now have "h" status.
+
+ git -C file_case_wrong ls-files -f >"$PWD/file_case_wrong-lsf2.out" &&
+ grep -q "h dir1/dir2/dir3/file-3-a" "$PWD/file_case_wrong-lsf2.out" &&
+ grep -q "h dir1/dir2/dir4/FILE-4-A" "$PWD/file_case_wrong-lsf2.out" &&
+
+
+ # We now have files with clean content, but with case-incorrect
+ # file names. Modify them to see if status properly reports
+ # them.
+
+ echo xx >>file_case_wrong/dir1/dir2/dir3/FILE-3-A &&
+ echo xx >>file_case_wrong/dir1/dir2/dir4/file-4-a &&
+
+ GIT_TRACE_FSMONITOR="$PWD/file_case_wrong-try3.log" \
+ git -C file_case_wrong --no-optional-locks status --short \
+ >"$PWD/file_case_wrong-try3.out" &&
+
+ # Verify that we get a mapping event to correct the case.
+ grep -q "fsmonitor_refresh_callback MAP:.*dir1/dir2/dir3/FILE-3-A.*dir1/dir2/dir3/file-3-a" \
+ "$PWD/file_case_wrong-try3.log" &&
+ grep -q "fsmonitor_refresh_callback MAP:.*dir1/dir2/dir4/file-4-a.*dir1/dir2/dir4/FILE-4-A" \
+ "$PWD/file_case_wrong-try3.log" &&
+
+ # FSEvents are in observed case.
+ grep -q "fsmonitor_refresh_callback.*FILE-3-A.*pos -3" "$PWD/file_case_wrong-try3.log" &&
+ grep -q "fsmonitor_refresh_callback.*file-4-a.*pos -9" "$PWD/file_case_wrong-try3.log" &&
+
+ # The refresh-callbacks should have caused "git status" to clear
+ # the CE_FSMONITOR_VALID bit on each of those files and caused
+ # the worktree scan to visit them and mark them as modified.
+ grep -q " M dir1/dir2/dir3/file-3-a" "$PWD/file_case_wrong-try3.out" &&
+ grep -q " M dir1/dir2/dir4/FILE-4-A" "$PWD/file_case_wrong-try3.out"
+'
+
+test_done
diff --git a/t/t7528-signed-commit-ssh.sh b/t/t7528-signed-commit-ssh.sh
new file mode 100755
index 0000000..065f780
--- /dev/null
+++ b/t/t7528-signed-commit-ssh.sh
@@ -0,0 +1,462 @@
+#!/bin/sh
+
+test_description='ssh signed commit tests'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+GNUPGHOME_NOT_USED=$GNUPGHOME
+. "$TEST_DIRECTORY/lib-gpg.sh"
+
+test_expect_success GPGSSH 'create signed commits' '
+ test_oid_cache <<-\EOF &&
+ header sha1:gpgsig
+ header sha256:gpgsig-sha256
+ EOF
+
+ test_when_finished "test_unconfig commit.gpgsign" &&
+ test_config gpg.format ssh &&
+ test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+
+ echo 1 >file && git add file &&
+ test_tick && git commit -S -m initial &&
+ git tag initial &&
+ git branch side &&
+
+ echo 2 >file && test_tick && git commit -a -S -m second &&
+ git tag second &&
+
+ git checkout side &&
+ echo 3 >elif && git add elif &&
+ test_tick && git commit -m "third on side" &&
+
+ git checkout main &&
+ test_tick && git merge -S side &&
+ git tag merge &&
+
+ echo 4 >file && test_tick && git commit -a -m "fourth unsigned" &&
+ git tag fourth-unsigned &&
+
+ test_tick && git commit --amend -S -m "fourth signed" &&
+ git tag fourth-signed &&
+
+ git config commit.gpgsign true &&
+ echo 5 >file && test_tick && git commit -a -m "fifth signed" &&
+ git tag fifth-signed &&
+
+ git config commit.gpgsign false &&
+ echo 6 >file && test_tick && git commit -a -m "sixth" &&
+ git tag sixth-unsigned &&
+
+ git config commit.gpgsign true &&
+ echo 7 >file && test_tick && git commit -a -m "seventh" --no-gpg-sign &&
+ git tag seventh-unsigned &&
+
+ test_tick && git rebase -f HEAD^^ && git tag sixth-signed HEAD^ &&
+ git tag seventh-signed &&
+
+ echo 8 >file && test_tick && git commit -a -m eighth -S"${GPGSSH_KEY_UNTRUSTED}" &&
+ git tag eighth-signed-alt &&
+
+ # commit.gpgsign is still on but this must not be signed
+ echo 9 | git commit-tree HEAD^{tree} >oid &&
+ test_line_count = 1 oid &&
+ git tag ninth-unsigned $(cat oid) &&
+ # explicit -S of course must sign.
+ echo 10 | git commit-tree -S HEAD^{tree} >oid &&
+ test_line_count = 1 oid &&
+ git tag tenth-signed $(cat oid) &&
+
+ # --gpg-sign[=<key-id>] must sign.
+ echo 11 | git commit-tree --gpg-sign HEAD^{tree} >oid &&
+ test_line_count = 1 oid &&
+ git tag eleventh-signed $(cat oid) &&
+ echo 12 | git commit-tree --gpg-sign="${GPGSSH_KEY_UNTRUSTED}" HEAD^{tree} >oid &&
+ test_line_count = 1 oid &&
+ git tag twelfth-signed-alt $(cat oid) &&
+
+ echo 13>file && test_tick && git commit -a -m thirteenth -S"${GPGSSH_KEY_ECDSA}" &&
+ git tag thirteenth-signed-ecdsa
+'
+
+test_expect_success GPGSSH 'sign commits using literal public keys with ssh-agent' '
+ test_when_finished "test_unconfig commit.gpgsign" &&
+ test_config gpg.format ssh &&
+ eval $(ssh-agent) &&
+ test_when_finished "kill ${SSH_AGENT_PID}" &&
+ ssh-add "${GPGSSH_KEY_PRIMARY}" &&
+ echo 1 >file && git add file &&
+ git commit -a -m rsa-inline -S"$(cat "${GPGSSH_KEY_PRIMARY}.pub")" &&
+ echo 2 >file &&
+ test_config user.signingkey "$(cat "${GPGSSH_KEY_PRIMARY}.pub")" &&
+ git commit -a -m rsa-config -S &&
+ ssh-add "${GPGSSH_KEY_ECDSA}" &&
+ echo 3 >file &&
+ git commit -a -m ecdsa-inline -S"key::$(cat "${GPGSSH_KEY_ECDSA}.pub")" &&
+ echo 4 >file &&
+ test_config user.signingkey "key::$(cat "${GPGSSH_KEY_ECDSA}.pub")" &&
+ git commit -a -m ecdsa-config -S
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'create signed commits with keys having defined lifetimes' '
+ test_when_finished "test_unconfig commit.gpgsign" &&
+ test_config gpg.format ssh &&
+
+ echo expired >file && test_tick && git commit -a -m expired -S"${GPGSSH_KEY_EXPIRED}" &&
+ git tag expired-signed &&
+
+ echo notyetvalid >file && test_tick && git commit -a -m notyetvalid -S"${GPGSSH_KEY_NOTYETVALID}" &&
+ git tag notyetvalid-signed &&
+
+ echo timeboxedvalid >file && test_tick && git commit -a -m timeboxedvalid -S"${GPGSSH_KEY_TIMEBOXEDVALID}" &&
+ git tag timeboxedvalid-signed &&
+
+ echo timeboxedinvalid >file && test_tick && git commit -a -m timeboxedinvalid -S"${GPGSSH_KEY_TIMEBOXEDINVALID}" &&
+ git tag timeboxedinvalid-signed
+'
+
+test_expect_success GPGSSH 'verify and show signatures' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ test_config gpg.mintrustlevel UNDEFINED &&
+ (
+ for commit in initial second merge fourth-signed \
+ fifth-signed sixth-signed seventh-signed tenth-signed \
+ eleventh-signed
+ do
+ git verify-commit $commit &&
+ git show --pretty=short --show-signature $commit >actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ echo $commit OK || exit 1
+ done
+ ) &&
+ (
+ for commit in merge^2 fourth-unsigned sixth-unsigned \
+ seventh-unsigned ninth-unsigned
+ do
+ test_must_fail git verify-commit $commit &&
+ git show --pretty=short --show-signature $commit >actual &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ echo $commit OK || exit 1
+ done
+ ) &&
+ (
+ for commit in eighth-signed-alt twelfth-signed-alt
+ do
+ git show --pretty=short --show-signature $commit >actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ grep "${GPGSSH_KEY_NOT_TRUSTED}" actual &&
+ echo $commit OK || exit 1
+ done
+ )
+'
+
+test_expect_success GPGSSH 'verify-commit exits failure on untrusted signature' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ test_must_fail git verify-commit eighth-signed-alt 2>actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ grep "${GPGSSH_KEY_NOT_TRUSTED}" actual
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-commit exits failure on expired signature key' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ test_must_fail git verify-commit expired-signed 2>actual &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-commit exits failure on not yet valid signature key' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ test_must_fail git verify-commit notyetvalid-signed 2>actual &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-commit succeeds with commit date and key validity matching' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git verify-commit timeboxedvalid-signed 2>actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-commit exits failure with commit date outside of key validity' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ test_must_fail git verify-commit timeboxedinvalid-signed 2>actual &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
+'
+
+test_expect_success GPGSSH 'verify-commit exits success with matching minTrustLevel' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ test_config gpg.minTrustLevel fully &&
+ git verify-commit sixth-signed
+'
+
+test_expect_success GPGSSH 'verify-commit exits success with low minTrustLevel' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ test_config gpg.minTrustLevel marginal &&
+ git verify-commit sixth-signed
+'
+
+test_expect_success GPGSSH 'verify-commit exits failure with high minTrustLevel' '
+ test_config gpg.minTrustLevel ultimate &&
+ test_must_fail git verify-commit eighth-signed-alt
+'
+
+test_expect_success GPGSSH 'verify signatures with --raw' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ (
+ for commit in initial second merge fourth-signed fifth-signed sixth-signed seventh-signed
+ do
+ git verify-commit --raw $commit 2>actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ echo $commit OK || exit 1
+ done
+ ) &&
+ (
+ for commit in merge^2 fourth-unsigned sixth-unsigned seventh-unsigned
+ do
+ test_must_fail git verify-commit --raw $commit 2>actual &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ echo $commit OK || exit 1
+ done
+ ) &&
+ (
+ for commit in eighth-signed-alt
+ do
+ test_must_fail git verify-commit --raw $commit 2>actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ echo $commit OK || exit 1
+ done
+ )
+'
+
+test_expect_success GPGSSH 'proper header is used for hash algorithm' '
+ git cat-file commit fourth-signed >output &&
+ grep "^$(test_oid header) -----BEGIN SSH SIGNATURE-----" output
+'
+
+test_expect_success GPGSSH 'show signed commit with signature' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git show -s initial >commit &&
+ git show -s --show-signature initial >show &&
+ git verify-commit -v initial >verify.1 2>verify.2 &&
+ git cat-file commit initial >cat &&
+ grep -v -e "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" -e "Warning: " show >show.commit &&
+ grep -e "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" -e "Warning: " show >show.gpg &&
+ grep -v "^ " cat | grep -v "^gpgsig.* " >cat.commit &&
+ test_cmp show.commit commit &&
+ test_cmp show.gpg verify.2 &&
+ test_cmp cat.commit verify.1
+'
+
+test_expect_success GPGSSH 'detect fudged signature' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git cat-file commit seventh-signed >raw &&
+ sed -e "s/^seventh/7th forged/" raw >forged1 &&
+ git hash-object -w -t commit forged1 >forged1.commit &&
+ test_must_fail git verify-commit $(cat forged1.commit) &&
+ git show --pretty=short --show-signature $(cat forged1.commit) >actual1 &&
+ grep "${GPGSSH_BAD_SIGNATURE}" actual1 &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual1 &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual1
+'
+
+test_expect_success GPGSSH 'detect fudged signature with NUL' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git cat-file commit seventh-signed >raw &&
+ cat raw >forged2 &&
+ echo Qwik | tr "Q" "\000" >>forged2 &&
+ git hash-object --literally -w -t commit forged2 >forged2.commit &&
+ test_must_fail git verify-commit $(cat forged2.commit) &&
+ git show --pretty=short --show-signature $(cat forged2.commit) >actual2 &&
+ grep "${GPGSSH_BAD_SIGNATURE}" actual2 &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual2
+'
+
+test_expect_success GPGSSH 'amending already signed commit' '
+ test_config gpg.format ssh &&
+ test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git checkout -f fourth-signed^0 &&
+ git commit --amend -S --no-edit &&
+ git verify-commit HEAD &&
+ git show -s --show-signature HEAD >actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual
+'
+
+test_expect_success GPGSSH 'show good signature with custom format' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") &&
+ cat >expect.tmpl <<-\EOF &&
+ G
+ FINGERPRINT
+ principal with number 1
+ FINGERPRINT
+
+ EOF
+ sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect &&
+ git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" sixth-signed >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPGSSH 'show bad signature with custom format' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ cat >expect <<-\EOF &&
+ B
+
+
+
+
+ EOF
+ git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" $(cat forged1.commit) >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPGSSH 'show untrusted signature with custom format' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ cat >expect.tmpl <<-\EOF &&
+ U
+ FINGERPRINT
+
+ FINGERPRINT
+
+ EOF
+ git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
+ FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_UNTRUSTED}" | awk "{print \$2;}") &&
+ sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success GPGSSH 'show untrusted signature with undefined trust level' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ cat >expect.tmpl <<-\EOF &&
+ undefined
+ FINGERPRINT
+
+ FINGERPRINT
+
+ EOF
+ git log -1 --format="%GT%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
+ FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_UNTRUSTED}" | awk "{print \$2;}") &&
+ sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success GPGSSH 'show untrusted signature with ultimate trust level' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ cat >expect.tmpl <<-\EOF &&
+ fully
+ FINGERPRINT
+ principal with number 1
+ FINGERPRINT
+
+ EOF
+ git log -1 --format="%GT%n%GK%n%GS%n%GF%n%GP" sixth-signed >actual &&
+ FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") &&
+ sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success GPGSSH 'show lack of signature with custom format' '
+ cat >expect <<-\EOF &&
+ N
+
+
+
+
+ EOF
+ git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" seventh-unsigned >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPGSSH 'log.showsignature behaves like --show-signature' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ test_config log.showsignature true &&
+ git show initial >actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
+'
+
+test_expect_success GPGSSH 'check config gpg.format values' '
+ test_config gpg.format ssh &&
+ test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+ test_config gpg.format ssh &&
+ git commit -S --amend -m "success" &&
+ test_config gpg.format OpEnPgP &&
+ test_must_fail git commit -S --amend -m "fail"
+'
+
+test_expect_failure GPGSSH 'detect fudged commit with double signature (TODO)' '
+ sed -e "/gpgsig/,/END PGP/d" forged1 >double-base &&
+ sed -n -e "/gpgsig/,/END PGP/p" forged1 | \
+ sed -e "s/^$(test_oid header)//;s/^ //" | gpg --dearmor >double-sig1.sig &&
+ gpg -o double-sig2.sig -u 29472784 --detach-sign double-base &&
+ cat double-sig1.sig double-sig2.sig | gpg --enarmor >double-combined.asc &&
+ sed -e "s/^\(-.*\)ARMORED FILE/\1SIGNATURE/;1s/^/$(test_oid header) /;2,\$s/^/ /" \
+ double-combined.asc > double-gpgsig &&
+ sed -e "/committer/r double-gpgsig" double-base >double-commit &&
+ git hash-object -w -t commit double-commit >double-commit.commit &&
+ test_must_fail git verify-commit $(cat double-commit.commit) &&
+ git show --pretty=short --show-signature $(cat double-commit.commit) >double-actual &&
+ grep "BAD signature from" double-actual &&
+ grep "Good signature from" double-actual
+'
+
+test_expect_failure GPGSSH 'show double signature with custom format (TODO)' '
+ cat >expect <<-\EOF &&
+ E
+
+
+
+
+ EOF
+ git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" $(cat double-commit.commit) >actual &&
+ test_cmp expect actual
+'
+
+
+test_expect_failure GPGSSH 'verify-commit verifies multiply signed commits (TODO)' '
+ git init multiply-signed &&
+ cd multiply-signed &&
+ test_commit first &&
+ echo 1 >second &&
+ git add second &&
+ tree=$(git write-tree) &&
+ parent=$(git rev-parse HEAD^{commit}) &&
+ git commit --gpg-sign -m second &&
+ git cat-file commit HEAD &&
+ # Avoid trailing whitespace.
+ sed -e "s/^Q//" -e "s/^Z/ /" >commit <<-EOF &&
+ Qtree $tree
+ Qparent $parent
+ Qauthor A U Thor <author@example.com> 1112912653 -0700
+ Qcommitter C O Mitter <committer@example.com> 1112912653 -0700
+ Qgpgsig -----BEGIN PGP SIGNATURE-----
+ QZ
+ Q iHQEABECADQWIQRz11h0S+chaY7FTocTtvUezd5DDQUCX/uBDRYcY29tbWl0dGVy
+ Q QGV4YW1wbGUuY29tAAoJEBO29R7N3kMNd+8AoK1I8mhLHviPH+q2I5fIVgPsEtYC
+ Q AKCTqBh+VabJceXcGIZuF0Ry+udbBQ==
+ Q =tQ0N
+ Q -----END PGP SIGNATURE-----
+ Qgpgsig-sha256 -----BEGIN PGP SIGNATURE-----
+ QZ
+ Q iHQEABECADQWIQRz11h0S+chaY7FTocTtvUezd5DDQUCX/uBIBYcY29tbWl0dGVy
+ Q QGV4YW1wbGUuY29tAAoJEBO29R7N3kMN/NEAn0XO9RYSBj2dFyozi0JKSbssYMtO
+ Q AJwKCQ1BQOtuwz//IjU8TiS+6S4iUw==
+ Q =pIwP
+ Q -----END PGP SIGNATURE-----
+ Q
+ Qsecond
+ EOF
+ head=$(git hash-object -t commit -w commit) &&
+ git reset --hard $head &&
+ git verify-commit $head 2>actual &&
+ grep "Good signature from" actual &&
+ ! grep "BAD signature from" actual
+'
+
+test_done
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
index 2ef39d3..e5ff073 100755
--- a/t/t7600-merge.sh
+++ b/t/t7600-merge.sh
@@ -105,7 +105,7 @@ verify_mergeheads () {
test_write_lines "$@" >mergehead.expected &&
while read sha1 rest
do
- git rev-parse $sha1
+ git rev-parse $sha1 || return 1
done <.git/MERGE_HEAD >mergehead.actual &&
test_cmp mergehead.expected mergehead.actual
}
@@ -175,7 +175,7 @@ test_expect_success 'merge -h with invalid index' '
>.git/index &&
test_expect_code 129 git merge -h 2>usage
) &&
- test_i18ngrep "[Uu]sage: git merge" broken/usage
+ test_grep "[Uu]sage: git merge" broken/usage
'
test_expect_success 'reject non-strategy with a git-merge-foo name' '
@@ -255,6 +255,15 @@ test_expect_success 'merge --squash c3 with c7' '
test_cmp expect actual
'
+test_expect_success 'merge --squash --autostash conflict does not attempt to apply autostash' '
+ git reset --hard c3 &&
+ >unrelated &&
+ git add unrelated &&
+ test_must_fail git merge --squash c7 --autostash >out 2>err &&
+ ! grep "Applying autostash resulted in conflicts." err &&
+ grep "When finished, apply stashed changes with \`git stash pop\`" out
+'
+
test_expect_success 'merge c3 with c7 with commit.cleanup = scissors' '
git config commit.cleanup scissors &&
git reset --hard c3 &&
@@ -630,41 +639,41 @@ test_expect_success 'merge log message' '
test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c1 with c0, c2, c0, and c1' '
- git reset --hard c1 &&
- test_tick &&
- git merge c0 c2 c0 c1 &&
- verify_merge file result.1-5 &&
- verify_parents $c1 $c2
+ git reset --hard c1 &&
+ test_tick &&
+ git merge c0 c2 c0 c1 &&
+ verify_merge file result.1-5 &&
+ verify_parents $c1 $c2
'
test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c1 with c0, c2, c0, and c1' '
- git reset --hard c1 &&
- test_tick &&
- git merge c0 c2 c0 c1 &&
- verify_merge file result.1-5 &&
- verify_parents $c1 $c2
+ git reset --hard c1 &&
+ test_tick &&
+ git merge c0 c2 c0 c1 &&
+ verify_merge file result.1-5 &&
+ verify_parents $c1 $c2
'
test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c1 with c1 and c2' '
- git reset --hard c1 &&
- test_tick &&
- git merge c1 c2 &&
- verify_merge file result.1-5 &&
- verify_parents $c1 $c2
+ git reset --hard c1 &&
+ test_tick &&
+ git merge c1 c2 &&
+ verify_merge file result.1-5 &&
+ verify_parents $c1 $c2
'
test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge fast-forward in a dirty tree' '
- git reset --hard c0 &&
- mv file file1 &&
- cat file1 >file &&
- rm -f file1 &&
- git merge c2
+ git reset --hard c0 &&
+ mv file file1 &&
+ cat file1 >file &&
+ rm -f file1 &&
+ git merge c2
'
test_debug 'git log --graph --decorate --oneline --all'
@@ -672,7 +681,7 @@ test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'in-index merge' '
git reset --hard c0 &&
git merge --no-ff -s resolve c1 >out &&
- test_i18ngrep "Wonderful." out &&
+ test_grep "Wonderful." out &&
verify_parents $c0 $c1
'
@@ -688,7 +697,7 @@ test_expect_success 'merge with --autostash' '
git reset --hard c1 &&
git merge-file file file.orig file.9 &&
git merge --autostash c2 2>err &&
- test_i18ngrep "Applied autostash." err &&
+ test_grep "Applied autostash." err &&
git show HEAD:file >merge-result &&
test_cmp result.1-5 merge-result &&
test_cmp result.1-5-9 file
@@ -699,7 +708,7 @@ test_expect_success 'merge with merge.autoStash' '
git reset --hard c1 &&
git merge-file file file.orig file.9 &&
git merge c2 2>err &&
- test_i18ngrep "Applied autostash." err &&
+ test_grep "Applied autostash." err &&
git show HEAD:file >merge-result &&
test_cmp result.1-5 merge-result &&
test_cmp result.1-5-9 file
@@ -709,7 +718,7 @@ test_expect_success 'fast-forward merge with --autostash' '
git reset --hard c0 &&
git merge-file file file.orig file.5 &&
git merge --autostash c1 2>err &&
- test_i18ngrep "Applied autostash." err &&
+ test_grep "Applied autostash." err &&
test_cmp result.1-5 file
'
@@ -717,8 +726,9 @@ test_expect_success 'failed fast-forward merge with --autostash' '
git reset --hard c0 &&
git merge-file file file.orig file.5 &&
cp file.5 other &&
+ test_when_finished "rm other" &&
test_must_fail git merge --autostash c1 2>err &&
- test_i18ngrep "Applied autostash." err &&
+ test_grep "Applied autostash." err &&
test_cmp file.5 file
'
@@ -726,7 +736,7 @@ test_expect_success 'octopus merge with --autostash' '
git reset --hard c1 &&
git merge-file file file.orig file.3 &&
git merge --autostash c2 c3 2>err &&
- test_i18ngrep "Applied autostash." err &&
+ test_grep "Applied autostash." err &&
git show HEAD:file >merge-result &&
test_cmp result.1-5-9 merge-result &&
test_cmp result.1-3-5-9 file
@@ -736,7 +746,7 @@ test_expect_success 'failed merge (exit 2) with --autostash' '
git reset --hard c1 &&
git merge-file file file.orig file.5 &&
test_must_fail git merge -s recursive --autostash c2 c3 2>err &&
- test_i18ngrep "Applied autostash." err &&
+ test_grep "Applied autostash." err &&
test_cmp result.1-5 file
'
@@ -745,7 +755,7 @@ test_expect_success 'conflicted merge with --autostash, --abort restores stash'
cp file.1 file &&
test_must_fail git merge --autostash c7 &&
git merge --abort 2>err &&
- test_i18ngrep "Applied autostash." err &&
+ test_grep "Applied autostash." err &&
test_cmp file.1 file
'
@@ -757,7 +767,7 @@ test_expect_success 'completed merge (git commit) with --no-commit and --autosta
git stash show -p MERGE_AUTOSTASH >actual &&
test_cmp expect actual &&
git commit 2>err &&
- test_i18ngrep "Applied autostash." err &&
+ test_grep "Applied autostash." err &&
git show HEAD:file >merge-result &&
test_cmp result.1-5 merge-result &&
test_cmp result.1-5-9 file
@@ -771,7 +781,7 @@ test_expect_success 'completed merge (git merge --continue) with --no-commit and
git stash show -p MERGE_AUTOSTASH >actual &&
test_cmp expect actual &&
git merge --continue 2>err &&
- test_i18ngrep "Applied autostash." err &&
+ test_grep "Applied autostash." err &&
git show HEAD:file >merge-result &&
test_cmp result.1-5 merge-result &&
test_cmp result.1-5-9 file
@@ -785,7 +795,7 @@ test_expect_success 'aborted merge (merge --abort) with --no-commit and --autost
git stash show -p MERGE_AUTOSTASH >actual &&
test_cmp expect actual &&
git merge --abort 2>err &&
- test_i18ngrep "Applied autostash." err &&
+ test_grep "Applied autostash." err &&
git diff >actual &&
test_cmp expect actual
'
@@ -798,7 +808,7 @@ test_expect_success 'aborted merge (reset --hard) with --no-commit and --autosta
git stash show -p MERGE_AUTOSTASH >actual &&
test_cmp expect actual &&
git reset --hard 2>err &&
- test_i18ngrep "Autostash exists; creating a new stash entry." err &&
+ test_grep "Autostash exists; creating a new stash entry." err &&
git diff --exit-code
'
@@ -811,7 +821,7 @@ test_expect_success 'quit merge with --no-commit and --autostash' '
test_cmp expect actual &&
git diff HEAD >expect &&
git merge --quit 2>err &&
- test_i18ngrep "Autostash exists; creating a new stash entry." err &&
+ test_grep "Autostash exists; creating a new stash entry." err &&
git diff HEAD >actual &&
test_cmp expect actual
'
@@ -822,7 +832,7 @@ test_expect_success 'merge with conflicted --autostash changes' '
git diff >expect &&
test_when_finished "test_might_fail git stash drop" &&
git merge --autostash c3 2>err &&
- test_i18ngrep "Applying autostash resulted in conflicts." err &&
+ test_grep "Applying autostash resulted in conflicts." err &&
git show HEAD:file >merge-result &&
test_cmp result.1-9 merge-result &&
git stash show -p >actual &&
@@ -966,7 +976,7 @@ test_expect_success 'set up mod-256 conflict scenario' '
# 256 near-identical stanzas...
for i in $(test_seq 1 256); do
for j in 1 2 3 4 5; do
- echo $i-$j
+ echo $i-$j || return 1
done
done >file &&
git add file &&
diff --git a/t/t7601-merge-pull-config.sh b/t/t7601-merge-pull-config.sh
index 1f652f4..a94387a 100755
--- a/t/t7601-merge-pull-config.sh
+++ b/t/t7601-merge-pull-config.sh
@@ -2,7 +2,7 @@
test_description='git merge
-Testing pull.* configuration parsing.'
+Testing pull.* configuration parsing and other things.'
. ./test-lib.sh
@@ -30,117 +30,117 @@ test_expect_success 'setup' '
test_expect_success 'pull.rebase not set, ff possible' '
git reset --hard c0 &&
git pull . c1 2>err &&
- test_i18ngrep ! "You have divergent branches" err
+ test_grep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and pull.ff=true' '
git reset --hard c0 &&
test_config pull.ff true &&
git pull . c1 2>err &&
- test_i18ngrep ! "You have divergent branches" err
+ test_grep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and pull.ff=false' '
git reset --hard c0 &&
test_config pull.ff false &&
git pull . c1 2>err &&
- test_i18ngrep ! "You have divergent branches" err
+ test_grep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and pull.ff=only' '
git reset --hard c0 &&
test_config pull.ff only &&
git pull . c1 2>err &&
- test_i18ngrep ! "You have divergent branches" err
+ test_grep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and --rebase given' '
git reset --hard c0 &&
git pull --rebase . c1 2>err &&
- test_i18ngrep ! "You have divergent branches" err
+ test_grep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and --no-rebase given' '
git reset --hard c0 &&
git pull --no-rebase . c1 2>err &&
- test_i18ngrep ! "You have divergent branches" err
+ test_grep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and --ff given' '
git reset --hard c0 &&
git pull --ff . c1 2>err &&
- test_i18ngrep ! "You have divergent branches" err
+ test_grep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and --no-ff given' '
git reset --hard c0 &&
git pull --no-ff . c1 2>err &&
- test_i18ngrep ! "You have divergent branches" err
+ test_grep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and --ff-only given' '
git reset --hard c0 &&
git pull --ff-only . c1 2>err &&
- test_i18ngrep ! "You have divergent branches" err
+ test_grep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set (not-fast-forward)' '
git reset --hard c2 &&
test_must_fail git -c color.advice=always pull . c1 2>err &&
test_decode_color <err >decoded &&
- test_i18ngrep "<YELLOW>hint: " decoded &&
- test_i18ngrep "You have divergent branches" decoded
+ test_grep "<YELLOW>hint: " decoded &&
+ test_grep "You have divergent branches" decoded
'
test_expect_success 'pull.rebase not set and pull.ff=true (not-fast-forward)' '
git reset --hard c2 &&
test_config pull.ff true &&
git pull . c1 2>err &&
- test_i18ngrep ! "You have divergent branches" err
+ test_grep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and pull.ff=false (not-fast-forward)' '
git reset --hard c2 &&
test_config pull.ff false &&
git pull . c1 2>err &&
- test_i18ngrep ! "You have divergent branches" err
+ test_grep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and pull.ff=only (not-fast-forward)' '
git reset --hard c2 &&
test_config pull.ff only &&
test_must_fail git pull . c1 2>err &&
- test_i18ngrep ! "You have divergent branches" err
+ test_grep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and --rebase given (not-fast-forward)' '
git reset --hard c2 &&
git pull --rebase . c1 2>err &&
- test_i18ngrep ! "You have divergent branches" err
+ test_grep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and --no-rebase given (not-fast-forward)' '
git reset --hard c2 &&
git pull --no-rebase . c1 2>err &&
- test_i18ngrep ! "You have divergent branches" err
+ test_grep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and --ff given (not-fast-forward)' '
git reset --hard c2 &&
git pull --ff . c1 2>err &&
- test_i18ngrep ! "You have divergent branches" err
+ test_grep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and --no-ff given (not-fast-forward)' '
git reset --hard c2 &&
git pull --no-ff . c1 2>err &&
- test_i18ngrep ! "You have divergent branches" err
+ test_grep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and --ff-only given (not-fast-forward)' '
git reset --hard c2 &&
test_must_fail git pull --ff-only . c1 2>err &&
- test_i18ngrep ! "You have divergent branches" err
+ test_grep ! "You have divergent branches" err
'
test_does_rebase () {
@@ -202,7 +202,7 @@ test_falls_back_to_full_merge () {
test_attempts_fast_forward () {
git reset --hard c2 &&
test_must_fail git "$@" . c1 2>err &&
- test_i18ngrep "Not possible to fast-forward, aborting" err
+ test_grep "Not possible to fast-forward, aborting" err
}
#
@@ -328,34 +328,34 @@ test_expect_success 'pull.rebase=false and --ff, ff not possible' '
test_expect_success 'Multiple heads warns about inability to fast forward' '
git reset --hard c1 &&
test_must_fail git pull . c2 c3 2>err &&
- test_i18ngrep "You have divergent branches" err
+ test_grep "You have divergent branches" err
'
test_expect_success 'Multiple can never be fast forwarded' '
git reset --hard c0 &&
test_must_fail git -c pull.ff=only pull . c1 c2 c3 2>err &&
- test_i18ngrep ! "You have divergent branches" err &&
+ test_grep ! "You have divergent branches" err &&
# In addition to calling out "cannot fast-forward", we very much
# want the "multiple branches" piece to be called out to users.
- test_i18ngrep "Cannot fast-forward to multiple branches" err
+ test_grep "Cannot fast-forward to multiple branches" err
'
test_expect_success 'Cannot rebase with multiple heads' '
git reset --hard c0 &&
test_must_fail git -c pull.rebase=true pull . c1 c2 c3 2>err &&
- test_i18ngrep ! "You have divergent branches" err &&
- test_i18ngrep "Cannot rebase onto multiple branches." err
+ test_grep ! "You have divergent branches" err &&
+ test_grep "Cannot rebase onto multiple branches." err
'
test_expect_success 'merge c1 with c2' '
git reset --hard c1 &&
- test -f c0.c &&
- test -f c1.c &&
- test ! -f c2.c &&
- test ! -f c3.c &&
+ test_path_is_file c0.c &&
+ test_path_is_file c1.c &&
+ test_path_is_missing c2.c &&
+ test_path_is_missing c3.c &&
git merge c2 &&
- test -f c1.c &&
- test -f c2.c
+ test_path_is_file c1.c &&
+ test_path_is_file c2.c
'
test_expect_success 'fast-forward pull succeeds with "true" in pull.ff' '
@@ -387,12 +387,32 @@ test_expect_success 'pull prevents non-fast-forward with "only" in pull.ff' '
test_must_fail git pull . c3
'
+test_expect_success 'already-up-to-date pull succeeds with unspecified pull.ff' '
+ git reset --hard c1 &&
+ git pull . c0 &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse c1)"
+'
+
+test_expect_success 'already-up-to-date pull succeeds with "only" in pull.ff' '
+ git reset --hard c1 &&
+ test_config pull.ff only &&
+ git pull . c0 &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse c1)"
+'
+
+test_expect_success 'already-up-to-date pull/rebase succeeds with "only" in pull.ff' '
+ git reset --hard c1 &&
+ test_config pull.ff only &&
+ git -c pull.rebase=true pull . c0 &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse c1)"
+'
+
test_expect_success 'merge c1 with c2 (ours in pull.twohead)' '
git reset --hard c1 &&
git config pull.twohead ours &&
git merge c2 &&
- test -f c1.c &&
- ! test -f c2.c
+ test_path_is_file c1.c &&
+ test_path_is_missing c2.c
'
test_expect_success 'merge c1 with c2 and c3 (recursive in pull.octopus)' '
@@ -411,10 +431,10 @@ test_expect_success 'merge c1 with c2 and c3 (recursive and octopus in pull.octo
test "$(git rev-parse c2)" = "$(git rev-parse HEAD^2)" &&
test "$(git rev-parse c3)" = "$(git rev-parse HEAD^3)" &&
git diff --exit-code &&
- test -f c0.c &&
- test -f c1.c &&
- test -f c2.c &&
- test -f c3.c
+ test_path_is_file c0.c &&
+ test_path_is_file c1.c &&
+ test_path_is_file c2.c &&
+ test_path_is_file c3.c
'
conflict_count()
diff --git a/t/t7602-merge-octopus-many.sh b/t/t7602-merge-octopus-many.sh
index a9c816b..3669d33 100755
--- a/t/t7602-merge-octopus-many.sh
+++ b/t/t7602-merge-octopus-many.sh
@@ -4,6 +4,7 @@ test_description='git merge
Testing octopus merge with more than 25 refs.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -29,8 +30,8 @@ test_expect_success 'merge c1 with c2, c3, c4, ... c29' '
refs="" &&
while test $i -le 30
do
- refs="$refs c$i"
- i=$(expr $i + 1)
+ refs="$refs c$i" &&
+ i=$(expr $i + 1) || return 1
done &&
git merge $refs &&
test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
diff --git a/t/t7603-merge-reduce-heads.sh b/t/t7603-merge-reduce-heads.sh
index 27cd94a..0e85b21 100755
--- a/t/t7603-merge-reduce-heads.sh
+++ b/t/t7603-merge-reduce-heads.sh
@@ -4,6 +4,7 @@ test_description='git merge
Testing octopus merge when reducing parents to independent branches.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# 0 - 1
@@ -95,7 +96,7 @@ test_expect_success 'setup' '
echo $i > $i.c &&
git add $i.c &&
git commit -m $i &&
- git tag $i
+ git tag $i || return 1
done &&
git reset --hard A &&
for i in F G H I
@@ -103,7 +104,7 @@ test_expect_success 'setup' '
echo $i > $i.c &&
git add $i.c &&
git commit -m $i &&
- git tag $i
+ git tag $i || return 1
done
'
diff --git a/t/t7604-merge-custom-message.sh b/t/t7604-merge-custom-message.sh
index cd4f960..eca7555 100755
--- a/t/t7604-merge-custom-message.sh
+++ b/t/t7604-merge-custom-message.sh
@@ -4,6 +4,7 @@ test_description='git merge
Testing merge when using a custom message for the merge commit.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
create_merge_msgs() {
diff --git a/t/t7605-merge-resolve.sh b/t/t7605-merge-resolve.sh
index 5d56c38..62d935d 100755
--- a/t/t7605-merge-resolve.sh
+++ b/t/t7605-merge-resolve.sh
@@ -4,6 +4,7 @@ test_description='git merge
Testing the resolve strategy.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t7607-merge-state.sh b/t/t7607-merge-state.sh
new file mode 100755
index 0000000..9001674
--- /dev/null
+++ b/t/t7607-merge-state.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+test_description="Test that merge state is as expected after failed merge"
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'Ensure we restore original state if no merge strategy handles it' '
+ test_commit --no-tag "Initial" base base &&
+
+ for b in branch1 branch2 branch3
+ do
+ git checkout -b $b main &&
+ test_commit --no-tag "Change on $b" base $b || return 1
+ done &&
+
+ git checkout branch1 &&
+ # This is a merge that octopus cannot handle. Note, that it does not
+ # just hit conflicts, it completely fails and says that it cannot
+ # handle this type of merge.
+ test_expect_code 2 git merge branch2 branch3 >output 2>&1 &&
+ grep "fatal: merge program failed" output &&
+ grep "Should not be doing an octopus" output &&
+
+ # Make sure we did not leave stray changes around when no appropriate
+ # merge strategy was found
+ git diff --exit-code --name-status &&
+ test_path_is_missing .git/MERGE_HEAD
+'
+
+test_done
diff --git a/t/t7608-merge-messages.sh b/t/t7608-merge-messages.sh
index 0b908ab..2179938 100755
--- a/t/t7608-merge-messages.sh
+++ b/t/t7608-merge-messages.sh
@@ -4,6 +4,7 @@ test_description='test auto-generated merge messages'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
check_oneline() {
diff --git a/t/t7609-mergetool--lib.sh b/t/t7609-mergetool--lib.sh
new file mode 100755
index 0000000..8b1c3bd
--- /dev/null
+++ b/t/t7609-mergetool--lib.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+test_description='git mergetool
+
+Testing basic merge tools options'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'mergetool --tool=vimdiff creates the expected layout' '
+ . "$GIT_BUILD_DIR"/mergetools/vimdiff &&
+ run_unit_tests
+'
+
+test_done
diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh
index 8cc6472..22b3a85 100755
--- a/t/t7610-mergetool.sh
+++ b/t/t7610-mergetool.sh
@@ -33,7 +33,7 @@ test_expect_success 'setup' '
git add foo &&
git commit -m "Add foo"
) &&
- git submodule add git://example.com/submod submod &&
+ git submodule add file:///dev/null submod &&
git add file1 "spaced name" file1[1-4] subdir/file3 .gitmodules submod &&
git commit -m "add initial versions" &&
@@ -614,7 +614,7 @@ test_expect_success 'submodule in subdirectory' '
)
) &&
test_when_finished "rm -rf subdir/subdir_module" &&
- git submodule add git://example.com/subsubmodule subdir/subdir_module &&
+ git submodule add file:///dev/null subdir/subdir_module &&
git add subdir/subdir_module &&
git commit -m "add submodule in subdirectory" &&
@@ -860,4 +860,42 @@ test_expect_success 'mergetool hideResolved' '
git commit -m "test resolved with mergetool"
'
+test_expect_success 'mergetool with guiDefault' '
+ test_config merge.guitool myguitool &&
+ test_config mergetool.myguitool.cmd "(printf \"gui \" && cat \"\$REMOTE\") >\"\$MERGED\"" &&
+ test_config mergetool.myguitool.trustExitCode true &&
+ test_when_finished "git reset --hard" &&
+ git checkout -b test$test_count branch1 &&
+ git submodule update -N &&
+ test_must_fail git merge main &&
+
+ test_config mergetool.guiDefault auto &&
+ DISPLAY=SOMETHING && export DISPLAY &&
+ yes "" | git mergetool both &&
+ yes "" | git mergetool file1 file1 &&
+
+ DISPLAY= && export DISPLAY &&
+ yes "" | git mergetool file2 "spaced name" &&
+
+ test_config mergetool.guiDefault true &&
+ yes "" | git mergetool subdir/file3 &&
+
+ yes "d" | git mergetool file11 &&
+ yes "d" | git mergetool file12 &&
+ yes "l" | git mergetool submod &&
+
+ echo "gui main updated" >expect &&
+ test_cmp expect file1 &&
+
+ echo "main new" >expect &&
+ test_cmp expect file2 &&
+
+ echo "gui main new sub" >expect &&
+ test_cmp expect subdir/file3 &&
+
+ echo "branch1 submodule" >expect &&
+ test_cmp expect submod/bar &&
+ git commit -m "branch1 resolved with mergetool"
+'
+
test_done
diff --git a/t/t7611-merge-abort.sh b/t/t7611-merge-abort.sh
index c0e9425..d6975ca 100755
--- a/t/t7611-merge-abort.sh
+++ b/t/t7611-merge-abort.sh
@@ -50,7 +50,7 @@ pre_merge_head="$(git rev-parse HEAD)"
test_expect_success 'fails without MERGE_HEAD (unstarted merge)' '
test_must_fail git merge --abort 2>output &&
- test_i18ngrep MERGE_HEAD output
+ test_grep MERGE_HEAD output
'
test_expect_success 'fails without MERGE_HEAD (unstarted merge): .git/MERGE_HEAD sanity' '
@@ -64,7 +64,7 @@ test_expect_success 'fails without MERGE_HEAD (completed merge)' '
# Merge successfully completed
post_merge_head="$(git rev-parse HEAD)" &&
test_must_fail git merge --abort 2>output &&
- test_i18ngrep MERGE_HEAD output
+ test_grep MERGE_HEAD output
'
test_expect_success 'fails without MERGE_HEAD (completed merge): .git/MERGE_HEAD sanity' '
diff --git a/t/t7612-merge-verify-signatures.sh b/t/t7612-merge-verify-signatures.sh
index 61330f7..84ddb56 100755
--- a/t/t7612-merge-verify-signatures.sh
+++ b/t/t7612-merge-verify-signatures.sh
@@ -4,6 +4,7 @@ test_description='merge signature verification tests'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY/lib-gpg.sh"
@@ -40,54 +41,54 @@ test_expect_success GPG 'create signed commits' '
test_expect_success GPG 'merge unsigned commit with verification' '
test_when_finished "git reset --hard && git checkout initial" &&
test_must_fail git merge --ff-only --verify-signatures side-unsigned 2>mergeerror &&
- test_i18ngrep "does not have a GPG signature" mergeerror
+ test_grep "does not have a GPG signature" mergeerror
'
test_expect_success GPG 'merge unsigned commit with merge.verifySignatures=true' '
test_when_finished "git reset --hard && git checkout initial" &&
test_config merge.verifySignatures true &&
test_must_fail git merge --ff-only side-unsigned 2>mergeerror &&
- test_i18ngrep "does not have a GPG signature" mergeerror
+ test_grep "does not have a GPG signature" mergeerror
'
test_expect_success GPG 'merge commit with bad signature with verification' '
test_when_finished "git reset --hard && git checkout initial" &&
test_must_fail git merge --ff-only --verify-signatures $(cat forged.commit) 2>mergeerror &&
- test_i18ngrep "has a bad GPG signature" mergeerror
+ test_grep "has a bad GPG signature" mergeerror
'
test_expect_success GPG 'merge commit with bad signature with merge.verifySignatures=true' '
test_when_finished "git reset --hard && git checkout initial" &&
test_config merge.verifySignatures true &&
test_must_fail git merge --ff-only $(cat forged.commit) 2>mergeerror &&
- test_i18ngrep "has a bad GPG signature" mergeerror
+ test_grep "has a bad GPG signature" mergeerror
'
test_expect_success GPG 'merge commit with untrusted signature with verification' '
test_when_finished "git reset --hard && git checkout initial" &&
test_must_fail git merge --ff-only --verify-signatures side-untrusted 2>mergeerror &&
- test_i18ngrep "has an untrusted GPG signature" mergeerror
+ test_grep "has an untrusted GPG signature" mergeerror
'
test_expect_success GPG 'merge commit with untrusted signature with verification and high minTrustLevel' '
test_when_finished "git reset --hard && git checkout initial" &&
test_config gpg.minTrustLevel marginal &&
test_must_fail git merge --ff-only --verify-signatures side-untrusted 2>mergeerror &&
- test_i18ngrep "has an untrusted GPG signature" mergeerror
+ test_grep "has an untrusted GPG signature" mergeerror
'
test_expect_success GPG 'merge commit with untrusted signature with verification and low minTrustLevel' '
test_when_finished "git reset --hard && git checkout initial" &&
test_config gpg.minTrustLevel undefined &&
git merge --ff-only --verify-signatures side-untrusted >mergeoutput &&
- test_i18ngrep "has a good GPG signature" mergeoutput
+ test_grep "has a good GPG signature" mergeoutput
'
test_expect_success GPG 'merge commit with untrusted signature with merge.verifySignatures=true' '
test_when_finished "git reset --hard && git checkout initial" &&
test_config merge.verifySignatures true &&
test_must_fail git merge --ff-only side-untrusted 2>mergeerror &&
- test_i18ngrep "has an untrusted GPG signature" mergeerror
+ test_grep "has an untrusted GPG signature" mergeerror
'
test_expect_success GPG 'merge commit with untrusted signature with merge.verifySignatures=true and minTrustLevel' '
@@ -95,20 +96,20 @@ test_expect_success GPG 'merge commit with untrusted signature with merge.verify
test_config merge.verifySignatures true &&
test_config gpg.minTrustLevel marginal &&
test_must_fail git merge --ff-only side-untrusted 2>mergeerror &&
- test_i18ngrep "has an untrusted GPG signature" mergeerror
+ test_grep "has an untrusted GPG signature" mergeerror
'
test_expect_success GPG 'merge signed commit with verification' '
test_when_finished "git reset --hard && git checkout initial" &&
git merge --verbose --ff-only --verify-signatures side-signed >mergeoutput &&
- test_i18ngrep "has a good GPG signature" mergeoutput
+ test_grep "has a good GPG signature" mergeoutput
'
test_expect_success GPG 'merge signed commit with merge.verifySignatures=true' '
test_when_finished "git reset --hard && git checkout initial" &&
test_config merge.verifySignatures true &&
git merge --verbose --ff-only side-signed >mergeoutput &&
- test_i18ngrep "has a good GPG signature" mergeoutput
+ test_grep "has a good GPG signature" mergeoutput
'
test_expect_success GPG 'merge commit with bad signature without verification' '
@@ -132,7 +133,7 @@ test_expect_success GPG 'merge unsigned commit into unborn branch' '
test_when_finished "git checkout initial" &&
git checkout --orphan unborn &&
test_must_fail git merge --verify-signatures side-unsigned 2>mergeerror &&
- test_i18ngrep "does not have a GPG signature" mergeerror
+ test_grep "does not have a GPG signature" mergeerror
'
test_done
diff --git a/t/t7614-merge-signoff.sh b/t/t7614-merge-signoff.sh
index fee258d..cf96a35 100755
--- a/t/t7614-merge-signoff.sh
+++ b/t/t7614-merge-signoff.sh
@@ -8,6 +8,7 @@ This test runs git merge --signoff and makes sure that it works.
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Setup test files
diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh
index 25b235c..127efe9 100755
--- a/t/t7700-repack.sh
+++ b/t/t7700-repack.sh
@@ -3,10 +3,17 @@
test_description='git repack works correctly'
. ./test-lib.sh
+. "${TEST_DIRECTORY}/lib-bitmap.sh"
+. "${TEST_DIRECTORY}/lib-midx.sh"
+. "${TEST_DIRECTORY}/lib-terminal.sh"
commit_and_pack () {
test_commit "$@" 1>&2 &&
incrpackid=$(git pack-objects --all --unpacked --incremental .git/objects/pack/pack </dev/null) &&
+ # Remove any loose object(s) created by test_commit, since they have
+ # already been packed. Leaving these around can create subtly different
+ # packs with `pack-objects`'s `--unpacked` option.
+ git prune-packed 1>&2 &&
echo pack-${incrpackid}.pack
}
@@ -63,13 +70,14 @@ test_expect_success 'objects in packs marked .keep are not repacked' '
test_expect_success 'writing bitmaps via command-line can duplicate .keep objects' '
# build on $oid, $packid, and .keep state from previous
- git repack -Adbl &&
+ GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0 git repack -Adbl &&
test_has_duplicate_object true
'
test_expect_success 'writing bitmaps via config can duplicate .keep objects' '
# build on $oid, $packid, and .keep state from previous
- git -c repack.writebitmaps=true repack -Adl &&
+ GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0 \
+ git -c repack.writebitmaps=true repack -Adl &&
test_has_duplicate_object true
'
@@ -86,6 +94,39 @@ test_expect_success 'loose objects in alternate ODB are not repacked' '
test_has_duplicate_object false
'
+test_expect_success SYMLINKS '--local keeps packs when alternate is objectdir ' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_commit -C repo A &&
+ (
+ cd repo &&
+ git repack -a &&
+ ls .git/objects/pack/*.pack >../expect &&
+ ln -s objects .git/alt_objects &&
+ echo "$(pwd)/.git/alt_objects" >.git/objects/info/alternates &&
+ git repack -a -d -l &&
+ ls .git/objects/pack/*.pack >../actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_expect_success '--local disables writing bitmaps when connected to alternate ODB' '
+ test_when_finished "rm -rf shared member" &&
+
+ git init shared &&
+ git clone --shared shared member &&
+ (
+ cd member &&
+ test_commit "object" &&
+ GIT_TEST_MULTI_PACK_INDEX=0 git repack -Adl --write-bitmap-index 2>err &&
+ cat >expect <<-EOF &&
+ warning: disabling bitmap writing, as some objects are not being packed
+ EOF
+ test_cmp expect err &&
+ test_path_is_missing .git/objects/pack-*.bitmap
+ )
+'
+
test_expect_success 'packed obs in alt ODB are repacked even when local repo is packless' '
mkdir alt_objects/pack &&
mv .git/objects/pack/* alt_objects/pack &&
@@ -114,7 +155,7 @@ test_expect_success 'packed obs in alternate ODB kept pack are repacked' '
rm alt_objects/pack/$base_name.keep
else
touch alt_objects/pack/$base_name.keep
- fi
+ fi || return 1
done &&
git repack -a -d &&
test_no_missing_in_packs
@@ -172,6 +213,8 @@ test_expect_success 'repack --keep-pack' '
test_create_repo keep-pack &&
(
cd keep-pack &&
+ # avoid producing different packs due to delta/base choices
+ git config pack.window 0 &&
P1=$(commit_and_pack 1) &&
P2=$(commit_and_pack 2) &&
P3=$(commit_and_pack 3) &&
@@ -183,13 +226,66 @@ test_expect_success 'repack --keep-pack' '
grep -q $P1 new-counts &&
grep -q $P4 new-counts &&
test_line_count = 3 new-counts &&
+ git fsck &&
+
+ P5=$(commit_and_pack --no-tag 5) &&
+ git reset --hard HEAD^ &&
+ git reflog expire --all --expire=all &&
+ rm -f ".git/objects/pack/${P5%.pack}.idx" &&
+ rm -f ".git/objects/info/commit-graph" &&
+ for from in $(find .git/objects/pack -type f -name "${P5%.pack}.*")
+ do
+ to="$(dirname "$from")/.tmp-1234-$(basename "$from")" &&
+ mv "$from" "$to" || return 1
+ done &&
+
+ # A .idx file without a .pack should not stop us from
+ # repacking what we can.
+ touch .git/objects/pack/pack-does-not-exist.idx &&
+
+ git repack --cruft -d --keep-pack $P1 --keep-pack $P4 &&
+
+ ls .git/objects/pack/*.pack >newer-counts &&
+ test_cmp new-counts newer-counts &&
git fsck
)
'
+test_expect_success 'repacking fails when missing .pack actually means missing objects' '
+ test_create_repo idx-without-pack &&
+ (
+ cd idx-without-pack &&
+
+ # Avoid producing different packs due to delta/base choices
+ git config pack.window 0 &&
+ P1=$(commit_and_pack 1) &&
+ P2=$(commit_and_pack 2) &&
+ P3=$(commit_and_pack 3) &&
+ P4=$(commit_and_pack 4) &&
+ ls .git/objects/pack/*.pack >old-counts &&
+ test_line_count = 4 old-counts &&
+
+ # Remove one .pack file
+ rm .git/objects/pack/$P2 &&
+
+ ls .git/objects/pack/*.pack >before-pack-dir &&
+
+ test_must_fail git fsck &&
+ test_must_fail env GIT_COMMIT_GRAPH_PARANOIA=true git repack --cruft -d 2>err &&
+ grep "bad object" err &&
+
+ # Before failing, the repack did not modify the
+ # pack directory.
+ ls .git/objects/pack/*.pack >after-pack-dir &&
+ test_cmp before-pack-dir after-pack-dir
+ )
+'
+
test_expect_success 'bitmaps are created by default in bare repos' '
git clone --bare .git bare.git &&
- git -C bare.git repack -ad &&
+ rm -f bare.git/objects/pack/*.bitmap &&
+ GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0 \
+ git -C bare.git repack -ad &&
bitmap=$(ls bare.git/objects/pack/*.bitmap) &&
test_path_is_file "$bitmap"
'
@@ -200,7 +296,8 @@ test_expect_success 'incremental repack does not complain' '
'
test_expect_success 'bitmaps can be disabled on bare repos' '
- git -c repack.writeBitmaps=false -C bare.git repack -ad &&
+ GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0 \
+ git -c repack.writeBitmaps=false -C bare.git repack -ad &&
bitmap=$(ls bare.git/objects/pack/*.bitmap || :) &&
test -z "$bitmap"
'
@@ -211,7 +308,8 @@ test_expect_success 'no bitmaps created if .keep files present' '
keep=${pack%.pack}.keep &&
test_when_finished "rm -f \"\$keep\"" &&
>"$keep" &&
- git -C bare.git repack -ad 2>stderr &&
+ GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0 \
+ git -C bare.git repack -ad 2>stderr &&
test_must_be_empty stderr &&
find bare.git/objects/pack/ -type f -name "*.bitmap" >actual &&
test_must_be_empty actual
@@ -222,10 +320,516 @@ test_expect_success 'auto-bitmaps do not complain if unavailable' '
blob=$(test-tool genrandom big $((1024*1024)) |
git -C bare.git hash-object -w --stdin) &&
git -C bare.git update-ref refs/tags/big $blob &&
- git -C bare.git repack -ad 2>stderr &&
+ GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0 \
+ git -C bare.git repack -ad 2>stderr &&
test_must_be_empty stderr &&
find bare.git/objects/pack -type f -name "*.bitmap" >actual &&
test_must_be_empty actual
'
+test_expect_success 'repacking with a filter works' '
+ git -C bare.git repack -a -d &&
+ test_stdout_line_count = 1 ls bare.git/objects/pack/*.pack &&
+ git -C bare.git -c repack.writebitmaps=false repack -a -d --filter=blob:none &&
+ test_stdout_line_count = 2 ls bare.git/objects/pack/*.pack &&
+ commit_pack=$(test-tool -C bare.git find-pack -c 1 HEAD) &&
+ blob_pack=$(test-tool -C bare.git find-pack -c 1 HEAD:file1) &&
+ test "$commit_pack" != "$blob_pack" &&
+ tree_pack=$(test-tool -C bare.git find-pack -c 1 HEAD^{tree}) &&
+ test "$tree_pack" = "$commit_pack" &&
+ blob_pack2=$(test-tool -C bare.git find-pack -c 1 HEAD:file2) &&
+ test "$blob_pack2" = "$blob_pack"
+'
+
+test_expect_success '--filter fails with --write-bitmap-index' '
+ test_must_fail \
+ env GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0 \
+ git -C bare.git repack -a -d --write-bitmap-index --filter=blob:none
+'
+
+test_expect_success 'repacking with two filters works' '
+ git init two-filters &&
+ (
+ cd two-filters &&
+ mkdir subdir &&
+ test_commit foo &&
+ test_commit subdir_bar subdir/bar &&
+ test_commit subdir_baz subdir/baz
+ ) &&
+ git clone --no-local --bare two-filters two-filters.git &&
+ (
+ cd two-filters.git &&
+ test_stdout_line_count = 1 ls objects/pack/*.pack &&
+ git -c repack.writebitmaps=false repack -a -d \
+ --filter=blob:none --filter=tree:1 &&
+ test_stdout_line_count = 2 ls objects/pack/*.pack &&
+ commit_pack=$(test-tool find-pack -c 1 HEAD) &&
+ blob_pack=$(test-tool find-pack -c 1 HEAD:foo.t) &&
+ root_tree_pack=$(test-tool find-pack -c 1 HEAD^{tree}) &&
+ subdir_tree_hash=$(git ls-tree --object-only HEAD -- subdir) &&
+ subdir_tree_pack=$(test-tool find-pack -c 1 "$subdir_tree_hash") &&
+
+ # Root tree and subdir tree are not in the same packfiles
+ test "$commit_pack" != "$blob_pack" &&
+ test "$commit_pack" = "$root_tree_pack" &&
+ test "$blob_pack" = "$subdir_tree_pack"
+ )
+'
+
+prepare_for_keep_packs () {
+ git init keep-packs &&
+ (
+ cd keep-packs &&
+ test_commit foo &&
+ test_commit bar
+ ) &&
+ git clone --no-local --bare keep-packs keep-packs.git &&
+ (
+ cd keep-packs.git &&
+
+ # Create two packs
+ # The first pack will contain all of the objects except one blob
+ git rev-list --objects --all >objs &&
+ grep -v "bar.t" objs | git pack-objects pack &&
+ # The second pack will contain the excluded object and be kept
+ packid=$(grep "bar.t" objs | git pack-objects pack) &&
+ >pack-$packid.keep &&
+
+ # Replace the existing pack with the 2 new ones
+ rm -f objects/pack/pack* &&
+ mv pack-* objects/pack/
+ )
+}
+
+test_expect_success '--filter works with .keep packs' '
+ prepare_for_keep_packs &&
+ (
+ cd keep-packs.git &&
+
+ foo_pack=$(test-tool find-pack -c 1 HEAD:foo.t) &&
+ bar_pack=$(test-tool find-pack -c 1 HEAD:bar.t) &&
+ head_pack=$(test-tool find-pack -c 1 HEAD) &&
+
+ test "$foo_pack" != "$bar_pack" &&
+ test "$foo_pack" = "$head_pack" &&
+
+ git -c repack.writebitmaps=false repack -a -d --filter=blob:none &&
+
+ foo_pack_1=$(test-tool find-pack -c 1 HEAD:foo.t) &&
+ bar_pack_1=$(test-tool find-pack -c 1 HEAD:bar.t) &&
+ head_pack_1=$(test-tool find-pack -c 1 HEAD) &&
+
+ # Object bar is still only in the old .keep pack
+ test "$foo_pack_1" != "$foo_pack" &&
+ test "$bar_pack_1" = "$bar_pack" &&
+ test "$head_pack_1" != "$head_pack" &&
+
+ test "$foo_pack_1" != "$bar_pack_1" &&
+ test "$foo_pack_1" != "$head_pack_1" &&
+ test "$bar_pack_1" != "$head_pack_1"
+ )
+'
+
+test_expect_success '--filter works with --pack-kept-objects and .keep packs' '
+ rm -rf keep-packs keep-packs.git &&
+ prepare_for_keep_packs &&
+ (
+ cd keep-packs.git &&
+
+ foo_pack=$(test-tool find-pack -c 1 HEAD:foo.t) &&
+ bar_pack=$(test-tool find-pack -c 1 HEAD:bar.t) &&
+ head_pack=$(test-tool find-pack -c 1 HEAD) &&
+
+ test "$foo_pack" != "$bar_pack" &&
+ test "$foo_pack" = "$head_pack" &&
+
+ git -c repack.writebitmaps=false repack -a -d --filter=blob:none \
+ --pack-kept-objects &&
+
+ foo_pack_1=$(test-tool find-pack -c 1 HEAD:foo.t) &&
+ test-tool find-pack -c 2 HEAD:bar.t >bar_pack_1 &&
+ head_pack_1=$(test-tool find-pack -c 1 HEAD) &&
+
+ test "$foo_pack_1" != "$foo_pack" &&
+ test "$foo_pack_1" != "$bar_pack" &&
+ test "$head_pack_1" != "$head_pack" &&
+
+ # Object bar is in both the old .keep pack and the new
+ # pack that contained the filtered out objects
+ grep "$bar_pack" bar_pack_1 &&
+ grep "$foo_pack_1" bar_pack_1 &&
+ test "$foo_pack_1" != "$head_pack_1"
+ )
+'
+
+test_expect_success '--filter-to stores filtered out objects' '
+ git -C bare.git repack -a -d &&
+ test_stdout_line_count = 1 ls bare.git/objects/pack/*.pack &&
+
+ git init --bare filtered.git &&
+ git -C bare.git -c repack.writebitmaps=false repack -a -d \
+ --filter=blob:none \
+ --filter-to=../filtered.git/objects/pack/pack &&
+ test_stdout_line_count = 1 ls bare.git/objects/pack/pack-*.pack &&
+ test_stdout_line_count = 1 ls filtered.git/objects/pack/pack-*.pack &&
+
+ commit_pack=$(test-tool -C bare.git find-pack -c 1 HEAD) &&
+ blob_pack=$(test-tool -C bare.git find-pack -c 0 HEAD:file1) &&
+ blob_hash=$(git -C bare.git rev-parse HEAD:file1) &&
+ test -n "$blob_hash" &&
+ blob_pack=$(test-tool -C filtered.git find-pack -c 1 $blob_hash) &&
+
+ echo $(pwd)/filtered.git/objects >bare.git/objects/info/alternates &&
+ blob_pack=$(test-tool -C bare.git find-pack -c 1 HEAD:file1) &&
+ blob_content=$(git -C bare.git show $blob_hash) &&
+ test "$blob_content" = "content1"
+'
+
+test_expect_success '--filter works with --max-pack-size' '
+ rm -rf filtered.git &&
+ git init --bare filtered.git &&
+ git init max-pack-size &&
+ (
+ cd max-pack-size &&
+ test_commit base &&
+ # two blobs which exceed the maximum pack size
+ test-tool genrandom foo 1048576 >foo &&
+ git hash-object -w foo &&
+ test-tool genrandom bar 1048576 >bar &&
+ git hash-object -w bar &&
+ git add foo bar &&
+ git commit -m "adding foo and bar"
+ ) &&
+ git clone --no-local --bare max-pack-size max-pack-size.git &&
+ (
+ cd max-pack-size.git &&
+ git -c repack.writebitmaps=false repack -a -d --filter=blob:none \
+ --max-pack-size=1M \
+ --filter-to=../filtered.git/objects/pack/pack &&
+ echo $(cd .. && pwd)/filtered.git/objects >objects/info/alternates &&
+
+ # Check that the 3 blobs are in different packfiles in filtered.git
+ test_stdout_line_count = 3 ls ../filtered.git/objects/pack/pack-*.pack &&
+ test_stdout_line_count = 1 ls objects/pack/pack-*.pack &&
+ foo_pack=$(test-tool find-pack -c 1 HEAD:foo) &&
+ bar_pack=$(test-tool find-pack -c 1 HEAD:bar) &&
+ base_pack=$(test-tool find-pack -c 1 HEAD:base.t) &&
+ test "$foo_pack" != "$bar_pack" &&
+ test "$foo_pack" != "$base_pack" &&
+ test "$bar_pack" != "$base_pack" &&
+ for pack in "$foo_pack" "$bar_pack" "$base_pack"
+ do
+ case "$foo_pack" in */filtered.git/objects/pack/*) true ;; *) return 1 ;; esac
+ done
+ )
+'
+
+objdir=.git/objects
+midx=$objdir/pack/multi-pack-index
+
+test_expect_success 'setup for --write-midx tests' '
+ git init midx &&
+ (
+ cd midx &&
+ git config core.multiPackIndex true &&
+
+ test_commit base
+ )
+'
+
+test_expect_success '--write-midx unchanged' '
+ (
+ cd midx &&
+ GIT_TEST_MULTI_PACK_INDEX=0 git repack &&
+ test_path_is_missing $midx &&
+ test_path_is_missing $midx-*.bitmap &&
+
+ GIT_TEST_MULTI_PACK_INDEX=0 git repack --write-midx &&
+
+ test_path_is_file $midx &&
+ test_path_is_missing $midx-*.bitmap &&
+ test_midx_consistent $objdir
+ )
+'
+
+test_expect_success '--write-midx with a new pack' '
+ (
+ cd midx &&
+ test_commit loose &&
+
+ GIT_TEST_MULTI_PACK_INDEX=0 git repack --write-midx &&
+
+ test_path_is_file $midx &&
+ test_path_is_missing $midx-*.bitmap &&
+ test_midx_consistent $objdir
+ )
+'
+
+test_expect_success '--write-midx with -b' '
+ (
+ cd midx &&
+ GIT_TEST_MULTI_PACK_INDEX=0 git repack -mb &&
+
+ test_path_is_file $midx &&
+ test_path_is_file $midx-*.bitmap &&
+ test_midx_consistent $objdir
+ )
+'
+
+test_expect_success '--write-midx with -d' '
+ (
+ cd midx &&
+ test_commit repack &&
+
+ GIT_TEST_MULTI_PACK_INDEX=0 git repack -Ad --write-midx &&
+
+ test_path_is_file $midx &&
+ test_path_is_missing $midx-*.bitmap &&
+ test_midx_consistent $objdir
+ )
+'
+
+test_expect_success 'cleans up MIDX when appropriate' '
+ (
+ cd midx &&
+
+ test_commit repack-2 &&
+ GIT_TEST_MULTI_PACK_INDEX=0 git repack -Adb --write-midx &&
+
+ checksum=$(midx_checksum $objdir) &&
+ test_path_is_file $midx &&
+ test_path_is_file $midx-$checksum.bitmap &&
+
+ test_commit repack-3 &&
+ GIT_TEST_MULTI_PACK_INDEX=0 git repack -Adb --write-midx &&
+
+ test_path_is_file $midx &&
+ test_path_is_missing $midx-$checksum.bitmap &&
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
+
+ test_commit repack-4 &&
+ GIT_TEST_MULTI_PACK_INDEX=0 git repack -Adb &&
+
+ find $objdir/pack -type f -name "multi-pack-index*" >files &&
+ test_must_be_empty files
+ )
+'
+
+test_expect_success '--write-midx with preferred bitmap tips' '
+ git init midx-preferred-tips &&
+ test_when_finished "rm -fr midx-preferred-tips" &&
+ (
+ cd midx-preferred-tips &&
+
+ test_commit_bulk --message="%s" 103 &&
+
+ git log --format="%H" >commits.raw &&
+ sort <commits.raw >commits &&
+
+ git log --format="create refs/tags/%s/%s %H" HEAD >refs &&
+ git update-ref --stdin <refs &&
+
+ GIT_TEST_MULTI_PACK_INDEX=0 \
+ git repack --write-midx --write-bitmap-index &&
+ test_path_is_file $midx &&
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
+
+ test-tool bitmap list-commits | sort >bitmaps &&
+ comm -13 bitmaps commits >before &&
+ test_line_count = 1 before &&
+
+ rm -fr $midx-$(midx_checksum $objdir).bitmap &&
+ rm -fr $midx &&
+
+ # instead of constructing the snapshot ourselves (c.f., the test
+ # "write a bitmap with --refs-snapshot (preferred tips)" in
+ # t5326), mark the missing commit as preferred by adding it to
+ # the pack.preferBitmapTips configuration.
+ git for-each-ref --format="%(refname:rstrip=1)" \
+ --points-at="$(cat before)" >missing &&
+ git config pack.preferBitmapTips "$(cat missing)" &&
+ git repack --write-midx --write-bitmap-index &&
+
+ test-tool bitmap list-commits | sort >bitmaps &&
+ comm -13 bitmaps commits >after &&
+
+ ! test_cmp before after
+ )
+'
+
+# The first argument is expected to be a filename
+# and that file should contain the name of a .idx
+# file. Send the list of objects in that .idx file
+# into stdout.
+get_sorted_objects_from_pack () {
+ git show-index <$(cat "$1") >raw &&
+ cut -d" " -f2 raw
+}
+
+test_expect_success '--write-midx -b packs non-kept objects' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ # Create a kept pack-file
+ test_commit base &&
+ git repack -ad &&
+ find $objdir/pack -name "*.idx" >before &&
+ test_line_count = 1 before &&
+ before_name=$(cat before) &&
+ >${before_name%.idx}.keep &&
+
+ # Create a non-kept pack-file
+ test_commit other &&
+ git repack &&
+
+ # Create loose objects
+ test_commit loose &&
+
+ # Repack everything
+ git repack --write-midx -a -b -d &&
+
+ # There should be two pack-files now, the
+ # old, kept pack and the new, non-kept pack.
+ find $objdir/pack -name "*.idx" | sort >after &&
+ test_line_count = 2 after &&
+ find $objdir/pack -name "*.keep" >kept &&
+ kept_name=$(cat kept) &&
+ echo ${kept_name%.keep}.idx >kept-idx &&
+ test_cmp before kept-idx &&
+
+ # Get object list from the kept pack.
+ get_sorted_objects_from_pack before >old.objects &&
+
+ # Get object list from the one non-kept pack-file
+ comm -13 before after >new-pack &&
+ test_line_count = 1 new-pack &&
+ get_sorted_objects_from_pack new-pack >new.objects &&
+
+ # None of the objects in the new pack should
+ # exist within the kept pack.
+ comm -12 old.objects new.objects >shared.objects &&
+ test_must_be_empty shared.objects
+ )
+'
+
+test_expect_success '--write-midx removes stale pack-based bitmaps' '
+ rm -fr repo &&
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ test_commit base &&
+ GIT_TEST_MULTI_PACK_INDEX=0 git repack -Ab &&
+
+ pack_bitmap=$(ls $objdir/pack/pack-*.bitmap) &&
+ test_path_is_file "$pack_bitmap" &&
+
+ test_commit tip &&
+ GIT_TEST_MULTI_PACK_INDEX=0 git repack -bm &&
+
+ test_path_is_file $midx &&
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
+ test_path_is_missing $pack_bitmap
+ )
+'
+
+test_expect_success '--write-midx with --pack-kept-objects' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit one &&
+ test_commit two &&
+
+ one="$(echo "one" | git pack-objects --revs $objdir/pack/pack)" &&
+ two="$(echo "one..two" | git pack-objects --revs $objdir/pack/pack)" &&
+
+ keep="$objdir/pack/pack-$one.keep" &&
+ touch "$keep" &&
+
+ GIT_TEST_MULTI_PACK_INDEX=0 \
+ git repack --write-midx --write-bitmap-index --geometric=2 -d \
+ --pack-kept-objects &&
+
+ test_path_is_file $keep &&
+ test_path_is_file $midx &&
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap
+ )
+'
+
+test_expect_success TTY '--quiet disables progress' '
+ test_terminal env GIT_PROGRESS_DELAY=0 \
+ git -C midx repack -ad --quiet --write-midx 2>stderr &&
+ test_must_be_empty stderr
+'
+
+test_expect_success 'clean up .tmp-* packs on error' '
+ test_must_fail ok=sigpipe git \
+ -c repack.cruftwindow=bogus \
+ repack -ad --cruft &&
+ find $objdir/pack -name '.tmp-*' >tmpfiles &&
+ test_must_be_empty tmpfiles
+'
+
+test_expect_success 'repack -ad cleans up old .tmp-* packs' '
+ git rev-parse HEAD >input &&
+ git pack-objects $objdir/pack/.tmp-1234 <input &&
+ git repack -ad &&
+ find $objdir/pack -name '.tmp-*' >tmpfiles &&
+ test_must_be_empty tmpfiles
+'
+
+test_expect_success 'setup for update-server-info' '
+ git init update-server-info &&
+ test_commit -C update-server-info message
+'
+
+test_server_info_present () {
+ test_path_is_file update-server-info/.git/objects/info/packs &&
+ test_path_is_file update-server-info/.git/info/refs
+}
+
+test_server_info_missing () {
+ test_path_is_missing update-server-info/.git/objects/info/packs &&
+ test_path_is_missing update-server-info/.git/info/refs
+}
+
+test_server_info_cleanup () {
+ rm -f update-server-info/.git/objects/info/packs update-server-info/.git/info/refs &&
+ test_server_info_missing
+}
+
+test_expect_success 'updates server info by default' '
+ test_server_info_cleanup &&
+ git -C update-server-info repack &&
+ test_server_info_present
+'
+
+test_expect_success '-n skips updating server info' '
+ test_server_info_cleanup &&
+ git -C update-server-info repack -n &&
+ test_server_info_missing
+'
+
+test_expect_success 'repack.updateServerInfo=true updates server info' '
+ test_server_info_cleanup &&
+ git -C update-server-info -c repack.updateServerInfo=true repack &&
+ test_server_info_present
+'
+
+test_expect_success 'repack.updateServerInfo=false skips updating server info' '
+ test_server_info_cleanup &&
+ git -C update-server-info -c repack.updateServerInfo=false repack &&
+ test_server_info_missing
+'
+
+test_expect_success '-n overrides repack.updateServerInfo=true' '
+ test_server_info_cleanup &&
+ git -C update-server-info -c repack.updateServerInfo=true repack -n &&
+ test_server_info_missing
+'
+
test_done
diff --git a/t/t7701-repack-unpack-unreachable.sh b/t/t7701-repack-unpack-unreachable.sh
index 937f89e..fe6c3e7 100755
--- a/t/t7701-repack-unpack-unreachable.sh
+++ b/t/t7701-repack-unpack-unreachable.sh
@@ -5,6 +5,7 @@ test_description='git repack works correctly'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
fsha1=
@@ -35,7 +36,7 @@ test_expect_success '-A with -d option leaves unreachable objects unpacked' '
git repack -A -d -l &&
# verify objects are packed in repository
test 3 = $(git verify-pack -v -- .git/objects/pack/*.idx |
- egrep "^($fsha1|$csha1|$tsha1) " |
+ grep -E "^($fsha1|$csha1|$tsha1) " |
sort | uniq | wc -l) &&
git show $fsha1 &&
git show $csha1 &&
@@ -49,7 +50,7 @@ test_expect_success '-A with -d option leaves unreachable objects unpacked' '
git repack -A -d -l &&
# verify objects are retained unpacked
test 0 = $(git verify-pack -v -- .git/objects/pack/*.idx |
- egrep "^($fsha1|$csha1|$tsha1) " |
+ grep -E "^($fsha1|$csha1|$tsha1) " |
sort | uniq | wc -l) &&
git show $fsha1 &&
git show $csha1 &&
@@ -112,6 +113,48 @@ test_expect_success 'do not bother loosening old objects' '
test_must_fail git cat-file -p $obj2
'
+test_expect_success 'gc.recentObjectsHook' '
+ obj1=$(echo one | git hash-object -w --stdin) &&
+ obj2=$(echo two | git hash-object -w --stdin) &&
+ obj3=$(echo three | git hash-object -w --stdin) &&
+ pack1=$(echo $obj1 | git pack-objects .git/objects/pack/pack) &&
+ pack2=$(echo $obj2 | git pack-objects .git/objects/pack/pack) &&
+ pack3=$(echo $obj3 | git pack-objects .git/objects/pack/pack) &&
+ git prune-packed &&
+
+ git cat-file -p $obj1 &&
+ git cat-file -p $obj2 &&
+ git cat-file -p $obj3 &&
+
+ # make an unreachable annotated tag object to ensure we rescue objects
+ # which are reachable from non-pruned unreachable objects
+ obj2_tag="$(git mktag <<-EOF
+ object $obj2
+ type blob
+ tag obj2-tag
+ tagger T A Gger <tagger@example.com> 1234567890 -0000
+ EOF
+ )" &&
+
+ obj2_tag_pack="$(echo $obj2_tag | git pack-objects .git/objects/pack/pack)" &&
+ git prune-packed &&
+
+ write_script precious-objects <<-EOF &&
+ echo $obj2_tag
+ EOF
+ git config gc.recentObjectsHook ./precious-objects &&
+
+ test-tool chmtime =-86400 .git/objects/pack/pack-$pack2.pack &&
+ test-tool chmtime =-86400 .git/objects/pack/pack-$pack3.pack &&
+ test-tool chmtime =-86400 .git/objects/pack/pack-$obj2_tag_pack.pack &&
+ git repack -A -d --unpack-unreachable=1.hour.ago &&
+
+ git cat-file -p $obj1 &&
+ git cat-file -p $obj2 &&
+ git cat-file -p $obj2_tag &&
+ test_must_fail git cat-file -p $obj3
+'
+
test_expect_success 'keep packed objects found only in index' '
echo my-unique-content >file &&
git add file &&
diff --git a/t/t7702-repack-cyclic-alternate.sh b/t/t7702-repack-cyclic-alternate.sh
index 93b7486..f3cdb98 100755
--- a/t/t7702-repack-cyclic-alternate.sh
+++ b/t/t7702-repack-cyclic-alternate.sh
@@ -4,6 +4,8 @@
#
test_description='repack involving cyclic alternate'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t7703-repack-geometric.sh b/t/t7703-repack-geometric.sh
index 5ccaa44..9fc1626 100755
--- a/t/t7703-repack-geometric.sh
+++ b/t/t7703-repack-geometric.sh
@@ -7,16 +7,23 @@ test_description='git repack --geometric works correctly'
GIT_TEST_MULTI_PACK_INDEX=0
objdir=.git/objects
+packdir=$objdir/pack
midx=$objdir/pack/multi-pack-index
+packed_objects () {
+ git show-index <"$1" >tmp-object-list &&
+ cut -d' ' -f2 tmp-object-list | sort &&
+ rm tmp-object-list
+ }
+
test_expect_success '--geometric with no packs' '
git init geometric &&
test_when_finished "rm -fr geometric" &&
(
cd geometric &&
- git repack --geometric 2 >out &&
- test_i18ngrep "Nothing new to pack" out
+ git repack --write-midx --geometric 2 >out &&
+ test_grep "Nothing new to pack" out
)
'
@@ -31,7 +38,7 @@ test_expect_success '--geometric with one pack' '
git repack --geometric 2 >out &&
- test_i18ngrep "Nothing new to pack" out
+ test_grep "Nothing new to pack" out
)
'
@@ -175,9 +182,267 @@ test_expect_success '--geometric ignores kept packs' '
# be repacked, too.
git repack --geometric 2 -d --pack-kept-objects &&
+ # After repacking, two packs remain: one new one (containing the
+ # objects in both the .keep and non-kept pack), and the .keep
+ # pack (since `--pack-kept-objects -d` does not actually delete
+ # the kept pack).
find $objdir/pack -name "*.pack" >after &&
- test_line_count = 1 after
+ test_line_count = 2 after
+ )
+'
+
+test_expect_success '--geometric ignores --keep-pack packs' '
+ git init geometric &&
+ test_when_finished "rm -fr geometric" &&
+ (
+ cd geometric &&
+
+ # Create two equal-sized packs
+ test_commit kept && # 3 objects
+ git repack -d &&
+ test_commit pack && # 3 objects
+ git repack -d &&
+
+ find $objdir/pack -type f -name "*.pack" | sort >packs.before &&
+ git repack --geometric 2 -dm \
+ --keep-pack="$(basename "$(head -n 1 packs.before)")" >out &&
+ find $objdir/pack -type f -name "*.pack" | sort >packs.after &&
+
+ # Packs should not have changed (only one non-kept pack, no
+ # loose objects), but $midx should now exist.
+ grep "Nothing new to pack" out &&
+ test_path_is_file $midx &&
+
+ test_cmp packs.before packs.after &&
+
+ git fsck
+ )
+'
+
+test_expect_success '--geometric chooses largest MIDX preferred pack' '
+ git init geometric &&
+ test_when_finished "rm -fr geometric" &&
+ (
+ cd geometric &&
+
+ # These packs already form a geometric progression.
+ test_commit_bulk --start=1 1 && # 3 objects
+ test_commit_bulk --start=2 2 && # 6 objects
+ ls $objdir/pack/pack-*.idx >before &&
+ test_commit_bulk --start=4 4 && # 12 objects
+ ls $objdir/pack/pack-*.idx >after &&
+
+ git repack --geometric 2 -dbm &&
+
+ comm -3 before after | xargs -n 1 basename >expect &&
+ test-tool read-midx --preferred-pack $objdir >actual &&
+
+ test_cmp expect actual
)
'
+test_expect_success '--geometric with pack.packSizeLimit' '
+ git init pack-rewrite &&
+ test_when_finished "rm -fr pack-rewrite" &&
+ (
+ cd pack-rewrite &&
+
+ test-tool genrandom foo 1048576 >foo &&
+ test-tool genrandom bar 1048576 >bar &&
+
+ git add foo bar &&
+ test_tick &&
+ git commit -m base &&
+
+ git rev-parse HEAD:foo HEAD:bar >p1.objects &&
+ git rev-parse HEAD HEAD^{tree} >p2.objects &&
+
+ # These two packs each contain two objects, so the following
+ # `--geometric` repack will try to combine them.
+ p1="$(git pack-objects $packdir/pack <p1.objects)" &&
+ p2="$(git pack-objects $packdir/pack <p2.objects)" &&
+
+ # Remove any loose objects in packs, since we do not want extra
+ # copies around (which would mask over potential object
+ # corruption issues).
+ git prune-packed &&
+
+ # Both p1 and p2 will be rolled up, but pack-objects will write
+ # three packs:
+ #
+ # - one containing object "foo",
+ # - another containing object "bar",
+ # - a final pack containing the commit and tree objects
+ # (identical to p2 above)
+ git repack --geometric 2 -d --max-pack-size=1048576 &&
+
+ # Ensure `repack` can detect that the third pack it wrote
+ # (containing just the tree and commit objects) was identical to
+ # one that was below the geometric split, so that we can save it
+ # from deletion.
+ #
+ # If `repack` fails to do that, we will incorrectly delete p2,
+ # causing object corruption.
+ git fsck
+ )
+'
+
+test_expect_success '--geometric --write-midx with packfiles in main and alternate ODB' '
+ test_when_finished "rm -fr shared member" &&
+
+ # Create a shared repository that will serve as the alternate object
+ # database for the member linked to it. It has got some objects on its
+ # own that are packed into a single packfile.
+ git init shared &&
+ test_commit -C shared common-object &&
+ git -C shared repack -ad &&
+
+ # We create member so that its alternates file points to the shared
+ # repository. We then create a commit in it so that git-repack(1) has
+ # something to repack.
+ # of the shared object database.
+ git clone --shared shared member &&
+ test_commit -C member unique-object &&
+ git -C member repack --geometric=2 --write-midx 2>err &&
+ test_must_be_empty err &&
+
+ # We should see that a new packfile was generated.
+ find shared/.git/objects/pack -type f -name "*.pack" >packs &&
+ test_line_count = 1 packs &&
+
+ # We should also see a multi-pack-index. This multi-pack-index should
+ # never refer to any packfiles in the alternate object database.
+ test_path_is_file member/.git/objects/pack/multi-pack-index &&
+ test-tool read-midx member/.git/objects >packs.midx &&
+ grep "^pack-.*\.idx$" packs.midx | sort >actual &&
+ basename member/.git/objects/pack/pack-*.idx >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success '--geometric --with-midx with no local objects' '
+ test_when_finished "rm -fr shared member" &&
+
+ # Create a repository with a single packfile that acts as alternate
+ # object database.
+ git init shared &&
+ test_commit -C shared "shared-objects" &&
+ git -C shared repack -ad &&
+
+ # Create a second repository linked to the first one and perform a
+ # geometric repack on it.
+ git clone --shared shared member &&
+ git -C member repack --geometric 2 --write-midx 2>err &&
+ test_must_be_empty err &&
+
+ # Assert that we wrote neither a new packfile nor a multi-pack-index.
+ # We should not have a packfile because the single packfile in the
+ # alternate object database does not invalidate the geometric sequence.
+ # And we should not have a multi-pack-index because these only index
+ # local packfiles, and there are none.
+ test_dir_is_empty member/$packdir
+'
+
+test_expect_success '--geometric with same pack in main and alternate ODB' '
+ test_when_finished "rm -fr shared member" &&
+
+ # Create a repository with a single packfile that acts as alternate
+ # object database.
+ git init shared &&
+ test_commit -C shared "shared-objects" &&
+ git -C shared repack -ad &&
+
+ # We create the member repository as an exact copy so that it has the
+ # same packfile.
+ cp -r shared member &&
+ test-tool path-utils real_path shared/.git/objects >member/.git/objects/info/alternates &&
+ find shared/.git/objects -type f >expected-files &&
+
+ # Verify that we can repack objects as expected without observing any
+ # error. Having the same packfile in both ODBs used to cause an error
+ # in git-pack-objects(1).
+ git -C member repack --geometric 2 2>err &&
+ test_must_be_empty err &&
+ # Nothing should have changed.
+ find shared/.git/objects -type f >actual-files &&
+ test_cmp expected-files actual-files
+'
+
+test_expect_success '--geometric -l with non-intact geometric sequence across ODBs' '
+ test_when_finished "rm -fr shared member" &&
+
+ git init shared &&
+ test_commit_bulk -C shared --start=1 1 &&
+
+ git clone --shared shared member &&
+ test_commit_bulk -C member --start=2 1 &&
+
+ # Verify that our assumptions actually hold: both generated packfiles
+ # should have three objects and should be non-equal.
+ packed_objects shared/.git/objects/pack/pack-*.idx >shared-objects &&
+ packed_objects member/.git/objects/pack/pack-*.idx >member-objects &&
+ test_line_count = 3 shared-objects &&
+ test_line_count = 3 member-objects &&
+ ! test_cmp shared-objects member-objects &&
+
+ # Perform the geometric repack. With `-l`, we should only see the local
+ # packfile and thus arrive at the conclusion that the geometric
+ # sequence is intact. We thus expect no changes.
+ #
+ # Note that we are tweaking mtimes of the packfiles so that we can
+ # verify they did not change. This is done in order to detect the case
+ # where we do repack objects, but the resulting packfile is the same.
+ test-tool chmtime --verbose =0 member/.git/objects/pack/* >expected-member-packs &&
+ git -C member repack --geometric=2 -l -d &&
+ test-tool chmtime --verbose member/.git/objects/pack/* >actual-member-packs &&
+ test_cmp expected-member-packs actual-member-packs &&
+
+ {
+ packed_objects shared/.git/objects/pack/pack-*.idx &&
+ packed_objects member/.git/objects/pack/pack-*.idx
+ } | sort >expected-objects &&
+
+ # On the other hand, when doing a non-local geometric repack we should
+ # see both packfiles and thus repack them. We expect that the shared
+ # object database was not changed.
+ test-tool chmtime --verbose =0 shared/.git/objects/pack/* >expected-shared-packs &&
+ git -C member repack --geometric=2 -d &&
+ test-tool chmtime --verbose shared/.git/objects/pack/* >actual-shared-packs &&
+ test_cmp expected-shared-packs actual-shared-packs &&
+
+ # Furthermore, we expect that the member repository now has a single
+ # packfile that contains the combined shared and non-shared objects.
+ ls member/.git/objects/pack/pack-*.idx >actual &&
+ test_line_count = 1 actual &&
+ packed_objects member/.git/objects/pack/pack-*.idx >actual-objects &&
+ test_line_count = 6 actual-objects &&
+ test_cmp expected-objects actual-objects
+'
+
+test_expect_success '--geometric -l disables writing bitmaps with non-local packfiles' '
+ test_when_finished "rm -fr shared member" &&
+
+ git init shared &&
+ test_commit_bulk -C shared --start=1 1 &&
+
+ git clone --shared shared member &&
+ test_commit_bulk -C member --start=2 1 &&
+
+ # When performing a geometric repack with `-l` while connected to an
+ # alternate object database that has a packfile we do not have full
+ # coverage of objects. As a result, we expect that writing the bitmap
+ # will be disabled.
+ git -C member repack -l --geometric=2 --write-midx --write-bitmap-index 2>err &&
+ cat >expect <<-EOF &&
+ warning: disabling bitmap writing, as some objects are not being packed
+ EOF
+ test_cmp expect err &&
+ test_path_is_missing member/.git/objects/pack/multi-pack-index-*.bitmap &&
+
+ # On the other hand, when we repack without `-l`, we should see that
+ # the bitmap gets created.
+ git -C member repack --geometric=2 --write-midx --write-bitmap-index 2>err &&
+ test_must_be_empty err &&
+ test_path_is_file member/.git/objects/pack/multi-pack-index-*.bitmap
+'
+
test_done
diff --git a/t/t7704-repack-cruft.sh b/t/t7704-repack-cruft.sh
new file mode 100755
index 0000000..71e1ef3
--- /dev/null
+++ b/t/t7704-repack-cruft.sh
@@ -0,0 +1,414 @@
+#!/bin/sh
+
+test_description='git repack works correctly'
+
+. ./test-lib.sh
+
+objdir=.git/objects
+packdir=$objdir/pack
+
+test_expect_success '--expire-to stores pruned objects (now)' '
+ git init expire-to-now &&
+ (
+ cd expire-to-now &&
+
+ git branch -M main &&
+
+ test_commit base &&
+
+ git checkout -b cruft &&
+ test_commit --no-tag cruft &&
+
+ git rev-list --objects --no-object-names main..cruft >moved.raw &&
+ sort moved.raw >moved.want &&
+
+ git rev-list --all --objects --no-object-names >expect.raw &&
+ sort expect.raw >expect &&
+
+ git checkout main &&
+ git branch -D cruft &&
+ git reflog expire --all --expire=all &&
+
+ git init --bare expired.git &&
+ git repack -d \
+ --cruft --cruft-expiration="now" \
+ --expire-to="expired.git/objects/pack/pack" &&
+
+ expired="$(ls expired.git/objects/pack/pack-*.idx)" &&
+ test_path_is_file "${expired%.idx}.mtimes" &&
+
+ # Since the `--cruft-expiration` is "now", the effective
+ # behavior is to move _all_ unreachable objects out to
+ # the location in `--expire-to`.
+ git show-index <$expired >expired.raw &&
+ cut -d" " -f2 expired.raw | sort >expired.objects &&
+ git rev-list --all --objects --no-object-names \
+ >remaining.objects &&
+
+ # ...in other words, the combined contents of this
+ # repository and expired.git should be the same as the
+ # set of objects we started with.
+ sort expired.objects remaining.objects >actual &&
+ test_cmp expect actual &&
+
+ # The "moved" objects (i.e., those in expired.git)
+ # should be the same as the cruft objects which were
+ # expired in the previous step.
+ test_cmp moved.want expired.objects
+ )
+'
+
+test_expect_success '--expire-to stores pruned objects (5.minutes.ago)' '
+ git init expire-to-5.minutes.ago &&
+ (
+ cd expire-to-5.minutes.ago &&
+
+ git branch -M main &&
+
+ test_commit base &&
+
+ # Create two classes of unreachable objects, one which
+ # is older than 5 minutes (stale), and another which is
+ # newer (recent).
+ for kind in stale recent
+ do
+ git checkout -b $kind main &&
+ test_commit --no-tag $kind || return 1
+ done &&
+
+ git rev-list --objects --no-object-names main..stale >in &&
+ stale="$(git pack-objects $objdir/pack/pack <in)" &&
+ mtime="$(test-tool chmtime --get =-600 $objdir/pack/pack-$stale.pack)" &&
+
+ # expect holds the set of objects we expect to find in
+ # this repository after repacking
+ git rev-list --objects --no-object-names recent >expect.raw &&
+ sort expect.raw >expect &&
+
+ # moved.want holds the set of objects we expect to find
+ # in expired.git
+ git rev-list --objects --no-object-names main..stale >out &&
+ sort out >moved.want &&
+
+ git checkout main &&
+ git branch -D stale recent &&
+ git reflog expire --all --expire=all &&
+ git prune-packed &&
+
+ git init --bare expired.git &&
+ git repack -d \
+ --cruft --cruft-expiration=5.minutes.ago \
+ --expire-to="expired.git/objects/pack/pack" &&
+
+ # Some of the remaining objects in this repository are
+ # unreachable, so use `cat-file --batch-all-objects`
+ # instead of `rev-list` to get their names
+ git cat-file --batch-all-objects --batch-check="%(objectname)" \
+ >remaining.objects &&
+ sort remaining.objects >actual &&
+ test_cmp expect actual &&
+
+ (
+ cd expired.git &&
+
+ expired="$(ls objects/pack/pack-*.mtimes)" &&
+ test-tool pack-mtimes $(basename $expired) >out &&
+ cut -d" " -f1 out | sort >../moved.got &&
+
+ # Ensure that there are as many objects with the
+ # expected mtime as were moved to expired.git.
+ #
+ # In other words, ensure that the recorded
+ # mtimes of any moved objects was written
+ # correctly.
+ grep " $mtime$" out >matching &&
+ test_line_count = $(wc -l <../moved.want) matching
+ ) &&
+ test_cmp moved.want moved.got
+ )
+'
+
+generate_random_blob() {
+ test-tool genrandom "$@" >blob &&
+ git hash-object -w -t blob blob &&
+ rm blob
+}
+
+pack_random_blob () {
+ generate_random_blob "$@" &&
+ git repack -d -q >/dev/null
+}
+
+generate_cruft_pack () {
+ pack_random_blob "$@" >/dev/null &&
+
+ ls $packdir/pack-*.pack | xargs -n 1 basename >in &&
+ pack="$(git pack-objects --cruft $packdir/pack <in)" &&
+ git prune-packed &&
+
+ echo "$packdir/pack-$pack.mtimes"
+}
+
+test_expect_success '--max-cruft-size creates new packs when above threshold' '
+ git init max-cruft-size-large &&
+ (
+ cd max-cruft-size-large &&
+ test_commit base &&
+
+ foo="$(pack_random_blob foo $((1*1024*1024)))" &&
+ git repack --cruft -d &&
+ cruft_foo="$(ls $packdir/pack-*.mtimes)" &&
+
+ bar="$(pack_random_blob bar $((1*1024*1024)))" &&
+ git repack --cruft -d --max-cruft-size=1M &&
+ cruft_bar="$(ls $packdir/pack-*.mtimes | grep -v $cruft_foo)" &&
+
+ test-tool pack-mtimes $(basename "$cruft_foo") >foo.objects &&
+ test-tool pack-mtimes $(basename "$cruft_bar") >bar.objects &&
+
+ grep "^$foo" foo.objects &&
+ test_line_count = 1 foo.objects &&
+ grep "^$bar" bar.objects &&
+ test_line_count = 1 bar.objects
+ )
+'
+
+test_expect_success '--max-cruft-size combines existing packs when below threshold' '
+ git init max-cruft-size-small &&
+ (
+ cd max-cruft-size-small &&
+ test_commit base &&
+
+ foo="$(pack_random_blob foo $((1*1024*1024)))" &&
+ git repack --cruft -d &&
+
+ bar="$(pack_random_blob bar $((1*1024*1024)))" &&
+ git repack --cruft -d --max-cruft-size=10M &&
+
+ cruft=$(ls $packdir/pack-*.mtimes) &&
+ test-tool pack-mtimes $(basename "$cruft") >cruft.objects &&
+
+ grep "^$foo" cruft.objects &&
+ grep "^$bar" cruft.objects &&
+ test_line_count = 2 cruft.objects
+ )
+'
+
+test_expect_success '--max-cruft-size combines smaller packs first' '
+ git init max-cruft-size-consume-small &&
+ (
+ cd max-cruft-size-consume-small &&
+
+ test_commit base &&
+ git repack -ad &&
+
+ cruft_foo="$(generate_cruft_pack foo 524288)" && # 0.5 MiB
+ cruft_bar="$(generate_cruft_pack bar 524288)" && # 0.5 MiB
+ cruft_baz="$(generate_cruft_pack baz 1048576)" && # 1.0 MiB
+ cruft_quux="$(generate_cruft_pack quux 1572864)" && # 1.5 MiB
+
+ test-tool pack-mtimes "$(basename $cruft_foo)" >expect.raw &&
+ test-tool pack-mtimes "$(basename $cruft_bar)" >>expect.raw &&
+ sort expect.raw >expect.objects &&
+
+ # repacking with `--max-cruft-size=2M` should combine
+ # both 0.5 MiB packs together, instead of, say, one of
+ # the 0.5 MiB packs with the 1.0 MiB pack
+ ls $packdir/pack-*.mtimes | sort >cruft.before &&
+ git repack -d --cruft --max-cruft-size=2M &&
+ ls $packdir/pack-*.mtimes | sort >cruft.after &&
+
+ comm -13 cruft.before cruft.after >cruft.new &&
+ comm -23 cruft.before cruft.after >cruft.removed &&
+
+ test_line_count = 1 cruft.new &&
+ test_line_count = 2 cruft.removed &&
+
+ # the two smaller packs should be rolled up first
+ printf "%s\n" $cruft_foo $cruft_bar | sort >expect.removed &&
+ test_cmp expect.removed cruft.removed &&
+
+ # ...and contain the set of objects rolled up
+ test-tool pack-mtimes "$(basename $(cat cruft.new))" >actual.raw &&
+ sort actual.raw >actual.objects &&
+
+ test_cmp expect.objects actual.objects
+ )
+'
+
+test_expect_success 'setup --max-cruft-size with freshened objects' '
+ git init max-cruft-size-freshen &&
+ (
+ cd max-cruft-size-freshen &&
+
+ test_commit base &&
+ git repack -ad &&
+
+ foo="$(generate_random_blob foo 64)" &&
+ test-tool chmtime --get -10000 \
+ "$objdir/$(test_oid_to_path "$foo")" >foo.mtime &&
+
+ git repack --cruft -d &&
+
+ cruft="$(ls $packdir/pack-*.mtimes)" &&
+ test-tool pack-mtimes "$(basename $cruft)" >actual &&
+ echo "$foo $(cat foo.mtime)" >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success '--max-cruft-size with freshened objects (loose)' '
+ (
+ cd max-cruft-size-freshen &&
+
+ # regenerate the object, setting its mtime to be more recent
+ foo="$(generate_random_blob foo 64)" &&
+ test-tool chmtime --get -100 \
+ "$objdir/$(test_oid_to_path "$foo")" >foo.mtime &&
+
+ git repack --cruft -d &&
+
+ cruft="$(ls $packdir/pack-*.mtimes)" &&
+ test-tool pack-mtimes "$(basename $cruft)" >actual &&
+ echo "$foo $(cat foo.mtime)" >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success '--max-cruft-size with freshened objects (packed)' '
+ (
+ cd max-cruft-size-freshen &&
+
+ # regenerate the object and store it in a packfile,
+ # setting its mtime to be more recent
+ #
+ # store it alongside another cruft object so that we
+ # do not create an identical copy of the existing
+ # cruft pack (which contains $foo).
+ foo="$(generate_random_blob foo 64)" &&
+ bar="$(generate_random_blob bar 64)" &&
+ foo_pack="$(printf "%s\n" $foo $bar | git pack-objects $packdir/pack)" &&
+ git prune-packed &&
+
+ test-tool chmtime --get -10 \
+ "$packdir/pack-$foo_pack.pack" >foo.mtime &&
+
+ git repack --cruft -d &&
+
+ cruft="$(ls $packdir/pack-*.mtimes)" &&
+ test-tool pack-mtimes "$(basename $cruft)" >actual &&
+ echo "$foo $(cat foo.mtime)" >expect.raw &&
+ echo "$bar $(cat foo.mtime)" >>expect.raw &&
+ sort expect.raw >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success '--max-cruft-size with pruning' '
+ git init max-cruft-size-prune &&
+ (
+ cd max-cruft-size-prune &&
+
+ test_commit base &&
+ foo="$(generate_random_blob foo $((1024*1024)))" &&
+ bar="$(generate_random_blob bar $((1024*1024)))" &&
+ baz="$(generate_random_blob baz $((1024*1024)))" &&
+
+ test-tool chmtime -10000 "$objdir/$(test_oid_to_path "$foo")" &&
+
+ git repack -d --cruft --max-cruft-size=1M &&
+
+ # backdate the mtimes of all cruft packs to validate
+ # that they were rewritten as a result of pruning
+ ls $packdir/pack-*.mtimes | sort >cruft.before &&
+ for cruft in $(cat cruft.before)
+ do
+ mtime="$(test-tool chmtime --get -10000 "$cruft")" &&
+ echo $cruft $mtime >>mtimes || return 1
+ done &&
+
+ # repack (and prune) with a --max-cruft-size to ensure
+ # that we appropriately split the resulting set of packs
+ git repack -d --cruft --max-cruft-size=1M \
+ --cruft-expiration=10.seconds.ago &&
+ ls $packdir/pack-*.mtimes | sort >cruft.after &&
+
+ for cruft in $(cat cruft.after)
+ do
+ old_mtime="$(grep $cruft mtimes | cut -d" " -f2)" &&
+ new_mtime="$(test-tool chmtime --get $cruft)" &&
+ test $old_mtime -lt $new_mtime || return 1
+ done &&
+
+ test_line_count = 3 cruft.before &&
+ test_line_count = 2 cruft.after &&
+ test_must_fail git cat-file -e $foo &&
+ git cat-file -e $bar &&
+ git cat-file -e $baz
+ )
+'
+
+test_expect_success '--max-cruft-size ignores non-local packs' '
+ repo="max-cruft-size-non-local" &&
+ git init $repo &&
+ (
+ cd $repo &&
+ test_commit base &&
+ generate_random_blob foo 64 &&
+ git repack --cruft -d
+ ) &&
+
+ git clone --reference=$repo $repo $repo-alt &&
+ (
+ cd $repo-alt &&
+
+ test_commit other &&
+ generate_random_blob bar 64 &&
+
+ # ensure that we do not attempt to pick up packs from
+ # the non-alternated repository, which would result in a
+ # crash
+ git repack --cruft --max-cruft-size=1M -d
+ )
+'
+
+test_expect_success 'reachable packs are preferred over cruft ones' '
+ repo="cruft-preferred-packs" &&
+ git init "$repo" &&
+ (
+ cd "$repo" &&
+
+ # This test needs to exercise careful control over when a MIDX
+ # is and is not written. Unset the corresponding TEST variable
+ # accordingly.
+ sane_unset GIT_TEST_MULTI_PACK_INDEX &&
+
+ test_commit base &&
+ test_commit --no-tag cruft &&
+
+ non_cruft="$(echo base | git pack-objects --revs $packdir/pack)" &&
+ # Write a cruft pack which both (a) sorts ahead of the non-cruft
+ # pack in lexical order, and (b) has an older mtime to appease
+ # the MIDX preferred pack selection routine.
+ cruft="$(echo pack-$non_cruft.pack | git pack-objects --cruft $packdir/pack-A)" &&
+ test-tool chmtime -1000 $packdir/pack-A-$cruft.pack &&
+
+ test_commit other &&
+ git repack -d &&
+
+ git repack --geometric 2 -d --write-midx --write-bitmap-index &&
+
+ # After repacking, there are two packs left: one reachable one
+ # (which is the result of combining both of the existing two
+ # non-cruft packs), and one cruft pack.
+ find .git/objects/pack -type f -name "*.pack" >packs &&
+ test_line_count = 2 packs &&
+
+ # Make sure that the pack we just wrote is marked as preferred,
+ # not the cruft one.
+ pack="$(test-tool read-midx --preferred-pack $objdir)" &&
+ test_path_is_missing "$packdir/$(basename "$pack" ".idx").mtimes"
+ )
+'
+
+test_done
diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh
index a173f56..cc917b2 100755
--- a/t/t7800-difftool.sh
+++ b/t/t7800-difftool.sh
@@ -28,14 +28,14 @@ prompt_given ()
test_expect_success 'basic usage requires no repo' '
test_expect_code 129 git difftool -h >output &&
- test_i18ngrep ^usage: output &&
+ test_grep ^usage: output &&
# create a ceiling directory to prevent Git from finding a repo
mkdir -p not/repo &&
test_when_finished rm -r not &&
test_expect_code 129 \
env GIT_CEILING_DIRECTORIES="$(pwd)/not" \
git -C not/repo difftool -h >output &&
- test_i18ngrep ^usage: output
+ test_grep ^usage: output
'
# Create a file on main and change it on branch
@@ -91,67 +91,128 @@ test_expect_success 'difftool forwards arguments to diff' '
rm for-diff
'
-test_expect_success 'difftool ignores exit code' '
- test_config difftool.error.cmd false &&
- git difftool -y -t error branch
-'
+for opt in '' '--dir-diff'
+do
+ test_expect_success "difftool ${opt:-without options} ignores exit code" '
+ test_config difftool.error.cmd false &&
+ git difftool ${opt} -y -t error branch
+ '
+
+ test_expect_success "difftool ${opt:-without options} forwards exit code with --trust-exit-code" '
+ test_config difftool.error.cmd false &&
+ test_must_fail git difftool ${opt} -y --trust-exit-code -t error branch
+ '
+
+ test_expect_success "difftool ${opt:-without options} forwards exit code with --trust-exit-code for built-ins" '
+ test_config difftool.vimdiff.path false &&
+ test_must_fail git difftool ${opt} -y --trust-exit-code -t vimdiff branch
+ '
+
+ test_expect_success "difftool ${opt:-without options} honors difftool.trustExitCode = true" '
+ test_config difftool.error.cmd false &&
+ test_config difftool.trustExitCode true &&
+ test_must_fail git difftool ${opt} -y -t error branch
+ '
+
+ test_expect_success "difftool ${opt:-without options} honors difftool.trustExitCode = false" '
+ test_config difftool.error.cmd false &&
+ test_config difftool.trustExitCode false &&
+ git difftool ${opt} -y -t error branch
+ '
+
+ test_expect_success "difftool ${opt:-without options} ignores exit code with --no-trust-exit-code" '
+ test_config difftool.error.cmd false &&
+ test_config difftool.trustExitCode true &&
+ git difftool ${opt} -y --no-trust-exit-code -t error branch
+ '
+
+ test_expect_success "difftool ${opt:-without options} stops on error with --trust-exit-code" '
+ test_when_finished "rm -f for-diff .git/fail-right-file" &&
+ test_when_finished "git reset -- for-diff" &&
+ write_script .git/fail-right-file <<-\EOF &&
+ echo failed
+ exit 1
+ EOF
+ >for-diff &&
+ git add for-diff &&
+ test_must_fail git difftool ${opt} -y --trust-exit-code \
+ --extcmd .git/fail-right-file branch >actual &&
+ test_line_count = 1 actual
+ '
+
+ test_expect_success "difftool ${opt:-without options} honors exit status if command not found" '
+ test_config difftool.nonexistent.cmd i-dont-exist &&
+ test_config difftool.trustExitCode false &&
+ if test "${opt}" = --dir-diff
+ then
+ expected_code=127
+ else
+ expected_code=128
+ fi &&
+ test_expect_code ${expected_code} git difftool ${opt} -y -t nonexistent branch
+ '
+done
-test_expect_success 'difftool forwards exit code with --trust-exit-code' '
- test_config difftool.error.cmd false &&
- test_must_fail git difftool -y --trust-exit-code -t error branch
-'
+test_expect_success 'difftool honors --gui' '
+ difftool_test_setup &&
+ test_config merge.tool bogus-tool &&
+ test_config diff.tool bogus-tool &&
+ test_config diff.guitool test-tool &&
-test_expect_success 'difftool forwards exit code with --trust-exit-code for built-ins' '
- test_config difftool.vimdiff.path false &&
- test_must_fail git difftool -y --trust-exit-code -t vimdiff branch
+ echo branch >expect &&
+ git difftool --no-prompt --gui branch >actual &&
+ test_cmp expect actual
'
-test_expect_success 'difftool honors difftool.trustExitCode = true' '
- test_config difftool.error.cmd false &&
- test_config difftool.trustExitCode true &&
- test_must_fail git difftool -y -t error branch
-'
+test_expect_success 'difftool with guiDefault auto selects gui tool when there is DISPLAY' '
+ difftool_test_setup &&
+ test_config merge.tool bogus-tool &&
+ test_config diff.tool bogus-tool &&
+ test_config diff.guitool test-tool &&
+ test_config difftool.guiDefault auto &&
+ DISPLAY=SOMETHING && export DISPLAY &&
-test_expect_success 'difftool honors difftool.trustExitCode = false' '
- test_config difftool.error.cmd false &&
- test_config difftool.trustExitCode false &&
- git difftool -y -t error branch
+ echo branch >expect &&
+ git difftool --no-prompt branch >actual &&
+ test_cmp expect actual
'
+test_expect_success 'difftool with guiDefault auto selects regular tool when no DISPLAY' '
+ difftool_test_setup &&
+ test_config diff.guitool bogus-tool &&
+ test_config diff.tool test-tool &&
+ test_config difftool.guiDefault Auto &&
+ DISPLAY= && export DISPLAY &&
-test_expect_success 'difftool ignores exit code with --no-trust-exit-code' '
- test_config difftool.error.cmd false &&
- test_config difftool.trustExitCode true &&
- git difftool -y --no-trust-exit-code -t error branch
+ echo branch >expect &&
+ git difftool --no-prompt branch >actual &&
+ test_cmp expect actual
'
-test_expect_success 'difftool stops on error with --trust-exit-code' '
- test_when_finished "rm -f for-diff .git/fail-right-file" &&
- test_when_finished "git reset -- for-diff" &&
- write_script .git/fail-right-file <<-\EOF &&
- echo failed
- exit 1
- EOF
- >for-diff &&
- git add for-diff &&
- test_must_fail git difftool -y --trust-exit-code \
- --extcmd .git/fail-right-file branch >actual &&
- test_line_count = 1 actual
-'
+test_expect_success 'difftool with guiDefault true selects gui tool' '
+ difftool_test_setup &&
+ test_config diff.tool bogus-tool &&
+ test_config diff.guitool test-tool &&
+ test_config difftool.guiDefault true &&
+
+ DISPLAY= && export DISPLAY &&
+ echo branch >expect &&
+ git difftool --no-prompt branch >actual &&
+ test_cmp expect actual &&
-test_expect_success 'difftool honors exit status if command not found' '
- test_config difftool.nonexistent.cmd i-dont-exist &&
- test_config difftool.trustExitCode false &&
- test_must_fail git difftool -y -t nonexistent branch
+ DISPLAY=Something && export DISPLAY &&
+ echo branch >expect &&
+ git difftool --no-prompt branch >actual &&
+ test_cmp expect actual
'
-test_expect_success 'difftool honors --gui' '
+test_expect_success 'difftool --no-gui trumps config guiDefault' '
difftool_test_setup &&
- test_config merge.tool bogus-tool &&
- test_config diff.tool bogus-tool &&
- test_config diff.guitool test-tool &&
+ test_config diff.guitool bogus-tool &&
+ test_config diff.tool test-tool &&
+ test_config difftool.guiDefault true &&
echo branch >expect &&
- git difftool --no-prompt --gui branch >actual &&
+ git difftool --no-prompt --no-gui branch >actual &&
test_cmp expect actual
'
@@ -453,6 +514,13 @@ run_dir_diff_test 'difftool --dir-diff' '
grep "^file$" output
'
+run_dir_diff_test 'difftool --dir-diff avoids repeated slashes in TMPDIR' '
+ TMPDIR="${TMPDIR:-/tmp}////" \
+ git difftool --dir-diff $symlinks --extcmd echo branch >output &&
+ grep -v // output >actual &&
+ test_line_count = 1 actual
+'
+
run_dir_diff_test 'difftool --dir-diff ignores --prompt' '
git difftool --dir-diff $symlinks --prompt --extcmd ls branch >output &&
grep "^sub$" output &&
@@ -629,6 +697,7 @@ test_expect_success 'difftool --no-symlinks detects conflict ' '
test_expect_success 'difftool properly honors gitlink and core.worktree' '
test_when_finished rm -rf submod/ule &&
+ test_config_global protocol.file.allow always &&
git submodule add ./. submod/ule &&
test_config -C submod/ule diff.tool checktrees &&
test_config -C submod/ule difftool.checktrees.cmd '\''
@@ -674,7 +743,6 @@ test_expect_success SYMLINKS 'difftool --dir-diff handles modified symlinks' '
rm c &&
ln -s d c &&
cat >expect <<-EOF &&
- b
c
c
@@ -710,7 +778,6 @@ test_expect_success SYMLINKS 'difftool --dir-diff handles modified symlinks' '
# Deleted symlinks
rm -f c &&
cat >expect <<-EOF &&
- b
c
EOF
@@ -723,6 +790,71 @@ test_expect_success SYMLINKS 'difftool --dir-diff handles modified symlinks' '
test_cmp expect actual
'
+test_expect_success SYMLINKS 'difftool --dir-diff writes symlinks as raw text' '
+ # Start out on a branch called "branch-init".
+ git init -b branch-init symlink-files &&
+ (
+ cd symlink-files &&
+ # This test ensures that symlinks are written as raw text.
+ # The "cat" tools output link and file contents.
+ git config difftool.cat-left-link.cmd "cat \"\$LOCAL/link\"" &&
+ git config difftool.cat-left-a.cmd "cat \"\$LOCAL/file-a\"" &&
+ git config difftool.cat-right-link.cmd "cat \"\$REMOTE/link\"" &&
+ git config difftool.cat-right-b.cmd "cat \"\$REMOTE/file-b\"" &&
+
+ # Record the empty initial state so that we can come back here
+ # later and not have to consider the any cases where difftool
+ # will create symlinks back into the worktree.
+ test_tick &&
+ git commit --allow-empty -m init &&
+
+ # Create a file called "file-a" with a symlink pointing to it.
+ git switch -c branch-a &&
+ echo a >file-a &&
+ ln -s file-a link &&
+ git add file-a link &&
+ test_tick &&
+ git commit -m link-to-file-a &&
+
+ # Create a file called "file-b" and point the symlink to it.
+ git switch -c branch-b &&
+ echo b >file-b &&
+ rm link &&
+ ln -s file-b link &&
+ git add file-b link &&
+ git rm file-a &&
+ test_tick &&
+ git commit -m link-to-file-b &&
+
+ # Checkout the initial branch so that the --symlinks behavior is
+ # not activated. The two directories should be completely
+ # independent with no symlinks pointing back here.
+ git switch branch-init &&
+
+ # The left link must be "file-a" and "file-a" must contain "a".
+ echo file-a >expect &&
+ git difftool --symlinks --dir-diff --tool cat-left-link \
+ branch-a branch-b >actual &&
+ test_cmp expect actual &&
+
+ echo a >expect &&
+ git difftool --symlinks --dir-diff --tool cat-left-a \
+ branch-a branch-b >actual &&
+ test_cmp expect actual &&
+
+ # The right link must be "file-b" and "file-b" must contain "b".
+ echo file-b >expect &&
+ git difftool --symlinks --dir-diff --tool cat-right-link \
+ branch-a branch-b >actual &&
+ test_cmp expect actual &&
+
+ echo b >expect &&
+ git difftool --symlinks --dir-diff --tool cat-right-b \
+ branch-a branch-b >actual &&
+ test_cmp expect actual
+ )
+'
+
test_expect_success 'add -N and difftool -d' '
test_when_finished git reset --hard &&
diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh
index 6b6423a..875dcfd 100755
--- a/t/t7810-grep.sh
+++ b/t/t7810-grep.sh
@@ -18,6 +18,9 @@ test_invalid_grep_expression() {
'
}
+LC_ALL=en_US.UTF-8 test-tool regex '^.$' '¿' &&
+ test_set_prereq MB_REGEX
+
cat >hello.c <<EOF
#include <assert.h>
#include <stdio.h>
@@ -31,28 +34,28 @@ int main(int argc, const char **argv)
EOF
test_expect_success setup '
- {
- echo foo mmap bar
- echo foo_mmap bar
- echo foo_mmap bar mmap
- echo foo mmap bar_mmap
- echo foo_mmap bar mmap baz
- } >file &&
- {
- echo Hello world
- echo HeLLo world
- echo Hello_world
- echo HeLLo_world
- } >hello_world &&
- {
- echo "a+b*c"
- echo "a+bc"
- echo "abc"
- } >ab &&
- {
- echo d &&
- echo 0
- } >d0 &&
+ cat >file <<-\EOF &&
+ foo mmap bar
+ foo_mmap bar
+ foo_mmap bar mmap
+ foo mmap bar_mmap
+ foo_mmap bar mmap baz
+ EOF
+ cat >hello_world <<-\EOF &&
+ Hello world
+ HeLLo world
+ Hello_world
+ HeLLo_world
+ EOF
+ cat >ab <<-\EOF &&
+ a+b*c
+ a+bc
+ abc
+ EOF
+ cat >d0 <<-\EOF &&
+ d
+ 0
+ EOF
echo vvv >v &&
echo ww w >w &&
echo x x xx x >x &&
@@ -63,13 +66,13 @@ test_expect_success setup '
echo vvv >t/v &&
mkdir t/a &&
echo vvv >t/a/v &&
- {
- echo "line without leading space1"
- echo " line with leading space1"
- echo " line with leading space2"
- echo " line with leading space3"
- echo "line without leading space2"
- } >space &&
+ qz_to_tab_space >space <<-\EOF &&
+ line without leading space1
+ Zline with leading space1
+ Zline with leading space2
+ Zline with leading space3
+ line without leading space2
+ EOF
cat >hello.ps1 <<-\EOF &&
# No-op.
function dummy() {}
@@ -77,6 +80,7 @@ test_expect_success setup '
# Say hello.
function hello() {
echo "Hello world."
+ echo "Hello again."
} # hello
# Still a no-op.
@@ -87,6 +91,10 @@ test_expect_success setup '
echo unusual >"\"unusual\" pathname" &&
echo unusual >"t/nested \"unusual\" pathname"
fi &&
+ if test_have_prereq MB_REGEX
+ then
+ echo "¿" >reverse-question-mark
+ fi &&
git add . &&
test_tick &&
git commit -m initial
@@ -98,6 +106,37 @@ test_expect_success 'grep should not segfault with a bad input' '
test_invalid_grep_expression --and -e A
+test_pattern_type () {
+ H=$1 &&
+ HC=$2 &&
+ L=$3 &&
+ type=$4 &&
+ shift 4 &&
+
+ expected_str= &&
+ case "$type" in
+ BRE)
+ expected_str="${HC}ab:a+bc"
+ ;;
+ ERE)
+ expected_str="${HC}ab:abc"
+ ;;
+ FIX)
+ expected_str="${HC}ab:a+b*c"
+ ;;
+ *)
+ BUG "unknown pattern type '$type'"
+ ;;
+ esac &&
+ config_str="$@" &&
+
+ test_expect_success "grep $L with '$config_str' interpreted as $type" '
+ echo $expected_str >expected &&
+ git $config_str grep "a+b*c" $H ab >actual &&
+ test_cmp expected actual
+ '
+}
+
for H in HEAD ''
do
case "$H" in
@@ -106,129 +145,129 @@ do
esac
test_expect_success "grep -w $L" '
- {
- echo ${HC}file:1:foo mmap bar
- echo ${HC}file:3:foo_mmap bar mmap
- echo ${HC}file:4:foo mmap bar_mmap
- echo ${HC}file:5:foo_mmap bar mmap baz
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}file:1:foo mmap bar
+ ${HC}file:3:foo_mmap bar mmap
+ ${HC}file:4:foo mmap bar_mmap
+ ${HC}file:5:foo_mmap bar mmap baz
+ EOF
git -c grep.linenumber=false grep -n -w -e mmap $H >actual &&
test_cmp expected actual
'
test_expect_success "grep -w $L (with --column)" '
- {
- echo ${HC}file:5:foo mmap bar
- echo ${HC}file:14:foo_mmap bar mmap
- echo ${HC}file:5:foo mmap bar_mmap
- echo ${HC}file:14:foo_mmap bar mmap baz
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}file:5:foo mmap bar
+ ${HC}file:14:foo_mmap bar mmap
+ ${HC}file:5:foo mmap bar_mmap
+ ${HC}file:14:foo_mmap bar mmap baz
+ EOF
git grep --column -w -e mmap $H >actual &&
test_cmp expected actual
'
test_expect_success "grep -w $L (with --column, extended OR)" '
- {
- echo ${HC}file:14:foo_mmap bar mmap
- echo ${HC}file:19:foo_mmap bar mmap baz
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}file:14:foo_mmap bar mmap
+ ${HC}file:19:foo_mmap bar mmap baz
+ EOF
git grep --column -w -e mmap$ --or -e baz $H >actual &&
test_cmp expected actual
'
test_expect_success "grep -w $L (with --column, --invert-match)" '
- {
- echo ${HC}file:1:foo mmap bar
- echo ${HC}file:1:foo_mmap bar
- echo ${HC}file:1:foo_mmap bar mmap
- echo ${HC}file:1:foo mmap bar_mmap
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}file:1:foo mmap bar
+ ${HC}file:1:foo_mmap bar
+ ${HC}file:1:foo_mmap bar mmap
+ ${HC}file:1:foo mmap bar_mmap
+ EOF
git grep --column --invert-match -w -e baz $H -- file >actual &&
test_cmp expected actual
'
test_expect_success "grep $L (with --column, --invert-match, extended OR)" '
- {
- echo ${HC}hello_world:6:HeLLo_world
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}hello_world:6:HeLLo_world
+ EOF
git grep --column --invert-match -e ll --or --not -e _ $H -- hello_world \
>actual &&
test_cmp expected actual
'
test_expect_success "grep $L (with --column, --invert-match, extended AND)" '
- {
- echo ${HC}hello_world:3:Hello world
- echo ${HC}hello_world:3:Hello_world
- echo ${HC}hello_world:6:HeLLo_world
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}hello_world:3:Hello world
+ ${HC}hello_world:3:Hello_world
+ ${HC}hello_world:6:HeLLo_world
+ EOF
git grep --column --invert-match --not -e _ --and --not -e ll $H -- hello_world \
>actual &&
test_cmp expected actual
'
test_expect_success "grep $L (with --column, double-negation)" '
- {
- echo ${HC}file:1:foo_mmap bar mmap baz
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}file:1:foo_mmap bar mmap baz
+ EOF
git grep --column --not \( --not -e foo --or --not -e baz \) $H -- file \
>actual &&
test_cmp expected actual
'
test_expect_success "grep -w $L (with --column, -C)" '
- {
- echo ${HC}file:5:foo mmap bar
- echo ${HC}file-foo_mmap bar
- echo ${HC}file:14:foo_mmap bar mmap
- echo ${HC}file:5:foo mmap bar_mmap
- echo ${HC}file:14:foo_mmap bar mmap baz
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}file:5:foo mmap bar
+ ${HC}file-foo_mmap bar
+ ${HC}file:14:foo_mmap bar mmap
+ ${HC}file:5:foo mmap bar_mmap
+ ${HC}file:14:foo_mmap bar mmap baz
+ EOF
git grep --column -w -C1 -e mmap $H >actual &&
test_cmp expected actual
'
test_expect_success "grep -w $L (with --line-number, --column)" '
- {
- echo ${HC}file:1:5:foo mmap bar
- echo ${HC}file:3:14:foo_mmap bar mmap
- echo ${HC}file:4:5:foo mmap bar_mmap
- echo ${HC}file:5:14:foo_mmap bar mmap baz
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}file:1:5:foo mmap bar
+ ${HC}file:3:14:foo_mmap bar mmap
+ ${HC}file:4:5:foo mmap bar_mmap
+ ${HC}file:5:14:foo_mmap bar mmap baz
+ EOF
git grep -n --column -w -e mmap $H >actual &&
test_cmp expected actual
'
test_expect_success "grep -w $L (with non-extended patterns, --column)" '
- {
- echo ${HC}file:5:foo mmap bar
- echo ${HC}file:10:foo_mmap bar
- echo ${HC}file:10:foo_mmap bar mmap
- echo ${HC}file:5:foo mmap bar_mmap
- echo ${HC}file:10:foo_mmap bar mmap baz
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}file:5:foo mmap bar
+ ${HC}file:10:foo_mmap bar
+ ${HC}file:10:foo_mmap bar mmap
+ ${HC}file:5:foo mmap bar_mmap
+ ${HC}file:10:foo_mmap bar mmap baz
+ EOF
git grep --column -w -e bar -e mmap $H >actual &&
test_cmp expected actual
'
test_expect_success "grep -w $L" '
- {
- echo ${HC}file:1:foo mmap bar
- echo ${HC}file:3:foo_mmap bar mmap
- echo ${HC}file:4:foo mmap bar_mmap
- echo ${HC}file:5:foo_mmap bar mmap baz
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}file:1:foo mmap bar
+ ${HC}file:3:foo_mmap bar mmap
+ ${HC}file:4:foo mmap bar_mmap
+ ${HC}file:5:foo_mmap bar mmap baz
+ EOF
git -c grep.linenumber=true grep -w -e mmap $H >actual &&
test_cmp expected actual
'
test_expect_success "grep -w $L" '
- {
- echo ${HC}file:foo mmap bar
- echo ${HC}file:foo_mmap bar mmap
- echo ${HC}file:foo mmap bar_mmap
- echo ${HC}file:foo_mmap bar mmap baz
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}file:foo mmap bar
+ ${HC}file:foo_mmap bar mmap
+ ${HC}file:foo mmap bar_mmap
+ ${HC}file:foo_mmap bar mmap baz
+ EOF
git -c grep.linenumber=true grep --no-line-number -w -e mmap $H >actual &&
test_cmp expected actual
'
@@ -239,17 +278,17 @@ do
'
test_expect_success "grep -w $L (x)" '
- {
- echo ${HC}x:1:x x xx x
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}x:1:x x xx x
+ EOF
git grep -n -w -e "x xx* x" $H >actual &&
test_cmp expected actual
'
test_expect_success "grep -w $L (y-1)" '
- {
- echo ${HC}y:1:y yy
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}y:1:y yy
+ EOF
git grep -n -w -e "^y" $H >actual &&
test_cmp expected actual
'
@@ -277,16 +316,16 @@ do
'
test_expect_success "grep $L (with --column, --only-matching)" '
- {
- echo ${HC}file:1:5:mmap
- echo ${HC}file:2:5:mmap
- echo ${HC}file:3:5:mmap
- echo ${HC}file:3:13:mmap
- echo ${HC}file:4:5:mmap
- echo ${HC}file:4:13:mmap
- echo ${HC}file:5:5:mmap
- echo ${HC}file:5:13:mmap
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}file:1:5:mmap
+ ${HC}file:2:5:mmap
+ ${HC}file:3:5:mmap
+ ${HC}file:3:13:mmap
+ ${HC}file:4:5:mmap
+ ${HC}file:4:13:mmap
+ ${HC}file:5:5:mmap
+ ${HC}file:5:13:mmap
+ EOF
git grep --column -n -o -e mmap $H >actual &&
test_cmp expected actual
'
@@ -320,11 +359,11 @@ do
'
test_expect_success "grep --max-depth -1 $L" '
- {
- echo ${HC}t/a/v:1:vvv
- echo ${HC}t/v:1:vvv
- echo ${HC}v:1:vvv
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}t/a/v:1:vvv
+ ${HC}t/v:1:vvv
+ ${HC}v:1:vvv
+ EOF
git grep --max-depth -1 -n -e vvv $H >actual &&
test_cmp expected actual &&
git grep --recursive -n -e vvv $H >actual &&
@@ -332,9 +371,9 @@ do
'
test_expect_success "grep --max-depth 0 $L" '
- {
- echo ${HC}v:1:vvv
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}v:1:vvv
+ EOF
git grep --max-depth 0 -n -e vvv $H >actual &&
test_cmp expected actual &&
git grep --no-recursive -n -e vvv $H >actual &&
@@ -342,11 +381,11 @@ do
'
test_expect_success "grep --max-depth 0 -- '*' $L" '
- {
- echo ${HC}t/a/v:1:vvv
- echo ${HC}t/v:1:vvv
- echo ${HC}v:1:vvv
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}t/a/v:1:vvv
+ ${HC}t/v:1:vvv
+ ${HC}v:1:vvv
+ EOF
git grep --max-depth 0 -n -e vvv $H -- "*" >actual &&
test_cmp expected actual &&
git grep --no-recursive -n -e vvv $H -- "*" >actual &&
@@ -354,18 +393,18 @@ do
'
test_expect_success "grep --max-depth 1 $L" '
- {
- echo ${HC}t/v:1:vvv
- echo ${HC}v:1:vvv
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}t/v:1:vvv
+ ${HC}v:1:vvv
+ EOF
git grep --max-depth 1 -n -e vvv $H >actual &&
test_cmp expected actual
'
test_expect_success "grep --max-depth 0 -- t $L" '
- {
- echo ${HC}t/v:1:vvv
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}t/v:1:vvv
+ EOF
git grep --max-depth 0 -n -e vvv $H -- t >actual &&
test_cmp expected actual &&
git grep --no-recursive -n -e vvv $H -- t >actual &&
@@ -373,10 +412,10 @@ do
'
test_expect_success "grep --max-depth 0 -- . t $L" '
- {
- echo ${HC}t/v:1:vvv
- echo ${HC}v:1:vvv
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}t/v:1:vvv
+ ${HC}v:1:vvv
+ EOF
git grep --max-depth 0 -n -e vvv $H -- . t >actual &&
test_cmp expected actual &&
git grep --no-recursive -n -e vvv $H -- . t >actual &&
@@ -384,44 +423,22 @@ do
'
test_expect_success "grep --max-depth 0 -- t . $L" '
- {
- echo ${HC}t/v:1:vvv
- echo ${HC}v:1:vvv
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}t/v:1:vvv
+ ${HC}v:1:vvv
+ EOF
git grep --max-depth 0 -n -e vvv $H -- t . >actual &&
test_cmp expected actual &&
git grep --no-recursive -n -e vvv $H -- t . >actual &&
test_cmp expected actual
'
- test_expect_success "grep $L with grep.extendedRegexp=false" '
- echo "${HC}ab:a+bc" >expected &&
- git -c grep.extendedRegexp=false grep "a+b*c" $H ab >actual &&
- test_cmp expected actual
- '
-
- test_expect_success "grep $L with grep.extendedRegexp=true" '
- echo "${HC}ab:abc" >expected &&
- git -c grep.extendedRegexp=true grep "a+b*c" $H ab >actual &&
- test_cmp expected actual
- '
- test_expect_success "grep $L with grep.patterntype=basic" '
- echo "${HC}ab:a+bc" >expected &&
- git -c grep.patterntype=basic grep "a+b*c" $H ab >actual &&
- test_cmp expected actual
- '
- test_expect_success "grep $L with grep.patterntype=extended" '
- echo "${HC}ab:abc" >expected &&
- git -c grep.patterntype=extended grep "a+b*c" $H ab >actual &&
- test_cmp expected actual
- '
-
- test_expect_success "grep $L with grep.patterntype=fixed" '
- echo "${HC}ab:a+b*c" >expected &&
- git -c grep.patterntype=fixed grep "a+b*c" $H ab >actual &&
- test_cmp expected actual
- '
+ test_pattern_type "$H" "$HC" "$L" BRE -c grep.extendedRegexp=false
+ test_pattern_type "$H" "$HC" "$L" ERE -c grep.extendedRegexp=true
+ test_pattern_type "$H" "$HC" "$L" BRE -c grep.patternType=basic
+ test_pattern_type "$H" "$HC" "$L" ERE -c grep.patternType=extended
+ test_pattern_type "$H" "$HC" "$L" FIX -c grep.patternType=fixed
test_expect_success PCRE "grep $L with grep.patterntype=perl" '
echo "${HC}ab:a+b*c" >expected &&
@@ -433,59 +450,76 @@ do
test_must_fail git -c grep.patterntype=perl grep "foo.*bar"
'
- test_expect_success "grep $L with grep.patternType=default and grep.extendedRegexp=true" '
- echo "${HC}ab:abc" >expected &&
- git \
- -c grep.patternType=default \
- -c grep.extendedRegexp=true \
- grep "a+b*c" $H ab >actual &&
- test_cmp expected actual
- '
-
- test_expect_success "grep $L with grep.extendedRegexp=true and grep.patternType=default" '
- echo "${HC}ab:abc" >expected &&
- git \
- -c grep.extendedRegexp=true \
- -c grep.patternType=default \
- grep "a+b*c" $H ab >actual &&
- test_cmp expected actual
- '
-
- test_expect_success "grep $L with grep.patternType=extended and grep.extendedRegexp=false" '
- echo "${HC}ab:abc" >expected &&
- git \
- -c grep.patternType=extended \
- -c grep.extendedRegexp=false \
- grep "a+b*c" $H ab >actual &&
- test_cmp expected actual
- '
-
- test_expect_success "grep $L with grep.patternType=basic and grep.extendedRegexp=true" '
- echo "${HC}ab:a+bc" >expected &&
- git \
- -c grep.patternType=basic \
- -c grep.extendedRegexp=true \
- grep "a+b*c" $H ab >actual &&
- test_cmp expected actual
- '
-
- test_expect_success "grep $L with grep.extendedRegexp=false and grep.patternType=extended" '
- echo "${HC}ab:abc" >expected &&
- git \
- -c grep.extendedRegexp=false \
- -c grep.patternType=extended \
- grep "a+b*c" $H ab >actual &&
- test_cmp expected actual
- '
-
- test_expect_success "grep $L with grep.extendedRegexp=true and grep.patternType=basic" '
- echo "${HC}ab:a+bc" >expected &&
- git \
- -c grep.extendedRegexp=true \
- -c grep.patternType=basic \
- grep "a+b*c" $H ab >actual &&
- test_cmp expected actual
- '
+ test_pattern_type "$H" "$HC" "$L" ERE \
+ -c grep.patternType=default \
+ -c grep.extendedRegexp=true
+ test_pattern_type "$H" "$HC" "$L" ERE \
+ -c grep.extendedRegexp=true \
+ -c grep.patternType=default
+ test_pattern_type "$H" "$HC" "$L" ERE \
+ -c grep.patternType=extended \
+ -c grep.extendedRegexp=false
+ test_pattern_type "$H" "$HC" "$L" BRE \
+ -c grep.patternType=basic \
+ -c grep.extendedRegexp=true
+ test_pattern_type "$H" "$HC" "$L" ERE \
+ -c grep.extendedRegexp=false \
+ -c grep.patternType=extended
+ test_pattern_type "$H" "$HC" "$L" BRE \
+ -c grep.extendedRegexp=true \
+ -c grep.patternType=basic
+
+ # grep.extendedRegexp is last-one-wins
+ test_pattern_type "$H" "$HC" "$L" BRE \
+ -c grep.extendedRegexp=true \
+ -c grep.extendedRegexp=false
+
+ # grep.patternType=basic pays no attention to grep.extendedRegexp
+ test_pattern_type "$H" "$HC" "$L" BRE \
+ -c grep.extendedRegexp=true \
+ -c grep.patternType=basic \
+ -c grep.extendedRegexp=false
+
+ # grep.patternType=extended pays no attention to grep.extendedRegexp
+ test_pattern_type "$H" "$HC" "$L" ERE \
+ -c grep.extendedRegexp=true \
+ -c grep.patternType=extended \
+ -c grep.extendedRegexp=false
+
+ # grep.extendedRegexp is used with a last-one-wins grep.patternType=default
+ test_pattern_type "$H" "$HC" "$L" ERE \
+ -c grep.patternType=fixed \
+ -c grep.extendedRegexp=true \
+ -c grep.patternType=default
+
+ # grep.extendedRegexp is used with earlier grep.patternType=default
+ test_pattern_type "$H" "$HC" "$L" ERE \
+ -c grep.extendedRegexp=false \
+ -c grep.patternType=default \
+ -c grep.extendedRegexp=true
+
+ # grep.extendedRegexp is used with a last-one-loses grep.patternType=default
+ test_pattern_type "$H" "$HC" "$L" ERE \
+ -c grep.extendedRegexp=false \
+ -c grep.extendedRegexp=true \
+ -c grep.patternType=default
+
+ # grep.extendedRegexp and grep.patternType are both last-one-wins independently
+ test_pattern_type "$H" "$HC" "$L" BRE \
+ -c grep.patternType=default \
+ -c grep.extendedRegexp=true \
+ -c grep.patternType=basic
+
+ # grep.patternType=extended and grep.patternType=default
+ test_pattern_type "$H" "$HC" "$L" BRE \
+ -c grep.patternType=extended \
+ -c grep.patternType=default
+
+ # grep.patternType=[extended -> default -> fixed] (BRE)" '
+ test_pattern_type "$H" "$HC" "$L" FIX \
+ -c grep.patternType=extended \
+ -c grep.patternType=default \
+ -c grep.patternType=fixed
test_expect_success "grep --count $L" '
echo ${HC}ab:3 >expected &&
@@ -542,6 +576,14 @@ do
'
done
+test_expect_success MB_REGEX 'grep exactly one char in single-char multibyte file' '
+ LC_ALL=en_US.UTF-8 git grep "^.$" reverse-question-mark
+'
+
+test_expect_success MB_REGEX 'grep two chars in single-char multibyte file' '
+ LC_ALL=en_US.UTF-8 test_expect_code 1 git grep ".." reverse-question-mark
+'
+
cat >expected <<EOF
file
EOF
@@ -569,6 +611,92 @@ test_expect_success 'grep --files-without-match --quiet' '
test_must_be_empty actual
'
+test_expect_success 'grep --max-count 0 (must exit with non-zero)' '
+ test_must_fail git grep --max-count 0 foo >actual &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'grep --max-count 3' '
+ cat >expected <<-EOF &&
+ file:foo mmap bar
+ file:foo_mmap bar
+ file:foo_mmap bar mmap
+ EOF
+ git grep --max-count 3 foo >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'grep --max-count -1 (no limit)' '
+ cat >expected <<-EOF &&
+ file:foo mmap bar
+ file:foo_mmap bar
+ file:foo_mmap bar mmap
+ file:foo mmap bar_mmap
+ file:foo_mmap bar mmap baz
+ EOF
+ git grep --max-count -1 foo >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'grep --max-count 1 --context 2' '
+ cat >expected <<-EOF &&
+ file-foo mmap bar
+ file:foo_mmap bar
+ file-foo_mmap bar mmap
+ EOF
+ git grep --max-count 1 --context 1 foo_mmap >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'grep --max-count 1 --show-function' '
+ cat >expected <<-EOF &&
+ hello.ps1=function hello() {
+ hello.ps1: echo "Hello world."
+ EOF
+ git grep --max-count 1 --show-function Hello hello.ps1 >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'grep --max-count 2 --show-function' '
+ cat >expected <<-EOF &&
+ hello.ps1=function hello() {
+ hello.ps1: echo "Hello world."
+ hello.ps1: echo "Hello again."
+ EOF
+ git grep --max-count 2 --show-function Hello hello.ps1 >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'grep --max-count 1 --count' '
+ cat >expected <<-EOF &&
+ hello.ps1:1
+ EOF
+ git grep --max-count 1 --count Hello hello.ps1 >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'grep --max-count 1 (multiple files)' '
+ cat >expected <<-EOF &&
+ hello.c:#include <stdio.h>
+ hello.ps1:# No-op.
+ EOF
+ git grep --max-count 1 -e o -- hello.\* >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'grep --max-count 1 --context 1 (multiple files)' '
+ cat >expected <<-EOF &&
+ hello.c-#include <assert.h>
+ hello.c:#include <stdio.h>
+ hello.c-
+ --
+ hello.ps1:# No-op.
+ hello.ps1-function dummy() {}
+ EOF
+ git grep --max-count 1 --context 1 -e o -- hello.\* >actual &&
+ test_cmp expected actual
+'
+
cat >expected <<EOF
file:foo mmap bar_mmap
EOF
@@ -680,6 +808,19 @@ test_expect_success 'grep -f, ignore empty lines, read patterns from stdin' '
test_cmp expected actual
'
+test_expect_success 'grep -f, use cwd relative file' '
+ test_when_finished "git rm -f sub/dir/file" &&
+ mkdir -p sub/dir &&
+ echo hit >sub/dir/file &&
+ git add sub/dir/file &&
+ echo hit >sub/dir/pattern &&
+ echo miss >pattern &&
+ (
+ cd sub/dir && git grep -f pattern file
+ ) &&
+ git -C sub/dir grep -f pattern file
+'
+
cat >expected <<EOF
y:y yy
--
@@ -873,7 +1014,9 @@ test_expect_success 'log --committer does not search in timestamp' '
test_expect_success 'grep with CE_VALID file' '
git update-index --assume-unchanged t/t &&
rm t/t &&
- test "$(git grep test)" = "t/t:test" &&
+ echo "t/t:test" >expect &&
+ git grep test >actual &&
+ test_cmp expect actual &&
git update-index --no-assume-unchanged t/t &&
git checkout t/t
'
@@ -1104,6 +1247,33 @@ test_expect_success 'outside of git repository with fallbackToNoIndex' '
)
'
+test_expect_success 'no repository with path outside $cwd' '
+ test_when_finished rm -fr non &&
+ rm -fr non &&
+ mkdir -p non/git/sub non/tig &&
+ (
+ GIT_CEILING_DIRECTORIES="$(pwd)/non" &&
+ export GIT_CEILING_DIRECTORIES &&
+ cd non/git &&
+ test_expect_code 128 git grep --no-index search .. 2>error &&
+ grep "is outside the directory tree" error
+ ) &&
+ (
+ GIT_CEILING_DIRECTORIES="$(pwd)/non" &&
+ export GIT_CEILING_DIRECTORIES &&
+ cd non/git &&
+ test_expect_code 128 git grep --no-index search ../tig 2>error &&
+ grep "is outside the directory tree" error
+ ) &&
+ (
+ GIT_CEILING_DIRECTORIES="$(pwd)/non" &&
+ export GIT_CEILING_DIRECTORIES &&
+ cd non/git &&
+ test_expect_code 128 git grep --no-index search ../non 2>error &&
+ grep "no such path in the working tree" error
+ )
+'
+
test_expect_success 'inside git repository but with --no-index' '
rm -fr is &&
mkdir -p is/git/sub &&
@@ -1256,7 +1426,7 @@ test_expect_success 'grep --no-index pattern -- path' '
test_expect_success 'grep --no-index complains of revs' '
test_must_fail git grep --no-index o main -- 2>err &&
- test_i18ngrep "cannot be used with revs" err
+ test_grep "cannot be used with revs" err
'
test_expect_success 'grep --no-index prefers paths to revs' '
@@ -1269,7 +1439,7 @@ test_expect_success 'grep --no-index prefers paths to revs' '
test_expect_success 'grep --no-index does not "diagnose" revs' '
test_must_fail git grep --no-index o :1:hello.c 2>err &&
- test_i18ngrep ! -i "did you mean" err
+ test_grep ! -i "did you mean" err
'
cat >expected <<EOF
@@ -1314,10 +1484,10 @@ test_expect_success PCRE 'grep -P pattern with grep.extendedRegexp=true' '
'
test_expect_success PCRE 'grep -P -v pattern' '
- {
- echo "ab:a+b*c"
- echo "ab:a+bc"
- } >expected &&
+ cat >expected <<-\EOF &&
+ ab:a+b*c
+ ab:a+bc
+ EOF
git grep -P -v "abc" ab >actual &&
test_cmp expected actual
'
@@ -1331,10 +1501,10 @@ test_expect_success PCRE 'grep -P -i pattern' '
'
test_expect_success PCRE 'grep -P -w pattern' '
- {
- echo "hello_world:Hello world"
- echo "hello_world:HeLLo world"
- } >expected &&
+ cat >expected <<-\EOF &&
+ hello_world:Hello world
+ hello_world:HeLLo world
+ EOF
git grep -P -w "He((?i)ll)o" hello_world >actual &&
test_cmp expected actual
'
@@ -1469,10 +1639,10 @@ test_expect_success 'grep -F pattern with grep.patternType=basic' '
'
test_expect_success 'grep -G pattern with grep.patternType=fixed' '
- {
- echo "ab:a+b*c"
- echo "ab:a+bc"
- } >expected &&
+ cat >expected <<-\EOF &&
+ ab:a+b*c
+ ab:a+bc
+ EOF
git \
-c grep.patterntype=fixed \
grep -G "a+b" ab >actual &&
@@ -1480,11 +1650,11 @@ test_expect_success 'grep -G pattern with grep.patternType=fixed' '
'
test_expect_success 'grep -E pattern with grep.patternType=fixed' '
- {
- echo "ab:a+b*c"
- echo "ab:a+bc"
- echo "ab:abc"
- } >expected &&
+ cat >expected <<-\EOF &&
+ ab:a+b*c
+ ab:a+bc
+ ab:abc
+ EOF
git \
-c grep.patterntype=fixed \
grep -E "a+" ab >actual &&
diff --git a/t/t7811-grep-open.sh b/t/t7811-grep-open.sh
index a98785d..fe38d88 100755
--- a/t/t7811-grep-open.sh
+++ b/t/t7811-grep-open.sh
@@ -3,6 +3,7 @@
test_description='git grep --open-files-in-pager
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-pager.sh
unset PAGER GIT_PAGER
@@ -62,7 +63,7 @@ test_expect_success SIMPLEPAGER 'git grep -O' '
test_expect_success 'git grep -O --cached' '
test_must_fail git grep --cached -O GREP_PATTERN >out 2>msg &&
- test_i18ngrep open-files-in-pager msg
+ test_grep open-files-in-pager msg
'
test_expect_success 'git grep -O --no-index' '
@@ -114,8 +115,8 @@ test_expect_success 'modified file' '
unrelated
EOF
+ test_when_finished "git reset --hard" &&
echo "enum grep_pat_token" >unrelated &&
- test_when_finished "git checkout HEAD unrelated" &&
GIT_PAGER=./less git grep -F -O "enum grep_pat_token" >out &&
test_cmp expect actual &&
test_must_be_empty out
diff --git a/t/t7812-grep-icase-non-ascii.sh b/t/t7812-grep-icase-non-ascii.sh
index e5d1e4e..31c66b6 100755
--- a/t/t7812-grep-icase-non-ascii.sh
+++ b/t/t7812-grep-icase-non-ascii.sh
@@ -2,8 +2,13 @@
test_description='grep icase on non-English locales'
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-gettext.sh
+doalarm () {
+ perl -e 'alarm shift; exec @ARGV' -- "$@"
+}
+
test_expect_success GETTEXT_LOCALE 'setup' '
test_write_lines "TILRAUN: Halló Heimur!" >file &&
git add file &&
@@ -11,9 +16,19 @@ test_expect_success GETTEXT_LOCALE 'setup' '
export LC_ALL
'
-test_have_prereq GETTEXT_LOCALE &&
-test-tool regex "HALLÓ" "Halló" ICASE &&
-test_set_prereq REGEX_LOCALE
+test_expect_success GETTEXT_LOCALE 'setup REGEX_LOCALE prerequisite' '
+ # This "test-tool" invocation is identical...
+ if test-tool regex "HALLÓ" "Halló" ICASE
+ then
+ test_set_prereq REGEX_LOCALE
+ else
+
+ # ... to this one, but this way "test_must_fail" will
+ # tell a segfault or abort() from the regexec() test
+ # itself
+ test_must_fail test-tool regex "HALLÓ" "Halló" ICASE
+ fi
+'
test_expect_success REGEX_LOCALE 'grep literal string, no -F' '
git grep -i "TILRAUN: Halló Heimur!" &&
@@ -123,4 +138,16 @@ test_expect_success GETTEXT_LOCALE,LIBPCRE2,PCRE2_MATCH_INVALID_UTF 'PCRE v2: gr
test_cmp invalid-0xe5 actual
'
+test_expect_success GETTEXT_LOCALE,LIBPCRE2 'PCRE v2: grep non-literal ASCII from UTF-8' '
+ git grep --perl-regexp -h -o -e ll. file >actual &&
+ echo "lló" >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success GETTEXT_LOCALE,LIBPCRE2 'PCRE v2: grep avoid endless loop bug' '
+ echo " Halló" >leading-whitespace &&
+ git add leading-whitespace &&
+ doalarm 1 git grep --perl-regexp "^\s" leading-whitespace
+'
+
test_done
diff --git a/t/t7813-grep-icase-iso.sh b/t/t7813-grep-icase-iso.sh
index 701e08a..1227885 100755
--- a/t/t7813-grep-icase-iso.sh
+++ b/t/t7813-grep-icase-iso.sh
@@ -2,6 +2,7 @@
test_description='grep icase on non-English locales'
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-gettext.sh
test_expect_success GETTEXT_ISO_LOCALE 'setup' '
diff --git a/t/t7814-grep-recurse-submodules.sh b/t/t7814-grep-recurse-submodules.sh
index 828cb3b..167fe66 100755
--- a/t/t7814-grep-recurse-submodules.sh
+++ b/t/t7814-grep-recurse-submodules.sh
@@ -6,8 +6,12 @@ This test verifies the recurse-submodules feature correctly greps across
submodules.
'
+TEST_CREATE_REPO_NO_TEMPLATE=1
. ./test-lib.sh
+GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1
+export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
+
test_expect_success 'setup directory structure and submodule' '
echo "(1|2)d(3|4)" >a &&
mkdir b &&
@@ -193,6 +197,7 @@ test_expect_success !MINGW 'grep recurse submodule colon in name' '
git -C "su:b" commit -m "add fi:le" &&
test_tick &&
+ test_config_global protocol.file.allow always &&
git -C parent submodule add "../su:b" "su:b" &&
git -C parent commit -m "add submodule" &&
test_tick &&
@@ -227,6 +232,7 @@ test_expect_success 'grep history with moved submoules' '
git -C sub commit -m "add file" &&
test_tick &&
+ test_config_global protocol.file.allow always &&
git -C parent submodule add ../sub dir/sub &&
git -C parent commit -m "add submodule" &&
test_tick &&
@@ -271,6 +277,7 @@ test_expect_success 'grep using relative path' '
mkdir parent/src &&
echo "(1|2)d(3|4)" >parent/src/file2 &&
git -C parent add src/file2 &&
+ test_config_global protocol.file.allow always &&
git -C parent submodule add ../sub &&
git -C parent commit -m "add files and submodule" &&
test_tick &&
@@ -313,6 +320,7 @@ test_expect_success 'grep from a subdir' '
mkdir parent/src &&
echo "(1|2)d(3|4)" >parent/src/file &&
git -C parent add src/file &&
+ test_config_global protocol.file.allow always &&
git -C parent submodule add ../sub src/sub &&
git -C parent submodule add ../sub sub &&
git -C parent commit -m "add files and submodules" &&
@@ -340,7 +348,7 @@ test_incompatible_with_recurse_submodules ()
{
test_expect_success "--recurse-submodules and $1 are incompatible" "
test_must_fail git grep -e. --recurse-submodules $1 2>actual &&
- test_i18ngrep 'not supported with --recurse-submodules' actual
+ test_grep 'not supported with --recurse-submodules' actual
"
}
@@ -438,4 +446,192 @@ test_expect_success 'grep --recurse-submodules with --cached ignores worktree mo
test_must_fail git grep --recurse-submodules --cached "A modified line in submodule" >actual 2>&1 &&
test_must_be_empty actual
'
+
+test_expect_failure 'grep --textconv: superproject .gitattributes does not affect submodules' '
+ reset_and_clean &&
+ test_config_global diff.d2x.textconv "sed -e \"s/d/x/\"" &&
+ echo "a diff=d2x" >.gitattributes &&
+
+ cat >expect <<-\EOF &&
+ a:(1|2)x(3|4)
+ EOF
+ git grep --textconv --recurse-submodules x >actual &&
+ test_cmp expect actual
+'
+
+test_expect_failure 'grep --textconv: superproject .gitattributes (from index) does not affect submodules' '
+ reset_and_clean &&
+ test_config_global diff.d2x.textconv "sed -e \"s/d/x/\"" &&
+ echo "a diff=d2x" >.gitattributes &&
+ git add .gitattributes &&
+ rm .gitattributes &&
+
+ cat >expect <<-\EOF &&
+ a:(1|2)x(3|4)
+ EOF
+ git grep --textconv --recurse-submodules x >actual &&
+ test_cmp expect actual
+'
+
+test_expect_failure 'grep --textconv: superproject .git/info/attributes does not affect submodules' '
+ reset_and_clean &&
+ test_config_global diff.d2x.textconv "sed -e \"s/d/x/\"" &&
+ super_info="$(git rev-parse --git-path info)" &&
+ super_attr="$super_info/attributes" &&
+ test_when_finished "rm -f \"$super_attr\"" &&
+ mkdir "$super_info" &&
+ echo "a diff=d2x" >"$super_attr" &&
+
+ cat >expect <<-\EOF &&
+ a:(1|2)x(3|4)
+ EOF
+ git grep --textconv --recurse-submodules x >actual &&
+ test_cmp expect actual
+'
+
+# Note: what currently prevents this test from passing is not that the
+# .gitattributes file from "./submodule" is being ignored, but that it is being
+# propagated to the nested "./submodule/sub" files.
+#
+test_expect_failure 'grep --textconv correctly reads submodule .gitattributes' '
+ reset_and_clean &&
+ test_config_global diff.d2x.textconv "sed -e \"s/d/x/\"" &&
+ echo "a diff=d2x" >submodule/.gitattributes &&
+
+ cat >expect <<-\EOF &&
+ submodule/a:(1|2)x(3|4)
+ EOF
+ git grep --textconv --recurse-submodules x >actual &&
+ test_cmp expect actual
+'
+
+test_expect_failure 'grep --textconv correctly reads submodule .gitattributes (from index)' '
+ reset_and_clean &&
+ test_config_global diff.d2x.textconv "sed -e \"s/d/x/\"" &&
+ echo "a diff=d2x" >submodule/.gitattributes &&
+ git -C submodule add .gitattributes &&
+ rm submodule/.gitattributes &&
+
+ cat >expect <<-\EOF &&
+ submodule/a:(1|2)x(3|4)
+ EOF
+ git grep --textconv --recurse-submodules x >actual &&
+ test_cmp expect actual
+'
+
+test_expect_failure 'grep --textconv correctly reads submodule .git/info/attributes' '
+ reset_and_clean &&
+ test_config_global diff.d2x.textconv "sed -e \"s/d/x/\"" &&
+
+ submodule_info="$(git -C submodule rev-parse --path-format=absolute --git-path info)" &&
+ submodule_attr="$submodule_info/attributes" &&
+ test_when_finished "rm -f \"$submodule_attr\"" &&
+ echo "a diff=d2x" >"$submodule_attr" &&
+
+ cat >expect <<-\EOF &&
+ submodule/a:(1|2)x(3|4)
+ EOF
+ git grep --textconv --recurse-submodules x >actual &&
+ test_cmp expect actual
+'
+
+test_expect_failure 'grep saves textconv cache in the appropriate repository' '
+ reset_and_clean &&
+ test_config_global diff.d2x_cached.textconv "sed -e \"s/d/x/\"" &&
+ test_config_global diff.d2x_cached.cachetextconv true &&
+ echo "a diff=d2x_cached" >submodule/.gitattributes &&
+
+ # We only read/write to the textconv cache when grepping from an OID,
+ # as the working tree file might have modifications.
+ git grep --textconv --cached --recurse-submodules x &&
+
+ super_textconv_cache="$(git rev-parse --git-path refs/notes/textconv/d2x_cached)" &&
+ sub_textconv_cache="$(git -C submodule rev-parse \
+ --path-format=absolute --git-path refs/notes/textconv/d2x_cached)" &&
+ test_path_is_missing "$super_textconv_cache" &&
+ test_path_is_file "$sub_textconv_cache"
+'
+
+test_expect_success 'grep partially-cloned submodule' '
+ # Set up clean superproject and submodule for partial cloning.
+ test_config_global protocol.file.allow always &&
+ git init super &&
+ git init super/sub &&
+ (
+ cd super &&
+ test_commit --no-tag "Add file in superproject" \
+ super-file "Some content for super-file" &&
+ test_commit -C sub --no-tag "Add file in submodule" \
+ sub-file "Some content for sub-file" &&
+ git submodule add ./sub &&
+ git commit -m "Add other as submodule sub" &&
+ test_tick &&
+ test_commit -C sub --no-tag --append "Update file in submodule" \
+ sub-file "Some more content for sub-file" &&
+ git add sub &&
+ git commit -m "Update submodule" &&
+ test_tick &&
+ git config --local uploadpack.allowfilter 1 &&
+ git config --local uploadpack.allowanysha1inwant 1 &&
+ git -C sub config --local uploadpack.allowfilter 1 &&
+ git -C sub config --local uploadpack.allowanysha1inwant 1
+ ) &&
+ # Clone the superproject & submodule, then make sure we can lazy-fetch submodule objects.
+ git clone --filter=blob:none --also-filter-submodules \
+ --recurse-submodules "file://$(pwd)/super" partial &&
+ (
+ cd partial &&
+ cat >expect <<-\EOF &&
+ HEAD^:sub/sub-file:Some content for sub-file
+ HEAD^:super-file:Some content for super-file
+ EOF
+
+ GIT_TRACE2_EVENT="$(pwd)/trace2.log" git grep -e content \
+ --recurse-submodules HEAD^ >actual &&
+ test_cmp expect actual &&
+ # Verify that we actually fetched data from the promisor remote:
+ grep \"category\":\"promisor\",\"key\":\"fetch_count\",\"value\":\"1\" trace2.log
+ )
+'
+
+test_expect_success 'check scope of core.useReplaceRefs' '
+ git init base &&
+ git init base/sub &&
+
+ echo A >base/a &&
+ echo B >base/b &&
+ echo C >base/sub/c &&
+ echo D >base/sub/d &&
+
+ git -C base/sub add c d &&
+ git -C base/sub commit -m "Add files" &&
+
+ git -C base submodule add ./sub &&
+ git -C base add a b sub &&
+ git -C base commit -m "Add files and submodule" &&
+
+ A=$(git -C base rev-parse HEAD:a) &&
+ B=$(git -C base rev-parse HEAD:b) &&
+ C=$(git -C base/sub rev-parse HEAD:c) &&
+ D=$(git -C base/sub rev-parse HEAD:d) &&
+
+ git -C base replace $A $B &&
+ git -C base/sub replace $C $D &&
+
+ test_must_fail git -C base grep --cached --recurse-submodules A &&
+ test_must_fail git -C base grep --cached --recurse-submodules C &&
+
+ git -C base config core.useReplaceRefs false &&
+ git -C base grep --recurse-submodules A &&
+ test_must_fail git -C base grep --cached --recurse-submodules C &&
+
+ git -C base/sub config core.useReplaceRefs false &&
+ git -C base grep --cached --recurse-submodules A &&
+ git -C base grep --cached --recurse-submodules C &&
+
+ git -C base config --unset core.useReplaceRefs &&
+ test_must_fail git -C base grep --cached --recurse-submodules A &&
+ git -C base grep --cached --recurse-submodules C
+'
+
test_done
diff --git a/t/t7815-grep-binary.sh b/t/t7815-grep-binary.sh
index 90ebb64..ac87128 100755
--- a/t/t7815-grep-binary.sh
+++ b/t/t7815-grep-binary.sh
@@ -2,6 +2,7 @@
test_description='git grep in binary files'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' "
diff --git a/t/t7816-grep-binary-pattern.sh b/t/t7816-grep-binary-pattern.sh
index 9d67a5f..4353be5 100755
--- a/t/t7816-grep-binary-pattern.sh
+++ b/t/t7816-grep-binary-pattern.sh
@@ -2,6 +2,7 @@
test_description='git grep with a binary pattern files'
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-gettext.sh
nul_match_internal () {
@@ -25,7 +26,7 @@ nul_match_internal () {
>stderr &&
printf '$pattern' | q_to_nul >f &&
test_must_fail env LC_ALL=\"$lc_all\" git grep $extra_flags -f f $flags a 2>stderr &&
- test_i18ngrep ! 'This is only supported with -P under PCRE v2' stderr
+ test_grep ! 'This is only supported with -P under PCRE v2' stderr
"
elif test "$matches" = P
then
@@ -33,7 +34,7 @@ nul_match_internal () {
>stderr &&
printf '$pattern' | q_to_nul >f &&
test_must_fail env LC_ALL=\"$lc_all\" git grep -f f $flags a 2>stderr &&
- test_i18ngrep 'This is only supported with -P under PCRE v2' stderr
+ test_grep 'This is only supported with -P under PCRE v2' stderr
"
else
test_expect_success "PANIC: Test framework error. Unknown matches value $matches" 'false'
diff --git a/t/t7817-grep-sparse-checkout.sh b/t/t7817-grep-sparse-checkout.sh
index 590b99b..eb59564 100755
--- a/t/t7817-grep-sparse-checkout.sh
+++ b/t/t7817-grep-sparse-checkout.sh
@@ -83,10 +83,13 @@ test_expect_success 'setup' '
# The test below covers a special case: the sparsity patterns exclude '/b' and
# sparse checkout is enabled, but the path exists in the working tree (e.g.
-# manually created after `git sparse-checkout init`). git grep should skip it.
+# manually created after `git sparse-checkout init`). Although b is marked
+# as SKIP_WORKTREE, git grep should notice it IS present in the worktree and
+# report it.
test_expect_success 'working tree grep honors sparse checkout' '
cat >expect <<-EOF &&
a:text
+ b:new-text
EOF
test_when_finished "rm -f b" &&
echo "new-text" >b &&
@@ -126,12 +129,16 @@ test_expect_success 'grep --cached searches entries with the SKIP_WORKTREE bit'
'
# Note that sub2/ is present in the worktree but it is excluded by the sparsity
-# patterns, so grep should not recurse into it.
+# patterns. We also explicitly mark it as SKIP_WORKTREE in case it got cleared
+# by previous git commands. Thus sub2 starts as SKIP_WORKTREE but since it is
+# present in the working tree, grep should recurse into it.
test_expect_success 'grep --recurse-submodules honors sparse checkout in submodule' '
cat >expect <<-EOF &&
a:text
sub/B/b:text
+ sub2/a:text
EOF
+ git update-index --skip-worktree sub2 &&
git grep --recurse-submodules "text" >actual &&
test_cmp expect actual
'
diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh
index fc16ac2..0943dfa 100755
--- a/t/t7900-maintenance.sh
+++ b/t/t7900-maintenance.sh
@@ -20,13 +20,26 @@ test_xmllint () {
fi
}
+test_lazy_prereq SYSTEMD_ANALYZE '
+ systemd-analyze verify /lib/systemd/system/basic.target
+'
+
+test_systemd_analyze_verify () {
+ if test_have_prereq SYSTEMD_ANALYZE
+ then
+ systemd-analyze verify "$@"
+ fi
+}
+
test_expect_success 'help text' '
- test_expect_code 129 git maintenance -h 2>err &&
- test_i18ngrep "usage: git maintenance <subcommand>" err &&
- test_expect_code 128 git maintenance barf 2>err &&
- test_i18ngrep "invalid subcommand: barf" err &&
+ test_expect_code 129 git maintenance -h >actual &&
+ test_grep "usage: git maintenance <subcommand>" actual &&
+ test_expect_code 129 git maintenance barf 2>err &&
+ test_grep "unknown subcommand: \`barf'\''" err &&
+ test_grep "usage: git maintenance" err &&
test_expect_code 129 git maintenance 2>err &&
- test_i18ngrep "usage: git maintenance" err
+ test_grep "error: need a subcommand" err &&
+ test_grep "usage: git maintenance" err
'
test_expect_success 'run [--auto|--quiet]' '
@@ -54,6 +67,51 @@ test_expect_success 'maintenance.auto config option' '
test_subcommand ! git maintenance run --auto --quiet <false
'
+test_expect_success 'register uses XDG_CONFIG_HOME config if it exists' '
+ test_when_finished rm -r .config/git/config &&
+ (
+ XDG_CONFIG_HOME=.config &&
+ export XDG_CONFIG_HOME &&
+ mkdir -p $XDG_CONFIG_HOME/git &&
+ >$XDG_CONFIG_HOME/git/config &&
+ git maintenance register &&
+ git config --file=$XDG_CONFIG_HOME/git/config --get maintenance.repo >actual &&
+ pwd >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'register does not need XDG_CONFIG_HOME config to exist' '
+ test_when_finished git maintenance unregister &&
+ test_path_is_missing $XDG_CONFIG_HOME/git/config &&
+ git maintenance register &&
+ git config --global --get maintenance.repo >actual &&
+ pwd >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'unregister uses XDG_CONFIG_HOME config if it exists' '
+ test_when_finished rm -r .config/git/config &&
+ (
+ XDG_CONFIG_HOME=.config &&
+ export XDG_CONFIG_HOME &&
+ mkdir -p $XDG_CONFIG_HOME/git &&
+ >$XDG_CONFIG_HOME/git/config &&
+ git maintenance register &&
+ git maintenance unregister &&
+ test_must_fail git config --file=$XDG_CONFIG_HOME/git/config --get maintenance.repo >actual &&
+ test_must_be_empty actual
+ )
+'
+
+test_expect_success 'unregister does not need XDG_CONFIG_HOME config to exist' '
+ test_path_is_missing $XDG_CONFIG_HOME/git/config &&
+ git maintenance register &&
+ git maintenance unregister &&
+ test_must_fail git config --global --get maintenance.repo >actual &&
+ test_must_be_empty actual
+'
+
test_expect_success 'maintenance.<task>.enabled' '
git config maintenance.gc.enabled false &&
git config maintenance.commit-graph.enabled true &&
@@ -118,12 +176,12 @@ test_expect_success 'commit-graph auto condition' '
test_expect_success 'run --task=bogus' '
test_must_fail git maintenance run --task=bogus 2>err &&
- test_i18ngrep "is not a valid task" err
+ test_grep "is not a valid task" err
'
test_expect_success 'run --task duplicate' '
test_must_fail git maintenance run --task=gc --task=gc 2>err &&
- test_i18ngrep "cannot be selected multiple times" err
+ test_grep "cannot be selected multiple times" err
'
test_expect_success 'run --task=prefetch with no remotes' '
@@ -144,14 +202,14 @@ test_expect_success 'prefetch multiple remotes' '
fetchargs="--prefetch --prune --no-tags --no-write-fetch-head --recurse-submodules=no --quiet" &&
test_subcommand git fetch remote1 $fetchargs <run-prefetch.txt &&
test_subcommand git fetch remote2 $fetchargs <run-prefetch.txt &&
- test_path_is_missing .git/refs/remotes &&
+ git for-each-ref refs/remotes >actual &&
+ test_must_be_empty actual &&
git log prefetch/remotes/remote1/one &&
git log prefetch/remotes/remote2/two &&
git fetch --all &&
test_cmp_rev refs/remotes/remote1/one refs/prefetch/remotes/remote1/one &&
test_cmp_rev refs/remotes/remote2/two refs/prefetch/remotes/remote2/two &&
- test_cmp_config refs/prefetch/ log.excludedecoration &&
git log --oneline --decorate --all >log &&
! grep "prefetch" log &&
@@ -162,26 +220,6 @@ test_expect_success 'prefetch multiple remotes' '
test_subcommand git fetch remote2 $fetchargs <skip-remote1.txt
'
-test_expect_success 'prefetch and existing log.excludeDecoration values' '
- git config --unset-all log.excludeDecoration &&
- git config log.excludeDecoration refs/remotes/remote1/ &&
- git maintenance run --task=prefetch &&
-
- git config --get-all log.excludeDecoration >out &&
- grep refs/remotes/remote1/ out &&
- grep refs/prefetch/ out &&
-
- git log --oneline --decorate --all >log &&
- ! grep "prefetch" log &&
- ! grep "remote1" log &&
- grep "remote2" log &&
-
- # a second run does not change the config
- git maintenance run --task=prefetch &&
- git log --oneline --decorate --all >log2 &&
- test_cmp log log2
-'
-
test_expect_success 'loose-objects task' '
# Repack everything so we know the state of the object dir
git repack -adk &&
@@ -265,7 +303,7 @@ test_expect_success 'incremental-repack task' '
# Delete refs that have not been repacked in these packs.
git for-each-ref --format="delete %(refname)" \
- refs/prefetch refs/tags >refs &&
+ refs/prefetch refs/tags refs/remotes >refs &&
git update-ref --stdin <refs &&
# Replace the object directory with this pack layout.
@@ -274,6 +312,10 @@ test_expect_success 'incremental-repack task' '
ls $packDir/*.pack >packs-before &&
test_line_count = 3 packs-before &&
+ # make sure we do not have any broken refs that were
+ # missed in the deletion above
+ git for-each-ref &&
+
# the job repacks the two into a new pack, but does not
# delete the old ones.
git maintenance run --task=incremental-repack &&
@@ -321,15 +363,15 @@ test_expect_success EXPENSIVE 'incremental-repack 2g limit' '
--no-progress --batch-size=2147483647 <run-2g.txt
'
-test_expect_success 'maintenance.incremental-repack.auto' '
+run_incremental_repack_and_verify () {
+ test_commit A &&
git repack -adk &&
- git config core.multiPackIndex true &&
git multi-pack-index write &&
GIT_TRACE2_EVENT="$(pwd)/midx-init.txt" git \
-c maintenance.incremental-repack.auto=1 \
maintenance run --auto --task=incremental-repack 2>/dev/null &&
test_subcommand ! git multi-pack-index write --no-progress <midx-init.txt &&
- test_commit A &&
+ test_commit B &&
git pack-objects --revs .git/objects/pack/pack <<-\EOF &&
HEAD
^HEAD~1
@@ -338,7 +380,7 @@ test_expect_success 'maintenance.incremental-repack.auto' '
-c maintenance.incremental-repack.auto=2 \
maintenance run --auto --task=incremental-repack 2>/dev/null &&
test_subcommand ! git multi-pack-index write --no-progress <trace-A &&
- test_commit B &&
+ test_commit C &&
git pack-objects --revs .git/objects/pack/pack <<-\EOF &&
HEAD
^HEAD~1
@@ -347,6 +389,26 @@ test_expect_success 'maintenance.incremental-repack.auto' '
-c maintenance.incremental-repack.auto=2 \
maintenance run --auto --task=incremental-repack 2>/dev/null &&
test_subcommand git multi-pack-index write --no-progress <trace-B
+}
+
+test_expect_success 'maintenance.incremental-repack.auto' '
+ rm -rf incremental-repack-true &&
+ git init incremental-repack-true &&
+ (
+ cd incremental-repack-true &&
+ git config core.multiPackIndex true &&
+ run_incremental_repack_and_verify
+ )
+'
+
+test_expect_success 'maintenance.incremental-repack.auto (when config is unset)' '
+ rm -rf incremental-repack-unset &&
+ git init incremental-repack-unset &&
+ (
+ cd incremental-repack-unset &&
+ test_unconfig core.multiPackIndex &&
+ run_incremental_repack_and_verify
+ )
'
test_expect_success 'pack-refs task' '
@@ -361,12 +423,12 @@ test_expect_success 'pack-refs task' '
test_expect_success '--auto and --schedule incompatible' '
test_must_fail git maintenance run --auto --schedule=daily 2>err &&
- test_i18ngrep "at most one" err
+ test_grep "at most one" err
'
test_expect_success 'invalid --schedule value' '
test_must_fail git maintenance run --schedule=annually 2>err &&
- test_i18ngrep "unrecognized --schedule" err
+ test_grep "unrecognized --schedule" err
'
test_expect_success '--schedule inheritance weekly -> daily -> hourly' '
@@ -464,6 +526,11 @@ test_expect_success 'maintenance.strategy inheritance' '
test_expect_success 'register and unregister' '
test_when_finished git config --global --unset-all maintenance.repo &&
+
+ test_must_fail git maintenance unregister 2>err &&
+ grep "is not registered" err &&
+ git maintenance unregister --force &&
+
git config --global --add maintenance.repo /existing1 &&
git config --global --add maintenance.repo /existing2 &&
git config --global --get-all maintenance.repo >before &&
@@ -477,7 +544,68 @@ test_expect_success 'register and unregister' '
git maintenance unregister &&
git config --global --get-all maintenance.repo >actual &&
- test_cmp before actual
+ test_cmp before actual &&
+
+ git config --file ./other --add maintenance.repo /existing1 &&
+ git config --file ./other --add maintenance.repo /existing2 &&
+ git config --file ./other --get-all maintenance.repo >before &&
+
+ git maintenance register --config-file ./other &&
+ test_cmp_config false maintenance.auto &&
+ git config --file ./other --get-all maintenance.repo >between &&
+ cp before expect &&
+ pwd >>expect &&
+ test_cmp expect between &&
+
+ git maintenance unregister --config-file ./other &&
+ git config --file ./other --get-all maintenance.repo >actual &&
+ test_cmp before actual &&
+
+ test_must_fail git maintenance unregister 2>err &&
+ grep "is not registered" err &&
+ git maintenance unregister --force &&
+
+ test_must_fail git maintenance unregister --config-file ./other 2>err &&
+ grep "is not registered" err &&
+ git maintenance unregister --config-file ./other --force
+'
+
+test_expect_success 'register with no value for maintenance.repo' '
+ cp .git/config .git/config.orig &&
+ test_when_finished mv .git/config.orig .git/config &&
+
+ cat >>.git/config <<-\EOF &&
+ [maintenance]
+ repo
+ EOF
+ cat >expect <<-\EOF &&
+ error: missing value for '\''maintenance.repo'\''
+ EOF
+ git maintenance register 2>actual &&
+ test_cmp expect actual &&
+ git config maintenance.repo
+'
+
+test_expect_success 'unregister with no value for maintenance.repo' '
+ cp .git/config .git/config.orig &&
+ test_when_finished mv .git/config.orig .git/config &&
+
+ cat >>.git/config <<-\EOF &&
+ [maintenance]
+ repo
+ EOF
+ cat >expect <<-\EOF &&
+ error: missing value for '\''maintenance.repo'\''
+ EOF
+ test_expect_code 128 git maintenance unregister 2>actual.raw &&
+ grep ^error actual.raw >actual &&
+ test_cmp expect actual &&
+ git config maintenance.repo &&
+
+ git maintenance unregister --force 2>actual.raw &&
+ grep ^error actual.raw >actual &&
+ test_cmp expect actual &&
+ git config maintenance.repo
'
test_expect_success !MINGW 'register and unregister with regex metacharacters' '
@@ -492,8 +620,21 @@ test_expect_success !MINGW 'register and unregister with regex metacharacters' '
maintenance.repo "$(pwd)/$META"
'
+test_expect_success 'start --scheduler=<scheduler>' '
+ test_expect_code 129 git maintenance start --scheduler=foo 2>err &&
+ test_grep "unrecognized --scheduler argument" err &&
+
+ test_expect_code 129 git maintenance start --no-scheduler 2>err &&
+ test_grep "unknown option" err &&
+
+ test_expect_code 128 \
+ env GIT_TEST_MAINT_SCHEDULER="launchctl:true,schtasks:true" \
+ git maintenance start --scheduler=crontab 2>err &&
+ test_grep "fatal: crontab scheduler is not available" err
+'
+
test_expect_success 'start from empty cron table' '
- GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt" git maintenance start &&
+ GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt" git maintenance start --scheduler=crontab &&
# start registers the repo
git config --get --global --fixed-value maintenance.repo "$(pwd)" &&
@@ -516,7 +657,7 @@ test_expect_success 'stop from existing schedule' '
test_expect_success 'start preserves existing schedule' '
echo "Important information!" >cron.txt &&
- GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt" git maintenance start &&
+ GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt" git maintenance start --scheduler=crontab &&
grep "Important information!" cron.txt
'
@@ -545,7 +686,7 @@ test_expect_success 'start and stop macOS maintenance' '
EOF
rm -f args &&
- GIT_TEST_MAINT_SCHEDULER=launchctl:./print-args git maintenance start &&
+ GIT_TEST_MAINT_SCHEDULER=launchctl:./print-args git maintenance start --scheduler=launchctl &&
# start registers the repo
git config --get --global --fixed-value maintenance.repo "$(pwd)" &&
@@ -584,11 +725,11 @@ test_expect_success 'start and stop macOS maintenance' '
test_expect_success 'use launchctl list to prevent extra work' '
# ensure we are registered
- GIT_TEST_MAINT_SCHEDULER=launchctl:./print-args git maintenance start &&
+ GIT_TEST_MAINT_SCHEDULER=launchctl:./print-args git maintenance start --scheduler=launchctl &&
# do it again on a fresh args file
rm -f args &&
- GIT_TEST_MAINT_SCHEDULER=launchctl:./print-args git maintenance start &&
+ GIT_TEST_MAINT_SCHEDULER=launchctl:./print-args git maintenance start --scheduler=launchctl &&
ls "$HOME/Library/LaunchAgents" >actual &&
cat >expect <<-\EOF &&
@@ -613,7 +754,7 @@ test_expect_success 'start and stop Windows maintenance' '
EOF
rm -f args &&
- GIT_TEST_MAINT_SCHEDULER="schtasks:./print-args" git maintenance start &&
+ GIT_TEST_MAINT_SCHEDULER="schtasks:./print-args" git maintenance start --scheduler=schtasks &&
# start registers the repo
git config --get --global --fixed-value maintenance.repo "$(pwd)" &&
@@ -636,6 +777,94 @@ test_expect_success 'start and stop Windows maintenance' '
test_cmp expect args
'
+test_expect_success 'start and stop Linux/systemd maintenance' '
+ write_script print-args <<-\EOF &&
+ printf "%s\n" "$*" >>args
+ EOF
+
+ XDG_CONFIG_HOME="$PWD" &&
+ export XDG_CONFIG_HOME &&
+ rm -f args &&
+ GIT_TEST_MAINT_SCHEDULER="systemctl:./print-args" git maintenance start --scheduler=systemd-timer &&
+
+ # start registers the repo
+ git config --get --global --fixed-value maintenance.repo "$(pwd)" &&
+
+ for schedule in hourly daily weekly
+ do
+ test_path_is_file "systemd/user/git-maintenance@$schedule.timer" || return 1
+ done &&
+ test_path_is_file "systemd/user/git-maintenance@.service" &&
+
+ test_systemd_analyze_verify "systemd/user/git-maintenance@hourly.service" &&
+ test_systemd_analyze_verify "systemd/user/git-maintenance@daily.service" &&
+ test_systemd_analyze_verify "systemd/user/git-maintenance@weekly.service" &&
+
+ printf -- "--user enable --now git-maintenance@%s.timer\n" hourly daily weekly >expect &&
+ test_cmp expect args &&
+
+ rm -f args &&
+ GIT_TEST_MAINT_SCHEDULER="systemctl:./print-args" git maintenance stop &&
+
+ # stop does not unregister the repo
+ git config --get --global --fixed-value maintenance.repo "$(pwd)" &&
+
+ for schedule in hourly daily weekly
+ do
+ test_path_is_missing "systemd/user/git-maintenance@$schedule.timer" || return 1
+ done &&
+ test_path_is_missing "systemd/user/git-maintenance@.service" &&
+
+ printf -- "--user disable --now git-maintenance@%s.timer\n" hourly daily weekly >expect &&
+ test_cmp expect args
+'
+
+test_expect_success 'start and stop when several schedulers are available' '
+ write_script print-args <<-\EOF &&
+ printf "%s\n" "$*" | sed "s:gui/[0-9][0-9]*:gui/[UID]:; s:\(schtasks /create .* /xml\).*:\1:;" >>args
+ EOF
+
+ rm -f args &&
+ GIT_TEST_MAINT_SCHEDULER="systemctl:./print-args systemctl,launchctl:./print-args launchctl,schtasks:./print-args schtasks" git maintenance start --scheduler=systemd-timer &&
+ printf "launchctl bootout gui/[UID] $pfx/Library/LaunchAgents/org.git-scm.git.%s.plist\n" \
+ hourly daily weekly >expect &&
+ printf "schtasks /delete /tn Git Maintenance (%s) /f\n" \
+ hourly daily weekly >>expect &&
+ printf -- "systemctl --user enable --now git-maintenance@%s.timer\n" hourly daily weekly >>expect &&
+ test_cmp expect args &&
+
+ rm -f args &&
+ GIT_TEST_MAINT_SCHEDULER="systemctl:./print-args systemctl,launchctl:./print-args launchctl,schtasks:./print-args schtasks" git maintenance start --scheduler=launchctl &&
+ printf -- "systemctl --user disable --now git-maintenance@%s.timer\n" hourly daily weekly >expect &&
+ printf "schtasks /delete /tn Git Maintenance (%s) /f\n" \
+ hourly daily weekly >>expect &&
+ for frequency in hourly daily weekly
+ do
+ PLIST="$pfx/Library/LaunchAgents/org.git-scm.git.$frequency.plist" &&
+ echo "launchctl bootout gui/[UID] $PLIST" >>expect &&
+ echo "launchctl bootstrap gui/[UID] $PLIST" >>expect || return 1
+ done &&
+ test_cmp expect args &&
+
+ rm -f args &&
+ GIT_TEST_MAINT_SCHEDULER="systemctl:./print-args systemctl,launchctl:./print-args launchctl,schtasks:./print-args schtasks" git maintenance start --scheduler=schtasks &&
+ printf -- "systemctl --user disable --now git-maintenance@%s.timer\n" hourly daily weekly >expect &&
+ printf "launchctl bootout gui/[UID] $pfx/Library/LaunchAgents/org.git-scm.git.%s.plist\n" \
+ hourly daily weekly >>expect &&
+ printf "schtasks /create /tn Git Maintenance (%s) /f /xml\n" \
+ hourly daily weekly >>expect &&
+ test_cmp expect args &&
+
+ rm -f args &&
+ GIT_TEST_MAINT_SCHEDULER="systemctl:./print-args systemctl,launchctl:./print-args launchctl,schtasks:./print-args schtasks" git maintenance stop &&
+ printf -- "systemctl --user disable --now git-maintenance@%s.timer\n" hourly daily weekly >expect &&
+ printf "launchctl bootout gui/[UID] $pfx/Library/LaunchAgents/org.git-scm.git.%s.plist\n" \
+ hourly daily weekly >>expect &&
+ printf "schtasks /delete /tn Git Maintenance (%s) /f\n" \
+ hourly daily weekly >>expect &&
+ test_cmp expect args
+'
+
test_expect_success 'register preserves existing strategy' '
git config maintenance.strategy none &&
git maintenance register &&
@@ -666,4 +895,17 @@ test_expect_success 'register and unregister bare repo' '
)
'
+test_expect_success 'failed schedule prevents config change' '
+ git init --bare failcase &&
+
+ for scheduler in crontab launchctl schtasks systemctl
+ do
+ GIT_TEST_MAINT_SCHEDULER="$scheduler:false" &&
+ export GIT_TEST_MAINT_SCHEDULER &&
+ test_must_fail \
+ git -C failcase maintenance start &&
+ test_must_fail git -C failcase config maintenance.auto || return 1
+ done
+'
+
test_done
diff --git a/t/t8001-annotate.sh b/t/t8001-annotate.sh
index a536a62..d7167f5 100755
--- a/t/t8001-annotate.sh
+++ b/t/t8001-annotate.sh
@@ -4,6 +4,7 @@ test_description='git annotate'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_CREATE_REPO_NO_TEMPLATE=1
. ./test-lib.sh
PROG='git annotate'
diff --git a/t/t8002-blame.sh b/t/t8002-blame.sh
index 5bb302b..0147de3 100755
--- a/t/t8002-blame.sh
+++ b/t/t8002-blame.sh
@@ -4,6 +4,7 @@ test_description='git blame'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_CREATE_REPO_NO_TEMPLATE=1
. ./test-lib.sh
PROG='git blame -c'
@@ -97,7 +98,7 @@ test_expect_success 'set up abbrev tests' '
test_commit abbrev &&
sha1=$(git rev-parse --verify HEAD) &&
check_abbrev () {
- expect=$1; shift
+ expect=$1 && shift &&
echo $sha1 | cut -c 1-$expect >expect &&
git blame "$@" abbrev.t >actual &&
perl -lne "/[0-9a-f]+/ and print \$&" <actual >actual.sha &&
diff --git a/t/t8003-blame-corner-cases.sh b/t/t8003-blame-corner-cases.sh
index da80f81..7312655 100755
--- a/t/t8003-blame-corner-cases.sh
+++ b/t/t8003-blame-corner-cases.sh
@@ -13,14 +13,8 @@ test_expect_success setup '
echo B B B B B >two &&
echo C C C C C >tres &&
echo ABC >mouse &&
- for i in 1 2 3 4 5 6 7 8 9
- do
- echo $i
- done >nine_lines &&
- for i in 1 2 3 4 5 6 7 8 9 a
- do
- echo $i
- done >ten_lines &&
+ test_write_lines 1 2 3 4 5 6 7 8 9 >nine_lines &&
+ test_write_lines 1 2 3 4 5 6 7 8 9 a >ten_lines &&
git add one two tres mouse nine_lines ten_lines &&
test_tick &&
GIT_AUTHOR_NAME=Initial git commit -m Initial &&
@@ -207,13 +201,13 @@ committer David Reiss <dreiss@facebook.com> 1234567890 +0000
some message
EOF
- COMMIT=$(git hash-object -t commit -w badcommit) &&
+ COMMIT=$(git hash-object --literally -t commit -w badcommit) &&
git --no-pager blame $COMMIT -- uno >/dev/null
'
test_expect_success 'blame -L with invalid start' '
test_must_fail git blame -L5 tres 2>errors &&
- test_i18ngrep "has only 2 lines" errors
+ test_grep "has only 2 lines" errors
'
test_expect_success 'blame -L with invalid end' '
diff --git a/t/t8007-cat-file-textconv.sh b/t/t8007-cat-file-textconv.sh
index eacd49a..c8266f1 100755
--- a/t/t8007-cat-file-textconv.sh
+++ b/t/t8007-cat-file-textconv.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='git cat-file textconv support'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
cat >helper <<'EOF'
@@ -19,6 +21,48 @@ test_expect_success 'setup ' '
GIT_AUTHOR_NAME=Number2 git commit -a -m Second --date="2010-01-01 20:00:00"
'
+test_expect_success 'usage: <bad rev>' '
+ cat >expect <<-\EOF &&
+ fatal: Not a valid object name HEAD2
+ EOF
+ test_must_fail git cat-file --textconv HEAD2 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'usage: <bad rev>:<bad path>' '
+ cat >expect <<-\EOF &&
+ fatal: invalid object name '\''HEAD2'\''.
+ EOF
+ test_must_fail git cat-file --textconv HEAD2:two.bin 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'usage: <rev>:<bad path>' '
+ cat >expect <<-\EOF &&
+ fatal: path '\''two.bin'\'' does not exist in '\''HEAD'\''
+ EOF
+ test_must_fail git cat-file --textconv HEAD:two.bin 2>actual &&
+ test_cmp expect actual
+'
+
+
+test_expect_success 'usage: <rev> with no <path>' '
+ cat >expect <<-\EOF &&
+ fatal: <object>:<path> required, only <object> '\''HEAD'\'' given
+ EOF
+ test_must_fail git cat-file --textconv HEAD 2>actual &&
+ test_cmp expect actual
+'
+
+
+test_expect_success 'usage: <bad rev>:<good (in HEAD) path>' '
+ cat >expect <<-\EOF &&
+ fatal: invalid object name '\''HEAD2'\''.
+ EOF
+ test_must_fail git cat-file --textconv HEAD2:one.bin 2>actual &&
+ test_cmp expect actual
+'
+
cat >expected <<EOF
bin: test version 2
EOF
diff --git a/t/t8010-cat-file-filters.sh b/t/t8010-cat-file-filters.sh
index 31de4b6..eb64b76 100755
--- a/t/t8010-cat-file-filters.sh
+++ b/t/t8010-cat-file-filters.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='git cat-file filters support'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup ' '
@@ -41,7 +43,7 @@ test_expect_success 'cat-file --textconv --path=<path> works' '
sha1=$(git rev-parse -q --verify HEAD:world.txt) &&
test_config diff.txt.textconv "tr A-Za-z N-ZA-Mn-za-m <" &&
git cat-file --textconv --path=hello.txt $sha1 >rot13 &&
- test uryyb = "$(cat rot13 | remove_cr)"
+ test uryyb = "$(remove_cr <rot13)"
'
test_expect_success '--path=<path> complains without --textconv/--filters' '
diff --git a/t/t8012-blame-colors.sh b/t/t8012-blame-colors.sh
index 90c75db..c3a5f6d 100755
--- a/t/t8012-blame-colors.sh
+++ b/t/t8012-blame-colors.sh
@@ -4,6 +4,7 @@ test_description='colored git blame'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_CREATE_REPO_NO_TEMPLATE=1
. ./test-lib.sh
PROG='git blame -c'
diff --git a/t/t8013-blame-ignore-revs.sh b/t/t8013-blame-ignore-revs.sh
index b18633d..dbfbd86 100755
--- a/t/t8013-blame-ignore-revs.sh
+++ b/t/t8013-blame-ignore-revs.sh
@@ -25,11 +25,11 @@ test_expect_success setup '
git blame --line-porcelain file >blame_raw &&
- grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
git rev-parse X >expect &&
test_cmp expect actual &&
- grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
git rev-parse X >expect &&
test_cmp expect actual
'
@@ -53,11 +53,11 @@ do
test_expect_success "ignore_rev_changing_lines ($I)" '
git blame --line-porcelain --ignore-rev $I file >blame_raw &&
- grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
git rev-parse A >expect &&
test_cmp expect actual &&
- grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
git rev-parse B >expect &&
test_cmp expect actual
'
@@ -79,10 +79,10 @@ test_expect_success ignore_rev_adding_unblamable_lines '
git rev-parse Y >expect &&
git blame --line-porcelain file --ignore-rev Y >blame_raw &&
- grep -E "^[0-9a-f]+ [0-9]+ 3" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 3/s/ .*//p" blame_raw >actual &&
test_cmp expect actual &&
- grep -E "^[0-9a-f]+ [0-9]+ 4" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 4/s/ .*//p" blame_raw >actual &&
test_cmp expect actual
'
@@ -92,11 +92,11 @@ test_expect_success ignore_revs_from_files '
git rev-parse Y >ignore_y &&
git blame --line-porcelain file --ignore-revs-file ignore_x --ignore-revs-file ignore_y >blame_raw &&
- grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
git rev-parse A >expect &&
test_cmp expect actual &&
- grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
git rev-parse B >expect &&
test_cmp expect actual
'
@@ -106,11 +106,11 @@ test_expect_success ignore_revs_from_configs_and_files '
git config --add blame.ignoreRevsFile ignore_x &&
git blame --line-porcelain file --ignore-revs-file ignore_y >blame_raw &&
- grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
git rev-parse A >expect &&
test_cmp expect actual &&
- grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
git rev-parse B >expect &&
test_cmp expect actual
'
@@ -121,22 +121,22 @@ test_expect_success override_ignore_revs_file '
git blame --line-porcelain file --ignore-revs-file "" --ignore-revs-file ignore_y >blame_raw &&
git rev-parse X >expect &&
- grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
test_cmp expect actual &&
- grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
test_cmp expect actual
'
test_expect_success bad_files_and_revs '
test_must_fail git blame file --ignore-rev NOREV 2>err &&
- test_i18ngrep "cannot find revision NOREV to ignore" err &&
+ test_grep "cannot find revision NOREV to ignore" err &&
test_must_fail git blame file --ignore-revs-file NOFILE 2>err &&
- test_i18ngrep "could not open.*: NOFILE" err &&
+ test_grep "could not open.*: NOFILE" err &&
echo NOREV >ignore_norev &&
test_must_fail git blame file --ignore-revs-file ignore_norev 2>err &&
- test_i18ngrep "invalid object name: NOREV" err
+ test_grep "invalid object name: NOREV" err
'
# For ignored revs that have added 'unblamable' lines, mark those lines with a
@@ -279,11 +279,11 @@ test_expect_success ignore_merge '
test_merge M B &&
git blame --line-porcelain file --ignore-rev M >blame_raw &&
- grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
git rev-parse B >expect &&
test_cmp expect actual &&
- grep -E "^[0-9a-f]+ [0-9]+ 9" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 9/s/ .*//p" blame_raw >actual &&
git rev-parse C >expect &&
test_cmp expect actual
'
diff --git a/t/t8014-blame-ignore-fuzzy.sh b/t/t8014-blame-ignore-fuzzy.sh
index e68e611..0bd0341 100755
--- a/t/t8014-blame-ignore-fuzzy.sh
+++ b/t/t8014-blame-ignore-fuzzy.sh
@@ -310,7 +310,7 @@ test_expect_success setup '
echo "$line" >>"$i" &&
git add "$i" &&
test_tick &&
- GIT_AUTHOR_NAME="$line_count" git commit -m "$line_count"
+ GIT_AUTHOR_NAME="$line_count" git commit -m "$line_count" || return 1
done <"a$i"
done &&
@@ -318,7 +318,7 @@ test_expect_success setup '
do
# Overwrite the files with the final content.
cp b$i $i &&
- git add $i
+ git add $i || return 1
done &&
test_tick &&
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index aa0c204..5a77100 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -4,6 +4,7 @@ test_description='git send-email'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# May be altered later in the test
@@ -11,7 +12,7 @@ PREREQ="PERL"
replace_variable_fields () {
sed -e "s/^\(Date:\).*/\1 DATE-STRING/" \
- -e "s/^\(Message-Id:\).*/\1 MESSAGE-ID-STRING/" \
+ -e "s/^\(Message-ID:\).*/\1 MESSAGE-ID-STRING/" \
-e "s/^\(X-Mailer:\).*/\1 X-MAILER-STRING/"
}
@@ -46,7 +47,7 @@ clean_fake_sendmail () {
test_expect_success $PREREQ 'Extract patches' '
patches=$(git format-patch -s --cc="One <one@example.com>" --cc=two@example.com -n HEAD^1) &&
- threaded_patches=$(git format-patch -o threaded -s --in-reply-to="format" HEAD^1)
+ threaded_patches=$(git format-patch -o threaded --thread=shallow -s --in-reply-to="format" HEAD^1)
'
# Test no confirm early to ensure remaining tests will not hang
@@ -60,8 +61,8 @@ test_no_confirm () {
--smtp-server="$(pwd)/fake.sendmail" \
$@ \
$patches >stdout &&
- ! grep "Send this email" stdout &&
- >no_confirm_okay
+ ! grep "Send this email" stdout &&
+ >no_confirm_okay
}
# Exit immediately to prevent hang if a no-confirm test fails
@@ -224,7 +225,7 @@ Cc: cc@example.com,
two@example.com
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
X-Mailer: X-MAILER-STRING
In-Reply-To: <unique-message-id@example.com>
References: <unique-message-id@example.com>
@@ -336,13 +337,14 @@ test_expect_success $PREREQ 'Show all headers' '
test_expect_success $PREREQ 'Prompting works' '
clean_fake_sendmail &&
(echo "to@example.com" &&
- echo ""
+ echo "my-message-id@example.com"
) | GIT_SEND_EMAIL_NOTTY=1 git send-email \
--smtp-server="$(pwd)/fake.sendmail" \
$patches \
2>errors &&
grep "^From: A U Thor <author@example.com>\$" msgtxt1 &&
- grep "^To: to@example.com\$" msgtxt1
+ grep "^To: to@example.com\$" msgtxt1 &&
+ grep "^In-Reply-To: <my-message-id@example.com>" msgtxt1
'
test_expect_success $PREREQ,AUTOIDENT 'implicit ident is allowed' '
@@ -369,17 +371,20 @@ test_expect_success $PREREQ,!AUTOIDENT 'broken implicit ident aborts send-email'
--smtp-server="$(pwd)/fake.sendmail" \
--to=to@example.com \
$patches </dev/null 2>errors &&
- test_i18ngrep "tell me who you are" errors
+ test_grep "tell me who you are" errors
)
'
-test_expect_success $PREREQ 'setup tocmd and cccmd scripts' '
+test_expect_success $PREREQ 'setup cmd scripts' '
write_script tocmd-sed <<-\EOF &&
sed -n -e "s/^tocmd--//p" "$1"
EOF
- write_script cccmd-sed <<-\EOF
+ write_script cccmd-sed <<-\EOF &&
sed -n -e "s/^cccmd--//p" "$1"
EOF
+ write_script headercmd-sed <<-\EOF
+ sed -n -e "s/^headercmd--//p" "$1"
+ EOF
'
test_expect_success $PREREQ 'tocmd works' '
@@ -409,6 +414,70 @@ test_expect_success $PREREQ 'cccmd works' '
grep "^ cccmd@example.com" msgtxt1
'
+test_expect_success $PREREQ 'headercmd works' '
+ clean_fake_sendmail &&
+ cp $patches headercmd.patch &&
+ echo "headercmd--X-Debbugs-CC: dummy@example.com" >>headercmd.patch &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --header-cmd=./headercmd-sed \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ headercmd.patch \
+ &&
+ grep "^X-Debbugs-CC: dummy@example.com" msgtxt1
+'
+
+test_expect_success $PREREQ '--no-header-cmd works' '
+ clean_fake_sendmail &&
+ cp $patches headercmd.patch &&
+ echo "headercmd--X-Debbugs-CC: dummy@example.com" >>headercmd.patch &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --header-cmd=./headercmd-sed \
+ --no-header-cmd \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ headercmd.patch \
+ &&
+ ! grep "^X-Debbugs-CC: dummy@example.com" msgtxt1
+'
+
+test_expect_success $PREREQ 'multiline fields are correctly unfolded' '
+ clean_fake_sendmail &&
+ cp $patches headercmd.patch &&
+ write_script headercmd-multiline <<-\EOF &&
+ echo "X-Debbugs-CC: someone@example.com
+FoldedField: This is a tale
+ best told using
+ multiple lines."
+ EOF
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --header-cmd=./headercmd-multiline \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ headercmd.patch &&
+ grep "^FoldedField: This is a tale best told using multiple lines.$" msgtxt1
+'
+
+# Blank lines in the middle of the output of a command are invalid.
+test_expect_success $PREREQ 'malform output reported on blank lines in command output' '
+ clean_fake_sendmail &&
+ cp $patches headercmd.patch &&
+ write_script headercmd-malformed-output <<-\EOF &&
+ echo "X-Debbugs-CC: someone@example.com
+
+SomeOtherField: someone-else@example.com"
+ EOF
+ ! git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --header-cmd=./headercmd-malformed-output \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ headercmd.patch
+'
+
test_expect_success $PREREQ 'reject long lines' '
z8=zzzzzzzz &&
z64=$z8$z8$z8$z8$z8$z8$z8$z8 &&
@@ -539,7 +608,7 @@ test_expect_success $PREREQ "--validate respects relative core.hooksPath path" '
test_path_is_file my-hooks.ran &&
cat >expect <<-EOF &&
fatal: longline.patch: rejected by sendemail-validate hook
- fatal: command '"'"'my-hooks/sendemail-validate'"'"' died with exit code 1
+ fatal: command '"'"'git hook run --ignore-missing sendemail-validate -- <patch> <header>'"'"' died with exit code 1
warning: no patches were sent
EOF
test_cmp expect actual
@@ -558,12 +627,68 @@ test_expect_success $PREREQ "--validate respects absolute core.hooksPath path" '
test_path_is_file my-hooks.ran &&
cat >expect <<-EOF &&
fatal: longline.patch: rejected by sendemail-validate hook
- fatal: command '"'"'$hooks_path/sendemail-validate'"'"' died with exit code 1
+ fatal: command '"'"'git hook run --ignore-missing sendemail-validate -- <patch> <header>'"'"' died with exit code 1
+ warning: no patches were sent
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success $PREREQ "--validate hook supports multiple addresses in arguments" '
+ hooks_path="$(pwd)/my-hooks" &&
+ test_config core.hooksPath "$hooks_path" &&
+ test_when_finished "rm my-hooks.ran" &&
+ test_must_fail git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com,abc@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ --validate \
+ longline.patch 2>actual &&
+ test_path_is_file my-hooks.ran &&
+ cat >expect <<-EOF &&
+ fatal: longline.patch: rejected by sendemail-validate hook
+ fatal: command '"'"'git hook run --ignore-missing sendemail-validate -- <patch> <header>'"'"' died with exit code 1
warning: no patches were sent
EOF
test_cmp expect actual
'
+test_expect_success $PREREQ "--validate hook supports header argument" '
+ write_script my-hooks/sendemail-validate <<-\EOF &&
+ if test "$#" -ge 2
+ then
+ grep "X-test-header: v1.0" "$2"
+ else
+ echo "No header arg passed"
+ exit 1
+ fi
+ EOF
+ test_config core.hooksPath "my-hooks" &&
+ rm -fr outdir &&
+ git format-patch \
+ --add-header="X-test-header: v1.0" \
+ -n HEAD^1 -o outdir &&
+ git send-email \
+ --dry-run \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ --validate \
+ outdir/000?-*.patch
+'
+
+test_expect_success $PREREQ 'clear message-id before parsing a new message' '
+ clean_fake_sendmail &&
+ echo true | write_script my-hooks/sendemail-validate &&
+ test_config core.hooksPath my-hooks &&
+ git send-email --validate --to=recipient@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ $patches $threaded_patches &&
+ id0=$(grep "^Message-ID: " $threaded_patches) &&
+ id1=$(grep "^Message-ID: " msgtxt1) &&
+ id2=$(grep "^Message-ID: " msgtxt2) &&
+ test "z$id0" = "z$id2" &&
+ test "z$id1" != "z$id2"
+'
+
for enc in 7bit 8bit quoted-printable base64
do
test_expect_success $PREREQ "--transfer-encoding=$enc produces correct header" '
@@ -616,7 +741,7 @@ test_expect_success $PREREQ 'In-Reply-To without --chain-reply-to' '
sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt1 >actual &&
test_cmp expect actual &&
# Second and subsequent messages are replies to the first one
- sed -n -e "s/^Message-Id: *\(.*\)/\1/p" msgtxt1 >expect &&
+ sed -n -e "s/^Message-ID: *\(.*\)/\1/p" msgtxt1 >expect &&
sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt2 >actual &&
test_cmp expect actual &&
sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt3 >actual &&
@@ -636,10 +761,10 @@ test_expect_success $PREREQ 'In-Reply-To with --chain-reply-to' '
2>errors &&
sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt1 >actual &&
test_cmp expect actual &&
- sed -n -e "s/^Message-Id: *\(.*\)/\1/p" msgtxt1 >expect &&
+ sed -n -e "s/^Message-ID: *\(.*\)/\1/p" msgtxt1 >expect &&
sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt2 >actual &&
test_cmp expect actual &&
- sed -n -e "s/^Message-Id: *\(.*\)/\1/p" msgtxt2 >expect &&
+ sed -n -e "s/^Message-ID: *\(.*\)/\1/p" msgtxt2 >expect &&
sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt3 >actual &&
test_cmp expect actual
'
@@ -712,7 +837,7 @@ Cc: cc@example.com,
two@example.com
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
X-Mailer: X-MAILER-STRING
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
@@ -758,7 +883,7 @@ Cc: A <author@example.com>,
two@example.com
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
X-Mailer: X-MAILER-STRING
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
@@ -795,7 +920,7 @@ Cc: A <author@example.com>,
C O Mitter <committer@example.com>
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
X-Mailer: X-MAILER-STRING
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
@@ -823,7 +948,7 @@ From: Example <from@example.com>
To: to@example.com
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
X-Mailer: X-MAILER-STRING
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
@@ -859,7 +984,7 @@ Cc: A <author@example.com>,
cc-cmd@example.com
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
X-Mailer: X-MAILER-STRING
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
@@ -892,7 +1017,7 @@ Cc: A <author@example.com>,
two@example.com
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
X-Mailer: X-MAILER-STRING
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
@@ -925,7 +1050,7 @@ Cc: A <author@example.com>,
two@example.com
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
X-Mailer: X-MAILER-STRING
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
@@ -962,7 +1087,7 @@ Cc: A <author@example.com>,
C O Mitter <committer@example.com>
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
X-Mailer: X-MAILER-STRING
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
@@ -992,7 +1117,7 @@ Cc: A <author@example.com>,
C O Mitter <committer@example.com>
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
X-Mailer: X-MAILER-STRING
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
@@ -1477,7 +1602,7 @@ test_expect_success $PREREQ 'To headers from files reset each patch' '
test_expect_success $PREREQ 'setup expect' '
cat >email-using-8bit <<\EOF
From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
-Message-Id: <bogus-message-id@example.com>
+Message-ID: <bogus-message-id@example.com>
From: author@example.com
Date: Sat, 12 Jun 2010 15:53:58 +0200
Subject: subject goes here
@@ -1518,7 +1643,7 @@ test_expect_success $PREREQ 'asks about and fixes 8bit encodings' '
grep "do not declare a Content-Transfer-Encoding" stdout &&
grep email-using-8bit stdout &&
grep "Which 8bit encoding" stdout &&
- egrep "Content|MIME" msgtxt1 >actual &&
+ grep -E "Content|MIME" msgtxt1 >actual &&
test_cmp content-type-decl actual
'
@@ -1529,7 +1654,7 @@ test_expect_success $PREREQ 'sendemail.8bitEncoding works' '
git send-email --from=author@example.com --to=nobody@example.com \
--smtp-server="$(pwd)/fake.sendmail" \
email-using-8bit >stdout &&
- egrep "Content|MIME" msgtxt1 >actual &&
+ grep -E "Content|MIME" msgtxt1 >actual &&
test_cmp content-type-decl actual
'
@@ -1544,7 +1669,7 @@ test_expect_success $PREREQ 'sendemail.8bitEncoding in .git/config overrides --g
git send-email --from=author@example.com --to=nobody@example.com \
--smtp-server="$(pwd)/fake.sendmail" \
email-using-8bit >stdout &&
- egrep "Content|MIME" msgtxt1 >actual &&
+ grep -E "Content|MIME" msgtxt1 >actual &&
test_cmp content-type-decl actual
'
@@ -1556,14 +1681,14 @@ test_expect_success $PREREQ '--8bit-encoding overrides sendemail.8bitEncoding' '
--smtp-server="$(pwd)/fake.sendmail" \
--8bit-encoding=UTF-8 \
email-using-8bit >stdout &&
- egrep "Content|MIME" msgtxt1 >actual &&
+ grep -E "Content|MIME" msgtxt1 >actual &&
test_cmp content-type-decl actual
'
test_expect_success $PREREQ 'setup expect' '
cat >email-using-8bit <<-\EOF
From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
- Message-Id: <bogus-message-id@example.com>
+ Message-ID: <bogus-message-id@example.com>
From: author@example.com
Date: Sat, 12 Jun 2010 15:53:58 +0200
Subject: Dieser Betreff enthält auch einen Umlaut!
@@ -1592,7 +1717,7 @@ test_expect_success $PREREQ '--8bit-encoding also treats subject' '
test_expect_success $PREREQ 'setup expect' '
cat >email-using-8bit <<-\EOF
From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
- Message-Id: <bogus-message-id@example.com>
+ Message-ID: <bogus-message-id@example.com>
From: A U Thor <author@example.com>
Date: Sat, 12 Jun 2010 15:53:58 +0200
Content-Type: text/plain; charset=UTF-8
@@ -1673,7 +1798,7 @@ test_expect_success $PREREQ '8-bit and sendemail.transferencoding=base64' '
test_expect_success $PREREQ 'setup expect' '
cat >email-using-qp <<-\EOF
From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
- Message-Id: <bogus-message-id@example.com>
+ Message-ID: <bogus-message-id@example.com>
From: A U Thor <author@example.com>
Date: Sat, 12 Jun 2010 15:53:58 +0200
MIME-Version: 1.0
@@ -1699,7 +1824,7 @@ test_expect_success $PREREQ 'convert from quoted-printable to base64' '
test_expect_success $PREREQ 'setup expect' "
tr -d '\\015' | tr '%' '\\015' >email-using-crlf <<EOF
From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
-Message-Id: <bogus-message-id@example.com>
+Message-ID: <bogus-message-id@example.com>
From: A U Thor <author@example.com>
Date: Sat, 12 Jun 2010 15:53:58 +0200
Content-Type: text/plain; charset=UTF-8
@@ -1956,7 +2081,7 @@ test_expect_success $PREREQ 'aliases and sendemail.identity' '
-c sendemail.aliasesfile=default-aliases \
-c sendemail.cloud.aliasesfile=cloud-aliases \
send-email -1 2>stderr &&
- test_i18ngrep "cloud-aliases" stderr
+ test_grep "cloud-aliases" stderr
'
test_sendmail_aliases () {
@@ -2288,9 +2413,7 @@ test_expect_success $PREREQ 'cmdline in-reply-to used with --no-thread' '
'
test_expect_success $PREREQ 'invoke hook' '
- mkdir -p .git/hooks &&
-
- write_script .git/hooks/sendemail-validate <<-\EOF &&
+ test_hook sendemail-validate <<-\EOF &&
# test that we have the correct environment variable, pwd, and
# argument
case "$GIT_DIR" in
@@ -2323,10 +2446,41 @@ test_expect_success $PREREQ 'invoke hook' '
--to=nobody@example.com \
--smtp-server="$(pwd)/../fake.sendmail" \
../another.patch 2>err &&
- test_i18ngrep "rejected by sendemail-validate hook" err
+ test_grep "rejected by sendemail-validate hook" err
)
'
+expected_file_counter_output () {
+ total=$1
+ count=0
+ while test $count -ne $total
+ do
+ count=$((count + 1)) &&
+ echo "$count/$total" || return
+ done
+}
+
+test_expect_success $PREREQ '--validate hook allows counting of messages' '
+ test_when_finished "rm -rf my-hooks.log" &&
+ test_config core.hooksPath "my-hooks" &&
+ mkdir -p my-hooks &&
+
+ write_script my-hooks/sendemail-validate <<-\EOF &&
+ num=$GIT_SENDEMAIL_FILE_COUNTER &&
+ tot=$GIT_SENDEMAIL_FILE_TOTAL &&
+ echo "$num/$tot" >>my-hooks.log || exit 1
+ EOF
+
+ >my-hooks.log &&
+ expected_file_counter_output 4 >expect &&
+ git send-email \
+ --from="Example <from@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ --validate -3 --cover-letter --force &&
+ test_cmp expect my-hooks.log
+'
+
test_expect_success $PREREQ 'test that send-email works outside a repo' '
nongit git send-email \
--from="Example <nobody@example.com>" \
@@ -2335,6 +2489,12 @@ test_expect_success $PREREQ 'test that send-email works outside a repo' '
"$(pwd)/0001-add-main.patch"
'
+test_expect_success $PREREQ 'send-email relays -v 3 to format-patch' '
+ test_when_finished "rm -f out" &&
+ git send-email --dry-run -v 3 -1 >out &&
+ grep "PATCH v3" out
+'
+
test_expect_success $PREREQ 'test that sendmail config is rejected' '
test_config sendmail.program sendmail &&
test_must_fail git send-email \
@@ -2342,7 +2502,7 @@ test_expect_success $PREREQ 'test that sendmail config is rejected' '
--to=nobody@example.com \
--smtp-server="$(pwd)/fake.sendmail" \
HEAD^ 2>err &&
- test_i18ngrep "found configuration options for '"'"sendmail"'"'" err
+ test_grep "found configuration options for '"'"sendmail"'"'" err
'
test_expect_success $PREREQ 'test that sendmail config rejection is specific' '
@@ -2364,4 +2524,45 @@ test_expect_success $PREREQ 'test forbidSendmailVariables behavior override' '
HEAD^
'
+test_expect_success $PREREQ '--compose handles lowercase headers' '
+ write_script fake-editor <<-\EOF &&
+ sed "s/^From:.*/from: edited-from@example.com/i" "$1" >"$1.tmp" &&
+ mv "$1.tmp" "$1"
+ EOF
+ clean_fake_sendmail &&
+ git send-email \
+ --compose \
+ --from="Example <from@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ HEAD^ &&
+ grep "From: edited-from@example.com" msgtxt1
+'
+
+test_expect_success $PREREQ '--compose handles to headers' '
+ write_script fake-editor <<-\EOF &&
+ sed "s/^To: .*/&, edited-to@example.com/" <"$1" >"$1.tmp" &&
+ echo this is the body >>"$1.tmp" &&
+ mv "$1.tmp" "$1"
+ EOF
+ clean_fake_sendmail &&
+ GIT_SEND_EMAIL_NOTTY=1 \
+ git send-email \
+ --compose \
+ --from="Example <from@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ HEAD^ &&
+ # Check both that the cover letter used our modified "to" line,
+ # but also that it was picked up for the patch.
+ q_to_tab >expect <<-\EOF &&
+ To: nobody@example.com,
+ Qedited-to@example.com
+ EOF
+ grep -A1 "^To:" msgtxt1 >msgtxt1.to &&
+ test_cmp expect msgtxt1.to &&
+ grep -A1 "^To:" msgtxt2 >msgtxt2.to &&
+ test_cmp expect msgtxt2.to
+'
+
test_done
diff --git a/t/t9002-column.sh b/t/t9002-column.sh
index 6d3dbde..d5b98e6 100755
--- a/t/t9002-column.sh
+++ b/t/t9002-column.sh
@@ -1,6 +1,7 @@
#!/bin/sh
test_description='git column'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -195,4 +196,15 @@ EOF
test_cmp expected actual
'
+test_expect_success 'padding must be non-negative' '
+ cat >input <<\EOF &&
+1 2 3 4 5 6
+EOF
+ cat >expected <<\EOF &&
+fatal: --padding must be non-negative
+EOF
+ test_must_fail git column --mode=column --padding=-1 <input >actual 2>&1 &&
+ test_cmp expected actual
+'
+
test_done
diff --git a/t/t9003-help-autocorrect.sh b/t/t9003-help-autocorrect.sh
index f00deaf..14a704d 100755
--- a/t/t9003-help-autocorrect.sh
+++ b/t/t9003-help-autocorrect.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='help.autocorrect finding a match'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -60,4 +62,10 @@ test_expect_success 'autocorrect can be declined altogether' '
test_line_count = 1 actual
'
+test_expect_success 'autocorrect works in work tree created from bare repo' '
+ git clone --bare . bare.git &&
+ git -C bare.git worktree add ../worktree &&
+ git -C worktree -c help.autocorrect=immediate stauts
+'
+
test_done
diff --git a/t/t9004-example.sh b/t/t9004-example.sh
index 7e8894a..590aab0 100755
--- a/t/t9004-example.sh
+++ b/t/t9004-example.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='check that example code compiles and runs'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'decorate' '
diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
index fea41b3..af28b01 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -21,7 +21,7 @@ test_expect_success 'git svn help works anywhere' '
'
test_expect_success \
- 'initialize git svn' '
+ 'initialize git svn' '
mkdir import &&
(
cd import &&
@@ -38,9 +38,9 @@ test_expect_success \
rm -rf import &&
git svn init "$svnrepo"'
-test_expect_success \
- 'import an SVN revision into git' \
- 'git svn fetch'
+test_expect_success 'import an SVN revision into git' '
+ git svn fetch
+'
test_expect_success "checkout from svn" 'svn co "$svnrepo" "$SVN_TREE"'
@@ -233,27 +233,26 @@ test_expect_success POSIXPERM,SYMLINKS "$name" '
'
test_expect_success 'exit if remote refs are ambigious' '
- git config --add svn-remote.svn.fetch \
+ git config --add svn-remote.svn.fetch \
bar:refs/remotes/git-svn &&
test_must_fail git svn migrate
'
test_expect_success 'exit if init-ing a would clobber a URL' '
- svnadmin create "${PWD}/svnrepo2" &&
- svn mkdir -m "mkdir bar" "${svnrepo}2/bar" &&
- git config --unset svn-remote.svn.fetch \
+ svnadmin create "${PWD}/svnrepo2" &&
+ svn mkdir -m "mkdir bar" "${svnrepo}2/bar" &&
+ git config --unset svn-remote.svn.fetch \
"^bar:refs/remotes/git-svn$" &&
test_must_fail git svn init "${svnrepo}2/bar"
'
-test_expect_success \
- 'init allows us to connect to another directory in the same repo' '
- git svn init --minimize-url -i bar "$svnrepo/bar" &&
- git config --get svn-remote.svn.fetch \
- "^bar:refs/remotes/bar$" &&
- git config --get svn-remote.svn.fetch \
- "^:refs/remotes/git-svn$"
- '
+test_expect_success 'init allows us to connect to another directory in the same repo' '
+ git svn init --minimize-url -i bar "$svnrepo/bar" &&
+ git config --get svn-remote.svn.fetch \
+ "^bar:refs/remotes/bar$" &&
+ git config --get svn-remote.svn.fetch \
+ "^:refs/remotes/git-svn$"
+'
test_expect_success 'dcommit $rev does not clobber current branch' '
git svn fetch -i bar &&
diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh
index 8b5681d..52046e6 100755
--- a/t/t9101-git-svn-props.sh
+++ b/t/t9101-git-svn-props.sh
@@ -4,6 +4,7 @@
#
test_description='git svn property tests'
+
. ./lib-git-svn.sh
mkdir import
diff --git a/t/t9102-git-svn-deep-rmdir.sh b/t/t9102-git-svn-deep-rmdir.sh
index 66cd511..946ef85 100755
--- a/t/t9102-git-svn-deep-rmdir.sh
+++ b/t/t9102-git-svn-deep-rmdir.sh
@@ -1,5 +1,6 @@
#!/bin/sh
test_description='git svn rmdir'
+
. ./lib-git-svn.sh
test_expect_success 'initialize repo' '
diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh
index 67eed2f..b5845e2 100755
--- a/t/t9104-git-svn-follow-parent.sh
+++ b/t/t9104-git-svn-follow-parent.sh
@@ -4,6 +4,7 @@
#
test_description='git svn fetching'
+
. ./lib-git-svn.sh
test_expect_success 'initialize repo' '
@@ -40,51 +41,51 @@ test_expect_success 'init and fetch a moved directory' '
'
test_expect_success 'init and fetch from one svn-remote' '
- git config svn-remote.svn.url "$svnrepo" &&
- git config --add svn-remote.svn.fetch \
- trunk:refs/remotes/svn/trunk &&
- git config --add svn-remote.svn.fetch \
- thunk:refs/remotes/svn/thunk &&
- git svn fetch -i svn/thunk &&
+ git config svn-remote.svn.url "$svnrepo" &&
+ git config --add svn-remote.svn.fetch \
+ trunk:refs/remotes/svn/trunk &&
+ git config --add svn-remote.svn.fetch \
+ thunk:refs/remotes/svn/thunk &&
+ git svn fetch -i svn/thunk &&
test "$(git rev-parse --verify refs/remotes/svn/trunk)" \
- = "$(git rev-parse --verify refs/remotes/svn/thunk~1)" &&
+ = "$(git rev-parse --verify refs/remotes/svn/thunk~1)" &&
git cat-file blob refs/remotes/svn/thunk:readme >actual &&
test "$(sed -n -e "3p" actual)" = goodbye
- '
+'
test_expect_success 'follow deleted parent' '
- (svn_cmd cp -m "resurrecting trunk as junk" \
- "$svnrepo"/trunk@2 "$svnrepo"/junk ||
- svn cp -m "resurrecting trunk as junk" \
- -r2 "$svnrepo"/trunk "$svnrepo"/junk) &&
- git config --add svn-remote.svn.fetch \
- junk:refs/remotes/svn/junk &&
- git svn fetch -i svn/thunk &&
- git svn fetch -i svn/junk &&
+ (svn_cmd cp -m "resurrecting trunk as junk" \
+ "$svnrepo"/trunk@2 "$svnrepo"/junk ||
+ svn cp -m "resurrecting trunk as junk" \
+ -r2 "$svnrepo"/trunk "$svnrepo"/junk) &&
+ git config --add svn-remote.svn.fetch \
+ junk:refs/remotes/svn/junk &&
+ git svn fetch -i svn/thunk &&
+ git svn fetch -i svn/junk &&
test -z "$(git diff svn/junk svn/trunk)" &&
test "$(git merge-base svn/junk svn/trunk)" \
- = "$(git rev-parse svn/trunk)"
- '
+ = "$(git rev-parse svn/trunk)"
+'
test_expect_success 'follow larger parent' '
- mkdir -p import/trunk/thunk/bump/thud &&
- echo hi > import/trunk/thunk/bump/thud/file &&
- svn import -m "import a larger parent" import "$svnrepo"/larger-parent &&
- svn cp -m "hi" "$svnrepo"/larger-parent "$svnrepo"/another-larger &&
- git svn init --minimize-url -i larger \
- "$svnrepo"/larger-parent/trunk/thunk/bump/thud &&
- git svn fetch -i larger &&
+ mkdir -p import/trunk/thunk/bump/thud &&
+ echo hi > import/trunk/thunk/bump/thud/file &&
+ svn import -m "import a larger parent" import "$svnrepo"/larger-parent &&
+ svn cp -m "hi" "$svnrepo"/larger-parent "$svnrepo"/another-larger &&
+ git svn init --minimize-url -i larger \
+ "$svnrepo"/larger-parent/trunk/thunk/bump/thud &&
+ git svn fetch -i larger &&
git svn init --minimize-url -i larger-parent \
- "$svnrepo"/another-larger/trunk/thunk/bump/thud &&
+ "$svnrepo"/another-larger/trunk/thunk/bump/thud &&
git svn fetch -i larger-parent &&
- git rev-parse --verify refs/remotes/larger &&
- git rev-parse --verify \
- refs/remotes/larger-parent &&
+ git rev-parse --verify refs/remotes/larger &&
+ git rev-parse --verify \
+ refs/remotes/larger-parent &&
test "$(git merge-base \
refs/remotes/larger-parent \
refs/remotes/larger)" = \
- "$(git rev-parse refs/remotes/larger)"
- '
+ "$(git rev-parse refs/remotes/larger)"
+'
test_expect_success 'follow higher-level parent' '
svn mkdir -m "follow higher-level parent" "$svnrepo"/blob &&
@@ -117,7 +118,7 @@ test_expect_success 'follow-parent avoids deleting relevant info' '
mkdir -p import/trunk/subversion/bindings/swig/perl/t &&
for i in a b c ; do \
echo $i > import/trunk/subversion/bindings/swig/perl/$i.pm &&
- echo _$i > import/trunk/subversion/bindings/swig/perl/t/$i.t; \
+ echo _$i > import/trunk/subversion/bindings/swig/perl/t/$i.t || return 1
done &&
echo "bad delete test" > \
import/trunk/subversion/bindings/swig/perl/t/larger-parent &&
@@ -134,7 +135,7 @@ test_expect_success 'follow-parent avoids deleting relevant info' '
svn mv t native/t &&
for i in a b c
do
- svn mv $i.pm native/$i.pm
+ svn mv $i.pm native/$i.pm || return 1
done &&
echo z >>native/t/c.t &&
poke native/t/c.t &&
diff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh
index aec45bc..bca496c 100755
--- a/t/t9106-git-svn-commit-diff-clobber.sh
+++ b/t/t9106-git-svn-commit-diff-clobber.sh
@@ -2,6 +2,7 @@
#
# Copyright (c) 2006 Eric Wong
test_description='git svn commit-diff clobber'
+
. ./lib-git-svn.sh
test_expect_success 'initialize repo' '
diff --git a/t/t9107-git-svn-migrate.sh b/t/t9107-git-svn-migrate.sh
index ceaa5ba..aa908bb 100755
--- a/t/t9107-git-svn-migrate.sh
+++ b/t/t9107-git-svn-migrate.sh
@@ -98,10 +98,10 @@ test_expect_success 'migrate --minimize on old inited layout' '
rm -rf "$GIT_DIR"/svn &&
for i in $(cat fetch.out)
do
- path=$(expr $i : "\([^:]*\):.*$")
- ref=$(expr $i : "[^:]*:\(refs/remotes/.*\)$")
- if test -z "$ref"; then continue; fi
- if test -n "$path"; then path="/$path"; fi
+ path=${i%%:*} &&
+ ref=${i#*:} &&
+ if test "$ref" = "${ref#refs/remotes/}"; then continue; fi &&
+ if test -n "$path"; then path="/$path"; fi &&
mkdir -p "$GIT_DIR"/svn/$ref/info/ &&
echo "$svnrepo"$path >"$GIT_DIR"/svn/$ref/info/url ||
return 1
diff --git a/t/t9114-git-svn-dcommit-merge.sh b/t/t9114-git-svn-dcommit-merge.sh
index 32317d6..e06538b 100755
--- a/t/t9114-git-svn-dcommit-merge.sh
+++ b/t/t9114-git-svn-dcommit-merge.sh
@@ -27,7 +27,7 @@ cat << EOF
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with this program; if not, see <http://www.gnu.org/licenses/>.
+# along with this program; if not, see <https://www.gnu.org/licenses/>.
#
EOF
}
diff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh
index 0a9f1ef..d74d7b2 100755
--- a/t/t9116-git-svn-log.sh
+++ b/t/t9116-git-svn-log.sh
@@ -4,6 +4,7 @@
#
test_description='git svn log tests'
+
. ./lib-git-svn.sh
test_expect_success 'setup repository and import' '
diff --git a/t/t9117-git-svn-init-clone.sh b/t/t9117-git-svn-init-clone.sh
index 62de819..3b038c3 100755
--- a/t/t9117-git-svn-init-clone.sh
+++ b/t/t9117-git-svn-init-clone.sh
@@ -17,32 +17,32 @@ test_expect_success 'setup svnrepo' '
test_expect_success 'basic clone' '
test ! -d trunk &&
git svn clone "$svnrepo"/project/trunk &&
- test -d trunk/.git/svn &&
- test -e trunk/foo &&
+ test_path_is_dir trunk/.git/svn &&
+ test_path_exists trunk/foo &&
rm -rf trunk
'
test_expect_success 'clone to target directory' '
test ! -d target &&
git svn clone "$svnrepo"/project/trunk target &&
- test -d target/.git/svn &&
- test -e target/foo &&
+ test_path_is_dir target/.git/svn &&
+ test_path_exists target/foo &&
rm -rf target
'
test_expect_success 'clone with --stdlayout' '
test ! -d project &&
git svn clone -s "$svnrepo"/project &&
- test -d project/.git/svn &&
- test -e project/foo &&
+ test_path_is_dir project/.git/svn &&
+ test_path_exists project/foo &&
rm -rf project
'
test_expect_success 'clone to target directory with --stdlayout' '
test ! -d target &&
git svn clone -s "$svnrepo"/project target &&
- test -d target/.git/svn &&
- test -e target/foo &&
+ test_path_is_dir target/.git/svn &&
+ test_path_exists target/foo &&
rm -rf target
'
diff --git a/t/t9118-git-svn-funky-branch-names.sh b/t/t9118-git-svn-funky-branch-names.sh
index a159ff9..d3261e3 100755
--- a/t/t9118-git-svn-funky-branch-names.sh
+++ b/t/t9118-git-svn-funky-branch-names.sh
@@ -38,7 +38,7 @@ test_expect_success 'setup svnrepo' '
# SVN 1.7 will truncate "not-a%40{0]" to just "not-a".
# Look at what SVN wound up naming the branch and use that.
# Be sure to escape the @ if it shows up.
-non_reflog=$(svn_cmd ls "$svnrepo/pr ject/branches" | grep not-a | sed 's/\///' | sed 's/@/%40/')
+non_reflog=$(svn_cmd ls "$svnrepo/pr ject/branches" | sed -ne '/not-a/ { s/\///; s/@/%40/; p }')
test_expect_success 'test clone with funky branch names' '
git svn clone -s "$svnrepo/pr ject" project &&
diff --git a/t/t9119-git-svn-info.sh b/t/t9119-git-svn-info.sh
index 8201c3e..088d1c5 100755
--- a/t/t9119-git-svn-info.sh
+++ b/t/t9119-git-svn-info.sh
@@ -28,7 +28,7 @@ test_cmp_info () {
rm -f tmp.expect tmp.actual
}
-quoted_svnrepo="$(echo $svnrepo | sed 's/ /%20/')"
+quoted_svnrepo="$(echo $svnrepo | test_uri_escape)"
test_expect_success 'setup repository and import' '
mkdir info &&
diff --git a/t/t9122-git-svn-author.sh b/t/t9122-git-svn-author.sh
index 9e8fe38..0fc289a 100755
--- a/t/t9122-git-svn-author.sh
+++ b/t/t9122-git-svn-author.sh
@@ -1,6 +1,7 @@
#!/bin/sh
test_description='git svn authorship'
+
. ./lib-git-svn.sh
test_expect_success 'setup svn repository' '
diff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh
index 2e4789d..97f495b 100755
--- a/t/t9127-git-svn-partial-rebuild.sh
+++ b/t/t9127-git-svn-partial-rebuild.sh
@@ -4,6 +4,7 @@
#
test_description='git svn partial-rebuild tests'
+
. ./lib-git-svn.sh
test_expect_success 'initialize svnrepo' '
diff --git a/t/t9128-git-svn-cmd-branch.sh b/t/t9128-git-svn-cmd-branch.sh
index 4e95f79..783e3ba 100755
--- a/t/t9128-git-svn-cmd-branch.sh
+++ b/t/t9128-git-svn-cmd-branch.sh
@@ -4,6 +4,7 @@
#
test_description='git svn partial-rebuild tests'
+
. ./lib-git-svn.sh
test_expect_success 'initialize svnrepo' '
diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh
index 01e1e8a..185248a 100755
--- a/t/t9129-git-svn-i18n-commitencoding.sh
+++ b/t/t9129-git-svn-i18n-commitencoding.sh
@@ -4,6 +4,7 @@
test_description='git svn honors i18n.commitEncoding in config'
+TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
compare_git_head_with () {
diff --git a/t/t9130-git-svn-authors-file.sh b/t/t9130-git-svn-authors-file.sh
index cb764bc..90325db 100755
--- a/t/t9130-git-svn-authors-file.sh
+++ b/t/t9130-git-svn-authors-file.sh
@@ -15,7 +15,7 @@ EOF
test_expect_success 'setup svnrepo' '
for i in aa bb cc dd
do
- svn_cmd mkdir -m $i --username $i "$svnrepo"/$i
+ svn_cmd mkdir -m $i --username $i "$svnrepo"/$i || return 1
done
'
@@ -59,8 +59,8 @@ test_expect_success 'authors-file against globs' '
git svn clone --authors-file=svn-authors -s "$svnrepo"/aa aa-work &&
for i in bb ee cc
do
- branch="aa/branches/$i"
- svn_cmd mkdir -m "$branch" --username $i "$svnrepo/$branch"
+ branch="aa/branches/$i" &&
+ svn_cmd mkdir -m "$branch" --username $i "$svnrepo/$branch" || return 1
done
'
diff --git a/t/t9133-git-svn-nested-git-repo.sh b/t/t9133-git-svn-nested-git-repo.sh
index f894860..8ca2467 100755
--- a/t/t9133-git-svn-nested-git-repo.sh
+++ b/t/t9133-git-svn-nested-git-repo.sh
@@ -11,7 +11,7 @@ test_expect_success 'setup repo with a git repo inside it' '
(
cd s &&
git init &&
- test -f .git/HEAD &&
+ git symbolic-ref HEAD &&
> .git/a &&
echo a > a &&
svn_cmd add .git a &&
@@ -35,7 +35,7 @@ test_expect_success 'SVN-side change outside of .git' '
echo b >> a &&
svn_cmd commit -m "SVN-side change outside of .git" &&
svn_cmd up &&
- svn_cmd log -v | fgrep "SVN-side change outside of .git"
+ svn_cmd log -v | grep -F "SVN-side change outside of .git"
)
'
@@ -59,7 +59,7 @@ test_expect_success 'SVN-side change inside of .git' '
svn_cmd add --force .git &&
svn_cmd commit -m "SVN-side change inside of .git" &&
svn_cmd up &&
- svn_cmd log -v | fgrep "SVN-side change inside of .git"
+ svn_cmd log -v | grep -F "SVN-side change inside of .git"
)
'
@@ -82,7 +82,7 @@ test_expect_success 'SVN-side change in and out of .git' '
git commit -m "add a inside an SVN repo" &&
svn_cmd commit -m "SVN-side change in and out of .git" &&
svn_cmd up &&
- svn_cmd log -v | fgrep "SVN-side change in and out of .git"
+ svn_cmd log -v | grep -F "SVN-side change in and out of .git"
)
'
diff --git a/t/t9134-git-svn-ignore-paths.sh b/t/t9134-git-svn-ignore-paths.sh
index fff49c4..3188400 100755
--- a/t/t9134-git-svn-ignore-paths.sh
+++ b/t/t9134-git-svn-ignore-paths.sh
@@ -27,7 +27,7 @@ test_expect_success 'setup test repository' '
test_expect_success 'clone an SVN repository with ignored www directory' '
git svn clone --ignore-paths="^www" "$svnrepo" g &&
echo test_qqq > expect &&
- for i in g/*/*.txt; do cat $i >> expect2; done &&
+ for i in g/*/*.txt; do cat $i >> expect2 || return 1; done &&
test_cmp expect expect2
'
@@ -36,14 +36,14 @@ test_expect_success 'init+fetch an SVN repository with ignored www directory' '
( cd c && git svn fetch --ignore-paths="^www" ) &&
rm expect2 &&
echo test_qqq > expect &&
- for i in c/*/*.txt; do cat $i >> expect2; done &&
+ for i in c/*/*.txt; do cat $i >> expect2 || return 1; done &&
test_cmp expect expect2
'
test_expect_success 'verify ignore-paths config saved by clone' '
(
cd g &&
- git config --get svn-remote.svn.ignore-paths | fgrep "www"
+ git config --get svn-remote.svn.ignore-paths | grep www
)
'
@@ -53,7 +53,7 @@ test_expect_success 'SVN-side change outside of www' '
echo b >> qqq/test_qqq.txt &&
svn_cmd commit -m "SVN-side change outside of www" &&
svn_cmd up &&
- svn_cmd log -v | fgrep "SVN-side change outside of www"
+ svn_cmd log -v | grep "SVN-side change outside of www"
)
'
@@ -62,7 +62,7 @@ test_expect_success 'update git svn-cloned repo (config ignore)' '
cd g &&
git svn rebase &&
printf "test_qqq\nb\n" > expect &&
- for i in */*.txt; do cat $i >> expect2; done &&
+ for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
test_cmp expect2 expect &&
rm expect expect2
)
@@ -73,7 +73,7 @@ test_expect_success 'update git svn-cloned repo (option ignore)' '
cd c &&
git svn rebase --ignore-paths="^www" &&
printf "test_qqq\nb\n" > expect &&
- for i in */*.txt; do cat $i >> expect2; done &&
+ for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
test_cmp expect2 expect &&
rm expect expect2
)
@@ -85,7 +85,7 @@ test_expect_success 'SVN-side change inside of ignored www' '
echo zaq >> www/test_www.txt &&
svn_cmd commit -m "SVN-side change inside of www/test_www.txt" &&
svn_cmd up &&
- svn_cmd log -v | fgrep "SVN-side change inside of www/test_www.txt"
+ svn_cmd log -v | grep -F "SVN-side change inside of www/test_www.txt"
)
'
@@ -94,7 +94,7 @@ test_expect_success 'update git svn-cloned repo (config ignore)' '
cd g &&
git svn rebase &&
printf "test_qqq\nb\n" > expect &&
- for i in */*.txt; do cat $i >> expect2; done &&
+ for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
test_cmp expect2 expect &&
rm expect expect2
)
@@ -105,7 +105,7 @@ test_expect_success 'update git svn-cloned repo (option ignore)' '
cd c &&
git svn rebase --ignore-paths="^www" &&
printf "test_qqq\nb\n" > expect &&
- for i in */*.txt; do cat $i >> expect2; done &&
+ for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
test_cmp expect2 expect &&
rm expect expect2
)
@@ -118,7 +118,7 @@ test_expect_success 'SVN-side change in and out of ignored www' '
echo ygg >> qqq/test_qqq.txt &&
svn_cmd commit -m "SVN-side change in and out of ignored www" &&
svn_cmd up &&
- svn_cmd log -v | fgrep "SVN-side change in and out of ignored www"
+ svn_cmd log -v | grep "SVN-side change in and out of ignored www"
)
'
@@ -127,7 +127,7 @@ test_expect_success 'update git svn-cloned repo again (config ignore)' '
cd g &&
git svn rebase &&
printf "test_qqq\nb\nygg\n" > expect &&
- for i in */*.txt; do cat $i >> expect2; done &&
+ for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
test_cmp expect2 expect &&
rm expect expect2
)
@@ -138,7 +138,7 @@ test_expect_success 'update git svn-cloned repo again (option ignore)' '
cd c &&
git svn rebase --ignore-paths="^www" &&
printf "test_qqq\nb\nygg\n" > expect &&
- for i in */*.txt; do cat $i >> expect2; done &&
+ for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
test_cmp expect2 expect &&
rm expect expect2
)
diff --git a/t/t9138-git-svn-authors-prog.sh b/t/t9138-git-svn-authors-prog.sh
index 027b416..784ec7f 100755
--- a/t/t9138-git-svn-authors-prog.sh
+++ b/t/t9138-git-svn-authors-prog.sh
@@ -27,7 +27,7 @@ test_expect_success 'svn-authors setup' '
test_expect_success 'setup svnrepo' '
for i in aa bb cc-sub dd-sub ee-foo ff
do
- svn mkdir -m $i --username $i "$svnrepo"/$i
+ svn mkdir -m $i --username $i "$svnrepo"/$i || return 1
done
'
diff --git a/t/t9139-git-svn-non-utf8-commitencoding.sh b/t/t9139-git-svn-non-utf8-commitencoding.sh
index 22d80b0..b7f756b 100755
--- a/t/t9139-git-svn-non-utf8-commitencoding.sh
+++ b/t/t9139-git-svn-non-utf8-commitencoding.sh
@@ -4,6 +4,7 @@
test_description='git svn refuses to dcommit non-UTF8 messages'
+TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
# ISO-2022-JP can pass for valid UTF-8, so skipping that in this test
diff --git a/t/t9140-git-svn-reset.sh b/t/t9140-git-svn-reset.sh
index e855904..a420b2a 100755
--- a/t/t9140-git-svn-reset.sh
+++ b/t/t9140-git-svn-reset.sh
@@ -43,7 +43,7 @@ test_expect_success 'fetch fails on modified hidden file' '
git svn find-rev refs/remotes/git-svn > ../expect &&
test_must_fail git svn fetch 2> ../errors &&
git svn find-rev refs/remotes/git-svn > ../expect2 ) &&
- fgrep "not found in commit" errors &&
+ grep "not found in commit" errors &&
test_cmp expect expect2
'
@@ -59,7 +59,7 @@ test_expect_success 'refetch succeeds not ignoring any files' '
( cd g &&
git svn fetch &&
git svn rebase &&
- fgrep "mod hidden" hid/hid.txt
+ grep "mod hidden" hid/hid.txt
)
'
diff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh
index 5f91c0d..926ac81 100755
--- a/t/t9146-git-svn-empty-dirs.sh
+++ b/t/t9146-git-svn-empty-dirs.sh
@@ -3,12 +3,13 @@
# Copyright (c) 2009 Eric Wong
test_description='git svn creates empty directories'
+
. ./lib-git-svn.sh
test_expect_success 'initialize repo' '
for i in a b c d d/e d/e/f "weird file name"
do
- svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i"
+ svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i" || return 1
done
'
@@ -19,11 +20,7 @@ test_expect_success 'empty directories exist' '
cd cloned &&
for i in a b c d d/e d/e/f "weird file name"
do
- if ! test -d "$i"
- then
- echo >&2 "$i does not exist" &&
- exit 1
- fi
+ test_path_is_dir "$i" || exit 1
done
)
'
@@ -36,11 +33,7 @@ test_expect_success 'option automkdirs set to false' '
git svn fetch &&
for i in a b c d d/e d/e/f "weird file name"
do
- if test -d "$i"
- then
- echo >&2 "$i exists" &&
- exit 1
- fi
+ test_path_is_missing "$i" || exit 1
done
)
'
@@ -51,7 +44,7 @@ test_expect_success 'more emptiness' '
test_expect_success 'git svn rebase creates empty directory' '
( cd cloned && git svn rebase ) &&
- test -d cloned/"! !"
+ test_path_is_dir cloned/"! !"
'
test_expect_success 'git svn mkdirs recreates empty directories' '
@@ -61,11 +54,7 @@ test_expect_success 'git svn mkdirs recreates empty directories' '
git svn mkdirs &&
for i in a b c d d/e d/e/f "weird file name" "! !"
do
- if ! test -d "$i"
- then
- echo >&2 "$i does not exist" &&
- exit 1
- fi
+ test_path_is_dir "$i" || exit 1
done
)
'
@@ -77,32 +66,20 @@ test_expect_success 'git svn mkdirs -r works' '
git svn mkdirs -r7 &&
for i in a b c d d/e d/e/f "weird file name"
do
- if ! test -d "$i"
- then
- echo >&2 "$i does not exist" &&
- exit 1
- fi
+ test_path_is_dir "$i" || exit 1
done &&
- if test -d "! !"
- then
- echo >&2 "$i should not exist" &&
- exit 1
- fi &&
+ test_path_is_missing "! !" || exit 1 &&
git svn mkdirs -r8 &&
- if ! test -d "! !"
- then
- echo >&2 "$i not exist" &&
- exit 1
- fi
+ test_path_is_dir "! !" || exit 1
)
'
test_expect_success 'initialize trunk' '
for i in trunk trunk/a trunk/"weird file name"
do
- svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i"
+ svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i" || return 1
done
'
@@ -113,11 +90,7 @@ test_expect_success 'empty directories in trunk exist' '
cd trunk &&
for i in a "weird file name"
do
- if ! test -d "$i"
- then
- echo >&2 "$i does not exist" &&
- exit 1
- fi
+ test_path_is_dir "$i" || exit 1
done
)
'
@@ -128,7 +101,7 @@ test_expect_success 'remove a top-level directory from svn' '
test_expect_success 'removed top-level directory does not exist' '
git svn clone "$svnrepo" removed &&
- test ! -e removed/d
+ test_path_is_missing removed/d
'
unhandled=.git/svn/refs/remotes/git-svn/unhandled.log
@@ -142,15 +115,11 @@ test_expect_success 'git svn gc-ed files work' '
svn_cmd mkdir -m gz "$svnrepo"/gz &&
git reset --hard $(git rev-list HEAD | tail -1) &&
git svn rebase &&
- test -f "$unhandled".gz &&
- test -f "$unhandled" &&
+ test_path_is_file "$unhandled".gz &&
+ test_path_is_file "$unhandled" &&
for i in a b c "weird file name" gz "! !"
do
- if ! test -d "$i"
- then
- echo >&2 "$i does not exist" &&
- exit 1
- fi
+ test_path_is_dir "$i" || exit 1
done
fi
)
diff --git a/t/t9147-git-svn-include-paths.sh b/t/t9147-git-svn-include-paths.sh
index d292bf9..63fa0b6 100755
--- a/t/t9147-git-svn-include-paths.sh
+++ b/t/t9147-git-svn-include-paths.sh
@@ -28,7 +28,7 @@ test_expect_success 'setup test repository' '
test_expect_success 'clone an SVN repository with filter to include qqq directory' '
git svn clone --include-paths="qqq" "$svnrepo" g &&
echo test_qqq > expect &&
- for i in g/*/*.txt; do cat $i >> expect2; done &&
+ for i in g/*/*.txt; do cat $i >> expect2 || return 1; done &&
test_cmp expect expect2
'
@@ -38,14 +38,14 @@ test_expect_success 'init+fetch an SVN repository with included qqq directory' '
( cd c && git svn fetch --include-paths="qqq" ) &&
rm expect2 &&
echo test_qqq > expect &&
- for i in c/*/*.txt; do cat $i >> expect2; done &&
+ for i in c/*/*.txt; do cat $i >> expect2 || return 1; done &&
test_cmp expect expect2
'
test_expect_success 'verify include-paths config saved by clone' '
(
cd g &&
- git config --get svn-remote.svn.include-paths | fgrep "qqq"
+ git config --get svn-remote.svn.include-paths | grep qqq
)
'
@@ -55,7 +55,7 @@ test_expect_success 'SVN-side change outside of www' '
echo b >> qqq/test_qqq.txt &&
svn_cmd commit -m "SVN-side change outside of www" &&
svn_cmd up &&
- svn_cmd log -v | fgrep "SVN-side change outside of www"
+ svn_cmd log -v | grep "SVN-side change outside of www"
)
'
@@ -64,7 +64,7 @@ test_expect_success 'update git svn-cloned repo (config include)' '
cd g &&
git svn rebase &&
printf "test_qqq\nb\n" > expect &&
- for i in */*.txt; do cat $i >> expect2; done &&
+ for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
test_cmp expect2 expect &&
rm expect expect2
)
@@ -75,7 +75,7 @@ test_expect_success 'update git svn-cloned repo (option include)' '
cd c &&
git svn rebase --include-paths="qqq" &&
printf "test_qqq\nb\n" > expect &&
- for i in */*.txt; do cat $i >> expect2; done &&
+ for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
test_cmp expect2 expect &&
rm expect expect2
)
@@ -87,7 +87,7 @@ test_expect_success 'SVN-side change inside of ignored www' '
echo zaq >> www/test_www.txt &&
svn_cmd commit -m "SVN-side change inside of www/test_www.txt" &&
svn_cmd up &&
- svn_cmd log -v | fgrep "SVN-side change inside of www/test_www.txt"
+ svn_cmd log -v | grep "SVN-side change inside of www/test_www.txt"
)
'
@@ -96,7 +96,7 @@ test_expect_success 'update git svn-cloned repo (config include)' '
cd g &&
git svn rebase &&
printf "test_qqq\nb\n" > expect &&
- for i in */*.txt; do cat $i >> expect2; done &&
+ for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
test_cmp expect2 expect &&
rm expect expect2
)
@@ -107,7 +107,7 @@ test_expect_success 'update git svn-cloned repo (option include)' '
cd c &&
git svn rebase --include-paths="qqq" &&
printf "test_qqq\nb\n" > expect &&
- for i in */*.txt; do cat $i >> expect2; done &&
+ for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
test_cmp expect2 expect &&
rm expect expect2
)
@@ -120,7 +120,7 @@ test_expect_success 'SVN-side change in and out of included qqq' '
echo ygg >> qqq/test_qqq.txt &&
svn_cmd commit -m "SVN-side change in and out of ignored www" &&
svn_cmd up &&
- svn_cmd log -v | fgrep "SVN-side change in and out of ignored www"
+ svn_cmd log -v | grep "SVN-side change in and out of ignored www"
)
'
@@ -129,7 +129,7 @@ test_expect_success 'update git svn-cloned repo again (config include)' '
cd g &&
git svn rebase &&
printf "test_qqq\nb\nygg\n" > expect &&
- for i in */*.txt; do cat $i >> expect2; done &&
+ for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
test_cmp expect2 expect &&
rm expect expect2
)
@@ -140,7 +140,7 @@ test_expect_success 'update git svn-cloned repo again (option include)' '
cd c &&
git svn rebase --include-paths="qqq" &&
printf "test_qqq\nb\nygg\n" > expect &&
- for i in */*.txt; do cat $i >> expect2; done &&
+ for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
test_cmp expect2 expect &&
rm expect expect2
)
diff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh
index 1fbe84f..c93a5be 100755
--- a/t/t9151-svn-mergeinfo.sh
+++ b/t/t9151-svn-mergeinfo.sh
@@ -5,9 +5,6 @@
test_description='git-svn svn mergeinfo properties'
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
. ./lib-git-svn.sh
test_expect_success 'load svn dump' "
diff --git a/t/t9152-svn-empty-dirs-after-gc.sh b/t/t9152-svn-empty-dirs-after-gc.sh
index 89f285d..a597c42 100755
--- a/t/t9152-svn-empty-dirs-after-gc.sh
+++ b/t/t9152-svn-empty-dirs-after-gc.sh
@@ -8,7 +8,7 @@ test_description='git svn creates empty directories, calls git gc, makes sure th
test_expect_success 'initialize repo' '
for i in a b c d d/e d/e/f "weird file name"
do
- svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i"
+ svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i" || return 1
done
'
diff --git a/t/t9162-git-svn-dcommit-interactive.sh b/t/t9162-git-svn-dcommit-interactive.sh
index e38d9fa..b3ce033 100755
--- a/t/t9162-git-svn-dcommit-interactive.sh
+++ b/t/t9162-git-svn-dcommit-interactive.sh
@@ -3,6 +3,7 @@
# Copyright (c) 2011 Frédéric Heitzmann
test_description='git svn dcommit --interactive series'
+
. ./lib-git-svn.sh
test_expect_success 'initialize repo' '
diff --git a/t/t9164-git-svn-dcommit-concurrent.sh b/t/t9164-git-svn-dcommit-concurrent.sh
index 8466269..d1dec89 100755
--- a/t/t9164-git-svn-dcommit-concurrent.sh
+++ b/t/t9164-git-svn-dcommit-concurrent.sh
@@ -4,6 +4,7 @@
#
test_description='concurrent git svn dcommit'
+
. ./lib-git-svn.sh
@@ -45,6 +46,14 @@ setup_hook()
"passed to setup_hook" >&2 ; return 1; }
echo "cnt=$skip_revs" > "$hook_type-counter"
rm -f "$rawsvnrepo/hooks/"*-commit # drop previous hooks
+
+ # Subversion hooks run with an empty environment by default. We thus
+ # need to propagate PATH so that we can find executables.
+ cat >"$rawsvnrepo/conf/hooks-env" <<-EOF
+ [default]
+ PATH = ${PATH}
+ EOF
+
hook="$rawsvnrepo/hooks/$hook_type"
cat > "$hook" <<- 'EOF1'
#!/bin/sh
@@ -62,7 +71,6 @@ EOF1
if [ "$hook_type" = "pre-commit" ]; then
echo "echo 'commit disallowed' >&2; exit 1" >>"$hook"
else
- echo "PATH=\"$PATH\"; export PATH" >>"$hook"
echo "svnconf=\"$svnconf\"" >>"$hook"
cat >>"$hook" <<- 'EOF2'
cd work-auto-commits.svn
diff --git a/t/t9167-git-svn-cmd-branch-subproject.sh b/t/t9167-git-svn-cmd-branch-subproject.sh
index ba35fc0..d812843 100755
--- a/t/t9167-git-svn-cmd-branch-subproject.sh
+++ b/t/t9167-git-svn-cmd-branch-subproject.sh
@@ -4,6 +4,7 @@
#
test_description='git svn branch for subproject clones'
+
. ./lib-git-svn.sh
test_expect_success 'initialize svnrepo' '
diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
index c5946cb..a44eabf 100755
--- a/t/t9200-git-cvsexportcommit.sh
+++ b/t/t9200-git-cvsexportcommit.sh
@@ -50,56 +50,56 @@ check_entries () {
fi
}
-test_expect_success \
- 'New file' \
- 'mkdir A B C D E F &&
- echo hello1 >A/newfile1.txt &&
- echo hello2 >B/newfile2.txt &&
- cp "$TEST_DIRECTORY"/test-binary-1.png C/newfile3.png &&
- cp "$TEST_DIRECTORY"/test-binary-1.png D/newfile4.png &&
- git add A/newfile1.txt &&
- git add B/newfile2.txt &&
- git add C/newfile3.png &&
- git add D/newfile4.png &&
- git commit -a -m "Test: New file" &&
- id=$(git rev-list --max-count=1 HEAD) &&
- (cd "$CVSWORK" &&
- git cvsexportcommit -c $id &&
- check_entries A "newfile1.txt/1.1/" &&
- check_entries B "newfile2.txt/1.1/" &&
- check_entries C "newfile3.png/1.1/-kb" &&
- check_entries D "newfile4.png/1.1/-kb" &&
- test_cmp A/newfile1.txt ../A/newfile1.txt &&
- test_cmp B/newfile2.txt ../B/newfile2.txt &&
- test_cmp C/newfile3.png ../C/newfile3.png &&
- test_cmp D/newfile4.png ../D/newfile4.png
- )'
+test_expect_success 'New file' '
+ mkdir A B C D E F &&
+ echo hello1 >A/newfile1.txt &&
+ echo hello2 >B/newfile2.txt &&
+ cp "$TEST_DIRECTORY"/test-binary-1.png C/newfile3.png &&
+ cp "$TEST_DIRECTORY"/test-binary-1.png D/newfile4.png &&
+ git add A/newfile1.txt &&
+ git add B/newfile2.txt &&
+ git add C/newfile3.png &&
+ git add D/newfile4.png &&
+ git commit -a -m "Test: New file" &&
+ id=$(git rev-list --max-count=1 HEAD) &&
+ (cd "$CVSWORK" &&
+ git cvsexportcommit -c $id &&
+ check_entries A "newfile1.txt/1.1/" &&
+ check_entries B "newfile2.txt/1.1/" &&
+ check_entries C "newfile3.png/1.1/-kb" &&
+ check_entries D "newfile4.png/1.1/-kb" &&
+ test_cmp A/newfile1.txt ../A/newfile1.txt &&
+ test_cmp B/newfile2.txt ../B/newfile2.txt &&
+ test_cmp C/newfile3.png ../C/newfile3.png &&
+ test_cmp D/newfile4.png ../D/newfile4.png
+ )
+'
-test_expect_success \
- 'Remove two files, add two and update two' \
- 'echo Hello1 >>A/newfile1.txt &&
- rm -f B/newfile2.txt &&
- rm -f C/newfile3.png &&
- echo Hello5 >E/newfile5.txt &&
- cp "$TEST_DIRECTORY"/test-binary-2.png D/newfile4.png &&
- cp "$TEST_DIRECTORY"/test-binary-1.png F/newfile6.png &&
- git add E/newfile5.txt &&
- git add F/newfile6.png &&
- git commit -a -m "Test: Remove, add and update" &&
- id=$(git rev-list --max-count=1 HEAD) &&
- (cd "$CVSWORK" &&
- git cvsexportcommit -c $id &&
- check_entries A "newfile1.txt/1.2/" &&
- check_entries B "" &&
- check_entries C "" &&
- check_entries D "newfile4.png/1.2/-kb" &&
- check_entries E "newfile5.txt/1.1/" &&
- check_entries F "newfile6.png/1.1/-kb" &&
- test_cmp A/newfile1.txt ../A/newfile1.txt &&
- test_cmp D/newfile4.png ../D/newfile4.png &&
- test_cmp E/newfile5.txt ../E/newfile5.txt &&
- test_cmp F/newfile6.png ../F/newfile6.png
- )'
+test_expect_success 'Remove two files, add two and update two' '
+ echo Hello1 >>A/newfile1.txt &&
+ rm -f B/newfile2.txt &&
+ rm -f C/newfile3.png &&
+ echo Hello5 >E/newfile5.txt &&
+ cp "$TEST_DIRECTORY"/test-binary-2.png D/newfile4.png &&
+ cp "$TEST_DIRECTORY"/test-binary-1.png F/newfile6.png &&
+ git add E/newfile5.txt &&
+ git add F/newfile6.png &&
+ git commit -a -m "Test: Remove, add and update" &&
+ id=$(git rev-list --max-count=1 HEAD) &&
+ (cd "$CVSWORK" &&
+ git cvsexportcommit -c $id &&
+ check_entries A "newfile1.txt/1.2/" &&
+ check_entries B "" &&
+ check_entries C "" &&
+ check_entries D "newfile4.png/1.2/-kb" &&
+ check_entries E "newfile5.txt/1.1/" &&
+ check_entries F "newfile6.png/1.1/-kb" &&
+ test_cmp A/newfile1.txt ../A/newfile1.txt &&
+ test_cmp D/newfile4.png ../D/newfile4.png &&
+ test_cmp E/newfile5.txt ../E/newfile5.txt &&
+ test_cmp F/newfile6.png ../F/newfile6.png
+ )
+'
# Should fail (but only on the git cvsexportcommit stage)
test_expect_success \
@@ -129,67 +129,67 @@ test_expect_success \
# This test is here because a patch for only binary files will
# fail with gnu patch, so cvsexportcommit must handle that.
-test_expect_success \
- 'Remove only binary files' \
- 'git reset --hard HEAD^^ &&
- rm -f D/newfile4.png &&
- git commit -a -m "test: remove only a binary file" &&
- id=$(git rev-list --max-count=1 HEAD) &&
- (cd "$CVSWORK" &&
- git cvsexportcommit -c $id &&
- check_entries A "newfile1.txt/1.2/" &&
- check_entries B "" &&
- check_entries C "" &&
- check_entries D "" &&
- check_entries E "newfile5.txt/1.1/" &&
- check_entries F "newfile6.png/1.1/-kb" &&
- test_cmp A/newfile1.txt ../A/newfile1.txt &&
- test_cmp E/newfile5.txt ../E/newfile5.txt &&
- test_cmp F/newfile6.png ../F/newfile6.png
- )'
+test_expect_success 'Remove only binary files' '
+ git reset --hard HEAD^^ &&
+ rm -f D/newfile4.png &&
+ git commit -a -m "test: remove only a binary file" &&
+ id=$(git rev-list --max-count=1 HEAD) &&
+ (cd "$CVSWORK" &&
+ git cvsexportcommit -c $id &&
+ check_entries A "newfile1.txt/1.2/" &&
+ check_entries B "" &&
+ check_entries C "" &&
+ check_entries D "" &&
+ check_entries E "newfile5.txt/1.1/" &&
+ check_entries F "newfile6.png/1.1/-kb" &&
+ test_cmp A/newfile1.txt ../A/newfile1.txt &&
+ test_cmp E/newfile5.txt ../E/newfile5.txt &&
+ test_cmp F/newfile6.png ../F/newfile6.png
+ )
+'
-test_expect_success \
- 'Remove only a text file' \
- 'rm -f A/newfile1.txt &&
- git commit -a -m "test: remove only a binary file" &&
- id=$(git rev-list --max-count=1 HEAD) &&
- (cd "$CVSWORK" &&
- git cvsexportcommit -c $id &&
- check_entries A "" &&
- check_entries B "" &&
- check_entries C "" &&
- check_entries D "" &&
- check_entries E "newfile5.txt/1.1/" &&
- check_entries F "newfile6.png/1.1/-kb" &&
- test_cmp E/newfile5.txt ../E/newfile5.txt &&
- test_cmp F/newfile6.png ../F/newfile6.png
- )'
+test_expect_success 'Remove only a text file' '
+ rm -f A/newfile1.txt &&
+ git commit -a -m "test: remove only a binary file" &&
+ id=$(git rev-list --max-count=1 HEAD) &&
+ (cd "$CVSWORK" &&
+ git cvsexportcommit -c $id &&
+ check_entries A "" &&
+ check_entries B "" &&
+ check_entries C "" &&
+ check_entries D "" &&
+ check_entries E "newfile5.txt/1.1/" &&
+ check_entries F "newfile6.png/1.1/-kb" &&
+ test_cmp E/newfile5.txt ../E/newfile5.txt &&
+ test_cmp F/newfile6.png ../F/newfile6.png
+ )
+'
-test_expect_success \
- 'New file with spaces in file name' \
- 'mkdir "G g" &&
- echo ok then >"G g/with spaces.txt" &&
- git add "G g/with spaces.txt" && \
- cp "$TEST_DIRECTORY"/test-binary-1.png "G g/with spaces.png" && \
- git add "G g/with spaces.png" &&
- git commit -a -m "With spaces" &&
- id=$(git rev-list --max-count=1 HEAD) &&
- (cd "$CVSWORK" &&
- git cvsexportcommit -c $id &&
- check_entries "G g" "with spaces.png/1.1/-kb|with spaces.txt/1.1/"
- )'
+test_expect_success 'New file with spaces in file name' '
+ mkdir "G g" &&
+ echo ok then >"G g/with spaces.txt" &&
+ git add "G g/with spaces.txt" && \
+ cp "$TEST_DIRECTORY"/test-binary-1.png "G g/with spaces.png" && \
+ git add "G g/with spaces.png" &&
+ git commit -a -m "With spaces" &&
+ id=$(git rev-list --max-count=1 HEAD) &&
+ (cd "$CVSWORK" &&
+ git cvsexportcommit -c $id &&
+ check_entries "G g" "with spaces.png/1.1/-kb|with spaces.txt/1.1/"
+ )
+'
-test_expect_success \
- 'Update file with spaces in file name' \
- 'echo Ok then >>"G g/with spaces.txt" &&
- cat "$TEST_DIRECTORY"/test-binary-1.png >>"G g/with spaces.png" && \
- git add "G g/with spaces.png" &&
- git commit -a -m "Update with spaces" &&
- id=$(git rev-list --max-count=1 HEAD) &&
- (cd "$CVSWORK" &&
- git cvsexportcommit -c $id &&
- check_entries "G g" "with spaces.png/1.2/-kb|with spaces.txt/1.2/"
- )'
+test_expect_success 'Update file with spaces in file name' '
+ echo Ok then >>"G g/with spaces.txt" &&
+ cat "$TEST_DIRECTORY"/test-binary-1.png >>"G g/with spaces.png" && \
+ git add "G g/with spaces.png" &&
+ git commit -a -m "Update with spaces" &&
+ id=$(git rev-list --max-count=1 HEAD) &&
+ (cd "$CVSWORK" &&
+ git cvsexportcommit -c $id &&
+ check_entries "G g" "with spaces.png/1.2/-kb|with spaces.txt/1.2/"
+ )
+'
# Some filesystems mangle pathnames with UTF-8 characters --
# check and skip
@@ -202,68 +202,68 @@ if p="Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö" &&
then
# This test contains UTF-8 characters
-test_expect_success !MINGW \
- 'File with non-ascii file name' \
- 'mkdir -p Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö &&
- echo Foo >Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.txt &&
- git add Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.txt &&
- cp "$TEST_DIRECTORY"/test-binary-1.png Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png &&
- git add Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png &&
- git commit -a -m "Går det så går det" && \
- id=$(git rev-list --max-count=1 HEAD) &&
- (cd "$CVSWORK" &&
- git cvsexportcommit -v -c $id &&
- check_entries \
- "Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö" \
- "gårdetsågårdet.png/1.1/-kb|gårdetsågårdet.txt/1.1/"
- )'
+test_expect_success !MINGW 'File with non-ascii file name' '
+ mkdir -p Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö &&
+ echo Foo >Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.txt &&
+ git add Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.txt &&
+ cp "$TEST_DIRECTORY"/test-binary-1.png Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png &&
+ git add Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png &&
+ git commit -a -m "Går det så går det" && \
+ id=$(git rev-list --max-count=1 HEAD) &&
+ (cd "$CVSWORK" &&
+ git cvsexportcommit -v -c $id &&
+ check_entries \
+ "Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö" \
+ "gårdetsågårdet.png/1.1/-kb|gårdetsågårdet.txt/1.1/"
+ )
+'
fi
rm -fr tst
-test_expect_success \
- 'Mismatching patch should fail' \
- 'date >>"E/newfile5.txt" &&
- git add "E/newfile5.txt" &&
- git commit -a -m "Update one" &&
- date >>"E/newfile5.txt" &&
- git add "E/newfile5.txt" &&
- git commit -a -m "Update two" &&
- id=$(git rev-list --max-count=1 HEAD) &&
- (cd "$CVSWORK" &&
- test_must_fail git cvsexportcommit -c $id
- )'
-
-test_expect_success FILEMODE \
- 'Retain execute bit' \
- 'mkdir G &&
- echo executeon >G/on &&
- chmod +x G/on &&
- echo executeoff >G/off &&
- git add G/on &&
- git add G/off &&
- git commit -a -m "Execute test" &&
- (cd "$CVSWORK" &&
- git cvsexportcommit -c HEAD &&
- test -x G/on &&
- ! test -x G/off
- )'
+test_expect_success 'Mismatching patch should fail' '
+ date >>"E/newfile5.txt" &&
+ git add "E/newfile5.txt" &&
+ git commit -a -m "Update one" &&
+ date >>"E/newfile5.txt" &&
+ git add "E/newfile5.txt" &&
+ git commit -a -m "Update two" &&
+ id=$(git rev-list --max-count=1 HEAD) &&
+ (cd "$CVSWORK" &&
+ test_must_fail git cvsexportcommit -c $id
+ )
+'
+
+test_expect_success FILEMODE 'Retain execute bit' '
+ mkdir G &&
+ echo executeon >G/on &&
+ chmod +x G/on &&
+ echo executeoff >G/off &&
+ git add G/on &&
+ git add G/off &&
+ git commit -a -m "Execute test" &&
+ (cd "$CVSWORK" &&
+ git cvsexportcommit -c HEAD &&
+ test -x G/on &&
+ ! test -x G/off
+ )
+'
test_expect_success '-w option should work with relative GIT_DIR' '
- mkdir W &&
- echo foobar >W/file1.txt &&
- echo bazzle >W/file2.txt &&
- git add W/file1.txt &&
- git add W/file2.txt &&
- git commit -m "More updates" &&
- id=$(git rev-list --max-count=1 HEAD) &&
- (cd "$GIT_DIR" &&
- GIT_DIR=. git cvsexportcommit -w "$CVSWORK" -c $id &&
- check_entries "$CVSWORK/W" "file1.txt/1.1/|file2.txt/1.1/" &&
- test_cmp "$CVSWORK/W/file1.txt" ../W/file1.txt &&
- test_cmp "$CVSWORK/W/file2.txt" ../W/file2.txt
- )
+ mkdir W &&
+ echo foobar >W/file1.txt &&
+ echo bazzle >W/file2.txt &&
+ git add W/file1.txt &&
+ git add W/file2.txt &&
+ git commit -m "More updates" &&
+ id=$(git rev-list --max-count=1 HEAD) &&
+ (cd "$GIT_DIR" &&
+ GIT_DIR=. git cvsexportcommit -w "$CVSWORK" -c $id &&
+ check_entries "$CVSWORK/W" "file1.txt/1.1/|file2.txt/1.1/" &&
+ test_cmp "$CVSWORK/W/file1.txt" ../W/file1.txt &&
+ test_cmp "$CVSWORK/W/file2.txt" ../W/file2.txt
+ )
'
test_expect_success 'check files before directories' '
@@ -290,21 +290,20 @@ test_expect_success 'check files before directories' '
'
test_expect_success 're-commit a removed filename which remains in CVS attic' '
-
- (cd "$CVSWORK" &&
- echo >attic_gremlin &&
- cvs -Q add attic_gremlin &&
- cvs -Q ci -m "added attic_gremlin" &&
- rm attic_gremlin &&
- cvs -Q rm attic_gremlin &&
- cvs -Q ci -m "removed attic_gremlin") &&
-
- echo > attic_gremlin &&
- git add attic_gremlin &&
- git commit -m "Added attic_gremlin" &&
+ (cd "$CVSWORK" &&
+ echo >attic_gremlin &&
+ cvs -Q add attic_gremlin &&
+ cvs -Q ci -m "added attic_gremlin" &&
+ rm attic_gremlin &&
+ cvs -Q rm attic_gremlin &&
+ cvs -Q ci -m "removed attic_gremlin") &&
+
+ echo > attic_gremlin &&
+ git add attic_gremlin &&
+ git commit -m "Added attic_gremlin" &&
git cvsexportcommit -w "$CVSWORK" -c HEAD &&
- (cd "$CVSWORK" && cvs -Q update -d) &&
- test -f "$CVSWORK/attic_gremlin"
+ (cd "$CVSWORK" && cvs -Q update -d) &&
+ test -f "$CVSWORK/attic_gremlin"
'
# the state of the CVS sandbox may be indeterminate for ' space'
diff --git a/t/t9210-scalar.sh b/t/t9210-scalar.sh
new file mode 100755
index 0000000..428339e
--- /dev/null
+++ b/t/t9210-scalar.sh
@@ -0,0 +1,241 @@
+#!/bin/sh
+
+test_description='test the `scalar` command'
+
+. ./test-lib.sh
+
+GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt,launchctl:true,schtasks:true"
+export GIT_TEST_MAINT_SCHEDULER
+
+test_expect_success 'scalar shows a usage' '
+ test_expect_code 129 scalar -h
+'
+
+test_expect_success 'scalar invoked on enlistment root' '
+ test_when_finished rm -rf test src deeper &&
+
+ for enlistment_root in test src deeper/test
+ do
+ git init ${enlistment_root}/src &&
+
+ # Register
+ scalar register ${enlistment_root} &&
+ scalar list >out &&
+ grep "$(pwd)/${enlistment_root}/src\$" out &&
+
+ # Delete (including enlistment root)
+ scalar delete $enlistment_root &&
+ test_path_is_missing $enlistment_root &&
+ scalar list >out &&
+ ! grep "^$(pwd)/${enlistment_root}/src\$" out || return 1
+ done
+'
+
+test_expect_success 'scalar invoked on enlistment src repo' '
+ test_when_finished rm -rf test src deeper &&
+
+ for enlistment_root in test src deeper/test
+ do
+ git init ${enlistment_root}/src &&
+
+ # Register
+ scalar register ${enlistment_root}/src &&
+ scalar list >out &&
+ grep "$(pwd)/${enlistment_root}/src\$" out &&
+
+ # Delete (will not include enlistment root)
+ scalar delete ${enlistment_root}/src &&
+ test_path_is_dir $enlistment_root &&
+ scalar list >out &&
+ ! grep "^$(pwd)/${enlistment_root}/src\$" out || return 1
+ done
+'
+
+test_expect_success 'scalar invoked when enlistment root and repo are the same' '
+ test_when_finished rm -rf test src deeper &&
+
+ for enlistment_root in test src deeper/test
+ do
+ git init ${enlistment_root} &&
+
+ # Register
+ scalar register ${enlistment_root} &&
+ scalar list >out &&
+ grep "$(pwd)/${enlistment_root}\$" out &&
+
+ # Delete (will not include enlistment root)
+ scalar delete ${enlistment_root} &&
+ test_path_is_missing $enlistment_root &&
+ scalar list >out &&
+ ! grep "^$(pwd)/${enlistment_root}\$" out &&
+
+ # Make sure we did not accidentally delete the trash dir
+ test_path_is_dir "$TRASH_DIRECTORY" || return 1
+ done
+'
+
+test_expect_success 'scalar repo search respects GIT_CEILING_DIRECTORIES' '
+ test_when_finished rm -rf test &&
+
+ git init test/src &&
+ mkdir -p test/src/deep &&
+ GIT_CEILING_DIRECTORIES="$(pwd)/test/src" &&
+ ! scalar register test/src/deep 2>err &&
+ grep "not a git repository" err
+'
+
+test_expect_success 'scalar enlistments need a worktree' '
+ test_when_finished rm -rf bare test &&
+
+ git init --bare bare/src &&
+ ! scalar register bare/src 2>err &&
+ grep "Scalar enlistments require a worktree" err &&
+
+ git init test/src &&
+ ! scalar register test/src/.git 2>err &&
+ grep "Scalar enlistments require a worktree" err
+'
+
+test_expect_success FSMONITOR_DAEMON 'scalar register starts fsmon daemon' '
+ git init test/src &&
+ test_must_fail git -C test/src fsmonitor--daemon status &&
+ scalar register test/src &&
+ git -C test/src fsmonitor--daemon status &&
+ test_cmp_config -C test/src true core.fsmonitor
+'
+
+test_expect_success 'scalar register warns when background maintenance fails' '
+ git init register-repo &&
+ GIT_TEST_MAINT_SCHEDULER="crontab:false,launchctl:false,schtasks:false" \
+ scalar register register-repo 2>err &&
+ grep "could not turn on maintenance" err
+'
+
+test_expect_success 'scalar unregister' '
+ git init vanish/src &&
+ scalar register vanish/src &&
+ git config --get --global --fixed-value \
+ maintenance.repo "$(pwd)/vanish/src" &&
+ scalar list >scalar.repos &&
+ grep -F "$(pwd)/vanish/src" scalar.repos &&
+ rm -rf vanish/src/.git &&
+ scalar unregister vanish &&
+ test_must_fail git config --get --global --fixed-value \
+ maintenance.repo "$(pwd)/vanish/src" &&
+ scalar list >scalar.repos &&
+ ! grep -F "$(pwd)/vanish/src" scalar.repos &&
+
+ # scalar unregister should be idempotent
+ scalar unregister vanish
+'
+
+test_expect_success 'set up repository to clone' '
+ test_commit first &&
+ test_commit second &&
+ test_commit third &&
+ git switch -c parallel first &&
+ mkdir -p 1/2 &&
+ test_commit 1/2/3 &&
+ git config uploadPack.allowFilter true &&
+ git config uploadPack.allowAnySHA1InWant true
+'
+
+test_expect_success 'scalar clone' '
+ second=$(git rev-parse --verify second:second.t) &&
+ scalar clone "file://$(pwd)" cloned --single-branch &&
+ (
+ cd cloned/src &&
+
+ git config --get --global --fixed-value maintenance.repo \
+ "$(pwd)" &&
+
+ git for-each-ref --format="%(refname)" refs/remotes/origin/ >actual &&
+ echo "refs/remotes/origin/parallel" >expect &&
+ test_cmp expect actual &&
+
+ test_path_is_missing 1/2 &&
+
+ # This relies on the fact that the presence of "--missing"
+ # on the command line forces lazy fetching off before
+ # "$second^{blob}" gets parsed. Without "^{blob}", a
+ # bare object name "$second" is taken into the queue and
+ # the command may not fail with a fixed "rev-list --missing".
+ test_must_fail git rev-list --missing=print "$second^{blob}" -- &&
+
+ git rev-list $second &&
+ git cat-file blob $second >actual &&
+ echo "second" >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'scalar reconfigure' '
+ git init one/src &&
+ scalar register one &&
+ git -C one/src config core.preloadIndex false &&
+ scalar reconfigure one &&
+ test true = "$(git -C one/src config core.preloadIndex)" &&
+ git -C one/src config core.preloadIndex false &&
+ scalar reconfigure -a &&
+ test true = "$(git -C one/src config core.preloadIndex)"
+'
+
+test_expect_success '`reconfigure -a` removes stale config entries' '
+ git init stale/src &&
+ scalar register stale &&
+ scalar list >scalar.repos &&
+ grep stale scalar.repos &&
+
+ grep -v stale scalar.repos >expect &&
+
+ rm -rf stale &&
+ scalar reconfigure -a &&
+ scalar list >scalar.repos &&
+ test_cmp expect scalar.repos
+'
+
+test_expect_success 'scalar delete without enlistment shows a usage' '
+ test_expect_code 129 scalar delete
+'
+
+test_expect_success 'scalar delete with enlistment' '
+ scalar delete cloned &&
+ test_path_is_missing cloned
+'
+
+test_expect_success 'scalar supports -c/-C' '
+ test_when_finished "scalar delete sub" &&
+ git init sub &&
+ scalar -C sub -c status.aheadBehind=bogus register &&
+ test -z "$(git -C sub config --local status.aheadBehind)" &&
+ test true = "$(git -C sub config core.preloadIndex)"
+'
+
+test_expect_success '`scalar [...] <dir>` errors out when dir is missing' '
+ ! scalar run config cloned 2>err &&
+ grep "cloned. does not exist" err
+'
+
+SQ="'"
+test_expect_success UNZIP 'scalar diagnose' '
+ scalar clone "file://$(pwd)" cloned --single-branch &&
+ git repack &&
+ echo "$(pwd)/.git/objects/" >>cloned/src/.git/objects/info/alternates &&
+ test_commit -C cloned/src loose &&
+ scalar diagnose cloned >out 2>err &&
+ grep "Available space" out &&
+ sed -n "s/.*$SQ\\(.*\\.zip\\)$SQ.*/\\1/p" <err >zip_path &&
+ zip_path=$(cat zip_path) &&
+ test -n "$zip_path" &&
+ "$GIT_UNZIP" -v "$zip_path" &&
+ folder=${zip_path%.zip} &&
+ test_path_is_missing "$folder" &&
+ "$GIT_UNZIP" -p "$zip_path" diagnostics.log >out &&
+ test_file_not_empty out &&
+ "$GIT_UNZIP" -p "$zip_path" packs-local.txt >out &&
+ grep "$(pwd)/.git/objects" out &&
+ "$GIT_UNZIP" -p "$zip_path" objects-local.txt >out &&
+ grep "^Total: [1-9]" out
+'
+
+test_done
diff --git a/t/t9211-scalar-clone.sh b/t/t9211-scalar-clone.sh
new file mode 100755
index 0000000..7869f45
--- /dev/null
+++ b/t/t9211-scalar-clone.sh
@@ -0,0 +1,195 @@
+#!/bin/sh
+
+test_description='test the `scalar clone` subcommand'
+
+. ./test-lib.sh
+. "${TEST_DIRECTORY}/lib-terminal.sh"
+
+GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt,launchctl:true,schtasks:true"
+export GIT_TEST_MAINT_SCHEDULER
+
+test_expect_success 'set up repository to clone' '
+ rm -rf .git &&
+ git init to-clone &&
+ (
+ cd to-clone &&
+ git branch -m base &&
+
+ test_commit first &&
+ test_commit second &&
+ test_commit third &&
+
+ git switch -c parallel first &&
+ mkdir -p 1/2 &&
+ test_commit 1/2/3 &&
+
+ git switch base &&
+
+ # By default, permit
+ git config uploadpack.allowfilter true &&
+ git config uploadpack.allowanysha1inwant true
+ )
+'
+
+cleanup_clone () {
+ rm -rf "$1"
+}
+
+test_expect_success 'creates content in enlistment root' '
+ enlistment=cloned &&
+
+ scalar clone "file://$(pwd)/to-clone" $enlistment &&
+ ls -A $enlistment >enlistment-root &&
+ test_line_count = 1 enlistment-root &&
+ test_path_is_dir $enlistment/src &&
+ test_path_is_dir $enlistment/src/.git &&
+
+ cleanup_clone $enlistment
+'
+
+test_expect_success 'with spaces' '
+ enlistment="cloned with space" &&
+
+ scalar clone "file://$(pwd)/to-clone" "$enlistment" &&
+ test_path_is_dir "$enlistment" &&
+ test_path_is_dir "$enlistment/src" &&
+ test_path_is_dir "$enlistment/src/.git" &&
+
+ cleanup_clone "$enlistment"
+'
+
+test_expect_success 'partial clone if supported by server' '
+ enlistment=partial-clone &&
+
+ scalar clone "file://$(pwd)/to-clone" $enlistment &&
+
+ (
+ cd $enlistment/src &&
+
+ # Two promisor packs: one for refs, the other for blobs
+ ls .git/objects/pack/pack-*.promisor >promisorlist &&
+ test_line_count = 2 promisorlist
+ ) &&
+
+ cleanup_clone $enlistment
+'
+
+test_expect_success 'fall back on full clone if partial unsupported' '
+ enlistment=no-partial-support &&
+
+ test_config -C to-clone uploadpack.allowfilter false &&
+ test_config -C to-clone uploadpack.allowanysha1inwant false &&
+
+ scalar clone "file://$(pwd)/to-clone" $enlistment 2>err &&
+ grep "filtering not recognized by server, ignoring" err &&
+
+ (
+ cd $enlistment/src &&
+
+ # Still get a refs promisor file, but none for blobs
+ ls .git/objects/pack/pack-*.promisor >promisorlist &&
+ test_line_count = 1 promisorlist
+ ) &&
+
+ cleanup_clone $enlistment
+'
+
+test_expect_success 'initializes sparse-checkout by default' '
+ enlistment=sparse &&
+
+ scalar clone "file://$(pwd)/to-clone" $enlistment &&
+ (
+ cd $enlistment/src &&
+ test_cmp_config true core.sparseCheckout &&
+ test_cmp_config true core.sparseCheckoutCone
+ ) &&
+
+ cleanup_clone $enlistment
+'
+
+test_expect_success '--full-clone does not create sparse-checkout' '
+ enlistment=full-clone &&
+
+ scalar clone --full-clone "file://$(pwd)/to-clone" $enlistment &&
+ (
+ cd $enlistment/src &&
+ test_cmp_config "" --default "" core.sparseCheckout &&
+ test_cmp_config "" --default "" core.sparseCheckoutCone
+ ) &&
+
+ cleanup_clone $enlistment
+'
+
+test_expect_success '--single-branch clones HEAD only' '
+ enlistment=single-branch &&
+
+ scalar clone --single-branch "file://$(pwd)/to-clone" $enlistment &&
+ (
+ cd $enlistment/src &&
+ git for-each-ref refs/remotes/origin >out &&
+ test_line_count = 1 out &&
+ grep "refs/remotes/origin/base" out
+ ) &&
+
+ cleanup_clone $enlistment
+'
+
+test_expect_success '--no-single-branch clones all branches' '
+ enlistment=no-single-branch &&
+
+ scalar clone --no-single-branch "file://$(pwd)/to-clone" $enlistment &&
+ (
+ cd $enlistment/src &&
+ git for-each-ref refs/remotes/origin >out &&
+ test_line_count = 2 out &&
+ grep "refs/remotes/origin/base" out &&
+ grep "refs/remotes/origin/parallel" out
+ ) &&
+
+ cleanup_clone $enlistment
+'
+
+test_expect_success TTY 'progress with tty' '
+ enlistment=progress1 &&
+
+ test_config -C to-clone uploadpack.allowfilter true &&
+ test_config -C to-clone uploadpack.allowanysha1inwant true &&
+
+ test_terminal env GIT_PROGRESS_DELAY=0 \
+ scalar clone "file://$(pwd)/to-clone" "$enlistment" 2>stderr &&
+ grep "Enumerating objects" stderr >actual &&
+ test_line_count = 2 actual &&
+ cleanup_clone $enlistment
+'
+
+test_expect_success 'progress without tty' '
+ enlistment=progress2 &&
+
+ test_config -C to-clone uploadpack.allowfilter true &&
+ test_config -C to-clone uploadpack.allowanysha1inwant true &&
+
+ GIT_PROGRESS_DELAY=0 scalar clone "file://$(pwd)/to-clone" "$enlistment" 2>stderr &&
+ ! grep "Enumerating objects" stderr &&
+ ! grep "Updating files" stderr &&
+ cleanup_clone $enlistment
+'
+
+test_expect_success 'scalar clone warns when background maintenance fails' '
+ GIT_TEST_MAINT_SCHEDULER="crontab:false,launchctl:false,schtasks:false" \
+ scalar clone "file://$(pwd)/to-clone" maint-fail 2>err &&
+ grep "could not turn on maintenance" err
+'
+
+test_expect_success '`scalar clone --no-src`' '
+ scalar clone --src "file://$(pwd)/to-clone" with-src &&
+ scalar clone --no-src "file://$(pwd)/to-clone" without-src &&
+
+ test_path_is_dir with-src/src &&
+ test_path_is_missing without-src/src &&
+
+ (cd with-src/src && ls ?*) >with &&
+ (cd without-src && ls ?*) >without &&
+ test_cmp with without
+'
+
+test_done
diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
index aa55b41..1e68426 100755
--- a/t/t9300-fast-import.sh
+++ b/t/t9300-fast-import.sh
@@ -388,9 +388,7 @@ test_expect_success 'B: accept branch name "TEMP_TAG"' '
INPUT_END
- test_when_finished "rm -f .git/TEMP_TAG
- git gc
- git prune" &&
+ test_when_finished "rm -f .git/TEMP_TAG && git gc --prune=now" &&
git fast-import <input &&
test $(test-tool ref-store main resolve-ref TEMP_TAG 0 | cut -f1 -d " " ) != "$ZERO_OID" &&
test $(git rev-parse main) = $(git rev-parse TEMP_TAG^)
@@ -406,8 +404,7 @@ test_expect_success 'B: accept empty committer' '
INPUT_END
test_when_finished "git update-ref -d refs/heads/empty-committer-1
- git gc
- git prune" &&
+ git gc --prune=now" &&
git fast-import <input &&
out=$(git fsck) &&
echo "$out" &&
@@ -452,8 +449,7 @@ test_expect_success 'B: accept and fixup committer with no name' '
INPUT_END
test_when_finished "git update-ref -d refs/heads/empty-committer-2
- git gc
- git prune" &&
+ git gc --prune=now" &&
git fast-import <input &&
out=$(git fsck) &&
echo "$out" &&
@@ -990,7 +986,7 @@ test_expect_success 'L: nested tree copy does not corrupt deltas' '
test_when_finished "git update-ref -d refs/heads/L2" &&
git fast-import <input &&
git ls-tree L2 g/b/ >tmp &&
- cat tmp | cut -f 2 >actual &&
+ cut -f 2 <tmp >actual &&
test_cmp expect actual &&
git fsck $(git rev-parse L2)
'
@@ -1063,30 +1059,33 @@ test_expect_success 'M: rename subdirectory to new subdirectory' '
compare_diff_raw expect actual
'
-test_expect_success 'M: rename root to subdirectory' '
- cat >input <<-INPUT_END &&
- commit refs/heads/M4
- committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
- data <<COMMIT
- rename root
- COMMIT
+for root in '""' ''
+do
+ test_expect_success "M: rename root ($root) to subdirectory" '
+ cat >input <<-INPUT_END &&
+ commit refs/heads/M4
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ rename root
+ COMMIT
- from refs/heads/M2^0
- R "" sub
+ from refs/heads/M2^0
+ R $root sub
- INPUT_END
+ INPUT_END
- cat >expect <<-EOF &&
- :100644 100644 $oldf $oldf R100 file2/oldf sub/file2/oldf
- :100755 100755 $f4id $f4id R100 file4 sub/file4
- :100755 100755 $newf $newf R100 i/am/new/to/you sub/i/am/new/to/you
- :100755 100755 $f6id $f6id R100 newdir/exec.sh sub/newdir/exec.sh
- :100644 100644 $f5id $f5id R100 newdir/interesting sub/newdir/interesting
- EOF
- git fast-import <input &&
- git diff-tree -M -r M4^ M4 >actual &&
- compare_diff_raw expect actual
-'
+ cat >expect <<-EOF &&
+ :100644 100644 $oldf $oldf R100 file2/oldf sub/file2/oldf
+ :100755 100755 $f4id $f4id R100 file4 sub/file4
+ :100755 100755 $newf $newf R100 i/am/new/to/you sub/i/am/new/to/you
+ :100755 100755 $f6id $f6id R100 newdir/exec.sh sub/newdir/exec.sh
+ :100644 100644 $f5id $f5id R100 newdir/interesting sub/newdir/interesting
+ EOF
+ git fast-import <input &&
+ git diff-tree -M -r M4^ M4 >actual &&
+ compare_diff_raw expect actual
+ '
+done
###
### series N
@@ -1263,49 +1262,52 @@ test_expect_success PIPE 'N: empty directory reads as missing' '
test_cmp expect actual
'
-test_expect_success 'N: copy root directory by tree hash' '
- cat >expect <<-EOF &&
- :100755 000000 $newf $zero D file3/newf
- :100644 000000 $oldf $zero D file3/oldf
- EOF
- root=$(git rev-parse refs/heads/branch^0^{tree}) &&
- cat >input <<-INPUT_END &&
- commit refs/heads/N6
- committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
- data <<COMMIT
- copy root directory by tree hash
- COMMIT
-
- from refs/heads/branch^0
- M 040000 $root ""
- INPUT_END
- git fast-import <input &&
- git diff-tree -C --find-copies-harder -r N4 N6 >actual &&
- compare_diff_raw expect actual
-'
+for root in '""' ''
+do
+ test_expect_success "N: copy root ($root) by tree hash" '
+ cat >expect <<-EOF &&
+ :100755 000000 $newf $zero D file3/newf
+ :100644 000000 $oldf $zero D file3/oldf
+ EOF
+ root_tree=$(git rev-parse refs/heads/branch^0^{tree}) &&
+ cat >input <<-INPUT_END &&
+ commit refs/heads/N6
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy root directory by tree hash
+ COMMIT
-test_expect_success 'N: copy root by path' '
- cat >expect <<-EOF &&
- :100755 100755 $newf $newf C100 file2/newf oldroot/file2/newf
- :100644 100644 $oldf $oldf C100 file2/oldf oldroot/file2/oldf
- :100755 100755 $f4id $f4id C100 file4 oldroot/file4
- :100755 100755 $f6id $f6id C100 newdir/exec.sh oldroot/newdir/exec.sh
- :100644 100644 $f5id $f5id C100 newdir/interesting oldroot/newdir/interesting
- EOF
- cat >input <<-INPUT_END &&
- commit refs/heads/N-copy-root-path
- committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
- data <<COMMIT
- copy root directory by (empty) path
- COMMIT
+ from refs/heads/branch^0
+ M 040000 $root_tree $root
+ INPUT_END
+ git fast-import <input &&
+ git diff-tree -C --find-copies-harder -r N4 N6 >actual &&
+ compare_diff_raw expect actual
+ '
+
+ test_expect_success "N: copy root ($root) by path" '
+ cat >expect <<-EOF &&
+ :100755 100755 $newf $newf C100 file2/newf oldroot/file2/newf
+ :100644 100644 $oldf $oldf C100 file2/oldf oldroot/file2/oldf
+ :100755 100755 $f4id $f4id C100 file4 oldroot/file4
+ :100755 100755 $f6id $f6id C100 newdir/exec.sh oldroot/newdir/exec.sh
+ :100644 100644 $f5id $f5id C100 newdir/interesting oldroot/newdir/interesting
+ EOF
+ cat >input <<-INPUT_END &&
+ commit refs/heads/N-copy-root-path
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy root directory by (empty) path
+ COMMIT
- from refs/heads/branch^0
- C "" oldroot
- INPUT_END
- git fast-import <input &&
- git diff-tree -C --find-copies-harder -r branch N-copy-root-path >actual &&
- compare_diff_raw expect actual
-'
+ from refs/heads/branch^0
+ C $root oldroot
+ INPUT_END
+ git fast-import <input &&
+ git diff-tree -C --find-copies-harder -r branch N-copy-root-path >actual &&
+ compare_diff_raw expect actual
+ '
+done
test_expect_success 'N: delete directory by copying' '
cat >expect <<-\EOF &&
@@ -1435,98 +1437,102 @@ test_expect_success 'N: reject foo/ syntax in ls argument' '
INPUT_END
'
-test_expect_success 'N: copy to root by id and modify' '
- echo "hello, world" >expect.foo &&
- echo hello >expect.bar &&
- git fast-import <<-SETUP_END &&
- commit refs/heads/N7
- committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
- data <<COMMIT
- hello, tree
- COMMIT
-
- deleteall
- M 644 inline foo/bar
- data <<EOF
- hello
- EOF
- SETUP_END
-
- tree=$(git rev-parse --verify N7:) &&
- git fast-import <<-INPUT_END &&
- commit refs/heads/N8
- committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
- data <<COMMIT
- copy to root by id and modify
- COMMIT
+for root in '""' ''
+do
+ test_expect_success "N: copy to root ($root) by id and modify" '
+ echo "hello, world" >expect.foo &&
+ echo hello >expect.bar &&
+ git fast-import <<-SETUP_END &&
+ commit refs/heads/N7
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ hello, tree
+ COMMIT
- M 040000 $tree ""
- M 644 inline foo/foo
- data <<EOF
- hello, world
- EOF
- INPUT_END
- git show N8:foo/foo >actual.foo &&
- git show N8:foo/bar >actual.bar &&
- test_cmp expect.foo actual.foo &&
- test_cmp expect.bar actual.bar
-'
+ deleteall
+ M 644 inline foo/bar
+ data <<EOF
+ hello
+ EOF
+ SETUP_END
-test_expect_success 'N: extract subtree' '
- branch=$(git rev-parse --verify refs/heads/branch^{tree}) &&
- cat >input <<-INPUT_END &&
- commit refs/heads/N9
- committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
- data <<COMMIT
- extract subtree branch:newdir
- COMMIT
+ tree=$(git rev-parse --verify N7:) &&
+ git fast-import <<-INPUT_END &&
+ commit refs/heads/N8
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy to root by id and modify
+ COMMIT
- M 040000 $branch ""
- C "newdir" ""
- INPUT_END
- git fast-import <input &&
- git diff --exit-code branch:newdir N9
-'
+ M 040000 $tree $root
+ M 644 inline foo/foo
+ data <<EOF
+ hello, world
+ EOF
+ INPUT_END
+ git show N8:foo/foo >actual.foo &&
+ git show N8:foo/bar >actual.bar &&
+ test_cmp expect.foo actual.foo &&
+ test_cmp expect.bar actual.bar
+ '
+
+ test_expect_success "N: extract subtree to the root ($root)" '
+ branch=$(git rev-parse --verify refs/heads/branch^{tree}) &&
+ cat >input <<-INPUT_END &&
+ commit refs/heads/N9
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ extract subtree branch:newdir
+ COMMIT
-test_expect_success 'N: modify subtree, extract it, and modify again' '
- echo hello >expect.baz &&
- echo hello, world >expect.qux &&
- git fast-import <<-SETUP_END &&
- commit refs/heads/N10
- committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
- data <<COMMIT
- hello, tree
- COMMIT
+ M 040000 $branch $root
+ C "newdir" $root
+ INPUT_END
+ git fast-import <input &&
+ git diff --exit-code branch:newdir N9
+ '
+
+ test_expect_success "N: modify subtree, extract it to the root ($root), and modify again" '
+ echo hello >expect.baz &&
+ echo hello, world >expect.qux &&
+ git fast-import <<-SETUP_END &&
+ commit refs/heads/N10
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ hello, tree
+ COMMIT
- deleteall
- M 644 inline foo/bar/baz
- data <<EOF
- hello
- EOF
- SETUP_END
+ deleteall
+ M 644 inline foo/bar/baz
+ data <<EOF
+ hello
+ EOF
+ SETUP_END
- tree=$(git rev-parse --verify N10:) &&
- git fast-import <<-INPUT_END &&
- commit refs/heads/N11
- committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
- data <<COMMIT
- copy to root by id and modify
- COMMIT
+ tree=$(git rev-parse --verify N10:) &&
+ git fast-import <<-INPUT_END &&
+ commit refs/heads/N11
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy to root by id and modify
+ COMMIT
- M 040000 $tree ""
- M 100644 inline foo/bar/qux
- data <<EOF
- hello, world
- EOF
- R "foo" ""
- C "bar/qux" "bar/quux"
- INPUT_END
- git show N11:bar/baz >actual.baz &&
- git show N11:bar/qux >actual.qux &&
- git show N11:bar/quux >actual.quux &&
- test_cmp expect.baz actual.baz &&
- test_cmp expect.qux actual.qux &&
- test_cmp expect.qux actual.quux'
+ M 040000 $tree $root
+ M 100644 inline foo/bar/qux
+ data <<EOF
+ hello, world
+ EOF
+ R "foo" $root
+ C "bar/qux" "bar/quux"
+ INPUT_END
+ git show N11:bar/baz >actual.baz &&
+ git show N11:bar/qux >actual.qux &&
+ git show N11:bar/quux >actual.quux &&
+ test_cmp expect.baz actual.baz &&
+ test_cmp expect.qux actual.qux &&
+ test_cmp expect.qux actual.quux
+ '
+done
###
### series O
@@ -1778,8 +1784,7 @@ test_expect_success 'P: verbatim SHA gitlinks' '
INPUT_END
git branch -D sub &&
- git gc &&
- git prune &&
+ git gc --prune=now &&
git fast-import <input &&
test $(git rev-parse --verify subuse2) = $(git rev-parse --verify subuse1)
'
@@ -2012,12 +2017,11 @@ test_expect_success 'Q: verify first notes commit' '
'
test_expect_success 'Q: verify first notes tree' '
- cat >expect.unsorted <<-EOF &&
+ sort >expect <<-EOF &&
100644 blob $commit1
100644 blob $commit2
100644 blob $commit3
EOF
- cat expect.unsorted | sort >expect &&
git cat-file -p refs/notes/foobar~2^{tree} | sed "s/ [0-9a-f]* / /" >actual &&
test_cmp expect actual
'
@@ -2053,12 +2057,11 @@ test_expect_success 'Q: verify second notes commit' '
'
test_expect_success 'Q: verify second notes tree' '
- cat >expect.unsorted <<-EOF &&
+ sort >expect <<-EOF &&
100644 blob $commit1
100644 blob $commit2
100644 blob $commit3
EOF
- cat expect.unsorted | sort >expect &&
git cat-file -p refs/notes/foobar^^{tree} | sed "s/ [0-9a-f]* / /" >actual &&
test_cmp expect actual
'
@@ -2093,10 +2096,9 @@ test_expect_success 'Q: verify third notes commit' '
'
test_expect_success 'Q: verify third notes tree' '
- cat >expect.unsorted <<-EOF &&
+ sort >expect <<-EOF &&
100644 blob $commit1
EOF
- cat expect.unsorted | sort >expect &&
git cat-file -p refs/notes/foobar2^{tree} | sed "s/ [0-9a-f]* / /" >actual &&
test_cmp expect actual
'
@@ -2120,10 +2122,9 @@ test_expect_success 'Q: verify fourth notes commit' '
'
test_expect_success 'Q: verify fourth notes tree' '
- cat >expect.unsorted <<-EOF &&
+ sort >expect <<-EOF &&
100644 blob $commit2
EOF
- cat expect.unsorted | sort >expect &&
git cat-file -p refs/notes/foobar^{tree} | sed "s/ [0-9a-f]* / /" >actual &&
test_cmp expect actual
'
@@ -2151,6 +2152,7 @@ test_expect_success 'Q: deny note on empty branch' '
EOF
test_must_fail git fast-import <input
'
+
###
### series R (feature and option)
###
@@ -2799,7 +2801,7 @@ test_expect_success 'R: blob appears only once' '
'
###
-### series S
+### series S (mark and path parsing)
###
#
# Make sure missing spaces and EOLs after mark references
@@ -2884,7 +2886,7 @@ test_expect_success 'S: filemodify with garbage after mark must fail' '
COMMIT
M 100644 :403x hello.c
EOF
- test_i18ngrep "space after mark" err
+ test_grep "space after mark" err
'
# inline is misspelled; fast-import thinks it is some unknown dataref
@@ -2900,7 +2902,7 @@ test_expect_success 'S: filemodify with garbage after inline must fail' '
inline
BLOB
EOF
- test_i18ngrep "nvalid dataref" err
+ test_grep "nvalid dataref" err
'
test_expect_success 'S: filemodify with garbage after sha1 must fail' '
@@ -2913,7 +2915,7 @@ test_expect_success 'S: filemodify with garbage after sha1 must fail' '
COMMIT
M 100644 ${sha1}x hello.c
EOF
- test_i18ngrep "space after SHA1" err
+ test_grep "space after SHA1" err
'
#
@@ -2928,7 +2930,7 @@ test_expect_success 'S: notemodify with garbage after mark dataref must fail' '
COMMIT
N :202x :302
EOF
- test_i18ngrep "space after mark" err
+ test_grep "space after mark" err
'
test_expect_success 'S: notemodify with garbage after inline dataref must fail' '
@@ -2943,7 +2945,7 @@ test_expect_success 'S: notemodify with garbage after inline dataref must fail'
note blob
BLOB
EOF
- test_i18ngrep "nvalid dataref" err
+ test_grep "nvalid dataref" err
'
test_expect_success 'S: notemodify with garbage after sha1 dataref must fail' '
@@ -2956,7 +2958,7 @@ test_expect_success 'S: notemodify with garbage after sha1 dataref must fail' '
COMMIT
N ${sha1}x :302
EOF
- test_i18ngrep "space after SHA1" err
+ test_grep "space after SHA1" err
'
#
@@ -2971,7 +2973,7 @@ test_expect_success 'S: notemodify with garbage after mark commit-ish must fail'
COMMIT
N :202 :302x
EOF
- test_i18ngrep "after mark" err
+ test_grep "after mark" err
'
#
@@ -3004,7 +3006,7 @@ test_expect_success 'S: from with garbage after mark must fail' '
EOF
# now evaluate the error
- test_i18ngrep "after mark" err
+ test_grep "after mark" err
'
@@ -3023,7 +3025,7 @@ test_expect_success 'S: merge with garbage after mark must fail' '
merge :303x
M 100644 :403 hello.c
EOF
- test_i18ngrep "after mark" err
+ test_grep "after mark" err
'
#
@@ -3038,7 +3040,7 @@ test_expect_success 'S: tag with garbage after mark must fail' '
tag S
TAG
EOF
- test_i18ngrep "after mark" err
+ test_grep "after mark" err
'
#
@@ -3048,7 +3050,7 @@ test_expect_success 'S: cat-blob with garbage after mark must fail' '
test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
cat-blob :403x
EOF
- test_i18ngrep "after mark" err
+ test_grep "after mark" err
'
#
@@ -3058,7 +3060,7 @@ test_expect_success 'S: ls with garbage after mark must fail' '
test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
ls :302x hello.c
EOF
- test_i18ngrep "space after mark" err
+ test_grep "space after mark" err
'
test_expect_success 'S: ls with garbage after sha1 must fail' '
@@ -3066,24 +3068,286 @@ test_expect_success 'S: ls with garbage after sha1 must fail' '
test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
ls ${sha1}x hello.c
EOF
- test_i18ngrep "space after tree-ish" err
+ test_grep "space after tree-ish" err
'
+#
+# Path parsing
+#
+# There are two sorts of ways a path can be parsed, depending on whether it is
+# the last field on the line. Additionally, ls without a <dataref> has a special
+# case. Test every occurrence of <path> in the grammar against every error case.
+# Paths for the root (empty strings) are tested elsewhere.
+#
+
+#
+# Valid paths at the end of a line: filemodify, filedelete, filecopy (dest),
+# filerename (dest), and ls.
+#
+# commit :301 from root -- modify hello.c (for setup)
+# commit :302 from :301 -- modify $path
+# commit :303 from :302 -- delete $path
+# commit :304 from :301 -- copy hello.c $path
+# commit :305 from :301 -- rename hello.c $path
+# ls :305 $path
+#
+test_path_eol_success () {
+ local test="$1" path="$2" unquoted_path="$3"
+ test_expect_success "S: paths at EOL with $test must work" '
+ test_when_finished "git branch -D S-path-eol" &&
+
+ git fast-import --export-marks=marks.out <<-EOF >out 2>err &&
+ blob
+ mark :401
+ data <<BLOB
+ hello world
+ BLOB
+
+ blob
+ mark :402
+ data <<BLOB
+ hallo welt
+ BLOB
+
+ commit refs/heads/S-path-eol
+ mark :301
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ initial commit
+ COMMIT
+ M 100644 :401 hello.c
+
+ commit refs/heads/S-path-eol
+ mark :302
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ commit filemodify
+ COMMIT
+ from :301
+ M 100644 :402 $path
+
+ commit refs/heads/S-path-eol
+ mark :303
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ commit filedelete
+ COMMIT
+ from :302
+ D $path
+
+ commit refs/heads/S-path-eol
+ mark :304
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ commit filecopy dest
+ COMMIT
+ from :301
+ C hello.c $path
+
+ commit refs/heads/S-path-eol
+ mark :305
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ commit filerename dest
+ COMMIT
+ from :301
+ R hello.c $path
+
+ ls :305 $path
+ EOF
+
+ commit_m=$(grep :302 marks.out | cut -d\ -f2) &&
+ commit_d=$(grep :303 marks.out | cut -d\ -f2) &&
+ commit_c=$(grep :304 marks.out | cut -d\ -f2) &&
+ commit_r=$(grep :305 marks.out | cut -d\ -f2) &&
+ blob1=$(grep :401 marks.out | cut -d\ -f2) &&
+ blob2=$(grep :402 marks.out | cut -d\ -f2) &&
+
+ (
+ printf "100644 blob $blob2\t$unquoted_path\n" &&
+ printf "100644 blob $blob1\thello.c\n"
+ ) | sort >tree_m.exp &&
+ git ls-tree $commit_m | sort >tree_m.out &&
+ test_cmp tree_m.exp tree_m.out &&
+
+ printf "100644 blob $blob1\thello.c\n" >tree_d.exp &&
+ git ls-tree $commit_d >tree_d.out &&
+ test_cmp tree_d.exp tree_d.out &&
+
+ (
+ printf "100644 blob $blob1\t$unquoted_path\n" &&
+ printf "100644 blob $blob1\thello.c\n"
+ ) | sort >tree_c.exp &&
+ git ls-tree $commit_c | sort >tree_c.out &&
+ test_cmp tree_c.exp tree_c.out &&
+
+ printf "100644 blob $blob1\t$unquoted_path\n" >tree_r.exp &&
+ git ls-tree $commit_r >tree_r.out &&
+ test_cmp tree_r.exp tree_r.out &&
+
+ test_cmp out tree_r.exp
+ '
+}
+
+test_path_eol_success 'quoted spaces' '" hello world.c "' ' hello world.c '
+test_path_eol_success 'unquoted spaces' ' hello world.c ' ' hello world.c '
+test_path_eol_success 'octal escapes' '"\150\151\056\143"' 'hi.c'
+
+#
+# Valid paths before a space: filecopy (source) and filerename (source).
+#
+# commit :301 from root -- modify $path (for setup)
+# commit :302 from :301 -- copy $path hello2.c
+# commit :303 from :301 -- rename $path hello2.c
+#
+test_path_space_success () {
+ local test="$1" path="$2" unquoted_path="$3"
+ test_expect_success "S: paths before space with $test must work" '
+ test_when_finished "git branch -D S-path-space" &&
+
+ git fast-import --export-marks=marks.out <<-EOF 2>err &&
+ blob
+ mark :401
+ data <<BLOB
+ hello world
+ BLOB
+
+ commit refs/heads/S-path-space
+ mark :301
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ initial commit
+ COMMIT
+ M 100644 :401 $path
+
+ commit refs/heads/S-path-space
+ mark :302
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ commit filecopy source
+ COMMIT
+ from :301
+ C $path hello2.c
+
+ commit refs/heads/S-path-space
+ mark :303
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ commit filerename source
+ COMMIT
+ from :301
+ R $path hello2.c
+
+ EOF
+
+ commit_c=$(grep :302 marks.out | cut -d\ -f2) &&
+ commit_r=$(grep :303 marks.out | cut -d\ -f2) &&
+ blob=$(grep :401 marks.out | cut -d\ -f2) &&
+
+ (
+ printf "100644 blob $blob\t$unquoted_path\n" &&
+ printf "100644 blob $blob\thello2.c\n"
+ ) | sort >tree_c.exp &&
+ git ls-tree $commit_c | sort >tree_c.out &&
+ test_cmp tree_c.exp tree_c.out &&
+
+ printf "100644 blob $blob\thello2.c\n" >tree_r.exp &&
+ git ls-tree $commit_r >tree_r.out &&
+ test_cmp tree_r.exp tree_r.out
+ '
+}
+
+test_path_space_success 'quoted spaces' '" hello world.c "' ' hello world.c '
+test_path_space_success 'no unquoted spaces' 'hello_world.c' 'hello_world.c'
+test_path_space_success 'octal escapes' '"\150\151\056\143"' 'hi.c'
+
+#
+# Test a single commit change with an invalid path. Run it with all occurrences
+# of <path> in the grammar against all error kinds.
+#
+test_path_fail () {
+ local change="$1" what="$2" prefix="$3" path="$4" suffix="$5" err_grep="$6"
+ test_expect_success "S: $change with $what must fail" '
+ test_must_fail git fast-import <<-EOF 2>err &&
+ blob
+ mark :1
+ data <<BLOB
+ hello world
+ BLOB
+
+ commit refs/heads/S-path-fail
+ mark :2
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ commit setup
+ COMMIT
+ M 100644 :1 hello.c
+
+ commit refs/heads/S-path-fail
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ commit with bad path
+ COMMIT
+ from :2
+ $prefix$path$suffix
+ EOF
+
+ test_grep "$err_grep" err
+ '
+}
+
+test_path_base_fail () {
+ local change="$1" prefix="$2" field="$3" suffix="$4"
+ test_path_fail "$change" 'unclosed " in '"$field" "$prefix" '"hello.c' "$suffix" "Invalid $field"
+ test_path_fail "$change" "invalid escape in quoted $field" "$prefix" '"hello\xff"' "$suffix" "Invalid $field"
+ test_path_fail "$change" "escaped NUL in quoted $field" "$prefix" '"hello\000"' "$suffix" "NUL in $field"
+}
+test_path_eol_quoted_fail () {
+ local change="$1" prefix="$2" field="$3"
+ test_path_base_fail "$change" "$prefix" "$field" ''
+ test_path_fail "$change" "garbage after quoted $field" "$prefix" '"hello.c"' 'x' "Garbage after $field"
+ test_path_fail "$change" "space after quoted $field" "$prefix" '"hello.c"' ' ' "Garbage after $field"
+}
+test_path_eol_fail () {
+ local change="$1" prefix="$2" field="$3"
+ test_path_eol_quoted_fail "$change" "$prefix" "$field"
+}
+test_path_space_fail () {
+ local change="$1" prefix="$2" field="$3"
+ test_path_base_fail "$change" "$prefix" "$field" ' world.c'
+ test_path_fail "$change" "missing space after quoted $field" "$prefix" '"hello.c"' 'x world.c' "Missing space after $field"
+ test_path_fail "$change" "missing space after unquoted $field" "$prefix" 'hello.c' '' "Missing space after $field"
+}
+
+test_path_eol_fail filemodify 'M 100644 :1 ' path
+test_path_eol_fail filedelete 'D ' path
+test_path_space_fail filecopy 'C ' source
+test_path_eol_fail filecopy 'C hello.c ' dest
+test_path_space_fail filerename 'R ' source
+test_path_eol_fail filerename 'R hello.c ' dest
+test_path_eol_fail 'ls (in commit)' 'ls :2 ' path
+
+# When 'ls' has no <dataref>, the <path> must be quoted.
+test_path_eol_quoted_fail 'ls (without dataref in commit)' 'ls ' path
+
###
### series T (ls)
###
# Setup is carried over from series S.
-test_expect_success 'T: ls root tree' '
- sed -e "s/Z\$//" >expect <<-EOF &&
- 040000 tree $(git rev-parse S^{tree}) Z
- EOF
- sha1=$(git rev-parse --verify S) &&
- git fast-import --import-marks=marks <<-EOF >actual &&
- ls $sha1 ""
- EOF
- test_cmp expect actual
-'
+for root in '""' ''
+do
+ test_expect_success "T: ls root ($root) tree" '
+ sed -e "s/Z\$//" >expect <<-EOF &&
+ 040000 tree $(git rev-parse S^{tree}) Z
+ EOF
+ sha1=$(git rev-parse --verify S) &&
+ git fast-import --import-marks=marks <<-EOF >actual &&
+ ls $sha1 $root
+ EOF
+ test_cmp expect actual
+ '
+done
test_expect_success 'T: delete branch' '
git branch to-delete &&
@@ -3185,30 +3449,33 @@ test_expect_success 'U: validate directory delete result' '
compare_diff_raw expect actual
'
-test_expect_success 'U: filedelete root succeeds' '
- cat >input <<-INPUT_END &&
- commit refs/heads/U
- committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
- data <<COMMIT
- must succeed
- COMMIT
- from refs/heads/U^0
- D ""
+for root in '""' ''
+do
+ test_expect_success "U: filedelete root ($root) succeeds" '
+ cat >input <<-INPUT_END &&
+ commit refs/heads/U-delete-root
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ must succeed
+ COMMIT
+ from refs/heads/U^0
+ D $root
- INPUT_END
+ INPUT_END
- git fast-import <input
-'
+ git fast-import <input
+ '
-test_expect_success 'U: validate root delete result' '
- cat >expect <<-EOF &&
- :100644 000000 $f7id $ZERO_OID D hello.c
- EOF
+ test_expect_success "U: validate root ($root) delete result" '
+ cat >expect <<-EOF &&
+ :100644 000000 $f7id $ZERO_OID D hello.c
+ EOF
- git diff-tree -M -r U^1 U >actual &&
+ git diff-tree -M -r U U-delete-root >actual &&
- compare_diff_raw expect actual
-'
+ compare_diff_raw expect actual
+ '
+done
###
### series V (checkpoint)
diff --git a/t/t9301-fast-import-notes.sh b/t/t9301-fast-import-notes.sh
index 1ae4d7c..5841322 100755
--- a/t/t9301-fast-import-notes.sh
+++ b/t/t9301-fast-import-notes.sh
@@ -7,6 +7,7 @@ test_description='test git fast-import of notes objects'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
diff --git a/t/t9302-fast-import-unpack-limit.sh b/t/t9302-fast-import-unpack-limit.sh
index f519e4f..d8b1f94 100755
--- a/t/t9302-fast-import-unpack-limit.sh
+++ b/t/t9302-fast-import-unpack-limit.sh
@@ -1,5 +1,7 @@
#!/bin/sh
test_description='test git fast-import unpack limit'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'create loose objects on import' '
diff --git a/t/t9303-fast-import-compression.sh b/t/t9303-fast-import-compression.sh
index 57d9165..4f5bf40 100755
--- a/t/t9303-fast-import-compression.sh
+++ b/t/t9303-fast-import-compression.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='compression setting of fast-import utility'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
import_large () {
diff --git a/t/t9304-fast-import-marks.sh b/t/t9304-fast-import-marks.sh
index d4359db..410a871 100755
--- a/t/t9304-fast-import-marks.sh
+++ b/t/t9304-fast-import-marks.sh
@@ -16,7 +16,7 @@ test_expect_success 'setup large marks file' '
blob=$(git rev-parse HEAD:one.t) &&
for i in $(test_seq 1024 16384)
do
- echo ":$i $blob"
+ echo ":$i $blob" || return 1
done >>marks
'
@@ -25,6 +25,7 @@ test_expect_success 'import with large marks file' '
'
test_expect_success 'setup dump with submodule' '
+ test_config_global protocol.file.allow always &&
git submodule add "$PWD" sub &&
git commit -m "add submodule" &&
git fast-export HEAD >dump
@@ -48,4 +49,33 @@ test_expect_success 'import with submodule mapping' '
test_cmp expect actual
'
+test_expect_success 'paths adjusted for relative subdir' '
+ git init deep-dst &&
+ mkdir deep-dst/subdir &&
+ >deep-dst/subdir/empty-marks &&
+ git -C deep-dst/subdir fast-import \
+ --rewrite-submodules-from=sub:../../from \
+ --rewrite-submodules-to=sub:../../to \
+ --import-marks=empty-marks \
+ --export-marks=exported-marks \
+ --export-pack-edges=exported-edges \
+ <dump &&
+ # we do not bother checking resulting repo; we just care that nothing
+ # complained about failing to open files for reading, and that files
+ # for writing were created in the expected spot
+ test_path_is_file deep-dst/subdir/exported-marks &&
+ test_path_is_file deep-dst/subdir/exported-edges
+'
+
+test_expect_success 'relative marks are not affected by subdir' '
+ git init deep-relative &&
+ mkdir deep-relative/subdir &&
+ git -C deep-relative/subdir fast-import \
+ --relative-marks \
+ --export-marks=exported-marks \
+ <dump &&
+ test_path_is_missing deep-relative/subdir/exported-marks &&
+ test_path_is_file deep-relative/.git/info/fast-import/exported-marks
+'
+
test_done
diff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh
index 409b48e..1eb035e 100755
--- a/t/t9350-fast-export.sh
+++ b/t/t9350-fast-export.sh
@@ -236,7 +236,7 @@ EOF
test_expect_success 'set up faked signed tag' '
- cat signed-tag-import | git fast-import
+ git fast-import <signed-tag-import
'
@@ -268,6 +268,7 @@ test_expect_success 'signed-tags=warn-strip' '
test_expect_success 'setup submodule' '
+ test_config_global protocol.file.allow always &&
git checkout -f main &&
mkdir sub &&
(
@@ -293,6 +294,7 @@ test_expect_success 'setup submodule' '
test_expect_success 'submodule fast-export | fast-import' '
+ test_config_global protocol.file.allow always &&
SUBENT1=$(git ls-tree main^ sub) &&
SUBENT2=$(git ls-tree main sub) &&
rm -rf new &&
@@ -371,7 +373,7 @@ EOF
test_expect_success 'cope with tagger-less tags' '
- TAG=$(git hash-object -t tag -w tag-content) &&
+ TAG=$(git hash-object --literally -t tag -w tag-content) &&
git update-ref refs/tags/sonnenschein $TAG &&
git fast-export -C -C --signed-tags=strip --all > output &&
test $(grep -c "^tag " output) = 4 &&
@@ -500,6 +502,13 @@ test_expect_success 'path limiting with import-marks does not lose unmodified fi
grep file0 actual
'
+test_expect_success 'path limiting works' '
+ git fast-export simple -- file >actual &&
+ sed -ne "s/^M .* //p" <actual | sort -u >actual.files &&
+ echo file >expect &&
+ test_cmp expect actual.files
+'
+
test_expect_success 'avoid corrupt stream with non-existent mark' '
test_create_repo avoid_non_existent_mark &&
(
@@ -528,7 +537,7 @@ test_expect_success 'full-tree re-shows unmodified files' '
test_expect_success 'set-up a few more tags for tag export tests' '
git checkout -f main &&
- HEAD_TREE=$(git show -s --pretty=raw HEAD | grep tree | sed "s/tree //") &&
+ HEAD_TREE=$(git show -s --pretty=raw HEAD | sed -n "/tree/s/tree //p") &&
git tag tree_tag -m "tagging a tree" $HEAD_TREE &&
git tag -a tree_tag-obj -m "tagging a tree" $HEAD_TREE &&
git tag tag-obj_tag -m "tagging a tag" tree_tag-obj &&
@@ -750,4 +759,46 @@ test_expect_success 'merge commit gets exported with --import-marks' '
)
'
+
+test_expect_success 'fast-export --first-parent outputs all revisions output by revision walk' '
+ git init first-parent &&
+ (
+ cd first-parent &&
+ test_commit A &&
+ git checkout -b topic1 &&
+ test_commit B &&
+ git checkout main &&
+ git merge --no-ff topic1 &&
+
+ git checkout -b topic2 &&
+ test_commit C &&
+ git checkout main &&
+ git merge --no-ff topic2 &&
+
+ test_commit D &&
+
+ git fast-export main -- --first-parent >first-parent-export &&
+ git fast-export main -- --first-parent --reverse >first-parent-reverse-export &&
+ test_cmp first-parent-export first-parent-reverse-export &&
+
+ git init import &&
+ git -C import fast-import <first-parent-export &&
+
+ git log --format="%ad %s" --first-parent main >expected &&
+ git -C import log --format="%ad %s" --all >actual &&
+ test_cmp expected actual &&
+ test_line_count = 4 actual
+ )
+'
+
+test_expect_success 'fast-export handles --end-of-options' '
+ git update-ref refs/heads/nodash HEAD &&
+ git update-ref refs/heads/--dashes HEAD &&
+ git fast-export --end-of-options nodash >expect &&
+ git fast-export --end-of-options --dashes >actual.raw &&
+ # fix up lines which mention the ref for comparison
+ sed s/--dashes/nodash/ <actual.raw >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t9351-fast-export-anonymize.sh b/t/t9351-fast-export-anonymize.sh
index 77047e2..156a647 100755
--- a/t/t9351-fast-export-anonymize.sh
+++ b/t/t9351-fast-export-anonymize.sh
@@ -25,6 +25,7 @@ test_expect_success 'setup simple repo' '
test_expect_success 'export anonymized stream' '
git fast-export --anonymize --all \
--anonymize-map=retain-me \
+ --anonymize-map=xyzzy:should-not-appear \
--anonymize-map=xyzzy:custom-name \
--anonymize-map=other \
>stream
@@ -41,6 +42,7 @@ test_expect_success 'stream omits path names' '
test_expect_success 'stream contains user-specified names' '
grep retain-me stream &&
+ ! grep should-not-appear stream &&
grep custom-name stream
'
diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh
index 2d29d48..e499c7f 100755
--- a/t/t9400-git-cvsserver-server.sh
+++ b/t/t9400-git-cvsserver-server.sh
@@ -36,6 +36,13 @@ CVSWORK="$PWD/cvswork"
CVS_SERVER=git-cvsserver
export CVSROOT CVS_SERVER
+if perl -e 'exit(1) if not defined crypt("", "cv")'
+then
+ PWDHASH='lac2ItudM3.KM'
+else
+ PWDHASH='$2b$10$t8fGvE/a9eLmfOLzsZme2uOa2QtoMYwIxq9wZA6aBKtF1Yb7FJIzi'
+fi
+
rm -rf "$CVSWORK" "$SERVERDIR"
test_expect_success 'setup' '
git config push.default matching &&
@@ -54,15 +61,16 @@ test_expect_success 'setup' '
GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log" &&
GIT_DIR="$SERVERDIR" git config gitcvs.authdb "$SERVERDIR/auth.db" &&
- echo cvsuser:cvGVEarMLnhlA > "$SERVERDIR/auth.db"
+ echo "cvsuser:$PWDHASH" >"$SERVERDIR/auth.db"
'
# note that cvs doesn't accept absolute pathnames
# as argument to co -d
-test_expect_success 'basic checkout' \
- 'GIT_CONFIG="$git_config" cvs -Q co -d cvswork main &&
- test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5 | head -n 1))" = "empty/1.1/" &&
- test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5 | sed -ne \$p))" = "secondrootfile/1.1/"'
+test_expect_success 'basic checkout' '
+ GIT_CONFIG="$git_config" cvs -Q co -d cvswork main &&
+ test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5 | head -n 1))" = "empty/1.1/" &&
+ test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5 | sed -ne \$p))" = "secondrootfile/1.1/"
+'
#------------------------
# PSERVER AUTHENTICATION
@@ -108,35 +116,40 @@ Ah<Z:yZZ30 e
END VERIFICATION REQUEST
EOF
-test_expect_success 'pserver authentication' \
- 'cat request-anonymous | git-cvsserver pserver >log 2>&1 &&
- sed -ne \$p log | grep "^I LOVE YOU\$"'
+test_expect_success 'pserver authentication' '
+ git-cvsserver pserver <request-anonymous >log 2>&1 &&
+ sed -ne \$p log | grep "^I LOVE YOU\$"
+'
-test_expect_success 'pserver authentication failure (non-anonymous user)' \
- 'if cat request-git | git-cvsserver pserver >log 2>&1
- then
- false
- else
- true
- fi &&
- sed -ne \$p log | grep "^I HATE YOU\$"'
+test_expect_success 'pserver authentication failure (non-anonymous user)' '
+ if git-cvsserver pserver <request-git >log 2>&1
+ then
+ false
+ else
+ true
+ fi &&
+ sed -ne \$p log | grep "^I HATE YOU\$"
+'
-test_expect_success 'pserver authentication success (non-anonymous user with password)' \
- 'cat login-git-ok | git-cvsserver pserver >log 2>&1 &&
- sed -ne \$p log | grep "^I LOVE YOU\$"'
+test_expect_success 'pserver authentication success (non-anonymous user with password)' '
+ git-cvsserver pserver <login-git-ok >log 2>&1 &&
+ sed -ne \$p log | grep "^I LOVE YOU\$"
+'
-test_expect_success 'pserver authentication (login)' \
- 'cat login-anonymous | git-cvsserver pserver >log 2>&1 &&
- sed -ne \$p log | grep "^I LOVE YOU\$"'
+test_expect_success 'pserver authentication (login)' '
+ git-cvsserver pserver <login-anonymous >log 2>&1 &&
+ sed -ne \$p log | grep "^I LOVE YOU\$"
+'
-test_expect_success 'pserver authentication failure (login/non-anonymous user)' \
- 'if cat login-git | git-cvsserver pserver >log 2>&1
- then
- false
- else
- true
- fi &&
- sed -ne \$p log | grep "^I HATE YOU\$"'
+test_expect_success 'pserver authentication failure (login/non-anonymous user)' '
+ if git-cvsserver pserver <login-git >log 2>&1
+ then
+ false
+ else
+ true
+ fi &&
+ sed -ne \$p log | grep "^I HATE YOU\$"
+'
# misuse pserver authentication for testing of req_Root
@@ -158,36 +171,38 @@ END AUTH REQUEST
Root $WORKDIR
EOF
-test_expect_success 'req_Root failure (relative pathname)' \
- 'if cat request-relative | git-cvsserver pserver >log 2>&1
- then
- echo unexpected success
- false
- else
- true
- fi &&
- tail log | grep "^error 1 Root must be an absolute pathname$"'
+test_expect_success 'req_Root failure (relative pathname)' '
+ if git-cvsserver pserver <request-relative >log 2>&1
+ then
+ echo unexpected success
+ false
+ else
+ true
+ fi &&
+ tail log | grep "^error 1 Root must be an absolute pathname$"
+'
-test_expect_success 'req_Root failure (conflicting roots)' \
- 'cat request-conflict | git-cvsserver pserver >log 2>&1 &&
- tail log | grep "^error 1 Conflicting roots specified$"'
+test_expect_success 'req_Root failure (conflicting roots)' '
+ git-cvsserver pserver <request-conflict >log 2>&1 &&
+ tail log | grep "^error 1 Conflicting roots specified$"
+'
-test_expect_success 'req_Root (strict paths)' \
- 'cat request-anonymous | git-cvsserver --strict-paths pserver "$SERVERDIR" >log 2>&1 &&
- sed -ne \$p log | grep "^I LOVE YOU\$"'
+test_expect_success 'req_Root (strict paths)' '
+ git-cvsserver --strict-paths pserver "$SERVERDIR" <request-anonymous >log 2>&1 &&
+ sed -ne \$p log | grep "^I LOVE YOU\$"
+'
test_expect_success 'req_Root failure (strict-paths)' '
- ! cat request-anonymous |
- git-cvsserver --strict-paths pserver "$WORKDIR" >log 2>&1
+ ! git-cvsserver --strict-paths pserver "$WORKDIR" <request-anonymous >log 2>&1
'
-test_expect_success 'req_Root (w/o strict-paths)' \
- 'cat request-anonymous | git-cvsserver pserver "$WORKDIR/" >log 2>&1 &&
- sed -ne \$p log | grep "^I LOVE YOU\$"'
+test_expect_success 'req_Root (w/o strict-paths)' '
+ git-cvsserver pserver "$WORKDIR/" <request-anonymous >log 2>&1 &&
+ sed -ne \$p log | grep "^I LOVE YOU\$"
+'
test_expect_success 'req_Root failure (w/o strict-paths)' '
- ! cat request-anonymous |
- git-cvsserver pserver "$WORKDIR/gitcvs" >log 2>&1
+ ! git-cvsserver pserver "$WORKDIR/gitcvs" <request-anonymous >log 2>&1
'
cat >request-base <<EOF
@@ -199,27 +214,29 @@ END AUTH REQUEST
Root /gitcvs.git
EOF
-test_expect_success 'req_Root (base-path)' \
- 'cat request-base | git-cvsserver --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" >log 2>&1 &&
- sed -ne \$p log | grep "^I LOVE YOU\$"'
+test_expect_success 'req_Root (base-path)' '
+ git-cvsserver --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" <request-base >log 2>&1 &&
+ sed -ne \$p log | grep "^I LOVE YOU\$"
+'
test_expect_success 'req_Root failure (base-path)' '
- ! cat request-anonymous |
- git-cvsserver --strict-paths --base-path "$WORKDIR" pserver "$SERVERDIR" >log 2>&1
+ ! git-cvsserver --strict-paths --base-path "$WORKDIR" pserver "$SERVERDIR" <request-anonymous >log 2>&1
'
GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false || exit 1
-test_expect_success 'req_Root (export-all)' \
- 'cat request-anonymous | git-cvsserver --export-all pserver "$WORKDIR" >log 2>&1 &&
- sed -ne \$p log | grep "^I LOVE YOU\$"'
+test_expect_success 'req_Root (export-all)' '
+ git-cvsserver --export-all pserver "$WORKDIR" <request-anonymous >log 2>&1 &&
+ sed -ne \$p log | grep "^I LOVE YOU\$"
+'
-test_expect_success 'req_Root failure (export-all w/o whitelist)' \
- '! (cat request-anonymous | git-cvsserver --export-all pserver >log 2>&1 || false)'
+test_expect_success 'req_Root failure (export-all w/o directory list)' '
+ ! (git-cvsserver --export-all pserver <request-anonymous >log 2>&1 || false)'
-test_expect_success 'req_Root (everything together)' \
- 'cat request-base | git-cvsserver --export-all --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" >log 2>&1 &&
- sed -ne \$p log | grep "^I LOVE YOU\$"'
+test_expect_success 'req_Root (everything together)' '
+ git-cvsserver --export-all --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" <request-base >log 2>&1 &&
+ sed -ne \$p log | grep "^I LOVE YOU\$"
+'
GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true || exit 1
@@ -240,45 +257,49 @@ test_expect_success 'gitcvs.enabled = false' \
test ! -d cvswork2'
rm -fr cvswork2
-test_expect_success 'gitcvs.ext.enabled = true' \
- 'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
- GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false &&
- GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1 &&
- test_cmp cvswork cvswork2'
+test_expect_success 'gitcvs.ext.enabled = true' '
+ GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
+ GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false &&
+ GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1 &&
+ test_cmp cvswork cvswork2
+'
rm -fr cvswork2
-test_expect_success 'gitcvs.ext.enabled = false' \
- 'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled false &&
- GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
- if GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1
- then
- echo unexpected cvs success
- false
- else
- true
- fi &&
- grep "GITCVS emulation disabled" cvs.log &&
- test ! -d cvswork2'
+test_expect_success 'gitcvs.ext.enabled = false' '
+ GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled false &&
+ GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
+ if GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1
+ then
+ echo unexpected cvs success
+ false
+ else
+ true
+ fi &&
+ grep "GITCVS emulation disabled" cvs.log &&
+ test ! -d cvswork2
+'
rm -fr cvswork2
-test_expect_success 'gitcvs.dbname' \
- 'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
- GIT_DIR="$SERVERDIR" git config gitcvs.dbname %Ggitcvs.%a.%m.sqlite &&
- GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1 &&
- test_cmp cvswork cvswork2 &&
- test -f "$SERVERDIR/gitcvs.ext.main.sqlite" &&
- cmp "$SERVERDIR/gitcvs.main.sqlite" "$SERVERDIR/gitcvs.ext.main.sqlite"'
+test_expect_success 'gitcvs.dbname' '
+ GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
+ GIT_DIR="$SERVERDIR" git config gitcvs.dbname %Ggitcvs.%a.%m.sqlite &&
+ GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1 &&
+ test_cmp cvswork cvswork2 &&
+ test -f "$SERVERDIR/gitcvs.ext.main.sqlite" &&
+ cmp "$SERVERDIR/gitcvs.main.sqlite" "$SERVERDIR/gitcvs.ext.main.sqlite"
+'
rm -fr cvswork2
-test_expect_success 'gitcvs.ext.dbname' \
- 'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
- GIT_DIR="$SERVERDIR" git config gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&
- GIT_DIR="$SERVERDIR" git config gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&
- GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1 &&
- test_cmp cvswork cvswork2 &&
- test -f "$SERVERDIR/gitcvs1.ext.main.sqlite" &&
- test ! -f "$SERVERDIR/gitcvs2.ext.main.sqlite" &&
- cmp "$SERVERDIR/gitcvs.main.sqlite" "$SERVERDIR/gitcvs1.ext.main.sqlite"'
+test_expect_success 'gitcvs.ext.dbname' '
+ GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
+ GIT_DIR="$SERVERDIR" git config gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&
+ GIT_DIR="$SERVERDIR" git config gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&
+ GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1 &&
+ test_cmp cvswork cvswork2 &&
+ test -f "$SERVERDIR/gitcvs1.ext.main.sqlite" &&
+ test ! -f "$SERVERDIR/gitcvs2.ext.main.sqlite" &&
+ cmp "$SERVERDIR/gitcvs.main.sqlite" "$SERVERDIR/gitcvs1.ext.main.sqlite"
+'
#------------
@@ -292,110 +313,115 @@ GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log" ||
exit 1
-test_expect_success 'cvs update (create new file)' \
- 'echo testfile1 >testfile1 &&
- git add testfile1 &&
- git commit -q -m "Add testfile1" &&
- git push gitcvs.git >/dev/null &&
- cd cvswork &&
- GIT_CONFIG="$git_config" cvs -Q update &&
- test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.1/" &&
- test_cmp testfile1 ../testfile1'
+test_expect_success 'cvs update (create new file)' '
+ echo testfile1 >testfile1 &&
+ git add testfile1 &&
+ git commit -q -m "Add testfile1" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs -Q update &&
+ test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.1/" &&
+ test_cmp testfile1 ../testfile1
+'
cd "$WORKDIR"
-test_expect_success 'cvs update (update existing file)' \
- 'echo line 2 >>testfile1 &&
- git add testfile1 &&
- git commit -q -m "Append to testfile1" &&
- git push gitcvs.git >/dev/null &&
- cd cvswork &&
- GIT_CONFIG="$git_config" cvs -Q update &&
- test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.2/" &&
- test_cmp testfile1 ../testfile1'
+test_expect_success 'cvs update (update existing file)' '
+ echo line 2 >>testfile1 &&
+ git add testfile1 &&
+ git commit -q -m "Append to testfile1" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs -Q update &&
+ test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.2/" &&
+ test_cmp testfile1 ../testfile1
+'
cd "$WORKDIR"
#TODO: cvsserver doesn't support update w/o -d
test_expect_failure "cvs update w/o -d doesn't create subdir (TODO)" '
- mkdir test &&
- echo >test/empty &&
- git add test &&
- git commit -q -m "Single Subdirectory" &&
- git push gitcvs.git >/dev/null &&
- cd cvswork &&
- GIT_CONFIG="$git_config" cvs -Q update &&
- test ! -d test
+ mkdir test &&
+ echo >test/empty &&
+ git add test &&
+ git commit -q -m "Single Subdirectory" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs -Q update &&
+ test ! -d test
'
cd "$WORKDIR"
-test_expect_success 'cvs update (subdirectories)' \
- '(for dir in A A/B A/B/C A/D E; do
- mkdir $dir &&
- echo "test file in $dir" >"$dir/file_in_$(echo $dir|sed -e "s#/# #g")" &&
- git add $dir
- done) &&
- git commit -q -m "deep sub directory structure" &&
- git push gitcvs.git >/dev/null &&
- cd cvswork &&
- GIT_CONFIG="$git_config" cvs -Q update -d &&
- (for dir in A A/B A/B/C A/D E; do
- filename="file_in_$(echo $dir|sed -e "s#/# #g")" &&
- if test "$(echo $(grep -v ^D $dir/CVS/Entries|cut -d/ -f2,3,5))" = "$filename/1.1/" &&
- test_cmp "$dir/$filename" "../$dir/$filename"; then
- :
- else
- echo >failure
- fi
- done) &&
- test ! -f failure'
+test_expect_success 'cvs update (subdirectories)' '
+ (for dir in A A/B A/B/C A/D E; do
+ mkdir $dir &&
+ echo "test file in $dir" >"$dir/file_in_$(echo $dir|sed -e "s#/# #g")" &&
+ git add $dir || exit 1
+ done) &&
+ git commit -q -m "deep sub directory structure" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs -Q update -d &&
+ (for dir in A A/B A/B/C A/D E; do
+ filename="file_in_$(echo $dir|sed -e "s#/# #g")" &&
+ if test "$(echo $(grep -v ^D $dir/CVS/Entries|cut -d/ -f2,3,5))" = "$filename/1.1/" &&
+ test_cmp "$dir/$filename" "../$dir/$filename"; then
+ :
+ else
+ exit 1
+ fi
+ done)
+'
cd "$WORKDIR"
-test_expect_success 'cvs update (delete file)' \
- 'git rm testfile1 &&
- git commit -q -m "Remove testfile1" &&
- git push gitcvs.git >/dev/null &&
- cd cvswork &&
- GIT_CONFIG="$git_config" cvs -Q update &&
- test -z "$(grep testfile1 CVS/Entries)" &&
- test ! -f testfile1'
+test_expect_success 'cvs update (delete file)' '
+ git rm testfile1 &&
+ git commit -q -m "Remove testfile1" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs -Q update &&
+ test -z "$(grep testfile1 CVS/Entries)" &&
+ test ! -f testfile1
+'
cd "$WORKDIR"
-test_expect_success 'cvs update (re-add deleted file)' \
- 'echo readded testfile >testfile1 &&
- git add testfile1 &&
- git commit -q -m "Re-Add testfile1" &&
- git push gitcvs.git >/dev/null &&
- cd cvswork &&
- GIT_CONFIG="$git_config" cvs -Q update &&
- test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.4/" &&
- test_cmp testfile1 ../testfile1'
+test_expect_success 'cvs update (re-add deleted file)' '
+ echo readded testfile >testfile1 &&
+ git add testfile1 &&
+ git commit -q -m "Re-Add testfile1" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs -Q update &&
+ test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.4/" &&
+ test_cmp testfile1 ../testfile1
+'
cd "$WORKDIR"
-test_expect_success 'cvs update (merge)' \
- 'echo Line 0 >expected &&
- for i in 1 2 3 4 5 6 7
- do
- echo Line $i >>merge &&
- echo Line $i >>expected
- done &&
- echo Line 8 >>expected &&
- git add merge &&
- git commit -q -m "Merge test (pre-merge)" &&
- git push gitcvs.git >/dev/null &&
- cd cvswork &&
- GIT_CONFIG="$git_config" cvs -Q update &&
- test "$(echo $(grep merge CVS/Entries|cut -d/ -f2,3,5))" = "merge/1.1/" &&
- test_cmp merge ../merge &&
- ( echo Line 0 && cat merge ) >merge.tmp &&
- mv merge.tmp merge &&
- cd "$WORKDIR" &&
- echo Line 8 >>merge &&
- git add merge &&
- git commit -q -m "Merge test (merge)" &&
- git push gitcvs.git >/dev/null &&
- cd cvswork &&
- sleep 1 && touch merge &&
- GIT_CONFIG="$git_config" cvs -Q update &&
- test_cmp merge ../expected'
+test_expect_success 'cvs update (merge)' '
+ echo Line 0 >expected &&
+ for i in 1 2 3 4 5 6 7
+ do
+ echo Line $i >>merge &&
+ echo Line $i >>expected || return 1
+ done &&
+ echo Line 8 >>expected &&
+ git add merge &&
+ git commit -q -m "Merge test (pre-merge)" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs -Q update &&
+ test "$(echo $(grep merge CVS/Entries|cut -d/ -f2,3,5))" = "merge/1.1/" &&
+ test_cmp merge ../merge &&
+ ( echo Line 0 && cat merge ) >merge.tmp &&
+ mv merge.tmp merge &&
+ cd "$WORKDIR" &&
+ echo Line 8 >>merge &&
+ git add merge &&
+ git commit -q -m "Merge test (merge)" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ sleep 1 && touch merge &&
+ GIT_CONFIG="$git_config" cvs -Q update &&
+ test_cmp merge ../expected
+'
cd "$WORKDIR"
@@ -412,55 +438,58 @@ do
echo Line $i >>expected.C
done
-test_expect_success 'cvs update (conflict merge)' \
- '( echo LINE 0 && cat merge ) >merge.tmp &&
- mv merge.tmp merge &&
- git add merge &&
- git commit -q -m "Merge test (conflict)" &&
- git push gitcvs.git >/dev/null &&
- cd cvswork &&
- GIT_CONFIG="$git_config" cvs -Q update &&
- test_cmp merge ../expected.C'
+test_expect_success 'cvs update (conflict merge)' '
+ ( echo LINE 0 && cat merge ) >merge.tmp &&
+ mv merge.tmp merge &&
+ git add merge &&
+ git commit -q -m "Merge test (conflict)" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs -Q update &&
+ test_cmp merge ../expected.C
+'
cd "$WORKDIR"
-test_expect_success 'cvs update (-C)' \
- 'cd cvswork &&
- GIT_CONFIG="$git_config" cvs -Q update -C &&
- test_cmp merge ../merge'
+test_expect_success 'cvs update (-C)' '
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs -Q update -C &&
+ test_cmp merge ../merge
+'
cd "$WORKDIR"
-test_expect_success 'cvs update (merge no-op)' \
- 'echo Line 9 >>merge &&
- cp merge cvswork/merge &&
- git add merge &&
- git commit -q -m "Merge test (no-op)" &&
- git push gitcvs.git >/dev/null &&
- cd cvswork &&
- sleep 1 && touch merge &&
- GIT_CONFIG="$git_config" cvs -Q update &&
- test_cmp merge ../merge'
+test_expect_success 'cvs update (merge no-op)' '
+ echo Line 9 >>merge &&
+ cp merge cvswork/merge &&
+ git add merge &&
+ git commit -q -m "Merge test (no-op)" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ sleep 1 && touch merge &&
+ GIT_CONFIG="$git_config" cvs -Q update &&
+ test_cmp merge ../merge
+'
cd "$WORKDIR"
test_expect_success 'cvs update (-p)' '
- touch really-empty &&
- echo Line 1 > no-lf &&
- printf "Line 2" >> no-lf &&
- git add really-empty no-lf &&
- git commit -q -m "Update -p test" &&
- git push gitcvs.git >/dev/null &&
- cd cvswork &&
- GIT_CONFIG="$git_config" cvs update &&
- for i in merge no-lf empty really-empty; do
- GIT_CONFIG="$git_config" cvs update -p "$i" >$i.out &&
- test_cmp $i.out ../$i || return 1
- done
+ touch really-empty &&
+ echo Line 1 > no-lf &&
+ printf "Line 2" >> no-lf &&
+ git add really-empty no-lf &&
+ git commit -q -m "Update -p test" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs update &&
+ for i in merge no-lf empty really-empty; do
+ GIT_CONFIG="$git_config" cvs update -p "$i" >$i.out &&
+ test_cmp $i.out ../$i || return 1
+ done
'
cd "$WORKDIR"
test_expect_success 'cvs update (module list supports packed refs)' '
- GIT_DIR="$SERVERDIR" git pack-refs --all &&
- GIT_CONFIG="$git_config" cvs -n up -d 2> out &&
- grep "cvs update: New directory \`main'\''" < out
+ GIT_DIR="$SERVERDIR" git pack-refs --all &&
+ GIT_CONFIG="$git_config" cvs -n up -d 2> out &&
+ grep "cvs update: New directory \`main'\''" < out
'
#------------
@@ -469,30 +498,30 @@ test_expect_success 'cvs update (module list supports packed refs)' '
cd "$WORKDIR"
test_expect_success 'cvs status' '
- mkdir status.dir &&
- echo Line > status.dir/status.file &&
- echo Line > status.file &&
- git add status.dir status.file &&
- git commit -q -m "Status test" &&
- git push gitcvs.git >/dev/null &&
- cd cvswork &&
- GIT_CONFIG="$git_config" cvs update &&
- GIT_CONFIG="$git_config" cvs status | grep "^File: status.file" >../out &&
- test_line_count = 2 ../out
+ mkdir status.dir &&
+ echo Line > status.dir/status.file &&
+ echo Line > status.file &&
+ git add status.dir status.file &&
+ git commit -q -m "Status test" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs update &&
+ GIT_CONFIG="$git_config" cvs status | grep "^File: status.file" >../out &&
+ test_line_count = 2 ../out
'
cd "$WORKDIR"
test_expect_success 'cvs status (nonrecursive)' '
- cd cvswork &&
- GIT_CONFIG="$git_config" cvs status -l | grep "^File: status.file" >../out &&
- test_line_count = 1 ../out
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs status -l | grep "^File: status.file" >../out &&
+ test_line_count = 1 ../out
'
cd "$WORKDIR"
test_expect_success 'cvs status (no subdirs in header)' '
- cd cvswork &&
- GIT_CONFIG="$git_config" cvs status | grep ^File: >../out &&
- ! grep / <../out
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs status | grep ^File: >../out &&
+ ! grep / <../out
'
#------------
@@ -501,9 +530,9 @@ test_expect_success 'cvs status (no subdirs in header)' '
cd "$WORKDIR"
test_expect_success 'cvs co -c (shows module database)' '
- GIT_CONFIG="$git_config" cvs co -c > out &&
- grep "^main[ ][ ]*main$" <out &&
- ! grep -v "^main[ ][ ]*main$" <out
+ GIT_CONFIG="$git_config" cvs co -c > out &&
+ grep "^main[ ][ ]*main$" <out &&
+ ! grep -v "^main[ ][ ]*main$" <out
'
#------------
@@ -569,11 +598,11 @@ expectStat="$?"
cd "$WORKDIR"
test_expect_success 'cvs log' '
- cd cvswork &&
- test x"$expectStat" = x"0" &&
- GIT_CONFIG="$git_config" cvs log merge >../out &&
- sed -e "s%2[0-9][0-9][0-9]/[01][0-9]/[0-3][0-9] [0-2][0-9]:[0-5][0-9]:[0-5][0-9]%__DATE__%" ../out > ../actual &&
- test_cmp ../expect ../actual
+ cd cvswork &&
+ test x"$expectStat" = x"0" &&
+ GIT_CONFIG="$git_config" cvs log merge >../out &&
+sed -e "s%2[0-9][0-9][0-9]/[01][0-9]/[0-3][0-9] [0-2][0-9]:[0-5][0-9]:[0-5][0-9]%__DATE__%" ../out > ../actual &&
+ test_cmp ../expect ../actual
'
#------------
@@ -582,11 +611,11 @@ test_expect_success 'cvs log' '
cd "$WORKDIR"
test_expect_success 'cvs annotate' '
- cd cvswork &&
- GIT_CONFIG="$git_config" cvs annotate merge >../out &&
- sed -e "s/ .*//" ../out >../actual &&
- for i in 3 1 1 1 1 1 1 1 2 4; do echo 1.$i; done >../expect &&
- test_cmp ../expect ../actual
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs annotate merge >../out &&
+ sed -e "s/ .*//" ../out >../actual &&
+ printf "1.%d\n" 3 1 1 1 1 1 1 1 2 4 >../expect &&
+ test_cmp ../expect ../actual
'
#------------
diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh
index 0333065..7679780 100755
--- a/t/t9500-gitweb-standalone-no-errors.sh
+++ b/t/t9500-gitweb-standalone-no-errors.sh
@@ -627,6 +627,7 @@ test_expect_success \
test_expect_success 'setup' '
version=$(git config core.repositoryformatversion) &&
algo=$(test_might_fail git config extensions.objectformat) &&
+ refstorage=$(test_might_fail git config extensions.refstorage) &&
cat >.git/config <<-\EOF &&
# testing noval and alternate separator
[gitweb]
@@ -637,6 +638,10 @@ test_expect_success 'setup' '
if test -n "$algo"
then
git config extensions.objectformat "$algo"
+ fi &&
+ if test -n "$refstorage"
+ then
+ git config extensions.refstorage "$refstorage"
fi
'
diff --git a/t/t9501-gitweb-standalone-http-status.sh b/t/t9501-gitweb-standalone-http-status.sh
index 32814e7..c900231 100755
--- a/t/t9501-gitweb-standalone-http-status.sh
+++ b/t/t9501-gitweb-standalone-http-status.sh
@@ -13,6 +13,7 @@ code and message.'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-gitweb.sh
#
diff --git a/t/t9502-gitweb-standalone-parse-output.sh b/t/t9502-gitweb-standalone-parse-output.sh
index 3167473..81d5625 100755
--- a/t/t9502-gitweb-standalone-parse-output.sh
+++ b/t/t9502-gitweb-standalone-parse-output.sh
@@ -34,7 +34,7 @@ EOF
#
# This will check that gitweb HTTP header contains proposed filename
# as <basename> with '.tar' suffix added, and that generated tarfile
-# (gitweb message body) has <prefix> as prefix for al files in tarfile
+# (gitweb message body) has <prefix> as prefix for all files in tarfile
#
# <prefix> default to <basename>
check_snapshot () {
@@ -207,4 +207,31 @@ test_expect_success 'xss checks' '
xss "" "$TAG+"
'
+no_http_equiv_content_type() {
+ gitweb_run "$@" &&
+ ! grep -E "http-equiv=['\"]?content-type" gitweb.body
+}
+
+# See: <https://html.spec.whatwg.org/dev/semantics.html#attr-meta-http-equiv-content-type>
+test_expect_success 'no http-equiv="content-type" in XHTML' '
+ no_http_equiv_content_type &&
+ no_http_equiv_content_type "p=.git" &&
+ no_http_equiv_content_type "p=.git;a=log" &&
+ no_http_equiv_content_type "p=.git;a=tree"
+'
+
+proper_doctype() {
+ gitweb_run "$@" &&
+ grep -F "<!DOCTYPE html [" gitweb.body &&
+ grep "<!ENTITY nbsp" gitweb.body &&
+ grep "<!ENTITY sdot" gitweb.body
+}
+
+test_expect_success 'Proper DOCTYPE with entity declarations' '
+ proper_doctype &&
+ proper_doctype "p=.git" &&
+ proper_doctype "p=.git;a=log" &&
+ proper_doctype "p=.git;a=tree"
+'
+
test_done
diff --git a/t/t9603-cvsimport-patchsets.sh b/t/t9603-cvsimport-patchsets.sh
index 0e9daa5..19f38f7 100755
--- a/t/t9603-cvsimport-patchsets.sh
+++ b/t/t9603-cvsimport-patchsets.sh
@@ -12,9 +12,6 @@
# bug.
test_description='git cvsimport testing for correct patchset estimation'
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
. ./lib-cvs.sh
setup_cvs_test_repository t9603
diff --git a/t/t9604-cvsimport-timestamps.sh b/t/t9604-cvsimport-timestamps.sh
index 2ff4aa9..2d03259 100755
--- a/t/t9604-cvsimport-timestamps.sh
+++ b/t/t9604-cvsimport-timestamps.sh
@@ -3,11 +3,28 @@
test_description='git cvsimport timestamps'
. ./lib-cvs.sh
+test_lazy_prereq POSIX_TIMEZONE '
+ local tz=XST-1XDT,M3.5.0,M11.1.0
+ echo "1711846799 -> 2024-03-31 01:59:59 +0100" >expected &&
+ TZ="$tz" test-tool date show:iso-local 1711846799 >actual &&
+ test_cmp expected actual &&
+ echo "1711846800 -> 2024-03-31 03:00:00 +0200" >expected &&
+ TZ="$tz" test-tool date show:iso-local 1711846800 >actual &&
+ test_cmp expected actual &&
+ echo "1730591999 -> 2024-11-03 01:59:59 +0200" >expected &&
+ TZ="$tz" test-tool date show:iso-local 1730591999 >actual &&
+ test_cmp expected actual &&
+ echo "1730592000 -> 2024-11-03 01:00:00 +0100" >expected &&
+ TZ="$tz" test-tool date show:iso-local 1730592000 >actual &&
+ test_cmp expected actual
+'
+
setup_cvs_test_repository t9604
-test_expect_success PERL 'check timestamps are UTC (TZ=CST6CDT)' '
+test_expect_success PERL,POSIX_TIMEZONE 'check timestamps are UTC' '
- TZ=CST6CDT git cvsimport -p"-x" -C module-1 module &&
+ TZ=CST6CDT,M4.1.0,M10.5.0 \
+ git cvsimport -p"-x" -C module-1 module &&
git cvsimport -p"-x" -C module-1 module &&
(
cd module-1 &&
@@ -34,13 +51,13 @@ test_expect_success PERL 'check timestamps are UTC (TZ=CST6CDT)' '
test_cmp expect-1 actual-1
'
-test_expect_success PERL 'check timestamps with author-specific timezones' '
+test_expect_success PERL,POSIX_TIMEZONE 'check timestamps with author-specific timezones' '
cat >cvs-authors <<-EOF &&
user1=User One <user1@domain.org>
- user2=User Two <user2@domain.org> CST6CDT
- user3=User Three <user3@domain.org> EST5EDT
- user4=User Four <user4@domain.org> MST7MDT
+ user2=User Two <user2@domain.org> CST6CDT,M4.1.0,M10.5.0
+ user3=User Three <user3@domain.org> EST5EDT,M4.1.0,M10.5.0
+ user4=User Four <user4@domain.org> MST7MDT,M4.1.0,M10.5.0
EOF
git cvsimport -p"-x" -A cvs-authors -C module-2 module &&
(
diff --git a/t/t9700-perl-git.sh b/t/t9700-perl-git.sh
index 102c133..ccc8212 100755
--- a/t/t9700-perl-git.sh
+++ b/t/t9700-perl-git.sh
@@ -4,57 +4,53 @@
#
test_description='perl interface (Git.pm)'
-. ./test-lib.sh
-if ! test_have_prereq PERL; then
- skip_all='skipping perl interface tests, perl not available'
- test_done
-fi
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-perl.sh
-perl -MTest::More -e 0 2>/dev/null || {
- skip_all="Perl Test::More unavailable, skipping test"
- test_done
-}
+skip_all_if_no_Test_More
# set up test repository
-test_expect_success \
- 'set up test repository' \
- 'echo "test file 1" > file1 &&
- echo "test file 2" > file2 &&
- mkdir directory1 &&
- echo "in directory1" >> directory1/file &&
- mkdir directory2 &&
- echo "in directory2" >> directory2/file &&
- git add . &&
- git commit -m "first commit" &&
-
- echo "new file in subdir 2" > directory2/file2 &&
- git add . &&
- git commit -m "commit in directory2" &&
-
- echo "changed file 1" > file1 &&
- git commit -a -m "second commit" &&
-
- git config --add color.test.slot1 green &&
- git config --add test.string value &&
- git config --add test.dupstring value1 &&
- git config --add test.dupstring value2 &&
- git config --add test.booltrue true &&
- git config --add test.boolfalse no &&
- git config --add test.boolother other &&
- git config --add test.int 2k &&
- git config --add test.path "~/foo" &&
- git config --add test.pathexpanded "$HOME/foo" &&
- git config --add test.pathmulti foo &&
- git config --add test.pathmulti bar
- '
-
-# The external test will outputs its own plan
-test_external_has_tap=1
-
-test_external_without_stderr \
- 'Perl API' \
- perl "$TEST_DIRECTORY"/t9700/test.pl
+test_expect_success 'set up test repository' '
+ echo "test file 1" >file1 &&
+ echo "test file 2" >file2 &&
+ mkdir directory1 &&
+ echo "in directory1" >>directory1/file &&
+ mkdir directory2 &&
+ echo "in directory2" >>directory2/file &&
+ git add . &&
+ git commit -m "first commit" &&
+
+ echo "new file in subdir 2" >directory2/file2 &&
+ git add . &&
+ git commit -m "commit in directory2" &&
+
+ echo "changed file 1" >file1 &&
+ git commit -a -m "second commit" &&
+
+ git config --add color.test.slot1 green &&
+ git config --add test.string value &&
+ git config --add test.dupstring value1 &&
+ git config --add test.dupstring value2 &&
+ git config --add test.booltrue true &&
+ git config --add test.boolfalse no &&
+ git config --add test.boolother other &&
+ git config --add test.int 2k &&
+ git config --add test.path "~/foo" &&
+ git config --add test.pathexpanded "$HOME/foo" &&
+ git config --add test.pathmulti foo &&
+ git config --add test.pathmulti bar
+'
+
+test_expect_success 'set up bare repository' '
+ git init --bare bare.git
+'
+
+test_expect_success 'use t9700/test.pl to test Git.pm' '
+ "$PERL_PATH" "$TEST_DIRECTORY"/t9700/test.pl 2>stderr &&
+ test_must_be_empty stderr
+'
test_done
diff --git a/t/t9700/test.pl b/t/t9700/test.pl
index e046f7d..d8e8548 100755
--- a/t/t9700/test.pl
+++ b/t/t9700/test.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
use lib (split(/:/, $ENV{GITPERLLIB}));
-use 5.008;
+use 5.008001;
use warnings;
use strict;
@@ -30,6 +30,18 @@ BEGIN { use_ok('Git') }
# set up
our $abs_repo_dir = cwd();
ok(our $r = Git->repository(Directory => "."), "open repository");
+{
+ local $ENV{GIT_TEST_ASSUME_DIFFERENT_OWNER} = 1;
+ my $failed;
+
+ $failed = eval { Git->repository(Directory => $abs_repo_dir) };
+ ok(!$failed, "reject unsafe non-bare repository");
+ like($@, qr/not a git repository/i, "unsafe error message");
+
+ $failed = eval { Git->repository(Directory => "$abs_repo_dir/bare.git") };
+ ok(!$failed, "reject unsafe bare repository");
+ like($@, qr/not a git repository/i, "unsafe error message");
+}
# config
is($r->config("test.string"), "value", "config scalar: string");
diff --git a/t/t9800-git-p4-basic.sh b/t/t9800-git-p4-basic.sh
index 81bc8e8..53af8e3 100755
--- a/t/t9800-git-p4-basic.sh
+++ b/t/t9800-git-p4-basic.sh
@@ -54,7 +54,7 @@ test_expect_success 'git p4 sync uninitialized repo' '
(
cd "$git" &&
test_must_fail git p4 sync 2>errs &&
- test_i18ngrep "Perhaps you never did" errs
+ test_grep "Perhaps you never did" errs
)
'
@@ -74,6 +74,91 @@ test_expect_success 'git p4 sync new branch' '
)
'
+#
+# Setup as before, and then explicitly sync imported branch, using a
+# different ref format.
+#
+test_expect_success 'git p4 sync existing branch without changes' '
+ test_create_repo "$git" &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ test_commit head &&
+ git p4 sync --branch=depot //depot@all &&
+ git p4 sync --branch=refs/remotes/p4/depot >out &&
+ test_grep "No changes to import!" out
+ )
+'
+
+#
+# Same as before, relative branch name.
+#
+test_expect_success 'git p4 sync existing branch with relative name' '
+ test_create_repo "$git" &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ test_commit head &&
+ git p4 sync --branch=branch1 //depot@all &&
+ git p4 sync --branch=p4/branch1 >out &&
+ test_grep "No changes to import!" out
+ )
+'
+
+#
+# Same as before, with a nested branch path, referenced different ways.
+#
+test_expect_success 'git p4 sync existing branch with nested path' '
+ test_create_repo "$git" &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ test_commit head &&
+ git p4 sync --branch=p4/some/path //depot@all &&
+ git p4 sync --branch=some/path >out &&
+ test_grep "No changes to import!" out
+ )
+'
+
+#
+# Same as before, with a full ref path outside the p4/* namespace.
+#
+test_expect_success 'git p4 sync branch explicit ref without p4 in path' '
+ test_create_repo "$git" &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ test_commit head &&
+ git p4 sync --branch=refs/remotes/someremote/depot //depot@all &&
+ git p4 sync --branch=refs/remotes/someremote/depot >out &&
+ test_grep "No changes to import!" out
+ )
+'
+
+test_expect_success 'git p4 sync nonexistent ref' '
+ test_create_repo "$git" &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ test_commit head &&
+ git p4 sync --branch=depot //depot@all &&
+ test_must_fail git p4 sync --branch=depot2 2>errs &&
+ test_grep "Perhaps you never did" errs
+ )
+'
+
+test_expect_success 'git p4 sync existing non-p4-imported ref' '
+ test_create_repo "$git" &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ test_commit head &&
+ git p4 sync --branch=depot //depot@all &&
+ test_must_fail git p4 sync --branch=refs/heads/master 2>errs &&
+ test_grep "Perhaps you never did" errs
+ )
+'
+
test_expect_success 'clone two dirs' '
(
cd "$cli" &&
@@ -171,7 +256,7 @@ test_expect_success 'clone using non-numeric revision ranges' '
cd "$git" &&
git ls-files >lines &&
test_line_count = 8 lines
- )
+ ) || return 1
done
'
@@ -205,7 +290,7 @@ test_expect_success 'exit when p4 fails to produce marshaled output' '
export PATH &&
test_expect_code 1 git p4 clone --dest="$git" //depot >errs 2>&1
) &&
- test_i18ngrep ! Traceback errs
+ test_grep ! Traceback errs
'
# Hide a file from p4d, make sure we catch its complaint. This won't fail in
@@ -216,7 +301,7 @@ test_expect_success 'exit gracefully for p4 server errors' '
mv "$db"/depot/file1,v "$db"/depot/file1,v,hidden &&
test_when_finished cleanup_git &&
test_expect_code 1 git p4 clone --dest="$git" //depot@1 >out 2>err &&
- test_i18ngrep "Error from p4 print" err
+ test_grep "Error from p4 print" err
'
test_expect_success 'clone --bare should make a bare repository' '
@@ -245,7 +330,7 @@ test_expect_success 'initial import time from top change time' '
test_when_finished cleanup_git &&
(
cd "$git" &&
- gittime=$(git show -s --raw --pretty=format:%at HEAD) &&
+ gittime=$(git show -s --pretty=format:%at HEAD) &&
echo $p4time $gittime &&
test $p4time = $gittime
)
@@ -277,16 +362,21 @@ test_expect_success 'run hook p4-pre-submit before submit' '
git commit -m "add hello.txt" &&
git config git-p4.skipSubmitEdit true &&
git p4 submit --dry-run >out &&
- grep "Would apply" out &&
- mkdir -p .git/hooks &&
- write_script .git/hooks/p4-pre-submit <<-\EOF &&
- exit 0
- EOF
+ grep "Would apply" out
+ ) &&
+ test_hook -C "$git" p4-pre-submit <<-\EOF &&
+ exit 0
+ EOF
+ (
+ cd "$git" &&
git p4 submit --dry-run >out &&
- grep "Would apply" out &&
- write_script .git/hooks/p4-pre-submit <<-\EOF &&
- exit 1
- EOF
+ grep "Would apply" out
+ ) &&
+ test_hook -C "$git" --clobber p4-pre-submit <<-\EOF &&
+ exit 1
+ EOF
+ (
+ cd "$git" &&
test_must_fail git p4 submit --dry-run >errs 2>&1 &&
! grep "Would apply" errs
)
diff --git a/t/t9801-git-p4-branch.sh b/t/t9801-git-p4-branch.sh
index 50a6f8b..c598011 100755
--- a/t/t9801-git-p4-branch.sh
+++ b/t/t9801-git-p4-branch.sh
@@ -129,6 +129,16 @@ test_expect_success 'import depot, branch detection' '
)
'
+test_expect_success 'sync specific detected branch' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" --detect-branches //depot@all &&
+ (
+ cd "$git" &&
+ git p4 sync --branch=depot/branch2 >out &&
+ test_grep "No changes to import!" out
+ )
+'
+
test_expect_success 'import depot, branch detection, branchList branch definition' '
test_when_finished cleanup_git &&
test_create_repo "$git" &&
@@ -456,7 +466,7 @@ test_expect_success 'git p4 clone complex branches with excluded files' '
)
'
-# From a report in http://stackoverflow.com/questions/11893688
+# From a report in https://stackoverflow.com/questions/11893688
# where --use-client-spec caused branch prefixes not to be removed;
# every file in git appeared into a subdirectory of the branch name.
test_expect_success 'use-client-spec detect-branches setup' '
diff --git a/t/t9802-git-p4-filetype.sh b/t/t9802-git-p4-filetype.sh
index 19073c6..bb236cd 100755
--- a/t/t9802-git-p4-filetype.sh
+++ b/t/t9802-git-p4-filetype.sh
@@ -175,7 +175,7 @@ test_expect_success 'keyword file create' '
cp k-text-k k-text-ko &&
p4 add -t text+ko k-text-ko &&
- cat k-text-k | iconv -f ascii -t utf-16 >k-utf16-k &&
+ iconv -f ascii -t utf-16 <k-text-k >k-utf16-k &&
p4 add -t utf16+k k-utf16-k &&
cp k-utf16-k k-utf16-ko &&
@@ -333,4 +333,38 @@ test_expect_success SYMLINKS 'empty symlink target' '
)
'
+test_expect_success SYMLINKS 'utf-8 with and without BOM in text file' '
+ (
+ cd "$cli" &&
+
+ # some utf8 content
+ echo some tǣxt >utf8-nobom-test &&
+
+ # same utf8 content as before but with bom
+ echo some tǣxt | sed '\''s/^/\xef\xbb\xbf/'\'' >utf8-bom-test &&
+
+ # bom only
+ dd bs=1 count=3 if=utf8-bom-test of=utf8-bom-empty-test &&
+
+ p4 add utf8-nobom-test utf8-bom-test utf8-bom-empty-test &&
+ p4 submit -d "add utf8 test files"
+ ) &&
+ test_when_finished cleanup_git &&
+
+ git p4 clone --dest="$git" //depot@all &&
+ (
+ cd "$git" &&
+ git checkout refs/remotes/p4/master &&
+
+ echo some tǣxt >utf8-nobom-check &&
+ test_cmp utf8-nobom-check utf8-nobom-test &&
+
+ echo some tǣxt | sed '\''s/^/\xef\xbb\xbf/'\'' >utf8-bom-check &&
+ test_cmp utf8-bom-check utf8-bom-test &&
+
+ dd bs=1 count=3 if=utf8-bom-check of=utf8-bom-empty-check &&
+ test_cmp utf8-bom-empty-check utf8-bom-empty-test
+ )
+'
+
test_done
diff --git a/t/t9807-git-p4-submit.sh b/t/t9807-git-p4-submit.sh
index 7d4109f..6ae7ced 100755
--- a/t/t9807-git-p4-submit.sh
+++ b/t/t9807-git-p4-submit.sh
@@ -75,7 +75,7 @@ test_expect_success 'submit --dry-run' '
test_commit "dry-run1" &&
test_commit "dry-run2" &&
git p4 submit --dry-run >out &&
- test_i18ngrep "Would apply" out
+ test_grep "Would apply" out
) &&
(
cd "$cli" &&
@@ -99,7 +99,7 @@ test_expect_success 'submit --dry-run --export-labels' '
git commit -m "dry-run2" dry-run2 &&
git tag -m "dry-run-tag1" dry-run-tag1 HEAD^ &&
git p4 submit --dry-run --export-labels >out &&
- test_i18ngrep "Would create p4 label" out
+ test_grep "Would create p4 label" out
) &&
(
cd "$cli" &&
@@ -418,7 +418,7 @@ test_expect_success 'description with Jobs and values on separate lines' '
marshal_dump job0 <change &&
marshal_dump job1 <change
) | sort >jobs &&
- cat jobname1 jobname2 | sort >expected &&
+ sort jobname1 jobname2 >expected &&
test_cmp expected jobs
)
'
@@ -443,7 +443,7 @@ test_expect_success 'description with Jobs section and bogus following text' '
# build a job
make_job $(cat jobname) &&
test_must_fail git p4 submit 2>err &&
- test_i18ngrep "Unknown field name" err
+ test_grep "Unknown field name" err
) &&
(
cd "$cli" &&
@@ -461,9 +461,9 @@ test_expect_success 'submit --prepare-p4-only' '
git add prep-only-add &&
git commit -m "prep only add" &&
git p4 submit --prepare-p4-only >out &&
- test_i18ngrep "prepared for submission" out &&
- test_i18ngrep "must be deleted" out &&
- test_i18ngrep ! "everything below this line is just the diff" out
+ test_grep "prepared for submission" out &&
+ test_grep "must be deleted" out &&
+ test_grep ! "everything below this line is just the diff" out
) &&
(
cd "$cli" &&
diff --git a/t/t9810-git-p4-rcs.sh b/t/t9810-git-p4-rcs.sh
index e383688..5fe8331 100755
--- a/t/t9810-git-p4-rcs.sh
+++ b/t/t9810-git-p4-rcs.sh
@@ -4,6 +4,8 @@ test_description='git p4 rcs keywords'
. ./lib-git-p4.sh
+CP1252="\223\224"
+
test_expect_success 'start p4d' '
start_p4d
'
@@ -32,6 +34,9 @@ test_expect_success 'init depot' '
p4 submit -d "filek" &&
p4 add -t text+ko fileko &&
p4 submit -d "fileko" &&
+ printf "$CP1252" >fileko_cp1252 &&
+ p4 add -t text+ko fileko_cp1252 &&
+ p4 submit -d "fileko_cp1252" &&
p4 add -t text file_text &&
p4 submit -d "file_text"
)
@@ -359,4 +364,14 @@ test_expect_failure 'Add keywords in git which do not match the default p4 value
)
'
+test_expect_success 'check cp1252 smart quote are preserved through RCS keyword processing' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ printf "$CP1252" >expect &&
+ test_cmp_bin expect fileko_cp1252
+ )
+'
+
test_done
diff --git a/t/t9814-git-p4-rename.sh b/t/t9814-git-p4-rename.sh
index 468767c..2a9838f 100755
--- a/t/t9814-git-p4-rename.sh
+++ b/t/t9814-git-p4-rename.sh
@@ -216,7 +216,7 @@ test_expect_success 'detect copies' '
# variable exists, which allows admins to disable the "p4 move" command.
test_lazy_prereq P4D_HAVE_CONFIGURABLE_RUN_MOVE_ALLOW '
p4 configure show run.move.allow >out &&
- egrep ^run.move.allow: out
+ grep -E ^run.move.allow: out
'
# If move can be disabled, turn it off and test p4 move handling
diff --git a/t/t9815-git-p4-submit-fail.sh b/t/t9815-git-p4-submit-fail.sh
index 9779dc0..c766fd1 100755
--- a/t/t9815-git-p4-submit-fail.sh
+++ b/t/t9815-git-p4-submit-fail.sh
@@ -35,7 +35,7 @@ test_expect_success 'conflict on one commit' '
git add file1 &&
git commit -m "line3 in file1 will conflict" &&
test_expect_code 1 git p4 submit >out &&
- test_i18ngrep "No commits applied" out
+ test_grep "No commits applied" out
)
'
@@ -58,7 +58,7 @@ test_expect_success 'conflict on second of two commits' '
git add file1 &&
git commit -m "line4 in file1 will conflict" &&
test_expect_code 1 git p4 submit >out &&
- test_i18ngrep "Applied only the commits" out
+ test_grep "Applied only the commits" out
)
'
@@ -81,7 +81,7 @@ test_expect_success 'conflict on first of two commits, skip' '
# but this commit is okay
test_commit "okay_commit_after_skip" &&
echo s | test_expect_code 1 git p4 submit >out &&
- test_i18ngrep "Applied only the commits" out
+ test_grep "Applied only the commits" out
)
'
@@ -104,7 +104,7 @@ test_expect_success 'conflict on first of two commits, quit' '
# but this commit is okay
test_commit "okay_commit_after_quit" &&
echo q | test_expect_code 1 git p4 submit >out &&
- test_i18ngrep "No commits applied" out
+ test_grep "No commits applied" out
)
'
@@ -144,7 +144,7 @@ test_expect_success 'conflict on first of two commits, --conflict=skip' '
# but this commit is okay
test_commit "okay_commit_after_auto_skip" &&
test_expect_code 1 git p4 submit --conflict=skip >out &&
- test_i18ngrep "Applied only the commits" out
+ test_grep "Applied only the commits" out
)
'
@@ -167,7 +167,7 @@ test_expect_success 'conflict on first of two commits, --conflict=quit' '
# but this commit is okay
test_commit "okay_commit_after_auto_quit" &&
test_expect_code 1 git p4 submit --conflict=quit >out &&
- test_i18ngrep "No commits applied" out
+ test_grep "No commits applied" out
)
'
@@ -417,8 +417,8 @@ test_expect_success 'cleanup chmod after submit cancel' '
! p4 fstat -T action text &&
test_path_is_file text+x &&
! p4 fstat -T action text+x &&
- ls -l text | egrep ^-r-- &&
- ls -l text+x | egrep ^-r-x
+ ls -l text | grep -E ^-r-- &&
+ ls -l text+x | grep -E ^-r-x
)
'
diff --git a/t/t9816-git-p4-locked.sh b/t/t9816-git-p4-locked.sh
index 9328410..5e904ac 100755
--- a/t/t9816-git-p4-locked.sh
+++ b/t/t9816-git-p4-locked.sh
@@ -9,7 +9,7 @@ test_expect_success 'start p4d' '
'
# See
-# http://www.perforce.com/perforce/doc.current/manuals/p4sag/03_superuser.html#1088563
+# https://web.archive.org/web/20150602090517/http://www.perforce.com/perforce/doc.current/manuals/p4sag/chapter.superuser.html#superuser.basic.typemap_locking
# for suggestions on how to configure "sitewide pessimistic locking"
# where only one person can have a file open for edit at a time.
test_expect_success 'init depot' '
diff --git a/t/t9818-git-p4-block.sh b/t/t9818-git-p4-block.sh
index 0db7ab9..de591d8 100755
--- a/t/t9818-git-p4-block.sh
+++ b/t/t9818-git-p4-block.sh
@@ -92,11 +92,11 @@ test_expect_success 'Add some more files' '
for i in $(test_seq 0 10)
do
p4_add_file "included/x$i" &&
- p4_add_file "excluded/x$i"
+ p4_add_file "excluded/x$i" || return 1
done &&
for i in $(test_seq 0 10)
do
- p4_add_file "excluded/y$i"
+ p4_add_file "excluded/y$i" || return 1
done
'
@@ -123,7 +123,7 @@ test_expect_success 'Create a repo with multiple depot paths' '
do
for i in $(test_seq 1 10)
do
- p4_add_file "$p/file$p$i"
+ p4_add_file "$p/file$p$i" || return 1
done
done
'
diff --git a/t/t9824-git-p4-git-lfs.sh b/t/t9824-git-p4-git-lfs.sh
index a28dbbd..80c8c31 100755
--- a/t/t9824-git-p4-git-lfs.sh
+++ b/t/t9824-git-p4-git-lfs.sh
@@ -17,8 +17,8 @@ test_file_in_lfs () {
sed -n '2,2 p' "$FILE" | grep "^oid " &&
sed -n '3,3 p' "$FILE" | grep "^size " &&
test_line_count = 3 "$FILE" &&
- cat "$FILE" | grep "size $SIZE" &&
- HASH=$(cat "$FILE" | grep "oid sha256:" | sed -e "s/oid sha256://g") &&
+ grep "size $SIZE" "$FILE" &&
+ HASH=$(sed -ne "/oid sha256:/s/oid sha256://gp" "$FILE") &&
LFS_FILE=".git/lfs/objects/$(echo "$HASH" | cut -c1-2)/$(echo "$HASH" | cut -c3-4)/$HASH" &&
echo $EXPECTED_CONTENT >expect &&
test_path_is_file "$FILE" &&
diff --git a/t/t9835-git-p4-metadata-encoding-python2.sh b/t/t9835-git-p4-metadata-encoding-python2.sh
new file mode 100755
index 0000000..036bf79
--- /dev/null
+++ b/t/t9835-git-p4-metadata-encoding-python2.sh
@@ -0,0 +1,213 @@
+#!/bin/sh
+
+test_description='git p4 metadata encoding
+
+This test checks that the import process handles inconsistent text
+encoding in p4 metadata (author names, commit messages, etc) without
+failing, and produces maximally sane output in git.'
+
+. ./lib-git-p4.sh
+
+python_target_version='2'
+
+###############################
+## SECTION REPEATED IN t9836 ##
+###############################
+
+# Please note: this test calls "git-p4.py" rather than "git-p4", because the
+# latter references a specific path so we can't easily force it to run under
+# the python version we need to.
+
+python_major_version=$(python -V 2>&1 | cut -c 8)
+python_target_binary=$(which python$python_target_version)
+if ! test "$python_major_version" = "$python_target_version" && test "$python_target_binary"
+then
+ mkdir temp_python
+ PATH="$(pwd)/temp_python:$PATH" && export PATH
+ ln -s $python_target_binary temp_python/python
+fi
+
+python_major_version=$(python -V 2>&1 | cut -c 8)
+if ! test "$python_major_version" = "$python_target_version"
+then
+ skip_all="skipping python$python_target_version-specific git p4 tests; python$python_target_version not available"
+ test_done
+fi
+
+remove_user_cache () {
+ rm "$HOME/.gitp4-usercache.txt" || true
+}
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+test_expect_success 'init depot' '
+ (
+ cd "$cli" &&
+
+ p4_add_user "utf8_author" "ǣuthor" &&
+ P4USER=utf8_author &&
+ touch file1 &&
+ p4 add file1 &&
+ p4 submit -d "first CL has some utf-8 tǣxt" &&
+
+ p4_add_user "latin1_author" "$(echo æuthor |
+ iconv -f utf8 -t latin1)" &&
+ P4USER=latin1_author &&
+ touch file2 &&
+ p4 add file2 &&
+ p4 submit -d "$(echo second CL has some latin-1 tæxt |
+ iconv -f utf8 -t latin1)" &&
+
+ p4_add_user "cp1252_author" "$(echo æuthœr |
+ iconv -f utf8 -t cp1252)" &&
+ P4USER=cp1252_author &&
+ touch file3 &&
+ p4 add file3 &&
+ p4 submit -d "$(echo third CL has sœme cp-1252 tæxt |
+ iconv -f utf8 -t cp1252)" &&
+
+ p4_add_user "cp850_author" "$(echo Åuthor |
+ iconv -f utf8 -t cp850)" &&
+ P4USER=cp850_author &&
+ touch file4 &&
+ p4 add file4 &&
+ p4 submit -d "$(echo fourth CL hÅs some cp850 text |
+ iconv -f utf8 -t cp850)"
+ )
+'
+
+test_expect_success 'clone non-utf8 repo with strict encoding' '
+ test_when_finished cleanup_git &&
+ test_when_finished remove_user_cache &&
+ test_must_fail git -c git-p4.metadataDecodingStrategy=strict p4.py clone --dest="$git" //depot@all 2>err &&
+ grep "Decoding perforce metadata failed!" err
+'
+
+test_expect_success 'check utf-8 contents with passthrough strategy' '
+ test_when_finished cleanup_git &&
+ test_when_finished remove_user_cache &&
+ git -c git-p4.metadataDecodingStrategy=passthrough p4.py clone --dest="$git" //depot@all &&
+ (
+ cd "$git" &&
+ git log >actual &&
+ grep "some utf-8 tǣxt" actual &&
+ grep "ǣuthor" actual
+ )
+'
+
+test_expect_success 'check latin-1 contents corrupted in git with passthrough strategy' '
+ test_when_finished cleanup_git &&
+ test_when_finished remove_user_cache &&
+ git -c git-p4.metadataDecodingStrategy=passthrough p4.py clone --dest="$git" //depot@all &&
+ (
+ cd "$git" &&
+ git log >actual &&
+ badly_encoded_in_git=$(echo "some latin-1 tæxt" | iconv -f utf8 -t latin1) &&
+ grep "$badly_encoded_in_git" actual &&
+ bad_author_in_git="$(echo æuthor | iconv -f utf8 -t latin1)" &&
+ grep "$bad_author_in_git" actual
+ )
+'
+
+test_expect_success 'check utf-8 contents with fallback strategy' '
+ test_when_finished cleanup_git &&
+ test_when_finished remove_user_cache &&
+ git -c git-p4.metadataDecodingStrategy=fallback p4.py clone --dest="$git" //depot@all &&
+ (
+ cd "$git" &&
+ git log >actual &&
+ grep "some utf-8 tǣxt" actual &&
+ grep "ǣuthor" actual
+ )
+'
+
+test_expect_success 'check latin-1 contents with fallback strategy' '
+ test_when_finished cleanup_git &&
+ test_when_finished remove_user_cache &&
+ git -c git-p4.metadataDecodingStrategy=fallback p4.py clone --dest="$git" //depot@all &&
+ (
+ cd "$git" &&
+ git log >actual &&
+ grep "some latin-1 tæxt" actual &&
+ grep "æuthor" actual
+ )
+'
+
+test_expect_success 'check cp-1252 contents with fallback strategy' '
+ test_when_finished cleanup_git &&
+ test_when_finished remove_user_cache &&
+ git -c git-p4.metadataDecodingStrategy=fallback p4.py clone --dest="$git" //depot@all &&
+ (
+ cd "$git" &&
+ git log >actual &&
+ grep "sœme cp-1252 tæxt" actual &&
+ grep "æuthœr" actual
+ )
+'
+
+test_expect_success 'check cp850 contents parsed with correct fallback' '
+ test_when_finished cleanup_git &&
+ test_when_finished remove_user_cache &&
+ git -c git-p4.metadataDecodingStrategy=fallback -c git-p4.metadataFallbackEncoding=cp850 p4.py clone --dest="$git" //depot@all &&
+ (
+ cd "$git" &&
+ git log >actual &&
+ grep "hÅs some cp850 text" actual &&
+ grep "Åuthor" actual
+ )
+'
+
+test_expect_success 'check cp850-only contents escaped when cp1252 is fallback' '
+ test_when_finished cleanup_git &&
+ test_when_finished remove_user_cache &&
+ git -c git-p4.metadataDecodingStrategy=fallback p4.py clone --dest="$git" //depot@all &&
+ (
+ cd "$git" &&
+ git log >actual &&
+ grep "h%8Fs some cp850 text" actual &&
+ grep "%8Futhor" actual
+ )
+'
+
+test_expect_success 'check cp-1252 contents on later sync after clone with fallback strategy' '
+ test_when_finished cleanup_git &&
+ test_when_finished remove_user_cache &&
+ git -c git-p4.metadataDecodingStrategy=fallback p4.py clone --dest="$git" //depot@all &&
+ (
+ cd "$cli" &&
+ P4USER=cp1252_author &&
+ touch file10 &&
+ p4 add file10 &&
+ p4 submit -d "$(echo later CL has sœme more cp-1252 tæxt |
+ iconv -f utf8 -t cp1252)"
+ ) &&
+ (
+ cd "$git" &&
+
+ git p4.py sync --branch=master &&
+
+ git log p4/master >actual &&
+ grep "sœme more cp-1252 tæxt" actual &&
+ grep "æuthœr" actual
+ )
+'
+
+############################
+## / END REPEATED SECTION ##
+############################
+
+test_expect_success 'passthrough (latin-1 contents corrupted in git) is the default with python2' '
+ test_when_finished cleanup_git &&
+ test_when_finished remove_user_cache &&
+ git -c git-p4.metadataDecodingStrategy=passthrough p4.py clone --dest="$git" //depot@all &&
+ (
+ cd "$git" &&
+ git log >actual &&
+ badly_encoded_in_git=$(echo "some latin-1 tæxt" | iconv -f utf8 -t latin1) &&
+ grep "$badly_encoded_in_git" actual
+ )
+'
+
+test_done
diff --git a/t/t9836-git-p4-metadata-encoding-python3.sh b/t/t9836-git-p4-metadata-encoding-python3.sh
new file mode 100755
index 0000000..63350dc
--- /dev/null
+++ b/t/t9836-git-p4-metadata-encoding-python3.sh
@@ -0,0 +1,214 @@
+#!/bin/sh
+
+test_description='git p4 metadata encoding
+
+This test checks that the import process handles inconsistent text
+encoding in p4 metadata (author names, commit messages, etc) without
+failing, and produces maximally sane output in git.'
+
+. ./lib-git-p4.sh
+
+python_target_version='3'
+
+###############################
+## SECTION REPEATED IN t9835 ##
+###############################
+
+# Please note: this test calls "git-p4.py" rather than "git-p4", because the
+# latter references a specific path so we can't easily force it to run under
+# the python version we need to.
+
+python_major_version=$(python -V 2>&1 | cut -c 8)
+python_target_binary=$(which python$python_target_version)
+if ! test "$python_major_version" = "$python_target_version" && test "$python_target_binary"
+then
+ mkdir temp_python
+ PATH="$(pwd)/temp_python:$PATH" && export PATH
+ ln -s $python_target_binary temp_python/python
+fi
+
+python_major_version=$(python -V 2>&1 | cut -c 8)
+if ! test "$python_major_version" = "$python_target_version"
+then
+ skip_all="skipping python$python_target_version-specific git p4 tests; python$python_target_version not available"
+ test_done
+fi
+
+remove_user_cache () {
+ rm "$HOME/.gitp4-usercache.txt" || true
+}
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+test_expect_success 'init depot' '
+ (
+ cd "$cli" &&
+
+ p4_add_user "utf8_author" "ǣuthor" &&
+ P4USER=utf8_author &&
+ touch file1 &&
+ p4 add file1 &&
+ p4 submit -d "first CL has some utf-8 tǣxt" &&
+
+ p4_add_user "latin1_author" "$(echo æuthor |
+ iconv -f utf8 -t latin1)" &&
+ P4USER=latin1_author &&
+ touch file2 &&
+ p4 add file2 &&
+ p4 submit -d "$(echo second CL has some latin-1 tæxt |
+ iconv -f utf8 -t latin1)" &&
+
+ p4_add_user "cp1252_author" "$(echo æuthœr |
+ iconv -f utf8 -t cp1252)" &&
+ P4USER=cp1252_author &&
+ touch file3 &&
+ p4 add file3 &&
+ p4 submit -d "$(echo third CL has sœme cp-1252 tæxt |
+ iconv -f utf8 -t cp1252)" &&
+
+ p4_add_user "cp850_author" "$(echo Åuthor |
+ iconv -f utf8 -t cp850)" &&
+ P4USER=cp850_author &&
+ touch file4 &&
+ p4 add file4 &&
+ p4 submit -d "$(echo fourth CL hÅs some cp850 text |
+ iconv -f utf8 -t cp850)"
+ )
+'
+
+test_expect_success 'clone non-utf8 repo with strict encoding' '
+ test_when_finished cleanup_git &&
+ test_when_finished remove_user_cache &&
+ test_must_fail git -c git-p4.metadataDecodingStrategy=strict p4.py clone --dest="$git" //depot@all 2>err &&
+ grep "Decoding perforce metadata failed!" err
+'
+
+test_expect_success 'check utf-8 contents with passthrough strategy' '
+ test_when_finished cleanup_git &&
+ test_when_finished remove_user_cache &&
+ git -c git-p4.metadataDecodingStrategy=passthrough p4.py clone --dest="$git" //depot@all &&
+ (
+ cd "$git" &&
+ git log >actual &&
+ grep "some utf-8 tǣxt" actual &&
+ grep "ǣuthor" actual
+ )
+'
+
+test_expect_success 'check latin-1 contents corrupted in git with passthrough strategy' '
+ test_when_finished cleanup_git &&
+ test_when_finished remove_user_cache &&
+ git -c git-p4.metadataDecodingStrategy=passthrough p4.py clone --dest="$git" //depot@all &&
+ (
+ cd "$git" &&
+ git log >actual &&
+ badly_encoded_in_git=$(echo "some latin-1 tæxt" | iconv -f utf8 -t latin1) &&
+ grep "$badly_encoded_in_git" actual &&
+ bad_author_in_git="$(echo æuthor | iconv -f utf8 -t latin1)" &&
+ grep "$bad_author_in_git" actual
+ )
+'
+
+test_expect_success 'check utf-8 contents with fallback strategy' '
+ test_when_finished cleanup_git &&
+ test_when_finished remove_user_cache &&
+ git -c git-p4.metadataDecodingStrategy=fallback p4.py clone --dest="$git" //depot@all &&
+ (
+ cd "$git" &&
+ git log >actual &&
+ grep "some utf-8 tǣxt" actual &&
+ grep "ǣuthor" actual
+ )
+'
+
+test_expect_success 'check latin-1 contents with fallback strategy' '
+ test_when_finished cleanup_git &&
+ test_when_finished remove_user_cache &&
+ git -c git-p4.metadataDecodingStrategy=fallback p4.py clone --dest="$git" //depot@all &&
+ (
+ cd "$git" &&
+ git log >actual &&
+ grep "some latin-1 tæxt" actual &&
+ grep "æuthor" actual
+ )
+'
+
+test_expect_success 'check cp-1252 contents with fallback strategy' '
+ test_when_finished cleanup_git &&
+ test_when_finished remove_user_cache &&
+ git -c git-p4.metadataDecodingStrategy=fallback p4.py clone --dest="$git" //depot@all &&
+ (
+ cd "$git" &&
+ git log >actual &&
+ grep "sœme cp-1252 tæxt" actual &&
+ grep "æuthœr" actual
+ )
+'
+
+test_expect_success 'check cp850 contents parsed with correct fallback' '
+ test_when_finished cleanup_git &&
+ test_when_finished remove_user_cache &&
+ git -c git-p4.metadataDecodingStrategy=fallback -c git-p4.metadataFallbackEncoding=cp850 p4.py clone --dest="$git" //depot@all &&
+ (
+ cd "$git" &&
+ git log >actual &&
+ grep "hÅs some cp850 text" actual &&
+ grep "Åuthor" actual
+ )
+'
+
+test_expect_success 'check cp850-only contents escaped when cp1252 is fallback' '
+ test_when_finished cleanup_git &&
+ test_when_finished remove_user_cache &&
+ git -c git-p4.metadataDecodingStrategy=fallback p4.py clone --dest="$git" //depot@all &&
+ (
+ cd "$git" &&
+ git log >actual &&
+ grep "h%8Fs some cp850 text" actual &&
+ grep "%8Futhor" actual
+ )
+'
+
+test_expect_success 'check cp-1252 contents on later sync after clone with fallback strategy' '
+ test_when_finished cleanup_git &&
+ test_when_finished remove_user_cache &&
+ git -c git-p4.metadataDecodingStrategy=fallback p4.py clone --dest="$git" //depot@all &&
+ (
+ cd "$cli" &&
+ P4USER=cp1252_author &&
+ touch file10 &&
+ p4 add file10 &&
+ p4 submit -d "$(echo later CL has sœme more cp-1252 tæxt |
+ iconv -f utf8 -t cp1252)"
+ ) &&
+ (
+ cd "$git" &&
+
+ git p4.py sync --branch=master &&
+
+ git log p4/master >actual &&
+ grep "sœme more cp-1252 tæxt" actual &&
+ grep "æuthœr" actual
+ )
+'
+
+############################
+## / END REPEATED SECTION ##
+############################
+
+
+test_expect_success 'fallback (both utf-8 and cp-1252 contents handled) is the default with python3' '
+ test_when_finished cleanup_git &&
+ test_when_finished remove_user_cache &&
+ git p4.py clone --dest="$git" //depot@all &&
+ (
+ cd "$git" &&
+ git log >actual &&
+ grep "sœme cp-1252 tæxt" actual &&
+ grep "æuthœr" actual
+ )
+'
+
+test_done
diff --git a/t/t9850-shell.sh b/t/t9850-shell.sh
new file mode 100755
index 0000000..cfc71c3
--- /dev/null
+++ b/t/t9850-shell.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+test_description='git shell tests'
+. ./test-lib.sh
+
+test_expect_success 'shell allows upload-pack' '
+ printf 0000 >input &&
+ git upload-pack . <input >expect &&
+ git shell -c "git-upload-pack $SQ.$SQ" <input >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'shell forbids other commands' '
+ test_must_fail git shell -c "git config foo.bar baz"
+'
+
+test_expect_success 'shell forbids interactive use by default' '
+ test_must_fail git shell
+'
+
+test_expect_success 'shell allows interactive command' '
+ mkdir git-shell-commands &&
+ write_script git-shell-commands/ping <<-\EOF &&
+ echo pong
+ EOF
+ echo pong >expect &&
+ echo ping | git shell >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'shell complains of overlong commands' '
+ perl -e "print \"a\" x 2**12 for (0..2**19)" |
+ test_must_fail git shell 2>err &&
+ grep "too long" err
+'
+
+test_done
diff --git a/t/t9901-git-web--browse.sh b/t/t9901-git-web--browse.sh
index de7152f..19f56e5 100755
--- a/t/t9901-git-web--browse.sh
+++ b/t/t9901-git-web--browse.sh
@@ -5,6 +5,7 @@ test_description='git web--browse basic tests
This test checks that git web--browse can handle various valid URLs.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_web_browse () {
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 5decc3b..569cf23 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -5,6 +5,14 @@
test_description='test bash completion'
+# The Bash completion scripts must not print anything to either stdout or
+# stderr, which we try to verify. When tracing is enabled without support for
+# BASH_XTRACEFD this assertion will fail, so we have to mark the test as
+# untraceable with such ancient Bash versions.
+test_untraceable=UnfortunatelyYes
+
+# Override environment and always use master for the default initial branch
+# name for these tests, so that rev completion candidates are as expected.
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
@@ -90,9 +98,11 @@ test_completion ()
else
sed -e 's/Z$//' |sort >expected
fi &&
- run_completion "$1" &&
+ run_completion "$1" >"$TRASH_DIRECTORY"/bash-completion-output 2>&1 &&
sort out >out_sorted &&
- test_cmp expected out_sorted
+ test_cmp expected out_sorted &&
+ test_must_be_empty "$TRASH_DIRECTORY"/bash-completion-output &&
+ rm "$TRASH_DIRECTORY"/bash-completion-output
}
# Test __gitcomp.
@@ -408,40 +418,40 @@ test_expect_success '__gitdir - remote as argument' '
test_expect_success '__git_dequote - plain unquoted word' '
__git_dequote unquoted-word &&
- verbose test unquoted-word = "$dequoted_word"
+ test unquoted-word = "$dequoted_word"
'
# input: b\a\c\k\'\\\"s\l\a\s\h\es
# expected: back'\"slashes
test_expect_success '__git_dequote - backslash escaped' '
__git_dequote "b\a\c\k\\'\''\\\\\\\"s\l\a\s\h\es" &&
- verbose test "back'\''\\\"slashes" = "$dequoted_word"
+ test "back'\''\\\"slashes" = "$dequoted_word"
'
# input: sin'gle\' '"quo'ted
# expected: single\ "quoted
test_expect_success '__git_dequote - single quoted' '
__git_dequote "'"sin'gle\\\\' '\\\"quo'ted"'" &&
- verbose test '\''single\ "quoted'\'' = "$dequoted_word"
+ test '\''single\ "quoted'\'' = "$dequoted_word"
'
# input: dou"ble\\" "\"\quot"ed
# expected: double\ "\quoted
test_expect_success '__git_dequote - double quoted' '
__git_dequote '\''dou"ble\\" "\"\quot"ed'\'' &&
- verbose test '\''double\ "\quoted'\'' = "$dequoted_word"
+ test '\''double\ "\quoted'\'' = "$dequoted_word"
'
# input: 'open single quote
test_expect_success '__git_dequote - open single quote' '
__git_dequote "'\''open single quote" &&
- verbose test "open single quote" = "$dequoted_word"
+ test "open single quote" = "$dequoted_word"
'
# input: "open double quote
test_expect_success '__git_dequote - open double quote' '
__git_dequote "\"open double quote" &&
- verbose test "open double quote" = "$dequoted_word"
+ test "open double quote" = "$dequoted_word"
'
@@ -619,7 +629,7 @@ test_expect_success '__git_is_configured_remote' '
test_when_finished "git remote remove remote_2" &&
git remote add remote_2 git://remote_2 &&
(
- verbose __git_is_configured_remote remote_2 &&
+ __git_is_configured_remote remote_2 &&
test_must_fail __git_is_configured_remote non-existent
)
'
@@ -879,7 +889,7 @@ test_expect_success '__git_refs - unique remote branches for git checkout DWIMer
refs/remotes/remote/branch-in-remote
do
git update-ref $remote_ref main &&
- test_when_finished "git update-ref -d $remote_ref"
+ test_when_finished "git update-ref -d $remote_ref" || return 1
done &&
(
cur= &&
@@ -1052,7 +1062,7 @@ test_expect_success '__git_refs - only matching refs - checkout DWIMery' '
refs/remotes/remote/branch-in-remote
do
git update-ref $remote_ref main &&
- test_when_finished "git update-ref -d $remote_ref"
+ test_when_finished "git update-ref -d $remote_ref" || return 1
done &&
(
cur=mat &&
@@ -1253,6 +1263,29 @@ test_expect_success '__git_complete_fetch_refspecs - fully qualified & prefix' '
test_cmp expected out
'
+test_expect_success '__git_complete_worktree_paths' '
+ test_when_finished "git worktree remove other_wt" &&
+ git worktree add --orphan other_wt &&
+ run_completion "git worktree remove " &&
+ grep other_wt out
+'
+
+test_expect_success '__git_complete_worktree_paths - not a git repository' '
+ (
+ cd non-repo &&
+ GIT_CEILING_DIRECTORIES="$ROOT" &&
+ export GIT_CEILING_DIRECTORIES &&
+ test_completion "git worktree remove " ""
+ )
+'
+
+test_expect_success '__git_complete_worktree_paths with -C' '
+ test_when_finished "git -C otherrepo worktree remove otherrepo_wt" &&
+ git -C otherrepo worktree add --orphan otherrepo_wt &&
+ run_completion "git -C otherrepo worktree remove " &&
+ grep otherrepo_wt out
+'
+
test_expect_success 'git switch - with no options, complete local branches and unique remote branch names for DWIM logic' '
test_completion "git switch " <<-\EOF
branch-in-other Z
@@ -1262,6 +1295,142 @@ test_expect_success 'git switch - with no options, complete local branches and u
EOF
'
+test_expect_success 'git bisect - when not bisecting, complete only replay and start subcommands' '
+ test_completion "git bisect " <<-\EOF
+ replay Z
+ start Z
+ EOF
+'
+
+test_expect_success 'git bisect - complete options to start subcommand' '
+ test_completion "git bisect start --" <<-\EOF
+ --term-new Z
+ --term-bad Z
+ --term-old Z
+ --term-good Z
+ --no-checkout Z
+ --first-parent Z
+ EOF
+'
+
+test_expect_success 'setup for git-bisect tests requiring a repo' '
+ git init git-bisect &&
+ (
+ cd git-bisect &&
+ echo "initial contents" >file &&
+ git add file &&
+ git commit -am "Initial commit" &&
+ git tag initial &&
+ echo "new line" >>file &&
+ git commit -am "First change" &&
+ echo "another new line" >>file &&
+ git commit -am "Second change" &&
+ git tag final
+ )
+'
+
+test_expect_success 'git bisect - start subcommand arguments before double-dash are completed as revs' '
+ (
+ cd git-bisect &&
+ test_completion "git bisect start " <<-\EOF
+ HEAD Z
+ final Z
+ initial Z
+ master Z
+ EOF
+ )
+'
+
+# Note that these arguments are <pathspec>s, which in practice the fallback
+# completion (not the git completion) later ends up completing as paths.
+test_expect_success 'git bisect - start subcommand arguments after double-dash are not completed' '
+ (
+ cd git-bisect &&
+ test_completion "git bisect start final initial -- " ""
+ )
+'
+
+test_expect_success 'setup for git-bisect tests requiring ongoing bisection' '
+ (
+ cd git-bisect &&
+ git bisect start --term-new=custom_new --term-old=custom_old final initial
+ )
+'
+
+test_expect_success 'git-bisect - when bisecting all subcommands are candidates' '
+ (
+ cd git-bisect &&
+ test_completion "git bisect " <<-\EOF
+ start Z
+ bad Z
+ custom_new Z
+ custom_old Z
+ new Z
+ good Z
+ old Z
+ terms Z
+ skip Z
+ reset Z
+ visualize Z
+ replay Z
+ log Z
+ run Z
+ help Z
+ EOF
+ )
+'
+
+test_expect_success 'git-bisect - options to terms subcommand are candidates' '
+ (
+ cd git-bisect &&
+ test_completion "git bisect terms --" <<-\EOF
+ --term-bad Z
+ --term-good Z
+ --term-new Z
+ --term-old Z
+ EOF
+ )
+'
+
+test_expect_success 'git-bisect - git-log options to visualize subcommand are candidates' '
+ (
+ cd git-bisect &&
+ # The completion used for git-log and here does not complete
+ # every git-log option, so rather than hope to stay in sync
+ # with exactly what it does we will just spot-test here.
+ test_completion "git bisect visualize --sta" <<-\EOF &&
+ --stat Z
+ EOF
+ test_completion "git bisect visualize --summar" <<-\EOF
+ --summary Z
+ EOF
+ )
+'
+
+test_expect_success 'git-bisect - view subcommand is not a candidate' '
+ (
+ cd git-bisect &&
+ test_completion "git bisect vi" <<-\EOF
+ visualize Z
+ EOF
+ )
+'
+
+test_expect_success 'git-bisect - existing view subcommand is recognized and enables completion of git-log options' '
+ (
+ cd git-bisect &&
+ # The completion used for git-log and here does not complete
+ # every git-log option, so rather than hope to stay in sync
+ # with exactly what it does we will just spot-test here.
+ test_completion "git bisect view --sta" <<-\EOF &&
+ --stat Z
+ EOF
+ test_completion "git bisect view --summar" <<-\EOF
+ --summary Z
+ EOF
+ )
+'
+
test_expect_success 'git checkout - completes refs and unique remote branches for DWIM' '
test_completion "git checkout " <<-\EOF
HEAD Z
@@ -1447,6 +1616,166 @@ test_expect_success 'git checkout - with --detach, complete only references' '
EOF
'
+test_expect_success 'setup sparse-checkout tests' '
+ # set up sparse-checkout repo
+ git init sparse-checkout &&
+ (
+ cd sparse-checkout &&
+ mkdir -p folder1/0/1 folder2/0 folder3 &&
+ touch folder1/0/1/t.txt &&
+ touch folder2/0/t.txt &&
+ touch folder3/t.txt &&
+ git add . &&
+ git commit -am "Initial commit"
+ )
+'
+
+test_expect_success 'sparse-checkout completes subcommands' '
+ test_completion "git sparse-checkout " <<-\EOF
+ list Z
+ init Z
+ set Z
+ add Z
+ reapply Z
+ disable Z
+ EOF
+'
+
+test_expect_success 'cone mode sparse-checkout completes directory names' '
+ # initialize sparse-checkout definitions
+ git -C sparse-checkout sparse-checkout set --cone folder1/0 folder3 &&
+
+ # test tab completion
+ (
+ cd sparse-checkout &&
+ test_completion "git sparse-checkout set f" <<-\EOF
+ folder1/
+ folder2/
+ folder3/
+ EOF
+ ) &&
+
+ (
+ cd sparse-checkout &&
+ test_completion "git sparse-checkout set folder1/" <<-\EOF
+ folder1/0/
+ EOF
+ ) &&
+
+ (
+ cd sparse-checkout &&
+ test_completion "git sparse-checkout set folder1/0/" <<-\EOF
+ folder1/0/1/
+ EOF
+ ) &&
+
+ (
+ cd sparse-checkout/folder1 &&
+ test_completion "git sparse-checkout add 0" <<-\EOF
+ 0/
+ EOF
+ )
+'
+
+test_expect_success 'cone mode sparse-checkout completes directory names with spaces and accents' '
+ # reset sparse-checkout
+ git -C sparse-checkout sparse-checkout disable &&
+ (
+ cd sparse-checkout &&
+ mkdir "directory with spaces" &&
+ mkdir "directory-with-áccent" &&
+ >"directory with spaces/randomfile" &&
+ >"directory-with-áccent/randomfile" &&
+ git add . &&
+ git commit -m "Add directory with spaces and directory with accent" &&
+ git sparse-checkout set --cone "directory with spaces" \
+ "directory-with-áccent" &&
+ test_completion "git sparse-checkout add dir" <<-\EOF &&
+ directory with spaces/
+ directory-with-áccent/
+ EOF
+ rm -rf "directory with spaces" &&
+ rm -rf "directory-with-áccent" &&
+ git add . &&
+ git commit -m "Remove directory with spaces and directory with accent"
+ )
+'
+
+# use FUNNYNAMES to avoid running on Windows, which doesn't permit tabs in paths
+test_expect_success FUNNYNAMES 'cone mode sparse-checkout completes directory names with tabs' '
+ # reset sparse-checkout
+ git -C sparse-checkout sparse-checkout disable &&
+ (
+ cd sparse-checkout &&
+ mkdir "$(printf "directory\twith\ttabs")" &&
+ >"$(printf "directory\twith\ttabs")/randomfile" &&
+ git add . &&
+ git commit -m "Add directory with tabs" &&
+ git sparse-checkout set --cone \
+ "$(printf "directory\twith\ttabs")" &&
+ test_completion "git sparse-checkout add dir" <<-\EOF &&
+ directory with tabs/
+ EOF
+ rm -rf "$(printf "directory\twith\ttabs")" &&
+ git add . &&
+ git commit -m "Remove directory with tabs"
+ )
+'
+
+# use FUNNYNAMES to avoid running on Windows, and !CYGWIN for Cygwin, as neither permit backslashes in paths
+test_expect_success FUNNYNAMES,!CYGWIN 'cone mode sparse-checkout completes directory names with backslashes' '
+ # reset sparse-checkout
+ git -C sparse-checkout sparse-checkout disable &&
+ (
+ cd sparse-checkout &&
+ mkdir "directory\with\backslashes" &&
+ >"directory\with\backslashes/randomfile" &&
+ git add . &&
+ git commit -m "Add directory with backslashes" &&
+ git sparse-checkout set --cone \
+ "directory\with\backslashes" &&
+ test_completion "git sparse-checkout add dir" <<-\EOF &&
+ directory\with\backslashes/
+ EOF
+ rm -rf "directory\with\backslashes" &&
+ git add . &&
+ git commit -m "Remove directory with backslashes"
+ )
+'
+
+test_expect_success 'non-cone mode sparse-checkout gives rooted paths' '
+ # reset sparse-checkout repo to non-cone mode
+ git -C sparse-checkout sparse-checkout disable &&
+ git -C sparse-checkout sparse-checkout set --no-cone &&
+
+ (
+ cd sparse-checkout &&
+ # expected to be empty since we have not configured
+ # custom completion for non-cone mode
+ test_completion "git sparse-checkout set f" <<-\EOF
+ /folder1/0/1/t.txt Z
+ /folder1/expected Z
+ /folder1/out Z
+ /folder1/out_sorted Z
+ /folder2/0/t.txt Z
+ /folder3/t.txt Z
+ EOF
+ )
+'
+
+test_expect_success 'git sparse-checkout set --cone completes directory names' '
+ git -C sparse-checkout sparse-checkout disable &&
+
+ (
+ cd sparse-checkout &&
+ test_completion "git sparse-checkout set --cone f" <<-\EOF
+ folder1/
+ folder2/
+ folder3/
+ EOF
+ )
+'
+
test_expect_success 'git switch - with -d, complete all references' '
test_completion "git switch -d " <<-\EOF
HEAD Z
@@ -1470,14 +1799,22 @@ test_expect_success 'git checkout - with -d, complete only references' '
'
test_expect_success 'git switch - with --track, complete only remote branches' '
- test_completion "git switch --track " <<-\EOF
+ test_completion "git switch --track " <<-\EOF &&
+ other/branch-in-other Z
+ other/main-in-other Z
+ EOF
+ test_completion "git switch -t " <<-\EOF
other/branch-in-other Z
other/main-in-other Z
EOF
'
test_expect_success 'git checkout - with --track, complete only remote branches' '
- test_completion "git checkout --track " <<-\EOF
+ test_completion "git checkout --track " <<-\EOF &&
+ other/branch-in-other Z
+ other/main-in-other Z
+ EOF
+ test_completion "git checkout -t " <<-\EOF
other/branch-in-other Z
other/main-in-other Z
EOF
@@ -1760,6 +2097,14 @@ test_expect_success 'git checkout - --orphan with branch already provided comple
EOF
'
+test_expect_success 'git restore completes modified files' '
+ test_commit A a.file &&
+ echo B >a.file &&
+ test_completion "git restore a." <<-\EOF
+ a.file
+ EOF
+'
+
test_expect_success 'teardown after ref completion' '
git branch -d matching-branch &&
git tag -d matching-tag &&
@@ -2103,6 +2448,36 @@ test_expect_success 'checkout completes ref names' '
EOF
'
+test_expect_success 'checkout does not match ref names of a different case' '
+ test_completion "git checkout M" ""
+'
+
+test_expect_success 'checkout matches case insensitively with GIT_COMPLETION_IGNORE_CASE' '
+ (
+ GIT_COMPLETION_IGNORE_CASE=1 &&
+ test_completion "git checkout M" <<-\EOF
+ main Z
+ mybranch Z
+ mytag Z
+ EOF
+ )
+'
+
+test_expect_success 'checkout completes pseudo refs' '
+ test_completion "git checkout H" <<-\EOF
+ HEAD Z
+ EOF
+'
+
+test_expect_success 'checkout completes pseudo refs case insensitively with GIT_COMPLETION_IGNORE_CASE' '
+ (
+ GIT_COMPLETION_IGNORE_CASE=1 &&
+ test_completion "git checkout h" <<-\EOF
+ HEAD Z
+ EOF
+ )
+'
+
test_expect_success 'git -C <path> checkout uses the right repo' '
test_completion "git -C subdir -C subsubdir -C .. -C ../otherrepo checkout b" <<-\EOF
branch-in-other Z
@@ -2148,6 +2523,9 @@ test_expect_success PERL 'send-email' '
--cover-from-description=Z
--cover-letter Z
EOF
+ test_completion "git send-email --val" <<-\EOF &&
+ --validate Z
+ EOF
test_completion "git send-email ma" "main "
'
@@ -2271,6 +2649,24 @@ test_expect_success 'completion used <cmd> completion for alias: !f() { : git <c
EOF
'
+test_expect_success 'completion used <cmd> completion for alias: !f() { : <cmd> ; ... }' '
+ test_config alias.co "!f() { : checkout ; if ... } f" &&
+ test_completion "git co m" <<-\EOF
+ main Z
+ mybranch Z
+ mytag Z
+ EOF
+'
+
+test_expect_success 'completion used <cmd> completion for alias: !f() { : <cmd>; ... }' '
+ test_config alias.co "!f() { : checkout; if ... } f" &&
+ test_completion "git co m" <<-\EOF
+ main Z
+ mybranch Z
+ mytag Z
+ EOF
+'
+
test_expect_success 'completion without explicit _git_xxx function' '
test_completion "git version --" <<-\EOF
--build-options Z
@@ -2330,6 +2726,13 @@ test_expect_success 'git config - section' '
EOF
'
+test_expect_success 'git config - section include, includeIf' '
+ test_completion "git config inclu" <<-\EOF
+ include.Z
+ includeIf.Z
+ EOF
+'
+
test_expect_success 'git config - variable name' '
test_completion "git config log.d" <<-\EOF
log.date Z
@@ -2338,6 +2741,41 @@ test_expect_success 'git config - variable name' '
EOF
'
+test_expect_success 'git config - variable name include' '
+ test_completion "git config include.p" <<-\EOF
+ include.path Z
+ EOF
+'
+
+test_expect_success 'setup for git config submodule tests' '
+ test_create_repo sub &&
+ test_commit -C sub initial &&
+ git submodule add ./sub
+'
+
+test_expect_success 'git config - variable name - submodule and __git_compute_first_level_config_vars_for_section' '
+ test_completion "git config submodule." <<-\EOF
+ submodule.active Z
+ submodule.alternateErrorStrategy Z
+ submodule.alternateLocation Z
+ submodule.fetchJobs Z
+ submodule.propagateBranches Z
+ submodule.recurse Z
+ submodule.sub.Z
+ EOF
+'
+
+test_expect_success 'git config - variable name - __git_compute_second_level_config_vars_for_section' '
+ test_completion "git config submodule.sub." <<-\EOF
+ submodule.sub.url Z
+ submodule.sub.update Z
+ submodule.sub.branch Z
+ submodule.sub.fetchRecurseSubmodules Z
+ submodule.sub.ignore Z
+ submodule.sub.active Z
+ EOF
+'
+
test_expect_success 'git config - value' '
test_completion "git config color.pager " <<-\EOF
false Z
@@ -2389,6 +2827,20 @@ test_expect_success 'git clone --config= - value' '
EOF
'
+test_expect_success 'git reflog show' '
+ test_when_finished "git checkout - && git branch -d shown" &&
+ git checkout -b shown &&
+ test_completion "git reflog sho" <<-\EOF &&
+ show Z
+ shown Z
+ EOF
+ test_completion "git reflog show sho" "shown " &&
+ test_completion "git reflog shown sho" "shown " &&
+ test_completion "git reflog --unt" "--until=" &&
+ test_completion "git reflog show --unt" "--until=" &&
+ test_completion "git reflog shown --unt" "--until="
+'
+
test_expect_success 'options with value' '
test_completion "git merge -X diff-algorithm=" <<-\EOF
@@ -2396,27 +2848,33 @@ test_expect_success 'options with value' '
'
test_expect_success 'sourcing the completion script clears cached commands' '
- __git_compute_all_commands &&
- verbose test -n "$__git_all_commands" &&
- . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" &&
- verbose test -z "$__git_all_commands"
+ (
+ __git_compute_all_commands &&
+ test -n "$__git_all_commands" &&
+ . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" &&
+ test -z "$__git_all_commands"
+ )
'
test_expect_success 'sourcing the completion script clears cached merge strategies' '
- __git_compute_merge_strategies &&
- verbose test -n "$__git_merge_strategies" &&
- . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" &&
- verbose test -z "$__git_merge_strategies"
+ (
+ __git_compute_merge_strategies &&
+ test -n "$__git_merge_strategies" &&
+ . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" &&
+ test -z "$__git_merge_strategies"
+ )
'
test_expect_success 'sourcing the completion script clears cached --options' '
- __gitcomp_builtin checkout &&
- verbose test -n "$__gitcomp_builtin_checkout" &&
- __gitcomp_builtin notes_edit &&
- verbose test -n "$__gitcomp_builtin_notes_edit" &&
- . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" &&
- verbose test -z "$__gitcomp_builtin_checkout" &&
- verbose test -z "$__gitcomp_builtin_notes_edit"
+ (
+ __gitcomp_builtin checkout &&
+ test -n "$__gitcomp_builtin_checkout" &&
+ __gitcomp_builtin notes_edit &&
+ test -n "$__gitcomp_builtin_notes_edit" &&
+ . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" &&
+ test -z "$__gitcomp_builtin_checkout" &&
+ test -z "$__gitcomp_builtin_notes_edit"
+ )
'
test_expect_success 'option aliases are not shown by default' '
@@ -2424,12 +2882,45 @@ test_expect_success 'option aliases are not shown by default' '
'
test_expect_success 'option aliases are shown with GIT_COMPLETION_SHOW_ALL' '
- . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" &&
- GIT_COMPLETION_SHOW_ALL=1 && export GIT_COMPLETION_SHOW_ALL &&
- test_completion "git clone --recurs" <<-\EOF
- --recurse-submodules Z
- --recursive Z
- EOF
+ (
+ . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" &&
+ GIT_COMPLETION_SHOW_ALL=1 && export GIT_COMPLETION_SHOW_ALL &&
+ test_completion "git clone --recurs" <<-\EOF
+ --recurse-submodules Z
+ --recursive Z
+ EOF
+ )
+'
+
+test_expect_success 'plumbing commands are excluded without GIT_COMPLETION_SHOW_ALL_COMMANDS' '
+ (
+ . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" &&
+ sane_unset GIT_TESTING_PORCELAIN_COMMAND_LIST &&
+
+ # Just mainporcelain, not plumbing commands
+ run_completion "git c" &&
+ grep checkout out &&
+ ! grep cat-file out
+ )
+'
+
+test_expect_success 'all commands are shown with GIT_COMPLETION_SHOW_ALL_COMMANDS (also main non-builtin)' '
+ (
+ . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" &&
+ GIT_COMPLETION_SHOW_ALL_COMMANDS=1 &&
+ export GIT_COMPLETION_SHOW_ALL_COMMANDS &&
+ sane_unset GIT_TESTING_PORCELAIN_COMMAND_LIST &&
+
+ # Both mainporcelain and plumbing commands
+ run_completion "git c" &&
+ grep checkout out &&
+ grep cat-file out &&
+
+ # Check "gitk", a "main" command, but not a built-in + more plumbing
+ run_completion "git g" &&
+ grep gitk out &&
+ grep get-tar-commit-id out
+ )
'
test_expect_success '__git_complete' '
@@ -2452,4 +2943,31 @@ test_expect_success '__git_complete' '
test_must_fail __git_complete ga missing
'
+test_expect_success '__git_pseudoref_exists' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ sane_unset __git_repo_path &&
+
+ # HEAD should exist, even if it points to an unborn branch.
+ __git_pseudoref_exists HEAD >output 2>&1 &&
+ test_must_be_empty output &&
+
+ # HEAD points to an existing branch, so it should exist.
+ test_commit A &&
+ __git_pseudoref_exists HEAD >output 2>&1 &&
+ test_must_be_empty output &&
+
+ # CHERRY_PICK_HEAD does not exist, so the existence check should fail.
+ ! __git_pseudoref_exists CHERRY_PICK_HEAD >output 2>&1 &&
+ test_must_be_empty output &&
+
+ # CHERRY_PICK_HEAD points to a commit, so it should exist.
+ git update-ref CHERRY_PICK_HEAD A &&
+ __git_pseudoref_exists CHERRY_PICK_HEAD >output 2>&1 &&
+ test_must_be_empty output
+ )
+'
+
test_done
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index bbd513b..d667dda 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -13,10 +13,10 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. "$GIT_BUILD_DIR/contrib/completion/git-prompt.sh"
actual="$TRASH_DIRECTORY/actual"
-c_red='\\[\\e[31m\\]'
-c_green='\\[\\e[32m\\]'
-c_lblue='\\[\\e[1;34m\\]'
-c_clear='\\[\\e[0m\\]'
+c_red='\001\e[31m\002'
+c_green='\001\e[32m\002'
+c_lblue='\001\e[1;34m\002'
+c_clear='\001\e[0m\002'
test_expect_success 'setup for prompt tests' '
git init otherrepo &&
@@ -590,7 +590,7 @@ test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirt
'
test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index and worktree' '
- printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}*${c_green}+${c_clear}):AFTER\\nmain" >expected &&
+ printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}*${c_clear}${c_green}+${c_clear}):AFTER\\nmain" >expected &&
echo "dirty index" >file &&
test_when_finished "git reset --hard" &&
git add -u &&
@@ -759,4 +759,20 @@ test_expect_success 'prompt - hide if pwd ignored - inside gitdir' '
test_cmp expected "$actual"
'
+test_expect_success 'prompt - conflict indicator' '
+ printf " (main|CONFLICT)" >expected &&
+ echo "stash" >file &&
+ git stash &&
+ test_when_finished "git stash drop" &&
+ echo "commit" >file &&
+ git commit -m "commit" file &&
+ test_when_finished "git reset --hard HEAD~" &&
+ test_must_fail git stash apply &&
+ (
+ GIT_PS1_SHOWCONFLICTSTATE="yes" &&
+ __git_ps1 >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
test_done
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index eef2262..862d80c 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -14,7 +14,7 @@
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with this program. If not, see http://www.gnu.org/licenses/ .
+# along with this program. If not, see https://www.gnu.org/licenses/ .
# The semantics of the editor variables are that of invoking
# sh -c "$EDITOR \"$@\"" files ...
@@ -32,6 +32,14 @@ test_set_editor () {
export EDITOR
}
+# Like test_set_editor but sets GIT_SEQUENCE_EDITOR instead of EDITOR
+test_set_sequence_editor () {
+ FAKE_SEQUENCE_EDITOR="$1"
+ export FAKE_SEQUENCE_EDITOR
+ GIT_SEQUENCE_EDITOR='"$FAKE_SEQUENCE_EDITOR"'
+ export GIT_SEQUENCE_EDITOR
+}
+
test_decode_color () {
awk '
function name(n) {
@@ -243,6 +251,61 @@ debug () {
done
}
+# Usage: test_ref_exists [options] <ref>
+#
+# -C <dir>:
+# Run all git commands in directory <dir>
+#
+# This helper function checks whether a reference exists. Symrefs or object IDs
+# will not be resolved. Can be used to check references with bad names.
+test_ref_exists () {
+ local indir=
+
+ while test $# != 0
+ do
+ case "$1" in
+ -C)
+ indir="$2"
+ shift
+ ;;
+ *)
+ break
+ ;;
+ esac
+ shift
+ done &&
+
+ indir=${indir:+"$indir"/} &&
+
+ if test "$#" != 1
+ then
+ BUG "expected exactly one reference"
+ fi &&
+
+ git ${indir:+ -C "$indir"} show-ref --exists "$1"
+}
+
+# Behaves the same as test_ref_exists, except that it checks for the absence of
+# a reference. This is preferable to `! test_ref_exists` as this function is
+# able to distinguish actually-missing references from other, generic errors.
+test_ref_missing () {
+ test_ref_exists "$@"
+ case "$?" in
+ 2)
+ # This is the good case.
+ return 0
+ ;;
+ 0)
+ echo >&4 "test_ref_missing: reference exists"
+ return 1
+ ;;
+ *)
+ echo >&4 "test_ref_missing: generic error"
+ return 1
+ ;;
+ esac
+}
+
# Usage: test_commit [options] <message> [<file> [<contents> [<tag>]]]
# -C <dir>:
# Run all git commands in directory <dir>
@@ -273,13 +336,13 @@ debug () {
# <file>, <contents>, and <tag> all default to <message>.
test_commit () {
- notick= &&
- echo=echo &&
- append= &&
- author= &&
- signoff= &&
- indir= &&
- tag=light &&
+ local notick= &&
+ local echo=echo &&
+ local append= &&
+ local author= &&
+ local signoff= &&
+ local indir= &&
+ local tag=light &&
while test $# != 0
do
case "$1" in
@@ -322,14 +385,14 @@ test_commit () {
shift
done &&
indir=${indir:+"$indir"/} &&
- file=${2:-"$1.t"} &&
+ local file="${2:-"$1.t"}" &&
if test -n "$append"
then
$echo "${3-$1}" >>"$indir$file"
else
$echo "${3-$1}" >"$indir$file"
fi &&
- git ${indir:+ -C "$indir"} add "$file" &&
+ git ${indir:+ -C "$indir"} add -- "$file" &&
if test -z "$notick"
then
test_tick
@@ -534,8 +597,17 @@ test_config () {
config_dir=$1
shift
fi
- test_when_finished "test_unconfig ${config_dir:+-C '$config_dir'} '$1'" &&
- git ${config_dir:+-C "$config_dir"} config "$@"
+
+ # If --worktree is provided, use it to configure/unconfigure
+ is_worktree=
+ if test "$1" = --worktree
+ then
+ is_worktree=1
+ shift
+ fi
+
+ test_when_finished "test_unconfig ${config_dir:+-C '$config_dir'} ${is_worktree:+--worktree} '$1'" &&
+ git ${config_dir:+-C "$config_dir"} config ${is_worktree:+--worktree} "$@"
}
test_config_global () {
@@ -551,13 +623,89 @@ write_script () {
chmod +x "$1"
}
+# Usage: test_hook [options] <hook-name> <<-\EOF
+#
+# -C <dir>:
+# Run all git commands in directory <dir>
+# --setup
+# Setup a hook for subsequent tests, i.e. don't remove it in a
+# "test_when_finished"
+# --clobber
+# Overwrite an existing <hook-name>, if it exists. Implies
+# --setup (i.e. the "test_when_finished" is assumed to have been
+# set up already).
+# --disable
+# Disable (chmod -x) an existing <hook-name>, which must exist.
+# --remove
+# Remove (rm -f) an existing <hook-name>, which must exist.
+test_hook () {
+ setup= &&
+ clobber= &&
+ disable= &&
+ remove= &&
+ indir= &&
+ while test $# != 0
+ do
+ case "$1" in
+ -C)
+ indir="$2" &&
+ shift
+ ;;
+ --setup)
+ setup=t
+ ;;
+ --clobber)
+ clobber=t
+ ;;
+ --disable)
+ disable=t
+ ;;
+ --remove)
+ remove=t
+ ;;
+ -*)
+ BUG "invalid argument: $1"
+ ;;
+ *)
+ break
+ ;;
+ esac &&
+ shift
+ done &&
+
+ git_dir=$(git -C "$indir" rev-parse --absolute-git-dir) &&
+ hook_dir="$git_dir/hooks" &&
+ hook_file="$hook_dir/$1" &&
+ if test -n "$disable$remove"
+ then
+ test_path_is_file "$hook_file" &&
+ if test -n "$disable"
+ then
+ chmod -x "$hook_file"
+ elif test -n "$remove"
+ then
+ rm -f "$hook_file"
+ fi &&
+ return 0
+ fi &&
+ if test -z "$clobber"
+ then
+ test_path_is_missing "$hook_file"
+ fi &&
+ if test -z "$setup$clobber"
+ then
+ test_when_finished "rm \"$hook_file\""
+ fi &&
+ write_script "$hook_file"
+}
+
# Use test_set_prereq to tell that a particular prerequisite is available.
# The prerequisite can later be checked for in two ways:
#
# - Explicitly using test_have_prereq.
#
# - Implicitly by specifying the prerequisite tag in the calls to
-# test_expect_{success,failure} and test_external{,_without_stderr}.
+# test_expect_{success,failure}
#
# The single parameter is the prerequisite tag (a simple word, in all
# capital letters by convention).
@@ -575,8 +723,7 @@ test_set_prereq () {
# test_unset_prereq()
!*)
;;
- # (Temporary?) whitelist of things we can't easily
- # pretend not to support
+ # List of things we can't easily pretend to not support
SYMLINKS)
;;
# Inspecting whether GIT_TEST_FAIL_PREREQS is on
@@ -680,6 +827,17 @@ test_have_prereq () {
# Keep a list of missing prerequisites; restore
# the negative marker if necessary.
prerequisite=${negative_prereq:+!}$prerequisite
+
+ # Abort if this prereq was marked as required
+ if test -n "$GIT_TEST_REQUIRE_PREREQ"
+ then
+ case " $GIT_TEST_REQUIRE_PREREQ " in
+ *" $prerequisite "*)
+ BAIL_OUT "required prereq $prerequisite failed"
+ ;;
+ esac
+ fi
+
if test -z "$missing_prereq"
then
missing_prereq=$prerequisite
@@ -708,7 +866,7 @@ test_verify_prereq () {
}
test_expect_failure () {
- test_start_
+ test_start_ "$@"
test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
test "$#" = 2 ||
BUG "not 2 or 3 parameters to test-expect-failure"
@@ -716,6 +874,7 @@ test_expect_failure () {
export test_prereq
if ! test_skip "$@"
then
+ test -n "$test_skip_test_preamble" ||
say >&3 "checking known breakage of $TEST_NUMBER.$test_count '$1': $2"
if test_run_ "$2" expecting_failure
then
@@ -728,7 +887,7 @@ test_expect_failure () {
}
test_expect_success () {
- test_start_
+ test_start_ "$@"
test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
test "$#" = 2 ||
BUG "not 2 or 3 parameters to test-expect-success"
@@ -736,6 +895,7 @@ test_expect_success () {
export test_prereq
if ! test_skip "$@"
then
+ test -n "$test_skip_test_preamble" ||
say >&3 "expecting success of $TEST_NUMBER.$test_count '$1': $2"
if test_run_ "$2"
then
@@ -747,93 +907,6 @@ test_expect_success () {
test_finish_
}
-# test_external runs external test scripts that provide continuous
-# test output about their progress, and succeeds/fails on
-# zero/non-zero exit code. It outputs the test output on stdout even
-# in non-verbose mode, and announces the external script with "# run
-# <n>: ..." before running it. When providing relative paths, keep in
-# mind that all scripts run in "trash directory".
-# Usage: test_external description command arguments...
-# Example: test_external 'Perl API' perl ../path/to/test.pl
-test_external () {
- test "$#" = 4 && { test_prereq=$1; shift; } || test_prereq=
- test "$#" = 3 ||
- BUG "not 3 or 4 parameters to test_external"
- descr="$1"
- shift
- test_verify_prereq
- export test_prereq
- if ! test_skip "$descr" "$@"
- then
- # Announce the script to reduce confusion about the
- # test output that follows.
- say_color "" "# run $test_count: $descr ($*)"
- # Export TEST_DIRECTORY, TRASH_DIRECTORY and GIT_TEST_LONG
- # to be able to use them in script
- export TEST_DIRECTORY TRASH_DIRECTORY GIT_TEST_LONG
- # Run command; redirect its stderr to &4 as in
- # test_run_, but keep its stdout on our stdout even in
- # non-verbose mode.
- "$@" 2>&4
- if test "$?" = 0
- then
- if test $test_external_has_tap -eq 0; then
- test_ok_ "$descr"
- else
- say_color "" "# test_external test $descr was ok"
- test_success=$(($test_success + 1))
- fi
- else
- if test $test_external_has_tap -eq 0; then
- test_failure_ "$descr" "$@"
- else
- say_color error "# test_external test $descr failed: $@"
- test_failure=$(($test_failure + 1))
- fi
- fi
- fi
-}
-
-# Like test_external, but in addition tests that the command generated
-# no output on stderr.
-test_external_without_stderr () {
- # The temporary file has no (and must have no) security
- # implications.
- tmp=${TMPDIR:-/tmp}
- stderr="$tmp/git-external-stderr.$$.tmp"
- test_external "$@" 4> "$stderr"
- test -f "$stderr" || error "Internal error: $stderr disappeared."
- descr="no stderr: $1"
- shift
- say >&3 "# expecting no stderr from previous command"
- if test ! -s "$stderr"
- then
- rm "$stderr"
-
- if test $test_external_has_tap -eq 0; then
- test_ok_ "$descr"
- else
- say_color "" "# test_external_without_stderr test $descr was ok"
- test_success=$(($test_success + 1))
- fi
- else
- if test "$verbose" = t
- then
- output=$(echo; echo "# Stderr is:"; cat "$stderr")
- else
- output=
- fi
- # rm first in case test_failure exits.
- rm "$stderr"
- if test $test_external_has_tap -eq 0; then
- test_failure_ "$descr" "$@" "$output"
- else
- say_color error "# test_external_without_stderr test $descr failed: $@: $output"
- test_failure=$(($test_failure + 1))
- fi
- fi
-}
-
# debugging-friendly alternatives to "test [-f|-d|-e]"
# The commands test the existence or non-existence of $1
test_path_is_file () {
@@ -845,6 +918,16 @@ test_path_is_file () {
fi
}
+test_path_is_file_not_symlink () {
+ test "$#" -ne 1 && BUG "1 param"
+ test_path_is_file "$1" &&
+ if test -h "$1"
+ then
+ echo "$1 shouldn't be a symbolic link"
+ false
+ fi
+}
+
test_path_is_dir () {
test "$#" -ne 1 && BUG "1 param"
if ! test -d "$1"
@@ -854,6 +937,16 @@ test_path_is_dir () {
fi
}
+test_path_is_dir_not_symlink () {
+ test "$#" -ne 1 && BUG "1 param"
+ test_path_is_dir "$1" &&
+ if test -h "$1"
+ then
+ echo "$1 shouldn't be a symbolic link"
+ false
+ fi
+}
+
test_path_exists () {
test "$#" -ne 1 && BUG "1 param"
if ! test -e "$1"
@@ -863,11 +956,29 @@ test_path_exists () {
fi
}
+test_path_is_symlink () {
+ test "$#" -ne 1 && BUG "1 param"
+ if ! test -h "$1"
+ then
+ echo "Symbolic link $1 doesn't exist"
+ false
+ fi
+}
+
+test_path_is_executable () {
+ test "$#" -ne 1 && BUG "1 param"
+ if ! test -x "$1"
+ then
+ echo "$1 is not executable"
+ false
+ fi
+}
+
# Check if the directory exists and is empty as expected, barf otherwise.
test_dir_is_empty () {
test "$#" -ne 1 && BUG "1 param"
test_path_is_dir "$1" &&
- if test -n "$(ls -a1 "$1" | egrep -v '^\.\.?$')"
+ if test -n "$(ls -a1 "$1" | grep -E -v '^\.\.?$')"
then
echo "Directory '$1' is not empty, it contains:"
ls -la "$1"
@@ -891,10 +1002,6 @@ test_path_is_missing () {
then
echo "Path exists:"
ls -ld "$1"
- if test $# -ge 1
- then
- echo "$*"
- fi
false
fi
}
@@ -990,7 +1097,7 @@ test_must_fail_acceptable () {
fi
case "$1" in
- git|__git*|test-tool|test_terminal)
+ git|__git*|scalar|test-tool|test_terminal)
return 0
;;
*)
@@ -1156,19 +1263,20 @@ test_cmp_bin () {
cmp "$@"
}
-# Wrapper for grep which used to be used for
-# GIT_TEST_GETTEXT_POISON=false. Only here as a shim for other
-# in-flight changes. Should not be used and will be removed soon.
test_i18ngrep () {
+ BUG "do not use test_i18ngrep---use test_grep instead"
+}
+
+test_grep () {
eval "last_arg=\${$#}"
test -f "$last_arg" ||
- BUG "test_i18ngrep requires a file to read as the last parameter"
+ BUG "test_grep requires a file to read as the last parameter"
if test $# -lt 2 ||
{ test "x!" = "x$1" && test $# -lt 3 ; }
then
- BUG "too few parameters to test_i18ngrep"
+ BUG "too few parameters to test_grep"
fi
if test "x!" = "x$1"
@@ -1193,15 +1301,6 @@ test_i18ngrep () {
return 1
}
-# Call any command "$@" but be more verbose about its
-# failure. This is handy for commands like "test" which do
-# not output anything when they fail.
-verbose () {
- "$@" && return 0
- echo >&4 "command failed: $(git rev-parse --sq-quote "$@")"
- return 1
-}
-
# Check if the file expected to be empty is indeed empty, and barfs
# otherwise.
@@ -1248,6 +1347,39 @@ test_cmp_rev () {
fi
}
+# Tests that a commit message matches the expected text
+#
+# Usage: test_commit_message <rev> [-m <msg> | <file>]
+#
+# When using "-m" <msg> will have a line feed appended. If the second
+# argument is omitted then the expected message is read from stdin.
+
+test_commit_message () {
+ local msg_file=expect.msg
+
+ case $# in
+ 3)
+ if test "$2" = "-m"
+ then
+ printf "%s\n" "$3" >"$msg_file"
+ else
+ BUG "Usage: test_commit_message <rev> [-m <message> | <file>]"
+ fi
+ ;;
+ 2)
+ msg_file="$2"
+ ;;
+ 1)
+ cat >"$msg_file"
+ ;;
+ *)
+ BUG "Usage: test_commit_message <rev> [-m <message> | <file>]"
+ ;;
+ esac
+ git show --no-patch --pretty=format:%B "$1" -- >actual.msg &&
+ test_cmp "$msg_file" actual.msg
+}
+
# Compare paths respecting core.ignoreCase
test_cmp_fspath () {
if test "x$1" = "x$2"
@@ -1396,7 +1528,7 @@ test_bool_env () {
BUG "test_bool_env requires two parameters (variable name and default value)"
fi
- git env--helper --type=bool --default="$2" --exit-code "$1"
+ test-tool env-helper --type=bool --default="$2" --exit-code "$1"
ret=$?
case $ret in
0|1) # unset or valid bool value
@@ -1424,72 +1556,6 @@ test_skip_or_die () {
error "$2"
}
-# The following mingw_* functions obey POSIX shell syntax, but are actually
-# bash scripts, and are meant to be used only with bash on Windows.
-
-# A test_cmp function that treats LF and CRLF equal and avoids to fork
-# diff when possible.
-mingw_test_cmp () {
- # Read text into shell variables and compare them. If the results
- # are different, use regular diff to report the difference.
- local test_cmp_a= test_cmp_b=
-
- # When text came from stdin (one argument is '-') we must feed it
- # to diff.
- local stdin_for_diff=
-
- # Since it is difficult to detect the difference between an
- # empty input file and a failure to read the files, we go straight
- # to diff if one of the inputs is empty.
- if test -s "$1" && test -s "$2"
- then
- # regular case: both files non-empty
- mingw_read_file_strip_cr_ test_cmp_a <"$1"
- mingw_read_file_strip_cr_ test_cmp_b <"$2"
- elif test -s "$1" && test "$2" = -
- then
- # read 2nd file from stdin
- mingw_read_file_strip_cr_ test_cmp_a <"$1"
- mingw_read_file_strip_cr_ test_cmp_b
- stdin_for_diff='<<<"$test_cmp_b"'
- elif test "$1" = - && test -s "$2"
- then
- # read 1st file from stdin
- mingw_read_file_strip_cr_ test_cmp_a
- mingw_read_file_strip_cr_ test_cmp_b <"$2"
- stdin_for_diff='<<<"$test_cmp_a"'
- fi
- test -n "$test_cmp_a" &&
- test -n "$test_cmp_b" &&
- test "$test_cmp_a" = "$test_cmp_b" ||
- eval "diff -u \"\$@\" $stdin_for_diff"
-}
-
-# $1 is the name of the shell variable to fill in
-mingw_read_file_strip_cr_ () {
- # Read line-wise using LF as the line separator
- # and use IFS to strip CR.
- local line
- while :
- do
- if IFS=$'\r' read -r -d $'\n' line
- then
- # good
- line=$line$'\n'
- else
- # we get here at EOF, but also if the last line
- # was not terminated by LF; in the latter case,
- # some text was read
- if test -z "$line"
- then
- # EOF, really
- break
- fi
- fi
- eval "$1=\$$1\$line"
- done
-}
-
# Like "env FOO=BAR some-program", but run inside a subshell, which means
# it also works for shell functions (though those functions cannot impact
# the environment outside of the test_env invocation).
@@ -1589,7 +1655,21 @@ test_set_hash () {
# Detect the hash algorithm in use.
test_detect_hash () {
- test_hash_algo="${GIT_TEST_DEFAULT_HASH:-sha1}"
+ case "$GIT_TEST_DEFAULT_HASH" in
+ "sha256")
+ test_hash_algo=sha256
+ test_compat_hash_algo=sha1
+ ;;
+ *)
+ test_hash_algo=sha1
+ test_compat_hash_algo=sha256
+ ;;
+ esac
+}
+
+# Detect the hash algorithm in use.
+test_detect_ref_format () {
+ echo "${GIT_TEST_DEFAULT_REF_FORMAT:-files}"
}
# Load common hash metadata and common placeholder object IDs for use with
@@ -1641,6 +1721,12 @@ test_oid () {
local algo="${test_hash_algo}" &&
case "$1" in
+ --hash=storage)
+ algo="$test_hash_algo" &&
+ shift;;
+ --hash=compat)
+ algo="$test_compat_hash_algo" &&
+ shift;;
--hash=*)
algo="${1#--hash=}" &&
shift;;
@@ -1656,20 +1742,30 @@ test_oid () {
then
BUG "undefined key '$1'"
fi &&
- eval "printf '%s' \"\${$var}\""
+ eval "printf '%s\n' \"\${$var}\""
}
# Insert a slash into an object ID so it can be used to reference a location
# under ".git/objects". For example, "deadbeef..." becomes "de/adbeef..".
test_oid_to_path () {
- local basename=${1#??}
+ local basename="${1#??}"
echo "${1%$basename}/$basename"
}
+# Parse oids from git ls-files --staged output
+test_parse_ls_files_stage_oids () {
+ awk '{print $2}' -
+}
+
+# Parse oids from git ls-tree output
+test_parse_ls_tree_oids () {
+ awk '{print $3}' -
+}
+
# Choose a port number based on the test script's number and store it in
# the given variable name, unless that variable already contains a number.
test_set_port () {
- local var=$1 port
+ local var="$1" port
if test $# -ne 1 || test -z "$var"
then
@@ -1715,6 +1811,13 @@ test_path_is_hidden () {
return 1
}
+# Poor man's URI escaping. Good enough for the test suite whose trash
+# directory has a space in it. See 93c3fcbe4d4 (git-svn: attempt to
+# mimic SVN 1.7 URL canonicalization, 2012-07-28) for prior art.
+test_uri_escape() {
+ sed 's/ /%20/g'
+}
+
# Check that the given command was invoked as part of the
# trace2-format trace on stdin.
#
@@ -1737,7 +1840,7 @@ test_subcommand () {
shift
fi
- local expr=$(printf '"%s",' "$@")
+ local expr="$(printf '"%s",' "$@")"
expr="${expr%,}"
if test -n "$negate"
@@ -1790,8 +1893,82 @@ test_region () {
return 0
}
+# Check that the given data fragment was included as part of the
+# trace2-format trace on stdin.
+#
+# test_trace2_data <category> <key> <value>
+#
+# For example, to look for trace2_data_intmax("pack-objects", repo,
+# "reused", N) in an invocation of "git pack-objects", run:
+#
+# GIT_TRACE2_EVENT="$(pwd)/trace.txt" git pack-objects ... &&
+# test_trace2_data pack-objects reused N <trace2.txt
+test_trace2_data () {
+ grep -e '"category":"'"$1"'","key":"'"$2"'","value":"'"$3"'"'
+}
+
+# Given a GIT_TRACE2_EVENT log over stdin, writes to stdout a list of URLs
+# sent to git-remote-https child processes.
+test_remote_https_urls() {
+ grep -e '"event":"child_start".*"argv":\["git-remote-https",".*"\]' |
+ sed -e 's/{"event":"child_start".*"argv":\["git-remote-https","//g' \
+ -e 's/"\]}//g'
+}
+
# Print the destination of symlink(s) provided as arguments. Basically
# the same as the readlink command, but it's not available everywhere.
test_readlink () {
perl -le 'print readlink($_) for @ARGV' "$@"
}
+
+# Set mtime to a fixed "magic" timestamp in mid February 2009, before we
+# run an operation that may or may not touch the file. If the file was
+# touched, its timestamp will not accidentally have such an old timestamp,
+# as long as your filesystem clock is reasonably correct. To verify the
+# timestamp, follow up with test_is_magic_mtime.
+#
+# An optional increment to the magic timestamp may be specified as second
+# argument.
+test_set_magic_mtime () {
+ local inc="${2:-0}" &&
+ local mtime=$((1234567890 + $inc)) &&
+ test-tool chmtime =$mtime "$1" &&
+ test_is_magic_mtime "$1" $inc
+}
+
+# Test whether the given file has the "magic" mtime set. This is meant to
+# be used in combination with test_set_magic_mtime.
+#
+# An optional increment to the magic timestamp may be specified as second
+# argument. Usually, this should be the same increment which was used for
+# the associated test_set_magic_mtime.
+test_is_magic_mtime () {
+ local inc="${2:-0}" &&
+ local mtime=$((1234567890 + $inc)) &&
+ echo $mtime >.git/test-mtime-expect &&
+ test-tool chmtime --get "$1" >.git/test-mtime-actual &&
+ test_cmp .git/test-mtime-expect .git/test-mtime-actual
+ local ret=$?
+ rm -f .git/test-mtime-expect
+ rm -f .git/test-mtime-actual
+ return $ret
+}
+
+# Given two filenames, parse both using 'git config --list --file'
+# and compare the sorted output of those commands. Useful when
+# wanting to ignore whitespace differences and sorting concerns.
+test_cmp_config_output () {
+ git config --list --file="$1" >config-expect &&
+ git config --list --file="$2" >config-actual &&
+ sort config-expect >sorted-expect &&
+ sort config-actual >sorted-actual &&
+ test_cmp sorted-expect sorted-actual
+}
+
+# Given a filename, extract its trailing hash as a hex string
+test_trailing_hash () {
+ local file="$1" &&
+ tail -c $(test_oid rawsz) "$file" |
+ test-tool hexdump |
+ sed "s/ //g"
+}
diff --git a/t/test-lib-github-workflow-markup.sh b/t/test-lib-github-workflow-markup.sh
new file mode 100644
index 0000000..33405c9
--- /dev/null
+++ b/t/test-lib-github-workflow-markup.sh
@@ -0,0 +1,56 @@
+# Library of functions to mark up test scripts' output suitable for
+# pretty-printing it in GitHub workflows.
+#
+# Copyright (c) 2022 Johannes Schindelin
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see https://www.gnu.org/licenses/ .
+#
+# The idea is for `test-lib.sh` to source this file when run in GitHub
+# workflows; these functions will then override (empty) functions
+# that are are called at the appropriate times during the test runs.
+
+test_skip_test_preamble=t
+
+start_test_output () {
+ test -n "$GIT_TEST_TEE_OUTPUT_FILE" ||
+ die "--github-workflow-markup requires --verbose-log"
+ github_markup_output="${GIT_TEST_TEE_OUTPUT_FILE%.out}.markup"
+ >$github_markup_output
+ GIT_TEST_TEE_OFFSET=0
+}
+
+# No need to override start_test_case_output
+
+finalize_test_case_output () {
+ test_case_result=$1
+ shift
+ case "$test_case_result" in
+ failure)
+ echo >>$github_markup_output "::error::failed: $this_test.$test_count $1"
+ ;;
+ fixed)
+ echo >>$github_markup_output "::notice::fixed: $this_test.$test_count $1"
+ ;;
+ ok|broken)
+ # Exit without printing the "ok" or ""broken" tests
+ return
+ ;;
+ esac
+ echo >>$github_markup_output "::group::$test_case_result: $this_test.$test_count $*"
+ test-tool >>$github_markup_output path-utils skip-n-bytes \
+ "$GIT_TEST_TEE_OUTPUT_FILE" $GIT_TEST_TEE_OFFSET
+ echo >>$github_markup_output "::endgroup::"
+}
+
+# No need to override finalize_test_output
diff --git a/t/test-lib-junit.sh b/t/test-lib-junit.sh
new file mode 100644
index 0000000..76cbbd3
--- /dev/null
+++ b/t/test-lib-junit.sh
@@ -0,0 +1,132 @@
+# Library of functions to format test scripts' output in JUnit XML
+# format, to support Git's test suite result to be presented in an
+# easily digestible way on Azure Pipelines.
+#
+# Copyright (c) 2022 Johannes Schindelin
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see https://www.gnu.org/licenses/ .
+#
+# The idea is for `test-lib.sh` to source this file when the user asks
+# for JUnit XML; these functions will then override (empty) functions
+# that are are called at the appropriate times during the test runs.
+
+start_test_output () {
+ junit_xml_dir="$TEST_OUTPUT_DIRECTORY/out"
+ mkdir -p "$junit_xml_dir"
+ junit_xml_base=${1##*/}
+ junit_xml_path="$junit_xml_dir/TEST-${junit_xml_base%.sh}.xml"
+ junit_attrs="name=\"${junit_xml_base%.sh}\""
+ junit_attrs="$junit_attrs timestamp=\"$(TZ=UTC \
+ date +%Y-%m-%dT%H:%M:%S)\""
+ write_junit_xml --truncate "<testsuites>" " <testsuite $junit_attrs>"
+ junit_suite_start=$(test-tool date getnanos)
+ if test -n "$GIT_TEST_TEE_OUTPUT_FILE"
+ then
+ GIT_TEST_TEE_OFFSET=0
+ fi
+}
+
+start_test_case_output () {
+ junit_start=$(test-tool date getnanos)
+}
+
+finalize_test_case_output () {
+ test_case_result=$1
+ shift
+ case "$test_case_result" in
+ ok)
+ set -- "$*"
+ ;;
+ failure)
+ junit_insert="<failure message=\"not ok $test_count -"
+ junit_insert="$junit_insert $(xml_attr_encode --no-lf "$1")\">"
+ junit_insert="$junit_insert $(xml_attr_encode \
+ "$(if test -n "$GIT_TEST_TEE_OUTPUT_FILE"
+ then
+ test-tool path-utils skip-n-bytes \
+ "$GIT_TEST_TEE_OUTPUT_FILE" $GIT_TEST_TEE_OFFSET
+ else
+ printf '%s\n' "$@" | sed 1d
+ fi)")"
+ junit_insert="$junit_insert</failure>"
+ if test -n "$GIT_TEST_TEE_OUTPUT_FILE"
+ then
+ junit_insert="$junit_insert<system-err>$(xml_attr_encode \
+ "$(cat "$GIT_TEST_TEE_OUTPUT_FILE")")</system-err>"
+ fi
+ set -- "$1" " $junit_insert"
+ ;;
+ fixed)
+ set -- "$* (breakage fixed)"
+ ;;
+ broken)
+ set -- "$* (known breakage)"
+ ;;
+ skip)
+ message="$(xml_attr_encode --no-lf "$skipped_reason")"
+ set -- "$1" " <skipped message=\"$message\" />"
+ ;;
+ esac
+
+ junit_attrs="name=\"$(xml_attr_encode --no-lf "$this_test.$test_count $1")\""
+ shift
+ junit_attrs="$junit_attrs classname=\"$this_test\""
+ junit_attrs="$junit_attrs time=\"$(test-tool \
+ date getnanos $junit_start)\""
+ write_junit_xml "$(printf '%s\n' \
+ " <testcase $junit_attrs>" "$@" " </testcase>")"
+ junit_have_testcase=t
+}
+
+finalize_test_output () {
+ if test -n "$junit_xml_path"
+ then
+ test -n "$junit_have_testcase" || {
+ junit_start=$(test-tool date getnanos)
+ write_junit_xml_testcase "all tests skipped"
+ }
+
+ # adjust the overall time
+ junit_time=$(test-tool date getnanos $junit_suite_start)
+ sed -e "s/\(<testsuite.*\) time=\"[^\"]*\"/\1/" \
+ -e "s/<testsuite [^>]*/& time=\"$junit_time\"/" \
+ -e '/^ *<\/testsuite/d' \
+ <"$junit_xml_path" >"$junit_xml_path.new"
+ mv "$junit_xml_path.new" "$junit_xml_path"
+
+ write_junit_xml " </testsuite>" "</testsuites>"
+ write_junit_xml=
+ fi
+}
+
+write_junit_xml () {
+ case "$1" in
+ --truncate)
+ >"$junit_xml_path"
+ junit_have_testcase=
+ shift
+ ;;
+ esac
+ printf '%s\n' "$@" >>"$junit_xml_path"
+}
+
+xml_attr_encode () {
+ if test "x$1" = "x--no-lf"
+ then
+ shift
+ printf '%s' "$*" | test-tool xml-encode
+ else
+ printf '%s\n' "$@" | test-tool xml-encode
+ fi
+}
diff --git a/t/test-lib.sh b/t/test-lib.sh
index d5ee964..79d3e0e 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -13,19 +13,26 @@
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with this program. If not, see http://www.gnu.org/licenses/ .
+# along with this program. If not, see https://www.gnu.org/licenses/ .
# Test the binaries we have just built. The tests are kept in
# t/ subdirectory and are run in 'trash directory' subdirectory.
if test -z "$TEST_DIRECTORY"
then
- # We allow tests to override this, in case they want to run tests
- # outside of t/, e.g. for running tests on the test library
- # itself.
- TEST_DIRECTORY=$(pwd)
-else
# ensure that TEST_DIRECTORY is an absolute path so that it
# is valid even if the current working directory is changed
+ TEST_DIRECTORY=$(pwd)
+else
+ # The TEST_DIRECTORY will always be the path to the "t"
+ # directory in the git.git checkout. This is overridden by
+ # e.g. t/lib-subtest.sh, but only because its $(pwd) is
+ # different. Those tests still set "$TEST_DIRECTORY" to the
+ # same path.
+ #
+ # See use of "$GIT_BUILD_DIR" and "$TEST_DIRECTORY" below for
+ # hard assumptions about "$GIT_BUILD_DIR/t" existing and being
+ # the "$TEST_DIRECTORY", and e.g. "$TEST_DIRECTORY/helper"
+ # needing to exist.
TEST_DIRECTORY=$(cd "$TEST_DIRECTORY" && pwd) || exit 1
fi
if test -z "$TEST_OUTPUT_DIRECTORY"
@@ -34,21 +41,57 @@ then
# elsewhere
TEST_OUTPUT_DIRECTORY=$TEST_DIRECTORY
fi
-GIT_BUILD_DIR="$TEST_DIRECTORY"/..
+GIT_BUILD_DIR="${TEST_DIRECTORY%/t}"
+if test "$TEST_DIRECTORY" = "$GIT_BUILD_DIR"
+then
+ echo "PANIC: Running in a $TEST_DIRECTORY that doesn't end in '/t'?" >&2
+ exit 1
+fi
+if test -f "$GIT_BUILD_DIR/GIT-BUILD-DIR"
+then
+ GIT_BUILD_DIR="$(cat "$GIT_BUILD_DIR/GIT-BUILD-DIR")" || exit 1
+ # On Windows, we must convert Windows paths lest they contain a colon
+ case "$(uname -s)" in
+ *MINGW*)
+ GIT_BUILD_DIR="$(cygpath -au "$GIT_BUILD_DIR")"
+ ;;
+ esac
+fi
+
+# Prepend a string to a VAR using an arbitrary ":" delimiter, not
+# adding the delimiter if VAR or VALUE is empty. I.e. a generalized:
+#
+# VAR=$1${VAR:+${1:+$2}$VAR}
+#
+# Usage (using ":" as the $2 delimiter):
+#
+# prepend_var VAR : VALUE
+prepend_var () {
+ eval "$1=\"$3\${$1:+${3:+$2}\$$1}\""
+}
+
+# If [AL]SAN is in effect we want to abort so that we notice
+# problems. The GIT_SAN_OPTIONS variable can be used to set common
+# defaults shared between [AL]SAN_OPTIONS.
+prepend_var GIT_SAN_OPTIONS : abort_on_error=1
+prepend_var GIT_SAN_OPTIONS : strip_path_prefix="$GIT_BUILD_DIR/"
# If we were built with ASAN, it may complain about leaks
# of program-lifetime variables. Disable it by default to lower
# the noise level. This needs to happen at the start of the script,
# before we even do our "did we build git yet" check (since we don't
# want that one to complain to stderr).
-: ${ASAN_OPTIONS=detect_leaks=0:abort_on_error=1}
+prepend_var ASAN_OPTIONS : $GIT_SAN_OPTIONS
+prepend_var ASAN_OPTIONS : detect_leaks=0
export ASAN_OPTIONS
-# If LSAN is in effect we _do_ want leak checking, but we still
-# want to abort so that we notice the problems.
-: ${LSAN_OPTIONS=abort_on_error=1}
+prepend_var LSAN_OPTIONS : $GIT_SAN_OPTIONS
+prepend_var LSAN_OPTIONS : fast_unwind_on_malloc=0
export LSAN_OPTIONS
+prepend_var UBSAN_OPTIONS : $GIT_SAN_OPTIONS
+export UBSAN_OPTIONS
+
if test ! -f "$GIT_BUILD_DIR"/GIT-BUILD-OPTIONS
then
echo >&2 'error: GIT-BUILD-OPTIONS missing (has Git been built?).'
@@ -107,6 +150,12 @@ mark_option_requires_arg () {
store_arg_to=$2
}
+# These functions can be overridden e.g. to output JUnit XML
+start_test_output () { :; }
+start_test_case_output () { :; }
+finalize_test_case_output () { :; }
+finalize_test_output () { :; }
+
parse_option () {
local opt="$1"
@@ -166,7 +215,10 @@ parse_option () {
tee=t
;;
--write-junit-xml)
- write_junit_xml=t
+ . "$TEST_DIRECTORY/test-lib-junit.sh"
+ ;;
+ --github-workflow-markup)
+ . "$TEST_DIRECTORY/test-lib-github-workflow-markup.sh"
;;
--stress)
stress=t ;;
@@ -199,6 +251,9 @@ parse_option () {
;;
esac
;;
+ --invert-exit-code)
+ invert_exit_code=t
+ ;;
*)
echo "error: unknown test option '$opt'" >&2; exit 1 ;;
esac
@@ -263,6 +318,11 @@ TEST_NUMBER="${TEST_NAME%%-*}"
TEST_NUMBER="${TEST_NUMBER#t}"
TEST_RESULTS_DIR="$TEST_OUTPUT_DIRECTORY/test-results"
TEST_RESULTS_BASE="$TEST_RESULTS_DIR/$TEST_NAME$TEST_STRESS_JOB_SFX"
+TEST_RESULTS_SAN_FILE_PFX=trace
+TEST_RESULTS_SAN_DIR_SFX=leak
+TEST_RESULTS_SAN_FILE=
+TEST_RESULTS_SAN_DIR="$TEST_RESULTS_DIR/$TEST_NAME.$TEST_RESULTS_SAN_DIR_SFX"
+TEST_RESULTS_SAN_DIR_NR_LEAKS_STARTUP=
TRASH_DIRECTORY="trash directory.$TEST_NAME$TEST_STRESS_JOB_SFX"
test -n "$root" && TRASH_DIRECTORY="$root/$TRASH_DIRECTORY"
case "$TRASH_DIRECTORY" in
@@ -270,6 +330,17 @@ case "$TRASH_DIRECTORY" in
*) TRASH_DIRECTORY="$TEST_OUTPUT_DIRECTORY/$TRASH_DIRECTORY" ;;
esac
+# Utility functions using $TEST_RESULTS_* variables
+nr_san_dir_leaks_ () {
+ # stderr piped to /dev/null because the directory may have
+ # been "rmdir"'d already.
+ find "$TEST_RESULTS_SAN_DIR" \
+ -type f \
+ -name "$TEST_RESULTS_SAN_FILE_PFX.*" 2>/dev/null |
+ xargs grep -lv "Unable to get registers from thread" |
+ wc -l
+}
+
# If --stress was passed, run this test repeatedly in several parallel loops.
if test "$GIT_TEST_STRESS_STARTED" = "done"
then
@@ -449,6 +520,8 @@ unset VISUAL EMAIL LANGUAGE $("$PERL_PATH" -e '
unset XDG_CACHE_HOME
unset XDG_CONFIG_HOME
unset GITPERLLIB
+unset GIT_TRACE2_PARENT_NAME
+unset GIT_TRACE2_PARENT_SID
TEST_AUTHOR_LOCALNAME=author
TEST_AUTHOR_DOMAIN=example.com
GIT_AUTHOR_EMAIL=${TEST_AUTHOR_LOCALNAME}@${TEST_AUTHOR_DOMAIN}
@@ -469,6 +542,8 @@ export EDITOR
GIT_DEFAULT_HASH="${GIT_TEST_DEFAULT_HASH:-sha1}"
export GIT_DEFAULT_HASH
+GIT_DEFAULT_REF_FORMAT="${GIT_TEST_DEFAULT_REF_FORMAT:-files}"
+export GIT_DEFAULT_REF_FORMAT
GIT_TEST_MERGE_ALGORITHM="${GIT_TEST_MERGE_ALGORITHM:-ort}"
export GIT_TEST_MERGE_ALGORITHM
@@ -476,6 +551,13 @@ export GIT_TEST_MERGE_ALGORITHM
GIT_TRACE_BARE=1
export GIT_TRACE_BARE
+# Some tests scan the GIT_TRACE2_EVENT feed for events, but the
+# default depth is 2, which frequently causes issues when the
+# events are wrapped in new regions. Set it to a sufficiently
+# large depth to avoid custom changes in the test suite.
+GIT_TRACE2_EVENT_NESTING=100
+export GIT_TRACE2_EVENT_NESTING
+
# Use specific version of the index file format
if test -n "${GIT_TEST_INDEX_VERSION:+isset}"
then
@@ -489,9 +571,19 @@ then
export GIT_PERL_FATAL_WARNINGS
fi
-# Add libc MALLOC and MALLOC_PERTURB test
-# only if we are not executing the test with valgrind
+case $GIT_TEST_FSYNC in
+'')
+ GIT_TEST_FSYNC=0
+ export GIT_TEST_FSYNC
+ ;;
+esac
+
+# Add libc MALLOC and MALLOC_PERTURB test only if we are not executing
+# the test with valgrind and have not compiled with conflict SANITIZE
+# options.
if test -n "$valgrind" ||
+ test -n "$SANITIZE_ADDRESS" ||
+ test -n "$SANITIZE_LEAK" ||
test -n "$TEST_NO_MALLOC_CHECK"
then
setup_malloc_check () {
@@ -501,12 +593,35 @@ then
: nothing
}
else
+ _USE_GLIBC_TUNABLES=
+ if _GLIBC_VERSION=$(getconf GNU_LIBC_VERSION 2>/dev/null) &&
+ _GLIBC_VERSION=${_GLIBC_VERSION#"glibc "} &&
+ expr 2.34 \<= "$_GLIBC_VERSION" >/dev/null
+ then
+ _USE_GLIBC_TUNABLES=YesPlease
+ fi
setup_malloc_check () {
+ local g
+ local t
MALLOC_CHECK_=3 MALLOC_PERTURB_=165
export MALLOC_CHECK_ MALLOC_PERTURB_
+ if test -n "$_USE_GLIBC_TUNABLES"
+ then
+ g=
+ LD_PRELOAD="libc_malloc_debug.so.0"
+ for t in \
+ glibc.malloc.check=1 \
+ glibc.malloc.perturb=165
+ do
+ g="${g#:}:$t"
+ done
+ GLIBC_TUNABLES=$g
+ export LD_PRELOAD GLIBC_TUNABLES
+ fi
}
teardown_malloc_check () {
unset MALLOC_CHECK_ MALLOC_PERTURB_
+ unset LD_PRELOAD GLIBC_TUNABLES
}
fi
@@ -534,14 +649,8 @@ SQ=\'
# when case-folding filenames
u200c=$(printf '\342\200\214')
-export _x05 _x35 _x40 _z40 LF u200c EMPTY_TREE EMPTY_BLOB ZERO_OID OID_REGEX
+export _x05 _x35 LF u200c EMPTY_TREE EMPTY_BLOB ZERO_OID OID_REGEX
-# Each test should start with something like this, after copyright notices:
-#
-# test_description='Description of this test...
-# This test checks if command xyzzy does the right thing...
-# '
-# . ./test-lib.sh
test "x$TERM" != "xdumb" && (
test -t 1 &&
tput bold >/dev/null 2>&1 &&
@@ -589,17 +698,42 @@ USER_TERM="$TERM"
TERM=dumb
export TERM USER_TERM
-error () {
- say_color error "error: $*"
- finalize_junit_xml
+# What is written by tests to stdout and stderr is sent to different places
+# depending on the test mode (e.g. /dev/null in non-verbose mode, piped to tee
+# with --tee option, etc.). We save the original stdin to FD #6 and stdout and
+# stderr to #5 and #7, so that the test framework can use them (e.g. for
+# printing errors within the test framework) independently of the test mode.
+exec 5>&1
+exec 6<&0
+exec 7>&2
+
+_error_exit () {
+ finalize_test_output
GIT_EXIT_OK=t
exit 1
}
+error () {
+ say_color error "error: $*"
+ _error_exit
+}
+
BUG () {
error >&7 "bug in the test script: $*"
}
+BAIL_OUT () {
+ test $# -ne 1 && BUG "1 param"
+
+ # Do not change "Bail out! " string. It's part of TAP syntax:
+ # https://testanything.org/tap-specification.html
+ local bail_out="Bail out! "
+ local message="$1"
+
+ say_color >&5 error $bail_out "$message"
+ _error_exit
+}
+
say () {
say_color info "$*"
}
@@ -608,9 +742,7 @@ if test -n "$HARNESS_ACTIVE"
then
if test "$verbose" = t || test -n "$verbose_only"
then
- printf 'Bail out! %s\n' \
- 'verbose mode forbidden under TAP harness; try --verbose-log'
- exit 1
+ BAIL_OUT 'verbose mode forbidden under TAP harness; try --verbose-log'
fi
fi
@@ -623,9 +755,6 @@ then
exit 0
fi
-exec 5>&1
-exec 6<&0
-exec 7>&2
if test "$verbose_log" = "t"
then
exec 3>>"$GIT_TEST_TEE_OUTPUT_FILE" 4>&3
@@ -655,6 +784,8 @@ test_fixed=0
test_broken=0
test_success=0
+test_missing_prereq=
+
test_external_has_tap=0
die () {
@@ -687,58 +818,51 @@ trap '{ code=$?; set +x; } 2>/dev/null; exit $code' INT TERM HUP
# the test_expect_* functions instead.
test_ok_ () {
- if test -n "$write_junit_xml"
- then
- write_junit_xml_testcase "$*"
- fi
test_success=$(($test_success + 1))
say_color "" "ok $test_count - $@"
+ finalize_test_case_output ok "$@"
+}
+
+_invert_exit_code_failure_end_blurb () {
+ say_color warn "# faked up failures as TODO & now exiting with 0 due to --invert-exit-code"
}
test_failure_ () {
- if test -n "$write_junit_xml"
+ failure_label=$1
+ test_failure=$(($test_failure + 1))
+ local pfx=""
+ if test -n "$invert_exit_code" # && test -n "$HARNESS_ACTIVE"
then
- junit_insert="<failure message=\"not ok $test_count -"
- junit_insert="$junit_insert $(xml_attr_encode "$1")\">"
- junit_insert="$junit_insert $(xml_attr_encode \
- "$(if test -n "$GIT_TEST_TEE_OUTPUT_FILE"
- then
- test-tool path-utils skip-n-bytes \
- "$GIT_TEST_TEE_OUTPUT_FILE" $GIT_TEST_TEE_OFFSET
- else
- printf '%s\n' "$@" | sed 1d
- fi)")"
- junit_insert="$junit_insert</failure>"
- if test -n "$GIT_TEST_TEE_OUTPUT_FILE"
- then
- junit_insert="$junit_insert<system-err>$(xml_attr_encode \
- "$(cat "$GIT_TEST_TEE_OUTPUT_FILE")")</system-err>"
- fi
- write_junit_xml_testcase "$1" " $junit_insert"
+ pfx="# TODO induced breakage (--invert-exit-code):"
fi
- test_failure=$(($test_failure + 1))
- say_color error "not ok $test_count - $1"
+ say_color error "not ok $test_count - ${pfx:+$pfx }$1"
shift
printf '%s\n' "$*" | sed -e 's/^/# /'
- test "$immediate" = "" || { finalize_junit_xml; GIT_EXIT_OK=t; exit 1; }
+ if test -n "$immediate"
+ then
+ say_color error "1..$test_count"
+ if test -n "$invert_exit_code"
+ then
+ finalize_test_output
+ _invert_exit_code_failure_end_blurb
+ GIT_EXIT_OK=t
+ exit 0
+ fi
+ _error_exit
+ fi
+ finalize_test_case_output failure "$failure_label" "$@"
}
test_known_broken_ok_ () {
- if test -n "$write_junit_xml"
- then
- write_junit_xml_testcase "$* (breakage fixed)"
- fi
test_fixed=$(($test_fixed+1))
- say_color error "ok $test_count - $@ # TODO known breakage vanished"
+ say_color error "ok $test_count - $1 # TODO known breakage vanished"
+ finalize_test_case_output fixed "$1"
}
test_known_broken_failure_ () {
- if test -n "$write_junit_xml"
- then
- write_junit_xml_testcase "$* (known breakage)"
- fi
test_broken=$(($test_broken+1))
- say_color warn "not ok $test_count - $@ # TODO known breakage"
+ say_color warn "not ok $test_count - $1 # TODO known breakage"
+ finalize_test_case_output broken "$1"
}
test_debug () {
@@ -923,10 +1047,7 @@ want_trace () {
# (and we want to make sure we run any cleanup like
# "set +x").
test_eval_inner_ () {
- # Do not add anything extra (including LF) after '$*'
- eval "
- want_trace && trace_level_=$(($trace_level_+1)) && set -x
- $*"
+ eval "$*"
}
test_eval_ () {
@@ -951,7 +1072,10 @@ test_eval_ () {
# be _inside_ the block to avoid polluting the "set -x" output
#
- test_eval_inner_ "$@" </dev/null >&3 2>&4
+ # Do not add anything extra (including LF) after '$*'
+ test_eval_inner_ </dev/null >&3 2>&4 "
+ want_trace && trace_level_=$(($trace_level_+1)) && set -x
+ $*"
{
test_eval_ret_=$?
if want_trace
@@ -968,26 +1092,22 @@ test_eval_ () {
return $test_eval_ret_
}
+fail_117 () {
+ return 117
+}
+
test_run_ () {
test_cleanup=:
expecting_failure=$2
if test "${GIT_TEST_CHAIN_LINT:-1}" != 0; then
- # turn off tracing for this test-eval, as it simply creates
- # confusing noise in the "-x" output
- trace_tmp=$trace
- trace=
# 117 is magic because it is unlikely to match the exit
# code of other programs
- if test "OK-117" != "$(test_eval_ "(exit 117) && $1${LF}${LF}echo OK-\$?" 3>&1)" ||
- {
- test "${GIT_TEST_CHAIN_LINT_HARDER:-${GIT_TEST_CHAIN_LINT_HARDER_DEFAULT:-1}}" != 0 &&
- $(printf '%s\n' "$1" | sed -f "$GIT_BUILD_DIR/t/chainlint.sed" | grep -q '?![A-Z][A-Z]*?!')
- }
+ test_eval_inner_ "fail_117 && $1" </dev/null >&3 2>&4
+ if test $? != 117
then
- BUG "broken &&-chain or run-away HERE-DOC: $1"
+ BUG "broken &&-chain: $1"
fi
- trace=$trace_tmp
fi
setup_malloc_check
@@ -1013,10 +1133,7 @@ test_start_ () {
test_count=$(($test_count+1))
maybe_setup_verbose
maybe_setup_valgrind
- if test -n "$write_junit_xml"
- then
- junit_start=$(test-tool date getnanos)
- fi
+ start_test_case_output "$@"
}
test_finish_ () {
@@ -1055,19 +1172,22 @@ test_skip () {
of_prereq=" of $test_prereq"
fi
skipped_reason="missing $missing_prereq${of_prereq}"
+
+ # Keep a list of all the missing prereq for result aggregation
+ if test -z "$missing_prereq"
+ then
+ test_missing_prereq=$missing_prereq
+ else
+ test_missing_prereq="$test_missing_prereq,$missing_prereq"
+ fi
fi
case "$to_skip" in
t)
- if test -n "$write_junit_xml"
- then
- message="$(xml_attr_encode "$skipped_reason")"
- write_junit_xml_testcase "$1" \
- " <skipped message=\"$message\" />"
- fi
say_color skip "ok $test_count # skip $1 ($skipped_reason)"
: true
+ finalize_test_case_output skip "$@"
;;
*)
false
@@ -1080,53 +1200,6 @@ test_at_end_hook_ () {
:
}
-write_junit_xml () {
- case "$1" in
- --truncate)
- >"$junit_xml_path"
- junit_have_testcase=
- shift
- ;;
- esac
- printf '%s\n' "$@" >>"$junit_xml_path"
-}
-
-xml_attr_encode () {
- printf '%s\n' "$@" | test-tool xml-encode
-}
-
-write_junit_xml_testcase () {
- junit_attrs="name=\"$(xml_attr_encode "$this_test.$test_count $1")\""
- shift
- junit_attrs="$junit_attrs classname=\"$this_test\""
- junit_attrs="$junit_attrs time=\"$(test-tool \
- date getnanos $junit_start)\""
- write_junit_xml "$(printf '%s\n' \
- " <testcase $junit_attrs>" "$@" " </testcase>")"
- junit_have_testcase=t
-}
-
-finalize_junit_xml () {
- if test -n "$write_junit_xml" && test -n "$junit_xml_path"
- then
- test -n "$junit_have_testcase" || {
- junit_start=$(test-tool date getnanos)
- write_junit_xml_testcase "all tests skipped"
- }
-
- # adjust the overall time
- junit_time=$(test-tool date getnanos $junit_suite_start)
- sed -e "s/\(<testsuite.*\) time=\"[^\"]*\"/\1/" \
- -e "s/<testsuite [^>]*/& time=\"$junit_time\"/" \
- -e '/^ *<\/testsuite/d' \
- <"$junit_xml_path" >"$junit_xml_path.new"
- mv "$junit_xml_path.new" "$junit_xml_path"
-
- write_junit_xml " </testsuite>" "</testsuites>"
- write_junit_xml=
- fi
-}
-
test_atexit_cleanup=:
test_atexit_handler () {
# In a succeeding test script 'test_atexit_handler' is invoked
@@ -1142,14 +1215,72 @@ test_atexit_handler () {
teardown_malloc_check
}
-test_done () {
- GIT_EXIT_OK=t
+sanitize_leak_log_message_ () {
+ local new="$1" &&
+ local old="$2" &&
+ local file="$3" &&
+
+ printf "With SANITIZE=leak at exit we have %d leak logs, but started with %d
+
+This means that we have a blindspot where git is leaking but we're
+losing the exit code somewhere, or not propagating it appropriately
+upwards!
+See the logs at \"%s.*\";
+those logs are reproduced below." \
+ "$new" "$old" "$file"
+}
+
+check_test_results_san_file_ () {
+ if test -z "$TEST_RESULTS_SAN_FILE"
+ then
+ return
+ fi &&
+ local old="$TEST_RESULTS_SAN_DIR_NR_LEAKS_STARTUP" &&
+ local new="$(nr_san_dir_leaks_)" &&
+
+ if test $new -le $old
+ then
+ return
+ fi &&
+ local out="$(sanitize_leak_log_message_ "$new" "$old" "$TEST_RESULTS_SAN_FILE")" &&
+ say_color error "$out" &&
+ if test "$old" != 0
+ then
+ echo &&
+ say_color error "The logs include output from past runs to avoid" &&
+ say_color error "that remove 'test-results' between runs."
+ fi &&
+ say_color error "$(cat "$TEST_RESULTS_SAN_FILE".*)" &&
+
+ if test -n "$passes_sanitize_leak" && test "$test_failure" = 0
+ then
+ say "As TEST_PASSES_SANITIZE_LEAK=true and our logs show we're leaking, exit non-zero!" &&
+ invert_exit_code=t
+ elif test -n "$passes_sanitize_leak"
+ then
+ say "As TEST_PASSES_SANITIZE_LEAK=true and our logs show we're leaking, and we're failing for other reasons too..." &&
+ invert_exit_code=
+ elif test -n "$sanitize_leak_check" && test "$test_failure" = 0
+ then
+ say "As TEST_PASSES_SANITIZE_LEAK=true isn't set the above leak is 'ok' with GIT_TEST_PASSING_SANITIZE_LEAK=check" &&
+ invert_exit_code=
+ elif test -n "$sanitize_leak_check"
+ then
+ say "As TEST_PASSES_SANITIZE_LEAK=true isn't set the above leak is 'ok' with GIT_TEST_PASSING_SANITIZE_LEAK=check" &&
+ invert_exit_code=t
+ else
+ say "With GIT_TEST_SANITIZE_LEAK_LOG=true our logs revealed a memory leak, exit non-zero!" &&
+ invert_exit_code=t
+ fi
+}
+
+test_done () {
# Run the atexit commands _before_ the trash directory is
# removed, so the commands can access pidfiles and socket files.
test_atexit_handler
- finalize_junit_xml
+ finalize_test_output
if test -z "$HARNESS_ACTIVE"
then
@@ -1161,10 +1292,16 @@ test_done () {
fixed $test_fixed
broken $test_broken
failed $test_failure
+ missing_prereq $test_missing_prereq
EOF
fi
+ if test -z "$passes_sanitize_leak" && test_bool_env TEST_PASSES_SANITIZE_LEAK false
+ then
+ BAIL_OUT "Please, set TEST_PASSES_SANITIZE_LEAK before sourcing test-lib.sh"
+ fi
+
if test "$test_fixed" != 0
then
say_color error "# $test_fixed known breakage(s) vanished; please update test(s)"
@@ -1183,28 +1320,32 @@ test_done () {
fi
case "$test_failure" in
0)
- if test $test_external_has_tap -eq 0
+ if test $test_remaining -gt 0
then
- if test $test_remaining -gt 0
- then
- say_color pass "# passed all $msg"
- fi
-
- # Maybe print SKIP message
- test -z "$skip_all" || skip_all="# SKIP $skip_all"
- case "$test_count" in
- 0)
- say "1..$test_count${skip_all:+ $skip_all}"
- ;;
- *)
- test -z "$skip_all" ||
- say_color warn "$skip_all"
- say "1..$test_count"
- ;;
- esac
+ say_color pass "# passed all $msg"
fi
- if test -z "$debug" && test -n "$remove_trash"
+ # Maybe print SKIP message
+ test -z "$skip_all" || skip_all="# SKIP $skip_all"
+ case "$test_count" in
+ 0)
+ say "1..$test_count${skip_all:+ $skip_all}"
+ ;;
+ *)
+ test -z "$skip_all" ||
+ say_color warn "$skip_all"
+ say "1..$test_count"
+ ;;
+ esac
+
+ if test -n "$stress" && test -n "$invert_exit_code"
+ then
+ # We're about to move our "$TRASH_DIRECTORY"
+ # to "$TRASH_DIRECTORY.stress-failed" if
+ # --stress is combined with
+ # --invert-exit-code.
+ say "with --stress and --invert-exit-code we're not removing '$TRASH_DIRECTORY'"
+ elif test -z "$debug" && test -n "$remove_trash"
then
test -d "$TRASH_DIRECTORY" ||
error "Tests passed but trash directory already removed before test cleanup; aborting"
@@ -1217,17 +1358,35 @@ test_done () {
} ||
error "Tests passed but test cleanup failed; aborting"
fi
+
+ check_test_results_san_file_ "$test_failure"
+
+ if test -z "$skip_all" && test -n "$invert_exit_code"
+ then
+ say_color warn "# faking up non-zero exit with --invert-exit-code"
+ GIT_EXIT_OK=t
+ exit 1
+ fi
+
test_at_end_hook_
+ GIT_EXIT_OK=t
exit 0 ;;
*)
- if test $test_external_has_tap -eq 0
+ say_color error "# failed $test_failure among $msg"
+ say "1..$test_count"
+
+ check_test_results_san_file_ "$test_failure"
+
+ if test -n "$invert_exit_code"
then
- say_color error "# failed $test_failure among $msg"
- say "1..$test_count"
+ _invert_exit_code_failure_end_blurb
+ GIT_EXIT_OK=t
+ exit 0
fi
+ GIT_EXIT_OK=t
exit 1 ;;
esac
@@ -1360,14 +1519,12 @@ fi
GITPERLLIB="$GIT_BUILD_DIR"/perl/build/lib
export GITPERLLIB
test -d "$GIT_BUILD_DIR"/templates/blt || {
- error "You haven't built things yet, have you?"
+ BAIL_OUT "You haven't built things yet, have you?"
}
if ! test -x "$GIT_BUILD_DIR"/t/helper/test-tool$X
then
- echo >&2 'You need to build test-tool:'
- echo >&2 'Run "make t/helper/test-tool" in the source (toplevel) directory'
- exit 1
+ BAIL_OUT 'You need to build test-tool; Run "make t/helper/test-tool" in the source (toplevel) directory'
fi
# Are we running this test at all?
@@ -1381,23 +1538,108 @@ then
test_done
fi
+BAIL_OUT_ENV_NEEDS_SANITIZE_LEAK () {
+ BAIL_OUT "$1 has no effect except when compiled with SANITIZE=leak"
+}
+
+if test -n "$SANITIZE_LEAK"
+then
+ # Normalize with test_bool_env
+ passes_sanitize_leak=
+
+ # We need to see TEST_PASSES_SANITIZE_LEAK in "test-tool
+ # env-helper" (via test_bool_env)
+ export TEST_PASSES_SANITIZE_LEAK
+ if test_bool_env TEST_PASSES_SANITIZE_LEAK false
+ then
+ passes_sanitize_leak=t
+ fi
+
+ if test "$GIT_TEST_PASSING_SANITIZE_LEAK" = "check"
+ then
+ sanitize_leak_check=t
+ if test -n "$invert_exit_code"
+ then
+ BAIL_OUT "cannot use --invert-exit-code under GIT_TEST_PASSING_SANITIZE_LEAK=check"
+ fi
+
+ if test -z "$passes_sanitize_leak"
+ then
+ say "in GIT_TEST_PASSING_SANITIZE_LEAK=check mode, setting --invert-exit-code for TEST_PASSES_SANITIZE_LEAK != true"
+ invert_exit_code=t
+ fi
+ elif test -z "$passes_sanitize_leak" &&
+ test_bool_env GIT_TEST_PASSING_SANITIZE_LEAK false
+ then
+ skip_all="skipping $this_test under GIT_TEST_PASSING_SANITIZE_LEAK=true"
+ test_done
+ fi
+
+ if test_bool_env GIT_TEST_SANITIZE_LEAK_LOG false
+ then
+ if ! mkdir -p "$TEST_RESULTS_SAN_DIR"
+ then
+ BAIL_OUT "cannot create $TEST_RESULTS_SAN_DIR"
+ fi &&
+ TEST_RESULTS_SAN_FILE="$TEST_RESULTS_SAN_DIR/$TEST_RESULTS_SAN_FILE_PFX"
+
+ # In case "test-results" is left over from a previous
+ # run: Only report if new leaks show up.
+ TEST_RESULTS_SAN_DIR_NR_LEAKS_STARTUP=$(nr_san_dir_leaks_)
+
+ # Don't litter *.leak dirs if there was nothing to report
+ test_atexit "rmdir \"$TEST_RESULTS_SAN_DIR\" 2>/dev/null || :"
+
+ prepend_var LSAN_OPTIONS : dedup_token_length=9999
+ prepend_var LSAN_OPTIONS : log_exe_name=1
+ prepend_var LSAN_OPTIONS : log_path=\"$TEST_RESULTS_SAN_FILE\"
+ export LSAN_OPTIONS
+ fi
+elif test "$GIT_TEST_PASSING_SANITIZE_LEAK" = "check" ||
+ test_bool_env GIT_TEST_PASSING_SANITIZE_LEAK false
+then
+ BAIL_OUT_ENV_NEEDS_SANITIZE_LEAK "GIT_TEST_PASSING_SANITIZE_LEAK=true"
+elif test_bool_env GIT_TEST_SANITIZE_LEAK_LOG false
+then
+ BAIL_OUT_ENV_NEEDS_SANITIZE_LEAK "GIT_TEST_SANITIZE_LEAK_LOG=true"
+fi
+
+if test "${GIT_TEST_CHAIN_LINT:-1}" != 0 &&
+ test "${GIT_TEST_EXT_CHAIN_LINT:-1}" != 0
+then
+ "$PERL_PATH" "$TEST_DIRECTORY/chainlint.pl" "$0" ||
+ BUG "lint error (see '?!...!? annotations above)"
+fi
+
# Last-minute variable setup
USER_HOME="$HOME"
HOME="$TRASH_DIRECTORY"
GNUPGHOME="$HOME/gnupg-home-not-used"
export HOME GNUPGHOME USER_HOME
+# "rm -rf" existing trash directory, even if a previous run left it
+# with bad permissions.
+remove_trash_directory () {
+ dir="$1"
+ if ! rm -rf "$dir" 2>/dev/null
+ then
+ chmod -R u+rwx "$dir"
+ rm -rf "$dir"
+ fi
+ ! test -d "$dir"
+}
+
# Test repository
-rm -fr "$TRASH_DIRECTORY" || {
- GIT_EXIT_OK=t
- echo >&5 "FATAL: Cannot prepare test area"
- exit 1
+remove_trash_directory "$TRASH_DIRECTORY" || {
+ BAIL_OUT 'cannot prepare test area'
}
remove_trash=t
if test -z "$TEST_NO_CREATE_REPO"
then
- git init "$TRASH_DIRECTORY" >&3 2>&4 ||
+ git init \
+ ${TEST_CREATE_REPO_NO_TEMPLATE:+--template=} \
+ "$TRASH_DIRECTORY" >&3 2>&4 ||
error "cannot run git init"
else
mkdir -p "$TRASH_DIRECTORY"
@@ -1405,30 +1647,14 @@ fi
# Use -P to resolve symlinks in our working directory so that the cwd
# in subprocesses like git equals our $PWD (for pathname comparisons).
-cd -P "$TRASH_DIRECTORY" || exit 1
+cd -P "$TRASH_DIRECTORY" || BAIL_OUT "cannot cd -P to \"$TRASH_DIRECTORY\""
-if test -n "$write_junit_xml"
-then
- junit_xml_dir="$TEST_OUTPUT_DIRECTORY/out"
- mkdir -p "$junit_xml_dir"
- junit_xml_base=${0##*/}
- junit_xml_path="$junit_xml_dir/TEST-${junit_xml_base%.sh}.xml"
- junit_attrs="name=\"${junit_xml_base%.sh}\""
- junit_attrs="$junit_attrs timestamp=\"$(TZ=UTC \
- date +%Y-%m-%dT%H:%M:%S)\""
- write_junit_xml --truncate "<testsuites>" " <testsuite $junit_attrs>"
- junit_suite_start=$(test-tool date getnanos)
- if test -n "$GIT_TEST_TEE_OUTPUT_FILE"
- then
- GIT_TEST_TEE_OFFSET=0
- fi
-fi
+start_test_output "$0"
# Convenience
-# A regexp to match 5, 35 and 40 hexdigits
+# A regexp to match 5 and 35 hexdigits
_x05='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
_x35="$_x05$_x05$_x05$_x05$_x05$_x05$_x05"
-_x40="$_x35$_x05"
test_oid_init
@@ -1437,7 +1663,6 @@ OID_REGEX=$(echo $ZERO_OID | sed -e 's/0/[0-9a-f]/g')
OIDPATH_REGEX=$(test_oid_to_path $ZERO_OID | sed -e 's/0/[0-9a-f]/g')
EMPTY_TREE=$(test_oid empty_tree)
EMPTY_BLOB=$(test_oid empty_blob)
-_z40=$ZERO_OID
# Provide an implementation of the 'yes' utility; the upper bound
# limit is there to help Windows that cannot stop this loop from
@@ -1463,7 +1688,7 @@ yes () {
# The GIT_TEST_FAIL_PREREQS code hooks into test_set_prereq(), and
# thus needs to be set up really early, and set an internal variable
# for convenience so the hot test_set_prereq() codepath doesn't need
-# to call "git env--helper" (via test_bool_env). Only do that work
+# to call "test-tool env-helper" (via test_bool_env). Only do that work
# if needed by seeing if GIT_TEST_FAIL_PREREQS is set at all.
GIT_TEST_FAIL_PREREQS_INTERNAL=
if test -n "$GIT_TEST_FAIL_PREREQS"
@@ -1502,7 +1727,7 @@ case $uname_s in
test_set_prereq SED_STRIPS_CR
test_set_prereq GREP_STRIPS_CR
test_set_prereq WINDOWS
- GIT_TEST_CMP=mingw_test_cmp
+ GIT_TEST_CMP="GIT_DIR=/dev/null git diff --no-index --ignore-cr-at-eol --"
;;
*CYGWIN*)
test_set_prereq POSIXPERM
@@ -1527,15 +1752,27 @@ parisc* | hppa*)
;;
esac
-test_set_prereq REFFILES
+case "$GIT_DEFAULT_REF_FORMAT" in
+files)
+ test_set_prereq REFFILES;;
+reftable)
+ test_set_prereq REFTABLE;;
+*)
+ echo 2>&1 "error: unknown ref format $GIT_DEFAULT_REF_FORMAT"
+ exit 1
+ ;;
+esac
( COLUMNS=1 && test $COLUMNS = 1 ) && test_set_prereq COLUMNS_CAN_BE_1
+test -z "$NO_CURL" && test_set_prereq LIBCURL
test -z "$NO_PERL" && test_set_prereq PERL
test -z "$NO_PTHREADS" && test_set_prereq PTHREADS
test -z "$NO_PYTHON" && test_set_prereq PYTHON
test -n "$USE_LIBPCRE2" && test_set_prereq PCRE
test -n "$USE_LIBPCRE2" && test_set_prereq LIBPCRE2
test -z "$NO_GETTEXT" && test_set_prereq GETTEXT
+test -n "$SANITIZE_LEAK" && test_set_prereq SANITIZE_LEAK
+test -n "$GIT_VALGRIND_ENABLED" && test_set_prereq VALGRIND
if test -z "$GIT_TEST_CHECK_CACHE_TREE"
then
@@ -1689,6 +1926,10 @@ build_option () {
sed -ne "s/^$1: //p"
}
+test_lazy_prereq SIZE_T_IS_64BIT '
+ test 8 -eq "$(build_option sizeof-size_t)"
+'
+
test_lazy_prereq LONG_IS_64BIT '
test 8 -le "$(build_option sizeof-long)"
'
@@ -1711,8 +1952,8 @@ test_lazy_prereq SHA1 '
esac
'
-test_lazy_prereq REBASE_P '
- test -z "$GIT_TEST_SKIP_REBASE_P"
+test_lazy_prereq DEFAULT_REPO_FORMAT '
+ test_have_prereq SHA1,REFFILES
'
# Ensure that no test accidentally triggers a Git command
@@ -1721,3 +1962,11 @@ test_lazy_prereq REBASE_P '
# Tests that verify the scheduler integration must set this locally
# to avoid errors.
GIT_TEST_MAINT_SCHEDULER="none:exit 1"
+export GIT_TEST_MAINT_SCHEDULER
+
+# Does this platform support `git fsmonitor--daemon`
+#
+test_lazy_prereq FSMONITOR_DAEMON '
+ git version --build-options >output &&
+ grep "feature: fsmonitor--daemon" output
+'
diff --git a/t/test-terminal.perl b/t/test-terminal.perl
index 1bcf01a..3810e9b 100755
--- a/t/test-terminal.perl
+++ b/t/test-terminal.perl
@@ -1,5 +1,5 @@
#!/usr/bin/perl
-use 5.008;
+use 5.008001;
use strict;
use warnings;
use IO::Pty;
diff --git a/t/unit-tests/.gitignore b/t/unit-tests/.gitignore
new file mode 100644
index 0000000..5e56e04
--- /dev/null
+++ b/t/unit-tests/.gitignore
@@ -0,0 +1 @@
+/bin
diff --git a/t/unit-tests/t-basic.c b/t/unit-tests/t-basic.c
new file mode 100644
index 0000000..fda1ae5
--- /dev/null
+++ b/t/unit-tests/t-basic.c
@@ -0,0 +1,95 @@
+#include "test-lib.h"
+
+/*
+ * The purpose of this "unit test" is to verify a few invariants of the unit
+ * test framework itself, as well as to provide examples of output from actually
+ * failing tests. As such, it is intended that this test fails, and thus it
+ * should not be run as part of `make unit-tests`. Instead, we verify it behaves
+ * as expected in the integration test t0080-unit-test-output.sh
+ */
+
+/* Used to store the return value of check_int(). */
+static int check_res;
+
+/* Used to store the return value of TEST(). */
+static int test_res;
+
+static void t_res(int expect)
+{
+ check_int(check_res, ==, expect);
+ check_int(test_res, ==, expect);
+}
+
+static void t_todo(int x)
+{
+ check_res = TEST_TODO(check(x));
+}
+
+static void t_skip(void)
+{
+ check(0);
+ test_skip("missing prerequisite");
+ check(1);
+}
+
+static int do_skip(void)
+{
+ test_skip("missing prerequisite");
+ return 1;
+}
+
+static void t_skip_todo(void)
+{
+ check_res = TEST_TODO(do_skip());
+}
+
+static void t_todo_after_fail(void)
+{
+ check(0);
+ TEST_TODO(check(0));
+}
+
+static void t_fail_after_todo(void)
+{
+ check(1);
+ TEST_TODO(check(0));
+ check(0);
+}
+
+static void t_messages(void)
+{
+ check_str("\thello\\", "there\"\n");
+ check_str("NULL", NULL);
+ check_char('a', ==, '\n');
+ check_char('\\', ==, '\'');
+}
+
+static void t_empty(void)
+{
+ ; /* empty */
+}
+
+int cmd_main(int argc, const char **argv)
+{
+ test_res = TEST(check_res = check_int(1, ==, 1), "passing test");
+ TEST(t_res(1), "passing test and assertion return 1");
+ test_res = TEST(check_res = check_int(1, ==, 2), "failing test");
+ TEST(t_res(0), "failing test and assertion return 0");
+ test_res = TEST(t_todo(0), "passing TEST_TODO()");
+ TEST(t_res(1), "passing TEST_TODO() returns 1");
+ test_res = TEST(t_todo(1), "failing TEST_TODO()");
+ TEST(t_res(0), "failing TEST_TODO() returns 0");
+ test_res = TEST(t_skip(), "test_skip()");
+ TEST(check_int(test_res, ==, 1), "skipped test returns 1");
+ test_res = TEST(t_skip_todo(), "test_skip() inside TEST_TODO()");
+ TEST(t_res(1), "test_skip() inside TEST_TODO() returns 1");
+ test_res = TEST(t_todo_after_fail(), "TEST_TODO() after failing check");
+ TEST(check_int(test_res, ==, 0), "TEST_TODO() after failing check returns 0");
+ test_res = TEST(t_fail_after_todo(), "failing check after TEST_TODO()");
+ TEST(check_int(test_res, ==, 0), "failing check after TEST_TODO() returns 0");
+ TEST(t_messages(), "messages from failing string and char comparison");
+ test_res = TEST(t_empty(), "test with no checks");
+ TEST(check_int(test_res, ==, 0), "test with no checks returns 0");
+
+ return test_done();
+}
diff --git a/t/unit-tests/t-ctype.c b/t/unit-tests/t-ctype.c
new file mode 100644
index 0000000..d6ac1fe
--- /dev/null
+++ b/t/unit-tests/t-ctype.c
@@ -0,0 +1,53 @@
+#include "test-lib.h"
+
+#define TEST_CHAR_CLASS(class, string) do { \
+ size_t len = ARRAY_SIZE(string) - 1 + \
+ BUILD_ASSERT_OR_ZERO(ARRAY_SIZE(string) > 0) + \
+ BUILD_ASSERT_OR_ZERO(sizeof(string[0]) == sizeof(char)); \
+ int skip = test__run_begin(); \
+ if (!skip) { \
+ for (int i = 0; i < 256; i++) { \
+ if (!check_int(class(i), ==, !!memchr(string, i, len)))\
+ test_msg(" i: 0x%02x", i); \
+ } \
+ check(!class(EOF)); \
+ } \
+ test__run_end(!skip, TEST_LOCATION(), #class " works"); \
+} while (0)
+
+#define DIGIT "0123456789"
+#define LOWER "abcdefghijklmnopqrstuvwxyz"
+#define UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define PUNCT "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
+#define ASCII \
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
+ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
+ "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" \
+ "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" \
+ "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" \
+ "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" \
+ "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" \
+ "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
+#define CNTRL \
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
+ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
+ "\x7f"
+
+int cmd_main(int argc, const char **argv) {
+ TEST_CHAR_CLASS(isspace, " \n\r\t");
+ TEST_CHAR_CLASS(isdigit, DIGIT);
+ TEST_CHAR_CLASS(isalpha, LOWER UPPER);
+ TEST_CHAR_CLASS(isalnum, LOWER UPPER DIGIT);
+ TEST_CHAR_CLASS(is_glob_special, "*?[\\");
+ TEST_CHAR_CLASS(is_regex_special, "$()*+.?[\\^{|");
+ TEST_CHAR_CLASS(is_pathspec_magic, "!\"#%&',-/:;<=>@_`~");
+ TEST_CHAR_CLASS(isascii, ASCII);
+ TEST_CHAR_CLASS(islower, LOWER);
+ TEST_CHAR_CLASS(isupper, UPPER);
+ TEST_CHAR_CLASS(iscntrl, CNTRL);
+ TEST_CHAR_CLASS(ispunct, PUNCT);
+ TEST_CHAR_CLASS(isxdigit, DIGIT "abcdefABCDEF");
+ TEST_CHAR_CLASS(isprint, LOWER UPPER DIGIT PUNCT " ");
+
+ return test_done();
+}
diff --git a/t/unit-tests/t-mem-pool.c b/t/unit-tests/t-mem-pool.c
new file mode 100644
index 0000000..a0d57df
--- /dev/null
+++ b/t/unit-tests/t-mem-pool.c
@@ -0,0 +1,31 @@
+#include "test-lib.h"
+#include "mem-pool.h"
+
+static void setup_static(void (*f)(struct mem_pool *), size_t block_alloc)
+{
+ struct mem_pool pool = { .block_alloc = block_alloc };
+ f(&pool);
+ mem_pool_discard(&pool, 0);
+}
+
+static void t_calloc_100(struct mem_pool *pool)
+{
+ size_t size = 100;
+ char *buffer = mem_pool_calloc(pool, 1, size);
+ for (size_t i = 0; i < size; i++)
+ check_int(buffer[i], ==, 0);
+ if (!check(pool->mp_block != NULL))
+ return;
+ check(pool->mp_block->next_free != NULL);
+ check(pool->mp_block->end != NULL);
+}
+
+int cmd_main(int argc, const char **argv)
+{
+ TEST(setup_static(t_calloc_100, 1024 * 1024),
+ "mem_pool_calloc returns 100 zeroed bytes with big block");
+ TEST(setup_static(t_calloc_100, 1),
+ "mem_pool_calloc returns 100 zeroed bytes with tiny block");
+
+ return test_done();
+}
diff --git a/t/unit-tests/t-prio-queue.c b/t/unit-tests/t-prio-queue.c
new file mode 100644
index 0000000..7a4e578
--- /dev/null
+++ b/t/unit-tests/t-prio-queue.c
@@ -0,0 +1,91 @@
+#include "test-lib.h"
+#include "prio-queue.h"
+
+static int intcmp(const void *va, const void *vb, void *data UNUSED)
+{
+ const int *a = va, *b = vb;
+ return *a - *b;
+}
+
+
+#define MISSING -1
+#define DUMP -2
+#define STACK -3
+#define GET -4
+#define REVERSE -5
+
+static int show(int *v)
+{
+ return v ? *v : MISSING;
+}
+
+static void test_prio_queue(int *input, size_t input_size,
+ int *result, size_t result_size)
+{
+ struct prio_queue pq = { intcmp };
+ int j = 0;
+
+ for (int i = 0; i < input_size; i++) {
+ void *peek, *get;
+ switch(input[i]) {
+ case GET:
+ peek = prio_queue_peek(&pq);
+ get = prio_queue_get(&pq);
+ if (!check(peek == get))
+ return;
+ if (!check_uint(j, <, result_size))
+ break;
+ if (!check_int(result[j], ==, show(get)))
+ test_msg(" j: %d", j);
+ j++;
+ break;
+ case DUMP:
+ while ((peek = prio_queue_peek(&pq))) {
+ get = prio_queue_get(&pq);
+ if (!check(peek == get))
+ return;
+ if (!check_uint(j, <, result_size))
+ break;
+ if (!check_int(result[j], ==, show(get)))
+ test_msg(" j: %d", j);
+ j++;
+ }
+ break;
+ case STACK:
+ pq.compare = NULL;
+ break;
+ case REVERSE:
+ prio_queue_reverse(&pq);
+ break;
+ default:
+ prio_queue_put(&pq, &input[i]);
+ break;
+ }
+ }
+ check_uint(j, ==, result_size);
+ clear_prio_queue(&pq);
+}
+
+#define TEST_INPUT(input, result) \
+ test_prio_queue(input, ARRAY_SIZE(input), result, ARRAY_SIZE(result))
+
+int cmd_main(int argc, const char **argv)
+{
+ TEST(TEST_INPUT(((int []){ 2, 6, 3, 10, 9, 5, 7, 4, 5, 8, 1, DUMP }),
+ ((int []){ 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10 })),
+ "prio-queue works for basic input");
+ TEST(TEST_INPUT(((int []){ 6, 2, 4, GET, 5, 3, GET, GET, 1, DUMP }),
+ ((int []){ 2, 3, 4, 1, 5, 6 })),
+ "prio-queue works for mixed put & get commands");
+ TEST(TEST_INPUT(((int []){ 1, 2, GET, GET, GET, 1, 2, GET, GET, GET }),
+ ((int []){ 1, 2, MISSING, 1, 2, MISSING })),
+ "prio-queue works when queue is empty");
+ TEST(TEST_INPUT(((int []){ STACK, 8, 1, 5, 4, 6, 2, 3, DUMP }),
+ ((int []){ 3, 2, 6, 4, 5, 1, 8 })),
+ "prio-queue works when used as a LIFO stack");
+ TEST(TEST_INPUT(((int []){ STACK, 1, 2, 3, 4, 5, 6, REVERSE, DUMP }),
+ ((int []){ 1, 2, 3, 4, 5, 6 })),
+ "prio-queue works when LIFO stack is reversed");
+
+ return test_done();
+}
diff --git a/t/unit-tests/t-strbuf.c b/t/unit-tests/t-strbuf.c
new file mode 100644
index 0000000..de434a4
--- /dev/null
+++ b/t/unit-tests/t-strbuf.c
@@ -0,0 +1,120 @@
+#include "test-lib.h"
+#include "strbuf.h"
+
+/* wrapper that supplies tests with an empty, initialized strbuf */
+static void setup(void (*f)(struct strbuf*, void*), void *data)
+{
+ struct strbuf buf = STRBUF_INIT;
+
+ f(&buf, data);
+ strbuf_release(&buf);
+ check_uint(buf.len, ==, 0);
+ check_uint(buf.alloc, ==, 0);
+}
+
+/* wrapper that supplies tests with a populated, initialized strbuf */
+static void setup_populated(void (*f)(struct strbuf*, void*), char *init_str, void *data)
+{
+ struct strbuf buf = STRBUF_INIT;
+
+ strbuf_addstr(&buf, init_str);
+ check_uint(buf.len, ==, strlen(init_str));
+ f(&buf, data);
+ strbuf_release(&buf);
+ check_uint(buf.len, ==, 0);
+ check_uint(buf.alloc, ==, 0);
+}
+
+static int assert_sane_strbuf(struct strbuf *buf)
+{
+ /* Initialized strbufs should always have a non-NULL buffer */
+ if (!check(!!buf->buf))
+ return 0;
+ /* Buffers should always be NUL-terminated */
+ if (!check_char(buf->buf[buf->len], ==, '\0'))
+ return 0;
+ /*
+ * Freshly-initialized strbufs may not have a dynamically allocated
+ * buffer
+ */
+ if (buf->len == 0 && buf->alloc == 0)
+ return 1;
+ /* alloc must be at least one byte larger than len */
+ return check_uint(buf->len, <, buf->alloc);
+}
+
+static void t_static_init(void)
+{
+ struct strbuf buf = STRBUF_INIT;
+
+ check_uint(buf.len, ==, 0);
+ check_uint(buf.alloc, ==, 0);
+ check_char(buf.buf[0], ==, '\0');
+}
+
+static void t_dynamic_init(void)
+{
+ struct strbuf buf;
+
+ strbuf_init(&buf, 1024);
+ check(assert_sane_strbuf(&buf));
+ check_uint(buf.len, ==, 0);
+ check_uint(buf.alloc, >=, 1024);
+ check_char(buf.buf[0], ==, '\0');
+ strbuf_release(&buf);
+}
+
+static void t_addch(struct strbuf *buf, void *data)
+{
+ const char *p_ch = data;
+ const char ch = *p_ch;
+ size_t orig_alloc = buf->alloc;
+ size_t orig_len = buf->len;
+
+ if (!check(assert_sane_strbuf(buf)))
+ return;
+ strbuf_addch(buf, ch);
+ if (!check(assert_sane_strbuf(buf)))
+ return;
+ if (!(check_uint(buf->len, ==, orig_len + 1) &&
+ check_uint(buf->alloc, >=, orig_alloc)))
+ return; /* avoid de-referencing buf->buf */
+ check_char(buf->buf[buf->len - 1], ==, ch);
+ check_char(buf->buf[buf->len], ==, '\0');
+}
+
+static void t_addstr(struct strbuf *buf, void *data)
+{
+ const char *text = data;
+ size_t len = strlen(text);
+ size_t orig_alloc = buf->alloc;
+ size_t orig_len = buf->len;
+
+ if (!check(assert_sane_strbuf(buf)))
+ return;
+ strbuf_addstr(buf, text);
+ if (!check(assert_sane_strbuf(buf)))
+ return;
+ if (!(check_uint(buf->len, ==, orig_len + len) &&
+ check_uint(buf->alloc, >=, orig_alloc) &&
+ check_uint(buf->alloc, >, orig_len + len) &&
+ check_char(buf->buf[orig_len + len], ==, '\0')))
+ return;
+ check_str(buf->buf + orig_len, text);
+}
+
+int cmd_main(int argc, const char **argv)
+{
+ if (!TEST(t_static_init(), "static initialization works"))
+ test_skip_all("STRBUF_INIT is broken");
+ TEST(t_dynamic_init(), "dynamic initialization works");
+ TEST(setup(t_addch, "a"), "strbuf_addch adds char");
+ TEST(setup(t_addch, ""), "strbuf_addch adds NUL char");
+ TEST(setup_populated(t_addch, "initial value", "a"),
+ "strbuf_addch appends to initial value");
+ TEST(setup(t_addstr, "hello there"), "strbuf_addstr adds string");
+ TEST(setup_populated(t_addstr, "initial value", "hello there"),
+ "strbuf_addstr appends string to initial value");
+
+ return test_done();
+}
diff --git a/t/unit-tests/test-lib.c b/t/unit-tests/test-lib.c
new file mode 100644
index 0000000..66d6980
--- /dev/null
+++ b/t/unit-tests/test-lib.c
@@ -0,0 +1,407 @@
+#include "test-lib.h"
+
+enum result {
+ RESULT_NONE,
+ RESULT_FAILURE,
+ RESULT_SKIP,
+ RESULT_SUCCESS,
+ RESULT_TODO
+};
+
+static struct {
+ enum result result;
+ int count;
+ unsigned failed :1;
+ unsigned lazy_plan :1;
+ unsigned running :1;
+ unsigned skip_all :1;
+ unsigned todo :1;
+} ctx = {
+ .lazy_plan = 1,
+ .result = RESULT_NONE,
+};
+
+/*
+ * Visual C interpolates the absolute Windows path for `__FILE__`,
+ * but we want to see relative paths, as verified by t0080.
+ * There are other compilers that do the same, and are not for
+ * Windows.
+ */
+#include "dir.h"
+
+static const char *make_relative(const char *location)
+{
+ static char prefix[] = __FILE__, buf[PATH_MAX], *p;
+ static size_t prefix_len;
+ static int need_bs_to_fs = -1;
+
+ /* one-time preparation */
+ if (need_bs_to_fs < 0) {
+ size_t len = strlen(prefix);
+ char needle[] = "t\\unit-tests\\test-lib.c";
+ size_t needle_len = strlen(needle);
+
+ if (len < needle_len)
+ die("unexpected prefix '%s'", prefix);
+
+ /*
+ * The path could be relative (t/unit-tests/test-lib.c)
+ * or full (/home/user/git/t/unit-tests/test-lib.c).
+ * Check the slash between "t" and "unit-tests".
+ */
+ prefix_len = len - needle_len;
+ if (prefix[prefix_len + 1] == '/') {
+ /* Oh, we're not Windows */
+ for (size_t i = 0; i < needle_len; i++)
+ if (needle[i] == '\\')
+ needle[i] = '/';
+ need_bs_to_fs = 0;
+ } else {
+ need_bs_to_fs = 1;
+ }
+
+ /*
+ * prefix_len == 0 if the compiler gives paths relative
+ * to the root of the working tree. Otherwise, we want
+ * to see that we did find the needle[] at a directory
+ * boundary. Again we rely on that needle[] begins with
+ * "t" followed by the directory separator.
+ */
+ if (fspathcmp(needle, prefix + prefix_len) ||
+ (prefix_len && prefix[prefix_len - 1] != needle[1]))
+ die("unexpected suffix of '%s'", prefix);
+ }
+
+ /*
+ * Does it not start with the expected prefix?
+ * Return it as-is without making it worse.
+ */
+ if (prefix_len && fspathncmp(location, prefix, prefix_len))
+ return location;
+
+ /*
+ * If we do not need to munge directory separator, we can return
+ * the substring at the tail of the location.
+ */
+ if (!need_bs_to_fs)
+ return location + prefix_len;
+
+ /* convert backslashes to forward slashes */
+ strlcpy(buf, location + prefix_len, sizeof(buf));
+ for (p = buf; *p; p++)
+ if (*p == '\\')
+ *p = '/';
+ return buf;
+}
+
+static void msg_with_prefix(const char *prefix, const char *format, va_list ap)
+{
+ fflush(stderr);
+ if (prefix)
+ fprintf(stdout, "%s", prefix);
+ vprintf(format, ap); /* TODO: handle newlines */
+ putc('\n', stdout);
+ fflush(stdout);
+}
+
+void test_msg(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ msg_with_prefix("# ", format, ap);
+ va_end(ap);
+}
+
+void test_plan(int count)
+{
+ assert(!ctx.running);
+
+ fflush(stderr);
+ printf("1..%d\n", count);
+ fflush(stdout);
+ ctx.lazy_plan = 0;
+}
+
+int test_done(void)
+{
+ assert(!ctx.running);
+
+ if (ctx.lazy_plan)
+ test_plan(ctx.count);
+
+ return ctx.failed;
+}
+
+void test_skip(const char *format, ...)
+{
+ va_list ap;
+
+ assert(ctx.running);
+
+ ctx.result = RESULT_SKIP;
+ va_start(ap, format);
+ if (format)
+ msg_with_prefix("# skipping test - ", format, ap);
+ va_end(ap);
+}
+
+void test_skip_all(const char *format, ...)
+{
+ va_list ap;
+ const char *prefix;
+
+ if (!ctx.count && ctx.lazy_plan) {
+ /* We have not printed a test plan yet */
+ prefix = "1..0 # SKIP ";
+ ctx.lazy_plan = 0;
+ } else {
+ /* We have already printed a test plan */
+ prefix = "Bail out! # ";
+ ctx.failed = 1;
+ }
+ ctx.skip_all = 1;
+ ctx.result = RESULT_SKIP;
+ va_start(ap, format);
+ msg_with_prefix(prefix, format, ap);
+ va_end(ap);
+}
+
+int test__run_begin(void)
+{
+ assert(!ctx.running);
+
+ ctx.count++;
+ ctx.result = RESULT_NONE;
+ ctx.running = 1;
+
+ return ctx.skip_all;
+}
+
+static void print_description(const char *format, va_list ap)
+{
+ if (format) {
+ fputs(" - ", stdout);
+ vprintf(format, ap);
+ }
+}
+
+int test__run_end(int was_run UNUSED, const char *location, const char *format, ...)
+{
+ va_list ap;
+
+ assert(ctx.running);
+ assert(!ctx.todo);
+
+ fflush(stderr);
+ va_start(ap, format);
+ if (!ctx.skip_all) {
+ switch (ctx.result) {
+ case RESULT_SUCCESS:
+ printf("ok %d", ctx.count);
+ print_description(format, ap);
+ break;
+
+ case RESULT_FAILURE:
+ printf("not ok %d", ctx.count);
+ print_description(format, ap);
+ break;
+
+ case RESULT_TODO:
+ printf("not ok %d", ctx.count);
+ print_description(format, ap);
+ printf(" # TODO");
+ break;
+
+ case RESULT_SKIP:
+ printf("ok %d", ctx.count);
+ print_description(format, ap);
+ printf(" # SKIP");
+ break;
+
+ case RESULT_NONE:
+ test_msg("BUG: test has no checks at %s",
+ make_relative(location));
+ printf("not ok %d", ctx.count);
+ print_description(format, ap);
+ ctx.result = RESULT_FAILURE;
+ break;
+ }
+ }
+ va_end(ap);
+ ctx.running = 0;
+ if (ctx.skip_all)
+ return 1;
+ putc('\n', stdout);
+ fflush(stdout);
+ ctx.failed |= ctx.result == RESULT_FAILURE;
+
+ return ctx.result != RESULT_FAILURE;
+}
+
+static void test_fail(void)
+{
+ assert(ctx.result != RESULT_SKIP);
+
+ ctx.result = RESULT_FAILURE;
+}
+
+static void test_pass(void)
+{
+ assert(ctx.result != RESULT_SKIP);
+
+ if (ctx.result == RESULT_NONE)
+ ctx.result = RESULT_SUCCESS;
+}
+
+static void test_todo(void)
+{
+ assert(ctx.result != RESULT_SKIP);
+
+ if (ctx.result != RESULT_FAILURE)
+ ctx.result = RESULT_TODO;
+}
+
+int test_assert(const char *location, const char *check, int ok)
+{
+ assert(ctx.running);
+
+ if (ctx.result == RESULT_SKIP) {
+ test_msg("skipping check '%s' at %s", check,
+ make_relative(location));
+ return 1;
+ }
+ if (!ctx.todo) {
+ if (ok) {
+ test_pass();
+ } else {
+ test_msg("check \"%s\" failed at %s", check,
+ make_relative(location));
+ test_fail();
+ }
+ }
+
+ return !!ok;
+}
+
+void test__todo_begin(void)
+{
+ assert(ctx.running);
+ assert(!ctx.todo);
+
+ ctx.todo = 1;
+}
+
+int test__todo_end(const char *location, const char *check, int res)
+{
+ assert(ctx.running);
+ assert(ctx.todo);
+
+ ctx.todo = 0;
+ if (ctx.result == RESULT_SKIP)
+ return 1;
+ if (res) {
+ test_msg("todo check '%s' succeeded at %s", check,
+ make_relative(location));
+ test_fail();
+ } else {
+ test_todo();
+ }
+
+ return !res;
+}
+
+int check_bool_loc(const char *loc, const char *check, int ok)
+{
+ return test_assert(loc, check, ok);
+}
+
+union test__tmp test__tmp[2];
+
+int check_int_loc(const char *loc, const char *check, int ok,
+ intmax_t a, intmax_t b)
+{
+ int ret = test_assert(loc, check, ok);
+
+ if (!ret) {
+ test_msg(" left: %"PRIdMAX, a);
+ test_msg(" right: %"PRIdMAX, b);
+ }
+
+ return ret;
+}
+
+int check_uint_loc(const char *loc, const char *check, int ok,
+ uintmax_t a, uintmax_t b)
+{
+ int ret = test_assert(loc, check, ok);
+
+ if (!ret) {
+ test_msg(" left: %"PRIuMAX, a);
+ test_msg(" right: %"PRIuMAX, b);
+ }
+
+ return ret;
+}
+
+static void print_one_char(char ch, char quote)
+{
+ if ((unsigned char)ch < 0x20u || ch == 0x7f) {
+ /* TODO: improve handling of \a, \b, \f ... */
+ printf("\\%03o", (unsigned char)ch);
+ } else {
+ if (ch == '\\' || ch == quote)
+ putc('\\', stdout);
+ putc(ch, stdout);
+ }
+}
+
+static void print_char(const char *prefix, char ch)
+{
+ printf("# %s: '", prefix);
+ print_one_char(ch, '\'');
+ fputs("'\n", stdout);
+}
+
+int check_char_loc(const char *loc, const char *check, int ok, char a, char b)
+{
+ int ret = test_assert(loc, check, ok);
+
+ if (!ret) {
+ fflush(stderr);
+ print_char(" left", a);
+ print_char(" right", b);
+ fflush(stdout);
+ }
+
+ return ret;
+}
+
+static void print_str(const char *prefix, const char *str)
+{
+ printf("# %s: ", prefix);
+ if (!str) {
+ fputs("NULL\n", stdout);
+ } else {
+ putc('"', stdout);
+ while (*str)
+ print_one_char(*str++, '"');
+ fputs("\"\n", stdout);
+ }
+}
+
+int check_str_loc(const char *loc, const char *check,
+ const char *a, const char *b)
+{
+ int ok = (!a && !b) || (a && b && !strcmp(a, b));
+ int ret = test_assert(loc, check, ok);
+
+ if (!ret) {
+ fflush(stderr);
+ print_str(" left", a);
+ print_str(" right", b);
+ fflush(stdout);
+ }
+
+ return ret;
+}
diff --git a/t/unit-tests/test-lib.h b/t/unit-tests/test-lib.h
new file mode 100644
index 0000000..a8f07ae
--- /dev/null
+++ b/t/unit-tests/test-lib.h
@@ -0,0 +1,149 @@
+#ifndef TEST_LIB_H
+#define TEST_LIB_H
+
+#include "git-compat-util.h"
+
+/*
+ * Run a test function, returns 1 if the test succeeds, 0 if it
+ * fails. If test_skip_all() has been called then the test will not be
+ * run. The description for each test should be unique. For example:
+ *
+ * TEST(test_something(arg1, arg2), "something %d %d", arg1, arg2)
+ */
+#define TEST(t, ...) \
+ test__run_end(test__run_begin() ? 0 : (t, 1), \
+ TEST_LOCATION(), __VA_ARGS__)
+
+/*
+ * Print a test plan, should be called before any tests. If the number
+ * of tests is not known in advance test_done() will automatically
+ * print a plan at the end of the test program.
+ */
+void test_plan(int count);
+
+/*
+ * test_done() must be called at the end of main(). It will print the
+ * plan if plan() was not called at the beginning of the test program
+ * and returns the exit code for the test program.
+ */
+int test_done(void);
+
+/* Skip the current test. */
+__attribute__((format (printf, 1, 2)))
+void test_skip(const char *format, ...);
+
+/* Skip all remaining tests. */
+__attribute__((format (printf, 1, 2)))
+void test_skip_all(const char *format, ...);
+
+/* Print a diagnostic message to stdout. */
+__attribute__((format (printf, 1, 2)))
+void test_msg(const char *format, ...);
+
+/*
+ * Test checks are built around test_assert(). checks return 1 on
+ * success, 0 on failure. If any check fails then the test will fail. To
+ * create a custom check define a function that wraps test_assert() and
+ * a macro to wrap that function to provide a source location and
+ * stringified arguments. Custom checks that take pointer arguments
+ * should be careful to check that they are non-NULL before
+ * dereferencing them. For example:
+ *
+ * static int check_oid_loc(const char *loc, const char *check,
+ * struct object_id *a, struct object_id *b)
+ * {
+ * int res = test_assert(loc, check, a && b && oideq(a, b));
+ *
+ * if (!res) {
+ * test_msg(" left: %s", a ? oid_to_hex(a) : "NULL";
+ * test_msg(" right: %s", b ? oid_to_hex(a) : "NULL";
+ *
+ * }
+ * return res;
+ * }
+ *
+ * #define check_oid(a, b) \
+ * check_oid_loc(TEST_LOCATION(), "oideq("#a", "#b")", a, b)
+ */
+int test_assert(const char *location, const char *check, int ok);
+
+/* Helper macro to pass the location to checks */
+#define TEST_LOCATION() TEST__MAKE_LOCATION(__LINE__)
+
+/* Check a boolean condition. */
+#define check(x) \
+ check_bool_loc(TEST_LOCATION(), #x, x)
+int check_bool_loc(const char *loc, const char *check, int ok);
+
+/*
+ * Compare two integers. Prints a message with the two values if the
+ * comparison fails. NB this is not thread safe.
+ */
+#define check_int(a, op, b) \
+ (test__tmp[0].i = (a), test__tmp[1].i = (b), \
+ check_int_loc(TEST_LOCATION(), #a" "#op" "#b, \
+ test__tmp[0].i op test__tmp[1].i, \
+ test__tmp[0].i, test__tmp[1].i))
+int check_int_loc(const char *loc, const char *check, int ok,
+ intmax_t a, intmax_t b);
+
+/*
+ * Compare two unsigned integers. Prints a message with the two values
+ * if the comparison fails. NB this is not thread safe.
+ */
+#define check_uint(a, op, b) \
+ (test__tmp[0].u = (a), test__tmp[1].u = (b), \
+ check_uint_loc(TEST_LOCATION(), #a" "#op" "#b, \
+ test__tmp[0].u op test__tmp[1].u, \
+ test__tmp[0].u, test__tmp[1].u))
+int check_uint_loc(const char *loc, const char *check, int ok,
+ uintmax_t a, uintmax_t b);
+
+/*
+ * Compare two chars. Prints a message with the two values if the
+ * comparison fails. NB this is not thread safe.
+ */
+#define check_char(a, op, b) \
+ (test__tmp[0].c = (a), test__tmp[1].c = (b), \
+ check_char_loc(TEST_LOCATION(), #a" "#op" "#b, \
+ test__tmp[0].c op test__tmp[1].c, \
+ test__tmp[0].c, test__tmp[1].c))
+int check_char_loc(const char *loc, const char *check, int ok,
+ char a, char b);
+
+/* Check whether two strings are equal. */
+#define check_str(a, b) \
+ check_str_loc(TEST_LOCATION(), "!strcmp("#a", "#b")", a, b)
+int check_str_loc(const char *loc, const char *check,
+ const char *a, const char *b);
+
+/*
+ * Wrap a check that is known to fail. If the check succeeds then the
+ * test will fail. Returns 1 if the check fails, 0 if it
+ * succeeds. For example:
+ *
+ * TEST_TODO(check(0));
+ */
+#define TEST_TODO(check) \
+ (test__todo_begin(), test__todo_end(TEST_LOCATION(), #check, check))
+
+/* Private helpers */
+
+#define TEST__STR(x) #x
+#define TEST__MAKE_LOCATION(line) __FILE__ ":" TEST__STR(line)
+
+union test__tmp {
+ intmax_t i;
+ uintmax_t u;
+ char c;
+};
+
+extern union test__tmp test__tmp[2];
+
+int test__run_begin(void);
+__attribute__((format (printf, 3, 4)))
+int test__run_end(int, const char *, const char *, ...);
+void test__todo_begin(void);
+int test__todo_end(const char *, const char *, int);
+
+#endif /* TEST_LIB_H */
diff --git a/t/valgrind/valgrind.sh b/t/valgrind/valgrind.sh
index 669ebaf..3c8ee19 100755
--- a/t/valgrind/valgrind.sh
+++ b/t/valgrind/valgrind.sh
@@ -23,7 +23,7 @@ memcheck)
VALGRIND_MAJOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*\([0-9]*\)')
VALGRIND_MINOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*[0-9]*\.\([0-9]*\)')
test 3 -gt "$VALGRIND_MAJOR" ||
- test 3 -eq "$VALGRIND_MAJOR" -a 4 -gt "$VALGRIND_MINOR" ||
+ { test 3 -eq "$VALGRIND_MAJOR" && test 4 -gt "$VALGRIND_MINOR"; } ||
TOOL_OPTIONS="$TOOL_OPTIONS --track-origins=yes"
;;
*)