| 1 | diff --git a/scripts/git-performance.mjs b/scripts/git-performance.mjs |
| 2 | index e31d29a..5202582 100644 |
| 3 | --- a/scripts/git-performance.mjs |
| 4 | +++ b/scripts/git-performance.mjs |
| 5 | @@ -248,8 +248,7 @@ async function benchmarkFixture(fixture, index) { |
| 6 | try { |
| 7 | const fixtures = (await readdir(reposDir,{withFileTypes:true})).filter(entry=>entry.isDirectory() && (!only.length || only.includes(entry.name.replace(/\.git$/,"")))) |
| 8 | .map(entry=>join(resolve(reposDir),entry.name)); |
| 9 | - const order=["nzip","bindws","diagramzip","strudel","doorbearer","atlas"]; |
| 10 | - fixtures.sort((a,b)=>order.indexOf(basename(a).replace(/\.git$/,""))-order.indexOf(basename(b).replace(/\.git$/,""))); |
| 11 | + fixtures.sort((a,b)=>basename(a).localeCompare(basename(b))); |
| 12 | for (let i=0;i<fixtures.length;i++) await benchmarkFixture(fixtures[i],i); |
| 13 | } finally { |
| 14 | control.close(); probes.close(); |
| 15 | diff --git a/scripts/prepare-git-performance.py b/scripts/prepare-git-performance.py |
| 16 | index 4297f36..09ec335 100644 |
| 17 | --- a/scripts/prepare-git-performance.py |
| 18 | +++ b/scripts/prepare-git-performance.py |
| 19 | @@ -1,15 +1,21 @@ |
| 20 | #!/usr/bin/env python3 |
| 21 | """Prepare immutable bare Git benchmark fixtures and inventory corpus metadata. |
| 22 | |
| 23 | -Usage: prepare-git-performance.py --source-directory ~/Developer --out /tmp/tinyrelay-corpus |
| 24 | -The output directory must not already exist. Each selected repository is cloned |
| 25 | +Usage: prepare-git-performance.py --source-directory ~/Developer --out /tmp/tinyrelay-corpus [--repos a,b] |
| 26 | +Every Git repository directly under the source directory is selected unless |
| 27 | +--repos names a subset. The output directory must not already exist. Each selected repository is cloned |
| 28 | with --bare --no-local; the source working trees are never modified. |
| 29 | """ |
| 30 | from __future__ import annotations |
| 31 | import argparse, json, os, shutil, subprocess, tempfile |
| 32 | from pathlib import Path |
| 33 | |
| 34 | -NAMES = ("nzip", "bindws", "diagramzip", "strudel", "doorbearer", "atlas") |
| 35 | +def is_repository(path: Path) -> bool: |
| 36 | + return (path / ".git").exists() or (path / "HEAD").exists() |
| 37 | + |
| 38 | +def select(source: Path, names: list[str] | None) -> list[str]: |
| 39 | + if names: return names |
| 40 | + return sorted(p.name for p in source.iterdir() if p.is_dir() and is_repository(p)) |
| 41 | |
| 42 | def run(*args: str, cwd: Path | None = None, input: str | None = None) -> str: |
| 43 | p = subprocess.run(args, cwd=cwd, input=input, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| 44 | @@ -59,14 +65,16 @@ def inventory(repo: Path, name: str) -> dict: |
| 45 | "blobs_over_10MiB": sum(size > 10 * 1024 * 1024 for size, _ in blob_sizes), |
| 46 | } |
| 47 | |
| 48 | -def prepare(source: Path, out: Path) -> list[dict]: |
| 49 | +def prepare(source: Path, out: Path, names: list[str] | None = None) -> list[dict]: |
| 50 | if out.exists(): raise FileExistsError(f"refusing existing destination: {out}") |
| 51 | + selected = select(source, names) |
| 52 | + if not selected: raise FileNotFoundError(f"no repositories under {source}") |
| 53 | out.mkdir(parents=True) |
| 54 | rows = [] |
| 55 | try: |
| 56 | - for name in NAMES: |
| 57 | + for name in selected: |
| 58 | src = source / name |
| 59 | - if not (src / ".git").exists() and not (src / "HEAD").exists(): raise FileNotFoundError(f"missing source repository: {src}") |
| 60 | + if not is_repository(src): raise FileNotFoundError(f"missing source repository: {src}") |
| 61 | dest = out / (name + ".git") |
| 62 | run("git", "clone", "--bare", "--no-local", str(src), str(dest)) |
| 63 | rows.append(inventory(dest, name)) |
| 64 | @@ -82,24 +90,26 @@ def self_test() -> None: |
| 65 | src.mkdir(); run("git", "init", "-b", "trunk", str(src)); run("git", "-C", str(src), "config", "user.email", "test@example.invalid"); run("git", "-C", str(src), "config", "user.name", "test") |
| 66 | (src / "tracked").write_text("tracked\n"); run("git", "-C", str(src), "add", "tracked"); run("git", "-C", str(src), "commit", "-m", "one"); run("git", "-C", str(src), "branch", "feature"); run("git", "-C", str(src), "tag", "v1") |
| 67 | (src / "uncommitted").write_text("must not be copied\n") |
| 68 | - # Exercise prepare() itself without copying the real corpus: six source |
| 69 | - # names all point at the same tiny synthetic repository. |
| 70 | - for name in NAMES: os.symlink(src, root / name, target_is_directory=True) |
| 71 | - rows = prepare(root, out) |
| 72 | - assert len(rows) == len(NAMES) |
| 73 | + # Exercise prepare() itself without copying a real corpus: three |
| 74 | + # fixture names all point at the same tiny synthetic repository. |
| 75 | + names = ("alpha", "beta", "gamma") |
| 76 | + for name in names: os.symlink(src, root / name, target_is_directory=True) |
| 77 | + rows = prepare(root, out, list(names)) |
| 78 | + assert len(rows) == len(names) |
| 79 | row = rows[0] |
| 80 | assert row["head_branch"] == "refs/heads/trunk" and row["head"] == run("git", "-C", str(src), "rev-parse", "HEAD") |
| 81 | assert row["tags"] == 1 and row["all_commits"] == 1 |
| 82 | - assert "uncommitted" not in run("git", "--git-dir", str(out / "nzip.git"), "ls-tree", "-r", "--name-only", "HEAD") |
| 83 | + assert "uncommitted" not in run("git", "--git-dir", str(out / "alpha.git"), "ls-tree", "-r", "--name-only", "HEAD") |
| 84 | |
| 85 | if __name__ == "__main__": |
| 86 | parser = argparse.ArgumentParser(description=__doc__) |
| 87 | parser.add_argument("--source-directory", type=Path, default=Path.home() / "Developer") |
| 88 | parser.add_argument("--out", type=Path) |
| 89 | + parser.add_argument("--repos", help="comma-separated repository directory names to select") |
| 90 | parser.add_argument("--self-test", action="store_true") |
| 91 | args = parser.parse_args() |
| 92 | if args.self_test: self_test() |
| 93 | else: |
| 94 | if args.out is None: parser.error("--out is required unless --self-test is used") |
| 95 | - rows = prepare(args.source_directory.expanduser(), args.out.expanduser()) |
| 96 | + rows = prepare(args.source_directory.expanduser(), args.out.expanduser(), args.repos.split(",") if args.repos else None) |
| 97 | print(json.dumps(rows, indent=2)) |