Mirroring SVN to Git
Puszcza now offers a one-way mirror of its SVN repositories to Git. This article explains the techniques used to implement it.
The basic prerequisites are:
- SVN repositories are located under
$SVNROOT
- Git repositorise are located under
$GITROOT
- In both cases, each project has its own repository
- Git version 1.6.0 or later is used
Creating the Mirror
The initial mirror is created using git-svn:
git svn clone -A$AUTHORS -s $SVNROOT/$PROJECT $GITROOT/$PROJECT
It is supposed that $PROJECT
is the project name,
and $AUTHORS
contains full pathname of the authors file.
The -s
switch tells git-svn to assume the
standard layout of the source SVN repository
(i.e. trunk/
, tags/
, branches/
).
When this command finishes, $GITROOT/$PROJECT
will contain
a valid Git repository set up for two-way intercommunication with
the SVN repository $SVNROOT/$PROJECT
. Any existing SVN tags
and branches are mapped to remote Git
branches. A tag $SVNROOT/$PROJECT/tags/foo
becomes
branch tags/foo
. A
branch $SVNROOT/$PROJECT/branches/bar
becomes Git
branch bar
.
This mapping is OK for mirroring, but is a bit inconvenient, because tags and branches are not visible using the gitweb interface.
To fix this, I provide local tracks for each remote branch:
cd $GITROOT/$PROJECT git branch -r | while read tag do case $tag in tags/*) localtag=${tag##tags/} git tag $localtag $tag ;; trunk) ;; *) git branch --track ${HEADPFX}$tag $tag ;; esac done
Note $HEADPFX
in the last git-branch
invocation. Theoretically, a local branch can have the same name as
the remote one it tracks. However such setup makes git
issue lots of spurious diagnostics in the form:
warning: refname 'release_4_4_patches' is ambiguous.
when doing various Git operations, e.g. git checkout
.
It is also possible that such duplicate names could cause other,
more serious problems with Git. I have not checked this, so I am not
sure. Nevertheless, I prefer to stay on the safe side and to prefix
all local branches with some unique string, so they differ from the
remote ones. In my config I set HEADPFX=git-
.
To finish the initial set up, we also need to install a pre-receive hook, which will forbid pushing to this repository.
I use the following hook:
#! /bin/sh SVN=$(git config --get svn-remote.svn.url) cat >&2 <<EOT ---------------------------- WARNING! ---------------------------- This repository is a one-way mirror of the SVN repository at $SVN Pushing to this repository is administratively prohibited. If you wish to drop SVN and switch to Git, please drop an email to <root@domain> saying so. Make sure the email is GPG-signed. ---------------------------- WARNING! ---------------------------- EOT exit 1
The git-svn-mirror-create
script does all that. To create a mirror of a
project foo
one will simply
run: git-svn-mirror-create foo
.
The created repository is not bare. To provide a
usual project.git
repository, I place Git mirrors in a
subdirectory, and then symlink the $PROJECT/.git
subdirectory to $GITROOT/$PROJECT.git
.
Maintaining the Mirror
Basically, the following commands will keep the currently checked out branch in sync with the SVN:
git svn fetch git svn rebase -l
However, this will not update other branches, so the same operation needs be repeated for each branch:
cd $GITROOT/$PROJECT git branch | sed 's/^\*//' | tr -d ' ' | tee $BRANCHLIST | while read branch do git checkout $branch git svn fetch git svn rebase -l done git checkout master
The file $BRANCHLIST
keeps a temporary list of
branches. This list is used by the code below to determine whether
any new branches were created in the SVN, and, if so, to mirror
them:
echo "Checking for new branches & tags..." git tag > $TAGLIST git branch -r | while read tag do case $tag in tags/*) localtag=${tag##tags/} if ! grep -q "^${localtag}\$" $TAGLIST; then echo "Mapping $tag to $localtag" git tag $localtag $tag fi;; trunk) ;; *) if ! grep -q "^${HEADPFX}${tag}\$" $BRANCHLIST; then echo "Tracking $tag as ${HEADPFX}$tag" git branch --track ${HEADPFX}$tag $tag fi;; esac done
These operations are performed by the script git-svn-mirror-synch, which takes the project name as its argument. This script should be executed as a cron job.
Unbinding the Mirror
After running SVN and Git in parrallel for some time, the project maintainer will probably decide to drop SVN and to switch to Git entirely.
This is done by git-svn-mirror-finish script. This script does the following:
- Removes remote branches;
- Renames local branches, by removing the
$HEADPFX
prefix; - Removes SVN-related sections from
.git/config;
- Disables the pre-receive hook;
The sysadmin is then supposed to convert the repository to bare format and to remove checked out sources. E.g.:
cd .svn-mirror mv .svn-mirror/$PROJECT/.git $PROJECT.git rm -rf .svn-mirror/$PROJECT/ cd $PROJECT.git git config bool core.bare true
Wyslij-po 2.0
As part of the update compain following the release of GNU Mailutils 2.0, the version 2.0 of wyslij-po is released.
New version of gnupload script.
Id est, patres conscripti...
BackUPS Pro 280 Battery Constant
For the record: the battery constant for BackUPS Pro 280 is BA.
If this model shows incredibly low runtime on a new, 100% loaded battery and the state persists even after calibration, try resetting the constant to this value. To do so, follow these instructions.