====== Migration von Subversion nach Git ======
* Urspruenglicher Autor: Dirk Deimeke
* Urspruengliches Datum: 18.11.2011
Aus verschiedenen Gruenden wollte ich unsere [[https://secure.wikimedia.org/wikipedia/de/wiki/Apache_Subversion|Subversion]]-Repositories nach [[https://secure.wikimedia.org/wikipedia/de/wiki/Git|Git]] migrieren. Die Entscheidung dafuer war unabhaengig davon, dass zwei Projekte, naemlich [[http://michael-prokop.at/blog/2011/11/05/fai-switch-from-subversion-to-git/|FAI]] und [[http://blog.s9y.org/archives/236-Details-about-the-GitHub-migration-process,-developers-please-read!.html|Serendipity]], etwa zur gleichen Zeit migriert sind.
Git ist ein [[https://secure.wikimedia.org/wikipedia/de/wiki/Verteiltes_Versionskontrollsystem#Verteilte_Versionsverwaltung|verteiltes Versionskontrollsystem]] (DVCS) - in der gleichen Liga spielen auch [[https://secure.wikimedia.org/wikipedia/de/wiki/Bazaar|Bazaar]] und [[https://secure.wikimedia.org/wikipedia/de/wiki/Mercurial|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 [[http://chaosradio.ccc.de/cre130.html|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 [[https://github.com/mzupan/gitosis|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 [[http://netzaffe.de/git-gitosis-gitweb-the-debian-way.html|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, [[http://progit.org/book/ch8-2.html|Anleitung]] dazu im freien [[http://progit.org/book/|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
lux = Dirk Deimeke
user = Vorname Nachname
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 [[http://git.or.cz/course/svn.html|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
[[adminstoriesartikel|Zurück zur Uebersicht]]