Github erleichtert Open-Source-Beiträge, indem es die Kommunikation zwischen Projekt-Maintainern und willigen Mitwirkenden radikal vereinfacht und beschleunigt. Mega-Projekte wie Ruby on Rails profitieren bereits von explosionsartig hochschnellender Beitragswut.
Wer schon einmal versucht hat, einen Patch an ein Open-Source-Projekt zu schicken, kennt die Hürden, die auch den motiviertesten Entwickler oft entmutigen: Die Email-Adresse der Projekt-Maintainer oder der entsprechenden Mailing-Liste ist zu erfragen und eventuell im Weg stehende Moderationsschwellen sind zu zu überwinden. Findet dann endlich ein Verantwortlicher die Zeit, sich den Beitrag anzusehen, ist vielleicht die Formatierung falsch oder der Patch überschneidet sich mit anderen noch nicht publik gemachten Änderungen.
Github hat es sich zur erklärten Aufgabe gemacht, diesen PITA-(Pain in the Ass)-Faktor ([2]) zu verringern. Öffentlich zugängliche Code-Repositories mit dem branch- und merge-freundlichen Source-Control-System Git erlauben jedem Open-Source-Interessierten, nach Herzenslust Änderungen an einer Vielzahl der dort gehosteten Projekte vorzunehmen, lokal zu testen und im Erfolgsfall beinahe reibungslos ins Originalprojekt einzuspielen.
Ein ``Fork'' zu erstellen, also eine Kopie eines Open-Source-Projekts anzulegen und auf eigene Faust Ä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 Projektmaintainer um Übernahme in den Hauptzweig des Projekts zu bitten.
Github hostet öffentlich zugängliche Open-Source-Projekte kostenlos und bietet 300MB Plattenplatz pro Entwickler. Möchte jemand den Service für nicht-öffentliche Projekte nutzen, bietet Github eine Reihe von kostenpflichtigen Plänen an, die der erfolgreichen etwa fünf Mann hohen Garagenfirma ein sicheres Einkommen ermöglichen.
Listing cvs2github hilft, CVS repositories in git-Repos umzuwandeln und
sie für den Release auf github.com vorzubereiten. Git selbst bringt,
zumindest mit dem Zusatzpaket git-cvs, eine Import-Funktion mit.
Alles was cvs2github
zu tun hat, ist, das CVS-Repository mittels rsync
auf den lokalen Rechner herunter zu laden, das Verzeichnis CVSROOT mit
hinzuzuladen und dann git-cvsimport
aufzurufen.
Hierzu erzeugt das Skript in Zeile 9 ein temporäres Verzeichnis, in
das rsync die Server-Repo-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-Repo
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 Autorenkonversionsdatei eine Zuordnung zwischen alten Unix-Usernamen
und neuen Github-IDs mit Emailaddresen.
Im vorliegenden Fall werden drei
verschiedene IDs (mschilli, perlmeis, mikeschilli) alle 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 überführen. 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
entgegennimmt, um das Mapping durchzuführen.
Da cvs2github
für Perl-Module geschrieben wurde, wandelt Zeile 29
einen Projektnamen wie Log-Log4perl in Kleinbuchstaben um und setzt ein
-perl
dahinter, so dass 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-Repo 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 wird
auf den ssh-Client gesetzt, 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 in Zeile 46 erfolgreich durchgeführten Import des CVS-Repos
in ein Git-Repo liegt letzteres unter 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 wird git
später die lokale Kopie und die Version auf dem Github-Server
mit push und pull syncronisieren.
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/$cvs_dir $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_name\n"; 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
Nun hat das CVS-Repo ausgedient, und um zu verhindern, dass vergessliche oder unwissende Entwickler doch noch Check-ins vornehmen, wird dort am besten eine gut sichtbare Datei wie MOVED_TO_GITHUB eingescheckt, die Schlafmützen wachrüttelt, bevor sie aktuelle Änderungen in ein totes Repo einspeisen.
Nun ist es an der Zeit, das Projekt auf Github anzulegen. Hat der Entwickler 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 Abbildung 1 ersichtlich.
Abbildung 1: Ein neues Github-Project ist schnell angelegt. |
Abbildung 2: Der Public Key des Entwicklers dient auf github der Identifikation. |
Als nächstes benötigt Github die Public Keys aller am Projekt Mitwirkenden mit Schreibberechtigung am Repo. Während Public Keys mit einer Secure Shell (ssh) typischerweise das Passworttippen reduzieren, nutzt Github den Public Key tatsächlich zur Identifizierung eines Entwicklers. Der Schreibzugang auf das Repo erfolgt später über git@github.com, ohne Angabe des Nutzernamens. Passwortgestützte Identifizierung ist also über den ssh-Zugang gar nicht möglich.
Wurde der Public Key auf github.com hinterlegt, synchronisiert der
Befehl git push origin master
im lokalen aus CVS konvertierten
git-Repo die lokale Version mit dem bislang leeren Repo auf github.com.
Danach zeigt die Webpage auf http://github.com/user/projekt das Projekt
mit seiner gesamten Historie und der Möglichkeit zum Mitmachen wie
in Abbildung 3 gezeigt an. Es ist vollbracht, das Projekt ist live!
Abbildung 3: Das aus CVS konvertierte Git-Repository ist auf Github angekommen. |
Stößt nun ein anderer Github User, nennen wir ihn ``open-source-dude'',
auf das Projekt, findet einen Bug oder
möchte eine Verbesserung anbringen, legt er schnurstracks einen Fork
an, einfach, indem er auf den ``Fork''-Button des Projektes (Pfeil in
Abbildung 4) drückt. Dies legt auf github.com eine Kopie des ursprünglichen
Repos an und gewährt open-source-dude
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.
Abbildung 4: Fork ist auf Github kein böses Wort. |
Abbildung 5: ... und schon wurde ein Projekt geforkt. |
Um nun Änderungen anzubringen, fertigt open-source-dude
mittels des
Git-Kommandos clone
einen lokalen Klon des Forks an, wie in Abbildung
6 dargestellt. Wie in git üblich, und undenkbar in Subversion oder CVS,
enthält der lokale Workspace nicht nur die letzte Version des Projektes
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.
Abbildung 6: Ein lokaler Klon des geforkten Projekts wird angelegt. |
Als Beispiel für eine Änderung im geforkten Projekt wird nun eine Zeile
in der Datei Changes
hinzugefügt, 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 Repo in Abbildung 7.
Der nachfolgende
commit
mit dem Kommentar 'Imaginary Changes' nagelt die Änderung im
lokalen Repo fest. Um den Commit auf den öffentlich sichtbaren Fork auf
Github hochzuspielen, dient der Befehl git push origin master
, der den
Hauptzweig master
des lokalen Repo zum kurz origin
genannten Git-Repo
hochspielt. Git ist bei der Syncronisation 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-Repo sich in Einklang befinden, kehrt git in solchen
Situtationen praktisch in Sekundenbruchteilen zurück.
Abbildung 7: Der Kontributor open-source-dude bringt eine Änderung im lokalen Repo an und spielt sie in das geforkte Repository auf Github. |
Abbildung 8: Der Network-Graph zeigt, dass der Kontributor open-source-dude eine Änderung weiter ist als das Hauptprojekt. |
Der Entwickler hat also eine Änderung im lokalen Repo angebracht und sie in das geforkte Repository auf Github eingespielt. Klickt man nun auf den ``Network''-Button auf der Projektseite, generiert Github eine etwas holperige Flash-Grafik nach Abbildung 8, aus der ersichtlich ist, dass der Besitzer des Forks das Projekt um ein Feature vorangetrieben hat, das das Originalprojekt noch nicht übernommen hat.
In den allermeisten 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 letztendlich nur temporäre Mittel zu dem 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 Forks 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: Der Maintainer wird mit einem Pull-Request aufgefordert, den Patch im Fork ins Hauptprojekt zu übernehmen. |
Der Projekt-Maintainer erhält daraufhin sowohl auf seiner Projektseite auf Github als auch in seiner Email-Inbox die Nachricht zugespielt (Abbildungen 10/11). Ein derzeitiger Schwachpunkt im Arbeitsablauf ist, dass die Email aus ``no-reply''-Land zu kommen scheint und der Maintainer nicht per Email antworten kann, sondern dazu mit einem Browser auf die Github-Seite muss.
Abbildung 10: Der Projekt-Maintainer erhält eine Nachricht und kann die Änderung übernehmen oder auch nicht. |
Abbildung 11: Auch per Email kommt die Aufforderung an, den Patch anzunehmen. |
Findet der Maintainer Gefallen an dem vorgeschlagenen neuen Feature, zieht er ihn in sein lokales Repo. Nach dem Motto ``Trau, schau, wem'' allerdings zunächst auf einen dafür extra eingerichteten Branch. Wer seine Source-Control-Erfahrungen mit Subversion oder CVS gemacht hat, wird nun wahrscheinlich aufstöhnen, denn das Anlegen von Nebenzweigen und deren Rückführung in den Hauptzweig (Mergen) ist auf diesen Plattformen ein alptraumähnliches Unterfangen. In Git hingegen gehört es zum Alltag und sowohl das Anlegen eines Branches als auch der Mergevorgang sind in Sekundenbruchteilen erledigt. Mit ein paar klugen Ideen und geschickter Datenhaltung hat Git es tatsächlich geschafft, dieses seit der Steinzeit der Programmentwicklung herumlungernde Problem elegant zu lösen. Es ist durchaus nicht ungewöhnlich, dass Entwickler für jeden zu fixenden Bug einen neuen Branch eröffnen und gleichzeitig dutzende davon offen halten.
Abbildung 12 illustriert die Befehlsabfolge, die der Maintainer auf
der Kommandozeile eintippt, um das neue Feature auf einen neuen Projektbranch
zu ziehen. Es wird vorausgesetzt, dass der Maintainer bereits einen
lokalen Klon seines Original-Repos auf Github besitzt und in dieses
hineingefahren ist. Zunächst legt er mit remote add
einen neuen Alias namens
open-source-dude-repo
für das Repo des vorpreschenden Kontributors
an. Der Befehl checkout
mit der Option -b
legt anschließend einen
neuen Branch open-source-dude-branch
im lokalen Repo an und springt in
diesen hinein.
Abbildung 12: Sieht der Patch interessant aus, zieht der Maintainer ihn auf einen neuen Branch des Projektes. |
Der folgende pull
-Befehl mit dem vorher angelegten Alias für den Fork
und dem Parameter master:open-source-dude-branch
zieht den Master-Zweig
des Forks auf Github auf den Branch open-source-dude-branch
des
lokalen Repos. Ein anschließend vom neuen Branch aus abgesetztes
diff
-Kommando mit dem Parameter master
zeigt die Unterschiede im
aktuellen Branch gegenüber dem master
-Branch, also dem Hauptzweig
des lokalen Repos, an.
Abbildung 13: Übernahme des Patches in den lokalen Workspace des master-Branches, und anschließender Commit. |
Akzeptiert der Maintainer den 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 Forks allesamt
wörtlich übernimmt und unter Umständen dutzende von Einträgen im Log
des Originalprojekts erzeugt, presst --squash
diese
auf einen Commit zusammen, und checkt das Ergebnis noch nicht
ins lokale Repo ein, sondern stellt es in Gits ``Staging Area'', dem
Bereich, der beim nächsten Aufruf von git commit
ins Repo wandert.
So kann der Projekt-Maintainer den Patch mit einer einzigen
Log-Nachricht seiner
Wahl ins Projekt einfließen lassen.
Diese revolutionäre Art, Beiträge zu Open-Source-Projekten zu leisten, ist auch schon aufs CPAN vorgedrungen, wo Woche für Woche hunderte von neuen Modulversionen veröffentlicht werden. Damit ein CPAN-Modul auf search.cpan.org wie in Abbildung 14 einen Link auf das verwendete Github-Repo anzeigt, ist lediglich ein META-Eintrag nach Abbildung 15 im zentralen Makefile.PL erforderlich, die CPAN-Software erledigt den Rest automatisch.
Abbildung 14: Mit dem richtigen META-Eintrag zeigt search.cpan.org auch das Source-Repository des Modules an. |
Abbildung 15: Dieser Eintrag im Makefile.PL des CPAN-Moduls erzeugt den Link zum Git-Repo auf search.cpan.org. |
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 plötzlich groß und breit im Internet als Futter für die Suchmaschinen. Dies gilt auch für konvertierte CVS-Repos: Was bislang vielleicht nur wenigen Entwicklern zugänglich war, ist nun selbst für Oma und Opa Meume gut auf normalen HTML-Seiten sichtbar. Vor der endgültigen Veröffentlichung sollte man schon ein kritisches Auge auf den Inhalt werfen.
Listings zu diesem Artikel: ftp://www.linux-magazin.de/pub/listings/magazin/2009/08/Perl
Dan Dascalescu, ``The PITA Threshold: GitHub vs. CPAN'', http://wiki.dandascalescu.com/essays/pita-threshold
``Turn your Sourceforge Project into a Git Repo'', http://blog.usarundbrief.com/?p=12
Michael Schilliarbeitet als Software-Engineer bei Yahoo! in Sunnyvale, Kalifornien. Er hat "Goto Perl 5" (deutsch) und "Perl Power" (englisch) für Addison-Wesley geschrieben und ist unter mschilli@perlmeister.com zu erreichen. Seine Homepage: http://perlmeister.com. |