Linux & Co.

Git: Theirs/Ours fürs Cherry-Picking

Für Merge-Konflikte hat Git tolle Werkzeuge - warum nicht beim Cherry-Picking?

Merge-Konflikte gehören freilich nicht zu den besonders beliebten Dingen, zu viel Handarbeit, wenig produktiv. Darum spendiert Git Merge-/Rebase-Vorgängen Merge-Strategien wie THEIRS und OURS und OCTOPUS natürlich ;) Dieselben Konflikte können auch beim Cherry-Picking auftreten. Leider lassen sich dort keinerlei Merge-Strategien nutzen. Wie wäre es mit einem trotzigen ICH WILL ABER, ICH WILL, ICH WILL ICH WILL ICH WILL …

Deine, meine, unsere

Nur ganz kurz vorab, weil Merge-Strategien selbst schon nicht unbedingt der bekannteste Git-Part sein dürften: Wenn Konflikte auftreten, markiert Git standardmäßig die konkurrierenden Stellen in der Datei und man entscheidet manuell, was man behalten will – etwa die eigenen oder die fremden Inhalte. Mit Merge-Strategien lassen sich Konflikte vermeiden, indem quasi eine Vorliebe mitgeteilt wird: Bitte behalte im Konfliktfall meine (aktueller Branch) beziehungsweise deren (fremder Branch aus dem gepickt wird) Änderungen. Es gibt noch weitere Strategien und Feinheiten, bei Dev-Insider habe ich das ausführlicher beschrieben.

Die wichtigsten Strategien sind: THEIRS und OURS, um wahlweise die gepickten Änderungen oder die vorhandenen Änderungen zu behalten. (Für die Klugscheisser: Ja, es geht nicht um die OURS-Strategie, sondern um die XOURS-Option der ORT-Strategie …) Beim Cherry-Picking wären diesen Optionen ebenso nützlich, stehen aber nicht zur Verfügung (da ich das immer noch obskur finde: falls ich mich irre, kläre man mich bitte auf!). Ich für meinen Teil nutze das des Öfteren und weiß dann in der Regel vorher genau, was ich behalten will – meist die Inhalte, die ich picke. Darum picke ich ja.

Also mussten Merge-Strategie-Tools für Arme her: Für OURS und THEIRS habe ich mir zwei Aliasse angelegt, die Konflikte direkt auf Dateiebene entsprechend beheben. Die Konfliktmarker werden gelöscht und eine der beide Änderungsversionen. Schön wäre auch ein automatisiertes Beibehalten von beiden Änderungen, aber dafür braucht es dann doch das menschliche Sortiersystem …

Die Lösung, mal wieder: sed

Zum besseren Verständnis, hier mal ein ganz konkretes Beispiel einer Datei samt Konfliktmarkern:

       │ File: test
───────┼───────────────────────────────────────────────────────────
   1   │ the beginning
   2   │ some text
   3   │ <<<<<<< HEAD
   4   │ OURS: OLD CONTENT FROM MASTER
   5   │ some more text
   6   │ the end
   7   │ =======
   8   │ THEIRS: NEW CONTENT FROM TESTBRANCH
   9   │ some more text
  10   │ the end
  11   │
  12   │ >>>>>>> 28b3f3c (test2)

Gepickt wurde hier im Branch Master und aus dem Branch Testbranch. Git markiert den Anfang des Konflikts mit <<<<<<< HEAD und zeigt dann den eigenen lokalen Inhalt. Dann folgen der Trenner =======, der gepickte Inhalt und letztlich der Endmarker >>>>>>> gefolgt von Commit-Hash und -Nachricht.

Die Aufgabe ist nun leicht: Die Marker müssen weg und entweder der Inhalt über oder unter dem Trenner.

Mit sed ist das recht simpel. Nun, wenn man sich ein wenig mit regulären Ausdrücken auskennt … Falls nicht: Regexe retten das Jahr 2302 ;) Hier also zunächst mal der OURS-Code:

sed -E ' /^={7,}/,/>{7,}.*$/d ; /^<{7,}.*$/d ' test

Der Reihe nach:

  • -E: Erweiterte Regex verwenden.
  • ^={7,}/,/>{7,}.*$: Matcht von mindestens 7 x = am Zeilenanfang (^) gefolgt von beliebig vielen beliebigen Zeichen (.*) bis (,) 7 x > gefolgt von beliebig vielen beliebigen Zeichen, gefolgt vom Zeilenende ($).
  • d: Löscht den Match.
  • ^<{7,}.*$/d: Löscht Zeilen, die mit mindestens 7 x \< beginnen, gefolgt von beliebig vielen beliebigen Zeichen, gefolgt vom Zeilenende ($).

In Menschensprache: Gesucht wird der gepickte THEIRS-Part vom Trenner bis inklusive Endmarker – und dann gelöscht. Anschließend wird der Startmarker des Konflikts gelöscht. Die Ausgabe:

the beginning
some text
OURS: OLD CONTENT FROM MASTER
some more text
the end

Der THEIRS-Code dürfte nun selbsterklärend sein:

sed -E ' /^<{7,}/,/={7,}.*$/d ; /^>{7,}.*$/d ' test

Hier wird erst vom Startmarker bis zum Trenner gelöscht und dann der Endmarker.

Bislang hat sed natürlich noch nichts verändert, sondern nur ausgegeben – was sich als Test-Tool aber durchaus auch lohnt ebenfalls veraliasst zu werden. Zum Ändern der Datei fehlt noch die Option -i. Hier also die vier Aliasse, einzutragen beispielsweise in der ~/.bashrc:

alias cherry-ours="sed -Ei ' /^={7,}/,/>{7,}.*$/d ; /^<{7,}.*$/d '"
alias cherry-ours-test="sed -E ' /^={7,}/,/>{7,}.*$/d ; /^<{7,}.*$/d '"

alias cherry-theirs="sed -Ei ' /^<{7,}/,/={7,}.*$/d ; /^>{7,}.*$/d '"
alias cherry-theirs_test="sed -E ' /^<{7,}/,/={7,}.*$/d ; /^>{7,}.*$/d '"

Das Ganze funktioniert übrigens auch, wenn in der Datei mehrere Konflikte markiert sind oder es mehrzeilige Commit-Nachrichten gibt.

Für mich persönlich funktioniert das Prozedere bislang, aber Aliasse sind freilich keine publizierbaren Apps! Wenn also bei Euch etwas nicht/anders läuft, packt es in die Kommentare. Und wenn Ihr Hilfe bei regulären Ausdrücken oder sed benötigt, nutzt unserer hauseigene Linux-Hilfe cli.help – direkt im Terminal:

curl cli.help/sed
curl cli.help/regex

Mehr zum Thema CLI.

Einstiegsbild basiert auf: Pixabay/Anrita

Mirco Lang

Freier Journalist, Exil-Sauerländer, (ziemlich alter) Skateboarder, Dipl.-Inf.-Wirt, Einzelhandelskaufmann, Open-Source-Nerd, Checkmk-Handbuchschreiber. Ex-Saturn'ler, Ex-Data-Becker'ler, Ex-BSI'ler. Computer-Erstkontakt: ca. 1982 - der C64 des großen Bruders eines Freunds. Wenn Ihr hier mehr über Open Source, Linux und Bastelkram lesen und Tutonaut unterstützen möchtet: Über Kaffeesponsoring via Paypal.freue ich mich immer. Schon mal im Voraus: Danke! Nicht verpassen: cli.help und VoltAmpereWatt.de. Neu: Mastodon

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.

Schaltfläche "Zurück zum Anfang"
Schließen

Ooopsi!

Bitte deaktiviere Deinen Adblocker.