Git and I normally have this fabulous relationship where Git does whatever I tell it to do and I am completely in love with the fact that it actually works. However, Git and I are having one of those trying times in our relationship where I want it to do something that seems complete impossible… and while Git does come with instructions, they are not very good if you want to do something hard or weird.
Here is the short version of the story: I am working on a project that is using a remote SVN repository. I have been using Git-SVN, since there have been (a few too many) times where I need to work on several tasks at once and not be able to commit anything immediately. So:
> git branch foo
> git checkout foo
[hack.. hack... hack...]
> git commit
[days go by]
> git svn dcommit
Easy enough, right? This has been working wonderfully for weeks; however all of a sudden:
> git branch bar
> git checkout bar
[hack.. hack... hack...]
> git commit
[days go by]
[oh.. by the way, can you commit your changes to this other branch instead?]
Crap. I now have several commits in my new branch that have been committed to Git, but now need to be committed to a different SVN branch. Now I need to find a way to do my svn switch in Git.
One would think there would be a simple command for this. But… um… no. Now, there are a number of forum and blog posts that pop up on the topic if you do a Google search for git svn switch. Most of those results did not work for me at all; however I did find one post from www.beletsky.net that helped tremendously. The first part of the solution to my problem is snipped from that post (with my own annotations to help figure out what is going on here).
1. Modify your Git config file.
Mine was in .git/config, but I understand that they could all be in different locations. You probably have something in there already that looks like this:
[svn-remote "svn"]
url = [OLD-REPO]
fetch = :refs/remotes/git-svn
You will need to add an additional entry in the config file that reads:
[svn-remote "svnnew"]
url = [NEW-REPO]
fetch = :refs/remotes/git-svn-new
2. Fetch your changes from the new SVN repository.
Just a note: this will take a very long time, depending on the size of your SVN repository. Go grab a coffee or something after firing this off.
> git svn fetch svnnew
3. Checkout your new SVN repository in Git.
This puts you into something called “detached HEAD” state. I’m not entirely sure what that means, but it basically allows you to create a new base branch in Git for your new SVN repository.
> git checkout git-svn-new
4. Create your new Git branch.
This will create a new Git branch that will now be associated with your new SVN repository.
> git checkout -b master-new
5. Look up the commit(s) from your old branch.
You will need the commit IDs from your old branch, for use when you merge the changes into your new branch.
> git checkout old-branch
> git log --pretty=oneline
You should get output something like the output below. This is the commit ID followed by the commit message you entered when you did your git commit. Note that the most recent commit is at the top of the list. Jot down all of the commits you want to move over to your new branch.
f8dace1a7b96ee59a6b5237920eb18da49fed0bf Commit #3
e2a93ad2f20621c33625cbc37f7a3818236ad761 Commit #2
6f72923317778e32fd67495c3599b825ead05f10 Commit #1
6. Cherry pick your commits.
You can use the git cherry-pick command to move your commits (one by one) from your old branch to your new branch. Use the commit ID that you grabbed from the step above. If you need to move over more than one commit, you will need to cherry pick each commit individually, starting with the commit that was committed first. The example below moves over commits #2 and #3 from the list above. Notice which commit ID I use first.
> git checkout master-new
> git cherry-pick e2a93ad2f20621c33625cbc37f7a3818236ad761
> git cherry-pick f8dace1a7b96ee59a6b5237920eb18da49fed0bf
It is entirely possible that you will have code conflicts as you cherry pick your commits, which is one of the reasons why you should do each commit separately. Git will let you know if there is a conflict. (Here is more information on cherry picking and conflicts.)
Voila!
Once you are done, there are a couple of commands you can run to verify everything is correct:
> git svn info # gives you information on what SVN branch you are synced to
> git log # gives all commits in your Git branch; use to confirm your are in the new branch
