versionsverwaltung:git

Git

Git ist eine freie Software zur 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.

Quelle

selbst-gehostet:

cloud-hosting:

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.

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

Lokales Repo neu anlegen (Unterordner .git wird erstellt):

git init

Änderungen in anderes Repo übertragen (git-push)

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

Entfernte Repositories werden über diese Wege angesprochen (hier Beispiel mit clone):

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
cd Ordner_ohne_git_versionierung
git init
git remote add origin $ADRESSE
git add .
git commit -m "Initial commit"
git push -u origin master
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
  • 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
  • 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: Guides: Ignore for Git

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
  • 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 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 <john@doe.org>"
    • 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 verwerfen1):
    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 How can I change the author name / email of a commit?.

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.

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

2)

bestimmten Tag übertragen:

git push origin <tag_name>

3)

Ä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 git log to return only the commits made to the master branch?.

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 \;

du brauchst serverseitig nur die Unterstützung. Im Repo musst du das mit dem client machen:

  1. git-lfs lokal installieren (bei ubuntu ein extra Paket)
  2. repo clonen
  3. cd repo
  4. git lfs install
  5. git lfs track *.iso (behandelt als iso-Dateien mit lfs)
  6. → legt .gitattributes an mit Inhalt: *.iso filter=lfs diff=lfs merge=lfs -text
  7. git add .gitattributes
  8. git commit .gitattributes

https://www.atlassian.com/git/tutorials/git-lfs

  • 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 (zlib-komprimiert; mit SHA-1 Hash ansprechbar)
    • Commit - bestimmter Änderungszeitpunkt, besteht aus diesen Elementen:
      1. Kommentar (auch „Commit-message“)
      2. Information über Zeit und Autor
      3. Verweise auf (beliebig viele) übergeordnete Commits („parents“)
      4. 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.

Git kann andere Repositories referenzieren (diese als Submodule einbinden). Dokumentation zu Submodulen.

anlegen

  • Entfernte Repos: git submodule add URL [lokaler_Pfad_im_Repo] 4)
  • 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 Zugangsdaten5). 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 (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_NAMEg
    • Branch lokal löschen:
      git branch -d BRANCH_NAME

      6)

    • Branch auf Server löschen:
      git push REMOTE_NAME :SERVER_BRANCH_NAME
    • Branch umbenennen7):
      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 <commit>
    • 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]
  • 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

Ablauf: auschecken, eigenen branch anlegen, committen, pushen, merge-request

  • git clone REPOADRESSE 8)
  • 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

siehe Seite von gitlab.

FIXME Diese Sektion ist wahrscheinlich veraltet und nicht mehr sinnvoll.

Lieder gibt es für git und Apache kein vergleichbares Modul wie für Subversion. Somit hat diese Zugangsmethode folgende Nachteile:

  1. es können keine detaillierten Rechte für die unterschiedlichen Benutzer gesetzt werden
  2. git-hooks werden nicht ausgeführt
  1. Git installieren (als root bzw. mit sudo):
    aptitude install git-core
  2. Verzeichnis für das Repository anlegen:
    mkdir /srv/git/git-repos
  3. optional:
    1. Gruppe für git-Benutzer erstellen:
      groupadd gitusers
    2. Benutzer hinzufügen:
      addgroup [USER] gitusers
  4. Leeres git-Repository erzeugen:
    cd /srv/git/git-repos/REPO1 && git --bare init
  5. Verzeichnis-/Dateirechte erteilen, Beispiel mit 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/*
  6. Module für WebDAV aktivieren: a2enmod dav dav_fs
  7. Apache2 konfigurieren: in /etc/apache2/sites-available einen virtuellen Host konfigurieren und zusätzlich für die Git-Location folgendes konfigurieren:
    	<Location /git-repos >
    	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
    	</Location>
  8. Zugangsdaten in /etc/apache2/git_passwd hinterlegen:
    htpasswd -c /etc/apache2/git_passwd USER
  9. Syntax überprüfen:
    apache2 -t
  10. Apache2 neustarten:
    apache2ctl restart

    ODER

    /etc/init.d/apache2 restart

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.

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 SSL/TLS-Zertifikats u. a. in diesen Fällen:

  1. man hat ein selbst-signiertes Zertifikat erstellt bzw.
  2. eine eigene CA aufgesetzt hat, die dem System unbekannt ist.

Temporäre Lösung (für selbst-signierte Zertifikate)

  1. in der Datei ~./bashrc folgendes eintragen
    export GIT_SSL_NO_VERIFY=true
  2. oder in ~/.gitconfig
    http.sslVerify=false 

Permanente Lösung

  1. linux: eigene CA im System anmelden
  2. Windows: FIXME Lösung bei 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.

1)
alternativ: temporäres Wegräumen geänderter Dateien mit git stash
2)
followTags = true in der [push]-Sektion von ~/.gitconfig
3)
push all tags at once (not recommended!):
git push --tags
4)
standard wäre der Reponame
5)
siehe auch die gitlab-Doku dazu
6)
forcen wenn nicht alles committed wurde:
git branch -D BRANCH_NAME
7)
sollte nicht allzu leichtfertig gemacht werden, Link: https://stackoverflow.com/questions/6591213/how-do-i-rename-a-local-git-branch
8)
ggf. mit submodulen: –recurse-submodules