Git development
 help / color / mirror / Atom feed
* [PATCH] ls-remote: add --show-object-format
@ 2026-11-05 10:45 Adrian Ratiu
  2026-11-05 14:13 ` Patrick Steinhardt
  0 siblings, 1 reply; 3+ messages in thread
From: Adrian Ratiu @ 2026-11-05 10:45 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, brian m . carlson, Axel Rasmussen, Emily Shaffer,
	Josh Steadmon, Adrian Ratiu

While working on JJ sha256 remote support (it uses Git for networking),
I noticed there is no way to get the object format of a repo without
cloning it. [1]

Git clone learns the remote format during the protocol handshake, but
it is not shown anywhere else. For example, when creating an empty repo
and running "git fetch":

1. If the remote has references, fetch can fail with a mismatch error
and "git ls-remote" shows the format indirectly through the length
of the object IDs. Not ideal, but the it can be figured out eventually.

2. If the remote is empty, "git ls-remote" prints nothing and fetch
succeeds, so the new local repo can silently get the wrong format,
which only shows up later, on the first push.

To fill this gap, add a ls-remote --show-object-format option which
prints the object format learned from the remote on a line of its own,
before the references.

[1]: https://github.com/jj-vcs/jj/issues/3813

Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com>
---
 Documentation/git-ls-remote.adoc | 10 ++++++-
 builtin/ls-remote.c              |  8 +++++-
 t/t5512-ls-remote.sh             | 46 ++++++++++++++++++++++++++++++++
 3 files changed, 62 insertions(+), 2 deletions(-)

diff --git a/Documentation/git-ls-remote.adoc b/Documentation/git-ls-remote.adoc
index d71c4ab3e2..1abf7b60b7 100644
--- a/Documentation/git-ls-remote.adoc
+++ b/Documentation/git-ls-remote.adoc
@@ -11,7 +11,8 @@ SYNOPSIS
 [verse]
 'git ls-remote' [--branches] [--tags] [--refs] [--upload-pack=<exec>]
 	      [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]
-	      [--symref] [<repository> [<patterns>...]]
+	      [--symref] [--show-object-format]
+	      [<repository> [<patterns>...]]
 
 DESCRIPTION
 -----------
@@ -64,6 +65,13 @@ OPTIONS
 	upload-pack only shows the symref HEAD, so it will be the only
 	one shown by ls-remote.
 
+--show-object-format::
+	Show the object format (hash algorithm, e.g. `sha1` or `sha256`) used
+	by the remote repository before printing the references, on a line of
+	its own. This also works for a remote repo without any refs, which is
+	useful to find out which object format to use for a local repository,
+	before pushing or fetching the new remote.
+
 --sort=<key>::
 	Sort based on the key given. Prefix `-` to sort in descending order
 	of the value. Supports "version:refname" or "v:refname" (tag names
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index fe77829557..6d961f9961 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -12,7 +12,8 @@
 static const char * const ls_remote_usage[] = {
 	N_("git ls-remote [--branches] [--tags] [--refs] [--upload-pack=<exec>]\n"
 	   "              [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]\n"
-	   "              [--symref] [<repository> [<patterns>...]]"),
+	   "              [--symref] [--show-object-format]\n"
+	   "              [<repository> [<patterns>...]]"),
 	NULL
 };
 
@@ -49,6 +50,7 @@ int cmd_ls_remote(int argc,
 	int quiet = 0;
 	int status = 0;
 	int show_symref_target = 0;
+	int show_object_format = 0;
 	const char *uploadpack = NULL;
 	struct strvec pattern = STRVEC_INIT;
 	struct transport_ls_refs_options transport_options =
@@ -89,6 +91,8 @@ int cmd_ls_remote(int argc,
 			      2, PARSE_OPT_NOCOMPLETE),
 		OPT_BOOL(0, "symref", &show_symref_target,
 			 N_("show underlying ref in addition to the object pointed by it")),
+		OPT_BOOL(0, "show-object-format", &show_object_format,
+			 N_("show the object format used by the remote repository")),
 		OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")),
 		OPT_END()
 	};
@@ -150,6 +154,8 @@ int cmd_ls_remote(int argc,
 
 	if (!dest && !quiet)
 		fprintf(stderr, "From %s\n", remote->url.v[0]);
+	if (show_object_format)
+		printf("%s\n", transport_get_hash_algo(transport)->name);
 	for ( ; ref; ref = ref->next) {
 		struct ref_array_item *item;
 		if (!check_ref_type(ref, flags))
diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh
index 2e1d4f7586..7e82154fe4 100755
--- a/t/t5512-ls-remote.sh
+++ b/t/t5512-ls-remote.sh
@@ -332,6 +332,52 @@ test_expect_success 'indicate no refs in v0 standards-compliant empty remote' '
 	test_expect_code 2 git ls-remote --exit-code --upload-pack=./cat-input .
 '
 
+# Run "ls-remote --show-object-format" with the given arguments over
+# protocol v0 and v2, and compare its output with "expect".
+test_show_object_format () {
+	for version in 0 2
+	do
+		git -c protocol.version=$version \
+			ls-remote --show-object-format "$@" >actual &&
+		test_cmp expect actual || return 1
+	done
+}
+
+test_expect_success 'ls-remote --show-object-format with empty sha1 remote' '
+	git init --bare --object-format=sha1 empty-sha1.git &&
+	echo sha1 >expect &&
+	test_show_object_format empty-sha1.git
+'
+
+test_expect_success 'ls-remote --show-object-format with empty sha256 remote' '
+	git init --bare --object-format=sha256 empty-sha256.git &&
+	echo sha256 >expect &&
+	test_show_object_format empty-sha256.git
+'
+
+test_expect_success 'ls-remote --show-object-format with sha1 remote' '
+	git init --object-format=sha1 repo-sha1 &&
+	test_commit -C repo-sha1 one &&
+	oid=$(git -C repo-sha1 rev-parse HEAD) &&
+	printf "sha1\n%s\tHEAD\n" "$oid" >expect &&
+	test_show_object_format repo-sha1 HEAD
+'
+
+test_expect_success 'ls-remote --show-object-format with sha256 remote' '
+	git init --object-format=sha256 repo-sha256 &&
+	test_commit -C repo-sha256 one &&
+	oid=$(git -C repo-sha256 rev-parse HEAD) &&
+	printf "sha256\n%s\tHEAD\n" "$oid" >expect &&
+	test_show_object_format repo-sha256 HEAD
+'
+
+test_expect_success 'ls-remote --show-object-format outside repository' '
+	nongit git init --bare --object-format=sha256 empty-nongit.git &&
+	echo sha256 >expect &&
+	nongit git ls-remote --show-object-format empty-nongit.git >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'ls-remote works outside repository' '
 	# It is important for this repo to be inside the nongit
 	# area, as we want a repo name that does not include
-- 
2.56.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH] ls-remote: add --show-object-format
  2026-11-05 10:45 [PATCH] ls-remote: add --show-object-format Adrian Ratiu
@ 2026-11-05 14:13 ` Patrick Steinhardt
  2026-11-05 16:35   ` Adrian Ratiu
  0 siblings, 1 reply; 3+ messages in thread
From: Patrick Steinhardt @ 2026-11-05 14:13 UTC (permalink / raw)
  To: Adrian Ratiu
  Cc: git, Junio C Hamano, brian m . carlson, Axel Rasmussen,
	Emily Shaffer, Josh Steadmon

On Thu, Nov 05, 2026 at 12:45:41PM +0200, Adrian Ratiu wrote:
> While working on JJ sha256 remote support (it uses Git for networking),
> I noticed there is no way to get the object format of a repo without
> cloning it. [1]
> 
> Git clone learns the remote format during the protocol handshake, but
> it is not shown anywhere else. For example, when creating an empty repo
> and running "git fetch":
> 
> 1. If the remote has references, fetch can fail with a mismatch error
> and "git ls-remote" shows the format indirectly through the length
> of the object IDs. Not ideal, but the it can be figured out eventually.

s/the it/then it/

Also, this is a somewhat imperfect way to detect the hash algorithm as
it assumes that we don't ever add a third hash that has the same length
as either of the existing ones. So it works now, but it may not work in
the future anymore.

> 2. If the remote is empty, "git ls-remote" prints nothing and fetch
> succeeds, so the new local repo can silently get the wrong format,
> which only shows up later, on the first push.
> 
> To fill this gap, add a ls-remote --show-object-format option which
> prints the object format learned from the remote on a line of its own,
> before the references.

Seems sensible.

> diff --git a/Documentation/git-ls-remote.adoc b/Documentation/git-ls-remote.adoc
> index d71c4ab3e2..1abf7b60b7 100644
> --- a/Documentation/git-ls-remote.adoc
> +++ b/Documentation/git-ls-remote.adoc
> @@ -64,6 +65,13 @@ OPTIONS
>  	upload-pack only shows the symref HEAD, so it will be the only
>  	one shown by ls-remote.
>  
> +--show-object-format::
> +	Show the object format (hash algorithm, e.g. `sha1` or `sha256`) used
> +	by the remote repository before printing the references, on a line of
> +	its own. This also works for a remote repo without any refs, which is
> +	useful to find out which object format to use for a local repository,

A bit curious that you first say "repo" and then "repository". I'd just
drop "repo".

> diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
> index fe77829557..6d961f9961 100644
> --- a/builtin/ls-remote.c
> +++ b/builtin/ls-remote.c
> @@ -150,6 +154,8 @@ int cmd_ls_remote(int argc,
>  
>  	if (!dest && !quiet)
>  		fprintf(stderr, "From %s\n", remote->url.v[0]);
> +	if (show_object_format)
> +		printf("%s\n", transport_get_hash_algo(transport)->name);

Should we maybe make this a tiny bit more extensible by not printing the
object format without any kind of prefix? We could for example instead
print "object-format: %s", which would allow us to add more fields in
the future without the need to make parsers be order-dependent.

Also, one more question I had is whether we know that the hash algo will
always be populated here. I would assume so, as we have this logic a
couple lines further up:

	ref = transport_get_remote_refs(transport, &transport_options);
	if (ref) {
		int hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
		repo_set_hash_algo(the_repository, hash_algo);
	}

But in theory, this logic would also work alright if the hash was NULL.

> diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh
> index 2e1d4f7586..7e82154fe4 100755
> --- a/t/t5512-ls-remote.sh
> +++ b/t/t5512-ls-remote.sh
> @@ -332,6 +332,52 @@ test_expect_success 'indicate no refs in v0 standards-compliant empty remote' '
>  	test_expect_code 2 git ls-remote --exit-code --upload-pack=./cat-input .
>  '
>  
> +# Run "ls-remote --show-object-format" with the given arguments over
> +# protocol v0 and v2, and compare its output with "expect".
> +test_show_object_format () {
> +	for version in 0 2
> +	do
> +		git -c protocol.version=$version \
> +			ls-remote --show-object-format "$@" >actual &&
> +		test_cmp expect actual || return 1

It's a bit weird that we expect the caller to already have written
`expect`. A better interface would be to have them call the expected
object format as another parameter.

> +	done
> +}
> +
> +test_expect_success 'ls-remote --show-object-format with empty sha1 remote' '
> +	git init --bare --object-format=sha1 empty-sha1.git &&
> +	echo sha1 >expect &&
> +	test_show_object_format empty-sha1.git
> +'
> +
> +test_expect_success 'ls-remote --show-object-format with empty sha256 remote' '
> +	git init --bare --object-format=sha256 empty-sha256.git &&
> +	echo sha256 >expect &&
> +	test_show_object_format empty-sha256.git
> +'

We could easily deduplicate these tests by looping around `for hash in
sha1 sha256`.

Thanks!

Patrick

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] ls-remote: add --show-object-format
  2026-11-05 14:13 ` Patrick Steinhardt
@ 2026-11-05 16:35   ` Adrian Ratiu
  0 siblings, 0 replies; 3+ messages in thread
From: Adrian Ratiu @ 2026-11-05 16:35 UTC (permalink / raw)
  To: Patrick Steinhardt
  Cc: git, Junio C Hamano, brian m . carlson, Axel Rasmussen,
	Emily Shaffer, Josh Steadmon

Hi Patrick and thank you for the awesome review, as always.

I'll let this sit for about 1 week to also give others a chance to
review, then send v2 addresing all your feedback.

On Thu, 05 Nov 2026, Patrick Steinhardt <ps@pks.im> wrote:
<snip>
>> diff --git a/Documentation/git-ls-remote.adoc b/Documentation/git-ls-remote.adoc
>> index d71c4ab3e2..1abf7b60b7 100644
>> --- a/Documentation/git-ls-remote.adoc
>> +++ b/Documentation/git-ls-remote.adoc
>> @@ -64,6 +65,13 @@ OPTIONS
>>  	upload-pack only shows the symref HEAD, so it will be the only
>>  	one shown by ls-remote.
>>  
>> +--show-object-format::
>> +	Show the object format (hash algorithm, e.g. `sha1` or `sha256`) used
>> +	by the remote repository before printing the references, on a line of
>> +	its own. This also works for a remote repo without any refs, which is
>> +	useful to find out which object format to use for a local repository,
>
> A bit curious that you first say "repo" and then "repository". I'd just
> drop "repo".

Initially I wrote repository twice then the lines didn't fit, so I cut
repository -> repo to make them fit. :)

English is also not my first nor second languege, so I tend to keep
rewriting multiple times before sending. It's still not "good" on the
Nth read, tbh.

I'll try to improve this further to avoid redundancies, for e.g. I just
realized I wrote "--show-objoct-format" then immediately I repeated "Show
the object format on the next line". =)

>
>> diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
>> index fe77829557..6d961f9961 100644
>> --- a/builtin/ls-remote.c
>> +++ b/builtin/ls-remote.c
>> @@ -150,6 +154,8 @@ int cmd_ls_remote(int argc,
>>  
>>  	if (!dest && !quiet)
>>  		fprintf(stderr, "From %s\n", remote->url.v[0]);
>> +	if (show_object_format)
>> +		printf("%s\n", transport_get_hash_algo(transport)->name);
>
> Should we maybe make this a tiny bit more extensible by not printing the
> object format without any kind of prefix? We could for example instead
> print "object-format: %s", which would allow us to add more fields in
> the future without the need to make parsers be order-dependent.

Yes, this is a great idea, will do in v2.

(I just need to know the sha1 vs sha256 information, the format can be
anything at all, no strong preference on my side).

>
> Also, one more question I had is whether we know that the hash algo will
> always be populated here. I would assume so, as we have this logic a
> couple lines further up:
>
> 	ref = transport_get_remote_refs(transport, &transport_options);
> 	if (ref) {
> 		int hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
> 		repo_set_hash_algo(the_repository, hash_algo);
> 	}
>
> But in theory, this logic would also work alright if the hash was
> NULL.

Yes, the hash format will always be populated by the time we call
transport_get_hash_algo() here.

>> diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh
>> index 2e1d4f7586..7e82154fe4 100755
>> --- a/t/t5512-ls-remote.sh
>> +++ b/t/t5512-ls-remote.sh
>> @@ -332,6 +332,52 @@ test_expect_success 'indicate no refs in v0 standards-compliant empty remote' '
>>  	test_expect_code 2 git ls-remote --exit-code --upload-pack=./cat-input .
>>  '
>>  
>> +# Run "ls-remote --show-object-format" with the given arguments over
>> +# protocol v0 and v2, and compare its output with "expect".
>> +test_show_object_format () {
>> +	for version in 0 2
>> +	do
>> +		git -c protocol.version=$version \
>> +			ls-remote --show-object-format "$@" >actual &&
>> +		test_cmp expect actual || return 1
>
> It's a bit weird that we expect the caller to already have written
> `expect`. A better interface would be to have them call the expected
> object format as another parameter.

Ack, will do in v2.

>> +	done
>> +}
>> +
>> +test_expect_success 'ls-remote --show-object-format with empty sha1 remote' '
>> +	git init --bare --object-format=sha1 empty-sha1.git &&
>> +	echo sha1 >expect &&
>> +	test_show_object_format empty-sha1.git
>> +'
>> +
>> +test_expect_success 'ls-remote --show-object-format with empty sha256 remote' '
>> +	git init --bare --object-format=sha256 empty-sha256.git &&
>> +	echo sha256 >expect &&
>> +	test_show_object_format empty-sha256.git
>> +'
>
> We could easily deduplicate these tests by looping around `for hash in
> sha1 sha256`.

Ack, will do in v2.

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-11-05 16:36 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-05 10:45 [PATCH] ls-remote: add --show-object-format Adrian Ratiu
2026-11-05 14:13 ` Patrick Steinhardt
2026-11-05 16:35   ` Adrian Ratiu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox