GitHub → GitLab
Move repositories and collaboration history from GitHub to GitLab with explicit handling for identities, LFS, attachments, CI/CD, protections, integrations, secrets, package registries, cutover, and mirror-based rollback.
Should you make this move?
Both platforms have a case. Compare what you gain with what you give up before scheduling the cutover.
GitHub
- The largest developer ecosystem combines collaboration, automation, and public discovery
- Repository-centered workflows keep code, review, and automation close together
- Enterprise planning and governance can require several overlapping GitHub products
- Advanced governance and automation can become configuration-heavy
GitLab
- A comprehensive DevSecOps platform offers deep CI/CD and self-hosting options
- Repository-centered workflows keep code, review, and automation close together
- Breadth, upgrades, and administration can be heavy for smaller teams
- Advanced governance and automation can become configuration-heavy
GitLab: A comprehensive DevSecOps platform offers deep CI/CD and self-hosting options. This removes a major source-side concern: Enterprise planning and governance can require several overlapping GitHub products.
What you lose: The largest developer ecosystem combines collaboration, automation, and public discovery. What you inherit: Breadth, upgrades, and administration can be heavy for smaller teams.
Know the shape of the move.
This timeline assumes
- Profile: one GitHub organization with at most 100 repositories, 500 users, 30,000 comments per project, and no GitHub Enterprise Server.
- You control organization settings, GitLab top-level groups, DNS, CI credentials, registries, OAuth applications, and production deployment targets.
- Every repository receives a mirror backup and its wiki, LFS objects, releases, packages, secrets, environments, and integrations are inventoried separately.
- GitLab usernames and emails are mapped before import; unidentified authors are accepted only with an explicit attribution exception.
- Production deploys are frozen while the final Git mirror, CI switch, and webhook cutover occur.
What survives the move.
“Partial” and “manual” are not footnotes. They are work that must be scheduled and verified.
| Item | Outcome | Impact | What happens | Mitigation |
|---|---|---|---|---|
| Git branches, tags, commits, and full revision history | clean | critical | A mirror clone and push preserve Git refs and object history when all refs are included. | Back up with --mirror, run fsck, compare refs and commit tips, and include submodules separately. |
| Issues and pull requests | partial | critical | The GitLab importer moves issues and pull requests into GitLab concepts, but state, review semantics, references, and edge cases are not identical. | Pilot repositories with complex reviews, cross-links, draft PRs, and merged branches. |
| Comments and review comments | partial | high | Large projects with roughly 30,000 or more comments can exceed the recommended importer path; attribution also depends on user mapping. | Use GitLab's documented alternate method for large projects and reconcile counts by type. |
| Markdown attachments | partial | high | Attachments require the relevant importer option; otherwise links in migrated Markdown can break. | Enable attachment import, scan migrated Markdown URLs, and preserve a source archive. |
| Git LFS objects | partial | critical | Git refs can migrate while LFS binaries are missing unless explicitly fetched and transferred. | Fetch all LFS objects, compare object inventories, and download representative binaries. |
| Users and organization structure | manual | critical | The GitHub importer does not create GitLab groups from organizations and imports only supported direct collaborators; outside collaborators can be omitted. | Create groups, subgroups, users, and identity mappings first, then audit effective membership. |
| Branch protection and rulesets | partial | critical | Some branch protection can map, but GitHub rulesets, status-check requirements, bypass actors, and custom roles are not equivalent. | Rebuild protected branches, approval rules, push rules, CODEOWNERS, and merge checks from an approved policy matrix. |
| GitHub Actions workflows | manual | critical | Workflow YAML, runners, environments, approvals, OIDC trust, caches, and marketplace actions do not become GitLab CI behavior automatically. | Translate to .gitlab-ci.yml, replace actions, use GitLab variables and identities, and run release-path tests. |
| Secrets, variables, and environments | lost | critical | Secret values are intentionally not exportable and environment rules differ. | Create a secret inventory without values, rotate credentials, and enter them through approved GitLab secret controls. |
| Packages, releases, and artifacts | partial | high | Release metadata, binary assets, container images, packages, Actions artifacts, and caches have separate migration paths and retention rules. | Inventory each registry and artifact class, then copy and verify with native clients. |
| Wikis and GitHub Pages | manual | high | Wikis are separate Git repositories and Pages build/deployment settings are platform-specific. | Mirror wiki repositories and rebuild Pages publishing and domains in GitLab. |
| Webhooks, apps, bots, and integrations | lost | critical | Installation IDs, tokens, webhook secrets, checks, project boards, Dependabot, and external automation do not transfer as working integrations. | Reauthorize or replace each integration and test delivery before disabling GitHub. |
| Fork relationships | partial | high | Repository content can transfer while fork-network relationships may not persist across migration methods. | Document required fork topology and test upstream/downstream contribution flows. |
Where each thing goes.
| Source | Destination | Method | Notes |
|---|---|---|---|
| GitHub organization | GitLab top-level group | manual | Create group and subgroup hierarchy before project import. |
| Repository | GitLab project | automatic | Confirm visibility, default branch, namespace, and path. |
| Git refs | Git refs | automatic | Use mirror semantics and compare every ref tip. |
| Pull request | Merge request | automatic | Review and merge semantics still require scenario validation. |
| Issue and label | Issue and label | automatic | Reconcile counts, states, assignees, milestones, and cross-references. |
| GitHub user/email | GitLab user/email | transform | Pre-create an identity map to preserve attribution. |
| Branch protection/ruleset | Protected branch and approval/merge rules | manual | Recreate policy from intent; status checks are not imported as an equivalent rule. |
| .github/workflows/*.yml | .gitlab-ci.yml and CI includes | manual | Translate jobs, matrices, permissions, environments, caching, and release steps. |
| Actions secret | GitLab masked/protected CI/CD variable or external secret | manual | Rotate; secret values cannot be exported safely. |
| GitHub release and asset | GitLab release and package/generic asset | transform | Copy binaries and verify checksums. |
| GitHub wiki | GitLab wiki | manual | Mirror the separate .wiki.git repository. |
| GitHub App/webhook | No destination | unsupported | Install a GitLab-native integration or update the external service. |
Make the move recoverable.
Create the source-of-truth backup
Preserve every repository and the operational metadata required to reconstruct development and release workflows.
- Inventory repositories, visibility, archived/fork/template status, refs, LFS, submodules, wikis, Pages, issues, PRs, releases, packages, Actions, environments, rules, teams, apps, webhooks, deploy keys, and production dependencies.
- Create a --mirror clone of every repository and wiki, fetch all LFS objects, run git fsck, and record ref tips.
- Export organization/repository metadata and issue/PR counts through approved GitHub APIs or enterprise migration exports.
- Download required release assets and package/container artifacts with checksums.
- Record secret names and consumers without exporting values; plan rotation.
Proof to capture: A repository-by-repository manifest contains mirror hashes, ref and LFS counts, collaboration counts, assets, policies, integration owners, and restore instructions.
Identity and namespace map
Preserve attribution and least-privilege access.
- Map organization, teams, users, bots, externals, and emails.
- Create GitLab groups and users before import.
- List every unmapped identity as a blocking exception.
Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.
CI/CD translation
Rebuild tested pipelines rather than copying inert workflow files.
- Map triggers, jobs, services, permissions, caches, artifacts, environments, and approvals.
- Rotate variables and update OIDC/cloud trust.
- Run build, test, preview, production, and rollback paths.
Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.
Policy reconstruction
Recreate repository protection from its security intent.
- Inventory GitHub rulesets, protections, CODEOWNERS, status checks, and bypass actors.
- Map to protected branches, approvals, push rules, and merge checks.
- Test allowed and forbidden actions with role accounts.
Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.
The things most likely to hurt.
These are operating limits. Treat every “Stop if” condition as a blocked migration, not a suggestion.
Developers push to both hosts
New commits appear on GitHub after the final mirror starts.
- Consequence
- Histories diverge and releases use inconsistent source.
- Mitigation
- Freeze pushes, archive or lock GitHub, and advertise one canonical remote.
Stop if: Any uncaptured GitHub ref advances after the freeze.
CI reaches production with broader credentials
Translated jobs use long-lived or unprotected variables.
- Consequence
- Source or infrastructure compromise.
- Mitigation
- Rotate secrets, minimize job tokens, protect environments, and test untrusted merge requests.
Stop if: A non-protected pipeline can read a production credential.
Required merge controls disappear
A role account can merge without required approval or checks.
- Consequence
- Unreviewed code reaches protected branches.
- Mitigation
- Run explicit negative policy tests.
Stop if: Any forbidden push or merge succeeds.
LFS or release binaries are missing
Pointers resolve but binaries fail to download.
- Consequence
- Builds, releases, or historical checkouts break.
- Mitigation
- Compare object inventories and checksums.
Stop if: Any production-required binary is unavailable.
Importer rate limits or partial states are ignored
GitLab marks a project partial or reports failed relation types.
- Consequence
- The project looks available while metadata is incomplete.
- Mitigation
- Save status details, retry controlled failures, and reconcile all object counts.
Stop if: Any project remains partial without an accepted exception.
Do the work in this order.
- Days 1–4 · inventory
Inventory and target design
18–28 hours active4 days elapsedOwner and security review waiting- Inventory repositories, identities, collaboration data, policies, CI/CD, artifacts, and integrations.
- Create namespace, identity, retention, and freeze plans.
Depends on: GitHub organization owner and GitLab owner access
Stop / go checkpointApprove migration waves?
Go when: Every repository and operational dependency has an owner and method.
Stop when: A production pipeline, secret consumer, or compliance requirement is unknown.
- Days 5–7 · backup
Mirrors and evidence
12–20 hours active3 days elapsedClone, LFS, and artifact transfer waiting- Create and validate Git, wiki, LFS, asset, and metadata backups.
- Record source ref and object counts.
Depends on: Approved inventory
Stop / go checkpointCan every repository be restored?
Go when: Mirrors pass fsck and all critical binaries and metadata are accounted for.
Stop when: A required ref, LFS object, release asset, or wiki is absent.
- Days 8–11 · pilot
Pilot representative projects
14–22 hours active4 days elapsedImporter and CI runs waiting- Import simple, large-history, LFS, release, and complex-CI projects.
- Validate attribution, policies, pipeline permissions, artifacts, and integrations.
Depends on: GitLab groups and users, Backups
Stop / go checkpointApprove bulk import?
Go when: All critical project archetypes pass data, security, CI, and release checks.
Stop when: Attribution, protections, binaries, or production rollback cannot be proven.
- Days 12–20 · waves
Import in waves
18–35 hours active5–9 days elapsedGitHub API limits and import queues waiting- Import projects by dependency wave.
- Reconcile each project and translate CI, policies, assets, and webhooks before advancing.
Depends on: Approved pilot
Stop / go checkpointSchedule final freeze?
Go when: Every project is complete or has an approved exception and release paths pass.
Stop when: Any importer partial state or critical CI failure remains.
- Days 21–23 · cutover
Final mirror and control-plane switch
5–9 hours active1–3 days elapsedFinal CI and webhook runs waiting- Freeze GitHub pushes and deploys.
- Mirror final refs and LFS, rotate secrets, switch webhooks/remotes, then run staged pipelines.
Depends on: Approved waves, Maintenance window
Stop / go checkpointMake GitLab canonical?
Go when: Refs match, policies reject forbidden actions, and production release/rollback pass.
Stop when: Any source ref advances or production controls differ from policy.
- Days 24–30 · observe
Read-only observation
3–16 hours active5–7 days elapsedDeveloper feedback and scheduled jobs waiting- Keep GitHub read-only and monitor clones, webhooks, pipelines, releases, bots, and developer workflows.
- Complete verification and owner sign-off.
Depends on: Successful cutover
Stop / go checkpointClose rollback window?
Go when: Blocking checks pass across normal and scheduled workflows.
Stop when: A critical integration still writes to GitHub or a GitLab workflow is unsafe.
Cut over with a way back.
Cutover
Make GitLab the only writable source and CI/CD control plane while preserving a final verified GitHub snapshot.
- Freeze GitHub merges, pushes, releases, package publication, and deploys.
- Record final refs, mirror Git and LFS, and reconcile object counts.
- Rotate credentials and configure protected GitLab variables, deploy keys, runners, and environments.
- Switch remotes, webhooks, apps, registries, badges, documentation, and deployment integrations.
- Run protected-branch negative tests and staged build, test, release, deploy, and rollback pipelines.
- Archive or lock GitHub repositories and show the canonical GitLab URL.
Proof to capture: Final refs match, policy tests pass, all critical binaries resolve, and one complete production release and rollback simulation succeeds from GitLab.
Rollback
Restore GitHub as canonical without discarding GitLab commits or security changes.
- Freeze GitLab pushes, pipelines, releases, and package publication.
- Mirror new GitLab refs and LFS objects into a quarantine backup.
- Review and push approved new refs back to GitHub.
- Restore rotated GitHub secrets, integrations, branch rules, remotes, and deployment controls.
- Run GitHub policy and release-path tests before reopening writes.
Proof to capture: GitHub contains all approved refs, rejects forbidden actions, and completes the tested release path while GitLab is read-only.
- Missing refs, LFS objects, or critical assets
- Required policy bypass is possible
- Production pipeline or rollback fails
- Identity or permission mapping exposes restricted projects
- Critical integration cannot operate against GitLab
Prove the migration worked.
Every blocking check must pass. Capture the evidence before cleanup begins.
| Pass | ID | Check | Method | Expected result | Evidence |
|---|---|---|---|---|---|
V-01Blocking | Refs and objects | Compare all branch/tag/ref tips, run fsck, and sample historical checkouts. | Exact ref parity and no corrupt or missing object. | Ref diff and fsck logs. | |
V-02Blocking | Large-file availability | Compare LFS inventories and download production-critical objects. | Every required pointer resolves to the correct checksum. | LFS object report. | |
V-03Blocking | Issues, merge requests, comments, and attribution | Reconcile counts and inspect representative complex histories. | Every object is imported or listed as an approved exception; authorship is explained. | Object-count reconciliation. | |
V-04Blocking | Membership and visibility | Test internal, external, bot, and unauthorized role accounts. | Least-privilege access matches the approved matrix. | Role test matrix. | |
V-05Blocking | Protected workflow | Attempt forbidden push, merge, approval, and secret-access scenarios. | Every forbidden action fails and required checks block correctly. | Negative-test log. | |
V-06Blocking | Release path | Run build, test, artifact, preview, protected deploy, and rollback scenarios. | Outputs and approvals match the accepted pipeline specification. | Pipeline URLs and deployment evidence. | |
V-07Blocking | Releases, packages, containers, and wiki | Reconcile inventories and verify representative checksums and clones. | Every retained artifact class is usable from its destination. | Artifact manifest. | |
V-08Blocking | External events | Trigger each webhook, bot, scanner, notification, Pages build, and deployment integration. | One successful destination event and no production write to GitHub. | Integration delivery log. |
Remove the scaffolding safely.
Safe after: At least two normal release cycles, all scheduled workflows, and 30 days without a rollback trigger.
- Preserve mirrors, LFS, assets, manifests, importer logs, identity mappings, policy tests, and verification evidence.
- Revoke GitHub PATs, deploy keys, app installations, webhooks, runner tokens, and machine credentials no longer required.
- Delete temporary migration tokens and staging mirrors from unsecured machines.
- Retain GitHub repositories archived for the approved compliance window before any deletion decision.
- Update onboarding, incident response, backup, release, and ownership documentation.
- Schedule quarterly restore, access, policy, integration, and dormant-secret reviews.
Built to be reviewed.
Tested 2026-07-19. Next scheduled review: 2026-10-19.
- GitLab: migrate from GitHubAccessed 2026-07-19
- GitLab: project import and exportAccessed 2026-07-19
- GitHub: backing up a repositoryAccessed 2026-07-19
- GitHub: organization migration exportsAccessed 2026-07-19