etc/plans/git2-to-gix-migration.md
Most of the broad migration is already done. This document only tracks the remaining scope needed to finish, plus the intentional git2 boundaries that are still allowed.
Repository audit on 2026-05-05:
git2:: callsites in crates/**/*.rs: 92git2:: references: 20ctx.git2_repo / ctx.with_git2_repo callsites: 21git2 / git2_repo / git2_hooks references: 36 across 7 filesgit2 is still acceptable only where we do not yet have a practical gix replacement or where the code is deliberately acting as a compatibility adapter:
Anything outside those areas should continue moving to gix.
The remaining work is concentrated in a small set of areas:
These modules still sit on the main checkout/index boundary and should be kept narrow:
crates/gitbutler-workspace/src/branch_trees.rscrates/gitbutler-edit-mode/src/lib.rscrates/gitbutler-oplog/src/oplog.rscrates/gitbutler-branch-actions/src/integration.rsGoal:
git2 usage isolated to the actual checkout/index handoffgix where possibleThese files still appear to contain actionable non-boundary git2 usage or backend leakage:
crates/gitbutler-repo/src/repository_ext.rscrates/gitbutler-repo/src/commands.rscrates/gitbutler-repo/src/rebase.rscrates/gitbutler-repo/src/credentials.rscrates/gitbutler-repo/src/hooks.rscrates/gitbutler-repo/src/managed_hooks.rscrates/gitbutler-repo/src/remote.rscrates/gitbutler-repo/src/staging.rscrates/gitbutler-repo-actions/src/repository.rscrates/gitbutler-tauri/src/projects.rscrates/but-napi/src/lib.rsGoal:
gix-first APIs for repo reads and domain logicgit2 repositories through higher-level application code unless required by the accepted boundarySome crates still intentionally expose git2 compatibility helpers or legacy types:
crates/but-ctx/src/lib.rscrates/but-oxidize/src/lib.rscrates/but-serde/src/lib.rscrates/but-schemars/src/lib.rscrates/gitbutler-repo/src/lib.rscrates/gitbutler-cherry-pick/src/*crates/gitbutler-commit/src/commit_ext.rscrates/gitbutler-stack/src/stack.rsGoal:
Test-only git2 usage still exists, but the next migration target is narrower than "all tests":
remove git2 from repository initial-state setup. Each reusable initial state should be expressed
as a dedicated tests/fixtures/scenario/*.sh script that uses plain git commands.
Tests should load those scripts through existing but-testsupport fixture primitives such as
writable_scenario, read_only_in_memory_scenario, writable_scenario_with_post, or
read_only_in_memory_scenario_named_with_post. Do not replace git2 setup with runtime Git command
helpers such as invoke_git(), git(), git_at_dir(), or similar helpers in test bodies. Those
helpers are useful for assertions and exceptional test actions, but initial repository shape belongs
in fixture scripts.
When a fixture needs virtual_branches.toml, keep Git history and refs in the shell fixture and write
metadata in a post-processing callback. This matches the existing documented but-testsupport pattern:
use a *_with_post fixture loader, increment the post-processing version when the callback changes,
open the generated repository with open_repo, then create or update VirtualBranchesTomlMetadata.
Priority targets:
crates/gitbutler-repo/tests/repo/support/*: done for repository initial states. The remaining
TestingRepository git2 handle exists only for tests that still mutate the index directly.crates/gitbutler-repo/tests/repo/create_wd_tree.rs: done for reusable initial states; they are
now fixture scripts. Remaining per-test filesystem/index mutations are the behavior under test.crates/gitbutler-repo/tests/repo/rebase.rs: done; synthetic git2 commit graph construction was
replaced with fixture scripts exposing named tags for target and incoming commits.crates/gitbutler-branch-actions/tests/branch-actions/virtual_branches/*: replace TestRepo's
git2 setup helpers with fixture states plus post-processed metadata where needed. The shared
default fixture now owns base config, remote URL normalization, and initial remote-tracking refs.Goal:
git2 or runtime Git helper calls
in test bodiesbut-testsupport fixture loaders and gix/but-* APIs for assertions and read-side accessgit2 remains only for explicit hard-boundary coverage, such as checkout/index conflict
materialization or hook compatibilityThe intended end state is:
gix::ObjectId, gix refs, config, and read-side repository access in normal application logicContext::git2_repo treated as a deprecated boundary escape hatch onlygit2 usage limited to explicit hard-boundary or compatibility-adapter codeThis plan is complete when all of the following are true:
git2.git2 use is confined to the accepted boundary or explicit compatibility/adapter code.ctx.git2_repo callers are limited to those accepted sites.git fixture scripts and loaded through but-testsupport
fixture primitives.Recommended checks:
cargo clippy --all-targets --workspace
rg -n "git2::" crates -S --glob '*.rs'
rg -n "ctx\\.(git2_repo|with_git2_repo)" crates -S --glob '*.rs'
rg -n "git2::|git2_repo|git2_hooks|CheckoutBuilder|IndexAddOption|ResetType" crates/*/tests -g '*.rs'
Context::git2_repo deprecatedgit fixture scriptsgit2 usage reduced to boundary coverage onlygit2 audit at zero outside accepted boundaries