Git ist ein Arbeitsmittel für Programmierer, um
Richte die Umgebungsvariable PATH ein, so dass der "git" Befehl im Terminalfenster (Eingabeaufforderung) gefunden wird. Konfiguriere deinen Namen und Email Adresse mit dem Befehl:
git config --global user.name "John Doe" git config --global user.email johndoe@example.comNützlich ist auch der credential helper. Er speichert Passwörter, so dass du sie nicht wiederholt eingeben musst.
git config --global credential.helper store
<html> <head> </head> <body> Hello World! </body> </html>
Bevor man Git in diesem Projekt verwenden kann, muss man es initialisieren. Führe dazu den Befehl git init im Projektverzeichnis aus:
cd MeinProjekt git init
Dabei wird ein versteckter Ordner mit dem Namen ".git" angelegt, in dem Git später die Schnappschüsse von den Quelltexten abspeichert:
Der Inhalt dieses Ordners ist uninteressant und sollte niemals manuell verändert werden!
git add index.html git commit -m "Projekt begonnen"
Mit dem Befehl git add legst du fest, welche Datei(en) in den nächsten Schnappschuss aufgenommen werden sollen. Der Befehl git commit erstellt den Schnappschuss, mit einem kurzen Beschreibungstext.
Wenn du den Parameter -m weglässt, öffnet sich ein Text-Editor wo du den Kommentar eingeben sollst.
Nehme nun ein paar Änderungen an der HTML Datei vor:
<html> <head> <link rel="stylesheet" type="text/css" href="style.css"> </head> <body> Hallo schöne neue Welt! <p> Morgenstund hat Gold im Mund. </body> </html>
Und erstelle eine neue Datei mit Namen "style.css":
body { font-family:sans-serif; font-size:150%; }
Mit dem Befehl git status schauen wir uns den Zustand des Projektes an:
git status
Wir erstellen einen zweiten Schnappschuss.
git add style.css index.html git commit -m "index.html verändert und neues StyleSheet"
Kontrolliere das Ergebnis mit
gitk
Gitk zeigt uns die Historie des Projektes grafisch an:
Wir erstellen noch einen dritten Schnappschuss, wo wir den dummen Spruch aus der HTML Datei entfernen:
<html> <head> <link rel="stylesheet" type="text/css" href="style.css"> </head> <body> Hallo schöne neue Welt! </body> </html>
Da dieses mal keine neue Datei dazu gekommen ist, können wir auf den git add Befehl verzichten. Wir müssen dann aber beim commit den Parameter -a angeben, welcher alle geänderten (nicht neue) Dateien erfasst.
git commit -a -m "Dummen Spruch entfernt" gitk
Mache dich mit gitk vertraut. Schau Dir an, wie es die Historie deiner Änderungen anzeigt. Jeder Schnappschuss hat eine eindeute SHA1 ID. Darunter werden die Änderungen gegenüber dem vorherigen Schnappschuss angezeigt.
Der Beschreibungstext sollte möglichst die zugrunde liegende Auftrags- oder Ticket-Nummer enthalten, damit man später nachvollziehen kann, warum etwas geändert wurde und welche Anforderungen zugrunde lagen.
gitk index.html
Im rechten Bereich von gitk kannst du mit der rechten Maustaste auf einen Dateinamen klicken und dann auf "Externes Diff Programm". Die folgende Ansicht variiert je nach Betriebssystem und welches Diff-Programm installiert wurde. In meinem Fall ist es das Programm "meld":
Alternativ zur grafischen Oberfläche kannst du Unterschiede zwischen zwei Schnappschüssen mit dem Befehl git diff anzeigen:
git diff 0e58e570a6360dee00ab330e95601731283014c3 150ae5f28260f38f24a3c6ed50d3dd3fd14ed6d8oder abgekürzt:
git diff 0e58e5 150ae5
git checkout 06aa26ce4cdb1ae19e86bbb245ff7a96e47d5261Dabei werden alle Arbeitsdateien auf die angegebene Version gebracht. Die Datei style.css ist nun verschwunden und du siehst wieder die alte index.html:
<html> <head> </head> <body> Hello World! </body> </html>
Wenn du jetzt gitk startest, siehst du nicht mehr alle Schnappschüsse. Gitk zeigt standardmäßig nämlich nur die Historie bis zum aktuellen Schnappschuss an. Benutze den Parameter --all um alle Schnappschüsse zu sehen:
gitk --all
Der gelbe Punkt kennzeichnet, auf welchem Schnappschuss du dich gerade befindest.
Um zur neuesten Version zu wechseln, kannst du anstelle der ID auch den grün hinterlegten Namen des Zweiges angeben. Der erste Zweig heisst immer "master" und wird automatisch angelegt. Also:
git checkout master gitk --all
Bevor du Markierungen hinzufügst, wechsle immer zum neuesten Schnappschuss des jeweiligen Zweiges, ansonsten werden die Markierungen später nicht korrekt angezeigt!
Lass uns die aktuelle Version als "1.0.0" kennzeichnen:
git checkout master git tag "1.0.0"
Wir wollen außerdem die allererste Version als "0.9.0" kennzeichnen. Diese können wir anhand ihrer ID bestimmen:
git tag "0.9.0" 06aa26ce4cdb1ae19e86bbb245ff7a96e47d5261 gitk --all
Wir haben jetzt zwei der drei Schnappschüsse mit einer Versionsmarkierung versehen. Man kann Markierungen nachträglich verschieben. Um dies zu probieren, ändern wir HTML Datei:
<html> <head> <link rel="stylesheet" type="text/css" href="style.css"> <meta name="author" content="Stefan Frings"> </head> <body> Hallo schöne neue Welt! </body> </html>
Nun erstellen wir davon einen neuen Schnappschuss und versetzen die Markierung hierhin:
git commit -a -m "Autor hinzugefügt" git tag -f "1.0.0" gitk --all
Der Paramter -f (force) erlaubt uns, die bestehende Markierung durch eine neue zu ersetzen.
Um dies auszuprobieren, führen wir zwei fiktive Änderungsaufträge ein:
Wir starten einen Zweig für den ersten Änderungsauftrag:
git checkout -b CR1001
Nun fügen wir das angeforderte Formular in die Datei index.html ein:
<html> <head> <link rel="stylesheet" type="text/css" href="style.css"> <meta name="author" content="Stefan Frings"> </head> <body> Hallo schöne neue Welt! <form> Ihr Name: <input type="text"> </form> </body> </html>
Davon machen wir einen Schnappschuss:
git commit -a -m "CR1001: Formular hinzugefügt"
Nun fällt uns auf, dass zwischen der Begrüßung und dem Formular etwas mehr Abstand sein sollte. Also korrigieren wir das in der HTML Datei und erstellen einen weiteren Schnappschuss:
<html> <head> <link rel="stylesheet" type="text/css" href="style.css"> <meta name="author" content="Stefan Frings"> </head> <body> Hallo schöne neue Welt! <p> <form> Ihr Name: <input type="text"> </form> </body> </html>
git commit -a -m "CR1001: Mehr Abstand zum Begrüßungstext" gitk --all
Wir sehen hier in grün, dass nach der Version 1.0.0 ein Zweig mit dem Namen "CR1001" angelegt wurde. Dieser Zweig enthält zwei Schnappschüsse. Etwas weiter unten sehen wir eine Markierung am ursprünglichen Haupt-Zweig mit dem automatisch vorgegebenen Namen "master".
Nun schlüpfen wir in die Rolle eines Kollegen, der den zweiten Änderungsauftrag bearbeiten soll. Dieser beginnt ebenfalls mit dem Master Zweig für seine Arbeit, deswegen wechseln wir zuerst dorthin bevor wir den neuen Zweig erstellen:
git checkout master git checkout -b CR1002
<html> <head> <link rel="stylesheet" type="text/css" href="style.css"> <meta name="author" content="Stefan Frings"> <meta name="language" content="de"> </head> <body> Hallo schöne neue Welt! </body> </html>
Davon erstellen wir einen Schnappschuss und schauen uns das Ergebnis in gitk an:
git commit -a -m "CR1002: Language Header hinzufügen"gitk --all
Du siehst hier, dass die Entwicklung nach der Version 1.0.0 in zwei unabhängige Zweige mit den Namen CR1001 und CR1002 aufgeteilt wurde.
git checkout master
Dann übertragen wir die Änderungen vom Zweig CR1001 in den aktuellen (master) Zweig:
git merge CR1001
Außerdem übertragen wir auch die Änderungen vom Zweig CR1002 in den aktuellen (master) Zweig:
git merge CR1002 gitk --all
Die Änderungen der beiden Zweige wurden in den Haupt-Zweig übertragen, also dort zusammen geführt. Kontrolliere die Datei index.html:
<html> <head> <link rel="stylesheet" type="text/css" href="style.css"> <meta name="author" content="Stefan Frings"> <meta name="language" content="de"> </head> <body> Hallo schöne neue Welt! <p> <form> Ihr Name: <input type="text"> </form> </body> </html>
Wir können diesen Stand nun als Version 1.1.0 markieren:
git tag "1.1.0" gitk --all
Die beiden Zweige für CR1001 und CR1002 sind sehr klein. In echten Projekten dauert die Entwicklung von neuen Funktionen oft mehrere Tage bis Wochen, so dass viel mehr Schnappschüsse erstellt werden. Dort macht das Abzweigen mehr Sinn, als in diesem kurzen Beispiel.
Angenommen in Version 1.1.0 soll eine "DOCTYPE" Zeile hinzugefügt werden und es stellt sich heraus, dass diese Änderung auch in der alten Version 0.9.0 benötigt wird. Dann ist die Vorgehensweise folgendermaßen:
Ändere zuerst die Datei index.html im master Zweig (da ist Version 1.1.0):
git checkout master
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <link rel="stylesheet" type="text/css" href="style.css"> <meta name="author" content="Stefan Frings"> <meta name="language" content="de"> </head> <body> Hallo schöne neue Welt! <p> <form> Ihr Name: <input type="text"> </form> </body> </html>
git commit -a -m "DOCTYPE Zeile hinzugefügt" git tag "1.1.1" gitk --all
Nun wechseln wir zur Version 0.9.0 und erstellen einen neuen Branch speziell für Fehlerkorrekturen der 0.9.x Versionen:
git checkout "0.9.0" git checkout -b "0.9.x_Hotfixes"Dann cherry-picken wir die vorherige Änderung dorthin, und markieren das dann als Version 0.9.1:
git cherry-pick db2150636fe51fe451d80da107f99d6dc3a13500 git tag "0.9.1" gitk --allAll diese Schritte sind anschließend in gitk sichtbar:
Kontrolliere das Ergebnis in der Datei index.html:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <link rel="stylesheet" type="text/css" href="style.css"> <meta name="author" content="Stefan Frings"> <meta name="language" content="de"> </head> <body> Hello World! </body> </html>
Du befindest dich noch im Zweig "0.9.x_Hotfixes". Lass uns dort eine weitere Änderung in der Datei index.html vornehmen:
<DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta name="language" content="en"> <meta http-equiv="content-type" content="text/html; charset=utf-8"> </head> <body> Hello World! </body> </html>
Erstelle einen Schnappschuss:
git commit -a -m "Language header und Content-Type hinzugefügt" gitk --all
Nun wechsele zum Haupt-Zweig (master) und übertrage diese Änderung auch dorthin:
git checkout master git cherry-pick b2ad18210aa184d4c0cdf5667c0bdaa0ef9d3ad3Es erscheint folgende Meldung:
Es ist ein Konflikt aufgetreten. Git hatte ein Problem, die Änderung automatisch zu übertragen. Du sollst jetzt die Datei index.html in einem Editor öffnen und das Problem dort manuell beheben.
<DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <<<<<<< HEAD <link rel="stylesheet" type="text/css" href="style.css"> <meta name="author" content="Stefan Frings"> <meta name="language" content="de"> ======= <meta name="language" content="en"> <meta http-equiv="content-type" content="text/html; charset=utf-8"> >>>>>>> b2ad182... Language header und Content-Type hinzugefügt </head> <body> Hallo schöne neue Welt! <p> <form> Ihr Name: <input type="text"> </form> </body> </html>
Zwischen den auffälligen Markierungen befindet sich oben die alte Variante vom Haupt-Zweig und darunter die neue Variante die dort eigentlich eingefügt werden sollte. Du kannst sehen, dass bei den beiden Language-Headern unterschiedliche Sprachen ("de" versus "en") angegeben waren. Das ist der Grund für den Abbruch.
Korrigiere die Datei und entferne die Markierungen:
<DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <link rel="stylesheet" type="text/css" href="style.css"> <meta name="author" content="Stefan Frings"> <meta name="language" content="de"> <meta http-equiv="content-type" content="text/html; charset=utf-8"> </head> <body> Hallo schöne neue Welt! <p> <form> Ihr Name: <input type="text"> </form> </body> </html>
Nun kannst du die Zusammenführung mit folgenden Befehlen fortsetzen:
git commit -a -m "Neuer Content-Type Header, gemerged vom 0.9.x Hotfix Zweig" gitk --allDas Ergebnis sieht in gitk so aus:
Es gibt grafische "merge Editoren", mit denen man solche Änderungen komfortabler vornehmen kann. Unter Linux benutze ich dazu gerne das Programm meld, indem ich im Projektverzeichnis eingebe:
meld .
Das Programm zeigt links die Datei aus dem "entfernten" (im Sinne von "dem anderen") 0.9.x Hotfix Zweig und rechts die Arbeitsdatei wie sie vor dem cherry-pick (oder merge) ausgesehen hatte. In der Mitte sollst du nun hin schreiben, wie das Ergebnis der Zusammenführung aussehen soll. Die rot markierten Zeilen sind diejenigen, die zum Konflikt führten.
Du kannst nun auf die Pfeile klicken, oder die Zwischenablage benutzen, oder ganz manuell tippen. In diesem konkreten Fall ist es am einfachsten, zuerst die Header von der rechten Seite zu übernehmen (auf den Pfeil klicken), und dann den neuen Content-Type Header von der linken Seite über die Zwischenablage in die Mitte zu kopieren. Dann hast du am Ende von beiden Seiten das Beste zusammen kopiert. Also so:
Für Windows empfehle ich dazu das Programm WinMerge.
Der tägliche Arbeitsablauf im Team könnte dann so aussehen:
Die Verbindung zum Server läuft üblicherweise über ein gemeinsames Dateisystem/Netzlaufwerk oder SSH.
Ich zeige das zuerst am Beispiel eines gemeinsamen Netzlaufwerkes. Auf dem Server ist abgesehen von der Dateifreigabe keine weitere Installation oder Konfiguration notwendig. In den folgenden Beispielen ist "/repository" das gemeinsame Netzlaufwerk auf das alle Entwickler Zugriff haben.
cd /repository git init --bare MeinProjekt.gitDabei wird ein Verzeichnis mit dem Namen "/repository/MeinProjekt.git" angelegt, worin Git die gleichen Verwaltung-Dateien ablegt, wie lokal im versteckten ".git" Verzeichnis.
Git unterscheidet zwischen normalen Projekt-Repositories für den Arbeitsplatz, und "bare" Repositories für den Server. Der Unterschied ist, dass bare Repositories keine Arbeitsdateien (also die eigentlichen Quelltexte, die man editiert) enthalten. Dennoch können beide Varianten zum pushen und pullen verwendet werden. Entwickler haben damit prinzipiell die Möglichkeit, Schnappschüsse am Server vorbei zu teilen. Außerdem kann man Schnappschüsse von einem Server zu einem anderen übertragen.
cd MeinProjekt git remote add origin /repository/MeinProjekt.git git push --all --set-upstream originIn diesem Fall ist "origin" ein symbolischer Name für die Verbindung zum Netzlaufwerk "/repository/MeinProjekt.git". Der Parameter --all sorgt dafür, dass alle Zweige bereit gestellt werden. Ansonsten würde Git nur den aktuellen Zweig senden.
Die anderen Team-Mitglieder müssen das Projekt anschließend vom Server klonen, um daran teilzunehmen.
cd Projekte git clone /repository/MeinProjekt.git cd MeinProjekt
Das funktioniert auch mit leeren Projekten. Anschließend kannst du die Namen der verfügbaren Zweige auflisten:
git branch --all
Der Parameter --all sorgt dafür, dass alle Zweige angezeigt werden. Ohne diesen Parameter würdest du nur die lokalen Zweige in deinem Projektverzeichnis sehen. Danach könntest du beispielsweise mit
git checkout NameDesBrancheszu dem gewünschten Zweig wechseln.
git push
Der git push Befehl sendet normalerweise nur den aktuellen Zweig. Du kannst den Parameter --all verwenden, um alle Zweige zu senden.
git pull
Der git pull Befehl holt normalerweise nur den aktuellen Zweig. Du kannst den Parameter --all verwenden, um alle Zweige zu holen.
Eventuell tritt dabei ein Konflikt auf, nämlich wenn jemand anderes zwischenzeitlich die gleiche Datei geändert hat, wir du. In diesem Fall zeigt Git eine Hilfe an, wie du den Konflikt lösen kann. In der Regel hat man nun die Wahl zwischen dem zusätzlichen Parameter --rebase oder --merge:
git push --tagsAnstelle von "--tags" kann man auch einzelne Markierungen senden:
git push "1.0.0"
git fetch --all --tags
git clone deinName@serverName:/repository/MeinProjekt.gitoder
git clone ssh://deinName@serverName/repository/MeinProjekt.git
Du kannst in deinem Projektverzeichnis eine Datei namens .gitignore anlegen, um Dateien/Verzeichnisse aufzulisten, die Git ignorieren soll. Das folgende Beispiel schließt alle "logs" Verzeichnisse und Backup-Dateien in beliebigen Verzeichnissen aus.
**/logs *.bak
Versehentlich gelöschte Dateien/Verzeichnisse kannst du sofort wieder herstellen, indem du eingibst:
git checkout dateiname
Mit dem gleichen Befehl kannst du auch Änderungen an Dateien verwerfen. Die Datei wird dabei wieder in den Zustand des letzten Schnappschusses zurück versetzt.
Um den letzten noch nicht gepushten Schnappschuss (commit) zu löschen, gibst du ein:
git reset --hard HEAD^
Dabei werden alle Dateien in den Zustand des vorletzten Schnappschusses zurück versetzt.
Um den letzten noch nicht gepushten Schnappschuss zu erweitern oder seinen Kommentar zu ändern:
git commit --amend
Man soll Schnappschüsse auf dem Server niemals verändern oder gar löschen, denn das kann üble Probleme auslösen. Versuche nicht, peinliche Fehlversuche vom Server zu entfernen! Erstelle stattdessen einen ganz normalen weiteren Schnappschuss, der den Fehler korrigiert.
Für weitere Informationen zu den Befehlen siehe die Seiten Git Book und Git Reference.
GitHub ist ein ähnlicher öffentlicher Dienst von Microsoft. Die meisten Open-Source Projekte sind hier zu finden.