Skip to content

Instantly share code, notes, and snippets.

@jamesarosen
Created October 24, 2012 16:13
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save jamesarosen/3947029 to your computer and use it in GitHub Desktop.
Save jamesarosen/3947029 to your computer and use it in GitHub Desktop.
Extracting a directory from a git project

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"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment