diff mbox series

[v5,3/6] set-head: better output for --auto

Message ID 20241009135747.3563204-3-bence@ferdinandy.com (mailing list archive)
State Superseded
Headers show
Series [v5,1/6] refs_update_symref: atomically record overwritten ref | expand

Commit Message

Bence Ferdinandy Oct. 9, 2024, 1:57 p.m. UTC
Currently, set-head --auto will print a message saying "remote/HEAD set
to branch", which implies something was changed.

Change the output of --auto, so the output actually reflects what was
done: a) set a previously unset HEAD, b) change HEAD because remote
changed or c) no updates.

Signed-off-by: Bence Ferdinandy <bence@ferdinandy.com>
---

Notes:
    v1-v2:
    
        was RFC in https://lore.kernel.org/git/20240910203835.2288291-1-bence@ferdinandy.com/
    
    v3:
    
        This patch was originally sent along when I thought set-head was
        going to be invoked by fetch, but the discussion on the RFC
        concluded that it should be not. This opened the possibility to make
        it more explicit.
    
        Note: although I feel both things the patch does are really just
        cosmetic, an argument could be made for breaking it into two, one
        for the no-op part and one for the --auto print update.
    
        Was sent in:
        https://lore.kernel.org/git/20240915221055.904107-1-bence@ferdinandy.com/
    
    v4:
        - changes are now handled atomically via the ref update transaction
        - outputs have changed along the lines of Junio's suggestion
        - minor refactor to set_head for improved legibility
    
    v5: - the minor refactor has been split out into its own patch

 builtin/remote.c  | 30 ++++++++++++++++++++++++++----
 t/t5505-remote.sh | 13 ++++++++++++-
 2 files changed, 38 insertions(+), 5 deletions(-)

Comments

Junio C Hamano Oct. 9, 2024, 8:53 p.m. UTC | #1
Bence Ferdinandy <bence@ferdinandy.com> writes:

> +static void report_auto(const char *remote, const char *head_name,
> +			struct strbuf *buf_prev) {
> +	struct strbuf buf_prefix = STRBUF_INIT;
> +	const char *prev_head;

I think we need to initialize prev_head to NULL.

> +	strbuf_addf(&buf_prefix, "refs/remotes/%s/", remote);
> +	skip_prefix(buf_prev->buf, buf_prefix.buf, &prev_head);

If the symref was not pointing into the "refs/remotes/<remote>/"
hierarchy previously, skip_prefix() comes back without touching
prev_head (i.e. not starting with the prefix does not clear it).

Assuming that we fix the initialization, the rest of the function
looks more or less correct.

> +	if (prev_head && !strcmp(prev_head, head_name))
> +		printf("'%s/HEAD' is unchanged and points to '%s'\n",
> +			remote, head_name);
> +	else if (prev_head)
> +		printf("'%s/HEAD' has changed from '%s' and now points to '%s'\n",
> +			remote, prev_head, head_name);
> +	else
> +		printf("'%s/HEAD' is now created and points to '%s'\n",
> +			remote, head_name);

The "more or less" part is that the message does not let you tell
between refs/remotes/<name>/HEAD that did not exist, and
refs/remotes/<name>/HEAD that used to point at somewhere unexpected,
outside refs/remotes/<name>/ hierarchy.

For that, we can check if buf_prev->len is 0 (in which case, the
"now created and points at" applies) or non-zero (in which case, we
say something like "used to point at '%s' (which is unusual), but
now points at '%s' as requested").
Junio C Hamano Oct. 10, 2024, 3:57 p.m. UTC | #2
Junio C Hamano <gitster@pobox.com> writes:

> Bence Ferdinandy <bence@ferdinandy.com> writes:
>
>> +static void report_auto(const char *remote, const char *head_name,
>> +			struct strbuf *buf_prev) {
>> +	struct strbuf buf_prefix = STRBUF_INIT;
>> +	const char *prev_head;
>
> I think we need to initialize prev_head to NULL.
>
>> +	strbuf_addf(&buf_prefix, "refs/remotes/%s/", remote);
>> +	skip_prefix(buf_prev->buf, buf_prefix.buf, &prev_head);
>
> If the symref was not pointing into the "refs/remotes/<remote>/"
> hierarchy previously, skip_prefix() comes back without touching
> prev_head (i.e. not starting with the prefix does not clear it).

The two uninitialized prev_head were also noticed by compilers in
multiple jobs at GitHub CI.

https://github.com/git/git/actions/runs/11265515664
Ramsay Jones Oct. 10, 2024, 4:54 p.m. UTC | #3
On 10/10/2024 16:57, Junio C Hamano wrote:
> Junio C Hamano <gitster@pobox.com> writes:
> 
>> Bence Ferdinandy <bence@ferdinandy.com> writes:
>>
>>> +static void report_auto(const char *remote, const char *head_name,
>>> +			struct strbuf *buf_prev) {
>>> +	struct strbuf buf_prefix = STRBUF_INIT;
>>> +	const char *prev_head;
>>
>> I think we need to initialize prev_head to NULL.
>>
>>> +	strbuf_addf(&buf_prefix, "refs/remotes/%s/", remote);
>>> +	skip_prefix(buf_prev->buf, buf_prefix.buf, &prev_head);
>>
>> If the symref was not pointing into the "refs/remotes/<remote>/"
>> hierarchy previously, skip_prefix() comes back without touching
>> prev_head (i.e. not starting with the prefix does not clear it).
> 
> The two uninitialized prev_head were also noticed by compilers in
> multiple jobs at GitHub CI.
> 
> https://github.com/git/git/actions/runs/11265515664

Heh, I was just about to send a patch to fix up these two warnings
(errors with -Werror), so that I could build 'seen'. However, it
seems to be well in hand, so I won't bother! :)

ATB,
Ramsay Jones
Bence Ferdinandy Oct. 10, 2024, 7:08 p.m. UTC | #4
On Thu Oct 10, 2024 at 17:57, Junio C Hamano <gitster@pobox.com> wrote:
> Junio C Hamano <gitster@pobox.com> writes:
>
>> Bence Ferdinandy <bence@ferdinandy.com> writes:
>>
>>> +static void report_auto(const char *remote, const char *head_name,
>>> +			struct strbuf *buf_prev) {
>>> +	struct strbuf buf_prefix = STRBUF_INIT;
>>> +	const char *prev_head;
>>
>> I think we need to initialize prev_head to NULL.
>>
>>> +	strbuf_addf(&buf_prefix, "refs/remotes/%s/", remote);
>>> +	skip_prefix(buf_prev->buf, buf_prefix.buf, &prev_head);
>>
>> If the symref was not pointing into the "refs/remotes/<remote>/"
>> hierarchy previously, skip_prefix() comes back without touching
>> prev_head (i.e. not starting with the prefix does not clear it).
>
> The two uninitialized prev_head were also noticed by compilers in
> multiple jobs at GitHub CI.
>
> https://github.com/git/git/actions/runs/11265515664

I wonder why that didn't fail the build for me locally. I was running

make -j8 install PREFIX=$HOME/.local

for testing. Should I have added a flag or ENV variable somewhere?

Anyhow, the v6 I sent earlier today should fix this (and hopefully all your
other comments as well).

Thanks,
Bence
diff mbox series

Patch

diff --git a/builtin/remote.c b/builtin/remote.c
index 353ffd2c43..2480128b88 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -1399,10 +1399,30 @@  static int show(int argc, const char **argv, const char *prefix)
 	return result;
 }
 
+static void report_auto(const char *remote, const char *head_name,
+			struct strbuf *buf_prev) {
+	struct strbuf buf_prefix = STRBUF_INIT;
+	const char *prev_head;
+
+	strbuf_addf(&buf_prefix, "refs/remotes/%s/", remote);
+	skip_prefix(buf_prev->buf, buf_prefix.buf, &prev_head);
+
+	if (prev_head && !strcmp(prev_head, head_name))
+		printf("'%s/HEAD' is unchanged and points to '%s'\n",
+			remote, head_name);
+	else if (prev_head)
+		printf("'%s/HEAD' has changed from '%s' and now points to '%s'\n",
+			remote, prev_head, head_name);
+	else
+		printf("'%s/HEAD' is now created and points to '%s'\n",
+			remote, head_name);
+}
+
 static int set_head(int argc, const char **argv, const char *prefix)
 {
 	int i, opt_a = 0, opt_d = 0, result = 0;
-	struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;
+	struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT,
+		buf_prev = STRBUF_INIT;
 	char *head_name = NULL;
 	struct ref_store *refs = get_main_ref_store(the_repository);
 
@@ -1445,15 +1465,17 @@  static int set_head(int argc, const char **argv, const char *prefix)
 		/* make sure it's valid */
 		if (!refs_ref_exists(refs, buf2.buf))
 			result |= error(_("Not a valid ref: %s"), buf2.buf);
-		else if (refs_update_symref(refs, buf.buf, buf2.buf, "remote set-head", NULL))
+		else if (refs_update_symref(refs, buf.buf, buf2.buf, "remote set-head", &buf_prev))
 			result |= error(_("Could not setup %s"), buf.buf);
-		else if (opt_a)
-			printf("%s/HEAD set to %s\n", argv[0], head_name);
+		else if (opt_a) {
+			report_auto(argv[0], head_name, &buf_prev);
+		}
 		free(head_name);
 	}
 
 	strbuf_release(&buf);
 	strbuf_release(&buf2);
+	strbuf_release(&buf_prev);
 	return result;
 }
 
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index 532035933f..262a4de0aa 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -434,7 +434,7 @@  test_expect_success 'set-head --auto has no problem w/multiple HEADs' '
 		cd test &&
 		git fetch two "refs/heads/*:refs/remotes/two/*" &&
 		git remote set-head --auto two >output 2>&1 &&
-		echo "two/HEAD set to main" >expect &&
+		echo "'\''two/HEAD'\'' is unchanged and points to '\''main'\''" >expect &&
 		test_cmp expect output
 	)
 '
@@ -453,6 +453,17 @@  test_expect_success 'set-head explicit' '
 	)
 '
 
+
+test_expect_success 'set-head --auto reports change' '
+	(
+		cd test &&
+		git remote set-head origin side2 &&
+		git remote set-head --auto origin >output 2>&1 &&
+		echo "'\''origin/HEAD'\'' has changed from '\''side2'\'' and now points to '\''main'\''" >expect &&
+		test_cmp expect output
+	)
+'
+
 cat >test/expect <<EOF
 Pruning origin
 URL: $(pwd)/one