28 Punkte von GN⁺ 2023-11-24 | 1 Kommentare | Auf WhatsApp teilen
  • Viele Menschen finden die Funktionsweise von Git-Branches nicht intuitiv.
  • Es wird der Unterschied zwischen dem üblichen intuitiven Modell von Git-Branches und der tatsächlichen internen Darstellung von Branches in Git erklärt.
  • Es wird gezeigt, dass das intuitive Modell und die tatsächliche Funktionsweise von Git in Wirklichkeit sehr eng miteinander verbunden sind.
  • Es wird diskutiert, welche Grenzen das intuitive Modell hat und warum es Probleme verursachen kann.

Das intuitive Branch-Modell

  • Viele Menschen stellen sich Branches als „Äste eines Apfelbaums“ vor.
  • In Git haben Branches kein Konzept von „Eltern“, was sich von der Vorstellung unterscheidet, dass sie von main abzweigen.

In Git ist ein Branch die gesamte Historie

  • In Git ist ein Branch nicht einfach nur ein abgezweigter Commit, sondern umfasst die vollständige Historie aller vorherigen Commits.
  • Anhand eines Beispiel-Repositorys wird gezeigt, dass sowohl main als auch mybranch jeweils 4 Commits haben.

Branches werden als Commit-ID gespeichert

  • Intern werden Branches in Git als kleine Textdateien gespeichert, die eine Commit-ID enthalten.
  • Der neueste Commit jedes Branches ist in dieser Datei verzeichnet.
  • Da es zwischen Commits keine Eltern-Kind-Beziehung gibt, kennt Git keine Beziehungen zwischen Branches.

Die Intuition der Menschen ist meistens nicht so falsch

  • Es ist etwas töricht zu sagen, die Intuition der Menschen über Git sei „falsch“.
  • Auch ein „falsches“ Modell kann in der Praxis nützlich sein.

Rebase verwendet das „intuitive“ Branch-Konzept

  • Rebase wendet nur die Commits des „intuitiven“ Branches erneut auf main an.
  • Das Ergebnis eines Rebase stimmt mit dem intuitiven Modell überein.

Auch Merge verwendet das „intuitive“ Branch-Konzept

  • Merge kopiert keine Commits, benötigt aber einen gemeinsamen Basis-Commit.
  • Die Merge-Base findet den Commit, an dem der im intuitiven Modell betrachtete Branch abgezweigt ist.

Auch GitHub-Pull-Requests verwenden die intuitive Idee

  • Wenn man auf GitHub einen Pull Request erstellt, um mybranch in main zu mergen, werden nur die Commits des intuitiven Branches angezeigt.

Intuition ist gut, hat aber Grenzen

  • Die intuitive Definition eines Branches passt gut zur tatsächlichen Arbeit mit Git, aber Git erkennt einen von main abgezweigten Branch nicht als etwas grundsätzlich anderes.

Trunk und abgezweigte Branches

  • Menschen nehmen main und mybranch unterschiedlich wahr, und das beeinflusst die Art, wie sie Git verwenden.
  • Git unterscheidet nicht, ob ein Branch ein „Abzweig“ eines anderen Branches ist.

Git kann Rebase auch „rückwärts“ ausführen

  • Da Git nicht mitteilt, ob ein Branch ein „Abzweig“ eines anderen Branches ist, müssen Nutzer selbst wissen, wann welcher Branch rebased werden sollte.
  • Sowohl git rebase main als auch das umgekehrte Rebase git rebase mybranch sind möglich. Dasselbe gilt für Merge.

Das Fehlen einer Hierarchie zwischen Git-Branches ist etwas seltsam

  • Die Aussage, dass der main-Branch nichts Besonderes ist, kommt daher, dass Git die Beziehungen zwischen Branches nicht erkennt.
  • Zwischen den Branches bestehen Beziehungen, aber Git weiß nichts davon.

Auch die Git-Branch-UI ist seltsam

  • Wenn man nur die „abgezweigten“ Commits sehen möchte, unterscheiden sich die Vorgehensweisen mit git log und git diff.

Auf GitHub ist der Standard-Branch etwas Besonderes

  • GitHub hat einen „Standard-Branch“, der eine besondere Rolle spielt.

Meinung von GN⁺

Das Wichtigste an diesem Artikel ist, den Unterschied zwischen dem intuitiven Verständnis der Menschen von Git-Branches und der tatsächlichen Funktionsweise von Git zu verstehen. Der Artikel wird Einsteigerinnen und Einsteigern im Software Engineering helfen, das Konzept von Git-Branches besser zu verstehen und effektiver zu nutzen. Es ist interessant und lehrreich zu sehen, wie das intuitive Modell von Git-Branches mit der praktischen Arbeit übereinstimmt und wie Git die Beziehungen zwischen Branches gerade nicht behandelt.

1 Kommentare

 
GN⁺ 2023-11-24
Hacker-News-Kommentare
  • Ein Branch ist ein Zeiger auf einen Commit, und dieser Zeiger wird bei jedem neuen Commit aktualisiert. Man kann sich einen Branch als einen umherwandernden Namen wie ein Tag vorstellen. Da Commits selbst auf ihre Eltern-Commits zeigen, ist ein Branch letztlich eine Kette zusammenhängender Commits mit einem benannten Einstiegspunkt. Löscht man den Branch, verschwindet nur das benannte Label, und es bleibt einfach die Kette der zugehörigen Commits zurück.
  • Es ist leichter zu verstehen, wenn man die Abstammung eines Commits als Zeiger denkt, die nicht „vorwärts“, sondern „rückwärts“ zeigen. Ein Branch ist eine Commit-ID, daher kann man durch Zurückverfolgen der Parent-Links die gesamte Historie dieses Branches finden. Der „Branch-Point“ ist die Stelle, an der zwei Commit-Ketten aufeinandertreffen, und ein Merge-Commit ist besonders, weil er zeigt, dass zwei Historien zu einer zusammengeführt wurden.
  • In privaten Projekten regen sich Freunde manchmal darüber auf, wenn sie sehen, wie ich mit git reset --hard und git stash Änderungen und Branch-Zeiger manipuliere. Um einen falschen Merge rückgängig zu machen, verwende ich git reset --hard <letzter Commit vor dem Merge>, und um kleine Änderungen aus einem lokalen Branch auf den Main-Branch anzuwenden, nutze ich git stash, checke dann den Main-Branch aus und verwende git stash apply.
  • In Git gibt es kein Konzept, dass „main etwas Besonderes ist“, aber Tools wie GitLab bieten geschützte Branches, um Fehler zu reduzieren. Ein Konzept von „Parent“- und „Child“-Branches könnte tatsächlich interessant sein, und für Long-Term-Support-Branches müsste man mehrere „Parent“-Branches unterstützen.
  • Beim Mergen, Rebasen und bei Pull Requests muss man explizit den anderen Branch angeben. Git weiß nicht, welchen Branch der Benutzer als Grundlage betrachtet. Manchmal möchte man einen Feature-Branch in einen anderen Feature-Branch mergen, daher muss klar angegeben werden, welcher Branch in welchen anderen gemergt werden soll.
  • Auch wenn die Intuition vieler Menschen technisch teilweise falsch sein mag, gibt es gute Gründe dafür, warum sie diese Intuition haben.
  • Es gibt ein interaktives Tutorial für Leute, die wissen, wie man git add und git commit verwendet. Dieses Tutorial visualisiert Branches und hilft dabei, sie lesend zu verstehen.
  • Wenn man sich merkt, dass Git-Befehle „immer“ den aktuellen Branch verändern, lässt sich die Git-Syntax „leicht“ verstehen. Zum Beispiel merged git merge my-branch my-branch in den aktuellen Branch, und git rebase my-branch rebased den aktuellen Branch auf my-branch.
  • Es wäre gut, wenn ein Branch (Head) einen „Schwanz“ hätte, der auf den Basis-Commit zeigt, von dem dieser Branch ausgegangen ist. Da Branches häufig rebased werden, muss man manchmal darüber nachdenken, wo sie gestartet sind. Es wäre bequemer, wenn Git anzeigen würde, dass der Basis-Commit zu main gehört.
  • Wenn man einen „Patch“ an eine Mailingliste sendet, kann man optional den Basis-Commit mit angeben. Das liegt daran, dass nicht immer klar ist, ob die Änderung auf dem neuesten Release, dem Hauptentwicklungs-Branch oder einem Integrations-Branch basiert. Auch bei git range-diff muss man die Basis im Kopf behalten. Dieses Tool vergleicht zwei Bereiche wie main..previous und main..current.
  • Nachdem ich meine persönliche Sicht auf Branches noch einmal gelesen habe, habe ich einiges wieder gelernt, das ich schon vergessen hatte.