tags:

views:

3128

answers:

7

I've been doing all my work in Git and pushing to GitHub. I've been very happy with both the software and the site and I have no wish to change my working practices at this point.

My PhD adviser is asking all students to keep their work in SVN. I've found tons of documentation and tutorials about to pull down an existing SVN repo into git, but nothing about pushing a git repo to a fresh SVN repo. I expect there must be some way to do this with a combination of git-svn and a fresh branch and rebasing and all those wonderful terms, but I'm a git newbie and don't feel confident with any of them.

I then want to just run a couple of commands to push commits to that SVN repo when I choose, I wish to keep using Git and just have the SVN repo mirror what's in Git.

I'll be the only person ever committing to SVN, if this makes any difference.

Any instructions on how to do this would be very much appreciated!

A: 

You can make a new svn repo. Export your git project (fleshing out the .git files). Add it to the svn repo (initializing the repo with what you had so far in git). Then use the instructions for importing svn repos in a fresh git project.

But this will loose your previous git history.

Vasil
+1  A: 

If you want to keep on working with git as your main repository and just need to "export" the revisions to svn from time to time, you could use tailor to keep the svn repository in sync. It can copy revisions between different source control systems and would update the svn with the changes you make in git.

I haven't tried a git -> svn conversion, but for a svn -> svn example see this answer.

sth
+4  A: 

Create a new directory in the subversion repository for your project.

# svn mkdir --parents svn://ip/path/project/trunk

Change to your Git-managed project and initialize git-svn.

# git svn init svn://ip/path/project -s
# git svn fetch

This will create a single commit because your svn project directory is still empty. Now rebase everything on that commit, git svn dcommit and you should be done. It will seriously mess up your commit dates, though.

Bombe
I used this answer with the instructions at http://hassox.blogspot.com/2007/12/using-git-with-svn.htmlI followed these commands, then "#git branch -a" to see the trunk name. Then: # git checkout -b local-svn trunk# git merge master# git svn dcommitRemember to .gitignore the .svn directory!
Lewisham
As I just did the same operation, I wanted to make it explicit that for some time now (January '09), git can perform rebase operation on the root commit. This makes the process much simpler than a lot of the old articles indicate, see comments on http://its.arubything.com/2009/1/4/commit-a-linear-git-history-to-subversion
Louis Jacomet
+23  A: 

I needed this as well, and with the help of Bombe's answer + some fiddling around, I got it working. Here's the recipe:

Import git -> svn

1. cd /path/to/git/localrepo
2. svn mkdir --parents protocol:///path/to/repo/PROJECT/trunk -m "Importing git repo"
3. git svn init protocol:///path/to/repo/PROJECT -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

After #3 you'll get a cryptic message like this:

Using higher level of URL: protocol:///path/to/repo/PROJECT => protocol:///path/to/repo

Just ignore that.

When you run #5, you might get conflicts. Resolve these by adding files with state "unmerged" and resuming rebase. 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.

troelskn
Why is the "git rebase trunk" step needed. What would happen if this step is skipped? Would the dcommit step still merge all of the commits to the SVN repo?
Casey
Nice answer. Does this also mess up the commit dates?
Drew Noakes
Good questions - Unfortunately, I don't know the answer to either. This is more a practical guide of what I found to work. I don't fully understand all the details. Regarding the commit-dates, I guess you could make a test and find out. Remember that you can init a local (fs-based) svn repo, for testing things out.
troelskn
Ok so I got less lazy and tried it out. Works fine but yes, the commits all appear to be made moments apart starting at the moment you fire off step #6. A note to others trying this, make sure the URL used in step 3 doesn't end with `trunk`.
Drew Noakes
worked great with some minor mucking around. oddly, init "-s" didn't make initial commit so I got error "unable to determine upstream SVN information" to fix, i created and committed a dummy file to SVN then git svn fetch. smooth sailing after that. i think i owe you a beer!
burkestar
+3  A: 

Using git rebase directly will lost the first commit. Git treats it different and cant rebase it.

There is a procedure that will preserve full history: http://kerneltrap.org/mailarchive/git/2008/10/26/3815034

I will transcribe the solution here, but credits are for Björn.

Initialize git-svn:

git svn init -s --prefix=svn/ https://svn/svn/SANDBOX/warren/test2

The --prefix gives you remote tracking branches like "svn/trunk" which is nice because you don't get ambiguous names if you call your local branch just "trunk" then. And -s is a shortcut for the standard trunk/tags/branches layout.

Fetch the initial stuff from svn:

git svn fetch

Now look up the hash of your root commit (should show a single commit):

git rev-list --parents master | grep '^.\{40\}$'

Then get the hash of the empty trunk commit:

git rev-parse svn/trunk

Create the graft:

echo <root-commit-hash> <svn-trunk-commit-hash> >> .git/info/grafts

Now, "gitk" should show svn/trunk as the first commit on which your master branch is based.

Make the graft permanent:

git filter-branch -- ^svn/trunk --all

Drop the graft:

rm .git/info/grafts

gitk should still show svn/trunk in the ancestry of master

Linearize your history on top of trunk:

git svn rebase

And now "git svn dcommit -n" should tell you that it is going to commit to trunk.

git svn dcommit
Can you explain how this technique is different from above more clearly.
Casey
A: 

I love Git and the fact that I can have my own local repository and versions before ‘push’ing the changes to a common repository has made me a fan. intranet software

Cristin