Git-Installation und -Konfiguration

Installation

Für ix-Distributionen sollte Git im Standard-Repository vorhanden sein.

Das Paket git-all stellt euch eine vollständige Umgebung für Git bereit. Dieses installiert ihr wie folgt:

$ sudo apt install git-all

Geht es ausschliesslich um Git, genügt das Paket namens git:

$ sudo apt install git

Mit der Bash-Autovervollständigung lässt sich Git auf der Kommandozeile einfacher bedienen. Das entsprechende Paket dazu heisst bash-completion und ihr installiert es wie folgt:

$ sudo apt install bash-completion

Es gibt verschiedene Möglichkeiten, Git auf einem Mac zu installieren. Am einfachsten ist es vermutlich, die Xcode Command Line Tools zu installieren. Hierfür müsst ihr nur git das erste Mal vom Terminal aufrufen:

$ git --version

git-completion könnt ihr mit Homebrew installieren:

Anschließend müsst ihr folgende Zeile in der Datei ~/.bash_profile hinzufügen:

[[ -r "$(brew --prefix)/etc/profile.d/bash_completion.sh" ]] && . "$(brew --prefix)/etc/profile.d/bash_completion.sh"

Ruft dazu https://git-scm.com/download/win auf und startet den passenden Download dazu.

Siehe auch

Konfiguration

Sichern der globalen Konfiguration in ~/.config/git/

Git nutzt verschiedene Ebenen von Konfigurationsdateien, die in der folgenden Reihenfolge ausgeführt werden:

system

gilt für alle User auf eurem Computer, aber es ist unwahrscheinlich, dass ihr dies jemals benutzen werdet.

global

gilt für alle Repositories eines einzelnen Users und wir werden uns das detaillierter anschauen.

local

gilt für ein einzelnes Repository und ist nur für einige wenige Repository-spezifische Optionen.

Git such nach einer globalen Konfigurationsdatei an zwei verschiedenen Orten: ~/.config/git/config und ~/.gitconfig. Der erste Ort ist der Standard für Konfigurationsdateien, der zweite ist der frühere Standard.

Bemerkung

Auf Linux-Maschinen kann ~/.config manchmal ein anderer Pfad sein, abhängig von der Umgebungsvariable XDG_CONFIG_HOME. Dieses Verhalten ist Teil der X Desktop Group (XDG) specification. Ihr erhaltet den Pfad mit:

$ echo $XDG_CONFIG_HOME

Wenn dies nichts zurückgibt, wird Ihr System ~/.config verwenden, andernfalls wird es den angezeigten Pfad verwenden. Der Einfachheit halber werden wir uns von nun an nur noch auf ~/.config beziehen.

Siehe auch

Da ihr Optionen an mehreren Ebenen festlegen könnt, möchtet ihr vielleicht nachvollziehen, woher Git einen bestimmten Wert liest. Mit git config --list [1] könnt ihr alle überschriebenen Optionen und Werte auflisten. Dies könnt ihr kombinieren mit --show-scope [2] um zu sehen, woher Git den Wert bezieht:

$ git config --list --show-scope
system  credential.helper=osxkeychain
global  user.name=veit
global  user.email=veit@cusy.io

Ihr könnt auch --show-origin [3] verwenden, um die Namen der Konfigurationsdateien aufzulisten:

$ git config --list --show-origin
file:/opt/homebrew/etc/gitconfig        credential.helper=osxkeychain
file:/Users/veit/.config/git/config     user.name=veit
file:/Users/veit/.config/git/config     user.email=veit@cusy.io

Bemerkung

Ein umfangreiches Beispiel einer Konfigurationsdatei findet ihr in meinem dotfiles-Repository: .gitconfig.

Migrieren von ~/.gitconfig zu ~/.config/git/config

Wenn ihr aktuell noch die alte Datei ~/.gitconfig verwendet, könnt ihr die Datei mit wenigen Schritten in das ~/.config-Verzeichnis verschieben:

  1. Stellt sicher, dass das ~/.config-Verzeichnis existiert.

  2. Verschiebt eure bestehende Konfigurationsdate in dieses Verzeichnis:

    $ mv ~/.gitconfig ~/.config/git/config
    
  3. Überprüft, ob Git weiterhin eure Konfigurationsdatei liest, indem ihr user.name abfragt:

    $ git config --global user.name
    Veit Schiele
    
  4. Möglicherweise solltet ihr auch noch andere Dateien verschieben, z.B. ~/.gitattributes und ~/.gitignore. Ob diese Dateien vorhanden sind, könnt ihr überprüfen mit

    $ git config --global core.excludesFile
    ~/.gitignore
    $ git config --global core.attributesFile
    ~/.gitattributes
    

    Dann müsst ihr die Dateien ebenfalls verschieben und die zugehörigen Konfigurationseinträge löschen:

    $ mv ~/.gitignore_global ~/.config/git/ignore
    $ git config --global --unset core.excludesFile
    $ mv ~/.gitattributes ~/.config/git/attributes
    $ git config --global --unset core.attributesFile
    

Lesen und Schreiben der Konfigurationseinträge

Wie wir bereits oben gesehen haben, können Konfigurationseinträge gelesen werden mit git config, z.B.:

$ git config --global user.name
Veit Schiele

… und um den Eintrag zu ändern

$ git config --global user.name 'veit'

Ihr könnt die Konfigurationsdatei auch direkt ändern, indem ihr git config zusammen mit der -e|--edit-Option aufruft:

$ git config --global -e

Dies öffnet die ~/.config/git/config-Datei in eurem Standardeditor.

Git speichert die Konfiguration in INI-Dateien.

Der Standardeditor für Git ist definiert in der GIT_EDITOR-Umgebungsvariable oder in Git’s core.editor-Option oder in der VISUAL oder EDITOR-Umgebungsvariable. Ihr könnt die Werte abfragen mit

$ echo $GIT_EDITOR
$ git config core.editor
$ echo $VISUAL
$ echo $EDITOR

Üblicherweise wollt ihr immer denselben Editor verwenden und daher sollte die EDITOR-Umgebungsvariable gesetzt werden. Um dies zu tun, könnt ihr folgendes in ~/.bash_profile oder ~/.zprofile eintragen:

export EDITOR='C:\Program Files (x86)\Microsoft VS Code\code.exe --wait'

Bemerkung

Auf macOS müsst ihr zunächst Visual Studio Code starten, dann die Befehlspalette mit +-p öffnen und schließlich den Befehl Install ‚code‘ command in PATH ausführen.

oder

export EDITOR='vim'

Basiskonfiguration

Git Commits haben zwei Pflichtfelder, die sich auf die Person beziehen: den Namen der Person, die die Code-Änderungen vorgenommen hat und die Person, die den Code ins Repository übertragen hat. In den meisten Workflows ist dies dieselbe Person. Mit den Optionen user.name und user.email könnt ihr diese Informationen für author und committer konfigurieren.

Tipp

Git-Hosts, wie GitHub oder GitLab, verknüpfen Commits mit eurem Profil über die E-Mail-Adresse. Wenn die konfigurierte E-Mail-Adresse nicht mit eurem Profil übereinstimmt, werden eure Commits nicht richtig zugewiesen. Dadurch können Teammitglieder schwerer bestimmen, dass der Commit von euch kommt. Daher solltet ihr den konfigurierten Namen und die E-Mail-Adresse stets überprüfen.

Alternative Konfigurationsdatei

Ihr könnt für bestimmte Arbeitsverzeichnisse andere Konfigurationsdateien verwenden, z.B. um zwischen privaten und beruflichen Projekten zu unterscheiden. Dazu könnt ihr eine lokale Konfiguration in eurem Repository verwenden oder aber Conditional Includes am Ende eurer globalen Konfiguration:


[includeIf "gitdir:~/private"]
path = ~/.config/git/config-private

Dieses Konstrukt sorgt dafür, dass Git zusätzliche Konfigurationen einbezieht oder bestehende überschreibt, wenn ihr in ~/private arbeitet.

Erstellt dazu nun die Datei ~/.config/git/config-private und legt dort eure alternative Konfiguration fest, z.B.:

[user]
    email = kontakt@veit-schiele.de

[core]
    sshCommand = ssh -i ~/.ssh/private_id_rsa

Siehe auch

Kolorieren

Standardmäßig nutzt Git die Fähigkeit eures Terminals, verschiedene Arten von Text einzufärben und zu formatieren. Eine solche Kolorierung ermöglicht euch, die Ausgabe schneller zu analysieren. Die Standardfarben sind jedoch suboptimal: git status beispielsweise markiert geänderte Dateien in Rot, einer Farbe, die im Allgemeinen mit Fehlern assoziiert wird; das Ändern von Dateien ist jedoch kein Fehler, sondern völlig normal in jedem Git-Prozess. Ihr könnt die Optionen color.* verwenden, um die Farben pro Befehl anzupassen. Ich verwende die Farben des cheat sheet colours schon seit langem:

[color "branch"]
    current = yellow reverse
    local = yellow
    remote = green

[color "status"]
    added = yellow
    changed = green
    untracked = cyan

Bemerkung

Später werden wir uns delta ansehen, ein Werkzeug zur besseren Visualisierung von Unterschieden. Seine Kolorierung würde die Informationen aus [colour "diff"] überschreiben, weshalb wir diesen Abschnitt nicht hinzugefügt haben.

Befehle korrigieren

Wenn ihr bei der Eingabe eines Git-Befehls einen Fehler macht, werden standardmäßig ähnliche Befehle aufgelistet und das Programm beendet:

$ git comit -m ':wrench: Update git config'
git: 'comit' ist kein Git-Befehl. Siehe 'git --help'.

Der ähnlichste Befehl ist
    commit

Ihr könnt Git aber auch mit git config --global help.autoCorrect immediate [4] so konfigurieren, dass der erste Treffer automatisch ausgeführt wird:

$ git comit -m ':wrench: Update git config'
WARNUNG: Sie haben Git-Befehl 'comit' ausgeführt, welcher nicht existiert.
Setze fort unter der Annahme, dass Sie 'commit' meinten.
[main 48cafbf5f] :wrench: Update git config

Git korrigiert jedoch nur dann automatisch, wenn ein Befehl eine ausreichend große Übereinstimmung aufweist. Wenn es mehrere potenzielle Übereinstimmungen gibt, werden diese aufgelistet und die Korrektur wird abgebrochen:

$ git co -m ':wrench: Update git config'
git: 'co' is not a git command. See 'git --help'.

The most similar commands are
    commit
    clone
    log

Wenn euch die automatische Korrektur eines Befehls zu viel ist, könnt ihr stattdessen den Prompt-Modus verwenden:

$ git config --global help.autoCorrect prompt
$ git comit -m ':wrench: Update git config'
WARNUNG: Sie haben Git-Befehl 'comit' ausgeführt, welcher nicht existiert.
Stattdessen 'commit' ausführen (y/N)? y
[main 48cafbf5f] :wrench: Update git config

Paginierung

Ihr könnt die Paginierung standardmäßig für einen Befehl aktivieren, indem ihr die entsprechende Option setzt: pager.CMD = true, [5] zum Beispiel, um den Git-Status auf Paginierung umzustellen:

$ git config --global pager.status true

Anmeldedaten verwalten

Seit der Git-Version 1.7.9 lassen sich die Zugangsdaten zu git-Repositories mit gitcredentials verwalten. Um diese zu nutzen, könnt ihr z.B. folgendes angeben:

$ git config --global credential.helper Cache

Hiermit wird euer Passwort für 15 Minuten im Cache-Speicher gehalten. Der Timeout kann ggf. erhöht werden, z.B. mit:

$ git config --global credential.helper 'cache --timeout=3600'

Unter Linux müsst ihr einen sog: Credential Store auswählen. In den meisten Fällen werdet ihr euch für die Secret Service API entscheiden, wie z.B. libsecret von Git, den ihr auswählen könnt mit:

$ git config --global credential.credentialStore secretservice

Unter macOS lässt sich mit osxkeychain die Schlüsselbundverwaltung (Keychain) nutzen um die Zugangsdaten zu speichern. osxkeychain setzt Git in der Version 1.7.10 oder neuer voraus und kann im selben Verzeichnis wie Git installiert werden mit:

$ git credential-osxkeychain
git: 'credential-osxkeychain' is not a git command. See 'git --help'.
$ curl -s -O http://github-media-downloads.s3.amazonaws.com/osx/git-credential-osxkeychain
$ chmod u+x git-credential-osxkeychain
$ sudo mv git-credential-osxkeychain /usr/bin/
Password:
git config --global credential.helper osxkeychain

Dies trägt folgendes in die ~/.gitconfig-Datei ein:

[credential]
    helper = osxkeychain

Alternativ könnt ihr auch den Git Credential Manager installieren mit

brew install --cask git-credential-manager

Für Windows steht der Git Credential Manager (GCM) zur Verfügung. Er ist integriert in Git for Windows und wird standardmäßig mitinstalliert. Zusätzlich besteht jedoch auch ein eigenständiges Installationsprogramm in Releases.

GCM wird mit dem nachfolgenden Aufruf konfiguriert:

$ git credential-manager configure
Configuring component 'Git Credential Manager'...
Configuring component 'Azure Repos provider'...

Dies trägt den [credential]-Abschnitt in eure ~/.gitconfig-Datei ein:

[credential]
    helper =
    helper = C:/Program\\ Files/Git/mingw64/bin/git-credential-manager.exe

Nun öffnet sich beim Clonen eines Repository ein Fenster des GCM und fordert euch zur Eingabe eurer Zugangsdaten auf.

Zudem wird die ~/.gitconfig-Datei ergänzt, z.B. um die folgenden beiden Zeilen:

[credential "https://ce.cusy.io"]
    provider = generic

Die .gitignore-Datei

In der .gitignore-Datei eines Repository könnt ihr Dateien von der Versionsverwaltung ausschließen. Eine typische .gitignore-Datei kann z.B. so aussehen:

/logs/*
!logs/.gitkeep
/tmp
*.swp

Dabei verwendet Git Globbing-Muster, u.a.:

Muster

Beispiel

Erläuterung

**/logs

logs/instance.log, logs/instance/error.log, prod/logs/instance.log

Ihr könnt zwei Sternchen voranstellen um Verzeichnisse an einer beliebigen Stelle im Verzeichnisbaum zu finden.

**/logs/instance.log

logs/instance.log, prod/logs/instance.log aber nicht logs/prod/instance.log

Ihr könnt zwei Sternchen voranstellen um Dateien anhand ihres Namens in einem übergeordneten Verzeichnis zu finden.

*.log

instance.log, error.log, logs/instance.log

Ein Sternchen ist ein Platzhalter für null oder mehr Zeichen.

/logs
!/logs/.gitkeep

/logs/instance.log, /logs/error.log, nicht jedoch /logs/.gitkeep oder /instance.log

Ein vor ein Muster gestelltes Anführungszeichen ignoriert dieses. Wenn eine Datei mit einem Muster übereinstimmt, aber auch mit einem negierenden, das später definiert ist, wird sie nicht ignoriert.

/instance.log

/instance.log, nicht jedoch logs/instance.log

Mit dem vorangestellten Schrägstrich passt das Muster nur zu Dateien im Stammverzeichnis des Repository.

instance.log

instance.log, logs/instance.log

Üblicherweise passen die Muster zu Dateien in jedem Verzeichnis.

instance?.log

instance0.log, instance1.log, aber nicht instance.log oder instance10.log

Ein Fragezeichen passt genau zu einem Zeichen.

instance[0-9].log

instance0.log, instance1.log, aber nicht instance.log oder instance10.log

Eckige Klammern können verwendet werden um ein einzelnes Zeichen aus einem bestimmten Bereich zu finden.

instance[01].log

instance0.log, instance1.log, aber nicht instance2.log oder instance01.log

Eckige Klammern passen auf ein einzelnes Zeichen aus einer bestimmten Menge.

instance[!01].log

instance2.log, aber nicht instance0.log, instance1.log oder instance01.log

Ein Ausrufezeichen kann verwendet werden um ein beliebiges Zeichen aus einer angegebenen Menge zu finden.

logs

logs logs/instance.log prod/logs/instance.log

Wenn kein Schrägstrich anhängt, passt das Muster sowohl auf Dateien als auch auf den Inhalt von Verzeichnissen mit diesem Namen.

logs/

logs/instance.log, logs/prod/instance.log, prod/logs/instance.log

Das Anhängen eines Schrägstrichs zeigt an, dass das Muster ein Verzeichnis ist. Der gesamte Inhalt jedes Verzeichnisses im Repository, das diesem Namen entspricht – einschließlich all seiner Dateien und Unterverzeichnisse – wird ignoriert.

var/**/instance.log

var/instance.log, var/logs/instance.log, nicht jedoch var/logs/instance/error.log

Zwei Sternchen passen zu null oder mehr Verzeichnissen.

logs/instance*/error.log

logs/instance/error.log, logs/instance1/error.log

Wildcards können auch in Verzeichnisnamen verwendet werden.

logs/instance.log

logs/instance.log, nicht jedoch var/logs/instance.log oder instance.log

Muster, die eine Datei in einem bestimmten Verzeichnis angeben, sind relativ zum Stammverzeichnis des Repository.

Git-commit eines leeren Verzeichnisses

In obigem Beispiel seht ihr, dass mit /logs/* keine Inhalte des logs-Verzeichnisses mit Git versioniert werden sollen, in der Folgezeile jedoch eine Ausnahme definiert wird:

!logs/.gitkeep

Diese Angabe erlaubt, dass die Datei .gitkeep mit Git verwaltet werden darf. Damit wird dann auch das logs-Verzeichnis in das Git-Repository übernommen. Eine solche Hilfskonstruktion ist erforderlich, da leere Verzeichnisse nicht mit Git verwaltet werden können.

Warnung

Diese Technik hat jedoch mehrere Nachteile:

  • Sowohl .gitignore wie auch log/.gitkeep müssen bearbeitet werden.

  • Beim Umbenennen des Verzeichnis kann leicht vergessen werden, auch die .gitignore-Datei zu ändern.

  • .gitkeep ist für Git eine ganz normale Datei; der Name legt jedoch nahe, dass die Datei von Git besonders behandelt würde.

Eine bessere Möglichkeit ist, in einem leeren Verzeichnis eine .gitignore-Datei mit folgendem Inhalt zu erstellen:

# ignore everything except .gitignore
*
!.gitignore

Dies vermeidet die vorher genannten Probleme.

Dateien zentral mit excludesfile ausschließen

Ihr könnt jedoch auch zentral für alle Git-Repositories Dateien ausschließen. Hierfür wird üblicherweise in der ~/.gitconfig-Datei folgendes angegeben:

[core]

    # Use custom `.gitignore`
    excludesfile = ~/.gitignore
    

Bemerkung

Hilfreiche Vorlagen findet ihr in meinem dotfiles-Repository oder auf der Website gitignore.io.

Ignorieren einer Datei aus dem Repository

Wenn ihr eine Datei ignorieren wollt, die in der Vergangenheit bereits dem Repository hinzugefügt wurde, müsst ihr die Datei aus eurem Repository löschen und dann eine .gitignore-Regel für sie hinzufügen. Die Verwendung der Option --cached bei git rm bedeutet, dass die Datei aus dem Repository gelöscht wird, aber als ignorierte Datei in eurem Arbeitsverzeichnis verbleibt.

$ echo *.log >> .gitignore
$ git rm --cached *.log
rm 'instance.log'
$ git commit -m "Remove log files"

Bemerkung

Ihr könnt die Option --cached weglassen, wenn ihr die Datei sowohl aus dem Repository als auch aus eurem lokalen Dateisystem löschen wollt.

Commit einer ignorierten Datei

Es ist möglich, den Commit einer ignorierten Datei an das Repository mit der Option -f (oder --force) bei git add zu erzwingen:

$ cat data/.gitignore
*
$ git add -f data/iris.csv
$ git commit -m "Force add iris.csv"

Ihr könnt dies in Erwägung ziehen, wenn ihr ein allgemeines Muster (wie *) definiert habt, aber eine bestimmte Datei übertragen wollt. Eine bessere Lösung ist meist jedoch, eine Ausnahme von der allgemeinen Regel zu definieren:

$ echo '!iris.csv' >> data/.gitignore
$ cat data/.gitignore
*
!iris.csv
$ git add data/iris.csv
$ git commit -m "Add iris.csv"

Dieser Ansatz dürfte für euer Team offensichtlicher und weniger verwirrend sein.

Fehlersuche in .gitignore-Dateien

Bei komplizierten .gitignore-Mustern oder bei Mustern, die über mehrere .gitignore-Dateien verteilt sind, kann es schwierig sein, herauszufinden, ob oder warum eine bestimmte Datei ignoriert wird.

Mit dem Aufruf git status --ignored=matching [6] wird der Ausgabe ein Abschnitt Ignorierte Dateien hinzugefügt, der zusätzlich alle von Git ignorierten Dateien und Verzeichnisse beinhaltet:

$ git status --ignored=matching
Auf Branch main
Ignorierte Dateien:
  (benutzen Sie "git add -f <Datei>...", um die Änderungen zum Commit vorzumerken)
    .DS_Store
    docs/.DS_Store
    docs/_build/doctrees/
    docs/_build/html/
    docs/clean-prep/.ipynb_checkpoints/

    nichts zu committen, Arbeitsverzeichnis unverändert

Ihr könnt den Befehl git check-ignore [7] mit der Option -v (Langform: --verbose) verwenden, um festzustellen, welches Muster die Ursache für das Ignorieren einer bestimmten Datei ist:

$ git check-ignore -v data/iris.csv
data/.gitignore:2:!iris.csv data/iris.csv

Obige Ausgabe besteht aus vier Feldern (Trennzeichen sind drei Doppelpunkte und ein Leerzeichen) und beinhaltet:

FILE_CONTAINING_THE_PATTERN

den Namen der Datei, die das Muster enthält.

LINE_NUMBER_OF_THE_PATTERN

die Zeilennummer, in der in der Datei FILE_CONTAINING_THE_PATTERN das Muster gefunden wurde.

PATTERN

das gefundene Muster.

FILE_NAME

den Namen der Datei inklusive Pfad, die Git ignoriert.

Ihr könnt mehrere Dateinamen an git check-ignore übergeben, wenn ihr möchtet, und die Namen selbst müssen nicht einmal den Dateien entsprechen, die in eurem Repository existieren.

Eine vollständige Liste aller ignorierten Dateien erhaltet ihr mit git ls-files --ignored --exclude-standard --others [8]. Mit --exclude-standard werden die Standard-ignore-Dateien gelesen und mit --others werden die nicht-versionierten Dateien statt der versionierten angezeigt:

$ git ls-files --ignored --exclude-standard --others
.DS_Store
_build/doctrees/clean-prep/bulwark.doctree
_build/doctrees/clean-prep/dask-pipeline.doctree
_build/doctrees/clean-prep/deduplicate.doctree

Gelegentlich möchtet ihr vielleicht die globale ~/.gitignore-Datei umgehen um zu sehen, welche Dateien Git unabhängig von eurer Konfiguration immer ignoriert. Ihr könnt dies tun, indem ihr zu einer anderen exclude-Option wechselt, --exclude-per-directory, die nur die .gitignore-Dateien des Repositorys verwendet:

$ git ls-files --ignored --exclude-per-directory=.gitignore --others
docs/_build/doctrees/clean-prep/bulwark.doctree
docs/_build/doctrees/clean-prep/dask-pipeline.doctree
docs/_build/doctrees/clean-prep/deduplicate.doctree

Beachtet, dass die Datei .DS_Store nicht mehr als ignoriert aufgeführt wird.

Wenn ihr --others durch --cached ersetzt, listet git ls-files Dateien auf, die ignoriert werden würden, es sei denn, sie wurden bereits übertragen:

$ git ls-files --ignored --exclude-per-directory=.gitignore --cached
data/iris.csv

Möglicherweise habt ihr solche Dateien, weil jemand sie vor den relevanten Mustern in einer .gitignore-Datei hinzugefügt hat, oder weil jemand sie mit git add --force hinzugefügt hat. So oder so, wenn ihr die Datei nicht mehr mit Git verwalten wollt, könnt ihr sie mit dem folgenden Einzeiler aus der Git-Verwaltung nehmen, sie aber nicht löschen:

$ git ls-files --ignored --exclude-per-directory=.gitignore --cached | xargs -r git rm --cached
rm 'data/iris.csv'