git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [RFC PATCH] ref-filter: sort detached HEAD lines firstlyxy

From
Johannes Schindelin <johannes.schindelin@gmx.de>
Date
Jun 9, 2019, 08:17 UTC
Message-ID
<nycvar.QRO.7.76.6.1906090954510.789@QRFXGBC-DHN364S.ybpnyqbznva>
In-Reply-To
<faaa9a3d6ba66d77cc2a8eab438d1bfc8f762fa1.1559857032.git.matvore@google.com>
Hi Matthew,
On Thu, 6 Jun 2019, Matthew DeVore wrote:
Show 9 quoted lines
> Before this patch, "git branch" would put "(HEAD detached...)" and "(no
> branch, rebasing...)" lines before all the other branches *in most
> cases* and only because of the fact that "(" is a low codepoint. This
> would not hold in the Chinese locale, which uses a full-width "(" symbol
> (codepoint FF08). This meant that the detached HEAD line would appear
> after all local refs and even after the remote refs if there were any.
>
> Deliberately sort the detached HEAD refs before other refs when sorting
> by refname rather than rely on codepoint subtleties.
This description is pretty convincing!
Show 20 quoted lines
> diff --git a/ref-filter.c b/ref-filter.c
> index 8500671bc6..cbfae790f9 100644
> --- a/ref-filter.c
> +++ b/ref-filter.c
> @@ -2157,25 +2157,29 @@ static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, stru
>  	cmp_type cmp_type = used_atom[s->atom].type;
>  	int (*cmp_fn)(const char *, const char *);
>  	struct strbuf err = STRBUF_INIT;
>
>  	if (get_ref_atom_value(a, s->atom, &va, &err))
>  		die("%s", err.buf);
>  	if (get_ref_atom_value(b, s->atom, &vb, &err))
>  		die("%s", err.buf);
>  	strbuf_release(&err);
>  	cmp_fn = s->ignore_case ? strcasecmp : strcmp;
> -	if (s->version)
> +	if (s->version) {
>  		cmp = versioncmp(va->s, vb->s);
> -	else if (cmp_type == FIELD_STR)
> +	} else if (cmp_type == FIELD_STR) {

I find that it makes sense in general to suppress one's urges regarding introducing `{ ... }` around one-liners when the patch does not actually require it.

For example, I found this patch harder than necessary to read because of it.

> +		if ((a->kind & FILTER_REFS_DETACHED_HEAD) !=
> +				(b->kind & FILTER_REFS_DETACHED_HEAD)) {
So in case that both are detached...
> +			return (a->kind & FILTER_REFS_DETACHED_HEAD) ? -1 : 1;
> +		}
>  		cmp = cmp_fn(va->s, vb->s);

... we compare their commit hashes, is that right? Might be worth a code comment.

> -	else {
> +	} else {
FWIW it would have been a much more obvious patch if it had done
 	if (s->version)
		[...]
+	else if (cmp_type == FIELD_STR &&
+		 (a->kind & FILTER_REFS_DETACHED_HEAD ||
+		  b->kind & FILTER_REFS_DETACHED_HEAD))
+		return (a->kind & FILTER_REFS_DETACHED_HEAD) ? -1 : 1;
 	else if (cmp_type == FIELD_STR)
		[...]
Maybe still worth doing.
FWIW I was *so* tempted to write
	((a->kind ^ b->kind) & FILTER_REFS_DETACHED_HEAD)

to make this code DRYer, but then, readers not intimately familiar with Boolean arithmetic might not even know about the `^` operator, making the code harder to read than necessary, too.

Show 52 quoted lines
> diff --git a/t/lib-gettext.sh b/t/lib-gettext.sh
> index 2139b427ca..de08d109dc 100644
> --- a/t/lib-gettext.sh
> +++ b/t/lib-gettext.sh
> @@ -25,23 +25,29 @@ then
>  		p
>  		q
>  	}')
>  	# is_IS.ISO8859-1 on Solaris and FreeBSD, is_IS.iso88591 on Debian
>  	is_IS_iso_locale=$(locale -a 2>/dev/null |
>  		sed -n '/^is_IS\.[iI][sS][oO]8859-*1$/{
>  		p
>  		q
>  	}')
>
> -	# Export them as an environment variable so the t0202/test.pl Perl
> -	# test can use it too
> -	export is_IS_locale is_IS_iso_locale
> +	zh_CN_locale=$(locale -a 2>/dev/null |
> +		sed -n '/^zh_CN\.[uU][tT][fF]-*8$/{
> +		p
> +		q
> +	}')
> +
> +	# Export them as environment variables so other tests can use them
> +	# too
> +	export is_IS_locale is_IS_iso_locale zh_CN_locale
>
>  	if test -n "$is_IS_locale" &&
>  		test $GIT_INTERNAL_GETTEXT_SH_SCHEME != "fallthrough"
>  	then
>  		# Some of the tests need the reference Icelandic locale
>  		test_set_prereq GETTEXT_LOCALE
>
>  		# Exporting for t0202/test.pl
>  		GETTEXT_LOCALE=1
>  		export GETTEXT_LOCALE
> @@ -53,11 +59,15 @@ then
>  	if test -n "$is_IS_iso_locale" &&
>  		test $GIT_INTERNAL_GETTEXT_SH_SCHEME != "fallthrough"
>  	then
>  		# Some of the tests need the reference Icelandic locale
>  		test_set_prereq GETTEXT_ISO_LOCALE
>
>  		say "# lib-gettext: Found '$is_IS_iso_locale' as an is_IS ISO-8859-1 locale"
>  	else
>  		say "# lib-gettext: No is_IS ISO-8859-1 locale available"
>  	fi
> +
> +	if test -z "$zh_CN_locale"; then
> +		say "# lib-gettext: No zh_CN UTF-8 locale available"
> +	fi

I wonder why this hunk, unlike the previous one, does not imitate the is_IS handling closely.

Show 14 quoted lines
> diff --git a/t/t3207-branch-intl.sh b/t/t3207-branch-intl.sh
> new file mode 100755
> index 0000000000..9f6fcc7481
> --- /dev/null
> +++ b/t/t3207-branch-intl.sh
> @@ -0,0 +1,38 @@
> +#!/bin/sh
> +
> +test_description='git branch internationalization tests'
> +
> +. ./lib-gettext.sh
> +
> +test_expect_success 'init repo' '
> +	git init r1 &&
Why?
> +	touch r1/foo &&
> +	git -C r1 add foo &&
> +	git -C r1 commit -m foo
> +'
Why not simply `test_commit foo`?
Show 8 quoted lines
> +test_expect_success 'detached head sorts before other branches' '
> +	# Ref sorting logic should put detached heads before the other
> +	# branches, but this is not automatic when a branch name sorts
> +	# lexically before "(" or the full-width "(" (Unicode codepoint FF08).
> +	# The latter case is nearly guaranteed for the Chinese locale.
> +
> +	git -C r1 checkout HEAD^{} -- &&
> +	git -C r1 branch !should_be_after_detached HEAD &&

I am not sure that `!` is a wise choice, as it might not be a legal file name character everywhere. A `.` or `-` might make more sense.

> +	LC_ALL=$zh_CN_locale LC_MESSAGES=$zh_CN_locale \
> +		git -C r1 branch >actual &&
> +	git -C r1 checkout - &&

Why call `checkout` after `branch`? That's unnecessary, we do not verify anything after that call.

Show 10 quoted lines
> +	awk "
> +	# We need full-width or half-width parens on the first line.
> +	NR == 1 && (/[(].*[)]/ || /\xef\xbc\x88.*\xef\xbc\x89/) {
> +		found_head = 1;
> +	}
> +	/!should_be_after_detached/ {
> +		found_control_branch = 1;
> +	}
> +	END { exit !found_head || !found_control_branch }
> +	" actual

This might look beautiful for a fan of `awk`. For the vast majority of us, this is not a good idea.

Remember, you do *not* write those tests for your own pleasure, you do *not* write those tests in order to help you catch problems while you develop your patches, you do *not* develop these tests in order to just catch future breakages.

You *do* write those tests for *other* developers who you try to help in preventing introducing regressions.

As such, you *want* the tests to be
- easy to understand for as wide a range of developers as you can make,
- quick,
- covering regressions, and *only* regressions,
- helping diagnose *and* fix regressions.

In the ideal case you won't even hear when developers found your test helpful, and you will never, ever learn about regressions that have been prevented.

You most frequently will hear about your tests when they did not do their job well.

In this instance, I would have expected something like
	test_expect_lines = 3 actual &&
	head -n 1 <actual >first &&
	test_i18ngrep "detached HEAD" first &&
	tail -n 1 <actual >last &&
	grep should_be_after last
instead of the "awk-ward" code above.

Ciao, Johannes

Show 7 quoted lines
> +'
> +
> +test_done
> --
> 2.21.0
>
>
Previous: Matthew DeVoreNext: Johannes Schindelin
Message 2 of 36 in “ref-filter: sort detached HEAD lines firstly”
  1. ref-filter: sort detached HEAD lines firstlyMatthew DeVore, Jun 6, 2019
  2. Johannes SchindelinJun 9, 2019
  3. Johannes SchindelinJun 9, 2019
  4. Matthew DeVoreJun 10, 2019
  5. Jonathan NiederJun 11, 2019
  6. Matthew DeVoreJun 11, 2019
  7. Junio C HamanoJun 11, 2019
  8. 0/1 Sort detached HEAD lines firstlyMatthew DeVore, Jun 11, 2019
  9. 1/1 ref-filter: sort detached HEAD lines firstlyMatthew DeVore, Jun 11, 2019
  10. Junio C HamanoJun 11, 2019
  11. Junio C HamanoJun 12, 2019
  12. Matthew DeVoreJun 13, 2019
  13. Matthew DeVoreJun 12, 2019
  14. Johannes SchindelinJun 12, 2019
  15. Jeff KingJun 13, 2019
  16. 0/1 Sort detached heads line firstlyMatthew DeVore, Jun 18, 2019
  17. 1/1 ref-filter: sort detached HEAD lines firstlyMatthew DeVore, Jun 18, 2019
  18. Junio C HamanoJun 19, 2019
  19. 0/5 branch: --sort improvementsÆvar Arnfjörð Bjarmason, Jan 6, 2021
  20. 1/7 branch: change "--local" to "--list" in commentÆvar Arnfjörð Bjarmason, Jan 7, 2021
  21. 0/7 branch: --sort improvementsÆvar Arnfjörð Bjarmason, Jan 7, 2021
  22. 4/7 ref-filter: move "cmp_fn" assignment into "else if" armÆvar Arnfjörð Bjarmason, Jan 7, 2021
  23. 2/7 branch tests: add to --sort testsÆvar Arnfjörð Bjarmason, Jan 7, 2021
  24. 3/7 ref-filter: add braces to if/else if/else chainÆvar Arnfjörð Bjarmason, Jan 7, 2021
  25. 6/7 branch: sort detached HEAD based on a flagÆvar Arnfjörð Bjarmason, Jan 7, 2021
  26. 7/7 branch: show "HEAD detached" first under reverse sortÆvar Arnfjörð Bjarmason, Jan 7, 2021
  27. 5/7 ref-filter: move ref_sorting flags to a bitfieldÆvar Arnfjörð Bjarmason, Jan 7, 2021
  28. Junio C HamanoJan 7, 2021
  29. 1/5 branch: change "--local" to "--list" in commentÆvar Arnfjörð Bjarmason, Jan 6, 2021
  30. 2/5 branch tests: add to --sort testsÆvar Arnfjörð Bjarmason, Jan 6, 2021
  31. Junio C HamanoJan 6, 2021
  32. 4/5 branch: use the "detached_head_first" sorting optionÆvar Arnfjörð Bjarmason, Jan 6, 2021
  33. 3/5 ref-filter: add a "detached_head_first" sorting optionÆvar Arnfjörð Bjarmason, Jan 6, 2021
  34. Junio C HamanoJan 6, 2021
  35. 5/5 branch: show "HEAD detached" first under reverse sortÆvar Arnfjörð Bjarmason, Jan 6, 2021
  36. Junio C HamanoJan 6, 2021

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.