- Basiskonzepte
 - Basisbefehle
 - Branches
 - Änderungen zwischenspeichern
 - Änderungen rückgängig machen
 - Inhalte löschen
 - Git under the hood
 - Git Ignore
 - Git Config
 - GitHub
 - Pull requests
 - Git Tags
 - Loeschen alter Branches
 - Git Squash
 - ToDos
 - Backup
 
- Working Tree --> Staging area --> (Local) Git Repository --> (Upstream / Remote) Git Repository
 - Git file lifecycle: Untracked --> Modified --> Staged --> Unmodified
 
Übersicht Git Repository / Status / Datei zum commiten etc.
git status
Übertragung bestimmter Änderungen aus dem Working Tree in die Staging Area
git add <filename>
Übertragung aller Änderungen aus dem Working Tree in die Staging Area
git add .
Löschen bestimmter Änderungen aus der Staging Area
git rm --cached <filename>
Übertragung von Änderungen aus der Staging Area in das Local Repository
git commit -m  "<Nachricht>"
Stage & Commit in einem Befehl
git commit -am "<Nachricht>"
Ändern der letzten Commit Nachricht (der letzte Commit wird durch einen neuen Commit ersetzt)
git commit --ammend -m "<Nachricht>"
Ändern des letzten Commit
git add Datei1.txt
git commit -m "Änderungen an Datei1.txt & Datei2.txt"
# Das Hinzufügen der Datei2.txt zur Staging Area wurde vergessen, damit ist die Datei auch nicht im Local Repository
git add Datei2.txt
git commit --amend --no-edit
# In der Staging Area  liegt nun die Datei2.txt. Der 'amend' Befehlt überführt diese in das Local Repository, so dass der Commit Datei1.txt & Datei2.txt umfasst, also geändert wurde. Die Commit Nachricht selbst wurde nicht geändert.Textbasierte Übersicht
git log
Textbasierte Übersicht des letzten Commit in einer Zeile
git log --oneline
Textbasierte Übersicht aller vergangenen Commit in einer Zeile
git log --pretty=oneline
Übersicht mit grafischer Ansicht (Verlinkung der jeweiligen parrent trees)
git log --graph
Übersicht der letzten Änderungen an einer Datei
git log <file> oder git blame <file>
Besser:
git log --pretty=format:'%h %an %ad %s' merge.txt
Anzeigen der Dateien die in einem commit geändert wurden
git show --stat <commit-hash>
Anzeigen aller Änderungen in einem commit
git show <commit-hash>
Detailansicht aller Änderungen die in einem commit geändert wurden
git show -p <commit-hash>
Detailansicht aller Änderungen in einer spezifischen Datei die in einem commit geändert wurden
git show -p <commit-hash> -- <path to file>
Nur lokale branches
git branch oder git branch -l
Nur remote branches
git branch -r
Alle branches
git branch -a
Anzeigen der upstream / remote (tracking) branches meiner local branches
git branch -vv
Kombination zum anzeigen aller local / remote (tracking) branches und deren upstreams
git branch -avv
Formatierte Liste aller lokalen Branches mit detaillierten Informationen
git for-each-ref --sort=committerdate refs/heads/ --format '%(HEAD) %(align:35)%(color:blue)%(refname:short)%(color:reset)%(end) - %(color:red)%(objectname:short)%(color:reset) - %(align:40)%(contents:subject)%(end) - %(authorname) (%(color:green)%(committerdate:relative)%(color:reset))'
Einen neuen local branch erstellen
Dies funktioniert auch wenn der branch nur remote existiert. In diesem Fall wird der neu erzeugte local branch automatisch zu einem tracking branch des remote branches
git branch <branch name>
Einen neuen local branch erstellen und sofort in diesen wechseln
git checkout -b <branch name>
Wechseln in einen bestehenden branch
git checkout <branch name | hash commit (frist 6 chars)>
   Example: Checkout of a dedicated hash commit in 'main' branch
   ========
   # Initial state
   (C1)--> (C2)-->(C3)-->(C4)-->(C5) <-- [HEAD, main]
    
   # Checkout hash commit
   git checkout c2
   # Final state (detachted HEAD state)
   (C1)--> (C2)-->(C3)-->(C4)-->(C5) <-- [main]
            |
          [HEAD]
Den aktuellen local branch umbenennen
git branch -M <name>
Einen anderen local branch umbennen
git branch -m <old name> <new name>
Von Git verwendente merge Konzepte:
- Fast-forward merge: main / HEAD pointer werden auf letzten commit des feature branch umgeleitet. Dies geht nur, wenn nach Erzeugung des feature branch nur in diesem commits durchgeführten wurden und keine weiteren commits im main branch erfolgten
 - 3-way" merge: Neuer commit im main branch wird erzeugt, der auf den letzten commit des main branch und den letzten commit des Feature commits zeigt. Der neue commit im main branch hat damit also zwei parrents. Dieses Konzept wird gewählt, wenn nach der Erstellung des feature branch ein / mehrere commit im feature branch und im main branch erfolgten
 
Mergen des aktuellen branch mit einem feature branch
git merge <feature branch name>
- Nutzen von 
git status, um die Dateien in den Konflikte entstandenen zu identifizieren - Verwenden von 
cat <file>, um die Konflikte in den einzelnen Dateien zu identifizieren. Typischerweise sollte dies in etwa wie folgt aussehen: 
<<<<<<< HEAD
<content that exists in the current branch>
=======
<content that is present in our merging branch>
>>>>>>> <name of merging branch>
- Lösen den Konflikts im Editor der Wahl, z.B. 
vimoderVS Code git commitausführen, um die Änderungen zu bestätigen
HINWEIS: Sind alle merge conflicts gelöst, wird der merge automatisch fortgeführt
Rebasing ist eine Alternative zum branch merging, wobei 3-way merges durch fast-forward merges ersetzt werden. Wurde ein feature branch erzeugt und sowohl in diesem, als auch in main branch ein/mehrere commits ausgeführt, kann ein merge nur durch einen 3-way merge erfolgt. Durch rebasing wird der Startpunkt des feature branch auf den letzten commit im main branch verschoben. Somit zweigt der feature branch vom letzten commit im master branch ab und ein einfacher fast-forward merge ist möglich. Faktisch wird durch einen rebase die Historie eines repositorys neu geschrieben, da durch den rebase alle vergangenen commits des feature branches neu gesetzt werden, um eine lineare Historie zu erzeugen.
git checkout feature-1
git rebase main
git checkout main
git merge feature-1
git branch -d feature-1   Example: Rebasing a feature branch
   ========
   # Initial state
            (FB1)-->(FB2)-->(FB3) <-- [HEAD, feature-1]
             /
   (C1)--> (C2)-->(C3)-->(C4)-->(C5) <-- [main]
    
   # Execute command from above
   git checkout feature-1
   git rebase main
   # State
                                 (FB1)-->(FB2)-->(FB3) <-- [HEAD, feature-1]
                                  /
   (C1)--> (C2)-->(C3)-->(C4)-->(C5) <-- [main]
- 
Relevante commits herausfinden
git logbzw.git log -1für nur den letzen commit - 
Checkout des neuen branches
git checkout -b <new-branch> - 
Relevante commits auswählen
git cherry-pick <commit-hash1> <commit-hash2> ...oder als Bereichgit cherry-pick <commit-hash1>^...<commit-hashN> - 
Löschen der commits im alten branch:
git checkout <source-branch>git rebase -i <commit-hash>^oder falls nur der letzte commit betroffen istgit reset --hard HEAD~1
 
In manchen Situation kann es hilfreich sein durchgeführte Änderungen in einem branch zwischenzuspeichern, ohne diese direkt zu commiten. Hierzu dient git stash
Speichern von Änderungen im Working Tree und der Staging Area.
Es sind mehrere Zwischenspeicher möglich. Jeder Zwischenspeicher hat eine eindeutige ID:
stash@{#}. Wird bei Verwendung vongit stashkeine eindeutige ID angegeben, wird implizit immerstash@{0}angenommen
git stash
Aufruf von Änderungen aus dem Zwischenspeicher
git stash pop // Änderungen werden aus dem Zwischenspeicher gelöscht
git stash apply // Änderungen bleiben im Zwischenspeicher erhalten
Zwischenspeichern von untracked / ignored Dateien
git stash -u // für untracked Dateien
git stash -a // für untracke und ignorierte Dateien
Anzeigen aller Änderungen im Zwischenspeicher
git stash list
Anzeigen der Inhalte eines Zwischenspeichers
git stash show
# For more details
git stash show -pZwischenspeicher löschen
`git stash drop <stash id>`
# All stashes
git stash clearLöschen von Dateien die nur im Working Tree liegen und bisher nicht getrackt werden
# Dry-run
git clean -n
# Löschen nicht getrackter Dateien
git clean -f
# Löschen nicht getrackter Dateien & Verzeichnisse
git clean -dfLöschen von Änderungen an getrackten Dateien im Working Tree
# Löschen aller Änderungen
git restore .
# Löschen bestimmter Änderungen 
git restore <path-to-file>Reset entspricht einem "Zurück in die Vergangenheit". Alle Commits die vor dem genannten Rücksprung-Commit liegen, exisieren danach nicht mehr. Mit git reset können also ein oder mehrere Commits zurück genommen werden
Reset ist immer 'destruktiv', da es die Historie ändert. Insbesondere die Verwendung von hard sollte mit Bedacht gewählt werden.
Folgende Varianten werden unterschieden:
- 
hard: Alle Änderungen im Working Directory, der Staging Area und ggf. der Commit History werden zurück gesetzt
# Reset auf den HEAD löscht nur alle Änderungen im Working Tree und der Staging Area git reset --hard # HEAD wird implizit angenommen, alternativ z.B. "origin/main" # Reset auf einen spezifischen Commit in der Vergangenheit, alle Änderungen danach sind verloren git reset --hard "<commit ID (first 6 chars)>"
 - 
mixed: Alle Änderungen in der Staging Area und im Local Repository werden zurück gesetzt, Änderungen im Working Tree bleiben erhalten
--> Dies ist das "default" Verhalten bei Verwendung von
git reset# Default git reset # HEAD wird implizit angenommen # Reset auf eien spezifischen Commit in der Vergangenheit, alle Änderungen danach sind verlorten git reset --mixed "<commit ID (first 6 chars)>"
 - 
soft: Alle Änderungen im Local Repository werden zurück gesetzt, Änderungen im Working Tree und der Staging Area bleiben erhalten
# Default git reset --soft <HEAD (implizit) | <commit hash (first 6 chars) | HEAD~5 to reset last 5 commits> # Reset auf eien spezifischen Commit in der Vergangenheit, alle Änderungen danach sind verlorten
 
Mit revert kann die letzte vergangene Änderungen rückgängig gemacht werden. Anders als bei reset wird bei revert jedoch nicht "die Zeit zurück gedreht", sondern es wird ein neuer commit erzeugt, welcher die letzten Änderungen Rückgängig macht. Die Historie bleibt bei revert also erhalten und wird nicht gelöscht.
Die Verwendung von revert ist sinnvoll, wenn Änderungen bereits in ein remote repository eingeflossen sind und andere Entwickler ggf. bereits ein pull auf diese remote repository ausgeführt haben.
Revert ist nicht destruktiv, da die Historie bestehen bleibt und ermöglich es immer nur einen Commit zurück zu nehmen.
git revert <HEAD OR commit hash (first 6 chars)>
Löschen getrackter Inhalte im Working Tree und der Staging Area
git rm <name>
git rm -n <name>  # dry-run
git rm -r <folder>  # recursive deleteDie Löschung im local repository wird erst wirksam, wenn diese mit
git commitbestätigt wurde. Vorher kann eine Löschung durchgit checkout .odergit reset HEADrückgängig gemacht werden.
Löschen getrackter Inhalte nur der Staging Area, ohne Auswirkungen auf das Working Tree
git rm --cached <name>
Informationen zu Git objects
git cat-file -p <commit hash (first 6 chars)>  # Content of the object
git cat-file -t <commit hash (first 6 chars)>  # Type of the object
git cat-file -s <commit hash (first 6 chars)>  # Size of the objectInformationen zu Dateien in der Staging area
git ls-files -s
In der Datei .gitignore können untracked Dateien / Verzeichnise verwaltet werden. Die Datei .gitignore muss dazu selbst commited werden.
# Ignore file 1
file-1.txt
# Igorne all filed in bin folder
/bin
# Ignore all temp files
*.tmp
Es gibt drei verschiedene Git Config Ebenen:
- System (optional): 
gitEinstellungen für alle User des Systems (Pfad:$/etc/gitconfig) - Global: 
gitEinstellungen für alle repositiries des Users (Pfad:$USER_HOME/.gitconfig) - Local: 
gitEinstellungen für ein dediziertes repositiries eines Users (Pfad:<repo-home>/.gitconfig) 
Globale config auslesen
git config --list
Gobalen user name und eail setzen
git config --global user.name "<Name>"
git config --global user.email <E-Mail>
Den default name eine neuen Reporistories ändern
git config --global init.defaultBranch >main>
Lokale Einstellungen überschreiben immer die globalen Einstellungen in einem local reporistory
Lokale Config auslegen
cat .git/config
Lokalen user namen und eMail setzen
git config user.name "<Name>"
git config user.email <E-Mail>
[user]
    name=<YOUR-USER-NAME>
    email=<YOUR-USER-EMAIL>
    signingkey=~/.ssh/id_ed25519
[init] 
    defaultbranch=main
[gpg]
    format=ssh                               
[commit]
    gpgsign=true
Nach Erstellung eines Git Hub Repositories
Anzeige aller remote repository, denen im local reporistory gefolgt wird
git remote
Überprüfen der fetch / push URLs des remote repository
git remote -v
Details zum remote repository
git remote show <name of remote repository, z.b. origin>
Verknüpfen des local mit dem remote repository
git remote add <remote repository name, z.B. origin> <url des remote repository>
Ändern der Verknüpfung des local mit dem remote repository
git remote set-url origin <new-url>
Initiales klonen des remote repository nach local Per Default wird nur der default branch des remote repository zu einem tracking branch im local repository, alle weiteren branches werden nicht geklont
git clone <url>
Anzeige aller branches und deren letzten commit IDs
git show-ref
Vergleich der commits zweier branches zwischen local und remote repositories
git show-ref <branch name>
Laden aller branches und/or tags aus dem remote repository in das local repository Dies hat keine Auswirkung auf Working Tree / die Staging Area. Es erfolgt kein sync von Versionen / keine Erzeugung eines local (tracking) branches
git fetch
Nach dem fetch Befehl kann der remote branch ausgecheckt und alle Daten übertragen werden
git checkout <remote branch name>
Daten aus dem remote repository laden
git pull
Änderungen aus dem remote repository sollten regelmäßig in das local repository überspielt werden.
Git pull" führt implit zwei Schritte aus:
- git fetch: Übertragen aller Updates / Changes etc. des remote repository in das local repository als objects (blob, tree, commit), jedoch ohne Änderungen am local working directory / staging area
 - git merge (Basis: FETCH_HEAD) : 2- oder 3-way merge zwischen local repository und local working area & staging area. Dies betrifft alle tracking branches, beginnend beim aktuell ausgecheckten branch
 
Vor der Durchführung mittels git branch -vv prüfen, ob und welche local branches tracking branches sind
Aktualisierung des tracking status eines local repository branches, z.B. nachdem der zugehörige remote branch gelöscht wurde
git remote update <origin> --prune
Der local branch kann hiernach gelöscht werden
git branch -D <local branch name>
Löschen der Referenzen auf remote repository branches, den es im remote repository nicht mehr gibt
git remote prune <remote repository name, z.B. origin>
Übertragung aller Änderungen des aktuellen local repositroy branches in den bestehenden remote repository branch
git push
Erstellung eines neuen remote repositroy branches, basierend auf einem bestehenden local repository branch und Übertragung aller Daten in den remote repository branch
git push -u <remote repository name, z.B. origin> <local branch name>
Löschen eines remote branches
git push <remote repository name, z.B. origin> -d <branch name>
Hier nach kann der local branch gelöscht werden
git branch -d <local branch name>
git remote add origin <URL>
git push -u origin main
Mit pull requestes (PR), auch "Merge requests", bittet man andere Entwickler um ein Review der eigenen Änderungen im local repository branch, bevor diese in den remote repository branch eingespielt werden.
Git Tags sind Referenzen, die auf bestimmte Zeitpunkte im Git-Verlauf verweisen (Commits). In der Regel werden mit Tags bestimmte Punkte im Verlauf erfasst, die für einen markierten Versions-Release (z. B. v1.0.1) verwendet werden.
Es wird zwischen Lightweight und Annotated-Tags unterschieden. Annotated-Tags werden von Git wie Commits behandelt und sind daher zu bevorzugen.
Übersicht aller aktuell vorhandenen Tags
git tag
Erstellen eines git tags der auf den letzten Commit verweist
git tag -s -a <tag-version> -m "<message>"
Erstellen eines git tags der nachträglich auf einen bestimmten Commit verweist
git tag -s -a <tag-version> -m "<message>" <commit-id (first 6 chars)>
Git Tags müssen explizit in das remote repositiry gepusht werden, da sie nicht implizit durch git push übertragen werden.
git push <remote repository name, z.B. origin> <tag-version>
Anzeigen aller local branches, die bereits gemergt wurden
git branch --merged
Anzeigen aller local branches, die noch nicht gemergt wurden
git branch --no-merged
Anzeigen aller remote branches, die bereits in den local branch gemergt wurden
git branch -r --merged
Anzeigen aller remote branches, die noch nicht in den local branch gemergt wurden
git branch -r --no-merged
Löschen eines (bereits gemergten) local branches
git branch -d <name>
Löschen eines nicht nicht gemergeten branch (z.B. wenn der branch nicht mehr gebraucht wird)
git branch -D <name>
Löschen aller local branches außer main und development, die bereits gemergt wurden
git branch --merged | egrep -v "(^\*|main|development)" | xargs git branch -d
Anzeigen welche remote branches bereits gelöscht wurden und daher auch deren local branch gelöscht werden könnte
git remote prune origin --dry-run
Löschen der local branches, die remote bereits gelöscht wurden
git remote prune origin
Aktualisieren der lokalen Referenzen auf das Remote-Repository (origin) und entfernen der lokale Referenzen auf Remote-Branches, die im Remote-Repository nicht mehr existieren
git remote update origin --prune
Löschen aller local branches außer main und development, die bereits gemergt wurden
git branch -r --merged | egrep -v "(^\*|main|development)" | sed 's/origin\///' | xargs -n 1 git push origin -d
ERKLÄRUNG
git branch -r --mergedlistet all remote branches die bereits gelöscht wurden.egrep -v "(^\*|main|development)"exkludiert aus der Liste alle branches mit dem Namen main oder developmentsed 's/origin\///'filtert aus der vorheringen Liste alleorigin/Einträge herausxargs -n 1 git push origin -dlöscht alle verbleibenden branches
Löschen eines (bereits gemergten) remote branches
git push origin --delete <name>
Git Squash ist eine Technik, um mehrere aufeinanderfolgende Commits in einen einzigen Commit zusammenzufassen. Dies ist besonders nützlich, um die Git-Historie übersichtlich zu halten und zusammengehörige Änderungen in einem sauberen Commit zu bündeln.
# Beispiel: Die letzten 3 Commits zusammenfassen
git rebase -i HEAD~3Dies öffnet einen Editor mit einer Liste der letzten 3 Commits. Hier kann man "squash" oder "s" für die Commits verwenden, die mit dem vorherigen Commit zusammengefasst werden sollen:
pick abc123f First commit
squash def456g Second commit
squash ghi789h Third commit
# Ausgangssituation:
main: A -> B -> C (HEAD)
# Commit Historie:
C: "Add error handling"
B: "Fix typo in function name"
A: "Add new function"
# Nach dem Squash:
main: A -> D (HEAD)
# Neue Commit Historie:
D: "Add new function with error handling"
Best Practice: Squash sollte verwendet werden, bevor Änderungen in ein öffentliches Repository gepusht werden. Nach dem Push sollte kein Squash mehr durchgeführt werden, da dies die Historie für andere Entwickler ändern würde.
             (FB1)-->(FB2)-->(FB3)
             /                   \
   (C1)--> (C2)-->(C3)-->(C4)-->(C5)