git-testkit provides helpers for writing Go tests that exercise real Git repositories.
- Exercise real git behavior instead of mocking command output.
- Build common repo states quickly (dirty trees, detached HEAD, diverged remotes, worktrees).
- Reuse expensive setups across tests with in-memory snapshots.
go get github.com/git-fire/git-testkitgitmust be installed and available onPATH- Go 1.22+
- Repository fixtures (
CreateTestRepo,CreateBareRemote,RunGitCmd) - Scenario builders for common multi-repo states (
NewScenario, conflict/worktree helpers) - Snapshot helpers for capturing and restoring repository state in tests
- Sanitize/rewrite validation helpers for blocked-string coverage across content/history/refs/paths
CreateTestRepo(t, RepoOptions)creates a real repository with optional files/remotes/branches.CreateBareRemote(t, name)creates a bare repository for remote testing.NewScenario(t)returns a fluent builder for multi-repo test topologies.SnapshotRepo(t, path)andRestoreSnapshot(t, snap)speed up repeated fixture setup.IsDirty,GetCurrentSHA,GetBranches,GetRemotesprovide common assertions/helpers.CreateRewriteValidationFixture(t, opts)seeds blocked strings across file contents, commit messages, refs, and paths.AssertBlockedStringCoverageDetectedandAssertBlockedStringCoverageRemovedvalidate sanitize/rewrite coverage before/after rewriting.
- Create fixture repos with
CreateTestRepoorNewScenario. - Apply setup operations with fluent helpers (
AddFile,Commit,WithRemote,Push). - Run your code under test against the real repository paths.
- Assert with helper methods (
IsDirty,GetCurrentSHA,GetBranches).
Minimal test flow:
func TestMyGitBehavior(t *testing.T) {
repoPath := testutil.CreateTestRepo(t, testutil.RepoOptions{Name: "subject"})
testutil.RunGitCmd(t, repoPath, "checkout", "-b", "feature")
// call your package functions here
if testutil.IsDirty(t, repoPath) {
t.Fatal("repo should be clean")
}
}package mypkg_test
import (
"testing"
testutil "github.com/git-fire/git-testkit"
)
func TestWithRepo(t *testing.T) {
repoPath := testutil.CreateTestRepo(t, testutil.RepoOptions{
Name: "my-repo",
})
if repoPath == "" {
t.Fatal("expected a repo path")
}
}- All helper-created repositories use
t.TempDir(). - Repos/worktrees are automatically removed by Go's test framework at test completion.
- As with any temp directories, force-killed test processes may leave files behind.
func TestConflictFlow(t *testing.T) {
_, local, _ := testutil.CreateConflictScenario(t)
// Exercise your logic against a real diverged local clone.
testutil.RunGitCmd(t, local.Path(), "status")
}func TestUsingSnapshot(t *testing.T) {
_, repo := testutil.CreateLargeRepoScenario(t, 20, 10)
snap := testutil.SnapshotRepo(t, repo.Path())
clonePath := testutil.RestoreSnapshot(t, snap)
// Use clonePath in assertions without rebuilding the fixture each time.
testutil.RunGitCmd(t, clonePath, "status")
}func TestRewriteRemovesBlockedString(t *testing.T) {
fixture := testutil.CreateRewriteValidationFixture(t, testutil.RewriteValidationFixtureOptions{
Name: "rewrite-fixture",
BlockedString: "blockedtoken",
})
// Confirm fixture coverage before running your rewrite logic.
testutil.AssertBlockedStringCoverageDetected(t, fixture.RepoPath, fixture.BlockedString)
// Run your sanitizer/rewrite command against fixture.RepoPath here.
// Example:
// testutil.RunGitCmd(t, fixture.RepoPath, "filter-branch", ...)
// Validate the blocked string is removed from all tracked surfaces.
testutil.AssertBlockedStringCoverageRemoved(t, fixture.RepoPath, fixture.BlockedString)
}Run just sanitize/rewrite-focused tests:
go test ./... -run 'RewriteValidation|BlockedStringCoverage'- Snapshots are intended for deterministic test fixtures and only restore regular files/directories.
- Helpers fail tests immediately (
t.Fatalf) when git commands fail, so errors surface close to setup code. - When tests build large repo graphs repeatedly, prefer snapshot/restore to reduce total runtime.
git-testkit was extracted from git-fire before its public launch and published as a standalone Go library. The test helpers had accumulated enough utility to be useful on their own, so they were split out to let other projects automate real Git repositories without depending on git-fire itself.
- See
DEVELOPER_GUIDE.mdfor:- testing and quality gates
- usage guidance for library consumers
- administration/maintenance and release workflow
- contribution process and PR expectations