Skip to content

feat(git-native): in-process gitoxide blob reads for the diff panel (Rust core, PR 1)#9312

Open
ioitiki wants to merge 16 commits into
stablyai:mainfrom
ioitiki:feat/git-native-blob-reads
Open

feat(git-native): in-process gitoxide blob reads for the diff panel (Rust core, PR 1)#9312
ioitiki wants to merge 16 commits into
stablyai:mainfrom
ioitiki:feat/git-native-blob-reads

Conversation

@ioitiki

@ioitiki ioitiki commented Jul 18, 2026

Copy link
Copy Markdown

Why

Discussion #6314 asked whether Orca should move to a Rust core. After a deep review of the codebase, the highest-leverage, lowest-risk first target isn't the terminal plane (which @nwparker's team is already experimenting on) — it's git, where Orca shells out to the git CLI for everything and the measured cost is process-spawn churn (the main-thread-churn-probe exists precisely for this, per #7576/#7225).

This is the first slice: diff blob reads. Opening a source-control review currently spawns two git show processes per file (git show <oid>:<path> + git show :<path>); a 50-file review is ~100 process spawns. This PR serves those reads from an in-process gitoxide reader instead.

@nwparker set the bar as "PRs welcome if they can guarantee zero regressions." That bar is the organizing principle of this PR — the design makes zero-regression structural, not a promise (see Safety below).

What this does

A new Rust crate native/git-native (napi-rs + gix) exposes two async blob reads. A strategy layer in src/main/git/blob-reader.ts decides per-read whether to use native or the existing CLI path; status.ts's two blob readers become thin mappers over it. Off by default.

Measured (200 iters, git show HEAD:package.json vs native, this repo):

cli  git show: p50=2.80ms  p95=3.51ms
native gix   : p50=0.26ms  p95=0.52ms
speedup: ~11x (10–15x across runs)

The CLI cost is dominated by process spawn — which is the real per-read cost being eliminated. The native side fully materializes the same blob bytes (it isn't doing less work); if anything the benchmark is conservative (it re-opens the repo every call, and the CLI's earlier run warms the OS cache in native's favor). Byte-for-byte equality is proven separately by the parity suite below, not by the benchmark.

Safety: zero-regression is structural

The CLI path is never modified in behavior — Task "blob-reader seam" is a behavior-neutral refactor proven by the entire existing src/main/git suite passing with zero test changes, and the two status.ts readers preserve their exact prior result semantics (verified branch-by-branch in review). With the flag off, native is never even loaded — the feature is provably inert.

When the flag is on, a fallback ladder guarantees a user can only ever get slower, never wrong:

Condition Behavior
flag off / no env CLI (native never loaded)
addon missing or fails to load CLI (one git_native_load_failed telemetry event, then silent)
WSL-routed (wslDistro / \\wsl.localhost) or SSH repo CLI / relay (native-host only)
native read throws CLI fallback
post-read abort signal not-found (mirrors CLI catch path)
any sampled native-vs-CLI divergence session "poisons" back to CLI-only for its lifetime

Shadow verification: in on mode the first few reads plus a 1/128 sample are re-run through the CLI in the background and compared byte-for-byte; a single divergence permanently reverts the process to CLI-only and emits a capped git_native_shadow_divergence event. ORCA_NATIVE_GIT=shadow dual-runs every read (serving the trusted CLI result) for dogfooding. So even an unsampled gix/git disagreement self-heals the moment the sampler catches it.

Kill switch: ORCA_NATIVE_GIT=0 / off / false. Env always overrides the setting.

Correctness: differential parity suite (the un-bypassable gate)

blob-reader.parity.test.ts runs the real native reader against the real git CLI — the CLI is the oracle — over a fixture matrix: loose and packed objects, CRLF (raw object bytes, no smudge), nested unicode paths, binary blobs, empty (0-byte) files, the exact 10 MB size boundary (limit → served, limit+1 → too-large, on both), explicit commit OIDs, bad revs, staged-content-≠-HEAD, linked-worktree private index, and unmerged (no stage-0) paths. 18 tests, zero divergence. CI builds the addon and runs this suite under ORCA_REQUIRE_GIT_NATIVE=1, so a missing addon fails the build loudly rather than silently skipping the gate.

The Rust crate additionally has 21 hermetic cargo tests (mutation-tested in review) covering the gix logic in isolation. cargo test and the parity suite are complementary — the former can't catch N-API marshaling or CLI-divergence bugs; the latter does.

Documented intentional divergences (excluded from the oracle, unreachable in production): gitlink/submodule and sparse-dir entries return not-found while git show pretty-prints a locally-resolvable gitlink — Orca already routes submodules away before blob reads (status.ts loadDiff). Also on the shadow watchlist: git replace refs, sparse-index repos.

Scope cuts (intentional, called out for review)

  1. No settings-UI toggle. The flag is reachable via the settings file and ORCA_NATIVE_GIT env only. A renderer checkbox is deliberately out of scope for PR 1.
  2. Release workflows not Rust-wired. PR CI builds the addon and enforces the parity gate; the release pipelines (release-mac-build.yml etc.) are not touched, because that path involves signing/notarization and mac universal-arch packaging that deserves its own maintainer-reviewed change. Until then, packaged release builds simply omit the addon (the packaging entry is conditional on the artifact existing) and the runtime degrades to CLI — safe by construction. The build script already supports the mac dual-arch lipo path for when that lands.

Reviewer notes / conscious choices

  • CI uses dtolnay/rust-toolchain@stable (matching the repo's floating-tag action pins; the crate declares rust-version = "1.95"). A pinned toolchain would be more deterministic at the cost of forward-compat signal — happy to change if you prefer.
  • gix is trimmed to default-features = false, features = ["revision", "index"] — dropping its network/credentials stack (213→175 crates) and structurally removing gix-filter, so the "raw object bytes, no smudge" contract can't be violated by accident.
  • Buffer transfer is zero-copy under Node, but Electron ≥21's V8 memory cage forces one copy per result — negligible at the 10 MB cap, noted for completeness.
  • PR CI gates byte-parity on Linux only (single ubuntu-latest job). macOS/Windows parity leans on shadow telemetry rather than a per-platform CI gate — gix is platform-agnostic and paths are forward-slash-normalized before the read. The recommendation (documented in git-compatibility.md) is to flip the flag on for real users only after clean cross-platform shadow results.
  • First Rust/cargo toolchain in the repo; Cargo.lock is committed (application crate).

Follow-ups (PR 2+)

The natural next slice is the status bundle — replacing the 3–9 spawns per status poll (porcelain + ahead/behind + numstat) with a single in-process gitoxide call, which is the actual #7576/#7225 churn kill-shot and reuses this crate's foundation. Then branch-compare, then a native relay for SSH.

Smaller follow-ups when this graduates from experimental: a settings-UI toggle in ExperimentalPane (read-path is already wired), which will also need experimentalNativeGit added to SETTINGS_CHANGED_WHITELIST; and Rust wiring in the release workflows so packaged builds ship the addon.

🤖 Generated with Claude Code

ioitiki and others added 15 commits July 17, 2026 14:33
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…gence

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rd clarity

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…utral)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…on poisoning

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…wiring

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 47c89859-e835-4852-9f04-e35849dd8985

📥 Commits

Reviewing files that changed from the base of the PR and between 306ec7a and 9b2674a.

📒 Files selected for processing (6)
  • native/git-native/src/blob_read.rs
  • native/git-native/src/lib.rs
  • native/git-native/tests/blob_read.rs
  • src/main/git/blob-reader.test.ts
  • src/main/git/blob-reader.ts
  • src/main/git/git-native-module.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/main/git/git-native-module.ts
  • native/git-native/src/lib.rs
  • src/main/git/blob-reader.test.ts
  • src/main/git/blob-reader.ts

📝 Walkthrough

Walkthrough

Adds a Rust N-API addon for bounded Git blob reads by revision and index path, with async JavaScript bindings and repository tests. Adds build, packaging, benchmark, and CI integration for the addon. Introduces native module loading, experimental settings, telemetry, CLI fallback, shadow verification, sampled parity checks, and session fallback on divergence. Updates Git status blob reads to use the shared abstraction and documents the experimental behavior.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is detailed, but it does not follow the required template headings and omits explicit Screenshots, Testing, AI Review Report, Security Audit, and Notes sections. Rewrite the PR description using the repository template: Summary, Screenshots, Testing checklist, AI Review Report, Security Audit, and Notes.
Docstring Coverage ⚠️ Warning Docstring coverage is 52.31% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: experimental Rust/gitoxide blob reads for the diff panel.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: adeba08b-037d-4139-b5ac-9d999c33e5b2

📥 Commits

Reviewing files that changed from the base of the PR and between 72d0a40 and 306ec7a.

⛔ Files ignored due to path filters (1)
  • native/git-native/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (23)
  • .github/workflows/pr.yml
  • .gitignore
  • config/electron-builder.config.cjs
  • config/scripts/build-git-native.mjs
  • config/scripts/build-native-for-platform.mjs
  • docs/reference/git-compatibility.md
  • native/git-native/Cargo.toml
  • native/git-native/build.rs
  • native/git-native/src/blob_read.rs
  • native/git-native/src/lib.rs
  • native/git-native/tests/blob_read.rs
  • package.json
  • src/main/git/blob-reader.parity.test.ts
  • src/main/git/blob-reader.test.ts
  • src/main/git/blob-reader.ts
  • src/main/git/git-native-module.test.ts
  • src/main/git/git-native-module.ts
  • src/main/git/status.ts
  • src/main/index.ts
  • src/shared/constants.ts
  • src/shared/telemetry-events.ts
  • src/shared/types.ts
  • tools/benchmarks/git-native-blob-read-bench.mjs

Comment thread native/git-native/src/blob_read.rs Outdated
Comment thread native/git-native/src/lib.rs Outdated
Comment thread src/main/git/blob-reader.ts Outdated
Comment thread src/main/git/git-native-module.ts Outdated
…verify before poisoning

Addresses CodeRabbit review on stablyai#9312.

- blob_read.rs/lib.rs: operational gix failures (repo open, rev resolution,
  object read) now return Err and the N-API call rejects, so the TS seam falls
  back to the git CLI instead of serving an empty diff on a repo feature gix
  cannot handle. Genuine missing paths still resolve as Ok(NotFound).
- git-native-module.ts: guard the load-failure track() so a throwing telemetry
  call cannot escape loadGitNativeModule and abort the read; the prior comment
  overclaimed the never-throw contract.
- blob-reader.ts: re-verify a detected divergence before poisoning. A concurrent
  HEAD move or index stage between the native read and the CLI verify was a false
  divergence that permanently disabled native reads and emitted misleading
  telemetry; poisoning now requires the mismatch to reproduce on a fresh read.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@AmethystLiang AmethystLiang self-assigned this Jul 18, 2026
@AmethystLiang
AmethystLiang self-requested a review July 18, 2026 16:48
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.

2 participants