|
| 1 | +# Phase A Handoff: State, Remaining Work, and Instructions for the Next Agent |
| 2 | + |
| 3 | +Last updated: end of the A6 commit (`b5c44b02ef`); A8 work in progress in the working tree. |
| 4 | + |
| 5 | +## Repo ground rules (from AGENTS.md) |
| 6 | + |
| 7 | +- Run **all** cargo commands from `crates/` (`cd crates`). The root `Cargo.toml` |
| 8 | + is a stale leftover; running cargo from the repo root is wrong. |
| 9 | +- C git is the spec; `t/` is the oracle; byte-identical stdout/stderr/exit |
| 10 | + codes are the acceptance test. |
| 11 | +- An **external watcher** (watch-and-commit / a formatting hook) rewrites |
| 12 | + files in this workspace (you will see "file has been modified since last |
| 13 | + read"). Re-read before editing; after writing, verify the bytes landed |
| 14 | + (plain `git diff`). |
| 15 | + **Escape gotcha:** this environment historically mangled `\n` escapes in |
| 16 | + Python heredocs. When editing Rust string/char literals, prefer `sed`/ |
| 17 | + byte-level Python (e.g. `bytes([92,110])`) or write the whole file with a |
| 18 | + single `write` tool call, then confirm with `git diff` before building. |
| 19 | +- Each item commits separately with `<git_commits>` conventions. |
| 20 | + |
| 21 | +## Completed and committed (all with crosswise suites in `xtask::suites()`, scoreboard updated) |
| 22 | + |
| 23 | +| Item | Commit | Notes | |
| 24 | +|---|---|---| |
| 25 | +| A2 repo discovery / env threading | `6fe5d152e4` (+watcher commits) | RepoContext everywhere, `--git-dir` etc. | |
| 26 | +| A1 sha1dc | `97bf4a36e3` | SHAttered detection, hash-object parity | |
| 27 | +| A3 cat-file batch | `f190e10c49` | `%(atom)`, `--batch-all-objects`, `-z`, rev:path | |
| 28 | +| A4 abbreviation resolution | `1c53275cd8` | `git_revision::Resolver`, peels, ambiguity text | |
| 29 | +| A5 rev-parse | `c27d6c5f52` | ranges, symbolic, shape flags, sq, env vars | |
| 30 | +| A12 local timezone | `151b04a961` | TZif reader, DST-correct idents, calendar months | |
| 31 | +| A10 count-objects | `e3145f6846` | garbage semantics, -H, usage errors | |
| 32 | +| A7 pretty engine | `14e0ac4337` | git-pretty crate; log driven by it | |
| 33 | +| A6 rev-list/log options | `b5c44b02ef` | rev_info walker; topo/date order, filters, ranges, --objects, path limits | |
| 34 | + |
| 35 | +`cargo test --workspace`: **55 test binaries, all green** (before the A8 |
| 36 | +work-tree edits; re-verify after finishing A8). |
| 37 | + |
| 38 | +## IN PROGRESS: A8 diff engine completion (uncommitted working tree) |
| 39 | + |
| 40 | +Target file: `docs/plan/phase-a/08-diff-options.md`. Much is implemented and |
| 41 | +byte-identical vs system git on the /tmp fixture matrix, listed below. |
| 42 | + |
| 43 | +### What is already done in the working tree (verify, don't redo) |
| 44 | + |
| 45 | +- `git-diff/src/unified.rs`: `render_unified_ctx` with `-U<n>`, the |
| 46 | + `\ No newline at end of file` marker, section-context in `@@` headers |
| 47 | + (last kept line before the hunk, e.g. `@@ -2 +2 @@ a`), and zero-count |
| 48 | + hunk ranges (`@@ -3,0 +4 @@`). Unit tests present. |
| 49 | +- `git-diff/src/tree.rs`: `Change` gained `old_path`, `new_path`, `score`; |
| 50 | + `MAX_SCORE = 60000`. |
| 51 | +- `git-command/src/patch.rs`: rewrote the renderers — `render_stat` |
| 52 | + (exact C `show_stats`: 80-col width, scale_linear, Bin handling, |
| 53 | + number_width=3 for binaries), `render_shortstat`, `render_numstat` |
| 54 | + (binary rows as `-\t-\tpath`), `render_name_line`, `render_raw` |
| 55 | + (abbreviated 7-char oids, `dirty` new-side as `0000000`), `render_summary`, |
| 56 | + `render_change_patch_ctx` (rename blocks, binary footers, mode-change |
| 57 | + lines, `-U`). `BlobSource` bridges odb + synthetic blobs. `mode6` |
| 58 | + zero-pads modes to 6 digits (`40000`→`040000`) and renders absent modes |
| 59 | + as `000000`. |
| 60 | +- `git-command/src/diff.rs`: rewrote with option parsing (`--stat`, |
| 61 | + `--shortstat`, `--numstat`, `--name-only`, `--name-status`, `--raw`, |
| 62 | + `--summary`, `--patch-with-stat`, `-U<n>`, `-s`, `--cached/--staged`, |
| 63 | + `-M/--find-renames[=n]`, `--no-renames`, `--diff-filter=`, |
| 64 | + `--exit-code/--quiet`, `--no-index`), sources (worktree synthetic tree |
| 65 | + built from `git-index` + stat-match reuse of index oids; index tree from |
| 66 | + flat entries; HEAD/tree resolution via `resolve_arg`), delta blobs kept |
| 67 | + in an `extra` map, rename detection (exact pass + line-similarity pass), |
| 68 | + and path limiting. |
| 69 | +- `git-index`: added `Default` impl for `Index`. |
| 70 | +- Crosswise suite `crates/git-command/tests/phaseA08_crosswise.rs` |
| 71 | + (fixture with no-EOL modify, exact rename, binary, staged add/delete). |
| 72 | + **Verified byte-identical on the matrix below** (run before registering): |
| 73 | + `diff HEAD`, `--stat`, `--numstat`, `--shortstat`, `--name-only`, |
| 74 | + `--name-status`, `--raw`, `--summary`, `--cached`, `--no-renames`, |
| 75 | + `--find-renames=90`, `-U0`, `-U1`, `-U3`, `HEAD -- f.txt`, `HEAD -- bin.dat`, |
| 76 | + `--diff-filter=M`, `--diff-filter=R`, `--exit-code`, plain `diff`, |
| 77 | + `--patch-with-stat`. `diff HEAD -U0` hunk headers byte-identical. |
| 78 | + |
| 79 | +### What must STILL be done to close A8 (in dependency order) |
| 80 | + |
| 81 | +1. **Finish `diff-tree --exit-code`** (`crates/git-command/src/diff_tree.rs`): |
| 82 | + recognize `--exit-code`/`--quiet`; after emitting changes, return |
| 83 | + `CommandError::silent(1)` when changes exist and 0 otherwise (like |
| 84 | + `diff.rs::finish`). Also port `diff-tree` raw rendering to use the new |
| 85 | + `patch::render_raw` (abbreviated 7-char oids per system git) — confirm |
| 86 | + with `git diff-tree HEAD~1 HEAD` in a dirty repo. Do NOT break the |
| 87 | + existing `phase5_crosswise.rs` expectations (it compares raw lines |
| 88 | + with FULL 40-char oids on its fixture — check whether that suite expects |
| 89 | + full or abbreviated; if it expects full, keep `diff-tree` raw full and |
| 90 | + only add `--exit-code`). |
| 91 | +2. **Register the A8 suite** in `xtask/src/main.rs::suites()` as |
| 92 | + `phaseA08-crosswise` and re-run `cargo run -p xtask -- scoreboard` |
| 93 | + (from `crates/`). Run `cargo test --workspace` — ensure 55+ suites green. |
| 94 | +3. **Remove debug leftovers** in the working tree before committing: |
| 95 | + - `crates/git-command/examples/dbgdiff.rs` (and the dir if empty) |
| 96 | + - `crates/git-index/examples/dbg.rs` (and the dir if empty) |
| 97 | + - any `DIFF_DEBUG` eprintln blocks in `git-command/src/diff.rs` |
| 98 | +4. **Commit A8** (conventional message describing what it enables), update |
| 99 | + `docs/plan/phase-a/PROGRESS.md` (mark A8 DONE with implemented scope), |
| 100 | + add the deferred list (word-diff, --color, --patience/--histogram |
| 101 | + algorithms, --dirstat, whitespace family, --relative, -S/-G pickaxe, |
| 102 | + rename/copy heuristic is similarity-approximation not C's |
| 103 | + diffcore-rename algorithm, stat width hard-coded 80 like non-tty C). |
| 104 | +5. **Known A8 deviations to record in FOLLOWUPS**: exact rename scoring |
| 105 | + differs from diffcore-rename for edited renames; `--diff-filter` |
| 106 | + lowercase = exclude implemented; `status` field for `T` (mode change) |
| 107 | + classification. |
| 108 | + |
| 109 | +## REMAINING PHASE A ITEMS (do next, same rigor) |
| 110 | + |
| 111 | +### A9 — userdiff hunk headers (`docs/plan/phase-a/09-userdiff-hunk-headers.md`) |
| 112 | + |
| 113 | +Hunk headers: the `@@ ... @@` suffix now uses the last context line (done in |
| 114 | +A8). Extend with the builtin userdiff drivers (C `userdiff.c`): known |
| 115 | +paths/extensions get function-name regexes (e.g. `*.c` uses |
| 116 | +`^[A-Za-z_].*[^:)]*$`) that select the nearest preceding matching line from |
| 117 | +the OLD side instead of "last context line". Gates: crosswise suite with a |
| 118 | +fixture of `.c`/`.py`/`.md` files; `t/t4018-*.sh` concepts (funcname |
| 119 | +patterns live in `userdiff.c`). Keep default (no driver) behavior |
| 120 | +byte-identical to today. |
| 121 | + |
| 122 | +### A11 — `.gitignore` + attributes engine (`docs/plan/phase-a/11-gitignore-attributes.md`) |
| 123 | + |
| 124 | +New crate (suggest `git-ignore` or put in `git-core`): |
| 125 | +- `.gitignore` parsing per C `dir.c` `parse_ignore_file` (patterns, |
| 126 | + unanchored vs anchored, `!` negation, trailing `/` dir-only, `**`). |
| 127 | +- `git-check-ignore`-style match (walk dirs bottom-up, LAST matching |
| 128 | + pattern wins, negation) and `git status --ignored` groundwork. |
| 129 | +- `.gitattributes` minimal subset: `-diff`, `binary`, |
| 130 | + `diff=<driver>`/`diff=<funcname>` piping into A9. |
| 131 | +- Gates: crosswise suites vs `git check-ignore` on crafted trees. |
| 132 | + |
| 133 | +### A13 — pack delta compression on write (`docs/plan/phase-a/13-pack-delta-compression.md`) |
| 134 | + |
| 135 | +`git pack-objects` currently writes non-deltified packs (recorded |
| 136 | +deviations). Implement delta candidate selection (window), `delta.c` |
| 137 | +delta encoding (`create_delta` semantics), OFS/REF delta entries, and |
| 138 | +`git verify-pack`/`git index-pack` compatibility (C git must verify our |
| 139 | +packs). Crosswise: `git pack-objects <obj-list>` from our binary vs C, |
| 140 | +then `git verify-pack -v` both. Big item; split commit by (a) delta |
| 141 | +encoder CRATE, (b) pack writer integration, (c) crosswise suite. |
| 142 | + |
| 143 | +## General do-this-every-time checklist |
| 144 | + |
| 145 | +1. Read the item's plan doc in `docs/plan/phase-a/` first; it names gates. |
| 146 | +2. Probe system git (`/usr/bin/git`) behavior before coding; write the |
| 147 | + crosswise test first when possible (style: `crates/git-command/tests/ |
| 148 | + phaseA0X_crosswise.rs`). |
| 149 | +3. After each change: `cd crates && cargo build --workspace` (0 errors), |
| 150 | + `cargo test --workspace` (all green), the item's new crosswise test, |
| 151 | + `cargo run -p xtask -- scoreboard` (writes baseline; fails on regression). |
| 152 | +4. `git add -A` + commit per item; update `docs/plan/phase-a/PROGRESS.md` |
| 153 | + and `docs/plan/FOLLOWUPS.md` (`**DONE (AX)**` style entries already used). |
| 154 | +5. Never edit `crates/scoreboard.json` by hand; never run |
| 155 | + `watch-and-commit.sh`; do not commit from the repo root (use `crates/`). |
| 156 | + |
| 157 | +## Suggested next-agent prompt (copy/paste) |
| 158 | + |
| 159 | +"Work through the Phase A remaining items of the pure-Rust git port at |
| 160 | +/Users/ali/dev/rust/git.rs, A8-finish first then A9, A11, A13 in that |
| 161 | +order. Read docs/plan/phase-a/README + the per-item docs (08, 09, 11, 13), |
| 162 | +and docs/plan/phase-a/HANDOFF.md for exact state. A8: finish diff-tree |
| 163 | +--exit-code, register phaseA08-crosswise in xtask::suites(), delete the |
| 164 | +debug example files, run cargo test --workspace and the scoreboard from |
| 165 | +crates/, commit A8 and mark PROGRESS.md; then implement A9 (userdiff hunk |
| 166 | +headers over A8's hunk-context), A11 (.gitignore + attributes with |
| 167 | +check-ignore crosswise parity), A13 (delta encoding in pack-objects |
| 168 | +verified by C git) — each with its own crosswise suite registered in |
| 169 | +xtask, gates green (cargo test --workspace + scoreboard no-regression), |
| 170 | +committed separately, and PROGRESS.md/FOLLOWUPS.md updated. Follow the |
| 171 | +handoff's escape gotcha (prefer byte-level Python/sed for \n edits) and |
| 172 | +the per-commit checklist." |
0 commit comments