Clean up historyAlways fixable

Merge two Git repositories into one

How do I combine two separate repos while keeping both histories?

Short answer

Add the second repository as a remote, fetch it, and merge with --allow-unrelated-histories into a subdirectory. Both sets of commits end up in one repository with their history intact.

quick fix
git remote add other ../other-repo
git fetch other
git merge other/main --allow-unrelated-histories

Pull another repository's full history into this one

Does this match your situation?

  • Two projects should live in one monorepo.
  • A library you wrote separately now belongs inside the main app.
  • You want to consolidate repositories without losing either history.

Step-by-step fix

  1. Back up both repositories

    This rewrites nothing by default, but a merge across unrelated histories is fiddly enough to be worth a safety copy of each side.

    step 1
    git clone --mirror ../repo-a ../repo-a-backup.git
    git clone --mirror ../repo-b ../repo-b-backup.git
    
  2. Move the incoming repo's files into a subdirectory first

    Doing this on the other repository before merging avoids every root-level collision — two READMEs, two .gitignores, two package.json files.

    step 2
    cd ../repo-b
    mkdir -p libs/widget
    git mv $(git ls-files | grep -v '^libs/') libs/widget/ 2>/dev/null
    git commit -m "Move files into libs/widget for merge"
    

    Or use git filter-repo --to-subdirectory-filter libs/widget, which rewrites the whole history as if it always lived there.

  3. Fetch it into the main repository

    A local path works as a remote URL, so nothing needs to be published anywhere.

    step 3
    cd ../repo-a
    git remote add widget ../repo-b
    git fetch widget
    
  4. Merge the unrelated history

    The flag is required because the two histories share no ancestor. With the files already namespaced, this usually merges without a single conflict.

    step 4
    git merge widget/main --allow-unrelated-histories
    git log --oneline | head -20
    
  5. Clean up

    Drop the temporary remote and verify both histories are present and attributed correctly.

    step 5
    git remote remove widget
    git log --oneline --graph | head -30
    git log --format="%an" | sort -u
    

Why this works

Git has no notion of a repository identity inside its object database — there are only objects and refs. Fetching from another repository copies its objects in, and because a merge commit can have any two parents, joining two entirely separate histories is structurally legal. The --allow-unrelated-histories flag exists purely because doing this accidentally is far more common than doing it deliberately. Namespacing the incoming files into a subdirectory first is what turns a conflict-ridden merge into a clean one.

If that didn’t work

  • git subtree add --prefix=libs/widget ../repo-b main is a one-command alternative that handles the namespacing for you.
  • If you don't need the second repository's history, copying the files in as a single commit is far simpler.
  • Use git filter-repo --to-subdirectory-filter on the incoming repo for a cleaner result than git mv.

How to stop it happening again

  • Decide early whether related projects belong in one repository.
  • If you expect to consolidate later, keep each project's files under a top-level directory from the start.

Commands used in this guide

git remote

Manage the named URLs your repository syncs with.

git fetch

Download new commits from the remote without changing any of your files.

git merge

Combine another branch's history into the current one.

git subtree

Vendor another repository into a subdirectory - without submodules' friction.

git filter-repo

Rewrite an entire repository's history - the supported way to purge a leaked secret or a huge file.

Still stuck?

Search the full command reference and every other rescue guide — there are 56 of them, covering everything from detached HEAD to force-push disasters.

Browse all guides →

Frequently asked questions

How do I merge two Git repositories keeping both histories?

Add the second repo as a remote, fetch it, then git merge <remote>/main --allow-unrelated-histories. Move the incoming files into a subdirectory beforehand so root-level files don't collide.

Why do I need --allow-unrelated-histories?

Because the two repositories share no common ancestor, so Git has no base to merge against and refuses by default. The flag confirms you genuinely intend to join two independent histories.

Will I keep the original commit authors and dates?

Yes. A merge copies the existing commit objects unchanged, so authors, dates, messages and hashes from both sides are preserved exactly.

Related rescue guides

← All Git Rescue guides