Moving A Subdirectory Into A Separate Git Repository
Like many people I have my dotfiles under version control. Until today I kept my Vim configuration in that repo too, but this morning I promoted the Vim stuff to its own repo.
Although this was relatively straightforward, the submodules in my vim/bundle
directory added some complexity.
This was the directory layout I started with:
dotfiles |-- .gitmodules |-- bashrc |-- [...etc...] |-- vim | |-- README.md | |-- autoload | | `-- pathogen.vim | |-- bundle | | |-- vim-cucumber | | `-- [...etc...] |-- gvimrc `-- vimrc
I wanted to move vimrc
, gvimrc
, and vim/
to a new dotvim
repo, preserving their history. This Stack Overflow question, “Detach subdirectory into separate Git repo”, was invaluable.
Moving the subdirectory to a separate Git repo
First we clone the dotfiles
repo to a new dotvim
repo.
$ git clone --no-hardlinks /path/to/dotfiles /path/to/dotvim
Next we discard everything other than the subdirectory we want, vim
, promoting it to the root level.
$ cd /path/to/dotvim $ git filter-branch --subdirectory-filter vim HEAD -- --all $ git reset --hard
Now we get rid of the objects we are no longer interested in.
$ git gc --aggressive $ git prune
Next we replace the reference to the original repo, dotfiles
, with our new remote
repo on GitHub.
$ git remote rm origin $ git remote add origin git@github.com:airblade/dotvim.git
Now we can push everything up to GitHub.
$ git push origin master
To make this easier in future, I ran a little script which I have come to love:
$ git track tracking origin/master
Now I can git push
without worrying about the remote name or branch name.
Fixing the submodules in the new repo
Although the submoduled directories are there in the new repo, they are empty because the repo doesn’t know they are submodules. Rectify this by copying the .gitmodules
file from the original repo and editing it to correct the submodules' paths.
$ cp /path/to/dotfiles/.gitmodules /path/to/dotvim $ cat /path/to/dotvim/.gitmodules [submodule "vim/bundle/vim-cucumber"] path = vim/bundle/vim-cucumber url = https://github.com/tpope/vim-cucumber.git ...etc...
Fix all the paths, e.g. edit in Vim and :%s|vim/||
. You want:
[submodule "bundle/vim-cucumber"] path = bundle/vim-cucumber url = https://github.com/tpope/vim-cucumber.git
Now you can do:
$ git add .gitmodules $ git bundle update --init $ git commit -m "Update bundles."
Removing the subdirectory from the original repo
Now we have moved the subdirectory into its own repo, we want to remove it from the original repo. If it didn’t contain any git submodules, there would be two ways to do this.
To simply remove it:
$ cd /path/to/dotfiles $ rm -rf vim $ git rm -r vim $ git commit -m "Extracted vim into its own repo."
This would leave the vim
subdirectory in the repo’s history. If instead you wanted dotfiles
to look as if vim
had never been there, you would do:
$ cd /path/to/dotfiles $ git filter-branch --index-filter "git rm -r -f --cached --ignore-unmatch vim" --prune-empty HEAD
However we do have submodules and we need to remove those first (probably – I’m not certain).
- Delete the relevant line(s) from
.gitmodules
. - Delete the relevant section from
.git/config
. (There wasn’t anything relevant in my.git/config
.) - Run
git rm --cached path_to_submodule
(no trailing slash). - Commit and delete the now untracked submodule files.
Thanks again, Stack Overflow.
Now we can remove the vim
subdirectory as above.
Moving the files vimrc
and gvimrc
to the new repo
The protocol above discards everything other than the vim
subdirectory including, unfortunately, the vimrc
and gvimrc
files. I couldn’t see a neat way to keep those as well as the subdirectory.
However I didn’t care too much about migrating their histories, so I just copied them across.
$ cp /path/to/dotfiles/{vimrc,gvimrc} /path/to/dotvim/ $ cd /path/to/dotfiles $ git rm {vimrc,gvimrc} $ git commit -m "Moved vimrc and gvimrc to dotvim repo." $ cd /path/to/dotvim $ git commit -am "Moved from dotfiles repo. See there for their history."
Done
That’s it. You can see my new dotvim repo kept its history, and the original dotfiles repo is without Vim.