girt 0.11.3

An incremental, idiomatic Rust implementation of Git
Documentation
#!/usr/bin/env python3
"""Render the committed jj matrix without running benchmarks.

Run after measuring on a quiet machine:
    python3 scripts/jj-e2e/render-performance.py
CI uses --check to reject stale generated Markdown.
"""
import argparse
import json
import re
import unicodedata
from pathlib import Path

ROOT = Path(__file__).resolve().parents[2]
SIZES = ('small', 'medium', 'large')
ISSUES = 'https://github.com/joshka/girt/issues/'


def issue_links(numbers):
    return ' '.join(f'[#{n}]({ISSUES}{n})' for n in numbers)


def cell(row):
    if row is None:
        return '⏳ pending ' + issue_links([66])
    links = issue_links(row.get('issues', []))
    status = row.get('status', 'ok')
    ratio = row.get('ratio_vs_stock')
    if status == 'pending':
        return '⏳ pending ' + (links or issue_links([66]))
    if status != 'ok' or ratio is None:
        return f'❌ {status if status != "ok" else "unavailable"} {links}'.strip()
    band = '🔴' if ratio > 2 else '🟠' if ratio > 1.2 else '🟢'
    n = row.get('girt_samples', '?')
    shown = f'{ratio:.3f}' if abs(ratio - 1.2) < 0.005 or abs(ratio - 2) < 0.005 else f'{ratio:.2f}'
    return f'{band} {shown}× (n={n}) {links}'.strip()


def format_markdown(text):
    """Match the repository's 100-column prose and aligned-table conventions."""
    def width(value):
        return sum(2 if unicodedata.east_asian_width(c) in ('W', 'F') else 1
                   for c in value)

    def wrap_prose(value):
        words = re.findall(r'(?:`[^`]+`|\[[^\]]+\]\([^)]*\)|[^\s`\[])+', value)
        wrapped, line = [], ''
        for word in words:
            candidate = (line + ' ' + word).strip()
            if line and width(candidate) > 100:
                wrapped.append(line)
                line = word
            else:
                line = candidate
        return '\n'.join([*wrapped, line])

    blocks = text.rstrip().split('\n\n')
    result = []
    for block in blocks:
        lines = block.splitlines()
        if lines[0].startswith('|'):
            rows = [[c.strip() for c in line.strip('|').split('|')] for line in lines]
            widths = [max(width(row[i]) for row in rows) for i in range(len(rows[0]))]
            table = []
            for n, row in enumerate(rows):
                if n == 1:
                    cells = ['-' * w for w in widths]
                else:
                    cells = [c + ' ' * (w - width(c)) for c, w in zip(row, widths)]
                table.append('| ' + ' | '.join(cells) + ' |')
            result.append('\n'.join(table))
        elif lines[0].startswith(('```', '#', '<!--', '- ')):
            result.append(block)
        else:
            result.append(wrap_prose(' '.join(lines)))
    return '\n\n'.join(result) + '\n'


def render(rows, meta):
    by_key = {}
    operations = {tuple(x) for x in meta['operations']}
    for row in rows:
        key = (row['op'], row['variant'], row['size'])
        if key in by_key:
            raise ValueError(f'duplicate matrix cell: {key}')
        if row['size'] not in SIZES:
            raise ValueError(f'unknown size: {row["size"]}')
        by_key[key] = row
        operations.add(key[:2])
        if (row.get('status', 'ok') != 'ok' and row.get('ratio_vs_stock') is not None):
            raise ValueError(f'failure has a successful ratio: {key}')
        if ((row.get('ratio_vs_stock') or 0) > 1.2
                or row.get('status', 'ok') != 'ok') and not row.get('issues'):
            raise ValueError(f'gap or failure needs an issue link: {key}')

    def severity(op):
        cells = [by_key.get((*op, size)) for size in SIZES]
        failed = any(r is None or r.get('status', 'ok') != 'ok' for r in cells)
        worst = max((r.get('ratio_vs_stock') or 0 for r in cells if r), default=0)
        return (-int(failed), -worst, op)

    lines = [
        '# jj performance', '',
        '<!-- Generated by scripts/jj-e2e/render-performance.py; edit the JSON inputs. -->', '',
        'Compare jj on girt with stock jj at the same base revision. Lower ratios are better.',
        'Failures and missing results sort first, followed by the worst measured ratio across sizes.',
        'These warm-cache measurements describe the recorded build epochs below.',
        'Cells can use different girt revisions; consult each row’s binary fingerprints and epoch.', '',
        '🟢 ≤1.20×; 🟠 >1.20–2.00×; 🔴 >2.00×; ❌ failure/refusal; ⏳ pending.',
        '`n` is the number of successful girt samples; JSON records each tool separately.', '',
        '| Operation / workload | Small | Medium | Large |',
        '| --- | --- | --- | --- |',
    ]
    for op, variant in sorted(operations, key=severity):
        label = f'`{op}` / {variant}'
        values = [cell(by_key.get((op, variant, size))) for size in SIZES]
        lines.append('| ' + ' | '.join([label, *values]) + ' |')
    lines += [
        '', '## Limits and interpretation', '',
        'Historical rename-detection refusals are tracked in ' + issue_links([227]) + '.',
        'Historical fetch/clone source pack-index refusals are tracked in ' + issue_links([228]) + '.',
        'The historical new-bookmark push destination refusal is tracked in ' + issue_links([234]) + '.',
        'Other originally refused push variants failed in prerequisite fetch before timing.',
        'The table reports the latest measured result for each cell; superseded refusals remain',
        'in the retained raw evidence.',
        'The related empty/binary-file rename-candidate counting policy is tracked in',
        issue_links([229]) + '; it is context, not a separately measured slowdown.', '',
        'A refusal during prerequisite setup has no timed girt sample. A timeout has a recorded',
        'limit and no successful girt ratio. Neither is presented as a fast result.',
        'Stock jj taking more than twice Git is flagged separately in the JSON as `jj_level_gap`.',
        'Git equivalents sometimes perform different work; read each row’s workload notes.',
        'Git has no timed counterpart for colocated initialization (the input is already a Git',
        'repository), import (Git already sees its own refs), or jj undo/operation restore',
        '(Git has no equivalent repository-wide operation restoration). Reflogs approximate jj',
        'evolution and operation history but contain different records.',
        'Ranges, peak RSS, sample counts, issue mappings, and limitations are in',
        '[the results JSON](benchmarks/jj-matrix.json). Machine load was not controlled; treat results',
        'as provisional where retained sample load or ranges show contention.',
        'Clean GC measures backend policy as well as speed: the backends can choose different',
        'packing work when nothing needs collection. Its ratio is not equal-work repack throughput.', '',
        '## Provenance', '',
        f'- Measurement dates: {meta["dates"]}.',
        f'- Machine: {meta["machine"]}.',
        f'- girt: `{meta["girt_commit"]}`.',
        f'- jj adapter: `{meta["jj_adapter_commit"]}`.',
        f'- Stock jj base: `{meta["jj_stock_base"]}`.',
        f'- Git: {meta["git_version"]}; release commit `{meta["git_commit"]}`.',
        f'- Build: {meta["build"]}.', '',
        'The Git commit identifies release source; distribution packaging may add patches.',
        'Binary fingerprints and per-repository object, ref, tag, worktree, and pack facts are in',
        '[the JSON provenance block](benchmarks/jj-matrix.json). Original small-run fingerprints',
        'are retained separately from later binaries rebuilt from the identical source revisions.',
        'The original small-run Git binary fingerprint was not captured. Empty per-row Git fingerprint',
        'lists mean no sample-local capture; later Git identity is recorded at provenance level.',
        'Small new, rebase/10, and status/many-edits profiles used the rebuilt frozen-source binary,',
        'not the original timing binary. Older profile entries can lack a sample-local fingerprint;',
        'interpret them using their retained build epoch. Profiles and raw checkpoints are retained',
        'locally at the evidence paths; they are not bundled in this repository.',
        'The [original 53 small rows](jj-performance-original-small.json) retain the historical',
        'timings; five cells are intentionally superseded by the fetch and limited-log corrections.',
        'The corrected small cells use a same-HEAD repacked fixture; per-epoch layouts are in',
        'the provenance JSON.', '',
        '| Fixture | Packed / loose objects | Refs | Tags | Worktree files | Pack bytes | Loose / packed refs |',
        '| --- | --- | --- | --- | --- | --- | --- |',
    ]
    current = meta.get('current_main_rebuild')
    if current:
        position = lines.index('| Fixture | Packed / loose objects | Refs | Tags | Worktree files | Pack bytes | Loose / packed refs |')
        lines[position:position] = [
            'Formerly refused cells were remeasured against girt',
            f'`{current["girt_commit"]}` using the same jj base plus mechanical API compatibility',
            'changes. The compatibility patch fingerprint and rebuilt binary fingerprint are in',
            'the provenance JSON. Other cells retain their earlier builds; this is a mixed-revision',
            'matrix, not a complete rerun of current girt main.', '',
        ]
    for size in SIZES:
        f = meta['fixtures'][size]
        counts = dict(line.split(': ',1) for line in f.get('object_counts','').splitlines() if ': ' in line)
        objects = f'{counts.get("in-pack",f["objects"])} / {counts.get("count","unknown")}'
        values = [size, objects, str(f['refs']), str(f['tags']),
                  str(f['worktree_files']), str(f['pack_bytes']),
                  f'{f["loose_refs"]} / {f["packed_refs"] or 0}']
        lines.append('| ' + ' | '.join(values) + ' |')
    lines += [
        '', '## Method', '',
        'Build both binaries before taking the shared `/tmp/girt-timing.lock`. Prepare inputs',
        'outside the lock. Use fresh APFS `cp -cR` copies for mutating samples, one large copy at',
        'a time; checkpoint timing and validation before deleting it. Reuse clean read-only',
        'baselines and verify unchanged jj operation heads. Later mutation inputs were rebuilt',
        'after history rendering accumulated internal Git objects; subsequent read cells use',
        'separate baselines to preserve pristine mutation inputs. Automatic Git maintenance is disabled',
        'in the later run epoch; explicit GC remains measured.', '',
        'Linux has tracked paths differing only by case. On case-insensitive APFS, Git rebase',
        'setup verifies that every unstaged path is such an alias and marks only those paths',
        'assume-unchanged before timing. Other unstaged edits fail setup. This platform limitation',
        'means the checkout cannot represent both versions of those files faithfully.', '',
        'The original method takes six samples per tool in Git, girt, stock, stock, girt, Git',
        'blocks. The remaining Linux cells use adaptive calibration in Git, stock, girt order:',
        'if the slowest first sample exceeds 60 seconds, take one sample per tool; if it exceeds',
        '10 seconds, take three; otherwise take six. Further samples alternate girt and stock',
        'positions. Preserve completed older cells and partial samples on resume. Slow-cell ratios',
        'therefore have lower precision. Counts in the JSON describe the samples actually retained.',
        '',
        'Kill each timed girt process group at `max(2 × first stock-jj seconds, 5 seconds)`.',
        'Stock jj and Git each have a 600-second cap. Timeouts fail the cell and record the limit.',
        'Report medians, full ranges, and peak RSS using `/usr/bin/time -l`. Release the lock',
        'between timed samples in bounded-disk mode. Caches are warm and are not flushed.', '',
        'Full `git fsck` succeeded for every small mutation variant on the frozen build. Clone and',
        'explicit GC were fully checked at each size, and the current-build Linux clone was checked',
        'again. The operation-type policy reuses earlier checks across build epochs: current-build',
        'fetch, push, and untracked-status reruns did not receive new full fsck checks. This limits',
        'validation of those reruns. Semantic checks cover imported/exported',
        'refs and actual garbage removal. Profiles diagnose gaps without replacing timings;',
        'adaptive diagnostic recordings are capped at five seconds.', '',
        'Fetch cells perform an untimed fetch before introducing the measured commit/tag delta.',
        'This imports existing tags and makes noop a genuine second fetch with no remote changes.',
        'The final stale-push rerun prepares remote-tracking refs with untimed Git fetch plus',
        'jj import, avoiding a repeated native-fetch workload before the separately timed push.',
        'Earlier completed push cells retain their original jj-fetch preparation.',
        'Limited log uses `::@ --limit 100` against Git `-n 100`; the superseded',
        '`ancestors(@, 100)` selected up to 100 parent steps, not 100 output revisions.',
        'Affected small/medium cells were remeasured; the corrected Linux limited log was also rerun.',
        'Linux girt fetch warm-up was refused by the original build’s pack-index limit; later',
        'current-main reruns use the fixes recorded in their build epoch. Superseded samples and fingerprints',
        'remain archived separately from the corrected results.', '',
        '## Refresh before a release', '',
        'On a quiet macOS machine, build the intended frozen jj/girt revisions and prepare all',
        'fixtures under `~/tmp/girt-bench` or `~/tmp/girt-fixtures`. Follow',
        '[the matrix runner instructions](../scripts/jj-e2e/README.md), recording exact commits,',
        'binary hashes, fixture facts, failures, and issue links. Keep the raw checkpoint and logs.',
        'Publication is a manual normalization step: the runner emits a timing list and raw checkpoint,',
        'not this publication schema. Merge selected reruns by repository/operation, retain replaced',
        'records, classify statuses, attach reviewed issue/profile mappings, and audit sample counts,',
        'ranges, fingerprints, and validation epochs before updating the publication envelope.',
        'Update the results and provenance in `docs/benchmarks/jj-matrix.json` from that run,',
        'then regenerate and check the page:', '',
        '```sh',
        'python3 scripts/jj-e2e/render-performance.py',
        'python3 scripts/jj-e2e/render-performance.py --check',
        '```', '',
        'CI runs only the freshness check. It never runs benchmarks.', '',
    ]
    return format_markdown('\n'.join(lines))


def main():
    ap = argparse.ArgumentParser(description=__doc__)
    ap.add_argument('--matrix', type=Path, default=ROOT/'docs/benchmarks/jj-matrix.json')
    ap.add_argument('--provenance', type=Path,
                    help='separate provenance for legacy list-only inputs')
    ap.add_argument('--output', type=Path, default=ROOT/'docs/jj-performance.md')
    ap.add_argument('--check', action='store_true')
    args = ap.parse_args()
    matrix = json.loads(args.matrix.read_text())
    if isinstance(matrix, dict):
        if matrix.get('schema_version') != 1:
            ap.error('unsupported matrix schema_version')
        rows, meta = matrix['results'], matrix['provenance']
        if args.check and args.matrix == ROOT/'docs/benchmarks/jj-matrix.json':
            mirrors = ((ROOT/'docs/jj-performance.json', rows),
                       (ROOT/'docs/jj-performance-provenance.json', meta))
            for path, expected in mirrors:
                if not path.exists() or json.loads(path.read_text()) != expected:
                    ap.exit(1, f'{path.name} differs from the matrix envelope\n')
    else:
        if args.provenance is None:
            ap.error('legacy list inputs require --provenance')
        rows, meta = matrix, json.loads(args.provenance.read_text())
    result = render(rows, meta)
    if args.check:
        if not args.output.exists() or args.output.read_text() != result:
            ap.exit(1, 'jj performance page is stale; run scripts/jj-e2e/render-performance.py\n')
    else:
        args.output.write_text(result)


if __name__ == '__main__':
    main()