{"thread":{"id":"64534","subject":"[PATCH v2 1/2] refs: support obtaining ref_store for given dir","startedAt":"2025-11-26T11:12:05Z","lastAt":"2025-11-27T21:45:53Z","messageCount":8,"participants":["Karthik Nayak","Junio C Hamano"],"isPatch":true,"patchVersion":2,"patchTotal":2},"messages":[{"id":"531291","messageId":"20251126-kn-alternate-ref-dir-v2-0-8b9f6f18f635@gmail.com","threadId":"64534","inReplyTo":"20251119-kn-alternate-ref-dir-v1-0-4cf4a94c8bed@gmail.com","subject":"[PATCH v2 0/2] refs: allow setting the reference directory","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-11-26T11:11:59Z","receivedAt":"2025-11-26T11:12:05Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"While Git allows users to select different reference backends, unlike\nwith objects, there is no flexibility in selecting the reference\ndirectory. Currently, the reference format is obtained from the config\nof the repository and the reference directory is set to the $GIT_DIR.\n\nThis patch series adds a new ENV variable 'GIT_REF_URI' which takes the\nreference backend and path in a URI form:\n\n    <reference_backend>://<URI-for-resource>\n\nFor e.g. 'reftable:///foo' or 'files://$GIT_DIR/ref_migration.0xBsa0'.\n\nOne use case for this is migration between different backends. On the\nserver side, migrating from the files backend to the newly introduced\nreftable backend can be achieved by running 'git refs migrate'. However,\nfor large repositories with millions of references, this migration can\ntake from seconds to minutes.\n\nFor some background, at GitLab, the criteria for our migration was to\nreduce the downtime of the migrate ideally to zero. So running 'git refs\nmigrate --ref-format=reftable' by itself wouldn't work, since it scales\nwith the number of references and we have repos with millions of\nreferences, so we need to migrate without loosing any information. We\ncame up with the following plan:\n\n  1. Run git-pack-refs(1) and note timestamp of the generated packed-refs\n     file.\n  2. Run git refs migrate –dry-run.\n  3. If there are no ongoing reference requests (read/write)\n     a. Lock the repository by blocking incoming requests (done on a\n        layer above git, in Gitaly [1]).\n     b. If the timestamp of the packed-refs file has changed, unlock\n        the repo and repeat from step 1.\n     c. Apply all the loose refs to the dry-run reftable folder (this\n        requires support in Git to write refs to arbitrary folder).\n     d. Move the reftable dry-run folder into the GIT_DIR.\n     e. Swap the repo config\n     f. Unlock repo access\n\nUsing such a route, scales much better since we only have to worry about\nblocking the repository by O(ref written between #1 and #3a) and not\nO(refs in repo). But for doing so, we need to be able to write to a\narbitrary reference backend + path. This is to add the missing\nreferences to the dry-run reftable folder. This series, achieves that.\n\nThe first commit adds the required changes to create a 'ref_store' for a\ngiven path. The second commit parses the URI if available when creating\nthe main ref store.\n\nThis is based on top of 9a2fb147f2 (Git 2.52, 2025-11-17).\n\n[1]: https://gitlab.com/gitlab-org/gitaly\n\n---\nChanges in v2:\n- Added more clarification and proper intent in the cover message.\n- Changed the format from '<ref_backend>://<path>' to\n  `<ref_backend>://<URI-for-resource>` as it much clearer.\n- Added logic to check for the '//' in the provided URI and a test for\n  the same.\n- In the tests:\n  - Use test_must_fail() instead of ! git\n  - Fix looped tests not using the variables correctly and ensure that\n    the test description is correct.\n- Link to v1: https://patch.msgid.link/20251119-kn-alternate-ref-dir-v1-0-4cf4a94c8bed@gmail.com\n\n---\n Documentation/git.adoc |   8 ++++\n environment.h          |   1 +\n refs.c                 |  71 +++++++++++++++++++++++++++--\n t/meson.build          |   1 +\n t/t1423-ref-backend.sh | 121 +++++++++++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 199 insertions(+), 3 deletions(-)\n\nKarthik Nayak (2):\n      refs: support obtaining ref_store for given dir\n      refs: add GIT_REF_URI to specify reference backend and directory\n\nRange-diff versus v1:\n\n1:  f6e8aa37fe ! 1:  c925726efd refs: support obtaining ref_store for given dir\n    @@ Commit message\n         The refs subsystem uses the `get_main_ref_store()` to obtain the main\n         ref_store for a given repository. In the upcoming patches we also want\n         to create a ref_store for any given reference directory, which may exist\n    -    in arbitrary paths. To support such behavior, extract out the core logic\n    -    for creating out the ref_store from `get_main_ref_store()` into a new\n    -    function `get_ref_store_for_dir()` which can provide the ref_store for a\n    +    in arbitrary paths. For the files backend and the reftable backend, the\n    +    reference directory is generally the $GIT_DIR.\n    +\n    +    To support such behavior, extract out the core logic for creating out\n    +    the ref_store from `get_main_ref_store()` into a new function\n    +    `get_ref_store_for_dir()` which can provide the ref_store for a\n         given (repository, directory, reference format) combination.\n     \n         Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n2:  5e30fa334e ! 2:  b859ebad64 refs: add GIT_REF_URI to specify reference backend and directory\n    @@ Commit message\n         Add a new environment variable 'GIT_REF_URI' that specifies both the\n         reference backend and directory path using a URI format:\n     \n    -        <ref_backend>://<path>\n    +        <ref_backend>://<URI-for-resource>\n     \n         When set, this variable is used to obtain the main reference store for\n         all Git commands. The variable is checked in `get_main_ref_store()`\n    @@ Commit message\n         Add a new test file 't1423-ref-backend.sh' to test this environment\n         variable.\n     \n    +    Helped-by: Jean-Noël Avila <jn.avila@free.fr>\n         Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n     \n      ## Documentation/git.adoc ##\n    @@ Documentation/git.adoc: double-quotes and respecting backslash escapes. E.g., th\n      \tSee `--ref-format` in linkgit:git-init[1].\n      \n     +`GIT_REF_URI`::\n    -+    Specify which reference backend and path to be used, if not specified the\n    -+    backend is inferred from the configuration and $GIT_DIR is used as the\n    -+    path.\n    ++    Specify which reference backend to be used along with its URI. Reference\n    ++    backends like the files, reftable backend use the $GIT_DIR as their URI.\n     ++\n    -+Expects the format '<ref_backend>://<path>', where the 'backend' specifies the\n    -+reference backend and the 'path' specifies the directory used by the backend.\n    ++Expects the format `<ref_backend>://<URI-for-resource>`, where the\n    ++_<ref_backend>_ specifies the reference backend and the _<URI-for-resource>_\n    ++specifies the URI used by the backend.\n     +\n      Git Commits\n      ~~~~~~~~~~~\n    @@ refs.c: static struct ref_store *get_ref_store_for_dir(struct repository *r,\n     +\t}\n     +\n     +\tformat_string = ref_backend_info.items[0].string;\n    ++\tif (!starts_with(ref_backend_info.items[1].string, \"//\")) {\n    ++\t\terror(\"invalid reference backend uri format '%s'\", uri);\n    ++\t\tgoto cleanup;\n    ++\t}\n    ++\tdir = ref_backend_info.items[1].string + 2;\n    ++\n    ++\tformat_string = ref_backend_info.items[0].string;\n     +\tdir = ref_backend_info.items[1].string + 2;\n     +\n     +\tif (!dir || !dir[0]) {\n    @@ t/t1423-ref-backend.sh (new)\n     +\t\tcd repo &&\n     +\t\tGIT_REF_URI=\"\" &&\n     +\t\texport GIT_REF_URI &&\n    -+\t\t! git refs list 2>err &&\n    ++\t\ttest_must_fail git refs list 2>err &&\n     +\t\ttest_grep \"reference backend uri is empty\" err\n     +\t)\n     +'\n    @@ t/t1423-ref-backend.sh (new)\n     +\t\tcd repo &&\n     +\t\tGIT_REF_URI=\"reftable@/home/reftable\" &&\n     +\t\texport GIT_REF_URI &&\n    -+\t\t! git refs list 2>err &&\n    ++\t\ttest_must_fail git refs list 2>err &&\n     +\t\ttest_grep \"invalid reference backend uri format\" err\n     +\t)\n     +'\n    @@ t/t1423-ref-backend.sh (new)\n     +\t\tcd repo &&\n     +\t\tGIT_REF_URI=\"reftable://\" &&\n     +\t\texport GIT_REF_URI &&\n    -+\t\t! git refs list 2>err &&\n    ++\t\ttest_must_fail git refs list 2>err &&\n     +\t\ttest_grep \"invalid path in uri\" err\n     +\t)\n     +'\n     +\n    ++test_expect_success 'uri ends at colon' '\n    ++\ttest_when_finished \"rm -rf repo\" &&\n    ++\tgit init --ref-format=files repo &&\n    ++\t(\n    ++\t\tcd repo &&\n    ++\t\tGIT_REF_URI=\"reftable:\" &&\n    ++\t\texport GIT_REF_URI &&\n    ++\t\ttest_must_fail git refs list 2>err &&\n    ++\t\ttest_grep \"invalid reference backend uri format\" err\n    ++\t)\n    ++'\n    ++\n     +test_expect_success 'unknown reference backend' '\n     +\ttest_when_finished \"rm -rf repo\" &&\n     +\tgit init --ref-format=files repo &&\n    @@ t/t1423-ref-backend.sh (new)\n     +\t\tcd repo &&\n     +\t\tGIT_REF_URI=\"db://.git\" &&\n     +\t\texport GIT_REF_URI &&\n    -+\t\t! git refs list 2>err &&\n    ++\t\ttest_must_fail git refs list 2>err &&\n     +\t\ttest_grep \"unknown reference backend\" err\n     +\t)\n     +'\n    @@ t/t1423-ref-backend.sh (new)\n     +\t\t\tcontinue\n     +\t\tfi\n     +\n    -+\t\ttest_expect_success 'read from other reference backend' '\n    ++\t\ttest_expect_success \"read from $to_format backend\" '\n     +\t\t\ttest_when_finished \"rm -rf repo\" &&\n    -+\t\t\tgit init --ref-format=files repo &&\n    ++\t\t\tgit init --ref-format=$from_format repo &&\n     +\t\t\t(\n     +\t\t\t\tcd repo &&\n     +\t\t\t\ttest_commit 1 &&\n     +\t\t\t\ttest_commit 2 &&\n     +\t\t\t\ttest_commit 3 &&\n     +\n    -+\t\t\t\tgit refs migrate --dry-run --ref-format=reftable >out &&\n    -+\t\t\t\tREFTABLE_PATH=$(cat out | sed \"s/.* ${SQ}\\(.*\\)${SQ}/\\1/\") &&\n    ++\t\t\t\tgit refs migrate --dry-run --ref-format=$to_format >out &&\n    ++\t\t\t\tBACKEND_PATH=$(cat out | sed \"s/.* ${SQ}\\(.*\\)${SQ}/\\1/\") &&\n     +\t\t\t\tgit refs list >expect &&\n    -+\t\t\t\tGIT_REF_URI=\"reftable://$REFTABLE_PATH\" git refs list >actual &&\n    ++\t\t\t\tGIT_REF_URI=\"$to_format://$BACKEND_PATH\" git refs list >actual &&\n     +\t\t\t\ttest_cmp expect actual\n     +\t\t\t)\n     +\t\t'\n     +\n    -+\t\ttest_expect_success 'write to other reference backend' '\n    ++\t\ttest_expect_success \"write to $to_format backend\" '\n     +\t\t\ttest_when_finished \"rm -rf repo\" &&\n    -+\t\t\tgit init --ref-format=files repo &&\n    ++\t\t\tgit init --ref-format=$from_format repo &&\n     +\t\t\t(\n     +\t\t\t\tcd repo &&\n     +\t\t\t\ttest_commit 1 &&\n     +\t\t\t\ttest_commit 2 &&\n     +\t\t\t\ttest_commit 3 &&\n     +\n    -+\t\t\t\tgit refs migrate --dry-run --ref-format=reftable >out &&\n    ++\t\t\t\tgit refs migrate --dry-run --ref-format=$to_format >out &&\n     +\t\t\t\tgit refs list >expect &&\n     +\n    -+\t\t\t\tREFTABLE_PATH=$(cat out | sed \"s/.* ${SQ}\\(.*\\)${SQ}/\\1/\") &&\n    -+\t\t\t\tGIT_REF_URI=\"reftable://$REFTABLE_PATH\" git tag -d 1 &&\n    ++\t\t\t\tBACKEND_PATH=$(cat out | sed \"s/.* ${SQ}\\(.*\\)${SQ}/\\1/\") &&\n    ++\t\t\t\tGIT_REF_URI=\"$to_format://$BACKEND_PATH\" git tag -d 1 &&\n     +\n     +\t\t\t\tgit refs list >actual &&\n     +\t\t\t\ttest_cmp expect actual &&\n     +\n    -+\t\t\t\tGIT_REF_URI=\"reftable://$REFTABLE_PATH\" git refs list >expect &&\n    ++\t\t\t\tGIT_REF_URI=\"$to_format://$BACKEND_PATH\" git refs list >expect &&\n     +\t\t\t\tgit refs list >out &&\n     +\t\t\t\tcat out | grep -v \"refs/tags/1\" >actual &&\n     +\t\t\t\ttest_cmp expect actual\n\n\nbase-commit: 9a2fb147f2c61d0cab52c883e7e26f5b7948e3ed\nchange-id: 20251105-kn-alternate-ref-dir-3e572e8cd0ef\n\nThanks\n- Karthik\n\n"},{"id":"531289","messageId":"20251126-kn-alternate-ref-dir-v2-1-8b9f6f18f635@gmail.com","threadId":"64534","inReplyTo":"20251126-kn-alternate-ref-dir-v2-0-8b9f6f18f635@gmail.com","subject":"[PATCH v2 1/2] refs: support obtaining ref_store for given dir","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-11-26T11:12:00Z","receivedAt":"2025-11-26T11:12:06Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"The refs subsystem uses the `get_main_ref_store()` to obtain the main\nref_store for a given repository. In the upcoming patches we also want\nto create a ref_store for any given reference directory, which may exist\nin arbitrary paths. For the files backend and the reftable backend, the\nreference directory is generally the $GIT_DIR.\n\nTo support such behavior, extract out the core logic for creating out\nthe ref_store from `get_main_ref_store()` into a new function\n`get_ref_store_for_dir()` which can provide the ref_store for a\ngiven (repository, directory, reference format) combination.\n\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n refs.c | 13 ++++++++++---\n 1 file changed, 10 insertions(+), 3 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 965381367e..23f46867f2 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -2177,6 +2177,15 @@ void ref_store_release(struct ref_store *ref_store)\n \tfree(ref_store->gitdir);\n }\n \n+static struct ref_store *get_ref_store_for_dir(struct repository *r,\n+\t\t\t\t\t       char *dir,\n+\t\t\t\t\t       enum ref_storage_format format)\n+{\n+\tstruct ref_store *ref_store = ref_store_init(r, format, dir,\n+\t\t\t\t\t\t     REF_STORE_ALL_CAPS);\n+\treturn maybe_debug_wrap_ref_store(dir, ref_store);\n+}\n+\n struct ref_store *get_main_ref_store(struct repository *r)\n {\n \tif (r->refs_private)\n@@ -2185,9 +2194,7 @@ struct ref_store *get_main_ref_store(struct repository *r)\n \tif (!r->gitdir)\n \t\tBUG(\"attempting to get main_ref_store outside of repository\");\n \n-\tr->refs_private = ref_store_init(r, r->ref_storage_format,\n-\t\t\t\t\t r->gitdir, REF_STORE_ALL_CAPS);\n-\tr->refs_private = maybe_debug_wrap_ref_store(r->gitdir, r->refs_private);\n+\tr->refs_private = get_ref_store_for_dir(r, r->gitdir, r->ref_storage_format);\n \treturn r->refs_private;\n }\n \n\n-- \n2.51.2\n\n"},{"id":"531290","messageId":"20251126-kn-alternate-ref-dir-v2-2-8b9f6f18f635@gmail.com","threadId":"64534","inReplyTo":"20251126-kn-alternate-ref-dir-v2-0-8b9f6f18f635@gmail.com","subject":"[PATCH v2 2/2] refs: add GIT_REF_URI to specify reference backend and directory","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-11-26T11:12:01Z","receivedAt":"2025-11-26T11:12:07Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Git allows setting a different object directory via\n'GIT_OBJECT_DIRECTORY', but provides no equivalent for references.\nThis asymmetry makes it difficult to test different reference backends\nor use alternative reference storage locations without modifying the\nrepository structure.\n\nAdd a new environment variable 'GIT_REF_URI' that specifies both the\nreference backend and directory path using a URI format:\n\n    <ref_backend>://<URI-for-resource>\n\nWhen set, this variable is used to obtain the main reference store for\nall Git commands. The variable is checked in `get_main_ref_store()`\nwhen lazily assigning `repo->refs_private`. We cannot initialize this\nearlier in `repo_set_gitdir()` because the repository's hash algorithm\nisn't known at that point, and the reftable backend requires this\ninformation during initialization.\n\nWhen used with worktrees, the specified directory is treated as the\nreference directory for all worktree operations.\n\nAdd a new test file 't1423-ref-backend.sh' to test this environment\nvariable.\n\nHelped-by: Jean-Noël Avila <jn.avila@free.fr>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n Documentation/git.adoc |   8 ++++\n environment.h          |   1 +\n refs.c                 |  60 +++++++++++++++++++++++-\n t/meson.build          |   1 +\n t/t1423-ref-backend.sh | 121 +++++++++++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 190 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/git.adoc b/Documentation/git.adoc\nindex ce099e78b8..8c6a3f6042 100644\n--- a/Documentation/git.adoc\n+++ b/Documentation/git.adoc\n@@ -584,6 +584,14 @@ double-quotes and respecting backslash escapes. E.g., the value\n \trepositories will be set to this value. The default is \"files\".\n \tSee `--ref-format` in linkgit:git-init[1].\n \n+`GIT_REF_URI`::\n+    Specify which reference backend to be used along with its URI. Reference\n+    backends like the files, reftable backend use the $GIT_DIR as their URI.\n++\n+Expects the format `<ref_backend>://<URI-for-resource>`, where the\n+_<ref_backend>_ specifies the reference backend and the _<URI-for-resource>_\n+specifies the URI used by the backend.\n+\n Git Commits\n ~~~~~~~~~~~\n `GIT_AUTHOR_NAME`::\ndiff --git a/environment.h b/environment.h\nindex 51898c99cd..9bc380bba4 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -42,6 +42,7 @@\n #define GIT_OPTIONAL_LOCKS_ENVIRONMENT \"GIT_OPTIONAL_LOCKS\"\n #define GIT_TEXT_DOMAIN_DIR_ENVIRONMENT \"GIT_TEXTDOMAINDIR\"\n #define GIT_ATTR_SOURCE_ENVIRONMENT \"GIT_ATTR_SOURCE\"\n+#define GIT_REF_URI_ENVIRONMENT \"GIT_REF_URI\"\n \n /*\n  * Environment variable used to propagate the --no-advice global option to the\ndiff --git a/refs.c b/refs.c\nindex 23f46867f2..a7af228799 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -2186,15 +2186,73 @@ static struct ref_store *get_ref_store_for_dir(struct repository *r,\n \treturn maybe_debug_wrap_ref_store(dir, ref_store);\n }\n \n+static struct ref_store *get_ref_store_from_uri(struct repository *repo,\n+\t\t\t\t\t\tconst char *uri)\n+{\n+\tstruct string_list ref_backend_info = STRING_LIST_INIT_DUP;\n+\tenum ref_storage_format format;\n+\tstruct ref_store *store = NULL;\n+\tchar *format_string;\n+\tchar *dir;\n+\n+\tif (!uri || !uri[0]) {\n+\t\terror(\"reference backend uri is empty\");\n+\t\tgoto cleanup;\n+\t}\n+\n+\tif (string_list_split(&ref_backend_info, uri, \":\", 2) != 2) {\n+\t\terror(\"invalid reference backend uri format '%s'\", uri);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tformat_string = ref_backend_info.items[0].string;\n+\tif (!starts_with(ref_backend_info.items[1].string, \"//\")) {\n+\t\terror(\"invalid reference backend uri format '%s'\", uri);\n+\t\tgoto cleanup;\n+\t}\n+\tdir = ref_backend_info.items[1].string + 2;\n+\n+\tformat_string = ref_backend_info.items[0].string;\n+\tdir = ref_backend_info.items[1].string + 2;\n+\n+\tif (!dir || !dir[0]) {\n+\t\terror(\"invalid path in uri '%s'\", uri);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tformat = ref_storage_format_by_name(format_string);\n+\tif (format == REF_STORAGE_FORMAT_UNKNOWN) {\n+\t\terror(\"unknown reference backend '%s'\", format_string);\n+\t\tgoto cleanup;\n+\t}\n+\n+\tstore = get_ref_store_for_dir(repo, dir, format);\n+\n+cleanup:\n+\tstring_list_clear(&ref_backend_info, 0);\n+\treturn store;\n+}\n+\n struct ref_store *get_main_ref_store(struct repository *r)\n {\n+\tchar *ref_uri;\n+\n \tif (r->refs_private)\n \t\treturn r->refs_private;\n \n \tif (!r->gitdir)\n \t\tBUG(\"attempting to get main_ref_store outside of repository\");\n \n-\tr->refs_private = get_ref_store_for_dir(r, r->gitdir, r->ref_storage_format);\n+\tref_uri = getenv(GIT_REF_URI_ENVIRONMENT);\n+\tif (ref_uri) {\n+\t\tr->refs_private = get_ref_store_from_uri(r, ref_uri);\n+\t\tif (!r->refs_private)\n+\t\t\tdie(\"failed to initialize ref store from URI: %s\", ref_uri);\n+\n+\t} else {\n+\t\tr->refs_private = get_ref_store_for_dir(r, r->gitdir,\n+\t\t\t\t\t\t\tr->ref_storage_format);\n+\t}\n \treturn r->refs_private;\n }\n \ndiff --git a/t/meson.build b/t/meson.build\nindex a5531df415..a66f8fafff 100644\n--- a/t/meson.build\n+++ b/t/meson.build\n@@ -208,6 +208,7 @@ integration_tests = [\n   't1420-lost-found.sh',\n   't1421-reflog-write.sh',\n   't1422-show-ref-exists.sh',\n+  't1423-ref-backend.sh',\n   't1430-bad-ref-name.sh',\n   't1450-fsck.sh',\n   't1451-fsck-buffer.sh',\ndiff --git a/t/t1423-ref-backend.sh b/t/t1423-ref-backend.sh\nnew file mode 100755\nindex 0000000000..f6756bdd2b\n--- /dev/null\n+++ b/t/t1423-ref-backend.sh\n@@ -0,0 +1,121 @@\n+#!/bin/sh\n+\n+test_description='Test different reference backend URIs'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'empty uri provided' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init --ref-format=files repo &&\n+\t(\n+\t\tcd repo &&\n+\t\tGIT_REF_URI=\"\" &&\n+\t\texport GIT_REF_URI &&\n+\t\ttest_must_fail git refs list 2>err &&\n+\t\ttest_grep \"reference backend uri is empty\" err\n+\t)\n+'\n+\n+test_expect_success 'invalid uri provided' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init --ref-format=files repo &&\n+\t(\n+\t\tcd repo &&\n+\t\tGIT_REF_URI=\"reftable@/home/reftable\" &&\n+\t\texport GIT_REF_URI &&\n+\t\ttest_must_fail git refs list 2>err &&\n+\t\ttest_grep \"invalid reference backend uri format\" err\n+\t)\n+'\n+\n+test_expect_success 'empty path in uri' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init --ref-format=files repo &&\n+\t(\n+\t\tcd repo &&\n+\t\tGIT_REF_URI=\"reftable://\" &&\n+\t\texport GIT_REF_URI &&\n+\t\ttest_must_fail git refs list 2>err &&\n+\t\ttest_grep \"invalid path in uri\" err\n+\t)\n+'\n+\n+test_expect_success 'uri ends at colon' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init --ref-format=files repo &&\n+\t(\n+\t\tcd repo &&\n+\t\tGIT_REF_URI=\"reftable:\" &&\n+\t\texport GIT_REF_URI &&\n+\t\ttest_must_fail git refs list 2>err &&\n+\t\ttest_grep \"invalid reference backend uri format\" err\n+\t)\n+'\n+\n+test_expect_success 'unknown reference backend' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init --ref-format=files repo &&\n+\t(\n+\t\tcd repo &&\n+\t\tGIT_REF_URI=\"db://.git\" &&\n+\t\texport GIT_REF_URI &&\n+\t\ttest_must_fail git refs list 2>err &&\n+\t\ttest_grep \"unknown reference backend\" err\n+\t)\n+'\n+\n+ref_formats=\"files reftable\"\n+for from_format in $ref_formats\n+do\n+\tfor to_format in $ref_formats\n+\tdo\n+\t\tif test \"$from_format\" = \"$to_format\"\n+\t\tthen\n+\t\t\tcontinue\n+\t\tfi\n+\n+\t\ttest_expect_success \"read from $to_format backend\" '\n+\t\t\ttest_when_finished \"rm -rf repo\" &&\n+\t\t\tgit init --ref-format=$from_format repo &&\n+\t\t\t(\n+\t\t\t\tcd repo &&\n+\t\t\t\ttest_commit 1 &&\n+\t\t\t\ttest_commit 2 &&\n+\t\t\t\ttest_commit 3 &&\n+\n+\t\t\t\tgit refs migrate --dry-run --ref-format=$to_format >out &&\n+\t\t\t\tBACKEND_PATH=$(cat out | sed \"s/.* ${SQ}\\(.*\\)${SQ}/\\1/\") &&\n+\t\t\t\tgit refs list >expect &&\n+\t\t\t\tGIT_REF_URI=\"$to_format://$BACKEND_PATH\" git refs list >actual &&\n+\t\t\t\ttest_cmp expect actual\n+\t\t\t)\n+\t\t'\n+\n+\t\ttest_expect_success \"write to $to_format backend\" '\n+\t\t\ttest_when_finished \"rm -rf repo\" &&\n+\t\t\tgit init --ref-format=$from_format repo &&\n+\t\t\t(\n+\t\t\t\tcd repo &&\n+\t\t\t\ttest_commit 1 &&\n+\t\t\t\ttest_commit 2 &&\n+\t\t\t\ttest_commit 3 &&\n+\n+\t\t\t\tgit refs migrate --dry-run --ref-format=$to_format >out &&\n+\t\t\t\tgit refs list >expect &&\n+\n+\t\t\t\tBACKEND_PATH=$(cat out | sed \"s/.* ${SQ}\\(.*\\)${SQ}/\\1/\") &&\n+\t\t\t\tGIT_REF_URI=\"$to_format://$BACKEND_PATH\" git tag -d 1 &&\n+\n+\t\t\t\tgit refs list >actual &&\n+\t\t\t\ttest_cmp expect actual &&\n+\n+\t\t\t\tGIT_REF_URI=\"$to_format://$BACKEND_PATH\" git refs list >expect &&\n+\t\t\t\tgit refs list >out &&\n+\t\t\t\tcat out | grep -v \"refs/tags/1\" >actual &&\n+\t\t\t\ttest_cmp expect actual\n+\t\t\t)\n+\t\t'\n+\tdone\n+done\n+\n+test_done\n\n-- \n2.51.2\n\n"},{"id":"531305","messageId":"xmqqfra0q0xs.fsf@gitster.g","threadId":"64534","inReplyTo":"20251126-kn-alternate-ref-dir-v2-1-8b9f6f18f635@gmail.com","subject":"Re: [PATCH v2 1/2] refs: support obtaining ref_store for given dir","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-11-26T15:16:15Z","receivedAt":"2025-11-26T15:16:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n> The refs subsystem uses the `get_main_ref_store()` to obtain the main\n> ref_store for a given repository. In the upcoming patches we also want\n> to create a ref_store for any given reference directory, which may exist\n> in arbitrary paths. For the files backend and the reftable backend, the\n> reference directory is generally the $GIT_DIR.\n>\n> To support such behavior, extract out the core logic for creating out\n> the ref_store from `get_main_ref_store()` into a new function\n> `get_ref_store_for_dir()` which can provide the ref_store for a\n> given (repository, directory, reference format) combination.\n\nI am guessing that this is meant to work with the REF_URI thing, and\nthe <path> part in REF_URI=<backend>:<path> corresponds to the \"dir\"\nparameter here.\n\nLooks like a good no-op split.\n\n> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n> ---\n>  refs.c | 13 ++++++++++---\n>  1 file changed, 10 insertions(+), 3 deletions(-)\n>\n> diff --git a/refs.c b/refs.c\n> index 965381367e..23f46867f2 100644\n> --- a/refs.c\n> +++ b/refs.c\n> @@ -2177,6 +2177,15 @@ void ref_store_release(struct ref_store *ref_store)\n>  \tfree(ref_store->gitdir);\n>  }\n>  \n> +static struct ref_store *get_ref_store_for_dir(struct repository *r,\n> +\t\t\t\t\t       char *dir,\n> +\t\t\t\t\t       enum ref_storage_format format)\n> +{\n> +\tstruct ref_store *ref_store = ref_store_init(r, format, dir,\n> +\t\t\t\t\t\t     REF_STORE_ALL_CAPS);\n> +\treturn maybe_debug_wrap_ref_store(dir, ref_store);\n> +}\n> +\n>  struct ref_store *get_main_ref_store(struct repository *r)\n>  {\n>  \tif (r->refs_private)\n> @@ -2185,9 +2194,7 @@ struct ref_store *get_main_ref_store(struct repository *r)\n>  \tif (!r->gitdir)\n>  \t\tBUG(\"attempting to get main_ref_store outside of repository\");\n>  \n> -\tr->refs_private = ref_store_init(r, r->ref_storage_format,\n> -\t\t\t\t\t r->gitdir, REF_STORE_ALL_CAPS);\n> -\tr->refs_private = maybe_debug_wrap_ref_store(r->gitdir, r->refs_private);\n> +\tr->refs_private = get_ref_store_for_dir(r, r->gitdir, r->ref_storage_format);\n>  \treturn r->refs_private;\n>  }\n"},{"id":"531309","messageId":"xmqq7bvcpy35.fsf@gitster.g","threadId":"64534","inReplyTo":"20251126-kn-alternate-ref-dir-v2-2-8b9f6f18f635@gmail.com","subject":"Re: [PATCH v2 2/2] refs: add GIT_REF_URI to specify reference backend and directory","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-11-26T16:17:50Z","receivedAt":"2025-11-26T16:17:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n> +`GIT_REF_URI`::\n> +    Specify which reference backend to be used along with its URI. Reference\n> +    backends like the files, reftable backend use the $GIT_DIR as their URI.\n> ++\n> +Expects the format `<ref_backend>://<URI-for-resource>`, where the\n> +_<ref_backend>_ specifies the reference backend and the _<URI-for-resource>_\n> +specifies the URI used by the backend.\n\nIt is more like \"<directory>\" that specifies the local directory the\nbackend is told to use to store its data.  It feels way too broad\nfor what the initial implementation achieves and what the design can\npotentially include, to say \"URI-for-resource\", I would think.\n\n> diff --git a/environment.h b/environment.h\n> index 51898c99cd..9bc380bba4 100644\n> --- a/environment.h\n> +++ b/environment.h\n> @@ -42,6 +42,7 @@\n>  #define GIT_OPTIONAL_LOCKS_ENVIRONMENT \"GIT_OPTIONAL_LOCKS\"\n>  #define GIT_TEXT_DOMAIN_DIR_ENVIRONMENT \"GIT_TEXTDOMAINDIR\"\n>  #define GIT_ATTR_SOURCE_ENVIRONMENT \"GIT_ATTR_SOURCE\"\n> +#define GIT_REF_URI_ENVIRONMENT \"GIT_REF_URI\"\n>  \n>  /*\n>   * Environment variable used to propagate the --no-advice global option to the\n> diff --git a/refs.c b/refs.c\n> index 23f46867f2..a7af228799 100644\n> --- a/refs.c\n> +++ b/refs.c\n> @@ -2186,15 +2186,73 @@ static struct ref_store *get_ref_store_for_dir(struct repository *r,\n>  \treturn maybe_debug_wrap_ref_store(dir, ref_store);\n>  }\n>  \n> +static struct ref_store *get_ref_store_from_uri(struct repository *repo,\n> +\t\t\t\t\t\tconst char *uri)\n> +{\n> +\tstruct string_list ref_backend_info = STRING_LIST_INIT_DUP;\n> +\tenum ref_storage_format format;\n> +\tstruct ref_store *store = NULL;\n> +\tchar *format_string;\n> +\tchar *dir;\n> +\n> +\tif (!uri || !uri[0]) {\n> +\t\terror(\"reference backend uri is empty\");\n> +\t\tgoto cleanup;\n> +\t}\n\nEquating !uri and !uri[0] and giving the same message would not help\ndiagnosing an error, and not _(\"localizing\") the message is of dubious\nvalue (after all, the message is not being given to somebody coming\nover the network, but meant to be given to the local user, right?).\n\nIf we remove the !uri[0] from the check, shouldn't the later check\ncatch it as \"invalid format\" anyway, and print '%s' it to show that\nwhat was given was empty clearly enough?\n\n> +\tif (string_list_split(&ref_backend_info, uri, \":\", 2) != 2) {\n> +\t\terror(\"invalid reference backend uri format '%s'\", uri);\n> +\t\tgoto cleanup;\n> +\t}\n> +\n> +\tformat_string = ref_backend_info.items[0].string;\n> +\tif (!starts_with(ref_backend_info.items[1].string, \"//\")) {\n> +\t\terror(\"invalid reference backend uri format '%s'\", uri);\n> +\t\tgoto cleanup;\n> +\t}\n> +\tdir = ref_backend_info.items[1].string + 2;\n\nTwo questions.  (1) do we still want the double-slash after the\ncolon?  (2) if so, would it make it simpler to string-list-split\nusing \"://\" as the separator?\n\n> +\tformat_string = ref_backend_info.items[0].string;\n> +\tdir = ref_backend_info.items[1].string + 2;\n\nThese two lines are fishy.  Perhaps leftover from an earlier draft\nthat did not have an error checking before the previous 5 lines were\nadded?\n\n> +\tif (!dir || !dir[0]) {\n> +\t\terror(\"invalid path in uri '%s'\", uri);\n> +\t\tgoto cleanup;\n> +\t}\n\nAt this point it is very unlikely for \"dir\" to be NULL, no?  Even if\nthe .string member after splitting were NULL, adding 2 to it would\nnot leave it NULL.\n\nBeing defensive and checking for NULL is good, but then exactly the\nsame question on \"NULL vs an empty string\" applies here.\n\n>  struct ref_store *get_main_ref_store(struct repository *r)\n>  {\n> +\tchar *ref_uri;\n> +\n>  \tif (r->refs_private)\n>  \t\treturn r->refs_private;\n>  \n>  \tif (!r->gitdir)\n>  \t\tBUG(\"attempting to get main_ref_store outside of repository\");\n>  \n> -\tr->refs_private = get_ref_store_for_dir(r, r->gitdir, r->ref_storage_format);\n> +\tref_uri = getenv(GIT_REF_URI_ENVIRONMENT);\n> +\tif (ref_uri) {\n> +\t\tr->refs_private = get_ref_store_from_uri(r, ref_uri);\n> +\t\tif (!r->refs_private)\n> +\t\t\tdie(\"failed to initialize ref store from URI: %s\", ref_uri);\n> +\n> +\t} else {\n> +\t\tr->refs_private = get_ref_store_for_dir(r, r->gitdir,\n> +\t\t\t\t\t\t\tr->ref_storage_format);\n> +\t}\n>  \treturn r->refs_private;\n>  }\n\nIf this mechanism is for consumption by \"git refs migrate\", is it\npossible to reduce the blast radius by giving the command a command\nline option to do an equivalent of this?  I really am not happy with\nthis environment variable that can change the behaviour of such a\nlow level layer from unsuspecting programs that are not ready.\n\nInstead of tweaking the behaviour of this function via environment\nthat can affect any programs, can't we give these callers like \"git\nrefs migrate\" with specific needs set_main_ref_store() function that\ntakes a ref_store and a repository.  Then they can use to call into\nget_ref_store_for_dir() to obtain a ref they need.  \"git refs migrate\"\nalready takes \"--ref-format\" variable, so all it needs is another\n\"--ref-directory\" command line option, right?\n\nIf the ability to set the ref backend location for arbitrary program\nproves to be useful, we _could_ give the same --ref-format and\n--ref-direcctory command line options to \"git\" itself (like \"git -C\nthere\" runs any subcommand in the named directory), which does the\nthe get_ref_store_for_dir() plus set_main_ref_store() dance,\nmodelled after how \"git refs migrate\" does them.\n\nHmm?\n"},{"id":"531375","messageId":"CAOLa=ZRPYUJu4hVuZrXdJ1vq89=Pkiyw0-As=0B6pL1-cymR8w@mail.gmail.com","threadId":"64534","inReplyTo":"xmqq7bvcpy35.fsf@gitster.g","subject":"Re: [PATCH v2 2/2] refs: add GIT_REF_URI to specify reference backend and directory","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-11-27T14:52:56Z","receivedAt":"2025-11-27T14:52:58Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>> +`GIT_REF_URI`::\n>> +    Specify which reference backend to be used along with its URI. Reference\n>> +    backends like the files, reftable backend use the $GIT_DIR as their URI.\n>> ++\n>> +Expects the format `<ref_backend>://<URI-for-resource>`, where the\n>> +_<ref_backend>_ specifies the reference backend and the _<URI-for-resource>_\n>> +specifies the URI used by the backend.\n>\n> It is more like \"<directory>\" that specifies the local directory the\n> backend is told to use to store its data.  It feels way too broad\n> for what the initial implementation achieves and what the design can\n> potentially include, to say \"URI-for-resource\", I would think.\n>\n\nWell I'm okay either ways, my first version was very specific as it\nmention '<path>'. I changed it based on the discussion with you and Toon\nabout how the '<path>' is the URI for the reference backend.\n\n>> diff --git a/environment.h b/environment.h\n>> index 51898c99cd..9bc380bba4 100644\n>> --- a/environment.h\n>> +++ b/environment.h\n>> @@ -42,6 +42,7 @@\n>>  #define GIT_OPTIONAL_LOCKS_ENVIRONMENT \"GIT_OPTIONAL_LOCKS\"\n>>  #define GIT_TEXT_DOMAIN_DIR_ENVIRONMENT \"GIT_TEXTDOMAINDIR\"\n>>  #define GIT_ATTR_SOURCE_ENVIRONMENT \"GIT_ATTR_SOURCE\"\n>> +#define GIT_REF_URI_ENVIRONMENT \"GIT_REF_URI\"\n>>\n>>  /*\n>>   * Environment variable used to propagate the --no-advice global option to the\n>> diff --git a/refs.c b/refs.c\n>> index 23f46867f2..a7af228799 100644\n>> --- a/refs.c\n>> +++ b/refs.c\n>> @@ -2186,15 +2186,73 @@ static struct ref_store *get_ref_store_for_dir(struct repository *r,\n>>  \treturn maybe_debug_wrap_ref_store(dir, ref_store);\n>>  }\n>>\n>> +static struct ref_store *get_ref_store_from_uri(struct repository *repo,\n>> +\t\t\t\t\t\tconst char *uri)\n>> +{\n>> +\tstruct string_list ref_backend_info = STRING_LIST_INIT_DUP;\n>> +\tenum ref_storage_format format;\n>> +\tstruct ref_store *store = NULL;\n>> +\tchar *format_string;\n>> +\tchar *dir;\n>> +\n>> +\tif (!uri || !uri[0]) {\n>> +\t\terror(\"reference backend uri is empty\");\n>> +\t\tgoto cleanup;\n>> +\t}\n>\n> Equating !uri and !uri[0] and giving the same message would not help\n> diagnosing an error, and not _(\"localizing\") the message is of dubious\n> value (after all, the message is not being given to somebody coming\n> over the network, but meant to be given to the local user, right?).\n>\n\nI think that's fair. I also missed localizing all the errors, I think\nsomeone did point that out too.\n\n> If we remove the !uri[0] from the check, shouldn't the later check\n> catch it as \"invalid format\" anyway, and print '%s' it to show that\n> what was given was empty clearly enough?\n>\n\nYeah, it should I'll remove the latter and modify the test.\n\n>> +\tif (string_list_split(&ref_backend_info, uri, \":\", 2) != 2) {\n>> +\t\terror(\"invalid reference backend uri format '%s'\", uri);\n>> +\t\tgoto cleanup;\n>> +\t}\n>> +\n>> +\tformat_string = ref_backend_info.items[0].string;\n>> +\tif (!starts_with(ref_backend_info.items[1].string, \"//\")) {\n>> +\t\terror(\"invalid reference backend uri format '%s'\", uri);\n>> +\t\tgoto cleanup;\n>> +\t}\n>> +\tdir = ref_backend_info.items[1].string + 2;\n>\n> Two questions.  (1) do we still want the double-slash after the\n> colon?  (2) if so, would it make it simpler to string-list-split\n> using \"://\" as the separator?\n>\n\n(1) Yes.\n(2) My understanding of `string_list_split()` was that the `delim`\nargument are a set of characters to split the string on.\n\nSo:\n    string_list_split(l, \"abc:def/ghi/jkl\", \"://\", -1) -> [\"abc\",\n\"def\", \"ghi\", \"jkl\"]\n    string_list_split(l, \"reftable://foo\", \"://\", -1) -> [\"reftable\",\n\"\", \"\", \"foo\", \"bar\"]\n\nBut this isn't what we want.\n\n>> +\tformat_string = ref_backend_info.items[0].string;\n>> +\tdir = ref_backend_info.items[1].string + 2;\n>\n> These two lines are fishy.  Perhaps leftover from an earlier draft\n> that did not have an error checking before the previous 5 lines were\n> added?\n>\n\nYes, will cleanup.\n\n>> +\tif (!dir || !dir[0]) {\n>> +\t\terror(\"invalid path in uri '%s'\", uri);\n>> +\t\tgoto cleanup;\n>> +\t}\n>\n> At this point it is very unlikely for \"dir\" to be NULL, no?  Even if\n> the .string member after splitting were NULL, adding 2 to it would\n> not leave it NULL.\n>\n> Being defensive and checking for NULL is good, but then exactly the\n> same question on \"NULL vs an empty string\" applies here.\n>\n\nYea, the '!dir[0]' should definitely be enough here.\n\n>>  struct ref_store *get_main_ref_store(struct repository *r)\n>>  {\n>> +\tchar *ref_uri;\n>> +\n>>  \tif (r->refs_private)\n>>  \t\treturn r->refs_private;\n>>\n>>  \tif (!r->gitdir)\n>>  \t\tBUG(\"attempting to get main_ref_store outside of repository\");\n>>\n>> -\tr->refs_private = get_ref_store_for_dir(r, r->gitdir, r->ref_storage_format);\n>> +\tref_uri = getenv(GIT_REF_URI_ENVIRONMENT);\n>> +\tif (ref_uri) {\n>> +\t\tr->refs_private = get_ref_store_from_uri(r, ref_uri);\n>> +\t\tif (!r->refs_private)\n>> +\t\t\tdie(\"failed to initialize ref store from URI: %s\", ref_uri);\n>> +\n>> +\t} else {\n>> +\t\tr->refs_private = get_ref_store_for_dir(r, r->gitdir,\n>> +\t\t\t\t\t\t\tr->ref_storage_format);\n>> +\t}\n>>  \treturn r->refs_private;\n>>  }\n>\n> If this mechanism is for consumption by \"git refs migrate\", is it\n> possible to reduce the blast radius by giving the command a command\n> line option to do an equivalent of this?  I really am not happy with\n> this environment variable that can change the behaviour of such a\n> low level layer from unsuspecting programs that are not ready.\n>\n\nBut the mechanism isn't for 'git refs migrate', but rather we want to\nadd/update references via 'git update-ref' into the dry-run folder\ncreated by the 'git refs migrate'. In the broader sense, we want to\nmanipulate references within this dry-run folder as if it is the\nreference folder for the underlying repository.\n\nI get the comprehension behind the environment variable and am happy to\nwork on something alternative if we can achieve something similar. The\nreason to pick the ENV variable was mostly because this isn't a regular\nuser flag which we expect users to use. Also, this is very similar to\nthe already existing GIT_OBJECT_DIRECTORY.\n\n> Instead of tweaking the behaviour of this function via environment\n> that can affect any programs, can't we give these callers like \"git\n> refs migrate\" with specific needs set_main_ref_store() function that\n> takes a ref_store and a repository.  Then they can use to call into\n> get_ref_store_for_dir() to obtain a ref they need.  \"git refs migrate\"\n> already takes \"--ref-format\" variable, so all it needs is another\n> \"--ref-directory\" command line option, right?\n>\n\nSomething like this would require us to add these flags to all commands,\ncurrently I can think of 'git update-ref' and 'git refs' but it could\nspread to all reference oriented commands.\n\n> If the ability to set the ref backend location for arbitrary program\n> proves to be useful, we _could_ give the same --ref-format and\n> --ref-direcctory command line options to \"git\" itself (like \"git -C\n> there\" runs any subcommand in the named directory), which does the\n> the get_ref_store_for_dir() plus set_main_ref_store() dance,\n> modelled after how \"git refs migrate\" does them.\n>\n> Hmm?\n\nThis could work indeed, I would instead swap it out for a single\n\"--ref-uri=<backend>://<uri>\" which would make it much simpler for users\nand future implementations which might not have a 'directory' like the\ncurrent backends do.\n\nOverall the ENV variable seemed the best based on the constraints and\nthe existing similar variables. Wdyt?\n"},{"id":"531382","messageId":"xmqqy0nrjlc0.fsf@gitster.g","threadId":"64534","inReplyTo":"CAOLa=ZRPYUJu4hVuZrXdJ1vq89=Pkiyw0-As=0B6pL1-cymR8w@mail.gmail.com","subject":"Re: [PATCH v2 2/2] refs: add GIT_REF_URI to specify reference backend and directory","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-11-27T20:02:07Z","receivedAt":"2025-11-27T20:02:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n> (2) My understanding of `string_list_split()` was that the `delim`\n> argument are a set of characters to split the string on.\n\nAh, silly me.\n\n> But the mechanism isn't for 'git refs migrate', but rather we want to\n> add/update references via 'git update-ref' into the dry-run folder\n> created by the 'git refs migrate'. In the broader sense, we want to\n> manipulate references within this dry-run folder as if it is the\n> reference folder for the underlying repository.\n\nOK, I took the cover letter description too literally, it seems.\n\nIf we want everybody in a single session to have a temporarily\ndistorted view of the world, it has been a tried and proven way to\nuse environment variables that override the default repository\nlayout, e.g., GIT_DIR, GIT_WORK_TREE, and this \"no reference\ninteractions go there, not the usual place the repository\nconfiguration says\" environment variable fits very well in the\ncontext.\n\nThanks.\n\n"},{"id":"531384","messageId":"CAOLa=ZQnR9s33tsUbKUevsp7W0+EUcM4x55CLOQn_A2znVo6Fg@mail.gmail.com","threadId":"64534","inReplyTo":"xmqqy0nrjlc0.fsf@gitster.g","subject":"Re: [PATCH v2 2/2] refs: add GIT_REF_URI to specify reference backend and directory","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-11-27T21:45:51Z","receivedAt":"2025-11-27T21:45:53Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>> (2) My understanding of `string_list_split()` was that the `delim`\n>> argument are a set of characters to split the string on.\n>\n> Ah, silly me.\n>\n>> But the mechanism isn't for 'git refs migrate', but rather we want to\n>> add/update references via 'git update-ref' into the dry-run folder\n>> created by the 'git refs migrate'. In the broader sense, we want to\n>> manipulate references within this dry-run folder as if it is the\n>> reference folder for the underlying repository.\n>\n> OK, I took the cover letter description too literally, it seems.\n>\n\nI did change the cover letter for this version with the plan of how this\nwould be used. Let me know if you think I could clarify further.\n\n> If we want everybody in a single session to have a temporarily\n> distorted view of the world, it has been a tried and proven way to\n> use environment variables that override the default repository\n> layout, e.g., GIT_DIR, GIT_WORK_TREE, and this \"no reference\n> interactions go there, not the usual place the repository\n> configuration says\" environment variable fits very well in the\n> context.\n>\n> Thanks.\n\nYes! Exactly. Good to see we're on the same page :)\n\nKarthik\n"}]}