{"thread":{"id":"63644","subject":"[GSoC][RFC PATCH 0/2] Add refs list subcommand","startedAt":"2025-06-14T07:06:29Z","lastAt":"2025-06-17T11:51:45Z","messageCount":5,"participants":["Meet Soni","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"520244","messageId":"20250614070536.17320-1-meetsoni3017@gmail.com","threadId":"63644","inReplyTo":null,"subject":"[GSoC][RFC PATCH 0/2] Add refs list subcommand","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-06-14T07:05:34Z","receivedAt":"2025-06-14T07:06:29Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"This patch series introduces the git refs list subcommand, as part of a\nlonger-term goal to consolidate and modernize ref listing functionality\ncurrently split between git-show-ref(1) and git-for-each-ref(1).\n\nThe initial implementation focuses on mirroring the behavior of\ngit-show-ref, providing support for filtering by --branches, --tags, and\n--head, and implementing pattern matching similar to the legacy command.\nThis ensures backward compatibility.\n\nThat said, git-for-each-ref(1) offers more flexible pattern matching and\nwe acknowledge that its style may be a better fit in the long run. As\nsuch, this RFC deliberately starts with the show-ref matching semantics\nto solicit feedback on whether to switch to for-each-ref style matching\nas the default, with a compatibility flag to preserve legacy behavior.\n\nIt's also worth highlighting that several options from git-show-ref are\nintended to be supported in the git refs list subcommand. These include\nflags such as '--abbrev', '--quiet', '--dereference', '--hash', and\n'--exclude-existing'. While this series focuses on core functionality\nand pattern matching, these additional options are within scope for\nfuture patches.\n\nAdditionally, the git-for-each-ref(1) command offers a rich set of\nfeatures that would be valuable to incorporate into git refs list. At\nthis point, all of its existing options appear to provide meaningful\nfunctionality, and my current thinking is to support them incrementally\nas part of expanding this subcommand. I'd appreciate feedback on whether\nthere are any options that should be reconsidered or excluded in this\nconsolidation effort.\n\nThis RFC is meant to start a broader discussion on:\n\n  - The desired default behavior of pattern matching in git refs list\n\n  - Which features from both git-show-ref and git-for-each-ref should be\n    preserved, rethought, or dropped\n\n  - How much backward compatibility we want to offer, and through what\n    interface (e.g., compatibility flags)\n\nFeedback and thoughts on these topics would be very welcome.\n\nMeet Soni (2):\n  builtin/refs: add list subcommand\n  t: add tests for refs list subcommand\n\n Documentation/git-refs.adoc |  25 ++++++++\n builtin/refs.c              | 110 ++++++++++++++++++++++++++++++++++++\n t/meson.build               |   1 +\n t/t1461-refs-list.sh        |  95 +++++++++++++++++++++++++++++++\n 4 files changed, 231 insertions(+)\n create mode 100755 t/t1461-refs-list.sh\n\n-- \n2.34.1\n\n"},{"id":"520245","messageId":"20250614070536.17320-2-meetsoni3017@gmail.com","threadId":"63644","inReplyTo":"20250614070536.17320-1-meetsoni3017@gmail.com","subject":"[GSoC][RFC PATCH 1/2] builtin/refs: add list subcommand","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-06-14T07:05:35Z","receivedAt":"2025-06-14T07:06:40Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"Git's reference management is distributed across multiple\ncommands and as part of an ongoing effort to streamline and modernize\nreference handling, we are beginning to consolidate these operations\ninto a cohesive `git refs` command.\n\nAdd a `list` subcommand to `git refs` as a modern replacement for\n`git show-ref`, consolidating ref listing functionality under the\nunified `git refs` command.\n\nThe initial implementation supports the following options from\n`git show-ref`:\n\n- --head\n- --tags\n- --branches\n- patterns argument\n\nFor large changes, this patch limits itself to the basic ref listing and\ncommonly used flags. Remaining options will be added incrementally in\nfollow-up patches, guided by feedback from the mailing list.\n\nMentored-by: Patrick Steinhardt <ps@pks.im>\nMentored-by: shejialuo <shejialuo@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nSigned-off-by: Meet Soni <meetsoni3017@gmail.com>\n---\n Documentation/git-refs.adoc |  25 ++++++++\n builtin/refs.c              | 110 ++++++++++++++++++++++++++++++++++++\n 2 files changed, 135 insertions(+)\n\ndiff --git a/Documentation/git-refs.adoc b/Documentation/git-refs.adoc\nindex 4d6dc994f9..397c3ceb01 100644\n--- a/Documentation/git-refs.adoc\n+++ b/Documentation/git-refs.adoc\n@@ -11,6 +11,7 @@ SYNOPSIS\n [synopsis]\n git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]\n git refs verify [--strict] [--verbose]\n+git refs list [--head] [--branches] [--tag] [--] [<pattern>...]\n \n DESCRIPTION\n -----------\n@@ -26,6 +27,12 @@ migrate::\n verify::\n \tVerify reference database consistency.\n \n+list::\n+\tDisplays references available in a local repository along with the associated\n+\tcommit IDs. Results can be filtered using a pattern.\n+\n+\tBy default, shows the tags, heads, and remote refs.\n+\n OPTIONS\n -------\n \n@@ -57,6 +64,24 @@ The following options are specific to 'git refs verify':\n --verbose::\n \tWhen verifying the reference database consistency, be chatty.\n \n+The following options are specific to 'git refs list':\n+\n+--head::\n+\tShow the HEAD reference, even if it would normally be filtered out.\n+\n+--branches::\n+--tags::\n+\tLimit to local branches and local tags, respectively.  These options\n+\tare not mutually exclusive; when given both, references stored in\n+\t\"refs/heads\" and \"refs/tags\" are displayed.\n+\n+<pattern>...::\n+\tShow references matching one or more patterns. Patterns are matched from\n+\tthe end of the full name, and only complete parts are matched, e.g.\n+\t'master' matches 'refs/heads/master', 'refs/remotes/origin/master',\n+\t'refs/tags/jedi/master' but not 'refs/heads/mymaster' or\n+\t'refs/remotes/master/jedi'.\n+\n KNOWN LIMITATIONS\n -----------------\n \ndiff --git a/builtin/refs.c b/builtin/refs.c\nindex 998d2a2c1c..c098132191 100644\n--- a/builtin/refs.c\n+++ b/builtin/refs.c\n@@ -2,6 +2,9 @@\n #include \"builtin.h\"\n #include \"config.h\"\n #include \"fsck.h\"\n+#include \"hex.h\"\n+#include \"object-name.h\"\n+#include \"object-store.h\"\n #include \"parse-options.h\"\n #include \"refs.h\"\n #include \"strbuf.h\"\n@@ -13,6 +16,9 @@\n #define REFS_VERIFY_USAGE \\\n \tN_(\"git refs verify [--strict] [--verbose]\")\n \n+#define REFS_LIST_USAGE \\\n+\tN_(\"git refs list [--head] [--branches] [--tag] [--] [<pattern>...]\")\n+\n static int cmd_refs_migrate(int argc, const char **argv, const char *prefix,\n \t\t\t    struct repository *repo UNUSED)\n {\n@@ -101,6 +107,108 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix,\n \treturn ret;\n }\n \n+struct list_options {\n+\tunsigned int show_head;\n+\tunsigned int filter_branches;\n+\tunsigned int filter_tags;\n+\tunsigned int found_match;\n+\tconst char **patterns;\n+};\n+\n+static void print_ref(const char *refname, const struct object_id *oid)\n+{\n+\tconst char *hex;\n+\n+\thex = oid_to_hex(oid);\n+\tif (!has_object(the_repository, oid,\n+\t\t\tHAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))\n+\t\tdie(\"git refs list: bad ref %s (%s)\", refname,\n+\t\t    hex);\n+\n+\tprintf(\"%s %s\\n\", hex, refname);\n+}\n+\n+static int list_ref(const char *refname, const char *referent UNUSED,\n+\t\t    const struct object_id *oid, int flag UNUSED, void *cbdata)\n+{\n+\tstruct list_options *data = cbdata;\n+\n+\tif (data->show_head && !strcmp(refname, \"HEAD\"))\n+\t\tgoto match;\n+\n+\tif (data->patterns) {\n+\t\tint reflen = strlen(refname);\n+\t\tconst char **pattern_ptr = data->patterns, *pattern;\n+\t\twhile ((pattern = *pattern_ptr++) != NULL) {\n+\t\t\tint pattern_len = strlen(pattern);\n+\t\t\tif (pattern_len > reflen)\n+\t\t\t\tcontinue;\n+\t\t\tif (memcmp(pattern, refname + reflen - pattern_len, pattern_len))\n+\t\t\t\tcontinue;\n+\t\t\tif (pattern_len == reflen)\n+\t\t\t\tgoto match;\n+\t\t\tif (refname[reflen - pattern_len - 1] == '/')\n+\t\t\t\tgoto match;\n+\t\t}\n+\t\treturn 0;\n+\t}\n+\n+match:\n+\tdata->found_match++;\n+\n+\tprint_ref(refname, oid);\n+\n+\treturn 0;\n+}\n+\n+static int cmd_refs_list(int argc, const char **argv, const char *prefix,\n+\t\t\t struct repository *repo UNUSED)\n+{\n+\tstruct list_options list_opts = {0};\n+\tconst char * const list_usage[] = {\n+\t\tREFS_LIST_USAGE,\n+\t\tNULL,\n+\t};\n+\tstruct option options[] = {\n+\t\tOPT_BOOL(0, \"head\", &list_opts.show_head,\n+\t\t\t N_(\"show the HEAD reference, even if it would be filtered out\")),\n+\t\tOPT_BOOL(0, \"tags\", &list_opts.filter_tags,\n+\t\t\t N_(\"only show tags (can be combined with --branches)\")),\n+\t\tOPT_BOOL(0, \"branches\", &list_opts.filter_branches,\n+\t\t\t N_(\"only show branches (can be combined with --tags)\")),\n+\t\tOPT_END(),\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, list_usage, 0);\n+\n+\tif (argv && *argv)\n+\t\tlist_opts.patterns = argv;\n+\n+\tif (list_opts.show_head)\n+\t\trefs_head_ref(get_main_ref_store(the_repository), list_ref,\n+\t\t\t      &list_opts);\n+\n+\tif (list_opts.filter_tags || list_opts.filter_branches) {\n+\t\tif (list_opts.filter_branches)\n+\t\t\trefs_for_each_fullref_in(get_main_ref_store(the_repository),\n+\t\t\t\t\t\t \"refs/heads/\", NULL,\n+\t\t\t\t\t\t list_ref, &list_opts);\n+\n+\t\tif (list_opts.filter_tags)\n+\t\t\trefs_for_each_fullref_in(get_main_ref_store(the_repository),\n+\t\t\t\t\t\t \"refs/tags/\", NULL,\n+\t\t\t\t\t\t list_ref, &list_opts);\n+\t} else {\n+\t\trefs_for_each_ref(get_main_ref_store(the_repository),\n+\t\t\t\t  list_ref, &list_opts);\n+\t}\n+\n+\tif (!list_opts.found_match)\n+\t\treturn 1;\n+\n+\treturn 0;\n+}\n+\n int cmd_refs(int argc,\n \t     const char **argv,\n \t     const char *prefix,\n@@ -109,12 +217,14 @@ int cmd_refs(int argc,\n \tconst char * const refs_usage[] = {\n \t\tREFS_MIGRATE_USAGE,\n \t\tREFS_VERIFY_USAGE,\n+\t\tREFS_LIST_USAGE,\n \t\tNULL,\n \t};\n \tparse_opt_subcommand_fn *fn = NULL;\n \tstruct option opts[] = {\n \t\tOPT_SUBCOMMAND(\"migrate\", &fn, cmd_refs_migrate),\n \t\tOPT_SUBCOMMAND(\"verify\", &fn, cmd_refs_verify),\n+\t\tOPT_SUBCOMMAND(\"list\", &fn, cmd_refs_list),\n \t\tOPT_END(),\n \t};\n \n-- \n2.34.1\n\n"},{"id":"520246","messageId":"20250614070536.17320-3-meetsoni3017@gmail.com","threadId":"63644","inReplyTo":"20250614070536.17320-1-meetsoni3017@gmail.com","subject":"[GSoC][RFC PATCH 2/2] t: add tests for refs list subcommand","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-06-14T07:05:36Z","receivedAt":"2025-06-14T07:06:47Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"Test the implemented functionality of `git refs list` and verify\nbackward compatibility with `git show-ref` for the supported flags\nand patterns.\n\nMentored-by: Patrick Steinhardt <ps@pks.im>\nMentored-by: shejialuo <shejialuo@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nSigned-off-by: Meet Soni <meetsoni3017@gmail.com>\n---\n t/meson.build        |  1 +\n t/t1461-refs-list.sh | 95 ++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 96 insertions(+)\n create mode 100755 t/t1461-refs-list.sh\n\ndiff --git a/t/meson.build b/t/meson.build\nindex d052fc3e23..c9d0863490 100644\n--- a/t/meson.build\n+++ b/t/meson.build\n@@ -224,6 +224,7 @@ integration_tests = [\n   't1450-fsck.sh',\n   't1451-fsck-buffer.sh',\n   't1460-refs-migrate.sh',\n+  't1461-refs-list.sh',\n   't1500-rev-parse.sh',\n   't1501-work-tree.sh',\n   't1502-rev-parse-parseopt.sh',\ndiff --git a/t/t1461-refs-list.sh b/t/t1461-refs-list.sh\nnew file mode 100755\nindex 0000000000..d628a193fc\n--- /dev/null\n+++ b/t/t1461-refs-list.sh\n@@ -0,0 +1,95 @@\n+#!/bin/sh\n+\n+test_description='Verify git refs list functionality and compatibility with git show-ref'\n+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n+\n+. ./test-lib.sh\n+\n+test_expect_success setup '\n+\ttest_commit --annotate A &&\n+\tgit checkout -b side &&\n+\ttest_commit --annotate B &&\n+\tgit checkout main &&\n+\ttest_commit C &&\n+\tgit branch B A^0\n+'\n+\n+test_expect_success 'refs list --branches, --tags, --head, pattern' '\n+\tfor branch in B main side\n+\tdo\n+\t\techo $(git rev-parse refs/heads/$branch) refs/heads/$branch || return 1\n+\tdone >expect.branches &&\n+\tgit refs list --branches >actual &&\n+\ttest_cmp expect.branches actual &&\n+\n+\tfor tag in A B C\n+\tdo\n+\t\techo $(git rev-parse refs/tags/$tag) refs/tags/$tag || return 1\n+\tdone >expect.tags &&\n+\tgit refs list --tags >actual &&\n+\ttest_cmp expect.tags actual &&\n+\n+\tcat expect.branches expect.tags >expect &&\n+\tgit refs list --branches --tags >actual &&\n+\ttest_cmp expect actual &&\n+\n+\t{\n+\t\techo $(git rev-parse HEAD) HEAD &&\n+\t\tcat expect.branches expect.tags\n+\t} >expect &&\n+\tgit refs list --branches --tags --head >actual &&\n+\ttest_cmp expect actual &&\n+\n+\t{\n+\t\techo $(git rev-parse HEAD) HEAD &&\n+\t\techo $(git rev-parse refs/heads/B) refs/heads/B &&\n+\t\techo $(git rev-parse refs/tags/B) refs/tags/B\n+\t} >expect &&\n+\tgit refs list --head B >actual &&\n+\ttest_cmp expect actual &&\n+\n+\t{\n+\t\techo $(git rev-parse refs/heads/B) refs/heads/B &&\n+\t\techo $(git rev-parse refs/tags/A) refs/tags/A &&\n+\t\techo $(git rev-parse refs/tags/B) refs/tags/B\n+\t} >expect &&\n+\tgit refs list A B >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'Backward compatibility with show-ref' '\n+\tgit show-ref >expect&&\n+\tgit refs list >actual&&\n+\ttest_cmp expect actual &&\n+\n+\tgit show-ref --branches >expect &&\n+\tgit refs list --branches >actual &&\n+\ttest_cmp expect actual &&\n+\n+\tgit show-ref --tags >expect &&\n+\tgit refs list --tags >actual &&\n+\ttest_cmp expect actual &&\n+\n+\tgit show-ref --head >expect &&\n+\tgit refs list --head >actual &&\n+\ttest_cmp expect actual &&\n+\n+\tgit show-ref --branches --tags --head >expect &&\n+\tgit refs list --branches --tags --head >actual &&\n+\ttest_cmp expect actual &&\n+\n+\tgit show-ref B >expect &&\n+\tgit refs list B >actual &&\n+\ttest_cmp expect actual &&\n+\n+\tgit show-ref --head B >expect &&\n+\tgit refs list --head B >actual &&\n+\ttest_cmp expect actual &&\n+\n+\tgit show-ref A B >expect &&\n+\tgit refs list A B >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_done\n-- \n2.34.1\n\n"},{"id":"520255","messageId":"xmqqsek16g6c.fsf@gitster.g","threadId":"63644","inReplyTo":"20250614070536.17320-1-meetsoni3017@gmail.com","subject":"Re: [GSoC][RFC PATCH 0/2] Add refs list subcommand","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-14T23:45:31Z","receivedAt":"2025-06-14T23:45:34Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Meet Soni <meetsoni3017@gmail.com> writes:\n\n> This RFC is meant to start a broader discussion on:\n>\n>   - The desired default behavior of pattern matching in git refs list\n>\n>   - Which features from both git-show-ref and git-for-each-ref should be\n>     preserved, rethought, or dropped\n>\n>   - How much backward compatibility we want to offer, and through what\n>     interface (e.g., compatibility flags)\n>\n> Feedback and thoughts on these topics would be very welcome.\n\nIn addition to these three points, 0th point perhaps is \"Is it\ndesirable to unify these two commands in the first place?\"\n\nI only use \"show-ref\" when I care about a single ref in a script,\nexpecting to be able to switch on its exit status.  Everything else\nI'd use for-each-ref.\n\nBut then that particular unique advantage of show-ref over for-each-ref\ncan be done with \"rev-parse --verify\".\n\nSo, I'd rather not to see yet another command to do the same thing.\nRather, is it insufficient to just use for-each-ref or rev-parse,\nlet the sleeping show-ref alone, and be happy?\n\n> Meet Soni (2):\n>   builtin/refs: add list subcommand\n>   t: add tests for refs list subcommand\n>\n>  Documentation/git-refs.adoc |  25 ++++++++\n>  builtin/refs.c              | 110 ++++++++++++++++++++++++++++++++++++\n>  t/meson.build               |   1 +\n>  t/t1461-refs-list.sh        |  95 +++++++++++++++++++++++++++++++\n>  4 files changed, 231 insertions(+)\n>  create mode 100755 t/t1461-refs-list.sh\n"},{"id":"520312","messageId":"CAPhwyn3N2O4OgPvEgec8C7Kvb1793s_KLq1u2y1tx6-msG+gFg@mail.gmail.com","threadId":"63644","inReplyTo":"xmqqsek16g6c.fsf@gitster.g","subject":"Re: [GSoC][RFC PATCH 0/2] Add refs list subcommand","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-06-17T11:51:33Z","receivedAt":"2025-06-17T11:51:45Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"On Sun, 15 Jun 2025 at 05:15, Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Meet Soni <meetsoni3017@gmail.com> writes:\n>\n> > This RFC is meant to start a broader discussion on:\n> >\n> >   - The desired default behavior of pattern matching in git refs list\n> >\n> >   - Which features from both git-show-ref and git-for-each-ref should be\n> >     preserved, rethought, or dropped\n> >\n> >   - How much backward compatibility we want to offer, and through what\n> >     interface (e.g., compatibility flags)\n> >\n> > Feedback and thoughts on these topics would be very welcome.\n>\n> In addition to these three points, 0th point perhaps is \"Is it\n> desirable to unify these two commands in the first place?\"\n>\n> I only use \"show-ref\" when I care about a single ref in a script,\n> expecting to be able to switch on its exit status.  Everything else\n> I'd use for-each-ref.\n>\n> But then that particular unique advantage of show-ref over for-each-ref\n> can be done with \"rev-parse --verify\".\n>\n> So, I'd rather not to see yet another command to do the same thing.\n> Rather, is it insufficient to just use for-each-ref or rev-parse,\n> let the sleeping show-ref alone, and be happy?\n\nThe broader motivation behind this effort (and the overarching GSoC project)\nhas been to reduce fragmentation in ref-related functionality by gradually\nconsolidating it under the git refs umbrella, aiming for consistency and\ndiscoverability.\n\nYour point about rev-parse --verify covering show-ref's main utility is well\ntaken. If that truly makes show-ref redundant, then perhaps it makes more sense\nto focus consolidation efforts around for-each-ref instead.\n\nOne concern I have is that if we simply replicate for-each-ref under a new\nname, we may still face the same question: what does this new command add that\nthe old one doesn't already handle?\n\nI'd love to hear thoughts on what a modernized or simplified ref-listing\ninterface could look like. Are there features or changes worth exploring that\ncould justify the consolidation?\n\nThanks,\nMeet\n"}]}