====== Git ======
Git ist eine freie Software zur [[versionsverwaltung:versionsverwaltungssysteme|verteilten Versionsverwaltung]] von Dateien. Es wurde ursprünglich für die Quellcode-Verwaltung des Linux-Kernels entwickelt.
Die Entwicklung von Git wurde im April 2005 von Linus Torvalds begonnen, um das bis dahin verwendete Versionskontrollsystem BitKeeper zu ersetzen, welches durch eine Lizenzänderung vielen Entwicklern den Zugang verwehrte. Die erste Version erschien bereits wenige Tage nach der Ankündigung.
[[wpde>Git|Quelle]]
===== Links =====
* [[http://chaosradio.ccc.de/cre130.html|Ausführlicher Podcast zu Verteilten Versionskontrollsystemen]]
* **[[http://git.or.cz/course/svn.html|Git - SVN Crash Course]]**
* [[http://git-scm.com/documentation|git-scm documentation]]
* [[http://code.google.com/p/gitextensions/|Git Extensions]]
* [[http://progit.org/|Pro Git book]]
* [[http://book.git-scm.com/|The Git Community Book]]
* [[http://www.pragprog.com/titles/tsgit/pragmatic-version-control-using-git|Pragmatic Version Control Using Git]]
* [[http://wiki.ubuntuusers.de/Git|Git (ubuntuusers)]]
* [[https://github.com/sitaramc/gitolite|Gitolite]] - Einen zentralen Git-Server mit Zugriffsrechten verwalten
===== Serversoftware =====
selbst-gehostet:
* [[https://gitea.io/en-us/|gitea]]
* [[https://gogs.io/docs|gogs]]
* gitolite
* [[software:gitlab]]
* [[https://bitbucket.org/|bitbucket server]] (kommerziell, nur noch in datacenter-variante für $$$$$)
* ...
cloud-hosting:
* [[https://github.com/|github]]
* [[https://bitbucket.org/|bitbucket]] (kommerziell)
* ...
===== Clientsoftware =====
* [[http://code.google.com/p/tortoisegit/|TortoiseGit]]
* [[http://sourcetreeapp.com/|sourcetree]] - Git & Mercurial client (win / mac)
* [[http://www.syntevo.com/smartgithg/index.html|smartgit]] - java-basierter Git & Mercurial client (kostenpflichtig)
* [[https://code.google.com/p/gitextensions/|Git Extensions]]
===== Befehle =====
Liste der Befehl ausgeben:
git help
usage: git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]
The most commonly used git commands are:
add Add file contents to the index
bisect Find the change that introduced a bug by binary search
branch List, create, or delete branches
checkout Checkout a branch or paths to the working tree
clone Clone a repository into a new directory
commit Record changes to the repository
diff Show changes between commits, commit and working tree, etc
fetch Download objects and refs from another repository
grep Print lines matching a pattern
init Create an empty git repository or reinitialize an existing one
log Show commit logs
merge Join two or more development histories together
mv Move or rename a file, a directory, or a symlink
pull Fetch from and merge with another repository or a local branch
push Update remote refs along with associated objects
rebase Forward-port local commits to the updated upstream head
reset Reset current HEAD to the specified state
rm Remove files from the working tree and from the index
show Show various types of objects
status Show the working tree status
tag Create, list, delete or verify a tag object signed with GPG
See 'git help COMMAND' for more information on a specific command.
==== Export eines Repos ====
branch master archivieren ("exportieren"), normalerweise geht das direkt in ein .tar.gz, hier in ein Verzeichnis ''/tmp/meinRepo'':
cd $Repo && git archive master | tar -x -C /tmp/meinRepo
===== Arbeiten mit Repositories =====
Lokales Repo neu anlegen (Unterordner .git wird erstellt): git init
Änderungen in anderes Repo übertragen ([[http://www.kernel.org/pub/software/scm/git/docs/git-push.html|git-push]])
==== Client konfigurieren ====
**Globale Einstellungen** (ohne --global gilt es nur für das aktuelle Repo):
* git config --global user.name "Vorname Nachname"
git config --global user.email meine.email@domain.tld
* Einstellungen anzeigen (globale und die vom Repo): git config --list
==== Adress-Schema ====
Entfernte Repositories werden über diese Wege angesprochen (hier Beispiel mit clone):
* [[netzwerke:SSH]]: git clone user@domain.tld/local/path/to/git/repo
* HTTP/HTTPS: git clone https://domain.tld
==== Methode 1: leeres Repo clonen und eine erste Änderung vornehmen ====
Lokal ist nichts vorhanden und das Zielrepo ist leer:
git clone $ADRESSE
cd $REPONAME
touch README.md
git add README.md
git commit -m "habe README angelegt"
git push -u origin master
==== Methode 2: vorhandener Ordner (ohne git) ====
cd Ordner_ohne_git_versionierung
git init
git remote add origin $ADRESSE
git add .
git commit -m "Initial commit"
git push -u origin master
==== Methode 2: vorhandener Ordner (mit git) ====
cd Ordner_mit_git_repo
git remote rename origin old-origin
git remote add origin $ADRESSE
git push -u origin --all
git push -u origin --tags
==== Änderungen vornehmen und tracken ====
* **Dateien hinzufügen**: git add Datei/Verzeichnis
* **Commit** (=Änderungen übertragen) absetzen:
* beim ersten mal: git commit -m "Kommentar";
danach:
* Fall 1: Datei hingekommen? git add neueDatei
* Fall 2: nur vorhandene Dateien geändert? git commit -a -m "Kommentar"
* **Konflikte** beheben
* [[http://www.kernel.org/pub/software/scm/git/docs/user-manual.html#resolving-a-merge|Resolving a merge]]
* [[http://weblog.masukomi.org/2008/07/12/handling-and-avoiding-conflicts-in-git|Handling and Avoiding Conflicts in Git]]
* Anzeige der **Änderungen**
* Commit-Historie: git log
* seit letztem Commit (bzw. dem aktuellen Stand, also der letzten Aktualisierung des Repository-Index): git diff
* seit letztem Commit und Stand: git diff HEAD
* Änderungen zwischen dem letzten Commit und dem Tag "TestTag": git diff TestTag HEAD
* Commit-Vergleich vom letzten Commit zum vorletzten Commit: git show
* **Dateien ignorieren**: [[http://github.com/guides/ignore-for-git|Guides: Ignore for Git]]
==== sauberes git pull ====
Wenn in einem lokalen Repo viele Änderungen vorgenommen werden (die nicht committed sind) kann git pull schlägt fehlschlagen weil lokale Dateien und Änderungen überschrieben werden würden. In diesem Fall kann es sinnvoll sein das lokale Verzeichnis komplett "sauber" zu machen, also alles nicht versionierte zu löschen:
git reset --hard
git clean -fdx
git pull
==== Änderungen zurücknehmen ====
* lokale Änderungen zurücknehmen (wie svn revert): git checkout PFAD_ODER_DATEI
* Datei doch nicht committen: git reset HEAD PFAD_ODER_DATEI
* letzte commit message ändern (öffnet Editor zur Änderung): git commit --amend
* Falls der commit bereits an entfernte Repositories gesendet wurde ist die [[https://help.github.com/en/articles/changing-a-commit-message|Reparatur mit rebase und force push möglich]] (hat aber gravierende Auswirkungen auf Dritte)
* Author / E-Mail beim letzten commit ändern: git commit --amend --author="John Doe "
* mehrere lokale commits mit der falschen user.email sind mit diesem Skript änderbar (leider nicht mit rebase), erfordert aber wieder force push falls bereits übertragen: git filter-branch --env-filter '
OLD_EMAIL="OLD@example.com"
NEW_NAME="NEW NAME"
NEW_EMAIL="NEW@example.com"
if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ]
then
export GIT_COMMITTER_NAME="$NEW_NAME"
export GIT_COMMITTER_EMAIL="$NEW_EMAIL"
fi
if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ]
then
export GIT_AUTHOR_NAME="$NEW_NAME"
export GIT_AUTHOR_EMAIL="$NEW_EMAIL"
fi
' --tag-name-filter cat -- --branches --tags
weitere Informationen bei der Quelle: https://mhagemann.medium.com/how-to-change-the-user-for-all-your-git-commits-ffefbacf2652
* Modifizierungen endgültig verwerfen((alternativ: temporäres Wegräumen geänderter Dateien mit git stash)): git checkout -- PFAD_ODER_DATEI
* rebase: grundlegende Änderungen vornehmen (potentiell desktruktiv, es sollten keine gepushten Commits verändert werden!):
* git rebase -i -p $HASH_DES_LETZEN_GUTEN_COMMITS
* Anschließend können für die nachfolgenden Commits diese Befehle ausgeführt werden:# Befehle:
# p, pick = Commit verwenden
# r, reword = Commit verwenden, aber Commit-Beschreibung bearbeiten
# e, edit = Commit verwenden, aber zum Nachbessern anhalten
# s, squash = Commit verwenden, aber mit vorherigem Commit vereinen
# f, fixup = wie "squash", aber diese Commit-Beschreibung verwerfen
# x, exec = Befehl (Rest der Zeile) mittels Shell ausführen
# d, drop = Commit entfernen
weitere Informationen siehe [[https://www.git-tower.com/learn/git/faq/change-author-name-email|How can I change the author name / email of a commit?]].
==== Pull Request (PR) erstellen ====
Ein Pull Request (PR) bietet die Möglichkeit der vier (oder mehr) Augenprüfung.
# branch erstellen
git branch myChanges origin/master
git checkout myChanges
# Änderungen vornemen + committen
git push --set-upstream origin myChanges
Kurzform (commit ID: b50b2e7):
git checkout -b miniBranch b50b2e7
git push --set-upstream origin miniBranch
Bei beiden Methoden wird die URL zurückgeliefert wo der PR erstellt werden kann.
==== Tags ====
[[https://git-scm.com/book/en/v2/Git-Basics-Tagging|Git Basics - Tagging]]
git tag -a 1.0 [-m "message like version 1.0"]
**Tags übertragen**: git push --follow-tags
dauerhaft konfigurieren:git config --global push.followTags true
((''followTags = true'' in der [push]-Sektion von ~/.gitconfig))
bestimmten Tag übertragen:git push origin
((push all tags at once (not recommended!):git push --tags
))
=== Änderungen zwischen Tags anzeigen ===
Hier zwischen $TAG (ersetzen!) und HEAD:
git log --pretty=oneline $TAG...HEAD
Anderes Format:
git log --pretty=format:"%h; author: %cn; date: %ci; subject:%s" $TAG...HEAD
-> auf branch master beschränken: --first-parent master (Einschränkung: funktioniert nicht für fast-forward-merges, siehe [[https://stackoverflow.com/a/15878581|git log to return only the commits made to the master branch?]].
==== Leere Verzeichnisse einchecken ====
Leere Verzeichnisse werden standardmäßig nicht benutzt, ein verbreiteter work-around ist eine Datei .gitkeep in das Verzeichnis zu legen.
find . -type d -empty -not -path "./.git/*" -exec touch {}/.gitkeep \;
==== git lfs (Large File Storage) ====
du brauchst serverseitig nur die Unterstützung. Im Repo musst du das mit dem client machen:
- git-lfs lokal installieren (bei ubuntu ein extra Paket)
- repo clonen
- cd repo
- git lfs install
- git lfs track *.iso (behandelt als iso-Dateien mit lfs)
- -> legt .gitattributes an mit Inhalt: *.iso filter=lfs diff=lfs merge=lfs -text
- git add .gitattributes
- git commit .gitattributes
https://www.atlassian.com/git/tutorials/git-lfs
===== Verzeichnisaufbau =====
* **Aufbau des internen .git Verzeichnissses**:
* config (Datei): Konfiguration, sehr einfaches Beispiel:
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
* index (Datei): Metadaten, auch als "Staging Area" bezeichnet
* info (Unterverzeichnis):
* exclude : enthält Dateinamen von Dateien die von der Versionverwaltung ausgeschlossen sind
* logs (Unterverzeichnis): Historie
* objects (Unterverzeichnis): Repository Struktur
* refs (Unterverzeichnis): Verweise innerhalb des Repository
==== Objektypen ====
* **Objektypen** (zlib-komprimiert; mit SHA-1 [[:Hash-Funktion|Hash]] ansprechbar)
* Commit - bestimmter Änderungszeitpunkt, besteht aus diesen Elementen:
- Kommentar (auch "Commit-message")
- Information über Zeit und Autor
- Verweise auf (beliebig viele) übergeordnete Commits ("parents")
- Zeiger auf einen Tree
* Tree - Liste von Dateien
* Blob - speichert beliebige Dateinhalte
* Tag - Beschriftung oder Etikett (Verweis auf ein Commit-Objekt das mit Namen und Kommentar "tag message"). Nützlich für Versionbezeichnung und auch GPG-Signatur möglich.
==== Submodules ====
Git kann andere Repositories referenzieren (diese als Submodule einbinden).
[[https://www.git-tower.com/learn/git/ebook/en/command-line/advanced-topics/submodules#start|Dokumentation zu Submodulen]].
=== anlegen ===
* Entfernte Repos: ''git submodule add URL [lokaler_Pfad_im_Repo]'' ((standard wäre der Reponame))
* Auf gleichem Speicherort/Ebene **mit relativer URL**: ''git submodule add ../URL [lokaler_Pfad_im_Repo]''. Relative Adresse verhindern eine Mischung unterschiedlicher Zugriffsformen (ssh/https) und ggf. damit extra Abfragen von Zugangsdaten((siehe auch [[https://docs.gitlab.com/ee/ci/git_submodules.html|die gitlab-Doku]] dazu)). [[https://github.com/ansible/awx/issues/594|awx kommt mit einer Mischung nicht klar]].
* Gefolgt von ''git commit -a'' (und ggf. ''git push'')
=== benutzen ===
Der Clone-Befehl erweitert sich dadurch: git clone --recurse-submodules URL
Sub-Module aktualisieren: git submodule update --init --recursive
bzw. im Verzeichnis des Submoduls: git pull origin master
Links: https://www.git-tower.com/learn/git/ebook/en/command-line/advanced-topics/submodules#start
=== Submodule entfernen ===
''git submodule deinit path/to/submodule
git rm path/to/submodule''
=== workflow Spiegelung entfernter Repos ===
Für den Fall das ein entferntes Repo gespiegelt werden soll, aber entfernte Änderungen nochmal überprüft werden sollen:
* Repo auf Server anlegen
* clonen nach Lokal
* Lokales Repo bekommt upstream: ''git remote add upstream https://github.com/REPO''
* Lokales Repo abgleichen mit entfernt: ''git pull upstream master''
* Lokales Repo mit eigenem Server syncronisieren: ''git push origin master''
==== Branches ====
* **Branches** (master ist der standard-branch; aktiver Branch mit "*" gekennzeichnet)
* Liste der Branches ausgeben: git branch
* **neuen** Branch anlegen: git branch BRANCH_NAME
* **Wechsel** in (diesen) neuen Branch: git checkout BRANCH_NAME
* **zurückführen** in master:
* zuerst zurück in master wechseln: git checkout master
* Änderung aus BRANCH_NAME in master mergen: git pull . BRANCH_NAME
* neuen Branch auch in entfernten Repos anlegen: git push -u origin BRANCH_NAME
* Branch **lokal löschen**: git branch -d BRANCH_NAME
((forcen wenn nicht alles committed wurde: git branch -D BRANCH_NAME
))
* Branch **auf Server löschen**: git push REMOTE_NAME :SERVER_BRANCH_NAME
* Branch umbenennen((sollte nicht allzu leichtfertig gemacht werden, Link: https://stackoverflow.com/questions/6591213/how-do-i-rename-a-local-git-branch)): git branch -m ALT NEU
* **lokale Branches aufräumen**, wenn bereits auf Server gelöscht: git remote prune [SERVER_BRANCH_NAME]
oder besser: verbindet sich vorher nochmal mit dem entfernten repo und aktualisiert die branch-liste:git fetch -p
* in welchem branch ist der commit ID?: git branch -r --contains
* commits in myBRANCH zu einem commit zusammenfügen ("squashen"):git checkout master
git merge --no-commit --squash $myBRANCH
git commit
* Änderungen auf HEAD losgelöst von $CommitID ("detached HEAD")
* git push origin HEAD:master
* git checkout master && git merge [ref of HEAD]
==== gemeinsamer Zugriff ====
* **Mehrbenutzerzugang** (zentral erreichbarer Datei-Speicher)
* Benutzerberechtigungen in Datei "allowed-users" in Verzeichnis: ''.git/info/''
*
* **Patches** austauschen (geht auch per eMail die sich als mbox-Datei wieder einzupflegen gehen); Beispiel: alle Änderungen als Patch die NICHT in "original" enthalten sind: git format-patch original
==== workflow-Beispiel merge-request mit gitlab ====
Ablauf: auschecken, eigenen branch anlegen, committen, pushen, merge-request
* ''git clone REPOADRESSE'' ((ggf. mit submodulen: --recurse-submodules))
* ''git checkout -b Zweck'' (branch "Zweck" anlegen)
* ''git commit -a -m Änderungsmitteilung''
* ''git push origin Zweck'' (branch "Zweck" wieder auf den ursprünglichen branch, z.B. master, zurückübertragen)
* => Bei Erfolg gibt es einen Meldung mit dem Deeplink für den merge-request zurück
===== Einrichtung Gitlab =====
siehe Seite von [[software:gitlab]].
===== Einrichtung Apache + Git und webdav (Debian) =====
FIXME Diese Sektion ist wahrscheinlich veraltet und nicht mehr sinnvoll.
Lieder gibt es für git und [[Apache:Apache]] kein vergleichbares Modul wie für [[versionsverwaltung:Subversion]]. Somit hat diese Zugangsmethode folgende **Nachteile**:
- es können keine detaillierten Rechte für die unterschiedlichen Benutzer gesetzt werden
- git-hooks werden nicht ausgeführt
==== Links ====
* [[http://www.kernel.org/pub/software/scm/git/docs/howto/setup-git-server-over-http.txt|englische Anleitung]]
==== Server konfigurieren ====
- Git installieren (als root bzw. mit sudo): aptitude install git-core
- Verzeichnis für das Repository anlegen: mkdir /srv/git/git-repos
- optional:
- Gruppe für git-Benutzer erstellen: groupadd gitusers
- Benutzer hinzufügen: addgroup [USER] gitusers
- Leeres git-Repository erzeugen: cd /srv/git/git-repos/REPO1 && git --bare init
- Verzeichnis-/Dateirechte erteilen, Beispiel mit [[linux:rechte#umask]] 027 (Gruppe ggf. von gitusersändern falls sie oben nicht angelegt wurde): chmod 770 /srv/git/git-repos && chown -R www-data:gitusers /srv/git/git-repos/* && chmod g+wX /srv/git/git-repos/*
- Module für WebDAV aktivieren: ''a2enmod dav dav_fs''
- Apache2 konfigurieren: in /etc/apache2/sites-available einen virtuellen Host konfigurieren und zusätzlich für die Git-Location folgendes konfigurieren:
DAV on
AuthType Basic
AuthName "Git Repository - consider using ssl to protect your credentials"
#AuthBasicProvider file
# (is default anyway; needs mod_authn_file which is loaded)
AuthUserFile /etc/apache2/git_passwd
Require valid-user
#for inital tests:
#Allow from all
- Zugangsdaten in ''/etc/apache2/git_passwd'' hinterlegen: htpasswd -c /etc/apache2/git_passwd USER
- Syntax überprüfen: apache2 -t
- Apache2 neustarten: apache2ctl restart
ODER /etc/init.d/apache2 restart
===== Fehlerbehebung =====
==== src refspec master does not match any ====
Dieser Fehler tritt bei ersten push auf, wenn lokal noch gar kein master-branch existiert. Lösung: Den ersten commit machen und dann den push.
==== SSL-Verifizierung schlägt fehl (return code 60) ====
Ein push schlägt mit folgender Fehlermeldung fehl:
error: Cannot access URL https://user@host.old/repo/, return code 60
error: failed to push some refs to 'https://user@host.old/repo/'
Der Grund ist eine fehlgeschlagene Verifizierung des [[netzwerke:ssl-und-tls|SSL/TLS]]-Zertifikats u. a. in diesen Fällen:
- man hat ein selbst-signiertes Zertifikat erstellt bzw.
- eine [[netzwerke:eigene ca für ssl|eigene CA]] aufgesetzt hat, die dem System unbekannt ist.
**Temporäre Lösung** (für selbst-signierte Zertifikate)
- in der Datei ~./bashrc folgendes eintragen export GIT_SSL_NO_VERIFY=true
- oder in ''~/.gitconfig'' http.sslVerify=false
**Permanente Lösung**
- [[linux:linux]]: eigene CA im System anmelden
- [[windows:Windows]]: FIXME Lösung bei [[http://code.google.com/p/msysgit/|msysgit]]?
* http.sslCAInfo: File containing the certificates to verify the peer with when fetching or pushing over HTTPS. Can be overridden by the GIT_SSL_CAINFO environment variable.
* http.sslCAPath: Path containing files with the CA certificates to verify the peer with when fetching or pushing over HTTPS. Can be overridden by the GIT_SSL_CAPATH environment variable.