Vorlage:Patches
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.
Inhaltsverzeichnis
Vorbereitung
Sicherstellen das ein gültiger Authorname und eine gültige Emailadresse hinterlegt ist. Wenn nicht, dies global für alle Git Repositories nachholen.
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"
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!
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
Um seine Änderungen als Patch weitergeben zu können, müssen die Quellcodeveränderungen zunächst committet werden. Das klingt vorallem für eingefleischte "CVS nach Git-Umsteiger" etwas verwirrend, da man von zentralen Versionskontrollsystemen wie CVS oder SVN in diesem Kontext an evtl. benötigte Schreibrechte für einen Remoteserver denkt und eben nur dahin Commits üblich sind, die eigentlich nur die Entwickler eines Projektes vornehmen. Git arbeitet aber dezentral und alles wird lokal behandelt, da jeder quasi ein vollständiges Repo mit Historie etc. lokal zur Verfügung hat. Commits sind aber die Grundlage im Ablauf der Entwicklungstätigkeit. Patche, die herkömmlich ohne Commits erzeugt werden (git diff), sind auch möglich sind aber mehr als ein Mittel für Schnelltests zu verstehen und erforden nachträgliche Behandlung.
In diversen Communities kann man bei einigen hilfswilligen Usern leider immer wieder feststellen, dass diese Paradigmen recht fest sitzen und sich erkennen lässt, dass man statt auf Git-Werkzeuge zu setzen, alte Vorgehensweisen weiterhin pflegt und z.B. nur komplette Repos immer wieder neu klont und seine Änderungen unsinnigerweise auf vollständige Klone mit Unix-Werkzeugen diff und patch verarbeitet und seine Ergebnisse dann den Entwicklern mitteilt. Das ist unter Umständen sehr umständlich und nicht zuletzt auch für die Entwickler unpraktisch. Es sei daher jedem Umsteiger angeraten, sich Sachen wie Commit, Branch, Stash, pull, Rebase, Merge im Zuammenhang mit Git zu Gemüte zu führen. Wer dafür nicht gewillt ist, kann die nächsten Zeilen getrost überpringen, wird sich aber langfristig auch keinen Gefallen damit tun. Dies ist auch eine Frage des Teamworks innerhalb des Projektes und die unter Git üblichen Vorgehensweisen wird man sehr schnell schätzen lernen.
Commit erzeugen
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 diesem Befehl zu einem Patch ausgegeben werden.
git format-patch -s [BRANCH]
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.
Wenn man z.B. aus seinen lokalen Änderungen genau die letzten Commits der Reihen nach als Patch ausgeben möchte, reicht auch dieser Befehl:
git format-patch -s [BRANCH]~6
~6 bewirkt, das genau die letzten 6 Commits der Reihe nach ausgegeben werden.
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
- 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 Message 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, Patches direkt aus E-Mails (am steht für ApplyMailbox) zu übernehmen, jedoch werden auch Dateien direkt übernommen. Befinden sich der oder die Patches 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
Git wird die Patches in der richtigen Reihenfolge anwenden sofern die Patches mit einer Nummerierung beginnen wie diese z.B. mit git format-patch automatisch erstellt werden.