GIT Versionsverwaltung

Was ist GIT?

Git ist eine Sammlung von Werkzeugen zur Versionsverwaltung von Dateien.
Im Laufe der Arbeit an Dateien (z.B. Programm-Code) kann damit jeder Entwicklungsstand eines Projektes festgehalten werden.

  • Es kann (fast) nichts verloren gehen – Snapshot, History
  • Es können nebeneinander verschiedene Versionen eines Projekts gepflegt werden (man möchte bspw. etwas ausprobieren, was später gegebenenfalls in das Projekt aufgenommen werden soll, wenn es erfolgreich getestet wurde) – Branch
  • Jeder Stand der Entwicklung ist referenzierbar und wieder herstellbar – History
  • Dezentrale Repositories – Clones

  • Dokumentation

Wozu sollte ich GIT benutzen?

GIT kann die Koordination von Team-Arbeit an gemeinsamen Projekten unterstützen.

GIT bietet aber auch praktische Möglichkeiten, wenn man es alleine nutzt.

  • Hilft, Übersicht zu behalten – z.B. kein manuelles Abspeichern unter anderem Namen/Datum nötig
  • Werkzeug zur Dokumentation – mit gehaltvollen commit messages kann ich die Entstehung meines Projektes sehr gut dokumentieren; alle wichtigen Änderungen können beschrieben und begründet werden; Fehler und “Sackgassen” können dokumentiert werden.
  • Unterstützt Kreativität – das Branchen ermöglicht es, jederzeit ohne Risiko Änderungen vorzunehmen und zu testen, ohne den “Haupt-” Entwicklungszweig zu beeinflussen; da das Branchen in git sehr einfach ist, gewöhnt man sich schnell an, Branches zu nutzen, um “mal eben schnell was zu testen”; bei der Nutzung von Ticket-Systemen können Branches die Ticket-Struktur widerspiegeln.

Wofür eignet sich git nicht so gut

Prinzipiell kann mit git jegliches Dateiformat verwaltet werden, allerdings funktionieren diff, merge etc. am besten mit rein textbasierten Dateiformaten (also Programm-Quelltext, HTML, LaTEX, usw.).
Binäre Datenformate (Bilder, Audio, Video, komprimierte Textformate (doc)) lassen sich auch mit git verwalten, allerdings kann bei ihnen bspw. ein Merge-Konflikt nur gelöst werden, indem sich für eine der beiden Versionen entschieden wird, ein Verschmelzen beider Dateien ist nich ohne weiteres möglich.

Was unterscheidet GIT von anderen VCS?

  • Git ist Snapshotbasiert, CVS/SVN ist Änderungs- (Patch/Diff)- basiert
  • Dezentral

Begriffe / Konzepte hinter GIT

Repository

Ein Repository ist der “Behälter”, in dem das Projekt mitsamt allen seinen Daten, Meta-Informationen und Versionen enthalten ist.

Man unterscheidet zwischen non bare Repositories – sie enthalten einen Arbeitsbereich (also die Dateien an denen gearbeitet werden kann) und bare Repositories – sie enthalten die kompletten Daten und Infos eines Projektes, aber keinen Arbeitsbereich; sie sind sozusagen als Server-Repositories gedacht; in der Regel kann nur in bare Repos gepusht werden (s.u.).

Arbeitsbereich (Clone)

Während in einigen anderen SCMs zwischen Arbeitsbereich und Repository unterschieden wird, verschwimmen in Git die Grenzen ein wenig. Ein Clone eines Repositories enthält immer den kompletten Satz an Informationen über das Projekt. Ein Clone kann ein “Arbeitsbereich” sein oder aber auch als Repository dienen.

Der Arbeitsbereich ist der Ordner, in dem sich mein Clone oder mein Projekt befindet. Hier habe ich alle Dateien, die zum Projekt gehören und kann Änderungen vornehmen.
Ein Clone ist ein Projekt, das von einem remote Repository ‘geholt’ wurde; sämtliche Informationen über Versionen, Branches etc. sind darin enthalten – ich verfüge sozusagen über das komplette Projekt mit allen Versionen aller Dateien, die es je gegebn hat.

(Fetch/Pull – Arbeitsbereich updaten)

Bei der Team-Arbeit an einem Projekt gibt es meist einen oder mehrere Server, auf denen die (Haupt-) Repos liegen, sogenannte ‘remotes’. Zum arbeiten clone ich das remote Repo, arbeite daran.
Wenn andere Team-Mitglieder auch an dem Projekt arbeiten, entstehen dadurch Änderungen, die mein Clone noch nicht kennt; diese muss ich vom remote anfordern.
git fetch holt die Informationen über alle seit dem letzten fetch geschehenen Änderungen.
git pull merget mir die so mitgeteilten Änderungen in einen bestimmten Branch

Staging Area (Änderungen für Commit vormerken)

Für Neulinge meist etwas verwirrend ist bei git das Konzept der Staging Area. Wenn ich in meinem Arbeitsbereich Änderungen vorgenommen habe, werden diese normalerweise committed, i.e. die Änderungen werden in das Respository übernommen.
In git gibt es jedoch zuvor eine Zwischenstufe, die Staging Area; ein commit berücksichtigt (im Normalfall) nur alle Änderungen, die sich in der Staging Area (auch Index) befinden.
Meine Änderungen an Dateien in meinem Projekt landen nicht automatisch in der Staging Area, ich muss sie explizit mit einem ‘add’ dorthin befördern.
Obwohl das auf den ersten Blick etwas umständlich erscheint, ist es aber ein mächtiges Werkzeug:

  • Es erlaubt die History – die Entwicklungsgeschichte des Projektes – gezielt zu strukturieren und zu dokumentieren (z.B. Ticket-basiert, Feature-basiert, Bugfix-basiert)
  • Man kann Dateien mit “privaten” Notizen versehen (oder bspw. Debug-Ausgaben), ohne diese zu commiten (es lässt sich sogar pro Datei zeilenweise auswählen, was gestaget werden soll).
  • Man kann auch mittels git commit -a den ganzen Staging Kram überspringen 😉

Illustration: Zustände von Dateien

Commit (Änderungen lokal übernehmen)

Mit einem git commit übernehme ich die gestageten Änderungen in das lokale Repository.

Push (Änderungen in ein remote Repo übertragen)

Wenn ich mit einem geclonten Repo arbeite und meine Änderungen auch den remote Repositories (ich kann beliebig viele remote Repositories verwalten) mitteilen möchte, tue ich das mit einem git push.

Branch – Änderungszweig erstellen; Neues ausprobieren, altes bleibt bestehen.

Ein gängiges Verfahren ist, zu Beginn seiner Arbeit einen neuen Zweig, einen Arbeits-Branch zu erstellen, so bleibt mein ursprünglicher Branch erhalten und ich kann jederzeit zum Vergleich hin- und her-schalten.
Praktisch ist es auch, während der Arbeit ggf. weitere Branches erstellen, um verschiedene Versionen einer Änderung anzuschauen – auch wieder ohne Einflussnahme auf den ursprünglichen Branch.
Neu angelegte Branches sind zunächst “privat” – also nur im lokalen Repo/Clone – es sei denn, sie werden explizit in ein remote Repo gepusht.

Merge – Änderungszweige zusammenführen

Sind alle Änderungen zur Zufriedenheit vorgenommen, stage und commite ich meine Arbeit (noch im Arbeitsbranch). Daraufhin kann ich meinen Arbeitsbranch in den ursprünglichen Branch mergen – d.h. Arbeits- und ursprünglicher Branch werden zusammengeführt.
Sind keine weiteren parallelen Änderungen geschehen, genügt ein Fast Forward Merge – dabei wird nur der HEAD-Zeiger weitergerückt.
Gab es auch parallele Änderungen, wird ein 3-Wege-Merge durchgeführt (basierend auf den jeweiligen Änderungen seit dem letzen gemeinsamen Vorfahren) – was evtl. zu Merge-Konflikten führen kann, wenn bspw. zwei unterschiedliche Änderungen an ein und dem selben Abschnitt einer Datei vorgenommen wurden, was dann manuell aufgelöst werden muss.

Wie fange ich an?

RTFM

Hilfe

git help [<Kommando>]

Ein neues Repository erzeugen

git init <verzeichnis>

Initialisiert ein neues Repo in , i.e. legt den Ordner /.git an, in dem die Versionsdatenbank etc. liegt.
Das Verzeichnis muss nicht leer sein, so kann ein bestehendes Projekt leicht in die git Versionsverwaltung eingebunden werden.
Evtl. vorhandene Dateien (auch das Verzeichnis selbst) sind noch nicht gestaget! Am besten wird nach dem Initialisieren eine erste Version erzeugt:
cd <verzeichnis>
git add .
git commit -m 'Initial commit'

Git Buch – Ein Git-Repository anlegen

Ein Repository clonen

Will man stattdessen ein bereits existierendes Repo benutzen, …

git clone https://github.com/AliTe/experimini_20151021_git.git

Erzeugt einen lokalen Clone des entsprechenden Repos auf GitHub.

Git Buch – Eein existisrendes Repository klonen

Änderungen vornehmen

Nachdem alle Änderungen vorgenommen wurden, …

git add <datei>

oder

git add -A

um alles zu stagen.

Danach dann mit

git commit

alle gestageten Änderungen in Repo übernehmen. Ein commit öffnet in der Regel den Editor zum Verfassen der commit-Message; eine leere Meldung bricht den commit ab.
Es bewährt sich in der commit message in der ersten Zeile eine kurze Zusammenfassung der vorgenommenen Änderungen zu schreiben, dann eine Leerzeile und dann ggf. etwas ausführlicheren Text. Viele Tools zeigen in Übersichts-Ansichten dann nur die erste Zeile an und gewähren so einen kompakten Überblick.

Git Buch – Änderungen am Repository nachverfolgen

Einen Branch anlegen

Um aktuell die Arbeit in einem neuen Branch zu beginnen bzw. fortzusetzen, nutzt man
git checkout -b <neuer Branchname>

Dies erzeugt einen neuen Branch und wechselt sofort in diesen.
Einen Branch erzeugen, ohnen ihn auch gleich auszuchecken geht mit:
git branch <neuer Branchname>

Git Buch – Git Branching

Arbeitsbereich updaten: Fetch und Pull

Vor der Arbeit an einem Projekt, an dem auch andere Team-Mitglieder arbeiten, empfiehlt es sich, Informationen über von anderen vorgenommen Änderungen zu erneuern:
git fetch [<remote>|--all]

Bevor man die Arbeit an einem bestimmten Branch fortsetzt, holt man sich die jew. aktuellen Änderungen mit
git pull

Git Buch – Mit externen Repositorys arbeiten

Die History betrachten

Mit
git log

kann man sich die History – das Log über die bisher vorgenommenen Änderungen – betrachten.
Mit
git status

kann man sich Informationen über den aktuellen Zustand des Arbeitsbereichs anzeigen lassen.

Git Buch – Änderungen am Repository nachverfolgen

Mergen

Habe ich die Änderungen in einem Arbeitsbranch abgeschlossen (i.e. gestaget und commitet), wechsle ich zurück in meinen Ausgangsbranch und merge die Änderungen meines Arbeitsbranches:
git checkout <Ausgangsbranch>
git merge <Arbeitsbranch>
git branch -d <Arbeitsbranch>

Letzter Befehl löscht meinen Arbeitsbranch wieder.

Gibt es keine parallelen Änderungen, findet ein Fast Forward Merge statt – d.h. nur der “HEAD-Zeiger” wird aktualisiert.

Haben parallel Änderungen stattgefunden, wird ein 3-Wege-Merge angewendet, bei dem die Änderungen seit dem letzten gemeinsamen Vorfahren verschmolzen werden. Dies kann u.U. zu Konflikten führen, die manuell aufgelöst werden müssen.

Änderungen in ein Remote Repository pushen

Um meine Änderungen in ein Remote zu übertragen, wird gepusht:
git push <remote> <Branchname>

Git Buch – Änderungen in ein Remote Repository hochladen

Taggen

Jeder Snapshot eines Repos wird mit einer (md5) Hashsumme versehen und ist auf diese Weise referenzierbar.
Möchte man einen Snapshot explizit markieren (z.B. ein Milestone des Projektes, eine Release-Nummer o.ä.) kann man dazu einen Tag setzen:
git tag <tagname>

Tags können auch auf ein Remote gepusht werden. Zudem ist es möglich ein Tag mit seinem PGP/GPG Key zu signieren.

Git Buch – Tags

Praktische Tools und Tipps

  • .gitignore / .gitkeep
  • .gitconfig lokal/global
  • Submodules / Subtrees

Weiterführende Techniken / Konzepte

  • Gitflow
  • GitHub/GitLab/Bitbucket