Skip to main content

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:

  1. --token
  2. REPOCLONE_TOKEN
  3. token_env in your profile (points at a variable of your choosing)
  4. GITLAB_TOKEN / GITHUB_TOKEN
  5. token_cmd in 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

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

repoclone_cli-0.1.0.tar.gz (103.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

repoclone_cli-0.1.0-py3-none-any.whl (41.2 kB view details)

Uploaded Python 3

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

Hashes for repoclone_cli-0.1.0.tar.gz
Algorithm Hash digest
SHA256 579e8709fa811a818cbde6badc90fb87caa3781966939d888c6bcd94fb002176
MD5 594cc593b9da6f4b3fa1a9db9fbedf55
BLAKE2b-256 077f73a3c4400512814d4ceebef843db005d680dbfc9be1e5f5b335072c8a39d

See more details on using hashes here.

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

Hashes for repoclone_cli-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d1ccc5af73aeba68e5913acf67c82b06726abe9552ba44fcbb5b09e125f11bd6
MD5 efd2c41896f9e55dbe05f84002e861d0
BLAKE2b-256 464c7389f50803c854d3831c82bcfcf1a4181245f550d5fc009669cd2c5460bf

See more details on using hashes here.

Release history Release notifications | RSS feed

0.1.1

2 files

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page