Disclaimer: I HAVE NO IDEA WHAT I AM DOING.
How to use git to extract a Module living in lib/
of your monorail application into its own gem, saving its commit history and including its tests.
If you are like me, your module probably lives in the following directory structure:
lib/
conductor.rb
conductor/
headgear.rb
demeanor.rb
demeanor/
laxidasical.rb
enthusiastic.rb
comic.rb
railcar.rb
railcar/
manufacturer_origin.rb
manufactyrer_origin/
italy.rb
germany.rb
spec/
models/
conductor_spec.rb
conductor/
demeanor_spec.rb
railcar_spec.rb
railcar/
italy_spec.rb
germany_spec.rb
Most StackOverflow solutions only solve for the sub-directories. Nothing allowed the ability to include the root module file (i.e. lib/conductor.rb
)
Start with a clean copy of your application. There is a good chance you’re about to mess things up real badly:
git clone --no-hardlinks git@github.com:jdoe/monorail.git extract_conductor
cd extract_conductor
Pull your lib/
and spec/models
folders into their own separate branches:
git filter-branch -f --tag-name-filter cat --subdirectory-filter lib --prune-empty lib-extraction
git filter-branch -f --tag-name-filter cat --subdirectory-filter spec/models --prune-empty spec-models-extraction
Repeat as neccessary.
If you checkout those branches, you’ll see that the contents of lib/
and spec/models
are now in the root directory of the repo, including some modules that we don’t care about. Not quite what we want, but it solves the problem of losing our lib/conductor.rb
file.
We now have more than what we want, so let’s ditch the modules we aren’t extracting:
git checkout lib-extraction
git filter-branch -f --index-filter 'git rm -r --cached --ignore-unmatch railcar' --prune-empty
git filter-branch -f --index-filter 'git rm -r --cached --ignore-unmatch railcar.rb' --prune-empty
... etc. until you are left with just the files you want.
git checkout spec-models-extraction
git filter-branch -f --index-filter 'git rm -r --cached --ignore-unmatch railcar' --prune-empty
git filter-branch -f --index-filter 'git rm -r --cached --ignore-unmatch railcar_spec.rb' --prune-empty
If I understand it correctly, the above command goes through the entire commit history and removes all references to the files you delete through the git rm -r
sub-command thingie.
Did I mention that I have no idea what I’m doing here?
You should now be left with two branches. One for lib/
and one for spec/models
that only contain your module.
As previously mentioned, the unfortunate effect of filter-branch is that it removes the lib
and spec
folders. To make the merge procedure we’ll make later easier, let’s rebuild that directory structure:
git checkout lib-extraction
mkdir lib
git mv conductor* lib/.
git add .
git commit -m "Gem extraction: Moved Conductor's lib files into their own `lib` folder."
git checkout spec-models-extraction
mkdir spec
git mv conductor* spec/.
git commit -m "Gem extraction: Moved Conductor's spec files into their own `spec` folder."
Sweet. Both of our branches now have the files we want and in a directory structure we want. Let’s make our gem.
cd ..
bundle gem conductor
cd conductor
By default, Bundler will create your lib files for you. If you commit those now, they will take priority over the merge you are about to do, which would make this whole excercise useless. So let’s stash those away for a minute:
git rm --cached lib
git commit -m "Initial commit."
git add lib
git stash save "Initial lib files."
Great. We now have a proper git repo with the standard set of files required for a gem. Let’s get our code into this puppy.
First, let’s create a branch in case we mess something up:
git branch import master
Now let’s connect your new gem repo to your old and busted application repo where the extracted code is waiting:
git remote add ~/Work/monorail conductor-extraction
This creates a link called “conductor-extraction” from your gem repo to your monorail application’s repo. Call it whatever you want.
git pull conductor-extraction lib-extraction
git pull conductor-extraction spec-models-extraction
Voila! Your extracted code is now in your gem’s repo!
Poke around, kick the tires, make sure everything looks as it should. If it doesn’t, you messed something up, not me!
If everything looks OK, merge everything into master.
git checkout master
git merge import
A gem isn’t a gem unless it has a version. Either create the files manually, or retrieve the previously stashed files:
git stash pop
You will almost certainly run into a merge conflict in lib/conductor.rb
. Resolve appropriately.
That’s it! Push and release unto the world!
http://stackoverflow.com/questions/359424/detach-subdirectory-into-separate-git-repository http://stackoverflow.com/questions/4669795/git-surgery-splitting-out-a-single-repository-into-many-repositories http://help.github.com/remove-sensitive-data/
File a GitHub Issue or submit a Pull Request! :+1: