A CLI tool to automate syncing commits between git branches while filtering out experimental commits.
Sometimes you want to track experimental or temporary changes in git without having them end up in your main codebase. Traditional solutions:
.gitignore- but you lose the changes entirely- Separate branch - but you lose the context and history with your real work
This tool lets you:
- Mark experimental commits with a prefix (default:
remove-me:) - Keep working normally on your dev branch
- Sync only the "real" commits to your target branch automatically
# Use directly with npx (no installation required)
npx git-sift --help
# Or install globally
npm install -g git-sift
# Or install as a dev dependency
npm install --save-dev git-siftFor development:
git clone <repo-url>
cd git-sift
bun install
bun link- Work on your dev branch:
git checkout -b dev
git commit -m "feat: real feature"
git commit -m "remove-me: debug logging"
git commit -m "fix: important fix"- First sync to master (use --force-checkpoint to start from beginning):
git-sift --target master --force-checkpoint-
Result: master gets "feat: real feature" and "fix: important fix", but not the debug logging. A checkpoint UUID is automatically added to both branches.
-
Continue working on dev:
git checkout dev
git commit -m "feat: another feature"
git commit -m "remove-me: temporary code"- Sync again (automatically picks up from last checkpoint):
git-sift --target master- Result: Only the new commits since the last sync are transferred to master
To prevent conflicts during sync, commits marked for removal should only modify files that are not touched by commits being kept.
For example:
- ✅ Safe: A
remove-me:commit adds debug logging todebug.js, while kept commits only modifyfeature.js - ❌ Risky: A
remove-me:commit modifiesutils.js, and a kept commit also modifiesutils.js
When files overlap between filtered and kept commits, the cherry-pick process may encounter conflicts that require manual resolution. Structure your work to isolate experimental changes in separate files when possible.
Recommendation: Consider working on a fresh clone of your repository to avoid any risk of data loss.
git-sift [options]Options:
--target <branch>- Target branch to sync to (default:master)--to-commit <hash>- Sync up to this commit (default:HEAD)--tag <prefix>- Commit message prefix to filter (default:remove-me)--resume- Resume after resolving conflicts--force-checkpoint- Use first commit if no checkpoint found--checkpoint <hash>- Use specific commit as checkpoint--dry-run- Show what would be synced without making changes--help- Show help
Preview what would be synced (dry run):
git-sift --dry-run
# Shows which commits would be synced and which would be filteredSync to main branch:
git-sift --target mainUse custom tag prefix:
# First sync with custom tag (use --force-checkpoint for first time)
git-sift --tag wip --target main --force-checkpoint
# Now commits like "wip: testing" will be filtered
# Subsequent syncs with the same tag
git-sift --tag wip --target mainSync up to specific commit:
git-sift --to-commit abc123 --target mainUse a specific commit as checkpoint:
git-sift --checkpoint abc123 --target mainResume after conflict:
# If sync encounters conflicts:
# 1. Resolve conflicts manually
# 2. Stage resolved files: git add <files>
# 3. Resume:
git-sift --resumeFirst-time sync (start from repository beginning):
# For the first sync on a repository, use --force-checkpoint
# to start from the very first commit
git-sift --target main --force-checkpoint
# After this, subsequent syncs will automatically use the checkpoint- Validation - Checks working directory is clean, you're not on target branch, and finds the checkpoint commit
- Analysis - Gets commits from checkpoint to current position, filters by tag prefix
- Transfer - Checks out target branch, cherry-picks filtered commits one by one
- Conflict Handling - If conflicts occur, saves state and pauses for manual resolution
- Finalization - Returns to dev branch and provides summary
The tool automatically manages checkpoints using git notes with random UUIDs. After each successful sync, a UUID is added as a note to the HEAD commit of both the target branch and the working branch. This UUID serves as a reference point for the next sync.
How it works:
-
After a sync completes: A random UUID is automatically added as a git note (in the
checkpointsnamespace) to:- The HEAD commit on the target branch (e.g., master)
- The HEAD commit on the working branch (e.g., dev)
-
When starting a new sync: The tool:
- Reads the UUID from the target branch's HEAD commit
- Searches the working branch for the commit with the same UUID
- Uses that commit as the starting point for the sync
- Only syncs commits that come after the checkpoint
First-time usage (no checkpoint exists):
Use the --force-checkpoint flag to start syncing from the very first commit:
git-sift --target main --force-checkpointAfter the first sync completes, checkpoints are automatically managed for all future syncs.
Viewing checkpoints:
To see the checkpoint UUID on any commit:
git notes --ref=checkpoints show HEADWhen to use --force-checkpoint:
- First time using git-sift on a repository
- You want to re-sync the entire history
- You need to reset the checkpoint system
Install dependencies:
bun installRun tests:
bun testRun in dev mode:
bun run dev --helpWatch tests:
bun test --watchBuild for production:
bun run buildTest package before publishing:
bun run publish:dryPublish to npm:
bun publishThe build process uses Bun's bundler to create a single optimized JavaScript file that includes all dependencies. The output is configured to run with Node.js (via the #!/usr/bin/env node shebang), making it compatible with npx and standard npm workflows.
The project is organized into focused modules:
GitOperations- Wrapper around simple-git for all git operations (checkout, cherry-pick, commit lookup, git notes)StateManager- Persists operation state to.git/git-sift-state.jsonfor resume capabilityCheckpointSync- Main orchestrator that runs the workflow phasescli.ts- Command-line interface using Node's built-inparseArgs
- Git Notes for Checkpoints - Uses git's built-in notes feature rather than tags or commit messages
- Stateful Resume - Saves progress to allow conflict resolution without losing context
- Cherry-pick Strategy - Preserves original commit metadata and allows selective syncing
- Zero Config - Works with sensible defaults, customizable when needed
- Bun runtime (tested with v1.3.1+)
- Git repository
MIT