threads / patch / 64190

patchbuiltin/refs: add 'get' subcommand

Subject: [GSoC][PATCH] builtin/refs: add 'get' subcommand

## tl;dr

11 messages between Sep 23, 2025 and Sep 25, 2025. Diffs are folded; open one to read it.

replies: 10people: 4as markdown or json

Meet Soni· Sep 23, 2025, 10:45 UTC · lore

While `git-rev-parse(1)` and `git-show-ref(1)` can be used to read reference values, they have drawbacks for scripting and discoverability. `rev-parse` performs DWIM expansion which is unpredictable for scripts, and `show-ref --verify` is difficult to discover and cannot read the direct target of a symbolic reference.

To address this, introduce a new plumbing command, `git refs get <ref>`. This new command provides three key advantages:

  - It requires an exact refname and does not perform expansion, making
    it safer and more predictable for scripting.
  - Its name clearly states its purpose and it lives in the logical `git
    refs` namespace, unlike the `--verify` flag which lives in
    `git-show-ref`.
  - It provides a clean, dedicated way to read the direct target of a
    symbolic reference (e.g., `HEAD`) without recursively dereferencing
    it to an object ID.

Add documentation for the new subcommand to the `git-refs(1)` man page and a comprehensive test suite to verify its behavior.

Mentored-by: Patrick Steinhardt <ps@pks.im>
Mentored-by: shejialuo <shejialuo@gmail.com>
Signed-off-by: Meet Soni <meetsoni3017@gmail.com>
---
 Documentation/git-refs.adoc |  7 ++++
 builtin/refs.c              | 43 ++++++++++++++++++++++++
 t/meson.build               |  1 +
 t/t1464-refs-get.sh         | 66 +++++++++++++++++++++++++++++++++++++
 4 files changed, 117 insertions(+)
 create mode 100755 t/t1464-refs-get.sh
Show changes to 4 files +117 −0

Documentation/git-refs.adoc, builtin/refs.c, t/meson.build, t/t1464-refs-get.sh

diff --git a/Documentation/git-refs.adoc b/Documentation/git-refs.adoc
index bfa9b3ea2d..f07fe8c864 100644
--- a/Documentation/git-refs.adoc
+++ b/Documentation/git-refs.adoc
@@ -19,6 +19,7 @@ git refs list [--count=<count>] [--shell|--perl|--python|--tcl]
 		   [(--exclude=<pattern>)...] [--start-after=<marker>]
 		   [ --stdin | (<pattern>...)]
 git refs exists <ref>
+git refs get <ref>
 
 DESCRIPTION
 -----------
@@ -45,6 +46,12 @@ exists::
 	failed with an error other than the reference being missing. This does
 	not verify whether the reference resolves to an actual object.
 
+get::
+	Reads the raw value of a single, exact reference. Instead of
+	recursively dereferencing symbolic references, this command prints the
+	direct target of the symref (e.g., ref: refs/heads/main). For regular
+	references, it prints the object ID (SHA-1) they point to.
+
 OPTIONS
 -------
 
diff --git a/builtin/refs.c b/builtin/refs.c
index 91548783b7..b473a78e18 100644
--- a/builtin/refs.c
+++ b/builtin/refs.c
@@ -2,6 +2,7 @@
 #include "builtin.h"
 #include "config.h"
 #include "fsck.h"
+#include "hex.h"
 #include "parse-options.h"
 #include "refs.h"
 #include "strbuf.h"
@@ -18,6 +19,9 @@
 #define REFS_EXISTS_USAGE \
 	N_("git refs exists <ref>")
 
+#define REFS_GET_USAGE \
+	N_("git refs get <ref>")
+
 static int cmd_refs_migrate(int argc, const char **argv, const char *prefix,
 			    struct repository *repo UNUSED)
 {
@@ -159,6 +163,43 @@ static int cmd_refs_exists(int argc, const char **argv, const char *prefix,
 	return ret;
 }
 
+static int cmd_refs_get(int argc, const char **argv, const char *prefix,
+			struct repository *repo UNUSED)
+{
+	const char *refname;
+	struct object_id oid;
+	unsigned int type;
+	int failure_errno = 0;
+	struct strbuf referent = STRBUF_INIT;
+
+	const char * const exists_usage[] = {
+		REFS_EXISTS_USAGE,
+		NULL,
+	};
+	struct option options[] = {
+		OPT_END(),
+	};
+
+	argc = parse_options(argc, argv, prefix, options, exists_usage, 0);
+	if (argc != 1)
+		die("refs get requires exactly one reference");
+
+	refname = *argv++;
+	if (refs_read_raw_ref(get_main_ref_store(the_repository), refname,
+			      &oid, &referent, &type, &failure_errno)) {
+		die("'%s' - not a valid ref", refname);
+	}
+
+	if (type & REF_ISSYMREF) {
+		printf("ref: %s\n", referent.buf);
+	} else {
+		printf("%s\n", oid_to_hex(&oid));
+	}
+
+	strbuf_release(&referent);
+	return 0;
+}
+
 int cmd_refs(int argc,
 	     const char **argv,
 	     const char *prefix,
@@ -169,6 +210,7 @@ int cmd_refs(int argc,
 		REFS_VERIFY_USAGE,
 		"git refs list " COMMON_USAGE_FOR_EACH_REF,
 		REFS_EXISTS_USAGE,
+		REFS_GET_USAGE,
 		NULL,
 	};
 	parse_opt_subcommand_fn *fn = NULL;
@@ -177,6 +219,7 @@ int cmd_refs(int argc,
 		OPT_SUBCOMMAND("verify", &fn, cmd_refs_verify),
 		OPT_SUBCOMMAND("list", &fn, cmd_refs_list),
 		OPT_SUBCOMMAND("exists", &fn, cmd_refs_exists),
+		OPT_SUBCOMMAND("get", &fn, cmd_refs_get),
 		OPT_END(),
 	};
 
diff --git a/t/meson.build b/t/meson.build
index 7974795fe4..0c8067c69d 100644
--- a/t/meson.build
+++ b/t/meson.build
@@ -213,6 +213,7 @@ integration_tests = [
   't1460-refs-migrate.sh',
   't1461-refs-list.sh',
   't1462-refs-exists.sh',
+  't1464-refs-get.sh',
   't1500-rev-parse.sh',
   't1501-work-tree.sh',
   't1502-rev-parse-parseopt.sh',
diff --git a/t/t1464-refs-get.sh b/t/t1464-refs-get.sh
new file mode 100755
index 0000000000..166176c881
--- /dev/null
+++ b/t/t1464-refs-get.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+
+test_description='git refs get'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+test_expect_success 'setup repository' '
+	test_commit one &&
+	git tag -a -m "tagging one" my-tag one &&
+	git symbolic-ref refs/my-symref refs/heads/main &&
+	git symbolic-ref refs/dangling-symref refs/heads/no-such-branch
+'
+
+test_expect_success 'fails with no arguments' '
+	test_must_fail git refs get >out 2>err &&
+	test_grep "refs get requires exactly one reference" err
+'
+
+test_expect_success 'fails with too many arguments' '
+	test_must_fail git refs get HEAD HEAD >out 2>err &&
+	test_grep "refs get requires exactly one reference" err
+'
+
+test_expect_success 'get a branch head' '
+	git rev-parse main >expect &&
+	git refs get refs/heads/main >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'get an annotated tag' '
+	git rev-parse my-tag >expect &&
+	git refs get refs/tags/my-tag >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'get HEAD (a symbolic ref)' '
+	echo "ref: refs/heads/main" >expect &&
+	git refs get HEAD >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'get a custom symbolic ref' '
+	echo "ref: refs/heads/main" >expect &&
+	git refs get refs/my-symref >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'get a dangling symbolic ref' '
+	echo "ref: refs/heads/no-such-branch" >expect &&
+	git refs get refs/dangling-symref >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'get a non-existent ref' '
+	test_must_fail git refs get refs/heads/no-such-branch 2>err &&
+	test_grep "not a valid ref" err
+'
+
+test_expect_success 'get does not perform DWIM' '
+	test_must_fail git refs get main 2>err &&
+	test_grep "not a valid ref" err
+'
+
+test_done

base-commit: ca2559c1d630eb4f04cdee2328aaf1c768907a9e
-- 
2.34.1
Ben Knoble· Sep 23, 2025, 16:57 UTC · re: Meet Soni · lore

Re: [GSoC][PATCH] builtin/refs: add 'get' subcommand

With apologies if I cover well-trodden ground, as I haven’t been closely following this effort.
Show 5 quoted lines
> Le 23 sept. 2025 à 06:47, Meet Soni <meetsoni3017@gmail.com> a écrit :
> 
> While `git-rev-parse(1)` and `git-show-ref(1)` can be used to read
> reference values, they have drawbacks for scripting and discoverability.
> `rev-parse` performs DWIM expansion which is unpredictable for scripts,
[snip]
Show 5 quoted lines
> To address this, introduce a new plumbing command, `git refs get <ref>`.
> This new command provides three key advantages:
> 
>  - It requires an exact refname and does not perform expansion, making
>    it safer and more predictable for scripting.
What are the disadvantages of rev-parse’s DWIMmery in scripts? I would think it makes handling user input easier (e.g., my custom script can take a local branch name without writing « refs/heads/ » on the command-line). OTOH, a script that wants to precisely identify a ref can do so already, no?
Since rev-parse presumably won’t go away, it might be ok to have 2 ways of parsing (one with magic and one without), but that might be back to the same boat of not having a unified interface 😅
Perhaps later we can add a « --dwim » flag for looser parsing, giving scripteds flexibility but strictness by default?
Patrick Steinhardt· Sep 24, 2025, 06:32 UTC · re: Ben Knoble · lore

Re: [GSoC][PATCH] builtin/refs: add 'get' subcommand

On Tue, Sep 23, 2025 at 12:57:04PM -0400, Ben Knoble wrote:
Show 28 quoted lines
> With apologies if I cover well-trodden ground, as I haven’t been closely following this effort.
> 
> > Le 23 sept. 2025 à 06:47, Meet Soni <meetsoni3017@gmail.com> a écrit :
> > 
> > While `git-rev-parse(1)` and `git-show-ref(1)` can be used to read
> > reference values, they have drawbacks for scripting and discoverability.
> > `rev-parse` performs DWIM expansion which is unpredictable for scripts,
> 
> [snip]
> 
> > To address this, introduce a new plumbing command, `git refs get <ref>`.
> > This new command provides three key advantages:
> > 
> >  - It requires an exact refname and does not perform expansion, making
> >    it safer and more predictable for scripting.
> 
> What are the disadvantages of rev-parse’s DWIMmery in scripts? I would
> think it makes handling user input easier (e.g., my custom script can
> take a local branch name without writing « refs/heads/ » on the
> command-line). OTOH, a script that wants to precisely identify a ref
> can do so already, no?
> 
> Since rev-parse presumably won’t go away, it might be ok to have 2
> ways of parsing (one with magic and one without), but that might be
> back to the same boat of not having a unified interface 😅
> 
> Perhaps later we can add a « --dwim » flag for looser parsing, giving
> scripteds flexibility but strictness by default?

I think having such a "--dwim" flag at a later point could be a good idea, yeah.

Patrick
Junio C Hamano· Sep 23, 2025, 21:50 UTC · re: Meet Soni · lore

Re: [GSoC][PATCH] builtin/refs: add 'get' subcommand

Meet Soni <meetsoni3017@gmail.com> writes:
Show 5 quoted lines
> While `git-rev-parse(1)` and `git-show-ref(1)` can be used to read
> reference values, they have drawbacks for scripting and discoverability.
> `rev-parse` performs DWIM expansion which is unpredictable for scripts,
> and `show-ref --verify` is difficult to discover and cannot read the
> direct target of a symbolic reference.

Well "refs get" is even harder to discover (it is not even in Git 2.50's manual that is available everywhere on the net), so difficult to discover is not a good excuse. In a sense show-ref was invented exactly to serve as something like "refs get" you are writing, so I wonder if a better approach is to extend it instead of introducing a new subcommand in a distant place from it?

Perhaps "show-ref --verify --no-deref" or something that does not dereference but works directly on a symbolic ref?

Patrick Steinhardt· Sep 24, 2025, 06:32 UTC · re: Junio C Hamano · lore

Re: [GSoC][PATCH] builtin/refs: add 'get' subcommand

On Tue, Sep 23, 2025 at 02:50:46PM -0700, Junio C Hamano wrote:
Show 17 quoted lines
> Meet Soni <meetsoni3017@gmail.com> writes:
> 
> > While `git-rev-parse(1)` and `git-show-ref(1)` can be used to read
> > reference values, they have drawbacks for scripting and discoverability.
> > `rev-parse` performs DWIM expansion which is unpredictable for scripts,
> > and `show-ref --verify` is difficult to discover and cannot read the
> > direct target of a symbolic reference.
> 
> Well "refs get" is even harder to discover (it is not even in Git
> 2.50's manual that is available everywhere on the net), so difficult
> to discover is not a good excuse.  In a sense show-ref was invented
> exactly to serve as something like "refs get" you are writing, so I
> wonder if a better approach is to extend it instead of introducing
> a new subcommand in a distant place from it?
> 
> Perhaps "show-ref --verify --no-deref" or something that does not
> dereference but works directly on a symbolic ref?

For now: yes, it's more difficult to discover for sure. But users will adjust over time as they get more familiar with git-refs(1), and from thereon I think it will become significantly easier to discover that subcommand.

git-refs(1) already hosts everything needed to handle references, so from my point of view it is only natural to also provide an easy way to read a single reference to complete the picture.

Patrick
Ben Knoble· Sep 24, 2025, 15:29 UTC · re: Patrick Steinhardt · lore

Re: [GSoC][PATCH] builtin/refs: add 'get' subcommand

Show 25 quoted lines
> Le 24 sept. 2025 à 02:37, Patrick Steinhardt <ps@pks.im> a écrit :
> 
> On Tue, Sep 23, 2025 at 02:50:46PM -0700, Junio C Hamano wrote:
>> Meet Soni <meetsoni3017@gmail.com> writes:
>> 
>>> While `git-rev-parse(1)` and `git-show-ref(1)` can be used to read
>>> reference values, they have drawbacks for scripting and discoverability.
>>> `rev-parse` performs DWIM expansion which is unpredictable for scripts,
>>> and `show-ref --verify` is difficult to discover and cannot read the
>>> direct target of a symbolic reference.
>> 
>> Well "refs get" is even harder to discover (it is not even in Git
>> 2.50's manual that is available everywhere on the net), so difficult
>> to discover is not a good excuse.  In a sense show-ref was invented
>> exactly to serve as something like "refs get" you are writing, so I
>> wonder if a better approach is to extend it instead of introducing
>> a new subcommand in a distant place from it?
>> 
>> Perhaps "show-ref --verify --no-deref" or something that does not
>> dereference but works directly on a symbolic ref?
> 
> For now: yes, it's more difficult to discover for sure. But users will
> adjust over time as they get more familiar with git-refs(1), and from
> thereon I think it will become significantly easier to discover that
> subcommand.
I think this goes to perhaps some of my unasked questions: who is the target audience? My experience suggest that most mostly-porcelain users don’t acquire familiarity with scripting commands, so it sounds like we’re talking about script-writers here (and in the commit message).
But how do we encourage script writers to discover these things? 🤔 Hm. 
Junio C Hamano· Sep 24, 2025, 17:11 UTC · re: Ben Knoble · lore

Re: [GSoC][PATCH] builtin/refs: add 'get' subcommand

Ben Knoble <ben.knoble@gmail.com> writes:
Show 7 quoted lines
>>> Perhaps "show-ref --verify --no-deref" or something that does not
>>> dereference but works directly on a symbolic ref?
>> 
>> For now: yes, it's more difficult to discover for sure. But users will
>> adjust over time as they get more familiar with git-refs(1), and from
>> thereon I think it will become significantly easier to discover that
>> subcommand.

But unfortunately, that is a tautology, isn't it? With the same effort to advertise git-refs to make it more familiar to the "users", you can make "show-ref" familiar to the same "users", and problem solved, without a need to do anything to "git-refs"?

Show 7 quoted lines
> I think this goes to perhaps some of my unasked questions: who is
> the target audience? My experience suggest that most
> mostly-porcelain users don’t acquire familiarity with scripting
> commands, so it sounds like we’re talking about script-writers
> here (and in the commit message).
>
> But how do we encourage script writers to discover these things? 🤔 Hm. 

Great question. I understand what the patch author is trying to achieve (i.e. "consolidate ref-related functionality into git-refs", which is the title of GSoC project [*]), but what are we, as Git project, trying to achive by "consolidating"? I often cannot shake the feeling that it may a make-work job without a clear answer to that question. Or perhps xkcd.com/927/?

Perhaps the hope is to have a single kitchen sink "git refs" command that does anything related to "refs", so that they only need to learn this single command (and unlearn all the previous experiences they gained) and after that, they do not have to "discover" more things?

[Reference]
* https://summerofcode.withgoogle.com/programs/2025/projects/xVrT5e2q
Patrick Steinhardt· Sep 25, 2025, 06:25 UTC · re: Junio C Hamano · lore

Re: [GSoC][PATCH] builtin/refs: add 'get' subcommand

On Wed, Sep 24, 2025 at 10:11:08AM -0700, Junio C Hamano wrote:
Show 14 quoted lines
> Ben Knoble <ben.knoble@gmail.com> writes:
> 
> >>> Perhaps "show-ref --verify --no-deref" or something that does not
> >>> dereference but works directly on a symbolic ref?
> >> 
> >> For now: yes, it's more difficult to discover for sure. But users will
> >> adjust over time as they get more familiar with git-refs(1), and from
> >> thereon I think it will become significantly easier to discover that
> >> subcommand.
> 
> But unfortunately, that is a tautology, isn't it?  With the same
> effort to advertise git-refs to make it more familiar to the
> "users", you can make "show-ref" familiar to the same "users", and
> problem solved, without a need to do anything to "git-refs"?

I don't quite think so. The problem is that we have so many different tools that relate to refs, and you have to remember all of them:

  - `git show-refs --verify` to read a single reference, unless it's a
    symbolic reference.
  - `git symbolic-ref` to read symbolic refs.
  - `git show-refs --exists` to check a reference for existence.
  - `git show-ref` and `git for-each-ref` to list references.
  - `git pack-refs` to optimize references.
  - `git update-refs` to update references`
I'd claim that this is quite hard to remember. So...
Show 20 quoted lines
> > I think this goes to perhaps some of my unasked questions: who is
> > the target audience? My experience suggest that most
> > mostly-porcelain users don’t acquire familiarity with scripting
> > commands, so it sounds like we’re talking about script-writers
> > here (and in the commit message).
> >
> > But how do we encourage script writers to discover these things? 🤔 Hm. 
> 
> Great question.  I understand what the patch author is trying to
> achieve (i.e. "consolidate ref-related functionality into git-refs",
> which is the title of GSoC project [*]), but what are we, as Git
> project, trying to achive by "consolidating"?  I often cannot shake
> the feeling that it may a make-work job without a clear answer to
> that question.  Or perhps xkcd.com/927/?
> 
> Perhaps the hope is to have a single kitchen sink "git refs" command
> that does anything related to "refs", so that they only need to
> learn this single command (and unlearn all the previous experiences
> they gained) and after that, they do not have to "discover" more
> things?

... yes, this is exactly the goal of this exercise. You basically only need to know about the entrypoint git-refs(1). Once you know about it, you don't have to discover all the other commands, as it is now way easier to discover what ref-related functionality you have available. You can easily use tab completion (well, once it's wired up), type `git refs -h` to learn about evertyhing refs, and we now have a single manpage that will tell you everything about ref-related use commands.

You could partially address that problem by providing a gitrefs(5) manpage that gives an overview. And maybe that's still something one could do, also to paint a bit of a broader picture. But documentation is only part of the solution -- with git-refs(1) we get some "natural" discoverability.

That's also where the "git refs get" proposal comes from. Sure, you can use `git show-refs --verify`, potentially with a `--no-dereference` flag if you want to read normal refs. But I would claim that this is almost impossible to discover without searching through our manpages.

Patrick
D. Ben Knoble· Sep 25, 2025, 18:08 UTC · re: Patrick Steinhardt · lore

Re: [GSoC][PATCH] builtin/refs: add 'get' subcommand

On Thu, Sep 25, 2025 at 2:25 AM Patrick Steinhardt <ps@pks.im> wrote:
Show 34 quoted lines
>
> On Wed, Sep 24, 2025 at 10:11:08AM -0700, Junio C Hamano wrote:
> > Ben Knoble <ben.knoble@gmail.com> writes:
> >
> > >>> Perhaps "show-ref --verify --no-deref" or something that does not
> > >>> dereference but works directly on a symbolic ref?
> > >>
> > >> For now: yes, it's more difficult to discover for sure. But users will
> > >> adjust over time as they get more familiar with git-refs(1), and from
> > >> thereon I think it will become significantly easier to discover that
> > >> subcommand.
> >
> > But unfortunately, that is a tautology, isn't it?  With the same
> > effort to advertise git-refs to make it more familiar to the
> > "users", you can make "show-ref" familiar to the same "users", and
> > problem solved, without a need to do anything to "git-refs"?
>
> I don't quite think so. The problem is that we have so many different
> tools that relate to refs, and you have to remember all of them:
>
>   - `git show-refs --verify` to read a single reference, unless it's a
>     symbolic reference.
>
>   - `git symbolic-ref` to read symbolic refs.
>
>   - `git show-refs --exists` to check a reference for existence.
>
>   - `git show-ref` and `git for-each-ref` to list references.
>
>   - `git pack-refs` to optimize references.
>
>   - `git update-refs` to update references`
>
> I'd claim that this is quite hard to remember. So...

Agreed! To be clear: me asking questions should be taken as support for this exercise :)

Show 28 quoted lines
> > > I think this goes to perhaps some of my unasked questions: who is
> > > the target audience? My experience suggest that most
> > > mostly-porcelain users don’t acquire familiarity with scripting
> > > commands, so it sounds like we’re talking about script-writers
> > > here (and in the commit message).
> > >
> > > But how do we encourage script writers to discover these things? 🤔 Hm.
> >
> > Great question.  I understand what the patch author is trying to
> > achieve (i.e. "consolidate ref-related functionality into git-refs",
> > which is the title of GSoC project [*]), but what are we, as Git
> > project, trying to achive by "consolidating"?  I often cannot shake
> > the feeling that it may a make-work job without a clear answer to
> > that question.  Or perhps xkcd.com/927/?
> >
> > Perhaps the hope is to have a single kitchen sink "git refs" command
> > that does anything related to "refs", so that they only need to
> > learn this single command (and unlearn all the previous experiences
> > they gained) and after that, they do not have to "discover" more
> > things?
>
> ... yes, this is exactly the goal of this exercise. You basically only
> need to know about the entrypoint git-refs(1). Once you know about it,
> you don't have to discover all the other commands, as it is now way
> easier to discover what ref-related functionality you have available.
> You can easily use tab completion (well, once it's wired up), type `git
> refs -h` to learn about evertyhing refs, and we now have a single
> manpage that will tell you everything about ref-related use commands.

Ah, but here's perhaps my question: tab-completion suggests primarily porcelain users over plumbing users, to me ;)

I admit I blur the line quite a bit myself, being unafraid to string together plumbing commands at a live shell (or make abominations like git-greb [1]).

At any rate, the target audience need not be precise now. I hope my confusion is clear, though :)

[1]: https://benknoble.github.io/blog/2025/09/17/blame/
Show 12 quoted lines
> You could partially address that problem by providing a gitrefs(5)
> manpage that gives an overview. And maybe that's still something one
> could do, also to paint a bit of a broader picture. But documentation is
> only part of the solution -- with git-refs(1) we get some "natural"
> discoverability.
>
> That's also where the "git refs get" proposal comes from. Sure, you can
> use `git show-refs --verify`, potentially with a `--no-dereference` flag
> if you want to read normal refs. But I would claim that this is almost
> impossible to discover without searching through our manpages.
>
> Patrick
This all makes sense to me.
-- 
D. Ben Knoble
Junio C Hamano· Sep 25, 2025, 18:43 UTC · re: D. Ben Knoble · lore

Re: [GSoC][PATCH] builtin/refs: add 'get' subcommand

"D. Ben Knoble" <ben.knoble@gmail.com> writes:
>> I don't quite think so. The problem is that we have so many different
>> tools that relate to refs, and you have to remember all of them:
Yup, but ...
Show 17 quoted lines
>>   - `git show-refs --verify` to read a single reference, unless it's a
>>     symbolic reference.
>>
>>   - `git symbolic-ref` to read symbolic refs.
>>
>>   - `git show-refs --exists` to check a reference for existence.
>>
>>   - `git show-ref` and `git for-each-ref` to list references.
>>
>>   - `git pack-refs` to optimize references.
>>
>>   - `git update-refs` to update references`
>>
>> I'd claim that this is quite hard to remember. So...
>
> Agreed! To be clear: me asking questions should be taken as support
> for this exercise :)

... the same thing can be said about subcommands of "git refs", all of which you have to remember. I am not sure if this "everything under "git refs" really makes much difference.

>> That's also where the "git refs get" proposal comes from. Sure, you can
>> use `git show-refs --verify`, potentially with a `--no-dereference` flag
>> if you want to read normal refs. But I would claim that this is almost
>> impossible to discover without searching through our manpages.

So? That still does not indicate adding yet another command to do it is the right solution to the discover-ability problem. Instead of shifting and moving things around, reimplementing things to risk introducing new bugs, wouldn't it be more productive to spend effort on improving the documentation and possibly filling the gaps of features?

Thanks.
Patrick Steinhardt· Sep 24, 2025, 06:32 UTC · re: Meet Soni · lore

Re: [GSoC][PATCH] builtin/refs: add 'get' subcommand

On Tue, Sep 23, 2025 at 04:15:33PM +0530, Meet Soni wrote:
Show 18 quoted lines
> diff --git a/Documentation/git-refs.adoc b/Documentation/git-refs.adoc
> index bfa9b3ea2d..f07fe8c864 100644
> --- a/Documentation/git-refs.adoc
> +++ b/Documentation/git-refs.adoc
> @@ -19,6 +19,7 @@ git refs list [--count=<count>] [--shell|--perl|--python|--tcl]
>  		   [(--exclude=<pattern>)...] [--start-after=<marker>]
>  		   [ --stdin | (<pattern>...)]
>  git refs exists <ref>
> +git refs get <ref>
>  
>  DESCRIPTION
>  -----------
> @@ -45,6 +46,12 @@ exists::
>  	failed with an error other than the reference being missing. This does
>  	not verify whether the reference resolves to an actual object.
>  
> +get::
> +	Reads the raw value of a single, exact reference. Instead of
Let's say "fully qualified" instead of "exact".
> +	recursively dereferencing symbolic references, this command prints the
> +	direct target of the symref (e.g., ref: refs/heads/main). For regular
> +	references, it prints the object ID (SHA-1) they point to.
I'd drop the reference to SHA1 here, as it may be any object hash.
Show 16 quoted lines
> diff --git a/builtin/refs.c b/builtin/refs.c
> index 91548783b7..b473a78e18 100644
> --- a/builtin/refs.c
> +++ b/builtin/refs.c
> @@ -159,6 +163,43 @@ static int cmd_refs_exists(int argc, const char **argv, const char *prefix,
>  	return ret;
>  }
>  
> +static int cmd_refs_get(int argc, const char **argv, const char *prefix,
> +			struct repository *repo UNUSED)
> +{
> +	const char *refname;
> +	struct object_id oid;
> +	unsigned int type;
> +	int failure_errno = 0;
> +	struct strbuf referent = STRBUF_INIT;

Tiny nit: we typically order variables that aren't accessed by options after the options array.

Show 11 quoted lines
> +	const char * const exists_usage[] = {
> +		REFS_EXISTS_USAGE,
> +		NULL,
> +	};
> +	struct option options[] = {
> +		OPT_END(),
> +	};
> +
> +	argc = parse_options(argc, argv, prefix, options, exists_usage, 0);
> +	if (argc != 1)
> +		die("refs get requires exactly one reference");

This should be translatable. Furthermore, we can probably use `usagef()` instead to have a "usage:" prefix instead of "fatal:".

> +	refname = *argv++;
> +	if (refs_read_raw_ref(get_main_ref_store(the_repository), refname,
> +			      &oid, &referent, &type, &failure_errno)) {
> +		die("'%s' - not a valid ref", refname);

We should discern by `failure_errno` here. Most importantly, I think we should handle `ENOENT` and `EISDIR` specially to both mean that the reference does not exist. So, e.g.:

	if (refs_read_raw_ref(get_main_ref_store(the_repository), refname,
			      &oid, &referent, &type, &failure_errno)) {
		if (failure_errno == ENOENT || failure_errno == EISDIR)
			die(_("reference does not exist"));
		else
			die_errno(_("failed to look up reference"));
	}
Show 7 quoted lines
> +	}
> +
> +	if (type & REF_ISSYMREF) {
> +		printf("ref: %s\n", referent.buf);
> +	} else {
> +		printf("%s\n", oid_to_hex(&oid));
> +	}
We can drop the curly braces around single-line bodies.
Patrick

← back to recent threads