Team Foundation Server mit GIT-Version-Control

Der Team Foundation Server bietet eine Basis für die Zusammenarbeit eines Entwicklungsteams. Dieser Artikel beschreibt, welche Unterschiede es gibt, wenn als Quelltextverwaltung für ein Projekt GIT ausgewählt wird.

 

Allgemeines

Die Entwicklung von Software hat frühzeitig den Bedarf erkannt, Quelltexte in verschiedenen Versionen im Verlauf der Zeit zu sichern. Es entstanden lokal versionierende Programme. In der nächsten Stufe sorgten die Quelltextverwaltungen dafür, Änderungen verschiedener Entwickler zusammenzuführen. Ein zentraler Server ist die Ablage (Repository), mit der jeder Entwickler Änderungen austauscht.

Jedoch hat nicht jeder Entwickler permanenten Zugriff auf eine zentrale Instanz:

  • Ohne Internetverbindung oder ohne VPN ist der Server nicht erreichbar.
  • Die Berechtigung ist nur temporär oder nur lesend erteilt.
  • Änderungen sollen durch einen Verantwortlichen geprüft und freigegeben werden.

Genau diese Anforderungen haben zur Entwicklung der dezentralen Versionskontrollsysteme geführt. Allen voran ist hier GIT zu nennen, das in der Open Source Szene den größten Zuspruch erhält.

Mit den 2013er-Versionen des Team Foundation Servers und Visual Studio (Update 1) hält die native GIT-Unterstützung Einzug in die Microsoft-Welt. Daher ist die Frage auch im Geschäftsalltag zu beantworten, welche Versionskontrolle in einem TFS-Projekt eingesetzt werden soll.

 

Visual Studio verbinden

Die Verbindung zum Team Foundation Server oder der Cloud-Version läuft in der bekannten Art:

 

Connect to Team Foundation Server

 

Nach der Auswahl der relevanten Projekte werden sie aufgelistet:

 

Projektliste

 

Tipp: Als Merkmal für die GIT-Versionskontrolle wird das GIT-Logo als Überlagerung dargestellt.

Ein Doppelklick auf das Projekt aus der Projektsammlung öffnet die HOME-Seite des Team Explorers.

 

Home-Seite im Team Explorer

 

Tipp: Das Visual Studio blendet die gelb hinterlegten Nachrichten als Hilfestellung ein. Gerade für den Einstieg ist diese Unterstützung sehr nützlich.

Ähnlich dem Workspace-Mapping ist der erste Schritt, eine lokale Kopie des Repositories zu erstellen. Dieser Schritt wird als Clone bezeichnet. Diese lokale Kopie ist das persönliche Repository des Entwicklers. Hier kann er beliege Änderungen eintragen, Verzweigungen erstellen und zusammenführen, Markierungen vergeben und Änderungen mit anderen Repositories austauschen.

 

Nun zeigt die HOME-Seite alle Solutions im aktuellen Branch an.

 

HOME-Seite mit Solutions-Auflistung

 

Tipp: Der Wechsel von zentralen Versionskontrollsysteme zu dezentralen Systemen erfordert auch ein Umdenken bezüglich der Speicherung auf dem Server. Die Einrichtung eines Subversion oder Team Foundation Version Control-Repositories war und ist teuer. Aus diesem Grund wurde ein Repository gern in Unterverzeichnisse aufgeteilt, dort wurden dann die unabhängigen Projekte (=Entwicklungsaufträge) gemeinsam verwaltet. Beispielsweise gibt es ein Repository für die Abteilung, für jedes einzelne und zeitlich versetzte Projekt ein dediziertes Unterverzeichnis. Das Anlegen eines lokalen Repositories mit GIT ist dagegen mühelos möglich. Deshalb bevorzugt GIT ein Vorgehen, jedes Projekt (=Entwicklungsauftrag) in ein einzelnes Repository zu legen.

 

Grundlagen zu Verzweigungen

Eine Verzweigung bezeichnet einen Punkt in einem Repository, an dem zwei Entwicklungsstränge entstehen. Jeder Strang wird unabhängig behandelt und kann für unterschiedliche Zwecke genutzt werden.

  1. Umfangreiche Funktionen werden entwickelt, ohne die übrigen Entwickler zu beeinflussen.
  2. Experimentelle Funktionen werden entwickelt, jedoch nicht weiter verfolgt.
  3. Mehrere Versionen eines Produkts werden gleichzeitig entwickelt.
  4. Funktionen werden entwickelt, gelangen jedoch erst nach Freigabe in die Hauptentwicklung.

Aus den Beispielen lässt sich erkennen, dass neben dem Abspalten (branch) auch das Zusammenführen (merge) von Zweigen ein wichtiger Bestandteil der Arbeit darstellt.

An dieser Stelle spielen verteilte Versionskontrollsysteme ihre Stärken aus. Konstruktionsbedingt müssen sie robust mit gleichzeitigen Änderungen und dem Zusammenführen von Zweigen umgehen.

Es gibt verschiedene Modelle zur Beschreibung, wie der Umgang mit Branches gehandhabt wird:

  • je Produkthauptversion, z. B. QUAM 5.0, QUAM 6.0
  • je Produktnebenversion, z. B. QUAM 5.0, QUAM 5.1, QUAM 5.2
  • je Patch, Hotfix, Bugfix, z. B. QUAM 5.0 mit Hotfix 1
  • je Feature, z. B. Entwicklung eines neuen WebParts

 

Tipp: Unüblich ist ein Branch je Entwickler, da jeder Entwickler bereits sein lokales Repository besitzt.

Nach dem Erzeugen eines Zweigs werden sie auch wieder zusammengeführt oder Änderungen zwischen den Zweigen ausgetauscht. Auch dafür gibt es verschiedene Modelle, die je nach Team-Größe und Anforderung variiert werden können. Beispielhaft seien genannt:

  1. Zentralisierter Workflow: jeder Entwickler arbeitet gleichberechtigt mit einem zentralen Repository
  2. Integrationsmanager: jeder Entwickler liest aus einem zentralen Repository. Änderungen werden einem Integrationmanager bereitgestellt, der sie in das zentrale Repository übertragen darf.
  3. Diktator und Leutnants: jeder Entwickler list aus einem zentralen Repository. Änderungen werden dem Leutnant (Integrationsmanager) eines bestimmten Teilsystems übergeben. Von dort bestimmt der Diktator, welche Änderungen in das zentrale Repository einfließen.

Wichtig ist die Unterscheidung zwischen lokalen Zweigen und entfernten Zweigen. Nicht jeder entfernte Zweig existiert auch im lokalen Repository, nicht jeder lokale Zweig wird veröffentlicht.

 

Feature-Branch

Für diesen Fall wird angenommen, dass zur Entwicklung ein lokaler Zweig für die neue Funktion angelegt wird. Anschließend wird der Zweig über einen Pull-Request durch einen Bereichsverantwortlichen in den master-Zweig übernommen.

Auf der Branches-Seite des Team Explorers werden alle lokalen Zweige dargestellt. Die Published Branches existieren auch im entfernten Repository. Unpublished branches sind nur in diesem Repository-Klon vorhanden.

 

Branches

 

Zuerst erzeugen wir den neuen Zweig für die Implementierung:

Create branch

 

Als Bezeichnung wählen wir features/sum, weil wir eine neue Summen-Funktion entwickeln. Als Quelle des Zweigs nutzen wir den master-Zweig, er enthält die jeweils neueste Version. Damit wir gleich mit der Arbeit beginnen können, wechseln wir auf den Zweig durch Aktivieren von Checkout branch.

Die Branches-Seite zeigt nun den lokalen Zweig an. Durch die Fettschrift und die Ausgabe im oberen Teil des Fensters ist erkennbar, dass sich unsere Dateien auf dem Stand dieses Zweigs befinden.

 

Branches

 

Wir können nun die neue Funktion integrieren.

Anschließend werden Änderungen in den lokalen Repository-Verlauf eingetragen (commit):

 

Commit

 

Tipp: GIT kümmert sich nicht automatisch um zusätzliche Dateien. Deshalb sollte der Abschnitt Untracked Files kontrolliert werden, ob dort projektrelevante Dateien vorliegen. Unwichtige Dateien können über die Datei .gitignore dauerhaft ausgeblendet werden.

 

Commit abgeschlossen

 

Ein Klick auf Commit übernimmt die ausgewählten Änderungen. Es erscheint die Meldung mit dem wichtigen Hinweis, dass Änderungen nur lokal abgelegt sind und noch zum Server gesendet werden müssen. Weiterhin ist die Bezeichnung des Commits zu sehen (Commit 1652055a). Jeder Commit in einem GIT-Repository ist durch einen Hash-Wert eindeutig gekennzeichnet.

Jetzt wird es Zeit, dem übrigen Team die neue Funktion bereitzustellen. Dazu muss unser lokaler Zweig veröffentlicht werden. Auf der Branches-Seite genügt ein Rechtsklick auf den unveröffentlichten Zweig, um die Veröffentlichung im Kontextmenü auszuwählen.

 

Branches

 

Pull Request

Im Rahmen eines Code Reviews soll nun die neue Funktion in den master-Branch übernommen werden. Ab Visual Studio 2015 lässt sich der folgende Vorgang auch dort ausführen. Für Visual Studio 2013 gibt es die Funktion nur im Web Portal. Im Bereich Code, Unterabschnitt Pull Requests, lässt sich ein neuer Pull Request anlegen:

 

Pull Request anlegen

 

Zuerst wird ein Quell-Zweig (features/sum) und der Ziel-Zweig (master) ausgewählt. Der Pull Request enthält einen Titel, eine optionale Beschreibung, die Liste der Commits und die Darstellung aller Dateiänderungen. Der Request kann nun angelegt werden. Die als Prüfer ausgewählte Gruppe erhält eine Benachrichtigung und kann nun den Pull Request begutachten:

Pull Request bearbeiten

 

Zu einem Pull Request können Kommentare ausgetauscht werden. Jeder Prüfer kann ein Votum abgeben: genehmigt, genehmigt mit Anmerkungen, abgelehnt, etc. Besonders wichtig ist der Hinweis, dass keine Merge-Konflikte auftreten (oben rechts). Vertragen sich die eingereichten Änderungen nicht mit dem akzeptierten Gemeinschaftsstand, dann ist der Absender des Pull Requests in der Pflicht, seine Änderungen kompatibel umzuschreiben. In klassischen zentralen Versionskontrollsystemen fiele diese Arbeit dem Reviewer zu. Der Einsatz von GIT verlagert diese Tätigkeit an den richtigen Adressaten, nämlich den Urheber der Änderungen.

Nach dem Review kann der Prüfer direkt im Browser den Merge durchführen. Die neuentwickelte Funktion ist damit integriert.

 

Fetch, Push, Pull, Sync

Das lokale Repository kennt die Zusammenführung im zentralen Repository noch nicht. Zusätzlich können weitere Commits vorliegen, die wir ebenfalls übertragen wollen. Dazu dient die Seite Unsynched Commits im Team Explorer.

Die Funktion Fetch lädt alle neuen Commits vom Zentralrepository in die lokale Datenbank. Dabei werden noch keine Dateien verändert. Die Liste der vorliegenden Commits für den Master-Branch wird nun gefüllt:

Unsynched commits

 

Die zweite Funktion Pull sorgt dafür, dass zuerst ein Fetch ausgeführt wird und anschließend die Änderungen auf die lokalen Dateien angewendet werden.

Um eigene Änderungen an das zentrale Repository zu senden, steht die Funktion Push bereit. Hierbei kann es erforderlich werden, zuerst ein Pull auszuführen, um die neuesten Änderungen zu haben. Erst dann können Commits übertragen werden.

Die prominente Sync-Schaltfläche ist Push und Pull in einem. Sie lädt alle Änderungen vom Server und versendet die eigenen Commits.

 

Merge

Zum Abschluss wird nun der Stand des master-Branch übertrangen in den stable-Branch. Dazu wird ein Merge ausgeführt.

Merge

 

Aus dem Stand im stable-Branch könnte nun ein neues Release abgeleitet werden.