Orca-The-Company/sparse
A git utility tool designed to ease your stacked pr git workflow
sparse
is a powerful command-line tool that transforms how you work with Git by enabling elegant stacked pull request workflows. Break down complex features into smaller, manageable, and reviewable chunks while maintaining clear dependencies between them.
Traditional development often leads to massive PRs that are:
Stacked PRs solve this by:
Sparse creates a structured branching system that mirrors your feature's logical breakdown:
gitGraph
commit id: "main"
branch sparse/user/auth-system
checkout sparse/user/auth-system
commit id: "Feature: Auth System"
branch sparse/user/auth-system/database
checkout sparse/user/auth-system/database
commit id: "Add user table schema"
commit id: "Add migration scripts"
branch sparse/user/auth-system/api
checkout sparse/user/auth-system/api
commit id: "Add login endpoint"
commit id: "Add registration endpoint"
branch sparse/user/auth-system/frontend
checkout sparse/user/auth-system/frontend
commit id: "Add login form"
commit id: "Add registration form"
Each branch (called a "slice") represents a focused PR:
PATH
git clone <repository-url>
cd sparse
zig build
The binary will be available at zig-out/bin/sparse
.
Sparse uses Git configuration variables to identify users and create properly namespaced branches. Before using sparse, you need to configure your user identification.
Sparse needs to identify you to create unique branch names. It uses a hierarchical configuration approach:
sparse.user.id
(recommended)user.email
(standard Git config)# Recommended: Set a sparse-specific user ID
git config sparse.user.id "jane.doe"
# Alternative: Ensure your Git email is set (usually already configured)
git config user.email "[email protected]"
Config Variable | Purpose | Example | Required |
---|---|---|---|
sparse.user.id |
Primary user identifier for branch naming | jane.doe |
✅ Recommended |
user.email |
Fallback identifier if sparse.user.id not set | [email protected] |
⚠️ Fallback |
The user identifier becomes part of your branch structure:
sparse/<user-id>/<feature-name>/slice/<slice-name>
Examples:
# With sparse.user.id = "jane.doe"
sparse/jane.doe/auth-system/slice/database
sparse/jane.doe/auth-system/slice/api
# With user.email = "[email protected]"
sparse/[email protected]/auth-system/slice/database
sparse/[email protected]/auth-system/slice/api
# Global configuration (affects all repositories)
git config --global sparse.user.id "jane.doe"
# Repository-specific configuration
git config sparse.user.id "jane.doe"
Check your configuration:
# Check sparse-specific config
git config sparse.user.id
# Check fallback email config
git config user.email
# View all sparse-related config
git config --get-regexp sparse
# Start a new feature targeting the main branch
sparse feature auth-system --to main
# Or create a feature with an initial slice
sparse feature auth-system database --to main
# Make some changes for your database layer
git add .
git commit -m "Add user table schema"
# Create a new slice for API work
sparse slice api
sparse status # to check dont be afraid to run this
sparse update # to update the slices in remote this does force-with-lease use with caution and make sure status doesnt report corruption
# Continue development...
git add .
git commit -m "Add authentication endpoints"
# Create another slice for frontend
sparse slice frontend
git add .
git commit -m "Add login components"
sparse status
This shows a beautiful overview of your feature:
┌─ Sparse Feature Status
│
├─ 🎯 Active Feature: auth-system
│
├─ 📊 Slice Analysis:
│ ✓ Orphan slices: 1 (ideal: 1)
│ ✓ Forked slices: 0 (ideal: 0)
│
├─ 🎯 Target Branch: main
│
├─ 🌳 Slice Graph:
│
│ ├─ 🍃 frontend ↓ api
│ ├─ 🔸 api ↓ database
│ └─ 🌱 database → main
│
├─ 🔄 Merge Status:
│ ✗ database: not merged
│ ✗ api: not merged
│ ✗ frontend: not merged
│
├─ 📈 Summary:
│ 📊 Total slices: 3
│ ✅ Merged: 0 / 3
│ 🔄 Pending: 3
│
└─ ✨ Status complete
sparse feature
Create or switch to a feature branch.
sparse feature [ options ] <feature_name> [<slice_name>]
args:
<feature_name>: name of the feature to be created. If a feature with the same name
exists, sparse simply switches to that feature.
<slice_name>: name of the first slice in newly created feature. This
argument is ignored if the feature already exists.
options:
--to <branch>: branch to build on top. Pull requests for the new
feature will target this branch. (default: main)
-h, --help: shows this help message.
Examples:
# Create a new feature
sparse feature user-dashboard --to main
# Create feature with initial slice
sparse feature payment-flow checkout --to develop
# Switch to existing feature
sparse feature user-dashboard
sparse slice
Create or switch to a slice within the current feature.
sparse slice [ options ] [<slice_name>]
args:
<slice_name>: name of the new slice in feature.
If the slice with the same name exists, then sparse switches to that slice.
If <slice_name> is not provided, then sparse creates the slice based on the number of slices currently in the feature.
options:
-h, --help: shows this help message.
Examples:
# Create auto-numbered slice
sparse slice
# Create named slice
sparse slice validation
# Switch to existing slice
sparse slice database
sparse status
Show comprehensive status of the current feature.
sparse status [ options ]
Show status information for current feature
options:
-h, --help: shows this help message.
Displays:
sparse update
Update and rebase all slices in the current feature.
sparse update [ options ] [<slice_name>]
Update all slices in current feature
options:
--continue: continue updating after fixing merge conflicts
-h, --help: shows this help message.
Let's walk through building a complete user authentication system:
sparse feature auth-system --to main
# Work on database schema
git add migrations/001_create_users.sql
git commit -m "Add users table with email and password fields"
git add models/user.js
git commit -m "Add User model with validation"
# you can either use git push or
# sparse update to push changes to remote
git push
# make sure to run sparse status whenever you feel like it
sparse slice api
git add routes/auth.js
git commit -m "Add POST /login and /register endpoints"
git add middleware/auth.js
git commit -m "Add JWT authentication middleware"
sparse slice frontend
git add components/LoginForm.jsx
git commit -m "Add login form component"
git add components/RegisterForm.jsx
git commit -m "Add registration form component"
sparse slice integration
git add tests/auth.integration.test.js
git commit -m "Add end-to-end authentication tests"
graph TD
A[main] --> B[database]
B --> C[api]
C --> D[frontend]
D --> E[integration]
B1[PR #1: Database Schema] -.-> B
C1[PR #2: API Endpoints] -.-> C
D1[PR #3: Frontend Components] -.-> D
E1[PR #4: Integration Tests] -.-> E
style B1 fill:#e1f5fe
style C1 fill:#f3e5f5
style D1 fill:#e8f5e8
style E1 fill:#fff3e0
sparse status
to visualize your progresssequenceDiagram
participant Dev as Developer
participant Git as Git Repository
participant CI as CI/CD
participant Rev as Reviewers
Dev->>Git: Push database slice
Git->>CI: Run tests for database
CI->>Rev: Request review for PR #1
Dev->>Git: Push API slice (depends on database)
Git->>CI: Run tests for API + database
CI->>Rev: Request review for PR #2
Rev->>Git: Approve & merge PR #1
Dev->>Git: Run `sparse update` to rebase unmerged slices
Git->>CI: Auto-rebase PR #2 onto main
Dev->>Git: Run `sparse update` to rebase unmerged slices
Rev->>Git: Approve & merge PR #2
# Continue the pattern...
Sparse uses a consistent naming convention:
sparse/<user.email>/<feature-name>/slice/<slice-name>
Example:
sparse/[email protected]/user-dashboard/slice/
├── database # Database schema changes
├── api # Backend API endpoints
├── frontend # UI components
└── integration # End-to-end tests
# When main branch gets updated
sparse update
# If conflicts occur
# ... resolve conflicts manually ... and then
sparse update --continue
Sparse provides helpful diagnostics:
sparse status
# Shows warnings if slice structure is corrupted
sparse update
# If conflicts occur, sparse pauses for manual resolution
git add resolved-files.js
git rebase --continue
sparse update --continue
This project is licensed under the LICENSE file in the repository.