I have a github-based git repository that represents development up to a certain point, and then a svn repository, not initialized with git svn, that has further development. I want to bring the svn changes into the git repository, start using the git repo for development, and push changes using git svn dcommit. Is this possible? Is it advisable?
Here’s my specifics:
We started development on a WordPress plugin here:
http://github.com/mrdoornbos/wpconfidentcaptcha
Master is at ef82b94a1232b44aae3e, and no further changes were made in github.
When our application to wp-plugins.org was accepted, an empty svn repo was created for us:
http://svn.wp-plugins.org/wp-confident-captcha/trunk@278927
Somewhat modified files were then copied in (r256425). Further changes were made, the last being r278935.
What I want is for the SVN changes to be applied to master, along with the git svn metadata.
Here’s what I have so far (takes about 4 minutes):
git clone git://github.com/mrdoornbos/wpconfidentcaptcha.git github_cc
cd github_cc
git svn init --stdlayout --prefix="svn/" http://svn.wp-plugins.org/wp-confident-captcha
git svn fetch --revision 256362:278935
This puts my github tree in origin/master, and my svn tree in svn/trunk (and all the tags in their own /svn branches as well). There is no common ancestor between origin/master and svn/trunk. I’m not sure where to go from here, or if there is a way to get the changes from svn/trunk onto origin/master, so that the head of the two repos have identical files, and let git svn dcommit work from origin/master.
Starting over with a new github repo seems like the most straightforward way, and I wouldn’t be sad about losing the early history. But, it seems like there should be a way to make this work with the existing github repo.
(Edit: it looks like this was already asked as How to merge two branches without a common ancestor?, but without the git filter-branch example needed to make it work. Unlike that question, these are public svn and git repos, so an answer with a working script is possible.)
Here’s what worked for me:
Import Histories
This part was already described in the question:
Now the history looks like this (friendly commit names in parens):
There are basically two independent histories in the repository, and it will take some gymnastics to join them.
Graft, Merge, and Filter to Rewrite History
In part 2, I use a graft to make the last git commit the parent of the first
svn commit:
Now the merge is super smooth:
This would work, but grafts aren’t pushed to repos. If I stick with the graft strategy, then everyone else who wants to work with the svn repo will have to recreate the graft themselves. This is easy enough to script, but this is a case where I can do better, using
git filter-branch. This command is used to re-write git history, and has some really powerful options. However, the default command does exactly what I want: recompute commit hashes, taking into account any ‘fake’ parents added by grafts:Now the git history looks like a proper sequence of changes, and others will see the same sequence without messing around with grafts.
Recreate git-svn Metadata
Git is happy, but git-svn isn’t:
git-svn keeps it’s own metadata about commits (in .git/svn/*), and looks to the refspec refs/remotes/svn/trunk branch (as set in the config during git svn init) to determine what the svn head commit is. I need to point the svn trunk to the new commit, and then recreate the metadata. This is the part that I’m not 100% sure about, but it works for me:
Recreate git-svn Metadata on Clone
If someone is cloning from my git repo, they get most of the git-svn metadata in the form of commit messages, but not enough to use git-svn themselves. Most people won’t need to, but someday I’ll need to set up a new computer or train my replacement. Here’s what worked for me:
Now the svn trunk is ready. To get the tags, I had to re-fetch:
I’m not sure if this exact sequence will work after there is more history in the tree.
I got some messages during
git svn rebase:W: Refspec glob conflict (ref: refs/remotes/svn/trunk): expected path: wp-confident-captcha/branches/trunk real path: wp-confident-captcha/trunk Continuing ahead with wp-confident-captcha/trunkI fixed these by manually setting the svn configuration in .git/config:
Summary
This is a lot of work to make
git svn rebaseandgit svn dcommitwork. I learned a whole lot about git and git svn, but I’m not convinced the end goal was worth it. For this use case (occasionally update an svn repository to the HEAD of the git repository), some custom scripts might have been more effective.