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.


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" ]
    mkdir --parents "${dir}"
    git ls-tree --name-only "$GIT_COMMIT" | xargs -I files mv files "$dir"

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)

  • 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

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).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.