Merge two independent Git repositories

Join unrelated histories at the repository root, import a project into a subdirectory, or rewrite its paths before combining histories.

Best practices
Illustration of Git branches and version history

To combine two independent repositories, fetch a branch from the second repository and merge it with --allow-unrelated-histories. If its files should live under a subdirectory, git subtree add is often a better fit. These are alternative layouts, so choose one before importing.

Merge both projects at the root

Work in a clean checkout of repository A and keep a backup or recovery branch:

git status --short
git switch -c import-project-b
git remote add project-b /path/to/project-b
git fetch --no-tags project-b
git merge --allow-unrelated-histories project-b/main

Replace the path and branch with the actual source. If both repositories contain a different README.md, for example, Git may report an add/add conflict. Resolve it deliberately, stage the affected files, and run git commit to finish. Use git merge --abort if you decide not to continue.

The merge retains the original commits reachable from the selected source branch. It does not automatically import every source branch or tag as a local ref. Two originally separate single-root histories yield two roots in the combined ancestry, but repositories with more complex histories need not have exactly two roots.

Inspect the result with git log --graph --oneline --all, review the tree, and test the combined project before publishing it.

Import into a subdirectory

In a clean checkout where the root-level merge above has not been performed, add and fetch the source remote, then run:

git subtree add --prefix=vendor/project-b project-b main

The prefix must be suitable for a new subtree. Without --squash, this retains the imported history in the ancestry while placing its current files under the prefix. Historical commits keep their original paths: an old source commit still has src/file.js, not automatically vendor/project-b/src/file.js.

With --squash, subtree instead records a squashed representation of the imported changes. That is a different history-preservation choice. For later updates using the same import policy:

git subtree pull --prefix=vendor/project-b project-b main

git subtree is distributed as a Git contribution and may require separate installation on some systems. Keep the source remote while you need it for updates.

Rewrite historical paths when required

If every imported commit must contain the subdirectory prefix, use git filter-repo on a disposable clone of repository B before merging:

git clone --no-local /path/to/project-b project-b-rewritten
cd project-b-rewritten
git filter-repo --to-subdirectory-filter vendor/project-b

Then fetch that rewritten repository into A and merge its selected branch. This rewrites commit IDs and may affect signatures and external references, so preserve the original repository and coordinate any use of the rewritten history.

Check more than the main branch

Using --no-tags avoids accidental tag-name collisions during the initial fetch. Inventory and import other branches or tags explicitly, choosing names that do not collide. Do not remove the temporary remote until wanted refs have been retained independently; a merge preserves reachable ancestry, not every remote-tracking ref.

Review CI paths, build scripts, licenses, submodule configuration, and Git LFS objects. Combining Git commits does not automatically migrate external artifacts or make two applications share one build system correctly. Hosting-platform issues and pull requests are separate records; migrate them through the platform's supported tools or API if they are part of the consolidation.

See git-merge, git-subtree, and git-filter-repo.

Get a tailored plan

Contact us

Join the community

Scan with WeChat
to join the community

Try Guance

Start online and pay only for what you use.

Get started

Choose a Guance plan

Code hosting