Der Webdienst Github erleichtert es Programmierern, einen Beitrag zu Open-Source-Projekten zu leisten, denn er vereinfacht und beschleunigt die Kommunikation zwischen Projekt-Maintainern und willigen Mitwirkenden radikal. Perl-Modulen ebnet ein Skript den Weg von CVS zu Git .
Wer schon mal ein Patch an ein Open-Source-Projekt geschickt hat, der kennt die Hürden, die auch den motiviertesten Entwickler entmutigen können: Die E-Mail-Adresse der Projekt-Maintainer oder der entsprechenden Mailingliste ist zu erfragen und eventuell im Weg stehende Moderationsschwellen sind zu überwinden. Findet dann endlich ein Verantwortlicher die Zeit, sich den Beitrag anzusehen, ist vielleicht die Formatierung falsch oder das Patch überschneidet sich mit anderen noch nicht veröffentlichten Änderungen.
PITA-Faktor verringern
Github hat es sich zur Aufgabe gemacht, diesen PITA-Faktor (Pain in the Ass, [2]) zu verringern. Öffentlich zugängliche Code-Repositories mit dem Branch- und Merge-freundlichen Revisionskontrollsystem Git erlauben es jedem Open-Source-Interessierten, nach Herzenslust viele der dort gehosteten Projekte zu ändern, lokal zu testen und im Erfolgsfall seinen Code beinahe reibungslos ins Originalprojekt einzuspielen.
Fork ist kein böses Wort
Einen Fork zu erzeugen, also eine Kopie eines Open-Source-Projekts anzulegen und auf eigene Faust daran Änderungen vorzunehmen, ist auf [github.com] kein hinterlistiges Vorgehen, sondern geplanter Alltag. Forks dienen dort nicht der feindlichen Projektübernahme, sondern erlauben es interessierten Programmbastlern, neue Features zu entwickeln, zu testen und schließlich die Maintainer um Übernahme in den Hauptzweig des Projekts zu bitten.
Github hostet öffentlich zugängliche Open-Source-Projekte kostenlos und bietet 300 MByte Plattenplatz pro Entwickler. Möchte jemand den Service für nicht öffentliche Projekte nutzen, bietet Github eine Reihe von kostenpflichtigen Varianten an, die der erfolgreichen kleinen Garagenfirma mit einer Handvoll Mitarbeitern ein gesichertes Einkommen ermöglichen.
Von CVS nach Git
Das Skript »cvs2github« in Listing 1 hilft CVS-Repositories in Git-Repositories umzuwandeln und sie für die Release auf [github.com] vorzubereiten. Git selbst bringt, zumindest mit dem Zusatzpaket Git-CVS, eine Importfunktion mit. Alles was »cvs2github« zu tun hat, ist, das CVS-Repository sowie das Verzeichnis »CVSROOT« mittels »rsync« auf den lokalen Rechner herunterzuladen und dann »git-cvsimport« aufzurufen.
|
Listing 1: |
|---|
01 #!/usr/bin/perl -w
02 use strict;
03 use Getopt::Std;
04 use Pod::Usage;
05 use File::Temp qw(tempdir tempfile);
06 use Sysadm::Install qw(:all);
07
08 my($proj) = @ARGV;
09 my($temp_dir) = tempdir(CLEANUP => 1);
10 my($fh, $author_conv_file) =
11 tempfile(UNLINK => 1);
12 my($home) = glob "~";
13 my $git_dir = "$home/DEV";
14
15 my $email = 'githubemail@mydomain.com';
16 my $cvs_loc =
17 'mikeschilli@some.cvs.server:cvs';
18 my $github_loc = 'git@github.com:mschilli';
19
20 blurt(<<EOT, $author_conv_file);
21 mschilli=mschilli <$email>
22 perlmeis=mschilli <$email>
23 mikeschilli=mschilli <$email>
24 EOT
25
26 pod2usage("No project given") unless
27 defined $proj;
28
29 my $git_proj_name = lc($proj) . "-perl";
30 my $git_path = "$git_dir/$git_proj_name";
31
32 if(-e $git_path) {
33 die "Path $git_path already exists";
34 }
35
36 mkd $git_path;
37
38 for my $cvs_dir ($proj, "CVSROOT")) {
39 sysrun(
40 "RSYNC_RSH=/usr/bin/ssh $rsync -avz " .
41 "$cvs_loc:cvs/$proj $temp_dir/");
42 }
43
44 cd $git_path;
45
46 sysrun("git-cvsimport -A " .
47 "$author_conv_file -d $temp_dir $proj");
48
49 sysrun("git remote add origin " .
50 "$github_loc/$git_proj_name.git");
51
52 print "Done: $git_proj_namen";
53
54 __END__
55
56 =head1 NAME
57
58 cvs2github - Convert cvs projects to git
59
60 =head1 SYNOPSIS
61
62 cvs2github My-Project-Name
63
64 =head1 DESCRIPTION
65
66 cvs2github takes a project checked into cvs
67 and converts it into a git repo ready for
68 github.
69
70 =head1 EXAMPLES
71
72 $ cvs2github JavaScript-SpiderMonkey
|
Hierzu erzeugt das Skript in Zeile 9 ein temporäres Verzeichnis, in dem Rsync die Server-Repository-Daten lokal ablegt. Die Kenndaten des CVS-Servers liegen in Zeile 17. Das Verzeichnis, in dem das Git-Repo schließlich landet, legt Zeile 13 in der Variablen »$git_dir« fest. Da Entwickler im CVS-Repository ihre Einträge unter ihren Unix-IDs vornehmen, auf Github aber üblicherweise unter einer anderen Identität arbeiten, definieren die Zeilen 21 bis 23 in einer Autoren-Konversionsdatei eine Zuordnung zwischen alten Unix-Usernamen und neuen Github-IDs mit E-Mail-Adressen.
Im vorliegenden Fall werden drei verschiedene IDs (»mschilli«, »perlmeis«, »mikeschilli«) in die neue Github-ID »mschilli« überführt. Haben hingegen mehrere Entwickler an einem Projekt gearbeitet, sind deren IDs samt und sonders in neue Github-IDs zu konvertieren. Die Funktion »blurt« aus dem CPAN-Modul Sysadm::Install legt die Zeilen in einer temporären Datei ab, die »git-cvsimport« mit der Option »-A« für das Mapping entgegennimmt.
Alte und neue Namen
Da »cvs2github« für Perl-Module gedacht ist, wandelt Zeile 29 einen Projektnamen wie Log-Log4perl in Kleinbuchstaben um und setzt ein »-perl« dahinter, sodass daraus etwa »log-log4perl-perl« wird, was genau dem Debian-Namensschema entspricht und den Namensraum auf Github sauber hält.
Zeile 39 ruft erst für das CVS-Repository und dann ein weiteres Mal für das Metadaten-Verzeichnis »CVSROOT« auf dem Server ein Rsync-Kommando auf, das die Serverdaten auf die lokale Festplatte kopiert, denn »git-cvsroot« verlangt auch nach »CVSROOT«. Die Environment-Variable »RSYNC_RSH« soll auf den SSH-Client verweisen, denn im vorliegenden Fall hat der Client Zugriff zum Server-seitigen Repository über einen SSH-Zugang. Für Sourceforge-Projekte gibt es ein ähnliches Verfahren [3].
Nach dem Import des CVS-Repository in Zeile 46 liegt es als Git-Repository in dem in Zeile 13 festgelegten Verzeichnis (hier »$HOME/DEV/log-log4perl-perl«). Das in Zeile 49 aufgerufene Kommando »git remote add« lässt den Remote-Branch »origin« auf das noch nicht angelegte Github-Projekt zeigen. Hierüber synchronisiert Git später die lokale Kopie und die Version auf dem Github-Server mit »push« und »pull«.
CVS ist tot
Nun hat das CVS-Repository ausgedient. Um zu verhindern, dass vergessliche oder unwissende Entwickler doch noch Check-ins vornehmen, checkt der Admin dort am besten eine gut sichtbare Datei wie »MOVED_TO_GITHUB« ein, die Schlafmützen wachrüttelt, bevor sie aktuelle Änderungen in ein totes Repository einspeisen.
Nun ist es an der Zeit, das Projekt auf Github anzulegen. Hat der Entwickler dann einen neuen Account mit einem Usernamen angelegt (»mschilli« in diesem Fall), genügen ein Klick auf »Your Repositories (create a new one)« und drei ausgefüllte Textzeilen, wie in der Abbildung 1 ersichtlich.
Als Nächstes benötigt Github die Public Keys aller am Projekt Mitwirkenden mit Schreibberechtigung am Repository. Während Public Keys mit einer Secure Shell typischerweise das Tippen von Passwörtern reduzieren, nutzt Github den Public Key tatsächlich zur Identifizierung eines Entwicklers (Abbildung 2). Der Schreibzugang auf das Repository erfolgt später über Git@github.com ohne Angabe des Nutzernamens. Passwortgestützte Identifizierung ist also über den SSH-Zugang gar nicht möglich.
Der erste Push
Ist der Public Key auf [github.com] hinterlegt, synchronisiert der Befehl »git push origin master« im lokalen aus CVS konvertierten Git-Repository die lokale Version mit dem bislang leeren Repository. Danach zeigt die Webpage auf [http://github.com/user/projekt] das Projekt mit seiner gesamten Historie und der Möglichkeit zum Mitmachen (Abbildung 3). Es ist vollbracht!
Ein Fork in Ehren
Stößt nun ein anderer Github-User, ich nenne ihn hier Open-Source-Dude, auf das Projekt und findet einen Bug oder möchte eine Verbesserung anbringen, legt er schnurstracks einen Fork an, einfach indem er auf den Fork-Button des Projekts (siehe Pfeil in Abbildung 4) drückt. Dies erzeugt eine Kopie des ursprünglichen Repository und gewährt dem Nutzer Schreibzugang auf diese Kopie. Auch er muss dazu einen Public Key auf Github hinterlegen. Abbildung 5 zeigt das nun Open-Source-Dude gehörende Projekt.
Um nun Änderungen anzubringen, fertigt Open-Source-Dude mit Hilfe des Git-Kommandos »clone« einen lokalen Klon des Fork an, wie in Abbildung 6 dargestellt. In Git üblich, aber undenkbar in Subversion oder CVS, enthält der lokale Workspace nicht nur die letzte Version des Projekts, sondern sämtliche Versionen, angefangen vom ersten Check-in. Das Kommando »git log« mit dem Parameter »HEAD~3..« zeigt die Meldungen der letzten drei Check-ins an.
Schlaue Synchronisation
Um das geforkte Projekt zu ändern, fügt das Beispiel eine Zeile in der Datei »Changes« ein, die einen imaginären Bugfix unter dem Kürzel von Open-Source-Dude ankündigt. Ein »git diff« zeigt den Unterschied zwischen lokalem Workspace und dem lokalen Repository in Abbildung 7. Der nachfolgende »commit« mit dem Kommentar »Imaginary Changes« nagelt die Änderung im lokalen Repository fest. Den Commit auf den öffentlich sichtbaren Fork auf Github hochzuspielen ist Sache des Befehls »git push origin master«, der den Hauptzweig »master« des lokalen Repository zum kurz »origin« genannten Git-Repository überträgt.

Abbildung 7: Der Kontributor Open-Source-Dude bringt eine Änderung im lokalen Repo an und spielt sie in das geforkte Repository auf Github.
Git ist bei der Synchronisation sehr effizient und tauscht kaum Daten aus, falls sich nichts oder nur wenig geändert hat. Während CVS bei großen Repositories geradezu ewig braucht, um festzustellen, dass lokaler Workspace und Server-Repository sich in Einklang befinden, kehrt Git in solchen Situationen praktisch in Sekundenbruchteilen zurück.
Der Entwickler hat also eine Änderung im lokalen Repository angebracht und sie in das geforkte Repository auf Github eingespielt. Klickt man nun auf den »Network«-Button auf der Projektseite, erzeugt Github eine etwas holperige Flash-Grafik wie in Abbildung 8, aus der ersichtlich ist, dass der Besitzer des Fork das Projekt um ein Feature vorangetrieben hat, welches das Originalprojekt noch nicht übernommen hat.

Abbildung 8: Der Network-Graph zeigt, dass der Kontributor Open-Source-Dude eine Änderung weiter ist als das Hauptprojekt.
In den meisten Fällen führt ein Fork auf Github zu einer Integration der neuen Funktionalität ins Original, niemand hat letztlich die Absicht eine Mauer zu errichten oder getrennte Wege zu gehen. Forks sind schließlich nur temporäre Mittel zum Zweck, die Entwicklung voranzutreiben und beim Erreichen eines stabilen Zustands um Übernahme ins Original zu bitten. Dieses Verfahren erleichtert Github nun ganz enorm, denn der voranpreschende Entwickler des Fork muss lediglich den Pull-Request-Button seiner Fork-Projektseite drücken und in die aufpoppende Dialogbox einige erklärende Sätze an den Projekt-Maintainer tippen (Abbildung 9).

Abbildung 9: Ein Pull-Request fordert den Maintainer dazu auf, das Patch im Fork ins Hauptprojekt zu übernehmen.
Bitte, nimm mich an
Der Maintainer erhält daraufhin sowohl auf seiner Projektseite auf Github als auch in seiner E-Mail-Inbox die Nachricht zugespielt (Abbildung 10). Ein Schwachpunkt im Arbeitsablauf ist zurzeit, dass die E-Mail aus No-Reply-Land zu kommen scheint und der Maintainer nicht einfach per E-Mail antworten kann, sondern dazu mit einem Browser auf die Github-Seite muss. Findet der Maintainer Gefallen an dem vorgeschlagenen neuen Feature, zieht er den Code in sein lokales Repository. Nach dem Motto “Trau, schau, wem” aber zunächst auf einen dafür extra eingerichteten Branch.

Abbildung 10: Der Projekt-Maintainer erhält eine Nachricht und kann die Änderung inspizieren und dann später übernehmen oder auch nicht.
Wer seine Source-Control-Erfahrungen mit Subversion oder CVS gemacht hat, wird aufstöhnen, denn das Anlegen von Nebenzweigen und deren Rückführung in den Hauptzweig (Mergen) ist auf diesen Plattformen ein alptraumhaftes Unterfangen. In Git hingegen gehört es zum Alltag und sowohl das Anlegen eines Branch als auch der Merge-Vorgang sind in Sekundenbruchteilen erledigt.
Mit ein paar klugen Ideen und geschickter Datenhaltung hat Git es tatsächlich geschafft, dieses seit der Steinzeit der Programmentwicklung existierende Problem elegant zu lösen. Es ist durchaus nicht ungewöhnlich, dass Entwickler für jeden zu fixenden Bug einen neuen Branch öffnen und gleichzeitig Dutzende davon offen halten.
Der Meister integriert
Abbildung 11 illustriert die Befehlsabfolge, die der Maintainer auf der Kommandozeile eintippt, um das neue Feature auf einen neuen Projektbranch zu ziehen. Das setzt voraus, dass er bereits einen lokalen Klon seines Original-Repository auf Github besitzt und dorthin gewechselt ist. Zunächst legt er mit »remote add« einen neuen Alias namens »open-source-dude-repo« für das Repository des Kontributors an. Der Befehl »checkout« mit der Option »-b« legt anschließend einen neuen Branch »open-source-dude-branch« im lokalen Repository an und springt hinein. Der folgende »pull«-Befehl mit dem Alias für den Fork und dem Parameter »master:open-source-dude-branch« zieht den Master-Zweig des Fork auf Github auf den Branch »open-source-dude-branch« des lokalen Repository.

Abbildung 11: Sieht das Patch interessant aus, zieht der Maintainer es auf einen neuen Branch des Projekts.
Akzeptiert der Maintainer das Patch, springt er dazu wieder mit »checkout master« in den Hauptzweig und ruft »git merge« mit der Option »–squash« und dem zu integrierenden Branch als Parameter auf. Während »merge« normalerweise die Änderungen des Fork wörtlich übernimmt und unter Umständen Dutzende von Einträgen im Log erzeugt, presst »–squash« diese auf einen Commit zusammen und checkt das Ergebnis auch noch nicht ins lokale Repository ein, sondern stellt es in Gits Staging Area, also jenen Bereich, der beim nächsten Aufruf von »git commit« ins Repository wandert (Abbildung 12). So kann der Maintainer das Patch mit einer einzigen Log-Nachricht einfließen lassen.

Abbildung 12: Übernahme des Patch in den lokalen Workspace des Master-Branch und anschließender Commit.
Diese revolutionäre Art, Beiträge zu Open-Source-Projekten zu leisten, ist auch schon bis zum CPAN vorgedrungen. Damit ein CPAN-Modul auf [search.cpan.org] – wie in Abbildung 13 zu sehen – einen Link auf das verwendete Github-Repository anzeigt, ist lediglich ein META-Eintrag nach Abbildung 14 im zentralen »Makefile.PL« erforderlich, die CPAN-Software erledigt den Rest dann automatisch.

Abbildung 13: Mit dem richtigen »META«-Eintrag zeigt Search.cpan.org auch das Source-Repository des Moduls an.

Abbildung 14: Dieser Eintrag im »Makefile.PL« des CPAN-Moduls erzeugt den Link zum Git-Repo auf Search.cpan.org.
Vorsicht, Internet!
Zu bedenken ist, dass Github die Projektarbeit plötzlich radikal veröffentlicht: Wer also die Angewohnheit hat, auf Mailinglisten herumzupöbeln oder anstößige Kommentare einzuchecken, sollte dies schleunigst abstellen, denn auf Github steht alles schonungslos groß und breit und für jedermann lesbar im Internet. Vor der endgültigen Veröffentlichung empfiehlt es sich also in jedem Fall, ein kritisches Auge auf den Inhalt der Repositories werfen. (jcb)
|
Infos |
|---|
|
[1] Listings zu diesem Artikel: [ftp://www.linux-magazin.de/pub/listings/magazin/2009/08/Perl] [2] Dan Dascalescu, “The PITA Threshold: Git Hub vs. CPAN”: [http://wiki.dandascalescu.com/essays/pita-threshold] [3] Vom Sourceforge-Project zum Git-Repo: [http://blog.usarundbrief.com/?p=12] |
|
Der Autor |
|---|
|
|












