Aufaben des Source-Code-Managments (SCM)
mit git bewältigen
Eine pragmatische Einarbeitung in git.
contributed by return42
Hit ‘?’ to see keyboard shortcuts / ‘s’ to view speaker notes
Kollaboration –> Konflikterkennung erforderlich
Qualitätsicherung –> seit wann gibt’s den Fehler
parallele Weiterentwicklung–> feature branch
Produktivität –> Änderungen jonglieren statt editieren
zentral
verteilt
Entwickler haben lokale Workspaces
Historie liegt auf dem SCM-Server
Patches gehen immer in das zentrale Repo
SCM-System limitiert den Workflow
Entwickler haben lokal einen Klon
Historie liegt auf jedem Klon vor
Anstelle EINES SCM-Servers gibt es N remote
Workflow frei wählbar
Git Extensions: https://gitextensions.github.io/
git identifiziert den Benutzer (Committer) über seine eMail-Adresse und seinen Namen:
$ git config --global user.name "Max Mustermann"
$ git config --global user.email "max.mustermann@muster.org"
pedantisch ..
$ git config --global --unset credential.helper
$ git config --global core.autocrlf false
$ git config --global core.symlinks true
aufrüschen ..
$ git config --global color.ui true
anpassen ..
$ git config --global push.default simple
$ git config --global core.editor emacsclient
nachlesen ..
im Workspace (WS)
im Stage (gibts beim SVN z.B. nicht)
im lokalem Repository (beim SVN nur remote)
$ git init
$ git add ...
$ git status ...
$ git add ...
$ git rm ...
$ git commit ...
$ git checkout ...
$ git log ...
$ git branch ...
$ git merge ...
Am Anfang war nichts … (Getting a Git Repository, git init)
$ mkdir git-teaching
$ cd git-teaching
$ git init
Initialized empty Git repository in git-teaching/.git/
Die erste Datei: README.txt
.. -*- coding: utf-8; mode: rst -*-
======
README
======
Nothing special here, only intended for teaching purposes.
$ git status
Auf branch master
Initialaler Commit
Unversionierte Dateien:
(benutzen Sie "git add <Datei>...", um die Änderungen\
zum Commit vorzumerken)
README.txt
README.txt~
nichts zum Commit vorgemerkt, aber es gibt unversionierte
Dateien (benutzen Sie "git add" zum Versionieren)
git status: aktueller Branch ist master
, Stage ist gerade leer,
vergleiche mit dem Diagramm
besser wir ignorieren README.txt~
.gitignore
: Pattern die ignoriert werden (nachlesen)
*~
*/#*#
.#*
*.pyc
*.pyo
$ git status
...
Untracked files:
...
.gitignore
README.txt
sieht schon besser aus :)
initial fügen wir einfach mal alles hinzu git add
$ git add --all ./
mal schauen wie der Status ist …
Auf branch master
Initialaler Commit
zum Commit vorgemerkte Änderungen:
(benutzen Sie "git rm --cached <Datei>..."
zum Entfernen aus der Staging-Area)
neue Datei: .gitignore
neue Datei: README.txt
schon im Repo? .. nein, nur im Stage Diagramm.
zuviel hinzugefügt? .. nimm es wieder aus dem Stage git rm
$ git rm --cached README.txt
rm 'README.txt'
Ups, hat er die jetzt etwa gelöscht?!?!
$ git status
...
zum Commit vorgemerkte Änderungen:
neue Datei: .gitignore
Unversionierte Dateien:
README.txt
nein, wurde nur aus dem Stage genommen Diagramm
So, jetzt ist der Patch aber fertig!
Alles was zum Patch gehört liegt im Stage, git commit
$ git commit -m "inital boilerplate"
[master (Basis-Commit) 849c175] inital boilerplate
1 file changed, 5 insertions(+)
create mode 100644 .gitignore
$ git status
Auf Branch master
Unversionierte Dateien:
(benutzen Sie "git add <Datei>...", um die Änderungen
zum Commit vorzumerken)
README.txt
Wie sieht so ein Patch im Repo eigentlich aus? git show
$ git show
commit 849c17589476d9451bc55659008cda5ac2aa68cb
Author: Max Mustermann <max.mustermann@muster.org>
Date: Mon Jul 31 14:59:40 2017 +0200
inital boilerplate
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..68c190d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+*~
+*/#*#
+.#*
+*.pyc
+*.pyo
jetzt mal die README.txt einchecken
$ git add README.txt
$ git commit -m "add README"
[master 9af1a51] add README
1 file changed, 7 insertions(+)
create mode 100644 README.txt
und mal das Log anschauen git log
$ git log
commit 9af1a518a77bc56dc697bee6ba1c356bb0c1b2f8
Author: Max Mustermann <max.mustermann@muster.org>
Date: Mon Jul 31 15:03:40 2017 +0200
add README
commit 849c17589476d9451bc55659008cda5ac2aa68cb
Author: Max Mustermann <max.mustermann@muster.org>
Date: Mon Jul 31 14:59:40 2017 +0200
inital boilerplate
Für die Branches eignen sich Graphen, aber auch das ist recht ausführlich. Man hätte da gerne was kompakteres (siehe Alias ‘git graph’ als two-liner).
$ git log --graph
Für faule Leute wie mich git st
& git unadd
$ git config --global alias.st "status"
$ git config --global alias.unadd "reset HEAD"
Alias ‘git graph’ als two-liner:
git config --global alias.graph "log --graph --abbrev-commit\
--decorate --format=format:'%C(bold blue)%h%C(reset)\
- %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)\
%C(bold yellow)%d%C(reset)%n''\
%C(white)%s%C(reset) %C(dim white)- %an%C(reset)'\
--all"
Bitte nicht genauer hinschauen, es gibt auch GUIs die so was visualisiern können. ;)
Initial gibt es den master branch
$ git branch -v
* master 9af1a51 add README
feature branch ‘hello-world’ anlegen git branch
$ git branch hello-world
$ git branch -v
hello-world 9af1a51 add README
* master 9af1a51 add README
git checkout um den branch auszuchecken
$ git checkout hello-world
Zu Branch 'hello-world' gewechselt
#!/usr/bin/env python
# -*- coding: utf-8; mode: python -*-
print("hello world")
Implementierung hello-world.py
testen
$ python hello-world.py
hello world
und einchecken
$ git add hello-world.py
$ git commit -m "add hello-world script"
[hello-world 0dd2abe] add hello-world script
1 file changed, 4 insertions(+)
create mode 100755 hello-world.py
$ git checkout master
Zu Branch 'master' gewechselt
Änderungen an der README.txt vornehmen ..
We created a 'hello-world' branch where one of our
provider implements an amazing 'hello world' program.
und einchecken
$ git add README.txt
$ git commit -m "add remark about 'hello world' order"
[master c1ce07c] add remark about 'hello world' order
1 file changed, 3 insertions(+)
$ git checkout hello-world
Zu Branch 'hello-world' gewechselt
Die README ist noch die alte (logisch)
$ cat README.txt
.. -*- coding: utf-8; mode: rst -*-
======
README
======
Nothing special here, only intended for teaching purposes.
Wir fügen ChangeLog Eintrag zur README hinzu:
ChangeLog:
2017-07-31 Max Mustermann <max.mustermann@muster.org>
* hello-world.py: inital implemented & tested
und im branch (hello-world) einchecken
$ git add README.txt
$ git commit -m "hello-world: add changelog"
[hello-world 8e448cd] hello-world: add changelog
1 file changed, 6 insertions(+)
Der feature branch hello-world
wird mit dem master
zusammengeführt.
$ git checkout master
Zu Branch 'master' gewechselt
Im master
wird jetzt der Merge durchgeführt git merge
$ git merge hello-world
automatischer Merge von README.txt
KONFLIKT (Inhalt): Merge-Konflikt in README.txt
Automatischer Merge fehlgeschlagen; beheben Sie die Konflikte
und committen Sie dann das Ergebnis.
Konflikte liegen in der Natur paralleler Entwicklung. Das SCM System hilft uns diese zu erkennen!
$ git status
Auf Branch master
Sie haben nicht zusammengeführte Pfade.
(beheben Sie die Konflikte und führen Sie "git commit" aus)
zum Commit vorgemerkte Änderungen:
neue Datei: hello-world.py
Nicht zusammengeführte Pfade:
(benutzen Sie "git add/rm <Datei>...",
um die Auflösung zu markieren)
von beiden geändert: README.txt
Mit hello-world.py
gab es keine Konflikte, wurde bereits ge-added.
Die README.txt
hatte einen Konflikt.
<<<<<<< HEAD
We created a 'hello-world' branch where one of our
provider implements an amazing 'hello world' program.
=======
ChangeLog:
2017-07-31 Max Mustermann <max.mustermann@muster.org>
* hello-world.py: inital implemented & tested
>>>>>>> hello-world
git hat die README.txt
zusammengeführt. Passagen die nicht automatisch
zusammengeführt werden können sind mit <<<<<<<
, =======
und
>>>>>>>
gekennzeichnet.
semantische Zusammenführung!
der alte Kommentar hat keine Gültigkeit mehr
Features:
* amazing 'hello world' program
ChangeLog:
2017-07-31 Max Mustermann <max.mustermann@muster.org>
* hello-world.py: inital implemented & tested
er wird durch einen komplett neuen Kommentar ersetzt.
git diff
@@@ -6,5 -6,8 +6,12 @@@ READM
Nothing special here, only intended for teaching purposes.
- We created a 'hello-world' branch where one of our
- provider implements an amazing 'hello world' program.
++Features:
++
++* amazing 'hello world' program
++
+ ChangeLog:
+
+ 2017-07-31 Max Mustermann <max.mustermann@muster.org>
+
+ * hello-world.py: inital implemented & tested
Implementierung testen …
$ python hello-world.py
hello world
und einchecken
$ git add README.txt
$ git commit -m "merge hello-world branch"
[master f5f3b62] merge hello-world branch
Mit git können Remotes verfolgt werden (track).
remote Branches (u. Tags) werden in das lokale Repository geklont.
Arbeiten mit remote Branches ist analog den lokalen Branches.
Es sind nur wenige zusätzliche Komandos erforderlich.
$ git clone ..
$ git remote ..
$ git fetch ..
$ git pull ..
$ git push ..
Meist beginnt es mit dem ersten Klonen git clone
$ git clone https://github.com/return42/git-teaching.git
Klone nach 'git-teaching' ...
remote: Counting objects: 18, done.
remote: Compressing objects: 100% (12/12), done.
remote: Total 18 (delta 4), reused 18 (delta 4), pack-reused 0
Entpacke Objekte: 100% (18/18), Fertig.
Prüfe Konnektivität ... Fertig.
was hat git clone
alles gemacht?
$ tree -L 1 -at git-teaching/
git-teaching/ # working tree
├── .git # repository
├── .gitignore
├── hello-world.py
└── README.txt
Repo wurde nach ./git-teaching/.git
geklont und
es wurde der Working-Tree git-teaching
ausgecheckt
$ git branch -vva
* master f5f3b62 [origin/master] merge hello-world
remotes/origin/HEAD -> origin/master
remotes/origin/master f5f3b62 merge hello-world branch
lokaler Branch master
wurde angelegt, er ist
ein Klon des Remote Branch origin/master
der lokale master
folgt (tracking) dem [origin/master]
$ git status
Auf Branch master
Ihr Branch ist auf dem selben Stand wie 'origin/master'.
nichts zu committen, Arbeitsverzeichnis unverändert
Der Alias-Name für das Original der Klon-Kopie ist origin
$ git remote -v
origin https://github.com/return42/git-teaching.git (fetch)
origin https://github.com/return42/git-teaching.git (push)
Die URL des origin
ist https://github.com/return42/git-teaching.git
Sie wird zum hochladen (push
) und zum runterladen (fetch
)
verwendet.
Zuvor Beschriebenes ist kompakt in der .git/config
zu finden, die git
clone
automatisch angelegt hat.
$ cat .git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[remote "origin"]
url = https://github.com/return42/git-teaching.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
$ git status
Auf Branch master
Ihr Branch ist vor 'origin/master' um 1 Commit.
(benutzen Sie "git push", um lokale Commits zu publizieren)
Änderungen müssen im lokalem Branch (Repository) commited werden. Erst
danach kann man sie mit git push zum Remote schieben. Der Alias
origin
wählt dabei den Remote.
$ git push origin master:master
refspec master:master
sagt: lokaler master
(links) auf Remote
master
(rechts) schieben. Lokaler master
folgt (tracking)
ohnehin schon dem [origin/master]
. Einfacher:
$ git push
Mit git fetch werden die remote Branches aktualisiert.
$ git fetch -v origin master
Von https://github.com/return42/git-teaching
* branch master -> FETCH_HEAD
= [aktuell] master -> origin/master
Der Remote ist wieder origin
. Die refspec master
gibt an,
dass der Fetch vom remote master
erfolgt.
Mit git pull kann man in einem Schritt fetchen und den FETCH_HEAD
gleich in den aktullen (lokalen) Branch mergen:
$ git pull
Mit git remote werden die Remotes verwaltet.
Alias origin
in upstream
umbenennen
$ git remote rename origin upstream
Einbinden eines Forks als origin
$ git remote add origin https://githost.intranet/git-teaching.git
Anzeigen der eingetragenen Remotes
$ git remote -v
origin https://githost.intranet/git-teaching.git (fetch)
origin https://githost.intranet/git-teaching.git (push)
upstream https://github.com/return42/git-teaching.git (fetch)
upstream https://github.com/return42/git-teaching.git (push)
Es können beliebig viele Remotes eingebunden werden, über den Alias kann man steuern mit welchem Remote man was machen will.
Obiges Beispiel ist typisch für ein Szenario bei dem der Fork eines
Projekts im Intranet origin
liegt und das Original des Forks im
Internet upstream
liegt. Mit git push origin
können die lokalen
Branches im Intranet abgelegt werden. Mit git push upstream
können die
Branches im Internet public gestellt werden.
Patches mit git format-patch und git am transportieren.
host_a$ git format-patch 9af1a51..hello-world \
-M -C --output-directory=patches
Patches von Commit 9af1a51
an (Abzweig hello-world
/ s.a.
git-graph), bis zum aktuellen Stand des hello-world
Branch werden im Ordner ./patches
angelegt.
host_b$ git checkout master
host_b$ git am --keep-cr ./patches/*
Merge der Patches aus Ordner ./patches
in den master
. Schalter
--keep-cr
erhält die CR (erforderlich bei Windows). Bei Konflikten muss man diese
auflösen (wie bereits bei git-merge
erläutert). Manchmal kann auch der Schalter --reject
hilfreich sein.