I recently wanted to merge two git repositories together again. TL;DR for the intent behind this, the two repos were so closely related that a change in one repo meant a change / bump in the other one. To me it made sense to just put them together as it makes no sense adding artificial boundaries that make development harder and more error-prone.

How I did it ?

Well, I came across a nifty tool called Josh (Just a Single History ; see here).

It is a tool that does things similar to git filter-branch if you ever dove into this rabbit hole.

See here an example to filter a repo.

In my case I had a repo A that I wanted to integrate into repo B.

I needed to rewrite repo B into a subfolder b and repo A in subfolder a.

I used the following in repo B :

First let's move B into subfolder b.

# puts branch into FETCH_HEAD
git fetch origin main

# Puts rewritten branch into FILTERED_HEAD
josh-filter ':prefix=b' FETCH_HEAD

# Let's check this out
git switch FILTERED_HEAD --detach

# Let's save this branch and use it as new main
git switch main
# Save old branch
git branch old-main
git switch FILTERED_HEAD
# Delete main
git branch -d main
# Create main from current branch
git switch -b main

And tada! We have our first repo rewrite.

For the merge of the A repo, it is quite similar.

In repo B.

git remote add A https://$ADDR
git fetch A main

josh-filter ':prefix=a' FETCH_HEAD

# Now the merge part
git switch FETCH_HEAD
git switch -b new-a

# I use rebase to get a linear history
git rebase main

# Merge it to main
git switch main
git merge --ff-only new-a

And voilà, we get our repo with the history of both repo!

Some problems with this :

  • Signatures are not kept since we modify the commits. This may be a problem for you.
  • We sometimes need to force-push the main branch which should be done cautiously (I recommend creating a backup branch just in case).

Changelog

<2023-04-13 jeu.> : fix example link, add --detach flag that's needed in the git command. I don't use it since I've been using git longer than this option has existed. Interestingly, I use git restore because I think this one makes way more sense >:).