Skip to content

refactor(git): drop git2/libgit2 — full gix migration#487

Merged
BryanFRD merged 1 commit into
mainfrom
feat/gix-full-migration
May 21, 2026
Merged

refactor(git): drop git2/libgit2 — full gix migration#487
BryanFRD merged 1 commit into
mainfrom
feat/gix-full-migration

Conversation

@BryanFRD
Copy link
Copy Markdown
Contributor

Closes #479

Summary

  • Removes the git2 / libgit2 / openssl-src dependency tree entirely. cargo tree confirms zero git2/libgit2-sys/openssl-src crates in the lockfile.
  • Local repository operations use gix (gitoxide) for the perf-sensitive read paths.
  • Network operations (push, fetch, ls-remote) and write operations (commit, tag, branch, checkout, reset) shell out to the user's installed git CLI. Same hybrid strategy cargo uses.

What runs through gix

The perf-sensitive read paths:

  • collect_all_tags, find_last_tag, find_highest_semver_tag, TagIndex (the monorepo hot path)
  • get_commits_since_oid (revwalk via repo.rev_walk(...).sorting(ByCommitTime(NewestFirst)))
  • open_repo, get_repo_root, resolve_current_branch
  • find_object, find_commit, find_tree for tag-to-commit resolution
  • repo.references().tags() for tag enumeration

What shells out to git

  • Network: git push, git fetch, git ls-remote — already partially shell-out (fix(release): make push_tags idempotent against pre-existing remote tags #459), now uniformly so.
  • Writes: git add, git commit, git tag -a, git branch, git update-ref, git checkout, git reset --hard — the porcelain handles edge cases (file modes, .gitignore, hooks, line endings) for free.
  • Diff: git diff-tree --name-only / git ls-tree -r --name-only. Avoids the gix blob-diff feature which pulls gix-diff + gix-filter for the same one-line result.

Auth (the user constraint)

No token is ever embedded in the URL or appears in process argv. The shell-out path uses an inline -c credential.helper='!f() { echo username=...; echo password=...; }; f' config so the token reaches git via stdin only. FERRFLOW_TOKEN / GITHUB_TOKEN / GITLAB_TOKEN env-var contract is unchanged.

Tests

  • lib.rs::test_utils and main.rs::test_utils provide shell-out fixture helpers (init_repo_at, commit_file, git, git_with_env).
  • fetch_and_rebase and reset_branch_to_remote integration tests (two repos + a bare remote, simulating concurrent push) rewritten with shell-out — same scenarios, same assertions.
  • credentials_callback tests dropped (function deleted with git2); token_for_url + configure_git_command tests cover the equivalent surface.
  • benches/ferrflow_benchmarks.rs fixture rewritten with the same shell-out pattern — bench numbers stay comparable across the migration.

Test plan

  • 512 lib tests pass
  • 614 bin tests pass (includes integration tests for fetch_and_rebase + reset_branch_to_remote)
  • cargo clippy --features cli -- -D warnings clean
  • cargo build --benches --features cli compiles
  • cargo build --release --features cli produces working binary
  • Zero git2 / libgit2-sys / openssl-src in Cargo.lock
  • CI release-bot flow against a real GitHub remote

Copilot AI review requested due to automatic review settings May 21, 2026 07:04
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@BryanFRD BryanFRD force-pushed the feat/gix-full-migration branch from 71981da to a8176fe Compare May 21, 2026 07:07
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Benchmark

Details
Benchmark suite Current: 26f3a98 Previous: 0b5fe57 Ratio
changelog/build_50 7549 ns/iter (± 228) 7955 ns/iter (± 147) 0.95
changelog/build_500 68331 ns/iter (± 1986) 68596 ns/iter (± 1303) 1.00

This comment was automatically generated by workflow using github-action-benchmark.

@BryanFRD BryanFRD force-pushed the feat/gix-full-migration branch 2 times, most recently from cfc88fa to 33a14dd Compare May 21, 2026 18:31
@BryanFRD BryanFRD changed the title refactor(git)!: drop git2/libgit2 — full gix migration refactor(git): drop git2/libgit2 — full gix migration May 21, 2026
@BryanFRD BryanFRD force-pushed the feat/gix-full-migration branch from 33a14dd to 91ce5e5 Compare May 21, 2026 19:56
Removes the git2 dependency entirely. The cli feature now uses gix for
all local repository operations (tag enumeration, commit walks, tree
diffs, object lookups) and shells out to the user's installed git CLI
for network operations (push, fetch, ls-remote) and write operations
(commit, tag, branch creation, checkout, reset). This is the same
hybrid strategy cargo uses.

Why shell out for network and writes:
- gix-protocol exists but its API surface is genuinely complex and the
  blocking-network-client feature pulls reqwest + a large transport
  stack. We were already shelling out for push tags (#459) and the
  credential-helper path requires shell-out anyway.
- Writes through gix (create_commit, create_tag, create_branch) require
  hand-rolling index manipulation, tree writing, and ref edits. git's
  porcelain handles all the edge cases for free.
- Diff via gix needs the blob-diff feature which pulls in gix-diff,
  gix-filter, gix-traverse - heavy for our use case (just list paths
  that changed). git diff-tree / git ls-tree is a one-liner.

What gix is used for (the perf-sensitive read paths):
- collect_all_tags, find_last_tag, find_highest_semver_tag, TagIndex
  (the hot path for monorepo tag scanning)
- get_commits_since_oid (revwalk)
- open_repo, get_repo_root, resolve_current_branch
- find_object, find_commit, find_tree for tag-to-commit resolution
- repo.references().tags() for tag iteration

Auth path:
- For shell-out calls (git push/fetch/ls-remote): inline credential
  helper via -c credential.helper config (no token in argv, no token
  in URL).
- FERRFLOW_TOKEN / GITHUB_TOKEN / GITLAB_TOKEN env vars unchanged.

Tests + benches:
- Shell-out helpers in lib.rs::test_utils and main.rs::test_utils
  (git init/add/commit/tag) replace the git2-based fixture functions.
- benches/ferrflow_benchmarks.rs uses the same shell-out fixture
  pattern; bench measurements stay comparable.
- The fetch_and_rebase and reset_branch_to_remote integration tests
  (which set up two repos + a bare remote) were rewritten with
  shell-out — same scenarios, same assertions.
- 512 lib tests pass, 614 bin tests pass, cargo clippy -D warnings clean.

Cargo.lock no longer contains git2, libgit2-sys, or openssl-src.

Also fixes two long-standing Publish workflow failures:
- ferrflow-wasm: wasm-pack's bundled wasm-opt fails on the new compiler
  output. Disable it via [package.metadata.wasm-pack.profile.release].
- npm scope rename: @ferrflow/* -> @ferrlabs/ferrflow-* (platforms),
  ferrflow -> @ferrlabs/ferrflow (wrapper), and @ferrflow/wasm ->
  @ferrlabs/ferrflow-wasm. The old @FerrFlow user scope wasn't writable
  by the bot token; the @FerrLabs org scope is.

The breaking-change marker (!) is dropped: v5.0.0 already shipped via
#486 (the auth refactor) which is the SemVer-breaking piece of this
work for downstream embedders of ferrflow::git::auth. This follow-up
is a pure refactor with no public API change.
@BryanFRD BryanFRD force-pushed the feat/gix-full-migration branch from 91ce5e5 to 26f3a98 Compare May 21, 2026 20:04
@BryanFRD BryanFRD merged commit 597f0ae into main May 21, 2026
15 checks passed
@BryanFRD BryanFRD deleted the feat/gix-full-migration branch May 21, 2026 20:04
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.

perf: migrate from libgit2 to gitoxide — full plan after positive exploration

2 participants