Recently, I needed to extract a number of directories from a git project each into their own projects. The original directory structure looked like
project/
subprojects/
Foo/
Bar/
Baz
My first attempt was to simply follow the instructions in this great StackOverflow answer:
# copy the project:
git clone project/ foo
cd foo
# delete the remote so we don't mess it up
git remote rm origin
# delete all tags
git tag -l | xargs git tag -d
# go through every commit and delete everything that isn't in subprojects/Foo,
# also removing any commits that don't touch that directory at all:
git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter subprojects/Foo -- --all
# clean up:
git reset --hard
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
git reflog expire --expire=now --all
git gc --aggressive --prune=now
Then I realized that subprojects/Foo
had formerly been subprojects/foo
, and likewise for the others. After some more serious searching on git filter-branch multiple directories
, I found this StackOverflow question. I figured out that I needed to enumerate all of the things that weren't in the history of subprojects/foo
and exclude those.
This would be a pretty tough task if I had to extract foo
from the whole project. Instead, I realized that foo
's whole history was within subprojects/
, so I extract that first, using the single-directory method above:
git clone project/ subprojects
cd subprojects
git remote rm origin
git tag -l | xargs git tag -d
git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter subprojects-- --all
git reset --hard
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
git reflog expire --expire=now --all
git gc --aggressive --prune=now
Now I could more easily extract foo
from that:
git clone subprojects foo
cd subprojects
# go through every commit and remove the parts that affect the Bar and Baz projects,
# in all their forms:
git filter-branch --prune-empty --index-filter 'git rm --cached --ignore-unmatch bar Bar BAR baz Baz BAZ' HEAD
giv mv Foo/* ./
git commit -m "move the Foo project to root"