Automatically synchronize Git worktrees with remote branches - perfect for multi-branch development workflows
npm install sync-worktreesAutomatically synchronize Git worktrees with remote branches. Keep your local worktrees in sync with remote repositories - perfect for multi-branch development workflows and automated testing setups.
sync-worktrees maintains a separate working directory for each remote branch, all sharing the same Git repository:
1. First run: Clones your repository as a bare repository (no working files, just Git data)
2. Automatic sync:
- Creates a dedicated worktree for every remote branch (main, develop, feature/*, etc.)
- Each branch gets its own isolated directory with a full working copy
- Fetches latest changes (doesn't merge - preserves your local work)
- Removes worktrees when remote branches are deleted (preserves local changes)
Why this matters: Switch between branches instantly without stashing, run tests on multiple branches simultaneously, or keep your CI and production branches always ready.
- š Automatically creates worktrees for all remote branches
- šļø Removes worktrees for deleted remote branches (preserves local changes)
- ā° Run as a scheduled cron job or one-time execution
- š”ļø Safe operations - won't delete worktrees with uncommitted changes or unpushed commits
- š Clear logging with timestamps and progress indicators
- š Config file support for managing multiple repositories
- š Automatic retry with exponential backoff for network and filesystem errors
- š Branch age filtering - only sync branches active within a specified time period
- š Smart handling of rebased/force-pushed branches with automatic divergence detection
``bash`
npm install -g sync-worktrees
Or with pnpm:
`bash`
pnpm add -g sync-worktrees
When running without all required arguments, sync-worktrees will prompt you interactively:
`bashInteractive setup - prompts for missing values
sync-worktrees
$3
`bash
Single repository (one-time sync)
sync-worktrees --repoUrl https://github.com/user/repo.git --worktreeDir ./worktrees --runOnceSingle repository (scheduled hourly)
sync-worktrees --repoUrl https://github.com/user/repo.git --worktreeDir ./worktreesMultiple repositories (using config file)
sync-worktrees --config ./sync-worktrees.config.js
`Options
| Option | Alias | Description | Required | Default |
|--------|-------|-------------|----------|---------|
|
--config | -c | Path to JavaScript config file | No | - |
| --filter | -f | Filter repositories by name (wildcards supported) | No | - |
| --list | -l | List configured repositories and exit | No | false |
| --repoUrl | -u | Git repository URL (HTTPS or SSH) | Yes* | - |
| --bareRepoDir | -b | Directory for bare repository | No | .bare/ |
| --worktreeDir | -w | Directory for storing worktrees | Yes* | - |
| --cronSchedule | -s | Cron pattern for scheduling | No | 0 (hourly) |
| --runOnce | - | Execute once and exit | No | false |
| --branchMaxAge | -a | Maximum age of branches to sync (e.g., '30d', '6m', '1y') | No | - |
| --skip-lfs | - | Skip Git LFS downloads when fetching and creating worktrees | No | false |
| --no-update-existing | - | Disable automatic updates of existing worktrees | No | false |
| --help | -h | Show help | No | - |\* Required when not using a config file
Examples
$3
`bash
One-time sync
sync-worktrees -u https://github.com/user/repo.git -w ./worktrees --runOnceScheduled sync (every 30 minutes)
sync-worktrees -u git@github.com:user/repo.git -w ./worktrees -s "/30 *"Only sync branches active in the last 30 days
sync-worktrees -u https://github.com/user/repo.git -w ./worktrees --branchMaxAge 30dSync branches active in the last 6 months, check every hour
sync-worktrees -u git@github.com:user/repo.git -w ./worktrees --branchMaxAge 6mDisable automatic updates of existing worktrees
sync-worktrees -u https://github.com/user/repo.git -w ./worktrees --no-update-existing
`$3
`bash
Sync all repositories
sync-worktrees --config ./sync-worktrees.config.jsFilter specific repositories
sync-worktrees --config ./sync-worktrees.config.js --filter "frontend-*"List configured repositories
sync-worktrees --config ./sync-worktrees.config.js --list
`Configuration File
For managing multiple repositories, create a JavaScript ES module config file:
`javascript
export default {
// Optional defaults for all repositories
defaults: {
cronSchedule: "0 ", // Hourly
runOnce: false,
branchMaxAge: "30d", // Only sync branches active in last 30 days
updateExistingWorktrees: true // Auto-update worktrees that are behind (default: true)
}, // Retry configuration (optional - these are the defaults)
retry: {
maxAttempts: 'unlimited', // or a number like 5
initialDelayMs: 1000, // Start with 1 second
maxDelayMs: 600000, // Max 10 minutes between retries
backoffMultiplier: 2 // Double the delay each time
},
repositories: [
{
name: "frontend", // Unique identifier
repoUrl: "https://github.com/company/frontend.git",
worktreeDir: "./worktrees/frontend", // Relative paths supported
cronSchedule: "/30 *" // Override default
},
{
name: "backend",
repoUrl: process.env.BACKEND_REPO_URL, // Environment variables supported
worktreeDir: "/absolute/path/backend-worktrees",
branchMaxAge: "6m", // Override: only sync branches active in last 6 months
// Uses default schedule
retry: { maxAttempts: 10 } // Override retry for this repo
}
]
};
`Notes:
- Relative paths are resolved from the config file location
-
bareRepoDir defaults to .bare/ if not specified
- Repository-specific settings override defaults$3
The tool automatically retries on network errors and filesystem race conditions:
- Default behavior: Unlimited retries with exponential backoff (1s, 2s, 4s... up to 10 minutes)
- Network errors: Connection timeouts, DNS failures, repository access issues
- Filesystem errors: Busy files, permission issues, race conditions
Simple retry examples:
`javascript
// Global retry configuration
retry: { maxAttempts: 5 } // Try 5 times then stop
retry: { maxAttempts: 'unlimited' } // Keep trying forever (default)
retry: { maxDelayMs: 60000 } // Cap retry delay at 1 minute
retry: { initialDelayMs: 5000 } // Start with 5 second delay// Per-repository override
repositories: [{
name: "critical-repo",
// ... other config ...
retry: { maxAttempts: 'unlimited', initialDelayMs: 10000 }
}]
`$3
For repositories with Git LFS issues or when large files aren't needed:
`bash
Skip LFS downloads
sync-worktrees -u https://github.com/user/repo.git -w ./worktrees --skip-lfsOr in config file
defaults: {
skipLfs: true
}
`The tool automatically handles LFS errors by retrying with LFS disabled (max 2 retries by default, configurable via
retry.maxLfsRetries).$3
To reduce clutter and save disk space, you can configure sync-worktrees to only sync branches that have been active within a specified time period. This is particularly useful for repositories with many stale or abandoned branches.
Duration format:
- h - hours (e.g., 24h)
- d - days (e.g., 30d)
- w - weeks (e.g., 4w)
- m - months (e.g., 6m)
- y - years (e.g., 1y)Examples:
`bash
Command line
sync-worktrees -u https://github.com/user/repo.git -w ./worktrees --branchMaxAge 30dConfig file - global default
defaults: {
branchMaxAge: "90d" // Only sync branches active in last 90 days
}Config file - per repository
repositories: [{
name: "active-project",
branchMaxAge: "14d", // Very active project - only last 2 weeks
}, {
name: "legacy-project",
branchMaxAge: "1y", // Legacy project - keep branches from last year
}]
`When branch filtering is active, the tool will:
- Fetch commit timestamps for all remote branches
- Filter out branches older than the specified age
- Log how many branches were excluded
- Only create/maintain worktrees for active branches
$3
sync-worktrees intelligently handles branches that have been rebased or force-pushed to prevent data loss:
Automatic behavior (no configuration needed):
1. Clean rebases - When a branch is rebased but the file content remains identical:
- Automatically resets the worktree to match the upstream
- No data loss since the content is the same
2. Diverged branches with NO local changes - When someone force-pushes but you haven't made local commits:
- Automatically resets to the new upstream state
- No move to
.diverged since you have no work to preserve
- Keeps .diverged clean by only preserving actual user work3. Diverged branches WITH local changes - When a branch has different content AND you've made local commits:
- Moves the worktree to
.diverged directory within your worktrees folder
- Preserves all your local changes and commits
- Creates a fresh worktree from the upstream branch
- Logs clear instructions for reviewing diverged changesExample diverged structure:
`
my-repo-worktrees/
āāā main/
āāā feature-a/
āāā feature-b/
āāā .diverged/ # Hidden diverged directory
āāā 2024-01-15-feature-x/ # Timestamp + branch name
ā āāā .diverged-info.json # Metadata about the divergence
ā āāā [all your local files]
āāā 2024-01-16-feature-y/
āāā .diverged-info.json
āāā [all your local files]
`Reviewing diverged worktrees:
`bash
See what's different
cd my-repo-worktrees/.diverged/2024-01-15-feature-x
git diff origin/feature-xIf you want to keep your changes
git push --force-with-leaseIf you want to discard and use upstream
cd ../..
rm -rf .diverged/2024-01-15-feature-x
``This ensures you never lose work due to force pushes while keeping your worktrees in sync with upstream.
- Node.js >= 22.0.0
- Git
Contributions are welcome! Please feel free to submit a Pull Request.
MIT Ā© Yordan Kanchelov