Geschrieben von

git

WebDev

In diesem Beitrag geht es um git. Ziel ist es eine Einführung für Anfänger und SEO-Techniker zu geben. Inhalte sind:

Was ist git?

Bei git handelt es sich um ein dezentrales Versionsverwaltungsprogramm. Wenn man zusammen im Team Softwares oder Websites entwickelt, die aus vielen verschiedenen Dateien bestehen, kann mit git jede einzelne Änderung dokumentiert und nachvollzogen werden.

Dabei wird git über das Terminal gesteuert. Hierzu steht das Kommando git zur Verfügung. Mit verschiedenen Optionen lassen sich so Projekte bzw. Dateien herunterladen, verändern und wieder an der gleichen Stelle hochladen.

git vs. GitHub vs. GitLab

Wer in git einsteigt wird schnell auch mit GitHub und GitLab konfrontiert. Hier eine kurze Übersicht, um die Unterschiede zu verstehen:

  • git: Hierbei handelt es sich um das Versionsverwaltungsprogramm. Gleichzeitig ist damit das Kommando git gemeint, welches über das Terminal gesteuert wird.
  • GitHub: Hierbei handelt es sich um git mit einer grafischen Benutzeroberfläche. Gemeint ist die Website github.com. GitHub stellt gegenüber git noch viele weitere Zusatzfunktionen bereit.
  • GitLab: Bei GitLab handelt es sich ebenfalls um git mit einer grafischen Benutzeroberfläche. Es ist auch eine Alternative zu GitHub, jedoch mit einem großen Unterschied. GitLab ist Opern Source und kann damit auf einen eigenen Server installiert werden.

Daneben gibt es noch ein paar weitere Anbieter wie z.B.:

  • Azure Repos
  • Bitbucket
  • Gitea
  • Gitolite

Grundsätzlich muss man berücksichtigen, dass die vielen Git-Anbieter wie GitHub oder GitLab das Basisprogramm git nicht ersetzen, sondern lediglich als Ergänzung dienen. Was damit konkret gemeint ist, werden wir später sehen.

Grundlagen git

Um die Grundlagen zu verstehen, müssen auch die einzelnen git-spezifischen Begriffe verstanden werden:

  • Repository: Aus dem Englischen übersetzt bedeutet Repository “Depot” oder “Lager”. Bei git ist das Repository eine Sammlung aller Dateien und Zwischenstände eines Projekts.
  • Branch: Hierbei handelt es sich um einen Entwicklerzweig. Mit Zweigen kann man unabhängig an unterschiedlichen Versionen einer Software arbeiten.
  • Checkout: Damit meint man den Wechseln zwischen Zweigen.
  • Commit: Ein Commit ist ein Abspeichern eines Zwischenstands.
  • Stash: Mit Stash meint man einen Bereich in git, wo man unfertigen und nicht commitfähigen Code abspeichern kann.
  • Merge: Damit werden zwei Zweige miteinander verschmolzen.

git installieren

Um git – also das Kommando – zu nutzen gibt es grundsätzlich 2 Möglichkeiten:

  • Man nutzt eine IDE (Integrierte Entwicklungsumgebung) wie z.B. Visual Code Studio, die git schon vorinstalliert hat.
  • git direkt von https://git-scm.com/ herunterladen und installieren.

Bei letzterem hängt die Installation vom Betriebssystem ab. Unter Linux installiert man git mit dem Paketverwaltungswerkzeug. Mehr dazu gibt es hier. Unter macOS ist es am einfachsten, wenn man Xcode inkl. Command Line Tools installiert. Dort ist git mitenthalten. Unter Windows ladet man sich am besten die Setup-Dateien von git-scm.com herunter. Nach dem Installationsvorgang wird das git-Kommando, eine Terminal-Umgebung (git Bash) und noch eine Benutzeroberfläche installiert.

git Bash sieht dann folgendermaßen aus:

git bash

Mit git arbeiten

In der Regel benutzt man git zusammen mit einem externen Repository. Dadurch tauscht man Dateien zwischen dem lokalen Rechner und der externen Instanz – meist GitLab oder GitHub – aus. Daher werden wir hier ein GitHub-Repository erstellen, um die Arbeit mit git zu verdeutlichen. Dabei gibt es zwei Wege wie die Zusammenarbeit zwischen lokalen Repository und dem Remote Repository stattfinden kann:

  • Es existiert ein Projekt auf einem Remote Repository, welches lokal kopiert werden muss, um daran zu arbeiten.
  • Der umgekehrte Weg ist, dass das Projekt noch nicht Remote existiert. Dazu erstellt man ein Projekt lokal und lädt es erst dann Remote hoch.

Beide Wege werde ich aufzeigen. Beide Szenarien arbeiten sehr ähnlich. Nur bei der initialen Projekt-Erstellung muss etwas berücksichtigt werden, was wir sehen werden. In den nächsten Abschnitten werden wir daher folgende Schritte durchführen, die git näher bringen sollen.

  1. GitHub-Konto einrichten
  2. GitHub-Repository anlegen
  3. git config: Nutzer anlegen
  4. git clone, git init und git remote: Repository anlegen
  5. git add: Dateien hinzufügen
  6. git commit: Snapshot der aktuellen Änderungen erstellen
  7. git status: Status abfragen
  8. .gitignore: Dateien ausschließen
  9. git push: Dateien zum Remote Server
  10. git pull: Aktualisierung Repository (lokal)
  11. git checkout: Mit Branches arbeiten
  12. git stash: Unfertigen Code speichern
  13. git merge: Zweige zusammenführen

GitHub-Konto einrichten

Ein GitHub-Konto einzurichten, geht relativ einfach. Dazu geht man auf die GitHub-Website und startet den Sign-Up-Prozess. Neben Mail-Adresse müssen nur Benutzername und Passwort eingegeben werden.

GitHub-Repository anlegen

Im nächsten Schritt legen wir ein Repository an. Dabei handelt es sich um eine Sammlung aller Dateien eines Projekts. Beim Klick auf das Plus-Symbol oben rechts innerhalb der Benutzeroberfläche gibt es den Punkt “New repository”, wo man sein erstes Projekt anlegen kann. Danach bekommt man folgendes Formular-Feld zum Ausfüllen:

GitHub-Repository

Neben dem Namen und einer Beschreibung ist die Wahl zwischen “Private” und “Public” wichtig:

  • Private bedeutet, dass man nur selbst und eingeladene Nutzer am Projekt teilnehmen dürfen.
  • Public bedeutet, dass das Projekt öffentlich sichtbar ist.

Zudem hat man unten noch weitere Einstellungsmöglichkeiten:

  • Add a README file: Hiermit kann man eine Readme-Datei mit einer Projekt-Beschreibung hinzufügen.
  • Add .gitignore: Mit dieser Datei kann man festlegen, welche Dateien beim Upload vom lokalen Rechner zu GitHub nicht mit hochgeladen werden sollen (z.B. private Dateien).
  • Choose a license: Hiermit kann noch die Lizenz unter der man den Code zur Verfügung stellt definiert werden.

Damit wir zu Demonstrationszwecken zumindest eine Datei haben, mit der wir arbeiten können, setzen wir den Haken bei “Add a README file”. Den Rest können wir vorerst deaktiviert lassen. Mit Klick auf “Create repository” haben wir unser erstes GitHub-Projekt und somit externes Repository angelegt.

git config: Nutzer anlegen

Ab hier arbeiten wir mit dem git-Terminal. Bei mir ist es Windows und die git Bash. Im ersten Schritt legen wir uns als Nutzer an. Dies geschieht innerhalb des Terminals mit:

git config --global user.name "Demir Jasarevic"
git config --global user.email "demir@domain.com"

Im besten Fall entspricht die Mail-Adresse der GitHub-Mailadresse. Die gespeicherten Daten gelten nun als Default-Werte im lokalen Repository. Jedes Mal wenn ein Commit durchgeführt wird, werden diese Nutzer-Daten mitgeschickt.

git clone, git init und git remote: Repository anlegen

Bei den Repositorys unterscheidet man in:

  • Lokales Repository: Dies ist das Projekt, welches lokal am Rechner bearbeitet wird.
  • Remote Repository: Dies sind nicht lokale Repositorys, also z.B. GitHub oder GitLab.

Die Kommunikation dieser beiden erfolgt über HTTPS oder SSH. Mit git clone kann z.B. ein Remote Repository lokal kopiert werden. Mit git push können wiederum lokale Dateien ins Remote Repository geschoben werden. In der Praxis heißt das, dass jeder Entwickler bei sich lokal ein Repository mit git steuern kann. Alle Entwickler schieben aber ihre Projekt-Dateien, an denen sie zusammen arbeiten ins Remote Repository, damit jeder auf alles wieder Zugriff hat.

Vorhin haben wir ein Repository auf GitHub angelegt. Gehen wir davon aus es heißt “Repo-Test” und ist unter der URL https://github.com/benutzername/repo-test.git aufrufbar. Jetzt müssen wir das Remote Repository bei uns lokal kopieren. Um eine Kopie zu erzeugen wechseln wir zunächst in das gewünschte Verzeichnis (mit cd) und führen dann folgenden Befehl aus:

git clone https://github.com/benutzername/repo-test.git

Wenn wir über HTTPS kommunizieren, dann wäre das der notwendige Befehl. Kommunizieren wir jedoch auf die andere Art – SSH – dann wäre das Kommando wie folgt:

git clone git@github.com:benutzername/repo-test.git

Wenn es sich um ein privates Repository handelt, dann wird man aufgefordert das Passwort dafür einzugeben. git clone wird nun eine Kopie lokal ablegen. Dazu wird ein neues Verzeichnis mit dem gleichen Namen wie das Remote Repository angelegt. Dort werden dann alle Dateien hineinkopiert. Gleichzeitig wird im Verzeichnis eine Daten namens “.git/config” angelegt. In dieser Datei werden dann alle individuellen, lokalen Einstellungen gespeichert.

Existiert kein Remote Projekt, dann arbeiten wir zunächst lokal. Dazu müssen wir unser lokales Verzeichnis zunächst in ein git-Repository umwandeln. Das geschieht mit:

git init

Wichtig ist dabei, dass wir uns im richtigen Verzeichnis befinden. Um nun mit dem Remote Server kommunizieren zu können, müssen wir das mit folgender Anweisung einleiten:

git remote add origin https://github.com/benutzername/repo-test.git

Um zu testen, ob die Verbindung erfolgreich war, gibt man an:

git remote -v

Im Anschluss können wir nun das lokale Repository hochladen:

git push

Der aktuelle Stand aller Dateien wird innerhalb eines Repositorys als “Working Directory” bezeichnet. Man sagt dazu aber auch Workspace. Zudem existiert noch die Stage. Es gibt also verschiedene Instanzen innerhalb eines Repositorys.

Das heißt:

  • Wenn wir aktuell an Dateien arbeiten, dann befinden wir uns im Working Directory, oder auch Workspace genannt.
  • Wenn man Dateien verändert hat, dann möchte man diese dauerhaft ins Repository speichern. Bevor das geschieht werden die geänderten Dateien zunächst in die Staging-Area (oder nur Stage) geschoben (man nennt diesen Bereich auch Index oder Cache). Dieser Bereich umfasst alle Dateien, die beim nächsten Commit dauerhaft zwischengespeichert werden sollen. Nachdem man Änderungen durchgeführt hat, werden diese mit git add [dateiname] oder git stage [dateiname] in die Stage befördert. Nun kann an weiteren Dateien gearbeitet werden, bis man diese ebenfalls in die Stage schiebt.
  • Liegt nun eine Code-Version vor, die zwischengespeichert werden soll, wird ein Commit durchgeführt. Mit dem Commit landen nun die Dateien dauerhaft im Repository.

Visuell kann man sich das in etwa so vorstellen:

Mit git arbeiten

git add: Dateien hinzufügen

Jetzt haben wir unser GitHub-Repository lokal abgespeichert oder ein lokales Projekt auf dem Remote Server übertragen. Wir können nun bei uns lokal weitere Dateien erstellen. Das können wir z.B. mit dem Befehl:

touch main.js

Dadurch ist eine JavaScript-Datei in unserem lokalen Projektverzeichnis angelegt. Das genügt git jedoch nicht. Wir müssen die Datei noch explizit mit folgendem Befehl zum Repository hinzufügen:

git add main.js

Erst damit ist die Datei für den nächsten Schritt im Workflow bereit. Hinweis: Statt git add lässt sich auch git stage verwenden.

git commit: Snapshot der aktuellen Änderungen erstellen

Haben wir nun lokal eine Programm-Erweiterung erfolgreich getestet, dann können wir den Zwischenstand mit dem Commit-Befehl im Repository speichern:

git commit -m "Feature 1.2 fertig"

Hinter “git commit” sehen wir noch -m “text”. Das “m” steht für Message, also Nachricht. Damit geben wir dem Commit eine kurze Nachricht mit, damit nachvollziehbar ist, was bei diesem Commit konkret gemacht worden ist. Wenn jetzt nun weitere Dateien hinzukommen, dann muss alles von vorne gemacht werden, um einen Commit durchführen zu können:

touch style.css
git add style.css
git commit -m "2 neue Style"

Nicht nur bei neuen Dateien muss dieser Prozess durchlaufen werden. Auch wenn sich eine bestehende Datei ändert – also wenn wir Aktualisierungen oder Änderungen vornehmen – müssen wir die Datei zu nächst hinzufügen. Nehmen wir an, wir haben die Datei main.js um weitere Code-Zeilen ergänzt. Dann müssen wir folgenden Befehl wieder machen:

git add main.js
git commit -m "10 neue Code-Zeilen"

Erst dann werden die Snapshots tatsächlich im Repository abgespeichert. Um nicht jedes Mal alle aktualisierten und geänderten Dateien mit git add oder git stage hinzufügen zu müssen, gibt es beim Commit-Befehl noch den Zusatzbefehl -a. Haben wir also bei den zwei existierenden Dateien style.css und main.js Änderungen durchgeführt können wir folgendes machen:

git commit -a -m "Neuer Button inkl. Styles"

Damit werden alle Dateien, die sich seit dem letzten Commit geändert haben, im Repository als Zwischenstand gespeichert. Man muss dabei nur berücksichtigen, dass neu angelegte Dateien davon ausgeschlossen sind. Diese müssen weiterhin mit git add neu hinzugefügt werden. Zudem muss man im Hinterkopf behalten, dass git commit nur lokal funktioniert. Wenn man Dateien zu einem Remote Repository hinzufügen möchte, muss man mit git push arbeiten (dazu später mehr).

Aus Sicht der Repository passiert bei den Befehlen git add und git commit folgendes:
Mit dem Commit werden alle Änderungen, die mit git add [dateiname] oder git stage [dateiname] in die Stage gelegt worden sind, für immer im Repository des jeweiligen Zweigs gespeichert. Der letzte bzw. aktuellste Commit innerhalb von Zweigen wird auch Head genannt. Es gibt aber auch HEAD (also in Großbuchstaben). Dies ist der aktuelle Commit im aktuell aktiven Zweig.

Gehen wir davon aus, dass wir uns in einem Ordner befinden, wo wir ein neues Repository anlegen möchten. Dazu legen wir uns zunächst einen neuen Ordner an:

mkdir git-test

Dann wechseln wir in den soeben erstellten Ordner:

cd git-test

Danach machen wir aus diesem Ordner ein git-Repository:

git init

Danach erstellen wir unsere erste Datei:

echo "Hallo" > index.html

Dann erstellen wir noch eine Datei:

echo "Kontaktseite" kontakt.html

Betrachten wir nun die verschiedenen Instanzen (siehe unter “Repository”), dann hätten wir nun im Workspace 2 Dateien. Stage und Repository wären noch leer.

Mit git arbeiten

Versuchen wir nun mit git commit die Änderungen direkt ins Repository zu schieben, dann würde git uns hier eine Fehlermeldung ausgeben.

Mit git arbeiten

Deshalb müssen wir zunächst folgende Anweisung durchführen:

git add index.html kontakt.html

Mit dieser Anweisung schieben wir den Status Quo des Workspaces in den Stage-Bereich. Dadurch würden wir folgendes erreichen:

Mit git arbeiten

Erst jetzt kann der Commit durchgeführt werden:

git commit -m "Erster Commit"

Dadurch sehen die Instanzen wie folgt aus:

Mit git arbeiten

Wenn ich jetzt innerhalb meiner kontakt.html eine weitere Zeile hinzufüge und sie direkt in den Stage schiebe:

echo "Adresse" >> kontakt.html
git add kontakt.html

Dann hätte ich folgendes Bild:

Mit git arbeiten

Und wenn ich die kontakt.html im Workspace lösche:

rm kontakt.html

Dann wäre die Gesamtsituation wie folgt:

Mit git arbeiten

Wenn wir jetzt davon ausgehen, dass wir eine Datei haben, die auf allen 3 Instanzen unterschiedliche Inhalte und Codes aufweist, dann haben wir die Möglichkeit die unterschiedlichen Versionen auch direkt anzusehen. Status wäre z.B.:

Mit git arbeiten

Mit folgenden Befehlen lassen sich die verschiedenen Zustände auslesen:

cat index.html // Zeigt den Zustand im Workspace
git show :index.html // Zeigt den Zustand im Stage
git show HEAD:index.html // Zeigt den Zustand beim letzten Commit

git status: Status abfragen

Arbeitet man an vielen Dateien gleichzeitig, dann kann man schnell den Überblick verlieren. Man weiß dann beim Commit nicht mehr, was sich alles geändert hat. Um aber einen Stand abzufragen gibt es glücklicherweise:

git status

.gitignore: Dateien ausschließen

Was wenn es im lokalen Repository Dateien gibt, die man bei Commits und Push-Befehlen nicht berücksichtigen möchte? Dazu gibt es die Datei .gitignore, mit der man Dateien definieren kann, die nicht getrackt werden sollten. Wie die Auszeichnung auszusehen hat und weitere Informationen zu .gitignore gibt es in der git-Dokumentation.

git push: Dateien zum Remote Server

Bis jetzt haben wir Dateien erstellt, ins lokale Repository gelegt und mit Commits Zwischenstände abgespeichert. Möchte man nun die Commits aus dem lokalen Repository zu einem Remote Repository übertragen (z.B. GitHub oder GitLab) muss das mit folgendem Befehl durchgeführt werden:

git push

Man kann aber auch den Zweig mitgeben zu welchen die Dateien übertragen werden sollen:

git push origin master

Im besten Fall funktioniert git push ohne Probleme. Dies ist aber auch nur dann der Fall, wenn sich seit dem Klonen des Projekts vom Remote Repository sonst nichts innerhalb des Projekts geändert hat. In der Praxis arbeiten viele Entwickler gleichzeitig, sodass es definitiv zu Änderungen kommt. In diesem Fall muss daher immer git pull ausgeführt werden, um das lokale Repository zu aktualisieren.

git pull: Aktualisierung Repository (lokal)

Der Befehl ist ganz einfach:

git pull

Dies sorgt dafür, dass das lokale Repository aktualisiert wird. Grundsätzlich sollte man daher immer git pull vor git push durchführen.

git checkout: Mit Branches arbeiten

Wenn man in mehreren Versionen der Website oder einer Software arbeiten möchte, macht es Sinn mehrere Branches (Zweige) zu haben. Im Master ist die stabile Live-Version. In einem weiteren Branch – dem Entwickler-Zweig – können hingegen neue Features ausprobiert werden. Mit Branches können also unabhängig voneinander verschiedene Versionen eines Programms entwickelt werden. Jeder Branch hat dabei seine eigenen Dateien.

Beim Erstellen eines Repositorys wird automatisch ein Branch erstellt. Dies ist der Master-Zweig. Um einen neuen Branch in git zu erstellen und zu aktivieren, gibt es folgenden Befehl:

git branch feature1
git checkout feature1

Oder man wählt die Kurz-Schreibweise:

git checkout -b feature1

Will man zwischen Zweigen wechseln, dann gibt man folgendes Kommando ein:

git checkout [zweigname]

Im neuen Branch kann man nun ein neues Feature programmieren und Änderungen durchführen. Bevor man hier aber einen einfachen git push durchführen kann, muss man zunächst git mitteilen, dass das ursprüngliche Repository für den neuen Branch benutzt werden kann. Das geschieht mit:

git push --set-upstream origin feature1

Wenn man wissen möchte, in welchen Branch man sich aktuell befindet, dann gibt es folgende Anweisung:

git branch

git stash: Unfertigen Code speichern

Unter Stash versteht man einen Bereich, wo man unfertigen Code ablegen kann. Stash kommt zum Einsatz, wenn man zwischen Branches wechseln möchte. Normalerweise führt man vor dem Wechsel einen Commit durch, damit die Änderungen nicht verloren gehen. Wenn man keinen Commit durchführen möchte, da der Code nicht commit-fähig ist, kann man einen Stash durchführen.

Die geschieht mit git stash.

git merge: Zweige zusammenführen

Merge bedeutet, dass man 2 Zweige zusammenführt. Hat man nun ein Feature fertiggestellt, müssen die Dateien aus dem Feature-Zweig in den Master-Zweig. Hierzu muss man zunächst in den Master wechseln. Danach führt man den Merge aus.

git checkout master
git merge feature1

Im Anschluss wird der Merge von git durchgeführt. Wenn sich bei einer Datei verschiedene Codebereiche seit der letzten Aktualisierung des Repositorys geändert haben, kann git den Merge selbst durchführen. Hat sich der Master-Zweig nicht verändert, dann spricht man vom Fast-Forward-Merge. Im schlimmsten Fall liegt aber ein Merge-Konflikt vor. Dies liegt vor, wenn git die Änderungen in beiden Zweigen nicht selbstständig zusammenführen kann. Mehr Infos dazu gibt es hier.

Ergebnis
Das Ergebnis ist, dass lokal erstellte, bearbeitete und aktualisierte Dateien auch im Remote Repository zur Verfügung stehen. Dadurch hat das ganze Team wieder Zugriff und man hat eine Versionskontrolle aller Dateien. Sollte einmal etwas schief gehen, kann man so ganz einfach einen alten Stand wiederherstellen.

Wichtiger Hinweis
Diese Vorgehensweise funktioniert, wenn man selbst Teil des Teams und Projekts ist. Es ist aber auch üblich, dass man an fremden Projekten mitwirkt. Hier kann man mit git clone das Repository lokal kopieren. Wenn man aber mit git push versucht seine Änderungen hochzuladen wird das scheitern, da man ja nicht Teil des Teams ist und somit keinen Zugang zum eigentlichen Remote Repository hat.

Hier muss man einen anderen Weg gehen. Dazu gibt es das Konzept des Fork. Fork ist ein Begriff von GitHub. Um die Funktion zu nutzen, muss man sich bei GitHub anmelden und zum gewünschten (fremden) Projekt navigieren. Auf der Projekt-Seite gibt es nun einen Link namens “Fork”. Wenn man draufklickt, dann wird eine Kopie des Projekts im eigenen GitHub-Konto erstellt. Nun kann man mit git clone das Projekt lokal kopieren und später mit git push Änderungen in das GitHub-Fork hochladen. Möchte man nun diese Änderungen dem Original-Projekt präsentieren, muss man in der GitHub-Oberfläche einen “Pull request” (bei GitLab “Merge request”) anstoßen. Dort wählt man seine Änderung aus und übermittelt diesen an das Entwickler-Team des Original-Projekts. Diese können die Änderung annehmen, ablehnen oder kommentieren.

git-Kommandos

Hier nochmal eine Übersicht der oben verwendeten git-Kommandos.

KommandoBedeutung
git mv [dateiname-alt] [dateiname-neu]Benennt eine Datei um
git mv [dateiname] [neue-pfad]Verschiebt eine Datei in ein anderes Verzeichnis
git rm [dateiname]Löscht eine Datei
git reset [dateiname]Entfernt eine Datei aus dem Stage ohne sie im Workspace zu ändern
git restore [dateiname]Änderungen an einer Datei, die seit dem letzten Commit entstanden sind, widerrufen
git stashAktuelle Änderungen im Workspace speichern, ohne einen Commit durchzuführen
git config –global user.name “Demir Jasarevic”Erstellt einen neuen Nutzer
git config –global user.email “demir@domain.com”Erstellt die Mail des Nutzers
git clone [url-des-projekts]Klont ein Projekt von GitHub über HTTPS
git clone git@github.com:[benutzername]/[repository-name].gitKlont ein Projekt von GitHub über SSH
git initWandelt ein lokales Verzeichnis in ein Git-Verzeichnis um
git pushLokales Repository auf ein Remote Repository übertragen
git addÄnderungen vom Workspace in die Stage schieben
git commitDateien aus dem Stage ins Repository schieben
git pullAktualisierung des lokalen Repositorys