Git
version control
repository management
file history
software development

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:

bash
cd /path/to/repo-b
git remote add repo-a /path/to/repo-a
git fetch repo-a

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:

bash
git merge -s ours --no-commit --allow-unrelated-histories repo-a/main
git read-tree --prefix=legacy/repo-a/ -u repo-a/main
git commit -m "Import repo-a into legacy/repo-a"

What these commands do:

  • 'git merge -s ours --no-commit --allow-unrelated-histories creates 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 commit records 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:

bash
git log -- legacy/repo-a/README.md

For rename-sensitive history, try:

bash
git log --follow -- legacy/repo-a/src/app.py

--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:

bash
1cd /path/to/repo-b
2git remote add repo-a /path/to/repo-a
3git fetch repo-a
4git merge --allow-unrelated-histories repo-a/main

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:

bash
cp -R /path/to/repo-a/* /path/to/repo-b/
git add .
git commit -m "Copied repo-a files"

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 --prefix is usually the safest approach.
  • Use --allow-unrelated-histories because the repositories started independently.
  • Verify the result with git log on imported files.
  • Expect rename tracking to be helpful but not perfect after large restructures.

Course illustration
Course illustration

All Rights Reserved.