Splitting a git repo
Its been almost an year since I last blogged and what can I say, micro-blogging killed blogging for me. Even micro-blogging has got to a point, I don't do as much as I used to. Perhaps the end of web 2.0 or perhaps I'm getting too old for this :)
Anyhow, getting back to the subject of this quick post, it seems splitting a git repo into two separate git repos is somewhat obscure and required a bit of googling around. Fortunately I came across this great blog post, but wanted to summarize it in one place (the author made me look at several pages to put it together).
Say I have a git project called foo.repo which had a subdirectory called bar, that I now want to make its own separate git project called bar.repo.
Current state
target state
Anyhow, getting back to the subject of this quick post, it seems splitting a git repo into two separate git repos is somewhat obscure and required a bit of googling around. Fortunately I came across this great blog post, but wanted to summarize it in one place (the author made me look at several pages to put it together).
Say I have a git project called foo.repo which had a subdirectory called bar, that I now want to make its own separate git project called bar.repo.
Current state
foo.repo/
.git/
bar/
abc/
xyz/
target state
foo.repo/
.git/
abc/
xyz/
bar.repo/
.git/
- Step 1 : Clone existing repo as desired repo on the local clone
$ git clone --no-hardlinks foo.repo bar.repo
- Step 2: Filter-branch and reset to exclude other files, so they can be pruned:
$ cd bar.repo
$ git filter-branch --subdirectory-filter bar HEAD -- --all
$ git reset --hard
$ git gc --aggressive
$ git prune- Step 3: Create new empty repo on git server
$ mkdir /var/git/bar.git
$ cd /var/git/bar.git
$ git --bare init- Step 4: Back on the local machine, replace remote origin to point to new repo
$ cd bar.repo
$ git remote rm origin
$ git remote add origin git@git-server:bar.repo
$ git push origin master- Step 5: Remove bar directory from original foo.repo
$ git filter-branch --tree-filter "rm -rf bar" --prune-empty HEAD
or supposed to be faster, I haven't tried$ git filter-branch --index-filter "git rm -r -f --cached --ignore-unmatch bar" --prune-empty HEAD
Comments
In case, if you only wanted to manage bar.repo as a separate repo from the main repo you could've achieved it using Git Submodules.
How do I commit the changes which have been made to the original foo repo. This is what I get at home after cleaning it out:
$ git s
# On branch master
# Your branch and 'origin/master' have diverged,
# and have 473 and 487 different commits each, respectively.
#
nothing to commit (working directory clean)
Do I just push it back or do I have to do anything extra such as a commit?
"# Your branch and 'origin/master' have diverged"
So you should be able to
$ git push origin master
Hopefully no one else has committed to origin where you'd have to pull & possibly deal with a merge conflict.
I try the push as advised but get knocked back (see below). When I do a pull to try to fix the conflicts I just pull down the folder which has been just removed with filter-branch. I gather that I may be able to force the push with -f. Any comments?
$ git push origin master
To git@bitbucket.org:xxx/yyy.git
! [rejected] master -> master (non-fast-forward)
error: failed to push some refs to 'git@bitbucket.org:xxx/yyy.git'
To prevent you from losing history, non-fast-forward updates were rejected
Merge the remote changes (e.g. 'git pull') before pushing again. See the
'Note about fast-forwards' section of 'git push --help' for details.
Many thanks.
Don't force it!
Have a read of my other blog post where I talk about using fetch instead of pull
http://www.geekaholic.org/2012/05/peer-to-peer-collaborative-development.html
C
|\
B|
|/
A
In order to eliminate the useless merge commit C, a filter-branch command with parent-filter is required.