By admin , 29 April 2026

What you will achieve

You will combine several existing Git repositories into a single monorepo, preserving each repo's history under its own subdirectory. The result: one repo, one CI pipeline, atomic cross-package commits.

Why monorepo

Multi-repo setups make cross-cutting changes hard. Renaming a function exported by a shared library and updating every caller becomes a coordination dance across PRs. In a monorepo, it is one PR. The trade-off: tooling investment and repo-size management.

Prerequisites

  • Recent Git (2.30+).
  • git filter-repo installed: pip install git-filter-repo.
  • The source repositories you want to combine.

Step 1: prepare each source

For each source repo, create a fresh clone and rewrite history so all files live under a subdirectory matching their target location in the monorepo.

mkdir monorepo-staging && cd monorepo-staging

# Repo 1: web app
git clone --no-local /path/to/web-source web-prep
cd web-prep
git filter-repo --to-subdirectory-filter packages/web
cd ..

# Repo 2: shared UI library
git clone --no-local /path/to/ui-source ui-prep
cd ui-prep
git filter-repo --to-subdirectory-filter packages/ui
cd ..

# Repo 3: CLI tool
git clone --no-local /path/to/cli-source cli-prep
cd cli-prep
git filter-repo --to-subdirectory-filter packages/cli
cd ..

Each prep repo now has its files under the chosen subdirectory; commits, authors, and dates are preserved.

Step 2: create the monorepo

git init monorepo
cd monorepo
git commit --allow-empty -m "Initial monorepo commit"

Step 3: import each source

git remote add web-source ../web-prep
git fetch web-source
git merge --allow-unrelated-histories web-source/main -m "Import web from web-source"
git remote remove web-source

git remote add ui-source ../ui-prep
git fetch ui-source
git merge --allow-unrelated-histories ui-source/main -m "Import ui from ui-source"
git remote remove ui-source

git remote add cli-source ../cli-prep
git fetch cli-source
git merge --allow-unrelated-histories cli-source/main -m "Import cli from cli-source"
git remote remove cli-source

Step 4: verify history

git log --oneline
git log --follow packages/web/src/index.js
git blame packages/ui/src/Button.js

Each subdirectory's history should trace back to the original commits.

Step 5: monorepo tooling

Adopt a build orchestrator that scopes work to changed packages:

# package.json
{
  "private": true,
  "workspaces": ["packages/*"],
  "scripts": {
    "build": "turbo run build",
    "test": "turbo run test",
    "lint": "turbo run lint"
  }
}

Common choices: Turborepo, Nx, Lerna, Bazel, Pants. Pick one and commit.

Step 6: affected-only CI

# Run tests only for packages affected by the PR
turbo run test --filter=...[origin/main]
# Or with Nx
npx nx affected:test --base=origin/main

Without affected-only filtering, every PR runs every test, and CI times balloon.

Step 7: sparse checkout

Developers working on one package can avoid checking out the whole tree:

git clone --filter=blob:none --sparse <url>
cd monorepo
git sparse-checkout init --cone
git sparse-checkout set packages/web

The clone is small and grows on demand.

Step 8: archive the source repos

Make the source repos read-only. Document the migration in their READMEs pointing to the monorepo. Do not delete - history archaeology may need them.

Step 9: communicate

Train the team:

  • New clone URL.
  • Build orchestrator commands.
  • How to add a new package.
  • CI behaviour - affected-only.
  • How to find history of files moved from old repos.

Step 10: cross-package refactors

The payoff: rename a function in packages/ui and update its callers in packages/web in one commit. The whole change is reviewed and merged together; no version coordination dance.

Pitfalls

  • Repository size grows. Plan for partial clone and sparse checkout from day one.
  • CI is slow without affected-only filtering. Set it up before scaling contributors.
  • Permission granularity is harder. If sub-teams need walled-off code, monorepo may not be right.
  • The first month after migration is rough - tooling, conventions, and habits all need to align.

The result

One repo with full history of every package, ready for atomic cross-cutting changes, with build and CI tooling that scales. The migration is a real engineering project, but the daily benefits compound for years.