Git-Workflows: Der zentralisierte Workflow (Teil 2)

Wenn Teams Git adaptieren wollen, ist es oft problematisch, den richtigen Einstieg für die Implementierung zu finden. Daher erläutern wir in mehreren Beiträgen die am häufigsten genutzten Git-Workflows. Der erste Teil dieses Artikels ist auf den klassischen zentralisierten Workflow eingegangen, den viele Teams von Subversion kennen. Nun zeigen wir, wie ein exemplarisches kleines Team mithilfe dieses Workflows zusammenarbeitet: Entwickler A und Entwickler B können an verschiedenen Features arbeiten und ihre Beiträge via zentralisiertem Repository teilen.

Zentrales Repository aufsetzen

git-workflow zentrale Repository

Zunächst muss das zentrale Repository auf einem Server angelegt werden. Für ein neues Projekt kann ein leeres Repository initialisiert werden, andernfalls muss ein bestehendes Git- oder SVN-Repository importiert werden:

ssh user@host
git init --bare /path/to/repo.git

Dabei muss user ein valider SSH-Nutzername sein, host ist die Domain oder IP-Adresse des Servers, /path/to/repo.git zeigt den Ort an, an dem das Repository gespeichert werden soll. Die Erweiterung .git wird dem Repository-Namen standardmäßig angefügt, um zu indizieren, dass es sich um eine leeres Repo handelt.

Jeder klont das zentrale Repository

git-workflow Repository klonen

Als nächstes erzeugt jeder Entwickler eine lokale Kopie des kompletten Projekts. Das erfolgt mit dem Git-Befehl git clone:

git clone ssh://user@host/path/to/repo.git

Wird ein Repository geklont, fügt Git automatisch einen Shortcut namens origin hinzu, der auf das Ursprungs-Repository zurückverweist, um künftig mit dieser interagieren zu können.

Entwickler A arbeitet an seinem Feature

git-workflow Feature 1

In seinem lokalen Repository kann Entwickler A nach dem Standard-Commit-Prozess von Git an seinen Feautures arbeiten: Edit, Stage, Commit. Der Staging-Bereich bietet eine Möglichkeit, einen Commit vorzubereiten, ohne der Working Directory jede Änderung hinzufügen zu müssen. So lassen sich höchst fokussierte Commits erzeugen, selbst wenn viele lokale Änderungen vorgenommen wurden.

git status # State der Repo anzeigen
git add # Datei stagen
git commit # Datei committen

Da dieses Befehle lokale Commits erzeugen, kann Entwickler A sie so oft ausführen, wie er möchte, ohne sich Sorgen machen zu müssen, was in der zentralen Repository passiert. Das kann bei großen Features sehr nützlich sein, die in kleinere Bestandteile zerlegt werden müssen.

Entwickler B arbeitet an seinem Feature

git-workflow Feature 2

In der Zwischenzeit arbeitet Entwickler B in seinem eigenen lokalen Repository an seinem Feature – ebenfalls nach dem Edit/Stage/Commit-Prozess. Er muss sich ebenfalls nicht darum kümmern, was im zentralen Repository los ist, und er muss sich auch nicht darum kümmern, was im Repo von Entwickler A vor sich geht, denn alle lokalen Repositories sind privat.

Entwickler A veröffentlicht sein Feature

git-workflow Feature 1 ausliefern

Wenn Entwickler A sein Feature fertig hat, wird er seine lokalen Commits dem zentralen Repository hinzufügen wollen, damit andere Teammitglieder darauf zugreifen können. Dies erfolgt mit dem Befehl git push:

git push origin master

Wie gesagt, ist origin die Remote-Verbindung zum zentralen Repository, die Git erstellt hat, als Entwickler A die Repo geklont hat. Das Argument master weist Git an, dass der origin-Master-Branch wie der lokale Master-Branch aussehen soll. Da das zentrale Repository bislang nicht aktualisiert wurde, wird es hier keine Konflikte geben und der Push wird wie gewünscht erfolgen.

Entwickler B versucht, sein Feature zu veröffentlichen

git-workflow Feature 2 ausliefern

Nachdem Entwickler A sein Feature gepusht hat, möchte das auch Entwickler B tun. Er nutzt denselben Befehl:

git push origin master

Doch da die lokale History inzwischen vom zentralen Repository abweicht, wird Git die Aufforderung verweigern und eine Fehlermeldung ausgeben, die Entwickler B daran hindert, offizielle Commits zu überschreiben:

error: failed to push some refs to '/path/to/repo.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

Er muss die Aktualisierungen von Entwickler A in sein eigenes Repository ziehen, diese mit seinen eigenen lokalen Änderungen integrieren und es erneut versuchen.

Rebasing auf den Commits von Entwickler A

git-workflow rebase

Entwickler B kann den Befehl git pull nutzen, um Upstream-Änderungen in sein Repository einzubinden. Dieses Kommando funktioniert ähnlich wie svn update – es zieht die gesamte Upstream-Commit-History in das lokale Repository von Entwickler B und versucht, diese mit seinen lokalen Commits zu integrieren:

git pull --rebase origin master

Die Option --rebase sagt Git, dass alle Commits von Entwickler B zuoberst auf den Master-Branch aufgesetzt werden sollen, nachdem die Synchronisation mit den Änderungen aus dem zentralen Repository erfolgt ist.

git-workflow_rebase_2

Der Pull würde auch funktionieren, wenn B diese Option vergessen hätte, aber das würde immer dann, wenn jemand einen Synchronisation mit dem zentralen Repository herbeiführen will, zu einem überflüssigen Merge Commit führen. Für diesen Workflow ist rebase immer besser als das Generieren eines Merge Commits.

Entwickler B löst einen Merge-Konflikt auf

git-workflow Merge-Konflikt

Das Rebasing funktioniert, indem ein lokaler Commit nach dem anderen in den aktualisierten Master-Branch transferiert wird. Das bedeutet, dass sich Merge-Konflikten viel leichter auf die Spur kommen lässt – im Gegensatz zu einem massiven Merge Commit.

Dadurch sind die Commits so fokussiert wie möglich und ist für eine saubere Projekt-Historie gesorgt. Zugleich ist es deutlich einfacher herauszufinden, wo Bugs eingeführt wurden, und Änderungen – falls nötig – zurückzurollen, ohne dass sich dies großartig auf das Projekt auswirkt.

Wenn A und B an Features arbeiten, die unabhängig voneinander sind, ist es unwahrscheinlich, dass der Rebasing-Prozess zu Konflikten führt. Falls aber doch, pausiert Git das Rebasing beim aktuellen Commit und gibt eine Fehlermeldung und entsprechende Anweisungen aus:

CONFLICT (content): Merge conflict in <some-file>

git-workflow_Konflikte_auflösen_2

Ein großer Vorteil von Git besteht darin, dass jeder seine eigenen Merge-Konflikte selbst auflösen kann. In diesem Beispiel würde Entwickler B den Befehl git status ausführen, um zu sehen, wo das Problem liegt. Dateien, die Konflikte ausgelöst haben, erscheinen in der Sektion Unmerged paths.

# Unmerged paths:
# (use "git reset HEAD <some-file>..." to unstage)
# (use "git add/rm <some-file>..." as appropriate to mark resolution)
#
# both modified: <some-file>>

Dann kann B die entsprechenden Dateien weiter bearbeiten, diese wie üblich stagen und mit git rebase integrieren:

git add <some-file>
git rebase --continue </some-file>

Ist alles okay, macht Git mit dem nächsten Commit weiter und wiederholt diesen Prozess bei allen weiteren Commits, die Konflikte verursachen.

Und falls sich die Auflösung von Konflikten problematischer als gedacht gestaltet, kann das Rebasing mit dem folgenden Kommando abgebrochen werden:

git rebase --abort

Der Zustand vor der initialen Ausführung des Rebasings wird wieder hergestellt.

Entwickler B veröffentlicht sein Feature

git-workflow Feature 2 ausliefern 2

Nach der Synchronisation mit dem zentralen Repository ist Entwickler B in der Lage, seine Änderungen erfolgreich zu veröffentlichen:

git push origin master

Fazit

Es ist also möglich, den traditionellen Subversion-Entwicklungs-Workflow mit einigen wenigen Git-Befehlen zu replizieren. Das ist gut für Teams, die von SVN umsteigen, macht sich die verteilte Natur von Git aber nicht zunutze. Wenn das Team einmal mit dem zentralisierten Workflow vertraut ist, sollte es sich definitiv mit den Vorteilen des Feature-Branch-Workflows beschäftigen. Auf diesen kommen wir im nächsten Git-Artikel zu sprechen.

Weiterführende Infos: Ihr Partner für Git und Stash

Kennen Sie Stash von Atlassian? Stash bietet eine zentrale Lösung zum Management des gesamten distributierten Codes: Hier kommen alle Git-Repositories im Unternehmen zusammen, hier finden Entwickler immer die letzte offizielle Version eines Projekts, hier können Projektverantwortliche Berechtigungen kontrollieren, um sicherzustellen, dass die richtigen Nutzer Zugriff auf den richtigen Code haben.

Möchten Sie mehr erfahren? Wir sind offizieller Vertriebspartner von Atlassian und einer der größten Atlassian Experts Partner weltweit. Gerne unterstützen wir Sie bei der Evaluierung, Lizenzierung und Adaption von Stash. Und wenn Sie Atlassian-Lizenzen bei //SEIBERT/MEDIA kaufen, gewähren wir Ihnen einen Rabatt in Höhe von 10% der Lizenzkosten in Form von Beratungsleistungen. Bitte sprechen Sie uns einfach an.

Git und Stash in der Software-Entwicklung: Eine Einführung
Stash in der Praxis
Stash Enterprise: Git-Repository-Management in großen Unternehmen
So funktioniert die Lizenzierung von Atlassian-Produkten