====== GIT ====== GIT kennenlernen, Spickzettel für Einsteiger. ^Begriff^Bedeutung ^ |Repository|Eine für das jeweilige Projekt eigene Git-DB (in Filesystem des Projektes), die durch GIT intern verwaltet wird.\\ Der Entwickler kommt mit dieser DB eigentlich nicht in Berührung. | |Working Tree|Das **Projekt-Verzeichnis**, in dem sich alle zum Projekt gehörende QuellCode-Files befinden (samt Unterverzeichnisse). | |Commit |Veränderungen am Working Tree, sprich an den QuellCode-Files, werden detailliert in den Commits festgehalten. | |HEAD |Eine symbolische Referenz auf den **neuesten** Commit im **aktuellen** Branch. | |Index |Der Index ist eine Zwischenstufe zwischen Working Tree und Repository.\\ Im Index werden (i.d.R. mehrere) Files für einen späteren Commit vorbereitet. | |Tag |Tags sind symbolische Namen für (schwer zu merkende) SHA-1-Summen. | |Branch |Eine Abzweigung in der Entwicklung.\\ :!: Tipp zur Benennung von Branches:\\ Es sorgt für mehr Übersicht, wenn man den Namen eines Arbeits-Branches aus 3 Teilen zusammenfasst, getrennt durch ein "**/**"-Zeichen, nach dem Schema "KindOfBranch/User/Topic".\\ Einige Beispiele: "feature/juergen/Task-12345.GUI-Erweiterung_XY" oder "bugfix/oliver/Task-54321.Falsche_Berechnung_XYZ". | |master |Default-Branch eines Repositories. (Kann/darf umbenannt/gelöscht werden.)\\ Es wird empfohlen, die laufende Entwicklung in (mindestens) einem separaten Branch zu führen, der i.d.R. "develop" benannt wird.\\ Noch besser, auch den "develop"-Branch in die weiteren **Feaure**- und **Bugfix**-Branches für die Dauer der jeweiligen Implementierungen abzuzweigen und danach wieder in den "develop"-Branch zu mergen. | ===== Installation ===== FIXME Client-Installation (für lokale Entwicklung / lokale Repositories) FIXME Installation auf dem Server (für remote-Repositories) ===== Konfiguration ===== GIT-Einstellungen und deren Ursprung anzeigen lassen: ~/prj> git config --list --show-origin Oder einfach die Einstellungen anzeigen lassen: ~/prj> git config --list Eigene Identität in der GIT-Konfiguration hinterlegen (die GIT-Commits speichern diese Informationen in den Commits unveränderlich ab): ~/prj> git config --global user.name "Vorname Nachname" ~/prj> git config --global user.email "vorname.nachname@domain.tld" # Oder so: ~/prj> git config --global user.email vorname.nachname@domain.tld Durch die Option "**%%--global%%**" werden diese Angaben für die **gesamte** GIT-Installation gelten.\\ Ohne diese Option gelten die Angaben nur für das jeweilige Projekt (z.B. falls man für einige Projekte abweichende Namen und eMail-Adresse verwenden möchte). Folgender Befehl definiert, wonach EOLs (Line-Endings) beim checkout konvertiert werden: # EOL - mögliche Werte: lf, crlf, native ~/prj> git config --global core.eol lf :!: Die Git-Konfiguration wird im File "**~/.gitconfig**" (globale Konfig) bzw. "**.git/config**" (für das jeweilige Repository) gespeichert.\\ Falls ein Parameter in beiden Files vorhanden ist (global __und__ im Repository), dann hat der Parameter aus der Repository-Konfig Vorrang.\\ Falls ein Git-Parameter in keinem der beiden Files vorhanden ist, wird ein für den jeweiligen Parameter festgelegter default-Wert verwendet. Liste aller gesetzten Einstellungen: ~/prj> git config -l :!: Mit den Kommandos "**%%git config -e%%**" bzw. "**%%git config --global -e%%**" lassen sich die beiden Konfigurationsdateien in einem Editor ändern. ==== SSH-Zugriff für das remote Repository konfigurieren ==== Um nicht jedes mal beim Datenaustausch zwischen dem remote und dem lokalen Repository den Benutzernamen und das Passwort eingeben zu müssen, ist es ratsam, einen SSH-Key dafür zu verwenden. Einen SSH-Key erzeugen: ~/prj> ssh-keygen -t rsa -b 2048 -C "Eine_aussagekräftige_Schlüsselbezeichnung" Bei der Frage nach dem FileNamen, unter dem das Schlüssel-Paar gespeichert werden soll, kann man den Standardnamen "id_rsa" übernehmen, besser aber einen für den Zweck **passenden Namen** anzugeben. Wenn das Schlüssel-Paar für GitLab gedacht ist, könnte man es z.B. als "**id_rsa_gitlab**" benennen.\\ :!: Die **Passphrase** lässt man am besten leer, damit der Datenaustausch zwischen dem remote und dem lokalen Repository quasi automatisch stattfindet, ohne dass man jederzeit die Passphrase eingeben muss. Da dieses Schlüssel-Paar ausschließlich für die Kommunikation mit dem remote-Repository zuständig ist, ist das Sicherheitsrisiko vertretbar. Der neu erstellte Schlüssel soll dem SSH-Agenten hinzugefügt werden: ~/prj> ssh-add ~/.ssh/id_rsa_gitlab Der SSH-Agent muss noch wissen, bei welchem Host der o.g. Schlüssel zum Einsatz kommen soll. Dafür ist das File "**~/.ssh/config**" zuständig: Host gitlab.com HostName gitlab.com PreferredAuthentications publickey IdentityFile ~/.ssh/id_rsa_gitlab Nicht vergessen, die Zugriffsrechte auf beide Files auf das Nötigste zu reduzieren: ~/prj> chmod 400 ~/.ssh/id_rsa_gitlab ~/prj> chmod 600 ~/.ssh/config Damit das lokale Git mit dem remote-Repository über das SSH-Protokoll kommuniziert, muss noch sichergestellt werden, dass in der Git-Konfiguration (".git/config") die URL für das **[remote "origin"]** auf das SSH-Protokoll (und nicht auf z.B. "HTTPS") eingestellt ist. Die allgemeine Syntax lautet: ssh://[username@]repositoryhost.tld[:port]/pfad/zum/repository.git :!: Es gibt dabei manche Stolperfallen! Einige Git-Hoster verlangen als UserNamen nicht den korrekten UserNamen des Repository-Besitzers, sondern einen bestimmten Namen. Zum Beispiel GitLab erwartet an dieser Stelle den Namen **git**: ssh://git@gitlab.com/pfad/zum/repository.git Und natürlich nicht vergessen, den **public-Key** (in dem o.g. Beispiel das File "~/.ssh/id_rsa_gitlab.pub") bei dem remote-Repository einzutragen. ===== Lokale und remote Repository ===== :?: Lokales Repository erstellen -> nach Remote (z.B. GitHub/GitLab) übertragen. ^Schritt ^Vorgehen ^ |Ein neues Repository erstellen. |Repository-Name beispielsweise "git_sample": ~/prj> git init git_sample | FIXME :?: Das Repository Remote (z.B. in GitHub/GitLab) erstellen -> von dort in die lokale Umgebung klonen. ^Schritt ^Vorgehen ^ |Repository von Remote nach Lokal klonen. |Repository beispielsweise "git_sample": ~/prj> git clone git://gitlab.com/juser-name/git_sample.git Dabei wird der Ordner "git_sample" erstellt.\\ \\ Oder das Gleiche per SSH-Protokoll: ~/prj> git clone ssh://git@gitlab.com/juser-name/git_sample.git\\ Falls der Ordner anders heißen soll als das Repository, den Ordner-Namen (hier als Beispiel "mein_git_sample") mitgeben: ~/prj> git clone git://gitlab.com/juser-name/git_sample.git mein_git_sample_ordner | | |Lokal wird das remote Repository default-mäßig als **origin** benannt. ~/prj> cd git_sample ~/prj/git_sample> git remote origin | |"origin" umbenennen. |Der default-Name "origin" läßt sich umbenennen, hier z.B. in "gitlab": ~/prj/git_sample> git remote rename origin gitlab ~/prj/git_sample> git remote gitlab | | |...oder gleich beim Klonen die Option **%%--origin%%** mitgeben: ~/prj> git clone --origin gitlab git://gitlab.com/juser-name/git_sample.git | FIXME ===== Beispiele für Kommandos und häufige Aufgaben ===== ==== Versionierung ==== ^Kommando%%¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯%% ^Bedeutung ^ |git **status** |Zeigt den aktuellen Stand des (lokalen) Repositorys an (im aktuell aktiven Branch). | |git **add** //FileName.Ext// |Die Datei //FileName.Ext// wird dem Git-**Index** (für einen späteren Commit) hinzugefügt. | |git **add** %%-u%%\\ git **add** %%--update%% |Alle Änderungen (an allen Dateien) in einem Rutsch in den Index übernehmen. | |git **add** %%--patch%% //FileName.Ext//\\ git **add** -p //FileName.Ext//\\ git **add** -p |Commits schrittweise (interaktiv) erstellen.\\ Interaktive Optionen:\\ **y** (yes) - übernimmt den aktuellen Hunk in den Index;\\ **n** (no) - übernimmt den aktuellen Hunk nicht;\\ **q** (quit)- übernimmt weder den aktuellen Hunk noch einen der folgenden;\\ **a** (all) - übernimmt den aktuellen Hunk und alle, die ihm folgen (in der aktuellen Datei);\\ **s** (split) - versucht, den aktuellen Hunk zu teilen;\\ **e** (edit) - editiert den aktuellen Hunk. | |git **diff**\\ \\ git **diff** %%--staged%% |Zeigt Änderungen an den Quelltexten, die durch einen Commit in das Repository einfließen würden.\\ \\ Ohne Parameter "%%--staged%%" werden Änderungen angezeigt, die sich noch im "Working Tree" befinden,\\ mit dem Parameter "**%%--staged%%**" - die, die sich (durch "git add") bereits in der "Staging Area" (im Index) befinden (Unterschiede zwischen Index und HEAD). | |git **diff** %%--word-diff%% |Statt der entfernten und hinzugefügten Zeilen zeigt diese Variante die Ausgabe mit einer entsprechenden Syntax sowie farblich kodiert die hinzugekommenen (grün) und entfernten (rot) Wörter. | |git **reset**\\ git **reset** HEAD\\ git **reset** -p\\ FIXME |"git reset" bewirkt das Gegenteil davon, was "git add" gemacht hat.\\ "git reset HEAD" setzt den Index zurück.\\ "git reset -p" tut es interaktiv (analog zu "git add -p"). | |git **commit** |Ohne Argumente fasst Git alle Files, die sich im Index befinden, zu einem Commit zusammen und öffnet einen Editor, in dem man die (teils bereits vorausgefüllte) Commit-Message erstellen kann. | |git **commit -m** "//Commit-Message//" |Alle Dateien aus dem Git-Index werden zusammengefasst und in einem in sich abgeschlossenen Daten-Block, dem s.g. Commit, festgehalten.\\ Alle Commits sind miteinander verkettet - dadurch sind sämtliche Änderungen an den QuellTexten nachvollziehbar. | |git **commit** %%--amend%%\\ FIXME |Damit kann man den aktuellen Commit noch geringfügig verändern. (Dadurch ändert sich konsequenterweise auch die Commit-ID.)\\ Es startet automatisch auch ein Editor, so dass man auch noch die Commit-Message bearbeiten kann. | |git **fetch**\\ git **fetch** origin |Lädt Commits aus dem remote-Repository, die dem lokalen Repository voraus sind, in das lokale Repository herunter. (**⇓**) | |git **merge**\\ git **merge** origin/master |Commits, die mit dem vorherigen "**git fetch**" herunter geladen wurden, werden mit dem lokalen Repository gemerged. (**⇔**)\\ Mit der Angabe "origin/master" legt man fest, in welchen Branch die Commits gemerged werden sollen. | |git **pull**\\ git **pull** origin master |Macht das gleiche wie "git fetch" und "git merge" zusammen, nur in einem Rutsch. (**⇓** + **⇔**) | |git **push**\\ git **push** origin master |Lädt Commits aus dem lokalen Repository in das remote-Repository hoch. Das ist das Gegenstück zu "git fetch" bzw. "git pull". (**⇑**) | |git **push** origin ein_neuer_branch\\ git **branch** -u origin/ein_neuer_branch |Lädt man Commits aus dem **neuen** Branch hoch, den es in dem remote-Repository noch nicht gibt,...\\ ...muss dieser in der Upstream-Konfiguration vermerkt werden. | |git **push** -u origin ein_neuer_branch |Das Gleiche wie oben, nur in einem Rutsch. | FIXME %%git stash%% - S.119-124 FIXME %%git notes%% - S.125-126 FIXME Revert - S.64-65 ==== Versionshistorie ==== ^Kommando ^Bedeutung ^ |git **show**\\ git **show** HEAD\\ git **show** master\\ \\ git **show** //Commit-ID//\\ \\ __Beispiele mit Commit-ID:__\\ git **show** 3363115a\\ git **show** 3363115ad35db647f088f064b57b10d6b9670298 |Infos zu einem bestimmten Commit einsehen.\\ Ohne Parameter wird der aktuellste Commit angezeigt.\\ \\ Es werden alle relevanten Infos über einen Commit angezeigt:\\ - Commit-ID\\ - Autor\\ - Datum/Uhrzeit des Commits\\ - Commit-Nachricht\\ - Zusammenfassung der Veränderungen im Unified-Diff-Format. | |git **log**\\ git **log** -//n//\\ \\ __Beispiele:__\\ git **log** -4\\ git **log** -1 //Commit-ID// |Mehrere Commits (als Liste) in einem Rutsch einsehen.\\ Die Ausgabe von Commits auf die letzten "n" Stück begrenzen.\\ \\ \\ Zeigt nur die letzten 4 Commits.\\ Zeigt einen einzelnen Commit mit der "Commit-ID". | |git **log** %%--after%%='//Datum//'\\ git **log** %%--since%%='//Datum//'\\ git **log** %%--before%%='//Datum//'\\ git **log** %%--until%%='//Datum//'\\ \\ __Beispiele:__\\ git **log** %%--since%%='yesterday'\\ git **log** %%--since%%='2021-10-04'\\ git **log** %%--since%%='2021-09-01' %%--until%%='2021-09-05' |Zeigt nur die Commits ab dem //Datum// an.\\ Parameter %%--since%% ist ein Synonym für %%--after%%.\\ Zeigt nur die Commits vor dem //Datum// an.\\ Parameter %%--until%% ist ein Synonym für %%--before%%. | |git **log** %%--%% //Filename.Ext// //*.Ext// //Ordnername%%/%%// |Es werden nur die Commits angezeigt, die die angegebenen Dateien betreffen.\\ :!: Zwischen dem Doppelstrich "%%--%%" und der Liste der Dateinamen ist eine Leerstelle. | |git **log** %%--author%%='//Max Mustermann//'\\ git **log** %%--committer%%='//Fritz Meier//' |Es werden Commits angezeigt, die den //Max Mustermann// als **Author** haben.\\ Es werden Commits angezeigt, die den //Fritz Meier// als **Committer** haben.\\ :!: Die beiden Namen müssen nicht vollständig angegeben werden. Nur ein Teil des Namens reicht auch. | |git **log** %%--grep%%=Suchmuster\\ git **log** -i %%--grep%%=Suchmuster |Es werden Commits angezeigt, in denen das Suchmuster innerhalb der Commit-Nachricht vorkommt.\\ Durch den Parameter "**-i**" wird die Groß-/Kleinschreibung ignoriert. | |gitk\\ gitk %%--all%% |**Gitk** ist ein GUI für die graphische Darstellung des aktuellen Branches.\\ Mit der Option "%%--all%%" werden alle Branches in einem Graphen dargestellt.\\ Gitk akzeptiert im wesentlichen die gleichen Optionen wie "git log". | FIXME Wie findet man heraus, von welchem Branch wurde der aktuelle (bzw. jeder beliebige) Branch abgeleitet? (Ohne graphische Branches-Übersicht.) ==== Suche ==== ^Kommando%%¯¯¯¯¯¯¯¯%% ^Bedeutung ^ |git **grep** //Suchmuster// |Git-eigenes "grep"-Kommando, dass alle gängigen Optionen von dem GNU-grep unterstützt, sucht jedoch innerhalb der von Git verwalteten Dateien und wesentlich **schneller** als das "Original" ist. | ==== Fileoperationen ==== ^Kommando ^Bedeutung ^ |git **rm** //FileName.Ext//\\ git **rm** -r //FileName.Ext//\\ git **rm** -f //FileName.Ext// |Datei löschen.\\ Funktioniert wie die reguläre Unix-Kommando, darüber hinaus jedoch modifiziert den Git-Index.\\ Mit der Option "-r" - rekursiv, mit der Option "-f" - ohne Rückfrage. | |git **mv** //FileName.Ext//\\ git **mv** -r //FileName.Ext//\\ git **mv** -f //FileName.Ext// |Datei verschieben/umbenennen.\\ Funktioniert wie die reguläre Unix-Kommando, darüber hinaus jedoch modifiziert den Git-Index.\\ Mit der Option "-r" - rekursiv, mit der Option "-f" (force) - ohne Rückfrage. | ==== Branches ==== ^Kommando ^Bedeutung ^ |git **branch**\\ git **branch** -v |Listet alle Branches auf (nur die Namen).\\ Mit der Option "-v" werden zusätzlich die Commit-IDs und die 1. Zeile aus der Commit-Message angezeigt. | |git **branch** //BranchName//\\ git **branch** //BranchName Commit-ID// |Erstellt aus dem aktuellen Branch einen neuen Branch mit dem Namen "//BranchName//".\\ FIXME | |git **branch** -m //NeuerBranchName//\\ git **branch** -m //AlterBranchName NeuerBranchName// |Der aktuelle Branch wird zu "//NeuerBranchName//" umbenannt.\\ Der Branch mit dem Namen "//AlterBranchName//" wird zu "//NeuerBranchName//" umbenannt.\\ :!: Die Umbenennung funktioniert nur wenn kein Branch mit dem Namen "//NeuerBranchName//" bereits existiert. | |git **branch** -d //BranchName// |Löscht den Branch "//BranchName//". Man kann auch mehrere Branches gleichzeitig angeben. | |git **checkout** //BranchName//\\ git **checkout** -b //NeuerBranch// |Wechselt von dem aktuellen Branch zu dem Branch "//BranchName//".\\ Zuerst erstellt einen neuen Branch "//NeuerBranch//" und dann wechselt zu ihm. | |git **merge** //BranchName// |Übernimmt Änderungen von dem Branch "//BranchName//" in den aktuellen Branch (also in den "HEAD"). | |git **branch** -r |Alle verfügbaren remote-Branches anzeigen. Ein ähnliches Ergebnis, jedoch mit mehr Infos:\\ git remote show origin | |git **branch** -a |Alle verfügbaren Branches anzeigen - remote und die lokalen. | FIXME mergetool - S.81-82 FIXME Tags - S.55-61 ==== Remotes ==== ^Kommando%%¯¯¯¯¯¯¯¯¯¯%% ^Bedeutung ^ |git **remote** show\\ git **remote** show origin |Die Zusammenfassung des Remotes anzeigen lassen. Durch die Option "-n" wird die Abfrage bei dem Remote unterdrückt (es wird nur die lokal vorhandene Zusammenfassung angezeigt). | ---- Stand: 13.05.2023 EOF