Git Fallstudie

Letzte Änderung: 26.06.2012 - 15:55 Uhr

Diese Fallstudie wurde unter Ubuntu 10.10 mit git in der Version v1.7.5.1 getestet. Alle hier genannte Kommandos werden in der Bash/dem Terminal/der Konsole ausgeführt. Es gibt auch Tools wie tortoisegit für Windows, auf die hier nicht weiter eingegangen wird.

Diese Seite ist eine Sammlung von git Kommandos und git Workflows.

1 Installation

1.1 Debian/Ubuntu

apt-get install git-core

1.2 Windows

msysgit oder tortoisegit, ggf. mit Cygwin.

1.3 Testen

Eine funktionierende Installation kann einfach getestet werden:

git version

2 Konfiguration

Man kann git global (mit --global) für alle git-Projekte und lokal (ohne --global) je git-Projekt konfigurieren. Zweiteres ist bei Projekten von github.com hilfreich.

2.1 Benutzer

git config --global user.name "Dennis Boldt"
git config --global user.email spam@dennis-boldt.de

2.2 Farben

git config --global color.diff true
git config --global color.ui true
git config --global color.status true

2.3 Zeilenumbrüche

git config --global core.autocrlf input

2.4 Konfiguration anzeigen

git config -l
git config --list

Es kann zum Folgenden Fehler kommen:

bash: git-upload-pack: command not found
fatal: The remote end hung up unexpectedly

Das Problem - auf Remote/Server-Seite - habe ich wie folgt gelöst:

$ cd /usr/bin/
$ sudo ln -s /[path/to/git]/bin/git* .

2.5 Standard-Editor zum Schreiben der Commit-Nachrichten

git config --global core.editor "nano"
git config core.editor "nano"

Man kann sich auch die von git erstelle globale Konfiguration anzeigen lassen

cat ~/.gitconfig

Die projektspezifische Konfiguration findet sich unter

cat .git/config

3 Git Repository anlegen

mkdir ~/git/fallstudie
cd ~/git/fallstudie
git init --bare --share                   // Initialized empty shared Git repository in ~/git/fallstudie/

In einem existierendem Projekt (.git-Ordner wird erstellt)

git init --share
git add .

4 Git Repository clonen

cd ~/Desktop/
git clone ~/git/fallstudie                // warning: You appear to have cloned an empty repository.
git clone git://../repo.git
git clone ssh://../repo.git
cd fallstudie/

5 Eine neue Datei zum "lokalen" Repository hinzufügen

touch README                              // Datei erstellen
nano README                               // Inhalt: "Das ist ein Test."
git status                                // Untracked files: README
git add README                            // Commit vorbereiten/Datei zum Index/Stage hinzufügen
git status                                // Changes to be committed: new file:   README
git commit                                // 1 files changed, 1 insertions(+), 0 deletions(-)
git status                                // nothing to commit (working directory clean)

6 Änderungen zum Remote-Repository hinzufügen

Der initiale Commit sieht wie folgt aus:

git push origin master                    // [new branch]      master -> master
Danach sollte noch die Datei HEAD wir folgt (hier als Linux-Kommando) angelegt werden (Nur nach dem initalien Commit!):
echo "ref: refs/remotes/origin/master" > .git/refs/remotes/origin/HEAD

oder

git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/master

7 Eine bekannte Datei ändern

nano README                               // Inhalt: "Das ist ein schöner Test."
git status                                // modified:   README
git diff                                  // -Das ist ein Test.
                                          // +Das ist ein schöner Test.
git add README                            // Commit vorbereiten/Datei zum Index/Stage hinzufügen
git status                                // modified:   README
git commit                                // 1 files changed, 1 insertions(+), 1 deletions(-)
git status                                // nothing to commit (working directory clean)

git push                                  // 5ae2478..083fd93  master -> master

8 Die Commit Historie anzeigen

git log
git log -1                                // -5
git log -p                                // Zeigt alle Änderungen
git log --stat                            // Zeigt die Statistik der Änderungen
git log --pretty=oneline
git log --reverse

Es können beliebige Kombinationen genutzt werden. Zum Beispiel:

git log --pretty=oneline --stat

9 Ein anderer Benutzer

cd ~/Destkop/
git clone ~/git/fallstudie ./test
cd test/
git status                                // nothing to commit (working directory clean)
git log                                   // Wir sollten nun alle bisherigen Änderungen sehen.

9.1 Benutzer lokal konfigurieren

Dies ist auch für github.com notwendig.

git config user.name "Hans Meier"        // Achtung: OHNE --global
git config user.email hans@meier.de

cat .git/config                          // Überprüfen, ob der neue User stimmt.

9.2 Eine neue Datei hinzufügen

nano TEST                                // "Ein neuer Test."
git status                               // Untracked files: TEST 
git add TEST
git commit
nano TEST                                // einige Änderungen.                              
git add TEST
git commit
git status                               // nothing to commit (working directory clean)                            
git push                                 // 083fd93..388d171  master -> master

10 Änderungen aus dem globalen Repository holen

Zurück zum ersten geklonten Verzeichnis "fallstudie"

cd ../fallstudie/
git fetch                                // 083fd93..388d171  master     -> origin/master
git rebase origin                        // First, rewinding head to replay your work on top of it...
                                         // Fast-forwarded master to origin/master.

Hinweis: In der Regel reicht (nachdem anpassen von HEAD unter 6):

git rebase origin

Falls HEAD nicht richtig gesetzt ist, muss das rebase wie folgt gemacht werden:

git rebase origin/master

1. Workflow: Normales arbeiten mit rebase

// Working,working,working
touch FILE
git add FILE
// Working,working,working      
git commit
...
git commit
git fetch
git rebase origin
git push   

2. Workflow: Normales arbeiten mit merge

// Working,working,working
touch FILE
git add FILE
// Working,working,working      
git commit
...
git commit
git fetch
git merge origin
git push   

Unterschied zwischen merge und rebase

git rebase scheibt die Historie neu, git merge lässt die History und vereint Branches. Vergleiche diesen Link.

Es gibt auch noch git pull welches das locake Repository gleich den Remote-Repository setz:

git pull == git fetch && git merge origin
git pull --rebase == git fetch && git rebase origin

11 Ein Konflikt

Wir befinden uns noch immer in "fallstudie"

nano README                               // "Das ist ein wunderschöner Test."
git commit                                // modified:   README
git add README
git commit
git status                                // nothing to commit (working directory clean)
git push                                  // 388d171..6ebc80e  master -> master

Zurück zu "test"

cd ../test/
nano README                               // "Das ist ein toller Test."
git add README
git commit

An dieser Stelle haben wir nun zwei verschiedene Versionen. Die vom Benuter "Hans Meier" mit dem Inhalt "Das ist ein wunderschöner Test." im Remote-Repository und die vom Benutzer "Dennis Boldt" mit dem Inhalt "Das ist ein schöner Test." im lokalen Repository. Wird nun versucht die lokale Version ins Remote-Repository zu pushen (git push), kommt es zu dem folgenden Fehler:

! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to '/home/dennis/git/fallstudie'
...
Merge the remote changes before pushing again.

Dies sagt nichts anderes, als das im Repository eine neuere Version liegt, welche wir abholen müssen.

git fetch                                // 388d171..6ebc80e  master     -> origin/master
git rebase origin                        // CONFLICT (content): Merge conflict in README

3. Workflow: Einen Konflikt lösen

git status                               //     both modified:      README 
vi README                                // "Das ist ein toller und wunderschöner Test."

git add README                           // Vereinigte Datei zu Index/Stage hinzufügen 
// KEIN COMMIT HIER (in anderen Tutorials steht hier ein Commit. Das ist falsch)
git status                               // modified:   README
git rebase --continue                    // Applying: Ein wunderschöner Test
     oder zum Abbrechen
git rebase --abort
git push                                 // 6ebc80e..2245a77  master -> master

12 Branches & Merges

Wir befinden uns noch immer in "test"

git branch test                          // Branch mit dem Namen "test" erstellen
git branch                               // Alle Branches anzeigen
                                         // * master
                                         //   test
git checkout test                        // Switched to branch 'test'
git branch                               // Alle Branches anzeigen
                                         //   master
                                         // * test 
nano TEST                                // "Ein spannender Test!"
git add TEST
git commit                               // [test 51ee4c1] Ein branch test
                                         // 1 files changed, 1 insertions(+), 1 deletions(-)
git checkout master                      // Switched to branch 'master'
git merge --no-ff test                   // Merge made by recursive.
                                         // Tipp: IMMER --no-ff benutzen. Warum sehen wir im nächten Abschnitt
git push                                 // 2245a77..bb71fd5  master -> master
git branch -d test                       // Deleted branch test (was 99fd86d)
git show-branch

13 More fancy Logs

Alle Branches & Merges kann man sich sehr schön mit dem zusatz --graph anzeigen lassen.

git log --graph
git log --graph --oneline
git log --graph --full-history --all --color --date=short --pretty=format:"%x1b[31m%h%x09%x1b[32m%d%x1b[0m%x20%ad %s"

git shortlog
git shortlog -n -s

git instaweb --httpd=webrick             // Git im Browser  

14 gitk

Das ultinative Werkzeug für git ist gitk.

15 Rückgängig machen

Ein Hinweis vorweg: Mit git reset vorsichtig sein. Insbesondere die Optionen --soft und --hard funktionieren sehr unterschiedlich.

Wir erzeugen etwas Müll...

echo "test" > README

15.1 Alle Änderungen wegwerfen

git checkout -f
git reset --hard
git reset --hard HEAD
git checkout FILE

15.2 Änderungen an einer einzelnen Datei verwerfen

rm filename                              // I.d.R. nicht notwendig
git checkout -- filename                 // Geht i.d.R. auch ohne die --, Problematisch wenn eine Datei 
                                         // den gleichen Namen wir ein branch hat.

15.3 Die letzte Commit-Nachricht ändern

Solange der Commit noch nicht im Remote-Repository ist, kann die letzte Commit-Nachricht verändert werden.

git commit --amend

16 Den letzten Commit Rückgängig machen

Solange der Commit noch nicht im Remote-Repository ist, kann der letzte Commit zurückgeholt werden. Im Folgenden bleiben die Änderungen im Working-Tree vorhanden:

git reset --soft HEAD^

HEAD^ steht für den Commit vor HEAD. Wenn man auch die Änderungen verwerfen will, nutzt man:

git reset --hard HEAD^

17 Etwas beim letzen Commit vergessen

1. Version

git reset --soft HEAD^
git add forgot.txt
git commit

2. Version

git commit -m 'initial commit'
git add forgot.txt
git commit --amend

18 Stash

Stash ist etwas besonderes und sehr hilfreiches in git. Änderungen, welche sich noch nicht im Index/Stage befinden können zur Seite gepackt werden, ohne diese zu verlieren. Dies ist dann nützlich, wenn man gerade an einer Datei arbeitet, während dessen aber einem anderen Bug korrigieren muss. Somit packt man die aktuellen Änderungen zur Seite und kann erstmal etwas anderes machen. Außerdem kann man einen Branch erstellen, ohne die Änderungen commiten zu müssen.

git stash
git stash list
git stash pop
git stash apply  
git stash clear
git stash branch <branchname>            // Seit git 1.6

4. Workflow: Stash

git stash
[fix the bug]
git commit -a -m "bug is now fixed"
git stash pop

5. Workflow: Normales arbeiten mit Stash

// Working,working,working
touch FILE
git add FILE
// Working,working,working
git commit
...
git commit
git stash
git fetch
git rebase origin
git stash apply
git push 

19 Tags

Ein Tag in git ist nur ein anderer Name für einen bestimmten Commit.

git tag "v1.3"                           // Die aktuelle Version als v1.3 bennen. 
git push --tags

Tags anzeigen lassen:

git fetch --tags
git show v1.3
git tag -l
git tag -l 'v1*'

Tags löschenn:

git -d v1.3

20 Aliase

Es ist hilfreich, sich für komplexere Kommandos Aliase anzulegen. Einige Beispiele:

git config --global alias.st status
git config --global alias.unstage 'reset HEAD --'    // git unstage aFile == git reset HEAD aFile
git config --global alias.last 'log -1 HEAD'

21 Einen Patch erstellen

Manchmal ist es notwendig Änderungen mit jemanden auszutauschen, welche im Remote-Repository nichts zu suchen haben, oder weil man einfach keinen Schreibzugriff hat. In diesem Fall kann man Patches der letzten Commits erstellen:

git format-patch origin
git am PATCHFILE                         // Genau ein Patch hinzufügen  
git am *.patch                           // Alle Patches hinzufügen
git am *.patch -i                        // Alle Patches interaktiv hinzufügen   

22 Trailing whitespaces

Es kommt vor, dass man an einigen Zeilen des Commits sogenannte trailing whitspaces hat. Diese kann man mittels eines pre-commit hook entfernen. Dafür erstellt man die Datei .git/hooks/pre-commit und füllt sie wie folgt:

#!/bin/sh

if git-rev-parse --verify HEAD >/dev/null 2>&1 ; then
  against=HEAD
else
  # Initial commit: diff against an empty tree object
  against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi
# Find files with trailing whitespace
for FILE in `exec git diff-index --check --cached $against -- | sed '/^[+-]/d' | sed -r 's/:[0-9]+:.*//' | uniq` ; do
  # Fix them!
  sed -i 's/[[:space:]]*$//' "$FILE"
done
exit

23 Einige Kommandos kurz erwähnt

git add -u                               // Alle geänderten Dateien zum Index/Stage hinzufügen
git commit -a -m "all"                   // Schneller commit aller geänderten Dateien 
git blame FILE                           // Zeigt, welche Zeile von welchem Autor verändert wurde
git mv file_from file_to                 // Eine Datei Verschieben/Umbenennen
git rm FILE                              // Eine Datei löschen 
cat .gitignore                           // Eine Liste von zu ignorierenden Dateien/Orndern, z.B. ./bin
git reset HEAD cake.txt                  // Eine Datei aus dem Index/Stage holen

24 ...

...

25 Weitere Informationen