summaryrefslogtreecommitdiff
path: root/t
diff options
context:
space:
mode:
Diffstat (limited to 't')
-rw-r--r--t/Makefile14
-rw-r--r--t/for-each-ref-tests.sh2141
-rw-r--r--t/helper/test-delete-gpgsig.c7
-rw-r--r--t/helper/test-find-pack.c3
-rw-r--r--t/helper/test-hashmap.c4
-rw-r--r--t/helper/test-json-writer.c4
-rw-r--r--t/helper/test-pack-deltas.c10
-rw-r--r--t/helper/test-pack-mtimes.c2
-rw-r--r--t/helper/test-path-utils.c3
-rw-r--r--t/helper/test-read-graph.c2
-rw-r--r--t/helper/test-read-midx.c31
-rw-r--r--t/helper/test-ref-store.c5
-rw-r--r--t/lib-gpg.sh25
-rw-r--r--t/meson.build16
-rw-r--r--t/pack-refs-tests.sh431
-rwxr-xr-xt/perf/p6010-merge-base.sh101
-rwxr-xr-xt/perf/p8020-last-modified.sh22
-rw-r--r--t/show-ref-exists-tests.sh77
-rwxr-xr-xt/t0001-init.sh16
-rwxr-xr-xt/t0008-ignores.sh11
-rwxr-xr-xt/t0014-alias.sh57
-rwxr-xr-xt/t0300-credentials.sh19
-rwxr-xr-xt/t0450-txt-doc-vs-help.sh15
-rw-r--r--t/t0450/adoc-help-mismatches2
-rw-r--r--t/t0450/adoc-missing9
-rwxr-xr-xt/t0600-reffiles-backend.sh26
-rwxr-xr-xt/t0601-reffiles-pack-refs.sh430
-rwxr-xr-xt/t0613-reftable-write-options.sh24
-rwxr-xr-xt/t0614-reftable-fsck.sh58
-rwxr-xr-xt/t1010-mktree.sh13
-rwxr-xr-xt/t1016-compatObjectFormat.sh8
-rwxr-xr-xt/t1016/gpg2
-rwxr-xr-xt/t1091-sparse-checkout-builtin.sh175
-rwxr-xr-xt/t1092-sparse-checkout-compatibility.sh13
-rwxr-xr-xt/t1300-config.sh349
-rwxr-xr-xt/t1400-update-ref.sh74
-rwxr-xr-xt/t1403-show-ref.sh65
-rwxr-xr-xt/t1421-reflog-write.sh162
-rwxr-xr-xt/t1422-show-ref-exists.sh9
-rwxr-xr-xt/t1450-fsck.sh54
-rwxr-xr-xt/t1460-refs-migrate.sh22
-rwxr-xr-xt/t1461-refs-list.sh8
-rwxr-xr-xt/t1462-refs-exists.sh10
-rwxr-xr-xt/t1463-refs-optimize.sh17
-rwxr-xr-xt/t1500-rev-parse.sh34
-rwxr-xr-xt/t1517-outside-repo.sh52
-rwxr-xr-xt/t1900-repo.sh113
-rwxr-xr-xt/t1901-repo-structure.sh129
-rwxr-xr-xt/t2401-worktree-prune.sh34
-rwxr-xr-xt/t3070-wildmatch.sh2
-rwxr-xr-xt/t3206-range-diff.sh16
-rwxr-xr-xt/t3404-rebase-interactive.sh20
-rwxr-xr-xt/t3415-rebase-autosquash.sh28
-rwxr-xr-xt/t3418-rebase-continue.sh2
-rwxr-xr-xt/t3701-add-interactive.sh179
-rwxr-xr-xt/t3903-stash.sh49
-rwxr-xr-xt/t3904-stash-patch.sh19
-rwxr-xr-xt/t3905-stash-include-untracked.sh1
-rwxr-xr-xt/t4013-diff-various.sh270
-rw-r--r--t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_main (renamed from t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_master)2
-rw-r--r--t/t4013/diff.diff-tree_--cc_--patch-with-stat_main (renamed from t/t4013/diff.diff-tree_--cc_--patch-with-stat_master)2
-rw-r--r--t/t4013/diff.diff-tree_--cc_--shortstat_main (renamed from t/t4013/diff.diff-tree_--cc_--shortstat_master)2
-rw-r--r--t/t4013/diff.diff-tree_--cc_--stat_--summary_main (renamed from t/t4013/diff.diff-tree_-c_--stat_--summary_master)2
-rw-r--r--t/t4013/diff.diff-tree_--cc_--stat_main (renamed from t/t4013/diff.diff-tree_-c_--stat_master)2
-rw-r--r--t/t4013/diff.diff-tree_--cc_main (renamed from t/t4013/diff.diff-tree_--cc_master)2
-rw-r--r--t/t4013/diff.diff-tree_-c_--abbrev_main (renamed from t/t4013/diff.diff-tree_-c_--abbrev_master)2
-rw-r--r--t/t4013/diff.diff-tree_-c_--stat_--summary_main (renamed from t/t4013/diff.diff-tree_--cc_--stat_--summary_master)2
-rw-r--r--t/t4013/diff.diff-tree_-c_--stat_main (renamed from t/t4013/diff.diff-tree_--cc_--stat_master)2
-rw-r--r--t/t4013/diff.diff-tree_-c_main (renamed from t/t4013/diff.diff-tree_-c_master)2
-rw-r--r--t/t4013/diff.diff-tree_-m_main (renamed from t/t4013/diff.diff-tree_-m_master)2
-rw-r--r--t/t4013/diff.diff-tree_-p_-m_main (renamed from t/t4013/diff.diff-tree_-p_-m_master)2
-rw-r--r--t/t4013/diff.diff-tree_-p_main2
-rw-r--r--t/t4013/diff.diff-tree_-p_master2
-rw-r--r--t/t4013/diff.diff-tree_main2
-rw-r--r--t/t4013/diff.diff-tree_master2
-rw-r--r--t/t4013/diff.diff_--dirstat_--cc_main~1_main3
-rw-r--r--t/t4013/diff.diff_--dirstat_--cc_master~1_master3
-rw-r--r--t/t4013/diff.diff_--dirstat_main~1_main~23
-rw-r--r--t/t4013/diff.diff_--dirstat_master~1_master~23
-rw-r--r--t/t4013/diff.diff_--line-prefix=abc_main_main^_side (renamed from t/t4013/diff.diff_--line-prefix=abc_master_master^_side)2
-rw-r--r--t/t4013/diff.diff_main_main^_side (renamed from t/t4013/diff.diff_master_master^_side)2
-rw-r--r--t/t4013/diff.format-patch_--attach_--stdout_initial..main (renamed from t/t4013/diff.format-patch_--attach_--stdout_initial..master)2
-rw-r--r--t/t4013/diff.format-patch_--attach_--stdout_initial..main^ (renamed from t/t4013/diff.format-patch_--attach_--stdout_initial..master^)2
-rw-r--r--t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..main (renamed from t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master)2
-rw-r--r--t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..main (renamed from t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master)2
-rw-r--r--t/t4013/diff.format-patch_--inline_--stdout_initial..main (renamed from t/t4013/diff.format-patch_--inline_--stdout_initial..master)2
-rw-r--r--t/t4013/diff.format-patch_--inline_--stdout_initial..main^ (renamed from t/t4013/diff.format-patch_--inline_--stdout_initial..master^)2
-rw-r--r--t/t4013/diff.format-patch_--inline_--stdout_initial..main^^ (renamed from t/t4013/diff.format-patch_--inline_--stdout_initial..master^^)2
-rw-r--r--t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..main^ (renamed from t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^)2
-rw-r--r--t/t4013/diff.format-patch_--stdout_--no-numbered_initial..main (renamed from t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master)2
-rw-r--r--t/t4013/diff.format-patch_--stdout_--numbered_initial..main (renamed from t/t4013/diff.format-patch_--stdout_--numbered_initial..master)2
-rw-r--r--t/t4013/diff.format-patch_--stdout_initial..main (renamed from t/t4013/diff.format-patch_--stdout_initial..master)2
-rw-r--r--t/t4013/diff.format-patch_--stdout_initial..main^ (renamed from t/t4013/diff.format-patch_--stdout_initial..master^)2
-rw-r--r--t/t4013/diff.log_--cc_-m_-p_main (renamed from t/t4013/diff.log_-c_-m_-p_master)2
-rw-r--r--t/t4013/diff.log_--decorate=full_--all2
-rw-r--r--t/t4013/diff.log_--decorate=full_--clear-decorations_--all2
-rw-r--r--t/t4013/diff.log_--decorate=full_--decorate-all_--all2
-rw-r--r--t/t4013/diff.log_--decorate_--all2
-rw-r--r--t/t4013/diff.log_--decorate_--clear-decorations_--all2
-rw-r--r--t/t4013/diff.log_--decorate_--decorate-all_--all2
-rw-r--r--t/t4013/diff.log_--diff-merges=first-parent_main (renamed from t/t4013/diff.log_--diff-merges=first-parent_master)2
-rw-r--r--t/t4013/diff.log_--diff-merges=off_-p_--first-parent_main (renamed from t/t4013/diff.log_--no-diff-merges_-p_--first-parent_master)2
-rw-r--r--t/t4013/diff.log_--first-parent_--diff-merges=off_-p_main (renamed from t/t4013/diff.log_--first-parent_--diff-merges=off_-p_master)2
-rw-r--r--t/t4013/diff.log_--no-diff-merges_-p_--first-parent_main (renamed from t/t4013/diff.log_--diff-merges=off_-p_--first-parent_master)2
-rw-r--r--t/t4013/diff.log_--patch-with-stat_--summary_main_--_dir_ (renamed from t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_)2
-rw-r--r--t/t4013/diff.log_--patch-with-stat_main (renamed from t/t4013/diff.log_--patch-with-stat_master)2
-rw-r--r--t/t4013/diff.log_--patch-with-stat_main_--_dir_ (renamed from t/t4013/diff.log_--patch-with-stat_master_--_dir_)2
-rw-r--r--t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_main (renamed from t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master)2
-rw-r--r--t/t4013/diff.log_--root_--patch-with-stat_--summary_main (renamed from t/t4013/diff.log_--root_--patch-with-stat_--summary_master)2
-rw-r--r--t/t4013/diff.log_--root_--patch-with-stat_main (renamed from t/t4013/diff.log_--root_--patch-with-stat_master)2
-rw-r--r--t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_main (renamed from t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master)2
-rw-r--r--t/t4013/diff.log_--root_-p_main (renamed from t/t4013/diff.log_--root_-p_master)2
-rw-r--r--t/t4013/diff.log_--root_main (renamed from t/t4013/diff.log_--root_master)2
-rw-r--r--t/t4013/diff.log_-GF_-p_--pickaxe-all_main (renamed from t/t4013/diff.log_-GF_-p_--pickaxe-all_master)2
-rw-r--r--t/t4013/diff.log_-GF_-p_main (renamed from t/t4013/diff.log_-GF_-p_master)2
-rw-r--r--t/t4013/diff.log_-GF_main (renamed from t/t4013/diff.log_-SF_master)2
-rw-r--r--t/t4013/diff.log_-IA_-IB_-I1_-I2_-p_main (renamed from t/t4013/diff.log_-IA_-IB_-I1_-I2_-p_master)2
-rw-r--r--t/t4013/diff.log_-SF_-p_main (renamed from t/t4013/diff.log_-SF_-p_master)2
-rw-r--r--t/t4013/diff.log_-SF_main (renamed from t/t4013/diff.log_-GF_master)2
-rw-r--r--t/t4013/diff.log_-SF_main_--max-count=02
-rw-r--r--t/t4013/diff.log_-SF_main_--max-count=1 (renamed from t/t4013/diff.log_-SF_master_--max-count=2)2
-rw-r--r--t/t4013/diff.log_-SF_main_--max-count=27
-rw-r--r--t/t4013/diff.log_-SF_master_--max-count=02
-rw-r--r--t/t4013/diff.log_-SF_master_--max-count=17
-rw-r--r--t/t4013/diff.log_-S_F_main (renamed from t/t4013/diff.log_-S_F_master)2
-rw-r--r--t/t4013/diff.log_-c_-m_-p_main (renamed from t/t4013/diff.log_--cc_-m_-p_master)2
-rw-r--r--t/t4013/diff.log_-m_--raw_main (renamed from t/t4013/diff.log_-m_--raw_master)2
-rw-r--r--t/t4013/diff.log_-m_--stat_main (renamed from t/t4013/diff.log_-m_--stat_master)2
-rw-r--r--t/t4013/diff.log_-m_-p_--first-parent_main (renamed from t/t4013/diff.log_-m_-p_--first-parent_master)2
-rw-r--r--t/t4013/diff.log_-m_-p_main (renamed from t/t4013/diff.log_-m_-p_master)2
-rw-r--r--t/t4013/diff.log_-p_--diff-merges=first-parent_main (renamed from t/t4013/diff.log_-p_--diff-merges=first-parent_master)2
-rw-r--r--t/t4013/diff.log_-p_--first-parent_main (renamed from t/t4013/diff.log_-p_--first-parent_master)2
-rw-r--r--t/t4013/diff.log_-p_main (renamed from t/t4013/diff.log_-p_master)2
-rw-r--r--t/t4013/diff.log_main (renamed from t/t4013/diff.log_master)2
-rw-r--r--t/t4013/diff.noellipses-diff-tree_-c_--abbrev_main (renamed from t/t4013/diff.noellipses-diff-tree_-c_--abbrev_master)2
-rw-r--r--t/t4013/diff.noellipses-whatchanged_--root_main (renamed from t/t4013/diff.noellipses-whatchanged_--root_master)2
-rw-r--r--t/t4013/diff.noellipses-whatchanged_-SF_main (renamed from t/t4013/diff.noellipses-whatchanged_-SF_master)2
-rw-r--r--t/t4013/diff.noellipses-whatchanged_main (renamed from t/t4013/diff.noellipses-whatchanged_master)2
-rw-r--r--t/t4013/diff.show_--first-parent_main (renamed from t/t4013/diff.show_--first-parent_master)2
-rw-r--r--t/t4013/diff.show_-c_main (renamed from t/t4013/diff.show_-c_master)2
-rw-r--r--t/t4013/diff.show_-m_main (renamed from t/t4013/diff.show_-m_master)2
-rw-r--r--t/t4013/diff.show_main (renamed from t/t4013/diff.show_master)2
-rw-r--r--t/t4013/diff.whatchanged_--patch-with-stat_--summary_main_--_dir_ (renamed from t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_)2
-rw-r--r--t/t4013/diff.whatchanged_--patch-with-stat_main (renamed from t/t4013/diff.whatchanged_--patch-with-stat_master)2
-rw-r--r--t/t4013/diff.whatchanged_--patch-with-stat_main_--_dir_ (renamed from t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_)2
-rw-r--r--t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_main (renamed from t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master)2
-rw-r--r--t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_main (renamed from t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master)2
-rw-r--r--t/t4013/diff.whatchanged_--root_--patch-with-stat_main (renamed from t/t4013/diff.whatchanged_--root_--patch-with-stat_master)2
-rw-r--r--t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_main (renamed from t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master)2
-rw-r--r--t/t4013/diff.whatchanged_--root_-p_main (renamed from t/t4013/diff.whatchanged_--root_-p_master)2
-rw-r--r--t/t4013/diff.whatchanged_--root_main (renamed from t/t4013/diff.whatchanged_--root_master)2
-rw-r--r--t/t4013/diff.whatchanged_-SF_-p_main (renamed from t/t4013/diff.whatchanged_-SF_-p_master)2
-rw-r--r--t/t4013/diff.whatchanged_-SF_main (renamed from t/t4013/diff.whatchanged_-SF_master)2
-rw-r--r--t/t4013/diff.whatchanged_-p_main (renamed from t/t4013/diff.whatchanged_-p_master)2
-rw-r--r--t/t4013/diff.whatchanged_main (renamed from t/t4013/diff.whatchanged_master)2
-rwxr-xr-xt/t4015-diff-whitespace.sh8
-rwxr-xr-xt/t4020-diff-external.sh10
-rwxr-xr-xt/t4035-diff-quiet.sh4
-rwxr-xr-xt/t4053-diff-no-index.sh33
-rwxr-xr-xt/t4072-diff-max-depth.sh116
-rwxr-xr-xt/t4211-line-log.sh2
-rw-r--r--t/t4211/sha1/expect.multiple6
-rw-r--r--t/t4211/sha1/expect.no-assertion-error90
-rw-r--r--t/t4211/sha1/expect.two-ranges6
-rw-r--r--t/t4211/sha256/expect.multiple6
-rw-r--r--t/t4211/sha256/expect.no-assertion-error90
-rw-r--r--t/t4211/sha256/expect.two-ranges6
-rwxr-xr-xt/t4216-log-bloom.sh31
-rwxr-xr-xt/t5200-update-server-info.sh5
-rwxr-xr-xt/t5304-prune.sh5
-rwxr-xr-xt/t5318-commit-graph.sh44
-rwxr-xr-xt/t5319-multi-pack-index.sh30
-rwxr-xr-xt/t5505-remote.sh73
-rwxr-xr-xt/t5510-fetch.sh657
-rwxr-xr-xt/t5516-fetch-push.sh54
-rwxr-xr-xt/t5530-upload-pack-error.sh68
-rwxr-xr-xt/t5564-http-proxy.sh4
-rwxr-xr-xt/t5710-promisor-remote-capability.sh65
-rwxr-xr-xt/t6120-describe.sh30
-rwxr-xr-xt/t6137-pathspec-wildcards-literal.sh12
-rwxr-xr-xt/t6300-for-each-ref.sh2140
-rwxr-xr-xt/t6302-for-each-ref-filter.sh65
-rwxr-xr-xt/t6423-merge-rename-directories.sh519
-rwxr-xr-xt/t7005-editor.sh145
-rwxr-xr-xt/t7500-commit-template-squash-signoff.sh19
-rwxr-xr-xt/t7502-commit-porcelain.sh52
-rwxr-xr-xt/t7508-status.sh11
-rwxr-xr-xt/t7528-signed-commit-ssh.sh2
-rwxr-xr-xt/t7700-repack.sh63
-rwxr-xr-xt/t7900-maintenance.sh245
-rwxr-xr-xt/t8020-last-modified.sh230
-rwxr-xr-xt/t9300-fast-import.sh20
-rwxr-xr-xt/t9305-fast-import-signatures.sh106
-rwxr-xr-xt/t9306-fast-import-signed-tags.sh80
-rwxr-xr-xt/t9350-fast-export.sh48
-rwxr-xr-xt/t9902-completion.sh6
-rw-r--r--t/test-lib-functions.sh9
-rw-r--r--t/test-lib.sh26
-rw-r--r--t/unit-tests/clar/.github/workflows/ci.yml37
-rw-r--r--t/unit-tests/clar/CMakeLists.txt13
-rw-r--r--t/unit-tests/clar/README.md37
-rw-r--r--t/unit-tests/clar/clar.c154
-rw-r--r--t/unit-tests/clar/clar.h83
-rw-r--r--t/unit-tests/clar/clar/fixtures.h6
-rw-r--r--t/unit-tests/clar/clar/fs.h29
-rw-r--r--t/unit-tests/clar/clar/print.h71
-rw-r--r--t/unit-tests/clar/clar/sandbox.h226
-rw-r--r--t/unit-tests/clar/clar/summary.h5
-rw-r--r--t/unit-tests/clar/example/CMakeLists.txt28
-rw-r--r--t/unit-tests/clar/example/example.c6
-rw-r--r--t/unit-tests/clar/example/main.c (renamed from t/unit-tests/clar/test/main.c.sample)2
-rwxr-xr-xt/unit-tests/clar/generate.py28
-rw-r--r--t/unit-tests/clar/test/CMakeLists.txt34
-rw-r--r--t/unit-tests/clar/test/clar_test.h16
-rw-r--r--t/unit-tests/clar/test/expected/help12
-rw-r--r--t/unit-tests/clar/test/expected/quiet44
-rw-r--r--t/unit-tests/clar/test/expected/specific_test9
-rw-r--r--t/unit-tests/clar/test/expected/stop_on_failure8
-rw-r--r--t/unit-tests/clar/test/expected/suite_names2
-rw-r--r--t/unit-tests/clar/test/expected/summary.xml41
-rw-r--r--t/unit-tests/clar/test/expected/summary_with_filename49
-rw-r--r--t/unit-tests/clar/test/expected/summary_without_filename49
-rw-r--r--t/unit-tests/clar/test/expected/tap92
-rw-r--r--t/unit-tests/clar/test/expected/without_arguments48
-rw-r--r--t/unit-tests/clar/test/main.c41
-rw-r--r--t/unit-tests/clar/test/selftest.c370
-rw-r--r--t/unit-tests/clar/test/selftest.h3
-rw-r--r--t/unit-tests/clar/test/suites/CMakeLists.txt53
-rw-r--r--t/unit-tests/clar/test/suites/combined.c (renamed from t/unit-tests/clar/test/sample.c)37
-rw-r--r--t/unit-tests/clar/test/suites/main.c27
-rw-r--r--t/unit-tests/clar/test/suites/pointer.c13
-rw-r--r--t/unit-tests/clar/test/suites/resources/test/file (renamed from t/unit-tests/clar/test/resources/test/file)0
-rw-r--r--t/unit-tests/u-dir.c47
-rw-r--r--t/unit-tests/u-reftable-basics.c24
-rw-r--r--t/unit-tests/u-reftable-stack.c59
-rw-r--r--t/unit-tests/u-string-list.c95
236 files changed, 8841 insertions, 3998 deletions
diff --git a/t/Makefile b/t/Makefile
index 757674e727..ab8a5b54aa 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -189,15 +189,9 @@ perf:
.PHONY: libgit-sys-test libgit-rs-test
libgit-sys-test:
- $(QUIET)(\
- cd ../contrib/libgit-sys && \
- cargo test \
- )
-libgit-rs-test:
- $(QUIET)(\
- cd ../contrib/libgit-rs && \
- cargo test \
- )
+ $(QUIET)cargo test --manifest-path ../contrib/libgit-sys/Cargo.toml
+libgit-rs-test: libgit-sys-test
+ $(QUIET)cargo test --manifest-path ../contrib/libgit-rs/Cargo.toml
ifdef INCLUDE_LIBGIT_RS
-all:: libgit-sys-test libgit-rs-test
+all:: libgit-rs-test
endif
diff --git a/t/for-each-ref-tests.sh b/t/for-each-ref-tests.sh
new file mode 100644
index 0000000000..e3ad19298a
--- /dev/null
+++ b/t/for-each-ref-tests.sh
@@ -0,0 +1,2141 @@
+git_for_each_ref=${git_for_each_ref:-git for-each-ref}
+GNUPGHOME_NOT_USED=$GNUPGHOME
+. "$TEST_DIRECTORY"/lib-gpg.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
+
+# Mon Jul 3 23:18:43 2006 +0000
+datestamp=1151968723
+setdate_and_increment () {
+ GIT_COMMITTER_DATE="$datestamp +0200"
+ datestamp=$(expr "$datestamp" + 1)
+ GIT_AUTHOR_DATE="$datestamp +0200"
+ datestamp=$(expr "$datestamp" + 1)
+ export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
+}
+
+test_object_file_size () {
+ oid=$(git rev-parse "$1")
+ path=".git/objects/$(test_oid_to_path $oid)"
+ test_file_size "$path"
+}
+
+test_expect_success setup '
+ # setup .mailmap
+ cat >.mailmap <<-EOF &&
+ A Thor <athor@example.com> A U Thor <author@example.com>
+ C Mitter <cmitter@example.com> C O Mitter <committer@example.com>
+ EOF
+
+ setdate_and_increment &&
+ echo "Using $datestamp" > one &&
+ git add one &&
+ git commit -m "Initial" &&
+ git branch -M main &&
+ setdate_and_increment &&
+ git tag -a -m "Tagging at $datestamp" testtag &&
+ git update-ref refs/remotes/origin/main main &&
+ git remote add origin nowhere &&
+ git config branch.main.remote origin &&
+ git config branch.main.merge refs/heads/main &&
+ git remote add myfork elsewhere &&
+ git config remote.pushdefault myfork &&
+ git config push.default current
+'
+
+test_atom () {
+ case "$1" in
+ head) ref=refs/heads/main ;;
+ tag) ref=refs/tags/testtag ;;
+ sym) ref=refs/heads/sym ;;
+ *) ref=$1 ;;
+ esac
+ format=$2
+ test_do=test_expect_${4:-success}
+
+ printf '%s\n' "$3" >expected
+ $test_do $PREREQ "basic atom: $ref $format" '
+ ${git_for_each_ref} --format="%($format)" "$ref" >actual &&
+ sanitize_pgp <actual >actual.clean &&
+ test_cmp expected actual.clean
+ '
+
+ # Automatically test "contents:size" atom after testing "contents"
+ if test "$format" = "contents"
+ then
+ # for commit leg, $3 is changed there
+ expect=$(printf '%s' "$3" | wc -c)
+ $test_do $PREREQ "basic atom: $ref contents:size" '
+ type=$(git cat-file -t "$ref") &&
+ case $type in
+ tag)
+ # We cannot use $3 as it expects sanitize_pgp to run
+ git cat-file tag $ref >out &&
+ expect=$(tail -n +6 out | wc -c) &&
+ rm -f out ;;
+ tree | blob)
+ expect="" ;;
+ commit)
+ : "use the calculated expect" ;;
+ *)
+ BUG "unknown object type" ;;
+ esac &&
+ # Leave $expect unquoted to lose possible leading whitespaces
+ echo $expect >expected &&
+ ${git_for_each_ref} --format="%(contents:size)" "$ref" >actual &&
+ test_cmp expected actual
+ '
+ fi
+}
+
+hexlen=$(test_oid hexsz)
+
+test_atom head refname refs/heads/main
+test_atom head refname: refs/heads/main
+test_atom head refname:short main
+test_atom head refname:lstrip=1 heads/main
+test_atom head refname:lstrip=2 main
+test_atom head refname:lstrip=-1 main
+test_atom head refname:lstrip=-2 heads/main
+test_atom head refname:rstrip=1 refs/heads
+test_atom head refname:rstrip=2 refs
+test_atom head refname:rstrip=-1 refs
+test_atom head refname:rstrip=-2 refs/heads
+test_atom head refname:strip=1 heads/main
+test_atom head refname:strip=2 main
+test_atom head refname:strip=-1 main
+test_atom head refname:strip=-2 heads/main
+test_atom head upstream refs/remotes/origin/main
+test_atom head upstream:short origin/main
+test_atom head upstream:lstrip=2 origin/main
+test_atom head upstream:lstrip=-2 origin/main
+test_atom head upstream:rstrip=2 refs/remotes
+test_atom head upstream:rstrip=-2 refs/remotes
+test_atom head upstream:strip=2 origin/main
+test_atom head upstream:strip=-2 origin/main
+test_atom head push refs/remotes/myfork/main
+test_atom head push:short myfork/main
+test_atom head push:lstrip=1 remotes/myfork/main
+test_atom head push:lstrip=-1 main
+test_atom head push:rstrip=1 refs/remotes/myfork
+test_atom head push:rstrip=-1 refs
+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 $(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)
+test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/main)
+test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/main)
+test_atom head tree $(git rev-parse refs/heads/main^{tree})
+test_atom head tree:short $(git rev-parse --short refs/heads/main^{tree})
+test_atom head tree:short=1 $(git rev-parse --short=1 refs/heads/main^{tree})
+test_atom head tree:short=10 $(git rev-parse --short=10 refs/heads/main^{tree})
+test_atom head parent ''
+test_atom head parent:short ''
+test_atom head parent:short=1 ''
+test_atom head parent:short=10 ''
+test_atom head numparent 0
+test_atom head object ''
+test_atom head type ''
+test_atom head raw "$(git cat-file commit refs/heads/main)
+"
+test_atom head '*objectname' ''
+test_atom head '*objecttype' ''
+test_atom head author 'A U Thor <author@example.com> 1151968724 +0200'
+test_atom head authorname 'A U Thor'
+test_atom head authorname:mailmap 'A Thor'
+test_atom head authoremail '<author@example.com>'
+test_atom head authoremail:trim 'author@example.com'
+test_atom head authoremail:localpart 'author'
+test_atom head authoremail:trim,localpart 'author'
+test_atom head authoremail:mailmap '<athor@example.com>'
+test_atom head authoremail:mailmap,trim 'athor@example.com'
+test_atom head authoremail:trim,mailmap 'athor@example.com'
+test_atom head authoremail:mailmap,localpart 'athor'
+test_atom head authoremail:localpart,mailmap 'athor'
+test_atom head authoremail:mailmap,trim,localpart,mailmap,trim 'athor'
+test_atom head authordate 'Tue Jul 4 01:18:44 2006 +0200'
+test_atom head committer 'C O Mitter <committer@example.com> 1151968723 +0200'
+test_atom head committername 'C O Mitter'
+test_atom head committername:mailmap 'C Mitter'
+test_atom head committeremail '<committer@example.com>'
+test_atom head committeremail:trim 'committer@example.com'
+test_atom head committeremail:localpart 'committer'
+test_atom head committeremail:localpart,trim 'committer'
+test_atom head committeremail:mailmap '<cmitter@example.com>'
+test_atom head committeremail:mailmap,trim 'cmitter@example.com'
+test_atom head committeremail:trim,mailmap 'cmitter@example.com'
+test_atom head committeremail:mailmap,localpart 'cmitter'
+test_atom head committeremail:localpart,mailmap 'cmitter'
+test_atom head committeremail:trim,mailmap,trim,trim,localpart 'cmitter'
+test_atom head committerdate 'Tue Jul 4 01:18:43 2006 +0200'
+test_atom head tag ''
+test_atom head tagger ''
+test_atom head taggername ''
+test_atom head taggeremail ''
+test_atom head taggeremail:trim ''
+test_atom head taggeremail:localpart ''
+test_atom head taggerdate ''
+test_atom head creator 'C O Mitter <committer@example.com> 1151968723 +0200'
+test_atom head creatordate 'Tue Jul 4 01:18:43 2006 +0200'
+test_atom head subject 'Initial'
+test_atom head subject:sanitize 'Initial'
+test_atom head contents:subject 'Initial'
+test_atom head body ''
+test_atom head contents:body ''
+test_atom head contents:signature ''
+test_atom head contents 'Initial
+'
+test_atom head HEAD '*'
+
+test_atom tag refname refs/tags/testtag
+test_atom tag refname:short testtag
+test_atom tag upstream ''
+test_atom tag push ''
+test_atom tag objecttype tag
+test_atom tag objectsize $((114 + hexlen))
+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)
+test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag)
+test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/main)
+test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/main)
+test_atom tag tree ''
+test_atom tag tree:short ''
+test_atom tag tree:short=1 ''
+test_atom tag tree:short=10 ''
+test_atom tag parent ''
+test_atom tag parent:short ''
+test_atom tag parent:short=1 ''
+test_atom tag parent:short=10 ''
+test_atom tag numparent ''
+test_atom tag object $(git rev-parse refs/tags/testtag^0)
+test_atom tag type 'commit'
+test_atom tag '*objectname' $(git rev-parse refs/tags/testtag^{})
+test_atom tag '*objecttype' 'commit'
+test_atom tag author ''
+test_atom tag authorname ''
+test_atom tag authorname:mailmap ''
+test_atom tag authoremail ''
+test_atom tag authoremail:trim ''
+test_atom tag authoremail:localpart ''
+test_atom tag authoremail:trim,localpart ''
+test_atom tag authoremail:mailmap ''
+test_atom tag authoremail:mailmap,trim ''
+test_atom tag authoremail:trim,mailmap ''
+test_atom tag authoremail:mailmap,localpart ''
+test_atom tag authoremail:localpart,mailmap ''
+test_atom tag authoremail:mailmap,trim,localpart,mailmap,trim ''
+test_atom tag authordate ''
+test_atom tag committer ''
+test_atom tag committername ''
+test_atom tag committername:mailmap ''
+test_atom tag committeremail ''
+test_atom tag committeremail:trim ''
+test_atom tag committeremail:localpart ''
+test_atom tag committeremail:localpart,trim ''
+test_atom tag committeremail:mailmap ''
+test_atom tag committeremail:mailmap,trim ''
+test_atom tag committeremail:trim,mailmap ''
+test_atom tag committeremail:mailmap,localpart ''
+test_atom tag committeremail:localpart,mailmap ''
+test_atom tag committeremail:trim,mailmap,trim,trim,localpart ''
+test_atom tag committerdate ''
+test_atom tag tag 'testtag'
+test_atom tag tagger 'C O Mitter <committer@example.com> 1151968725 +0200'
+test_atom tag taggername 'C O Mitter'
+test_atom tag taggername:mailmap 'C Mitter'
+test_atom tag taggeremail '<committer@example.com>'
+test_atom tag taggeremail:trim 'committer@example.com'
+test_atom tag taggeremail:localpart 'committer'
+test_atom tag taggeremail:trim,localpart 'committer'
+test_atom tag taggeremail:mailmap '<cmitter@example.com>'
+test_atom tag taggeremail:mailmap,trim 'cmitter@example.com'
+test_atom tag taggeremail:trim,mailmap 'cmitter@example.com'
+test_atom tag taggeremail:mailmap,localpart 'cmitter'
+test_atom tag taggeremail:localpart,mailmap 'cmitter'
+test_atom tag taggeremail:trim,mailmap,trim,localpart,localpart 'cmitter'
+test_atom tag taggerdate 'Tue Jul 4 01:18:45 2006 +0200'
+test_atom tag creator 'C O Mitter <committer@example.com> 1151968725 +0200'
+test_atom tag creatordate 'Tue Jul 4 01:18:45 2006 +0200'
+test_atom tag subject 'Tagging at 1151968727'
+test_atom tag subject:sanitize 'Tagging-at-1151968727'
+test_atom tag contents:subject 'Tagging at 1151968727'
+test_atom tag body ''
+test_atom tag contents:body ''
+test_atom tag contents:signature ''
+test_atom tag contents 'Tagging at 1151968727
+'
+test_atom tag HEAD ' '
+
+test_expect_success 'basic atom: refs/tags/testtag *raw' '
+ git cat-file commit refs/tags/testtag^{} >expected &&
+ ${git_for_each_ref} --format="%(*raw)" refs/tags/testtag >actual &&
+ sanitize_pgp <expected >expected.clean &&
+ echo >>expected.clean &&
+ sanitize_pgp <actual >actual.clean &&
+ test_cmp expected.clean actual.clean
+'
+
+test_expect_success 'Check invalid atoms names are errors' '
+ test_must_fail ${git_for_each_ref} --format="%(INVALID)" refs/heads
+'
+
+test_expect_success 'Check format specifiers are ignored in naming date atoms' '
+ ${git_for_each_ref} --format="%(authordate)" refs/heads &&
+ ${git_for_each_ref} --format="%(authordate:default) %(authordate)" refs/heads &&
+ ${git_for_each_ref} --format="%(authordate) %(authordate:default)" refs/heads &&
+ ${git_for_each_ref} --format="%(authordate:default) %(authordate:default)" refs/heads
+'
+
+test_expect_success 'Check valid format specifiers for date fields' '
+ ${git_for_each_ref} --format="%(authordate:default)" refs/heads &&
+ ${git_for_each_ref} --format="%(authordate:relative)" refs/heads &&
+ ${git_for_each_ref} --format="%(authordate:short)" refs/heads &&
+ ${git_for_each_ref} --format="%(authordate:local)" refs/heads &&
+ ${git_for_each_ref} --format="%(authordate:iso8601)" refs/heads &&
+ ${git_for_each_ref} --format="%(authordate:rfc2822)" refs/heads
+'
+
+test_expect_success 'Check invalid format specifiers are errors' '
+ test_must_fail ${git_for_each_ref} --format="%(authordate:INVALID)" refs/heads
+'
+
+test_expect_success 'arguments to %(objectname:short=) must be positive integers' '
+ test_must_fail ${git_for_each_ref} --format="%(objectname:short=0)" &&
+ test_must_fail ${git_for_each_ref} --format="%(objectname:short=-1)" &&
+ test_must_fail ${git_for_each_ref} --format="%(objectname:short=foo)"
+'
+
+test_bad_atom () {
+ case "$1" in
+ head) ref=refs/heads/main ;;
+ tag) ref=refs/tags/testtag ;;
+ sym) ref=refs/heads/sym ;;
+ *) ref=$1 ;;
+ esac
+ format=$2
+ test_do=test_expect_${4:-success}
+
+ printf '%s\n' "$3" >expect
+ $test_do $PREREQ "err basic atom: $ref $format" '
+ test_must_fail ${git_for_each_ref} \
+ --format="%($format)" "$ref" 2>error &&
+ test_cmp expect error
+ '
+}
+
+test_bad_atom head 'authoremail:foo' \
+ 'fatal: unrecognized %(authoremail) argument: foo'
+
+test_bad_atom head 'authoremail:mailmap,trim,bar' \
+ 'fatal: unrecognized %(authoremail) argument: bar'
+
+test_bad_atom head 'authoremail:trim,' \
+ 'fatal: unrecognized %(authoremail) argument: '
+
+test_bad_atom head 'authoremail:mailmaptrim' \
+ 'fatal: unrecognized %(authoremail) argument: trim'
+
+test_bad_atom head 'committeremail: ' \
+ 'fatal: unrecognized %(committeremail) argument: '
+
+test_bad_atom head 'committeremail: trim,foo' \
+ 'fatal: unrecognized %(committeremail) argument: trim,foo'
+
+test_bad_atom head 'committeremail:mailmap,localpart ' \
+ 'fatal: unrecognized %(committeremail) argument: '
+
+test_bad_atom head 'committeremail:trim_localpart' \
+ 'fatal: unrecognized %(committeremail) argument: _localpart'
+
+test_bad_atom head 'committeremail:localpart,,,trim' \
+ 'fatal: unrecognized %(committeremail) argument: ,,trim'
+
+test_bad_atom tag 'taggeremail:mailmap,trim, foo ' \
+ 'fatal: unrecognized %(taggeremail) argument: foo '
+
+test_bad_atom tag 'taggeremail:trim,localpart,' \
+ 'fatal: unrecognized %(taggeremail) argument: '
+
+test_bad_atom tag 'taggeremail:mailmap;localpart trim' \
+ 'fatal: unrecognized %(taggeremail) argument: ;localpart trim'
+
+test_bad_atom tag 'taggeremail:localpart trim' \
+ 'fatal: unrecognized %(taggeremail) argument: trim'
+
+test_bad_atom tag 'taggeremail:mailmap,mailmap,trim,qux,localpart,trim' \
+ 'fatal: unrecognized %(taggeremail) argument: qux,localpart,trim'
+
+test_date () {
+ f=$1 &&
+ committer_date=$2 &&
+ author_date=$3 &&
+ tagger_date=$4 &&
+ cat >expected <<-EOF &&
+ 'refs/heads/main' '$committer_date' '$author_date'
+ 'refs/tags/testtag' '$tagger_date'
+ EOF
+ (
+ ${git_for_each_ref} --shell \
+ --format="%(refname) %(committerdate${f:+:$f}) %(authordate${f:+:$f})" \
+ refs/heads &&
+ ${git_for_each_ref} --shell \
+ --format="%(refname) %(taggerdate${f:+:$f})" \
+ refs/tags
+ ) >actual &&
+ test_cmp expected actual
+}
+
+test_expect_success 'Check unformatted date fields output' '
+ test_date "" \
+ "Tue Jul 4 01:18:43 2006 +0200" \
+ "Tue Jul 4 01:18:44 2006 +0200" \
+ "Tue Jul 4 01:18:45 2006 +0200"
+'
+
+test_expect_success 'Check format "default" formatted date fields output' '
+ test_date default \
+ "Tue Jul 4 01:18:43 2006 +0200" \
+ "Tue Jul 4 01:18:44 2006 +0200" \
+ "Tue Jul 4 01:18:45 2006 +0200"
+'
+
+test_expect_success 'Check format "default-local" date fields output' '
+ test_date default-local "Mon Jul 3 23:18:43 2006" "Mon Jul 3 23:18:44 2006" "Mon Jul 3 23:18:45 2006"
+'
+
+# Don't know how to do relative check because I can't know when this script
+# is going to be run and can't fake the current time to git, and hence can't
+# provide expected output. Instead, I'll just make sure that "relative"
+# doesn't exit in error
+test_expect_success 'Check format "relative" date fields output' '
+ f=relative &&
+ (${git_for_each_ref} --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
+ ${git_for_each_ref} --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual
+'
+
+# We just check that this is the same as "relative" for now.
+test_expect_success 'Check format "relative-local" date fields output' '
+ test_date relative-local \
+ "$(${git_for_each_ref} --format="%(committerdate:relative)" refs/heads)" \
+ "$(${git_for_each_ref} --format="%(authordate:relative)" refs/heads)" \
+ "$(${git_for_each_ref} --format="%(taggerdate:relative)" refs/tags)"
+'
+
+test_expect_success 'Check format "short" date fields output' '
+ test_date short 2006-07-04 2006-07-04 2006-07-04
+'
+
+test_expect_success 'Check format "short-local" date fields output' '
+ test_date short-local 2006-07-03 2006-07-03 2006-07-03
+'
+
+test_expect_success 'Check format "local" date fields output' '
+ test_date local \
+ "Mon Jul 3 23:18:43 2006" \
+ "Mon Jul 3 23:18:44 2006" \
+ "Mon Jul 3 23:18:45 2006"
+'
+
+test_expect_success 'Check format "iso8601" date fields output' '
+ test_date iso8601 \
+ "2006-07-04 01:18:43 +0200" \
+ "2006-07-04 01:18:44 +0200" \
+ "2006-07-04 01:18:45 +0200"
+'
+
+test_expect_success 'Check format "iso8601-local" date fields output' '
+ test_date iso8601-local "2006-07-03 23:18:43 +0000" "2006-07-03 23:18:44 +0000" "2006-07-03 23:18:45 +0000"
+'
+
+test_expect_success 'Check format "rfc2822" date fields output' '
+ test_date rfc2822 \
+ "Tue, 4 Jul 2006 01:18:43 +0200" \
+ "Tue, 4 Jul 2006 01:18:44 +0200" \
+ "Tue, 4 Jul 2006 01:18:45 +0200"
+'
+
+test_expect_success 'Check format "rfc2822-local" date fields output' '
+ test_date rfc2822-local "Mon, 3 Jul 2006 23:18:43 +0000" "Mon, 3 Jul 2006 23:18:44 +0000" "Mon, 3 Jul 2006 23:18:45 +0000"
+'
+
+test_expect_success 'Check format "raw" date fields output' '
+ test_date raw "1151968723 +0200" "1151968724 +0200" "1151968725 +0200"
+'
+
+test_expect_success 'Check format "raw-local" date fields output' '
+ test_date raw-local "1151968723 +0000" "1151968724 +0000" "1151968725 +0000"
+'
+
+test_expect_success 'Check format of strftime date fields' '
+ echo "my date is 2006-07-04" >expected &&
+ ${git_for_each_ref} \
+ --format="%(authordate:format:my date is %Y-%m-%d)" \
+ refs/heads >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'Check format of strftime-local date fields' '
+ echo "my date is 2006-07-03" >expected &&
+ ${git_for_each_ref} \
+ --format="%(authordate:format-local:my date is %Y-%m-%d)" \
+ refs/heads >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'exercise strftime with odd fields' '
+ echo >expected &&
+ ${git_for_each_ref} --format="%(authordate:format:)" refs/heads >actual &&
+ test_cmp expected actual &&
+ long="long format -- $ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID" &&
+ echo $long >expected &&
+ ${git_for_each_ref} --format="%(authordate:format:$long)" refs/heads >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+refs/heads/main
+refs/remotes/origin/main
+refs/tags/testtag
+EOF
+
+test_expect_success 'Verify ascending sort' '
+ ${git_for_each_ref} --format="%(refname)" --sort=refname >actual &&
+ test_cmp expected actual
+'
+
+
+cat >expected <<\EOF
+refs/tags/testtag
+refs/remotes/origin/main
+refs/heads/main
+EOF
+
+test_expect_success 'Verify descending sort' '
+ ${git_for_each_ref} --format="%(refname)" --sort=-refname >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'Give help even with invalid sort atoms' '
+ test_expect_code 129 ${git_for_each_ref} --sort=bogus -h >actual 2>&1 &&
+ grep "^usage: ${git_for_each_ref}" actual
+'
+
+cat >expected <<\EOF
+refs/tags/testtag
+refs/tags/testtag-2
+EOF
+
+test_expect_success 'exercise patterns with prefixes' '
+ git tag testtag-2 &&
+ test_when_finished "git tag -d testtag-2" &&
+ ${git_for_each_ref} --format="%(refname)" \
+ refs/tags/testtag refs/tags/testtag-2 >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+refs/tags/testtag
+refs/tags/testtag-2
+EOF
+
+test_expect_success 'exercise glob patterns with prefixes' '
+ git tag testtag-2 &&
+ test_when_finished "git tag -d testtag-2" &&
+ ${git_for_each_ref} --format="%(refname)" \
+ refs/tags/testtag "refs/tags/testtag-*" >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+refs/tags/bar
+refs/tags/baz
+refs/tags/testtag
+EOF
+
+test_expect_success 'exercise patterns with prefix exclusions' '
+ for tag in foo/one foo/two foo/three bar baz
+ do
+ git tag "$tag" || return 1
+ done &&
+ test_when_finished "git tag -d foo/one foo/two foo/three bar baz" &&
+ ${git_for_each_ref} --format="%(refname)" \
+ refs/tags/ --exclude=refs/tags/foo >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+refs/tags/bar
+refs/tags/baz
+refs/tags/foo/one
+refs/tags/testtag
+EOF
+
+test_expect_success 'exercise patterns with pattern exclusions' '
+ for tag in foo/one foo/two foo/three bar baz
+ do
+ git tag "$tag" || return 1
+ done &&
+ test_when_finished "git tag -d foo/one foo/two foo/three bar baz" &&
+ ${git_for_each_ref} --format="%(refname)" \
+ refs/tags/ --exclude="refs/tags/foo/t*" >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+'refs/heads/main'
+'refs/remotes/origin/main'
+'refs/tags/testtag'
+EOF
+
+test_expect_success 'Quoting style: shell' '
+ ${git_for_each_ref} --shell --format="%(refname)" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'Quoting style: perl' '
+ ${git_for_each_ref} --perl --format="%(refname)" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'Quoting style: python' '
+ ${git_for_each_ref} --python --format="%(refname)" >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+"refs/heads/main"
+"refs/remotes/origin/main"
+"refs/tags/testtag"
+EOF
+
+test_expect_success 'Quoting style: tcl' '
+ ${git_for_each_ref} --tcl --format="%(refname)" >actual &&
+ test_cmp expected actual
+'
+
+for i in "--perl --shell" "-s --python" "--python --tcl" "--tcl --perl"; do
+ test_expect_success "more than one quoting style: $i" "
+ test_must_fail ${git_for_each_ref} $i 2>err &&
+ grep '^error: more than one quoting style' err
+ "
+done
+
+test_expect_success 'setup for upstream:track[short]' '
+ test_commit two
+'
+
+test_atom head upstream:track '[ahead 1]'
+test_atom head upstream:trackshort '>'
+test_atom head upstream:track,nobracket 'ahead 1'
+test_atom head upstream:nobracket,track 'ahead 1'
+
+test_expect_success 'setup for push:track[short]' '
+ test_commit third &&
+ git update-ref refs/remotes/myfork/main main &&
+ git reset main~1
+'
+
+test_atom head push:track '[behind 1]'
+test_atom head push:trackshort '<'
+
+test_expect_success 'Check that :track[short] cannot be used with other atoms' '
+ test_must_fail ${git_for_each_ref} --format="%(refname:track)" 2>/dev/null &&
+ test_must_fail ${git_for_each_ref} --format="%(refname:trackshort)" 2>/dev/null
+'
+
+test_expect_success 'Check that :track[short] works when upstream is invalid' '
+ cat >expected <<-\EOF &&
+ [gone]
+
+ EOF
+ test_when_finished "git config branch.main.merge refs/heads/main" &&
+ git config branch.main.merge refs/heads/does-not-exist &&
+ ${git_for_each_ref} \
+ --format="%(upstream:track)$LF%(upstream:trackshort)" \
+ refs/heads >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'Check for invalid refname format' '
+ test_must_fail ${git_for_each_ref} --format="%(refname:INVALID)"
+'
+
+test_expect_success 'set up color tests' '
+ cat >expected.color <<-EOF &&
+ $(git rev-parse --short refs/heads/main) <GREEN>main<RESET>
+ $(git rev-parse --short refs/remotes/myfork/main) <GREEN>myfork/main<RESET>
+ $(git rev-parse --short refs/remotes/origin/main) <GREEN>origin/main<RESET>
+ $(git rev-parse --short refs/tags/testtag) <GREEN>testtag<RESET>
+ $(git rev-parse --short refs/tags/third) <GREEN>third<RESET>
+ $(git rev-parse --short refs/tags/two) <GREEN>two<RESET>
+ EOF
+ sed "s/<[^>]*>//g" <expected.color >expected.bare &&
+ color_format="%(objectname:short) %(color:green)%(refname:short)"
+'
+
+test_expect_success TTY '%(color) shows color with a tty' '
+ test_terminal ${git_for_each_ref} --format="$color_format" >actual.raw &&
+ test_decode_color <actual.raw >actual &&
+ test_cmp expected.color actual
+'
+
+test_expect_success '%(color) does not show color without tty' '
+ TERM=vt100 ${git_for_each_ref} --format="$color_format" >actual &&
+ test_cmp expected.bare actual
+'
+
+test_expect_success '--color can override tty check' '
+ ${git_for_each_ref} --color --format="$color_format" >actual.raw &&
+ test_decode_color <actual.raw >actual &&
+ test_cmp expected.color actual
+'
+
+test_expect_success 'color.ui=always does not override tty check' '
+ git -c color.ui=always ${git_for_each_ref#git} --format="$color_format" >actual &&
+ test_cmp expected.bare actual
+'
+
+test_expect_success 'setup for describe atom tests' '
+ git init -b master describe-repo &&
+ (
+ cd describe-repo &&
+
+ test_commit --no-tag one &&
+ git tag tagone &&
+
+ test_commit --no-tag two &&
+ git tag -a -m "tag two" tagtwo
+ )
+'
+
+test_expect_success 'describe atom vs git describe' '
+ (
+ cd describe-repo &&
+
+ ${git_for_each_ref} --format="%(objectname)" \
+ refs/tags/ >obj &&
+ while read hash
+ do
+ if desc=$(git describe $hash)
+ then
+ : >expect-contains-good
+ else
+ : >expect-contains-bad
+ fi &&
+ echo "$hash $desc" || return 1
+ done <obj >expect &&
+ test_path_exists expect-contains-good &&
+ test_path_exists expect-contains-bad &&
+
+ ${git_for_each_ref} --format="%(objectname) %(describe)" \
+ refs/tags/ >actual 2>err &&
+ test_cmp expect actual &&
+ test_must_be_empty err
+ )
+'
+
+test_expect_success 'describe:tags vs describe --tags' '
+ (
+ cd describe-repo &&
+ git describe --tags >expect &&
+ ${git_for_each_ref} --format="%(describe:tags)" \
+ refs/heads/master >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'describe:abbrev=... vs describe --abbrev=...' '
+ (
+ cd describe-repo &&
+
+ # Case 1: We have commits between HEAD and the most
+ # recent tag reachable from it
+ test_commit --no-tag file &&
+ git describe --abbrev=14 >expect &&
+ ${git_for_each_ref} --format="%(describe:abbrev=14)" \
+ refs/heads/master >actual &&
+ test_cmp expect actual &&
+
+ # Make sure the hash used is at least 14 digits long
+ sed -e "s/^.*-g\([0-9a-f]*\)$/\1/" <actual >hexpart &&
+ test 15 -le $(wc -c <hexpart) &&
+
+ # Case 2: We have a tag at HEAD, describe directly gives
+ # the name of the tag
+ git tag -a -m tagged tagname &&
+ git describe --abbrev=14 >expect &&
+ ${git_for_each_ref} --format="%(describe:abbrev=14)" \
+ refs/heads/master >actual &&
+ test_cmp expect actual &&
+ test tagname = $(cat actual)
+ )
+'
+
+test_expect_success 'describe:match=... vs describe --match ...' '
+ (
+ cd describe-repo &&
+ git tag -a -m "tag foo" tag-foo &&
+ git describe --match "*-foo" >expect &&
+ ${git_for_each_ref} --format="%(describe:match="*-foo")" \
+ refs/heads/master >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'describe:exclude:... vs describe --exclude ...' '
+ (
+ cd describe-repo &&
+ git tag -a -m "tag bar" tag-bar &&
+ git describe --exclude "*-bar" >expect &&
+ ${git_for_each_ref} --format="%(describe:exclude="*-bar")" \
+ refs/heads/master >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'deref with describe atom' '
+ (
+ cd describe-repo &&
+ cat >expect <<-\EOF &&
+
+ tagname
+ tagname
+ tagname
+
+ tagtwo
+ EOF
+ ${git_for_each_ref} --format="%(*describe)" >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'err on bad describe atom arg' '
+ (
+ cd describe-repo &&
+
+ # The bad arg is the only arg passed to describe atom
+ cat >expect <<-\EOF &&
+ fatal: unrecognized %(describe) argument: baz
+ EOF
+ test_must_fail ${git_for_each_ref} --format="%(describe:baz)" \
+ refs/heads/master 2>actual &&
+ test_cmp expect actual &&
+
+ # The bad arg is in the middle of the option string
+ # passed to the describe atom
+ cat >expect <<-\EOF &&
+ fatal: unrecognized %(describe) argument: qux=1,abbrev=14
+ EOF
+ test_must_fail ${git_for_each_ref} \
+ --format="%(describe:tags,qux=1,abbrev=14)" \
+ ref/heads/master 2>actual &&
+ test_cmp expect actual
+ )
+'
+
+cat >expected <<\EOF
+heads/main
+tags/main
+EOF
+
+test_expect_success 'Check ambiguous head and tag refs (strict)' '
+ git config --bool core.warnambiguousrefs true &&
+ git checkout -b newtag &&
+ echo "Using $datestamp" > one &&
+ git add one &&
+ git commit -m "Branch" &&
+ setdate_and_increment &&
+ git tag -m "Tagging at $datestamp" main &&
+ ${git_for_each_ref} --format "%(refname:short)" refs/heads/main refs/tags/main >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+heads/main
+main
+EOF
+
+test_expect_success 'Check ambiguous head and tag refs (loose)' '
+ git config --bool core.warnambiguousrefs false &&
+ ${git_for_each_ref} --format "%(refname:short)" refs/heads/main refs/tags/main >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+heads/ambiguous
+ambiguous
+EOF
+
+test_expect_success 'Check ambiguous head and tag refs II (loose)' '
+ git checkout main &&
+ git tag ambiguous testtag^0 &&
+ git branch ambiguous testtag^0 &&
+ ${git_for_each_ref} --format "%(refname:short)" refs/heads/ambiguous refs/tags/ambiguous >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'create tag without tagger' '
+ git tag -a -m "Broken tag" taggerless &&
+ git tag -f taggerless $(git cat-file tag taggerless |
+ sed -e "/^tagger /d" |
+ git hash-object --literally --stdin -w -t tag)
+'
+
+test_atom refs/tags/taggerless type 'commit'
+test_atom refs/tags/taggerless tag 'taggerless'
+test_atom refs/tags/taggerless tagger ''
+test_atom refs/tags/taggerless taggername ''
+test_atom refs/tags/taggerless taggeremail ''
+test_atom refs/tags/taggerless taggeremail:trim ''
+test_atom refs/tags/taggerless taggeremail:localpart ''
+test_atom refs/tags/taggerless taggerdate ''
+test_atom refs/tags/taggerless committer ''
+test_atom refs/tags/taggerless committername ''
+test_atom refs/tags/taggerless committeremail ''
+test_atom refs/tags/taggerless committeremail:trim ''
+test_atom refs/tags/taggerless committeremail:localpart ''
+test_atom refs/tags/taggerless committerdate ''
+test_atom refs/tags/taggerless subject 'Broken tag'
+
+test_expect_success 'an unusual tag with an incomplete line' '
+
+ git tag -m "bogo" bogo &&
+ bogo=$(git cat-file tag bogo) &&
+ bogo=$(printf "%s" "$bogo" | git mktag) &&
+ git tag -f bogo "$bogo" &&
+ ${git_for_each_ref} --format "%(body)" refs/tags/bogo
+
+'
+
+test_expect_success 'create tag with subject and body content' '
+ cat >>msg <<-\EOF &&
+ the subject line
+
+ first body line
+ second body line
+ EOF
+ git tag -F msg subject-body
+'
+test_atom refs/tags/subject-body subject 'the subject line'
+test_atom refs/tags/subject-body subject:sanitize 'the-subject-line'
+test_atom refs/tags/subject-body body 'first body line
+second body line
+'
+test_atom refs/tags/subject-body contents 'the subject line
+
+first body line
+second body line
+'
+
+test_expect_success 'create tag with multiline subject' '
+ cat >msg <<-\EOF &&
+ first subject line
+ second subject line
+
+ first body line
+ second body line
+ EOF
+ git tag -F msg multiline
+'
+test_atom refs/tags/multiline subject 'first subject line second subject line'
+test_atom refs/tags/multiline subject:sanitize 'first-subject-line-second-subject-line'
+test_atom refs/tags/multiline contents:subject 'first subject line second subject line'
+test_atom refs/tags/multiline body 'first body line
+second body line
+'
+test_atom refs/tags/multiline contents:body 'first body line
+second body line
+'
+test_atom refs/tags/multiline contents:signature ''
+test_atom refs/tags/multiline contents 'first subject line
+second subject line
+
+first body line
+second body line
+'
+
+test_expect_success GPG 'create signed tags' '
+ git tag -s -m "" signed-empty &&
+ git tag -s -m "subject line" signed-short &&
+ cat >msg <<-\EOF &&
+ subject line
+
+ body contents
+ EOF
+ git tag -s -F msg signed-long
+'
+
+sig='-----BEGIN PGP SIGNATURE-----
+-----END PGP SIGNATURE-----
+'
+
+PREREQ=GPG
+test_atom refs/tags/signed-empty subject ''
+test_atom refs/tags/signed-empty subject:sanitize ''
+test_atom refs/tags/signed-empty contents:subject ''
+test_atom refs/tags/signed-empty body "$sig"
+test_atom refs/tags/signed-empty contents:body ''
+test_atom refs/tags/signed-empty contents:signature "$sig"
+test_atom refs/tags/signed-empty contents "$sig"
+
+test_expect_success GPG 'basic atom: refs/tags/signed-empty raw' '
+ git cat-file tag refs/tags/signed-empty >expected &&
+ ${git_for_each_ref} --format="%(raw)" refs/tags/signed-empty >actual &&
+ sanitize_pgp <expected >expected.clean &&
+ echo >>expected.clean &&
+ sanitize_pgp <actual >actual.clean &&
+ test_cmp expected.clean actual.clean
+'
+
+test_atom refs/tags/signed-short subject 'subject line'
+test_atom refs/tags/signed-short subject:sanitize 'subject-line'
+test_atom refs/tags/signed-short contents:subject 'subject line'
+test_atom refs/tags/signed-short body "$sig"
+test_atom refs/tags/signed-short contents:body ''
+test_atom refs/tags/signed-short contents:signature "$sig"
+test_atom refs/tags/signed-short contents "subject line
+$sig"
+
+test_expect_success GPG 'basic atom: refs/tags/signed-short raw' '
+ git cat-file tag refs/tags/signed-short >expected &&
+ ${git_for_each_ref} --format="%(raw)" refs/tags/signed-short >actual &&
+ sanitize_pgp <expected >expected.clean &&
+ echo >>expected.clean &&
+ sanitize_pgp <actual >actual.clean &&
+ test_cmp expected.clean actual.clean
+'
+
+test_atom refs/tags/signed-long subject 'subject line'
+test_atom refs/tags/signed-long subject:sanitize 'subject-line'
+test_atom refs/tags/signed-long contents:subject 'subject line'
+test_atom refs/tags/signed-long body "body contents
+$sig"
+test_atom refs/tags/signed-long contents:body 'body contents
+'
+test_atom refs/tags/signed-long contents:signature "$sig"
+test_atom refs/tags/signed-long contents "subject line
+
+body contents
+$sig"
+
+test_expect_success GPG 'basic atom: refs/tags/signed-long raw' '
+ git cat-file tag refs/tags/signed-long >expected &&
+ ${git_for_each_ref} --format="%(raw)" refs/tags/signed-long >actual &&
+ sanitize_pgp <expected >expected.clean &&
+ echo >>expected.clean &&
+ sanitize_pgp <actual >actual.clean &&
+ test_cmp expected.clean actual.clean
+'
+
+test_expect_success 'set up refs pointing to tree and blob' '
+ git update-ref refs/mytrees/first refs/heads/main^{tree} &&
+ git update-ref refs/myblobs/first refs/heads/main:one
+'
+
+test_atom refs/mytrees/first subject ""
+test_atom refs/mytrees/first contents:subject ""
+test_atom refs/mytrees/first body ""
+test_atom refs/mytrees/first contents:body ""
+test_atom refs/mytrees/first contents:signature ""
+test_atom refs/mytrees/first contents ""
+
+test_expect_success 'basic atom: refs/mytrees/first raw' '
+ git cat-file tree refs/mytrees/first >expected &&
+ echo >>expected &&
+ ${git_for_each_ref} --format="%(raw)" refs/mytrees/first >actual &&
+ test_cmp expected actual &&
+ git cat-file -s refs/mytrees/first >expected &&
+ ${git_for_each_ref} --format="%(raw:size)" refs/mytrees/first >actual &&
+ test_cmp expected actual
+'
+
+test_atom refs/myblobs/first subject ""
+test_atom refs/myblobs/first contents:subject ""
+test_atom refs/myblobs/first body ""
+test_atom refs/myblobs/first contents:body ""
+test_atom refs/myblobs/first contents:signature ""
+test_atom refs/myblobs/first contents ""
+
+test_expect_success 'basic atom: refs/myblobs/first raw' '
+ git cat-file blob refs/myblobs/first >expected &&
+ echo >>expected &&
+ ${git_for_each_ref} --format="%(raw)" refs/myblobs/first >actual &&
+ test_cmp expected actual &&
+ git cat-file -s refs/myblobs/first >expected &&
+ ${git_for_each_ref} --format="%(raw:size)" refs/myblobs/first >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'set up refs pointing to binary blob' '
+ printf "a\0b\0c" >blob1 &&
+ printf "a\0c\0b" >blob2 &&
+ printf "\0a\0b\0c" >blob3 &&
+ printf "abc" >blob4 &&
+ printf "\0 \0 \0 " >blob5 &&
+ printf "\0 \0a\0 " >blob6 &&
+ printf " " >blob7 &&
+ >blob8 &&
+ obj=$(git hash-object -w blob1) &&
+ git update-ref refs/myblobs/blob1 "$obj" &&
+ obj=$(git hash-object -w blob2) &&
+ git update-ref refs/myblobs/blob2 "$obj" &&
+ obj=$(git hash-object -w blob3) &&
+ git update-ref refs/myblobs/blob3 "$obj" &&
+ obj=$(git hash-object -w blob4) &&
+ git update-ref refs/myblobs/blob4 "$obj" &&
+ obj=$(git hash-object -w blob5) &&
+ git update-ref refs/myblobs/blob5 "$obj" &&
+ obj=$(git hash-object -w blob6) &&
+ git update-ref refs/myblobs/blob6 "$obj" &&
+ obj=$(git hash-object -w blob7) &&
+ git update-ref refs/myblobs/blob7 "$obj" &&
+ obj=$(git hash-object -w blob8) &&
+ git update-ref refs/myblobs/blob8 "$obj"
+'
+
+test_expect_success 'Verify sorts with raw' '
+ cat >expected <<-EOF &&
+ refs/myblobs/blob8
+ refs/myblobs/blob5
+ refs/myblobs/blob6
+ refs/myblobs/blob3
+ refs/myblobs/blob7
+ refs/mytrees/first
+ refs/myblobs/first
+ refs/myblobs/blob1
+ refs/myblobs/blob2
+ refs/myblobs/blob4
+ refs/heads/main
+ EOF
+ ${git_for_each_ref} --format="%(refname)" --sort=raw \
+ refs/heads/main refs/myblobs/ refs/mytrees/first >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'Verify sorts with raw:size' '
+ cat >expected <<-EOF &&
+ refs/myblobs/blob8
+ refs/myblobs/blob7
+ refs/myblobs/blob4
+ refs/myblobs/blob1
+ refs/myblobs/blob2
+ refs/myblobs/blob3
+ refs/myblobs/blob5
+ refs/myblobs/blob6
+ refs/myblobs/first
+ refs/mytrees/first
+ refs/heads/main
+ EOF
+ ${git_for_each_ref} --format="%(refname)" --sort=raw:size \
+ refs/heads/main refs/myblobs/ refs/mytrees/first >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'validate raw atom with %(if:equals)' '
+ cat >expected <<-EOF &&
+ not equals
+ not equals
+ not equals
+ not equals
+ not equals
+ not equals
+ refs/myblobs/blob4
+ not equals
+ not equals
+ not equals
+ not equals
+ not equals
+ EOF
+ ${git_for_each_ref} --format="%(if:equals=abc)%(raw)%(then)%(refname)%(else)not equals%(end)" \
+ refs/myblobs/ refs/heads/ >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'validate raw atom with %(if:notequals)' '
+ cat >expected <<-EOF &&
+ refs/heads/ambiguous
+ refs/heads/main
+ refs/heads/newtag
+ refs/myblobs/blob1
+ refs/myblobs/blob2
+ refs/myblobs/blob3
+ equals
+ refs/myblobs/blob5
+ refs/myblobs/blob6
+ refs/myblobs/blob7
+ refs/myblobs/blob8
+ refs/myblobs/first
+ EOF
+ ${git_for_each_ref} --format="%(if:notequals=abc)%(raw)%(then)%(refname)%(else)equals%(end)" \
+ refs/myblobs/ refs/heads/ >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'empty raw refs with %(if)' '
+ cat >expected <<-EOF &&
+ refs/myblobs/blob1 not empty
+ refs/myblobs/blob2 not empty
+ refs/myblobs/blob3 not empty
+ refs/myblobs/blob4 not empty
+ refs/myblobs/blob5 not empty
+ refs/myblobs/blob6 not empty
+ refs/myblobs/blob7 empty
+ refs/myblobs/blob8 empty
+ refs/myblobs/first not empty
+ EOF
+ ${git_for_each_ref} --format="%(refname) %(if)%(raw)%(then)not empty%(else)empty%(end)" \
+ refs/myblobs/ >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '%(raw) with --python must fail' '
+ test_must_fail ${git_for_each_ref} --format="%(raw)" --python
+'
+
+test_expect_success '%(raw) with --tcl must fail' '
+ test_must_fail ${git_for_each_ref} --format="%(raw)" --tcl
+'
+
+test_expect_success PERL_TEST_HELPERS '%(raw) with --perl' '
+ ${git_for_each_ref} --format="\$name= %(raw);
+print \"\$name\"" refs/myblobs/blob1 --perl | perl >actual &&
+ cmp blob1 actual &&
+ ${git_for_each_ref} --format="\$name= %(raw);
+print \"\$name\"" refs/myblobs/blob3 --perl | perl >actual &&
+ cmp blob3 actual &&
+ ${git_for_each_ref} --format="\$name= %(raw);
+print \"\$name\"" refs/myblobs/blob8 --perl | perl >actual &&
+ cmp blob8 actual &&
+ ${git_for_each_ref} --format="\$name= %(raw);
+print \"\$name\"" refs/myblobs/first --perl | perl >actual &&
+ cmp one actual &&
+ git cat-file tree refs/mytrees/first > expected &&
+ ${git_for_each_ref} --format="\$name= %(raw);
+print \"\$name\"" refs/mytrees/first --perl | perl >actual &&
+ cmp expected actual
+'
+
+test_expect_success '%(raw) with --shell must fail' '
+ test_must_fail ${git_for_each_ref} --format="%(raw)" --shell
+'
+
+test_expect_success '%(raw) with --shell and --sort=raw must fail' '
+ test_must_fail ${git_for_each_ref} --format="%(raw)" --sort=raw --shell
+'
+
+test_expect_success '%(raw:size) with --shell' '
+ ${git_for_each_ref} --format="%(raw:size)" | sed "s/^/$SQ/;s/$/$SQ/" >expect &&
+ ${git_for_each_ref} --format="%(raw:size)" --shell >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success "${git_for_each_ref} --format compare with cat-file --batch" '
+ git rev-parse refs/mytrees/first | git cat-file --batch >expected &&
+ ${git_for_each_ref} --format="%(objectname) %(objecttype) %(objectsize)
+%(raw)" refs/mytrees/first >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'verify sorts with contents:size' '
+ cat >expect <<-\EOF &&
+ refs/heads/main
+ refs/heads/newtag
+ refs/heads/ambiguous
+ EOF
+ ${git_for_each_ref} --format="%(refname)" \
+ --sort=contents:size refs/heads/ >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'set up multiple-sort tags' '
+ for when in 100000 200000
+ do
+ for email in user1 user2
+ do
+ for ref in ref1 ref2
+ do
+ GIT_COMMITTER_DATE="@$when +0000" \
+ GIT_COMMITTER_EMAIL="$email@example.com" \
+ git tag -m "tag $ref-$when-$email" \
+ multi-$ref-$when-$email || return 1
+ done
+ done
+ done
+'
+
+test_expect_success 'Verify sort with multiple keys' '
+ cat >expected <<-\EOF &&
+ 100000 <user1@example.com> refs/tags/multi-ref2-100000-user1
+ 100000 <user1@example.com> refs/tags/multi-ref1-100000-user1
+ 100000 <user2@example.com> refs/tags/multi-ref2-100000-user2
+ 100000 <user2@example.com> refs/tags/multi-ref1-100000-user2
+ 200000 <user1@example.com> refs/tags/multi-ref2-200000-user1
+ 200000 <user1@example.com> refs/tags/multi-ref1-200000-user1
+ 200000 <user2@example.com> refs/tags/multi-ref2-200000-user2
+ 200000 <user2@example.com> refs/tags/multi-ref1-200000-user2
+ EOF
+ ${git_for_each_ref} \
+ --format="%(taggerdate:unix) %(taggeremail) %(refname)" \
+ --sort=-refname \
+ --sort=taggeremail \
+ --sort=taggerdate \
+ "refs/tags/multi-*" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'equivalent sorts fall back on refname' '
+ cat >expected <<-\EOF &&
+ 100000 <user1@example.com> refs/tags/multi-ref1-100000-user1
+ 100000 <user2@example.com> refs/tags/multi-ref1-100000-user2
+ 100000 <user1@example.com> refs/tags/multi-ref2-100000-user1
+ 100000 <user2@example.com> refs/tags/multi-ref2-100000-user2
+ 200000 <user1@example.com> refs/tags/multi-ref1-200000-user1
+ 200000 <user2@example.com> refs/tags/multi-ref1-200000-user2
+ 200000 <user1@example.com> refs/tags/multi-ref2-200000-user1
+ 200000 <user2@example.com> refs/tags/multi-ref2-200000-user2
+ EOF
+ ${git_for_each_ref} \
+ --format="%(taggerdate:unix) %(taggeremail) %(refname)" \
+ --sort=taggerdate \
+ "refs/tags/multi-*" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '--no-sort cancels the previous sort keys' '
+ cat >expected <<-\EOF &&
+ 100000 <user1@example.com> refs/tags/multi-ref1-100000-user1
+ 100000 <user2@example.com> refs/tags/multi-ref1-100000-user2
+ 100000 <user1@example.com> refs/tags/multi-ref2-100000-user1
+ 100000 <user2@example.com> refs/tags/multi-ref2-100000-user2
+ 200000 <user1@example.com> refs/tags/multi-ref1-200000-user1
+ 200000 <user2@example.com> refs/tags/multi-ref1-200000-user2
+ 200000 <user1@example.com> refs/tags/multi-ref2-200000-user1
+ 200000 <user2@example.com> refs/tags/multi-ref2-200000-user2
+ EOF
+ ${git_for_each_ref} \
+ --format="%(taggerdate:unix) %(taggeremail) %(refname)" \
+ --sort=-refname \
+ --sort=taggeremail \
+ --no-sort \
+ --sort=taggerdate \
+ "refs/tags/multi-*" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '--no-sort without subsequent --sort prints expected refs' '
+ cat >expected <<-\EOF &&
+ refs/tags/multi-ref1-100000-user1
+ refs/tags/multi-ref1-100000-user2
+ refs/tags/multi-ref1-200000-user1
+ refs/tags/multi-ref1-200000-user2
+ refs/tags/multi-ref2-100000-user1
+ refs/tags/multi-ref2-100000-user2
+ refs/tags/multi-ref2-200000-user1
+ refs/tags/multi-ref2-200000-user2
+ EOF
+
+ # Sort the results with `sort` for a consistent comparison against
+ # expected
+ ${git_for_each_ref} \
+ --format="%(refname)" \
+ --no-sort \
+ "refs/tags/multi-*" | sort >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'set up custom date sorting' '
+ # Dates:
+ # - Wed Feb 07 2024 21:34:20 +0000
+ # - Tue Dec 14 1999 00:05:22 +0000
+ # - Fri Jun 04 2021 11:26:51 +0000
+ # - Mon Jan 22 2007 16:44:01 GMT+0000
+ i=1 &&
+ for when in 1707341660 945129922 1622806011 1169484241
+ do
+ GIT_COMMITTER_DATE="@$when +0000" \
+ GIT_COMMITTER_EMAIL="user@example.com" \
+ git tag -m "tag $when" custom-dates-$i &&
+ i=$(($i+1)) || return 1
+ done
+'
+
+test_expect_success 'sort by date defaults to full timestamp' '
+ cat >expected <<-\EOF &&
+ 945129922 refs/tags/custom-dates-2
+ 1169484241 refs/tags/custom-dates-4
+ 1622806011 refs/tags/custom-dates-3
+ 1707341660 refs/tags/custom-dates-1
+ EOF
+
+ ${git_for_each_ref} \
+ --format="%(creatordate:unix) %(refname)" \
+ --sort=creatordate \
+ "refs/tags/custom-dates-*" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'sort by custom date format' '
+ cat >expected <<-\EOF &&
+ 00:05:22 refs/tags/custom-dates-2
+ 11:26:51 refs/tags/custom-dates-3
+ 16:44:01 refs/tags/custom-dates-4
+ 21:34:20 refs/tags/custom-dates-1
+ EOF
+
+ ${git_for_each_ref} \
+ --format="%(creatordate:format:%H:%M:%S) %(refname)" \
+ --sort="creatordate:format:%H:%M:%S" \
+ "refs/tags/custom-dates-*" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '
+ test_when_finished "git checkout main" &&
+ ${git_for_each_ref} --format="%(HEAD) %(refname:short)" refs/heads/ >actual &&
+ sed -e "s/^\* / /" actual >expect &&
+ git checkout --orphan orphaned-branch &&
+ ${git_for_each_ref} --format="%(HEAD) %(refname:short)" refs/heads/ >actual &&
+ test_cmp expect actual
+'
+
+cat >trailers <<EOF
+Reviewed-by: A U Thor <author@example.com>
+Signed-off-by: A U Thor <author@example.com>
+[ v2 updated patch description ]
+Acked-by: A U Thor
+ <author@example.com>
+EOF
+
+unfold () {
+ perl -0pe 's/\n\s+/ /g'
+}
+
+test_expect_success 'set up trailers for next test' '
+ echo "Some contents" > two &&
+ git add two &&
+ git commit -F - <<-EOF
+ trailers: this commit message has trailers
+
+ Some message contents
+
+ $(cat trailers)
+ EOF
+'
+
+test_trailer_option () {
+ if test "$#" -eq 3
+ then
+ prereq="$1"
+ shift
+ fi &&
+ title=$1 option=$2
+ cat >expect
+ test_expect_success $prereq "$title" '
+ ${git_for_each_ref} --format="%($option)" refs/heads/main >actual &&
+ test_cmp expect actual &&
+ ${git_for_each_ref} --format="%(contents:$option)" refs/heads/main >actual &&
+ test_cmp expect actual
+ '
+}
+
+test_trailer_option PERL_TEST_HELPERS '%(trailers:unfold) unfolds trailers' \
+ 'trailers:unfold' <<-EOF
+ $(unfold <trailers)
+
+ EOF
+
+test_trailer_option '%(trailers:only) shows only "key: value" trailers' \
+ 'trailers:only' <<-EOF
+ $(grep -v patch.description <trailers)
+
+ EOF
+
+test_trailer_option '%(trailers:only=no,only=true) shows only "key: value" trailers' \
+ 'trailers:only=no,only=true' <<-EOF
+ $(grep -v patch.description <trailers)
+
+ EOF
+
+test_trailer_option '%(trailers:only=yes) shows only "key: value" trailers' \
+ 'trailers:only=yes' <<-EOF
+ $(grep -v patch.description <trailers)
+
+ EOF
+
+test_trailer_option '%(trailers:only=no) shows all trailers' \
+ 'trailers:only=no' <<-EOF
+ $(cat trailers)
+
+ EOF
+
+test_trailer_option PERL_TEST_HELPERS '%(trailers:only) and %(trailers:unfold) work together' \
+ 'trailers:only,unfold' <<-EOF
+ $(grep -v patch.description <trailers | unfold)
+
+ EOF
+
+test_trailer_option PERL_TEST_HELPERS '%(trailers:unfold) and %(trailers:only) work together' \
+ 'trailers:unfold,only' <<-EOF
+ $(grep -v patch.description <trailers | unfold)
+
+ EOF
+
+test_trailer_option '%(trailers:key=foo) shows that trailer' \
+ 'trailers:key=Signed-off-by' <<-EOF
+ Signed-off-by: A U Thor <author@example.com>
+
+ EOF
+
+test_trailer_option '%(trailers:key=foo) is case insensitive' \
+ 'trailers:key=SiGned-oFf-bY' <<-EOF
+ Signed-off-by: A U Thor <author@example.com>
+
+ EOF
+
+test_trailer_option '%(trailers:key=foo:) trailing colon also works' \
+ 'trailers:key=Signed-off-by:' <<-EOF
+ Signed-off-by: A U Thor <author@example.com>
+
+ EOF
+
+test_trailer_option '%(trailers:key=foo) multiple keys' \
+ 'trailers:key=Reviewed-by:,key=Signed-off-by' <<-EOF
+ Reviewed-by: A U Thor <author@example.com>
+ Signed-off-by: A U Thor <author@example.com>
+
+ EOF
+
+test_trailer_option '%(trailers:key=nonexistent) becomes empty' \
+ 'trailers:key=Shined-off-by:' <<-EOF
+
+ EOF
+
+test_trailer_option '%(trailers:key=foo) handles multiple lines even if folded' \
+ 'trailers:key=Acked-by' <<-EOF
+ $(grep -v patch.description <trailers | grep -v Signed-off-by | grep -v Reviewed-by)
+
+ EOF
+
+test_trailer_option '%(trailers:key=foo,unfold) properly unfolds' \
+ 'trailers:key=Signed-Off-by,unfold' <<-EOF
+ $(unfold <trailers | grep Signed-off-by)
+
+ EOF
+
+test_trailer_option '%(trailers:key=foo,only=no) also includes nontrailer lines' \
+ 'trailers:key=Signed-off-by,only=no' <<-EOF
+ Signed-off-by: A U Thor <author@example.com>
+ $(grep patch.description <trailers)
+
+ EOF
+
+test_trailer_option '%(trailers:key=foo,valueonly) shows only value' \
+ 'trailers:key=Signed-off-by,valueonly' <<-EOF
+ A U Thor <author@example.com>
+
+ EOF
+
+test_trailer_option '%(trailers:separator) changes separator' \
+ 'trailers:separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF
+ Reviewed-by: A U Thor <author@example.com>,Signed-off-by: A U Thor <author@example.com>
+ EOF
+
+test_trailer_option '%(trailers:key_value_separator) changes key-value separator' \
+ 'trailers:key_value_separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF
+ Reviewed-by,A U Thor <author@example.com>
+ Signed-off-by,A U Thor <author@example.com>
+
+ EOF
+
+test_trailer_option '%(trailers:separator,key_value_separator) changes both separators' \
+ 'trailers:separator=%x2C,key_value_separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF
+ Reviewed-by,A U Thor <author@example.com>,Signed-off-by,A U Thor <author@example.com>
+ EOF
+
+test_expect_success 'multiple %(trailers) use their own options' '
+ git tag -F - tag-with-trailers <<-\EOF &&
+ body
+
+ one: foo
+ one: bar
+ two: baz
+ two: qux
+ EOF
+ t1="%(trailers:key=one,key_value_separator=W,separator=X)" &&
+ t2="%(trailers:key=two,key_value_separator=Y,separator=Z)" &&
+ ${git_for_each_ref} --format="$t1%0a$t2" refs/tags/tag-with-trailers >actual &&
+ cat >expect <<-\EOF &&
+ oneWfooXoneWbar
+ twoYbazZtwoYqux
+ EOF
+ test_cmp expect actual
+'
+
+test_failing_trailer_option () {
+ title=$1 option=$2
+ cat >expect
+ test_expect_success "$title" '
+ # error message cannot be checked under i18n
+ test_must_fail ${git_for_each_ref} --format="%($option)" refs/heads/main 2>actual &&
+ test_cmp expect actual &&
+ test_must_fail ${git_for_each_ref} --format="%(contents:$option)" refs/heads/main 2>actual &&
+ test_cmp expect actual
+ '
+}
+
+test_failing_trailer_option '%(trailers) rejects unknown trailers arguments' \
+ 'trailers:unsupported' <<-\EOF
+ fatal: unknown %(trailers) argument: unsupported
+ EOF
+
+test_failing_trailer_option '%(trailers:key) without value is error' \
+ 'trailers:key' <<-\EOF
+ fatal: expected %(trailers:key=<value>)
+ EOF
+
+test_expect_success 'if arguments, %(contents:trailers) shows error if colon is missing' '
+ cat >expect <<-EOF &&
+ fatal: unrecognized %(contents) argument: trailersonly
+ EOF
+ test_must_fail ${git_for_each_ref} --format="%(contents:trailersonly)" 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'basic atom: head contents:trailers' '
+ ${git_for_each_ref} --format="%(contents:trailers)" refs/heads/main >actual &&
+ sanitize_pgp <actual >actual.clean &&
+ # ${git_for_each_ref} ends with a blank line
+ cat >expect <<-EOF &&
+ $(cat trailers)
+
+ EOF
+ test_cmp expect actual.clean
+'
+
+test_expect_success 'basic atom: rest must fail' '
+ test_must_fail ${git_for_each_ref} --format="%(rest)" refs/heads/main
+'
+
+test_expect_success 'HEAD atom does not take arguments' '
+ test_must_fail ${git_for_each_ref} --format="%(HEAD:foo)" 2>err &&
+ echo "fatal: %(HEAD) does not take arguments" >expect &&
+ test_cmp expect err
+'
+
+test_expect_success 'subject atom rejects unknown arguments' '
+ test_must_fail ${git_for_each_ref} --format="%(subject:foo)" 2>err &&
+ echo "fatal: unrecognized %(subject) argument: foo" >expect &&
+ test_cmp expect err
+'
+
+test_expect_success 'refname atom rejects unknown arguments' '
+ test_must_fail ${git_for_each_ref} --format="%(refname:foo)" 2>err &&
+ echo "fatal: unrecognized %(refname) argument: foo" >expect &&
+ test_cmp expect err
+'
+
+test_expect_success 'trailer parsing not fooled by --- line' '
+ git commit --allow-empty -F - <<-\EOF &&
+ this is the subject
+
+ This is the body. The message has a "---" line which would confuse a
+ message+patch parser. But here we know we have only a commit message,
+ so we get it right.
+
+ trailer: wrong
+ ---
+ This is more body.
+
+ trailer: right
+ EOF
+
+ {
+ echo "trailer: right" &&
+ echo
+ } >expect &&
+ ${git_for_each_ref} --format="%(trailers)" refs/heads/main >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Add symbolic ref for the following tests' '
+ git symbolic-ref refs/heads/sym refs/heads/main
+'
+
+cat >expected <<EOF
+refs/heads/main
+EOF
+
+test_expect_success 'Verify usage of %(symref) atom' '
+ ${git_for_each_ref} --format="%(symref)" refs/heads/sym >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+heads/main
+EOF
+
+test_expect_success 'Verify usage of %(symref:short) atom' '
+ ${git_for_each_ref} --format="%(symref:short)" refs/heads/sym >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+main
+heads/main
+EOF
+
+test_expect_success 'Verify usage of %(symref:lstrip) atom' '
+ ${git_for_each_ref} --format="%(symref:lstrip=2)" refs/heads/sym > actual &&
+ ${git_for_each_ref} --format="%(symref:lstrip=-2)" refs/heads/sym >> actual &&
+ test_cmp expected actual &&
+
+ ${git_for_each_ref} --format="%(symref:strip=2)" refs/heads/sym > actual &&
+ ${git_for_each_ref} --format="%(symref:strip=-2)" refs/heads/sym >> actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+refs
+refs/heads
+EOF
+
+test_expect_success 'Verify usage of %(symref:rstrip) atom' '
+ ${git_for_each_ref} --format="%(symref:rstrip=2)" refs/heads/sym > actual &&
+ ${git_for_each_ref} --format="%(symref:rstrip=-2)" refs/heads/sym >> actual &&
+ test_cmp expected actual
+'
+
+test_expect_success ':remotename and :remoteref' '
+ git init remote-tests &&
+ (
+ cd remote-tests &&
+ test_commit initial &&
+ git branch -M main &&
+ git remote add from fifth.coffee:blub &&
+ git config branch.main.remote from &&
+ git config branch.main.merge refs/heads/stable &&
+ git remote add to southridge.audio:repo &&
+ git config remote.to.push "refs/heads/*:refs/heads/pushed/*" &&
+ git config branch.main.pushRemote to &&
+ for pair in "%(upstream)=refs/remotes/from/stable" \
+ "%(upstream:remotename)=from" \
+ "%(upstream:remoteref)=refs/heads/stable" \
+ "%(push)=refs/remotes/to/pushed/main" \
+ "%(push:remotename)=to" \
+ "%(push:remoteref)=refs/heads/pushed/main"
+ do
+ echo "${pair#*=}" >expect &&
+ ${git_for_each_ref} --format="${pair%=*}" \
+ refs/heads/main >actual &&
+ test_cmp expect actual || exit 1
+ done &&
+ git branch push-simple &&
+ git config branch.push-simple.pushRemote from &&
+ actual="$(${git_for_each_ref} \
+ --format="%(push:remotename),%(push:remoteref)" \
+ refs/heads/push-simple)" &&
+ test from, = "$actual"
+ )
+'
+
+test_expect_success "${git_for_each_ref} --ignore-case ignores case" '
+ ${git_for_each_ref} --format="%(refname)" refs/heads/MAIN >actual &&
+ test_must_be_empty actual &&
+
+ echo refs/heads/main >expect &&
+ ${git_for_each_ref} --format="%(refname)" --ignore-case \
+ refs/heads/MAIN >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success "${git_for_each_ref} --omit-empty works" '
+ ${git_for_each_ref} --format="%(refname)" >actual &&
+ test_line_count -gt 1 actual &&
+ ${git_for_each_ref} --format="%(if:equals=refs/heads/main)%(refname)%(then)%(refname)%(end)" --omit-empty >actual &&
+ echo refs/heads/main >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success "${git_for_each_ref} --ignore-case works on multiple sort keys" '
+ # name refs numerically to avoid case-insensitive filesystem conflicts
+ nr=0 &&
+ for email in a A b B
+ do
+ for subject in a A b B
+ do
+ GIT_COMMITTER_EMAIL="$email@example.com" \
+ git tag -m "tag $subject" icase-$(printf %02d $nr) &&
+ nr=$((nr+1))||
+ return 1
+ done
+ done &&
+ ${git_for_each_ref} --ignore-case \
+ --format="%(taggeremail) %(subject) %(refname)" \
+ --sort=refname \
+ --sort=subject \
+ --sort=taggeremail \
+ refs/tags/icase-* >actual &&
+ cat >expect <<-\EOF &&
+ <a@example.com> tag a refs/tags/icase-00
+ <a@example.com> tag A refs/tags/icase-01
+ <A@example.com> tag a refs/tags/icase-04
+ <A@example.com> tag A refs/tags/icase-05
+ <a@example.com> tag b refs/tags/icase-02
+ <a@example.com> tag B refs/tags/icase-03
+ <A@example.com> tag b refs/tags/icase-06
+ <A@example.com> tag B refs/tags/icase-07
+ <b@example.com> tag a refs/tags/icase-08
+ <b@example.com> tag A refs/tags/icase-09
+ <B@example.com> tag a refs/tags/icase-12
+ <B@example.com> tag A refs/tags/icase-13
+ <b@example.com> tag b refs/tags/icase-10
+ <b@example.com> tag B refs/tags/icase-11
+ <B@example.com> tag b refs/tags/icase-14
+ <B@example.com> tag B refs/tags/icase-15
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "${git_for_each_ref} reports broken tags" '
+ git tag -m "good tag" broken-tag-good HEAD &&
+ git cat-file tag broken-tag-good >good &&
+ sed s/commit/blob/ <good >bad &&
+ bad=$(git hash-object -w -t tag bad) &&
+ git update-ref refs/tags/broken-tag-bad $bad &&
+ test_must_fail ${git_for_each_ref} --format="%(*objectname)" \
+ refs/tags/broken-tag-*
+'
+
+test_expect_success 'set up tag with signature and no blank lines' '
+ git tag -F - fake-sig-no-blanks <<-\EOF
+ this is the subject
+ -----BEGIN PGP SIGNATURE-----
+ not a real signature, but we just care about the
+ subject/body parsing. It is important here that
+ there are no blank lines in the signature.
+ -----END PGP SIGNATURE-----
+ EOF
+'
+
+test_atom refs/tags/fake-sig-no-blanks contents:subject 'this is the subject'
+test_atom refs/tags/fake-sig-no-blanks contents:body ''
+test_atom refs/tags/fake-sig-no-blanks contents:signature "$sig"
+
+test_expect_success 'set up tag with CRLF signature' '
+ append_cr <<-\EOF |
+ this is the subject
+ -----BEGIN PGP SIGNATURE-----
+
+ not a real signature, but we just care about
+ the subject/body parsing. It is important here
+ that there is a blank line separating this
+ from the signature header.
+ -----END PGP SIGNATURE-----
+ EOF
+ git tag -F - --cleanup=verbatim fake-sig-crlf
+'
+
+test_atom refs/tags/fake-sig-crlf contents:subject 'this is the subject'
+test_atom refs/tags/fake-sig-crlf contents:body ''
+
+# CRLF is retained in the signature, so we have to pass our expected value
+# through append_cr. But test_atom requires a shell string, which means command
+# substitution, and the shell will strip trailing newlines from the output of
+# the substitution. Hack around it by adding and then removing a dummy line.
+sig_crlf="$(printf "%s" "$sig" | append_cr; echo dummy)"
+sig_crlf=${sig_crlf%dummy}
+test_atom refs/tags/fake-sig-crlf contents:signature "$sig_crlf"
+
+test_expect_success 'set up tag with signature and trailers' '
+ git tag -F - fake-sig-trailer <<-\EOF
+ this is the subject
+
+ this is the body
+
+ My-Trailer: foo
+ -----BEGIN PGP SIGNATURE-----
+
+ not a real signature, but we just care about the
+ subject/body/trailer parsing.
+ -----END PGP SIGNATURE-----
+ EOF
+'
+
+# use "separator=" here to suppress the terminating newline
+test_atom refs/tags/fake-sig-trailer trailers:separator= 'My-Trailer: foo'
+
+test_expect_success "${git_for_each_ref} --stdin: empty" '
+ >in &&
+ ${git_for_each_ref} --format="%(refname)" --stdin <in >actual &&
+ ${git_for_each_ref} --format="%(refname)" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success "${git_for_each_ref} --stdin: fails if extra args" '
+ >in &&
+ test_must_fail ${git_for_each_ref} --format="%(refname)" \
+ --stdin refs/heads/extra <in 2>err &&
+ grep "unknown arguments supplied with --stdin" err
+'
+
+test_expect_success "${git_for_each_ref} --stdin: matches" '
+ cat >in <<-EOF &&
+ refs/tags/multi*
+ refs/heads/amb*
+ EOF
+
+ cat >expect <<-EOF &&
+ refs/heads/ambiguous
+ refs/tags/multi-ref1-100000-user1
+ refs/tags/multi-ref1-100000-user2
+ refs/tags/multi-ref1-200000-user1
+ refs/tags/multi-ref1-200000-user2
+ refs/tags/multi-ref2-100000-user1
+ refs/tags/multi-ref2-100000-user2
+ refs/tags/multi-ref2-200000-user1
+ refs/tags/multi-ref2-200000-user2
+ refs/tags/multiline
+ EOF
+
+ ${git_for_each_ref} --format="%(refname)" --stdin <in >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success "${git_for_each_ref} with non-existing refs" '
+ cat >in <<-EOF &&
+ refs/heads/this-ref-does-not-exist
+ refs/tags/bogus
+ EOF
+
+ ${git_for_each_ref} --format="%(refname)" --stdin <in >actual &&
+ test_must_be_empty actual &&
+
+ xargs ${git_for_each_ref} --format="%(refname)" <in >actual &&
+ test_must_be_empty actual
+'
+
+test_expect_success "${git_for_each_ref} with nested tags" '
+ git tag -am "Normal tag" nested/base HEAD &&
+ git tag -am "Nested tag" nested/nest1 refs/tags/nested/base &&
+ git tag -am "Double nested tag" nested/nest2 refs/tags/nested/nest1 &&
+
+ head_oid="$(git rev-parse HEAD)" &&
+ base_tag_oid="$(git rev-parse refs/tags/nested/base)" &&
+ nest1_tag_oid="$(git rev-parse refs/tags/nested/nest1)" &&
+ nest2_tag_oid="$(git rev-parse refs/tags/nested/nest2)" &&
+
+ cat >expect <<-EOF &&
+ refs/tags/nested/base $base_tag_oid tag $head_oid commit
+ refs/tags/nested/nest1 $nest1_tag_oid tag $head_oid commit
+ refs/tags/nested/nest2 $nest2_tag_oid tag $head_oid commit
+ EOF
+
+ ${git_for_each_ref} \
+ --format="%(refname) %(objectname) %(objecttype) %(*objectname) %(*objecttype)" \
+ refs/tags/nested/ >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'is-base atom with non-commits' '
+ ${git_for_each_ref} --format="%(is-base:HEAD) %(refname)" >out 2>err &&
+ grep "(HEAD) refs/heads/main" out &&
+
+ test_line_count = 2 err &&
+ grep "error: object .* is a commit, not a blob" err &&
+ grep "error: bad tag pointer to" err
+'
+
+GRADE_FORMAT="%(signature:grade)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)"
+TRUSTLEVEL_FORMAT="%(signature:trustlevel)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)"
+
+test_expect_success GPG 'setup for signature atom using gpg' '
+ git checkout -b signed &&
+
+ test_when_finished "test_unconfig commit.gpgSign" &&
+
+ echo "1" >file &&
+ git add file &&
+ test_tick &&
+ git commit -S -m "file: 1" &&
+ git tag first-signed &&
+
+ echo "2" >file &&
+ test_tick &&
+ git commit -a -m "file: 2" &&
+ git tag second-unsigned &&
+
+ git config commit.gpgSign 1 &&
+ echo "3" >file &&
+ test_tick &&
+ git commit -a --no-gpg-sign -m "file: 3" &&
+ git tag third-unsigned &&
+
+ test_tick &&
+ git rebase -f HEAD^^ && git tag second-signed HEAD^ &&
+ git tag third-signed &&
+
+ echo "4" >file &&
+ test_tick &&
+ git commit -a -SB7227189 -m "file: 4" &&
+ git tag fourth-signed &&
+
+ echo "5" >file &&
+ test_tick &&
+ git commit -a --no-gpg-sign -m "file: 5" &&
+ git tag fifth-unsigned &&
+
+ echo "6" >file &&
+ test_tick &&
+ git commit -a --no-gpg-sign -m "file: 6" &&
+
+ test_tick &&
+ git rebase -f HEAD^^ &&
+ git tag fifth-signed HEAD^ &&
+ git tag sixth-signed &&
+
+ echo "7" >file &&
+ test_tick &&
+ git commit -a --no-gpg-sign -m "file: 7" &&
+ git tag seventh-unsigned
+'
+
+test_expect_success GPGSSH 'setup for signature atom using ssh' '
+ test_when_finished "test_unconfig gpg.format user.signingkey" &&
+
+ test_config gpg.format ssh &&
+ test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+ echo "8" >file &&
+ test_tick &&
+ git add file &&
+ git commit -S -m "file: 8" &&
+ git tag eighth-signed-ssh
+'
+
+test_expect_success GPG2 'bare signature atom' '
+ git verify-commit first-signed 2>expect &&
+ echo >>expect &&
+ ${git_for_each_ref} refs/tags/first-signed \
+ --format="%(signature)" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG 'show good signature with custom format' '
+ git verify-commit first-signed &&
+ cat >expect <<-\EOF &&
+ G
+ 13B6F51ECDDE430D
+ C O Mitter <committer@example.com>
+ 73D758744BE721698EC54E8713B6F51ECDDE430D
+ 73D758744BE721698EC54E8713B6F51ECDDE430D
+ EOF
+ ${git_for_each_ref} refs/tags/first-signed \
+ --format="$GRADE_FORMAT" >actual &&
+ test_cmp expect actual
+'
+test_expect_success GPGSSH 'show good signature with custom format with ssh' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") &&
+ cat >expect.tmpl <<-\EOF &&
+ G
+ FINGERPRINT
+ principal with number 1
+ FINGERPRINT
+
+ EOF
+ sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect &&
+ ${git_for_each_ref} refs/tags/eighth-signed-ssh \
+ --format="$GRADE_FORMAT" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG 'signature atom with grade option and bad signature' '
+ git cat-file commit third-signed >raw &&
+ sed -e "s/^file: 3/file: 3 forged/" raw >forged1 &&
+ FORGED1=$(git hash-object -w -t commit forged1) &&
+ git update-ref refs/tags/third-signed "$FORGED1" &&
+ test_must_fail git verify-commit "$FORGED1" &&
+
+ cat >expect <<-\EOF &&
+ B
+ 13B6F51ECDDE430D
+ C O Mitter <committer@example.com>
+
+
+ EOF
+ ${git_for_each_ref} refs/tags/third-signed \
+ --format="$GRADE_FORMAT" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG 'show untrusted signature with custom format' '
+ cat >expect <<-\EOF &&
+ U
+ 65A0EEA02E30CAD7
+ Eris Discordia <discord@example.net>
+ F8364A59E07FFE9F4D63005A65A0EEA02E30CAD7
+ D4BE22311AD3131E5EDA29A461092E85B7227189
+ EOF
+ ${git_for_each_ref} refs/tags/fourth-signed \
+ --format="$GRADE_FORMAT" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG 'show untrusted signature with undefined trust level' '
+ cat >expect <<-\EOF &&
+ undefined
+ 65A0EEA02E30CAD7
+ Eris Discordia <discord@example.net>
+ F8364A59E07FFE9F4D63005A65A0EEA02E30CAD7
+ D4BE22311AD3131E5EDA29A461092E85B7227189
+ EOF
+ ${git_for_each_ref} refs/tags/fourth-signed \
+ --format="$TRUSTLEVEL_FORMAT" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG 'show untrusted signature with ultimate trust level' '
+ cat >expect <<-\EOF &&
+ ultimate
+ 13B6F51ECDDE430D
+ C O Mitter <committer@example.com>
+ 73D758744BE721698EC54E8713B6F51ECDDE430D
+ 73D758744BE721698EC54E8713B6F51ECDDE430D
+ EOF
+ ${git_for_each_ref} refs/tags/sixth-signed \
+ --format="$TRUSTLEVEL_FORMAT" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG 'show unknown signature with custom format' '
+ cat >expect <<-\EOF &&
+ E
+ 13B6F51ECDDE430D
+
+
+
+ EOF
+ GNUPGHOME="$GNUPGHOME_NOT_USED" ${git_for_each_ref} \
+ refs/tags/sixth-signed --format="$GRADE_FORMAT" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG 'show lack of signature with custom format' '
+ cat >expect <<-\EOF &&
+ N
+
+
+
+
+ EOF
+ ${git_for_each_ref} refs/tags/seventh-unsigned \
+ --format="$GRADE_FORMAT" >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/helper/test-delete-gpgsig.c b/t/helper/test-delete-gpgsig.c
index e36831af03..658c7a37f7 100644
--- a/t/helper/test-delete-gpgsig.c
+++ b/t/helper/test-delete-gpgsig.c
@@ -23,8 +23,7 @@ int cmd__delete_gpgsig(int argc, const char **argv)
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;
+ goto out;
}
bufptr = buf.buf;
@@ -56,7 +55,9 @@ int cmd__delete_gpgsig(int argc, const char **argv)
fwrite(bufptr, 1, (eol - bufptr) + 1, stdout);
bufptr = eol + 1;
}
- fflush(stdout);
+out:
+ fflush(stdout);
+ strbuf_release(&buf);
return 0;
}
diff --git a/t/helper/test-find-pack.c b/t/helper/test-find-pack.c
index 611a13a326..fc4b8a77b3 100644
--- a/t/helper/test-find-pack.c
+++ b/t/helper/test-find-pack.c
@@ -39,11 +39,12 @@ int cmd__find_pack(int argc, const char **argv)
if (repo_get_oid(the_repository, argv[0], &oid))
die("cannot parse %s as an object name", argv[0]);
- for (p = get_all_packs(the_repository); p; p = p->next)
+ repo_for_each_pack(the_repository, p) {
if (find_pack_entry_one(&oid, p)) {
printf("%s\n", p->pack_name);
actual_count++;
}
+ }
if (count > -1 && count != actual_count)
die("bad packfile count %d instead of %d", actual_count, count);
diff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c
index 16a3145c3a..56d223a283 100644
--- a/t/helper/test-hashmap.c
+++ b/t/helper/test-hashmap.c
@@ -154,8 +154,8 @@ int cmd__hashmap(int argc UNUSED, const char **argv UNUSED)
/* break line into command and up to two parameters */
string_list_setlen(&parts, 0);
- string_list_split_in_place(&parts, line.buf, DELIM, 2);
- string_list_remove_empty_items(&parts, 0);
+ string_list_split_in_place_f(&parts, line.buf, DELIM, 2,
+ STRING_LIST_SPLIT_NONEMPTY);
/* ignore empty lines */
if (!parts.nr)
diff --git a/t/helper/test-json-writer.c b/t/helper/test-json-writer.c
index a288069b04..f8316a7d29 100644
--- a/t/helper/test-json-writer.c
+++ b/t/helper/test-json-writer.c
@@ -492,8 +492,8 @@ static int scripted(void)
/* break line into command and zero or more tokens */
string_list_setlen(&parts, 0);
- string_list_split_in_place(&parts, line, " ", -1);
- string_list_remove_empty_items(&parts, 0);
+ string_list_split_in_place_f(&parts, line, " ", -1,
+ STRING_LIST_SPLIT_NONEMPTY);
/* ignore empty lines */
if (!parts.nr || !*parts.items[0].string)
diff --git a/t/helper/test-pack-deltas.c b/t/helper/test-pack-deltas.c
index 4caa024b1e..4981401eaa 100644
--- a/t/helper/test-pack-deltas.c
+++ b/t/helper/test-pack-deltas.c
@@ -51,16 +51,14 @@ static void write_ref_delta(struct hashfile *f,
unsigned long size, base_size, delta_size, compressed_size, hdrlen;
enum object_type type;
void *base_buf, *delta_buf;
- void *buf = repo_read_object_file(the_repository,
- oid, &type,
- &size);
+ void *buf = odb_read_object(the_repository->objects,
+ oid, &type, &size);
if (!buf)
die("unable to read %s", oid_to_hex(oid));
- base_buf = repo_read_object_file(the_repository,
- base, &type,
- &base_size);
+ base_buf = odb_read_object(the_repository->objects,
+ base, &type, &base_size);
if (!base_buf)
die("unable to read %s", oid_to_hex(base));
diff --git a/t/helper/test-pack-mtimes.c b/t/helper/test-pack-mtimes.c
index d51aaa3dc4..7a8ee1de24 100644
--- a/t/helper/test-pack-mtimes.c
+++ b/t/helper/test-pack-mtimes.c
@@ -37,7 +37,7 @@ int cmd__pack_mtimes(int argc, const char **argv)
if (argc != 2)
usage(pack_mtimes_usage);
- for (p = get_all_packs(the_repository); p; p = p->next) {
+ repo_for_each_pack(the_repository, p) {
strbuf_addstr(&buf, basename(p->pack_name));
strbuf_strip_suffix(&buf, ".pack");
strbuf_addstr(&buf, ".mtimes");
diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c
index 086238c826..f5f33751da 100644
--- a/t/helper/test-path-utils.c
+++ b/t/helper/test-path-utils.c
@@ -348,6 +348,7 @@ int cmd__path_utils(int argc, const char **argv)
if (argc == 4 && !strcmp(argv[1], "longest_ancestor_length")) {
int len;
struct string_list ceiling_dirs = STRING_LIST_INIT_DUP;
+ const char path_sep[] = { PATH_SEP, '\0' };
char *path = xstrdup(argv[2]);
/*
@@ -362,7 +363,7 @@ int cmd__path_utils(int argc, const char **argv)
*/
if (normalize_path_copy(path, path))
die("Path \"%s\" could not be normalized", argv[2]);
- string_list_split(&ceiling_dirs, argv[3], PATH_SEP, -1);
+ string_list_split(&ceiling_dirs, argv[3], path_sep, -1);
filter_string_list(&ceiling_dirs, 0,
normalize_ceiling_entry, NULL);
len = longest_ancestor_length(path, &ceiling_dirs);
diff --git a/t/helper/test-read-graph.c b/t/helper/test-read-graph.c
index ef5339bbee..6a5f64e473 100644
--- a/t/helper/test-read-graph.c
+++ b/t/helper/test-read-graph.c
@@ -81,7 +81,7 @@ int cmd__read_graph(int argc, const char **argv)
prepare_repo_settings(the_repository);
- graph = read_commit_graph_one(the_repository, source);
+ graph = read_commit_graph_one(source);
if (!graph) {
ret = 1;
goto done;
diff --git a/t/helper/test-read-midx.c b/t/helper/test-read-midx.c
index da2aa036b5..6de5d1665a 100644
--- a/t/helper/test-read-midx.c
+++ b/t/helper/test-read-midx.c
@@ -11,14 +11,24 @@
#include "gettext.h"
#include "pack-revindex.h"
+static struct multi_pack_index *setup_midx(const char *object_dir)
+{
+ struct odb_source *source;
+ setup_git_directory();
+ source = odb_find_source(the_repository->objects, object_dir);
+ if (!source)
+ source = odb_add_to_alternates_memory(the_repository->objects,
+ object_dir);
+ return load_multi_pack_index(source);
+}
+
static int read_midx_file(const char *object_dir, const char *checksum,
int show_objects)
{
uint32_t i;
struct multi_pack_index *m;
- setup_git_directory();
- m = load_multi_pack_index(the_repository, object_dir, 1);
+ m = setup_midx(object_dir);
if (!m)
return 1;
@@ -56,7 +66,7 @@ static int read_midx_file(const char *object_dir, const char *checksum,
for (i = 0; i < m->num_packs; i++)
printf("%s\n", m->pack_names[i]);
- printf("object-dir: %s\n", m->object_dir);
+ printf("object-dir: %s\n", m->source->path);
if (show_objects) {
struct object_id oid;
@@ -65,7 +75,7 @@ static int read_midx_file(const char *object_dir, const char *checksum,
for (i = 0; i < m->num_objects; i++) {
nth_midxed_object_oid(&oid, m,
i + m->num_objects_in_base);
- fill_midx_entry(the_repository, &oid, &e, m);
+ fill_midx_entry(m, &oid, &e);
printf("%s %"PRIu64"\t%s\n",
oid_to_hex(&oid), e.offset, e.p->pack_name);
@@ -81,8 +91,7 @@ static int read_midx_checksum(const char *object_dir)
{
struct multi_pack_index *m;
- setup_git_directory();
- m = load_multi_pack_index(the_repository, object_dir, 1);
+ m = setup_midx(object_dir);
if (!m)
return 1;
printf("%s\n", hash_to_hex(get_midx_checksum(m)));
@@ -96,9 +105,7 @@ static int read_midx_preferred_pack(const char *object_dir)
struct multi_pack_index *midx = NULL;
uint32_t preferred_pack;
- setup_git_directory();
-
- midx = load_multi_pack_index(the_repository, object_dir, 1);
+ midx = setup_midx(object_dir);
if (!midx)
return 1;
@@ -119,14 +126,12 @@ static int read_midx_bitmapped_packs(const char *object_dir)
struct bitmapped_pack pack;
uint32_t i;
- setup_git_directory();
-
- midx = load_multi_pack_index(the_repository, object_dir, 1);
+ midx = setup_midx(object_dir);
if (!midx)
return 1;
for (i = 0; i < midx->num_packs + midx->num_packs_in_base; i++) {
- if (nth_bitmapped_pack(the_repository, midx, &pack, i) < 0) {
+ if (nth_bitmapped_pack(midx, &pack, i) < 0) {
close_midx(midx);
return 1;
}
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index 8d9a271845..83b06d39a3 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -29,7 +29,7 @@ static unsigned int parse_flags(const char *str, struct flag_definition *defs)
if (!strcmp(str, "0"))
return 0;
- string_list_split(&masks, str, ',', 64);
+ string_list_split(&masks, str, ",", 64);
for (size_t i = 0; i < masks.nr; i++) {
const char *name = masks.items[i].string;
struct flag_definition *def = defs;
@@ -215,7 +215,8 @@ static int cmd_for_each_reflog(struct ref_store *refs,
return refs_for_each_reflog(refs, each_reflog, NULL);
}
-static int each_reflog_ent(struct object_id *old_oid, struct object_id *new_oid,
+static int each_reflog_ent(const char *refname UNUSED,
+ 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)
{
diff --git a/t/lib-gpg.sh b/t/lib-gpg.sh
index 937b876bd0..97268ae07c 100644
--- a/t/lib-gpg.sh
+++ b/t/lib-gpg.sh
@@ -9,6 +9,16 @@
GNUPGHOME="$(pwd)/gpghome"
export GNUPGHOME
+# All the "test_lazy_prereq GPG*" below should use
+# `prepare_gnupghome()` either directly or through a call to
+# `test_have_prereq GPG*`. That's because `gpg` and `gpgsm`
+# only create the directory specified using "$GNUPGHOME" or
+# `--homedir` if it's the default (usually "~/.gnupg").
+prepare_gnupghome() {
+ mkdir -p "$GNUPGHOME" &&
+ chmod 0700 "$GNUPGHOME"
+}
+
test_lazy_prereq GPG '
gpg_version=$(gpg --version 2>&1)
test $? != 127 || exit 1
@@ -38,8 +48,7 @@ test_lazy_prereq GPG '
# To export ownertrust:
# gpg --homedir /tmp/gpghome --export-ownertrust \
# > lib-gpg/ownertrust
- mkdir "$GNUPGHOME" &&
- chmod 0700 "$GNUPGHOME" &&
+ prepare_gnupghome &&
(gpgconf --kill all || : ) &&
gpg --homedir "${GNUPGHOME}" --import \
"$TEST_DIRECTORY"/lib-gpg/keyring.gpg &&
@@ -62,7 +71,16 @@ test_lazy_prereq GPG2 '
exit 1
;;
*)
+ prepare_gnupghome &&
(gpgconf --kill all || : ) &&
+
+ # NEEDSWORK: prepare_gnupghome() should definitely be
+ # called here, but it looks like it exposes a
+ # pre-existing, hidden bug by allowing some tests in
+ # t1016-compatObjectFormat.sh to run instead of being
+ # skipped. See:
+ # https://lore.kernel.org/git/ZoV8b2RvYxLOotSJ@teonanacatl.net/
+
gpg --homedir "${GNUPGHOME}" --import \
"$TEST_DIRECTORY"/lib-gpg/keyring.gpg &&
gpg --homedir "${GNUPGHOME}" --import-ownertrust \
@@ -132,8 +150,7 @@ test_lazy_prereq GPGSSH '
test $? = 0 || exit 1;
# Setup some keys and an allowed signers file
- mkdir -p "${GNUPGHOME}" &&
- chmod 0700 "${GNUPGHOME}" &&
+ prepare_gnupghome &&
(setfacl -k "${GNUPGHOME}" 2>/dev/null || true) &&
ssh-keygen -t ed25519 -N "" -C "git ed25519 key" -f "${GPGSSH_KEY_PRIMARY}" >/dev/null &&
ssh-keygen -t rsa -b 2048 -N "" -C "git rsa2048 key" -f "${GPGSSH_KEY_SECONDARY}" >/dev/null &&
diff --git a/t/meson.build b/t/meson.build
index bbeba1a8d5..a5531df415 100644
--- a/t/meson.build
+++ b/t/meson.build
@@ -1,5 +1,6 @@
clar_test_suites = [
'unit-tests/u-ctype.c',
+ 'unit-tests/u-dir.c',
'unit-tests/u-example-decorate.c',
'unit-tests/u-hash.c',
'unit-tests/u-hashmap.c',
@@ -145,6 +146,7 @@ integration_tests = [
't0611-reftable-httpd.sh',
't0612-reftable-jgit-compatibility.sh',
't0613-reftable-write-options.sh',
+ 't0614-reftable-fsck.sh',
't1000-read-tree-m-3way.sh',
't1001-read-tree-m-2way.sh',
't1002-read-tree-m-u-2way.sh',
@@ -204,10 +206,15 @@ integration_tests = [
't1418-reflog-exists.sh',
't1419-exclude-refs.sh',
't1420-lost-found.sh',
+ 't1421-reflog-write.sh',
+ 't1422-show-ref-exists.sh',
't1430-bad-ref-name.sh',
't1450-fsck.sh',
't1451-fsck-buffer.sh',
't1460-refs-migrate.sh',
+ 't1461-refs-list.sh',
+ 't1462-refs-exists.sh',
+ 't1463-refs-optimize.sh',
't1500-rev-parse.sh',
't1501-work-tree.sh',
't1502-rev-parse-parseopt.sh',
@@ -230,6 +237,8 @@ integration_tests = [
't1700-split-index.sh',
't1701-racy-split-index.sh',
't1800-hook.sh',
+ 't1900-repo.sh',
+ 't1901-repo-structure.sh',
't2000-conflict-when-checking-files-out.sh',
't2002-checkout-cache-u.sh',
't2003-checkout-cache-mkdir.sh',
@@ -486,6 +495,7 @@ integration_tests = [
't4069-remerge-diff.sh',
't4070-diff-pairs.sh',
't4071-diff-minimal.sh',
+ 't4072-diff-max-depth.sh',
't4100-apply-stat.sh',
't4101-apply-nonl.sh',
't4102-apply-rename.sh',
@@ -946,6 +956,7 @@ integration_tests = [
't8012-blame-colors.sh',
't8013-blame-ignore-revs.sh',
't8014-blame-ignore-fuzzy.sh',
+ 't8020-last-modified.sh',
't9001-send-email.sh',
't9002-column.sh',
't9003-help-autocorrect.sh',
@@ -1026,6 +1037,8 @@ integration_tests = [
't9302-fast-import-unpack-limit.sh',
't9303-fast-import-compression.sh',
't9304-fast-import-marks.sh',
+ 't9305-fast-import-signatures.sh',
+ 't9306-fast-import-signed-tags.sh',
't9350-fast-export.sh',
't9351-fast-export-anonymize.sh',
't9400-git-cvsserver-server.sh',
@@ -1139,6 +1152,7 @@ benchmarks = [
'perf/p7820-grep-engines.sh',
'perf/p7821-grep-engines-fixed.sh',
'perf/p7822-grep-perl-character.sh',
+ 'perf/p8020-last-modified.sh',
'perf/p9210-scalar.sh',
'perf/p9300-fast-import-export.sh',
]
@@ -1214,4 +1228,4 @@ if perl.found() and time.found()
timeout: 0,
)
endforeach
-endif \ No newline at end of file
+endif
diff --git a/t/pack-refs-tests.sh b/t/pack-refs-tests.sh
new file mode 100644
index 0000000000..3dbcc01718
--- /dev/null
+++ b/t/pack-refs-tests.sh
@@ -0,0 +1,431 @@
+pack_refs=${pack_refs:-pack-refs}
+
+test_expect_success 'enable reflogs' '
+ git config core.logallrefupdates true
+'
+
+test_expect_success 'prepare a trivial repository' '
+ echo Hello > A &&
+ git update-index --add A &&
+ git commit -m "Initial commit." &&
+ HEAD=$(git rev-parse --verify HEAD)
+'
+
+test_expect_success '${pack_refs} --prune --all' '
+ test_path_is_missing .git/packed-refs &&
+ git ${pack_refs} --no-prune --all &&
+ test_path_is_file .git/packed-refs &&
+ N=$(find .git/refs -type f | wc -l) &&
+ test "$N" != 0 &&
+
+ git ${pack_refs} --prune --all &&
+ test_path_is_file .git/packed-refs &&
+ N=$(find .git/refs -type f) &&
+ test -z "$N"
+'
+
+SHA1=
+
+test_expect_success 'see if git show-ref works as expected' '
+ git branch a &&
+ SHA1=$(cat .git/refs/heads/a) &&
+ echo "$SHA1 refs/heads/a" >expect &&
+ git show-ref a >result &&
+ test_cmp expect result
+'
+
+test_expect_success 'see if a branch still exists when packed' '
+ git branch b &&
+ git ${pack_refs} --all &&
+ rm -f .git/refs/heads/b &&
+ echo "$SHA1 refs/heads/b" >expect &&
+ git show-ref b >result &&
+ test_cmp expect result
+'
+
+test_expect_success 'git branch c/d should barf if branch c exists' '
+ git branch c &&
+ git ${pack_refs} --all &&
+ rm -f .git/refs/heads/c &&
+ test_must_fail git branch c/d
+'
+
+test_expect_success 'see if a branch still exists after git ${pack_refs} --prune' '
+ git branch e &&
+ git ${pack_refs} --all --prune &&
+ echo "$SHA1 refs/heads/e" >expect &&
+ git show-ref e >result &&
+ test_cmp expect result
+'
+
+test_expect_success 'see if git ${pack_refs} --prune remove ref files' '
+ git branch f &&
+ git ${pack_refs} --all --prune &&
+ ! test -f .git/refs/heads/f
+'
+
+test_expect_success 'see if git ${pack_refs} --prune removes empty dirs' '
+ git branch r/s/t &&
+ git ${pack_refs} --all --prune &&
+ ! test -e .git/refs/heads/r
+'
+
+test_expect_success 'git branch g should work when git branch g/h has been deleted' '
+ git branch g/h &&
+ git ${pack_refs} --all --prune &&
+ git branch -d g/h &&
+ git branch g &&
+ git ${pack_refs} --all &&
+ git branch -d g
+'
+
+test_expect_success 'git branch i/j/k should barf if branch i exists' '
+ git branch i &&
+ git ${pack_refs} --all --prune &&
+ test_must_fail git branch i/j/k
+'
+
+test_expect_success 'test git branch k after branch k/l/m and k/lm have been deleted' '
+ git branch k/l &&
+ git branch k/lm &&
+ git branch -d k/l &&
+ git branch k/l/m &&
+ git branch -d k/l/m &&
+ git branch -d k/lm &&
+ git branch k
+'
+
+test_expect_success 'test git branch n after some branch deletion and pruning' '
+ git branch n/o &&
+ git branch n/op &&
+ git branch -d n/o &&
+ git branch n/o/p &&
+ git branch -d n/op &&
+ git ${pack_refs} --all --prune &&
+ git branch -d n/o/p &&
+ git branch n
+'
+
+test_expect_success 'test excluded refs are not packed' '
+ git branch dont_pack1 &&
+ git branch dont_pack2 &&
+ git branch pack_this &&
+ git ${pack_refs} --all --exclude "refs/heads/dont_pack*" &&
+ test -f .git/refs/heads/dont_pack1 &&
+ test -f .git/refs/heads/dont_pack2 &&
+ ! test -f .git/refs/heads/pack_this'
+
+test_expect_success 'test --no-exclude refs clears excluded refs' '
+ git branch dont_pack3 &&
+ git branch dont_pack4 &&
+ git ${pack_refs} --all --exclude "refs/heads/dont_pack*" --no-exclude &&
+ ! test -f .git/refs/heads/dont_pack3 &&
+ ! test -f .git/refs/heads/dont_pack4'
+
+test_expect_success 'test only included refs are packed' '
+ git branch pack_this1 &&
+ git branch pack_this2 &&
+ git tag dont_pack5 &&
+ git ${pack_refs} --include "refs/heads/pack_this*" &&
+ test -f .git/refs/tags/dont_pack5 &&
+ ! test -f .git/refs/heads/pack_this1 &&
+ ! test -f .git/refs/heads/pack_this2'
+
+test_expect_success 'test --no-include refs clears included refs' '
+ git branch pack1 &&
+ git branch pack2 &&
+ git ${pack_refs} --include "refs/heads/pack*" --no-include &&
+ test -f .git/refs/heads/pack1 &&
+ test -f .git/refs/heads/pack2'
+
+test_expect_success 'test --exclude takes precedence over --include' '
+ git branch dont_pack5 &&
+ git ${pack_refs} --include "refs/heads/pack*" --exclude "refs/heads/pack*" &&
+ test -f .git/refs/heads/dont_pack5'
+
+test_expect_success 'see if up-to-date packed refs are preserved' '
+ git branch q &&
+ git ${pack_refs} --all --prune &&
+ git update-ref refs/heads/q refs/heads/q &&
+ ! test -f .git/refs/heads/q
+'
+
+test_expect_success 'pack, prune and repack' '
+ git tag foo &&
+ git ${pack_refs} --all --prune &&
+ git show-ref >all-of-them &&
+ git ${pack_refs} &&
+ git show-ref >again &&
+ test_cmp all-of-them again
+'
+
+test_expect_success 'explicit ${pack_refs} with dangling packed reference' '
+ git commit --allow-empty -m "soon to be garbage-collected" &&
+ git ${pack_refs} --all &&
+ git reset --hard HEAD^ &&
+ git reflog expire --expire=all --all &&
+ git prune --expire=all &&
+ git ${pack_refs} --all 2>result &&
+ test_must_be_empty result
+'
+
+test_expect_success 'delete ref with dangling packed version' '
+ git checkout -b lamb &&
+ git commit --allow-empty -m "future garbage" &&
+ git ${pack_refs} --all &&
+ git reset --hard HEAD^ &&
+ git checkout main &&
+ git reflog expire --expire=all --all &&
+ git prune --expire=all &&
+ git branch -d lamb 2>result &&
+ test_must_be_empty result
+'
+
+test_expect_success 'delete ref while another dangling packed ref' '
+ git branch lamb &&
+ git commit --allow-empty -m "future garbage" &&
+ git ${pack_refs} --all &&
+ git reset --hard HEAD^ &&
+ git reflog expire --expire=all --all &&
+ git prune --expire=all &&
+ git branch -d lamb 2>result &&
+ test_must_be_empty result
+'
+
+test_expect_success 'pack ref directly below refs/' '
+ git update-ref refs/top HEAD &&
+ git ${pack_refs} --all --prune &&
+ grep refs/top .git/packed-refs &&
+ test_path_is_missing .git/refs/top
+'
+
+test_expect_success 'do not pack ref in refs/bisect' '
+ git update-ref refs/bisect/local HEAD &&
+ git ${pack_refs} --all --prune &&
+ ! grep refs/bisect/local .git/packed-refs >/dev/null &&
+ test_path_is_file .git/refs/bisect/local
+'
+
+test_expect_success 'disable reflogs' '
+ git config core.logallrefupdates false &&
+ rm -rf .git/logs
+'
+
+test_expect_success 'create packed foo/bar/baz branch' '
+ git branch foo/bar/baz &&
+ git ${pack_refs} --all --prune &&
+ test_path_is_missing .git/refs/heads/foo/bar/baz &&
+ test_must_fail git reflog exists refs/heads/foo/bar/baz
+'
+
+test_expect_success 'notice d/f conflict with existing directory' '
+ test_must_fail git branch foo &&
+ test_must_fail git branch foo/bar
+'
+
+test_expect_success 'existing directory reports concrete ref' '
+ test_must_fail git branch foo 2>stderr &&
+ test_grep refs/heads/foo/bar/baz stderr
+'
+
+test_expect_success 'notice d/f conflict with existing ref' '
+ test_must_fail git branch foo/bar/baz/extra &&
+ test_must_fail git branch foo/bar/baz/lots/of/extra/components
+'
+
+test_expect_success 'reject packed-refs with unterminated line' '
+ cp .git/packed-refs .git/packed-refs.bak &&
+ test_when_finished "mv .git/packed-refs.bak .git/packed-refs" &&
+ printf "%s" "$HEAD refs/zzzzz" >>.git/packed-refs &&
+ echo "fatal: unterminated line in .git/packed-refs: $HEAD refs/zzzzz" >expected_err &&
+ test_must_fail git for-each-ref >out 2>err &&
+ test_cmp expected_err err
+'
+
+test_expect_success 'reject packed-refs containing junk' '
+ cp .git/packed-refs .git/packed-refs.bak &&
+ test_when_finished "mv .git/packed-refs.bak .git/packed-refs" &&
+ printf "%s\n" "bogus content" >>.git/packed-refs &&
+ echo "fatal: unexpected line in .git/packed-refs: bogus content" >expected_err &&
+ test_must_fail git for-each-ref >out 2>err &&
+ test_cmp expected_err err
+'
+
+test_expect_success 'reject packed-refs with a short SHA-1' '
+ cp .git/packed-refs .git/packed-refs.bak &&
+ test_when_finished "mv .git/packed-refs.bak .git/packed-refs" &&
+ printf "%.7s %s\n" $HEAD refs/zzzzz >>.git/packed-refs &&
+ printf "fatal: unexpected line in .git/packed-refs: %.7s %s\n" $HEAD refs/zzzzz >expected_err &&
+ test_must_fail git for-each-ref >out 2>err &&
+ test_cmp expected_err err
+'
+
+test_expect_success 'timeout if packed-refs.lock exists' '
+ LOCK=.git/packed-refs.lock &&
+ >"$LOCK" &&
+ test_when_finished "rm -f $LOCK" &&
+ test_must_fail git ${pack_refs} --all --prune
+'
+
+test_expect_success 'retry acquiring packed-refs.lock' '
+ LOCK=.git/packed-refs.lock &&
+ >"$LOCK" &&
+ test_when_finished "wait && rm -f $LOCK" &&
+ {
+ ( sleep 1 && rm -f $LOCK ) &
+ } &&
+ git -c core.packedrefstimeout=3000 ${pack_refs} --all --prune
+'
+
+test_expect_success SYMLINKS 'pack symlinked packed-refs' '
+ # First make sure that symlinking works when reading:
+ git update-ref refs/heads/lossy refs/heads/main &&
+ git for-each-ref >all-refs-before &&
+ mv .git/packed-refs .git/my-deviant-packed-refs &&
+ ln -s my-deviant-packed-refs .git/packed-refs &&
+ git for-each-ref >all-refs-linked &&
+ test_cmp all-refs-before all-refs-linked &&
+ git ${pack_refs} --all --prune &&
+ git for-each-ref >all-refs-packed &&
+ test_cmp all-refs-before all-refs-packed &&
+ test -h .git/packed-refs &&
+ test "$(test_readlink .git/packed-refs)" = "my-deviant-packed-refs"
+'
+
+# The 'packed-refs' file is stored directly in .git/. This means it is global
+# to the repository, and can only contain refs that are shared across all
+# worktrees.
+test_expect_success 'refs/worktree must not be packed' '
+ test_commit initial &&
+ test_commit wt1 &&
+ test_commit wt2 &&
+ git worktree add wt1 wt1 &&
+ git worktree add wt2 wt2 &&
+ git checkout initial &&
+ git update-ref refs/worktree/foo HEAD &&
+ git -C wt1 update-ref refs/worktree/foo HEAD &&
+ git -C wt2 update-ref refs/worktree/foo HEAD &&
+ git ${pack_refs} --all &&
+ test_path_is_missing .git/refs/tags/wt1 &&
+ test_path_is_file .git/refs/worktree/foo &&
+ test_path_is_file .git/worktrees/wt1/refs/worktree/foo &&
+ test_path_is_file .git/worktrees/wt2/refs/worktree/foo
+'
+
+# we do not want to count on running ${pack_refs} to
+# actually pack it, as it is perfectly reasonable to
+# skip processing a broken ref
+test_expect_success 'create packed-refs file with broken ref' '
+ test_tick && git commit --allow-empty -m one &&
+ recoverable=$(git rev-parse HEAD) &&
+ test_tick && git commit --allow-empty -m two &&
+ missing=$(git rev-parse HEAD) &&
+ rm -f .git/refs/heads/main &&
+ cat >.git/packed-refs <<-EOF &&
+ $missing refs/heads/main
+ $recoverable refs/heads/other
+ EOF
+ echo $missing >expect &&
+ git rev-parse refs/heads/main >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '${pack_refs} does not silently delete broken packed ref' '
+ git ${pack_refs} --all --prune &&
+ git rev-parse refs/heads/main >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '${pack_refs} does not drop broken refs during deletion' '
+ git update-ref -d refs/heads/other &&
+ git rev-parse refs/heads/main >actual &&
+ test_cmp expect actual
+'
+
+for command in "git ${pack_refs} --all --auto" "git maintenance run --task=${pack_refs} --auto"
+do
+ test_expect_success "$command does not repack below 16 refs without packed-refs" '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ git config set maintenance.auto false &&
+ git commit --allow-empty --message "initial" &&
+
+ # Create 14 additional references, which brings us to
+ # 15 together with the default branch.
+ printf "create refs/heads/loose-%d HEAD\n" $(test_seq 14) >stdin &&
+ git update-ref --stdin <stdin &&
+ test_path_is_missing .git/packed-refs &&
+ git ${pack_refs} --auto --all &&
+ test_path_is_missing .git/packed-refs &&
+
+ # Create the 16th reference, which should cause us to repack.
+ git update-ref refs/heads/loose-15 HEAD &&
+ git ${pack_refs} --auto --all &&
+ test_path_is_file .git/packed-refs
+ )
+ '
+
+ test_expect_success "$command does not repack below 16 refs with small packed-refs" '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ git config set maintenance.auto false &&
+ git commit --allow-empty --message "initial" &&
+
+ git ${pack_refs} --all &&
+ test_line_count = 2 .git/packed-refs &&
+
+ # Create 15 loose references.
+ printf "create refs/heads/loose-%d HEAD\n" $(test_seq 15) >stdin &&
+ git update-ref --stdin <stdin &&
+ git ${pack_refs} --auto --all &&
+ test_line_count = 2 .git/packed-refs &&
+
+ # Create the 16th loose reference, which should cause us to repack.
+ git update-ref refs/heads/loose-17 HEAD &&
+ git ${pack_refs} --auto --all &&
+ test_line_count = 18 .git/packed-refs
+ )
+ '
+
+ test_expect_success "$command scales with size of packed-refs" '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ git config set maintenance.auto false &&
+ git commit --allow-empty --message "initial" &&
+
+ # Create 99 packed refs. This should cause the heuristic
+ # to require more than the minimum amount of loose refs.
+ test_seq 99 |
+ while read i
+ do
+ printf "create refs/heads/packed-%d HEAD\n" $i || return 1
+ done >stdin &&
+ git update-ref --stdin <stdin &&
+ git ${pack_refs} --all &&
+ test_line_count = 101 .git/packed-refs &&
+
+ # Create 24 loose refs, which should not yet cause us to repack.
+ printf "create refs/heads/loose-%d HEAD\n" $(test_seq 24) >stdin &&
+ git update-ref --stdin <stdin &&
+ git ${pack_refs} --auto --all &&
+ test_line_count = 101 .git/packed-refs &&
+
+ # Create another handful of refs to cross the border.
+ # Note that we explicitly do not check for strict
+ # boundaries here, as this also depends on the size of
+ # the object hash.
+ printf "create refs/heads/addn-%d HEAD\n" $(test_seq 10) >stdin &&
+ git update-ref --stdin <stdin &&
+ git ${pack_refs} --auto --all &&
+ test_line_count = 135 .git/packed-refs
+ )
+ '
+done
+
+test_done
diff --git a/t/perf/p6010-merge-base.sh b/t/perf/p6010-merge-base.sh
new file mode 100755
index 0000000000..54f52fa23e
--- /dev/null
+++ b/t/perf/p6010-merge-base.sh
@@ -0,0 +1,101 @@
+#!/bin/sh
+
+test_description='Test git merge-base'
+
+. ./perf-lib.sh
+
+test_perf_fresh_repo
+
+#
+# Creates lots of merges to make history traversal costly. In
+# particular it creates 2^($max_level-1)-1 2-way merges on top of
+# 2^($max_level-1) root commits. E.g., the commit history looks like
+# this for a $max_level of 3:
+#
+# _1_
+# / \
+# 2 3
+# / \ / \
+# 4 5 6 7
+#
+# The numbers are the fast-import marks, which also are the commit
+# messages. 1 is the HEAD commit and a merge, 2 and 3 are also merges,
+# 4-7 are the root commits.
+#
+build_history () {
+ local max_level="$1" &&
+ local level="${2:-1}" &&
+ local mark="${3:-1}" &&
+ if test $level -eq $max_level
+ then
+ echo "reset refs/heads/master" &&
+ echo "from $ZERO_OID" &&
+ echo "commit refs/heads/master" &&
+ echo "mark :$mark" &&
+ echo "committer C <c@example.com> 1234567890 +0000" &&
+ echo "data <<EOF" &&
+ echo "$mark" &&
+ echo "EOF"
+ else
+ local level1=$((level+1)) &&
+ local mark1=$((2*mark)) &&
+ local mark2=$((2*mark+1)) &&
+ build_history $max_level $level1 $mark1 &&
+ build_history $max_level $level1 $mark2 &&
+ echo "commit refs/heads/master" &&
+ echo "mark :$mark" &&
+ echo "committer C <c@example.com> 1234567890 +0000" &&
+ echo "data <<EOF" &&
+ echo "$mark" &&
+ echo "EOF" &&
+ echo "from :$mark1" &&
+ echo "merge :$mark2"
+ fi
+}
+
+#
+# Creates a new merge history in the same shape as build_history does,
+# while reusing the same root commits. This way the two top commits
+# have 2^($max_level-1) merge bases between them.
+#
+build_history2 () {
+ local max_level="$1" &&
+ local level="${2:-1}" &&
+ local mark="${3:-1}" &&
+ if test $level -lt $max_level
+ then
+ local level1=$((level+1)) &&
+ local mark1=$((2*mark)) &&
+ local mark2=$((2*mark+1)) &&
+ build_history2 $max_level $level1 $mark1 &&
+ build_history2 $max_level $level1 $mark2 &&
+ echo "commit refs/heads/master" &&
+ echo "mark :$mark" &&
+ echo "committer C <c@example.com> 1234567890 +0000" &&
+ echo "data <<EOF" &&
+ echo "$mark II" &&
+ echo "EOF" &&
+ echo "from :$mark1" &&
+ echo "merge :$mark2"
+ fi
+}
+
+test_expect_success 'setup' '
+ max_level=15 &&
+ build_history $max_level | git fast-import --export-marks=marks &&
+ git tag one &&
+ build_history2 $max_level | git fast-import --import-marks=marks --force &&
+ git tag two &&
+ git gc &&
+ git log --format=%H --no-merges >expect
+'
+
+test_perf 'git merge-base' '
+ git merge-base --all one two >actual
+'
+
+test_expect_success 'verify result' '
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/perf/p8020-last-modified.sh b/t/perf/p8020-last-modified.sh
new file mode 100755
index 0000000000..cb1f98d3db
--- /dev/null
+++ b/t/perf/p8020-last-modified.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+test_description='last-modified perf tests'
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+test_perf 'top-level last-modified' '
+ git last-modified HEAD
+'
+
+test_perf 'top-level recursive last-modified' '
+ git last-modified -r HEAD
+'
+
+test_perf 'subdir last-modified' '
+ git ls-tree -d HEAD >subtrees &&
+ path="$(head -n 1 subtrees | cut -f2)" &&
+ git last-modified -r HEAD -- "$path"
+'
+
+test_done
diff --git a/t/show-ref-exists-tests.sh b/t/show-ref-exists-tests.sh
new file mode 100644
index 0000000000..36e8e9df33
--- /dev/null
+++ b/t/show-ref-exists-tests.sh
@@ -0,0 +1,77 @@
+git_show_ref_exists=${git_show_ref_exists:-git show-ref --exists}
+
+test_expect_success setup '
+ test_commit --annotate A &&
+ git checkout -b side &&
+ test_commit --annotate B &&
+ git checkout main &&
+ test_commit C &&
+ git branch B A^0
+'
+
+test_expect_success '--exists with existing reference' '
+ ${git_show_ref_exists} refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+'
+
+test_expect_success '--exists with missing reference' '
+ test_expect_code 2 ${git_show_ref_exists} refs/heads/does-not-exist
+'
+
+test_expect_success '--exists does not use DWIM' '
+ test_expect_code 2 ${git_show_ref_exists} $GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME 2>err &&
+ grep "reference does not exist" err
+'
+
+test_expect_success '--exists with HEAD' '
+ ${git_show_ref_exists} HEAD
+'
+
+test_expect_success '--exists with bad reference name' '
+ test_when_finished "git update-ref -d refs/heads/bad...name" &&
+ new_oid=$(git rev-parse HEAD) &&
+ test-tool ref-store main update-ref msg refs/heads/bad...name $new_oid $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+ ${git_show_ref_exists} refs/heads/bad...name
+'
+
+test_expect_success '--exists with arbitrary symref' '
+ test_when_finished "git symbolic-ref -d refs/symref" &&
+ git symbolic-ref refs/symref refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME &&
+ ${git_show_ref_exists} refs/symref
+'
+
+test_expect_success '--exists with dangling symref' '
+ test_when_finished "git symbolic-ref -d refs/heads/dangling" &&
+ git symbolic-ref refs/heads/dangling refs/heads/does-not-exist &&
+ ${git_show_ref_exists} refs/heads/dangling
+'
+
+test_expect_success '--exists with nonexistent object ID' '
+ test-tool ref-store main update-ref msg refs/heads/missing-oid $(test_oid 001) $ZERO_OID REF_SKIP_OID_VERIFICATION &&
+ ${git_show_ref_exists} refs/heads/missing-oid
+'
+
+test_expect_success '--exists with non-commit object' '
+ tree_oid=$(git rev-parse HEAD^{tree}) &&
+ test-tool ref-store main update-ref msg refs/heads/tree ${tree_oid} $ZERO_OID REF_SKIP_OID_VERIFICATION &&
+ ${git_show_ref_exists} refs/heads/tree
+'
+
+test_expect_success '--exists with directory fails with generic error' '
+ cat >expect <<-EOF &&
+ error: reference does not exist
+ EOF
+ test_expect_code 2 ${git_show_ref_exists} refs/heads 2>err &&
+ test_cmp expect err
+'
+
+test_expect_success '--exists with non-existent special ref' '
+ test_expect_code 2 ${git_show_ref_exists} FETCH_HEAD
+'
+
+test_expect_success '--exists with existing special ref' '
+ test_when_finished "rm .git/FETCH_HEAD" &&
+ git rev-parse HEAD >.git/FETCH_HEAD &&
+ ${git_show_ref_exists} FETCH_HEAD
+'
+
+test_done
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index f593c53687..618da080dc 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -883,6 +883,22 @@ test_expect_success 'advice on unconfigured init.defaultBranch disabled' '
test_grep ! "hint: " err
'
+test_expect_success 'default branch name' '
+ if test_have_prereq WITH_BREAKING_CHANGES
+ then
+ expect=main
+ else
+ expect=master
+ fi &&
+ echo "refs/heads/$expect" >expect &&
+ (
+ sane_unset GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME &&
+ git init default-initial-branch-name
+ ) &&
+ git -C default-initial-branch-name symbolic-ref HEAD >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'overridden default main branch name (env)' '
test_config_global init.defaultBranch nmb &&
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=env git init main-branch-env &&
diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh
index 273d71411f..db8bde280e 100755
--- a/t/t0008-ignores.sh
+++ b/t/t0008-ignores.sh
@@ -847,6 +847,17 @@ test_expect_success 'directories and ** matches' '
test_cmp expect actual
'
+test_expect_success '** not confused by matching leading prefix' '
+ cat >.gitignore <<-\EOF &&
+ foo**/bar
+ EOF
+ git check-ignore foobar foo/bar >actual &&
+ cat >expect <<-\EOF &&
+ foo/bar
+ EOF
+ test_cmp expect actual
+'
+
############################################################################
#
# test whitespace handling
diff --git a/t/t0014-alias.sh b/t/t0014-alias.sh
index 854d59ec58..07a53e7366 100755
--- a/t/t0014-alias.sh
+++ b/t/t0014-alias.sh
@@ -27,6 +27,20 @@ test_expect_success 'looping aliases - internal execution' '
test_grep "^fatal: alias loop detected: expansion of" output
'
+test_expect_success 'looping aliases - deprecated builtins' '
+ test_config alias.whatchanged pack-redundant &&
+ test_config alias.pack-redundant whatchanged &&
+ cat >expect <<-EOF &&
+ ${SQ}whatchanged${SQ} is aliased to ${SQ}pack-redundant${SQ}
+ ${SQ}pack-redundant${SQ} is aliased to ${SQ}whatchanged${SQ}
+ fatal: alias loop detected: expansion of ${SQ}whatchanged${SQ} does not terminate:
+ whatchanged <==
+ pack-redundant ==>
+ EOF
+ test_must_fail git whatchanged -h 2>actual &&
+ test_cmp expect actual
+'
+
# This test is disabled until external loops are fixed, because would block
# the test suite for a full minute.
#
@@ -55,4 +69,47 @@ test_expect_success 'tracing a shell alias with arguments shows trace of prepare
test_cmp expect actual
'
+can_alias_deprecated_builtin () {
+ cmd="$1" &&
+ # some git(1) commands will fail for `-h` (the case for
+ # git-status as of 2025-09-07)
+ test_might_fail git status -h >expect &&
+ test_file_not_empty expect &&
+ test_might_fail git -c alias."$cmd"=status "$cmd" -h >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'can alias-shadow deprecated builtins' '
+ for cmd in $(git --list-cmds=deprecated)
+ do
+ can_alias_deprecated_builtin "$cmd" || return 1
+ done
+'
+
+test_expect_success 'can alias-shadow via two deprecated builtins' '
+ # some git(1) commands will fail... (see above)
+ test_might_fail git status -h >expect &&
+ test_file_not_empty expect &&
+ test_might_fail git -c alias.whatchanged=pack-redundant \
+ -c alias.pack-redundant=status whatchanged -h >actual &&
+ test_cmp expect actual
+'
+
+cannot_alias_regular_builtin () {
+ cmd="$1" &&
+ # some git(1) commands will fail... (see above)
+ test_might_fail git "$cmd" -h >expect &&
+ test_file_not_empty expect &&
+ test_might_fail git -c alias."$cmd"=status "$cmd" -h >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'cannot alias-shadow a sample of regular builtins' '
+ for cmd in grep check-ref-format interpret-trailers \
+ checkout-index fast-import diagnose rev-list prune
+ do
+ cannot_alias_regular_builtin "$cmd" || return 1
+ done
+'
+
test_done
diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh
index cb3a85c7ff..07aa834d33 100755
--- a/t/t0300-credentials.sh
+++ b/t/t0300-credentials.sh
@@ -991,18 +991,24 @@ test_expect_success 'url parser not confused by encoded markers' '
test_expect_success 'credential config with partial URLs' '
echo "echo password=yep" | write_script git-credential-yep &&
- test_write_lines url=https://user@example.com/repo.git >stdin &&
+ test_write_lines url=https://user@example.com/org/repo.git >stdin &&
for partial in \
example.com \
+ example.com/org/repo.git \
user@example.com \
+ user@example.com/org/repo.git \
https:// \
https://example.com \
https://example.com/ \
+ https://example.com/org \
+ https://example.com/org/ \
+ https://example.com/org/repo.git \
https://user@example.com \
https://user@example.com/ \
- https://example.com/repo.git \
- https://user@example.com/repo.git \
- /repo.git
+ https://user@example.com/org \
+ https://user@example.com/org/ \
+ https://user@example.com/org/repo.git \
+ /org/repo.git
do
git -c credential.$partial.helper=yep \
credential fill <stdin >stdout &&
@@ -1012,7 +1018,12 @@ test_expect_success 'credential config with partial URLs' '
for partial in \
dont.use.this \
+ example.com/o \
+ user@example.com/o \
http:// \
+ https://example.com/o \
+ https://user@example.com/o \
+ /o \
/repo
do
git -c credential.$partial.helper=yep \
diff --git a/t/t0450-txt-doc-vs-help.sh b/t/t0450-txt-doc-vs-help.sh
index 2f7504ae7e..e12e18f97f 100755
--- a/t/t0450-txt-doc-vs-help.sh
+++ b/t/t0450-txt-doc-vs-help.sh
@@ -41,7 +41,7 @@ help_to_synopsis () {
}
builtin_to_adoc () {
- echo "$GIT_BUILD_DIR/Documentation/git-$1.adoc"
+ echo "$GIT_SOURCE_DIR/Documentation/git-$1.adoc"
}
adoc_to_synopsis () {
@@ -112,10 +112,19 @@ do
adoc="$(builtin_to_adoc "$builtin")" &&
preq="$(echo BUILTIN_ADOC_$builtin | tr '[:lower:]-' '[:upper:]_')" &&
- if test -f "$adoc"
+ # If and only if *.adoc is missing, builtin shall be listed in t0450/adoc-missing.
+ if grep -q "^$builtin$" "$TEST_DIRECTORY"/t0450/adoc-missing
then
+ test_expect_success "$builtin appropriately marked as not having .adoc" '
+ ! test -f "$adoc"
+ '
+ else
test_set_prereq "$preq"
- fi &&
+
+ test_expect_success "$builtin appropriately marked as having .adoc" '
+ test -f "$adoc"
+ '
+ fi
# *.adoc output assertions
test_expect_success "$preq" "$builtin *.adoc SYNOPSIS has dashed labels" '
diff --git a/t/t0450/adoc-help-mismatches b/t/t0450/adoc-help-mismatches
index 06b469bdee..8ee2d3f7c8 100644
--- a/t/t0450/adoc-help-mismatches
+++ b/t/t0450/adoc-help-mismatches
@@ -2,7 +2,6 @@ add
am
apply
archive
-bisect
blame
branch
check-ref-format
@@ -17,7 +16,6 @@ fast-export
fast-import
fetch-pack
fmt-merge-msg
-for-each-ref
format-patch
fsck-objects
fsmonitor--daemon
diff --git a/t/t0450/adoc-missing b/t/t0450/adoc-missing
new file mode 100644
index 0000000000..1ec9f8dcf3
--- /dev/null
+++ b/t/t0450/adoc-missing
@@ -0,0 +1,9 @@
+checkout--worker
+merge-ours
+merge-recursive
+merge-recursive-ours
+merge-recursive-theirs
+merge-subtree
+pickaxe
+submodule--helper
+upload-archive--writer
diff --git a/t/t0600-reffiles-backend.sh b/t/t0600-reffiles-backend.sh
index 1e62c791d9..b11126ed47 100755
--- a/t/t0600-reffiles-backend.sh
+++ b/t/t0600-reffiles-backend.sh
@@ -477,9 +477,29 @@ test_expect_success SYMLINKS 'symref transaction supports symlinks' '
prepare
commit
EOF
- git update-ref --no-deref --stdin <stdin &&
- test_path_is_symlink .git/TEST_SYMREF_HEAD &&
- test "$(test_readlink .git/TEST_SYMREF_HEAD)" = refs/heads/new
+ git update-ref --no-deref --stdin <stdin 2>err &&
+ if test_have_prereq WITH_BREAKING_CHANGES
+ then
+ test_path_is_file .git/TEST_SYMREF_HEAD &&
+ echo "ref: refs/heads/new" >expect &&
+ test_cmp expect .git/TEST_SYMREF_HEAD &&
+ test_must_be_empty err
+ else
+ test_path_is_symlink .git/TEST_SYMREF_HEAD &&
+ test "$(test_readlink .git/TEST_SYMREF_HEAD)" = refs/heads/new &&
+ cat >expect <<-EOF &&
+ warning: ${SQ}core.preferSymlinkRefs=true${SQ} is nominated for removal.
+ hint: The use of symbolic links for symbolic refs is deprecated
+ hint: and will be removed in Git 3.0. The configuration that
+ hint: tells Git to use them is thus going away. You can unset
+ hint: it with:
+ hint:
+ hint: git config unset core.preferSymlinkRefs
+ hint:
+ hint: Git will then use the textual symref format instead.
+ EOF
+ test_cmp expect err
+ fi
'
test_expect_success 'symref transaction supports false symlink config' '
diff --git a/t/t0601-reffiles-pack-refs.sh b/t/t0601-reffiles-pack-refs.sh
index aa7f6ecd81..12cf5d1dcb 100755
--- a/t/t0601-reffiles-pack-refs.sh
+++ b/t/t0601-reffiles-pack-refs.sh
@@ -17,432 +17,4 @@ export GIT_TEST_DEFAULT_REF_FORMAT
. ./test-lib.sh
-test_expect_success 'enable reflogs' '
- git config core.logallrefupdates true
-'
-
-test_expect_success 'prepare a trivial repository' '
- echo Hello > A &&
- git update-index --add A &&
- git commit -m "Initial commit." &&
- HEAD=$(git rev-parse --verify HEAD)
-'
-
-test_expect_success 'pack-refs --prune --all' '
- test_path_is_missing .git/packed-refs &&
- git pack-refs --no-prune --all &&
- test_path_is_file .git/packed-refs &&
- N=$(find .git/refs -type f | wc -l) &&
- test "$N" != 0 &&
-
- git pack-refs --prune --all &&
- test_path_is_file .git/packed-refs &&
- N=$(find .git/refs -type f) &&
- test -z "$N"
-'
-
-SHA1=
-
-test_expect_success 'see if git show-ref works as expected' '
- git branch a &&
- SHA1=$(cat .git/refs/heads/a) &&
- echo "$SHA1 refs/heads/a" >expect &&
- git show-ref a >result &&
- test_cmp expect result
-'
-
-test_expect_success 'see if a branch still exists when packed' '
- git branch b &&
- git pack-refs --all &&
- rm -f .git/refs/heads/b &&
- echo "$SHA1 refs/heads/b" >expect &&
- git show-ref b >result &&
- test_cmp expect result
-'
-
-test_expect_success 'git branch c/d should barf if branch c exists' '
- git branch c &&
- git pack-refs --all &&
- rm -f .git/refs/heads/c &&
- test_must_fail git branch c/d
-'
-
-test_expect_success 'see if a branch still exists after git pack-refs --prune' '
- git branch e &&
- git pack-refs --all --prune &&
- echo "$SHA1 refs/heads/e" >expect &&
- git show-ref e >result &&
- test_cmp expect result
-'
-
-test_expect_success 'see if git pack-refs --prune remove ref files' '
- git branch f &&
- git pack-refs --all --prune &&
- ! test -f .git/refs/heads/f
-'
-
-test_expect_success 'see if git pack-refs --prune removes empty dirs' '
- git branch r/s/t &&
- git pack-refs --all --prune &&
- ! test -e .git/refs/heads/r
-'
-
-test_expect_success 'git branch g should work when git branch g/h has been deleted' '
- git branch g/h &&
- git pack-refs --all --prune &&
- git branch -d g/h &&
- git branch g &&
- git pack-refs --all &&
- git branch -d g
-'
-
-test_expect_success 'git branch i/j/k should barf if branch i exists' '
- git branch i &&
- git pack-refs --all --prune &&
- test_must_fail git branch i/j/k
-'
-
-test_expect_success 'test git branch k after branch k/l/m and k/lm have been deleted' '
- git branch k/l &&
- git branch k/lm &&
- git branch -d k/l &&
- git branch k/l/m &&
- git branch -d k/l/m &&
- git branch -d k/lm &&
- git branch k
-'
-
-test_expect_success 'test git branch n after some branch deletion and pruning' '
- git branch n/o &&
- git branch n/op &&
- git branch -d n/o &&
- git branch n/o/p &&
- git branch -d n/op &&
- git pack-refs --all --prune &&
- git branch -d n/o/p &&
- git branch n
-'
-
-test_expect_success 'test excluded refs are not packed' '
- git branch dont_pack1 &&
- git branch dont_pack2 &&
- git branch pack_this &&
- git pack-refs --all --exclude "refs/heads/dont_pack*" &&
- test -f .git/refs/heads/dont_pack1 &&
- test -f .git/refs/heads/dont_pack2 &&
- ! test -f .git/refs/heads/pack_this'
-
-test_expect_success 'test --no-exclude refs clears excluded refs' '
- git branch dont_pack3 &&
- git branch dont_pack4 &&
- git pack-refs --all --exclude "refs/heads/dont_pack*" --no-exclude &&
- ! test -f .git/refs/heads/dont_pack3 &&
- ! test -f .git/refs/heads/dont_pack4'
-
-test_expect_success 'test only included refs are packed' '
- git branch pack_this1 &&
- git branch pack_this2 &&
- git tag dont_pack5 &&
- git pack-refs --include "refs/heads/pack_this*" &&
- test -f .git/refs/tags/dont_pack5 &&
- ! test -f .git/refs/heads/pack_this1 &&
- ! test -f .git/refs/heads/pack_this2'
-
-test_expect_success 'test --no-include refs clears included refs' '
- git branch pack1 &&
- git branch pack2 &&
- git pack-refs --include "refs/heads/pack*" --no-include &&
- test -f .git/refs/heads/pack1 &&
- test -f .git/refs/heads/pack2'
-
-test_expect_success 'test --exclude takes precedence over --include' '
- git branch dont_pack5 &&
- git pack-refs --include "refs/heads/pack*" --exclude "refs/heads/pack*" &&
- test -f .git/refs/heads/dont_pack5'
-
-test_expect_success 'see if up-to-date packed refs are preserved' '
- git branch q &&
- git pack-refs --all --prune &&
- git update-ref refs/heads/q refs/heads/q &&
- ! test -f .git/refs/heads/q
-'
-
-test_expect_success 'pack, prune and repack' '
- git tag foo &&
- git pack-refs --all --prune &&
- git show-ref >all-of-them &&
- git pack-refs &&
- git show-ref >again &&
- test_cmp all-of-them again
-'
-
-test_expect_success 'explicit pack-refs with dangling packed reference' '
- git commit --allow-empty -m "soon to be garbage-collected" &&
- git pack-refs --all &&
- git reset --hard HEAD^ &&
- git reflog expire --expire=all --all &&
- git prune --expire=all &&
- git pack-refs --all 2>result &&
- test_must_be_empty result
-'
-
-test_expect_success 'delete ref with dangling packed version' '
- git checkout -b lamb &&
- git commit --allow-empty -m "future garbage" &&
- git pack-refs --all &&
- git reset --hard HEAD^ &&
- git checkout main &&
- git reflog expire --expire=all --all &&
- git prune --expire=all &&
- git branch -d lamb 2>result &&
- test_must_be_empty result
-'
-
-test_expect_success 'delete ref while another dangling packed ref' '
- git branch lamb &&
- git commit --allow-empty -m "future garbage" &&
- git pack-refs --all &&
- git reset --hard HEAD^ &&
- git reflog expire --expire=all --all &&
- git prune --expire=all &&
- git branch -d lamb 2>result &&
- test_must_be_empty result
-'
-
-test_expect_success 'pack ref directly below refs/' '
- git update-ref refs/top HEAD &&
- git pack-refs --all --prune &&
- grep refs/top .git/packed-refs &&
- test_path_is_missing .git/refs/top
-'
-
-test_expect_success 'do not pack ref in refs/bisect' '
- git update-ref refs/bisect/local HEAD &&
- git pack-refs --all --prune &&
- ! grep refs/bisect/local .git/packed-refs >/dev/null &&
- test_path_is_file .git/refs/bisect/local
-'
-
-test_expect_success 'disable reflogs' '
- git config core.logallrefupdates false &&
- rm -rf .git/logs
-'
-
-test_expect_success 'create packed foo/bar/baz branch' '
- git branch foo/bar/baz &&
- git pack-refs --all --prune &&
- test_path_is_missing .git/refs/heads/foo/bar/baz &&
- test_must_fail git reflog exists refs/heads/foo/bar/baz
-'
-
-test_expect_success 'notice d/f conflict with existing directory' '
- test_must_fail git branch foo &&
- test_must_fail git branch foo/bar
-'
-
-test_expect_success 'existing directory reports concrete ref' '
- test_must_fail git branch foo 2>stderr &&
- test_grep refs/heads/foo/bar/baz stderr
-'
-
-test_expect_success 'notice d/f conflict with existing ref' '
- test_must_fail git branch foo/bar/baz/extra &&
- test_must_fail git branch foo/bar/baz/lots/of/extra/components
-'
-
-test_expect_success 'reject packed-refs with unterminated line' '
- cp .git/packed-refs .git/packed-refs.bak &&
- test_when_finished "mv .git/packed-refs.bak .git/packed-refs" &&
- printf "%s" "$HEAD refs/zzzzz" >>.git/packed-refs &&
- echo "fatal: unterminated line in .git/packed-refs: $HEAD refs/zzzzz" >expected_err &&
- test_must_fail git for-each-ref >out 2>err &&
- test_cmp expected_err err
-'
-
-test_expect_success 'reject packed-refs containing junk' '
- cp .git/packed-refs .git/packed-refs.bak &&
- test_when_finished "mv .git/packed-refs.bak .git/packed-refs" &&
- printf "%s\n" "bogus content" >>.git/packed-refs &&
- echo "fatal: unexpected line in .git/packed-refs: bogus content" >expected_err &&
- test_must_fail git for-each-ref >out 2>err &&
- test_cmp expected_err err
-'
-
-test_expect_success 'reject packed-refs with a short SHA-1' '
- cp .git/packed-refs .git/packed-refs.bak &&
- test_when_finished "mv .git/packed-refs.bak .git/packed-refs" &&
- printf "%.7s %s\n" $HEAD refs/zzzzz >>.git/packed-refs &&
- printf "fatal: unexpected line in .git/packed-refs: %.7s %s\n" $HEAD refs/zzzzz >expected_err &&
- test_must_fail git for-each-ref >out 2>err &&
- test_cmp expected_err err
-'
-
-test_expect_success 'timeout if packed-refs.lock exists' '
- LOCK=.git/packed-refs.lock &&
- >"$LOCK" &&
- test_when_finished "rm -f $LOCK" &&
- test_must_fail git pack-refs --all --prune
-'
-
-test_expect_success 'retry acquiring packed-refs.lock' '
- LOCK=.git/packed-refs.lock &&
- >"$LOCK" &&
- test_when_finished "wait && rm -f $LOCK" &&
- {
- ( sleep 1 && rm -f $LOCK ) &
- } &&
- git -c core.packedrefstimeout=3000 pack-refs --all --prune
-'
-
-test_expect_success SYMLINKS 'pack symlinked packed-refs' '
- # First make sure that symlinking works when reading:
- git update-ref refs/heads/lossy refs/heads/main &&
- git for-each-ref >all-refs-before &&
- mv .git/packed-refs .git/my-deviant-packed-refs &&
- ln -s my-deviant-packed-refs .git/packed-refs &&
- git for-each-ref >all-refs-linked &&
- test_cmp all-refs-before all-refs-linked &&
- git pack-refs --all --prune &&
- git for-each-ref >all-refs-packed &&
- test_cmp all-refs-before all-refs-packed &&
- test -h .git/packed-refs &&
- test "$(test_readlink .git/packed-refs)" = "my-deviant-packed-refs"
-'
-
-# The 'packed-refs' file is stored directly in .git/. This means it is global
-# to the repository, and can only contain refs that are shared across all
-# worktrees.
-test_expect_success 'refs/worktree must not be packed' '
- test_commit initial &&
- test_commit wt1 &&
- test_commit wt2 &&
- git worktree add wt1 wt1 &&
- git worktree add wt2 wt2 &&
- git checkout initial &&
- git update-ref refs/worktree/foo HEAD &&
- git -C wt1 update-ref refs/worktree/foo HEAD &&
- git -C wt2 update-ref refs/worktree/foo HEAD &&
- git pack-refs --all &&
- test_path_is_missing .git/refs/tags/wt1 &&
- test_path_is_file .git/refs/worktree/foo &&
- test_path_is_file .git/worktrees/wt1/refs/worktree/foo &&
- test_path_is_file .git/worktrees/wt2/refs/worktree/foo
-'
-
-# we do not want to count on running pack-refs to
-# actually pack it, as it is perfectly reasonable to
-# skip processing a broken ref
-test_expect_success 'create packed-refs file with broken ref' '
- test_tick && git commit --allow-empty -m one &&
- recoverable=$(git rev-parse HEAD) &&
- test_tick && git commit --allow-empty -m two &&
- missing=$(git rev-parse HEAD) &&
- rm -f .git/refs/heads/main &&
- cat >.git/packed-refs <<-EOF &&
- $missing refs/heads/main
- $recoverable refs/heads/other
- EOF
- echo $missing >expect &&
- git rev-parse refs/heads/main >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'pack-refs does not silently delete broken packed ref' '
- git pack-refs --all --prune &&
- git rev-parse refs/heads/main >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'pack-refs does not drop broken refs during deletion' '
- git update-ref -d refs/heads/other &&
- git rev-parse refs/heads/main >actual &&
- test_cmp expect actual
-'
-
-for command in "git pack-refs --all --auto" "git maintenance run --task=pack-refs --auto"
-do
- test_expect_success "$command does not repack below 16 refs without packed-refs" '
- test_when_finished "rm -rf repo" &&
- git init repo &&
- (
- cd repo &&
- git config set maintenance.auto false &&
- git commit --allow-empty --message "initial" &&
-
- # Create 14 additional references, which brings us to
- # 15 together with the default branch.
- printf "create refs/heads/loose-%d HEAD\n" $(test_seq 14) >stdin &&
- git update-ref --stdin <stdin &&
- test_path_is_missing .git/packed-refs &&
- git pack-refs --auto --all &&
- test_path_is_missing .git/packed-refs &&
-
- # Create the 16th reference, which should cause us to repack.
- git update-ref refs/heads/loose-15 HEAD &&
- git pack-refs --auto --all &&
- test_path_is_file .git/packed-refs
- )
- '
-
- test_expect_success "$command does not repack below 16 refs with small packed-refs" '
- test_when_finished "rm -rf repo" &&
- git init repo &&
- (
- cd repo &&
- git config set maintenance.auto false &&
- git commit --allow-empty --message "initial" &&
-
- git pack-refs --all &&
- test_line_count = 2 .git/packed-refs &&
-
- # Create 15 loose references.
- printf "create refs/heads/loose-%d HEAD\n" $(test_seq 15) >stdin &&
- git update-ref --stdin <stdin &&
- git pack-refs --auto --all &&
- test_line_count = 2 .git/packed-refs &&
-
- # Create the 16th loose reference, which should cause us to repack.
- git update-ref refs/heads/loose-17 HEAD &&
- git pack-refs --auto --all &&
- test_line_count = 18 .git/packed-refs
- )
- '
-
- test_expect_success "$command scales with size of packed-refs" '
- test_when_finished "rm -rf repo" &&
- git init repo &&
- (
- cd repo &&
- git config set maintenance.auto false &&
- git commit --allow-empty --message "initial" &&
-
- # Create 99 packed refs. This should cause the heuristic
- # to require more than the minimum amount of loose refs.
- test_seq 99 |
- while read i
- do
- printf "create refs/heads/packed-%d HEAD\n" $i || return 1
- done >stdin &&
- git update-ref --stdin <stdin &&
- git pack-refs --all &&
- test_line_count = 101 .git/packed-refs &&
-
- # Create 24 loose refs, which should not yet cause us to repack.
- printf "create refs/heads/loose-%d HEAD\n" $(test_seq 24) >stdin &&
- git update-ref --stdin <stdin &&
- git pack-refs --auto --all &&
- test_line_count = 101 .git/packed-refs &&
-
- # Create another handful of refs to cross the border.
- # Note that we explicitly do not check for strict
- # boundaries here, as this also depends on the size of
- # the object hash.
- printf "create refs/heads/addn-%d HEAD\n" $(test_seq 10) >stdin &&
- git update-ref --stdin <stdin &&
- git pack-refs --auto --all &&
- test_line_count = 135 .git/packed-refs
- )
- '
-done
-
-test_done
+. "$TEST_DIRECTORY"/pack-refs-tests.sh
diff --git a/t/t0613-reftable-write-options.sh b/t/t0613-reftable-write-options.sh
index d77e601111..e334751759 100755
--- a/t/t0613-reftable-write-options.sh
+++ b/t/t0613-reftable-write-options.sh
@@ -11,16 +11,18 @@ export GIT_TEST_REFTABLE_AUTOCOMPACTION
# Block sizes depend on the hash function, so we force SHA1 here.
GIT_TEST_DEFAULT_HASH=sha1
export GIT_TEST_DEFAULT_HASH
-# Block sizes also depend on the actual refs we write, so we force "master" to
-# be the default initial branch name.
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
+# Block sizes depend on the actual refs we write, so, for tests
+# that check block size, we force the initial branch name to be "master".
+init_repo () {
+ git init --initial-branch master repo
+}
+
test_expect_success 'default write options' '
test_when_finished "rm -rf repo" &&
- git init repo &&
+ init_repo &&
(
cd repo &&
test_commit initial &&
@@ -43,7 +45,7 @@ test_expect_success 'default write options' '
test_expect_success 'disabled reflog writes no log blocks' '
test_config_global core.logAllRefUpdates false &&
test_when_finished "rm -rf repo" &&
- git init repo &&
+ init_repo &&
(
cd repo &&
test_commit initial &&
@@ -62,7 +64,7 @@ test_expect_success 'disabled reflog writes no log blocks' '
test_expect_success 'many refs results in multiple blocks' '
test_when_finished "rm -rf repo" &&
- git init repo &&
+ init_repo &&
(
cd repo &&
test_commit initial &&
@@ -115,7 +117,7 @@ test_expect_success 'tiny block size leads to error' '
test_expect_success 'small block size leads to multiple ref blocks' '
test_config_global core.logAllRefUpdates false &&
test_when_finished "rm -rf repo" &&
- git init repo &&
+ init_repo &&
(
cd repo &&
test_commit A &&
@@ -172,7 +174,7 @@ test_expect_success 'block size exceeding maximum supported size' '
test_expect_success 'restart interval at every single record' '
test_when_finished "rm -rf repo" &&
- git init repo &&
+ init_repo &&
(
cd repo &&
test_commit initial &&
@@ -212,7 +214,7 @@ test_expect_success 'restart interval exceeding maximum supported interval' '
test_expect_success 'object index gets written by default with ref index' '
test_config_global core.logAllRefUpdates false &&
test_when_finished "rm -rf repo" &&
- git init repo &&
+ init_repo &&
(
cd repo &&
test_commit initial &&
@@ -247,7 +249,7 @@ test_expect_success 'object index gets written by default with ref index' '
test_expect_success 'object index can be disabled' '
test_config_global core.logAllRefUpdates false &&
test_when_finished "rm -rf repo" &&
- git init repo &&
+ init_repo &&
(
cd repo &&
test_commit initial &&
diff --git a/t/t0614-reftable-fsck.sh b/t/t0614-reftable-fsck.sh
new file mode 100755
index 0000000000..85cc47d67e
--- /dev/null
+++ b/t/t0614-reftable-fsck.sh
@@ -0,0 +1,58 @@
+#!/bin/sh
+
+test_description='Test reftable backend consistency check'
+
+GIT_TEST_DEFAULT_REF_FORMAT=reftable
+export GIT_TEST_DEFAULT_REF_FORMAT
+
+. ./test-lib.sh
+
+test_expect_success "no errors reported on a well formed repository" '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ git commit --allow-empty -m initial &&
+
+ for i in $(test_seq 20)
+ do
+ git update-ref refs/heads/branch-$i HEAD || return 1
+ done &&
+
+ # The repository should end up with multiple tables.
+ test_line_count ">" 1 .git/reftable/tables.list &&
+
+ git refs verify 2>err &&
+ test_must_be_empty err
+ )
+'
+
+for TABLE_NAME in "foo-bar-e4d12d59.ref" \
+ "0x00000000zzzz-0x00000000zzzz-e4d12d59.ref" \
+ "0x000000000001-0x000000000002-e4d12d59.abc" \
+ "0x000000000001-0x000000000002-e4d12d59.refabc"; do
+ test_expect_success "table name $TABLE_NAME should be checked" '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ git commit --allow-empty -m initial &&
+
+ git refs verify 2>err &&
+ test_must_be_empty err &&
+
+ EXISTING_TABLE=$(head -n1 .git/reftable/tables.list) &&
+ mv ".git/reftable/$EXISTING_TABLE" ".git/reftable/$TABLE_NAME" &&
+ sed "s/${EXISTING_TABLE}/${TABLE_NAME}/g" .git/reftable/tables.list > tables.list &&
+ mv tables.list .git/reftable/tables.list &&
+
+ git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: ${TABLE_NAME}: badReftableTableName: invalid reftable table name
+ EOF
+ test_cmp expect err
+ )
+ '
+done
+
+test_done
diff --git a/t/t1010-mktree.sh b/t/t1010-mktree.sh
index e9973f7494..312fe6717a 100755
--- a/t/t1010-mktree.sh
+++ b/t/t1010-mktree.sh
@@ -11,10 +11,13 @@ test_expect_success setup '
git add "$d" || return 1
done &&
echo zero >one &&
- git update-index --add --info-only one &&
- git write-tree --missing-ok >tree.missing &&
- git ls-tree $(cat tree.missing) >top.missing &&
- git ls-tree -r $(cat tree.missing) >all.missing &&
+ if test_have_prereq BROKEN_OBJECTS
+ then
+ git update-index --add --info-only one &&
+ git write-tree --missing-ok >tree.missing &&
+ git ls-tree $(cat tree.missing) >top.missing &&
+ git ls-tree -r $(cat tree.missing) >all.missing
+ fi &&
echo one >one &&
git add one &&
git write-tree >tree &&
@@ -53,7 +56,7 @@ test_expect_success 'ls-tree output in wrong order given to mktree (2)' '
test_cmp tree.withsub actual
'
-test_expect_success 'allow missing object with --missing' '
+test_expect_success BROKEN_OBJECTS 'allow missing object with --missing' '
git mktree --missing <top.missing >actual &&
test_cmp tree.missing actual
'
diff --git a/t/t1016-compatObjectFormat.sh b/t/t1016-compatObjectFormat.sh
index e88362fbe4..0efce53f3a 100755
--- a/t/t1016-compatObjectFormat.sh
+++ b/t/t1016-compatObjectFormat.sh
@@ -21,6 +21,12 @@ test_description='Test how well compatObjectFormat works'
# 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.
+#
+# Similarly this test relies on:
+# gpg --faked-system-time '20230918T154812!
+# freezing the system time from gpg perspective so that two different
+# runs of gpg applied to the same data result in identical signatures.
+#
compat_hash () {
case "$1" in
@@ -114,7 +120,7 @@ do
git config core.repositoryformatversion 1 &&
git config extensions.objectformat $hash &&
git config extensions.compatobjectformat $(compat_hash $hash) &&
- test_config gpg.program $TEST_DIRECTORY/t1016/gpg &&
+ git config gpg.program $TEST_DIRECTORY/t1016/gpg &&
echo "Hello World!" >hello &&
eval hello_${hash}_oid=$(git hash-object hello) &&
git update-index --add hello &&
diff --git a/t/t1016/gpg b/t/t1016/gpg
index 2601cb18a5..34d6e055fc 100755
--- a/t/t1016/gpg
+++ b/t/t1016/gpg
@@ -1,2 +1,2 @@
#!/bin/sh
-exec gpg --faked-system-time "20230918T154812" "$@"
+exec gpg --faked-system-time '20230918T154812!' "$@"
diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh
index ab3a105fff..b2da4feaef 100755
--- a/t/t1091-sparse-checkout-builtin.sh
+++ b/t/t1091-sparse-checkout-builtin.sh
@@ -1050,5 +1050,180 @@ test_expect_success 'check-rules null termination' '
test_cmp expect actual
'
+test_expect_success 'clean' '
+ git -C repo sparse-checkout set --cone deep/deeper1 &&
+ git -C repo sparse-checkout reapply &&
+ mkdir -p repo/deep/deeper2 repo/folder1/extra/inside &&
+
+ # Add untracked files
+ touch repo/deep/deeper2/file &&
+ touch repo/folder1/extra/inside/file &&
+
+ test_must_fail git -C repo sparse-checkout clean 2>err &&
+ grep "refusing to clean" err &&
+
+ git -C repo config clean.requireForce true &&
+ test_must_fail git -C repo sparse-checkout clean 2>err &&
+ grep "refusing to clean" err &&
+
+ cat >expect <<-\EOF &&
+ Would remove deep/deeper2/
+ Would remove folder1/
+ EOF
+
+ git -C repo sparse-checkout clean --dry-run >out &&
+ test_cmp expect out &&
+ test_path_exists repo/deep/deeper2 &&
+ test_path_exists repo/folder1/extra/inside/file &&
+
+ cat >expect <<-\EOF &&
+ Would remove deep/deeper2/file
+ Would remove folder1/extra/inside/file
+ EOF
+
+ git -C repo sparse-checkout clean --dry-run --verbose >out &&
+ test_cmp expect out &&
+
+ cat >expect <<-\EOF &&
+ Removing deep/deeper2/
+ Removing folder1/
+ EOF
+
+ git -C repo sparse-checkout clean -f >out &&
+ test_cmp expect out &&
+
+ test_path_is_missing repo/deep/deeper2 &&
+ test_path_is_missing repo/folder1
+'
+
+test_expect_success 'clean with sparse file states' '
+ test_when_finished git reset --hard &&
+ git -C repo sparse-checkout set --cone deep/deeper1 &&
+ mkdir repo/folder2 &&
+
+ # The previous test case checked the -f option, so
+ # test the config option in this one.
+ git -C repo config clean.requireForce false &&
+
+ # create an untracked file and a modified file
+ touch repo/folder2/file &&
+ echo dirty >repo/folder2/a &&
+
+ # First clean/reapply pass will do nothing.
+ git -C repo sparse-checkout clean >out &&
+ test_must_be_empty out &&
+ test_path_exists repo/folder2/a &&
+ test_path_exists repo/folder2/file &&
+
+ git -C repo sparse-checkout reapply 2>err &&
+ test_grep folder2 err &&
+ test_path_exists repo/folder2/a &&
+ test_path_exists repo/folder2/file &&
+
+ # Now, stage the change to the tracked file.
+ git -C repo add --sparse folder2/a &&
+
+ # Clean will continue not doing anything.
+ git -C repo sparse-checkout clean >out &&
+ test_line_count = 0 out &&
+ test_path_exists repo/folder2/a &&
+ test_path_exists repo/folder2/file &&
+
+ # But we can reapply to remove the staged change.
+ git -C repo sparse-checkout reapply 2>err &&
+ test_grep folder2 err &&
+ test_path_is_missing repo/folder2/a &&
+ test_path_exists repo/folder2/file &&
+
+ # We can clean now.
+ cat >expect <<-\EOF &&
+ Removing folder2/
+ EOF
+ git -C repo sparse-checkout clean >out &&
+ test_cmp expect out &&
+ test_path_is_missing repo/folder2 &&
+
+ # At the moment, the file is staged.
+ cat >expect <<-\EOF &&
+ M folder2/a
+ EOF
+
+ git -C repo status -s >out &&
+ test_cmp expect out &&
+
+ # Reapply persists the modified state.
+ git -C repo sparse-checkout reapply &&
+ cat >expect <<-\EOF &&
+ M folder2/a
+ EOF
+ git -C repo status -s >out &&
+ test_cmp expect out &&
+
+ # Committing the change leads to resolved status.
+ git -C repo commit -m "modified" &&
+ git -C repo status -s >out &&
+ test_must_be_empty out &&
+
+ # Repeat, but this time commit before reapplying.
+ mkdir repo/folder2/ &&
+ echo dirtier >repo/folder2/a &&
+ git -C repo add --sparse folder2/a &&
+ git -C repo sparse-checkout clean >out &&
+ test_must_be_empty out &&
+ test_path_exists repo/folder2/a &&
+
+ # Committing without reapplying makes it look like a deletion
+ # due to no skip-worktree bit.
+ git -C repo commit -m "dirtier" &&
+ git -C repo status -s >out &&
+ test_must_be_empty out &&
+
+ git -C repo sparse-checkout reapply &&
+ git -C repo status -s >out &&
+ test_must_be_empty out
+'
+
+test_expect_success 'sparse-checkout operations with merge conflicts' '
+ git clone repo merge &&
+
+ (
+ cd merge &&
+ mkdir -p folder1/even/more/dirs &&
+ echo base >folder1/even/more/dirs/file &&
+ git add folder1 &&
+ git commit -m "base" &&
+
+ git checkout -b right&&
+ echo right >folder1/even/more/dirs/file &&
+ git commit -a -m "right" &&
+
+ git checkout -b left HEAD~1 &&
+ echo left >folder1/even/more/dirs/file &&
+ git commit -a -m "left" &&
+
+ git checkout -b merge &&
+ git sparse-checkout set deep/deeper1 &&
+
+ test_must_fail git merge -m "will-conflict" right &&
+
+ test_must_fail git sparse-checkout clean -f 2>err &&
+ grep "failed to convert index to a sparse index" err &&
+
+ echo merged >folder1/even/more/dirs/file &&
+ git add --sparse folder1 &&
+ git merge --continue &&
+
+ test_path_exists folder1/even/more/dirs/file &&
+
+ # clean does not remove the file, because the
+ # SKIP_WORKTREE bit was not cleared by the merge command.
+ git sparse-checkout clean -f >out &&
+ test_line_count = 0 out &&
+ test_path_exists folder1/even/more/dirs/file &&
+
+ git sparse-checkout reapply &&
+ test_path_is_missing folder1
+ )
+'
test_done
diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh
index d8101139b4..b0f691c151 100755
--- a/t/t1092-sparse-checkout-compatibility.sh
+++ b/t/t1092-sparse-checkout-compatibility.sh
@@ -1506,6 +1506,8 @@ test_expect_success 'sparse-index is not expanded' '
ensure_not_expanded reset --hard &&
ensure_not_expanded restore -s rename-out-to-out -- deep/deeper1 &&
+ ensure_not_expanded ls-files deep/deeper1 &&
+
echo >>sparse-index/README.md &&
ensure_not_expanded add -A &&
echo >>sparse-index/extra.txt &&
@@ -1607,6 +1609,17 @@ test_expect_success 'describe tested on all' '
test_all_match git describe --dirty
'
+test_expect_success 'ls-files filtering and expansion' '
+ init_repos &&
+
+ # This filtering will hit a sparse directory midway
+ # through the iteration.
+ test_all_match git ls-files deep &&
+
+ # This pathspec will filter the index to only a sparse
+ # directory.
+ test_all_match git ls-files folder1
+'
test_expect_success 'sparse-index is not expanded: describe' '
init_repos &&
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index f856821839..358d636379 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -9,6 +9,7 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
for mode in legacy subcommands
do
@@ -134,38 +135,39 @@ test_expect_success 'clear default config' '
rm -f .git/config
'
-cat > expect << EOF
+test_expect_success 'initial' '
+ cat >expect <<\EOF &&
[section]
penguin = little blue
EOF
-test_expect_success 'initial' '
git config ${mode_set} section.penguin "little blue" &&
test_cmp expect .git/config
'
-cat > expect << EOF
+test_expect_success 'mixed case' '
+ cat >expect <<\EOF &&
[section]
penguin = little blue
Movie = BadPhysics
EOF
-test_expect_success 'mixed case' '
git config ${mode_set} Section.Movie BadPhysics &&
test_cmp expect .git/config
'
-cat > expect << EOF
+test_expect_success 'similar section' '
+ cat >expect <<\EOF &&
[section]
penguin = little blue
Movie = BadPhysics
[Sections]
WhatEver = Second
EOF
-test_expect_success 'similar section' '
git config ${mode_set} Sections.WhatEver Second &&
test_cmp expect .git/config
'
-cat > expect << EOF
+test_expect_success 'uppercase section' '
+ cat >expect <<\EOF &&
[section]
penguin = little blue
Movie = BadPhysics
@@ -173,7 +175,6 @@ cat > expect << EOF
[Sections]
WhatEver = Second
EOF
-test_expect_success 'uppercase section' '
git config ${mode_set} SECTION.UPPERCASE true &&
test_cmp expect .git/config
'
@@ -186,7 +187,8 @@ test_expect_success 'replace with non-match (actually matching)' '
git config section.penguin "very blue" !kingpin
'
-cat > expect << EOF
+test_expect_success 'append comments' '
+ cat >expect <<\EOF &&
[section]
Movie = BadPhysics
UPPERCASE = true
@@ -198,8 +200,6 @@ cat > expect << EOF
[Sections]
WhatEver = Second
EOF
-
-test_expect_success 'append comments' '
git config --replace-all --comment="Pygoscelis papua" section.penguin gentoo &&
git config ${mode_set} --comment="find fish" section.disposition peckish &&
git config ${mode_set} --comment="#abc" section.foo bar &&
@@ -214,7 +214,9 @@ test_expect_success 'Prohibited LF in comment' '
test_must_fail git config ${mode_set} --comment="a${LF}b" section.k v
'
-test_expect_success 'non-match result' 'test_cmp expect .git/config'
+test_expect_success 'non-match result' '
+ test_cmp expect .git/config
+'
test_expect_success 'find mixed-case key by canonical name' '
test_cmp_config Second sections.whatever
@@ -265,14 +267,15 @@ test_expect_success 'unset with cont. lines' '
git config ${mode_unset} beta.baz
'
-cat > expect <<\EOF
-[alpha]
-bar = foo
-[beta]
-foo = bar
-EOF
-
-test_expect_success 'unset with cont. lines is correct' 'test_cmp expect .git/config'
+test_expect_success 'unset with cont. lines is correct' '
+ cat >expect <<-\EOF &&
+ [alpha]
+ bar = foo
+ [beta]
+ foo = bar
+ EOF
+ test_cmp expect .git/config
+'
cat > .git/config << EOF
[beta] ; silly comment # another comment
@@ -292,16 +295,15 @@ test_expect_success 'multiple unset' '
git config ${mode_unset_all} beta.haha
'
-cat > expect << EOF
+test_expect_success 'multiple unset is correct' '
+ cat >expect <<EOF &&
[beta] ; silly comment # another comment
-noIndent= sillyValue ; 'nother silly comment
+noIndent= sillyValue ; ${SQ}nother silly comment
# empty line
; comment
[nextSection] noNewline = ouch
EOF
-
-test_expect_success 'multiple unset is correct' '
test_cmp expect .git/config
'
@@ -318,37 +320,37 @@ test_expect_success '--replace-all' '
git config ${mode_replace_all} beta.haha gamma
'
-cat > expect << EOF
+test_expect_success 'all replaced' '
+ cat >expect <<EOF &&
[beta] ; silly comment # another comment
-noIndent= sillyValue ; 'nother silly comment
+noIndent= sillyValue ; ${SQ}nother silly comment
# empty line
; comment
haha = gamma
[nextSection] noNewline = ouch
EOF
-
-test_expect_success 'all replaced' '
test_cmp expect .git/config
'
-cat > expect << EOF
+test_expect_success 'really mean test' '
+ cat >expect <<EOF &&
[beta] ; silly comment # another comment
-noIndent= sillyValue ; 'nother silly comment
+noIndent= sillyValue ; ${SQ}nother silly comment
# empty line
; comment
haha = alpha
[nextSection] noNewline = ouch
EOF
-test_expect_success 'really mean test' '
git config ${mode_set} beta.haha alpha &&
test_cmp expect .git/config
'
-cat > expect << EOF
+test_expect_success 'really really mean test' '
+ cat >expect <<EOF &&
[beta] ; silly comment # another comment
-noIndent= sillyValue ; 'nother silly comment
+noIndent= sillyValue ; ${SQ}nother silly comment
# empty line
; comment
@@ -356,7 +358,6 @@ noIndent= sillyValue ; 'nother silly comment
[nextSection]
nonewline = wow
EOF
-test_expect_success 'really really mean test' '
git config ${mode_set} nextsection.nonewline wow &&
test_cmp expect .git/config
'
@@ -365,23 +366,24 @@ test_expect_success 'get value' '
test_cmp_config alpha beta.haha
'
-cat > expect << EOF
+test_expect_success 'unset' '
+ cat >expect <<EOF &&
[beta] ; silly comment # another comment
-noIndent= sillyValue ; 'nother silly comment
+noIndent= sillyValue ; ${SQ}nother silly comment
# empty line
; comment
[nextSection]
nonewline = wow
EOF
-test_expect_success 'unset' '
git config ${mode_unset} beta.haha &&
test_cmp expect .git/config
'
-cat > expect << EOF
+test_expect_success 'multivar' '
+ cat >expect <<EOF &&
[beta] ; silly comment # another comment
-noIndent= sillyValue ; 'nother silly comment
+noIndent= sillyValue ; ${SQ}nother silly comment
# empty line
; comment
@@ -389,7 +391,6 @@ noIndent= sillyValue ; 'nother silly comment
nonewline = wow
NoNewLine = wow2 for me
EOF
-test_expect_success 'multivar' '
git config nextsection.NoNewLine "wow2 for me" "for me$" &&
test_cmp expect .git/config
'
@@ -415,9 +416,10 @@ test_expect_success 'multi-valued get-all returns all' '
test_cmp expect actual
'
-cat > expect << EOF
+test_expect_success 'multivar replace' '
+ cat >expect <<EOF &&
[beta] ; silly comment # another comment
-noIndent= sillyValue ; 'nother silly comment
+noIndent= sillyValue ; ${SQ}nother silly comment
# empty line
; comment
@@ -425,7 +427,6 @@ noIndent= sillyValue ; 'nother silly comment
nonewline = wow3
NoNewLine = wow2 for me
EOF
-test_expect_success 'multivar replace' '
git config nextsection.nonewline "wow3" "wow$" &&
test_cmp expect .git/config
'
@@ -438,17 +439,16 @@ test_expect_success 'invalid unset' '
test_must_fail git config ${mode_unset} somesection.nonewline
'
-cat > expect << EOF
+test_expect_success 'multivar unset' '
+ cat >expect <<EOF &&
[beta] ; silly comment # another comment
-noIndent= sillyValue ; 'nother silly comment
+noIndent= sillyValue ; ${SQ}nother silly comment
# empty line
; comment
[nextSection]
NoNewLine = wow2 for me
EOF
-
-test_expect_success 'multivar unset' '
case "$mode" in
legacy)
git config --unset nextsection.nonewline "wow3$";;
@@ -458,17 +458,22 @@ test_expect_success 'multivar unset' '
test_cmp expect .git/config
'
-test_expect_success 'invalid key' 'test_must_fail git config inval.2key blabla'
+test_expect_success 'invalid key' '
+ test_must_fail git config inval.2key blabla
+'
-test_expect_success 'correct key' 'git config 123456.a123 987'
+test_expect_success 'correct key' '
+ git config 123456.a123 987
+'
test_expect_success 'hierarchical section' '
git config Version.1.2.3eX.Alpha beta
'
-cat > expect << EOF
+test_expect_success 'hierarchical section value' '
+ cat >expect <<EOF &&
[beta] ; silly comment # another comment
-noIndent= sillyValue ; 'nother silly comment
+noIndent= sillyValue ; ${SQ}nother silly comment
# empty line
; comment
@@ -479,65 +484,59 @@ noIndent= sillyValue ; 'nother silly comment
[Version "1.2.3eX"]
Alpha = beta
EOF
-
-test_expect_success 'hierarchical section value' '
test_cmp expect .git/config
'
-cat > expect << EOF
-beta.noindent=sillyValue
-nextsection.nonewline=wow2 for me
-123456.a123=987
-version.1.2.3eX.alpha=beta
-EOF
-
test_expect_success 'working --list' '
+ cat >expect <<-\EOF &&
+ beta.noindent=sillyValue
+ nextsection.nonewline=wow2 for me
+ 123456.a123=987
+ version.1.2.3eX.alpha=beta
+ EOF
git config ${mode_prefix}list > output &&
test_cmp expect output
'
+
test_expect_success '--list without repo produces empty output' '
git --git-dir=nonexistent config ${mode_prefix}list >output &&
test_must_be_empty output
'
-cat > expect << EOF
-beta.noindent
-nextsection.nonewline
-123456.a123
-version.1.2.3eX.alpha
-EOF
-
test_expect_success '--name-only --list' '
+ cat >expect <<-\EOF &&
+ beta.noindent
+ nextsection.nonewline
+ 123456.a123
+ version.1.2.3eX.alpha
+ EOF
git config ${mode_prefix}list --name-only >output &&
test_cmp expect output
'
-cat > expect << EOF
-beta.noindent sillyValue
-nextsection.nonewline wow2 for me
-EOF
-
test_expect_success '--get-regexp' '
+ cat >expect <<-\EOF &&
+ beta.noindent sillyValue
+ nextsection.nonewline wow2 for me
+ EOF
git config ${mode_get_regexp} in >output &&
test_cmp expect output
'
-cat > expect << EOF
-beta.noindent
-nextsection.nonewline
-EOF
-
test_expect_success '--name-only --get-regexp' '
+ cat >expect <<-\EOF &&
+ beta.noindent
+ nextsection.nonewline
+ EOF
git config ${mode_get_regexp} --name-only in >output &&
test_cmp expect output
'
-cat > expect << EOF
-wow2 for me
-wow4 for you
-EOF
-
test_expect_success '--add' '
+ cat >expect <<-\EOF &&
+ wow2 for me
+ wow4 for you
+ EOF
git config --add nextsection.nonewline "wow4 for you" &&
git config ${mode_get_all} nextsection.nonewline > output &&
test_cmp expect output
@@ -558,37 +557,32 @@ test_expect_success 'get variable with empty value' '
git config --get emptyvalue.variable ^$
'
-echo novalue.variable > expect
-
test_expect_success 'get-regexp variable with no value' '
+ echo novalue.variable >expect &&
git config ${mode_get_regexp} novalue > output &&
test_cmp expect output
'
-echo 'novalue.variable true' > expect
-
test_expect_success 'get-regexp --bool variable with no value' '
+ echo "novalue.variable true" >expect &&
git config ${mode_get_regexp} --bool novalue > output &&
test_cmp expect output
'
-echo 'emptyvalue.variable ' > expect
-
test_expect_success 'get-regexp variable with empty value' '
+ echo "emptyvalue.variable " >expect &&
git config ${mode_get_regexp} emptyvalue > output &&
test_cmp expect output
'
-echo true > expect
-
test_expect_success 'get bool variable with no value' '
+ echo true >expect &&
git config --bool novalue.variable > output &&
test_cmp expect output
'
-echo false > expect
-
test_expect_success 'get bool variable with empty value' '
+ echo false >expect &&
git config --bool emptyvalue.variable > output &&
test_cmp expect output
'
@@ -604,19 +598,19 @@ cat > .git/config << EOF
c = d
EOF
-cat > expect << EOF
+test_expect_success 'new section is partial match of another' '
+ cat >expect <<\EOF &&
[a.b]
c = d
[a]
x = y
EOF
-
-test_expect_success 'new section is partial match of another' '
git config a.x y &&
test_cmp expect .git/config
'
-cat > expect << EOF
+test_expect_success 'new variable inserts into proper section' '
+ cat >expect <<\EOF &&
[a.b]
c = d
[a]
@@ -625,8 +619,6 @@ cat > expect << EOF
[b]
x = y
EOF
-
-test_expect_success 'new variable inserts into proper section' '
git config b.x y &&
git config a.b c &&
test_cmp expect .git/config
@@ -642,11 +634,10 @@ cat > other-config << EOF
bahn = strasse
EOF
-cat > expect << EOF
-ein.bahn=strasse
-EOF
-
test_expect_success 'alternative GIT_CONFIG' '
+ cat >expect <<-\EOF &&
+ ein.bahn=strasse
+ EOF
GIT_CONFIG=other-config git config ${mode_prefix}list >output &&
test_cmp expect output
'
@@ -675,14 +666,13 @@ test_expect_success 'refer config from subdirectory' '
test_cmp_config -C x strasse --file=../other-config --get ein.bahn
'
-cat > expect << EOF
+test_expect_success '--set in alternative file' '
+ cat >expect <<\EOF &&
[ein]
bahn = strasse
[anwohner]
park = ausweis
EOF
-
-test_expect_success '--set in alternative file' '
git config --file=other-config anwohner.park ausweis &&
test_cmp expect other-config
'
@@ -730,7 +720,8 @@ test_expect_success 'rename another section' '
git config ${mode_prefix}rename-section branch."1 234 blabl/a" branch.drei
'
-cat > expect << EOF
+test_expect_success 'rename succeeded' '
+ cat >expect <<\EOF &&
# Hallo
#Bello
[branch "zwei"]
@@ -740,8 +731,6 @@ cat > expect << EOF
[branch "drei"]
weird
EOF
-
-test_expect_success 'rename succeeded' '
test_cmp expect .git/config
'
@@ -753,7 +742,8 @@ test_expect_success 'rename a section with a var on the same line' '
git config ${mode_prefix}rename-section branch.vier branch.zwei
'
-cat > expect << EOF
+test_expect_success 'rename succeeded' '
+ cat >expect <<\EOF &&
# Hallo
#Bello
[branch "zwei"]
@@ -765,8 +755,6 @@ weird
[branch "zwei"]
z = 1
EOF
-
-test_expect_success 'rename succeeded' '
test_cmp expect .git/config
'
@@ -816,32 +804,29 @@ test_expect_success 'remove section' '
git config ${mode_prefix}remove-section branch.zwei
'
-cat > expect << EOF
+test_expect_success 'section was removed properly' '
+ cat >expect <<\EOF &&
# Hallo
#Bello
[branch "drei"]
weird
EOF
-
-test_expect_success 'section was removed properly' '
test_cmp expect .git/config
'
-cat > expect << EOF
+test_expect_success 'section ending' '
+ cat >expect <<\EOF &&
[gitcvs]
enabled = true
dbname = %Ggitcvs2.%a.%m.sqlite
[gitcvs "ext"]
dbname = %Ggitcvs1.%a.%m.sqlite
EOF
-
-test_expect_success 'section ending' '
rm -f .git/config &&
git config ${mode_set} gitcvs.enabled true &&
git config ${mode_set} gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&
git config ${mode_set} gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&
test_cmp expect .git/config
-
'
test_expect_success numbers '
@@ -885,19 +870,17 @@ test_expect_success 'invalid stdin config' '
test_grep "bad config line 1 in standard input" output
'
-cat > expect << EOF
-true
-false
-true
-false
-true
-false
-true
-false
-EOF
-
test_expect_success bool '
-
+ cat >expect <<-\EOF &&
+ true
+ false
+ true
+ false
+ true
+ false
+ true
+ false
+ EOF
git config ${mode_set} bool.true1 01 &&
git config ${mode_set} bool.true2 -1 &&
git config ${mode_set} bool.true3 YeS &&
@@ -912,18 +895,20 @@ test_expect_success bool '
git config --bool --get bool.true$i >>result &&
git config --bool --get bool.false$i >>result || return 1
done &&
- test_cmp expect result'
+ test_cmp expect result
+'
test_expect_success 'invalid bool (--get)' '
-
git config ${mode_set} bool.nobool foobar &&
- test_must_fail git config --bool --get bool.nobool'
+ test_must_fail git config --bool --get bool.nobool
+'
test_expect_success 'invalid bool (set)' '
+ test_must_fail git config --bool bool.nobool foobar
+'
- test_must_fail git config --bool bool.nobool foobar'
-
-cat > expect <<\EOF
+test_expect_success 'set --bool' '
+ cat >expect <<\EOF &&
[bool]
true1 = true
true2 = true
@@ -934,9 +919,6 @@ cat > expect <<\EOF
false3 = false
false4 = false
EOF
-
-test_expect_success 'set --bool' '
-
rm -f .git/config &&
git config --bool bool.true1 01 &&
git config --bool bool.true2 -1 &&
@@ -948,15 +930,13 @@ test_expect_success 'set --bool' '
git config --bool bool.false4 FALSE &&
test_cmp expect .git/config'
-cat > expect <<\EOF
+test_expect_success 'set --int' '
+ cat >expect <<\EOF &&
[int]
val1 = 1
val2 = -1
val3 = 5242880
EOF
-
-test_expect_success 'set --int' '
-
rm -f .git/config &&
git config --int int.val1 01 &&
git config --int int.val2 -1 &&
@@ -994,7 +974,8 @@ test_expect_success 'get --bool-or-int' '
test_cmp expect actual
'
-cat >expect <<\EOF
+test_expect_success 'set --bool-or-int' '
+ cat >expect <<\EOF &&
[bool]
true1 = true
false1 = false
@@ -1005,8 +986,6 @@ cat >expect <<\EOF
int2 = 1
int3 = -1
EOF
-
-test_expect_success 'set --bool-or-int' '
rm -f .git/config &&
git config --bool-or-int bool.true1 true &&
git config --bool-or-int bool.false1 false &&
@@ -1018,44 +997,42 @@ test_expect_success 'set --bool-or-int' '
test_cmp expect .git/config
'
-cat >expect <<\EOF
+test_expect_success !MINGW 'set --path' '
+ cat >expect <<\EOF &&
[path]
home = ~/
normal = /dev/null
trailingtilde = foo~
EOF
-
-test_expect_success !MINGW 'set --path' '
rm -f .git/config &&
git config --path path.home "~/" &&
git config --path path.normal "/dev/null" &&
git config --path path.trailingtilde "foo~" &&
- test_cmp expect .git/config'
+ test_cmp expect .git/config
+'
if test_have_prereq !MINGW && test "${HOME+set}"
then
test_set_prereq HOMEVAR
fi
-cat >expect <<EOF
-$HOME/
-/dev/null
-foo~
-EOF
-
test_expect_success HOMEVAR 'get --path' '
+ cat >expect <<-EOF &&
+ $HOME/
+ /dev/null
+ foo~
+ EOF
git config --get --path path.home > result &&
git config --get --path path.normal >> result &&
git config --get --path path.trailingtilde >> result &&
test_cmp expect result
'
-cat >expect <<\EOF
-/dev/null
-foo~
-EOF
-
test_expect_success !MINGW 'get --path copes with unset $HOME' '
+ cat >expect <<-\EOF &&
+ /dev/null
+ foo~
+ EOF
(
sane_unset HOME &&
test_must_fail git config --get --path path.home \
@@ -1107,17 +1084,35 @@ test_expect_success 'get --type=color' '
rm .git/config &&
git config ${mode_set} foo.color "red" &&
git config --get --type=color foo.color >actual.raw &&
+ git config get --type=color foo.color >actual-subcommand.raw &&
+ test_cmp actual.raw actual-subcommand.raw &&
+ test_decode_color <actual.raw >actual &&
+ echo "<RED>" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'get --type=color with default value only' '
+ git config --get-color "" "red" >actual.raw &&
+ test_decode_color <actual.raw >actual &&
+ echo "<RED>" >expect &&
+ test_cmp expect actual &&
+ git config get --type=color --default="red" "" >actual-subcommand.raw &&
+ test_cmp actual.raw actual-subcommand.raw
+'
+
+test_expect_success TTY 'get --type=color does not use a pager' '
+ test_config core.pager "echo foobar" &&
+ test_terminal git config get --type=color --default="red" "" >actual.raw &&
test_decode_color <actual.raw >actual &&
echo "<RED>" >expect &&
test_cmp expect actual
'
-cat >expect << EOF
+test_expect_success 'set --type=color' '
+ cat >expect <<\EOF &&
[foo]
color = red
EOF
-
-test_expect_success 'set --type=color' '
rm .git/config &&
git config --type=color foo.color "red" &&
test_cmp expect .git/config
@@ -1133,14 +1128,14 @@ test_expect_success 'set --type=color barfs on non-color' '
test_grep "cannot parse color" error
'
-cat > expect << EOF
+test_expect_success 'quoting' '
+ cat >expect <<\EOF &&
[quote]
leading = " test"
ending = "test "
semicolon = "test;test"
hash = "test#test"
EOF
-test_expect_success 'quoting' '
rm -f .git/config &&
git config ${mode_set} quote.leading " test" &&
git config ${mode_set} quote.ending "test " &&
@@ -1151,10 +1146,13 @@ test_expect_success 'quoting' '
test_expect_success 'key with newline' '
test_must_fail git config ${mode_get} "key.with
-newline" 123'
+newline" 123
+'
-test_expect_success 'value with newline' 'git config ${mode_set} key.sub value.with\\\
-newline'
+test_expect_success 'value with newline' '
+ git config ${mode_set} key.sub value.with\\\
+newline
+'
cat > .git/config <<\EOF
[section]
@@ -1166,13 +1164,12 @@ inued
inued"
EOF
-cat > expect <<\EOF
-section.continued=continued
-section.noncont=not continued
-section.quotecont=cont;inued
-EOF
-
test_expect_success 'value continued on next line' '
+ cat >expect <<-\EOF &&
+ section.continued=continued
+ section.noncont=not continued
+ section.quotecont=cont;inued
+ EOF
git config ${mode_prefix}list > result &&
test_cmp expect result
'
@@ -1365,7 +1362,6 @@ test_expect_success 'multiple git -c appends config' '
'
test_expect_success 'last one wins: two level vars' '
-
# sec.var and sec.VAR are the same variable, as the first
# and the last level of a configuration variable name is
# case insensitive.
@@ -1384,7 +1380,6 @@ test_expect_success 'last one wins: two level vars' '
'
test_expect_success 'last one wins: three level vars' '
-
# v.a.r and v.A.r are not the same variable, as the middle
# level of a three-level configuration variable name is
# case sensitive.
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 96648a6e5d..db7f5444da 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -2294,6 +2294,59 @@ do
)
'
+ test_expect_success CASE_INSENSITIVE_FS,REFFILES "stdin $type batch-updates existing reference" '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ test_commit one &&
+ old_head=$(git rev-parse HEAD) &&
+ test_commit two &&
+ head=$(git rev-parse HEAD) &&
+
+ {
+ format_command $type "create refs/heads/foo" "$head" &&
+ format_command $type "create refs/heads/ref" "$old_head" &&
+ format_command $type "create refs/heads/Foo" "$old_head"
+ } >stdin &&
+ git update-ref $type --stdin --batch-updates <stdin >stdout &&
+
+ echo $head >expect &&
+ git rev-parse refs/heads/foo >actual &&
+ echo $old_head >expect &&
+ git rev-parse refs/heads/ref >actual &&
+ test_cmp expect actual &&
+ test_grep -q "reference conflict due to case-insensitive filesystem" stdout
+ )
+ '
+
+ test_expect_success CASE_INSENSITIVE_FS "stdin $type batch-updates existing reference" '
+ git init --ref-format=reftable repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ test_commit one &&
+ old_head=$(git rev-parse HEAD) &&
+ test_commit two &&
+ head=$(git rev-parse HEAD) &&
+
+ {
+ format_command $type "create refs/heads/foo" "$head" &&
+ format_command $type "create refs/heads/ref" "$old_head" &&
+ format_command $type "create refs/heads/Foo" "$old_head"
+ } >stdin &&
+ git update-ref $type --stdin --batch-updates <stdin >stdout &&
+
+ echo $head >expect &&
+ git rev-parse refs/heads/foo >actual &&
+ echo $old_head >expect &&
+ git rev-parse refs/heads/ref >actual &&
+ test_cmp expect actual &&
+ git rev-parse refs/heads/Foo >actual &&
+ test_cmp expect actual
+ )
+ '
+
test_expect_success "stdin $type batch-updates delete incorrect symbolic ref" '
git init repo &&
test_when_finished "rm -fr repo" &&
@@ -2368,4 +2421,25 @@ test_expect_success REFFILES 'empty directories are pruned when not committing'
test_path_is_missing .git/refs/heads/nested
'
+test_expect_success 'dangling symref not overwritten by creation' '
+ test_when_finished "git update-ref -d refs/heads/dangling" &&
+ git symbolic-ref refs/heads/dangling refs/heads/does-not-exist &&
+ test_must_fail git update-ref --no-deref --stdin 2>err <<-\EOF &&
+ create refs/heads/dangling HEAD
+ EOF
+ test_grep "cannot lock.*dangling symref already exists" err &&
+ test_must_fail git rev-parse --verify refs/heads/dangling &&
+ test_must_fail git rev-parse --verify refs/heads/does-not-exist
+'
+
+test_expect_success 'dangling symref overwritten without old oid' '
+ test_when_finished "git update-ref -d refs/heads/dangling" &&
+ git symbolic-ref refs/heads/dangling refs/heads/does-not-exist &&
+ git update-ref --no-deref --stdin <<-\EOF &&
+ update refs/heads/dangling HEAD
+ EOF
+ git rev-parse --verify refs/heads/dangling &&
+ test_must_fail git rev-parse --verify refs/heads/does-not-exist
+'
+
test_done
diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh
index 9da3650e91..36c903ca19 100755
--- a/t/t1403-show-ref.sh
+++ b/t/t1403-show-ref.sh
@@ -228,69 +228,4 @@ test_expect_success 'show-ref sub-modes are mutually exclusive' '
grep "cannot be used together" err
'
-test_expect_success '--exists with existing reference' '
- git show-ref --exists refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-'
-
-test_expect_success '--exists with missing reference' '
- test_expect_code 2 git show-ref --exists refs/heads/does-not-exist
-'
-
-test_expect_success '--exists does not use DWIM' '
- test_expect_code 2 git show-ref --exists $GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME 2>err &&
- grep "reference does not exist" err
-'
-
-test_expect_success '--exists with HEAD' '
- git show-ref --exists HEAD
-'
-
-test_expect_success '--exists with bad reference name' '
- test_when_finished "git update-ref -d refs/heads/bad...name" &&
- new_oid=$(git rev-parse HEAD) &&
- test-tool ref-store main update-ref msg refs/heads/bad...name $new_oid $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
- git show-ref --exists refs/heads/bad...name
-'
-
-test_expect_success '--exists with arbitrary symref' '
- test_when_finished "git symbolic-ref -d refs/symref" &&
- git symbolic-ref refs/symref refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME &&
- git show-ref --exists refs/symref
-'
-
-test_expect_success '--exists with dangling symref' '
- test_when_finished "git symbolic-ref -d refs/heads/dangling" &&
- git symbolic-ref refs/heads/dangling refs/heads/does-not-exist &&
- git show-ref --exists refs/heads/dangling
-'
-
-test_expect_success '--exists with nonexistent object ID' '
- test-tool ref-store main update-ref msg refs/heads/missing-oid $(test_oid 001) $ZERO_OID REF_SKIP_OID_VERIFICATION &&
- git show-ref --exists refs/heads/missing-oid
-'
-
-test_expect_success '--exists with non-commit object' '
- tree_oid=$(git rev-parse HEAD^{tree}) &&
- test-tool ref-store main update-ref msg refs/heads/tree ${tree_oid} $ZERO_OID REF_SKIP_OID_VERIFICATION &&
- git show-ref --exists refs/heads/tree
-'
-
-test_expect_success '--exists with directory fails with generic error' '
- cat >expect <<-EOF &&
- error: reference does not exist
- EOF
- test_expect_code 2 git show-ref --exists refs/heads 2>err &&
- test_cmp expect err
-'
-
-test_expect_success '--exists with non-existent special ref' '
- test_expect_code 2 git show-ref --exists FETCH_HEAD
-'
-
-test_expect_success '--exists with existing special ref' '
- test_when_finished "rm .git/FETCH_HEAD" &&
- git rev-parse HEAD >.git/FETCH_HEAD &&
- git show-ref --exists FETCH_HEAD
-'
-
test_done
diff --git a/t/t1421-reflog-write.sh b/t/t1421-reflog-write.sh
new file mode 100755
index 0000000000..603ec3f6ed
--- /dev/null
+++ b/t/t1421-reflog-write.sh
@@ -0,0 +1,162 @@
+#!/bin/sh
+
+test_description='Manually write reflog entries'
+
+. ./test-lib.sh
+
+SIGNATURE="C O Mitter <committer@example.com> 1112911993 -0700"
+
+test_reflog_matches () {
+ repo="$1" &&
+ refname="$2" &&
+ cat >actual &&
+ test-tool -C "$repo" ref-store main for-each-reflog-ent "$refname" >expected &&
+ test_cmp expected actual
+}
+
+test_expect_success 'invalid number of arguments' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ for args in "" "1" "1 2" "1 2 3" "1 2 3 4 5"
+ do
+ test_must_fail git reflog write $args 2>err &&
+ test_grep "usage: git reflog write" err || return 1
+ done
+ )
+'
+
+test_expect_success 'invalid refname' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_must_fail git reflog write "refs/heads/ invalid" $ZERO_OID $ZERO_OID first 2>err &&
+ test_grep "invalid reference name: " err
+ )
+'
+
+test_expect_success 'unqualified refname is rejected' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_must_fail git reflog write unqualified $ZERO_OID $ZERO_OID first 2>err &&
+ test_grep "invalid reference name: " err
+ )
+'
+
+test_expect_success 'nonexistent object IDs' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_must_fail git reflog write refs/heads/something $(test_oid deadbeef) $ZERO_OID old-object-id 2>err &&
+ test_grep "old object .* does not exist" err &&
+ test_must_fail git reflog write refs/heads/something $ZERO_OID $(test_oid deadbeef) new-object-id 2>err &&
+ test_grep "new object .* does not exist" err
+ )
+'
+
+test_expect_success 'abbreviated object IDs' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ abbreviated_oid=$(git rev-parse HEAD | test_copy_bytes 8) &&
+ test_must_fail git reflog write refs/heads/something $abbreviated_oid $ZERO_OID old-object-id 2>err &&
+ test_grep "invalid old object ID" err &&
+ test_must_fail git reflog write refs/heads/something $ZERO_OID $abbreviated_oid new-object-id 2>err &&
+ test_grep "invalid new object ID" err
+ )
+'
+
+test_expect_success 'reflog message gets normalized' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ COMMIT_OID=$(git rev-parse HEAD) &&
+ git reflog write HEAD $COMMIT_OID $COMMIT_OID "$(printf "message\nwith\nnewlines")" &&
+ git reflog show -1 --format=%gs HEAD >actual &&
+ echo "message with newlines" >expected &&
+ test_cmp expected actual
+ )
+'
+
+test_expect_success 'simple writes' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ COMMIT_OID=$(git rev-parse HEAD) &&
+
+ git reflog write refs/heads/something $ZERO_OID $COMMIT_OID first &&
+ test_reflog_matches . refs/heads/something <<-EOF &&
+ $ZERO_OID $COMMIT_OID $SIGNATURE first
+ EOF
+
+ git reflog write refs/heads/something $COMMIT_OID $COMMIT_OID second &&
+ test_reflog_matches . refs/heads/something <<-EOF
+ $ZERO_OID $COMMIT_OID $SIGNATURE first
+ $COMMIT_OID $COMMIT_OID $SIGNATURE second
+ EOF
+ )
+'
+
+test_expect_success 'uses user.name and user.email config' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ COMMIT_OID=$(git rev-parse HEAD) &&
+
+ sane_unset GIT_COMMITTER_NAME &&
+ sane_unset GIT_COMMITTER_EMAIL &&
+ git config --local user.name "Author" &&
+ git config --local user.email "a@uth.or" &&
+ git reflog write refs/heads/something $ZERO_OID $COMMIT_OID first &&
+ test_reflog_matches . refs/heads/something <<-EOF
+ $ZERO_OID $COMMIT_OID Author <a@uth.or> $GIT_COMMITTER_DATE first
+ EOF
+ )
+'
+
+test_expect_success 'environment variables take precedence over config' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ COMMIT_OID=$(git rev-parse HEAD) &&
+
+ git config --local user.name "Author" &&
+ git config --local user.email "a@uth.or" &&
+ git reflog write refs/heads/something $ZERO_OID $COMMIT_OID first &&
+ test_reflog_matches . refs/heads/something <<-EOF
+ $ZERO_OID $COMMIT_OID $SIGNATURE first
+ EOF
+ )
+'
+
+test_expect_success 'can write to root ref' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ COMMIT_OID=$(git rev-parse HEAD) &&
+
+ git reflog write ROOT_REF_HEAD $ZERO_OID $COMMIT_OID first &&
+ test_reflog_matches . ROOT_REF_HEAD <<-EOF
+ $ZERO_OID $COMMIT_OID $SIGNATURE first
+ EOF
+ )
+'
+
+test_done
diff --git a/t/t1422-show-ref-exists.sh b/t/t1422-show-ref-exists.sh
new file mode 100755
index 0000000000..fdca3f16c8
--- /dev/null
+++ b/t/t1422-show-ref-exists.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+test_description='show-ref --exists'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+. "$TEST_DIRECTORY"/show-ref-exists-tests.sh
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index 5ae86c42be..c4b651c2dc 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -454,6 +454,60 @@ test_expect_success 'tag with NUL in header' '
test_grep "error in tag $tag.*unterminated header: NUL at offset" out
'
+test_expect_success 'tag accepts gpgsig header even if not validly signed' '
+ test_oid_cache <<-\EOF &&
+ header sha1:gpgsig-sha256
+ header sha256:gpgsig
+ EOF
+ header=$(test_oid header) &&
+ sha=$(git rev-parse HEAD) &&
+ cat >good-tag <<-EOF &&
+ object $sha
+ type commit
+ tag good
+ tagger T A Gger <tagger@example.com> 1234567890 -0000
+ $header -----BEGIN PGP SIGNATURE-----
+ Not a valid signature
+ -----END PGP SIGNATURE-----
+
+ This is a good tag.
+ EOF
+
+ tag=$(git hash-object --literally -t tag -w --stdin <good-tag) &&
+ test_when_finished "remove_object $tag" &&
+ git update-ref refs/tags/good $tag &&
+ test_when_finished "git update-ref -d refs/tags/good" &&
+ git -c fsck.extraHeaderEntry=error fsck --tags
+'
+
+test_expect_success 'tag rejects invalid headers' '
+ test_oid_cache <<-\EOF &&
+ header sha1:gpgsig-sha256
+ header sha256:gpgsig
+ EOF
+ header=$(test_oid header) &&
+ sha=$(git rev-parse HEAD) &&
+ cat >bad-tag <<-EOF &&
+ object $sha
+ type commit
+ tag good
+ tagger T A Gger <tagger@example.com> 1234567890 -0000
+ $header -----BEGIN PGP SIGNATURE-----
+ Not a valid signature
+ -----END PGP SIGNATURE-----
+ junk
+
+ This is a bad tag with junk at the end of the headers.
+ EOF
+
+ tag=$(git hash-object --literally -t tag -w --stdin <bad-tag) &&
+ test_when_finished "remove_object $tag" &&
+ git update-ref refs/tags/bad $tag &&
+ test_when_finished "git update-ref -d refs/tags/bad" &&
+ test_must_fail git -c fsck.extraHeaderEntry=error fsck --tags 2>out &&
+ test_grep "error in tag $tag.*invalid format - extra header" out
+'
+
test_expect_success 'cleaned up' '
git fsck >actual 2>&1 &&
test_must_be_empty actual
diff --git a/t/t1460-refs-migrate.sh b/t/t1460-refs-migrate.sh
index 2ab97e1b7d..0e1116a319 100755
--- a/t/t1460-refs-migrate.sh
+++ b/t/t1460-refs-migrate.sh
@@ -7,6 +7,17 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
+print_all_reflog_entries () {
+ repo=$1 &&
+ test-tool -C "$repo" ref-store main for-each-reflog >reflogs &&
+ while read reflog
+ do
+ echo "REFLOG: $reflog" &&
+ test-tool -C "$repo" ref-store main for-each-reflog-ent "$reflog" ||
+ return 1
+ done <reflogs
+}
+
# Migrate the provided repository from one format to the other and
# verify that the references and logs are migrated over correctly.
# Usage: test_migration <repo> <format> [<skip_reflog_verify> [<options...>]]
@@ -28,8 +39,7 @@ test_migration () {
--format='%(refname) %(objectname) %(symref)' >expect &&
if ! $skip_reflog_verify
then
- git -C "$repo" reflog --all >expect_logs &&
- git -C "$repo" reflog list >expect_log_list
+ print_all_reflog_entries "$repo" >expect_logs
fi &&
git -C "$repo" refs migrate --ref-format="$format" "$@" &&
@@ -39,10 +49,8 @@ test_migration () {
test_cmp expect actual &&
if ! $skip_reflog_verify
then
- git -C "$repo" reflog --all >actual_logs &&
- git -C "$repo" reflog list >actual_log_list &&
- test_cmp expect_logs actual_logs &&
- test_cmp expect_log_list actual_log_list
+ print_all_reflog_entries "$repo" >actual_logs &&
+ test_cmp expect_logs actual_logs
fi &&
git -C "$repo" rev-parse --show-ref-format >actual &&
@@ -273,7 +281,7 @@ test_expect_success 'multiple reftable blocks with multiple entries' '
test_commit -C repo second &&
printf "update refs/heads/ref-%d HEAD\n" $(test_seq 3000) >stdin &&
git -C repo update-ref --stdin <stdin &&
- test_migration repo reftable
+ test_migration repo reftable true
'
test_expect_success 'migrating from files format deletes backend files' '
diff --git a/t/t1461-refs-list.sh b/t/t1461-refs-list.sh
new file mode 100755
index 0000000000..36e3d81e59
--- /dev/null
+++ b/t/t1461-refs-list.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+test_description='git refs list tests'
+
+. ./test-lib.sh
+
+git_for_each_ref='git refs list'
+. "$TEST_DIRECTORY"/for-each-ref-tests.sh
diff --git a/t/t1462-refs-exists.sh b/t/t1462-refs-exists.sh
new file mode 100755
index 0000000000..349453c4ca
--- /dev/null
+++ b/t/t1462-refs-exists.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+test_description='refs exists'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+git_show_ref_exists='git refs exists'
+. "$TEST_DIRECTORY"/show-ref-exists-tests.sh
diff --git a/t/t1463-refs-optimize.sh b/t/t1463-refs-optimize.sh
new file mode 100755
index 0000000000..c11c905d79
--- /dev/null
+++ b/t/t1463-refs-optimize.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+test_description='git refs optimize should not change the branch semantic
+
+This test runs git refs optimize and git show-ref and checks that the branch
+semantic is still the same.
+'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+GIT_TEST_DEFAULT_REF_FORMAT=files
+export GIT_TEST_DEFAULT_REF_FORMAT
+
+. ./test-lib.sh
+
+pack_refs='refs optimize'
+. "$TEST_DIRECTORY"/pack-refs-tests.sh
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index 58a4583088..7739ab611b 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -207,6 +207,40 @@ 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-object-format in repo with compat mode' '
+ mkdir repo &&
+ (
+ sane_unset GIT_DEFAULT_HASH &&
+ cd repo &&
+ git init --object-format=sha256 &&
+ git config extensions.compatobjectformat sha1 &&
+ echo sha256 >expect &&
+ git rev-parse --show-object-format >actual &&
+ test_cmp expect actual &&
+ git rev-parse --show-object-format=storage >actual &&
+ test_cmp expect actual &&
+ git rev-parse --show-object-format=input >actual &&
+ test_cmp expect actual &&
+ git rev-parse --show-object-format=output >actual &&
+ test_cmp expect actual &&
+ echo sha1 >expect &&
+ git rev-parse --show-object-format=compat >actual &&
+ test_cmp expect actual &&
+ test_must_fail git rev-parse --show-object-format=squeamish-ossifrage 2>err &&
+ grep "unknown mode for --show-object-format: squeamish-ossifrage" err
+ ) &&
+ mkdir repo2 &&
+ (
+ sane_unset GIT_DEFAULT_HASH &&
+ cd repo2 &&
+ git init --object-format=sha256 &&
+ echo >expect &&
+ git rev-parse --show-object-format=compat >actual &&
+ test_cmp expect actual
+ )
+'
+
test_expect_success 'rev-parse --show-ref-format' '
test_detect_ref_format >expect &&
git rev-parse --show-ref-format >actual &&
diff --git a/t/t1517-outside-repo.sh b/t/t1517-outside-repo.sh
index 8f59b867f2..c824c1a25c 100755
--- a/t/t1517-outside-repo.sh
+++ b/t/t1517-outside-repo.sh
@@ -107,18 +107,46 @@ test_expect_success LIBCURL 'remote-http outside repository' '
test_grep "^error: remote-curl" actual
'
-test_expect_success 'update-server-info does not crash with -h' '
- test_expect_code 129 git update-server-info -h >usage &&
- test_grep "[Uu]sage: git update-server-info " usage &&
- test_expect_code 129 nongit git update-server-info -h >usage &&
- test_grep "[Uu]sage: git update-server-info " usage
-'
-
-test_expect_success 'prune does not crash with -h' '
- test_expect_code 129 git prune -h >usage &&
- test_grep "[Uu]sage: git prune " usage &&
- test_expect_code 129 nongit git prune -h >usage &&
- test_grep "[Uu]sage: git prune " usage
+for cmd in $(git --list-cmds=main)
+do
+ cmd=${cmd%.*} # strip .sh, .perl, etc.
+ case "$cmd" in
+ archimport | citool | credential-netrc | credential-libsecret | \
+ credential-osxkeychain | cvsexportcommit | cvsimport | cvsserver | \
+ daemon | \
+ difftool--helper | filter-branch | fsck-objects | get-tar-commit-id | \
+ gui | gui--askpass | \
+ http-backend | http-fetch | http-push | init-db | \
+ merge-octopus | merge-one-file | merge-resolve | mergetool | \
+ mktag | p4 | p4.py | pickaxe | remote-ftp | remote-ftps | \
+ remote-http | remote-https | replay | send-email | \
+ sh-i18n--envsubst | shell | show | stage | submodule | svn | \
+ upload-archive--writer | upload-pack | web--browse | whatchanged)
+ expect_outcome=expect_failure ;;
+ *)
+ expect_outcome=expect_success ;;
+ esac
+ case "$cmd" in
+ instaweb)
+ prereq=PERL ;;
+ *)
+ prereq= ;;
+ esac
+ test_$expect_outcome $prereq "'git $cmd -h' outside a repository" '
+ test_expect_code 129 nongit git $cmd -h >usage &&
+ test_grep "[Uu]sage: git $cmd " usage
+ '
+ test_$expect_outcome $prereq "'git $cmd --help-all' outside a repository" '
+ test_expect_code 129 nongit git $cmd --help-all >usage &&
+ test_grep "[Uu]sage: git $cmd " usage
+ '
+done
+
+test_expect_success 'fmt-merge-msg does not crash with -h' '
+ test_expect_code 129 git fmt-merge-msg -h >usage &&
+ test_grep "[Uu]sage: git fmt-merge-msg " usage &&
+ test_expect_code 129 nongit git fmt-merge-msg -h >usage &&
+ test_grep "[Uu]sage: git fmt-merge-msg " usage
'
test_done
diff --git a/t/t1900-repo.sh b/t/t1900-repo.sh
new file mode 100755
index 0000000000..2beba67889
--- /dev/null
+++ b/t/t1900-repo.sh
@@ -0,0 +1,113 @@
+#!/bin/sh
+
+test_description='test git repo-info'
+
+. ./test-lib.sh
+
+# Test whether a key-value pair is correctly returned
+#
+# Usage: test_repo_info <label> <init command> <repo_name> <key> <expected value>
+#
+# Arguments:
+# label: the label of the test
+# init_command: a command which creates a repository
+# repo_name: the name of the repository that will be created in init_command
+# key: the key of the field that is being tested
+# expected_value: the value that the field should contain
+test_repo_info () {
+ label=$1
+ init_command=$2
+ repo_name=$3
+ key=$4
+ expected_value=$5
+
+ test_expect_success "setup: $label" '
+ eval "$init_command $repo_name"
+ '
+
+ test_expect_success "keyvalue: $label" '
+ echo "$key=$expected_value" > expect &&
+ git -C "$repo_name" repo info "$key" >actual &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "nul: $label" '
+ printf "%s\n%s\0" "$key" "$expected_value" >expect &&
+ git -C "$repo_name" repo info --format=nul "$key" >actual &&
+ test_cmp_bin expect actual
+ '
+}
+
+test_repo_info 'ref format files is retrieved correctly' \
+ 'git init --ref-format=files' 'format-files' 'references.format' 'files'
+
+test_repo_info 'ref format reftable is retrieved correctly' \
+ 'git init --ref-format=reftable' 'format-reftable' 'references.format' 'reftable'
+
+test_repo_info 'bare repository = false is retrieved correctly' \
+ 'git init' 'nonbare' 'layout.bare' 'false'
+
+test_repo_info 'bare repository = true is retrieved correctly' \
+ 'git init --bare' 'bare' 'layout.bare' 'true'
+
+test_repo_info 'shallow repository = false is retrieved correctly' \
+ 'git init' 'nonshallow' 'layout.shallow' 'false'
+
+test_expect_success 'setup remote' '
+ git init remote &&
+ echo x >remote/x &&
+ git -C remote add x &&
+ git -C remote commit -m x
+'
+
+test_repo_info 'shallow repository = true is retrieved correctly' \
+ 'git clone --depth 1 "file://$PWD/remote"' 'shallow' 'layout.shallow' 'true'
+
+test_repo_info 'object.format = sha1 is retrieved correctly' \
+ 'git init --object-format=sha1' 'sha1' 'object.format' 'sha1'
+
+test_repo_info 'object.format = sha256 is retrieved correctly' \
+ 'git init --object-format=sha256' 'sha256' 'object.format' 'sha256'
+
+test_expect_success 'values returned in order requested' '
+ cat >expect <<-\EOF &&
+ layout.bare=false
+ references.format=files
+ layout.bare=false
+ EOF
+ git init --ref-format=files ordered &&
+ git -C ordered repo info layout.bare references.format layout.bare >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git-repo-info fails if an invalid key is requested' '
+ echo "error: key ${SQ}foo${SQ} not found" >expect &&
+ test_must_fail git repo info foo 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git-repo-info outputs data even if there is an invalid field' '
+ echo "references.format=$(test_detect_ref_format)" >expect &&
+ test_must_fail git repo info foo references.format bar >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git-repo-info aborts when requesting an invalid format' '
+ echo "fatal: invalid format ${SQ}foo${SQ}" >expect &&
+ test_must_fail git repo info --format=foo 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '-z uses nul-terminated format' '
+ printf "layout.bare\nfalse\0layout.shallow\nfalse\0" >expected &&
+ git repo info -z layout.bare layout.shallow >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'git repo info uses the last requested format' '
+ echo "layout.bare=false" >expected &&
+ git repo info --format=nul -z --format=keyvalue layout.bare >actual &&
+ test_cmp expected actual
+'
+
+test_done
diff --git a/t/t1901-repo-structure.sh b/t/t1901-repo-structure.sh
new file mode 100755
index 0000000000..36a71a144e
--- /dev/null
+++ b/t/t1901-repo-structure.sh
@@ -0,0 +1,129 @@
+#!/bin/sh
+
+test_description='test git repo structure'
+
+. ./test-lib.sh
+
+test_expect_success 'empty repository' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ cat >expect <<-\EOF &&
+ | Repository structure | Value |
+ | -------------------- | ----- |
+ | * References | |
+ | * Count | 0 |
+ | * Branches | 0 |
+ | * Tags | 0 |
+ | * Remotes | 0 |
+ | * Others | 0 |
+ | | |
+ | * Reachable objects | |
+ | * Count | 0 |
+ | * Commits | 0 |
+ | * Trees | 0 |
+ | * Blobs | 0 |
+ | * Tags | 0 |
+ EOF
+
+ git repo structure >out 2>err &&
+
+ test_cmp expect out &&
+ test_line_count = 0 err
+ )
+'
+
+test_expect_success 'repository with references and objects' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit_bulk 42 &&
+ git tag -a foo -m bar &&
+
+ oid="$(git rev-parse HEAD)" &&
+ git update-ref refs/remotes/origin/foo "$oid" &&
+
+ # Also creates a commit, tree, and blob.
+ git notes add -m foo &&
+
+ cat >expect <<-\EOF &&
+ | Repository structure | Value |
+ | -------------------- | ----- |
+ | * References | |
+ | * Count | 4 |
+ | * Branches | 1 |
+ | * Tags | 1 |
+ | * Remotes | 1 |
+ | * Others | 1 |
+ | | |
+ | * Reachable objects | |
+ | * Count | 130 |
+ | * Commits | 43 |
+ | * Trees | 43 |
+ | * Blobs | 43 |
+ | * Tags | 1 |
+ EOF
+
+ git repo structure >out 2>err &&
+
+ test_cmp expect out &&
+ test_line_count = 0 err
+ )
+'
+
+test_expect_success 'keyvalue and nul format' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit_bulk 42 &&
+ git tag -a foo -m bar &&
+
+ cat >expect <<-\EOF &&
+ references.branches.count=1
+ references.tags.count=1
+ references.remotes.count=0
+ references.others.count=0
+ objects.commits.count=42
+ objects.trees.count=42
+ objects.blobs.count=42
+ objects.tags.count=1
+ EOF
+
+ git repo structure --format=keyvalue >out 2>err &&
+
+ test_cmp expect out &&
+ test_line_count = 0 err &&
+
+ # Replace key and value delimiters for nul format.
+ tr "\n=" "\0\n" <expect >expect_nul &&
+ git repo structure --format=nul >out 2>err &&
+
+ test_cmp expect_nul out &&
+ test_line_count = 0 err
+ )
+'
+
+test_expect_success 'progress meter option' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit foo &&
+
+ GIT_PROGRESS_DELAY=0 git repo structure --progress >out 2>err &&
+
+ test_file_not_empty out &&
+ test_grep "Counting references: 2, done." err &&
+ test_grep "Counting objects: 3, done." err &&
+
+ GIT_PROGRESS_DELAY=0 git repo structure --no-progress >out 2>err &&
+
+ test_file_not_empty out &&
+ test_line_count = 0 err
+ )
+'
+
+test_done
diff --git a/t/t2401-worktree-prune.sh b/t/t2401-worktree-prune.sh
index fe671d4197..f8f28c76ee 100755
--- a/t/t2401-worktree-prune.sh
+++ b/t/t2401-worktree-prune.sh
@@ -24,8 +24,8 @@ test_expect_success 'prune files inside $GIT_DIR/worktrees' '
Removing worktrees/abc: not a valid directory
EOF
test_cmp expect actual &&
- ! test -f .git/worktrees/abc &&
- ! test -d .git/worktrees
+ test_path_is_missing .git/worktrees/abc &&
+ test_path_is_missing .git/worktrees
'
test_expect_success 'prune directories without gitdir' '
@@ -36,8 +36,8 @@ Removing worktrees/def: gitdir file does not exist
EOF
git worktree prune --verbose 2>actual &&
test_cmp expect actual &&
- ! test -d .git/worktrees/def &&
- ! test -d .git/worktrees
+ test_path_is_missing .git/worktrees/def &&
+ test_path_is_missing .git/worktrees
'
test_expect_success SANITY 'prune directories with unreadable gitdir' '
@@ -47,8 +47,8 @@ test_expect_success SANITY 'prune directories with unreadable gitdir' '
chmod u-r .git/worktrees/def/gitdir &&
git worktree prune --verbose 2>actual &&
test_grep "Removing worktrees/def: unable to read gitdir file" actual &&
- ! test -d .git/worktrees/def &&
- ! test -d .git/worktrees
+ test_path_is_missing .git/worktrees/def &&
+ test_path_is_missing .git/worktrees
'
test_expect_success 'prune directories with invalid gitdir' '
@@ -57,8 +57,8 @@ test_expect_success 'prune directories with invalid gitdir' '
: >.git/worktrees/def/gitdir &&
git worktree prune --verbose 2>actual &&
test_grep "Removing worktrees/def: invalid gitdir file" actual &&
- ! test -d .git/worktrees/def &&
- ! test -d .git/worktrees
+ test_path_is_missing .git/worktrees/def &&
+ test_path_is_missing .git/worktrees
'
test_expect_success 'prune directories with gitdir pointing to nowhere' '
@@ -67,8 +67,8 @@ test_expect_success 'prune directories with gitdir pointing to nowhere' '
echo "$(pwd)"/nowhere >.git/worktrees/def/gitdir &&
git worktree prune --verbose 2>actual &&
test_grep "Removing worktrees/def: gitdir file points to non-existent location" actual &&
- ! test -d .git/worktrees/def &&
- ! test -d .git/worktrees
+ test_path_is_missing .git/worktrees/def &&
+ test_path_is_missing .git/worktrees
'
test_expect_success 'not prune locked checkout' '
@@ -76,23 +76,23 @@ test_expect_success 'not prune locked checkout' '
mkdir -p .git/worktrees/ghi &&
: >.git/worktrees/ghi/locked &&
git worktree prune &&
- test -d .git/worktrees/ghi
+ test_path_is_dir .git/worktrees/ghi
'
test_expect_success 'not prune recent checkouts' '
test_when_finished rm -r .git/worktrees &&
git worktree add jlm HEAD &&
- test -d .git/worktrees/jlm &&
+ test_path_is_dir .git/worktrees/jlm &&
rm -rf jlm &&
git worktree prune --verbose --expire=2.days.ago &&
- test -d .git/worktrees/jlm
+ test_path_is_dir .git/worktrees/jlm
'
test_expect_success 'not prune proper checkouts' '
test_when_finished rm -r .git/worktrees &&
git worktree add --detach "$PWD/nop" main &&
git worktree prune &&
- test -d .git/worktrees/nop
+ test_path_is_dir .git/worktrees/nop
'
test_expect_success 'prune duplicate (linked/linked)' '
@@ -103,8 +103,8 @@ test_expect_success 'prune duplicate (linked/linked)' '
mv .git/worktrees/w2/gitdir.new .git/worktrees/w2/gitdir &&
git worktree prune --verbose 2>actual &&
test_grep "duplicate entry" actual &&
- test -d .git/worktrees/w1 &&
- ! test -d .git/worktrees/w2
+ test_path_is_dir .git/worktrees/w1 &&
+ test_path_is_missing .git/worktrees/w2
'
test_expect_success 'prune duplicate (main/linked)' '
@@ -116,7 +116,7 @@ test_expect_success 'prune duplicate (main/linked)' '
mv repo wt &&
git -C wt worktree prune --verbose 2>actual &&
test_grep "duplicate entry" actual &&
- ! test -d .git/worktrees/wt
+ test_path_is_missing .git/worktrees/wt
'
test_expect_success 'not prune proper worktrees inside linked worktree with relative paths' '
diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh
index 3da824117c..655bb1a0f2 100755
--- a/t/t3070-wildmatch.sh
+++ b/t/t3070-wildmatch.sh
@@ -235,6 +235,8 @@ match 1 1 1 1 aaaaaaabababab '*ab'
match 1 1 1 1 'foo*' 'foo\*'
match 0 0 0 0 foobar 'foo\*bar'
match 1 1 1 1 'f\oo' 'f\\oo'
+match 0 0 0 0 \
+ 1 1 1 1 'foo\' 'foo\'
match 1 1 1 1 ball '*[al]?'
match 0 0 0 0 ten '[ten]'
match 1 1 1 1 ten '**[!te]'
diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh
index e091df6d01..1e812df806 100755
--- a/t/t3206-range-diff.sh
+++ b/t/t3206-range-diff.sh
@@ -707,7 +707,7 @@ test_expect_success 'format-patch --range-diff does not compare notes by default
! grep "note" 0000-*
'
-test_expect_success 'format-patch --notes=custom --range-diff only compares custom notes' '
+test_expect_success 'format-patch --notes=custom --range-diff --cover-letter only compares custom notes' '
test_when_finished "git notes remove topic unmodified || :" &&
git notes add -m "topic note" topic &&
git notes add -m "unmodified note" unmodified &&
@@ -721,6 +721,20 @@ test_expect_success 'format-patch --notes=custom --range-diff only compares cust
! grep "## Notes ##" 0000-*
'
+# --range-diff on a single commit requires --no-cover-letter
+test_expect_success 'format-patch --notes=custom --range-diff on single commit only compares custom notes' '
+ test_when_finished "git notes remove HEAD unmodified || :" &&
+ git notes add -m "topic note" HEAD &&
+ test_when_finished "git notes --ref=custom remove HEAD unmodified || :" &&
+ git notes add -m "unmodified note" unmodified &&
+ git notes --ref=custom add -m "topic note (custom)" HEAD &&
+ git notes --ref=custom add -m "unmodified note (custom)" unmodified &&
+ git format-patch --notes=custom --range-diff=$prev \
+ -1 --stdout >actual &&
+ test_grep "## Notes (custom) ##" actual &&
+ test_grep ! "## Notes ##" actual
+'
+
test_expect_success 'format-patch --range-diff with --no-notes' '
test_when_finished "git notes remove topic unmodified || :" &&
git notes add -m "topic note" topic &&
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 6bac217ed3..e778dd8ae4 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -1176,7 +1176,7 @@ test_expect_success 'rebase -i respects core.commentchar' '
test B = $(git cat-file commit HEAD^ | sed -ne \$p)
'
-test_expect_success 'rebase -i respects core.commentchar=auto' '
+test_expect_success !WITH_BREAKING_CHANGES 'rebase -i respects core.commentchar=auto' '
test_config core.commentchar auto &&
write_script copy-edit-script.sh <<-\EOF &&
cp "$1" edit-script
@@ -1184,8 +1184,23 @@ test_expect_success 'rebase -i respects core.commentchar=auto' '
test_when_finished "git rebase --abort || :" &&
(
test_set_editor "$(pwd)/copy-edit-script.sh" &&
- git rebase -i HEAD^
+ git rebase -i HEAD^ 2>err
) &&
+ sed -n "s/^hint: *\$//p; s/^hint: //p; s/^warning: //p" err >actual &&
+ cat >expect <<-EOF &&
+ Support for ${SQ}core.commentChar=auto${SQ} is deprecated and will be removed in Git 3.0
+
+ To use the default comment string (#) please run
+
+ git config unset core.commentChar
+
+ To set a custom comment string please run
+
+ git config set core.commentChar <comment string>
+
+ where ${SQ}<comment string>${SQ} is the string you wish to use.
+ EOF
+ test_cmp expect actual &&
test -z "$(grep -ve "^#" -e "^\$" -e "^pick" edit-script)"
'
@@ -2263,6 +2278,7 @@ test_expect_success 'non-merge commands reject merge commits' '
edit $oid
fixup $oid
squash $oid
+ drop $oid # acceptable, no advice
EOF
(
set_replace_editor todo &&
diff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh
index 5d093e3a7a..5033411a43 100755
--- a/t/t3415-rebase-autosquash.sh
+++ b/t/t3415-rebase-autosquash.sh
@@ -486,12 +486,28 @@ test_expect_success 'fixup a fixup' '
test XZWY = $(git show | tr -cd W-Z)
'
-test_expect_success 'fixup does not clean up commit message' '
- oneline="#818" &&
- git commit --allow-empty -m "$oneline" &&
- git commit --fixup HEAD --allow-empty &&
- git -c commit.cleanup=strip rebase -ki --autosquash HEAD~2 &&
- test "$oneline" = "$(git show -s --format=%s)"
+test_expect_success 'pick and fixup respect commit.cleanup' '
+ git reset --hard base &&
+ test_commit --no-tag "fixup! second commit" file1 fixup &&
+ test_commit something &&
+ write_script .git/hooks/prepare-commit-msg <<-\EOF &&
+ printf "\n# Prepared\n" >> "$1"
+ EOF
+ git rebase -i --autosquash HEAD~3 &&
+ test_commit_message HEAD~1 <<-\EOF &&
+ second commit
+
+ # Prepared
+ EOF
+ test_commit_message HEAD <<-\EOF &&
+ something
+
+ # Prepared
+ EOF
+ git reset --hard something &&
+ git -c commit.cleanup=strip rebase -i --autosquash HEAD~3 &&
+ test_commit_message HEAD~1 -m "second commit" &&
+ test_commit_message HEAD -m "something"
'
test_done
diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh
index b8a8dd77e7..f9b8999db5 100755
--- a/t/t3418-rebase-continue.sh
+++ b/t/t3418-rebase-continue.sh
@@ -328,7 +328,7 @@ test_expect_success 'there is no --no-reschedule-failed-exec in an ongoing rebas
test_expect_code 129 git rebase --edit-todo --no-reschedule-failed-exec
'
-test_expect_success 'no change in comment character due to conflicts markers with core.commentChar=auto' '
+test_expect_success !WITH_BREAKING_CHANGES 'no change in comment character due to conflicts markers with core.commentChar=auto' '
git checkout -b branch-a &&
test_commit A F1 &&
git checkout -b branch-b HEAD^ &&
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index 04d2a19835..4285314f35 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -48,8 +48,8 @@ test_expect_success 'unknown command' '
git add -N command &&
git diff command >expect &&
cat >>expect <<-EOF &&
- (1/1) Stage addition [y,n,q,a,d,e,p,?]? Unknown command ${SQ}W${SQ} (use ${SQ}?${SQ} for help)
- (1/1) Stage addition [y,n,q,a,d,e,p,?]?$SP
+ (1/1) Stage addition [y,n,q,a,d,e,p,P,?]? Unknown command ${SQ}W${SQ} (use ${SQ}?${SQ} for help)
+ (1/1) Stage addition [y,n,q,a,d,e,p,P,?]?$SP
EOF
git add -p -- command <command >actual 2>&1 &&
test_cmp expect actual
@@ -332,9 +332,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,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,?]?
+ (1/1) Stage deletion [y,n,q,a,d,p,P,?]?
+ (1/2) Stage mode change [y,n,q,a,d,k,K,j,J,g,/,p,P,?]?
+ (2/2) Stage this hunk [y,n,q,a,d,K,J,g,/,e,p,P,?]?
EOF
test_cmp expect actual.filtered
'
@@ -521,13 +521,13 @@ test_expect_success 'split hunk setup' '
test_expect_success 'goto hunk 1 with "g 1"' '
test_when_finished "git reset" &&
tr _ " " >expect <<-EOF &&
- (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? + 1: -1,2 +1,3 +15
+ (2/2) Stage this hunk [y,n,q,a,d,K,J,g,/,e,p,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,p,?]?_
+ (1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
EOF
test_write_lines s y g 1 | git add -p >actual &&
tail -n 7 <actual >actual.trimmed &&
@@ -540,7 +540,7 @@ test_expect_success 'goto hunk 1 with "g1"' '
_10
+15
_20
- (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
+ (1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
EOF
test_write_lines s y g1 | git add -p >actual &&
tail -n 4 <actual >actual.trimmed &&
@@ -550,11 +550,11 @@ test_expect_success 'goto hunk 1 with "g1"' '
test_expect_success 'navigate to hunk via regex /pattern' '
test_when_finished "git reset" &&
tr _ " " >expect <<-EOF &&
- (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? @@ -1,2 +1,3 @@
+ (2/2) Stage this hunk [y,n,q,a,d,K,J,g,/,e,p,P,?]? @@ -1,2 +1,3 @@
_10
+15
_20
- (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
+ (1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
EOF
test_write_lines s y /1,2 | git add -p >actual &&
tail -n 5 <actual >actual.trimmed &&
@@ -567,7 +567,7 @@ test_expect_success 'navigate to hunk via regex / pattern' '
_10
+15
_20
- (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
+ (1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
EOF
test_write_lines s y / 1,2 | git add -p >actual &&
tail -n 4 <actual >actual.trimmed &&
@@ -579,11 +579,11 @@ test_expect_success 'print again the hunk' '
tr _ " " >expect <<-EOF &&
+15
20
- (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? @@ -1,2 +1,3 @@
+ (1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? @@ -1,2 +1,3 @@
10
+15
20
- (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
+ (1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
EOF
test_write_lines s y g 1 p | git add -p >actual &&
tail -n 7 <actual >actual.trimmed &&
@@ -595,11 +595,11 @@ test_expect_success TTY 'print again the hunk (PAGER)' '
cat >expect <<-EOF &&
<GREEN>+<RESET><GREEN>15<RESET>
20<RESET>
- <BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET>PAGER <CYAN>@@ -1,2 +1,3 @@<RESET>
+ <BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>PAGER <CYAN>@@ -1,2 +1,3 @@<RESET>
PAGER 10<RESET>
PAGER <GREEN>+<RESET><GREEN>15<RESET>
PAGER 20<RESET>
- <BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET>
+ <BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
EOF
test_write_lines s y g 1 P |
(
@@ -796,21 +796,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,p,?]? <RESET><BOLD>Split into 2 hunks.<RESET>
+ <YELLOW>(1/1) Stage this hunk [y,n,q,a,d,s,e,p,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,p,?]? <RESET><MAGENTA>@@ -3 +3,2 @@<RESET>
+ <YELLOW>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,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,p,?]? <RESET><MAGENTA>@@ -1,3 +1,3 @@<RESET>
+ <YELLOW>(2/2) Stage this hunk [y,n,q,a,d,K,J,g,/,e,p,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,p,?]? <RESET>
+ <YELLOW>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
EOF
test_cmp expect actual
'
@@ -866,6 +866,44 @@ test_expect_success 'colorized diffs respect diff.wsErrorHighlight' '
test_grep "old<" output
'
+test_expect_success 'diff color respects color.diff' '
+ git reset --hard &&
+
+ echo old >test &&
+ git add test &&
+ echo new >test &&
+
+ printf n >n &&
+ force_color git \
+ -c color.interactive=auto \
+ -c color.interactive.prompt=blue \
+ -c color.diff=false \
+ -c color.diff.old=red \
+ add -p >output.raw 2>&1 <n &&
+ test_decode_color <output.raw >output &&
+ test_grep "BLUE.*Stage this hunk" output &&
+ test_grep ! "RED" output
+'
+
+test_expect_success 're-coloring diff without color.interactive' '
+ git reset --hard &&
+
+ test_write_lines 1 2 3 >test &&
+ git add test &&
+ test_write_lines one 2 three >test &&
+
+ test_write_lines s n n |
+ force_color git \
+ -c color.interactive=false \
+ -c color.interactive.prompt=blue \
+ -c color.diff=true \
+ -c color.diff.frag="bold magenta" \
+ add -p >output.raw 2>&1 &&
+ test_decode_color <output.raw >output &&
+ test_grep "<BOLD;MAGENTA>@@" output &&
+ test_grep ! "BLUE" output
+'
+
test_expect_success 'diffFilter filters diff' '
git reset --hard &&
@@ -1283,6 +1321,12 @@ test_expect_success 'stash accepts -U and --inter-hunk-context' '
test_grep "@@ -2,20 +2,20 @@" actual
'
+test_expect_success 'set up base for -p color tests' '
+ echo commit >file &&
+ git commit -am "commit state" &&
+ git tag patch-base
+'
+
for cmd in add checkout commit reset restore "stash save" "stash push"
do
test_expect_success "$cmd rejects invalid context options" '
@@ -1299,6 +1343,103 @@ do
test_must_fail git $cmd --inter-hunk-context 2 2>actual &&
test_grep -E ".--inter-hunk-context. requires .(--interactive/)?--patch." actual
'
+
+ test_expect_success "$cmd falls back to color.ui" '
+ git reset --hard patch-base &&
+ echo working-tree >file &&
+ test_write_lines y |
+ force_color git -c color.ui=false $cmd -p >output.raw 2>&1 &&
+ test_decode_color <output.raw >output &&
+ test_cmp output.raw output
+ '
done
+test_expect_success 'splitting previous hunk marks split hunks as undecided' '
+ test_write_lines a " " b c d e f g h i j k >file &&
+ git add file &&
+ test_write_lines x " " b y d e f g h i j x >file &&
+ test_write_lines n K s n y q | git add -p file &&
+ git cat-file blob :file >actual &&
+ test_write_lines a " " b y d e f g h i j k >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'splitting edited hunk' '
+ # Before the first hunk is edited it can be split into two
+ # hunks, after editing it can be split into three hunks.
+
+ write_script fake-editor.sh <<-\EOF &&
+ sed "s/^ c/-c/" "$1" >"$1.tmp" &&
+ mv "$1.tmp" "$1"
+ EOF
+
+ test_write_lines a b c d e f g h i j k l m n >file &&
+ git add file &&
+ test_write_lines A b c d E f g h i j k l M n >file &&
+ (
+ test_set_editor "$(pwd)/fake-editor.sh" &&
+ test_write_lines e K s j y n y q | git add -p file
+ ) &&
+ git cat-file blob :file >actual &&
+ test_write_lines a b d e f g h i j k l M n >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'options J, K roll over' '
+ test_write_lines a b c d e f g h i >file &&
+ git add file &&
+ test_write_lines X b c d e f g h X >file &&
+ test_write_lines J J K q | git add -p >out &&
+ test_write_lines 1 2 1 2 >expect &&
+ sed -n -e "s-/.*--" -e "s/^(//p" <out >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'options y, n, a, d, j, k, e roll over to next undecided (1)' '
+ test_write_lines a b c d e f g h i j k l m n o p q >file &&
+ git add file &&
+ test_write_lines X b c d e f g h X j k l m n o p X >file &&
+ test_set_editor : &&
+ test_write_lines g3 y g3 n g3 a g3 d g3 j g3 e k q | git add -p >out &&
+ test_write_lines 1 3 1 3 1 3 1 3 1 3 1 3 1 2 >expect &&
+ sed -n -e "s-/.*--" -e "s/^(//p" <out >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'options y, n, a, d, j, k, e roll over to next undecided (2)' '
+ test_write_lines a b c d e f g h i j k l m n o p q >file &&
+ git add file &&
+ test_write_lines X b c d e f g h X j k l m n o p X >file &&
+ test_set_editor : &&
+ test_write_lines y g3 y g3 n g3 a g3 d g3 j g3 e g1 k q | git add -p >out &&
+ test_write_lines 1 2 3 2 3 2 3 2 3 2 3 2 3 2 1 2 >expect &&
+ sed -n -e "s-/.*--" -e "s/^(//p" <out >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'invalid option s is rejected' '
+ test_write_lines a b c d e f g h i j k >file &&
+ git add file &&
+ test_write_lines X b X d e f g h i j X >file &&
+ test_write_lines j s q | git add -p >out &&
+ sed -ne "s/ @@.*//" -e "s/ \$//" -e "/^(/p" <out >actual &&
+ cat >expect <<-EOF &&
+ (1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,s,e,p,P,?]?
+ (2/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? Sorry, cannot split this hunk
+ (2/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'EOF quits' '
+ echo a >file &&
+ echo a >file2 &&
+ git add file file2 &&
+ echo X >file &&
+ echo X >file2 &&
+ git add -p </dev/null >out &&
+ test_grep file out &&
+ test_grep ! file2 out
+'
+
test_done
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 0bb4648e36..70879941c2 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -902,6 +902,7 @@ test_expect_success 'branch: should not drop the stash if the apply fails' '
test_expect_success 'apply: show same status as git status (relative to ./)' '
git stash clear &&
+ mkdir -p subdir &&
echo 1 >subdir/subfile1 &&
echo 2 >subdir/subfile2 &&
git add subdir/subfile1 &&
@@ -1356,6 +1357,7 @@ test_expect_success 'stash -k -- <pathspec> leaves unstaged files intact' '
test_expect_success 'stash -- <subdir> leaves untracked files in subdir intact' '
git reset &&
+ mkdir -p subdir &&
>subdir/untracked &&
>subdir/tracked1 &&
>subdir/tracked2 &&
@@ -1372,6 +1374,7 @@ test_expect_success 'stash -- <subdir> leaves untracked files in subdir intact'
test_expect_success 'stash -- <subdir> works with binary files' '
git reset &&
+ mkdir -p subdir &&
>subdir/untracked &&
>subdir/tracked &&
cp "$TEST_DIRECTORY"/test-binary-1.png subdir/tracked-binary &&
@@ -1741,4 +1744,50 @@ test_expect_success 'submodules does not affect the branch recorded in stash mes
)
'
+test_expect_success SANITIZE_LEAK 'stash show handles -- without leaking' '
+ git stash show --
+'
+
+test_expect_success 'controlled error return on unrecognized option' '
+ test_expect_code 129 git stash show -p --invalid 2>usage &&
+ grep -e "^usage: git stash show" usage
+'
+
+test_expect_success 'stash.index=true implies --index' '
+ # setup for a few related tests
+ test_commit file base &&
+ echo index >file &&
+ git add file &&
+ echo working >file &&
+ git stash &&
+
+ test_when_finished "git reset --hard" &&
+ git -c stash.index=true stash apply &&
+ echo index >expect &&
+ git show :0:file >actual &&
+ test_cmp expect actual &&
+ echo working >expect &&
+ test_cmp expect file
+'
+
+test_expect_success 'stash.index=true overridden by --no-index' '
+ test_when_finished "git reset --hard" &&
+ git -c stash.index=true stash apply --no-index &&
+ echo base >expect &&
+ git show :0:file >actual &&
+ test_cmp expect actual &&
+ echo working >expect &&
+ test_cmp expect file
+'
+
+test_expect_success 'stash.index=false overridden by --index' '
+ test_when_finished "git reset --hard" &&
+ git -c stash.index=false stash apply --index &&
+ echo index >expect &&
+ git show :0:file >actual &&
+ test_cmp expect actual &&
+ echo working >expect &&
+ test_cmp expect file
+'
+
test_done
diff --git a/t/t3904-stash-patch.sh b/t/t3904-stash-patch.sh
index ae313e3c70..90a4ff2c10 100755
--- a/t/t3904-stash-patch.sh
+++ b/t/t3904-stash-patch.sh
@@ -107,4 +107,23 @@ test_expect_success 'stash -p with split hunk' '
! grep "added line 2" test
'
+test_expect_success 'stash -p not confused by GIT_PAGER_IN_USE' '
+ echo to-stash >test &&
+ # Set both GIT_PAGER_IN_USE and TERM. Our goal is to entice any
+ # diff subprocesses into thinking that they could output
+ # color, even though their stdout is not going into a tty.
+ echo y |
+ GIT_PAGER_IN_USE=1 TERM=vt100 git stash -p &&
+ git diff --exit-code
+'
+
+test_expect_success 'index push not confused by GIT_PAGER_IN_USE' '
+ echo index >test &&
+ git add test &&
+ echo working-tree >test &&
+ # As above, we try to entice the child diff into using color.
+ GIT_PAGER_IN_USE=1 TERM=vt100 git stash push test &&
+ git diff --exit-code
+'
+
test_done
diff --git a/t/t3905-stash-include-untracked.sh b/t/t3905-stash-include-untracked.sh
index 1289ae3e07..7704709054 100755
--- a/t/t3905-stash-include-untracked.sh
+++ b/t/t3905-stash-include-untracked.sh
@@ -87,7 +87,6 @@ test_expect_success 'stash save --patch --all fails' '
test_expect_success 'clean up untracked/untracked file to prepare for next tests' '
git clean --force --quiet
-
'
test_expect_success 'stash pop after save --include-untracked leaves files untracked again' '
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index 8ebd170451..d35695f5b0 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -5,7 +5,7 @@
test_description='Various diff formatting options'
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
@@ -70,7 +70,7 @@ test_expect_success setup '
GIT_COMMITTER_DATE="2006-06-26 00:04:00 +0000" &&
export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
- git checkout master &&
+ git checkout main &&
git pull -s ours --no-rebase . side &&
GIT_AUTHOR_DATE="2006-06-26 00:05:00 +0000" &&
@@ -95,7 +95,7 @@ test_expect_success setup '
test_write_lines B A >dir/sub &&
git add dir/sub &&
git commit -m "Rearranged lines in dir/sub" &&
- git checkout master &&
+ git checkout main &&
GIT_AUTHOR_DATE="2006-06-26 00:06:00 +0000" &&
GIT_COMMITTER_DATE="2006-06-26 00:06:00 +0000" &&
@@ -103,7 +103,7 @@ test_expect_success setup '
git checkout -b mode initial &&
git update-index --chmod=+x file0 &&
git commit -m "update mode" &&
- git checkout -f master &&
+ git checkout -f main &&
GIT_AUTHOR_DATE="2006-06-26 00:06:00 +0000" &&
GIT_COMMITTER_DATE="2006-06-26 00:06:00 +0000" &&
@@ -112,12 +112,12 @@ test_expect_success setup '
git update-index --chmod=+x file2 &&
git commit -m "update mode (file2)" &&
git notes add -m "note" &&
- git checkout -f master &&
+ git checkout -f main &&
- # Same merge as master, but with parents reversed. Hide it in a
+ # Same merge as main, but with parents reversed. Hide it in a
# pseudo-ref to avoid impacting tests with --all.
commit=$(echo reverse |
- git commit-tree -p master^2 -p master^1 master^{tree}) &&
+ git commit-tree -p main^2 -p main^1 main^{tree}) &&
git update-ref REVERSE $commit &&
git config diff.renames false &&
@@ -127,15 +127,15 @@ test_expect_success setup '
: <<\EOF
! [initial] Initial
- * [master] Merge branch 'side'
+ * [main] Merge branch 'side'
! [rearrange] Rearranged lines in dir/sub
! [side] Side
----
+ [rearrange] Rearranged lines in dir/sub
- - [master] Merge branch 'side'
+ - [main] Merge branch 'side'
* + [side] Side
- * [master^] Third
- * [master~2] Second
+ * [main^] Third
+ * [main~2] Second
+*++ [initial] Initial
EOF
@@ -311,64 +311,64 @@ diff-tree initial mode
diff-tree --stat initial mode
diff-tree --summary initial mode
-diff-tree master
-diff-tree -m master
-diff-tree -p master
-diff-tree -p -m master
-diff-tree -c master
-diff-tree -c --abbrev master
-:noellipses diff-tree -c --abbrev master
-diff-tree --cc master
+diff-tree main
+diff-tree -m main
+diff-tree -p main
+diff-tree -p -m main
+diff-tree -c main
+diff-tree -c --abbrev main
+:noellipses diff-tree -c --abbrev main
+diff-tree --cc main
# stat only should show the diffstat with the first parent
-diff-tree -c --stat master
-diff-tree --cc --stat master
-diff-tree -c --stat --summary master
-diff-tree --cc --stat --summary master
+diff-tree -c --stat main
+diff-tree --cc --stat main
+diff-tree -c --stat --summary main
+diff-tree --cc --stat --summary main
# stat summary should show the diffstat and summary with the first parent
diff-tree -c --stat --summary side
diff-tree --cc --stat --summary side
-diff-tree --cc --shortstat master
+diff-tree --cc --shortstat main
diff-tree --cc --summary REVERSE
# improved by Timo's patch
-diff-tree --cc --patch-with-stat master
+diff-tree --cc --patch-with-stat main
# improved by Timo's patch
-diff-tree --cc --patch-with-stat --summary master
+diff-tree --cc --patch-with-stat --summary main
# this is correct
diff-tree --cc --patch-with-stat --summary side
-log master
-log -p master
-log --root master
-log --root -p master
-log --patch-with-stat master
-log --root --patch-with-stat master
-log --root --patch-with-stat --summary master
+log main
+log -p main
+log --root main
+log --root -p main
+log --patch-with-stat main
+log --root --patch-with-stat main
+log --root --patch-with-stat --summary main
# improved by Timo's patch
-log --root -c --patch-with-stat --summary master
+log --root -c --patch-with-stat --summary main
# improved by Timo's patch
-log --root --cc --patch-with-stat --summary master
-log --no-diff-merges -p --first-parent master
-log --diff-merges=off -p --first-parent master
-log --first-parent --diff-merges=off -p master
-log -p --first-parent master
-log -p --diff-merges=first-parent master
-log --diff-merges=first-parent master
-log -m -p --first-parent master
-log -m -p master
-log --cc -m -p master
-log -c -m -p master
-log -m --raw master
-log -m --stat master
-log -SF master
-log -S F master
-log -SF -p master
-log -SF master --max-count=0
-log -SF master --max-count=1
-log -SF master --max-count=2
-log -GF master
-log -GF -p master
-log -GF -p --pickaxe-all master
-log -IA -IB -I1 -I2 -p master
+log --root --cc --patch-with-stat --summary main
+log --no-diff-merges -p --first-parent main
+log --diff-merges=off -p --first-parent main
+log --first-parent --diff-merges=off -p main
+log -p --first-parent main
+log -p --diff-merges=first-parent main
+log --diff-merges=first-parent main
+log -m -p --first-parent main
+log -m -p main
+log --cc -m -p main
+log -c -m -p main
+log -m --raw main
+log -m --stat main
+log -SF main
+log -S F main
+log -SF -p main
+log -SF main --max-count=0
+log -SF main --max-count=1
+log -SF main --max-count=2
+log -GF main
+log -GF -p main
+log -GF -p --pickaxe-all main
+log -IA -IB -I1 -I2 -p main
log --decorate --all
log --decorate=full --all
log --decorate --clear-decorations --all
@@ -377,35 +377,35 @@ log --decorate=full --clear-decorations --all
rev-list --parents HEAD
rev-list --children HEAD
-whatchanged master
-:noellipses whatchanged master
-whatchanged -p master
-whatchanged --root master
-:noellipses whatchanged --root master
-whatchanged --root -p master
-whatchanged --patch-with-stat master
-whatchanged --root --patch-with-stat master
-whatchanged --root --patch-with-stat --summary master
+whatchanged main
+:noellipses whatchanged main
+whatchanged -p main
+whatchanged --root main
+:noellipses whatchanged --root main
+whatchanged --root -p main
+whatchanged --patch-with-stat main
+whatchanged --root --patch-with-stat main
+whatchanged --root --patch-with-stat --summary main
# improved by Timo's patch
-whatchanged --root -c --patch-with-stat --summary master
+whatchanged --root -c --patch-with-stat --summary main
# improved by Timo's patch
-whatchanged --root --cc --patch-with-stat --summary master
-whatchanged -SF master
-:noellipses whatchanged -SF master
-whatchanged -SF -p master
+whatchanged --root --cc --patch-with-stat --summary main
+whatchanged -SF main
+:noellipses whatchanged -SF main
+whatchanged -SF -p main
-log --patch-with-stat master -- dir/
-whatchanged --patch-with-stat master -- dir/
-log --patch-with-stat --summary master -- dir/
-whatchanged --patch-with-stat --summary master -- dir/
+log --patch-with-stat main -- dir/
+whatchanged --patch-with-stat main -- dir/
+log --patch-with-stat --summary main -- dir/
+whatchanged --patch-with-stat --summary main -- dir/
show initial
show --root initial
show side
-show master
-show -c master
-show -m master
-show --first-parent master
+show main
+show -c main
+show -m main
+show --first-parent main
show --stat side
show --stat --summary side
show --patch-with-stat side
@@ -414,22 +414,22 @@ show --patch-with-raw side
show --patch-with-stat --summary side
format-patch --stdout initial..side
-format-patch --stdout initial..master^
-format-patch --stdout initial..master
-format-patch --stdout --no-numbered initial..master
-format-patch --stdout --numbered initial..master
+format-patch --stdout initial..main^
+format-patch --stdout initial..main
+format-patch --stdout --no-numbered initial..main
+format-patch --stdout --numbered initial..main
format-patch --attach --stdout initial..side
format-patch --attach --stdout --suffix=.diff initial..side
-format-patch --attach --stdout initial..master^
-format-patch --attach --stdout initial..master
+format-patch --attach --stdout initial..main^
+format-patch --attach --stdout initial..main
format-patch --inline --stdout initial..side
-format-patch --inline --stdout initial..master^
-format-patch --inline --stdout --numbered-files initial..master
-format-patch --inline --stdout initial..master
-format-patch --inline --stdout --subject-prefix=TESTCASE initial..master
+format-patch --inline --stdout initial..main^
+format-patch --inline --stdout --numbered-files initial..main
+format-patch --inline --stdout initial..main
+format-patch --inline --stdout --subject-prefix=TESTCASE initial..main
config format.subjectprefix DIFFERENT_PREFIX
-format-patch --inline --stdout initial..master^^
-format-patch --stdout --cover-letter -n initial..master^
+format-patch --inline --stdout initial..main^^
+format-patch --stdout --cover-letter -n initial..main^
diff --abbrev initial..side
diff -U initial..side
@@ -448,13 +448,13 @@ diff --name-status dir2 dir
diff --no-index --name-status dir2 dir
diff --no-index --name-status -- dir2 dir
diff --no-index dir dir3
-diff master master^ side
+diff main main^ side
# Can't use spaces...
-diff --line-prefix=abc master master^ side
-diff --dirstat master~1 master~2
+diff --line-prefix=abc main main^ side
+diff --dirstat main~1 main~2
diff --dirstat initial rearrange
diff --dirstat-by-file initial rearrange
-diff --dirstat --cc master~1 master
+diff --dirstat --cc main~1 main
# No-index --abbrev and --no-abbrev
diff --raw initial
:noellipses diff --raw initial
@@ -482,7 +482,7 @@ test_expect_success !WITH_BREAKING_CHANGES 'whatchanged needs --i-still-use-this
'
test_expect_success 'log -m matches pure log' '
- git log master >result &&
+ git log main >result &&
process_diffs result >expected &&
git log -m >result &&
process_diffs result >actual &&
@@ -490,17 +490,17 @@ test_expect_success 'log -m matches pure log' '
'
test_expect_success 'log --diff-merges=on matches --diff-merges=separate' '
- git log -p --diff-merges=separate master >result &&
+ git log -p --diff-merges=separate main >result &&
process_diffs result >expected &&
- git log -p --diff-merges=on master >result &&
+ git log -p --diff-merges=on main >result &&
process_diffs result >actual &&
test_cmp expected actual
'
test_expect_success 'log --dd matches --diff-merges=1 -p' '
- git log --diff-merges=1 -p master >result &&
+ git log --diff-merges=1 -p main >result &&
process_diffs result >expected &&
- git log --dd master >result &&
+ git log --dd main >result &&
process_diffs result >actual &&
test_cmp expected actual
'
@@ -511,19 +511,19 @@ test_expect_success 'deny wrong log.diffMerges config' '
'
test_expect_success 'git config log.diffMerges first-parent' '
- git log -p --diff-merges=first-parent master >result &&
+ git log -p --diff-merges=first-parent main >result &&
process_diffs result >expected &&
test_config log.diffMerges first-parent &&
- git log -p --diff-merges=on master >result &&
+ git log -p --diff-merges=on main >result &&
process_diffs result >actual &&
test_cmp expected actual
'
test_expect_success 'git config log.diffMerges first-parent vs -m' '
- git log -p --diff-merges=first-parent master >result &&
+ git log -p --diff-merges=first-parent main >result &&
process_diffs result >expected &&
test_config log.diffMerges first-parent &&
- git log -p -m master >result &&
+ git log -p -m main >result &&
process_diffs result >actual &&
test_cmp expected actual
'
@@ -572,7 +572,7 @@ test_expect_success 'diff-tree --stdin with log formatting' '
Third
Second
EOF
- git rev-list master | git diff-tree --stdin --format=%s -s >actual &&
+ git rev-list main | git diff-tree --stdin --format=%s -s >actual &&
test_cmp expect actual
'
@@ -585,16 +585,16 @@ test_expect_success 'diff-tree --stdin with pathspec' '
dir/sub
EOF
- git rev-list master^ |
+ git rev-list main^ |
git diff-tree -r --stdin --name-only --format=%s dir >actual &&
test_cmp expect actual
'
test_expect_success 'show A B ... -- <pathspec>' '
# side touches dir/sub, file0, and file3
- # master^ touches dir/sub, and file1
- # master^^ touches dir/sub, file0, and file2
- git show --name-only --format="<%s>" side master^ master^^ -- dir >actual &&
+ # main^ touches dir/sub, and file1
+ # main^^ touches dir/sub, file0, and file2
+ git show --name-only --format="<%s>" side main^ main^^ -- dir >actual &&
cat >expect <<-\EOF &&
<Side>
@@ -610,7 +610,7 @@ test_expect_success 'show A B ... -- <pathspec>' '
'
test_expect_success 'diff -I<regex>: setup' '
- git checkout master &&
+ git checkout main &&
test_seq 50 >file0 &&
git commit -m "Set up -I<regex> test file" file0 &&
test_seq 50 | sed -e "s/13/ten and three/" -e "/7\$/d" >file0 &&
@@ -648,6 +648,56 @@ test_expect_success 'diff -I<regex>: detect malformed regex' '
test_grep "invalid regex given to -I: " error
'
+test_expect_success 'diff -I<regex>: ignore matching file' '
+ test_when_finished "git rm -f file1" &&
+ test_seq 50 >file1 &&
+ git add file1 &&
+ test_seq 50 | sed -e "s/13/ten and three/" -e "s/^[124-9].*/& /" >file1 &&
+
+ : >actual &&
+ git diff --raw --ignore-blank-lines -I"ten.*e" -I"^[124-9]" >>actual &&
+ git diff --name-only --ignore-blank-lines -I"ten.*e" -I"^[124-9]" >>actual &&
+ git diff --name-status --ignore-blank-lines -I"ten.*e" -I"^[124-9]" >>actual &&
+ test_grep ! "file1" actual
+'
+
+test_expect_success 'diff -I<regex>: ignore all content changes' '
+ test_when_finished "git rm -f file1 file2 file3" &&
+ : >file1 &&
+ git add file1 &&
+ : >file2 &&
+ git add file2 &&
+ : >file3 &&
+ git add file3 &&
+
+ rm -f file1 file2 &&
+ mkdir file2 &&
+ echo "A" >file3 &&
+ A_hash=$(git hash-object -w file3) &&
+ echo "B" >file3 &&
+ B_hash=$(git hash-object -w file3) &&
+ cat <<-EOF | git update-index --index-info &&
+ 100644 $A_hash 1 file3
+ 100644 $B_hash 2 file3
+ EOF
+
+ test_diff_no_content_changes () {
+ git diff $1 --ignore-blank-lines -I".*" >actual &&
+ test_line_count = 3 actual &&
+ test_grep "file1" actual &&
+ test_grep "file2" actual &&
+ test_grep "file3" actual &&
+ test_grep ! "diff --git" actual
+ } &&
+ test_diff_no_content_changes "--raw" &&
+ test_diff_no_content_changes "--name-only" &&
+ test_diff_no_content_changes "--name-status" &&
+
+ : >actual &&
+ test_must_fail git diff --quiet -I".*" >actual &&
+ test_must_be_empty actual
+'
+
# check_prefix <patch> <src> <dst>
# check only lines with paths to avoid dependency on exact oid/contents
check_prefix () {
diff --git a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_main
index 9951e3677d..af1cf20f4c 100644
--- a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_main
@@ -1,4 +1,4 @@
-$ git diff-tree --cc --patch-with-stat --summary master
+$ git diff-tree --cc --patch-with-stat --summary main
59d314ad6f356dd08601a4cd5e530381da3e3c64
dir/sub | 2 ++
file0 | 3 +++
diff --git a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_master b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_main
index db3c0a7b2c..0ec6042097 100644
--- a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_master
+++ b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_main
@@ -1,4 +1,4 @@
-$ git diff-tree --cc --patch-with-stat master
+$ git diff-tree --cc --patch-with-stat main
59d314ad6f356dd08601a4cd5e530381da3e3c64
dir/sub | 2 ++
file0 | 3 +++
diff --git a/t/t4013/diff.diff-tree_--cc_--shortstat_master b/t/t4013/diff.diff-tree_--cc_--shortstat_main
index a4ca42df2a..9a4ef03197 100644
--- a/t/t4013/diff.diff-tree_--cc_--shortstat_master
+++ b/t/t4013/diff.diff-tree_--cc_--shortstat_main
@@ -1,4 +1,4 @@
-$ git diff-tree --cc --shortstat master
+$ git diff-tree --cc --shortstat main
59d314ad6f356dd08601a4cd5e530381da3e3c64
2 files changed, 5 insertions(+)
$
diff --git a/t/t4013/diff.diff-tree_-c_--stat_--summary_master b/t/t4013/diff.diff-tree_--cc_--stat_--summary_main
index 81c3021541..9db08a4aa9 100644
--- a/t/t4013/diff.diff-tree_-c_--stat_--summary_master
+++ b/t/t4013/diff.diff-tree_--cc_--stat_--summary_main
@@ -1,4 +1,4 @@
-$ git diff-tree -c --stat --summary master
+$ git diff-tree --cc --stat --summary main
59d314ad6f356dd08601a4cd5e530381da3e3c64
dir/sub | 2 ++
file0 | 3 +++
diff --git a/t/t4013/diff.diff-tree_-c_--stat_master b/t/t4013/diff.diff-tree_--cc_--stat_main
index 89d59b1548..7ecc67a524 100644
--- a/t/t4013/diff.diff-tree_-c_--stat_master
+++ b/t/t4013/diff.diff-tree_--cc_--stat_main
@@ -1,4 +1,4 @@
-$ git diff-tree -c --stat master
+$ git diff-tree --cc --stat main
59d314ad6f356dd08601a4cd5e530381da3e3c64
dir/sub | 2 ++
file0 | 3 +++
diff --git a/t/t4013/diff.diff-tree_--cc_master b/t/t4013/diff.diff-tree_--cc_main
index 5ecb4e14ae..1a962856ad 100644
--- a/t/t4013/diff.diff-tree_--cc_master
+++ b/t/t4013/diff.diff-tree_--cc_main
@@ -1,4 +1,4 @@
-$ git diff-tree --cc master
+$ git diff-tree --cc main
59d314ad6f356dd08601a4cd5e530381da3e3c64
diff --cc dir/sub
index cead32e,7289e35..992913c
diff --git a/t/t4013/diff.diff-tree_-c_--abbrev_master b/t/t4013/diff.diff-tree_-c_--abbrev_main
index b8e4aa2530..039d127cf2 100644
--- a/t/t4013/diff.diff-tree_-c_--abbrev_master
+++ b/t/t4013/diff.diff-tree_-c_--abbrev_main
@@ -1,4 +1,4 @@
-$ git diff-tree -c --abbrev master
+$ git diff-tree -c --abbrev main
59d314ad6f356dd08601a4cd5e530381da3e3c64
::100644 100644 100644 cead32e... 7289e35... 992913c... MM dir/sub
::100644 100644 100644 b414108... f4615da... 10a8a9f... MM file0
diff --git a/t/t4013/diff.diff-tree_--cc_--stat_--summary_master b/t/t4013/diff.diff-tree_-c_--stat_--summary_main
index d019867dd9..05a8d16ba7 100644
--- a/t/t4013/diff.diff-tree_--cc_--stat_--summary_master
+++ b/t/t4013/diff.diff-tree_-c_--stat_--summary_main
@@ -1,4 +1,4 @@
-$ git diff-tree --cc --stat --summary master
+$ git diff-tree -c --stat --summary main
59d314ad6f356dd08601a4cd5e530381da3e3c64
dir/sub | 2 ++
file0 | 3 +++
diff --git a/t/t4013/diff.diff-tree_--cc_--stat_master b/t/t4013/diff.diff-tree_-c_--stat_main
index 40b91796b3..61d9f450df 100644
--- a/t/t4013/diff.diff-tree_--cc_--stat_master
+++ b/t/t4013/diff.diff-tree_-c_--stat_main
@@ -1,4 +1,4 @@
-$ git diff-tree --cc --stat master
+$ git diff-tree -c --stat main
59d314ad6f356dd08601a4cd5e530381da3e3c64
dir/sub | 2 ++
file0 | 3 +++
diff --git a/t/t4013/diff.diff-tree_-c_master b/t/t4013/diff.diff-tree_-c_main
index e2d2bb2611..a84e1185cf 100644
--- a/t/t4013/diff.diff-tree_-c_master
+++ b/t/t4013/diff.diff-tree_-c_main
@@ -1,4 +1,4 @@
-$ git diff-tree -c master
+$ git diff-tree -c main
59d314ad6f356dd08601a4cd5e530381da3e3c64
::100644 100644 100644 cead32e925b1420c84c14cbf7cf755e7e45af8ad 7289e35bff32727c08dda207511bec138fdb9ea5 992913c5aa0a5476d10c49ed0f21fc0c6d1aedf3 MM dir/sub
::100644 100644 100644 b414108e81e5091fe0974a1858b4d0d22b107f70 f4615da674c09df322d6ba8d6b21ecfb1b1ba510 10a8a9f3657f91a156b9f0184ed79a20adef9f7f MM file0
diff --git a/t/t4013/diff.diff-tree_-m_master b/t/t4013/diff.diff-tree_-m_main
index 6d0a2207fb..5da1f7f525 100644
--- a/t/t4013/diff.diff-tree_-m_master
+++ b/t/t4013/diff.diff-tree_-m_main
@@ -1,4 +1,4 @@
-$ git diff-tree -m master
+$ git diff-tree -m main
59d314ad6f356dd08601a4cd5e530381da3e3c64
:040000 040000 65f5c9dd60ce3b2b3324b618ac7accf8d912c113 0564e026437809817a64fff393079714b6dd4628 M dir
:100644 100644 b414108e81e5091fe0974a1858b4d0d22b107f70 10a8a9f3657f91a156b9f0184ed79a20adef9f7f M file0
diff --git a/t/t4013/diff.diff-tree_-p_-m_master b/t/t4013/diff.diff-tree_-p_-m_main
index b60bea039d..29c9fc20b8 100644
--- a/t/t4013/diff.diff-tree_-p_-m_master
+++ b/t/t4013/diff.diff-tree_-p_-m_main
@@ -1,4 +1,4 @@
-$ git diff-tree -p -m master
+$ git diff-tree -p -m main
59d314ad6f356dd08601a4cd5e530381da3e3c64
diff --git a/dir/sub b/dir/sub
index cead32e..992913c 100644
diff --git a/t/t4013/diff.diff-tree_-p_main b/t/t4013/diff.diff-tree_-p_main
new file mode 100644
index 0000000000..c658062422
--- /dev/null
+++ b/t/t4013/diff.diff-tree_-p_main
@@ -0,0 +1,2 @@
+$ git diff-tree -p main
+$
diff --git a/t/t4013/diff.diff-tree_-p_master b/t/t4013/diff.diff-tree_-p_master
deleted file mode 100644
index b182875fb2..0000000000
--- a/t/t4013/diff.diff-tree_-p_master
+++ /dev/null
@@ -1,2 +0,0 @@
-$ git diff-tree -p master
-$
diff --git a/t/t4013/diff.diff-tree_main b/t/t4013/diff.diff-tree_main
new file mode 100644
index 0000000000..dc5b9fdeb6
--- /dev/null
+++ b/t/t4013/diff.diff-tree_main
@@ -0,0 +1,2 @@
+$ git diff-tree main
+$
diff --git a/t/t4013/diff.diff-tree_master b/t/t4013/diff.diff-tree_master
deleted file mode 100644
index fe9226f8a1..0000000000
--- a/t/t4013/diff.diff-tree_master
+++ /dev/null
@@ -1,2 +0,0 @@
-$ git diff-tree master
-$
diff --git a/t/t4013/diff.diff_--dirstat_--cc_main~1_main b/t/t4013/diff.diff_--dirstat_--cc_main~1_main
new file mode 100644
index 0000000000..168a357a02
--- /dev/null
+++ b/t/t4013/diff.diff_--dirstat_--cc_main~1_main
@@ -0,0 +1,3 @@
+$ git diff --dirstat --cc main~1 main
+ 40.0% dir/
+$
diff --git a/t/t4013/diff.diff_--dirstat_--cc_master~1_master b/t/t4013/diff.diff_--dirstat_--cc_master~1_master
deleted file mode 100644
index fba4e34175..0000000000
--- a/t/t4013/diff.diff_--dirstat_--cc_master~1_master
+++ /dev/null
@@ -1,3 +0,0 @@
-$ git diff --dirstat --cc master~1 master
- 40.0% dir/
-$
diff --git a/t/t4013/diff.diff_--dirstat_main~1_main~2 b/t/t4013/diff.diff_--dirstat_main~1_main~2
new file mode 100644
index 0000000000..6809733708
--- /dev/null
+++ b/t/t4013/diff.diff_--dirstat_main~1_main~2
@@ -0,0 +1,3 @@
+$ git diff --dirstat main~1 main~2
+ 40.0% dir/
+$
diff --git a/t/t4013/diff.diff_--dirstat_master~1_master~2 b/t/t4013/diff.diff_--dirstat_master~1_master~2
deleted file mode 100644
index b672e1ca63..0000000000
--- a/t/t4013/diff.diff_--dirstat_master~1_master~2
+++ /dev/null
@@ -1,3 +0,0 @@
-$ git diff --dirstat master~1 master~2
- 40.0% dir/
-$
diff --git a/t/t4013/diff.diff_--line-prefix=abc_master_master^_side b/t/t4013/diff.diff_--line-prefix=abc_main_main^_side
index 99f91e7f0e..67a2145a36 100644
--- a/t/t4013/diff.diff_--line-prefix=abc_master_master^_side
+++ b/t/t4013/diff.diff_--line-prefix=abc_main_main^_side
@@ -1,4 +1,4 @@
-$ git diff --line-prefix=abc master master^ side
+$ git diff --line-prefix=abc main main^ side
abcdiff --cc dir/sub
abcindex cead32e,7289e35..992913c
abc--- a/dir/sub
diff --git a/t/t4013/diff.diff_master_master^_side b/t/t4013/diff.diff_main_main^_side
index 50ec9cadd6..ab81ec9e47 100644
--- a/t/t4013/diff.diff_master_master^_side
+++ b/t/t4013/diff.diff_main_main^_side
@@ -1,4 +1,4 @@
-$ git diff master master^ side
+$ git diff main main^ side
diff --cc dir/sub
index cead32e,7289e35..992913c
--- a/dir/sub
diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..master b/t/t4013/diff.format-patch_--attach_--stdout_initial..main
index 52fedc179e..9f56380350 100644
--- a/t/t4013/diff.format-patch_--attach_--stdout_initial..master
+++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..main
@@ -1,4 +1,4 @@
-$ git format-patch --attach --stdout initial..master
+$ git format-patch --attach --stdout initial..main
From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
From: A U Thor <author@example.com>
Date: Mon, 26 Jun 2006 00:01:00 +0000
diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..master^ b/t/t4013/diff.format-patch_--attach_--stdout_initial..main^
index 1c3cde251b..80132ea99e 100644
--- a/t/t4013/diff.format-patch_--attach_--stdout_initial..master^
+++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..main^
@@ -1,4 +1,4 @@
-$ git format-patch --attach --stdout initial..master^
+$ git format-patch --attach --stdout initial..main^
From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
From: A U Thor <author@example.com>
Date: Mon, 26 Jun 2006 00:01:00 +0000
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..main
index 02c4db7ec5..8e889090fb 100644
--- a/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master
+++ b/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..main
@@ -1,4 +1,4 @@
-$ git format-patch --inline --stdout --numbered-files initial..master
+$ git format-patch --inline --stdout --numbered-files initial..main
From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
From: A U Thor <author@example.com>
Date: Mon, 26 Jun 2006 00:01:00 +0000
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..main
index c7677c5951..d7d2b12d15 100644
--- a/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master
+++ b/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..main
@@ -1,4 +1,4 @@
-$ git format-patch --inline --stdout --subject-prefix=TESTCASE initial..master
+$ git format-patch --inline --stdout --subject-prefix=TESTCASE initial..main
From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
From: A U Thor <author@example.com>
Date: Mon, 26 Jun 2006 00:01:00 +0000
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_initial..main
index 5b3e34e2c0..c49c423f82 100644
--- a/t/t4013/diff.format-patch_--inline_--stdout_initial..master
+++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..main
@@ -1,4 +1,4 @@
-$ git format-patch --inline --stdout initial..master
+$ git format-patch --inline --stdout initial..main
From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
From: A U Thor <author@example.com>
Date: Mon, 26 Jun 2006 00:01:00 +0000
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^ b/t/t4013/diff.format-patch_--inline_--stdout_initial..main^
index d13f8a8128..8669dbfe6c 100644
--- a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^
+++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..main^
@@ -1,4 +1,4 @@
-$ git format-patch --inline --stdout initial..master^
+$ git format-patch --inline --stdout initial..main^
From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
From: A U Thor <author@example.com>
Date: Mon, 26 Jun 2006 00:01:00 +0000
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^ b/t/t4013/diff.format-patch_--inline_--stdout_initial..main^^
index caec5537de..b749be58b0 100644
--- a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^
+++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..main^^
@@ -1,4 +1,4 @@
-$ git format-patch --inline --stdout initial..master^^
+$ git format-patch --inline --stdout initial..main^^
From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
From: A U Thor <author@example.com>
Date: Mon, 26 Jun 2006 00:01:00 +0000
diff --git a/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..main^
index 244d964fc6..567f222198 100644
--- a/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^
+++ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..main^
@@ -1,4 +1,4 @@
-$ git format-patch --stdout --cover-letter -n initial..master^
+$ git format-patch --stdout --cover-letter -n initial..main^
From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
From: C O Mitter <committer@example.com>
Date: Mon, 26 Jun 2006 00:06:00 +0000
diff --git a/t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master b/t/t4013/diff.format-patch_--stdout_--no-numbered_initial..main
index bfc287a147..195b62ea4f 100644
--- a/t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master
+++ b/t/t4013/diff.format-patch_--stdout_--no-numbered_initial..main
@@ -1,4 +1,4 @@
-$ git format-patch --stdout --no-numbered initial..master
+$ git format-patch --stdout --no-numbered initial..main
From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
From: A U Thor <author@example.com>
Date: Mon, 26 Jun 2006 00:01:00 +0000
diff --git a/t/t4013/diff.format-patch_--stdout_--numbered_initial..master b/t/t4013/diff.format-patch_--stdout_--numbered_initial..main
index 568f6f584e..0678a38515 100644
--- a/t/t4013/diff.format-patch_--stdout_--numbered_initial..master
+++ b/t/t4013/diff.format-patch_--stdout_--numbered_initial..main
@@ -1,4 +1,4 @@
-$ git format-patch --stdout --numbered initial..master
+$ git format-patch --stdout --numbered initial..main
From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
From: A U Thor <author@example.com>
Date: Mon, 26 Jun 2006 00:01:00 +0000
diff --git a/t/t4013/diff.format-patch_--stdout_initial..master b/t/t4013/diff.format-patch_--stdout_initial..main
index 5f0352f9f7..b4a6302e7c 100644
--- a/t/t4013/diff.format-patch_--stdout_initial..master
+++ b/t/t4013/diff.format-patch_--stdout_initial..main
@@ -1,4 +1,4 @@
-$ git format-patch --stdout initial..master
+$ git format-patch --stdout initial..main
From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
From: A U Thor <author@example.com>
Date: Mon, 26 Jun 2006 00:01:00 +0000
diff --git a/t/t4013/diff.format-patch_--stdout_initial..master^ b/t/t4013/diff.format-patch_--stdout_initial..main^
index 2ae454d807..36b3221582 100644
--- a/t/t4013/diff.format-patch_--stdout_initial..master^
+++ b/t/t4013/diff.format-patch_--stdout_initial..main^
@@ -1,4 +1,4 @@
-$ git format-patch --stdout initial..master^
+$ git format-patch --stdout initial..main^
From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
From: A U Thor <author@example.com>
Date: Mon, 26 Jun 2006 00:01:00 +0000
diff --git a/t/t4013/diff.log_-c_-m_-p_master b/t/t4013/diff.log_--cc_-m_-p_main
index b660f3d5f2..f32746ea3e 100644
--- a/t/t4013/diff.log_-c_-m_-p_master
+++ b/t/t4013/diff.log_--cc_-m_-p_main
@@ -1,4 +1,4 @@
-$ git log -c -m -p master
+$ git log --cc -m -p main
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0)
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_--decorate=full_--all b/t/t4013/diff.log_--decorate=full_--all
index 6b0b334a5d..c099399525 100644
--- a/t/t4013/diff.log_--decorate=full_--all
+++ b/t/t4013/diff.log_--decorate=full_--all
@@ -26,7 +26,7 @@ Date: Mon Jun 26 00:06:00 2006 +0000
Notes added by 'git notes add'
-commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> refs/heads/master)
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> refs/heads/main)
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
diff --git a/t/t4013/diff.log_--decorate=full_--clear-decorations_--all b/t/t4013/diff.log_--decorate=full_--clear-decorations_--all
index 1c030a6554..c43684e536 100644
--- a/t/t4013/diff.log_--decorate=full_--clear-decorations_--all
+++ b/t/t4013/diff.log_--decorate=full_--clear-decorations_--all
@@ -26,7 +26,7 @@ Date: Mon Jun 26 00:06:00 2006 +0000
Notes added by 'git notes add'
-commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> refs/heads/master)
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> refs/heads/main)
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
diff --git a/t/t4013/diff.log_--decorate=full_--decorate-all_--all b/t/t4013/diff.log_--decorate=full_--decorate-all_--all
index d6e7928784..48dca61681 100644
--- a/t/t4013/diff.log_--decorate=full_--decorate-all_--all
+++ b/t/t4013/diff.log_--decorate=full_--decorate-all_--all
@@ -26,7 +26,7 @@ Date: Mon Jun 26 00:06:00 2006 +0000
Notes added by 'git notes add'
-commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> refs/heads/master)
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> refs/heads/main)
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
diff --git a/t/t4013/diff.log_--decorate_--all b/t/t4013/diff.log_--decorate_--all
index c7df1f5814..8bbf891f65 100644
--- a/t/t4013/diff.log_--decorate_--all
+++ b/t/t4013/diff.log_--decorate_--all
@@ -26,7 +26,7 @@ Date: Mon Jun 26 00:06:00 2006 +0000
Notes added by 'git notes add'
-commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> master)
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> main)
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
diff --git a/t/t4013/diff.log_--decorate_--clear-decorations_--all b/t/t4013/diff.log_--decorate_--clear-decorations_--all
index 88be82cce3..86b1353f51 100644
--- a/t/t4013/diff.log_--decorate_--clear-decorations_--all
+++ b/t/t4013/diff.log_--decorate_--clear-decorations_--all
@@ -26,7 +26,7 @@ Date: Mon Jun 26 00:06:00 2006 +0000
Notes added by 'git notes add'
-commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> master)
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> main)
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
diff --git a/t/t4013/diff.log_--decorate_--decorate-all_--all b/t/t4013/diff.log_--decorate_--decorate-all_--all
index 5d22618bb6..59fb17bfc5 100644
--- a/t/t4013/diff.log_--decorate_--decorate-all_--all
+++ b/t/t4013/diff.log_--decorate_--decorate-all_--all
@@ -26,7 +26,7 @@ Date: Mon Jun 26 00:06:00 2006 +0000
Notes added by 'git notes add'
-commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> master)
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> main)
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
diff --git a/t/t4013/diff.log_--diff-merges=first-parent_master b/t/t4013/diff.log_--diff-merges=first-parent_main
index fa63a557dd..bacee621e5 100644
--- a/t/t4013/diff.log_--diff-merges=first-parent_master
+++ b/t/t4013/diff.log_--diff-merges=first-parent_main
@@ -1,4 +1,4 @@
-$ git log --diff-merges=first-parent master
+$ git log --diff-merges=first-parent main
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_--no-diff-merges_-p_--first-parent_master b/t/t4013/diff.log_--diff-merges=off_-p_--first-parent_main
index 597002232e..fe180fda3b 100644
--- a/t/t4013/diff.log_--no-diff-merges_-p_--first-parent_master
+++ b/t/t4013/diff.log_--diff-merges=off_-p_--first-parent_main
@@ -1,4 +1,4 @@
-$ git log --no-diff-merges -p --first-parent master
+$ git log --diff-merges=off -p --first-parent main
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_--first-parent_--diff-merges=off_-p_master b/t/t4013/diff.log_--first-parent_--diff-merges=off_-p_main
index 5d7461a167..dca62d4e60 100644
--- a/t/t4013/diff.log_--first-parent_--diff-merges=off_-p_master
+++ b/t/t4013/diff.log_--first-parent_--diff-merges=off_-p_main
@@ -1,4 +1,4 @@
-$ git log --first-parent --diff-merges=off -p master
+$ git log --first-parent --diff-merges=off -p main
commit 80e25ffa65bcdbe82ef654b4d06dbbde7945c37f
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_--diff-merges=off_-p_--first-parent_master b/t/t4013/diff.log_--no-diff-merges_-p_--first-parent_main
index 194e893c94..0b54118088 100644
--- a/t/t4013/diff.log_--diff-merges=off_-p_--first-parent_master
+++ b/t/t4013/diff.log_--no-diff-merges_-p_--first-parent_main
@@ -1,4 +1,4 @@
-$ git log --diff-merges=off -p --first-parent master
+$ git log --no-diff-merges -p --first-parent main
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_ b/t/t4013/diff.log_--patch-with-stat_--summary_main_--_dir_
index a18f1472a9..3ed46cc867 100644
--- a/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_
+++ b/t/t4013/diff.log_--patch-with-stat_--summary_main_--_dir_
@@ -1,4 +1,4 @@
-$ git log --patch-with-stat --summary master -- dir/
+$ git log --patch-with-stat --summary main -- dir/
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_--patch-with-stat_master b/t/t4013/diff.log_--patch-with-stat_main
index ae425c4672..2e12b557cb 100644
--- a/t/t4013/diff.log_--patch-with-stat_master
+++ b/t/t4013/diff.log_--patch-with-stat_main
@@ -1,4 +1,4 @@
-$ git log --patch-with-stat master
+$ git log --patch-with-stat main
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_--patch-with-stat_master_--_dir_ b/t/t4013/diff.log_--patch-with-stat_main_--_dir_
index d5207cadf4..d511ea7f6b 100644
--- a/t/t4013/diff.log_--patch-with-stat_master_--_dir_
+++ b/t/t4013/diff.log_--patch-with-stat_main_--_dir_
@@ -1,4 +1,4 @@
-$ git log --patch-with-stat master -- dir/
+$ git log --patch-with-stat main -- dir/
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_main
index 0fc1e8cd71..3cfd0e6422 100644
--- a/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_main
@@ -1,4 +1,4 @@
-$ git log --root --cc --patch-with-stat --summary master
+$ git log --root --cc --patch-with-stat --summary main
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_--root_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_--patch-with-stat_--summary_main
index dffc09dde9..9f4d6dfa44 100644
--- a/t/t4013/diff.log_--root_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.log_--root_--patch-with-stat_--summary_main
@@ -1,4 +1,4 @@
-$ git log --root --patch-with-stat --summary master
+$ git log --root --patch-with-stat --summary main
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_--root_--patch-with-stat_master b/t/t4013/diff.log_--root_--patch-with-stat_main
index 55aa98012d..0d69ae2e11 100644
--- a/t/t4013/diff.log_--root_--patch-with-stat_master
+++ b/t/t4013/diff.log_--root_--patch-with-stat_main
@@ -1,4 +1,4 @@
-$ git log --root --patch-with-stat master
+$ git log --root --patch-with-stat main
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_main
index 019d85f7de..1b71add9de 100644
--- a/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_main
@@ -1,4 +1,4 @@
-$ git log --root -c --patch-with-stat --summary master
+$ git log --root -c --patch-with-stat --summary main
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_--root_-p_master b/t/t4013/diff.log_--root_-p_main
index b42c334439..04581296ed 100644
--- a/t/t4013/diff.log_--root_-p_master
+++ b/t/t4013/diff.log_--root_-p_main
@@ -1,4 +1,4 @@
-$ git log --root -p master
+$ git log --root -p main
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_--root_master b/t/t4013/diff.log_--root_main
index e8f46159da..d5e90fd6b4 100644
--- a/t/t4013/diff.log_--root_master
+++ b/t/t4013/diff.log_--root_main
@@ -1,4 +1,4 @@
-$ git log --root master
+$ git log --root main
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_-GF_-p_--pickaxe-all_master b/t/t4013/diff.log_-GF_-p_--pickaxe-all_main
index d36f88098b..1f7a497c2d 100644
--- a/t/t4013/diff.log_-GF_-p_--pickaxe-all_master
+++ b/t/t4013/diff.log_-GF_-p_--pickaxe-all_main
@@ -1,4 +1,4 @@
-$ git log -GF -p --pickaxe-all master
+$ git log -GF -p --pickaxe-all main
commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:02:00 2006 +0000
diff --git a/t/t4013/diff.log_-GF_-p_master b/t/t4013/diff.log_-GF_-p_main
index 9d93f2c23a..c80dda41e9 100644
--- a/t/t4013/diff.log_-GF_-p_master
+++ b/t/t4013/diff.log_-GF_-p_main
@@ -1,4 +1,4 @@
-$ git log -GF -p master
+$ git log -GF -p main
commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:02:00 2006 +0000
diff --git a/t/t4013/diff.log_-SF_master b/t/t4013/diff.log_-GF_main
index c1599f2f52..b94a7f7839 100644
--- a/t/t4013/diff.log_-SF_master
+++ b/t/t4013/diff.log_-GF_main
@@ -1,4 +1,4 @@
-$ git log -SF master
+$ git log -GF main
commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:02:00 2006 +0000
diff --git a/t/t4013/diff.log_-IA_-IB_-I1_-I2_-p_master b/t/t4013/diff.log_-IA_-IB_-I1_-I2_-p_main
index 929f35a05b..67e26b4e54 100644
--- a/t/t4013/diff.log_-IA_-IB_-I1_-I2_-p_master
+++ b/t/t4013/diff.log_-IA_-IB_-I1_-I2_-p_main
@@ -1,4 +1,4 @@
-$ git log -IA -IB -I1 -I2 -p master
+$ git log -IA -IB -I1 -I2 -p main
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_-SF_-p_master b/t/t4013/diff.log_-SF_-p_main
index 5e32438972..fa82ac1490 100644
--- a/t/t4013/diff.log_-SF_-p_master
+++ b/t/t4013/diff.log_-SF_-p_main
@@ -1,4 +1,4 @@
-$ git log -SF -p master
+$ git log -SF -p main
commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:02:00 2006 +0000
diff --git a/t/t4013/diff.log_-GF_master b/t/t4013/diff.log_-SF_main
index 4c6708d2d0..dbf770db49 100644
--- a/t/t4013/diff.log_-GF_master
+++ b/t/t4013/diff.log_-SF_main
@@ -1,4 +1,4 @@
-$ git log -GF master
+$ git log -SF main
commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:02:00 2006 +0000
diff --git a/t/t4013/diff.log_-SF_main_--max-count=0 b/t/t4013/diff.log_-SF_main_--max-count=0
new file mode 100644
index 0000000000..683b17eb99
--- /dev/null
+++ b/t/t4013/diff.log_-SF_main_--max-count=0
@@ -0,0 +1,2 @@
+$ git log -SF main --max-count=0
+$
diff --git a/t/t4013/diff.log_-SF_master_--max-count=2 b/t/t4013/diff.log_-SF_main_--max-count=1
index a6c55fd482..2102426f8c 100644
--- a/t/t4013/diff.log_-SF_master_--max-count=2
+++ b/t/t4013/diff.log_-SF_main_--max-count=1
@@ -1,4 +1,4 @@
-$ git log -SF master --max-count=2
+$ git log -SF main --max-count=1
commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:02:00 2006 +0000
diff --git a/t/t4013/diff.log_-SF_main_--max-count=2 b/t/t4013/diff.log_-SF_main_--max-count=2
new file mode 100644
index 0000000000..23e12a4cbb
--- /dev/null
+++ b/t/t4013/diff.log_-SF_main_--max-count=2
@@ -0,0 +1,7 @@
+$ git log -SF main --max-count=2
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+$
diff --git a/t/t4013/diff.log_-SF_master_--max-count=0 b/t/t4013/diff.log_-SF_master_--max-count=0
deleted file mode 100644
index c1fc6c8731..0000000000
--- a/t/t4013/diff.log_-SF_master_--max-count=0
+++ /dev/null
@@ -1,2 +0,0 @@
-$ git log -SF master --max-count=0
-$
diff --git a/t/t4013/diff.log_-SF_master_--max-count=1 b/t/t4013/diff.log_-SF_master_--max-count=1
deleted file mode 100644
index c981a03814..0000000000
--- a/t/t4013/diff.log_-SF_master_--max-count=1
+++ /dev/null
@@ -1,7 +0,0 @@
-$ git log -SF master --max-count=1
-commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
-Author: A U Thor <author@example.com>
-Date: Mon Jun 26 00:02:00 2006 +0000
-
- Third
-$
diff --git a/t/t4013/diff.log_-S_F_master b/t/t4013/diff.log_-S_F_main
index 978d2b4118..a75a42e143 100644
--- a/t/t4013/diff.log_-S_F_master
+++ b/t/t4013/diff.log_-S_F_main
@@ -1,4 +1,4 @@
-$ git log -S F master
+$ git log -S F main
commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:02:00 2006 +0000
diff --git a/t/t4013/diff.log_--cc_-m_-p_master b/t/t4013/diff.log_-c_-m_-p_main
index 7c217cf348..427f732456 100644
--- a/t/t4013/diff.log_--cc_-m_-p_master
+++ b/t/t4013/diff.log_-c_-m_-p_main
@@ -1,4 +1,4 @@
-$ git log --cc -m -p master
+$ git log -c -m -p main
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0)
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_-m_--raw_master b/t/t4013/diff.log_-m_--raw_main
index cd2ecc4628..31d9bc7707 100644
--- a/t/t4013/diff.log_-m_--raw_master
+++ b/t/t4013/diff.log_-m_--raw_main
@@ -1,4 +1,4 @@
-$ git log -m --raw master
+$ git log -m --raw main
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0)
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_-m_--stat_master b/t/t4013/diff.log_-m_--stat_main
index c7db084fd9..4c8909229b 100644
--- a/t/t4013/diff.log_-m_--stat_master
+++ b/t/t4013/diff.log_-m_--stat_main
@@ -1,4 +1,4 @@
-$ git log -m --stat master
+$ git log -m --stat main
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0)
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_-m_-p_--first-parent_master b/t/t4013/diff.log_-m_-p_--first-parent_main
index 7a0073f529..459e10786b 100644
--- a/t/t4013/diff.log_-m_-p_--first-parent_master
+++ b/t/t4013/diff.log_-m_-p_--first-parent_main
@@ -1,4 +1,4 @@
-$ git log -m -p --first-parent master
+$ git log -m -p --first-parent main
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_-m_-p_master b/t/t4013/diff.log_-m_-p_main
index 9ca62a01ed..07453c5698 100644
--- a/t/t4013/diff.log_-m_-p_master
+++ b/t/t4013/diff.log_-m_-p_main
@@ -1,4 +1,4 @@
-$ git log -m -p master
+$ git log -m -p main
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0)
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_-p_--diff-merges=first-parent_master b/t/t4013/diff.log_-p_--diff-merges=first-parent_main
index 9538a27511..264a2f373e 100644
--- a/t/t4013/diff.log_-p_--diff-merges=first-parent_master
+++ b/t/t4013/diff.log_-p_--diff-merges=first-parent_main
@@ -1,4 +1,4 @@
-$ git log -p --diff-merges=first-parent master
+$ git log -p --diff-merges=first-parent main
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_-p_--first-parent_master b/t/t4013/diff.log_-p_--first-parent_main
index 28840ebea1..247980817b 100644
--- a/t/t4013/diff.log_-p_--first-parent_master
+++ b/t/t4013/diff.log_-p_--first-parent_main
@@ -1,4 +1,4 @@
-$ git log -p --first-parent master
+$ git log -p --first-parent main
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_-p_master b/t/t4013/diff.log_-p_main
index bf1326dc36..c82b4dbf16 100644
--- a/t/t4013/diff.log_-p_master
+++ b/t/t4013/diff.log_-p_main
@@ -1,4 +1,4 @@
-$ git log -p master
+$ git log -p main
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_master b/t/t4013/diff.log_main
index a8f6ce5abd..50401f73e6 100644
--- a/t/t4013/diff.log_master
+++ b/t/t4013/diff.log_main
@@ -1,4 +1,4 @@
-$ git log master
+$ git log main
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.noellipses-diff-tree_-c_--abbrev_master b/t/t4013/diff.noellipses-diff-tree_-c_--abbrev_main
index bb80f013b3..3aa1f80af3 100644
--- a/t/t4013/diff.noellipses-diff-tree_-c_--abbrev_master
+++ b/t/t4013/diff.noellipses-diff-tree_-c_--abbrev_main
@@ -1,4 +1,4 @@
-$ git diff-tree -c --abbrev master
+$ git diff-tree -c --abbrev main
59d314ad6f356dd08601a4cd5e530381da3e3c64
::100644 100644 100644 cead32e 7289e35 992913c MM dir/sub
::100644 100644 100644 b414108 f4615da 10a8a9f MM file0
diff --git a/t/t4013/diff.noellipses-whatchanged_--root_master b/t/t4013/diff.noellipses-whatchanged_--root_main
index c2cfd4e729..2bec055835 100644
--- a/t/t4013/diff.noellipses-whatchanged_--root_master
+++ b/t/t4013/diff.noellipses-whatchanged_--root_main
@@ -1,4 +1,4 @@
-$ git whatchanged --root master
+$ git whatchanged --root main
commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:03:00 2006 +0000
diff --git a/t/t4013/diff.noellipses-whatchanged_-SF_master b/t/t4013/diff.noellipses-whatchanged_-SF_main
index b36ce5886e..0c1476d19e 100644
--- a/t/t4013/diff.noellipses-whatchanged_-SF_master
+++ b/t/t4013/diff.noellipses-whatchanged_-SF_main
@@ -1,4 +1,4 @@
-$ git whatchanged -SF master
+$ git whatchanged -SF main
commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:02:00 2006 +0000
diff --git a/t/t4013/diff.noellipses-whatchanged_master b/t/t4013/diff.noellipses-whatchanged_main
index 55e500f2ed..c48d2851aa 100644
--- a/t/t4013/diff.noellipses-whatchanged_master
+++ b/t/t4013/diff.noellipses-whatchanged_main
@@ -1,4 +1,4 @@
-$ git whatchanged master
+$ git whatchanged main
commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:03:00 2006 +0000
diff --git a/t/t4013/diff.show_--first-parent_master b/t/t4013/diff.show_--first-parent_main
index 3dcbe473a0..480502d65c 100644
--- a/t/t4013/diff.show_--first-parent_master
+++ b/t/t4013/diff.show_--first-parent_main
@@ -1,4 +1,4 @@
-$ git show --first-parent master
+$ git show --first-parent main
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.show_-c_master b/t/t4013/diff.show_-c_main
index 81aba8da96..74ef8bc96b 100644
--- a/t/t4013/diff.show_-c_master
+++ b/t/t4013/diff.show_-c_main
@@ -1,4 +1,4 @@
-$ git show -c master
+$ git show -c main
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.show_-m_master b/t/t4013/diff.show_-m_main
index 4ea2ee453d..8fd56736d9 100644
--- a/t/t4013/diff.show_-m_master
+++ b/t/t4013/diff.show_-m_main
@@ -1,4 +1,4 @@
-$ git show -m master
+$ git show -m main
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0)
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.show_master b/t/t4013/diff.show_main
index fb08ce0e46..630b52a95e 100644
--- a/t/t4013/diff.show_master
+++ b/t/t4013/diff.show_main
@@ -1,4 +1,4 @@
-$ git show master
+$ git show main
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_ b/t/t4013/diff.whatchanged_--patch-with-stat_--summary_main_--_dir_
index c8b6af2f43..ce0754d556 100644
--- a/t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_
+++ b/t/t4013/diff.whatchanged_--patch-with-stat_--summary_main_--_dir_
@@ -1,4 +1,4 @@
-$ git whatchanged --patch-with-stat --summary master -- dir/
+$ git whatchanged --patch-with-stat --summary main -- dir/
commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:03:00 2006 +0000
diff --git a/t/t4013/diff.whatchanged_--patch-with-stat_master b/t/t4013/diff.whatchanged_--patch-with-stat_main
index 1ac431ba92..aabccf39a5 100644
--- a/t/t4013/diff.whatchanged_--patch-with-stat_master
+++ b/t/t4013/diff.whatchanged_--patch-with-stat_main
@@ -1,4 +1,4 @@
-$ git whatchanged --patch-with-stat master
+$ git whatchanged --patch-with-stat main
commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:03:00 2006 +0000
diff --git a/t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_ b/t/t4013/diff.whatchanged_--patch-with-stat_main_--_dir_
index b30c28588f..c05a0e8149 100644
--- a/t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_
+++ b/t/t4013/diff.whatchanged_--patch-with-stat_main_--_dir_
@@ -1,4 +1,4 @@
-$ git whatchanged --patch-with-stat master -- dir/
+$ git whatchanged --patch-with-stat main -- dir/
commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:03:00 2006 +0000
diff --git a/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_main
index 30aae7817b..1f74b1b548 100644
--- a/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_main
@@ -1,4 +1,4 @@
-$ git whatchanged --root --cc --patch-with-stat --summary master
+$ git whatchanged --root --cc --patch-with-stat --summary main
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_main
index db90e51525..80d9812151 100644
--- a/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_main
@@ -1,4 +1,4 @@
-$ git whatchanged --root --patch-with-stat --summary master
+$ git whatchanged --root --patch-with-stat --summary main
commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:03:00 2006 +0000
diff --git a/t/t4013/diff.whatchanged_--root_--patch-with-stat_master b/t/t4013/diff.whatchanged_--root_--patch-with-stat_main
index 9a6cc92ce7..c0b9082a2c 100644
--- a/t/t4013/diff.whatchanged_--root_--patch-with-stat_master
+++ b/t/t4013/diff.whatchanged_--root_--patch-with-stat_main
@@ -1,4 +1,4 @@
-$ git whatchanged --root --patch-with-stat master
+$ git whatchanged --root --patch-with-stat main
commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:03:00 2006 +0000
diff --git a/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_main
index d1d32bd34c..0002c6912e 100644
--- a/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_main
@@ -1,4 +1,4 @@
-$ git whatchanged --root -c --patch-with-stat --summary master
+$ git whatchanged --root -c --patch-with-stat --summary main
commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.whatchanged_--root_-p_master b/t/t4013/diff.whatchanged_--root_-p_main
index ebf1f0661e..39f3e2be74 100644
--- a/t/t4013/diff.whatchanged_--root_-p_master
+++ b/t/t4013/diff.whatchanged_--root_-p_main
@@ -1,4 +1,4 @@
-$ git whatchanged --root -p master
+$ git whatchanged --root -p main
commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:03:00 2006 +0000
diff --git a/t/t4013/diff.whatchanged_--root_master b/t/t4013/diff.whatchanged_--root_main
index a405cb6138..36f4d6697f 100644
--- a/t/t4013/diff.whatchanged_--root_master
+++ b/t/t4013/diff.whatchanged_--root_main
@@ -1,4 +1,4 @@
-$ git whatchanged --root master
+$ git whatchanged --root main
commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:03:00 2006 +0000
diff --git a/t/t4013/diff.whatchanged_-SF_-p_master b/t/t4013/diff.whatchanged_-SF_-p_main
index f39da84822..0e2e67c0df 100644
--- a/t/t4013/diff.whatchanged_-SF_-p_master
+++ b/t/t4013/diff.whatchanged_-SF_-p_main
@@ -1,4 +1,4 @@
-$ git whatchanged -SF -p master
+$ git whatchanged -SF -p main
commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:02:00 2006 +0000
diff --git a/t/t4013/diff.whatchanged_-SF_master b/t/t4013/diff.whatchanged_-SF_main
index 0499321d0e..34c6bf6b95 100644
--- a/t/t4013/diff.whatchanged_-SF_master
+++ b/t/t4013/diff.whatchanged_-SF_main
@@ -1,4 +1,4 @@
-$ git whatchanged -SF master
+$ git whatchanged -SF main
commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:02:00 2006 +0000
diff --git a/t/t4013/diff.whatchanged_-p_master b/t/t4013/diff.whatchanged_-p_main
index f18d43209c..18f3bdeef4 100644
--- a/t/t4013/diff.whatchanged_-p_master
+++ b/t/t4013/diff.whatchanged_-p_main
@@ -1,4 +1,4 @@
-$ git whatchanged -p master
+$ git whatchanged -p main
commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:03:00 2006 +0000
diff --git a/t/t4013/diff.whatchanged_master b/t/t4013/diff.whatchanged_main
index cd3bcc2c72..d6c83ed8b0 100644
--- a/t/t4013/diff.whatchanged_master
+++ b/t/t4013/diff.whatchanged_main
@@ -1,4 +1,4 @@
-$ git whatchanged master
+$ git whatchanged main
commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:03:00 2006 +0000
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index 52e3e476ff..9de7f73f42 100755
--- a/t/t4015-diff-whitespace.sh
+++ b/t/t4015-diff-whitespace.sh
@@ -11,12 +11,8 @@ test_description='Test special whitespace in diff engine.
. "$TEST_DIRECTORY"/lib-diff.sh
for opt_res in --patch --quiet -s --stat --shortstat --dirstat=lines \
- --raw! --name-only! --name-status!
+ --raw --name-only --name-status
do
- opts=${opt_res%!} expect_failure=
- test "$opts" = "$opt_res" ||
- expect_failure="test_expect_code 1"
-
test_expect_success "status with $opts (different)" '
echo foo >x &&
git add x &&
@@ -43,7 +39,7 @@ do
echo foo >x &&
git add x &&
echo " foo" >x &&
- $expect_failure git diff -w $opts --exit-code x
+ git diff -w $opts --exit-code x
'
done
diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh
index c8a23d5148..7ec5854f74 100755
--- a/t/t4020-diff-external.sh
+++ b/t/t4020-diff-external.sh
@@ -44,6 +44,16 @@ test_expect_success 'GIT_EXTERNAL_DIFF environment and --no-ext-diff' '
'
+test_expect_success 'GIT_EXTERNAL_DIFF and --output' '
+ cat >expect <<-EOF &&
+ file $(git rev-parse --verify HEAD:file) 100644 file $(test_oid zero) 100644
+ EOF
+ GIT_EXTERNAL_DIFF=echo git diff --output=out >stdout &&
+ cut -d" " -f1,3- <out >actual &&
+ test_must_be_empty stdout &&
+ test_cmp expect actual
+'
+
test_expect_success SYMLINKS 'typechange diff' '
rm -f file &&
ln -s elif file &&
diff --git a/t/t4035-diff-quiet.sh b/t/t4035-diff-quiet.sh
index 0352bf81a9..35eaf0855f 100755
--- a/t/t4035-diff-quiet.sh
+++ b/t/t4035-diff-quiet.sh
@@ -50,6 +50,10 @@ test_expect_success 'git diff-tree HEAD HEAD' '
test_expect_code 0 git diff-tree --quiet HEAD HEAD >cnt &&
test_line_count = 0 cnt
'
+test_expect_success 'git diff-tree -w HEAD^ HEAD' '
+ test_expect_code 1 git diff-tree --quiet -w HEAD^ HEAD >cnt &&
+ test_line_count = 0 cnt
+'
test_expect_success 'git diff-files' '
test_expect_code 0 git diff-files --quiet >cnt &&
test_line_count = 0 cnt
diff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh
index 01db9243ab..69599279e9 100755
--- a/t/t4053-diff-no-index.sh
+++ b/t/t4053-diff-no-index.sh
@@ -26,6 +26,23 @@ test_expect_success 'git diff --no-index directories' '
test_line_count = 14 cnt
'
+test_expect_success 'git diff --no-index with -' '
+ cat >expect <<-\EOF &&
+ diff --git a/- b/-
+ new file mode 100644
+ --- /dev/null
+ +++ b/-
+ @@ -0,0 +1 @@
+ +frotz
+ EOF
+ (
+ cd a &&
+ echo frotz |
+ test_expect_code 1 git diff --no-index /dev/null - >../actual
+ ) &&
+ test_cmp expect actual
+'
+
test_expect_success 'git diff --no-index relative path outside repo' '
(
cd repo &&
@@ -322,6 +339,22 @@ test_expect_success 'diff --no-index with pathspec' '
test_cmp expect actual
'
+test_expect_success 'diff --no-index first path ending in slash with pathspec' '
+ test_expect_code 1 git diff --name-status --no-index a/ b 1 >actual &&
+ cat >expect <<-EOF &&
+ D a/1
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'diff --no-index second path ending in slash with pathspec' '
+ test_expect_code 1 git diff --name-status --no-index a b/ 1 >actual &&
+ cat >expect <<-EOF &&
+ D a/1
+ EOF
+ test_cmp expect actual
+'
+
test_expect_success 'diff --no-index with pathspec no matches' '
test_expect_code 0 git diff --name-status --no-index a b missing
'
diff --git a/t/t4072-diff-max-depth.sh b/t/t4072-diff-max-depth.sh
new file mode 100755
index 0000000000..0fbf1321f7
--- /dev/null
+++ b/t/t4072-diff-max-depth.sh
@@ -0,0 +1,116 @@
+#!/bin/sh
+
+test_description='check that diff --max-depth will limit recursion'
+. ./test-lib.sh
+
+make_dir() {
+ mkdir -p "$1" &&
+ echo "$2" >"$1/file"
+}
+
+make_files() {
+ echo "$1" >file &&
+ make_dir one "$1" &&
+ make_dir one/two "$1" &&
+ make_dir one/two/three "$1"
+}
+
+test_expect_success 'setup' '
+ git commit --allow-empty -m empty &&
+ git tag empty &&
+ make_files added &&
+ git add . &&
+ git commit -m added &&
+ make_files modified &&
+ git add . &&
+ git commit -m modified &&
+ make_files index &&
+ git add . &&
+ make_files worktree
+'
+
+test_expect_success '--max-depth is disallowed with wildcard pathspecs' '
+ test_must_fail git diff-tree --max-depth=0 HEAD^ HEAD -- "f*"
+'
+
+check_one() {
+ type=$1; shift
+ args=$1; shift
+ path=$1; shift
+ depth=$1; shift
+ test_expect_${expect:-success} "diff-$type $args, path=$path, depth=$depth" "
+ for i in $*; do echo \$i; done >expect &&
+ git diff-$type --max-depth=$depth --name-only $args -- $path >actual &&
+ test_cmp expect actual
+ "
+}
+
+# For tree comparisons, we expect to see subtrees at the boundary
+# get their own entry.
+check_trees() {
+ check_one tree "$*" '' 0 file one
+ check_one tree "$*" '' 1 file one/file one/two
+ check_one tree "$*" '' 2 file one/file one/two/file one/two/three
+ check_one tree "$*" '' 3 file one/file one/two/file one/two/three/file
+ check_one tree "$*" '' -1 file one/file one/two/file one/two/three/file
+ check_one tree "$*" one 0 one
+ check_one tree "$*" one 1 one/file one/two
+ check_one tree "$*" one 2 one/file one/two/file one/two/three
+ check_one tree "$*" one 3 one/file one/two/file one/two/three/file
+ check_one tree "$*" one/two 0 one/two
+ check_one tree "$*" one/two 1 one/two/file one/two/three
+ check_one tree "$*" one/two 2 one/two/file one/two/three/file
+ check_one tree "$*" one/two 2 one/two/file one/two/three/file
+ check_one tree "$*" one/two/three 0 one/two/three
+ check_one tree "$*" one/two/three 1 one/two/three/file
+}
+
+# But for index comparisons, we do not store subtrees at all, so we do not
+# expect them.
+check_index() {
+ check_one "$@" '' 0 file
+ check_one "$@" '' 1 file one/file
+ check_one "$@" '' 2 file one/file one/two/file
+ check_one "$@" '' 3 file one/file one/two/file one/two/three/file
+ check_one "$@" one 0
+ check_one "$@" one 1 one/file
+ check_one "$@" one 2 one/file one/two/file
+ check_one "$@" one 3 one/file one/two/file one/two/three/file
+ check_one "$@" one/two 0
+ check_one "$@" one/two 1 one/two/file
+ check_one "$@" one/two 2 one/two/file one/two/three/file
+ check_one "$@" one/two/three 0
+ check_one "$@" one/two/three 1 one/two/three/file
+
+ # Value '-1' for '--max-depth is the same as recursion without limit,
+ # and thus should always succeed.
+ local expect=
+ check_one "$@" '' -1 file one/file one/two/file one/two/three/file
+}
+
+# Check as a modification...
+check_trees HEAD^ HEAD
+# ...and as an addition...
+check_trees empty HEAD
+# ...and as a deletion.
+check_trees HEAD empty
+
+# We currently only implement max-depth for trees.
+expect=failure
+# Check index against a tree
+check_index index "--cached HEAD"
+# and index against the worktree
+check_index files ""
+expect=
+
+test_expect_success 'find shortest path within embedded pathspecs' '
+ cat >expect <<-\EOF &&
+ one/file
+ one/two/file
+ one/two/three/file
+ EOF
+ git diff-tree --max-depth=2 --name-only HEAD^ HEAD -- one one/two >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh
index 950451cf6a..0a7c3ca42f 100755
--- a/t/t4211-line-log.sh
+++ b/t/t4211-line-log.sh
@@ -78,6 +78,8 @@ canned_test "-L :main:a.c -L 4,18:a.c simple" multiple-overlapping
canned_test "-L 4:a.c -L 8,12:a.c simple" multiple-superset
canned_test "-L 8,12:a.c -L 4:a.c simple" multiple-superset
+canned_test "-L 10,16:b.c -L 18,26:b.c main" no-assertion-error
+
test_bad_opts "-L" "switch.*requires a value"
test_bad_opts "-L b.c" "argument not .start,end:file"
test_bad_opts "-L 1:" "argument not .start,end:file"
diff --git a/t/t4211/sha1/expect.multiple b/t/t4211/sha1/expect.multiple
index 76ad5b598c..1eee8a7801 100644
--- a/t/t4211/sha1/expect.multiple
+++ b/t/t4211/sha1/expect.multiple
@@ -102,3 +102,9 @@ diff --git a/a.c b/a.c
+ s++;
+ }
+}
+@@ -0,0 +16,5 @@
++int main ()
++{
++ printf("%d\n", f(15));
++ return 0;
++}
diff --git a/t/t4211/sha1/expect.no-assertion-error b/t/t4211/sha1/expect.no-assertion-error
new file mode 100644
index 0000000000..994c37db1e
--- /dev/null
+++ b/t/t4211/sha1/expect.no-assertion-error
@@ -0,0 +1,90 @@
+commit 0d8dcfc6b968e06a27d5215bad1fdde3de9d6235
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:50:24 2013 +0100
+
+ move within the file
+
+diff --git a/b.c b/b.c
+--- a/b.c
++++ b/b.c
+@@ -25,0 +18,9 @@
++long f(long x)
++{
++ int s = 0;
++ while (x) {
++ x /= 2;
++ s++;
++ }
++ return s;
++}
+
+commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:48:43 2013 +0100
+
+ change back to complete line
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -18,5 +18,7 @@
+ int main ()
+ {
+ printf("%ld\n", f(15));
+ return 0;
+-}
+\ No newline at end of file
++}
++
++/* incomplete lines are bad! */
+
+commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:48:10 2013 +0100
+
+ change to an incomplete line at end
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -18,5 +18,5 @@
+ int main ()
+ {
+ printf("%ld\n", f(15));
+ return 0;
+-}
++}
+\ No newline at end of file
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:45:16 2013 +0100
+
+ touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -17,5 +17,5 @@
+ int main ()
+ {
+- printf("%d\n", f(15));
++ printf("%ld\n", f(15));
+ return 0;
+ }
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:44:48 2013 +0100
+
+ initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +16,5 @@
++int main ()
++{
++ printf("%d\n", f(15));
++ return 0;
++}
diff --git a/t/t4211/sha1/expect.two-ranges b/t/t4211/sha1/expect.two-ranges
index 6109aa0dce..c5164f3be3 100644
--- a/t/t4211/sha1/expect.two-ranges
+++ b/t/t4211/sha1/expect.two-ranges
@@ -100,3 +100,9 @@ diff --git a/a.c b/a.c
+ s++;
+ }
+}
+@@ -0,0 +16,5 @@
++int main ()
++{
++ printf("%d\n", f(15));
++ return 0;
++}
diff --git a/t/t4211/sha256/expect.multiple b/t/t4211/sha256/expect.multiple
index ca00409b9a..dbd987b74a 100644
--- a/t/t4211/sha256/expect.multiple
+++ b/t/t4211/sha256/expect.multiple
@@ -102,3 +102,9 @@ diff --git a/a.c b/a.c
+ s++;
+ }
+}
+@@ -0,0 +16,5 @@
++int main ()
++{
++ printf("%d\n", f(15));
++ return 0;
++}
diff --git a/t/t4211/sha256/expect.no-assertion-error b/t/t4211/sha256/expect.no-assertion-error
new file mode 100644
index 0000000000..36ed12aa9c
--- /dev/null
+++ b/t/t4211/sha256/expect.no-assertion-error
@@ -0,0 +1,90 @@
+commit eb871b8aa9aff323e484723039c9a92ab0266e060bc0ef2afb08fadda25c5ace
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:50:24 2013 +0100
+
+ move within the file
+
+diff --git a/b.c b/b.c
+--- a/b.c
++++ b/b.c
+@@ -25,0 +18,9 @@
++long f(long x)
++{
++ int s = 0;
++ while (x) {
++ x /= 2;
++ s++;
++ }
++ return s;
++}
+
+commit 5526ed05c2476b56af8b7be499e8f78bd50f490740733a9ca7e1f55878fa90a9
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:48:43 2013 +0100
+
+ change back to complete line
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -18,5 +18,7 @@
+ int main ()
+ {
+ printf("%ld\n", f(15));
+ return 0;
+-}
+\ No newline at end of file
++}
++
++/* incomplete lines are bad! */
+
+commit 29f32ac3141c48b22803e5c4127b719917b67d0f8ca8c5248bebfa2a19f7da10
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:48:10 2013 +0100
+
+ change to an incomplete line at end
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -18,5 +18,5 @@
+ int main ()
+ {
+ printf("%ld\n", f(15));
+ return 0;
+-}
++}
+\ No newline at end of file
+
+commit ccf97b9878189c40a981da50b15713bb80a35755326320ec80900caf22ced46f
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:45:16 2013 +0100
+
+ touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -17,5 +17,5 @@
+ int main ()
+ {
+- printf("%d\n", f(15));
++ printf("%ld\n", f(15));
+ return 0;
+ }
+
+commit 1dd7e9b2b1699324b53b341e728653b913bc192a14dfea168c5b51f2b3d03592
+Author: Thomas Rast <trast@student.ethz.ch>
+Date: Thu Feb 28 10:44:48 2013 +0100
+
+ initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +16,5 @@
++int main ()
++{
++ printf("%d\n", f(15));
++ return 0;
++}
diff --git a/t/t4211/sha256/expect.two-ranges b/t/t4211/sha256/expect.two-ranges
index af57c8b997..6a94d3b9cb 100644
--- a/t/t4211/sha256/expect.two-ranges
+++ b/t/t4211/sha256/expect.two-ranges
@@ -100,3 +100,9 @@ diff --git a/a.c b/a.c
+ s++;
+ }
+}
+@@ -0,0 +16,5 @@
++int main ()
++{
++ printf("%d\n", f(15));
++ return 0;
++}
diff --git a/t/t4216-log-bloom.sh b/t/t4216-log-bloom.sh
index 639868ac56..1064990de3 100755
--- a/t/t4216-log-bloom.sh
+++ b/t/t4216-log-bloom.sh
@@ -154,11 +154,34 @@ test_expect_success 'git log with multiple literal paths uses Bloom filter' '
test_bloom_filters_used "-- file*"
'
-test_expect_success 'git log with path contains a wildcard does not use Bloom filter' '
+test_expect_success 'git log with paths all contain non-wildcard part uses Bloom filter' '
+ test_bloom_filters_used "-- A/\* file4" &&
+ test_bloom_filters_used "-- A/file\*" &&
+ test_bloom_filters_used "-- * A/\*"
+'
+
+test_expect_success 'git log with path only contains wildcard part does not use Bloom filter' '
test_bloom_filters_not_used "-- file\*" &&
- test_bloom_filters_not_used "-- A/\* file4" &&
- test_bloom_filters_not_used "-- file4 A/\*" &&
- test_bloom_filters_not_used "-- * A/\*"
+ test_bloom_filters_not_used "-- file\* A/\*" &&
+ test_bloom_filters_not_used "-- file\* *" &&
+ test_bloom_filters_not_used "-- \*"
+'
+
+test_expect_success 'git log with path contains various magic signatures' '
+ cd A &&
+ test_bloom_filters_used "-- \:\(top\)B" &&
+ cd .. &&
+
+ test_bloom_filters_used "-- \:\(glob\)A/\*\*/C" &&
+ test_bloom_filters_not_used "-- \:\(icase\)FILE4" &&
+ test_bloom_filters_not_used "-- \:\(exclude\)A/B/C" &&
+
+ test_when_finished "rm -f .gitattributes" &&
+ cat >.gitattributes <<-EOF &&
+ A/file1 text
+ A/B/file2 -text
+ EOF
+ test_bloom_filters_used "-- \:\(attr\:text\)A"
'
test_expect_success 'setup - add commit-graph to the chain without Bloom filters' '
diff --git a/t/t5200-update-server-info.sh b/t/t5200-update-server-info.sh
index 8365907055..a551e955b5 100755
--- a/t/t5200-update-server-info.sh
+++ b/t/t5200-update-server-info.sh
@@ -46,4 +46,9 @@ test_expect_success 'midx does not create duplicate pack entries' '
test_must_be_empty dups
'
+test_expect_success 'update-server-info does not crash with -h' '
+ test_expect_code 129 git update-server-info -h >usage &&
+ test_grep "[Uu]sage: git update-server-info " usage
+'
+
test_done
diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh
index 1f1f664871..2be7cd30de 100755
--- a/t/t5304-prune.sh
+++ b/t/t5304-prune.sh
@@ -364,4 +364,9 @@ test_expect_success 'gc.recentObjectsHook' '
git cat-file -p $BLOB
'
+test_expect_success 'prune does not crash with -h' '
+ test_expect_code 129 git prune -h >usage &&
+ test_grep "[Uu]sage: git prune " usage
+'
+
test_done
diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh
index 0b3404f58f..98c6910963 100755
--- a/t/t5318-commit-graph.sh
+++ b/t/t5318-commit-graph.sh
@@ -946,4 +946,48 @@ test_expect_success 'stale commit cannot be parsed when traversing graph' '
)
'
+test_expect_success 'config commitGraph.changedPaths acts like --changed-paths' '
+ git init config-changed-paths &&
+ (
+ cd config-changed-paths &&
+
+ # commitGraph.changedPaths is not set and it should not write Bloom filters
+ test_commit first &&
+ GIT_PROGRESS_DELAY=0 git commit-graph write --reachable --progress 2>error &&
+ test_grep ! "Bloom filters" error &&
+
+ # Set commitGraph.changedPaths to true and it should write Bloom filters
+ test_commit second &&
+ git config commitGraph.changedPaths true &&
+ GIT_PROGRESS_DELAY=0 git commit-graph write --reachable --progress 2>error &&
+ test_grep "Bloom filters" error &&
+
+ # Add one more config commitGraph.changedPaths as false to disable the previous true config value
+ # It should still write Bloom filters due to existing filters
+ test_commit third &&
+ git config --add commitGraph.changedPaths false &&
+ GIT_PROGRESS_DELAY=0 git commit-graph write --reachable --progress 2>error &&
+ test_grep "Bloom filters" error &&
+
+ # commitGraph.changedPaths is still false and command line options should take precedence
+ test_commit fourth &&
+ GIT_PROGRESS_DELAY=0 git commit-graph write --no-changed-paths --reachable --progress 2>error &&
+ test_grep ! "Bloom filters" error &&
+ GIT_PROGRESS_DELAY=0 git commit-graph write --reachable --progress 2>error &&
+ test_grep ! "Bloom filters" error &&
+
+ # commitGraph.changedPaths is all cleared and then set to false again, command line options should take precedence
+ test_commit fifth &&
+ git config --unset-all commitGraph.changedPaths &&
+ git config commitGraph.changedPaths false &&
+ GIT_PROGRESS_DELAY=0 git commit-graph write --changed-paths --reachable --progress 2>error &&
+ test_grep "Bloom filters" error &&
+
+ # commitGraph.changedPaths is still false and it should write Bloom filters due to existing filters
+ test_commit sixth &&
+ GIT_PROGRESS_DELAY=0 git commit-graph write --reachable --progress 2>error &&
+ test_grep "Bloom filters" error
+ )
+'
+
test_done
diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh
index bd75dea950..93f319a4b2 100755
--- a/t/t5319-multi-pack-index.sh
+++ b/t/t5319-multi-pack-index.sh
@@ -28,11 +28,11 @@ midx_read_expect () {
EOF
if test $NUM_PACKS -ge 1
then
- ls $OBJECT_DIR/pack/ | grep idx | sort
+ ls "$OBJECT_DIR"/pack/ | grep idx | sort
fi &&
printf "object-dir: $OBJECT_DIR\n"
} >expect &&
- test-tool read-midx $OBJECT_DIR >actual &&
+ test-tool read-midx "$OBJECT_DIR" >actual &&
test_cmp expect actual
}
@@ -305,7 +305,7 @@ test_expect_success 'midx picks objects from preferred pack' '
ofs=$(git show-index <objects/pack/test-BC-$bc.idx | grep $b |
cut -d" " -f1) &&
- printf "%s %s\tobjects/pack/test-BC-%s.pack\n" \
+ printf "%s %s\t./objects/pack/test-BC-%s.pack\n" \
"$b" "$ofs" "$bc" >expect &&
grep ^$b out >actual &&
@@ -639,7 +639,7 @@ test_expect_success 'force some 64-bit offsets with pack-objects' '
( cd ../objects64 && pwd ) >.git/objects/info/alternates &&
midx64=$(git multi-pack-index --object-dir=../objects64 write)
) &&
- midx_read_expect 1 63 5 objects64 " large-offsets"
+ midx_read_expect 1 63 5 "$(pwd)/objects64" " large-offsets"
'
test_expect_success 'verify multi-pack-index with 64-bit offsets' '
@@ -989,6 +989,23 @@ test_expect_success 'repack --batch-size=0 repacks everything' '
)
'
+test_expect_success EXPENSIVE 'repack/expire with many packs' '
+ cp -r dup many &&
+ (
+ cd many &&
+
+ for i in $(test_seq 1 100)
+ do
+ test_commit extra$i &&
+ git maintenance run --task=loose-objects || return 1
+ done &&
+
+ git multi-pack-index write &&
+ git multi-pack-index repack &&
+ git multi-pack-index expire
+ )
+'
+
test_expect_success 'repack --batch-size=<large> repacks everything' '
(
cd dup2 &&
@@ -1083,7 +1100,10 @@ test_expect_success 'load reverse index when missing .idx, .pack' '
mv $idx.bak $idx &&
mv $pack $pack.bak &&
- git cat-file --batch-check="%(objectsize:disk)" <tip
+ git cat-file --batch-check="%(objectsize:disk)" <tip &&
+
+ test_must_fail git multi-pack-index write 2>err &&
+ test_grep "could not load pack" err
)
'
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index 2701eef85e..e592c0bcde 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -1658,4 +1658,77 @@ test_expect_success 'forbid adding superset of existing remote' '
test_grep ".outer. is a superset of existing remote .outer/inner." err
'
+test_expect_success 'rename handles unborn HEAD' '
+ test_when_finished "git remote remove unborn-renamed" &&
+ git remote add unborn url &&
+ git symbolic-ref refs/remotes/unborn/HEAD refs/remotes/unborn/nonexistent &&
+ git remote rename unborn unborn-renamed &&
+ git symbolic-ref refs/remotes/unborn-renamed/HEAD >actual &&
+ echo refs/remotes/unborn-renamed/nonexistent >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'rename can nest a remote into itself' '
+ test_commit parent-commit &&
+ COMMIT_ID=$(git rev-parse HEAD) &&
+ test_when_finished "git remote remove parent || true" &&
+ git remote add parent url &&
+ git update-ref refs/remotes/parent/branch $COMMIT_ID &&
+ test_when_finished "git remote remove parent/child" &&
+ git remote rename parent parent/child &&
+ git for-each-ref refs/remotes/ >actual &&
+ printf "$COMMIT_ID commit\trefs/remotes/parent/child/branch\n" >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'rename can nest a remote into itself with a conflicting branch name' '
+ test_commit parent-conflict &&
+ COMMIT_ID=$(git rev-parse HEAD) &&
+ test_when_finished "git remote remove parent || true" &&
+ git remote add parent url &&
+ git update-ref refs/remotes/parent/child $COMMIT_ID &&
+ test_when_finished "git remote remove parent/child" &&
+ test_must_fail git remote rename parent parent/child 2>err &&
+ test_grep "renaming remote references failed" err &&
+ test_grep "The remote you are trying to rename has conflicting references" err &&
+ git for-each-ref refs/remotes/ >actual &&
+ printf "$COMMIT_ID commit\trefs/remotes/parent/child\n" >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'rename can unnest a remote' '
+ test_commit parent-child-commit &&
+ COMMIT_ID=$(git rev-parse HEAD) &&
+ test_when_finished "git remote remove parent/child || true" &&
+ git remote add parent/child url &&
+ git update-ref refs/remotes/parent/child/branch $COMMIT_ID &&
+ git remote rename parent/child parent &&
+ git for-each-ref refs/remotes/ >actual &&
+ printf "$COMMIT_ID commit\trefs/remotes/parent/branch\n" >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'rename moves around the reflog' '
+ test_commit reflog-old &&
+ COMMIT_ID=$(git rev-parse HEAD) &&
+ test_config core.logAllRefUpdates true &&
+ test_when_finished "git remote remove reflog-old || true" &&
+ git remote add reflog-old url &&
+ git update-ref refs/remotes/reflog-old/branch $COMMIT_ID &&
+ test-tool ref-store main for-each-reflog >actual &&
+ test_grep refs/remotes/reflog-old/branch actual &&
+ test-tool ref-store main for-each-reflog-ent refs/remotes/reflog-old/branch >reflog-entries-old &&
+ test_line_count = 1 reflog-entries-old &&
+ git remote rename reflog-old reflog-new &&
+ test-tool ref-store main for-each-reflog >actual &&
+ test_grep ! refs/remotes/reflog-old actual &&
+ test_grep refs/remotes/reflog-new/branch actual &&
+ test-tool ref-store main for-each-reflog-ent refs/remotes/reflog-new/branch >reflog-entries-new &&
+ cat >expect <<-EOF &&
+ $(cat reflog-entries-old)
+ $COMMIT_ID $COMMIT_ID $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1112912173 -0700 remote: renamed refs/remotes/reflog-old/branch to refs/remotes/reflog-new/branch
+ EOF
+ test_cmp expect reflog-entries-new
+'
+
test_done
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index ebc696546b..b7059cccaa 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -14,8 +14,6 @@ then
test_done
fi
-D=$(pwd)
-
test_expect_success setup '
echo >file original &&
git add file &&
@@ -47,50 +45,72 @@ test_expect_success "clone and setup child repos" '
git config set branch.main.merge refs/heads/one
) &&
git clone . bundle &&
- git clone . seven
+ git clone . seven &&
+ git clone --ref-format=reftable . case_sensitive &&
+ (
+ cd case_sensitive &&
+ git branch branch1 &&
+ git branch bRanch1
+ ) &&
+ git clone --ref-format=reftable . case_sensitive_fd &&
+ (
+ cd case_sensitive_fd &&
+ git branch foo/bar &&
+ git branch Foo
+ ) &&
+ git clone --ref-format=reftable . case_sensitive_df &&
+ (
+ cd case_sensitive_df &&
+ git branch Foo/bar &&
+ git branch foo
+ )
'
test_expect_success "fetch test" '
- cd "$D" &&
echo >file updated by origin &&
git commit -a -m "updated by origin" &&
- cd two &&
- git fetch &&
- git rev-parse --verify refs/heads/one &&
- mine=$(git rev-parse refs/heads/one) &&
- his=$(cd ../one && git rev-parse refs/heads/main) &&
- test "z$mine" = "z$his"
+ (
+ cd two &&
+ git fetch &&
+ git rev-parse --verify refs/heads/one &&
+ mine=$(git rev-parse refs/heads/one) &&
+ his=$(cd ../one && git rev-parse refs/heads/main) &&
+ test "z$mine" = "z$his"
+ )
'
test_expect_success "fetch test for-merge" '
- cd "$D" &&
- cd three &&
- git fetch &&
- git rev-parse --verify refs/heads/two &&
- git rev-parse --verify refs/heads/one &&
- main_in_two=$(cd ../two && git rev-parse main) &&
- one_in_two=$(cd ../two && git rev-parse one) &&
- {
- echo "$one_in_two " &&
- echo "$main_in_two not-for-merge"
- } >expected &&
- cut -f -2 .git/FETCH_HEAD >actual &&
- test_cmp expected actual'
+ (
+ cd three &&
+ git fetch &&
+ git rev-parse --verify refs/heads/two &&
+ git rev-parse --verify refs/heads/one &&
+ main_in_two=$(cd ../two && git rev-parse main) &&
+ one_in_two=$(cd ../two && git rev-parse one) &&
+ {
+ echo "$one_in_two " &&
+ echo "$main_in_two not-for-merge"
+ } >expected &&
+ cut -f -2 .git/FETCH_HEAD >actual &&
+ test_cmp expected actual
+ )
+'
test_expect_success "fetch test remote HEAD" '
- cd "$D" &&
- cd two &&
- git fetch &&
- git rev-parse --verify refs/remotes/origin/HEAD &&
- git rev-parse --verify refs/remotes/origin/main &&
- head=$(git rev-parse refs/remotes/origin/HEAD) &&
- branch=$(git rev-parse refs/remotes/origin/main) &&
- test "z$head" = "z$branch"'
+ (
+ cd two &&
+ git fetch &&
+ git rev-parse --verify refs/remotes/origin/HEAD &&
+ git rev-parse --verify refs/remotes/origin/main &&
+ head=$(git rev-parse refs/remotes/origin/HEAD) &&
+ branch=$(git rev-parse refs/remotes/origin/main) &&
+ test "z$head" = "z$branch"
+ )
+'
test_expect_success "fetch test remote HEAD in bare repository" '
test_when_finished rm -rf barerepo &&
(
- cd "$D" &&
git init --bare barerepo &&
cd barerepo &&
git remote add upstream ../two &&
@@ -105,262 +125,235 @@ test_expect_success "fetch test remote HEAD in bare repository" '
test_expect_success "fetch test remote HEAD change" '
- cd "$D" &&
- cd two &&
- git switch -c other &&
- git push -u origin other &&
- git rev-parse --verify refs/remotes/origin/HEAD &&
- git rev-parse --verify refs/remotes/origin/main &&
- git rev-parse --verify refs/remotes/origin/other &&
- git remote set-head origin other &&
- git fetch &&
- head=$(git rev-parse refs/remotes/origin/HEAD) &&
- branch=$(git rev-parse refs/remotes/origin/other) &&
- test "z$head" = "z$branch"'
-
-test_expect_success "fetch test followRemoteHEAD never" '
- test_when_finished "git config unset remote.origin.followRemoteHEAD" &&
- (
- cd "$D" &&
- cd two &&
- git update-ref --no-deref -d refs/remotes/origin/HEAD &&
- git config set remote.origin.followRemoteHEAD "never" &&
- GIT_TRACE_PACKET=$PWD/trace.out git fetch &&
- # Confirm that we do not even ask for HEAD when we are
- # not going to act on it.
- test_grep ! "ref-prefix HEAD" trace.out &&
- test_must_fail git rev-parse --verify refs/remotes/origin/HEAD
- )
-'
-
-test_expect_success "fetch test followRemoteHEAD warn no change" '
- test_when_finished "git config unset remote.origin.followRemoteHEAD" &&
(
- cd "$D" &&
cd two &&
- git rev-parse --verify refs/remotes/origin/other &&
- git remote set-head origin other &&
+ git switch -c other &&
+ git push -u origin other &&
git rev-parse --verify refs/remotes/origin/HEAD &&
git rev-parse --verify refs/remotes/origin/main &&
- git config set remote.origin.followRemoteHEAD "warn" &&
- git fetch >output &&
- echo "${SQ}HEAD${SQ} at ${SQ}origin${SQ} is ${SQ}main${SQ}," \
- "but we have ${SQ}other${SQ} locally." >expect &&
- test_cmp expect output &&
+ git rev-parse --verify refs/remotes/origin/other &&
+ git remote set-head origin other &&
+ git fetch &&
head=$(git rev-parse refs/remotes/origin/HEAD) &&
branch=$(git rev-parse refs/remotes/origin/other) &&
test "z$head" = "z$branch"
)
'
+test_expect_success "fetch test followRemoteHEAD never" '
+ git -C two update-ref --no-deref -d refs/remotes/origin/HEAD &&
+ test_config -C two remote.origin.followRemoteHEAD "never" &&
+ GIT_TRACE_PACKET=$PWD/trace.out git -C two fetch &&
+ # Confirm that we do not even ask for HEAD when we are
+ # not going to act on it.
+ test_grep ! "ref-prefix HEAD" trace.out &&
+ test_must_fail git -C two rev-parse --verify refs/remotes/origin/HEAD
+'
+
+test_expect_success "fetch test followRemoteHEAD warn no change" '
+ git -C two rev-parse --verify refs/remotes/origin/other &&
+ git -C two remote set-head origin other &&
+ git -C two rev-parse --verify refs/remotes/origin/HEAD &&
+ git -C two rev-parse --verify refs/remotes/origin/main &&
+ test_config -C two remote.origin.followRemoteHEAD "warn" &&
+ git -C two fetch >output &&
+ echo "${SQ}HEAD${SQ} at ${SQ}origin${SQ} is ${SQ}main${SQ}," \
+ "but we have ${SQ}other${SQ} locally." >expect &&
+ test_cmp expect output &&
+ head=$(git -C two rev-parse refs/remotes/origin/HEAD) &&
+ branch=$(git -C two rev-parse refs/remotes/origin/other) &&
+ test "z$head" = "z$branch"
+'
+
test_expect_success "fetch test followRemoteHEAD warn create" '
- test_when_finished "git config unset remote.origin.followRemoteHEAD" &&
- (
- cd "$D" &&
- cd two &&
- git update-ref --no-deref -d refs/remotes/origin/HEAD &&
- git config set remote.origin.followRemoteHEAD "warn" &&
- git rev-parse --verify refs/remotes/origin/main &&
- output=$(git fetch) &&
- test "z" = "z$output" &&
- head=$(git rev-parse refs/remotes/origin/HEAD) &&
- branch=$(git rev-parse refs/remotes/origin/main) &&
- test "z$head" = "z$branch"
- )
+ git -C two update-ref --no-deref -d refs/remotes/origin/HEAD &&
+ test_config -C two remote.origin.followRemoteHEAD "warn" &&
+ git -C two rev-parse --verify refs/remotes/origin/main &&
+ output=$(git -C two fetch) &&
+ test "z" = "z$output" &&
+ head=$(git -C two rev-parse refs/remotes/origin/HEAD) &&
+ branch=$(git -C two rev-parse refs/remotes/origin/main) &&
+ test "z$head" = "z$branch"
'
test_expect_success "fetch test followRemoteHEAD warn detached" '
- test_when_finished "git config unset remote.origin.followRemoteHEAD" &&
- (
- cd "$D" &&
- cd two &&
- git update-ref --no-deref -d refs/remotes/origin/HEAD &&
- git update-ref refs/remotes/origin/HEAD HEAD &&
- HEAD=$(git log --pretty="%H") &&
- git config set remote.origin.followRemoteHEAD "warn" &&
- git fetch >output &&
- echo "${SQ}HEAD${SQ} at ${SQ}origin${SQ} is ${SQ}main${SQ}," \
- "but we have a detached HEAD pointing to" \
- "${SQ}${HEAD}${SQ} locally." >expect &&
- test_cmp expect output
- )
+ git -C two update-ref --no-deref -d refs/remotes/origin/HEAD &&
+ git -C two update-ref refs/remotes/origin/HEAD HEAD &&
+ HEAD=$(git -C two log --pretty="%H") &&
+ test_config -C two remote.origin.followRemoteHEAD "warn" &&
+ git -C two fetch >output &&
+ echo "${SQ}HEAD${SQ} at ${SQ}origin${SQ} is ${SQ}main${SQ}," \
+ "but we have a detached HEAD pointing to" \
+ "${SQ}${HEAD}${SQ} locally." >expect &&
+ test_cmp expect output
'
test_expect_success "fetch test followRemoteHEAD warn quiet" '
- test_when_finished "git config unset remote.origin.followRemoteHEAD" &&
- (
- cd "$D" &&
- cd two &&
- git rev-parse --verify refs/remotes/origin/other &&
- git remote set-head origin other &&
- git rev-parse --verify refs/remotes/origin/HEAD &&
- git rev-parse --verify refs/remotes/origin/main &&
- git config set remote.origin.followRemoteHEAD "warn" &&
- output=$(git fetch --quiet) &&
- test "z" = "z$output" &&
- head=$(git rev-parse refs/remotes/origin/HEAD) &&
- branch=$(git rev-parse refs/remotes/origin/other) &&
- test "z$head" = "z$branch"
- )
+ git -C two rev-parse --verify refs/remotes/origin/other &&
+ git -C two remote set-head origin other &&
+ git -C two rev-parse --verify refs/remotes/origin/HEAD &&
+ git -C two rev-parse --verify refs/remotes/origin/main &&
+ test_config -C two remote.origin.followRemoteHEAD "warn" &&
+ output=$(git -C two fetch --quiet) &&
+ test "z" = "z$output" &&
+ head=$(git -C two rev-parse refs/remotes/origin/HEAD) &&
+ branch=$(git -C two rev-parse refs/remotes/origin/other) &&
+ test "z$head" = "z$branch"
'
test_expect_success "fetch test followRemoteHEAD warn-if-not-branch branch is same" '
- test_when_finished "git config unset remote.origin.followRemoteHEAD" &&
- (
- cd "$D" &&
- cd two &&
- git rev-parse --verify refs/remotes/origin/other &&
- git remote set-head origin other &&
- git rev-parse --verify refs/remotes/origin/HEAD &&
- git rev-parse --verify refs/remotes/origin/main &&
- git config set remote.origin.followRemoteHEAD "warn-if-not-main" &&
- actual=$(git fetch) &&
- test "z" = "z$actual" &&
- head=$(git rev-parse refs/remotes/origin/HEAD) &&
- branch=$(git rev-parse refs/remotes/origin/other) &&
- test "z$head" = "z$branch"
- )
+ git -C two rev-parse --verify refs/remotes/origin/other &&
+ git -C two remote set-head origin other &&
+ git -C two rev-parse --verify refs/remotes/origin/HEAD &&
+ git -C two rev-parse --verify refs/remotes/origin/main &&
+ test_config -C two remote.origin.followRemoteHEAD "warn-if-not-main" &&
+ actual=$(git -C two fetch) &&
+ test "z" = "z$actual" &&
+ head=$(git -C two rev-parse refs/remotes/origin/HEAD) &&
+ branch=$(git -C two rev-parse refs/remotes/origin/other) &&
+ test "z$head" = "z$branch"
'
test_expect_success "fetch test followRemoteHEAD warn-if-not-branch branch is different" '
- test_when_finished "git config unset remote.origin.followRemoteHEAD" &&
- (
- cd "$D" &&
- cd two &&
- git rev-parse --verify refs/remotes/origin/other &&
- git remote set-head origin other &&
- git rev-parse --verify refs/remotes/origin/HEAD &&
- git rev-parse --verify refs/remotes/origin/main &&
- git config set remote.origin.followRemoteHEAD "warn-if-not-some/different-branch" &&
- git fetch >actual &&
- echo "${SQ}HEAD${SQ} at ${SQ}origin${SQ} is ${SQ}main${SQ}," \
- "but we have ${SQ}other${SQ} locally." >expect &&
- test_cmp expect actual &&
- head=$(git rev-parse refs/remotes/origin/HEAD) &&
- branch=$(git rev-parse refs/remotes/origin/other) &&
- test "z$head" = "z$branch"
- )
+ git -C two rev-parse --verify refs/remotes/origin/other &&
+ git -C two remote set-head origin other &&
+ git -C two rev-parse --verify refs/remotes/origin/HEAD &&
+ git -C two rev-parse --verify refs/remotes/origin/main &&
+ test_config -C two remote.origin.followRemoteHEAD "warn-if-not-some/different-branch" &&
+ git -C two fetch >actual &&
+ echo "${SQ}HEAD${SQ} at ${SQ}origin${SQ} is ${SQ}main${SQ}," \
+ "but we have ${SQ}other${SQ} locally." >expect &&
+ test_cmp expect actual &&
+ head=$(git -C two rev-parse refs/remotes/origin/HEAD) &&
+ branch=$(git -C two rev-parse refs/remotes/origin/other) &&
+ test "z$head" = "z$branch"
'
test_expect_success "fetch test followRemoteHEAD always" '
- test_when_finished "git config unset remote.origin.followRemoteHEAD" &&
- (
- cd "$D" &&
- cd two &&
- git rev-parse --verify refs/remotes/origin/other &&
- git remote set-head origin other &&
- git rev-parse --verify refs/remotes/origin/HEAD &&
- git rev-parse --verify refs/remotes/origin/main &&
- git config set remote.origin.followRemoteHEAD "always" &&
- git fetch &&
- head=$(git rev-parse refs/remotes/origin/HEAD) &&
- branch=$(git rev-parse refs/remotes/origin/main) &&
- test "z$head" = "z$branch"
- )
+ git -C two rev-parse --verify refs/remotes/origin/other &&
+ git -C two remote set-head origin other &&
+ git -C two rev-parse --verify refs/remotes/origin/HEAD &&
+ git -C two rev-parse --verify refs/remotes/origin/main &&
+ test_config -C two remote.origin.followRemoteHEAD "always" &&
+ git -C two fetch &&
+ head=$(git -C two rev-parse refs/remotes/origin/HEAD) &&
+ branch=$(git -C two rev-parse refs/remotes/origin/main) &&
+ test "z$head" = "z$branch"
'
test_expect_success 'followRemoteHEAD does not kick in with refspecs' '
- test_when_finished "git config unset remote.origin.followRemoteHEAD" &&
- (
- cd "$D" &&
- cd two &&
- git remote set-head origin other &&
- git config set remote.origin.followRemoteHEAD always &&
- git fetch origin refs/heads/main:refs/remotes/origin/main &&
- echo refs/remotes/origin/other >expect &&
- git symbolic-ref refs/remotes/origin/HEAD >actual &&
- test_cmp expect actual
- )
+ git -C two remote set-head origin other &&
+ test_config -C two remote.origin.followRemoteHEAD always &&
+ git -C two fetch origin refs/heads/main:refs/remotes/origin/main &&
+ echo refs/remotes/origin/other >expect &&
+ git -C two symbolic-ref refs/remotes/origin/HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'followRemoteHEAD create does not overwrite dangling symref' '
+ git -C two remote add -m does-not-exist custom-head ../one &&
+ test_config -C two remote.custom-head.followRemoteHEAD create &&
+ git -C two fetch custom-head &&
+ echo refs/remotes/custom-head/does-not-exist >expect &&
+ git -C two symbolic-ref refs/remotes/custom-head/HEAD >actual &&
+ test_cmp expect actual
'
test_expect_success 'fetch --prune on its own works as expected' '
- cd "$D" &&
git clone . prune &&
- cd prune &&
- git update-ref refs/remotes/origin/extrabranch main &&
+ (
+ cd prune &&
+ git update-ref refs/remotes/origin/extrabranch main &&
- git fetch --prune origin &&
- test_must_fail git rev-parse origin/extrabranch
+ git fetch --prune origin &&
+ test_must_fail git rev-parse origin/extrabranch
+ )
'
test_expect_success 'fetch --prune with a branch name keeps branches' '
- cd "$D" &&
git clone . prune-branch &&
- cd prune-branch &&
- git update-ref refs/remotes/origin/extrabranch main &&
+ (
+ cd prune-branch &&
+ git update-ref refs/remotes/origin/extrabranch main &&
- git fetch --prune origin main &&
- git rev-parse origin/extrabranch
+ git fetch --prune origin main &&
+ git rev-parse origin/extrabranch
+ )
'
test_expect_success 'fetch --prune with a namespace keeps other namespaces' '
- cd "$D" &&
git clone . prune-namespace &&
- cd prune-namespace &&
+ (
+ cd prune-namespace &&
- git fetch --prune origin refs/heads/a/*:refs/remotes/origin/a/* &&
- git rev-parse origin/main
+ git fetch --prune origin refs/heads/a/*:refs/remotes/origin/a/* &&
+ git rev-parse origin/main
+ )
'
test_expect_success 'fetch --prune handles overlapping refspecs' '
- cd "$D" &&
git update-ref refs/pull/42/head main &&
git clone . prune-overlapping &&
- cd prune-overlapping &&
- git config --add remote.origin.fetch refs/pull/*/head:refs/remotes/origin/pr/* &&
+ (
+ cd prune-overlapping &&
+ git config --add remote.origin.fetch refs/pull/*/head:refs/remotes/origin/pr/* &&
- git fetch --prune origin &&
- git rev-parse origin/main &&
- git rev-parse origin/pr/42 &&
+ git fetch --prune origin &&
+ git rev-parse origin/main &&
+ git rev-parse origin/pr/42 &&
- git config --unset-all remote.origin.fetch &&
- git config remote.origin.fetch refs/pull/*/head:refs/remotes/origin/pr/* &&
- git config --add remote.origin.fetch refs/heads/*:refs/remotes/origin/* &&
+ git config --unset-all remote.origin.fetch &&
+ git config remote.origin.fetch refs/pull/*/head:refs/remotes/origin/pr/* &&
+ git config --add remote.origin.fetch refs/heads/*:refs/remotes/origin/* &&
- git fetch --prune origin &&
- git rev-parse origin/main &&
- git rev-parse origin/pr/42
+ git fetch --prune origin &&
+ git rev-parse origin/main &&
+ git rev-parse origin/pr/42
+ )
'
test_expect_success 'fetch --prune --tags prunes branches but not tags' '
- cd "$D" &&
git clone . prune-tags &&
- cd prune-tags &&
- git tag sometag main &&
- # Create what looks like a remote-tracking branch from an earlier
- # fetch that has since been deleted from the remote:
- git update-ref refs/remotes/origin/fake-remote main &&
-
- git fetch --prune --tags origin &&
- git rev-parse origin/main &&
- test_must_fail git rev-parse origin/fake-remote &&
- git rev-parse sometag
+ (
+ cd prune-tags &&
+ git tag sometag main &&
+ # Create what looks like a remote-tracking branch from an earlier
+ # fetch that has since been deleted from the remote:
+ git update-ref refs/remotes/origin/fake-remote main &&
+
+ git fetch --prune --tags origin &&
+ git rev-parse origin/main &&
+ test_must_fail git rev-parse origin/fake-remote &&
+ git rev-parse sometag
+ )
'
test_expect_success 'fetch --prune --tags with branch does not prune other things' '
- cd "$D" &&
git clone . prune-tags-branch &&
- cd prune-tags-branch &&
- git tag sometag main &&
- git update-ref refs/remotes/origin/extrabranch main &&
+ (
+ cd prune-tags-branch &&
+ git tag sometag main &&
+ git update-ref refs/remotes/origin/extrabranch main &&
- git fetch --prune --tags origin main &&
- git rev-parse origin/extrabranch &&
- git rev-parse sometag
+ git fetch --prune --tags origin main &&
+ git rev-parse origin/extrabranch &&
+ git rev-parse sometag
+ )
'
test_expect_success 'fetch --prune --tags with refspec prunes based on refspec' '
- cd "$D" &&
git clone . prune-tags-refspec &&
- cd prune-tags-refspec &&
- git tag sometag main &&
- git update-ref refs/remotes/origin/foo/otherbranch main &&
- git update-ref refs/remotes/origin/extrabranch main &&
-
- git fetch --prune --tags origin refs/heads/foo/*:refs/remotes/origin/foo/* &&
- test_must_fail git rev-parse refs/remotes/origin/foo/otherbranch &&
- git rev-parse origin/extrabranch &&
- git rev-parse sometag
+ (
+ cd prune-tags-refspec &&
+ git tag sometag main &&
+ git update-ref refs/remotes/origin/foo/otherbranch main &&
+ git update-ref refs/remotes/origin/extrabranch main &&
+
+ git fetch --prune --tags origin refs/heads/foo/*:refs/remotes/origin/foo/* &&
+ test_must_fail git rev-parse refs/remotes/origin/foo/otherbranch &&
+ git rev-parse origin/extrabranch &&
+ git rev-parse sometag
+ )
'
test_expect_success 'fetch --tags gets tags even without a configured remote' '
@@ -381,21 +374,21 @@ test_expect_success 'fetch --tags gets tags even without a configured remote' '
'
test_expect_success REFFILES 'fetch --prune fails to delete branches' '
- cd "$D" &&
git clone . prune-fail &&
- cd prune-fail &&
- git update-ref refs/remotes/origin/extrabranch main &&
- git pack-refs --all &&
- : this will prevent --prune from locking packed-refs for deleting refs, but adding loose refs still succeeds &&
- >.git/packed-refs.new &&
+ (
+ cd prune-fail &&
+ git update-ref refs/remotes/origin/extrabranch main &&
+ git pack-refs --all &&
+ : this will prevent --prune from locking packed-refs for deleting refs, but adding loose refs still succeeds &&
+ >.git/packed-refs.new &&
- test_must_fail git fetch --prune origin
+ test_must_fail git fetch --prune origin
+ )
'
test_expect_success 'fetch --atomic works with a single branch' '
- test_when_finished "rm -rf \"$D\"/atomic" &&
+ test_when_finished "rm -rf atomic" &&
- cd "$D" &&
git clone . atomic &&
git branch atomic-branch &&
oid=$(git rev-parse atomic-branch) &&
@@ -408,9 +401,8 @@ test_expect_success 'fetch --atomic works with a single branch' '
'
test_expect_success 'fetch --atomic works with multiple branches' '
- test_when_finished "rm -rf \"$D\"/atomic" &&
+ test_when_finished "rm -rf atomic" &&
- cd "$D" &&
git clone . atomic &&
git branch atomic-branch-1 &&
git branch atomic-branch-2 &&
@@ -423,9 +415,8 @@ test_expect_success 'fetch --atomic works with multiple branches' '
'
test_expect_success 'fetch --atomic works with mixed branches and tags' '
- test_when_finished "rm -rf \"$D\"/atomic" &&
+ test_when_finished "rm -rf atomic" &&
- cd "$D" &&
git clone . atomic &&
git branch atomic-mixed-branch &&
git tag atomic-mixed-tag &&
@@ -437,9 +428,8 @@ test_expect_success 'fetch --atomic works with mixed branches and tags' '
'
test_expect_success 'fetch --atomic prunes references' '
- test_when_finished "rm -rf \"$D\"/atomic" &&
+ test_when_finished "rm -rf atomic" &&
- cd "$D" &&
git branch atomic-prune-delete &&
git clone . atomic &&
git branch --delete atomic-prune-delete &&
@@ -453,9 +443,8 @@ test_expect_success 'fetch --atomic prunes references' '
'
test_expect_success 'fetch --atomic aborts with non-fast-forward update' '
- test_when_finished "rm -rf \"$D\"/atomic" &&
+ test_when_finished "rm -rf atomic" &&
- cd "$D" &&
git branch atomic-non-ff &&
git clone . atomic &&
git rev-parse HEAD >actual &&
@@ -472,9 +461,8 @@ test_expect_success 'fetch --atomic aborts with non-fast-forward update' '
'
test_expect_success 'fetch --atomic executes a single reference transaction only' '
- test_when_finished "rm -rf \"$D\"/atomic" &&
+ test_when_finished "rm -rf atomic" &&
- cd "$D" &&
git clone . atomic &&
git branch atomic-hooks-1 &&
git branch atomic-hooks-2 &&
@@ -499,9 +487,8 @@ test_expect_success 'fetch --atomic executes a single reference transaction only
'
test_expect_success 'fetch --atomic aborts all reference updates if hook aborts' '
- test_when_finished "rm -rf \"$D\"/atomic" &&
+ test_when_finished "rm -rf atomic" &&
- cd "$D" &&
git clone . atomic &&
git branch atomic-hooks-abort-1 &&
git branch atomic-hooks-abort-2 &&
@@ -536,9 +523,8 @@ test_expect_success 'fetch --atomic aborts all reference updates if hook aborts'
'
test_expect_success 'fetch --atomic --append appends to FETCH_HEAD' '
- test_when_finished "rm -rf \"$D\"/atomic" &&
+ test_when_finished "rm -rf atomic" &&
- cd "$D" &&
git clone . atomic &&
oid=$(git rev-parse HEAD) &&
@@ -574,8 +560,7 @@ test_expect_success REFFILES 'fetch --atomic fails transaction if reference lock
'
test_expect_success '--refmap="" ignores configured refspec' '
- cd "$TRASH_DIRECTORY" &&
- git clone "$D" remote-refs &&
+ git clone . remote-refs &&
git -C remote-refs rev-parse remotes/origin/main >old &&
git -C remote-refs update-ref refs/remotes/origin/main main~1 &&
git -C remote-refs rev-parse remotes/origin/main >new &&
@@ -599,34 +584,26 @@ test_expect_success '--refmap="" and --prune' '
test_expect_success 'fetch tags when there is no tags' '
- cd "$D" &&
-
- mkdir notags &&
- cd notags &&
- git init &&
-
- git fetch -t ..
+ git init notags &&
+ git -C notags fetch -t ..
'
test_expect_success 'fetch following tags' '
- cd "$D" &&
git tag -a -m "annotated" anno HEAD &&
git tag light HEAD &&
- mkdir four &&
- cd four &&
- git init &&
-
- git fetch .. :track &&
- git show-ref --verify refs/tags/anno &&
- git show-ref --verify refs/tags/light
-
+ git init four &&
+ (
+ cd four &&
+ git fetch .. :track &&
+ git show-ref --verify refs/tags/anno &&
+ git show-ref --verify refs/tags/light
+ )
'
test_expect_success 'fetch uses remote ref names to describe new refs' '
- cd "$D" &&
git init descriptive &&
(
cd descriptive &&
@@ -654,30 +631,20 @@ test_expect_success 'fetch uses remote ref names to describe new refs' '
test_expect_success 'fetch must not resolve short tag name' '
- cd "$D" &&
-
- mkdir five &&
- cd five &&
- git init &&
-
- test_must_fail git fetch .. anno:five
+ git init five &&
+ test_must_fail git -C five fetch .. anno:five
'
test_expect_success 'fetch can now resolve short remote name' '
- cd "$D" &&
git update-ref refs/remotes/six/HEAD HEAD &&
- mkdir six &&
- cd six &&
- git init &&
-
- git fetch .. six:six
+ git init six &&
+ git -C six fetch .. six:six
'
test_expect_success 'create bundle 1' '
- cd "$D" &&
echo >file updated again by origin &&
git commit -a -m "tip" &&
git bundle create --version=3 bundle1 main^..main
@@ -691,35 +658,36 @@ test_expect_success 'header of bundle looks right' '
OID refs/heads/main
EOF
- sed -e "s/$OID_REGEX/OID/g" -e "5q" "$D"/bundle1 >actual &&
+ sed -e "s/$OID_REGEX/OID/g" -e "5q" bundle1 >actual &&
test_cmp expect actual
'
test_expect_success 'create bundle 2' '
- cd "$D" &&
git bundle create bundle2 main~2..main
'
test_expect_success 'unbundle 1' '
- cd "$D/bundle" &&
- git checkout -b some-branch &&
- test_must_fail git fetch "$D/bundle1" main:main
+ (
+ cd bundle &&
+ git checkout -b some-branch &&
+ test_must_fail git fetch bundle1 main:main
+ )
'
test_expect_success 'bundle 1 has only 3 files ' '
- cd "$D" &&
test_bundle_object_count bundle1 3
'
test_expect_success 'unbundle 2' '
- cd "$D/bundle" &&
- git fetch ../bundle2 main:main &&
- test "tip" = "$(git log -1 --pretty=oneline main | cut -d" " -f2)"
+ (
+ cd bundle &&
+ git fetch ../bundle2 main:main &&
+ test "tip" = "$(git log -1 --pretty=oneline main | cut -d" " -f2)"
+ )
'
test_expect_success 'bundle does not prerequisite objects' '
- cd "$D" &&
touch file2 &&
git add file2 &&
git commit -m add.file2 file2 &&
@@ -729,7 +697,6 @@ test_expect_success 'bundle does not prerequisite objects' '
test_expect_success 'bundle should be able to create a full history' '
- cd "$D" &&
git tag -a -m "1.0" v1.0 main &&
git bundle create bundle4 v1.0
@@ -783,7 +750,6 @@ test_expect_success 'quoting of a strangely named repo' '
test_expect_success 'bundle should record HEAD correctly' '
- cd "$D" &&
git bundle create bundle5 HEAD main &&
git bundle list-heads bundle5 >actual &&
for h in HEAD refs/heads/main
@@ -803,7 +769,6 @@ test_expect_success 'mark initial state of origin/main' '
test_expect_success 'explicit fetch should update tracking' '
- cd "$D" &&
git branch -f side &&
(
cd three &&
@@ -818,7 +783,6 @@ test_expect_success 'explicit fetch should update tracking' '
test_expect_success 'explicit pull should update tracking' '
- cd "$D" &&
git branch -f side &&
(
cd three &&
@@ -832,7 +796,6 @@ test_expect_success 'explicit pull should update tracking' '
'
test_expect_success 'explicit --refmap is allowed only with command-line refspec' '
- cd "$D" &&
(
cd three &&
test_must_fail git fetch --refmap="*:refs/remotes/none/*"
@@ -840,7 +803,6 @@ test_expect_success 'explicit --refmap is allowed only with command-line refspec
'
test_expect_success 'explicit --refmap option overrides remote.*.fetch' '
- cd "$D" &&
git branch -f side &&
(
cd three &&
@@ -855,7 +817,6 @@ test_expect_success 'explicit --refmap option overrides remote.*.fetch' '
'
test_expect_success 'explicitly empty --refmap option disables remote.*.fetch' '
- cd "$D" &&
git branch -f side &&
(
cd three &&
@@ -870,7 +831,6 @@ test_expect_success 'explicitly empty --refmap option disables remote.*.fetch' '
test_expect_success 'configured fetch updates tracking' '
- cd "$D" &&
git branch -f side &&
(
cd three &&
@@ -884,7 +844,6 @@ test_expect_success 'configured fetch updates tracking' '
'
test_expect_success 'non-matching refspecs do not confuse tracking update' '
- cd "$D" &&
git update-ref refs/odd/location HEAD &&
(
cd three &&
@@ -901,14 +860,12 @@ test_expect_success 'non-matching refspecs do not confuse tracking update' '
test_expect_success 'pushing nonexistent branch by mistake should not segv' '
- cd "$D" &&
test_must_fail git push seven no:no
'
test_expect_success 'auto tag following fetches minimum' '
- cd "$D" &&
git clone .git follow &&
git checkout HEAD^0 &&
(
@@ -1307,7 +1264,7 @@ test_expect_success 'fetch --prune prints the remotes url' '
cd only-prunes &&
git fetch --prune origin 2>&1 | head -n1 >../actual
) &&
- echo "From ${D}/." >expect &&
+ echo "From $(pwd)/." >expect &&
test_cmp expect actual
'
@@ -1357,14 +1314,14 @@ test_expect_success 'fetching with auto-gc does not lock up' '
echo "$*" &&
false
EOF
- git clone "file://$D" auto-gc &&
+ git clone "file://$PWD" auto-gc &&
test_commit test2 &&
(
cd auto-gc &&
git config fetch.unpackLimit 1 &&
git config gc.autoPackLimit 1 &&
git config gc.autoDetach false &&
- GIT_ASK_YESNO="$D/askyesno" git fetch --verbose >fetch.out 2>&1 &&
+ GIT_ASK_YESNO="$TRASH_DIRECTORY/askyesno" git fetch --verbose >fetch.out 2>&1 &&
test_grep "Auto packing the repository" fetch.out &&
! grep "Should I try again" fetch.out
)
@@ -1526,6 +1483,100 @@ test_expect_success SYMLINKS 'clone does not get confused by a D/F conflict' '
test_path_is_missing whoops
'
+test_expect_success CASE_INSENSITIVE_FS,REFFILES 'existing references in a case insensitive filesystem' '
+ test_when_finished rm -rf case_insensitive &&
+ (
+ git init --bare case_insensitive &&
+ cd case_insensitive &&
+ git remote add origin -- ../case_sensitive &&
+ test_must_fail git fetch -f origin "refs/heads/*:refs/heads/*" 2>err &&
+ test_grep "You${SQ}re on a case-insensitive filesystem" err &&
+ git rev-parse refs/heads/main >expect &&
+ git rev-parse refs/heads/branch1 >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success REFFILES 'existing reference lock in repo' '
+ test_when_finished rm -rf base repo &&
+ (
+ git init --ref-format=reftable base &&
+ cd base &&
+ echo >file update &&
+ git add . &&
+ git commit -m "updated" &&
+ git branch -M main &&
+
+ git update-ref refs/heads/foo @ &&
+ git update-ref refs/heads/branch @ &&
+ cd .. &&
+
+ git init --ref-format=files --bare repo &&
+ cd repo &&
+ git remote add origin ../base &&
+ touch refs/heads/foo.lock &&
+ test_must_fail git fetch -f origin "refs/heads/*:refs/heads/*" 2>err &&
+ test_grep "error: fetching ref refs/heads/foo failed: reference already exists" err &&
+ git rev-parse refs/heads/main >expect &&
+ git rev-parse refs/heads/branch >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success CASE_INSENSITIVE_FS,REFFILES 'F/D conflict on case insensitive filesystem' '
+ test_when_finished rm -rf case_insensitive &&
+ (
+ git init --bare case_insensitive &&
+ cd case_insensitive &&
+ git remote add origin -- ../case_sensitive_fd &&
+ test_must_fail git fetch -f origin "refs/heads/*:refs/heads/*" 2>err &&
+ test_grep "failed: refname conflict" err &&
+ git rev-parse refs/heads/main >expect &&
+ git rev-parse refs/heads/foo/bar >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success CASE_INSENSITIVE_FS,REFFILES 'D/F conflict on case insensitive filesystem' '
+ test_when_finished rm -rf case_insensitive &&
+ (
+ git init --bare case_insensitive &&
+ cd case_insensitive &&
+ git remote add origin -- ../case_sensitive_df &&
+ test_must_fail git fetch -f origin "refs/heads/*:refs/heads/*" 2>err &&
+ test_grep "failed: refname conflict" err &&
+ git rev-parse refs/heads/main >expect &&
+ git rev-parse refs/heads/Foo/bar >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success REFFILES 'D/F conflict on case sensitive filesystem with lock' '
+ (
+ git init --ref-format=reftable base &&
+ cd base &&
+ echo >file update &&
+ git add . &&
+ git commit -m "updated" &&
+ git branch -M main &&
+
+ git update-ref refs/heads/foo @ &&
+ git update-ref refs/heads/branch @ &&
+ cd .. &&
+
+ git init --ref-format=files --bare repo &&
+ cd repo &&
+ git remote add origin ../base &&
+ mkdir refs/heads/foo &&
+ touch refs/heads/foo/random.lock &&
+ test_must_fail git fetch origin "refs/heads/*:refs/heads/*" 2>err &&
+ test_grep "some local refs could not be updated; try running" err &&
+ git rev-parse refs/heads/main >expect &&
+ git rev-parse refs/heads/branch >actual &&
+ test_cmp expect actual
+ )
+'
+
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 4e9c27b0f2..46926e7bbd 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -105,7 +105,6 @@ check_push_result () {
}
test_expect_success setup '
-
>path1 &&
git add path1 &&
test_tick &&
@@ -117,7 +116,6 @@ test_expect_success setup '
test_tick &&
git commit -a -m second &&
the_commit=$(git show-ref -s --verify refs/heads/main)
-
'
for cmd in push fetch
@@ -322,104 +320,82 @@ test_expect_success 'push with pushInsteadOf and explicit pushurl (pushInsteadOf
'
test_expect_success 'push with matching heads' '
-
mk_test testrepo heads/main &&
git push testrepo : &&
check_push_result testrepo $the_commit heads/main
-
'
test_expect_success 'push with matching heads on the command line' '
-
mk_test testrepo heads/main &&
git push testrepo : &&
check_push_result testrepo $the_commit heads/main
-
'
test_expect_success 'failed (non-fast-forward) push with matching heads' '
-
mk_test testrepo heads/main &&
git push testrepo : &&
git commit --amend -massaged &&
test_must_fail git push testrepo &&
check_push_result testrepo $the_commit heads/main &&
git reset --hard $the_commit
-
'
test_expect_success 'push --force with matching heads' '
-
mk_test testrepo heads/main &&
git push testrepo : &&
git commit --amend -massaged &&
git push --force testrepo : &&
! check_push_result testrepo $the_commit heads/main &&
git reset --hard $the_commit
-
'
test_expect_success 'push with matching heads and forced update' '
-
mk_test testrepo heads/main &&
git push testrepo : &&
git commit --amend -massaged &&
git push testrepo +: &&
! check_push_result testrepo $the_commit heads/main &&
git reset --hard $the_commit
-
'
test_expect_success 'push with no ambiguity (1)' '
-
mk_test testrepo heads/main &&
git push testrepo main:main &&
check_push_result testrepo $the_commit heads/main
-
'
test_expect_success 'push with no ambiguity (2)' '
-
mk_test testrepo remotes/origin/main &&
git push testrepo main:origin/main &&
check_push_result testrepo $the_commit remotes/origin/main
-
'
test_expect_success 'push with colon-less refspec, no ambiguity' '
-
mk_test testrepo heads/main heads/t/main &&
git branch -f t/main main &&
git push testrepo main &&
check_push_result testrepo $the_commit heads/main &&
check_push_result testrepo $the_first_commit heads/t/main
-
'
test_expect_success 'push with weak ambiguity (1)' '
-
mk_test testrepo heads/main remotes/origin/main &&
git push testrepo main:main &&
check_push_result testrepo $the_commit heads/main &&
check_push_result testrepo $the_first_commit remotes/origin/main
-
'
test_expect_success 'push with weak ambiguity (2)' '
-
mk_test testrepo heads/main remotes/origin/main remotes/another/main &&
git push testrepo main:main &&
check_push_result testrepo $the_commit heads/main &&
check_push_result testrepo $the_first_commit remotes/origin/main remotes/another/main
-
'
test_expect_success 'push with ambiguity' '
-
mk_test testrepo heads/frotz tags/frotz &&
test_must_fail git push testrepo main:frotz &&
check_push_result testrepo $the_first_commit heads/frotz tags/frotz
-
'
test_expect_success 'push with onelevel ref' '
@@ -428,17 +404,14 @@ test_expect_success 'push with onelevel ref' '
'
test_expect_success 'push with colon-less refspec (1)' '
-
mk_test testrepo heads/frotz tags/frotz &&
git branch -f frotz main &&
git push testrepo frotz &&
check_push_result testrepo $the_commit heads/frotz &&
check_push_result testrepo $the_first_commit tags/frotz
-
'
test_expect_success 'push with colon-less refspec (2)' '
-
mk_test testrepo heads/frotz tags/frotz &&
if git show-ref --verify -q refs/heads/frotz
then
@@ -448,7 +421,6 @@ test_expect_success 'push with colon-less refspec (2)' '
git push -f testrepo frotz &&
check_push_result testrepo $the_commit tags/frotz &&
check_push_result testrepo $the_first_commit heads/frotz
-
'
test_expect_success 'push with colon-less refspec (3)' '
@@ -465,7 +437,6 @@ test_expect_success 'push with colon-less refspec (3)' '
'
test_expect_success 'push with colon-less refspec (4)' '
-
mk_test testrepo &&
if git show-ref --verify -q refs/heads/frotz
then
@@ -475,38 +446,34 @@ test_expect_success 'push with colon-less refspec (4)' '
git push testrepo frotz &&
check_push_result testrepo $the_commit tags/frotz &&
test 1 = $( cd testrepo && git show-ref | wc -l )
-
'
test_expect_success 'push head with non-existent, incomplete dest' '
-
mk_test testrepo &&
git push testrepo main:branch &&
check_push_result testrepo $the_commit heads/branch
-
'
test_expect_success 'push tag with non-existent, incomplete dest' '
-
mk_test testrepo &&
git tag -f v1.0 &&
git push testrepo v1.0:tag &&
check_push_result testrepo $the_commit tags/tag
-
'
test_expect_success 'push oid with non-existent, incomplete dest' '
-
mk_test testrepo &&
test_must_fail git push testrepo $(git rev-parse main):foo
-
'
test_expect_success 'push ref expression with non-existent, incomplete dest' '
-
mk_test testrepo &&
test_must_fail git push testrepo main^:branch
+'
+test_expect_success 'push ref expression with non-existent oid src' '
+ mk_test testrepo &&
+ test_must_fail git push testrepo $(test_oid 001):branch
'
for head in HEAD @
@@ -550,7 +517,6 @@ do
git checkout main &&
git push testrepo $head:branch &&
check_push_result testrepo $the_commit heads/branch
-
'
test_expect_success "push with config remote.*.push = $head" '
@@ -596,7 +562,6 @@ test_expect_success 'push with remote.pushdefault' '
'
test_expect_success 'push with config remote.*.pushurl' '
-
mk_test testrepo heads/main &&
git checkout main &&
test_config remote.there.url test2repo &&
@@ -655,7 +620,6 @@ test_expect_success 'push ignores "branch." config without subsection' '
'
test_expect_success 'push with dry-run' '
-
mk_test testrepo heads/main &&
old_commit=$(git -C testrepo show-ref -s --verify refs/heads/main) &&
git push --dry-run testrepo : &&
@@ -663,7 +627,6 @@ test_expect_success 'push with dry-run' '
'
test_expect_success 'push updates local refs' '
-
mk_test testrepo heads/main &&
mk_child testrepo child &&
(
@@ -673,11 +636,9 @@ test_expect_success 'push updates local refs' '
test $(git rev-parse main) = \
$(git rev-parse remotes/origin/main)
)
-
'
test_expect_success 'push updates up-to-date local refs' '
-
mk_test testrepo heads/main &&
mk_child testrepo child1 &&
mk_child testrepo child2 &&
@@ -689,11 +650,9 @@ test_expect_success 'push updates up-to-date local refs' '
test $(git rev-parse main) = \
$(git rev-parse remotes/origin/main)
)
-
'
test_expect_success 'push preserves up-to-date packed refs' '
-
mk_test testrepo heads/main &&
mk_child testrepo child &&
(
@@ -701,11 +660,9 @@ test_expect_success 'push preserves up-to-date packed refs' '
git push &&
! test -f .git/refs/remotes/origin/main
)
-
'
test_expect_success 'push does not update local refs on failure' '
-
mk_test testrepo heads/main &&
mk_child testrepo child &&
echo "#!/no/frobnication/today" >testrepo/.git/hooks/pre-receive &&
@@ -717,16 +674,13 @@ test_expect_success 'push does not update local refs on failure' '
test $(git rev-parse main) != \
$(git rev-parse remotes/origin/main)
)
-
'
test_expect_success 'allow deleting an invalid remote ref' '
-
mk_test testrepo heads/branch &&
rm -f testrepo/.git/objects/??/* &&
git push testrepo :refs/heads/branch &&
(cd testrepo && test_must_fail git rev-parse --verify refs/heads/branch)
-
'
test_expect_success 'pushing valid refs triggers post-receive and post-update hooks' '
diff --git a/t/t5530-upload-pack-error.sh b/t/t5530-upload-pack-error.sh
index 558eedf25a..d40292cfb7 100755
--- a/t/t5530-upload-pack-error.sh
+++ b/t/t5530-upload-pack-error.sh
@@ -4,8 +4,6 @@ test_description='errors in upload-pack'
. ./test-lib.sh
-D=$(pwd)
-
corrupt_repo () {
object_sha1=$(git rev-parse "$1") &&
ob=$(expr "$object_sha1" : "\(..\)") &&
@@ -21,11 +19,7 @@ test_expect_success 'setup and corrupt repository' '
test_tick &&
echo changed >file &&
git commit -a -m changed &&
- corrupt_repo HEAD:file
-
-'
-
-test_expect_success 'fsck fails' '
+ corrupt_repo HEAD:file &&
test_must_fail git fsck
'
@@ -40,17 +34,12 @@ test_expect_success 'upload-pack fails due to error in pack-objects packing' '
'
test_expect_success 'corrupt repo differently' '
-
git hash-object -w file &&
- corrupt_repo HEAD^^{tree}
-
-'
-
-test_expect_success 'fsck fails' '
+ corrupt_repo HEAD^^{tree} &&
test_must_fail git fsck
'
-test_expect_success 'upload-pack fails due to error in rev-list' '
+test_expect_success 'upload-pack fails due to error in rev-list' '
printf "%04xwant %s\n%04xshallow %s00000009done\n0000" \
$(($hexsz + 10)) $(git rev-parse HEAD) \
$(($hexsz + 12)) $(git rev-parse HEAD^) >input &&
@@ -59,7 +48,6 @@ test_expect_success 'upload-pack fails due to error in rev-list' '
'
test_expect_success 'upload-pack fails due to bad want (no object)' '
-
printf "%04xwant %s multi_ack_detailed\n00000009done\n0000" \
$(($hexsz + 29)) $(test_oid deadbeef) >input &&
test_must_fail git upload-pack . <input >output 2>output.err &&
@@ -69,7 +57,6 @@ test_expect_success 'upload-pack fails due to bad want (no object)' '
'
test_expect_success 'upload-pack fails due to bad want (not tip)' '
-
oid=$(echo an object we have | git hash-object -w --stdin) &&
printf "%04xwant %s multi_ack_detailed\n00000009done\n0000" \
$(($hexsz + 29)) "$oid" >input &&
@@ -80,7 +67,6 @@ test_expect_success 'upload-pack fails due to bad want (not tip)' '
'
test_expect_success 'upload-pack fails due to error in pack-objects enumeration' '
-
printf "%04xwant %s\n00000009done\n0000" \
$((hexsz + 10)) $(git rev-parse HEAD) >input &&
test_must_fail git upload-pack . <input >/dev/null 2>output.err &&
@@ -105,18 +91,48 @@ test_expect_success 'upload-pack tolerates EOF just after stateless client wants
test_cmp expect actual
'
-test_expect_success 'create empty repository' '
-
- mkdir foo &&
- cd foo &&
- git init
-
-'
-
test_expect_success 'fetch fails' '
+ git init foo &&
+ test_must_fail git -C foo fetch .. main
+'
- test_must_fail git fetch .. main
+test_expect_success 'upload-pack ACKs repeated non-commit objects repeatedly (protocol v0)' '
+ commit_id=$(git rev-parse HEAD) &&
+ tree_id=$(git rev-parse HEAD^{tree}) &&
+ test-tool pkt-line pack >request <<-EOF &&
+ want $commit_id
+ 0000
+ have $tree_id
+ have $tree_id
+ 0000
+ EOF
+ git upload-pack --stateless-rpc . <request >actual &&
+ depacketize <actual >actual.raw &&
+ grep ^ACK actual.raw >actual.acks &&
+ cat >expect <<-EOF &&
+ ACK $tree_id
+ ACK $tree_id
+ EOF
+ test_cmp expect actual.acks
+'
+test_expect_success 'upload-pack ACKs repeated non-commit objects once only (protocol v2)' '
+ commit_id=$(git rev-parse HEAD) &&
+ tree_id=$(git rev-parse HEAD^{tree}) &&
+ test-tool pkt-line pack >request <<-EOF &&
+ command=fetch
+ object-format=$(test_oid algo)
+ 0001
+ want $commit_id
+ have $tree_id
+ have $tree_id
+ 0000
+ EOF
+ GIT_PROTOCOL=version=2 git upload-pack . <request >actual &&
+ depacketize <actual >actual.raw &&
+ grep ^ACK actual.raw >actual.acks &&
+ echo "ACK $tree_id" >expect &&
+ test_cmp expect actual.acks
'
test_done
diff --git a/t/t5564-http-proxy.sh b/t/t5564-http-proxy.sh
index b27e481f95..c3903faf2d 100755
--- a/t/t5564-http-proxy.sh
+++ b/t/t5564-http-proxy.sh
@@ -72,7 +72,9 @@ test_expect_success SOCKS_PROXY 'clone via Unix socket' '
test_when_finished "rm -rf clone" &&
test_config_global http.proxy "socks4://localhost$PWD/%2530.sock" && {
{
- GIT_TRACE_CURL=$PWD/trace git clone "$HTTPD_URL/smart/repo.git" clone 2>err &&
+ GIT_TRACE_CURL=$PWD/trace \
+ GIT_TRACE_CURL_COMPONENTS=socks \
+ git clone "$HTTPD_URL/smart/repo.git" clone 2>err &&
grep -i "SOCKS4 request granted" trace
} ||
old_libcurl_error err
diff --git a/t/t5710-promisor-remote-capability.sh b/t/t5710-promisor-remote-capability.sh
index cb061b1f35..023735d6a8 100755
--- a/t/t5710-promisor-remote-capability.sh
+++ b/t/t5710-promisor-remote-capability.sh
@@ -295,6 +295,71 @@ test_expect_success "clone with 'KnownUrl' and empty url, so not advertised" '
check_missing_objects server 1 "$oid"
'
+test_expect_success "clone with promisor.sendFields" '
+ git -C server config promisor.advertise true &&
+ test_when_finished "rm -rf client" &&
+
+ git -C server remote add otherLop "https://invalid.invalid" &&
+ git -C server config remote.otherLop.token "fooBar" &&
+ git -C server config remote.otherLop.stuff "baz" &&
+ git -C server config remote.otherLop.partialCloneFilter "blob:limit=10k" &&
+ test_when_finished "git -C server remote remove otherLop" &&
+ test_config -C server promisor.sendFields "partialCloneFilter, token" &&
+ test_when_finished "rm trace" &&
+
+ # Clone from server to create a client
+ GIT_TRACE_PACKET="$(pwd)/trace" GIT_NO_LAZY_FETCH=0 git clone \
+ -c remote.lop.promisor=true \
+ -c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
+ -c remote.lop.url="file://$(pwd)/lop" \
+ -c promisor.acceptfromserver=All \
+ --no-local --filter="blob:limit=5k" server client &&
+
+ # Check that fields are properly transmitted
+ ENCODED_URL=$(echo "file://$(pwd)/lop" | sed -e "s/ /%20/g") &&
+ PR1="name=lop,url=$ENCODED_URL,partialCloneFilter=blob:none" &&
+ PR2="name=otherLop,url=https://invalid.invalid,partialCloneFilter=blob:limit=10k,token=fooBar" &&
+ test_grep "clone< promisor-remote=$PR1;$PR2" trace &&
+ test_grep "clone> promisor-remote=lop;otherLop" trace &&
+
+ # Check that the largest object is still missing on the server
+ check_missing_objects server 1 "$oid"
+'
+
+test_expect_success "clone with promisor.checkFields" '
+ git -C server config promisor.advertise true &&
+ test_when_finished "rm -rf client" &&
+
+ git -C server remote add otherLop "https://invalid.invalid" &&
+ git -C server config remote.otherLop.token "fooBar" &&
+ git -C server config remote.otherLop.stuff "baz" &&
+ git -C server config remote.otherLop.partialCloneFilter "blob:limit=10k" &&
+ test_when_finished "git -C server remote remove otherLop" &&
+ test_config -C server promisor.sendFields "partialCloneFilter, token" &&
+ test_when_finished "rm trace" &&
+
+ # Clone from server to create a client
+ GIT_TRACE_PACKET="$(pwd)/trace" GIT_NO_LAZY_FETCH=0 git clone \
+ -c remote.lop.promisor=true \
+ -c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
+ -c remote.lop.url="file://$(pwd)/lop" \
+ -c remote.lop.partialCloneFilter="blob:none" \
+ -c promisor.acceptfromserver=All \
+ -c promisor.checkFields=partialcloneFilter \
+ --no-local --filter="blob:limit=5k" server client &&
+
+ # Check that fields are properly transmitted
+ ENCODED_URL=$(echo "file://$(pwd)/lop" | sed -e "s/ /%20/g") &&
+ PR1="name=lop,url=$ENCODED_URL,partialCloneFilter=blob:none" &&
+ PR2="name=otherLop,url=https://invalid.invalid,partialCloneFilter=blob:limit=10k,token=fooBar" &&
+ test_grep "clone< promisor-remote=$PR1;$PR2" trace &&
+ test_grep "clone> promisor-remote=lop" trace &&
+ test_grep ! "clone> promisor-remote=lop;otherLop" trace &&
+
+ # Check that the largest object is still missing on the server
+ check_missing_objects server 1 "$oid"
+'
+
test_expect_success "clone with promisor.advertise set to 'true' but don't delete the client" '
git -C server config promisor.advertise true &&
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index 256ccaefb7..2c70cc561a 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -409,6 +409,36 @@ test_expect_success 'describe tag object' '
test_grep "fatal: test-blob-1 is neither a commit nor blob" actual
'
+test_expect_success 'describe an unreachable blob' '
+ blob=$(echo not-found-anywhere | git hash-object -w --stdin) &&
+ test_must_fail git describe $blob 2>actual &&
+ test_grep "blob .$blob. not reachable from HEAD" actual
+'
+
+test_expect_success 'describe blob on an unborn branch' '
+ oldbranch=$(git symbolic-ref HEAD) &&
+ test_when_finished "git symbolic-ref HEAD $oldbranch" &&
+ git symbolic-ref HEAD refs/heads/does-not-exist &&
+ test_must_fail git describe test-blob 2>actual &&
+ test_grep "cannot search .* on an unborn branch" actual
+'
+
+# This test creates a repository state that we generally try to disallow: HEAD
+# is pointing to an object that is not a commit. The ref update code forbids
+# non-commit writes directly to HEAD or to any branch in refs/heads/. But we
+# can use the loophole of pointing HEAD to another non-branch ref (something we
+# should forbid, but don't for historical reasons).
+#
+# Do not take this test as an endorsement of the loophole! If we ever tighten
+# it, it is reasonable to just drop this test entirely.
+test_expect_success 'describe blob on a non-commit HEAD' '
+ oldbranch=$(git symbolic-ref HEAD) &&
+ test_when_finished "git symbolic-ref HEAD $oldbranch" &&
+ git symbolic-ref HEAD refs/tags/test-blob &&
+ test_must_fail git describe test-blob 2>actual &&
+ test_grep "blob .* not reachable from HEAD" actual
+'
+
test_expect_success ULIMIT_STACK_SIZE 'name-rev works in a deep repo' '
i=1 &&
while test $i -lt 8000
diff --git a/t/t6137-pathspec-wildcards-literal.sh b/t/t6137-pathspec-wildcards-literal.sh
index 20abad5667..17a03085ef 100755
--- a/t/t6137-pathspec-wildcards-literal.sh
+++ b/t/t6137-pathspec-wildcards-literal.sh
@@ -3,8 +3,8 @@ test_description='test wildcards and literals with git add/commit (subshell styl
. ./test-lib.sh
-test_have_prereq FUNNYNAMES || {
- skip_all='skipping: needs FUNNYNAMES (non-Windows only)'
+test_have_prereq BSLASHPSPEC || {
+ skip_all='skipping: needs BSLASHPSPEC (backslashes in pathspecs)'
test_done
}
@@ -184,7 +184,7 @@ test_expect_success 'add wildcard f?z' '
)
'
-test_expect_success 'add literal \? literal' '
+test_expect_success 'add literal \?' '
git init test-q-lit &&
(
cd test-q-lit &&
@@ -241,7 +241,7 @@ test_expect_success 'add literal hello\?world' '
)
'
-test_expect_success 'add literal [abc]' '
+test_expect_success 'add literal \[abc\]' '
git init test-brackets-lit &&
(
cd test-brackets-lit &&
@@ -280,7 +280,7 @@ test_expect_success 'commit: wildcard *' '
)
'
-test_expect_success 'commit: literal *' '
+test_expect_success 'commit: literal \*' '
git init test-c-asterisk-lit &&
(
cd test-c-asterisk-lit &&
@@ -328,7 +328,7 @@ test_expect_success 'commit: literal f\*' '
)
'
-test_expect_success 'commit: wildcard pathspec limits commit' '
+test_expect_success 'commit: wildcard f**' '
git init test-c-pathlimit &&
(
cd test-c-pathlimit &&
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index ce9af79ab1..1d9809114d 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -6,2150 +6,14 @@
test_description='for-each-ref test'
. ./test-lib.sh
-GNUPGHOME_NOT_USED=$GNUPGHOME
-. "$TEST_DIRECTORY"/lib-gpg.sh
-. "$TEST_DIRECTORY"/lib-terminal.sh
-# Mon Jul 3 23:18:43 2006 +0000
-datestamp=1151968723
-setdate_and_increment () {
- GIT_COMMITTER_DATE="$datestamp +0200"
- datestamp=$(expr "$datestamp" + 1)
- GIT_AUTHOR_DATE="$datestamp +0200"
- datestamp=$(expr "$datestamp" + 1)
- export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
-}
-
-test_object_file_size () {
- oid=$(git rev-parse "$1")
- path=".git/objects/$(test_oid_to_path $oid)"
- test_file_size "$path"
-}
-
-test_expect_success setup '
- # setup .mailmap
- cat >.mailmap <<-EOF &&
- A Thor <athor@example.com> A U Thor <author@example.com>
- C Mitter <cmitter@example.com> C O Mitter <committer@example.com>
- EOF
-
- setdate_and_increment &&
- echo "Using $datestamp" > one &&
- git add one &&
- git commit -m "Initial" &&
- git branch -M main &&
- setdate_and_increment &&
- git tag -a -m "Tagging at $datestamp" testtag &&
- git update-ref refs/remotes/origin/main main &&
- git remote add origin nowhere &&
- git config branch.main.remote origin &&
- git config branch.main.merge refs/heads/main &&
- git remote add myfork elsewhere &&
- git config remote.pushdefault myfork &&
- git config push.default current
-'
-
-test_atom () {
- case "$1" in
- head) ref=refs/heads/main ;;
- tag) ref=refs/tags/testtag ;;
- sym) ref=refs/heads/sym ;;
- *) ref=$1 ;;
- esac
- format=$2
- test_do=test_expect_${4:-success}
-
- printf '%s\n' "$3" >expected
- $test_do $PREREQ "basic atom: $ref $format" '
- git for-each-ref --format="%($format)" "$ref" >actual &&
- sanitize_pgp <actual >actual.clean &&
- test_cmp expected actual.clean
- '
-
- # Automatically test "contents:size" atom after testing "contents"
- if test "$format" = "contents"
- then
- # for commit leg, $3 is changed there
- expect=$(printf '%s' "$3" | wc -c)
- $test_do $PREREQ "basic atom: $ref contents:size" '
- type=$(git cat-file -t "$ref") &&
- case $type in
- tag)
- # We cannot use $3 as it expects sanitize_pgp to run
- git cat-file tag $ref >out &&
- expect=$(tail -n +6 out | wc -c) &&
- rm -f out ;;
- tree | blob)
- expect="" ;;
- commit)
- : "use the calculated expect" ;;
- *)
- BUG "unknown object type" ;;
- esac &&
- # Leave $expect unquoted to lose possible leading whitespaces
- echo $expect >expected &&
- git for-each-ref --format="%(contents:size)" "$ref" >actual &&
- test_cmp expected actual
- '
- fi
-}
-
-hexlen=$(test_oid hexsz)
-
-test_atom head refname refs/heads/main
-test_atom head refname: refs/heads/main
-test_atom head refname:short main
-test_atom head refname:lstrip=1 heads/main
-test_atom head refname:lstrip=2 main
-test_atom head refname:lstrip=-1 main
-test_atom head refname:lstrip=-2 heads/main
-test_atom head refname:rstrip=1 refs/heads
-test_atom head refname:rstrip=2 refs
-test_atom head refname:rstrip=-1 refs
-test_atom head refname:rstrip=-2 refs/heads
-test_atom head refname:strip=1 heads/main
-test_atom head refname:strip=2 main
-test_atom head refname:strip=-1 main
-test_atom head refname:strip=-2 heads/main
-test_atom head upstream refs/remotes/origin/main
-test_atom head upstream:short origin/main
-test_atom head upstream:lstrip=2 origin/main
-test_atom head upstream:lstrip=-2 origin/main
-test_atom head upstream:rstrip=2 refs/remotes
-test_atom head upstream:rstrip=-2 refs/remotes
-test_atom head upstream:strip=2 origin/main
-test_atom head upstream:strip=-2 origin/main
-test_atom head push refs/remotes/myfork/main
-test_atom head push:short myfork/main
-test_atom head push:lstrip=1 remotes/myfork/main
-test_atom head push:lstrip=-1 main
-test_atom head push:rstrip=1 refs/remotes/myfork
-test_atom head push:rstrip=-1 refs
-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 $(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)
-test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/main)
-test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/main)
-test_atom head tree $(git rev-parse refs/heads/main^{tree})
-test_atom head tree:short $(git rev-parse --short refs/heads/main^{tree})
-test_atom head tree:short=1 $(git rev-parse --short=1 refs/heads/main^{tree})
-test_atom head tree:short=10 $(git rev-parse --short=10 refs/heads/main^{tree})
-test_atom head parent ''
-test_atom head parent:short ''
-test_atom head parent:short=1 ''
-test_atom head parent:short=10 ''
-test_atom head numparent 0
-test_atom head object ''
-test_atom head type ''
-test_atom head raw "$(git cat-file commit refs/heads/main)
-"
-test_atom head '*objectname' ''
-test_atom head '*objecttype' ''
-test_atom head author 'A U Thor <author@example.com> 1151968724 +0200'
-test_atom head authorname 'A U Thor'
-test_atom head authorname:mailmap 'A Thor'
-test_atom head authoremail '<author@example.com>'
-test_atom head authoremail:trim 'author@example.com'
-test_atom head authoremail:localpart 'author'
-test_atom head authoremail:trim,localpart 'author'
-test_atom head authoremail:mailmap '<athor@example.com>'
-test_atom head authoremail:mailmap,trim 'athor@example.com'
-test_atom head authoremail:trim,mailmap 'athor@example.com'
-test_atom head authoremail:mailmap,localpart 'athor'
-test_atom head authoremail:localpart,mailmap 'athor'
-test_atom head authoremail:mailmap,trim,localpart,mailmap,trim 'athor'
-test_atom head authordate 'Tue Jul 4 01:18:44 2006 +0200'
-test_atom head committer 'C O Mitter <committer@example.com> 1151968723 +0200'
-test_atom head committername 'C O Mitter'
-test_atom head committername:mailmap 'C Mitter'
-test_atom head committeremail '<committer@example.com>'
-test_atom head committeremail:trim 'committer@example.com'
-test_atom head committeremail:localpart 'committer'
-test_atom head committeremail:localpart,trim 'committer'
-test_atom head committeremail:mailmap '<cmitter@example.com>'
-test_atom head committeremail:mailmap,trim 'cmitter@example.com'
-test_atom head committeremail:trim,mailmap 'cmitter@example.com'
-test_atom head committeremail:mailmap,localpart 'cmitter'
-test_atom head committeremail:localpart,mailmap 'cmitter'
-test_atom head committeremail:trim,mailmap,trim,trim,localpart 'cmitter'
-test_atom head committerdate 'Tue Jul 4 01:18:43 2006 +0200'
-test_atom head tag ''
-test_atom head tagger ''
-test_atom head taggername ''
-test_atom head taggeremail ''
-test_atom head taggeremail:trim ''
-test_atom head taggeremail:localpart ''
-test_atom head taggerdate ''
-test_atom head creator 'C O Mitter <committer@example.com> 1151968723 +0200'
-test_atom head creatordate 'Tue Jul 4 01:18:43 2006 +0200'
-test_atom head subject 'Initial'
-test_atom head subject:sanitize 'Initial'
-test_atom head contents:subject 'Initial'
-test_atom head body ''
-test_atom head contents:body ''
-test_atom head contents:signature ''
-test_atom head contents 'Initial
-'
-test_atom head HEAD '*'
-
-test_atom tag refname refs/tags/testtag
-test_atom tag refname:short testtag
-test_atom tag upstream ''
-test_atom tag push ''
-test_atom tag objecttype tag
-test_atom tag objectsize $((114 + hexlen))
-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)
-test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag)
-test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/main)
-test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/main)
-test_atom tag tree ''
-test_atom tag tree:short ''
-test_atom tag tree:short=1 ''
-test_atom tag tree:short=10 ''
-test_atom tag parent ''
-test_atom tag parent:short ''
-test_atom tag parent:short=1 ''
-test_atom tag parent:short=10 ''
-test_atom tag numparent ''
-test_atom tag object $(git rev-parse refs/tags/testtag^0)
-test_atom tag type 'commit'
-test_atom tag '*objectname' $(git rev-parse refs/tags/testtag^{})
-test_atom tag '*objecttype' 'commit'
-test_atom tag author ''
-test_atom tag authorname ''
-test_atom tag authorname:mailmap ''
-test_atom tag authoremail ''
-test_atom tag authoremail:trim ''
-test_atom tag authoremail:localpart ''
-test_atom tag authoremail:trim,localpart ''
-test_atom tag authoremail:mailmap ''
-test_atom tag authoremail:mailmap,trim ''
-test_atom tag authoremail:trim,mailmap ''
-test_atom tag authoremail:mailmap,localpart ''
-test_atom tag authoremail:localpart,mailmap ''
-test_atom tag authoremail:mailmap,trim,localpart,mailmap,trim ''
-test_atom tag authordate ''
-test_atom tag committer ''
-test_atom tag committername ''
-test_atom tag committername:mailmap ''
-test_atom tag committeremail ''
-test_atom tag committeremail:trim ''
-test_atom tag committeremail:localpart ''
-test_atom tag committeremail:localpart,trim ''
-test_atom tag committeremail:mailmap ''
-test_atom tag committeremail:mailmap,trim ''
-test_atom tag committeremail:trim,mailmap ''
-test_atom tag committeremail:mailmap,localpart ''
-test_atom tag committeremail:localpart,mailmap ''
-test_atom tag committeremail:trim,mailmap,trim,trim,localpart ''
-test_atom tag committerdate ''
-test_atom tag tag 'testtag'
-test_atom tag tagger 'C O Mitter <committer@example.com> 1151968725 +0200'
-test_atom tag taggername 'C O Mitter'
-test_atom tag taggername:mailmap 'C Mitter'
-test_atom tag taggeremail '<committer@example.com>'
-test_atom tag taggeremail:trim 'committer@example.com'
-test_atom tag taggeremail:localpart 'committer'
-test_atom tag taggeremail:trim,localpart 'committer'
-test_atom tag taggeremail:mailmap '<cmitter@example.com>'
-test_atom tag taggeremail:mailmap,trim 'cmitter@example.com'
-test_atom tag taggeremail:trim,mailmap 'cmitter@example.com'
-test_atom tag taggeremail:mailmap,localpart 'cmitter'
-test_atom tag taggeremail:localpart,mailmap 'cmitter'
-test_atom tag taggeremail:trim,mailmap,trim,localpart,localpart 'cmitter'
-test_atom tag taggerdate 'Tue Jul 4 01:18:45 2006 +0200'
-test_atom tag creator 'C O Mitter <committer@example.com> 1151968725 +0200'
-test_atom tag creatordate 'Tue Jul 4 01:18:45 2006 +0200'
-test_atom tag subject 'Tagging at 1151968727'
-test_atom tag subject:sanitize 'Tagging-at-1151968727'
-test_atom tag contents:subject 'Tagging at 1151968727'
-test_atom tag body ''
-test_atom tag contents:body ''
-test_atom tag contents:signature ''
-test_atom tag contents 'Tagging at 1151968727
-'
-test_atom tag HEAD ' '
-
-test_expect_success 'basic atom: refs/tags/testtag *raw' '
- git cat-file commit refs/tags/testtag^{} >expected &&
- git for-each-ref --format="%(*raw)" refs/tags/testtag >actual &&
- sanitize_pgp <expected >expected.clean &&
- echo >>expected.clean &&
- sanitize_pgp <actual >actual.clean &&
- test_cmp expected.clean actual.clean
-'
-
-test_expect_success 'Check invalid atoms names are errors' '
- test_must_fail git for-each-ref --format="%(INVALID)" refs/heads
-'
-
-test_expect_success 'for-each-ref does not crash with -h' '
+test_expect_success "for-each-ref does not crash with -h" '
test_expect_code 129 git for-each-ref -h >usage &&
test_grep "[Uu]sage: git for-each-ref " usage &&
test_expect_code 129 nongit git for-each-ref -h >usage &&
test_grep "[Uu]sage: git for-each-ref " usage
'
-test_expect_success 'Check format specifiers are ignored in naming date atoms' '
- git for-each-ref --format="%(authordate)" refs/heads &&
- git for-each-ref --format="%(authordate:default) %(authordate)" refs/heads &&
- git for-each-ref --format="%(authordate) %(authordate:default)" refs/heads &&
- git for-each-ref --format="%(authordate:default) %(authordate:default)" refs/heads
-'
-
-test_expect_success 'Check valid format specifiers for date fields' '
- git for-each-ref --format="%(authordate:default)" refs/heads &&
- git for-each-ref --format="%(authordate:relative)" refs/heads &&
- git for-each-ref --format="%(authordate:short)" refs/heads &&
- git for-each-ref --format="%(authordate:local)" refs/heads &&
- git for-each-ref --format="%(authordate:iso8601)" refs/heads &&
- git for-each-ref --format="%(authordate:rfc2822)" refs/heads
-'
-
-test_expect_success 'Check invalid format specifiers are errors' '
- test_must_fail git for-each-ref --format="%(authordate:INVALID)" refs/heads
-'
-
-test_expect_success 'arguments to %(objectname:short=) must be positive integers' '
- test_must_fail git for-each-ref --format="%(objectname:short=0)" &&
- test_must_fail git for-each-ref --format="%(objectname:short=-1)" &&
- test_must_fail git for-each-ref --format="%(objectname:short=foo)"
-'
-
-test_bad_atom () {
- case "$1" in
- head) ref=refs/heads/main ;;
- tag) ref=refs/tags/testtag ;;
- sym) ref=refs/heads/sym ;;
- *) ref=$1 ;;
- esac
- format=$2
- test_do=test_expect_${4:-success}
-
- printf '%s\n' "$3" >expect
- $test_do $PREREQ "err basic atom: $ref $format" '
- test_must_fail git for-each-ref \
- --format="%($format)" "$ref" 2>error &&
- test_cmp expect error
- '
-}
-
-test_bad_atom head 'authoremail:foo' \
- 'fatal: unrecognized %(authoremail) argument: foo'
-
-test_bad_atom head 'authoremail:mailmap,trim,bar' \
- 'fatal: unrecognized %(authoremail) argument: bar'
-
-test_bad_atom head 'authoremail:trim,' \
- 'fatal: unrecognized %(authoremail) argument: '
-
-test_bad_atom head 'authoremail:mailmaptrim' \
- 'fatal: unrecognized %(authoremail) argument: trim'
-
-test_bad_atom head 'committeremail: ' \
- 'fatal: unrecognized %(committeremail) argument: '
-
-test_bad_atom head 'committeremail: trim,foo' \
- 'fatal: unrecognized %(committeremail) argument: trim,foo'
-
-test_bad_atom head 'committeremail:mailmap,localpart ' \
- 'fatal: unrecognized %(committeremail) argument: '
-
-test_bad_atom head 'committeremail:trim_localpart' \
- 'fatal: unrecognized %(committeremail) argument: _localpart'
-
-test_bad_atom head 'committeremail:localpart,,,trim' \
- 'fatal: unrecognized %(committeremail) argument: ,,trim'
-
-test_bad_atom tag 'taggeremail:mailmap,trim, foo ' \
- 'fatal: unrecognized %(taggeremail) argument: foo '
-
-test_bad_atom tag 'taggeremail:trim,localpart,' \
- 'fatal: unrecognized %(taggeremail) argument: '
-
-test_bad_atom tag 'taggeremail:mailmap;localpart trim' \
- 'fatal: unrecognized %(taggeremail) argument: ;localpart trim'
-
-test_bad_atom tag 'taggeremail:localpart trim' \
- 'fatal: unrecognized %(taggeremail) argument: trim'
-
-test_bad_atom tag 'taggeremail:mailmap,mailmap,trim,qux,localpart,trim' \
- 'fatal: unrecognized %(taggeremail) argument: qux,localpart,trim'
-
-test_date () {
- f=$1 &&
- committer_date=$2 &&
- author_date=$3 &&
- tagger_date=$4 &&
- cat >expected <<-EOF &&
- 'refs/heads/main' '$committer_date' '$author_date'
- 'refs/tags/testtag' '$tagger_date'
- EOF
- (
- git for-each-ref --shell \
- --format="%(refname) %(committerdate${f:+:$f}) %(authordate${f:+:$f})" \
- refs/heads &&
- git for-each-ref --shell \
- --format="%(refname) %(taggerdate${f:+:$f})" \
- refs/tags
- ) >actual &&
- test_cmp expected actual
-}
-
-test_expect_success 'Check unformatted date fields output' '
- test_date "" \
- "Tue Jul 4 01:18:43 2006 +0200" \
- "Tue Jul 4 01:18:44 2006 +0200" \
- "Tue Jul 4 01:18:45 2006 +0200"
-'
-
-test_expect_success 'Check format "default" formatted date fields output' '
- test_date default \
- "Tue Jul 4 01:18:43 2006 +0200" \
- "Tue Jul 4 01:18:44 2006 +0200" \
- "Tue Jul 4 01:18:45 2006 +0200"
-'
-
-test_expect_success 'Check format "default-local" date fields output' '
- test_date default-local "Mon Jul 3 23:18:43 2006" "Mon Jul 3 23:18:44 2006" "Mon Jul 3 23:18:45 2006"
-'
-
-# Don't know how to do relative check because I can't know when this script
-# is going to be run and can't fake the current time to git, and hence can't
-# provide expected output. Instead, I'll just make sure that "relative"
-# doesn't exit in error
-test_expect_success 'Check format "relative" date fields output' '
- f=relative &&
- (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
- git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual
-'
-
-# We just check that this is the same as "relative" for now.
-test_expect_success 'Check format "relative-local" date fields output' '
- test_date relative-local \
- "$(git for-each-ref --format="%(committerdate:relative)" refs/heads)" \
- "$(git for-each-ref --format="%(authordate:relative)" refs/heads)" \
- "$(git for-each-ref --format="%(taggerdate:relative)" refs/tags)"
-'
-
-test_expect_success 'Check format "short" date fields output' '
- test_date short 2006-07-04 2006-07-04 2006-07-04
-'
-
-test_expect_success 'Check format "short-local" date fields output' '
- test_date short-local 2006-07-03 2006-07-03 2006-07-03
-'
-
-test_expect_success 'Check format "local" date fields output' '
- test_date local \
- "Mon Jul 3 23:18:43 2006" \
- "Mon Jul 3 23:18:44 2006" \
- "Mon Jul 3 23:18:45 2006"
-'
-
-test_expect_success 'Check format "iso8601" date fields output' '
- test_date iso8601 \
- "2006-07-04 01:18:43 +0200" \
- "2006-07-04 01:18:44 +0200" \
- "2006-07-04 01:18:45 +0200"
-'
-
-test_expect_success 'Check format "iso8601-local" date fields output' '
- test_date iso8601-local "2006-07-03 23:18:43 +0000" "2006-07-03 23:18:44 +0000" "2006-07-03 23:18:45 +0000"
-'
-
-test_expect_success 'Check format "rfc2822" date fields output' '
- test_date rfc2822 \
- "Tue, 4 Jul 2006 01:18:43 +0200" \
- "Tue, 4 Jul 2006 01:18:44 +0200" \
- "Tue, 4 Jul 2006 01:18:45 +0200"
-'
-
-test_expect_success 'Check format "rfc2822-local" date fields output' '
- test_date rfc2822-local "Mon, 3 Jul 2006 23:18:43 +0000" "Mon, 3 Jul 2006 23:18:44 +0000" "Mon, 3 Jul 2006 23:18:45 +0000"
-'
-
-test_expect_success 'Check format "raw" date fields output' '
- test_date raw "1151968723 +0200" "1151968724 +0200" "1151968725 +0200"
-'
-
-test_expect_success 'Check format "raw-local" date fields output' '
- test_date raw-local "1151968723 +0000" "1151968724 +0000" "1151968725 +0000"
-'
-
-test_expect_success 'Check format of strftime date fields' '
- echo "my date is 2006-07-04" >expected &&
- git for-each-ref \
- --format="%(authordate:format:my date is %Y-%m-%d)" \
- refs/heads >actual &&
- test_cmp expected actual
-'
-
-test_expect_success 'Check format of strftime-local date fields' '
- echo "my date is 2006-07-03" >expected &&
- git for-each-ref \
- --format="%(authordate:format-local:my date is %Y-%m-%d)" \
- refs/heads >actual &&
- test_cmp expected actual
-'
-
-test_expect_success 'exercise strftime with odd fields' '
- echo >expected &&
- git for-each-ref --format="%(authordate:format:)" refs/heads >actual &&
- test_cmp expected actual &&
- long="long format -- $ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID" &&
- echo $long >expected &&
- git for-each-ref --format="%(authordate:format:$long)" refs/heads >actual &&
- test_cmp expected actual
-'
-
-cat >expected <<\EOF
-refs/heads/main
-refs/remotes/origin/main
-refs/tags/testtag
-EOF
-
-test_expect_success 'Verify ascending sort' '
- git for-each-ref --format="%(refname)" --sort=refname >actual &&
- test_cmp expected actual
-'
-
-
-cat >expected <<\EOF
-refs/tags/testtag
-refs/remotes/origin/main
-refs/heads/main
-EOF
-
-test_expect_success 'Verify descending sort' '
- git for-each-ref --format="%(refname)" --sort=-refname >actual &&
- test_cmp expected actual
-'
-
-test_expect_success 'Give help even with invalid sort atoms' '
- test_expect_code 129 git for-each-ref --sort=bogus -h >actual 2>&1 &&
- grep "^usage: git for-each-ref" actual
-'
-
-cat >expected <<\EOF
-refs/tags/testtag
-refs/tags/testtag-2
-EOF
-
-test_expect_success 'exercise patterns with prefixes' '
- git tag testtag-2 &&
- test_when_finished "git tag -d testtag-2" &&
- git for-each-ref --format="%(refname)" \
- refs/tags/testtag refs/tags/testtag-2 >actual &&
- test_cmp expected actual
-'
-
-cat >expected <<\EOF
-refs/tags/testtag
-refs/tags/testtag-2
-EOF
-
-test_expect_success 'exercise glob patterns with prefixes' '
- git tag testtag-2 &&
- test_when_finished "git tag -d testtag-2" &&
- git for-each-ref --format="%(refname)" \
- refs/tags/testtag "refs/tags/testtag-*" >actual &&
- test_cmp expected actual
-'
-
-cat >expected <<\EOF
-refs/tags/bar
-refs/tags/baz
-refs/tags/testtag
-EOF
-
-test_expect_success 'exercise patterns with prefix exclusions' '
- for tag in foo/one foo/two foo/three bar baz
- do
- git tag "$tag" || return 1
- done &&
- test_when_finished "git tag -d foo/one foo/two foo/three bar baz" &&
- git for-each-ref --format="%(refname)" \
- refs/tags/ --exclude=refs/tags/foo >actual &&
- test_cmp expected actual
-'
-
-cat >expected <<\EOF
-refs/tags/bar
-refs/tags/baz
-refs/tags/foo/one
-refs/tags/testtag
-EOF
-
-test_expect_success 'exercise patterns with pattern exclusions' '
- for tag in foo/one foo/two foo/three bar baz
- do
- git tag "$tag" || return 1
- done &&
- test_when_finished "git tag -d foo/one foo/two foo/three bar baz" &&
- git for-each-ref --format="%(refname)" \
- refs/tags/ --exclude="refs/tags/foo/t*" >actual &&
- test_cmp expected actual
-'
-
-cat >expected <<\EOF
-'refs/heads/main'
-'refs/remotes/origin/main'
-'refs/tags/testtag'
-EOF
-
-test_expect_success 'Quoting style: shell' '
- git for-each-ref --shell --format="%(refname)" >actual &&
- test_cmp expected actual
-'
-
-test_expect_success 'Quoting style: perl' '
- git for-each-ref --perl --format="%(refname)" >actual &&
- test_cmp expected actual
-'
-
-test_expect_success 'Quoting style: python' '
- git for-each-ref --python --format="%(refname)" >actual &&
- test_cmp expected actual
-'
-
-cat >expected <<\EOF
-"refs/heads/main"
-"refs/remotes/origin/main"
-"refs/tags/testtag"
-EOF
-
-test_expect_success 'Quoting style: tcl' '
- git for-each-ref --tcl --format="%(refname)" >actual &&
- test_cmp expected actual
-'
-
-for i in "--perl --shell" "-s --python" "--python --tcl" "--tcl --perl"; do
- test_expect_success "more than one quoting style: $i" "
- test_must_fail git for-each-ref $i 2>err &&
- grep '^error: more than one quoting style' err
- "
-done
-
-test_expect_success 'setup for upstream:track[short]' '
- test_commit two
-'
-
-test_atom head upstream:track '[ahead 1]'
-test_atom head upstream:trackshort '>'
-test_atom head upstream:track,nobracket 'ahead 1'
-test_atom head upstream:nobracket,track 'ahead 1'
-
-test_expect_success 'setup for push:track[short]' '
- test_commit third &&
- git update-ref refs/remotes/myfork/main main &&
- git reset main~1
-'
-
-test_atom head push:track '[behind 1]'
-test_atom head push:trackshort '<'
-
-test_expect_success 'Check that :track[short] cannot be used with other atoms' '
- test_must_fail git for-each-ref --format="%(refname:track)" 2>/dev/null &&
- test_must_fail git for-each-ref --format="%(refname:trackshort)" 2>/dev/null
-'
-
-test_expect_success 'Check that :track[short] works when upstream is invalid' '
- cat >expected <<-\EOF &&
- [gone]
-
- EOF
- test_when_finished "git config branch.main.merge refs/heads/main" &&
- git config branch.main.merge refs/heads/does-not-exist &&
- git for-each-ref \
- --format="%(upstream:track)$LF%(upstream:trackshort)" \
- refs/heads >actual &&
- test_cmp expected actual
-'
-
-test_expect_success 'Check for invalid refname format' '
- test_must_fail git for-each-ref --format="%(refname:INVALID)"
-'
-
-test_expect_success 'set up color tests' '
- cat >expected.color <<-EOF &&
- $(git rev-parse --short refs/heads/main) <GREEN>main<RESET>
- $(git rev-parse --short refs/remotes/myfork/main) <GREEN>myfork/main<RESET>
- $(git rev-parse --short refs/remotes/origin/main) <GREEN>origin/main<RESET>
- $(git rev-parse --short refs/tags/testtag) <GREEN>testtag<RESET>
- $(git rev-parse --short refs/tags/third) <GREEN>third<RESET>
- $(git rev-parse --short refs/tags/two) <GREEN>two<RESET>
- EOF
- sed "s/<[^>]*>//g" <expected.color >expected.bare &&
- color_format="%(objectname:short) %(color:green)%(refname:short)"
-'
-
-test_expect_success TTY '%(color) shows color with a tty' '
- test_terminal git for-each-ref --format="$color_format" >actual.raw &&
- test_decode_color <actual.raw >actual &&
- test_cmp expected.color actual
-'
-
-test_expect_success '%(color) does not show color without tty' '
- TERM=vt100 git for-each-ref --format="$color_format" >actual &&
- test_cmp expected.bare actual
-'
-
-test_expect_success '--color can override tty check' '
- git for-each-ref --color --format="$color_format" >actual.raw &&
- test_decode_color <actual.raw >actual &&
- test_cmp expected.color actual
-'
-
-test_expect_success 'color.ui=always does not override tty check' '
- git -c color.ui=always for-each-ref --format="$color_format" >actual &&
- test_cmp expected.bare actual
-'
-
-test_expect_success 'setup for describe atom tests' '
- git init -b master describe-repo &&
- (
- cd describe-repo &&
-
- test_commit --no-tag one &&
- git tag tagone &&
-
- test_commit --no-tag two &&
- git tag -a -m "tag two" tagtwo
- )
-'
-
-test_expect_success 'describe atom vs git describe' '
- (
- cd describe-repo &&
-
- git for-each-ref --format="%(objectname)" \
- refs/tags/ >obj &&
- while read hash
- do
- if desc=$(git describe $hash)
- then
- : >expect-contains-good
- else
- : >expect-contains-bad
- fi &&
- echo "$hash $desc" || return 1
- done <obj >expect &&
- test_path_exists expect-contains-good &&
- test_path_exists expect-contains-bad &&
-
- git for-each-ref --format="%(objectname) %(describe)" \
- refs/tags/ >actual 2>err &&
- test_cmp expect actual &&
- test_must_be_empty err
- )
-'
-
-test_expect_success 'describe:tags vs describe --tags' '
- (
- cd describe-repo &&
- git describe --tags >expect &&
- git for-each-ref --format="%(describe:tags)" \
- refs/heads/master >actual &&
- test_cmp expect actual
- )
-'
-
-test_expect_success 'describe:abbrev=... vs describe --abbrev=...' '
- (
- cd describe-repo &&
-
- # Case 1: We have commits between HEAD and the most
- # recent tag reachable from it
- test_commit --no-tag file &&
- git describe --abbrev=14 >expect &&
- git for-each-ref --format="%(describe:abbrev=14)" \
- refs/heads/master >actual &&
- test_cmp expect actual &&
-
- # Make sure the hash used is at least 14 digits long
- sed -e "s/^.*-g\([0-9a-f]*\)$/\1/" <actual >hexpart &&
- test 15 -le $(wc -c <hexpart) &&
-
- # Case 2: We have a tag at HEAD, describe directly gives
- # the name of the tag
- git tag -a -m tagged tagname &&
- git describe --abbrev=14 >expect &&
- git for-each-ref --format="%(describe:abbrev=14)" \
- refs/heads/master >actual &&
- test_cmp expect actual &&
- test tagname = $(cat actual)
- )
-'
-
-test_expect_success 'describe:match=... vs describe --match ...' '
- (
- cd describe-repo &&
- git tag -a -m "tag foo" tag-foo &&
- git describe --match "*-foo" >expect &&
- git for-each-ref --format="%(describe:match="*-foo")" \
- refs/heads/master >actual &&
- test_cmp expect actual
- )
-'
-
-test_expect_success 'describe:exclude:... vs describe --exclude ...' '
- (
- cd describe-repo &&
- git tag -a -m "tag bar" tag-bar &&
- git describe --exclude "*-bar" >expect &&
- git for-each-ref --format="%(describe:exclude="*-bar")" \
- refs/heads/master >actual &&
- test_cmp expect actual
- )
-'
-
-test_expect_success 'deref with describe atom' '
- (
- cd describe-repo &&
- cat >expect <<-\EOF &&
-
- tagname
- tagname
- tagname
-
- tagtwo
- EOF
- git for-each-ref --format="%(*describe)" >actual &&
- test_cmp expect actual
- )
-'
-
-test_expect_success 'err on bad describe atom arg' '
- (
- cd describe-repo &&
-
- # The bad arg is the only arg passed to describe atom
- cat >expect <<-\EOF &&
- fatal: unrecognized %(describe) argument: baz
- EOF
- test_must_fail git for-each-ref --format="%(describe:baz)" \
- refs/heads/master 2>actual &&
- test_cmp expect actual &&
-
- # The bad arg is in the middle of the option string
- # passed to the describe atom
- cat >expect <<-\EOF &&
- fatal: unrecognized %(describe) argument: qux=1,abbrev=14
- EOF
- test_must_fail git for-each-ref \
- --format="%(describe:tags,qux=1,abbrev=14)" \
- ref/heads/master 2>actual &&
- test_cmp expect actual
- )
-'
-
-cat >expected <<\EOF
-heads/main
-tags/main
-EOF
-
-test_expect_success 'Check ambiguous head and tag refs (strict)' '
- git config --bool core.warnambiguousrefs true &&
- git checkout -b newtag &&
- echo "Using $datestamp" > one &&
- git add one &&
- git commit -m "Branch" &&
- setdate_and_increment &&
- git tag -m "Tagging at $datestamp" main &&
- git for-each-ref --format "%(refname:short)" refs/heads/main refs/tags/main >actual &&
- test_cmp expected actual
-'
-
-cat >expected <<\EOF
-heads/main
-main
-EOF
-
-test_expect_success 'Check ambiguous head and tag refs (loose)' '
- git config --bool core.warnambiguousrefs false &&
- git for-each-ref --format "%(refname:short)" refs/heads/main refs/tags/main >actual &&
- test_cmp expected actual
-'
-
-cat >expected <<\EOF
-heads/ambiguous
-ambiguous
-EOF
-
-test_expect_success 'Check ambiguous head and tag refs II (loose)' '
- git checkout main &&
- git tag ambiguous testtag^0 &&
- git branch ambiguous testtag^0 &&
- git for-each-ref --format "%(refname:short)" refs/heads/ambiguous refs/tags/ambiguous >actual &&
- test_cmp expected actual
-'
-
-test_expect_success 'create tag without tagger' '
- git tag -a -m "Broken tag" taggerless &&
- git tag -f taggerless $(git cat-file tag taggerless |
- sed -e "/^tagger /d" |
- git hash-object --literally --stdin -w -t tag)
-'
-
-test_atom refs/tags/taggerless type 'commit'
-test_atom refs/tags/taggerless tag 'taggerless'
-test_atom refs/tags/taggerless tagger ''
-test_atom refs/tags/taggerless taggername ''
-test_atom refs/tags/taggerless taggeremail ''
-test_atom refs/tags/taggerless taggeremail:trim ''
-test_atom refs/tags/taggerless taggeremail:localpart ''
-test_atom refs/tags/taggerless taggerdate ''
-test_atom refs/tags/taggerless committer ''
-test_atom refs/tags/taggerless committername ''
-test_atom refs/tags/taggerless committeremail ''
-test_atom refs/tags/taggerless committeremail:trim ''
-test_atom refs/tags/taggerless committeremail:localpart ''
-test_atom refs/tags/taggerless committerdate ''
-test_atom refs/tags/taggerless subject 'Broken tag'
-
-test_expect_success 'an unusual tag with an incomplete line' '
-
- git tag -m "bogo" bogo &&
- bogo=$(git cat-file tag bogo) &&
- bogo=$(printf "%s" "$bogo" | git mktag) &&
- git tag -f bogo "$bogo" &&
- git for-each-ref --format "%(body)" refs/tags/bogo
-
-'
-
-test_expect_success 'create tag with subject and body content' '
- cat >>msg <<-\EOF &&
- the subject line
-
- first body line
- second body line
- EOF
- git tag -F msg subject-body
-'
-test_atom refs/tags/subject-body subject 'the subject line'
-test_atom refs/tags/subject-body subject:sanitize 'the-subject-line'
-test_atom refs/tags/subject-body body 'first body line
-second body line
-'
-test_atom refs/tags/subject-body contents 'the subject line
-
-first body line
-second body line
-'
-
-test_expect_success 'create tag with multiline subject' '
- cat >msg <<-\EOF &&
- first subject line
- second subject line
-
- first body line
- second body line
- EOF
- git tag -F msg multiline
-'
-test_atom refs/tags/multiline subject 'first subject line second subject line'
-test_atom refs/tags/multiline subject:sanitize 'first-subject-line-second-subject-line'
-test_atom refs/tags/multiline contents:subject 'first subject line second subject line'
-test_atom refs/tags/multiline body 'first body line
-second body line
-'
-test_atom refs/tags/multiline contents:body 'first body line
-second body line
-'
-test_atom refs/tags/multiline contents:signature ''
-test_atom refs/tags/multiline contents 'first subject line
-second subject line
-
-first body line
-second body line
-'
-
-test_expect_success GPG 'create signed tags' '
- git tag -s -m "" signed-empty &&
- git tag -s -m "subject line" signed-short &&
- cat >msg <<-\EOF &&
- subject line
-
- body contents
- EOF
- git tag -s -F msg signed-long
-'
-
-sig='-----BEGIN PGP SIGNATURE-----
------END PGP SIGNATURE-----
-'
-
-PREREQ=GPG
-test_atom refs/tags/signed-empty subject ''
-test_atom refs/tags/signed-empty subject:sanitize ''
-test_atom refs/tags/signed-empty contents:subject ''
-test_atom refs/tags/signed-empty body "$sig"
-test_atom refs/tags/signed-empty contents:body ''
-test_atom refs/tags/signed-empty contents:signature "$sig"
-test_atom refs/tags/signed-empty contents "$sig"
-
-test_expect_success GPG 'basic atom: refs/tags/signed-empty raw' '
- git cat-file tag refs/tags/signed-empty >expected &&
- git for-each-ref --format="%(raw)" refs/tags/signed-empty >actual &&
- sanitize_pgp <expected >expected.clean &&
- echo >>expected.clean &&
- sanitize_pgp <actual >actual.clean &&
- test_cmp expected.clean actual.clean
-'
-
-test_atom refs/tags/signed-short subject 'subject line'
-test_atom refs/tags/signed-short subject:sanitize 'subject-line'
-test_atom refs/tags/signed-short contents:subject 'subject line'
-test_atom refs/tags/signed-short body "$sig"
-test_atom refs/tags/signed-short contents:body ''
-test_atom refs/tags/signed-short contents:signature "$sig"
-test_atom refs/tags/signed-short contents "subject line
-$sig"
-
-test_expect_success GPG 'basic atom: refs/tags/signed-short raw' '
- git cat-file tag refs/tags/signed-short >expected &&
- git for-each-ref --format="%(raw)" refs/tags/signed-short >actual &&
- sanitize_pgp <expected >expected.clean &&
- echo >>expected.clean &&
- sanitize_pgp <actual >actual.clean &&
- test_cmp expected.clean actual.clean
-'
-
-test_atom refs/tags/signed-long subject 'subject line'
-test_atom refs/tags/signed-long subject:sanitize 'subject-line'
-test_atom refs/tags/signed-long contents:subject 'subject line'
-test_atom refs/tags/signed-long body "body contents
-$sig"
-test_atom refs/tags/signed-long contents:body 'body contents
-'
-test_atom refs/tags/signed-long contents:signature "$sig"
-test_atom refs/tags/signed-long contents "subject line
-
-body contents
-$sig"
-
-test_expect_success GPG 'basic atom: refs/tags/signed-long raw' '
- git cat-file tag refs/tags/signed-long >expected &&
- git for-each-ref --format="%(raw)" refs/tags/signed-long >actual &&
- sanitize_pgp <expected >expected.clean &&
- echo >>expected.clean &&
- sanitize_pgp <actual >actual.clean &&
- test_cmp expected.clean actual.clean
-'
-
-test_expect_success 'set up refs pointing to tree and blob' '
- git update-ref refs/mytrees/first refs/heads/main^{tree} &&
- git update-ref refs/myblobs/first refs/heads/main:one
-'
-
-test_atom refs/mytrees/first subject ""
-test_atom refs/mytrees/first contents:subject ""
-test_atom refs/mytrees/first body ""
-test_atom refs/mytrees/first contents:body ""
-test_atom refs/mytrees/first contents:signature ""
-test_atom refs/mytrees/first contents ""
-
-test_expect_success 'basic atom: refs/mytrees/first raw' '
- git cat-file tree refs/mytrees/first >expected &&
- echo >>expected &&
- git for-each-ref --format="%(raw)" refs/mytrees/first >actual &&
- test_cmp expected actual &&
- git cat-file -s refs/mytrees/first >expected &&
- git for-each-ref --format="%(raw:size)" refs/mytrees/first >actual &&
- test_cmp expected actual
-'
-
-test_atom refs/myblobs/first subject ""
-test_atom refs/myblobs/first contents:subject ""
-test_atom refs/myblobs/first body ""
-test_atom refs/myblobs/first contents:body ""
-test_atom refs/myblobs/first contents:signature ""
-test_atom refs/myblobs/first contents ""
-
-test_expect_success 'basic atom: refs/myblobs/first raw' '
- git cat-file blob refs/myblobs/first >expected &&
- echo >>expected &&
- git for-each-ref --format="%(raw)" refs/myblobs/first >actual &&
- test_cmp expected actual &&
- git cat-file -s refs/myblobs/first >expected &&
- git for-each-ref --format="%(raw:size)" refs/myblobs/first >actual &&
- test_cmp expected actual
-'
-
-test_expect_success 'set up refs pointing to binary blob' '
- printf "a\0b\0c" >blob1 &&
- printf "a\0c\0b" >blob2 &&
- printf "\0a\0b\0c" >blob3 &&
- printf "abc" >blob4 &&
- printf "\0 \0 \0 " >blob5 &&
- printf "\0 \0a\0 " >blob6 &&
- printf " " >blob7 &&
- >blob8 &&
- obj=$(git hash-object -w blob1) &&
- git update-ref refs/myblobs/blob1 "$obj" &&
- obj=$(git hash-object -w blob2) &&
- git update-ref refs/myblobs/blob2 "$obj" &&
- obj=$(git hash-object -w blob3) &&
- git update-ref refs/myblobs/blob3 "$obj" &&
- obj=$(git hash-object -w blob4) &&
- git update-ref refs/myblobs/blob4 "$obj" &&
- obj=$(git hash-object -w blob5) &&
- git update-ref refs/myblobs/blob5 "$obj" &&
- obj=$(git hash-object -w blob6) &&
- git update-ref refs/myblobs/blob6 "$obj" &&
- obj=$(git hash-object -w blob7) &&
- git update-ref refs/myblobs/blob7 "$obj" &&
- obj=$(git hash-object -w blob8) &&
- git update-ref refs/myblobs/blob8 "$obj"
-'
-
-test_expect_success 'Verify sorts with raw' '
- cat >expected <<-EOF &&
- refs/myblobs/blob8
- refs/myblobs/blob5
- refs/myblobs/blob6
- refs/myblobs/blob3
- refs/myblobs/blob7
- refs/mytrees/first
- refs/myblobs/first
- refs/myblobs/blob1
- refs/myblobs/blob2
- refs/myblobs/blob4
- refs/heads/main
- EOF
- git for-each-ref --format="%(refname)" --sort=raw \
- refs/heads/main refs/myblobs/ refs/mytrees/first >actual &&
- test_cmp expected actual
-'
-
-test_expect_success 'Verify sorts with raw:size' '
- cat >expected <<-EOF &&
- refs/myblobs/blob8
- refs/myblobs/blob7
- refs/myblobs/blob4
- refs/myblobs/blob1
- refs/myblobs/blob2
- refs/myblobs/blob3
- refs/myblobs/blob5
- refs/myblobs/blob6
- refs/myblobs/first
- refs/mytrees/first
- refs/heads/main
- EOF
- git for-each-ref --format="%(refname)" --sort=raw:size \
- refs/heads/main refs/myblobs/ refs/mytrees/first >actual &&
- test_cmp expected actual
-'
-
-test_expect_success 'validate raw atom with %(if:equals)' '
- cat >expected <<-EOF &&
- not equals
- not equals
- not equals
- not equals
- not equals
- not equals
- refs/myblobs/blob4
- not equals
- not equals
- not equals
- not equals
- not equals
- EOF
- git for-each-ref --format="%(if:equals=abc)%(raw)%(then)%(refname)%(else)not equals%(end)" \
- refs/myblobs/ refs/heads/ >actual &&
- test_cmp expected actual
-'
-
-test_expect_success 'validate raw atom with %(if:notequals)' '
- cat >expected <<-EOF &&
- refs/heads/ambiguous
- refs/heads/main
- refs/heads/newtag
- refs/myblobs/blob1
- refs/myblobs/blob2
- refs/myblobs/blob3
- equals
- refs/myblobs/blob5
- refs/myblobs/blob6
- refs/myblobs/blob7
- refs/myblobs/blob8
- refs/myblobs/first
- EOF
- git for-each-ref --format="%(if:notequals=abc)%(raw)%(then)%(refname)%(else)equals%(end)" \
- refs/myblobs/ refs/heads/ >actual &&
- test_cmp expected actual
-'
-
-test_expect_success 'empty raw refs with %(if)' '
- cat >expected <<-EOF &&
- refs/myblobs/blob1 not empty
- refs/myblobs/blob2 not empty
- refs/myblobs/blob3 not empty
- refs/myblobs/blob4 not empty
- refs/myblobs/blob5 not empty
- refs/myblobs/blob6 not empty
- refs/myblobs/blob7 empty
- refs/myblobs/blob8 empty
- refs/myblobs/first not empty
- EOF
- git for-each-ref --format="%(refname) %(if)%(raw)%(then)not empty%(else)empty%(end)" \
- refs/myblobs/ >actual &&
- test_cmp expected actual
-'
-
-test_expect_success '%(raw) with --python must fail' '
- test_must_fail git for-each-ref --format="%(raw)" --python
-'
-
-test_expect_success '%(raw) with --tcl must fail' '
- test_must_fail git for-each-ref --format="%(raw)" --tcl
-'
-
-test_expect_success PERL_TEST_HELPERS '%(raw) with --perl' '
- git for-each-ref --format="\$name= %(raw);
-print \"\$name\"" refs/myblobs/blob1 --perl | perl >actual &&
- cmp blob1 actual &&
- git for-each-ref --format="\$name= %(raw);
-print \"\$name\"" refs/myblobs/blob3 --perl | perl >actual &&
- cmp blob3 actual &&
- git for-each-ref --format="\$name= %(raw);
-print \"\$name\"" refs/myblobs/blob8 --perl | perl >actual &&
- cmp blob8 actual &&
- git for-each-ref --format="\$name= %(raw);
-print \"\$name\"" refs/myblobs/first --perl | perl >actual &&
- cmp one actual &&
- git cat-file tree refs/mytrees/first > expected &&
- git for-each-ref --format="\$name= %(raw);
-print \"\$name\"" refs/mytrees/first --perl | perl >actual &&
- cmp expected actual
-'
-
-test_expect_success '%(raw) with --shell must fail' '
- test_must_fail git for-each-ref --format="%(raw)" --shell
-'
-
-test_expect_success '%(raw) with --shell and --sort=raw must fail' '
- test_must_fail git for-each-ref --format="%(raw)" --sort=raw --shell
-'
-
-test_expect_success '%(raw:size) with --shell' '
- git for-each-ref --format="%(raw:size)" | sed "s/^/$SQ/;s/$/$SQ/" >expect &&
- git for-each-ref --format="%(raw:size)" --shell >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'for-each-ref --format compare with cat-file --batch' '
- git rev-parse refs/mytrees/first | git cat-file --batch >expected &&
- git for-each-ref --format="%(objectname) %(objecttype) %(objectsize)
-%(raw)" refs/mytrees/first >actual &&
- test_cmp expected actual
-'
-
-test_expect_success 'verify sorts with contents:size' '
- cat >expect <<-\EOF &&
- refs/heads/main
- refs/heads/newtag
- refs/heads/ambiguous
- EOF
- git for-each-ref --format="%(refname)" \
- --sort=contents:size refs/heads/ >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'set up multiple-sort tags' '
- for when in 100000 200000
- do
- for email in user1 user2
- do
- for ref in ref1 ref2
- do
- GIT_COMMITTER_DATE="@$when +0000" \
- GIT_COMMITTER_EMAIL="$email@example.com" \
- git tag -m "tag $ref-$when-$email" \
- multi-$ref-$when-$email || return 1
- done
- done
- done
-'
-
-test_expect_success 'Verify sort with multiple keys' '
- cat >expected <<-\EOF &&
- 100000 <user1@example.com> refs/tags/multi-ref2-100000-user1
- 100000 <user1@example.com> refs/tags/multi-ref1-100000-user1
- 100000 <user2@example.com> refs/tags/multi-ref2-100000-user2
- 100000 <user2@example.com> refs/tags/multi-ref1-100000-user2
- 200000 <user1@example.com> refs/tags/multi-ref2-200000-user1
- 200000 <user1@example.com> refs/tags/multi-ref1-200000-user1
- 200000 <user2@example.com> refs/tags/multi-ref2-200000-user2
- 200000 <user2@example.com> refs/tags/multi-ref1-200000-user2
- EOF
- git for-each-ref \
- --format="%(taggerdate:unix) %(taggeremail) %(refname)" \
- --sort=-refname \
- --sort=taggeremail \
- --sort=taggerdate \
- "refs/tags/multi-*" >actual &&
- test_cmp expected actual
-'
-
-test_expect_success 'equivalent sorts fall back on refname' '
- cat >expected <<-\EOF &&
- 100000 <user1@example.com> refs/tags/multi-ref1-100000-user1
- 100000 <user2@example.com> refs/tags/multi-ref1-100000-user2
- 100000 <user1@example.com> refs/tags/multi-ref2-100000-user1
- 100000 <user2@example.com> refs/tags/multi-ref2-100000-user2
- 200000 <user1@example.com> refs/tags/multi-ref1-200000-user1
- 200000 <user2@example.com> refs/tags/multi-ref1-200000-user2
- 200000 <user1@example.com> refs/tags/multi-ref2-200000-user1
- 200000 <user2@example.com> refs/tags/multi-ref2-200000-user2
- EOF
- git for-each-ref \
- --format="%(taggerdate:unix) %(taggeremail) %(refname)" \
- --sort=taggerdate \
- "refs/tags/multi-*" >actual &&
- test_cmp expected actual
-'
-
-test_expect_success '--no-sort cancels the previous sort keys' '
- cat >expected <<-\EOF &&
- 100000 <user1@example.com> refs/tags/multi-ref1-100000-user1
- 100000 <user2@example.com> refs/tags/multi-ref1-100000-user2
- 100000 <user1@example.com> refs/tags/multi-ref2-100000-user1
- 100000 <user2@example.com> refs/tags/multi-ref2-100000-user2
- 200000 <user1@example.com> refs/tags/multi-ref1-200000-user1
- 200000 <user2@example.com> refs/tags/multi-ref1-200000-user2
- 200000 <user1@example.com> refs/tags/multi-ref2-200000-user1
- 200000 <user2@example.com> refs/tags/multi-ref2-200000-user2
- EOF
- git for-each-ref \
- --format="%(taggerdate:unix) %(taggeremail) %(refname)" \
- --sort=-refname \
- --sort=taggeremail \
- --no-sort \
- --sort=taggerdate \
- "refs/tags/multi-*" >actual &&
- test_cmp expected actual
-'
-
-test_expect_success '--no-sort without subsequent --sort prints expected refs' '
- cat >expected <<-\EOF &&
- refs/tags/multi-ref1-100000-user1
- refs/tags/multi-ref1-100000-user2
- refs/tags/multi-ref1-200000-user1
- refs/tags/multi-ref1-200000-user2
- refs/tags/multi-ref2-100000-user1
- refs/tags/multi-ref2-100000-user2
- refs/tags/multi-ref2-200000-user1
- refs/tags/multi-ref2-200000-user2
- EOF
-
- # Sort the results with `sort` for a consistent comparison against
- # expected
- git for-each-ref \
- --format="%(refname)" \
- --no-sort \
- "refs/tags/multi-*" | sort >actual &&
- test_cmp expected actual
-'
-
-test_expect_success 'set up custom date sorting' '
- # Dates:
- # - Wed Feb 07 2024 21:34:20 +0000
- # - Tue Dec 14 1999 00:05:22 +0000
- # - Fri Jun 04 2021 11:26:51 +0000
- # - Mon Jan 22 2007 16:44:01 GMT+0000
- i=1 &&
- for when in 1707341660 945129922 1622806011 1169484241
- do
- GIT_COMMITTER_DATE="@$when +0000" \
- GIT_COMMITTER_EMAIL="user@example.com" \
- git tag -m "tag $when" custom-dates-$i &&
- i=$(($i+1)) || return 1
- done
-'
-
-test_expect_success 'sort by date defaults to full timestamp' '
- cat >expected <<-\EOF &&
- 945129922 refs/tags/custom-dates-2
- 1169484241 refs/tags/custom-dates-4
- 1622806011 refs/tags/custom-dates-3
- 1707341660 refs/tags/custom-dates-1
- EOF
-
- git for-each-ref \
- --format="%(creatordate:unix) %(refname)" \
- --sort=creatordate \
- "refs/tags/custom-dates-*" >actual &&
- test_cmp expected actual
-'
-
-test_expect_success 'sort by custom date format' '
- cat >expected <<-\EOF &&
- 00:05:22 refs/tags/custom-dates-2
- 11:26:51 refs/tags/custom-dates-3
- 16:44:01 refs/tags/custom-dates-4
- 21:34:20 refs/tags/custom-dates-1
- EOF
-
- git for-each-ref \
- --format="%(creatordate:format:%H:%M:%S) %(refname)" \
- --sort="creatordate:format:%H:%M:%S" \
- "refs/tags/custom-dates-*" >actual &&
- test_cmp expected actual
-'
-
-test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '
- test_when_finished "git checkout main" &&
- git for-each-ref --format="%(HEAD) %(refname:short)" refs/heads/ >actual &&
- sed -e "s/^\* / /" actual >expect &&
- git checkout --orphan orphaned-branch &&
- git for-each-ref --format="%(HEAD) %(refname:short)" refs/heads/ >actual &&
- test_cmp expect actual
-'
-
-cat >trailers <<EOF
-Reviewed-by: A U Thor <author@example.com>
-Signed-off-by: A U Thor <author@example.com>
-[ v2 updated patch description ]
-Acked-by: A U Thor
- <author@example.com>
-EOF
-
-unfold () {
- perl -0pe 's/\n\s+/ /g'
-}
-
-test_expect_success 'set up trailers for next test' '
- echo "Some contents" > two &&
- git add two &&
- git commit -F - <<-EOF
- trailers: this commit message has trailers
-
- Some message contents
-
- $(cat trailers)
- EOF
-'
-
-test_trailer_option () {
- if test "$#" -eq 3
- then
- prereq="$1"
- shift
- fi &&
- title=$1 option=$2
- cat >expect
- test_expect_success $prereq "$title" '
- git for-each-ref --format="%($option)" refs/heads/main >actual &&
- test_cmp expect actual &&
- git for-each-ref --format="%(contents:$option)" refs/heads/main >actual &&
- test_cmp expect actual
- '
-}
-
-test_trailer_option PERL_TEST_HELPERS '%(trailers:unfold) unfolds trailers' \
- 'trailers:unfold' <<-EOF
- $(unfold <trailers)
-
- EOF
-
-test_trailer_option '%(trailers:only) shows only "key: value" trailers' \
- 'trailers:only' <<-EOF
- $(grep -v patch.description <trailers)
-
- EOF
-
-test_trailer_option '%(trailers:only=no,only=true) shows only "key: value" trailers' \
- 'trailers:only=no,only=true' <<-EOF
- $(grep -v patch.description <trailers)
-
- EOF
-
-test_trailer_option '%(trailers:only=yes) shows only "key: value" trailers' \
- 'trailers:only=yes' <<-EOF
- $(grep -v patch.description <trailers)
-
- EOF
-
-test_trailer_option '%(trailers:only=no) shows all trailers' \
- 'trailers:only=no' <<-EOF
- $(cat trailers)
-
- EOF
-
-test_trailer_option PERL_TEST_HELPERS '%(trailers:only) and %(trailers:unfold) work together' \
- 'trailers:only,unfold' <<-EOF
- $(grep -v patch.description <trailers | unfold)
-
- EOF
-
-test_trailer_option PERL_TEST_HELPERS '%(trailers:unfold) and %(trailers:only) work together' \
- 'trailers:unfold,only' <<-EOF
- $(grep -v patch.description <trailers | unfold)
-
- EOF
-
-test_trailer_option '%(trailers:key=foo) shows that trailer' \
- 'trailers:key=Signed-off-by' <<-EOF
- Signed-off-by: A U Thor <author@example.com>
-
- EOF
-
-test_trailer_option '%(trailers:key=foo) is case insensitive' \
- 'trailers:key=SiGned-oFf-bY' <<-EOF
- Signed-off-by: A U Thor <author@example.com>
-
- EOF
-
-test_trailer_option '%(trailers:key=foo:) trailing colon also works' \
- 'trailers:key=Signed-off-by:' <<-EOF
- Signed-off-by: A U Thor <author@example.com>
-
- EOF
-
-test_trailer_option '%(trailers:key=foo) multiple keys' \
- 'trailers:key=Reviewed-by:,key=Signed-off-by' <<-EOF
- Reviewed-by: A U Thor <author@example.com>
- Signed-off-by: A U Thor <author@example.com>
-
- EOF
-
-test_trailer_option '%(trailers:key=nonexistent) becomes empty' \
- 'trailers:key=Shined-off-by:' <<-EOF
-
- EOF
-
-test_trailer_option '%(trailers:key=foo) handles multiple lines even if folded' \
- 'trailers:key=Acked-by' <<-EOF
- $(grep -v patch.description <trailers | grep -v Signed-off-by | grep -v Reviewed-by)
-
- EOF
-
-test_trailer_option '%(trailers:key=foo,unfold) properly unfolds' \
- 'trailers:key=Signed-Off-by,unfold' <<-EOF
- $(unfold <trailers | grep Signed-off-by)
-
- EOF
-
-test_trailer_option '%(trailers:key=foo,only=no) also includes nontrailer lines' \
- 'trailers:key=Signed-off-by,only=no' <<-EOF
- Signed-off-by: A U Thor <author@example.com>
- $(grep patch.description <trailers)
-
- EOF
-
-test_trailer_option '%(trailers:key=foo,valueonly) shows only value' \
- 'trailers:key=Signed-off-by,valueonly' <<-EOF
- A U Thor <author@example.com>
-
- EOF
-
-test_trailer_option '%(trailers:separator) changes separator' \
- 'trailers:separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF
- Reviewed-by: A U Thor <author@example.com>,Signed-off-by: A U Thor <author@example.com>
- EOF
-
-test_trailer_option '%(trailers:key_value_separator) changes key-value separator' \
- 'trailers:key_value_separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF
- Reviewed-by,A U Thor <author@example.com>
- Signed-off-by,A U Thor <author@example.com>
-
- EOF
-
-test_trailer_option '%(trailers:separator,key_value_separator) changes both separators' \
- 'trailers:separator=%x2C,key_value_separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF
- Reviewed-by,A U Thor <author@example.com>,Signed-off-by,A U Thor <author@example.com>
- EOF
-
-test_expect_success 'multiple %(trailers) use their own options' '
- git tag -F - tag-with-trailers <<-\EOF &&
- body
-
- one: foo
- one: bar
- two: baz
- two: qux
- EOF
- t1="%(trailers:key=one,key_value_separator=W,separator=X)" &&
- t2="%(trailers:key=two,key_value_separator=Y,separator=Z)" &&
- git for-each-ref --format="$t1%0a$t2" refs/tags/tag-with-trailers >actual &&
- cat >expect <<-\EOF &&
- oneWfooXoneWbar
- twoYbazZtwoYqux
- EOF
- test_cmp expect actual
-'
-
-test_failing_trailer_option () {
- title=$1 option=$2
- cat >expect
- test_expect_success "$title" '
- # error message cannot be checked under i18n
- test_must_fail git for-each-ref --format="%($option)" refs/heads/main 2>actual &&
- test_cmp expect actual &&
- test_must_fail git for-each-ref --format="%(contents:$option)" refs/heads/main 2>actual &&
- test_cmp expect actual
- '
-}
-
-test_failing_trailer_option '%(trailers) rejects unknown trailers arguments' \
- 'trailers:unsupported' <<-\EOF
- fatal: unknown %(trailers) argument: unsupported
- EOF
-
-test_failing_trailer_option '%(trailers:key) without value is error' \
- 'trailers:key' <<-\EOF
- fatal: expected %(trailers:key=<value>)
- EOF
-
-test_expect_success 'if arguments, %(contents:trailers) shows error if colon is missing' '
- cat >expect <<-EOF &&
- fatal: unrecognized %(contents) argument: trailersonly
- EOF
- test_must_fail git for-each-ref --format="%(contents:trailersonly)" 2>actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'basic atom: head contents:trailers' '
- git for-each-ref --format="%(contents:trailers)" refs/heads/main >actual &&
- sanitize_pgp <actual >actual.clean &&
- # git for-each-ref ends with a blank line
- cat >expect <<-EOF &&
- $(cat trailers)
-
- EOF
- test_cmp expect actual.clean
-'
-
-test_expect_success 'basic atom: rest must fail' '
- test_must_fail git for-each-ref --format="%(rest)" refs/heads/main
-'
-
-test_expect_success 'HEAD atom does not take arguments' '
- test_must_fail git for-each-ref --format="%(HEAD:foo)" 2>err &&
- echo "fatal: %(HEAD) does not take arguments" >expect &&
- test_cmp expect err
-'
-
-test_expect_success 'subject atom rejects unknown arguments' '
- test_must_fail git for-each-ref --format="%(subject:foo)" 2>err &&
- echo "fatal: unrecognized %(subject) argument: foo" >expect &&
- test_cmp expect err
-'
-
-test_expect_success 'refname atom rejects unknown arguments' '
- test_must_fail git for-each-ref --format="%(refname:foo)" 2>err &&
- echo "fatal: unrecognized %(refname) argument: foo" >expect &&
- test_cmp expect err
-'
-
-test_expect_success 'trailer parsing not fooled by --- line' '
- git commit --allow-empty -F - <<-\EOF &&
- this is the subject
-
- This is the body. The message has a "---" line which would confuse a
- message+patch parser. But here we know we have only a commit message,
- so we get it right.
-
- trailer: wrong
- ---
- This is more body.
-
- trailer: right
- EOF
-
- {
- echo "trailer: right" &&
- echo
- } >expect &&
- git for-each-ref --format="%(trailers)" refs/heads/main >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'Add symbolic ref for the following tests' '
- git symbolic-ref refs/heads/sym refs/heads/main
-'
-
-cat >expected <<EOF
-refs/heads/main
-EOF
-
-test_expect_success 'Verify usage of %(symref) atom' '
- git for-each-ref --format="%(symref)" refs/heads/sym >actual &&
- test_cmp expected actual
-'
-
-cat >expected <<EOF
-heads/main
-EOF
-
-test_expect_success 'Verify usage of %(symref:short) atom' '
- git for-each-ref --format="%(symref:short)" refs/heads/sym >actual &&
- test_cmp expected actual
-'
-
-cat >expected <<EOF
-main
-heads/main
-EOF
-
-test_expect_success 'Verify usage of %(symref:lstrip) atom' '
- git for-each-ref --format="%(symref:lstrip=2)" refs/heads/sym > actual &&
- git for-each-ref --format="%(symref:lstrip=-2)" refs/heads/sym >> actual &&
- test_cmp expected actual &&
-
- git for-each-ref --format="%(symref:strip=2)" refs/heads/sym > actual &&
- git for-each-ref --format="%(symref:strip=-2)" refs/heads/sym >> actual &&
- test_cmp expected actual
-'
-
-cat >expected <<EOF
-refs
-refs/heads
-EOF
-
-test_expect_success 'Verify usage of %(symref:rstrip) atom' '
- git for-each-ref --format="%(symref:rstrip=2)" refs/heads/sym > actual &&
- git for-each-ref --format="%(symref:rstrip=-2)" refs/heads/sym >> actual &&
- test_cmp expected actual
-'
-
-test_expect_success ':remotename and :remoteref' '
- git init remote-tests &&
- (
- cd remote-tests &&
- test_commit initial &&
- git branch -M main &&
- git remote add from fifth.coffee:blub &&
- git config branch.main.remote from &&
- git config branch.main.merge refs/heads/stable &&
- git remote add to southridge.audio:repo &&
- git config remote.to.push "refs/heads/*:refs/heads/pushed/*" &&
- git config branch.main.pushRemote to &&
- for pair in "%(upstream)=refs/remotes/from/stable" \
- "%(upstream:remotename)=from" \
- "%(upstream:remoteref)=refs/heads/stable" \
- "%(push)=refs/remotes/to/pushed/main" \
- "%(push:remotename)=to" \
- "%(push:remoteref)=refs/heads/pushed/main"
- do
- echo "${pair#*=}" >expect &&
- git for-each-ref --format="${pair%=*}" \
- refs/heads/main >actual &&
- test_cmp expect actual || exit 1
- done &&
- git branch push-simple &&
- git config branch.push-simple.pushRemote from &&
- actual="$(git for-each-ref \
- --format="%(push:remotename),%(push:remoteref)" \
- refs/heads/push-simple)" &&
- test from, = "$actual"
- )
-'
-
-test_expect_success 'for-each-ref --ignore-case ignores case' '
- git for-each-ref --format="%(refname)" refs/heads/MAIN >actual &&
- test_must_be_empty actual &&
-
- echo refs/heads/main >expect &&
- git for-each-ref --format="%(refname)" --ignore-case \
- refs/heads/MAIN >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'for-each-ref --omit-empty works' '
- git for-each-ref --format="%(refname)" >actual &&
- test_line_count -gt 1 actual &&
- git for-each-ref --format="%(if:equals=refs/heads/main)%(refname)%(then)%(refname)%(end)" --omit-empty >actual &&
- echo refs/heads/main >expect &&
- test_cmp expect actual
-'
-
-test_expect_success 'for-each-ref --ignore-case works on multiple sort keys' '
- # name refs numerically to avoid case-insensitive filesystem conflicts
- nr=0 &&
- for email in a A b B
- do
- for subject in a A b B
- do
- GIT_COMMITTER_EMAIL="$email@example.com" \
- git tag -m "tag $subject" icase-$(printf %02d $nr) &&
- nr=$((nr+1))||
- return 1
- done
- done &&
- git for-each-ref --ignore-case \
- --format="%(taggeremail) %(subject) %(refname)" \
- --sort=refname \
- --sort=subject \
- --sort=taggeremail \
- refs/tags/icase-* >actual &&
- cat >expect <<-\EOF &&
- <a@example.com> tag a refs/tags/icase-00
- <a@example.com> tag A refs/tags/icase-01
- <A@example.com> tag a refs/tags/icase-04
- <A@example.com> tag A refs/tags/icase-05
- <a@example.com> tag b refs/tags/icase-02
- <a@example.com> tag B refs/tags/icase-03
- <A@example.com> tag b refs/tags/icase-06
- <A@example.com> tag B refs/tags/icase-07
- <b@example.com> tag a refs/tags/icase-08
- <b@example.com> tag A refs/tags/icase-09
- <B@example.com> tag a refs/tags/icase-12
- <B@example.com> tag A refs/tags/icase-13
- <b@example.com> tag b refs/tags/icase-10
- <b@example.com> tag B refs/tags/icase-11
- <B@example.com> tag b refs/tags/icase-14
- <B@example.com> tag B refs/tags/icase-15
- EOF
- test_cmp expect actual
-'
-
-test_expect_success 'for-each-ref reports broken tags' '
- git tag -m "good tag" broken-tag-good HEAD &&
- git cat-file tag broken-tag-good >good &&
- sed s/commit/blob/ <good >bad &&
- bad=$(git hash-object -w -t tag bad) &&
- git update-ref refs/tags/broken-tag-bad $bad &&
- test_must_fail git for-each-ref --format="%(*objectname)" \
- refs/tags/broken-tag-*
-'
-
-test_expect_success 'set up tag with signature and no blank lines' '
- git tag -F - fake-sig-no-blanks <<-\EOF
- this is the subject
- -----BEGIN PGP SIGNATURE-----
- not a real signature, but we just care about the
- subject/body parsing. It is important here that
- there are no blank lines in the signature.
- -----END PGP SIGNATURE-----
- EOF
-'
-
-test_atom refs/tags/fake-sig-no-blanks contents:subject 'this is the subject'
-test_atom refs/tags/fake-sig-no-blanks contents:body ''
-test_atom refs/tags/fake-sig-no-blanks contents:signature "$sig"
-
-test_expect_success 'set up tag with CRLF signature' '
- append_cr <<-\EOF |
- this is the subject
- -----BEGIN PGP SIGNATURE-----
-
- not a real signature, but we just care about
- the subject/body parsing. It is important here
- that there is a blank line separating this
- from the signature header.
- -----END PGP SIGNATURE-----
- EOF
- git tag -F - --cleanup=verbatim fake-sig-crlf
-'
-
-test_atom refs/tags/fake-sig-crlf contents:subject 'this is the subject'
-test_atom refs/tags/fake-sig-crlf contents:body ''
-
-# CRLF is retained in the signature, so we have to pass our expected value
-# through append_cr. But test_atom requires a shell string, which means command
-# substitution, and the shell will strip trailing newlines from the output of
-# the substitution. Hack around it by adding and then removing a dummy line.
-sig_crlf="$(printf "%s" "$sig" | append_cr; echo dummy)"
-sig_crlf=${sig_crlf%dummy}
-test_atom refs/tags/fake-sig-crlf contents:signature "$sig_crlf"
-
-test_expect_success 'set up tag with signature and trailers' '
- git tag -F - fake-sig-trailer <<-\EOF
- this is the subject
-
- this is the body
-
- My-Trailer: foo
- -----BEGIN PGP SIGNATURE-----
-
- not a real signature, but we just care about the
- subject/body/trailer parsing.
- -----END PGP SIGNATURE-----
- EOF
-'
-
-# use "separator=" here to suppress the terminating newline
-test_atom refs/tags/fake-sig-trailer trailers:separator= 'My-Trailer: foo'
-
-test_expect_success 'git for-each-ref --stdin: empty' '
- >in &&
- git for-each-ref --format="%(refname)" --stdin <in >actual &&
- git for-each-ref --format="%(refname)" >expect &&
- test_cmp expect actual
-'
-
-test_expect_success 'git for-each-ref --stdin: fails if extra args' '
- >in &&
- test_must_fail git for-each-ref --format="%(refname)" \
- --stdin refs/heads/extra <in 2>err &&
- grep "unknown arguments supplied with --stdin" err
-'
-
-test_expect_success 'git for-each-ref --stdin: matches' '
- cat >in <<-EOF &&
- refs/tags/multi*
- refs/heads/amb*
- EOF
-
- cat >expect <<-EOF &&
- refs/heads/ambiguous
- refs/tags/multi-ref1-100000-user1
- refs/tags/multi-ref1-100000-user2
- refs/tags/multi-ref1-200000-user1
- refs/tags/multi-ref1-200000-user2
- refs/tags/multi-ref2-100000-user1
- refs/tags/multi-ref2-100000-user2
- refs/tags/multi-ref2-200000-user1
- refs/tags/multi-ref2-200000-user2
- refs/tags/multiline
- EOF
-
- git for-each-ref --format="%(refname)" --stdin <in >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'git for-each-ref with non-existing refs' '
- cat >in <<-EOF &&
- refs/heads/this-ref-does-not-exist
- refs/tags/bogus
- EOF
-
- git for-each-ref --format="%(refname)" --stdin <in >actual &&
- test_must_be_empty actual &&
-
- xargs git for-each-ref --format="%(refname)" <in >actual &&
- test_must_be_empty actual
-'
-
-test_expect_success 'git for-each-ref with nested tags' '
- git tag -am "Normal tag" nested/base HEAD &&
- git tag -am "Nested tag" nested/nest1 refs/tags/nested/base &&
- git tag -am "Double nested tag" nested/nest2 refs/tags/nested/nest1 &&
-
- head_oid="$(git rev-parse HEAD)" &&
- base_tag_oid="$(git rev-parse refs/tags/nested/base)" &&
- nest1_tag_oid="$(git rev-parse refs/tags/nested/nest1)" &&
- nest2_tag_oid="$(git rev-parse refs/tags/nested/nest2)" &&
-
- cat >expect <<-EOF &&
- refs/tags/nested/base $base_tag_oid tag $head_oid commit
- refs/tags/nested/nest1 $nest1_tag_oid tag $head_oid commit
- refs/tags/nested/nest2 $nest2_tag_oid tag $head_oid commit
- EOF
-
- git for-each-ref \
- --format="%(refname) %(objectname) %(objecttype) %(*objectname) %(*objecttype)" \
- refs/tags/nested/ >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'is-base atom with non-commits' '
- git for-each-ref --format="%(is-base:HEAD) %(refname)" >out 2>err &&
- grep "(HEAD) refs/heads/main" out &&
-
- test_line_count = 2 err &&
- grep "error: object .* is a commit, not a blob" err &&
- grep "error: bad tag pointer to" err
-'
-
-GRADE_FORMAT="%(signature:grade)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)"
-TRUSTLEVEL_FORMAT="%(signature:trustlevel)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)"
-
-test_expect_success GPG 'setup for signature atom using gpg' '
- git checkout -b signed &&
-
- test_when_finished "test_unconfig commit.gpgSign" &&
-
- echo "1" >file &&
- git add file &&
- test_tick &&
- git commit -S -m "file: 1" &&
- git tag first-signed &&
-
- echo "2" >file &&
- test_tick &&
- git commit -a -m "file: 2" &&
- git tag second-unsigned &&
-
- git config commit.gpgSign 1 &&
- echo "3" >file &&
- test_tick &&
- git commit -a --no-gpg-sign -m "file: 3" &&
- git tag third-unsigned &&
-
- test_tick &&
- git rebase -f HEAD^^ && git tag second-signed HEAD^ &&
- git tag third-signed &&
-
- echo "4" >file &&
- test_tick &&
- git commit -a -SB7227189 -m "file: 4" &&
- git tag fourth-signed &&
-
- echo "5" >file &&
- test_tick &&
- git commit -a --no-gpg-sign -m "file: 5" &&
- git tag fifth-unsigned &&
-
- echo "6" >file &&
- test_tick &&
- git commit -a --no-gpg-sign -m "file: 6" &&
-
- test_tick &&
- git rebase -f HEAD^^ &&
- git tag fifth-signed HEAD^ &&
- git tag sixth-signed &&
-
- echo "7" >file &&
- test_tick &&
- git commit -a --no-gpg-sign -m "file: 7" &&
- git tag seventh-unsigned
-'
-
-test_expect_success GPGSSH 'setup for signature atom using ssh' '
- test_when_finished "test_unconfig gpg.format user.signingkey" &&
-
- test_config gpg.format ssh &&
- test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
- echo "8" >file &&
- test_tick &&
- git add file &&
- git commit -S -m "file: 8" &&
- git tag eighth-signed-ssh
-'
-
-test_expect_success GPG2 'bare signature atom' '
- git verify-commit first-signed 2>expect &&
- echo >>expect &&
- git for-each-ref refs/tags/first-signed \
- --format="%(signature)" >actual &&
- test_cmp expect actual
-'
-
-test_expect_success GPG 'show good signature with custom format' '
- git verify-commit first-signed &&
- cat >expect <<-\EOF &&
- G
- 13B6F51ECDDE430D
- C O Mitter <committer@example.com>
- 73D758744BE721698EC54E8713B6F51ECDDE430D
- 73D758744BE721698EC54E8713B6F51ECDDE430D
- EOF
- git for-each-ref refs/tags/first-signed \
- --format="$GRADE_FORMAT" >actual &&
- test_cmp expect actual
-'
-test_expect_success GPGSSH 'show good signature with custom format with ssh' '
- test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
- FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") &&
- cat >expect.tmpl <<-\EOF &&
- G
- FINGERPRINT
- principal with number 1
- FINGERPRINT
-
- EOF
- sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect &&
- git for-each-ref refs/tags/eighth-signed-ssh \
- --format="$GRADE_FORMAT" >actual &&
- test_cmp expect actual
-'
-
-test_expect_success GPG 'signature atom with grade option and bad signature' '
- git cat-file commit third-signed >raw &&
- sed -e "s/^file: 3/file: 3 forged/" raw >forged1 &&
- FORGED1=$(git hash-object -w -t commit forged1) &&
- git update-ref refs/tags/third-signed "$FORGED1" &&
- test_must_fail git verify-commit "$FORGED1" &&
-
- cat >expect <<-\EOF &&
- B
- 13B6F51ECDDE430D
- C O Mitter <committer@example.com>
-
-
- EOF
- git for-each-ref refs/tags/third-signed \
- --format="$GRADE_FORMAT" >actual &&
- test_cmp expect actual
-'
-
-test_expect_success GPG 'show untrusted signature with custom format' '
- cat >expect <<-\EOF &&
- U
- 65A0EEA02E30CAD7
- Eris Discordia <discord@example.net>
- F8364A59E07FFE9F4D63005A65A0EEA02E30CAD7
- D4BE22311AD3131E5EDA29A461092E85B7227189
- EOF
- git for-each-ref refs/tags/fourth-signed \
- --format="$GRADE_FORMAT" >actual &&
- test_cmp expect actual
-'
-
-test_expect_success GPG 'show untrusted signature with undefined trust level' '
- cat >expect <<-\EOF &&
- undefined
- 65A0EEA02E30CAD7
- Eris Discordia <discord@example.net>
- F8364A59E07FFE9F4D63005A65A0EEA02E30CAD7
- D4BE22311AD3131E5EDA29A461092E85B7227189
- EOF
- git for-each-ref refs/tags/fourth-signed \
- --format="$TRUSTLEVEL_FORMAT" >actual &&
- test_cmp expect actual
-'
-
-test_expect_success GPG 'show untrusted signature with ultimate trust level' '
- cat >expect <<-\EOF &&
- ultimate
- 13B6F51ECDDE430D
- C O Mitter <committer@example.com>
- 73D758744BE721698EC54E8713B6F51ECDDE430D
- 73D758744BE721698EC54E8713B6F51ECDDE430D
- EOF
- git for-each-ref refs/tags/sixth-signed \
- --format="$TRUSTLEVEL_FORMAT" >actual &&
- test_cmp expect actual
-'
-
-test_expect_success GPG 'show unknown signature with custom format' '
- cat >expect <<-\EOF &&
- E
- 13B6F51ECDDE430D
-
-
-
- EOF
- GNUPGHOME="$GNUPGHOME_NOT_USED" git for-each-ref \
- refs/tags/sixth-signed --format="$GRADE_FORMAT" >actual &&
- test_cmp expect actual
-'
-
-test_expect_success GPG 'show lack of signature with custom format' '
- cat >expect <<-\EOF &&
- N
-
-
-
-
- EOF
- git for-each-ref refs/tags/seventh-unsigned \
- --format="$GRADE_FORMAT" >actual &&
- test_cmp expect actual
-'
+. "$TEST_DIRECTORY"/for-each-ref-tests.sh
test_done
diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
index 9b80ea1e3b..7f060d97bf 100755
--- a/t/t6302-for-each-ref-filter.sh
+++ b/t/t6302-for-each-ref-filter.sh
@@ -754,4 +754,69 @@ test_expect_success 'start after used with custom sort order' '
test_cmp expect actual
'
+test_expect_success 'start after with packed refs' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit default &&
+
+ git update-ref --stdin <<-\EOF &&
+ create refs/heads/branch @
+ create refs/heads/side @
+ create refs/odd/spot @
+ create refs/tags/one @
+ create refs/tags/two @
+ commit
+ EOF
+
+ cat >expect <<-\EOF &&
+ refs/tags/default
+ refs/tags/one
+ refs/tags/two
+ EOF
+
+ git pack-refs --all &&
+ git for-each-ref --format="%(refname)" --start-after=refs/odd/spot >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'start after with packed refs and some loose refs' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit default &&
+
+ git update-ref --stdin <<-\EOF &&
+ create refs/heads/branch @
+ create refs/heads/side @
+ create refs/odd/spot @
+ create refs/tags/one @
+ create refs/tags/two @
+ commit
+ EOF
+
+ git pack-refs --all &&
+
+ git update-ref --stdin <<-\EOF &&
+ create refs/heads/foo @
+ create refs/odd/tee @
+ commit
+ EOF
+
+ cat >expect <<-\EOF &&
+ refs/odd/tee
+ refs/tags/default
+ refs/tags/one
+ refs/tags/two
+ EOF
+
+
+ git for-each-ref --format="%(refname)" --start-after=refs/odd/spot >actual &&
+ test_cmp expect actual
+ )
+'
+
test_done
diff --git a/t/t6423-merge-rename-directories.sh b/t/t6423-merge-rename-directories.sh
index f48ed6d035..533ac85dc8 100755
--- a/t/t6423-merge-rename-directories.sh
+++ b/t/t6423-merge-rename-directories.sh
@@ -4731,7 +4731,7 @@ test_setup_12i () {
mkdir -p source/subdir &&
echo foo >source/subdir/foo &&
- echo bar >source/bar &&
+ printf "%d\n" 1 2 3 4 5 6 7 >source/bar &&
echo baz >source/baz &&
git add source &&
git commit -m orig &&
@@ -4747,6 +4747,7 @@ test_setup_12i () {
git switch B &&
git mv source/bar source/subdir/bar &&
echo more baz >>source/baz &&
+ git add source/baz &&
git commit -m B
)
}
@@ -4758,6 +4759,88 @@ test_expect_success '12i: Directory rename causes rename-to-self' '
git checkout A^0 &&
+ # NOTE: A potentially better resolution would be for
+ # source/bar -> source/subdir/bar
+ # to use the directory rename to become
+ # source/bar -> source/bar
+ # (a rename to self), and thus we end up with bar with
+ # a path conflict (given merge.directoryRenames=conflict).
+ # However, since the relevant renames optimization
+ # prevents us from noticing
+ # source/bar -> source/subdir/bar
+ # as a rename and looking at it just as
+ # delete source/bar
+ # add source/subdir/bar
+ # the directory rename of source/subdir/bar -> source/bar does
+ # not look like a rename-to-self situation but a
+ # rename-on-top-of-other-file situation. We do not want
+ # stage 1 entries from an unrelated file, so we expect an
+ # error about there being a file in the way.
+
+ test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 >out &&
+
+ grep "CONFLICT (implicit dir rename).*source/bar in the way" out &&
+ test_path_is_missing source/bar &&
+ test_path_is_file source/subdir/bar &&
+ test_path_is_file source/baz &&
+
+ git ls-files >actual &&
+ uniq <actual >tracked &&
+ test_line_count = 3 tracked &&
+
+ git status --porcelain -uno >actual &&
+ cat >expect <<-\EOF &&
+ M source/baz
+ R source/bar -> source/subdir/bar
+ EOF
+ test_cmp expect actual
+ )
+'
+
+# Testcase 12i2, Identical to 12i except that source/subdir/bar modified on unrenamed side
+# Commit O: source/{subdir/foo, bar, baz_1}
+# Commit A: source/{foo, bar_2, baz_1}
+# Commit B: source/{subdir/{foo, bar}, baz_2}
+# Expected: source/{foo, bar, baz_2}, with conflicts on
+# source/bar vs. source/subdir/bar
+
+test_setup_12i2 () {
+ git init 12i2 &&
+ (
+ cd 12i2 &&
+
+ mkdir -p source/subdir &&
+ echo foo >source/subdir/foo &&
+ printf "%d\n" 1 2 3 4 5 6 7 >source/bar &&
+ echo baz >source/baz &&
+ git add source &&
+ git commit -m orig &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git switch A &&
+ git mv source/subdir/foo source/foo &&
+ echo 8 >> source/bar &&
+ git add source/bar &&
+ git commit -m A &&
+
+ git switch B &&
+ git mv source/bar source/subdir/bar &&
+ echo more baz >>source/baz &&
+ git add source/baz &&
+ git commit -m B
+ )
+}
+
+test_expect_success '12i2: Directory rename causes rename-to-self' '
+ test_setup_12i2 &&
+ (
+ cd 12i2 &&
+
+ git checkout A^0 &&
+
test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 &&
test_path_is_missing source/subdir &&
@@ -4771,7 +4854,7 @@ test_expect_success '12i: Directory rename causes rename-to-self' '
git status --porcelain -uno >actual &&
cat >expect <<-\EOF &&
UU source/bar
- M source/baz
+ M source/baz
EOF
test_cmp expect actual
)
@@ -4806,6 +4889,7 @@ test_setup_12j () {
git switch B &&
git mv bar subdir/bar &&
echo more baz >>baz &&
+ git add baz &&
git commit -m B
)
}
@@ -4817,10 +4901,29 @@ test_expect_success '12j: Directory rename to root causes rename-to-self' '
git checkout A^0 &&
- test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 &&
-
- test_path_is_missing subdir &&
- test_path_is_file bar &&
+ # NOTE: A potentially better resolution would be for
+ # bar -> subdir/bar
+ # to use the directory rename to become
+ # bar -> bar
+ # (a rename to self), and thus we end up with bar with
+ # a path conflict (given merge.directoryRenames=conflict).
+ # However, since the relevant renames optimization
+ # prevents us from noticing
+ # bar -> subdir/bar
+ # as a rename and looking at it just as
+ # delete bar
+ # add subdir/bar
+ # the directory rename of subdir/bar -> bar does not look
+ # like a rename-to-self situation but a
+ # rename-on-top-of-other-file situation. We do not want
+ # stage 1 entries from an unrelated file, so we expect an
+ # error about there being a file in the way.
+
+ test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 >out &&
+ grep "CONFLICT (implicit dir rename).*bar in the way" out &&
+
+ test_path_is_missing bar &&
+ test_path_is_file subdir/bar &&
test_path_is_file baz &&
git ls-files >actual &&
@@ -4829,8 +4932,8 @@ test_expect_success '12j: Directory rename to root causes rename-to-self' '
git status --porcelain -uno >actual &&
cat >expect <<-\EOF &&
- UU bar
- M baz
+ M baz
+ R bar -> subdir/bar
EOF
test_cmp expect actual
)
@@ -4865,6 +4968,7 @@ test_setup_12k () {
git switch B &&
git mv dirA/bar dirB/bar &&
echo more baz >>dirA/baz &&
+ git add dirA/baz &&
git commit -m B
)
}
@@ -4876,10 +4980,29 @@ test_expect_success '12k: Directory rename with sibling causes rename-to-self' '
git checkout A^0 &&
- test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 &&
-
- test_path_is_missing dirB &&
- test_path_is_file dirA/bar &&
+ # NOTE: A potentially better resolution would be for
+ # dirA/bar -> dirB/bar
+ # to use the directory rename (dirB/ -> dirA/) to become
+ # dirA/bar -> dirA/bar
+ # (a rename to self), and thus we end up with bar with
+ # a path conflict (given merge.directoryRenames=conflict).
+ # However, since the relevant renames optimization
+ # prevents us from noticing
+ # dirA/bar -> dirB/bar
+ # as a rename and looking at it just as
+ # delete dirA/bar
+ # add dirB/bar
+ # the directory rename of dirA/bar -> dirB/bar does
+ # not look like a rename-to-self situation but a
+ # rename-on-top-of-other-file situation. We do not want
+ # stage 1 entries from an unrelated file, so we expect an
+ # error about there being a file in the way.
+
+ test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 >out &&
+ grep "CONFLICT (implicit dir rename).*dirA/bar in the way" out &&
+
+ test_path_is_missing dirA/bar &&
+ test_path_is_file dirB/bar &&
test_path_is_file dirA/baz &&
git ls-files >actual &&
@@ -4888,8 +5011,8 @@ test_expect_success '12k: Directory rename with sibling causes rename-to-self' '
git status --porcelain -uno >actual &&
cat >expect <<-\EOF &&
- UU dirA/bar
- M dirA/baz
+ M dirA/baz
+ R dirA/bar -> dirB/bar
EOF
test_cmp expect actual
)
@@ -5056,6 +5179,25 @@ test_expect_success '12m: Change parent of renamed-dir to symlink on other side'
)
'
+# Testcase 12n, Directory rename transitively makes rename back to self
+#
+# (Since this is a cherry-pick instead of merge, the labels are a bit weird.
+# O, the original commit, is A~1 rather than what branch O points to.)
+#
+# Commit O: tools/hello
+# world
+# Commit A: tools/hello
+# tools/world
+# Commit B: hello
+# In words:
+# A: world -> tools/world
+# B: tools/ -> /, i.e. rename all of tools to toplevel directory
+# delete world
+#
+# Expected:
+# CONFLICT (file location): tools/world vs. world
+#
+
test_setup_12n () {
git init 12n &&
(
@@ -5092,10 +5234,357 @@ test_expect_success '12n: Directory rename transitively makes rename back to sel
git checkout -q B^0 &&
test_must_fail git cherry-pick A^0 >out &&
- grep "CONFLICT (file location).*should perhaps be moved" out
+ test_grep "CONFLICT (file location).*should perhaps be moved" out &&
+
+ # Should have 1 entry for hello, and 2 for world
+ test_stdout_line_count = 3 git ls-files -s &&
+ test_stdout_line_count = 1 git ls-files -s hello &&
+ test_stdout_line_count = 2 git ls-files -s world
+ )
+'
+
+# Testcase 12n2, Directory rename transitively makes rename back to self
+#
+# Commit O: tools/hello
+# world
+# Commit A: tools/hello
+# tools/world
+# Commit B: hello
+# In words:
+# A: world -> tools/world
+# B: tools/ -> /, i.e. rename all of tools to toplevel directory
+# delete world
+#
+# Expected:
+# CONFLICT (file location): tools/world vs. world
+#
+
+test_setup_12n2 () {
+ git init 12n2 &&
+ (
+ cd 12n2 &&
+
+ mkdir tools &&
+ echo hello >tools/hello &&
+ git add tools/hello &&
+ echo world >world &&
+ git add world &&
+ git commit -m "O" &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git switch A &&
+ git mv world tools/world &&
+ git commit -m "Move world into tools/" &&
+
+ git switch B &&
+ git mv tools/hello hello &&
+ git rm world &&
+ git commit -m "Move hello from tools/ to toplevel"
+ )
+}
+
+test_expect_success '12n2: Directory rename transitively makes rename back to self' '
+ test_setup_12n2 &&
+ (
+ cd 12n2 &&
+
+ git checkout -q B^0 &&
+
+ test_might_fail git -c merge.directoryRenames=true merge A^0 >out &&
+
+ # Should have 1 entry for hello, and either 0 or 2 for world
+ #
+ # NOTE: Since merge.directoryRenames=true, there is no path
+ # conflict for world vs. tools/world; it should end up at
+ # world. The fact that world was unmodified on side A, means
+ # there was no content conflict; we should just take the
+ # content from side B -- i.e. delete the file. So merging
+ # could just delete world.
+ #
+ # However, rename-to-self-via-directory-rename is a bit more
+ # challenging. Relax this test to allow world to be treated
+ # as a modify/delete conflict as well, meaning it will have
+ # two higher order stages, that just so happen to match.
+ #
+ test_stdout_line_count = 1 git ls-files -s hello &&
+ test_stdout_line_count = 2 git ls-files -s world &&
+ test_grep "CONFLICT (modify/delete).*world deleted in HEAD" out
)
'
+# Testcase 12o, Directory rename hits other rename source; file still in way on same side
+# Commit O: A/file1_1
+# A/stuff
+# B/file1_2
+# B/stuff
+# C/other
+# Commit A: A/file1_1
+# A/stuff
+# B/stuff
+# C/file1_2
+# C/other
+# Commit B: D/file2_1
+# A/stuff
+# B/file1_2
+# B/stuff
+# A/other
+# In words:
+# A: rename B/file1_2 -> C/file1_2
+# B: rename C/ -> A/
+# rename A/file1_1 -> D/file2_1
+# Rationale:
+# A/stuff is unmodified, it shows up in final output
+# B/stuff is unmodified, it shows up in final output
+# C/other touched on one side (rename to A), so A/other shows up in output
+# A/file1 is renamed to D/file2
+# B/file1 -> C/file1 and even though C/ -> A/, A/file1 is
+# "in the way" so we don't do the directory rename
+# Expected: A/stuff
+# B/stuff
+# A/other
+# D/file2
+# C/file1
+# + CONFLICT (implicit dir rename): A/file1 in way of C/file1
+#
+
+test_setup_12o () {
+ git init 12o &&
+ (
+ cd 12o &&
+
+ mkdir -p A B C &&
+ echo 1 >A/file1 &&
+ echo 2 >B/file1 &&
+ echo other >C/other &&
+ echo Astuff >A/stuff &&
+ echo Bstuff >B/stuff &&
+ git add . &&
+ git commit -m "O" &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git switch A &&
+ git mv B/file1 C/ &&
+ git add . &&
+ git commit -m "A" &&
+
+ git switch B &&
+ mkdir -p D &&
+ git mv A/file1 D/file2 &&
+ git mv C/other A/other &&
+ git add . &&
+ git commit -m "B"
+ )
+}
+
+test_expect_success '12o: Directory rename hits other rename source; file still in way on same side' '
+ test_setup_12o &&
+ (
+ cd 12o &&
+
+ git checkout -q A^0 &&
+
+ test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 >out &&
+
+ test_stdout_line_count = 5 git ls-files -s &&
+ test_stdout_line_count = 1 git ls-files -s A/other &&
+ test_stdout_line_count = 1 git ls-files -s A/stuff &&
+ test_stdout_line_count = 1 git ls-files -s B/stuff &&
+ test_stdout_line_count = 1 git ls-files -s D/file2 &&
+
+ grep "CONFLICT (implicit dir rename).*Existing file/dir at A/file1 in the way" out &&
+ test_stdout_line_count = 1 git ls-files -s C/file1
+ )
+'
+
+# Testcase 12p, Directory rename hits other rename source; file still in way on other side
+# Commit O: A/file1_1
+# A/stuff
+# B/file1_2
+# B/stuff
+# C/other
+# Commit A: D/file2_1
+# A/stuff
+# B/stuff
+# C/file1_2
+# C/other
+# Commit B: A/file1_1
+# A/stuff
+# B/file1_2
+# B/stuff
+# A/other
+# Short version:
+# A: rename A/file1_1 -> D/file2_1
+# rename B/file1_2 -> C/file1_2
+# B: Rename C/ -> A/
+# Rationale:
+# A/stuff is unmodified, it shows up in final output
+# B/stuff is unmodified, it shows up in final output
+# C/other touched on one side (rename to A), so A/other shows up in output
+# A/file1 is renamed to D/file2
+# B/file1 -> C/file1 and even though C/ -> A/, A/file1 is
+# "in the way" so we don't do the directory rename
+# Expected: A/stuff
+# B/stuff
+# A/other
+# D/file2
+# C/file1
+# + CONFLICT (implicit dir rename): A/file1 in way of C/file1
+#
+
+test_setup_12p () {
+ git init 12p &&
+ (
+ cd 12p &&
+
+ mkdir -p A B C &&
+ echo 1 >A/file1 &&
+ echo 2 >B/file1 &&
+ echo other >C/other &&
+ echo Astuff >A/stuff &&
+ echo Bstuff >B/stuff &&
+ git add . &&
+ git commit -m "O" &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git switch A &&
+ mkdir -p D &&
+ git mv A/file1 D/file2 &&
+ git mv B/file1 C/ &&
+ git add . &&
+ git commit -m "A" &&
+
+ git switch B &&
+ git mv C/other A/other &&
+ git add . &&
+ git commit -m "B"
+ )
+}
+
+test_expect_success '12p: Directory rename hits other rename source; file still in way on other side' '
+ test_setup_12p &&
+ (
+ cd 12p &&
+
+ git checkout -q A^0 &&
+
+ test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 >out &&
+
+ test_stdout_line_count = 5 git ls-files -s &&
+ test_stdout_line_count = 1 git ls-files -s A/other &&
+ test_stdout_line_count = 1 git ls-files -s A/stuff &&
+ test_stdout_line_count = 1 git ls-files -s B/stuff &&
+ test_stdout_line_count = 1 git ls-files -s D/file2 &&
+
+ grep "CONFLICT (implicit dir rename).*Existing file/dir at A/file1 in the way" out &&
+ test_stdout_line_count = 1 git ls-files -s C/file1
+ )
+'
+
+# Testcase 12q, Directory rename hits other rename source; file removed though
+# Commit O: A/file1_1
+# A/stuff
+# B/file1_2
+# B/stuff
+# C/other
+# Commit A: A/stuff
+# B/stuff
+# C/file1_2
+# C/other
+# Commit B: D/file2_1
+# A/stuff
+# B/file1_2
+# B/stuff
+# A/other
+# In words:
+# A: delete A/file1_1, rename B/file1_2 -> C/file1_2
+# B: Rename C/ -> A/, rename A/file1_1 -> D/file2_1
+# Rationale:
+# A/stuff is unmodified, it shows up in final output
+# B/stuff is unmodified, it shows up in final output
+# C/other touched on one side (rename to A), so A/other shows up in output
+# A/file1 is rename/delete to D/file2, so two stages for D/file2
+# B/file1 -> C/file1 and even though C/ -> A/, A/file1 as a source was
+# "in the way" (ish) so we don't do the directory rename
+# Expected: A/stuff
+# B/stuff
+# A/other
+# D/file2 (two stages)
+# C/file1
+# + CONFLICT (implicit dir rename): A/file1 in way of C/file1
+# + CONFLICT (rename/delete): D/file2
+#
+
+test_setup_12q () {
+ git init 12q &&
+ (
+ cd 12q &&
+
+ mkdir -p A B C &&
+ echo 1 >A/file1 &&
+ echo 2 >B/file1 &&
+ echo other >C/other &&
+ echo Astuff >A/stuff &&
+ echo Bstuff >B/stuff &&
+ git add . &&
+ git commit -m "O" &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git switch A &&
+ git rm A/file1 &&
+ git mv B/file1 C/ &&
+ git add . &&
+ git commit -m "A" &&
+
+ git switch B &&
+ mkdir -p D &&
+ git mv A/file1 D/file2 &&
+ git mv C/other A/other &&
+ git add . &&
+ git commit -m "B"
+ )
+}
+
+test_expect_success '12q: Directory rename hits other rename source; file removed though' '
+ test_setup_12q &&
+ (
+ cd 12q &&
+
+ git checkout -q A^0 &&
+
+ test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 >out &&
+
+ grep "CONFLICT (rename/delete).*A/file1.*D/file2" out &&
+ grep "CONFLICT (implicit dir rename).*Existing file/dir at A/file1 in the way" out &&
+
+ test_stdout_line_count = 6 git ls-files -s &&
+ test_stdout_line_count = 1 git ls-files -s A/other &&
+ test_stdout_line_count = 1 git ls-files -s A/stuff &&
+ test_stdout_line_count = 1 git ls-files -s B/stuff &&
+ test_stdout_line_count = 2 git ls-files -s D/file2 &&
+
+ # This is a slightly suboptimal resolution; allowing the
+ # rename of C/ -> A/ to affect C/file1 and further rename
+ # it to A/file1 would probably be preferable, but since
+ # A/file1 existed as the source of another rename, allowing
+ # the dir rename of C/file1 -> A/file1 would mean modifying
+ # the code so that renames do not adjust both their source
+ # and target paths in all cases.
+ ! grep "CONFLICT (file location)" out &&
+ test_stdout_line_count = 1 git ls-files -s C/file1
+ )
+'
###########################################################################
# SECTION 13: Checking informational and conflict messages
diff --git a/t/t7005-editor.sh b/t/t7005-editor.sh
index 5fcf281dfb..c490e5707a 100755
--- a/t/t7005-editor.sh
+++ b/t/t7005-editor.sh
@@ -7,125 +7,96 @@ test_description='GIT_EDITOR, core.editor, and stuff'
unset EDITOR VISUAL GIT_EDITOR
test_expect_success 'determine default editor' '
-
vi=$(TERM=vt100 git var GIT_EDITOR) &&
test -n "$vi"
-
'
-if ! expr "$vi" : '[a-z]*$' >/dev/null
-then
- vi=
-fi
-
-for i in GIT_EDITOR core_editor EDITOR VISUAL $vi
-do
- cat >e-$i.sh <<-EOF
- #!$SHELL_PATH
- echo "Edited by $i" >"\$1"
- EOF
- chmod +x e-$i.sh
-done
+test_expect_success setup '
+ if ! expr "$vi" : "[a-z]*$" >/dev/null
+ then
+ vi=
+ fi &&
-if ! test -z "$vi"
-then
- mv e-$vi.sh $vi
-fi
+ for i in GIT_EDITOR core_editor EDITOR VISUAL $vi
+ do
+ write_script e-$i.sh <<-EOF || return 1
+ echo "Edited by $i" >"\$1"
+ EOF
+ done &&
-test_expect_success setup '
+ if ! test -z "$vi"
+ then
+ mv e-$vi.sh $vi
+ fi &&
msg="Hand-edited" &&
test_commit "$msg" &&
- echo "$msg" >expect &&
- git show -s --format=%s > actual &&
- test_cmp expect actual
-
+ test_commit_message HEAD -m "$msg"
'
-TERM=dumb
-export TERM
test_expect_success 'dumb should error out when falling back on vi' '
-
- if git commit --amend
- then
- echo "Oops?"
- false
- else
- : happy
- fi
+ test_must_fail env TERM=dumb git commit --amend
'
test_expect_success 'dumb should prefer EDITOR to VISUAL' '
-
- EDITOR=./e-EDITOR.sh &&
- VISUAL=./e-VISUAL.sh &&
- export EDITOR VISUAL &&
- git commit --amend &&
- test "$(git show -s --format=%s)" = "Edited by EDITOR"
-
+ TERM=dumb EDITOR=./e-EDITOR.sh VISUAL=./e-VISUAL.sh \
+ git commit --amend &&
+ test_commit_message HEAD -m "Edited by EDITOR"
'
-TERM=vt100
-export TERM
for i in $vi EDITOR VISUAL core_editor GIT_EDITOR
do
- echo "Edited by $i" >expect
- unset EDITOR VISUAL GIT_EDITOR
- git config --unset-all core.editor
- case "$i" in
- core_editor)
- git config core.editor ./e-core_editor.sh
- ;;
- [A-Z]*)
- eval "$i=./e-$i.sh"
- export $i
- ;;
- esac
test_expect_success "Using $i" '
- git --exec-path=. commit --amend &&
- git show -s --pretty=oneline |
- sed -e "s/^[0-9a-f]* //" >actual &&
- test_cmp expect actual
+ if test "$i" = core_editor
+ then
+ test_config core.editor ./e-core_editor.sh
+ fi &&
+ (
+ case "$i" in
+ [A-Z]*)
+ eval "$i=./e-$i.sh" &&
+ export $i
+ ;;
+ esac &&
+ PATH="$PWD:$PATH" TERM=vt100 git commit --amend
+ ) &&
+ test_commit_message HEAD -m "Edited by $i"
'
done
-unset EDITOR VISUAL GIT_EDITOR
-git config --unset-all core.editor
-for i in $vi EDITOR VISUAL core_editor GIT_EDITOR
-do
- echo "Edited by $i" >expect
- case "$i" in
- core_editor)
- git config core.editor ./e-core_editor.sh
- ;;
- [A-Z]*)
- eval "$i=./e-$i.sh"
- export $i
- ;;
- esac
- test_expect_success "Using $i (override)" '
- git --exec-path=. commit --amend &&
- git show -s --pretty=oneline |
- sed -e "s/^[0-9a-f]* //" >actual &&
- test_cmp expect actual
- '
-done
+test_expect_success 'Using editors with overrides' '
+ (
+ TERM=vt100 &&
+ export TERM &&
+ for i in $vi EDITOR VISUAL core_editor GIT_EDITOR
+ do
+ echo "Edited by $i" >expect &&
+ case "$i" in
+ core_editor)
+ git config core.editor ./e-core_editor.sh
+ ;;
+ [A-Z]*)
+ eval "$i=./e-$i.sh" &&
+ export $i
+ ;;
+ esac &&
+ PATH="$PWD:$PATH" git commit --amend &&
+ test_commit_message HEAD expect || exit 1
+ done
+ )
+'
test_expect_success 'editor with a space' '
echo "echo space >\"\$1\"" >"e space.sh" &&
chmod a+x "e space.sh" &&
GIT_EDITOR="./e\ space.sh" git commit --amend &&
- test space = "$(git show -s --pretty=format:%s)"
-
+ test_commit_message HEAD -m space
'
-unset GIT_EDITOR
test_expect_success 'core.editor with a space' '
-
- git config core.editor \"./e\ space.sh\" &&
+ test_config core.editor \"./e\ space.sh\" &&
git commit --amend &&
- test space = "$(git show -s --pretty=format:%s)"
-
+ test_commit_message HEAD -m space
'
test_done
diff --git a/t/t7500-commit-template-squash-signoff.sh b/t/t7500-commit-template-squash-signoff.sh
index 1935171d68..66aff8e097 100755
--- a/t/t7500-commit-template-squash-signoff.sh
+++ b/t/t7500-commit-template-squash-signoff.sh
@@ -33,7 +33,7 @@ test_expect_success 'nonexistent template file should return error' '
(
GIT_EDITOR="echo hello >" &&
export GIT_EDITOR &&
- test_must_fail git commit --template "$PWD"/notexist
+ test_must_fail git commit --template "$(pwd)"/notexist
)
'
@@ -43,12 +43,12 @@ test_expect_success 'nonexistent optional template file on command line' '
(
GIT_EDITOR="echo hello >\"\$1\"" &&
export GIT_EDITOR &&
- git commit --template ":(optional)$PWD/notexist"
+ git commit --template ":(optional)$(pwd)/notexist"
)
'
test_expect_success 'nonexistent template file in config should return error' '
- test_config commit.template "$PWD"/notexist &&
+ test_config commit.template "$(pwd)"/notexist &&
(
GIT_EDITOR="echo hello >" &&
export GIT_EDITOR &&
@@ -57,7 +57,7 @@ test_expect_success 'nonexistent template file in config should return error' '
'
test_expect_success 'nonexistent optional template file in config' '
- test_config commit.template ":(optional)$PWD"/notexist &&
+ test_config commit.template ":(optional)$(pwd)"/notexist &&
GIT_EDITOR="echo hello >" git commit --allow-empty &&
git cat-file commit HEAD | sed -e "1,/^$/d" >actual &&
echo hello >expect &&
@@ -65,7 +65,7 @@ test_expect_success 'nonexistent optional template file in config' '
'
# From now on we'll use a template file that exists.
-TEMPLATE="$PWD"/template
+TEMPLATE="$(pwd)"/template
test_expect_success 'unedited template should not commit' '
echo "template line" >"$TEMPLATE" &&
@@ -99,6 +99,15 @@ test_expect_success 'adding real content to a template should commit' '
commit_msg_is "template linecommit message"
'
+test_expect_success 'existent template marked optional should commit' '
+ echo "existent template" >"$TEMPLATE" &&
+ (
+ test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
+ git commit --allow-empty --template ":(optional)$TEMPLATE"
+ ) &&
+ commit_msg_is "existent templatecommit message"
+'
+
test_expect_success '-t option should be short for --template' '
echo "short template" > "$TEMPLATE" &&
echo "new content" >> foo &&
diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
index b37e2018a7..05f6da4ad9 100755
--- a/t/t7502-commit-porcelain.sh
+++ b/t/t7502-commit-porcelain.sh
@@ -956,13 +956,39 @@ test_expect_success 'commit --status with custom comment character' '
test_grep "^; Changes to be committed:" .git/COMMIT_EDITMSG
'
-test_expect_success 'switch core.commentchar' '
+test_expect_success !WITH_BREAKING_CHANGES 'switch core.commentchar' '
test_commit "#foo" foo &&
- GIT_EDITOR=.git/FAKE_EDITOR git -c core.commentChar=auto commit --amend &&
+ cat >config-include <<-\EOF &&
+ [core]
+ commentString=:
+ commentString=%
+ commentChar=auto
+ EOF
+ test_when_finished "rm config-include" &&
+ test_config include.path "$(pwd)/config-include" &&
+ test_config core.commentChar ! &&
+ GIT_EDITOR=.git/FAKE_EDITOR git commit --amend 2>err &&
+ sed -n "s/^hint: *\$//p; s/^hint: //p; s/^warning: //p" err >actual &&
+ cat >expect <<-EOF &&
+ Support for ${SQ}core.commentChar=auto${SQ} is deprecated and will be removed in Git 3.0
+
+ To use the default comment string (#) please run
+
+ git config unset core.commentChar
+ git config unset --file ~/config-include --all core.commentString
+ git config unset --file ~/config-include core.commentChar
+
+ To set a custom comment string please run
+
+ git config set --file ~/config-include core.commentChar <comment string>
+
+ where ${SQ}<comment string>${SQ} is the string you wish to use.
+ EOF
+ test_cmp expect actual &&
test_grep "^; Changes to be committed:" .git/COMMIT_EDITMSG
'
-test_expect_success 'switch core.commentchar but out of options' '
+test_expect_success !WITH_BREAKING_CHANGES 'switch core.commentchar but out of options' '
cat >text <<\EOF &&
# 1
; 2
@@ -982,4 +1008,24 @@ EOF
)
'
+test_expect_success WITH_BREAKING_CHANGES 'core.commentChar=auto is rejected' '
+ test_config core.commentChar auto &&
+ test_must_fail git rev-parse --git-dir 2>err &&
+ sed -n "s/^hint: *\$//p; s/^hint: //p; s/^fatal: //p" err >actual &&
+ cat >expect <<-EOF &&
+ Support for ${SQ}core.commentChar=auto${SQ} has been removed in Git 3.0
+
+ To use the default comment string (#) please run
+
+ git config unset core.commentChar
+
+ To set a custom comment string please run
+
+ git config set core.commentChar <comment string>
+
+ where ${SQ}<comment string>${SQ} is the string you wish to use.
+ EOF
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index cdc1d6fcc7..abad229e9d 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -717,6 +717,17 @@ test_expect_success TTY 'status -s with color.status' '
'
+test_expect_success TTY 'status -s keeps colors with -z' '
+ test_when_finished "rm -f output.*" &&
+ test_terminal git status -s -z >output.raw &&
+ # convert back to newlines to avoid portability issues with
+ # test_decode_color and test_cmp, and to let us use the same expected
+ # output as earlier tests
+ tr "\0" "\n" <output.raw >output.nl &&
+ test_decode_color <output.nl >output &&
+ test_cmp expect output
+'
+
cat >expect <<\EOF
## <YELLOW>main<RESET>...<CYAN>upstream<RESET> [ahead <YELLOW>1<RESET>, behind <CYAN>2<RESET>]
<RED>M<RESET> dir1/modified
diff --git a/t/t7528-signed-commit-ssh.sh b/t/t7528-signed-commit-ssh.sh
index 0f887a3ebe..b50306b9b3 100755
--- a/t/t7528-signed-commit-ssh.sh
+++ b/t/t7528-signed-commit-ssh.sh
@@ -82,7 +82,7 @@ test_expect_success GPGSSH 'create signed commits' '
test_expect_success GPGSSH 'sign commits using literal public keys with ssh-agent' '
test_when_finished "test_unconfig commit.gpgsign" &&
test_config gpg.format ssh &&
- eval $(ssh-agent) &&
+ eval $(ssh-agent -T || ssh-agent) &&
test_when_finished "kill ${SSH_AGENT_PID}" &&
test_when_finished "test_unconfig user.signingkey" &&
mkdir tmpdir &&
diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh
index 611755cc13..73b78bdd88 100755
--- a/t/t7700-repack.sh
+++ b/t/t7700-repack.sh
@@ -838,4 +838,67 @@ test_expect_success '-n overrides repack.updateServerInfo=true' '
test_server_info_missing
'
+test_expect_success 'pending objects are repacked appropriately' '
+ test_when_finished rm -rf pending &&
+ git init pending &&
+
+ (
+ cd pending &&
+
+ # Commit file, a/b/c and never change them.
+ mkdir -p a/b &&
+ echo singleton >file &&
+ echo stuff >a/b/c &&
+ echo more >a/d &&
+ git add file a &&
+ git commit -m "single blobs" &&
+
+ # Files a/d and a/e will not be singletons.
+ echo d >a/d &&
+ echo e >a/e &&
+ git add a &&
+ git commit -m "more blobs" &&
+
+ # This use of a sparse index helps to force
+ # test that the cache-tree is walked, too.
+ git sparse-checkout set --sparse-index a x &&
+
+ # Create staged changes:
+ # * a/e now has multiple versions.
+ # * a/i now has only one version.
+ echo f >a/d &&
+ echo h >a/e &&
+ echo i >a/i &&
+ git add a &&
+
+ # Stage and unstage a change to make use of
+ # resolve-undo cache and how that impacts fsck.
+ mkdir x &&
+ echo y >x/y &&
+ git add x &&
+ xy=$(git rev-parse :x/y) &&
+ git rm --cached x/y &&
+
+ # The blob for x/y must persist through repacks,
+ # but fsck currently ignores the REUC extension
+ # for finding links to the blob.
+ cat >expect <<-EOF &&
+ dangling blob $xy
+ EOF
+
+ # Bring the loose objects into a packfile to avoid
+ # leftovers in next test. Without this, the loose
+ # objects persist and the test succeeds for other
+ # reasons.
+ git repack -adf &&
+ git fsck >out &&
+ test_cmp expect out &&
+
+ # Test path walk version with pack.useSparse.
+ git -c pack.useSparse=true repack -adf --path-walk &&
+ git fsck >out &&
+ test_cmp expect out
+ )
+'
+
test_done
diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh
index ddd273d8dc..614184a097 100755
--- a/t/t7900-maintenance.sh
+++ b/t/t7900-maintenance.sh
@@ -465,6 +465,176 @@ test_expect_success 'maintenance.incremental-repack.auto (when config is unset)'
)
'
+run_and_verify_geometric_pack () {
+ EXPECTED_PACKS="$1" &&
+
+ # Verify that we perform a geometric repack.
+ rm -f "trace2.txt" &&
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+ git maintenance run --task=geometric-repack 2>/dev/null &&
+ test_subcommand git repack -d -l --geometric=2 \
+ --quiet --write-midx <trace2.txt &&
+
+ # Verify that the number of packfiles matches our expectation.
+ ls -l .git/objects/pack/*.pack >packfiles &&
+ test_line_count = "$EXPECTED_PACKS" packfiles &&
+
+ # And verify that there are no loose objects anymore.
+ git count-objects -v >count &&
+ test_grep '^count: 0$' count
+}
+
+test_expect_success 'geometric repacking task' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ git config set maintenance.auto false &&
+ test_commit initial &&
+
+ # The initial repack causes an all-into-one repack.
+ GIT_TRACE2_EVENT="$(pwd)/initial-repack.txt" \
+ git maintenance run --task=geometric-repack 2>/dev/null &&
+ test_subcommand git repack -d -l --cruft --cruft-expiration=2.weeks.ago \
+ --quiet --write-midx <initial-repack.txt &&
+
+ # Repacking should now cause a no-op geometric repack because
+ # no packfiles need to be combined.
+ ls -l .git/objects/pack/*.pack >before &&
+ run_and_verify_geometric_pack 1 &&
+ ls -l .git/objects/pack/*.pack >after &&
+ test_cmp before after &&
+
+ # This incremental change creates a new packfile that only
+ # soaks up loose objects. The packfiles are not getting merged
+ # at this point.
+ test_commit loose &&
+ run_and_verify_geometric_pack 2 &&
+
+ # Both packfiles have 3 objects, so the next run would cause us
+ # to merge all packfiles together. This should be turned into
+ # an all-into-one-repack.
+ GIT_TRACE2_EVENT="$(pwd)/all-into-one-repack.txt" \
+ git maintenance run --task=geometric-repack 2>/dev/null &&
+ test_subcommand git repack -d -l --cruft --cruft-expiration=2.weeks.ago \
+ --quiet --write-midx <all-into-one-repack.txt &&
+
+ # The geometric repack soaks up unreachable objects.
+ echo blob-1 | git hash-object -w --stdin -t blob &&
+ run_and_verify_geometric_pack 2 &&
+
+ # A second unreachable object should be written into another packfile.
+ echo blob-2 | git hash-object -w --stdin -t blob &&
+ run_and_verify_geometric_pack 3 &&
+
+ # And these two small packs should now be merged via the
+ # geometric repack. The large packfile should remain intact.
+ run_and_verify_geometric_pack 2 &&
+
+ # If we now add two more objects and repack twice we should
+ # then see another all-into-one repack. This time around
+ # though, as we have unreachable objects, we should also see a
+ # cruft pack.
+ echo blob-3 | git hash-object -w --stdin -t blob &&
+ echo blob-4 | git hash-object -w --stdin -t blob &&
+ run_and_verify_geometric_pack 3 &&
+ GIT_TRACE2_EVENT="$(pwd)/cruft-repack.txt" \
+ git maintenance run --task=geometric-repack 2>/dev/null &&
+ test_subcommand git repack -d -l --cruft --cruft-expiration=2.weeks.ago \
+ --quiet --write-midx <cruft-repack.txt &&
+ ls .git/objects/pack/*.pack >packs &&
+ test_line_count = 2 packs &&
+ ls .git/objects/pack/*.mtimes >cruft &&
+ test_line_count = 1 cruft
+ )
+'
+
+test_geometric_repack_needed () {
+ NEEDED="$1"
+ GEOMETRIC_CONFIG="$2" &&
+ rm -f trace2.txt &&
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+ git ${GEOMETRIC_CONFIG:+-c maintenance.geometric-repack.$GEOMETRIC_CONFIG} \
+ maintenance run --auto --task=geometric-repack 2>/dev/null &&
+ case "$NEEDED" in
+ true)
+ test_grep "\[\"git\",\"repack\"," trace2.txt;;
+ false)
+ ! test_grep "\[\"git\",\"repack\"," trace2.txt;;
+ *)
+ BUG "invalid parameter: $NEEDED";;
+ esac
+}
+
+test_expect_success 'geometric repacking with --auto' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ # An empty repository does not need repacking, except when
+ # explicitly told to do it.
+ test_geometric_repack_needed false &&
+ test_geometric_repack_needed false auto=0 &&
+ test_geometric_repack_needed false auto=1 &&
+ test_geometric_repack_needed true auto=-1 &&
+
+ test_oid_init &&
+
+ # Loose objects cause a repack when crossing the limit. Note
+ # that the number of objects gets extrapolated by having a look
+ # at the "objects/17/" shard.
+ test_commit "$(test_oid blob17_1)" &&
+ test_geometric_repack_needed false &&
+ test_commit "$(test_oid blob17_2)" &&
+ test_geometric_repack_needed false auto=257 &&
+ test_geometric_repack_needed true auto=256 &&
+
+ # Force another repack.
+ test_commit first &&
+ test_commit second &&
+ test_geometric_repack_needed true auto=-1 &&
+
+ # We now have two packfiles that would be merged together. As
+ # such, the repack should always happen unless the user has
+ # disabled the auto task.
+ test_geometric_repack_needed false auto=0 &&
+ test_geometric_repack_needed true auto=9000
+ )
+'
+
+test_expect_success 'geometric repacking honors configured split factor' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ git config set maintenance.auto false &&
+
+ # Create three different packs with 9, 2 and 1 object, respectively.
+ # This is done so that only a subset of packs would be merged
+ # together so that we can verify that `git repack` receives the
+ # correct geometric factor.
+ for i in $(test_seq 9)
+ do
+ echo first-$i | git hash-object -w --stdin -t blob || return 1
+ done &&
+ git repack --geometric=2 -d &&
+
+ for i in $(test_seq 2)
+ do
+ echo second-$i | git hash-object -w --stdin -t blob || return 1
+ done &&
+ git repack --geometric=2 -d &&
+
+ echo third | git hash-object -w --stdin -t blob &&
+ git repack --geometric=2 -d &&
+
+ test_geometric_repack_needed false splitFactor=2 &&
+ test_geometric_repack_needed true splitFactor=3 &&
+ test_subcommand git repack -d -l --geometric=3 --quiet --write-midx <trace2.txt
+ )
+'
+
test_expect_success 'pack-refs task' '
for n in $(test_seq 1 5)
do
@@ -716,6 +886,76 @@ test_expect_success 'maintenance.strategy inheritance' '
<modified-daily.txt
'
+test_strategy () {
+ STRATEGY="$1"
+ shift
+
+ cat >expect &&
+ rm -f trace2.txt &&
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+ git -c maintenance.strategy=$STRATEGY maintenance run --quiet "$@" &&
+ sed -n 's/{"event":"child_start","sid":"[^/"]*",.*,"argv":\["\(.*\)\"]}/\1/p' <trace2.txt |
+ sed 's/","/ /g' >actual
+ test_cmp expect actual
+}
+
+test_expect_success 'maintenance.strategy is respected' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+
+ test_must_fail git -c maintenance.strategy=unknown maintenance run 2>err &&
+ test_grep "unknown maintenance strategy: .unknown." err &&
+
+ test_strategy incremental <<-\EOF &&
+ git pack-refs --all --prune
+ git reflog expire --all
+ git gc --quiet --no-detach --skip-foreground-tasks
+ EOF
+
+ test_strategy incremental --schedule=weekly <<-\EOF &&
+ git pack-refs --all --prune
+ git prune-packed --quiet
+ git multi-pack-index write --no-progress
+ git multi-pack-index expire --no-progress
+ git multi-pack-index repack --no-progress --batch-size=1
+ git commit-graph write --split --reachable --no-progress
+ EOF
+
+ test_strategy gc <<-\EOF &&
+ git pack-refs --all --prune
+ git reflog expire --all
+ git gc --quiet --no-detach --skip-foreground-tasks
+ EOF
+
+ test_strategy gc --schedule=weekly <<-\EOF &&
+ git pack-refs --all --prune
+ git reflog expire --all
+ git gc --quiet --no-detach --skip-foreground-tasks
+ EOF
+
+ test_strategy geometric <<-\EOF &&
+ git pack-refs --all --prune
+ git reflog expire --all
+ git repack -d -l --geometric=2 --quiet --write-midx
+ git commit-graph write --split --reachable --no-progress
+ git worktree prune --expire 3.months.ago
+ git rerere gc
+ EOF
+
+ test_strategy geometric --schedule=weekly <<-\EOF
+ git pack-refs --all --prune
+ git reflog expire --all
+ git repack -d -l --geometric=2 --quiet --write-midx
+ git commit-graph write --split --reachable --no-progress
+ git worktree prune --expire 3.months.ago
+ git rerere gc
+ EOF
+ )
+'
+
test_expect_success 'register and unregister' '
test_when_finished git config --global --unset-all maintenance.repo &&
@@ -1093,6 +1333,11 @@ test_expect_success 'fails when running outside of a repository' '
nongit test_must_fail git maintenance unregister
'
+test_expect_success 'fails when configured to use an invalid strategy' '
+ test_must_fail git -c maintenance.strategy=invalid maintenance run --schedule=hourly 2>err &&
+ test_grep "unknown maintenance strategy: .invalid." err
+'
+
test_expect_success 'register and unregister bare repo' '
test_when_finished "git config --global --unset-all maintenance.repo || :" &&
test_might_fail git config --global --unset-all maintenance.repo &&
diff --git a/t/t8020-last-modified.sh b/t/t8020-last-modified.sh
new file mode 100755
index 0000000000..61f00bc15c
--- /dev/null
+++ b/t/t8020-last-modified.sh
@@ -0,0 +1,230 @@
+#!/bin/sh
+
+test_description='last-modified tests'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit 1 file &&
+ mkdir a &&
+ test_commit 2 a/file &&
+ mkdir a/b &&
+ test_commit 3 a/b/file
+'
+
+test_expect_success 'cannot run last-modified on two trees' '
+ test_must_fail git last-modified HEAD HEAD~1
+'
+
+check_last_modified() {
+ local indir= &&
+ while test $# != 0
+ do
+ case "$1" in
+ -C)
+ indir="$2"
+ shift
+ ;;
+ *)
+ break
+ ;;
+ esac &&
+ shift
+ done &&
+
+ cat >expect &&
+ git ${indir:+-C "$indir"} last-modified "$@" >tmp.1 &&
+ git name-rev --annotate-stdin --name-only --tags \
+ <tmp.1 >tmp.2 &&
+ tr '\t' ' ' <tmp.2 >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'last-modified non-recursive' '
+ check_last_modified <<-\EOF
+ 3 a
+ 1 file
+ EOF
+'
+
+test_expect_success 'last-modified recursive' '
+ check_last_modified -r <<-\EOF
+ 3 a/b/file
+ 2 a/file
+ 1 file
+ EOF
+'
+
+test_expect_success 'last-modified recursive with show-trees' '
+ check_last_modified -r -t <<-\EOF
+ 3 a
+ 3 a/b
+ 3 a/b/file
+ 2 a/file
+ 1 file
+ EOF
+'
+
+test_expect_success 'last-modified non-recursive with show-trees' '
+ check_last_modified -t <<-\EOF
+ 3 a
+ 1 file
+ EOF
+'
+
+test_expect_success 'last-modified subdir' '
+ check_last_modified a <<-\EOF
+ 3 a
+ EOF
+'
+
+test_expect_success 'last-modified subdir recursive' '
+ check_last_modified -r a <<-\EOF
+ 3 a/b/file
+ 2 a/file
+ EOF
+'
+
+test_expect_success 'last-modified from non-HEAD commit' '
+ check_last_modified HEAD^ <<-\EOF
+ 2 a
+ 1 file
+ EOF
+'
+
+test_expect_success 'last-modified from subdir defaults to root' '
+ check_last_modified -C a <<-\EOF
+ 3 a
+ 1 file
+ EOF
+'
+
+test_expect_success 'last-modified from subdir uses relative pathspecs' '
+ check_last_modified -C a -r b <<-\EOF
+ 3 a/b/file
+ EOF
+'
+
+test_expect_success 'limit last-modified traversal by count' '
+ check_last_modified -1 <<-\EOF
+ 3 a
+ ^2 file
+ EOF
+'
+
+test_expect_success 'limit last-modified traversal by commit' '
+ check_last_modified HEAD~2..HEAD <<-\EOF
+ 3 a
+ ^1 file
+ EOF
+'
+
+test_expect_success 'only last-modified files in the current tree' '
+ git rm -rf a &&
+ git commit -m "remove a" &&
+ check_last_modified <<-\EOF
+ 1 file
+ EOF
+'
+
+test_expect_success 'subdirectory modified via merge' '
+ test_when_finished rm -rf repo &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit base &&
+ git switch --create left &&
+ mkdir subdir &&
+ test_commit left subdir/left &&
+ git switch --create right base &&
+ mkdir subdir &&
+ test_commit right subdir/right &&
+ git switch - &&
+ test_merge merge right &&
+ check_last_modified <<-\EOF
+ merge subdir
+ base base.t
+ EOF
+ )
+'
+
+test_expect_success 'cross merge boundaries in blaming' '
+ git checkout HEAD^0 &&
+ git rm -rf . &&
+ test_commit m1 &&
+ git checkout HEAD^ &&
+ git rm -rf . &&
+ test_commit m2 &&
+ git merge m1 &&
+ check_last_modified <<-\EOF
+ m2 m2.t
+ m1 m1.t
+ EOF
+'
+
+test_expect_success 'last-modified merge for resolved conflicts' '
+ git checkout HEAD^0 &&
+ git rm -rf . &&
+ test_commit c1 conflict &&
+ git checkout HEAD^ &&
+ git rm -rf . &&
+ test_commit c2 conflict &&
+ test_must_fail git merge c1 &&
+ test_commit resolved conflict &&
+ check_last_modified conflict <<-\EOF
+ resolved conflict
+ EOF
+'
+
+
+# Consider `file` with this content through history:
+#
+# A---B---B-------B---B
+# \ /
+# C---D
+test_expect_success 'last-modified merge ignores content from branch' '
+ git checkout HEAD^0 &&
+ git rm -rf . &&
+ test_commit a1 file A &&
+ test_commit a2 file B &&
+ test_commit a3 file C &&
+ test_commit a4 file D &&
+ git checkout a2 &&
+ git merge --no-commit --no-ff a4 &&
+ git checkout a2 -- file &&
+ git merge --continue &&
+ check_last_modified <<-\EOF
+ a2 file
+ EOF
+'
+
+# Consider `file` with this content through history:
+#
+# A---B---B---C---D---B---B
+# \ /
+# B-------B
+test_expect_success 'last-modified merge undoes changes' '
+ git checkout HEAD^0 &&
+ git rm -rf . &&
+ test_commit b1 file A &&
+ test_commit b2 file B &&
+ test_commit b3 file C &&
+ test_commit b4 file D &&
+ git checkout b2 &&
+ test_commit b5 file2 2 &&
+ git checkout b4 &&
+ git merge --no-commit --no-ff b5 &&
+ git checkout b2 -- file &&
+ git merge --continue &&
+ check_last_modified <<-\EOF
+ b5 file2
+ b2 file
+ EOF
+'
+
+test_expect_success 'last-modified complains about unknown arguments' '
+ test_must_fail git last-modified --foo 2>err &&
+ grep "unknown last-modified argument: --foo" err
+'
+
+test_done
diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
index 4dc3d645bf..5685cce6fe 100755
--- a/t/t9300-fast-import.sh
+++ b/t/t9300-fast-import.sh
@@ -2927,16 +2927,16 @@ test_expect_success 'R: blob appears only once' '
# The error message when a space is missing not at the
# end of the line is:
#
-# Missing space after ..
+# missing space after ..
#
# or when extra characters come after the mark at the end
# of the line:
#
-# Garbage after ..
+# garbage after ..
#
# or when the dataref is neither "inline " or a known SHA1,
#
-# Invalid dataref ..
+# invalid dataref ..
#
test_expect_success 'S: initialize for S tests' '
test_tick &&
@@ -3405,15 +3405,15 @@ test_path_fail () {
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" '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_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"
@@ -3422,8 +3422,8 @@ test_path_eol_fail () {
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_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
@@ -3820,7 +3820,7 @@ test_expect_success 'X: replace ref that becomes useless is removed' '
sed -e s/othername/somename/ tmp >tmp2 &&
git fast-import --force <tmp2 2>msgs &&
- grep "Dropping.*since it would point to itself" msgs &&
+ grep "dropping.*since it would point to itself" msgs &&
git show-ref >refs &&
! grep refs/replace refs
)
diff --git a/t/t9305-fast-import-signatures.sh b/t/t9305-fast-import-signatures.sh
new file mode 100755
index 0000000000..c2b4271658
--- /dev/null
+++ b/t/t9305-fast-import-signatures.sh
@@ -0,0 +1,106 @@
+#!/bin/sh
+
+test_description='git fast-import --signed-commits=<mode>'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-gpg.sh"
+
+test_expect_success 'set up unsigned initial commit and import repo' '
+ test_commit first &&
+ git init new
+'
+
+test_expect_success GPG 'set up OpenPGP signed commit' '
+ git checkout -b openpgp-signing main &&
+ echo "Content for OpenPGP signing." >file-sign &&
+ git add file-sign &&
+ git commit -S -m "OpenPGP signed commit" &&
+ OPENPGP_SIGNING=$(git rev-parse --verify openpgp-signing)
+'
+
+test_expect_success GPG 'import OpenPGP signature with --signed-commits=verbatim' '
+ git fast-export --signed-commits=verbatim openpgp-signing >output &&
+ git -C new fast-import --quiet --signed-commits=verbatim <output >log 2>&1 &&
+ IMPORTED=$(git -C new rev-parse --verify refs/heads/openpgp-signing) &&
+ test $OPENPGP_SIGNING = $IMPORTED &&
+ test_must_be_empty log
+'
+
+test_expect_success GPGSM 'set up X.509 signed commit' '
+ git checkout -b x509-signing main &&
+ test_config gpg.format x509 &&
+ test_config user.signingkey $GIT_COMMITTER_EMAIL &&
+ echo "Content for X.509 signing." >file-sign &&
+ git add file-sign &&
+ git commit -S -m "X.509 signed commit" &&
+ X509_SIGNING=$(git rev-parse HEAD)
+'
+
+test_expect_success GPGSM 'import X.509 signature fails with --signed-commits=abort' '
+ git fast-export --signed-commits=verbatim x509-signing >output &&
+ test_must_fail git -C new fast-import --quiet --signed-commits=abort <output
+'
+
+test_expect_success GPGSM 'import X.509 signature with --signed-commits=warn-verbatim' '
+ git -C new fast-import --quiet --signed-commits=warn-verbatim <output >log 2>&1 &&
+ IMPORTED=$(git -C new rev-parse --verify refs/heads/x509-signing) &&
+ test $X509_SIGNING = $IMPORTED &&
+ test_grep "importing a commit signature" log
+'
+
+test_expect_success GPGSSH 'set up SSH signed commit' '
+ git checkout -b ssh-signing main &&
+ test_config gpg.format ssh &&
+ test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+ echo "Content for SSH signing." >file-sign &&
+ git add file-sign &&
+ git commit -S -m "SSH signed commit" &&
+ SSH_SIGNING=$(git rev-parse HEAD)
+'
+
+test_expect_success GPGSSH 'strip SSH signature with --signed-commits=strip' '
+ git fast-export --signed-commits=verbatim ssh-signing >output &&
+ git -C new fast-import --quiet --signed-commits=strip <output >log 2>&1 &&
+ IMPORTED=$(git -C new rev-parse --verify refs/heads/ssh-signing) &&
+ test $SSH_SIGNING != $IMPORTED &&
+ git -C new cat-file commit "$IMPORTED" >actual &&
+ test_grep ! -E "^gpgsig" actual &&
+ test_must_be_empty log
+'
+
+test_expect_success GPG 'setup a commit with dual OpenPGP signatures on its SHA-1 and SHA-256 formats' '
+ # Create a signed SHA-256 commit
+ git init --object-format=sha256 explicit-sha256 &&
+ git -C explicit-sha256 config extensions.compatObjectFormat sha1 &&
+ git -C explicit-sha256 checkout -b dual-signed &&
+ test_commit -C explicit-sha256 A &&
+ echo B >explicit-sha256/B &&
+ git -C explicit-sha256 add B &&
+ test_tick &&
+ git -C explicit-sha256 commit -S -m "signed" B &&
+ SHA256_B=$(git -C explicit-sha256 rev-parse dual-signed) &&
+
+ # Create the corresponding SHA-1 commit
+ SHA1_B=$(git -C explicit-sha256 rev-parse --output-object-format=sha1 dual-signed) &&
+
+ # Check that the resulting SHA-1 commit has both signatures
+ git -C explicit-sha256 cat-file -p $SHA1_B >out &&
+ test_grep -E "^gpgsig " out &&
+ test_grep -E "^gpgsig-sha256 " out
+'
+
+test_expect_success GPG 'strip both OpenPGP signatures with --signed-commits=warn-strip' '
+ git -C explicit-sha256 fast-export --signed-commits=verbatim dual-signed >output &&
+ test_grep -E "^gpgsig sha1 openpgp" output &&
+ test_grep -E "^gpgsig sha256 openpgp" output &&
+ git -C new fast-import --quiet --signed-commits=warn-strip <output >log 2>&1 &&
+ git -C new cat-file commit refs/heads/dual-signed >actual &&
+ test_grep ! -E "^gpgsig " actual &&
+ test_grep ! -E "^gpgsig-sha256 " actual &&
+ test_grep "stripping a commit signature" log >out &&
+ test_line_count = 2 out
+'
+
+test_done
diff --git a/t/t9306-fast-import-signed-tags.sh b/t/t9306-fast-import-signed-tags.sh
new file mode 100755
index 0000000000..363619e7d1
--- /dev/null
+++ b/t/t9306-fast-import-signed-tags.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+
+test_description='git fast-import --signed-tags=<mode>'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-gpg.sh"
+
+test_expect_success 'set up unsigned initial commit and import repo' '
+ test_commit first &&
+ git init new
+'
+
+test_expect_success 'import no signed tag with --signed-tags=abort' '
+ git fast-export --signed-tags=verbatim >output &&
+ git -C new fast-import --quiet --signed-tags=abort <output
+'
+
+test_expect_success GPG 'set up OpenPGP signed tag' '
+ git tag -s -m "OpenPGP signed tag" openpgp-signed first &&
+ OPENPGP_SIGNED=$(git rev-parse --verify refs/tags/openpgp-signed) &&
+ git fast-export --signed-tags=verbatim openpgp-signed >output
+'
+
+test_expect_success GPG 'import OpenPGP signed tag with --signed-tags=abort' '
+ test_must_fail git -C new fast-import --quiet --signed-tags=abort <output
+'
+
+test_expect_success GPG 'import OpenPGP signed tag with --signed-tags=verbatim' '
+ git -C new fast-import --quiet --signed-tags=verbatim <output >log 2>&1 &&
+ IMPORTED=$(git -C new rev-parse --verify refs/tags/openpgp-signed) &&
+ test $OPENPGP_SIGNED = $IMPORTED &&
+ test_must_be_empty log
+'
+
+test_expect_success GPGSM 'setup X.509 signed tag' '
+ test_config gpg.format x509 &&
+ test_config user.signingkey $GIT_COMMITTER_EMAIL &&
+
+ git tag -s -m "X.509 signed tag" x509-signed first &&
+ X509_SIGNED=$(git rev-parse --verify refs/tags/x509-signed) &&
+ git fast-export --signed-tags=verbatim x509-signed >output
+'
+
+test_expect_success GPGSM 'import X.509 signed tag with --signed-tags=warn-strip' '
+ git -C new fast-import --quiet --signed-tags=warn-strip <output >log 2>&1 &&
+ test_grep "stripping a tag signature for tag '\''x509-signed'\''" log &&
+ IMPORTED=$(git -C new rev-parse --verify refs/tags/x509-signed) &&
+ test $X509_SIGNED != $IMPORTED &&
+ git -C new cat-file -p x509-signed >out &&
+ test_grep ! "SIGNED MESSAGE" out
+'
+
+test_expect_success GPGSSH 'setup SSH signed tag' '
+ test_config gpg.format ssh &&
+ test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+
+ git tag -s -m "SSH signed tag" ssh-signed first &&
+ SSH_SIGNED=$(git rev-parse --verify refs/tags/ssh-signed) &&
+ git fast-export --signed-tags=verbatim ssh-signed >output
+'
+
+test_expect_success GPGSSH 'import SSH signed tag with --signed-tags=warn-verbatim' '
+ git -C new fast-import --quiet --signed-tags=warn-verbatim <output >log 2>&1 &&
+ test_grep "importing a tag signature verbatim for tag '\''ssh-signed'\''" log &&
+ IMPORTED=$(git -C new rev-parse --verify refs/tags/ssh-signed) &&
+ test $SSH_SIGNED = $IMPORTED
+'
+
+test_expect_success GPGSSH 'import SSH signed tag with --signed-tags=strip' '
+ git -C new fast-import --quiet --signed-tags=strip <output >log 2>&1 &&
+ test_must_be_empty log &&
+ IMPORTED=$(git -C new rev-parse --verify refs/tags/ssh-signed) &&
+ test $SSH_SIGNED != $IMPORTED &&
+ git -C new cat-file -p ssh-signed >out &&
+ test_grep ! "SSH SIGNATURE" out
+'
+
+test_done
diff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh
index 8f85c69d62..3d153a4805 100755
--- a/t/t9350-fast-export.sh
+++ b/t/t9350-fast-export.sh
@@ -35,6 +35,7 @@ test_expect_success 'setup' '
git commit -m sitzt file2 &&
test_tick &&
git tag -a -m valentin muss &&
+ ANNOTATED_TAG_COUNT=1 &&
git merge -s ours main
'
@@ -229,7 +230,8 @@ EOF
test_expect_success 'set up faked signed tag' '
- git fast-import <signed-tag-import
+ git fast-import <signed-tag-import &&
+ ANNOTATED_TAG_COUNT=$((ANNOTATED_TAG_COUNT + 1))
'
@@ -277,6 +279,42 @@ test_expect_success 'signed-tags=warn-strip' '
test -s err
'
+test_expect_success GPGSM 'setup X.509 signed tag' '
+ test_config gpg.format x509 &&
+ test_config user.signingkey $GIT_COMMITTER_EMAIL &&
+
+ git tag -s -m "X.509 signed tag" x509-signed $(git rev-parse HEAD) &&
+ ANNOTATED_TAG_COUNT=$((ANNOTATED_TAG_COUNT + 1))
+'
+
+test_expect_success GPGSM 'signed-tags=verbatim with X.509' '
+ git fast-export --signed-tags=verbatim x509-signed > output &&
+ test_grep "SIGNED MESSAGE" output
+'
+
+test_expect_success GPGSM 'signed-tags=strip with X.509' '
+ git fast-export --signed-tags=strip x509-signed > output &&
+ test_grep ! "SIGNED MESSAGE" output
+'
+
+test_expect_success GPGSSH 'setup SSH signed tag' '
+ test_config gpg.format ssh &&
+ test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+
+ git tag -s -m "SSH signed tag" ssh-signed $(git rev-parse HEAD) &&
+ ANNOTATED_TAG_COUNT=$((ANNOTATED_TAG_COUNT + 1))
+'
+
+test_expect_success GPGSSH 'signed-tags=verbatim with SSH' '
+ git fast-export --signed-tags=verbatim ssh-signed > output &&
+ test_grep "SSH SIGNATURE" output
+'
+
+test_expect_success GPGSSH 'signed-tags=strip with SSH' '
+ git fast-export --signed-tags=strip ssh-signed > output &&
+ test_grep ! "SSH SIGNATURE" output
+'
+
test_expect_success GPG 'set up signed commit' '
# Generate a commit with both "gpgsig" and "encoding" set, so
@@ -491,8 +529,9 @@ test_expect_success 'fast-export -C -C | fast-import' '
test_expect_success 'fast-export | fast-import when main is tagged' '
git tag -m msg last &&
+ ANNOTATED_TAG_COUNT=$((ANNOTATED_TAG_COUNT + 1)) &&
git fast-export -C -C --signed-tags=strip --all > output &&
- test $(grep -c "^tag " output) = 3
+ test $(grep -c "^tag " output) = $ANNOTATED_TAG_COUNT
'
@@ -506,12 +545,13 @@ test_expect_success 'cope with tagger-less tags' '
TAG=$(git hash-object --literally -t tag -w tag-content) &&
git update-ref refs/tags/sonnenschein $TAG &&
+ ANNOTATED_TAG_COUNT=$((ANNOTATED_TAG_COUNT + 1)) &&
git fast-export -C -C --signed-tags=strip --all > output &&
- test $(grep -c "^tag " output) = 4 &&
+ test $(grep -c "^tag " output) = $ANNOTATED_TAG_COUNT &&
! grep "Unspecified Tagger" output &&
git fast-export -C -C --signed-tags=strip --all \
--fake-missing-tagger > output &&
- test $(grep -c "^tag " output) = 4 &&
+ test $(grep -c "^tag " output) = $ANNOTATED_TAG_COUNT &&
grep "Unspecified Tagger" output
'
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 6650d33fba..964e1f1569 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -11,9 +11,9 @@ test_description='test bash completion'
# untraceable with such ancient Bash versions.
test_untraceable=UnfortunatelyYes
-# Override environment and always use master for the default initial branch
+# Override environment and always use main for the default initial branch
# name for these tests, so that rev completion candidates are as expected.
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./lib-bash.sh
@@ -1453,7 +1453,7 @@ test_expect_success 'git bisect - start subcommand arguments before double-dash
HEAD Z
final Z
initial Z
- master Z
+ main Z
EOF
)
'
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index a28de7b19b..52d7759bf5 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -1708,11 +1708,16 @@ test_set_hash () {
# Detect the hash algorithm in use.
test_detect_hash () {
case "${GIT_TEST_DEFAULT_HASH:-$GIT_TEST_BUILTIN_HASH}" in
- "sha256")
+ *:*)
+ test_hash_algo="${GIT_TEST_DEFAULT_HASH%%:*}"
+ test_compat_hash_algo="${GIT_TEST_DEFAULT_HASH##*:}"
+ test_repo_compat_hash_algo="$test_compat_hash_algo"
+ ;;
+ sha256)
test_hash_algo=sha256
test_compat_hash_algo=sha1
;;
- *)
+ sha1)
test_hash_algo=sha1
test_compat_hash_algo=sha256
;;
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 621cd31ae1..ef0ab7ec2d 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -127,11 +127,18 @@ then
export GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS
fi
-# Explicitly set the default branch name for testing, to avoid the
-# transitory "git init" warning under --verbose.
-: ${GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME:=master}
+# Explicitly set the default branch name for testing, to squelch hints
+# from "git init" during the transition period. Should be removed
+# after we decide to remove ADVICE_DEFAULT_BRANCH_NAME
+if test -z "$WITH_BREAKING_CHANGES"
+then
+ : ${GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME:=master}
+else
+ : ${GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME:=main}
+fi
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
################################################################
# It appears that people try to run tests without building...
GIT_BINARY="${GIT_TEST_INSTALLED:-$GIT_BUILD_DIR}/git$X"
@@ -1917,6 +1924,19 @@ test_lazy_prereq DEFAULT_HASH_ALGORITHM '
test_lazy_prereq DEFAULT_REPO_FORMAT '
test_have_prereq SHA1,REFFILES
'
+# BROKEN_OBJECTS is a test whether we can write deliberately broken objects and
+# expect them to work. When running using SHA-256 mode with SHA-1
+# compatibility, we cannot write such objects because there's no SHA-1
+# compatibility value for a nonexistent object.
+test_lazy_prereq BROKEN_OBJECTS '
+ ! test_have_prereq COMPAT_HASH
+'
+
+# COMPAT_HASH is a test if we're operating in a repository with SHA-256 with
+# SHA-1 compatibility.
+test_lazy_prereq COMPAT_HASH '
+ test -n "$test_repo_compat_hash_algo"
+'
# Ensure that no test accidentally triggers a Git command
# that runs the actual maintenance scheduler, affecting a user's
diff --git a/t/unit-tests/clar/.github/workflows/ci.yml b/t/unit-tests/clar/.github/workflows/ci.yml
index 0065843d17..4d4724222c 100644
--- a/t/unit-tests/clar/.github/workflows/ci.yml
+++ b/t/unit-tests/clar/.github/workflows/ci.yml
@@ -13,23 +13,56 @@ jobs:
platform:
- os: ubuntu-latest
generator: Unix Makefiles
+ env:
+ CFLAGS: "-Werror -Wall -Wextra"
+ - os: ubuntu-latest
+ generator: Unix Makefiles
+ env:
+ CC: "clang"
+ CFLAGS: "-Werror -Wall -Wextra -fsanitize=leak"
+ - os: ubuntu-latest
+ generator: Unix Makefiles
+ image: i386/debian:latest
+ env:
+ CFLAGS: "-Werror -Wall -Wextra"
- os: macos-latest
generator: Unix Makefiles
+ env:
+ CFLAGS: "-Werror -Wall -Wextra"
- os: windows-latest
generator: Visual Studio 17 2022
- os: windows-latest
generator: MSYS Makefiles
+ env:
+ CFLAGS: "-Werror -Wall -Wextra"
- os: windows-latest
generator: MinGW Makefiles
+ env:
+ CFLAGS: "-Werror -Wall -Wextra"
+ fail-fast: false
runs-on: ${{ matrix.platform.os }}
+ container: ${{matrix.platform.image}}
+
+ env:
+ CC: ${{matrix.platform.env.CC}}
+ CFLAGS: ${{matrix.platform.env.CFLAGS}}
steps:
+ - name: Prepare 32 bit container image
+ if: matrix.platform.image == 'i386/debian:latest'
+ run: apt -q update && apt -q -y install cmake gcc libc6-amd64 lib64stdc++6 make python3
- name: Check out
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Build
+ shell: bash
run: |
mkdir build
cd build
cmake .. -G "${{matrix.platform.generator}}"
- cmake --build .
+ cmake --build . --verbose
+ - name: Test
+ shell: bash
+ run: |
+ cd build
+ CTEST_OUTPUT_ON_FAILURE=1 ctest --build-config Debug
diff --git a/t/unit-tests/clar/CMakeLists.txt b/t/unit-tests/clar/CMakeLists.txt
index 12d4af114f..125db05bc1 100644
--- a/t/unit-tests/clar/CMakeLists.txt
+++ b/t/unit-tests/clar/CMakeLists.txt
@@ -1,8 +1,15 @@
+include(CheckFunctionExists)
+
cmake_minimum_required(VERSION 3.16..3.29)
project(clar LANGUAGES C)
-option(BUILD_TESTS "Build test executable" ON)
+option(BUILD_EXAMPLE "Build the example." ON)
+
+check_function_exists(realpath CLAR_HAS_REALPATH)
+if(CLAR_HAS_REALPATH)
+ add_compile_definitions(-DCLAR_HAS_REALPATH)
+endif()
add_library(clar INTERFACE)
target_sources(clar INTERFACE
@@ -25,4 +32,8 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
if(BUILD_TESTING)
add_subdirectory(test)
endif()
+
+ if(BUILD_EXAMPLE)
+ add_subdirectory(example)
+ endif()
endif()
diff --git a/t/unit-tests/clar/README.md b/t/unit-tests/clar/README.md
index a8961c5f10..41595989ca 100644
--- a/t/unit-tests/clar/README.md
+++ b/t/unit-tests/clar/README.md
@@ -26,8 +26,7 @@ Can you count to funk?
~~~~ sh
$ mkdir tests
$ cp -r $CLAR_ROOT/clar* tests
- $ cp $CLAR_ROOT/test/clar_test.h tests
- $ cp $CLAR_ROOT/test/main.c.sample tests/main.c
+ $ cp $CLAR_ROOT/example/*.c tests
~~~~
- **One: Write some tests**
@@ -147,7 +146,7 @@ To use Clar:
1. copy the Clar boilerplate to your test directory
2. copy (and probably modify) the sample `main.c` (from
- `$CLAR_PATH/test/main.c.sample`)
+ `$CLAR_PATH/example/main.c`)
3. run the Clar mixer (a.k.a. `generate.py`) to scan your test directory and
write out the test suite metadata.
4. compile your test files and the Clar boilerplate into a single test
@@ -159,7 +158,7 @@ The Clar boilerplate gives you a set of useful test assertions and features
the `clar.c` and `clar.h` files, plus the code in the `clar/` subdirectory.
You should not need to edit these files.
-The sample `main.c` (i.e. `$CLAR_PATH/test/main.c.sample`) file invokes
+The sample `main.c` (i.e. `$CLAR_PATH/example/main.c`) file invokes
`clar_test(argc, argv)` to run the tests. Usually, you will edit this file
to perform any framework specific initialization and teardown that you need.
@@ -251,11 +250,16 @@ suite.
- `cl_fixture(const char *)`: Gets the full path to a fixture file.
-Please do note that these methods are *always* available whilst running a
-test, even when calling auxiliary/static functions inside the same file.
+### Auxiliary / helper functions
-It's strongly encouraged to perform test assertions in auxiliary methods,
-instead of returning error values. This is considered good Clar style.
+The clar API is always available while running a test, even when calling
+"auxiliary" (helper) functions.
+
+You're encouraged to perform test assertions in those auxiliary
+methods, instead of returning error values. This is considered good
+Clar style. _However_, when you do this, you need to call `cl_invoke`
+to preserve the current state; this ensures that failures are reported
+as coming from the actual test, instead of the auxiliary method.
Style Example:
@@ -310,20 +314,19 @@ static void check_string(const char *str)
void test_example__a_test_with_auxiliary_methods(void)
{
- check_string("foo");
- check_string("bar");
+ cl_invoke(check_string("foo"));
+ cl_invoke(check_string("bar"));
}
~~~~
About Clar
==========
-Clar has been written from scratch by [Vicent Martí](https://github.com/vmg),
-to replace the old testing framework in [libgit2][libgit2].
-
-Do you know what languages are *in* on the SF startup scene? Node.js *and*
-Latin. Follow [@vmg](https://www.twitter.com/vmg) on Twitter to
-receive more lessons on word etymology. You can be hip too.
-
+Clar was originally written by [Vicent Martí](https://github.com/vmg),
+to replace the old testing framework in [libgit2][libgit2]. It is
+currently maintained by [Edward Thomson](https://github.com/ethomson),
+and used by the [libgit2][libgit2] and [git][git] projects, amongst
+others.
[libgit2]: https://github.com/libgit2/libgit2
+[git]: https://github.com/git/git
diff --git a/t/unit-tests/clar/clar.c b/t/unit-tests/clar/clar.c
index 03a3aa8e87..d6176e50b2 100644
--- a/t/unit-tests/clar/clar.c
+++ b/t/unit-tests/clar/clar.c
@@ -79,6 +79,8 @@
# else
# define p_snprintf snprintf
# endif
+
+# define localtime_r(timer, buf) (localtime_s(buf, timer) == 0 ? buf : NULL)
#else
# include <sys/wait.h> /* waitpid(2) */
# include <unistd.h>
@@ -150,7 +152,6 @@ static struct {
enum cl_output_format output_format;
- int report_errors_only;
int exit_on_error;
int verbosity;
@@ -164,6 +165,10 @@ static struct {
struct clar_report *reports;
struct clar_report *last_report;
+ const char *invoke_file;
+ const char *invoke_func;
+ size_t invoke_line;
+
void (*local_cleanup)(void *);
void *local_cleanup_payload;
@@ -190,7 +195,7 @@ struct clar_suite {
};
/* From clar_print_*.c */
-static void clar_print_init(int test_count, int suite_count, const char *suite_names);
+static void clar_print_init(int test_count, int suite_count);
static void clar_print_shutdown(int test_count, int suite_count, int error_count);
static void clar_print_error(int num, const struct clar_report *report, const struct clar_error *error);
static void clar_print_ontest(const char *suite_name, const char *test_name, int test_number, enum cl_test_status failed);
@@ -199,8 +204,10 @@ static void clar_print_onabortv(const char *msg, va_list argp);
static void clar_print_onabort(const char *msg, ...);
/* From clar_sandbox.c */
-static void clar_unsandbox(void);
-static void clar_sandbox(void);
+static void clar_tempdir_init(void);
+static void clar_tempdir_shutdown(void);
+static int clar_sandbox_create(const char *suite_name, const char *test_name);
+static int clar_sandbox_cleanup(void);
/* From summary.h */
static struct clar_summary *clar_summary_init(const char *filename);
@@ -304,6 +311,8 @@ clar_run_test(
CL_TRACE(CL_TRACE__TEST__BEGIN);
+ clar_sandbox_create(suite->name, test->name);
+
_clar.last_report->start = time(NULL);
clar_time_now(&start);
@@ -328,9 +337,13 @@ clar_run_test(
if (_clar.local_cleanup != NULL)
_clar.local_cleanup(_clar.local_cleanup_payload);
+ clar__clear_invokepoint();
+
if (cleanup->ptr != NULL)
cleanup->ptr();
+ clar_sandbox_cleanup();
+
CL_TRACE(CL_TRACE__TEST__END);
_clar.tests_ran++;
@@ -339,11 +352,7 @@ clar_run_test(
_clar.local_cleanup = NULL;
_clar.local_cleanup_payload = NULL;
- if (_clar.report_errors_only) {
- clar_report_errors(_clar.last_report);
- } else {
- clar_print_ontest(suite->name, test->name, _clar.tests_ran, _clar.last_report->status);
- }
+ clar_print_ontest(suite->name, test->name, _clar.tests_ran, _clar.last_report->status);
}
static void
@@ -360,8 +369,7 @@ clar_run_suite(const struct clar_suite *suite, const char *filter)
if (_clar.exit_on_error && _clar.total_errors)
return;
- if (!_clar.report_errors_only)
- clar_print_onsuite(suite->name, ++_clar.suites_ran);
+ clar_print_onsuite(suite->name, ++_clar.suites_ran);
_clar.active_suite = suite->name;
_clar.active_test = NULL;
@@ -428,12 +436,12 @@ clar_usage(const char *arg)
printf(" -iname Include the suite with `name`\n");
printf(" -xname Exclude the suite with `name`\n");
printf(" -v Increase verbosity (show suite names)\n");
- printf(" -q Only report tests that had an error\n");
+ printf(" -q Decrease verbosity, inverse to -v\n");
printf(" -Q Quit as soon as a test fails\n");
printf(" -t Display results in tap format\n");
printf(" -l Print suite names\n");
printf(" -r[filename] Write summary file (to the optional filename)\n");
- exit(-1);
+ exit(1);
}
static void
@@ -441,18 +449,11 @@ clar_parse_args(int argc, char **argv)
{
int i;
- /* Verify options before execute */
for (i = 1; i < argc; ++i) {
char *argument = argv[i];
- if (argument[0] != '-' || argument[1] == '\0'
- || strchr("sixvqQtlr", argument[1]) == NULL) {
+ if (argument[0] != '-' || argument[1] == '\0')
clar_usage(argv[0]);
- }
- }
-
- for (i = 1; i < argc; ++i) {
- char *argument = argv[i];
switch (argument[1]) {
case 's':
@@ -465,8 +466,13 @@ clar_parse_args(int argc, char **argv)
argument += offset;
arglen = strlen(argument);
- if (arglen == 0)
- clar_usage(argv[0]);
+ if (arglen == 0) {
+ if (i + 1 == argc)
+ clar_usage(argv[0]);
+
+ argument = argv[++i];
+ arglen = strlen(argument);
+ }
for (j = 0; j < _clar_suite_count; ++j) {
suitelen = strlen(_clar_suites[j].name);
@@ -483,9 +489,6 @@ clar_parse_args(int argc, char **argv)
++found;
- if (!exact)
- _clar.verbosity = MAX(_clar.verbosity, 1);
-
switch (action) {
case 's': {
struct clar_explicit *explicit;
@@ -517,23 +520,37 @@ clar_parse_args(int argc, char **argv)
if (!found)
clar_abort("No suite matching '%s' found.\n", argument);
+
break;
}
case 'q':
- _clar.report_errors_only = 1;
+ if (argument[2] != '\0')
+ clar_usage(argv[0]);
+
+ _clar.verbosity--;
break;
case 'Q':
+ if (argument[2] != '\0')
+ clar_usage(argv[0]);
+
_clar.exit_on_error = 1;
break;
case 't':
+ if (argument[2] != '\0')
+ clar_usage(argv[0]);
+
_clar.output_format = CL_OUTPUT_TAP;
break;
case 'l': {
size_t j;
+
+ if (argument[2] != '\0')
+ clar_usage(argv[0]);
+
printf("Test suites (use -s<name> to run just one):\n");
for (j = 0; j < _clar_suite_count; ++j)
printf(" %3d: %s\n", (int)j, _clar_suites[j].name);
@@ -542,23 +559,27 @@ clar_parse_args(int argc, char **argv)
}
case 'v':
+ if (argument[2] != '\0')
+ clar_usage(argv[0]);
+
_clar.verbosity++;
break;
case 'r':
_clar.write_summary = 1;
free(_clar.summary_filename);
+
if (*(argument + 2)) {
if ((_clar.summary_filename = strdup(argument + 2)) == NULL)
clar_abort("Failed to allocate summary filename.\n");
} else {
_clar.summary_filename = NULL;
}
+
break;
default:
- clar_abort("Unexpected commandline argument '%s'.\n",
- argument[1]);
+ clar_usage(argv[0]);
}
}
}
@@ -571,11 +592,7 @@ clar_test_init(int argc, char **argv)
if (argc > 1)
clar_parse_args(argc, argv);
- clar_print_init(
- (int)_clar_callback_count,
- (int)_clar_suite_count,
- ""
- );
+ clar_print_init((int)_clar_callback_count, (int)_clar_suite_count);
if (!_clar.summary_filename &&
(summary_env = getenv("CLAR_SUMMARY")) != NULL) {
@@ -591,7 +608,7 @@ clar_test_init(int argc, char **argv)
if (_clar.write_summary)
_clar.summary = clar_summary_init(_clar.summary_filename);
- clar_sandbox();
+ clar_tempdir_init();
}
int
@@ -623,7 +640,7 @@ clar_test_shutdown(void)
_clar.total_errors
);
- clar_unsandbox();
+ clar_tempdir_shutdown();
if (_clar.write_summary && clar_summary_shutdown(_clar.summary) < 0)
clar_abort("Failed to write the summary file '%s: %s.\n",
@@ -635,6 +652,14 @@ clar_test_shutdown(void)
}
for (report = _clar.reports; report; report = report_next) {
+ struct clar_error *error, *error_next;
+
+ for (error = report->errors; error; error = error_next) {
+ free(error->description);
+ error_next = error->next;
+ free(error);
+ }
+
report_next = report->next;
free(report);
}
@@ -660,7 +685,7 @@ static void abort_test(void)
clar_print_onabort(
"Fatal error: a cleanup method raised an exception.\n");
clar_report_errors(_clar.last_report);
- exit(-1);
+ exit(1);
}
CL_TRACE(CL_TRACE__TEST__LONGJMP);
@@ -695,9 +720,9 @@ void clar__fail(
_clar.last_report->last_error = error;
- error->file = file;
- error->function = function;
- error->line_number = line;
+ error->file = _clar.invoke_file ? _clar.invoke_file : file;
+ error->function = _clar.invoke_func ? _clar.invoke_func : function;
+ error->line_number = _clar.invoke_line ? _clar.invoke_line : line;
error->error_msg = error_msg;
if (description != NULL &&
@@ -754,7 +779,12 @@ void clar__assert_equal(
p_snprintf(buf, sizeof(buf), "'%s' != '%s' (at byte %d)",
s1, s2, pos);
} else {
- p_snprintf(buf, sizeof(buf), "'%s' != '%s'", s1, s2);
+ const char *q1 = s1 ? "'" : "";
+ const char *q2 = s2 ? "'" : "";
+ s1 = s1 ? s1 : "NULL";
+ s2 = s2 ? s2 : "NULL";
+ p_snprintf(buf, sizeof(buf), "%s%s%s != %s%s%s",
+ q1, s1, q1, q2, s2, q2);
}
}
}
@@ -767,12 +797,17 @@ void clar__assert_equal(
if (!is_equal) {
if (s1 && s2) {
int pos;
- for (pos = 0; s1[pos] == s2[pos] && pos < len; ++pos)
+ for (pos = 0; pos < len && s1[pos] == s2[pos]; ++pos)
/* find differing byte offset */;
p_snprintf(buf, sizeof(buf), "'%.*s' != '%.*s' (at byte %d)",
len, s1, len, s2, pos);
} else {
- p_snprintf(buf, sizeof(buf), "'%.*s' != '%.*s'", len, s1, len, s2);
+ const char *q1 = s1 ? "'" : "";
+ const char *q2 = s2 ? "'" : "";
+ s1 = s1 ? s1 : "NULL";
+ s2 = s2 ? s2 : "NULL";
+ p_snprintf(buf, sizeof(buf), "%s%.*s%s != %s%.*s%s",
+ q1, len, s1, q1, q2, len, s2, q2);
}
}
}
@@ -790,7 +825,12 @@ void clar__assert_equal(
p_snprintf(buf, sizeof(buf), "'%ls' != '%ls' (at byte %d)",
wcs1, wcs2, pos);
} else {
- p_snprintf(buf, sizeof(buf), "'%ls' != '%ls'", wcs1, wcs2);
+ const char *q1 = wcs1 ? "'" : "";
+ const char *q2 = wcs2 ? "'" : "";
+ wcs1 = wcs1 ? wcs1 : L"NULL";
+ wcs2 = wcs2 ? wcs2 : L"NULL";
+ p_snprintf(buf, sizeof(buf), "%s%ls%s != %s%ls%s",
+ q1, wcs1, q1, q2, wcs2, q2);
}
}
}
@@ -803,12 +843,17 @@ void clar__assert_equal(
if (!is_equal) {
if (wcs1 && wcs2) {
int pos;
- for (pos = 0; wcs1[pos] == wcs2[pos] && pos < len; ++pos)
+ for (pos = 0; pos < len && wcs1[pos] == wcs2[pos]; ++pos)
/* find differing byte offset */;
p_snprintf(buf, sizeof(buf), "'%.*ls' != '%.*ls' (at byte %d)",
len, wcs1, len, wcs2, pos);
} else {
- p_snprintf(buf, sizeof(buf), "'%.*ls' != '%.*ls'", len, wcs1, len, wcs2);
+ const char *q1 = wcs1 ? "'" : "";
+ const char *q2 = wcs2 ? "'" : "";
+ wcs1 = wcs1 ? wcs1 : L"NULL";
+ wcs2 = wcs2 ? wcs2 : L"NULL";
+ p_snprintf(buf, sizeof(buf), "%s%.*ls%s != %s%.*ls%s",
+ q1, len, wcs1, q1, q2, len, wcs2, q2);
}
}
}
@@ -850,6 +895,23 @@ void cl_set_cleanup(void (*cleanup)(void *), void *opaque)
_clar.local_cleanup_payload = opaque;
}
+void clar__set_invokepoint(
+ const char *file,
+ const char *func,
+ size_t line)
+{
+ _clar.invoke_file = file;
+ _clar.invoke_func = func;
+ _clar.invoke_line = line;
+}
+
+void clar__clear_invokepoint(void)
+{
+ _clar.invoke_file = NULL;
+ _clar.invoke_func = NULL;
+ _clar.invoke_line = 0;
+}
+
#include "clar/sandbox.h"
#include "clar/fixtures.h"
#include "clar/fs.h"
diff --git a/t/unit-tests/clar/clar.h b/t/unit-tests/clar/clar.h
index 8c22382bd5..ca72292ae9 100644
--- a/t/unit-tests/clar/clar.h
+++ b/t/unit-tests/clar/clar.h
@@ -8,6 +8,25 @@
#define __CLAR_TEST_H__
#include <stdlib.h>
+#include <limits.h>
+
+#if defined(_WIN32) && defined(CLAR_WIN32_LONGPATHS)
+# define CLAR_MAX_PATH 4096
+#elif defined(_WIN32)
+# define CLAR_MAX_PATH MAX_PATH
+#else
+# define CLAR_MAX_PATH PATH_MAX
+#endif
+
+#ifndef CLAR_SELFTEST
+# define CLAR_CURRENT_FILE __FILE__
+# define CLAR_CURRENT_LINE __LINE__
+# define CLAR_CURRENT_FUNC __func__
+#else
+# define CLAR_CURRENT_FILE "file"
+# define CLAR_CURRENT_LINE 42
+# define CLAR_CURRENT_FUNC "func"
+#endif
enum cl_test_status {
CL_TEST_OK,
@@ -30,6 +49,7 @@ void clar_test_shutdown(void);
int clar_test(int argc, char *argv[]);
const char *clar_sandbox_path(void);
+const char *clar_tempdir_path(void);
void cl_set_cleanup(void (*cleanup)(void *), void *opaque);
void cl_fs_cleanup(void);
@@ -84,18 +104,32 @@ const char *cl_fixture_basename(const char *fixture_name);
#endif
/**
+ * Invoke a helper function, which itself will use `cl_assert`
+ * constructs. This will preserve the stack information of the
+ * current call point, so that function name and line number
+ * information is shown from the line of the test, instead of
+ * the helper function.
+ */
+#define cl_invoke(expr) \
+ do { \
+ clar__set_invokepoint(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE); \
+ expr; \
+ clar__clear_invokepoint(); \
+ } while(0)
+
+/**
* Assertion macros with explicit error message
*/
-#define cl_must_pass_(expr, desc) clar__assert((expr) >= 0, __FILE__, __func__, __LINE__, "Function call failed: " #expr, desc, 1)
-#define cl_must_fail_(expr, desc) clar__assert((expr) < 0, __FILE__, __func__, __LINE__, "Expected function call to fail: " #expr, desc, 1)
-#define cl_assert_(expr, desc) clar__assert((expr) != 0, __FILE__, __func__, __LINE__, "Expression is not true: " #expr, desc, 1)
+#define cl_must_pass_(expr, desc) clar__assert((expr) >= 0, CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Function call failed: " #expr, desc, 1)
+#define cl_must_fail_(expr, desc) clar__assert((expr) < 0, CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Expected function call to fail: " #expr, desc, 1)
+#define cl_assert_(expr, desc) clar__assert((expr) != 0, CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Expression is not true: " #expr, desc, 1)
/**
* Check macros with explicit error message
*/
-#define cl_check_pass_(expr, desc) clar__assert((expr) >= 0, __FILE__, __func__, __LINE__, "Function call failed: " #expr, desc, 0)
-#define cl_check_fail_(expr, desc) clar__assert((expr) < 0, __FILE__, __func__, __LINE__, "Expected function call to fail: " #expr, desc, 0)
-#define cl_check_(expr, desc) clar__assert((expr) != 0, __FILE__, __func__, __LINE__, "Expression is not true: " #expr, desc, 0)
+#define cl_check_pass_(expr, desc) clar__assert((expr) >= 0, CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Function call failed: " #expr, desc, 0)
+#define cl_check_fail_(expr, desc) clar__assert((expr) < 0, CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Expected function call to fail: " #expr, desc, 0)
+#define cl_check_(expr, desc) clar__assert((expr) != 0, CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Expression is not true: " #expr, desc, 0)
/**
* Assertion macros with no error message
@@ -114,33 +148,33 @@ const char *cl_fixture_basename(const char *fixture_name);
/**
* Forced failure/warning
*/
-#define cl_fail(desc) clar__fail(__FILE__, __func__, __LINE__, "Test failed.", desc, 1)
-#define cl_warning(desc) clar__fail(__FILE__, __func__, __LINE__, "Warning during test execution:", desc, 0)
+#define cl_fail(desc) clar__fail(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Test failed.", desc, 1)
+#define cl_warning(desc) clar__fail(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Warning during test execution:", desc, 0)
#define cl_skip() clar__skip()
/**
* Typed assertion macros
*/
-#define cl_assert_equal_s(s1,s2) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%s", (s1), (s2))
-#define cl_assert_equal_s_(s1,s2,note) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%s", (s1), (s2))
+#define cl_assert_equal_s(s1,s2) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #s1 " != " #s2, 1, "%s", (s1), (s2))
+#define cl_assert_equal_s_(s1,s2,note) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%s", (s1), (s2))
-#define cl_assert_equal_wcs(wcs1,wcs2) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2, 1, "%ls", (wcs1), (wcs2))
-#define cl_assert_equal_wcs_(wcs1,wcs2,note) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%ls", (wcs1), (wcs2))
+#define cl_assert_equal_wcs(wcs1,wcs2) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #wcs1 " != " #wcs2, 1, "%ls", (wcs1), (wcs2))
+#define cl_assert_equal_wcs_(wcs1,wcs2,note) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%ls", (wcs1), (wcs2))
-#define cl_assert_equal_strn(s1,s2,len) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%.*s", (s1), (s2), (int)(len))
-#define cl_assert_equal_strn_(s1,s2,len,note) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%.*s", (s1), (s2), (int)(len))
+#define cl_assert_equal_strn(s1,s2,len) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #s1 " != " #s2, 1, "%.*s", (s1), (s2), (int)(len))
+#define cl_assert_equal_strn_(s1,s2,len,note) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%.*s", (s1), (s2), (int)(len))
-#define cl_assert_equal_wcsn(wcs1,wcs2,len) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2, 1, "%.*ls", (wcs1), (wcs2), (int)(len))
-#define cl_assert_equal_wcsn_(wcs1,wcs2,len,note) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%.*ls", (wcs1), (wcs2), (int)(len))
+#define cl_assert_equal_wcsn(wcs1,wcs2,len) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #wcs1 " != " #wcs2, 1, "%.*ls", (wcs1), (wcs2), (int)(len))
+#define cl_assert_equal_wcsn_(wcs1,wcs2,len,note) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%.*ls", (wcs1), (wcs2), (int)(len))
-#define cl_assert_equal_i(i1,i2) clar__assert_equal(__FILE__,__func__,__LINE__,#i1 " != " #i2, 1, "%d", (int)(i1), (int)(i2))
-#define cl_assert_equal_i_(i1,i2,note) clar__assert_equal(__FILE__,__func__,__LINE__,#i1 " != " #i2 " (" #note ")", 1, "%d", (i1), (i2))
-#define cl_assert_equal_i_fmt(i1,i2,fmt) clar__assert_equal(__FILE__,__func__,__LINE__,#i1 " != " #i2, 1, (fmt), (int)(i1), (int)(i2))
+#define cl_assert_equal_i(i1,i2) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,#i1 " != " #i2, 1, "%d", (int)(i1), (int)(i2))
+#define cl_assert_equal_i_(i1,i2,note) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,#i1 " != " #i2 " (" #note ")", 1, "%d", (i1), (i2))
+#define cl_assert_equal_i_fmt(i1,i2,fmt) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,#i1 " != " #i2, 1, (fmt), (int)(i1), (int)(i2))
-#define cl_assert_equal_b(b1,b2) clar__assert_equal(__FILE__,__func__,__LINE__,#b1 " != " #b2, 1, "%d", (int)((b1) != 0),(int)((b2) != 0))
+#define cl_assert_equal_b(b1,b2) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,#b1 " != " #b2, 1, "%d", (int)((b1) != 0),(int)((b2) != 0))
-#define cl_assert_equal_p(p1,p2) clar__assert_equal(__FILE__,__func__,__LINE__,"Pointer mismatch: " #p1 " != " #p2, 1, "%p", (p1), (p2))
+#define cl_assert_equal_p(p1,p2) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"Pointer mismatch: " #p1 " != " #p2, 1, "%p", (p1), (p2))
void clar__skip(void);
@@ -170,4 +204,11 @@ void clar__assert_equal(
const char *fmt,
...);
+void clar__set_invokepoint(
+ const char *file,
+ const char *func,
+ size_t line);
+
+void clar__clear_invokepoint(void);
+
#endif
diff --git a/t/unit-tests/clar/clar/fixtures.h b/t/unit-tests/clar/clar/fixtures.h
index 6ec6423484..9f1023df59 100644
--- a/t/unit-tests/clar/clar/fixtures.h
+++ b/t/unit-tests/clar/clar/fixtures.h
@@ -2,7 +2,7 @@
static const char *
fixture_path(const char *base, const char *fixture_name)
{
- static char _path[4096];
+ static char _path[CLAR_MAX_PATH];
size_t root_len;
root_len = strlen(base);
@@ -28,7 +28,7 @@ const char *cl_fixture(const char *fixture_name)
void cl_fixture_sandbox(const char *fixture_name)
{
- fs_copy(cl_fixture(fixture_name), _clar_path);
+ fs_copy(cl_fixture(fixture_name), clar_sandbox_path());
}
const char *cl_fixture_basename(const char *fixture_name)
@@ -45,6 +45,6 @@ const char *cl_fixture_basename(const char *fixture_name)
void cl_fixture_cleanup(const char *fixture_name)
{
- fs_rm(fixture_path(_clar_path, cl_fixture_basename(fixture_name)));
+ fs_rm(fixture_path(clar_sandbox_path(), cl_fixture_basename(fixture_name)));
}
#endif
diff --git a/t/unit-tests/clar/clar/fs.h b/t/unit-tests/clar/clar/fs.h
index 2203743fb4..f1311d91e8 100644
--- a/t/unit-tests/clar/clar/fs.h
+++ b/t/unit-tests/clar/clar/fs.h
@@ -8,12 +8,6 @@
#ifdef _WIN32
-#ifdef CLAR_WIN32_LONGPATHS
-# define CLAR_MAX_PATH 4096
-#else
-# define CLAR_MAX_PATH MAX_PATH
-#endif
-
#define RM_RETRY_COUNT 5
#define RM_RETRY_DELAY 10
@@ -296,7 +290,7 @@ void
cl_fs_cleanup(void)
{
#ifdef CLAR_FIXTURE_PATH
- fs_rm(fixture_path(_clar_path, "*"));
+ fs_rm(fixture_path(clar_tempdir_path(), "*"));
#else
((void)fs_copy); /* unused */
#endif
@@ -371,17 +365,19 @@ static void
fs_copydir_helper(const char *source, const char *dest, int dest_mode)
{
DIR *source_dir;
- struct dirent *d;
mkdir(dest, dest_mode);
cl_assert_(source_dir = opendir(source), "Could not open source dir");
- for (;;) {
+ while (1) {
+ struct dirent *d;
char *child;
errno = 0;
- if ((d = readdir(source_dir)) == NULL)
+ d = readdir(source_dir);
+ if (!d)
break;
+
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
continue;
@@ -479,15 +475,18 @@ static void
fs_rmdir_helper(const char *path)
{
DIR *dir;
- struct dirent *d;
cl_assert_(dir = opendir(path), "Could not open dir");
- for (;;) {
+
+ while (1) {
+ struct dirent *d;
char *child;
errno = 0;
- if ((d = readdir(dir)) == NULL)
+ d = readdir(dir);
+ if (!d)
break;
+
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
continue;
@@ -524,7 +523,7 @@ fs_rm(const char *path)
void
cl_fs_cleanup(void)
{
- clar_unsandbox();
- clar_sandbox();
+ clar_tempdir_shutdown();
+ clar_tempdir_init();
}
#endif
diff --git a/t/unit-tests/clar/clar/print.h b/t/unit-tests/clar/clar/print.h
index 69d0ee967e..89b66591d7 100644
--- a/t/unit-tests/clar/clar/print.h
+++ b/t/unit-tests/clar/clar/print.h
@@ -1,9 +1,13 @@
/* clap: clar protocol, the traditional clar output format */
-static void clar_print_clap_init(int test_count, int suite_count, const char *suite_names)
+static void clar_print_clap_init(int test_count, int suite_count)
{
(void)test_count;
- printf("Loaded %d suites: %s\n", (int)suite_count, suite_names);
+
+ if (_clar.verbosity < 0)
+ return;
+
+ printf("Loaded %d suites:\n", (int)suite_count);
printf("Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')\n");
}
@@ -13,10 +17,27 @@ static void clar_print_clap_shutdown(int test_count, int suite_count, int error_
(void)suite_count;
(void)error_count;
- printf("\n\n");
+ if (_clar.verbosity >= 0)
+ printf("\n\n");
clar_report_all();
}
+
+static void clar_print_indented(const char *str, int indent)
+{
+ const char *bol, *eol;
+
+ for (bol = str; *bol; bol = eol) {
+ eol = strchr(bol, '\n');
+ if (eol)
+ eol++;
+ else
+ eol = bol + strlen(bol);
+ printf("%*s%.*s", indent, "", (int)(eol - bol), bol);
+ }
+ putc('\n', stdout);
+}
+
static void clar_print_clap_error(int num, const struct clar_report *report, const struct clar_error *error)
{
printf(" %d) Failure:\n", num);
@@ -27,10 +48,10 @@ static void clar_print_clap_error(int num, const struct clar_report *report, con
error->file,
error->line_number);
- printf(" %s\n", error->error_msg);
+ clar_print_indented(error->error_msg, 2);
if (error->description != NULL)
- printf(" %s\n", error->description);
+ clar_print_indented(error->description, 2);
printf("\n");
fflush(stdout);
@@ -41,14 +62,17 @@ static void clar_print_clap_ontest(const char *suite_name, const char *test_name
(void)test_name;
(void)test_number;
+ if (_clar.verbosity < 0)
+ return;
+
if (_clar.verbosity > 1) {
printf("%s::%s: ", suite_name, test_name);
switch (status) {
case CL_TEST_OK: printf("ok\n"); break;
case CL_TEST_FAILURE: printf("fail\n"); break;
- case CL_TEST_SKIP: printf("skipped"); break;
- case CL_TEST_NOTRUN: printf("notrun"); break;
+ case CL_TEST_SKIP: printf("skipped\n"); break;
+ case CL_TEST_NOTRUN: printf("notrun\n"); break;
}
} else {
switch (status) {
@@ -64,6 +88,8 @@ static void clar_print_clap_ontest(const char *suite_name, const char *test_name
static void clar_print_clap_onsuite(const char *suite_name, int suite_index)
{
+ if (_clar.verbosity < 0)
+ return;
if (_clar.verbosity == 1)
printf("\n%s", suite_name);
@@ -77,11 +103,10 @@ static void clar_print_clap_onabort(const char *fmt, va_list arg)
/* tap: test anywhere protocol format */
-static void clar_print_tap_init(int test_count, int suite_count, const char *suite_names)
+static void clar_print_tap_init(int test_count, int suite_count)
{
(void)test_count;
(void)suite_count;
- (void)suite_names;
printf("TAP version 13\n");
}
@@ -127,18 +152,20 @@ static void clar_print_tap_ontest(const char *suite_name, const char *test_name,
case CL_TEST_FAILURE:
printf("not ok %d - %s::%s\n", test_number, suite_name, test_name);
- printf(" ---\n");
- printf(" reason: |\n");
- printf(" %s\n", error->error_msg);
+ if (_clar.verbosity >= 0) {
+ printf(" ---\n");
+ printf(" reason: |\n");
+ clar_print_indented(error->error_msg, 6);
- if (error->description)
- printf(" %s\n", error->description);
+ if (error->description)
+ clar_print_indented(error->description, 6);
- printf(" at:\n");
- printf(" file: '"); print_escaped(error->file); printf("'\n");
- printf(" line: %" PRIuMAX "\n", error->line_number);
- printf(" function: '%s'\n", error->function);
- printf(" ---\n");
+ printf(" at:\n");
+ printf(" file: '"); print_escaped(error->file); printf("'\n");
+ printf(" line: %" PRIuMAX "\n", error->line_number);
+ printf(" function: '%s'\n", error->function);
+ printf(" ---\n");
+ }
break;
case CL_TEST_SKIP:
@@ -152,6 +179,8 @@ static void clar_print_tap_ontest(const char *suite_name, const char *test_name,
static void clar_print_tap_onsuite(const char *suite_name, int suite_index)
{
+ if (_clar.verbosity < 0)
+ return;
printf("# start of suite %d: %s\n", suite_index, suite_name);
}
@@ -177,9 +206,9 @@ static void clar_print_tap_onabort(const char *fmt, va_list arg)
} \
} while (0)
-static void clar_print_init(int test_count, int suite_count, const char *suite_names)
+static void clar_print_init(int test_count, int suite_count)
{
- PRINT(init, test_count, suite_count, suite_names);
+ PRINT(init, test_count, suite_count);
}
static void clar_print_shutdown(int test_count, int suite_count, int error_count)
diff --git a/t/unit-tests/clar/clar/sandbox.h b/t/unit-tests/clar/clar/sandbox.h
index bc960f50e0..52add8aceb 100644
--- a/t/unit-tests/clar/clar/sandbox.h
+++ b/t/unit-tests/clar/clar/sandbox.h
@@ -2,7 +2,17 @@
#include <sys/syslimits.h>
#endif
-static char _clar_path[4096 + 1];
+/*
+ * The tempdir is the temporary directory for the entirety of the clar
+ * process execution. The sandbox is an individual temporary directory
+ * for the execution of an individual test. Sandboxes are deleted
+ * entirely after test execution to avoid pollution across tests.
+ */
+
+static char _clar_tempdir[CLAR_MAX_PATH];
+static size_t _clar_tempdir_len;
+
+static char _clar_sandbox[CLAR_MAX_PATH];
static int
is_valid_tmp_path(const char *path)
@@ -15,7 +25,10 @@ is_valid_tmp_path(const char *path)
if (!S_ISDIR(st.st_mode))
return 0;
- return (access(path, W_OK) == 0);
+ if (access(path, W_OK) != 0)
+ return 0;
+
+ return (strlen(path) < CLAR_MAX_PATH);
}
static int
@@ -31,14 +44,11 @@ find_tmp_path(char *buffer, size_t length)
for (i = 0; i < var_count; ++i) {
const char *env = getenv(env_vars[i]);
+
if (!env)
continue;
if (is_valid_tmp_path(env)) {
-#ifdef __APPLE__
- if (length >= PATH_MAX && realpath(env, buffer) != NULL)
- return 0;
-#endif
strncpy(buffer, env, length - 1);
buffer[length - 1] = '\0';
return 0;
@@ -47,21 +57,18 @@ find_tmp_path(char *buffer, size_t length)
/* If the environment doesn't say anything, try to use /tmp */
if (is_valid_tmp_path("/tmp")) {
-#ifdef __APPLE__
- if (length >= PATH_MAX && realpath("/tmp", buffer) != NULL)
- return 0;
-#endif
strncpy(buffer, "/tmp", length - 1);
buffer[length - 1] = '\0';
return 0;
}
#else
- DWORD env_len = GetEnvironmentVariable("CLAR_TMP", buffer, (DWORD)length);
- if (env_len > 0 && env_len < (DWORD)length)
+ DWORD len = GetEnvironmentVariable("CLAR_TMP", buffer, (DWORD)length);
+ if (len > 0 && len < (DWORD)length)
return 0;
- if (GetTempPath((DWORD)length, buffer))
+ len = GetTempPath((DWORD)length, buffer);
+ if (len > 0 && len < (DWORD)length)
return 0;
#endif
@@ -75,17 +82,53 @@ find_tmp_path(char *buffer, size_t length)
return -1;
}
-static void clar_unsandbox(void)
+static int canonicalize_tmp_path(char *buffer)
+{
+#ifdef _WIN32
+ char tmp[CLAR_MAX_PATH], *p;
+ DWORD ret;
+
+ ret = GetFullPathName(buffer, CLAR_MAX_PATH, tmp, NULL);
+
+ if (ret == 0 || ret > CLAR_MAX_PATH)
+ return -1;
+
+ ret = GetLongPathName(tmp, buffer, CLAR_MAX_PATH);
+
+ if (ret == 0 || ret > CLAR_MAX_PATH)
+ return -1;
+
+ /* normalize path to POSIX forward slashes */
+ for (p = buffer; *p; p++)
+ if (*p == '\\')
+ *p = '/';
+
+ return 0;
+#elif defined(CLAR_HAS_REALPATH)
+ char tmp[CLAR_MAX_PATH];
+
+ if (realpath(buffer, tmp) == NULL)
+ return -1;
+
+ strcpy(buffer, tmp);
+ return 0;
+#else
+ (void)buffer;
+ return 0;
+#endif
+}
+
+static void clar_tempdir_shutdown(void)
{
- if (_clar_path[0] == '\0')
+ if (_clar_tempdir[0] == '\0')
return;
cl_must_pass(chdir(".."));
- fs_rm(_clar_path);
+ fs_rm(_clar_tempdir);
}
-static int build_sandbox_path(void)
+static int build_tempdir_path(void)
{
#ifdef CLAR_TMPDIR
const char path_tail[] = CLAR_TMPDIR "_XXXXXX";
@@ -95,64 +138,153 @@ static int build_sandbox_path(void)
size_t len;
- if (find_tmp_path(_clar_path, sizeof(_clar_path)) < 0)
+ if (find_tmp_path(_clar_tempdir, sizeof(_clar_tempdir)) < 0 ||
+ canonicalize_tmp_path(_clar_tempdir) < 0)
return -1;
- len = strlen(_clar_path);
+ len = strlen(_clar_tempdir);
-#ifdef _WIN32
- { /* normalize path to POSIX forward slashes */
- size_t i;
- for (i = 0; i < len; ++i) {
- if (_clar_path[i] == '\\')
- _clar_path[i] = '/';
- }
- }
-#endif
+ if (len + strlen(path_tail) + 2 > CLAR_MAX_PATH)
+ return -1;
- if (_clar_path[len - 1] != '/') {
- _clar_path[len++] = '/';
- }
+ if (_clar_tempdir[len - 1] != '/')
+ _clar_tempdir[len++] = '/';
- strncpy(_clar_path + len, path_tail, sizeof(_clar_path) - len);
+ strncpy(_clar_tempdir + len, path_tail, sizeof(_clar_tempdir) - len);
#if defined(__MINGW32__)
- if (_mktemp(_clar_path) == NULL)
+ if (_mktemp(_clar_tempdir) == NULL)
return -1;
- if (mkdir(_clar_path, 0700) != 0)
+ if (mkdir(_clar_tempdir, 0700) != 0)
return -1;
#elif defined(_WIN32)
- if (_mktemp_s(_clar_path, sizeof(_clar_path)) != 0)
+ if (_mktemp_s(_clar_tempdir, sizeof(_clar_tempdir)) != 0)
return -1;
- if (mkdir(_clar_path, 0700) != 0)
+ if (mkdir(_clar_tempdir, 0700) != 0)
return -1;
-#elif defined(__sun) || defined(__TANDEM)
- if (mktemp(_clar_path) == NULL)
+#elif defined(__sun) || defined(__TANDEM) || defined(__hpux)
+ if (mktemp(_clar_tempdir) == NULL)
return -1;
- if (mkdir(_clar_path, 0700) != 0)
+ if (mkdir(_clar_tempdir, 0700) != 0)
return -1;
#else
- if (mkdtemp(_clar_path) == NULL)
+ if (mkdtemp(_clar_tempdir) == NULL)
return -1;
#endif
+ _clar_tempdir_len = strlen(_clar_tempdir);
return 0;
}
-static void clar_sandbox(void)
+static void clar_tempdir_init(void)
{
- if (_clar_path[0] == '\0' && build_sandbox_path() < 0)
- clar_abort("Failed to build sandbox path.\n");
+ if (_clar_tempdir[0] == '\0' && build_tempdir_path() < 0)
+ clar_abort("Failed to build tempdir path.\n");
- if (chdir(_clar_path) != 0)
- clar_abort("Failed to change into sandbox directory '%s': %s.\n",
- _clar_path, strerror(errno));
+ if (chdir(_clar_tempdir) != 0)
+ clar_abort("Failed to change into tempdir '%s': %s.\n",
+ _clar_tempdir, strerror(errno));
+
+#if !defined(CLAR_SANDBOX_TEST_NAMES) && defined(_WIN32)
+ srand(clock() ^ (unsigned int)time(NULL) ^ GetCurrentProcessId() ^ GetCurrentThreadId());
+#elif !defined(CLAR_SANDBOX_TEST_NAMES)
+ srand(clock() ^ time(NULL) ^ ((unsigned)getpid() << 16));
+#endif
+}
+
+static void append(char *dst, const char *src)
+{
+ char *d;
+ const char *s;
+
+ for (d = dst; *d; d++)
+ ;
+
+ for (s = src; *s; d++, s++)
+ if (*s == ':')
+ *d = '_';
+ else
+ *d = *s;
+
+ *d = '\0';
+}
+
+static int clar_sandbox_create(const char *suite_name, const char *test_name)
+{
+#ifndef CLAR_SANDBOX_TEST_NAMES
+ char alpha[] = "0123456789abcdef";
+ int num = rand();
+#endif
+
+ cl_assert(_clar_sandbox[0] == '\0');
+
+ /*
+ * We may want to use test names as sandbox directory names for
+ * readability, _however_ on platforms with restrictions for short
+ * file / folder names (eg, Windows), this may be too long.
+ */
+#ifdef CLAR_SANDBOX_TEST_NAMES
+ cl_assert(strlen(_clar_tempdir) + strlen(suite_name) + strlen(test_name) + 3 < CLAR_MAX_PATH);
+
+ strcpy(_clar_sandbox, _clar_tempdir);
+ _clar_sandbox[_clar_tempdir_len] = '/';
+ _clar_sandbox[_clar_tempdir_len + 1] = '\0';
+
+ append(_clar_sandbox, suite_name);
+ append(_clar_sandbox, "__");
+ append(_clar_sandbox, test_name);
+#else
+ ((void)suite_name);
+ ((void)test_name);
+ ((void)append);
+
+ cl_assert(strlen(_clar_tempdir) + 9 < CLAR_MAX_PATH);
+
+ strcpy(_clar_sandbox, _clar_tempdir);
+ _clar_sandbox[_clar_tempdir_len] = '/';
+
+ _clar_sandbox[_clar_tempdir_len + 1] = alpha[(num & 0xf0000000) >> 28];
+ _clar_sandbox[_clar_tempdir_len + 2] = alpha[(num & 0x0f000000) >> 24];
+ _clar_sandbox[_clar_tempdir_len + 3] = alpha[(num & 0x00f00000) >> 20];
+ _clar_sandbox[_clar_tempdir_len + 4] = alpha[(num & 0x000f0000) >> 16];
+ _clar_sandbox[_clar_tempdir_len + 5] = alpha[(num & 0x0000f000) >> 12];
+ _clar_sandbox[_clar_tempdir_len + 6] = alpha[(num & 0x00000f00) >> 8];
+ _clar_sandbox[_clar_tempdir_len + 7] = alpha[(num & 0x000000f0) >> 4];
+ _clar_sandbox[_clar_tempdir_len + 8] = alpha[(num & 0x0000000f) >> 0];
+ _clar_sandbox[_clar_tempdir_len + 9] = '\0';
+#endif
+
+ if (mkdir(_clar_sandbox, 0700) != 0)
+ return -1;
+
+ if (chdir(_clar_sandbox) != 0)
+ return -1;
+
+ return 0;
+}
+
+static int clar_sandbox_cleanup(void)
+{
+ cl_assert(_clar_sandbox[0] != '\0');
+
+ if (chdir(_clar_tempdir) != 0)
+ return -1;
+
+ fs_rm(_clar_sandbox);
+ _clar_sandbox[0] = '\0';
+
+ return 0;
+}
+
+const char *clar_tempdir_path(void)
+{
+ return _clar_tempdir;
}
const char *clar_sandbox_path(void)
{
- return _clar_path;
+ return _clar_sandbox;
}
diff --git a/t/unit-tests/clar/clar/summary.h b/t/unit-tests/clar/clar/summary.h
index 0d0b646fe7..7b85f162d8 100644
--- a/t/unit-tests/clar/clar/summary.h
+++ b/t/unit-tests/clar/clar/summary.h
@@ -23,10 +23,11 @@ static int clar_summary_testsuite(struct clar_summary *summary,
int idn, const char *name, time_t timestamp,
int test_count, int fail_count, int error_count)
{
- struct tm *tm = localtime(&timestamp);
+ struct tm tm;
char iso_dt[20];
- if (strftime(iso_dt, sizeof(iso_dt), "%Y-%m-%dT%H:%M:%S", tm) == 0)
+ localtime_r(&timestamp, &tm);
+ if (strftime(iso_dt, sizeof(iso_dt), "%Y-%m-%dT%H:%M:%S", &tm) == 0)
return -1;
return fprintf(summary->fp, "\t<testsuite"
diff --git a/t/unit-tests/clar/example/CMakeLists.txt b/t/unit-tests/clar/example/CMakeLists.txt
new file mode 100644
index 0000000000..b72f187523
--- /dev/null
+++ b/t/unit-tests/clar/example/CMakeLists.txt
@@ -0,0 +1,28 @@
+find_package(Python COMPONENTS Interpreter REQUIRED)
+
+add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/clar.suite"
+ COMMAND "${Python_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/generate.py" --output "${CMAKE_CURRENT_BINARY_DIR}"
+ DEPENDS main.c example.c
+ WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+)
+
+add_executable(example)
+set_target_properties(example PROPERTIES
+ C_STANDARD 90
+ C_STANDARD_REQUIRED ON
+ C_EXTENSIONS OFF
+)
+target_sources(example PRIVATE
+ main.c
+ example.c
+ "${CMAKE_CURRENT_BINARY_DIR}/clar.suite"
+)
+target_compile_definitions(example PRIVATE)
+target_compile_options(example PRIVATE
+ $<IF:$<CXX_COMPILER_ID:MSVC>,/W4,-Wall>
+)
+target_include_directories(example PRIVATE
+ "${CMAKE_SOURCE_DIR}"
+ "${CMAKE_CURRENT_BINARY_DIR}"
+)
+target_link_libraries(example clar)
diff --git a/t/unit-tests/clar/example/example.c b/t/unit-tests/clar/example/example.c
new file mode 100644
index 0000000000..c07d6bf68e
--- /dev/null
+++ b/t/unit-tests/clar/example/example.c
@@ -0,0 +1,6 @@
+#include "clar.h"
+
+void test_example__simple_assert(void)
+{
+ cl_assert_equal_i(1, 1);
+}
diff --git a/t/unit-tests/clar/test/main.c.sample b/t/unit-tests/clar/example/main.c
index a4d91b72fa..f8def7fa6e 100644
--- a/t/unit-tests/clar/test/main.c.sample
+++ b/t/unit-tests/clar/example/main.c
@@ -5,7 +5,7 @@
* For full terms see the included COPYING file.
*/
-#include "clar_test.h"
+#include "clar.h"
/*
* Minimal main() for clar tests.
diff --git a/t/unit-tests/clar/generate.py b/t/unit-tests/clar/generate.py
index 80996ac3e7..fd2f0ee83b 100755
--- a/t/unit-tests/clar/generate.py
+++ b/t/unit-tests/clar/generate.py
@@ -158,17 +158,24 @@ class TestSuite(object):
def find_modules(self):
modules = []
- for root, _, files in os.walk(self.path):
- module_root = root[len(self.path):]
- module_root = [c for c in module_root.split(os.sep) if c]
- tests_in_module = fnmatch.filter(files, "*.c")
+ if os.path.isfile(self.path):
+ full_path = os.path.abspath(self.path)
+ module_name = os.path.basename(self.path)
+ module_name = os.path.splitext(module_name)[0]
+ modules.append((full_path, module_name))
+ else:
+ for root, _, files in os.walk(self.path):
+ module_root = root[len(self.path):]
+ module_root = [c for c in module_root.split(os.sep) if c]
- for test_file in tests_in_module:
- full_path = os.path.join(root, test_file)
- module_name = "_".join(module_root + [test_file[:-2]]).replace("-", "_")
+ tests_in_module = fnmatch.filter(files, "*.c")
- modules.append((full_path, module_name))
+ for test_file in tests_in_module:
+ full_path = os.path.join(root, test_file)
+ module_name = "_".join(module_root + [test_file[:-2]]).replace("-", "_")
+
+ modules.append((full_path, module_name))
return modules
@@ -217,6 +224,7 @@ class TestSuite(object):
def write(self):
output = os.path.join(self.output, 'clar.suite')
+ os.makedirs(self.output, exist_ok=True)
if not self.should_generate(output):
return False
@@ -258,7 +266,11 @@ if __name__ == '__main__':
sys.exit(1)
path = args.pop() if args else '.'
+ if os.path.isfile(path) and not options.output:
+ print("Must provide --output when specifying a file")
+ sys.exit(1)
output = options.output or path
+
suite = TestSuite(path, output)
suite.load(options.force)
suite.disable(options.excluded)
diff --git a/t/unit-tests/clar/test/CMakeLists.txt b/t/unit-tests/clar/test/CMakeLists.txt
index 7f2c1dc17a..f240166439 100644
--- a/t/unit-tests/clar/test/CMakeLists.txt
+++ b/t/unit-tests/clar/test/CMakeLists.txt
@@ -2,12 +2,12 @@ find_package(Python COMPONENTS Interpreter REQUIRED)
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/clar.suite"
COMMAND "${Python_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/generate.py" --output "${CMAKE_CURRENT_BINARY_DIR}"
- DEPENDS main.c sample.c clar_test.h
+ DEPENDS main.c selftest.c
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
)
-add_executable(clar_test)
-set_target_properties(clar_test PROPERTIES
+add_executable(selftest)
+set_target_properties(selftest PROPERTIES
C_STANDARD 90
C_STANDARD_REQUIRED ON
C_EXTENSIONS OFF
@@ -15,25 +15,35 @@ set_target_properties(clar_test PROPERTIES
# MSVC generates all kinds of warnings. We may want to fix these in the future
# and then unconditionally treat warnings as errors.
-if(NOT MSVC)
- set_target_properties(clar_test PROPERTIES
+if (NOT MSVC)
+ set_target_properties(selftest PROPERTIES
COMPILE_WARNING_AS_ERROR ON
)
endif()
-target_sources(clar_test PRIVATE
+target_sources(selftest PRIVATE
main.c
- sample.c
+ selftest.c
"${CMAKE_CURRENT_BINARY_DIR}/clar.suite"
)
-target_compile_definitions(clar_test PRIVATE
- CLAR_FIXTURE_PATH="${CMAKE_CURRENT_SOURCE_DIR}/resources/"
+target_compile_definitions(selftest PRIVATE
+ CLAR_FIXTURE_PATH="${CMAKE_CURRENT_SOURCE_DIR}/expected/"
)
-target_compile_options(clar_test PRIVATE
+target_compile_options(selftest PRIVATE
$<IF:$<CXX_COMPILER_ID:MSVC>,/W4,-Wall>
)
-target_include_directories(clar_test PRIVATE
+target_include_directories(selftest PRIVATE
"${CMAKE_SOURCE_DIR}"
"${CMAKE_CURRENT_BINARY_DIR}"
)
-target_link_libraries(clar_test clar)
+target_link_libraries(selftest clar)
+
+add_test(NAME build_selftest
+ COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}" --config "$<CONFIG>" --target selftest
+)
+set_tests_properties(build_selftest PROPERTIES FIXTURES_SETUP clar_test_fixture)
+
+add_subdirectory(suites)
+
+add_test(NAME selftest COMMAND "${CMAKE_CURRENT_BINARY_DIR}/selftest" $<TARGET_FILE_DIR:combined_suite>)
+set_tests_properties(selftest PROPERTIES FIXTURES_REQUIRED clar_test_fixture)
diff --git a/t/unit-tests/clar/test/clar_test.h b/t/unit-tests/clar/test/clar_test.h
deleted file mode 100644
index 0fcaa639aa..0000000000
--- a/t/unit-tests/clar/test/clar_test.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright (c) Vicent Marti. All rights reserved.
- *
- * This file is part of clar, distributed under the ISC license.
- * For full terms see the included COPYING file.
- */
-#ifndef __CLAR_TEST__
-#define __CLAR_TEST__
-
-/* Import the standard clar helper functions */
-#include "clar.h"
-
-/* Your custom shared includes / defines here */
-extern int global_test_counter;
-
-#endif
diff --git a/t/unit-tests/clar/test/expected/help b/t/unit-tests/clar/test/expected/help
new file mode 100644
index 0000000000..9428def2d7
--- /dev/null
+++ b/t/unit-tests/clar/test/expected/help
@@ -0,0 +1,12 @@
+Usage: combined [options]
+
+Options:
+ -sname Run only the suite with `name` (can go to individual test name)
+ -iname Include the suite with `name`
+ -xname Exclude the suite with `name`
+ -v Increase verbosity (show suite names)
+ -q Decrease verbosity, inverse to -v
+ -Q Quit as soon as a test fails
+ -t Display results in tap format
+ -l Print suite names
+ -r[filename] Write summary file (to the optional filename)
diff --git a/t/unit-tests/clar/test/expected/quiet b/t/unit-tests/clar/test/expected/quiet
new file mode 100644
index 0000000000..280c99d8ad
--- /dev/null
+++ b/t/unit-tests/clar/test/expected/quiet
@@ -0,0 +1,44 @@
+ 1) Failure:
+combined::1 [file:42]
+ Function call failed: -1
+
+ 2) Failure:
+combined::2 [file:42]
+ Expression is not true: 100 == 101
+
+ 3) Failure:
+combined::strings [file:42]
+ String mismatch: "mismatched" != actual ("this one fails")
+ 'mismatched' != 'expected' (at byte 0)
+
+ 4) Failure:
+combined::strings_with_length [file:42]
+ String mismatch: "exactly" != actual ("this one fails")
+ 'exa' != 'exp' (at byte 2)
+
+ 5) Failure:
+combined::int [file:42]
+ 101 != value ("extra note on failing test")
+ 101 != 100
+
+ 6) Failure:
+combined::int_fmt [file:42]
+ 022 != value
+ 0022 != 0144
+
+ 7) Failure:
+combined::bool [file:42]
+ 0 != value
+ 0 != 1
+
+ 8) Failure:
+combined::multiline_description [file:42]
+ Function call failed: -1
+ description line 1
+ description line 2
+
+ 9) Failure:
+combined::null_string [file:42]
+ String mismatch: "expected" != actual ("this one fails")
+ 'expected' != NULL
+
diff --git a/t/unit-tests/clar/test/expected/specific_test b/t/unit-tests/clar/test/expected/specific_test
new file mode 100644
index 0000000000..6c22e9f507
--- /dev/null
+++ b/t/unit-tests/clar/test/expected/specific_test
@@ -0,0 +1,9 @@
+Loaded 1 suites:
+Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')
+F
+
+ 1) Failure:
+combined::bool [file:42]
+ 0 != value
+ 0 != 1
+
diff --git a/t/unit-tests/clar/test/expected/stop_on_failure b/t/unit-tests/clar/test/expected/stop_on_failure
new file mode 100644
index 0000000000..c23610754f
--- /dev/null
+++ b/t/unit-tests/clar/test/expected/stop_on_failure
@@ -0,0 +1,8 @@
+Loaded 1 suites:
+Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')
+F
+
+ 1) Failure:
+combined::1 [file:42]
+ Function call failed: -1
+
diff --git a/t/unit-tests/clar/test/expected/suite_names b/t/unit-tests/clar/test/expected/suite_names
new file mode 100644
index 0000000000..10d1538427
--- /dev/null
+++ b/t/unit-tests/clar/test/expected/suite_names
@@ -0,0 +1,2 @@
+Test suites (use -s<name> to run just one):
+ 0: combined
diff --git a/t/unit-tests/clar/test/expected/summary.xml b/t/unit-tests/clar/test/expected/summary.xml
new file mode 100644
index 0000000000..9a89d43a59
--- /dev/null
+++ b/t/unit-tests/clar/test/expected/summary.xml
@@ -0,0 +1,41 @@
+<testsuites>
+ <testsuite id="0" name="selftest" hostname="localhost" timestamp="2024-09-06T10:04:08" tests="8" failures="8" errors="0">
+ <testcase name="1" classname="selftest" time="0.00">
+ <failure type="assert"><![CDATA[Function call failed: -1
+(null)]]></failure>
+ </testcase>
+ <testcase name="2" classname="selftest" time="0.00">
+ <failure type="assert"><![CDATA[Expression is not true: 100 == 101
+(null)]]></failure>
+ </testcase>
+ <testcase name="strings" classname="selftest" time="0.00">
+ <failure type="assert"><![CDATA[String mismatch: "mismatched" != actual ("this one fails")
+'mismatched' != 'expected' (at byte 0)]]></failure>
+ </testcase>
+ <testcase name="strings_with_length" classname="selftest" time="0.00">
+ <failure type="assert"><![CDATA[String mismatch: "exactly" != actual ("this one fails")
+'exa' != 'exp' (at byte 2)]]></failure>
+ </testcase>
+ <testcase name="int" classname="selftest" time="0.00">
+ <failure type="assert"><![CDATA[101 != value ("extra note on failing test")
+101 != 100]]></failure>
+ </testcase>
+ <testcase name="int_fmt" classname="selftest" time="0.00">
+ <failure type="assert"><![CDATA[022 != value
+0022 != 0144]]></failure>
+ </testcase>
+ <testcase name="bool" classname="selftest" time="0.00">
+ <failure type="assert"><![CDATA[0 != value
+0 != 1]]></failure>
+ </testcase>
+ <testcase name="multiline_description" classname="selftest" time="0.00">
+ <failure type="assert"><![CDATA[Function call failed: −1
+description line 1
+description line 2]]></failure>
+ </testcase>
+ <testcase name="null_string" classname="selftest" time="0.00">
+ <failure type="assert"><![CDATA[String mismatch: "expected" != actual ("this one fails")
+'expected' != NULL]]></failure>
+ </testcase>
+ </testsuite>
+</testsuites>
diff --git a/t/unit-tests/clar/test/expected/summary_with_filename b/t/unit-tests/clar/test/expected/summary_with_filename
new file mode 100644
index 0000000000..460160791d
--- /dev/null
+++ b/t/unit-tests/clar/test/expected/summary_with_filename
@@ -0,0 +1,49 @@
+Loaded 1 suites:
+Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')
+FFFFFFFFF
+
+ 1) Failure:
+combined::1 [file:42]
+ Function call failed: -1
+
+ 2) Failure:
+combined::2 [file:42]
+ Expression is not true: 100 == 101
+
+ 3) Failure:
+combined::strings [file:42]
+ String mismatch: "mismatched" != actual ("this one fails")
+ 'mismatched' != 'expected' (at byte 0)
+
+ 4) Failure:
+combined::strings_with_length [file:42]
+ String mismatch: "exactly" != actual ("this one fails")
+ 'exa' != 'exp' (at byte 2)
+
+ 5) Failure:
+combined::int [file:42]
+ 101 != value ("extra note on failing test")
+ 101 != 100
+
+ 6) Failure:
+combined::int_fmt [file:42]
+ 022 != value
+ 0022 != 0144
+
+ 7) Failure:
+combined::bool [file:42]
+ 0 != value
+ 0 != 1
+
+ 8) Failure:
+combined::multiline_description [file:42]
+ Function call failed: -1
+ description line 1
+ description line 2
+
+ 9) Failure:
+combined::null_string [file:42]
+ String mismatch: "expected" != actual ("this one fails")
+ 'expected' != NULL
+
+written summary file to different.xml
diff --git a/t/unit-tests/clar/test/expected/summary_without_filename b/t/unit-tests/clar/test/expected/summary_without_filename
new file mode 100644
index 0000000000..7874c1d98b
--- /dev/null
+++ b/t/unit-tests/clar/test/expected/summary_without_filename
@@ -0,0 +1,49 @@
+Loaded 1 suites:
+Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')
+FFFFFFFFF
+
+ 1) Failure:
+combined::1 [file:42]
+ Function call failed: -1
+
+ 2) Failure:
+combined::2 [file:42]
+ Expression is not true: 100 == 101
+
+ 3) Failure:
+combined::strings [file:42]
+ String mismatch: "mismatched" != actual ("this one fails")
+ 'mismatched' != 'expected' (at byte 0)
+
+ 4) Failure:
+combined::strings_with_length [file:42]
+ String mismatch: "exactly" != actual ("this one fails")
+ 'exa' != 'exp' (at byte 2)
+
+ 5) Failure:
+combined::int [file:42]
+ 101 != value ("extra note on failing test")
+ 101 != 100
+
+ 6) Failure:
+combined::int_fmt [file:42]
+ 022 != value
+ 0022 != 0144
+
+ 7) Failure:
+combined::bool [file:42]
+ 0 != value
+ 0 != 1
+
+ 8) Failure:
+combined::multiline_description [file:42]
+ Function call failed: -1
+ description line 1
+ description line 2
+
+ 9) Failure:
+combined::null_string [file:42]
+ String mismatch: "expected" != actual ("this one fails")
+ 'expected' != NULL
+
+written summary file to summary.xml
diff --git a/t/unit-tests/clar/test/expected/tap b/t/unit-tests/clar/test/expected/tap
new file mode 100644
index 0000000000..bddbd5dfe9
--- /dev/null
+++ b/t/unit-tests/clar/test/expected/tap
@@ -0,0 +1,92 @@
+TAP version 13
+# start of suite 1: combined
+not ok 1 - combined::1
+ ---
+ reason: |
+ Function call failed: -1
+ at:
+ file: 'file'
+ line: 42
+ function: 'func'
+ ---
+not ok 2 - combined::2
+ ---
+ reason: |
+ Expression is not true: 100 == 101
+ at:
+ file: 'file'
+ line: 42
+ function: 'func'
+ ---
+not ok 3 - combined::strings
+ ---
+ reason: |
+ String mismatch: "mismatched" != actual ("this one fails")
+ 'mismatched' != 'expected' (at byte 0)
+ at:
+ file: 'file'
+ line: 42
+ function: 'func'
+ ---
+not ok 4 - combined::strings_with_length
+ ---
+ reason: |
+ String mismatch: "exactly" != actual ("this one fails")
+ 'exa' != 'exp' (at byte 2)
+ at:
+ file: 'file'
+ line: 42
+ function: 'func'
+ ---
+not ok 5 - combined::int
+ ---
+ reason: |
+ 101 != value ("extra note on failing test")
+ 101 != 100
+ at:
+ file: 'file'
+ line: 42
+ function: 'func'
+ ---
+not ok 6 - combined::int_fmt
+ ---
+ reason: |
+ 022 != value
+ 0022 != 0144
+ at:
+ file: 'file'
+ line: 42
+ function: 'func'
+ ---
+not ok 7 - combined::bool
+ ---
+ reason: |
+ 0 != value
+ 0 != 1
+ at:
+ file: 'file'
+ line: 42
+ function: 'func'
+ ---
+not ok 8 - combined::multiline_description
+ ---
+ reason: |
+ Function call failed: -1
+ description line 1
+ description line 2
+ at:
+ file: 'file'
+ line: 42
+ function: 'func'
+ ---
+not ok 9 - combined::null_string
+ ---
+ reason: |
+ String mismatch: "expected" != actual ("this one fails")
+ 'expected' != NULL
+ at:
+ file: 'file'
+ line: 42
+ function: 'func'
+ ---
+1..9
diff --git a/t/unit-tests/clar/test/expected/without_arguments b/t/unit-tests/clar/test/expected/without_arguments
new file mode 100644
index 0000000000..1111d418a0
--- /dev/null
+++ b/t/unit-tests/clar/test/expected/without_arguments
@@ -0,0 +1,48 @@
+Loaded 1 suites:
+Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')
+FFFFFFFFF
+
+ 1) Failure:
+combined::1 [file:42]
+ Function call failed: -1
+
+ 2) Failure:
+combined::2 [file:42]
+ Expression is not true: 100 == 101
+
+ 3) Failure:
+combined::strings [file:42]
+ String mismatch: "mismatched" != actual ("this one fails")
+ 'mismatched' != 'expected' (at byte 0)
+
+ 4) Failure:
+combined::strings_with_length [file:42]
+ String mismatch: "exactly" != actual ("this one fails")
+ 'exa' != 'exp' (at byte 2)
+
+ 5) Failure:
+combined::int [file:42]
+ 101 != value ("extra note on failing test")
+ 101 != 100
+
+ 6) Failure:
+combined::int_fmt [file:42]
+ 022 != value
+ 0022 != 0144
+
+ 7) Failure:
+combined::bool [file:42]
+ 0 != value
+ 0 != 1
+
+ 8) Failure:
+combined::multiline_description [file:42]
+ Function call failed: -1
+ description line 1
+ description line 2
+
+ 9) Failure:
+combined::null_string [file:42]
+ String mismatch: "expected" != actual ("this one fails")
+ 'expected' != NULL
+
diff --git a/t/unit-tests/clar/test/main.c b/t/unit-tests/clar/test/main.c
index 59e56ad255..94af440643 100644
--- a/t/unit-tests/clar/test/main.c
+++ b/t/unit-tests/clar/test/main.c
@@ -1,23 +1,9 @@
-/*
- * Copyright (c) Vicent Marti. All rights reserved.
- *
- * This file is part of clar, distributed under the ISC license.
- * For full terms see the included COPYING file.
- */
+#include <stdio.h>
+#include <string.h>
-#include "clar_test.h"
+#include "selftest.h"
-/*
- * Sample main() for clar tests.
- *
- * You should write your own main routine for clar tests that does specific
- * setup and teardown as necessary for your application. The only required
- * line is the call to `clar_test(argc, argv)`, which will execute the test
- * suite. If you want to check the return value of the test application,
- * your main() should return the same value returned by clar_test().
- */
-
-int global_test_counter = 0;
+const char *selftest_suite_directory;
#ifdef _WIN32
int __cdecl main(int argc, char *argv[])
@@ -25,16 +11,15 @@ int __cdecl main(int argc, char *argv[])
int main(int argc, char *argv[])
#endif
{
- int ret;
-
- /* Your custom initialization here */
- global_test_counter = 0;
-
- /* Run the test suite */
- ret = clar_test(argc, argv);
+ if (argc < 2) {
+ fprintf(stderr, "usage: %s <selftest-suite-directory> <options>\n",
+ argv[0]);
+ exit(1);
+ }
- /* Your custom cleanup here */
- cl_assert_equal_i(8, global_test_counter);
+ selftest_suite_directory = argv[1];
+ memmove(argv + 1, argv + 2, argc - 1);
+ argc -= 1;
- return ret;
+ return clar_test(argc, argv);
}
diff --git a/t/unit-tests/clar/test/selftest.c b/t/unit-tests/clar/test/selftest.c
new file mode 100644
index 0000000000..eed83e4512
--- /dev/null
+++ b/t/unit-tests/clar/test/selftest.c
@@ -0,0 +1,370 @@
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "selftest.h"
+
+#ifdef _WIN32
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+
+static char *read_full(HANDLE h, int is_pipe)
+{
+ char *data = NULL;
+ size_t data_size = 0;
+
+ while (1) {
+ CHAR buf[4096];
+ DWORD bytes_read;
+
+ if (!ReadFile(h, buf, sizeof(buf), &bytes_read, NULL)) {
+ if (!is_pipe)
+ cl_fail("Failed reading file handle.");
+ cl_assert_equal_i(GetLastError(), ERROR_BROKEN_PIPE);
+ break;
+ }
+ if (!bytes_read)
+ break;
+
+ data = realloc(data, data_size + bytes_read);
+ cl_assert(data);
+ memcpy(data + data_size, buf, bytes_read);
+ data_size += bytes_read;
+ }
+
+ data = realloc(data, data_size + 1);
+ cl_assert(data);
+ data[data_size] = '\0';
+
+ while (strstr(data, "\r\n")) {
+ char *ptr = strstr(data, "\r\n");
+ memmove(ptr, ptr + 1, strlen(ptr));
+ }
+
+ return data;
+}
+
+static char *read_file(const char *path)
+{
+ char *content;
+ HANDLE file;
+
+ file = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ cl_assert(file != INVALID_HANDLE_VALUE);
+ content = read_full(file, 0);
+ cl_assert_equal_b(1, CloseHandle(file));
+
+ return content;
+}
+
+static char *execute(const char *suite, int expected_error_code, const char **args, size_t nargs)
+{
+ SECURITY_ATTRIBUTES security_attributes = { 0 };
+ PROCESS_INFORMATION process_info = { 0 };
+ STARTUPINFO startup_info = { 0 };
+ char binary_path[4096] = { 0 };
+ char cmdline[4096] = { 0 };
+ char *output = NULL;
+ HANDLE stdout_write;
+ HANDLE stdout_read;
+ DWORD exit_code;
+ size_t i;
+
+ snprintf(binary_path, sizeof(binary_path), "%s/%s_suite.exe",
+ selftest_suite_directory, suite);
+
+ /*
+ * Assemble command line arguments. In theory we'd have to properly
+ * quote them. In practice none of our tests actually care.
+ */
+ snprintf(cmdline, sizeof(cmdline), suite);
+ for (i = 0; i < nargs; i++) {
+ size_t cmdline_len = strlen(cmdline);
+ const char *arg = args[i];
+ cl_assert(cmdline_len + strlen(arg) < sizeof(cmdline));
+ snprintf(cmdline + cmdline_len, sizeof(cmdline) - cmdline_len,
+ " %s", arg);
+ }
+
+ /*
+ * Create a pipe that we will use to read data from the child process.
+ * The writing side needs to be inheritable such that the child can use
+ * it as stdout and stderr. The reading side should only be used by the
+ * parent.
+ */
+ security_attributes.nLength = sizeof(security_attributes);
+ security_attributes.bInheritHandle = TRUE;
+ cl_assert_equal_b(1, CreatePipe(&stdout_read, &stdout_write, &security_attributes, 0));
+ cl_assert_equal_b(1, SetHandleInformation(stdout_read, HANDLE_FLAG_INHERIT, 0));
+
+ /*
+ * Create the child process with our pipe.
+ */
+ startup_info.cb = sizeof(startup_info);
+ startup_info.hStdError = stdout_write;
+ startup_info.hStdOutput = stdout_write;
+ startup_info.dwFlags |= STARTF_USESTDHANDLES;
+ cl_assert_equal_b(1, CreateProcess(binary_path, cmdline, NULL, NULL, TRUE,
+ 0, NULL, NULL, &startup_info, &process_info));
+ cl_assert_equal_b(1, CloseHandle(stdout_write));
+
+ output = read_full(stdout_read, 1);
+ cl_assert_equal_b(1, CloseHandle(stdout_read));
+ cl_assert_equal_b(1, GetExitCodeProcess(process_info.hProcess, &exit_code));
+ cl_assert_equal_i(exit_code, expected_error_code);
+
+ return output;
+}
+
+static void assert_output(const char *suite, const char *expected_output_file, int expected_error_code, ...)
+{
+ char *expected_output = NULL;
+ char *output = NULL;
+ const char *args[16];
+ va_list ap;
+ size_t i;
+
+ va_start(ap, expected_error_code);
+ for (i = 0; ; i++) {
+ const char *arg = va_arg(ap, const char *);
+ if (!arg)
+ break;
+ cl_assert(i < sizeof(args) / sizeof(*args));
+ args[i] = arg;
+ }
+ va_end(ap);
+
+ output = execute(suite, expected_error_code, args, i);
+ expected_output = read_file(cl_fixture(expected_output_file));
+ cl_assert_equal_s(output, expected_output);
+
+ free(expected_output);
+ free(output);
+}
+
+#else
+# include <errno.h>
+# include <fcntl.h>
+# include <limits.h>
+# include <unistd.h>
+# include <sys/wait.h>
+
+static char *read_full(int fd)
+{
+ size_t data_bytes = 0;
+ char *data = NULL;
+
+ while (1) {
+ char buf[4096];
+ ssize_t n;
+
+ n = read(fd, buf, sizeof(buf));
+ if (n < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ cl_fail("Failed reading from child process.");
+ }
+ if (!n)
+ break;
+
+ data = realloc(data, data_bytes + n);
+ cl_assert(data);
+
+ memcpy(data + data_bytes, buf, n);
+ data_bytes += n;
+ }
+
+ data = realloc(data, data_bytes + 1);
+ cl_assert(data);
+ data[data_bytes] = '\0';
+
+ return data;
+}
+
+static char *read_file(const char *path)
+{
+ char *data;
+ int fd;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ cl_fail("Failed reading expected file.");
+
+ data = read_full(fd);
+ cl_must_pass(close(fd));
+
+ return data;
+}
+
+static char *execute(const char *suite, int expected_error_code, const char **args, size_t nargs)
+{
+ int pipe_fds[2];
+ pid_t pid;
+
+ cl_must_pass(pipe(pipe_fds));
+
+ pid = fork();
+ if (!pid) {
+ const char *final_args[17] = { NULL };
+ char binary_path[4096];
+ size_t len = 0;
+ size_t i;
+
+ cl_assert(nargs < sizeof(final_args) / sizeof(*final_args));
+ final_args[0] = suite;
+ for (i = 0; i < nargs; i++)
+ final_args[i + 1] = args[i];
+
+ if (dup2(pipe_fds[1], STDOUT_FILENO) < 0 ||
+ dup2(pipe_fds[1], STDERR_FILENO) < 0 ||
+ close(0) < 0 ||
+ close(pipe_fds[0]) < 0 ||
+ close(pipe_fds[1]) < 0)
+ exit(1);
+
+ cl_assert(len + strlen(selftest_suite_directory) < sizeof(binary_path));
+ strcpy(binary_path, selftest_suite_directory);
+ len += strlen(selftest_suite_directory);
+
+ cl_assert(len + 1 < sizeof(binary_path));
+ binary_path[len] = '/';
+ len += 1;
+
+ cl_assert(len + strlen(suite) < sizeof(binary_path));
+ strcpy(binary_path + len, suite);
+ len += strlen(suite);
+
+ cl_assert(len + strlen("_suite") < sizeof(binary_path));
+ strcpy(binary_path + len, "_suite");
+ len += strlen("_suite");
+
+ binary_path[len] = '\0';
+
+ execv(binary_path, (char **) final_args);
+ exit(1);
+ } else if (pid > 0) {
+ pid_t waited_pid;
+ char *output;
+ int stat;
+
+ cl_must_pass(close(pipe_fds[1]));
+
+ output = read_full(pipe_fds[0]);
+
+ waited_pid = waitpid(pid, &stat, 0);
+ cl_assert_equal_i(pid, waited_pid);
+ cl_assert(WIFEXITED(stat));
+ cl_assert_equal_i(WEXITSTATUS(stat), expected_error_code);
+
+ return output;
+ } else {
+ cl_fail("Fork failed.");
+ }
+
+ return NULL;
+}
+
+static void assert_output(const char *suite, const char *expected_output_file, int expected_error_code, ...)
+{
+ char *expected_output, *output;
+ const char *args[16];
+ va_list ap;
+ size_t i;
+
+ va_start(ap, expected_error_code);
+ for (i = 0; ; i++) {
+ cl_assert(i < sizeof(args) / sizeof(*args));
+ args[i] = va_arg(ap, const char *);
+ if (!args[i])
+ break;
+ }
+ va_end(ap);
+
+ output = execute(suite, expected_error_code, args, i);
+ expected_output = read_file(cl_fixture(expected_output_file));
+ cl_assert_equal_s(output, expected_output);
+
+ free(expected_output);
+ free(output);
+}
+#endif
+
+void test_selftest__help(void)
+{
+ cl_invoke(assert_output("combined", "help", 1, "-h", NULL));
+}
+
+void test_selftest__without_arguments(void)
+{
+ cl_invoke(assert_output("combined", "without_arguments", 9, NULL));
+}
+
+void test_selftest__specific_test(void)
+{
+ cl_invoke(assert_output("combined", "specific_test", 1, "-scombined::bool", NULL));
+}
+
+void test_selftest__stop_on_failure(void)
+{
+ cl_invoke(assert_output("combined", "stop_on_failure", 1, "-Q", NULL));
+}
+
+void test_selftest__quiet(void)
+{
+ cl_invoke(assert_output("combined", "quiet", 9, "-q", NULL));
+}
+
+void test_selftest__tap(void)
+{
+ cl_invoke(assert_output("combined", "tap", 9, "-t", NULL));
+}
+
+void test_selftest__suite_names(void)
+{
+ cl_invoke(assert_output("combined", "suite_names", 0, "-l", NULL));
+}
+
+void test_selftest__summary_without_filename(void)
+{
+ struct stat st;
+ cl_invoke(assert_output("combined", "summary_without_filename", 9, "-r", NULL));
+ /* The summary contains timestamps, so we cannot verify its contents. */
+ cl_must_pass(stat("summary.xml", &st));
+}
+
+void test_selftest__summary_with_filename(void)
+{
+ struct stat st;
+ cl_invoke(assert_output("combined", "summary_with_filename", 9, "-rdifferent.xml", NULL));
+ /* The summary contains timestamps, so we cannot verify its contents. */
+ cl_must_pass(stat("different.xml", &st));
+}
+
+void test_selftest__pointer_equal(void)
+{
+ const char *args[] = {
+ "-spointer::equal",
+ "-t"
+ };
+ char *output = execute("pointer", 0, args, 2);
+ cl_assert_equal_s(output,
+ "TAP version 13\n"
+ "# start of suite 1: pointer\n"
+ "ok 1 - pointer::equal\n"
+ "1..1\n"
+ );
+ free(output);
+}
+
+void test_selftest__pointer_unequal(void)
+{
+ const char *args[] = {
+ "-spointer::unequal",
+ };
+ char *output = execute("pointer", 1, args, 1);
+ cl_assert(output);
+ cl_assert(strstr(output, "Pointer mismatch: "));
+ free(output);
+}
diff --git a/t/unit-tests/clar/test/selftest.h b/t/unit-tests/clar/test/selftest.h
new file mode 100644
index 0000000000..c24e0c5af4
--- /dev/null
+++ b/t/unit-tests/clar/test/selftest.h
@@ -0,0 +1,3 @@
+#include "clar.h"
+
+extern const char *selftest_suite_directory;
diff --git a/t/unit-tests/clar/test/suites/CMakeLists.txt b/t/unit-tests/clar/test/suites/CMakeLists.txt
new file mode 100644
index 0000000000..fa8ab9416a
--- /dev/null
+++ b/t/unit-tests/clar/test/suites/CMakeLists.txt
@@ -0,0 +1,53 @@
+list(APPEND suites
+ "combined"
+ "pointer"
+)
+
+foreach(suite IN LISTS suites)
+ add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${suite}/clar.suite"
+ COMMAND "${Python_EXECUTABLE}"
+ "${CMAKE_SOURCE_DIR}/generate.py"
+ "${CMAKE_CURRENT_SOURCE_DIR}/${suite}.c"
+ --output "${CMAKE_CURRENT_BINARY_DIR}/${suite}"
+ DEPENDS ${suite}.c
+ WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+ )
+
+ add_executable(${suite}_suite)
+ set_target_properties(${suite}_suite PROPERTIES
+ C_STANDARD 90
+ C_STANDARD_REQUIRED ON
+ C_EXTENSIONS OFF
+ )
+
+ # MSVC generates all kinds of warnings. We may want to fix these in the future
+ # and then unconditionally treat warnings as errors.
+ if(NOT MSVC)
+ set_target_properties(${suite}_suite PROPERTIES
+ COMPILE_WARNING_AS_ERROR ON
+ )
+ endif()
+
+ target_sources(${suite}_suite PRIVATE
+ main.c
+ ${suite}.c
+ "${CMAKE_CURRENT_BINARY_DIR}/${suite}/clar.suite"
+ )
+ target_compile_definitions(${suite}_suite PRIVATE
+ CLAR_FIXTURE_PATH="${CMAKE_CURRENT_SOURCE_DIR}/resources/"
+ CLAR_SELFTEST
+ )
+ target_compile_options(${suite}_suite PRIVATE
+ $<IF:$<CXX_COMPILER_ID:MSVC>,/W4,-Wall>
+ )
+ target_include_directories(${suite}_suite PRIVATE
+ "${CMAKE_SOURCE_DIR}"
+ "${CMAKE_CURRENT_BINARY_DIR}/${suite}"
+ )
+ target_link_libraries(${suite}_suite clar)
+
+ add_test(NAME build_${suite}_suite
+ COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}" --config "$<CONFIG>" --target selftest
+ )
+ set_tests_properties(build_${suite}_suite PROPERTIES FIXTURES_SETUP clar_test_fixture)
+endforeach()
diff --git a/t/unit-tests/clar/test/sample.c b/t/unit-tests/clar/test/suites/combined.c
index faa1209262..e8b41c98c3 100644
--- a/t/unit-tests/clar/test/sample.c
+++ b/t/unit-tests/clar/test/suites/combined.c
@@ -1,6 +1,7 @@
-#include "clar_test.h"
#include <sys/stat.h>
+#include "clar.h"
+
static int file_size(const char *filename)
{
struct stat st;
@@ -10,19 +11,14 @@ static int file_size(const char *filename)
return -1;
}
-void test_sample__initialize(void)
-{
- global_test_counter++;
-}
-
-void test_sample__cleanup(void)
+void test_combined__cleanup(void)
{
cl_fixture_cleanup("test");
cl_assert(file_size("test/file") == -1);
}
-void test_sample__1(void)
+void test_combined__1(void)
{
cl_assert(1);
cl_must_pass(0); /* 0 == success */
@@ -30,7 +26,7 @@ void test_sample__1(void)
cl_must_pass(-1); /* demonstrate a failing call */
}
-void test_sample__2(void)
+void test_combined__2(void)
{
cl_fixture_sandbox("test");
@@ -39,7 +35,7 @@ void test_sample__2(void)
cl_assert(100 == 101);
}
-void test_sample__strings(void)
+void test_combined__strings(void)
{
const char *actual = "expected";
cl_assert_equal_s("expected", actual);
@@ -47,7 +43,7 @@ void test_sample__strings(void)
cl_assert_equal_s_("mismatched", actual, "this one fails");
}
-void test_sample__strings_with_length(void)
+void test_combined__strings_with_length(void)
{
const char *actual = "expected";
cl_assert_equal_strn("expected_", actual, 8);
@@ -56,29 +52,34 @@ void test_sample__strings_with_length(void)
cl_assert_equal_strn_("exactly", actual, 3, "this one fails");
}
-void test_sample__int(void)
+void test_combined__int(void)
{
int value = 100;
cl_assert_equal_i(100, value);
cl_assert_equal_i_(101, value, "extra note on failing test");
}
-void test_sample__int_fmt(void)
+void test_combined__int_fmt(void)
{
int value = 100;
cl_assert_equal_i_fmt(022, value, "%04o");
}
-void test_sample__bool(void)
+void test_combined__bool(void)
{
int value = 100;
cl_assert_equal_b(1, value); /* test equality as booleans */
cl_assert_equal_b(0, value);
}
-void test_sample__ptr(void)
+void test_combined__multiline_description(void)
{
- const char *actual = "expected";
- cl_assert_equal_p(actual, actual); /* pointers to same object */
- cl_assert_equal_p(&actual, actual);
+ cl_must_pass_(-1, "description line 1\ndescription line 2");
+}
+
+void test_combined__null_string(void)
+{
+ const char *actual = NULL;
+ cl_assert_equal_s(actual, actual);
+ cl_assert_equal_s_("expected", actual, "this one fails");
}
diff --git a/t/unit-tests/clar/test/suites/main.c b/t/unit-tests/clar/test/suites/main.c
new file mode 100644
index 0000000000..3ab581d390
--- /dev/null
+++ b/t/unit-tests/clar/test/suites/main.c
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) Vicent Marti. All rights reserved.
+ *
+ * This file is part of clar, distributed under the ISC license.
+ * For full terms see the included COPYING file.
+ */
+
+#include "clar.h"
+
+/*
+ * Selftest main() for clar tests.
+ *
+ * You should write your own main routine for clar tests that does specific
+ * setup and teardown as necessary for your application. The only required
+ * line is the call to `clar_test(argc, argv)`, which will execute the test
+ * suite. If you want to check the return value of the test application,
+ * your main() should return the same value returned by clar_test().
+ */
+
+#ifdef _WIN32
+int __cdecl main(int argc, char *argv[])
+#else
+int main(int argc, char *argv[])
+#endif
+{
+ return clar_test(argc, argv);
+}
diff --git a/t/unit-tests/clar/test/suites/pointer.c b/t/unit-tests/clar/test/suites/pointer.c
new file mode 100644
index 0000000000..20535b159e
--- /dev/null
+++ b/t/unit-tests/clar/test/suites/pointer.c
@@ -0,0 +1,13 @@
+#include "clar.h"
+
+void test_pointer__equal(void)
+{
+ void *p1 = (void *)0x1;
+ cl_assert_equal_p(p1, p1);
+}
+
+void test_pointer__unequal(void)
+{
+ void *p1 = (void *)0x1, *p2 = (void *)0x2;
+ cl_assert_equal_p(p1, p2);
+}
diff --git a/t/unit-tests/clar/test/resources/test/file b/t/unit-tests/clar/test/suites/resources/test/file
index 220f4aa98a..220f4aa98a 100644
--- a/t/unit-tests/clar/test/resources/test/file
+++ b/t/unit-tests/clar/test/suites/resources/test/file
diff --git a/t/unit-tests/u-dir.c b/t/unit-tests/u-dir.c
new file mode 100644
index 0000000000..2d0adaa39e
--- /dev/null
+++ b/t/unit-tests/u-dir.c
@@ -0,0 +1,47 @@
+#include "unit-test.h"
+#include "dir.h"
+
+#define TEST_WITHIN_DEPTH(path, depth, max_depth, expect) do { \
+ int actual = within_depth(path, strlen(path), \
+ depth, max_depth); \
+ if (actual != expect) \
+ cl_failf("path '%s' with depth '%d' and max-depth '%d': expected %d, got %d", \
+ path, depth, max_depth, expect, actual); \
+ } while (0)
+
+void test_dir__within_depth(void)
+{
+ /* depth = 0; max_depth = 0 */
+ TEST_WITHIN_DEPTH("", 0, 0, 1);
+ TEST_WITHIN_DEPTH("file", 0, 0, 1);
+ TEST_WITHIN_DEPTH("a", 0, 0, 1);
+ TEST_WITHIN_DEPTH("a/file", 0, 0, 0);
+ TEST_WITHIN_DEPTH("a/b", 0, 0, 0);
+ TEST_WITHIN_DEPTH("a/b/file", 0, 0, 0);
+
+ /* depth = 0; max_depth = 1 */
+ TEST_WITHIN_DEPTH("", 0, 1, 1);
+ TEST_WITHIN_DEPTH("file", 0, 1, 1);
+ TEST_WITHIN_DEPTH("a", 0, 1, 1);
+ TEST_WITHIN_DEPTH("a/file", 0, 1, 1);
+ TEST_WITHIN_DEPTH("a/b", 0, 1, 1);
+ TEST_WITHIN_DEPTH("a/b/file", 0, 1, 0);
+
+ /* depth = 1; max_depth = 1 */
+ TEST_WITHIN_DEPTH("", 1, 1, 1);
+ TEST_WITHIN_DEPTH("file", 1, 1, 1);
+ TEST_WITHIN_DEPTH("a", 1, 1, 1);
+ TEST_WITHIN_DEPTH("a/file", 1, 1, 0);
+ TEST_WITHIN_DEPTH("a/b", 1, 1, 0);
+ TEST_WITHIN_DEPTH("a/b/file", 1, 1, 0);
+
+ /* depth = 1; max_depth = 0 */
+ TEST_WITHIN_DEPTH("", 1, 0, 0);
+ TEST_WITHIN_DEPTH("file", 1, 0, 0);
+ TEST_WITHIN_DEPTH("a", 1, 0, 0);
+ TEST_WITHIN_DEPTH("a/file", 1, 0, 0);
+ TEST_WITHIN_DEPTH("a/b", 1, 0, 0);
+ TEST_WITHIN_DEPTH("a/b/file", 1, 0, 0);
+
+
+}
diff --git a/t/unit-tests/u-reftable-basics.c b/t/unit-tests/u-reftable-basics.c
index a0471083e7..73566ed0eb 100644
--- a/t/unit-tests/u-reftable-basics.c
+++ b/t/unit-tests/u-reftable-basics.c
@@ -9,6 +9,7 @@ https://developers.google.com/open-source/licenses/bsd
#include "unit-test.h"
#include "lib-reftable.h"
#include "reftable/basics.h"
+#include "reftable/reftable-error.h"
struct integer_needle_lesseq_args {
int needle;
@@ -79,14 +80,18 @@ void test_reftable_basics__names_equal(void)
void test_reftable_basics__parse_names(void)
{
char in1[] = "line\n";
- char in2[] = "a\nb\nc";
- char **out = parse_names(in1, strlen(in1));
+ char in2[] = "a\nb\nc\n";
+ char **out = NULL;
+ int err = parse_names(in1, strlen(in1), &out);
+ cl_assert(err == 0);
cl_assert(out != NULL);
cl_assert_equal_s(out[0], "line");
cl_assert(!out[1]);
free_names(out);
- out = parse_names(in2, strlen(in2));
+ out = NULL;
+ err = parse_names(in2, strlen(in2), &out);
+ cl_assert(err == 0);
cl_assert(out != NULL);
cl_assert_equal_s(out[0], "a");
cl_assert_equal_s(out[1], "b");
@@ -95,10 +100,21 @@ void test_reftable_basics__parse_names(void)
free_names(out);
}
+void test_reftable_basics__parse_names_missing_newline(void)
+{
+ char in1[] = "line\nline2";
+ char **out = NULL;
+ int err = parse_names(in1, strlen(in1), &out);
+ cl_assert(err == REFTABLE_FORMAT_ERROR);
+ cl_assert(out == NULL);
+}
+
void test_reftable_basics__parse_names_drop_empty_string(void)
{
char in[] = "a\n\nb\n";
- char **out = parse_names(in, strlen(in));
+ char **out = NULL;
+ int err = parse_names(in, strlen(in), &out);
+ cl_assert(err == 0);
cl_assert(out != NULL);
cl_assert_equal_s(out[0], "a");
/* simply '\n' should be dropped as empty string */
diff --git a/t/unit-tests/u-reftable-stack.c b/t/unit-tests/u-reftable-stack.c
index e4ea57138e..a8b91812e8 100644
--- a/t/unit-tests/u-reftable-stack.c
+++ b/t/unit-tests/u-reftable-stack.c
@@ -128,7 +128,7 @@ static void write_n_ref_tables(struct reftable_stack *st,
cl_reftable_set_hash(ref.value.val1, i, REFTABLE_HASH_SHA1);
cl_assert_equal_i(reftable_stack_add(st,
- &write_test_ref, &ref), 0);
+ &write_test_ref, &ref, 0), 0);
}
st->opts.disable_auto_compact = disable_auto_compact;
@@ -171,7 +171,7 @@ void test_reftable_stack__add_one(void)
err = reftable_new_stack(&st, dir, &opts);
cl_assert(!err);
- err = reftable_stack_add(st, write_test_ref, &ref);
+ err = reftable_stack_add(st, write_test_ref, &ref, 0);
cl_assert(!err);
err = reftable_stack_read_ref(st, ref.refname, &dest);
@@ -235,12 +235,12 @@ void test_reftable_stack__uptodate(void)
cl_assert_equal_i(reftable_new_stack(&st1, dir, &opts), 0);
cl_assert_equal_i(reftable_new_stack(&st2, dir, &opts), 0);
cl_assert_equal_i(reftable_stack_add(st1, write_test_ref,
- &ref1), 0);
+ &ref1, 0), 0);
cl_assert_equal_i(reftable_stack_add(st2, write_test_ref,
- &ref2), REFTABLE_OUTDATED_ERROR);
+ &ref2, 0), REFTABLE_OUTDATED_ERROR);
cl_assert_equal_i(reftable_stack_reload(st2), 0);
cl_assert_equal_i(reftable_stack_add(st2, write_test_ref,
- &ref2), 0);
+ &ref2, 0), 0);
reftable_stack_destroy(st1);
reftable_stack_destroy(st2);
clear_dir(dir);
@@ -406,7 +406,7 @@ void test_reftable_stack__auto_compaction_fails_gracefully(void)
cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0);
cl_assert_equal_i(reftable_stack_add(st, write_test_ref,
- &ref), 0);
+ &ref, 0), 0);
cl_assert_equal_i(st->merged->tables_len, 1);
cl_assert_equal_i(st->stats.attempts, 0);
cl_assert_equal_i(st->stats.failures, 0);
@@ -424,7 +424,7 @@ void test_reftable_stack__auto_compaction_fails_gracefully(void)
write_file_buf(table_path.buf, "", 0);
ref.update_index = 2;
- err = reftable_stack_add(st, write_test_ref, &ref);
+ err = reftable_stack_add(st, write_test_ref, &ref, 0);
cl_assert(!err);
cl_assert_equal_i(st->merged->tables_len, 2);
cl_assert_equal_i(st->stats.attempts, 1);
@@ -460,9 +460,9 @@ void test_reftable_stack__update_index_check(void)
cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0);
cl_assert_equal_i(reftable_stack_add(st, write_test_ref,
- &ref1), 0);
+ &ref1, 0), 0);
cl_assert_equal_i(reftable_stack_add(st, write_test_ref,
- &ref2), REFTABLE_API_ERROR);
+ &ref2, 0), REFTABLE_API_ERROR);
reftable_stack_destroy(st);
clear_dir(dir);
}
@@ -477,7 +477,7 @@ void test_reftable_stack__lock_failure(void)
cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0);
for (i = -1; i != REFTABLE_EMPTY_TABLE_ERROR; i--)
cl_assert_equal_i(reftable_stack_add(st, write_error,
- &i), i);
+ &i, 0), i);
reftable_stack_destroy(st);
clear_dir(dir);
@@ -521,7 +521,7 @@ void test_reftable_stack__add(void)
for (i = 0; i < N; i++)
cl_assert_equal_i(reftable_stack_add(st, write_test_ref,
- &refs[i]), 0);
+ &refs[i], 0), 0);
for (i = 0; i < N; i++) {
struct write_log_arg arg = {
@@ -529,7 +529,7 @@ void test_reftable_stack__add(void)
.update_index = reftable_stack_next_update_index(st),
};
cl_assert_equal_i(reftable_stack_add(st, write_test_log,
- &arg), 0);
+ &arg, 0), 0);
}
cl_assert_equal_i(reftable_stack_compact_all(st, NULL), 0);
@@ -612,8 +612,8 @@ void test_reftable_stack__iterator(void)
}
for (i = 0; i < N; i++)
- cl_assert_equal_i(reftable_stack_add(st,
- write_test_ref, &refs[i]), 0);
+ cl_assert_equal_i(reftable_stack_add(st, write_test_ref,
+ &refs[i], 0), 0);
for (i = 0; i < N; i++) {
struct write_log_arg arg = {
@@ -621,8 +621,8 @@ void test_reftable_stack__iterator(void)
.update_index = reftable_stack_next_update_index(st),
};
- cl_assert_equal_i(reftable_stack_add(st,
- write_test_log, &arg), 0);
+ cl_assert_equal_i(reftable_stack_add(st, write_test_log,
+ &arg, 0), 0);
}
reftable_stack_init_ref_iterator(st, &it);
@@ -697,11 +697,11 @@ void test_reftable_stack__log_normalize(void)
input.value.update.message = (char *) "one\ntwo";
cl_assert_equal_i(reftable_stack_add(st, write_test_log,
- &arg), REFTABLE_API_ERROR);
+ &arg, 0), REFTABLE_API_ERROR);
input.value.update.message = (char *) "one";
cl_assert_equal_i(reftable_stack_add(st, write_test_log,
- &arg), 0);
+ &arg, 0), 0);
cl_assert_equal_i(reftable_stack_read_log(st, input.refname,
&dest), 0);
cl_assert_equal_s(dest.value.update.message, "one\n");
@@ -709,7 +709,7 @@ void test_reftable_stack__log_normalize(void)
input.value.update.message = (char *) "two\n";
arg.update_index = 2;
cl_assert_equal_i(reftable_stack_add(st, write_test_log,
- &arg), 0);
+ &arg, 0), 0);
cl_assert_equal_i(reftable_stack_read_log(st, input.refname,
&dest), 0);
cl_assert_equal_s(dest.value.update.message, "two\n");
@@ -759,15 +759,16 @@ void test_reftable_stack__tombstone(void)
}
}
for (i = 0; i < N; i++)
- cl_assert_equal_i(reftable_stack_add(st, write_test_ref, &refs[i]), 0);
+ cl_assert_equal_i(reftable_stack_add(st, write_test_ref,
+ &refs[i], 0), 0);
for (i = 0; i < N; i++) {
struct write_log_arg arg = {
.log = &logs[i],
.update_index = reftable_stack_next_update_index(st),
};
- cl_assert_equal_i(reftable_stack_add(st,
- write_test_log, &arg), 0);
+ cl_assert_equal_i(reftable_stack_add(st, write_test_log,
+ &arg, 0), 0);
}
cl_assert_equal_i(reftable_stack_read_ref(st, "branch",
@@ -815,7 +816,7 @@ void test_reftable_stack__hash_id(void)
cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0);
cl_assert_equal_i(reftable_stack_add(st, write_test_ref,
- &ref), 0);
+ &ref, 0), 0);
/* can't read it with the wrong hash ID. */
cl_assert_equal_i(reftable_new_stack(&st32, dir,
@@ -884,7 +885,7 @@ void test_reftable_stack__reflog_expire(void)
.update_index = reftable_stack_next_update_index(st),
};
cl_assert_equal_i(reftable_stack_add(st, write_test_log,
- &arg), 0);
+ &arg, 0), 0);
}
cl_assert_equal_i(reftable_stack_compact_all(st, NULL), 0);
@@ -924,7 +925,7 @@ void test_reftable_stack__empty_add(void)
cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0);
cl_assert_equal_i(reftable_stack_add(st, write_nothing,
- NULL), 0);
+ NULL, 0), 0);
cl_assert_equal_i(reftable_new_stack(&st2, dir, &opts), 0);
clear_dir(dir);
reftable_stack_destroy(st);
@@ -963,7 +964,7 @@ void test_reftable_stack__auto_compaction(void)
};
snprintf(name, sizeof(name), "branch%04"PRIuMAX, (uintmax_t)i);
- err = reftable_stack_add(st, write_test_ref, &ref);
+ err = reftable_stack_add(st, write_test_ref, &ref, 0);
cl_assert(!err);
err = reftable_stack_auto_compact(st);
@@ -999,7 +1000,7 @@ void test_reftable_stack__auto_compaction_factor(void)
};
xsnprintf(name, sizeof(name), "branch%04"PRIuMAX, (uintmax_t)i);
- err = reftable_stack_add(st, &write_test_ref, &ref);
+ err = reftable_stack_add(st, &write_test_ref, &ref, 0);
cl_assert(!err);
cl_assert(i < 5 || st->merged->tables_len < 5 * fastlogN(i, 5));
@@ -1078,8 +1079,8 @@ void test_reftable_stack__add_performs_auto_compaction(void)
snprintf(buf, sizeof(buf), "branch-%04"PRIuMAX, (uintmax_t)i);
ref.refname = buf;
- cl_assert_equal_i(reftable_stack_add(st,
- write_test_ref, &ref), 0);
+ cl_assert_equal_i(reftable_stack_add(st, write_test_ref,
+ &ref, 0), 0);
/*
* The stack length should grow continuously for all runs where
diff --git a/t/unit-tests/u-string-list.c b/t/unit-tests/u-string-list.c
index d4ba5f9fa5..a2457d7b1e 100644
--- a/t/unit-tests/u-string-list.c
+++ b/t/unit-tests/u-string-list.c
@@ -43,7 +43,7 @@ static void t_string_list_equal(struct string_list *list,
expected_strings->items[i].string);
}
-static void t_string_list_split(const char *data, int delim, int maxsplit, ...)
+static void t_string_list_split(const char *data, const char *delim, int maxsplit, ...)
{
struct string_list expected_strings = STRING_LIST_INIT_DUP;
struct string_list list = STRING_LIST_INIT_DUP;
@@ -63,15 +63,94 @@ static void t_string_list_split(const char *data, int delim, int maxsplit, ...)
string_list_clear(&list, 0);
}
+static void t_string_list_split_f(const char *data, const char *delim,
+ int maxsplit, unsigned flags, ...)
+{
+ struct string_list expected_strings = STRING_LIST_INIT_DUP;
+ struct string_list list = STRING_LIST_INIT_DUP;
+ va_list ap;
+ int len;
+
+ va_start(ap, flags);
+ t_vcreate_string_list_dup(&expected_strings, 0, ap);
+ va_end(ap);
+
+ string_list_clear(&list, 0);
+ len = string_list_split_f(&list, data, delim, maxsplit, flags);
+ cl_assert_equal_i(len, expected_strings.nr);
+ t_string_list_equal(&list, &expected_strings);
+
+ string_list_clear(&expected_strings, 0);
+ string_list_clear(&list, 0);
+}
+
+void test_string_list__split_f(void)
+{
+ t_string_list_split_f("::foo:bar:baz:", ":", -1, 0,
+ "", "", "foo", "bar", "baz", "", NULL);
+ t_string_list_split_f(" foo:bar : baz", ":", -1, STRING_LIST_SPLIT_TRIM,
+ "foo", "bar", "baz", NULL);
+ t_string_list_split_f(" a b c ", " ", 1, STRING_LIST_SPLIT_TRIM,
+ "a", "b c", NULL);
+ t_string_list_split_f("::foo::bar:baz:", ":", -1, STRING_LIST_SPLIT_NONEMPTY,
+ "foo", "bar", "baz", NULL);
+ t_string_list_split_f("foo:baz", ":", -1, STRING_LIST_SPLIT_NONEMPTY,
+ "foo", "baz", NULL);
+ t_string_list_split_f("foo :: : baz", ":", -1,
+ STRING_LIST_SPLIT_NONEMPTY | STRING_LIST_SPLIT_TRIM,
+ "foo", "baz", NULL);
+}
+
+static void t_string_list_split_in_place_f(const char *data_, const char *delim,
+ int maxsplit, unsigned flags, ...)
+{
+ struct string_list expected_strings = STRING_LIST_INIT_DUP;
+ struct string_list list = STRING_LIST_INIT_NODUP;
+ char *data = xstrdup(data_);
+ va_list ap;
+ int len;
+
+ va_start(ap, flags);
+ t_vcreate_string_list_dup(&expected_strings, 0, ap);
+ va_end(ap);
+
+ string_list_clear(&list, 0);
+ len = string_list_split_in_place_f(&list, data, delim, maxsplit, flags);
+ cl_assert_equal_i(len, expected_strings.nr);
+ t_string_list_equal(&list, &expected_strings);
+
+ free(data);
+ string_list_clear(&expected_strings, 0);
+ string_list_clear(&list, 0);
+}
+
+void test_string_list__split_in_place_f(void)
+{
+ t_string_list_split_in_place_f("::foo:bar:baz:", ":", -1, 0,
+ "", "", "foo", "bar", "baz", "", NULL);
+ t_string_list_split_in_place_f(" foo:bar : baz", ":", -1, STRING_LIST_SPLIT_TRIM,
+ "foo", "bar", "baz", NULL);
+ t_string_list_split_in_place_f(" a b c ", " ", 1, STRING_LIST_SPLIT_TRIM,
+ "a", "b c", NULL);
+ t_string_list_split_in_place_f("::foo::bar:baz:", ":", -1,
+ STRING_LIST_SPLIT_NONEMPTY,
+ "foo", "bar", "baz", NULL);
+ t_string_list_split_in_place_f("foo:baz", ":", -1, STRING_LIST_SPLIT_NONEMPTY,
+ "foo", "baz", NULL);
+ t_string_list_split_in_place_f("foo :: : baz", ":", -1,
+ STRING_LIST_SPLIT_NONEMPTY | STRING_LIST_SPLIT_TRIM,
+ "foo", "baz", NULL);
+}
+
void test_string_list__split(void)
{
- t_string_list_split("foo:bar:baz", ':', -1, "foo", "bar", "baz", NULL);
- t_string_list_split("foo:bar:baz", ':', 0, "foo:bar:baz", NULL);
- t_string_list_split("foo:bar:baz", ':', 1, "foo", "bar:baz", NULL);
- t_string_list_split("foo:bar:baz", ':', 2, "foo", "bar", "baz", NULL);
- t_string_list_split("foo:bar:", ':', -1, "foo", "bar", "", NULL);
- t_string_list_split("", ':', -1, "", NULL);
- t_string_list_split(":", ':', -1, "", "", NULL);
+ t_string_list_split("foo:bar:baz", ":", -1, "foo", "bar", "baz", NULL);
+ t_string_list_split("foo:bar:baz", ":", 0, "foo:bar:baz", NULL);
+ t_string_list_split("foo:bar:baz", ":", 1, "foo", "bar:baz", NULL);
+ t_string_list_split("foo:bar:baz", ":", 2, "foo", "bar", "baz", NULL);
+ t_string_list_split("foo:bar:", ":", -1, "foo", "bar", "", NULL);
+ t_string_list_split("", ":", -1, "", NULL);
+ t_string_list_split(":", ":", -1, "", "", NULL);
}
static void t_string_list_split_in_place(const char *data, const char *delim,