Skip to content

The for-each-ref ordering was pinned but its RELATION was never modelled - #9509

Merged
briansrls merged 3 commits into
mainfrom
session/calm-ram-380-ref-order
Aug 28, 2026
Merged

briansrls merged 3 commits into
mainfrom
session/calm-ram-380-ref-order

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Raised by the operator on #9506, and the answer turned out to be more interesting than either "it is documented" or "it is not".

What was already right

git.Core.ForEachRefIn pins --sort=refname in its transport argv — the order is not inherited from a default, it is requested. There is a cited ExternalAuthority at git-scm.com/docs/git-for-each-ref. And extdeps.git.enumeration deliberately validates the order rather than imposing it, so the flag stays falsifiable: a decoder that sorted its own rows would make the pin decorative, and deleting --sort=refname would leave every witness green.

None of that changes here.

What was missing

Pinning the flag fixes the ORDER and says nothing about the RELATION — and a consumer has to reimplement that relation in order to check it. Ours does:

  • git orders refnames bytewise. Refnames are byte strings; git-check-ref-format(1) constrains which bytes may appear and says nothing about an encoding.
  • string_is_lexicographically_before -> string_lex_compare compares code points (code_point(char_at(s, 0)), recursing).

Those are two different relations from two different authorities, equated by adjacency. They do agree — because UTF-8 is order-preserving: a code point's encoding sorts, byte for byte, in the same relation as the code point itself. That is a property of the encoding, not of either comparator, and it was written down nowhere.

Why that is worth a change rather than a shrug

The check is correct today and reads as coincidence, which fails in a specific and nasty direction: make string_lex_compare locale-aware — an entirely reasonable-looking improvement — and the decoder starts refusing valid for-each-ref output with "ref names are not strictly increasing", a message that accuses git rather than the change that broke it. The reader who investigates begins at the wrong end.

So the relation is now stated at both ends: the upstream half beside the operation that pins the flag, and the mismatch named at the check that relies on it.

Two gaps declared rather than papered over

Non-UTF-8 refnames. git permits them; a code-point comparator has no defined relation to them. No refusal is claimed, because whether such a name survives the shell transport into a String at all is unestablished, and a wall whose reachability has not been measured is asserted coverage rather than a wall (§4b). The next rung here is a measurement, not a check — and saying so is the point, since the tempting move is to add a guard that might be permanently green.

No git version is pinned. ExternalAuthority carries a Uri and nothing else — there is no version field to pin one with. §3 asks an extdeps subject to declare its version, so this is a standing gap for the whole module, named here because sort semantics are exactly the kind of fact that is version-relative. Not fixed here: adding a version axis to ExternalAuthority touches every citation in the corpus and belongs in its own change.

Scope

Annotations only — no type, function, argv or emitted byte changes. Deliberately not folded into #9506, which consumes this decoder: a correction to a merged module should not arrive inside a feature that depends on it.

— sent from calm-ram-380

Side-chat review accepted this PR's factual content and said mechanisation
was owed. It was: the two commits before this one were prose, and the claim
they make -- that our code-point comparator agrees with git's bytewise order
-- had no executing evidence.

The existing ordering witnesses establish that the check refuses a
DECREASING pair. They say nothing about WHICH ORDER is being enforced,
because their fixtures are pure ASCII and every plausible comparator agrees
there.

THE DISCRIMINATING PAIR IS ASCII-vs-MULTIBYTE, chosen to separate our
comparator from the one substitution the annotation warns about:

  "refs/heads/z" vs "refs/heads/e-acute" (U+00E9, bytes C3 A9)
    bytes        0x7A < 0xC3  -> z first
    code points  0x7A < 0xE9  -> z first
    locale       e-acute sorts near "e", BEFORE z

A locale-aware comparator therefore disagrees with git on exactly this pair,
and it is the change that would look like an improvement. Both rows fail if
anyone makes it: ascending would refuse, descending would be accepted.

13 witnesses PASS.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@briansrls
briansrls merged commit 651b724 into main Aug 28, 2026
0 of 3 checks passed
@briansrls
briansrls deleted the session/calm-ram-380-ref-order branch August 28, 2026 00:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant