Hand-rolled setup
80+ lines of git init → writeFile → commit per test. Drift between tests is inevitable.
Deterministic fixtures for merge conflicts, submodules, multiple remotes, in‑progress operations, monorepos, and more — plus in‑memory mock factories for blazing‑fast unit tests. Real repos or typed mocks, one line each.
npm i -D @gfargo/git-scenarios simple-git Every git tool — TUI, IDE, CLI, linter, formatter, AI assistant — behaves differently against different repo states. Testing those behaviors well means starting from real repositories in carefully-constructed states. @gfargo/git-scenarios is the typed, composable kit for producing those states deterministically.
It ships in three layers, increasingly inline:
// 01. Named scenario — the one-linerconst repo = await spinUpScenario('mid-merge-conflict')
// 02. Scenario + custom atoms on topconst repo = await fromScenario('feature-pr-ready', addCommit({ message: 'extra', files: { 'x.ts': 'x' } }),)
// 03. Compose from primitivesconst repo = await createTempGitRepo()await chain( addCommit({ message: 'base', files: { 'x.ts': 'base\n' } }), switchToBranch('feat/theirs'), addCommit({ message: 'theirs', files: { 'x.ts': 'theirs\n' } }), switchToBranch('main'), addCommit({ message: 'ours', files: { 'x.ts': 'ours\n' } }), startMerge('feat/theirs'),)(repo)// repo is mid-merge with src/x.ts conflictedFor unit tests that don’t need a real repo on disk, the mock layer gives you the same typed state objects in under 1 ms:
// 04. Unit test mocks — no git, no disk, <1msimport { mockStatus, mockSimpleGit } from '@gfargo/git-scenarios/mocks'const status = mockStatus().staged('auth.ts').conflicted('merge.ts').build()const git = mockSimpleGit({ createMockFn: jest.fn, overrides: { status } })Every scenario produces byte-identical state every run. Tests built on top are deterministic too — no flake from clock-drift or hash variance.
spinUpScenario for real repos on disk,
mockFromScenario for typed in-memory mocks.
Hand-rolled setup
80+ lines of git init → writeFile → commit per test. Drift between tests is inevitable.
Checked-in fixtures
Real .git directories in your repo. Bloats the tree, breaks on updates, no programmatic access.
Mocking git commands
Shallow command mocks verify you called the right git CLI args — they say nothing about how your code handles the actual state. Mock the state, not the command. Mock Factories →
Docker containers
Pre-baked images with git state. Heavy, slow, CI-only. Not interactive-test-friendly.
Full comparison: why not just… →
46 curated scenarios
Branch states, merge / rebase / cherry-pick / revert conflicts, bisect, stash, worktrees, submodules, shallow clones, monorepos, orphan branches, large histories. All deterministic. Browse them →
60+ composable atoms
Build any state from typed building blocks — chain(), addCommit, startMerge, withAuthor, insideSubmodule, more. Write your own; they’re just functions. Atom reference →
7 test-runner adapters
Jest, Vitest, node:test, Mocha, AVA, Playwright, Cypress — describeWithScenario('name', (getRepo) => { … }) with automatic cleanup. Pick your runner; the API stays the same. Setup guide →
Tool-agnostic CLI
git-scenarios create mid-merge-conflict --run "lazygit" — materialize any scenario and launch any tool against it. JSON output and tag/kind filters too. CLI docs →
Custom registration
registerScenario(myScenario) — your custom states work everywhere built-ins do. Define your own →
Mock Factories
Zero-dep in-memory mocks for StatusResult, LogResult, BranchSummary — typed, realistic, under 1 ms. Unit-test your git UI without touching disk. Mock Factories →
Dual CJS / ESM
Ships both formats. Use import or require — both work. TypeScript-first with full type declarations. Install →
.git/ marker file. git‑scenarios
produces all four deterministically.
.git/MERGE_HEAD Two branches edit the same line. Merge halts; conflict markers in worktree.
mid-merge-conflict .git/rebase-merge/ Replay halts on a conflict. REBASE_HEAD set; resume with --continue.
mid-rebase-conflict .git/CHERRY_PICK_HEAD Pick from another branch conflicts. Worktree shows both sides.
mid-cherry-pick-conflict .git/REVERT_HEAD Reverting a commit conflicts with later changes. Distinct UI state.
mid-revert-conflict 
git-scenarios inspect — see any scenario’s commit graph, branches, and status without keeping a thing on disk.
# Spin up a merge conflict, launch lazygit against itnpx git-scenarios create mid-merge-conflict --run "lazygit"
# Filter the cataloguenpx git-scenarios list --kind operationnpx git-scenarios list --tag conflict --json
# Describe a specific scenario's contractsnpx git-scenarios describe feature-pr-ready
# See a scenario's shape — graph, branches, status — without keeping itnpx git-scenarios inspect feature-pr-ready
# Snapshot a real repo's shape into a reusable scenario modulenpx git-scenarios capture . --name my-bug-repro > scenarios/my-bug.ts
# Persist a scenario for hand-pokingnpx git-scenarios create monorepo-multi-package --path ~/sandboxOriginally extracted from coco, an AI-powered git CLI/TUI with sixteen specialised views. Every view needed deterministic git states to test against. After writing the same git init → writeFile → commit setup forty-seven times, the scenario library was born.
Now it stands alone — for anyone building git tools, IDE extensions, CLI utilities, linters, formatters, AI assistants, or any program that reads repo state.
Get started
Browse scenarios
Architecture
Source