A command-line Python tool to clone or update (pull) all GitLab repositories under one or more groups (including nested subgroups), organizing them into a mirror directory tree that matches each project’s namespace.
- Recursively discovers all projects in specified GitLab group(s) and their subgroups
- Dedupe projects appearing in multiple groups
- Clones each repo into
DEST// - If a target folder already exists and is a Git repo, performs a
git pullinstead of recloning - Supports both HTTPS and SSH clone URLs
- Handles GitLab API pagination transparently
- Python 3.6 or newer
gitCLI installed and on your PATH- Python package:
requests
- Clone or download this repository (or save the script)
- (Optional) Create and activate a virtual environment:
python3 -m venv venv source venv/bin/activate - Install dependencies:
pip install requests
python sbg.py \
--gitlab-url \
--token \
--group-ids [ …] \
--dest \
[--use-ssh]Arguments:
--gitlab-url
Base URL of your GitLab instance (e.g.https://gitlab.comorhttps://gitlab.example.com)--token
A Personal Access Token with at least theapiandread_repositoryscopes--group-ids
One or more group identifiers (either numeric IDs or full paths, e.g.123,my-group/subgroup)--dest
Root directory where repositories will be cloned/pulled (default: current directory.)--use-ssh
If set, clones/pulls via SSH (git@gitlab…) instead of HTTPS
Clone or update all projects under group 42 into ./repos via HTTPS:
python sbg.py \
--gitlab-url https://gitlab.com \
--token $GITLAB_TOKEN \
--group-ids 42 \
--dest ./reposClone or update projects from two groups by full path, using SSH:
python sbg.py \
--gitlab-url https://gitlab.example.com \
--token $GITLAB_TOKEN \
--group-ids team/backend team/frontend \
--dest ~/gitlab-mirror \
--use-ssh- Authentication & API Calls
Uses your PAT in thePrivate-Tokenheader to call the GitLab REST API (/api/v4/groups/:id/projectsand/api/v4/groups/:id/subgroups). - Pagination
Fetches up to 100 items per page and loops until no more pages remain. - Recursive Discovery
Maintains a stack of group IDs to visit. For each group it fetches direct projects and subgroups, pushes new subgroups onto the stack, and marks visited groups to prevent cycles. - Deduplication
Projects are keyed by their unique GitLab project ID so that if the same project appears under multiple parent groups (e.g. via subgroup membership), it's only processed once. - Namespace Folder Structure
Each project’snamespace.full_path(e.g.team/backend) is used to create a matching folder hierarchy under--dest. The repository is cloned or updated inDEST/team/backend/. - Clone vs. Pull
- If
target/.gitexists: runsgit -C target pull - Otherwise: runs
git clone target
- If
The script uses Python's standard logging module for all console output. This provides users with more control over verbosity and output destinations (e.g., logging to a file). By default, it logs messages at the INFO level and above to standard output.
sbg.py
├── parse_args() # CLI argument parsing
├── GitLabCloner
│ ├── _get() # GET with pagination
│ ├── list_projects()
│ ├── list_subgroups()
│ ├── gather_all_projects()
│ └── clone_or_pull()
└── main() # Coordinates fetching, dedupe, folder setup, clone/pull loop
- Authentication errors
– Ensure your token has the correct scopes (api,read_repository).
– Verify you’re using the right--gitlab-url. - Git command failures
– Check network connectivity to GitLab
– Ensuregitis on your PATH and supports the-Coption (Git ≥ 1.8.5)
This project is released under the MIT License. Feel free to copy, modify, and distribute!