Development:Git Patch erstellen

Aus TuxBoxWIKI
Wechseln zu: Navigation, Suche


Review-KandidatDieser Artikel befindet sich derzeit im Reviewprozess. Hilf mit, ihn zu verbessern! Falls du bei weiteren Artikeln helfen willst, findest du hier eine Auswahl offener Artikel.

Wer einen Patch bereitstellen möchte, kann mit Git speziell importierbare Patchfiles erstellen. Die nötigen Funktionen werden durch Git ohne Zusatzsoftware zur Verfügung gestellt. Zusätzliche Tools wie diff und patch werden nicht benötigt.

Durch diese Git Eigenheit wird das Arbeiten mit Git auch für die Entwickler deutlich vereinfacht. Anders als CVS oder auch SVN unterscheidet Git zwischen Committer und Autor des Patches. Dies hat unter Anderem auch zur Folge, das durch einen mit Git formatierter Patch, sofern er korrekt erzeugt und angewendet wurde, auch der Autor in die Historie des Repostories aufgeführt wird.

Da mit Git formatierte Patche in der Regel auf lokale Commits des Autors basieren, werden auch die darin bereits vorhandenen Commit Messages übernommen. Sofern diese auch den Patch erklären, ist es für den Entwickler, der den Patch erhält auch schneller nachvollziebar, worum es bei diesem oder jenem Patch geht und braucht sich nicht selbst um die Commit Message zu kümmern. Andererseits kann der Autor vor der Überführung in das Remote-Repo und auch Dank der vorhandenen Autordaten im Repo später um Nachbesserung gebeten werden.


Vorbereitung

Bitte stellt sicher das ein gültiger Authorname und eine gültige Emailadresse hinterlegt ist. Wenn nicht, dies global für alle Git Repositories nachholen. Bitte benutzt echte Namen, es geht insgesamt um Open Source und Ihr habt natürlich nichts zu verstecken. Die Angabe hat den Sinn das jeder der sich mit einem Commit auseinandersetzt gfls. den Autor kontaktieren kann.

git config --global user.name "Firstname Lastname"
git config --global user.email "your_email@youremail.com"

Oder nur für das lokale Git Repository.

cd /Pfad/zum/Repository 
git config user.name "Firstname Lastname"
git config user.email "your_email@youremail.com"


Stop hand.png HINWEIS:

Es ist übrigens unangemessen ungültige, falsche, keine oder gar sinnlose Angaben zu machen und zeugt gegenüber anderen Entwicklern und Contributoren eher von schlechtem Stil und ist deshalb nicht gern gesehen und Commits mit solchen Angaben werden in OpenSource Projekten zurückgewiesen!

Quellcode verändern

Diese Schritt ist genauso wie bei anderen SCM-Systemen. Man verändert den Quellcode mit den eigenen gewünschten Veränderungen.

git pull
# adding some modifications

Patch erstellen

Patche werden unter Git üblicherweise aus Commits erstellt und lassen sich somit entwicklerfreundlich weitergeben. Patche, die herkömmlich ohne Commits erzeugt werden (git diff), sind lediglich ein Mittel für Schnelltests. Auch wenn man dies evtl. von früheren zentralen Versionskontrollsystemen so gewohnt ist, sollte man beim Arbeiten mit Git davon absehen, unformatierte bzw. rohe Patches an Entwickler zu übergeben. Der Aufwand hierfür geht im Verhältnis gegenüber alten Methoden gegen Null, wenn man bedenkt, dass mehr als nur ein einfacher Patch dabei herauskommt. Diese Arbeitsweise scheint vor allem für Umsteiger ein Problem zu sein, aber dies ist auch eine Frage des Teamworks innerhalb des Projektes und diese unter Git übliche Vorgehensweise wird man sehr schnell schätzen lernen.

Grundlage: Commit erzeugen

Um seine Änderungen weitergeben zu können, müssen die Quellcodeveränderungen zunächst lokal committet werden. Wenn Ihr wirklich nur die Dateien verändert habt, die zum Commit gehören, kann einfach dieses Kommando verwendet werden.

git commit -a

Wenn nicht per

git add <Datei1> <Datei2> ...

die relevanten Dateien selektiv hinzufügen und abschließend

git commit

aufrufen. Hier im Beispiel eine einfache Veränderung der neutrino.cpp.

user@pc~/gitprojects/buildsystem-cs/source/neutrino-hd [tuxbox-port]$ vi src/neutrino.cpp
user@pc~/gitprojects/buildsystem-cs/source/neutrino-hd [tuxbox-port]$ git commit -a
[tuxbox-port 5756abf] neutrino.cpp: changing debug output to use macro dprintf
1 files changed, 2 insertions(+), 2 deletions(-)

Nun liegen die Veränderungen im lokalen Git Repo und können mit

git format-patch -s [BRANCH]

als Patch ausgegeben werden. Der Parameter -s bewirkt ein SignOff im Patch, also eine Art Unterschrift das Du/Ihr den Patch auch geprüft habt. Dies hilft Irritationen zu vermeiden wenn es mal Diskussionen über einen Patch gibt und wer nun eigentlich den Patch auf sachlich richtig geprüft hat. Es gibt noch weitere Arten einen Patch zu kommentieren, sehr bekannt und üblich sind die Flags "Acked-by:", "Tested-by:" und "Reviewed-by:". Für diese Flags gibt es aber keinen Switch innerhalb git format-patch. Diese müssen manuell oder per Alias eingefügt werden.

Stop hand.png HINWEIS:

Spätestens jetzt sollte man erkennen, dass man für seine lokalen Änderungen, die man für eine bestimmte Aufgabe abarbeitet immer mit Branches arbeiten sollte und darin seine Commits klein und nachvollziehbar hält. Möchte man später trotzdem mehrere Commits zu einem oder weniger Commits zusammenfassen, kann man das über git rebase -i nachholen.

Im Beispiel wurde die neutrino.cpp vom Branch tuxbox-hd/tuxbox-port verändert. Daher dann auch der git-format-patch Befehl gegen diesen Branch. Würde man gegen origin/master in dem Falle den Befehl anwenden, dann würden auch alle Patches bzw. Änderungen aus weiteren Commits ausgegeben werden, die sich zwischen tuxbox-hd/tuxbox-port und origin/master ergeben.

user@pc~/gitprojects/buildsystem-cs/source/neutrino-hd [tuxbox-port]$ git format-patch -s tuxbox-hd/tuxbox-port

Ergebnis:

0001-neutrino.cpp-changing-debug-output-to-use-macro-dpri.patch

Das ist eigentlich schon alles. Diese Datei(en) können nun an einen Entwickler geschickt werden.

Patch von einem Einzel-Commit erstellen

zunächst das Log aufrufen, um den benötigten Commit zu finden. In diesem Beispiel liegt der betreffende Commit ein einem lokalem Branch und sollte unter den letzten paar commits zu finden sein. Pauschal suchen wir nun die letzten Commits ab:

git log -3

Dies gibt nun die letzten Commits aus:

commit ac7abd8db402724f9c195b2091c7a92997edce6e
Author: Thilo Graf <###@n###x.de>
Date:   Mon Apr 16 10:51:07 2012 +0200

    target ntp: add target for ntp
    
    neutrino sectionsd missed ntpdate with error log
    "getUTC: read: invalid argument
    ntpdate not found"
    
    ntpdate should also now can be intstalling with system-tools
    
    
    Signed-off-by: Thilo Graf <###@n###x.de>>

commit 3caecd682aea585f101286ec7cb550813c7ac1df
Author: Stefan Seyfried <seife@tuxbox-git.#######.de>
Date:   Sun Apr 15 19:52:05 2012 +0200

    start_neutrino: prepare for deep standby

commit 874ba0c75afe65ebd0a2482000634ac0c75303f2
Author: Stefan Seyfried <seife@tuxbox-git.#######.de>
Date:   Sun Apr 15 18:48:23 2012 +0200

    spark: use aotom FP to switch the box into deep standby
    
    For this to be really useful, we need support in neutrino.
lines 1-28/28 (END)

Wenn der gewünschte Commit dieser ist: ac7abd8db402724f9c195b2091c7a92997edce6e erzeugt folgendes Kommando einen entsprechenden Patch

 git format-patch -s -M -C ac7abd8db402724f9c195b2091c7a92997edce6e~1...ac7abd8db402724f9c195b2091c7a92997edce6e

Heraus kommt ein nummerierter Patch:

0001-target-ntp-add-target-for-ntp.patch


Stop hand.png HINWEIS:
  • Die Tilde (~) bei der ersten SHA1 Summe hat die Bedeutung das Git den Bezug auf die Anzahl nach der Tilde angegebenen Comit vor der SHA1 Summe nimmt (auf Commit 3caecd6), im Beispiel eben genau einen Commit davor. Man könnte so z.B. auch Bezug auf den drittletzten oder zehnten Comit vor der angebeben SHA1 Summe nehmen. Damit kann man auch mehrere Commits zu einem in Form eines Patches ohne Rebasing zusammen fassen.
  • Man muss sich nicht die langen SHA1 IDs merken. Git begnügt sich auch mit den ersten sieben Zahlen der SHA1 Summe, wenn es nicht andere Commits gibt die mit den selben Zahlen beginnen.
    • Der obere Befehl kann also auch verkürzt werden:
git format-patch -s -M -C ac7abd8~1...ac7abd8

Ergebnis:

0001-target-ntp-add-target-for-ntp.patch

Patch aus mehreren Commits erstellen

Angenommen man arbeitet lokal an einer Sache, die man üblicherweise in mehreren Commits und in einem separaten Branch abgearbeitet hat und möchte genau diese Commits nun als Patche weitergeben, kann man diese nun als Folge von Einzelpatches erzeugen. Befindet man sich also in einem lokalen Branch und dieser beinhaltet 2 Commits, und möchte genau diese letzten beiden Commits als Patche haben, würde man das etwa so machen:

$ git format-patch [BRANCH]~2

Ergebnis:

0001-dvbsi-try-to-fix-broken-neutrino-build-with-cst-dvbs.patch
0002-libdvbsi-add-patch-for-src-time_date_section.cpp.patch

Den oder die erzeugten Patche kann man nun per eMail an eine Mailingliste oder direkt an einen Entwickler schicken.

einen Patch von einer dritten Person erhalten

Es ist möglich, das Ihr von einer anderen Person einen Patch erhalten habt und diesen weitergeben wollt. Wenn diese Person nun wiederum den Patch nicht selbst weitergeben wollte, aber trotzdem einverstanden ist, das der Patch weitergegeben werden kann, dann könnt Ihr diesen Patch noch mit einem SignOff versehen. Dazu einfach die Option --signoff beim git-format-patch mit angeben. Damit fügt Ihr dem Patch ein Signed-by: <Euer Name> hinzu.

 
user@pc~/gitprojects/buildsystem-cs/source/neutrino-hd [tuxbox-port]$ git format-patch tuxbox-hd/tuxbox-port --signoff

Ergebnis:

0001-neutrino.cpp-changing-debug-output-to-use-macro-dpri.patch

Danach wie oben schon beschrieben den Patch an einen Developer weiter geben.

einen Patch einspielen

Es gibt einige Möglichkeiten einen Patch direkt anzuwenden. Git stellt hierfür einige Kommandos bereit

git apply

git apply ist quasi das Git-Gegenstück zum allgemein bekannten Unix-Werkzeug patch. Dazu wird dieser Befehl benutzt:

git apply [/Pfad/zur/Patchdatei]

Einzige Bedingung ist, das Ihr im entsprechenden Git Repository seid.

user@pc~/gitprojects/buildsystem-cs/source/neutrino-hd [tuxbox-port]$ git apply 0001-neutrino.cpp-changing-debug-output-to-use-macro-dpri.patch

Die Anwendung eines solchen Patches ist also recht einfach, ist aber nur für lokales Arbeiten sinnvoll, um Schnelltests durchzuführen, ansonsten sollte man vorzugsweise auf git am zurückgreifen, insbesondere dann, wenn der Patch vom Autor als "commitreif" zugestellt wurde und für einen Commit in das Remote-Repo vorgesehen ist.

Wenn man auf diese Weise einen unformatierten Patch erhält, hat man als Entwickler leider nun den Aufwand nachträglich einige Daten für einen Commit zu ermitteln, um letztlich einen Commit in das Remoterepo durchzuführen zu können. Nach dem der Patch mit git apply angewendet wurde, muss man die Änderungen also nun selbst samt fehlender Angaben erst lokal committen:

$ git commit -a --author="MrX <MrX@mrxathome.de>" --message="This is my opinion what this patch is doing, but MrX should known it better"

git am

Im Unterschied zu git apply werden mit git am angewendete Patche wie Commits behandelt. Hat man also einen mit git format-patch formatierten Patch erhalten und angewendet, hat man diesen Patch bereits committet und relevante Daten wie z.B. Autor-Informationen, Commit Messaage in seine lokale Historie übernommen. Git am ist daher gegenüber der git apply Methode vorzuziehen, da hier der Aufwand für den Entwickler minimiert ist und auch die Leistungen von Contributoren eines Projektes gewürdigt werden. Die Chancen für eine Übernahme in das Projektrepo könnten je nach Umfang der Änderungen und gerade wegen des geringeren Aufwands auch steigen.

Ursprünglich ist git am dazu gedacht, Patche direkt aus EMails zu übernehmen, jedoch werden auch Dateien direkt übernommen. Befinden sich der oder die Patche im Stammverzeichnis des Gitrepos, reicht dieser Befehl:

 
git am 0001-neutrino.cpp-changing-debug-output-to-use-macro-dpri.patch

Bei mehreren Patchdateien kann man auch Platzhalter verwenden:

 
git am *.patch