Migration von Subversion nach Git

  • Urspruenglicher Autor: Dirk Deimeke
  • Urspruengliches Datum: 18.11.2011

Aus verschiedenen Gruenden wollte ich unsere Subversion-Repositories nach Git migrieren. Die Entscheidung dafuer war unabhaengig davon, dass zwei Projekte, naemlich FAI und Serendipity, etwa zur gleichen Zeit migriert sind.

Git ist ein verteiltes Versionskontrollsystem (DVCS) - in der gleichen Liga spielen auch Bazaar und Mercurial, wobei das „verteilt“ nicht der Grund fuer den Wechsel war. Neben der komprimierten Speicherung und bandbreitenschonenden Uebertragung, spielen die neuen Systeme ihre Staerken vor allem im intelligenten Zusammenfuehren (merging) von unterschiedlichen Versionen aus.

Weitere Vorteile sind (die Liste ist nicht komplett):

  • Es gibt kaum ein System, was besser dokumentiert waere.
  • Kleinerer Administrationsaufwand.
  • Sparen eines Apache-Moduls
  • Alle haben die komplette Historie lokal und koennen auch ohne Server arbeiten.
  • Alle koennen beliebig oft lokal committen und alle Aenderungen erst dann uebertragen, wenn der eigene Teil fertig ist.

Einen sehr guten Einstieg in verteilte Versionskontrollsysteme und insbesondere Git bietet die Ausgabe 130 von Chaosradio Express.

Subversion wurde bei uns ueber dav_svn, ein Modul des Webservers, benutzt. Die Migration musste also in drei Schritten passieren, zuerst die Serverseite, um die Infrastruktur zur Verfügung zu stellen, danach die Migration der bestehenden Projekte und zum Schluss die Umstellung der Clients.

Da Taskwarrior Gitosis einsetzt und ich so immer jemanden habe, den ich fragen kann, habe ich mich fuer Gitosis auf der Serverseite entschieden. Dafuer gibt es bereits eine sehr gute Anleitung, die ich leider ziemlich unuebersichtlich finde, daher fuehre ich hier noch einmal die einzelnen Schritte auf. Unsere Server laufen auf Ubuntu 10.04 LTS und ich bin strikt nach Anleitung vorgegangen.

sudo aptitude install git-core gitosis

Erzeugt einen User „gitosis“ und die nötigen Verzeichnisse unter /srv/gitosis

Danach muss ich den eigenen oeffentlichen SSH-Schluessel auf den Server kopieren und damit das Administrations-Repository initialisieren (darin wird die ganze Konfiguration gemacht).

dirk@client$ scp .ssh/id_rsa.pub server:/tmp/dirk@client.pub
dirk@server$ sudo -H -u gitosis gitosis-init < /tmp/dirk@client.pub

Damit ist die serverseitige Einrichtung bereits abgeschlossen. Unglaublich aber wahr.

Was nicht in der Anleitung steht, jetzt aber lokal erledigt werden sollte, da commits in Git immer Usernamen und E-Mail-Adresse enthalten:

git config --global user.name "John Doe"
git config --global user.email "john@doe.com"
git config --global color.ui always

Der letzte Befehl ist nicht wichtig, der macht es nur huebsch.

Jetzt kann das gerade angelegte Adminrepository ausgechecked werden:

git clone gitosis@server:gitosis-admin.git gitosis-admin-server.git

Das letzte in der Kommandozeile ist der Name, wie das Verzeichnis lokal heissen soll. Ich habe ein „-server.git“ hinzugefügt, da das Verzeichnis sonst nur „gitosis-admin“ geheissen haette.

Jetzt können der Admingruppe weitere User hinzugefügt werden.

In gitosis.conf stand am Anfang nur dirk@client:

[group gitosis-admin]
writable = gitosis-admin
members = dirk@client dirk@workstation

Im Verzeichnis keydir sind die ganzen Schluessel zu finden. dirk@client.pub war schon vorher enthalten.

ls -l keydir/
total 12
-rw-r--r-- 1 dirk dirk 393 Nov  4 10:50 dirk@client.pub
-rw-r--r-- 1 dirk dirk 395 Nov  4 10:54 dirk@workstation.pub

Die Aenderungen werden jetzt an den Server uebertragen.

git add keydir/dirk@workstation.pub
git commit -a -m "User dirk@workstation hinzugefuegt"
git push

Git verhaelt sich ein bisschen anders als Subversion.

  • Bei einem „git commit“ passiert gar nichts, auch wenn sich Dateien veraendert haben.
  • git add datei ; git commit -m „nachricht“ oder git commit -a -m „nachricht“
  • letzterer nimmt alle Dateien in den Commit, die bereits unter Versionskontrolle stehen und sich veraendert haben
  • Der Commit ist lokal, erst „git push“ uebertraegt die Änderungen zum Server
  • Analog dazu synchronisiert sich „git pull“ mit dem Server

Ein neues Repository auf dem Server legt man wie folgt an. Zuerst traegt man es in die gitosis.conf ein.

[group playground]
writable = playground
members = dirk@client dirk@workstation

Danach uebertraegt man die neue Konfiguration auf den Server.

$ git commit -a -m "playground erzeugt"
[master 8a62406] playground erzeugt
 1 files changed, 4 insertions(+), 0 deletions(-)
 
$ git push
Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 375 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To gitosis@foo:gitosis-admin.git
   4755284..8a62406  master -> master

Wir initialisieren das Repositories (ebenfalls lokal):

$ git clone gitosis@server:playground.git playground-server.git
Cloning into playground-server.git...
Initialized empty Git repository in /srv/gitosis/repositories/playground.git/
warning: You appear to have cloned an empty repository.
 
$ cd playground-foo.git/
 
$ touch foo
 
$ git add .
 
$ git commit -m "init"
[master (root-commit) 8b70e1d] init
 0 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 server
 
$ git push origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 197 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To gitosis@server:playground.git
 * [new branch]      master -> master

Nachdem das alles funktioniert, koennen die Subversion-Repositories migriert werden, Anleitung dazu im freien Buch Pro Git.

Subversion nutzt Usernamen um die Commits zu kennzeichnen, Git nutzt reale Namen und E-Mail-Adressen, damit ist der erste Schritt klar, wir brauchen eine Liste der User, die schon in unser Repository geschrieben haben und muessen diese mit Realnamen abgleichen.

$ svn log --xml | grep author | sort -u | perl -pe 's/.>(.?)<./$1 = /'

Mit dem obigen Befehl bekommt man alle Committer aus einem Subversion-Repository, damit laesst sich eine Datei users.txt aufbauen, in der die Usernamen und die Git-Namen zu finden sind. Ich habe das auch zur Konsolidierrung benutzt.

dirk = Dirk Deimeke <dirk@deimeke.net>
lux = Dirk Deimeke <dirk@deimeke.net>
user = Vorname Nachname <name@example.com>

Mit dieser Vorarbeit ist es bereits moeglich, das Subversion-Repository zu klonen und zu konvertieren (ACHTUNG: Unterschied zum Buch, bei mir hat die Option -s nicht funktioniert, da ich kein Standard-Layout verwendet habe und statt git-svn habe ich git svn benutzt).

$ git svn clone http://my-project.googlecode.com/svn/ \
      --authors-file=users.txt --no-metadata my_project

Jetzt gilt es noch, Aufraeumarbeiten durchzufuehren. Zuerst machen wir aus den Tags von Subversion Git Tags.

$ cp -Rf .git/refs/remotes/tags/* .git/refs/tags/
$ rm -Rf .git/refs/remotes/tags

Als naechstes machen wir aus den remote Referenzen lokale Branches:

$ cp -Rf .git/refs/remotes/* .git/refs/heads/
$ rm -Rf .git/refs/remotes

So, jetzt muessen wir das ganze noch hochladen.

$ git remote add origin git@my-git-server:myrepository.git

Und, weil wir alles uebertragen wollen, geht es mit dem folgenden Kommando los:

$ git push origin --all

Fertig!

Ich hatte bei der Migration mit einem groesseren Problem zu kaempfen. Mein privates Repository hat ueber 2000 commits und einige Gigabytes an Daten. Das hat meinen lokalen Rechner in die Knie gezwungen, da waren 4 GB Speicher zu knapp. Ich habe die Migration kurzerhand auf einem Server mit 8 GB RAM laufen lassen, das ging. Alternativ dazu kann man aber mit git-svn die Migration haeppchenweise durchfuehren. Mit git svn clone -r0:100 (und den anderen Optionen weiter oben) werden nur die ersten 100 Commits geklont, dann macht man mit -r100:200 weiter bis man letztendlich fertig ist.

Es gibt einen sehr guten Git - SVN Crash Course, daher fuehre ich hier nur die Unterschiede der haeufigsten Kommandos an.

  • git clone ersetzt svn checkout
  • git pull ersetzt svn update
  • git add mach das gleiche wie svn add
  • git commit -a (oder git commit -am) ersetzt svn commit bzw. svn commit -m
  • UNTERSCHIED: git push uebertraegt die lokalen Commits erst auf den Server

Zurück zur Uebersicht