Merge multiple Git repositories into a single monorepo while preserving full commit history, tags, and Git LFS objects.
This Bash script automates the process of:
- importing each repo into its own subfolder,
- auto-creating the target GitHub repo when needed,
- handling tag prefixing to avoid collisions,
- doing robust default branch detection,
- and cleaning up all temporary directories automatically to save disk space.
Perfect for teams migrating from many scattered repos to a single source of truth with a clean, auditable history.
- Full history preservation for each imported repository under its own subfolder.
- Tag prefixing (optional) → prevents tag name collisions across repos (e.g.
repoA-v1.0). - Git LFS support → fetches and preserves large files & LFS pointers.
- Auto-create target repo on GitHub (or use an existing one).
- Robust default branch detection (
symbolic-ref, remote HEAD, fallback). - Skips empty source repos gracefully without breaking the run.
- Initial empty commit in the monorepo if no commits exist yet.
- Immediate per-repo cleanup + final workspace cleanup (
trap cleanup EXIT) — no leftover temp dirs. - Detailed merge commit messages with source repo & branch info.
- Safe subdir validation to prevent path traversal risks.
bashgitgit-filter-repo(must be in$PATH)jqgit-lfs(required ifUSE_GIT_LFS=true)- GitHub Personal Access Token with
reposcope (needed only for auto-creation mode)
-
Clone this repo
git clone https://github.com//multirepos-to-monorepo.git cd multirepos-to-monorepo -
Configure your migration
- Open
merge-repos.sh - Update:
MONOREPO_OWNER→ your GitHub username or organization.MONOREPO_NAME→ name of the destination monorepo.REPOS_TO_IMPORT→ list of repos informat.
- Example:
REPOS_TO_IMPORT=( "https://github.com/myorg/service-a.git service-a" "git@github.com:myorg/libB.git libB" )
- Open
-
Choose target repo mode
- Existing repo: set
MONOREPO_URLto HTTPS/SSH clone URL. - Auto-create repo: leave
MONOREPO_URLblank and export a GitHub token:export GITHUB_TOKEN="ghp_yourtoken"
- Existing repo: set
-
Run the script
chmod +x merge-repos.sh ./merge-repos.sh
-
Verify the results
- Inspect your monorepo on GitHub — each imported repo will be under its own folder.
- Tags will appear with prefixes if
PREFIX_TAGS=true.
- Creates or reuses the target monorepo (auto-create via GitHub API if enabled).
- Clones each source repo into a temporary directory inside a workspace.
- Detects the default branch automatically:
- Tries remote HEAD (
symbolic-ref), thengit remote show origin, then common names likemainormaster.
- Tries remote HEAD (
- Optionally renames tags using
-pattern to prevent collisions. - Runs
git filter-repo --to-subdirectory-filterto move history into its subfolder. - Merges into the monorepo branch with
--allow-unrelated-histories. - Pushes monorepo (and tags) to GitHub and sets default branch.
- Deletes the individual temp clone immediately and cleans up the whole temp workspace on exit.
- Empty source repo → detected & skipped with a warning.
- Empty monorepo branch → automatically starts with an empty commit.
- Existing temp remotes → removed before reuse to avoid Git conflicts.
- Unsafe subdir names (
..or starting/) → skipped to prevent security issues. - Temp space cleanup:
- Per-repo clone deleted after merge.
- Global temp workspace deleted on script exit — even on failure.
- Script runs
git lfs fetch --allin each source repo. - Runs
git lfs install --localin the monorepo after clone. - Preserves
.gitattributespatterns inside each imported subdir. - Does not auto-convert large files into LFS — it preserves existing LFS usage.
Merge multiple git repositories into one monorepo with full history, tags & LFS. Automates imports, tag prefixing, empty repo handling, and GitHub pushes.
Topics:
monorepo git migration import merge git-filter-repo lfs tags history cli
This project is licensed under the MIT License — see the LICENSE file for details.
