get git started

get git started

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

Aufgaben des SCM?

Kollaboration –> Konflikterkennung erforderlich

Qualitätsicherung –> seit wann gibt’s den Fehler

parallele Weiterentwicklung–> feature branch

Produktivität –> Änderungen jonglieren statt editieren

_images/Passing_2jugglers_6balls_Ultimate-Valse_side.gif

SCM Systeme

zentral

_images/svn-logo.png

verteilt

_images/git-logo.png

Zentrales SCM

Entwickler haben lokale Workspaces

Historie liegt auf dem SCM-Server

_images/zentralisiert-wf.png

Patches gehen immer in das zentrale Repo

SCM-System limitiert den Workflow

Verteiltes SCM mit Remotes

Entwickler haben lokal einen Klon

Historie liegt auf jedem Klon vor

_images/verteilter-wf.png

Anstelle EINES SCM-Servers gibt es N remote

Workflow frei wählbar

Installation -- git

https://git-scm.com/downloads

Einrichten -- git-config

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

Einrichten -- git-config (optional)

aufrüschen ..

$ git config --global color.ui true

anpassen ..

$ git config --global push.default simple
$ git config --global core.editor emacsclient

nachlesen ..

git help config

lokales Arbeiten -- Dateien & Änderungen

  • im Workspace (WS)

  • im Stage (gibts beim SVN z.B. nicht)

  • im lokalem Repository (beim SVN nur remote)

_images/lifecycle.png

lokales Arbeiten -- Grundlagen

$ git init
$ git add ...
$ git status ...
$ git add ...
$ git rm ...
$ git commit ...
$ git checkout ...
$ git log ...
$ git branch ...
$ git merge ...

lokales Arbeiten -- git init

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.

lokales Arbeiten -- git status

$ 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~

Einrichten -- .gitignore

.gitignore: Pattern die ignoriert werden (nachlesen)

*~
*/#*#
.#*
*.pyc
*.pyo

.gitignore Vorlagen

$ git status
...
Untracked files:
  ...
        .gitignore
        README.txt

sieht schon besser aus :)

lokales Arbeiten -- git add

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.

lokales Arbeiten -- git rm

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

lokales Arbeiten -- git commit

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

lokales Arbeiten -- git show

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

lokales Arbeiten -- git log

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

lokales Arbeiten -- 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

Einrichten -- git config / alias

Git Aliases

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. ;)

So könnte es im Terminal aussehen

_images/cmdline-git-graph.png

lokales Arbeiten -- git branch

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

branch: hello-world

#!/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

branch: master

$ 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 graph

_images/git-graph-001.svg

branch: hello-world

$ 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.

branch: hello-world

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(+)

git graph

_images/git-graph-002.svg

git merge

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 merge

$ 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.

git merge conflict

<<<<<<< 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.

git merge conflict

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 merge conflict

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

git merge conflict

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

git graph

_images/git-graph-003.svg

remotes -- Grundlagen

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   ..

remotes -- git clone

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?

remotes -- Grundlagen

$ 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

remotes -- Grundlagen

$ 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

remotes -- Grundlagen

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.

remotes -- Grundlagen

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

remotes -- git push

$ 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

remotes -- git fetch & pull

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

remotes -- git remote

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

remotes -- git remote

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.

online server

_images/github-octocat.png _images/gitlab-logo.png _images/bitbucket-logo.png

GitLab.comGitHubBitbucket

self hosted

_images/gogs-logo.jpg _images/gitlab-logo.png

gogs leichtgewichtGitLab CE Team & CI

offline Szenarien

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.

Danke

Verweise