12

Let's say I've got two repositories aye and bee and I want to get rid of bee, in such a way that the linear history of bee/master is "replayed" in a new subdirectory (several levels deep) of aye. I just want the files and the commit messages, I don't care about commit IDs. I do want a sensible history, so git subtree add --prefix=subdirectory and git read-tree --prefix=subdirectory/ are not what I'm looking for.

Both repositories are private, so there's no risk of rewriting history for someone else. However, bee does have a submodule cee.

l0b0
  • 51,350

2 Answers2

14

First, rewrite bee's history to move all files into the subdirectory:

cd /path/to/bee
git filter-branch --force --prune-empty --tree-filter '
dir="my fancy/target directory"
if [ ! -e "$dir" ]
then
    mkdir --parents "${dir}"
    git ls-tree --name-only "$GIT_COMMIT" | xargs -I files mv files "$dir"
fi'

git log --stat should show every file appearing under my fancy/target directory. Now you can merge the history into aye with ease:

cd /path/to/aye
git remote add -f bee /path/to/bee
git checkout -b bee-master bee/master
git rebase master
git checkout master
git rebase bee-master

Recreate the submodule in aye:

git submodule add git://my-submodule 'my fancy/target directory/my-submodule'

Finally you can clean up aye:

git rm 'my fancy/target directory/.gitmodules'
git branch --delete --force bee-master
git remote remove bee

You may also have to fix any absolute paths in your repository (for example in .gitignore)

l0b0
  • 51,350
  • Condition check caused an error: /usr/lib/git-core/git-filter-branch: 3: eval: [[: not found. Had to remove lines with if, then and fi and job was done, thanks! Ubuntu 18.04; git 2.22.0; bash 4.4.20 – d9k Jul 26 '19 at 17:44
  • 1
    You're not using Bash then. I've changed it to be more POSIX compatible. – l0b0 Jul 28 '19 at 20:09
4

Much faster version (hat tip to this answer):

git filter-branch --index-filter 'git ls-files -s | sed "s-\t\"*-&newsubdir/-" | GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info && mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"'

Replace newsubdir with any directory or nested directory you like. On macOS, you may have to use gsed instead of sed (install with brew install gnu-sed).