-
1. Erste Schritte
-
2. Git Grundlagen
-
3. Git Branching
- 3.1 Branches auf einen Blick
- 3.2 Einfaches Branching und Merging
- 3.3 Branch-Management
- 3.4 Branching-Workflows
- 3.5 Remote-Branches
- 3.6 Rebasing
- 3.7 Zusammenfassung
-
4. Git auf dem Server
- 4.1 Die Protokolle
- 4.2 Git auf einem Server einrichten
- 4.3 Erstellung eines SSH-Public-Keys
- 4.4 Einrichten des Servers
- 4.5 Git-Daemon
- 4.6 Smart HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Von Drittanbietern gehostete Optionen
- 4.10 Zusammenfassung
-
5. Verteiltes Git
-
6. GitHub
-
7. Git Tools
- 7.1 Revisions-Auswahl
- 7.2 Interaktives Stagen
- 7.3 Stashen und Bereinigen
- 7.4 Deine Arbeit signieren
- 7.5 Suchen
- 7.6 Den Verlauf umschreiben
- 7.7 Reset entzaubert
- 7.8 Fortgeschrittenes Merging
- 7.9 Rerere
- 7.10 Debuggen mit Git
- 7.11 Submodule
- 7.12 Bundling
- 7.13 Replace (Ersetzen)
- 7.14 Anmeldeinformationen speichern
- 7.15 Zusammenfassung
-
8. Git einrichten
- 8.1 Git Konfiguration
- 8.2 Git-Attribute
- 8.3 Git Hooks
- 8.4 Beispiel für Git-forcierte Regeln
- 8.5 Zusammenfassung
-
9. Git und andere VCS-Systeme
- 9.1 Git als Client
- 9.2 Migration zu Git
- 9.3 Zusammenfassung
-
10. Git Interna
-
A1. Anhang A: Git in anderen Umgebungen
- A1.1 Grafische Schnittstellen
- A1.2 Git in Visual Studio
- A1.3 Git in Visual Studio Code
- A1.4 Git in IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine
- A1.5 Git in Sublime Text
- A1.6 Git in Bash
- A1.7 Git in Zsh
- A1.8 Git in PowerShell
- A1.9 Zusammenfassung
-
A2. Anhang B: Git in deine Anwendungen einbetten
- A2.1 Die Git-Kommandozeile
- A2.2 Libgit2
- A2.3 JGit
- A2.4 go-git
- A2.5 Dulwich
-
A3. Anhang C: Git Kommandos
- A3.1 Setup und Konfiguration
- A3.2 Projekte importieren und erstellen
- A3.3 Einfache Snapshot-Funktionen
- A3.4 Branching und Merging
- A3.5 Projekte gemeinsam nutzen und aktualisieren
- A3.6 Kontrollieren und Vergleichen
- A3.7 Debugging
- A3.8 Patchen bzw. Fehlerkorrektur
- A3.9 E-mails
- A3.10 Externe Systeme
- A3.11 Administration
- A3.12 Basisbefehle
3.2 Git Branching - Einfaches Branching und Merging
Einfaches Branching und Merging
Lass uns ein einfaches Beispiel für das Verzweigen und Zusammenführen (engl. branching and merging) anschauen, wie es dir in einem praxisnahen Workflow begegnen könnte. Stell dir vor, du führst folgende Schritte aus:
-
Du arbeitest an einer Website
-
Du erstellst einen Branch für eine neue Anwendergeschichte (engl. User Story), an der du gerade arbeitest
-
Du erledigst einige Arbeiten in diesem Branch
In diesem Moment erhältst du einen Anruf, dass ein anderes Problem kritisch ist und ein Hotfix benötigt wird. Dazu wirst du folgendes tun:
-
Du wechselst zu deinem Produktions-Branch
-
Du erstellst einen Branch, um den Hotfix einzufügen
-
Nachdem der Test abgeschlossen ist, mergst du den Hotfix-Branch und schiebst ihn in den Produktions-Branch
-
Du wechselst zurück zu deiner ursprünglichen Anwenderstory und arbeitest daran weiter
Einfaches Branching
Lass uns zunächst annehmen, du arbeitest an deinem Projekt und hast bereits ein paar Commits in deinem master
Branch gemacht.
Du hast dich dafür entschieden, an „Issue #53“ aus irgendeinem Fehlerverfolgungssystem, das deine Firma benutzt, zu arbeiten.
Um einen neuen Branch anzulegen und gleichzeitig zu diesem zu wechseln, kannst du die Anweisung git checkout
zusammen mit der Option -b
ausführen:
$ git checkout -b iss53
Switched to a new branch "iss53"
Das ist die Kurzform der beiden folgenden Befehle:
$ git branch iss53
$ git checkout iss53
Du arbeitest an deiner Website und führst einige Commits durch.
Sobald du das machst, bewegt das den iss53
Branch vorwärts, weil du in ihn gewechselt (engl. checked out) bist. Das bedeutet, dein HEAD
zeigt auf diesen Branch:
$ vim index.html
$ git commit -a -m 'Create new footer [issue 53]'
iss53
Branch hat sich bei deiner Arbeit vorwärts bewegtJetzt bekommst du einen Anruf, dass es ein Problem mit der Website gibt und du es umgehend beheben musst.
Bei Git musst du deinen Fix nicht zusammen mit den Änderungen bereitstellen, die du bereits an iss53
vorgenommen hast. Du musst auch keinen großen Aufwand betreiben, diese Änderungen rückgängig zu machen, bevor du an den neuen Hotfix arbeiten kannst, um die Produktionsumgebung zu fixen.
Alles, was du machen musst, ist zu deinem vorherigen master
Branch zu wechseln.
Beachten dabei, dass Git das Wechseln zu einem anderen Branch blockiert, falls dein Arbeitsverzeichnis oder dein Staging-Bereich nicht committete Modifikationen enthält, die Konflikte verursachen.
Generell ist es am besten, einen sauberen Zustand des Arbeitsbereichs anzustreben, bevor du Branches wechselst.
Es gibt Möglichkeiten, das zu umgehen (nämlich das Verstecken (engl. Stashen) und Revidieren (engl. Amending) von Änderungen), die wir später in Kapitel 7 Git Stashing behandeln werden.
Lass uns vorerst annehmen, du hast für alle deine Änderungen Commits durchgeführt, sodass du zu deinem vorherigen master
Branch wechseln kannst.
$ git checkout master
Switched to branch 'master'
Zu diesem Zeitpunkt befindet sich das Arbeitsverzeichnis des Projektes in exakt dem gleichen Zustand, in dem es sich befand, bevor du mit der Arbeit an „Issue #53“ begonnen hast und du kannst dich direkt auf den Hotfix konzentrieren. Das ist ein wichtiger Punkt, den du unbedingt beachten solltest: Wenn du die Branches wechselst, setzt Git dein Arbeitsverzeichnis zurück, um so auszusehen, wie es das letzte Mal war, als du in den Branch committed hast. Dateien werden automatisch hinzugefügt, entfernt und verändert, um sicherzustellen, dass deine Arbeitskopie auf demselben Stand ist wie zum Zeitpunkt deines letzten Commits auf diesem Branch.
Als Nächstes musst du dich um den Hotfix kümmern.
Lass uns einen hotfix
Branch erstellen, an dem du bis zu dessen Fertigstellung arbeitest:
$ git checkout -b hotfix
Switched to a new branch 'hotfix'
$ vim index.html
$ git commit -a -m 'Fix broken email address'
[hotfix 1fb7853] Fix broken email address
1 file changed, 2 insertions(+)
master
Branch basierender Hotfix-BranchDu kannst deine Tests durchführen, dich vergewissern, dass der Hotfix das macht, was du von ihm erwartest und schließlich den Branch hotfix
wieder in deinen master
Branch integrieren (engl. merge), um ihn in der Produktion einzusetzen.
Das machst du mit der Anweisung git merge
:
$ git checkout master
$ git merge hotfix
Updating f42c576..3a0874c
Fast-forward
index.html | 2 ++
1 file changed, 2 insertions(+)
Dir wird bei diesem Zusammenführen der Ausdruck „fast-forward“ auffallen.
Da der Commit C4
, auf den der von dir eingebundene Branch hotfix
zeigt, direkt vor dem Commit C2
liegt, auf dem du dich befindest, bewegt Git den Pointer einfach nach vorne.
Um es anders auszudrücken: Wenn du versuchst, einen Commit mit einem Commit zusammenzuführen, der durch Verfolgen der Historie des ersten Commits erreicht werden kann, vereinfacht Git die Dinge, indem er den Zeiger nach vorne bewegt, da es keine verzweigte Arbeiten gibt, die miteinander gemerged werden müssen – das wird als „fast-forward“ bezeichnet.
Deine Änderung befindet sich nun im Schnappschuss des Commits, auf den der master
Branch zeigt und du kannst deinen Fix ausliefern und installieren.
master
wurde zu hotfix
„fast-forwarded“Nachdem deine überaus wichtiger Fix bereitgestellt wurde, kannst du dich wieder dem zuwenden, woran du vorher gearbeitet hast.
Zunächst solltest du jedoch den hotfix
Branch löschen, weil du diesen nicht länger benötigst – schließlich verweist der master
Branch auf denselben Entwicklungsstand.
Du kannst ihn mit der Anweisung git branch
und der Option -d
löschen:
$ git branch -d hotfix
Deleted branch hotfix (3a0874c).
Jetzt kannst du zum vorherigen Branch wechseln, auf dem du mit deinen Arbeiten an „Issue #53“ begonnen hattest und daran weiter arbeiten.
$ git checkout iss53
Switched to branch "iss53"
$ vim index.html
$ git commit -a -m 'Finish the new footer [issue 53]'
[iss53 ad82d7a] Finish the new footer [issue 53]
1 file changed, 1 insertion(+)
iss53
fortsetzenErwähnenswert ist, dass die Änderungen, die du in deinem hotfix
Branch durchgeführt hast, nicht in den Dateien deines iss53
Branch enthalten sind.
Wenn du diese Änderungen übernehmen willst, kannst du deinen master
Branch in den iss53
Branch mergen, indem du git merge master
ausführst. Du kannst aber auch warten und dich später dazu entscheiden, den iss53
Branch in master
zu übernehmen (engl. pullen).
Einfaches Merging
Angenommen, du hast dich entschieden, dass dein Issue #53 abgeschlossen ist und du bereit bist, ihn in deinem Branch master
zu mergen.
Dann wirst du deinen iss53
Branch in den master
Branch mergen, so wie du es zuvor mit dem hotfix
Branch gemacht hast.
Du musst nur mit der Anweisung checkout
zum Branch wechseln, in welchen du etwas einfließen lassen willst und dann die Anweisung git merge
ausführen:
$ git checkout master
Switched to branch 'master'
$ git merge iss53
Merge made by the 'recursive' strategy.
index.html | 1 +
1 file changed, 1 insertion(+)
Das sieht ein bisschen anders aus, als das Merging mit dem hotfix
Branch, welches du zuvor gemacht hast.
Hier hat sich der Entwicklungsverlauf an einem früheren Zustand geteilt.
Da der Commit auf dem Branch, auf dem du dich gerade befindest, kein unmittelbarer Vorgänger des Branches ist, in den du mergst, muss Git einige Arbeiten erledigen.
In diesem Fall führt Git einen einfachen Drei-Wege-Merge durch, indem er die beiden Schnappschüsse verwendet, auf die das Branch-Ende und der gemeinsame Vorgänger der beiden zeigen.
merge
benutzt werdenAnstatt einfach den Zeiger des Branches vorwärts zu bewegen, erstellt Git einen neuen Schnappschuss, der aus dem Drei-Wege-Merge resultiert und erzeugt automatisch einen neuen Commit, der darauf zeigt. Das wird auch als Merge-Commit bezeichnet und ist ein Spezialfall, weil er mehr als nur einen Vorgänger hat.
Da deine Änderungen jetzt eingeflossen sind, hast du keine weitere Verwendung mehr für den iss53
Branch.
Du kannst den Issue in deinem Issue-Tracking-System schließen und den Branch löschen:
$ git branch -d iss53
Einfache Merge-Konflikte
Gelegentlich verläuft der Merge-Prozess nicht ganz reibungslos.
Wenn du in den beiden Branches, die du zusammenführen willst, an derselben Stelle in derselben Datei unterschiedliche Änderungen vorgenommen hast, wird Git nicht in der Lage sein, diese sauber zusammenzuführen.
Wenn dein Fix für „Issue #53“ den gleichen Teil einer Datei wie der Branch hotfix
geändert hat, erhältst du einen Merge-Konflikt, der ungefähr so aussieht:
$ git merge iss53
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
Git konnte einen neuen Merge-Commit nicht automatisch erstellen.
Es hat den Prozess angehalten, bis du den Konflikt beseitigt hast.
Wenn du sehen möchtest, welche Dateien zu irgendeinem Zeitpunkt nach einem Merge-Konflikt nicht zusammengeführt wurden, kannst du git status
ausführen:
$ git status
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: index.html
no changes added to commit (use "git add" and/or "git commit -a")
Alles, was Merge-Konflikte ausgelöst hat und nicht behoben wurde, wird als unmerged
angezeigt.
Git fügt den Dateien, die Konflikte haben, Standardmarkierungen zur Konfliktlösung hinzu, so dass du sie manuell öffnen und diese Konflikte lösen können.
Deine Datei enthält einen Bereich, der in etwa so aussieht:
<<<<<<< HEAD:index.html
<div id="footer">contact : email.support@github.com</div>
=======
<div id="footer">
please contact us at support@github.com
</div>
>>>>>>> iss53:index.html
Das bedeutet, die Version in HEAD
(das ist der aktuelle commit master
Branches, denn der wurde per checkout
aktiviert, als du den Merge
gestartet hast) ist der obere Teil des Blocks (alles oberhalb von =======
) und die Version aus dem iss53
Branch ist in dem darunter befindliche Teil.
Um den Konflikt zu lösen, musst du dich entweder für einen der beiden Teile entscheiden oder du führst die Inhalte selbst zusammen.
Du kannst diesen Konflikt beispielsweise lösen, indem du den gesamten Block durch diesen ersetzen:
<div id="footer">
please contact us at email.support@github.com
</div>
Diese Lösung hat von beiden Teilen etwas und die Zeilen mit <<<<<<<
, =======
und >>>>>>>
wurden vollständig entfernt.
Nachdem du alle problematischen Bereiche in allen von dem Konflikt betroffenen Dateien beseitigt hast, führst du einfach die Anweisung git add
für alle betroffenen Dateien aus, um sie als gelöst zu markieren.
Dieses ‚Staging‘ der Dateien markiert sie für Git als bereinigt.
Wenn du ein grafisches Tool benutzen möchtest, um die Probleme zu lösen, dann kannst du git mergetool
verwenden, welches ein passendes grafisches Merge-Tool startet und dich durch die Konfliktbereiche führt:
$ git mergetool
This message is displayed because 'merge.tool' is not configured.
See 'git mergetool --tool-help' or 'git help config' for more details.
'git mergetool' will now attempt to use one of the following tools:
opendiff kdiff3 tkdiff xxdiff meld tortoisemerge gvimdiff diffuse diffmerge ecmerge p4merge araxis bc3 codecompare vimdiff emerge
Merging:
index.html
Normal merge conflict for 'index.html':
{local}: modified file
{remote}: modified file
Hit return to start merge resolution tool (opendiff):
Wenn du ein anderes Merge-Tool anstelle des Standardwerkzeugs verwenden möchten (Git wählte in diesem Fall opendiff
, da die Anweisung auf einem Mac ausgeführt wurde), dann kannst du alle unterstützten Werkzeuge sehen, die oben nach „one of the following tools“ aufgelistet sind.
Tippe einfach den Namen des gewünschten Programms ein.
Anmerkung
|
Wenn du fortgeschrittenere Werkzeuge zur Lösung kniffliger Merge-Konflikte benötigst, erfährst du mehr darüber in Kapitel 7 Fortgeschrittenes Merging. |
Nachdem du das Merge-Tool beendet hast, wirst du von Git gefragt, ob das Zusammenführen erfolgreich war.
Wenn du das bestätigst, wird die Datei der Staging-Area hinzugefügt und der Konflikt als gelöst markiert.
Du kannst den Befehl git status
erneut ausführen, um zu überprüfen, ob alle Konflikte gelöst wurden:
$ git status
On branch master
All conflicts fixed but you are still merging.
(use "git commit" to conclude merge)
Changes to be committed:
modified: index.html
Wenn du damit zufrieden bist und geprüft hast, dass alles, was Konflikte aufwies, der Staging-Area hinzugefügt wurde, kannst du die Anweisung git commit
ausführen, um den Merge-Commit abzuschließen.
Die standardmäßige Commit-Nachricht sieht ungefähr so aus:
Merge branch 'iss53'
Conflicts:
index.html
#
# It looks like you may be committing a merge.
# If this is not correct, please remove the file
# .git/MERGE_HEAD
# and try again.
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# All conflicts fixed but you are still merging.
#
# Changes to be committed:
# modified: index.html
#
Du kannst diese Commit-Nachricht noch Details darüber hinzufügen, wie du diesen Merge-Konflikt gelöst hast. Es könnte für künftige Betrachter dieses Commits hilfreich sein, zu verstehen, warum du etwas getan hast, falls es nicht offensichtlich ist.