Tuesday, January 15, 2013

Git to SVN

Sometimes cases might arise when you need to transfer projects from Git to SVN, like when you are required to maintain an in-house SVN repository of multiple external Git repositories. In my case I had to transfer two Git repositories (say git1 and git2) to SVN.

Since there are two Git repos, I wanted to set them as different projects in SVN repo. SVN project is simply a folder in the repo server (with trunk/branches/tags subfolders when following standard svn project format).

First step would be to checkout the Git repos to the system.
git clone https://github.com/your-project1 git1
git clone https://github.com/your-project2 git2

The next step would be to follow these set of instructions from each local Git directory to import them to SVN project.
1. cd /path_to/git1
2. svn mkdir --parents protocol:///path/to/repo/PROJECT1/trunk -m "Importing git repo"
3. git svn init protocol:///path/to/repo/PROJECT1 -s
4. git svn fetch
5. git rebase trunk
5.1.  git status
5.2.  git add (conflicted-files)
5.3.  git rebase --continue
5.4.  (repeat 5.1.)
6. git svn dcommit

In step-2, svn mkdir would create both the PROJECT1 and trunk folders and commits the change.
Here protocol:///path/to/repo/ is the http/https location of the SVN repo like http://IP/svn  or the local SVN repo like file:///home/user/svn-repo.

In step-3, "-s" indicates that project follows standard svn trunk/branches/tags folder structure. If not following the non-standard format, we can use --trunk, --branches and --tags flags. Following a non-standard folder structure is complicating the Git-to-SVN process. So prefer the standard structure.

After #3 you'll get a cryptic message like this:
Using higher level of URL: protocol:///path/to/repo/PROJECT1 => protocol:///path/to/repo
Just ignore that.

When you run #5, you might get conflicts. Resolve these by adding/removing files with state "unmerged" and resuming rebase. You might also need to solve content conflicts. To identify the conflicts when the rebase stops at a patch #, use the command:
git status

If there is CONFLICT(add/add) for a file or "added" message in the git status's contents in the unmerged section, then add the file using:
git add conflict-file

If there is a delete/modify message in the status, then we need to remove the file using:
git rm conflict-file

If there is content conflict, then we need to perform the following operations:
git checkout --theirs conflict-file
git add conflict-file
As per [3], when we perform git rebase trunk, our HEAD would be on the svn trunk branch and our git master commits would be on other branch. Hence we need to checkout the file from theirs branch and add the file to SVN.

After we resolve the conflicts as above, type command git rebase --continue.
Eventually, you'll be done; Then sync back to the svn-repo, using dcommit. That's all.

Keeping repos in sync

You can now sync from svn -> git, using the following commands:
git svn fetch
git rebase trunk
And to sync from git -> svn, use:
git svn dcommit

Final note

You might want to try this out on a local copy, before applying to a live repo. You can make a copy of your git-repo to a temporary place, simply using cp -r, as all data is in the repo itself. You can then set up a file-based testing repo, using:
svnadmin create /home/name/tmp/test-repo

And check a working copy out, using:
svn co file:///home/name/tmp/test-repo svn-working-copy

That'll allow you to play around with things before making any lasting changes.

Addendum: If you mess up git svn init

If you accidentally run git svn init with the wrong url, and you weren't smart enough to take a backup of your work (don't ask ...), you can't just run the same command again. You can however undo the changes by issuing:
rm -rf .git/svn
edit .git/config
And remove the section [svn-remote "svn"] section.

You can then run git svn init anew.

References:
1. http://stackoverflow.com/questions/661018/pushing-an-existing-git-repository-to-svn
2. http://stackoverflow.com/questions/8146289/git-how-to-get-theirs-in-the-middle-of-conflicting-rebase
3. http://stackoverflow.com/questions/2959443/why-is-the-meaning-of-ours-and-theirs-reversed-with-git-svn

APPENDIX
Another way of achieving the same would be using https://github.com/guilhermechapiewski/git2svn, where you run a script to transfer the commits from git to svn. But this completely shifts you to SVN and your SVN and Git are delinked. So future commits to SVN will not be reflected to Git and vice versa.





1 comment:

  1. While performing dcommit on one of the projects, my network connection was interrupted. Since I was performing the task on a VM, the VM still showed as if the operation was in progress but in real it got stopped. So, later on after cancelling the operation, I tried performing "git svn fetch" and "git rebase trunk" to sync the git and svn repos, but I received many "needs update" errors. So I had to delete the entire project folder and start afresh again. Using the same folder was pulling/fetching up half cooked svn commits. It might have worked, but I did not want to take risk spending more time in debugging new errors. Hence I had redone the project import again. If any one has a better solution for this problem, please feel free to add a comment. It might be useful for me or for other readers.

    ReplyDelete