Um richtig zu verstehen, wie Git das Verzweigen (engl. Branching) realisiert, müssen wir einen Schritt zurück gehen und untersuchen, wie Git seine Daten speichert.
Wie du vielleicht aus Kapitel 1 ch01-getting-started.asc in Erinnerung hast, speichert Git seine Daten nicht als Serie von Änderungen oder Differenzen, sondern statt dessen als eine Reihe von Snapshots.
Wenn du einen Commit durchführst, speichert Git ein Commit-Objekt, das einen Zeiger auf den Snapshot des von dir gestagten Inhalts enthält. Dieses Objekt enthält auch den Namen und die E-Mail-Adresse des Autors, die Nachricht, die er eingegeben hat, und zeigt auf den Commit oder die Commits, die direkt vor diesem Commit stattfanden (zu seinem Vorgänger bzw. seinen Vorgängern): keine Vorgänger für den ersten Commit, einen Vorgänger für einen normalen Commit und mehrere Vorgänger für einen Commit, welcher aus dem Zusammenführen (engl. mergen) von zwei oder mehr Branches resultiert.
Um das zu veranschaulichen, lass uns annehmen, du hast ein Verzeichnis, welches drei Dateien enthält, und du fügst alle Dateien zur Staging-Area hinzu und führst einen Commit durch. Durch das Hinzufügen der Dateien zur Staging-Area erzeugt Git für jede Datei eine Prüfsumme (den SHA-1-Hashwert, den wir in Kapitel 1 ch01-getting-started.asc erwähnt haben), speichert diese Version der Datei im Git-Repository (Git verweist auf diese als blobs) und fügt die Prüfsumme der Staging-Area hinzu:
$ git add README test.rb LICENSE
$ git commit -m 'Initial commit'
Wenn du mit der Anweisung git commit
einen Commit erzeugst, berechnet Git für jedes Unterverzeichnis (in diesem Fall nur das Wurzelverzeichnis des Projektes) eine Prüfsumme und speichert diese als tree-Objekt im Git-Repository.
Git erzeugt dann ein commit-Objekt, welches die Metadaten und einen Zeiger zum tree-Objekt des Wurzelverzeichnisses enthält, sodass es bei Bedarf den Snapshot erneut erzeugen kann.
Dein Git-Repository enthält jetzt fünf Objekte: drei blobs (die jeweils den Inhalt einer der drei Dateien repräsentieren), ein tree-Objekt, welches den Inhalt des Verzeichnisses auflistet und angibt, welcher Dateiname zu welchem Blob gehört, und ein commit-Objekt mit dem Zeiger, der auf die Wurzel des Projektbaumes und die Metadaten des Commits verweist.
Wenn du einige Änderungen vornimmst und wieder einen Commit durchführst, speichert dieser einen Zeiger zu dem Commit, der unmittelbar davor gemacht wurde.
Ein Branch in Git ist einfach ein leichter, beweglicher Zeiger auf einen dieser Commits.
Die Standardbezeichnung für einen Branch bei Git lautet master
.
Wenn du damit beginnst, Commits durchzuführen, erhältst du einen master
Branch, der auf den letzten Commit zeigt, den du gemacht hast.
Jedes Mal, wenn du einen Commit durchführst, bewegt er sich automatisch vorwärts.
Note
|
Der „master“-Branch in Git ist kein spezieller Branch.
Er ist genau wie jeder andere Branch.
Der einzige Grund dafür, dass nahezu jedes Repository einen „master“-Branch hat, ist der Umstand, dass die Anweisung |
Was passiert, wenn du einen neuen Branch anlegst?
Nun, wenn du das tust, wird ein neuer Zeiger (Pointer) erstellt, mit dem du dich in der Entwicklung fortbewegen kannst.
Nehmen wir an, du erzeugst einen neuen Branch mit dem Namen „testing“.
Das machst du mit der Anweisung git branch
:
$ git branch testing
Dieser Befehl erzeugt einen neuen Zeiger, der auf denselben Commit zeigt, auf dem du dich gegenwärtig befindest.
Woher weiß Git, auf welchem Branch du gegenwärtig bist?
Es besitzt einen speziellen Zeiger namens HEAD
.
Beachte, dass dieser HEAD
sich sehr stark unterscheidet von den HEAD
Konzepten anderer Versionsverwaltungen, mit denen du vielleicht vertraut bist, wie Subversion oder CVS.
Bei Git handelt es sich bei HEAD
um einen Zeiger auf den lokalen Branch, auf dem du dich gegenwärtig befindest.
In diesem Fall bist du weiterhin auf dem master
Branch.
Die Anweisung git branch
hat den neuen Branch nur erzeugt, aber nicht zu diesem gewechselt.
Du kannst das leicht nachvollziehen, indem du den einfachen Befehl git log
ausführst, mit dem du siehst, wohin die Zeiger der Branches zeigen.
Diese Option wird --decorate
genannt.
$ git log --oneline --decorate
f30ab (HEAD -> master, testing) Add feature #32 - ability to add new formats to the central interface
34ac2 Fix bug #1328 - stack overflow under certain conditions
98ca9 Initial commit
Du kannst die Branches master
und testing
sehen, die sich rechts neben dem Commit von f30ab
befinden.
Um zu einem existierenden Branch zu wechseln, führe die Anweisung git checkout
aus.
Lass uns zum neuen testing
Branch wechseln.
$ git checkout testing
Dadurch wird HEAD
verschoben, um auf den Branch testing
zu zeigen.
Was bedeutet das? Nun, lass uns einen weiteren Commit durchführen.
$ vim test.rb
$ git commit -a -m 'made a change'
Das ist interessant, weil sich jetzt dein testing
Branch vorwärts bewegt hat, aber dein master
Branch noch auf den Commit zeigt, auf dem du dich befandest, als du die Anweisung git checkout
ausführtest, um die Branches zu wechseln.
Lassen uns zum Branch master
zurückwechseln.
$ git checkout master
Note
|
git log zeigt nicht immer alle BranchesWenn du jetzt Der Branch ist nicht spurlos verschwunden. Git weiß nur nicht, dass du dich für diesen Branch interessierst. Git versucht, dir das zu zeigen, woran du seiner Meinung nach interessiert bist.
Anders gesagt, standardmäßig zeigt Um die Commit-Historie für den gewünschten Branch anzuzeigen, musst du ihn explizit angeben: |
Diese Anweisung hat zwei Dinge bewirkt.
Es bewegte den HEAD-Zeiger zurück, um auf den master
Branch zu zeigen und es setzte die Dateien in deinem Arbeitsverzeichnis auf den Snapshot zurück, auf den master
zeigt.
Das bedeutet auch, dass die Änderungen, die du von diesem Punkt aus vornimmst, von einer älteren Version des Projekts abzweigen werden.
Du machst im Grunde genommen die Änderungen rückgängig, die du auf deinem testing
Branch vorgenommen hast, sodass du in eine andere Richtung gehen kannst.
Note
|
Das Wechseln der Branches ändert Dateien in deinem Arbeitsverzeichnis
Es ist wichtig zu beachten, dass sich die Dateien in deinem Arbeitsverzeichnis verändern, wenn du in Git die Branches wechselst. Wenn du zu einem älteren Branch wechselst, wird dein Arbeitsverzeichnis zurückverwandelt, sodass es aussieht wie zu dem Zeitpunkt, als du deinen letzten Commit auf diesem Branch durchgeführt hast. Wenn Git das nicht problemlos durchführen kann, lässt es dich die Branches überhaupt nicht wechseln. |
Lass uns ein paar Änderungen vornehmen und noch einen Commit durchführen:
$ vim test.rb
$ git commit -a -m 'made other changes'
Jetzt hat sich dein Projektverlauf verzweigt (siehe Verzweigter Verlauf).
Du hast einen Branch erstellt und bist zu ihm gewechselt, hast einige Arbeiten daran durchgeführt und bist dann wieder zu deinem Haupt-Branch zurückgekehrt, um andere Arbeiten durchzuführen.
Beide Änderungen sind in separaten Branches isoliert: Du kannst zwischen den Branches hin und her wechseln sowie sie zusammenführen, wenn du soweit bist.
Und das alles mit den einfachen Befehlen branch
, checkout
und commit
.
Du kannst dir dies ansehen, wenn du die Anweisung git log
ausführst.
Wenn du die Anweisung git log --oneline --decorate --graph --all
ausführst, wird dir der Verlauf deiner Commits so angezeigt, dass erkennbar ist, wo deine Branch-Zeiger sich befinden und wie dein Verlauf sich verzweigt hat.
$ git log --oneline --decorate --graph --all
* c2b9e (HEAD, master) Made other changes
| * 87ab2 (testing) Made a change
|/
* f30ab Add feature #32 - ability to add new formats to the central interface
* 34ac2 Fix bug #1328 - stack overflow under certain conditions
* 98ca9 initial commit of my project
Da ein Branch in Git in Wirklichkeit eine einfache Datei ist, welche die 40-Zeichen lange SHA-1-Prüfsumme des Commits enthält, auf dem sie zeigt, können Branches ohne großen Aufwand erzeugt und gelöscht werden. Einen neuen Branch anzulegen, geht so schnell und ist so einfach, wie 41 Bytes in eine Datei zu schreiben (40 Zeichen und einen Zeilenumbruch).
Das steht im krassen Gegensatz zur Art und Weise, wie die meisten älteren Werkzeuge zur Versionsverwaltung Branches anlegen, bei der alle Projektdateien in ein zweites Verzeichnis kopiert werden. Das kann, in Abhängigkeit von der Projektgröße, mehrere Sekunden oder sogar Minuten dauern, während bei Git dieser Prozess augenblicklich erledigt ist. Da wir außerdem immer die Vorgänger mit aufzeichnen, wenn wir einen Commit durchführen, wird die Suche nach einer geeigneten Basis für das Zusammenführen (engl. merging) für uns automatisch durchgeführt, was in der Regel sehr einfach erledigt werden kann. Diese Funktionen tragen dazu bei, dass Entwickler ermutigt werden, häufig Branches zu erstellen und zu nutzen.
Lass uns herausfinden, warum du das tun solltest.
Note
|
Einen neuen Branch erzeugen und gleichzeitig dorthin wechseln.
Es ist üblich, einen neuen Branch zu erstellen und gleichzeitig zu diesem neuen Branch zu wechseln – dies kann in einem Arbeitsschritt mit |
Note
|
Ab Git version 2.23 kannst du
|