Git repository synchronization tool for teams
Project description
repo-sync-kitty
Git repository synchronization tool for teams. Keep multiple git repositories in sync across machines using a declarative TOML manifest.
Features
- Manifest-driven: Define repositories in a single TOML file
- Safe pulls: Multiple precondition checks before pulling (dirty state, wrong branch, unpushed commits)
- Parallel operations: Configurable concurrency for faster syncs
- Multiple remotes: Support for GitHub, GitLab, Bitbucket, and custom git servers
- Rich output: Progress bars, colored status tables, and detailed summaries
Installation
Homebrew (macOS)
brew tap vladistan/gizmos
brew install repo-sync-kitty
PyPI
pip install repo-sync-kitty
From source
# Using uv (recommended)
cd tools/repo-sync-kitty
uv sync
uv run repo-sync-kitty --help
# Or install globally with pip
pip install .
Quick Start
- Initialize a manifest in your projects directory:
# Create an empty template
repo-sync-kitty init -o manifest.toml
# Or scan an existing directory for repos
repo-sync-kitty init --scan-dir ~/Projects -o manifest.toml
- Validate your manifest:
repo-sync-kitty check -m manifest.toml
- Sync your repositories:
# Dry run first
repo-sync-kitty sync -m manifest.toml --dry-run
# Actually sync
repo-sync-kitty sync -m manifest.toml
- Check status anytime:
repo-sync-kitty status -m manifest.toml
Commands
sync - Clone and update repositories
repo-sync-kitty sync [OPTIONS]
Options:
-m, --manifest PATH Path to manifest.toml
--fetch-only Only fetch, don't clone or pull
--clone-only Only clone missing repos
--pull Pull changes after fetch (if safe)
-n, --dry-run Show what would be done
-v, --verbose Show detailed output instead of progress bar
-j, --parallelism INT Override parallelism setting
Examples:
# Clone missing repos and fetch all
repo-sync-kitty sync -m manifest.toml
# Clone missing repos only
repo-sync-kitty sync -m manifest.toml --clone-only
# Fetch and pull (when safe)
repo-sync-kitty sync -m manifest.toml --pull
# Verbose output with 2 parallel workers
repo-sync-kitty sync -m manifest.toml -v -j 2
status - Show repository status
repo-sync-kitty status [OPTIONS]
Options:
-m, --manifest PATH Path to manifest.toml
-v, --verbose Show all repos (not just issues)
--show-deleted Include deleted repos in output
Output shows:
- Missing repos (not cloned)
- Dirty repos (uncommitted changes)
- Wrong branch
- Ahead/behind remote
- Detached HEAD state
check - Validate manifest
repo-sync-kitty check [OPTIONS]
Options:
-m, --manifest PATH Path to manifest.toml
Validates manifest syntax, checks that all referenced remotes exist, and displays a summary of configuration.
fix-detached - Fix detached HEAD states
repo-sync-kitty fix-detached [OPTIONS]
Options:
-m, --manifest PATH Path to manifest.toml
-n, --dry-run Show what would be done without making changes
-v, --verbose Show detailed output instead of progress bar
-j, --parallelism INT Number of parallel workers
Examples:
# Dry run to see what would be fixed
repo-sync-kitty fix-detached -m manifest.toml --dry-run
# Fix detached HEADs
repo-sync-kitty fix-detached -m manifest.toml
Safely fixes detached HEAD states left by git-repo. For each detached repo:
- Checks for loose files - aborts if there are untracked, modified, or staged files
- Finds branch at HEAD - checks if HEAD points to a branch tip
- Checks out the branch - only if HEAD is at a branch tip
This is safe for repos previously maintained by git-repo, which leaves repos in detached HEAD state pointing at branch tips.
init - Create new manifest
repo-sync-kitty init [OPTIONS]
Options:
-o, --output PATH Output path for manifest.toml (default: manifest.toml)
--scan-dir PATH Scan directory for existing repos
--scan-forge NAME Scan forge for repos (not yet implemented)
--org NAME Organization/user to scan on forge
-f, --force Overwrite existing manifest
Examples:
# Create empty template
repo-sync-kitty init
# Scan ~/Projects for git repos and generate manifest
repo-sync-kitty init --scan-dir ~/Projects -o my-manifest.toml
add - Add repository to manifest
repo-sync-kitty add REMOTE SLUG [OPTIONS]
Arguments:
REMOTE Remote name (must exist in manifest)
SLUG Repository slug (e.g., 'owner/repo')
Options:
-m, --manifest PATH Path to manifest.toml
-p, --path PATH Local path (defaults to repo name from slug)
-b, --branch NAME Branch to track
Examples:
# Add repo with default path
repo-sync-kitty add origin octocat/Hello-World -m manifest.toml
# Add repo with custom path and branch
repo-sync-kitty add origin owner/repo -p libs/myrepo -b develop
import-repo - Import from git-repo manifest
repo-sync-kitty import-repo SOURCE [OPTIONS]
Arguments:
SOURCE Path to git-repo manifest XML file (e.g., default.xml)
Options:
-o, --output PATH Output path for manifest.toml (default: manifest.toml)
-r, --root PATH Root path for repos (default: parent of source file)
-f, --force Overwrite existing manifest
Examples:
# Import from default.xml in current directory
repo-sync-kitty import-repo default.xml
# Import with custom output path
repo-sync-kitty import-repo ~/repo/default.xml -o my-manifest.toml
# Import and set custom root path
repo-sync-kitty import-repo default.xml -r ~/Projects/myteam -o manifest.toml
This command converts Google's git-repo manifest format (XML) to repo-sync-kitty's TOML format. The importer handles:
- Multiple remotes with different base URLs
- Default remote and branch settings
- Per-project remote and branch overrides
- Stripping
.gitsuffix from project names
scan - List repositories from forge
repo-sync-kitty scan FORGE [OPTIONS]
Arguments:
FORGE Forge to scan (github, gitlab)
Options:
--org, -o NAME Organization/user to scan (required)
--token, -t TOKEN API token for authentication
-m, --manifest PATH Path to manifest.toml (for comparison)
-r, --remote NAME Remote to use when adding repos
--missing Only show repos not already in manifest
--add Add scanned repos to manifest (requires -r)
Examples:
# Scan public GitHub repos
repo-sync-kitty scan github --org octocat
# Scan with token (required for private repos)
repo-sync-kitty scan github --org myorg --token $GH_TOKEN
# Compare against manifest (shows checkmarks for enrolled repos)
repo-sync-kitty scan github --org myorg -m manifest.toml
# Show only repos not in manifest
repo-sync-kitty scan github --org myorg -m manifest.toml --missing
# Add missing repos to manifest
repo-sync-kitty scan github --org myorg -m manifest.toml -r origin --add
Note: For GitHub, the token is checked in order: -t option → GH_TOKEN env var → GITHUB_TOKEN env var. A token is required to see private repositories.
Manifest Format
Basic Structure
[common]
root_path = "~/Projects/my-team"
branch = "main"
remote = "origin"
parallelism = 4
timeout = 300
[remotes]
origin = { base_url = "https://github.com/" }
gitlab = { base_url = "https://gitlab.com/", branch = "master" }
[projects]
"libs/repo1" = { slug = "owner/repo1" }
"libs/repo2" = { slug = "owner/repo2", branch = "develop" }
Common Section
| Field | Type | Default | Description |
|---|---|---|---|
root_path |
string | required | Base directory for all repos |
branch |
string | "main" |
Default branch to track |
remote |
string | required | Default remote name |
parallelism |
int | 4 |
Max concurrent operations |
timeout |
int | 300 |
Operation timeout in seconds |
Remotes Section
Supports two formats:
Compact (inline):
[remotes]
ghpub = { base_url = "https://github.com/" }
mine = { base_url = "ssh://git@github.com/myuser/", branch = "develop" }
Expanded (multi-line):
[remotes.mine]
base_url = "ssh://git@github.com/myuser/"
branch = "develop"
parallelism = 2
timeout = 600
| Field | Type | Default | Description |
|---|---|---|---|
base_url |
string | required | Base URL for clone URLs |
branch |
string | inherited | Default branch for this remote |
parallelism |
int | inherited | Max concurrent ops for this remote |
timeout |
int | inherited | Timeout for this remote's operations |
Projects Section
Compact:
[projects]
"libs/linkml" = { slug = "linkml/linkml", remote = "ghpub" }
"infra" = { slug = "infra", branch = "develop" }
Expanded:
[projects."libs/linkml"]
slug = "linkml/linkml"
remote = "ghpub"
branch = "main"
| Field | Type | Default | Description |
|---|---|---|---|
slug |
string | required | Repository slug (owner/repo) |
path |
string | key | Local path relative to root_path |
remote |
string | inherited | Remote to use |
branch |
string | inherited | Branch to track |
status |
string | "active" |
One of: active, archived, deleted |
Inheritance
Values inherit in this order: project → remote → common
branch: project.branch → remote.branch → common.branch
parallelism: remote.parallelism → common.parallelism
timeout: remote.timeout → common.timeout
URL Construction
Clone URLs are constructed as: {remote.base_url}{project.slug}.git
Examples:
https://github.com/+linkml/linkml→https://github.com/linkml/linkml.gitssh://git@github.com/myuser/+infra→ssh://git@github.com/myuser/infra.git
Safety Checks
Before pulling, repo-sync-kitty checks:
- Correct branch: Current branch matches expected
- Clean state: No uncommitted changes
- No staged files: Nothing in the index
- No in-progress operations: Not rebasing, merging, or cherry-picking
- Not detached: HEAD is attached to a branch
- Not ahead: No unpushed commits
If any check fails, the repo is skipped with a warning.
Environment Variables
| Variable | Description |
|---|---|
GH_TOKEN |
GitHub API token for scan command (preferred) |
GITHUB_TOKEN |
GitHub API token for scan command (fallback) |
GITLAB_TOKEN |
GitLab API token for scan command |
SENTRY_DSN |
Optional Sentry DSN for error tracking |
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | One or more operations failed |
| 2 | Invalid manifest or configuration |
Development
# Install with dev dependencies
uv sync --extra dev
# Run tests
uv run pytest
# Run with coverage
uv run pytest --cov=src --cov-report=term-missing
# Lint
uv run ruff check src/ tests/
# Type check
uv run mypy src/
Troubleshooting
"Not safe to pull" warnings
This means one of the safety checks failed. Run status -v to see details:
repo-sync-kitty status -m manifest.toml -v
Common causes:
- Uncommitted changes → commit or stash them
- Wrong branch → checkout the expected branch
- Unpushed commits → push your changes first
- In-progress rebase → complete or abort the rebase
"Remote not found" errors
The remote referenced in a project doesn't exist in the [remotes] section. Add it or fix the reference.
Slow syncs
Try increasing parallelism:
repo-sync-kitty sync -m manifest.toml -j 8
Or set it in your manifest:
[common]
parallelism = 8
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file repo_sync_kitty-0.2.1.tar.gz.
File metadata
- Download URL: repo_sync_kitty-0.2.1.tar.gz
- Upload date:
- Size: 179.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8c55c4c15753a7f74c785a4384629b2274caa2f484afda1b7a2e8d83ae0b2228
|
|
| MD5 |
3859dc20399069e76d8ff31aeeca0ff4
|
|
| BLAKE2b-256 |
d335f97374816232baae24d08a7554a1a3f650f6072a8647a4dc1434dae43aec
|
File details
Details for the file repo_sync_kitty-0.2.1-py3-none-any.whl.
File metadata
- Download URL: repo_sync_kitty-0.2.1-py3-none-any.whl
- Upload date:
- Size: 58.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
81bfd1cbb8a0572c00635dc92c8e772a197d8f00685fe3b12f18d1870649afce
|
|
| MD5 |
a68702ce80c9ab732348a1d197189ccb
|
|
| BLAKE2b-256 |
6e6931185075685d0ba3ed1efd10fb69c57e18331237f92877e21268e10d8710
|