{"thread":{"id":"60390","subject":"[PATCH 00/11] t: reduce direct disk access to data structures","startedAt":"2023-10-18T05:35:13Z","lastAt":"2023-11-02T08:47:22Z","messageCount":59,"participants":["Patrick Steinhardt","Han-Wen Nienhuys","Junio C Hamano","Eric Sunshine","Taylor Blau"],"isPatch":true,"patchVersion":1,"patchTotal":11},"messages":[{"id":"483383","messageId":"cover.1697607222.git.ps@pks.im","threadId":"60390","inReplyTo":null,"subject":"[PATCH 00/11] t: reduce direct disk access to data structures","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-18T05:35:03Z","receivedAt":"2023-10-18T05:35:13Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Hi,\n\nthis patch series refactors a bunch of our tests to perform less direct\ndisk access to on-disk data structures. Instead, the tests are converted\nto use Git tools or our test-tool to access data to the best extent\npossible. This serves two benefits:\n\n    - We increase test coverage of our own code base.\n\n    - We become less dependent on the actual on-disk format.\n\nThe main motivation for this patch series was the second bullet point as\nit is preparatory work to get the reftable backend upstreamed. My intent\nis to get rid of many or even most of the current blockers in the Git\nproject before trying to send the reftable implementation upstream.\nWhile this will be a lot of up-front work that is going to span over a\nlong time period, I think this approach will make everyones live easier\nby doing comparatively small and incremental improvements to the Git\nproject. Ultimately, the final patch series should in the best case only\ncontain the new backend as well as testing infrastructure, but not much\nelse.\n\nPatrick\n\nPatrick Steinhardt (11):\n  t: add helpers to test for reference existence\n  t: allow skipping expected object ID in `ref-store update-ref`\n  t: convert tests to use helpers for reference existence\n  t: convert tests to not write references via the filesystem\n  t: convert tests to not access symrefs via the filesystem\n  t: convert tests to not access reflog via the filesystem\n  t1450: convert tests to remove worktrees via git-worktree(1)\n  t4207: delete replace references via git-update-ref(1)\n  t7300: assert exact states of repo\n  t7900: assert the absence of refs via git-for-each-ref(1)\n  t: mark several tests that assume the files backend with REFFILES\n\n t/README                           |  9 ++++\n t/helper/test-ref-store.c          | 38 +++++++++++++--\n t/t1400-update-ref.sh              | 49 ++++++++++----------\n t/t1430-bad-ref-name.sh            | 39 ++++++++++------\n t/t1450-fsck.sh                    | 46 ++++++++++---------\n t/t2011-checkout-invalid-head.sh   | 16 +++----\n t/t3200-branch.sh                  | 74 ++++++++++++++++--------------\n t/t3400-rebase.sh                  |  2 +-\n t/t3404-rebase-interactive.sh      |  2 +-\n t/t4013-diff-various.sh            |  2 +-\n t/t4202-log.sh                     |  2 +-\n t/t4207-log-decoration-colors.sh   |  4 +-\n t/t5521-pull-options.sh            |  4 +-\n t/t5526-fetch-submodules.sh        |  2 +-\n t/t5605-clone-local.sh             |  6 +--\n t/t5702-protocol-v2.sh             | 24 +++++++---\n t/t7300-clean.sh                   | 23 ++++++----\n t/t7900-maintenance.sh             |  3 +-\n t/t9133-git-svn-nested-git-repo.sh |  2 +-\n t/test-lib-functions.sh            | 66 ++++++++++++++++++++++++++\n 20 files changed, 277 insertions(+), 136 deletions(-)\n\n-- \n2.42.0\n\n"},{"id":"483384","messageId":"e947feb1c77f7e9f3c7f983bbe47137fbce42367.1697607222.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1697607222.git.ps@pks.im","subject":"[PATCH 01/11] t: add helpers to test for reference existence","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-18T05:35:07Z","receivedAt":"2023-10-18T05:35:13Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"There are two major ways to check for the existence of a reference in\nour tests:\n\n    - `git rev-parse --verify` can be used to check for existence of a\n      reference. This only works in the case where the reference is well\n      formed though and resolves to an actual object ID. This does not\n      work with malformed reference names or invalid contents.\n\n    - `test_path_is_file` can be used to check for existence of a loose\n      reference if it is known to not resolve to an actual object ID. It\n      by necessity reaches into implementation details of the reference\n      backend though.\n\nSimilarly, there are two equivalent ways to check for the absence of a\nreference:\n\n    - `test_must_fail git rev-parse` can be used to check for the\n      absence of a reference. It could fail due to a number of reasons\n      though, and all of these reasons will be thrown into the same bag\n      as an absent reference.\n\n    - `test_path_is_missing` can be used to check explicitly for the\n      absence of a loose reference, but again reaches into internal\n      implementation details of the reference backend.\n\nSo both our tooling to check for the presence and for the absence of\nreferences in tests is lacking as either failure cases are thrown into\nthe same bag or we need to reach into internal implementation details of\nthe respective reference backend.\n\nIntroduce a new subcommand for our ref-store test helper that explicitly\nchecks only for the presence or absence of a reference. This addresses\nthese limitations:\n\n    - We can check for the presence of references with malformed names.\n\n    - We can check for the presence of references that don't resolve.\n\n    - We can explicitly handle the case where a reference is missing by\n      special-casing ENOENT errors.\n\n    - We don't need to reach into implementation details of the backend,\n      which would allow us to use this helper for the future reftable\n      backend.\n\nNext to this subcommand we also provide two wrappers `test_ref_exists`\nand `test_ref_missing` that make the helper easier to use.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/README                  |  9 ++++++\n t/helper/test-ref-store.c | 27 +++++++++++++++-\n t/test-lib-functions.sh   | 66 +++++++++++++++++++++++++++++++++++++++\n 3 files changed, 101 insertions(+), 1 deletion(-)\n\ndiff --git a/t/README b/t/README\nindex 61080859899..779f7e7dd86 100644\n--- a/t/README\n+++ b/t/README\n@@ -928,6 +928,15 @@ see test-lib-functions.sh for the full list and their options.\n    committer times to defined state.  Subsequent calls will\n    advance the times by a fixed amount.\n \n+ - test_ref_exists <ref>, test_ref_missing <ref>\n+\n+   Check whether a reference exists or is missing. In contrast to\n+   git-rev-parse(1), these helpers also work with invalid reference\n+   names and references whose contents are unresolvable. The latter\n+   function also distinguishes generic errors from the case where a\n+   reference explicitly doesn't exist and is thus safer to use than\n+   `test_must_fail git rev-parse`.\n+\n  - test_commit <message> [<filename> [<contents>]]\n \n    Creates a commit with the given message, committing the given\ndiff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\nindex 48552e6a9e0..7400f560ab6 100644\n--- a/t/helper/test-ref-store.c\n+++ b/t/helper/test-ref-store.c\n@@ -1,6 +1,6 @@\n #include \"test-tool.h\"\n #include \"hex.h\"\n-#include \"refs.h\"\n+#include \"refs/refs-internal.h\"\n #include \"setup.h\"\n #include \"worktree.h\"\n #include \"object-store-ll.h\"\n@@ -221,6 +221,30 @@ static int cmd_verify_ref(struct ref_store *refs, const char **argv)\n \treturn ret;\n }\n \n+static int cmd_ref_exists(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\tstruct strbuf unused_referent = STRBUF_INIT;\n+\tstruct object_id unused_oid;\n+\tunsigned int unused_type;\n+\tint failure_errno;\n+\n+\tif (refs_read_raw_ref(refs, refname, &unused_oid, &unused_referent,\n+\t\t\t      &unused_type, &failure_errno)) {\n+\t\t/*\n+\t\t * We handle ENOENT separately here such that it is possible to\n+\t\t * distinguish actually-missing references from any kind of\n+\t\t * generic error.\n+\t\t */\n+\t\tif (failure_errno == ENOENT)\n+\t\t\treturn 17;\n+\t\treturn -1;\n+\t}\n+\n+\tstrbuf_release(&unused_referent);\n+\treturn 0;\n+}\n+\n static int cmd_for_each_reflog(struct ref_store *refs,\n \t\t\t       const char **argv UNUSED)\n {\n@@ -325,6 +349,7 @@ static struct command commands[] = {\n \t{ \"for-each-ref--exclude\", cmd_for_each_ref__exclude },\n \t{ \"resolve-ref\", cmd_resolve_ref },\n \t{ \"verify-ref\", cmd_verify_ref },\n+\t{ \"ref-exists\", cmd_ref_exists },\n \t{ \"for-each-reflog\", cmd_for_each_reflog },\n \t{ \"for-each-reflog-ent\", cmd_for_each_reflog_ent },\n \t{ \"for-each-reflog-ent-reverse\", cmd_for_each_reflog_ent_reverse },\ndiff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh\nindex 2f8868caa17..212fddffa96 100644\n--- a/t/test-lib-functions.sh\n+++ b/t/test-lib-functions.sh\n@@ -251,6 +251,72 @@ debug () {\n \tdone\n }\n \n+# Usage: test_ref_exists [options] <ref>\n+#   -C <dir>:\n+#\tRun all git commands in directory <dir>\n+#   --refdb <refdb>:\n+#\tThe reference database to run in. One of:\n+#\t\t- \"main\", the main reference database (default).\n+#\t\t- \"submodule:<submodule>\", the reference database of a\n+#\t\t  submodule.\n+#\t\t- \"worktree:<worktree>\", the reference database for a\n+#\t\t  worktree's per-worktree references.\n+#\n+# This helper function checks whether a reference exists. Symrefs will not be\n+# resolved. Can be used to check references with bad names.\n+test_ref_exists () {\n+\tlocal indir=\n+\tlocal refdb=main\n+\n+\twhile test $# != 0\n+\tdo\n+\t\tcase \"$1\" in\n+\t\t-C)\n+\t\t\tindir=\"$2\"\n+\t\t\tshift\n+\t\t\t;;\n+\t\t--refdb)\n+\t\t\trefdb=\"$2\"\n+\t\t\tshift\n+\t\t\t;;\n+\t\t*)\n+\t\t\tbreak\n+\t\t\t;;\n+\t\tesac\n+\t\tshift\n+\tdone &&\n+\n+\tindir=${indir:+\"$indir\"/} &&\n+\n+\tif test \"$#\" != 1\n+\tthen\n+\t\tBUG \"expected exactly one reference\"\n+\tfi &&\n+\n+\ttest-tool ${indir:+ -C \"$indir\"} ref-store \"${refdb}\" ref-exists \"$1\"\n+}\n+\n+# Behaves the same as test_ref_exists, except that it checks for the absence of\n+# a reference. This is preferable to `! test_ref_exists` as this function is\n+# able to distinguish actually-missing references from other, generic errors.\n+test_ref_missing () {\n+\ttest_ref_exists \"$@\"\n+\tcase \"$?\" in\n+\t17)\n+\t\t# This is the good case.\n+\t\treturn 0\n+\t\t;;\n+\t0)\n+\t\techo >&4 \"test_ref_missing: reference exists\"\n+\t\treturn 1\n+\t\t;;\n+\t*)\n+\t\techo >&4 \"test_ref_missing: generic error\"\n+\t\treturn 1\n+\t\t;;\n+\tesac\n+}\n+\n # Usage: test_commit [options] <message> [<file> [<contents> [<tag>]]]\n #   -C <dir>:\n #\tRun all git commands in directory <dir>\n-- \n2.42.0\n\n"},{"id":"483385","messageId":"1f615d62f99e9ab47d37500f05b29615bafffba2.1697607222.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1697607222.git.ps@pks.im","subject":"[PATCH 02/11] t: allow skipping expected object ID in `ref-store update-ref`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-18T05:35:12Z","receivedAt":"2023-10-18T05:35:16Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"We require the caller to pass both the old and new expected object ID to\nour `test-tool ref-store update-ref` helper. When trying to update a\nsymbolic reference though it's impossible to specify the expected object\nID, which means that the test would instead have to force-update the\nreference. This is currently impossible though.\n\nUpdate the helper to optionally skip verification of the old object ID\nin case the test passes in an empty old object ID as input.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/helper/test-ref-store.c | 11 +++++++----\n 1 file changed, 7 insertions(+), 4 deletions(-)\n\ndiff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\nindex 7400f560ab6..7dc83137584 100644\n--- a/t/helper/test-ref-store.c\n+++ b/t/helper/test-ref-store.c\n@@ -322,16 +322,19 @@ static int cmd_update_ref(struct ref_store *refs, const char **argv)\n \tconst char *new_sha1_buf = notnull(*argv++, \"new-sha1\");\n \tconst char *old_sha1_buf = notnull(*argv++, \"old-sha1\");\n \tunsigned int flags = arg_flags(*argv++, \"flags\", transaction_flags);\n-\tstruct object_id old_oid;\n+\tstruct object_id old_oid, *old_oid_ptr = NULL;\n \tstruct object_id new_oid;\n \n-\tif (get_oid_hex(old_sha1_buf, &old_oid))\n-\t\tdie(\"cannot parse %s as %s\", old_sha1_buf, the_hash_algo->name);\n+\tif (*old_sha1_buf) {\n+\t\tif (get_oid_hex(old_sha1_buf, &old_oid))\n+\t\t\tdie(\"cannot parse %s as %s\", old_sha1_buf, the_hash_algo->name);\n+\t\told_oid_ptr = &old_oid;\n+\t}\n \tif (get_oid_hex(new_sha1_buf, &new_oid))\n \t\tdie(\"cannot parse %s as %s\", new_sha1_buf, the_hash_algo->name);\n \n \treturn refs_update_ref(refs, msg, refname,\n-\t\t\t       &new_oid, &old_oid,\n+\t\t\t       &new_oid, old_oid_ptr,\n \t\t\t       flags, UPDATE_REFS_DIE_ON_ERR);\n }\n \n-- \n2.42.0\n\n"},{"id":"483386","messageId":"ac6a49c7c84ad2b836f099557dd6989703ebda8f.1697607222.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1697607222.git.ps@pks.im","subject":"[PATCH 03/11] t: convert tests to use helpers for reference existence","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-18T05:35:16Z","receivedAt":"2023-10-18T05:35:22Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Convert tests that use `test_path_is_file` and `test_path_is_missing` to\ninstead use our new helpers `test_ref_exists` and `test_ref_missing`.\nThese hook into the reference database directly and thus work indepently\nof the actual reference backend that is being used.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t1430-bad-ref-name.sh | 27 ++++++++++++++++++---------\n t/t3200-branch.sh       | 33 ++++++++++++++++++---------------\n t/t5521-pull-options.sh |  4 ++--\n t/t5605-clone-local.sh  |  2 +-\n 4 files changed, 39 insertions(+), 27 deletions(-)\n\ndiff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh\nindex ff1c967d550..7b7d6953c62 100755\n--- a/t/t1430-bad-ref-name.sh\n+++ b/t/t1430-bad-ref-name.sh\n@@ -205,8 +205,9 @@ test_expect_success 'update-ref --no-deref -d can delete symref to broken name'\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref\" &&\n \ttest-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname\" &&\n+\ttest_ref_exists refs/heads/badname &&\n \tgit update-ref --no-deref -d refs/heads/badname >output 2>error &&\n-\ttest_path_is_missing .git/refs/heads/badname &&\n+\ttest_ref_missing refs/heads/badname &&\n \ttest_must_be_empty output &&\n \ttest_must_be_empty error\n '\n@@ -216,8 +217,9 @@ test_expect_success 'branch -d can delete symref to broken name' '\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref\" &&\n \ttest-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname\" &&\n+\ttest_ref_exists refs/heads/badname &&\n \tgit branch -d badname >output 2>error &&\n-\ttest_path_is_missing .git/refs/heads/badname &&\n+\ttest_ref_missing refs/heads/badname &&\n \ttest_i18ngrep \"Deleted branch badname (was refs/heads/broken\\.\\.\\.ref)\" output &&\n \ttest_must_be_empty error\n '\n@@ -225,8 +227,9 @@ test_expect_success 'branch -d can delete symref to broken name' '\n test_expect_success 'update-ref --no-deref -d can delete dangling symref to broken name' '\n \ttest-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname\" &&\n+\ttest_ref_exists refs/heads/badname &&\n \tgit update-ref --no-deref -d refs/heads/badname >output 2>error &&\n-\ttest_path_is_missing .git/refs/heads/badname &&\n+\ttest_ref_missing refs/heads/badname &&\n \ttest_must_be_empty output &&\n \ttest_must_be_empty error\n '\n@@ -234,8 +237,9 @@ test_expect_success 'update-ref --no-deref -d can delete dangling symref to brok\n test_expect_success 'branch -d can delete dangling symref to broken name' '\n \ttest-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname\" &&\n+\ttest_ref_exists refs/heads/badname &&\n \tgit branch -d badname >output 2>error &&\n-\ttest_path_is_missing .git/refs/heads/badname &&\n+\ttest_ref_missing refs/heads/badname &&\n \ttest_i18ngrep \"Deleted branch badname (was refs/heads/broken\\.\\.\\.ref)\" output &&\n \ttest_must_be_empty error\n '\n@@ -245,8 +249,9 @@ test_expect_success 'update-ref -d can delete broken name through symref' '\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref\" &&\n \ttest-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname\" &&\n+\ttest_ref_exists refs/heads/broken...ref &&\n \tgit update-ref -d refs/heads/badname >output 2>error &&\n-\ttest_path_is_missing .git/refs/heads/broken...ref &&\n+\ttest_ref_missing refs/heads/broken...ref &&\n \ttest_must_be_empty output &&\n \ttest_must_be_empty error\n '\n@@ -254,8 +259,9 @@ test_expect_success 'update-ref -d can delete broken name through symref' '\n test_expect_success 'update-ref --no-deref -d can delete symref with broken name' '\n \tprintf \"ref: refs/heads/main\\n\" >.git/refs/heads/broken...symref &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n+\ttest_ref_exists refs/heads/broken...symref &&\n \tgit update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&\n-\ttest_path_is_missing .git/refs/heads/broken...symref &&\n+\ttest_ref_missing refs/heads/broken...symref &&\n \ttest_must_be_empty output &&\n \ttest_must_be_empty error\n '\n@@ -263,8 +269,9 @@ test_expect_success 'update-ref --no-deref -d can delete symref with broken name\n test_expect_success 'branch -d can delete symref with broken name' '\n \tprintf \"ref: refs/heads/main\\n\" >.git/refs/heads/broken...symref &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n+\ttest_ref_exists refs/heads/broken...symref &&\n \tgit branch -d broken...symref >output 2>error &&\n-\ttest_path_is_missing .git/refs/heads/broken...symref &&\n+\ttest_ref_missing refs/heads/broken...symref &&\n \ttest_i18ngrep \"Deleted branch broken...symref (was refs/heads/main)\" output &&\n \ttest_must_be_empty error\n '\n@@ -272,8 +279,9 @@ test_expect_success 'branch -d can delete symref with broken name' '\n test_expect_success 'update-ref --no-deref -d can delete dangling symref with broken name' '\n \tprintf \"ref: refs/heads/idonotexist\\n\" >.git/refs/heads/broken...symref &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n+\ttest_ref_exists refs/heads/broken...symref &&\n \tgit update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&\n-\ttest_path_is_missing .git/refs/heads/broken...symref &&\n+\ttest_ref_missing refs/heads/broken...symref &&\n \ttest_must_be_empty output &&\n \ttest_must_be_empty error\n '\n@@ -281,8 +289,9 @@ test_expect_success 'update-ref --no-deref -d can delete dangling symref with br\n test_expect_success 'branch -d can delete dangling symref with broken name' '\n \tprintf \"ref: refs/heads/idonotexist\\n\" >.git/refs/heads/broken...symref &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n+\ttest_ref_exists refs/heads/broken...symref &&\n \tgit branch -d broken...symref >output 2>error &&\n-\ttest_path_is_missing .git/refs/heads/broken...symref &&\n+\ttest_ref_missing refs/heads/broken...symref &&\n \ttest_i18ngrep \"Deleted branch broken...symref (was refs/heads/idonotexist)\" output &&\n \ttest_must_be_empty error\n '\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex 080e4f24a6e..bde4f1485b7 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -25,7 +25,7 @@ test_expect_success 'prepare a trivial repository' '\n \n test_expect_success 'git branch --help should not have created a bogus branch' '\n \ttest_might_fail git branch --man --help </dev/null >/dev/null 2>&1 &&\n-\ttest_path_is_missing .git/refs/heads/--help\n+\ttest_ref_missing refs/heads/--help\n '\n \n test_expect_success 'branch -h in broken repository' '\n@@ -40,7 +40,8 @@ test_expect_success 'branch -h in broken repository' '\n '\n \n test_expect_success 'git branch abc should create a branch' '\n-\tgit branch abc && test_path_is_file .git/refs/heads/abc\n+\tgit branch abc &&\n+\ttest_ref_exists refs/heads/abc\n '\n \n test_expect_success 'git branch abc should fail when abc exists' '\n@@ -61,11 +62,13 @@ test_expect_success 'git branch --force abc should succeed when abc exists' '\n '\n \n test_expect_success 'git branch a/b/c should create a branch' '\n-\tgit branch a/b/c && test_path_is_file .git/refs/heads/a/b/c\n+\tgit branch a/b/c &&\n+\ttest_ref_exists refs/heads/a/b/c\n '\n \n test_expect_success 'git branch mb main... should create a branch' '\n-\tgit branch mb main... && test_path_is_file .git/refs/heads/mb\n+\tgit branch mb main... &&\n+\ttest_ref_exists refs/heads/mb\n '\n \n test_expect_success 'git branch HEAD should fail' '\n@@ -78,14 +81,14 @@ EOF\n test_expect_success 'git branch --create-reflog d/e/f should create a branch and a log' '\n \tGIT_COMMITTER_DATE=\"2005-05-26 23:30\" \\\n \tgit -c core.logallrefupdates=false branch --create-reflog d/e/f &&\n-\ttest_path_is_file .git/refs/heads/d/e/f &&\n+\ttest_ref_exists refs/heads/d/e/f &&\n \ttest_path_is_file .git/logs/refs/heads/d/e/f &&\n \ttest_cmp expect .git/logs/refs/heads/d/e/f\n '\n \n test_expect_success 'git branch -d d/e/f should delete a branch and a log' '\n \tgit branch -d d/e/f &&\n-\ttest_path_is_missing .git/refs/heads/d/e/f &&\n+\ttest_ref_missing refs/heads/d/e/f &&\n \ttest_must_fail git reflog exists refs/heads/d/e/f\n '\n \n@@ -213,7 +216,7 @@ test_expect_success 'git branch -M should leave orphaned HEAD alone' '\n \t\ttest_commit initial &&\n \t\tgit checkout --orphan lonely &&\n \t\tgrep lonely .git/HEAD &&\n-\t\ttest_path_is_missing .git/refs/head/lonely &&\n+\t\ttest_ref_missing refs/head/lonely &&\n \t\tgit branch -M main mistress &&\n \t\tgrep lonely .git/HEAD\n \t)\n@@ -799,8 +802,8 @@ test_expect_success 'deleting a symref' '\n \tgit symbolic-ref refs/heads/symref refs/heads/target &&\n \techo \"Deleted branch symref (was refs/heads/target).\" >expect &&\n \tgit branch -d symref >actual &&\n-\ttest_path_is_file .git/refs/heads/target &&\n-\ttest_path_is_missing .git/refs/heads/symref &&\n+\ttest_ref_exists refs/heads/target &&\n+\ttest_ref_missing refs/heads/symref &&\n \ttest_cmp expect actual\n '\n \n@@ -809,16 +812,16 @@ test_expect_success 'deleting a dangling symref' '\n \ttest_path_is_file .git/refs/heads/dangling-symref &&\n \techo \"Deleted branch dangling-symref (was nowhere).\" >expect &&\n \tgit branch -d dangling-symref >actual &&\n-\ttest_path_is_missing .git/refs/heads/dangling-symref &&\n+\ttest_ref_missing refs/heads/dangling-symref &&\n \ttest_cmp expect actual\n '\n \n test_expect_success 'deleting a self-referential symref' '\n \tgit symbolic-ref refs/heads/self-reference refs/heads/self-reference &&\n-\ttest_path_is_file .git/refs/heads/self-reference &&\n+\ttest_ref_exists refs/heads/self-reference &&\n \techo \"Deleted branch self-reference (was refs/heads/self-reference).\" >expect &&\n \tgit branch -d self-reference >actual &&\n-\ttest_path_is_missing .git/refs/heads/self-reference &&\n+\ttest_ref_missing refs/heads/self-reference &&\n \ttest_cmp expect actual\n '\n \n@@ -826,8 +829,8 @@ test_expect_success 'renaming a symref is not allowed' '\n \tgit symbolic-ref refs/heads/topic refs/heads/main &&\n \ttest_must_fail git branch -m topic new-topic &&\n \tgit symbolic-ref refs/heads/topic &&\n-\ttest_path_is_file .git/refs/heads/main &&\n-\ttest_path_is_missing .git/refs/heads/new-topic\n+\ttest_ref_exists refs/heads/main &&\n+\ttest_ref_missing refs/heads/new-topic\n '\n \n test_expect_success SYMLINKS 'git branch -m u v should fail when the reflog for u is a symlink' '\n@@ -1142,7 +1145,7 @@ EOF\n test_expect_success 'git checkout -b g/h/i -l should create a branch and a log' '\n \tGIT_COMMITTER_DATE=\"2005-05-26 23:30\" \\\n \tgit checkout -b g/h/i -l main &&\n-\ttest_path_is_file .git/refs/heads/g/h/i &&\n+\ttest_ref_exists refs/heads/g/h/i &&\n \ttest_path_is_file .git/logs/refs/heads/g/h/i &&\n \ttest_cmp expect .git/logs/refs/heads/g/h/i\n '\ndiff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh\nindex 079b2f2536e..3681859f983 100755\n--- a/t/t5521-pull-options.sh\n+++ b/t/t5521-pull-options.sh\n@@ -143,7 +143,7 @@ test_expect_success 'git pull --dry-run' '\n \t\tcd clonedry &&\n \t\tgit pull --dry-run ../parent &&\n \t\ttest_path_is_missing .git/FETCH_HEAD &&\n-\t\ttest_path_is_missing .git/refs/heads/main &&\n+\t\ttest_ref_missing refs/heads/main &&\n \t\ttest_path_is_missing .git/index &&\n \t\ttest_path_is_missing file\n \t)\n@@ -157,7 +157,7 @@ test_expect_success 'git pull --all --dry-run' '\n \t\tgit remote add origin ../parent &&\n \t\tgit pull --all --dry-run &&\n \t\ttest_path_is_missing .git/FETCH_HEAD &&\n-\t\ttest_path_is_missing .git/refs/remotes/origin/main &&\n+\t\ttest_ref_missing refs/remotes/origin/main &&\n \t\ttest_path_is_missing .git/index &&\n \t\ttest_path_is_missing file\n \t)\ndiff --git a/t/t5605-clone-local.sh b/t/t5605-clone-local.sh\nindex 1d7b1abda1a..946c5751885 100755\n--- a/t/t5605-clone-local.sh\n+++ b/t/t5605-clone-local.sh\n@@ -69,7 +69,7 @@ test_expect_success 'local clone of repo with nonexistent ref in HEAD' '\n \tgit clone a d &&\n \t(cd d &&\n \tgit fetch &&\n-\ttest ! -e .git/refs/remotes/origin/HEAD)\n+\ttest_ref_missing refs/remotes/origin/HEAD)\n '\n \n test_expect_success 'bundle clone without .bundle suffix' '\n-- \n2.42.0\n\n"},{"id":"483387","messageId":"c79431c0bf117d756e1d584f4c9415888d9ff9eb.1697607222.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1697607222.git.ps@pks.im","subject":"[PATCH 04/11] t: convert tests to not write references via the filesystem","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-18T05:35:20Z","receivedAt":"2023-10-18T05:35:28Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Some of our tests manually create, update or delete references by\nwriting the respective new values into the filesystem directly. While\nthis works with the current files reference backend, this will break\nonce we have a second reference backend implementation in our codebase.\n\nRefactor these tests to instead use git-update-ref(1) or our `ref-store`\ntest tool. The latter is required in some cases where safety checks of\ngit-update-ref(1) would otherwise reject a reference update.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t1400-update-ref.sh         | 16 +++++++---------\n t/t1450-fsck.sh               | 28 +++++++++++++++-------------\n t/t3404-rebase-interactive.sh |  2 +-\n t/t5526-fetch-submodules.sh   |  2 +-\n 4 files changed, 24 insertions(+), 24 deletions(-)\n\ndiff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh\nindex 4d66cd7f4a1..cd24018ce99 100755\n--- a/t/t1400-update-ref.sh\n+++ b/t/t1400-update-ref.sh\n@@ -9,8 +9,6 @@ test_description='Test git update-ref and basic ref logging'\n Z=$ZERO_OID\n \n m=refs/heads/main\n-n_dir=refs/heads/gu\n-n=$n_dir/fixes\n outside=refs/foo\n bare=bare-repo\n \n@@ -62,10 +60,10 @@ test_expect_success \"delete $m without oldvalue verification\" '\n \ttest_must_fail git show-ref --verify -q $m\n '\n \n-test_expect_success \"fail to create $n\" '\n-\ttest_when_finished \"rm -f .git/$n_dir\" &&\n-\ttouch .git/$n_dir &&\n-\ttest_must_fail git update-ref $n $A\n+test_expect_success \"fail to create $n due to file/directory conflict\" '\n+\ttest_when_finished \"git update-ref -d refs/heads/gu\" &&\n+\tgit update-ref refs/heads/gu $A &&\n+\ttest_must_fail git update-ref refs/heads/gu/fixes $A\n '\n \n test_expect_success \"create $m (by HEAD)\" '\n@@ -222,7 +220,7 @@ test_expect_success 'delete symref without dereference when the referred ref is\n \n test_expect_success 'update-ref -d is not confused by self-reference' '\n \tgit symbolic-ref refs/heads/self refs/heads/self &&\n-\ttest_when_finished \"rm -f .git/refs/heads/self\" &&\n+\ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self\" &&\n \ttest_path_is_file .git/refs/heads/self &&\n \ttest_must_fail git update-ref -d refs/heads/self &&\n \ttest_path_is_file .git/refs/heads/self\n@@ -230,7 +228,7 @@ test_expect_success 'update-ref -d is not confused by self-reference' '\n \n test_expect_success 'update-ref --no-deref -d can delete self-reference' '\n \tgit symbolic-ref refs/heads/self refs/heads/self &&\n-\ttest_when_finished \"rm -f .git/refs/heads/self\" &&\n+\ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self\" &&\n \ttest_path_is_file .git/refs/heads/self &&\n \tgit update-ref --no-deref -d refs/heads/self &&\n \ttest_must_fail git show-ref --verify -q refs/heads/self\n@@ -434,7 +432,7 @@ test_expect_success 'Query \"main@{2005-05-28}\" (past end of history)' '\n \ttest_i18ngrep -F \"warning: log for ref $m unexpectedly ended on $ld\" e\n '\n \n-rm -f .git/$m .git/logs/$m expect\n+git update-ref -d $m\n \n test_expect_success 'creating initial files' '\n \ttest_when_finished rm -f M &&\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 10a539158c4..5cce24f1006 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -115,15 +115,16 @@ test_expect_success 'zlib corrupt loose object output ' '\n '\n \n test_expect_success 'branch pointing to non-commit' '\n-\tgit rev-parse HEAD^{tree} >.git/refs/heads/invalid &&\n+\ttree_oid=$(git rev-parse --verify HEAD^{tree}) &&\n+\ttest-tool ref-store main update-ref msg refs/heads/invalid $tree_oid $ZERO_OID REF_SKIP_OID_VERIFICATION &&\n \ttest_when_finished \"git update-ref -d refs/heads/invalid\" &&\n \ttest_must_fail git fsck 2>out &&\n \ttest_i18ngrep \"not a commit\" out\n '\n \n test_expect_success 'HEAD link pointing at a funny object' '\n-\ttest_when_finished \"mv .git/SAVED_HEAD .git/HEAD\" &&\n-\tmv .git/HEAD .git/SAVED_HEAD &&\n+\tsaved_head=$(git rev-parse --verify HEAD) &&\n+\ttest_when_finished \"git update-ref HEAD ${saved_head}\" &&\n \techo $ZERO_OID >.git/HEAD &&\n \t# avoid corrupt/broken HEAD from interfering with repo discovery\n \ttest_must_fail env GIT_DIR=.git git fsck 2>out &&\n@@ -131,8 +132,8 @@ test_expect_success 'HEAD link pointing at a funny object' '\n '\n \n test_expect_success 'HEAD link pointing at a funny place' '\n-\ttest_when_finished \"mv .git/SAVED_HEAD .git/HEAD\" &&\n-\tmv .git/HEAD .git/SAVED_HEAD &&\n+\tsaved_head=$(git rev-parse --verify HEAD) &&\n+\ttest_when_finished \"git update-ref --no-deref HEAD ${saved_head}\" &&\n \techo \"ref: refs/funny/place\" >.git/HEAD &&\n \t# avoid corrupt/broken HEAD from interfering with repo discovery\n \ttest_must_fail env GIT_DIR=.git git fsck 2>out &&\n@@ -140,10 +141,10 @@ test_expect_success 'HEAD link pointing at a funny place' '\n '\n \n test_expect_success 'HEAD link pointing at a funny object (from different wt)' '\n-\ttest_when_finished \"mv .git/SAVED_HEAD .git/HEAD\" &&\n+\tsaved_head=$(git rev-parse --verify HEAD) &&\n+\ttest_when_finished \"git update-ref HEAD $saved_head\" &&\n \ttest_when_finished \"rm -rf .git/worktrees wt\" &&\n \tgit worktree add wt &&\n-\tmv .git/HEAD .git/SAVED_HEAD &&\n \techo $ZERO_OID >.git/HEAD &&\n \t# avoid corrupt/broken HEAD from interfering with repo discovery\n \ttest_must_fail git -C wt fsck 2>out &&\n@@ -161,7 +162,8 @@ test_expect_success 'other worktree HEAD link pointing at a funny object' '\n test_expect_success 'other worktree HEAD link pointing at missing object' '\n \ttest_when_finished \"rm -rf .git/worktrees other\" &&\n \tgit worktree add other &&\n-\techo \"Contents missing from repo\" | git hash-object --stdin >.git/worktrees/other/HEAD &&\n+\tobject_id=$(echo \"Contents missing from repo\" | git hash-object --stdin) &&\n+\ttest-tool -C other ref-store main update-ref msg HEAD $object_id \"\" REF_NO_DEREF,REF_SKIP_OID_VERIFICATION &&\n \ttest_must_fail git fsck 2>out &&\n \ttest_i18ngrep \"worktrees/other/HEAD: invalid sha1 pointer\" out\n '\n@@ -391,7 +393,7 @@ test_expect_success 'tag pointing to nonexistent' '\n \n \ttag=$(git hash-object -t tag -w --stdin <invalid-tag) &&\n \ttest_when_finished \"remove_object $tag\" &&\n-\techo $tag >.git/refs/tags/invalid &&\n+\tgit update-ref refs/tags/invalid $tag &&\n \ttest_when_finished \"git update-ref -d refs/tags/invalid\" &&\n \ttest_must_fail git fsck --tags >out &&\n \ttest_i18ngrep \"broken link\" out\n@@ -411,7 +413,7 @@ test_expect_success 'tag pointing to something else than its type' '\n \n \ttag=$(git hash-object -t tag -w --stdin <wrong-tag) &&\n \ttest_when_finished \"remove_object $tag\" &&\n-\techo $tag >.git/refs/tags/wrong &&\n+\tgit update-ref refs/tags/wrong $tag &&\n \ttest_when_finished \"git update-ref -d refs/tags/wrong\" &&\n \ttest_must_fail git fsck --tags\n '\n@@ -428,7 +430,7 @@ test_expect_success 'tag with incorrect tag name & missing tagger' '\n \n \ttag=$(git hash-object --literally -t tag -w --stdin <wrong-tag) &&\n \ttest_when_finished \"remove_object $tag\" &&\n-\techo $tag >.git/refs/tags/wrong &&\n+\tgit update-ref refs/tags/wrong $tag &&\n \ttest_when_finished \"git update-ref -d refs/tags/wrong\" &&\n \tgit fsck --tags 2>out &&\n \n@@ -452,7 +454,7 @@ test_expect_success 'tag with bad tagger' '\n \n \ttag=$(git hash-object --literally -t tag -w --stdin <wrong-tag) &&\n \ttest_when_finished \"remove_object $tag\" &&\n-\techo $tag >.git/refs/tags/wrong &&\n+\tgit update-ref refs/tags/wrong $tag &&\n \ttest_when_finished \"git update-ref -d refs/tags/wrong\" &&\n \ttest_must_fail git fsck --tags 2>out &&\n \ttest_i18ngrep \"error in tag .*: invalid author/committer\" out\n@@ -471,7 +473,7 @@ test_expect_success 'tag with NUL in header' '\n \n \ttag=$(git hash-object --literally -t tag -w --stdin <tag-NUL-header) &&\n \ttest_when_finished \"remove_object $tag\" &&\n-\techo $tag >.git/refs/tags/wrong &&\n+\tgit update-ref refs/tags/wrong $tag &&\n \ttest_when_finished \"git update-ref -d refs/tags/wrong\" &&\n \ttest_must_fail git fsck --tags 2>out &&\n \ttest_i18ngrep \"error in tag $tag.*unterminated header: NUL at offset\" out\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex 8ea2bf13026..d2a7a91f170 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -2160,7 +2160,7 @@ test_expect_success '--update-refs: check failed ref update' '\n \t# recorded in the update-refs file. We will force-update the\n \t# \"second\" ref, but \"git branch -f\" will not work because of\n \t# the lock in the update-refs file.\n-\tgit rev-parse third >.git/refs/heads/second &&\n+\tgit update-ref refs/heads/second third &&\n \n \ttest_must_fail git rebase --continue 2>err &&\n \tgrep \"update_ref failed for ref '\\''refs/heads/second'\\''\" err &&\ndiff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh\nindex 26e933f93ae..7ab220fa313 100755\n--- a/t/t5526-fetch-submodules.sh\n+++ b/t/t5526-fetch-submodules.sh\n@@ -771,7 +771,7 @@ test_expect_success 'fetching submodule into a broken repository' '\n \tgit -C dst fetch --recurse-submodules &&\n \n \t# Break the receiving submodule\n-\trm -f dst/sub/.git/HEAD &&\n+\ttest-tool -C dst/sub ref-store main delete-refs REF_NO_DEREF msg HEAD &&\n \n \t# NOTE: without the fix the following tests will recurse forever!\n \t# They should terminate with an error.\n-- \n2.42.0\n\n"},{"id":"483388","messageId":"1ac120368c6cd995841c28bde7542e882ec7b04f.1697607222.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1697607222.git.ps@pks.im","subject":"[PATCH 05/11] t: convert tests to not access symrefs via the filesystem","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-18T05:35:24Z","receivedAt":"2023-10-18T05:35:36Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Some of our tests access symbolic references via the filesystem\ndirectly. While this works with the current files reference backend, it\nthis will break once we have a second reference backend in our codebase.\n\nRefactor these tests to instead use git-symbolic-ref(1) or our\n`ref-store` test tool. The latter is required in some cases where safety\nchecks of git-symbolic-ref(1) would otherwise reject writing a symbolic\nreference.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t1400-update-ref.sh              |  8 ++++----\n t/t1430-bad-ref-name.sh            | 12 ++++++------\n t/t1450-fsck.sh                    |  4 ++--\n t/t3200-branch.sh                  |  9 ++++++---\n t/t4013-diff-various.sh            |  2 +-\n t/t4202-log.sh                     |  2 +-\n t/t5605-clone-local.sh             |  2 +-\n t/t5702-protocol-v2.sh             | 24 ++++++++++++++++++------\n t/t9133-git-svn-nested-git-repo.sh |  2 +-\n 9 files changed, 40 insertions(+), 25 deletions(-)\n\ndiff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh\nindex cd24018ce99..5f505e2f353 100755\n--- a/t/t1400-update-ref.sh\n+++ b/t/t1400-update-ref.sh\n@@ -221,15 +221,15 @@ test_expect_success 'delete symref without dereference when the referred ref is\n test_expect_success 'update-ref -d is not confused by self-reference' '\n \tgit symbolic-ref refs/heads/self refs/heads/self &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self\" &&\n-\ttest_path_is_file .git/refs/heads/self &&\n+\tgit symbolic-ref --no-recurse refs/heads/self &&\n \ttest_must_fail git update-ref -d refs/heads/self &&\n-\ttest_path_is_file .git/refs/heads/self\n+\tgit symbolic-ref --no-recurse refs/heads/self\n '\n \n test_expect_success 'update-ref --no-deref -d can delete self-reference' '\n \tgit symbolic-ref refs/heads/self refs/heads/self &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self\" &&\n-\ttest_path_is_file .git/refs/heads/self &&\n+\tgit symbolic-ref --no-recurse refs/heads/self &&\n \tgit update-ref --no-deref -d refs/heads/self &&\n \ttest_must_fail git show-ref --verify -q refs/heads/self\n '\n@@ -239,7 +239,7 @@ test_expect_success 'update-ref --no-deref -d can delete reference to bad ref' '\n \ttest_when_finished \"rm -f .git/refs/heads/bad\" &&\n \tgit symbolic-ref refs/heads/ref-to-bad refs/heads/bad &&\n \ttest_when_finished \"git update-ref -d refs/heads/ref-to-bad\" &&\n-\ttest_path_is_file .git/refs/heads/ref-to-bad &&\n+\tgit symbolic-ref --no-recurse refs/heads/ref-to-bad &&\n \tgit update-ref --no-deref -d refs/heads/ref-to-bad &&\n \ttest_must_fail git show-ref --verify -q refs/heads/ref-to-bad\n '\ndiff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh\nindex 7b7d6953c62..5debb91f7b7 100755\n--- a/t/t1430-bad-ref-name.sh\n+++ b/t/t1430-bad-ref-name.sh\n@@ -164,9 +164,9 @@ test_expect_success 'rev-parse skips symref pointing to broken name' '\n test_expect_success 'for-each-ref emits warnings for broken names' '\n \ttest-tool ref-store main update-ref msg \"refs/heads/broken...ref\" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref\" &&\n-\tprintf \"ref: refs/heads/broken...ref\\n\" >.git/refs/heads/badname &&\n+\ttest-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname\" &&\n-\tprintf \"ref: refs/heads/main\\n\" >.git/refs/heads/broken...symref &&\n+\ttest-tool ref-store main create-symref refs/heads/broken...symref refs/heads/main &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n \tgit for-each-ref >output 2>error &&\n \t! grep -e \"broken\\.\\.\\.ref\" output &&\n@@ -257,7 +257,7 @@ test_expect_success 'update-ref -d can delete broken name through symref' '\n '\n \n test_expect_success 'update-ref --no-deref -d can delete symref with broken name' '\n-\tprintf \"ref: refs/heads/main\\n\" >.git/refs/heads/broken...symref &&\n+\ttest-tool ref-store main create-symref refs/heads/broken...symref refs/heads/main &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n \ttest_ref_exists refs/heads/broken...symref &&\n \tgit update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&\n@@ -267,7 +267,7 @@ test_expect_success 'update-ref --no-deref -d can delete symref with broken name\n '\n \n test_expect_success 'branch -d can delete symref with broken name' '\n-\tprintf \"ref: refs/heads/main\\n\" >.git/refs/heads/broken...symref &&\n+\ttest-tool ref-store main create-symref refs/heads/broken...symref refs/heads/main &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n \ttest_ref_exists refs/heads/broken...symref &&\n \tgit branch -d broken...symref >output 2>error &&\n@@ -277,7 +277,7 @@ test_expect_success 'branch -d can delete symref with broken name' '\n '\n \n test_expect_success 'update-ref --no-deref -d can delete dangling symref with broken name' '\n-\tprintf \"ref: refs/heads/idonotexist\\n\" >.git/refs/heads/broken...symref &&\n+\ttest-tool ref-store main create-symref refs/heads/broken...symref refs/heads/idonotexist &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n \ttest_ref_exists refs/heads/broken...symref &&\n \tgit update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&\n@@ -287,7 +287,7 @@ test_expect_success 'update-ref --no-deref -d can delete dangling symref with br\n '\n \n test_expect_success 'branch -d can delete dangling symref with broken name' '\n-\tprintf \"ref: refs/heads/idonotexist\\n\" >.git/refs/heads/broken...symref &&\n+\ttest-tool ref-store main create-symref refs/heads/broken...symref refs/heads/idonotexist &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n \ttest_ref_exists refs/heads/broken...symref &&\n \tgit branch -d broken...symref >output 2>error &&\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 5cce24f1006..804a5594ddd 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -134,7 +134,7 @@ test_expect_success 'HEAD link pointing at a funny object' '\n test_expect_success 'HEAD link pointing at a funny place' '\n \tsaved_head=$(git rev-parse --verify HEAD) &&\n \ttest_when_finished \"git update-ref --no-deref HEAD ${saved_head}\" &&\n-\techo \"ref: refs/funny/place\" >.git/HEAD &&\n+\ttest-tool ref-store main create-symref HEAD refs/funny/place &&\n \t# avoid corrupt/broken HEAD from interfering with repo discovery\n \ttest_must_fail env GIT_DIR=.git git fsck 2>out &&\n \ttest_i18ngrep \"HEAD points to something strange\" out\n@@ -171,7 +171,7 @@ test_expect_success 'other worktree HEAD link pointing at missing object' '\n test_expect_success 'other worktree HEAD link pointing at a funny place' '\n \ttest_when_finished \"rm -rf .git/worktrees other\" &&\n \tgit worktree add other &&\n-\techo \"ref: refs/funny/place\" >.git/worktrees/other/HEAD &&\n+\tgit -C other symbolic-ref HEAD refs/funny/place &&\n \ttest_must_fail git fsck 2>out &&\n \ttest_i18ngrep \"worktrees/other/HEAD points to something strange\" out\n '\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex bde4f1485b7..874520e3f10 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -215,10 +215,13 @@ test_expect_success 'git branch -M should leave orphaned HEAD alone' '\n \t\tcd orphan &&\n \t\ttest_commit initial &&\n \t\tgit checkout --orphan lonely &&\n-\t\tgrep lonely .git/HEAD &&\n+\t\tgit symbolic-ref HEAD >expect &&\n+\t\techo refs/heads/lonely >actual &&\n+\t\ttest_cmp expect actual &&\n \t\ttest_ref_missing refs/head/lonely &&\n \t\tgit branch -M main mistress &&\n-\t\tgrep lonely .git/HEAD\n+\t\tgit symbolic-ref HEAD >expect &&\n+\t\ttest_cmp expect actual\n \t)\n '\n \n@@ -809,7 +812,7 @@ test_expect_success 'deleting a symref' '\n \n test_expect_success 'deleting a dangling symref' '\n \tgit symbolic-ref refs/heads/dangling-symref nowhere &&\n-\ttest_path_is_file .git/refs/heads/dangling-symref &&\n+\tgit symbolic-ref --no-recurse refs/heads/dangling-symref &&\n \techo \"Deleted branch dangling-symref (was nowhere).\" >expect &&\n \tgit branch -d dangling-symref >actual &&\n \ttest_ref_missing refs/heads/dangling-symref &&\ndiff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh\nindex 5de1d190759..5abbea36b39 100755\n--- a/t/t4013-diff-various.sh\n+++ b/t/t4013-diff-various.sh\n@@ -514,7 +514,7 @@ test_expect_success 'log -S requires an argument' '\n '\n \n test_expect_success 'diff --cached on unborn branch' '\n-\techo ref: refs/heads/unborn >.git/HEAD &&\n+\tgit symbolic-ref HEAD refs/heads/unborn &&\n \tgit diff --cached >result &&\n \tprocess_diffs result >actual &&\n \tprocess_diffs \"$TEST_DIRECTORY/t4013/diff.diff_--cached\" >expected &&\ndiff --git a/t/t4202-log.sh b/t/t4202-log.sh\nindex af4a123cd22..57b298a4e22 100755\n--- a/t/t4202-log.sh\n+++ b/t/t4202-log.sh\n@@ -2265,7 +2265,7 @@ test_expect_success REFFILES 'log diagnoses bogus HEAD hash' '\n \n test_expect_success REFFILES 'log diagnoses bogus HEAD symref' '\n \tgit init empty &&\n-\techo \"ref: refs/heads/invalid.lock\" > empty/.git/HEAD &&\n+\ttest-tool -C empty ref-store main create-symref HEAD refs/heads/invalid.lock &&\n \ttest_must_fail git -C empty log 2>stderr &&\n \ttest_i18ngrep broken stderr &&\n \ttest_must_fail git -C empty log --default totally-bogus 2>stderr &&\ndiff --git a/t/t5605-clone-local.sh b/t/t5605-clone-local.sh\nindex 946c5751885..bedd29d0550 100755\n--- a/t/t5605-clone-local.sh\n+++ b/t/t5605-clone-local.sh\n@@ -65,7 +65,7 @@ test_expect_success 'Even without -l, local will make a hardlink' '\n '\n \n test_expect_success 'local clone of repo with nonexistent ref in HEAD' '\n-\techo \"ref: refs/heads/nonexistent\" > a.git/HEAD &&\n+\tgit -C a.git symbolic-ref HEAD refs/heads/nonexistent &&\n \tgit clone a d &&\n \t(cd d &&\n \tgit fetch &&\ndiff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh\nindex 6af5c2062fd..dcc4cd95fe7 100755\n--- a/t/t5702-protocol-v2.sh\n+++ b/t/t5702-protocol-v2.sh\n@@ -221,7 +221,9 @@ test_expect_success 'clone of empty repo propagates name of default branch' '\n \tGIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \\\n \tgit -c init.defaultBranch=main -c protocol.version=2 \\\n \t\tclone \"file://$(pwd)/file_empty_parent\" file_empty_child &&\n-\tgrep \"refs/heads/mydefaultbranch\" file_empty_child/.git/HEAD\n+\techo refs/heads/mydefaultbranch >expect &&\n+\tgit -C file_empty_child symbolic-ref HEAD >actual &&\n+\ttest_cmp expect actual\n '\n \n test_expect_success '...but not if explicitly forbidden by config' '\n@@ -234,7 +236,9 @@ test_expect_success '...but not if explicitly forbidden by config' '\n \tGIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \\\n \tgit -c init.defaultBranch=main -c protocol.version=2 \\\n \t\tclone \"file://$(pwd)/file_empty_parent\" file_empty_child &&\n-\t! grep \"refs/heads/mydefaultbranch\" file_empty_child/.git/HEAD\n+\techo refs/heads/main >expect &&\n+\tgit -C file_empty_child symbolic-ref HEAD >actual &&\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'bare clone propagates empty default branch' '\n@@ -247,7 +251,9 @@ test_expect_success 'bare clone propagates empty default branch' '\n \tgit -c init.defaultBranch=main -c protocol.version=2 \\\n \t\tclone --bare \\\n \t\t\"file://$(pwd)/file_empty_parent\" file_empty_child.git &&\n-\tgrep \"refs/heads/mydefaultbranch\" file_empty_child.git/HEAD\n+\techo \"refs/heads/mydefaultbranch\" >expect &&\n+\tgit -C file_empty_child.git symbolic-ref HEAD >actual &&\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'clone propagates unborn HEAD from non-empty repo' '\n@@ -265,7 +271,9 @@ test_expect_success 'clone propagates unborn HEAD from non-empty repo' '\n \tgit -c init.defaultBranch=main -c protocol.version=2 \\\n \t\tclone \"file://$(pwd)/file_unborn_parent\" \\\n \t\tfile_unborn_child 2>stderr &&\n-\tgrep \"refs/heads/mydefaultbranch\" file_unborn_child/.git/HEAD &&\n+\techo \"refs/heads/mydefaultbranch\" >expect &&\n+\tgit -C file_unborn_child symbolic-ref HEAD >actual &&\n+\ttest_cmp expect actual &&\n \tgrep \"warning: remote HEAD refers to nonexistent ref\" stderr\n '\n \n@@ -295,7 +303,9 @@ test_expect_success 'bare clone propagates unborn HEAD from non-empty repo' '\n \tgit -c init.defaultBranch=main -c protocol.version=2 \\\n \t\tclone --bare \"file://$(pwd)/file_unborn_parent\" \\\n \t\tfile_unborn_child.git 2>stderr &&\n-\tgrep \"refs/heads/mydefaultbranch\" file_unborn_child.git/HEAD &&\n+\techo \"refs/heads/mydefaultbranch\" >expect &&\n+\tgit -C file_unborn_child.git symbolic-ref HEAD >actual &&\n+\ttest_cmp expect actual &&\n \t! grep \"warning:\" stderr\n '\n \n@@ -315,7 +325,9 @@ test_expect_success 'defaulted HEAD uses remote branch if available' '\n \tgit -c init.defaultBranch=branchwithstuff -c protocol.version=2 \\\n \t\tclone \"file://$(pwd)/file_unborn_parent\" \\\n \t\tfile_unborn_child 2>stderr &&\n-\tgrep \"refs/heads/branchwithstuff\" file_unborn_child/.git/HEAD &&\n+\techo \"refs/heads/branchwithstuff\" >expect &&\n+\tgit -C file_unborn_child symbolic-ref HEAD >actual &&\n+\ttest_cmp expect actual &&\n \ttest_path_is_file file_unborn_child/stuff.t &&\n \t! grep \"warning:\" stderr\n '\ndiff --git a/t/t9133-git-svn-nested-git-repo.sh b/t/t9133-git-svn-nested-git-repo.sh\nindex d8d536269cf..8ca24670acb 100755\n--- a/t/t9133-git-svn-nested-git-repo.sh\n+++ b/t/t9133-git-svn-nested-git-repo.sh\n@@ -11,7 +11,7 @@ test_expect_success 'setup repo with a git repo inside it' '\n \t(\n \t\tcd s &&\n \t\tgit init &&\n-\t\ttest -f .git/HEAD &&\n+\t\tgit symbolic-ref HEAD &&\n \t\t> .git/a &&\n \t\techo a > a &&\n \t\tsvn_cmd add .git a &&\n-- \n2.42.0\n\n"},{"id":"483389","messageId":"eaac658bbfd8259ed9a3cce6ca3c8486d6682e8f.1697607222.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1697607222.git.ps@pks.im","subject":"[PATCH 06/11] t: convert tests to not access reflog via the filesystem","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-18T05:35:29Z","receivedAt":"2023-10-18T05:35:40Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Some of our tests reach directly into the filesystem in order to both\nread or modify the reflog, which will break once we have a second\nreference backend in our codebase that stores reflogs differently.\n\nRefactor these tests to either use git-reflog(1) or the ref-store test\nhelper. Note that the refactoring to use git-reflog(1) also requires us\nto adapt our expectations in some cases where we previously verified the\nexact on-disk log entries. This seems like an acceptable tradeoff though\nto ensure that different backends have the same user-visible behaviour\nas any user would typically use git-reflog(1) anyway to access the logs.\nAny backend-specific verification of the written on-disk format should\nbe implemented in a separate, backend-specific test.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t1400-update-ref.sh | 17 +++++++++++------\n t/t3200-branch.sh     | 24 ++++++++++++------------\n 2 files changed, 23 insertions(+), 18 deletions(-)\n\ndiff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh\nindex 5f505e2f353..b1d2c014132 100755\n--- a/t/t1400-update-ref.sh\n+++ b/t/t1400-update-ref.sh\n@@ -90,7 +90,8 @@ test_expect_success \"deleting current branch adds message to HEAD's log\" '\n \tgit symbolic-ref HEAD $m &&\n \tgit update-ref -m delete-$m -d $m &&\n \ttest_must_fail git show-ref --verify -q $m &&\n-\tgrep \"delete-$m$\" .git/logs/HEAD\n+\ttest-tool ref-store main for-each-reflog-ent HEAD >actual &&\n+\tgrep \"delete-$m$\" actual\n '\n \n test_expect_success \"deleting by HEAD adds message to HEAD's log\" '\n@@ -99,7 +100,8 @@ test_expect_success \"deleting by HEAD adds message to HEAD's log\" '\n \tgit symbolic-ref HEAD $m &&\n \tgit update-ref -m delete-by-head -d HEAD &&\n \ttest_must_fail git show-ref --verify -q $m &&\n-\tgrep \"delete-by-head$\" .git/logs/HEAD\n+\ttest-tool ref-store main for-each-reflog-ent HEAD >actual &&\n+\tgrep \"delete-by-head$\" actual\n '\n \n test_expect_success 'update-ref does not create reflogs by default' '\n@@ -130,7 +132,7 @@ test_expect_success 'creates no reflog in bare repository' '\n \n test_expect_success 'core.logAllRefUpdates=true creates reflog in bare repository' '\n \ttest_when_finished \"git -C $bare config --unset core.logAllRefUpdates && \\\n-\t\trm $bare/logs/$m\" &&\n+\t\ttest-tool ref-store main delete-reflog $m\" &&\n \tgit -C $bare config core.logAllRefUpdates true &&\n \tgit -C $bare update-ref $m $bareB &&\n \tgit -C $bare rev-parse $bareB >expect &&\n@@ -263,7 +265,10 @@ test_expect_success \"(not) changed .git/$m\" '\n \t! test $B = $(git show-ref -s --verify $m)\n '\n \n-rm -f .git/logs/refs/heads/main\n+test_expect_success \"clean up reflog\" '\n+\ttest-tool ref-store main delete-reflog $m\n+'\n+\n test_expect_success \"create $m (logged by touch)\" '\n \ttest_config core.logAllRefUpdates false &&\n \tGIT_COMMITTER_DATE=\"2005-05-26 23:30\" \\\n@@ -316,7 +321,7 @@ $A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150260 +0000\tSwitch\n $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000\n EOF\n test_expect_success \"verifying $m's log (logged by touch)\" '\n-\ttest_when_finished \"git update-ref -d $m && rm -rf .git/logs actual expect\" &&\n+\ttest_when_finished \"git update-ref -d $m && git reflog expire --expire=all --all && rm -rf actual expect\" &&\n \ttest-tool ref-store main for-each-reflog-ent $m >actual &&\n \ttest_cmp actual expect\n '\n@@ -346,7 +351,7 @@ $A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 +0000\tSwitch\n $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 +0000\n EOF\n test_expect_success \"verifying $m's log (logged by config)\" '\n-\ttest_when_finished \"git update-ref -d $m && rm -rf .git/logs actual expect\" &&\n+\ttest_when_finished \"git update-ref -d $m && git reflog expire --expire=all --all && rm -rf actual expect\" &&\n \ttest-tool ref-store main for-each-reflog-ent $m >actual &&\n \ttest_cmp actual expect\n '\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex 874520e3f10..933aa9eebbd 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -76,14 +76,14 @@ test_expect_success 'git branch HEAD should fail' '\n '\n \n cat >expect <<EOF\n-$ZERO_OID $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000\tbranch: Created from main\n+$HEAD refs/heads/d/e/f@{0}: branch: Created from main\n EOF\n test_expect_success 'git branch --create-reflog d/e/f should create a branch and a log' '\n \tGIT_COMMITTER_DATE=\"2005-05-26 23:30\" \\\n \tgit -c core.logallrefupdates=false branch --create-reflog d/e/f &&\n \ttest_ref_exists refs/heads/d/e/f &&\n-\ttest_path_is_file .git/logs/refs/heads/d/e/f &&\n-\ttest_cmp expect .git/logs/refs/heads/d/e/f\n+\tgit reflog show --no-abbrev-commit refs/heads/d/e/f >actual &&\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'git branch -d d/e/f should delete a branch and a log' '\n@@ -203,10 +203,9 @@ test_expect_success 'git branch -M baz bam should succeed when baz is checked ou\n \ttest $(git rev-parse --abbrev-ref HEAD) = bam\n '\n \n-test_expect_success 'git branch -M baz bam should add entries to .git/logs/HEAD' '\n-\tmsg=\"Branch: renamed refs/heads/baz to refs/heads/bam\" &&\n-\tgrep \" $ZERO_OID.*$msg$\" .git/logs/HEAD &&\n-\tgrep \"^$ZERO_OID.*$msg$\" .git/logs/HEAD\n+test_expect_success 'git branch -M baz bam should add entries to HEAD reflog' '\n+\tgit reflog show HEAD >actual &&\n+\tgrep \"HEAD@{0}: Branch: renamed refs/heads/baz to refs/heads/bam\" actual\n '\n \n test_expect_success 'git branch -M should leave orphaned HEAD alone' '\n@@ -228,7 +227,7 @@ test_expect_success 'git branch -M should leave orphaned HEAD alone' '\n test_expect_success 'resulting reflog can be shown by log -g' '\n \toid=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\tHEAD@{0} $oid $msg\n+\tHEAD@{0} $oid Branch: renamed refs/heads/baz to refs/heads/bam\n \tHEAD@{2} $oid checkout: moving from foo to baz\n \tEOF\n \tgit log -g --format=\"%gd %H %gs\" -2 HEAD >actual &&\n@@ -702,7 +701,8 @@ test_expect_success 'git branch -C c1 c2 should succeed when c1 is checked out'\n \n test_expect_success 'git branch -C c1 c2 should never touch HEAD' '\n \tmsg=\"Branch: copied refs/heads/c1 to refs/heads/c2\" &&\n-\t! grep \"$msg$\" .git/logs/HEAD\n+\tgit reflog HEAD >actual &&\n+\t! grep \"$msg$\" actual\n '\n \n test_expect_success 'git branch -C main should work when main is checked out' '\n@@ -1143,14 +1143,14 @@ test_expect_success '--set-upstream-to notices an error to set branch as own ups\n \n # Keep this test last, as it changes the current branch\n cat >expect <<EOF\n-$ZERO_OID $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000\tbranch: Created from main\n+$HEAD refs/heads/g/h/i@{0}: branch: Created from main\n EOF\n test_expect_success 'git checkout -b g/h/i -l should create a branch and a log' '\n \tGIT_COMMITTER_DATE=\"2005-05-26 23:30\" \\\n \tgit checkout -b g/h/i -l main &&\n \ttest_ref_exists refs/heads/g/h/i &&\n-\ttest_path_is_file .git/logs/refs/heads/g/h/i &&\n-\ttest_cmp expect .git/logs/refs/heads/g/h/i\n+\tgit reflog show --no-abbrev-commit refs/heads/g/h/i >actual &&\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'checkout -b makes reflog by default' '\n-- \n2.42.0\n\n"},{"id":"483390","messageId":"3dc65a800746d54e41460fb019abddb7d4524586.1697607222.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1697607222.git.ps@pks.im","subject":"[PATCH 07/11] t1450: convert tests to remove worktrees via git-worktree(1)","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-18T05:35:33Z","receivedAt":"2023-10-18T05:35:45Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Some of our tests in t1450 create worktrees and then corrupt them.\nAs it is impossible to delete such worktrees via a normal call to `git\nworktree remove`, we instead opt to remove them manually by calling\nrm(1) instead.\n\nThis is ultimately unnecessary though as we can use the `-f` switch to\nremove the worktree. Let's convert the tests to do so such that we don't\nhave to reach into internal implementation details of worktrees.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t1450-fsck.sh | 8 ++++----\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 804a5594ddd..d356605d132 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -143,7 +143,7 @@ test_expect_success 'HEAD link pointing at a funny place' '\n test_expect_success 'HEAD link pointing at a funny object (from different wt)' '\n \tsaved_head=$(git rev-parse --verify HEAD) &&\n \ttest_when_finished \"git update-ref HEAD $saved_head\" &&\n-\ttest_when_finished \"rm -rf .git/worktrees wt\" &&\n+\ttest_when_finished \"git worktree remove -f wt\" &&\n \tgit worktree add wt &&\n \techo $ZERO_OID >.git/HEAD &&\n \t# avoid corrupt/broken HEAD from interfering with repo discovery\n@@ -152,7 +152,7 @@ test_expect_success 'HEAD link pointing at a funny object (from different wt)' '\n '\n \n test_expect_success 'other worktree HEAD link pointing at a funny object' '\n-\ttest_when_finished \"rm -rf .git/worktrees other\" &&\n+\ttest_when_finished \"git worktree remove -f other\" &&\n \tgit worktree add other &&\n \techo $ZERO_OID >.git/worktrees/other/HEAD &&\n \ttest_must_fail git fsck 2>out &&\n@@ -160,7 +160,7 @@ test_expect_success 'other worktree HEAD link pointing at a funny object' '\n '\n \n test_expect_success 'other worktree HEAD link pointing at missing object' '\n-\ttest_when_finished \"rm -rf .git/worktrees other\" &&\n+\ttest_when_finished \"git worktree remove -f other\" &&\n \tgit worktree add other &&\n \tobject_id=$(echo \"Contents missing from repo\" | git hash-object --stdin) &&\n \ttest-tool -C other ref-store main update-ref msg HEAD $object_id \"\" REF_NO_DEREF,REF_SKIP_OID_VERIFICATION &&\n@@ -169,7 +169,7 @@ test_expect_success 'other worktree HEAD link pointing at missing object' '\n '\n \n test_expect_success 'other worktree HEAD link pointing at a funny place' '\n-\ttest_when_finished \"rm -rf .git/worktrees other\" &&\n+\ttest_when_finished \"git worktree remove -f other\" &&\n \tgit worktree add other &&\n \tgit -C other symbolic-ref HEAD refs/funny/place &&\n \ttest_must_fail git fsck 2>out &&\n-- \n2.42.0\n\n"},{"id":"483391","messageId":"c4d09e3e5dbd11221cc4d229b815434d441cdb4d.1697607222.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1697607222.git.ps@pks.im","subject":"[PATCH 08/11] t4207: delete replace references via git-update-ref(1)","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-18T05:35:37Z","receivedAt":"2023-10-18T05:35:52Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"In t4207 we set up a set of replace objects via git-replace(1). Because\nthese references should not be impacting subsequent tests we also set up\nsome cleanup logic that deletes the replacement references via a call to\n`rm -rf`. This reaches into the internal implementation details of the\nreference backend and will thus break when we grow an alternative refdb\nimplementation.\n\nRefactor the tests to delete the replacement refs via Git commands so\nthat we become independent of the actual refdb that's in use. As we\ndon't have a nice way to delete all replacements or all references in a\ncertain namespace, we opt for a combination of git-for-each-ref(1) and\ngit-update-ref(1)'s `--stdin` mode.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t4207-log-decoration-colors.sh | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh\nindex 21986a866df..d138e513a04 100755\n--- a/t/t4207-log-decoration-colors.sh\n+++ b/t/t4207-log-decoration-colors.sh\n@@ -71,7 +71,7 @@ ${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_commit})${c_reset} A\n '\n \n test_expect_success 'test coloring with replace-objects' '\n-\ttest_when_finished rm -rf .git/refs/replace* &&\n+\ttest_when_finished \"git for-each-ref refs/replace*/** --format=${SQ}delete %(refname)${SQ} | git update-ref --stdin\" &&\n \ttest_commit C &&\n \ttest_commit D &&\n \n@@ -99,7 +99,7 @@ EOF\n '\n \n test_expect_success 'test coloring with grafted commit' '\n-\ttest_when_finished rm -rf .git/refs/replace* &&\n+\ttest_when_finished \"git for-each-ref refs/replace*/** --format=${SQ}delete %(refname)${SQ} | git update-ref --stdin\" &&\n \n \tgit replace --graft HEAD HEAD~2 &&\n \n-- \n2.42.0\n\n"},{"id":"483392","messageId":"153b5b199c8d1d5893c3ccec777609ab561cfa3b.1697607222.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1697607222.git.ps@pks.im","subject":"[PATCH 09/11] t7300: assert exact states of repo","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-18T05:35:42Z","receivedAt":"2023-10-18T05:36:01Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Some of the tests in t7300 verify that git-clean(1) doesn't touch\nrepositories that are embedded into the main repository. This is done by\nasserting a small set of substructures that are assumed to always exist,\nlike the \"refs/\", \"objects/\" or \"HEAD\". This has the downside that we\nneed to assume a specific repository structure that may be subject to\nchange when new backends for the refdb land. At the same time, we don't\nthoroughly assert that git-clean(1) really didn't end up cleaning any\nfiles in the repository either.\n\nConvert the tests to instead assert that all files continue to exist\nafter git-clean(1) by comparing a file listing via find(1) before and\nafter executing clean. This makes our actual assertions stricter while\nhaving to care less about the repository's actual on-disk format.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t7300-clean.sh | 23 ++++++++++++++---------\n 1 file changed, 14 insertions(+), 9 deletions(-)\n\ndiff --git a/t/t7300-clean.sh b/t/t7300-clean.sh\nindex 0ef7b784573..d7d9202f37f 100755\n--- a/t/t7300-clean.sh\n+++ b/t/t7300-clean.sh\n@@ -517,8 +517,12 @@ test_expect_success 'nested (empty) git should be kept' '\n \tgit init empty_repo &&\n \tmkdir to_clean &&\n \t>to_clean/should_clean.this &&\n+\t# Note that we put the expect file in the .git directory so that it\n+\t# does not get cleaned.\n+\tfind empty_repo | sort >.git/expect &&\n \tgit clean -f -d &&\n-\ttest_path_is_file empty_repo/.git/HEAD &&\n+\tfind empty_repo | sort >actual &&\n+\ttest_cmp .git/expect actual &&\n \ttest_path_is_missing to_clean\n '\n \n@@ -559,10 +563,10 @@ test_expect_success 'giving path in nested git work tree will NOT remove it' '\n \t\tmkdir -p bar/baz &&\n \t\ttest_commit msg bar/baz/hello.world\n \t) &&\n+\tfind repo | sort >expect &&\n \tgit clean -f -d repo/bar/baz &&\n-\ttest_path_is_file repo/.git/HEAD &&\n-\ttest_path_is_dir repo/bar/ &&\n-\ttest_path_is_file repo/bar/baz/hello.world\n+\tfind repo | sort >actual &&\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'giving path to nested .git will not remove it' '\n@@ -573,10 +577,10 @@ test_expect_success 'giving path to nested .git will not remove it' '\n \t\tgit init &&\n \t\ttest_commit msg hello.world\n \t) &&\n+\tfind repo | sort >expect &&\n \tgit clean -f -d repo/.git &&\n-\ttest_path_is_file repo/.git/HEAD &&\n-\ttest_path_is_dir repo/.git/refs &&\n-\ttest_path_is_dir repo/.git/objects &&\n+\tfind repo | sort >actual &&\n+\ttest_cmp expect actual &&\n \ttest_path_is_dir untracked/\n '\n \n@@ -588,9 +592,10 @@ test_expect_success 'giving path to nested .git/ will NOT remove contents' '\n \t\tgit init &&\n \t\ttest_commit msg hello.world\n \t) &&\n+\tfind repo | sort >expect &&\n \tgit clean -f -d repo/.git/ &&\n-\ttest_path_is_dir repo/.git &&\n-\ttest_path_is_file repo/.git/HEAD &&\n+\tfind repo | sort >actual &&\n+\ttest_cmp expect actual &&\n \ttest_path_is_dir untracked/\n '\n \n-- \n2.42.0\n\n"},{"id":"483393","messageId":"b99d98b00a3b4ead2e88aaed23cad778087c69b8.1697607222.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1697607222.git.ps@pks.im","subject":"[PATCH 10/11] t7900: assert the absence of refs via git-for-each-ref(1)","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-18T05:35:46Z","receivedAt":"2023-10-18T05:36:07Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"We're asserting that a prefetch of remotes via git-maintenance(1)\ndoesn't write any references in refs/remotes by validating that the\ndirectory \".git/refs/remotes\" is missing. This is quite roundabout: we\ndon't care about the directory existing, we care about the references\nnot existing, and the way these are stored is on the behest of the\nreference database.\n\nConvert the test to instead check via git-for-each-ref(1) whether any\nremote reference exist.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t7900-maintenance.sh | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh\nindex e56f5980dc4..cefecee732f 100755\n--- a/t/t7900-maintenance.sh\n+++ b/t/t7900-maintenance.sh\n@@ -157,7 +157,8 @@ test_expect_success 'prefetch multiple remotes' '\n \tfetchargs=\"--prefetch --prune --no-tags --no-write-fetch-head --recurse-submodules=no --quiet\" &&\n \ttest_subcommand git fetch remote1 $fetchargs <run-prefetch.txt &&\n \ttest_subcommand git fetch remote2 $fetchargs <run-prefetch.txt &&\n-\ttest_path_is_missing .git/refs/remotes &&\n+\tgit for-each-ref refs/remotes >actual &&\n+\ttest_must_be_empty actual &&\n \tgit log prefetch/remotes/remote1/one &&\n \tgit log prefetch/remotes/remote2/two &&\n \tgit fetch --all &&\n-- \n2.42.0\n\n"},{"id":"483394","messageId":"67cb282a414bf5b17934c20820289a416a7b49e7.1697607222.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1697607222.git.ps@pks.im","subject":"[PATCH 11/11] t: mark several tests that assume the files backend with REFFILES","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-18T05:35:53Z","receivedAt":"2023-10-18T05:36:22Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Add the REFFILES prerequisite to several tests that assume we're using\nthe files backend. There are various reasons why we cannot easily\nconvert those tests to be backend-independent, where the most common\none is that we have no way to write corrupt references into the refdb\nvia our tooling. We may at a later point in time grow the tooling to\nmake this possible, but for now we just mark these tests as requiring\nthe files backend.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t1400-update-ref.sh            |  8 ++++----\n t/t1450-fsck.sh                  |  6 +++---\n t/t2011-checkout-invalid-head.sh | 16 ++++++++--------\n t/t3200-branch.sh                |  8 ++++----\n t/t3400-rebase.sh                |  2 +-\n t/t5605-clone-local.sh           |  2 +-\n 6 files changed, 21 insertions(+), 21 deletions(-)\n\ndiff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh\nindex b1d2c014132..feeba6ed7cd 100755\n--- a/t/t1400-update-ref.sh\n+++ b/t/t1400-update-ref.sh\n@@ -236,7 +236,7 @@ test_expect_success 'update-ref --no-deref -d can delete self-reference' '\n \ttest_must_fail git show-ref --verify -q refs/heads/self\n '\n \n-test_expect_success 'update-ref --no-deref -d can delete reference to bad ref' '\n+test_expect_success REFFILES 'update-ref --no-deref -d can delete reference to bad ref' '\n \t>.git/refs/heads/bad &&\n \ttest_when_finished \"rm -f .git/refs/heads/bad\" &&\n \tgit symbolic-ref refs/heads/ref-to-bad refs/heads/bad &&\n@@ -288,7 +288,7 @@ test_expect_success \"set $m (logged by touch)\" '\n \ttest $A = $(git show-ref -s --verify $m)\n '\n \n-test_expect_success 'empty directory removal' '\n+test_expect_success REFFILES 'empty directory removal' '\n \tgit branch d1/d2/r1 HEAD &&\n \tgit branch d1/r2 HEAD &&\n \ttest_path_is_file .git/refs/heads/d1/d2/r1 &&\n@@ -300,7 +300,7 @@ test_expect_success 'empty directory removal' '\n \ttest_path_is_file .git/logs/refs/heads/d1/r2\n '\n \n-test_expect_success 'symref empty directory removal' '\n+test_expect_success REFFILES 'symref empty directory removal' '\n \tgit branch e1/e2/r1 HEAD &&\n \tgit branch e1/r2 HEAD &&\n \tgit checkout e1/e2/r1 &&\n@@ -1638,7 +1638,7 @@ test_expect_success PIPE 'transaction flushes status updates' '\n \ttest_cmp expected actual\n '\n \n-test_expect_success 'directory not created deleting packed ref' '\n+test_expect_success REFFILES 'directory not created deleting packed ref' '\n \tgit branch d1/d2/r1 HEAD &&\n \tgit pack-refs --all &&\n \ttest_path_is_missing .git/refs/heads/d1/d2 &&\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex d356605d132..ca5a2719a19 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -122,7 +122,7 @@ test_expect_success 'branch pointing to non-commit' '\n \ttest_i18ngrep \"not a commit\" out\n '\n \n-test_expect_success 'HEAD link pointing at a funny object' '\n+test_expect_success REFFILES 'HEAD link pointing at a funny object' '\n \tsaved_head=$(git rev-parse --verify HEAD) &&\n \ttest_when_finished \"git update-ref HEAD ${saved_head}\" &&\n \techo $ZERO_OID >.git/HEAD &&\n@@ -140,7 +140,7 @@ test_expect_success 'HEAD link pointing at a funny place' '\n \ttest_i18ngrep \"HEAD points to something strange\" out\n '\n \n-test_expect_success 'HEAD link pointing at a funny object (from different wt)' '\n+test_expect_success REFFILES 'HEAD link pointing at a funny object (from different wt)' '\n \tsaved_head=$(git rev-parse --verify HEAD) &&\n \ttest_when_finished \"git update-ref HEAD $saved_head\" &&\n \ttest_when_finished \"git worktree remove -f wt\" &&\n@@ -151,7 +151,7 @@ test_expect_success 'HEAD link pointing at a funny object (from different wt)' '\n \ttest_i18ngrep \"main-worktree/HEAD: detached HEAD points\" out\n '\n \n-test_expect_success 'other worktree HEAD link pointing at a funny object' '\n+test_expect_success REFFILES 'other worktree HEAD link pointing at a funny object' '\n \ttest_when_finished \"git worktree remove -f other\" &&\n \tgit worktree add other &&\n \techo $ZERO_OID >.git/worktrees/other/HEAD &&\ndiff --git a/t/t2011-checkout-invalid-head.sh b/t/t2011-checkout-invalid-head.sh\nindex d9997e7b6b4..3c8135831b8 100755\n--- a/t/t2011-checkout-invalid-head.sh\n+++ b/t/t2011-checkout-invalid-head.sh\n@@ -18,18 +18,18 @@ test_expect_success 'checkout should not start branch from a tree' '\n \ttest_must_fail git checkout -b newbranch main^{tree}\n '\n \n-test_expect_success 'checkout main from invalid HEAD' '\n+test_expect_success REFFILES 'checkout main from invalid HEAD' '\n \techo $ZERO_OID >.git/HEAD &&\n \tgit checkout main --\n '\n \n-test_expect_success 'checkout notices failure to lock HEAD' '\n+test_expect_success REFFILES 'checkout notices failure to lock HEAD' '\n \ttest_when_finished \"rm -f .git/HEAD.lock\" &&\n \t>.git/HEAD.lock &&\n \ttest_must_fail git checkout -b other\n '\n \n-test_expect_success 'create ref directory/file conflict scenario' '\n+test_expect_success REFFILES 'create ref directory/file conflict scenario' '\n \tgit update-ref refs/heads/outer/inner main &&\n \n \t# do not rely on symbolic-ref to get a known state,\n@@ -39,26 +39,26 @@ test_expect_success 'create ref directory/file conflict scenario' '\n \t}\n '\n \n-test_expect_success 'checkout away from d/f HEAD (unpacked, to branch)' '\n+test_expect_success REFFILES 'checkout away from d/f HEAD (unpacked, to branch)' '\n \treset_to_df &&\n \tgit checkout main\n '\n \n-test_expect_success 'checkout away from d/f HEAD (unpacked, to detached)' '\n+test_expect_success REFFILES 'checkout away from d/f HEAD (unpacked, to detached)' '\n \treset_to_df &&\n \tgit checkout --detach main\n '\n \n-test_expect_success 'pack refs' '\n+test_expect_success REFFILES 'pack refs' '\n \tgit pack-refs --all --prune\n '\n \n-test_expect_success 'checkout away from d/f HEAD (packed, to branch)' '\n+test_expect_success REFFILES 'checkout away from d/f HEAD (packed, to branch)' '\n \treset_to_df &&\n \tgit checkout main\n '\n \n-test_expect_success 'checkout away from d/f HEAD (packed, to detached)' '\n+test_expect_success REFFILES 'checkout away from d/f HEAD (packed, to detached)' '\n \treset_to_df &&\n \tgit checkout --detach main\n '\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex 933aa9eebbd..606c50fe66c 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -28,7 +28,7 @@ test_expect_success 'git branch --help should not have created a bogus branch' '\n \ttest_ref_missing refs/heads/--help\n '\n \n-test_expect_success 'branch -h in broken repository' '\n+test_expect_success REFFILES 'branch -h in broken repository' '\n \tmkdir broken &&\n \t(\n \t\tcd broken &&\n@@ -245,7 +245,7 @@ test_expect_success 'git branch -M baz bam should succeed when baz is checked ou\n \tgit worktree prune\n '\n \n-test_expect_success 'git branch -M fails if updating any linked working tree fails' '\n+test_expect_success REFFILES 'git branch -M fails if updating any linked working tree fails' '\n \tgit worktree add -b baz bazdir1 &&\n \tgit worktree add -f bazdir2 baz &&\n \ttouch .git/worktrees/bazdir1/HEAD.lock &&\n@@ -836,14 +836,14 @@ test_expect_success 'renaming a symref is not allowed' '\n \ttest_ref_missing refs/heads/new-topic\n '\n \n-test_expect_success SYMLINKS 'git branch -m u v should fail when the reflog for u is a symlink' '\n+test_expect_success SYMLINKS,REFFILES 'git branch -m u v should fail when the reflog for u is a symlink' '\n \tgit branch --create-reflog u &&\n \tmv .git/logs/refs/heads/u real-u &&\n \tln -s real-u .git/logs/refs/heads/u &&\n \ttest_must_fail git branch -m u v\n '\n \n-test_expect_success SYMLINKS 'git branch -m with symlinked .git/refs' '\n+test_expect_success SYMLINKS,REFFILES 'git branch -m with symlinked .git/refs' '\n \ttest_when_finished \"rm -rf subdir\" &&\n \tgit init --bare subdir &&\n \ndiff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh\nindex d3df19a51f8..435943a0891 100755\n--- a/t/t3400-rebase.sh\n+++ b/t/t3400-rebase.sh\n@@ -424,7 +424,7 @@ test_expect_success 'refuse to switch to branch checked out elsewhere' '\n \ttest_i18ngrep \"already used by worktree at\" err\n '\n \n-test_expect_success MINGW,SYMLINKS_WINDOWS 'rebase when .git/logs is a symlink' '\n+test_expect_success REFFILES,MINGW,SYMLINKS_WINDOWS 'rebase when .git/logs is a symlink' '\n \tgit checkout main &&\n \tmv .git/logs actual_logs &&\n \tcmd //c \"mklink /D .git\\logs ..\\actual_logs\" &&\ndiff --git a/t/t5605-clone-local.sh b/t/t5605-clone-local.sh\nindex bedd29d0550..a3055869bc7 100755\n--- a/t/t5605-clone-local.sh\n+++ b/t/t5605-clone-local.sh\n@@ -157,7 +157,7 @@ test_expect_success 'cloning locally respects \"-u\" for fetching refs' '\n \ttest_must_fail git clone --bare -u false a should_not_work.git\n '\n \n-test_expect_success 'local clone from repo with corrupt refs fails gracefully' '\n+test_expect_success REFFILES 'local clone from repo with corrupt refs fails gracefully' '\n \tgit init corrupt &&\n \ttest_commit -C corrupt one &&\n \techo a >corrupt/.git/refs/heads/topic &&\n-- \n2.42.0\n\n"},{"id":"483395","messageId":"ZS9vp9dnoVVUsMIt@tanuki","threadId":"60390","inReplyTo":"cover.1697607222.git.ps@pks.im","subject":"Re: [PATCH 00/11] t: reduce direct disk access to data structures","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-18T05:39:51Z","receivedAt":"2023-10-18T05:39:57Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Wed, Oct 18, 2023 at 07:35:03AM +0200, Patrick Steinhardt wrote:\n> Hi,\n> \n> this patch series refactors a bunch of our tests to perform less direct\n> disk access to on-disk data structures. Instead, the tests are converted\n> to use Git tools or our test-tool to access data to the best extent\n> possible. This serves two benefits:\n> \n>     - We increase test coverage of our own code base.\n> \n>     - We become less dependent on the actual on-disk format.\n> \n> The main motivation for this patch series was the second bullet point as\n> it is preparatory work to get the reftable backend upstreamed. My intent\n> is to get rid of many or even most of the current blockers in the Git\n> project before trying to send the reftable implementation upstream.\n> While this will be a lot of up-front work that is going to span over a\n> long time period, I think this approach will make everyones live easier\n> by doing comparatively small and incremental improvements to the Git\n> project. Ultimately, the final patch series should in the best case only\n> contain the new backend as well as testing infrastructure, but not much\n> else.\n> \n> Patrick\n\nAs usual, I forgot to mention that this applies on top of current\nmaster, which is at a9ecda2788 (The eighteenth batch, 2023-10-13) at the\ntime of writing. I look forward to the day where I remember to include\nthis information in the cover letter...\n\nPatrick\n\n> Patrick Steinhardt (11):\n>   t: add helpers to test for reference existence\n>   t: allow skipping expected object ID in `ref-store update-ref`\n>   t: convert tests to use helpers for reference existence\n>   t: convert tests to not write references via the filesystem\n>   t: convert tests to not access symrefs via the filesystem\n>   t: convert tests to not access reflog via the filesystem\n>   t1450: convert tests to remove worktrees via git-worktree(1)\n>   t4207: delete replace references via git-update-ref(1)\n>   t7300: assert exact states of repo\n>   t7900: assert the absence of refs via git-for-each-ref(1)\n>   t: mark several tests that assume the files backend with REFFILES\n> \n>  t/README                           |  9 ++++\n>  t/helper/test-ref-store.c          | 38 +++++++++++++--\n>  t/t1400-update-ref.sh              | 49 ++++++++++----------\n>  t/t1430-bad-ref-name.sh            | 39 ++++++++++------\n>  t/t1450-fsck.sh                    | 46 ++++++++++---------\n>  t/t2011-checkout-invalid-head.sh   | 16 +++----\n>  t/t3200-branch.sh                  | 74 ++++++++++++++++--------------\n>  t/t3400-rebase.sh                  |  2 +-\n>  t/t3404-rebase-interactive.sh      |  2 +-\n>  t/t4013-diff-various.sh            |  2 +-\n>  t/t4202-log.sh                     |  2 +-\n>  t/t4207-log-decoration-colors.sh   |  4 +-\n>  t/t5521-pull-options.sh            |  4 +-\n>  t/t5526-fetch-submodules.sh        |  2 +-\n>  t/t5605-clone-local.sh             |  6 +--\n>  t/t5702-protocol-v2.sh             | 24 +++++++---\n>  t/t7300-clean.sh                   | 23 ++++++----\n>  t/t7900-maintenance.sh             |  3 +-\n>  t/t9133-git-svn-nested-git-repo.sh |  2 +-\n>  t/test-lib-functions.sh            | 66 ++++++++++++++++++++++++++\n>  20 files changed, 277 insertions(+), 136 deletions(-)\n> \n> -- \n> 2.42.0\n> \n\n\n"},{"id":"483403","messageId":"CAFQ2z_MiOazk8qLvEn5xfHyMwY=5pa6rEJD3vC3+rnHz4AeQ2Q@mail.gmail.com","threadId":"60390","inReplyTo":"c4d09e3e5dbd11221cc4d229b815434d441cdb4d.1697607222.git.ps@pks.im","subject":"Re: [PATCH 08/11] t4207: delete replace references via git-update-ref(1)","fromName":"Han-Wen Nienhuys","fromEmail":"hanwen@google.com","sentAt":"2023-10-18T13:27:11Z","receivedAt":"2023-10-18T13:27:31Z","isPatch":true,"sender":{"key":"hanwen@google.com","avatar":"https://avatars.githubusercontent.com/u/31547?v=4"},"body":"On Wed, Oct 18, 2023 at 7:35 AM Patrick Steinhardt <ps@pks.im> wrote:\n>\n> In t4207 we set up a set of replace objects via git-replace(1). Because\n> these references should not be impacting subsequent tests we also set up\n> some cleanup logic that deletes the replacement references via a call to\n> `rm -rf`. This reaches into the internal implementation details of the\n> reference backend and will thus break when we grow an alternative refdb\n> implementation.\n>\n> Refactor the tests to delete the replacement refs via Git commands so\n> that we become independent of the actual refdb that's in use. As we\n> don't have a nice way to delete all replacements or all references in a\n> certain namespace, we opt for a combination of git-for-each-ref(1) and\n> git-update-ref(1)'s `--stdin` mode.\n\nThere is a test helper that can directly access the ref database,\nt/helper/test-ref-store.c.\n\nIf you use that manipulate refs for testing purposes, you make the\ntest independent of behavior\ngit-for-each-ref/git-update-ref, which is what you want for testing\nreplace-objects?\n\n-- \nHan-Wen Nienhuys - Google Munich\nI work 80%. Don't expect answers from me on Fridays.\n--\nGoogle Germany GmbH, Erika-Mann-Strasse 33, 80636 Munich\nRegistergericht und -nummer: Hamburg, HRB 86891\nSitz der Gesellschaft: Hamburg\nGeschäftsführer: Paul Manicle, Liana Sebastian\n"},{"id":"483404","messageId":"xmqqbkcwuetq.fsf@gitster.g","threadId":"60390","inReplyTo":"cover.1697607222.git.ps@pks.im","subject":"Re: [PATCH 00/11] t: reduce direct disk access to data structures","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-10-18T15:32:01Z","receivedAt":"2023-10-18T15:32:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> this patch series refactors a bunch of our tests to perform less direct\n> disk access to on-disk data structures. Instead, the tests are converted\n> to use Git tools or our test-tool to access data to the best extent\n> possible. This serves two benefits:\n\nLaudable goal.\n\n>     - We increase test coverage of our own code base.\n\nMeaning the new code added to test-tool for this series will also\nget tested and bugs spotted?\n\n>     - We become less dependent on the actual on-disk format.\n\nYes, this is very desirable.  Without looking at the implementation,\nI see some issues aiming for this goal may involve. [a] using the\nproduction code for validation would mean our expectation to be\ncompared to the reality to be validated can be affected by the same\nbug, making two wrongs to appear right; [b] using a separate\nimplementation used only for validation would at least mean we will\nhave to make the same mistake in unique part of both implementations\nthat is less likely to miss bugs compared to [a], but bugs in shared\npart of the production code and validation code will be hidden the\nsame way as [a].\n\nBut you have thought about this series a lot deeper and longer than\nI have, so let me read on and find out what your solution is.\n\n\n> The main motivation for this patch series was the second bullet point as\n> it is preparatory work to get the reftable backend upstreamed.\n\nYay.\n\n> My intent\n> is to get rid of many or even most of the current blockers in the Git\n> project before trying to send the reftable implementation upstream.\n> While this will be a lot of up-front work that is going to span over a\n> long time period, I think this approach will make everyones live easier\n> by doing comparatively small and incremental improvements to the Git\n> project.\n\nThank you very much for being considerate.  I assume this approach\nis what those in the Contributors' Summit agreed to be the best way\nforward to do anything sizeable.\n\n> Ultimately, the final patch series should in the best case only\n> contain the new backend as well as testing infrastructure, but not much\n> else.\n\n;-)\n"},{"id":"483405","messageId":"xmqqmswfud7p.fsf@gitster.g","threadId":"60390","inReplyTo":"e947feb1c77f7e9f3c7f983bbe47137fbce42367.1697607222.git.ps@pks.im","subject":"Re: [PATCH 01/11] t: add helpers to test for reference existence","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-10-18T16:06:50Z","receivedAt":"2023-10-18T16:06:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> There are two major ways to check for the existence of a reference in\n> our tests:\n>\n>     - `git rev-parse --verify` can be used to check for existence of a\n>       reference. This only works in the case where the reference is well\n>       formed though and resolves to an actual object ID. This does not\n>       work with malformed reference names or invalid contents.\n>\n>     - `test_path_is_file` can be used to check for existence of a loose\n>       reference if it is known to not resolve to an actual object ID. It\n>       by necessity reaches into implementation details of the reference\n>       backend though.\n\nTrue.  It would be ideal if we can limit the use of latter when we\n_care_ how the ref is stored (e.g., \"we expect it to be stored as a\nloose ref, not packed\").  \"The ref R at must be pointing at the\ncommit X\" is better asserted by using the former (or \"git show-ref\")\nas we not just only want to see the .git/refs/R file holding the\nobject name X, but also want to see the production git tools observe\nthe same---if what rev-parse or show-ref observes is different from\nthe expected state and they say ref R does not point at commit X, we\nshould complain (rev-parse or show-ref may be broken in the version\nof Git being tested, but we can assume that their breakage will be\ncaught elsewhere in the test suite as well, so as long as we trust\nthem, using them as the validator is better than going into the\nimplementation detail and assuming things like \"new refs always\nappear as a loose ref\" that we might want to change in the future).\n\n> Similarly, there are two equivalent ways to check for the absence of a\n> reference:\n>\n>     - `test_must_fail git rev-parse` can be used to check for the\n>       absence of a reference. It could fail due to a number of reasons\n>       though, and all of these reasons will be thrown into the same bag\n>       as an absent reference.\n>\n>     - `test_path_is_missing` can be used to check explicitly for the\n>       absence of a loose reference, but again reaches into internal\n>       implementation details of the reference backend.\n>\n> So both our tooling to check for the presence and for the absence of\n> references in tests is lacking as either failure cases are thrown into\n> the same bag or we need to reach into internal implementation details of\n> the respective reference backend.\n\n> Introduce a new subcommand for our ref-store test helper that explicitly\n> checks only for the presence or absence of a reference. This addresses\n> these limitations:\n>\n>     - We can check for the presence of references with malformed names.\n\nBut for the purpose of tests, we can control the input.  When we\nperform an operation that we expect a ref R to be created, we would\nknow R is well formed and we can validate using a tool that we know\nwould be broken when fed a malformed name.  So I do not see this as\na huge \"limitation\".\n\n>     - We can check for the presence of references that don't resolve.\n\nDo you mean a dangling symbolic ref?  We are using a wrong tool if\nyou are using rev-parse for that, aren't we?  Isn't symbolic-ref\nthere for us for this exact use case?  That is\n\n>     - We can explicitly handle the case where a reference is missing by\n>       special-casing ENOENT errors.\n\nYou probably know the error conditions refs_read_raw_ref() can be\nbroken better than I do, but this feels a bit too intimate with how\nthe method for the files backend happens to be implemented, which at\nthe same time, can risk that [a] other backends can implement their\n\"ref does not resolve to an object name---is it because it is\nmissing?\" report incorrectly and [b] we would eventually want to\nknow error conditions other than \"the ref requested is missing\" and\nat that point we would need more \"special casing\", which does not\nsmell easy to scale.\n\n>     - We don't need to reach into implementation details of the backend,\n>       which would allow us to use this helper for the future reftable\n>       backend.\n\nThis is exactly what we want to aim for.\n\n> Next to this subcommand we also provide two wrappers `test_ref_exists`\n> and `test_ref_missing` that make the helper easier to use.\n\nHmmmm.  This may introduce \"who watches the watchers\" problem, no?\nI briefly wondered if a better approach is to teach the production\ncode, e.g., rev-parse, to optionally give more detailed diag.  It\nessentially may be the same (making the code in test-ref-store.c\nadded by this patch available from rev-parse, we would easily get\nthere), so I do not think the distinction matters.\n\n> diff --git a/t/README b/t/README\n> index 61080859899..779f7e7dd86 100644\n> --- a/t/README\n> +++ b/t/README\n> @@ -928,6 +928,15 @@ see test-lib-functions.sh for the full list and their options.\n>     committer times to defined state.  Subsequent calls will\n>     advance the times by a fixed amount.\n>  \n> + - test_ref_exists <ref>, test_ref_missing <ref>\n> +\n> +   Check whether a reference exists or is missing. In contrast to\n> +   git-rev-parse(1), these helpers also work with invalid reference\n> +   names and references whose contents are unresolvable. The latter\n> +   function also distinguishes generic errors from the case where a\n> +   reference explicitly doesn't exist and is thus safer to use than\n> +   `test_must_fail git rev-parse`.\n> +\n>   - test_commit <message> [<filename> [<contents>]]\n>  \n>     Creates a commit with the given message, committing the given\n> diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\n> index 48552e6a9e0..7400f560ab6 100644\n> --- a/t/helper/test-ref-store.c\n> +++ b/t/helper/test-ref-store.c\n> @@ -1,6 +1,6 @@\n>  #include \"test-tool.h\"\n>  #include \"hex.h\"\n> -#include \"refs.h\"\n> +#include \"refs/refs-internal.h\"\n>  #include \"setup.h\"\n>  #include \"worktree.h\"\n>  #include \"object-store-ll.h\"\n> @@ -221,6 +221,30 @@ static int cmd_verify_ref(struct ref_store *refs, const char **argv)\n>  \treturn ret;\n>  }\n>  \n> +static int cmd_ref_exists(struct ref_store *refs, const char **argv)\n> +{\n> +\tconst char *refname = notnull(*argv++, \"refname\");\n> +\tstruct strbuf unused_referent = STRBUF_INIT;\n> +\tstruct object_id unused_oid;\n> +\tunsigned int unused_type;\n> +\tint failure_errno;\n> +\n> +\tif (refs_read_raw_ref(refs, refname, &unused_oid, &unused_referent,\n> +\t\t\t      &unused_type, &failure_errno)) {\n> +\t\t/*\n> +\t\t * We handle ENOENT separately here such that it is possible to\n> +\t\t * distinguish actually-missing references from any kind of\n> +\t\t * generic error.\n> +\t\t */\n> +\t\tif (failure_errno == ENOENT)\n> +\t\t\treturn 17;\n\nCan we tell between the cases where the ref itself is missing, and\nthe requested ref is symbolic and points at a missing ref?  This\nparticular case might be OK, but there may other cases where this\n\"special case\" may not be narrow enough.\n\nAs long we are going to spend cycles to refine the classification of\nerror conditions, which is a very good thing to aim for the reason\ndescribed in the proposed log message, namely \"rev-parse can fail\nfor reasons other than the ref being absent\", I have to wonder again\nthat the fruit of such an effort should become available in the\nproduction code, instead of being kept only in test-tool.\n"},{"id":"483406","messageId":"xmqqil73ud5j.fsf@gitster.g","threadId":"60390","inReplyTo":"1f615d62f99e9ab47d37500f05b29615bafffba2.1697607222.git.ps@pks.im","subject":"Re: [PATCH 02/11] t: allow skipping expected object ID in `ref-store update-ref`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-10-18T16:08:08Z","receivedAt":"2023-10-18T16:08:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> We require the caller to pass both the old and new expected object ID to\n> our `test-tool ref-store update-ref` helper. When trying to update a\n> symbolic reference though it's impossible to specify the expected object\n> ID, which means that the test would instead have to force-update the\n> reference. This is currently impossible though.\n>\n> Update the helper to optionally skip verification of the old object ID\n> in case the test passes in an empty old object ID as input.\n>\n> Signed-off-by: Patrick Steinhardt <ps@pks.im>\n> ---\n>  t/helper/test-ref-store.c | 11 +++++++----\n>  1 file changed, 7 insertions(+), 4 deletions(-)\n\nGood.\n\nEven better would be to make the old one optional, though.\n\n> diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\n> index 7400f560ab6..7dc83137584 100644\n> --- a/t/helper/test-ref-store.c\n> +++ b/t/helper/test-ref-store.c\n> @@ -322,16 +322,19 @@ static int cmd_update_ref(struct ref_store *refs, const char **argv)\n>  \tconst char *new_sha1_buf = notnull(*argv++, \"new-sha1\");\n>  \tconst char *old_sha1_buf = notnull(*argv++, \"old-sha1\");\n>  \tunsigned int flags = arg_flags(*argv++, \"flags\", transaction_flags);\n> -\tstruct object_id old_oid;\n> +\tstruct object_id old_oid, *old_oid_ptr = NULL;\n>  \tstruct object_id new_oid;\n>  \n> -\tif (get_oid_hex(old_sha1_buf, &old_oid))\n> -\t\tdie(\"cannot parse %s as %s\", old_sha1_buf, the_hash_algo->name);\n> +\tif (*old_sha1_buf) {\n> +\t\tif (get_oid_hex(old_sha1_buf, &old_oid))\n> +\t\t\tdie(\"cannot parse %s as %s\", old_sha1_buf, the_hash_algo->name);\n> +\t\told_oid_ptr = &old_oid;\n> +\t}\n>  \tif (get_oid_hex(new_sha1_buf, &new_oid))\n>  \t\tdie(\"cannot parse %s as %s\", new_sha1_buf, the_hash_algo->name);\n>  \n>  \treturn refs_update_ref(refs, msg, refname,\n> -\t\t\t       &new_oid, &old_oid,\n> +\t\t\t       &new_oid, old_oid_ptr,\n>  \t\t\t       flags, UPDATE_REFS_DIE_ON_ERR);\n>  }\n"},{"id":"483407","messageId":"xmqqcyxbuc71.fsf@gitster.g","threadId":"60390","inReplyTo":"ac6a49c7c84ad2b836f099557dd6989703ebda8f.1697607222.git.ps@pks.im","subject":"Re: [PATCH 03/11] t: convert tests to use helpers for reference existence","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-10-18T16:28:50Z","receivedAt":"2023-10-18T16:29:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> Convert tests that use `test_path_is_file` and `test_path_is_missing` to\n> instead use our new helpers `test_ref_exists` and `test_ref_missing`.\n> These hook into the reference database directly and thus work indepently\n> of the actual reference backend that is being used.\n>\n> Signed-off-by: Patrick Steinhardt <ps@pks.im>\n> ---\n>  t/t1430-bad-ref-name.sh | 27 ++++++++++++++++++---------\n>  t/t3200-branch.sh       | 33 ++++++++++++++++++---------------\n>  t/t5521-pull-options.sh |  4 ++--\n>  t/t5605-clone-local.sh  |  2 +-\n>  4 files changed, 39 insertions(+), 27 deletions(-)\n\nI scanned through the changes, and all of them looked sensible.  I\nalso very much liked the added \"now we have done something to create\n'badname' branch, let's make sure it exists, before trying to see if\nthe machinery to delete it works correctly\" tests.\n\nLooking good.\n\nThanks.\n\n> diff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh\n> index ff1c967d550..7b7d6953c62 100755\n> --- a/t/t1430-bad-ref-name.sh\n> +++ b/t/t1430-bad-ref-name.sh\n> @@ -205,8 +205,9 @@ test_expect_success 'update-ref --no-deref -d can delete symref to broken name'\n>  \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref\" &&\n>  \ttest-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&\n>  \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname\" &&\n> +\ttest_ref_exists refs/heads/badname &&\n>  \tgit update-ref --no-deref -d refs/heads/badname >output 2>error &&\n> -\ttest_path_is_missing .git/refs/heads/badname &&\n> +\ttest_ref_missing refs/heads/badname &&\n>  \ttest_must_be_empty output &&\n>  \ttest_must_be_empty error\n>  '\n> @@ -216,8 +217,9 @@ test_expect_success 'branch -d can delete symref to broken name' '\n>  \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref\" &&\n>  \ttest-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&\n>  \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname\" &&\n> +\ttest_ref_exists refs/heads/badname &&\n>  \tgit branch -d badname >output 2>error &&\n> -\ttest_path_is_missing .git/refs/heads/badname &&\n> +\ttest_ref_missing refs/heads/badname &&\n>  \ttest_i18ngrep \"Deleted branch badname (was refs/heads/broken\\.\\.\\.ref)\" output &&\n>  \ttest_must_be_empty error\n>  '\n> @@ -225,8 +227,9 @@ test_expect_success 'branch -d can delete symref to broken name' '\n>  test_expect_success 'update-ref --no-deref -d can delete dangling symref to broken name' '\n>  \ttest-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&\n>  \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname\" &&\n> +\ttest_ref_exists refs/heads/badname &&\n>  \tgit update-ref --no-deref -d refs/heads/badname >output 2>error &&\n> -\ttest_path_is_missing .git/refs/heads/badname &&\n> +\ttest_ref_missing refs/heads/badname &&\n>  \ttest_must_be_empty output &&\n>  \ttest_must_be_empty error\n>  '\n> @@ -234,8 +237,9 @@ test_expect_success 'update-ref --no-deref -d can delete dangling symref to brok\n>  test_expect_success 'branch -d can delete dangling symref to broken name' '\n>  \ttest-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&\n>  \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname\" &&\n> +\ttest_ref_exists refs/heads/badname &&\n>  \tgit branch -d badname >output 2>error &&\n> -\ttest_path_is_missing .git/refs/heads/badname &&\n> +\ttest_ref_missing refs/heads/badname &&\n>  \ttest_i18ngrep \"Deleted branch badname (was refs/heads/broken\\.\\.\\.ref)\" output &&\n>  \ttest_must_be_empty error\n>  '\n> @@ -245,8 +249,9 @@ test_expect_success 'update-ref -d can delete broken name through symref' '\n>  \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref\" &&\n>  \ttest-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&\n>  \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname\" &&\n> +\ttest_ref_exists refs/heads/broken...ref &&\n>  \tgit update-ref -d refs/heads/badname >output 2>error &&\n> -\ttest_path_is_missing .git/refs/heads/broken...ref &&\n> +\ttest_ref_missing refs/heads/broken...ref &&\n>  \ttest_must_be_empty output &&\n>  \ttest_must_be_empty error\n>  '\n> @@ -254,8 +259,9 @@ test_expect_success 'update-ref -d can delete broken name through symref' '\n>  test_expect_success 'update-ref --no-deref -d can delete symref with broken name' '\n>  \tprintf \"ref: refs/heads/main\\n\" >.git/refs/heads/broken...symref &&\n>  \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n> +\ttest_ref_exists refs/heads/broken...symref &&\n>  \tgit update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&\n> -\ttest_path_is_missing .git/refs/heads/broken...symref &&\n> +\ttest_ref_missing refs/heads/broken...symref &&\n>  \ttest_must_be_empty output &&\n>  \ttest_must_be_empty error\n>  '\n> @@ -263,8 +269,9 @@ test_expect_success 'update-ref --no-deref -d can delete symref with broken name\n>  test_expect_success 'branch -d can delete symref with broken name' '\n>  \tprintf \"ref: refs/heads/main\\n\" >.git/refs/heads/broken...symref &&\n>  \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n> +\ttest_ref_exists refs/heads/broken...symref &&\n>  \tgit branch -d broken...symref >output 2>error &&\n> -\ttest_path_is_missing .git/refs/heads/broken...symref &&\n> +\ttest_ref_missing refs/heads/broken...symref &&\n>  \ttest_i18ngrep \"Deleted branch broken...symref (was refs/heads/main)\" output &&\n>  \ttest_must_be_empty error\n>  '\n> @@ -272,8 +279,9 @@ test_expect_success 'branch -d can delete symref with broken name' '\n>  test_expect_success 'update-ref --no-deref -d can delete dangling symref with broken name' '\n>  \tprintf \"ref: refs/heads/idonotexist\\n\" >.git/refs/heads/broken...symref &&\n>  \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n> +\ttest_ref_exists refs/heads/broken...symref &&\n>  \tgit update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&\n> -\ttest_path_is_missing .git/refs/heads/broken...symref &&\n> +\ttest_ref_missing refs/heads/broken...symref &&\n>  \ttest_must_be_empty output &&\n>  \ttest_must_be_empty error\n>  '\n> @@ -281,8 +289,9 @@ test_expect_success 'update-ref --no-deref -d can delete dangling symref with br\n>  test_expect_success 'branch -d can delete dangling symref with broken name' '\n>  \tprintf \"ref: refs/heads/idonotexist\\n\" >.git/refs/heads/broken...symref &&\n>  \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n> +\ttest_ref_exists refs/heads/broken...symref &&\n>  \tgit branch -d broken...symref >output 2>error &&\n> -\ttest_path_is_missing .git/refs/heads/broken...symref &&\n> +\ttest_ref_missing refs/heads/broken...symref &&\n>  \ttest_i18ngrep \"Deleted branch broken...symref (was refs/heads/idonotexist)\" output &&\n>  \ttest_must_be_empty error\n>  '\n> diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\n> index 080e4f24a6e..bde4f1485b7 100755\n> --- a/t/t3200-branch.sh\n> +++ b/t/t3200-branch.sh\n> @@ -25,7 +25,7 @@ test_expect_success 'prepare a trivial repository' '\n>  \n>  test_expect_success 'git branch --help should not have created a bogus branch' '\n>  \ttest_might_fail git branch --man --help </dev/null >/dev/null 2>&1 &&\n> -\ttest_path_is_missing .git/refs/heads/--help\n> +\ttest_ref_missing refs/heads/--help\n>  '\n>  \n>  test_expect_success 'branch -h in broken repository' '\n> @@ -40,7 +40,8 @@ test_expect_success 'branch -h in broken repository' '\n>  '\n>  \n>  test_expect_success 'git branch abc should create a branch' '\n> -\tgit branch abc && test_path_is_file .git/refs/heads/abc\n> +\tgit branch abc &&\n> +\ttest_ref_exists refs/heads/abc\n>  '\n>  \n>  test_expect_success 'git branch abc should fail when abc exists' '\n> @@ -61,11 +62,13 @@ test_expect_success 'git branch --force abc should succeed when abc exists' '\n>  '\n>  \n>  test_expect_success 'git branch a/b/c should create a branch' '\n> -\tgit branch a/b/c && test_path_is_file .git/refs/heads/a/b/c\n> +\tgit branch a/b/c &&\n> +\ttest_ref_exists refs/heads/a/b/c\n>  '\n>  \n>  test_expect_success 'git branch mb main... should create a branch' '\n> -\tgit branch mb main... && test_path_is_file .git/refs/heads/mb\n> +\tgit branch mb main... &&\n> +\ttest_ref_exists refs/heads/mb\n>  '\n>  \n>  test_expect_success 'git branch HEAD should fail' '\n> @@ -78,14 +81,14 @@ EOF\n>  test_expect_success 'git branch --create-reflog d/e/f should create a branch and a log' '\n>  \tGIT_COMMITTER_DATE=\"2005-05-26 23:30\" \\\n>  \tgit -c core.logallrefupdates=false branch --create-reflog d/e/f &&\n> -\ttest_path_is_file .git/refs/heads/d/e/f &&\n> +\ttest_ref_exists refs/heads/d/e/f &&\n>  \ttest_path_is_file .git/logs/refs/heads/d/e/f &&\n>  \ttest_cmp expect .git/logs/refs/heads/d/e/f\n>  '\n>  \n>  test_expect_success 'git branch -d d/e/f should delete a branch and a log' '\n>  \tgit branch -d d/e/f &&\n> -\ttest_path_is_missing .git/refs/heads/d/e/f &&\n> +\ttest_ref_missing refs/heads/d/e/f &&\n>  \ttest_must_fail git reflog exists refs/heads/d/e/f\n>  '\n>  \n> @@ -213,7 +216,7 @@ test_expect_success 'git branch -M should leave orphaned HEAD alone' '\n>  \t\ttest_commit initial &&\n>  \t\tgit checkout --orphan lonely &&\n>  \t\tgrep lonely .git/HEAD &&\n> -\t\ttest_path_is_missing .git/refs/head/lonely &&\n> +\t\ttest_ref_missing refs/head/lonely &&\n>  \t\tgit branch -M main mistress &&\n>  \t\tgrep lonely .git/HEAD\n>  \t)\n> @@ -799,8 +802,8 @@ test_expect_success 'deleting a symref' '\n>  \tgit symbolic-ref refs/heads/symref refs/heads/target &&\n>  \techo \"Deleted branch symref (was refs/heads/target).\" >expect &&\n>  \tgit branch -d symref >actual &&\n> -\ttest_path_is_file .git/refs/heads/target &&\n> -\ttest_path_is_missing .git/refs/heads/symref &&\n> +\ttest_ref_exists refs/heads/target &&\n> +\ttest_ref_missing refs/heads/symref &&\n>  \ttest_cmp expect actual\n>  '\n>  \n> @@ -809,16 +812,16 @@ test_expect_success 'deleting a dangling symref' '\n>  \ttest_path_is_file .git/refs/heads/dangling-symref &&\n>  \techo \"Deleted branch dangling-symref (was nowhere).\" >expect &&\n>  \tgit branch -d dangling-symref >actual &&\n> -\ttest_path_is_missing .git/refs/heads/dangling-symref &&\n> +\ttest_ref_missing refs/heads/dangling-symref &&\n>  \ttest_cmp expect actual\n>  '\n>  \n>  test_expect_success 'deleting a self-referential symref' '\n>  \tgit symbolic-ref refs/heads/self-reference refs/heads/self-reference &&\n> -\ttest_path_is_file .git/refs/heads/self-reference &&\n> +\ttest_ref_exists refs/heads/self-reference &&\n>  \techo \"Deleted branch self-reference (was refs/heads/self-reference).\" >expect &&\n>  \tgit branch -d self-reference >actual &&\n> -\ttest_path_is_missing .git/refs/heads/self-reference &&\n> +\ttest_ref_missing refs/heads/self-reference &&\n>  \ttest_cmp expect actual\n>  '\n>  \n> @@ -826,8 +829,8 @@ test_expect_success 'renaming a symref is not allowed' '\n>  \tgit symbolic-ref refs/heads/topic refs/heads/main &&\n>  \ttest_must_fail git branch -m topic new-topic &&\n>  \tgit symbolic-ref refs/heads/topic &&\n> -\ttest_path_is_file .git/refs/heads/main &&\n> -\ttest_path_is_missing .git/refs/heads/new-topic\n> +\ttest_ref_exists refs/heads/main &&\n> +\ttest_ref_missing refs/heads/new-topic\n>  '\n>  \n>  test_expect_success SYMLINKS 'git branch -m u v should fail when the reflog for u is a symlink' '\n> @@ -1142,7 +1145,7 @@ EOF\n>  test_expect_success 'git checkout -b g/h/i -l should create a branch and a log' '\n>  \tGIT_COMMITTER_DATE=\"2005-05-26 23:30\" \\\n>  \tgit checkout -b g/h/i -l main &&\n> -\ttest_path_is_file .git/refs/heads/g/h/i &&\n> +\ttest_ref_exists refs/heads/g/h/i &&\n>  \ttest_path_is_file .git/logs/refs/heads/g/h/i &&\n>  \ttest_cmp expect .git/logs/refs/heads/g/h/i\n>  '\n> diff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh\n> index 079b2f2536e..3681859f983 100755\n> --- a/t/t5521-pull-options.sh\n> +++ b/t/t5521-pull-options.sh\n> @@ -143,7 +143,7 @@ test_expect_success 'git pull --dry-run' '\n>  \t\tcd clonedry &&\n>  \t\tgit pull --dry-run ../parent &&\n>  \t\ttest_path_is_missing .git/FETCH_HEAD &&\n> -\t\ttest_path_is_missing .git/refs/heads/main &&\n> +\t\ttest_ref_missing refs/heads/main &&\n>  \t\ttest_path_is_missing .git/index &&\n>  \t\ttest_path_is_missing file\n>  \t)\n> @@ -157,7 +157,7 @@ test_expect_success 'git pull --all --dry-run' '\n>  \t\tgit remote add origin ../parent &&\n>  \t\tgit pull --all --dry-run &&\n>  \t\ttest_path_is_missing .git/FETCH_HEAD &&\n> -\t\ttest_path_is_missing .git/refs/remotes/origin/main &&\n> +\t\ttest_ref_missing refs/remotes/origin/main &&\n>  \t\ttest_path_is_missing .git/index &&\n>  \t\ttest_path_is_missing file\n>  \t)\n> diff --git a/t/t5605-clone-local.sh b/t/t5605-clone-local.sh\n> index 1d7b1abda1a..946c5751885 100755\n> --- a/t/t5605-clone-local.sh\n> +++ b/t/t5605-clone-local.sh\n> @@ -69,7 +69,7 @@ test_expect_success 'local clone of repo with nonexistent ref in HEAD' '\n>  \tgit clone a d &&\n>  \t(cd d &&\n>  \tgit fetch &&\n> -\ttest ! -e .git/refs/remotes/origin/HEAD)\n> +\ttest_ref_missing refs/remotes/origin/HEAD)\n>  '\n>  \n>  test_expect_success 'bundle clone without .bundle suffix' '\n"},{"id":"483413","messageId":"CAPig+cTueWuiWiw9_vou0Ud71utVMRkk5tR==tfkkMXBj9qWxA@mail.gmail.com","threadId":"60390","inReplyTo":"e947feb1c77f7e9f3c7f983bbe47137fbce42367.1697607222.git.ps@pks.im","subject":"Re: [PATCH 01/11] t: add helpers to test for reference existence","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2023-10-18T17:08:45Z","receivedAt":"2023-10-18T17:09:53Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Oct 18, 2023 at 1:35 AM Patrick Steinhardt <ps@pks.im> wrote:\n> Introduce a new subcommand for our ref-store test helper that explicitly\n> checks only for the presence or absence of a reference. This addresses\n> these limitations:\n> [...]\n> Signed-off-by: Patrick Steinhardt <ps@pks.im>\n> ---\n> diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\n> @@ -221,6 +221,30 @@ static int cmd_verify_ref(struct ref_store *refs, const char **argv)\n> +static int cmd_ref_exists(struct ref_store *refs, const char **argv)\n> +{\n> +       const char *refname = notnull(*argv++, \"refname\");\n> +       struct strbuf unused_referent = STRBUF_INIT;\n> +       struct object_id unused_oid;\n> +       unsigned int unused_type;\n> +       int failure_errno;\n> +\n> +       if (refs_read_raw_ref(refs, refname, &unused_oid, &unused_referent,\n> +                             &unused_type, &failure_errno)) {\n> +               /*\n> +                * We handle ENOENT separately here such that it is possible to\n> +                * distinguish actually-missing references from any kind of\n> +                * generic error.\n> +                */\n> +               if (failure_errno == ENOENT)\n> +                       return 17;\n> +               return -1;\n> +       }\n> +\n> +       strbuf_release(&unused_referent);\n> +       return 0;\n> +}\n\nUnless refs_read_raw_ref() guarantees that `unused_referent` remains\nunallocated upon failure[*], then the early returns inside the\nconditional leak the strbuf. True, the program is exiting immediately\nanyhow, so this (potential) leak isn't significant, but it seems odd\nto clean up in one case (return 0) but not in the others (return -1 &\n17).\n\n[*] In my (admittedly brief) scan of the code and documentation, I\ndidn't see any such promise.\n"},{"id":"483444","messageId":"xmqq1qdru6ds.fsf@gitster.g","threadId":"60390","inReplyTo":"c79431c0bf117d756e1d584f4c9415888d9ff9eb.1697607222.git.ps@pks.im","subject":"Re: [PATCH 04/11] t: convert tests to not write references via the filesystem","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-10-18T18:34:23Z","receivedAt":"2023-10-18T18:34:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> -test_expect_success \"fail to create $n\" '\n> -\ttest_when_finished \"rm -f .git/$n_dir\" &&\n> -\ttouch .git/$n_dir &&\n> -\ttest_must_fail git update-ref $n $A\n> +test_expect_success \"fail to create $n due to file/directory conflict\" '\n> +\ttest_when_finished \"git update-ref -d refs/heads/gu\" &&\n> +\tgit update-ref refs/heads/gu $A &&\n> +\ttest_must_fail git update-ref refs/heads/gu/fixes $A\n>  '\n\nOK, the original checks \"if a random garbage file, which may not\nnecessarily be a ref, exists at $n_dir, we cannot create a ref at\n$n_dir/fixes, due to D/F conflict\" more directly, but as long as our\nintention is to enforce the D/F restriction across different ref\nbackends [*], creating a ref at $n_dir and making sure $n_dir/fixes\ncannot be created is an equivalent check that is better (because it\ncan be applied for other backends).\n\n    Side note: there is no fundamental need to, though, and there\n         are cases where being able to have the 'seen' branch and\n         'seen/ps/ref-test-tools' branches at the same time is\n         beneficial---packed-refs and ref-table backends would not\n         have such an inherent limitation, but they can of course be\n         castrated to match what files-backend can(not) do.\n\n> @@ -222,7 +220,7 @@ test_expect_success 'delete symref without dereference when the referred ref is\n>  \n>  test_expect_success 'update-ref -d is not confused by self-reference' '\n>  \tgit symbolic-ref refs/heads/self refs/heads/self &&\n> -\ttest_when_finished \"rm -f .git/refs/heads/self\" &&\n> +\ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self\" &&\n>  \ttest_path_is_file .git/refs/heads/self &&\n\nI trust that this will be corrected to use some wrapper around \"git\nsymbolic-ref\" (or an equivalent for it as a test-tool subcommand) in\nsome future patch, if not in this series?\n\n>  \ttest_must_fail git update-ref -d refs/heads/self &&\n>  \ttest_path_is_file .git/refs/heads/self\n\nLikewise.\n\n> @@ -230,7 +228,7 @@ test_expect_success 'update-ref -d is not confused by self-reference' '\n>  \n>  test_expect_success 'update-ref --no-deref -d can delete self-reference' '\n>  \tgit symbolic-ref refs/heads/self refs/heads/self &&\n> -\ttest_when_finished \"rm -f .git/refs/heads/self\" &&\n> +\ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self\" &&\n>  \ttest_path_is_file .git/refs/heads/self &&\n>  \tgit update-ref --no-deref -d refs/heads/self &&\n>  \ttest_must_fail git show-ref --verify -q refs/heads/self\n\nWe already have the \"ref is missing\" test here.\n\nI'll stop at this point for now; will hopefully continue in a\nseparate message later.  Thanks.\n"},{"id":"483455","messageId":"xmqqfs27r5ng.fsf@gitster.g","threadId":"60390","inReplyTo":"c79431c0bf117d756e1d584f4c9415888d9ff9eb.1697607222.git.ps@pks.im","subject":"Re: [PATCH 04/11] t: convert tests to not write references via the filesystem","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-10-18T21:18:27Z","receivedAt":"2023-10-18T21:18:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> @@ -434,7 +432,7 @@ test_expect_success 'Query \"main@{2005-05-28}\" (past end of history)' '\n>  \ttest_i18ngrep -F \"warning: log for ref $m unexpectedly ended on $ld\" e\n>  '\n>  \n> -rm -f .git/$m .git/logs/$m expect\n> +git update-ref -d $m\n\nWe are not clearing \"expect\" file.  I do not know if it matters\nhere, but I am only recording what I noticed.\n\n> diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\n> index 10a539158c4..5cce24f1006 100755\n> --- a/t/t1450-fsck.sh\n> +++ b/t/t1450-fsck.sh\n> @@ -115,15 +115,16 @@ test_expect_success 'zlib corrupt loose object output ' '\n>  '\n>  \n>  test_expect_success 'branch pointing to non-commit' '\n> -\tgit rev-parse HEAD^{tree} >.git/refs/heads/invalid &&\n> +\ttree_oid=$(git rev-parse --verify HEAD^{tree}) &&\n> +\ttest-tool ref-store main update-ref msg refs/heads/invalid $tree_oid $ZERO_OID REF_SKIP_OID_VERIFICATION &&\n\nI have mixed feelings on this.\n\nIn olden days, plumbing commands tended to allow to pass anything\nthe user told them to use, but in more recent versions of Git, we,\nprobably by mistake, managed to butcher some of the plumbing\ncommands to make them unable to deliberately \"break\" repositories,\none victim being \"update-ref\", i.e.\n\n    $ git update-ref refs/heads/invalid HEAD^{tree}\n\nis rejected these days (I just checked with v1.3.0 and it allows me\nto do this), and that is one of the reasons why we manually broke\nthe repository in these tests.  We need to have a warning message in\ncomments near the implementation of \"ref-store update-ref\" that says\nnever ever attempt to share code with the production version of\nupdate-ref---otherwise this (or the \"safety\" given to the plumbing\ncommand, possibly by mistake) will be broken, depending on which\ndirection such a sharing goes.  On the other hand, forcing us to\nkeep two separate implementations, one deliberately loose to allow\nus corrupt repositories, the other for production and actively used,\nwould mean the former one that is only used for validation would risk\nbitrotting.\n\n>  \ttest_when_finished \"git update-ref -d refs/heads/invalid\" &&\n\nNot a problem this patch introduces, but I think it is a better\ndiscipline to have when_finished clean-up routine prepared before we\ndo actual damage, i.e. if I were writing this test today from scratch,\nI would expect it to be before \"git rev-parse >.git/refs/heads/invalid\"\nis done.\n\n>  \ttest_must_fail git fsck 2>out &&\n>  \ttest_i18ngrep \"not a commit\" out\n>  '\n\nA #leftoverbit that is not relevant to the topic; we should clean\nthese test_i18ngrep and replace them with a plain \"grep\".\n\n>  test_expect_success 'HEAD link pointing at a funny object' '\n> -\ttest_when_finished \"mv .git/SAVED_HEAD .git/HEAD\" &&\n> -\tmv .git/HEAD .git/SAVED_HEAD &&\n> +\tsaved_head=$(git rev-parse --verify HEAD) &&\n> +\ttest_when_finished \"git update-ref HEAD ${saved_head}\" &&\n>  \techo $ZERO_OID >.git/HEAD &&\n\nAre you sure .git/HEAD when this test is entered is a detached HEAD?\nThe title of the test says \"HEAD link\", and I take it to mean HEAD\nis a symlink, and we save it away, while we create a loose ref that\npoints at 0{40} in a detached HEAD state.  Actually, the original\nwould also work if HEAD is detached on entry.  In either case,\nmoving SAVED_HEAD back to HEAD would restore the original state.\n\nBut the updated one only works if HEAD upon entry is already\ndetached.  Is this intended?\n\n> @@ -131,8 +132,8 @@ test_expect_success 'HEAD link pointing at a funny object' '\n>  '\n>  \n>  test_expect_success 'HEAD link pointing at a funny place' '\n> -\ttest_when_finished \"mv .git/SAVED_HEAD .git/HEAD\" &&\n> -\tmv .git/HEAD .git/SAVED_HEAD &&\n> +\tsaved_head=$(git rev-parse --verify HEAD) &&\n> +\ttest_when_finished \"git update-ref --no-deref HEAD ${saved_head}\" &&\n\nLikewise.  Use of \"update-ref\" in the previous one vs \"update-ref\n--no-deref\" in this one to recover from the damage the tests make\nmakes me feel that we may be assuming too much.\n\n>  \techo \"ref: refs/funny/place\" >.git/HEAD &&\n\nEven though \"git symbolic-ref\" refuses to point HEAD outside refs/,\nas plumbing command should, it allows it to point it outside refs/heads/.\nso this line should probably become\n\n\tgit symbolic-ref HEAD refs/funny/place\n\nin the same spirit as the rest of the series.\n\n> @@ -391,7 +393,7 @@ test_expect_success 'tag pointing to nonexistent' '\n>  \n>  \ttag=$(git hash-object -t tag -w --stdin <invalid-tag) &&\n>  \ttest_when_finished \"remove_object $tag\" &&\n> -\techo $tag >.git/refs/tags/invalid &&\n> +\tgit update-ref refs/tags/invalid $tag &&\n\nGood (not just this one, but similar ones throughout this patch).\n\n\n"},{"id":"483464","messageId":"xmqqlebzzeh8.fsf@gitster.g","threadId":"60390","inReplyTo":"ZS9vp9dnoVVUsMIt@tanuki","subject":"Re: [PATCH 00/11] t: reduce direct disk access to data structures","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-10-18T23:40:35Z","receivedAt":"2023-10-18T23:40:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> As usual, I forgot to mention that this applies on top of current\n> master, which is at a9ecda2788 (The eighteenth batch, 2023-10-13) at the\n> time of writing. I look forward to the day where I remember to include\n> this information in the cover letter...\n\nI heard rumors that we have a --base option that records such a\npiece of information in the format-patch output (I haven't used it\nmyself).  Would it have helped, or perhaps it still needs some\nusability tweak?\n"},{"id":"483477","messageId":"CAFQ2z_Om724+o+EG1FAhC9VrvJECnQ5UA+Z04Rzycpi_mXvMHg@mail.gmail.com","threadId":"60390","inReplyTo":"xmqqbkcwuetq.fsf@gitster.g","subject":"Re: [PATCH 00/11] t: reduce direct disk access to data structures","fromName":"Han-Wen Nienhuys","fromEmail":"hanwen@google.com","sentAt":"2023-10-19T10:13:12Z","receivedAt":"2023-10-19T10:13:29Z","isPatch":true,"sender":{"key":"hanwen@google.com","avatar":"https://avatars.githubusercontent.com/u/31547?v=4"},"body":"On Wed, Oct 18, 2023 at 5:32 PM Junio C Hamano <gitster@pobox.com> wrote:\n> > this patch series refactors a bunch of our tests to perform less direct\n> > disk access to on-disk data structures. Instead, the tests are converted\n> > to use Git tools or our test-tool to access data to the best extent\n> > possible. This serves two benefits:\n>\n> Laudable goal.\n>\n> >     - We increase test coverage of our own code base.\n>\n> Meaning the new code added to test-tool for this series will also\n> get tested and bugs spotted?\n>\n> >     - We become less dependent on the actual on-disk format.\n>\n> Yes, this is very desirable.  Without looking at the implementation,\n> I see some issues aiming for this goal may involve. [a] using the\n> production code for validation would mean our expectation to be\n> compared to the reality to be validated can be affected by the same\n> bug, making two wrongs to appear right; [b] using a separate\n> implementation used only for validation would at least mean we will\n> have to make the same mistake in unique part of both implementations\n> that is less likely to miss bugs compared to [a], but bugs in shared\n> part of the production code and validation code will be hidden the\n> same way as [a].\n\nI think it would be really great if there were separate unittests for\nthe ref backend API. Some of the reftable work was needlessly\ndifficult because the contract of the API was underspecified. The API\nis well compartmentalized in refs-internal.h, and a lot of the API\nbehavior can be tested as a black box, eg.\n\n* setup symref HEAD pointing to R1\n* setup transaction updating ref R1 from C1 to C2\n* commit transaction, check that it succeeds\n* read ref R1, check if it is C2\n* read reflog for R1, see that it has a C1 => C2 update\n* read reflog for HEAD, see that it has a C1 => C2 update\n\nTests for the loose/packed backend could directly mess with the\non-disk files to test failure scenarios.\n\nWith unittests like that, the tests can zoom in on the functionality\nof the ref backend, and provide more convenient coverage for\ndynamic/static analysis.\n\n-- \nHan-Wen Nienhuys - Google Munich\nI work 80%. Don't expect answers from me on Fridays.\n--\nGoogle Germany GmbH, Erika-Mann-Strasse 33, 80636 Munich\nRegistergericht und -nummer: Hamburg, HRB 86891\nSitz der Gesellschaft: Hamburg\nGeschäftsführer: Paul Manicle, Liana Sebastian\n"},{"id":"483507","messageId":"xmqq8r7yxzsz.fsf@gitster.g","threadId":"60390","inReplyTo":"CAFQ2z_Om724+o+EG1FAhC9VrvJECnQ5UA+Z04Rzycpi_mXvMHg@mail.gmail.com","subject":"Re: [PATCH 00/11] t: reduce direct disk access to data structures","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-10-19T17:55:08Z","receivedAt":"2023-10-19T17:55:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Han-Wen Nienhuys <hanwen@google.com> writes:\n\n> I think it would be really great if there were separate unittests for\n> the ref backend API. Some of the reftable work was needlessly\n> difficult because the contract of the API was underspecified. The API\n> is well compartmentalized in refs-internal.h, and a lot of the API\n> behavior can be tested as a black box, eg.\n>\n> * setup symref HEAD pointing to R1\n> * setup transaction updating ref R1 from C1 to C2\n> * commit transaction, check that it succeeds\n> * read ref R1, check if it is C2\n> * read reflog for R1, see that it has a C1 => C2 update\n> * read reflog for HEAD, see that it has a C1 => C2 update\n>\n> Tests for the loose/packed backend could directly mess with the\n> on-disk files to test failure scenarios.\n>\n> With unittests like that, the tests can zoom in on the functionality\n> of the ref backend, and provide more convenient coverage for\n> dynamic/static analysis.\n\nYeah, I agree that something like that would really be great.\n"},{"id":"483597","messageId":"xmqq4jilqdff.fsf@gitster.g","threadId":"60390","inReplyTo":"1ac120368c6cd995841c28bde7542e882ec7b04f.1697607222.git.ps@pks.im","subject":"Re: [PATCH 05/11] t: convert tests to not access symrefs via the filesystem","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-10-20T19:52:36Z","receivedAt":"2023-10-20T19:52:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> @@ -164,9 +164,9 @@ test_expect_success 'rev-parse skips symref pointing to broken name' '\n>  test_expect_success 'for-each-ref emits warnings for broken names' '\n>  \ttest-tool ref-store main update-ref msg \"refs/heads/broken...ref\" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&\n>  \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref\" &&\n> -\tprintf \"ref: refs/heads/broken...ref\\n\" >.git/refs/heads/badname &&\n> +\ttest-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref &&\n\nI am of two minds here.  While it certainly smells nicer because we\ncan test ref backends other than the files backend with this change,\nwe are forcing all ref backends to support creating a symbolic ref\nwith invalid name, because otherwise \"test-tool\" would not be able\nto do this.  Newer more database-oriented ref backends should be\nallowed to implement their file format in which it is imposssible to\nstore such a bad name, but this makes it impossible.\n\nI guess it is OK, because we would introduce some new prerequisite\n(i.e. REF_BACKEND_ALLOWS_BROKEN_REFS) to skip this test on certain\nref backend where making invalid refs is impossible.\n\nOther kind of changes in this patch, e.g., ...\n\n> @@ -315,7 +325,9 @@ test_expect_success 'defaulted HEAD uses remote branch if available' '\n>  \tgit -c init.defaultBranch=branchwithstuff -c protocol.version=2 \\\n>  \t\tclone \"file://$(pwd)/file_unborn_parent\" \\\n>  \t\tfile_unborn_child 2>stderr &&\n> -\tgrep \"refs/heads/branchwithstuff\" file_unborn_child/.git/HEAD &&\n> +\techo \"refs/heads/branchwithstuff\" >expect &&\n> +\tgit -C file_unborn_child symbolic-ref HEAD >actual &&\n> +\ttest_cmp expect actual &&\n>  \ttest_path_is_file file_unborn_child/stuff.t &&\n>  \t! grep \"warning:\" stderr\n>  '\n> diff --git a/t/t9133-git-svn-nested-git-repo.sh b/t/t9133-git-svn-nested-git-repo.sh\n> index d8d536269cf..8ca24670acb 100755\n> --- a/t/t9133-git-svn-nested-git-repo.sh\n> +++ b/t/t9133-git-svn-nested-git-repo.sh\n> @@ -11,7 +11,7 @@ test_expect_success 'setup repo with a git repo inside it' '\n>  \t(\n>  \t\tcd s &&\n>  \t\tgit init &&\n> -\t\ttest -f .git/HEAD &&\n> +\t\tgit symbolic-ref HEAD &&\n>  \t\t> .git/a &&\n>  \t\techo a > a &&\n>  \t\tsvn_cmd add .git a &&\n\n... all looked sensible, though.\n\nThanks.\n"},{"id":"483622","messageId":"xmqqy1fvsh6q.fsf@gitster.g","threadId":"60390","inReplyTo":"eaac658bbfd8259ed9a3cce6ca3c8486d6682e8f.1697607222.git.ps@pks.im","subject":"Re: [PATCH 06/11] t: convert tests to not access reflog via the filesystem","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-10-21T23:13:01Z","receivedAt":"2023-10-21T23:13:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh\n> index 5f505e2f353..b1d2c014132 100755\n> --- a/t/t1400-update-ref.sh\n> +++ b/t/t1400-update-ref.sh\n> @@ -90,7 +90,8 @@ test_expect_success \"deleting current branch adds message to HEAD's log\" '\n>  \tgit symbolic-ref HEAD $m &&\n>  \tgit update-ref -m delete-$m -d $m &&\n>  \ttest_must_fail git show-ref --verify -q $m &&\n> -\tgrep \"delete-$m$\" .git/logs/HEAD\n> +\ttest-tool ref-store main for-each-reflog-ent HEAD >actual &&\n> +\tgrep \"delete-$m$\" actual\n>  '\n>  \n>  test_expect_success \"deleting by HEAD adds message to HEAD's log\" '\n> @@ -99,7 +100,8 @@ test_expect_success \"deleting by HEAD adds message to HEAD's log\" '\n>  \tgit symbolic-ref HEAD $m &&\n>  \tgit update-ref -m delete-by-head -d HEAD &&\n>  \ttest_must_fail git show-ref --verify -q $m &&\n> -\tgrep \"delete-by-head$\" .git/logs/HEAD\n> +\ttest-tool ref-store main for-each-reflog-ent HEAD >actual &&\n> +\tgrep \"delete-by-head$\" actual\n>  '\n\nThese are quite straight-forward.\n\n>  test_expect_success \"verifying $m's log (logged by config)\" '\n> -\ttest_when_finished \"git update-ref -d $m && rm -rf .git/logs actual expect\" &&\n> +\ttest_when_finished \"git update-ref -d $m && git reflog expire --expire=all --all && rm -rf actual expect\" &&\n>  \ttest-tool ref-store main for-each-reflog-ent $m >actual &&\n>  \ttest_cmp actual expect\n>  '\n\nThe approach forces us to assume that \"git reflog expire\" performs\ncorrectly in order to test reflog, but it probably is OK---we'll\nnotice breakages in \"reflog expire\" in other tests, hopefully.\n"},{"id":"483666","messageId":"ZTZfjPDyw1nSP74d@tanuki","threadId":"60390","inReplyTo":"xmqqlebzzeh8.fsf@gitster.g","subject":"Re: [PATCH 00/11] t: reduce direct disk access to data structures","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-23T11:57:00Z","receivedAt":"2023-10-23T11:57:07Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Wed, Oct 18, 2023 at 04:40:35PM -0700, Junio C Hamano wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n> \n> > As usual, I forgot to mention that this applies on top of current\n> > master, which is at a9ecda2788 (The eighteenth batch, 2023-10-13) at the\n> > time of writing. I look forward to the day where I remember to include\n> > this information in the cover letter...\n> \n> I heard rumors that we have a --base option that records such a\n> piece of information in the format-patch output (I haven't used it\n> myself).  Would it have helped, or perhaps it still needs some\n> usability tweak?\n\nI was aware of the option, but somehow never thought to use it. I guess\nothers are in the same boat as this is a semi-frequent topic to come up\non the mailing list.\n\nWhat will fix this for me from now on is that I've now discovered the\n`format.useAutoBase` config. To fix this for everybody else without\nhaving to go through the same process we could change the default to\n`format.useAutoBase=auto`. This can lead to hard-to-understand errors as\npointed out by 7efba5fa39f (format-patch: teach format.useAutoBase\n\"whenAble\" option, 2020-10-01) though:\n\n```\n$ git format-patch -1 <an old commit>\nfatal: base commit shouldn't be in revision list\n```\n\nBut this was fixed via the new \"whenAble\" value for this configuration,\nwhich will cause Git to ignore the case where it's unable to figure out\nthe base commit. So maybe we should consider to make it the new default?\nI cannot think of any real downside. In the best case we simplify the\nlife of many maintainers of mailing list based projects. In the worst\ncase where we are unable to figure out the base commit nothing changes.\n\nPatrick\n"},{"id":"483669","messageId":"ZTZ79DENl6c-1C0q@tanuki","threadId":"60390","inReplyTo":"CAFQ2z_Om724+o+EG1FAhC9VrvJECnQ5UA+Z04Rzycpi_mXvMHg@mail.gmail.com","subject":"Re: [PATCH 00/11] t: reduce direct disk access to data structures","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-23T13:58:12Z","receivedAt":"2023-10-23T13:58:19Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Thu, Oct 19, 2023 at 12:13:12PM +0200, Han-Wen Nienhuys wrote:\n> On Wed, Oct 18, 2023 at 5:32 PM Junio C Hamano <gitster@pobox.com> wrote:\n> > > this patch series refactors a bunch of our tests to perform less direct\n> > > disk access to on-disk data structures. Instead, the tests are converted\n> > > to use Git tools or our test-tool to access data to the best extent\n> > > possible. This serves two benefits:\n> >\n> > Laudable goal.\n> >\n> > >     - We increase test coverage of our own code base.\n> >\n> > Meaning the new code added to test-tool for this series will also\n> > get tested and bugs spotted?\n\nFor now all the helpers of the test-tool only have implicit test\ncoverage, but I get your point. If we decide to instead transform this\ntest tool into production-level code as you suggested (e.g. `git\nrev-parse --exists`) then this becomes less of a discussion point as we\nwould of course have proper test coverage for it.\n\n> > >     - We become less dependent on the actual on-disk format.\n> >\n> > Yes, this is very desirable.  Without looking at the implementation,\n> > I see some issues aiming for this goal may involve. [a] using the\n> > production code for validation would mean our expectation to be\n> > compared to the reality to be validated can be affected by the same\n> > bug, making two wrongs to appear right; [b] using a separate\n> > implementation used only for validation would at least mean we will\n> > have to make the same mistake in unique part of both implementations\n> > that is less likely to miss bugs compared to [a], but bugs in shared\n> > part of the production code and validation code will be hidden the\n> > same way as [a].\n> \n> I think it would be really great if there were separate unittests for\n> the ref backend API. Some of the reftable work was needlessly\n> difficult because the contract of the API was underspecified. The API\n> is well compartmentalized in refs-internal.h, and a lot of the API\n> behavior can be tested as a black box, eg.\n> \n> * setup symref HEAD pointing to R1\n> * setup transaction updating ref R1 from C1 to C2\n> * commit transaction, check that it succeeds\n> * read ref R1, check if it is C2\n> * read reflog for R1, see that it has a C1 => C2 update\n> * read reflog for HEAD, see that it has a C1 => C2 update\n> \n> Tests for the loose/packed backend could directly mess with the\n> on-disk files to test failure scenarios.\n> \n> With unittests like that, the tests can zoom in on the functionality\n> of the ref backend, and provide more convenient coverage for\n> dynamic/static analysis.\n\nAgreed. Ideally, I think our tests should be split up into two layers:\n\n    1. Low-level tests which are backend specific. These allow us to\n       assert the on-disk state of the respective backend thoroughly and\n       also allow us to explicitly test for edge cases that are only of\n       relevance to this specific backend.\n\n       The reftable backend in its current (non-upstream) already has\n       such tests, but we don't really have explicit tests for the files\n       backend. This is a gap that should ideally be filled at some\n       point in time.\n\n    2. Higher-level tests should then be allowed to assume that the\n       underlying logic works as expected. These tests are thus free to\n       use plumbing tools that tie into the reference backends.\n\nPatrick\n"},{"id":"483670","messageId":"ZTZ7-oMqek8kQqhJ@tanuki","threadId":"60390","inReplyTo":"xmqqil73ud5j.fsf@gitster.g","subject":"Re: [PATCH 02/11] t: allow skipping expected object ID in `ref-store update-ref`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-23T13:58:18Z","receivedAt":"2023-10-23T13:58:23Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Wed, Oct 18, 2023 at 09:08:08AM -0700, Junio C Hamano wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n> \n> > We require the caller to pass both the old and new expected object ID to\n> > our `test-tool ref-store update-ref` helper. When trying to update a\n> > symbolic reference though it's impossible to specify the expected object\n> > ID, which means that the test would instead have to force-update the\n> > reference. This is currently impossible though.\n> >\n> > Update the helper to optionally skip verification of the old object ID\n> > in case the test passes in an empty old object ID as input.\n> >\n> > Signed-off-by: Patrick Steinhardt <ps@pks.im>\n> > ---\n> >  t/helper/test-ref-store.c | 11 +++++++----\n> >  1 file changed, 7 insertions(+), 4 deletions(-)\n> \n> Good.\n> \n> Even better would be to make the old one optional, though.\n\nI was also a bit torn when writing this. We could of course make the\nbehaviour conditional on whether `argc` is 4 or 5. But I wasn't quite\nsure how important it is to provide a nice UI for this test helper, and\nwe don't have `argc` readily available. It's not hard to count them\nmanually, but until now I was under the impression that the test helpers\nonly need to be \"good enough\".\n\nPatrick\n\n> > diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\n> > index 7400f560ab6..7dc83137584 100644\n> > --- a/t/helper/test-ref-store.c\n> > +++ b/t/helper/test-ref-store.c\n> > @@ -322,16 +322,19 @@ static int cmd_update_ref(struct ref_store *refs, const char **argv)\n> >  \tconst char *new_sha1_buf = notnull(*argv++, \"new-sha1\");\n> >  \tconst char *old_sha1_buf = notnull(*argv++, \"old-sha1\");\n> >  \tunsigned int flags = arg_flags(*argv++, \"flags\", transaction_flags);\n> > -\tstruct object_id old_oid;\n> > +\tstruct object_id old_oid, *old_oid_ptr = NULL;\n> >  \tstruct object_id new_oid;\n> >  \n> > -\tif (get_oid_hex(old_sha1_buf, &old_oid))\n> > -\t\tdie(\"cannot parse %s as %s\", old_sha1_buf, the_hash_algo->name);\n> > +\tif (*old_sha1_buf) {\n> > +\t\tif (get_oid_hex(old_sha1_buf, &old_oid))\n> > +\t\t\tdie(\"cannot parse %s as %s\", old_sha1_buf, the_hash_algo->name);\n> > +\t\told_oid_ptr = &old_oid;\n> > +\t}\n> >  \tif (get_oid_hex(new_sha1_buf, &new_oid))\n> >  \t\tdie(\"cannot parse %s as %s\", new_sha1_buf, the_hash_algo->name);\n> >  \n> >  \treturn refs_update_ref(refs, msg, refname,\n> > -\t\t\t       &new_oid, &old_oid,\n> > +\t\t\t       &new_oid, old_oid_ptr,\n> >  \t\t\t       flags, UPDATE_REFS_DIE_ON_ERR);\n> >  }\n> \n"},{"id":"483671","messageId":"ZTZ8ATohRe7GVu5D@tanuki","threadId":"60390","inReplyTo":"xmqq1qdru6ds.fsf@gitster.g","subject":"Re: [PATCH 04/11] t: convert tests to not write references via the filesystem","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-23T13:58:25Z","receivedAt":"2023-10-23T13:58:29Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Wed, Oct 18, 2023 at 11:34:23AM -0700, Junio C Hamano wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n> \n> > -test_expect_success \"fail to create $n\" '\n> > -\ttest_when_finished \"rm -f .git/$n_dir\" &&\n> > -\ttouch .git/$n_dir &&\n> > -\ttest_must_fail git update-ref $n $A\n> > +test_expect_success \"fail to create $n due to file/directory conflict\" '\n> > +\ttest_when_finished \"git update-ref -d refs/heads/gu\" &&\n> > +\tgit update-ref refs/heads/gu $A &&\n> > +\ttest_must_fail git update-ref refs/heads/gu/fixes $A\n> >  '\n> \n> OK, the original checks \"if a random garbage file, which may not\n> necessarily be a ref, exists at $n_dir, we cannot create a ref at\n> $n_dir/fixes, due to D/F conflict\" more directly, but as long as our\n> intention is to enforce the D/F restriction across different ref\n> backends [*], creating a ref at $n_dir and making sure $n_dir/fixes\n> cannot be created is an equivalent check that is better (because it\n> can be applied for other backends).\n> \n>     Side note: there is no fundamental need to, though, and there\n>          are cases where being able to have the 'seen' branch and\n>          'seen/ps/ref-test-tools' branches at the same time is\n>          beneficial---packed-refs and ref-table backends would not\n>          have such an inherent limitation, but they can of course be\n>          castrated to match what files-backend can(not) do.\n\nI think initially it is beneficial to keep any such restriction and cut\nback new backends to match them, even though it's more work. The main\nreason why I think this makes sense is that hosting providers are the\nmost likely to update to the newer backend fast. It would be detrimental\nto the users though if hosting providers that converted to the newer\nbackend were able to store such references that would be conflicting\nwith the files backend because they wouldn't be able to clone such\nrepositories anymore. And this isn't only true for hosting providers,\nbut essentially whenever two repositories that use different backends\ntry to interact with each other.\n\nSo even though I wish for a future where we don't need to care for D/F\nconflicts anymore, I think the time isn't ripe for it and we should for\nnow aim for matching behaviour.\n\n> > @@ -222,7 +220,7 @@ test_expect_success 'delete symref without dereference when the referred ref is\n> >  \n> >  test_expect_success 'update-ref -d is not confused by self-reference' '\n> >  \tgit symbolic-ref refs/heads/self refs/heads/self &&\n> > -\ttest_when_finished \"rm -f .git/refs/heads/self\" &&\n> > +\ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self\" &&\n> >  \ttest_path_is_file .git/refs/heads/self &&\n> \n> I trust that this will be corrected to use some wrapper around \"git\n> symbolic-ref\" (or an equivalent for it as a test-tool subcommand) in\n> some future patch, if not in this series?\n\nYup, this is getting fixed in a subsequent patch. I had two different\noptions to structure this series:\n\n    - Either by test file so that questions like this don't come up when\n      a reviewer sees in the context that there are still tests that\n      exercise the filesystem directly. This was my first approach.\n\n    - The alternative is to write it such that we address common\n      patterns globally, which I then rewrote my first version to.\n\nThere were two reasons why I didn't like the first iteration:\n\n    - Commit messages would basically have to reexplain the exact same\n      thing for every single testcase as the motivation is the same\n      everywhere.\n\n    - I think it's easier to review when you only see the same kind of\n      transformation per patch.\n\nBut yes, it does have the downside that the reader is now left wondering\nwhy the other call to `test_path_is_file` still exists.\n\nPatrick\n\n> >  \ttest_must_fail git update-ref -d refs/heads/self &&\n> >  \ttest_path_is_file .git/refs/heads/self\n> \n> Likewise.\n> \n> > @@ -230,7 +228,7 @@ test_expect_success 'update-ref -d is not confused by self-reference' '\n> >  \n> >  test_expect_success 'update-ref --no-deref -d can delete self-reference' '\n> >  \tgit symbolic-ref refs/heads/self refs/heads/self &&\n> > -\ttest_when_finished \"rm -f .git/refs/heads/self\" &&\n> > +\ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self\" &&\n> >  \ttest_path_is_file .git/refs/heads/self &&\n> >  \tgit update-ref --no-deref -d refs/heads/self &&\n> >  \ttest_must_fail git show-ref --verify -q refs/heads/self\n> \n> We already have the \"ref is missing\" test here.\n> \n> I'll stop at this point for now; will hopefully continue in a\n> separate message later.  Thanks.\n"},{"id":"483672","messageId":"ZTZ8BpK-PV5fdYIT@tanuki","threadId":"60390","inReplyTo":"xmqqfs27r5ng.fsf@gitster.g","subject":"Re: [PATCH 04/11] t: convert tests to not write references via the filesystem","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-23T13:58:30Z","receivedAt":"2023-10-23T13:58:35Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Wed, Oct 18, 2023 at 02:18:27PM -0700, Junio C Hamano wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n> \n> > @@ -434,7 +432,7 @@ test_expect_success 'Query \"main@{2005-05-28}\" (past end of history)' '\n> >  \ttest_i18ngrep -F \"warning: log for ref $m unexpectedly ended on $ld\" e\n> >  '\n> >  \n> > -rm -f .git/$m .git/logs/$m expect\n> > +git update-ref -d $m\n> \n> We are not clearing \"expect\" file.  I do not know if it matters\n> here, but I am only recording what I noticed.\n\nOops, will fix.\n\n> > diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\n> > index 10a539158c4..5cce24f1006 100755\n> > --- a/t/t1450-fsck.sh\n> > +++ b/t/t1450-fsck.sh\n> > @@ -115,15 +115,16 @@ test_expect_success 'zlib corrupt loose object output ' '\n> >  '\n> >  \n> >  test_expect_success 'branch pointing to non-commit' '\n> > -\tgit rev-parse HEAD^{tree} >.git/refs/heads/invalid &&\n> > +\ttree_oid=$(git rev-parse --verify HEAD^{tree}) &&\n> > +\ttest-tool ref-store main update-ref msg refs/heads/invalid $tree_oid $ZERO_OID REF_SKIP_OID_VERIFICATION &&\n> \n> I have mixed feelings on this.\n> \n> In olden days, plumbing commands tended to allow to pass anything\n> the user told them to use, but in more recent versions of Git, we,\n> probably by mistake, managed to butcher some of the plumbing\n> commands to make them unable to deliberately \"break\" repositories,\n> one victim being \"update-ref\", i.e.\n> \n>     $ git update-ref refs/heads/invalid HEAD^{tree}\n> \n> is rejected these days (I just checked with v1.3.0 and it allows me\n> to do this), and that is one of the reasons why we manually broke\n> the repository in these tests.\n\nMy first try was indeed to use git-update-ref(1) to update the ref to\nthe tree object, and I was surprised to learn that it would not let me\ndo so.\n\n> We need to have a warning message in\n> comments near the implementation of \"ref-store update-ref\" that says\n> never ever attempt to share code with the production version of\n> update-ref---otherwise this (or the \"safety\" given to the plumbing\n> command, possibly by mistake) will be broken, depending on which\n> direction such a sharing goes.  On the other hand, forcing us to\n> keep two separate implementations, one deliberately loose to allow\n> us corrupt repositories, the other for production and actively used,\n> would mean the former one that is only used for validation would risk\n> bitrotting.\n\nWouldn't any eventual bitrot be contained by the tests though? As our\nuse of the test helper grows via this patch series its behaviour will\nalso be verified more thoroughly.\n\n> >  \ttest_when_finished \"git update-ref -d refs/heads/invalid\" &&\n> \n> Not a problem this patch introduces, but I think it is a better\n> discipline to have when_finished clean-up routine prepared before we\n> do actual damage, i.e. if I were writing this test today from scratch,\n> I would expect it to be before \"git rev-parse >.git/refs/heads/invalid\"\n> is done.\n\nI'll fix this while at it.\n\n> >  \ttest_must_fail git fsck 2>out &&\n> >  \ttest_i18ngrep \"not a commit\" out\n> >  '\n> \n> A #leftoverbit that is not relevant to the topic; we should clean\n> these test_i18ngrep and replace them with a plain \"grep\".\n> \n> >  test_expect_success 'HEAD link pointing at a funny object' '\n> > -\ttest_when_finished \"mv .git/SAVED_HEAD .git/HEAD\" &&\n> > -\tmv .git/HEAD .git/SAVED_HEAD &&\n> > +\tsaved_head=$(git rev-parse --verify HEAD) &&\n> > +\ttest_when_finished \"git update-ref HEAD ${saved_head}\" &&\n> >  \techo $ZERO_OID >.git/HEAD &&\n> \n> Are you sure .git/HEAD when this test is entered is a detached HEAD?\n> The title of the test says \"HEAD link\", and I take it to mean HEAD\n> is a symlink, and we save it away, while we create a loose ref that\n> points at 0{40} in a detached HEAD state.  Actually, the original\n> would also work if HEAD is detached on entry.  In either case,\n> moving SAVED_HEAD back to HEAD would restore the original state.\n> \n> But the updated one only works if HEAD upon entry is already\n> detached.  Is this intended?\n\nYes and no -- it's a reflection of the state when this test runs. One\nproblem in this test suite here is that many of the tests' states are\nheavily interwoven with each other, which only makes this harder to\nrefactor without making any assumptions.\n\nWell. Instead of restoring to whatever the state was previous to the\ntest we could also restore it to something sane-ish like \"master\". That\nof course breaks other tests though... I'll investigate.\n\n> > @@ -131,8 +132,8 @@ test_expect_success 'HEAD link pointing at a funny object' '\n> >  '\n> >  \n> >  test_expect_success 'HEAD link pointing at a funny place' '\n> > -\ttest_when_finished \"mv .git/SAVED_HEAD .git/HEAD\" &&\n> > -\tmv .git/HEAD .git/SAVED_HEAD &&\n> > +\tsaved_head=$(git rev-parse --verify HEAD) &&\n> > +\ttest_when_finished \"git update-ref --no-deref HEAD ${saved_head}\" &&\n> \n> Likewise.  Use of \"update-ref\" in the previous one vs \"update-ref\n> --no-deref\" in this one to recover from the damage the tests make\n> makes me feel that we may be assuming too much.\n> \n> >  \techo \"ref: refs/funny/place\" >.git/HEAD &&\n> \n> Even though \"git symbolic-ref\" refuses to point HEAD outside refs/,\n> as plumbing command should, it allows it to point it outside refs/heads/.\n> so this line should probably become\n> \n> \tgit symbolic-ref HEAD refs/funny/place\n> \n> in the same spirit as the rest of the series.\n\nYup, this will be adressed in a later patch.\n\nPatrick\n\n> > @@ -391,7 +393,7 @@ test_expect_success 'tag pointing to nonexistent' '\n> >  \n> >  \ttag=$(git hash-object -t tag -w --stdin <invalid-tag) &&\n> >  \ttest_when_finished \"remove_object $tag\" &&\n> > -\techo $tag >.git/refs/tags/invalid &&\n> > +\tgit update-ref refs/tags/invalid $tag &&\n> \n> Good (not just this one, but similar ones throughout this patch).\n> \n> \n"},{"id":"483673","messageId":"ZTZ8C08IrhpggfiY@tanuki","threadId":"60390","inReplyTo":"CAFQ2z_MiOazk8qLvEn5xfHyMwY=5pa6rEJD3vC3+rnHz4AeQ2Q@mail.gmail.com","subject":"Re: [PATCH 08/11] t4207: delete replace references via git-update-ref(1)","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-23T13:58:35Z","receivedAt":"2023-10-23T13:58:40Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Wed, Oct 18, 2023 at 03:27:11PM +0200, Han-Wen Nienhuys wrote:\n> On Wed, Oct 18, 2023 at 7:35 AM Patrick Steinhardt <ps@pks.im> wrote:\n> >\n> > In t4207 we set up a set of replace objects via git-replace(1). Because\n> > these references should not be impacting subsequent tests we also set up\n> > some cleanup logic that deletes the replacement references via a call to\n> > `rm -rf`. This reaches into the internal implementation details of the\n> > reference backend and will thus break when we grow an alternative refdb\n> > implementation.\n> >\n> > Refactor the tests to delete the replacement refs via Git commands so\n> > that we become independent of the actual refdb that's in use. As we\n> > don't have a nice way to delete all replacements or all references in a\n> > certain namespace, we opt for a combination of git-for-each-ref(1) and\n> > git-update-ref(1)'s `--stdin` mode.\n> \n> There is a test helper that can directly access the ref database,\n> t/helper/test-ref-store.c.\n> \n> If you use that manipulate refs for testing purposes, you make the\n> test independent of behavior git-for-each-ref/git-update-ref, which is\n> what you want for testing replace-objects?\n\nIs there any specific reason why we shouldn't be using\ngit-for-each-ref(1) or git-update-ref(1) here? Neither of those commands\nare part of the system under test, as we rather care about git-log(1)\nhere. So as those commands are already being verified in other tests I\nthink it should be fine to assume that they work as intended here.\n\nHappy to hear differing viewpoints though.\n\nPatrick\n"},{"id":"483674","messageId":"ZTZ8EfCgilMP_NCp@tanuki","threadId":"60390","inReplyTo":"CAPig+cTueWuiWiw9_vou0Ud71utVMRkk5tR==tfkkMXBj9qWxA@mail.gmail.com","subject":"Re: [PATCH 01/11] t: add helpers to test for reference existence","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-23T13:58:41Z","receivedAt":"2023-10-23T13:58:47Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Wed, Oct 18, 2023 at 01:08:45PM -0400, Eric Sunshine wrote:\n> On Wed, Oct 18, 2023 at 1:35 AM Patrick Steinhardt <ps@pks.im> wrote:\n> > Introduce a new subcommand for our ref-store test helper that explicitly\n> > checks only for the presence or absence of a reference. This addresses\n> > these limitations:\n> > [...]\n> > Signed-off-by: Patrick Steinhardt <ps@pks.im>\n> > ---\n> > diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\n> > @@ -221,6 +221,30 @@ static int cmd_verify_ref(struct ref_store *refs, const char **argv)\n> > +static int cmd_ref_exists(struct ref_store *refs, const char **argv)\n> > +{\n> > +       const char *refname = notnull(*argv++, \"refname\");\n> > +       struct strbuf unused_referent = STRBUF_INIT;\n> > +       struct object_id unused_oid;\n> > +       unsigned int unused_type;\n> > +       int failure_errno;\n> > +\n> > +       if (refs_read_raw_ref(refs, refname, &unused_oid, &unused_referent,\n> > +                             &unused_type, &failure_errno)) {\n> > +               /*\n> > +                * We handle ENOENT separately here such that it is possible to\n> > +                * distinguish actually-missing references from any kind of\n> > +                * generic error.\n> > +                */\n> > +               if (failure_errno == ENOENT)\n> > +                       return 17;\n> > +               return -1;\n> > +       }\n> > +\n> > +       strbuf_release(&unused_referent);\n> > +       return 0;\n> > +}\n> \n> Unless refs_read_raw_ref() guarantees that `unused_referent` remains\n> unallocated upon failure[*], then the early returns inside the\n> conditional leak the strbuf. True, the program is exiting immediately\n> anyhow, so this (potential) leak isn't significant, but it seems odd\n> to clean up in one case (return 0) but not in the others (return -1 &\n> 17).\n> \n> [*] In my (admittedly brief) scan of the code and documentation, I\n> didn't see any such promise.\n\nAgreed, let's just be thorough and plug any potential memor leak here.\n\nPatrick\n"},{"id":"483675","messageId":"ZTZ8Fm1Gk7Rxn6Yu@tanuki","threadId":"60390","inReplyTo":"xmqqmswfud7p.fsf@gitster.g","subject":"Re: [PATCH 01/11] t: add helpers to test for reference existence","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-23T13:58:46Z","receivedAt":"2023-10-23T13:58:52Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Wed, Oct 18, 2023 at 09:06:50AM -0700, Junio C Hamano wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n> \n> > There are two major ways to check for the existence of a reference in\n> > our tests:\n> >\n> >     - `git rev-parse --verify` can be used to check for existence of a\n> >       reference. This only works in the case where the reference is well\n> >       formed though and resolves to an actual object ID. This does not\n> >       work with malformed reference names or invalid contents.\n> >\n> >     - `test_path_is_file` can be used to check for existence of a loose\n> >       reference if it is known to not resolve to an actual object ID. It\n> >       by necessity reaches into implementation details of the reference\n> >       backend though.\n> \n> True.  It would be ideal if we can limit the use of latter when we\n> _care_ how the ref is stored (e.g., \"we expect it to be stored as a\n> loose ref, not packed\").  \"The ref R at must be pointing at the\n> commit X\" is better asserted by using the former (or \"git show-ref\")\n> as we not just only want to see the .git/refs/R file holding the\n> object name X, but also want to see the production git tools observe\n> the same---if what rev-parse or show-ref observes is different from\n> the expected state and they say ref R does not point at commit X, we\n> should complain (rev-parse or show-ref may be broken in the version\n> of Git being tested, but we can assume that their breakage will be\n> caught elsewhere in the test suite as well, so as long as we trust\n> them, using them as the validator is better than going into the\n> implementation detail and assuming things like \"new refs always\n> appear as a loose ref\" that we might want to change in the future).\n> \n> > Similarly, there are two equivalent ways to check for the absence of a\n> > reference:\n> >\n> >     - `test_must_fail git rev-parse` can be used to check for the\n> >       absence of a reference. It could fail due to a number of reasons\n> >       though, and all of these reasons will be thrown into the same bag\n> >       as an absent reference.\n> >\n> >     - `test_path_is_missing` can be used to check explicitly for the\n> >       absence of a loose reference, but again reaches into internal\n> >       implementation details of the reference backend.\n> >\n> > So both our tooling to check for the presence and for the absence of\n> > references in tests is lacking as either failure cases are thrown into\n> > the same bag or we need to reach into internal implementation details of\n> > the respective reference backend.\n> \n> > Introduce a new subcommand for our ref-store test helper that explicitly\n> > checks only for the presence or absence of a reference. This addresses\n> > these limitations:\n> >\n> >     - We can check for the presence of references with malformed names.\n> \n> But for the purpose of tests, we can control the input.  When we\n> perform an operation that we expect a ref R to be created, we would\n> know R is well formed and we can validate using a tool that we know\n> would be broken when fed a malformed name.  So I do not see this as\n> a huge \"limitation\".\n\nThis is explicitly about the case where such a ref R is not well-formed\nthough. This limitation was mostly a problem in t1430-bad-ref-name.sh,\nwhich verifies many such scenarios.\n\n> >     - We can check for the presence of references that don't resolve.\n> \n> Do you mean a dangling symbolic ref?  We are using a wrong tool if\n> you are using rev-parse for that, aren't we?  Isn't symbolic-ref\n> there for us for this exact use case?  That is\n\nAgain, t1430-bad-ref-name.sh has been the inspiration for this:\n\n```\n$ git symbolic-ref refs/heads/bad...name refs/heads/master\n$ git symbolic-ref refs/heads/bad...name\nfatal: No such ref: refs/heads/bad...name\n```\n\nThe mismatch that you can write but not read the reference is kind of\nastonishing though. We could fix this limitation, but I think there were\nmore usecases than only bad reference names. I honestly can't quite\nremember right now.\n\n> >     - We can explicitly handle the case where a reference is missing by\n> >       special-casing ENOENT errors.\n> \n> You probably know the error conditions refs_read_raw_ref() can be\n> broken better than I do, but this feels a bit too intimate with how\n> the method for the files backend happens to be implemented, which at\n> the same time, can risk that [a] other backends can implement their\n> \"ref does not resolve to an object name---is it because it is\n> missing?\" report incorrectly and [b] we would eventually want to\n> know error conditions other than \"the ref requested is missing\" and\n> at that point we would need more \"special casing\", which does not\n> smell easy to scale.\n\nWe actually rely on some of these error codes to be consistent across\nbackends. E.g. \"refs.c\" itself has higher-level logic that verifies\nspecific error codes when resolving symrefs. And as we explicitly made\nthese error codes part of the API design with `refs_read_raw_ref()` my\nassumption is that any other backend needs to match the behaviour here.\n\nI also think that this is a somewhat sane assumption to make. While it\nmay not be a good idea to tie this to standard error codes, the backend\nshould indeed be able to signal specific error cases to the caller. We\ncould refactor this to be more explicit about the expected failure cases\nin the form of specialized error codes. But I'm not sure whether that\nwould be worth it for now, but it's sure something to keep in mind for\nfuture patch series.\n\n> >     - We don't need to reach into implementation details of the backend,\n> >       which would allow us to use this helper for the future reftable\n> >       backend.\n> \n> This is exactly what we want to aim for.\n> \n> > Next to this subcommand we also provide two wrappers `test_ref_exists`\n> > and `test_ref_missing` that make the helper easier to use.\n> \n> Hmmmm.  This may introduce \"who watches the watchers\" problem, no?\n> I briefly wondered if a better approach is to teach the production\n> code, e.g., rev-parse, to optionally give more detailed diag.  It\n> essentially may be the same (making the code in test-ref-store.c\n> added by this patch available from rev-parse, we would easily get\n> there), so I do not think the distinction matters.\n> \n> > diff --git a/t/README b/t/README\n> > index 61080859899..779f7e7dd86 100644\n> > --- a/t/README\n> > +++ b/t/README\n> > @@ -928,6 +928,15 @@ see test-lib-functions.sh for the full list and their options.\n> >     committer times to defined state.  Subsequent calls will\n> >     advance the times by a fixed amount.\n> >  \n> > + - test_ref_exists <ref>, test_ref_missing <ref>\n> > +\n> > +   Check whether a reference exists or is missing. In contrast to\n> > +   git-rev-parse(1), these helpers also work with invalid reference\n> > +   names and references whose contents are unresolvable. The latter\n> > +   function also distinguishes generic errors from the case where a\n> > +   reference explicitly doesn't exist and is thus safer to use than\n> > +   `test_must_fail git rev-parse`.\n> > +\n> >   - test_commit <message> [<filename> [<contents>]]\n> >  \n> >     Creates a commit with the given message, committing the given\n> > diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\n> > index 48552e6a9e0..7400f560ab6 100644\n> > --- a/t/helper/test-ref-store.c\n> > +++ b/t/helper/test-ref-store.c\n> > @@ -1,6 +1,6 @@\n> >  #include \"test-tool.h\"\n> >  #include \"hex.h\"\n> > -#include \"refs.h\"\n> > +#include \"refs/refs-internal.h\"\n> >  #include \"setup.h\"\n> >  #include \"worktree.h\"\n> >  #include \"object-store-ll.h\"\n> > @@ -221,6 +221,30 @@ static int cmd_verify_ref(struct ref_store *refs, const char **argv)\n> >  \treturn ret;\n> >  }\n> >  \n> > +static int cmd_ref_exists(struct ref_store *refs, const char **argv)\n> > +{\n> > +\tconst char *refname = notnull(*argv++, \"refname\");\n> > +\tstruct strbuf unused_referent = STRBUF_INIT;\n> > +\tstruct object_id unused_oid;\n> > +\tunsigned int unused_type;\n> > +\tint failure_errno;\n> > +\n> > +\tif (refs_read_raw_ref(refs, refname, &unused_oid, &unused_referent,\n> > +\t\t\t      &unused_type, &failure_errno)) {\n> > +\t\t/*\n> > +\t\t * We handle ENOENT separately here such that it is possible to\n> > +\t\t * distinguish actually-missing references from any kind of\n> > +\t\t * generic error.\n> > +\t\t */\n> > +\t\tif (failure_errno == ENOENT)\n> > +\t\t\treturn 17;\n> \n> Can we tell between the cases where the ref itself is missing, and\n> the requested ref is symbolic and points at a missing ref?  This\n> particular case might be OK, but there may other cases where this\n> \"special case\" may not be narrow enough.\n\nYes, because `refs_read_raw_ref()` doesn't concern itself with recursive\nresolving of the reference, which is done at a higher level. It only\nreads and parses the reference without caring whether their target\nactually exists.\n\n> As long we are going to spend cycles to refine the classification of\n> error conditions, which is a very good thing to aim for the reason\n> described in the proposed log message, namely \"rev-parse can fail\n> for reasons other than the ref being absent\", I have to wonder again\n> that the fruit of such an effort should become available in the\n> production code, instead of being kept only in test-tool.\n\nFair enough, I'm happy to lift this up into production code. I just\ndidn't think this would be all that useful in general, but I can see\nthat somebody might want to use such functionality as part of our\nplumbing interfaces.\n\nI wonder what the best spot would be for it. Should we add a new\n`--exists` switch to git-rev-parse(1)?\n\nPatrick\n"},{"id":"483685","messageId":"ZTaiYEyhKT/yZwHZ@nand.local","threadId":"60390","inReplyTo":"c4d09e3e5dbd11221cc4d229b815434d441cdb4d.1697607222.git.ps@pks.im","subject":"Re: [PATCH 08/11] t4207: delete replace references via git-update-ref(1)","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2023-10-23T16:42:08Z","receivedAt":"2023-10-23T16:42:16Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Wed, Oct 18, 2023 at 07:35:37AM +0200, Patrick Steinhardt wrote:\n> In t4207 we set up a set of replace objects via git-replace(1). Because\n> these references should not be impacting subsequent tests we also set up\n> some cleanup logic that deletes the replacement references via a call to\n> `rm -rf`. This reaches into the internal implementation details of the\n> reference backend and will thus break when we grow an alternative refdb\n> implementation.\n>\n> Refactor the tests to delete the replacement refs via Git commands so\n> that we become independent of the actual refdb that's in use. As we\n> don't have a nice way to delete all replacements or all references in a\n> certain namespace, we opt for a combination of git-for-each-ref(1) and\n> git-update-ref(1)'s `--stdin` mode.\n>\n> Signed-off-by: Patrick Steinhardt <ps@pks.im>\n> ---\n>  t/t4207-log-decoration-colors.sh | 4 ++--\n>  1 file changed, 2 insertions(+), 2 deletions(-)\n>\n> diff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh\n> index 21986a866df..d138e513a04 100755\n> --- a/t/t4207-log-decoration-colors.sh\n> +++ b/t/t4207-log-decoration-colors.sh\n> @@ -71,7 +71,7 @@ ${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_commit})${c_reset} A\n>  '\n>\n>  test_expect_success 'test coloring with replace-objects' '\n> -\ttest_when_finished rm -rf .git/refs/replace* &&\n> +\ttest_when_finished \"git for-each-ref refs/replace*/** --format=${SQ}delete %(refname)${SQ} | git update-ref --stdin\" &&\n\nHere and below, should we avoid the for-each-ref showing up on the\nleft-hand side of the pipe? I'd think we want something closer to:\n\n    test_when_finished \"git for-each-ref refs/replace*/** --format=${SQ}delete %(refname)${SQ} >in && git update-ref --stdin <in\" &&\n\nBut having to quote the --format argument with \"${SQ}\"s makes the whole\nthing a little awkward to read and parse.\n\nDo you think that something like the below would be a readability\nimprovement?\n\ndiff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh\nindex d138e513a0..de8f6638cb 100755\n--- a/t/t4207-log-decoration-colors.sh\n+++ b/t/t4207-log-decoration-colors.sh\n@@ -70,8 +70,13 @@ ${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_commit})${c_reset} A\n \tcmp_filtered_decorations\n '\n\n--- >8 ---\n+remove_replace_refs () {\n+\tgit for-each-ref 'refs/replace*/**' --format='delete %(refname)' >in &&\n+\tgit update-ref --stdin <in\n+}\n+\n test_expect_success 'test coloring with replace-objects' '\n-\ttest_when_finished \"git for-each-ref refs/replace*/** --format=${SQ}delete %(refname)${SQ} | git update-ref --stdin\" &&\n+\ttest_when_finished remove_replace_refs &&\n \ttest_commit C &&\n \ttest_commit D &&\n\n@@ -99,7 +104,7 @@ EOF\n '\n\n test_expect_success 'test coloring with grafted commit' '\n-\ttest_when_finished \"git for-each-ref refs/replace*/** --format=${SQ}delete %(refname)${SQ} | git update-ref --stdin\" &&\n+\ttest_when_finished remove_replace_refs &&\n\n \tgit replace --graft HEAD HEAD~2 &&\n--- 8< ---\n\nThanks,\nTaylor\n"},{"id":"483709","messageId":"xmqq7cndnopl.fsf@gitster.g","threadId":"60390","inReplyTo":"ZTZ7-oMqek8kQqhJ@tanuki","subject":"Re: [PATCH 02/11] t: allow skipping expected object ID in `ref-store update-ref`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-10-23T19:06:14Z","receivedAt":"2023-10-23T19:06:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n>> Good.\n>> \n>> Even better would be to make the old one optional, though.\n>\n> I was also a bit torn when writing this. We could of course make the\n> behaviour conditional on whether `argc` is 4 or 5. But I wasn't quite\n> sure how important it is to provide a nice UI for this test helper, and\n> we don't have `argc` readily available. It's not hard to count them\n> manually, but until now I was under the impression that the test helpers\n> only need to be \"good enough\".\n\nYup, good enough would probably be good enough in this case, I agree\n;-)\n"},{"id":"483710","messageId":"xmqq1qdlnoi3.fsf@gitster.g","threadId":"60390","inReplyTo":"ZTZ8ATohRe7GVu5D@tanuki","subject":"Re: [PATCH 04/11] t: convert tests to not write references via the filesystem","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-10-23T19:10:44Z","receivedAt":"2023-10-23T19:10:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n>> OK, the original checks \"if a random garbage file, which may not\n>> necessarily be a ref, exists at $n_dir, we cannot create a ref at\n>> $n_dir/fixes, due to D/F conflict\" more directly, but as long as our\n>> intention is to enforce the D/F restriction across different ref\n>> backends [*], creating a ref at $n_dir and making sure $n_dir/fixes\n>> cannot be created is an equivalent check that is better (because it\n>> can be applied for other backends).\n>> \n>>     Side note: there is no fundamental need to, though, and there\n>>          are cases where being able to have the 'seen' branch and\n>>          'seen/ps/ref-test-tools' branches at the same time is\n>>          beneficial---packed-refs and ref-table backends would not\n>>          have such an inherent limitation, but they can of course be\n>>          castrated to match what files-backend can(not) do.\n>\n> I think initially it is beneficial to keep any such restriction and cut\n> back new backends to match them, even though it's more work.\n\nNote that the same thing can be said for \"Can I have Main and main\nbranches?\".  Loose refs on systems with case-sensitive filesystem\nare not penalized, though.\n\nIn any case, I think we are in agreement.\n\n>> I trust that this will be corrected to use some wrapper around \"git\n>> symbolic-ref\" (or an equivalent for it as a test-tool subcommand) in\n>> some future patch, if not in this series?\n>\n> Yup, this is getting fixed in a subsequent patch. I had two different\n> options to structure this series:\n> ...\n> There were two reasons why I didn't like the first iteration:\n\nYup.  I tend to agree with the choice and criteria you made and used\nhere.\n"},{"id":"483758","messageId":"ZTdnWoTaAMuVMc7v@tanuki","threadId":"60390","inReplyTo":"ZTaiYEyhKT/yZwHZ@nand.local","subject":"Re: [PATCH 08/11] t4207: delete replace references via git-update-ref(1)","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-24T06:42:34Z","receivedAt":"2023-10-24T06:42:40Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Mon, Oct 23, 2023 at 12:42:08PM -0400, Taylor Blau wrote:\n> On Wed, Oct 18, 2023 at 07:35:37AM +0200, Patrick Steinhardt wrote:\n> > In t4207 we set up a set of replace objects via git-replace(1). Because\n> > these references should not be impacting subsequent tests we also set up\n> > some cleanup logic that deletes the replacement references via a call to\n> > `rm -rf`. This reaches into the internal implementation details of the\n> > reference backend and will thus break when we grow an alternative refdb\n> > implementation.\n> >\n> > Refactor the tests to delete the replacement refs via Git commands so\n> > that we become independent of the actual refdb that's in use. As we\n> > don't have a nice way to delete all replacements or all references in a\n> > certain namespace, we opt for a combination of git-for-each-ref(1) and\n> > git-update-ref(1)'s `--stdin` mode.\n> >\n> > Signed-off-by: Patrick Steinhardt <ps@pks.im>\n> > ---\n> >  t/t4207-log-decoration-colors.sh | 4 ++--\n> >  1 file changed, 2 insertions(+), 2 deletions(-)\n> >\n> > diff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh\n> > index 21986a866df..d138e513a04 100755\n> > --- a/t/t4207-log-decoration-colors.sh\n> > +++ b/t/t4207-log-decoration-colors.sh\n> > @@ -71,7 +71,7 @@ ${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_commit})${c_reset} A\n> >  '\n> >\n> >  test_expect_success 'test coloring with replace-objects' '\n> > -\ttest_when_finished rm -rf .git/refs/replace* &&\n> > +\ttest_when_finished \"git for-each-ref refs/replace*/** --format=${SQ}delete %(refname)${SQ} | git update-ref --stdin\" &&\n> \n> Here and below, should we avoid the for-each-ref showing up on the\n> left-hand side of the pipe? I'd think we want something closer to:\n> \n>     test_when_finished \"git for-each-ref refs/replace*/** --format=${SQ}delete %(refname)${SQ} >in && git update-ref --stdin <in\" &&\n> \n> But having to quote the --format argument with \"${SQ}\"s makes the whole\n> thing a little awkward to read and parse.\n> \n> Do you think that something like the below would be a readability\n> improvement?\n\nYes, this certainly looks like a good improvement to me, thanks!\n\nPatrick\n\n> diff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh\n> index d138e513a0..de8f6638cb 100755\n> --- a/t/t4207-log-decoration-colors.sh\n> +++ b/t/t4207-log-decoration-colors.sh\n> @@ -70,8 +70,13 @@ ${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_commit})${c_reset} A\n>  \tcmp_filtered_decorations\n>  '\n> \n> --- >8 ---\n> +remove_replace_refs () {\n> +\tgit for-each-ref 'refs/replace*/**' --format='delete %(refname)' >in &&\n> +\tgit update-ref --stdin <in\n> +}\n> +\n>  test_expect_success 'test coloring with replace-objects' '\n> -\ttest_when_finished \"git for-each-ref refs/replace*/** --format=${SQ}delete %(refname)${SQ} | git update-ref --stdin\" &&\n> +\ttest_when_finished remove_replace_refs &&\n>  \ttest_commit C &&\n>  \ttest_commit D &&\n> \n> @@ -99,7 +104,7 @@ EOF\n>  '\n> \n>  test_expect_success 'test coloring with grafted commit' '\n> -\ttest_when_finished \"git for-each-ref refs/replace*/** --format=${SQ}delete %(refname)${SQ} | git update-ref --stdin\" &&\n> +\ttest_when_finished remove_replace_refs &&\n> \n>  \tgit replace --graft HEAD HEAD~2 &&\n> --- 8< ---\n> \n> Thanks,\n> Taylor\n"},{"id":"483786","messageId":"cover.1698156169.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1697607222.git.ps@pks.im","subject":"[PATCH v2 0/9] t: reduce direct disk access to data structures","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-24T14:04:49Z","receivedAt":"2023-10-24T14:04:59Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Hi,\n\nthis is the second version of my patch series that aims to reduce access\nto on-disk data structures in favor of using plumbing tools where\npossible.\n\nChanges compared to v1:\n\n    - Patches 1, 3: I've dropped these patches that introduced and\n      started to use the test helper for reference existence. This has\n      been split out into a separate patch series now that instead\n      implements the logic as part of git-show-ref(1), see [1].\n\n    - Patch 4: I've made it more explicit that tests in t1450 are all\n      ran in detached HEAD mode via a new `orig_head` variable that is\n      set in the test setup. This variable is later used to reset HEAD\n      back to that original state.\n\n    - Patch 4, 5: I've reordered some of the logic such that we schedule\n      `test_when_finished` before doing the actual mutation of the repo.\n\n    - Patch 8: I've adopted the proposal of a `remove_replace_refs()` \n      helper function to clean up replace refs.\n\n    - Now comes with a base commit. Unbelievable.\n\nThanks for all the feedback so far!\n\nPatrick\n\nPatrick Steinhardt (9):\n  t: allow skipping expected object ID in `ref-store update-ref`\n  t: convert tests to not write references via the filesystem\n  t: convert tests to not access symrefs via the filesystem\n  t: convert tests to not access reflog via the filesystem\n  t1450: convert tests to remove worktrees via git-worktree(1)\n  t4207: delete replace references via git-update-ref(1)\n  t7300: assert exact states of repo\n  t7900: assert the absence of refs via git-for-each-ref(1)\n  t: mark several tests that assume the files backend with REFFILES\n\n t/helper/test-ref-store.c          | 11 ++++---\n t/t1400-update-ref.sh              | 50 ++++++++++++++++--------------\n t/t1430-bad-ref-name.sh            | 12 +++----\n t/t1450-fsck.sh                    | 44 +++++++++++++-------------\n t/t2011-checkout-invalid-head.sh   | 16 +++++-----\n t/t3200-branch.sh                  | 41 ++++++++++++------------\n t/t3400-rebase.sh                  |  2 +-\n t/t3404-rebase-interactive.sh      |  2 +-\n t/t4013-diff-various.sh            |  2 +-\n t/t4202-log.sh                     |  2 +-\n t/t4207-log-decoration-colors.sh   | 10 ++++--\n t/t5526-fetch-submodules.sh        |  2 +-\n t/t5605-clone-local.sh             |  4 +--\n t/t5702-protocol-v2.sh             | 24 ++++++++++----\n t/t7300-clean.sh                   | 23 ++++++++------\n t/t7900-maintenance.sh             |  3 +-\n t/t9133-git-svn-nested-git-repo.sh |  2 +-\n 17 files changed, 142 insertions(+), 108 deletions(-)\n\nRange-diff against v1:\n 1:  e947feb1c77 <  -:  ----------- t: add helpers to test for reference existence\n 2:  1f615d62f99 =  1:  c868198f8c1 t: allow skipping expected object ID in `ref-store update-ref`\n 3:  ac6a49c7c84 <  -:  ----------- t: convert tests to use helpers for reference existence\n 4:  c79431c0bf1 !  2:  4c0939d868e t: convert tests to not write references via the filesystem\n    @@ Commit message\n         test tool. The latter is required in some cases where safety checks of\n         git-update-ref(1) would otherwise reject a reference update.\n     \n    +    While at it, refactor some of the tests to schedule the cleanup command\n    +    via `test_when_finished` before modifying the repository.\n    +\n         Signed-off-by: Patrick Steinhardt <ps@pks.im>\n     \n      ## t/t1400-update-ref.sh ##\n    @@ t/t1400-update-ref.sh: test_expect_success \"delete $m without oldvalue verificat\n      \n      test_expect_success \"create $m (by HEAD)\" '\n     @@ t/t1400-update-ref.sh: test_expect_success 'delete symref without dereference when the referred ref is\n    + '\n      \n      test_expect_success 'update-ref -d is not confused by self-reference' '\n    ++\ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self\" &&\n      \tgit symbolic-ref refs/heads/self refs/heads/self &&\n     -\ttest_when_finished \"rm -f .git/refs/heads/self\" &&\n    -+\ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self\" &&\n      \ttest_path_is_file .git/refs/heads/self &&\n      \ttest_must_fail git update-ref -d refs/heads/self &&\n      \ttest_path_is_file .git/refs/heads/self\n    -@@ t/t1400-update-ref.sh: test_expect_success 'update-ref -d is not confused by self-reference' '\n    + '\n      \n      test_expect_success 'update-ref --no-deref -d can delete self-reference' '\n    ++\ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self\" &&\n      \tgit symbolic-ref refs/heads/self refs/heads/self &&\n     -\ttest_when_finished \"rm -f .git/refs/heads/self\" &&\n    -+\ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self\" &&\n      \ttest_path_is_file .git/refs/heads/self &&\n      \tgit update-ref --no-deref -d refs/heads/self &&\n      \ttest_must_fail git show-ref --verify -q refs/heads/self\n    @@ t/t1400-update-ref.sh: test_expect_success 'Query \"main@{2005-05-28}\" (past end\n      '\n      \n     -rm -f .git/$m .git/logs/$m expect\n    ++rm -f expect\n     +git update-ref -d $m\n      \n      test_expect_success 'creating initial files' '\n      \ttest_when_finished rm -f M &&\n     \n      ## t/t1450-fsck.sh ##\n    +@@ t/t1450-fsck.sh: test_expect_success setup '\n    + \tgit config --unset i18n.commitencoding &&\n    + \tgit checkout HEAD^0 &&\n    + \ttest_commit B fileB two &&\n    ++\torig_head=$(git rev-parse HEAD) &&\n    + \tgit tag -d A B &&\n    + \tgit reflog expire --expire=now --all\n    + '\n     @@ t/t1450-fsck.sh: test_expect_success 'zlib corrupt loose object output ' '\n      '\n      \n      test_expect_success 'branch pointing to non-commit' '\n     -\tgit rev-parse HEAD^{tree} >.git/refs/heads/invalid &&\n     +\ttree_oid=$(git rev-parse --verify HEAD^{tree}) &&\n    -+\ttest-tool ref-store main update-ref msg refs/heads/invalid $tree_oid $ZERO_OID REF_SKIP_OID_VERIFICATION &&\n      \ttest_when_finished \"git update-ref -d refs/heads/invalid\" &&\n    ++\ttest-tool ref-store main update-ref msg refs/heads/invalid $tree_oid $ZERO_OID REF_SKIP_OID_VERIFICATION &&\n      \ttest_must_fail git fsck 2>out &&\n      \ttest_i18ngrep \"not a commit\" out\n      '\n    @@ t/t1450-fsck.sh: test_expect_success 'zlib corrupt loose object output ' '\n      test_expect_success 'HEAD link pointing at a funny object' '\n     -\ttest_when_finished \"mv .git/SAVED_HEAD .git/HEAD\" &&\n     -\tmv .git/HEAD .git/SAVED_HEAD &&\n    -+\tsaved_head=$(git rev-parse --verify HEAD) &&\n    -+\ttest_when_finished \"git update-ref HEAD ${saved_head}\" &&\n    ++\ttest_when_finished \"git update-ref HEAD $orig_head\" &&\n      \techo $ZERO_OID >.git/HEAD &&\n      \t# avoid corrupt/broken HEAD from interfering with repo discovery\n      \ttest_must_fail env GIT_DIR=.git git fsck 2>out &&\n    @@ t/t1450-fsck.sh: test_expect_success 'HEAD link pointing at a funny object' '\n      test_expect_success 'HEAD link pointing at a funny place' '\n     -\ttest_when_finished \"mv .git/SAVED_HEAD .git/HEAD\" &&\n     -\tmv .git/HEAD .git/SAVED_HEAD &&\n    -+\tsaved_head=$(git rev-parse --verify HEAD) &&\n    -+\ttest_when_finished \"git update-ref --no-deref HEAD ${saved_head}\" &&\n    ++\ttest_when_finished \"git update-ref --no-deref HEAD $orig_head\" &&\n      \techo \"ref: refs/funny/place\" >.git/HEAD &&\n      \t# avoid corrupt/broken HEAD from interfering with repo discovery\n      \ttest_must_fail env GIT_DIR=.git git fsck 2>out &&\n    @@ t/t1450-fsck.sh: test_expect_success 'HEAD link pointing at a funny place' '\n      \n      test_expect_success 'HEAD link pointing at a funny object (from different wt)' '\n     -\ttest_when_finished \"mv .git/SAVED_HEAD .git/HEAD\" &&\n    -+\tsaved_head=$(git rev-parse --verify HEAD) &&\n    -+\ttest_when_finished \"git update-ref HEAD $saved_head\" &&\n    ++\ttest_when_finished \"git update-ref HEAD $orig_head\" &&\n      \ttest_when_finished \"rm -rf .git/worktrees wt\" &&\n      \tgit worktree add wt &&\n     -\tmv .git/HEAD .git/SAVED_HEAD &&\n 5:  1ac120368c6 !  3:  048583ed2c3 t: convert tests to not access symrefs via the filesystem\n    @@ Commit message\n      ## t/t1400-update-ref.sh ##\n     @@ t/t1400-update-ref.sh: test_expect_success 'delete symref without dereference when the referred ref is\n      test_expect_success 'update-ref -d is not confused by self-reference' '\n    - \tgit symbolic-ref refs/heads/self refs/heads/self &&\n      \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self\" &&\n    + \tgit symbolic-ref refs/heads/self refs/heads/self &&\n     -\ttest_path_is_file .git/refs/heads/self &&\n     +\tgit symbolic-ref --no-recurse refs/heads/self &&\n      \ttest_must_fail git update-ref -d refs/heads/self &&\n    @@ t/t1400-update-ref.sh: test_expect_success 'delete symref without dereference wh\n      '\n      \n      test_expect_success 'update-ref --no-deref -d can delete self-reference' '\n    - \tgit symbolic-ref refs/heads/self refs/heads/self &&\n      \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self\" &&\n    + \tgit symbolic-ref refs/heads/self refs/heads/self &&\n     -\ttest_path_is_file .git/refs/heads/self &&\n     +\tgit symbolic-ref --no-recurse refs/heads/self &&\n      \tgit update-ref --no-deref -d refs/heads/self &&\n    @@ t/t1430-bad-ref-name.sh: test_expect_success 'update-ref -d can delete broken na\n     -\tprintf \"ref: refs/heads/main\\n\" >.git/refs/heads/broken...symref &&\n     +\ttest-tool ref-store main create-symref refs/heads/broken...symref refs/heads/main &&\n      \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n    - \ttest_ref_exists refs/heads/broken...symref &&\n      \tgit update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&\n    + \ttest_path_is_missing .git/refs/heads/broken...symref &&\n     @@ t/t1430-bad-ref-name.sh: test_expect_success 'update-ref --no-deref -d can delete symref with broken name\n      '\n      \n    @@ t/t1430-bad-ref-name.sh: test_expect_success 'update-ref --no-deref -d can delet\n     -\tprintf \"ref: refs/heads/main\\n\" >.git/refs/heads/broken...symref &&\n     +\ttest-tool ref-store main create-symref refs/heads/broken...symref refs/heads/main &&\n      \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n    - \ttest_ref_exists refs/heads/broken...symref &&\n      \tgit branch -d broken...symref >output 2>error &&\n    + \ttest_path_is_missing .git/refs/heads/broken...symref &&\n     @@ t/t1430-bad-ref-name.sh: test_expect_success 'branch -d can delete symref with broken name' '\n      '\n      \n    @@ t/t1430-bad-ref-name.sh: test_expect_success 'branch -d can delete symref with b\n     -\tprintf \"ref: refs/heads/idonotexist\\n\" >.git/refs/heads/broken...symref &&\n     +\ttest-tool ref-store main create-symref refs/heads/broken...symref refs/heads/idonotexist &&\n      \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n    - \ttest_ref_exists refs/heads/broken...symref &&\n      \tgit update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&\n    + \ttest_path_is_missing .git/refs/heads/broken...symref &&\n     @@ t/t1430-bad-ref-name.sh: test_expect_success 'update-ref --no-deref -d can delete dangling symref with br\n      '\n      \n    @@ t/t1430-bad-ref-name.sh: test_expect_success 'update-ref --no-deref -d can delet\n     -\tprintf \"ref: refs/heads/idonotexist\\n\" >.git/refs/heads/broken...symref &&\n     +\ttest-tool ref-store main create-symref refs/heads/broken...symref refs/heads/idonotexist &&\n      \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n    - \ttest_ref_exists refs/heads/broken...symref &&\n      \tgit branch -d broken...symref >output 2>error &&\n    + \ttest_path_is_missing .git/refs/heads/broken...symref &&\n     \n      ## t/t1450-fsck.sh ##\n     @@ t/t1450-fsck.sh: test_expect_success 'HEAD link pointing at a funny object' '\n    + \n      test_expect_success 'HEAD link pointing at a funny place' '\n    - \tsaved_head=$(git rev-parse --verify HEAD) &&\n    - \ttest_when_finished \"git update-ref --no-deref HEAD ${saved_head}\" &&\n    + \ttest_when_finished \"git update-ref --no-deref HEAD $orig_head\" &&\n     -\techo \"ref: refs/funny/place\" >.git/HEAD &&\n     +\ttest-tool ref-store main create-symref HEAD refs/funny/place &&\n      \t# avoid corrupt/broken HEAD from interfering with repo discovery\n    @@ t/t3200-branch.sh: test_expect_success 'git branch -M should leave orphaned HEAD\n     +\t\tgit symbolic-ref HEAD >expect &&\n     +\t\techo refs/heads/lonely >actual &&\n     +\t\ttest_cmp expect actual &&\n    - \t\ttest_ref_missing refs/head/lonely &&\n    + \t\ttest_path_is_missing .git/refs/head/lonely &&\n      \t\tgit branch -M main mistress &&\n     -\t\tgrep lonely .git/HEAD\n     +\t\tgit symbolic-ref HEAD >expect &&\n    @@ t/t3200-branch.sh: test_expect_success 'deleting a symref' '\n     +\tgit symbolic-ref --no-recurse refs/heads/dangling-symref &&\n      \techo \"Deleted branch dangling-symref (was nowhere).\" >expect &&\n      \tgit branch -d dangling-symref >actual &&\n    - \ttest_ref_missing refs/heads/dangling-symref &&\n    + \ttest_path_is_missing .git/refs/heads/dangling-symref &&\n     \n      ## t/t4013-diff-various.sh ##\n     @@ t/t4013-diff-various.sh: test_expect_success 'log -S requires an argument' '\n 6:  eaac658bbfd !  4:  5e7937e7904 t: convert tests to not access reflog via the filesystem\n    @@ t/t3200-branch.sh: test_expect_success 'git branch HEAD should fail' '\n      test_expect_success 'git branch --create-reflog d/e/f should create a branch and a log' '\n      \tGIT_COMMITTER_DATE=\"2005-05-26 23:30\" \\\n      \tgit -c core.logallrefupdates=false branch --create-reflog d/e/f &&\n    - \ttest_ref_exists refs/heads/d/e/f &&\n    + \ttest_path_is_file .git/refs/heads/d/e/f &&\n     -\ttest_path_is_file .git/logs/refs/heads/d/e/f &&\n     -\ttest_cmp expect .git/logs/refs/heads/d/e/f\n     +\tgit reflog show --no-abbrev-commit refs/heads/d/e/f >actual &&\n    @@ t/t3200-branch.sh: test_expect_success '--set-upstream-to notices an error to se\n      test_expect_success 'git checkout -b g/h/i -l should create a branch and a log' '\n      \tGIT_COMMITTER_DATE=\"2005-05-26 23:30\" \\\n      \tgit checkout -b g/h/i -l main &&\n    - \ttest_ref_exists refs/heads/g/h/i &&\n    + \ttest_path_is_file .git/refs/heads/g/h/i &&\n     -\ttest_path_is_file .git/logs/refs/heads/g/h/i &&\n     -\ttest_cmp expect .git/logs/refs/heads/g/h/i\n     +\tgit reflog show --no-abbrev-commit refs/heads/g/h/i >actual &&\n 7:  3dc65a80074 !  5:  089565a358e t1450: convert tests to remove worktrees via git-worktree(1)\n    @@ Commit message\n     \n      ## t/t1450-fsck.sh ##\n     @@ t/t1450-fsck.sh: test_expect_success 'HEAD link pointing at a funny place' '\n    + \n      test_expect_success 'HEAD link pointing at a funny object (from different wt)' '\n    - \tsaved_head=$(git rev-parse --verify HEAD) &&\n    - \ttest_when_finished \"git update-ref HEAD $saved_head\" &&\n    + \ttest_when_finished \"git update-ref HEAD $orig_head\" &&\n     -\ttest_when_finished \"rm -rf .git/worktrees wt\" &&\n     +\ttest_when_finished \"git worktree remove -f wt\" &&\n      \tgit worktree add wt &&\n 8:  c4d09e3e5db !  6:  cb738888ed7 t4207: delete replace references via git-update-ref(1)\n    @@ Commit message\n     \n      ## t/t4207-log-decoration-colors.sh ##\n     @@ t/t4207-log-decoration-colors.sh: ${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_commit})${c_reset} A\n    + \tcmp_filtered_decorations\n      '\n      \n    ++remove_replace_refs () {\n    ++\tgit for-each-ref 'refs/replace*/**' --format='delete %(refname)' >in &&\n    ++\tgit update-ref --stdin <in &&\n    ++\trm in\n    ++}\n    ++\n      test_expect_success 'test coloring with replace-objects' '\n     -\ttest_when_finished rm -rf .git/refs/replace* &&\n    -+\ttest_when_finished \"git for-each-ref refs/replace*/** --format=${SQ}delete %(refname)${SQ} | git update-ref --stdin\" &&\n    ++\ttest_when_finished remove_replace_refs &&\n      \ttest_commit C &&\n      \ttest_commit D &&\n      \n    @@ t/t4207-log-decoration-colors.sh: EOF\n      \n      test_expect_success 'test coloring with grafted commit' '\n     -\ttest_when_finished rm -rf .git/refs/replace* &&\n    -+\ttest_when_finished \"git for-each-ref refs/replace*/** --format=${SQ}delete %(refname)${SQ} | git update-ref --stdin\" &&\n    ++\ttest_when_finished remove_replace_refs &&\n      \n      \tgit replace --graft HEAD HEAD~2 &&\n      \n 9:  153b5b199c8 =  7:  e730e011de4 t7300: assert exact states of repo\n10:  b99d98b00a3 =  8:  a1bdea52397 t7900: assert the absence of refs via git-for-each-ref(1)\n11:  67cb282a414 !  9:  497e43ae5c3 t: mark several tests that assume the files backend with REFFILES\n    @@ t/t1450-fsck.sh: test_expect_success 'branch pointing to non-commit' '\n      \n     -test_expect_success 'HEAD link pointing at a funny object' '\n     +test_expect_success REFFILES 'HEAD link pointing at a funny object' '\n    - \tsaved_head=$(git rev-parse --verify HEAD) &&\n    - \ttest_when_finished \"git update-ref HEAD ${saved_head}\" &&\n    + \ttest_when_finished \"git update-ref HEAD $orig_head\" &&\n      \techo $ZERO_OID >.git/HEAD &&\n    + \t# avoid corrupt/broken HEAD from interfering with repo discovery\n     @@ t/t1450-fsck.sh: test_expect_success 'HEAD link pointing at a funny place' '\n      \ttest_i18ngrep \"HEAD points to something strange\" out\n      '\n      \n     -test_expect_success 'HEAD link pointing at a funny object (from different wt)' '\n     +test_expect_success REFFILES 'HEAD link pointing at a funny object (from different wt)' '\n    - \tsaved_head=$(git rev-parse --verify HEAD) &&\n    - \ttest_when_finished \"git update-ref HEAD $saved_head\" &&\n    + \ttest_when_finished \"git update-ref HEAD $orig_head\" &&\n      \ttest_when_finished \"git worktree remove -f wt\" &&\n    + \tgit worktree add wt &&\n     @@ t/t1450-fsck.sh: test_expect_success 'HEAD link pointing at a funny object (from different wt)' '\n      \ttest_i18ngrep \"main-worktree/HEAD: detached HEAD points\" out\n      '\n    @@ t/t2011-checkout-invalid-head.sh: test_expect_success 'create ref directory/file\n     \n      ## t/t3200-branch.sh ##\n     @@ t/t3200-branch.sh: test_expect_success 'git branch --help should not have created a bogus branch' '\n    - \ttest_ref_missing refs/heads/--help\n    + \ttest_path_is_missing .git/refs/heads/--help\n      '\n      \n     -test_expect_success 'branch -h in broken repository' '\n    @@ t/t3200-branch.sh: test_expect_success 'git branch -M baz bam should succeed whe\n      \tgit worktree add -f bazdir2 baz &&\n      \ttouch .git/worktrees/bazdir1/HEAD.lock &&\n     @@ t/t3200-branch.sh: test_expect_success 'renaming a symref is not allowed' '\n    - \ttest_ref_missing refs/heads/new-topic\n    + \ttest_path_is_missing .git/refs/heads/new-topic\n      '\n      \n     -test_expect_success SYMLINKS 'git branch -m u v should fail when the reflog for u is a symlink' '\n\nbase-commit: a9ecda2788e229afc9b611acaa26d0d9d4da53ed\n-- \n2.42.0\n\n"},{"id":"483787","messageId":"c868198f8c1e0ddeb6577efdf741623584653633.1698156169.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1698156169.git.ps@pks.im","subject":"[PATCH v2 1/9] t: allow skipping expected object ID in `ref-store update-ref`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-24T14:04:53Z","receivedAt":"2023-10-24T14:05:00Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"We require the caller to pass both the old and new expected object ID to\nour `test-tool ref-store update-ref` helper. When trying to update a\nsymbolic reference though it's impossible to specify the expected object\nID, which means that the test would instead have to force-update the\nreference. This is currently impossible though.\n\nUpdate the helper to optionally skip verification of the old object ID\nin case the test passes in an empty old object ID as input.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/helper/test-ref-store.c | 11 +++++++----\n 1 file changed, 7 insertions(+), 4 deletions(-)\n\ndiff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\nindex 48552e6a9e0..702ec1f128a 100644\n--- a/t/helper/test-ref-store.c\n+++ b/t/helper/test-ref-store.c\n@@ -298,16 +298,19 @@ static int cmd_update_ref(struct ref_store *refs, const char **argv)\n \tconst char *new_sha1_buf = notnull(*argv++, \"new-sha1\");\n \tconst char *old_sha1_buf = notnull(*argv++, \"old-sha1\");\n \tunsigned int flags = arg_flags(*argv++, \"flags\", transaction_flags);\n-\tstruct object_id old_oid;\n+\tstruct object_id old_oid, *old_oid_ptr = NULL;\n \tstruct object_id new_oid;\n \n-\tif (get_oid_hex(old_sha1_buf, &old_oid))\n-\t\tdie(\"cannot parse %s as %s\", old_sha1_buf, the_hash_algo->name);\n+\tif (*old_sha1_buf) {\n+\t\tif (get_oid_hex(old_sha1_buf, &old_oid))\n+\t\t\tdie(\"cannot parse %s as %s\", old_sha1_buf, the_hash_algo->name);\n+\t\told_oid_ptr = &old_oid;\n+\t}\n \tif (get_oid_hex(new_sha1_buf, &new_oid))\n \t\tdie(\"cannot parse %s as %s\", new_sha1_buf, the_hash_algo->name);\n \n \treturn refs_update_ref(refs, msg, refname,\n-\t\t\t       &new_oid, &old_oid,\n+\t\t\t       &new_oid, old_oid_ptr,\n \t\t\t       flags, UPDATE_REFS_DIE_ON_ERR);\n }\n \n-- \n2.42.0\n\n"},{"id":"483788","messageId":"4c0939d868efeb11710e68fd81224bcf31d38aba.1698156169.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1698156169.git.ps@pks.im","subject":"[PATCH v2 2/9] t: convert tests to not write references via the filesystem","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-24T14:04:58Z","receivedAt":"2023-10-24T14:05:05Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Some of our tests manually create, update or delete references by\nwriting the respective new values into the filesystem directly. While\nthis works with the current files reference backend, this will break\nonce we have a second reference backend implementation in our codebase.\n\nRefactor these tests to instead use git-update-ref(1) or our `ref-store`\ntest tool. The latter is required in some cases where safety checks of\ngit-update-ref(1) would otherwise reject a reference update.\n\nWhile at it, refactor some of the tests to schedule the cleanup command\nvia `test_when_finished` before modifying the repository.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t1400-update-ref.sh         | 17 ++++++++---------\n t/t1450-fsck.sh               | 26 +++++++++++++-------------\n t/t3404-rebase-interactive.sh |  2 +-\n t/t5526-fetch-submodules.sh   |  2 +-\n 4 files changed, 23 insertions(+), 24 deletions(-)\n\ndiff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh\nindex 4d66cd7f4a1..91cc6dff724 100755\n--- a/t/t1400-update-ref.sh\n+++ b/t/t1400-update-ref.sh\n@@ -9,8 +9,6 @@ test_description='Test git update-ref and basic ref logging'\n Z=$ZERO_OID\n \n m=refs/heads/main\n-n_dir=refs/heads/gu\n-n=$n_dir/fixes\n outside=refs/foo\n bare=bare-repo\n \n@@ -62,10 +60,10 @@ test_expect_success \"delete $m without oldvalue verification\" '\n \ttest_must_fail git show-ref --verify -q $m\n '\n \n-test_expect_success \"fail to create $n\" '\n-\ttest_when_finished \"rm -f .git/$n_dir\" &&\n-\ttouch .git/$n_dir &&\n-\ttest_must_fail git update-ref $n $A\n+test_expect_success \"fail to create $n due to file/directory conflict\" '\n+\ttest_when_finished \"git update-ref -d refs/heads/gu\" &&\n+\tgit update-ref refs/heads/gu $A &&\n+\ttest_must_fail git update-ref refs/heads/gu/fixes $A\n '\n \n test_expect_success \"create $m (by HEAD)\" '\n@@ -221,16 +219,16 @@ test_expect_success 'delete symref without dereference when the referred ref is\n '\n \n test_expect_success 'update-ref -d is not confused by self-reference' '\n+\ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self\" &&\n \tgit symbolic-ref refs/heads/self refs/heads/self &&\n-\ttest_when_finished \"rm -f .git/refs/heads/self\" &&\n \ttest_path_is_file .git/refs/heads/self &&\n \ttest_must_fail git update-ref -d refs/heads/self &&\n \ttest_path_is_file .git/refs/heads/self\n '\n \n test_expect_success 'update-ref --no-deref -d can delete self-reference' '\n+\ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self\" &&\n \tgit symbolic-ref refs/heads/self refs/heads/self &&\n-\ttest_when_finished \"rm -f .git/refs/heads/self\" &&\n \ttest_path_is_file .git/refs/heads/self &&\n \tgit update-ref --no-deref -d refs/heads/self &&\n \ttest_must_fail git show-ref --verify -q refs/heads/self\n@@ -434,7 +432,8 @@ test_expect_success 'Query \"main@{2005-05-28}\" (past end of history)' '\n \ttest_i18ngrep -F \"warning: log for ref $m unexpectedly ended on $ld\" e\n '\n \n-rm -f .git/$m .git/logs/$m expect\n+rm -f expect\n+git update-ref -d $m\n \n test_expect_success 'creating initial files' '\n \ttest_when_finished rm -f M &&\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 10a539158c4..f55b539b083 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -15,6 +15,7 @@ test_expect_success setup '\n \tgit config --unset i18n.commitencoding &&\n \tgit checkout HEAD^0 &&\n \ttest_commit B fileB two &&\n+\torig_head=$(git rev-parse HEAD) &&\n \tgit tag -d A B &&\n \tgit reflog expire --expire=now --all\n '\n@@ -115,15 +116,15 @@ test_expect_success 'zlib corrupt loose object output ' '\n '\n \n test_expect_success 'branch pointing to non-commit' '\n-\tgit rev-parse HEAD^{tree} >.git/refs/heads/invalid &&\n+\ttree_oid=$(git rev-parse --verify HEAD^{tree}) &&\n \ttest_when_finished \"git update-ref -d refs/heads/invalid\" &&\n+\ttest-tool ref-store main update-ref msg refs/heads/invalid $tree_oid $ZERO_OID REF_SKIP_OID_VERIFICATION &&\n \ttest_must_fail git fsck 2>out &&\n \ttest_i18ngrep \"not a commit\" out\n '\n \n test_expect_success 'HEAD link pointing at a funny object' '\n-\ttest_when_finished \"mv .git/SAVED_HEAD .git/HEAD\" &&\n-\tmv .git/HEAD .git/SAVED_HEAD &&\n+\ttest_when_finished \"git update-ref HEAD $orig_head\" &&\n \techo $ZERO_OID >.git/HEAD &&\n \t# avoid corrupt/broken HEAD from interfering with repo discovery\n \ttest_must_fail env GIT_DIR=.git git fsck 2>out &&\n@@ -131,8 +132,7 @@ test_expect_success 'HEAD link pointing at a funny object' '\n '\n \n test_expect_success 'HEAD link pointing at a funny place' '\n-\ttest_when_finished \"mv .git/SAVED_HEAD .git/HEAD\" &&\n-\tmv .git/HEAD .git/SAVED_HEAD &&\n+\ttest_when_finished \"git update-ref --no-deref HEAD $orig_head\" &&\n \techo \"ref: refs/funny/place\" >.git/HEAD &&\n \t# avoid corrupt/broken HEAD from interfering with repo discovery\n \ttest_must_fail env GIT_DIR=.git git fsck 2>out &&\n@@ -140,10 +140,9 @@ test_expect_success 'HEAD link pointing at a funny place' '\n '\n \n test_expect_success 'HEAD link pointing at a funny object (from different wt)' '\n-\ttest_when_finished \"mv .git/SAVED_HEAD .git/HEAD\" &&\n+\ttest_when_finished \"git update-ref HEAD $orig_head\" &&\n \ttest_when_finished \"rm -rf .git/worktrees wt\" &&\n \tgit worktree add wt &&\n-\tmv .git/HEAD .git/SAVED_HEAD &&\n \techo $ZERO_OID >.git/HEAD &&\n \t# avoid corrupt/broken HEAD from interfering with repo discovery\n \ttest_must_fail git -C wt fsck 2>out &&\n@@ -161,7 +160,8 @@ test_expect_success 'other worktree HEAD link pointing at a funny object' '\n test_expect_success 'other worktree HEAD link pointing at missing object' '\n \ttest_when_finished \"rm -rf .git/worktrees other\" &&\n \tgit worktree add other &&\n-\techo \"Contents missing from repo\" | git hash-object --stdin >.git/worktrees/other/HEAD &&\n+\tobject_id=$(echo \"Contents missing from repo\" | git hash-object --stdin) &&\n+\ttest-tool -C other ref-store main update-ref msg HEAD $object_id \"\" REF_NO_DEREF,REF_SKIP_OID_VERIFICATION &&\n \ttest_must_fail git fsck 2>out &&\n \ttest_i18ngrep \"worktrees/other/HEAD: invalid sha1 pointer\" out\n '\n@@ -391,7 +391,7 @@ test_expect_success 'tag pointing to nonexistent' '\n \n \ttag=$(git hash-object -t tag -w --stdin <invalid-tag) &&\n \ttest_when_finished \"remove_object $tag\" &&\n-\techo $tag >.git/refs/tags/invalid &&\n+\tgit update-ref refs/tags/invalid $tag &&\n \ttest_when_finished \"git update-ref -d refs/tags/invalid\" &&\n \ttest_must_fail git fsck --tags >out &&\n \ttest_i18ngrep \"broken link\" out\n@@ -411,7 +411,7 @@ test_expect_success 'tag pointing to something else than its type' '\n \n \ttag=$(git hash-object -t tag -w --stdin <wrong-tag) &&\n \ttest_when_finished \"remove_object $tag\" &&\n-\techo $tag >.git/refs/tags/wrong &&\n+\tgit update-ref refs/tags/wrong $tag &&\n \ttest_when_finished \"git update-ref -d refs/tags/wrong\" &&\n \ttest_must_fail git fsck --tags\n '\n@@ -428,7 +428,7 @@ test_expect_success 'tag with incorrect tag name & missing tagger' '\n \n \ttag=$(git hash-object --literally -t tag -w --stdin <wrong-tag) &&\n \ttest_when_finished \"remove_object $tag\" &&\n-\techo $tag >.git/refs/tags/wrong &&\n+\tgit update-ref refs/tags/wrong $tag &&\n \ttest_when_finished \"git update-ref -d refs/tags/wrong\" &&\n \tgit fsck --tags 2>out &&\n \n@@ -452,7 +452,7 @@ test_expect_success 'tag with bad tagger' '\n \n \ttag=$(git hash-object --literally -t tag -w --stdin <wrong-tag) &&\n \ttest_when_finished \"remove_object $tag\" &&\n-\techo $tag >.git/refs/tags/wrong &&\n+\tgit update-ref refs/tags/wrong $tag &&\n \ttest_when_finished \"git update-ref -d refs/tags/wrong\" &&\n \ttest_must_fail git fsck --tags 2>out &&\n \ttest_i18ngrep \"error in tag .*: invalid author/committer\" out\n@@ -471,7 +471,7 @@ test_expect_success 'tag with NUL in header' '\n \n \ttag=$(git hash-object --literally -t tag -w --stdin <tag-NUL-header) &&\n \ttest_when_finished \"remove_object $tag\" &&\n-\techo $tag >.git/refs/tags/wrong &&\n+\tgit update-ref refs/tags/wrong $tag &&\n \ttest_when_finished \"git update-ref -d refs/tags/wrong\" &&\n \ttest_must_fail git fsck --tags 2>out &&\n \ttest_i18ngrep \"error in tag $tag.*unterminated header: NUL at offset\" out\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex 8ea2bf13026..d2a7a91f170 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -2160,7 +2160,7 @@ test_expect_success '--update-refs: check failed ref update' '\n \t# recorded in the update-refs file. We will force-update the\n \t# \"second\" ref, but \"git branch -f\" will not work because of\n \t# the lock in the update-refs file.\n-\tgit rev-parse third >.git/refs/heads/second &&\n+\tgit update-ref refs/heads/second third &&\n \n \ttest_must_fail git rebase --continue 2>err &&\n \tgrep \"update_ref failed for ref '\\''refs/heads/second'\\''\" err &&\ndiff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh\nindex 26e933f93ae..7ab220fa313 100755\n--- a/t/t5526-fetch-submodules.sh\n+++ b/t/t5526-fetch-submodules.sh\n@@ -771,7 +771,7 @@ test_expect_success 'fetching submodule into a broken repository' '\n \tgit -C dst fetch --recurse-submodules &&\n \n \t# Break the receiving submodule\n-\trm -f dst/sub/.git/HEAD &&\n+\ttest-tool -C dst/sub ref-store main delete-refs REF_NO_DEREF msg HEAD &&\n \n \t# NOTE: without the fix the following tests will recurse forever!\n \t# They should terminate with an error.\n-- \n2.42.0\n\n"},{"id":"483789","messageId":"048583ed2c3bd70fb455b9355c6b9030a7ca77cb.1698156169.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1698156169.git.ps@pks.im","subject":"[PATCH v2 3/9] t: convert tests to not access symrefs via the filesystem","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-24T14:05:02Z","receivedAt":"2023-10-24T14:05:08Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Some of our tests access symbolic references via the filesystem\ndirectly. While this works with the current files reference backend, it\nthis will break once we have a second reference backend in our codebase.\n\nRefactor these tests to instead use git-symbolic-ref(1) or our\n`ref-store` test tool. The latter is required in some cases where safety\nchecks of git-symbolic-ref(1) would otherwise reject writing a symbolic\nreference.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t1400-update-ref.sh              |  8 ++++----\n t/t1430-bad-ref-name.sh            | 12 ++++++------\n t/t1450-fsck.sh                    |  4 ++--\n t/t3200-branch.sh                  |  9 ++++++---\n t/t4013-diff-various.sh            |  2 +-\n t/t4202-log.sh                     |  2 +-\n t/t5605-clone-local.sh             |  2 +-\n t/t5702-protocol-v2.sh             | 24 ++++++++++++++++++------\n t/t9133-git-svn-nested-git-repo.sh |  2 +-\n 9 files changed, 40 insertions(+), 25 deletions(-)\n\ndiff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh\nindex 91cc6dff724..51a8d0bba98 100755\n--- a/t/t1400-update-ref.sh\n+++ b/t/t1400-update-ref.sh\n@@ -221,15 +221,15 @@ test_expect_success 'delete symref without dereference when the referred ref is\n test_expect_success 'update-ref -d is not confused by self-reference' '\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self\" &&\n \tgit symbolic-ref refs/heads/self refs/heads/self &&\n-\ttest_path_is_file .git/refs/heads/self &&\n+\tgit symbolic-ref --no-recurse refs/heads/self &&\n \ttest_must_fail git update-ref -d refs/heads/self &&\n-\ttest_path_is_file .git/refs/heads/self\n+\tgit symbolic-ref --no-recurse refs/heads/self\n '\n \n test_expect_success 'update-ref --no-deref -d can delete self-reference' '\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self\" &&\n \tgit symbolic-ref refs/heads/self refs/heads/self &&\n-\ttest_path_is_file .git/refs/heads/self &&\n+\tgit symbolic-ref --no-recurse refs/heads/self &&\n \tgit update-ref --no-deref -d refs/heads/self &&\n \ttest_must_fail git show-ref --verify -q refs/heads/self\n '\n@@ -239,7 +239,7 @@ test_expect_success 'update-ref --no-deref -d can delete reference to bad ref' '\n \ttest_when_finished \"rm -f .git/refs/heads/bad\" &&\n \tgit symbolic-ref refs/heads/ref-to-bad refs/heads/bad &&\n \ttest_when_finished \"git update-ref -d refs/heads/ref-to-bad\" &&\n-\ttest_path_is_file .git/refs/heads/ref-to-bad &&\n+\tgit symbolic-ref --no-recurse refs/heads/ref-to-bad &&\n \tgit update-ref --no-deref -d refs/heads/ref-to-bad &&\n \ttest_must_fail git show-ref --verify -q refs/heads/ref-to-bad\n '\ndiff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh\nindex ff1c967d550..7d2dfc97256 100755\n--- a/t/t1430-bad-ref-name.sh\n+++ b/t/t1430-bad-ref-name.sh\n@@ -164,9 +164,9 @@ test_expect_success 'rev-parse skips symref pointing to broken name' '\n test_expect_success 'for-each-ref emits warnings for broken names' '\n \ttest-tool ref-store main update-ref msg \"refs/heads/broken...ref\" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref\" &&\n-\tprintf \"ref: refs/heads/broken...ref\\n\" >.git/refs/heads/badname &&\n+\ttest-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname\" &&\n-\tprintf \"ref: refs/heads/main\\n\" >.git/refs/heads/broken...symref &&\n+\ttest-tool ref-store main create-symref refs/heads/broken...symref refs/heads/main &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n \tgit for-each-ref >output 2>error &&\n \t! grep -e \"broken\\.\\.\\.ref\" output &&\n@@ -252,7 +252,7 @@ test_expect_success 'update-ref -d can delete broken name through symref' '\n '\n \n test_expect_success 'update-ref --no-deref -d can delete symref with broken name' '\n-\tprintf \"ref: refs/heads/main\\n\" >.git/refs/heads/broken...symref &&\n+\ttest-tool ref-store main create-symref refs/heads/broken...symref refs/heads/main &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n \tgit update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&\n \ttest_path_is_missing .git/refs/heads/broken...symref &&\n@@ -261,7 +261,7 @@ test_expect_success 'update-ref --no-deref -d can delete symref with broken name\n '\n \n test_expect_success 'branch -d can delete symref with broken name' '\n-\tprintf \"ref: refs/heads/main\\n\" >.git/refs/heads/broken...symref &&\n+\ttest-tool ref-store main create-symref refs/heads/broken...symref refs/heads/main &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n \tgit branch -d broken...symref >output 2>error &&\n \ttest_path_is_missing .git/refs/heads/broken...symref &&\n@@ -270,7 +270,7 @@ test_expect_success 'branch -d can delete symref with broken name' '\n '\n \n test_expect_success 'update-ref --no-deref -d can delete dangling symref with broken name' '\n-\tprintf \"ref: refs/heads/idonotexist\\n\" >.git/refs/heads/broken...symref &&\n+\ttest-tool ref-store main create-symref refs/heads/broken...symref refs/heads/idonotexist &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n \tgit update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&\n \ttest_path_is_missing .git/refs/heads/broken...symref &&\n@@ -279,7 +279,7 @@ test_expect_success 'update-ref --no-deref -d can delete dangling symref with br\n '\n \n test_expect_success 'branch -d can delete dangling symref with broken name' '\n-\tprintf \"ref: refs/heads/idonotexist\\n\" >.git/refs/heads/broken...symref &&\n+\ttest-tool ref-store main create-symref refs/heads/broken...symref refs/heads/idonotexist &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n \tgit branch -d broken...symref >output 2>error &&\n \ttest_path_is_missing .git/refs/heads/broken...symref &&\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex f55b539b083..a3c97b9c7fc 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -133,7 +133,7 @@ test_expect_success 'HEAD link pointing at a funny object' '\n \n test_expect_success 'HEAD link pointing at a funny place' '\n \ttest_when_finished \"git update-ref --no-deref HEAD $orig_head\" &&\n-\techo \"ref: refs/funny/place\" >.git/HEAD &&\n+\ttest-tool ref-store main create-symref HEAD refs/funny/place &&\n \t# avoid corrupt/broken HEAD from interfering with repo discovery\n \ttest_must_fail env GIT_DIR=.git git fsck 2>out &&\n \ttest_i18ngrep \"HEAD points to something strange\" out\n@@ -169,7 +169,7 @@ test_expect_success 'other worktree HEAD link pointing at missing object' '\n test_expect_success 'other worktree HEAD link pointing at a funny place' '\n \ttest_when_finished \"rm -rf .git/worktrees other\" &&\n \tgit worktree add other &&\n-\techo \"ref: refs/funny/place\" >.git/worktrees/other/HEAD &&\n+\tgit -C other symbolic-ref HEAD refs/funny/place &&\n \ttest_must_fail git fsck 2>out &&\n \ttest_i18ngrep \"worktrees/other/HEAD points to something strange\" out\n '\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex 080e4f24a6e..7d9393f190d 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -212,10 +212,13 @@ test_expect_success 'git branch -M should leave orphaned HEAD alone' '\n \t\tcd orphan &&\n \t\ttest_commit initial &&\n \t\tgit checkout --orphan lonely &&\n-\t\tgrep lonely .git/HEAD &&\n+\t\tgit symbolic-ref HEAD >expect &&\n+\t\techo refs/heads/lonely >actual &&\n+\t\ttest_cmp expect actual &&\n \t\ttest_path_is_missing .git/refs/head/lonely &&\n \t\tgit branch -M main mistress &&\n-\t\tgrep lonely .git/HEAD\n+\t\tgit symbolic-ref HEAD >expect &&\n+\t\ttest_cmp expect actual\n \t)\n '\n \n@@ -806,7 +809,7 @@ test_expect_success 'deleting a symref' '\n \n test_expect_success 'deleting a dangling symref' '\n \tgit symbolic-ref refs/heads/dangling-symref nowhere &&\n-\ttest_path_is_file .git/refs/heads/dangling-symref &&\n+\tgit symbolic-ref --no-recurse refs/heads/dangling-symref &&\n \techo \"Deleted branch dangling-symref (was nowhere).\" >expect &&\n \tgit branch -d dangling-symref >actual &&\n \ttest_path_is_missing .git/refs/heads/dangling-symref &&\ndiff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh\nindex 5de1d190759..5abbea36b39 100755\n--- a/t/t4013-diff-various.sh\n+++ b/t/t4013-diff-various.sh\n@@ -514,7 +514,7 @@ test_expect_success 'log -S requires an argument' '\n '\n \n test_expect_success 'diff --cached on unborn branch' '\n-\techo ref: refs/heads/unborn >.git/HEAD &&\n+\tgit symbolic-ref HEAD refs/heads/unborn &&\n \tgit diff --cached >result &&\n \tprocess_diffs result >actual &&\n \tprocess_diffs \"$TEST_DIRECTORY/t4013/diff.diff_--cached\" >expected &&\ndiff --git a/t/t4202-log.sh b/t/t4202-log.sh\nindex af4a123cd22..57b298a4e22 100755\n--- a/t/t4202-log.sh\n+++ b/t/t4202-log.sh\n@@ -2265,7 +2265,7 @@ test_expect_success REFFILES 'log diagnoses bogus HEAD hash' '\n \n test_expect_success REFFILES 'log diagnoses bogus HEAD symref' '\n \tgit init empty &&\n-\techo \"ref: refs/heads/invalid.lock\" > empty/.git/HEAD &&\n+\ttest-tool -C empty ref-store main create-symref HEAD refs/heads/invalid.lock &&\n \ttest_must_fail git -C empty log 2>stderr &&\n \ttest_i18ngrep broken stderr &&\n \ttest_must_fail git -C empty log --default totally-bogus 2>stderr &&\ndiff --git a/t/t5605-clone-local.sh b/t/t5605-clone-local.sh\nindex 1d7b1abda1a..a26689de379 100755\n--- a/t/t5605-clone-local.sh\n+++ b/t/t5605-clone-local.sh\n@@ -65,7 +65,7 @@ test_expect_success 'Even without -l, local will make a hardlink' '\n '\n \n test_expect_success 'local clone of repo with nonexistent ref in HEAD' '\n-\techo \"ref: refs/heads/nonexistent\" > a.git/HEAD &&\n+\tgit -C a.git symbolic-ref HEAD refs/heads/nonexistent &&\n \tgit clone a d &&\n \t(cd d &&\n \tgit fetch &&\ndiff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh\nindex 6af5c2062fd..dcc4cd95fe7 100755\n--- a/t/t5702-protocol-v2.sh\n+++ b/t/t5702-protocol-v2.sh\n@@ -221,7 +221,9 @@ test_expect_success 'clone of empty repo propagates name of default branch' '\n \tGIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \\\n \tgit -c init.defaultBranch=main -c protocol.version=2 \\\n \t\tclone \"file://$(pwd)/file_empty_parent\" file_empty_child &&\n-\tgrep \"refs/heads/mydefaultbranch\" file_empty_child/.git/HEAD\n+\techo refs/heads/mydefaultbranch >expect &&\n+\tgit -C file_empty_child symbolic-ref HEAD >actual &&\n+\ttest_cmp expect actual\n '\n \n test_expect_success '...but not if explicitly forbidden by config' '\n@@ -234,7 +236,9 @@ test_expect_success '...but not if explicitly forbidden by config' '\n \tGIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \\\n \tgit -c init.defaultBranch=main -c protocol.version=2 \\\n \t\tclone \"file://$(pwd)/file_empty_parent\" file_empty_child &&\n-\t! grep \"refs/heads/mydefaultbranch\" file_empty_child/.git/HEAD\n+\techo refs/heads/main >expect &&\n+\tgit -C file_empty_child symbolic-ref HEAD >actual &&\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'bare clone propagates empty default branch' '\n@@ -247,7 +251,9 @@ test_expect_success 'bare clone propagates empty default branch' '\n \tgit -c init.defaultBranch=main -c protocol.version=2 \\\n \t\tclone --bare \\\n \t\t\"file://$(pwd)/file_empty_parent\" file_empty_child.git &&\n-\tgrep \"refs/heads/mydefaultbranch\" file_empty_child.git/HEAD\n+\techo \"refs/heads/mydefaultbranch\" >expect &&\n+\tgit -C file_empty_child.git symbolic-ref HEAD >actual &&\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'clone propagates unborn HEAD from non-empty repo' '\n@@ -265,7 +271,9 @@ test_expect_success 'clone propagates unborn HEAD from non-empty repo' '\n \tgit -c init.defaultBranch=main -c protocol.version=2 \\\n \t\tclone \"file://$(pwd)/file_unborn_parent\" \\\n \t\tfile_unborn_child 2>stderr &&\n-\tgrep \"refs/heads/mydefaultbranch\" file_unborn_child/.git/HEAD &&\n+\techo \"refs/heads/mydefaultbranch\" >expect &&\n+\tgit -C file_unborn_child symbolic-ref HEAD >actual &&\n+\ttest_cmp expect actual &&\n \tgrep \"warning: remote HEAD refers to nonexistent ref\" stderr\n '\n \n@@ -295,7 +303,9 @@ test_expect_success 'bare clone propagates unborn HEAD from non-empty repo' '\n \tgit -c init.defaultBranch=main -c protocol.version=2 \\\n \t\tclone --bare \"file://$(pwd)/file_unborn_parent\" \\\n \t\tfile_unborn_child.git 2>stderr &&\n-\tgrep \"refs/heads/mydefaultbranch\" file_unborn_child.git/HEAD &&\n+\techo \"refs/heads/mydefaultbranch\" >expect &&\n+\tgit -C file_unborn_child.git symbolic-ref HEAD >actual &&\n+\ttest_cmp expect actual &&\n \t! grep \"warning:\" stderr\n '\n \n@@ -315,7 +325,9 @@ test_expect_success 'defaulted HEAD uses remote branch if available' '\n \tgit -c init.defaultBranch=branchwithstuff -c protocol.version=2 \\\n \t\tclone \"file://$(pwd)/file_unborn_parent\" \\\n \t\tfile_unborn_child 2>stderr &&\n-\tgrep \"refs/heads/branchwithstuff\" file_unborn_child/.git/HEAD &&\n+\techo \"refs/heads/branchwithstuff\" >expect &&\n+\tgit -C file_unborn_child symbolic-ref HEAD >actual &&\n+\ttest_cmp expect actual &&\n \ttest_path_is_file file_unborn_child/stuff.t &&\n \t! grep \"warning:\" stderr\n '\ndiff --git a/t/t9133-git-svn-nested-git-repo.sh b/t/t9133-git-svn-nested-git-repo.sh\nindex d8d536269cf..8ca24670acb 100755\n--- a/t/t9133-git-svn-nested-git-repo.sh\n+++ b/t/t9133-git-svn-nested-git-repo.sh\n@@ -11,7 +11,7 @@ test_expect_success 'setup repo with a git repo inside it' '\n \t(\n \t\tcd s &&\n \t\tgit init &&\n-\t\ttest -f .git/HEAD &&\n+\t\tgit symbolic-ref HEAD &&\n \t\t> .git/a &&\n \t\techo a > a &&\n \t\tsvn_cmd add .git a &&\n-- \n2.42.0\n\n"},{"id":"483790","messageId":"5e7937e790411a5fe8f3182328ca5a43529f499a.1698156169.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1698156169.git.ps@pks.im","subject":"[PATCH v2 4/9] t: convert tests to not access reflog via the filesystem","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-24T14:05:06Z","receivedAt":"2023-10-24T14:05:13Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Some of our tests reach directly into the filesystem in order to both\nread or modify the reflog, which will break once we have a second\nreference backend in our codebase that stores reflogs differently.\n\nRefactor these tests to either use git-reflog(1) or the ref-store test\nhelper. Note that the refactoring to use git-reflog(1) also requires us\nto adapt our expectations in some cases where we previously verified the\nexact on-disk log entries. This seems like an acceptable tradeoff though\nto ensure that different backends have the same user-visible behaviour\nas any user would typically use git-reflog(1) anyway to access the logs.\nAny backend-specific verification of the written on-disk format should\nbe implemented in a separate, backend-specific test.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t1400-update-ref.sh | 17 +++++++++++------\n t/t3200-branch.sh     | 24 ++++++++++++------------\n 2 files changed, 23 insertions(+), 18 deletions(-)\n\ndiff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh\nindex 51a8d0bba98..b7d1e5deede 100755\n--- a/t/t1400-update-ref.sh\n+++ b/t/t1400-update-ref.sh\n@@ -90,7 +90,8 @@ test_expect_success \"deleting current branch adds message to HEAD's log\" '\n \tgit symbolic-ref HEAD $m &&\n \tgit update-ref -m delete-$m -d $m &&\n \ttest_must_fail git show-ref --verify -q $m &&\n-\tgrep \"delete-$m$\" .git/logs/HEAD\n+\ttest-tool ref-store main for-each-reflog-ent HEAD >actual &&\n+\tgrep \"delete-$m$\" actual\n '\n \n test_expect_success \"deleting by HEAD adds message to HEAD's log\" '\n@@ -99,7 +100,8 @@ test_expect_success \"deleting by HEAD adds message to HEAD's log\" '\n \tgit symbolic-ref HEAD $m &&\n \tgit update-ref -m delete-by-head -d HEAD &&\n \ttest_must_fail git show-ref --verify -q $m &&\n-\tgrep \"delete-by-head$\" .git/logs/HEAD\n+\ttest-tool ref-store main for-each-reflog-ent HEAD >actual &&\n+\tgrep \"delete-by-head$\" actual\n '\n \n test_expect_success 'update-ref does not create reflogs by default' '\n@@ -130,7 +132,7 @@ test_expect_success 'creates no reflog in bare repository' '\n \n test_expect_success 'core.logAllRefUpdates=true creates reflog in bare repository' '\n \ttest_when_finished \"git -C $bare config --unset core.logAllRefUpdates && \\\n-\t\trm $bare/logs/$m\" &&\n+\t\ttest-tool ref-store main delete-reflog $m\" &&\n \tgit -C $bare config core.logAllRefUpdates true &&\n \tgit -C $bare update-ref $m $bareB &&\n \tgit -C $bare rev-parse $bareB >expect &&\n@@ -263,7 +265,10 @@ test_expect_success \"(not) changed .git/$m\" '\n \t! test $B = $(git show-ref -s --verify $m)\n '\n \n-rm -f .git/logs/refs/heads/main\n+test_expect_success \"clean up reflog\" '\n+\ttest-tool ref-store main delete-reflog $m\n+'\n+\n test_expect_success \"create $m (logged by touch)\" '\n \ttest_config core.logAllRefUpdates false &&\n \tGIT_COMMITTER_DATE=\"2005-05-26 23:30\" \\\n@@ -316,7 +321,7 @@ $A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150260 +0000\tSwitch\n $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000\n EOF\n test_expect_success \"verifying $m's log (logged by touch)\" '\n-\ttest_when_finished \"git update-ref -d $m && rm -rf .git/logs actual expect\" &&\n+\ttest_when_finished \"git update-ref -d $m && git reflog expire --expire=all --all && rm -rf actual expect\" &&\n \ttest-tool ref-store main for-each-reflog-ent $m >actual &&\n \ttest_cmp actual expect\n '\n@@ -346,7 +351,7 @@ $A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 +0000\tSwitch\n $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 +0000\n EOF\n test_expect_success \"verifying $m's log (logged by config)\" '\n-\ttest_when_finished \"git update-ref -d $m && rm -rf .git/logs actual expect\" &&\n+\ttest_when_finished \"git update-ref -d $m && git reflog expire --expire=all --all && rm -rf actual expect\" &&\n \ttest-tool ref-store main for-each-reflog-ent $m >actual &&\n \ttest_cmp actual expect\n '\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex 7d9393f190d..a38601dbbb7 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -73,14 +73,14 @@ test_expect_success 'git branch HEAD should fail' '\n '\n \n cat >expect <<EOF\n-$ZERO_OID $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000\tbranch: Created from main\n+$HEAD refs/heads/d/e/f@{0}: branch: Created from main\n EOF\n test_expect_success 'git branch --create-reflog d/e/f should create a branch and a log' '\n \tGIT_COMMITTER_DATE=\"2005-05-26 23:30\" \\\n \tgit -c core.logallrefupdates=false branch --create-reflog d/e/f &&\n \ttest_path_is_file .git/refs/heads/d/e/f &&\n-\ttest_path_is_file .git/logs/refs/heads/d/e/f &&\n-\ttest_cmp expect .git/logs/refs/heads/d/e/f\n+\tgit reflog show --no-abbrev-commit refs/heads/d/e/f >actual &&\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'git branch -d d/e/f should delete a branch and a log' '\n@@ -200,10 +200,9 @@ test_expect_success 'git branch -M baz bam should succeed when baz is checked ou\n \ttest $(git rev-parse --abbrev-ref HEAD) = bam\n '\n \n-test_expect_success 'git branch -M baz bam should add entries to .git/logs/HEAD' '\n-\tmsg=\"Branch: renamed refs/heads/baz to refs/heads/bam\" &&\n-\tgrep \" $ZERO_OID.*$msg$\" .git/logs/HEAD &&\n-\tgrep \"^$ZERO_OID.*$msg$\" .git/logs/HEAD\n+test_expect_success 'git branch -M baz bam should add entries to HEAD reflog' '\n+\tgit reflog show HEAD >actual &&\n+\tgrep \"HEAD@{0}: Branch: renamed refs/heads/baz to refs/heads/bam\" actual\n '\n \n test_expect_success 'git branch -M should leave orphaned HEAD alone' '\n@@ -225,7 +224,7 @@ test_expect_success 'git branch -M should leave orphaned HEAD alone' '\n test_expect_success 'resulting reflog can be shown by log -g' '\n \toid=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\tHEAD@{0} $oid $msg\n+\tHEAD@{0} $oid Branch: renamed refs/heads/baz to refs/heads/bam\n \tHEAD@{2} $oid checkout: moving from foo to baz\n \tEOF\n \tgit log -g --format=\"%gd %H %gs\" -2 HEAD >actual &&\n@@ -699,7 +698,8 @@ test_expect_success 'git branch -C c1 c2 should succeed when c1 is checked out'\n \n test_expect_success 'git branch -C c1 c2 should never touch HEAD' '\n \tmsg=\"Branch: copied refs/heads/c1 to refs/heads/c2\" &&\n-\t! grep \"$msg$\" .git/logs/HEAD\n+\tgit reflog HEAD >actual &&\n+\t! grep \"$msg$\" actual\n '\n \n test_expect_success 'git branch -C main should work when main is checked out' '\n@@ -1140,14 +1140,14 @@ test_expect_success '--set-upstream-to notices an error to set branch as own ups\n \n # Keep this test last, as it changes the current branch\n cat >expect <<EOF\n-$ZERO_OID $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000\tbranch: Created from main\n+$HEAD refs/heads/g/h/i@{0}: branch: Created from main\n EOF\n test_expect_success 'git checkout -b g/h/i -l should create a branch and a log' '\n \tGIT_COMMITTER_DATE=\"2005-05-26 23:30\" \\\n \tgit checkout -b g/h/i -l main &&\n \ttest_path_is_file .git/refs/heads/g/h/i &&\n-\ttest_path_is_file .git/logs/refs/heads/g/h/i &&\n-\ttest_cmp expect .git/logs/refs/heads/g/h/i\n+\tgit reflog show --no-abbrev-commit refs/heads/g/h/i >actual &&\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'checkout -b makes reflog by default' '\n-- \n2.42.0\n\n"},{"id":"483791","messageId":"089565a358eb28544f0ad6b83b8c47e1edf2db6f.1698156169.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1698156169.git.ps@pks.im","subject":"[PATCH v2 5/9] t1450: convert tests to remove worktrees via git-worktree(1)","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-24T14:05:10Z","receivedAt":"2023-10-24T14:05:16Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Some of our tests in t1450 create worktrees and then corrupt them.\nAs it is impossible to delete such worktrees via a normal call to `git\nworktree remove`, we instead opt to remove them manually by calling\nrm(1) instead.\n\nThis is ultimately unnecessary though as we can use the `-f` switch to\nremove the worktree. Let's convert the tests to do so such that we don't\nhave to reach into internal implementation details of worktrees.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t1450-fsck.sh | 8 ++++----\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex a3c97b9c7fc..a6af550867c 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -141,7 +141,7 @@ test_expect_success 'HEAD link pointing at a funny place' '\n \n test_expect_success 'HEAD link pointing at a funny object (from different wt)' '\n \ttest_when_finished \"git update-ref HEAD $orig_head\" &&\n-\ttest_when_finished \"rm -rf .git/worktrees wt\" &&\n+\ttest_when_finished \"git worktree remove -f wt\" &&\n \tgit worktree add wt &&\n \techo $ZERO_OID >.git/HEAD &&\n \t# avoid corrupt/broken HEAD from interfering with repo discovery\n@@ -150,7 +150,7 @@ test_expect_success 'HEAD link pointing at a funny object (from different wt)' '\n '\n \n test_expect_success 'other worktree HEAD link pointing at a funny object' '\n-\ttest_when_finished \"rm -rf .git/worktrees other\" &&\n+\ttest_when_finished \"git worktree remove -f other\" &&\n \tgit worktree add other &&\n \techo $ZERO_OID >.git/worktrees/other/HEAD &&\n \ttest_must_fail git fsck 2>out &&\n@@ -158,7 +158,7 @@ test_expect_success 'other worktree HEAD link pointing at a funny object' '\n '\n \n test_expect_success 'other worktree HEAD link pointing at missing object' '\n-\ttest_when_finished \"rm -rf .git/worktrees other\" &&\n+\ttest_when_finished \"git worktree remove -f other\" &&\n \tgit worktree add other &&\n \tobject_id=$(echo \"Contents missing from repo\" | git hash-object --stdin) &&\n \ttest-tool -C other ref-store main update-ref msg HEAD $object_id \"\" REF_NO_DEREF,REF_SKIP_OID_VERIFICATION &&\n@@ -167,7 +167,7 @@ test_expect_success 'other worktree HEAD link pointing at missing object' '\n '\n \n test_expect_success 'other worktree HEAD link pointing at a funny place' '\n-\ttest_when_finished \"rm -rf .git/worktrees other\" &&\n+\ttest_when_finished \"git worktree remove -f other\" &&\n \tgit worktree add other &&\n \tgit -C other symbolic-ref HEAD refs/funny/place &&\n \ttest_must_fail git fsck 2>out &&\n-- \n2.42.0\n\n"},{"id":"483792","messageId":"cb738888ed703fa56dc3c9f46e1afda81e66d8d0.1698156169.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1698156169.git.ps@pks.im","subject":"[PATCH v2 6/9] t4207: delete replace references via git-update-ref(1)","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-24T14:05:15Z","receivedAt":"2023-10-24T14:05:21Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"In t4207 we set up a set of replace objects via git-replace(1). Because\nthese references should not be impacting subsequent tests we also set up\nsome cleanup logic that deletes the replacement references via a call to\n`rm -rf`. This reaches into the internal implementation details of the\nreference backend and will thus break when we grow an alternative refdb\nimplementation.\n\nRefactor the tests to delete the replacement refs via Git commands so\nthat we become independent of the actual refdb that's in use. As we\ndon't have a nice way to delete all replacements or all references in a\ncertain namespace, we opt for a combination of git-for-each-ref(1) and\ngit-update-ref(1)'s `--stdin` mode.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t4207-log-decoration-colors.sh | 10 ++++++++--\n 1 file changed, 8 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh\nindex 21986a866df..73ea9e51550 100755\n--- a/t/t4207-log-decoration-colors.sh\n+++ b/t/t4207-log-decoration-colors.sh\n@@ -70,8 +70,14 @@ ${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_commit})${c_reset} A\n \tcmp_filtered_decorations\n '\n \n+remove_replace_refs () {\n+\tgit for-each-ref 'refs/replace*/**' --format='delete %(refname)' >in &&\n+\tgit update-ref --stdin <in &&\n+\trm in\n+}\n+\n test_expect_success 'test coloring with replace-objects' '\n-\ttest_when_finished rm -rf .git/refs/replace* &&\n+\ttest_when_finished remove_replace_refs &&\n \ttest_commit C &&\n \ttest_commit D &&\n \n@@ -99,7 +105,7 @@ EOF\n '\n \n test_expect_success 'test coloring with grafted commit' '\n-\ttest_when_finished rm -rf .git/refs/replace* &&\n+\ttest_when_finished remove_replace_refs &&\n \n \tgit replace --graft HEAD HEAD~2 &&\n \n-- \n2.42.0\n\n"},{"id":"483793","messageId":"e730e011de4cf00785af07a52d7b2745de009e48.1698156169.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1698156169.git.ps@pks.im","subject":"[PATCH v2 7/9] t7300: assert exact states of repo","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-24T14:05:19Z","receivedAt":"2023-10-24T14:05:26Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Some of the tests in t7300 verify that git-clean(1) doesn't touch\nrepositories that are embedded into the main repository. This is done by\nasserting a small set of substructures that are assumed to always exist,\nlike the \"refs/\", \"objects/\" or \"HEAD\". This has the downside that we\nneed to assume a specific repository structure that may be subject to\nchange when new backends for the refdb land. At the same time, we don't\nthoroughly assert that git-clean(1) really didn't end up cleaning any\nfiles in the repository either.\n\nConvert the tests to instead assert that all files continue to exist\nafter git-clean(1) by comparing a file listing via find(1) before and\nafter executing clean. This makes our actual assertions stricter while\nhaving to care less about the repository's actual on-disk format.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t7300-clean.sh | 23 ++++++++++++++---------\n 1 file changed, 14 insertions(+), 9 deletions(-)\n\ndiff --git a/t/t7300-clean.sh b/t/t7300-clean.sh\nindex 0ef7b784573..d7d9202f37f 100755\n--- a/t/t7300-clean.sh\n+++ b/t/t7300-clean.sh\n@@ -517,8 +517,12 @@ test_expect_success 'nested (empty) git should be kept' '\n \tgit init empty_repo &&\n \tmkdir to_clean &&\n \t>to_clean/should_clean.this &&\n+\t# Note that we put the expect file in the .git directory so that it\n+\t# does not get cleaned.\n+\tfind empty_repo | sort >.git/expect &&\n \tgit clean -f -d &&\n-\ttest_path_is_file empty_repo/.git/HEAD &&\n+\tfind empty_repo | sort >actual &&\n+\ttest_cmp .git/expect actual &&\n \ttest_path_is_missing to_clean\n '\n \n@@ -559,10 +563,10 @@ test_expect_success 'giving path in nested git work tree will NOT remove it' '\n \t\tmkdir -p bar/baz &&\n \t\ttest_commit msg bar/baz/hello.world\n \t) &&\n+\tfind repo | sort >expect &&\n \tgit clean -f -d repo/bar/baz &&\n-\ttest_path_is_file repo/.git/HEAD &&\n-\ttest_path_is_dir repo/bar/ &&\n-\ttest_path_is_file repo/bar/baz/hello.world\n+\tfind repo | sort >actual &&\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'giving path to nested .git will not remove it' '\n@@ -573,10 +577,10 @@ test_expect_success 'giving path to nested .git will not remove it' '\n \t\tgit init &&\n \t\ttest_commit msg hello.world\n \t) &&\n+\tfind repo | sort >expect &&\n \tgit clean -f -d repo/.git &&\n-\ttest_path_is_file repo/.git/HEAD &&\n-\ttest_path_is_dir repo/.git/refs &&\n-\ttest_path_is_dir repo/.git/objects &&\n+\tfind repo | sort >actual &&\n+\ttest_cmp expect actual &&\n \ttest_path_is_dir untracked/\n '\n \n@@ -588,9 +592,10 @@ test_expect_success 'giving path to nested .git/ will NOT remove contents' '\n \t\tgit init &&\n \t\ttest_commit msg hello.world\n \t) &&\n+\tfind repo | sort >expect &&\n \tgit clean -f -d repo/.git/ &&\n-\ttest_path_is_dir repo/.git &&\n-\ttest_path_is_file repo/.git/HEAD &&\n+\tfind repo | sort >actual &&\n+\ttest_cmp expect actual &&\n \ttest_path_is_dir untracked/\n '\n \n-- \n2.42.0\n\n"},{"id":"483794","messageId":"a1bdea523971f600032eed82e29368fb6bc1754b.1698156169.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1698156169.git.ps@pks.im","subject":"[PATCH v2 8/9] t7900: assert the absence of refs via git-for-each-ref(1)","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-24T14:05:23Z","receivedAt":"2023-10-24T14:05:30Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"We're asserting that a prefetch of remotes via git-maintenance(1)\ndoesn't write any references in refs/remotes by validating that the\ndirectory \".git/refs/remotes\" is missing. This is quite roundabout: we\ndon't care about the directory existing, we care about the references\nnot existing, and the way these are stored is on the behest of the\nreference database.\n\nConvert the test to instead check via git-for-each-ref(1) whether any\nremote reference exist.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t7900-maintenance.sh | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh\nindex e56f5980dc4..cefecee732f 100755\n--- a/t/t7900-maintenance.sh\n+++ b/t/t7900-maintenance.sh\n@@ -157,7 +157,8 @@ test_expect_success 'prefetch multiple remotes' '\n \tfetchargs=\"--prefetch --prune --no-tags --no-write-fetch-head --recurse-submodules=no --quiet\" &&\n \ttest_subcommand git fetch remote1 $fetchargs <run-prefetch.txt &&\n \ttest_subcommand git fetch remote2 $fetchargs <run-prefetch.txt &&\n-\ttest_path_is_missing .git/refs/remotes &&\n+\tgit for-each-ref refs/remotes >actual &&\n+\ttest_must_be_empty actual &&\n \tgit log prefetch/remotes/remote1/one &&\n \tgit log prefetch/remotes/remote2/two &&\n \tgit fetch --all &&\n-- \n2.42.0\n\n"},{"id":"483795","messageId":"497e43ae5c362da9ad972e2813dc907df58a8676.1698156169.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1698156169.git.ps@pks.im","subject":"[PATCH v2 9/9] t: mark several tests that assume the files backend with REFFILES","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-10-24T14:05:28Z","receivedAt":"2023-10-24T14:05:36Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Add the REFFILES prerequisite to several tests that assume we're using\nthe files backend. There are various reasons why we cannot easily\nconvert those tests to be backend-independent, where the most common\none is that we have no way to write corrupt references into the refdb\nvia our tooling. We may at a later point in time grow the tooling to\nmake this possible, but for now we just mark these tests as requiring\nthe files backend.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t1400-update-ref.sh            |  8 ++++----\n t/t1450-fsck.sh                  |  6 +++---\n t/t2011-checkout-invalid-head.sh | 16 ++++++++--------\n t/t3200-branch.sh                |  8 ++++----\n t/t3400-rebase.sh                |  2 +-\n t/t5605-clone-local.sh           |  2 +-\n 6 files changed, 21 insertions(+), 21 deletions(-)\n\ndiff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh\nindex b7d1e5deede..70a760ba378 100755\n--- a/t/t1400-update-ref.sh\n+++ b/t/t1400-update-ref.sh\n@@ -236,7 +236,7 @@ test_expect_success 'update-ref --no-deref -d can delete self-reference' '\n \ttest_must_fail git show-ref --verify -q refs/heads/self\n '\n \n-test_expect_success 'update-ref --no-deref -d can delete reference to bad ref' '\n+test_expect_success REFFILES 'update-ref --no-deref -d can delete reference to bad ref' '\n \t>.git/refs/heads/bad &&\n \ttest_when_finished \"rm -f .git/refs/heads/bad\" &&\n \tgit symbolic-ref refs/heads/ref-to-bad refs/heads/bad &&\n@@ -288,7 +288,7 @@ test_expect_success \"set $m (logged by touch)\" '\n \ttest $A = $(git show-ref -s --verify $m)\n '\n \n-test_expect_success 'empty directory removal' '\n+test_expect_success REFFILES 'empty directory removal' '\n \tgit branch d1/d2/r1 HEAD &&\n \tgit branch d1/r2 HEAD &&\n \ttest_path_is_file .git/refs/heads/d1/d2/r1 &&\n@@ -300,7 +300,7 @@ test_expect_success 'empty directory removal' '\n \ttest_path_is_file .git/logs/refs/heads/d1/r2\n '\n \n-test_expect_success 'symref empty directory removal' '\n+test_expect_success REFFILES 'symref empty directory removal' '\n \tgit branch e1/e2/r1 HEAD &&\n \tgit branch e1/r2 HEAD &&\n \tgit checkout e1/e2/r1 &&\n@@ -1639,7 +1639,7 @@ test_expect_success PIPE 'transaction flushes status updates' '\n \ttest_cmp expected actual\n '\n \n-test_expect_success 'directory not created deleting packed ref' '\n+test_expect_success REFFILES 'directory not created deleting packed ref' '\n \tgit branch d1/d2/r1 HEAD &&\n \tgit pack-refs --all &&\n \ttest_path_is_missing .git/refs/heads/d1/d2 &&\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex a6af550867c..50b15bd7fc0 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -123,7 +123,7 @@ test_expect_success 'branch pointing to non-commit' '\n \ttest_i18ngrep \"not a commit\" out\n '\n \n-test_expect_success 'HEAD link pointing at a funny object' '\n+test_expect_success REFFILES 'HEAD link pointing at a funny object' '\n \ttest_when_finished \"git update-ref HEAD $orig_head\" &&\n \techo $ZERO_OID >.git/HEAD &&\n \t# avoid corrupt/broken HEAD from interfering with repo discovery\n@@ -139,7 +139,7 @@ test_expect_success 'HEAD link pointing at a funny place' '\n \ttest_i18ngrep \"HEAD points to something strange\" out\n '\n \n-test_expect_success 'HEAD link pointing at a funny object (from different wt)' '\n+test_expect_success REFFILES 'HEAD link pointing at a funny object (from different wt)' '\n \ttest_when_finished \"git update-ref HEAD $orig_head\" &&\n \ttest_when_finished \"git worktree remove -f wt\" &&\n \tgit worktree add wt &&\n@@ -149,7 +149,7 @@ test_expect_success 'HEAD link pointing at a funny object (from different wt)' '\n \ttest_i18ngrep \"main-worktree/HEAD: detached HEAD points\" out\n '\n \n-test_expect_success 'other worktree HEAD link pointing at a funny object' '\n+test_expect_success REFFILES 'other worktree HEAD link pointing at a funny object' '\n \ttest_when_finished \"git worktree remove -f other\" &&\n \tgit worktree add other &&\n \techo $ZERO_OID >.git/worktrees/other/HEAD &&\ndiff --git a/t/t2011-checkout-invalid-head.sh b/t/t2011-checkout-invalid-head.sh\nindex d9997e7b6b4..3c8135831b8 100755\n--- a/t/t2011-checkout-invalid-head.sh\n+++ b/t/t2011-checkout-invalid-head.sh\n@@ -18,18 +18,18 @@ test_expect_success 'checkout should not start branch from a tree' '\n \ttest_must_fail git checkout -b newbranch main^{tree}\n '\n \n-test_expect_success 'checkout main from invalid HEAD' '\n+test_expect_success REFFILES 'checkout main from invalid HEAD' '\n \techo $ZERO_OID >.git/HEAD &&\n \tgit checkout main --\n '\n \n-test_expect_success 'checkout notices failure to lock HEAD' '\n+test_expect_success REFFILES 'checkout notices failure to lock HEAD' '\n \ttest_when_finished \"rm -f .git/HEAD.lock\" &&\n \t>.git/HEAD.lock &&\n \ttest_must_fail git checkout -b other\n '\n \n-test_expect_success 'create ref directory/file conflict scenario' '\n+test_expect_success REFFILES 'create ref directory/file conflict scenario' '\n \tgit update-ref refs/heads/outer/inner main &&\n \n \t# do not rely on symbolic-ref to get a known state,\n@@ -39,26 +39,26 @@ test_expect_success 'create ref directory/file conflict scenario' '\n \t}\n '\n \n-test_expect_success 'checkout away from d/f HEAD (unpacked, to branch)' '\n+test_expect_success REFFILES 'checkout away from d/f HEAD (unpacked, to branch)' '\n \treset_to_df &&\n \tgit checkout main\n '\n \n-test_expect_success 'checkout away from d/f HEAD (unpacked, to detached)' '\n+test_expect_success REFFILES 'checkout away from d/f HEAD (unpacked, to detached)' '\n \treset_to_df &&\n \tgit checkout --detach main\n '\n \n-test_expect_success 'pack refs' '\n+test_expect_success REFFILES 'pack refs' '\n \tgit pack-refs --all --prune\n '\n \n-test_expect_success 'checkout away from d/f HEAD (packed, to branch)' '\n+test_expect_success REFFILES 'checkout away from d/f HEAD (packed, to branch)' '\n \treset_to_df &&\n \tgit checkout main\n '\n \n-test_expect_success 'checkout away from d/f HEAD (packed, to detached)' '\n+test_expect_success REFFILES 'checkout away from d/f HEAD (packed, to detached)' '\n \treset_to_df &&\n \tgit checkout --detach main\n '\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex a38601dbbb7..8d82f9bc52a 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -28,7 +28,7 @@ test_expect_success 'git branch --help should not have created a bogus branch' '\n \ttest_path_is_missing .git/refs/heads/--help\n '\n \n-test_expect_success 'branch -h in broken repository' '\n+test_expect_success REFFILES 'branch -h in broken repository' '\n \tmkdir broken &&\n \t(\n \t\tcd broken &&\n@@ -242,7 +242,7 @@ test_expect_success 'git branch -M baz bam should succeed when baz is checked ou\n \tgit worktree prune\n '\n \n-test_expect_success 'git branch -M fails if updating any linked working tree fails' '\n+test_expect_success REFFILES 'git branch -M fails if updating any linked working tree fails' '\n \tgit worktree add -b baz bazdir1 &&\n \tgit worktree add -f bazdir2 baz &&\n \ttouch .git/worktrees/bazdir1/HEAD.lock &&\n@@ -833,14 +833,14 @@ test_expect_success 'renaming a symref is not allowed' '\n \ttest_path_is_missing .git/refs/heads/new-topic\n '\n \n-test_expect_success SYMLINKS 'git branch -m u v should fail when the reflog for u is a symlink' '\n+test_expect_success SYMLINKS,REFFILES 'git branch -m u v should fail when the reflog for u is a symlink' '\n \tgit branch --create-reflog u &&\n \tmv .git/logs/refs/heads/u real-u &&\n \tln -s real-u .git/logs/refs/heads/u &&\n \ttest_must_fail git branch -m u v\n '\n \n-test_expect_success SYMLINKS 'git branch -m with symlinked .git/refs' '\n+test_expect_success SYMLINKS,REFFILES 'git branch -m with symlinked .git/refs' '\n \ttest_when_finished \"rm -rf subdir\" &&\n \tgit init --bare subdir &&\n \ndiff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh\nindex d3df19a51f8..435943a0891 100755\n--- a/t/t3400-rebase.sh\n+++ b/t/t3400-rebase.sh\n@@ -424,7 +424,7 @@ test_expect_success 'refuse to switch to branch checked out elsewhere' '\n \ttest_i18ngrep \"already used by worktree at\" err\n '\n \n-test_expect_success MINGW,SYMLINKS_WINDOWS 'rebase when .git/logs is a symlink' '\n+test_expect_success REFFILES,MINGW,SYMLINKS_WINDOWS 'rebase when .git/logs is a symlink' '\n \tgit checkout main &&\n \tmv .git/logs actual_logs &&\n \tcmd //c \"mklink /D .git\\logs ..\\actual_logs\" &&\ndiff --git a/t/t5605-clone-local.sh b/t/t5605-clone-local.sh\nindex a26689de379..8ad6445eca7 100755\n--- a/t/t5605-clone-local.sh\n+++ b/t/t5605-clone-local.sh\n@@ -157,7 +157,7 @@ test_expect_success 'cloning locally respects \"-u\" for fetching refs' '\n \ttest_must_fail git clone --bare -u false a should_not_work.git\n '\n \n-test_expect_success 'local clone from repo with corrupt refs fails gracefully' '\n+test_expect_success REFFILES 'local clone from repo with corrupt refs fails gracefully' '\n \tgit init corrupt &&\n \ttest_commit -C corrupt one &&\n \techo a >corrupt/.git/refs/heads/topic &&\n-- \n2.42.0\n\n"},{"id":"483949","messageId":"CAPig+cTbtT1xu+CcypmPYuvxhtY8VE3SfGppshL7QmvaervaTg@mail.gmail.com","threadId":"60390","inReplyTo":"089565a358eb28544f0ad6b83b8c47e1edf2db6f.1698156169.git.ps@pks.im","subject":"Re: [PATCH v2 5/9] t1450: convert tests to remove worktrees via git-worktree(1)","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2023-10-27T02:42:18Z","receivedAt":"2023-10-27T02:42:33Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tue, Oct 24, 2023 at 10:05 AM Patrick Steinhardt <ps@pks.im> wrote:\n> Some of our tests in t1450 create worktrees and then corrupt them.\n> As it is impossible to delete such worktrees via a normal call to `git\n> worktree remove`, we instead opt to remove them manually by calling\n> rm(1) instead.\n>\n> This is ultimately unnecessary though as we can use the `-f` switch to\n> remove the worktree. Let's convert the tests to do so such that we don't\n> have to reach into internal implementation details of worktrees.\n>\n> Signed-off-by: Patrick Steinhardt <ps@pks.im>\n> ---\n> diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\n> @@ -141,7 +141,7 @@ test_expect_success 'HEAD link pointing at a funny place' '\n>  test_expect_success 'HEAD link pointing at a funny object (from different wt)' '\n>         test_when_finished \"git update-ref HEAD $orig_head\" &&\n> -       test_when_finished \"rm -rf .git/worktrees wt\" &&\n> +       test_when_finished \"git worktree remove -f wt\" &&\n>         git worktree add wt &&\n>         echo $ZERO_OID >.git/HEAD &&\n\nTechnically, this is a change of behavior since the original code\nremoved the entire .git/worktrees directory, which deleted\nadministrative metainformation for _all_ worktrees, whereas the new\ncode only deletes administrative metadata for the mentioned worktree.\nHowever, since there are no other existing worktrees at this point in\nany of these tests, the result is functionally the same, so the change\nof behavior is immaterial. Moreover, the revised code has a smaller\nblast-radius, which may be a desirable property since there has been\nsome movement toward making tests more self-contained so that they can\nbe run individually more easily.\n"},{"id":"484334","messageId":"3caf9e3f28f2c2b8c87d95307852299b66d0fe12.1698914571.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1698914571.git.ps@pks.im","subject":"[PATCH v3 1/9] t: allow skipping expected object ID in `ref-store update-ref`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-11-02T08:46:41Z","receivedAt":"2023-11-02T08:46:51Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"We require the caller to pass both the old and new expected object ID to\nour `test-tool ref-store update-ref` helper. When trying to update a\nsymbolic reference though it's impossible to specify the expected object\nID, which means that the test would instead have to force-update the\nreference. This is currently impossible though.\n\nUpdate the helper to optionally skip verification of the old object ID\nin case the test passes in an empty old object ID as input.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/helper/test-ref-store.c | 11 +++++++----\n 1 file changed, 7 insertions(+), 4 deletions(-)\n\ndiff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\nindex 48552e6a9e0..702ec1f128a 100644\n--- a/t/helper/test-ref-store.c\n+++ b/t/helper/test-ref-store.c\n@@ -298,16 +298,19 @@ static int cmd_update_ref(struct ref_store *refs, const char **argv)\n \tconst char *new_sha1_buf = notnull(*argv++, \"new-sha1\");\n \tconst char *old_sha1_buf = notnull(*argv++, \"old-sha1\");\n \tunsigned int flags = arg_flags(*argv++, \"flags\", transaction_flags);\n-\tstruct object_id old_oid;\n+\tstruct object_id old_oid, *old_oid_ptr = NULL;\n \tstruct object_id new_oid;\n \n-\tif (get_oid_hex(old_sha1_buf, &old_oid))\n-\t\tdie(\"cannot parse %s as %s\", old_sha1_buf, the_hash_algo->name);\n+\tif (*old_sha1_buf) {\n+\t\tif (get_oid_hex(old_sha1_buf, &old_oid))\n+\t\t\tdie(\"cannot parse %s as %s\", old_sha1_buf, the_hash_algo->name);\n+\t\told_oid_ptr = &old_oid;\n+\t}\n \tif (get_oid_hex(new_sha1_buf, &new_oid))\n \t\tdie(\"cannot parse %s as %s\", new_sha1_buf, the_hash_algo->name);\n \n \treturn refs_update_ref(refs, msg, refname,\n-\t\t\t       &new_oid, &old_oid,\n+\t\t\t       &new_oid, old_oid_ptr,\n \t\t\t       flags, UPDATE_REFS_DIE_ON_ERR);\n }\n \n-- \n2.42.0\n\n"},{"id":"484335","messageId":"cover.1698914571.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1697607222.git.ps@pks.im","subject":"[PATCH v3 0/9] t: reduce direct disk access to data structures","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-11-02T08:46:36Z","receivedAt":"2023-11-02T08:46:52Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Hi,\n\nthis is the third version of my patch series that aims to reduce access\nto on-disk data structures in favor of using plumbing tools where\npossible.\n\nThere are no changes compared to v2 except for a rebase on top of\n0497e6c611 (t: use git-show-ref(1) to check for ref existence,\n2023-10-31), which has been merged to \"next\" now and conflicts with this\npatch series.\n\nPatrick\n\nPatrick Steinhardt (9):\n  t: allow skipping expected object ID in `ref-store update-ref`\n  t: convert tests to not write references via the filesystem\n  t: convert tests to not access symrefs via the filesystem\n  t: convert tests to not access reflog via the filesystem\n  t1450: convert tests to remove worktrees via git-worktree(1)\n  t4207: delete replace references via git-update-ref(1)\n  t7300: assert exact states of repo\n  t7900: assert the absence of refs via git-for-each-ref(1)\n  t: mark several tests that assume the files backend with REFFILES\n\n t/helper/test-ref-store.c          | 11 ++++---\n t/t1400-update-ref.sh              | 50 ++++++++++++++++--------------\n t/t1430-bad-ref-name.sh            | 12 +++----\n t/t1450-fsck.sh                    | 44 +++++++++++++-------------\n t/t2011-checkout-invalid-head.sh   | 16 +++++-----\n t/t3200-branch.sh                  | 41 ++++++++++++------------\n t/t3400-rebase.sh                  |  2 +-\n t/t3404-rebase-interactive.sh      |  2 +-\n t/t4013-diff-various.sh            |  2 +-\n t/t4202-log.sh                     |  2 +-\n t/t4207-log-decoration-colors.sh   | 10 ++++--\n t/t5526-fetch-submodules.sh        |  2 +-\n t/t5605-clone-local.sh             |  4 +--\n t/t5702-protocol-v2.sh             | 24 ++++++++++----\n t/t7300-clean.sh                   | 23 ++++++++------\n t/t7900-maintenance.sh             |  3 +-\n t/t9133-git-svn-nested-git-repo.sh |  2 +-\n 17 files changed, 142 insertions(+), 108 deletions(-)\n\nRange-diff against v2:\n 1:  c868198f8c1 =  1:  3caf9e3f28f t: allow skipping expected object ID in `ref-store update-ref`\n 2:  4c0939d868e =  2:  1884b7006cf t: convert tests to not write references via the filesystem\n 3:  048583ed2c3 !  3:  d6307177132 t: convert tests to not access symrefs via the filesystem\n    @@ t/t1430-bad-ref-name.sh: test_expect_success 'update-ref -d can delete broken na\n     -\tprintf \"ref: refs/heads/main\\n\" >.git/refs/heads/broken...symref &&\n     +\ttest-tool ref-store main create-symref refs/heads/broken...symref refs/heads/main &&\n      \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n    + \ttest_ref_exists refs/heads/broken...symref &&\n      \tgit update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&\n    - \ttest_path_is_missing .git/refs/heads/broken...symref &&\n     @@ t/t1430-bad-ref-name.sh: test_expect_success 'update-ref --no-deref -d can delete symref with broken name\n      '\n      \n    @@ t/t1430-bad-ref-name.sh: test_expect_success 'update-ref --no-deref -d can delet\n     -\tprintf \"ref: refs/heads/main\\n\" >.git/refs/heads/broken...symref &&\n     +\ttest-tool ref-store main create-symref refs/heads/broken...symref refs/heads/main &&\n      \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n    + \ttest_ref_exists refs/heads/broken...symref &&\n      \tgit branch -d broken...symref >output 2>error &&\n    - \ttest_path_is_missing .git/refs/heads/broken...symref &&\n     @@ t/t1430-bad-ref-name.sh: test_expect_success 'branch -d can delete symref with broken name' '\n      '\n      \n    @@ t/t1430-bad-ref-name.sh: test_expect_success 'branch -d can delete symref with b\n     -\tprintf \"ref: refs/heads/idonotexist\\n\" >.git/refs/heads/broken...symref &&\n     +\ttest-tool ref-store main create-symref refs/heads/broken...symref refs/heads/idonotexist &&\n      \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n    + \ttest_ref_exists refs/heads/broken...symref &&\n      \tgit update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&\n    - \ttest_path_is_missing .git/refs/heads/broken...symref &&\n     @@ t/t1430-bad-ref-name.sh: test_expect_success 'update-ref --no-deref -d can delete dangling symref with br\n      '\n      \n    @@ t/t1430-bad-ref-name.sh: test_expect_success 'update-ref --no-deref -d can delet\n     -\tprintf \"ref: refs/heads/idonotexist\\n\" >.git/refs/heads/broken...symref &&\n     +\ttest-tool ref-store main create-symref refs/heads/broken...symref refs/heads/idonotexist &&\n      \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n    + \ttest_ref_exists refs/heads/broken...symref &&\n      \tgit branch -d broken...symref >output 2>error &&\n    - \ttest_path_is_missing .git/refs/heads/broken...symref &&\n     \n      ## t/t1450-fsck.sh ##\n     @@ t/t1450-fsck.sh: test_expect_success 'HEAD link pointing at a funny object' '\n    @@ t/t3200-branch.sh: test_expect_success 'git branch -M should leave orphaned HEAD\n     +\t\tgit symbolic-ref HEAD >expect &&\n     +\t\techo refs/heads/lonely >actual &&\n     +\t\ttest_cmp expect actual &&\n    - \t\ttest_path_is_missing .git/refs/head/lonely &&\n    + \t\ttest_ref_missing refs/head/lonely &&\n      \t\tgit branch -M main mistress &&\n     -\t\tgrep lonely .git/HEAD\n     +\t\tgit symbolic-ref HEAD >expect &&\n    @@ t/t3200-branch.sh: test_expect_success 'deleting a symref' '\n     +\tgit symbolic-ref --no-recurse refs/heads/dangling-symref &&\n      \techo \"Deleted branch dangling-symref (was nowhere).\" >expect &&\n      \tgit branch -d dangling-symref >actual &&\n    - \ttest_path_is_missing .git/refs/heads/dangling-symref &&\n    + \ttest_ref_missing refs/heads/dangling-symref &&\n     \n      ## t/t4013-diff-various.sh ##\n     @@ t/t4013-diff-various.sh: test_expect_success 'log -S requires an argument' '\n 4:  5e7937e7904 !  4:  fdf67689227 t: convert tests to not access reflog via the filesystem\n    @@ t/t3200-branch.sh: test_expect_success 'git branch HEAD should fail' '\n      test_expect_success 'git branch --create-reflog d/e/f should create a branch and a log' '\n      \tGIT_COMMITTER_DATE=\"2005-05-26 23:30\" \\\n      \tgit -c core.logallrefupdates=false branch --create-reflog d/e/f &&\n    - \ttest_path_is_file .git/refs/heads/d/e/f &&\n    + \ttest_ref_exists refs/heads/d/e/f &&\n     -\ttest_path_is_file .git/logs/refs/heads/d/e/f &&\n     -\ttest_cmp expect .git/logs/refs/heads/d/e/f\n     +\tgit reflog show --no-abbrev-commit refs/heads/d/e/f >actual &&\n    @@ t/t3200-branch.sh: test_expect_success '--set-upstream-to notices an error to se\n      test_expect_success 'git checkout -b g/h/i -l should create a branch and a log' '\n      \tGIT_COMMITTER_DATE=\"2005-05-26 23:30\" \\\n      \tgit checkout -b g/h/i -l main &&\n    - \ttest_path_is_file .git/refs/heads/g/h/i &&\n    + \ttest_ref_exists refs/heads/g/h/i &&\n     -\ttest_path_is_file .git/logs/refs/heads/g/h/i &&\n     -\ttest_cmp expect .git/logs/refs/heads/g/h/i\n     +\tgit reflog show --no-abbrev-commit refs/heads/g/h/i >actual &&\n 5:  089565a358e =  5:  a5d55b68828 t1450: convert tests to remove worktrees via git-worktree(1)\n 6:  cb738888ed7 =  6:  3213d45b537 t4207: delete replace references via git-update-ref(1)\n 7:  e730e011de4 =  7:  36e79f266c2 t7300: assert exact states of repo\n 8:  a1bdea52397 =  8:  4af0dc00165 t7900: assert the absence of refs via git-for-each-ref(1)\n 9:  497e43ae5c3 !  9:  9fc039a1764 t: mark several tests that assume the files backend with REFFILES\n    @@ t/t2011-checkout-invalid-head.sh: test_expect_success 'create ref directory/file\n     \n      ## t/t3200-branch.sh ##\n     @@ t/t3200-branch.sh: test_expect_success 'git branch --help should not have created a bogus branch' '\n    - \ttest_path_is_missing .git/refs/heads/--help\n    + \ttest_ref_missing refs/heads/--help\n      '\n      \n     -test_expect_success 'branch -h in broken repository' '\n    @@ t/t3200-branch.sh: test_expect_success 'git branch -M baz bam should succeed whe\n      \tgit worktree add -f bazdir2 baz &&\n      \ttouch .git/worktrees/bazdir1/HEAD.lock &&\n     @@ t/t3200-branch.sh: test_expect_success 'renaming a symref is not allowed' '\n    - \ttest_path_is_missing .git/refs/heads/new-topic\n    + \ttest_ref_missing refs/heads/new-topic\n      '\n      \n     -test_expect_success SYMLINKS 'git branch -m u v should fail when the reflog for u is a symlink' '\n\nbase-commit: 0497e6c61172faabf412e013f2fc30a05054a46d\n-- \n2.42.0\n\n"},{"id":"484336","messageId":"1884b7006cf72404a69b05ccf4668d75497148a1.1698914571.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1698914571.git.ps@pks.im","subject":"[PATCH v3 2/9] t: convert tests to not write references via the filesystem","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-11-02T08:46:45Z","receivedAt":"2023-11-02T08:46:56Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Some of our tests manually create, update or delete references by\nwriting the respective new values into the filesystem directly. While\nthis works with the current files reference backend, this will break\nonce we have a second reference backend implementation in our codebase.\n\nRefactor these tests to instead use git-update-ref(1) or our `ref-store`\ntest tool. The latter is required in some cases where safety checks of\ngit-update-ref(1) would otherwise reject a reference update.\n\nWhile at it, refactor some of the tests to schedule the cleanup command\nvia `test_when_finished` before modifying the repository.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t1400-update-ref.sh         | 17 ++++++++---------\n t/t1450-fsck.sh               | 26 +++++++++++++-------------\n t/t3404-rebase-interactive.sh |  2 +-\n t/t5526-fetch-submodules.sh   |  2 +-\n 4 files changed, 23 insertions(+), 24 deletions(-)\n\ndiff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh\nindex 4d66cd7f4a1..91cc6dff724 100755\n--- a/t/t1400-update-ref.sh\n+++ b/t/t1400-update-ref.sh\n@@ -9,8 +9,6 @@ test_description='Test git update-ref and basic ref logging'\n Z=$ZERO_OID\n \n m=refs/heads/main\n-n_dir=refs/heads/gu\n-n=$n_dir/fixes\n outside=refs/foo\n bare=bare-repo\n \n@@ -62,10 +60,10 @@ test_expect_success \"delete $m without oldvalue verification\" '\n \ttest_must_fail git show-ref --verify -q $m\n '\n \n-test_expect_success \"fail to create $n\" '\n-\ttest_when_finished \"rm -f .git/$n_dir\" &&\n-\ttouch .git/$n_dir &&\n-\ttest_must_fail git update-ref $n $A\n+test_expect_success \"fail to create $n due to file/directory conflict\" '\n+\ttest_when_finished \"git update-ref -d refs/heads/gu\" &&\n+\tgit update-ref refs/heads/gu $A &&\n+\ttest_must_fail git update-ref refs/heads/gu/fixes $A\n '\n \n test_expect_success \"create $m (by HEAD)\" '\n@@ -221,16 +219,16 @@ test_expect_success 'delete symref without dereference when the referred ref is\n '\n \n test_expect_success 'update-ref -d is not confused by self-reference' '\n+\ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self\" &&\n \tgit symbolic-ref refs/heads/self refs/heads/self &&\n-\ttest_when_finished \"rm -f .git/refs/heads/self\" &&\n \ttest_path_is_file .git/refs/heads/self &&\n \ttest_must_fail git update-ref -d refs/heads/self &&\n \ttest_path_is_file .git/refs/heads/self\n '\n \n test_expect_success 'update-ref --no-deref -d can delete self-reference' '\n+\ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self\" &&\n \tgit symbolic-ref refs/heads/self refs/heads/self &&\n-\ttest_when_finished \"rm -f .git/refs/heads/self\" &&\n \ttest_path_is_file .git/refs/heads/self &&\n \tgit update-ref --no-deref -d refs/heads/self &&\n \ttest_must_fail git show-ref --verify -q refs/heads/self\n@@ -434,7 +432,8 @@ test_expect_success 'Query \"main@{2005-05-28}\" (past end of history)' '\n \ttest_i18ngrep -F \"warning: log for ref $m unexpectedly ended on $ld\" e\n '\n \n-rm -f .git/$m .git/logs/$m expect\n+rm -f expect\n+git update-ref -d $m\n \n test_expect_success 'creating initial files' '\n \ttest_when_finished rm -f M &&\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 10a539158c4..f55b539b083 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -15,6 +15,7 @@ test_expect_success setup '\n \tgit config --unset i18n.commitencoding &&\n \tgit checkout HEAD^0 &&\n \ttest_commit B fileB two &&\n+\torig_head=$(git rev-parse HEAD) &&\n \tgit tag -d A B &&\n \tgit reflog expire --expire=now --all\n '\n@@ -115,15 +116,15 @@ test_expect_success 'zlib corrupt loose object output ' '\n '\n \n test_expect_success 'branch pointing to non-commit' '\n-\tgit rev-parse HEAD^{tree} >.git/refs/heads/invalid &&\n+\ttree_oid=$(git rev-parse --verify HEAD^{tree}) &&\n \ttest_when_finished \"git update-ref -d refs/heads/invalid\" &&\n+\ttest-tool ref-store main update-ref msg refs/heads/invalid $tree_oid $ZERO_OID REF_SKIP_OID_VERIFICATION &&\n \ttest_must_fail git fsck 2>out &&\n \ttest_i18ngrep \"not a commit\" out\n '\n \n test_expect_success 'HEAD link pointing at a funny object' '\n-\ttest_when_finished \"mv .git/SAVED_HEAD .git/HEAD\" &&\n-\tmv .git/HEAD .git/SAVED_HEAD &&\n+\ttest_when_finished \"git update-ref HEAD $orig_head\" &&\n \techo $ZERO_OID >.git/HEAD &&\n \t# avoid corrupt/broken HEAD from interfering with repo discovery\n \ttest_must_fail env GIT_DIR=.git git fsck 2>out &&\n@@ -131,8 +132,7 @@ test_expect_success 'HEAD link pointing at a funny object' '\n '\n \n test_expect_success 'HEAD link pointing at a funny place' '\n-\ttest_when_finished \"mv .git/SAVED_HEAD .git/HEAD\" &&\n-\tmv .git/HEAD .git/SAVED_HEAD &&\n+\ttest_when_finished \"git update-ref --no-deref HEAD $orig_head\" &&\n \techo \"ref: refs/funny/place\" >.git/HEAD &&\n \t# avoid corrupt/broken HEAD from interfering with repo discovery\n \ttest_must_fail env GIT_DIR=.git git fsck 2>out &&\n@@ -140,10 +140,9 @@ test_expect_success 'HEAD link pointing at a funny place' '\n '\n \n test_expect_success 'HEAD link pointing at a funny object (from different wt)' '\n-\ttest_when_finished \"mv .git/SAVED_HEAD .git/HEAD\" &&\n+\ttest_when_finished \"git update-ref HEAD $orig_head\" &&\n \ttest_when_finished \"rm -rf .git/worktrees wt\" &&\n \tgit worktree add wt &&\n-\tmv .git/HEAD .git/SAVED_HEAD &&\n \techo $ZERO_OID >.git/HEAD &&\n \t# avoid corrupt/broken HEAD from interfering with repo discovery\n \ttest_must_fail git -C wt fsck 2>out &&\n@@ -161,7 +160,8 @@ test_expect_success 'other worktree HEAD link pointing at a funny object' '\n test_expect_success 'other worktree HEAD link pointing at missing object' '\n \ttest_when_finished \"rm -rf .git/worktrees other\" &&\n \tgit worktree add other &&\n-\techo \"Contents missing from repo\" | git hash-object --stdin >.git/worktrees/other/HEAD &&\n+\tobject_id=$(echo \"Contents missing from repo\" | git hash-object --stdin) &&\n+\ttest-tool -C other ref-store main update-ref msg HEAD $object_id \"\" REF_NO_DEREF,REF_SKIP_OID_VERIFICATION &&\n \ttest_must_fail git fsck 2>out &&\n \ttest_i18ngrep \"worktrees/other/HEAD: invalid sha1 pointer\" out\n '\n@@ -391,7 +391,7 @@ test_expect_success 'tag pointing to nonexistent' '\n \n \ttag=$(git hash-object -t tag -w --stdin <invalid-tag) &&\n \ttest_when_finished \"remove_object $tag\" &&\n-\techo $tag >.git/refs/tags/invalid &&\n+\tgit update-ref refs/tags/invalid $tag &&\n \ttest_when_finished \"git update-ref -d refs/tags/invalid\" &&\n \ttest_must_fail git fsck --tags >out &&\n \ttest_i18ngrep \"broken link\" out\n@@ -411,7 +411,7 @@ test_expect_success 'tag pointing to something else than its type' '\n \n \ttag=$(git hash-object -t tag -w --stdin <wrong-tag) &&\n \ttest_when_finished \"remove_object $tag\" &&\n-\techo $tag >.git/refs/tags/wrong &&\n+\tgit update-ref refs/tags/wrong $tag &&\n \ttest_when_finished \"git update-ref -d refs/tags/wrong\" &&\n \ttest_must_fail git fsck --tags\n '\n@@ -428,7 +428,7 @@ test_expect_success 'tag with incorrect tag name & missing tagger' '\n \n \ttag=$(git hash-object --literally -t tag -w --stdin <wrong-tag) &&\n \ttest_when_finished \"remove_object $tag\" &&\n-\techo $tag >.git/refs/tags/wrong &&\n+\tgit update-ref refs/tags/wrong $tag &&\n \ttest_when_finished \"git update-ref -d refs/tags/wrong\" &&\n \tgit fsck --tags 2>out &&\n \n@@ -452,7 +452,7 @@ test_expect_success 'tag with bad tagger' '\n \n \ttag=$(git hash-object --literally -t tag -w --stdin <wrong-tag) &&\n \ttest_when_finished \"remove_object $tag\" &&\n-\techo $tag >.git/refs/tags/wrong &&\n+\tgit update-ref refs/tags/wrong $tag &&\n \ttest_when_finished \"git update-ref -d refs/tags/wrong\" &&\n \ttest_must_fail git fsck --tags 2>out &&\n \ttest_i18ngrep \"error in tag .*: invalid author/committer\" out\n@@ -471,7 +471,7 @@ test_expect_success 'tag with NUL in header' '\n \n \ttag=$(git hash-object --literally -t tag -w --stdin <tag-NUL-header) &&\n \ttest_when_finished \"remove_object $tag\" &&\n-\techo $tag >.git/refs/tags/wrong &&\n+\tgit update-ref refs/tags/wrong $tag &&\n \ttest_when_finished \"git update-ref -d refs/tags/wrong\" &&\n \ttest_must_fail git fsck --tags 2>out &&\n \ttest_i18ngrep \"error in tag $tag.*unterminated header: NUL at offset\" out\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex 8ea2bf13026..d2a7a91f170 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -2160,7 +2160,7 @@ test_expect_success '--update-refs: check failed ref update' '\n \t# recorded in the update-refs file. We will force-update the\n \t# \"second\" ref, but \"git branch -f\" will not work because of\n \t# the lock in the update-refs file.\n-\tgit rev-parse third >.git/refs/heads/second &&\n+\tgit update-ref refs/heads/second third &&\n \n \ttest_must_fail git rebase --continue 2>err &&\n \tgrep \"update_ref failed for ref '\\''refs/heads/second'\\''\" err &&\ndiff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh\nindex 26e933f93ae..7ab220fa313 100755\n--- a/t/t5526-fetch-submodules.sh\n+++ b/t/t5526-fetch-submodules.sh\n@@ -771,7 +771,7 @@ test_expect_success 'fetching submodule into a broken repository' '\n \tgit -C dst fetch --recurse-submodules &&\n \n \t# Break the receiving submodule\n-\trm -f dst/sub/.git/HEAD &&\n+\ttest-tool -C dst/sub ref-store main delete-refs REF_NO_DEREF msg HEAD &&\n \n \t# NOTE: without the fix the following tests will recurse forever!\n \t# They should terminate with an error.\n-- \n2.42.0\n\n"},{"id":"484337","messageId":"d6307177132b57034d7544d49713013bdad2e891.1698914571.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1698914571.git.ps@pks.im","subject":"[PATCH v3 3/9] t: convert tests to not access symrefs via the filesystem","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-11-02T08:46:49Z","receivedAt":"2023-11-02T08:46:57Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Some of our tests access symbolic references via the filesystem\ndirectly. While this works with the current files reference backend, it\nthis will break once we have a second reference backend in our codebase.\n\nRefactor these tests to instead use git-symbolic-ref(1) or our\n`ref-store` test tool. The latter is required in some cases where safety\nchecks of git-symbolic-ref(1) would otherwise reject writing a symbolic\nreference.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t1400-update-ref.sh              |  8 ++++----\n t/t1430-bad-ref-name.sh            | 12 ++++++------\n t/t1450-fsck.sh                    |  4 ++--\n t/t3200-branch.sh                  |  9 ++++++---\n t/t4013-diff-various.sh            |  2 +-\n t/t4202-log.sh                     |  2 +-\n t/t5605-clone-local.sh             |  2 +-\n t/t5702-protocol-v2.sh             | 24 ++++++++++++++++++------\n t/t9133-git-svn-nested-git-repo.sh |  2 +-\n 9 files changed, 40 insertions(+), 25 deletions(-)\n\ndiff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh\nindex 91cc6dff724..51a8d0bba98 100755\n--- a/t/t1400-update-ref.sh\n+++ b/t/t1400-update-ref.sh\n@@ -221,15 +221,15 @@ test_expect_success 'delete symref without dereference when the referred ref is\n test_expect_success 'update-ref -d is not confused by self-reference' '\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self\" &&\n \tgit symbolic-ref refs/heads/self refs/heads/self &&\n-\ttest_path_is_file .git/refs/heads/self &&\n+\tgit symbolic-ref --no-recurse refs/heads/self &&\n \ttest_must_fail git update-ref -d refs/heads/self &&\n-\ttest_path_is_file .git/refs/heads/self\n+\tgit symbolic-ref --no-recurse refs/heads/self\n '\n \n test_expect_success 'update-ref --no-deref -d can delete self-reference' '\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self\" &&\n \tgit symbolic-ref refs/heads/self refs/heads/self &&\n-\ttest_path_is_file .git/refs/heads/self &&\n+\tgit symbolic-ref --no-recurse refs/heads/self &&\n \tgit update-ref --no-deref -d refs/heads/self &&\n \ttest_must_fail git show-ref --verify -q refs/heads/self\n '\n@@ -239,7 +239,7 @@ test_expect_success 'update-ref --no-deref -d can delete reference to bad ref' '\n \ttest_when_finished \"rm -f .git/refs/heads/bad\" &&\n \tgit symbolic-ref refs/heads/ref-to-bad refs/heads/bad &&\n \ttest_when_finished \"git update-ref -d refs/heads/ref-to-bad\" &&\n-\ttest_path_is_file .git/refs/heads/ref-to-bad &&\n+\tgit symbolic-ref --no-recurse refs/heads/ref-to-bad &&\n \tgit update-ref --no-deref -d refs/heads/ref-to-bad &&\n \ttest_must_fail git show-ref --verify -q refs/heads/ref-to-bad\n '\ndiff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh\nindex 7b7d6953c62..5debb91f7b7 100755\n--- a/t/t1430-bad-ref-name.sh\n+++ b/t/t1430-bad-ref-name.sh\n@@ -164,9 +164,9 @@ test_expect_success 'rev-parse skips symref pointing to broken name' '\n test_expect_success 'for-each-ref emits warnings for broken names' '\n \ttest-tool ref-store main update-ref msg \"refs/heads/broken...ref\" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref\" &&\n-\tprintf \"ref: refs/heads/broken...ref\\n\" >.git/refs/heads/badname &&\n+\ttest-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname\" &&\n-\tprintf \"ref: refs/heads/main\\n\" >.git/refs/heads/broken...symref &&\n+\ttest-tool ref-store main create-symref refs/heads/broken...symref refs/heads/main &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n \tgit for-each-ref >output 2>error &&\n \t! grep -e \"broken\\.\\.\\.ref\" output &&\n@@ -257,7 +257,7 @@ test_expect_success 'update-ref -d can delete broken name through symref' '\n '\n \n test_expect_success 'update-ref --no-deref -d can delete symref with broken name' '\n-\tprintf \"ref: refs/heads/main\\n\" >.git/refs/heads/broken...symref &&\n+\ttest-tool ref-store main create-symref refs/heads/broken...symref refs/heads/main &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n \ttest_ref_exists refs/heads/broken...symref &&\n \tgit update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&\n@@ -267,7 +267,7 @@ test_expect_success 'update-ref --no-deref -d can delete symref with broken name\n '\n \n test_expect_success 'branch -d can delete symref with broken name' '\n-\tprintf \"ref: refs/heads/main\\n\" >.git/refs/heads/broken...symref &&\n+\ttest-tool ref-store main create-symref refs/heads/broken...symref refs/heads/main &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n \ttest_ref_exists refs/heads/broken...symref &&\n \tgit branch -d broken...symref >output 2>error &&\n@@ -277,7 +277,7 @@ test_expect_success 'branch -d can delete symref with broken name' '\n '\n \n test_expect_success 'update-ref --no-deref -d can delete dangling symref with broken name' '\n-\tprintf \"ref: refs/heads/idonotexist\\n\" >.git/refs/heads/broken...symref &&\n+\ttest-tool ref-store main create-symref refs/heads/broken...symref refs/heads/idonotexist &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n \ttest_ref_exists refs/heads/broken...symref &&\n \tgit update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&\n@@ -287,7 +287,7 @@ test_expect_success 'update-ref --no-deref -d can delete dangling symref with br\n '\n \n test_expect_success 'branch -d can delete dangling symref with broken name' '\n-\tprintf \"ref: refs/heads/idonotexist\\n\" >.git/refs/heads/broken...symref &&\n+\ttest-tool ref-store main create-symref refs/heads/broken...symref refs/heads/idonotexist &&\n \ttest_when_finished \"test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref\" &&\n \ttest_ref_exists refs/heads/broken...symref &&\n \tgit branch -d broken...symref >output 2>error &&\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex f55b539b083..a3c97b9c7fc 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -133,7 +133,7 @@ test_expect_success 'HEAD link pointing at a funny object' '\n \n test_expect_success 'HEAD link pointing at a funny place' '\n \ttest_when_finished \"git update-ref --no-deref HEAD $orig_head\" &&\n-\techo \"ref: refs/funny/place\" >.git/HEAD &&\n+\ttest-tool ref-store main create-symref HEAD refs/funny/place &&\n \t# avoid corrupt/broken HEAD from interfering with repo discovery\n \ttest_must_fail env GIT_DIR=.git git fsck 2>out &&\n \ttest_i18ngrep \"HEAD points to something strange\" out\n@@ -169,7 +169,7 @@ test_expect_success 'other worktree HEAD link pointing at missing object' '\n test_expect_success 'other worktree HEAD link pointing at a funny place' '\n \ttest_when_finished \"rm -rf .git/worktrees other\" &&\n \tgit worktree add other &&\n-\techo \"ref: refs/funny/place\" >.git/worktrees/other/HEAD &&\n+\tgit -C other symbolic-ref HEAD refs/funny/place &&\n \ttest_must_fail git fsck 2>out &&\n \ttest_i18ngrep \"worktrees/other/HEAD points to something strange\" out\n '\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex bde4f1485b7..874520e3f10 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -215,10 +215,13 @@ test_expect_success 'git branch -M should leave orphaned HEAD alone' '\n \t\tcd orphan &&\n \t\ttest_commit initial &&\n \t\tgit checkout --orphan lonely &&\n-\t\tgrep lonely .git/HEAD &&\n+\t\tgit symbolic-ref HEAD >expect &&\n+\t\techo refs/heads/lonely >actual &&\n+\t\ttest_cmp expect actual &&\n \t\ttest_ref_missing refs/head/lonely &&\n \t\tgit branch -M main mistress &&\n-\t\tgrep lonely .git/HEAD\n+\t\tgit symbolic-ref HEAD >expect &&\n+\t\ttest_cmp expect actual\n \t)\n '\n \n@@ -809,7 +812,7 @@ test_expect_success 'deleting a symref' '\n \n test_expect_success 'deleting a dangling symref' '\n \tgit symbolic-ref refs/heads/dangling-symref nowhere &&\n-\ttest_path_is_file .git/refs/heads/dangling-symref &&\n+\tgit symbolic-ref --no-recurse refs/heads/dangling-symref &&\n \techo \"Deleted branch dangling-symref (was nowhere).\" >expect &&\n \tgit branch -d dangling-symref >actual &&\n \ttest_ref_missing refs/heads/dangling-symref &&\ndiff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh\nindex 4b474808311..fc8a0588acc 100755\n--- a/t/t4013-diff-various.sh\n+++ b/t/t4013-diff-various.sh\n@@ -522,7 +522,7 @@ test_expect_success 'log -S requires an argument' '\n '\n \n test_expect_success 'diff --cached on unborn branch' '\n-\techo ref: refs/heads/unborn >.git/HEAD &&\n+\tgit symbolic-ref HEAD refs/heads/unborn &&\n \tgit diff --cached >result &&\n \tprocess_diffs result >actual &&\n \tprocess_diffs \"$TEST_DIRECTORY/t4013/diff.diff_--cached\" >expected &&\ndiff --git a/t/t4202-log.sh b/t/t4202-log.sh\nindex af4a123cd22..57b298a4e22 100755\n--- a/t/t4202-log.sh\n+++ b/t/t4202-log.sh\n@@ -2265,7 +2265,7 @@ test_expect_success REFFILES 'log diagnoses bogus HEAD hash' '\n \n test_expect_success REFFILES 'log diagnoses bogus HEAD symref' '\n \tgit init empty &&\n-\techo \"ref: refs/heads/invalid.lock\" > empty/.git/HEAD &&\n+\ttest-tool -C empty ref-store main create-symref HEAD refs/heads/invalid.lock &&\n \ttest_must_fail git -C empty log 2>stderr &&\n \ttest_i18ngrep broken stderr &&\n \ttest_must_fail git -C empty log --default totally-bogus 2>stderr &&\ndiff --git a/t/t5605-clone-local.sh b/t/t5605-clone-local.sh\nindex 946c5751885..bedd29d0550 100755\n--- a/t/t5605-clone-local.sh\n+++ b/t/t5605-clone-local.sh\n@@ -65,7 +65,7 @@ test_expect_success 'Even without -l, local will make a hardlink' '\n '\n \n test_expect_success 'local clone of repo with nonexistent ref in HEAD' '\n-\techo \"ref: refs/heads/nonexistent\" > a.git/HEAD &&\n+\tgit -C a.git symbolic-ref HEAD refs/heads/nonexistent &&\n \tgit clone a d &&\n \t(cd d &&\n \tgit fetch &&\ndiff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh\nindex 6af5c2062fd..dcc4cd95fe7 100755\n--- a/t/t5702-protocol-v2.sh\n+++ b/t/t5702-protocol-v2.sh\n@@ -221,7 +221,9 @@ test_expect_success 'clone of empty repo propagates name of default branch' '\n \tGIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \\\n \tgit -c init.defaultBranch=main -c protocol.version=2 \\\n \t\tclone \"file://$(pwd)/file_empty_parent\" file_empty_child &&\n-\tgrep \"refs/heads/mydefaultbranch\" file_empty_child/.git/HEAD\n+\techo refs/heads/mydefaultbranch >expect &&\n+\tgit -C file_empty_child symbolic-ref HEAD >actual &&\n+\ttest_cmp expect actual\n '\n \n test_expect_success '...but not if explicitly forbidden by config' '\n@@ -234,7 +236,9 @@ test_expect_success '...but not if explicitly forbidden by config' '\n \tGIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \\\n \tgit -c init.defaultBranch=main -c protocol.version=2 \\\n \t\tclone \"file://$(pwd)/file_empty_parent\" file_empty_child &&\n-\t! grep \"refs/heads/mydefaultbranch\" file_empty_child/.git/HEAD\n+\techo refs/heads/main >expect &&\n+\tgit -C file_empty_child symbolic-ref HEAD >actual &&\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'bare clone propagates empty default branch' '\n@@ -247,7 +251,9 @@ test_expect_success 'bare clone propagates empty default branch' '\n \tgit -c init.defaultBranch=main -c protocol.version=2 \\\n \t\tclone --bare \\\n \t\t\"file://$(pwd)/file_empty_parent\" file_empty_child.git &&\n-\tgrep \"refs/heads/mydefaultbranch\" file_empty_child.git/HEAD\n+\techo \"refs/heads/mydefaultbranch\" >expect &&\n+\tgit -C file_empty_child.git symbolic-ref HEAD >actual &&\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'clone propagates unborn HEAD from non-empty repo' '\n@@ -265,7 +271,9 @@ test_expect_success 'clone propagates unborn HEAD from non-empty repo' '\n \tgit -c init.defaultBranch=main -c protocol.version=2 \\\n \t\tclone \"file://$(pwd)/file_unborn_parent\" \\\n \t\tfile_unborn_child 2>stderr &&\n-\tgrep \"refs/heads/mydefaultbranch\" file_unborn_child/.git/HEAD &&\n+\techo \"refs/heads/mydefaultbranch\" >expect &&\n+\tgit -C file_unborn_child symbolic-ref HEAD >actual &&\n+\ttest_cmp expect actual &&\n \tgrep \"warning: remote HEAD refers to nonexistent ref\" stderr\n '\n \n@@ -295,7 +303,9 @@ test_expect_success 'bare clone propagates unborn HEAD from non-empty repo' '\n \tgit -c init.defaultBranch=main -c protocol.version=2 \\\n \t\tclone --bare \"file://$(pwd)/file_unborn_parent\" \\\n \t\tfile_unborn_child.git 2>stderr &&\n-\tgrep \"refs/heads/mydefaultbranch\" file_unborn_child.git/HEAD &&\n+\techo \"refs/heads/mydefaultbranch\" >expect &&\n+\tgit -C file_unborn_child.git symbolic-ref HEAD >actual &&\n+\ttest_cmp expect actual &&\n \t! grep \"warning:\" stderr\n '\n \n@@ -315,7 +325,9 @@ test_expect_success 'defaulted HEAD uses remote branch if available' '\n \tgit -c init.defaultBranch=branchwithstuff -c protocol.version=2 \\\n \t\tclone \"file://$(pwd)/file_unborn_parent\" \\\n \t\tfile_unborn_child 2>stderr &&\n-\tgrep \"refs/heads/branchwithstuff\" file_unborn_child/.git/HEAD &&\n+\techo \"refs/heads/branchwithstuff\" >expect &&\n+\tgit -C file_unborn_child symbolic-ref HEAD >actual &&\n+\ttest_cmp expect actual &&\n \ttest_path_is_file file_unborn_child/stuff.t &&\n \t! grep \"warning:\" stderr\n '\ndiff --git a/t/t9133-git-svn-nested-git-repo.sh b/t/t9133-git-svn-nested-git-repo.sh\nindex d8d536269cf..8ca24670acb 100755\n--- a/t/t9133-git-svn-nested-git-repo.sh\n+++ b/t/t9133-git-svn-nested-git-repo.sh\n@@ -11,7 +11,7 @@ test_expect_success 'setup repo with a git repo inside it' '\n \t(\n \t\tcd s &&\n \t\tgit init &&\n-\t\ttest -f .git/HEAD &&\n+\t\tgit symbolic-ref HEAD &&\n \t\t> .git/a &&\n \t\techo a > a &&\n \t\tsvn_cmd add .git a &&\n-- \n2.42.0\n\n"},{"id":"484338","messageId":"fdf6768922739322854fa3ccfa295228f279ac5a.1698914571.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1698914571.git.ps@pks.im","subject":"[PATCH v3 4/9] t: convert tests to not access reflog via the filesystem","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-11-02T08:46:53Z","receivedAt":"2023-11-02T08:47:03Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Some of our tests reach directly into the filesystem in order to both\nread or modify the reflog, which will break once we have a second\nreference backend in our codebase that stores reflogs differently.\n\nRefactor these tests to either use git-reflog(1) or the ref-store test\nhelper. Note that the refactoring to use git-reflog(1) also requires us\nto adapt our expectations in some cases where we previously verified the\nexact on-disk log entries. This seems like an acceptable tradeoff though\nto ensure that different backends have the same user-visible behaviour\nas any user would typically use git-reflog(1) anyway to access the logs.\nAny backend-specific verification of the written on-disk format should\nbe implemented in a separate, backend-specific test.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t1400-update-ref.sh | 17 +++++++++++------\n t/t3200-branch.sh     | 24 ++++++++++++------------\n 2 files changed, 23 insertions(+), 18 deletions(-)\n\ndiff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh\nindex 51a8d0bba98..b7d1e5deede 100755\n--- a/t/t1400-update-ref.sh\n+++ b/t/t1400-update-ref.sh\n@@ -90,7 +90,8 @@ test_expect_success \"deleting current branch adds message to HEAD's log\" '\n \tgit symbolic-ref HEAD $m &&\n \tgit update-ref -m delete-$m -d $m &&\n \ttest_must_fail git show-ref --verify -q $m &&\n-\tgrep \"delete-$m$\" .git/logs/HEAD\n+\ttest-tool ref-store main for-each-reflog-ent HEAD >actual &&\n+\tgrep \"delete-$m$\" actual\n '\n \n test_expect_success \"deleting by HEAD adds message to HEAD's log\" '\n@@ -99,7 +100,8 @@ test_expect_success \"deleting by HEAD adds message to HEAD's log\" '\n \tgit symbolic-ref HEAD $m &&\n \tgit update-ref -m delete-by-head -d HEAD &&\n \ttest_must_fail git show-ref --verify -q $m &&\n-\tgrep \"delete-by-head$\" .git/logs/HEAD\n+\ttest-tool ref-store main for-each-reflog-ent HEAD >actual &&\n+\tgrep \"delete-by-head$\" actual\n '\n \n test_expect_success 'update-ref does not create reflogs by default' '\n@@ -130,7 +132,7 @@ test_expect_success 'creates no reflog in bare repository' '\n \n test_expect_success 'core.logAllRefUpdates=true creates reflog in bare repository' '\n \ttest_when_finished \"git -C $bare config --unset core.logAllRefUpdates && \\\n-\t\trm $bare/logs/$m\" &&\n+\t\ttest-tool ref-store main delete-reflog $m\" &&\n \tgit -C $bare config core.logAllRefUpdates true &&\n \tgit -C $bare update-ref $m $bareB &&\n \tgit -C $bare rev-parse $bareB >expect &&\n@@ -263,7 +265,10 @@ test_expect_success \"(not) changed .git/$m\" '\n \t! test $B = $(git show-ref -s --verify $m)\n '\n \n-rm -f .git/logs/refs/heads/main\n+test_expect_success \"clean up reflog\" '\n+\ttest-tool ref-store main delete-reflog $m\n+'\n+\n test_expect_success \"create $m (logged by touch)\" '\n \ttest_config core.logAllRefUpdates false &&\n \tGIT_COMMITTER_DATE=\"2005-05-26 23:30\" \\\n@@ -316,7 +321,7 @@ $A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150260 +0000\tSwitch\n $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000\n EOF\n test_expect_success \"verifying $m's log (logged by touch)\" '\n-\ttest_when_finished \"git update-ref -d $m && rm -rf .git/logs actual expect\" &&\n+\ttest_when_finished \"git update-ref -d $m && git reflog expire --expire=all --all && rm -rf actual expect\" &&\n \ttest-tool ref-store main for-each-reflog-ent $m >actual &&\n \ttest_cmp actual expect\n '\n@@ -346,7 +351,7 @@ $A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 +0000\tSwitch\n $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 +0000\n EOF\n test_expect_success \"verifying $m's log (logged by config)\" '\n-\ttest_when_finished \"git update-ref -d $m && rm -rf .git/logs actual expect\" &&\n+\ttest_when_finished \"git update-ref -d $m && git reflog expire --expire=all --all && rm -rf actual expect\" &&\n \ttest-tool ref-store main for-each-reflog-ent $m >actual &&\n \ttest_cmp actual expect\n '\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex 874520e3f10..933aa9eebbd 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -76,14 +76,14 @@ test_expect_success 'git branch HEAD should fail' '\n '\n \n cat >expect <<EOF\n-$ZERO_OID $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000\tbranch: Created from main\n+$HEAD refs/heads/d/e/f@{0}: branch: Created from main\n EOF\n test_expect_success 'git branch --create-reflog d/e/f should create a branch and a log' '\n \tGIT_COMMITTER_DATE=\"2005-05-26 23:30\" \\\n \tgit -c core.logallrefupdates=false branch --create-reflog d/e/f &&\n \ttest_ref_exists refs/heads/d/e/f &&\n-\ttest_path_is_file .git/logs/refs/heads/d/e/f &&\n-\ttest_cmp expect .git/logs/refs/heads/d/e/f\n+\tgit reflog show --no-abbrev-commit refs/heads/d/e/f >actual &&\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'git branch -d d/e/f should delete a branch and a log' '\n@@ -203,10 +203,9 @@ test_expect_success 'git branch -M baz bam should succeed when baz is checked ou\n \ttest $(git rev-parse --abbrev-ref HEAD) = bam\n '\n \n-test_expect_success 'git branch -M baz bam should add entries to .git/logs/HEAD' '\n-\tmsg=\"Branch: renamed refs/heads/baz to refs/heads/bam\" &&\n-\tgrep \" $ZERO_OID.*$msg$\" .git/logs/HEAD &&\n-\tgrep \"^$ZERO_OID.*$msg$\" .git/logs/HEAD\n+test_expect_success 'git branch -M baz bam should add entries to HEAD reflog' '\n+\tgit reflog show HEAD >actual &&\n+\tgrep \"HEAD@{0}: Branch: renamed refs/heads/baz to refs/heads/bam\" actual\n '\n \n test_expect_success 'git branch -M should leave orphaned HEAD alone' '\n@@ -228,7 +227,7 @@ test_expect_success 'git branch -M should leave orphaned HEAD alone' '\n test_expect_success 'resulting reflog can be shown by log -g' '\n \toid=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\tHEAD@{0} $oid $msg\n+\tHEAD@{0} $oid Branch: renamed refs/heads/baz to refs/heads/bam\n \tHEAD@{2} $oid checkout: moving from foo to baz\n \tEOF\n \tgit log -g --format=\"%gd %H %gs\" -2 HEAD >actual &&\n@@ -702,7 +701,8 @@ test_expect_success 'git branch -C c1 c2 should succeed when c1 is checked out'\n \n test_expect_success 'git branch -C c1 c2 should never touch HEAD' '\n \tmsg=\"Branch: copied refs/heads/c1 to refs/heads/c2\" &&\n-\t! grep \"$msg$\" .git/logs/HEAD\n+\tgit reflog HEAD >actual &&\n+\t! grep \"$msg$\" actual\n '\n \n test_expect_success 'git branch -C main should work when main is checked out' '\n@@ -1143,14 +1143,14 @@ test_expect_success '--set-upstream-to notices an error to set branch as own ups\n \n # Keep this test last, as it changes the current branch\n cat >expect <<EOF\n-$ZERO_OID $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000\tbranch: Created from main\n+$HEAD refs/heads/g/h/i@{0}: branch: Created from main\n EOF\n test_expect_success 'git checkout -b g/h/i -l should create a branch and a log' '\n \tGIT_COMMITTER_DATE=\"2005-05-26 23:30\" \\\n \tgit checkout -b g/h/i -l main &&\n \ttest_ref_exists refs/heads/g/h/i &&\n-\ttest_path_is_file .git/logs/refs/heads/g/h/i &&\n-\ttest_cmp expect .git/logs/refs/heads/g/h/i\n+\tgit reflog show --no-abbrev-commit refs/heads/g/h/i >actual &&\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'checkout -b makes reflog by default' '\n-- \n2.42.0\n\n"},{"id":"484339","messageId":"a5d55b6882845d1ae0885b3fc879d3167cb0e816.1698914571.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1698914571.git.ps@pks.im","subject":"[PATCH v3 5/9] t1450: convert tests to remove worktrees via git-worktree(1)","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-11-02T08:46:57Z","receivedAt":"2023-11-02T08:47:07Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Some of our tests in t1450 create worktrees and then corrupt them.\nAs it is impossible to delete such worktrees via a normal call to `git\nworktree remove`, we instead opt to remove them manually by calling\nrm(1) instead.\n\nThis is ultimately unnecessary though as we can use the `-f` switch to\nremove the worktree. Let's convert the tests to do so such that we don't\nhave to reach into internal implementation details of worktrees.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t1450-fsck.sh | 8 ++++----\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex a3c97b9c7fc..a6af550867c 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -141,7 +141,7 @@ test_expect_success 'HEAD link pointing at a funny place' '\n \n test_expect_success 'HEAD link pointing at a funny object (from different wt)' '\n \ttest_when_finished \"git update-ref HEAD $orig_head\" &&\n-\ttest_when_finished \"rm -rf .git/worktrees wt\" &&\n+\ttest_when_finished \"git worktree remove -f wt\" &&\n \tgit worktree add wt &&\n \techo $ZERO_OID >.git/HEAD &&\n \t# avoid corrupt/broken HEAD from interfering with repo discovery\n@@ -150,7 +150,7 @@ test_expect_success 'HEAD link pointing at a funny object (from different wt)' '\n '\n \n test_expect_success 'other worktree HEAD link pointing at a funny object' '\n-\ttest_when_finished \"rm -rf .git/worktrees other\" &&\n+\ttest_when_finished \"git worktree remove -f other\" &&\n \tgit worktree add other &&\n \techo $ZERO_OID >.git/worktrees/other/HEAD &&\n \ttest_must_fail git fsck 2>out &&\n@@ -158,7 +158,7 @@ test_expect_success 'other worktree HEAD link pointing at a funny object' '\n '\n \n test_expect_success 'other worktree HEAD link pointing at missing object' '\n-\ttest_when_finished \"rm -rf .git/worktrees other\" &&\n+\ttest_when_finished \"git worktree remove -f other\" &&\n \tgit worktree add other &&\n \tobject_id=$(echo \"Contents missing from repo\" | git hash-object --stdin) &&\n \ttest-tool -C other ref-store main update-ref msg HEAD $object_id \"\" REF_NO_DEREF,REF_SKIP_OID_VERIFICATION &&\n@@ -167,7 +167,7 @@ test_expect_success 'other worktree HEAD link pointing at missing object' '\n '\n \n test_expect_success 'other worktree HEAD link pointing at a funny place' '\n-\ttest_when_finished \"rm -rf .git/worktrees other\" &&\n+\ttest_when_finished \"git worktree remove -f other\" &&\n \tgit worktree add other &&\n \tgit -C other symbolic-ref HEAD refs/funny/place &&\n \ttest_must_fail git fsck 2>out &&\n-- \n2.42.0\n\n"},{"id":"484340","messageId":"3213d45b5372a225d16971ebc9dcb5dc2b1005c6.1698914571.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1698914571.git.ps@pks.im","subject":"[PATCH v3 6/9] t4207: delete replace references via git-update-ref(1)","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-11-02T08:47:01Z","receivedAt":"2023-11-02T08:47:11Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"In t4207 we set up a set of replace objects via git-replace(1). Because\nthese references should not be impacting subsequent tests we also set up\nsome cleanup logic that deletes the replacement references via a call to\n`rm -rf`. This reaches into the internal implementation details of the\nreference backend and will thus break when we grow an alternative refdb\nimplementation.\n\nRefactor the tests to delete the replacement refs via Git commands so\nthat we become independent of the actual refdb that's in use. As we\ndon't have a nice way to delete all replacements or all references in a\ncertain namespace, we opt for a combination of git-for-each-ref(1) and\ngit-update-ref(1)'s `--stdin` mode.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t4207-log-decoration-colors.sh | 10 ++++++++--\n 1 file changed, 8 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh\nindex 21986a866df..73ea9e51550 100755\n--- a/t/t4207-log-decoration-colors.sh\n+++ b/t/t4207-log-decoration-colors.sh\n@@ -70,8 +70,14 @@ ${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_commit})${c_reset} A\n \tcmp_filtered_decorations\n '\n \n+remove_replace_refs () {\n+\tgit for-each-ref 'refs/replace*/**' --format='delete %(refname)' >in &&\n+\tgit update-ref --stdin <in &&\n+\trm in\n+}\n+\n test_expect_success 'test coloring with replace-objects' '\n-\ttest_when_finished rm -rf .git/refs/replace* &&\n+\ttest_when_finished remove_replace_refs &&\n \ttest_commit C &&\n \ttest_commit D &&\n \n@@ -99,7 +105,7 @@ EOF\n '\n \n test_expect_success 'test coloring with grafted commit' '\n-\ttest_when_finished rm -rf .git/refs/replace* &&\n+\ttest_when_finished remove_replace_refs &&\n \n \tgit replace --graft HEAD HEAD~2 &&\n \n-- \n2.42.0\n\n"},{"id":"484341","messageId":"36e79f266c219142d105424c29eb3f1b9fad6398.1698914571.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1698914571.git.ps@pks.im","subject":"[PATCH v3 7/9] t7300: assert exact states of repo","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-11-02T08:47:06Z","receivedAt":"2023-11-02T08:47:16Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Some of the tests in t7300 verify that git-clean(1) doesn't touch\nrepositories that are embedded into the main repository. This is done by\nasserting a small set of substructures that are assumed to always exist,\nlike the \"refs/\", \"objects/\" or \"HEAD\". This has the downside that we\nneed to assume a specific repository structure that may be subject to\nchange when new backends for the refdb land. At the same time, we don't\nthoroughly assert that git-clean(1) really didn't end up cleaning any\nfiles in the repository either.\n\nConvert the tests to instead assert that all files continue to exist\nafter git-clean(1) by comparing a file listing via find(1) before and\nafter executing clean. This makes our actual assertions stricter while\nhaving to care less about the repository's actual on-disk format.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t7300-clean.sh | 23 ++++++++++++++---------\n 1 file changed, 14 insertions(+), 9 deletions(-)\n\ndiff --git a/t/t7300-clean.sh b/t/t7300-clean.sh\nindex 0ef7b784573..d7d9202f37f 100755\n--- a/t/t7300-clean.sh\n+++ b/t/t7300-clean.sh\n@@ -517,8 +517,12 @@ test_expect_success 'nested (empty) git should be kept' '\n \tgit init empty_repo &&\n \tmkdir to_clean &&\n \t>to_clean/should_clean.this &&\n+\t# Note that we put the expect file in the .git directory so that it\n+\t# does not get cleaned.\n+\tfind empty_repo | sort >.git/expect &&\n \tgit clean -f -d &&\n-\ttest_path_is_file empty_repo/.git/HEAD &&\n+\tfind empty_repo | sort >actual &&\n+\ttest_cmp .git/expect actual &&\n \ttest_path_is_missing to_clean\n '\n \n@@ -559,10 +563,10 @@ test_expect_success 'giving path in nested git work tree will NOT remove it' '\n \t\tmkdir -p bar/baz &&\n \t\ttest_commit msg bar/baz/hello.world\n \t) &&\n+\tfind repo | sort >expect &&\n \tgit clean -f -d repo/bar/baz &&\n-\ttest_path_is_file repo/.git/HEAD &&\n-\ttest_path_is_dir repo/bar/ &&\n-\ttest_path_is_file repo/bar/baz/hello.world\n+\tfind repo | sort >actual &&\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'giving path to nested .git will not remove it' '\n@@ -573,10 +577,10 @@ test_expect_success 'giving path to nested .git will not remove it' '\n \t\tgit init &&\n \t\ttest_commit msg hello.world\n \t) &&\n+\tfind repo | sort >expect &&\n \tgit clean -f -d repo/.git &&\n-\ttest_path_is_file repo/.git/HEAD &&\n-\ttest_path_is_dir repo/.git/refs &&\n-\ttest_path_is_dir repo/.git/objects &&\n+\tfind repo | sort >actual &&\n+\ttest_cmp expect actual &&\n \ttest_path_is_dir untracked/\n '\n \n@@ -588,9 +592,10 @@ test_expect_success 'giving path to nested .git/ will NOT remove contents' '\n \t\tgit init &&\n \t\ttest_commit msg hello.world\n \t) &&\n+\tfind repo | sort >expect &&\n \tgit clean -f -d repo/.git/ &&\n-\ttest_path_is_dir repo/.git &&\n-\ttest_path_is_file repo/.git/HEAD &&\n+\tfind repo | sort >actual &&\n+\ttest_cmp expect actual &&\n \ttest_path_is_dir untracked/\n '\n \n-- \n2.42.0\n\n"},{"id":"484342","messageId":"4af0dc00165b092a54ee54e90d340e61f3bd84fd.1698914571.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1698914571.git.ps@pks.im","subject":"[PATCH v3 8/9] t7900: assert the absence of refs via git-for-each-ref(1)","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-11-02T08:47:10Z","receivedAt":"2023-11-02T08:47:20Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"We're asserting that a prefetch of remotes via git-maintenance(1)\ndoesn't write any references in refs/remotes by validating that the\ndirectory \".git/refs/remotes\" is missing. This is quite roundabout: we\ndon't care about the directory existing, we care about the references\nnot existing, and the way these are stored is on the behest of the\nreference database.\n\nConvert the test to instead check via git-for-each-ref(1) whether any\nremote reference exist.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t7900-maintenance.sh | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh\nindex e56f5980dc4..cefecee732f 100755\n--- a/t/t7900-maintenance.sh\n+++ b/t/t7900-maintenance.sh\n@@ -157,7 +157,8 @@ test_expect_success 'prefetch multiple remotes' '\n \tfetchargs=\"--prefetch --prune --no-tags --no-write-fetch-head --recurse-submodules=no --quiet\" &&\n \ttest_subcommand git fetch remote1 $fetchargs <run-prefetch.txt &&\n \ttest_subcommand git fetch remote2 $fetchargs <run-prefetch.txt &&\n-\ttest_path_is_missing .git/refs/remotes &&\n+\tgit for-each-ref refs/remotes >actual &&\n+\ttest_must_be_empty actual &&\n \tgit log prefetch/remotes/remote1/one &&\n \tgit log prefetch/remotes/remote2/two &&\n \tgit fetch --all &&\n-- \n2.42.0\n\n"},{"id":"484343","messageId":"9fc039a1764f58d5919d9559e726d0d52ca839d6.1698914571.git.ps@pks.im","threadId":"60390","inReplyTo":"cover.1698914571.git.ps@pks.im","subject":"[PATCH v3 9/9] t: mark several tests that assume the files backend with REFFILES","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2023-11-02T08:47:14Z","receivedAt":"2023-11-02T08:47:22Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Add the REFFILES prerequisite to several tests that assume we're using\nthe files backend. There are various reasons why we cannot easily\nconvert those tests to be backend-independent, where the most common\none is that we have no way to write corrupt references into the refdb\nvia our tooling. We may at a later point in time grow the tooling to\nmake this possible, but for now we just mark these tests as requiring\nthe files backend.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t1400-update-ref.sh            |  8 ++++----\n t/t1450-fsck.sh                  |  6 +++---\n t/t2011-checkout-invalid-head.sh | 16 ++++++++--------\n t/t3200-branch.sh                |  8 ++++----\n t/t3400-rebase.sh                |  2 +-\n t/t5605-clone-local.sh           |  2 +-\n 6 files changed, 21 insertions(+), 21 deletions(-)\n\ndiff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh\nindex b7d1e5deede..70a760ba378 100755\n--- a/t/t1400-update-ref.sh\n+++ b/t/t1400-update-ref.sh\n@@ -236,7 +236,7 @@ test_expect_success 'update-ref --no-deref -d can delete self-reference' '\n \ttest_must_fail git show-ref --verify -q refs/heads/self\n '\n \n-test_expect_success 'update-ref --no-deref -d can delete reference to bad ref' '\n+test_expect_success REFFILES 'update-ref --no-deref -d can delete reference to bad ref' '\n \t>.git/refs/heads/bad &&\n \ttest_when_finished \"rm -f .git/refs/heads/bad\" &&\n \tgit symbolic-ref refs/heads/ref-to-bad refs/heads/bad &&\n@@ -288,7 +288,7 @@ test_expect_success \"set $m (logged by touch)\" '\n \ttest $A = $(git show-ref -s --verify $m)\n '\n \n-test_expect_success 'empty directory removal' '\n+test_expect_success REFFILES 'empty directory removal' '\n \tgit branch d1/d2/r1 HEAD &&\n \tgit branch d1/r2 HEAD &&\n \ttest_path_is_file .git/refs/heads/d1/d2/r1 &&\n@@ -300,7 +300,7 @@ test_expect_success 'empty directory removal' '\n \ttest_path_is_file .git/logs/refs/heads/d1/r2\n '\n \n-test_expect_success 'symref empty directory removal' '\n+test_expect_success REFFILES 'symref empty directory removal' '\n \tgit branch e1/e2/r1 HEAD &&\n \tgit branch e1/r2 HEAD &&\n \tgit checkout e1/e2/r1 &&\n@@ -1639,7 +1639,7 @@ test_expect_success PIPE 'transaction flushes status updates' '\n \ttest_cmp expected actual\n '\n \n-test_expect_success 'directory not created deleting packed ref' '\n+test_expect_success REFFILES 'directory not created deleting packed ref' '\n \tgit branch d1/d2/r1 HEAD &&\n \tgit pack-refs --all &&\n \ttest_path_is_missing .git/refs/heads/d1/d2 &&\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex a6af550867c..50b15bd7fc0 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -123,7 +123,7 @@ test_expect_success 'branch pointing to non-commit' '\n \ttest_i18ngrep \"not a commit\" out\n '\n \n-test_expect_success 'HEAD link pointing at a funny object' '\n+test_expect_success REFFILES 'HEAD link pointing at a funny object' '\n \ttest_when_finished \"git update-ref HEAD $orig_head\" &&\n \techo $ZERO_OID >.git/HEAD &&\n \t# avoid corrupt/broken HEAD from interfering with repo discovery\n@@ -139,7 +139,7 @@ test_expect_success 'HEAD link pointing at a funny place' '\n \ttest_i18ngrep \"HEAD points to something strange\" out\n '\n \n-test_expect_success 'HEAD link pointing at a funny object (from different wt)' '\n+test_expect_success REFFILES 'HEAD link pointing at a funny object (from different wt)' '\n \ttest_when_finished \"git update-ref HEAD $orig_head\" &&\n \ttest_when_finished \"git worktree remove -f wt\" &&\n \tgit worktree add wt &&\n@@ -149,7 +149,7 @@ test_expect_success 'HEAD link pointing at a funny object (from different wt)' '\n \ttest_i18ngrep \"main-worktree/HEAD: detached HEAD points\" out\n '\n \n-test_expect_success 'other worktree HEAD link pointing at a funny object' '\n+test_expect_success REFFILES 'other worktree HEAD link pointing at a funny object' '\n \ttest_when_finished \"git worktree remove -f other\" &&\n \tgit worktree add other &&\n \techo $ZERO_OID >.git/worktrees/other/HEAD &&\ndiff --git a/t/t2011-checkout-invalid-head.sh b/t/t2011-checkout-invalid-head.sh\nindex d9997e7b6b4..3c8135831b8 100755\n--- a/t/t2011-checkout-invalid-head.sh\n+++ b/t/t2011-checkout-invalid-head.sh\n@@ -18,18 +18,18 @@ test_expect_success 'checkout should not start branch from a tree' '\n \ttest_must_fail git checkout -b newbranch main^{tree}\n '\n \n-test_expect_success 'checkout main from invalid HEAD' '\n+test_expect_success REFFILES 'checkout main from invalid HEAD' '\n \techo $ZERO_OID >.git/HEAD &&\n \tgit checkout main --\n '\n \n-test_expect_success 'checkout notices failure to lock HEAD' '\n+test_expect_success REFFILES 'checkout notices failure to lock HEAD' '\n \ttest_when_finished \"rm -f .git/HEAD.lock\" &&\n \t>.git/HEAD.lock &&\n \ttest_must_fail git checkout -b other\n '\n \n-test_expect_success 'create ref directory/file conflict scenario' '\n+test_expect_success REFFILES 'create ref directory/file conflict scenario' '\n \tgit update-ref refs/heads/outer/inner main &&\n \n \t# do not rely on symbolic-ref to get a known state,\n@@ -39,26 +39,26 @@ test_expect_success 'create ref directory/file conflict scenario' '\n \t}\n '\n \n-test_expect_success 'checkout away from d/f HEAD (unpacked, to branch)' '\n+test_expect_success REFFILES 'checkout away from d/f HEAD (unpacked, to branch)' '\n \treset_to_df &&\n \tgit checkout main\n '\n \n-test_expect_success 'checkout away from d/f HEAD (unpacked, to detached)' '\n+test_expect_success REFFILES 'checkout away from d/f HEAD (unpacked, to detached)' '\n \treset_to_df &&\n \tgit checkout --detach main\n '\n \n-test_expect_success 'pack refs' '\n+test_expect_success REFFILES 'pack refs' '\n \tgit pack-refs --all --prune\n '\n \n-test_expect_success 'checkout away from d/f HEAD (packed, to branch)' '\n+test_expect_success REFFILES 'checkout away from d/f HEAD (packed, to branch)' '\n \treset_to_df &&\n \tgit checkout main\n '\n \n-test_expect_success 'checkout away from d/f HEAD (packed, to detached)' '\n+test_expect_success REFFILES 'checkout away from d/f HEAD (packed, to detached)' '\n \treset_to_df &&\n \tgit checkout --detach main\n '\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex 933aa9eebbd..606c50fe66c 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -28,7 +28,7 @@ test_expect_success 'git branch --help should not have created a bogus branch' '\n \ttest_ref_missing refs/heads/--help\n '\n \n-test_expect_success 'branch -h in broken repository' '\n+test_expect_success REFFILES 'branch -h in broken repository' '\n \tmkdir broken &&\n \t(\n \t\tcd broken &&\n@@ -245,7 +245,7 @@ test_expect_success 'git branch -M baz bam should succeed when baz is checked ou\n \tgit worktree prune\n '\n \n-test_expect_success 'git branch -M fails if updating any linked working tree fails' '\n+test_expect_success REFFILES 'git branch -M fails if updating any linked working tree fails' '\n \tgit worktree add -b baz bazdir1 &&\n \tgit worktree add -f bazdir2 baz &&\n \ttouch .git/worktrees/bazdir1/HEAD.lock &&\n@@ -836,14 +836,14 @@ test_expect_success 'renaming a symref is not allowed' '\n \ttest_ref_missing refs/heads/new-topic\n '\n \n-test_expect_success SYMLINKS 'git branch -m u v should fail when the reflog for u is a symlink' '\n+test_expect_success SYMLINKS,REFFILES 'git branch -m u v should fail when the reflog for u is a symlink' '\n \tgit branch --create-reflog u &&\n \tmv .git/logs/refs/heads/u real-u &&\n \tln -s real-u .git/logs/refs/heads/u &&\n \ttest_must_fail git branch -m u v\n '\n \n-test_expect_success SYMLINKS 'git branch -m with symlinked .git/refs' '\n+test_expect_success SYMLINKS,REFFILES 'git branch -m with symlinked .git/refs' '\n \ttest_when_finished \"rm -rf subdir\" &&\n \tgit init --bare subdir &&\n \ndiff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh\nindex d3df19a51f8..435943a0891 100755\n--- a/t/t3400-rebase.sh\n+++ b/t/t3400-rebase.sh\n@@ -424,7 +424,7 @@ test_expect_success 'refuse to switch to branch checked out elsewhere' '\n \ttest_i18ngrep \"already used by worktree at\" err\n '\n \n-test_expect_success MINGW,SYMLINKS_WINDOWS 'rebase when .git/logs is a symlink' '\n+test_expect_success REFFILES,MINGW,SYMLINKS_WINDOWS 'rebase when .git/logs is a symlink' '\n \tgit checkout main &&\n \tmv .git/logs actual_logs &&\n \tcmd //c \"mklink /D .git\\logs ..\\actual_logs\" &&\ndiff --git a/t/t5605-clone-local.sh b/t/t5605-clone-local.sh\nindex bedd29d0550..a3055869bc7 100755\n--- a/t/t5605-clone-local.sh\n+++ b/t/t5605-clone-local.sh\n@@ -157,7 +157,7 @@ test_expect_success 'cloning locally respects \"-u\" for fetching refs' '\n \ttest_must_fail git clone --bare -u false a should_not_work.git\n '\n \n-test_expect_success 'local clone from repo with corrupt refs fails gracefully' '\n+test_expect_success REFFILES 'local clone from repo with corrupt refs fails gracefully' '\n \tgit init corrupt &&\n \ttest_commit -C corrupt one &&\n \techo a >corrupt/.git/refs/heads/topic &&\n-- \n2.42.0\n\n"}]}