git diff/show/log route by predicate set membership, where git resolves output shape by suppressor recency plus a precedence lattice

Open
#3,910 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
35/100
Issue type
Refactor
Clarity
Mostly clear
Activity status
Active
Tech stack
rust

Research direction

Start with the run_diff, run_show, and run_log call sites and the differential harness used for #3681; first measure the complete shape precedence lattice against git before changing the implementation. Done means the three call sites share the folded output-shape result and the harness reports zero unexplained divergences across randomized flag combinations.

Written by the indexing model from the issue text.

Description

run_diff, run_show and run_log decide between three output routes — raw passthrough, RTK's compacted diff, and summary-only — by asking a set of per-token predicates "does any token match", at three separate call sites. git does not work that way, and the mismatch has been the single most productive source of regressions in the arg_tokenizer work (#3681): most review rounds there found at least one, and they were all the same shape — a flag combination taking the wrong route.

Filing the measurements rather than the fix, because the fix belongs on develop after #3681 lands (that PR is what introduced most of these predicates, so doing it before would mean rewriting code that does not exist here yet).

Measured semantics

Probed against git 2.53, LC_ALL=C, in a repo with a modification, a staged create, a rename, a delete, a binary file and a merge commit. Every flag singly across diff/show/log, then all 272 ordered pairs per subcommand.

Result 1 — only suppressors are order-sensitive. 40 ordered pairs per subcommand change shape when reversed, and every one of them has -s or --no-patch on one side. Identical across all three subcommands:

-s -p            = PATCH      -p -s            = NONE
-s --stat        = STAT       --stat -s        = NONE
-s --numstat     = NUMSTAT    --numstat -s     = NONE
-s --raw         = RAW        --raw -s         = NONE
--no-patch -U0   = PATCH      -U0 --no-patch   = NONE

Result 2 — --quiet is not one of them. Zero order-sensitive pairs. It loses to any shape request from either side and suppresses only when nothing else asked for output. Modelling it as a third spelling of -s silently dropped patches; fixed in #3681, but it is the clearest example of the class.

Result 3 — "last flag wins" is wrong for shape-vs-shape. I tested that model against 343 random 2–4 flag combinations: it predicts 207. Since two shape flags are never order-sensitive (Result 1), what git applies among them is a fixed precedence, not recency:

--name-only --compact-summary            -> NAMES   (not STAT)
--patch-with-stat --numstat --compact-…  -> PATCH   (not STAT)
--numstat -W --compact-summary           -> NUMSTAT (not STAT)
--check --raw --shortstat                -> NONE    (not STAT)

So the real model is two rules, not one:

  • suppressor vs shape → last one wins
  • shape vs shape → fixed precedence, order-independent

Neither is expressible as "does any token match this predicate", which is why each new flag combination has had to be discovered empirically.

Suggested shape of the fix

Fold the tokens once into an output shape, and let the three call sites read the result instead of each re-deriving it:

enum OutputShape { Patch { unified: Option<u32> }, Stat, NumStat, Raw, Names, None, Unparseable }

fn output_shape(sub: Subcommand, tokens: &[Token]) -> OutputShape

Each flag classifies as Suppressor, Sets(shape) or Inert — data, not control flow — and the precedence lattice among Sets is a table derived from the probe above rather than from reading documentation. Unparseable covers the flags that re-shape the columns the compaction keys on (--line-prefix, --output-indicator-*, word diff) and therefore have to take the raw route; note the probe classifier independently rediscovers this, since --line-prefix=X stops matching as a patch.

Before implementing, the precedence lattice needs measuring properly — the four rows above are a refutation of the simple model, not a complete ordering. That measurement is most of the work and is what makes this worth doing as its own change with its own verification budget.

Verification available

The differential harness used for #3681 compares real git / develop / branch across randomised flag combinations on exit code, first stderr line, and body presence, and it is what caught the --quiet case. Any implementation here should have to pass it with zero divergences that are not improvements — that is a much stronger gate than unit tests over the individual predicates, which reported green for every one of these regressions.

Dominant language
Rust
Stars
81.1k
Forks
5.1k
Avg merge
4d 11h
Merged PRs (30d)
40

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from rtk-ai/rtk

All issues in rtk-ai/rtk

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.