repofleet
Clone, update and track a fleet of git repositories from a single declarative config.
One command (repofleet sync) gets any machine into the same state:
- repos in the list but not on disk are cloned,
- repos already on disk are fetched, switched to their default branch and pulled (local work is auto-stashed and restored),
- repos found on disk that are not in the list yet are updated and then added to the config, so the shared list grows organically.
It is generic: nothing is hard-coded to a team, org or git host. Any team can ship its own
repofleet.toml (or a plain repos.txt) and get the same workflow.
Documentation: full usage guide · publishing guide · changelog
Contents
Install · Quick start · Commands · Configuration · Output & exit codes · Authentication · Troubleshooting · Development
Install
pip install repofleet # from your package index
pipx install repofleet # isolated, always on PATH
pip install -e ".[dev]" # from a checkout of this folder
Private feed:
pip install repofleet --index-url https://pkgs.dev.azure.com/<org>/_packaging/<feed>/pypi/simple/
No install? Unzip the folder and use the bundled launcher:
python bootstrap.py sync
Requires Python 3.9+ and git on PATH. Verify with repofleet --version.
Quick start
You already have the repos checked out
cd "/path/to/your/workspace"
repofleet init --name "Backend Services" --match "svc.*"
repofleet sync
init scans the folder, records every git repo it finds and writes repofleet.toml plus an empty
repo-urls.txt you can paste more URLs into.
You have nothing yet (typical for a new joiner)
Drop the shared repofleet.toml in an empty folder and run:
repofleet sync
Everything is cloned into the workspace directory declared in the config
(directory = "...").
An example profile ships with the package at src/repofleet/profiles/example-workspace.toml -
copy it, swap in your own URLs, and run:
repofleet sync -c example-workspace.toml
You just want a list of URLs cloned somewhere
repofleet clone --repos-file repo-urls.txt --root ./workspace
Commands
| Command | What it does |
|---|---|
repofleet init |
Create a config from the repositories found on disk. |
repofleet list |
Show every tracked repo and whether it is present locally. |
repofleet status |
Per-repo branch + clean/dirty state, and what is missing. |
repofleet clone |
Clone only the repositories that are missing. |
repofleet update |
Fetch + pull every cloned repo (add --clone-missing to do both). |
repofleet sync |
Clone missing, update existing, adopt new local repos into the config. |
repofleet add <url...> |
Track new repositories (--clone to fetch them immediately). |
repofleet remove <name...> |
Stop tracking repositories (files on disk are left alone). |
Common options (available on every command):
-c, --config FILE config file to use (default: nearest repofleet.toml, then $REPOFLEET_CONFIG)
--root DIR where the repositories live / should be cloned
--repos-file FILE extra repo list, TOML or plain text (repeatable)
--repo URL extra repository straight from the command line (repeatable)
--only PATTERN restrict to matching repo names (glob)
--exclude PATTERN skip matching repo names (glob)
--remote NAME remote to use (default: origin)
-j, --jobs N run N repos in parallel
--dry-run print the plan without touching anything
-q, --quiet summary only
update / sync also accept --no-stash (skip dirty repos instead of stashing) and --no-prune.
sync accepts --no-adopt to disable writing newly discovered repos back to the config.
Examples:
repofleet update --only "svc.a*" -j 8
repofleet clone --repos-file repo-urls.txt --root ./workspace
repofleet sync --dry-run
repofleet add https://dev.azure.com/org/Project/_git/new.service --clone
Every flag is documented in the usage guide.
Configuration
repofleet.toml
[workspace]
name = "Backend Services"
# "auto": use this file's folder when it already holds repos, else use `directory`.
root = "auto"
directory = "Backend Services"
match = ["svc.*"] # which local folders may be auto-adopted
remote = "origin"
repos_file = "repo-urls.txt" # optional: extra repos listed one URL per line
[defaults]
stash = true # stash local changes before pulling, restore afterwards
prune = true # git fetch --prune
jobs = 4 # parallel workers
autoadopt = true # write newly discovered local repos back into this file
[[repos]]
name = "svc.api"
url = "https://github.com/org/svc.api.git"
# branch = "develop" # optional, defaults to the remote's default branch
name is optional - it is derived from the URL when omitted.
Plain text list (easiest to share)
# repo-urls.txt
https://github.com/org/svc.api.git
https://github.com/org/tooling.git
custom-folder-name = https://github.com/org/other.git
https://github.com/org/legacy.git release/2024 # pin a branch
Use it with --repos-file repo-urls.txt, or reference it from repos_file in the TOML.
repofleet init creates this file for you, pre-filled with commented-out examples.
Config lookup order
--config/-c$REPOFLEET_CONFIG- nearest
repofleet.toml/.repofleet.tomlwalking up from the current directory %APPDATA%\repofleet\repofleet.toml(Windows) or~/.config/repofleet/repofleet.toml
Where repos end up
--root wins; otherwise [workspace] root (relative to the config file); with the default
"auto" the config's own folder is used when it already contains repos, and
<config folder>/<directory> when it does not. repofleet status prints the resolved root.
Output & exit codes
config : C:\code\Backend Services\repofleet.toml
root : C:\code\Backend Services
[+] svc.api: updated
[x] svc.auth: failed
--------------------------------------------------
Sync summary
--------------------------------------------------
svc.api updated
- on main
svc.auth failed
- git pull failed: ...
--------------------------------------------------
2 repo(s): 1 failed, 1 updated
| Action | Meaning |
|---|---|
cloned |
Freshly cloned. |
updated |
Pulled new commits. |
up to date |
Already current. |
skipped |
Nothing to do (already cloned, dirty with --no-stash, or dry run). |
missing |
Listed but not cloned yet. |
failed |
Git reported an error; details follow. |
Exit codes: 0 success · 1 a repo failed / nothing selected · 2 config or environment error ·
130 interrupted.
Authentication
repofleet uses your existing git credentials and sets GIT_TERMINAL_PROMPT=0, so a batch run fails
fast instead of hanging on a password prompt. Configure Git Credential Manager, a credential
helper, or SSH keys once before the first run.
Troubleshooting
| Symptom | Fix |
|---|---|
'git' was not found on PATH |
Install git or open a fresh shell. |
repofleet: command not found |
Use python -m repofleet ..., or install with pipx. |
could not read Username |
Credentials are missing and prompts are disabled; clone one repo manually or use SSH. |
No repositories selected |
No config found — run repofleet init, or pass --repo / --repos-file. |
| Repos cloned somewhere unexpected | Check the root : header from repofleet status, or pass --root. |
stash pop failed - stash kept |
Resolve the conflict in that repo, then git stash pop. |
More cases in the usage guide.
Safety notes
- Git is always invoked with an argument list, never through a shell.
GIT_TERMINAL_PROMPT=0prevents a batch run from hanging on a credential prompt.- Any credentials embedded in a remote URL are masked in console output.
pull --ff-onlyis tried first; a merge pull is only attempted if fast-forward is impossible.- If
git stash popfails after a pull, the stash is kept and the repo is reported as failed - your work is never discarded. removeonly edits the config; it never deletes directories.
Development
pip install -e ".[dev]"
pytest -q
Tests spin up real local git repositories in a temp folder — no network needed. See the usage guide for the project layout and the publishing guide for cutting a release.
License
MIT.
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 repofleet-0.2.0.tar.gz.
File metadata
- Download URL: repofleet-0.2.0.tar.gz
- Upload date:
- Size: 33.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9046882b94b0cf012e3ac4d5c232607acbd11e91699306ae718230783a580207
|
|
| MD5 |
629b5d1adb6e67aea3e010e904bbfdd6
|
|
| BLAKE2b-256 |
5875b1599eea736aa0ae8066caab4d16af27d06e2f0e38b25023db6302f916ff
|
Provenance
The following attestation bundles were made for repofleet-0.2.0.tar.gz:
Publisher:
publish.yml on kavirayuni-dev/repofleet
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
repofleet-0.2.0.tar.gz -
Subject digest:
9046882b94b0cf012e3ac4d5c232607acbd11e91699306ae718230783a580207 - Sigstore transparency entry: 2581149028
- Sigstore integration time:
-
Permalink:
kavirayuni-dev/repofleet@c8f01afc51512127ec57512f71a031e9fe1b50f7 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/kavirayuni-dev
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c8f01afc51512127ec57512f71a031e9fe1b50f7 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file repofleet-0.2.0-py3-none-any.whl.
File metadata
- Download URL: repofleet-0.2.0-py3-none-any.whl
- Upload date:
- Size: 22.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1e18e0c2331cd095ca078c72be6656ff562c666e6947b2830ff81aa430d7f421
|
|
| MD5 |
337f3e815f0b6f673160535de98d0ada
|
|
| BLAKE2b-256 |
693918cf93f2587bea20b373a0a7d5271b2c2c5739bb6ebb9295466cb8753d4f
|
Provenance
The following attestation bundles were made for repofleet-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on kavirayuni-dev/repofleet
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
repofleet-0.2.0-py3-none-any.whl -
Subject digest:
1e18e0c2331cd095ca078c72be6656ff562c666e6947b2830ff81aa430d7f421 - Sigstore transparency entry: 2581149050
- Sigstore integration time:
-
Permalink:
kavirayuni-dev/repofleet@c8f01afc51512127ec57512f71a031e9fe1b50f7 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/kavirayuni-dev
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c8f01afc51512127ec57512f71a031e9fe1b50f7 -
Trigger Event:
workflow_dispatch
-
Statement type: