Multi-Worktree ist manchmal nützlich
Einige Beispiele:
Der Build ist gebrochen. Lokal funktionieren alle Tests, aber in der CI klemmt was. Während der Wartezeit zwischen Check-In und dem nächsten Resultat aus der CI möchte ich mit dem Feature weitermachen, an dem ich gerade entwickle.
Allgemeiner gesprochen: Als DevOp entwickle ich gleichzeitig an einem Dev-Feature und einem Op-Infrastrukturthema.
Eine Kollegin bittet mich um einen Codereview ihres Feature-Branches. Ich unterbreche dazu meine laufenden Tätigkeiten. Meinen Worktree möchte ich einerseits für das Review nicht durcheinander bringen. Andererseits hätte ich ihren Code gerne lokal auf meiner Festplatte.
Ich möchte den Code des letzten offiziellen Standes (Produktionsrelease, letzte freigegebene Version, …) ausgecheckt parat haben, für schnelle Vergleiche.
Ich entwickle parallel an mehreren Feature-Branches.
Wie so oft gibt es verschiedene Wege, auf solche Herausforderungen zu
reagieren. Man kann zwischen mehreren Branches hin- und her wechseln.
Man kann mit git stash
arbeiten. Ich finde es in solchen
Situationen angenehm, für jede Aktivität in ein eigenes Verzeichnis
wechseln zu können, in git
-Sprech: einen eigenen Worktree zu haben.
Darum geht es hier.
Dazu kann man natürlich mit git clone
in verschiedenen
Verzeichnissen jeweils ganz von vorne anfangen. Schneller und
bequemer geht es mit dem Git-Subkommando git worktree
. Es erlaubt,
vom selben lokalen Repository aus mehrere Worktrees zu bedienen.
Konkretes Beispiel
Während ich diesen Artikel schreibe, habe ich tatsächlich das Problem, dass der Build (mal wieder…) gebrochen ist. Lokal laufen die Tests durch. Ich vermute, dass im CI-Setup eine Testdatenbank nicht richtig initialisiert wird.
Wenn ich nur an diesem Buildproblem arbeiten wollte, würde ich einen neuen Branch eröffnen und loslegen:
Aber leider braucht unsere CI-Pipeline knapp 10 Minuten vom git push
bis zum Fehlschlagen des Tests. Insofern mag ich meine sonstige
Projektarbeit nicht völlig beiseite legen, bis ich das Problem gefixt
habe.
Also mache ich es anders. Einen neuen Branch eröffne ich auch, aber
ich checke ihn in ein Nachbarverzeichnis aus. Das git checkout
Kommando ersetze ich dafür durch:
Das erzeugt mir einen neuen Branch build_repair
und checkt ihn in
einem zweiten Worktree im Nachbarverzeichnis ../build-repair
aus.
(Zur Unterscheidung benutze ich “-
” im Verzeichnis und “_
” im
Branch.)
Wenn es den Branch schon gibt, verkürzt sich das zu:
Damit ist build-repair
ein reiner Worktree ohne eigene Kopie des
lokalen Repositories. Statt des üblichen Unterverzeichnisses .git
findet sich dort nur ein Verweis.
Beide Worktrees teilen sich ein Repository. Dadurch stehen Commits,
Branches und so weiter einheitlich zur Verfügung. Was man im einen
Worktree erarbeitet hat, kann man im anderen sofort nutzen, zum
Beispiel für git rebase
oder git merge
oder sogar für git push
.
Ein Umweg über origin
ist nicht nötig.
Natürlich gibt es einen getrennten Index für den neuen Worktree.
Damit funktionieren git add
, git rm
, git reset
und so weiter wie
erwartet.
Im konkreten Beispiel arbeite ich im build-repair
-Verzeichnis ganz
normal: git commit
, git push
. Wartezeiten kann ich mit Arbeiten
im Originalverzeichnis sinnvoll füllen. Wenn der Build endlich läuft
(im konkreten Fall war das nach zwei Commits der Fall), putze ich mit
git rebase -i
die Historie sauber[1]. Schließlich stelle ich den
Pull Request.
Habe ich den Build repariert und brauche das build-repair
-
Verzeichnis nicht mehr, so kann ich es (aus dem Haupt-Worktree heraus)
sauber abräumen mit
Fazit
Ich empfinde es als übersichtlich und angenehm, wenn verschiedene
Dinge nebeneinander in verschiedenen Verzeichnissen liegen. Mit git
worktree
kann ich Aktivitäten an Git-Repos so organisieren. Seit ich
es kennengelernt habe, nutze ich es häufig. Es kommt meiner
Arbeitsweise sehr entgegen.
-
Ich empfehle, “rewriting of history” in allen Feature Branches mindestens bis zum Pull Request zuzulassen. Und sogar weiter bis zum Merge, wenn man sicherstellt, dabei die Reviewer nicht zu stören, zum Beispiel durch Absprachen. Übersichtlichkeit der entstehenden Historie ist (mir) wertvoller als ein Ablaufprotokoll. ↩