vcspull import¶
The vcspull import command bulk-imports repositories from remote hosting
services into your vcspull configuration. It connects to the service API,
fetches a list of repositories, and writes them to your config file in a single
step.
Supported services: GitHub, GitLab, Codeberg, Gitea, Forgejo, and AWS CodeCommit.
Command¶
Usage¶
usage: vcspull import [-h] {github,gh,gitlab,gl,codeberg,cb,gitea,forgejo,codecommit,cc,aws} ...
Choose a service subcommand for details:
vcspull import github — GitHub or GitHub Enterprise
vcspull import gitlab — GitLab (gitlab.com or self-hosted)
vcspull import codeberg — Codeberg
vcspull import gitea — Self-hosted Gitea instance
vcspull import forgejo — Self-hosted Forgejo instance
vcspull import codecommit — AWS CodeCommit
Basic usage¶
Import all repositories for a GitHub user into a workspace:
$ vcspull import github myuser --workspace ~/code/
→ Fetching repositories from GitHub...
✓ Found 12 repositories
+ project-a [Python]
+ project-b [Rust] ★42
+ dotfiles
... and 9 more
Import 12 repositories to ~/.vcspull.yaml? [y/N]: y
✓ Added 12 repositories to ~/.vcspull.yaml
Supported services¶
Service |
Aliases |
Self-hosted |
Auth env var(s) |
|---|---|---|---|
GitHub |
|
|
|
GitLab |
|
|
|
Codeberg |
|
No |
|
Gitea |
|
|
|
Forgejo |
|
|
|
CodeCommit |
|
N/A |
AWS CLI credentials |
For Gitea and Forgejo, --url is required because there is no default
instance.
Import modes¶
User mode (default)¶
Fetch all repositories owned by a user:
$ vcspull import gh myuser --workspace ~/code/
Organization mode¶
Fetch repositories belonging to an organization or group:
$ vcspull import gh my-org \
--mode org \
--workspace ~/code/
For GitLab, subgroups are supported with slash notation:
$ vcspull import gl my-group/sub-group \
--mode org \
--workspace ~/code/
Search mode¶
Search for repositories matching a query:
$ vcspull import gh django \
--mode search \
--workspace ~/code/ \
--min-stars 100
Filtering¶
Narrow results with filtering flags:
$ vcspull import gh myuser \
--workspace ~/code/ \
--language python
$ vcspull import gh myuser \
--workspace ~/code/ \
--topics cli,automation
$ vcspull import gh django \
--mode search \
--workspace ~/code/ \
--min-stars 50
Include archived or forked repositories (excluded by default):
$ vcspull import gh myuser \
--workspace ~/code/ \
--archived \
--forks
Limit the number of repositories fetched:
$ vcspull import gh myuser \
--workspace ~/code/ \
--limit 50
Note
Not all filters work with every service. For example, --language may not
return results for GitLab or CodeCommit because those APIs don’t expose
language metadata. vcspull warns when a filter is unlikely to work.
Output formats¶
Human-readable output (default):
$ vcspull import gh myuser --workspace ~/code/
JSON for automation:
$ vcspull import gh myuser \
--workspace ~/code/ \
--json
NDJSON for streaming:
$ vcspull import gh myuser \
--workspace ~/code/ \
--ndjson
Dry runs and confirmation¶
Preview what would be imported without writing to the config file:
$ vcspull import gh myuser \
--workspace ~/code/ \
--dry-run
Skip the confirmation prompt (useful for scripts):
$ vcspull import gh myuser \
--workspace ~/code/ \
--yes
Syncing existing entries¶
By default, repositories that already exist in your configuration are skipped — even if the remote URL has changed. This prevents accidental updates when re-importing from a service.
For example, suppose your team migrated from HTTPS to SSH. Without
--sync, the old HTTPS URLs stay in your config:
$ vcspull import gh myorg \
--mode org \
--workspace ~/code/
→ Fetching repositories from GitHub...
✓ Found 8 repositories
+ new-project [Python]
⊘ api-server (already in config)
⊘ web-frontend (already in config)
... and 5 more
Import 1 new repository to ~/.vcspull.yaml? [y/N]: y
✓ Added 1 repository to ~/.vcspull.yaml
! Skipped 7 existing repositories
Pass --sync to fully reconcile your config with the remote — update changed
URLs, and remove entries no longer on the remote:
$ vcspull import gh myorg \
--mode org \
--workspace ~/code/ \
--sync
--sync does three things:
Add new repositories (same as without
--sync)Update URLs for existing entries whose URL has changed
Prune entries that are no longer on the remote
When syncing, vcspull replaces only the repo URL. All other metadata is
preserved:
options(including pins)remotesshell_command_afterworktrees
For example, given this config before the import:
~/code/:
api-server:
repo: "git+https://github.com/myorg/api-server.git"
remotes:
upstream: "git+https://github.com/upstream/api-server.git"
shell_command_after:
- make setup
After vcspull import gh myorg --workspace ~/code/ --sync, the repo URL
is updated to SSH while remotes and shell_command_after are kept:
~/code/:
api-server:
repo: "[email protected]:myorg/api-server.git"
remotes:
upstream: "git+https://github.com/upstream/api-server.git"
shell_command_after:
- make setup
Provenance tracking¶
When --sync (or --prune) is used, vcspull tags each imported repo with
a metadata.imported_from field recording the import source:
~/code/:
api-server:
repo: "[email protected]:myorg/api-server.git"
metadata:
imported_from: "github:myorg"
The tag format is "{service}:{target}" — e.g. "github:myorg",
"gitlab:mygroup", "codeberg:myuser".
Provenance tags scope the prune step: only entries tagged with the same import source are candidates for removal. This means:
Manually added repos (no
metadata.imported_from) are never prunedRepos imported from a different source (e.g.
"github:other-org") are never pruned when syncing"github:myorg"
Pruning is config-only — cloned directories on disk are not deleted.
Pruning stale entries¶
To remove stale entries without updating URLs, use --prune:
$ vcspull import gh myorg \
--mode org \
--workspace ~/code/ \
--prune
Preview what would be pruned with --dry-run:
$ vcspull import gh myorg \
--mode org \
--workspace ~/code/ \
--prune \
--dry-run
--sync and --prune can be combined — --sync alone already includes
pruning, so --sync --prune behaves identically to --sync.
Flags |
Add new |
Update URLs |
Prune stale |
Prune untracked |
|---|---|---|---|---|
(none) |
yes |
no |
no |
no |
|
yes |
yes |
yes |
no |
|
yes |
no |
yes |
no |
|
yes |
yes |
yes |
no |
|
yes |
yes |
yes |
yes |
|
yes |
no |
yes |
yes |
Pruning untracked entries¶
Standard --sync / --prune only removes entries tagged with the current
import source. Manually added repos — entries without any
metadata.imported_from tag — are left untouched. To also remove these
“untracked” entries, add --prune-untracked:
$ vcspull import gh myorg \
--mode org \
--workspace ~/code/ \
--sync \
--prune-untracked
--prune-untracked requires --sync or --prune — it cannot be used alone.
Safety rails:
Pinned entries are always preserved (regardless of provenance)
Entries tagged from a different source (e.g.
"gitlab:other") are preserved — they are “tracked” by that other importOnly workspaces the import targets are scanned — entries in other workspaces are untouched
A confirmation prompt lists exactly what would be removed before proceeding (use
--yesto skip,--dry-runto preview)
Preview with dry-run:
$ vcspull import gh myorg \
--mode org \
--workspace ~/code/ \
--prune \
--prune-untracked \
--dry-run
Pin-aware behavior¶
Repositories protected by a pin are exempt from both URL updates and
pruning. The following configurations all prevent --sync from modifying
an entry:
options.pin: true— blocks all operationsoptions.pin.import: true— blocks import onlyoptions.allow_overwrite: false— shorthand forpin: {import: true}
Pinned repositories are skipped with an informational message showing the
pin_reason (if set):
$ vcspull import gh myorg \
--mode org \
--workspace ~/code/ \
--sync
→ Fetching repositories from GitHub...
✓ Found 8 repositories
↻ api-server (URL changed)
⊘ internal-fork (pinned to company mirror)
... and 6 more
Import 7 repositories to ~/.vcspull.yaml? [y/N]: y
✓ Updated 6 repositories in ~/.vcspull.yaml
! Skipped 1 pinned repository
For example, this entry cannot be updated or pruned regardless of --sync:
~/code/:
internal-fork:
repo: "git+ssh://[email protected]/team/internal-fork.git"
options:
pin:
import: true
pin_reason: "pinned to company mirror — update manually"
See Repository pinning for full pin configuration.
Configuration file selection¶
vcspull writes to ~/.vcspull.yaml by default. Override with -f/--file:
$ vcspull import gh myuser \
--workspace ~/code/ \
--file ~/configs/github.yaml
Protocol selection¶
SSH clone URLs are used by default. Switch to HTTPS with --https:
$ vcspull import gh myuser \
--workspace ~/code/ \
--https
Self-hosted instances¶
Point to a self-hosted GitHub Enterprise, GitLab, Gitea, or Forgejo instance
with --url:
$ vcspull import gitea myuser \
--workspace ~/code/ \
--url https://git.example.com
Authentication¶
vcspull reads API tokens from environment variables. Use --token to override.
Environment variables are preferred for security. See each service page for
details.
Service |
Env var(s) |
Token type |
Min scope / permissions |
|---|---|---|---|
GitHub |
|
PAT (classic or fine) |
None (public), |
GitLab |
|
PAT |
|
Codeberg |
|
API token |
None (public), any token (private) |
Gitea |
|
API token |
|
Forgejo |
|
API token |
|
CodeCommit |
AWS CLI credentials |
IAM access key |
|
After importing¶
Run
vcspull fmt --writeto normalize and sort the configuration (see vcspull fmt).Run
vcspull listto verify the imported entries (see vcspull list).Run
vcspull syncto clone the repositories (see vcspull sync).