Friday, August 28, 2026

Merging Two Git Repositories with Combined Histories

This procedure merges two Git repositories while preserving the complete commit history of both repositories.

Goal

We have:

  • Repo A — the final/main repository
  • Repo B — the repository being imported
  • Repo B should appear inside Repo A under the priorwork/ directory
  • Both repositories use the main branch

The final layout should look like:

repo-A/
├── existing files from Repo A
└── priorwork/
    ├── files from Repo B
    └── ...

Both repositories' Git histories will be preserved.


1. Start with a fresh clone of Repo B

Do not run git filter-repo inside Repo A.

Clone Repo B into a separate directory:

git clone <REPO-B-URL> repo-B
cd repo-B

Make sure you are on Repo B's main branch:

git checkout main

2. Rewrite Repo B so its files live under priorwork/

Run this command inside the fresh Repo B clone:

git filter-repo --to-subdirectory-filter priorwork

Because this is a fresh clone, you should not need --force.

Verify the result:

git ls-tree -r HEAD --name-only | head -20

You should see paths like:

priorwork/.gitignore
priorwork/README.md
priorwork/file1...
priorwork/src/...

This confirms that Repo B's files have been moved under priorwork/ in its history.


3. Go to Repo A

Switch to the final repository:

cd ../repo-A

Make sure you are on the branch that should receive Repo B:

git checkout main

Check that your working tree is clean:

git status

Ideally, Git should report:

nothing to commit, working tree clean

4. Add the rewritten Repo B as a remote

From inside Repo A:

git remote add priorwork ../repo-B

Verify:

git remote -v

You should see something similar to:

origin     <REPO-A-URL>
priorwork  ../repo-B

Here, priorwork is simply the name of the Git remote. It is not a directory in the working tree.


5. Fetch Repo B's rewritten history

Run:

git fetch priorwork

This downloads Repo B's Git history into Repo A's .git directory.

It does not create a priorwork/ directory in the working tree yet.

Verify that Repo B's files are stored under the desired path:

git ls-tree -r priorwork/main --name-only | head -20

You should see:

priorwork/.gitignore
priorwork/README.md
priorwork/file1...
priorwork/src/...

This confirms that Repo B was correctly rewritten before the merge.


6. Merge the two histories

Now merge Repo B into Repo A:

git merge priorwork/main --allow-unrelated-histories

The --allow-unrelated-histories option is required because Repo A and Repo B originally had independent Git histories.

If there are no conflicts, the merge should complete successfully.

The working tree should now look approximately like:

repo-A/
├── .git/
├── <Repo A files>
├── ...
└── priorwork/
    ├── .gitignore
    ├── README.md
    ├── file1...
    └── src/
        ├── ...
        └── ...

7. Verify that both histories were preserved

View the complete commit graph:

git log --graph --oneline --all --decorate

You should see the two independent histories joining at the merge commit.

You can also inspect Repo B's history:

git log --oneline priorwork/main

And inspect all history:

git log --oneline --all

8. Push the combined repository

Once everything has been verified:

git push origin main

The remote Repo A repository will now contain:

  • Repo A's original files
  • Repo B's files under priorwork/
  • Repo A's complete Git history
  • Repo B's complete Git history
  • A merge commit connecting the two histories

Important Concept

The overall process is:

             Rewrite Repo B
Repo B ───────────────────────────►
                                    Repo B'
                                    priorwork/
                                        │
                                        │
Repo A ───────────────────────────────►│
                 merge                │
                                        ▼
                             Combined Repo A
                             ├── Repo A files
                             └── priorwork/
                                 └── Repo B files

The most important rule is:

Run git filter-repo on a fresh clone of Repo B, not on Repo A.


Quick Command Summary

Repo B

git clone <REPO-B-URL> repo-B
cd repo-B
git checkout main
git filter-repo --to-subdirectory-filter priorwork
git ls-tree -r HEAD --name-only | head -20

Repo A

cd ../repo-A
git checkout main
git status

git remote add priorwork ../repo-B
git fetch priorwork

git ls-tree -r priorwork/main --name-only | head -20

git merge priorwork/main --allow-unrelated-histories

Verify and push

git log --graph --oneline --all --decorate
git push origin main

Expected Result

repo-A/
├── files originally belonging to Repo A
└── priorwork/
    └── files originally belonging to Repo B

Both repositories' commit histories are preserved.