Informatik/Mathematik / Software Engineering

Ziel: Vertiefung der Anwendung von Branches in Git

Praktikumsaufgaben Teil 7 - Branches

In den folgenden Aufgaben wird das Branching von Git genauer betrachtet.

Git-Versionsblütenzweige
Abbildung 1. Git-Versionsblütenzweige

Es werden hilfreiche Konzepte zu folgenden Themen erarbeitet:

  • Branches (Zweige) erstellen, vereinen und entfernen


Hinweise zur Bearbeitung

Bearbeiten Sie die Aufgaben in Ihrem vorhandenem htwd-se-example-project-Repository oder legen Sie sich dafür ein neues Repository auf GitHub an (Siehe hierzu Praktikumsaufgaben Teil 1 - Grundlagen). Falls noch nicht geschehen, clonen Sie es als lokales Arbeitsverzeichnis auf Ihren Rechner und bearbeiten Sie in diesem die Aufgaben. Wenn nicht anders angegeben, entscheiden Sie selbst, was für eine (beispielhafte) Änderung zur Lösung der entsprechenden Aufgabe Sie an Ihrem Repository vornehmen.

Nutzen Sie die Möglichkeiten sich auf Webseiten wie der offiziellen Git Dokumentation oder anderen Ressourcen über die verwendeten git-Kommandos und -Konzepte zu informieren.

Linkliste zu hilfreichen Webseiten und Videos
  • Für Visual Studio Code gibt es die hilfreiche Erweiterung Git Graph, welche eine grafische Darstellung von Branches und Versionsketten erlaubt.

In den Aufgaben gibt es ebenfalls aufklappbare Hinweise. Diese beinhalten die benötigten git-Kommandos mit erklärenden Anmerkungen.

Je nach verwendetem git-Client oder IDE sind die Kommandos über entsprechenden Menüpunkte und Schaltflächen zu erreichen. Es ist Ihnen freigestellt, ob Sie einen grafischen Client/IDE oder das Terminal verwenden.

Terminal in Visual Studio Code

Über das Hauptmenü: View  Terminal kann innerhalb von VS Code ein Terminal geöffnet werde.

Terminal unter Windows

PowerShell, git-Bash, Eingabeaufforderung (cmd), …​

Terminal unter Linux, macOS

XTerm, Konsole, Terminal.app, …​

Hinweise zum Praktikum in den Windows-Laboren

Aufgrund der nicht gespeicherten Nutzer-Profile in den Windows-Laboren, sind zu jedem Praktikumsbeginn folgende Schritte durchzuführen:

Schritte zur Vorbereitung des Praktikums
  1. Git-Konfiguration C:\Users\<username>\.gitconfig wiederherstellen oder anpassen:

    per Editor in der .gitconfig
    [user]
    	name = Vorname Nachname
    	email = s00000@htw-dresden.de
    [safe]
        directory = *
    [http]
    	proxy = http://www-cache.htw-dresden.de:3128
    oder per PowerShell und Git-Kommandos
    > git config --global user.name "Vorname Nachname"
    > git config --global user.email s00000@informatik.htw-dresden.de
    > git config --global --add safe.directory *
    > git config --global http.proxy http://www-cache.htw-dresden.de:3128
  2. Visual Studio Code: Anpassen der AsciiDoc-Einstellungen:

    • Asciidoc > Preview: Use Editor Style: (deaktiviert)

    • Asciidoc > Extensions: Enable Kroki: (aktiviert)

  3. GitHub Login mit gespeichertem oder neuem Personal Access Token über die PowerShell bekanntgeben:

    Repository vorhanden
    > U:
    > cd path/to/repository/<repo-name>/
    > git pull
    Authentifizierung ...
    Repository nicht vorhanden (Home-/SAMBA-Laufwerk)
    > U:
    > cd path/to/repository/
    > git clone https://github.com/.../<repo-name>
    Authentifizierung ...
    Repository nicht vorhanden (TEMP-Laufwerk)
    > T:
    > git clone https://github.com/.../<repo-name>
    Authentifizierung ...


1. Branches verwenden

Mit Branches (Verzweigungen) bietet Git die Möglichkeit, ausgehend vom Hauptzweig, Versionen auf unterschiedlichen Zweigen zu erstellen. Diese Versionen können mit anderen Branches zusammengeführt (merge), verworfen oder bewusst getrennt gehalten werden.

Beispielhaftes Branching
Abbildung 2. Beispielhaftes Branching

Mit Hilfe von Branching kann bspw. ein neues Feature oder ein Bugfix in einem extra Branch entwickelt und getestet werden, ohne den Hauptzweig zu beeinflussen. Nach Fertigstellung werden die Änderungen in den stabilen Hauptzweig aufgenommen.

Hinweise: merge

Merge

Wird der Versionsstand eines Branches in einen anderen Branch aufgenommen (merge) geschieht dies entweder mit der fast-forward oder recursive (3-Wege-Merge) Strategie.

fast-forward
…​
Beispiel: Git-Kommandos
$ git switch -c feature
$   vi feature.txt → git add . → git commit -m "..."
$   vi feature.txt → git add . → git commit -m "..."
$ git switch main
$ git merge feature

Basiert der zu mergende Branch (feature-Branch) auf dem letzten Stand des aktuellen Branches (main-Branch), liegen die letzten Versionen der beiden Branches in einer direkten Nachfolge. Somit wird durch ein fast-forward der Zeiger des aktuellen Branches (main-Branch) auf die letzte Version des zu mergenden Branches (feature-Branch) bewegt.
Möchte man keinen fast-forward-Merge, sondern immer einen neuen merge-Commit haben, kann dies mit --no-ff erzwungen werden.

recursive (3-Wege-Merge, merge-Commit)
…​
Beispiel: Git-Kommandos
$ git switch -c feature
$   vi feature.txt → git add . → git commit -m "..."
$   vi feature.txt → git add . → git commit -m "..."
$ git switch main
$   vi main.txt → git add . → git commit -m "..."
$ git merge feature

Gibt es im aktuellen Branch (main-Branch) Versionen, die nach der Abspaltung des zu mergenden Branches (feature-Branch) erstellt worden sind, wird beim Merge im aktuellen Branch (main-Branch) eine neue Version (merge-Commit) erstellt. Diese enthält das vereinte Ergebnis aus den letzten Versionen der zu mergenden Branches.

Hinweise: rebase

rebase

…​

Mit einem Rebase wird die Basis, auf der die Versionen in einem Branch basieren, auf eine neuere Version aktualisiert.

Beispiel: Git-Kommandos
$ git switch -c feature
$   vi feature.txt → git add . → git commit -m "..."
$   vi feature.txt → git add . → git commit -m "..."
$ git switch main
$   vi main.txt → git add . → git commit -m "..."
$ git switch feature
$ git rebase main

Wird zusätzlich noch die Option -i verwendet, können die neu erstellt Commits zusätzlich verändert und zusammengefasst werden. (Siehe: Aufgabe 2.2)

Ein Rebase erzeugt immer neue Versionen (Commits-IDs, Commit-Inhalte) was in Verbindung mit öffentlichen und geteilten Team-Repositories zu Problemen führen kann.
  • Rebase ist hilfreich, wenn bspw. im main-Branch eine neue Version (bugfix-Commit) hinzukommt, welche für die weitere Bearbeitung im feature-Branch ebenfalls relevant ist.

  • Ebenfalls kann es hilfreich sein, wenn man eine flache Historie im Repository haben möchte, so dass bei einem Merge statt eines merge-Commits ein fast-forward-Merge durchgeführt werden kann.

    TODO Grafik (Tafelbild): Merge-Commits vs Fast Forward Merges

Hinweise: Aktualisierung eines Branches

Der feature-Branch kann während seiner Bearbeitung wie folgt auf zwischenzeitlich neuere Versionen im main-Branch aktualisiert werden.

Variante 1: rebase feature-Branch auf main-Branch
$ git switch feature
$ git rebase main
Variante 2: merge main-Branch auf feature-Branch
$ git switch feature
$ git merge main
  • Für die Aktualisierung eines in der Version länger zurückliegenden oder mit einem alten Feature abgeschlossenen feature-Branches ist die Variante 2 mit einem Merge empfohlen.

  • Ein Rebase des feature-Branches kann auch sinnvoll sein, um zwischenzeitliche neuere Versionen im main-Branch vor einem Merge in diesen im feature-Branch erst zu testen bzw. mögliche Konflikte zu lösen. Andernfalls fallen die Konflikte erst beim Merge in den main-Branch auf.

Hinweise: Aussagekräftige Branch-Namen

Verwendet man mehrere Branches im Repository, ist es sinnvoll aussagekräftige Namen zu verwenden und im Team entsprechende Namenskonventionen festzulegen.

Namen mit Trennzeichen:

Namen könnten mit einem Wort für die Art des Branches beginnen gefolgt von spezifischen Informationen. Trennzeichen für eine bessere Lesbarkeit könnten - und _ sein.

  • Art: hotfix-, fix-, bugfix-, feature-, update-, test-, etc.

  • Info: feature-pdf-generation, bugfix-issue-12, update-js-framework, etc.

Namen mit hierarchischer Struktur:

Eine weitere Variante wäre mit Hilfe von / (forward slash) hierarchische Branch-Namen zu verwenden. So könnten bspw. mit git branch --list 'feature/*' oder git show-branch 'feature/*' alle vorhandenen bzw. Commits zu den feature-Branches aufgelistet werden.

  • Bspw.: feature/pdf-generation, bugfix/issue-12, bugfix/issue-15, etc.

  • Einige Clients bilden die Branches dann auch als navigierbare Struktur ab:

    ├── bugfix
    │   ├── issue-12
    │   └── issue-15
    ├── feature
    │   ├── pdf-generation
    │   └── xyz
    └── main
Branch-Namen dürfen nicht mit einem / enden.
Beispiel: Verwendung von Branches mit merge und rebase

Das Repository befindet sich im main-Branch. Für ein neues Feature und einen zwischenzeitlichen Bugfix werden ein feature-Branch und ein bugfix-Branch angelegt. Fertig bearbeitet werden die beiden neuen Branches in den main-Branch aufgenommen und anschließend entfernt.

$ git switch -c feature (1)
$ vi feature.txt → git add . → git commit -m "Add ..." (2)
$ vi feature.txt → git add . → git commit -m "Change ..."
$ git switch main (3)
$ git switch -c bugfix (4)
$ vi main.txt → git add . → git commit -m "Bugfix ..."
$ vi main.txt → git add . → git commit -m "Bugfix ..."
$ git rebase -i (5)
$ git switch main (6)
$ git merge bugfix → git branch -d bugfix (7)
$ git switch feature (8)
$ vi feature.txt → git add . → git commit -m "Change ..."
$ git switch main (9)
$ git merge --no-ff feature → git branch -d feature (10)
1 feature-Branch vom main-Branch erstellen und hin wechseln.
2 Feature bearbeiten und als neuen Commit in den aktuellen Branch aufnehmen.
Die Option -a lässt git commit …​ vorher geänderte und gelöschte Dateien mit aufnehmen.
3 in den main-Branch wechseln
4 bugfix-Branch vom main-Branch erstellen und hin wechseln
5 bugfix-Commits mit rebasing zu einem neuen Commit zusammenfassen
6 in den main-Branch wechseln
7 bugfix-Branch in den main-Branch mergen (optional mit --no-ff) und anschließend löschen
8 in den feature-Branch wechseln
9 in den main-Branch wechseln
10 feature-Branch in den main-Branch mergen und anschließend löschen
Tabelle 1. Hilfreiche Branching Kommandos
Kommando Beschreibung

git branch

lokale Branches auflisten
-r remote Branches, -a local und remote Branches

git show-branch

Branches mit ihren Commits auflisten
-r remote Branches, -a local und remote Branches

git branch <new-branch>

Branch anlegen

git switch <branch>
git checkout <branch>

Branch wechseln

git switch -

Zum vorherigen Branch wechseln

git switch -c <new-branch>
git checkout -b <new-branch>

Branch anlegen und wechseln. Kurzform für git branch <new-branch> gefolgt von einem git switch <new-branch> oder git checkout <new-branch>.

git branch -d <branch>

Branch löschen (-d merged, -D unmerged)

git merge <branch>

Versionen aus <branch> in den aktuellen Branch aufnehmen.

git merge --no-ff <branch>

Kein fast forward merge, sondern immer neuer merge-Commit

git rebase <branch>

Versionen aus dem aktuellen Branch auf die aktuelle Version von <branch> aktualisieren. Mit der Option -i können Commits angepasst und zusammengefasst werden.

git diff <branch1>..<branch2>

Unterschiede zwischen <branch1> und <branch2> anzeigen

Aufgabe 1.1 - Branches (local)

  1. Legen Sie in Ihrem Repository ein neues Asciidoctor-Dokument temp.adoc mit folgendem Inhalt an:

    Vorlage: temp.adoc
    = Title
    
    == Beschreibung
    
    <insert_bugfix_here>
    
    == Feature 1
    
    <insert_feature_here>
    
    == Feature 2
    
    <insert_feature_here>
  2. Nehmen Sie das eben angelegte Dokument in den main-Branch als neue Version auf und pushen Sie es in Ihr remote-Repository.

  3. Legen Sie ausgehend vom main-Branch einen neuen lokalen feature-Branch an und wechseln Sie in diesen. Fügen Sie in den Abschnitte Feature 1 neuen Text, verteilt auf zwei Commits, ein.

  4. Legen Sie ausgehend vom main-Branch einen neuen lokalen bugfix-Branch an und wechseln Sie in diesen. Korrigieren Sie, verteilt auf zwei Commits, die beiden Bugfixes im Abschnitt Beschreibung. Führen Sie anschließend ein interaktives Rebasing aus, um die beiden Bugfix-Änderungen in einem Commit zu vereinen. Jetzt übernehmen (mergen) Sie die Änderungen vom bugfix-Branch in den main-Branch und entfernen den bugfix-Branch. Synchronisieren Sie Ihre Änderung in Ihr remote-Repository.

  5. Wechseln Sie in den feature-Branch und fügen in den Abschnitte Feature 1 weiteren Text als neuen Commit ein. Nehmen Sie anschließend die Änderungen aus dem feature-Branch in den main-Branch auf und entfernen den feature-Branch. Synchronisieren Sie Ihre Änderung in Ihr remote-Repository.

Aufgabe 1.2 - Branches (remote)

Lokale Branches können auf einem remote-Repository bereitgestellt werden, so dass diese öffentlich bzw. von Teammitglieder mit genutzt werden können.

  1. Legen Sie in Ihrem Repository einen neuen lokalen feature-x-Branch an und wechseln Sie in diesen.

  2. Nehmen Sie in den feature-x-Branch ein paar Commits auf.

  3. Veröffentlichen Sie Ihren lokalen feature-x-Branch auf Ihrem remote-Repository.

  4. Übernehmen (mergen) Sie im lokalen Repository die Änderungen vom feature-x-Branch in den main-Branch und entfernen Sie anschließend den lokalen feature-x-Branch. Synchronisieren Sie Ihre Änderung in Ihr remote-Repository.

  5. Entfernen Sie jetzt den feature-x-Branch auf Ihrem remote-Repository.

Hinweise: remote-Branches
Neuen local-Branch in das remote-Repository aufnehmen:
$ git push -u origin <new_local_branch> (1)
1 Nimmt einen neuen lokalen Branch <new_local_branch> in das remote-Repository (origin) auf und verbindet den lokalen mit diesem.
Neuen remote-Branch in das lokale Repository aufnehmen:
$ git pull (1) (2)
$ git switch <new_remote_branch> (3)
1 lokalen Stand vom remote-Repository aktualisieren
2 Alternativ geht auch ein git fetch, wenn man nur neue Branches und Versionen laden möchte, ohne diese schon mit dem Arbeitsverzeichnis zu mergen.
3 In den neuen remote-Branch wechseln. Da er lokal nicht vorhanden ist, wird automatisch ein gleichnamiger lokaler Branch angelegt und mit dem remote-Branch verbunden.
local-Branch und remote-Branch synchronisieren:
$ git pull (1)
$ git pull origin <branch> (2)

$ git push --all (3)
$ git push (4)
$ git push origin <branch> (5)
1 Lade alle Änderung von remote Branches, welche mit lokalen verbunden sind.
2 Lade nur <branch> Änderungen vom remote-Repository origin.
3 Sende alle Änderungen von local-Branches, welche mit remote-Branches verbunden sind.
4 Sende alle Änderungen vom aktuellen local-Branch, wenn er mit einem remote-Branch verbunden ist.
5 Sende alle Änderungen vom local-Branch <branch>, wenn er mit einem remote-Branch auf dem remote-Repository origin verbunden ist.
remote-Branch löschen:
git push <remote-repo> --delete <remote-branch> (1)
1 Löscht den <remote-branch> auf dem <remote-repo>. In der Regel lautet der <remote-repo> Name origin.
Veraltete Referenzen entfernen:

Sind die Branches remote und lokal gelöscht, aber ein git branch -a listet noch veraltete Referenzen zu remote-Branches auf, können Sie wie folgt entfernt werden:

% git remote prune origin

Seitenübersicht