Dies ist eine alte Version des Dokuments!
Verteilte Versionsverwaltung für Programmcode und Dokumente mit Hilfe von Git
Git ist ein freies und quelloffenes, verteiltes Versionskontrollsystem, welches Anfang 2005 von Linus Torvalds, dem Initiator und heutigen Maintainer des Linux-Kernels, entwickelt wurde. Mit Hilfe der verteilten Versionsverwaltung von lassen sich die Dateien eines Projektes einfach organisieren und an beliebiger Stelle erweitern, optimieren und deployen und so kann fast alles von kleinen bis zu sehr grossen Projekten schnell und effizient bearbeitet werden.
In einem verteilten System wie Git gibt es viele gleichwertige Instanzen des Repositorys, so dass jeder Entwickler über sein eigenes Repository verfügt. Der Austausch von Veränderungen ist flexibler und erfolgt nicht zwingend über einen zentralen Server. Bei einem zentralen System, wie z.B. Subversion, muss es einen zentralen Server geben, auf dem die Geschichte des Projekts gespeichert wird. Alle Entwickler müssen sich mit diesem Server verbinden, um die Versionsgeschichte einzusehen oder Änderungen vorzunehmen.
Grundlagen
Literatur
Zum Einlesen was Git insbesondere leisten kann, welche Einsatzgebiete es gibt, welche Unterschiede es zu zu anderen Versionsverwaltungstools wie z.B. Subversion oder CVS bietet, eigenen sich unter anderem folgende hervorragende Bücher, die auch online zur Verfügung stehen.
Pro Git von Apress
Das Git-Buch
Git und seine Befehlsoptionen
Einen schnellen Überblick über Git und seine Kommandos kann man mit Hilfe der Option --help
zur Ansicht bringen.
$ git --help
usage: git [--version] [--help] [-C <path>] [-c <name>=<value>] [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path] [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare] [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>] <command> [<args>] These are common Git commands used in various situations: start a working area (see also: git help tutorial) clone Clone a repository into a new directory init Create an empty Git repository or reinitialize an existing one work on the current change (see also: git help everyday) add Add file contents to the index mv Move or rename a file, a directory, or a symlink restore Restore working tree files rm Remove files from the working tree and from the index sparse-checkout Initialize and modify the sparse-checkout examine the history and state (see also: git help revisions) bisect Use binary search to find the commit that introduced a bug diff Show changes between commits, commit and working tree, etc grep Print lines matching a pattern log Show commit logs show Show various types of objects status Show the working tree status grow, mark and tweak your common history branch List, create, or delete branches commit Record changes to the repository merge Join two or more development histories together rebase Reapply commits on top of another base tip reset Reset current HEAD to the specified state switch Switch branches tag Create, list, delete or verify a tag object signed with GPG collaborate (see also: git help workflows) fetch Download objects and refs from another repository pull Fetch from and integrate with another repository or a local branch push Update remote refs along with associated objects 'git help -a' and 'git help -g' list available subcommands and some concept guides. See 'git help <command>' or 'git help <concept>' to read about a specific subcommand or concept. See 'git help git' for an overview of the system.
Eine umfangreiche Dokumentation von Git liegt in Form vorinstallierter Manual-Pages vor, bei dem fast jedes Subkommando über eine eigene manpage verfügt. Die entsprechende Hilfeseiten erreicht man z.B. am Beispiel des Befehls git init
über folgende Varianten:
$ man git-init
$ git init --help
bzw.
$ git help status
Vorbereitende Konfiguration
Damit Git bei einem commit
die Änderungen einem Urheber zuordnen kann, werden wir zunächst ein paar grundlegende Einstellungen vornehmen.
$ git config --global user.name "Michael Nausch" $ git config --global user.email "django@nausch.org"
Optional können wir, sofern ausser uns kein anderer Nutzer, Zugriff auf den Rechner hat, auch ein Passwort für spätere git push
hinterlegen
$ git config --global user.password "0l1v3r157einec001350ck3!"
Da wir auf unserer Linux-Admin Workstation unseren gewohnten Editor vim
verwenden wollen, definieren wir diesen für git*.
$ git config –global core.editor „vim“
$ git config –global color.ui auto
$ git config –list –show-origin
<code>file:/home/django/.gitconfig user.name=Michael Nausch
file:/home/django/.gitconfig user.email=django@nausch.org
file:/home/django/.gitconfig user.password=0l1v3r157einec001350ck3!
file:/home/django/.gitconfig core.editor=vim</code>
$ ls -alF ~/.gitconfig
<code>-rw-rw-r– 1 django django 139 Apr 6 17:24 /home/django/.gitconfig</code>
$ cat ~/.gitconfig
<code>[user]
name = Michael Nausch
email = django@nausch.org
password = 0l1v3r157einec001350ck3!
[core]
editor = vim
</code>
===== Git Beispielsprojekt - initiale Aufgaben =====
==== Arbeitsverzeichnis / Entwicklungsumgebung anlegen ====
Zunächst legen wir uns im Arbeitsverzeichnis Freifunk
im Home-Verzeichnis unseres Users ein Verzeichnis für unser Projekt (Repository) an.
$ mkdir -p $HOME/Freifunk/ffmuc-offloader_rpb4
Dieses Verzeichnis ist aktuell noch leer und ohne jedwede Inhalte.
$ ls -alF ~/Freifunk/ffmuc-offloader_rpb4
<code>total 8
drwxrwxr-x 2 django django 4096 Apr 6 17:15 ./
drwxrwxr-x 4 django django 4096 Apr 6 17:15 ../</code>
==== Erste Schritte mit Git ====
Nun können wir unser Repository git
bekannt machen und initialisieren.
$ git init ~/Freifunk/ffmuc-offloader_rpb4
Initialized empty Git repository in /home/django/Freifunk/ffmuc-offloader_rpb4/.git/
Im entsprechenden Verzeichnis finden wir nun das Verzeichnis .git
ls -alF ~/Freifunk/ffmuc-offloader_rpb4
<code>total 12
drwxrwxr-x 3 django django 4096 Apr 6 17:33 ./
drwxrwxr-x 4 django django 4096 Apr 6 17:15 ../
drwxrwxr-x 7 django django 4096 Apr 6 17:33 .git/</code>
$ tree /home/django/Freifunk/ffmuc-offloader_rpb4/.git/
<code>/home/django/Freifunk/ffmuc-offloader_rpb4/.git/
├── branches
├── config
├── description
├── HEAD
├── hooks
│ ├── applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── fsmonitor-watchman.sample
│ ├── post-update.sample
│ ├── pre-applypatch.sample
│ ├── pre-commit.sample
│ ├── pre-merge-commit.sample
│ ├── prepare-commit-msg.sample
│ ├── pre-push.sample
│ ├── pre-rebase.sample
│ ├── pre-receive.sample
│ └── update.sample
├── info
│ └── exclude
├── objects
│ ├── info
│ └── pack
└── refs
├── heads
└── tags
9 directories, 16 files</code>
In diesem Verzeichnis verwaltet git intern unser Repository. Wir brauchen diesem in aller Regel keine besondere Beachtung schenken und vertrauen und verlassen uns da voll darauf, dass git hier alles nötige verwalten wird.
Nun können wir mit der Option status
den aktuellen Status unseres Verzeichnisses ansehen. Dazu wechseln wir zunächst in das zuvor angelegte Arbeitsverzeichnis.
$ cd ~/Freifunk/ffmuc-offloader_rpb4/
Anschliessend fragen wir den Status ab.
$ git status
<code>On branch master
No commits yet
nothing to commit (create/copy files and use „git add“ to track)</code>
Was genau teilt uns nun git hier mit? Nun zum einen weist ins git zum einen darauf hin, dass wir vor unserem ersten commit stehen No commits yet
und zum anderen dass aktuell noch keinerlei Inhalte existieren nothing to commit
, die git aktuell in einen commit einfliessen lassen könnte.
Git ist aber so nett und gibt uns auch gleich einen Hinweis, was wir als nächsten tun sollten um diesen Makel zu beheben:
(create/copy files and use „git add“ to track)
Wir sollen also Dateien erstellen oder kopieren und mit Hilfe des Befehls git add
dann diesem Repo bzw. Git bekannt zu geben.
==== Ansible: Directory Layout anlegen ====
Da wir uns bei unserem Repository-Beispiel an den Ansible's Best Practices orientieren wollen, werden wir zunächst das Ansible Directory Layout anlegen.
Dieses Verzeichnis-Layout werden wir nun einfach und schnell auf den Weg bringen. Hierzu verwenden wir die nachfolgend gezeigten zwei Befehle bzw. genauer gesagt die beiden Befehlsketten:
$ mkdir -p ~/Freifunk/ffmuc-offloader_rpb4/inventories/{production,staging}/{group_vars,host_vars} \
~/Freifunk/ffmuc-offloader_rpb4/{library,module_utils,filter_plugins} \
~/Freifunk/ffmuc-offloader_rpb4/roles/common/{tasks,handlers,templates,files,vars,defaults,meta,library,module_utils,lookup_plugin}
$ touch ~/Freifunk/ffmuc-offloader_rpb4/inventories/{production,staging}/hosts.yml \
~/Freifunk/ffmuc-offloader_rpb4/site.yml \
~/Freifunk/ffmuc-offloader_rpb4/roles/common/{tasks,handlers,templates,files,vars,defaults,meta}/main.yml
Somit haben wir nun in unserem Repository Verzeichnis die gewünschte Struktur, das Ansible Directory Layout:
$ tree ~/Freifunk/ffmuc-offloader_rpb4
<code>/home/django/Freifunk/ffmuc-offloader_rpb4
├── filter_plugins
├── inventories
│ ├── production
│ │ ├── group_vars
│ │ ├── hosts.yml
│ │ └── host_vars
│ └── staging
│ ├── group_vars
│ ├── hosts.yml
│ └── host_vars
├── library
├── module_utils
├── roles
│ └── common
│ ├── defaults
│ │ └── main.yml
│ ├── files
│ │ └── main.yml
│ ├── handlers
│ │ └── main.yml
│ ├── library
│ ├── lookup_plugin
│ ├── meta
│ │ └── main.yml
│ ├── module_utils
│ ├── tasks
│ │ └── main.yml
│ ├── templates
│ │ └── main.yml
│ └── vars
│ └── main.yml
└── site.yml
22 directories, 10 files</code>
Rufen wir nun erneut den git status
informiert uns nunmehr Git wie folgt:
$ git status
<html><pre class=„code“>
<font style=„color: rgb(0, 0, 0)“>On branch master
No commits yet
Untracked files:
(use „git add <file>…“ to include in what will be committed)</font>
<font style=„color: rgb(201, 0, 0)“> inventories/
roles/
site.yml
</font>
<font style=„color: rgb(0, 0, 0)“>nothing added to commit but untracked files present (use „git add“ to track)</font>
</pre>
</html>
Hmmm eigentlich komisch, da wir doch auch z.B. ein Verzeichnis haben wie dies hier: ~/Freifunk/ffmuc-offloader_rpb4/filter_plugins
. Warum nur unterschlägt uns Git nun dieses Verzeichnis. Die Lösung finden wir in dem Hinweis Untracked files
. Git erfasst erst einmal „nur Inhalte, genauer gesagt Dateien. Leere Verzeichnisse werden erst einmal ignoriert!
Wir werden also, dem Vorschlag von Git die drei rot markierten Zeilen mit einm git add
Git bekannt geben.
$ git add inventories/
$ git add roles/
$ git add site.yml
Nun rufen wir erneut git status
auf.
<html><pre class=„code“>
<font style=„color: rgb(0, 0, 0)“>On branch master
No commits yet
Changes to be committed:
(use „git rm –cached <file>…“ to unstage)</font>
<font style=„color: rgb(51, 145, 5)“> new file: inventories/production/hosts.yml
new file: inventories/staging/hosts.yml
new file: roles/common/defaults/main.yml
new file: roles/common/files/main.yml
new file: roles/common/handlers/main.yml
new file: roles/common/meta/main.yml
new file: roles/common/tasks/main.yml
new file: roles/common/templates/main.yml
new file: roles/common/vars/main.yml
new file: site.yml</font>
</pre>
</html>
Nun ist es an der Zeit unseren ersten Commit um damit unsere Dateien Git zur Verwaltung anzuvertrauen; Git markiert die neu gefundenen Datei grün und markiert diese als new file:
. Dieser Commit wurde aber noch nicht vollzogen, sondern erst nur vorbereitet! Git sammelt die so markierten Dateien im sog. Index** und merkt sich intern so die Inhalte vor, die beim nächsten Commit mit einfliessen sollen.