I and my small team work in Git, and the larger group uses Subversion. I'd like to schedule a cron job to publish our repositories current HEAD
s every hour into a certain directory in the SVN repo.
I thought I had this figured out, but the recipe I wrote down previously doesn't seem to be working now:
git clone ssh://me@gitserver/git-repo/Projects/ProjX px2
cd px2
svn mkdir --parents http://me@svnserver/svn/repo/play/me/fromgit/ProjX
git svn init -s http://me@svnserver/svn/repo/play/me/fromgit/ProjX
git svn fetch
git rebase trunk master
git svn dcommit
Here's what happens when I attempt:
% git clone ssh://me@gitserver/git-repo/Projects/ProjX px2
Cloning into 'ProjX'...
...
% cd px2
% svn mkdir --parents http://me@svnserver/svn/repo/play/me/fromgit/ProjX
Committed revision 123.
% git svn init -s http://me@svnserver/svn/repo/play/me/fromgit/ProjX
Using higher level of URL: http://me@svnserver/svn/repo/play/me/fromgit/ProjX => http://me@svnserver/svn/repo
% git svn fetch
W: Ignoring error from SVN, path probably does not exist: (160013): Filesystem has no item: File not found: revision 100, path '/play/me/fromgit/ProjX'
W: Do not be alarmed at the above message git-svn is just searching aggressively for old history.
This may take a while on large repositories
% git rebase trunk master
fatal: Needed a single revision
invalid upstream trunk
I could have sworn this worked previously, anyone have any suggestions? Thanks.
There are a few issues with your approach:
git svn init
. Rebasing assumes a common ancestor, but if your git repo was previously initialised via git init
, then git svn init
will create a second root (i.e. parent-less) commit, and rebasing from one tip to the other will not work without --onto
.-s
option to git svn init
, which causes it to search for branches/
, tags/
, and trunk/
. As the warning (Using higher level...
) clearly states, this results in the git-svn
config pointing at the top of the svn repo, not the fromgit/ProjX
subdirectory.trunk
even though there's no good reason for this branch to exist; git svn init
actually creates a tracking branch called remotes/git-svn
.So the actual sequence you want is:
# 1st time only
svn mkdir --parents http://me@svnserver/svn/repo/play/me/fromgit/ProjX
mkdir px2
cd px2
git svn init http://me@svnserver/svn/repo/play/me/fromgit/ProjX
git svn fetch
Now hacking can occur concurrently in git and svn. Next time you want to dcommit
from git to svn, you simply do:
cd px2
git svn rebase
git svn dcommit
If you already initialised the git repository, started hacking in it, and need to transplant that history into svn, then the first-time-only sequence is more difficult because you need to transplant all the git history into svn, even though they don't share a common ancestor:
# 1st time only
svn mkdir --parents http://me@svnserver/svn/repo/play/me/fromgit/ProjX
git clone ssh://me@gitserver/git-repo/Projects/ProjX px2
cd px2
git svn init http://me@svnserver/svn/repo/play/me/fromgit/ProjX
git svn fetch
# transplant original git branch onto git-svn branch
root_commit=$( git rev-list --reverse HEAD | head -n1 )
git tag original-git
git reset --hard $root_commit
git reset --soft git-svn
git commit -C $root_commit
# N.B. this bit requires git >= 1.7.2
git cherry-pick $root_commit..original-git
# For older gits you could do
# git rev-list $root_commit..original-git | xargs -n1 git cherry-pick
# or use git rebase --onto but that requires jumping through some
# hoops to stop moving remotes/git-svn.
Subsequently, do the same svn rebase
and dcommit
as before.
In case anyone wants to test this approach in a sandbox, you can download my test script. I'd recommend you do a visual security audit before running though ;-)
I1 would like to split the problem into a couple of issues:
My proposal is based on SubGit2.
If you have local access to Subversion repository at svnserver, the setup is pretty much straight-forward and well documented at SubGit Book:
Let's assume that Subversion repository is located at $SVN_REPO and Git repository is located at $GIT_REPO. First, run the following command:
$ subgit configure $SVN_REPO
Then adjust generated $SVN_REPO/conf/subgit.conf file as follows:
[git "ProjX"]
repository = $GIT_REPO
translationRoot = /play/me/fromgit/ProjX
trunk = trunk:refs/heads/master
branches = branches/*:refs/heads/*
shelves = shelves/*:refs/shelves/*
tags = tags/*:refs/tags/*
I'm not quite sure regarding translationRoot option for your case. The value must be a project path relative to SVN repository root, please specify a proper one.
Don't forget to remove other 'git' sections, so SubGit won't translate those parts of SVN repository into Git.
Optionally you can adjust $SVN_REPO/conf/authors.txt file in order to specify how Git committer names should be translated into SVN author names:
SvnAuthor = Git Committer <[email protected]>
Finally, import your Git repository into SVN:
$ subgit install $SVN_REPO
At this moment $SVN_REPO and $GIT_REPO have special hooks installed by SubGit. Subversion and Git servers trigger these hooks on every incoming modification. This way SubGit automatically synchronizes SVN and Git repositories. Created mirror is bi-directional, i.e. some of developers may use SVN clients others may choose any Git clients.
In case you don't need such kind of synchronization, just disable it:
$ subgit uninstall [--purge] $SVN_REPO
If you don't have local access to Subversion repository, things get much more tricky but still possible:
First, fetch the whole SVN repository to your machine, so you can access it locally:
$ svnadmin create repo
$ svnrdump dump http://me@svnserver/svn/repo | svnadmin load repo
Now keep in mind the latest revision of fetched repository:
$ svn info file:///path/to/repo
Path: repo
URL: file:///path/to/repo
Repository Root: file:///path/to/repo
Repository UUID: cbc56e97-717f-4d50-b705-cb6de2c836eb
Revision: $LATEST_REVISION
Node Kind: directory
Last Changed Author: SvnAuthor
Last Changed Rev: $LATEST_REVISION
Last Changed Date: 2012-10-27 14:01:38 +0200 (Sat, 27 Oct 2012)
Then repeat all the steps from a previous clause. So the local SVN repository should store imported Git history afterwards.
Finally, send the generated history back to SVN:
$ svnsync initialize http://me@svnserver/svn/repo file://path/to/repo
$ svn propset --revprop -r0 svn:sync-last-merged-rev $LATEST_REVISION http://me@svnserver/svn/repo
$ svnsync synchronize http://me@svnserver/svn/repo
Note that remote repository must have pre-revprop-change hook enabled in order to make svnsync work.
If you have local access to Subversion repository, you can keep SubGit working after you've installed it. As I already mentioned it translates changes immediately after they were sent to one of repositories, so there's no need to maintain any cron job in this case.
If you have no local access to Subversion repository but still would like to use SubGit, I'd recommend you to use svnsync to synchronize SVN repository hosted at svnserver and SVN repository controlled by SubGit3.
You may find a guide on how to setup such kind of mirror in this HOW-TO.
Please note that for this case your project should have a separate Subversion repository, otherwise it's very hard to keep svnsync working properly.
If for any reason you decide to use git-svn
, I'd recommend you to create a fresh clone of Subversion repository after you've imported Git history with SubGit:
$ git svn init -s http://me@svnserver/svn/repo/play/me/fromgit/ProjX
$ git svn fetch
From my experience, importing Git history into SVN is not what git-svn
was made for. On the other side SubGit handles that well.
1 Full disclosure: I'm one of SubGit developers.
2 SubGit is a commercial product but it is free for small teams with up to 10 committers. I think it applies for your case.
3 Our team is working on SubGit 2.0 which supports synchronization of SVN and Git repositories located on different hosts. Basically, you can create Git repository anywhere and install SubGit into it with SVN URL specified. After that you can work with any of these repositories — changes get translated automatically between them.
We're going to publish an EAP build with that functionality, so may try it soon.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With