Skip to content

GitHub Enterprise Importer

Lesson 6 of 8Advanced15 min readGit at Scale & Enterprise Engineering · Repository FleetVerified: GitHub Enterprise Importer documentation, September 2026

The importer moves repositories. The list of what it does not move is longer than most migration plans account for, and it is where migration projects overrun.

Destinations: GitHub Enterprise Cloud — either GitHub.com or GHE.com.

Sources: Azure DevOps Cloud, Bitbucket Server and Data Center 5.14 and later, GitHub.com, GitHub Enterprise Server 3.4.1 and later, and GitLab — either GitLab.com or a maintained self-hosted instance.

GitHub documents that “GitHub Enterprise Importer does not currently support migrations from GHE.com.”

Migrations are repository by repository, or organisation by organisation when the source is GitHub.com.

The gap is the important part, and it is covered below.

The documented list, and every item is manual work.

Actions secrets, variables, environments, self-hosted runners, larger runners, workflow artifacts and workflow run history. All of it. A repository arrives with its workflow files and none of the configuration that makes them run.

Git LFS objects and large binaries. A repository using LFS arrives with its pointers and without its content.

Teams and team access, when migrating a repository directly.

Team membership.

The cumulative effect: for a repository with active CI, the migration delivers the code and history, and leaves the secrets, environments, runner configuration and team access to be recreated. That is the bulk of the work.

Two granularities, with different implications.

Repository migration moves one repository at a time. Available from every supported source, and it is what most migrations use because it gives per-repository control over timing and verification.

Organisation migration is available when the source is GitHub.com, and moves an organisation’s repositories together.

The teams caveat matters here. GitHub documents that teams and team access are not migrated when migrating a repository directly — which is the more common mode, and it means permissions are a manual step in most migrations.

Repository-by-repository suits wave-based migration, which is what most large migrations need: a team’s repositories move together, their configuration is recreated once, and their verification is one exercise.

Organisation migration suits a straightforward lift where the source organisation structure is the destination structure and there is little configuration to recreate.

Most large migrations use repository granularity because the destination structure is rarely a copy of the source’s — a migration is usually also a reorganisation, and the opportunity to fix the structure is one of the few benefits of the disruption.

Whichever you use, drive it from your own inventory rather than from the source’s repository list, so archived and excluded repositories are never attempted. See migration automation.

A single Git commit: 2 GB.

A single file: 400 MiB.

The Git repository: 20 GB, source code only — GitHub’s guidance is to check this with git-sizer’s total blob size.

Metadata: 20 GB, covering issues, pull requests, releases and attachments.

Which means some repositories cannot be migrated as they are. A repository exceeding a limit needs history rewriting, content removal, or a different approach — and finding that out during the migration window is expensive.

Check every repository against the limits before planning. This is a fleet query and it takes an afternoon. See repository health.

Note the asymmetry with GitHub’s ordinary limits. The 400 MiB file limit is above GitHub’s 100 MB push limit, and the 20 GB repository limit is above the 10 GB repository limit — so a repository can be migratable and still be unhealthy at its destination.

Balancing the picture, because the tool does carry a substantial amount.

The Git repository. All commits, branches, tags and the full history. This is the part that matters most and the part people worry about needlessly.

Issues, with their comments, labels and state.

Pull requests, with their comments, review comments and state. The review record — who approved what — is what makes this valuable for anything with a change management control depending on it.

Releases, subject to the metadata size limit.

Attachments, within the same limit.

Which covers the historical record. A migrated repository can answer “who approved this change in 2023”, which is frequently the requirement that makes migration preferable to starting fresh.

Repository settings largely do not carry, and this is where the boundary sits: the content and history migrate, the configuration does not.

That framing is the most useful way to remember it. If it is something a person wrote — code, an issue, a review — it migrates. If it is something an administrator configured — a secret, an environment, a permission, a webhook — it does not.

The dimension people forget until they see the result.

Commit authorship is in the commit. Author name and email are part of the Git object, and they migrate unchanged.

Whether those commits link to accounts on the destination depends on whether the email addresses map to accounts there. A commit authored with an address that exists on the destination links; one that does not shows as an unlinked author.

Issue and pull request authorship is a platform concept and needs mapping. GEI supports mapping users from source to destination, and the quality of that mapping determines whether the migrated history is attributable.

Build the mapping before migrating. Source user to destination user, for everybody who has ever contributed. This is genuinely tedious and it is much worse to fix afterwards.

Departed contributors have no destination account, and their contributions will show as unattributed or mapped to a placeholder. That is expected; deciding how to handle it in advance is better than discovering it.

Under Enterprise Managed Users the mapping is to managed accounts, which is a further consideration — and the accounts must exist before the migration references them. See Enterprise Managed Users.

Tell people what to expect. An engineer whose ten years of contribution history appears unattributed on the new platform will notice, and being told in advance is the difference between disappointment and a grievance.

Inventory what will not migrate, per repository, before starting.

Actions secrets. Enumerate them by name. Values are not readable, so each must be re-created from its original source — which means finding whoever knows what each one is. This is frequently the longest pole in a migration, and it is a good moment to move to OIDC and eliminate the stored credential entirely.

Environments and their protection rules. Required reviewers, wait timers, branch restrictions. Recreate deliberately rather than approximately.

Self-hosted runners. Re-register against the destination, with the group structure re-established. See runner groups.

Teams and membership. Recreate, ideally driven from your identity provider rather than by hand. See team synchronisation.

LFS content. A separate migration, with its own tooling and its own verification.

Workflow run history. Not migrated, and generally accepted as lost. If it is compliance evidence, extract it before the migration rather than after.

  1. Inventory and triage. What exists, what is dead, what exceeds limits. Archive rather than migrate wherever possible — see repository lifecycle.

  2. Check every repository against the size limits. Remediate the ones that exceed them, which may mean history rewriting and its coordination cost.

  3. Set up the destination. Organisations, teams, rulesets, custom properties, apps. A repository migrating into a correctly configured destination arrives governed.

  4. Migrate a pilot. One repository, then a handful, including one with LFS and one with substantial CI configuration. Learn what breaks.

  5. Build the gap checklist from what the pilot revealed, per repository type.

  6. Migrate in waves, by team, so each team’s repositories move together and their configuration work is one exercise rather than several.

  7. Recreate the gap for each wave: secrets, environments, runners, teams, LFS.

  8. Verify. Not “it appeared” — commits match, branches match, CI runs, LFS resolves, permissions are correct.

  9. Cut over, with the old repository archived rather than deleted.

The migration authenticates to both source and destination.

Use a dedicated identity, scoped to the migration, with the permissions the operation needs and no more.

Under conditional access, exempt it. GitHub notes specifically that service accounts should be exempt from Entra ID’s conditional access policy when using the importer, or the migration will be blocked. See OIDC for EMU.

Revoke it afterwards. A migration credential that survives the migration is standing access to two platforms with nobody using it.

A repository migrating into a well-configured destination arrives governed. One migrating into an empty organisation arrives as a problem to fix later.

Organisations first. The structure you decided in enterprise account architecture, not a mirror of the source’s structure.

Custom properties defined, so migrated repositories can be classified immediately. See custom properties.

Rulesets in place, targeted by property. A repository arriving with its tier set is governed from the moment it lands.

Teams created, ideally synchronised from your identity provider rather than recreated by hand.

Organisation settings configured — base permission, default branch name, automatic branch deletion, Actions permissions. These apply to migrated repositories automatically.

Apps installed, with their scopes decided.

Shared workflows available, so migrated repositories can be converted to call them rather than carrying their own copies.

Doing this first costs a week and saves a remediation project. The alternative — migrating four hundred repositories and then governing them — means a bulk campaign to set properties on repositories that could have arrived with them.

And set the properties as part of the migration, not afterwards. The migration script knows which team’s wave it is running; that is the moment the owning team property is known and cheapest to set.

Commit count and tip SHA on the default branch and on any branch that matters.

Branch and tag count.

Repository size, which should be comparable.

LFS content resolves — clone and check out a commit referencing an LFS object.

Issues and pull requests present, with their comments.

CI runs successfully, which is the check that exercises the secrets, environments and runners.

Permissions correct — the right teams with the right access.

Automate the verification. A migration of four hundred repositories verified by hand is not verified.

Handling repositories that exceed the limits

Section titled “Handling repositories that exceed the limits”

The cases that stop a migration, and what to do about each.

A commit over 2 GB. Almost always a single enormous file committed once. History rewriting is the only remedy, and it invalidates every clone.

A file over 400 MiB. The same cause and the same remedy. Note that such a file could not have been pushed to GitHub in the first place — a repository containing one has come from a platform with different limits, which is a useful diagnostic about where the migration is coming from.

A repository over 20 GB of source. Either history rewriting, or splitting the repository, or a decision that it does not migrate. Check with git-sizer’s total blob size, as GitHub’s guidance suggests.

Metadata over 20 GB. Issues, pull requests, releases and attachments. Unusual, and it happens with repositories that have accumulated large release assets over many years. Releases may need to be moved to another store before migration.

For each, the options are the same three: rewrite, split, or do not migrate.

Do not migrate is a legitimate answer. A repository that is enormous and dead should be archived on the source platform and left there for a retention period. Rewriting a 30 GB repository nobody uses is work performed for nothing.

Find these early. A fleet-wide size check against all four limits is an afternoon of work, and doing it in week one rather than week twelve is the difference between a plan and a surprise.

Within a migration, the unit of work is a wave — a team’s repositories moving together. The sequence within a wave:

  1. Freeze. Announce a window during which the source repositories should not be changed. Short — hours, not days.

  2. Migrate the repositories. The mechanical step, and usually the fastest.

  3. Migrate LFS content, separately, for any repository that uses it.

  4. Recreate teams and permissions on the destination.

  5. Recreate secrets and environments. The longest step, and the one requiring the team’s involvement.

  6. Re-register runners, if the team has self-hosted ones.

  7. Run the verification. Automated, per repository.

  8. Have the team run their pipeline, end to end, including a deployment to a non-production environment. This is the real verification.

  9. Update references — documentation, other repositories’ workflows, external systems pointing at the old location.

  10. Archive the source repositories, read-only, retained.

Step 8 is the acceptance criterion. A migration is not complete when the code has moved; it is complete when the team can ship from the new location.

Keep the freeze short. A long freeze means a team cannot work, and the pressure to skip verification becomes considerable.

Assuming secrets and environments migrate. They do not.

Discovering the LFS gap late. It can be the largest item.

Not checking size limits first. A repository that cannot migrate found during the window.

Migrating dead repositories. Archive them; they cost nothing to leave.

Configuring the destination after migrating. Repositories arrive ungoverned.

Migrating by repository rather than by team. Configuration work is repeated per repository.

Verifying by looking. Four hundred repositories need automated verification.

Leaving the migration credential active. Standing access to two platforms.

Not extracting workflow run history where it is compliance evidence.

The sources differ in what maps cleanly, and knowing where the friction is helps scope the work.

GitHub.com to GitHub Enterprise Cloud. The cleanest case — the same platform, the same concepts, and organisation-by-organisation migration is available. The work is mostly the configuration gap.

GitHub Enterprise Server to Cloud. Also same-platform, from version 3.4.1 onward. Configuration gap plus whatever differs between the Server version’s features and the destination’s.

Azure DevOps Cloud. A different platform with different concepts. Repositories and pull requests map; the surrounding build and release pipelines do not, and rebuilding them in Actions is a project of its own — frequently larger than the repository migration.

Bitbucket Server and Data Center, 5.14 and later. Repositories and pull requests map. Pipelines, if any, do not.

GitLab, either GitLab.com or a maintained self-hosted instance. Repositories and merge requests map. GitLab CI configuration does not translate to Actions, and that conversion is the bulk of the work for an estate with substantial pipelines.

For every non-GitHub source, budget the CI conversion separately. It is usually larger than the migration and it is frequently omitted from the plan because the migration tooling does not mention it.

And check the source version requirements. Bitbucket 5.14, GitHub Enterprise Server 3.4.1 — a source below the minimum needs upgrading before migrating, which is a project ahead of a project.

Workflow run history is not migrated, and for most organisations that is an acceptable loss. For some it is not.

Ask whether anything depends on it. Frequently the answer is genuinely nothing — nobody looks at builds from eighteen months ago.

Where it is compliance evidence, it needs extracting before the migration. A run history demonstrating that a required check passed on every merge is exactly the kind of artifact an assessor asks for, and it will not exist on the destination.

Extract via the API into your own storage, before cutover. Once the source repository is archived or the platform decommissioned, the opportunity has passed.

Store it with the other evidence artifacts. See compliance evidence.

Deployment history has the same property, and is more likely to matter — “when was this deployed and who approved it” is a question that arrives after an incident.

Decide deliberately. Losing run history because you chose to is fine; losing it because nobody asked is the avoidable version.

Every migration wave needs a position on what happens if it goes wrong, decided before the wave rather than during it.

The source is the rollback. Keep it intact and unmodified until the destination is verified and the team has shipped from it. Do not archive the source at cutover; archive it a week later.

Which means the freeze matters. If work continued on the source during migration, rolling back is straightforward and rolling forward later means migrating again with the new commits. If work continued on the destination, rolling back means losing it.

Define the rollback trigger. What would make you revert: verification failure, a CI problem that cannot be resolved in the window, missing LFS content, a permission model that cannot be reproduced.

Make the decision quickly. A wave that is half-working, argued about for two days, costs more than a clean revert and a second attempt.

Point the team back explicitly. A rollback where half the team is pushing to the destination and half to the source produces divergence that has to be merged by hand.

Record why. A wave that rolled back has revealed something the pilot did not, and that finding should change the process for every subsequent wave.

Retain the source for a defined period after every wave succeeds — long enough that a problem discovered a fortnight later is still recoverable. Then archive, and decommission on the schedule you decided at the start.

The importer moves code, history, issues and pull requests. Everything that makes a repository work — secrets, environments, runners, LFS content, teams — is yours to recreate. Plan the migration around that gap rather than around the tool, because the gap is where the time goes.

  • Destinations are GitHub Enterprise Cloud on GitHub.com or GHE.com; migrations from GHE.com are not currently supported
  • Sources include Azure DevOps Cloud, Bitbucket Server and Data Center 5.14+, GitHub.com, GitHub Enterprise Server 3.4.1+, and GitLab
  • Actions secrets, variables, environments, runners, artifacts and run history are not migrated
  • Git LFS objects and large binaries are not migrated
  • Teams, team access when migrating a repository directly, and team membership are not migrated
  • Limits are 2 GB per commit, 400 MiB per file, 20 GB per repository and 20 GB of metadata
  • Configure the destination before migrating so repositories arrive governed
  • Migrate by team so configuration work happens once per team
  • Verification must be automated and must include a successful CI run

Reason from an estate you know, or from documentation.

  1. Pick ten repositories. For each, list what would not migrate: secrets, environments, runners, LFS, teams.

  2. Estimate the manual work per repository from that list. Predict: more or less than the migration itself?

  3. Check every repository in one organisation against the four size limits.

  4. Find repositories using LFS. Estimate the LFS content volume.

  5. Enumerate Actions secrets in one repository. For each, identify who would know its value.

  6. Design the verification script: what it would check per repository.

  7. Determine what identity would run the migration and what permissions it needs.

  8. Estimate what fraction of the estate could be archived rather than migrated.

Engineering Team Onboarding SystemA 30-day Git and GitHub programme with standards templates, assessments and governance checklists.