summaryrefslogtreecommitdiffstats
path: root/t
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-20 05:14:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-20 05:14:39 +0000
commit7260c37aa8c91c8008dcd2442a19c23d1c9040fb (patch)
tree83953428f11212a71a4616e535c1053076f9bb94 /t
parentReleasing progress-linux version 1:2.43.0-1~progress7.99u1. (diff)
downloadgit-7260c37aa8c91c8008dcd2442a19c23d1c9040fb.tar.xz
git-7260c37aa8c91c8008dcd2442a19c23d1c9040fb.zip
Merging upstream version 1:2.45.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--t/Makefile37
-rw-r--r--t/README34
-rw-r--r--t/annotate-tests.sh2
-rw-r--r--t/chainlint/blank-line-before-esac.expect8
-rw-r--r--t/chainlint/blank-line.expect4
-rw-r--r--t/chainlint/block.expect4
-rw-r--r--t/chainlint/chain-break-background.expect4
-rw-r--r--t/chainlint/chain-break-return-exit.expect14
-rw-r--r--t/chainlint/chain-break-status.expect4
-rw-r--r--t/chainlint/chained-subshell.expect4
-rw-r--r--t/chainlint/command-substitution-subsubshell.expect2
-rw-r--r--t/chainlint/dqstring-line-splice.expect6
-rw-r--r--t/chainlint/dqstring-no-interpolate.expect5
-rw-r--r--t/chainlint/empty-here-doc.expect4
-rw-r--r--t/chainlint/exclamation.expect2
-rw-r--r--t/chainlint/for-loop-abbreviated.expect2
-rw-r--r--t/chainlint/for-loop.expect1
-rw-r--r--t/chainlint/function.expect4
-rw-r--r--t/chainlint/here-doc.expect4
-rw-r--r--t/chainlint/loop-detect-status.expect20
-rw-r--r--t/chainlint/nested-cuddled-subshell.expect6
-rw-r--r--t/chainlint/nested-loop-detect-failure.expect24
-rw-r--r--t/chainlint/nested-subshell.expect1
-rw-r--r--t/chainlint/pipe.expect2
-rw-r--r--t/chainlint/subshell-here-doc.expect4
-rw-r--r--t/chainlint/subshell-one-liner.expect5
-rw-r--r--t/chainlint/t7900-subtree.expect1
-rw-r--r--t/chainlint/token-pasting.expect14
-rw-r--r--t/chainlint/while-loop.expect1
-rwxr-xr-xt/check-non-portable-shell.pl2
-rw-r--r--t/helper/test-bundle-uri.c2
-rw-r--r--t/helper/test-ctype.c70
-rw-r--r--t/helper/test-date.c2
-rw-r--r--t/helper/test-delete-gpgsig.c62
-rw-r--r--t/helper/test-fast-rebase.c241
-rw-r--r--t/helper/test-path-utils.c11
-rw-r--r--t/helper/test-pkt-line.c60
-rw-r--r--t/helper/test-prio-queue.c51
-rw-r--r--t/helper/test-reach.c13
-rw-r--r--t/helper/test-read-midx.c41
-rw-r--r--t/helper/test-ref-store.c49
-rw-r--r--t/helper/test-regex.c2
-rw-r--r--t/helper/test-repository.c2
-rw-r--r--t/helper/test-simple-ipc.c1
-rw-r--r--t/helper/test-submodule.c53
-rw-r--r--t/helper/test-tool.c4
-rw-r--r--t/helper/test-tool.h4
-rw-r--r--t/helper/test-trace2.c55
-rw-r--r--t/lib-credential.sh1
-rw-r--r--t/lib-cvs.sh4
-rw-r--r--t/lib-gpg.sh2
-rw-r--r--t/lib-httpd.sh32
-rw-r--r--t/lib-httpd/passwd2
-rw-r--r--t/lib-httpd/proxy-passwd2
-rw-r--r--t/lib-parallel-checkout.sh2
-rw-r--r--t/oid-info/hash-info12
-rwxr-xr-xt/perf/p5332-multi-pack-reuse.sh81
-rwxr-xr-xt/perf/p6300-for-each-ref.sh87
-rw-r--r--t/perf/perf-lib.sh4
-rwxr-xr-xt/perf/repos/inflate-repo.sh2
-rwxr-xr-xt/perf/run9
-rwxr-xr-xt/t0000-basic.sh28
-rwxr-xr-xt/t0001-init.sh70
-rwxr-xr-xt/t0002-gitfile.sh2
-rwxr-xr-xt/t0003-attributes.sh76
-rwxr-xr-xt/t0006-date.sh9
-rwxr-xr-xt/t0009-prio-queue.sh66
-rwxr-xr-xt/t0010-racy-git.sh31
-rwxr-xr-xt/t0011-hashmap.sh2
-rwxr-xr-xt/t0018-advice.sh1
-rwxr-xr-xt/t0024-crlf-archive.sh13
-rwxr-xr-xt/t0028-working-tree-encoding.sh4
-rwxr-xr-xt/t0030-stripspace.sh15
-rwxr-xr-xt/t0033-safe-directory.sh24
-rwxr-xr-xt/t0035-safe-bare-repository.sh34
-rwxr-xr-xt/t0040-parse-options.sh24
-rwxr-xr-xt/t0060-path-utils.sh41
-rwxr-xr-xt/t0070-fundamental.sh62
-rwxr-xr-xt/t0080-unit-test-output.sh59
-rwxr-xr-xt/t0091-bugreport.sh4
-rwxr-xr-xt/t0202/test.pl2
-rwxr-xr-xt/t0204-gettext-reencode-sanity.sh2
-rwxr-xr-xt/t0210-trace2-normal.sh20
-rwxr-xr-xt/t0211-trace2-perf.sh252
-rwxr-xr-xt/t0212-trace2-event.sh40
-rwxr-xr-xt/t0301-credential-cache.sh8
-rwxr-xr-xt/t0303-credential-external.sh26
-rwxr-xr-xt/t0410-partial-clone.sh19
-rwxr-xr-xt/t0411-clone-from-partial.sh78
-rwxr-xr-xt/t0450-txt-doc-vs-help.sh4
-rwxr-xr-xt/t0600-reffiles-backend.sh475
-rwxr-xr-xt/t0601-reffiles-pack-refs.sh (renamed from t/t3210-pack-refs.sh)86
-rwxr-xr-xt/t0610-reftable-basics.sh1032
-rwxr-xr-xt/t0611-reftable-httpd.sh26
-rwxr-xr-xt/t1006-cat-file.sh417
-rwxr-xr-xt/t1007-hash-object.sh6
-rwxr-xr-xt/t1016-compatObjectFormat.sh281
-rwxr-xr-xt/t1016/gpg2
-rwxr-xr-xt/t1091-sparse-checkout-builtin.sh10
-rwxr-xr-xt/t1300-config.sh214
-rwxr-xr-xt/t1301-shared-repo.sh31
-rwxr-xr-xt/t1302-repo-version.sh23
-rwxr-xr-xt/t1400-update-ref.sh173
-rwxr-xr-xt/t1401-symbolic-ref.sh9
-rwxr-xr-xt/t1403-show-ref.sh38
-rwxr-xr-xt/t1404-update-ref-errors.sh274
-rwxr-xr-xt/t1405-main-ref-store.sh24
-rwxr-xr-xt/t1406-submodule-ref-store.sh8
-rwxr-xr-xt/t1407-worktree-ref-store.sh37
-rwxr-xr-xt/t1409-avoid-packing-refs.sh6
-rwxr-xr-xt/t1410-reflog.sh154
-rwxr-xr-xt/t1414-reflog-walk.sh11
-rwxr-xr-xt/t1415-worktree-refs.sh11
-rwxr-xr-xt/t1417-reflog-updateref.sh10
-rwxr-xr-xt/t1419-exclude-refs.sh6
-rwxr-xr-xt/t1430-bad-ref-name.sh12
-rwxr-xr-xt/t1450-fsck.sh81
-rwxr-xr-xt/t1500-rev-parse.sh17
-rwxr-xr-xt/t1502-rev-parse-parseopt.sh11
-rwxr-xr-xt/t1503-rev-parse-verify.sh5
-rwxr-xr-xt/t1509/prepare-chroot.sh2
-rwxr-xr-xt/t1800-hook.sh15
-rwxr-xr-xt/t2011-checkout-invalid-head.sh9
-rwxr-xr-xt/t2016-checkout-patch.sh47
-rwxr-xr-xt/t2017-checkout-orphan.sh2
-rwxr-xr-xt/t2020-checkout-detach.sh17
-rwxr-xr-xt/t2024-checkout-dwim.sh2
-rwxr-xr-xt/t2060-switch.sh2
-rwxr-xr-xt/t2070-restore.sh1
-rwxr-xr-xt/t2071-restore-patch.sh47
-rwxr-xr-xt/t2072-restore-pathspec-file.sh1
-rwxr-xr-xt/t2104-update-index-skip-worktree.sh30
-rwxr-xr-xt/t2200-add-update.sh10
-rwxr-xr-xt/t2400-worktree-add.sh38
-rwxr-xr-xt/t3200-branch.sh261
-rwxr-xr-xt/t3202-show-branch.sh49
-rwxr-xr-xt/t3310-notes-merge-manual-resolve.sh6
-rwxr-xr-xt/t3321-notes-stripspace.sh8
-rwxr-xr-xt/t3400-rebase.sh10
-rwxr-xr-xt/t3404-rebase-interactive.sh14
-rwxr-xr-xt/t3407-rebase-abort.sh17
-rwxr-xr-xt/t3415-rebase-autosquash.sh38
-rwxr-xr-xt/t3420-rebase-autostash.sh10
-rwxr-xr-xt/t3422-rebase-incompatible-options.sh12
-rwxr-xr-xt/t3424-rebase-empty.sh55
-rwxr-xr-xt/t3428-rebase-signoff.sh67
-rwxr-xr-xt/t3438-rebase-broken-files.sh9
-rwxr-xr-xt/t3501-revert-cherry-pick.sh15
-rwxr-xr-xt/t3505-cherry-pick-empty.sh51
-rwxr-xr-xt/t3507-cherry-pick-conflict.sh2
-rwxr-xr-xt/t3510-cherry-pick-sequence.sh32
-rwxr-xr-xt/t3650-replay-basics.sh198
-rwxr-xr-xt/t3700-add.sh47
-rwxr-xr-xt/t3701-add-interactive.sh23
-rwxr-xr-xt/t3903-stash.sh64
-rwxr-xr-xt/t3904-stash-patch.sh6
-rwxr-xr-xt/t3920-crlf-messages.sh4
-rwxr-xr-xt/t4001-diff-rename.sh24
-rwxr-xr-xt/t4002-diff-basic.sh2
-rwxr-xr-xt/t4011-diff-symlink.sh4
-rwxr-xr-xt/t4013-diff-various.sh90
-rwxr-xr-xt/t4014-format-patch.sh10
-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
-rwxr-xr-xt/t4020-diff-external.sh2
-rwxr-xr-xt/t4042-diff-textconv-caching.sh22
-rwxr-xr-xt/t4053-diff-no-index.sh12
-rwxr-xr-xt/t4103-apply-binary.sh1
-rwxr-xr-xt/t4104-apply-boundary.sh1
-rwxr-xr-xt/t4113-apply-ending.sh1
-rwxr-xr-xt/t4117-apply-reject.sh1
-rwxr-xr-xt/t4123-apply-shrink.sh1
-rwxr-xr-xt/t4126-apply-empty.sh24
-rwxr-xr-xt/t4129-apply-samemode.sh33
-rwxr-xr-xt/t4150-am.sh8
-rwxr-xr-xt/t4200-rerere.sh63
-rwxr-xr-xt/t4201-shortlog.sh32
-rwxr-xr-xt/t4202-log.sh17
-rwxr-xr-xt/t4205-log-pretty-formats.sh40
-rwxr-xr-xt/t4207-log-decoration-colors.sh10
-rwxr-xr-xt/t4210-log-i18n.sh4
-rwxr-xr-xt/t4252-am-options.sh2
-rwxr-xr-xt/t4254-am-corrupt.sh2
-rwxr-xr-xt/t4258-am-quoted-cr.sh1
-rwxr-xr-xt/t4301-merge-tree-write-tree.sh55
-rwxr-xr-xt/t5000-tar-tree.sh10
-rwxr-xr-xt/t5003-archive-zip.sh34
-rwxr-xr-xt/t5100-mailinfo.sh24
-rw-r--r--t/t5100/comment.expect2
-rw-r--r--t/t5100/comment.in2
-rwxr-xr-xt/t5300-pack-object.sh41
-rwxr-xr-xt/t5312-prune-corruption.sh26
-rwxr-xr-xt/t5317-pack-objects-filter-objects.sh2
-rwxr-xr-xt/t5318-commit-graph.sh24
-rwxr-xr-xt/t5319-multi-pack-index.sh49
-rwxr-xr-xt/t5326-multi-pack-bitmaps.sh17
-rwxr-xr-xt/t5332-multi-pack-reuse.sh207
-rwxr-xr-xt/t5401-update-hooks.sh8
-rwxr-xr-xt/t5510-fetch.sh28
-rwxr-xr-xt/t5514-fetch-multiple.sh161
-rwxr-xr-xt/t5526-fetch-submodules.sh2
-rwxr-xr-xt/t5534-push-signed.sh2
-rwxr-xr-xt/t5541-http-push-smart.sh18
-rwxr-xr-xt/t5550-http-fetch-dumb.sh4
-rwxr-xr-xt/t5551-http-fetch-smart.sh22
-rwxr-xr-xt/t5555-http-smart-common.sh1
-rwxr-xr-xt/t5558-clone-bundle-uri.sh18
-rw-r--r--t/t5562/invoke-with-content-length.pl2
-rwxr-xr-xt/t5574-fetch-output.sh89
-rwxr-xr-xt/t5601-clone.sh95
-rwxr-xr-xt/t5605-clone-local.sh4
-rwxr-xr-xt/t5606-clone-options.sh8
-rwxr-xr-xt/t5701-git-serve.sh24
-rwxr-xr-xt/t5702-protocol-v2.sh43
-rwxr-xr-xt/t5801/git-remote-testgit9
-rwxr-xr-xt/t6005-rev-list-count.sh18
-rwxr-xr-xt/t6009-rev-list-parent.sh11
-rwxr-xr-xt/t6018-rev-list-glob.sh6
-rwxr-xr-xt/t6021-rev-list-exclude-hidden.sh4
-rwxr-xr-xt/t6022-rev-list-missing.sh81
-rwxr-xr-xt/t6030-bisect-porcelain.sh10
-rwxr-xr-xt/t6112-rev-list-filters-objects.sh2
-rwxr-xr-xt/t6113-rev-list-bitmap-filters.sh3
-rwxr-xr-xt/t6135-pathspec-with-attrs.sh135
-rwxr-xr-xt/t6300-for-each-ref.sh107
-rwxr-xr-xt/t6301-for-each-ref-errors.sh13
-rwxr-xr-xt/t6302-for-each-ref-filter.sh35
-rwxr-xr-xt/t6403-merge-file.sh124
-rwxr-xr-xt/t6406-merge-attr.sh16
-rwxr-xr-xt/t6413-merge-crlf.sh4
-rwxr-xr-xt/t6418-merge-text-auto.sh1
-rwxr-xr-xt/t6429-merge-sequence-rename-caching.sh45
-rwxr-xr-xt/t6437-submodule-merge.sh14
-rwxr-xr-xt/t6500-gc.sh30
-rwxr-xr-xt/t7003-filter-branch.sh5
-rwxr-xr-xt/t7004-tag.sh49
-rwxr-xr-xt/t7102-reset.sh8
-rwxr-xr-xt/t7105-reset-patch.sh38
-rwxr-xr-xt/t7106-reset-unborn-branch.sh2
-rwxr-xr-xt/t7201-co.sh66
-rwxr-xr-xt/t7300-clean.sh30
-rwxr-xr-xt/t7301-clean-interactive.sh490
-rwxr-xr-xt/t7400-submodule-basic.sh34
-rwxr-xr-xt/t7402-submodule-rebase.sh2
-rwxr-xr-xt/t7406-submodule-update.sh48
-rwxr-xr-xt/t7423-submodule-symlinks.sh67
-rwxr-xr-xt/t7450-bad-git-dotfiles.sh60
-rwxr-xr-xt/t7501-commit-basic-functionality.sh84
-rwxr-xr-xt/t7502-commit-porcelain.sh23
-rwxr-xr-xt/t7507-commit-verbose.sh10
-rwxr-xr-xt/t7508-status.sh36
-rwxr-xr-xt/t7512-status-help.sh28
-rwxr-xr-xt/t7513-interpret-trailers.sh33
-rwxr-xr-xt/t7514-commit-patch.sh8
-rwxr-xr-xt/t7527-builtin-fsmonitor.sh225
-rwxr-xr-xt/t7700-repack.sh4
-rwxr-xr-xt/t7704-repack-cruft.sh2
-rwxr-xr-xt/t7800-difftool.sh113
-rwxr-xr-xt/t7900-maintenance.sh48
-rwxr-xr-xt/t8010-cat-file-filters.sh2
-rwxr-xr-xt/t8013-blame-ignore-revs.sh28
-rwxr-xr-xt/t9002-column.sh12
-rwxr-xr-xt/t9114-git-svn-dcommit-merge.sh2
-rwxr-xr-xt/t9117-git-svn-init-clone.sh16
-rwxr-xr-xt/t9118-git-svn-funky-branch-names.sh2
-rwxr-xr-xt/t9133-git-svn-nested-git-repo.sh2
-rwxr-xr-xt/t9146-git-svn-empty-dirs.sh56
-rwxr-xr-xt/t9164-git-svn-dcommit-concurrent.sh9
-rwxr-xr-xt/t9210-scalar.sh9
-rwxr-xr-xt/t9300-fast-import.sh644
-rwxr-xr-xt/t9350-fast-export.sh14
-rwxr-xr-xt/t9400-git-cvsserver-server.sh35
-rwxr-xr-xt/t9500-gitweb-standalone-no-errors.sh5
-rwxr-xr-xt/t9604-cvsimport-timestamps.sh29
-rwxr-xr-xt/t9700/test.pl2
-rwxr-xr-xt/t9801-git-p4-branch.sh2
-rwxr-xr-xt/t9802-git-p4-filetype.sh2
-rwxr-xr-xt/t9807-git-p4-submit.sh2
-rwxr-xr-xt/t9816-git-p4-locked.sh2
-rwxr-xr-xt/t9824-git-p4-git-lfs.sh4
-rwxr-xr-xt/t9902-completion.sh263
-rw-r--r--t/test-lib-functions.sh55
-rw-r--r--t/test-lib-github-workflow-markup.sh6
-rw-r--r--t/test-lib-junit.sh2
-rw-r--r--t/test-lib.sh25
-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
-rw-r--r--tempfile.c21
-rw-r--r--tempfile.h2
-rwxr-xr-xtemplates/hooks--pre-commit.sample2
-rw-r--r--tmp-objdir.c1
-rw-r--r--trace.c2
-rw-r--r--trace2.c138
-rw-r--r--trace2.h4
-rw-r--r--trace2/tr2_ctr.c1
-rw-r--r--trace2/tr2_sysenv.c2
-rw-r--r--trace2/tr2_tgt_normal.c1
-rw-r--r--trace2/tr2_tls.c1
-rw-r--r--trace2/tr2_tls.h1
-rw-r--r--trace2/tr2_tmr.c1
-rw-r--r--trailer.c325
-rw-r--r--trailer.h52
-rw-r--r--transport-helper.c47
-rw-r--r--transport.c4
-rw-r--r--tree-walk.c60
-rw-r--r--tree-walk.h7
-rw-r--r--tree.c5
333 files changed, 10690 insertions, 3003 deletions
diff --git a/t/Makefile b/t/Makefile
index 3e00cdd..2d95046 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -1,3 +1,6 @@
+# The default target of this Makefile is...
+all::
+
# Import tree-wide shared Makefile behavior and libraries
include ../shared.mak
@@ -6,6 +9,7 @@ include ../shared.mak
# Copyright (c) 2005 Junio C Hamano
#
+-include ../config.mak.uname
-include ../config.mak.autogen
-include ../config.mak
@@ -17,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
@@ -41,13 +46,16 @@ 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 = '$(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)
$(CHAINLINTSUPPRESS) $(MAKE) aggregate-results-and-cleanup
@@ -65,6 +73,17 @@ prove: pre-clean check-chainlint $(TEST_LINT)
$(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)'
@@ -90,20 +109,12 @@ check-chainlint:
echo "# chainlint: $(CHAINLINTTMP_SQ)/tests" && \
for i in $(CHAINLINTTESTS); do \
echo "# chainlint: $$i" && \
- sed -e '/^[ ]*$$/d' chainlint/$$i.expect; \
+ cat chainlint/$$i.expect; \
done \
} >'$(CHAINLINTTMP_SQ)'/expect && \
$(CHAINLINT) --emit-all '$(CHAINLINTTMP_SQ)'/tests | \
- sed -e 's/^[1-9][0-9]* //;/^[ ]*$$/d' >'$(CHAINLINTTMP_SQ)'/actual && \
- if test -f ../GIT-BUILD-OPTIONS; then \
- . ../GIT-BUILD-OPTIONS; \
- fi && \
- if test -x ../git$$X; then \
- DIFFW="../git$$X --no-pager diff -w --no-index"; \
- else \
- DIFFW="diff -w -u"; \
- fi && \
- $$DIFFW '$(CHAINLINTTMP_SQ)'/expect '$(CHAINLINTTMP_SQ)'/actual
+ 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
@@ -149,4 +160,4 @@ perf:
$(MAKE) -C perf/ all
.PHONY: pre-clean $(T) aggregate-results clean valgrind perf \
- check-chainlint clean-chainlint test-chainlint
+ check-chainlint clean-chainlint test-chainlint $(UNIT_TESTS)
diff --git a/t/README b/t/README
index a0ebe29..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):
@@ -479,6 +486,9 @@ 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_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.
@@ -721,6 +731,26 @@ 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:"
@@ -1278,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/annotate-tests.sh b/t/annotate-tests.sh
index 5e21e84..8757245 100644
--- a/t/annotate-tests.sh
+++ b/t/annotate-tests.sh
@@ -532,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
'
diff --git a/t/chainlint/blank-line-before-esac.expect b/t/chainlint/blank-line-before-esac.expect
index 48ed4eb..056e030 100644
--- a/t/chainlint/blank-line-before-esac.expect
+++ b/t/chainlint/blank-line-before-esac.expect
@@ -1,11 +1,11 @@
-test_done ( ) {
+test_done () {
case "$test_failure" in
- 0 )
+ 0)
test_at_end_hook_
exit 0 ;;
- * )
+ *)
if test $test_external_has_tap -eq 0
then
say_color error "# failed $test_failure among $msg"
@@ -14,5 +14,5 @@ test_done ( ) {
exit 1 ;;
- esac
+ esac
}
diff --git a/t/chainlint/blank-line.expect b/t/chainlint/blank-line.expect
index f76fde1..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/block.expect b/t/chainlint/block.expect
index a3bcea4..1c87326 100644
--- a/t/chainlint/block.expect
+++ b/t/chainlint/block.expect
@@ -12,9 +12,9 @@
) &&
{
- echo a ; ?!AMP?! echo b
+ echo a; ?!AMP?! echo b
} &&
-{ echo a ; ?!AMP?! echo b ; } &&
+{ echo a; ?!AMP?! echo b; } &&
{
echo "${var}9" &&
diff --git a/t/chainlint/chain-break-background.expect b/t/chainlint/chain-break-background.expect
index 28f9114..20d0bb5 100644
--- a/t/chainlint/chain-break-background.expect
+++ b/t/chainlint/chain-break-background.expect
@@ -1,9 +1,9 @@
JGIT_DAEMON_PID= &&
git init --bare empty.git &&
-> empty.git/git-daemon-export-ok &&
+>empty.git/git-daemon-export-ok &&
mkfifo jgit_daemon_output &&
{
- jgit daemon --port="$JGIT_DAEMON_PORT" . > 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-return-exit.expect b/t/chainlint/chain-break-return-exit.expect
index 1732d22..4cd18e2 100644
--- a/t/chainlint/chain-break-return-exit.expect
+++ b/t/chainlint/chain-break-return-exit.expect
@@ -1,16 +1,16 @@
case "$(git ls-files)" in
-one ) echo pass one ;;
-* ) echo bad one ; return 1 ;;
+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
+ 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 ;;
+dir/two"$LF"one) echo pass both ;;
+*) echo bad; return 1 ;;
esac &&
for i in 1 2 3 4 ; do
diff --git a/t/chainlint/chain-break-status.expect b/t/chainlint/chain-break-status.expect
index f4bada9..e6b3b21 100644
--- a/t/chainlint/chain-break-status.expect
+++ b/t/chainlint/chain-break-status.expect
@@ -1,7 +1,7 @@
-OUT=$(( ( large_git ; echo $? 1 >& 3 ) | : ) 3 >& 1) &&
+OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT" &&
-{ test-tool sigchain > actual ; ret=$? ; } &&
+{ test-tool sigchain >actual; ret=$?; } &&
{
test_match_signal 15 "$ret" ||
test "$ret" = 3
diff --git a/t/chainlint/chained-subshell.expect b/t/chainlint/chained-subshell.expect
index af0369d..83810ea 100644
--- a/t/chainlint/chained-subshell.expect
+++ b/t/chainlint/chained-subshell.expect
@@ -4,7 +4,7 @@ mkdir sub && (
nuff said
) &&
-cut "-d " -f actual | ( read s1 s2 s3 &&
+cut "-d " -f actual | (read s1 s2 s3 &&
test -f $s1 ?!AMP?!
test $(cat $s2) = tree2path1 &&
-test $(cat $s3) = tree3path1 )
+test $(cat $s3) = tree3path1)
diff --git a/t/chainlint/command-substitution-subsubshell.expect b/t/chainlint/command-substitution-subsubshell.expect
index ab2f79e..ec42f2c 100644
--- a/t/chainlint/command-substitution-subsubshell.expect
+++ b/t/chainlint/command-substitution-subsubshell.expect
@@ -1,2 +1,2 @@
-OUT=$(( ( large_git 1 >& 3 ) | : ) 3 >& 1) &&
+OUT=$( ((large_git 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT"
diff --git a/t/chainlint/dqstring-line-splice.expect b/t/chainlint/dqstring-line-splice.expect
index bf9ced6..37eab80 100644
--- a/t/chainlint/dqstring-line-splice.expect
+++ b/t/chainlint/dqstring-line-splice.expect
@@ -1,3 +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 &&
+
+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
index 1072498..087eda1 100644
--- a/t/chainlint/dqstring-no-interpolate.expect
+++ b/t/chainlint/dqstring-no-interpolate.expect
@@ -6,6 +6,7 @@ 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 &&
+) >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
index e8733c9..8507721 100644
--- a/t/chainlint/empty-here-doc.expect
+++ b/t/chainlint/empty-here-doc.expect
@@ -1,4 +1,4 @@
-git ls-tree $tree path > current &&
-cat > expected <<\EOF &&
+git ls-tree $tree path >current &&
+cat >expected <<\EOF &&
EOF
test_output
diff --git a/t/chainlint/exclamation.expect b/t/chainlint/exclamation.expect
index 2d961a5..765a35b 100644
--- a/t/chainlint/exclamation.expect
+++ b/t/chainlint/exclamation.expect
@@ -1,4 +1,4 @@
-if ! condition ; then echo nope ; else yep ; fi &&
+if ! condition; then echo nope; else yep; fi &&
test_prerequisite !MINGW &&
mail uucp!address &&
echo !whatever!
diff --git a/t/chainlint/for-loop-abbreviated.expect b/t/chainlint/for-loop-abbreviated.expect
index a21007a..02c0d15 100644
--- a/t/chainlint/for-loop-abbreviated.expect
+++ b/t/chainlint/for-loop-abbreviated.expect
@@ -1,5 +1,5 @@
for it
do
- path=$(expr "$it" : ( [^:]*) ) &&
+ 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 d65c821..d2237f1 100644
--- a/t/chainlint/for-loop.expect
+++ b/t/chainlint/for-loop.expect
@@ -6,6 +6,7 @@
bar
EOF
done ?!AMP?!
+
for i in a b c; do
echo $i &&
cat $i ?!LOOP?!
diff --git a/t/chainlint/function.expect b/t/chainlint/function.expect
index a14388e..dd7c997 100644
--- a/t/chainlint/function.expect
+++ b/t/chainlint/function.expect
@@ -1,8 +1,8 @@
-sha1_file ( ) {
+sha1_file() {
echo "$*" | sed "s#..#.git/objects/&/#"
} &&
-remove_object ( ) {
+remove_object() {
file=$(sha1_file "$*") &&
test -e "$file" ?!AMP?!
rm -f "$file"
diff --git a/t/chainlint/here-doc.expect b/t/chainlint/here-doc.expect
index 1df3f78..91b9612 100644
--- a/t/chainlint/here-doc.expect
+++ b/t/chainlint/here-doc.expect
@@ -1,6 +1,6 @@
boodle wobba \
- gorgo snoot \
- wafta snurb <<EOF &&
+ gorgo snoot \
+ wafta snurb <<EOF &&
quoth the raven,
nevermore...
EOF
diff --git a/t/chainlint/loop-detect-status.expect b/t/chainlint/loop-detect-status.expect
index 24da9e8..7ce3a34 100644
--- a/t/chainlint/loop-detect-status.expect
+++ b/t/chainlint/loop-detect-status.expect
@@ -1,18 +1,18 @@
-( while test $i -le $blobcount
-do
- printf "Generating blob $i/$blobcount\r" >& 2 &&
+(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 ) |
+ 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/nested-cuddled-subshell.expect b/t/chainlint/nested-cuddled-subshell.expect
index 2a86885..3836049 100644
--- a/t/chainlint/nested-cuddled-subshell.expect
+++ b/t/chainlint/nested-cuddled-subshell.expect
@@ -2,18 +2,24 @@
(cd foo &&
bar
) &&
+
(cd foo &&
bar
) ?!AMP?!
+
(
cd foo &&
bar) &&
+
(
cd foo &&
bar) ?!AMP?!
+
(cd foo &&
bar) &&
+
(cd foo &&
bar) ?!AMP?!
+
foobar
)
diff --git a/t/chainlint/nested-loop-detect-failure.expect b/t/chainlint/nested-loop-detect-failure.expect
index 4793a0e..3461df4 100644
--- a/t/chainlint/nested-loop-detect-failure.expect
+++ b/t/chainlint/nested-loop-detect-failure.expect
@@ -1,31 +1,31 @@
-for i in 0 1 2 3 4 5 6 7 8 9 ;
+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 ;
+ for j in 0 1 2 3 4 5 6 7 8 9;
do
- echo "$i$j" > "path$i$j" ?!LOOP?!
+ echo "$i$j" >"path$i$j" ?!LOOP?!
done ?!LOOP?!
done &&
-for i in 0 1 2 3 4 5 6 7 8 9 ;
+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 ;
+ for j in 0 1 2 3 4 5 6 7 8 9;
do
- echo "$i$j" > "path$i$j" || return 1
+ echo "$i$j" >"path$i$j" || return 1
done
done &&
-for i in 0 1 2 3 4 5 6 7 8 9 ;
+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 ;
+ for j in 0 1 2 3 4 5 6 7 8 9;
do
- echo "$i$j" > "path$i$j" ?!LOOP?!
+ echo "$i$j" >"path$i$j" ?!LOOP?!
done || return 1
done &&
-for i in 0 1 2 3 4 5 6 7 8 9 ;
+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 ;
+ for j in 0 1 2 3 4 5 6 7 8 9;
do
- echo "$i$j" > "path$i$j" || return 1
+ echo "$i$j" >"path$i$j" || return 1
done || return 1
done
diff --git a/t/chainlint/nested-subshell.expect b/t/chainlint/nested-subshell.expect
index 02e0a9f..73ff285 100644
--- a/t/chainlint/nested-subshell.expect
+++ b/t/chainlint/nested-subshell.expect
@@ -4,6 +4,7 @@
echo a &&
echo b
) >file &&
+
cd foo &&
(
echo a ?!AMP?!
diff --git a/t/chainlint/pipe.expect b/t/chainlint/pipe.expect
index 2cfc028..811971b 100644
--- a/t/chainlint/pipe.expect
+++ b/t/chainlint/pipe.expect
@@ -2,7 +2,9 @@
foo |
bar |
baz &&
+
fish |
cow ?!AMP?!
+
sunder
)
diff --git a/t/chainlint/subshell-here-doc.expect b/t/chainlint/subshell-here-doc.expect
index 5278927..75d6f60 100644
--- a/t/chainlint/subshell-here-doc.expect
+++ b/t/chainlint/subshell-here-doc.expect
@@ -1,7 +1,7 @@
(
echo wobba \
- gorgo snoot \
- wafta snurb <<-EOF &&
+ gorgo snoot \
+ wafta snurb <<-EOF &&
quoth the raven,
nevermore...
EOF
diff --git a/t/chainlint/subshell-one-liner.expect b/t/chainlint/subshell-one-liner.expect
index b701536..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 &&
+
(foo; ?!AMP?! bar) &&
(foo; ?!AMP?! bar) |
(foo; ?!AMP?! bar) >baz &&
+
(foo || exit 1) &&
(foo || exit 1) |
(foo || exit 1) >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 71b3b3b..02f3129 100644
--- a/t/chainlint/t7900-subtree.expect
+++ b/t/chainlint/t7900-subtree.expect
@@ -15,6 +15,7 @@ main-sub4" &&
$chkms
TXT
) &&
+
subfiles=$(git ls-files) &&
check_equal "$subfiles" "$chkms
$chks"
diff --git a/t/chainlint/token-pasting.expect b/t/chainlint/token-pasting.expect
index 342360b..6a38791 100644
--- a/t/chainlint/token-pasting.expect
+++ b/t/chainlint/token-pasting.expect
@@ -4,22 +4,22 @@ git config filter.rot13.clean ./rot13.sh &&
{
echo "*.t filter=rot13" ?!AMP?!
echo "*.i ident"
-} > .gitattributes &&
+} >.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 &&
+} >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?!
+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" |
diff --git a/t/chainlint/while-loop.expect b/t/chainlint/while-loop.expect
index 1f5eaea..06c1567 100644
--- a/t/chainlint/while-loop.expect
+++ b/t/chainlint/while-loop.expect
@@ -6,6 +6,7 @@
bar
EOF
done ?!AMP?!
+
while true; do
echo foo &&
cat bar ?!LOOP?!
diff --git a/t/check-non-portable-shell.pl b/t/check-non-portable-shell.pl
index dd8107c..b2b28c2 100755
--- a/t/check-non-portable-shell.pl
+++ b/t/check-non-portable-shell.pl
@@ -47,6 +47,8 @@ while (<>) {
/\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-bundle-uri.c b/t/helper/test-bundle-uri.c
index 4750585..09dc787 100644
--- a/t/helper/test-bundle-uri.c
+++ b/t/helper/test-bundle-uri.c
@@ -5,9 +5,7 @@
#include "strbuf.h"
#include "string-list.h"
#include "transport.h"
-#include "ref-filter.h"
#include "remote.h"
-#include "refs.h"
enum input_mode {
KEY_VALUE_PAIRS,
diff --git a/t/helper/test-ctype.c b/t/helper/test-ctype.c
deleted file mode 100644
index e5659df..0000000
--- a/t/helper/test-ctype.c
+++ /dev/null
@@ -1,70 +0,0 @@
-#include "test-tool.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. Accept it as the first
- * character in the spec -- there are no empty classes.
- */
- if (ch == '\0')
- return ch == *s;
- if (*s == '\0')
- s++;
- 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); \
- } \
- if (t(EOF)) \
- report_error(#t, EOF); \
-}
-
-#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__ctype(int argc UNUSED, const char **argv UNUSED)
-{
- 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, "!\"#%&',-/:;<=>@_`~");
- TEST_CLASS(isascii, ASCII);
- TEST_CLASS(islower, LOWER);
- TEST_CLASS(isupper, UPPER);
- TEST_CLASS(iscntrl, CNTRL);
- TEST_CLASS(ispunct, PUNCT);
- TEST_CLASS(isxdigit, DIGIT "abcdefABCDEF");
- TEST_CLASS(isprint, LOWER UPPER DIGIT PUNCT " ");
-
- return rc;
-}
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 0683d46..f25512d 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -52,7 +52,7 @@ 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);
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-fast-rebase.c b/t/helper/test-fast-rebase.c
deleted file mode 100644
index cac20a7..0000000
--- a/t/helper/test-fast-rebase.c
+++ /dev/null
@@ -1,241 +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_VARIABLE
-#include "test-tool.h"
-#include "cache-tree.h"
-#include "commit.h"
-#include "environment.h"
-#include "gettext.h"
-#include "hash.h"
-#include "hex.h"
-#include "lockfile.h"
-#include "merge-ort.h"
-#include "object-name.h"
-#include "read-cache-ll.h"
-#include "refs.h"
-#include "revision.h"
-#include "sequencer.h"
-#include "setup.h"
-#include "strvec.h"
-#include "tree.h"
-
-static const char *short_commit_name(struct commit *commit)
-{
- return repo_find_unique_abbrev(the_repository, &commit->object.oid,
- DEFAULT_ABBREV);
-}
-
-static struct commit *peel_committish(const char *name)
-{
- struct object *obj;
- struct object_id oid;
-
- if (repo_get_oid(the_repository, name, &oid))
- return NULL;
- obj = parse_object(the_repository, &oid);
- return (struct commit *)repo_peel_to_type(the_repository, 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 = repo_logmsg_reencode(the_repository, 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;
- int ret = 0;
-
- /*
- * 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 (repo_get_oid(the_repository, "HEAD", &head))
- die(_("Cannot read HEAD"));
- assert(oideq(&onto->object.oid, &head));
-
- repo_hold_locked_index(the_repository, &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) {
- ret = error(_("unhandled options"));
- goto cleanup;
- }
-
- strvec_clear(&rev_walk_args);
-
- if (prepare_revision_walk(&revs) < 0) {
- ret = error(_("error preparing revisions"));
- goto cleanup;
- }
-
- init_merge_options(&merge_opt, the_repository);
- memset(&result, 0, sizeof(result));
- merge_opt.show_rename_progress = 1;
- merge_opt.branch1 = "HEAD";
- head_tree = repo_get_commit_tree(the_repository, 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 = repo_get_commit_tree(the_repository, commit);
- base_tree = repo_get_commit_tree(the_repository, 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);
- }
-
- 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"));
-
- 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());
-
- ret = (result.clean == 0);
-cleanup:
- strbuf_release(&reflog_msg);
- strbuf_release(&branch_name);
- release_revisions(&revs);
- return ret;
-}
diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c
index 70396fa..023ed2e 100644
--- a/t/helper/test-path-utils.c
+++ b/t/helper/test-path-utils.c
@@ -7,6 +7,7 @@
#include "string-list.h"
#include "trace.h"
#include "utf8.h"
+#include "copy.h"
/*
* A "string_list_each_func_t" function that normalizes an entry from
@@ -500,6 +501,16 @@ int cmd__path_utils(int argc, const char **argv)
return !!res;
}
+ if (argc == 4 && !strcmp(argv[1], "do_files_match")) {
+ int ret = do_files_match(argv[2], argv[3]);
+
+ if (ret)
+ printf("equal\n");
+ else
+ printf("different\n");
+ return !ret;
+ }
+
fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
argv[1] ? argv[1] : "(there was none)");
return 1;
diff --git a/t/helper/test-pkt-line.c b/t/helper/test-pkt-line.c
index f4d134a..4daa82f 100644
--- a/t/helper/test-pkt-line.c
+++ b/t/helper/test-pkt-line.c
@@ -1,7 +1,9 @@
#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)
{
@@ -64,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;
@@ -79,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 */
@@ -97,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;
}
@@ -126,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 f0bf255..0000000
--- a/t/helper/test-prio-queue.c
+++ /dev/null
@@ -1,51 +0,0 @@
-#include "test-tool.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;
-}
-
-static void show(int *v)
-{
- if (!v)
- printf("NULL\n");
- else
- printf("%d\n", *v);
- free(v);
-}
-
-int cmd__prio_queue(int argc UNUSED, 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);
- }
- }
-
- clear_prio_queue(&pq);
-
- return 0;
-}
diff --git a/t/helper/test-reach.c b/t/helper/test-reach.c
index 3e17339..1e3b431 100644
--- a/t/helper/test-reach.c
+++ b/t/helper/test-reach.c
@@ -1,11 +1,9 @@
#include "test-tool.h"
#include "commit.h"
#include "commit-reach.h"
-#include "config.h"
#include "gettext.h"
#include "hex.h"
#include "object-name.h"
-#include "parse-options.h"
#include "ref-filter.h"
#include "setup.h"
#include "string-list.h"
@@ -113,13 +111,16 @@ int cmd__reach(int ac, const char **av)
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],
- repo_in_merge_bases_many(the_repository, A, X_nr, X_array));
+ 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 = repo_get_merge_bases_many(the_repository,
- 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")) {
diff --git a/t/helper/test-read-midx.c b/t/helper/test-read-midx.c
index e9a444d..4acae41 100644
--- a/t/helper/test-read-midx.c
+++ b/t/helper/test-read-midx.c
@@ -6,6 +6,7 @@
#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)
{
@@ -79,7 +80,7 @@ static int read_midx_checksum(const char *object_dir)
static int read_midx_preferred_pack(const char *object_dir)
{
struct multi_pack_index *midx = NULL;
- struct bitmap_index *bitmap = NULL;
+ uint32_t preferred_pack;
setup_git_directory();
@@ -87,23 +88,45 @@ static int read_midx_preferred_pack(const char *object_dir)
if (!midx)
return 1;
- bitmap = prepare_bitmap_git(the_repository);
- if (!bitmap)
+ if (midx_preferred_pack(midx, &preferred_pack) < 0) {
+ warning(_("could not determine MIDX preferred pack"));
return 1;
- if (!bitmap_is_midx(bitmap)) {
- free_bitmap_index(bitmap);
+ }
+
+ 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);
}
- printf("%s\n", midx->pack_names[midx_preferred_pack(bitmap)]);
- free_bitmap_index(bitmap);
+ close_midx(midx);
+
return 0;
}
int cmd__read_midx(int argc, const char **argv)
{
if (!(argc == 2 || argc == 3))
- usage("read-midx [--show-objects|--checksum|--preferred-pack] <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);
@@ -111,5 +134,7 @@ int cmd__read_midx(int argc, const char **argv)
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 48552e6..82bbf6e 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -112,25 +112,6 @@ static const char **get_store(const char **argv, struct ref_store **refs)
return argv + 1;
}
-static struct flag_definition pack_flags[] = { FLAG_DEF(PACK_REFS_PRUNE),
- FLAG_DEF(PACK_REFS_ALL),
- { NULL, 0 } };
-
-static int cmd_pack_refs(struct ref_store *refs, const char **argv)
-{
- unsigned int flags = arg_flags(*argv++, "flags", pack_flags);
- static struct ref_exclusions exclusions = REF_EXCLUSIONS_INIT;
- static struct string_list included_refs = STRING_LIST_INIT_NODUP;
- struct pack_refs_opts pack_opts = { .flags = flags,
- .exclusions = &exclusions,
- .includes = &included_refs };
-
- if (pack_opts.flags & PACK_REFS_ALL)
- string_list_append(pack_opts.includes, "*");
-
- return refs_pack_refs(refs, &pack_opts);
-}
-
static int cmd_create_symref(struct ref_store *refs, const char **argv)
{
const char *refname = notnull(*argv++, "refname");
@@ -221,15 +202,21 @@ static int cmd_verify_ref(struct ref_store *refs, const char **argv)
return ret;
}
+static int each_reflog(const char *refname, void *cb_data UNUSED)
+{
+ printf("%s\n", refname);
+ return 0;
+}
+
static int cmd_for_each_reflog(struct ref_store *refs,
const char **argv UNUSED)
{
- return refs_for_each_reflog(refs, each_ref, NULL);
+ return refs_for_each_reflog(refs, each_reflog, NULL);
}
-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 UNUSED)
+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,
@@ -241,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)
@@ -298,16 +285,19 @@ static int cmd_update_ref(struct ref_store *refs, const char **argv)
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", transaction_flags);
- struct object_id old_oid;
+ struct object_id old_oid, *old_oid_ptr = NULL;
struct object_id new_oid;
- if (get_oid_hex(old_sha1_buf, &old_oid))
- die("cannot parse %s as %s", old_sha1_buf, the_hash_algo->name);
+ 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);
}
@@ -317,7 +307,6 @@ 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 },
diff --git a/t/helper/test-regex.c b/t/helper/test-regex.c
index bd871a7..80042ea 100644
--- a/t/helper/test-regex.c
+++ b/t/helper/test-regex.c
@@ -30,7 +30,7 @@ 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");
diff --git a/t/helper/test-repository.c b/t/helper/test-repository.c
index 4cd8a95..0c7c5aa 100644
--- a/t/helper/test-repository.c
+++ b/t/helper/test-repository.c
@@ -1,10 +1,8 @@
#include "test-tool.h"
#include "commit-graph.h"
#include "commit.h"
-#include "config.h"
#include "environment.h"
#include "hex.h"
-#include "object-store-ll.h"
#include "object.h"
#include "repository.h"
#include "setup.h"
diff --git a/t/helper/test-simple-ipc.c b/t/helper/test-simple-ipc.c
index 941ae7e..fb59277 100644
--- a/t/helper/test-simple-ipc.c
+++ b/t/helper/test-simple-ipc.c
@@ -4,7 +4,6 @@
#include "test-tool.h"
#include "gettext.h"
-#include "strbuf.h"
#include "simple-ipc.h"
#include "parse-options.h"
#include "thread-utils.h"
diff --git a/t/helper/test-submodule.c b/t/helper/test-submodule.c
index 356e0a2..7197969 100644
--- a/t/helper/test-submodule.c
+++ b/t/helper/test-submodule.c
@@ -4,16 +4,24 @@
#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 <name>"
+ "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[] = {
@@ -30,31 +38,26 @@ static const char *submodule_resolve_relative_url_usage[] = {
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 *);
+
/*
- * Exit non-zero if any of the submodule names given on the command line is
- * invalid. If no names are given, filter stdin to print only valid names
- * (which is primarily intended for testing).
+ * Apply 'check_fn' to each line of stdin, printing values that pass the check
+ * to stdout.
*/
-static int check_name(int argc, const char **argv)
+static int check_submodule(check_fn_t check_fn)
{
- if (argc > 1) {
- while (*++argv) {
- if (check_submodule_name(*argv) < 0)
- return 1;
- }
- } else {
- struct strbuf buf = STRBUF_INIT;
- while (strbuf_getline(&buf, stdin) != EOF) {
- if (!check_submodule_name(buf.buf))
- printf("%s\n", buf.buf);
- }
- strbuf_release(&buf);
+ 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;
}
@@ -68,7 +71,20 @@ static int cmd__submodule_check_name(int argc, const char **argv)
if (argc)
usage_with_options(submodule_check_name_usage, options);
- return check_name(argc, argv);
+ 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)
@@ -194,6 +210,7 @@ static int cmd__submodule_config_writeable(int argc, const char **argv UNUSED)
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 },
diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c
index 876cd2d..80a946b 100644
--- a/t/helper/test-tool.c
+++ b/t/helper/test-tool.c
@@ -19,8 +19,8 @@ static struct test_cmd cmds[] = {
{ "config", cmd__config },
{ "crontab", cmd__crontab },
{ "csprng", cmd__csprng },
- { "ctype", cmd__ctype },
{ "date", cmd__date },
+ { "delete-gpgsig", cmd__delete_gpgsig },
{ "delta", cmd__delta },
{ "dir-iterator", cmd__dir_iterator },
{ "drop-caches", cmd__drop_caches },
@@ -30,7 +30,6 @@ static struct test_cmd cmds[] = {
{ "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 },
@@ -57,7 +56,6 @@ static struct test_cmd cmds[] = {
{ "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 },
{ "progress", cmd__progress },
{ "reach", cmd__reach },
diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h
index 70dd4eb..2808b92 100644
--- a/t/helper/test-tool.h
+++ b/t/helper/test-tool.h
@@ -12,9 +12,9 @@ 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__csprng(int argc, const char **argv);
-int cmd__ctype(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);
@@ -24,7 +24,6 @@ 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);
@@ -50,7 +49,6 @@ 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);
diff --git a/t/helper/test-trace2.c b/t/helper/test-trace2.c
index d5ca004..1adac29 100644
--- a/t/helper/test-trace2.c
+++ b/t/helper/test-trace2.c
@@ -412,6 +412,56 @@ static int ut_201counter(int argc, const char **argv)
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>
@@ -438,6 +488,11 @@ static struct unit_test ut_table[] = {
{ 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 */
diff --git a/t/lib-credential.sh b/t/lib-credential.sh
index 15fc9a3..44799c0 100644
--- a/t/lib-credential.sh
+++ b/t/lib-credential.sh
@@ -50,6 +50,7 @@ helper_test_clean() {
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
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-gpg.sh b/t/lib-gpg.sh
index 83b83c9..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
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index 5fe3c8a..d83bafe 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -55,21 +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/libexec/httpd'
+ '/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
@@ -127,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"
}
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
index 77c2513..2ad7705 100644
--- a/t/lib-httpd/proxy-passwd
+++ b/t/lib-httpd/proxy-passwd
@@ -1 +1 @@
-proxuser:2x7tAukjAED5M
+proxuser:$apr1$RxS6MLkD$DYsqQdflheq4GPNxzJpx5.
diff --git a/t/lib-parallel-checkout.sh b/t/lib-parallel-checkout.sh
index acaee9c..8324d6c 100644
--- a/t/lib-parallel-checkout.sh
+++ b/t/lib-parallel-checkout.sh
@@ -20,7 +20,7 @@ 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 &&
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/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/perf-lib.sh b/t/perf/perf-lib.sh
index e778677..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.
@@ -31,7 +31,7 @@ unset GIT_CONFIG_NOSYSTEM
GIT_CONFIG_SYSTEM="$TEST_DIRECTORY/perf/config"
export GIT_CONFIG_SYSTEM
-if test -n "$GIT_TEST_INSTALLED" -a -z "$PERF_SET_GIT_TEST_INSTALLED"
+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.
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 34115ed..486ead2 100755
--- a/t/perf/run
+++ b/t/perf/run
@@ -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
@@ -180,7 +180,8 @@ run_subsection () {
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
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 6e300be..98b81e4 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -1201,6 +1201,34 @@ test_expect_success 'very long name in the index handled sanely' '
test $len = 4098
'
+# D/F conflict checking uses an optimization when adding to the end.
+# make sure it does not get confused by `a-` sorting _between_
+# `a` and `a/`.
+test_expect_success 'more update-index D/F conflicts' '
+ # empty the index to make sure our entry is last
+ git read-tree --empty &&
+ cacheinfo=100644,$(test_oid empty_blob) &&
+ git update-index --add --cacheinfo $cacheinfo,path5/a &&
+
+ test_must_fail git update-index --add --cacheinfo $cacheinfo,path5/a/file &&
+ test_must_fail git update-index --add --cacheinfo $cacheinfo,path5/a/b/file &&
+ test_must_fail git update-index --add --cacheinfo $cacheinfo,path5/a/b/c/file &&
+
+ # "a-" sorts between "a" and "a/"
+ git update-index --add --cacheinfo $cacheinfo,path5/a- &&
+
+ test_must_fail git update-index --add --cacheinfo $cacheinfo,path5/a/file &&
+ test_must_fail git update-index --add --cacheinfo $cacheinfo,path5/a/b/file &&
+ test_must_fail git update-index --add --cacheinfo $cacheinfo,path5/a/b/c/file &&
+
+ cat >expected <<-\EOF &&
+ path5/a
+ path5/a-
+ EOF
+ git ls-files >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'test_must_fail on a failing git command' '
test_must_fail git notacommand
'
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index 2b78e3b..b131d66 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -532,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 &&
diff --git a/t/t0002-gitfile.sh b/t/t0002-gitfile.sh
index 736516c..bf3bf60 100755
--- a/t/t0002-gitfile.sh
+++ b/t/t0002-gitfile.sh
@@ -40,7 +40,7 @@ test_expect_success 'final setup + check rev-parse --git-dir' '
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)"
'
diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh
index aee2298..774b52c 100755
--- a/t/t0003-attributes.sh
+++ b/t/t0003-attributes.sh
@@ -19,6 +19,20 @@ 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" &&
@@ -558,4 +572,66 @@ test_expect_success EXPENSIVE 'large attributes file ignored in index' '
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/t0006-date.sh b/t/t0006-date.sh
index e18b160..3031256 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -46,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'
@@ -69,6 +70,14 @@ 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
diff --git a/t/t0009-prio-queue.sh b/t/t0009-prio-queue.sh
deleted file mode 100755
index eea9910..0000000
--- a/t/t0009-prio-queue.sh
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/bin/sh
-
-test_description='basic tests for priority queue implementation'
-
-TEST_PASSES_SANITIZE_LEAK=true
-. ./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 837c8b7..84172a3 100755
--- a/t/t0010-racy-git.sh
+++ b/t/t0010-racy-git.sh
@@ -10,25 +10,24 @@ TEST_PASSES_SANITIZE_LEAK=true
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 1cb6aa6..46e74ad 100755
--- a/t/t0011-hashmap.sh
+++ b/t/t0011-hashmap.sh
@@ -239,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/t0018-advice.sh b/t/t0018-advice.sh
index c13057a..0dcfb76 100755
--- a/t/t0018-advice.sh
+++ b/t/t0018-advice.sh
@@ -17,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/t0024-crlf-archive.sh b/t/t0024-crlf-archive.sh
index a34de56..a7f4de4 100755
--- a/t/t0024-crlf-archive.sh
+++ b/t/t0024-crlf-archive.sh
@@ -9,7 +9,7 @@ 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 &&
@@ -19,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
@@ -30,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/t0028-working-tree-encoding.sh b/t/t0028-working-tree-encoding.sh
index 1b55f59..ad151a3 100755
--- a/t/t0028-working-tree-encoding.sh
+++ b/t/t0028-working-tree-encoding.sh
@@ -131,8 +131,8 @@ do
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 &&
diff --git a/t/t0030-stripspace.sh b/t/t0030-stripspace.sh
index d1b3be8..f10f42f 100755
--- a/t/t0030-stripspace.sh
+++ b/t/t0030-stripspace.sh
@@ -401,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/t0033-safe-directory.sh b/t/t0033-safe-directory.sh
index dc34968..11c3e8f 100755
--- a/t/t0033-safe-directory.sh
+++ b/t/t0033-safe-directory.sh
@@ -80,4 +80,28 @@ test_expect_success 'safe.directory in included file' '
git status
'
+test_expect_success 'local clone of unowned repo refused in unsafe directory' '
+ test_when_finished "rm -rf source" &&
+ git init source &&
+ (
+ sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
+ test_commit -C source initial
+ ) &&
+ test_must_fail git clone --local source target &&
+ test_path_is_missing target
+'
+
+test_expect_success 'local clone of unowned repo accepted in safe directory' '
+ test_when_finished "rm -rf source" &&
+ git init source &&
+ (
+ sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
+ test_commit -C source initial
+ ) &&
+ test_must_fail git clone --local source target &&
+ git config --global --add safe.directory "$(pwd)/source/.git" &&
+ git clone --local source target &&
+ test_path_is_dir target
+'
+
test_done
diff --git a/t/t0035-safe-bare-repository.sh b/t/t0035-safe-bare-repository.sh
index 038b8b7..d3cb2a1 100755
--- a/t/t0035-safe-bare-repository.sh
+++ b/t/t0035-safe-bare-repository.sh
@@ -29,9 +29,20 @@ expect_rejected () {
grep -F "implicit-bare-repository:$pwd" "$pwd/trace.perf"
}
-test_expect_success 'setup bare repo in worktree' '
+test_expect_success 'setup an embedded bare repo, secondary worktree and submodule' '
git init outer-repo &&
- git init --bare outer-repo/bare-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' '
@@ -53,8 +64,7 @@ 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 -C outer-repo/bare-repo safe.bareRepository all &&
test_config_global safe.bareRepository explicit &&
expect_rejected -C outer-repo/bare-repo
'
@@ -78,4 +88,20 @@ 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 8fdef88..8bb2a8b 100755
--- a/t/t0040-parse-options.sh
+++ b/t/t0040-parse-options.sh
@@ -210,6 +210,22 @@ test_expect_success 'superfluous value provided: boolean' '
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
@@ -376,7 +392,7 @@ test_expect_success 'OPT_CMDMODE() detects incompatibility (1)' '
test_must_be_empty output &&
test_grep "mode1" output.err &&
test_grep "mode2" output.err &&
- test_grep "is incompatible with" output.err
+ test_grep "cannot be used together" output.err
'
test_expect_success 'OPT_CMDMODE() detects incompatibility (2)' '
@@ -384,7 +400,7 @@ test_expect_success 'OPT_CMDMODE() detects incompatibility (2)' '
test_must_be_empty output &&
test_grep "mode2" output.err &&
test_grep "set23" output.err &&
- test_grep "is incompatible with" output.err
+ test_grep "cannot be used together" output.err
'
test_expect_success 'OPT_CMDMODE() detects incompatibility (3)' '
@@ -392,7 +408,7 @@ test_expect_success 'OPT_CMDMODE() detects incompatibility (3)' '
test_must_be_empty output &&
test_grep "mode2" output.err &&
test_grep "set23" output.err &&
- test_grep "is incompatible with" output.err
+ test_grep "cannot be used together" output.err
'
test_expect_success 'OPT_CMDMODE() detects incompatibility (4)' '
@@ -401,7 +417,7 @@ test_expect_success 'OPT_CMDMODE() detects incompatibility (4)' '
test_must_be_empty output &&
test_grep "mode2" output.err &&
test_grep "mode34.3" output.err &&
- test_grep "is incompatible with" output.err
+ test_grep "cannot be used together" output.err
'
test_expect_success 'OPT_COUNTUP() with PARSE_OPT_NODASH works' '
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 0afa3d0..85686ee 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -610,4 +610,45 @@ test_expect_success !VALGRIND,RUNTIME_PREFIX,CAN_EXEC_IN_PWD '%(prefix)/ works'
test_cmp expect actual
'
+test_expect_success 'do_files_match()' '
+ test_seq 0 10 >0-10.txt &&
+ test_seq -1 10 >-1-10.txt &&
+ test_seq 1 10 >1-10.txt &&
+ test_seq 1 9 >1-9.txt &&
+ test_seq 0 8 >0-8.txt &&
+
+ test-tool path-utils do_files_match 0-10.txt 0-10.txt >out &&
+
+ assert_fails() {
+ test_must_fail \
+ test-tool path-utils do_files_match "$1" "$2" >out &&
+ grep different out
+ } &&
+
+ assert_fails 0-8.txt 1-9.txt &&
+ assert_fails -1-10.txt 0-10.txt &&
+ assert_fails 1-10.txt 1-9.txt &&
+ assert_fails 1-10.txt .git &&
+ assert_fails does-not-exist 1-10.txt &&
+
+ if test_have_prereq FILEMODE
+ then
+ cp 0-10.txt 0-10.x &&
+ chmod a+x 0-10.x &&
+ assert_fails 0-10.txt 0-10.x
+ fi &&
+
+ if test_have_prereq SYMLINKS
+ then
+ ln -sf 0-10.txt symlink &&
+ ln -s 0-10.txt another-symlink &&
+ ln -s over-the-ocean yet-another-symlink &&
+ ln -s "$PWD/0-10.txt" absolute-symlink &&
+ assert_fails 0-10.txt symlink &&
+ test-tool path-utils do_files_match symlink another-symlink &&
+ assert_fails symlink yet-another-symlink &&
+ assert_fails symlink absolute-symlink
+ fi
+'
+
test_done
diff --git a/t/t0070-fundamental.sh b/t/t0070-fundamental.sh
index 487bc8d..0ecec2b 100755
--- a/t/t0070-fundamental.sh
+++ b/t/t0070-fundamental.sh
@@ -9,10 +9,6 @@ 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
@@ -53,4 +49,62 @@ test_expect_success 'missing sideband designator is reported' '
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/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/t0091-bugreport.sh b/t/t0091-bugreport.sh
index 8798fee..fca3904 100755
--- a/t/t0091-bugreport.sh
+++ b/t/t0091-bugreport.sh
@@ -39,9 +39,9 @@ test_expect_success 'sanity check "System Info" section' '
sed -ne "/^\[System Info\]$/,/^$/p" <git-bugreport-format.txt >system &&
- # The beginning should match "git version --build-info" verbatim,
+ # The beginning should match "git version --build-options" verbatim,
# but rather than checking bit-for-bit equality, just test some basics.
- grep "git version [0-9]." system &&
+ grep "git version " system &&
grep "shell-path: ." system &&
# After the version, there should be some more info.
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/t0204-gettext-reencode-sanity.sh b/t/t0204-gettext-reencode-sanity.sh
index 4f2e0dc..310a450 100755
--- a/t/t0204-gettext-reencode-sanity.sh
+++ b/t/t0204-gettext-reencode-sanity.sh
@@ -82,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 80e76a4..c312657 100755
--- a/t/t0210-trace2-normal.sh
+++ b/t/t0210-trace2-normal.sh
@@ -2,7 +2,7 @@
test_description='test trace2 facility (normal target)'
-TEST_PASSES_SANITIZE_LEAK=true
+TEST_PASSES_SANITIZE_LEAK=false
. ./test-lib.sh
# Turn off any inherited trace2 settings for this test.
@@ -283,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 cfba686..13ef69b 100755
--- a/t/t0211-trace2-perf.sh
+++ b/t/t0211-trace2-perf.sh
@@ -2,7 +2,7 @@
test_description='test trace2 facility (perf target)'
-TEST_PASSES_SANITIZE_LEAK=true
+TEST_PASSES_SANITIZE_LEAK=false
. ./test-lib.sh
# Turn off any inherited trace2 settings for this test.
@@ -268,4 +268,254 @@ test_expect_success PTHREADS 'global counter test/test2' '
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/t0212-trace2-event.sh b/t/t0212-trace2-event.sh
index 6d3374f..147643d 100755
--- a/t/t0212-trace2-event.sh
+++ b/t/t0212-trace2-event.sh
@@ -323,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/t0301-credential-cache.sh b/t/t0301-credential-cache.sh
index 8300faa..f2c146f 100755
--- a/t/t0301-credential-cache.sh
+++ b/t/t0301-credential-cache.sh
@@ -8,6 +8,14 @@ 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
diff --git a/t/t0303-credential-external.sh b/t/t0303-credential-external.sh
index 095574b..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" ||
@@ -59,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 5b7bee8..88a66f0 100755
--- a/t/t0410-partial-clone.sh
+++ b/t/t0410-partial-clone.sh
@@ -49,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 &&
@@ -60,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 &&
@@ -665,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/t0411-clone-from-partial.sh b/t/t0411-clone-from-partial.sh
new file mode 100755
index 0000000..c98d501
--- /dev/null
+++ b/t/t0411-clone-from-partial.sh
@@ -0,0 +1,78 @@
+#!/bin/sh
+
+test_description='check that local clone does not fetch from promisor remotes'
+
+. ./test-lib.sh
+
+test_expect_success 'create evil repo' '
+ git init tmp &&
+ test_commit -C tmp a &&
+ git -C tmp config uploadpack.allowfilter 1 &&
+ git clone --filter=blob:none --no-local --no-checkout tmp evil &&
+ rm -rf tmp &&
+
+ git -C evil config remote.origin.uploadpack \"\$TRASH_DIRECTORY/fake-upload-pack\" &&
+ write_script fake-upload-pack <<-\EOF &&
+ echo >&2 "fake-upload-pack running"
+ >"$TRASH_DIRECTORY/script-executed"
+ exit 1
+ EOF
+ export TRASH_DIRECTORY &&
+
+ # empty shallow file disables local clone optimization
+ >evil/.git/shallow
+'
+
+test_expect_success 'local clone must not fetch from promisor remote and execute script' '
+ rm -f script-executed &&
+ test_must_fail git clone \
+ --upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \
+ evil clone1 2>err &&
+ test_grep "detected dubious ownership" err &&
+ test_grep ! "fake-upload-pack running" err &&
+ test_path_is_missing script-executed
+'
+
+test_expect_success 'clone from file://... must not fetch from promisor remote and execute script' '
+ rm -f script-executed &&
+ test_must_fail git clone \
+ --upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \
+ "file://$(pwd)/evil" clone2 2>err &&
+ test_grep "detected dubious ownership" err &&
+ test_grep ! "fake-upload-pack running" err &&
+ test_path_is_missing script-executed
+'
+
+test_expect_success 'fetch from file://... must not fetch from promisor remote and execute script' '
+ rm -f script-executed &&
+ test_must_fail git fetch \
+ --upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \
+ "file://$(pwd)/evil" 2>err &&
+ test_grep "detected dubious ownership" err &&
+ test_grep ! "fake-upload-pack running" err &&
+ test_path_is_missing script-executed
+'
+
+test_expect_success 'pack-objects should fetch from promisor remote and execute script' '
+ rm -f script-executed &&
+ echo "HEAD" | test_must_fail git -C evil pack-objects --revs --stdout >/dev/null 2>err &&
+ test_grep "fake-upload-pack running" err &&
+ test_path_is_file script-executed
+'
+
+test_expect_success 'clone from promisor remote does not lazy-fetch by default' '
+ rm -f script-executed &&
+ test_must_fail git clone evil no-lazy 2>err &&
+ test_grep "lazy fetching disabled" err &&
+ test_path_is_missing script-executed
+'
+
+test_expect_success 'promisor lazy-fetching can be re-enabled' '
+ rm -f script-executed &&
+ test_must_fail env GIT_NO_LAZY_FETCH=0 \
+ git clone evil lazy-ok 2>err &&
+ test_grep "fake-upload-pack running" err &&
+ test_path_is_file script-executed
+'
+
+test_done
diff --git a/t/t0450-txt-doc-vs-help.sh b/t/t0450-txt-doc-vs-help.sh
index cd3969e..69917d7 100755
--- a/t/t0450-txt-doc-vs-help.sh
+++ b/t/t0450-txt-doc-vs-help.sh
@@ -59,7 +59,9 @@ txt_to_synopsis () {
-e '/^\[verse\]$/,/^$/ {
/^$/d;
/^\[verse\]$/d;
-
+ s/_//g;
+ s/++//g;
+ s/`//g;
s/{litdd}/--/g;
s/'\''\(git[ a-z-]*\)'\''/\1/g;
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/t3210-pack-refs.sh b/t/t0601-reffiles-pack-refs.sh
index 7f4e98d..7d4ab0b 100755
--- a/t/t3210-pack-refs.sh
+++ b/t/t0601-reffiles-pack-refs.sh
@@ -15,6 +15,12 @@ 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
'
@@ -26,6 +32,19 @@ test_expect_success 'prepare a trivial repository' '
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' '
@@ -145,6 +164,13 @@ test_expect_success 'test --exclude takes precedence over --include' '
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 &&
@@ -294,4 +320,64 @@ test_expect_success SYMLINKS 'pack symlinked 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/t1006-cat-file.sh b/t/t1006-cat-file.sh
index d73a0be..e12b221 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -6,7 +6,7 @@ test_description='git cat-file'
test_cmdmode_usage () {
test_expect_code 129 "$@" 2>err &&
- grep "^error:.*is incompatible with" err
+ grep "^error: .* cannot be used together" err
}
for switches in \
@@ -112,65 +112,65 @@ strlen () {
run_tests () {
type=$1
- sha1=$2
+ oid=$2
size=$3
content=$4
pretty_content=$5
- 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" '
echo_without_newline "$content" >expect &&
- git cat-file $type $sha1 >actual &&
+ git cat-file $type $oid >actual &&
test_cmp expect actual
'
test_expect_success "Pretty content of $type is correct" '
echo_without_newline "$pretty_content" >expect &&
- git cat-file -p $sha1 >actual &&
+ git cat-file -p $oid >actual &&
test_cmp expect actual
'
test -z "$content" ||
test_expect_success "--batch output of $type is correct" '
echo "$batch_output" >expect &&
- echo $sha1 | git cat-file --batch >actual &&
+ 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
'
@@ -179,33 +179,33 @@ $content"
test -z "$content" ||
test_expect_success "--batch-command $opt output of $type content is correct" '
echo "$batch_output" >expect &&
- test_write_lines "contents $sha1" | git cat-file --batch-command $opt >actual &&
+ 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 "$sha1 $type $size" >expect &&
- test_write_lines "info $sha1" |
+ 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 $sha1" >expect &&
- echo "info $sha1" | git cat-file --batch-command="%(objecttype) %(objectname)" >actual &&
+ 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
'
@@ -216,7 +216,7 @@ $content"
echo "$size" &&
echo "$content"
} >expect &&
- echo $sha1 | git cat-file --batch="%(objectsize)" >actual &&
+ echo $oid | git cat-file --batch="%(objectsize)" >actual &&
test_cmp expect actual
'
@@ -226,114 +226,154 @@ $content"
echo "$type" &&
echo "$content"
} >expect &&
- echo $sha1 | git cat-file --batch="%(objecttype)" >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
-test_expect_success '--batch-command --buffer with flush for blob info' '
- echo "$hello_sha1 blob $hello_size" >expect &&
- test_write_lines "info $hello_sha1" "flush" |
+ 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' '
+ test_expect_success '--batch-command --buffer without flush for blob info' '
touch output &&
- test_write_lines "info $hello_sha1" |
+ 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${LF}"
+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
+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"
+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 0 +0000
$tag_description"
+tag_compat_content="$tag_compat_header_without_timestamp 0 +0000
-tag_sha1=$(echo_without_newline "$tag_content" | git hash-object -t tag --stdin -w)
+$tag_description"
+
+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"
+tag_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $tag_oid)
+tag_compat_size=$(strlen "$tag_compat_content")
+
+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"
test_expect_success "Reach a blob from a tag pointing to it" '
echo_without_newline "$hello_content" >expect &&
- git cat-file blob $tag_sha1 >actual &&
+ git cat-file blob $tag_oid >actual &&
test_cmp expect actual
'
-for batch in batch batch-check batch-command
+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" '
@@ -360,12 +400,12 @@ test_expect_success "--batch-check for a non-existent hash" '
test_expect_success "--batch for an existent and a non-existent hash" '
cat >expect <<-EOF &&
- $tag_sha1 tag $tag_size
+ $tag_oid tag $tag_size
$tag_content
0000000000000000000000000000000000000000 missing
EOF
- printf "$tag_sha1\n0000000000000000000000000000000000000000" >in &&
+ printf "$tag_oid\n0000000000000000000000000000000000000000" >in &&
git cat-file --batch <in >actual &&
test_cmp expect actual
'
@@ -386,112 +426,102 @@ 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
"
-printf "%s\0" \
- "$hello_sha1 blob $hello_size" \
+ printf "%s\0" \
+ "$boid blob $hello_size" \
"$hello_content" \
- "$commit_sha1 commit $commit_size" \
- "$commit_content" \
- "$tag_sha1 tag $tag_size" \
- "$tag_content" \
+ "$coid commit $csize" \
+ "$ccontent" \
+ "$toid tag $tsize" \
+ "$tcontent" \
"deadbeef missing" \
" missing" >batch_output
-test_expect_success '--batch with multiple sha1s gives correct format' '
+ 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 sha1s gives correct format' '
+ 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, -Z with multiple sha1s gives correct format' '
+ 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
"
-printf "%s\0" \
- "$hello_sha1 blob $hello_size" \
- "$tree_sha1 tree $tree_size" \
- "$commit_sha1 commit $commit_size" \
- "$tag_sha1 tag $tag_size" \
+ 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 sha1s gives correct format" '
+ 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 sha1s gives correct format" '
+ 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 sha1s gives correct format" '
+ 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
-'
-
-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
-'
+ '
-batch_command_multiple_info="info $hello_sha1
-info $tree_sha1
-info $commit_sha1
-info $tag_sha1
+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' '
+ test_expect_success '--batch-command with multiple info calls gives correct format' '
cat >expect <<-EOF &&
- $hello_sha1 blob $hello_size
- $tree_sha1 tree $tree_size
- $commit_sha1 commit $commit_size
- $tag_sha1 tag $tag_size
+ $boid blob $hello_size
+ $loid tree $lsize
+ $coid commit $csize
+ $toid tag $tsize
deadbeef missing
EOF
@@ -510,22 +540,22 @@ test_expect_success '--batch-command with multiple info calls gives correct form
git cat-file --batch-command --buffer -Z <in >actual &&
test_cmp expect_nul actual
-'
+ '
-batch_command_multiple_contents="contents $hello_sha1
-contents $commit_sha1
-contents $tag_sha1
+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' '
+ test_expect_success '--batch-command with multiple command calls gives correct format' '
printf "%s\0" \
- "$hello_sha1 blob $hello_size" \
+ "$boid blob $hello_size" \
"$hello_content" \
- "$commit_sha1 commit $commit_size" \
- "$commit_content" \
- "$tag_sha1 tag $tag_size" \
- "$tag_content" \
+ "$coid commit $csize" \
+ "$ccontent" \
+ "$toid tag $tsize" \
+ "$tcontent" \
"deadbeef missing" >expect_nul &&
tr "\0" "\n" <expect_nul >expect &&
@@ -543,6 +573,33 @@ test_expect_success '--batch-command with multiple command calls gives correct f
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' '
@@ -569,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 &&
@@ -583,12 +640,12 @@ test_expect_success 'setup bogus data' '
bogus_short_type="bogus" &&
bogus_short_content="bogus" &&
bogus_short_size=$(strlen "$bogus_short_content") &&
- bogus_short_sha1=$(echo_without_newline "$bogus_short_content" | git hash-object -t $bogus_short_type --literally -w --stdin) &&
+ 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_sha1=$(echo_without_newline "$bogus_long_content" | git hash-object -t $bogus_long_type --literally -w --stdin)
+ 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
@@ -608,9 +665,9 @@ do
if test "$arg1" = "--allow-unknown-type"
then
- git cat-file $arg1 $arg2 $bogus_short_sha1
+ git cat-file $arg1 $arg2 $bogus_short_oid
else
- test_must_fail git cat-file $arg1 $arg2 $bogus_short_sha1 >out 2>actual &&
+ test_must_fail git cat-file $arg1 $arg2 $bogus_short_oid >out 2>actual &&
test_must_be_empty out &&
test_cmp expect actual
fi
@@ -620,21 +677,21 @@ do
if test "$arg2" = "-p"
then
cat >expect <<-EOF
- error: header for $bogus_long_sha1 too long, exceeds 32 bytes
- fatal: Not a valid object name $bogus_long_sha1
+ 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_sha1 too long, exceeds 32 bytes
+ 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_sha1
+ git cat-file $arg1 $arg2 $bogus_short_oid
else
- test_must_fail git cat-file $arg1 $arg2 $bogus_long_sha1 >out 2>actual &&
+ test_must_fail git cat-file $arg1 $arg2 $bogus_long_oid >out 2>actual &&
test_must_be_empty out &&
test_cmp expect actual
fi
@@ -668,28 +725,28 @@ do
done
test_expect_success '-e is OK with a broken object without --allow-unknown-type' '
- git cat-file -e $bogus_short_sha1
+ 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_sha1
+ 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_sha1 &&
- test_expect_code 128 git cat-file -p --allow-unknown-type $bogus_short_sha1
+ 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_sha1 2>err.actual &&
+ 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_sha1 >bogus-oid &&
+ echo $bogus_short_oid >bogus-oid &&
cat >err.expect <<-\EOF &&
fatal: invalid object type
@@ -711,52 +768,52 @@ test_expect_success 'the --allow-unknown-type option does not consider replaceme
cat >expect <<-EOF &&
$bogus_short_type
EOF
- git cat-file -t --allow-unknown-type $bogus_short_sha1 >actual &&
+ 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_sha1" &&
- test-tool ref-store main update-ref msg "refs/replace/$bogus_short_sha1" $head $ZERO_OID REF_SKIP_OID_VERIFICATION &&
+ 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_sha1 >actual &&
+ 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_short_type >expect &&
- git cat-file -t --allow-unknown-type $bogus_short_sha1 >actual &&
+ 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_short_size >expect &&
- git cat-file -s --allow-unknown-type $bogus_short_sha1 >actual &&
+ git cat-file -s --allow-unknown-type $bogus_short_oid >actual &&
test_cmp expect actual
'
test_expect_success 'clean up broken object' '
- rm .git/objects/$(test_oid_to_path $bogus_short_sha1)
+ 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_long_type >expect &&
- git cat-file -t --allow-unknown-type $bogus_long_sha1 >actual &&
+ 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_long_size >expect &&
- git cat-file -s --allow-unknown-type $bogus_long_sha1 >actual &&
+ 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_sha1)
+ rm .git/objects/$(test_oid_to_path $bogus_long_oid)
'
test_expect_success 'cat-file -t and -s on corrupt loose object' '
@@ -853,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' '
@@ -941,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
'
@@ -1100,6 +1157,42 @@ 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 &&
diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh
index ac3d173..64aea38 100755
--- a/t/t1007-hash-object.sh
+++ b/t/t1007-hash-object.sh
@@ -124,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"
'
@@ -154,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"
'
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/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh
index f67611d..ab3a105 100755
--- a/t/t1091-sparse-checkout-builtin.sh
+++ b/t/t1091-sparse-checkout-builtin.sh
@@ -334,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 &&
/*
!/*/
@@ -886,6 +886,12 @@ test_expect_success 'by default, cone mode will error out when passed files' '
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 &&
@@ -962,7 +968,7 @@ test_expect_success 'check-rules non-cone mode' '
git -C bare sparse-checkout check-rules --no-cone --rules-file ../rules\
>check-rules-file <all-files &&
- cat rules | git -C repo sparse-checkout set --no-cone --stdin &&
+ 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 &&
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index f4e2752..9b65d9e 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -11,6 +11,98 @@ 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
'
@@ -69,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' '
@@ -1066,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' '
@@ -1098,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 &&
@@ -1157,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' '
@@ -2009,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' '
@@ -2052,22 +2189,33 @@ 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' '
diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh
index e5a0d65..29cf8a9 100755
--- a/t/t1301-shared-repo.sh
+++ b/t/t1301-shared-repo.sh
@@ -52,7 +52,7 @@ test_expect_success 'shared=all' '
test 2 = $(git config core.sharedrepository)
'
-test_expect_failure 'template can set core.bare' '
+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 &&
@@ -60,18 +60,7 @@ test_expect_failure 'template can set core.bare' '
mkdir -p templates/ &&
cp .git/config templates/config &&
git init --template=templates subdir &&
- test_path_exists subdir/HEAD
-'
-
-test_expect_success 'template can set core.bare but overridden by command line' '
- 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 --no-bare --template=templates subdir &&
- test_path_exists subdir/.git/HEAD
+ test_path_is_missing subdir/HEAD
'
test_expect_success POSIXPERM 'update-server-info honors core.sharedRepository' '
@@ -137,22 +126,6 @@ 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 &&
diff --git a/t/t1302-repo-version.sh b/t/t1302-repo-version.sh
index 179474f..42caa0d 100755
--- a/t/t1302-repo-version.sh
+++ b/t/t1302-repo-version.sh
@@ -9,10 +9,6 @@ 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
@@ -28,7 +24,12 @@ test_expect_success 'setup' '
'
test_expect_success 'gitdir selection on normal repos' '
- 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 &&
@@ -79,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
@@ -94,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/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 9ac4b70..ec3443c 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -9,8 +9,6 @@ test_description='Test git update-ref and basic ref logging'
Z=$ZERO_OID
m=refs/heads/main
-n_dir=refs/heads/gu
-n=$n_dir/fixes
outside=refs/foo
bare=bare-repo
@@ -62,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)" '
@@ -92,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" '
@@ -101,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' '
@@ -132,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 &&
@@ -221,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
'
@@ -265,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" \
@@ -285,40 +288,13 @@ 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 "git update-ref -d $m && rm -rf .git/logs actual expect" &&
+ 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
'
@@ -348,20 +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 "git update-ref -d $m && rm -rf .git/logs actual expect" &&
+ 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"
@@ -409,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_grep -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
'
@@ -434,7 +423,24 @@ test_expect_success 'Query "main@{2005-05-28}" (past end of history)' '
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 &&
@@ -491,51 +497,51 @@ test_expect_success 'given old value for missing pseudoref, do not create' '
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 $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 $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 $A = $(git show-ref -s --verify PSEUDOREF) &&
test_grep "already exists" err
'
@@ -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' '
@@ -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' '
@@ -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
@@ -1635,13 +1641,4 @@ test_expect_success PIPE 'transaction flushes status updates' '
test_cmp expected actual
'
-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_done
diff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh
index c7745e1..5c60d6f 100755
--- a/t/t1401-symbolic-ref.sh
+++ b/t/t1401-symbolic-ref.sh
@@ -106,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' '
@@ -171,8 +170,8 @@ test_expect_success 'symbolic-ref refuses invalid target for non-HEAD' '
'
test_expect_success 'symbolic-ref allows top-level target for non-HEAD' '
- git symbolic-ref refs/heads/top-level FETCH_HEAD &&
- git update-ref FETCH_HEAD HEAD &&
+ git symbolic-ref refs/heads/top-level ORIG_HEAD &&
+ git update-ref ORIG_HEAD HEAD &&
test_cmp_rev top-level HEAD
'
diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh
index b50ae6f..33fb7a3 100755
--- a/t/t1403-show-ref.sh
+++ b/t/t1403-show-ref.sh
@@ -174,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/&/#"
@@ -197,18 +205,20 @@ test_expect_success 'show-ref --verify with dangling ref' '
'
test_expect_success 'show-ref sub-modes are mutually exclusive' '
- cat >expect <<-EOF &&
- fatal: only one of ${SQ}--exclude-existing${SQ}, ${SQ}--verify${SQ} or ${SQ}--exists${SQ} can be given
- EOF
-
test_must_fail git show-ref --verify --exclude-existing 2>err &&
- test_cmp expect err &&
+ grep "verify" err &&
+ grep "exclude-existing" err &&
+ grep "cannot be used together" err &&
test_must_fail git show-ref --verify --exists 2>err &&
- test_cmp expect err &&
+ grep "verify" err &&
+ grep "exists" err &&
+ grep "cannot be used together" err &&
test_must_fail git show-ref --exclude-existing --exists 2>err &&
- test_cmp expect err
+ grep "exclude-existing" err &&
+ grep "exists" err &&
+ grep "cannot be used together" err
'
test_expect_success '--exists with existing reference' '
@@ -260,10 +270,20 @@ test_expect_success '--exists with non-commit object' '
test_expect_success '--exists with directory fails with generic error' '
cat >expect <<-EOF &&
- error: failed to look up reference: Is a directory
+ error: reference does not exist
EOF
- test_expect_code 1 git show-ref --exists refs/heads 2>err &&
+ 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 0369bee..98e9158 100755
--- a/t/t1404-update-ref-errors.sh
+++ b/t/t1404-update-ref-errors.sh
@@ -92,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
@@ -103,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
@@ -191,141 +188,69 @@ 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 REFFILES 'D/F conflict prevents add long + delete short' '
+test_expect_success 'D/F conflict prevents add long + delete short' '
df_test refs/df-al-ds --add-del foo/bar foo
'
-test_expect_success REFFILES 'D/F conflict prevents add short + delete long' '
+test_expect_success 'D/F conflict prevents add short + delete long' '
df_test refs/df-as-dl --add-del foo foo/bar
'
-test_expect_success REFFILES 'D/F conflict prevents delete long + add short' '
+test_expect_success 'D/F conflict prevents delete long + add short' '
df_test refs/df-dl-as --del-add foo/bar foo
'
-test_expect_success REFFILES 'D/F conflict prevents delete short + add long' '
+test_expect_success 'D/F conflict prevents delete short + add long' '
df_test refs/df-ds-al --del-add foo foo/bar
'
-test_expect_success REFFILES 'D/F conflict prevents add long + delete short packed' '
+test_expect_success 'D/F conflict prevents add long + delete short packed' '
df_test refs/df-al-dsp --pack --add-del foo/bar foo
'
-test_expect_success REFFILES 'D/F conflict prevents add short + delete long packed' '
+test_expect_success 'D/F conflict prevents add short + delete long packed' '
df_test refs/df-as-dlp --pack --add-del foo foo/bar
'
-test_expect_success REFFILES 'D/F conflict prevents delete long packed + add short' '
+test_expect_success 'D/F conflict prevents delete long packed + add short' '
df_test refs/df-dlp-as --pack --del-add foo/bar foo
'
-test_expect_success REFFILES 'D/F conflict prevents delete short packed + add long' '
+test_expect_success 'D/F conflict prevents delete short packed + add long' '
df_test refs/df-dsp-al --pack --del-add foo foo/bar
'
# Try some combinations involving symbolic refs...
-test_expect_success REFFILES 'D/F conflict prevents indirect add long + delete short' '
+test_expect_success 'D/F conflict prevents indirect add long + delete short' '
df_test refs/df-ial-ds --sym-add --add-del foo/bar foo
'
-test_expect_success REFFILES 'D/F conflict prevents indirect add long + indirect delete short' '
+test_expect_success 'D/F conflict prevents indirect add long + indirect delete short' '
df_test refs/df-ial-ids --sym-add --sym-del --add-del foo/bar foo
'
-test_expect_success REFFILES 'D/F conflict prevents indirect add short + indirect delete long' '
+test_expect_success 'D/F conflict prevents indirect add short + indirect delete long' '
df_test refs/df-ias-idl --sym-add --sym-del --add-del foo foo/bar
'
-test_expect_success REFFILES 'D/F conflict prevents indirect delete long + indirect add short' '
+test_expect_success 'D/F conflict prevents indirect delete long + indirect add short' '
df_test refs/df-idl-ias --sym-add --sym-del --del-add foo/bar foo
'
-test_expect_success REFFILES 'D/F conflict prevents indirect add long + delete short packed' '
+test_expect_success 'D/F conflict prevents indirect add long + delete short packed' '
df_test refs/df-ial-dsp --sym-add --pack --add-del foo/bar foo
'
-test_expect_success REFFILES 'D/F conflict prevents indirect add long + indirect delete short packed' '
+test_expect_success 'D/F conflict prevents indirect add long + indirect delete short packed' '
df_test refs/df-ial-idsp --sym-add --sym-del --pack --add-del foo/bar foo
'
-test_expect_success REFFILES 'D/F conflict prevents add long + indirect delete short packed' '
+test_expect_success 'D/F conflict prevents add long + indirect delete short packed' '
df_test refs/df-al-idsp --sym-del --pack --add-del foo/bar foo
'
-test_expect_success REFFILES 'D/F conflict prevents indirect delete long packed + indirect add short' '
+test_expect_success 'D/F conflict prevents indirect delete long packed + indirect add short' '
df_test refs/df-idlp-ias --sym-add --sym-del --pack --del-add foo/bar foo
'
@@ -468,169 +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 &&
- # 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_grep "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 e4627cf..a6bcd62 100755
--- a/t/t1405-main-ref-store.sh
+++ b/t/t1405-main-ref-store.sh
@@ -15,14 +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 &&
- $RUN pack-refs PACK_REFS_PRUNE,PACK_REFS_ALL &&
- 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 &&
@@ -41,12 +33,6 @@ test_expect_success 'delete_refs(FOO, refs/tags/new-tag)' '
test_must_fail git rev-parse refs/tags/new-tag --
'
-# In reftable, we keep the reflogs around for deleted refs.
-test_expect_success !REFFILES 'delete-reflog(FOO, refs/tags/new-tag)' '
- $RUN delete-reflog FOO &&
- $RUN delete-reflog refs/tags/new-tag
-'
-
test_expect_success 'rename_refs(main, new-main)' '
git rev-parse main >expected &&
$RUN rename-ref refs/heads/main refs/heads/new-main &&
@@ -82,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
'
@@ -112,7 +98,7 @@ test_expect_success 'delete_reflog(HEAD)' '
test_must_fail git reflog exists HEAD
'
-test_expect_success REFFILES 'create-reflog(HEAD)' '
+test_expect_success 'create-reflog(HEAD)' '
$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 e6a7f73..c01f0f1 100755
--- a/t/t1406-submodule-ref-store.sh
+++ b/t/t1406-submodule-ref-store.sh
@@ -63,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
'
diff --git a/t/t1407-worktree-ref-store.sh b/t/t1407-worktree-ref-store.sh
index 05b1881..48b1c92 100755
--- a/t/t1407-worktree-ref-store.sh
+++ b/t/t1407-worktree-ref-store.sh
@@ -53,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/t1409-avoid-packing-refs.sh b/t/t1409-avoid-packing-refs.sh
index f23c015..7748973 100755
--- a/t/t1409-avoid-packing-refs.sh
+++ b/t/t1409-avoid-packing-refs.sh
@@ -5,6 +5,12 @@ 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 aeddc2f..5bf883f 100755
--- a/t/t1410-reflog.sh
+++ b/t/t1410-reflog.sh
@@ -354,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 REFFILES,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
-'
-
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 &&
@@ -397,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 &&
(
@@ -469,13 +427,121 @@ test_expect_success 'expire one of multiple worktrees' '
)
'
-test_expect_success REFFILES 'empty reflog' '
+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 &&
+ 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
+ )
+'
+
test_done
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 3b53184..eb4eec8 100755
--- a/t/t1415-worktree-refs.sh
+++ b/t/t1415-worktree-refs.sh
@@ -17,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/t1417-reflog-updateref.sh b/t/t1417-reflog-updateref.sh
index 14f13b5..0eb5e67 100755
--- a/t/t1417-reflog-updateref.sh
+++ b/t/t1417-reflog-updateref.sh
@@ -14,9 +14,13 @@ test_expect_success 'setup' '
test_commit B &&
test_commit C &&
- cp .git/logs/HEAD HEAD.old &&
+ git reflog HEAD >expect &&
git reset --hard HEAD~ &&
- cp HEAD.old .git/logs/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
)
'
@@ -25,7 +29,7 @@ test_reflog_updateref () {
shift
args="$@"
- test_expect_success REFFILES "get '$exp' with '$args'" '
+ test_expect_success "get '$exp' with '$args'" '
test_when_finished "rm -rf copy" &&
cp -R repo copy &&
diff --git a/t/t1419-exclude-refs.sh b/t/t1419-exclude-refs.sh
index 5d8c86b..1359574 100755
--- a/t/t1419-exclude-refs.sh
+++ b/t/t1419-exclude-refs.sh
@@ -8,6 +8,12 @@ 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
diff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh
index 68cc9e7..0c00118 100755
--- a/t/t1430-bad-ref-name.sh
+++ b/t/t1430-bad-ref-name.sh
@@ -164,9 +164,9 @@ test_expect_success 'rev-parse skips symref pointing to broken name' '
test_expect_success 'for-each-ref emits warnings for broken names' '
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" &&
- printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname &&
+ 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" &&
- printf "ref: refs/heads/main\n" >.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" &&
git for-each-ref >output 2>error &&
! grep -e "broken\.\.\.ref" output &&
@@ -257,7 +257,7 @@ test_expect_success 'update-ref -d can delete broken name through symref' '
'
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-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 &&
@@ -267,7 +267,7 @@ test_expect_success 'update-ref --no-deref -d can delete symref with broken name
'
test_expect_success 'branch -d can delete symref with broken name' '
- printf "ref: refs/heads/main\n" >.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 &&
@@ -277,7 +277,7 @@ test_expect_success 'branch -d can delete symref with broken name' '
'
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-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 &&
@@ -287,7 +287,7 @@ test_expect_success 'update-ref --no-deref -d can delete dangling symref with br
'
test_expect_success 'branch -d can delete dangling symref with broken name' '
- printf "ref: refs/heads/idonotexist\n" >.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 &&
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index 0e3e87d..173b4fa 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
'
@@ -115,15 +116,15 @@ test_expect_success 'zlib corrupt loose object output ' '
'
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_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 &&
@@ -131,27 +132,25 @@ test_expect_success 'HEAD link pointing at a funny object' '
'
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_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_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 &&
@@ -159,17 +158,18 @@ test_expect_success 'other worktree HEAD link pointing at a funny object' '
'
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_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_grep "worktrees/other/HEAD points to something strange" out
'
@@ -391,7 +391,7 @@ 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_grep "broken link" out
@@ -411,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
'
@@ -428,7 +428,7 @@ test_expect_success 'tag with incorrect tag name & missing 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" &&
git fsck --tags 2>out &&
@@ -452,7 +452,7 @@ 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_grep "error in tag .*: invalid author/committer" out
@@ -471,7 +471,7 @@ 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_grep "error in tag $tag.*unterminated header: NUL at offset" out
@@ -1060,4 +1060,41 @@ test_expect_success 'fsck reports problems in current worktree index without fil
test_cmp expect actual
'
+test_expect_success 'fsck warning on symlink target with excessive length' '
+ symlink_target=$(printf "pattern %032769d" 1 | git hash-object -w --stdin) &&
+ test_when_finished "remove_object $symlink_target" &&
+ tree=$(printf "120000 blob %s\t%s\n" $symlink_target symlink | git mktree) &&
+ test_when_finished "remove_object $tree" &&
+ cat >expected <<-EOF &&
+ warning in blob $symlink_target: symlinkTargetLength: symlink target too long
+ EOF
+ git fsck --no-dangling >actual 2>&1 &&
+ test_cmp expected actual
+'
+
+test_expect_success 'fsck warning on symlink target pointing inside git dir' '
+ gitdir=$(printf ".git" | git hash-object -w --stdin) &&
+ ntfs_gitdir=$(printf "GIT~1" | git hash-object -w --stdin) &&
+ hfs_gitdir=$(printf ".${u200c}git" | git hash-object -w --stdin) &&
+ inside_gitdir=$(printf "nested/.git/config" | git hash-object -w --stdin) &&
+ benign_target=$(printf "legit/config" | git hash-object -w --stdin) &&
+ tree=$(printf "120000 blob %s\t%s\n" \
+ $benign_target benign_target \
+ $gitdir gitdir \
+ $hfs_gitdir hfs_gitdir \
+ $inside_gitdir inside_gitdir \
+ $ntfs_gitdir ntfs_gitdir |
+ git mktree) &&
+ for o in $gitdir $ntfs_gitdir $hfs_gitdir $inside_gitdir $benign_target $tree
+ do
+ test_when_finished "remove_object $o" || return 1
+ done &&
+ printf "warning in blob %s: symlinkPointsToGitDir: symlink target points to git dir\n" \
+ $gitdir $hfs_gitdir $inside_gitdir $ntfs_gitdir |
+ sort >expected &&
+ git fsck --no-dangling >actual 2>&1 &&
+ sort actual >actual.sorted &&
+ test_cmp expected actual.sorted
+'
+
test_done
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index 3f9e7f6..a669e59 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -208,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 &&
diff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh
index f073759..b754b9f 100755
--- a/t/t1502-rev-parse-parseopt.sh
+++ b/t/t1502-rev-parse-parseopt.sh
@@ -322,4 +322,15 @@ 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/t1503-rev-parse-verify.sh b/t/t1503-rev-parse-verify.sh
index bc13683..79df65e 100755
--- a/t/t1503-rev-parse-verify.sh
+++ b/t/t1503-rev-parse-verify.sh
@@ -144,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/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/t1800-hook.sh b/t/t1800-hook.sh
index 8b0234c..1894ebe 100755
--- a/t/t1800-hook.sh
+++ b/t/t1800-hook.sh
@@ -185,4 +185,19 @@ test_expect_success 'stdin to hooks' '
test_cmp expect actual
'
+test_expect_success 'clone protections' '
+ test_config core.hooksPath "$(pwd)/my-hooks" &&
+ mkdir -p my-hooks &&
+ write_script my-hooks/test-hook <<-\EOF &&
+ echo Hook ran $1
+ EOF
+
+ git hook run test-hook 2>err &&
+ test_grep "Hook ran" err &&
+ test_must_fail env GIT_CLONE_PROTECTION_ACTIVE=true \
+ git hook run test-hook 2>err &&
+ test_grep "active .core.hooksPath" err &&
+ test_grep ! "Hook ran" err
+'
+
test_done
diff --git a/t/t2011-checkout-invalid-head.sh b/t/t2011-checkout-invalid-head.sh
index d9997e7..04f53b1 100755
--- a/t/t2011-checkout-invalid-head.sh
+++ b/t/t2011-checkout-invalid-head.sh
@@ -18,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
@@ -31,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/t2016-checkout-patch.sh b/t/t2016-checkout-patch.sh
index 747eb55..c40b661 100755
--- a/t/t2016-checkout-patch.sh
+++ b/t/t2016-checkout-patch.sh
@@ -2,6 +2,7 @@
test_description='git checkout --patch'
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-patch-mode.sh
test_expect_success 'setup' '
@@ -38,26 +39,32 @@ test_expect_success 'git checkout -p with staged changes' '
verify_state dir/foo index index
'
-test_expect_success '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 '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 '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
-'
+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
diff --git a/t/t2017-checkout-orphan.sh b/t/t2017-checkout-orphan.sh
index 947d158..a5c7358 100755
--- a/t/t2017-checkout-orphan.sh
+++ b/t/t2017-checkout-orphan.sh
@@ -86,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/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh
index 8202ef8..8d90d02 100755
--- a/t/t2020-checkout-detach.sh
+++ b/t/t2020-checkout-detach.sh
@@ -45,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 &&
@@ -164,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/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh
index a97416c..a3b1449 100755
--- a/t/t2024-checkout-dwim.sh
+++ b/t/t2024-checkout-dwim.sh
@@ -113,7 +113,7 @@ test_expect_success 'checkout of branch from multiple remotes fails with advice'
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 &&
diff --git a/t/t2060-switch.sh b/t/t2060-switch.sh
index e247a47..c91c4db 100755
--- a/t/t2060-switch.sh
+++ b/t/t2060-switch.sh
@@ -170,8 +170,10 @@ test_expect_success 'switch back when temporarily detached and checked out elsew
# 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
'
diff --git a/t/t2070-restore.sh b/t/t2070-restore.sh
index 16d6348..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' '
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 8198a1e..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
diff --git a/t/t2104-update-index-skip-worktree.sh b/t/t2104-update-index-skip-worktree.sh
index 0bab134..7ec7f30 100755
--- a/t/t2104-update-index-skip-worktree.sh
+++ b/t/t2104-update-index-skip-worktree.sh
@@ -11,27 +11,27 @@ TEST_PASSES_SANITIZE_LEAK=true
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 &&
diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh
index c01492f..df235ac 100755
--- a/t/t2200-add-update.sh
+++ b/t/t2200-add-update.sh
@@ -65,6 +65,16 @@ test_expect_success 'update did not touch untracked files' '
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' '
git ls-files -s |
diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh
index df4aff7..ba320dc 100755
--- a/t/t2400-worktree-add.sh
+++ b/t/t2400-worktree-add.sh
@@ -126,6 +126,28 @@ test_expect_success 'die the same branch is already checked out' '
)
'
+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
+ )
+'
+
test_expect_success SYMLINKS 'die the same branch is already checked out (symlink)' '
head=$(git -C there rev-parse --git-path HEAD) &&
ref=$(git -C there symbolic-ref HEAD) &&
@@ -405,7 +427,7 @@ test_expect_success '"add" worktree with orphan branch, lock, and reason' '
# Note: Quoted arguments containing spaces are not supported.
test_wt_add_orphan_hint () {
local context="$1" &&
- local use_branch=$2 &&
+ local use_branch="$2" &&
shift 2 &&
local opts="$*" &&
test_expect_success "'worktree add' show orphan hint in bad/orphan HEAD w/ $context" '
@@ -415,7 +437,7 @@ test_wt_add_orphan_hint () {
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 orphan branch" 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
@@ -436,7 +458,7 @@ test_expect_success "'worktree add' doesn't show orphan hint in bad/orphan HEAD
(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 orphan branch" actual
+ ! grep "hint: If you meant to create a worktree containing a new unborn branch" actual
'
test_expect_success 'local clone from linked checkout' '
@@ -468,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
)
'
@@ -509,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
)
@@ -709,9 +733,9 @@ 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 orphan branch" &&
+ 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: '[-a-z]*' and '[-a-z]*' cannot be used together" &&
+ 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" &&
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index c54fd9e..ccfa6a7 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -28,7 +28,7 @@ test_expect_success 'git branch --help should not have created a bogus branch' '
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 &&
@@ -75,15 +75,15 @@ 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_ref_exists 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
+ 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' '
@@ -203,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 " $ZERO_OID.*$msg$" .git/logs/HEAD &&
- grep "^$ZERO_OID.*$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' '
@@ -215,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 &&
+ 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 &&
@@ -243,7 +245,7 @@ test_expect_success 'git branch -M baz bam should succeed when baz is checked ou
git worktree prune
'
-test_expect_success 'git branch -M fails if updating any linked working tree fails' '
+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 &&
@@ -438,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
'
@@ -451,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
'
@@ -479,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
'
@@ -494,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 &&
@@ -577,7 +576,7 @@ 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
'
@@ -699,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' '
@@ -809,7 +809,7 @@ test_expect_success 'deleting a symref' '
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_ref_missing refs/heads/dangling-symref &&
@@ -833,35 +833,6 @@ test_expect_success 'renaming a symref is not allowed' '
test_ref_missing 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_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 'test tracking setup via --track' '
git config remote.local.url . &&
git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
@@ -1138,16 +1109,16 @@ test_expect_success '--set-upstream-to notices an error to set branch as own ups
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_ref_exists 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
+ 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' '
@@ -1183,9 +1154,9 @@ test_expect_success 'avoid ambiguous track and advise' '
hint: tracking ref '\''refs/heads/main'\'':
hint: ambi1
hint: ambi2
- hint: ''
+ hint:
hint: This is typically a configuration error.
- hint: ''
+ hint:
hint: To support setting up tracking branches, ensure that
hint: different remotes'\'' fetch refspecs map into different
hint: tracking namespaces.
@@ -1573,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 &&
@@ -1595,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
@@ -1609,10 +1582,70 @@ 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" &&
# this works in the "listing" mode, so bad sort key
# is a dying offence.
@@ -1660,4 +1693,14 @@ test_expect_success '--track overrides branch.autoSetupMerge' '
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/t3202-show-branch.sh b/t/t3202-show-branch.sh
index 6a98b2d..a1139f7 100755
--- a/t/t3202-show-branch.sh
+++ b/t/t3202-show-branch.sh
@@ -4,9 +4,6 @@ test_description='test show-branch'
. ./test-lib.sh
-# arbitrary reference time: 2009-08-30 19:20:00
-GIT_TEST_DATE_NOW=1251660000; export GIT_TEST_DATE_NOW
-
test_expect_success 'error descriptions on empty repository' '
current=$(git branch --show-current) &&
cat >expect <<-EOF &&
@@ -187,18 +184,6 @@ test_expect_success 'show branch --merge-base with N arguments' '
test_cmp expect actual
'
-test_expect_success 'show branch --reflog=2' '
- sed "s/^> //" >expect <<-\EOF &&
- > ! [refs/heads/branch10@{0}] (4 years, 5 months ago) commit: branch10
- > ! [refs/heads/branch10@{1}] (4 years, 5 months ago) commit: branch10
- > --
- > + [refs/heads/branch10@{0}] branch10
- > ++ [refs/heads/branch10@{1}] initial
- EOF
- git show-branch --reflog=2 >actual &&
- test_cmp actual expect
-'
-
# incompatible options
while read combo
do
@@ -264,4 +249,38 @@ test_expect_success 'error descriptions on orphan branch' '
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/t3310-notes-merge-manual-resolve.sh b/t/t3310-notes-merge-manual-resolve.sh
index 60d6ed2..597df5e 100755
--- a/t/t3310-notes-merge-manual-resolve.sh
+++ b/t/t3310-notes-merge-manual-resolve.sh
@@ -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 &&
diff --git a/t/t3321-notes-stripspace.sh b/t/t3321-notes-stripspace.sh
index 088a852..beca346 100755
--- a/t/t3321-notes-stripspace.sh
+++ b/t/t3321-notes-stripspace.sh
@@ -442,7 +442,7 @@ test_expect_success 'add note by specifying "-C", "--no-stripspace" is the defau
${LF}
EOF
- cat expect | git hash-object -w --stdin >blob &&
+ git hash-object -w --stdin <expect >blob &&
git notes add -C $(cat blob) &&
git notes show >actual &&
test_cmp expect actual &&
@@ -468,7 +468,7 @@ test_expect_success 'reuse note by specifying "-C" and "--stripspace"' '
second-line
EOF
- cat data | git hash-object -w --stdin >blob &&
+ git hash-object -w --stdin <data >blob &&
git notes add --stripspace -C $(cat blob) &&
git notes show >actual &&
test_cmp expect actual
@@ -492,7 +492,7 @@ test_expect_success 'reuse with "-C" and add note with "-m", "-m" will stripspac
third-line
EOF
- cat data | git hash-object -w --stdin >blob &&
+ git hash-object -w --stdin <data >blob &&
git notes add -C $(cat blob) -m "third-line" &&
git notes show >actual &&
test_cmp expect actual
@@ -511,7 +511,7 @@ test_expect_success 'add note with "-m" and reuse note with "-C", "-C" will not
second-line
EOF
- cat data | git hash-object -w --stdin >blob &&
+ git hash-object -w --stdin <data >blob &&
git notes add -m "first-line" -C $(cat blob) &&
git notes show >actual &&
test_cmp expect actual
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 24a539c..e1c8c5f 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -424,16 +424,6 @@ test_expect_success 'refuse to switch to branch checked out elsewhere' '
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 &&
(
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index c5f3055..d1bead6 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -153,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 &&
@@ -2160,7 +2172,7 @@ test_expect_success '--update-refs: check failed ref update' '
# 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 rev-parse third >.git/refs/heads/second &&
+ 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 &&
diff --git a/t/t3407-rebase-abort.sh b/t/t3407-rebase-abort.sh
index ebbaed1..9f49c42 100755
--- a/t/t3407-rebase-abort.sh
+++ b/t/t3407-rebase-abort.sh
@@ -40,9 +40,24 @@ testrebase() {
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" '
# 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 "$state_dir" &&
test_must_fail git rebase --skip &&
diff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh
index a364530..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' '
diff --git a/t/t3420-rebase-autostash.sh b/t/t3420-rebase-autostash.sh
index 693934e..1a820f1 100755
--- a/t/t3420-rebase-autostash.sh
+++ b/t/t3420-rebase-autostash.sh
@@ -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/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh
index 2eba00b..b40f262 100755
--- a/t/t3422-rebase-incompatible-options.sh
+++ b/t/t3422-rebase-incompatible-options.sh
@@ -100,12 +100,6 @@ test_rebase_am_only () {
test_must_fail git rebase $opt --root A
"
- test_expect_success "$opt incompatible with rebase.autosquash" "
- git checkout B^0 &&
- test_must_fail git -c rebase.autosquash=true rebase $opt A 2>err &&
- grep -e --no-autosquash err
- "
-
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 &&
@@ -118,12 +112,6 @@ test_rebase_am_only () {
grep -e --no-update-refs err
"
- test_expect_success "$opt okay with overridden rebase.autosquash" "
- test_when_finished \"git reset --hard B^0\" &&
- git checkout B^0 &&
- git -c rebase.autosquash=true rebase --no-autosquash $opt A
- "
-
test_expect_success "$opt okay with overridden rebase.rebaseMerges" "
test_when_finished \"git reset --hard B^0\" &&
git checkout B^0 &&
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/t3428-rebase-signoff.sh b/t/t3428-rebase-signoff.sh
index e1b1e94..1bebd1c 100755
--- a/t/t3428-rebase-signoff.sh
+++ b/t/t3428-rebase-signoff.sh
@@ -8,47 +8,45 @@ 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' '
@@ -56,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/t3438-rebase-broken-files.sh b/t/t3438-rebase-broken-files.sh
index c614c4f..821f08e 100755
--- a/t/t3438-rebase-broken-files.sh
+++ b/t/t3438-rebase-broken-files.sh
@@ -58,4 +58,13 @@ test_expect_success 'unknown key in 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/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh
index aeab689..411027f 100755
--- a/t/t3501-revert-cherry-pick.sh
+++ b/t/t3501-revert-cherry-pick.sh
@@ -104,11 +104,19 @@ test_expect_success 'revert forbidden on dirty working tree' '
'
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
'
@@ -170,6 +178,7 @@ 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 &&
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/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh
index c88d597..f3947b4 100755
--- a/t/t3507-cherry-pick-conflict.sh
+++ b/t/t3507-cherry-pick-conflict.sh
@@ -60,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 &&
@@ -74,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 &&
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
index 72020a5..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
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 f23d39f..839c904 100755
--- a/t/t3700-add.sh
+++ b/t/t3700-add.sh
@@ -28,6 +28,16 @@ test_expect_success 'Test of git add' '
touch foo && git add 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 'Post-check that foo is in the index' '
git ls-files foo | grep foo
'
@@ -339,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 &&
@@ -370,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'
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index 0b5339a..04d8333 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -4,6 +4,7 @@ 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
@@ -325,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
'
@@ -514,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 &&
@@ -530,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 &&
@@ -715,21 +716,21 @@ 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
'
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 34faeac..00db82f 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -200,7 +200,7 @@ test_expect_success 'drop stash reflog updates refs/stash' '
test_cmp expect actual
'
-test_expect_success REFFILES 'drop stash reflog updates refs/stash with rewrite' '
+test_expect_success 'drop stash reflog updates refs/stash with rewrite' '
git init repo &&
(
cd repo &&
@@ -213,16 +213,16 @@ test_expect_success REFFILES 'drop stash reflog updates refs/stash with rewrite'
new_oid="$(git -C repo rev-parse stash@{0})" &&
cat >expect <<-EOF &&
- $(test_oid zero) $old_oid
- $old_oid $new_oid
+ $new_oid
+ $old_oid
EOF
- cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+ git -C repo reflog show refs/stash --format=%H >actual &&
test_cmp expect actual &&
git -C repo stash drop stash@{1} &&
- cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+ git -C repo reflog show refs/stash --format=%H >actual &&
cat >expect <<-EOF &&
- $(test_oid zero) $new_oid
+ $new_oid
EOF
test_cmp expect actual
'
@@ -1516,4 +1516,56 @@ test_expect_success 'restore untracked files even when we hit conflicts' '
)
'
+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/t3920-crlf-messages.sh b/t/t3920-crlf-messages.sh
index 67fd234..50ae222 100755
--- a/t/t3920-crlf-messages.sh
+++ b/t/t3920-crlf-messages.sh
@@ -10,7 +10,7 @@ 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-orig-$branch.txt | append_cr >.crlf-body-$branch.txt &&
LIB_CRLF_BRANCHES="${LIB_CRLF_BRANCHES} ${branch}" &&
@@ -97,7 +97,7 @@ test_expect_success 'branch: --verbose works with messages using CRLF' '
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/t4001-diff-rename.sh b/t/t4001-diff-rename.sh
index 85be136..49c042a 100755
--- a/t/t4001-diff-rename.sh
+++ b/t/t4001-diff-rename.sh
@@ -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 7afc883..cb33070 100755
--- a/t/t4002-diff-basic.sh
+++ b/t/t4002-diff-basic.sh
@@ -405,7 +405,7 @@ test_expect_success 'diff-tree -r B A == diff-tree -r -R A 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 &&
+ 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
diff --git a/t/t4011-diff-symlink.sh b/t/t4011-diff-symlink.sh
index d7a5f7a..bc8ba88 100755
--- a/t/t4011-diff-symlink.sh
+++ b/t/t4011-diff-symlink.sh
@@ -13,13 +13,13 @@ TEST_PASSES_SANITIZE_LEAK=true
# 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/t4013-diff-various.sh b/t/t4013-diff-various.sh
index 5cc17c2..3855d68 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -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
@@ -522,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 &&
@@ -636,8 +633,8 @@ check_prefix () {
test_cmp expect actual.paths
}
-test_expect_success 'diff-files does not respect diff.noprefix' '
- git -c diff.noprefix diff-files -p >actual &&
+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
'
@@ -646,24 +643,65 @@ test_expect_success 'diff-files respects --no-prefix' '
check_prefix actual file0 file0
'
-test_expect_success 'diff respects diff.noprefix' '
- git -c diff.noprefix diff >actual &&
+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 &&
+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 &&
+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 --default-prefix overrides diff.mnemonicprefix' '
- git -c diff.mnemonicprefix diff --default-prefix >actual &&
+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/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 5ced27e..e37a141 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -1906,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
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..e56dfce
--- /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/t4020-diff-external.sh b/t/t4020-diff-external.sh
index c1ac09e..fdd865f 100755
--- a/t/t4020-diff-external.sh
+++ b/t/t4020-diff-external.sh
@@ -232,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/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/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh
index 5ce345d..651ec77 100755
--- a/t/t4053-diff-no-index.sh
+++ b/t/t4053-diff-no-index.sh
@@ -205,6 +205,18 @@ 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
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/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/t4117-apply-reject.sh b/t/t4117-apply-reject.sh
index c86d05a..4d15ccd 100755
--- a/t/t4117-apply-reject.sh
+++ b/t/t4117-apply-reject.sh
@@ -7,6 +7,7 @@ test_description='git apply with rejects
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t4123-apply-shrink.sh b/t/t4123-apply-shrink.sh
index 3ef8461..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
diff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh
index ece9fae..56210b5 100755
--- a/t/t4126-apply-empty.sh
+++ b/t/t4126-apply-empty.sh
@@ -66,4 +66,28 @@ test_expect_success 'apply --index create' '
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/t4129-apply-samemode.sh b/t/t4129-apply-samemode.sh
index e7a7295..4eb8444 100755
--- a/t/t4129-apply-samemode.sh
+++ b/t/t4129-apply-samemode.sh
@@ -41,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)' '
@@ -60,7 +61,8 @@ 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' '
@@ -101,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/t4150-am.sh b/t/t4150-am.sh
index 3b12576..5e2b6c8 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -1224,8 +1224,8 @@ test_expect_success 'record as an empty commit when meeting e-mail message that
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 >err &&
- grep "Patch is empty." err &&
+ 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 &&
@@ -1236,8 +1236,8 @@ test_expect_success 'skip an empty patch in the middle of an am session' '
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 >err &&
- grep "Patch is empty." err &&
+ 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 &&
diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh
index fb53ddd..b0a3e84 100755
--- a/t/t4200-rerere.sh
+++ b/t/t4200-rerere.sh
@@ -671,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 d738270..f698d0c 100755
--- a/t/t4201-shortlog.sh
+++ b/t/t4201-shortlog.sh
@@ -312,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
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index 7086366..60fe60d 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -2255,23 +2255,6 @@ test_expect_success 'log on empty repo fails' '
test_grep 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_grep broken stderr
-'
-
-test_expect_success REFFILES 'log diagnoses bogus HEAD symref' '
- git init empty &&
- echo "ref: refs/heads/invalid.lock" > empty/.git/HEAD &&
- 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 'log does not default to HEAD when rev input is given' '
git log --branches=does-not-exist >actual &&
test_must_be_empty actual
diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh
index e3d655e..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 || return 1
+ lf_to_nul <raw || return 1
done >expect &&
# the trailing NUL is already produced so we do not need to
# output another one
diff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh
index 21986a8..73ea9e5 100755
--- a/t/t4207-log-decoration-colors.sh
+++ b/t/t4207-log-decoration-colors.sh
@@ -70,8 +70,14 @@ ${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_commit})${c_reset} A
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 rm -rf .git/refs/replace* &&
+ test_when_finished remove_replace_refs &&
test_commit C &&
test_commit D &&
@@ -99,7 +105,7 @@ EOF
'
test_expect_success 'test coloring with grafted commit' '
- test_when_finished rm -rf .git/refs/replace* &&
+ test_when_finished remove_replace_refs &&
git replace --graft HEAD HEAD~2 &&
diff --git a/t/t4210-log-i18n.sh b/t/t4210-log-i18n.sh
index d2dfcf1..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
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 45f1d4f..661feb6 100755
--- a/t/t4254-am-corrupt.sh
+++ b/t/t4254-am-corrupt.sh
@@ -59,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/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/t4301-merge-tree-write-tree.sh b/t/t4301-merge-tree-write-tree.sh
index b2c8a43..eea1990 100755
--- a/t/t4301-merge-tree-write-tree.sh
+++ b/t/t4301-merge-tree-write-tree.sh
@@ -313,7 +313,7 @@ test_expect_success 'rename/add handling' '
# First, check that the bar that appears at stage 3 does not
# correspond to an individual blob anywhere in history
#
- hash=$(cat out | tr "\0" "\n" | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
+ 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 &&
@@ -380,7 +380,7 @@ test_expect_success SYMLINKS 'rename/add, where add is a mode conflict' '
# First, check that the bar that appears at stage 3 does not
# correspond to an individual blob anywhere in history
#
- hash=$(cat out | tr "\0" "\n" | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
+ 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 &&
@@ -630,8 +630,8 @@ test_expect_success 'mod6: chains of rename/rename(1to2) and add/add via collidi
# conflict entries do not appear as individual blobs anywhere
# in history.
#
- hash1=$(cat out | tr "\0" "\n" | head | grep 2.four | cut -f 2 -d " ") &&
- hash2=$(cat out | tr "\0" "\n" | head | grep 3.two | cut -f 2 -d " ") &&
+ 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 &&
@@ -887,7 +887,7 @@ test_expect_success '--stdin with both a successful and a conflicted merge' '
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 is incompatible with --stdin" expect
+ grep "^fatal: .*merge-base.*stdin.* cannot be used together" expect
'
# specify merge-base as parent of branch2
@@ -945,4 +945,49 @@ test_expect_success 'check the input format when --stdin is passed' '
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 4b4c331..72b8d0f 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -124,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 &&
diff --git a/t/t5003-archive-zip.sh b/t/t5003-archive-zip.sh
index fc499cd..961c6aa 100755
--- a/t/t5003-archive-zip.sh
+++ b/t/t5003-archive-zip.sh
@@ -239,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/t5100-mailinfo.sh b/t/t5100-mailinfo.sh
index db11cab..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
'
@@ -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/t5300-pack-object.sh b/t/t5300-pack-object.sh
index d402ec1..61e2be2 100755
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
@@ -441,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) &&
diff --git a/t/t5312-prune-corruption.sh b/t/t5312-prune-corruption.sh
index 230cb38..d8d2e30 100755
--- a/t/t5312-prune-corruption.sh
+++ b/t/t5312-prune-corruption.sh
@@ -111,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 REFFILES '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 REFFILES '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 REFFILES '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/t5317-pack-objects-filter-objects.sh b/t/t5317-pack-objects-filter-objects.sh
index 2ff3eef..79552d6 100755
--- a/t/t5317-pack-objects-filter-objects.sh
+++ b/t/t5317-pack-objects-filter-objects.sh
@@ -455,7 +455,7 @@ test_expect_success 'setup r1 - delete loose blobs' '
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 || return 1
done
diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh
index d4fc65e..a2b4442 100755
--- a/t/t5318-commit-graph.sh
+++ b/t/t5318-commit-graph.sh
@@ -540,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' '
@@ -560,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" \
- "oid table and fanout disagree on size"
+ "OID lookup chunk is the wrong size"
'
test_expect_success 'detect incorrect OID order' '
@@ -842,7 +842,7 @@ test_expect_success 'reader notices too-small oid fanout chunk' '
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 is missing the OID Fanout chunk
+ error: commit-graph required OID fanout chunk missing or corrupted
EOF
test_cmp expect.err err
'
@@ -850,7 +850,8 @@ test_expect_success 'reader notices too-small oid fanout chunk' '
test_expect_success 'reader notices fanout/lookup table mismatch' '
check_corrupt_chunk OIDF 1020 "FFFFFFFF" &&
cat >expect.err <<-\EOF &&
- error: commit-graph oid table and fanout disagree on size
+ 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
'
@@ -866,6 +867,7 @@ test_expect_success 'reader notices out-of-bounds fanout' '
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
'
@@ -874,7 +876,7 @@ 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 is missing the Commit Data chunk
+ error: commit-graph required commit data chunk missing or corrupted
EOF
test_cmp expect.err err
'
@@ -909,10 +911,10 @@ test_expect_success 'stale commit cannot be parsed when given directly' '
# Verify that it is possible to read the commit from the
# commit graph when not being paranoid, ...
- GIT_COMMIT_GRAPH_PARANOIA=false git rev-list B &&
+ git rev-list B &&
# ... but parsing the commit when double checking that
# it actually exists in the object database should fail.
- test_must_fail git rev-list -1 B
+ test_must_fail env GIT_COMMIT_GRAPH_PARANOIA=true git rev-list -1 B
)
'
@@ -936,9 +938,9 @@ test_expect_success 'stale commit cannot be parsed when traversing graph' '
# Again, we should be able to parse the commit when not
# being paranoid about commit graph staleness...
- GIT_COMMIT_GRAPH_PARANOIA=false git rev-parse HEAD~2 &&
+ git rev-parse HEAD~2 &&
# ... but fail when we are paranoid.
- test_must_fail git rev-parse HEAD~2 2>error &&
+ 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
)
'
diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh
index c4c6060..dd09134 100755
--- a/t/t5319-multi-pack-index.sh
+++ b/t/t5319-multi-pack-index.sh
@@ -1157,4 +1157,53 @@ test_expect_success 'reader notices too-small revindex chunk' '
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/t5326-multi-pack-bitmaps.sh b/t/t5326-multi-pack-bitmaps.sh
index 70d1b58..5d7d321 100755
--- a/t/t5326-multi-pack-bitmaps.sh
+++ b/t/t5326-multi-pack-bitmaps.sh
@@ -513,4 +513,21 @@ test_expect_success 'corrupt MIDX with bitmap causes fallback' '
)
'
+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/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/t5401-update-hooks.sh b/t/t5401-update-hooks.sh
index 001b7a1..d8cadee 100755
--- a/t/t5401-update-hooks.sh
+++ b/t/t5401-update-hooks.sh
@@ -123,7 +123,7 @@ 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
'
@@ -133,10 +133,8 @@ test_expect_success 'pre-receive hook that forgets to read its input' '
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/t5510-fetch.sh b/t/t5510-fetch.sh
index dcadd56..9441793 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -169,6 +169,7 @@ test_expect_success REFFILES 'fetch --prune fails to delete branches' '
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 &&
@@ -802,7 +803,8 @@ 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
)
'
@@ -1250,6 +1252,30 @@ EOF
test_cmp fatal-expect fatal-actual
'
+test_expect_success SYMLINKS 'clone does not get confused by a D/F conflict' '
+ git init df-conflict &&
+ (
+ cd df-conflict &&
+ ln -s .git a &&
+ git add a &&
+ test_tick &&
+ git commit -m symlink &&
+ test_commit a- &&
+ rm a &&
+ mkdir -p a/hooks &&
+ write_script a/hooks/post-checkout <<-EOF &&
+ echo WHOOPSIE >&2
+ echo whoopsie >"$TRASH_DIRECTORY"/whoops
+ EOF
+ git add a/hooks/post-checkout &&
+ test_tick &&
+ git commit -m post-checkout
+ ) &&
+ git clone df-conflict clone 2>err &&
+ test_grep ! WHOOPS err &&
+ test_path_is_missing whoops
+'
+
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
diff --git a/t/t5514-fetch-multiple.sh b/t/t5514-fetch-multiple.sh
index a95841d..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 &&
@@ -209,4 +218,156 @@ test_expect_success 'git fetch --multiple --jobs=0 picks a default' '
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/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh
index 26e933f..5e56620 100755
--- a/t/t5526-fetch-submodules.sh
+++ b/t/t5526-fetch-submodules.sh
@@ -771,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.
diff --git a/t/t5534-push-signed.sh b/t/t5534-push-signed.sh
index b4bc246..c91a62b 100755
--- a/t/t5534-push-signed.sh
+++ b/t/t5534-push-signed.sh
@@ -303,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) &&
diff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh
index df758e1..71428f3 100755
--- a/t/t5541-http-push-smart.sh
+++ b/t/t5541-http-push-smart.sh
@@ -232,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 &&
@@ -241,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 &&
diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh
index e444b30..4c3b327 100755
--- a/t/t5550-http-fetch-dumb.sh
+++ b/t/t5550-http-fetch-dumb.sh
@@ -66,11 +66,11 @@ test_expect_success 'create empty remote repository' '
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
diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh
index 8a41adf..a623a10 100755
--- a/t/t5551-http-fetch-smart.sh
+++ b/t/t5551-http-fetch-smart.sh
@@ -359,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' '
@@ -731,4 +733,22 @@ test_expect_success 'no empty path components' '
! 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/t5555-http-smart-common.sh b/t/t5555-http-smart-common.sh
index b1cfe8b..3dcb334 100755
--- a/t/t5555-http-smart-common.sh
+++ b/t/t5555-http-smart-common.sh
@@ -131,7 +131,6 @@ test_expect_success 'git upload-pack --advertise-refs: v2' '
fetch=shallow wait-for-done
server-option
object-format=$(test_oid algo)
- object-info
0000
EOF
diff --git a/t/t5558-clone-bundle-uri.sh b/t/t5558-clone-bundle-uri.sh
index 996a08e..1ca5f74 100755
--- a/t/t5558-clone-bundle-uri.sh
+++ b/t/t5558-clone-bundle-uri.sh
@@ -33,6 +33,15 @@ test_expect_success 'clone with path bundle' '
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 &&
@@ -284,6 +293,15 @@ test_expect_success 'clone HTTP bundle' '
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 &&
diff --git a/t/t5562/invoke-with-content-length.pl b/t/t5562/invoke-with-content-length.pl
index 718dd9b..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;
diff --git a/t/t5574-fetch-output.sh b/t/t5574-fetch-output.sh
index a9b06b2..5883839 100755
--- a/t/t5574-fetch-output.sh
+++ b/t/t5574-fetch-output.sh
@@ -61,11 +61,10 @@ test_expect_success 'fetch compact output' '
test_cmp expect actual
'
-test_expect_success 'fetch porcelain output' '
- test_when_finished "rm -rf porcelain" &&
-
+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" &&
@@ -74,15 +73,10 @@ test_expect_success 'fetch porcelain output' '
FORCE_UPDATED_OLD=$(git rev-parse HEAD) &&
git checkout main &&
- # 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 . porcelain &&
- git -C porcelain fetch origin $refspecs &&
+ # Backup to preseed.git
+ git clone --mirror . preseed.git &&
- # Now that we have set up the client repositories we can change our
- # local references.
+ # Continue changing our local references.
git branch new-branch &&
git branch -d deleted-branch &&
git checkout fast-forward &&
@@ -91,36 +85,53 @@ test_expect_success 'fetch porcelain output' '
git checkout force-updated &&
git reset --hard HEAD~ &&
test_commit --no-tag force-update-new &&
- FORCE_UPDATED_NEW=$(git rev-parse HEAD) &&
-
- 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
-
- # 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 \
- --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 \
- --porcelain --prune origin $refspecs >actual 2>stderr &&
- test_cmp expect actual &&
- test_must_be_empty stderr
+ 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" &&
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index 47eae64..deb1c28 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -157,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/ &&
@@ -633,6 +650,21 @@ test_expect_success CASE_INSENSITIVE_FS 'colliding file detection' '
test_grep "the following paths have collided" icasefs/warning
'
+test_expect_success CASE_INSENSITIVE_FS,SYMLINKS \
+ 'colliding symlink/directory keeps directory' '
+ git init icasefs-colliding-symlink &&
+ (
+ cd icasefs-colliding-symlink &&
+ a=$(printf a | git hash-object -w --stdin) &&
+ printf "100644 %s 0\tA/dir/b\n120000 %s 0\ta\n" $a $a >idx &&
+ git update-index --index-info <idx &&
+ test_tick &&
+ git commit -m initial
+ ) &&
+ git clone icasefs-colliding-symlink icasefs-colliding-symlink-clone &&
+ test_file_not_empty icasefs-colliding-symlink-clone/A/dir/b
+'
+
test_expect_success 'clone with GIT_DEFAULT_HASH' '
(
sane_unset GIT_DEFAULT_HASH &&
@@ -756,9 +788,72 @@ test_expect_success 'batch missing blob request does not inadvertently try to fe
git clone --filter=blob:limit=0 "file://$(pwd)/server" client
'
+test_expect_success 'clone with init.templatedir runs hooks' '
+ git init tmpl/hooks &&
+ write_script tmpl/hooks/post-checkout <<-EOF &&
+ echo HOOK-RUN >&2
+ echo I was here >hook.run
+ EOF
+ git -C tmpl/hooks add . &&
+ test_tick &&
+ git -C tmpl/hooks commit -m post-checkout &&
+
+ test_when_finished "git config --global --unset init.templateDir || :" &&
+ test_when_finished "git config --unset init.templateDir || :" &&
+ (
+ sane_unset GIT_TEMPLATE_DIR &&
+ NO_SET_GIT_TEMPLATE_DIR=t &&
+ export NO_SET_GIT_TEMPLATE_DIR &&
+
+ git -c core.hooksPath="$(pwd)/tmpl/hooks" \
+ clone tmpl/hooks hook-run-hookspath 2>err &&
+ test_grep ! "active .* hook found" err &&
+ test_path_is_file hook-run-hookspath/hook.run &&
+
+ git -c init.templateDir="$(pwd)/tmpl" \
+ clone tmpl/hooks hook-run-config 2>err &&
+ test_grep ! "active .* hook found" err &&
+ test_path_is_file hook-run-config/hook.run &&
+
+ git clone --template=tmpl tmpl/hooks hook-run-option 2>err &&
+ test_grep ! "active .* hook found" err &&
+ test_path_is_file hook-run-option/hook.run &&
+
+ git config --global init.templateDir "$(pwd)/tmpl" &&
+ git clone tmpl/hooks hook-run-global-config 2>err &&
+ git config --global --unset init.templateDir &&
+ test_grep ! "active .* hook found" err &&
+ test_path_is_file hook-run-global-config/hook.run &&
+
+ # clone ignores local `init.templateDir`; need to create
+ # a new repository because we deleted `.git/` in the
+ # `setup` test case above
+ git init local-clone &&
+ cd local-clone &&
+
+ git config init.templateDir "$(pwd)/../tmpl" &&
+ git clone ../tmpl/hooks hook-run-local-config 2>err &&
+ git config --unset init.templateDir &&
+ test_grep ! "active .* hook found" err &&
+ test_path_is_missing hook-run-local-config/hook.run
+ )
+'
+
. "$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"
'
diff --git a/t/t5605-clone-local.sh b/t/t5605-clone-local.sh
index 946c575..a305586 100755
--- a/t/t5605-clone-local.sh
+++ b/t/t5605-clone-local.sh
@@ -65,7 +65,7 @@ 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 &&
@@ -157,7 +157,7 @@ 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 'local clone from repo with corrupt refs fails gracefully' '
+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 &&
diff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh
index fc4bbd9..e93e0d0 100755
--- a/t/t5606-clone-options.sh
+++ b/t/t5606-clone-options.sh
@@ -64,7 +64,7 @@ test_expect_success 'disallows --bundle-uri with shallow options' '
for option in --depth=1 --shallow-since=01-01-2000 --shallow-exclude=HEAD
do
test_must_fail git clone --bundle-uri=bundle $option from to 2>err &&
- grep "bundle-uri is incompatible" err || return 1
+ grep "bundle-uri.* cannot be used together" err || return 1
done
'
@@ -120,14 +120,14 @@ test_expect_success 'prefers -c config over --template config' '
'
-test_expect_failure 'prefers --template config even for core.bare' '
+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)" = "true" &&
- test_path_is_file clone-bare-config/HEAD
+ 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' '
diff --git a/t/t5701-git-serve.sh b/t/t5701-git-serve.sh
index 3591bc2..c48830d 100755
--- a/t/t5701-git-serve.sh
+++ b/t/t5701-git-serve.sh
@@ -20,7 +20,6 @@ test_expect_success 'test capability advertisement' '
fetch=shallow wait-for-done
server-option
object-format=$(test_oid algo)
- object-info
EOF
cat >expect.trailer <<-EOF &&
0000
@@ -323,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)
@@ -380,4 +381,25 @@ test_expect_success 'basics of bundle-uri: dies if not enabled' '
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 3c0c604..1ef540f 100755
--- a/t/t5702-protocol-v2.sh
+++ b/t/t5702-protocol-v2.sh
@@ -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,9 @@ 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' '
@@ -247,7 +251,9 @@ test_expect_success 'bare clone propagates empty default branch' '
git -c init.defaultBranch=main -c protocol.version=2 \
clone --bare \
"file://$(pwd)/file_empty_parent" file_empty_child.git &&
- grep "refs/heads/mydefaultbranch" file_empty_child.git/HEAD
+ 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' '
@@ -265,7 +271,9 @@ test_expect_success 'clone propagates unborn HEAD from non-empty repo' '
git -c init.defaultBranch=main -c protocol.version=2 \
clone "file://$(pwd)/file_unborn_parent" \
file_unborn_child 2>stderr &&
- grep "refs/heads/mydefaultbranch" file_unborn_child/.git/HEAD &&
+ 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
'
@@ -295,7 +303,9 @@ test_expect_success 'bare clone propagates unborn HEAD from non-empty repo' '
git -c init.defaultBranch=main -c protocol.version=2 \
clone --bare "file://$(pwd)/file_unborn_parent" \
file_unborn_child.git 2>stderr &&
- grep "refs/heads/mydefaultbranch" file_unborn_child.git/HEAD &&
+ echo "refs/heads/mydefaultbranch" >expect &&
+ git -C file_unborn_child.git symbolic-ref HEAD >actual &&
+ test_cmp expect actual &&
! grep "warning:" stderr
'
@@ -315,7 +325,9 @@ test_expect_success 'defaulted HEAD uses remote branch if available' '
git -c init.defaultBranch=branchwithstuff -c protocol.version=2 \
clone "file://$(pwd)/file_unborn_parent" \
file_unborn_child 2>stderr &&
- grep "refs/heads/branchwithstuff" file_unborn_child/.git/HEAD &&
+ 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
'
@@ -766,6 +778,25 @@ test_expect_success 'archive with custom path does not request v2' '
! 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
#
. "$TEST_DIRECTORY"/lib-httpd.sh
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/t6005-rev-list-count.sh b/t/t6005-rev-list-count.sh
index 0729f80..ee0306a 100755
--- a/t/t6005-rev-list-count.sh
+++ b/t/t6005-rev-list-count.sh
@@ -18,20 +18,34 @@ test_expect_success 'no options' '
'
test_expect_success '--max-count' '
+ 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=10 &&
+ test_stdout_line_count = 5 git rev-list HEAD --max-count=-1
'
test_expect_success '--max-count all forms' '
+ 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 = 1 git rev-list HEAD -n 1 &&
+ test_stdout_line_count = 5 git rev-list HEAD -n -1
'
test_expect_success '--skip' '
+ 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 &&
diff --git a/t/t6009-rev-list-parent.sh b/t/t6009-rev-list-parent.sh
index ced4015..91db8fa 100755
--- a/t/t6009-rev-list-parent.sh
+++ b/t/t6009-rev-list-parent.sh
@@ -63,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
diff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh
index 67d523d..3b181f7 100755
--- a/t/t6018-rev-list-glob.sh
+++ b/t/t6018-rev-list-glob.sh
@@ -214,15 +214,13 @@ do
for pseudoopt in branches tags remotes
do
test_expect_success "rev-parse --exclude-hidden=$section fails with --$pseudoopt" '
- echo "error: --exclude-hidden cannot be used together with --$pseudoopt" >expected &&
test_must_fail git rev-parse --exclude-hidden=$section --$pseudoopt 2>err &&
- test_cmp expected err
+ test_grep "error: options .--exclude-hidden. and .--$pseudoopt. cannot be used together" err
'
test_expect_success "rev-parse --exclude-hidden=$section fails with --$pseudoopt=pattern" '
- echo "error: --exclude-hidden cannot be used together with --$pseudoopt" >expected &&
test_must_fail git rev-parse --exclude-hidden=$section --$pseudoopt=pattern 2>err &&
- test_cmp expected err
+ test_grep "error: options .--exclude-hidden. and .--$pseudoopt. cannot be used together" err
'
done
done
diff --git a/t/t6021-rev-list-exclude-hidden.sh b/t/t6021-rev-list-exclude-hidden.sh
index cdf7aa9..51df021 100755
--- a/t/t6021-rev-list-exclude-hidden.sh
+++ b/t/t6021-rev-list-exclude-hidden.sh
@@ -151,12 +151,12 @@ do
do
test_expect_success "$section: fails with --$pseudoopt" '
test_must_fail git rev-list --exclude-hidden=$section --$pseudoopt 2>err &&
- test_grep "error: --exclude-hidden cannot be used together with --$pseudoopt" 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: --exclude-hidden cannot be used together with --$pseudoopt" err
+ test_grep "error: options .--exclude-hidden. and .--$pseudoopt. cannot be used together" err
'
done
done
diff --git a/t/t6022-rev-list-missing.sh b/t/t6022-rev-list-missing.sh
index 40265a4..127180e 100755
--- a/t/t6022-rev-list-missing.sh
+++ b/t/t6022-rev-list-missing.sh
@@ -10,9 +10,18 @@ TEST_PASSES_SANITIZE_LEAK=true
test_expect_success 'create repository and alternate directory' '
test_commit 1 &&
test_commit 2 &&
- test_commit 3
+ 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" '
@@ -40,9 +49,10 @@ do
git rev-list --objects --no-object-names \
HEAD ^$obj >expect.raw &&
- # Blobs are shared by all commits, so evethough a commit/tree
+ # Blobs are shared by all commits, so even though a commit/tree
# might be skipped, its blob must be accounted for.
- if [ $obj != "HEAD:1.t" ]; then
+ 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 &&
@@ -71,4 +81,69 @@ do
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 2a5b7d8..cdc0270 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -170,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 &&
@@ -872,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' '
@@ -1176,7 +1182,7 @@ 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" &&
diff --git a/t/t6112-rev-list-filters-objects.sh b/t/t6112-rev-list-filters-objects.sh
index 52822b9..43e1afd 100755
--- a/t/t6112-rev-list-filters-objects.sh
+++ b/t/t6112-rev-list-filters-objects.sh
@@ -670,7 +670,7 @@ 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 || return 1
done &&
diff --git a/t/t6113-rev-list-bitmap-filters.sh b/t/t6113-rev-list-bitmap-filters.sh
index 86c7052..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 &&
diff --git a/t/t6135-pathspec-with-attrs.sh b/t/t6135-pathspec-with-attrs.sh
index a9c1e4e..120dcd7 100755
--- a/t/t6135-pathspec-with-attrs.sh
+++ b/t/t6135-pathspec-with-attrs.sh
@@ -64,12 +64,24 @@ test_expect_success 'setup .gitattributes' '
fileSetLabel label
fileValue label=foo
fileWrongLabel label☺
+ newFileA* labelA
+ newFileB* labelB
EOF
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
@@ -150,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
@@ -175,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
@@ -200,6 +214,7 @@ test_expect_success 'check unspecified attr (2)' '
test_expect_success 'check multiple unspecified attr' '
cat <<-\EOF >expect &&
.gitattributes
+ .gitignore
fileC
fileNoLabel
fileWrongLabel
@@ -239,16 +254,99 @@ test_expect_success 'fail on multiple attr specifiers in one pathspec item' '
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 &&
+ # 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' '
test_must_fail git ls-files . ":(attr:☺)"
'
@@ -295,4 +393,31 @@ test_expect_success 'reading from .gitattributes in a subdirectory (3)' '
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/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index 00a060d..eb6c820 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -20,12 +20,13 @@ setdate_and_increment () {
export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
}
-test_expect_success setup '
- test_oid_cache <<-EOF &&
- disklen sha1:138
- disklen sha256:154
- EOF
+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 '
# setup .mailmap
cat >.mailmap <<-EOF &&
A Thor <athor@example.com> A U Thor <author@example.com>
@@ -94,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
@@ -129,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)
@@ -203,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)
@@ -1335,6 +1335,73 @@ test_expect_success '--no-sort cancels the previous sort keys' '
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 &&
@@ -1818,6 +1885,28 @@ test_expect_success 'git for-each-ref with non-existing refs' '
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)"
diff --git a/t/t6301-for-each-ref-errors.sh b/t/t6301-for-each-ref-errors.sh
index 2667dd1..83b8a19 100755
--- a/t/t6301-for-each-ref-errors.sh
+++ b/t/t6301-for-each-ref-errors.sh
@@ -15,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" &&
@@ -25,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" &&
@@ -39,15 +39,14 @@ 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 &&
diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
index af223e4..948f1bb 100755
--- a/t/t6302-for-each-ref-filter.sh
+++ b/t/t6302-for-each-ref-filter.sh
@@ -31,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
@@ -45,8 +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 An annotated tag
- refs/tags/doubly-signed-tag A signed tag
+ 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/t6403-merge-file.sh b/t/t6403-merge-file.sh
index 2c92209..fb872c5 100755
--- a/t/t6403-merge-file.sh
+++ b/t/t6403-merge-file.sh
@@ -56,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' '
@@ -447,4 +507,66 @@ test_expect_success '--object-id fails without repository' '
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/t6406-merge-attr.sh b/t/t6406-merge-attr.sh
index 72f8c17..156a1ef 100755
--- a/t/t6406-merge-attr.sh
+++ b/t/t6406-merge-attr.sh
@@ -42,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 ==="
@@ -121,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" &&
@@ -132,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
@@ -142,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" &&
@@ -159,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 &&
@@ -173,7 +179,7 @@ test_expect_success !WINDOWS 'custom merge driver that is killed with a signal'
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" &&
diff --git a/t/t6413-merge-crlf.sh b/t/t6413-merge-crlf.sh
index b4f4a31..647ea1e 100755
--- a/t/t6413-merge-crlf.sh
+++ b/t/t6413-merge-crlf.sh
@@ -34,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/t6418-merge-text-auto.sh b/t/t6418-merge-text-auto.sh
index 41288a6..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
diff --git a/t/t6429-merge-sequence-rename-caching.sh b/t/t6429-merge-sequence-rename-caching.sh
index d02fa16..0f39ed0 100755
--- a/t/t6429-merge-sequence-rename-caching.sh
+++ b/t/t6429-merge-sequence-rename-caching.sh
@@ -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 &&
@@ -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
@@ -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 &&
@@ -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 &&
@@ -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
@@ -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 &&
@@ -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 &&
diff --git a/t/t6437-submodule-merge.sh b/t/t6437-submodule-merge.sh
index 7065052..7a3f1cb 100755
--- a/t/t6437-submodule-merge.sh
+++ b/t/t6437-submodule-merge.sh
@@ -113,7 +113,7 @@ test_expect_success 'merging should conflict for non fast-forward' '
git checkout -b test-nonforward-a b &&
if test "$GIT_TEST_MERGE_ALGORITHM" = ort
then
- test_must_fail git merge c >actual &&
+ 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
@@ -154,9 +154,9 @@ test_expect_success 'merging should conflict for non fast-forward (resolution ex
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" actual
+ grep "$sub_expect" sub-actual
else
test_must_fail git merge c 2> actual
fi &&
@@ -181,9 +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" actual
+ grep "$sub_expect" sub-actual
else
test_must_fail git merge c 2> actual
fi &&
@@ -227,7 +227,7 @@ 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 >actual &&
+ 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)" &&
@@ -535,7 +535,7 @@ test_expect_success 'merging should fail with no merge base' '
git checkout -b b init &&
git add sub &&
git commit -m "b" &&
- test_must_fail git merge a >actual &&
+ 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)" &&
diff --git a/t/t6500-gc.sh b/t/t6500-gc.sh
index 18fe1c2..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' '
@@ -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
) &&
@@ -146,13 +130,13 @@ 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_grep ! "^warning:" err &&
diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh
index f6aebe9..5ab4d41 100755
--- a/t/t7003-filter-branch.sh
+++ b/t/t7003-filter-branch.sh
@@ -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' '
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index e689db4..696866d 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -1777,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
@@ -1862,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
'
diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh
index 4287863..62d9f84 100755
--- a/t/t7102-reset.sh
+++ b/t/t7102-reset.sh
@@ -616,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/t7105-reset-patch.sh b/t/t7105-reset-patch.sh
index 05079c7..f4f3b7a 100755
--- a/t/t7105-reset-patch.sh
+++ b/t/t7105-reset-patch.sh
@@ -5,7 +5,7 @@ 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 &&
@@ -19,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_grep "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_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_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 &&
@@ -66,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 d20e570..88d1c8a 100755
--- a/t/t7106-reset-unborn-branch.sh
+++ b/t/t7106-reset-unborn-branch.sh
@@ -34,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 &&
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index 10cc6c4..42352dc 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -631,6 +631,72 @@ 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 &&
diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh
index 1a310a4..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
@@ -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 &&
@@ -517,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
'
@@ -559,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' '
@@ -573,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/
'
@@ -588,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/
'
diff --git a/t/t7301-clean-interactive.sh b/t/t7301-clean-interactive.sh
index d82a321..4afe53c 100755
--- a/t/t7301-clean-interactive.sh
+++ b/t/t7301-clean-interactive.sh
@@ -25,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
'
@@ -46,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
'
@@ -67,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
'
@@ -88,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
'
@@ -110,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
'
@@ -132,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
'
@@ -154,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
'
@@ -176,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
'
@@ -198,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
'
@@ -220,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
'
@@ -242,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
'
@@ -264,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
'
@@ -285,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
'
@@ -301,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
'
@@ -323,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
'
@@ -345,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
'
@@ -367,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
'
@@ -389,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
'
@@ -412,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
'
@@ -435,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
'
@@ -458,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 00c1f1a..9814888 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -212,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 &&
@@ -1452,4 +1451,35 @@ test_expect_success 'recursive clone respects -q' '
test_must_be_empty actual
'
+test_expect_success '`submodule init` and `init.templateDir`' '
+ mkdir -p tmpl/hooks &&
+ write_script tmpl/hooks/post-checkout <<-EOF &&
+ echo HOOK-RUN >&2
+ echo I was here >hook.run
+ exit 1
+ EOF
+
+ test_config init.templateDir "$(pwd)/tmpl" &&
+ test_when_finished \
+ "git config --global --unset init.templateDir || true" &&
+ (
+ sane_unset GIT_TEMPLATE_DIR &&
+ NO_SET_GIT_TEMPLATE_DIR=t &&
+ export NO_SET_GIT_TEMPLATE_DIR &&
+
+ git config --global init.templateDir "$(pwd)/tmpl" &&
+ test_must_fail git submodule \
+ add "$submodurl" sub-global 2>err &&
+ git config --global --unset init.templateDir &&
+ test_grep HOOK-RUN err &&
+ test_path_is_file sub-global/hook.run &&
+
+ git config init.templateDir "$(pwd)/tmpl" &&
+ git submodule add "$submodurl" sub-local 2>err &&
+ git config --unset init.templateDir &&
+ test_grep ! HOOK-RUN err &&
+ test_path_is_missing sub-local/hook.run
+ )
+'
+
test_done
diff --git a/t/t7402-submodule-rebase.sh b/t/t7402-submodule-rebase.sh
index 2b3c363..aa2fdc3 100755
--- a/t/t7402-submodule-rebase.sh
+++ b/t/t7402-submodule-rebase.sh
@@ -116,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 >actual_output &&
+ test_must_fail git rebase --onto HEAD^^ HEAD^ HEAD^0 2>actual_output &&
git ls-files -s submodule >actual &&
(
cd submodule &&
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index 8491b8c..297c6c3 100755
--- a/t/t7406-submodule-update.sh
+++ b/t/t7406-submodule-update.sh
@@ -1202,4 +1202,52 @@ test_expect_success 'commit with staged submodule change with ignoreSubmodules a
add_submodule_commit_and_validate
'
+test_expect_success CASE_INSENSITIVE_FS,SYMLINKS \
+ 'submodule paths must not follow symlinks' '
+
+ # This is only needed because we want to run this in a self-contained
+ # test without having to spin up an HTTP server; However, it would not
+ # be needed in a real-world scenario where the submodule is simply
+ # hosted on a public site.
+ test_config_global protocol.file.allow always &&
+
+ # Make sure that Git tries to use symlinks on Windows
+ test_config_global core.symlinks true &&
+
+ tell_tale_path="$PWD/tell.tale" &&
+ git init hook &&
+ (
+ cd hook &&
+ mkdir -p y/hooks &&
+ write_script y/hooks/post-checkout <<-EOF &&
+ echo HOOK-RUN >&2
+ echo hook-run >"$tell_tale_path"
+ EOF
+ git add y/hooks/post-checkout &&
+ test_tick &&
+ git commit -m post-checkout
+ ) &&
+
+ hook_repo_path="$(pwd)/hook" &&
+ git init captain &&
+ (
+ cd captain &&
+ git submodule add --name x/y "$hook_repo_path" A/modules/x &&
+ test_tick &&
+ git commit -m add-submodule &&
+
+ printf .git >dotgit.txt &&
+ git hash-object -w --stdin <dotgit.txt >dot-git.hash &&
+ printf "120000 %s 0\ta\n" "$(cat dot-git.hash)" >index.info &&
+ git update-index --index-info <index.info &&
+ test_tick &&
+ git commit -m add-symlink
+ ) &&
+
+ test_path_is_missing "$tell_tale_path" &&
+ git clone --recursive captain hooked 2>err &&
+ test_grep ! HOOK-RUN err &&
+ test_path_is_missing "$tell_tale_path"
+'
+
test_done
diff --git a/t/t7423-submodule-symlinks.sh b/t/t7423-submodule-symlinks.sh
new file mode 100755
index 0000000..3d3c7af
--- /dev/null
+++ b/t/t7423-submodule-symlinks.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+test_description='check that submodule operations do not follow symlinks'
+
+. ./test-lib.sh
+
+test_expect_success 'prepare' '
+ git config --global protocol.file.allow always &&
+ test_commit initial &&
+ git init upstream &&
+ test_commit -C upstream upstream submodule_file &&
+ git submodule add ./upstream a/sm &&
+ test_tick &&
+ git commit -m submodule
+'
+
+test_expect_success SYMLINKS 'git submodule update must not create submodule behind symlink' '
+ rm -rf a b &&
+ mkdir b &&
+ ln -s b a &&
+ test_path_is_missing b/sm &&
+ test_must_fail git submodule update &&
+ test_path_is_missing b/sm
+'
+
+test_expect_success SYMLINKS,CASE_INSENSITIVE_FS 'git submodule update must not create submodule behind symlink on case insensitive fs' '
+ rm -rf a b &&
+ mkdir b &&
+ ln -s b A &&
+ test_must_fail git submodule update &&
+ test_path_is_missing b/sm
+'
+
+prepare_symlink_to_repo() {
+ rm -rf a &&
+ mkdir a &&
+ git init a/target &&
+ git -C a/target fetch ../../upstream &&
+ ln -s target a/sm
+}
+
+test_expect_success SYMLINKS 'git restore --recurse-submodules must not be confused by a symlink' '
+ prepare_symlink_to_repo &&
+ test_must_fail git restore --recurse-submodules a/sm &&
+ test_path_is_missing a/sm/submodule_file &&
+ test_path_is_dir a/target/.git &&
+ test_path_is_missing a/target/submodule_file
+'
+
+test_expect_success SYMLINKS 'git restore --recurse-submodules must not migrate git dir of symlinked repo' '
+ prepare_symlink_to_repo &&
+ rm -rf .git/modules &&
+ test_must_fail git restore --recurse-submodules a/sm &&
+ test_path_is_dir a/target/.git &&
+ test_path_is_missing .git/modules/a/sm &&
+ test_path_is_missing a/target/submodule_file
+'
+
+test_expect_success SYMLINKS 'git checkout -f --recurse-submodules must not migrate git dir of symlinked repo when removing submodule' '
+ prepare_symlink_to_repo &&
+ rm -rf .git/modules &&
+ test_must_fail git checkout -f --recurse-submodules initial &&
+ test_path_is_dir a/target/.git &&
+ test_path_is_missing .git/modules/a/sm
+'
+
+test_done
diff --git a/t/t7450-bad-git-dotfiles.sh b/t/t7450-bad-git-dotfiles.sh
index 35a31ac..4a9c22c 100755
--- a/t/t7450-bad-git-dotfiles.sh
+++ b/t/t7450-bad-git-dotfiles.sh
@@ -45,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
@@ -294,7 +320,7 @@ test_expect_success WINDOWS 'prevent git~1 squatting on Windows' '
fi
'
-test_expect_success 'git dirs of sibling submodules must not be nested' '
+test_expect_success 'setup submodules with nested git dirs' '
git init nested &&
test_commit -C nested nested &&
(
@@ -312,9 +338,39 @@ test_expect_success 'git dirs of sibling submodules must not be nested' '
git add .gitmodules thing1 thing2 &&
test_tick &&
git commit -m nested
- ) &&
+ )
+'
+
+test_expect_success 'git dirs of sibling submodules must not be nested' '
test_must_fail git clone --recurse-submodules nested clone 2>err &&
test_grep "is inside git dir" err
'
+test_expect_success 'submodule git dir nesting detection must work with parallel cloning' '
+ test_must_fail git clone --recurse-submodules --jobs=2 nested clone_parallel 2>err &&
+ cat err &&
+ grep -E "(already exists|is inside git dir|not a git repository)" err &&
+ {
+ test_path_is_missing .git/modules/hippo/HEAD ||
+ test_path_is_missing .git/modules/hippo/hooks/HEAD
+ }
+'
+
+test_expect_success 'checkout -f --recurse-submodules must not use a nested gitdir' '
+ git clone nested nested_checkout &&
+ (
+ cd nested_checkout &&
+ git submodule init &&
+ git submodule update thing1 &&
+ mkdir -p .git/modules/hippo/hooks/refs &&
+ mkdir -p .git/modules/hippo/hooks/objects/info &&
+ echo "../../../../objects" >.git/modules/hippo/hooks/objects/info/alternates &&
+ echo "ref: refs/heads/master" >.git/modules/hippo/hooks/HEAD
+ ) &&
+ test_must_fail git -C nested_checkout checkout -f --recurse-submodules HEAD 2>err &&
+ cat err &&
+ grep "is inside git dir" err &&
+ test_path_is_missing nested_checkout/thing2/.git
+'
+
test_done
diff --git a/t/t7501-commit-basic-functionality.sh b/t/t7501-commit-basic-functionality.sh
index 3d8500a..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
@@ -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
@@ -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
diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
index 61c8e81..b37e201 100755
--- a/t/t7502-commit-porcelain.sh
+++ b/t/t7502-commit-porcelain.sh
@@ -485,6 +485,24 @@ test_expect_success 'commit --trailer not confused by --- separator' '
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 &&
@@ -718,6 +736,11 @@ test_expect_success 'message shows date when it is explicitly set' '
.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 &&
diff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh
index c3281b1..4c7db19 100755
--- a/t/t7507-commit-verbose.sh
+++ b/t/t7507-commit-verbose.sh
@@ -101,6 +101,16 @@ test_expect_success 'verbose diff is stripped out with set core.commentChar' '
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' '
git status >actual &&
! grep "^diff --git" actual
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index a3c18a4..773383f 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -419,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 &&
@@ -488,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
@@ -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" '
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index c2ab8a4..802f8f7 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -692,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 &&
diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
index 832aff0..3d3e13c 100755
--- a/t/t7513-interpret-trailers.sh
+++ b/t/t7513-interpret-trailers.sh
@@ -1916,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/t7527-builtin-fsmonitor.sh b/t/t7527-builtin-fsmonitor.sh
index 7850315..730f3c7 100755
--- a/t/t7527-builtin-fsmonitor.sh
+++ b/t/t7527-builtin-fsmonitor.sh
@@ -978,7 +978,7 @@ test_expect_success !UNICODE_COMPOSITION_SENSITIVE 'Unicode nfc/nfd' '
mkdir test_unicode/nfd &&
mkdir test_unicode/nfd/d_${utf8_nfd} &&
- git -C test_unicode fsmonitor--daemon stop &&
+ test-tool -C test_unicode fsmonitor-client query --token 0 &&
if test_have_prereq UNICODE_NFC_PRESERVED
then
@@ -1037,4 +1037,227 @@ test_expect_success 'split-index and FSMonitor work well together' '
)
'
+# 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/t7700-repack.sh b/t/t7700-repack.sh
index d2975e6..127efe9 100755
--- a/t/t7700-repack.sh
+++ b/t/t7700-repack.sh
@@ -271,7 +271,7 @@ test_expect_success 'repacking fails when missing .pack actually means missing o
ls .git/objects/pack/*.pack >before-pack-dir &&
test_must_fail git fsck &&
- test_must_fail git repack --cruft -d 2>err &&
+ 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
@@ -629,6 +629,7 @@ test_expect_success '--write-midx with preferred bitmap tips' '
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 &&
@@ -749,6 +750,7 @@ test_expect_success '--write-midx with --pack-kept-objects' '
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 &&
diff --git a/t/t7704-repack-cruft.sh b/t/t7704-repack-cruft.sh
index be3735d..71e1ef3 100755
--- a/t/t7704-repack-cruft.sh
+++ b/t/t7704-repack-cruft.sh
@@ -48,7 +48,7 @@ test_expect_success '--expire-to stores pruned objects (now)' '
# ...in other words, the combined contents of this
# repository and expired.git should be the same as the
# set of objects we started with.
- cat expired.objects remaining.objects | sort >actual &&
+ sort expired.objects remaining.objects >actual &&
test_cmp expect actual &&
# The "moved" objects (i.e., those in expired.git)
diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh
index 6a36be1..cc917b2 100755
--- a/t/t7800-difftool.sh
+++ b/t/t7800-difftool.sh
@@ -91,58 +91,67 @@ 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
-'
-
-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 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
-'
-
-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 honors difftool.trustExitCode = false' '
- test_config difftool.error.cmd false &&
- test_config difftool.trustExitCode false &&
- git difftool -y -t error branch
-'
-
-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
-'
-
-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 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
-'
+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 honors --gui' '
difftool_test_setup &&
diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh
index 935df6a..0943dfa 100755
--- a/t/t7900-maintenance.sh
+++ b/t/t7900-maintenance.sh
@@ -67,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 &&
@@ -157,7 +202,8 @@ 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 &&
diff --git a/t/t8010-cat-file-filters.sh b/t/t8010-cat-file-filters.sh
index ca04242..eb64b76 100755
--- a/t/t8010-cat-file-filters.sh
+++ b/t/t8010-cat-file-filters.sh
@@ -43,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/t8013-blame-ignore-revs.sh b/t/t8013-blame-ignore-revs.sh
index 9a03b0f..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,10 +121,10 @@ 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 '
@@ -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/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/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/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/t9133-git-svn-nested-git-repo.sh b/t/t9133-git-svn-nested-git-repo.sh
index d8d5362..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 &&
diff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh
index 09606f1..926ac81 100755
--- a/t/t9146-git-svn-empty-dirs.sh
+++ b/t/t9146-git-svn-empty-dirs.sh
@@ -20,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
)
'
@@ -37,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
)
'
@@ -52,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' '
@@ -62,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
)
'
@@ -78,25 +66,13 @@ 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
)
'
@@ -114,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
)
'
@@ -129,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
@@ -143,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/t9164-git-svn-dcommit-concurrent.sh b/t/t9164-git-svn-dcommit-concurrent.sh
index c8e6c07..d1dec89 100755
--- a/t/t9164-git-svn-dcommit-concurrent.sh
+++ b/t/t9164-git-svn-dcommit-concurrent.sh
@@ -46,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
@@ -63,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/t9210-scalar.sh b/t/t9210-scalar.sh
index 4432a30..428339e 100755
--- a/t/t9210-scalar.sh
+++ b/t/t9210-scalar.sh
@@ -154,7 +154,14 @@ test_expect_success 'scalar clone' '
test_cmp expect actual &&
test_path_is_missing 1/2 &&
- test_must_fail git rev-list --missing=print $second &&
+
+ # 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 &&
diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
index dbb5042..1e68426 100755
--- a/t/t9300-fast-import.sh
+++ b/t/t9300-fast-import.sh
@@ -986,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)
'
@@ -1059,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
@@ -1259,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 &&
@@ -1431,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
@@ -2007,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
'
@@ -2048,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
'
@@ -2088,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
'
@@ -2115,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
'
@@ -2146,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)
###
@@ -2794,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
@@ -3064,21 +3071,283 @@ test_expect_success 'S: ls with garbage after sha1 must fail' '
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 &&
@@ -3180,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/t9350-fast-export.sh b/t/t9350-fast-export.sh
index 26c25c0..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
'
@@ -537,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 &&
@@ -791,4 +791,14 @@ test_expect_success 'fast-export --first-parent outputs all revisions output by
)
'
+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/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh
index 003c0b6..e499c7f 100755
--- a/t/t9400-git-cvsserver-server.sh
+++ b/t/t9400-git-cvsserver-server.sh
@@ -117,12 +117,12 @@ END VERIFICATION REQUEST
EOF
test_expect_success 'pserver authentication' '
- cat request-anonymous | git-cvsserver pserver >log 2>&1 &&
+ 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
+ if git-cvsserver pserver <request-git >log 2>&1
then
false
else
@@ -132,17 +132,17 @@ test_expect_success 'pserver authentication failure (non-anonymous user)' '
'
test_expect_success 'pserver authentication success (non-anonymous user with password)' '
- cat login-git-ok | git-cvsserver pserver >log 2>&1 &&
+ 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 &&
+ 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
+ if git-cvsserver pserver <login-git >log 2>&1
then
false
else
@@ -172,7 +172,7 @@ Root $WORKDIR
EOF
test_expect_success 'req_Root failure (relative pathname)' '
- if cat request-relative | git-cvsserver pserver >log 2>&1
+ if git-cvsserver pserver <request-relative >log 2>&1
then
echo unexpected success
false
@@ -183,28 +183,26 @@ test_expect_success 'req_Root failure (relative pathname)' '
'
test_expect_success 'req_Root failure (conflicting roots)' '
- cat request-conflict | git-cvsserver pserver >log 2>&1 &&
+ 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 &&
+ 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 &&
+ 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
@@ -217,27 +215,26 @@ 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 &&
+ 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 &&
+ 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 directory list)' '
- ! (cat request-anonymous | git-cvsserver --export-all pserver >log 2>&1 || false)'
+ ! (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 &&
+ git-cvsserver --export-all --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" <request-base >log 2>&1 &&
sed -ne \$p log | grep "^I LOVE YOU\$"
'
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/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/test.pl b/t/t9700/test.pl
index 6d75370..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;
diff --git a/t/t9801-git-p4-branch.sh b/t/t9801-git-p4-branch.sh
index 73cca0d..c598011 100755
--- a/t/t9801-git-p4-branch.sh
+++ b/t/t9801-git-p4-branch.sh
@@ -466,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 2a6ee2a..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 &&
diff --git a/t/t9807-git-p4-submit.sh b/t/t9807-git-p4-submit.sh
index af4b286..6ae7ced 100755
--- a/t/t9807-git-p4-submit.sh
+++ b/t/t9807-git-p4-submit.sh
@@ -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
)
'
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/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/t9902-completion.sh b/t/t9902-completion.sh
index a7c3b4e..569cf23 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -5,6 +5,17 @@
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
+
. ./lib-bash.sh
complete ()
@@ -87,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.
@@ -1250,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
@@ -1259,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
@@ -1571,7 +1743,7 @@ test_expect_success FUNNYNAMES,!CYGWIN 'cone mode sparse-checkout completes dire
)
'
-test_expect_success 'non-cone mode sparse-checkout uses bash completion' '
+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 &&
@@ -1581,7 +1753,12 @@ test_expect_success 'non-cone mode sparse-checkout uses bash completion' '
# 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
)
'
@@ -1920,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 &&
@@ -2562,6 +2747,35 @@ test_expect_success 'git config - variable name include' '
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
@@ -2613,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
@@ -2715,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/test-lib-functions.sh b/t/test-lib-functions.sh
index 9c3cf12..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 ...
@@ -385,7 +385,7 @@ test_commit () {
shift
done &&
indir=${indir:+"$indir"/} &&
- local file=${2:-"$1.t"} &&
+ local file="${2:-"$1.t"}" &&
if test -n "$append"
then
$echo "${3-$1}" >>"$indir$file"
@@ -1263,9 +1263,8 @@ test_cmp_bin () {
cmp "$@"
}
-# Deprecated - do not use this in new code
test_i18ngrep () {
- test_grep "$@"
+ BUG "do not use test_i18ngrep---use test_grep instead"
}
test_grep () {
@@ -1277,7 +1276,7 @@ test_grep () {
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"
@@ -1656,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
@@ -1708,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;;
@@ -1729,7 +1748,7 @@ test_oid () {
# 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"
}
@@ -1746,7 +1765,7 @@ test_parse_ls_tree_oids () {
# 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
@@ -1821,7 +1840,7 @@ test_subcommand () {
shift
fi
- local expr=$(printf '"%s",' "$@")
+ local expr="$(printf '"%s",' "$@")"
expr="${expr%,}"
if test -n "$negate"
@@ -1874,6 +1893,20 @@ 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() {
@@ -1897,7 +1930,7 @@ test_readlink () {
# An optional increment to the magic timestamp may be specified as second
# argument.
test_set_magic_mtime () {
- local inc=${2:-0} &&
+ local inc="${2:-0}" &&
local mtime=$((1234567890 + $inc)) &&
test-tool chmtime =$mtime "$1" &&
test_is_magic_mtime "$1" $inc
@@ -1910,7 +1943,7 @@ test_set_magic_mtime () {
# 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 inc="${2:-0}" &&
local mtime=$((1234567890 + $inc)) &&
echo $mtime >.git/test-mtime-expect &&
test-tool chmtime --get "$1" >.git/test-mtime-actual &&
diff --git a/t/test-lib-github-workflow-markup.sh b/t/test-lib-github-workflow-markup.sh
index 9c5339c..33405c9 100644
--- a/t/test-lib-github-workflow-markup.sh
+++ b/t/test-lib-github-workflow-markup.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 idea is for `test-lib.sh` to source this file when run in GitHub
# workflows; these functions will then override (empty) functions
@@ -42,8 +42,8 @@ finalize_test_case_output () {
fixed)
echo >>$github_markup_output "::notice::fixed: $this_test.$test_count $1"
;;
- ok)
- # Exit without printing the "ok" tests
+ ok|broken)
+ # Exit without printing the "ok" or ""broken" tests
return
;;
esac
diff --git a/t/test-lib-junit.sh b/t/test-lib-junit.sh
index 79c31c7..76cbbd3 100644
--- a/t/test-lib-junit.sh
+++ b/t/test-lib-junit.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/ .
#
# 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
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 1656c9e..79d3e0e 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -13,7 +13,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/ .
# Test the binaries we have just built. The tests are kept in
# t/ subdirectory and are run in 'trash directory' subdirectory.
@@ -542,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
@@ -1295,6 +1297,11 @@ test_done () {
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)"
@@ -1745,7 +1752,16 @@ 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
@@ -1936,12 +1952,17 @@ test_lazy_prereq SHA1 '
esac
'
+test_lazy_prereq DEFAULT_REPO_FORMAT '
+ test_have_prereq SHA1,REFFILES
+'
+
# Ensure that no test accidentally triggers a Git command
# that runs the actual maintenance scheduler, affecting a user's
# system permanently.
# 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`
#
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"
;;
*)
diff --git a/tempfile.c b/tempfile.c
index ecdebf1..ed88cf8 100644
--- a/tempfile.c
+++ b/tempfile.c
@@ -50,15 +50,17 @@
static VOLATILE_LIST_HEAD(tempfile_list);
-static void remove_template_directory(struct tempfile *tempfile,
+static int remove_template_directory(struct tempfile *tempfile,
int in_signal_handler)
{
if (tempfile->directory) {
if (in_signal_handler)
- rmdir(tempfile->directory);
+ return rmdir(tempfile->directory);
else
- rmdir_or_warn(tempfile->directory);
+ return rmdir_or_warn(tempfile->directory);
}
+
+ return 0;
}
static void remove_tempfiles(int in_signal_handler)
@@ -353,16 +355,19 @@ int rename_tempfile(struct tempfile **tempfile_p, const char *path)
return 0;
}
-void delete_tempfile(struct tempfile **tempfile_p)
+int delete_tempfile(struct tempfile **tempfile_p)
{
struct tempfile *tempfile = *tempfile_p;
+ int err = 0;
if (!is_tempfile_active(tempfile))
- return;
+ return 0;
- close_tempfile_gently(tempfile);
- unlink_or_warn(tempfile->filename.buf);
- remove_template_directory(tempfile, 0);
+ err |= close_tempfile_gently(tempfile);
+ err |= unlink_or_warn(tempfile->filename.buf);
+ err |= remove_template_directory(tempfile, 0);
deactivate_tempfile(tempfile);
*tempfile_p = NULL;
+
+ return err ? -1 : 0;
}
diff --git a/tempfile.h b/tempfile.h
index d0413af..2d2ae5b 100644
--- a/tempfile.h
+++ b/tempfile.h
@@ -269,7 +269,7 @@ int reopen_tempfile(struct tempfile *tempfile);
* `delete_tempfile()` for a `tempfile` object that has already been
* deleted or renamed.
*/
-void delete_tempfile(struct tempfile **tempfile_p);
+int delete_tempfile(struct tempfile **tempfile_p);
/*
* Close the file descriptor and/or file pointer if they are still
diff --git a/templates/hooks--pre-commit.sample b/templates/hooks--pre-commit.sample
index e144712..29ed5ee 100755
--- a/templates/hooks--pre-commit.sample
+++ b/templates/hooks--pre-commit.sample
@@ -28,7 +28,7 @@ if [ "$allownonascii" != "true" ] &&
# Note that the use of brackets around a tr range is ok here, (it's
# even required, for portability to Solaris 10's /usr/bin/tr), since
# the square bracket bytes happen to fall in the designated range.
- test $(git diff --cached --name-only --diff-filter=A -z $against |
+ test $(git diff-index --cached --name-only --diff-filter=A -z $against |
LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
then
cat <<\EOF
diff --git a/tmp-objdir.c b/tmp-objdir.c
index 5f9074a..3509258 100644
--- a/tmp-objdir.c
+++ b/tmp-objdir.c
@@ -6,7 +6,6 @@
#include "environment.h"
#include "object-file.h"
#include "path.h"
-#include "sigchain.h"
#include "string-list.h"
#include "strbuf.h"
#include "strvec.h"
diff --git a/trace.c b/trace.c
index 971a68a..8669ddf 100644
--- a/trace.c
+++ b/trace.c
@@ -18,7 +18,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/>.
*/
#include "git-compat-util.h"
diff --git a/trace2.c b/trace2.c
index 6dc74df..f894532 100644
--- a/trace2.c
+++ b/trace2.c
@@ -1,12 +1,9 @@
#include "git-compat-util.h"
#include "config.h"
-#include "json-writer.h"
-#include "quote.h"
#include "repository.h"
#include "run-command.h"
#include "sigchain.h"
#include "thread-utils.h"
-#include "version.h"
#include "trace.h"
#include "trace2.h"
#include "trace2/tr2_cfg.h"
@@ -20,6 +17,7 @@
#include "trace2/tr2_tmr.h"
static int trace2_enabled;
+static int trace2_redact = 1;
static int tr2_next_child_id; /* modify under lock */
static int tr2_next_exec_id; /* modify under lock */
@@ -227,6 +225,8 @@ void trace2_initialize_fl(const char *file, int line)
if (!tr2_tgt_want_builtins())
return;
trace2_enabled = 1;
+ if (!git_env_bool("GIT_TRACE2_REDACT", 1))
+ trace2_redact = 0;
tr2_sid_get();
@@ -247,12 +247,93 @@ int trace2_is_enabled(void)
return trace2_enabled;
}
+/*
+ * Redacts an argument, i.e. ensures that no password in
+ * https://user:password@host/-style URLs is logged.
+ *
+ * Returns the original if nothing needed to be redacted.
+ * Returns a pointer that needs to be `free()`d otherwise.
+ */
+static const char *redact_arg(const char *arg)
+{
+ const char *p, *colon;
+ size_t at;
+
+ if (!trace2_redact ||
+ (!skip_prefix(arg, "https://", &p) &&
+ !skip_prefix(arg, "http://", &p)))
+ return arg;
+
+ at = strcspn(p, "@/");
+ if (p[at] != '@')
+ return arg;
+
+ colon = memchr(p, ':', at);
+ if (!colon)
+ return arg;
+
+ return xstrfmt("%.*s:<REDACTED>%s", (int)(colon - arg), arg, p + at);
+}
+
+/*
+ * Redacts arguments in an argument list.
+ *
+ * Returns the original if nothing needed to be redacted.
+ * Otherwise, returns a new array that needs to be released
+ * via `free_redacted_argv()`.
+ */
+static const char **redact_argv(const char **argv)
+{
+ int i, j;
+ const char *redacted = NULL;
+ const char **ret;
+
+ if (!trace2_redact)
+ return argv;
+
+ for (i = 0; argv[i]; i++)
+ if ((redacted = redact_arg(argv[i])) != argv[i])
+ break;
+
+ if (!argv[i])
+ return argv;
+
+ for (j = 0; argv[j]; j++)
+ ; /* keep counting */
+
+ ALLOC_ARRAY(ret, j + 1);
+ ret[j] = NULL;
+
+ for (j = 0; j < i; j++)
+ ret[j] = argv[j];
+ ret[i] = redacted;
+ for (++i; argv[i]; i++) {
+ redacted = redact_arg(argv[i]);
+ ret[i] = redacted ? redacted : argv[i];
+ }
+
+ return ret;
+}
+
+static void free_redacted_argv(const char **redacted, const char **argv)
+{
+ int i;
+
+ if (redacted != argv) {
+ for (i = 0; argv[i]; i++)
+ if (redacted[i] != argv[i])
+ free((void *)redacted[i]);
+ free((void *)redacted);
+ }
+}
+
void trace2_cmd_start_fl(const char *file, int line, const char **argv)
{
struct tr2_tgt *tgt_j;
int j;
uint64_t us_now;
uint64_t us_elapsed_absolute;
+ const char **redacted;
if (!trace2_enabled)
return;
@@ -260,10 +341,14 @@ void trace2_cmd_start_fl(const char *file, int line, const char **argv)
us_now = getnanotime() / 1000;
us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
+ redacted = redact_argv(argv);
+
for_each_wanted_builtin (j, tgt_j)
if (tgt_j->pfn_start_fl)
tgt_j->pfn_start_fl(file, line, us_elapsed_absolute,
- argv);
+ redacted);
+
+ free_redacted_argv(redacted, argv);
}
void trace2_cmd_exit_fl(const char *file, int line, int code)
@@ -348,6 +433,9 @@ void trace2_cmd_name_fl(const char *file, int line, const char *name)
for_each_wanted_builtin (j, tgt_j)
if (tgt_j->pfn_command_name_fl)
tgt_j->pfn_command_name_fl(file, line, name, hierarchy);
+
+ trace2_cmd_list_config();
+ trace2_cmd_list_env_vars();
}
void trace2_cmd_mode_fl(const char *file, int line, const char *mode)
@@ -379,17 +467,29 @@ void trace2_cmd_alias_fl(const char *file, int line, const char *alias,
void trace2_cmd_list_config_fl(const char *file, int line)
{
+ static int emitted = 0;
+
if (!trace2_enabled)
return;
+ if (emitted)
+ return;
+ emitted = 1;
+
tr2_cfg_list_config_fl(file, line);
}
void trace2_cmd_list_env_vars_fl(const char *file, int line)
{
+ static int emitted = 0;
+
if (!trace2_enabled)
return;
+ if (emitted)
+ return;
+ emitted = 1;
+
tr2_list_env_vars_fl(file, line);
}
@@ -409,6 +509,7 @@ void trace2_child_start_fl(const char *file, int line,
int j;
uint64_t us_now;
uint64_t us_elapsed_absolute;
+ const char **orig_argv = cmd->args.v;
if (!trace2_enabled)
return;
@@ -419,10 +520,24 @@ void trace2_child_start_fl(const char *file, int line,
cmd->trace2_child_id = tr2tls_locked_increment(&tr2_next_child_id);
cmd->trace2_child_us_start = us_now;
+ /*
+ * The `pfn_child_start_fl` API takes a `struct child_process`
+ * rather than a simple `argv` for the child because some
+ * targets make use of the additional context bits/values. So
+ * temporarily replace the original argv (inside the `strvec`)
+ * with a possibly redacted version.
+ */
+ cmd->args.v = redact_argv(orig_argv);
+
for_each_wanted_builtin (j, tgt_j)
if (tgt_j->pfn_child_start_fl)
tgt_j->pfn_child_start_fl(file, line,
us_elapsed_absolute, cmd);
+
+ if (cmd->args.v != orig_argv) {
+ free_redacted_argv(cmd->args.v, orig_argv);
+ cmd->args.v = orig_argv;
+ }
}
void trace2_child_exit_fl(const char *file, int line, struct child_process *cmd,
@@ -493,6 +608,7 @@ int trace2_exec_fl(const char *file, int line, const char *exe,
int exec_id;
uint64_t us_now;
uint64_t us_elapsed_absolute;
+ const char **redacted;
if (!trace2_enabled)
return -1;
@@ -502,10 +618,14 @@ int trace2_exec_fl(const char *file, int line, const char *exe,
exec_id = tr2tls_locked_increment(&tr2_next_exec_id);
+ redacted = redact_argv(argv);
+
for_each_wanted_builtin (j, tgt_j)
if (tgt_j->pfn_exec_fl)
tgt_j->pfn_exec_fl(file, line, us_elapsed_absolute,
- exec_id, exe, argv);
+ exec_id, exe, redacted);
+
+ free_redacted_argv(redacted, argv);
return exec_id;
}
@@ -637,13 +757,19 @@ void trace2_def_param_fl(const char *file, int line, const char *param,
{
struct tr2_tgt *tgt_j;
int j;
+ const char *redacted;
if (!trace2_enabled)
return;
+ redacted = redact_arg(value);
+
for_each_wanted_builtin (j, tgt_j)
if (tgt_j->pfn_param_fl)
- tgt_j->pfn_param_fl(file, line, param, value, kvi);
+ tgt_j->pfn_param_fl(file, line, param, redacted, kvi);
+
+ if (redacted != value)
+ free((void *)redacted);
}
void trace2_def_repo_fl(const char *file, int line, struct repository *repo)
diff --git a/trace2.h b/trace2.h
index 40d8c2e..1f0669b 100644
--- a/trace2.h
+++ b/trace2.h
@@ -337,8 +337,8 @@ struct key_value_info;
void trace2_def_param_fl(const char *file, int line, const char *param,
const char *value, const struct key_value_info *kvi);
-#define trace2_def_param(param, value) \
- trace2_def_param_fl(__FILE__, __LINE__, (param), (value))
+#define trace2_def_param(param, value, kvi) \
+ trace2_def_param_fl(__FILE__, __LINE__, (param), (value), (kvi))
/*
* Tell trace2 about a newly instantiated repo object and assign
diff --git a/trace2/tr2_ctr.c b/trace2/tr2_ctr.c
index 87cf903..d3a3371 100644
--- a/trace2/tr2_ctr.c
+++ b/trace2/tr2_ctr.c
@@ -1,5 +1,4 @@
#include "git-compat-util.h"
-#include "thread-utils.h"
#include "trace2/tr2_tgt.h"
#include "trace2/tr2_tls.h"
#include "trace2/tr2_ctr.h"
diff --git a/trace2/tr2_sysenv.c b/trace2/tr2_sysenv.c
index d3ecac2..048cdd5 100644
--- a/trace2/tr2_sysenv.c
+++ b/trace2/tr2_sysenv.c
@@ -68,6 +68,8 @@ static int tr2_sysenv_cb(const char *key, const char *value,
for (k = 0; k < ARRAY_SIZE(tr2_sysenv_settings); k++) {
if (!strcmp(key, tr2_sysenv_settings[k].git_config_name)) {
+ if (!value)
+ return config_error_nonbool(key);
free(tr2_sysenv_settings[k].value);
tr2_sysenv_settings[k].value = xstrdup(value);
return 0;
diff --git a/trace2/tr2_tgt_normal.c b/trace2/tr2_tgt_normal.c
index 38d5ebd..baef48a 100644
--- a/trace2/tr2_tgt_normal.c
+++ b/trace2/tr2_tgt_normal.c
@@ -2,6 +2,7 @@
#include "config.h"
#include "repository.h"
#include "run-command.h"
+#include "strbuf.h"
#include "quote.h"
#include "version.h"
#include "trace2/tr2_dst.h"
diff --git a/trace2/tr2_tls.c b/trace2/tr2_tls.c
index 601c9e5..4f75392 100644
--- a/trace2/tr2_tls.c
+++ b/trace2/tr2_tls.c
@@ -1,4 +1,5 @@
#include "git-compat-util.h"
+#include "strbuf.h"
#include "thread-utils.h"
#include "trace.h"
#include "trace2/tr2_tls.h"
diff --git a/trace2/tr2_tls.h b/trace2/tr2_tls.h
index f904980..3dfe655 100644
--- a/trace2/tr2_tls.h
+++ b/trace2/tr2_tls.h
@@ -1,7 +1,6 @@
#ifndef TR2_TLS_H
#define TR2_TLS_H
-#include "strbuf.h"
#include "trace2/tr2_ctr.h"
#include "trace2/tr2_tmr.h"
diff --git a/trace2/tr2_tmr.c b/trace2/tr2_tmr.c
index 31d0e4d..51f564b 100644
--- a/trace2/tr2_tmr.c
+++ b/trace2/tr2_tmr.c
@@ -1,5 +1,4 @@
#include "git-compat-util.h"
-#include "thread-utils.h"
#include "trace2/tr2_tgt.h"
#include "trace2/tr2_tls.h"
#include "trace2/tr2_tmr.h"
diff --git a/trailer.c b/trailer.c
index b6de5d9..c72ae68 100644
--- a/trailer.c
+++ b/trailer.c
@@ -5,7 +5,6 @@
#include "string-list.h"
#include "run-command.h"
#include "commit.h"
-#include "tempfile.h"
#include "trailer.h"
#include "list.h"
/*
@@ -145,37 +144,6 @@ static char last_non_space_char(const char *s)
return '\0';
}
-static void print_tok_val(FILE *outfile, const char *tok, const char *val)
-{
- char c;
-
- if (!tok) {
- fprintf(outfile, "%s\n", val);
- return;
- }
-
- c = last_non_space_char(tok);
- if (!c)
- return;
- if (strchr(separators, c))
- fprintf(outfile, "%s%s\n", tok, val);
- else
- fprintf(outfile, "%s%c %s\n", tok, separators[0], val);
-}
-
-static void print_all(FILE *outfile, struct list_head *head,
- const struct process_trailer_options *opts)
-{
- struct list_head *pos;
- struct trailer_item *item;
- list_for_each(pos, head) {
- item = list_entry(pos, struct trailer_item, list);
- if ((!opts->trim_empty || strlen(item->value) > 0) &&
- (!opts->only_trailers || item->token))
- print_tok_val(outfile, item->token, item->value);
- }
-}
-
static struct trailer_item *trailer_from_arg(struct arg_item *arg_tok)
{
struct trailer_item *new_item = xcalloc(1, sizeof(*new_item));
@@ -366,8 +334,8 @@ static int find_same_and_apply_arg(struct list_head *head,
return 0;
}
-static void process_trailers_lists(struct list_head *head,
- struct list_head *arg_head)
+void process_trailers_lists(struct list_head *head,
+ struct list_head *arg_head)
{
struct list_head *pos, *p;
struct arg_item *arg_tok;
@@ -507,6 +475,8 @@ static int git_trailer_default_config(const char *conf_key, const char *value,
warning(_("unknown value '%s' for key '%s'"),
value, conf_key);
} else if (!strcmp(trailer_item, "separators")) {
+ if (!value)
+ return config_error_nonbool(conf_key);
separators = xstrdup(value);
}
}
@@ -551,16 +521,22 @@ static int git_trailer_config(const char *conf_key, const char *value,
case TRAILER_KEY:
if (conf->key)
warning(_("more than one %s"), conf_key);
+ if (!value)
+ return config_error_nonbool(conf_key);
conf->key = xstrdup(value);
break;
case TRAILER_COMMAND:
if (conf->command)
warning(_("more than one %s"), conf_key);
+ if (!value)
+ return config_error_nonbool(conf_key);
conf->command = xstrdup(value);
break;
case TRAILER_CMD:
if (conf->cmd)
warning(_("more than one %s"), conf_key);
+ if (!value)
+ return config_error_nonbool(conf_key);
conf->cmd = xstrdup(value);
break;
case TRAILER_WHERE:
@@ -581,7 +557,7 @@ static int git_trailer_config(const char *conf_key, const char *value,
return 0;
}
-static void ensure_configured(void)
+void trailer_config_init(void)
{
if (configured)
return;
@@ -711,7 +687,7 @@ static void add_arg_item(struct list_head *arg_head, char *tok, char *val,
list_add_tail(&new_item->list, arg_head);
}
-static void parse_trailers_from_config(struct list_head *config_head)
+void parse_trailers_from_config(struct list_head *config_head)
{
struct arg_item *item;
struct list_head *pos;
@@ -727,8 +703,8 @@ static void parse_trailers_from_config(struct list_head *config_head)
}
}
-static void parse_trailers_from_command_line_args(struct list_head *arg_head,
- struct list_head *new_trailer_head)
+void parse_trailers_from_command_line_args(struct list_head *arg_head,
+ struct list_head *new_trailer_head)
{
struct strbuf tok = STRBUF_INIT;
struct strbuf val = STRBUF_INIT;
@@ -767,17 +743,6 @@ static void parse_trailers_from_command_line_args(struct list_head *arg_head,
free(cl_separators);
}
-static void read_input_file(struct strbuf *sb, const char *file)
-{
- if (file) {
- if (strbuf_read_file(sb, file, 0) < 0)
- die_errno(_("could not read input file '%s'"), file);
- } else {
- if (strbuf_read(sb, fileno(stdin), 0) < 0)
- die_errno(_("could not read from stdin"));
- }
-}
-
static const char *next_line(const char *str)
{
const char *nl = strchrnul(str, '\n');
@@ -809,28 +774,55 @@ static ssize_t last_line(const char *buf, size_t len)
}
/*
- * Return the position of the start of the patch or the length of str if there
- * is no patch in the message.
+ * Find the end of the log message as an offset from the start of the input
+ * (where callers of this function are interested in looking for a trailers
+ * block in the same input). We have to consider two categories of content that
+ * can come at the end of the input which we want to ignore (because they don't
+ * belong in the log message):
+ *
+ * (1) the "patch part" which begins with a "---" divider and has patch
+ * information (like the output of git-format-patch), and
+ *
+ * (2) any trailing comment lines, blank lines like in the output of "git
+ * commit -v", or stuff below the "cut" (scissor) line.
+ *
+ * As a formula, the situation looks like this:
+ *
+ * INPUT = LOG MESSAGE + IGNORED
+ *
+ * where IGNORED can be either of the two categories described above. It may be
+ * that there is nothing to ignore. Now it may be the case that the LOG MESSAGE
+ * contains a trailer block, but that's not the concern of this function.
*/
-static size_t find_patch_start(const char *str)
+static size_t find_end_of_log_message(const char *input, int no_divider)
{
+ size_t end;
const char *s;
- for (s = str; *s; s = next_line(s)) {
- const char *v;
+ /* Assume the naive end of the input is already what we want. */
+ end = strlen(input);
+
+ /* Optionally skip over any patch part ("---" line and below). */
+ if (!no_divider) {
+ for (s = input; *s; s = next_line(s)) {
+ const char *v;
- if (skip_prefix(s, "---", &v) && isspace(*v))
- return s - str;
+ if (skip_prefix(s, "---", &v) && isspace(*v)) {
+ end = s - input;
+ break;
+ }
+ }
}
- return s - str;
+ /* Skip over other ignorable bits. */
+ return end - ignored_log_message_bytes(input, end);
}
/*
* Return the position of the first trailer line or len if there are no
* trailers.
*/
-static size_t find_trailer_start(const char *buf, size_t len)
+static size_t find_trailer_block_start(const char *buf, size_t len)
{
const char *s;
ssize_t end_of_title, l;
@@ -847,7 +839,7 @@ static size_t find_trailer_start(const char *buf, size_t len)
/* The first paragraph is the title and cannot be trailers */
for (s = buf; s < buf + len; s = next_line(s)) {
- if (s[0] == comment_line_char)
+ if (starts_with_mem(s, buf + len - s, comment_line_str))
continue;
if (is_blank_line(s))
break;
@@ -867,7 +859,7 @@ static size_t find_trailer_start(const char *buf, size_t len)
const char **p;
ssize_t separator_pos;
- if (bol[0] == comment_line_char) {
+ if (starts_with_mem(bol, buf + len - bol, comment_line_str)) {
non_trailer_lines += possible_continuation_lines;
possible_continuation_lines = 0;
continue;
@@ -925,12 +917,6 @@ continue_outer_loop:
return len;
}
-/* Return the position of the end of the trailers. */
-static size_t find_trailer_end(const char *buf, size_t len)
-{
- return len - ignore_non_trailer(buf, len);
-}
-
static int ends_with_blank_line(const char *buf, size_t len)
{
ssize_t ll = last_line(buf, len);
@@ -970,21 +956,21 @@ static void unfold_value(struct strbuf *val)
* Parse trailers in "str", populating the trailer info and "head"
* linked list structure.
*/
-static void parse_trailers(struct trailer_info *info,
- const char *str,
- struct list_head *head,
- const struct process_trailer_options *opts)
+void parse_trailers(const struct process_trailer_options *opts,
+ struct trailer_info *info,
+ const char *str,
+ struct list_head *head)
{
struct strbuf tok = STRBUF_INIT;
struct strbuf val = STRBUF_INIT;
size_t i;
- trailer_info_get(info, str, opts);
+ trailer_info_get(opts, str, info);
for (i = 0; i < info->trailer_nr; i++) {
int separator_pos;
char *trailer = info->trailers[i];
- if (trailer[0] == comment_line_char)
+ if (starts_with(trailer, comment_line_str))
continue;
separator_pos = find_separator(trailer, separators);
if (separator_pos >= 1) {
@@ -1005,120 +991,32 @@ static void parse_trailers(struct trailer_info *info,
}
}
-static void free_all(struct list_head *head)
+void free_trailers(struct list_head *trailers)
{
struct list_head *pos, *p;
- list_for_each_safe(pos, p, head) {
+ list_for_each_safe(pos, p, trailers) {
list_del(pos);
free_trailer_item(list_entry(pos, struct trailer_item, list));
}
}
-static struct tempfile *trailers_tempfile;
-
-static FILE *create_in_place_tempfile(const char *file)
+void trailer_info_get(const struct process_trailer_options *opts,
+ const char *str,
+ struct trailer_info *info)
{
- struct stat st;
- struct strbuf filename_template = STRBUF_INIT;
- const char *tail;
- FILE *outfile;
-
- if (stat(file, &st))
- die_errno(_("could not stat %s"), file);
- if (!S_ISREG(st.st_mode))
- die(_("file %s is not a regular file"), file);
- if (!(st.st_mode & S_IWUSR))
- die(_("file %s is not writable by user"), file);
-
- /* Create temporary file in the same directory as the original */
- tail = strrchr(file, '/');
- if (tail)
- strbuf_add(&filename_template, file, tail - file + 1);
- strbuf_addstr(&filename_template, "git-interpret-trailers-XXXXXX");
-
- trailers_tempfile = xmks_tempfile_m(filename_template.buf, st.st_mode);
- strbuf_release(&filename_template);
- outfile = fdopen_tempfile(trailers_tempfile, "w");
- if (!outfile)
- die_errno(_("could not open temporary file"));
-
- return outfile;
-}
-
-void process_trailers(const char *file,
- const struct process_trailer_options *opts,
- struct list_head *new_trailer_head)
-{
- LIST_HEAD(head);
- struct strbuf sb = STRBUF_INIT;
- struct trailer_info info;
- size_t trailer_end;
- FILE *outfile = stdout;
-
- ensure_configured();
-
- read_input_file(&sb, file);
-
- if (opts->in_place)
- outfile = create_in_place_tempfile(file);
-
- parse_trailers(&info, sb.buf, &head, opts);
- trailer_end = info.trailer_end - sb.buf;
-
- /* Print the lines before the trailers */
- if (!opts->only_trailers)
- fwrite(sb.buf, 1, info.trailer_start - sb.buf, outfile);
-
- if (!opts->only_trailers && !info.blank_line_before_trailer)
- fprintf(outfile, "\n");
-
-
- if (!opts->only_input) {
- LIST_HEAD(config_head);
- LIST_HEAD(arg_head);
- parse_trailers_from_config(&config_head);
- parse_trailers_from_command_line_args(&arg_head, new_trailer_head);
- list_splice(&config_head, &arg_head);
- process_trailers_lists(&head, &arg_head);
- }
-
- print_all(outfile, &head, opts);
-
- free_all(&head);
- trailer_info_release(&info);
-
- /* Print the lines after the trailers as is */
- if (!opts->only_trailers)
- fwrite(sb.buf + trailer_end, 1, sb.len - trailer_end, outfile);
-
- if (opts->in_place)
- if (rename_tempfile(&trailers_tempfile, file))
- die_errno(_("could not rename temporary file to %s"), file);
-
- strbuf_release(&sb);
-}
-
-void trailer_info_get(struct trailer_info *info, const char *str,
- const struct process_trailer_options *opts)
-{
- int patch_start, trailer_end, trailer_start;
+ size_t end_of_log_message = 0, trailer_block_start = 0;
struct strbuf **trailer_lines, **ptr;
char **trailer_strings = NULL;
size_t nr = 0, alloc = 0;
char **last = NULL;
- ensure_configured();
+ trailer_config_init();
- if (opts->no_divider)
- patch_start = strlen(str);
- else
- patch_start = find_patch_start(str);
+ end_of_log_message = find_end_of_log_message(str, opts->no_divider);
+ trailer_block_start = find_trailer_block_start(str, end_of_log_message);
- trailer_end = find_trailer_end(str, patch_start);
- trailer_start = find_trailer_start(str, trailer_end);
-
- trailer_lines = strbuf_split_buf(str + trailer_start,
- trailer_end - trailer_start,
+ trailer_lines = strbuf_split_buf(str + trailer_block_start,
+ end_of_log_message - trailer_block_start,
'\n',
0);
for (ptr = trailer_lines; *ptr; ptr++) {
@@ -1139,9 +1037,9 @@ void trailer_info_get(struct trailer_info *info, const char *str,
strbuf_list_free(trailer_lines);
info->blank_line_before_trailer = ends_with_blank_line(str,
- trailer_start);
- info->trailer_start = str + trailer_start;
- info->trailer_end = str + trailer_end;
+ trailer_block_start);
+ info->trailer_block_start = trailer_block_start;
+ info->trailer_block_end = end_of_log_message;
info->trailers = trailer_strings;
info->trailer_nr = nr;
}
@@ -1154,35 +1052,32 @@ void trailer_info_release(struct trailer_info *info)
free(info->trailers);
}
-static void format_trailer_info(struct strbuf *out,
- const struct trailer_info *info,
- const struct process_trailer_options *opts)
+void format_trailers(const struct process_trailer_options *opts,
+ struct list_head *trailers,
+ struct strbuf *out)
{
size_t origlen = out->len;
- size_t i;
-
- /* If we want the whole block untouched, we can take the fast path. */
- if (!opts->only_trailers && !opts->unfold && !opts->filter &&
- !opts->separator && !opts->key_only && !opts->value_only &&
- !opts->key_value_separator) {
- strbuf_add(out, info->trailer_start,
- info->trailer_end - info->trailer_start);
- return;
- }
-
- for (i = 0; i < info->trailer_nr; i++) {
- char *trailer = info->trailers[i];
- ssize_t separator_pos = find_separator(trailer, separators);
+ struct list_head *pos;
+ struct trailer_item *item;
- if (separator_pos >= 1) {
+ list_for_each(pos, trailers) {
+ item = list_entry(pos, struct trailer_item, list);
+ if (item->token) {
struct strbuf tok = STRBUF_INIT;
struct strbuf val = STRBUF_INIT;
+ strbuf_addstr(&tok, item->token);
+ strbuf_addstr(&val, item->value);
+
+ /*
+ * Skip key/value pairs where the value was empty. This
+ * can happen from trailers specified without a
+ * separator, like `--trailer "Reviewed-by"` (no
+ * corresponding value).
+ */
+ if (opts->trim_empty && !strlen(item->value))
+ continue;
- parse_trailer(&tok, &val, NULL, trailer, separator_pos);
if (!opts->filter || opts->filter(&tok, opts->filter_data)) {
- if (opts->unfold)
- unfold_value(&val);
-
if (opts->separator && out->len != origlen)
strbuf_addbuf(out, opts->separator);
if (!opts->value_only)
@@ -1190,8 +1085,11 @@ static void format_trailer_info(struct strbuf *out,
if (!opts->key_only && !opts->value_only) {
if (opts->key_value_separator)
strbuf_addbuf(out, opts->key_value_separator);
- else
- strbuf_addstr(out, ": ");
+ else {
+ char c = last_non_space_char(tok.buf);
+ if (c && !strchr(separators, c))
+ strbuf_addf(out, "%c ", separators[0]);
+ }
}
if (!opts->key_only)
strbuf_addbuf(out, &val);
@@ -1205,22 +1103,34 @@ static void format_trailer_info(struct strbuf *out,
if (opts->separator && out->len != origlen) {
strbuf_addbuf(out, opts->separator);
}
- strbuf_addstr(out, trailer);
- if (opts->separator) {
+ strbuf_addstr(out, item->value);
+ if (opts->separator)
strbuf_rtrim(out);
- }
+ else
+ strbuf_addch(out, '\n');
}
}
-
}
-void format_trailers_from_commit(struct strbuf *out, const char *msg,
- const struct process_trailer_options *opts)
+void format_trailers_from_commit(const struct process_trailer_options *opts,
+ const char *msg,
+ struct strbuf *out)
{
+ LIST_HEAD(trailer_objects);
struct trailer_info info;
- trailer_info_get(&info, msg, opts);
- format_trailer_info(out, &info, opts);
+ parse_trailers(opts, &info, msg, &trailer_objects);
+
+ /* If we want the whole block untouched, we can take the fast path. */
+ if (!opts->only_trailers && !opts->unfold && !opts->filter &&
+ !opts->separator && !opts->key_only && !opts->value_only &&
+ !opts->key_value_separator) {
+ strbuf_add(out, msg + info.trailer_block_start,
+ info.trailer_block_end - info.trailer_block_start);
+ } else
+ format_trailers(opts, &trailer_objects, out);
+
+ free_trailers(&trailer_objects);
trailer_info_release(&info);
}
@@ -1230,7 +1140,7 @@ void trailer_iterator_init(struct trailer_iterator *iter, const char *msg)
strbuf_init(&iter->key, 0);
strbuf_init(&iter->val, 0);
opts.no_divider = 1;
- trailer_info_get(&iter->internal.info, msg, &opts);
+ trailer_info_get(&opts, msg, &iter->internal.info);
iter->internal.cur = 0;
}
@@ -1247,6 +1157,7 @@ int trailer_iterator_advance(struct trailer_iterator *iter)
strbuf_reset(&iter->val);
parse_trailer(&iter->key, &iter->val, NULL,
trailer, separator_pos);
+ /* Always unfold values during iteration. */
unfold_value(&iter->val);
return 1;
}
diff --git a/trailer.h b/trailer.h
index ab2cd01..9f42aa7 100644
--- a/trailer.h
+++ b/trailer.h
@@ -32,16 +32,16 @@ int trailer_set_if_missing(enum trailer_if_missing *item, const char *value);
struct trailer_info {
/*
* True if there is a blank line before the location pointed to by
- * trailer_start.
+ * trailer_block_start.
*/
int blank_line_before_trailer;
/*
- * Pointers to the start and end of the trailer block found. If there
- * is no trailer block found, these 2 pointers point to the end of the
- * input string.
+ * Offsets to the trailer block start and end positions in the input
+ * string. If no trailer block is found, these are both set to the
+ * "true" end of the input (find_end_of_log_message()).
*/
- const char *trailer_start, *trailer_end;
+ size_t trailer_block_start, trailer_block_end;
/*
* Array of trailers found.
@@ -81,28 +81,38 @@ struct process_trailer_options {
#define PROCESS_TRAILER_OPTIONS_INIT {0}
-void process_trailers(const char *file,
- const struct process_trailer_options *opts,
- struct list_head *new_trailer_head);
+void parse_trailers_from_config(struct list_head *config_head);
-void trailer_info_get(struct trailer_info *info, const char *str,
- const struct process_trailer_options *opts);
+void parse_trailers_from_command_line_args(struct list_head *arg_head,
+ struct list_head *new_trailer_head);
+
+void process_trailers_lists(struct list_head *head,
+ struct list_head *arg_head);
+
+void parse_trailers(const struct process_trailer_options *,
+ struct trailer_info *,
+ const char *str,
+ struct list_head *head);
+
+void trailer_info_get(const struct process_trailer_options *,
+ const char *str,
+ struct trailer_info *);
void trailer_info_release(struct trailer_info *info);
+void trailer_config_init(void);
+void format_trailers(const struct process_trailer_options *,
+ struct list_head *trailers,
+ struct strbuf *out);
+void free_trailers(struct list_head *);
+
/*
- * Format the trailers from the commit msg "msg" into the strbuf "out".
- * Note two caveats about "opts":
- *
- * - this is primarily a helper for pretty.c, and not
- * all of the flags are supported.
- *
- * - this differs from process_trailers slightly in that we always format
- * only the trailer block itself, even if the "only_trailers" option is not
- * set.
+ * Convenience function to format the trailers from the commit msg "msg" into
+ * the strbuf "out". Reuses format_trailers() internally.
*/
-void format_trailers_from_commit(struct strbuf *out, const char *msg,
- const struct process_trailer_options *opts);
+void format_trailers_from_commit(const struct process_trailer_options *,
+ const char *msg,
+ struct strbuf *out);
/*
* An interface for iterating over the trailers found in a particular commit
diff --git a/transport-helper.c b/transport-helper.c
index 49811ef..8d284b2 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -3,13 +3,11 @@
#include "quote.h"
#include "run-command.h"
#include "commit.h"
-#include "diff.h"
#include "environment.h"
#include "gettext.h"
#include "hex.h"
#include "object-name.h"
#include "repository.h"
-#include "revision.h"
#include "remote.h"
#include "string-list.h"
#include "thread-utils.h"
@@ -19,6 +17,7 @@
#include "refspec.h"
#include "transport-internal.h"
#include "protocol.h"
+#include "packfile.h"
static int debug;
@@ -434,6 +433,8 @@ static int fetch_with_fetch(struct transport *transport,
warning(_("%s unexpectedly said: '%s'"), data->name, buf.buf);
}
strbuf_release(&buf);
+
+ reprepare_packed_git(the_repository);
return 0;
}
@@ -628,7 +629,8 @@ static int process_connect_service(struct transport *transport,
ret = run_connect(transport, &cmdbuf);
} else if (data->stateless_connect &&
(get_protocol_version_config() == protocol_v2) &&
- !strcmp("git-upload-pack", name)) {
+ (!strcmp("git-upload-pack", name) ||
+ !strcmp("git-upload-archive", name))) {
strbuf_addf(&cmdbuf, "stateless-connect %s\n", name);
ret = run_connect(transport, &cmdbuf);
if (ret)
@@ -645,6 +647,7 @@ static int process_connect(struct transport *transport,
struct helper_data *data = transport->data;
const char *name;
const char *exec;
+ int ret;
name = for_push ? "git-receive-pack" : "git-upload-pack";
if (for_push)
@@ -652,7 +655,10 @@ static int process_connect(struct transport *transport,
else
exec = data->transport_options.uploadpack;
- return process_connect_service(transport, name, exec);
+ ret = process_connect_service(transport, name, exec);
+ if (ret)
+ do_take_over(transport);
+ return ret;
}
static int connect_helper(struct transport *transport, const char *name,
@@ -662,14 +668,14 @@ static int connect_helper(struct transport *transport, const char *name,
/* Get_helper so connect is inited. */
get_helper(transport);
- if (!data->connect)
- die(_("operation not supported by protocol"));
if (!process_connect_service(transport, name, exec))
die(_("can't connect to subservice %s"), name);
fd[0] = data->helper->out;
fd[1] = data->helper->in;
+
+ do_take_over(transport);
return 0;
}
@@ -684,10 +690,8 @@ static int fetch_refs(struct transport *transport,
get_helper(transport);
- if (process_connect(transport, 0)) {
- do_take_over(transport);
+ if (process_connect(transport, 0))
return transport->vtable->fetch_refs(transport, nr_heads, to_fetch);
- }
/*
* If we reach here, then the server, the client, and/or the transport
@@ -1074,7 +1078,7 @@ static int push_refs_with_export(struct transport *transport,
set_common_push_options(transport, data->name, flags);
if (flags & TRANSPORT_PUSH_FORCE) {
if (set_helper_option(transport, "force", "true") != 0)
- warning(_("helper %s does not support 'force'"), data->name);
+ warning(_("helper %s does not support '--force'"), data->name);
}
helper = get_helper(transport);
@@ -1144,10 +1148,8 @@ static int push_refs(struct transport *transport,
{
struct helper_data *data = transport->data;
- if (process_connect(transport, 1)) {
- do_take_over(transport);
+ if (process_connect(transport, 1))
return transport->vtable->push_refs(transport, remote_refs, flags);
- }
if (!remote_refs) {
fprintf(stderr,
@@ -1188,11 +1190,9 @@ static struct ref *get_refs_list(struct transport *transport, int for_push,
{
get_helper(transport);
- if (process_connect(transport, for_push)) {
- do_take_over(transport);
+ if (process_connect(transport, for_push))
return transport->vtable->get_refs_list(transport, for_push,
transport_options);
- }
return get_refs_list_using_list(transport, for_push);
}
@@ -1210,16 +1210,13 @@ static struct ref *get_refs_list_using_list(struct transport *transport,
data->get_refs_list_called = 1;
helper = get_helper(transport);
- if (data->object_format) {
- write_str_in_full(helper->in, "option object-format\n");
- if (recvline(data, &buf) || strcmp(buf.buf, "ok"))
- exit(128);
- }
+ if (data->object_format)
+ set_helper_option(transport, "object-format", "true");
if (data->push && for_push)
- write_str_in_full(helper->in, "list for-push\n");
+ write_constant(helper->in, "list for-push\n");
else
- write_str_in_full(helper->in, "list\n");
+ write_constant(helper->in, "list\n");
while (1) {
char *eov, *eon;
@@ -1276,10 +1273,8 @@ static int get_bundle_uri(struct transport *transport)
{
get_helper(transport);
- if (process_connect(transport, 0)) {
- do_take_over(transport);
+ if (process_connect(transport, 0))
return transport->vtable->get_bundle_uri(transport);
- }
return -1;
}
diff --git a/transport.c b/transport.c
index 219af8f..df518ea 100644
--- a/transport.c
+++ b/transport.c
@@ -10,9 +10,7 @@
#include "remote.h"
#include "connect.h"
#include "send-pack.h"
-#include "walker.h"
#include "bundle.h"
-#include "dir.h"
#include "gettext.h"
#include "refs.h"
#include "refspec.h"
@@ -26,7 +24,6 @@
#include "transport-internal.h"
#include "protocol.h"
#include "object-name.h"
-#include "object-store-ll.h"
#include "color.h"
#include "bundle-uri.h"
@@ -1470,6 +1467,7 @@ int transport_push(struct repository *r,
if (porcelain && !push_ret)
puts("Done");
else if (!quiet && !ret && !transport_refs_pushed(remote_refs))
+ /* stable plumbing output; do not modify or localize */
fprintf(stderr, "Everything up-to-date\n");
done:
diff --git a/tree-walk.c b/tree-walk.c
index b517792..6565d9a 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -11,35 +11,19 @@
#include "json-writer.h"
#include "environment.h"
-static const char *get_mode(const char *str, unsigned int *modep)
-{
- unsigned char c;
- unsigned int mode = 0;
-
- if (*str == ' ')
- return NULL;
-
- while ((c = *str++) != ' ') {
- if (c < '0' || c > '7')
- return NULL;
- mode = (mode << 3) + (c - '0');
- }
- *modep = mode;
- return str;
-}
-
static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned long size, struct strbuf *err)
{
const char *path;
- unsigned int mode, len;
- const unsigned hashsz = the_hash_algo->rawsz;
+ unsigned int len;
+ uint16_t mode;
+ const unsigned hashsz = desc->algo->rawsz;
if (size < hashsz + 3 || buf[size - (hashsz + 1)]) {
strbuf_addstr(err, _("too-short tree object"));
return -1;
}
- path = get_mode(buf, &mode);
+ path = parse_mode(buf, &mode);
if (!path) {
strbuf_addstr(err, _("malformed mode in tree entry"));
return -1;
@@ -54,15 +38,19 @@ static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned l
desc->entry.path = path;
desc->entry.mode = (desc->flags & TREE_DESC_RAW_MODES) ? mode : canon_mode(mode);
desc->entry.pathlen = len - 1;
- oidread(&desc->entry.oid, (const unsigned char *)path + len);
+ oidread_algop(&desc->entry.oid, (const unsigned char *)path + len,
+ desc->algo);
return 0;
}
-static int init_tree_desc_internal(struct tree_desc *desc, const void *buffer,
- unsigned long size, struct strbuf *err,
+static int init_tree_desc_internal(struct tree_desc *desc,
+ const struct object_id *oid,
+ const void *buffer, unsigned long size,
+ struct strbuf *err,
enum tree_desc_flags flags)
{
+ desc->algo = (oid && oid->algo) ? &hash_algos[oid->algo] : the_hash_algo;
desc->buffer = buffer;
desc->size = size;
desc->flags = flags;
@@ -71,19 +59,21 @@ static int init_tree_desc_internal(struct tree_desc *desc, const void *buffer,
return 0;
}
-void init_tree_desc(struct tree_desc *desc, const void *buffer, unsigned long size)
+void init_tree_desc(struct tree_desc *desc, const struct object_id *tree_oid,
+ const void *buffer, unsigned long size)
{
struct strbuf err = STRBUF_INIT;
- if (init_tree_desc_internal(desc, buffer, size, &err, 0))
+ if (init_tree_desc_internal(desc, tree_oid, buffer, size, &err, 0))
die("%s", err.buf);
strbuf_release(&err);
}
-int init_tree_desc_gently(struct tree_desc *desc, const void *buffer, unsigned long size,
+int init_tree_desc_gently(struct tree_desc *desc, const struct object_id *oid,
+ const void *buffer, unsigned long size,
enum tree_desc_flags flags)
{
struct strbuf err = STRBUF_INIT;
- int result = init_tree_desc_internal(desc, buffer, size, &err, flags);
+ int result = init_tree_desc_internal(desc, oid, buffer, size, &err, flags);
if (result)
error("%s", err.buf);
strbuf_release(&err);
@@ -100,9 +90,9 @@ void *fill_tree_descriptor(struct repository *r,
if (oid) {
buf = read_object_with_reference(r, oid, OBJ_TREE, &size, NULL);
if (!buf)
- die("unable to read tree %s", oid_to_hex(oid));
+ die(_("unable to read tree (%s)"), oid_to_hex(oid));
}
- init_tree_desc(desc, buf, size);
+ init_tree_desc(desc, oid, buf, size);
return buf;
}
@@ -119,7 +109,7 @@ static void entry_extract(struct tree_desc *t, struct name_entry *a)
static int update_tree_entry_internal(struct tree_desc *desc, struct strbuf *err)
{
const void *buf = desc->buffer;
- const unsigned char *end = (const unsigned char *)desc->entry.path + desc->entry.pathlen + 1 + the_hash_algo->rawsz;
+ const unsigned char *end = (const unsigned char *)desc->entry.path + desc->entry.pathlen + 1 + desc->algo->rawsz;
unsigned long size = desc->size;
unsigned long len = end - (const unsigned char *)buf;
@@ -633,7 +623,7 @@ int get_tree_entry(struct repository *r,
retval = -1;
} else {
struct tree_desc t;
- init_tree_desc(&t, tree, size);
+ init_tree_desc(&t, tree_oid, tree, size);
retval = find_tree_entry(r, &t, name, oid, mode);
}
free(tree);
@@ -676,7 +666,7 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,
struct tree_desc t;
int follows_remaining = GET_TREE_ENTRY_FOLLOW_SYMLINKS_MAX_LINKS;
- init_tree_desc(&t, NULL, 0UL);
+ init_tree_desc(&t, NULL, NULL, 0UL);
strbuf_addstr(&namebuf, name);
oidcpy(&current_tree_oid, tree_oid);
@@ -712,7 +702,7 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,
goto done;
/* descend */
- init_tree_desc(&t, tree, size);
+ init_tree_desc(&t, &current_tree_oid, tree, size);
}
/* Handle symlinks to e.g. a//b by removing leading slashes */
@@ -746,7 +736,7 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,
free(parent->tree);
parents_nr--;
parent = &parents[parents_nr - 1];
- init_tree_desc(&t, parent->tree, parent->size);
+ init_tree_desc(&t, &parent->oid, parent->tree, parent->size);
strbuf_remove(&namebuf, 0, remainder ? 3 : 2);
continue;
}
@@ -826,7 +816,7 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,
contents_start = contents;
parent = &parents[parents_nr - 1];
- init_tree_desc(&t, parent->tree, parent->size);
+ init_tree_desc(&t, &parent->oid, parent->tree, parent->size);
strbuf_splice(&namebuf, 0, len,
contents_start, link_len);
if (remainder)
diff --git a/tree-walk.h b/tree-walk.h
index a6bfa3d..0b1067f 100644
--- a/tree-walk.h
+++ b/tree-walk.h
@@ -24,6 +24,7 @@ struct name_entry {
* A semi-opaque data structure used to maintain the current state of the walk.
*/
struct tree_desc {
+ const struct git_hash_algo *algo;
/*
* pointer into the memory representation of the tree. It always
* points at the current entry being visited.
@@ -83,9 +84,11 @@ int update_tree_entry_gently(struct tree_desc *);
* size parameters are assumed to be the same as the buffer and size
* members of `struct tree`.
*/
-void init_tree_desc(struct tree_desc *desc, const void *buf, unsigned long size);
+void init_tree_desc(struct tree_desc *desc, const struct object_id *tree_oid,
+ const void *buf, unsigned long size);
-int init_tree_desc_gently(struct tree_desc *desc, const void *buf, unsigned long size,
+int init_tree_desc_gently(struct tree_desc *desc, const struct object_id *oid,
+ const void *buf, unsigned long size,
enum tree_desc_flags flags);
/*
diff --git a/tree.c b/tree.c
index 990f9c9..7973d3f 100644
--- a/tree.c
+++ b/tree.c
@@ -1,12 +1,9 @@
#include "git-compat-util.h"
-#include "cache-tree.h"
#include "hex.h"
#include "tree.h"
#include "object-name.h"
#include "object-store-ll.h"
-#include "blob.h"
#include "commit.h"
-#include "tag.h"
#include "alloc.h"
#include "tree-walk.h"
#include "repository.h"
@@ -32,7 +29,7 @@ int read_tree_at(struct repository *r,
if (parse_tree(tree))
return -1;
- init_tree_desc(&desc, tree->buffer, tree->size);
+ init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
if (retval != all_entries_interesting) {