repoclone
Clone, update and report on every repository in a GitLab group or GitHub organisation — in parallel, safely, and repeatably.
Point it at a namespace and it mirrors the whole tree onto your laptop. Run it again next week and it fast-forwards what changed, leaves your local work alone, and tells you what happened.
$ repoclone clone platform/tooling --token glpat-xxxx
gitlab platform/tooling → /Users/you/src/work
cloned platform/tooling/api
cloned platform/tooling/cli
updated platform/tooling/docs
skipped platform/tooling/legacy (local changes, left alone)
42 repositories: 30 cloned, 9 updated, 2 unchanged, 1 skipped
Install
Requires Python 3.11+ and git on your PATH.
# recommended — isolated install, command on your PATH
uv tool install repoclone-cli
# or with pipx
pipx install repoclone-cli
# or plain pip
pip install repoclone-cli
# or straight from the repository, before a release is cut
uv tool install git+https://github.com/devops-monk/repoclone-cli
The package is published as repoclone-cli and installs a command called repoclone — the
name repoclone was already taken on PyPI by an unrelated project.
Check everything is wired up:
repoclone check --token glpat-xxxx
New to it? Follow the step-by-step first test — the first four steps cannot touch your disk.
Quick start
# GitLab group (path or numeric id), token on the command line
repoclone clone platform/tooling --token glpat-xxxx --dest ~/src/work
# GitHub organisation
repoclone clone kubernetes --provider github --token ghp_xxxx --dest ~/src/oss
# see what would happen first
repoclone clone platform/tooling --token glpat-xxxx --dry-run
Run the same command again tomorrow. Existing clones are pulled up to date, anything new is cloned, and repositories with uncommitted work are left alone. That is the default; --sync says it explicitly if you prefer it spelled out in a script:
repoclone clone platform/tooling --token glpat-xxxx --sync
Private / self-hosted instances
Pass your own domain with --host — this is the common case for company GitLab and GitHub Enterprise, and everything else works identically:
# self-hosted GitLab on your own domain
repoclone clone platform/tooling \
--host https://git.tech.example.com \
--token glpat-xxxx \
--dest ~/src/work
# GitHub Enterprise Server
repoclone clone platform \
--provider github \
--host https://github.example.com \
--token ghp_xxxx \
--dest ~/src/work
# internal host with a self-signed certificate
repoclone clone platform/tooling --host https://git.internal.example.com --token $T --insecure
The API path is derived for you (/api/v4 for GitLab, /api/v3 for GitHub Enterprise) — give it the plain domain. Without --host the public gitlab.com / github.com is used. Put the host in a profile once and you never type it again.
Re-running is the normal case: repositories already on disk are fast-forwarded, new ones are cloned, and anything you have edited locally is left untouched.
Commands
| Command | What it does |
|---|---|
repoclone clone [NAMESPACE]... |
Clone new repositories, update existing ones |
repoclone list [NAMESPACE]... |
List what would be cloned — no disk changes |
repoclone report |
Branch, last commit, dirty state of clones already on disk |
repoclone check |
Verify git, config and token before a large run |
repoclone config init |
Write a starter configuration file |
repoclone config show |
Show the settings that would apply, and from where |
Every command supports -h/--help. Passing an unknown option or a bad value prints the full help for that command, so you never have to guess.
Common options
| Option | Default | Notes |
|---|---|---|
--token TEXT |
— | Read-only token. read_api (GitLab) or repo:read (GitHub) |
--provider [gitlab|github] |
gitlab |
Which forge to talk to |
--host URL |
public forge | Self-hosted GitLab or GitHub Enterprise |
--dest, -d PATH |
current directory | Where the clones go |
--protocol [ssh|https] |
ssh |
https uses the token for auth |
--sync |
on | Pull existing clones, clone new ones. Explicit form of the default |
--update, -u [pull|fetch|skip] |
pull |
What to do with existing clones |
--jobs, -j N |
8 |
Parallel git operations |
--include / --exclude REGEX |
— | Filter by repository path. Repeatable |
--depth N |
full | Shallow clone |
--blobless |
off | Partial clone: full history, file contents on demand |
--flat / --nested |
nested | Flatten a/b/c into one directory level |
--archived / --forks |
excluded | Include archived repositories or forks |
--timeout N |
30 |
Seconds allowed per git operation. Raise it for large repositories |
--dry-run |
off | Show the plan, change nothing |
--prune |
off | Report local clones that no longer exist remotely |
--insecure |
off | Skip TLS verification (self-signed internal forge) |
Useful combinations
# a fast, space-efficient mirror for code search
repoclone clone platform --token $T --blobless -j 16
# only the services, skipping anything archived-looking
repoclone clone platform --token $T --include '/services/' --exclude '^platform/old-'
# refresh remote refs without ever touching working trees
repoclone clone platform --token $T --update fetch
# what have I got locally that is going stale?
repoclone report --stale-days 180 --output table
# daily refresh: pull everything, clone anything new
repoclone clone platform --token $T --sync
# include forks and archived repositories in a full audit
repoclone clone platform --token $T --forks --archived
# fail fast on a stuck repository instead of waiting ten minutes
repoclone clone platform --token $T --timeout 120
Tokens
Pass the token on the command line with --token, which is what most people do:
repoclone clone platform/tooling --token glpat-xxxx
If you would rather not have it in shell history, any of these also work — in this order of precedence:
--tokenREPOCLONE_TOKENtoken_envin your profile (points at a variable of your choosing)GITLAB_TOKEN/GITHUB_TOKENtoken_cmdin your profile, e.g.op read op://Private/GitLab/token
A read-only scope is enough. Over https, the token is used for the clone and then stripped from the saved remote, so it never lands in .git/config. Tokens are redacted from error output.
Configuration profiles
Typing the same flags every day gets old. repoclone config init writes ~/.config/repoclone/config.toml:
default_profile = "work"
[profiles.work]
provider = "gitlab"
host = "https://gitlab.example.com"
namespaces = ["platform/tooling"]
dest = "~/src/work"
protocol = "ssh"
concurrency = 8
token_env = "GITLAB_TOKEN"
[profiles.oss]
provider = "github"
namespaces = ["kubernetes"]
dest = "~/src/oss"
protocol = "https"
Then:
repoclone clone # uses the default profile
repoclone clone --profile oss # uses the oss profile
repoclone config show # what would apply right now
Precedence: command-line flags → environment variables → profile → built-in defaults. A flag you do not pass never overwrites your profile.
How it decides what to do
| Situation | What happens |
|---|---|
| Directory missing | Clone it |
| Clone exists, clean, behind | Fast-forward |
| Clone exists, clean, up to date | Nothing (unchanged) |
| Clone exists, uncommitted changes | Fetch only, working tree untouched |
| Clone exists, diverged | Fetch only, reported as needing a manual merge |
| Directory exists but is not a repository | Skipped, never overwritten |
| Repository is empty | Skipped |
repoclone never runs git merge unless it can fast-forward, never force-updates, and never deletes anything. --prune only reports orphans — removing them stays your decision.
Output formats
list and report accept --output table|json|csv, so results pipe into other tools:
repoclone list platform --token $T --output json | jq -r '.[].repository'
repoclone report --output csv > inventory.csv
Exit codes
| Code | Meaning |
|---|---|
0 |
Success |
1 |
At least one repository failed, or configuration was invalid |
2 |
Bad command-line usage (help is printed) |
Documentation
- docs/usage.md — task-by-task guide with worked examples
- Step-by-step first test — start here if you have just installed it
- docs/architecture.md — how the pieces fit together
- CONTRIBUTING.md — how to add a command, a provider or an output format
- docs/releasing.md — publishing to PyPI, versioning, trusted publishing
Licence
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 repoclone_cli-0.1.0.tar.gz.
File metadata
- Download URL: repoclone_cli-0.1.0.tar.gz
- Upload date:
- Size: 103.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
579e8709fa811a818cbde6badc90fb87caa3781966939d888c6bcd94fb002176
|
|
| MD5 |
594cc593b9da6f4b3fa1a9db9fbedf55
|
|
| BLAKE2b-256 |
077f73a3c4400512814d4ceebef843db005d680dbfc9be1e5f5b335072c8a39d
|
File details
Details for the file repoclone_cli-0.1.0-py3-none-any.whl.
File metadata
- Download URL: repoclone_cli-0.1.0-py3-none-any.whl
- Upload date:
- Size: 41.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d1ccc5af73aeba68e5913acf67c82b06726abe9552ba44fcbb5b09e125f11bd6
|
|
| MD5 |
efd2c41896f9e55dbe05f84002e861d0
|
|
| BLAKE2b-256 |
464c7389f50803c854d3831c82bcfcf1a4181245f550d5fc009669cd2c5460bf
|