-
Notifications
You must be signed in to change notification settings - Fork 9
Import Repositories to Harness Code
This guide explains how developers and customers use the Harness CLI to migrate git repositories: including pull requests, comments, labels, webhooks, and branch rules, from any supported SCM into Harness Code.
- What gets migrated
- Supported source providers
- Prerequisites
- Install the CLI and migrate plugin
- Migration overview
- Step 1 — Authenticate to Harness (import only)
- Step 2 — Export from your SCM
- Step 3 — Map user emails (optional)
- Step 4 — Import into Harness Code
- Step 5 — Verify the migration
- Provider-specific reference
- Advanced scenarios
- Command quick reference
- Troubleshooting
A full migration can include:
| Entity | Export | Import |
|---|---|---|
| Git repository (all branches and tags) | ✓ | ✓ |
| Git LFS objects | ✓ (requires git-lfs) |
✓ |
| Repository visibility (public/private) | ✓ | ✓ |
| Pull requests / merge requests | ✓ | ✓ |
| PR comments and review comments | ✓ | ✓ |
| PR reviewers | ✓ | ✓ |
| Labels | ✓ | ✓ |
| Webhooks | ✓ | ✓ |
| Branch protection / branch rules | ✓ | ✓ |
Use --no-* flags on export or import to skip specific entity types (see Command quick reference).
| SCM | Export command |
--from identifies |
|---|---|---|
| GitHub (cloud or Enterprise) | migrate github_organization:scm_bundle |
GitHub organization |
| GitLab (cloud or self-managed) | migrate gitlab_group:scm_bundle |
GitLab group |
| Bitbucket Cloud | migrate bitbucket_workspace:scm_bundle |
Bitbucket workspace |
| Bitbucket Server (Stash) | migrate stash_project:scm_bundle |
Bitbucket Server project |
All exports write the same scm_bundle format. A single import command consumes bundles from any provider:
harness migrate scm_bundle:repository --from ./harness| Tool | Required for | Notes |
|---|---|---|
Harness CLI (harness) |
All steps | See Install the CLI and migrate plugin |
| migrate plugin | Export and import | Installed separately via harness install plugin migrate
|
git |
Export and import | Must be on PATH
|
git-lfs |
Repos with LFS objects | Must be on PATH; skip with --no-lfs if not needed |
Create a personal access token on your source SCM with read access to the org/group/workspace/project you are migrating. Minimum scopes vary by provider — see Token permissions.
You can pass tokens via command flags (--github-token, --gitlab-token, etc.) or environment variables where supported.
The export phase does not require a Harness login. The import phase requires a Harness API token (PAT or SAT) with permission to create repositories in the target org/project.
Export runs entirely against your source SCM. Import creates resources under the account/org/project in your active CLI profile.
harness install cliFor scripting/CI, see docs/manual-install.md.
harness install plugin migrateCheck availability without installing:
harness install plugin migrate --checkConfirm both are loaded:
harness list moduleYou should see code (builtin) and migrate (plugin, installed).
harness get module migrate
harness migrate --help
harness get noun scm_bundleMigration is deliberately two phases separated by a bundle on disk:
┌─────────────────┐ export ┌──────────────┐ import ┌─────────────────┐
│ Source SCM │ ──────────────► │ scm_bundle │ ──────────────► │ Harness Code │
│ (GitHub, etc.) │ (no Harness │ ./harness/ │ (Harness auth │ repositories │
│ │ login needed) │ harness.zip │ required) │ + PR metadata │
└─────────────────┘ └──────────────┘ └─────────────────┘
│
▼
update scm_bundle:users (optional)
rewrite emails in bundle
Why two steps?
- Org-scale exports can run for hours; checkpoints and
--resumelet you recover from interruptions. - The bundle can be inspected, backed up, or edited (e.g. user email mapping) before anything is created in Harness.
- Export can run on a machine with SCM access but no Harness credentials (e.g. inside your corporate network or on the SCM server itself).
Skip this step if you are only exporting. Come back here before running the import.
harness auth loginThe wizard prompts for API URL, token, org, and project.
harness auth login \
--api-url https://app.harness.io \
--api-token "$HARNESS_API_KEY" \
--org <org_id> \
--project <project_id>For self-hosted Harness, set --api-url to your instance URL.
Repositories are created under the profile's org/project:
harness auth setscope --org <org_id> --project <project_id>Or override per command:
harness migrate scm_bundle:repository --from ./harness --org <org_id> --project <project_id>harness auth status
harness list repositoryPick the command that matches your source provider. Each command:
- Reads repositories and metadata from the live SCM API
- Writes a local bundle to
./harnessby default (override with--to) - Produces
harness.zipinside the output folder - Supports
--resumeto continue an interrupted export - Supports
--repoto export a single repository instead of the whole org/group/project
harness migrate github_organization:scm_bundle \
--from <github_org> \
--github-token <token> \
--github-user <username> \
--to ./harnessGitHub Enterprise:
harness migrate github_organization:scm_bundle \
--from <github_org> \
--github-host https://github.example.com \
--github-token <token> \
--github-user <username>Single repository:
harness migrate github_organization:scm_bundle \
--from <github_org> \
--repo <repo_name> \
--github-token <token> \
--github-user <username>harness migrate gitlab_group:scm_bundle \
--from <group_path> \
--gitlab-token <token> \
--gitlab-user <username> \
--to ./harnessSelf-managed GitLab:
harness migrate gitlab_group:scm_bundle \
--from <group_path> \
--gitlab-host https://gitlab.example.com \
--gitlab-token <token> \
--gitlab-user <username>Include nested subgroups:
harness migrate gitlab_group:scm_bundle \
--from <group_path> \
--include-subgroups \
--gitlab-token <token> \
--gitlab-user <username>Single project:
harness migrate gitlab_group:scm_bundle \
--from <group_path> \
--repo <project_name> \
--gitlab-token <token> \
--gitlab-user <username>harness migrate bitbucket_workspace:scm_bundle \
--from <workspace> \
--bitbucket-token <token> \
--to ./harnessSingle repository:
harness migrate bitbucket_workspace:scm_bundle \
--from <workspace> \
--repo <repo_slug> \
--bitbucket-token <token>harness migrate stash_project:scm_bundle \
--from <project_key> \
--stash-host https://bitbucket.example.com \
--stash-token <token> \
--stash-user <username> \
--to ./harnessSingle repository:
harness migrate stash_project:scm_bundle \
--from <project_key> \
--repo <repo_slug> \
--stash-host https://bitbucket.example.com \
--stash-token <token> \
--stash-user <username>If export stops partway through, re-run the same command with --resume:
harness migrate github_organization:scm_bundle \
--from <github_org> \
--github-token <token> \
--github-user <username> \
--to ./harness \
--resumeAfter a successful export, expect:
./harness/
harness.zip # bundle ready for import
... # checkpoint and intermediate files
No Harness resources are created during export.
Harness matches PR authors, commenters, reviewers, and branch-rule members by email. If a source SCM email does not match any Harness user, the import stops.
Fix this before import by rewriting emails in the bundle:
users.json:
{
"alice@old-company.com": "alice@new-company.com",
"bob@github-noreply.com": "bob@harness.example.com"
}harness update scm_bundle:users ./harness --user-mapping users.jsonThis rewrites the bundle in place (the original is replaced only after success).
Alternatively, skip strict user matching at import time (see Import with unmapped users).
With Harness auth configured and the bundle ready:
harness migrate scm_bundle:repository --from ./harnessYou can pass the folder (./harness) or the zip directly (./harness/harness.zip).
harness migrate scm_bundle:repository \
--from ./harness \
--repo <org_or_group>/<repo_name>Raise the temporary git push size limit (default 100 MB):
harness migrate scm_bundle:repository \
--from ./harness \
--file-size-limit 500000000Lower the PR batch size:
harness migrate scm_bundle:repository \
--from ./harness \
--batch-size 50To continue when some emails have no Harness match (PRs/comments/rules are attributed to your profile's account):
harness migrate scm_bundle:repository \
--from ./harness \
--skip-usersUse a service account profile when using --skip-users, so unmapped history is not credited to a personal account.
Commit authors always come from git objects in the bundle, not from the importing identity.
Org-scale imports can take hours. Disable the timeout:
harness migrate scm_bundle:repository --from ./harness --timeout 0Use the built-in code module to confirm repositories and metadata landed correctly.
harness list repository
harness list repository --org <org_id> --project <project_id>harness get repository <repo_id>harness list branch <repo_id>
harness list tag <repo_id>
harness list commit <repo_id> --branch mainharness list pr <repo_id>
harness list pr <repo_id> --state open
harness get pr <repo_id>/<pr_number>harness list pr_activity <repo_id>/<pr_number>
harness list pr_comment <repo_id>/<pr_number>
harness list pr_check <repo_id>/<pr_number>harness list repo_label <repo_id>
harness list pr_label <repo_id>/<pr_number>| Provider | Token type | Minimum permissions / scopes |
|---|---|---|
| GitHub | Classic PAT |
repo, read:org
|
| GitHub Enterprise | Same as above | Admin/write on repos recommended for full metadata export |
| GitLab | PAT |
read_api, read_repository
|
| Bitbucket Cloud | App password / PAT | Repository Read |
| Bitbucket Server | HTTP access token | Repository Read on target project |
| Harness (import) | PAT or SAT | Permission to create Code repositories in target org/project |
For GitHub, admin/write access on repositories helps export webhooks, branch rules, and full PR metadata.
| Provider | Flag | Example |
|---|---|---|
| GitHub Enterprise | --github-host |
https://github.example.com |
| Self-managed GitLab | --gitlab-host |
https://gitlab.example.com |
| Bitbucket Server | --stash-host |
https://bitbucket.example.com |
Bitbucket Cloud uses the public API by default; --bitbucket-host is available for non-standard endpoints.
When a repository already exists in Harness Code and you only need additional PR metadata:
harness migrate scm_bundle:repository \
--from ./harness \
--no-gitThis skips git push operations and imports only pull requests, comments, and related metadata. The CLI calculates PR number offsets to avoid conflicts with existing PRs.
Requirements:
- Target repository must already exist in Harness Code
- You must have write permission on the target repository
Useful for a first pass or when PR export is slow:
harness migrate github_organization:scm_bundle \
--from <org> \
--no-pr \
--github-token <token> \
--github-user <username>Then run a second export with --no-git on import, or export PRs separately.
| Flag | Effect |
|---|---|
--no-pr |
Skip pull requests |
--no-pr-metadata |
Skip PR comments and reviewers (keep PR shells) |
--no-comment |
Skip PR comments |
--no-label |
Skip labels |
--no-webhook |
Skip webhooks |
--no-rule |
Skip branch protection rules |
--no-lfs |
Skip LFS objects |
These flags work on both export (<provider>:scm_bundle) and import (scm_bundle:repository) commands.
For repos behind a firewall, run export on a host that has direct SCM access (your laptop on VPN, a jump box, or the SCM server itself). Copy the ./harness folder to a machine with Harness access for import:
# On SCM-accessible host — export only, no Harness credentials needed
harness migrate github_organization:scm_bundle \
--from <org> \
--github-token <token> \
--github-user <username> \
--to /data/migration-bundle
# Copy /data/migration-bundle to import host, then:
harness migrate scm_bundle:repository --from /data/migration-bundleTypical pipeline stages:
-
Export job — SCM credentials only; artifact the
./harnessfolder -
Optional transform job —
harness update scm_bundle:users -
Import job — Harness credentials;
harness migrate scm_bundle:repository
Use non-interactive auth:
export HARNESS_API_KEY=<token>
export HARNESS_ACCOUNT=<account_id>
export HARNESS_ORG=<org_id>
export HARNESS_PROJECT=<project_id>
harness migrate scm_bundle:repository --from ./harness --timeout 0See docs/auth.md for env-var mode details.
# GitHub
harness migrate github_organization:scm_bundle --from <org> [--to <folder>] [--repo <name>] [--resume] \
[--github-token <token>] [--github-user <user>] [--github-host <url>]
# GitLab
harness migrate gitlab_group:scm_bundle --from <group> [--to <folder>] [--repo <name>] [--resume] [--include-subgroups] \
[--gitlab-token <token>] [--gitlab-user <user>] [--gitlab-host <url>]
# Bitbucket Cloud
harness migrate bitbucket_workspace:scm_bundle --from <workspace> [--to <folder>] [--repo <name>] [--resume] \
[--bitbucket-token <token>] [--bitbucket-host <url>]
# Bitbucket Server
harness migrate stash_project:scm_bundle --from <project> [--to <folder>] [--repo <name>] [--resume] \
[--stash-host <url>] [--stash-token <token>] [--stash-user <user>]harness update scm_bundle:users <folder-or-zip> --user-mapping users.jsonharness migrate scm_bundle:repository --from <folder-or-zip> [--repo <org/repo>] \
[--skip-users] [--no-git] [--batch-size <n>] [--file-size-limit <bytes>] [--timeout 0]harness list repository
harness get repository <repo_id>
harness list pr <repo_id>
harness list branch <repo_id>
harness list repo_label <repo_id>harness install cli
harness install plugin migrate
harness auth login
harness auth setscope --org <org> --project <project>
harness get module migrateThe migrate plugin is not installed:
harness install plugin migrate
harness list module # migrate should show Installed: yesOption A — map emails before import:
harness update scm_bundle:users ./harness --user-mapping users.json
harness migrate scm_bundle:repository --from ./harnessOption B — skip strict matching (attributes unmapped users to your profile):
harness migrate scm_bundle:repository --from ./harness --skip-usersRe-run with --resume and the same --to folder:
harness migrate github_organization:scm_bundle --from <org> --to ./harness --resume ...Reduce batch size:
harness migrate scm_bundle:repository --from ./harness --batch-size 25Increase file size limit:
harness migrate scm_bundle:repository --from ./harness --file-size-limit 1000000000Ensure git-lfs is installed on both export and import hosts. Do not pass --no-lfs unless you intentionally skip LFS.
Import scope comes from the active profile or --org/--project flags. Set scope before import:
harness auth setscope --org <org> --project <project>
harness migrate scm_bundle:repository --from ./harnessharness migrate <provider>:scm_bundle --help
harness migrate scm_bundle:repository --help
harness update scm_bundle:users --help- Harness CLI auth — profiles, env vars, scope overrides
- Command Reference — Code module — post-migration repo/PR operations
- Plugins — how plugin modules work
-
Verbs — migrate — grammar of the
migratepair verb - harness-migrate engine — underlying open-source migrator
- Harness Developer Hub — Import repositories — UI import and migration overview