Merge two Git repositories without breaking file history
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
If you copy files from one repository into another with the filesystem, Git sees only new files and the old history disappears from normal logs. To preserve file history, you need to merge commit graphs, not just directory contents.
Decide Whether the Imported Repo Gets a Prefix
The cleanest way to combine two unrelated repositories is usually to import one into a subdirectory of the other. That avoids path collisions and keeps the origin of the imported code obvious.
Suppose you want to import repo-a into repo-b under the directory legacy/repo-a. Start from repo-b:
At this point repo-b knows about the commit history from repo-a, but nothing has been merged yet.
Merge Histories and Read the Tree Under a Prefix
One reliable low-level technique is:
What these commands do:
- '
git merge -s ours --no-commit --allow-unrelated-historiescreates a merge state between two independent histories without overwriting your current files' - '
git read-tree --prefix=...stages the other repository's tree under a subdirectory' - '
git commitrecords the merge so both histories are now connected'
After that, the imported files live in legacy/repo-a, and Git still knows the original commits that created them.
Verifying That History Was Preserved
Pick one imported file and inspect its log:
For rename-sensitive history, try:
--follow is helpful when you later rename a file after the import. Keep in mind that Git's rename tracking is heuristic, so very large rewrites can still make history harder to follow. The important part is that the underlying commit graph has been preserved.
If You Want Both Repositories at the Root
Sometimes the goal is not to place one repo inside a subdirectory, but to merge two codebases into the same root tree. That is possible, but it is riskier because overlapping paths create conflicts immediately.
A simple root-level history merge looks like this:
If the repositories contain files with the same names, you will need to resolve conflicts manually. This still preserves history, but the result is harder to organize than the prefix-based import.
Why Copying Files Is Not Enough
If you do this instead:
you preserve file contents, but not the existing commit ancestry. Git now sees one big add or rewrite in repo-b, and the previous history from repo-a is invisible from the combined repository.
That is the exact scenario people mean when they say the history was "broken."
Practical Workflow Advice
Before merging repositories:
- make sure both working trees are clean
- create a backup branch or tag
- decide where the imported project should live
- document the import commit clearly
A message such as Import repo-a into legacy/repo-a is much better than a vague message like merge stuff.
If this is a long-term monorepo move, the prefix-based layout is usually easier for future maintenance. Teams can gradually refactor directory structure later without losing the original import boundary.
Common Pitfalls
The biggest mistake is copying files instead of merging histories. That always loses the original ancestry from the imported repository.
Another issue is importing at the root when the two repositories contain similar top-level paths. Conflict resolution becomes messy, and it is harder to prove which files came from which project.
Teams also forget that git log --follow is heuristic. History may still look odd after major renames, even when the merge was done correctly. That does not mean the import failed.
Summary
- Preserve history by merging commit graphs, not by copying files.
- A prefix-based import using
git read-tree --prefixis usually the safest approach. - Use
--allow-unrelated-historiesbecause the repositories started independently. - Verify the result with
git logon imported files. - Expect rename tracking to be helpful but not perfect after large restructures.

