<h1>Los geht’s</h1>

<p>
  In diesem Kapitel wird es darum gehen, wie man mit Git loslegen kann. Wir
  werden erläutern, wozu Versionskontrollsysteme gut sind, wie man Git auf
  verschiedenen Systemen installieren und konfigurieren kann, sodass man in der
  Lage ist, mit der Arbeit anzufangen. Am Ende dieses Kapitels solltest Du
  verstehen, wozu Git gut ist, weshalb Du es verwenden solltest und wie Du
  damit loslegen kannst.
</p>

<h2>Wozu Versionskontrolle?</h2>

<p>
  Was ist&emsp14;&emsp14;die Versionskontrolle, und warum solltest Du Dich
  dafür interessieren? Versionskontrollsysteme (VCS) protokollieren Änderungen
  an einer Datei oder einer Anzahl von Dateien über die Zeit hinweg, so dass
  man zu jedem Zeitpunkt auf Versionen und Änderungen zugreifen kann. Die
  Beispiele in diesem Buch verwenden Software Quellcode, tatsächlich aber
  kannst Du Änderungen an praktisch jeder Art von Datei per Versionskontrolle
  nachverfolgen.
</p>

<p>
  Als ein Grafik- oder Webdesigner zum Beispiel willst Du in der Lage sein,
  jede Version eines Bildes oder Layouts zurückverfolgen zu können. Es wäre
  daher sehr ratsam, ein Versionskontrollsystem zu verwenden. Ein solches
  System erlaubt Dir, einzelne Dateien oder auch ein ganzes Projekt in einen
  früheren Zustand zurückzuversetzen, nachzuvollziehen, wer zuletzt welche
  Änderungen vorgenommen hat, die möglicherweise Probleme verursachen, wer eine
  Änderung ursprünglich vorgenommen hat usw. Ein Versionskontrollsystem für
  Deine Arbeit zu verwenden, versetzt Dich in die Lage jederzeit zu einem
  vorherigen, funktionierenden Zustand zurückzugehen, wenn Du vielleicht Mist
  gebaut oder aus irgendeinem Grunde Dateien verloren hast. All diese Vorteile
  erhältst Du für einen nur sehr geringen, zusätzlichen Aufwand.
</p>

<h3>Lokale Versionskontrollsysteme</h3>

<p>
  Viele Leute kontrollieren Versionen ihrer Arbeit, indem sie einfach Dateien
  in ein anderes Verzeichnis kopieren (wenn sie clever sind: ein Verzeichnis
  mit einem Zeitstempel im Namen). Diese Vorgehensweise ist üblich, weil sie so
  einfach ist. Aber sie ist auch unglaublich fehleranfällig. Man vergisst sehr
  leicht, in welchem Verzeichnis man sich gerade befindet und kopiert die
  falschen Dateien oder überschreibt Dateien, die man eigentlich nicht
  überschreiben wollte.
</p>

<p>
  Um diese Arbeit zu erleichtern und sicherer zu machen, haben Programmierer
  vor langer Zeit Versionskontrollsysteme entwickelt, die alle Änderungen an
  allen relevanten Dateien in einer lokalen Datenbank verfolgten (siehe Bild
  1-1).
</p>

<p>Insert 18333fig0101.png Bild 1-1. Diagramm: Lokale Versionskontrolle</p>

<p>
  Eines der populärsten Versionskontrollsysteme war rcs, und es wird heute
  immer noch mit vielen Computern ausgeliefert. Z.B. umfasst auch das
  Betriebssystem Mac OS X den Befehl rcs, wenn Du die Developer Tools
  installierst. Dieser Befehl arbeitet nach dem Prinzip, dass er für jede
  Änderung einen Patch (d.h. eine Kodierung der Unterschiede, die eine Änderung
  an einer oder mehreren Dateien umfasst) in einem speziellen Format in einer
  Datei auf der Festplatte speichert.
</p>

<h3>Zentralisierte Versionskontrollsysteme</h3>

<p>
  Das nächste Problem, mit dem Programmierer sich dann konfrontiert sahen,
  bestand in der Zusammenarbeit mit anderen: Änderungen an dem gleichen Projekt
  mussten auf verschiedenen Computern, möglicherweise verschiedenen
  Betriebssystemen vorgenommen werden können. Um dieses Problem zu lösen,
  wurden Zentralisierte Versionskontrollsysteme (CVCS) entwickelt. Diese
  Systeme, beispielsweise CVS, Subversion und Perforce, basieren auf einem
  zentralen Server, der alle versionierten Dateien verwaltet. Wer an diesen
  Dateien arbeiten will, kann sie von diesem Server abholen („check out“ xxx),
  auf seinem eigenen Computer bearbeiten und dann wieder auf dem Server
  abliefern. Diese Art von System war über viele Jahre hinweg der Standard für
  Versionskontrollsysteme (siehe Bild 1-2).
</p>

<p>
  Insert 18333fig0102.png Bild 1-2. Diagramm: Zentralisierte
  Versionskontrollsysteme
</p>

<p>
  Dieser Aufbau hat viele Vorteile gegenüber Lokalen Versionskontrollsystemen.
  Zum Beispiel weiß jeder mehr oder weniger genau darüber Bescheid, was andere,
  an einem Projekt Beteiligte gerade tun. Administratoren haben die
  Möglichkeit, detailliert festzulegen, wer was tun kann. Und es ist sehr viel
  einfacher, ein CVCS zu administrieren als lokale Datenbanken auf jedem
  einzelnen Anwenderrechner zu verwalten.
</p>

<p>
  Allerdings hat dieser Aufbau auch einige erhebliche Nachteile. Der
  offensichtlichste Nachteil ist der „Single Point of Failure“, den der
  zentralisierte Server darstellt. Wenn dieser Server für nur eine Stunde nicht
  verfügbar ist, dann kann in dieser Stunde niemand in irgendeiner Form mit
  anderen arbeiten oder versionierte Änderungen an den Dateien speichern, an
  denen sie momentan arbeiten. Wenn die auf dem zentralen Server verwendete
  Festplatte beschädigt wird und keine Sicherheitskopien erstellt wurden, dann
  sind all diese Daten unwiederbringlich verloren – die komplette Historie des
  Projektes, abgesehen natürlich von dem jeweiligen Zustand, den Mitarbeiter
  gerade zufällig auf ihrem Rechner haben. Lokale Versionskontrollsysteme haben
  natürlich dasselbe Problem: wenn man die Historie eines Projektes an einer
  einzigen, zentralen Stelle verwaltet, riskiert man, sie vollständig zu
  verlieren, wenn irgendetwas an dieser zentralen Stelle ernsthaft schief
  läuft.
</p>

<h3>Verteilte Versionskontrollsysteme</h3>

<p>
  Und an dieser Stelle kommen verteilte Versionskontrollsysteme (DVCS) ins
  Spiel. In einem DVCS (wie z.B. Git, Mercurial, Bazaar oder Darcs) erhalten
  Anwender nicht einfach den jeweils letzten Snapshot des Projektes von einem
  Server: sie erhalten statt dessen eine vollständige Kopie des Repositories.
  Auf diese Weise kann, wenn ein Server beschädigt wird, jedes beliebige
  Repository von jedem beliebigen Anwenderrechner zurück kopiert werden und der
  Server so wieder hergestellt werden.
</p>

<p>
  Insert 18333fig0103.png Bild 1-3. Diagramm: Distribuierte Versionskontrolle
</p>

<p>
  Darüber hinaus können derartige Systeme hervorragend mit verschiedenen
  externen („remote“) Repositories umgehen, sodass man mit verschiedenen
  Gruppen von Leuten simultan in verschiedenen Weisen zusammenarbeiten kann.
  Das macht es möglich, verschiedene Arten von Arbeitsabläufen (wie
  Hierarchien) zu integrieren, was mit zentralisierten Systemen nicht möglich
  ist.
</p>

<h2>Die Geschichte von Git</h2>

<p>
  Wie viele großartige Dinge im Leben entstand Git aus kreativem Chaos und
  hitziger Diskussion. Der Linux Kernel ist ein Open Source Software Projekt
  von erheblichem Umfang. Während der gesamten Entwicklungszeit des Linux
  Kernels von 1991 bis 2002 wurden Änderungen an der Software in Form von
  Patches (d.h. Änderungen an bestehendem Code) und archivierten Dateien
  herumgereicht. 2002 began man dann, ein proprietäres DVCS System mit dem
  Namen „Bitkeeper“ zu verwenden.
</p>

<p>
  2005 ging die Beziehung zwischen der Community, die den Linux Kernel
  entwickelte, und des kommerziell ausgerichteten Unternehmens, das BitKeeper
  entwickelte, kaputt. Die zuvor ausgesprochene Erlaubnis, BitKeeper kostenlos
  zu verwenden, wurde widerrufen. Dies war für die Linux Entwickler Community
  (und besonders für Linus Torvald, der Erfinder von Linux) der Auslöser dafür,
  ein eigenes Tool zu entwickeln, das auf den Erfahrungen mit BitKeeper
  basierte. Ziele des neuen Systems waren unter anderem:
</p>

<ul>
  <li>
    <p>Geschwindigkeit</p>
  </li>

  <li>
    <p>Einfaches Design</p>
  </li>

  <li>
    <p>
      Gute Unterstützung von nicht-linearer Entwicklung (tausende paralleler
      Branches, d.h. verschiedener Verzweigungen der Versionen)
    </p>
  </li>

  <li>
    <p>Vollständig verteilt</p>
  </li>

  <li>
    <p>
      Fähig, große Projekte wie den Linux Kernel effektiv zu verwalten
      (Geschwindigkeit und Datenumfang)
    </p>
  </li>
</ul>

<p>
  Seit seiner Geburt 2005 entwickelte sich Git kontinuierlich weiter und reifte
  zu einem System heran, das einfach zu bedienen ist, die ursprünglichen Ziele
  dabei aber weiter beibehält. Es ist unglaublich schnell, äußerst effizient,
  wenn es um große Projekte geht, und es hat ein fantastisches Branching
  Konzept für nicht-lineare Entwicklung (mehr dazu in Kapitel 3).
</p>

<h2>Git Grundlagen</h2>

<p>
  Was also ist Git, in kurzen Worten? Es ist wichtig, den folgenden Abschnitt
  zu verstehen, in dem es um die grundlegenden Konzepte von Git geht. Das wird
  Dich in die Lage versetzen, Git einfacher und effektiver anzuwenden. Versuche
  Dein vorhandenes Wissen über andere Versionskontrollsysteme, wie Subversion
  oder Perforce, zu ignorieren, während Du Git kennen lernst. Git speichert und
  konzipiert Information anders als andere Systeme, auch wenn das Interface
  relativ ähnlich wirkt. Diese Unterschiede zu verstehen wird Dir helfen,
  Verwirrung bei der Anwendung von Git zu vermeiden.
</p>

<h3>Snapshots, nicht Diffs</h3>

<p>
  Der Hauptunterschied zwischen Git und anderen Versionskontrollsystemen (auch
  Subversion und vergleichbaren Systemen) besteht in der Art und Weise wie Git
  Daten betrachtet. Die meisten anderen Systeme speichern Information als eine
  fortlaufende Liste von Änderungen an Dateien („Diffs“). Diese Systeme (CVS,
  Subversion, Perforce, Bazaar usw.) betrachten die Informationen, die sie
  verwalten, als eine Menge von Dateien und die Änderungen, die über die Zeit
  hinweg an einzelnen Dateien vorgenommen werden. (Siehe Bild 1-4.)
</p>

<p>
  Insert 18333fig0104.png Bild 1-4. Andere Systeme speichern Daten als
  Änderungen an einzelnen Dateien einer Datenbasis
</p>

<p>
  Git sieht Daten nicht in dieser Weise. Stattdessen betrachtet Git seine Daten
  eher als eine Reihe von Snapshots eines Mini-Dateisystems. Jedes Mal, wenn Du
  committest (d.h. den gegenwärtigen Status Deines Projektes als eine Version
  in Git speicherst), sichert Git den Zustand sämtlicher Dateien in diesem
  Moment („Snapshot“) und speichert eine Referenz auf diesen Snapshot. Um dies
  möglichst effizient und schnell tun zu können, kopiert Git unveränderte
  Dateien nicht, sondern legt lediglich eine Verknüpfung zu der vorherigen
  Version der Datei an. Git betrachtet Daten also wie in Bild 1-5 dargestellt.
</p>

<p>
  Insert 18333fig0105.png Bild 1-5. Git speichert Daten als eine Historie von
  Snapshots des Projektes.
</p>

<p>
  Dies ist ein wichtiger Unterschied zwischen Git und praktisch allen anderen
  Versionskontrollsystemen. In Git wurden daher fast alle Aspekte der
  Versionskontrolle neu überdacht, die in anderen Systemen mehr oder weniger
  von ihren jeweiligen Vorgängergeneration übernommen worden waren. Git
  arbeitet im Großen und Ganzen eher wie ein (mit einigen unglaublich mächtigen
  Werkezeugen ausgerüstetes) Mini-Dateisystem, als wie ein gängiges
  Versionskontrollsystem. Auf einige der Vorteile, die es mit sich bringt,
  Daten in dieser Weise zu betrachten, werden wir in Kapitel 3 eingehen, wenn
  wir das Git Branching Konzept diskutieren.
</p>

<h3>Fast jede Operation ist lokal</h3>

<p>
  Die meisten Operationen in Git benötigen nur die lokalen Dateien und
  Ressourcen auf Deinem Rechner, um zu funktionieren. D.h. im Allgemeinen
  werden keine Informationen von einem anderen Rechner im Netzwerk benötigt.
  Wenn Du mit einem CVCS gearbeitet hast, das für die meisten Operationen einen
  Network Latency Overhead (also Wartezeiten, die das Netzwerk benötigt werden)
  hat, dann wirst Du den Eindruck haben, dass die Götter der Geschwindigkeit
  Git mit unaussprechlichen Fähigkeiten ausgestattet haben. Weil man die
  vollständige Historie lokal auf dem Rechner hat, werden die allermeisten
  Operationen ohne jede Verzögerung ausgeführt und sind sehr schnell.
</p>

<p>
  Um beispielsweise die Historie des Projektes zu durchsuchen, braucht Git sie
  nicht von einem externen Server zu holen – es liest sie einfach aus der
  lokalen Datenbank. Das heißt, Du siehst die vollständige Projekthistorie ohne
  jede Verzögerung. Wenn Du sehen willst, worin sich die aktuelle Version einer
  Datei von einer Version von vor einem Monat unterscheidet, dann kann Git
  diese Versionen lokal nachschlagen und ihre Unterschiede lokal bestimmen. Es
  braucht dazu keinen externen Server – weder um Dateien dort nachzuschlagen,
  noch um Unterschiede dort bestimmen zu lassen.
</p>

<p>
  Dies bedeutet natürlich außerdem, dass es fast nichts gibt, was Du nicht tun
  kannst, bloß weil Du gerade offline bist oder keinen Zugriff auf ein VPN
  hast. Wenn Du im Flugzeug oder Zug ein wenig arbeiten willst, kannst Du
  problemlos Deine Arbeit committen und Deine Arbeit erst auf den Server pushen
  (hochladen), wenn Du wieder mit dem Internet verbunden bist. Wenn Du zu Hause
  bist aber nicht auf das VPN zugreifen kannst, kannst Du dennoch arbeiten.
  Perforce z.B. erlaubt Dir dagegen nicht sonderlich viel zu tun, solange Du
  nicht mit dem Server verbunden bist. Und in Subversion und CVS kannst Du
  Dateien zwar ändern, die Änderungen aber nicht in der Datenbank sichern (weil
  die Datenbank offline ist). Das mag auf den ersten Blick nicht nach einem
  großen Problem aussehen, aber Du wirst überrascht sein, was für einen großen
  Unterschied das ausmachen kann.
</p>

<h3>Git stellt Integrität sicher</h3>

<p>
  In Git werden Änderungen in Checksummen umgerechnet, bevor sie gespeichert
  werden. Anschließend werden sie mit dieser Checksumme referenziert. Das macht
  es unmöglich, dass sich die Inhalte von Dateien oder Verzeichnissen ändern,
  ohne dass Git das mitbekommt. Git basiert auf dieser Funktionalität und sie
  ist ein integraler Teil von Gits Philosophie. Man kann Informationen deshalb
  z.B. nicht während der Übermittlung verlieren oder unwissentlich beschädigte
  Dateien verwenden, ohne dass Git in der Lage wäre, dies festzustellen.
</p>

<p>
  Der Mechanismus, den Git verwendet, um diese Checksummen zu erstellen, heißt
  SHA-1 Hash. Eine solche Checksumme ist eine 40 Zeichen lange Zeichenkette,
  die aus hexadezimalen Zeichen besteht und diese wird von Git aus den Inhalten
  einer Datei oder Verzeichnisstruktur kalkuliert. Ein SHA-1 Hash sieht wie
  folgt aus:
</p>

<pre>
24b9da6552252987aa493b52f8696cd6d3b00373
</pre>

<p>
  Dir werden solche Hash Werte überall in Git begegnen, weil es sie so
  ausgiebig benutzt. Tatsächlich speichert und referenziert Git Informationen
  über Dateien in der Datenbank nicht nach ihren Dateinamen sondern nach den
  Hash Werten ihrer Inhalte.
</p>

<h3>Git verwaltet fast ausschließlich Daten</h3>

<p>
  Fast alle Operationen, die Du in der täglichen Arbeit mit Git verwendest,
  fügen Daten jeweils nur zur internen Git Datenbank hinzu. Deshalb ist es sehr
  schwer, das System dazu zu bewegen, irgendetwas zu tun, das nicht wieder
  rückgängig zu machen ist, oder dazu, Daten in irgendeiner Form zu löschen. In
  jedem anderen VCS ist es leicht, Änderungen, die man noch nicht gespeichert
  hat, zu verlieren oder unbrauchbar zu machen. In Git dagegen ist es
  schwierig, einen einmal gespeicherten Snapshot zu verlieren, insbesondere
  wenn man regelmäßig in ein anderes Repository pusht.
</p>

<p>
  U.a. deshalb macht es so viel Spaß, mit Git zu arbeiten. Man kann mit
  Änderungen experimentieren, ohne befürchten zu müssen, irgendetwas zu
  zerstören oder durcheinander zu bringen. Einen tieferen Einblick in die Art,
  wie Git Daten speichert und wie man Daten, die scheinbar verloren sind,
  wieder herstellen kann, wird Kapitel 9 gewähren.
</p>

<h3>Die drei Zustände</h3>

<p>
  Jetzt aufgepasst. Es folgt die wichtigste Information, die Du Dir merken
  musst, wenn Du Git kennen lernen willst und Fallstricke vermeiden willst. Git
  definiert drei Haupt-Zustände, in denen sich eine Datei befinden kann:
  committed, modified („geändert“) und staged („vorgemerkt“). „Committed“
  bedeutet, dass die Daten in der lokalen Datenbank gesichert sind. „Modified“
  bedeutet, dass die Datei geändert, diese Änderung aber noch nicht committed
  wurde. „Staged“ bedeutet, dass Du eine geänderte Datei in ihrem gegenwärtigen
  Zustand für den nächsten Commit vorgemerkt hast.
</p>

<p>
  Das führt uns zu den drei Hauptbereichen eines Git Projektes: das Git
  Verzeichnis, das Arbeitsverzeichnis und die Staging Area.
</p>

<p>
  Insert 18333fig0106.png Bild 1-6. Arbeitsverzeichnis, Staging Area (xxx) und
  Git Verzeichnis
</p>

<p>
  Das Git Verzeichnis ist der Ort, an dem Git Metadaten und die lokale
  Datenbank für Dein Projekt speichert. Dies ist der wichtigste Teil von Git,
  und dieser Teil wird kopiert, wenn Du ein Repository von einem anderen
  Rechner klonst.
</p>

<p>
  Dein Arbeitsverzeichnis ist ein Checkout („Abbild“ xxx) einer spezifischen
  Version des Projektes. Diese Dateien werden aus der komprimierten Datenbank
  geholt und auf der Festplatte in einer Form gespeichert, die Du bearbeiten
  und modifizieren kannst.
</p>

<p>
  Die Staging Area ist einfach eine Datei (normalerweise im Git Verzeichnis),
  in der vorgemerkt wird, welche Änderungen Dein nächster Commit umfassen soll.
  Sie wird manchmal auch als „Index“ bezeichnet, aber der Begriff „Staging
  Area“ ist der gängigere.
</p>

<p>Der grundlegend Git Arbeitsprozess sieht in etwa so aus:</p>

<ol>
  <li>
    <p>Du bearbeitest Dateien in Deinem Arbeitsverzeichnis.</p>
  </li>

  <li>
    <p>
      Du markierst Dateien für den nächsten Commit, indem Du Snapshots zur
      Staging Area hinzufügst.
    </p>
  </li>

  <li>
    <p>
      Du legst den Commit an, wodurch die in der Staging Area vorgemerkten
      Snapshots dauerhaft im Git Verzeichnis (d.h. der lokalen Datenbank)
      gespeichert werden.
    </p>
  </li>
</ol>

<p>
  Wenn eine bestimmte Version einer Datei im Git Verzeichnis liegt, gilt sie
  als „committed“. Wenn sie geändert und in der Staging Area vorgemerkt ist,
  gilt sie als „staged“. Und wenn sie geändert, aber noch nicht zur Staging
  Area hinzugefügt wurde, gilt sie als „modified“. In Kapitel 2 wirst Du mehr
  über diese Zustände lernen und darüber, wie Du sie sinnvoll einsetzen und wie
  Du den Zwischenschritt der Staging Area auch einfach überspringen kannst.
</p>

<h2>Git installieren</h2>

<p>
  Lass uns damit anfangen, Git tatsächlich zu verwenden. Der erste Schritt
  besteht natürlich darin, Git zu installieren und das kann, wie üblich, auf
  unterschiedliche Weisen geschehen. Die beiden wichtigsten bestehen darin,
  entweder den Quellcode herunterzuladen und selbst zu kompilieren oder ein
  fertiges Paket für Dein Betriebssystem zu installieren.
</p>

<h3>Vom Quellcode aus installieren</h3>

<p>
  Wenn es Dir möglich ist, empfehlen wir, Git vom Quellcode aus zu
  installieren, weil Du die jeweils neueste Version erhältst. In der Regel
  bringt jede Version nützliche Verbesserungen (z.B. am Interface), sodass es
  sich lohnt die jeweils neueste Version zu verwenden – sofern Du natürlich
  damit klarkommst, Software aus dem Quellcode zu kompilieren. Viele Linux
  Distributionen umfassen sehr alte Git Versionen. Wenn Du also keine sehr
  aktuelle Distribution oder Backports (xxx) verwendest, empfehlen wir, diesen
  Weg in Erwägung ziehen.
</p>

<p>
  Um Git zu installieren, benötigst Du die folgenden Bibliotheken, die von Git
  verwendet werden: curl, zlib, openssl, expat und libiconv. Wenn Dir auf
  Deinem System yum (z.B. auf Fedora) oder apt-get (z.B. auf Debian-basierten
  Systemen) zur Verfügung steht, kannst Du einen der folgenden Befehle
  verwenden, um diese Abhängigkeiten zu installieren:
</p>

<pre>
$ yum install curl-devel expat-devel gettext-devel \
  openssl-devel zlib-devel

$ sudo apt-get install curl-devel expat-devel gettext-devel \
  openssl-devel zlib-devel
</pre>

<p>
  Nachdem Du die genannten Bibliotheken installiert hast, besorge Dir die
  aktuelle Version des Git Quellcodes von der Git Webseite:
</p>

<pre>
http://git-scm.com/download
</pre>

<p>Danach kannst Du dann Git kompilieren und installieren:</p>

<pre>
$ tar -zxf git-1.7.2.2.tar.gz
$ cd git-1.7.2.2
$ make prefix=/usr/local all
$ sudo make prefix=/usr/local install
</pre>

<p>Von nun an kannst Du Git mit Hilfe von Git selbst aktualisieren:</p>

<pre>
$ git clone git://git.kernel.org/pub/scm/git/git.git
</pre>

<h3>Installation unter Linux</h3>

<p>
  Wenn Du Git unter Linux mit einem Installationsprogramm installieren willst,
  kannst Du das normalerweise mit dem Paketmanager tun, der von Deinem
  Betriebssystem verwendet wird. Unter Fedora zum Beispiel kannst Du yum
  verwenden:
</p>

<pre>
$ yum install git-core
</pre>

<p>
  Auf einem Debian-basierten System wie Ubuntu steht Dir apt-get zur Verfügung:
</p>

<pre>
$ sudo apt-get install git
</pre>

<h3>Installation unter Mac OS X</h3>

<p>
  Auf einem Mac kann man Git auf zwei Arten installieren. Der einfachste ist,
  das grafische Git Installationsprogramm zu verwenden, den man von der
  SourceForge Webseite herunterladen kann (siehe Bild 1-7)
</p>

<pre>
http://sourceforge.net/projects/git-osx-installer/
</pre>

<p>Insert 18333fig0107.png Bild 1-7. Git OS X Installationsprogramm</p>

<p>
  Die andere Möglichkeit ist, Git via MacPorts (http://www.macports.org) zu
  installieren. Wenn Du MacPorts auf Deinem System hast, installiert der
  folgende Befehl Git:
</p>

<pre>
$ sudo port install git-core +svn +doc +bash_completion +gitweb
</pre>

<p>
  Du brauchst die optionalen Features natürlich nicht mit zu installieren, aber
  es macht Sinn <code>+svn</code> zu verwenden, falls Du jemals Git mit einem Subversion Repository verwenden
  willst.
</p>

<p>
  Alternativ kann man Git auch über Homebrew (<code>http://brew.sh/</code>) installieren. Hast Du Homebrew bereits, kannst Du Git einfach über den
  folgenden Befehl installieren:
</p>

<pre>
$ brew install git
</pre>

<h3>Installation unter Windows</h3>

<p>
  Das msysGit Projekt macht die Installation von Git unter Windows sehr
  einfach. Lade einfach das Installationsprogramm für Windows von der GitHub
  Webseite herunter und führe es aus:
</p>

<pre>
http://msysgit.github.com/
</pre>

<p>
  Danach hast Du sowohl eine Kommandozeilenversion (inklusive eines SSH
  Clients, der sich später noch als nützlich erweisen wird) als auch die
  Standard GUI installiert.
</p>

<p>
  Hinweis für Windows Benutzer: Du solltest Git mit der in msysGit enthaltenen
  Shell (Unix Style) ausführen. Dies erlaubt es Dir auch die komplexen
  Kommandozeilenbefehle aus diesem Buch auszuführen. Wenn Du aus irgendeinem
  Grund die native Windows Shell, also die Eingabeaufforderung, verwenden
  musst, müssen Gänsefüßchen, statt einzelnen Anführungszeichen verwendet
  werden (für Parameter, die ein Leerzeichen enthalten). Außerdem müssen alle
  Parameter, die mit einem Zirkumflex (^) enden und am Ende einer Zeile stehen,
  mit Gänsefüßchen umschlossen werden. Der Zirkumflex am Ende einer Zeile teilt
  Windows sonst mit, dass diese Zeile noch nicht beendet ist und in der
  nächsten Zeile fortgesetzt werden soll.
</p>

<h2>Git konfigurieren</h2>

<p>
  Nachdem Du jetzt Git auf Deinem System installiert hast, solltest Du Deine
  Git Konfiguration anpassen. Das brauchst Du nur einmal zu tun, die
  Konfiguration bleibt auch bestehen, wenn Du Git auf eine neuere Version
  aktualisierst. Du kannst sie jederzeit ändern, indem Du die folgenden Befehle
  einfach noch einmal ausführst.
</p>

<p>
  Git umfasst das Werkzeug <code>git config</code>, das Dir erlaubt, Konfigurationswerte zu verändern. Auf diese Weise kannst
  Du anpassen, wie Git aussieht und arbeitet. Diese Werte sind an drei
  verschiedenen Orten gespeichert:
</p>

<ul>
  <li>
    <p>
      Die Datei <code>/etc/gitconfig</code> enthält Werte, die für jeden Anwender des Systems und all ihre Projekte
      gelten. Wenn Du <code>git config</code> mit der Option <code>--system</code> verwendest, wird diese Datei verwendet.
    </p>
  </li>

  <li>
    <p>
      Die Werte in der Datei <code>~/.gitconfig</code> gelten ausschließlich für Dich und all Deine Projekte. Wenn Du <code>git config</code> mit der Option <code>--global</code> verwendest, wird diese Datei verwendet.
    </p>
  </li>

  <li>
    <p>
      Die Datei <code>.git/config</code> im Git Verzeichnis eines Projektes enthält Werte, die nur für das
      jeweilige Projekt gelten. Diese Dateien überschreiben Werte aus den
      jeweils vorhergehenden Dateien in dieser Reihenfolge. D.h. Werte in
      beispielsweise <code>.git/config</code> überschreiben diejenigen in <code>/etc/gitconfig</code>.
    </p>
  </li>
</ul>

<p>
  Auf Windows Systemen sucht Git nach der <code>.gitconfig</code> Datei im <code>$HOME</code> Verzeichnis (für die meisten Leute ist das das Verzeichnis <code>C:\Dokumente und Einstellungen\$USER</code>). Es schaut auch immer nach <code>/etc/gitconfig</code>, auch wenn dieses relativ zu dem MSys Wurzelverzeichnis ist, welches das
  ist, wohin Du Git bei der Installation in Windows installiert hast.
</p>

<h3>Deine Identität</h3>

<p>
  Nachdem Du Git installiert hast, solltest Du als erstes Deinen Namen und
  Deine E-Mail Adresse konfigurieren. Das ist wichtig, weil Git diese
  Information für jeden Commit verwendet, den Du anlegst, und sie ist
  unveränderlich in Deine Commits eingebaut (xxx):
</p>

<pre>
$ git config --global user.name "John Doe"
$ git config --global user.email johndoe@example.com
</pre>

<p>
  Du brauchst diese Konfiguration, wie schon erwähnt, nur einmal vorzunehmen,
  wenn Du die <code>--global</code> Option verwendest, weil Git diese Information dann für all Deine Projekte
  verwenden wird. Wenn Du sie für ein spezielles Projekt mit einem anderen
  Namen oder einer anderen E-Mail Adresse überschreiben willst, kannst Du dazu
  den Befehl ohne die <code>--global</code> Option innerhalb dieses Projektes ausführen.
</p>

<h3>Dein Editor</h3>

<p>
  Nachdem Du Deine Identität jetzt konfiguriert hast, kannst Du einstellen,
  welchen Texteditor Git in Situationen verwenden soll, in denen Du eine
  Nachricht eingeben musst. Normalerweise verwendet Git den Standard-Texteditor
  Deines Systems – das ist üblicherweise Vi oder Vim. Wenn Du einen anderen
  Texteditor, z.B. Emacs, verwenden willst, kannst Du das wie folgt festlegen:
</p>

<pre>
$ git config --global core.editor emacs
</pre>

<h3>Dein Diff Programm</h3>

<p>
  Eine andere nützliche Einstellung, die Du möglicherweise vornehmen willst,
  ist welches Diff Programm Git verwendet. Mit diesem Programm kannst Du
  Konflikte auflösen, die während der Arbeit mit Git manchmal auftreten. Wenn
  Du beispielsweise vimdiff verwenden willst, kannst Du das so festlegen:
</p>

<pre>
$ git config --global merge.tool vimdiff
</pre>

<p>
  Git kann von Hause aus mit den folgenden Diff Programmen arbeiten: kdiff3,
  tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff.
  Außerdem kannst Du ein eigenes Programm aufsetzen. Wir werden in Kapitel 7
  darauf eingehen, wie das geht.
</p>

<h3>Deine Einstellungen überprüfen</h3>

<p>
  Wenn Du Deine Einstellungen überprüfen willst, kannst Du mit dem Befehl <code>git config --list</code> alle Einstellungen anzuzeigen, die Git an dieser Stelle (z.B. innerhalb
  eines bestimmten Projektes) bekannt sind:
</p>

<pre>
$ git config --list
user.name=Scott Chacon
user.email=schacon@gmail.com
color.status=auto
color.branch=auto
color.interactive=auto
color.diff=auto
...
</pre>

<p>
  Manche Variablen werden möglicherweise mehrfach aufgelistet, weil Git
  dieselbe Variable in verschiedenen Dateien (z.B. <code>/etc/gitconfig</code> und <code>~/.gitconfig</code>) findet. In diesem Fall verwendet Git dann den jeweils zuletzt aufgelisteten
  Wert.
</p>

<p>
  Außerdem kannst Du mit dem Befehl <code>git config {key}</code> prüfen, welchen Wert Git für einen bestimmten Variablennamen verwendet:
</p>

<pre>
$ git config user.name
Scott Chacon
</pre>

<h2>Hilfe finden</h2>

<p>
  Falls Du jemals Hilfe in der Anwendung von Git benötigst, gibt es drei
  Möglichkeiten, die entsprechende Seite aus der Dokumentation (manpage) für
  jeden Git Befehl anzuzeigen:
</p>

<pre>
$ git help &lt;verb&gt;
$ git &lt;verb&gt; --help
$ man git-&lt;verb&gt;
</pre>

<p>
  Beispielsweise erhältst Du die Hilfeseite für den <code>git config</code> Befehl so:
</p>

<pre>
$ git help config
</pre>

<p>
  Die „manpage“ Dokumentation ist nützlich, weil Du sie Dir jederzeit anzeigen
  lassen kannst, auch wenn Du offline bist. Wenn Dir die manpages und dieses
  Buch nicht ausreichen, kannst Du Deine Fragen auch in den Chaträumen <code>#git</code> oder <code>#github</code> auf dem Freenode IRC Server (irc.freenode.net) stellen. Diese Räume sind in
  der Regel sehr gut besucht. Normalerweise findet sich unter den hunderten von
  Anwendern, die oft sehr viel Erfahrung mit Git haben, irgendjemand, der Deine
  Fragen gern beantwortet.
</p>

<h2>Zusammenfassung</h2>

<p>
  Du solltest jetzt ein grundlegendes Verständnis davon haben, was Git ist und
  wie es sich von anderen CVCS unterscheidet, die Du möglicherweise schon
  verwendet hast. Du solltest außerdem eine funktionierende Git Version auf
  Deinem Rechner installiert und konfiguriert haben. Jetzt wird es Zeit, einige
  Git Grundlagen zu besprechen.
</p>

<h1>Git Grundlagen</h1>

<p>
  Wenn Du nur ein einziges Kapitel aus diesem Buch lesen willst, um mit Git
  loslegen zu können, dann lies dieses hier. Wir werden hier auf die
  grundlegenden Git Befehle eingehen, die Du für den größten Teil Deiner
  täglichen Arbeit mit Git brauchst. Am Ende des Kapitels solltest Du in der
  Lage sein, ein neues Repository anzulegen und zu konfigurieren, Dateien zur
  Versionskontrolle hinzuzufügen und wieder aus ihr zu entfernen, Änderungen in
  der Staging Area für einen Commit vorzumerken und schließlich einen Commit
  durchzuführen. Wir werden außerdem besprechen, wie Du Git so konfigurieren
  kannst, dass es bestimmte Dateien und Dateimuster ignoriert, wie Du Fehler
  schnell und einfach rückgängig machen, wie Du die Historie Deines Projektes
  durchsuchen und Änderungen zwischen bestimmten Commits nachschlagen, und wie
  Du in externe Repositorys herauf- und von dort herunterladen kannst.
</p>

<h2>Ein Git Repository anlegen</h2>

<p>
  Es gibt grundsätzlich zwei Möglichkeiten, ein Git Repository auf dem eigenen
  Rechner anzulegen. Erstens kann man ein existierendes Projekt oder
  Verzeichnis in ein neues Git Repository importieren. Zweitens kann man ein
  existierendes Repository von einem anderen Rechner, der als Server fungiert,
  auf den eigenen Rechner klonen.
</p>

<h3>Ein existierendes Verzeichnis als Git Repository initialisieren</h3>

<p>
  Wenn Du künftige Änderungen an einem bestehenden Projekt auf Deinem Rechner
  mit Git versionieren und nachverfolgen willst, kannst Du dazu einfach in das
  jeweilige Verzeichnis wechseln und diesen Befehl ausführen:
</p>

<pre>
$ git init
</pre>

<p>
  Das erzeugt ein Unterverzeichnis <code>.git</code>, in dem alle relevanten Git Repository Daten enthalten sind, also ein Git
  Repository Grundgerüst. Zu diesem Zeitpunkt werden noch keine Dateien in Git
  versioniert. (In Kapitel 9 werden wir genauer darauf eingehen, welche Dateien
  im .git Verzeichnis enthalten sind und was ihre Aufgabe ist.)
</p>

<p>
  Wenn in Deinem Projekt bereits Dateien vorhanden sind (und es sich nicht nur
  um ein leeres Verzeichnis handelt), willst Du diese vermutlich zur
  Versionskontrolle hinzufügen, damit Änderungen daran künftig nachverfolgbar
  sind. Dazu kannst Du die folgenden Git Befehle ausführen um die Dateien zur
  Versionskontrolle hinzuzufügen. Anschließend kannst Du Deinen ersten Commit
  anlegen:
</p>

<pre>
$ git add *.c
$ git add README
$ git commit -m 'initial project version'
</pre>

<p>
  Wir werden gleich noch einmal genauer auf diese Befehle eingehen. Im Moment
  ist nur wichtig zu verstehen, dass Du jetzt ein Git Repository erzeugt und
  einen ersten Commit angelegt hast.
</p>

<h3>Ein existierendes Repository klonen</h3>

<p>
  Wenn Du eine Kopie eines existierenden Git Repositorys anlegen willst – z.B.
  um an einem Projekt mitzuarbeiten – dann kannst Du dazu den Befehl <code>git clone</code> verwenden. Wenn Du schon mit anderen VCS Sytemen wie Subversion gearbeitet
  hast, wird Dir auffallen, dass der Befehl <code>clone</code> heißt und nicht <code>checkout</code>. Dies ist ein wichtiger Unterschied, den Du verstehen solltest. Git lädt
  eine Kopie aller Daten, die sich im existierenden Repository befinden, auf
  Deinen Rechner. Mit <code>git clone</code> wird jede einzelne Version jeder einzelnen Datei in der Historie des
  Repositorys heruntergeladen. Wenn ein Repository auf einem Server einmal
  beschädigt wird (z.B. weil die Festplatte beschädigt wird), kann man
  tatsächlich jeden beliebigen Klon des Repositorys verwenden, um das
  Repository auf dem Server wieder in dem Zustand wieder herzustellen, in dem
  es sich befand, als es geklont wurde. (Es kann passieren, dass man einige auf
  dem Server vorhandenen Hooks verliert, aber alle versionierten Daten bleiben
  erhalten. In Kapitel 4 gehen wir darauf noch einmal genauer ein.)
</p>

<p>
  Du kannst ein Repository mit dem Befehl <code>git clone [url]</code> klonen. Um beispielsweise das Repository der Ruby Git Bibliothek Grit zu
  klonen, führst Du den folgenden Befehl aus:
</p>

<pre>
$ git clone git://github.com/schacon/grit.git
</pre>

<p>
  Git legt dann ein Verzeichnis <code>grit</code> an, initialisiert ein <code>.git</code> Verzeichnis darin, lädt alle Daten des Repositorys herunter, und checkt eine
  Arbeitskopie der letzten Version aus. Wenn Du in das neue <code>grit</code> Verzeichnis wechselst, findest Du dort die in diesem Projekt enthaltenen
  Dateien und kannst sie benutzen oder bearbeiten. Wenn Du das Repository in
  ein Verzeichnis mit einem anderen Namen als <code>grit</code> klonen willst, kannst Du das wie folgt angeben:
</p>

<pre>
$ git clone git://github.com/schacon/grit.git mygrit
</pre>

<p>
  Dieser Befehl tut das gleiche wie der vorhergehende, aber das Zielverzeichnis
  ist diesmal <code>mygrit</code>.
</p>

<p>
  Git unterstützt eine Reihe unterschiedlicher Übertragungsprotokolle. Das
  vorhergehende Beispiel verwendet das <code>git://</code> Protokoll, aber Du wirst auch auf <code>http(s)://</code> oder <code>user@server:/path.git</code> treffen, die das SSH Protokoll verwenden. In Kapitel 4 gehen wir auf die
  verfügbaren Optionen (und deren Vor- und Nachteile) ein, die ein Server hat,
  um Zugriff auf ein Git Repository zu ermöglichen.
</p>

<h2>Änderungen am Repository nachverfolgen</h2>

<p>
  Du hast jetzt ein voll funktionsfähiges Git Repository und eine Arbeitskopie
  des Projekts ist in Deinem Verzeichnis ausgecheckt. Du kannst nun die Dateien
  im Projekt bearbeiten. Immer wenn Dein Projekt einen Zustand erreicht hat,
  den Du festhalten willst, musst Du diese Änderungen einchecken.
</p>

<p>
  Jede Datei in Deinem Arbeitsverzeichnis kann sich in einem von zwei Zuständen
  befinden: Änderungen werden verfolgt (engl. tracked) oder nicht (engl.
  untracked). Alle Dateien, die sich im letzten Snapshot (Commit) befanden,
  werden in der Versionskontrolle verfolgt. Sie können entweder unverändert
  (engl. unmodified), modifiziert (engl. modified) oder für den nächsten Commit
  vorgemerkt (engl. staged) sein. Alle anderen Dateien in Deinem
  Arbeitsverzeichnis dagegen sind nicht versioniert: das sind all diejenigen
  Dateien, die nicht schon im letzten Snapshot enthalten waren und die sich
  nicht in der Staging Area befinden. Wenn Du ein Repository gerade geklont
  hast, sind alle Dateien versioniert und unverändert – Du hast sie gerade
  ausgecheckt aber noch nicht verändert.
</p>

<p>
  Sobald Du versionierte Dateien bearbeitest, wird Git sie als modifiziert
  erkennen, weil Du sie seit dem letzten Commit geändert hast. Du merkst diese
  geänderten Dateien für den nächsten Commit vor (d.h. Du fügst sie zur Staging
  Area hinzu bzw. Du stagest sie), legst aus allen markierten Änderungen einen
  Commit an und der Vorgang beginnt von vorn. Bild 2-1 stellt diesen Zyklus
  dar:
</p>

<p>
  Insert 18333fig0201.png Bild 2-1. Zyklus der Grundzustände Deiner Dateien
</p>

<h3>Den Zustand Deiner Dateien prüfen</h3>

<p>
  Das wichtigste Hilfsmittel, um den Zustand zu überprüfen, in dem sich die
  Dateien in Deinem Repository gerade befinden, ist der Befehl <code>git status</code>. Wenn Du diesen Befehl unmittelbar nach dem Klonen eines Repositorys
  ausführst, sollte er folgende Ausgabe liefern:
</p>

<pre>
$ git status
On branch master
nothing to commit, working directory clean
</pre>

<p>
  Dieser Zustand wird auch als sauberes Arbeitsverzeichnis (engl. clean working
  directory) bezeichnet. Mit anderen Worten, es gibt keine Dateien, die unter
  Versionskontrolle stehen und seit dem letzten Commit geändert wurden –
  andernfalls würden sie hier aufgelistet werden. Außerdem teilt Dir der Befehl
  mit, in welchem Branch Du Dich gerade befindest. In diesem Beispiel ist dies
  der Branch <code>master</code>. Mach Dir darüber im Moment keine Gedanken, wir werden im nächsten Kapitel
  auf Branches detailliert eingehen.
</p>

<p>
  Sagen wir Du fügst eine neue <code>README</code> Datei zu Deinem Projekt hinzu. Wenn die Datei zuvor nicht existiert hat und
  Du jetzt <code>git status</code> ausführst, zeigt Git die bisher nicht versionierte Datei wie folgt an:
</p>

<pre>
$ vim README
$ git status
On branch master
Untracked files:
  (use "git add &lt;file&gt;..." to include in what will be committed)

        README

nothing added to commit but untracked files present (use "git add" to track)
</pre>

<p>
  Alle Dateien, die in der Sektion „Untracked files“ aufgelistet werden, sind
  Dateien, die bisher noch nicht versioniert sind. Dort wird jetzt auch die
  Datei <code>README</code> angezeigt. Mit anderen Worten, die Datei <code>README</code> wird in diesem Bereich gelistet, weil sie im letzen Snapshot (Commit) von
  Git nicht enthalten ist. Git nimmt eine solche Datei nicht automatisch in die
  Versionskontrolle auf, sondern man muss Git dazu ausdrücklich auffordern.
  Ansonsten würden generierte Binärdateien oder andere Dateien, die Du nicht in
  Deinem Repository haben willst, automatisch hinzugefügt werden. Das möchte
  man in den meisten Fällen vermeiden. Jetzt wollen wir aber Änderungen an der
  Datei <code>README</code> verfolgen und fügen sie deshalb zur Versionskontrolle hinzu.
</p>

<h3>Neue Dateien zur Versionskontrolle hinzufügen</h3>

<p>
  Um eine neue Datei zur Versionskontrolle hinzuzufügen, verwendest Du den
  Befehl <code>git add</code>. Für Deine neue <code>README</code> Datei kannst Du ihn wie folgt ausführen:
</p>

<pre>
$ git add README
</pre>

<p>
  Wenn Du den <code>git status</code> Befehl erneut ausführst, siehst Du, dass sich Deine <code>README</code> Datei jetzt unter Versionskontrolle befindet und für den nächsten Commit
  vorgemerkt ist (gestaged ist):
</p>

<pre>
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD &lt;file&gt;..." to unstage)

        new file:   README
</pre>

<p>
  Dass die Datei für den nächsten Commit vorgemerkt ist, siehst Du daran, dass
  sie in der Sektion „Changes to be committed“ aufgelistet ist. Wenn Du jetzt
  einen Commit anlegst, wird der Snapshot den Zustand der Datei beinhalten, den
  sie zum Zeitpunkt des Befehls <code>git add</code> hatte. Du erinnerst Dich daran, dass Du, als Du vorhin <code>git init</code> ausgeführt hast, anschließend <code>git add</code> ausgeführt hast: an dieser Stelle hast Du die Dateien in Deinem Verzeichnis
  der Versionskontrolle hinzugefügt. Der <code>git add</code> Befehl akzeptiert einen Pfadnamen einer Datei oder eines Verzeichnisses.
  Wenn Du ein Verzeichnis angibst, fügt <code>git add</code> alle Dateien in diesem Verzeichnis und allen Unterverzeichnissen rekursiv
  hinzu.
</p>

<h3>Geänderte Dateien stagen</h3>

<p>
  Wenn Du eine bereits versionierte Datei <code>benchmarks.rb</code> änderst und den <code>git status</code> Befehl ausführst, erhältst Du folgendes:
</p>

<pre>
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD &lt;file&gt;..." to unstage)

        new file:   README

Changes not staged for commit:
  (use "git add &lt;file&gt;..." to update what will be committed)
  (use "git checkout -- &lt;file&gt;..." to discard changes in working directory)

        modified:   benchmarks.rb
</pre>

<p>
  Die Datei <code>benchmarks.rb</code> erscheint in der Sektion „Changes not staged for commit“ – d.h., dass eine
  versionierte Datei im Arbeitsverzeichnis verändert worden ist, aber noch
  nicht für den Commit vorgemerkt wurde. Um sie vorzumerken, führst Du den
  Befehl <code>git add</code> aus. (<code>git add</code> wird zu verschiedenen Zwecken eingesetzt. Man verwendet ihn, um neue Dateien
  zur Versionskontrolle hinzuzufügen, Dateien für einen Commit zu markieren und
  verschiedene andere Dinge – beispielsweise, einen Konflikt aus einem Merge
  als aufgelöst zu kennzeichnen.)
</p>

<pre>
$ git add benchmarks.rb
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD &lt;file&gt;..." to unstage)

        new file:   README
        modified:   benchmarks.rb
</pre>

<p>
  Beide Dateien sind nun für den nächsten Commit vorgemerkt. Nehmen wir an, Du
  willst jetzt aber noch eine weitere Änderung an der Datei <code>benchmarks.rb</code> vornehmen, bevor Du den Commit tatsächlich anlegst. Du öffnest die Datei und
  änderst sie. Jetzt könntest Du den Commit anlegen. Aber zuvor führen wir noch
  mal <code>git status</code> aus:
</p>

<pre>
$ vim benchmarks.rb
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD &lt;file&gt;..." to unstage)

        new file:   README
        modified:   benchmarks.rb

Changes not staged for commit:
  (use "git add &lt;file&gt;..." to update what will be committed)
  (use "git checkout -- &lt;file&gt;..." to discard changes in working directory)

        modified:   benchmarks.rb
</pre>

<p>
  Huch, was ist das? Jetzt wird <code>benchmarks.rb</code> sowohl in der Staging Area als auch als geändert aufgelistet. Die Erklärung
  dafür ist, dass Git eine Datei in exakt dem Zustand für den Commit vormerkt,
  in dem sie sich befindet, wenn Du den Befehl <code>git add</code> ausführst. Wenn Du den Commit jetzt anlegst, wird die Version der Datei <code>benchmarks.rb</code> diejenigen Inhalte haben, die sie hatte, als Du <code>git add</code> zuletzt ausgeführt hast – nicht diejenigen, die sie in dem Moment hat, wenn
  Du den Commit anlegst. Wenn Du stattdessen die gegenwärtige Version im Commit
  haben willst, kannst Du einfach erneut <code>git add</code> ausführen:
</p>

<pre>
$ git add benchmarks.rb
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD &lt;file&gt;..." to unstage)

        new file:   README
        modified:   benchmarks.rb
</pre>

<h3>Dateien ignorieren</h3>

<p>
  Du wirst in der Regel eine Reihe von Dateien in Deinem Projektverzeichnis
  haben, die Du nicht versionieren bzw. im Repository haben willst, wie z.B.
  automatisch generierte Dateien, wie Logdateien oder Dateien, die Dein
  Build-System erzeugt. In solchen Fällen kannst Du in der Datei <code>.gitignore</code> alle Dateien oder Dateimuster angeben, die Du ignorieren willst.
</p>

<pre>
$ cat .gitignore
*.[oa]
*~
</pre>

<p>
  Die erste Zeile weist Git an, alle Dateien zu ignorieren, die mit einem <code>.o</code> oder <code>.a</code> enden (also Objekt- und Archiv-Dateien, die von Deinem Build-System erzeugt
  werden). Die zweite Zeile bewirkt, dass alle Dateien ignoriert werden, die
  mit einer Tilde (<code>~</code>) enden. Viele Texteditoren speichern ihre temporären Dateien auf diese
  Weise, wie bespielsweise Emacs. Du kannst außerdem Verzeichnisse wie <code>log</code>, <code>tmp</code> oder <code>pid</code> hinzufügen, automatisch erzeugte Dokumentation, und so weiter. Es ist
  normalerweise empfehlenswert, eine <code>.gitignore</code> Datei anzulegen, bevor man mit der eigentlichen Arbeit anfängt, damit man
  nicht versehentlich Dateien ins Repository hinzufügt, die man dort nicht
  wirklich haben will.
</p>

<p>Folgende Regeln gelten in einer <code>.gitignore</code> Datei:</p>

<ul>
  <li>
    <p>
      Leere Zeilen oder Zeilen, die mit <code>#</code> beginnen, werden ignoriert.
    </p>
  </li>

  <li>
    <p>Standard <code>glob</code> Muster funktionieren.</p>
  </li>

  <li>
    <p>
      Du kannst ein Muster mit einem Schrägstrich (<code>/</code>) abschließen, um ein Verzeichnis zu deklarieren.
    </p>
  </li>

  <li>
    <p>
      Du kannst ein Muster negieren, indem Du ein Ausrufezeichen (<code>!</code>) voranstellst.
    </p>
  </li>
</ul>

<p>
  Glob Muster sind vereinfachte reguläre Ausdrücke, die von der Shell verwendet
  werden. Ein Stern (<code>*</code>) bezeichnet „kein oder mehrere Zeichen“; <code>[abc]</code> bezeichnet eines der in den eckigen Klammern angegebenen Zeichen (in diesem
  Fall also <code>a</code>, <code>b</code> oder <code>c</code>); ein Fragezeichen (<code>?</code>) bezeichnet ein beliebiges, einzelnes Zeichen; und eckige Klammern mit
  Zeichen, die von einem Bindestrich getrennt werden (<code>[0-9]</code>) bezeichnen ein Zeichen aus der jeweiligen Menge von Zeichen (in diesem Fall
  also aus der Menge der Zeichen von 0 bis 9).
</p>

<p>Hier ist ein weiteres Beispiel für eine <code>.gitignore</code> Datei:</p>

<pre>
# ein Kommentar - dieser wird ignoriert
# ignoriert alle Dateien, die mit .a enden
*.a
# nicht aber lib.a Dateien (obwohl obige Zeile *.a ignoriert)
!lib.a
# ignoriert eine TODO Datei nur im Wurzelverzeichnis, nicht aber
/TODO
# ignoriert alle Dateien im build/ Verzeichnis
build/
# ignoriert doc/notes.txt, aber nicht doc/server/arch.txt
doc/*.txt
# ignoriert alle .txt Dateien unterhalb des doc/ Verzeichnis
doc/**/*.txt
</pre>

<p>
  Die Kombination <code>**/</code> wurde in der Git Version 1.8.2 eingeführt.
</p>

<h3>Die Änderungen in der Staging Area durchsehen</h3>

<p>
  Wenn Dir die Ausgabe des Befehl <code>git status</code> nicht aussagekräftig genug ist, weil Du exakt wissen willst, was sich
  geändert hat – und nicht lediglich, welche Dateien geändert wurden – kannst
  Du den <code>git diff</code> Befehl verwenden. Wir werden <code>git diff</code> später noch einmal im Detail besprechen, aber Du wirst diesen Befehl in der
  Regel verwenden wollen, um eine der folgenden, zwei Fragen zu beantworten:
  Was hast Du geändert, aber noch nicht für einen Commit vorgemerkt? Und welche
  Änderungen hast Du für einen Commit bereits vorgemerkt? Während <code>git status</code> diese Fragen nur mit Dateinamen beantwortet, zeigt Dir <code>git diff</code> exakt an, welche Zeilen hinzugefügt, geändert und entfernt wurden. Dies
  entspricht gewissermaßen einem Patch.
</p>

<p>
  Nehmen wir an, Du hast die Datei <code>README</code> geändert und für einen Commit in der Staging Area vorgemerkt. Dann änderst
  Du außerdem die Datei <code>benchmarks.rb</code>, fügst sie aber noch nicht zur Staging Area hinzu. Wenn Du den <code>git status</code> Befehl dann ausführst, zeigt er Dir in etwa Folgendes an:
</p>

<pre>
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD &lt;file&gt;..." to unstage)

        new file:   README

Changes not staged for commit:
  (use "git add &lt;file&gt;..." to update what will be committed)
  (use "git checkout -- &lt;file&gt;..." to discard changes in working directory)

        modified:   benchmarks.rb
</pre>

<p>
  Um festzustellen, welche Änderungen Du bisher nicht gestaged hast, führe <code>git diff</code> ohne irgendwelche weiteren Argumente aus:
</p>

<pre>
$ git diff
diff --git a/benchmarks.rb b/benchmarks.rb
index 3cb747f..da65585 100644
--- a/benchmarks.rb
+++ b/benchmarks.rb
@@ -36,6 +36,10 @@ def main
           @commit.parents[0].parents[0].parents[0]
         end

+        run_code(x, 'commits 1') do
+          git.commits.size
+        end
+
         run_code(x, 'commits 2') do
           log = git.commits('master', 15)
           log.size
</pre>

<p>
  Dieser Befehl vergleicht die Inhalte Deines Arbeitsverzeichnisses mit den
  Inhalten Deiner Staging Area. Das Ergebnis zeigt Dir die Änderungen, die Du
  an Dateien im Arbeitsverzeichnis vorgenommen, aber noch nicht für den
  nächsten Commit vorgemerkt hast.
</p>

<p>
  Wenn Du sehen willst, welche Änderungen in der Staging Area und somit für den
  nächsten Commit vorgesehen sind, kannst Du <code>git diff --cached</code> verwenden. (Ab der Version Git 1.6.1 kannst Du außerdem <code>git diff --staged</code> verwenden, was vielleicht leichter zu merken ist.) Dieser Befehl vergleicht
  die Inhalte der Staging Area mit dem letzten Commit:
</p>

<pre>
$ git diff --cached
diff --git a/README b/README
new file mode 100644
index 0000000..03902a1
--- /dev/null
+++ b/README2
@@ -0,0 +1,5 @@
+grit
+ by Tom Preston-Werner, Chris Wanstrath
+ http://github.com/mojombo/grit
+
+Grit is a Ruby library for extracting information from a Git repository
</pre>

<p>
  Es ist wichtig, im Kopf zu behalten, dass <code>git diff</code> nicht alle Änderungen seit dem letzten Commit anzeigt – er zeigt lediglich
  diejenigen Änderungen an, die noch nicht in der Staging Area sind. Das kann
  verwirrend sein. Wenn Du all Deine Änderungen bereits für einen Commit
  vorgemerkt hast, zeigt <code>git diff</code> überhaupt nichts an.
</p>

<p>
  Ein anderes Beispiel: Wenn Du Änderungen an der Datei <code>benchmarks.rb</code> bereits zur Staging Area hinzugefügt hast und sie dann anschließend noch mal
  änderst, kannst Du <code>git diff</code> verwenden, um diese letzten Änderungen anzuzeigen, die noch nicht in der
  Staging Area sind:
</p>

<pre>
$ git add benchmarks.rb
$ echo '# test line' &gt;&gt; benchmarks.rb
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD &lt;file&gt;..." to unstage)

        modified:   benchmarks.rb

Changes not staged for commit:
  (use "git add &lt;file&gt;..." to update what will be committed)
  (use "git checkout -- &lt;file&gt;..." to discard changes in working directory)

        modified:   benchmarks.rb
</pre>

<p>
  Jetzt kannst Du <code>git diff</code> verwenden, um zu sehen, was noch nicht für den nächsten Commit vorgemerkt
  ist:
</p>

<pre>
$ git diff
diff --git a/benchmarks.rb b/benchmarks.rb
index e445e28..86b2f7c 100644
--- a/benchmarks.rb
+++ b/benchmarks.rb
@@ -127,3 +127,4 @@ end
 main()

 ##pp Grit::GitRuby.cache_client.stats
+# test line
</pre>

<p>
  und <code>git diff --cached</code>, um zu sehen, was für den nächsten Commit vorgesehen ist:
</p>

<pre>
$ git diff --cached
diff --git a/benchmarks.rb b/benchmarks.rb
index 3cb747f..e445e28 100644
--- a/benchmarks.rb
+++ b/benchmarks.rb
@@ -36,6 +36,10 @@ def main
          @commit.parents[0].parents[0].parents[0]
        end

+        run_code(x, 'commits 1') do
+          git.commits.size
+        end
+
        run_code(x, 'commits 2') do
          log = git.commits('master', 15)
          log.size
</pre>

<h3>Einen Commit erzeugen</h3>

<p>
  Nachdem Du jetzt alle Änderungen, die Du im nächsten Commit haben willst, in
  Deiner Staging Area gesammelt hast, kannst Du den Commit anlegen. Denke
  daran, dass Änderungen, die nicht in der Staging Area sind (also alle
  Änderungen, die Du vorgenommen hast, seit Du zuletzt <code>git add</code> ausgeführt hast), auch nicht in den Commit aufgenommen werden. Sie werden
  ganz einfach weiterhin als geänderte Dateien im Arbeitsverzeichnis
  verbleiben. In unserem Beispiel haben wir gesehen, dass alle Änderungen
  vorgemerkt waren, als wir zuletzt <code>git status</code> ausgeführt haben, also können wir den Commit jetzt anlegen. Das geht am
  einfachsten mit dem Befehl:
</p>

<pre>
$ git commit
</pre>

<p>
  Wenn Du diesen Befehl ausführst, wird Git den Texteditor Deiner Wahl starten.
  (D.h. denjenigen Texteditor, der durch die <code>$EDITOR</code> Variable Deiner Shell angegeben wird – normalerweise ist das vim oder emacs,
  aber Du kannst jeden Editor Deiner Wahl angeben. Wie in Kapitel 1 besprochen,
  kannst Du dazu <code>git config --global core.editor</code> verwenden.)
</p>

<p>
  Der Editor zeigt in etwa folgenden Text an (dies ist ein Beispiel mit vim):
</p>

<pre>
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
#       new file:   README
#       modified:   benchmarks.rb
#
~
~
~
".git/COMMIT_EDITMSG" 10L, 283C
</pre>

<p>
  Du siehst, dass die vorausgefüllte Commit Meldung die Ausgabe des letzten <code>git status</code> Befehls als einen Kommentar und darüber eine leere Zeile enthält. Du kannst
  die Kommentare entfernen und Deine eigene Meldung einfügen. Oder Du kannst
  sie stehen lassen, damit Du siehst, was im Commit enthalten sein wird. (Um
  die Änderungen noch detaillierter sehen zu können, kannst Du den Befehl <code>git commit</code> mit der Option <code>-v</code> verwenden. Das fügt zusätzlich das Diff Deiner Änderungen im Editor ein,
  sodass Du exakt sehen kannst, was sich im Commit befindet.) Wenn Du den
  Texteditor beendest, erzeugt Git den Commit mit der gegebenen Meldung (d.h.,
  ohne den Kommentar und das Diff).
</p>

<p>
  Alternativ kannst Du die Commit Meldung direkt mit dem Befehl <code>git commit</code> angeben, indem Du die Option <code>-m</code> wie folgt verwendest:
</p>

<pre>
$ git commit -m "Story 182: Fix benchmarks for speed"
[master 463dc4f] Fix benchmarks for speed
 2 files changed, 3 insertions(+)
 create mode 100644 README
</pre>

<p>
  Du hast jetzt Deinen ersten Commit angelegt! Git zeigt Dir als Rückmeldung
  einige Details über den neu angelegten Commit an: in welchem Branch er sich
  befindet (master), welche SHA-1 Checksumme er hat (<code>463dc4f</code>, in diesem Fall nur die Kurzform), wie viele Dateien geändert wurden und
  eine Zusammenfassung über die insgesamt neu hinzugefügten und entfernten
  Zeilen in diesem Commit.
</p>

<p>
  Denke daran, dass jeder neue Commit denjenigen Snapshot aufzeichnet, den Du
  in der Staging Area vorbereitet hast. Änderungen, die nicht in der Staging
  Area waren, werden weiterhin als modifizierte Dateien im Arbeitsverzeichnis
  vorliegen. Jedes Mal wenn Du einen Commit anlegst, zeichnest Du einen
  Snapshot Deines Projektes auf, zu dem Du zurückkehren oder mit dem Du spätere
  Änderungen vergleichen kannst.
</p>

<h3>Die Staging Area überspringen</h3>

<p>
  Obwohl die Staging Area unglaublich nützlich ist, um genau diejenigen Commits
  anzulegen, die Du in Deiner Projekt Historie haben willst, ist sie manchmal
  auch ein bisschen umständlich. Git stellt Dir deshalb eine Alternative zur
  Verfügung, mit der Du die Staging Area überspringen kannst. Wenn Du den
  Befehl <code>git commit</code> mit der Option <code>-a</code> ausführst, übernimmt Git automatisch alle Änderungen an dejenigen Dateien,
  die sich bereits unter Versionskontrolle befinden, in den Commit – sodass Du
  auf diese Weise den Schritt <code>git add</code> weglassen kannst:
</p>

<pre>
$ git status
On branch master
Changes not staged for commit:
  (use "git add &lt;file&gt;..." to update what will be committed)
  (use "git checkout -- &lt;file&gt;..." to discard changes in working directory)

        modified:   benchmarks.rb

no changes added to commit (use "git add" and/or "git commit -a")
$ git commit -a -m 'added new benchmarks'
[master 83e38c7] added new benchmarks
 1 files changed, 5 insertions(+)
</pre>

<p>
  Beachte, dass Du in diesem Fall <code>git add</code> zuvor noch nicht ausgeführt hast, die Änderungen an <code>benchmarks.rb</code> aber dennoch in den Commit übernommen werden.
</p>

<h3>Dateien entfernen</h3>

<p>
  Um eine Datei aus der Git Versionskontrolle zu entfernen, muss diese von den
  verfolgten Dateien (genauer, aus der Staging Area) entfernt werden und dann
  mit einem Commit bestätigt werden. Der Befehl <code>git rm</code> tut genau das – und löscht die Datei außerdem aus dem Arbeitsverzeichnis,
  sodass sie dort nicht unbeabsichtigt (als eine nun unversionierte Datei)
  liegen bleibt.
</p>

<p>
  Wenn Du einfach nur eine Datei aus dem Arbeitsverzeichnis löschst, wird sie
  in der Sektion „Changes not staged for commit“ angezeigt, wenn Du <code>git status</code> ausführst:
</p>

<pre>
$ rm grit.gemspec
$ git status
On branch master
Changes not staged for commit:
  (use "git add/rm &lt;file&gt;..." to update what will be committed)
  (use "git checkout -- &lt;file&gt;..." to discard changes in working directory)

        deleted:    grit.gemspec

no changes added to commit (use "git add" and/or "git commit -a")
</pre>

<p>
  Wenn Du jetzt <code>git rm</code> ausführst, wird diese Änderung für den nächsten Commit in der Staging Area
  vorgemerkt:
</p>

<pre>
$ git rm grit.gemspec
rm 'grit.gemspec'
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD &lt;file&gt;..." to unstage)

        deleted:    grit.gemspec
</pre>

<p>
  Nach dem nächsten Anlegen eines Commits, wird die Datei nicht mehr im
  Arbeitsverzeichnis liegen und sich nicht länger unter Versionskontrolle
  befinden. Wenn Du die Datei zuvor geändert und diese Änderung bereits zur
  Staging Area hinzugefügt hattest, musst Du die Option <code>-f</code> verwenden, um zu erzwingen, dass sie gelöscht wird. Dies ist eine
  Sicherheitsmaßnahme, um zu vermeiden, dass Du versehentlich Daten löschst,
  die sich bisher noch nicht als Commit Snapshot in der Historie Deines
  Projektes befinden – und deshalb auch nicht wiederhergestellt werden können.
</p>

<p>
  Ein anderer Anwendungsfall für <code>git rm</code> ist, dass Du eine Datei in Deinem Arbeitsverzeichnis behalten, aber aus der
  Staging Area nehmen willst. In anderen Worten, Du willst die Datei nicht
  löschen, sondern aus der Versionskontrolle nehmen. Das könnte zum Beispiel
  der Fall sein, wenn Du vergessen hattest, eine Datei in <code>.gitignore</code> anzugeben und sie versehentlich zur Versionskontrolle hinzugefügt hast,
  beispielsweise eine große Logdatei oder eine Reihe kompilierter <code>.a</code> Dateien. Hierzu kannst Du dann die <code>--cached</code> Option verwenden:
</p>

<pre>
$ git rm --cached readme.txt
</pre>

<p>
  Der <code>git rm</code> Befehl akzeptiert Dateien, Verzeichnisse und <code>glob</code> Dateimuster. D.h., Du kannst z.B. folgendes tun:
</p>

<pre>
$ git rm log/\*.log
</pre>

<p>
  Beachte den Backslash (<code>\</code>) vor dem Stern (<code>*</code>). Er ist nötig, weil Git Dateinamen zusätzlich zur Dateinamen-Expansion
  Deiner Shell selbst vervollständigt. D.h., dieser Befehl entfernt alle
  Dateien, die die Erweiterung <code>.log</code> haben und sich im <code>/log</code> Verzeichnis befinden. Ein anderes Beispiel ist:
</p>

<pre>
$ git rm \*~
</pre>

<p>
  Dieser Befehl entfernt alle Dateien, die mit einer Tilde (<code>~</code>) aufhören.
</p>

<h3>Dateien verschieben</h3>

<p>
  Anders als andere VCS Systeme verfolgt Git nicht explizit, ob Dateien
  verschoben werden. Wenn Du eine Datei umbenennst, werden darüber keine
  Metadaten in der Historie gespeichert. Stattdessen ist Git schlau genug,
  solche Dinge im Nachhinein zu erkennen. Wir werden uns damit später noch
  befassen.
</p>

<p>
  Es ist allerdings ein bisschen verwirrend, dass Git trotzdem einen <code>git mv</code> Befehl kennt. Wenn Du eine Datei umbenennen willst, kannst Du folgendes tun:
</p>

<pre>
$ git mv file_from file_to
</pre>

<p>
  Das funktioniert einwandfrei. Wenn Du diesen Befehl ausführst und danach den <code>git status</code> ausführst, zeigt Git an, dass die Datei umbenannt wurde:
</p>

<pre>
$ git mv README.txt README
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD &lt;file&gt;..." to unstage)

        renamed:    README.txt -&gt; README
</pre>

<p>Allerdings kannst Du genauso folgendes tun:</p>

<pre>
$ mv README.txt README
$ git rm README.txt
$ git add README
</pre>

<p>
  Git ist clever genug, selbst herauszufinden, dass Du die Datei umbenannt
  hast. Du brauchst dies also nicht explizit mit dem <code>git mv</code> Befehl zu tun. Der einzige Unterschied ist, dass Du mit <code>git mv</code> nur einen Befehl, nicht drei, ausführen musst – das ist natürlich etwas
  bequemer. Darüberhinaus kannst Du aber Dateien auf jede beliebige Art und
  Weise extern umbenennen und dann später <code>git add</code> bzw. <code>git rm</code> verwenden, wenn Du einen Commit zusammenstellst.
</p>

<h2>Die Commit Historie anzeigen</h2>

<p>
  Nachdem Du einige Commits angelegt oder ein bestehendes Repository geklont
  hast, willst Du vielleicht wissen, welche Änderungen zuletzt vorgenommen
  wurden. Der grundlegende und mächtige Befehl, mit dem Du das tun kannst, ist <code>git log</code>.
</p>

<p>
  Die folgende Beispiele beziehen sich auf ein sehr simples Repository mit dem
  Namen „simplegit“, das ich oft für Demonstationszwecke verwende:
</p>

<pre>
git clone git://github.com/schacon/simplegit-progit.git
</pre>

<p>
  Wenn Du in diesem Projekt <code>git log</code> ausführst, solltest Du eine Ausgabe wie die folgende sehen:
</p>

<pre>
$ git log
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon &lt;schacon@gee-mail.com&gt;
Date:   Mon Mar 17 21:52:11 2008 -0700

    changed the version number

commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Author: Scott Chacon &lt;schacon@gee-mail.com&gt;
Date:   Sat Mar 15 16:40:33 2008 -0700

    removed unnecessary test code

commit a11bef06a3f659402fe7563abf99ad00de2209e6
Author: Scott Chacon &lt;schacon@gee-mail.com&gt;
Date:   Sat Mar 15 10:31:28 2008 -0700

    first commit
</pre>

<p>
  Der Befehl <code>git log</code> listet die Historie der Commits eines Projekts in umgekehrter
  chronologischer Reihenfolge auf, wenn man ihn ohne weitere Argumente
  ausführt, d.h. die letzten Commits stehen oben. Wie Du sehen kannst wird
  jeder Commit mit seiner SHA-1 Checksumme, Namen und E-Mail Adresse des
  Autors, dem Datum und der Commit Meldung aufgelistet.
</p>

<p>
  Für den Befehl <code>git log</code> gibt es eine riesige Anzahl von Optionen, mit denen man sehr genau
  eingrenzen kann, wonach man in einer Historie sucht. Schauen wir uns also
  einige der am häufigsten verwendeten Optionen an.
</p>

<p>
  Eine sehr nützliche Option ist <code>-p</code>. Sie zeigt die Änderungen an, die in einem Commit enthalten sind. Du kannst
  außerdem -2 angeben, wodurch nur die letzten beiden Einträge angezeigt
  werden:
</p>

<pre>
$ git log -p -2
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon &lt;schacon@gee-mail.com&gt;
Date:   Mon Mar 17 21:52:11 2008 -0700

    changed the version number

diff --git a/Rakefile b/Rakefile
index a874b73..8f94139 100644
--- a/Rakefile
+++ b/Rakefile
@@ -5,5 +5,5 @@ require 'rake/gempackagetask'
 spec = Gem::Specification.new do |s|
     s.name      =   "simplegit"
-    s.version   =   "0.1.0"
+    s.version   =   "0.1.1"
     s.author    =   "Scott Chacon"
     s.email     =   "schacon@gee-mail.com

commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Author: Scott Chacon &lt;schacon@gee-mail.com&gt;
Date:   Sat Mar 15 16:40:33 2008 -0700

    removed unnecessary test code

diff --git a/lib/simplegit.rb b/lib/simplegit.rb
index a0a60ae..47c6340 100644
--- a/lib/simplegit.rb
+++ b/lib/simplegit.rb
@@ -18,8 +18,3 @@ class SimpleGit
     end

 end
-
-if $0 == __FILE__
-  git = SimpleGit.new
-  puts git.show
-end
\ No newline at end of file
</pre>

<p>
  Diese Option zeigt also im Prinzip die gleiche Information wie zuvor, aber
  zusätzlich zu jedem Eintrag ein Diff. Das ist nützlich, um einen Code Review
  zu machen oder eben mal eine Reihe von Commits durchzuschauen, die ein
  Mitarbeiter angelegt hat.
</p>

<p>
  Manchmal ist es einfacher Änderungen an Hand der Wörter anstatt zeilenbasiert
  zu überprüfen. Git bietet dafür die Option <code>--word-diff</code>, welche man an den Befehl <code>git log -p</code> anhängen kann. Man weist Git damit an, einen Vergleich auf Basis der Wörter
  anstatt Zeile für Zeile durchzuführen. Dieser Vergleich ist ziemlich nutzlos
  wenn man Änderungen innerhalb von Quellcode vergleicht. Beim Vergleich von
  langen Textdateien zeigt er aber seine Stärke. Er bietet sich zum Beispiel
  für Bücher oder wissenschaftliche Texte an. Hierzu ein Beispiel:
</p>

<pre>
$ git log -U1 --word-diff
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon &lt;schacon@gee-mail.com&gt;
Date:   Mon Mar 17 21:52:11 2008 -0700

    changed the version number

diff --git a/Rakefile b/Rakefile
index a874b73..8f94139 100644
--- a/Rakefile
+++ b/Rakefile
@@ -7,3 +7,3 @@ spec = Gem::Specification.new do |s|
    s.name      =   "simplegit"
    s.version   =   [-"0.1.0"-]{+"0.1.1"+}
    s.author    =   "Scott Chacon"
</pre>

<p>
  Wie man in der Ausgabe sehen kann, zeigt dieser Vergleich nicht an, welche
  Zeilen hinzugekommen und welche entfallen sind. Stattdessen werden Änderungen
  innerhalb der Zeilen dargestellt. Mit der Sequenz <code>{+ +}</code> wird ein neu hinzugekommes Wort gekennzeichnet, mit <code>[- -]</code> ein Wort, welches entfernt wurde. Normalerweise zeigt Git bei einem
  Vergleich drei zusätzliche Zeilen ober- und unterhalb der eigentlichen
  Änderung an. Bei einem Textvergleich reicht meist eine zusätzliche Zeile. Man
  kann dies mit der Option <code>-U1</code> erreichen, so wie in dem oben gezeigten Beispiel.
</p>

<p>
  Außerdem gibt es verschiedene Optionen, die nützlich sind, um Dinge
  zusammenzufassen. Beispielsweise kannst Du eine kurze Statistik über jeden
  Commit mit der Option <code>--stat</code> anzeigen lassen:
</p>

<pre>
$ git log --stat
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon &lt;schacon@gee-mail.com&gt;
Date:   Mon Mar 17 21:52:11 2008 -0700

    changed the version number

 Rakefile |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Author: Scott Chacon &lt;schacon@gee-mail.com&gt;
Date:   Sat Mar 15 16:40:33 2008 -0700

    removed unnecessary test code

 lib/simplegit.rb |    5 -----
 1 file changed, 5 deletions(-)

commit a11bef06a3f659402fe7563abf99ad00de2209e6
Author: Scott Chacon &lt;schacon@gee-mail.com&gt;
Date:   Sat Mar 15 10:31:28 2008 -0700

    first commit

 README           |    6 ++++++
 Rakefile         |   23 +++++++++++++++++++++++
 lib/simplegit.rb |   25 +++++++++++++++++++++++++
 3 files changed, 54 insertions(+)
</pre>

<p>
  Die <code>--stat</code> Option zeigt unterhalb jedes Commits eine kurze Statistik über die
  jeweiligen Änderungen an: welche Dateien geändert wurden und wieviele Zeilen
  insgesamt hinzugefügt oder entfernt wurden. Eine weitere nützliche Option ist
  <code>--pretty</code>. Diese Option ändert das Format der Ausgabe und es gibt eine Anzahl
  mitgelieferter Formate. Das <code>oneline</code> Format listet jeden Commit in einer einzigen Zeile, was nützlich ist, wenn
  Du eine große Anzahl von Commits durchsuchen willst. Die <code>short</code>, <code>full</code> und <code>fuller</code> Formate zeigen die Commits in ähnlicher Form an, aber mit jeweils mehr oder
  weniger Informationen.
</p>

<pre>
$ git log --pretty=oneline
ca82a6dff817ec66f44342007202690a93763949 changed the version number
085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 removed unnecessary test code
a11bef06a3f659402fe7563abf99ad00de2209e6 first commit
</pre>

<p>
  Eines der interessantesten Formate ist <code>format</code>, das Dir erlaubt, Dein eigenes Format zu verwenden. Dies ist inbesondere
  nützlich, wenn Du die Ausgabe in ein anderes Programm einlesen willst (da Du
  das Format explizit angibst, kannst Du sicher sein, dass es sich nicht
  ändert, wenn Du Git auf eine neuere Version aktualisierst):
</p>

<pre>
$ git log --pretty=format:"%h - %an, %ar : %s"
ca82a6d - Scott Chacon, 11 months ago : changed the version number
085bb3b - Scott Chacon, 11 months ago : removed unnecessary test code
a11bef0 - Scott Chacon, 11 months ago : first commit
</pre>

<p>
  Tabelle 2-1 zeigt einige nützliche Optionen, die von <code>format</code> akzeptiert werden:
</p>

<pre>
Option	Beschreibung
%H	Commit Hash
%h	Abgekürzter Commit Hash
%T	Baum Hash
%t	Abgekürzter Baum Hash
%P	Eltern Hashs
%p	Abgekürzte Eltern Hashs
%an	Autor Name
%ae	Autor E-Mail
%ad	Autor Datum (format akzeptiert eine –-date= Option)
%ar	Autor Datum, relativ
%cn	Committer Name
%ce	Committer E-Mail
%cd	Committer Datum
%cr	Committer Datum, relativ
%s	Betreff
</pre>

<p>
  Du fragst Dich vielleicht, was der Unterschied zwischen Autor und Committer
  ist. Der Autor ist diejenige Person, die eine Änderung ursprünglich
  vorgenommen hat. Der Committer dagegen ist diejenige Person, die den Commit
  angelegt hat. D.h., wenn Du einen Patch an ein Projekt Team schickst und
  eines der Team Mitglieder den Patch akzeptiert und verwendet, wird beiden
  Anerkennung gezollt – sowohl Dir als Autor als auch dem Teammitglied als
  Comitter. Wir werden auf diese Unterschiedung in Kapitel 5 noch einmal
  genauer eingehen.
</p>

<p>
  Die <code>oneline</code> und <code>format</code> Optionen können außerdem zusammen mit einer weiteren Option <code>--graph</code> verwendet werden. Diese Option fügt einen netten kleinen ASCII Graphen
  hinzu, der die Branch- und Merge-Historie des Projektes anzeigt. Das kannst
  Du z.B. in Deinem Klon des Grit Projekt Repositorys sehen:
</p>

<pre>
$ git log --pretty=format:"%h %s" --graph
* 2d3acf9 ignore errors from SIGCHLD on trap
*  5e3ee11 Merge branch 'master' of git://github.com/dustin/grit
|\
| * 420eac9 Added a method for getting the current branch.
* | 30e367c timeout code and tests
* | 5a09431 add timeout protection to grit
* | e1193f8 support for heads with slashes in them
|/
* d6016bc require time for xmlschema
*  11d191e Merge branch 'defunkt' into local
</pre>

<p>
  Das sind nur einige eher simple Format Optionen für die Ausgabe von <code>git log</code> – es gibt sehr viel mehr davon. Tabelle 2-2 listet diejenigen Optionen auf,
  die wir bisher besprochen haben, und einige weitere, die besonders nützlich
  sind:
</p>

<pre>
Option	Beschreibung
-p	Zeigt den Patch, der einem Commit entspricht.
--word-diff	Führt den Vergleich Wort für Wort, anstatt Zeile für Zeile aus.
--stat	Zeigt Statistiken über die in einem Commit geänderten Dateien und eingefügten/entfernten Zeilen.
--shortstat	Zeigt nur die Kurzstatistik über eingefügte/entfernte Zeilen aus der `--stat` Option.
--name-only	Zeigt die Liste der geänderte Dateien nach der Commit Information.
--name-status	Zeigt die Liste der Dateien mit der hinzugefügt/geändert/entfernt Statistik.
--abbrev-commit	Zeigt nur die ersten Zeichen einer SHA-1 Checksumme, nicht alle 40.
--relative-date	Zeigt das Datum in relativem Format (z.B. „2 weeks ago“), nicht als vollständiges Datumsformat.
--graph	Zeigt einen ASCII Graphen der Branch- und Merge-Historie neben der Ausgabe.
--pretty	Zeigt Commits in einem alternativen Format. Gültige Optionen sind: oneline, short, full, fuller und format (mit dem Du Dein eigenes Format spezifizieren kannst)
</pre>

<h3>Log Daten filtern</h3>

<p>
  Zusätzlich zu den Formatierungsoptionen für die Ausgabe, akzeptiert <code>git log</code> eine Reihe nützlicher Optionen, um die Anzahl der ausgegebenen Commits
  einzuschränken. Eine solche Option haben wir bereits verwendet: die <code>-2</code> Option, die bewirkt, dass nur die letzten beiden Commits angezeigt werden.
  D.h., Du kannst <code>-&lt;n></code> verwenden, wobei <code>n</code> irgendeine ganze Zahl sein kann. Im Alltag wirst Du diese Option vermutlich
  nicht sehr oft verwenden, weil Git die Ausgabe standardmäßig formatiert,
  sodass nur jeweils eine Seite anzeigt.
</p>

<p>
  Darüber hinaus gibt es noch die hilfreichen Optionen <code>--since</code> und <code>--until</code>, welche die Ausgabe auf Basis der Zeitangaben eingrenzen. Beispielsweise
  gibt der folgende Befehl eine Liste aller Commits aus, die in den letzten
  zwei Wochen angelegt wurden:
</p>

<pre>
$ git log --since=2.weeks
</pre>

<p>
  Das funktioniert mit einer Reihe von Formaten. Git akzeptiert sowohl ein
  vollständiges Datum („2008-01-15“) oder ein relatives Datum wie „2 years 1
  day 3 minutes ago“.
</p>

<p>
  Du kannst außerdem die Liste der Commits nach Suchkriterien filtern. Die <code>--author</code> Option erlaubt, nach einem bestimmten Autor zu suchen, und die <code>--grep</code> Option nach Stichworten in den Commit Meldungen. (Wenn Du sowohl nach dem
  Autor als auch nach Stichworten suchen willst, musst Du zusätzlich <code>--all-match</code> angeben – andernfalls zeigt der Befehl alle Commits, die entweder das eine
  oder das andere Kriterium erfüllen.)
</p>

<p>
  Eine letzte sehr nützliche Option, die von <code>git log</code> akzeptiert wird, ist ein Pfad. Wenn Du einen Verzeichnis- oder Dateinamen
  angibst, kannst Du die Ausgabe auf Commits einschränken, die sich auf die
  jeweiligen Verzeichnisse oder Dateien beziehen. Der Pfad muss als letztes
  angegeben und mit einem doppelten Bindestrich (<code>--</code>) von den Optionen getrennt werden.
</p>

<p>Tabelle 2-3 zeigt die besprochenen und einige weitere, übliche Optionen:</p>

<pre>
Option	Beschreibung
-(n)	Begrenzt die Ausgabe auf die letzten n commits
--since, --after	Zeigt nur Commits, die nach dem angegebenen Datum angelegt wurden.
--until, --before	Zeigt nur Commits, die vor dem angegebenen Datum angelegt wurden.
--author	Zeigt nur Commits, die von dem angegebenen Autor vorgenommen wurden.
--committer	Zeigt nur Commits, die von dem angegebenen Committer angelegt wurden.
</pre>

<p>
  Um beispielweise alle Commits aus der Git Quelltext Historie anzuzeigen, die
  alle der folgende Bedinungen erfüllen:
</p>

<ul>
  <li>
    <p>Autor des Commits ist Junio Hamano</p>
  </li>

  <li>
    <p>Commit Datum Oktober 2008</p>
  </li>

  <li>
    <p>Commits, welche Änderungen im Testverzeichnis beinhalten</p>
  </li>

  <li>
    <p>Commits, welche keine Merges sind</p>
  </li>
</ul>

<p>kannst Du folgenden Befehl verwenden:</p>

<pre>
$ git log --pretty="%h - %s" --author=gitster --since="2008-10-01" \
   --before="2008-11-01" --no-merges -- t/
5610e3b - Fix testcase failure when extended attribute
acd3b9e - Enhance hold_lock_file_for_{update,append}()
f563754 - demonstrate breakage of detached checkout wi
d1a43f2 - reset --hard/read-tree --reset -u: remove un
51a94af - Fix "checkout --track -b newbranch" on detac
b0ad11e - pull: allow "git pull origin $something:$cur
</pre>

<p>
  Aus etwa 20.000 Commits in der Git Quellcode Historie, filtert dieser Befehl
  gerade einmal 6 Commits heraus, die diesen Kriterien entsprechen.
</p>

<h3>Grafische Darstellung der Historie</h3>

<p>
  Wenn Dir eine grafische Anzeige der Commit Historie lieber ist, kannst Du das
  Tcl/Tk Programm <code>gitk</code>, welches mit Git ausgeliefert wird, ausprobieren. <code>gitk</code> ist im wesentlichen eine grafische Version von <code>git log</code> und akzeptiert fast alle Filteroptionen, die <code>git log</code> auch akzeptiert. Wenn Du <code>gitk</code> in einem Projekt ausführst, siehst Du etwa folgende Ausgabe:
</p>

<p>Insert 18333fig0202.png Bild 2-2. Die gitk Oberfläche</p>

<p>
  Die Commit Historie wird in der oberen Hälfte des Fensters dargestellt.
  Daneben ein Graph, der die Branches und Merges zeigt. Nach Auswahl eines
  Commits, zeigt die Vergleichsanzeige in der unteren Hälfte des Fensters die
  jeweiligen Änderungen in diesem Commit.
</p>

<h2>Änderungen rückgängig machen</h2>

<p>
  Es kommt immer wieder mal vor, dass Du Änderungen rückgängig machen willst.
  Im Folgenden gehen wir auf einige grundlegende Möglichkeiten dazu ein. Sei
  allerdings vorsichtig damit, denn Du kannst nicht immer alles wieder
  herstellen, was Du rückgängig gemacht hast. Dies ist eine der wenigen
  Situationen in Git, in denen man Daten verlieren kann, wenn man etwas falsch
  macht.
</p>

<h3>Den letzten Commit ändern</h3>

<p>
  Manchmal hat man einen Commit zu früh angelegt und möglicherweise vergessen,
  einige Dateien hinzuzufügen, oder eine falsche Commit Meldung verwendet. Wenn
  Du den letzten Commit korrigieren willst, kannst Du dazu <code>git commit</code> zusammen mit der <code>--amend</code> Option verwenden:
</p>

<pre>
$ git commit --amend
</pre>

<p>
  Dieser Befehl verwendet Deine Staging Area für den Commit. Wenn Du seit dem
  letzten Commit keine Änderungen vorgenommen hast (z.B. wenn Du den Befehl
  unmittelbar nach einem Commit ausführst), wird der Snapshot exakt genauso
  aussehen wie der vorherige – alles, was Du dann änderst, ist die Commit
  Meldung.
</p>

<p>
  Der Texteditor startet wie üblich, aber diesmal enthält er bereits die
  Meldung aus dem vorherigen Commit. Du kannst diese Meldung wie gewohnt
  bearbeiten, speichern und die vorherige Meldung dadurch überschreiben.
</p>

<p>
  Wenn Du beispielsweise einen Commit angelegt hast und dann feststellst, dass
  Du zuvor vergessen hast, die Änderungen in einer bestimmten Datei zur Staging
  Area hinzuzufügen, kannst Du folgendes tun:
</p>

<pre>
$ git commit -m 'initial commit'
$ git add forgotten_file
$ git commit --amend
</pre>

<p>
  Diese drei Befehle legen einen einzigen neuen Commit an – der letzte Befehl
  ersetzt dabei das Ergebnis des ersten Befehls.
</p>

<h3>Änderungen aus der Staging Area entfernen</h3>

<p>
  Die nächsten zwei Abschnitte gehen darauf ein, wie Du Änderungen in der
  Staging Area und dem Arbeitsverzeichnis verwalten kannst. Praktischerweise
  liefert Dir der Befehl <code>git status</code>, den Du verwendest, um den Status dieser beiden Bereiche zu überprüfen,
  zugleich auch einen Hinweis dafür, wie Du Änderungen rückgängig machen kanst.
  Nehmen wir beispielsweise an, Du hast zwei Dateien geändert und willst sie
  als zwei seperate Commits anlegen, Du hast aber versehentlich <code>git add *</code> ausgeführt und damit beide zur Staging Area hinzugefügt. Wie kannst Du jetzt
  eine der beiden Änderungen wieder aus der Staging Area nehmen? <code>git status</code> gibt Dir einen Hinweis:
</p>

<pre>
$ git add .
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD &lt;file&gt;..." to unstage)

        modified:   README.txt
        modified:   benchmarks.rb
</pre>

<p>
  Direkt unter der Zeile „Changes to be committed“ findest Du den Hinweis „use <code>git reset HEAD &lt;file>...</code> to unstage“, d.h. „aus der Staging Area zu entfernen“. Wir verwenden nun
  also diesen Befehl, um die Änderungen an der Datei <code>benchmarks.rb</code> aus der Staging Area zu nehmen:
</p>

<pre>
$ git reset HEAD benchmarks.rb
Unstaged changes after reset:
M       benchmarks.rb
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD &lt;file&gt;..." to unstage)

        modified:   README.txt

Changes not staged for commit:
  (use "git add &lt;file&gt;..." to update what will be committed)
  (use "git checkout -- &lt;file&gt;..." to discard changes in working directory)

        modified:   benchmarks.rb
</pre>

<p>
  Der Befehl liest sich zunächst vielleicht etwas merkwürdig, aber wie Du
  siehst, funktioniert er. Die Datei benchmarks.rb ist weiterhin modifiziert,
  befindet sich aber nicht mehr in der Staging Area.
</p>

<h3>Eine Änderung an einer Datei rückgängig machen</h3>

<p>
  Was aber, wenn Du die Änderungen an der Datei <code>benchmarks.rb</code> überhaupt nicht beibehalten willst? D.h., wenn Du sie in den Zustand
  zurückversetzen willst, in dem sie sich befand, als Du den letzten Commit
  angelegt hast (oder das Repository geklont hast). Das ist einfach, und
  glücklicherweise zeigt der <code>git status</code> Befehl ebenfalls bereits einen Hinweis dafür an. Die obige Ausgabe enthält
  den folgenden Text:
</p>

<pre>
Changes not staged for commit:
  (use "git add &lt;file&gt;..." to update what will be committed)
  (use "git checkout -- &lt;file&gt;..." to discard changes in working directory)

        modified:   benchmarks.rb
</pre>

<p>
  Das sagt ziemlich klar, was wir zu tun haben um die Änderungen an der Datei
  zu verwerfen (genauer gesagt, Git tut dies seit der Version 1.6.1 – wenn Du
  eine ältere Version hast, empfehlen wir dir, sie zu aktualisieren). Wir
  führen den vorgeschlagenen Befehl also aus:
</p>

<pre>
$ git checkout -- benchmarks.rb
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD &lt;file&gt;..." to unstage)

        modified:   README.txt
</pre>

<p>
  Die Änderung wurde also rückgängig gemacht: sie taucht nicht mehr in der
  Liste der geänderten Dateien auf. Sei Dir bewusst, dass dieser Befehl
  potentiell gefährlich ist, da er Änderungen an einer Datei vollständig
  verwirft. Es ist also ratsam, ihn nur dann zu verwenden, wenn Du Dir absolut
  sicher bist, dass Du die Änderungen nicht mehr brauchst. Für Situationen, in
  denen Du eine Änderung lediglich vorläufig aus dem Weg räumen willst, werden
  wir im nächsten Kapitel noch auf Stashing und Branching eingehen – die dazu
  besser geeignet sind.
</p>

<p>
  Beachte, dass alles was jemals in einem Commit in Git enthalten war, fast
  immer wieder hergestellt werden kann. Selbst Commits, die sich in gelöschten
  Branches befanden, oder Commits, die mit einem <code>--amend</code> Commit überschrieben wurden, können wieder hergestellt werden (siehe Kapitel
  9 für Datenrettung). Allerdings wirst Du Änderungen, die es nie in einen
  Commit geschafft haben, wahrscheinlich auch nie wieder restaurieren können.
</p>

<h2>Mit externen Repositorys arbeiten</h2>

<p>
  Um mit anderen via Git zusammenzuarbeiten, musst Du wissen, wie Du auf
  externe (engl. „remote“) Repositorys zugreifen kannst. Remote Repositorys
  sind Versionen Deines Projektes, die im Internet oder irgendwo in einem
  anderen Netzwerk gespeichert sind. Du kannst mehrere solcher Repositorys
  haben und Du kannst jedes davon entweder nur lesen oder lesen und schreiben.
  Mit anderen via Git zusammenzuarbeiten impliziert, solche Repositorys zu
  verwalten und Daten aus ihnen herunter- oder heraufzuladen, um Deine Arbeit
  für andere verfügbar zu machen. Um Remote Repositorys zu verwalten, muss man
  wissen, wie man sie anlegt und wieder entfernt, wenn sie nicht mehr verwendet
  werden, wie man externe Branches verwalten und nachverfolgen kann, und mehr.
  In diesem Kapitel werden wir auf diese Aufgaben eingehen.
</p>

<h3>Remote Repositorys anzeigen</h3>

<p>
  Der <code>git remote</code> Befehl zeigt Dir an, welche externen Server Du für Dein Projekt lokal
  konfiguriert hast, und listet die Kurzbezeichnungen für diese Remote
  Repository auf. Wenn Du ein Repository geklont hast, solltest Du mindestens <code>origin</code> sehen – welches der Standardname ist, den Git für denjenigen Server vergibt,
  von dem Du geklont hast:
</p>

<pre>
$ git clone git://github.com/schacon/ticgit.git
Cloning into 'ticgit'...
remote: Reusing existing pack: 1857, done.
remote: Total 1857 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (1857/1857), 374.35 KiB | 193.00 KiB/s, done.
Resolving deltas: 100% (772/772), done.
Checking connectivity... done.
$ cd ticgit
$ git remote
origin
</pre>

<p>
  Du kannst außerdem die Option <code>-v</code> verwenden, welche für jeden Kurznamen auch die jeweilige URL anzeigt, die
  Git gespeichert hat:
</p>

<pre>
$ git remote -v
origin  git://github.com/schacon/ticgit.git (fetch)
origin  git://github.com/schacon/ticgit.git (push)
</pre>

<p>
  Wenn Du mehr als ein Remote Repository konfiguriert hast, zeigt der Befehl
  alle an. Für mein eigenes Grit Repository sieht das beispielsweise wie folgt
  aus:
</p>

<pre>
$ cd grit
$ git remote -v
bakkdoor  git://github.com/bakkdoor/grit.git
cho45     git://github.com/cho45/grit.git
defunkt   git://github.com/defunkt/grit.git
koke      git://github.com/koke/grit.git
origin    git@github.com:mojombo/grit.git
</pre>

<p>
  D.h., mein lokales Repository kennt die Repositorys von all diesen Leuten und
  ich kann ihre Beiträge zu meinem Projekt ganz einfach herunterladen und zum
  Projekt hinzufügen.
</p>

<h3>Remote Repositorys hinzufügen</h3>

<p>
  Ich habe in vorangegangenen Kapiteln schon Beispiele dafür aufgezeigt, wie
  man ein Remote Repository hinzufügen kann, aber ich will noch einmal darauf
  eingehen. Um ein neues Remote Repository mit einem Kurznamen hinzuzufügen,
  den Du Dir leicht merken kannst, führst Du den Befehl <code>git remote add [shortname] [url]</code> aus:
</p>

<pre>
$ git remote
origin
$ git remote add pb git://github.com/paulboone/ticgit.git
$ git remote -v
origin	git://github.com/schacon/ticgit.git
pb	git://github.com/paulboone/ticgit.git
</pre>

<p>
  Jetzt kannst Du den Namen <code>pb</code> anstelle der vollständingen URL in verschiedenen Befehlen verwenden. Wenn Du
  bespielsweise alle Informationen, die in Pauls, aber noch nicht in Deinem
  eigenen Repository verfügbar sind, herunterladen willst, kannst Du den Befehl
  <code>git fetch pb</code> verwenden:
</p>

<pre>
$ git fetch pb
remote: Counting objects: 58, done.
remote: Compressing objects: 100% (41/41), done.
remote: Total 44 (delta 24), reused 1 (delta 0)
Unpacking objects: 100% (44/44), done.
From git://github.com/paulboone/ticgit
 * [new branch]      master     -&gt; pb/master
 * [new branch]      ticgit     -&gt; pb/ticgit
</pre>

<p>
  Pauls master Branch ist jetzt lokal auf Deinem Rechner als <code>pb/master</code> verfügbar – Du kannst ihn mit einem Deiner eigenen Branches zusammenführen
  oder auf einen lokalen Branch wechseln, um damit zu arbeiten.
</p>

<h3>
  Änderungen aus Remote Repositorys herunterladen und herunterladen inkl.
  zusammenführen
</h3>

<p>
  Wie Du gerade gesehen hast, kannst Du Daten aus Remote Repositorys
  herunterladen, indem Du den folgenden Befehl verwendest:
</p>

<pre>
$ git fetch [remote-name]
</pre>

<p>
  Dieser Befehl lädt alle Daten aus dem Remote Repository herunter, die noch
  nicht auf Deinem Rechner verfügbar sind. Danach kennt Dein eigenes Repository
  Verweise auf alle Branches in dem Remote Repository, die Du jederzeit mit
  Deinen eigenen Branches zusammenführen oder durchschauen kannst. (Wir werden
  in Kapitel 3 detaillierter darauf eingehen, was genau Branches sind.)
</p>

<p>
  Wenn Du ein Repository geklont hast, legt der Befehl automatisch einen
  Verweis auf dieses Repository unter dem Namen <code>origin</code> an. D.h. <code>git fetch origin</code> lädt alle Neuigkeiten herunter, die in dem Remote Repository von anderen
  hinzugefügt wurden, seit Du es geklont hast (oder zuletzt <code>git fetch</code> ausgeführt hast). Es ist wichtig, zu verstehen, dass der <code>git fetch</code> Befehl Daten lediglich in Dein lokales Repository lädt. Er führt sich mit
  Deinen eigenen Commits in keiner Weise zusammen (mergt) oder modifiziert,
  woran Du gerade arbeitest. D.h. Du musst die heruntergeladenen Änderungen
  anschließend selbst manuell mit Deinen eigenen zusammenführen, wenn Du das
  willst.
</p>

<p>
  Wenn Du allerdings einen Branch so aufgesetzt hast, dass er einem Remote
  Branch „folgt“ (also einen „Tracking Branch“, wir werden im nächsten
  Abschnitt und in Kapitel 3 noch genauer darauf eingehen), dann kannst Du den
  Befehl <code>git pull</code> verwenden, um automatisch neue Daten herunterzuladen und den externen Branch
  gleichzeitig mit dem aktuellen, lokalen Branch zusammenzuführen. Das ist oft
  die bequemere Arbeitsweise. <code>git clone</code> setzt Deinen lokalen master Branch deshalb standardmäßig so auf, dass er dem
  Remote master Branch des geklonten Repositorys folgt (sofern das Remote
  Repository einen master Branch hat). Wenn Du dann <code>git pull</code> ausführst, wird Git die neuen Commits aus dem externen Repository holen und
  versuchen, sie automatisch mit dem Code zusammenzuführen, an dem Du gerade
  arbeitest.
</p>

<h3>Änderungen in ein Remote Repository hochladen</h3>

<p>
  Wenn Du mit Deinem Projekt an einen Punkt gekommen bist, an dem Du es anderen
  zur Verfügung stellen willst, kannst Du Deine Änderungen in ein gemeinsam
  genutztes Repository hochladen (engl. „push“). Der Befehl dafür ist einfach: <code>git push [remote-name] [branch-name]</code>. Wenn Du Deinen master Branch auf den <code>origin</code> Server hochladen willst (noch einmal, wenn Du ein Repository klonst, setzt
  Git diesen Namen automatisch für dich), dann kannst Du diesen Befehl
  verwenden:
</p>

<pre>
$ git push origin master
</pre>

<p>
  Das funktioniert nur dann, wenn Du Schreibrechte für das jeweilige Repository
  besitzt und niemand anders in der Zwischenzeit irgendwelche Änderungen
  hochgeladen hat. Wenn zwei Leute ein Repository zur gleichen Zeit klonen,
  dann zuerst der eine seine Änderungen hochlädt und der zweite anschließend
  versucht, das gleiche zu tun, dann wird sein Versuch korrekterweise
  abgewiesen. In dieser Situation muss man neue Änderungen zunächst
  herunterladen und mit seinen eigenen zusammenführen, um sie dann erst
  hochzuladen. In Kapitel 3 gehen wir noch einmal ausführlicher darauf ein.
</p>

<h3>Ein Remote Repository durchstöbern</h3>

<p>
  Wenn Du etwas über ein bestimmtes Remote Repository wissen willst, kannst Du
  den Befehl <code>git remote show [remote-name]</code> verwenden. Wenn Du diesen Befehl mit dem entsprechenden Kurznamen, z.B. <code>origin</code> verwendest, erhältst Du etwa folgende Ausgabe:
</p>

<pre>
$ git remote show origin
* remote origin
  URL: git://github.com/schacon/ticgit.git
  Remote branch merged with 'git pull' while on branch master
    master
  Tracked remote branches
    master
    ticgit
</pre>

<p>
  Das zeigt Dir die URL für das Remote Repository, die Information welche
  Branches verfolgt werden und welcher Branch aus dem Remote Repository mit
  Deinem eigenen Master zusammengeführt wird, wenn Du <code>git pull</code> ausführst.
</p>

<p>
  Dies ist ein eher einfaches Beispiel, das Dir früher oder später so ähnlich
  über den Weg laufen wird. Wenn Du Git aber täglich verwendest, erhältst Du
  mit <code>git remote show</code> sehr viel mehr Informationen:
</p>

<pre>
$ git remote show origin
* remote origin
  URL: git@github.com:defunkt/github.git
  Remote branch merged with 'git pull' while on branch issues
    issues
  Remote branch merged with 'git pull' while on branch master
    master
  New remote branches (next fetch will store in remotes/origin)
    caching
  Stale tracking branches (use 'git remote prune')
    libwalker
    walker2
  Tracked remote branches
    acl
    apiv2
    dashboard2
    issues
    master
    postgres
  Local branch pushed with 'git push'
    master:master
</pre>

<p>
  Dieser Befehl zeigt, welcher Branch automatisch hochgeladen werden wird, wenn
  Du <code>git push</code> auf bestimmten Branches ausführst. Er zeigt außerdem, welche Branches es im
  Remote Repository gibt, die Du selbst noch nicht hast, welche Branches dort
  gelöscht wurden, und Branches, die automatisch mit lokalen Branches
  zusammengeführt werden, wenn Du <code>git pull</code> ausführst.
</p>

<h3>Verweise auf externe Repositorys löschen und umbenennen</h3>

<p>
  Wenn Du eine Referenz auf ein Remote Repository umbenennen willst, kannst Du
  in neueren Git Versionen den Befehl <code>git remote rename</code> verwenden, um den Kurznamen zu ändern. Wenn Du beispielsweise <code>pb</code> in <code>paul</code> umbenennen willst, lautet der Befehl:
</p>

<pre>
$ git remote rename pb paul
$ git remote
origin
paul
</pre>

<p>
  Beachte dabei, dass dies Deine Branch Namen für Remote Branches ebenfalls
  ändert. Der Branch, der zuvor mit <code>pb/master</code> referenziert werden konnte, heißt jetzt <code>paul/master</code>.
</p>

<p>
  Wenn Du eine Referenz aus irgendeinem Grund entfernen willst (z.B. weil Du
  den Server umgezogen hast oder einen bestimmten Mirror nicht länger
  verwendest, oder weil jemand vielleicht nicht länger mitarbeitet), kannst Du <code>git remote rm</code> verwenden:
</p>

<pre>
$ git remote rm paul
$ git remote
origin
</pre>

<h2>Tags</h2>

<p>
  Wie die meisten anderen VCS kann Git bestimmte Punkte in der Historie als
  besonders wichtig markieren, also taggen. Normalerweise verwendet man diese
  Funktionalität, um Release Versionen zu markieren (z.B. v1.0). In diesem
  Abschnitt gehen wir darauf ein, wie Du vorhandene Tags anzeigen und neue Tags
  erstellen kannst, und worin die Unterschiede zwischen verschiedenen Typen von
  Tags bestehen.
</p>

<h3>Vorhandene Tags anzeigen</h3>

<p>
  Um die in einem Repository vorhandenen Tags anzuzeigen, kannst Du den Befehl <code>git tag</code> ohne irgendwelche weiteren Optionen verwenden:
</p>

<pre>
$ git tag
v0.1
v1.3
</pre>

<p>
  Dieser Befehl listet die Tags in alphabetischer Reihenfolge auf. Die
  Reihenfolge ist aber eigentlich nicht so wichtig.
</p>

<p>
  Du kannst auch nach Tags mit einem bestimmten Muster suchen. Das Git
  Quellcode Repository enthält beispielsweise mehr als 240 Tags. Wenn Du nur an
  denjenigen interessiert bist, die zur Version 1.4.2 gehören, kannst Du
  folgendes tun:
</p>

<pre>
$ git tag -l 'v1.4.2.*'
v1.4.2.1
v1.4.2.2
v1.4.2.3
v1.4.2.4
</pre>

<h3>Neue Tags anlegen</h3>

<p>
  Git kennt im wesentlichen zwei Typen von Tags: einfache (engl. lightweight)
  und kommentierte (engl. annotated) Tags. Ein einfacher Tag ist wie ein
  Branch, der sich niemals ändert – es ist lediglich ein Zeiger auf einen
  bestimmten Commit. Kommentierte Tags dagegen werden als vollwertige Objekte
  in der Git Datenbank gespeichert. Sie haben eine Checksumme, beinhalten Namen
  und E-Mail Adresse desjenigen, der den Tag angelegt hat, das jeweilige Datum
  sowie eine Meldung. Sie können überdies mit GNU Privacy Guard (GPG) signiert
  und verifiziert werden. Generell empfiehlt sich deshalb, kommentierte Tags
  anzulegen. Wenn man aber aus irgendeinem Grund einen temporären Tag anlegen
  will, für den all diese zusätzlichen Informationen nicht nötig sind, dann
  kann man auf einfache Tags zurückgreifen.
</p>

<h3>Kommentierte Tags</h3>

<p>
  Einen kommentierten Tag legst Du an, indem Du dem <code>git tag</code> Befehl die Option <code>-a</code> übergibst:
</p>

<pre>
$ git tag -a v1.4 -m 'my version 1.4'
$ git tag
v0.1
v1.3
v1.4
</pre>

<p>
  Die Option <code>-m</code> gibt dabei wiederum die Meldung an, die zum Tag hinzugefügt wird. Wenn Du
  keine Meldung angibst, startet Git wie üblich Deinen Editor, sodass Du eine
  Meldung eingeben kannst.
</p>

<p>
  <code>git show</code> zeigt Dir dann folgenden Tag zusammen mit dem jeweiligen Commit, auf den der
  Tag verweist, an:
</p>

<pre>
$ git show v1.4
tag v1.4
Tagger: Scott Chacon &lt;schacon@gee-mail.com&gt;
Date:   Mon Feb 9 14:45:11 2009 -0800

my version 1.4

commit 15027957951b64cf874c3557a0f3547bd83b3ff6
Merge: 4a447f7... a6b4c97...
Author: Scott Chacon &lt;schacon@gee-mail.com&gt;
Date:   Sun Feb 8 19:02:46 2009 -0800

    Merge branch 'experiment'
</pre>

<p>
  Die Ausgabe listet also zunächst die Informationen über denjenigen auf, der
  den Tag angelegt hat, sowie die Tag Meldung und dann die Commit Informationen
  selbst.
</p>

<h3>Signierte Tags</h3>

<p>
  Wenn Du einen privaten GPG Schlüssel hast, kannst Du Deine Tags zusätzlich
  mit GPG signieren. Dazu verwendest Du einfach die Option <code>-s</code> anstelle von <code>-a</code>:
</p>

<pre>
$ git tag -s v1.5 -m 'my signed 1.5 tag'
You need a passphrase to unlock the secret key for
user: "Scott Chacon &lt;schacon@gee-mail.com&gt;"
1024-bit DSA key, ID F721C45A, created 2009-02-09
</pre>

<p>
  Wenn Du jetzt <code>git show</code> auf diesen Tag anwendest, siehst Du, dass der Tag Deine GPG Signatur
  hinterlegt hat:
</p>

<pre>
$ git show v1.5
tag v1.5
Tagger: Scott Chacon &lt;schacon@gee-mail.com&gt;
Date:   Mon Feb 9 15:22:20 2009 -0800

my signed 1.5 tag
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.8 (Darwin)

iEYEABECAAYFAkmQurIACgkQON3DxfchxFr5cACeIMN+ZxLKggJQf0QYiQBwgySN
Ki0An2JeAVUCAiJ7Ox6ZEtK+NvZAj82/
=WryJ
-----END PGP SIGNATURE-----
commit 15027957951b64cf874c3557a0f3547bd83b3ff6
Merge: 4a447f7... a6b4c97...
Author: Scott Chacon &lt;schacon@gee-mail.com&gt;
Date:   Sun Feb 8 19:02:46 2009 -0800

    Merge branch 'experiment'
</pre>

<p>
  Darauf, wie Du signierte Tags verifizieren kannst, werden wir gleich noch
  eingehen.
</p>

<h3>Einfache Tags</h3>

<p>
  Einfache Tags sind die zweite Form von Tags, die Git kennt. Für einen
  einfachen Tag wird im wesentlichen die jeweilige Commit Prüfsumme, und sonst
  keine andere Information, in einer Datei gespeichert. Um einen einfachen Tag
  anzulegen, verwendest Du einfach keine der drei Optionen <code>-a</code>, <code>-s</code> und <code>-m</code>:
</p>

<pre>
$ git tag v1.4-lw
$ git tag
v0.1
v1.3
v1.4
v1.4-lw
v1.5
</pre>

<p>
  Wenn Du jetzt <code>git show</code> auf den Tag ausführst, siehst Du keine der zusätzlichen Tag Informationen.
  Der Befehl zeigt einfach den jeweiligen Commit:
</p>

<pre>
$ git show v1.4-lw
commit 15027957951b64cf874c3557a0f3547bd83b3ff6
Merge: 4a447f7... a6b4c97...
Author: Scott Chacon &lt;schacon@gee-mail.com&gt;
Date:   Sun Feb 8 19:02:46 2009 -0800

    Merge branch 'experiment'
</pre>

<h3>Tags verifizieren</h3>

<p>
  Um einen signierten Tag zu verifizieren, kannst Du <code>git tag -v [Tag Name]</code> verwenden. Dieser Befehl verwendet GPG, um die Signatur mit Hilfe des
  öffentlichen Schlüssels des Signierenden zu verifizieren – weshalb Du diesen
  Schlüssel in Deinem Schlüsselbund haben musst:
</p>

<pre>
$ git tag -v v1.4.2.1
object 883653babd8ee7ea23e6a5c392bb739348b1eb61
type commit
tag v1.4.2.1
tagger Junio C Hamano &lt;junkio@cox.net&gt; 1158138501 -0700

GIT 1.4.2.1

Minor fixes since 1.4.2, including git-mv and git-http with alternates.
gpg: Signature made Wed Sep 13 02:08:25 2006 PDT using DSA key ID F3119B9A
gpg: Good signature from "Junio C Hamano &lt;junkio@cox.net&gt;"
gpg:                 aka "[jpeg image of size 1513]"
Primary key fingerprint: 3565 2A26 2040 E066 C9A7  4A7D C0C6 D9A4 F311 9B9A
</pre>

<p>
  Wenn Du den öffentlichen Schlüssel des Signierenden nicht in Deinem
  Schlüsselbund hast, wirst Du statt dessen eine Meldung sehen wie:
</p>

<pre>
gpg: Signature made Wed Sep 13 02:08:25 2006 PDT using DSA key ID F3119B9A
gpg: Can't check signature: public key not found
error: could not verify the tag 'v1.4.2.1'
</pre>

<h3>Nachträglich taggen</h3>

<p>
  Du kannst Commits jederzeit taggen, auch lange Zeit nachdem sie angelegt
  wurden. Nehmen wir an, Deine Commit Historie sieht wie folgt aus:
</p>

<pre>
$ git log --pretty=oneline
15027957951b64cf874c3557a0f3547bd83b3ff6 Merge branch 'experiment'
a6b4c97498bd301d84096da251c98a07c7723e65 beginning write support
0d52aaab4479697da7686c15f77a3d64d9165190 one more thing
6d52a271eda8725415634dd79daabbc4d9b6008e Merge branch 'experiment'
0b7434d86859cc7b8c3d5e1dddfed66ff742fcbc added a commit function
4682c3261057305bdd616e23b64b0857d832627b added a todo file
166ae0c4d3f420721acbb115cc33848dfcc2121a started write support
9fceb02d0ae598e95dc970b74767f19372d61af8 updated rakefile
964f16d36dfccde844893cac5b347e7b3d44abbc commit the todo
8a5cbc430f1a9c3d00faaeffd07798508422908a updated readme
</pre>

<p>
  Nehmen wir an, dass Du vergessen hast, Version v1.2 des Projekts zu taggen
  und dass dies der Commit „updated rakefile“ gewesen ist. Du kannst diesen
  jetzt im Nachhinein taggen, indem Du die Checksumme des Commits (oder einen
  Teil davon) am Ende des Befehls angibst:
</p>

<pre>
$ git tag -a v1.2 -m 'version 1.2' 9fceb02
</pre>

<p>Du siehst jetzt, dass Du einen Tag für den Commit angelegt hast:</p>

<pre>
$ git tag
v0.1
v1.2
v1.3
v1.4
v1.4-lw
v1.5

$ git show v1.2
tag v1.2
Tagger: Scott Chacon &lt;schacon@gee-mail.com&gt;
Date:   Mon Feb 9 15:32:16 2009 -0800

version 1.2
commit 9fceb02d0ae598e95dc970b74767f19372d61af8
Author: Magnus Chacon &lt;mchacon@gee-mail.com&gt;
Date:   Sun Apr 27 20:43:35 2008 -0700

    updated rakefile
...
</pre>

<h3>Tags veröffentlichen</h3>

<p>
  Der <code>git push</code> Befehl lädt Tags nicht von sich aus auf externe Server. Stattdessen muss Du
  Tags explizit auf einen externen Server hochladen, nachdem Du sie angelegt
  hast. Der Vorgang entspricht dem&emsp14;&emsp14;bei Branches: Du kannst den
  Befehl <code>git push origin [tagname]</code> verwenden.
</p>

<pre>
$ git push origin v1.5
Counting objects: 50, done.
Compressing objects: 100% (38/38), done.
Writing objects: 100% (44/44), 4.56 KiB, done.
Total 44 (delta 18), reused 8 (delta 1)
To git@github.com:schacon/simplegit.git
* [new tag]         v1.5 -&gt; v1.5
</pre>

<p>
  Wenn Du viele Tags auf einmal hochladen willst, kannst Du dem <code>git push</code> Befehl außerdem die <code>--tags</code> Option übergeben und auf diese Weise sämtliche Tags auf dem Remote Server
  veröffentlichen, die dort noch nicht bekannt sind.
</p>

<pre>
$ git push origin --tags
Counting objects: 50, done.
Compressing objects: 100% (38/38), done.
Writing objects: 100% (44/44), 4.56 KiB, done.
Total 44 (delta 18), reused 8 (delta 1)
To git@github.com:schacon/simplegit.git
 * [new tag]         v0.1 -&gt; v0.1
 * [new tag]         v1.2 -&gt; v1.2
 * [new tag]         v1.4 -&gt; v1.4
 * [new tag]         v1.4-lw -&gt; v1.4-lw
 * [new tag]         v1.5 -&gt; v1.5
</pre>

<p>
  Wenn jetzt jemand anderes das Repository klont oder von dort aktualisiert,
  wird er all diese Tags ebenfalls erhalten.
</p>

<h2>Tipps und Tricks</h2>

<p>
  Bevor wir zum Ende dieses Grundlagenkapitels kommen, möchten wir noch einige
  Tipps und Tricks vorstellen, die Dir den Umgang mit Git ein bisschen
  vereinfachen können. Du kannst Git natürlich einsetzen, ohne diese Tipps
  anzuwenden, und wir werden später in diesem Buch auch nicht darauf Bezug
  nehmen oder sie voraussetzen. Aber wir finden, Du solltest sie kennen, weil
  sie einfach nützlich sind.
</p>

<h3>Auto-Vervollständigung</h3>

<p>
  Wenn Du die Bash Shell verwendest, dann kannst Du ein Skript für die Git
  Auto-Vervollständigung einbinden. Du kannst dieses Skript direkt aus den Git
  Quellen von
  https://github.com/git/git/blob/master/contrib/completion/git-completion.bash
  herunterladen. Kopiere diese Datei in Dein Home
  Verzeichnis&emsp14;&emsp14;und füge die folgende Zeile in Deine <code>.bashrc</code> Datei hinzu:
</p>

<pre>
source ~/git-completion.bash
</pre>

<p>
  Wenn Du Git Auto-Vervollständigung für alle Benutzer Deines Rechners
  aufsetzen willst, kopiere das Skript in das Verzeichnis <code>/opt/local/etc/bash_completion.d</code> (auf Mac OS X Systemen) bzw. <code>/etc/bash_completion.d/</code> (auf Linux Systemen). Bash sucht in diesem Verzeichnis nach Erweiterungen
  für die Autovervollständigung und lädt sie automatisch.
</p>

<p>
  Auf Windows Systemen sollte die Autovervollständigung bereits aktiv sein,
  wenn Du die Git Bash aus dem msysGit Paket verwendest.
</p>

<p>
  Während Du einen Git Befehl eintippst, kannst Du die Tab Taste drücken und Du
  erhälst eine Auswahl von Vorschlägen, aus denen Du auswählen kannst:
</p>

<pre>
$ git co&lt;tab&gt;&lt;tab&gt;
commit config
</pre>

<p>
  D.h., wenn Du <code>git co</code> schreibst und dann die Tab Taste zwei Mal drückst, erhältst Du die
  Vorschläge <code>commit</code> und <code>config</code>. Wenn Du Tab nur ein Mal drückst, vervollständigt den Befehl Deine Eingabe
  direkt zu <code>git commit</code>.
</p>

<p>
  Das funktioniert auch mit Optionen – was oftmals noch hilfreicher ist. Wenn
  Du beispielsweise <code>git log</code> verwenden willst und Dich nicht an eine bestimmte Option erinnern kannst,
  schreibst Du einfach den Befehl und drückst die Tab Taste, um die Optionen
  anzuzeigen:
</p>

<pre>
$ git log --s&lt;tab&gt;
--shortstat  --since=  --src-prefix=  --stat   --summary
</pre>

<p>
  Du musst also nicht dauernd die Dokumentation zu Rate ziehen und erspart Dir
  somit etwas Zeit. Ein toller Trick, nicht wahr?
</p>

<h3>Git Aliase</h3>

<p>
  Git versucht nicht zu erraten, welchen Befehl Du verwenden willst, wenn Du
  ihn nur teilweise eingibst. Wenn Du lange Befehle nicht immer wieder
  eintippen willst, kannst Du mit <code>git config</code> auf einfache Weise Aliase definieren. Hier einige Beispiele, die Du
  vielleicht nützlich findest:
</p>

<pre>
$ git config --global alias.co checkout
$ git config --global alias.br branch
$ git config --global alias.ci commit
$ git config --global alias.st status
</pre>

<p>
  Das heißt, dass Du z.B. einfach <code>git ci</code> anstelle von <code>git commit</code> schreiben kannst. Wenn Du Git oft verwendest, werden Dir sicher weitere
  Befehle begegnen, die Du sehr oft nutzt. In diesem Fall zögere nicht, weitere
  Aliase zu definieren.
</p>

<p>
  Diese Technik kann auch dabei helfen, Git Befehle zu definieren, von denen Du
  denkst, es sollte sie geben:
</p>

<pre>
$ git config --global alias.unstage 'reset HEAD --'
</pre>

<p>Das bewirkt, dass die beiden folgenden Befehle äquivalent sind:</p>

<pre>
$ git unstage fileA
$ git reset HEAD fileA
</pre>

<p>
  Unser neuer Alias ist wahrscheinlich aussagekräftiger, oder? Ein weiterer,
  typischer Alias ist der <code>last</code> Befehl:
</p>

<pre>
$ git config --global alias.last 'log -1 HEAD'
</pre>

<p>Auf diese Weise kannst Du leicht den letzten Commit nachschlagen:</p>

<pre>
$ git last
commit 66938dae3329c7aebe598c2246a8e6af90d04646
Author: Josh Goebel &lt;dreamer3@example.com&gt;
Date:   Tue Aug 26 19:48:51 2008 +0800

    test for current head

    Signed-off-by: Scott Chacon &lt;schacon@example.com&gt;
</pre>

<p>
  Wie Du Dir denken kannst, ersetzt Git ganz einfach den Alias mit dem
  jeweiligen Befehl, für den er definiert ist. Wenn Du allerdings einen
  externen Befehl anstelle eines Git Befehls ausführen willst, kannst Du den
  Befehl mit einem Auführungszeichen (<code>!</code>) am Anfang kennzeichnen. Das ist in der Regel nützlich, wenn Du Deine
  eigenen Hilfsmittel schreibst, um Git zu erweitern. Wir können das
  demonstrieren, indem wir <code>git visual</code> als <code>gitk</code> definieren:
</p>

<pre>
$ git config --global alias.visual '!gitk'
</pre>

<h2>Zusammenfassung</h2>

<p>
  Du solltest jetzt in der Lage sein, die wichtigsten Git Befehle einzusetzen
  und Repositorys neu zu erzeugen und zu klonen, Änderungen vorzunehmen und zur
  Staging Area hinzuzufügen, Commits anzulegen und die Historie aller Commits
  in einem Repository zu durchsuchen. Als nächstes werden wir auf ein
  herausragendes Feature von Git eingehen: das Branch Konzept.
</p>

<h1>Git Branching</h1>

<p>
  Nahezu jedes VCS unterstützt eine Form von Branching. Branching bedeutet,
  dass Du von der Hauptentwicklungslinie abzweigst und unabhängig vom
  Hauptzweig weiterarbeitest. Bei vielen VCS ist das ein ziemlch aufwendiger
  Prozess, welcher häufig erfordert, eine Kopie des kompletten
  Arbeitsverzeichnisses zu erstellen, was bei großen Projekten eine ganze Weile
  dauern kann.
</p>

<p>
  Manche Leute bezeichnen Gits Branching-Modell als dessen „Killer-Feature“,
  was Git zweifellos von dem Rest der VCS-Community abhebt. Was ist das
  Besondere daran? Die Art und Weise, wie Git Branches behandelt, ist
  unglaublich leichtgewichtig, wodurch Branch-Operationen nahezu
  verzögerungsfrei ausgeführt werden und auch das Hin- und Herschalten zwischen
  einzelnen Entwicklungszweigen im allgemeinen genauso schnell abläuft. Im
  Gegensatz zu anderen VCS ermutigt Git zu einer Arbeitsweise mit häufigem
  Branching und Merging – oft mehrmals am Tag. Wenn Du diese Funktion
  vertstehst und beherrschst, besitzt Du ein mächtiges und einmaliges Werkzeug,
  welches Deine Art zu entwickeln buchstäblich verändern.
</p>

<h2>Was ist ein Branch?</h2>

<p>
  Um wirklich zu verstehen, wie Git Branching durchführt, müssen wir einen
  Schritt zurück gehen und untersuchen, wie Git die Daten speichert. Wie Du
  Dich vielleicht noch an Kapitel 1 erinnerst, speichert Git seine Daten nicht
  als Serie von Änderungen oder Unterschieden, sondern als Serie von
  Schnappschüssen.
</p>

<p>
  Wenn Du in Git committest, speichert Git ein sogenanntes Commit-Objekt.
  Dieses enthält einen Zeiger zu dem Schnappschuss mit den Objekten der
  Staging-Area, dem Autor, den Commit-Metadaten und einem Zeiger zu den
  direkten Eltern des Commits. Ein initialer Commit hat keine Eltern-Commits,
  ein normaler Commit stammt von einem Eltern-Commit ab und ein Merge-Commit,
  welcher aus einer Zusammenführung von zwei oder mehr Branches resultiert,
  besitzt ebenso viele Eltern-Commits.
</p>

<p>
  Um das zu verdeutlichen, lass uns annehmen, Du hast ein Verzeichnis mit drei
  Dateien, die Du alle zu der Staging-Area hinzufügst und in einem Commit
  verpackst. Durch das Stagen der Dateien erzeugt Git für jede Datei eine
  Prüfsumme (der SHA-1 Hash, den wir in Kapitel 1 erwähnt haben), speichert
  diese Version der Datei im Git-Repository (Git referenziert auf diese als
  Blobs) und fügt die Prüfsumme der Staging-Area hinzu:
</p>

<pre>
$ git add README test.rb LICENSE
$ git commit -m 'initial commit of my project'
</pre>

<p>
  Wenn Du einen Commit mit der Anweisung <code>git commit</code> erstellst, erzeugt Git für jedes Projektverzeichnis eine Prüfsumme und
  speichert diese als sogenanntes <code>tree</code>-Objekt im Git Repository. Git erzeugt dann ein Commit Objekt, das die
  Metadaten und den Zeiger zum <code>tree</code>-Objekt des Wurzelverzeichnis enthält, um bei Bedarf den Snapshot erneut
  erzeugen zu können.
</p>

<p>
  Dein Git-Repository enthält nun fünf Objekte: einen Blob für den Inhalt von
  jeder der drei Dateien, einen Baum, der den Inhalt des Verzeichnisses
  auflistet und spezifiziert, welcher Dateiname zu welchem Blob gehört, sowie
  einen Zeiger, der auf die Wurzel des Projektbaumes und die Metadaten des
  Commits verweist. Prinzipiell sehen Deine Daten im Git Repository wie in
  Abbildung 3-1 aus.
</p>

<p>
  Insert 18333fig0301.png Abbildung 3-1. Repository-Daten eines einzelnen
  Commits.
</p>

<p>
  Wenn Du erneut etwas änderst und wieder einen Commit machst, wird dieser
  einen Zeiger enthalten, der auf den Vorhergehenden verweist. Nach zwei
  weiteren Commits könnte die Historie wie in Abbildung 3-2 aussehen.
</p>

<p>
  Insert 18333fig0302.png Abbildung 3-2. Git Objektdaten für mehrere Commits.
</p>

<p>
  Ein Branch in Git ist nichts anderes als ein simpler Zeiger auf einen dieser
  Commits. Der Standardname eines Git-Branches lautet <code>master</code>. Mit dem initialen Commit erhältst Du einen <code>master</code>-Branch, der auf Deinen letzten Commit zeigt. Mit jedem Commit bewegt er sich
  automatisch vorwärts.
</p>

<p>
  Insert 18333fig0303.png Abbildung 3-3. Branch, der auf einen Commit in der
  Historie zeigt.
</p>

<p>
  Was passiert, wenn Du einen neuen Branch erstellst? Nun, zunächst wird ein
  neuer Zeiger erstellt. Sagen wir, Du erstellst einen neuen Branch mit dem
  Namen <code>testing</code>. Das machst Du mit der Anweisung <code>git branch</code>:
</p>

<pre>
$ git branch testing
</pre>

<p>
  Dies erzeugt einen neuen Zeiger, der auf den gleichen Commit zeigt, auf dem
  Du gerade arbeitest (siehe Abbildung 3-4).
</p>

<p>
  Insert 18333fig0304.png Abbildung 3-4. Mehrere Branches zeigen in den
  Commit-Verlauf
</p>

<p>
  Woher weiß Git, welchen Branch Du momentan verwendest? Dafür gibt es einen
  speziellen Zeiger mit dem Namen HEAD. Berücksichtige, dass dieses Konzept
  sich grundsätzlich von anderen HEAD-Konzepten anderer VCS, wie Subversion
  oder CVS, unterscheidet. Bei Git handelt es sich bei HEAD um einen Zeiger,
  der auf Deinen aktuellen lokalen Branch zeigt. In dem Fall bist Du aber immer
  noch auf dem <code>master</code>-Branch. Die Anweisung <code>git branch</code> hat nur einen neuen Branch erstellt, aber nicht zu diesem gewechselt (siehe
  Abbildung 3-5).
</p>

<p>
  Insert 18333fig0305.png Abbildung 3-5. Der HEAD-Zeiger verweist auf Deinen
  aktuellen Branch.
</p>

<p>
  Um zu einem anderen Branch zu wechseln, benutze die Anweisung <code>git checkout</code>. Lass uns nun zu unserem neuen Branch <code>testing</code> wechseln:
</p>

<pre>
$ git checkout testing
</pre>

<p>
  Das lässt HEAD neuerdings auf den Branch „testing“ verweisen (siehe Abbildung
  3-6).
</p>

<p>
  Insert 18333fig0306.png Abbildung 3-6. Wenn Du den Branch wechselst, zeigt
  HEAD auf einen neuen Zweig.
</p>

<p>Und was bedeutet das? Ok, lass uns noch einen weiteren Commit machen:</p>

<pre>
$ vim test.rb
$ git commit -a -m 'made a change'
</pre>

<p>Abbildung 3-7 verdeutlicht das Ergebnis.</p>

<p>
  Insert 18333fig0307.png Abbildung 3-7. Der HEAD-Zeiger schreitet mit jedem
  weiteren Commit voran.
</p>

<p>
  Das ist interessant, denn Dein Branch <code>testing</code> hat sich jetzt voranbewegt, während Dein <code>master</code>-Branch immer noch auf den Commit zeigt, welchen Du bearbeitet hast, bevor Du
  mit <code>git checkout</code> den aktuellen Zweig gewechselt hast. Lass uns zurück zu dem <code>master</code>-Branch wechseln:
</p>

<pre>
$ git checkout master
</pre>

<p>Abbildung 3-8 zeigt das Ergebnis.</p>

<p>
  Insert 18333fig0308.png Abbildung 3-8. HEAD zeigt nach einem Checkout auf
  einen anderen Branch.
</p>

<p>
  Diese Anweisung hat zwei Dinge veranlasst. Zum einen bewegt es den
  HEAD-Zeiger zurück zum <code>master</code>-Branch, zum anderen setzt es alle Dateien im Arbeitsverzeichnis auf den
  Bearbeitungsstand des letzten Commits in diesem Zweig zurück. Das bedeutet
  aber auch, dass nun alle Änderungen am Projekt vollkommen unabhängig von
  älteren Projektversionen erfolgen. Kurz gesagt, werden alle Änderungen aus
  dem <code>testing</code>-Zweig vorübergehend rückgängig gemacht und Du hast die Möglichkeit, einen
  vollkommen neuen Weg in der Entwicklung einzuschlagen.
</p>

<p>Lass uns ein paar Änderungen machen und mit einem Commit festhalten:</p>

<pre>
$ vim test.rb
$ git commit -a -m 'made other changes'
</pre>

<p>
  Nun verzweigen sich die Projektverläufe (siehe Abbildung 3-9). Du hast einen
  Branch erstellt und zu ihm gewechselt, hast ein bisschen gearbeitet, bist zu
  Deinem Haupt-Zweig zurückgekehrt und hast da was ganz anderes gemacht. Beide
  Arbeiten existieren vollständig unabhängig voneinander in zwei
  unterschiedlichen Branches. Du kannst beliebig zwischen den beiden Zweigen
  wechseln und sie zusammenführen, wenn Du meinst, es wäre soweit. Und das
  alles hast Du mit simplen <code>branch</code> und <code>checkout</code>-Anweisungen vollbracht.
</p>

<p>Insert 18333fig0309.png Abbildung 3-9. Die Historie läuft auseinander.</p>

<p>
  Branches können in Git spielend erstellt und entfernt werden, da sie nur
  kleine Dateien sind, die eine 40 Zeichen lange SHA-1 Prüfsumme der Commits
  enthalten, auf die sie verweisen. Einen neuen Zweig zu erstellen, erzeugt
  ebenso viel Aufwand wie das Schreiben einer 41 Byte großen Datei (40 Zeichen
  und einen Zeilenumbruch).
</p>

<p>
  Das steht im krassen Gegensatz zu dem Weg, den die meisten andere VCS Tools
  beim Thema Branching einschlagen. Diese kopieren oftmals jeden neuen
  Entwicklungszweig in ein weiteres Verzeichnis, was – je nach Projektgröße –
  mehrere Minuten in Anspruch nehmen kann, wohingegen Git diese Aufgabe sofort
  erledigt. Da wir außerdem immer den Ursprungs-Commit festhalten, lässt sich
  problemlos eine gemeinsame Basis für eine Zusammenführung finden und
  umsetzen. Diese Eigenschaft soll Entwickler ermutigen Entwicklungszweige
  häufig zu erstellen und zu nutzen.
</p>

<p>Lass uns mal sehen, warum Du das machen solltest.</p>

<h2>Einfaches Branching und Merging</h2>

<p>
  Lass uns das Ganze an einem Beispiel durchgehen, dessen Workflow zum Thema
  Branching und Zusammenführen Du im echten Leben verwenden kannst. Folge
  einfach diesen Schritten:
</p>

<ol>
  <li>
    <p>Arbeite an einer Webseite.</p>
  </li>

  <li>
    <p>
      Erstelle einen Branch für irgendeine neue Geschichte, an der Du
      arbeitest.
    </p>
  </li>

  <li>
    <p>Arbeite in dem Branch.</p>
  </li>
</ol>

<p>
  In diesem Augenblick kommt ein Anruf, dass ein kritisches Problem aufgetreten
  ist und sofort gelöst werden muss. Du machst folgendes:
</p>

<ol>
  <li>
    <p>Schalte zurück zu Deinem „Produktiv“-Zweig.</p>
  </li>

  <li>
    <p>Erstelle einen Branch für den Hotfix.</p>
  </li>

  <li>
    <p>
      Nach dem Testen führst Du den Hotfix-Branch mit dem „Produktiv“-Branch
      zusammen.
    </p>
  </li>

  <li>
    <p>Schalte wieder auf Deine alte Arbeit zurück und werkel weiter.</p>
  </li>
</ol>

<h3>Branching Grundlagen</h3>

<p>
  Sagen wir, Du arbeitest an Deinem Projekt und hast bereits einige Commits
  durchgeführt (siehe Abbildung 3-10).
</p>

<p>
  Insert 18333fig0310.png Abbildung 3-10. Eine kurze, einfache Commit-Historie
</p>

<p>
  Du hast Dich dafür entschieden, am Issue #53 des Issue-Trackers XY zu
  arbeiten. Um eines klarzustellen, Git ist an kein Issue-Tracking-System
  gebunden. Da der Issue #53 allerdings ein Schwerpunktthema betrifft, wirst Du
  einen neuen Branch erstellen, um daran zu arbeiten. Um in einem
  Arbeitsschritt einen neuen Branch zu erstellen und zu aktivieren, kannst Du
  die Anweisung <code>git checkout</code> mit der Option <code>-b</code> verwenden:
</p>

<pre>
$ git checkout -b iss53
Switched to a new branch 'iss53'
</pre>

<p>Das ist die Kurzform von</p>

<pre>
$ git branch iss53
$ git checkout iss53
</pre>

<p>Abbildung 3-11 verdeutlicht das Ergebnis.</p>

<p>
  Insert 18333fig0311.png Abbildung 3-11. Erstellung eines neuen Branch-Zeigers
</p>

<p>
  Du arbeitest an Deiner Web-Seite und machst ein paar Commits. Das bewegt den <code>iss53</code>-Branch vorwärts, da Du ihn ausgebucht hast (das heißt, dass Dein HEAD-Zeiger
  darauf verweist; siehe Abbildung 3-12):
</p>

<pre>
$ vim index.html
$ git commit -a -m 'added a new footer [issue 53]'
</pre>

<p>
  Insert 18333fig0312.png Abbildung 3-12. Der <code>iss53</code>-Branch hat mit Deiner Arbeit Schritt gehalten.
</p>

<p>
  Nun bekommst Du einen Anruf, in dem Dir mitgeteilt wird, dass es ein Problem
  mit der Internet-Seite gibt, das Du umgehend beheben sollst. Bei Git musst Du
  Deine Fehlerkorrektur nicht zusammen mit den <code>iss53</code>-Änderungen einbringen. Und Du musst keine Zeit damit verschwenden, Deine
  bisherigen Änderungen rückgängig zu machen, bevor Du mit der Fehlerbehebung
  an der Produktionsumgebung beginnen kannst. Alles, was Du tun musst, ist, zu
  Deinem master-Branch wechseln.
</p>

<p>
  Beachte jedoch, dass Dich Git den Branch nur wechseln lässt, wenn bisherige
  Änderungen in Deinem Arbeitsverzeichnis oder Deiner Staging-Area nicht in
  Konflikt mit dem Zweig stehen, zu dem Du nun wechseln möchtest. Am besten ist
  es, wenn ein sauberer Arbeitsstand vorliegt, wenn man den Branch wechselt.
  Wir werden uns später mit Wegen befassen, dieses Verhalten zu umgehen
  (namentlich „Stashing“ und „Commit Ammending“). Vorerst aber hast Du Deine
  Änderungen bereits comitted, sodass Du zu Deinem master-Branch zurückwechseln
  kannst.
</p>

<pre>
$ git checkout master
Switched to branch 'master'
</pre>

<p>
  Zu diesem Zeitpunkt befindet sich das Arbeitsverzeichnis des Projektes in
  exakt dem gleichen Zustand, in dem es sich befand, als Du mit der Arbeit an
  Issue #53 begonnen hast und Du kannst Dich direkt auf Deinen Hotfix
  konzentrieren. Dies ist ein wichtiger Moment, um sich vor Augen zu halten,
  dass Git Dein Arbeitsverzeichnis auf den Zustand des Commits, auf den dieser
  Branch zeigt, zurücksetzt. Es erstellt, entfernt und verändert Dateien
  automatisch, um sicherzustellen, dass Deine Arbeitskopie haargenau so
  aussieht wie der Zweig nach Deinem letzten Commit.
</p>

<p>
  Nun hast Du einen Hotfix zu erstellen. Lass uns dazu einen hotfix-Branch
  erstellen, an dem Du bis zu dessen Fertigstellung arbeitest (siehe Abbildung
  3-13):
</p>

<pre>
$ git checkout -b hotfix
Switched to a new branch 'hotfix'
$ vim index.html
$ git commit -a -m 'fixed the broken email address'
[hotfix 3a0874c] fixed the broken email address
 1 files changed, 1 deletion(-)
</pre>

<p>
  Insert 18333fig0313.png Abbildung 3-13. Der hotfix-Branch basiert auf dem
  zurückliegenden master-Branch.
</p>

<p>
  Du kannst Deine Tests durchführen und sicherstellen, dass der Hotfix seinen
  Zweck erfüllt und ihn dann mit dem master-Branch zusammenführen, um ihn in
  die Produktionsumgebung zu integrieren. Das machst Du mit der Anweisung <code>git merge</code>:
</p>

<pre>
$ git checkout master
$ git merge hotfix
Updating f42c576..3a0874c
Fast-forward
 README | 1 -
 1 file changed, 1 deletion(-)
</pre>

<p>
  Du wirst die Mitteilung „Fast Forward“ während des Zusammenführens bemerken.
  Da der neue Commit direkt von dem ursprünglichen Commit, auf den sich der nun
  eingebrachte Zweig bezieht, abstammt, bewegt Git einfach den Zeiger weiter.
  Mit anderen Worten kann Git den neuen Commit durch Verfolgen der
  Commitabfolge direkt erreichen, dann bewegt es ausschließlich den
  Branch-Zeiger. Zu einer tatsächlichen Kombination der Commits besteht ja kein
  Anlass. Dieses Vorgehen wird „Fast Forward“ genannt.
</p>

<p>
  Deine Modifikationen befinden sich nun als Schnappschuss in dem Commit, auf
  den der <code>master</code>-Branch zeigt, diese lassen sich nun veröffentlichen (siehe Abbildung 3-14).
</p>

<p>
  Insert 18333fig0314.png Abbildung 3-14. Der master-Branch zeigt nach der
  Zusammenführung auf den gleichen Commit wie der hotfix-Branch.
</p>

<p>
  Nachdem Dein superwichtiger Hotfix veröffentlicht wurde, kannst Du Dich
  wieder Deiner ursprünglichen Arbeit zuwenden. Vorher wird sich allerdings des
  nun nutzlosen hotfix-Zweiges entledigt, schließlich zeigt der master-Branch
  ebenfalls auf die aktuelle Version. Du kannst ihn mit der <code>-d</code>-Option von <code>git branch</code> entfernen:
</p>

<pre>
$ git branch -d hotfix
Deleted branch hotfix (was 3a0874c).
</pre>

<p>
  Nun kannst Du zu Deinem Issue #53-Branch zurückwechseln und mit Deiner Arbeit
  fortfahren (Abbildung 3-15):
</p>

<pre>
$ git checkout iss53
Switched to branch 'iss53'
$ vim index.html
$ git commit -a -m 'finished the new footer [issue 53]'
[iss53 ad82d7a] finished the new footer [issue 53]
 1 file changed, 1 insertion(+)
</pre>

<p>
  Insert 18333fig0315.png Abbildung 3-15. Dein <code>iss53</code>-Branch kann sich unabhängig weiterentwickeln.
</p>

<p>
  An dieser Stelle ist anzumerken, dass die Änderungen an dem <code>hotfix</code>-Branch nicht in Deinen <code>iss53</code>-Branch eingeflossen sind. Falls nötig, kannst Du den <code>master</code>-Branch allerdings mit der Anweisung <code>git merge master</code> mit Deinem Branch kombinieren. Oder Du wartest, bis Du den <code>iss53</code>-Branch später in den <code>master</code>-Branch zurückführst.
</p>

<h3>Die Grundlagen des Zusammenführens (Mergen)</h3>

<p>
  Angenommen, Du hast entschieden, dass Deine Arbeiten an issue #53
  abgeschlossen sind und das Ganze soweit ist, dass es mit dem <code>master</code> Branch zusammengeführt werden kann. Um das zu erreichen, wirst Du Deinen <code>iss53</code> Branch in den <code>master</code> Branch einfließen lassen, genauso wie Du es mit dem <code>hotfix</code> Branch zuvor getan hast. Du musst nur mit der Anweisung <code>checkout</code> zum dem Branch zu wechseln, in welchen Du etwas einfließen lassen möchtest
  und dann die Anweisung <code>git merge</code> ausführen:
</p>

<pre>
$ git checkout master
$ git merge iss53
Auto-merging README
Merge made by the 'recursive' strategy.
 README | 1 +
 1 file changed, 1 insertion(+)
</pre>

<p>
  Das sieht ein bisschen anders aus als das <code>hotfix merge</code> von vorhin. Hier haben sich die Entwicklungsstränge schon zu einem früheren
  Zeitpunkt geteilt. Da der <code>commit</code> auf dem Branch, in dem Du Dich befindest, kein direkter Nachfolger von dem
  Branch ist, in den Du das <code>merge</code> machst, hat Git einiges zu tun. Dazu macht Git einen 3-Wege <code>merge</code>, wobei es die beiden Schnappschüsse verwendet, welche auf die Enden der
  Branches zeigen, und den gemeinsamen Vorfahren dieser beiden. Abbildung 3-16
  zeigt die drei <code>snapshots</code>, die Git in diesem Fall für das <code>merge</code> verwendet.
</p>

<p>
  Insert 18333fig0316.png Abbildung 3-16. Git erkennt automatisch den am besten
  geeigneten gemeinsamen Vorgänger für die Branchzusammenführung.
</p>

<p>
  Anstatt einfach den Zeiger vorwärts zu bewegen, erstellt Git einen neuen <code>snapshot</code>, der aus dem 3-Wege 'merge' resultiert und erzeugt automatisch einen neuen <code>commit</code>, der darauf verweist (siehe Abbildung 3-17). Dies wird auch als <code>merge commit</code> bezeichnet und ist ein Spezialfall, weil es mehr als nur einen Elternteil
  hat.
</p>

<p>
  Es ist wichtig herauszustellen, dass Git den am besten geeigneten gemeinsamen
  Vorgänger als Grundlage für das Zusammenführen bestimmt, denn hierin
  unterscheidet es sich von CVS und Subversion (vor Version 1.5), wo der
  Entwickler beim Zusammenführen die 'merge' Basis selbst ermitteln muss. In
  Git ist das Zusammenführen dadurch wesentlich einfacher, als in anderen
  Systemen.
</p>

<p>
  Insert 18333fig0317.png Abbildung 3-17. Git erstellt automatisch ein
  'commit', dass die zusammengeführte Arbeit enthält.
</p>

<p>
  Jetzt, da wir die Arbeit zusammengeführt haben, ist der <code>iss53</code>-Branch nicht mehr notwendig. Du kannst ihn löschen und das Ticket im
  Ticket-Tracking-System schließen.
</p>

<pre>
$ git branch -d iss53
</pre>

<h3>Grundlegende Merge-Konflikte</h3>

<p>
  Gelegentlich verläuft der Prozess nicht ganz so glatt. Wenn Du an den selben
  Stellen in den selben Dateien unterschiedlicher Branches etwas geändert hast,
  kann Git diese nicht sauber zusammenführen. Wenn Dein Fix an 'issue #53' die
  selbe Stelle in einer Datei verändert hat, die Du auch mit <code>hotfix</code> angefasst hast, wirst Du einen 'merge'-Konflikt erhalten, der ungefähr so
  aussehen könnte:
</p>

<pre>
$ git merge iss53
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
</pre>

<p>
  Git hat hier keinen 'merge commit' erstellt. Es hat den Prozess gestoppt,
  damit Du den Konflikt beseitigen kannst. Wenn Du sehen willst, welche Dateien
  'unmerged' aufgrund eines 'merge' Konflikts sind, benutze einfach <code>git status</code>:
</p>

<pre>
$ git status
On branch master
You have unmerged paths.
  (fix conflicts and run "git commit")

Unmerged paths:
  (use "git add &lt;file&gt;..." to mark resolution)

        both modified:      index.html

no changes added to commit (use "git add" and/or "git commit -a")
</pre>

<p>
  Alles, was einen 'merge' Konflikt aufweist und nicht gelöst werden konnte,
  wird als 'unmerged' aufgeführt. Git fügt den betroffenen Dateien
  Standard-Konfliktlösungsmarker hinzu, sodass Du diese öffnen und den Konflikt
  manuell lösen kannst. Deine Datei enthält einen Bereich, der so aussehen
  könnte:
</p>

<pre>
&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD
&lt;div id="footer"&gt;contact : email.support@github.com&lt;/div&gt;
=======
&lt;div id="footer"&gt;
  please contact us at support@github.com
&lt;/div&gt;
&gt;&gt;&gt;&gt;&gt;&gt;&gt; iss53
</pre>

<p>
  Das heißt, die Version in HEAD (Deines 'master'-Branches, denn der wurde per
  'checkout' aktiviert, als Du das 'merge' gemacht hast) ist der obere Teil des
  Blocks (alles oberhalb von '======='), und die Version aus dem <code>iss53</code>-Branch sieht wie der darunter befindliche Teil aus. Um den Konflikt zu
  lösen, musst Du Dich entweder für einen der beiden Teile entscheiden oder Du
  ersetzt den Teil komplett:
</p>

<pre>
&lt;div id="footer"&gt;
please contact us at email.support@github.com
&lt;/div&gt;
</pre>

<p>
  Diese Lösung hat von beiden Teilen etwas und ich habe die Zeilen mit <code>&lt;&lt;&lt;&lt;&lt;&lt;&lt;</code>, <code>=======</code>, und <code>>>>>>>></code> komplett gelöscht. Nachdem Du alle problematischen Bereiche in allen von dem
  Konflikt betroffenen Dateien beseitigt hast, führe einfach <code>git add</code> für alle betroffenen Dateien aus und markiere sie damit als bereinigt.
  Dieses 'staging' der Dateien markiert sie für Git als bereinigt. Wenn Du ein
  grafisches Tool zur Bereinigung benutzen willst, dann verwende <code>git mergetool</code>, welches ein passendes grafisches 'merge'-Tool startet und Dich durch die
  Konfliktbereiche führt:
</p>

<pre>
$ git mergetool

This message is displayed because 'merge.tool' is not configured.
See 'git mergetool --tool-help' or 'git help config' for more details.
'git mergetool' will now attempt to use one of the following tools:
opendiff kdiff3 tkdiff xxdiff meld tortoisemerge gvimdiff diffuse diffmerge ecmerge p4merge araxis bc3 codecompare vimdiff emerge
Merging:
index.html

Normal merge conflict for 'index.html':
  {local}: modified file
  {remote}: modified file
Hit return to start merge resolution tool (opendiff):
</pre>

<p>
  Wenn Du ein anderes Tool anstelle des Standardwerkzeugs für ein 'merge'
  verwenden möchtest (Git verwendet in meinem Fall <code>opendiff</code>, da ich auf einem Mac arbeite), dann kannst Du alle unterstützten Werkzeuge
  oben – unter „one of the following tools“ – aufgelistet sehen. Tippe einfach
  den Namen Deines gewünschten Werkzeugs ein. In Kapitel 7 besprechen wir, wie
  Du diesen Standardwert in Deiner Umgebung dauerhaft ändern kannst.
</p>

<p>
  Wenn Du das 'merge' Werkzeug beendest, fragt Dich Git, ob das Zusammenführen
  erfolgreich war. Wenn Du mit 'Ja' antwortest, wird das Skript diese Dateien
  als gelöst markieren.
</p>

<p>
  Du kannst <code>git status</code> erneut ausführen, um zu sehen, ob alle Konflikte gelöst sind:
</p>

<pre>
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD &lt;file&gt;..." to unstage)

        modified:   index.html
</pre>

<p>
  Wenn Du zufrieden bist und Du geprüft hast, dass alle Konflikte beseitigt
  wurden, kannst Du <code>git commit</code> ausführen, um den 'merge commit' abzuschließen. Die Standardbeschreibung für
  diese Art 'commit' sieht wie folgt aus:
</p>

<pre>
Merge branch 'iss53'

Conflicts:
  index.html
#
# It looks like you may be committing a merge.
# If this is not correct, please remove the file
#       .git/MERGE_HEAD
# and try again.
#
</pre>

<p>
  Wenn Du glaubst, für zukünftige Betrachter des Commits könnte es hilfreich
  sein zu erklären, warum Du getan hast, was Du getan hast, dann kannst Du der
  Commit-Beschreibung noch zusätzliche Informationen hinzufügen, falls es nicht
  offensichtlich ist.
</p>

<h2>Branch Management</h2>

<p>
  Du weißt jetzt, wie Du Branches erstellen, zusammenfügen und löschen kannst.
  Wir schauen uns jetzt noch ein paar recht nützliche Tools für die Arbeit mit
  Branches an.
</p>

<p>
  Die Anweisung <code>git branch</code> kann mehr, als nur Branches zu erstellen oder zu löschen. Wenn Du es ohne
  weitere Argumente ausführst, wird es Dir eine Liste mit Deinen aktuellen
  Branches anzeigen:
</p>

<pre>
$ git branch
  iss53
* master
  testing
</pre>

<p>
  Das <code>*</code> vor dem <code>master</code>-Branch bedeutet, dass dies der gerade ausgecheckte Branch ist. Wenn Du also
  jetzt einen Commit erzeugst, wird dieser in den <code>master</code>-Branch gehen. Du kannst Dir mit <code>git branch -v</code> ganz schnell für jeden Branch den letzten Commit anzeigen lassen:
</p>

<pre>
$ git branch -v
  iss53   93b412c fix javascript issue
* master  7a98805 Merge branch 'iss53'
  testing 782fd34 add scott to the author list in the readmes
</pre>

<p>
  Mit einer weiteren nützlichen Option kannst Du herausfinden, in welchem
  Zustand Deine Branches sind: welche der Branches wurden bereits in den
  aktuellen Branch gemergt und welche wurden es nicht. Für diesen Zweck gibt es
  in Git die Optionen <code>--merged</code> und <code>--no-merged</code>. Um herauszufinden, welche Branches schon in den aktuell ausgecheckten
  gemergt wurden, kannst Du einfach <code>git branch --merged</code> aufrufen:
</p>

<pre>
$ git branch --merged
  iss53
* master
</pre>

<p>
  Da Du den Branch <code>iss53</code> schon gemergt hast, siehst Du ihn in dieser Liste. Alle Branches in dieser
  Liste, welchen kein <code>*</code> voransteht, können ohne Datenverlust mit <code>git branch -d</code> gelöscht werden, da sie ja bereits gemergt wurden.
</p>

<p>
  Um alle Branches zu sehen, welche noch nicht gemergte Änderungen enthalten,
  kannst Du <code>git branch --no-merged</code> aufrufen:
</p>

<pre>
$ git branch --no-merged
  testing
</pre>

<p>
  Die Liste zeigt Dir den anderen Branch. Er enthält Arbeit, die noch nicht
  gemergt wurde. Der Versuch, den Branch mit <code>git branch -d</code> zu löschen schlägt fehl:
</p>

<pre>
$ git branch -d testing
error: The branch 'testing' is not fully merged.
If you are sure you want to delete it, run 'git branch -D testing'.
</pre>

<p>
  Wenn Du den Branch trotzdem löschen und damit alle Änderungen dieses Branches
  verlieren willst, kannst Du das mit <code>git branch -D testing</code> machen.
</p>

<h2>Branching Workflows</h2>

<p>
  Jetzt, da Du die Grundlagen von 'branching' und 'merging' kennst, fragst Du
  Dich sicher, was Du damit anfangen kannst. In diesem Abschnitt werden wir uns
  typische Workflows anschauen, die dieses leichtgewichtige 'branching' möglich
  macht. Und Du kannst dann entscheiden, ob Du es in Deinem eigenen
  Entwicklungszyklus verwenden willst.
</p>

<h3>Langfristige Branches</h3>

<p>
  Da Git das einfache 3-Wege-'merge' verwendet, ist häufiges Zusammenführen von
  einem Branch in einen anderen über einen langen Zeitraum generell einfach zu
  bewerkstelligen. Das heißt, Du kannst mehrere Branches haben, die immer offen
  sind und die Du für unterschiedliche Stadien Deines Entwicklungszyklus
  verwendest, und Du kannst regelmäßig welche von diesen mit anderen
  zusammenführen.
</p>

<p>
  Viele Git Entwickler verfolgen mit ihrem Workflow den Ansatz, nur den
  stabilen Code in dem <code>master</code>-Branch zu halten – möglicherweise auch nur Code, der released wurde oder
  werden kann. Sie betreiben parallel einen anderen Branch zum Arbeiten oder
  Testen. Wenn dieser parallele Zweig einen stabilen Status erreicht, kann er
  mit dem <code>master</code>-Branch zusammengeführt werden. Dies findet bei themenbezogenen Branches
  (kurzfristigen Branches, wie der zuvor genannte <code>iss53</code>-Branch) Anwendung, um sicherzustellen, dass dieser die Tests besteht und
  keine Fehler verursacht.
</p>

<p>
  Eigentlich reden wir gerade über Zeiger, die in der Reihe Deiner Commits
  aufsteigen. Die stabilen Branches liegen weiter unten und die bleeding-edge
  Branches weiter oben in diesem Verlauf (siehe Abbildung 3-18).
</p>

<p>
  Insert 18333fig0318.png Abbildung 3-18. Stabilere Branches sind generell
  weiter unten im Entwicklungsverlauf.
</p>

<p>
  Es ist leichter, sich die verschiedenen Branches als Arbeitsdepots
  vorzustellen, in denen Sätze von Commits in stabilere Depots aufsteigen,
  sobald sie ausreichend getestet wurden (siehe Abbildung 3-19).
</p>

<p>
  Insert 18333fig0319.png Abbildung 3-19. Es könnte hilfreich sein, sich die
  Branches als Depots vorzustellen.
</p>

<p>
  Das lässt sich für beliebig viele Stabilitätsabstufungen umsetzen. Manche
  größeren Projekte haben auch einen <code>proposed</code> (vorgeschlagenen) oder <code>pu</code> (proposed updates – vorgeschlagene Updates) Zweig mit Branches, die
  vielleicht noch nicht bereit sind, in den <code>next</code>- oder <code>master</code>-Branch integriert zu werden. Die Idee dahinter ist, dass Deine Branches
  verschiedene Stabilitätsabstufungen repräsentieren. Sobald sie eine stabilere
  Stufe erreichen, werden sie mit dem nächsthöheren Branch zusammengeführt.
</p>

<p>
  Nochmal, langfristig verschiedene Branches parallel laufen zu lassen, ist
  nicht notwendig, aber oft hilfreich,&emsp14;&emsp14;insbesondere wenn man es
  mit sehr großen oder komplexen Projekten zu tun hat.
</p>

<h3>Themen-Branches</h3>

<p>
  Themen-Branches sind in jedem Projekt nützlich, egal bei welcher Größe. Ein
  Themen-Branch ist ein kurzlebiger Zweig, der für eine spezielle Aufgabe oder
  ähnliche Arbeiten erstellt und benutzt wird. Das ist vielleicht etwas, was Du
  noch nie zuvor mit einem Versionierungssystem gemacht hast, weil es
  normalerweise zu aufwändig und mühsam ist, Branches zu erstellen und
  zusammenzuführen. Mit Git ist es allerdings vollkommen geläufig, mehrmals am
  Tag Branches zu erstellen, an ihnen zu arbeiten, sie zusammenzuführen und sie
  anschließend wieder zu löschen.
</p>

<p>
  Du hast das im letzten Abschnitt an den von Dir erstellten <code>iss53</code>- und <code>hotfix</code>-Branches gesehen. Du hast mehrere Commits auf sie angewendet und sie
  unmittelbar nach Zusammenführung mit Deinem Hauptzweig gelöscht. Diese
  Technik erlaubt es Dir, schnell und vollständig den Kontext zu wechseln. Da
  Deine Arbeit in verschiedene Depots aufgeteilt ist, in denen alle Änderungen
  unter die Thematik dieses Branches fallen, ist es leichter nachzuvollziehen,
  was bei Code-Überprüfungen und ähnlichem geschehen ist. Du kannst die
  Änderungen darin für Minuten, Tage oder Monate aufbewahren und sie einfließen
  lassen, wenn diese fertig sind, ungeachtet der Reihenfolge, in welcher diese
  erstellt oder bearbeitet wurden.
</p>

<p>
  Stell Dir vor, Du arbeitest ein bisschen (in <code>master</code>), erstellst mal eben einen Branch für einen Fehler (<code>iss91</code>), arbeitest an dem für eine Weile, erstellst einen zweiten Branch, um eine
  andere Problemlösung für den selben Fehler auszuprobieren (<code>iss91v2</code>), wechselst zurück zu Deinem master-Branch, arbeitest dort ein bisschen und
  machst dann einen neuen Branch für etwas, wovon Du nicht weißt, ob es eine
  gute Idee ist (<code>dumbidea</code>-Branch). Dein Commit-Verlauf wird wie in Abbildung 3-20 aussehen.
</p>

<p>
  Insert 18333fig0320.png Abbildung 3-20. Dein Commit-Verlauf mit verschiedenen
  Themen-Branches.
</p>

<p>
  Nun, sagen wir, Du hast Dich entschieden, die zweite Lösung des Fehlers (<code>iss91v2</code>) zu bevorzugen, außerdem hast den <code>dumbidea</code>-Branch Deinen Mitarbeitern gezeigt und es hat sich herausgestellt, dass er
  genial ist. Du kannst also den ursprünglichen <code>iss91</code>-Branch (unter Verlust der Commits C5 und C6) wegwerfen und die anderen
  beiden einfließen lassen. Dein Verlauf sieht dann aus wie in Abbildung 3-21.
</p>

<p>
  Insert 18333fig0321.png Abbildung 3-21. Dein Verlauf nach Zusammenführung von
  <code>dumbidea</code> und <code>iss91v2</code>.
</p>

<p>
  Es ist wichtig, sich daran zu erinnern, dass alle diese Branches nur lokal
  existieren. Wenn Du Verzweigungen schaffst (branchst) und wieder
  zusammenführst (mergest), findet dies nur in Deinem Git-Repository statt – es
  findet keine Server-Kommunikation statt.
</p>

<h2>Externe Branches</h2>

<p>
  Externe (Remote) Branches sind Referenzen auf den Zustand der Branches in
  Deinen externen Repositorys. Sie sind lokale Branches, die Du nicht verändern
  kannst, sie werden automatisch verändert, wann immer Du eine
  Netzwerkoperation durchführst. Externe Branches verhalten sich wie
  Lesezeichen, um Dich daran zu erinnern, an welcher Position sich die Branches
  in Deinen externen Repositorys befanden, als Du Dich zum letzten Mal mit
  ihnen verbunden hattest.
</p>

<p>
  Externe Branches besitzen die Schreibweise <code>(Repository)/(Branch)</code>. Wenn Du beispielsweise wissen möchtest, wie der <code>master</code>-Branch in Deinem <code>origin</code>-Repository ausgesehen hat, als Du zuletzt Kontakt mit ihm hattest, dann
  würdest Du den <code>origin/master</code>-Branch überprüfen. Wenn Du mit einem Mitarbeiter an einer Fehlerbehebung
  gearbeitet hast und dieser bereits einen <code>iss53</code>-Branch hochgeladen hat, besitzt Du möglicherweise Deinen eigenen lokalen <code>iss53</code>-Branch. Der Branch auf dem Server würde allerdings auf den Commit von <code>origin/iss53</code> zeigen.
</p>

<p>
  Das kann ein wenig verwirrend sein, lass uns also ein Besipiel betrachten.
  Nehmen wir an, Du hättest in Deinem Netzwerk einen Git-Server mit der Adresse
  <code>git.ourcompany.com</code>. Wenn Du von diesem klonst, nennt Git ihn automatisch <code>origin</code> für Dich, lädt all seine Daten herunter, erstellt einen Zeiger zur der
  Stelle, wo sein <code>master</code>-Branch ist und benennt es lokal <code>origin/master</code>; und er ist unveränderbar für Dich. Git gibt Dir auch einen eigenen <code>master</code>-Branch mit der gleichen Ausgangsposition wie origins <code>master</code>-Branch, damit Du einen Punkt für den Beginn Deiner Arbeiten hast (siehe
  Abbildung 3-22).
</p>

<p>
  Insert 18333fig0322.png Abbildung 3-22. Ein 'git clone' gibt Dir Deinen
  eigenen <code>master</code>-Branch und <code>origin/master</code>, welcher auf origins 'master'-Branch zeigt.
</p>

<p>
  Wenn Du ein wenig an Deinem lokalen <code>master</code>-Branch arbeitest und in der Zwischenzeit ein anderer etwas zu <code>git.ourcompany.com</code> herauflädt und damit den <code>master</code>-Branch auf dem externen Server aktualisiert, dann entwickeln sich Eure
  Arbeitsverläufe unterschiedlich. Außerdem bewegt sich Dein <code>origin/master</code>-Zeiger nicht, solange Du keinen Kontakt mit Deinem <code>origin</code>-Server aufnimmst (siehe Abbildung 3-23).
</p>

<p>
  Insert 18333fig0323.png Abbildung 3-23. Lokal zu arbeiten, während ein
  anderer auf Deinen externen Server hochlädt, führt zu unterschiedlichen
  Verläufen.
</p>

<p>
  Um Deine Arbeit abzugleichen, führe die Anweisung <code>git fetch origin</code> aus. Die Anweisung schlägt nach, welcher Server <code>orgin</code> ist (in diesem Fall <code>git.ourcompany.com</code>), holt alle Daten, die Dir bisher fehlen und aktualisiert Deine lokale
  Datenbank, indem es Deinen <code>orgin/master</code>-Zeiger auf seine neue aktuellere Position bewegt (siehe Abbildung 3-24).
</p>

<p>
  Insert 18333fig0324.png Abbildung 3-24. Die Anweisung <code>git fetch</code> aktualisiert Deine externen Referenzen.
</p>

<p>
  Um zu demonstrieren, wie Branches auf verschiedenen Remote-Servern aussehen,
  stellen wir uns vor, dass Du einen weiteren internen Git-Server besitzt,
  welcher nur von einem Deiner Sprint-Teams zur Entwicklung genutzt wird.
  Diesen Server erreichen wir unter <code>git.team1.ourcompany.com</code>. Du kannst ihn mit der Git-Anweisung <code>git remote add</code>, wie in Kapitel 2 beschrieben, Deinem derzeitigen Arbeitsprojekt als
  weiteren Quell-Server hinzufügen. Gib dem Remote-Server den Namen <code>teamone</code>, welcher nun als Synonym für die ausgeschriebene Internetadresse dient
  (siehe Abbildung 3-25).
</p>

<p>
  Insert 18333fig0325.png Abbildung 3-25. Einen weiteren Server als Quelle
  hinzufügen.
</p>

<p>
  Nun kannst Du einfach <code>git fetch teamone</code> ausführen, um alles vom Server zu holen, was Du noch nicht hast. Da der
  Datenbestand auf dem Teamserver ein Teil der Informationen auf Deinem <code>origin</code>-Server ist, holt Git keine Daten, erstellt allerdings einen Remote-Branch
  namens <code>teamone/master</code>, der auf den gleichen Commit wie <code>teamone</code>s <code>master</code>-Branch zeigt (siehe Abbildung 3-26).
</p>

<p>
  Insert 18333fig0326.png Abbildung 3-26. Du bekommst eine lokale Referenz auf <code>teamone</code>s <code>master</code>-Branch.
</p>

<h3>Hochladen</h3>

<p>
  Wenn Du einen Branch mit der Welt teilen möchtest, musst Du ihn auf einen
  externen Server laden, auf dem Du Schreibrechte besitzt. Deine lokalen Zweige
  werden nicht automatisch mit den Remote-Servern synchronisiert, wenn Du etwas
  änderst – Du musst die zu veröffentlichenden Branches explizit hochladen
  (pushen). Auf diesem Weg kannst Du an privaten Zweigen arbeiten, die Du nicht
  veröffentlichen möchtest, und nur die Themen-Branches replizieren, an denen
  Du gemeinsam mit anderen entwickeln möchtest.
</p>

<p>
  Wenn Du einen Zweig namens <code>serverfix</code> besitzt, an dem Du mit anderen arbeiten möchtest, dann kannst Du diesen auf
  dieselbe Weise hochladen wie Deinen ersten Branch. Führe <code>git push (remote) (branch)</code> aus:
</p>

<pre>
$ git push origin serverfix
Counting objects: 20, done.
Compressing objects: 100% (14/14), done.
Writing objects: 100% (15/15), 1.74 KiB, done.
Total 15 (delta 5), reused 0 (delta 0)
To git@github.com:schacon/simplegit.git
 * [new branch]      serverfix -&gt; serverfix
</pre>

<p>
  Hierbei handelt es sich um eine Abkürzung. Git erweitert die <code>serverfix</code>-Branchbezeichnung automatisch zu <code>refs/heads/serverfix:refs/heads/serverfix</code>, was soviel bedeutet wie “Nimm meinen lokalen <code>serverfix</code>-Branch und aktualisiere damit den <code>serverfix</code>-Branch auf meinem externen Server”. Wir werden den <code>refs/heads/</code>-Teil in Kapitel 9 noch näher beleuchten, Du kannst ihn aber in der Regel
  weglassen. Du kannst auch <code>git push origin serverfix:serverfix</code> ausführen, was das Gleiche bewirkt – es bedeutet “Nimm meinen <code>serverfix</code> und mach ihn zum externen <code>serverfix</code>”. Du kannst dieses Format auch benutzen, um einen lokalen Zweig in einen
  externen Branch mit anderem Namen zu laden. Wenn Du ihn auf dem externen
  Server nicht <code>serverfix</code> nennen möchtest, könntest Du stattdessen <code>git push origin serverfix:awesomebranch</code> ausführen, um Deinen lokalen <code>serverfix</code>-Branch in den <code>awesomebranch</code>-Zweig in Deinem externen Projekt zu laden.
</p>

<p>
  Das nächste Mal, wenn einer Deiner Mitarbeiter den aktuellen Status des
  Git-Projektes vom Server abruft, wird er eine Referenz auf den externen
  Branch <code>origin/serverfix</code> unter dem Namen <code>serverfix</code> erhalten:
</p>

<pre>
$ git fetch origin
remote: Counting objects: 20, done.
remote: Compressing objects: 100% (14/14), done.
remote: Total 15 (delta 5), reused 0 (delta 0)
Unpacking objects: 100% (15/15), done.
From git@github.com:schacon/simplegit
 * [new branch]      serverfix    -&gt; origin/serverfix
</pre>

<p>
  Es ist wichtig festzuhalten, dass Du mit Abrufen eines neuen externen
  Branches nicht automatisch eine lokale, bearbeitbare Kopie desselben
  erhältst. Mit anderen Worten, in diesem Fall bekommst Du keinen neuen <code>serverfix</code>-Branch – sondern nur einen <code>origin/serverfix</code>-Zeiger, den Du nicht verändern kannst.
</p>

<p>
  Um diese referenzierte Arbeit mit Deinem derzeitigen Arbeitsbranch
  zusammenzuführen, kannst Du <code>git merge origin/serverfix</code> ausführen. Wenn Du allerdings Deine eigene Arbeitskopie des <code>serverfix</code>-Branches erstellen möchtest, dann kannst Du diesen auf Grundlage des
  externen Zweiges erstellen:
</p>

<pre>
$ git checkout -b serverfix origin/serverfix
Branch serverfix set up to track remote branch refs/remotes/origin/serverfix.
Switched to a new branch "serverfix"
</pre>

<p>
  Dies erstellt Dir einen lokalen bearbeitbaren Branch mit der Grundlage des <code>origin/serverfix</code>-Zweiges.
</p>

<h3>Tracking Branches</h3>

<p>
  Das Auschecken eines lokalen Branches von einem Remote-Branch erzeugt
  automatisch einen sogenannten <i>Tracking-Branch</i>. Tracking Branches sind lokale Branches mit einer direkten Beziehung zu dem
  Remote-Zweig. Wenn Du Dich in einem Tracking-Branch befindest und <code>git push</code> eingibst, weiß Git automatisch, zu welchem Server und Repository es pushen
  soll. Ebenso führt <code>git pull</code> in einem dieser Branches dazu, dass alle entfernten Referenzen gefetched und
  automatisch in den Zweig gemerged werden.
</p>

<p>
  Wenn Du ein Repository klonst, wird automatisch ein <code>master</code>-Branch erzeugt, welcher <code>origin/master</code> verfolgt. Deshalb können <code>git push</code> und <code>git pull</code> ohne weitere Argumente aufgerufen werden. Du kannst natürlich auch eigene
  Tracking-Branches erzeugen – welche die nicht Zweige auf <code>origin</code> und dessen <code>master</code>-Branch verfolgen. Der einfachste Fall ist das bereits gesehene Beispiel, in
  welchem Du <code>git checkout -b [branch] [remotename]/[branch]</code> ausführst. Mit der Git-Version 1.6.2 oder später kannst Du auch die <code>--track</code>-Kurzvariante nutzen:
</p>

<pre>
$ git checkout --track origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'
</pre>

<p>
  Um einen lokalen Branch zu erzeugen mit einem anderen Namen als dem des
  Remote-Branches, kannst Du einfach die erste Variante mit einem neuen lokalen
  Branch-Namen verwenden:
</p>

<pre>
$ git checkout -b sf origin/serverfix
Branch sf set up to track remote branch serverfix from origin.
Switched to a new branch 'sf'
</pre>

<p>
  Nun wird Dein lokaler Branch <code>sf</code> automatisch push und pull auf <code>origin/serverfix</code> durchführen.
</p>

<h3>Löschen entfernter Branches</h3>

<p>
  Stellen wir uns vor, Du bist fertig mit Deinem Remote-Branch – sagen wir
  Deine Mitarbeiter und Du, Ihr seid fertig mit einer neuen Funktion und habt
  sie in den entfernten <code>master</code>-Branch (oder in welchem Zweig Ihr sonst den stabilen Code ablegt) gemerged.
  Dann kannst Du den Remote-Branch löschen, indem Du die recht stumpfsinnige
  Syntax <code>git push [remotename] :[branch]</code> benutzt. Wenn Du Deinen <code>serverfix</code>-Branch vom Server löschen möchtest, führe folgendes aus:
</p>

<pre>
$ git push origin :serverfix
To git@github.com:schacon/simplegit.git
 - [deleted]         serverfix
</pre>

<p>
  Boom. Kein Zweig mehr auf Deinem Server. Du möchtest Dir diese Seite
  vielleicht markieren, weil Du diese Anweisung noch benötigen wirst und man
  leicht deren Syntax vergisst. Ein Weg, sich an diese Anweisung zu erinnern,
  führt über die <code>git push [remotename] [localbranch]:[remotebranch]</code>-Snytax, welche wir bereits behandelt haben. Wenn Du den <code>[localbranch]</code>-Teil weglässt, dann sagst Du einfach „Nimm nichts von meiner Seite und mach
  es zu <code>[remotebranch]</code>“.
</p>

<h2>Rebasing</h2>

<p>
  Es gibt in Git zwei Wege, um Änderungen von einem Branch in einen anderen zu
  überführen: <code>merge</code> und <code>rebase</code>. In diesem Abschnitt wirst Du erfahren, was Rebasing ist, wie Du es
  anwendest, warum es ein verdammt abgefahrenes Werkzeug ist und bei welchen
  Gelegenheiten Du es besser nicht einsetzen solltest.
</p>

<h3>Der einfache Rebase</h3>

<p>
  Wenn Du zu einem früheren Beispiel aus dem Merge-Kapitel zurückkehrst (siehe
  Abbildung 3-27), kannst Du sehen, dass Du Deine Arbeit aufgeteilt und Commits
  auf zwei unterschiedlichen Branches erstellt hast.
</p>

<p>
  Insert 18333fig0327.png Abbildung 3-27. Deine initiale Commit-Historie zum
  Zeitpunkt der Aufteilung.
</p>

<p>
  Der einfachste Weg, um Zweige zusammenzuführen, ist, wie bereits behandelt,
  die <code>merge</code>-Anweisung. Sie führt einen Drei-Wege-Merge durch zwischen den beiden letzten
  Branch-Zuständen (C3 und C4) und dem letzen gemeinsamen Vorgänger (C2) der
  beiden, erstellt einen neuen Schnappschuss (und einen Commit), wie in
  Abbildung 3-28 dargestellt.
</p>

<p>
  Insert 18333fig0328.png Abbildung 3-28. Zusammenführen von Branches, um die
  verschiedenen Arbeitsfortschritte zu integrieren.
</p>

<p>
  Allerdings gibt es noch einen anderen Weg: Du kannst den Patch der
  Änderungen, den wir in C3 eingeführt haben, nehmen und erneut anwenden auf
  C4. Dieses Vorgehen nennt man in Git <i>rebasing</i>. Mit der <code>rebase</code>-Anweisung kannst Du alle Änderungen, die an einem Branch vorgenommen wurden,
  auf einen anderen Branch erneut anwenden.
</p>

<p>In unserem Beispiel würdest Du folgendes ausführen:</p>

<pre>
$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command
</pre>

<p>
  Dies funktioniert, indem Git zum letzten gemeinsamen Vorfahren der beiden
  Branches (der, auf dem Du arbeitest, und jener, auf den Du <i>rebasen</i> möchtest) geht, dann die Informationen zu den Änderungen (diffs) sammelt,
  welche seit dem bei jedem einzelen Commit des aktuellen Branches gemacht
  wurden, diese in temporären Dateien speichert, den aktuellen Branch auf den
  gleichen Commit setzt wie den Branch, auf den Du <i>rebasen</i> möchtest und dann alle Änderungen erneut durchführt. Die Abbildung 3-29
  bildet diesen Prozess ab.
</p>

<p>
  Insert 18333fig0329.png Abbildung 3-29. Rebasen der in C3 durchgeführten
  Änderungen auf C4.
</p>

<p>
  An diesem Punkt kannst Du zurück zum Master-Branch wechseln und einen
  fast-forward Merge durchführen (siehe Abbildung 3-30).
</p>

<p>
  Insert 18333fig0330.png Abbildung 3-30. Fast-forward des Master-Branches.
</p>

<p>
  Nun ist der Schnappschuss, auf den C3' zeigt, exakt der gleiche, wie der auf
  den C5 in dem Merge-Beispiel gezeigt hat. Bei dieser Zusammenführung entsteht
  kein unterschiedliches Produkt, durch Rebasing ensteht allerdings ein
  sauberer Verlauf. Bei genauerer Betrachtung der Historie entpuppt sich der
  Rebased-Branch als linearer Verlauf – es scheint, als sei die ganze Arbeit in
  einer Serie entstanden, auch wenn sie in Wirklichkeit parallel stattfand.
</p>

<p>
  Du wirst das häufig anwenden um sicherzustellen, dass sich Deine Commits
  sauber in einen Remote-Branch integrieren – möglicherweise in einem Projekt,
  bei dem Du Dich beteiligen möchtest, Du jedoch nicht der Verantwortliche
  bist. In diesem Fall würdest Du Deine Arbeiten in einem eigenen Branch
  erledigen und im Anschluss Deine Änderungen auf <code>origin/master</code> rebasen. Dann hätte der Verantwortliche nämlich keinen Aufwand mit der
  Integration – nur einen Fast-Forward oder eine saubere Integration (=
  Rebase?).
</p>

<p>
  Beachte, dass der Schnappschuss, welche auf den letzten Commit zeigt, ob es
  nun der letzte der Rebase-Commits nach einem Rebase oder der finale
  Merge-Commit nach einem Merge ist, der selbe Schnappschuss ist, nur der
  Verlauf ist ein anderer. Rebasing wiederholt einfach die Änderungen einer
  Arbeitslinie auf einer anderen in der Reihenfolge, in der sie entstanden
  sind. Im Gegensatz hierzu nimmt Merging die beiden Endpunkte der
  Arbeitslinien und führt diese zusammen.
</p>

<h3>Mehr interessante Rebases</h3>

<p>
  Du kannst Deinen Rebase auch auf einem anderen Branch als dem Rebase-Branch
  anwenden lassen. Nimm zum Beispiel den Verlauf in Abbildung 3-31. Du hattest
  einen Themen-Branch (<code>server</code>) angelegt, um ein paar serverseitige Funktionalitäten zu Deinem Projekt
  hinzuzufügen, und hast dann einen Commit gemacht. Dann hast Du einen weiteren
  Branch abgezweigt, um clientseitige Änderungen (<code>client</code>) vorzunehmen und dort ein paarmal committed. Zum Schluss hast Du wieder zu
  Deinem Server-Branch gewechselt und ein paar weitere Commits gebaut.
</p>

<p>
  Insert 18333fig0331.png Abbildung 3-31. Ein Verlauf mit einem Themen-Branch
  basierend auf einem weiteren Themen-Branch.
</p>

<p>
  Angenommen, Du entscheidest Dich, Deine clientseitigen Änderungen für einen
  Release in die Hauptlinie zu mergen, während Du die serverseitigen Änderungen
  noch zurückhalten möchtest, bis sie besser getestet wurden. Du kannst einfach
  die Änderungen am Client, die den Server nicht betreffen, (C8 und C9) mit der
  <code>--onto</code>-Option von <code>git rebase</code> erneut auf den Master-Branch anwenden:
</p>

<pre>
$ git rebase --onto master server client
</pre>

<p>
  Das bedeutet einfach, “Checke den Client-Branch aus, finde die Patches
  heraus, die auf dem gemeinsamen Vorfahr der <code>client</code>- und <code>server</code>-Branches basieren und wende sie erneut auf dem <code>master</code>-Branch an.” Das ist ein bisschen komplex, aber das Ergebnis – wie in
  Abbildung 3-32 – ist richtig cool.
</p>

<p>
  Insert 18333fig0332.png Abbildung 3-32. Rebasing eines Themen-Branches von
  einem anderen Themen-Branch.
</p>

<p>
  Jetzt kannst Du Deinen Master-Branch fast-forwarden (siehe Abbildung 3-33):
</p>

<pre>
$ git checkout master
$ git merge client
</pre>

<p>
  Insert 18333fig0333.png Abbildung 3-33. Fast-forwarding Deines
  Master-Branches um die Client-Branch-Änderungen zu integrieren.
</p>

<p>
  Lass uns annehmen, Du entscheidest Dich, Deinen Server-Branch ebenfalls
  einzupflegen. Du kannst den Server-Branch auf den Master-Branch rebasen, ohne
  diesen vorher auschecken zu müssen, indem Du die Anweisung <code>git rebase [Basis-Branch] [Themen-Branch]</code> ausführst. Sie macht für Dich den Checkout des Themen-Branches (in diesem
  Fall <code>server</code>) und wiederholt ihn auf dem Basis-Branch (<code>master</code>):
</p>

<pre>
$ git rebase master server
</pre>

<p>
  Das wiederholt Deine <code>server</code>-Arbeit auf der Basis der <code>master</code>-Arbeit, wie in Abbildung 3-34 ersichtlich.
</p>

<p>
  Insert 18333fig0334.png Abbildung 3-34. Rebasing Deines Server-Branches auf
  Deinen Master-Branch.
</p>

<p>Dann kannst Du den Basis-Branch (<code>master</code>) fast-forwarden:</p>

<pre>
$ git checkout master
$ git merge server
</pre>

<p>
  Du kannst den <code>client</code>- und <code>server</code>-Branch nun entfernen, da Du die ganze Arbeit bereits integriert wurde und
  sie nicht mehr benötigst. Du hinterlässt den Verlauf für den ganzen Prozess
  wie in Abbildung 3-35:
</p>

<pre>
$ git branch -d client
$ git branch -d server
</pre>

<p>Insert 18333fig0335.png Abbildung 3-35: Endgültiger Commit-Verlauf.</p>

<h3>Die Gefahren des Rebasings</h3>

<p>
  Ahh, aber der ganze Spaß mit dem Rebasing kommt nicht ohne seine
  Schattenseiten, welche in einer einzigen Zeile zusammengefasst werden können:
</p>

<p>
  <b>Rebase keine Commits die Du in ein öffentliches Repository hochgeladen hast.</b>
</p>

<p>
  Wenn Du diesem Ratschlag folgst, ist alles in Ordnung. Falls nicht, werden
  die Leute Dich hassen und Du wirst von Deinen Freunden und Deiner Familie
  verachtet.
</p>

<p>
  Wenn Du Zeug rebased, hebst Du bestehende Commits auf und erstellst
  stattdessen welche, die zwar ähnlich aber unterschiedlich sind. Wenn Du
  Commits irgendwohin hochlädst und andere ziehen sich diese herunter und
  nehmen sie als Grundlage für ihre Arbeit, dann müssen Deine Mitwirkenden ihre
  Arbeit jedesmal re-mergen, sobald Du Deine Commits mit einem <code>git rebase</code> überschreibst und verteilst. Und richtig chaotisch wird es, wenn Du
  versuchst, deren Arbeit in Deine Commits zu integrieren.
</p>

<p>
  Lass uns mal ein Beispiel betrachten, wie das Rebasen veröffentlichter Arbeit
  Probleme verursachen kann. Angenommen, Du klonst von einem zentralen Server
  und werkelst ein bisschen daran rum. Dein Commit-Verlauf sieht wie in
  Abbildung 3-36 aus.
</p>

<p>
  Insert 18333fig0336.png Abbildung 3-36. Klone ein Repository und baue etwas
  darauf auf.
</p>

<p>
  Ein anderer arbeitet unterdessen weiter, macht einen Merge und lädt seine
  Arbeit auf den zentralen Server. Du fetchst die Änderungen und mergest den
  neuen Remote-Branch in Deine Arbeit, sodass Dein Verlauf wie in Abbildung
  3-37 aussieht.
</p>

<p>
  Insert 18333fig0337.png Abbildung 3-37. Fetche mehrere Commits und merge sie
  in Deine Arbeit.
</p>

<p>
  Als nächstes entscheidet sich die Person, welche den Merge hochgeladen hat,
  diesen rückgängig zu machen und stattdessen die Commits zu rebasen. Sie macht
  einen <code>git push --force</code>, um den Verlauf auf dem Server zu überschreiben. Du lädst Dir das Ganze dann
  mit den neuen Commits herunter.
</p>

<p>
  Insert 18333fig0338.png Abbildung 3-38. Jemand pusht rebased Commits und
  verwirft damit Commits, auf denen Deine Arbeit basiert.
</p>

<p>
  Nun musst Du seine Arbeit erneut in Deine Arbeitslinie mergen, obwohl Du das
  bereits einmal gemacht hast. Rebasing ändert die SHA-1-Hashes der Commits,
  weshalb sie für Git wie neue Commits aussehen. In Wirklichkeit hast Du die
  C4-Arbeit bereits in Deinem Verlauf (siehe Abbildung 3-39).
</p>

<p>
  Insert 18333fig0339.png Abbildung 3-39. Du mergst die gleiche Arbeit nochmals
  in einen neuen Merge-Commit.
</p>

<p>
  Irgendwann musst Du seine Arbeit mergen, damit Du auch zukünftig mit dem
  anderen Entwickler zusammenarbeiten kannst. Danach wird Dein Commit-Verlauf
  sowohl den C4 als auch den C4'-Commit enthalten, welche zwar verschiedene
  SHA-1-Hashes besitzen, aber die gleichen Änderungen und die gleiche
  Commit-Beschreibung enthalten. Wenn Du so einen Verlauf mit <code>git log</code> betrachtest, wirst Du immer zwei Commits des gleichen Autors, zur gleichen
  Zeit und mit der gleichen Commit-Nachricht sehen. Was ganz schön verwirrend
  sein wird. Wenn Du diesen Verlauf außerdem auf den Server hochlädst, wirst Du
  dort alle rebasierten Commits nochmals einführen, was die Leute noch mehr
  verwirren kann.
</p>

<p>
  Wenn Du rebasing als einen Weg getrachtest, um aufzuräumen und mit Commits zu
  arbeiten, bevor Du sie hochlädst und wenn Du rebase nur auf Commits
  anwendest, die noch nie öffentlich zugänglich waren, dann fährst Du
  goldrichtig. Wenn Du rebase auf Commits anwendest, die bereits veröffentlicht
  wurden und Leute vielleicht schon ihre Arbeit darauf aufgebaut haben, dann
  kannst Du Dich auf frustrierenden Ärger gefasst machen.
</p>

<h2>Zusammenfassung</h2>

<p>
  Wir haben einfaches Branching und Merging mit Git behandelt. Du solltest nun
  gut damit zurecht kommen, Branches zu erstellen, zwischen Branches zu
  wechseln und lokale Branches mit einem Merge zusammenzuführen. Außerdem
  solltest Du in der Lage sein, Deine Branches zu veröffentlichen, indem Du sie
  auf einen zentralen Server lädst, mit anderen auf öffentlichen Branches
  zusammenzuarbeiten und Deine Branches zu rebasen, bevor sie veröffentlicht
  werden.
</p>

<h1>Git auf dem Server</h1>

<p>
  Zum jetzigen Zeitpunkt solltest Du in der Lage sein, die am häufigsten
  wiederkehrenden Aufgaben mit Git zu lösen. Um eine Zusammenarbeit zu
  ermöglichen solltest Du jedoch darüber nachdenken, ein externes Repository
  zur Verfügung stellen. Wenngleich es technisch möglich ist, direkt mit
  Repositories Anderer zu arbeiten und Änderungen dorthin zu pushen oder von
  dort zu holen, ist dieses Vorgehen verpöhnt, da es sehr leicht die Arbeit
  Anderer durcheinander zu bringen. Wenn man nicht vorsichtig ist, verliert man
  schnell den Überblick darüber wer woran arbeitet. Des weiteren erfordert der
  direkte Umgang mit Git Repositories, dass diese permanent verfügbar sind. Ein
  Repository auf dem eigenen Computer ist nur dann für alle Mitentwickler
  erreichbar, wenn der Computer läuft. Es macht deshalb Sinn, ein zentrales und
  zuverlässig erreichbares Repository einzurichten. Wir werden dieses
  Repository im Folgenden als „Git Server“ bezeichnen. Es sollte jedoch schnell
  klar werden, dass nur minimale Ressourcen notwendig sind, um Git Repositories
  zu hosten. In den seltensten Fällen ist ein dedizierter Server dafür
  notwendig.
</p>

<p>
  Einen Git Server zu betreiben ist einfach. Die erste Entscheidung, die zu
  treffen ist, ist die des zu verwendenden Protokolls zur Kommunikation mit dem
  Server. Der erste Teil dieses Kapitels wird deshalb die zur Verfügung
  stehenden Protokolle und ihre Vor- und Nachteile beschreiben. Darüber hinaus
  werden einige typische Konfigurationen zum Betreiben eines Git Servers
  vorgestellt. Falls keine Bedenken bestehen, Code von externen Anbietern
  hosten zu lassen, werden zuletzt ein paar Optionen für gehostete Git
  Repositories aufgezeigt. Dies erspart die Mehrarbeit der Einrichtung und
  Wartung eines eigenen Git Servers.
</p>

<p>
  Wenn Du kein Interesse am Betreiben eines eigenen Servers hast, kannst Du zum
  letzten Absatz des Kapitels springen, um ein paar Möglichkeiten zum
  Einrichten eines gehosteten Accounts zu sehen. Im nächsten Kapitel
  diskutieren wir verschiedene Vor- und Nachteile vom Arbeiten in einer
  verteilten Quellcode-Kontroll-Umgebung.
</p>

<p>
  Ein externes Repository ist im Allgemeinen ein <i>einfaches Repository</i> – ein Git Repository ohne Arbeitsverzeichnis. Weil das Repository nur als
  Zusammenarbeitspunkt genutzt wird, gibt es keinen Grund, einen Schnappschuss
  ausgecheckt auf der Festplatte zu haben; es sind nur die Git Daten. Mit
  einfachen Begriffen, ein einfaches Repository ist der Inhalt von Deinem <code>.git</code> Verzeichnis in Deinem Projekt und nichts anderes.
</p>

<h2>Die Protokolle</h2>

<p>
  Git kann vier wichtige Netzwerk Protokolle zum Datentransfer benutzen: Lokal,
  Secure Shell (SSH), Git und HTTP. Hier wollen wir diskutieren, was diese
  Protokolle sind und unter welchen grundlegenden Gegebenheiten Du sie benutzen
  möchtest (oder auch nicht).
</p>

<p>
  Es ist wichtig zu beachten, dass alle Protokolle mit Ausnahme von HTTP eine
  funktionierende Git Installation auf dem Server benötigen.
</p>

<h3>Lokales Protokoll</h3>

<p>
  Am einfachsten ist das <i>Lokale Protokoll</i>, wobei das externe Repository in einem anderen Verzeichnis auf der
  Festplatte ist. Das wird oft genutzt, wenn jeder aus Deinem Team Zugriff zu
  einem gemeinsamen Dateisystem hat, zum Beispiel ein eingebundenes NFS, oder
  im unwahrscheinlicheren Fall jeder loggt sich auf bei dem gleichen Computer
  ein. Das letztere ist nicht ideal, weil alle Code Repository Instanzen auf
  dem selben Computer wären, ein katastrophaler Verlust wäre wahrscheinlicher.
</p>

<p>
  Wenn Du ein gemeinsames Dateisystem eingebunden hast, kannst Du von einem
  lokalen Datei-basiertem Repository clonen, pushen und pullen. Um ein
  Repository wie dieses zu clonen, oder ein externes zu einem bestehenden
  Projekt hinzuzufügen, benutze den Pfad zu dem Repository als URL. Um zum
  Beispiel ein lokales Repository zu clonen kannst Du einen Befehl wie diesen
  nutzen:
</p>

<pre>
$ git clone /opt/git/project.git
</pre>

<p>Oder Du kannst das machen:</p>

<pre>
$ git clone file:///opt/git/project.git
</pre>

<p>
  Git arbeitet etwas anders, wenn Du am Anfang der URL ausdrücklich <code>file://</code> angibst. Wenn Du nur den Pfad angibst, und sowohl die Quelle, als auch das
  Ziel sich auf dem selben Dateisystem befinden, versucht Git harte Links zu
  benutzen. Wenn sie sich nicht auf dem selben Dateisystem befinden, kopiert
  Git die benötigten Objekte mit Hilfe der Standardkopierfunktion des
  jeweiligen Betriebssystems. Wenn Du <code>file://</code> angibst, startet Git den Prozess, den es normalerweise zum Übertragen von
  Daten über ein Netzwerk verwendet, dass ist gewöhnlich eine wesentlich
  ineffizientere Methode zum Übertragen der Daten. Der Hauptgrund das <code>file://</code>-Präfix anzugeben ist eine saubere Kopie von dem Repository mit fremden
  Referenzen oder fehlenden Objekten – generell nach einem Import von einem
  anderen Versionskontrollsystem oder etwas ähnliches (siehe Kapitel 9 für
  Wartungsarbeiten). Wir benutzen hier den normalen Pfad, weil das fast immer
  schneller ist.
</p>

<p>
  Um ein lokales Repository zu einem existierenden Git Projekt hinzuzufügen,
  kannst Du einen Befehl wie diesen ausführen:
</p>

<pre>
$ git remote add local_proj /opt/git/project.git
</pre>

<p>
  Dann kannst Du zu diesem externen Repository pushen und davon pullen, als
  würdest Du das über ein Netzwerk machen.
</p>

<p>Die Vorteile</p>

<p>
  Die Vorteile von Datei-basierten Repositories sind die Einfachheit und das
  Nutzen bereits bestehender Datei-Berechtigungen und bestehendem
  Netzwerk-Zugriff. Wenn Du bereits ein gemeinsames Dateisystem hast, zu dem
  das gesamte Team Zugriff hat, ist das Einrichten eines Repositories sehr
  einfach. Du exportierst eine Kopie des einfachen Repositories dahin, wo jeder
  gemeinsamen Zugriff hat und stellst die Lese- und Schreibberechtigungen wie
  bei jedem anderem gemeinsamen Verzeichnis ein. Wir werden im nächsten
  Abschnitt „Git auf einen Server bekommen“ diskutieren, wie man die Kopie
  eines einfachen Repositories für diesen Zweck exportiert.
</p>

<p>
  Dies ist auch eine nette Möglichkeit zum schnellen Abholen von Änderungen aus
  dem Arbeitsverzeichnis von jemand anderem. Wenn Du und ein Kollege an dem
  gleichen Projekt arbeitet und ihr wollt etwas auschecken, dann ein Befehl wie
  <code>git pull /home/john/project</code> ist oft einfacher als das pushen zu einem externen Server und das pullen
  zurück.
</p>

<p>Die Nachteile</p>

<p>
  Die Nachteile von dieser Methode sind, dass ein gemeinsamer Zugriff im
  allgemeinen schwieriger einrichten ist und der Zugriff von mehreren Orten ist
  schwieriger als einfacher Netzwerk Zugriff. Wenn Du von Deinem Laptop zuhause
  pushen möchtest, musst Du eine entfernte Festplatte einbinden. Das kann
  schwierig und langsam sein, verglichen mit netzwerk-basiertem Zugriff.
</p>

<p>
  Es ist auch wichtig zu erwähnen, dass dies nicht unbedingt die schnellste
  Möglichkeit ist, wenn Du ein gemeinsames Dateisystem oder ähnliches hast. Ein
  lokales Repository ist nur dann schnell, wenn Du schnellen Zugriff auf die
  Daten hast. Ein NFS-basiertes Repository ist oftmals langsamer als ein
  Repository über SSH auf dem gleichen Server, weil Git über SSH auf jedem
  System auf den lokalen Festplatten arbeitet.
</p>

<h3>Das SSH Protokoll</h3>

<p>
  Das vermutlich gebräuchlichste Transport-Protokoll für Git ist SSH. Das hat
  den Grund, dass der SSH-Zugriff an den meisten Orten bereits eingerichtet ist
  – und wenn das nicht der Fall ist, einfach zu machen ist. SSH ist außerdem
  das einzige netzwerk-basierte Protokoll von dem man einfach lesen und darauf
  schreiben kann. Die beiden anderen Netzwerk-Protokolle (HTTP und Git) sind
  nur lesbar. Auch wenn sie für die breite Masse sind, brauchst Du trotzdem SSH
  für Deine Schreib-Befehle. SSH ist auch ein authentifiziertes
  Netzwerk-Protokoll, und weil es universell ist, ist es im Allgemeinen einfach
  einzurichten und zu benutzen.
</p>

<p>
  Um ein Git Repository über SSH zu clonen, kannst Du eine ssh:// URL angeben:
</p>

<pre>
$ git clone ssh://user@server/project.git
</pre>

<p>
  Oder Du kannst auch kein Protokoll angeben – Git nimmt SSH an, wenn Du nicht
  eindeutig bist:
</p>

<pre>
$ git clone user@server:project.git
</pre>

<p>
  Du kannst auch keinen Benutzer angeben, und Git nimmt den Benutzer an, als
  der Du gerade eingeloggt bist.
</p>

<p>Die Vorteile</p>

<p>
  Die Vorteile von SSH sind vielseitig. Erstens, grundlegend musst Du es
  benutzen, wenn Du authentifizierten Schreib-Zugriff auf Dein Repository über
  ein Netzwerk haben möchtest. Zweitens, SSH ist relativ einfach einzurichten –
  SSH-Dämonen sind alltäglich, viele Netzwerk-Administratoren haben Erfahrungen
  mit ihnen und viele Betriebssysteme sind mit ihnen eingerichtet oder haben
  Werkzeuge um sie zu verwalten. Als nächstes, Zugriff über SSH ist sicher –
  der gesamte Daten-Transfer ist verschlüsselt und authentifiziert. Als
  letztes, wie Git und die lokalen Protokolle, SSH ist effizient, es macht die
  Daten so kompakt wie möglich bevor es die Daten überträgt.
</p>

<p>Die Nachteile</p>

<p>
  Die negative Seite von SSH ist, dass Du Deine Repositories nicht anonym
  darüber anbieten kannst. Die Leute müssen Zugriff auf Deine Maschine über SSH
  haben um zuzugreifen, auch mit einem Nur-Lese-Zugriff, was SSH nicht
  zuträglich zu Open-Source-Projekten macht. Wenn Du es nur innerhalb von
  Deinem Firmen-Netzwerk benutzt, SSH ist vielleicht das einzige Protokoll mit
  dem Du arbeiten musst. Wenn Du anonymen Nur-Lese-Zugriff zu Deinen Projekten
  erlauben willst, musst Du SSH für Dich einsetzen um zu pushen, aber ein
  anderes Protokoll für andere um zu pullen.
</p>

<h3>Das Git Protokoll</h3>

<p>
  Als nächstes kommt das Git Protokoll. Das ist ein spezieller Dämon, der
  zusammen mit Git kommt. Er horcht auf einem bestimmten Port (9418), dieser
  Service ist vergleichbar mit dem SSH-Protokoll, aber ohne jegliche
  Authentifizierung. Um ein Repository über das Git Protokoll, musst Du die <code>git-daemon-export-ok</code> Datei erstellen – der Dämon bietet kein Repository ohne die Datei darin an –
  außer dieser Datei gibt es keine Sicherheit. Entweder das Git Repository ist
  für jeden zum Clonen verfügbar oder halt nicht. Das bedeutet, dass dieses
  Protokoll generell kein push anbietet. Du kannst push-Zugriff aktivieren;
  aber ohne Authentifizierung, wenn Du den push-Zugriff aktivierst, kann jeder
  im Internet, der Deine Projekt-URL findet, zu Deinem Projekt pushen.
  Ausreichend zu sagen, dass das selten ist.
</p>

<p>Die Vorteile</p>

<p>
  Das Git Protokoll ist das schnellste verfügbare Transfer Protokoll. Wenn Du
  viel Traffic für ein öffentliches Projekt hast oder ein sehr großes Projekt
  hast, dass keine Benutzer-Authentifizierung für den Lese-Zugriff voraussetzt,
  es ist üblich einen Git Dämon einzurichten, der Dein Projekt serviert. Er
  benutzt den selben Daten-Transfer Mechanismus wie das SSH-Protokoll, aber
  ohne den Entschlüsselungs- und Authentifizierungs-Overhead.
</p>

<p>Die Nachteile</p>

<p>
  Die Negativseite des Git Protokoll ist das Fehlen der Authentifizierung. Es
  ist generell unerwünscht, dass das Git Protokoll der einzige Zugang zu dem
  Projekt ist. Im Allgemeinen willst Du es mit SSH-Zugriff für die Entwickler
  paaren, die push (Schreib) Zugriff haben und jeder andere benutzt <code>git://</code> für Nur-Lese-Zugriff. Es ist vielleicht auch das das schwierigste Protokoll
  beim Einrichten. Es muss ein eigener Dämon laufen, welcher Git-spezifisch ist
  – wir wollen im „Gitosis“-Abschnitt in diesem Kapitel schauen, wie man einen
  einrichtet – es setzt eine <code>xinetd</code>-Konfiguration oder ähnliches voraus, das ist nicht immer ein Spaziergang. Es
  setzt auch einen Firewall-Zugriff auf den Port 9418 voraus, das ist kein
  Standard-Port, den Firmen-Firewalls immer erlauben. Hinter einer großen
  Firmen-Firewall ist dieser unklare Port häufig gesperrt.
</p>

<h3>Das HTTP/S Protokoll</h3>

<p>
  Als letztes haben wir das HTTP Protokoll. Das Schöne am HTTP bzw. HTTPS
  Protokoll ist die Einfachheit des Einrichtens. Im Grunde musst Du nur das
  einfach Git Repository in Dein HTTP Hauptverzeichnis legen und einen
  speziellen <code>post-update</code> hook (xxx) einrichten und schon bist Du fertig (siehe Kapitel 7 für Details
  zu Git hooks (xxx)). Jetzt kann jeder, der auf den Web-Server mit dem
  Repository zugreifen kann, das Repository clonen. Um Lese-Zugriff auf das
  Repository über HTTP zu erlauben, führe die folgenden Befehle aus:
</p>

<pre>
$ cd /var/www/htdocs/
$ git clone --bare /path/to/git_project gitproject.git
$ cd gitproject.git
$ mv hooks/post-update.sample hooks/post-update
$ chmod a+x hooks/post-update
</pre>

<p>
  Das ist alles. Der <code>post-update</code> hook (xxx) der standardmäßig zusammen mit Git kommt führt den richtigen
  Befehl aus (<code>git update-server-info</code>), damit der HTTP-Server das Repository richtig abruft und klont. Dieser
  Befehl läuft, wenn Du zu diesem Repository per SSH pusht, andere Leute können
  dann clonen mit dem Befehl
</p>

<pre>
$ git clone http://example.com/gitproject.git
</pre>

<p>
  In diesem besonderen Fall benutzen Dir den <code>/var/www/htdocs</code>-Pfad, der typisch für Apache-Setups ist, aber Du kannst jeden statischen
  Web-Server benutzen – nur das einfache Repository in den richtigen Ordner
  legen. Die Git-Daten werden als einfache statische Dateien serviert (siehe
  Kapitel 9 für Details, wie es genau serviert wird).
</p>

<p>
  Es ist möglich, Git-Daten auch über HTTP zu pushen, trotzdem wird diese
  Technik nicht oft eingesetzt und es setzt komplexe WebDAV-Anforderungen
  voraus. Weil es selten genutzt wird, werden wir das nicht in diesem Buch
  behandeln. Wenn Du Interesse am HTTP-Push-Protokoll hast, kannst Du das
  Einrichten unter <code>http://www.kernel.org/pub/software/scm/git/docs/howto/setup-git-server-over-http.txt</code> nachlesen. Eine Schöne Sache über Git-Push über HTTP ist, dass Du jeden
  WebDAV-Server benutzen kannst, ohne spezifische Git-Features; also kannst Du
  diese Funktionalität nutzen, wenn Dein Web-Hosting-Provider WebDAV
  unterstützt, um Änderungen auf Deine Webseite zu schreiben.
</p>

<p>Die Vorteile</p>

<p>
  Die Positivseite beim Benutzen des HTTP-Protokolls ist, dass es einfach
  einzurichten ist. Das Ausführen von einer Handvoll notwendiger Befehle ist
  ein einfacher Weg, um der Welt Lese-Zugriff auf Dein Git-Repository zu geben.
  Es braucht nur ein paar Minuten. Das HTTP Protokoll benötigt auch nicht viele
  Ressourcen auf Deinem Server. Es braucht generell nur einen statischen Server
  um die Daten zu auszuliefern. Ein normaler Apache-Server kann Tausende von
  Dateien pro Sekunde servieren – es ist schwierig selbst einen kleinen Server
  zu überlasten.
</p>

<p>
  Du kannst Deine Repositories auch als Nur-Lese-Repositories über HTTPS
  servieren, Du kannst also den Daten-Transfer verschlüsseln. Oder Du kannst so
  weit gehen, dass die Clients spezifische signierte SSL-Zertifikate benutzen.
  Im Allgemeinen, wenn Du soweit gehst, ist es einfacher öffentliche
  SSH-Schlüssel zu benutzen; aber es ist vielleicht für Deinen Fall eine
  bessere Lösung, signierte SSL-Zertifikate zu benutzen oder andere
  HTTP-basierte Authentifizierungs-Methoden für Nur-Lese-Zugriff über HTTPS zu
  benutzen.
</p>

<p>
  Eine andere schöne Sache ist, dass HTTP so oft genutzt wird, dass
  Firmen-Firewalls oft Traffic über den HTTP-Port erlauben.
</p>

<p>Die Nachteile</p>

<p>
  Die Unterseite vom Servieren von Deinem Repository über HTTP ist, dass es
  recht ineffizient für den Client ist. Es braucht im Allgemeinen länger zu
  clonen oder Daten vom Repository zu holen und Du hast oft wesentlich mehr
  Netzwerk-Overhead und Transfer-Volumen als mit jedem anderen Netzwerk
  Protokoll. Weil es nicht so intelligent beim Daten-Transfer ist, um nur die
  benötigten Daten zu übertragen – es gibt keine dynamische Arbeit auf dem
  Server bei diesen Aktionen – das HTTP Protokoll wird oft als <i>dummes</i> Protokoll bezeichnet. Für mehr Informationen über die Unterschiede bei der
  Effizienz zwischen dem HTTP Protokoll und den anderen Protokollen: siehe
  Kapitel 9.
</p>

<h2>Git auf einen Server bekommen</h2>

<p>
  Um zunächst einen beliebigen Git Server einzurichten, musst Du ein
  existierendes Repository in ein neues einfaches Repository exportieren – ein
  Repository, dass kein Arbeitsverzeichnis enthält. Das ist im Allgemeinen
  einfach zu erledigen. Um zunächst Dein Repository zu klonen, um ein neues
  einfaches Repository anzulegen, führst Du den Klonen-Befehl mit der <code>--bare</code> Option aus. Per Konvention haben einfache Repository Verzeichnisse die
  Endung <code>.git</code>, wie hier:
</p>

<pre>
$ git clone --bare my_project my_project.git
Cloning into bare repository 'my_project.git'...
done.
</pre>

<p>
  Die Ausgabe für diesen Befehl ist etwas verwirrend. Weil <code>clone</code> im Grunde ein <code>git init</code> und ein <code>git fetch</code> ist, sehen wir eine Ausgabe vom <code>git init</code>-Teil, der ein leeres Verzeichnis anlegt. Die eigentliche Objekt-Übertragung
  erzeugt keine Ausgabe, aber sie findet statt. Du solltest jetzt eine Kopie
  von den Git-Verzeichnis Daten in Deinem <code>my_project.git</code> Verzeichnis haben.
</p>

<p>Dies ist entsprechend zu etwas wie</p>

<pre>
$ cp -Rf my_project/.git my_project.git
</pre>

<p>
  Es gibt ein paar kleine Unterschiede in der Konfigurationsdatei, aber für
  Deine Zwecke ist es nahezu dasselbe. Es nimmt das Git-Repository selbst, ohne
  ein Arbeitsverzeichnis, und erzeugt ein Verzeichnis speziell für es allein.
</p>

<h3>Inbetriebnahme des einfachen Repository auf einem Server</h3>

<p>
  Jetzt da Du eine einfache Kopie Deines Repository hast, ist alles was Du tun
  musst das Aufsetzen auf einem Server und das Einrichten Deiner Protokolle.
  Angenommen, Du hast einen Server mit dem Namen <code>git.example.com</code>, zu dem Du SSH-Zugang hast, und Du möchtest alle Deine Git Repositories im
  Verzeichnis <code>/opt/git</code> speichern. Du kannst Dein neues Repository einrichten, indem Du Dein
  einfaches Repository dorthin kopierst:
</p>

<pre>
$ scp -r my_project.git user@git.example.com:/opt/git
</pre>

<p>
  An diesem Punkt können andere Benutzer, die SSH-Zugang zu dem gleichen Server
  und Lesezugriff auf das <code>/opt/git</code> Verzeichnis haben, Dein Repository klonen:
</p>

<pre>
$ git clone user@git.example.com:/opt/git/my_project.git
</pre>

<p>
  Wenn sich ein Benutzer per SSH mit dem Server verbindet und Schreibzugriff zu
  dem Verzeichnis <code>/opt/git/my_project.git</code> hat, wird er automatisch auch Push-Zugriff haben. Git wird automatisch die
  richtigen Gruppenschreibrechte zu einem Repository hinzufügen, wenn Du den <code>git init</code> Befehl mit der <code>--shared</code> Option ausführst:
</p>

<pre>
$ ssh user@git.example.com
$ cd /opt/git/my_project.git
$ git init --bare --shared
</pre>

<p>
  Du siehst wie einfach es ist ein Git Repository zu nehmen, eine einfache
  Version zu erzeugen, es auf einen Server zu platzieren zu dem Du und Deine
  Mitarbeiter SSH-Zugriff haben.
</p>

<p>
  Es ist wichtig zu beachten, dass dies wortwörtlich alles ist, was Du tun
  musst, um einen nützlichen Git-Server laufen zu lassen, zu dem mehrere
  Personen Zugriff haben – füge auf dem Server einfach SSH-fähige Konten und
  irgendwo ein einfaches Repository hinzu, zu dem alle Benutzer Schreib- und
  Lesezugriff haben.
</p>

<p>
  In den nächsten Abschnitten wirst Du sehen, wie man auf anspruchsvollere
  Konfigurationen erweitert. Diese Diskussion wird beinhalten nicht
  Benutzerkonten für jeden Benutzer hinzufügen zu müssen, öffentlichen
  Lese-Zugriff auf Repositories hinzuzufügen, die Einrichtung von Web-UIs, die
  Benutzung des Gitosis-Tools und weiteres. Aber denke daran, zur
  Zusammenarbeit mit ein paar Personen an einem privaten Projekt, ist alles was
  Du <i>brauchst</i> ein SSH-Server und ein einfaches Repository.
</p>

<h3>Kleine Konfigurationen</h3>

<p>
  Wenn Du eine kleine Ausrüstung hast oder Git gerade in Deinem Unternehmen
  ausprobierst und nur ein paar Entwickler hast, sind die Dinge einfach für
  dich. Einer der kompliziertesten Aspekte der Einrichtung eines Git-Servers
  ist die Benutzerverwaltung. Wenn einige Repositories für bestimmte Benutzer
  nur lesend zugänglich sein sollen und andere Benutzer Lese/Schreib-Zugriff
  haben sollen, können Zugriff und Berechtigungen ein bisschen schwierig zu
  organisieren sein.
</p>

<p>SSH-Zugriff</p>

<p>
  Wenn Du bereits einen Server hast, zu dem alle Entwickler SSH-Zugriff haben,
  ist es generell einfach, Dein erstes Repository einzurichten, weil Du fast
  keine Arbeit zu erledigen hast (wie wir im letzen Abschnitt abgedeckt haben).
  Wenn Du komplexere Zugriffskontroll-Berechtigungen auf Deine Repositories
  willst, kannst Du diese mit normalen Dateisystem-Berechtigungen des
  Betriebssystems Deines Servers bewältigen.
</p>

<p>
  Wenn Du Deine Repositories auf einem Server platzieren möchtest, der keine
  Accounts für jeden aus Deinem Team hat, der Schreibzugriff haben soll, dann
  musst Du SSH-Zugriff für diese Personen einrichten. Wir nehmen an, wenn Du
  einen Server hast, mit welchem Du dies tun möchtest, Du bereits einen
  SSH-Server installiert hast und so greifst Du auf den Server zu.
</p>

<p>
  Es gibt ein paar Wege allen Mitgliedern Deines Teams Zugriff zu geben. Der
  erste ist einen Account für jeden einzurichten, was unkompliziert aber mühsam
  sein kann. Du möchtest vielleicht nicht für jeden Benutzer <code>adduser</code> ausführen und ein temporäres Passwort setzen.
</p>

<p>
  Eine zweite Methode ist, einen einzigen ‚git‘-Benutzer auf der Maschine zu
  erstellen und jeden Benutzer, der Schreibzugriff haben soll, nach einem
  öffentliche SSH-Schlüssel zu fragen und diesen Schlüssel zu der <code>~/.ssh/authorized_keys</code>-Datei Deines neuen ‚git‘ Benutzers hinzuzufügen. Das beeinflusst die
  Commit-Daten in keiner Weise – der SSH-Benutzer, mit dem Du Dich verbindest,
  beeinflusst die von Dir aufgezeichneten Commits nicht.
</p>

<p>
  Ein anderer Weg ist, einen LDAP-Server zur Authentifizierung zu benutzen oder
  eine andere zentrale Authentifizierungsquelle, die Du vielleicht bereits
  eingerichtet hast. Solange jeder Benutzer Shell-Zugriff zu der Maschine hat,
  sollte jede SSH-Authentifizierungsmethode funktionieren, die Du Dir
  vorstellen kannst.
</p>

<h2>Generiere Deinen öffentlichen SSH-Schlüssel</h2>

<p>
  Darüber hinaus benutzen viele Git-Server öffentliche SSH-Schlüssel zur
  Authentifizierung. Um einen öffentlichen Schlüssel bereitzustellen muss jeder
  Benutzer Deines Systems einen solchen Schlüssel generieren, falls sie noch
  keinen haben. Dieser Prozess ist bei allen Betriebssystemen ähnlich. Als
  erstes solltest Du überprüfen, ob Du nicht schon einen Schlüssel hast.
  Standardmäßig werden die SSH-Schlüssel der Benutzer in ihrem <code>~/.ssh</code>-Verzeichnis gespeichert. Du kannst einfach überprüfen, ob Du einen Schlüssel
  hast, indem Du in das Verzeichnis gehst und den Inhalt auflistest:
</p>

<pre>
$ cd ~/.ssh
$ ls
authorized_keys2  id_dsa       known_hosts
config            id_dsa.pub
</pre>

<p>
  Du suchst nach einem Dateienpaar namens <code>irgendetwas</code> und <code>irgendetwas.pub</code>, die Datei <code>irgendetwas</code> heißt normalerweise <code>id_dsa</code> oder <code>id_rsa</code>. Die <code>.pub</code>-Datei ist Dein öffentlicher Schlüssel, und die andere Datei ist Dein
  privater Schlüssel. Wenn Du diese Dateien nicht hast (oder gar kein <code>.ssh</code>-Verzeichnis hast), kannst Du sie mit dem Ausführen des Programms <code>ssh-keygen</code> erzeugen. Das Programm wird mit dem SSH-Paket auf Linux/Mac-Systemen
  mitgeliefert und kommt mit dem MSysGit-Paket unter Windows:
</p>

<pre>
$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/schacon/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /Users/schacon/.ssh/id_rsa.
Your public key has been saved in /Users/schacon/.ssh/id_rsa.pub.
The key fingerprint is:
43:c5:5b:5f:b1:f1:50:43:ad:20:a6:92:6a:1f:9a:3a schacon@agadorlaptop.local
</pre>

<p>
  Zunächst wird bestätigt, wo Du den Schlüssel speichern möchtest (<code>.ssh/id_rsa</code>) und dann wird zweimal nach der Passphrase gefragt, die Du leer lassen
  kannst, wenn Du kein Passwort bei der Benutzung des Schlüssels eintippen
  möchtest.
</p>

<p>
  Jeder Benutzer der dies macht, muss seinen öffentlichen Schlüssel an sich
  senden oder wer auch immer den Git-Server administriert (angenommen Du
  benutzt eine SSH-Server Konfiguration, die öffentliche Schlüssel benötigt).
  Alles was die Benutzer tun müssen ist, den Inhalt der <code>.pub</code>-Datei zu kopieren und an Dich per E-Mail zu schicken. Der öffentliche
  Schlüssel sieht etwa wie folgt aus:
</p>

<pre>
$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSU
GPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3
Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XA
t3FaoJoAsncM1Q9x5+3V0Ww68/eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw/Pb0rwert/En
mZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z/il8b+gw3r3+1nKatmIkjn2so1d01QraTlMqVSsbx
NrRFi9wrf+M7Q== schacon@agadorlaptop.local
</pre>

<p>
  Eine detailliertere Anleitung zur Erstellung eines SSH-Schlüssels unter den
  verschiedenen Betriebssystemen ist der GitHub-Leitfaden für SSH-Schlüssel
  unter <code>http://github.com/guides/providing-your-ssh-key</code>.
</p>

<h2>Einrichten des Servers</h2>

<p>
  Nun kommen wir zur Einrichtung des SSH-Zugangs auf der Server-Seite. In
  diesem Beispiel verwendest Du die <code>authorized_keys</code>-Methode zur Authentifizierung der Benutzer. Wir nehmen auch an, dass Du eine
  gebräuchliche Linux-Distribution wie Ubuntu verwendest. Zuerst erstellst Du
  den Benutzer ‚git‘ und ein <code>.ssh</code>-Verzeichnis für diesen Benutzer.
</p>

<pre>
$ sudo adduser git
$ su git
$ cd
$ mkdir .ssh
</pre>

<p>
  Als nächstes ist es nötig, einige öffentliche SSH-Schlüssel der Entwickler zu
  der <code>authorized_keys</code>-Datei des Benutzers hinzuzufügen. Nehmen wir an, dass Du ein paar Schlüssel
  per E-Mail empfangen hast und diese in temporären Dateien gespeichert hast.
  Die öffentlichen Schlüssel sehen wieder etwa wie folgt aus:
</p>

<pre>
$ cat /tmp/id_rsa.john.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCB007n/ww+ouN4gSLKssMxXnBOvf9LGt4L
ojG6rs6hPB09j9R/T17/x4lhJA0F3FR1rP6kYBRsWj2aThGw6HXLm9/5zytK6Ztg3RPKK+4k
Yjh6541NYsnEAZuXz0jTTyAUfrtU3Z5E003C4oxOj6H0rfIF1kKI9MAQLMdpGW1GYEIgS9Ez
Sdfd8AcCIicTDWbqLAcU4UpkaX8KyGlLwsNuuGztobF8m72ALC/nLF6JLtPofwFBlgc+myiv
O7TCUSBdLQlgMVOFq1I2uPWQOkOWQAHukEOmfjy2jctxSDBQ220ymjaNsHT4kgtZg2AYYgPq
dAv8JggJICUvax2T9va5 gsg-keypair
</pre>

<p>Du hängst sie einfach an Deine <code>authorized_keys</code>-Datei an:</p>

<pre>
$ cat /tmp/id_rsa.john.pub &gt;&gt; ~/.ssh/authorized_keys
$ cat /tmp/id_rsa.josie.pub &gt;&gt; ~/.ssh/authorized_keys
$ cat /tmp/id_rsa.jessica.pub &gt;&gt; ~/.ssh/authorized_keys
</pre>

<p>
  Jetzt kannst Du einen leeren Ordner für sie anlegen, indem Du den Befehl <code>git init</code> mit der Option <code>--bare</code> ausführst. Damit wird ein Repository ohne ein Arbeitsverzeichnis erzeugt.
</p>

<pre>
$ cd /opt/git
$ mkdir project.git
$ cd project.git
$ git --bare init
</pre>

<p>
  Dann können John, Josie oder Jessica die erste Version ihres Projektes in das
  Repository hochladen, indem sie es als externes Repository hinzufügen und
  einen Branch hochladen. Beachte, dass sich bei jeder Projekterstellung jemand
  mit der Maschine auf eine Shell verbinden muss, um ein einfaches Repository
  zu erzeugen. Lass uns <code>gitserver</code> als Hostnamen des Servers verwenden, auf dem Du den Benutzer ‚git‘ und das
  Repository eingerichtet hast. Wenn Du den Server intern betreibst und das DNS
  so eingerichtet hast, dass <code>gitserver</code> auf den Server zeigt, dann kannst Du die Befehle ziemlich wie hier benutzen:
</p>

<pre>
# on Johns computer
$ cd myproject
$ git init
$ git add .
$ git commit -m 'initial commit'
$ git remote add origin git@gitserver:/opt/git/project.git
$ git push origin master
</pre>

<p>
  An diesem Punkt können die anderen das Repository klonen und Änderungen
  ebenso leicht hochladen:
</p>

<pre>
$ git clone git@gitserver:/opt/git/project.git
$ cd project
$ vim README
$ git commit -am 'fix for the README file'
$ git push origin master
</pre>

<p>
  Mit dieser Methode kannst Du schnell für eine Handvoll Entwickler einen
  Lese/Schreib Git-Server zum Laufen bekommen.
</p>

<p>
  Als zusätzliche Vorsichtsmaßnahme kannst Du den Benutzer ‚git‘ so
  beschränken, dass er nur Git-Aktivitäten mit einem limitierten Shell-Tool
  namens <code>git-shell</code> ausführen kann, dass mit Git kommt. Wenn Du das als Login-Shell des
  ‚git‘-Benutzers einrichtest, dann hat der Benutzer ‚git‘ keinen normalen
  Shell-Zugriff auf den Server. Zur Benutzung bestimme <code>git-shell</code> anstatt von bash oder csh als Login-Shell Deines Benutzers. Um das zu tun
  wirst Du wahrscheinlich Deine <code>/etc/passwd</code> editieren:
</p>

<pre>
$ sudo vim /etc/passwd
</pre>

<p>Am Ende solltest Du eine Zeile finden, die in etwa so aussieht:</p>

<pre>
git:x:1000:1000::/home/git:/bin/sh
</pre>

<p>
  Ändere <code>/bin/sh</code> zu <code>/usr/bin/git-shell</code> (oder führe <code>which git-shell</code> aus, um zu sehen, wo es installiert ist). Die Zeile sollte in etwa so
  aussehen:
</p>

<pre>
git:x:1000:1000::/home/git:/usr/bin/git-shell
</pre>

<p>
  Jetzt kann der ‚git‘-Benutzer die SSH-Verbindung nur noch verwenden, um
  Git-Repositories hochzuladen und herunterzuladen. Der Benutzer kann sich
  nicht mehr per Shell zur Maschine verbinden. Wenn Du es versuchst, siehst Du
  eine Login-Ablehnung wie diese:
</p>

<pre>
$ ssh git@gitserver
fatal: What do you think I am? A shell?
Connection to gitserver closed.
</pre>

<h2>Öffentlicher Zugang</h2>

<p>
  Was ist, wenn Du anonymen Lese-Zugriff zu Deinem Projekt ermöglichen
  möchtest? Vielleicht möchtest Du ein Open-Source Projekt, anstatt einem
  privaten, nicht öffentlichen Projekt hosten. Oder Du hast ein paar
  automatisierte Build-Server oder Continuous Integration Server, die ständig
  wechseln, und Du möchtest für diese nicht dauernd neue SSH-Schlüssel
  generieren. Dann wäre es doch schön, wenn ein anonymer Lese-Zugriff zu Deinem
  Projekt möglich wäre.
</p>

<p>
  Der wahrscheinlich einfachste Weg für kleinere Konfigurationen ist, einen
  Webserver, in dessen Basisverzeichnis die Git Repositorys liegen, laufen zu
  lassen und den <code>post-update</code> Hook, den wir im ersten Abschnitt dieses Kapitels erwähnt haben, zu
  aktivieren. Gehen wir vom vorherigen Beispiel aus. Sagen wir, Du hast Deine
  Repositorys im Verzeichnis <code>/opt/git</code> und ein Apache-Server läuft auf Deiner Maschine. Du kannst dafür jeden
  beliebigen Webserver benutzen, aber in diesem Beispiel demonstrieren wir das
  Ganze an Hand einer Apache Basis-Konfiguration. Dies sollte Dir eine
  Vorstellung geben, wie Du es mit dem Webserver Deiner Wahl umsetzen kannst.
</p>

<p>Zuerst musst Du den Hook aktivieren:</p>

<pre>
$ cd project.git
$ mv hooks/post-update.sample hooks/post-update
$ chmod a+x hooks/post-update
</pre>

<p>
  Wenn Du eine ältere Git Version als 1.6 benutzt, brauchst Du den <code>mv</code>-Befehl nicht auszuführen. Das Namensschema mit der .sample Endung wurde erst
  bei den neueren Git Versionen eingeführt.
</p>

<p>
  Welche Aufgabe hat der <code>post-update</code> Hook? Er enthält in etwa folgendes:
</p>

<pre>
$ cat .git/hooks/post-update
#!/bin/sh
#
# An example hook script to prepare a packed repository for use over
# dumb transports.
#
# To enable this hook, rename this file to "post-update".
#

exec git-update-server-info
</pre>

<p>
  Wenn Du via SSH etwas auf den Server hochlädst, wird Git den Befehl <code>git-update-server-info</code> ausführen. Dieser Befehl aktualisiert alle Dateien, die benötigt werden,
  damit das Repository über HTTP geholt (fetch) beziehungsweise geklont werden
  kann.
</p>

<p>
  Als nächstes musst Du einen VirtualHost Eintrag zu Deiner
  Apache-Konfiguration hinzufügen. Das dort angegebene DocumentRoot Verzeichnis
  muss mit dem Basisverzeichnis Deiner Git Projekte übereinstimmen. In diesem
  Beispiel gehen wir davon aus, dass ein Wildcard-DNS Eintrag besteht, der
  dafür sorgt, dass <code>*.gitserver</code> auf den Server zeigt, auf dem das Ganze hier läuft:
</p>

<pre>
&lt;VirtualHost *:80&gt;
    ServerName git.gitserver
    DocumentRoot /opt/git
    &lt;Directory /opt/git/&gt;
        Order allow, deny
        allow from all
    &lt;/Directory&gt;
&lt;/VirtualHost&gt;
</pre>

<p>
  Da die Apache-Instanz, die das CGI-Skript ausführt, standardmäßig unter dem
  Benutzer <code>www-data</code> läuft, musst Du auch die Unix Eigentümer-Gruppe des Verzeichnisses <code>/opt/git</code> auf <code>www-data</code> setzen. Ansonsten kann Dein Webserver die Repositorys nicht lesen:
</p>

<pre>
$ chgrp -R www-data /opt/git
</pre>

<p>
  Nach einem Neustart des Apache, solltest Du in der Lage sein, Deine
  Repositorys innerhalb diesem Verzeichnis zu klonen, indem Du die URL für das
  jeweilige Projekt angibst.
</p>

<pre>
$ git clone http://git.gitserver/project.git
</pre>

<p>
  Auf diese Art und Weise kannst Du in wenigen Minuten einen HTTP-basierten
  Lese-Zugriff auf all Deine Projekte für eine große Anzahl von Benutzern
  ermöglichen. Ein Git Daemon ist eine andere einfache Möglichkeit für einen
  öffentlichen Zugang. Wenn Du diese Methode bevorzugst, solltest Du Dir den
  nächsten Abschnitt unbedingt anschauen.
</p>

<h2>GitWeb</h2>

<p>
  Da Du jetzt sowohl einen einfachen Lese- und Schreibzugriff, als auch einen
  schreibgeschützten Zugang auf Dein Projekt hast, wird es jetzt Zeit eine
  simple webbasierte Visualisierung dafür einzurichten. Git wird mit einem
  CGI-Skript namens GitWeb ausgeliefert, welches für diese Aufgabe häufig
  verwendet wird. Auf Seiten, wie zum Beispiel <code>http://git.kernel.org</code>, kannst Du Dir GitWeb in Aktion anschauen (siehe Abbildung 4-1).
</p>

<p>
  Insert 18333fig0401.png Abbildung 4-1. Die webbasierte Benutzeroberfläche von
  GitWeb.
</p>

<p>
  Wenn Du Dir anschauen möchtest, wie GitWeb bei Deinem Projekt ausschauen
  würde, gibt es dafür eine einfache Möglichkeit. Wenn Du einen einfachen
  Webserver wie <code>lighttpd</code> or <code>webrick</code> auf Deinem Server installiert hast, kannst Du mit einem in Git integrierten
  Kommando eine temporäre Instanz von GitWeb starten. Da <code>lighttpd</code> auf vielen Linux Rechnern bereits installiert ist, kannst Du versuchen ihn
  zum Laufen zu bringen, indem Du das Kommando&emsp14;&emsp14;<code>git instaweb</code> in Deinem Projektverzeichnis ausführst. Bei Mac OS X 10.5 alias Leopard ist
  Ruby bereits vorinstalliert. Falls Du also einen Mac verwendest, solltest Du
  es mal mit <code>webrick</code> versuchen. Um <code>instaweb</code> mit einem anderen Webserver als <code>lighthttpd</code> zu starten, kannst Du an den Befehl die Option <code>--httpd</code> anhängen.
</p>

<pre>
$ git instaweb --httpd=webrick
[2009-02-21 10:02:21] INFO  WEBrick 1.3.1
[2009-02-21 10:02:21] INFO  ruby 1.8.6 (2008-03-03) [universal-darwin9.0]
</pre>

<p>
  Dadurch wird auf dem Port 1234 ein HTPPD Server gestartet. Gleichzeitig wird
  automatisch Dein Webbrowser mit der entsprechenden Seite geöffnet. Das Ganze
  gestaltet sich also ziemlich einfach. Wenn Du dann fertig bist und den Server
  wieder beenden willst, kannst Du das gleiche Kommando mit der Option <code>--stop</code> ausführen:
</p>

<pre>
$ git instaweb --httpd=webrick --stop
</pre>

<p>
  Wenn Du das Web Interface für Dein Team oder ein von Dir gehostetes
  Open-Source Projekt dauerhaft zur Verfügung stellen willst, musst Du das
  CGI-Skript so einrichten, dass es von Deinem normalen Webserver zur Verfügung
  gestellt werden kann. Bei manchen Linux Distributionen ist ein <code>gitweb</code> Paket enthalten, welches Du via <code>apt</code> oder <code>yum</code> installieren kannst. Vielleicht probierst Du das einfach mal zuerst aus. Wir
  werden hier nämlich die manuelle Installation von GitWeb nur kurz
  überfliegen. Zum Starten benötigst Du den Git Quellcode. Dort ist GitWeb
  enthalten und Du kannst damit Dein angepasstest CGI-Skript erstellen:
</p>

<pre>
$ git clone git://git.kernel.org/pub/scm/git/git.git
$ cd git/
$ make GITWEB_PROJECTROOT="/opt/git" \
        prefix=/usr gitweb
$ sudo cp -Rf gitweb /var/www/
</pre>

<p>
  Bitte beachte, dass Du dem Kommando angeben musst, wo sich Deine Git
  Repositorys befinden. Dazu wird die <code>GITWEB_PROJECTROOT</code> Variable verwendet. Jetzt musst Du den Apache noch so konfigurieren, dass er
  CGI für das Skript verwendet. Dazu kannst Du einen VirtualHost einrichten:
</p>

<pre>
&lt;VirtualHost *:80&gt;
    ServerName gitserver
    DocumentRoot /var/www/gitweb
    &lt;Directory /var/www/gitweb&gt;
        Options ExecCGI +FollowSymLinks +SymLinksIfOwnerMatch
        AllowOverride All
        order allow,deny
        Allow from all
        AddHandler cgi-script cgi
        DirectoryIndex gitweb.cgi
    &lt;/Directory&gt;
&lt;/VirtualHost&gt;
</pre>

<p>
  Ich möchte Dich noch einmal darauf hinweisen, dass GitWeb prinzipiell mit
  jedem CGI-fähigen Webserver funktioniert. Wenn Du einen anderen Webserver
  bevorzugst, sollte es also kein Problem sein, GitWeb dafür einzurichten. Nach
  einem Neustart Deines Apache solltest Du jetzt in der Lage sein, Deine
  Repositorys über die Adresse <code>http://gitserver/</code> in GitWeb anzuschauen. Gleichzeitig kannst Du über <code>http://git.gitserver</code> Deine Repositorys per HTTP klonen und abholen (im Sinne eines Fetch).
</p>

<h2>Gitosis</h2>

<p>
  Das manuelle Verwalten der öffentlichen Benutzerschlüssel in der Datei <code>authorized_keys</code> ist auf Dauer nicht sinnvoll. Wenn Du hunderte von Benutzer verwalten musst,
  wird dieser Prozess noch viel schwieriger und macht keinen Spaß mehr. Du
  musst jedes mal über die Shell auf Deinen Server zugreifen und es gibt auch
  keine Zugriffsberechtigungen. Jeder der in der <code>authorized_keys</code> Datei eingetragen ist, hat auf jedes Projekt Lese- und Schreibzugriff.
</p>

<p>
  Vielleicht ist es deshalb sinnvoll, dass Du Dich mit dem weit verbreiteten
  Projekt Gitosis beschäftigst. Gitosis ist im Grunde eine Sammlung von
  Skripts, die Dir dabei helfen, sowohl die Datei <code>authorized_keys</code> zu verwalten, als auch ein paar einfache Zugriffsberechtigungen zu setzen.
  Das wirklich interessante an diesem Werkzeug ist es, dass die
  Benutzeroberfläche zum Hinzufügen von Benutzern oder Setzen von
  Berechtigungen, kein Web Interface ist, sondern ein spezielles Git
  Repository. Die ganzen Informationen werden in diesem Projekt verwaltet und
  sobald dieses gepusht wird, konfiguriert Gitosis den Server auf Basis dieser
  Daten entsprechend um. Das ist ziemlich cool, oder?
</p>

<p>
  Die Installation von Gitosis ist nicht einfach, aber auf jeden Fall machbar.
  Am einfachsten gestaltet sich die Installation auf einem Linux Server. In
  unserem Beispiel verwenden wir dafür einen Standard Ubuntu Server in der
  Version 8.10.
</p>

<p>
  Gitosis setzt einige Python Werkzeuge voraus. Deshalb solltest Du zuerst das
  Python Setuptools Paket installieren. Unter Ubuntu wird es als
  python-setuptools zur Verfügung gestellt:
</p>

<pre>
$ apt-get install python-setuptools
</pre>

<p>
  Danach klonst Du Gitosis von der offiziellen Projektseite und installierst
  es:
</p>

<pre>
$ git clone https://github.com/tv42/gitosis.git
$ cd gitosis
$ sudo python setup.py install
</pre>

<p>
  Einige ausführbare Dateien, welche von Gitosis benötigt werden, werden mit
  dem Skript installiert. Außerdem will Gitosis seine Repositorys im
  Verzeichnis <code>/home/git</code> ablegen. Das ist auch ok so. Allerdings liegen unsere Repositorys bereits im
  Verzeichnis <code>/opt/git</code>, aber anstatt alles umzukonfigurieren, erstellst Du einfach eine symbolische
  Verknüpfung (symlink):
</p>

<pre>
$ ln -s /opt/git /home/git/repositories
</pre>

<p>
  Gitosis wird Deine Schlüssel für Dich verwalten. Deshalb musst Du die bereits
  vorhandene Datei <code>authorized_keys</code> entfernen, die Schlüssel später wieder hinzufügen und Gitosis die
  automatisierte Verarbeitung dieser Datei überlassen. Zuerst musst Du also die
  Datei <code>authorized_keys</code> aus dem Weg räumen:
</p>

<pre>
$ mv /home/git/.ssh/authorized_keys /home/git/.ssh/ak.bak
</pre>

<p>
  Wenn Du für den Benutzer ‚git‘ die Shell auf die <code>git-shell</code> gesetzt hast (siehe Kapitel 4.4), musst Du die normale Shell für diesen
  Benutzer wieder aktivieren. Den Benutzern wird es danach immer noch nicht
  möglich sein sich einzuloggen, dafür sorgt Gitosis. Ändere also in Deiner <code>/etc/passwd</code> die Zeile
</p>

<pre>
git:x:1000:1000::/home/git:/usr/bin/git-shell
</pre>

<p>zurück in folgendes:</p>

<pre>
git:x:1000:1000::/home/git:/bin/sh
</pre>

<p>
  Jetzt wird es Zeit Gitosis zu initialisieren. Dafür musst Du das Kommando <code>gitosis-init</code> mit Deinem öffentlichen Schlüssel ausführen. Wenn sich Dein öffentlicher
  Schlüssel nicht auf dem Server befindet, musst Du ihn vorher dorthin
  kopieren:
</p>

<pre>
$ sudo -H -u git gitosis-init &lt; /tmp/id_dsa.pub
Initialized empty Git repository in /opt/git/gitosis-admin.git/
Reinitialized existing Git repository in /opt/git/gitosis-admin.git/
</pre>

<p>
  Dem Benutzer, mit dem hier angegeben Schlüssel, ist es jetzt möglich, das Git
  Repository, mit dem Gitosis konfiguriert wird, zu modifizieren. Als nächstes
  musst Du manuell das Execute-Bit für das Skript <code>post-update</code> in Deinem neuen „Verwaltungs“-Repository setzen.
</p>

<pre>
$ sudo chmod 755 /opt/git/gitosis-admin.git/hooks/post-update
</pre>

<p>
  Jetzt kann es losgehen. Wenn Du alles richtig eingerichtet hast, kannst Du
  jetzt versuchen Dich über SSH einzuloggen. Du musst dafür den Benutzer
  verwenden, dem der öffentliche Schlüssel gehört, den Du in den vorherigen
  Schritten hinzugefügt hast. Du solltest dann in etwa folgende Ausgabe
  erhalten:
</p>

<pre>
$ ssh git@gitserver
PTY allocation request failed on channel 0
ERROR:gitosis.serve.main:Need SSH_ORIGINAL_COMMAND in environment.
  Connection to gitserver closed.
</pre>

<p>
  Das bedeutet das Gitosis Dich als Benutzer kennt, aber Dich ausschließt, weil
  Du nicht versuchst ein Git Kommando auszuführen. Lass uns also ein Git
  Kommando ausprobieren. Wir klonen dazu das Verwaltungsrepository von Gitosis:
</p>

<pre>
# on your local computer
$ git clone git@gitserver:gitosis-admin.git
</pre>

<p>
  Jetzt hast Du auf Deinem Rechner ein Verzeichnis mit dem Namen <code>gitosis-admin</code>. Dieses besteht aus hauptsächlich zwei Teilen:
</p>

<pre>
$ cd gitosis-admin
$ find .
./gitosis.conf
./keydir
./keydir/scott.pub
</pre>

<p>
  Mit Hilfe der Datei <code>gitosis.conf</code> kannst Du die Benutzer, Repositorys und die Zugriffsrechte festlegen. Im
  Verzeichnis <code>keydir</code> werden alle öffentlichen Schlüssel der Benutzer abgelegt, die einen Zugriff
  auf Deine Repositorys haben sollen und zwar für jeden Benutzer eine einzelne
  Datei. Der Name der Datei, die sich jetzt bereits im <code>keydir</code> Verzeichnis befindet (in diesem Beispiel ist es <code>scott.pub</code>), wird bei Dir anders lauten. Die Beschreibung, die sich am Ende des
  öffentlichen Schlüssels befindet, der beim initialen Import mit dem Skript <code>gitosis-init</code> angegeben wurde, wird von Gitosis als Dateiname verwendet.
</p>

<p>
  Wenn Du Dir die Datei <code>gitosis.conf</code> anschaust, sollten lediglich Daten für das Projekt <code>gitosis-admin</code> enthalten sein. <code>gitosis-admin</code> ist das Projekt, welches Du gerade geklont hast.
</p>

<pre>
$ cat gitosis.conf
[gitosis]

[group gitosis-admin]
members = scott
writable = gitosis-admin
</pre>

<p>
  In dieser Datei wird Dir angezeigt, dass nur der Benutzer ‚scott‘ — das ist
  der Anwender mit dessen öffentlichen Schlüssel Gitosis initialisiert wurde —
  Zugriff auf das Projekt <code>gitosis-admin</code> hat.
</p>

<p>
  Lass uns jetzt für Dich ein neues Projekt hinzufügen. Dazu fügen wir eine
  neue Sektion mit dem Namen <code>mobile</code> hinzu, in der wir alle Teammitglieder aus dem „Mobile“-Team und alle
  Repositorys, die von Ihnen benötigt werden, auflisten. Da ‚scott‘ bisher der
  einzige Benutzer im System ist, werden wir ihn als einziges Teammitglied
  hinzufügen. Das erste von uns erzeugte Projekt mit dem wir anfangen, nennt
  sich <code>iphone_project</code>:
</p>

<pre>
[group mobile]
members = scott
writable = iphone_project
</pre>

<p>
  Immer wenn Du Änderungen im Projekt <code>gitosis-admin</code> durchführst, musst Du diese Änderungen auch einchecken und auf den Server
  pushen, damit diese auch eine Wirkung zeigen:
</p>

<pre>
$ git commit -am 'add iphone_project and mobile group'
[master 8962da8] add iphone_project and mobile group
 1 file changed, 4 insertions(+)
$ git push origin master
Counting objects: 5, done.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 272 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@gitserver:gitosis-admin.git
   fb27aec..8962da8  master -&gt; master
</pre>

<p>
  Du kannst Deinen ersten Push für das neue Projekt <code>iphone_project</code> ausführen, indem Du Deinen Server als Remote zu Deinem lokalen Repository
  hinzufügst und dann auf diesen pushst. Ab jetzt musst Du auf Deinem Server
  nicht mehr manuell ein Bare Repository für neue Projekte erstellen. Gitosis
  übernimmt diese Aufgabe für Dich, sobald es den ersten Push erhält:
</p>

<pre>
$ git remote add origin git@gitserver:iphone_project.git
$ git push origin master
Initialized empty Git repository in /opt/git/iphone_project.git/
Counting objects: 3, done.
Writing objects: 100% (3/3), 230 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@gitserver:iphone_project.git
 * [new branch]      master -&gt; master
</pre>

<p>
  Beachte, dass Du den Pfad nicht angeben musst (es ist sogar so, wenn Du ihn
  angibst, wird es nicht funktionieren). Gib lediglich ein Doppelpunkt gefolgt
  vom Namen des Projekts an. Das reicht Gitosis aus, um den richtigen Pfad für
  Dich zu finden.
</p>

<p>
  Da Du an diesem Projekt nicht alleine, sondern mit Deinen Freunden, arbeiten
  willst, musst Du deren öffentliche Schlüssel wieder hinzufügen. Aber anstatt
  diese manuell in der Datei <code>~/.ssh/authorized_keys</code> auf Deinem Server einzutragen, fügst Du jeden Schlüssel als einzelne Datei
  in das Verzeichnis <code>keydir</code> hinzu. Der Name der Dateien entspricht den gleichen Namen, auf die Du in der
  Datei <code>gitosis.conf</code> referenzierst. Lass uns für John, Josie und Jessica die öffentlichen
  Schlüssel hinzufügen:
</p>

<pre>
$ cp /tmp/id_rsa.john.pub keydir/john.pub
$ cp /tmp/id_rsa.josie.pub keydir/josie.pub
$ cp /tmp/id_rsa.jessica.pub keydir/jessica.pub
</pre>

<p>
  Damit diese Personen Lese- und Schreibzugriff auf das Projekt <code>iphone_project</code> haben, musst Du sie dem ‚mobile‘-Team hinzufügen:
</p>

<pre>
[group mobile]
members = scott john josie jessica
writable = iphone_project
</pre>

<p>
  Nachdem Du diese Änderung commitet und gepusht hast, können alle vier
  Benutzer dieses Projekt lesen und schreiben.
</p>

<p>
  Mit Gitosis kann man auch einfache Zugriffsberechtigungen setzen. Wenn John
  nur Lesezugriff zum Projekt haben soll, dann kannst Du stattdessen folgendes
  angeben:
</p>

<pre>
[group mobile]
members = scott josie jessica
writable = iphone_project

[group mobile_ro]
members = john
readonly = iphone_project
</pre>

<p>
  Mit dieser Konfiguration kann John das Projekt klonen und kann neue Stände
  herunterladen, aber Gitosis wird jeden Push von ihm ablehnen. Du kannst
  beliebig viele Gruppen angeben, die jeweils unterschiedliche Benutzer und
  Projekte enthalten. Ebenso ist es möglich eine andere Gruppe als Mitglied
  hinzuzufügen damit deren Teammitglieder automatisch miteinbezogen werden.
  Dazu musst Du bei der Gruppe <code>@</code> als Präfix angeben:
</p>

<pre>
[group mobile_committers]
members = scott josie jessica

[group mobile]
members   = @mobile_committers
writable  = iphone_project

[group mobile_2]
members   = @mobile_committers john
writable  = another_iphone_project
</pre>

<p>
  Solltest Du Probleme mit Gitosis haben, hilft es Dir vielleicht, wenn Du den
  Eintrag <code>loglevel=DEBUG</code> in der Sektion <code>[gitosis]</code> hinzufügst. Wenn Du keinen Push auf das Verwaltungsrepository ausführen
  kannst und Du Dich damit ausgeschlossen hast, kannst Du die Konfiguration
  unter <code>/home/git/.gitosis.conf</code> manuell bearbeiten. Diese Datei verwendet Gitosis als Konfigurationsdatei.
  Bei einem Push wird diese durch die im Verwaltungsrepository enthaltene Datei
  ersetzt. Wenn Du diese Datei also manuell änderst, bleibt diese bis zum
  nächsten erfolgreichen Push auf das Projekt <code>gitosis-admin</code> bestehen.
</p>

<h2>Gitolite</h2>

<p>
  In diesem Abschnitt werde ich einen kurzen Einblick in Gitolite geben und die
  Basisinstallation und Konfiguration besprechen. Jedoch kann meine kurze
  Einführung nicht die ausführliche <a href="http://sitaramc.github.com/gitolite/master-toc.html">Dokumentation</a>, die Gitolite bietet, ersetzen. Es könnte sein, dass es gelegentlich
  Änderungen an diesem Abschnitt gibt, deshalb solltest Du auch einen Blick auf
  die <a href="http://sitaramc.github.com/gitolite/progit.html">aktuellste Version</a> wagen.
</p>

<p>
  Gitolite ist als Schicht für die Zugriffsberechtigung oberhalb von Git
  angeordnet und verwendet <code>sshd</code> oder <code>httpd</code>&emsp14;&emsp14;zur Authentifikation. (Kurze Wiederholung: Bei der
  Authentifizierung wird der Benutzer identifiziert und die
  Zugriffsberechtigung entscheidet, ob der Benutzer die gewünschte Operation
  ausführen darf oder nicht).
</p>

<p>
  Gitolite ermöglicht es Dir die Berechtigungen auf Repository Ebene
  festzulegen, erlaubt aber zusätzlich auch die Berechtigung auf Ebene von
  Branches oder Tags innerhalb eines Repositorys zu definieren. Das bedeutet
  also, dass Du festlegen kannst, dass bestimmte Leute (oder eine Gruppe von
  Leuten) nur bestimmte „refs“ (Branches oder Tags) pushen können, andere
  Personen sollen das wiederum nicht können.
</p>

<h3>Installation</h3>

<p>
  Auch ohne Studium der ausführlichen Dokumentation, die Gitolite beiliegt,
  gestaltet sich die Installtion sehr einfach. Du benötigst dazu einen Account
  auf irgendeiner Art von Unix Server. Du brauchst keine Root-Rechte,
  vorausgesetzt Git, Perl und ein OpenSSH kompatibler SSH Server sind bereits
  installiert. In unserem Beispiel verwenden wir den Benutzer <code>git</code> auf einem Host mit dem Namen <code>gitserver</code>.
</p>

<p>
  Gitolite ist in Bezug auf „Server“-Software ein wenig ungewöhnlich — der
  Zugriff erfolgt per SSH und somit ist jede auf dem Server vorhandene User-Id
  ein potentieller „gitolite host“. In unserem Beispiel werden wir die
  einfachste Methode der Installation beschreiben. Die anderen Möglichkeiten
  können der Dokumentation entnommen werden.
</p>

<p>
  Zu Beginn legst Du auf Deinem Server einen Benutzer mit dem Namen <code>git</code> an und loggst Dich mit diesem ein. Danach kopierst Du Deinen öffentlichen
  SSH Schlüssel (die Datei lautet <code>~/.ssh/id_rsa.pub</code>, falls Du <code>ssh-keygen</code> mit den Standardoptionen ausgeführt hast) von Deiner Workstation auf den
  Server und nennst ihn entsprechend dem Schema <code>&lt;yourname>.pub</code> um (in unserem Beispiel verwenden wir die Datei <code>scott.pub</code>). Danach führst Du die folgenden Kommandos aus:
</p>

<pre>
$ git clone git://github.com/sitaramc/gitolite
$ gitolite/install -ln
    # assumes $HOME/bin exists and is in your $PATH
$ gitolite setup -pk $HOME/scott.pub
</pre>

<p>
  Der letzte Befehl erzeugt ein neues Git Repository mit dem Namen <code>gitolite-admin</code> auf Deinem Server.
</p>

<p>
  Zum Abschluss musst Du auf Deiner Workstation den Befehl <code>git clone git@gitserver:gitolite-admin</code> ausführen. Jetzt bist Du im Prinzip fertig. Gitolite ist nun auf Deinem
  Server installiert und auf Deiner Workstation liegt das neue Repository <code>gitolite-admin</code> vor. Du kannst Gitolite nun administrieren, indem Du Änderungen an diesem
  Repository ausführst und zurück auf den Server pushst.
</p>

<h3>Benutzerdefinierte Installation</h3>

<p>
  Obwohl die schnelle Standardinstallation für die meisten Leute ausreicht,
  gibt es ein paar Möglichkeiten die Installation an Deine Gegebenheiten
  anzupassen, falls Du dies für nötig hältst. Teilweise reicht es, die rc Datei
  zu bearbeiten. Sollte das nicht ausreichen, gibt es genügend Dokumentation,
  die beschreibt, wie Gitolite angepasst werden kann.
</p>

<h3>Konfigurationsdateien und Regeln für die Zugangskontrolle</h3>

<p>
  Nachdem die Installation abgeschlossen ist, wechselst Du in den <code>gitolite-admin</code> Klon auf Deiner Workstation und stöberst dort am besten ein wenig herum:
</p>

<pre>
$ cd ~/gitolite-admin/
$ ls
conf/  keydir/
$ find conf keydir -type f
conf/gitolite.conf
keydir/scott.pub
$ cat conf/gitolite.conf

repo gitolite-admin
    RW+                 = scott

repo testing
    RW+                 = @all
</pre>

<p>
  Es ist wichtig anzumerken, dass „scott“ (das entspricht dem Name des
  öffentlichen Schlüssel, den Du beim Ausführen des <code>gitolite setup</code> Kommandos angegeben hast) Lese- und Schreibzugriff auf das <code>gitolite-admin</code> Repository hat. Zusätzlich existiert eine Datei mit dem gleichen Namen.
  Diese beinhaltet den öffentlichen Schlüssel.
</p>

<p>
  Neue Benutzer hinzuzufügen gestaltet sich einfach. Um einen neuen Anwender
  mit dem Namen „alice“ hinzuzufügen, benötigst Du ihren öffentlichen
  Schlüssel. Diesen nennst Du in <code>alice.pub</code> um und legst ihn im Verzeichnis <code>keydir</code> Deines geklonten <code>gitolite-admin</code> Repositorys auf Deiner Workstation ab. Danach stagst Du die Änderungen,
  commitest diese und pushst sie auf den Server und voi­là, der Benutzer wurde
  hinzugefügt.
</p>

<p>
  Die Syntax der Gitolite Konfigurationsdateien ist gut dokumentiert, deshalb
  gehen wir hier nur auf die wichtigsten Details ein.
</p>

<p>
  Einzelne Benutzer oder Repositorys können zur besseren Verwaltung zu Gruppen
  zusammengefasst werden. Die Gruppennamen verhalten sich wie Makros. Beim
  Anlegen ist es unabhängig, ob es sich um Projekte oder Benutzer handelt.
  Diese Festlegung wird erst getroffen, wenn diese „Makros“ verwendet werden.
</p>

<pre>
@oss_repos      = linux perl rakudo git gitolite
@secret_repos   = fenestra pear

@admins         = scott
@interns        = ashok
@engineers      = sitaram dilbert wally alice
@staff          = @admins @engineers @interns
</pre>

<p>
  Du kannst die Berechtigungen auf „ref“-Ebene (Branches und Tags) festlegen.
  Im folgenden Beispiel darf die Gruppe „interns“ nur auf den „int“ Branch
  pushen. Die Benutzer der Gruppe „engineers“ können jeden Branch pushen, der
  mit dem Prefix „eng-“ beginnt. Zusätzlich kann diese Gruppe jeden Tag, mit
  dem Namen „rc“, gefolgt von einer einzelnen Zahl, pushen. Die Benutzer der
  Gruppe „admins“ können jede Operation für jeden „ref“ durchführen (inklusive
  Rewind-Operationen).
</p>

<pre>
repo @oss_repos
    RW  int$                = @interns
    RW  eng-                = @engineers
    RW  refs/tags/rc[0-9]   = @engineers
    RW+                     = @admins
</pre>

<p>
  Der Ausdruck hinter <code>RW</code> oder <code>RW+</code> ist ein regulärer Ausdruck (Regex), gegen den die Referenzen (ref), die
  gepusht werden, verglichen werden. Wir nennen das auch „Refex“. Mit einem
  solchen Refex hat man ein sehr mächtiges Werkzeug an der Hand und kann noch
  viel mehr machen, als hier aufgezeigt ist. Aus diesem Grund solltest Du es
  aber damit auch nicht übertreiben, wenn Du mit den regulären Ausdrücken aus
  Perl nicht vertraut bist.
</p>

<p>
  Wie Du vielleicht bereits vermutest hast, stellt Gitolite den Ausdruck <code>refs/heads/</code> den Refex voran, wenn diese nicht mit <code>refs/</code> beginnen.
</p>

<p>
  Ein wichtige Eigenschaft der Syntax der Konfigurationsdatei ist, dass nicht
  alle Regeln für ein Repository an einer gemeinsamen Stelle festgehalten
  werden müssen. Du kannst die ganzen allgemeingültigen Dinge, wie zum Beispiel
  die oben gezeigten Regeln für alle <code>oss_repos</code>, an einer Stelle zusammenfassen und später dann spezifische Regeln für die
  einzelnen Fälle festlegen. Zum Beispiel folgendermaßen:
</p>

<pre>
repo gitolite
    RW+                     = sitaram
</pre>

<p>
  Diese Regel gehört dann zum Regelsatz des <code>gitolite</code> Repository.
</p>

<p>
  An dieser Stelle fragst Du Dich vielleicht, wie die Zugriffsregeln eigentlich
  angewandt werden. Lass uns das kurz anschauen.
</p>

<p>
  Es gibt zwei Ebenen für die Zugriffsberechtigung in Gitolite. Die erste
  befindet sich auf Repository Ebene. Wenn Du Lese- oder Schreifzugriff auf
  jede Ref in einem Repository hast, dann kannst Du damit das ganze Repository
  sowohl lesen, als auch schreiben.
</p>

<p>
  Die zweite Ebene bezieht sich auf Branches oder Tags innerhalb eines
  Repositorys. Auf dieser Ebene kann allerdings nur der Schreibzugriff
  beschränkt werden. Der Benutzername, die Art des Zugriffs (<code>W</code> oder <code>+</code>) und der Refname, der aktualisiert wird, sind bekannt. Gitolite prüft, ob
  einer dieser Regeln auf diese Kombination zutrifft (hierbei ist allerdings zu
  beachten, dass der Refname mit dem regulären Ausdruck verglichen und kein
  eins zu eins String-Vergleich durchgeführt wird). Die Zugriffsregeln werden
  entsprechend der Reihenfolge innerhalb der Konfigurationsdatei abgearbeitet.
  Wenn eine Kombination zutrifft, kann der Push durchgeführt werden. Trifft
  keine zu, dann wird der Push verweigert.
</p>

<h3>Erweiterte Zugriffsberechtigungen mit „deny“ Regeln</h3>

<p>
  Bis jetzt waren alle vorgestellten Berechtigungen entweder <code>R</code>, <code>RW</code>, oder <code>RW+</code>. Gitolite kennt aber noch eine weitere Berechtigung: <code>-</code>, welche für „deny“, also ablehnen steht. Dies gibt Dir noch viel mehr
  Möglichkeiten, allerdings auf Kosten der Komplexität, denn ab jetzt ist ein
  Falltrough nicht die einzige Möglichkeit, wie ein Zugriff auf das Repository
  abgelehnt wird. Das heißt, die Reihenfolge der aufgestellte Regeln, hat auch
  eine Bedeutung.
</p>

<p>
  Nehmen wir mal an, dass bei unserem bekannten Beispiel, die Gruppe
  „engineers“ auf alle Branches, außer master und integ, Rewind-Rechte haben
  soll. Das können wir folgendermaßen erreichen:
</p>

<pre>
    RW  master integ    = @engineers
    -   master integ    = @engineers
    RW+                 = @engineers
</pre>

<p>
  Noch einmal zur Wiederholung, man muss jede einzelne Regel von oben nach
  unten durchgehen und überprüfen, ob eine Regel auf den aktuellen
  Zugriffsmodus zutrifft oder ob eine Deny-Regel den Zugriff verhindert. Ein
  Push auf den Branch master oder integ, welcher nicht einem Rewind Push
  entspricht, wird durch die erste Regel erlaubt. Ein Rewind Push auf diese
  Refs trifft also auf die erste Regel nicht zu. Deshalb wird die zweite Regel
  geprüft und auf Grund der Deny-Regel wird der Push verweigert. Jeder Push
  (unabhängig, ob es sich um einen Rewind Push oder einen normalen Push
  handelt) auf eine Ref, welche nicht master oder integ entspricht, trifft
  nicht auf einer der beiden ersten Regeln zu, und wird damit auf Grund der
  dritten Regel erlaubt.
</p>

<h3>Ein Push auf Basis von Dateiänderungen einschränken</h3>

<p>
  Neben der Zugriffsbeschränkung auf Basis von Branches, kannst Du genauso
  verhindern, dass eine Änderung an einer bestimmten Datei gepusht wird.
  Beispielsweise ist ein Makefile (oder auch andere Programme) nicht dafür
  geeignet, dass es von jeder x-beliebiegen Person geändert wird. Meist hängen
  von so einem Makefile viele Dinge ab oder vieles könnte schief laufen, wenn
  die Änderungen an der Datei nicht korrekt durchgeführt werden würden. Du
  kannst deshalb Gitolite folgendermaßen konfigurieren:
</p>

<pre>
repo foo
    RW                      =   @junior_devs @senior_devs

    -   VREF/NAME/Makefile  =   @junior_devs
</pre>

<p>
  Alle Anwender von älteren Gitolite Versionen, die auf eine neue Gitolite
  Version wechseln, sollten darauf achten, dass sich die neue Version
  signifikant anders im Bezug auf dieses Feature verhält. Die
  Umstellungsanleitung (migration guide) weist hier auf weitere Details hin.
</p>

<h3>Personenbezogene Branches</h3>

<p>
  Gitolite bietet mit den „personal branches“ (genauer „personal branch
  namespace“) eine weitere Eigenschaft, die im Unternehmensumfeld sehr
  hilfreich sein kann.
</p>

<p>
  Jede Menge Codeänderungen in der Welt von Git passieren, weil jemand einen
  Pull-Request durchführt. Im Unternehmensumfeld ist ein anonymer Zugriff ein
  absolutes No-Go und oft kann eine Entwickler Workstation keine
  Authentifizierung bieten. Deshalb müssen die Änderungen an den zentralen
  Server gepusht werden und jemand anders muss diese von dort abholen.
</p>

<p>
  Dies würde normalerweise zu dem gleichen Branchnamen-Wirrwarr führen, wie es
  in zentralisierten Versionskontrollsystemen anzufinden ist. Außerdem wäre es
  für den Administrator äußerst lästig, die ganzen Berechtigungen dafür zu
  setzen.
</p>

<p>
  Gitolite lässt die Definition eines „personal“ oder „scratch“ Namensraum für
  jeden einzelnen Entwickler zu (zum Beispiel: <code>refs/personal/&lt;devname>/*</code>). Die Dokumentation enthält dazu weitere Details.
</p>

<h3>„Wildcard“ Repositorys</h3>

<p>
  Mit Platzhaltern (eigentlich Perl reguläre Ausdrücke), wie zum Beispiel <code>assignments/s[0-9][0-9]/a[0-9][0-9]</code>, kannst Du in Gitolite auch Repositorys definieren. Außerdem bietet Gitolite
  eine neuen Berechtigungsmodus (<code>C</code>), welcher es den Benutzern ermöglicht, auf Basis dieser Platzhalter,
  Repositorys zu erzeugen. Dem Benutzer, der das Repository erzeugt hat, wird
  dieses automatisch zugewiesen, was es ihm oder ihr ermöglicht anderen
  Benutzern Lese- oder Schreibrechte (<code>R</code> und <code>RW</code>) zuzuweisen, damit diese zum Projekt beitragen können. Wieder möchte ich
  Dich darauf hinweisen, dass die Dokumentation weitere Details enthält.
</p>

<h3>Weitere Besonderheiten</h3>

<p>
  Ich möchte das Thema Gitolite abschließen, indem ich noch ein paar weitere
  Besonderheiten kurz anspreche. Diese und viele weitere Features von Gitolite
  werden ausführlich in der Dokumentation beschrieben.
</p>

<p>
  <b>Protokollierung</b>: Gitolite protokolliert alle Zugriffe, die erfolgreich waren. Wenn Du ein
  bisschen nachlässig bei der Vergabe von Rewind-Rechten (<code>RW+</code>) warst und irgendeiner der Personen mit Rewinde-Rechte dann den <code>master</code> zerstört, dann kann Dir die Protokolldatei eine Menge Arbeit ersparen, weil
  sie Dir hilft, leicht und schnell die SHA Prüfsumme zu finden, die dem
  Erdboden gleich gemacht wurde.
</p>

<p>
  <b>Zugriffsrechte herausfinden</b>: Ein anderes praktisches Merkmal von Gitolite lernst Du kennen, wenn Du
  versuchst Dich über SSH auf dem Server einzuloggen. Gitolite zeigt Dir dann
  alle Repositorys an, auf die Du Zugriff hast und welche Berechtigung Du für
  diese hast. Hierzu ein Beispiel:
</p>

<pre>
    hello scott, this is git@git running gitolite3 v3.01-18-g9609868 on git 1.7.4.4

         R     anu-wsd
         R     entrans
         R  W  git-notes
         R  W  gitolite
         R  W  gitolite-admin
         R     indic_web_input
         R     shreelipi_converter
</pre>

<p>
  <b>Administration aufteilen</b>: Bei richtig großen Installationen kannst Du die Verantwortlichkeit für
  verschiedene Gruppen von Repositorys an verschiedene Leute verteilen, damit
  diese die Repositorys unabhängig verwalten können. Das macht das Leben des
  Haupt-Administrator leichter und verhindert, dass er der Flaschenhals im
  System ist.
</p>

<p>
  <b>Spiegelung</b>: Gitolite kann Dir helfen verschiedene Mirrors (Spiegelserver) zu verwalten.
  Außerdem ist es damit einfach zwischen verschiedenen Mirrors zu wechseln,
  wenn der primäre Server offline ist.
</p>

<h2>Git Daemon</h2>

<p>
  Wenn Du anonymen, öffentlichen Lesezugriff für Deine Repositorys zur
  Verfügung stellen willst, solltest Du Dir mal das Git Protokoll, als Ersatz
  für das HTTP Protokoll anschauen. Der Hauptgrund dafür ist die
  Geschwindigkeit. Das Git Protokoll ist weitaus effizienter und deshalb viel
  schneller als das HTTP Protokoll. Wenn Du Deinen Anwendern Zeit ersparen
  willst, solltest Du es zur Verfügung stellen.
</p>

<p>
  Ich möchte Dich noch mal darauf hinweisen, dass das Git Protokoll nur für
  anonymen (also ohne Authentifizierung) Lesezugriff geeignet ist. Wenn Du
  einen öffentlichen Server betreibst, sollte das Git Protokoll nur für
  Projekte eingesetzt werden, die öffentlich für den Rest einsehbar sein
  sollen. Innerhalb Deines eigenen Netzwerks, welches mit einer Firewall
  abgeschottet ist, ist es sinnvoll, da Du mit dem Git Protokoll einer großen
  Anzahl von Benutzern und Computern (Continuous Integration oder
  Build-Server), Lesezugriff zur Verfügung stellen kannst, ohne dass Du für
  jeden einzelnen Nutzer ein SSH Schlüssel verwalten musst.
</p>

<p>
  Auf jeden Fall ist es sehr einfach das Git Protokoll einzurichten. Im Prinzip
  musst Du nur folgendes tun:
</p>

<pre>
git daemon --reuseaddr --base-path=/opt/git/ /opt/git/
</pre>

<p>
  Mit <code>--reuseaddr</code> teilst Du dem Server mit, dass ein Neustart sofort durchgeführt werden kann,
  ohne darauf zu warten, dass alte, offene Verbindungen mit einem Timeout
  abbrechen. Die Option <code>--base-path</code> erlaubt es den Nutzern, Projekte zu klonen, ohne den gesamten Pfad angeben
  zu müssen. Die Pfadangabe als letztes Argument gibt dem Daemon an, wo sich
  die zu exportierenden Repositorys befinden. Wenn Du eine Firewall
  eingerichtet hast, musst Du zusätzlich den Port 9418 freischalten.
</p>

<p>
  Du kannst den Hintergrunddienst für diesen Prozess auf verschiedene Art und
  Weise einrichten. Das ist natürlich abhängig vom verwendeten Betriebssystem.
  Auf einem Ubuntu System kannst Du dazu ein Upstart Skript verwenden. Zum
  Beispiel fügst Du in der folgenden Datei
</p>

<pre>
/etc/event.d/local-git-daemon
</pre>

<p>
  das folgende Skript ein (Achtung: In neueren Ubuntu-Versionen lautet der Pfad
  /etc/init):
</p>

<pre>
start on startup
stop on shutdown
exec /usr/bin/git daemon \
    --user=git --group=git \
    --reuseaddr \
    --base-path=/opt/git/ \
    /opt/git/
respawn
</pre>

<p>
  Aus Sicherheitsgründen wird dringend empfohlen, dass dieser Daemon als
  Benutzer ausgeführt wird, welcher nur Lesezugriff auf die betreffenden
  Repositorys hat. Du stellst das auf einfache Art und Weise sicher, indem Du
  einen neuen Benutzer ‚git-ro‘ erstellst und den Daemon mit diesem ausführst.
  Einfachheitshalber verwenden wir hier den Benutzer ‚git‘, den wir auch schon
  für Gitosis verwendet haben.
</p>

<p>
  Nach einem Neustart des System wird der Git Daemon automatisch starten. Er
  startet ebenso neu, wenn er unerwartet beendet wird. Der Daemon kann auch
  ohne einen Neustart gestartet werden:
</p>

<pre>
initctl start local-git-daemon
</pre>

<p>
  Auf anderen Systemen kannst Du <code>xinetd</code>, ein Skript in der <code>sysvinit</code>-Umgebung oder irgendetwas anderes verwenden. Du musst nur sicherstellen,
  dass der Befehl als Hintergrunddienst ausgeführt wird.
</p>

<p>
  Als nächstes musst Du dem Gitosis Server mitteilen, für welche Repositorys
  ein anonymer Zugriff über das Git Protokoll möglich sein soll. Für jedes
  einzelne Repository kannst Du individuell festlegen, ob der Git Daemon auf
  dieses Zugriff haben soll. Wenn Du beispielsweise das Git Protokoll für das
  Projekt <code>iphone_project</code> erlauben willst, kannst Du folgendes am Ende der Konfigurationsdatei <code>gitosis.conf</code> einfügen:
</p>

<pre>
[repo iphone_project]
daemon = yes
</pre>

<p>
  Nachdem Du dies eingecheckt und gepusht hast, sollte Dein im Hintergrund
  laufender Git Daemon die Anfragen aller Benutzer, die Zugriff auf den Port
  9418 haben, bearbeiten.
</p>

<p>
  Wenn Du Dich gegen Gitosis entschieden hast, aber trotzdem dem Git Daemon
  verwenden willst, musst Du auf dem Server für jedes Projekt, welches der Git
  Daemon zur Verfügung stellen soll, folgendes ausführen:
</p>

<pre>
$ cd /path/to/project.git
$ touch git-daemon-export-ok
</pre>

<p>
  Wenn diese Datei existiert, erlaubt Git einen anonymen Lesezugriff auf dieses
  Projekt.
</p>

<p>
  In Gitosis kann man ebenso einstellen, welche Projekte in GitWeb dargestellt
  werden sollen. Dazu musst Du als erstes in etwa folgendes in die
  Konfigurationsdatei <code>/etc/gitweb.conf</code> einfügen:
</p>

<pre>
$projects_list = "/home/git/gitosis/projects.list";
$projectroot = "/home/git/repositories";
$export_ok = "git-daemon-export-ok";
@git_base_url_list = ('git://gitserver');
</pre>

<p>
  Um für die einzelnen Projekte festzulegen, dass diese in GitWeb auftauchen,
  musst Du die Einstellung <code>gitweb</code> in der Gitosis Konfigurationsdatei festlegen. Wenn Du beispielsweise willst,
  dass das Repository <code>iphone_project</code> in GitWeb erscheint, muss die Einstellung <code>repo</code> in etwa folgendermaßen aussehen:
</p>

<pre>
[repo iphone_project]
daemon = yes
gitweb = yes
</pre>

<p>
  Das Projekt <code>iphone_projekt</code> wird automatisch in GitWeb angezeigt, sobald Du die Änderungen eingecheckt
  und auf den Server gepusht hast.
</p>

<h2>Git Hosting</h2>

<p>
  Wenn Du Dir die ganze Arbeit sparen willst, die beim Einrichten eines eigenen
  Git Servers so anfällt, kannst Du auch einen der vielen Hosting-Anbieter
  benutzen, um Deine Git Projekte zu verwalten. Wenn Du Dich für diese Option
  entscheidest, hat das gewisse Vorteile: Die Konfiguration bei den
  Hosting-Anbietern ist meist sehr schnell durchgeführt und Du kannst sofort
  mit Deinen Projekten loslegen. Zusätzlich ersparst Du Dir die Wartung und
  Überwachung Deines eigenen Servers. Auch wenn Du für Deine privaten oder
  firmeninternen Projekte einen eigenen Server betreibst, sind solche
  Hosting-Anbieter nützlich, da Du diese dann für Deine Open-Source Projekte
  verwenden kannst. Dadurch wirst Du innerhalb der Open-Source Community
  leichter gefunden und es ist einfacher Dir bei Deinen Projekten zu helfen.
</p>

<p>
  Heutzutage stehen Dir viele Anbieter zur Auswahl. Jeder hat seine Vor- und
  Nachteile. Eine aktuelle Liste von Anbietern findest Du auf der folgenden
  Seite:
</p>

<pre>
https://git.wiki.kernel.org/index.php/GitHosting
</pre>

<p>
  Da wir nicht alle Anbieter vorstellen können und ich zufälligerweise bei
  einem der Anbieter, nämlich GitHub,&emsp14;&emsp14;arbeite, werde ich in
  diesem Kapitel auf diese Plattform näher eingehen. Wir werden die Erstellung
  eines Accounts und die Erzeugung eines Projekts auf GitHub besprechen. Das
  sollte Dir einen leichten Einstieg in die Welt von GitHub ermöglichen.
</p>

<p>
  GitHub ist die mit Abstand größte Open-Source Git Hosting Plattform und
  bietet als einer der wenigen, sowohl öffentliche und private
  Hosting-Optionen. Das erlaubt es Dir, Deine Open-Source Projekte und
  proprietären Code in einer einzelnen Plattform zu verwalten. Sogar für dieses
  Buch haben wir GitHub benutzt, um gemeinsam unter Ausschluss der
  Öffentlichkeit daran zu arbeiten.
</p>

<h3>GitHub</h3>

<p>
  GitHub verwaltet und gruppiert Projekte ein wenig anders ein, als andere
  Code-Hosting Webseiten. GitHub fokussiert sich dabei nicht speziell auf die
  Projekte, sondern eher auf den Anwender. Dazu ein Beispiel. Wenn ich mein
  Projekt <code>grit</code> auf GitHub einstelle, dann findet man dieses nicht unter <code>github.com/grit</code>, sondern unter <code>github.com/schacon/grit</code>. Es gibt in diesem Sinne, keine in Stein gemeißelte Stelle an dem das
  Projekt verwaltet wird. Das bedeutet, man kann ein Projekt nahtlos von einem
  Benutzer zu einem anderen Benutzer übertragen, wenn dieser zum Beispiel das
  Projekt aufgibt.
</p>

<p>
  Wenn ein Anwender ein geschützes, nicht öffentliches Repository auf GitHub
  verwalten will, so muss er dafür bezahlen. Damit verdient die Firma GitHub
  ihr Geld. Aber die Verwaltung und Nutzung für Open-Source Projekte ist
  kostenlos. Die Anzahl der Projekte ist dabei nicht beschränkt. Der Nutzer
  muss dazu lediglich einen Account erstellen. Wie das geht, möchte ich hier
  kurz vorstellen.
</p>

<h2>Einrichten eines Benutzeraccounts</h2>

<p>
  Um loslegen zu können, musst Du Dir einen Benutzeraccount erstellen. Gib dazu
  die Adresse <code>https://github.com/pricing</code> in Deinem Browser ein und wähle den Button „Sign Up“ unter dem „Free
  account“-Bereich aus (siehe Abbildung 4-2). Danach wirst Du auf die
  Anmeldeseite weitergeleitet.
</p>

<p>Insert 18333fig0402.png Abbildung 4-2. Die Angebotsseite von GitHub.</p>

<p>
  Auf dieser Seite musst Du einen Nutzernamen auswählen, der bisher im System
  nicht vorhanden ist. Zusätzlich musst Du Deine E-Mail Adresse angeben, die
  mit Deinem Account verknüpft wird, und ein Passwort angeben (siehe Abbildung
  4-3).
</p>

<p>
  Insert 18333fig0403.png Abbildung 4-3. Das Formular für die GitHub
  Benutzerregistrierung.
</p>

<p>
  Wenn Du Deinen öffentlichen SSH Schlüssel zur Hand hast, kannst Du diesen
  auch gleich bei der Registrierung angeben. Die Vorgehensweise zum Generieren
  eines Schlüssels haben wir bereits im Kapitel 4.3 besprochen. Du musst den
  Inhalt der öffentlichen Schlüsseldatei kopieren und in das „SSH Public Key“
  Formularfeld einfügen. Wenn Du auf den „explain ssh keys“ Link klickst,
  erhälst Du detailierte Anweisungen zum Ausführen dieses Vorgangs auf
  verschiedenen Betriebssystemen. Wenn Du auf den „I agree, sign me up“ Button
  drückst, landest Du in Deinem neuen Benutzer-Dashboard (siehe Abbildung 4-4).
</p>

<p>Insert 18333fig0404.png Abbildung 4-4. Das GitHub Benutzer-Dashboard.</p>

<p>Im nächsten Schritt kannst Du ein neues Repository erzeugen.</p>

<h3>Erzeugen eines neuen Repository</h3>

<p>
  Um ein neues Repository anzulegen, musst Du auf den „create a new one“-Link,
  welcher neben Deinen Repositorys auf dem Benutzer-Dashboard angezeigt wird,
  klicken. Du landest daraufhin im Formular zum Erzeugen eines neuen
  Repositorys (siehe Abbildung 4-5).
</p>

<p>
  Insert 18333fig0405.png Abbildung 4-5. Erzeugen eines Repositorys auf GitHub.
</p>

<p>
  Im Prinzip musst Du nur einen Projektnamen und wenn Du es für nötig erachtest
  eine Beschreibung Deines Projekts angeben. Wenn das erledigt ist, kannst Du
  auf den „Create Repository“ Button klicken. Du hast soeben Dein erstes
  Repository auf GitHub erzeugt (siehe Abbildung 4-6).
</p>

<p>Insert 18333fig0406.png Abbildung 4-6. GitHub Projektinformationen.</p>

<p>
  Da in dem Repository noch kein Code enthalten ist, gibt Dir GitHub ein paar
  Hinweise, wie Du ein neues Projekt anlegst, wie Du ein bereits vorhandes Git
  Projekt auf GitHub pusht oder wie man ein bestehendes Subversion Repository
  in GitHub importieren kann (siehe Abbildung 4-7).
</p>

<p>
  Insert 18333fig0407.png Abbildung 4-7. Anleitung zum Erzeugen eines neuen
  Repository.
</p>

<p>
  Die Hilfestellung entspricht der Vorgehensweise, die ich bereits in diesem
  Buch vorgestellt habe. Um ein neues Git Projekt in einem vorhandenen
  Verzeichnis anzulegen, kannst Du die folgenden Befehle verwenden:
</p>

<pre>
$ git init
$ git add .
$ git commit -m 'initial commit'
</pre>

<p>
  Wenn Du bereits ein lokales Git Repository auf Deinem PC hast, kannst Du
  GitHub als zusätzlichen Remote hinzufügen und den master Branch pushen:
</p>

<pre>
$ git remote add origin git@github.com:testinguser/iphone_project.git
$ git push origin master
</pre>

<p>
  Das war es schon. Dein Projekt ist nun auf GitHub erreichbar. Du kannst jetzt
  die zugehörige URL an jeden weitergeben, den Du am Projekt teilhaben lassen
  willst. In unserem Beispiel lautet der Link hierfür <code>http://github.com/testinguser/iphone_project</code>. Im oberen Header-Bereich jeder Projektseite werden zwei verschiedene Git
  URLs angezeigt (siehe Abbildung 4-8).
</p>

<p>
  Insert 18333fig0408.png Abbildung 4-8. Projekt Header mit der Angabe der
  öffentlichen und privaten URL.
</p>

<p>
  Die „Public Clone URL“ ist eine öffentliche Git URL, die man zum Klonen des
  Projekts verwenden kann. Über diese URL kann lediglich lesend zugegriffen
  werden. Ein Schreibzugriff ist nicht möglich. Du kannst diese URL beliebig
  weiter verteilen und zum Beispiel auch auf Deiner Homepage oder einem anderen
  Medium veröffentlichen.
</p>

<p>
  Die „Your Clone URL“ ist eine auf SSH basierte URL, mit Hilfe derer, vom
  Projekt gelesen, als auch geschrieben werden kann. Diese URL kann aber nur
  der Anwender nutzen, der im Besitz des privaten Schlüssel ist, welcher zu dem
  öffentlichen Schlüssel gehört, der bei dem GitHub Benutzer für dieses Projekt
  hinterlegt ist (Du hast den öffentlichen Schlüssel bei der Registrierung
  Deines Accounts angegeben).. Dieser Link wird allerdings nur Dir angezeigt.
  Andere Benutzer, die Deine Projekte besuchen, können nur die „Public Clone
  URL“ sehen.
</p>

<h3>Import von Subversion</h3>

<p>
  Wenn Du bereits ein Subversion Projekt hast und dieses in Git importieren
  möchtest, kann GitHub Dir diese Aufgabe in vielen Fällen übernehmen. Am Ende
  der Seite mit den Hilfestellungen ist ein Link, der Dich auf die Seite mit
  dem Formular zum Importieren eines Subversion Projekts weiterleitet. Du musst
  in diesem Formular nur die URL des Subversion Projekts angeben (siehe
  Abbildung 4-9).
</p>

<p>
  Insert 18333fig0409.png Abbildung 4-9. Import-Schnittstelle für Subversion
  Projekte.
</p>

<p>
  Wenn Dein Projekt sehr groß, nicht standardkonform oder nicht öffentlich
  einsehbar ist, kann es passieren das dieser Vorgang fehlschlägt. In Kapitel 7
  liefere ich die Antwort, wie ein solcher Import, manuell durchgeführt werden
  kann.
</p>

<h3>Mitarbeiter hinzufügen</h3>

<p>
  Um gemeinsam an einem Projekt zu arbeiten, kannst Du auch andere Personen für
  das Projekt freischalten. Wenn Du willst das John, Josie und Jessica ebenso
  auf das Projekt pushen können, musst Du sie als Mitarbeiter für Dein Projekt
  freischalten. Voraussetzung hierfür ist natürlich, dass Sie alle einen GitHub
  Account besitzen. Nachdem Du sie zum Projekt hinzugefügt hast, können sie
  unter Verwendung ihrer öffentlichen Schlüssel auf das Projekt pushen.
</p>

<p>
  Klicke auf die „edit“-Schaltfläche in der Projektübersicht oder wähle den
  Admin-Tab im oberen Bereich Deines Projekts um zur Administrationsoberfläche
  zu gelangen (siehe Abbildung 4-10).
</p>

<p>
  Insert 18333fig0410.png Abbildung 4-10. GitHub Administrationsoberfläche.
</p>

<p>
  Um einem anderen Benutzer Schreibrechte zu Deinem Projekt zu gewähren, kannst
  Du auf den „Add another collaborator“-Link klicken. Daraufhin erscheint ein
  Eingabefeld, in welches Du die Benutzer eingeben kannst. Während des Tippens,
  erscheint ein kleines Popup, welches Benutzernamen anzeigt, die Deiner
  Eingabe entsprechen. Wenn Du den gewünschten Benutzer gefunden hast, kannst
  Du die „Add“-Schaltfläche betätigen, um diesen Benutzer als Mitarbeiter zu
  Deinem Projekt hinzuzufügen (siehe Abbildung 4-11).
</p>

<p>
  Insert 18333fig0411.png Abbildung 4-11. Ein Mitarbeiter zu Deinem Projekt
  hinzufügen.
</p>

<p>
  Wenn Du Dein Team fertig zusammengestellt hast, solltest Du eine Liste aller
  Mitarbeiter im „Repository Collaborators“-Bereich sehen (siehe Abbildung
  4.12).
</p>

<p>
  Insert 18333fig0412.png Abbildung 4-12. Übersicht über alle Mitarbeiter in
  Deinem Projekt.
</p>

<p>
  Wenn Du einer Person den Zugriff auf Dein Repository entziehen willst, kannst
  Du auf den „revoke“-Link klicken. Dadurch kann diese Person nicht mehr auf
  Dein Repository pushen. Für zukünftige Projekte kannst Du die Liste der
  Benutzer auch für andere Projekte übernehmen.
</p>

<h3>Dein Projekt</h3>

<p>
  Nachdem Du das erste mal auf das GitHub Repository gepusht hast oder es von
  Subversion importiert hast, sieht die Hauptseite Deines GitHub-Projekts
  entsprechend Abbildung 4-13 aus.
</p>

<p>
  Insert 18333fig0413.png Abbildung 4-13. Beispiel einer Hauptseite eines
  GitHub-Projekts.
</p>

<p>
  Wenn andere Dein Projekt in GitHub aufrufen, sehen sie als erstes diese
  Hauptseite. Die verschiedenen Funktionen, die GitHub für ein Projekt
  unterstützt, sind in verschiedene Tabs aufgeteilt. Der „Commit“-Tab enthält
  eine Liste aller Commits in chronologischer Reihenfolge. Dabei steht der
  neueste Commit ganz oben. Eine ähnliche Liste erhälst Du, wenn Du den <code>git log</code> Befehl ausführst. Der „Network“-Tab zeigt alle Benutzer an, die ein Fork von
  Deinem Projekt erstellt haben und Ihren Teil zum Projekt beigetragen haben.
  Im „Download“-Tab kannst Du Binärdateien hochladen oder Zip-Archive
  beziehungsweise Tarballs zur Verfügung stellen, die einem bestimmten Tag
  Deines Projekts entsprechen. Im „Wiki“-Tab findest Du ein Wiki, welches Du zu
  Dokumentationszwecken verwenden kannst. Außerdem kannst Du dort andere
  Informationen über Dein Projekt der Welt mitteilen. Der „Graphs“-Tab stellt
  Dir eine grafische Übersicht zur Verfügung, die Dir anzeigt, wer und wann
  jemand etwas zu Deinem Projekt beigetragen hat. Außerdem enthält dieser Tab
  eine Projekt-Statistik. Der „Source“-Tab, welcher standardmäßig beim Aufruf
  Deines Projekts angezeigt wird, zeigt das oberste Verzeichnis Deines
  Repositorys an. Wenn Dein Projekt eine README-Datei enthält, wird der Inhalt
  dieser Datei unterhalb der Verzeichnisstruktur angezeigt. Zusätzlich zeigt
  dieser Tab eine Übersicht mit den letzten Commit-Informationen an.
</p>

<h3>Fork von einem Projekt erstellen</h3>

<p>
  Wenn Du an einem bereits vorhandenen Projekt mitarbeiten willst und Du zu
  diesem keine Schreibrechte hast, bietet Dir GitHub die Möglichkeit einen Fork
  von diesem Projekt zu erstellen. Wenn Du beim Stöbern durch GitHub bei einem
  interessanten Projekt landest und Du damit ein bisschen spielen willst,
  kannst Du die „Fork“-Schaltfläche im Projekt-Header auswählen. GitHub kopiert
  das gesamte Projekt in Deinen Benutzerbereich. Auf dieses kannst Du jetzt
  auch pushen.
</p>

<p>
  Dadurch das jeder, jedes Projekt kopieren kann, muss sich der Verwalter eines
  Projekts nicht darum kümmern, das jedem Mitarbeiter die entsprechenden
  Schreibrechte erhält. Man kann einfach einen Fork erstellen und auf diesen
  pushen. Der Verwalter des geforkten Projekts kann dieses neue Projekt als
  neuen Remote hinzufügen und die Änderungen davon holen. Dann kann er diese
  Änderungen in das Hauptprojekt mergen.
</p>

<p>
  Um einen Fork von einem Projekt zu erstellen, kannst Du die jeweilige
  Projektseite besuchen (in diesem Fall mojombo/chronic) und die
  „Fork“-Schaltfläche im oberen Bereich auswählen (siehe Abbildung 4-14).
</p>

<p>
  Insert 18333fig0414.png Abbildung 4-14. Durch Betätigen der
  „Fork“-Schaltfläche erhält man eine Kopie eines Repositorys, auf welches man
  Schreibrechte hat.
</p>

<p>
  Nach ein paar Sekunden wirst Du zu der neuen Projektseite weitergeleitet.
  Dort siehst Du auch, dass dieses Projekt ein Fork eines anderen Projekts ist
  (siehe Abbildung 4-15).
</p>

<p>Insert 18333fig0415.png Abbildung 4-15. Dein Fork eines Projekts.</p>

<h3>GitHub Zusammenfassung</h3>

<p>
  Das war alles was ich über GitHub berichten möchte. Herausheben möchte ich
  aber, dass sich die Arbeit mit GitHub sehr einfach gestaltet. Innerhalb
  kürzester Zeit kannst Du Dir einen Account einrichten, ein neues Projekt
  hinzufügen und auf dieses pushen. Wenn Dein Projekt ein Open-Source Projekt
  ist und damit öffentlich einsehbar ist, steht Dir mit GitHub eine riesige
  Community mit zahlreichen Entwicklern zur Seite, die sich an Deinem Projekt
  beteiligen können, indem sie einen Fork erstellen. Vielleicht gelingt Dir mit
  GitHub der Einstieg in die Welt von Git noch einfacher.
</p>

<h2>Zusammenfassung</h2>

<p>
  Es stehen Dir viele Möglichkeiten offen, ein Remote-Repository einzurichten,
  sodass Du mit anderen Entwicklern zusammenarbeiten kannst und Deine Arbeit
  anderen zur Verfügung stellen kannst.
</p>

<p>
  Wenn Du Deinen eigenen Server betreibst, stehen Dir natürlich alle
  Möglichkeiten offen und Du kannst ihn entsprechend Deiner Anforderungen
  konfigurieren. Denoch darf man den Zeitaufwand zum Aufsetzen und Warten des
  Servers nicht unterschätzen. Wenn Du Deine Repositorys bei einem
  Hosting-Anbieter verwaltest, hält sich der Aufwand für die Einrichtung und
  Wartung in Grenzen, allerdings vertraust Du Deine Daten auch einem fremden
  Anbieter an, was manche Firmen oder Organisationen untersagen.
</p>

<p>
  Es sollte Dir nicht schwerfallen eine passende Lösung für Dich, Deine Firma
  oder Deine Organisation zu finden.
</p>

<h1>Distribuierte Arbeit mit Git (xxx)</h1>

<p>
  Du hast jetzt ein externes Repository aufgesetzt, sodass alle Mitglieder des
  Teams ihren Code zur Verfügung stellen können, und Du hast Dich mit den
  wesentlichen Git Befehlen für die Arbeit in einem lokalen Repository vertraut
  gemacht. Als nächstes werden wir uns einige Arbeitsabläufe für distribuierte
  Repositories ansehen, die Git Dir ermöglicht.
</p>

<p>
  In diesem Kapitel wirst Du lernen, wie Du in einer distribuierten Umgebung
  als Entwickler Code zu einem Projekt beisteuern, als für Verantwortlicher
  Code von anderen ins Projekt übernehmen kannst und wie Du das so gestalten
  kannst, dass es in einem Projekt mit einer großen Anzahl von Entwicklern für
  alle Beteiligten möglichst einfach ist.
</p>

<h2>Distribuierte Workflows</h2>

<p>
  Anders als in zentralisierten Versionskontrollsystemen (CVCS) ermöglicht die
  Distribuiertheit von Git eine sehr viel flexiblere Zusammenarbeit von
  Entwicklern. In zentralisierten Systemen fungieren alle Beteiligten als
  gleichwertige Netzknoten, die in mehr oder weniger der gleichen Weise am
  zentralen Knotenpunkt (dem zentralen Repository) arbeiten. In Git dagegen ist
  jeder Beteiligten selbst potentiell zentraler Knotenpunkt. D.h. jeder
  Entwickler kann sowohl Code zu anderen Repositories beitragen und ein
  öffentliches Repository zur Verfügung stellen, an dem wiederum andere
  mitarbeiten. Das ermöglicht eine riesige Anzahl von Möglichkeiten,
  Arbeitsabläufe zu gestalten, die auf das jeweilige Projekt und/oder Team
  perfekt zugeschnitten sind. Wir werden auf einige übliche Paradigmen, die
  diese Flexibilität nutzen, und deren Vor- und Nachteile eingehen. Du kannst
  daraus ein Modell auswählen, oder Du kannst sie miteinander kombinieren, um
  sie an Deine eigenen Erfordernisse anzupassen.
</p>

<h3>Zentralisierter Workflow</h3>

<p>
  In einem zentralisierten System gibt es grob gesagt ein einziges Modell der
  Zusammenarbeit. Ein zentraler Knotenpunkt (oder Repository) kann Code von
  anderen akzeptieren und übernehmen, und alle Beteiligten synchronisieren ihre
  Arbeit damit. Entwickler fungieren als Knoten, die ihre Arbeit an diesem
  einen, zentralen Punkt synchronisieren (siehe Bild 5-1).
</p>

<p>Insert 18333fig0501.png Bild 5-1. Zentralisierter Workflow</p>

<p>
  Das heißt: wenn zwei Entwickler Code aus dem zentralen Repository abholen und
  beide Änderungen vornehmen, dann kann der erste Entwickler seine Änderungen
  ohne Probleme im zentralen Repository abliefern. Der zweite Entwickler muss
  sie zunächst mit den Änderungen des ersten Entwicklers zusammenführen, damit
  er dessen Arbeit nicht überschreibt. Dieses Konzept trifft sowohl auf Git als
  auch auf Subversion (und jedes andere CVCS) zu, und es funktioniert in Git
  perfekt.
</p>

<p>
  In einem kleinen Team oder einem Team, das mit einem zentralisierten Workflow
  zufrieden ist, kann man diesen Workflow ohne weiteres mit Git realisieren.
  Man setzt einfach ein einziges Repository auf und gibt jedem im Team
  Schreibzugriff („push access“). Git sorgt dann dafür, dass niemand die Arbeit
  von anderen überschreiben kann. Wenn ein Entwickler das Repository klont,
  Änderungen vornimmt und dann versucht ins zentrale Repository zu pushen,
  obwohl jemand anders in der Zwischenzeit Änderungen gepusht hat, dann wird
  der Server das zurückweisen. Dem Entwickler wird dann mitgeteilt, dass er
  versucht hat, sogeannte „non-fast-forward“ Änderungen hochzuladen und dass er
  zuvor die Änderungen des anderen Entwicklers herunterladen und mit seinen
  zusammenführen muss. Viele Leute mögen diesen Arbeitsablauf, weil sie mit dem
  Paradigma bereits vertraut sind und sich damit wohl fühlen.
</p>

<h3>Integration-Manager Workflow</h3>

<p>
  Weil Git ermöglicht, eine Vielzahl von externen Repositories zu betreiben,
  ist es außerdem möglich, einen Arbeitsprozess zu gestalten, in dem jeder
  Entwickler Schreibzugriff auf sein eigenes öffentliches Repository hat, aber
  nur Lesezugriff auf die Repositories von allen anderen Beteiligten. In diesem
  Szenario stellt jedes Repository ein eigenes „offizielles“ Projekt dar. Um zu
  einem solchen distribuierten Projekt Änderungen beizusteuern, kannst Du einen
  eigenen, öffentlichen Klon des Projektes anlegen und Deine Änderungen dort
  publizieren. Anschließend kannst Du den Betreiber des Haupt-Repositories
  bitten, Deine Änderungen in sein Repository zu übernehmen. Er kann dann Dein
  Repository als ein externes Repository auf seinem Rechner einrichten, Deine
  Änderungen lokal testen, sie in einen seiner Branches (z.B. master) mergen
  und dann in sein öffentliches Repository pushen. Dieser Prozess läuft wie
  folgt ab (siehe Bild 5-2):
</p>

<ol>
  <li>
    <p>Der Projekt Betreiber pusht in ein öffentliches Repository.</p>
  </li>

  <li>
    <p>Ein Mitarbeiter klont das Repository und nimmt Änderungen daran vor.</p>
  </li>

  <li>
    <p>Der Mitarbeiter pusht diese in sein eigenes öffentliches Repository.</p>
  </li>

  <li>
    <p>
      Der Mitarbeiter schickt dem Betreiber eine E-Mail und bittet darum, die
      Änderungen zu übernehmen.
    </p>
  </li>

  <li>
    <p>
      Der Betreiber richtet das Repository des Mitarbeiters als ein externes
      Repository ein und führt die Änderungen mit einem seiner eigenen Branches
      zusammen.
    </p>
  </li>

  <li>
    <p>
      Der Betreiber pusht die zusammengeführten Änderungen in sein öffentliches
      Repository.
    </p>
  </li>
</ol>

<p>Insert 18333fig0502.png Bild 5-2. Integration-Manager Workflow</p>

<p>
  Dies ist ein weit verbreiteter Arbeitsablauf wie ihn z.B. auch GitHub
  ermöglicht, wo man ein Projekt auf sehr einfache Weise forken und seine
  Änderungen in seinen eigenen Fork pushen kann, um sie anderen zur Verfügung
  zu stellen. Einer der Hauptvorteile dieser Vorgehensweise ist, dass man an
  seinem Fork jederzeit weiterarbeiten, der Betreiber des Projektes Änderungen
  aber auch jederzeit übernehmen kann. Mitarbieter müssen nicht darauf warten,
  dass der Betreiber Änderungen übernimmt – und jeder Beteiligte kann in seinem
  eigenen Rhythmus und Tempo arbeiten.
</p>

<h3>Diktator und Leutnants Workflow</h3>

<p>
  Dies ist Variante eines Workflows mit zahlreichen Repositories, die
  normalerweise von sehr großen Projekten mit hunderten von Mitarbeitern
  verwendet wird. Das bekannteste Beispiel ist wahrscheinlich der Linux Kernel.
  In diesem Projekt sind zahlreiche Integration Manager, die „Leutnants“, für
  verschiedene Bereiche des Repositories zuständig. Für sämtliche Leutnants
  gibt es wiederum einen Integration Manager, der als der „wohlwollende
  Diktator“ („benevolent dictator“) bezeichnet wird. Das Repository des
  wohlwollenden Diktators fungiert als das Referenz-Repository aus dem alle
  Beteiligten ihre eigenen Repositories aktualisieren müssen. Dieser Prozess
  funktioniert also wie folgt (siehe Bild 5-3)
</p>

<ol>
  <li>
    <p>
      Normale Entwickler arbeiten in ihren Arbeitsbranches (xxx) und rebasen
      (xxx) ihre Änderungen auf der Basis des Master Branches. Der Master
      Branch ist derjenige des Diktators.
    </p>
  </li>

  <li>
    <p>
      Die Leutnants mergen die Arbeitsbranches der Entwickler in ihre Master
      Branches.
    </p>
  </li>

  <li>
    <p>
      Der Diktator merged die Master Branches der Leutnants mit seinem eigenen
      Master Branch zusammen.
    </p>
  </li>

  <li>
    <p>
      Der Diktator pusht seinen Master Branch ins Referenz-Repository, sodass
      alle ihre Arbeit wiederum damit synchronisieren (rebasen) können.
    </p>
  </li>
</ol>

<p>Insert 18333fig0503.png Bild 5-3. Wohlwollender Diktator Workflow</p>

<p>
  Diese Art Workflow ist nicht unbedingt weit verbreitet, aber für große
  Projekte oder Projekte mit strikten hierarchischen Rollen sehr nützlich, weil
  der Projektleiter (der Diktator) Arbeit in großem Umfang delegieren und ganze
  Teilbereiche von Code von verschiedenen Endpunkten zusammensammeln und
  integrieren kann.
</p>

<p>
  Wir haben jetzt einige übliche Workflows besprochen, die in einem
  distribuierten System wie Git möglich sind. Natürlich kann man sie
  mannigfaltig abwandeln und miteinander kombinieren, um sie an ein spezielles
  reales Projekt und Team anzupassen. Nachdem Du jetzt hoffentlich in der Lage
  bist, Dir einen Workflow vorzustellen, der für Dich selbst Sinn macht, gehen
  wir auf einige etwas spezifischere Beispiele ein und darauf, wie man die
  verschiedenen Rollen umsetzen kann, die die Workflows ausmachen.
</p>

<h2>An einem Projekt mitarbeiten</h2>

<p>
  Du kennst jetzt einige grundlegende Workflow Varianten, und Du solltest ein
  gutes Verständnis im Umgang mit grundlegenden Git Befehlen haben. In diesem
  Abschnitt lernst Du wie Du an einem Projekt mitzuarbeiten kannst.
</p>

<p>
  Diesen Prozess zu beschreiben ist nicht leicht, weil es so viele Variationen
  gibt. Git ist so unheimlich flexibel, dass Leute auf vielen unterschiedlichen
  Wegen zusammenarbeiten können, und es ist problematisch, zu erklären, wie Du
  arbeiten <i>solltest</i>, weil jedes Projekt ein bisschen anders ist. Zu den Variablen gehören: die
  Anzahl der aktiven Mitarbeiter, der Workflow des Projektes, Deine Commit
  Rechte und möglicherweise eine vorgeschriebene, externe Methode, Änderungen
  einzureichen.
</p>

<p>
  Wie viele Mitarbeiter tragen aktive Code zum Projekt bei? Und wie oft? In
  vielen Fällen findest Du zwei oder drei Entwickler, die täglich einige
  Commits anlegen, möglicherweise weniger in eher (xxx dormant xxx) Projekten.
  In wirklich großen Unternehmen oder Projekten können tausende Entwickler
  involviert sein und täglich dutzende oder sogar hunderte von Patches
  produzieren. Mit so vielen Mitarbeitern ist es aufwendiger, sicher zu
  stellen, dass Änderungen sauber mit der Codebase zusammengeführt werden
  können. Deine Änderungen könnten sich als überflüssig oder dysfunktional
  (xxx) erweisen, nachdem andere Änderungen übernommen wurden, seit Du
  angefangen hast, an Deinen eigenen zu arbeiten oder während sie darauf
  warteten, geprüft und eingefügt (xxx) zu werden.
</p>

<p>
  Die nächste Variable ist der Workflow, der in diesem Projekt besteht. Ist es
  ein zentralisierter Workflow, in dem jeder Entwickler Schreibzugriff auf die
  Hauptentwicklungslinie hat? Hat das Projekt einen Leiter oder Integration
  Manager, der alle Patches prüft? Werden die Patches durch eine bestimmte
  Gruppe oder öffentlich, z.B. durch die Community, geprüft? Nimmst Du selbst
  an diesem Prozess teil? Gibt es ein Leutnant System, in dem Du Deine Arbeit
  zunächst an einen Leutnant übergibst?
</p>

<p>
  Eine weitere Frage ist, welche Commit Rechte Du hast. Wenn Du Schreibrechte
  hast, sieht der Arbeitsablauf, mit dem Du Änderungen beisteuern kannst,
  natürlich völlig anders aus, als wenn Du nur Leserechte hast. Und in
  letzterem Fall: in welcher Form werden Änderungen in diesem Projekt
  akzeptiert? Gibt es dafür überhaupt eine Richtlinie? Wie umfangreich sind die
  Änderungen, die Du jeweils beisteuerst? Und wie oft tust Du das?
</p>

<p>
  Die Antworten auf diese Fragen können maßgeblich beeinflussen, wie Du
  effektiv an einem Projekt mitarbeiten kannst und welche Workflows Du zur
  Auswahl hast. Wir werden verschiedene Aspekte davon in einer Reihe von
  Fallbeispielen besprechen, wobei wir mit simplen Beispielen anfangen und
  später komplexere Szenarios besprechen. Du wirst hoffentlich in der Lage
  sein, aus diesen Beispielen einen eigenen Workflow zu konstruieren, der
  Deinen Anforderungen entspricht.
</p>

<h3>Commit Richtlinien</h3>

<p>
  Bevor wir uns verschiedene konkrete Fallbeispiele ansehen, einige kurze
  Anmerkungen über Commit Meldungen. Gute Richtlinien für Commit Meldungen zu
  haben und sich danach zu richten, macht die Zusammenarbeit mit anderen und
  die Arbeit mit Git selbst sehr viel einfacher. Im Git Projekt gibt es ein
  Dokument mit einer Reihe nützlicher Tipps für das Anlegen von Commits, aus
  denen man Patches erzeugen will. Schaue im Git Quellcode nach der Datei <code>Documentation/SubmittingPatches</code>.
</p>

<p>Zunächst einmal solltest Du keine Whitespace Fehler (xxx) comitten:</p>

<pre>
$ git diff --check
lib/simplegit.rb:5: trailing whitespace.
+    @git_dir = File.expand_path(git_dir)XX
lib/simplegit.rb:7: trailing whitespace.
+ XXXXXXXXXXX
lib/simplegit.rb:26: trailing whitespace.
+    def command(git_cmd)XXXX
</pre>

<p>
  Wenn Du diesen Befehl ausführst, bevor Du einen Commit anlegst, warnt er
  dich, falls in Deinen Änderungen Whitespace Probleme vorliegen, die andere
  Entwickler ärgern könnten.
</p>

<p>
  Versuche außerdem, Deine Änderungen in logisch zusammenhängende Einheiten zu
  gruppieren. Wenn möglich, versuche Commits möglichst leichtverständlich (xxx)
  zu gestalten: arbeite nicht ein ganzes Wochenende lang an fünf verschiedenen
  Problemen und committe sie dann am Montag als einen einzigen, riesigen
  Commit. Selbst wenn Du am Wochenende keine Commits angelegt hast, verwende
  die Staging Area, um Deine Änderungen auf mehrere Commits aufzuteilen,
  jeweils mit einer verständlichen Meldung. Wenn einige Änderungen dieselbe
  Datei betreffen, probiere sie mit <code>git add --patch</code> nur teilweise zur Staging Area hinzuzufügen (das werden wir in Kapitel 6
  noch im Detail besprechen). Der Projekt Snapshot wird am Ende derselbe sein,
  ob Du nun einen einzigen großen oder mehrere kleine Commits anlegst, daher
  versuche, es anderen Entwickler zu erleichtern machen, Deine Änderungen zu
  verstehen. Auf diese Weise machst Du es auch einfacher, einzelne Änderungen
  später herauszunehmen oder rückgängig zu machen. Kapitel 6 beschreibt eine
  Reihe nützlicher Git Tricks, die hilfreich sind, um die Historie
  umzuschreiben oder interaktiv Dateien zur Staging Area hinzuzufügen. Verwende
  diese Hilfsmittel, um eine sauber und leicht verständliche Historie von
  Änderungen aufzubauen.
</p>

<p>
  Ein weitere Sache, der Du ein bisschen Aufmerksamkeit schenken solltest, ist
  die Commit Meldung selbst. Wenn man sich angewöhnt, aussagekräftige und
  hochwertige Commit Meldungen zu schreiben, macht man sich selbst und anderen
  das Leben erheblich einfacher. Im allgemeinen sollte eine Commit Meldung mit
  einer einzelnen Zeile anfangen, die nicht länger als 50 Zeichen sein sollte.
  Dann sollte eine leere Zeile folgen und schließlich eine ausführlichere
  Beschreibung der Änderungen.
</p>

<pre>
Short (50 chars or less) summary of changes

More detailed explanatory text, if necessary.  Wrap it to about 72
characters or so.  In some contexts, the first line is treated as the
subject of an email and the rest of the text as the body.  The blank
line separating the summary from the body is critical (unless you omit
the body entirely); tools like rebase can get confused if you run the
two together.

Further paragraphs come after blank lines.

 - Bullet points are okay, too

 - Typically a hyphen or asterisk is used for the bullet, preceded by a
   single space, with blank lines in between, but conventions vary here
</pre>

<p>
  Wenn Du Deine Commit Meldungen in dieser Weise formatierst, kannst Du Dir und
  anderen eine Menge Ärger ersparen. Das Git Projekt selbst hat
  wohl-formatierte Commit Meldungen. Wir empfehlen, einmal <code>git log --no-merges</code> in diesem Repository auszuführen, um einen Eindruck zu erhalten, wie eine
  gute Commit History eines Projektes aussehen kann.
</p>

<p>
  In den folgenden Beispielen hier und fast überall in diesem Buch verwende ich
  keine derartigen, schön formatierten Meldungen. Stattdessen verwende ich die <code>-m</code> Option zusammen mit <code>git commit</code>. Also folge meinen Worten, nicht meinem Beispiel.
</p>

<h3>Kleine Teams</h3>

<p>
  Das einfachste Setup, mit dem Du zu tun haben wirst, ist ein privates Projekt
  mit ein oder zwei Entwicklern. Mit „privat“ meine ich, dass es „closed
  source“, d.h. nicht lesbar für Dritte ist. Alle beteiligten Entwickler haben
  Schreibzugriff auf das Repository.
</p>

<p>
  In einer solchen Umgebung kann man einen ähnlichen Workflow verwenden, wie
  für Subversion oder ein anderes zentralisiertes System. Du hast dann immer
  noch Vorteile wie, dass Du offline committen kannst und dass Branching und
  Merging so unglaublich einfach ist. Der Hauptunterschied ist, dass Merges auf
  der Client Seite stattfinden und nicht, wenn man committet, auf dem Server.
  Schauen wir uns an, wie die Arbeit von zwei Entwicklern in einem gemeinsamen
  Repository abläuft. Der erste Entwickler, John, klont das Repository, nimmt
  eine Änderung vor und comittet auf seinem Rechner. (Wir kürzen die Beispiele
  etwas ab und ersetzen die hierfür irrelevanten Protokoll Meldungen mit <code>xxx</code>.)
</p>

<pre>
# John's Machine
$ git clone john@githost:simplegit.git
Initialized empty Git repository in /home/john/simplegit/.git/
...
$ cd simplegit/
$ vim lib/simplegit.rb
$ git commit -am 'removed invalid default value'
[master 738ee87] removed invalid default value
 1 files changed, 1 insertions(+), 1 deletions(-)
</pre>

<p>
  Der zweite Entwickler, Jessica, tut das gleiche. Sie klont das Repository und
  committet eine Änderung:
</p>

<pre>
# Jessica's Machine
$ git clone jessica@githost:simplegit.git
Initialized empty Git repository in /home/jessica/simplegit/.git/
...
$ cd simplegit/
$ vim TODO
$ git commit -am 'add reset task'
[master fbff5bc] add reset task
 1 files changed, 1 insertions(+), 0 deletions(-)
</pre>

<p>Jetzt lädt Jessica ihre Arbeit mit <code>git push</code> auf den Server:</p>

<pre>
# Jessica's Machine
$ git push origin master
...
To jessica@githost:simplegit.git
   1edee6b..fbff5bc  master -&gt; master
</pre>

<p>John versucht, das selbe zu tun:</p>

<pre>
# John's Machine
$ git push origin master
To john@githost:simplegit.git
 ! [rejected]        master -&gt; master (non-fast forward)
error: failed to push some refs to 'john@githost:simplegit.git'
</pre>

<p>
  John darf seine Änderung nicht pushen, weil Jessica in der Zwischenzeit
  gepushed hat. Dies ist ein Unterschied zu Subversion: wie Du siehst, haben
  die beiden Entwickler nicht dieselbe Datei bearbeitet. Während Subversion
  automatisch merged, wenn lediglich verschiedene Dateien bearbeitet wurden,
  muss man Commits in Git lokal mergen. John muss also Jessicas Änderungen
  herunterladen und mergen, bevor er dann selbst pushen darf:
</p>

<pre>
$ git fetch origin
...
From john@githost:simplegit
 + 049d078...fbff5bc master     -&gt; origin/master
</pre>

<p>
  Zu diesem Zeitpunkt sieht Johns lokales Repository jetzt aus wie in Bild 5-4.
</p>

<p>Insert 18333fig0504.png Bild 5-4. Johns ursprüngliches Repository</p>

<p>
  John hat eine Referenz auf Jessicas Änderungen, aber er muss sie mit seinen
  eigenen Änderungen mergen, bevor er auf den Server pushen darf:
</p>

<pre>
$ git merge origin/master
Merge made by recursive.
 TODO |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)
</pre>

<p>
  Der Merge verläuft glatt: Johns Commit Historie sieht jetzt aus wie in Bild
  5-5.
</p>

<p>
  Insert 18333fig0505.png Johns Repository nach dem Merge mit origin/master
</p>

<p>
  John sollte seinen Code jetzt testen, um sicher zu stellen, dass alles
  weiterhin funktioniert. Dann kann er seine Arbeit auf den Server pushen:
</p>

<pre>
$ git push origin master
...
To john@githost:simplegit.git
   fbff5bc..72bbc59  master -&gt; master
</pre>

<p>Johns Commit Historie sieht schließlich aus wie in Bild 5-6.</p>

<p>
  Insert 18333fig0506.png Johns Commit Historie nach dem pushen auf den origin
  Server
</p>

<p>
  In der Zwischenzeit hat Jessica auf einem Topic Branch (xxx) gearbeitet. Sie
  hat einen Topic Branch mit dem Namen <code>issue54</code> und darin drei Commits angelegt. Sie hat Johns Änderungen bisher noch nicht
  herunter geladen, sodass ihre Commit Historie jetzt so aussieht wie in Bild
  5-7.
</p>

<p>Insert 18333fig0507.png Bild 5-7. Jessicas ursprüngliche Commit Historie</p>

<p>
  Jessica will ihre Arbeit jetzt mit John synchronisieren. Also lädt sie seine
  Änderungen herunter:
</p>

<pre>
# Jessica's Machine
$ git fetch origin
...
From jessica@githost:simplegit
   fbff5bc..72bbc59  master     -&gt; origin/master
</pre>

<p>
  Das lädt die Änderungen, die John in der Zwischenzeit hochgeladen hat.
  Jessicas Historie entspricht jetzt Bild 5-8.
</p>

<p>
  Insert 18333fig0508.png Bild 5-8. Jessicas Historie nachdem sie Johns
  Änderungen geladen hat
</p>

<p>
  Jessica hat die Arbeit in ihrem Topic Branch abgeschlossen, aber sie will
  wissen, welche neuen Änderungen es gibt, mit denen sie ihre eigenen mergen
  muss.
</p>

<pre>
$ git log --no-merges origin/master ^issue54
commit 738ee872852dfaa9d6634e0dea7a324040193016
Author: John Smith &lt;jsmith@example.com&gt;
Date:   Fri May 29 16:01:27 2009 -0700

    removed invalid default value
</pre>

<p>
  Jetzt kann Jessica zunächst ihren Topic Branch <code>issue54</code> in ihren <code>master</code> Branch mergen, dann Johns Änderungen aus <code>origin/master</code> in ihren <code>master</code> Branch mergen und schließlich das Resultat auf den <code>origin</code> Server pushen. Als erstes wechselt sie zurück auf ihren <code>master</code> Branch:
</p>

<pre>
$ git checkout master
Switched to branch "master"
Your branch is behind 'origin/master' by 2 commits, and can be fast-forwarded.
</pre>

<p>
  Sie kann jetzt entweder <code>origin/master</code> oder <code>issue54</code> zuerst mergen – sie sind beide „upstream“ (xxx). Der resultierende Snapshot
  wäre identisch, egal in welcher Reihenfolge sie beide Branches in ihren <code>master</code> Branch merged, lediglich die Historie würde natürlich minimal anders
  aussehen. Jessica entscheidet sich, <code>issue54</code> zuerst zu mergen:
</p>

<pre>
$ git merge issue54
Updating fbff5bc..4af4298
Fast forward
 README           |    1 +
 lib/simplegit.rb |    6 +++++-
 2 files changed, 6 insertions(+), 1 deletions(-)
</pre>

<p>
  Das ging glatt, wie Du siehst, war es ein einfacher „fast-forward“ Merge. Als
  nächstes merged Jessica Johns Änderungen aus <code>origin/master</code>:
</p>

<pre>
$ git merge origin/master
Auto-merging lib/simplegit.rb
Merge made by recursive.
 lib/simplegit.rb |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
</pre>

<p>
  Auch hier treten keine Konflikte auf. Jessicas Historie sieht jetzt wie folgt
  aus (Bild 5-9).
</p>

<p>
  Insert 18333fig0509.png Bild 5-9. Jessicas Historie nach dem Merge mit Johns
  Änderungen
</p>

<p>
  <code>origin/master</code> ist jetzt in Jessicas <code>master</code> Branch enthalten (xxx reachable xxx), sodass sie in der Lage sein sollte,
  auf den <code>origin</code> Server zu pushen (vorausgesetzt, John hat zwischenzeitlich nicht gepusht):
</p>

<pre>
$ git push origin master
...
To jessica@githost:simplegit.git
   72bbc59..8059c15  master -&gt; master
</pre>

<p>
  Beide Entwickler haben jetzt einige Male committed und die Arbeit des jeweils
  anderen erfolgreich mit ihrer eigenen zusammengeführt.
</p>

<p>
  Insert 18333fig0510.png Bild 5-10. Jessicas Historie nachdem sie sämtliche
  Änderungen auf den Server gepusht hat
</p>

<p>
  Dies ist eine der simpelsten Workflow Varianten. Du arbeitest eine Weile,
  normalerweise in einem Topic Branch, und mergst in Deinen <code>master</code> Branch, wenn Du fertig bist. Wenn Du Deine Änderungen anderen zur Verfügung
  stellen willst, holst Du den aktuellen <code>origin/master</code> Branch, mergst Deinen <code>master</code> Branch damit und pushst das ganze zurück auf den <code>origin</code> Server. Der Ablauf sieht in etwa wie folgt aus (Bild 5-11).
</p>

<p>
  Insert 18333fig0511.png Bild 5-11. Ablauf eines einfachen Workflows für
  mehrere Entwickler
</p>

<h3>Teil-Teams mit Integration Manager</h3>

<p>
  Im folgenden Szenario sehen wir uns die Rollen von Mitarbeitern in einem
  größeren, nicht öffentlich arbeitenden Team an. Du wirst sehen, wie man in
  einer Umgebung arbeiten kann, in der kleine Gruppen (z.B. an einzelnen
  Features) zusammenarbeiten und ihre Ergebnisse dann von einer weiteren Gruppe
  in die Hauptentwicklungslinie integriert werden.
</p>

<p>
  Sagen wir John und Jessica arbeiten gemeinsam an einem Feature, während
  Jessica und Josie an einem anderen arbeiten. Das Unternehmen verwendet einen
  Integration-Manager Workflow, in dem die Arbeit der verschiedenen Gruppen von
  anderen Mitarbeitern zentral integriert werden – und der <code>master</code> Branch nur von diesen letzteren geschrieben werden kann. In diesem Szenario
  wird sämtliche Arbeit von den Teams in Branches erledigt und dann von den
  Integration-Manangern zusammengeführt.
</p>

<p>
  Schauen wir uns Jessicas Workflow an, während sie mit jeweils verschiedenen
  Entwicklern parallel an zwei Features arbeitet. Nehmen wir an, sie hat das
  Repository bereits geklont und will zuerst an <code>featureA</code> arbeiten. Sie legt einen neuen Branch für das Feature an und fängt an, daran
  zu arbeiten:
</p>

<pre>
# Jessica's Machine
$ git checkout -b featureA
Switched to a new branch "featureA"
$ vim lib/simplegit.rb
$ git commit -am 'add limit to log function'
[featureA 3300904] add limit to log function
 1 files changed, 1 insertions(+), 1 deletions(-)
</pre>

<p>
  Jetzt will sie ihre Arbeit John zur Verfügung stellen, der am gleichen
  Feature arbeiten will, und pusht dazu ihre Commits in ihrem <code>featureA</code> Branch auf den Server. Jessica hat keinen Schreibzugriff auf den <code>master</code> Branch – den haben nur die Integration Manager – also pusht sie ihren
  Feature Branch, der nur der Zusammenarbeit mit John dient:
</p>

<pre>
$ git push origin featureA
...
To jessica@githost:simplegit.git
 * [new branch]      featureA -&gt; featureA
</pre>

<p>
  Jessica schickt John eine E-Mail und lässt ihn wissen, dass sie ihre Arbeit
  in einen Branch <code>featureA</code> hochgeladen hat. Während sie jetzt auf Feedback von John wartet, kann
  Jessica anfangen, an <code>featureB</code> zuarbeiten – diesmal gemeinsam mit Josie. Also legt sie einen neuen Feature
  Branch an, der auf dem gegenwärtigen <code>master</code> Branch des <code>origin</code> Servers basiert:
</p>

<pre>
# Jessica's Machine
$ git fetch origin
$ git checkout -b featureB origin/master
Switched to a new branch "featureB"
</pre>

<p>
  Jetzt legt Jessica eine Reihe von Commits im <code>featureB</code> Branch an:
</p>

<pre>
$ vim lib/simplegit.rb
$ git commit -am 'made the ls-tree function recursive'
[featureB e5b0fdc] made the ls-tree function recursive
 1 files changed, 1 insertions(+), 1 deletions(-)
$ vim lib/simplegit.rb
$ git commit -am 'add ls-files'
[featureB 8512791] add ls-files
 1 files changed, 5 insertions(+), 0 deletions(-)
</pre>

<p>Jessicas Repository entspricht jetzt Bild 5-12.</p>

<p>
  Insert 18333fig0512.png Bild 5-12. Jessicas ursprüngliche Commit Historie
</p>

<p>
  Jessica könnte ihre Arbeit jetzt hochladen, aber sie hat eine E-Mail von
  Josie erhalten, dass sie bereits einen Feature Branch <code>featureBee</code> für dasselbe Feature auf dem Server angelegt hat. Jessica muss also erst
  ihre eigenen Änderungen mit diesem Branch mergen und dann dorthin pushen. Sie
  lädt also Josies Änderungen mit <code>git fetch</code> herunter:
</p>

<pre>
$ git fetch origin
...
From jessica@githost:simplegit
 * [new branch]      featureBee -&gt; origin/featureBee
</pre>

<p>
  Jessica kann ihre eigene Arbeit jetzt mit diesen Änderungen zusammenführen:
</p>

<pre>
$ git merge origin/featureBee
Auto-merging lib/simplegit.rb
Merge made by recursive.
 lib/simplegit.rb |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)
</pre>

<p>
  Es gibt jetzt ein kleines Problem. Jessica muss die zusammengeführten
  Änderungen in ihrem <code>featureB</code> Branch in den <code>featureBee</code> Branch auf dem Server pushen. Das kann sie tun, indem sie sowohl den Namen
  ihres lokalen Branches als auch des externen Branches angibt, und zwar mit
  einem Doppelpunkt getrennt:
</p>

<pre>
$ git push origin featureB:featureBee
...
To jessica@githost:simplegit.git
   fba9af8..cd685d1  featureB -&gt; featureBee
</pre>

<p>
  Das nennt man eine <i>Refspec</i>. In Kapitel 9 gehen wir detailliert auf Git Refspecs ein und darauf, was man
  noch mit ihnen machen kann.
</p>

<p>
  Als nächstes schickt John Jessica eine E-Mail. Er schreibt, dass er einige
  Änderungen in den <code>featureA</code> Branch gepusht hat, und bittet sie, diese zu prüfen. Sie führt also <code>git fetch</code> aus, um die Änderungen herunter zu laden:
</p>

<pre>
$ git fetch origin
...
From jessica@githost:simplegit
   3300904..aad881d  featureA   -&gt; origin/featureA
</pre>

<p>Danach kann sie die neuen Änderungen mit <code>git log</code> auflisten:</p>

<pre>
$ git log origin/featureA ^featureA
commit aad881d154acdaeb2b6b18ea0e827ed8a6d671e6
Author: John Smith &lt;jsmith@example.com&gt;
Date:   Fri May 29 19:57:33 2009 -0700

    changed log output to 30 from 25
</pre>

<p>
  Schließlich aktualisiert sie ihren eigenen <code>featureA</code> Branch mit Johns Änderungen:
</p>

<pre>
$ git checkout featureA
Switched to branch "featureA"
$ git merge origin/featureA
Updating 3300904..aad881d
Fast forward
 lib/simplegit.rb |   10 +++++++++-
1 files changed, 9 insertions(+), 1 deletions(-)
</pre>

<p>
  Jessica will eine kleine Änderung vornehmen. Also comittet sie und pusht den
  neuen Commit auf den Server:
</p>

<pre>
$ git commit -am 'small tweak'
[featureA 774b3ed] small tweak
 1 files changed, 1 insertions(+), 1 deletions(-)
$ git push origin featureA
...
To jessica@githost:simplegit.git
   3300904..774b3ed  featureA -&gt; featureA
</pre>

<p>Jessicas Commit Historie sieht jetzt wie folgt aus (Bild 5-13).</p>

<p>
  Insert 18333fig0513.png Bild 5-13. Jessicas Historie mit dem neuen Commit im
  Feature Branch
</p>

<p>
  Jessica, Josie und John informieren jetzt ihre Integration Manager, dass die
  Ändeurngen in den Branches <code>featureA</code> und <code>featureBee</code> fertig sind und in die Hauptlinie in <code>master</code> übernommen werden können. Nachdem das geschehen ist, wird <code>git fetch</code> die neuen Merge Commits herunter laden und die Commit Historie in etwa wie
  folgt aussehen (Bild 5-14):
</p>

<p>
  Insert 18333fig0514.png Bild 5-14. Jessicas Historie nachdem beide Feature
  Branches gemerged wurden
</p>

<p>
  Viele Teams wechseln zu Git, weil es auf einfache Weise ermöglicht,
  verschiedene Teams parallel an verschiedenen Entwicklungslinien zu arbeiten,
  die erst später im Prozess integriert werden. Ein riesiger Vorteil von Git
  besteht darin, dass man in kleinen Teilgruppen über externe Branches
  zusammenarbeiten kann, ohne dass dazu notwendig wäre, das gesamte Team zu
  involvieren und möglicherweise aufzuhalten. Der Ablauf dieser Art von
  Workflow kann wie folgt dargestellt werden (Bild 5-15).
</p>

<p>
  Insert 18333fig0515.png Bild 5-15. Workflow mit Teil-Teams und Integration
  Manager
</p>

<h3>Kleine, öffentliche Projekte</h3>

<p>
  An öffentlichen Projekten mitzuarbeiten funktioniert ein bisschen anders.
  Weil man normalerweise keinen Schreibzugriff auf das öffentliche Repository
  des Projektes hat, muss man mit den Betreibern in anderer Form
  zusammenarbeiten. Unser erstes Beispiel beschreibt, wie man zu Projekten auf
  Git Hosts beitragen kann, die es erlauben Forks eines Projektes anzulegen.
  Z.B. unterstützen die Git Hosting Seiten repo.or.cz und GitHub dieses Feature
  – und viele Projekt Betreiber akzeptieren Änderungen in dieser Form. Das
  nächste Beispiel geht dann darauf ein, wie man mit Projekten arbeiten kann,
  die es bevorzugen, Patches per E-Mail zu erhalten (xxx oder Ticket Tracker,
  wie z.B. Rails xxx).
</p>

<p>
  Zunächst wirst vermutlich das Hauptrepository klonen, einen Topic Branch für
  Deinen Patch anlegen und dann darin arbeiten. Der Prozess sieht dann in etwa
  so aus:
</p>

<pre>
$ git clone (url)
$ cd project
$ git checkout -b featureA
$ (work)
$ git commit
$ (work)
$ git commit
</pre>

<p>
  Es ist wahrscheinlich sinnvoll, <code>git rebase -i</code> zu verwenden, um die verschiedenen Commits zu einem einzigen zusammen zu
  packen („squash“, quetschen) oder um sie in anderer Weise neu zu arrangieren,
  sodass es für die Projekt Betreiber leichter ist, die Änderungen nach zu
  vollziehen. In Kapitel 6 gehen wir ausführlicher auf das interaktive <code>rebase -i</code> ein.
</p>

<p>
  Wenn Dein Branch fertig ist und Du Deine Arbeit den Projekt Betreibern zur
  Verfügung stellen willst, gehst Du auf die Projekt Seite und klickst auf den
  „Fork“ Button. Dadurch legst Du Deinen eigenen Fork des Projektes an, in den
  Du dann schreiben kannst. Die Repository URL dieses Forks musst Du dann als
  ein zweites, externes Repository („remote“) einrichten. In unserem Beispiel
  verwenden wir den Namen <code>myfork</code>:
</p>

<pre>
$ git remote add myfork (url)
</pre>

<p>
  Jetzt kannst Du Deine Änderungen dorthin hochladen. Am besten tust Du das,
  indem Du Deinen Topic Branch hochlädst (statt ihn in Deinen <code>master</code> Branch zu mergen und den dann hochzuladen). Dies deshalb, weil Du, wenn
  Deine Änderungen nicht akzeptiert werden, Deinen eigenen <code>master</code> Branch nicht zurücksetzen musst. Wenn die Projekt Betreiber Deine Änderungen
  mergen, rebasen oder cherry-picken, landen sie schließlich ohnehin in Deinem <code>master</code> Branch.
</p>

<pre>
$ git push myfork featureA
</pre>

<p>
  Nachdem Du Deine Arbeit in Deinen Fork hochgeladen hast, musst Du die Projekt
  Betreiber benachrichtigen. Dies wird oft als „pull request“ bezeichnet. Du
  kannst ihn entweder direkt über die Webseite schicken (GitHub hat dazu einen
  „pull request“ Button) oder den Git Befehl <code>git request-pull</code> verwenden und manuell eine E-Mail an die Projekt Betreiber schicken.
</p>

<p>
  Der <code>request-pull</code> Befehl vergleicht denjenigen Branch, für den Deine Änderungen gedacht sind,
  mit Deinem Topic Branch und gibt eine Übersicht der Änderungen aus. Wenn
  Jessica zwei Änderungen in einem Topic Branch hat und nun John einen pull
  request schicken will, kann sie folgendes tun:
</p>

<pre>
$ git request-pull origin/master myfork
The following changes since commit 1edee6b1d61823a2de3b09c160d7080b8d1b3a40:
  John Smith (1):
        added a new function

are available in the git repository at:

  git://githost/simplegit.git featureA

Jessica Smith (2):
      add limit to log function
      change log output to 30 from 25

 lib/simplegit.rb |   10 +++++++++-
 1 files changed, 9 insertions(+), 1 deletions(-)
</pre>

<p>
  Die Ausgabe kann an den Projekt Betreiber geschickt werden – sie sagt klar,
  auf welchem Branch die Arbeit basiert, gibt eine Zusammenfassung der
  Änderungen und gibt an, aus welchem Fork oder Repository man die Änderungen
  herunterladen kann.
</p>

<p>
  Wenn Du nicht selbst Betreiber eines bestimmten Projektes bist, ist es im
  Allgemeinen einfacher, einen Branch <code>master</code> immer dem <code>origin/master</code> Branch tracken (xxx folgen) zu lassen und eigene Änderungen in Topic
  Branches vorzunehmen, die man leicht wieder löschen kann, wenn sie nicht
  akzeptiert werden. Wenn Du Aspekte Deiner Arbeit in Topic Branches isolierst,
  kannst Du sie außerdem recht leicht auf den letzten Stand des
  Hauptrepositories rebasen, falls das Hauptrepository in der Zwischenzeit
  weiter entwickelt wurde und Deine Commits nicht mehr sauber passen. Wenn Du
  beispielsweise an einem anderen Patch für das Projekt arbeiten willst,
  verwende dazu nicht weiter den gleichen Topic Branch, den Du gerade in Deinen
  Fork hochgeladen hast. Lege statt dessen einen neuen Topic Branch an, der
  wiederum auf dem <code>master</code> Branch des Hauptrepositories basiert.
</p>

<pre>
$ git checkout -b featureB origin/master
$ (work)
$ git commit
$ git push myfork featureB
$ (email maintainer)
$ git fetch origin
</pre>

<p>
  Deine Arbeit an den verschiedenen Patches sind jetzt in Deine Topic Branches
  isoliert – ähnlich wie in einer Patch Queue – sodass Du die einzelnen Topic
  Branches neu schreiben, rebasen und ändern kannst, ohne dass sie mit einander
  in Konflikt geraten (siehe Bild 5-16).
</p>

<p>
  Insert 18333fig0516.png Bild 5-16. Ursprüngliche Commit Historie mit dem <code>featureB</code> Branch
</p>

<p>
  Sagen wir, der Projekt Betreiber hat eine Reihe von Änderungen Dritter in das
  Projekt übernommen und Deine eigenen Änderungen lassen sich jetzt nicht mehr
  sauber mergen. In diesem Fall kannst Du Deine Änderungen auf dem neuen Stand
  des <code>origin/master</code> Branches rebasen, Konflikte beheben und Deine Arbeit erneut einreichen:
</p>

<pre>
$ git checkout featureA
$ git rebase origin/master
$ git push -f myfork featureA
</pre>

<p>
  Das schreibt Deine Commit Historie neu, sodass sie jetzt so aussieht (Bild
  5-17):
</p>

<p>
  Insert 18333fig0517.png Bild 5-17. Commit Historie nach dem rebase von <code>featureA</code>
</p>

<p>
  Weil Du den Branch rebased hast, musst Du die Option <code>-f</code> verwenden, um den <code>featureA</code> Branch auf dem Server zu ersetzen, denn Du hast die Commit Historie
  umgeschrieben und nun ist ein Commit enthalten, von dem der gegenwärtig
  letzte Commit des externen Branches nicht abstammt. Eine Alternative dazu
  wäre, den Branch jetzt in einen neuen externen Branch zu pushen, z.B. <code>featureAv2</code>.
</p>

<p>
  Schauen wir uns noch ein anderes Szenario an: der Projekt Betreiber hat sich
  Deine Arbeit angesehen und will die Änderungen übernehmen, aber er bittet
  dich, noch eine Kleinigkeit an der Implementierung zu ändern. Du willst die
  Gelegenheit außerdem nutzen, um Deine Änderungen neu auf den gegenwärtigen <code>master</code> Branch des Projektes zu basieren. Du legst dazu einen neuen Branch von <code>origin/master</code> an, übernimmst Deine Änderungen dahin, löst ggf. Konflikte auf, nimmst die
  angeforderte Änderung an der Implementierung vor und lädst die Änderungen auf
  den Server:
</p>

<pre>
$ git checkout -b featureBv2 origin/master
$ git merge --no-commit --squash featureB
$ (change implementation)
$ git commit
$ git push myfork featureBv2
</pre>

<p>
  Die <code>--squash</code> Option bewirkt, dass alle Änderungen des Merge Branches (<code>featureB</code>) übernommen werden, ohne aber dass zusätzlich ein Merge Commit angelegt
  wird. Die <code>--no-commit</code> Option instruiert Git außerdem, nicht automatisch einen Commit anzulegen.
  Das erlaubt dir, sämtliche Änderungen aus dem anderen Branch zu übernehmen
  und dann weitere Änderungen vorzunehmen, bevor Du das Ganze dann in einem
  neuen Commit speicherst.
</p>

<p>
  Jetzt kannst Du dem Projekt Betreiber eine Nachricht schicken, dass Du die
  angeforderte Änderung vorgenommen hast und dass er Deine Arbeit in Deinem <code>featureBv2</code> Branch finden kann (siehe Bild 5-18).
</p>

<p>
  Insert 18333fig0518.png Bild 5-18. Commit Historie mit dem neuen <code>featureBv2</code> Branch
</p>

<h3>Große öffentliche Projekte</h3>

<p>
  Viele große Projekte haben einen etablierten Prozess, nach dem sie vorgehen,
  wenn es darum geht, Patches zu akzeptieren. Du musst Dich mit den jeweiligen
  Regeln vertraut machen, die in jedem Projekt ein bisschen anders sind.
  Allerdings akzeptieren viele große Projekte Patches per E-Mail über eine
  Entwickler Mailingliste (xxx oder einen Bugtracker, wie Rails xxx). Deshalb
  gehen wir auf dieses Beispiel als nächstes ein.
</p>

<p>
  Der Workflow ist ähnlich wie im vorherigen Szenario. Du legst für jeden Patch
  oder jede Patch Serie einen Topic Branch an, in dem Du arbeitest. Der
  Unterschied besteht dann darin, auf welchem Wege Du die Änderungen an das
  Projekt schickst. Statt das Projekt zu forken und Änderungen in Deinen Fork
  hochzuladen, erzeugst Du eine E-Mail Version Deiner Commits und schickst sie
  als Patch an die Entwickler Mailingliste.
</p>

<pre>
$ git checkout -b topicA
$ (work)
$ git commit
$ (work)
$ git commit
</pre>

<p>
  Jetzt hast Du zwei Commits, die Du an die Mailingliste schicken willst. Du
  kannst den Befehl <code>git format-patch</code> verwenden, um aus diesen Commits Dateien zu erzeugen, die im mbox-Format
  formatiert sind und die Du per E-Mail verschicken kannst. Dieser Befehl macht
  aus jedem Commit eine E-Mail Datei. Die erste Zeile der Commit Meldung wird
  zum Betreff der E-Mail und der Rest der Commit Meldung sowie der Patch des
  Commits selbst wird zum Text der E-Mail. Das schöne daran ist, dass wenn man
  einen auf diese Weise erzeugten Patch benutzt, dann bleiben alle Commit
  Informationen erhalten. Du kannst das in den nächsten Beispielen sehen:
</p>

<pre>
$ git format-patch -M origin/master
0001-add-limit-to-log-function.patch
0002-changed-log-output-to-30-from-25.patch
</pre>

<p>
  Der Befehl <code>git format-patch</code> zeigt Dir die Namen der Patch Dateien an, die er erzeugt hat. (Die <code>-M</code> option weist Git an, nach umbenannten Dateien Ausschau zu halten.) Die
  Dateien sehen dann so aus:
</p>

<pre>
$ cat 0001-add-limit-to-log-function.patch
From 330090432754092d704da8e76ca5c05c198e71a8 Mon Sep 17 00:00:00 2001
From: Jessica Smith &lt;jessica@example.com&gt;
Date: Sun, 6 Apr 2008 10:17:23 -0700
Subject: [PATCH 1/2] add limit to log function

Limit log functionality to the first 20

---
 lib/simplegit.rb |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/lib/simplegit.rb b/lib/simplegit.rb
index 76f47bc..f9815f1 100644
--- a/lib/simplegit.rb
+++ b/lib/simplegit.rb
@@ -14,7 +14,7 @@ class SimpleGit
   end

   def log(treeish = 'master')
-    command("git log #{treeish}")
+    command("git log -n 20 #{treeish}")
   end

   def ls_tree(treeish = 'master')
--
1.6.2.rc1.20.g8c5b.dirty
</pre>

<p>
  Du kannst diese Patch Dateien anschließend bearbeiten, z.B. um weitere
  Informationen für die Mailingliste hinzuzufügen, die Du nicht in der Commit
  Meldung haben willst. Wenn Du zusätzlichen Text zwischen der <code>---</code> Zeile und dem Anfang des Patches (der Zeile <code>lib/simplegit.rb</code> in diesem Fall), dann ist er für den Leser sichtbar, aber Git wird ihn
  ignorieren, wenn man den Patch verwendet.
</p>

<p>
  Um das jetzt an die Mailingliste zu schicken, kannst Du entweder die Datei
  per copy-and-paste in Dein E-Mail Programm kopieren, als Anhang an eine
  E-Mail anhängen oder Du kannst die Dateien mit einem Befehlszeilen Programm
  direkt verschicken. Patches zu kopieren verursacht oft Formatierungsprobleme
  – insbesondere mit „smarten“ E-Mail Clients, die die Dinge umformatieren, die
  man einfügt. Zum Glück bringt Git aber ein Tool mit, mit dem man Patches in
  ihrer korrekten Formatierung über IMAP verschicken kann. In folgendem
  Beispiel zeige ich, wie man Patches über Gmail verschicken kann, welches der
  E-Mail Client ist, den ich selbst verwende. Darüberhinaus findest Du
  ausführliche Beschreibungen für zahlreiche E-Mail Programme am Ende der schon
  erwähnten Datei <code>Documentation/SubmittingPatches</code> im Git Quellcode.
</p>

<p>
  Zunächst musst Du die IMAP Sektion in Deiner <code>~/.gitconfig</code> Datei ausfüllen. Du kannst jeden Wert separat mit dem Befehl <code>git config</code> eingeben oder Du kannst die Datei öffnen und sie manuell eingeben. Im
  Endeffekt sollte Deine <code>~/.gitconfig</code> Datei in etwa so aussehen:
</p>

<pre>
[imap]
  folder = "[Gmail]/Drafts"
  host = imaps://imap.gmail.com
  user = user@gmail.com
  pass = p4ssw0rd
  port = 993
  sslverify = false
</pre>

<p>
  Wenn Dein IMAP Server kein SSL verwendet, kannst Du die letzten beiden Zeilen
  wahrscheinlich weglassen und der <code>host</code> dürfte mit <code>imap://</code> und nicht <code>imaps://</code> beginnen. Wenn Du diese Einstellungen konfiguriert hast, kannst Du <code>git imap-send</code> verwenden, um Deine Patches in den Entwurfsordner des angegebenen IMAP
  Servers zu kopieren:
</p>

<pre>
$ cat *.patch |git imap-send
Resolving imap.gmail.com... ok
Connecting to [74.125.142.109]:993... ok
Logging in...
sending 2 messages
100% (2/2) done
</pre>

<p>
  Jetzt kannst Du in Deinen Entwürfe-Ordner wechseln, die Mailingliste an die
  Du den Patch senden möchtest im An-Feld setzen, vielleicht noch den
  Maintainer oder die verantwortliche Person in das CC-Feld einfügen und dann
  das Ganze losschicken.
</p>

<p>
  Man kann Patches auch über einen SMTP-Server schicken. Wie im letzen
  Beispiel, kann man auch hier jeden einzelnen Wert mit einer Reihe von <code>git config</code> Kommandos setzen. Oder aber Du änderst die Sektion sendemail in Deiner <code>~/.gitconfig</code> Datei manuell:
</p>

<pre>
[sendemail]
  smtpencryption = tls
  smtpserver = smtp.gmail.com
  smtpuser = user@gmail.com
  smtpserverport = 587
</pre>

<p>
  Nach der Änderungen kannst Du mit <code>git send-email</code> die Patches abschicken:
</p>

<pre>
$ git send-email *.patch
0001-added-limit-to-log-function.patch
0002-changed-log-output-to-30-from-25.patch
Who should the emails appear to be from? [Jessica Smith &lt;jessica@example.com&gt;]
Emails will be sent from: Jessica Smith &lt;jessica@example.com&gt;
Who should the emails be sent to? jessica@example.com
Message-ID to be used as In-Reply-To for the first email? y
</pre>

<p>
  Git gibt dann für jeden Patch, den Du verschickst, ein paar Log Informationen
  aus, die in etwa so aussehen:
</p>

<pre>
(mbox) Adding cc: Jessica Smith &lt;jessica@example.com&gt; from
  \line 'From: Jessica Smith &lt;jessica@example.com&gt;'
OK. Log says:
Sendmail: /usr/sbin/sendmail -i jessica@example.com
From: Jessica Smith &lt;jessica@example.com&gt;
To: jessica@example.com
Subject: [PATCH 1/2] added limit to log function
Date: Sat, 30 May 2009 13:29:15 -0700
Message-Id: &lt;1243715356-61726-1-git-send-email-jessica@example.com&gt;
X-Mailer: git-send-email 1.6.2.rc1.20.g8c5b.dirty
In-Reply-To: &lt;y&gt;
References: &lt;y&gt;

Result: OK
</pre>

<p>
  Jetzt solltest Du in den Entwurfsordner Deines E-Mail Clients gehen, als
  Empfänger Adresse die jeweilige Mailingliste angeben, möglicherweise ein CC
  an den Projektbetreiber oder einen anderen Verantwortlichen setzen und die
  E-Mail dann verschicken können.
</p>

<h3>Zusammenfassung</h3>

<p>
  Wir haben jetzt eine Reihe von Workflows besprochen, die für jeweils sehr
  verschiedene Arten von Projekten üblich sind und denen Du vermutlich begegnen
  wirst. Wir haben außerdem einige neue Tools besprochen, die dabei hilfreich
  sind, diese Workflows umzusetzen. Als nächstes werden wir auf die andere
  Seite dieser Medaille eingehen: wie Du selbst ein Git Projekt betreiben
  kannst. Du wirst lernen, wie Du als „wohlwollender Diktator“ oder als
  Integration Manager arbeiten kannst.
</p>

<h2>Ein Projekt betreiben</h2>

<p>
  Neben dem Wissen, das Du brauchst, um zu einem bestehenden Projekt Änderungen
  beizutragen, wirst Du vermutlich wissen wollen, wie Du selbst ein Projekt
  betreiben kannst. Dazu willst Du Patches akzeptieren und anwenden, die per <code>git format-patch</code> erzeugt und Dir per E-Mail geschickt wurden. Oder Du willst Änderungen aus
  externen Branches übernehmen, die Du zu Deinem Projekt hinzugefügt hast. Ob
  Du nun für das Hauptrepository verantwortlich bist oder ob Du dabei helfen
  willst, Patches zu verifizieren und zu bestätigen – in beiden Fällen musst Du
  wissen, wie Du Änderungen in einer Weise übernehmen kannst, die für andere
  Mitarbeiter nachvollziehbar und für Dich selbst tragbar ist.
</p>

<h3>In Topic Branches arbeiten</h3>

<p>
  Wenn Du Änderungen von anderen übernehmen willst, ist normalerweise eine gute
  Idee, sie in einem Topic Branch auszuprobieren – d.h., einem temporären
  Branch, dessen Zweck nur darin besteht, die jeweiligen Änderungen
  auszuprobieren. Auf diese Weise ist es einfach, Patches ggf. anzupassen oder
  sie im Zweifelsfall im Topic Branch liegen zu lassen, wenn sie nicht
  funktionieren und Du im Moment nicht die Zeit hast, Dich weiter damit zu
  befassen. Es ist empfehlenswert, Topic Branches Namen zu geben, die gut
  kommunizieren, worum es sich bei den jeweiligen Änderungen dreht, wie z.B. <code>ruby_client</code> oder etwas ähnlich aussagekräftiges, das Dir hilft, Dich daran zu erinnern.
  Der Projekt Betreiber des Git Projektes selbst vergibt Namensräume für solche
  Branches – wie z.B. <code>sc/ruby_client</code>, wobei <code>sc</code> ein Kürzel für den jeweiligen Autor des Patches ist. Wie Du inzwischen
  weißt, kannst Du einen neuen Branch, der auf dem gegenwärtigen <code>master</code> Branch basiert, wie folgt erzeugen (xxx falsch, das stimmt nur, wenn <code>master</code> der aktuelle Branch ist xxx):
</p>

<pre>
$ git branch sc/ruby_client master
</pre>

<p>
  Oder, wenn außerdem direkt zu dem neuen Branch wechseln willst, kannst Du die
  <code>-b</code> für den <code>git checkout</code> Befehl verwenden:
</p>

<pre>
$ git checkout -b sc/ruby_client master
</pre>

<p>
  Nachdem Du jetzt einen Topic Branch angelegt hast, kannst Du die Änderungen
  zu diesem Branch hinzufügen, um herauszufinden, ob Du sie dauerhaft in einen
  offiziellen Branch übernehmen willst.
</p>

<h3>Patches aus E-Mails verwenden</h3>

<p>
  Wenn Du einen Patch, den Du auf Dein Projekt anwenden willst, per E-Mail
  erhältst, gibt es zwei Möglichkeiten, das zu tun: <code>git apply</code> und <code>git am</code>.
</p>

<p>Einen Patch verwenden: git apply</p>

<p>
  Wenn der Patch mit <code>git diff</code> oder dem Unix Befehl <code>diff</code> erzeugt wurde, dann kannst Du ihn mit dem Befehl <code>git apply</code> anwenden. Nehmen wir an, Du hast den Patch nach <code>/tmp/patch-ruby-client.patch</code> gespeichert. Dann kannst Du ihn wie folgt verwenden:
</p>

<pre>
$ git apply /tmp/patch-ruby-client.patch
</pre>

<p>
  Das ändert die Dateien in Deinem Git Arbeitsverzeichnis. Das ist fast das
  selbe wie wenn Du den Unix Befehl <code>patch -p1</code> verwendest. Der Git Befehl ist aber paranoider und akzeptiert nicht so viele
  unklare Übereinstimmungen. Außerdem kann er mit neu hinzugefügten, gelöschten
  und umbenannten Dateien umgehen, was der Unix Befehl <code>patch</code> nicht kann. Schließlich ist <code>git apply</code> ein „alles oder nichts“ Befehl, der entweder alle Änderungen übernimmt oder
  gar keine (wenn bei einem etwas schief geht), während <code>patch</code> Änderungen auch teilweise übernimmt, sodass er Dein Arbeitsverzeichnis
  gegebenenfalls in einem unbrauchbaren Zustand hinterlässt. <code>git apply</code> ist also insgesamt strenger als <code>patch</code>. Es legt im übrigen keinen Commit für Dich an. Nachdem Du <code>git apply</code> ausgeführt hast, musst Du die Änderungen manuell zur Staging Area hinzufügen
  und comitten.
</p>

<p>
  Du kannst <code>git apply --check</code> verwenden, um zu testen, ob der Patch sauber anwendbar wäre:
</p>

<pre>
$ git apply --check 0001-seeing-if-this-helps-the-gem.patch
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply
</pre>

<p>
  Wenn dieser Befehl nichts ausgibt, sollte der Befehl sauber anwendbar sein.
</p>

<p>Einen Patch verwenden: git am</p>

<p>
  Wenn der Autor des Patches selbst mit Git arbeitet, kann er Dir das Leben
  leichter machen, indem er <code>git format-patch</code> verwender, um seinen Patch zu erzeugen: der Patch wird dann die Commit
  Informationen über den Autor sowie die Commit Meldung enthalten. Es ist also
  empfehlenswert, Entwickler darum zu bitten und zu ermutigen, <code>git format-patch</code> statt <code>git diff</code> zu verwenden. Du wirst dann <code>git apply</code> nur sehr selten anwenden müssen (xxx legacy patches ??? xxx)
</p>

<p>
  Um einen Patch zu verwenden, der mit <code>git format-patch</code> erzeugt wurde, benutzt Du den Befehl <code>git am</code>. Technisch gesehen ist <code>git am</code> ein Befehl, der eine mbox Datei lesen kann, d.h. eine einfache
  Nur-Text-Datei, die eine oder mehrere E-Mails enthalten kann. Eine solche
  Datei sieht in etwa wie folgt aus:
</p>

<pre>
From 330090432754092d704da8e76ca5c05c198e71a8 Mon Sep 17 00:00:00 2001
From: Jessica Smith &lt;jessica@example.com&gt;
Date: Sun, 6 Apr 2008 10:17:23 -0700
Subject: [PATCH 1/2] add limit to log function

Limit log functionality to the first 20
</pre>

<p>
  Das ist der Anfang der Ausgabe des <code>git format-patch</code> Befehls, die Du im vorherigen Abschnitt gesehen hast – und außerdem valides
  mbox E-Mail Format. Wenn Du eine solche Datei im mbox Format erhalten hast
  und der Absender <code>git send-email</code> korrekt verwendet hat, kannst Du <code>git am</code> mit der Datei verwenden und alle darin enthaltenen Patches werden auf Dein
  Projekt angewendet. Wenn Du einen E-Mail Client verwendest, der mehrere
  E-Mails im mbox Format in einer Datei speichern oder exportieren kann, kannst
  Du auch eine ganze Reihe von Patches in eine einzige Datei speichern und dann
  <code>git am</code> verwenden, um sie nacheinander anzuwenden.
</p>

<p>
  Wenn jemand einen Patch, der mit <code>git format-patch</code> erzeugt wurde, in einem Ticketsystem oder ähnlichem abgelegt hat, kannst Du
  die Datei lokal speichern und dann ebenfalls <code>git am</code> ausführen, um den Patch anzuwenden:
</p>

<pre>
$ git am 0001-limit-log-function.patch
Applying: add limit to log function
</pre>

<p>
  Der Patch passte sauber auf die Codebase und hat automatisch einen neuen
  Commit angelegt. Die Autor Information wurde aus den <code>From</code> und <code>Date</code> Headern der E-Mail übernommen und die Commit Meldung aus dem Subject und
  Body der E-Mail. Wenn der Patch z.B. aus dem mbox Beispiel von oben stammt,
  sieht der resultierende Commit wie folgt aus:
</p>

<pre>
$ git log --pretty=fuller -1
commit 6c5e70b984a60b3cecd395edd5b48a7575bf58e0
Author:     Jessica Smith &lt;jessica@example.com&gt;
AuthorDate: Sun Apr 6 10:17:23 2008 -0700
Commit:     Scott Chacon &lt;schacon@gmail.com&gt;
CommitDate: Thu Apr 9 09:19:06 2009 -0700

   add limit to log function

   Limit log functionality to the first 20
</pre>

<p>
  Das <code>Commit</code> Feld zeigt den Namen desjenigen, der den Patch angewendet hat und <code>CommitDate</code> das jeweilige Datum und Uhrzeit. Die Felder <code>Author</code> und <code>AuthorDate</code> geben an, wer den Commit wann angelegt hat.
</p>

<p>
  Es ist allerdings möglich, dass der Patch nicht sauber auf den gegenwärtigen
  Code passt. Möglicherweise unterscheidet sich der jeweilige Branch inzwischen
  erheblich von dem Zustand, in dem er sich befand, als die in dem Patch
  enthaltenen Änderungen geschrieben wurden. In dem Fall wird <code>git am</code> fehlschlagen und Dir mitteilen, was zu tun ist:
</p>

<pre>
$ git am 0001-seeing-if-this-helps-the-gem.patch
Applying: seeing if this helps the gem
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply
Patch failed at 0001.
When you have resolved this problem run "git am --resolved".
If you would prefer to skip this patch, instead run "git am --skip".
To restore the original branch and stop patching run "git am --abort".
</pre>

<p>
  Der Befehl fügt Konfliktmarkierungen in allen problematischen Dateien ein, so
  wie bei einem konfligierenden Merge oder Rebase. Und Du kannst den Konflikt
  in der selben Weise beheben: die Datei bearbeiten, die Änderungen zur Staging
  Area hinzufügen und dann <code>git am --resolved</code> ausführen, um mit dem jeweils nächsten Patch (falls vorhanden) fortzufahren.
</p>

<pre>
$ (fix the file)
$ git add ticgit.gemspec
$ git am --resolved
Applying: seeing if this helps the gem
</pre>

<p>
  Wenn Du willst, dass Git versucht, einen Konflikt etwas intelligenter zu
  lösen, kannst Du die <code>-3</code> Option angeben, sodass Git einen 3-Wege-Merge versucht. Dies ist deshalb
  nicht der Standard, weil ein 3-Wege-Merge nicht funktioniert, wenn der
  Commit, auf dem der Patch basiert, nicht Teil Deines Repositories ist. Wenn
  Du den Commit allerdings in Deiner Historie hast, d.h. wenn der Patch auf
  einem öffentlichen Commit basiert, dann ist die <code>-3</code> Option oft die bessere Variante, um einen konfligierenden Patch anzuwenden:
</p>

<pre>
$ git am -3 0001-seeing-if-this-helps-the-gem.patch
Applying: seeing if this helps the gem
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
No changes -- Patch already applied.
</pre>

<p>
  In diesem Fall habe ich versucht, einen Patch anzuwenden, der ich bereits
  zuvor angewendet hatte. Ohne die <code>-3</code> Option würde ich einen Konflikt erhalten.
</p>

<p>
  Wenn Du eine Reihe von Patches aus einer Datei im mbox Format anwendest,
  kannst Du außerdem den <code>git am</code> Befehl in einem interaktiven Modus ausführen. In diesem Modus hält Git bei
  jedem Patch an und fragt Dich jeweils, ob Du den Patch anwenden willst:
</p>

<pre>
$ git am -3 -i mbox
Commit Body is:
--------------------------
seeing if this helps the gem
--------------------------
Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all
</pre>

<p>
  Das ist praktisch, wenn Du eine ganze Reihe von Patches in einer Datei hast.
  Du kannst jeweils Patches anzeigen, an die Du Dich nicht erinnern kannst,
  oder Patches auslassen, z.B. weil Du sie schon zuvor angewendet hattest.
</p>

<p>
  Nachdem Du alle Patches in Deinem Topic Branch angewendet hast, kannst Du die
  Änderungen durchsehen, testen und entscheiden, ob Du sie in einen dauerhaft
  bestehenden Branch übernehmen willst.
</p>

<h3>Checking Out Remote Branches</h3>

<p>
  Möglicherweise kommen die Änderungen aber nicht als Patch sondern von einem
  Git Anwender, der sein eigenes Repository aufgesetzt hat, seine Änderungen
  dorthin hochgeladen und Dir dann die URL des Repositories und den Namen des
  Branches geschickt hat. In diesem Fall kannst Du das Repository als ein
  „remote“ (externes Repository) hinzufügen und die Änderungen lokal mergen.
</p>

<p>
  Wenn Dir z.B. Jessica eine E-Mail schickt und mitteilt, dass sie ein
  großartiges, neues Feature im <code>ruby-client</code> Branch ihres Repositories hat, dann kannst Du das Feature testen, indem Du
  das Repository als externes Repository Deines Projektes konfigurieren und den
  Branch lokal auscheckst:
</p>

<pre>
$ git remote add jessica git://github.com/jessica/myproject.git
$ git fetch jessica
$ git checkout -b rubyclient jessica/ruby-client
</pre>

<p>
  Wenn sie Dir später erneut eine E-Mail mit einem anderen Branch schickt, der
  ein anderes, großartiges Feature enthält, dann kannst Du diesen Branch direkt
  herunterladen und auschecken, weil Du das externe Repository noch
  konfiguriert hast.
</p>

<p>
  Dies ist insbesondere nützlich, wenn Du mit jemandem regelmäßig zusammen
  arbeitest. Wenn jemand lediglich gelegentlich einen einzelnen Patch beiträgt,
  dann ist es wahrscheinlich weniger aufwendig, ihn per E-Mail zu akzeptieren,
  als von jedem zu erwarten, einen eigenen Server zu betreiben, und selbst
  ständig externe Repositories hinzuzufügen und zu entfernen. Du wirst kaum
  hunderte von externen Repositories verwalten wollen, nur um von jedem ein
  paar Änderungen zu erhalten. Auf der anderen Seite erleichtern Dir Scripts
  und Hosted Services diesen Prozess. Es hängt also alles davon ab, wie Du
  selbst und wie Deine Mitarbeiter entwickeln.
</p>

<p>
  Wenn Du mit jemandem nicht regelmäßig zusammen arbeitest, aber trotzdem aus
  ihrem Repository mergen willst, dann kannst Du dem <code>git pull</code> Befehl die URL ihres externen Repositories übergeben. Das lädt den
  entsprechenden Branch einmalig herunter und merged ihn in Deinen aktuellen
  Branch, ohne aber die externe URL als eine Referenz zu speichern:
</p>

<pre>
$ git pull git://github.com/onetimeguy/project.git
From git://github.com/onetimeguy/project
 * branch            HEAD       -&gt; FETCH_HEAD
Merge made by recursive.
</pre>

<h3>Neuigkeiten durchsehen</h3>

<p>
  Du hast jetzt einen Topic Branch, der die neuen Änderungen enthält, und
  kannst jetzt herausfinden, was Du damit anfangen willst. In diesem Abschnitt
  gehen wir noch mal auf einige Befehle ein, die nützlich sind, um
  herauszufinden, welche Änderungen Du übernehmen würdest, wenn Du den Topic
  Branch in Deinen Hauptbranch mergest.
</p>

<p>
  Es ist in der Regel hilfreich, sich Commits anzusehen, die sich in diesem
  Branch, nicht aber im <code>master</code> Branch befinden. Du kannst Commits aus dem <code>master</code> Branch ausschließen, indem Du die <code>--not</code> Option verwendest. Wenn Du beispielsweise zwei neue Commits erhalten und sie
  in einen Topic Branch <code>contrib</code> übernommen hast, kannst Du folgendes tun:
</p>

<pre>
$ git log contrib --not master
commit 5b6235bd297351589efc4d73316f0a68d484f118
Author: Scott Chacon &lt;schacon@gmail.com&gt;
Date:   Fri Oct 24 09:53:59 2008 -0700

    seeing if this helps the gem

commit 7482e0d16d04bea79d0dba8988cc78df655f16a0
Author: Scott Chacon &lt;schacon@gmail.com&gt;
Date:   Mon Oct 22 19:38:36 2008 -0700

    updated the gemspec to hopefully work better
</pre>

<p>
  Wie Du schon gelernt hast, kannst Du außerdem die Option <code>-p</code> verwenden, um zu sehen, welche Diffs die Commits enthalten.
</p>

<p>
  Wenn Du ein vollständiges Diff aller Änderungen sehen willst, die Dein Topic
  Branch gegenüber z.B. dem <code>master</code> Branch enthält, brauchst Du einen Trick. Möglicherweise würdest Du zuerst
  das hier ausprobieren:
</p>

<pre>
$ git diff master
</pre>

<p>
  Der Befehl gibt Dir ein Diff aus, kann aber irreführend sein. Wenn im <code>master</code> Branch Änderungen committed wurden, seit der Branch angelegt wurde, erhältst
  Du scheinbar merkwürdige Ergebnisse. Das liegt daran, dass Git den Snapshot
  des letzten Commits des Topic Branches, in dem Du Dich momentan befindest,
  mit dem letzten Commit des <code>master</code> Branches vergleicht. Wenn Du beispielsweise eine Zeile in einer Datei im <code>master</code> branch hinzugefügt hast, scheint der direkte Vergleich auszusagen, dass
  diese Zeile im Topic Branch entfernt wurde.
</p>

<p>
  Wenn <code>master</code> ein direkter Vorfahr Deines Topic Branches ist, ist das kein Problem. Aber
  wenn sich die beiden Historien auseinander bewegt (xxx) haben, dann scheint
  das Diff auszusagen, dass Du alle Neuigkeiten im Topic Branch hinzufügst und
  alle Neuigkeiten im <code>master</code> Branch entfernst.
</p>

<p>
  Was Du aber eigentlich wissen willst, ist welche Änderungen der Topic Branch
  hinzugefügt hat, d.h. die Änderungen, die Du in den <code>master</code> Branch neu einführen würdest, wenn Du den Topic Branch mergest. Dieses
  Ergebnis erhältst Du, wenn Du den letzten Commit im Topic Branch mit dem
  letzten Commit vergleichst, den der Topic Branch mit <code>master</code> gemeinsam hat.
</p>

<p>
  Technisch gesehen könntest Du den letzten gemeinsamen Commit explizit
  erfragen und dann ein Diff darauf ausführen:
</p>

<pre>
$ git merge-base contrib master
36c7dba2c95e6bbb78dfa822519ecfec6e1ca649
$ git diff 36c7db
</pre>

<p>
  Das ist natürlich nicht sonderlich bequem, weshalb Git eine Kurzform dafür
  definiert: die triple-dot Syntax (xxx). Im Context des <code>git diff</code> Befehls bewirkt dies, dass Du ein Diff erhältst, das den letzten gemeinsamen
  Commit der Histories beider angegebener Branches mit dem letzten Commit des
  zuletzt angegebenen Branches vergleicht:
</p>

<pre>
$ git diff master...contrib
</pre>

<p>
  Dieser Befehl zeigt Dir diejenigen Änderungen, die im Topic Branch eingeführt
  wurden, die aber noch nicht in <code>master</code> enthalten sind.
</p>

<h3>Beiträge anderer integrieren</h3>

<p>
  Sobald Du die Änderungen in Deinem Topic Branch in einen dauerhafteren Branch
  übernehmen willst, fragt sich, wie Du das anstellen kannst. Und welchen
  generellen Workflow willst Du verwenden, um das Projekt zu pflegen? Wir
  werden eine Reihe von Möglichkeiten besprechen, die Dir zur Verfügung stehen.
</p>

<p>Merge Workflows</p>

<p>
  Eine einfache Möglichkeit besteht darin, Deine Arbeit einfach in den <code>master</code> Branch zu mergen. In diesem Workflow hast Du einen <code>master</code> Branch, der eine stabilen Code beinhaltet. Wenn Du Änderungen in einem Topic
  Branch hast, die von Dir selbst oder jemand anderem geschrieben und die
  verifiziert sind, dann mergest Du diesen Topic Branch in den <code>master</code> Branch, löschst den Topic Branch und fährst mit diesem Prozess so fort. Wenn
  es ein Repository mit zwei Branches gibt, die <code>ruby_client</code> und <code>php_client</code> heißen (wie in Bild 5-19), und Du mergest <code>ruby_client</code> zuerst und <code>php_client</code> danach, dann wird die Historie danach aussehen wie im Bild 5-20.
</p>

<p>
  Insert 18333fig0519.png Bild 5-19. Historie mit verschiedenen Topic Branches
</p>

<p>
  Insert 18333fig0520.png Bild 5-20. Nach dem Merge mit verschiedenen Topic
  Branches
</p>

<p>
  Dies ist vermutlich der einfachste, mögliche Workflow. Er ist allerdings für
  große Repositories oder Projekte manchmal problematisch.
</p>

<p>
  Wenn Du mehr Entwickler oder ein größeres Projekt hast, wirst Du in der Regel
  einen Merge Zyklus mit mindestens zwei Phasen verwenden wollen. In einem
  solchen Workflow hast Du dann zwei dauerhafte Branches, z.B. <code>master</code> und <code>develop</code>, wobei <code>master</code> ausschließlich sehr stabile Releases enthält und neuer Code im <code>develop</code> Branch integriert wird. Jedes Mal, wenn Du einen neuen Topic Branch mergen
  willst, mergest Du ihn nach <code>develop</code> (Bild 5-22). Und nur dann, wenn Du einen Release taggen willst, führst Du
  ein fast-forward (xxx) it <code>master</code> bis zum gegenwärtigen, nun stabilen Status des <code>develop</code> Branch durch.
</p>

<p>Insert 18333fig0521.png Bild 5-21. Vor dem Topic Branch Merge</p>

<p>Insert 18333fig0522.png Bild 5-22. Nach dem Topic Branch Merge</p>

<p>Insert 18333fig0523.png Bild 5-23. Nach dem Topic Branch Release</p>

<p>
  Auf diese Weise kann jeder, der Dein Repository klont, auf einfache Weise
  Deinen aktuellen <code>master</code> Branch verwenden und ihn auf neue Releases aktualisieren. Oder er kann den <code>develop</code> Branch ausprobieren, in dem sich die jeweils letzten, brandneuen Änderungen
  befinden. Du kannst dieses Konzept noch weiterführen, indem Du einen <code>integrate</code> Branch pflegst, in den neue Änderungen jeweils integriert werden. Sobald der
  Code in diesem Branch stabil zu sein scheint und alle Tests durchlaufen
  (xxx), übernimmst Du die Änderungen in den <code>develop</code> Branch. Und wenn sie sich für eine Weile in der Praxis als stabil erwiesen
  haben, fast-forwardest (xxx) Du den <code>master</code> Branch.
</p>

<p>Workflows für umfassende Merges</p>

<p>
  Das Git Projekt selbst hat view dauerhafte Branches: <code>master</code>, <code>next</code>, <code>pu</code> („proposed updates“, d.h. vorgeschlagene Änderungen) und <code>maint</code> („maintenance backports“, d.h. xxx Rückportierungen). Wenn neue Änderungen
  herein kommen, werden sie in Topic Branches im Projekt Repository gesammelt,
  ganz ähnlich wie wir gerade besprochen haben (siehe Bild 5-24). Dann wird
  evaluiert, ob die Änderungen sicher sind und übernommen werden sollen oder ob
  sie noch weiter bearbeitet werden müssen. Wenn sie übernommen werden sollen,
  werden sie in den Branch <code>next</code> gemerged und dieser Branch wird hochgeladen, sodass jeder ausprobieren kann,
  wie die neue Codebase funktioniert, nachdem die Änderungen miteinander
  integriert wurden.
</p>

<p>
  Insert 18333fig0524.png Bild 5-24. Komplexe, parallel entwickelte Topic
  Branches verwalten
</p>

<p>
  Wenn die Topic Branches noch weiter bearbeitet werden müssen, werden sie
  statt dessen in den <code>pu</code> Branch gemerged. Wenn sie dann stabil sind, werden sie erneut in <code>master</code> gemerged und aus denjenigen Änderungen neu aufgebaut, die sich in <code>next</code> befanden, es aber noch nicht bis in den <code>master</code> Branch geschafft haben (xxx wie jetzt?? xxx). D.h., <code>master</code> bewegt sich fast ständig, <code>next</code> wird gelegentlich rebased, und <code>pu</code> wird noch sehr viel häufiger rebased (siehe Bild 5-25).
</p>

<p>
  Insert 18333fig0525.png Bild 5-25. Topic Branches in dauerhafte
  Integrationsbranches mergen
</p>

<p>
  Wenn ein Topic Branch schließlich in <code>master</code> gemerged wird, wird er aus dem Repository gelöscht. Das Git Projekt hat
  außerdem einen <code>maint</code> Branch, der jeweils vom letzten Release verzweigt. In diesem Branch werden
  rückportierte Patches für den Fall gesammelt, dass ein Maintenance Release
  nötig ist. D.h., wenn Du das Git Projekt Repository klonst, findest Du vier
  Branches des Projektes in verschiedenen Stadien, die Du jeweils ausprobieren
  kannst, je nachdem wie hochaktuellen Code Du testen oder wie Du zu dem
  Projekt beitragen willst. Und der Projekt Betreiber hat auf diese Weise einen
  klar strukturierten Workflow, der es einfacher macht, neue Beiträge zu prüfen
  und zu verarbeiten.
</p>

<p>Rebase und Cherry Picking Workflows</p>

<p>
  Andere Betreiber bevorzugen, neue Änderungen auf der Basis ihres <code>master</code> Branches zu rebasen oder zu cherry-picken statt sie zu mergen, um auf diese
  Weise eine eher lineare Historie zu erhalten. Wenn Du Änderungen in einem
  Topic Branch hast, die Du integrieren willst, dann gehst Du in diesen Branch
  und führst den <code>rebase</code> Befehl aus, um diese Änderungen auf der Basis des gegenwärtigen <code>master</code> Branches (oder irgendeines anderen, stabileren Branches) neu zu schreiben.
  Wenn das glatt läuft, kannst Du den <code>master</code> Branch fast-forwarden (xxx) und erhältst so eine lineare Projekt Historie.
</p>

<p>
  Eine andere Möglichkeit, Commits aus einem Branch in einen anderen zu
  übernehmen ist der <code>cherry-pick</code> Befehl. In Git ist dieser Befehl quasi ein rebase für einen einzelnen
  Commit. Er nimmt den Patch, der mit dem Commit eingeführt wurde, und
  versucht, diesen auf den Branch anzuwenden, in dem Du Dich gerade befindest.
  Das ist nützlich, wenn Du in einem Topic Branch eine Anzahl von Commits hast,
  aber lediglich einen davon übernehmen willst. Oder wenn Du überhaupt nur
  einen Commit im Topic Branch hast, diesen aber lieber cherry-picken willst,
  statt den ganzen Branch zu rebasen. Nehmen wir z.B. an, Du hast ein Projekt,
  das so aussieht wie in Bild 5-26.
</p>

<p>
  Insert 18333fig0526.png Bild 5-26. Beispiel Historie vor einem cherry-pick
</p>

<p>
  Wenn Du den Commit <code>e43a6</code> in Deinen <code>master</code> Branch übernehmen willst, kannst Du folgendes ausführen:
</p>

<pre>
$ git cherry-pick e43a6fd3e94888d76779ad79fb568ed180e5fcdf
Finished one cherry-pick.
[master]: created a0a41a9: "More friendly message when locking the index fails."
 3 files changed, 17 insertions(+), 3 deletions(-)
</pre>

<p>
  Das wendet dieselben Änderungen, die in <code>e43a6</code> eingeführt wurden, auf den <code>master</code> Branch an, aber Du erhältst einen neuen Commit SHA-1 Hash, weil auch das
  Datum ein anderes ist. Jetzt sieht Deine Historie so aus:
</p>

<p>
  Insert 18333fig0527.png Bild 5-27. Historie nach dem cherry-pick eines
  Commits aus einem Topic Branch
</p>

<p>
  Jetzt kannst Du den Topic Branch inklusive der ggf. darin enthaltenen Commits
  löschen, falls Du sie nicht noch übernehmen willst.
</p>

<h3>Releases taggen</h3>

<p>
  Wenn Du einen Release herausgeben willst, ist es empfehlenswert, einen Tag
  dafür anzulegen, sodass man den jeweiligen Zustand der Historie jederzeit
  leicht wiederherstellen kann. Wir sind bereits in Kapitel 2 auf Git Tags
  eingegangen. Wenn Du als Betreiber den neuen Tag signieren willst, könnte das
  wie folgt aussehen:
</p>

<pre>
$ git tag -s v1.5 -m 'my signed 1.5 tag'
You need a passphrase to unlock the secret key for
user: "Scott Chacon &lt;schacon@gmail.com&gt;"
1024-bit DSA key, ID F721C45A, created 2009-02-09
</pre>

<p>
  Wenn Du Deine Tags signierst, könnte das Problem bestehen, dass Du den
  jeweiligen öffentlichen PGP key zur Verfügung stellen musst. Der Betreiber
  des Git Projektes löst das, in dem er den öffentlichen Schlüssel als Inhalt
  im Repository selbst zur Verfügung stellt und einen Tag hat, der direkt auf
  diesen Inhalt zeigt. Um das zu tun, musst Du zunächst herausfinden, welchen
  Schlüssel Du verwenden willst:
</p>

<pre>
$ gpg --list-keys
/Users/schacon/.gnupg/pubring.gpg
---------------------------------
pub   1024D/F721C45A 2009-02-09 [expires: 2010-02-09]
uid                  Scott Chacon &lt;schacon@gmail.com&gt;
sub   2048g/45D02282 2009-02-09 [expires: 2010-02-09]
</pre>

<p>
  Dann kannst Du den Schlüssel direkt in die Git Datenbank importieren, indem
  Du ihn aus GPG exportierst und die Ausgabe nach <code>git hash-object</code> weiterreichst. Das schreibt ein neues Objekt mit dem Schlüssel in die Git
  Datenbank und gibt Dir einen SHA-1 Hash zurück, der dieses Objekt
  referenziert:
</p>

<pre>
$ gpg -a --export F721C45A | git hash-object -w --stdin
659ef797d181633c87ec71ac3f9ba29fe5775b92
</pre>

<p>
  Nachdem Du jetzt den Schlüssel im Repository hast, kannst Du einen Tag für
  den SHA-1 Hash anlegen, den <code>git hash-object</code> zurückgegeben hat:
</p>

<pre>
$ git tag -a maintainer-pgp-pub 659ef797d181633c87ec71ac3f9ba29fe5775b92
</pre>

<p>
  Wenn Du <code>git push --tags</code> ausführst, wird jetzt der <code>maintainer-pgp-pub</code> Tag auf den Server geladen, sodass jeder darauf zugreifen kann. Wenn jemand
  jetzt einen signierten Tag verifizieren will, kann er Deinen öffentlichen PGP
  Schlüssel direkt aus der Datenbank holen und in seinen Schlüsselbund
  importieren:
</p>

<pre>
$ git show maintainer-pgp-pub | gpg --import
</pre>

<p>
  Dieser Schlüssel kann anschließend für alle signierten Tages verwendet
  werden. Zusätzlich kannst Du Deinen Anwendern in der Tag Meldung erklären,
  wie sie signierte Tags mit diesem Schlüssel verifizieren können.
</p>

<h3>Eine Build Nummer generieren</h3>

<p>
  Weil Git keine globalen Nummern wie <code>v123</code> kennt, die mit jedem Commit monoton hochgezählt werden, kannst Du, um einen
  leicht lesbaren Bezeichner für einen bestimmten Commit zu erhalten, den
  Befehl <code>git describe</code> auf diesen Befehl ausführen. Git erzeugt dann einen Bezeichner zurück, der
  den Namen des nächsten Tags enthält, der Anzahl der Commits seit diesem Tag
  und die ersten Zeichen des SHA-1 Hashs des Commits:
</p>

<pre>
$ git describe master
v1.6.2-rc1-20-g8c5b85c
</pre>

<p>
  Auf diese Weise kannst Du in einer Weise auf einen Commit oder Build
  verweisen, der für Andere leichter verständlich ist. Wenn Du z.B. Git selbst
  aus dem Quellcode kompilierst, der sich im Git Projekt Repository befindet,
  dann gibt <code>git --version</code> einen ähnlichen Bezeichner zurück. Wenn Du übrigens <code>git describe</code> auf einen Commit ausführst, den Du direkt getagged hast, dann erhältst Du
  statt dessen den Tag Namen.
</p>

<p>
  Der <code>git describe</code> Befehl funktioniert mit kommentierten Tags besser (d.h. Tags, die mit dem <code>-a</code> oder <code>-s</code> Flag erzeugt wurden), sodass es sich empfiehlt, Release Tags auf diese Weise
  anzulegen, wenn man <code>git describe</code> verwenden will. Du kannst diese Bezeichner auch als Parameter für andere Git
  Befehle, z.B. <code>git checkout</code> oder <code>git show</code>, wobei Git allerdings lediglich auf den abgekürzten SHA-1 Hash am Ende
  achtet, sodass er möglicherweise nicht ewig gültig ist. Das Linux Kernel
  Projekt beispielsweise erhöhte die Anzahl der Zeichen in abgekürzten Hashes
  kürzlich von 8 auf 10, um die Eindeutigkeit von SHA-1 Hashes sicherzustellen.
  Ältere <code>git describe</code> Ausgaben wurden damit ungültig.
</p>

<h3>Ein Release vorbereiten</h3>

<p>
  Du willst jetzt ein Release herausgeben. Dazu willst Du u.a. ein Archiv mit
  dem letzten Snapshot Deines Codes erzeugen, damit ihn auch arme Seelen
  herunterladen können, die Git nicht verwenden. Der folgende Befehl hilft Dir
  dabei:
</p>

<pre>
$ git archive master --prefix='project/' | gzip &gt; `git describe master`.tar.gz
$ ls *.tar.gz
v1.6.2-rc1-20-g8c5b85c.tar.gz
</pre>

<p>
  Das erzeugt einen Tarball, der den aktuellen Snapshot in Deinem
  Arbeitsverzeichnis enthält. Du kannst auf die gleiche Weise ein Zip Archiv
  erzeugen, indem Du <code>git archive</code> die <code>--format=zip</code> Option übergibst.
</p>

<pre>
$ git archive master --prefix='project/' --format=zip &gt; `git describe master`.zip
</pre>

<p>
  Du hast jetzt sowohl einen Tarball als auch ein Zip Archiv Deines Releases.
  Diese kannst Du z.B. auf Deiner Webseite publizieren oder auch per E-Mail
  verschicken.
</p>

<h3>Das Shortlog</h3>

<p>
  Es wird Zeit, den Lesern der Mailingliste zu erklären, was es im Projekt
  Neues gibt. Der <code>git shortlog</code> Befehl ist eine Möglichkeit, schnell eine Art Changelog der Änderungen seit
  dem letzten Release auszugeben. Er fasst alle Commits in der angegebenen
  Zeitspanne zusammen. Der folgende Befehl z.B. erzeugt eine Zusammenfassung
  der Commits seit dem letzten Release, der als <code>v1.0.1</code> getagged wurde:
</p>

<pre>
$ git shortlog --no-merges master --not v1.0.1
Chris Wanstrath (8):
      Add support for annotated tags to Grit::Tag
      Add packed-refs annotated tag support.
      Add Grit::Commit#to_patch
      Update version and History.txt
      Remove stray `puts`
      Make ls_tree ignore nils

Tom Preston-Werner (4):
      fix dates in history
      dynamic version method
      Version bump to 1.0.2
      Regenerated gemspec for version 1.0.2
</pre>

<p>
  Du erhältst eine saubere Auflistung aller Commits seit <code>v1.0.1</code>, gruppiert nach Autor. Diese kannst Du z.B. an die Mailingliste schicken
  oder irgendwie anders publizieren.
</p>

<h2>Zusammenfassung</h2>

<p>
  Du solltest Dich jetzt einigermaßen vertraut damit fühlen, sowohl Beiträge
  bei einem bestehenden Projekt einzureichen als auch selbst ein eigenes
  Projekt zu betreiben und Beiträge anderer zu integrieren. Herzlichen
  Glückwunsch, Du bist jetzt ein erfolgreicher Git Entwickler! (xxx hä? xxx) Im
  nächsten Kapitel wirst Du weitere mächtige Git Werkzeuge und Tipps dafür
  kennenlernen, mit komplexen Situationen umzugehen – die einen wahren Git
  Meister aus Dir werden. (xxx aha? xxx)
</p>

<h1>Git Tools</h1>

<p>
  Bis hierher hast Du die meisten täglichen Kommandos und Arbeitsweisen
  gelernt, die Du brauchst um ein Git-Repository für Deine Quellcode-Kontrolle,
  zu benutzen. Du hast die grundlegenden Aufgaben des Nachverfolgens und
  Eincheckens von Dateien gemeistert und Du hast von der Macht der
  Staging-Area, des Branching und des Mergens Gebrauch gemacht.
</p>

<p>
  Als Nächstes werden wir einige sehr mächtige Werkzeuge besprechen, die Dir
  Git zur Verfügung stellt. Du wirst zwar nicht unbedingt jeden Tag verwenden,
  aber mit Sicherheit an einem bestimmten Punkt gut brauchen können.
</p>

<h2>Revision Auswahl</h2>

<p>
  Git erlaubt Dir, Commits auf verschiedenste Art und Weise auszuwählen. Diese
  sind nicht immer offensichtlich, aber es ist hilfreich diese zu kennen.
</p>

<h3>Einzelne Revisionen</h3>

<p>
  Du kannst offensichtlich mithilfe des SHA-1-Hashes einen Commit auswählen,
  aber es gibt auch menschenfreundlichere Methoden, auf einen Commit zu
  verweisen. Dieser Bereich skizziert die verschiedenen Wege, die man gehen
  kann, um sich auf ein einzelnen Commit zu beziehen.
</p>

<h3>Abgekürztes SHA</h3>

<p>
  Git ist intelligent genug, den richtigen Commit herauszufinden, wenn man nur
  die ersten paar Zeichen angibt, aber nur unter der Bedingung, dass der
  SHA-1-Hash mindestens vier Zeichen lang und einzigartig ist — das bedeutet,
  dass es nur ein Objekt im derzeitigen Repository gibt, das mit diesem
  bestimmten SHA-1-Hash beginnt.
</p>

<p>
  Um zum Beispiel einen bestimmten Commit zu sehen, kann man das <code>git log</code> Kommando ausführen und den Commit identifizieren, indem man eine bestimmte
  Funktionalität hinzugefügt hat:
</p>

<pre>
$ git log
commit 734713bc047d87bf7eac9674765ae793478c50d3
Author: Scott Chacon &lt;schacon@gmail.com&gt;
Date:   Fri Jan 2 18:32:33 2009 -0800

    fixed refs handling, added gc auto, updated tests

commit d921970aadf03b3cf0e71becdaab3147ba71cdef
Merge: 1c002dd... 35cfb2b...
Author: Scott Chacon &lt;schacon@gmail.com&gt;
Date:   Thu Dec 11 15:08:43 2008 -0800

    Merge commit 'phedders/rdocs'

commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b
Author: Scott Chacon &lt;schacon@gmail.com&gt;
Date:   Thu Dec 11 14:58:32 2008 -0800

    added some blame and merge stuff
</pre>

<p>
  In diesem Fall wählt man <code>1c002dd....</code>, wenn man diesen Commit mit <code>git show</code> anzeigen lassen will, die folgenden Kommandos sind äquivalent (vorausgesetzt
  die verkürzte Version ist einzigartig):
</p>

<pre>
$ git show 1c002dd4b536e7479fe34593e72e6c6c1819e53b
$ git show 1c002dd4b536e7479f
$ git show 1c002d
</pre>

<p>
  Git kann auch selber eine Kurzform für Deine einzigartigen SHA-1-Werte
  erzeugen. Wenn Du <code>--abbrev-commit</code> dem <code>git log</code> Kommando übergibst, wird es den kürzeren Wert benutzen, diesen aber
  einzigartig halten; die Standardeinstellung sind sieben Zeichen, aber es
  werden automatisch mehr benutzt, wenn dies nötig ist, um den SHA-1-Hash
  eindeutig zu bezeichnen.
</p>

<pre>
$ git log --abbrev-commit --pretty=oneline
ca82a6d changed the version number
085bb3b removed unnecessary test code
a11bef0 first commit
</pre>

<p>
  Generell kann man sagen, dass acht bis zehn Zeichen mehr als ausreichend in
  einem Projekt sind, um eindeutig zu bleiben. Eines der größten Git-Projekte,
  der Linux-Kernel, fängt langsam an 12 von maximal 40 Zeichen zu nutzen, um
  eindeutig zu bleiben.
</p>

<h3>Ein kurzer Hinweis zu SHA-1</h3>

<p>
  Eine Menge Leute machen sich Sorgen, dass ab einem zufälligen Punkt zwei
  Objekte im Repository vorhanden sind, die den gleichen SHA-1-Hashwert haben.
  Was dann?
</p>

<p>
  Wenn es passiert, dass bei einem Commit, ein Objekt mit dem gleichen
  SHA-1-Hashwert im Repository vorhanden ist, wird Git sehen, dass das
  vorherige Objekt bereits in der Datenbank vorhanden ist und davon ausgehen,
  dass es bereits geschrieben wurde. Wenn Du versuchst, das Objekt später
  wieder auszuchecken, wirst Du immer die Daten des ersten Objekts bekommen.
</p>

<p>
  Allerdings solltest Du Dir bewusst machen, wie unglaublich unwahrscheinlich
  dieses Szenario ist. Die Länge des SHA-1-Hashs beträgt 20 Bytes oder 160
  Bits. Die Anzahl der Objekte, die benötigt werden, um eine 50% Chance einer
  Kollision zu haben, beträgt ungefähr 2^80 (die Formel zum Berechnen der
  Kollisionswahrscheinlichkeit lautet <code>p = (n(n-1)/2) * (1/2^160)</code>). 2^80 ist somit 1.2 x 10^24 oder eine Trilliarde. Das ist 1200 Mal so viel,
  wie es Sandkörner auf der Erde gibt.
</p>

<p>
  Hier ist ein Beispiel, das Dir eine Vorstellung davon gibt, was nötig ist, um
  in SHA-1 eine Kollision zu bekommen. Wenn alle 6,5 Milliarden Menschen auf
  der Erde programmieren würden und jeder jede Sekunde Code schreiben würde,
  der der gesamten Geschichte des Linux-Kernels (1 Million Git-Objekte)
  entspricht, und diesen dann in ein gigantisches Git-Repository übertragen
  würde, würde es fünf Jahre dauern, bis das Repository genügend Objekte hätte,
  um eine 50% Wahrscheinlichkeit für eine einzige SHA-1-Kollision aufzuweisen.
  Es ist wahrscheinlicher, dass jedes Mitglied Deines Programmierer-Teams,
  unabhängig voneinander, in einer Nacht von Wölfen angegriffen und getötet
  wird.
</p>

<h3>Branch-Referenzen</h3>

<p>
  Am direktesten kannst Du einen Commit spezifizieren, wenn eine
  Branch-Referenz direkt auf ihn zeigt. In dem Fall kannst Du in allen
  Git-Befehlen, die ein Commit-Objekt oder einen SHA-1-Wert erwarten,
  stattdessen den Branch-Namen verwenden. Wenn Du z.B. den letzten Commit in
  einem Branch sehen willst, sind die folgenden Befehle äquivalent
  (vorausgesetzt der <code>topic1</code> Branch zeigt auf den Commit <code>ca82a6d</code>):
</p>

<pre>
$ git show ca82a6dff817ec66f44342007202690a93763949
$ git show topic1
</pre>

<p>
  Wenn Du sehen willst, auf welchen SHA-1-Wert ein Branch zeigt, oder wie
  unsere Beispiele intern in SHA-1-Werte übersetzt aussähen, kannst Du den
  Git-Plumbing-Befehl <code>rev-parse</code> verwenden. In Kapitel 9 werden wir genauer auf Plumbing-Befehle eingehen.
  Kurz gesagt ist <code>rev-parse</code> als eine Low-Level-Operation gedacht und nicht dafür, im tagtäglichen
  Gebrauch eingesetzt zu werden. Aber es kann manchmal hilfreich sein, wenn man
  wissen muss, was unter der Haube vor sich geht:
</p>

<pre>
$ git rev-parse topic1
ca82a6dff817ec66f44342007202690a93763949
</pre>

<h3>RefLog Kurznamen</h3>

<p>
  Eine andere Sache, die während Deiner täglichen Arbeit im Hintergrund
  passiert ist, dass Git ein sogenanntes Reflog führt, d.h. ein Log darüber,
  wohin Deine HEAD- und Branch-Referenzen in den letzten Monaten jeweils
  gezeigt haben.
</p>

<p>Du kannst das Reflog mit <code>git reflog</code> anzeigen:</p>

<pre>
$ git reflog
734713b... HEAD@{0}: commit: fixed refs handling, added gc auto, updated
d921970... HEAD@{1}: merge phedders/rdocs: Merge made by recursive.
1c002dd... HEAD@{2}: commit: added some blame and merge stuff
1c36188... HEAD@{3}: rebase -i (squash): updating HEAD
95df984... HEAD@{4}: commit: # This is a combination of two commits.
1c36188... HEAD@{5}: rebase -i (squash): updating HEAD
7e05da5... HEAD@{6}: rebase -i (pick): updating HEAD
</pre>

<p>
  Immer dann, wenn ein Branch in irgendeiner Weise aktualisiert wird oder Du
  den aktuellen Branch wechselst, speichert Git diese Information ebenso im
  Reflog wie Commits und anderen Informationen. Wenn Du wissen willst, welches
  der fünfte Wert vor dem HEAD war, kannst Du die <code>@{n}</code> Referenz angeben, die Du in Reflog-Ausgabe sehen kannst:
</p>

<pre>
$ git show HEAD@{5}
</pre>

<p>
  Außerdem kannst Du dieselbe Syntax verwenden, um eine Zeitspanne anzugeben.
  Um z.B. zu sehen, wo Dein <code>master</code> Branch gestern war, kannst Du eingeben:
</p>

<pre>
$ git show master@{yesterday}
</pre>

<p>
  Das zeigt Dir, wo der <code>master</code> Branch gestern war. Diese Technik funktioniert nur mit Daten, die noch im
  Reflog sind, d.h. man kann sie nicht für Commits verwenden, die ein älter
  sind als ein paar Monate.
</p>

<p>
  Um Reflog Informationen in einem Format wie in <code>git log</code> anzeigen, kannst Du <code>git log -g</code> verwenden:
</p>

<pre>
$ git log -g master
commit 734713bc047d87bf7eac9674765ae793478c50d3
Reflog: master@{0} (Scott Chacon &lt;schacon@gmail.com&gt;)
Reflog message: commit: fixed refs handling, added gc auto, updated
Author: Scott Chacon &lt;schacon@gmail.com&gt;
Date:   Fri Jan 2 18:32:33 2009 -0800

    fixed refs handling, added gc auto, updated tests

commit d921970aadf03b3cf0e71becdaab3147ba71cdef
Reflog: master@{1} (Scott Chacon &lt;schacon@gmail.com&gt;)
Reflog message: merge phedders/rdocs: Merge made by recursive.
Author: Scott Chacon &lt;schacon@gmail.com&gt;
Date:   Thu Dec 11 15:08:43 2008 -0800

    Merge commit 'phedders/rdocs'
</pre>

<p>
  Es ist wichtig zu verstehen, dass das Reflog ausschließlich lokale Daten
  enthält. Es ist ein Log darüber, was Du in Deinem Repository getan hast, und
  es ist nie dasselbe wie in einem anderen Klon des selben Repositorys. Direkt
  nachdem Du ein Repository geklont hast, ist das Reflog leer, weil noch keine
  weitere Aktivität stattgefunden hat. <code>git show HEAD@{2.months.ago}</code> funktioniert nur dann, wenn das Projekt mindestens zwei Monate alt ist –
  wenn Du es vor fünf Minuten erst geklont hast, erhältst Du keine Ergebnisse.
</p>

<h3>Vorfahren Referenzen</h3>

<p>
  Außerdem kann man Commits über ihre Vorfahren spezifizieren. Wenn Du ein <code>^</code> ans Ende einer Referenz setzt, schlägt Git den direkten Vorfahren dieses
  Commits nach. Nehmen wir an, Deine Historie sieht so aus:
</p>

<pre>
$ git log --pretty=format:'%h %s' --graph
* 734713b fixed refs handling, added gc auto, updated tests
*   d921970 Merge commit 'phedders/rdocs'
|\
| * 35cfb2b Some rdoc changes
* | 1c002dd added some blame and merge stuff
|/
* 1c36188 ignore *.gem
* 9b29157 add open3_detach to gemspec file list
</pre>

<p>
  Du kannst jetzt den vorletzten Commit mit <code>HEAD^</code> referenzieren, d.h. „den direkten Vorfahren von HEAD“.
</p>

<pre>
$ git show HEAD^
commit d921970aadf03b3cf0e71becdaab3147ba71cdef
Merge: 1c002dd... 35cfb2b...
Author: Scott Chacon &lt;schacon@gmail.com&gt;
Date:   Thu Dec 11 15:08:43 2008 -0800

    Merge commit 'phedders/rdocs'
</pre>

<p>
  Außerdem kannst Du nach dem <code>^</code> eine Zahl angeben. Beispielsweise heißt <code>d921970^2</code>: „der zweite Vorfahr von d921970“. Diese Syntax ist allerdings nur für Merge
  Commits nützlich, die mehr als einen Vorfahren haben. Der erste Vorfahr ist
  der Branch, auf dem Du Dich beim Merge befandest, der zweite ist der Commit
  auf dem Branch, den Du gemergt hast.
</p>

<pre>
$ git show d921970^
commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b
Author: Scott Chacon &lt;schacon@gmail.com&gt;
Date:   Thu Dec 11 14:58:32 2008 -0800

    added some blame and merge stuff

$ git show d921970^2
commit 35cfb2b795a55793d7cc56a6cc2060b4bb732548
Author: Paul Hedderly &lt;paul+git@mjr.org&gt;
Date:   Wed Dec 10 22:22:03 2008 +0000

    Some rdoc changes
</pre>

<p>
  Eine andere Vorfahren-Spezifikation ist <code>~</code>. Dies bezieht sich ebenfalls auf den ersten Vorfahren, d.h. <code>HEAD~</code> und <code>HEAD^</code> sind äquivalent. Einen Unterschied macht es allerdings, wenn Du außerdem
  eine Zahl angibst. <code>HEAD~2</code> bedeutet z.B. „der Vorfahr des Vorfahren von HEAD“ oder „der n-te Vorfahr
  von HEAD“. Beispielsweise würde <code>HEAD~3</code> in der obigen Historie auf den folgenden Commit zeigen:
</p>

<pre>
$ git show HEAD~3
commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d
Author: Tom Preston-Werner &lt;tom@mojombo.com&gt;
Date:   Fri Nov 7 13:47:59 2008 -0500

    ignore *.gem
</pre>

<p>
  Dasselbe kannst Du mit <code>HEAD^^^</code> angeben, was wiederum den „Vorfahren des Vorfahren des Vorfahren“
  referenziert:
</p>

<pre>
$ git show HEAD^^^
commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d
Author: Tom Preston-Werner &lt;tom@mojombo.com&gt;
Date:   Fri Nov 7 13:47:59 2008 -0500

    ignore *.gem
</pre>

<p>
  Du kannst diese Schreibweisen auch kombinieren und z.B. auf den zweiten
  Vorfahren der obigen Referenz mit <code>HEAD~3^2</code> zugreifen.
</p>

<h3>Commit Reihen</h3>

<p>
  Nachdem Du jetzt einzelne Commits spezifizieren kannst, schauen wir uns an,
  wie man auf Commit-Reihen zugreift. Dies ist vor allem nützlich, um Branches
  zu verwalten, z.B. wenn man viele Branches hat und solche Fragen beantworten
  will wie „Welche Änderungen befinden sich in diesem Branch, die ich noch
  nicht in meinen Hauptbranch gemergt habe?“.
</p>

<p>Zwei-Punkte-Syntax</p>

<p>
  Die gängigste Weise, Commit-Reihen anzugeben, ist die Zwei-Punkte-Notation.
  Allgemein gesagt liefert Git damit eine Reihe von Commits, die von dem einem
  Commit aus erreichbar sind, allerdings nicht von dem anderen aus. Nehmen wir
  z.B. an, Du hättest eine Commit-Historie wie die folgende (Bild 6-1).
</p>

<p>Insert 18333fig0601.png</p>

<p>
  Du willst jetzt herausfinden, welche Änderungen in Deinem <code>experiment</code>-Branch sind, die noch nicht in den <code>master</code>-Branch gemergt wurden. Dann kannst Du ein Log dieser Commits mit <code>master..experiment</code> anzeigen, d.h. „alle Commits, die von <code>experiment</code> aus erreichbar sind, aber nicht von <code>master</code>“. Um die folgenden Beispiele ein bisschen abzukürzen und deutlicher zu
  machen, verwende ich für die Commit-Objekte die Buchstaben aus dem Diagramm
  anstelle der tatsächlichen Log Ausgabe:
</p>

<pre>
$ git log master..experiment
D
C
</pre>

<p>
  Wenn Du allerdings – anders herum – diejenigen Commits anzeigen willst, die
  in <code>master</code>, aber noch nicht in <code>experiment</code> sind, kannst Du die Branch-Namen umdrehen: <code>experiment..master</code> zeigt „alles in <code>master</code>, das nicht in <code>experiment</code> enthalten ist“.
</p>

<pre>
$ git log experiment..master
F
E
</pre>

<p>
  Dies ist nützlich, wenn Du vorhast, den <code>experiment</code>-Branch zu aktualisieren, und anzeigen willst, was Du dazu mergen wirst. Oder
  wenn Du vorhast, in ein Remote-Repository zu pushen und sehen willst, welche
  Commits betroffen sind:
</p>

<pre>
$ git log origin/master..HEAD
</pre>

<p>
  Dieser Befehl zeigt Dir alle Commits im gegenwärtigen, lokalen Branch, die
  noch nicht im <code>master</code>-Branch des <code>origin</code> Repositorys sind. D.h., der Befehl listet diejenigen Commits auf, die auf
  den Server transferiert würden, wenn Du <code>git push</code> benutzt und der aktuelle Branch <code>origin/master</code> trackt. Du kannst mit dieser Syntax außerdem eine Seite der beiden Punkte
  leer lassen. Git nimmt dann an, Du meinst an dieser Stelle HEAD. Z.B. kannst
  Du dieselben Commits wie im vorherigen Beispiel auch mit <code>git log origin/master..</code> anzeigen lassen. Git fügt dann HEAD auf der rechten Seite ein.
</p>

<p>Mehrere Bezugspunkte</p>

<p>
  Die Zwei-Punkte-Syntax ist eine nützliche Abkürzung, aber möglicherweise
  willst Du mehr als nur zwei Branches angeben, um z.B. herauszufinden, welche
  Commits in einem beliebigen anderen Branch enthalten sind, ausgenommen in
  demjenigen, auf dem Du Dich gerade befindest. Dazu kannst Du in Git das <code>^</code> Zeichen oder <code>--not</code> verwenden, um Commits auszuschließen, die von den angegebenen Referenzen aus
  erreichbar sind. D.h., die folgenden drei Befehle sind äquivalent:
</p>

<pre>
$ git log refA..refB
$ git log ^refA refB
$ git log refB --not refA
</pre>

<p>
  Das ist praktisch, weil Du auf diese Weise mehr als nur zwei Referenzen
  angeben kannst, was mit der Zwei-Punkte-Notation nicht geht. Wenn Du
  beispielsweise alle Commits sehen willst, die von <code>refA</code> oder <code>refB</code> erreichbar sind, nicht aber von <code>refC</code>, dann kannst Du folgende (äquivalente) Befehle benutzen:
</p>

<pre>
$ git log refA refB ^refC
$ git log refA refB --not refC
</pre>

<p>
  Damit hast Du ein sehr mächtiges System von Abfragen zur Verfügung, mit denen
  Du herausfinden kannst, was in welchen Deiner Branches enthalten ist.
</p>

<p>Drei-Punkte-Syntax</p>

<p>
  Die letzte wichtige Syntax, mit der man Commit-Reihen spezifizieren kann, ist
  die Drei-Punkte-Syntax, die alle Commits anzeigt, die in einer der beiden
  Referenzen enthalten sind, aber nicht in beiden. Schau Dir noch mal die
  Commit Historie in Bild 6-1 an. Wenn Du diejenigen Commits anzeigen willst,
  die in den <code>master</code>- und <code>experiment</code>-Branches, nicht aber in beiden Branches gleichzeitig enthalten sind, dann
  kannst Du folgendes tun:
</p>

<pre>
$ git log master...experiment
F
E
D
C
</pre>

<p>
  Dies gibt Dir wiederum ein normale <code>log</code> Ausgabe, aber zeigt nur die Informationen dieser vier Commits – wie üblich
  sortiert nach dem Commit-Datum.
</p>

<p>
  Eine nützliche Option für den <code>log</code> Befehl ist in diesem Fall <code>--left-right</code>. Sie zeigt Dir an, in welchem der beiden Branches der jeweilige Commit
  enthalten ist, sodass die Ausgabe noch nützlicher ist:
</p>

<pre>
$ git log --left-right master...experiment
&lt; F
&lt; E
&gt; D
&gt; C
</pre>

<p>
  Mit diesen Hilfsmitteln kannst Du noch einfacher und genauer angeben, welche
  Commits Du nachschlagen willst.
</p>

<h2>Interaktives Stagen</h2>

<p>
  Git umfasst eine Reihe von Skripten, die so manche Aufgabe auf der
  Kommandozeile leichter machen. Im Folgenden schauen wir uns einige
  interaktive Befehle an, die dabei hilfreich sein können, wenn man Änderungen
  in vielen Dateien vorgenommen hat, aber nur einige Änderungen gezielt
  committen will – nicht alles auf einmal in einem riesigen Commit. Auf diese
  Weise kann man Commits logisch gruppieren und macht es anderen Entwicklern
  damit leichter, sie zu verstehen. Wenn Du <code>git add</code> mit der <code>-i</code> oder <code>--interactive</code> Option verwendest, geht Git in einen interaktiven Shell-Modus, der in etwa
  wie folgt aussieht:
</p>

<pre>
$ git add -i
           staged     unstaged path
  1:    unchanged        +0/-1 TODO
  2:    unchanged        +1/-1 index.html
  3:    unchanged        +5/-1 lib/simplegit.rb

*** Commands ***
  1: status     2: update      3: revert     4: add untracked
  5: patch      6: diff        7: quit       8: help
What now&gt;
</pre>

<p>
  Wie Du siehst, zeigt dieser Befehl eine andere Ansicht der Staging-Area an –
  im Wesentlichen also die Information, die Du auch mit <code>git status</code> erhältst, aber anders formatiert, kurz und knapp, und informativer. Sie
  listet alle Änderungen, die in der Staging-Area enthalten sind, auf der
  linken Seite, und alle anderen Änderungen auf der rechten Seite.
</p>

<p>
  Danach folgt eine Liste von Befehlen wie, u.a., Dateien ganz oder teilweise
  stagen und unstagen, nicht versionskontrollierte Dateien hinzufügen, Diffs
  der gestageten Änderungen anzeigen etc.
</p>

<h3>Hinzufügen und Enfernen von Dateien aus der Staging-Area</h3>

<p>
  Wenn Du am <code>What now></code> Prompt <code>2</code> oder <code>u</code> eingibst, wirst Du als Nächstes gefragt, welche Dateien Du stagen willst:
</p>

<pre>
What now&gt; 2
           staged     unstaged path
  1:    unchanged        +0/-1 TODO
  2:    unchanged        +1/-1 index.html
  3:    unchanged        +5/-1 lib/simplegit.rb
Update&gt;&gt;
</pre>

<p>
  Um z.B. die TODO und index.html Dateien zu stagen, gibst Du die jeweiligen
  Zahlen ein:
</p>

<pre>
Update&gt;&gt; 1,2
           staged     unstaged path
* 1:    unchanged        +0/-1 TODO
* 2:    unchanged        +1/-1 index.html
  3:    unchanged        +5/-1 lib/simplegit.rb
Update&gt;&gt;
</pre>

<p>
  Das <code>*</code> neben den Dateinamen bedeutet, dass die Datei ausgewählt ist und zur
  Staging-Area hinzugefügt werden wird, sobald Du (bei einem sonst leeren <code>Update>></code> Prompt) Enter drückst:
</p>

<pre>
Update&gt;&gt;
updated 2 paths

*** Commands ***
  1: status     2: update      3: revert     4: add untracked
  5: patch      6: diff        7: quit       8: help
What now&gt; 1
           staged     unstaged path
  1:        +0/-1      nothing TODO
  2:        +1/-1      nothing index.html
  3:    unchanged        +5/-1 lib/simplegit.rb
</pre>

<p>
  Du kannst sehen, dass die TODO und index.html Dateien jetzt gestaget sind,
  während simplegit.rb immer noch ungestaget ist. Wenn Du die TODO unstagen
  willst, kannst Du die Option <code>3</code> oder <code>r</code> (für revert) nutzen:
</p>

<pre>
*** Commands ***
  1: status     2: update      3: revert     4: add untracked
  5: patch      6: diff        7: quit       8: help
What now&gt; 3
           staged     unstaged path
  1:        +0/-1      nothing TODO
  2:        +1/-1      nothing index.html
  3:    unchanged        +5/-1 lib/simplegit.rb
Revert&gt;&gt; 1
           staged     unstaged path
* 1:        +0/-1      nothing TODO
  2:        +1/-1      nothing index.html
  3:    unchanged        +5/-1 lib/simplegit.rb
Revert&gt;&gt; [enter]
reverted one path
</pre>

<p>
  Wenn Du wiederum Deinen Git-Status ansiehst, kannst Du sehen, dass Du die
  TODO ungestaget hast.
</p>

<pre>
*** Commands ***
  1: status     2: update      3: revert     4: add untracked
  5: patch      6: diff        7: quit       8: help
What now&gt; 1
           staged     unstaged path
  1:    unchanged        +0/-1 TODO
  2:        +1/-1      nothing index.html
  3:    unchanged        +5/-1 lib/simplegit.rb
</pre>

<p>
  Um einen Diff dessen zu sehen, das Du gestaget hast, kannst Du den Befehl <code>6</code> oder <code>d</code> (für diff) nutzen. Dieser zeigt Dir eine Liste der gestageten Dateien, und
  Du kannst diejenigen auswählen, von denen Du den gestageten Diff sehen
  willst. Dies ähnelt sehr dem Befehl <code>git diff --cached</code> auf der Kommandozeile.
</p>

<pre>
*** Commands ***
  1: status     2: update      3: revert     4: add untracked
  5: patch      6: diff        7: quit       8: help
What now&gt; 6
           staged     unstaged path
  1:        +1/-1      nothing index.html
Review diff&gt;&gt; 1
diff --git a/index.html b/index.html
index 4d07108..4335f49 100644
--- a/index.html
+++ b/index.html
@@ -16,7 +16,7 @@ Date Finder

 &lt;p id="out"&gt;...&lt;/p&gt;

-&lt;div id="footer"&gt;contact : support@github.com&lt;/div&gt;
+&lt;div id="footer"&gt;contact : email.support@github.com&lt;/div&gt;

 &lt;script type="text/javascript"&gt;
</pre>

<p>
  Mit diesen grundlegenden Befehlen kannst Du den interaktiven Hinzufüge-Modus
  nutzen, um Dir den Umgang mit Deiner Staging-Area etwas zu erleichtern.
</p>

<h3>Patches stagen</h3>

<p>
  Es ist für Git auch möglich, bestimmte Teile einer Datei zu stagen und nicht
  den Rest. Wenn Du z.B. zwei Veränderungen an der simplegit.rb machst und eine
  davon stagen willst und die andere nicht, ist dies sehr einfach in Git
  möglich. Wähle <code>5</code> oder <code>p</code> (für patch) auf dem interaktiven Prompt. Git wird Dich fragen, welche
  Dateien Du teilweise stagen willst; dann wird es für jeden Abschnitt der
  gewählten Dateien Diff-Ausschnitte ausgeben und Dich jeweils einzeln fragen,
  ob Du sie stagen willst.
</p>

<pre>
diff --git a/lib/simplegit.rb b/lib/simplegit.rb
index dd5ecc4..57399e0 100644
--- a/lib/simplegit.rb
+++ b/lib/simplegit.rb
@@ -22,7 +22,7 @@ class SimpleGit
   end

   def log(treeish = 'master')
-    command("git log -n 25 #{treeish}")
+    command("git log -n 30 #{treeish}")
   end

   def blame(path)
Stage this hunk [y,n,a,d,/,j,J,g,e,?]?
</pre>

<p>
  Du hast an diesem Punkt viele Optionen. Tippe <code>?</code> ein, um eine Liste der Möglichkeiten zu bekommen:
</p>

<pre>
Stage this hunk [y,n,a,d,/,j,J,g,e,?]? ?
y - stage this hunk
n - do not stage this hunk
a - stage this and all the remaining hunks in the file
d - do not stage this hunk nor any of the remaining hunks in the file
g - select a hunk to go to
/ - search for a hunk matching the given regex
j - leave this hunk undecided, see next undecided hunk
J - leave this hunk undecided, see next hunk
k - leave this hunk undecided, see previous undecided hunk
K - leave this hunk undecided, see previous hunk
s - split the current hunk into smaller hunks
e - manually edit the current hunk
? - print help
</pre>

<p>
  Im Allgemeinen, wirst Du <code>y</code> oder <code>n</code> nutzen, wenn Du jeden Ausschnitt stagen willst, aber alle Ausschnitte in
  bestimmten Dateien zu stagen oder die Entscheidung für einen Ausschnitt auf
  später zu verschieben kann auch sehr hilfreich sein. Wenn Du nur einen Teil
  der Datei stagest und den anderen ungestaget lässt, sieht Deine
  Status-Ausgabe in etwa so aus:
</p>

<pre>
What now&gt; 1
           staged     unstaged path
  1:    unchanged        +0/-1 TODO
  2:        +1/-1      nothing index.html
  3:        +1/-1        +4/-0 lib/simplegit.rb
</pre>

<p>
  Der Status der simplegit.rb ist interessant. Er zeigt Dir, dass ein paar
  Zeilen gestaget und ein paar ungestaget sind. Du hast diese Datei teilweise
  gestaget. An dieser Stelle kannst Du das interaktive Hinzufüge-Skript
  verlassen und <code>git commit</code> ausführen, um die teilweise gestageten Dateien zu commiten.
</p>

<p>
  Letztendlich musst Du nicht den interaktiven Hinzufüge-Modus nutzen, um
  Dateien teilweise zu stagen – Du kannst das gleiche Skript starten, indem Du <code>git add -p</code> oder <code>git add --patch</code> auf der Kommandozeile eingibst.
</p>

<h2>Stashen</h2>

<p>
  Während man an einer bestimmten Funktion eines Projekts arbeitet, ist es oft
  so, dass man den Branch wechseln will, weil man an etwas anderem
  weiterarbeiten will. Meist ist dann auch das Arbeitsverzeichnis in einem
  chaotischen Zustand, da die Funktion noch nicht fertiggestellt ist. Das
  Problem dabei ist, dass Du Deine halbfertige Arbeit dann auch nicht committen
  möchtest, um später daran weiter arbeiten zu können. Die Lösung dieses
  Problems bietet der <code>git stash</code> Befehl.
</p>

<p>
  Beim Stashen werden die aus Deinem Arbeitsverzeichnis noch nicht committeten
  Änderungen – also Deine geänderten beobachteten Dateien und die in der
  Staging-Area enthaltenen Dateien – in einem Stack voller unfertiger
  Änderungen gespeichert. Diese kannst Du dann jederzeit wieder vom Stack holen
  und auf Dein Arbeitsverzeichnis anwenden.
</p>

<h3>Stash verwenden</h3>

<p>
  Um dies zu demonstrieren, gehst Du in Dein Projekt und beginnst an ein paar
  Dateien zu arbeiten und merkst ein paar dieser Änderungen in der Staging-Area
  vor. Wenn Du den Befehl <code>git status</code> ausführst, siehst Du, dass sich einige Dateien seit dem letzen Commit
  verändert haben.
</p>

<pre>
$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD &lt;file&gt;..." to unstage)
#
#      modified:   index.html
#
# Changes not staged for commit:
#   (use "git add &lt;file&gt;..." to update what will be committed)
#
#      modified:   lib/simplegit.rb
#
</pre>

<p>
  Jetzt kommt der Zeitpunkt, an dem Du den aktuellen Branch wechseln möchtest.
  Allerdings willst Du den aktuellen Zustand auch nicht committen, da Deine
  Arbeit noch nicht ganz fertiggestellt ist. Darum legst Du Deine Änderungen
  jetzt in einem Stash ab. Um diesen neuen Stash auf dem Stack abzulegen,
  verwendest Du den Befehl <code>git stash</code>:
</p>

<pre>
$ git stash
Saved working directory and index state \
  "WIP on master: 049d078 added the index file"
HEAD is now at 049d078 added the index file
(To restore them type "git stash apply")
</pre>

<p>
  In Deinem Arbeitsverzeichnis befinden sich jetzt keine geänderten Dateien
  mehr und die Staging-Area ist auch leer:
</p>

<pre>
$ git status
# On branch master
nothing to commit, working directory clean
</pre>

<p>
  In diesem Zustand, kannst Du in beliebig andere Branches wechseln und an
  etwas anderem weiterarbeiten. Deine Änderungen sind alle in einem Stack
  gesichert. Um eine Übersicht, der bereits gestashten Änderungen anzusehen,
  kannst Du den Befehl <code>git stash list</code> verwenden:
</p>

<pre>
$ git stash list
stash@{0}: WIP on master: 049d078 added the index file
stash@{1}: WIP on master: c264051... Revert "added file_size"
stash@{2}: WIP on master: 21d80a5... added number to log
</pre>

<p>
  In diesem Beispiel waren bereits zwei Stashes auf dem Stack vorhanden. Sie
  wurden zu einem früheren Zeitpunkt gespeichert. Dir stehen jetzt also drei
  verschiedene Stashes auf dem Stack zur Verfügung. Mit dem Befehl <code>git stash apply</code> kannst Du die zuletzt gestashten Änderungen in Deinem Arbeitsverzeichnis
  wiederherstellen. Git zeigt diesen Befehlsaufruf auch bei Ausführen des
  Befehls <code>git stash</code> als Hilfestellung an. Wenn Du einen der älteren Stashes auf Dein
  Arbeitsverzeichnis anwenden willst, kannst Du den entsprechenden Stashnamen
  an den Befehl anhängen: <code>git stash apply stash@{2}</code>. Wie Du bereits gesehen hast, verwendet Git die zuletzt gestashten
  Änderungen und versucht diese im Arbeitsverzeichnis wiederherzustellen, wenn
  der Stashname nicht angegeben wird:
</p>

<pre>
$ git stash apply
# On branch master
# Changes not staged for commit:
#   (use "git add &lt;file&gt;..." to update what will be committed)
#
#      modified:   index.html
#      modified:   lib/simplegit.rb
#
</pre>

<p>
  Wie Du sehen kannst, stellt Git die Dateien wieder her, die Du in einem Stash
  gespeichert hast. In dem Beispiel war Dein Arbeitsverzeichnis in einem
  sauberen Zustand, als Du versucht hast, den Stash zurückzuladen. Ebenso wurde
  der Stash auf dem gleichen Branch angewandt, der auch beim Stashen der
  Änderungen ausgecheckt war. Aber es ist nicht zwingend notwendig, dass der
  gleiche Branch verwendet wird und dass das Arbeitsverzeichnis in einem
  sauberen Zustand ist, wenn ein Stash zurückgeladen wird. Du kannst die
  Änderungen in einem Stash ablegen, zu einem anderen Branch wechseln und die
  Änderungen in diesem neuen Branch wiederherstellen. Es können auch geänderte
  oder gestagte Dateien im Arbeitsverzeichnis vorhanden sein, während ein Stash
  zurückgeladen wird. Können die Änderungen nicht ordnungsgemäß zurückgeladen
  werden, zeigt Git einen entsprechenden Merge-Konflikt an.
</p>

<p>
  Die Änderungen an den Dateien wurden in Deinem Arbeitsverzeichnis
  wiederhergestellt. Allerdings ist die Datei, die beim Stashen in der
  Staging-Area vorhanden war, nicht automatisch wieder in die Staging-Area
  gewandert. Wenn Du die Option <code>--index</code> an den Befehl <code>git stash apply</code> anhängst, wird Git versuchen, die Dateien wieder zu stagen. Wenn Du diesen
  Befehl angewandt hättest, wäre Dein Arbeitsverzeichnis und Deine Staging-Area
  im exakt gleichen Zustand, wie vor dem Stashen:
</p>

<pre>
$ git stash apply --index
# On branch master
# Changes to be committed:
#   (use "git reset HEAD &lt;file&gt;..." to unstage)
#
#      modified:   index.html
#
# Changes not staged for commit:
#   (use "git add &lt;file&gt;..." to update what will be committed)
#
#      modified:   lib/simplegit.rb
#
</pre>

<p>
  Mit der Option <code>apply</code> wird nur versucht die Änderungen wiederherzustellen. Der Stash an sich
  bleibt weiterhin auf dem Stack vorhanden. Um diesen zu entfernen, kannst Du
  den Befehl <code>git stash drop</code> zusammen mit dem Namen des Stashes anwenden:
</p>

<pre>
$ git stash list
stash@{0}: WIP on master: 049d078 added the index file
stash@{1}: WIP on master: c264051... Revert "added file_size"
stash@{2}: WIP on master: 21d80a5... added number to log
$ git stash drop stash@{0}
Dropped stash@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43)
</pre>

<p>
  Um den Stash zurückzuführen und gleichzeitig vom Stack zu entfernen, kann der
  Befehl <code>git stash pop</code> verwendet werden.
</p>

<h3>Zurückgeführten Stash wieder rückgängig machen</h3>

<p>
  Stellen wir uns folgendes Szenario vor: Du wendest die Änderungen eines
  Stashes wieder auf das Arbeitsverzeichnis an und änderst danach noch ein paar
  Dateien von Hand. Jetzt möchtest Du die Änderungen, die vom Stash her rühren,
  aber wieder rückgängig machen. Git besitzt kein Feature, welches dies möglich
  macht. Allerdings kann man den gleichen Effekt erzeugen, indem man vom
  betreffenden Stash einen Patch erzeugt und diesen mit der Option <code>-R</code> wieder anwendet (Patch rückwärts anwenden).
</p>

<pre>
$ git stash show -p stash@{0} | git apply -R
</pre>

<p>
  An dieser Stelle noch einmal der Hinweis, dass Git den zuletzt erstellten
  Stash verwendet, wenn kein Stashname angegeben wird:
</p>

<pre>
$ git stash show -p | git apply -R
</pre>

<p>
  Wenn Du dieses Feature öfters benötigst, ist es wahrscheinlich sinnvoll,
  einen Alias <code>stash-unapply</code> in Git dafür anzulegen:
</p>

<pre>
$ git config --global alias.stash-unapply '!git stash show -p | git apply -R'
$ git stash apply
$ #... work work work
$ git stash-unapply
</pre>

<h3>Branch auf Basis eines Stashes erzeugen</h3>

<p>
  Wenn Du einen Teil Deiner Arbeit in einem Stash ablegst, dort eine Weile
  liegen lässt und danach Deine Arbeit an dem Branch fortsetzt, der auch für
  den Stash verwendet wurde, hast Du vielleicht später Probleme beim
  Zurückführen des Stashes. Wenn beim Anwenden des Stashes Dateien modifiziert
  werden sollen, die Du im bisherigen Verlauf bereits geänderst hast, werden
  Merge-Konflikte auftreten, die Du manuell auflösen musst. Wenn Du nach einer
  einfachen Möglichkeit suchst, die gestashten Änderungen separat zu testen,
  kannst Du den Befehl <code>git stash branch</code> verwenden. Dieser Befehl erzeugt einen neuen Branch, checkt den Commit aus,
  auf dessen Basis der Stash erstellt wurde, und führt den Stash wieder in das
  Arbeitsverzeichnis zurück. Wenn dabei kein Fehler auftritt, wird der Stash
  automatisch gelöscht:
</p>

<pre>
$ git stash branch testchanges
Switched to a new branch "testchanges"
# On branch testchanges
# Changes to be committed:
#   (use "git reset HEAD &lt;file&gt;..." to unstage)
#
#      modified:   index.html
#
# Changes not staged for commit:
#   (use "git add &lt;file&gt;..." to update what will be committed)
#
#      modified:   lib/simplegit.rb
#
Dropped refs/stash@{0} (f0dfc4d5dc332d1cee34a634182e168c4efc3359)
</pre>

<p>
  Damit ist es auf einfache Art und Weise möglich, die gestashten Änderungen in
  einem neuen Branch wiederherzustellen und daran weiterzuarbeiten.
</p>

<h2>Änderungshistorie verändern</h2>

<p>
  Beim Arbeiten mit Git kommt es häufig vor, dass man seine Commit-Historie aus
  irgendeinem Grund noch einmal ändern möchte. Und das Tolle an Git ist, dass
  es Dir die Möglichkeit bietet, Entscheidungen erst im allerletzten Moment zu
  treffen. Zum Beispiel bietet Dir Git mit Hilfe der Staging-Area die
  Möglichkeit, alle Dateien zu sammeln und kurz vor einem Commit zu
  entscheiden, welche Daten alle in einen Commit wandern sollen. Du kannst auch
  Deine Dateien, die sich geändert haben, aber noch nicht ins Repository
  eingepflegt werden sollen, mit dem Stash-Kommando in einem Zwischenspeicher
  ablegen. Außerdem kannst Du bereits verfasste Commits nachträglich noch
  einmal ändern, sodass sich die Historie so ändert, als wäre sie ganz anders
  vorangeschritten. Das kann man zum Beispiel durch Änderung der Reihenfolge
  der Commits, durch Ändern von Commit-Nachrichten, durch Modifikationen an
  Dateien innerhalb eines Commits, durch Zusammenfügen zweier Commits zu einem
  Commit oder durch Löschen eines Commits erreichen. Und das Besondere daran:
  Das alles, bevor Du Deine Arbeit mit anderen teilst und veröffentlichst.
</p>

<p>
  In diesem Kapitel werden wir die nützlichen Arbeitsschritte besprechen, die
  Dir helfen, Deine Commit-Historie Deinen Wünschen entsprechend zu gestalten,
  sodass Du Dein Ergebnis danach mit anderen teilen kannst und es damit Deinem
  gewünschten Ergebnis entspricht.
</p>

<h3>Ändern des letzten Commits</h3>

<p>
  Am häufigsten möchte man wahrscheinlich seinen letzten durchgeführten Commit
  noch einmal nachträglich ändern. Meist sind es zwei Dinge, die man verändern
  möchte: Änderung der eingegebenen Commit-Nachricht oder den eigentlichen
  Inhalt des Schnappschusses durch Hinzufügen, Ändern oder Löschen von Dateien.
</p>

<p>Die letzte Commit-Nachricht noch einmal zu ändern ist sehr einfach:</p>

<pre>
$ git commit --amend
</pre>

<p>
  Nach Eingabe dieses Befehls wird der Texteditor mit dem Inhalt der letzten
  Commit-Nachricht geöffnet. Jetzt hat man Gelegenheit diesen Text zu ändern.
  Nach dem Speichern und Schließen des Editors, wird die Commit-Nachricht des
  letzten Commits entsprechend angepasst. Der alte Commit ist dadurch nicht
  mehr vorhanden und Du erhältst einen neuen Commit mit dem gleichen Inhalt und
  Deiner neuen Commit-Nachricht.
</p>

<p>
  Wenn Du Deine Änderungen bereits eingecheckt hast und den Schnappschuss
  nachträglich durch Hinzufügen oder Ändern von Dateien noch einmal ändern
  möchtest, läuft das im Prinzip auf die gleiche Art und Weise ab. Meist kommt
  so etwas vor, weil man vergessen hat, eine neu erstellte Datei zu stagen.
  Wenn so etwas passiert, kannst Du Folgendes machen: Führe Deine gewünschte
  Änderungen durch Ändern oder Hinzufügen einer Datei aus und stage dieses
  Ergebnis mit dem Befehl <code>git add</code>. Alternativ kannst Du auch mit dem Befehl <code>git rm</code> eine Datei aus dem Repository entfernen. Wenn die Staging-Area Dein
  gewünschtes Ergebnis enthält, führst Du einfach den Befehl&emsp14;&emsp14;<code>git commit --amend</code> aus. Der neue Commit enthält nun die Änderungen aus dem alten Commit plus
  die Änderungen aus Deiner Staging-Area.
</p>

<p>
  Mit dem Befehl <code>--amend</code> sollte man vorsichtig umgehen, weil sich mit jeder nachträglichen
  Modifikation eines Commits auch die SHA-1-Prüfsumme ändert. Das Ändern des
  letzten Commits hat ein ähnliches Verhalten wie das Durchführen eines
  Rebase-Befehls. Deshalb sollte man einen Commit niemals nachträglich
  anpassen, wenn dieser bereits veröffentlicht wurde.
</p>

<h3>Änderung von mehreren Commit-Nachrichten</h3>

<p>
  Um einen Commit, der etwas weiter in der Historie zurückliegt, zu ändern,
  hilft einem der Befehl <code>--amend</code> nicht weiter. Man benötigt dazu ein etwas mächtigeres und komplexeres
  Werkzeug. Für diese Aufgabe kann man den Rebase-Befehl, den wir bereits
  kennengelernt haben, auf eine etwas andere Art und Weise nutzen. Anstatt den
  Rebase auf einen HEAD eines anderen Commits auszuführen, führt man den Rebase
  auf genau dem gleichen Commit aus, auf dem er bereits basiert. Dazu müssen
  wir nur den interaktiven Modus des Rebase-Befehls nutzen. Dieser bietet einem
  die Möglichkeit bei jedem Commit, der geändert werden soll, zu stoppen. Dann
  kann man seine Änderungen an den Dateien oder an der Commit-Nachricht
  entsprechend einpflegen und mit dem nächsten Commit fortfahren. Um einen
  interaktiven Rebase durchzuführen, muss man die Option <code>-i</code> an den Befehl <code>git rebase</code> anhängen. Außerdem musst Du natürlich bestimmen, wie viele Commits Du ändern
  möchtest. Dazu musst Du den Commit angeben, auf welchem der Rebase basieren
  soll.
</p>

<p>
  Wenn Du zum Beispiel die letzten drei, oder eine oder mehrere der letzten
  drei Commit-Nachrichten ändern möchtest, musst Du zusätzlich zu dem Befehl <code>git rebase -i</code> den übergeordneten Commit (also dem Commit, der in der Historie genau ein
  Commit zurückliegt) des letzten Commits, den Du ändern möchtest, angeben. Bei
  drei Commit-Nachrichten müsste das Argument also <code>HEAD~2^</code> beziehungsweise <code>HEAD~3</code> lauten. Wahrscheinlich fällt es Dir leichter das Argument <code>~3</code> zu merken, weil Du ja schließlich auf die letzten drei Einträge verweisen
  möchtest. Du solltest Dir aber bewusst sein, dass Du auf den viertältesten
  Commit verweisen musst, also den übergeordneten Commit, den Du ändern
  möchtest.
</p>

<pre>
$ git rebase -i HEAD~3
</pre>

<p>
  Es ist wichtig, dass Du Dir bewusst bist, dass mit diesem Rebase-Befehl jeder
  Commit im Bereich <code>HEAD~3..HEAD</code> geändert wird, unabhängig davon, ob Du die Commit-Nachricht beziehungsweise
  den Schnappschuss änderst oder nicht. Der Rebase-Befehl sollte nie einen
  Commit beinhalten, der bereits an einen zentralen Server gepusht worden ist.
  Hältst Du Dich nicht daran, werden sich andere Entwickler über Dich ärgern
  oder wundern, weil es jetzt eine alternative Version der gleichen Änderung
  gibt.
</p>

<p>
  Wenn Du den Befehl ausführst, erhältst Du eine Reihe von Commits in Deinem
  Texteditor. Das könnte in etwa folgendermaßen aussehen:
</p>

<pre>
pick f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file

# Rebase 710f0f8..a5f4a0d onto 710f0f8
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#
</pre>

<p>
  Vielleicht ist es Dir schon aufgefallen, die Commits werden genau in der
  umgekehrten Reihenfolge dargestellt, wie sie der <code>log</code> Befehl ausgegeben hätte. Wenn Du also den Befehl <code>log</code> ausführst, erhält man in etwa die folgende Ausgabe:
</p>

<pre>
$ git log --pretty=format:"%h %s" HEAD~3..HEAD
a5f4a0d added cat-file
310154e updated README formatting and added blame
f7f3f6d changed my name a bit
</pre>

<p>
  Siehst Du den Unterschied? Es ist genau die umgekehrte Reihenfolge. Ein
  interaktiver Rebase wird nach einem festen Schema, einer Art Skript,
  durchgeführt und der Texteditor zeigt Dir an, wie dieses Skript genau
  ablaufen wird. Der Rebase startet bei dem Commit, der in der Kommandozeile
  angegeben wird (<code>HEAD~3</code>) und führt die Änderungen, die durch jeden Commit hinzukommen, von oben nach
  unten aus. Das bedeutet, dass anstatt des neuesten, der älteste Commit ganz
  oben steht, weil dieser der erste Commit ist, der bearbeitet wird.
</p>

<p>
  Du musst das Skript so anpassen, dass es an jedem Commit anhält, den Du
  ändern möchtest. Dazu musst Du bei jedem Commit, an dem das Skript anhalten
  soll, das Wort „pick“ mit dem Wort „edit“ ersetzen. Um zum Beispiel die
  drittälteste Commit-Nachricht zu ändern, müssen die Änderungen am Skript in
  etwa folgendermaßen aussehen:
</p>

<pre>
edit f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file
</pre>

<p>
  Nachdem Du das Skript gespeichert und den Editor beendet hast, setzt Git nun
  alle Änderungen bis zum letzten Commit der Liste zurück und zeigt danach in
  der Kommandozeile in etwa Folgendes an:
</p>

<pre>
$ git rebase -i HEAD~3
Stopped at 7482e0d... updated the gemspec to hopefully work better
You can amend the commit now, with

       git commit --amend

Once you’re satisfied with your changes, run

       git rebase --continue
</pre>

<p>
  Diese Anweisungen zeigen Dir sehr genau, was Du zu tun hast. Gib also den
  folgenden Befehl ein:
</p>

<pre>
$ git commit --amend
</pre>

<p>
  Im sich öffnenden Texteditor kannst Du jetzt die Commit-Nachricht ändern und
  danch wieder schließen. Danach führst Du folgenden Befehl aus:
</p>

<pre>
$ git rebase --continue
</pre>

<p>
  Der letzte Befehl speichert die letzten beiden Commits automatisch im
  Repository und der Rebase ist danach abgeschlossen. Wenn Du in einer weiteren
  Zeile „pick“ mit „edit“ ersetzt hast, kannst Du die oben dargestellten
  Schritte entsprechend noch einmal ausführen. Git wird nach jedem Commit
  anhalten und Dir die Möglichkeit bieten, den Commit anzupassen. Danach kannst
  Du Git auffordern, den Rebase fortzusetzen (<code>git rebase --continue</code>).
</p>

<h3>Reihenfolge von Commits verändern</h3>

<p>
  Mit einem interaktiven Rebase kannst Du ebenso die Reihenfolge von Commits
  ändern oder sogar komplette Commits löschen. Um den Commit „added cat-file“
  zu löschen und die Reihenfolge der beiden anderen Commits zu ändern, kannst
  Du das vorhandene Skript
</p>

<pre>
pick f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file
</pre>

<p>folgendermaßen ändern:</p>

<pre>
pick 310154e updated README formatting and added blame
pick f7f3f6d changed my name a bit
</pre>

<p>
  Nach dem Speichern und Verlassen des Editors, setzt Git nun alle Änderungen
  bis zum letzten Commit der Liste zurück, speichert den Commit <code>310154e</code>, danach den Commit <code>310154e</code> und beendet danach den Rebase. Das Ergebnis: Der Commit „added cat-file“ ist
  aus der Historie verschwunden und die Reihenfolge der beiden restlichen
  Commits ist getauscht.
</p>

<h3>Mehrere Commits zusammenfassen</h3>

<p>
  Man kann mit einem interaktiven Rebase auch mehrere Commits zu einem
  einzelnen Commit zusammenfassen. Im Skript der Rebase-Nachricht steht eine
  Anleitung, wie Du dazu vorgehen musst:
</p>

<pre>
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#
</pre>

<p>
  Wenn Du statt „pick“ oder „edit“, den Befehl „squash“ angibst, führt Git
  beide Commits zu einem gemeinsamen Commit zusammen und bietet Dir die
  Möglichkeit, die Commit-Nachricht ebenso entsprechend zu verheiraten. Wenn Du
  also aus den drei Commits einen einzelnen Commit machen willst, muss Dein
  Skript folgendermaßen aufgebaut sein:
</p>

<pre>
pick f7f3f6d changed my name a bit
squash 310154e updated README formatting and added blame
squash a5f4a0d added cat-file
</pre>

<p>
  Nach dem Speichern und Beenden des Editors, führt Git alle drei Änderungen zu
  einem einzelnen Commit zusammen und öffnet einen Texteditor, der alle drei
  Commit-Nachrichten enthält:
</p>

<pre>
# This is a combination of 3 commits.
# The first commit's message is:
changed my name a bit

# This is the 2nd commit message:

updated README formatting and added blame

# This is the 3rd commit message:

added cat-file
</pre>

<p>
  Du kannst nun die Commit-Nachricht entsprechend anpassen oder auch
  entsprechend dem vorgeschlagenen Ergebnis belassen. Wenn Du die
  Commit-Nachricht speicherst, hast Du danach nur noch einen einzelnen Commit,
  der die letzten drei Commits beinhaltet, in Deiner Historie.
</p>

<h3>Aufsplitten eines einzelnen Commits</h3>

<p>
  Man kann mit Git einen einzelnen Commit auch aufsplitten. Das bedeutet, man
  setzt den ursprünglichen Commit zurück, fügt dann einen Teil der Änderungen
  zur Staging-Area hinzu und checkt das Ergebnis ein. Dies kann man unbegrenzt
  oft wiederholen und so einen einzelnen Commit in mehrere Commits aufteilen.
  Nehmen wir an, wir möchten den mittleren der beiden Commits aufteilen.
  Anstatt „updated README formatting and added blame“, möchten wir den Commit
  in folgende beiden Commits aufteilen: „updated README formatting“ soll das
  Thema des ersten Commits und „added blame“ soll das Thema des zweiten Commits
  sein. Dazu kannst Du das angezeigte Skript, welches Dir der Befehl <code>rebase -i</code> erzeugt, folgendermaßen anpassen:
</p>

<pre>
pick f7f3f6d changed my name a bit
edit 310154e updated README formatting and added blame
pick a5f4a0d added cat-file
</pre>

<p>
  Nach dem Speichern und Schließen des Editors, setzt Git die Änderungen
  entsprechend zurück und wendet den ersten (<code>f7f3f6d</code>) und zweiten (<code>310154e</code>) Commit an und wechselt danach zurück zur Kommandozeile. Jetzt hast Du die
  Möglichkeit den letzten Commit zurückzusetzen, ohne dass die Änderungen im
  Arbeitsverzeichnis zurückgesetzt werden. Das heißt, der Commit im Repository
  wird gelöscht, aber Deine Änderungen im Arbeitsverzeichnis gehen nicht
  verloren. Um dies durchzuführen, kannst Du den Befehl <code>git reset HEAD^</code> verwenden. Jetzt kannst Du die gewünschten Änderungen für den ersten Commit
  zur Staging-Area hinzufügen und danach einchecken. Diesen Vorgang kannst Du
  beliebig wiederholen, bis alle Änderungen eingecheckt sind. Wenn Du fertig
  bist, kannst Du den Rebase mit <code>git rebase --continue</code> fortsetzen beziehungsweise abschließen:
</p>

<pre>
$ git reset HEAD^
$ git add README
$ git commit -m 'updated README formatting'
$ git add lib/simplegit.rb
$ git commit -m 'added blame'
$ git rebase --continue
</pre>

<p>
  Git speichert dazu den letzten Commit (<code>a5f4a0d</code>) aus dem Skript im Repository. Das Resultat sieht in etwa folgendermaßen
  aus:
</p>

<pre>
$ git log -4 --pretty=format:"%h %s"
1c002dd added cat-file
9b29157 added blame
35cfb2b updated README formatting
f3cc40e changed my name a bit
</pre>

<p>
  Ich möchte Dich noch einmal darauf hinweisen, dass jede SHA-Prüfsumme von
  allen Commits aus der Liste geändert werden. Bitte stell also sicher, dass
  diese Commits in keinem öffentlichen Repository verfügbar sind.
</p>

<h3>Hol den Vorschlaghammer raus: filter-branch</h3>

<p>
  Es gibt noch eine weitere Möglichkeit, wie man die Historie nach seinen
  Wünschen anpassen kann. Diese wird oft angewandt, wenn man eine große Zahl
  von Commits automatisiert mit Hilfe eines Skripts anpassen will. Zum Beispiel
  kann man damit die E-Mail-Adresse in jedem Commit ändern oder auch eine Datei
  aus jedem Commit entfernen. Das Werkzeug dazu heißt <code>filter-branch</code>. Damit kann man einen riesigen Teil der Historie ändern. Man sollte diesen
  Befehl also nur verwenden, wenn das Projekt noch nicht weit verbreitet ist,
  oder andere Personen noch nicht damit begonnen haben an dem Projekt zu
  arbeiten (also auf Basis der bisherigen Historie neue Branches mit Commits
  erstellt wurden). Trotzdem kann dieses Werkzeug sehr nützlich sein. Ich
  möchte hier ein paar der Möglichkeiten dieses Werkzeugs vorstellen.
</p>

<p>Löschen einer Datei aus jedem Commit</p>

<p>
  Dieses Szenario tritt sogar relativ häufig auf. Nehmen wir einmal an, jemand
  fügt gedankenlos eine große binäre Datei mit <code>git add .</code> zum Repository dazu und diese soll aber in keinem der Commits enthalten
  sein. Oder Du hast aus Versehen eine Datei, welche ein Passwort enthält, zum
  Repository hinzugefügt und möchtest dieses Repository nun veröffentlichen. <code>filter-branch</code> ist dann das Werkzeug Deiner Wahl, um die komplette Historie umzukrempeln.
  Um eine Datei mit dem Namen „passwords.txt“ aus der kompletten Historie zu
  löschen, kannst Du die Option <code>--tree-filter</code> verwenden:
</p>

<pre>
$ git filter-branch --tree-filter 'rm -f passwords.txt' HEAD
Rewrite 6b9b3cf04e7c5686a9cb838c3f36a8cb6a0fc2bd (21/21)
Ref 'refs/heads/master' was rewritten
</pre>

<p>
  Die Option <code>--tree-filer</code> führt den nachfolgenden Befehl nach jedem Auschecken eines Commits des
  Projekts aus und checkt danach das Ergebnis wieder ein. In diesem Beispiel
  wird die Datei „passwords.txt“ aus jedem Schnappschuss entfernt, unabhängig
  davon, ob sie existiert oder nicht. Ein anderes Beispiel wäre es, alle
  Backup-Dateien eines Texteditors aus dem Repository zu löschen. Dazu kann man
  in etwa den Befehl <code>git filter-branch -\-tree-filter "rm -f *~" HEAD</code> ausführen.
</p>

<p>
  Git informiert Dich über den Fortschritt dieses Vorgangs und Du siehst, wie
  jeder Commit angepasst wird und der Zeiger auf den Branch auf den letzten
  Commit gesetzt wird. Es ist empfehlenswert, diesen Befehl in einem Testzweig
  durchzuführen. Wenn das Ergebnis, wie gewünscht ausfällt, kann man danach den
  Branch master auf diesen Testzweig setzen. Wenn man an den Befehl <code>filter-branch</code> die Option <code>--all</code> anfügt, führt Git diesen Vorgang für jeden vorhandenen Zweig aus.
</p>

<p>Aus einem Unterverzeichnis das neue Wurzelverzeichnis machen</p>

<p>
  Wenn man zum Beispiel ein Projekt aus einem anderen Versionskontrollwerkzeug
  in Git importiert, gibt es dort oft Verzeichnisse, die in Git nicht relevant
  sind, zum Beispiel trunk, tags, usw.. Wenn man also das Unterverzeichnis <code>trunk</code> das neue Wurzelverzeichnis machen will, kann man dies mit Hilfe von <code>filter-branch</code> umsetzen:
</p>

<pre>
$ git filter-branch --subdirectory-filter trunk HEAD
Rewrite 856f0bf61e41a27326cdae8f09fe708d679f596f (12/12)
Ref 'refs/heads/master' was rewritten
</pre>

<p>
  Nach der Ausführung dieses Befehls ist das „trunk“ Verzeichnis das neue
  Arbeitsverzeichnis. Bei diesem Vorgang entfernt Git außerdem alle Commits,
  die nicht eine Änderung des „trunk“-Verzeichnisses beinhalten.
</p>

<p>E-Mail Adresse in jedem Commit ändern</p>

<p>
  Verflixt, es ist schon wieder passiert. Du hast vergessen, den Befehl <code>git config</code> auszuführen und Deinen Namen und E-Mail-Adresse zu setzen, bevor Du mit der
  Arbeit begonnen hast. Mit <code>filter-branch</code> kann man diesen Fehler einfach beheben. Man sollte nur darauf achten, dass
  man nur seine eigene E-Mail-Adresse ändert. Deshalb verwenden wir die Option <code>--commit-filter</code>:
</p>

<pre>
$ git filter-branch --commit-filter '
        if [ "$GIT_AUTHOR_EMAIL" = "schacon@localhost" ];
        then
                GIT_AUTHOR_NAME="Scott Chacon";
                GIT_AUTHOR_EMAIL="schacon@example.com";
                git commit-tree "$@";
        else
                git commit-tree "$@";
        fi' HEAD
</pre>

<p>
  Dieser Befehl durchforstet das Repository und ersetzt in jedem Commit, dessen
  E-Mail-Adresse des Autors „schacon@localhost“ lautet, mit der neuen
  E-Mail-Adresse „schacon@example.com“. Zusätzlich wird der Name des Autors
  geändert, falls dieser nicht vorher schon „Scott Chacon“ war. Auf Grund der
  Architektur, dass in Git in jedem Commit die SHA1-Prüfsumme des
  Vorgänger-Commits enthalten ist, ändert dieser Befehl jeden Commit in Deiner
  Historie. Die SHA1-Prüfsumme wird sich auch in allen Commits, die nicht die
  angegebene E-Mail-Adresse enthalten, verändern.
</p>

<h2>Mit Hilfe von Git debuggen</h2>

<p>
  Git bietet auch ein paar Werkzeuge, die den Debug-Vorgang bei einem Projekt
  unterstützen. Da Git so aufgebaut ist, dass es für nahezu jedes Projekt
  eingesetzt werden kann, sind diese Werkzeuge sehr generisch gehalten. Wenn
  gewisse Dinge schief laufen, können Dir aber diese Tools oft helfen, den Bug
  oder den Übeltäter zu finden.
</p>

<h3>Datei Annotation</h3>

<p>
  Wenn Du nach einem Bug in Deinem Code suchst und gerne wissen willst, wann
  und warum dieser zum ersten Mal auftrat, dann kann Dir das Werkzeug
  Datei-Annotation (engl. File Annotation) sicher weiterhelfen. Es kann Dir
  anzeigen, in welchem Commit die jeweilige Zeile einer Datei zuletzt geändert
  wurde. Wenn Du also feststellst, dass eine Methode beziehungsweise eine
  Funktion in Deinem Code nicht mehr das gewünschte Resultat liefert, kannst Du
  Dir die Datei mit <code>git blame</code> genauer ansehen. Nach Aufruf des Befehls zeigt Git Dir an, welche Zeile von
  welcher Person als letztes geändert wurde, inklusive Datum. Das folgende
  Beispiel verwendet die Option <code>-L</code>, um die Ausgabe auf die Zeilen 12 bis 22 einzuschränken:
</p>

<pre>
$ git blame -L 12,22 simplegit.rb
^4832fe2 (Scott Chacon  2008-03-15 10:31:28 -0700 12)  def show(tree = 'master')
^4832fe2 (Scott Chacon  2008-03-15 10:31:28 -0700 13)   command("git show #{tree}")
^4832fe2 (Scott Chacon  2008-03-15 10:31:28 -0700 14)  end
^4832fe2 (Scott Chacon  2008-03-15 10:31:28 -0700 15)
9f6560e4 (Scott Chacon  2008-03-17 21:52:20 -0700 16)  def log(tree = 'master')
79eaf55d (Scott Chacon  2008-04-06 10:15:08 -0700 17)   command("git log #{tree}")
9f6560e4 (Scott Chacon  2008-03-17 21:52:20 -0700 18)  end
9f6560e4 (Scott Chacon  2008-03-17 21:52:20 -0700 19)
42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 20)  def blame(path)
42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 21)   command("git blame #{path}")
42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 22)  end
</pre>

<p>
  In der ersten Spalte wird die Kurzform der SHA1-Prüfsumme des Commits
  angezeigt, in welchem diese Zeile zuletzt verändert wurde. Die nächsten
  beiden Spalten weisen auf den Autor des Commits und wann dieser verfasst
  wurde, hin. Auf diese Weise kannst Du leicht bestimmen, wer die jeweilige
  Zeile geändert hat und wann dies durchgeführt wurde. In den nächsten Spalten
  wird die Zeilennummer und der Inhalt der Zeile angezeigt. Die Zeilen mit der
  SHA-1-Prüfsumme <code>^4832fe2</code> weisen darauf hin, dass diese bereits im ersten Commit vorhanden waren. Das
  ist also der Commit, in dem die Datei „simplegit.rb“ zum Repository
  hinzugefügt wurde und die Zeilen deuten damit darauf hin, dass diese bisher
  nie geändert wurden. Das ist für Dich wahrscheinlich ein bisschen verwirrend,
  denn nun kennst Du bereits drei Möglichkeiten, wie Git mit dem Zeichen <code>^</code> einer SHA-Prüfsumme eine neue Bedeutung gibt. Aber in Zusammenhang
  mit&emsp14;&emsp14;<code>git blame</code> weist das Zeichen auf den eben geschilderten Sachverhalt hin.
</p>

<p>
  Eine weitere herausragende Eigenschaft von Git ist die Tatsache, dass es
  nicht per se das Umbenennen von Dateien verfolgt. Git speichert immer den
  jeweiligen Schnappschuss des Dateisystems und versucht erst danach zu
  bestimmen, welche Dateien umbenannt wurden. Das bietet Dir zum Beispiel die
  Möglichkeit herauszufinden, wie Code innerhalb des Repositorys hin und her
  verschoben wurde. Wenn Du also die Option <code>-C</code> an <code>git blame</code> anfügst, analysiert Git die angegebene Datei und versucht herauszufinden, ob
  und von wo bestimmte Codezeilen herkopiert wurden. Vor kurzem habe ich ein
  Refactoring an einer Datei mit dem Namen <code>GITServerHandler.m</code> durchgeführt. Dabei habe ich diese Datei in mehrere Dateien aufgeteilt, eine
  davon war <code>GITPackUpload.m</code>. Wenn ich jetzt <code>git blame</code> mit der Option <code>-C</code> auf die Datei <code>GITPackUpload.m</code> ausführe, erhalte ich eine Ausgabe mit den Codezeilen, von denen das
  Ergebnis ursprünglich stammt:
</p>

<pre>
$ git blame -C -L 141,153 GITPackUpload.m
f344f58d GITServerHandler.m (Scott 2009-01-04 141)
f344f58d GITServerHandler.m (Scott 2009-01-04 142) - (void) gatherObjectShasFromC
f344f58d GITServerHandler.m (Scott 2009-01-04 143) {
70befddd GITServerHandler.m (Scott 2009-03-22 144)         //NSLog(@"GATHER COMMI
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 145)
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 146)         NSString *parentSha;
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 147)         GITCommit *commit = [g
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 148)
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 149)         //NSLog(@"GATHER COMMI
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 150)
56ef2caf GITServerHandler.m (Scott 2009-01-05 151)         if(commit) {
56ef2caf GITServerHandler.m (Scott 2009-01-05 152)                 [refDict setOb
56ef2caf GITServerHandler.m (Scott 2009-01-05 153)
</pre>

<p>
  Das ist enorm hilfreich. Für gewöhnlich erhältst Du damit als ursprünglichen
  Commit, den Commit, von welchem der Code kopiert wurde, da dies der Zeitpunkt
  war, bei dem diese Zeilen zum ersten Mal angefasst wurden. Git zeigt Dir den
  ursprünglichen Commit, in dem Du die Zeilen verfasst hast, sogar an, wenn es
  sich dabei um eine andere Datei handelt.
</p>

<h3>Das Bisect Werkzeug – Binäre Suche###</h3>

<p>
  <code>git blame</code> kann Dir sehr weiterhelfen, wenn Du bereits weißt, an welcher Stelle das
  Problem liegt. Wenn Du aber nicht weißt, warum gewisse Dinge schief laufen,
  und es gibt inzwischen Dutzende oder Hunderte von Commits seit dem letzten
  funktionierenden Stand, dann solltest Du <code>git bisect</code> als Hilfestellung verwenden. Der <code>bisect</code> Befehl führt eine binäre Suche durch die Commit-Historie durch und hilft Dir
  auf schnelle Art und Weise die Commits zu bestimmen, die eventuell für das
  Problem verantwortlich sind.
</p>

<p>
  Nehmen wir zum Beispiel an, dass Du gerade eben Deinen Code in einer
  Produktivumgebung veröffentlicht hast und auf einmal bekommst Du zahlreiche
  Fehlerberichte über Probleme, die in Deiner Entwicklungsumgebung nicht
  aufgetreten sind. Du kannst Dir auch keinen Reim darauf bilden, warum der
  Code so reagiert. Nachdem Du Dich noch einmal näher mit Deinem Code
  beschäftigt hast, stellst Du fest, dass Du die Fehlerwirkung reproduzieren
  kannst, aber Dir ist es immer noch ein Rätsel, was genau schief läuft. Wenn
  Du vor einem solchen Problem stehst, hilft Dir es bestimmt, wenn Du die
  Historie in mehrere Teile aufspaltest (engl. bisect: halbieren, zweiteilen).
  Als erstes startest Du mit dem Befehl <code>git bisect start</code>. Danach gibst Du mit dem Befehl <code>git bisect bad</code> an, dass der derzeit ausgecheckte Commit den Fehler aufweist. Jetzt braucht
  Git noch die Information, in welchem Commit das Problem noch nicht
  aufgetreten ist. Dazu verwendest Du den Befehl <code>git bisect good [good_commit]</code>:
</p>

<pre>
$ git bisect start
$ git bisect bad
$ git bisect good v1.0
Bisecting: 6 revisions left to test after this
[ecb6e1bc347ccecc5f9350d878ce677feb13d3b2] error handling on repo
</pre>

<p>
  Nach Ausführen des letzten Befehls, zeigt Git Dir als Erstes an, dass in etwa
  12 Commits zwischen der letzten guten Revision (v1.0) und der aktuellen,
  fehlerhaften Revision liegen. Auf Basis dieser Information hat Git Dir den
  mittleren Commit ausgecheckt. Jetzt hast Du die Möglichkeit Deine Tests auf
  Basis des ausgecheckten Stands durchzuführen, um herauszufinden, ob in diesem
  Commit der Fehler bereits bestand. Wenn der Fehler hier bereits auftritt,
  dann wurde er in diesem oder in einem der früheren Commits eingefügt. Wenn
  der Fehler hier noch nicht auftritt, dann wurde er in einem der späteren
  Commits eingeschleppt. In unserem Beispiel nehmen wir an, dass in diesem
  Commit der Fehler noch nicht bestand. Das geben wir mit dem Befehl <code>git bisect good</code> an und fahren fort:
</p>

<pre>
$ git bisect good
Bisecting: 3 revisions left to test after this
[b047b02ea83310a70fd603dc8cd7a6cd13d15c04] secure this thing
</pre>

<p>
  Git hat Dir jetzt einen weiteren Commit ausgecheckt, und zwar wieder den
  mittleren Commit zwischen dem letzten Stand im Repository und dem mittleren
  Commit aus der letzten Runde. Hier nehmen wir an, dass Du nach Deinen
  durchgeführten Tests feststellst, dass in diesem Commit der Fehler bereits
  vorhanden ist. Das müssen wir Git über <code>git bisect bad</code> mitteilen:
</p>

<pre>
$ git bisect bad
Bisecting: 1 revisions left to test after this
[f71ce38690acf49c1f3c9bea38e09d82a5ce6014] drop exceptions table
</pre>

<p>
  Git checkt wieder den nächsten mittleren Commit aus und wir stellen fest,
  dass dieser in Ordnung ist. Ab jetzt hat Git alle notwendigen Informationen
  um festzustellen, in welchem Commit der Fehler eingebaut wurde. Git zeigt Dir
  dazu die SHA-1-Prüfsumme des ersten fehlerhaften Commits an. Zusätzlich gibt
  es noch weitere Commit-Informationen und welche Dateien in diesem Commit
  geändert wurden, an. Das sollte Dir nun helfen, den Fehler näher zu
  bestimmen:
</p>

<pre>
$ git bisect good
b047b02ea83310a70fd603dc8cd7a6cd13d15c04 is first bad commit
commit b047b02ea83310a70fd603dc8cd7a6cd13d15c04
Author: PJ Hyett &lt;pjhyett@example.com&gt;
Date:   Tue Jan 27 14:48:32 2009 -0800

    secure this thing

:040000 040000 40ee3e7821b895e52c1695092db9bdc4c61d1730
f24d3c6ebcfc639b1a3814550e62d60b8e68a8e4 M  config
</pre>

<p>
  Wenn Du fertig mit der Fehlersuche bist, solltest Du den Befehl <code>git bisect reset</code> ausführen. Dies checkt den ursprünglichen Stand aus, den Du ausgecheckt
  hattest, bevor Du mit der Fehlersuche begonnen hast:
</p>

<pre>
$ git bisect reset
</pre>

<p>
  Wie Du vielleicht gesehen hast, ist dieser Befehl ein mächtiges Werkzeug, um
  Hunderte von Commits auf schnelle Art und Weise nach einem bestimmten Fehler
  zu durchsuchen. Besonders nützlich ist es, wenn Du ein Skript hast, welches
  mit dem Fehlercode Null beendet, wenn das Projekt in Ordnung ist und mit
  einem Fehlercode größer Null, wenn das Projekt Fehler enthält. Wenn Dir ein
  solches Skript zur Verfügung steht, kannst Du den bisher manuell
  durchgeführten Vorgang auch automatisieren. Wie im vorigen Beispiel musst Du
  Git den zuletzt fehlerfreien Commit und den fehlerhaften Commit angeben. Als
  verkürzte Schreibweise kannst Du an den Befehl <code>bisect start</code> den fehlerhaften und den fehlerfreien Commit angeben:
</p>

<pre>
$ git bisect start HEAD v1.0
$ git bisect run test-error.sh
</pre>

<p>
  Wenn Du die untere, genannte Zeile ausführst, führt Git automatisch nach
  jedem Auscheckvorgang das Skript <code>test-error.sh</code> aus und zwar solange bis es den Commit findet, der als erstes ein
  fehlerhaftes Ergebnis liefert. Statt eines Skripts kannst Du natürlich auch <code>make</code> oder <code>make tests</code> oder eine beliebige, andere Testumgebung starten.
</p>

<h2>Submodule</h2>

<p>
  Oft möchte man während der Arbeit an einem Projekt ein weiteres Projekt darin
  einbinden und verwenden. Das kann zum Beispiel eine Bibliothek von einer
  anderen Firma oder vielleicht auch eine selbstentwickelte Bibliothek sein. In
  einem solchen Szenario tritt dann meistens folgendes Problem auf: Die zwei
  Projekte sollen unabhängig voneinander entwickelt werden können, aber
  trotzdem soll es möglich sein, das eine Projekt im anderen zu verwenden.
</p>

<p>
  Dazu ein Beispiel. Nehmen wir an, Du entwickelst gerade eine Webseite, die
  unter anderem einen Atom-Feed zur Verfügung stellen soll. Anstatt den
  notwendigen Code zur Auslieferung des Atom-Feeds selber zu schreiben,
  entscheidest Du Dich für eine geeignete Bibliothek. Dann wirst Du
  wahrscheinlich den Code in Dein Projekt einbinden müssen, zum Beispiel durch
  eine CPAN-Installation oder ein Ruby-Gem oder durch Kopieren des Quellcodes
  in das Arbeitsverzeichnis Deines Projekts. Das Problem beim Einbinden einer
  Bibliothek ist, dass es schwierig ist, diese an die eigene Bedürfnisse
  anzupassen. Noch schwieriger gestaltet sich dann die Veröffentlichung des
  Projekts, da man sicherstellen muss, dass jeder der die Software verwendet,
  auf die Bibliothek zugreifen kann. Wenn man die Bibliothek im eigenen Projekt
  projektspezifisch anpasst, hat man meist ein Problem, wenn man eine neue
  Version der Bibliothek einspielen will.
</p>

<p>
  Git löst dieses Problem mit Hilfe von Submodulen. Mit Hilfe von Submodulen
  kann man innerhalb eines Git-Repositorys ein weiteres Git-Repository in einem
  Unterverzeichnis verwalten. Daraus entsteht der Vorteil, dass man ein anderes
  Repository in das eigene Projekt klonen kann und die Commits der jeweiligen
  Projekte trennen kann.
</p>

<h3>Die ersten Schritte mit Submodulen</h3>

<p>
  Nehmen wir einmal an, dass Du die Rack-Bibliothek (eine
  Ruby-Gateway-Schnittstelle für Webserver) zu Deinem Projekt hinzufügen
  willst. Dabei möchtest Du Deine eigenen Änderungen an dieser Bibliothek
  nachverfolgen, aber auch weiterhin Änderungen von den
  Rack-Bibliothek-Entwicklern verwalten und zusammenmergen. Das erste was Du
  dazu tun musst, ist das Repository der Rack Bibliothek in ein
  Unterverzeichnis Deines Projekts zu klonen. Diesen Vorgang kannst Du mit
  Hilfe des Befehls <code>git submodule add</code> ausführen:
</p>

<pre>
$ git submodule add git://github.com/chneukirchen/rack.git rack
Initialized empty Git repository in /opt/subtest/rack/.git/
remote: Counting objects: 3181, done.
remote: Compressing objects: 100% (1534/1534), done.
remote: Total 3181 (delta 1951), reused 2623 (delta 1603)
Receiving objects: 100% (3181/3181), 675.42 KiB | 422 KiB/s, done.
Resolving deltas: 100% (1951/1951), done.
</pre>

<p>
  Innerhalb Deines Projekts befindet sich nun im Unterverzeichnis <code>rack</code> das komplette Rack-Projekt. Man kann jetzt in diesem Verzeichnis Änderungen
  vornehmen und ein eigenes Remote-Repository mit Schreibrechten, zu welchem
  man pushen kann, hinzufügen. Ebenso ist es möglich, Änderungen von den
  Rack-Entwicklern in sein Repository zu laden und diese mit den eigenen
  Ergebnissen zu mergen. Im Prinzip kann man innerhalb eines Submoduls die
  gleichen Vorgänge, wie in einem normalen Repository ausführen. Vorher müssen
  wir aber noch ein paar weitere Dinge zu Submodulen besprechen. Wenn Du gleich
  nach dem Hinzufügen des Submoduls, den Befehl <code>git status</code> ausführst, wirst Du gleich zwei Dinge bemerken:
</p>

<pre>
$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD &lt;file&gt;..." to unstage)
#
#      new file:   .gitmodules
#      new file:   rack
#
</pre>

<p>
  Als erstes wird Dir die neue Datei <code>.gitmodules</code> auffallen. Das ist eine Konfigurationsdatei, welche die Zuordnung der URL
  des geklonten Projekts und dem lokalen Unterverzeichnis, in welches das
  Projekt geklont wurde, festlegt:
</p>

<pre>
$ cat .gitmodules
[submodule "rack"]
      path = rack
      url = git://github.com/chneukirchen/rack.git
</pre>

<p>
  Wenn Du mehrere Submodule in einem Projekt verwaltest, werden auch mehrere
  Einträge in dieser Datei auftauchen. Dabei ist es wichtig zu wissen, dass
  diese Datei zusammen mit all den anderen Dateien aus Deinem Projekt, ebenso
  in die Versionskontrolle aufgenommen wird, ähnlich wie die Datei <code>.gitignore</code>. Die Datei wird so wie der Rest Deines Projekts gepusht und gepullt. Dadurch
  wissen andere Personen, die Dein Projekt klonen, von welchem Ort sie die
  Submodule erhalten können.
</p>

<p>
  Die zweite Auffälligkeit bei der Ausgabe von <code>git status</code> ist der Eintrag rack. Wenn Du auf diesen Eintrag ein <code>git diff</code> durchführst, erhält man in etwa die folgende, interessante Ausgabe:
</p>

<pre>
$ git diff --cached rack
diff --git a/rack b/rack
new file mode 160000
index 0000000..08d709f
--- /dev/null
+++ b/rack
@@ -0,0 +1 @@
+Subproject commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433
</pre>

<p>
  Obwohl <code>rack</code> ein Unterverzeichnis in Deinem Arbeitsverzeichnis ist, erkennt Git dieses
  Verzeichnis als Submodul und verfolgt die Änderungen innerhalb dieses
  Verzeichnisses nicht, wenn Git nicht innerhalb dieses Verzeichnisses
  aufgerufen wird. Stattdessen erfasst Git, welcher Commit in diesem Repository
  ausgecheckt ist. Wenn Du also Änderungen in diesem Unterverzeichnis
  durchführst und eincheckst, kann das Superprojekt erkennen, dass sich der
  aktuelle HEAD von diesem Projekt geändert hat. Das Superprojekt kann sich
  jetzt diesen Commit merken. Auf diese Art und Weise ist es möglich, den
  kompletten Zustand des Projekts mit allen Projekten, die als Submodul
  hinzugefügt wurden, zu reproduzieren. In der Git-Terminologie wird das
  Projekt, welches eines oder mehrere Submodule enthält, als Superprojekt
  bezeichnet.
</p>

<p>
  Dabei muss man sich folgender Eigenschaft bewusst sein: Git verwaltet den
  exakten Commit, der gerade ausgecheckt ist und nicht etwa den Branch oder
  eine andere Referenz. Git kann also zum Beispiel nicht speichern, dass der
  aktuelle Stand im Branch <code>master</code> enthalten ist.
</p>

<p>
  Wenn Du Dein Projekt das erste Mal eincheckst, erhältst Du in etwa folgende
  Ausgabe:
</p>

<pre>
$ git commit -m 'first commit with submodule rack'
[master 0550271] first commit with submodule rack
 2 files changed, 4 insertions(+), 0 deletions(-)
 create mode 100644 .gitmodules
 create mode 160000 rack
</pre>

<p>
  Der Mode 160000, der für den rack-Eintrag gilt, ist ein spezieller Mode in
  Git. Er bedeutet in etwa, dass man einen Commit als Verzeichnis-Eintrag in
  Git verwaltet und damit nicht wie normalerweise ein Verzeichnis oder eine
  Datei.
</p>

<p>
  Das Verzeichnis <code>rack</code> kann man wie ein separates Projekt behandeln und verwenden. Und von Zeit zur
  Zeit aktualisiert man auch das Superprojekt und speichert damit die letzte
  Commit-ID des Unterprojekts. Jedes Git-Kommando arbeitet unabhängig in einem
  der beiden Unterverzeichnisse:
</p>

<pre>
$ git log -1
commit 0550271328a0038865aad6331e620cd7238601bb
Author: Scott Chacon &lt;schacon@gmail.com&gt;
Date:   Thu Apr 9 09:03:56 2009 -0700

    first commit with submodule rack
$ cd rack/
$ git log -1
commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433
Author: Christian Neukirchen &lt;chneukirchen@gmail.com&gt;
Date:   Wed Mar 25 14:49:04 2009 +0100

    Document version change
</pre>

<h3>Klonen eines Projekts mit den dazugehörigen Submodulen</h3>

<p>
  Als nächstes klonen wir ein Projekt, welches ein Submodul verwendet. Wenn man
  ein solches Projekt klont, werden die entsprechenden Verzeichnisse, welche
  ein Submodul enthalten, erstellt. Allerdings enthalten diese Verzeichnisse
  noch keinen Inhalt:
</p>

<pre>
$ git clone git://github.com/schacon/myproject.git
Initialized empty Git repository in /opt/myproject/.git/
remote: Counting objects: 6, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 6 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (6/6), done.
$ cd myproject
$ ls -l
total 8
-rw-r--r--  1 schacon  admin   3 Apr  9 09:11 README
drwxr-xr-x  2 schacon  admin  68 Apr  9 09:11 rack
$ ls rack/
$
</pre>

<p>
  Das Verzeichnis <code>rack</code> wurde zwar erzeugt, aber es ist leer. Deshalb musst Du zwei Dinge ausführen:
  <code>git submodule init</code>, damit wird die Datei für die lokale Konfiguration initialisiert. Und <code>git submodule update</code>, welches die gesamten Daten des Projekts von der im <code>.gitmodules</code> angegebenen Quelle holt und den entsprechenden Commit, welcher im
  Superprojekt hinterlegt ist, auscheckt:
</p>

<pre>
$ git submodule init
Submodule 'rack' (git://github.com/chneukirchen/rack.git) registered for path 'rack'
$ git submodule update
Initialized empty Git repository in /opt/myproject/rack/.git/
remote: Counting objects: 3181, done.
remote: Compressing objects: 100% (1534/1534), done.
remote: Total 3181 (delta 1951), reused 2623 (delta 1603)
Receiving objects: 100% (3181/3181), 675.42 KiB | 173 KiB/s, done.
Resolving deltas: 100% (1951/1951), done.
Submodule path 'rack': checked out '08d709f78b8c5b0fbeb7821e37fa53e69afcf433'
</pre>

<p>
  Nach Ausführung der beiden Befehle befindet sich das Verzeichnis <code>rack</code> in genau dem gleichen Zustand, wie wir es ursprünglich eingecheckt haben.
  Wenn ein anderer Entwickler Änderungen am Rack-Code durchführt, diese
  eincheckt und Du diese dann pullst und mergst, erhält man einen etwas
  seltsamen Zustand:
</p>

<pre>
$ git merge origin/master
Updating 0550271..85a3eee
Fast forward
 rack |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
[master*]$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add &lt;file&gt;..." to update what will be committed)
#   (use "git checkout -- &lt;file&gt;..." to discard changes in working directory)
#
#      modified:   rack
#
</pre>

<p>
  Der Merge, der gerade durchgeführt worden ist, ist eigentlich nur eine
  Aktualisierung des Zeigers auf einen neuen Commit des Submoduls. Der
  eigentliche Inhalt des Submodul-Verzeichnis wurde allerdings nicht
  aktualisiert. Das sieht dann so aus, als gäbe es noch nicht eingecheckte
  Dateien innerhalb Deines Arbeitsverzeichnis:
</p>

<pre>
$ git diff
diff --git a/rack b/rack
index 6c5e70b..08d709f 160000
--- a/rack
+++ b/rack
@@ -1 +1 @@
-Subproject commit 6c5e70b984a60b3cecd395edd5b48a7575bf58e0
+Subproject commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433
</pre>

<p>
  Dieser Zustand tritt auf, weil der Zeiger auf den Commit im Submodul derzeit
  nicht der Commit ist, welcher im Submodul ausgecheckt ist. Um dies zu
  beheben, muss man den Befehl <code>git submodule update</code> erneut ausführen:
</p>

<pre>
$ git submodule update
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 1), reused 2 (delta 0)
Unpacking objects: 100% (3/3), done.
From git@github.com:schacon/rack
   08d709f..6c5e70b  master     -&gt; origin/master
Submodule path 'rack': checked out '6c5e70b984a60b3cecd395edd5b48a7575bf58e0'
</pre>

<p>
  Dieser Update muss jedes Mal ausgeführt werden, wenn man das Superprojekt
  pullt und dort ein Änderung in einem Submodul enthalten ist. Es ist
  vielleicht ein wenig merkwürdig, aber es funktioniert.
</p>

<p>
  Häufig tritt beim Arbeiten mit Submodulen ein Problem bei folgendem Szenario
  auf: Ein Entwickler führt Änderungen in einem Submodul durch, checkt diese
  ein, vergisst aber diese Änderungen zum zentralen Server zu pushen. Wenn dann
  im Superprojekt die Änderung des Submoduls ebenso eingecheckt wird und dieses
  dann gepusht wird, tritt ein Problem auf. Wenn jetzt andere Entwickler den
  neuen Stand des Superprojekts holen und den Befehl <code>git submodule update</code> ausführen, erhalten sie eine Fehlermeldung, dass der entsprechend
  referenzierte Commit von dem Submodul nicht gefunden werden konnte. Das
  passiert weil dieser Commit bei dem zweiten Entwickler noch gar nicht
  existiert. Wenn ein solcher Fall auftritt, erhält man in etwa folgende
  Fehlermeldung:
</p>

<pre>
$ git submodule update
fatal: reference isn’t a tree: 6c5e70b984a60b3cecd395edd5b48a7575bf58e0
Unable to checkout '6c5e70b984a60b3cecd395edd5ba7575bf58e0' in submodule path 'rack'
</pre>

<p>
  Dann kann man allerdings herausfinden, wer zum letzten Mal eine Änderung
  eingecheckt hat:
</p>

<pre>
$ git log -1 rack
commit 85a3eee996800fcfa91e2119372dd4172bf76678
Author: Scott Chacon &lt;schacon@gmail.com&gt;
Date:   Thu Apr 9 09:19:14 2009 -0700

    added a submodule reference I will never make public. hahahahaha!
</pre>

<p>
  Dann kannst Du diesem Entwickler eine E-Mail schreiben und ihn auf seinen
  Fehler aufmerksam machen.
</p>

<h3>Superprojekte</h3>

<p>
  In manchen großen Projekten möchten die Entwickler die Arbeit in
  verschiedenen Verzeichnissen aufteilen, sodass das jeweilige Team in diesen
  Verzeichnissen arbeiten kann. Man trifft diese Vorgehensweise häufig an, wenn
  ein Team gerade von CVS oder Subversion nach Git gewechselt hat, im alten
  System ein Modul oder eine Sammlung von solchen Unterverzeichnissen gebildet
  hat und diesen Arbeitsablauf weiterhin verwenden möchte.
</p>

<p>
  In Git kann man diese Vorgehensweise gut abbilden, indem man für jedes
  Unterverzeichnis ein neues Git-Repository erzeugt. Zusätzlich kann man dann
  ein Superprojekt erzeugen und die ganzen Git-Repositorys als Submodul
  hinzufügen. Ein Vorteil dabei ist, dass man mit Hilfe von Tags und Branches
  im Superprojekt das Verhältnis der einzelnen Submodule zueinander festhalten
  kann.
</p>

<h3>Häufige Probleme mit Submodulen</h3>

<p>
  Die Arbeit mit Submodulen verläuft jedoch nicht immer reibungslos. Man muss
  verhältnismäßig gut aufpassen, wenn man in einem Submodul-Verzeichnis
  arbeitet. Wenn man nämlich den Befehl <code>git submodule update</code> ausführt, checkt Git den entsprechenden Zustand des Commits aus, aber checkt
  dabei keinen Branch aus. Diesen Zustand nennt man auch <code>detached HEAD</code>. Das bedeutet, dass die Datei HEAD direkt auf einen Commit zeigt und nicht,
  wie sonst üblich, auf eine symbolische Referenz, also zum Beispiel auf einen
  Branch. Das Problem dabei ist, dass man normalerweise in einem solchen
  Zustand nicht weiterarbeiten möchte, weil es sehr leicht vorkommen kann, dass
  Änderungen verloren gehen. Wenn man also den Befehl <code>git submodule update</code> aufruft, dann einen Commit in dem entsprechenden Submodul-Verzeichnis
  ausführt, ohne davor einen Branch auszuchecken, und dann noch einmal <code>git submodule update</code> im Superprojekt aufruft, ohne dass man die Änderungen im Submodul im
  Superprojekt eingecheckt hat, verliert man die ganzen Änderungen, ohne dass
  Git einen darauf vorher hinweist. Tatsächlich ist es so, dass die Änderungen
  nicht verloren gehen, aber es gibt keinen Branch, der auf die entsprechenden
  Commits hinzeigt, und damit kann es schwierig werden, die entsprechenden
  Commits wiederherzustellen beziehungsweise sichtbar zu machen.
</p>

<p>
  Um dieses Problem zu vermeiden, sollte man also immer in dem
  Submodul-Verzeichnis einen neuen Branch mit <code>git checkout -b work</code> oder auf eine andere Art und Weise erzeugen. Wenn man dann wieder das
  Aktualisieren des Submoduls ausführt, wird Git wieder den ursprünglichen
  Commit auschecken, allerdings hat man jetzt mit dem Branch einen Zeiger auf
  die neuen Commits und man kann sie leicht wieder auschecken.
</p>

<p>
  Wenn ein Projekt ein Submodul enthält und man im Superprojekt zwischen
  einzelnen Branches hin und her wechseln möchte, kann sich das manchmal auch
  schwierig gestalten. Wenn man zum Beispiel einen neuen Branch erzeugt, in
  diesem dann ein Submodul hinzufügt und dann wieder in den ursprünglichen
  Branch, welcher das Submodul noch nicht enthält, zurückwechselt, hat man im
  Arbeitsverzeichnis immer noch das Submodul-Verzeichnis, welches auch so
  dargestellt wird, als ob es von Git noch nicht verfolgt wird:
</p>

<pre>
$ git checkout -b rack
Switched to a new branch "rack"
$ git submodule add git@github.com:schacon/rack.git rack
Initialized empty Git repository in /opt/myproj/rack/.git/
...
Receiving objects: 100% (3184/3184), 677.42 KiB | 34 KiB/s, done.
Resolving deltas: 100% (1952/1952), done.
$ git commit -am 'added rack submodule'
[rack cc49a69] added rack submodule
 2 files changed, 4 insertions(+), 0 deletions(-)
 create mode 100644 .gitmodules
 create mode 160000 rack
$ git checkout master
Switched to branch "master"
$ git status
# On branch master
# Untracked files:
#   (use "git add &lt;file&gt;..." to include in what will be committed)
#
#      rack/
</pre>

<p>
  In diesem Fall muss man das Verzeichnis entweder an einen anderen Ort
  verschieben oder löschen. Im letzteren Fall muss man aber wieder das Submodul
  komplett klonen, wenn man in den anderen Zweig zurückwechselt. Außerdem kann
  man dabei lokale Änderungen zunichte machen oder Zweige, welche man noch
  nicht gepusht hat, verlieren.
</p>

<p>
  Die letzte Falle, in die viele Leute tappen, tritt auf, wenn man bereits
  vorhandene Verzeichnisse in Submodule umwandeln will. Wenn man also Dateien,
  die bereits von Git verwaltet werden, entfernen und in ein entsprechendes
  Submodul verschieben möchte, muss man vorsichtig sein. Ansonsten können
  schwer zu behebende Probleme mit Git auftreten. Nehmen wir zum Beispiel an,
  dass Du die Dateien vom Rack-Projekt in ein Unterverzeichnis Deines Projekts
  abgelegt hast und diese jetzt aber in ein Submodul verschieben möchtest. Wenn
  Du das Unterverzeichnis einfach löschst und dann den Befehl <code>submodule add</code> ausführst, zeigt Dir Git folgende Fehlermeldung an:
</p>

<pre>
$ rm -Rf rack/
$ git submodule add git@github.com:schacon/rack.git rack
'rack' already exists in the index
</pre>

<p>
  Man muss dann das Verzeichnis <code>rack</code> erst aus der Staging-Area entfernen. Danach kann man dann das Submodul
  erzeugen:
</p>

<pre>
$ git rm -r rack
$ git submodule add git@github.com:schacon/rack.git rack
Initialized empty Git repository in /opt/testsub/rack/.git/
remote: Counting objects: 3184, done.
remote: Compressing objects: 100% (1465/1465), done.
remote: Total 3184 (delta 1952), reused 2770 (delta 1675)
Receiving objects: 100% (3184/3184), 677.42 KiB | 88 KiB/s, done.
Resolving deltas: 100% (1952/1952), done.
</pre>

<p>
  Wenn wir jetzt annehmen, dass Du diesen Vorgang innerhalb eines Zweigs
  durchgeführt hast und jetzt auf einen anderen Zweig, in dem das Submodul noch
  nicht existiert hat und damit die Dateien noch ganz normal im Repository
  enthalten waren, wechselst, erhält Du folgenden Fehler:
</p>

<pre>
$ git checkout master
error: Untracked working tree file 'rack/AUTHORS' would be overwritten by merge.
</pre>

<p>
  Dann musst Du das Submodul-Verzeichnis <code>rack</code> an einen anderen Ort verschieben, bevor Du diesen Branch auschecken kannst:
</p>

<pre>
$ mv rack /tmp/
$ git checkout master
Switched to branch "master"
$ ls
README	rack
</pre>

<p>
  Wenn man dann wieder in den Zweig mit dem Submodul zurückwechseln will,
  erhält man ein leeres Verzeichnis <code>rack</code>. Um dieses zu befüllen, kannst Du entweder <code>git submodule update</code> ausführen oder Du kannst Deine Kopie von <code>/tmp/rack</code> wieder an den ursprünglichen Ort wiederherstellen.
</p>

<h2>Subtree Merging</h2>

<p>
  Nachdem wir neben den Vor- und Nachteilen beim Arbeiten mit Submodulen
  kennengelernt haben, möchte ich jetzt noch eine alternative Lösung zeigen,
  wie man ähnliche Probleme lösen kann. Wenn Git etwas zusammenführt, also
  mergt, analysiert es die Teile, die es mergen muss. Auf Basis dieser Analyse
  entscheidet Git sich für eine geeignete Merging-Methode. Wenn man zwei
  Branches mergt, dann verwendet Git automatisch die sogenannte
  Recursive-Strategie. Wenn man mehr als zwei Branches mergt, verwendet Git die
  sogenannte Octopus-Strategie. Diese Strategien werden automatisch für Dich
  gewählt, weil die Recursive-Strategie normalerweise sehr gut geeignet ist, um
  einen Drei-Wege-Merge (engl. three-way merge) durchzuführen — zum Beispiel,
  wenn es mehr als einen gemeinsamen Vorgänger-Commit gibt — aber der
  Drei-Wege-Merge ist nur für das Mergen von zwei Branches geeignet. Die
  Octopus-Merge-Strategie kann mehrere Branches zusammenführen, aber es wird
  dabei vorsichtiger vorgegangen, um schwierig aufzulösende Konflikte zu
  vermeiden. Aus diesem Grund wird diese Strategie standardmäßig verwendet,
  wenn man mehr als zwei Branches zusammenführen möchte.
</p>

<p>
  Es gibt jedoch noch weitere Strategien, die man verwenden kann. Einer dieser
  Strategien ist der sogenannte Subtree-Merge. Dies kann verwendet werden, um
  unser Problem mit Unterprojekten zu lösen. Ich möchte Dir im Folgenden
  aufzeigen, wie man das Rack-Projekt aus dem letzten Kapitel einbindet und
  dabei den Subtree-Merge anstatt der Submodule verwendet.
</p>

<p>
  Das Prinzip, das hinter einem Subtree-Merge steckt, ist, dass man zwei
  Projekte hat und eines der Projekte wird in ein Unterverzeichnis des anderen
  Projekts abgebildet. Wenn man ein Subtree-Merge ausführt, ist Git schlau
  genug, um zu erkennen, dass ein Projekt ein Abbild von einem anderen Projekt
  ist und es kann den Merge in geeigneter Weise durchführen — das ist wirklich
  sehr erstaunlich.
</p>

<p>
  Als erstes musst Du dazu die Rack-Applikation zu Deinem Projekt hinzufügen.
  Dazu fügst Du das Rack-Projekt als neues Remote-Repository in Deinem Projekt
  hinzu und checkst dieses in einem separaten Branch aus:
</p>

<pre>
$ git remote add rack_remote git@github.com:schacon/rack.git
$ git fetch rack_remote
warning: no common commits
remote: Counting objects: 3184, done.
remote: Compressing objects: 100% (1465/1465), done.
remote: Total 3184 (delta 1952), reused 2770 (delta 1675)
Receiving objects: 100% (3184/3184), 677.42 KiB | 4 KiB/s, done.
Resolving deltas: 100% (1952/1952), done.
From git@github.com:schacon/rack
 * [new branch]      build      -&gt; rack_remote/build
 * [new branch]      master     -&gt; rack_remote/master
 * [new branch]      rack-0.4   -&gt; rack_remote/rack-0.4
 * [new branch]      rack-0.9   -&gt; rack_remote/rack-0.9
$ git checkout -b rack_branch rack_remote/master
Branch rack_branch set up to track remote branch refs/remotes/rack_remote/master.
Switched to a new branch "rack_branch"
</pre>

<p>
  Nach der Ausführung der drei Befehle befindet sich das Rack-Projekt in Deinem
  Branch <code>rack_branch</code> und Dein eigenes Projekt liegt weiterhin im Branch <code>master</code>. Wenn man jetzt den jeweiligen Zweig auscheckt, sieht man die
  unterschiedlichen Inhalte im Wurzelverzeichnis:
</p>

<pre>
$ ls
AUTHORS	       KNOWN-ISSUES   Rakefile      contrib	       lib
COPYING	       README         bin           example	       test
$ git checkout master
Switched to branch "master"
$ ls
README
</pre>

<p>
  Jetzt möchten wir das Rack-Projekt in Deinen Branch <code>master</code> als Unterverzeichnis hinzufügen. Dies kann man in Git mit dem Befehl <code>git read-tree</code> durchführen. In Kapitel 9 werde ich den Befehl <code>read-tree</code> und dessen verwandte Befehle näher erläutern. Hier möchte ich nur erklären,
  dass der Befehl das Wurzelverzeichnis eines Branches in die aktuelle
  Staging-Area und in das Arbeitsverzeichnis packt. Damit hast Du jetzt zu
  Deinem Branch <code>master</code> zurückgewechselt, den Inhalt des Branches <code>rack_branch</code> in das Unterverzeichnis <code>rack</code> im Branch <code>master</code> Deines Projekts hinterlegt:
</p>

<pre>
$ git read-tree --prefix=rack/ -u rack_branch
</pre>

<p>
  Wenn Du jetzt einen Commit ausführst, erscheint es einem so, als ob die
  ganzen Dateien aus dem Rack-Projekt in diesem Unterverzeichnis liegen — als
  ob man das Projekt aus einem Tarball-Container hineinkopiert hätte. Das
  Besondere ist jetzt aber, dass man Änderungen zwischen den verschiedenen
  Branches jetzt einfach zusammenführen kann. Das bedeutet, wenn das
  Rack-Projekt aktualisiert wird, kann man sich diese Änderungen einfach holen,
  indem man in diesen Branch wechselt und dort einen Pull durchführt:
</p>

<pre>
$ git checkout rack_branch
$ git pull
</pre>

<p>
  Danach kann man diese Änderungen wieder in den Branch master mergen. Wenn man
  den Befehl <code>git merge -s subtree</code> verwendet, sollte dies einwandfrei funktionieren. Allerdings wird Git bei
  Ausführen dieses Befehls auch die jeweilige Historie mergen, was Du
  wahrscheinlich nicht haben möchtest. Um nun die Änderungen zu holen und eine
  entsprechende Commit-Nachricht vorzubereiten, hängt man einfach <code>--squash</code>, <code>--no-commit</code> und natürlich <code>-s subtree</code> als Option an:
</p>

<pre>
$ git checkout master
$ git merge --squash -s subtree --no-commit rack_branch
Squash commit -- not updating HEAD
Automatic merge went well; stopped before committing as requested
</pre>

<p>
  Die ganzen Änderungen des Rack-Projekts wurden nun zusammengeführt, Du musst
  jetzt nur noch einen entsprechenden Commit durchführen. Man kann aber auch
  genau das Gegenteil machen: Man führt Änderungen im Unterverzeichnis <code>rack</code> des Branches master aus und mergt diese dann später in den Zweig <code>rack_branch</code>. Diesen kann man dann den Entwicklern des Rack-Projekts zur Verfügung
  stellen.
</p>

<p>
  Um die Unterschiede zwischen dem Inhalt in Deinem Verzeichnis <code>rack</code> und dem Code im Zweig <code>rack_branch</code> anzuzeigen, kann man keinen normalen Vergleich mit <code>diff</code> durchführen. Man muss stattdessen den Befehl <code>git diff-tree</code> verwenden und als Argument den zu vergleichenden Branch angeben:
</p>

<pre>
$ git diff-tree -p rack_branch
</pre>

<p>
  Um Dein Verzeichnis <code>rack</code> mit dem letzten Stand des Branches <code>master</code> auf dem Server zu vergleichen, kannst Du folgenden Befehl verwenden:
</p>

<pre>
$ git diff-tree -p rack_remote/master
</pre>

<h2>Zusammenfassung</h2>

<p>
  In diesem Kapitel hast Du viele ausgeklügelte Werkzeuge kennengelernt, die es
  Dir ermöglichen, Commits und die Staging-Area nach Deinen Vorstellungen zu
  beeinflussen. Wenn ein Problem in Deinem Projekt auftaucht, solltest Du jetzt
  leicht bestimmen können, welcher Commit den Fehler verursacht hat, sowie wann
  und von wem der Fehler begangen wurde. Wenn Du andere Projekte in Deinem
  Projekt verwenden möchtest, hast Du jetzt mehrere Möglichkeiten
  kennengelernt, wie Du dies handhaben kannst. An dieser Stelle solltest Du
  jetzt in der Lage sein, die meisten Dinge, die Du bei der täglichen Arbeit
  benötigst, in der Kommandozeile durchzuführen, ohne dass Dir dabei
  Schweißperlen auf der Stirn stehen.
</p>

<h1>Git individuell einrichten</h1>

<p>
  Ich habe nun die grundlegende Funktionsweise und die Benutzung von Git
  besprochen. Weiterhin habe ich einige Werkzeuge von Git präsentiert, die dem
  Benutzer ein einfaches und effizientes Arbeiten ermöglichen. In diesem
  Kapitel werde ich nun auf einige Operationen eingehen, die Du benutzen kannst
  um die Funktionsweise von Git Deinen persönlichen Bedürfnissen anzupassen.
  Dazu führe ich einige wichtige Konfigurationseinstellungen ein, sowie
  verschiedene Einschubmethoden, auch Hooks genannt. Mit diesen Mitteln kann
  man Git leicht anpassen, sodass es genau Deinen Ansprüchen, des Unternehmens
  oder des Teams entspricht.
</p>

<h2>Git Konfiguration</h2>

<p>
  Wie in Kapitel 1 schon kurz beschrieben, kann man die Konfiguration von Git
  mit Hilfe des Befehls <code>git config</code> steuern. Einer Deiner ersten Aktionen war es, Deinen Namen und E-Mail
  Adresse anzugeben:
</p>

<pre>
$ git config --global user.name "John Doe"
$ git config --global user.email johndoe@example.com
</pre>

<p>
  Jetzt wirst Du einige weitere, interessantere Optionen kennenlernen, die Du
  auf gleiche Art und Weise einsetzen kannst, um Git Deiner Arbeitsumgebung
  anzupassen.
</p>

<p>
  In Kapitel 1 hast Du bereits Deine ersten Erfahrungen mit einigen einfachen
  Einstellparametern von Git gemacht, aber ich möchte sie hier noch einmal kurz
  wiederholen. Git verwendet eine Reihe von Konfigurationsdateien, um Deine
  persönliche Einstellungen, welche von den Standard-Einstellungen abweichen,
  festzuhalten. Zu aller erst prüft Git die Einstellungen in der Datei <code>/etc/gitconfig</code>. Diese Datei enthält Werte, welche für alle Benutzer des Systems und deren
  Repositorys gelten. Wenn Du <code>git config</code> mit der Option <code>--system</code> benutzt, liest und schreibt Git von genau dieser Datei.
</p>

<p>
  Als nächstes prüft Git die Datei <code>~/.gitconfig</code>, welche nur für den jeweiligen Benutzer gilt. Damit Git diese Datei zum
  Lesen und Schreiben nutzt, kannst Du die Option <code>--global</code> angeben.
</p>

<p>
  Als Letztes sucht Git in der Konfigurationsdatei im Git Verzeichnis des
  gerade verwendeten Repositorys (<code>.git/config</code>). Die dort enthaltenen Parameter sind nur für dieses einzelne Repository
  gültig. Jede der erwähnten Ebenen überschreibt die vorhergehende. Das
  bedeutet, dass z.B. die Einstellungen in der Datei <code>/etc/gitconfig</code> von den Einstellungen in der Datei <code>.git/config</code> überschrieben werden. Du kannst alle Parameter auch durch manuelles
  Editieren der jeweiligen Datei setzen bzw. verändern (vorausgesetzt Du
  verwendest die richtige Syntax). In der Regel ist es aber einfacher den
  Befehl <code>git config</code> zu verwenden.
</p>

<h3>Grundlegende Client Konfiguration</h3>

<p>
  Einstellparameter in Git lassen sich in zwei Kategorien aufteilen: Parameter
  für die Client-Konfiguration und für die Server-Konfiguration. Der Großteil
  der Einstellungen bezieht sich auf den Client – zur Konfiguration Deines
  persönlichen Arbeitsablaufs. Auch wenn es eine große Anzahl an
  Einstellmöglichkeiten gibt, werde ich nur die wenigen besprechen, die sehr
  gebräuchlich sind oder Deine Arbeitsweise bedeutend beeinflussen können.
  Viele Optionen sind nur für Spezialfälle interessant, auf die ich hier aber
  nicht weiter eingehen möchte. Falls Du eine Liste aller Optionen haben
  willst, kannst Du folgenden Befehl ausführen:
</p>

<pre>
$ git config --help
</pre>

<p>
  Die Hilfeseite zu <code>git config</code> listet alle verfügbaren Optionen sehr detailliert auf.
</p>

<p>core.editor</p>

<p>
  In der Grundeinstellung benutzt Git Deinen Standard Texteditor oder greift
  auf den Vi Editor zurück, um Deine Commit und Tag Nachrichten zu erstellen
  und zu bearbeiten. Um einen andern Editor als Standard einzurichten kannst Du
  die Option <code>core.editor</code> nutzen:
</p>

<pre>
$ git config --global core.editor emacs
</pre>

<p>
  Ab jetzt wird Git immer Emacs starten um Nachrichten zu editieren, unabhängig
  davon welcher Standard Shell-Editor gesetzt ist.
</p>

<p>commit.template</p>

<p>
  Wenn Du diese Einstellung auf einen Pfad zu einer Datei auf Deinem System
  einstellst, wird Git den Inhalt dieser Datei als Standard Commit Nachricht
  verwenden. Nehmen wir zum Beispiel an, Du erstellst eine Vorlage unter dem
  Namen <code>$HOME/.gitmessage.txt</code>, die den folgenden Inhalt hat:
</p>

<pre>
subject line

what happened

[ticket: X]
</pre>

<p>
  Damit Git diese Datei als Standard Nachricht benutzt, die in Deinem Editor
  erscheint, wenn Du <code>git commit</code> aufrufst, richte die Option <code>commit.template</code> ein:
</p>

<pre>
$ git config --global commit.template $HOME/.gitmessage.txt
$ git commit
</pre>

<p>
  Wenn Du dann das nächste Mal einen Commit durchführst, wird Dein Editor mit
  etwa der folgenden Nachricht starten:
</p>

<pre>
subject line

what happened

[ticket: X]
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
#   (use "git reset HEAD &lt;file&gt;..." to unstage)
#
# modified:   lib/test.rb
#
~
~
".git/COMMIT_EDITMSG" 14L, 297C
</pre>

<p>
  Falls eine Richtlinie für Commit Nachrichten existiert, solltest Du Git so
  konfigurieren, dass eine Vorlage davon bei einem Commit geladen wird. Dies
  erhöht die Chance, dass diese Richtlinie auch eingehalten wird.
</p>

<p>core.pager</p>

<p>
  Die Einstellung <code>core.pager</code> legt fest, welche Anwendung zur Seitenanzeige benutzt wird, wenn Git Text
  ausgibt, wie zum Beispiel bei <code>log</code> und <code>diff</code>. Du kannst es auch auf <code>more</code> oder eine andere Seitenanzeige Deiner Wahl (der Standard ist <code>less</code>) einstellen, oder Du kannst es mittels eines leeren Strings ganz
  ausschalten:
</p>

<pre>
$ git config --global core.pager ''
</pre>

<p>
  Wenn Du dies ausführst, wird Git immer die komplette Ausgabe aller Befehle
  anzeigen, egal wie lange sie ist.
</p>

<p>user.signingkey</p>

<p>
  Falls Du signierte kommentierte Tags erstellst (wie in Kapitel 2
  beschrieben), so macht es die Arbeit leichter, wenn Du Deinen GPG
  Signierschlüssel in Git festlegst. Du kannst Deine Schlüssel ID wie folgt
  festlegen:
</p>

<pre>
$ git config --global user.signingkey &lt;gpg-key-id&gt;
</pre>

<p>
  Beim Signieren von Tags mit Hilfe von <code>git tag</code> musst Du Deinen Schlüssel jetzt nicht mehr angeben. Es reicht folgendes
  auszuführen:
</p>

<pre>
$ git tag -s &lt;tag-name&gt;
</pre>

<p>core.excludesfile</p>

<p>
  In Kapitel 2 habe ich bereits beschrieben, wie Du mit Hilfe der
  projektspezifischen <code>.gitignore</code> Datei Git dazu bringst, bestimmte Dateien nicht weiter zu verfolgen
  beziehungsweise zu stagen, wenn Du den Befehl <code>git add</code> verwendest. Falls Du jedoch eine weitere Datei außerhalb Deines Projekts
  verwenden willst, die diese Werte enthält oder zusätzliche Muster definiert,
  dann kannst Du Git mit der Option <code>core.excludesfile</code> mitteilen, wo sich diese Datei befindet. Trage hier einfach den Pfad zu
  einer Datei ein, welche entsprechend einer <code>.gitignore</code> Datei aufgebaut ist.
</p>

<p>help.autocorrect</p>

<p>
  Diese Option ist in Git ab Version 1.6.1 verfügbar. Wenn Du in Git einen
  Befehl falsch schreibst, bekommst Du eine Meldung wie diese:
</p>

<pre>
$ git com
git: 'com' is not a git-command. See 'git --help'.

Did you mean this?
     commit
</pre>

<p>
  Wenn Du die Option <code>help.autocorrect</code> auf 1 setzt, wird Git automatisch den entsprechenden Befehl ausführen, falls
  es in dieser Situation die einzige passende Alternative ist.
</p>

<h3>Farben in Git</h3>

<p>
  Git kann für die Textanzeige im Terminal Farben benutzen, die Dir helfen
  können, die Ausgabe schnell und einfach zu begreifen. Mit einer Vielzahl von
  Optionen kannst Du die Farben an Deine Vorlieben anpassen.
</p>

<p>color.ui</p>

<p>
  Wenn Du Git entsprechend konfigurierst, wird es den Großteil der Ausgaben
  automatisch farblich darstellen. Du kannst sehr detailliert einstellen, wie
  und welche Farben verwendet werden sollen, aber um die
  Standard-Terminalfarben zu aktivieren musst Du <code>color.ui</code> auf ‚true‘ setzen:
</p>

<pre>
$ git config --global color.ui true
</pre>

<p>
  Wenn dieser Wert gesetzt wurde, benutzt Git für seine Ausgaben Farben, sofern
  diese zu einem Terminal geleitet werden. Weitere mögliche Einstellungen sind
  ‚false‘, wodurch alle Farben deaktiviert werden, sowie ‚always‘, wodurch
  Farben immer aktiviert sind, selbst wenn Du Git Befehle in eine Datei oder
  über eine Pipe zu einem anderen Befehl umleitest.
</p>

<p>
  Du wirst selten die Einstellung <code>color.ui = always</code> benötigen. In den meisten Fällen in denen Du in Deiner umgeleiteten Ausgabe
  Farben haben willst, kannst Du stattdessen die Option <code>--color</code> in der Kommandozeile benutzen. Damit weist Du Git an, die Farbkodierung für
  die Ausgabe zu verwenden. Die Einstellung <code>color.ui = true</code> sollte aber in den meisten Fällen Deinen Anforderungen genügen.
</p>

<p><code>color.*</code></p>

<p>
  Falls Du im Detail einstellen willst, welche Befehle wie gefärbt werden, dann
  stellt Git Verb-spezifische Farbeinstellungen zur Verfügung. Jede dieser
  Optionen kann auf <code>true</code>, <code>false</code>, oder <code>always</code> eingestellt werden:
</p>

<pre>
color.branch
color.diff
color.interactive
color.status
</pre>

<p>
  Zusätzlich hat jede dieser Einstellungen Unteroptionen, die Du benutzen
  kannst, um die Farbe für einzelne Teile der Ausgabe festzulegen. Um zum
  Beispiel die Meta Informationen in Deiner Diff Ausgabe mit blauem, fettem
  Text auf schwarzem Hintergrund darzustellen, kannst Du folgenden Befehl
  verwenden:
</p>

<pre>
$ git config --global color.diff.meta "blue black bold"
</pre>

<p>
  Du kannst als Farben jeden der folgenden Werte verwenden: <code>normal</code>, <code>black</code>, <code>red</code>, <code>green</code>, <code>yellow</code>, <code>blue</code>, <code>magenta</code>, <code>cyan</code>, oder <code>white</code>. Falls Du ein Attribut wie z.B. die Fettschrift aus dem vorigen Beispiel
  verwenden willst, stehen Dir folgende Werte zur Auswahl: <code>bold</code>, <code>dim</code>, <code>ul</code>, <code>blink</code>, und <code>reverse</code>.
</p>

<p>
  Auf der Manpage zu <code>git config</code> findest Du eine Liste aller Unteroptionen, die Du konfigurieren kannst.
</p>

<h3>Externe Merge- und Diff-Werzeuge</h3>

<p>
  Bisher hast Du die in Git integrierte Implementierung von diff benutzt, aber
  Du kannst stattdessen auch eine externe Anwendung verwenden. Du kannst ebenso
  ein grafisches Merge-Werkzeug zur Auflösung von Konflikten einsetzen, statt
  diese manuell zu lösen. Ich werde demonstrieren, wie man das grafische
  Merge-Werkzeug von Perforce (P4Merge) konfiguriert, um Diffs und Merges zu
  bearbeiten. Ich habe P4Merge gewählt, da es ein freies und gutes grafisches
  Werkzeug ist.
</p>

<p>
  Da P4Merge für die üblichen Plattformen verfügbar ist, sollte es kein Problem
  sein, es einmal auszuprobieren. In den Beispielen werde ich Pfadnamen nutzen,
  die auf Mac- und Linux-System funktionieren. Die Windows Benutzer müssen <code>/usr/local/bin</code> durch einen Pfad ersetzen, der in der Umgebungsvariable <code>PATH</code> gelistet ist.
</p>

<p>Du kannst P4Merge hier herunterladen:</p>

<pre>
http://www.perforce.com/product/components/perforce-visual-merge-and-diff-tools
</pre>

<p>
  Als erstes solltest Du einige Wrapper Skripte erstellen um Deine Befehle
  auszuführen. Ich verwende hier die Pfade, die für einen Mac gelten. Auf
  anderen Systemen muss der Pfad zur ausführbaren Datei von P4Merge
  entsprechend angepasst werden. Mit den folgenden Befehlen erzeugen wir ein
  Skript mit dem Namen <code>extMerge</code>, welches die Anwendung mit allen angegebenen Argumenten aufruft:
</p>

<pre>
$ cat /usr/local/bin/extMerge
#!/bin/sh
/Applications/p4merge.app/Contents/MacOS/p4merge $*
</pre>

<p>
  Das Wrapper Skript für den Diff Befehl stellt sicher, dass es mit sieben
  Parametern aufgerufen wird und leitet zwei von diesen an das Merge Skript
  weiter. Standardmäßig übergibt Git die folgenden Argumente an das
  Diff-Werkzeug:
</p>

<pre>
path old-file old-hex old-mode new-file new-hex new-mode
</pre>

<p>
  Da nur die Parameter <code>old-file</code> und <code>new-file</code> benötigt werden, verwenden wir das Wrapper Skript um nur die notwendigen
  Parameter weiterzugeben.
</p>

<pre>
$ cat /usr/local/bin/extDiff
#!/bin/sh
[ $# -eq 7 ] &amp;&amp; /usr/local/bin/extMerge "$2" "$5"
</pre>

<p>Außerdem muss sichergestellt werden, dass die Skripte ausführbar sind::</p>

<pre>
$ sudo chmod +x /usr/local/bin/extMerge
$ sudo chmod +x /usr/local/bin/extDiff
</pre>

<p>
  Jetzt kannst Du Git so konfigurieren, dass es Deine persönlichen Merge- und
  Diff-Werkzeuge benutzt. Dazu sind einige weitere Einstellungen nötig: <code>merge.tool</code>, um die von Git verwendete Merge Strategie festzulegen, <code>mergetool.*.cmd</code>, um festzulegen, wie der Befehl auszuführen ist, <code>mergetool.trustExitCode</code>, damit Git weiß, ob der Exit-Code des Programms eine erfolgreiche Merge
  Auflösung anzeigt oder nicht, und <code>diff.external</code>, um einzustellen welches Diff Kommando Git benutzen soll. Du kannst also
  entweder die vier folgenden Befehle ausführen
</p>

<pre>
$ git config --global merge.tool extMerge
$ git config --global mergetool.extMerge.cmd \
    'extMerge "$BASE" "$LOCAL" "$REMOTE" "$MERGED"'
$ git config --global mergetool.trustExitCode false
$ git config --global diff.external extDiff
</pre>

<p>
  oder Du bearbeitest Deine <code>~/.gitconfig</code> Datei und fügst dort folgende Zeilen hinzu:
</p>

<pre>
[merge]
  tool = extMerge
[mergetool "extMerge"]
  cmd = extMerge \"$BASE\" \"$LOCAL\" \"$REMOTE\" \"$MERGED\"
  trustExitCode = false
[diff]
  external = extDiff
</pre>

<p>
  Nach Setzen dieser Einstellungen und beim Ausführen eines Diff Befehls wie
  den folgenden:
</p>

<pre>
$ git diff 32d1776b1^ 32d1776b1
</pre>

<p>
  wird Git P4Merge starten, anstatt den Vergleich in der Kommandozeile
  auszugeben. Abbildung 7-1 zeigt hierzu ein Beispiel.
</p>

<p>Insert 18333fig0701.png Abbildung 7-1. P4Merge</p>

<p>
  Wenn Du versuchst zwei Branches zu mergen und dabei Merge Konflikte
  auftreten, kannst Du den Befehl <code>git mergetool</code> ausführen. Das Kommando startet P4Merge und erlaubt es Dir, die Konflikte
  mit Hilfe des grafischen Werkzeugs aufzulösen.
</p>

<p>
  Das Tolle an dem Wrapper Ansatz ist, dass Du Deine Diff- und Merge-Werkzeuge
  sehr leicht wechseln kannst. Wenn Du zum Beispiel für <code>extDiff</code> und <code>extMerge</code> statt P4Merge, KDiff3 verwenden willst, musst Du lediglich Dein Wrapper
  Skript <code>extMerge</code> anpassen:
</p>

<pre>
$ cat /usr/local/bin/extMerge
#!/bin/sh
/Applications/kdiff3.app/Contents/MacOS/kdiff3 $*
</pre>

<p>
  Ab jetzt verwendet Git KDiff3 zur Anzeige von Diffs und zur Auflösung von
  Merge Konflikten.
</p>

<p>
  Git wird bereits mit Standard-Einstellungen für verschiedene
  Merge-Auflösungswerkzeuge ausgeliefert, sodass Du diese nicht extra
  konfigurieren musst. Als Merge-Werkzeug kann Du kdiff3, opendiff, tkdiff,
  meld, xxdiff, emerge, vimdiff oder gvimdiff einstellen. Wenn Du KDiff3 nur
  zum Auflösen von Konflikten und nicht für einen Diff verwenden willst, kannst
  Du den folgenden Befehl ausführen (vorausgesetzt KDiff3 befindet sich im
  Standard-Pfad):
</p>

<pre>
$ git config --global merge.tool kdiff3
</pre>

<p>
  Wenn Du diesen Befehl ausführst, anstatt die <code>extMerge</code> und <code>extDiff</code> Skripte zu erstellen, dann wird Git KDiff3 zum Auflösen von Merge Konflikten
  verwenden. Für einen Vergleich verwendet Git weiterhin das integrierte
  Diff-Werkzeug.
</p>

<h3>Formatierungen und Leerzeichen</h3>

<p>
  Bei der Zusammenarbeit mit anderen Entwicklern sind Probleme mit
  Formatierungen und Leerzeichen einige der frustrierendsten und heikelsten
  Themen denen viele Entwickler begegnen, vor allem bei plattformübergreifenden
  Projekten. Es kann sehr leicht passieren, dass durch Patches oder andere
  gemeinsame Arbeit fast unmerklich Leerzeichen Änderungen eingeführt werden,
  z.B. weil ein Editor sie stillschweigend einfügt. Beim Programmieren unter
  Windows können durch Änderungen an einer Zeile auch leicht Wagenrückläufe
  (CR) am Zeilenende eingefügt werden (relevant bei plattformübergreifenden
  Projekten). Git kann mit ein paar Einstellungen hierbei unterstützend
  eingreifen.
</p>

<p>core.autocrlf</p>

<p>
  Falls Du unter Windows programmierst oder ein anderes System benutzt und mit
  anderen zusammenarbeitest, die unter Windows programmieren, wirst Du sehr
  wahrscheinlich irgendwann Problemen mit Zeilenenden begegnen. Dies liegt
  daran, dass Windows sowohl ein CR Zeichen, als auch ein LF Zeichen zum
  Signalisieren einer neuen Zeile in Dateien verwendet. Mac und Linux nutzen
  stattdessen nur ein LF Zeichen (Mac OS bis Version 9 verwendet ein einzelnes
  CR Zeichen). Dies ist eine kleine, aber extrem störende Tatsache beim
  Arbeiten über Plattformgrenzen hinweg.
</p>

<p>
  Git kann dies vermeiden, indem es CRLF am Zeilenende automatisch zu LF
  konvertiert, wenn Du ein Commit durchführst, und umgekehrt wenn es Code in
  Dein lokales Dateisystem auscheckt. Du kannst diese Funktionalität mittels
  der Option <code>core.autocrlf</code> aktivieren. Falls Du auf einem Windows System arbeitest, setze sie auf <code>true</code> — dies konvertiert LF zu CRLF, wenn Du Code auscheckst:
</p>

<pre>
$ git config --global core.autocrlf true
</pre>

<p>
  Falls Du auf einem Linux oder Mac System arbeitest, welches LF Zeilenenden
  verwendet, dann soll Git keine Datei automatisch konvertieren, wenn sie
  ausgecheckt wird. Wenn allerdings versehentlich eine Datei mit CRLF in das
  Repository eingeführt wurde, dann möchtest Du vielleicht, dass Git dies
  automatisch für Dich repariert. Wenn Du den Parameter <code>core.autocrlf</code> auf input setzt, wird Git bei einem Commit automatisch CRLF in LF umwandeln.
  Allerdings nicht in die andere Richtung bei einem Checkout:
</p>

<pre>
$ git config --global core.autocrlf input
</pre>

<p>
  Mit dieser Einstellung solltest Du CRLF Zeilenenden in unter Windows
  ausgecheckten Dateien haben und LF Zeilenenden auf Mac und Linux Sytemen und
  im Repository.
</p>

<p>
  Falls Du ein Windows Programmierer bist und an einem Projekt arbeitest,
  welches nur unter Windows entwickelt wird, dann kannst Du diese
  Funktionalität auch deaktivieren. In diesem Fall werden Zeilenenden mit CRLF
  im Repository gespeichert. Dazu setzt Du die Option auf <code>false</code>:
</p>

<pre>
$ git config --global core.autocrlf false
</pre>

<p>core.whitespace</p>

<p>
  Git ist so voreingestellt, dass es einige Leerzeichen Probleme erkennen und
  beheben kann. Es kann nach vier grundlegenden Problemen mit Leerzeichen
  suchen — Zwei davon sind standardmässig aktiviert und können deaktiviert
  werden. Die anderen beiden sind inaktiv, können aber aktiviert werden.
</p>

<p>
  Die zwei standardmäßig aktiven Optionen sind <code>trailing-space</code>, das nach Leerzeichen am Ende einer Zeile sucht, und <code>space-before-tab</code>, das nach Leerzeichen vor Tabulatoren am Anfang einer Zeile sucht.
</p>

<p>
  Die beiden aktivierbaren, aber normalerweise deaktivierten Optionen sind <code>indent-with-non-tab</code>, welches nach Zeilen sucht, die mit acht oder mehr Leerzeichen anstelle von
  Tabulatoren beginnen, und <code>cr-at-eol</code>, wodurch Git angewiesen wird, dass CR Zeichen am Zeilenende in Ordnung sind.
</p>

<p>
  Du kannst Git mitteilen, welche dieser Optionen es aktivieren soll, indem Du <code>core.whitespace</code> auf die Werte setzt, die Du an- oder abgeschaltet haben möchtest. Die
  jeweiligen Werte werden mit einem Komma getrennt. Du kannst Optionen
  deaktivieren, indem Du sie entweder aus der Parameterliste entfernst, oder
  ihnen ein <code>-</code> Zeichen voranstellst. Wenn Du zum Beispiel alle Optionen außer <code>cr-at-eol</code> aktivieren willst, kannst Du folgenden Befehl ausführen:
</p>

<pre>
$ git config --global core.whitespace \
    trailing-space,space-before-tab,indent-with-non-tab
</pre>

<p>
  Git wird die möglichen Problemstellen erkennen, wenn Du den <code>git diff</code> Befehl ausführst, und es wird versuchen, sie farblich hervorzuheben, damit
  Du sie vor einem Commit beheben kannst. Git wird diese Einstellungen auch
  benutzen, um Dir zu helfen, wenn Du mit <code>git apply</code> Patches anwendest. Wenn Du Patches anwendest, kannst Du Git anweisen eine
  Warnung auszugeben, falls es beim Patchen die spezifizierten
  Leerzeichenprobleme erkennt:
</p>

<pre>
$ git apply --whitespace=warn &lt;patch&gt;
</pre>

<p>
  Oder Du kannst Git versuchen lassen, diese Probleme automatisch zu beheben,
  bevor es den Patch anwendet:
</p>

<pre>
$ git apply --whitespace=fix &lt;patch&gt;
</pre>

<p>
  Diese Optionen gelten auch für den Rebase Befehl. Falls Du einen Commit
  gemacht hast, der problematische Leerzeichen enthält, aber Du die Änderungen
  noch nicht auf den Server gepusht hast, kannst Du ein <code>rebase</code> mit dem Parameter <code>--whitespace=fix</code> ausführen. Damit behebt Git automatisch die Leerzeichenfehler während des
  Rebase-Vorgangs.
</p>

<h3>Server Konfiguration</h3>

<p>
  Es gibt nicht annähernd so viele Konfigurationsmöglichkeiten für die
  Serverfunktionalitäten von Git, aber es gibt dabei einige interessante
  Parameter, die Du Dir anschauen solltest.
</p>

<p>receive.fsckObjects</p>

<p>
  Die Objekte, die Git durch einen Push empfängt, werden von Haus aus nicht auf
  Konsistenz geprüft. Auch wenn Git sicherstellen kann, dass jedes Objekt mit
  dessen SHA-1 Checksumme übereinstimmt und auf gültige Objekte verweist, so
  wird dies standardmäßig nicht bei jedem Push durchgeführt. Das ist eine
  aufwändige Operation und kann abhängig von der Größe des Repositorys oder dem
  Push eine Menge Zeit kosten. Wenn Du die Objektkonsistenz bei jedem Push
  durch Git prüfen lassen willst, so kannst Du das erzwingen, indem Du <code>receive.fsckObjects</code> auf ‚true‘ setzt:
</p>

<pre>
$ git config --system receive.fsckObjects true
</pre>

<p>
  Ab jetzt prüft Git die Integrität des Repositorys bevor der Push akzeptiert
  wird. Damit ist sichergestellt, dass kein Client korrupte Daten einspeist.
</p>

<p>receive.denyNonFastForwards</p>

<p>
  Falls Du auf Commits, die bereits gepusht sind, einen Rebase anwendest, und
  diese dann versuchst zu pushen, wird Git dies mit einer Fehlermeldung
  zurückweisen. Wenn der Remote Branch auf einen Commit zeigt, welcher nicht in
  Deinem lokalen Branch enthalten ist und Du versuchst diesen Branch zu pushen,
  wird sich Git genau gleich verhalten und den Push verweigern. Das ist in den
  meisten Fällen eine gute Richtlinie, aber im Falle eines Rebase ist eventuell
  ein anderes Verhalten gewünscht (vorausgesetzt Du weißt was Du tust). Dann
  kannst Du den Push auch erzwingen, indem Du den Parameter <code>-f</code> zu dem Push Kommando hinzufügst.
</p>

<p>
  Aktualisierungen auf dem Remote Branch, welche nicht einem Fast-Forward
  entsprechen können durch Setzen des Parameters <code>receive.denyNonFastForward</code> auf den Wert ‚true‘ deaktiviert werden:
</p>

<pre>
$ git config --system receive.denyNonFastForwards true
</pre>

<p>
  Eine andere Möglichkeit ist die Einrichtung von serverseitigen Hooks, die ich
  etwas später noch beschreiben werde. Dieser Ansatz erlaubt noch komplexere
  Szenarien. Man kann z.B. die Pushes, welche nicht einem Fast-Forward
  entsprechen nur für bestimmte Benutzergruppen verweigern.
</p>

<p>receive.denyDeletes</p>

<p>
  Es ist möglich die Option <code>denyNonFastForwards</code> zu umgehen, indem man den Remote Branch zuerst löscht und dann mit einer
  neuen Referenz pusht. In neueren Versionen von Git (ab Version 1.6.1) kann
  man den Parameter&emsp14;&emsp14;<code>receive.denyDeletes</code> auf ‚true‘ setzen:
</p>

<pre>
$ git config --system receive.denyDeletes true
</pre>

<p>
  Dies verbietet grundsätzlich jedem Benutzer das Löschen eines Branches oder
  Tags. Um einen Remote Branch zu löschen müssen die ref Dateien manuell vom
  Server entfernt werden. Es gibt aber auch noch andere interessantere Wege
  dies auf Benutzerbasis über Zugriffssteuerungslisten (ACL) durchzuführen. Ich
  werde dies am Ende dieses Kapitel noch vorstellen.
</p>

<h2>Git Attribute</h2>

<p>
  Einige dieser Einstellungen können auch auf einen Pfad beschränkt werden,
  sodass sie nur für bestimmte Unterverzeichnisse oder eine Gruppe von Dateien
  gültig sind. Diese Einstellungen werden Git Attribute genannt und werden in
  der Datei <code>.gitattributes</code> in einem der Projektverzeichnisse verwaltet (üblicherweise im
  Root-Verzeichnis Deines Projekts). Alternativ kannst Du diese auch unter <code>.git/info/attributes</code> ablegen. In diesem Fall werden die Attribute nicht in das Repository
  eingecheckt und gelten nur für dieses einzelne, lokale Repository.
</p>

<p>
  Mittels den Attributen ist es zum Beispiel möglich, verschiedene Merge
  Strategien für einzelne Dateien oder Verzeichnisse innerhalb Deines Projekts
  vorzugeben. Ebenso kannst Du Git anweisen, wie ein Vergleich von Binärdateien
  durchzuführen ist. Oder Du konfigurierst Git so, dass der Inhalt von Dateien
  vorgefiltert wird, wenn Du ein Commit oder Checkout durchführst. In diesem
  Abschnitt wirst Du einiger der Attribute kennenlernen, die Du für die
  einzelnen Verzeichnisse in Deinem Git Projekt vorgeben kannst. Außerdem werde
  ich einige Beispiele aus der Praxis näher erläutern.
</p>

<h3>Binärdateien</h3>

<p>
  Mit Hilfe der Git Attribute ist es Dir möglich, Git mitzuteilen, welche
  Dateien binär sind (für den Fall, dass Git nicht in der Lage ist, dies selbst
  feszustellen) und wie Git diese behandeln soll. Es kann zum Beispiel sein,
  dass automatisiert, erstellte Textdateien nicht einfach verglichen werden
  können. Oder umgekehrt können manche Binärdateien leicht von einem Menschen
  verglichen werden. Ich werde jetzt aufzeigen, wie Du Git konfigurierst damit
  es solche Dateien unterscheiden kann.
</p>

<p>Binärdateien erkennen</p>

<p>
  Manche Dateien sehen zwar wie Textdateien aus, sollten aber streng genommen
  als Binärdateien behandelt werden. So enthalten zum Beispiel Xcode Projekte
  auf dem Mac eine Datei mit der Endung <code>.pbxproj</code>. Die Datei ist eigentlich nur ein JSON-Datensatz (ein Klartext Javascript
  Datenformat), der von der IDE gespeichert wird und unter anderem die Build
  Einstellungen enthält. Obwohl sie nur ASCII Zeichen enthält und damit
  technisch gesehen eine Textdatei ist, sollte man diese nicht als solche
  behandeln. In Wirklichkeit ist diese Datei eine kleine Datenbank, deren
  Inhalt nicht zusammengeführt werden kann, wenn zwei Leute sie geändert haben.
  Das Vergleichen der Datei ist ebenso selten hilfreich. Die Datei ist für die
  Verarbeitung durch einen Computer gedacht. Kurz gesagt, Du willst, dass man
  sie als Binärdatei behandelt.
</p>

<p>
  Um Git anzuweisen alle <code>pbxproj</code> Dateien als Binärdateien zu behandeln, kannst Du die folgende Zeile zu
  Deiner <code>.gitattributes</code> Datei hinzufügen:
</p>

<pre>
*.pbxproj -crlf -diff
</pre>

<p>
  Ab jetzt wird Git nicht mehr versuchen CRLF Probleme zu lösen oder die Datei
  beim Commit oder Checkout zu ändern. Außerdem ermittelt Git keine
  Dateiunterschiede mehr und gibt diese auch nicht aus, wenn Du den Befehl <code>git show</code> oder <code>git diff</code> ausführst. Alternativ gibt es auch ein integriertes Makro <code>binary</code>, welches den Parametern <code>-crlf -diff</code> entspricht:
</p>

<pre>
*.pbxproj binary
</pre>

<p>Diff bei Binärdateien</p>

<p>
  Mit Hilfe der Git Attribute können Unterschiede in binären Dateien effektiv
  und leicht angezeigt werden. Du kannst Git so konfigurieren, dass es
  automatisch Binärdateien in Textdateien umwandelt, damit sie mit einem
  normalen Diff verglichen werden können. Meist stellt sich aber die Frage, wie
  man binäre Daten in Text konvertieren soll. Wenn man ein Werkzeug findet,
  welches einem diese Konvertierung abnimmt und die binäre Daten in ein
  Textformat umwandelt, ist dies meist die beste Lösung. Leider gibt es nur
  sehr wenige binäre Formate, die sich dafür eignen, dass man sie in lesbare
  Textformate umwandelt (Ich denke dabei zum Beispiel an Audio-Daten). Wenn
  dies der Fall ist und Du keine geeignete Möglichkeit gefunden hast, die Daten
  in lesbare Form zu wandeln, dann ist es oft relativ einfach eine
  entsprechende Beschreibung des eigentlichen Inhalts zu erhalten. Alternativ
  gibt es noch Metadaten, wobei Metadaten einem nicht ein vollständiges Abbild
  vom Dateiinhalt liefern können, aber in diesem Fall ist das besser als gar
  nichts.
</p>

<p>
  Im folgenden Abschnitt werden wir beide Möglichkeiten besprechen, wie man für
  weit verbreitete binäre Formate eine lesbare Form für einen Vergleich erhält.
</p>

<p>
  Anmerkung: Es gibt verschiedene Arten von binären Formaten, welche Text
  beinhalten, und es ist meist sehr schwierig einen passenden Konverter zu
  finden. In solchen Fällen kann man sein Glück mit dem <code>strings</code>-Programm versuchen. Manche der Formate verwenden ein UTF-16 Encoding oder
  andere Zeichentabellen. In diesem Fall wird es mit dem Programm <code>strings</code> meist nicht funktionieren. Da <code>strings</code> jedoch auf den meisten Mac- und Linux-Systemen verfügbar ist, sollte man es
  durchaus auf einen Versuch ankommen lassen.
</p>

<p>MS Word files</p>

<p>
  Als erstes werden wir die beschriebene Technik benutzen um eines der
  lästigsten Probleme der Menschheit zu lösen: Versionskontrolle von Word
  Dokumenten. Jeder weiß, dass Word der schrecklichste Editor der Welt ist,
  aber trotzdem benutzt ihn jeder. Wenn Du Word Dokumente versionieren willst,
  kannst Du sie in Dein Repository packen und ab und zu einen Commit
  durchführen. Aber wozu ist das nützlich? Wenn Du einen Vergleich mit <code>git diff</code> ausführst, erhälst Du ähnliche Ausgabe wie diese:
</p>

<pre>
$ git diff
diff --git a/chapter1.doc b/chapter1.doc
index 88839c4..4afcb7c 100644
Binary files a/chapter1.doc and b/chapter1.doc differ
</pre>

<p>
  Du kannst zwei Versionen nicht direkt vergleichen, außer Du checkst sie aus
  und prüfst sie manuell, richtig? Es stellt sich heraus, dass dies recht gut
  mittels Git Attributen möglich ist. Füge dazu die folgende Zeile in Deine <code>.gitattributes</code> Datei ein:
</p>

<pre>
*.doc diff=word
</pre>

<p>
  Dies weist Git an, dass auf jede Datei, die diesem Dateimuster (.doc)
  entspricht, der „word“ Filter angewandt werden soll, wenn Du versuchst, einen
  Diff mit Dateiunterschieden anzusehen. Was ist nun der „word“ Filter? Dieser
  muss von Dir noch konfiguriert werden. Du kannst Git so konfigurieren, dass
  es das <code>catdoc</code> Programm verwendet um Word Dokumente in lesbare Textdateien zu konvertieren.
  <code>catdoc</code> wurde speziell dafür entwickelt um lesbaren Text aus binären MS Word
  Dokumenten zu extrahieren (du erhälst es unter <code>http://www.wagner.pp.ru/~vitus/software/catdoc/</code>).&emsp14;&emsp14;&emsp14;Bei jedem Diff wird Git diese Konvertierung
  durchführen:
</p>

<pre>
$ git config diff.word.textconv catdoc
</pre>

<p>
  Dieser Befehl fügt in der Datei <code>.git/config</code> eine Sektion mit folgendem Aufbau hinzu:
</p>

<pre>
[diff "word"]
	textconv = catdoc
</pre>

<p>
  Bei jedem Vergleich von zwei Schnappschüssen wird Git Dateien mit der
  Dateiendung <code>.doc</code> durch den „word“ Filter jagen, welcher durch das <code>catdoc</code> Programm definiert ist. Das erzeugt gut lesbare Textversionen Deiner Word
  Dateien, die für den Vergleich herangezogen werden.
</p>

<p>
  Dazu ein Beispiel. Ich habe Kapitel 1 des Buches in ein Word-Dokument
  einfgefügt und in Git gespeichert. Danach habe ich etwas Text in einem Absatz
  geändert, die Datei gespeichert und den Befehl <code>git diff</code> ausgeführt um zu prüfen, was sich geändert hat:
</p>

<pre>
$ git diff
diff --git a/chapter1.doc b/chapter1.doc
index c1c8a0a..b93c9e4 100644
--- a/chapter1.doc
+++ b/chapter1.doc
@@ -128,7 +128,7 @@ and data size)
 Since its birth in 2005, Git has evolved and matured to be easy to use
 and yet retain these initial qualities. It’s incredibly fast, it’s
 very efficient with large projects, and it has an incredible branching
-system for non-linear development.
+system for non-linear development (See Chapter 3).
</pre>

<p>
  Git war erfolgreich und zeigt nun kurz und bündig an, dass ich den Text „(See
  Chapter 3)“ hinzugefügt habe, was korrekt ist. Wie du siehst, funktioniert
  perfekt.
</p>

<p>OpenDocument Textdateien</p>

<p>
  Bei OpenDocument Textdateien (<code>*.odt</code>), die mit OpenOffice erstellt wurden, können wir die gleiche
  Herangehensweise wie bei MS Word Dateien (<code>*.odt</code>) anwenden.
</p>

<p>Füge die folgende Zeile zu der <code>.gitattributes</code> Datei hinzu:</p>

<pre>
*.odt diff=odt
</pre>

<p>
  Jetzt müssen wir noch den <code>odt</code> Diff Filter in der <code>.git/config</code> hinzufügen:
</p>

<pre>
[diff "odt"]
	binary = true
	textconv = /usr/local/bin/odt-to-txt
</pre>

<p>
  OpenDocument Dateien sind eigentlich komprimierte Zip Verzeichnisse, die
  mehrere Dateien enthalten (der Inhalt: XML-Dateien, Stylesheets, Bilder,
  usw.). Wir müssen ein Skript schreiben um den Inhalt zu extrahieren und das
  Ergebnis als reinen Text zurückliefern. Erzeuge dazu eine Datei <code>/usr/local/bin/odt-to-txt</code> (die Datei kann in einem beliebigen Verzeichnis abgelegt werden) mit dem
  folgenden Inhalt:
</p>

<pre>
#! /usr/bin/env perl
# Simplistic OpenDocument Text (.odt) to plain text converter.
# Author: Philipp Kempgen

if (! defined($ARGV[0])) {
	print STDERR "No filename given!\n";
	print STDERR "Usage: $0 filename\n";
	exit 1;
}

my $content = '';
open my $fh, '-|', 'unzip', '-qq', '-p', $ARGV[0], 'content.xml' or die $!;
{
	local $/ = undef;  # slurp mode
	$content = &lt;$fh&gt;;
}
close $fh;
$_ = $content;
s/&lt;text:span\b[^&gt;]*&gt;//g;           # remove spans
s/&lt;text:h\b[^&gt;]*&gt;/\n\n*****  /g;   # headers
s/&lt;text:list-item\b[^&gt;]*&gt;\s*&lt;text:p\b[^&gt;]*&gt;/\n    --  /g;  # list items
s/&lt;text:list\b[^&gt;]*&gt;/\n\n/g;       # lists
s/&lt;text:p\b[^&gt;]*&gt;/\n  /g;          # paragraphs
s/&lt;[^&gt;]+&gt;//g;                      # remove all XML tags
s/\n{2,}/\n\n/g;                   # remove multiple blank lines
s/\A\n+//;                         # remove leading blank lines
print "\n", $_, "\n\n";
</pre>

<p>Nun musst Du diese Datei noch ausführbar machen:</p>

<pre>
chmod +x /usr/local/bin/odt-to-txt
</pre>

<p>
  Jetzt kann Dir <code>git diff</code> aufzeigen, was sich in <code>.odt</code> Dateien geändert hat.
</p>

<p>Bilddateien</p>

<p>
  Auf diese Art und Weise kann man ein weiteres, interessantes Problem lösen.
  Das Vergleichen von Bilddateien. Eine Möglichkeit dies zu tun, ist es, JPEG
  Dateien durch einen Filter zu schicken, der ihre EXIF Bildinformationen
  extrahiert. EXIF Bildinformationen sind Metadaten, die den meisten
  Bilddateien beigefügt werden. Wenn Du das Programm <code>exiftool</code> herunterlädst und installierst, kannst Du es benutzen um Deine Bilder in
  einen Text mit diesen Metainformationen umzuwandeln. Damit kann Dir ein Diff
  zumindest eine textuelle Repräsentation aller Veränderungen an der Datei
  anzeigen:
</p>

<pre>
$ echo '*.png diff=exif' &gt;&gt; .gitattributes
$ git config diff.exif.textconv exiftool
</pre>

<p>
  Wenn Du nun ein Bild in Deinem Projekt ersetzt und <code>git diff</code> ausführst, erhälst Du in etwa folgende Ausgabe:
</p>

<pre>
diff --git a/image.png b/image.png
index 88839c4..4afcb7c 100644
--- a/image.png
+++ b/image.png
@@ -1,12 +1,12 @@
 ExifTool Version Number         : 7.74
-File Size                       : 70 kB
-File Modification Date/Time     : 2009:04:17 10:12:35-07:00
+File Size                       : 94 kB
+File Modification Date/Time     : 2009:04:21 07:02:43-07:00
 File Type                       : PNG
 MIME Type                       : image/png
-Image Width                     : 1058
-Image Height                    : 889
+Image Width                     : 1056
+Image Height                    : 827
 Bit Depth                       : 8
 Color Type                      : RGB with Alpha
</pre>

<p>
  Man sieht auf einen Blick, dass sowohl Dateigröße als auch die
  Bildabmessungen verändert wurden.
</p>

<h3>Schlüsselworterweiterung</h3>

<p>
  Entwickler, die an SVN- oder CVS-ähnliche Systeme gewöhnt sind, fragen oft
  nach der Möglichkeit Schlüsselwörter zu erweitern oder zu ersetzen. Mit Git
  ist dies nicht so einfach möglich, da eine Datei nach einem durchgeführten
  Commit nicht mehr verändert werden kann. Die Information über den Commit kann
  also nicht zur Datei hinzugefügt werden, da Git bereits bereits vor dem
  Commit die Prüfsumme berechnet. Jedoch hast Du die Möglichkeit Text
  einzufügen, wenn die Datei ausgecheckt wird und diesen dann wieder entfernen,
  wenn die Datei zu einem Commit hinzugefügt wird. Die Git Attribute bieten
  hierfür zwei Möglichkeiten an.
</p>

<p>
  Zunächst kannst Du die SHA-1 Prüfsumme eines Blobs automatisch in ein <code>$Id$</code> Feld einer Datei einfügen. Wenn Du das folgende Attribut für eine oder eine
  Gruppe von Dateien einstellst, wird Git dieses Feld beim nächsten Checkout
  mit dem SHA-1 Wert dessen Blobs ersetzen. Hierbei ist es wichtig zu beachten,
  dass es die Prüfsumme des Blobs selbst ist, und nicht die des Commits:
</p>

<pre>
$ echo '*.txt ident' &gt;&gt; .gitattributes
$ echo '$Id$' &gt; test.txt
</pre>

<p>
  Wenn Du diese Datei das nächste Mal auscheckst, wird Git den SHA Wert des
  Blobs einfügen:
</p>

<pre>
$ rm test.txt
$ git checkout -- test.txt
$ cat test.txt
$Id: 42812b7653c7b88933f8a9d6cad0ca16714b9bb3 $
</pre>

<p>
  Allerdings ist das Ergebnis nur beschränkt verwertbar. Die SHA Werte als
  solches sind nicht sehr hilfreich, da sie recht zufällig sind und nicht
  festgestellt werden kann ob ein SHA Wert älter oder neuer ist, als der
  andere. In anderen Systemen, wie CVS oder Subversion kann man mit Hilfe der
  Keyword Expansion Datum- und Zeitstempel einfügen.
</p>

<p>
  Wie sich herausstellt, kann man aber seine eigenen Filter schreiben, um bei
  Commits oder Checkouts Schlüsselwörter in Dateien zu ersetzen. In der <code>.gitattributes</code> Datei kann man einen Filter für bestimmte Pfade angeben und dann Skripte
  einrichten, die Dateien kurz vor einem Checkout („smudge“, siehe Abbildung
  7-2) und kurz vor einem Commit („clean“, siehe Abbildung 7-3) modifizieren.
  Diese Filter können eingerichtet werden, um alle möglichen witzigen Dinge zu
  machen.
</p>

<p>
  Insert 18333fig0702.png Abbildung 7-2. Der „smudge“ Filter wird beim Checkout
  ausgeführt.
</p>

<p>
  Insert 18333fig0703.png Abbildung 7-3. Der „clean“ Filter wird beim Transfer
  in die Staging Area ausgeführt.
</p>

<p>
  Die Beschreibung des ersten Commits dieser Funktionalität enthält ein
  einfaches Beispiel, wie man all seinen C Quellcode durch das <code>indent</code> Programm leiten lassen kann, bevor ein Commit gemacht wird. Du kannst dies
  einrichten, indem Du das entsprechende Filterattribut in der <code>.gitattributes</code> Datei auflistest, damit <code>*.c</code> Dateien mit dem „indent“ Programm gefiltert werden:
</p>

<pre>
*.c     filter=indent
</pre>

<p>
  Dann muss Git noch gesagt werden, was der „indent“ Filter bei „smudge“ und
  „clean“ zu tun hat:
</p>

<pre>
$ git config --global filter.indent.clean indent
$ git config --global filter.indent.smudge cat
</pre>

<p>
  Wenn ein Commit Dateien umfasst, die dem Muster <code>*.c</code> entspechen, wird Git diese Dateien vor Ausführung des Commits durch das <code>indent</code> Programm leiten. Werden sie wieder ausgecheckt, so schickt Git sie durch das
  <code>cat</code> Programm. <code>cat</code> ist im Grunde genommen eine Null-Operation: es gibt genau die Daten wieder
  aus, die hereinkommen. Diese Einstellung bewirkt also tatsächlich nur, dass
  alle C Quellcode Dateien vor einem Commit durch den <code>indent</code> Filter bearbeitet werden.
</p>

<p>
  Ein weiteres interessantes Beispiel ermöglicht im Stile von RCS die
  Schlüsselworterweiterung <code>$Date$</code>. Damit dies vernünftig funktioniert, brauchst Du ein kleines Skript, welches
  mit Hilfe des Dateinamen das letzte Commitdatum in diesem Projekt
  herausfindet und dieses Datum in die Datei einfügt. Hierzu ein kleines
  Beispiel als Ruby Skript:
</p>

<pre>
#! /usr/bin/env ruby
data = STDIN.read
last_date = `git log --pretty=format:"%ad" -1`
puts data.gsub('$Date$', '$Date: ' + last_date.to_s + '$')
</pre>

<p>
  Das Skript ermittelt das letzte Commitdatum mittels des Befehls <code>git log</code>, ersetzt jede Zeichenfolge von <code>$Date</code> im Stream stdin mit dem Commitdatum und gibt das Ergebnis wieder aus. Dieses
  Skript sollte auch in der Skriptsprache Deiner Wahl leicht umzusetzen sein.
  Am besten nennst Du dieses Skript <code>expand_date</code> und legst es in Deinem Standard Suchpfad ab. Nun musst Du noch einen Filter
  (nennen wir ihn <code>dater</code>) in Git einrichten, der Dein <code>expand_date</code> Skript benutzt, um die Textdateien beim Checkout zu modifizieren. Zum
  Säubern der Dateien wird beim Commit ein Perl Ausdruck verwendet:
</p>

<pre>
$ git config filter.dater.smudge expand_date
$ git config filter.dater.clean 'perl -pe "s/\\\$Date[^\\\$]*\\\$/\\\$Date\\\$/"'
</pre>

<p>
  Um wieder zum Ursprungszustand zurückzukehren entfernt dieses kurze Perl
  Schnipsel alles was es in einer <code>$Date$</code> Zeichenfolge findet. Jetzt da Dein Filter fertig ist, kannst Du ihn testen
  indem Du eine Datei mit dem <code>$Date$</code> Schlüsselwort erstellst und das entsprechende Git Attribut für diese Datei
  einrichtest:
</p>

<pre>
$ echo '# $Date$' &gt; date_test.txt
$ echo 'date*.txt filter=dater' &gt;&gt; .gitattributes
</pre>

<p>
  Wenn Du diese Änderungen eincheckst und wieder erneut auscheckst, sollte Dein
  Schlüsselwort korrekt ersetzt worden sein:
</p>

<pre>
$ git add date_test.txt .gitattributes
$ git commit -m "Testing date expansion in Git"
$ rm date_test.txt
$ git checkout date_test.txt
$ cat date_test.txt
# $Date: Tue Apr 21 07:26:52 2009 -0700$
</pre>

<p>
  Man kann sehen wie mächtig diese Technik für Deinen Entwickleralltag sein
  kann. Da die <code>.gitattributes</code> Datei ebenfalls im Git Repository verwaltet wird und damit an alle Benutzer
  weitergeben wird, solltest Du vorsichtig mit Filtern umgehen. Denn Dein
  Filterskript (in diesem Fall das Skript <code>dater</code>) liegt nicht unter Versionskontrolle. Deshalb kann es passieren, dass die
  Schlüsselwortersetzung beziehungsweise das Arbeiten mit dem Repository nicht
  bei jedem funktioniert. Beim Entwickeln von Filtern solltest Du deshalb
  darauf achten, dass das Projekt weiterhin benutzt werden kann, auch wenn ein
  Filter einmal fehlschlägt.
</p>

<h3>Exportieren von Repositorys</h3>

<p>
  Git Attribute erlauben auch einige interessante Dinge, wenn Du Dein Projekt
  in ein Archiv exportierst.
</p>

<p>export-ignore</p>

<p>
  Du kannst Git anweisen gewisse Dateien oder Verzeichnisse nicht zu
  exportieren, wenn es ein Archiv erzeugt. Falls es Unterverzeichnisse oder
  Dateien gibt, die Du nicht in Deiner Archivdatei haben willst, aber in Deinem
  Projektrepository, so kannst Du diese Datein mit Hilfe des <code>export-ignore</code> Attributes festlegen.
</p>

<p>
  Nehmen wir zum Beispiel an, Du hast einige Testdateien in einem <code>test/</code> Unterverzeichnis und es macht keinen Sinn, dass diese in einem Tarball
  Export Deines Projekts enthalten sind. In diesem Fall kannst Du die folgende
  Zeile in Deine Git Attribute aufnehmen:
</p>

<pre>
test/ export-ignore
</pre>

<p>
  Wenn Du jetzt <code>git archive</code> ausführst, um einen Tarball Deines Projekts zu erstellen, wird das
  Verzeichnis nicht mit in das Archiv aufgenommen.
</p>

<p>export-subst</p>

<p>
  Auch das einfache Ersetzen von Schlüsselwörtern ist bei einem
  Archivierungsvorgang möglich. Git erlaubt die Zeichenfolge <code>$Format:$</code> mit allen Formatierungsoptionen des Parameters <code>--pretty=format</code> in jeglichen Dateien. Viele der Optionen hast Du bereits in Kapitel 2
  kennengelernt. Wenn Du zum Beispiel eine Datei namens <code>LAST_COMMIT</code> zu Deinem&emsp14;&emsp14;Projekt hinzufügen willst, welche das Datum des
  letzten&emsp14;&emsp14;Commits enthalten soll, dann kannst Du die folgenden
  Befehle ausführen:
</p>

<pre>
$ echo 'Last commit date: $Format:%cd$' &gt; LAST_COMMIT
$ echo "LAST_COMMIT export-subst" &gt;&gt; .gitattributes
$ git add LAST_COMMIT .gitattributes
$ git commit -am 'adding LAST_COMMIT file for archives'
</pre>

<p>
  Nach Ausführung des Befehls <code>git archive</code>, wird die Datei <code>LAST_COMMIT</code> in Deinem Archiv in etwa folgendermaßen aussehen:
</p>

<pre>
$ cat LAST_COMMIT
Last commit date: $Format:Tue Apr 21 08:38:48 2009 -0700$
</pre>

<h3>Merge Strategien</h3>

<p>
  Die Git Attribute ermöglichen es ebenso verschiedene Regeln für das
  Zusammenführen bestimmter Dateien innerhalb Deines Projekts festzulegen. Eine
  besonders nützliche Option ist es, Git so einzustellen, dass es bei
  bestimmten Dateien kein Zusammenführen von Konfliktstellen versucht, sondern
  einfach Deine Version übernimmt und die des anderen verwirft.
</p>

<p>
  Dies ist hilfreich, falls ein Zweig Deines Projekts sehr weit vom Hauptzweig
  abgewichen oder sehr speziell ist, aber Du weiterhin in der Lage sein willst,
  Änderungen daran zurückzuführen und dabei gewisse Dateien zu ignorieren.
  Nehmen wir an Du hast eine Konfigurationsdatei einer Datenbank namens
  database.xml, welche sich in zwei Zweigen unterscheidet. Wenn Du jetzt einen
  Merge von dem anderen Zweig machen möchtest ohne Deine Datenbankdatei
  unbrauchbar zu machen, dann kannst Du folgendes Attribut einrichten:
</p>

<pre>
database.xml merge=ours
</pre>

<p>
  Wenn Du ein Merge des anderen Zweiges machst, werden für die Datei
  database.xml keine Merge-Konflikte auftreten, sondern es wird folgendes
  ausgegeben:
</p>

<pre>
$ git merge topic
Auto-merging database.xml
Merge made by recursive.
</pre>

<p>
  In diesem Fall wird die Datei database.xml aus dem anderen Zweig ignoriert
  und in Deinem Zweig bleibt die Datei im gleichen Zustand wie vor dem Merge.
</p>

<h2>Git Hooks</h2>

<p>
  Genau wie bei vielen anderen Versionskontrollsystemen gibt es auch bei Git
  die Möglichkeit eigene Skripte zu starten, wenn bestimmte, wichtige
  Ereignisse auftreten. Es gibt zwei Gruppen dieser Einschubmethoden: Hooks für
  den Client und Hooks für den Server. Die Hooks für den Client können bei
  Ereignissen, wie zum Beispiel einem Commit oder Merge, eingerichtet werden.
  Die Hooks für den Server können bei Operationen wie den Empfang von
  hochgeladenen Commits, ausgeführt werden. Es gibt viele Möglichkeiten diese
  Hooks sinnvoll einzusetzen. Einige davon werde ich hier vorstellen.
</p>

<h3>Installieren eines Hooks</h3>

<p>
  Sämtliche Hooks werden im <code>hooks</code> Unterverzeichnis des Git Verzeichnisses gespeichert. In den meisten
  Projekten wird das <code>.git/hooks</code> sein. Git installiert in dieses Verzeichnis standardmäßig Beispielskripte.
  Einige davon sind auch ohne Änderung nützlich und sofort einsetzbar.
  Zusätzlich dokumentieren diese Beispiele die Eingabewerte des jeweiligen
  Skripts. Alle Beispiele sind Shellskripte, die hier und da ein Paar Zeilen
  Perl Code enthalten. Prinzipiell sollte aber jedes ausführbare Skript
  funktionieren, wenn es korrekt benannt wird. Du kannst also die Skriptsprache
  Deiner Wahl verwenden, z.B. Ruby oder Python. Die Beispieldateien haben die
  Endung .sample, sie müssen also nur noch umbenannt werden.
</p>

<p>
  Um ein Hook-Skript zu aktivieren, speichere eine entsprechend benannte und
  ausführbare Datei im <code>hooks</code> Unterverzeichnis Deines Git Verzeichnisses. Von diesem Augenblick an sollte
  es ausgeführt werden. Ich werde hier die meisten der wichtigen Hook
  Dateinamen besprechen.
</p>

<h3>Hooks für den Client</h3>

<p>
  Es gibt eine Menge Hooks auf Seiten des Clients. Der folgende Abschnitt teilt
  die Hooks in drei Gruppen auf: Skripte für den Commit Vorgang, Skripte für
  den Arbeitsablauf mit E-Mails und den Rest der Client Skripte.
</p>

<p>Hooks für den Commit Vorgang</p>

<p>
  Die ersten vier Hooks hängen mit dem Commit Prozess zusammen. Der <code>pre-commit</code> Hook wird zuerst ausgeführt, schon bevor Du die Commit Nachricht eingegeben
  hast. Der Hook wird oft benutzt, um den zu versionierenden Zustand des
  Arbeitsverzeichnisses zu prüfen, um festzustellen ob etwas vergessen wurde,
  um sicherzustellen das Tests ausgeführt wurden oder aus irgendeinem anderen
  Grund, der es nötig macht, den Code vor dem Commit zu inspizieren. Wenn das
  entsprechende Skript einen Wert ungleich Null zurückgibt, wird der Commit
  abgebrochen. Auch für die Prüfung, ob Kodierrichtlinien eingehalten wurden
  oder für eine statische Codeanalyse (z.B. mit lint oder einem entsprechenden
  Programm) kann dieses Skript verwendet werden. Das von Git installierte
  Beispielskript prüft zum Beispiel, ob am Zeilenende Leerzeichen vorhanden
  sind. Der Hook kann mit <code>git commit --no-verify</code> auch umgangen werden.
</p>

<p>
  Der <code>prepare-commit-msg</code> Hook wird ausgeführt, bevor der Editor für die Commit Nachricht geöffnet
  wird, aber nachdem die Standardnachricht erstellt wurde. Er erlaubt es die
  Standardnachricht zu modifizieren, bevor der Autor des Commits sie sieht.
  Dieser Hook akzeptiert diverse Optionen: den Pfad der Datei, die die
  bisherige Commit Nachricht enthält, den Typ des Commit und den SHA-1 Hash des
  Commit, falls es sich um ein Korrektur-Commit handelt. Dieser Hook ist
  üblicherweise nicht sehr nützlich bei normalen Commits; er ist eher für
  solche Commits gedacht, bei denen die Standardnachricht automatisch generiert
  wird, wie zum Beispiel vorlagenbasierte Commit Nachrichten, Commits nach
  einem Merge, Commits, die zusammengeführt werden und Korrektur-Commits. Du
  kannst diesen Hook mit einer Commit Vorlage kombinieren, um automatisiert
  Informationen einzufügen.
</p>

<p>
  Der <code>commit-msg</code> Hook akzeptiert einen Parameter, der wiederum der Pfad zu der temporären
  Datei ist, die die momentane Commit Nachricht enthält. Falls dieses Skript
  nicht Null zurückgibt, so wird der Commit abgebrochen. Damit kannst Du die
  Gültigkeit des Projekstatus oder die Commit Nachricht prüfen, bevor ein
  Commit akzeptiert wird. Im letzten Abschnitt dieses Kapitels werde ich
  beschreiben, wie man diesen Hook benutzt, um sicherzustellen, dass Commit
  Nachrichten einem bestimmten Muster entsprechen.
</p>

<p>
  Wenn ein Commit komplett abgeschlossen wurde, wird der <code>post-commit</code> Hook ausgeführt. Er akzeptiert keine Parameter, aber Du kannst den letzten
  Commit einfach mit dem Befehl <code>git log -1 HEAD</code> abfragen. Dieses Skript wird üblicherweise für das Senden von
  Benachrichtigungen oder ähnlichem benutzt.
</p>

<p>
  Diese Skripte für den Commit Prozess können für jeden anderen Arbeitsablauf
  entsprechend angepasst werden. Oft werden sie benutzt um bestimmte Regeln zu
  erzwingen. Dabei ist es wichtig zu wissen, dass diese Skripte beim Klonen
  eines Repositorys nicht mit übertragen werden. Du kannst auf Seiten des
  Servers die Einhaltung von bestimmten Regeln erzwingen indem die
  hochgeladenen Commits abgelehnt werden, wenn sie diesen Prinzipien nicht
  entsprechen. Auf dem Client entscheidet aber der Anwender selber, ob er diese
  Skripte verwendet oder nicht. Dies sind also Skripte, die den Entwicklern
  helfen sollen, und sie müssen von ihnen erstellt und gepflegt werden. Aber
  sie können auch von ihnen jederzeit verändert oder umgangen werden.
</p>

<p>Hooks für den Arbeitsablauf mit E-Mails</p>

<p>
  Für einen E-Mail basierten Arbeitsablauf kannst Du drei Hooks auf dem Client
  einrichten. Sie werden alle bei Ausführung des Befehls <code>git am</code> aufgerufen. Wenn Du also diesen Befehl in Deinem normalen Arbeitsablauf
  nicht verwendest, kann Du guten Gewissens zum nächsten Abschnitt springen.
  Falls Du aber Patches per E-Mail erhälst, die mit <code>git format-patch</code> erstellt wurden, könnten trotzdem einige dieser Skripte nützlich für Dich
  sein.
</p>

<p>
  Der erste Hook, der ausgeführt wird, ist <code>applypatch-msg</code>. Er akzeptiert genau einen Parameter: den Namen der temporären Datei, die
  die vorgegebene Commit Nachricht enthält. Git bricht den Patch ab, falls
  dieses Skript nicht Null zurückgibt. Du kannst dies benutzen um
  sicherzustellen, dass die Commit Nachricht richtig formatiert ist, oder um
  die Nachricht zu standardisieren, indem das Skript sie direkt editiert.
</p>

<p>
  Der nächste Hook, der beim Anwenden von Patches via <code>git am</code> ausgeführt wird, ist <code>pre-applypatch</code>. Er benötigt keine Parameter und wird direkt nach Anwendung des Patches
  ausgeführt. Damit kannst Du den Zustand Deines Projektes noch vor dem
  eigentlich Commit inspizieren. Du kannst mit diesem Skript Tests ablaufen
  lassen oder das Arbeitsverzeichnis anderweitig untersuchen. Falls etwas fehlt
  oder ein Test fehlschlägt, sorgt eine Beenden des Skripts mit einem Wert
  ungleich Null ebenfalls für das Abbrechen des <code>git am</code> Skripts. Es wird also auch kein Commit ausgeführt.
</p>

<p>
  Der letzte Hook, der während der <code>git am</code> Operation ausgeführt wird, ist <code>post-applypatch</code>. Du kannst dies verwenden, um eine Benutzergruppe oder den Autoren des
  Patches darueber zu informieren, dass der Patch übernommen wurde. Der
  eigentliche Patch Vorgang kann mit diesem Skript aber nicht mehr abgebrochen
  werden.
</p>

<p>Weitere Hooks für den Client</p>

<p>
  Der <code>pre-rebase</code> Hook wird ausgeführt, bevor ein Rebase gestartet wird. Durch einen
  Rückgabewert ungleich Null kann der Rebase Vorgang abgebrochen werden. Du
  kannst diesen Hook dazu verwenden um beispielsweise zu verhindern, dass auf
  bereits gepushte Commits ein Rebase durchgeführt wird. Der von Git
  installierte Beispiel-Hook für <code>pre-rebase</code> macht genau das. Allerdings nimmt dieser an, dass der Name des
  veröffentlichten Branches ‚next‘ ist. Du musst wahrscheinlich den Namen durch
  den Deines stabilen, öffentlichen Branches ersetzen.
</p>

<p>
  Nach jedem erfolgreichen <code>git-checkout</code> wird der <code>post-checkout</code> Hook ausgeführt. Du kannst ihn verwenden, um Dein
  Arbeitsverzeichnis&emsp14;&emsp14;für Deine Arbeitsumgebung einzurichten. Das
  kann das Hinzukopieren großer Binärdateien bedeuten, die Du nicht unter
  Versionskontrolle stellen möchtest, das automatisierte Generieren von
  Dokumentation, oder entsprechend ähnliche Aktionen.
</p>

<p>
  Der letzte Hook, den ich vorstellen möchte, ist der <code>post-merge</code> Hook. Er wird nach jedem erfolgreichen Aufruf von <code>merge</code> ausgeführt. Du kannst diesen benutzen, um Daten in Deinem Arbeitsverzeichnis
  wiederherzustellen, die Git nicht unter Versionskontrolle stellen kann. Das
  sind zum Beispiel Berechtigungsdaten. Dieser Hook kann genauso überprüfen, ob
  Dateien, die nicht unter Versionskontrolle stehen, entsprechend in das
  Arbeitsverzeichnis kopiert worden sind, wenn sich dieses ändert.
</p>

<h3>Serverseitige Hooks</h3>

<p>
  Neben den Hooks für den Client, kannst Du als Systemadministrator auch einige
  wichtige Hooks auf Seiten des Servers installieren. Damit kannst Du nahezu
  jede Art von Richtlinie für Dein Projekt erzwingen. Die Skripte werden
  ausgeführt bevor und nachdem ein Push auf den Server durchgeführt wurde. Das
  Skript für den vorgelagerten Hook kann den Push jederzeit abbrechen indem es
  einen Wert ungleich Null zurückgibt. Zusätzlich kann dem Client eine
  Fehlermeldung zurückgeliefert werden. Mit diesen Hooks kannst Du eine
  beliebig komplexe Push Richtlinie umsetzen.
</p>

<p>pre-receive und post-receive</p>

<p>
  Das erste Skript, dass ausgeführt wird, wenn ein Push von einem Client
  empfangen wird, ist <code>pre-receive</code>. Es akzeptiert eine Liste von Referenzen, die über ‚stdin‘ hochgeladen
  werden. Wird es mit einem Wert ungleich Null beendet, so wird keine von ihnen
  akzeptiert. Du kannst diesen Hook benutzen, um sicherzustellen, dass keine
  Pushes durchgeführt werden können, welche nicht einem Fast-Forward
  entsprechen. Ebenso ist es möglich zu Prüfen, ob der Client, die
  entsprechende Berechtigung zum Erstellen, Löschen oder Aktualisieren eines
  Branches hat oder ob er die Berechtigung hat, die jeweiligen Dateien zu
  ändern, die mit dem Push hochgeladen werden.
</p>

<p>
  Der <code>post-receive</code> Hook wird aufgerufen, nachdem der komplette Prozess abgeschlossen ist und
  kann zum Aktualisieren anderer Dienste oder zum Benachrichtigen von Benutzern
  verwendet werden. Er erwartet die gleichen ‚stdin‘ Daten wie <code>pre-receive</code>. Beispielsweise können folgende Aktionen ausgeführt werden: Versand von
  E-Mails an eine vorgefertigte Liste von Personen, Benachrichtigen eins
  Continuous Integration Servers oder Aktualisieren eines
  Issue-Tracking-Werkzeugs (Du kannst sogar die Commit Nachrichten parsen um zu
  prüfen, ob bestimmte Tickets geöffnet, aktualisiert oder geschlossen werden
  müssen). Das Skript kann allerdings den Push Prozess nicht abbrechen und der
  Client bleibt bis zum Abschluss des Skripts mit dem Server verbunden. Du
  solltest deshalb darauf achten, dass Du keinen Vorgang ausführst, der zu viel
  Zeit in Anspruch nimmt.
</p>

<p>update</p>

<p>
  Das Update Skript ist dem <code>pre-receive</code> Skript sehr ähnlich, außer dass es für jeden Branch, den der Client
  aktualisieren will, ausgeführt wird. Wenn der Benutzer des Clients versucht
  mehrere Branches zu pushen, wird <code>pre-receive</code> nur einmalig aufgerufen, wohingegen das Update Skript für jeden einzelnen
  Branch ausgeführt wird. Anstatt von dem Stream stdin zu lesen, akzeptiert
  dieses Skript drei Argumente: der Name der Referenz (Branch), die SHA-1
  Prüfsumme auf die die Referenz vor dem Push zeigt und die SHA-1 Prüfsumme,
  die der Anwender versucht zu pushen. Wenn das Update Skript einen Wert
  ungleich Null zurückgibt, wird der Vorgang nur für diese Referenz
  abgebrochen, die anderen Referenzen werden weiterhin aktualisiert.
</p>

<h2>Beispiel für die Durchsetzung von Richtlinien mit Hilfe von Git</h2>

<p>
  In diesem Abschnitt werden wir die gelernten Dinge verwenden um einen Git
  Arbeitsablauf umzusetzen, der das Format der Commit Nachrichten prüft, nur
  Pushes zulässt, die einem Fast-Forward entsprechen und der es nur einem
  beschränkten Kreis von Nutzern ermöglicht einzelne Unterverzeichnisse
  innerhalb eines Projekts zu modifizieren. Wir werden Client Skripte
  erstellen, die für den Entwickler prüfen, ob seine Pushes abgelehnt werden
  würden und wir werden Server Skripte erstellen, die diese Richtlinien um-
  bzw. durchsetzen.
</p>

<p>
  Ich habe für diese Hooks Ruby verwendet, weil es einerseits meine bevorzugte
  Skriptsprache ist und andererseits weil der resultierende Code nahezu einem
  leicht zu lesenden Pseudo-Code entspricht. Auch wenn Du Ruby normalerweise
  nicht einsetzt, solltest Du deshalb in der Lage sein, meinen Ausführungen zu
  folgen. Jede andere Sprache sollte aber genauso funktionieren. Alle
  Beispielskripte, die standardmäßig in Git enthalten sind, sind entweder Perl
  oder Bash Skripte. Für diese Sprache findest Du also auch genügend Beispiele.
</p>

<h3>Server Hooks</h3>

<p>
  Die gesamten Skripte für den Server gehören in die Update Datei in Deinem
  Hooks Verzeichnis. Die Update Datei wird für jeden Branch, der gepusht wird,
  gestartet und erhält als Parameter die Referenz, die gepusht wird, die alte
  Revision auf der der Branch stand und die neue Revision, die gepusht wird.
  Wenn der Push über SSH ausgeführt wird, hat es auch Zugriff auf den Benutzer
  mit dem der Push durchgeführt wird. Wenn Du den Server so konfiguriert hast,
  dass jeder über einen einzelnen Benutzer (zum Beispiel „git“) über das
  Public-Key Verfahren zugreifen kann, dann wäre es sinnvoll diesem Benutzer
  einen Shell Wrapper einzurichten, der über den öffentlichen Schlüssel die
  Identität feststellt und damit die Umgebungsvariablen für den jeweiligen
  Benutzer setzen kann. In dem Beispiel setze ich voraus, dass der Benutzer,
  der sich verbinden will, in der Umgebungsvariable <code>$USER</code> enthalten ist. Deshalb sammelt das Update Skript erstmal alle benötigten
  Informationen:
</p>

<pre>
#!/usr/bin/env ruby

$refname = ARGV[0]
$oldrev  = ARGV[1]
$newrev  = ARGV[2]
$user    = ENV['USER']

puts "Enforcing Policies... \n(#{$refname}) (#{$oldrev[0,6]}) (#{$newrev[0,6]})"
</pre>

<p>
  Ja, ich verwende globale Variablen. Bitte steinigt mich dafür nicht. Auf
  diese Art und Weise ist es für mich einfacher das Ganze zu demonstrieren.
</p>

<p>Format der Commit Nachricht erzwingen</p>

<p>
  Deine erste Herausforderung wird es sein, sicherzustellen, dass jede Commit
  Nachricht einem bestimmten Format entspricht. Nehmen wir zum Beispiel an,
  dass jeder Commit mit einem Ticket in Deinem Issue-Tracking-System verknüpft
  sein soll. Deshalb soll jede Commit Nachricht diese Referenz in etwa dem
  Format „ref: 1234“ enthalten. Dazu musst Du jeden Commit, der gepusht werden
  soll, prüfen, ob der entsprechende Text enthalten ist. Ist er es nicht, so
  musst Du das entsprechende Skripte mit einem Rückgabewert ungleich Null
  beenden, damit der Push abgelehnt beziehungsweise abgebrochen wird.
</p>

<p>
  Eine Liste aller SHA-1 Prüfsummen, die gepusht werden sollen, erhälst Du,
  indem Du die Werte <code>$newrev</code> und <code>$oldrev</code> an das Git Kommando <code>git rev-list</code> übergibst (Dieser Befehl gehört zu den Low-Level Funktionen von Git. Im
  Englischen werden diese auch als „plumbing“ Befehle bezeichnet). Der Befehl
  entspricht dem <code>git log</code> Kommando, gibt aber im Gegensatz zu diesem nur die SHA-1 Prüfsummen und
  keine weitere Informationen aus. Um eine Liste aller SHA-1 Prüfsummen
  zwischen zwei Commits zu erhalten, musst Du in etwa folgendes eingeben:
</p>

<pre>
$ git rev-list 538c33..d14fc7
d14fc7c847ab946ec39590d87783c69b031bdfb7
9f585da4401b0a3999e84113824d15245c13f0be
234071a1be950e2a8d078e6141f5cd20c1e61ad3
dfa04c9ef3d5197182f13fb5b9b1fb7717d2222a
17716ec0f1ff5c77eff40b7fe912f9f6cfd0e475
</pre>

<p>
  Du kannst nun durch diese Liste iterieren und für jeden SHA-1 Commit die
  entsprechende Commit Nachricht anfordern und diese mit Hilfe eines regulären
  Ausdrucks auf das jeweilige Format prüfen.
</p>

<p>
  Um dies durchführen zu können, benötigst Du das Wissen, wie man an die Commit
  Nachricht eines einzelnen Commits herankommt. Um die Rohdaten eines Commits
  zu erhalten, kannst Du eine andere Low-Level Funktion von Git verwenden,
  nämlich <code>git cat-file</code>. Weitere Low-Level Funktionen werde ich in Kapitel 9 näher erläutern, aber
  hier reicht es erst einmal, wenn Du das Kommando einfach mal ausprobierst:
</p>

<pre>
$ git cat-file commit ca82a6
tree cfda3bf379e4f8dba8717dee55aab78aef7f4daf
parent 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
author Scott Chacon &lt;schacon@gmail.com&gt; 1205815931 -0700
committer Scott Chacon &lt;schacon@gmail.com&gt; 1240030591 -0700

changed the version number
</pre>

<p>
  Um die Commit Nachricht auf Basis der SHA-1 Prüfsumme zu extrahieren, gibt es
  eine einfache Möglichkeit. Dazu musst Du die Position der ersten leeren Zeile
  bestimmen. Der gesamte Text nach dieser leeren Zeile entspricht der Commit
  Nachricht. Mit dem <code>sed</code> Befehl funktioniert das unter Unix Systemen ganz einfach:
</p>

<pre>
$ git cat-file commit ca82a6 | sed '1,/^$/d'
changed the version number
</pre>

<p>
  Damit sollte es Dir auf einfache Art und Weise möglich sein, jede einzelne
  Commit Nachricht eines Commits, welcher gepusht werden soll, zu prüfen. Du
  kannst den Push abbrechen, sollte einer der Nachrichten nicht dem gewünschten
  Format entsprechen. Um ihn abzubrechen reicht es, wenn der Rückgabewert des
  Skripts ungleich Null ist. Zusammengefasst ergibt sich die folgende Methode:
</p>

<pre>
$regex = /\[ref: (\d+)\]/

# enforced custom commit message format
def check_message_format
  missed_revs = `git rev-list #{$oldrev}..#{$newrev}`.split("\n")
  missed_revs.each do |rev|
    message = `git cat-file commit #{rev} | sed '1,/^$/d'`
    if !$regex.match(message)
      puts "[POLICY] Your message is not formatted correctly"
      exit 1
    end
  end
end
check_message_format
</pre>

<p>
  Wenn Du diesen Auszug in Dein <code>update</code> Skript einbaust, wird jeder Push abgelehnt, der eine Commit Nachricht
  enthält, die nicht Deinen Regeln entspricht.
</p>

<p>Einrichten eines benutzerspezifischen ACL-Systems</p>

<p>
  Nehmen wir einmal an, dass Du für Deine Projekte ein Mechanismus einrichten
  willst, der festlegt, wer auf welche Teile Deines Projekts pushen kann. Mit
  Hilfe einer Zugriffssteuerungsliste (ACL – Access Control List) ist so etwas
  möglich. Manche Benutzer sollen vollen Zugriff auf das gesamte Repository
  haben, andere widerrum dürfen nur auf bestimmte Unterverzeichnisse oder
  spezielle Dateien pushen. Um diese Regeln durchzusetzen werden wir eine Datei
  mit dem Namen <code>acl</code> erstellen und diese im Bare Repository auf Deinem Git Server ablegen.
  Außerdem werden wir den <code>update</code> Hook so anpassen, dass dieser die erstellten Regeln prüft und bestimmt, ob
  die jeweilige Aktion vom jeweiligen Benutzer ausgeführt werden darf. Dazu
  muss der Hook alle Commits, die gepusht werden, prüfen.
</p>

<p>
  Der erste Schritt ist das Erstellen einer ACL. In unserem Beispiel verwenden
  wir ein Format, welches der CVS ACL sehr ähnlich ist. Jede Zeile ist nach dem
  selben Format aufgebaut. Das erste Feld einer Zeile enthält entweder <code>avail</code> oder <code>unavail</code>. Das nächste Feld ist ein kommaseparierte Liste aller User, auf die die
  Regel zutrifft. Das letzte Feld enthält den Pfad auf welche die Regel
  zutrifft (ein leeres Feld bedeutet in diesem Fall freien Zugriff). Alle
  Felder werden durch einen senkrechten Strich (<code>|</code>, auch Pipe genannt) getrennt.
</p>

<p>
  In unserem Beispiel gibt es ein paar Administratoren, ein paar Leute, die
  sich um die Dokumentation im Verzeichnis <code>doc</code> kümmern, und einen Entwickler, der nur auf das <code>lib</code> und das <code>test</code> Verzeichnis zugreifen darf. In diesem Fall sollte die ACL Datei etwa
  folgendermaßen aussehen:
</p>

<pre>
avail|nickh,pjhyett,defunkt,tpw
avail|usinclair,cdickens,ebronte|doc
avail|schacon|lib
avail|schacon|tests
</pre>

<p>
  Als erstes müssen wir die Daten in eine Struktur bringen, die wir einfach
  weiterverwenden können. Um das ganze Beispiel einfach zu halten, erzwingen
  wir hier nur die <code>avail</code> Direktive. Die folgende Funktion erzeugt ein assoziatives Array, in dem der
  Benutzername als Schlüssel verwendet wird. Der jeweilige Wert ist ein Array
  von Dateipfaden, auf die der Benutzer Zugriffsrechte besitzt.
</p>

<pre>
def get_acl_access_data(acl_file)
  # read in ACL data
  acl_file = File.read(acl_file).split("\n").reject { |line| line == '' }
  access = {}
  acl_file.each do |line|
    avail, users, path = line.split('|')
    next unless avail == 'avail'
    users.split(',').each do |user|
      access[user] ||= []
      access[user] &lt;&lt; path
    end
  end
  access
end
</pre>

<p>
  Übergibt man der Funktion <code>get_acl_access_data</code> die oben overgestellte ACL wird eine Datenstruktur zurückgegeben, die etwa
  folgendermaßen aussieht:
</p>

<pre>
{"defunkt"=&gt;[nil],
 "tpw"=&gt;[nil],
 "nickh"=&gt;[nil],
 "pjhyett"=&gt;[nil],
 "schacon"=&gt;["lib", "tests"],
 "cdickens"=&gt;["doc"],
 "usinclair"=&gt;["doc"],
 "ebronte"=&gt;["doc"]}
</pre>

<p>
  Nachdem wir auf diese Weise die jeweiligen Zugriffsrechte bestimmt haben,
  müssen wir noch rausfinden, welche Verzeichnisse bei den gepushten Commits
  geändert werden. Nur so können wir sicherstellen, dass ein Benutzer die
  entsprechenden Zugriffsrechte für das jeweilige Verzeichnis hat.
</p>

<p>
  Mit Hilfe des <code>git log</code> Befehls und der Option <code>--name-only</code> findet man sehr leicht heraus, welche Dateien in einem einzelnen Commit
  geändert wurden (dies haben wir bereits im Kapitel 2 vorgestellt):
</p>

<pre>
$ git log -1 --name-only --pretty=format:'' 9f585d

README
lib/test.rb
</pre>

<p>
  Wenn wir nun die Liste der geänderten Dateien, mit der ACL Struktur, die <code>get_acl_access_data</code> zurückliefert, vergleichen, kann man ganz einfach herausfinden, ob der
  Benutzer das Recht hat, alle seine Commits zu pushen:
</p>

<pre>
# only allows certain users to modify certain subdirectories in a project
def check_directory_perms
  access = get_acl_access_data('acl')

  # see if anyone is trying to push something they can't
  new_commits = `git rev-list #{$oldrev}..#{$newrev}`.split("\n")
  new_commits.each do |rev|
    files_modified = `git log -1 --name-only --pretty=format:'' #{rev}`.split("\n")
    files_modified.each do |path|
      next if path.size == 0
      has_file_access = false
      access[$user].each do |access_path|
        if !access_path || # user has access to everything
          (path.index(access_path) == 0) # access to this path
          has_file_access = true
        end
      end
      if !has_file_access
        puts "[POLICY] You do not have access to push to #{path}"
        exit 1
      end
    end
  end
end

check_directory_perms
</pre>

<p>
  Ich hoffe Du kannst dem Skript leicht folgen. Mit dem Befehl <code>git rev-list</code> erhälst Du eine Liste aller Dateien, die gepusht werden. Danach bestimmen
  wir für jeden Commit, welche Dateien geändert wurden und prüfen, ob der
  Benutzer auf diese Pfade zugreifen darf. Die Ruby-Zeile <code>path.index(access_path) == 0</code>, die vielleicht nicht so einfach zu verstehen ist, liefert true zurück, wenn
  path mit der gleichen Zeichenfolge beginnt, wie <code>access_path</code>. Das stellt sicher, dass <code>access_path</code> nicht nur innerhalb eines erlaubten Pfads als Zeichenfolge enthalten ist,
  sondern das wirklich der Anfang der Zeichenketten verglichen wird.
</p>

<p>
  Ab jetzt haben alle Benutzer nur für die jeweils freigegebenen Verzeichnisse
  Zugriffsrechte und es ist sichergestellt, dass keine falsch formatierten
  Commit-Nachrichten gepusht werden können.
</p>

<p>Verweigern von Pushes, welche nicht einem Fast-Forward entsprechen</p>

<p>
  Nun müssen wir unser System nur noch so einrichten, dass es nur Fast-Forward
  Push-Operationen zulässt. Man verwendet dafür die <code>receive.denyDeletes</code> und <code>receive.denyNonFastForwards</code> Konfigurationsparameter. Das gleiche Ergebnis kann man aber auch über einen
  Hook erreichen und diesen kann man dann so konfigurieren, dass die Regeln nur
  für bestimmte Benutzer gelten.
</p>

<p>
  Um herauszufinden, ob es sich um einen Fast-Forward handelt, müssen wir
  prüfen, ob alle Commits, die ausgehend von der letzten Revision erreichbar
  sind, auch von der neuen Revision aus erreichbar sind. Gibt es einen Commit
  auf den das nicht zutrifft, so war der Push kein Fast-Forward und wir
  verweigern ihn:
</p>

<pre>
# enforces fast-forward only pushes
def check_fast_forward
  missed_refs = `git rev-list #{$newrev}..#{$oldrev}`
  missed_ref_count = missed_refs.split("\n").size
  if missed_ref_count &gt; 0
    puts "[POLICY] Cannot push a non fast-forward reference"
    exit 1
  end
end

check_fast_forward
</pre>

<p>
  Das war es. Jetzt sollte alles eingerichtet sein. Wenn Du jetzt noch den
  Befehl <code>chmod u+x .git/hooks/update</code> für die Datei ausführst, in die Du den obigen Code eingefügt hast, und dann
  einen Push ausführst, welcher keinem Fast-Forward entspricht, erhälst Du in
  etwa folgende Ausgabe:
</p>

<pre>
$ git push -f origin master
Counting objects: 5, done.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 323 bytes, done.
Total 3 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
Enforcing Policies...
(refs/heads/master) (8338c5) (c5b616)
[POLICY] Cannot push a non fast-forward reference
error: hooks/update exited with error code 1
error: hook declined to update refs/heads/master
To git@gitserver:project.git
 ! [remote rejected] master -&gt; master (hook declined)
error: failed to push some refs to 'git@gitserver:project.git'
</pre>

<p>
  Lass uns die Ausgabe etwas genauer anschauen, denn sie enthält ein paar
  interessante Dinge. An Hand der folgenden Zeile erkennst Du, wenn der Hook
  gestartet wird.
</p>

<pre>
Enforcing Policies...
(refs/heads/master) (8338c5) (c5b616)
</pre>

<p>
  Bitte beachte, dass wir diesen Text beim Start des <code>update</code>-Skripts auf stdout ausgegeben haben. Es ist wichtig zu wissen, dass alles
  was Dein Skript auf stdout ausgibt, auf den Client übertragen wird und dort
  ausgegeben wird.
</p>

<p>Als nächstes haben wir da noch die folgende Fehlermeldung.</p>

<pre>
[POLICY] Cannot push a non fast-forward reference
error: hooks/update exited with error code 1
error: hook declined to update refs/heads/master
</pre>

<p>
  Die erste Zeile hast Du innerhalb des Skripts ausgegeben. Die anderen zwei
  stammen von Git und teilen Dir mit, dass Dein <code>update</code>-Skript einen Rückgabewert ungleich Null zurückgegeben hat und das der Push
  verweigert wird. Als Letztes schauen wir uns noch die folgenden Zeilen an:
</p>

<pre>
To git@gitserver:project.git
 ! [remote rejected] master -&gt; master (hook declined)
error: failed to push some refs to 'git@gitserver:project.git'
</pre>

<p>
  Du siehst dort eine „remote rejected“ Nachricht für jede Referenz, die Dein
  Hook verweigert hat. Zusätzlich wird dort angegeben, aus welchem Grund der
  Push verweigert wurde. In diesem Fall hat der Hook den Push verweigert.
</p>

<p>
  Wenn in einem Deiner Commits die Refernez zu dem Issue-Tracking-System fehlt,
  wird die folgende von Dir festgelegte Fehlermeldung ausgegeben.
</p>

<pre>
[POLICY] Your message is not formatted correctly
</pre>

<p>
  Auch wenn jemand in einem Commit eine Datei geändert hat, die er eigentlich
  nicht ändern hätte dürfen, und dann versucht diesen Commit zu pushen, wird
  eine ähnliche Fehlermeldung ausgegeben. Wenn zum Beispiel einer der Jungs und
  Mädels aus dem Dokumentationsteam versucht einen Commit zu pushen, der
  irgendeine Änderung im Verzeichnis <code>lib</code> enthält, wird diesen die folgende Meldung angezeigt:
</p>

<pre>
[POLICY] You do not have access to push to lib/test.rb
</pre>

<p>
  Von nun an wird Dein Repository immer in einem ordentlichen Zustand sein.
  Niemand kann Dein Repository durcheinanderbringen oder eine Commit-Nachricht
  einbringen, die nicht Deinen Vorgaben entspricht. Vorausgesetzt das <code>update</code>-Skript ist vorhanden und ausführbar.
</p>

<h3>Client Hooks</h3>

<p>
  Allerdings hat unser strenger <code>update</code>-Hook auch einen Nachteil. Du kannst Dich schon mal auf das unvermeidliche
  Jammern Deiner Mitarbeiter einstellen, wenn diese ihre Commits nicht pushen
  können, weil sie verweigert werden. Wenn Du deren mit viel Mühe erstellte
  Arbeit in letzter Minute ablehnst, kann das für die Benutzer extrem
  frustrierend und verwirrend sein. Dazu kommt noch, dass diese ihre Historie
  ändern müssen um das ganze zu korrigieren. Und das ist nicht immer etwas für
  schwache Nerven.
</p>

<p>
  Um dieses Dilemma zu vermeiden, ist es sinnvoll Deinen Mitarbeiter eine
  Handvoll Client Hooks zur Verfügung zu stellen, die darauf hinweisen, dass
  der gerade durchgeführte Commit wahrscheinlich vom Server verweigert wird.
  Auf diese Art und Weise können Deine Mitarbeiter ihre Arbeit noch korrigieren
  bevor sie sie einchecken. Zu diesem Zeitpunkt sind die Probleme meistens noch
  einfacher zu lösen. Da die Hooks während des Klonvorgangs nicht mitübertragen
  werden, musst Du diese auf andere Weise zur Verfügung stellen. Die Benutzer
  müssen diese Hooks dann auch noch in ihr <code>.git/hooks</code>-Verzeichnis kopieren und ausführbar machen. Du kannst die Hooks auch in
  Deinem Projekt oder in einem separaten Projekt verwalten und verteilen.
  Allerdings gibt es keine Möglichkeit, dass diese automatisch eingerichtet
  werden. Dies muss vom Nutzer selber durchgeführt werden.
</p>

<p>
  Als erstes fangen wir damit an, die Commit-Nachrichten beim Einchecken zu
  prüfen. Damit ist sichergestellt, dass Dein Server die Commits und damit die
  Änderungen nicht ablehnt, weil sie eine falsch formatierte Commit-Nachricht
  enthalten. Um dies sicherzustellen, kannst Du den <code>commit-msg</code>-Hook einrichten. Wenn Du in diesem die Nachricht aus der im ersten Argument
  übergebenen Datei ausliest und mit Deinem Muster vergleichst, kannst Du Git
  dazu bringen, dass der Commit abgebrochen wird, wenn das Muster nicht passt:
</p>

<pre>
#!/usr/bin/env ruby
message_file = ARGV[0]
message = File.read(message_file)

$regex = /\[ref: (\d+)\]/

if !$regex.match(message)
  puts "[POLICY] Your message is not formatted correctly"
  exit 1
end
</pre>

<p>
  Wenn dieses Skript an der richtigen Stelle (<code>.git/hooks/commit-msg</code>) liegt und ausführbar ist und ein Commit durchgeführt wird, welcher nicht
  korrekt formatiert ist, wirst Du folgende Ausgabe sehen:
</p>

<pre>
$ git commit -am 'test'
[POLICY] Your message is not formatted correctly
</pre>

<p>
  In diesem Fall wurde der Commit nicht durchgeführt. Wenn die Commit-Nachricht
  allerdings richtig formatiert ist, erlaubt Git den Commit:
</p>

<pre>
$ git commit -am 'test [ref: 132]'
[master e05c914] test [ref: 132]
 1 files changed, 1 insertions(+), 0 deletions(-)
</pre>

<p>
  Als nächstes möchten wir sicherstellen, dass Dateien nur von den Personen
  geändert werden, die diese auch ändern dürfen. Dazu verwenden wir wieder die
  Zugriffssteuerungsliste. Wenn Dein lokales <code>.git</code>-Verzeichnis eine Kopie der ACL Datei enthält, die wir vorher erstellt haben,
  kann das folgende <code>pre-commit</code>-Skript dafür sorgen, dass die Regeln eingehalten werden.
</p>

<pre>
#!/usr/bin/env ruby

$user    = ENV['USER']

# [ insert acl_access_data method from above ]

# only allows certain users to modify certain subdirectories in a project
def check_directory_perms
  access = get_acl_access_data('.git/acl')

  files_modified = `git diff-index --cached --name-only HEAD`.split("\n")
  files_modified.each do |path|
    next if path.size == 0
    has_file_access = false
    access[$user].each do |access_path|
    if !access_path || (path.index(access_path) == 0)
      has_file_access = true
    end
    if !has_file_access
      puts "[POLICY] You do not have access to push to #{path}"
      exit 1
    end
  end
end

check_directory_perms
</pre>

<p>
  Das vorgestellte Skript entspricht nahezu dem Skript, welches wir für den
  Server erstellt haben. Bis auf zwei wichtige Ausnahmen. Erstens, die ACL
  Datei befindet sich an einem anderen Speicherort, da das Skript ausgehend von
  Deinem Arbeitsverzeichnis und nicht ausgehend von Deinem Git-Verzeichnis
  ausgeführt wird. Aus diesem Grund muss der Pfad zu der ACL Datei von
</p>

<pre>
access = get_acl_access_data('acl')
</pre>

<p>nach</p>

<pre>
access = get_acl_access_data('.git/acl')
</pre>

<p>geändert werden.</p>

<p>
  Der andere wichtige Unterschied besteht darin, auf welche Art und Weise Du
  eine Liste der geänderten Dateien erhälst. Auf dem Server haben wir die
  Möglichkeit die Commits zu durchsuchen. Diese Möglichkeit haben wir beim
  Client nicht, da der Commit noch gar nicht ausgeführt wurde. Deswegen müssen
  wir die Dateien aus der Staging Area prüfen. Statt
</p>

<pre>
files_modified = `git log -1 --name-only --pretty=format:'' #{ref}`
</pre>

<p>musst Du folgende Zeile verwenden:</p>

<pre>
files_modified = `git diff-index --cached --name-only HEAD`
</pre>

<p>
  Das sind die einzigen Unterschiede, ansonsten funktioniert das Skript auf die
  gleiche Art und Weise. Ein Nachteil besteht darin, dass davon ausgegangen
  wird, dass das Skript mit dem gleichen Benutzer ausgeführt wird, wie die
  Commits auf den Remote gepusht werden. Wenn sich diese unterscheiden, muss
  die <code>$user</code>-Variable manuell angepasst werden.
</p>

<p>
  Im letzten Schritt müssen wir noch prüfen, ob versucht wird einen Push
  durchzuführen, der keinem Fast-Forward entspricht. Das kommt normalerweise
  aber nicht so oft vor. Dazu muss entweder ein Rebase für Commits durchgeführt
  werden, die bereits gepusht wurden oder es muss ein lokaler Branch gepusht
  werden, dessen Name bereits auf dem Remote vorhanden ist und eine andere
  Historie aufweist.
</p>

<p>
  Da der Server bereits jeden Push ablehnt, der nicht einem Fast-Forward
  entspricht und alle Push verweigert werden, die die Historie ändern würden,
  kann man jetzt nur noch prüfen, ob der Benutzer einen Rebase für bereits
  gepushte Commits durchführt.
</p>

<p>
  Hier möchte ich ein Beispiel <code>pre-rebase</code>-Skript vorstellen, welches diese Prüfung vornimmt. Es bestimmt eine Liste
  aller Commits, die neu geschrieben werden und prüft, ob diese bereits auf
  irgendeinem Remote vorhanden sind. Wenn dies der Fall ist, wird der Rebase
  abgebrochen:
</p>

<pre>
#!/usr/bin/env ruby

base_branch = ARGV[0]
if ARGV[1]
  topic_branch = ARGV[1]
else
  topic_branch = "HEAD"
end

target_shas = `git rev-list #{base_branch}..#{topic_branch}`.split("\n")
remote_refs = `git branch -r`.split("\n").map { |r| r.strip }

target_shas.each do |sha|
  remote_refs.each do |remote_ref|
    shas_pushed = `git rev-list ^#{sha}^@ refs/remotes/#{remote_ref}`
    if shas_pushed.split("\n").include?(sha)
      puts "[POLICY] Commit #{sha} has already been pushed to #{remote_ref}"
      exit 1
    end
  end
end
</pre>

<p>
  Das Skript verwendet eine Syntax, die wir bereits im Kapitel 6.1 verwendet
  haben. Man erhält eine Liste aller Commits, die bereits gepusht wurden, wenn
  folgender Befehl ausgeführt wird:
</p>

<pre>
git rev-list ^#{sha}^@ refs/remotes/#{remote_ref}
</pre>

<p>
  Die <code>SHA^@</code>-Syntax gibt an, dass alle Eltern-Commits miteinbezogen werden sollen. Man
  sucht auf diese Art und Weise nach allen Commits, die ausgehend vom letzten
  auf dem Server vorhandenen Commit, erreichbar sind und nach allen Commits,
  die ausgehend von dem letzten zu pushenden Commit, nicht erreichbar sind.
</p>

<p>
  Diese Methode ist allerdings auch sehr langsam und meistens auch unnötig.
  Wenn ein Push ohne die Option <code>-f</code> ausgeführt wird und es sich um einen Push handelt, der keinem Fast-Forward
  entspricht, wird der Server eine Warnung ausgeben und den Push nicht
  akzeptieren. Allerdings ist diese Methode eine interessante Übung und kann
  zumindest in der Theorie verhindern, dass ein Rebase durchgeführt wird, der
  später wieder rückgängig gemacht werden müsste.
</p>

<h2>Zusammenfassung</h2>

<p>
  In diesem Kapitel hast Du die wichtigsten Möglichkeiten kennengelernt, wie Du
  Deinen Git Client und Git Server an Deine gewohnte Arbeitsweise und Projekte
  anpassen kannst. Wir haben eine große Auswahl an Konfigurationsparametern,
  dateibasierten Attributen und Hooks vorgestellt. Außerdem haben wir einen
  Server eingerichtet, der dafür sorgt, dass Deine vorgegebenen Richtlinien
  eingehalten werden. Du solltest jetzt in der Lage sein, Git an nahezu jeden
  Workflow anzupassen, den Du Dir vorstellen kannst.
</p>

<h1>Git und andere Versionsverwaltungen</h1>

<p>
  Leider ist die Welt nicht perfekt. Normalerweise kannst Du nicht bei jedem
  Deiner Projekte sofort auf Git umsteigen. Manchmal musst Du in einem Deiner
  Projekte irgendeine andere Versionsverwaltung nutzen, ziemlich oft ist das
  Subversion. Im ersten Teil dieses Kapitels werden wir das bidirektionale
  Gateway zwischen Git und Subversion kennenlernen: <code>git svn</code>.
</p>

<p>
  Manchmal kommst Du an den Zeitpunkt, zu dem Du ein bestehendes Projekt zu Git
  konvertieren willst. Der zweite Teil dieses Kapitels zeigt Dir, wie Du Dein
  Projekt zu Git migrieren kannst. Zunächst behandeln wir Subversion, dann
  Perforce und zum Schluss verwenden wir ein angepasstes Import-Skript, um
  einen nicht standard-mäßigen Import abzudecken.
</p>

<h2>Git und Subversion</h2>

<p>
  Gegenwärtig verwenden die meisten Open-Source-Entwicklungsprojekte und eine
  große Anzahl von Projekten in Unternehmen Subversion, um ihren Quellcode zu
  verwalten. Es ist die populärste Open-Source-Versionsverwaltung und wird seit
  fast einem Jahrzehnt eingesetzt. In vielen Bereichen ähnelt es CVS, das
  vorher der König der Versionsverwaltungen war.
</p>

<p>
  Eines der großartigen Features von Git ist die bi-direktionale Brücke zu
  Subversion: <code>git svn</code>. Dieses Tool ermöglicht es, Git als ganz normalen Client für einen
  Subversion-Server zu benutzen, sodass Du alle lokalen Features von Git nutzen
  kannst und Deine Änderungen dann auf einen Subversion-Server pushen kannst,
  so als ob Du Subversion lokal nutzen würdest. Das bedeutet, dass Du lokale
  Branches anlegen kannst, mergen, die staging area, rebasing, cherry-picking
  etc. verwenden, während Deine Kollegen weiterhin auf ihre angestaubte Art und
  Weise arbeiten. Das ist eine gute Gelegenheit, um Git in einem Unternehmen
  einzuführen und Deinen Entwickler-Kollegen dabei zu helfen, effizienter zu
  werden während Du an der Unterstützung arbeitest, die Infrastruktur so
  umzubauen, dass Git voll unterstützt wird. Die Subversion-Bridge von Git ist
  quasi die Einstiegsdroge in die Welt der verteilten
  Versionsverwaltungssysteme (distributed version control systems, DVCS).
</p>

<h3>git svn</h3>

<p>
  Das Haupt-Kommando in Git für alle Kommandos der Subversion Bridge ist <code>git svn</code>. Dieser Befehl wird allen anderen vorangestellt. Er kennt zahlreiche
  Optionen, daher werden wir jetzt die gebräuchlichsten zusammen anhand von ein
  paar Beispielen durchspielen.
</p>

<p>
  Es ist wichtig, dass Du im Hinterkopf behältst, dass Du mit dem Befehl <code>git svn</code> mit Subversion interagierst, einem System, das nicht ganz so fortschrittlich
  ist wie Git. Obwohl Du auch dort ganz einfach Branches erstellen und wieder
  zusammenführen kannst, ist es üblicherweise am einfachsten, wenn Du die
  History so geradlinig wie möglich gestaltest, indem Du ein rebase für Deine
  Arbeit durchfürst und es vermeidest, zum Beispiel mit einem entfernten
  Git-Repository zu interagieren.
</p>

<p>
  Du solltest auf keinen Fall Deine History neu schreiben und dann versuchen,
  die Änderungen zu publizieren. Bitte schicke Deine Änderungen auch nicht
  zeitgleich dazu an ein anderes Git-Repository, in dem Du mit Deinen Kollegen
  zusammenarbeitest, die bereits Git nutzen. Subversion kennt nur eine einzige
  lineare History für das gesamte Repository und da kommt man schnell mal
  durcheinander. Wenn Du in einem Team arbeitest, in dem manche Deiner Kollegen
  SVN und andere schon Git nutzen, dann solltest Du sicherstellen, dass Ihr
  alle einen SVN-Server zur Zusammenarbeit nutzt, das macht Dein Leben deutlich
  einfacher.
</p>

<h3>Installation</h3>

<p>
  Um dieses Feature zu demonstrieren, brauchst Du zunächst ein typisches
  SVN-Repository, in dem Du Schreibzugriff hast. Wenn Du die folgenden
  Beispiele selbst ausprobieren willst, brauchst Du eine beschreibbare Kopie
  meines Test-Repositories. Das geht ganz einfach mit einem kleinen Tool namens
  <code>svnsync</code>, das mit den letzten Subversion-Versionen (ab Version 1.4) mitgeliefert
  wird. Für diese Test habe ich ein neues Subversion Repository auf Google Code
  angelegt, das einen Teil aus dem <code>protobuf</code>-Projekts kopiert, einem Tool, das Datenstrukturen für die Übertragung über
  ein Netzwerk umwandelt.
</p>

<p>
  Zunächst einmal musst Du ein neues lokales Subversion-Repository anlegen:
</p>

<pre>
$ mkdir /tmp/test-svn
$ svnadmin create /tmp/test-svn
</pre>

<p>
  Anschließend gibst Du allen Usern die Möglichkeit, die revprops zu ändern. Am
  einfachsten geht das, indem wir ein pre-revprop-change Skript erstellen, das
  immer 0 zurückgibt:
</p>

<pre>
$ cat /tmp/test-svn/hooks/pre-revprop-change
#!/bin/sh
exit 0;
$ chmod +x /tmp/test-svn/hooks/pre-revprop-change
</pre>

<p>
  Jetzt kannst Du dieses Projekt auf Deinen lokalen Rechner mit einem Aufruf
  von <code>svnsync init</code> synchronisieren. Als Optionen gibst Du das Ziel- und das Quell-Repository
  an.
</p>

<pre>
$ svnsync init file:///tmp/test-svn http://progit-example.googlecode.com/svn/
</pre>

<p>
  Das richtet die Properties ein, um die Synchronisierung laufen zu lassen. Nun
  kannst Du den Code klonen:
</p>

<pre>
$ svnsync sync file:///tmp/test-svn
Committed revision 1.
Copied properties for revision 1.
Committed revision 2.
Copied properties for revision 2.
Committed revision 3.
...
</pre>

<p>
  Obwohl diese Operation möglicherweise nur ein paar wenige Minuten in Anspruch
  nimmt, wird das Kopieren des Quell-Repositories von einem entfernten
  Repository in ein lokales fast eine Stunde dauern, auch wenn weniger als 100
  Commits getätigt wurden. Subversion muss jede Revision einzeln klonen und sie
  dann in ein anderes Repository schieben – das ist zwar ziemlich ineffizient,
  aber für uns der einfachste Weg.
</p>

<h3>Die ersten Schritte (Getting Started)</h3>

<p>
  Jetzt, da wir ein beschreibbares Subversion Repository haben, können wir mit
  einem typischen Workflow loslegen. Du beginnst mit dem <code>git svn clone</code>Kommando, das ein komplettes Subversion-Repository in ein lokales
  Git-Repository importiert. Denk daran, dass Du <code>file:///tmp/test-svn</code> im folgenden Beispiel mit der URL Deines eigenen Subversion-Repositorys
  ersetzt, wenn Du den Import für ein real existierendes Subversion-Repository
  durchführen willst.
</p>

<pre>
$ git svn clone file:///tmp/test-svn -T trunk -b branches -t tags
Initialized empty Git repository in /Users/schacon/projects/testsvnsync/svn/.git/
r1 = b4e387bc68740b5af56c2a5faf4003ae42bd135c (trunk)
      A    m4/acx_pthread.m4
      A    m4/stl_hash.m4
...
r75 = d1957f3b307922124eec6314e15bcda59e3d9610 (trunk)
Found possible branch point: file:///tmp/test-svn/trunk =&gt; \
    file:///tmp/test-svn /branches/my-calc-branch, 75
Found branch parent: (my-calc-branch) d1957f3b307922124eec6314e15bcda59e3d9610
Following parent with do_switch
Successfully followed parent
r76 = 8624824ecc0badd73f40ea2f01fce51894189b01 (my-calc-branch)
Checked out HEAD:
 file:///tmp/test-svn/branches/my-calc-branch r76
</pre>

<p>
  Hier werden für die angegebene URL eigentlich zwei Befehle ausgeführt, <code>git svn init</code> und anschließend <code>git svn fetch</code>. Das kann auch eine Weile dauern. Das Testprojekt hat nur etwa 75 Commits
  und die Codebase ist nicht so groß, daher benötigen wir nur ein paar Minuten.
  Da Git aber jede Version einzeln auschecken muss, kann es unter Umständen
  Stunden oder gar Tage dauern, bis die Ausführung des Befehls fertig ist.
</p>

<p>
  Die Parameter <code>-T trunk -b branches -t tags</code> teilen Git mit, dass das Subversion-Repository den normalen Konventionen
  bezüglich Branching und Tagging folgt. Wenn Du Deinen Trunk, Deine Branches
  oder Deine Tags anders benannt hast, kannst Du diese hier anpassen. Da die
  Angabe aus dem Beispiel für die meisten Repositories gängig ist, kannst Du
  das ganze auch mit <code>-s</code> abkürzen. Diese Option steht für das Standard-Repository-Layount und umfasst
  die oben genannten Parameter. Der folgende Befehl ist äquivalent zum zuvor
  genannten:
</p>

<pre>
$ git svn clone file:///tmp/test-svn -s
</pre>

<p>
  Jetzt solltest Du ein Git-Repository erzeugt haben, in das Deine Branches und
  Tags übernommen wurden:
</p>

<pre>
$ git branch -a
* master
  my-calc-branch
  tags/2.0.2
  tags/release-2.0.1
  tags/release-2.0.2
  tags/release-2.0.2rc1
  trunk
</pre>

<p>
  An dieser Stelle soll die wichtige Anmerkung nicht fehlen, dass dieses Tool
  die Namespaces Deiner entfernten Referenzen unterschiedlich behandelt. Wenn
  Du ein normales Git-Repository klonst, werden alle Branches auf jenem
  entfernten Server für Dich lokal verfügbar gemacht, zum Beispiel als <code>origin/[branch]</code>, der Namespace entspricht dem Namen des entfernten Branches. <code>git svn</code> get allerdings davon aus, dass es nicht mehrere entfernte Repositorys gibt
  und speichert daher seie Referenzen auf die Bereiche entfernter Server ohne
  Namespaces. Du kannst das Git-Kommando <code>show-ref</code> verwenden, um Dir die vollständigen Namen aller Referenzen anzeigen zu
  lassen:
</p>

<pre>
$ git show-ref
1cbd4904d9982f386d87f88fce1c24ad7c0f0471 refs/heads/master
aee1ecc26318164f355a883f5d99cff0c852d3c4 refs/remotes/my-calc-branch
03d09b0e2aad427e34a6d50ff147128e76c0e0f5 refs/remotes/tags/2.0.2
50d02cc0adc9da4319eeba0900430ba219b9c376 refs/remotes/tags/release-2.0.1
4caaa711a50c77879a91b8b90380060f672745cb refs/remotes/tags/release-2.0.2
1c4cb508144c513ff1214c3488abe66dcb92916f refs/remotes/tags/release-2.0.2rc1
1cbd4904d9982f386d87f88fce1c24ad7c0f0471 refs/remotes/trunk
</pre>

<p>Ein normales Git-Repository sieht dagegen eher so aus:</p>

<pre>
$ git show-ref
83e38c7a0af325a9722f2fdc56b10188806d83a1 refs/heads/master
3e15e38c198baac84223acfc6224bb8b99ff2281 refs/remotes/gitserver/master
0a30dd3b0c795b80212ae723640d4e5d48cabdff refs/remotes/origin/master
25812380387fdd55f916652be4881c6f11600d6f refs/remotes/origin/testing
</pre>

<p>
  Du hast zwei entfernte Server: einen, der <code>gitserver</code> heißt und einen <code>master</code>-Branch beinhaltet, und einen weiteren, der <code>origin</code> heißt und zwei Branches (<code>master</code> und <code>testing</code>) enthält.
</p>

<p>
  Hast Du bemerkt, dass die entfernten Referenzen, die von <code>git svn</code> im Beispiel importiert wurden, nicht als echte Git-Tags importiert wurden,
  sondern als entfernte Branches? Dein Subversion-Import sieht aus als besäße
  er einen eigenen Remote-Bereich namens <code>tags</code> und unterhalb davon einzelne Branches.
</p>

<h3>Änderungen ins Subversion-Repository committen</h3>

<p>
  Mit unserem funktionierenden Repository können wir nun am Projekt arbeiten
  und unsere Änderungen commiten; dabei nutzen wir Git als SVN-Client. Wenn Du
  eine der Dateien bearbeitest und sie commitest, hast Du lokal einen Commit in
  Git, der auf dem Subversion-Server (noch) nicht vorhanden ist:
</p>

<pre>
$ git commit -am 'Adding git-svn instructions to the README'
[master 97031e5] Adding git-svn instructions to the README
 1 files changed, 1 insertions(+), 1 deletions(-)
</pre>

<p>
  Als nächsten Schritt wirst Du Deine Änderungen einchecken wollen. Dein Umgang
  mit Subversion wird sich dabei verändern -- Du kannst eine Vielzahl an
  Commits lokal durchführen und dann alle zusammen an den Subversion-Server
  schicken. Um Deine Änderungen auf den Subversion-Server zu pushen, verwendest
  Du das <code>git svn dcommit</code> Kommando:
</p>

<pre>
$ git svn dcommit
Committing to file:///tmp/test-svn/trunk ...
       M      README.txt
Committed r79
       M      README.txt
r79 = 938b1a547c2cc92033b74d32030e86468294a5c8 (trunk)
No changes between current HEAD and refs/remotes/trunk
Resetting to the latest refs/remotes/trunk
</pre>

<p>
  Das bündelt alle Commits, die Du auf Basis des Codes im Subversion-Server
  durchgeführt hast und und führt für jede Änderung ein Subversion-Commit
  durch. Anschließend werden Deine lokalen Git-Commits angepasst und jeder von
  ihnen bekommt einen eindeutigen Identifier. Das bedeutet, dass alle SHA-1
  Checksums Deiner Commits verändert werden. Dies ist einer der Gründe, warum
  das Arbeiten mit Git-basierten entfernten Versionen Deines Projekts und
  zeitgleich mit einem Subversion-Server keine gute Idee ist. Wenn Du Dir den
  letzten Commit ansiehst, wirst Du feststellen, dass eine neue <code>git-svn-id</code> hinzugefügt wurde.
</p>

<pre>
$ git log -1
commit 938b1a547c2cc92033b74d32030e86468294a5c8
Author: schacon &lt;schacon@4c93b258-373f-11de-be05-5f7a86268029&gt;
Date:   Sat May 2 22:06:44 2009 +0000

    Adding git-svn instructions to the README

    git-svn-id: file:///tmp/test-svn/trunk@79 4c93b258-373f-11de-be05-5f7a86268029
</pre>

<p>
  Die SHA-Checksum Deines ursprünglichen Commits begann mit <code>97031e5</code>, jetzt fängt sie mit <code>938b1a5</code> an. Wenn Du zugleich auf einen Git- und einen Subversion-Server pushen
  willst, solltest Du zunächst an den Subversion-Server pushen (<code>dcommit</code>), da diese Aktion Deine Commit-Daten verändert.
</p>

<h3>Änderungen ins lokale Repository übernehmen</h3>

<p>
  Wenn Du mit anderen Entwicklern zusammenarbeitest, wirst Du irgendwann an den
  Punkt gelangen an dem einer von Euch Änderungen ins Repository pusht und
  jemand anderes versuchen wird, ebenfalls seine Änderungen zu pushen und damit
  einen Konflikt erzeugt. Diese Änderung wird solange zurückgewiesen bis Du die
  Arbeit des anderen Entwicklers mergt. Mit <code>git svn</code> sieht das so aus:
</p>

<pre>
$ git svn dcommit
Committing to file:///tmp/test-svn/trunk ...
Merge conflict during commit: Your file or directory 'README.txt' is probably \
out-of-date: resource out of date; try updating at /Users/schacon/libexec/git-\
core/git-svn line 482
</pre>

<p>
  Um diese Situation zu lösen, kannst Du <code>git svn rebase</code> laufen lassen. Das zieht alle Änderungen vom Server, die Dir noch fehlen und
  führt ein rebase Deiner lokalen Kopie durch (auf Basis dessen, was auf dem
  Server vorhanden ist).
</p>

<pre>
$ git svn rebase
       M      README.txt
r80 = ff829ab914e8775c7c025d741beb3d523ee30bc4 (trunk)
First, rewinding head to replay your work on top of it...
Applying: first user change
</pre>

<p>
  Jetzt sind alle Deine Arbeiten auf der gleichen Ebene wie die auf dem
  Subversion-Server und nun kannst Du erfolgreich ein <code>dcommit</code> absetzen:
</p>

<pre>
$ git svn dcommit
Committing to file:///tmp/test-svn/trunk ...
       M      README.txt
Committed r81
       M      README.txt
r81 = 456cbe6337abe49154db70106d1836bc1332deed (trunk)
No changes between current HEAD and refs/remotes/trunk
Resetting to the latest refs/remotes/trunk
</pre>

<p>
  Es ist wichtig, im Hinterkopf zu behalten, dass <code>git svn</code> sich an dieser Stelle anders als Git verhält. Git erwartet von Dir, dass Du
  upstream-Arbeiten, die Du lokal noch nicht hast, zunächst mergst, bevor Du
  pushen kannst. Dieses Vorgehen ist bei <code>git svn</code> nur nötig, wenn es Konflikte bei den Änderungen gibt. Wenn jemand anderes
  eine geänderte Datei gepusht hat und Du eine andere geänderte Datei pushst,
  wird Dein <code>dcommit</code> problemlos funktionieren:
</p>

<pre>
$ git svn dcommit
Committing to file:///tmp/test-svn/trunk ...
       M      configure.ac
Committed r84
       M      autogen.sh
r83 = 8aa54a74d452f82eee10076ab2584c1fc424853b (trunk)
       M      configure.ac
r84 = cdbac939211ccb18aa744e581e46563af5d962d0 (trunk)
W: d2f23b80f67aaaa1f6f5aaef48fce3263ac71a92 and refs/remotes/trunk differ, \
  using rebase:
:100755 100755 efa5a59965fbbb5b2b0a12890f1b351bb5493c18 \
  015e4c98c482f0fa71e4d5434338014530b37fa6 M   autogen.sh
First, rewinding head to replay your work on top of it...
Nothing to do.
</pre>

<p>
  Das ist darum wichtig, weil der daraus resultierende Projekt-Status auf
  keinem der Computer existierte als Du die Änderungen gepusht hast. Wenn die
  Änderungen nicht zueinander kompatibel sind aber keinen Konflikt ergeben,
  wirst Du Probleme bekommen, die schwer zu diagnostizieren sind. Das ist der
  Unterschied zu einem Git-Server — mit Git kannst Du den Zustand Deines
  Client-Systems komplett testen bevor Du ihn veröffentlichst, während Du bei
  Subversion nie sicher sein kannst, dass der Zustand direkt vor und direkt
  nach dem Commit identisch sind.
</p>

<p>
  Du solltest Dieses Kommando auch ausführen um Änderungen vom
  Subversion-Server zu ziehen, selbst wenn Du noch nicht so weit bist, einen
  Commit durchzuführen. Du kannst <code>git svn fetch</code> ausführen um die neuen Daten zu besorgen aber <code>git svn rebase</code> zieht die Daten ebenfalls und aktualisiert Deine lokalen Commits.
</p>

<pre>
$ git svn rebase
       M      generate_descriptor_proto.sh
r82 = bd16df9173e424c6f52c337ab6efa7f7643282f1 (trunk)
First, rewinding head to replay your work on top of it...
Fast-forwarded master to refs/remotes/trunk.
</pre>

<p>
  Wenn Du <code>git svn rebase</code>ab und an ausführst, stellst Du sicher, dass Dein Code immer up-to-date ist.
  Du musst Dir aber sicher sein, dass Dein Arbeitsverzeichnis „sauber“ ist,
  bevor Du den Befehl ausführst. Wenn Du lokale Änderungen hast, musst Du Deine
  Arbeit vor dem <code>git svn rebase</code> entweder stashen oder temporär commiten. Anderenfalls wird die Ausführung
  des Befehls angehalten, wenn das Rebase in einem Merge-Konflikt enden würde.
</p>

<h3>Probleme beim Benutzen von Branches</h3>

<p>
  Wenn Du Dich an den Git-Workflow gewöhnt hast, wirst Du höchstwahrscheinlich
  Zweige für Deine Arbeitspakete anlegen, mit ihnen arbeiten und sie
  anschließend wieder mergen. Wenn Du mit <code>git svn</code> auf einen Subversion-Server pushst, führst Du am besten eine
  Rebase-Operation in einen einzigen Zweig durch anstatt alle Zweige
  zusammenzufügen. Der Grund dafür, das Rebase zu bevorzugen, liegt darin, dass
  Subversion eine lineare Historie hat und mit dem Merge-Operationen nicht wie
  Git es tut. Daher folgt <code>git svn</code> nur dem ersten Elternelement, wenn es Snapshots in Subversion-Commits
  umwandelt.
</p>

<p>
  Angenommen, Deine Historie sieht wie folgt aus: Du hast einen Zweig mit dem
  Namen <code>experiment</code> angelegt, zwei Commits durchgeführt und diese dann anschließen mit <code>master</code>zusammengeführt. Führst Du nun ein <code>dcommit</code> durch, sieht die Ausgabe wie folgt aus:
</p>

<pre>
$ git svn dcommit
Committing to file:///tmp/test-svn/trunk ...
       M      CHANGES.txt
Committed r85
       M      CHANGES.txt
r85 = 4bfebeec434d156c36f2bcd18f4e3d97dc3269a2 (trunk)
No changes between current HEAD and refs/remotes/trunk
Resetting to the latest refs/remotes/trunk
COPYING.txt: locally modified
INSTALL.txt: locally modified
       M      COPYING.txt
       M      INSTALL.txt
Committed r86
       M      INSTALL.txt
       M      COPYING.txt
r86 = 2647f6b86ccfcaad4ec58c520e369ec81f7c283c (trunk)
No changes between current HEAD and refs/remotes/trunk
Resetting to the latest refs/remotes/trunk
</pre>

<p>
  Das Ausführen von <code>dcommit</code> in einem Zweig mit zusammengeführter Historie funktioniert wunderbar, mit
  der folgenden Ausnahme: wenn Du Deinen Git-Projekthistorie anschaust, wurden
  die Commits, die Du im <code>experiment</code>-Zweig gemacht hast, nicht neu geschrieben — stattdessen tauchen alle diese
  Änderungen in der SVN-Version des einzelnen Merge-Commits auf.
</p>

<p>
  Wenn nun jemand anderes Deine Arbeit klont, ist alles, was er oder sie sieht,
  der Merge-Commit mit all Deinen Änderungen insgesamt; die Daten zu den
  einzelnen Commits (den Urpsrung und die Zeit, wann die Commits stattfanden)
  sehen sie nicht.
</p>

<h3>Subversion-Zweige</h3>

<p>
  Das Arbeiten mit Zweigen in Subversion ist nicht das gleiche wie mit Zweigen
  in Git arbeiten; es ist wohl das beste, wenn Du es vermeiden kannst, viel
  damit zu arbeiten. Dennoch kannst Du mit <code>git svn</code> Zweige in Subversion anlegen und Commits darin durchführen.
</p>

<p>Einen neuen SVN-Zweig anlegen</p>

<p>
  Um einen neuen Zweig in Subversion anzulegen, führst Du <code>git svn branch [branchname]</code> aus:
</p>

<pre>
$ git svn branch opera
Copying file:///tmp/test-svn/trunk at r87 to file:///tmp/test-svn/branches/opera...
Found possible branch point: file:///tmp/test-svn/trunk =&gt; \
  file:///tmp/test-svn/branches/opera, 87
Found branch parent: (opera) 1f6bfe471083cbca06ac8d4176f7ad4de0d62e5f
Following parent with do_switch
Successfully followed parent
r89 = 9b6fe0b90c5c9adf9165f700897518dbc54a7cbf (opera)
</pre>

<p>
  Dieser Befehl macht genau das gleiche wie das <code>svn copy trunk branches/opera</code>-Kommando in Subversion und arbeitet auf dem Subversion-Server. Wichtig
  hierbei ist, dass Du mit Deiner Arbeit nicht automatisch in diesen Zweig
  wechselst: wenn Du zu diesem Zeitpunkt einen Commit durchführst, wird dieser
  in <code>trunk</code> auf dem Server landen, nicht im <code>opera</code>-Zweig.
</p>

<h3>Den aktiven Zweig wechseln</h3>

<p>
  Git findet heraus, in welchem Zweig Deine <code>dcommits</code> abgelegt werden, indem es den tip jedes Subversion-Zweiges in Deiner
  Historie untersucht — Du solltest nur einen einzigen haben und es sollte der
  letzte mit einer <code>git-svn-id</code> in der aktuellen Historie des Zweiges sein.
</p>

<p>
  Wenn Du gleichzeitig mit mehr als einem Zweig arbeiten willst, kannst Du
  lokale Zweige derart anlegen, dass ein <code>dcommit</code> für sie in bestimmte Subversion-Zweige durchgeführt wird. Dazu startest Du
  diese beim importierten Subversion-Commit für diesen Zweig. Wenn Du einen <code>opera</code>-Zweig haben willst, an dem Du separat arbeiten kannst, führst Du
</p>

<pre>
$ git branch opera remotes/opera
</pre>

<p>
  aus. Wenn Du jetzt Deinen <code>opera</code>-Zweig in <code>trunk</code> (Deinen <code>master</code>-Zweig) zusammenführen willst , kannst Du das mit einem normalen <code>git merge</code> machen. Du solltest allerdings eine aussagekräftige Commit-Beschreibung
  angeben (mit <code>-m</code>) oder sie wird statt etwas Sinnvollem „Merge branch opera“ lauten.
</p>

<p>
  Behalte im Hinterkopf, dass dieses Vorgehen kein normaler Git-Merge-Commit
  ist, auch wenn Du den <code>git merge</code>-Befehl verwendes und das Zusammenführen wahrscheinlich wesentlich einfacher
  ist als es in Subversion gewesen wäre (weil Git automatisch die passende
  Basis für das Zusammenführen der Zweige für Dich herausfindet=. Du musst
  diese Daten&emsp14;&emsp14;zurück auf den Subversion-Server schieben, der
  nicht mit Commits umgehen kann, die mehr als ein Elternelement haben; all
  Deine Änderungen aus einem anderen Zweig werden in diesem einen Commit
  zusammengepresst, wenn Du die Änderungen hochschiebst. Nachdem Du einen Zweig
  mit einem anderen zusammengefügt hast, kannst Du nicht einfach zurückgehen
  und mit der Arbeit an diesem Zweig weitermachen, wie Du das normalerweise in
  Git machen würdest. Wenn Du das <code>dcommit</code>-Kommando ausführst, löscht es jegliche Information darüber, welcher Zweig
  hier hineingefügt wurde und als Folge dessen werden künftige
  merge-base-Berechnungen falsche sein — die <code>dcommit</code>-Operation lässt Dein <code>git merge</code>-Ergebnis so aussehen als ob Du <code>git merge --squash</code> verwendet hättest. Unglücklicherweise gibt es kein ideales Mittel, diese
  Situation zu vermeiden — Subversion kann diese Information einfach nicht
  speichern, daher wirst Du immer unter seinen beschränkten Möglichkeiten zu
  leiden haben, so lange Du es als Server verwendest. Um diese Probleme zu
  vermeiden, solltest Du den lokalen Zweig (in unserem Beispiel <code>opera</code>) löschen, nachdem Du ihn mit dem <code>trunk</code> zusammengeführt hast.
</p>

<h3>Subversion Befehle</h3>

<p>
  Das <code>git svn</code>-Werkzeug bietet eine ganze Reihe von Befehlen an, die Dir helfen, den
  Übergang zu Git zu vereinfachen, indem sie einige Funktionen bereitstellen,
  die jeden ähneln, die Du bereits in Subversion kanntest. Hier sind ein paar
  Befehle, die Dir solche Funktionen bereitstellen wie das Subversion früher
  für Dich tat:
</p>

<p>Historie im SVN-Stil</p>

<p>
  Wenn Du an Subversion gewöhnt bist und Deine Historie so sehen möchtest, wie
  SVN sie ausgeben würde, kannst Du <code>git svn log</code> ausführen, um Deine Commit-Historie in der SVN-Formatierung anzusehen:
</p>

<pre>
$ git svn log
------------------------------------------------------------------------
r87 | schacon | 2009-05-02 16:07:37 -0700 (Sat, 02 May 2009) | 2 lines

autogen change

------------------------------------------------------------------------
r86 | schacon | 2009-05-02 16:00:21 -0700 (Sat, 02 May 2009) | 2 lines

Merge branch 'experiment'

------------------------------------------------------------------------
r85 | schacon | 2009-05-02 16:00:09 -0700 (Sat, 02 May 2009) | 2 lines

updated the changelog
</pre>

<p>
  Du solltest zwei wichtige Dinge über <code>git svn log</code> wissen. Erstens: es arbeitet offline, im Gegensatz zum echten <code>svn log</code>-Befehl, der den Subversion-Server nach den Daten fragt. Zweitens: es zeigt
  Dir nur Commits an, die auf den Subversion-Server committet wurden. Lokale
  Git-Commits die Du nicht mit <code>dcommit</code> bestätigt hast, werden nicht aufgeführt, genausowenig wie Commits, die
  andere in der Zwischenzeit auf dem Subversion-Server gemacht haben. Die
  Ausgabe zeigt Dir eher den letzten bekannten Zustand der Commits auf dem
  Subversion-Server.
</p>

<p>SVN Vermerke (SVN Annotation)</p>

<p>
  Genauso wie das Kommando <code>git svn log</code> den <code>svn log</code>-Befehl offline simuliert kannst Du das Pendant zu <code>svn annotate</code> mit dem Befehl <code>git svn blame [FILE]</code> ausführen. Die Ausgabe sieht so aus:
</p>

<pre>
$ git svn blame README.txt
 2   temporal Protocol Buffers - Google's data interchange format
 2   temporal Copyright 2008 Google Inc.
 2   temporal http://code.google.com/apis/protocolbuffers/
 2   temporal
22   temporal C++ Installation - Unix
22   temporal =======================
 2   temporal
79    schacon Committing in git-svn.
78    schacon
 2   temporal To build and install the C++ Protocol Buffer runtime and the Protocol
 2   temporal Buffer compiler (protoc) execute the following:
 2   temporal
</pre>

<p>
  Auch dieser Befehl zeigt die Commits nicht an, die Du lokal getätigt hast,
  genauso wenig wie jene, die in der Zwischenzeit zum Subversion-Server
  übertragen wurden.
</p>

<p>SVN-Server-Informationen</p>

<p>
  Die selben Informationen wie bei <code>svn info</code> bekommst Du, wenn Du <code>git svn info</code> ausführst:
</p>

<pre>
$ git svn info
Path: .
URL: https://schacon-test.googlecode.com/svn/trunk
Repository Root: https://schacon-test.googlecode.com/svn
Repository UUID: 4c93b258-373f-11de-be05-5f7a86268029
Revision: 87
Node Kind: directory
Schedule: normal
Last Changed Author: schacon
Last Changed Rev: 87
Last Changed Date: 2009-05-02 16:07:37 -0700 (Sat, 02 May 2009)
</pre>

<p>
  Genauso wie <code>blame</code> und <code>log</code> läuft die Ausführung dieses Befehls offline ab und ist nur so aktuell wie zu
  dem Zeitpunkt, zu dem Du das letzte Mal mit dem Subversion-Server verbunden
  warst.
</p>

<p>Ignorieren, was Subversion ignoriert</p>

<p>
  Wenn Du ein Subversion-Repository klonst, das irgendwo <code>svn:ignore</code> Eigenschaften definiert hat, wirst Du die <code>.gitignore</code>-Dateien wahrscheinlich entsprechend setzen, damit Du nicht aus Versehen
  Dateien committest, bei denen Du das besser nicht tun solltest. <code>git svn</code> kennt zwei Befehle, um Dir bei diesem Problem zu helfen. Der erste ist <code>git svn create-ignore</code>, der automatisch entsprechende <code>.gitignore</code>-Dateien für Dich anlegt, sodass Dein nächster Commit diese beinhalten kann.
</p>

<p>
  Der zweite Befehl ist <code>git svn show-ignore</code>, der Dir diejenigen Zeilen auf stdout ausgibt, die Du in eine <code>.gitignore</code>-Datei einfügen musst. So kannst Du die Ausgabe des Befehls direkt in die
  Ausnahmedatei umleiten:
</p>

<pre>
$ git svn show-ignore &gt; .git/info/exclude
</pre>

<p>
  Auf diese Weise müllst Du Dein Projekt nicht mit <code>.gitignore</code>-Dateien zu. Das ist eine gute Wahl wenn Du der einzige Git-Benutzer in
  Deinem Team bist (alle anderen benutzen Subversion) und Deine Kollegen keine <code>.gitignore</code>-Dateien im Projekt haben wollen.
</p>

<h3>Zusammenfassung von Git-Svn</h3>

<p>
  Die <code>git svn</code>-Werkzeuge sind sehr nützlich, wenn Du derzeit (noch) an einen
  Subversion-Server gebunden bist oder Dich anderweitig in einer
  Entwicklungsumgebung befindest, die nicht auf einen Subversion-Server
  verzichten kann. Wie auch immer: Du solltest es als eine Art gestutztes Git
  ansehen. Anderenfalls läufst Du Gefahr, Dich und Deine Kollegen durcheinander
  zu bringen. Um dieses Kliff zu umschiffen, solltest Du folgende Richtlinien
  befolgen:
</p>

<ul>
  <li>
    <p>
      Versuch, eine „geradlinige“ Git-Historie zu führen, die keine von <code>git merge</code> durchgeführten Merges enthält. Alle Arbeiten, die Du außerhalb des
      Hauptzweiges durchführst, solltest Du mit <code>rebase</code> in ihn aufnehmen anstatt sie zu mit <code>merge</code> zusammenzuführen.
    </p>
  </li>

  <li>
    <p>
      Setz keinen zusätzlichen, externen Git-Server auf, mit dem Du arbeiten
      möchtest. Du kannst einen aufsetzen um die Klone für neue Entwickler zu
      beschleunigen, aber Du solltest keine Änderungen dorthin pushen, die
      keine <code>git-svn-id</code>-Einträge haben. Du solltest vielleicht sogar darüber nachdenken, einen <code>pre-receive</code>-Hook einzusetzen, der jede Commit-Nachricht auf eine <code>git-svn-id</code> prüft und bestimmte Pushes ablehnt, bei denen diese IDs fehlt.
    </p>
  </li>
</ul>

<p>
  Wenn Du diese Ratschläge befolgst, werden sie die Arbeit mit dem
  Subversion-Server erträglich machen. Wenn es Dir irgendwie möglich ist,
  solltest Du trotzdem zu einem echten Git-Server umziehen, denn davon
  profitiert Dein Team wesentlich deutlicher.
</p>

<h2>Zu Git umziehen</h2>

<p>
  Wenn Du bereits Quellcode in einer anderen Versionsverwaltung abgelegt hast,
  aber Dich nun entschieden hast, von nun an Git zu benutzen, musst Du Dein
  Projekt so oder so umziehen. Für geläufige Systeme bringt Git einige Importer
  mit. Anschließend lernen wir, wie Du Deinen eigenen, angepassten Importer
  entwickeln kann. All das wird im folgenden Abschnitt behandelt.
</p>

<h3>Import</h3>

<p>
  Jetzt ist es an der Zeit zu lernen, wie Du Daten aus zwei der am meisten
  benutzten (professionellen) SCM-Systeme importieren kannst: Subversion und
  Perforce. Ein Großteil der Benutzer, die gegenwärtig zu Git umziehen,
  arbeiten mit einem von diesen beiden Systemen. Außerdem liefert Git für beide
  jeweils hochprofessionelle Werkzeuge für den Import mit.
</p>

<h3>Subversion</h3>

<p>
  Wenn Du die letzten Abschnitte über <code>git svn</code> gelesen hast, kannst Du diese Anleitungen ganz einfach benutzen um mit <code>git svn clone</code> ein Repository zu klonen. Anschließend stoppst den Subversion-Server, führst
  einen Push auf den neuen Git-Server durch und beginnst ihn zu benutzen. Wenn
  Du an die Historie ran willst, kannst Du das genausoschnell erreichen als ob
  Du die Daten aus dem Subversion-Server beziehen würdest (was eine Weile
  dauern könnte).
</p>

<p>
  Trotzdem ist der Import nicht perfekt. Und weil das ziemlich lange dauern
  wird, kannst Du es auch gleich richtig machen. Das erste Problem sind die
  Informationen über die Autoren. In Subversion besitzt jede Person, die mit
  dem System arbeitet, einen eigenen User-Account, der in den
  Commit-Informationen aufgezeichnet wird. Die Beispiele in den vorherigen
  Abschnitten zeigten dafür manchmal <code>schacon</code> an, wie beispielsweise bei der Ausgabe von <code>blame</code> und bei <code>git svn log</code>. Wenn Du dies näher an die Autoren-Daten von Git binden willst, musst Du
  Mapping-Informationen für die Subversion-Benutzer und die Git-Autoren
  anlegen. Erstelle eine Datei mit dem Namen <code>users.txt</code>, die folgendes Mapping-Format verwendet:
</p>

<pre>
schacon = Scott Chacon &lt;schacon@geemail.com&gt;
selse = Someo Nelse &lt;selse@geemail.com&gt;
</pre>

<p>
  Um eine Liste der Namen der Autoren bekommen, die SVN benutzen, kannst Du
  folgendes Kommando ausführen:
</p>

<pre>
$ svn log ^/ --xml | grep -P "^&lt;author" | sort -u | \
      perl -pe 's/&lt;author&gt;(.*?)&lt;\/author&gt;/$1 = /' &gt; users.txt
</pre>

<p>
  Dies erzeugt Dir die Log-Ausgabe im XML-Format — Du suchst damit nach den
  Autoren, erzeugst eine Liste ohne doppelte Einträge und wirfst anschließend
  das überflüssige XML weg. Leite anschließend die Ausgabe in die Datei <code>users.txt</code> um, sodass Du jedem Eintrag den entsprechenden Git-Benutzer zuordnen kannst.
</p>

<p>
  Du kannst diese Datei dann <code>git svn</code> zur Verfügung stellen um das Tool dabei zu unterstützen, die
  Autoreninformationen besser zu mappen. Du kannst <code>git svn</code> ebenfalls mitteilen, dass es die Metadaten nicht einbeziehen soll, die
  Subversion normalerweise importiert, indem Du dem <code>clone</code> oder <code>init</code> Kommando die <code>--no-metadata</code>-Option mitgibst.
</p>

<pre>
$ git svn clone http://my-project.googlecode.com/svn/ \
      --authors-file=users.txt --no-metadata -s my_project
</pre>

<p>
  Jetzt solltest Du einen hübscheren Subversion-Import in Deinem <code>my_project</code>-Verzeichnis haben. Statt eines Commit, die so aussehen:
</p>

<pre>
commit 37efa680e8473b615de980fa935944215428a35a
Author: schacon &lt;schacon@4c93b258-373f-11de-be05-5f7a86268029&gt;
Date:   Sun May 3 00:12:22 2009 +0000

    fixed install - go to trunk

    git-svn-id: https://my-project.googlecode.com/svn/trunk@94 4c93b258-373f-11de-
    be05-5f7a86268029
</pre>

<p>sehen sie jetzt so aus:</p>

<pre>
commit 03a8785f44c8ea5cdb0e8834b7c8e6c469be2ff2
Author: Scott Chacon &lt;schacon@geemail.com&gt;
Date:   Sun May 3 00:12:22 2009 +0000

    fixed install - go to trunk
</pre>

<p>
  Nicht nur das Autoren-Feld sieht jetzt wesentlich besser aus. Auch die <code>git-svn-id</code> wird jetzt nicht mehr gebraucht.
</p>

<p>
  Nach dem Import musst Du noch ein wenig aufräumen. Dafür solltest Du all die
  merkwürdigen Referenzen säubern, die <code>git svn</code> angelegt hat. Zuerst verschiebst Du die Tags, damit sie tatsächliche
  Git-Tags sind statt merkwürdigen Remote-Zweigen. Anschließend verschieben wir
  den Rest der Zweige, sodass sie lokale Zweige werden.
</p>

<p>
  Um die Tags so zu verschieben, dass sie echte Git-Tags werden, führst Du
  folgenden Befehl aus:
</p>

<pre>
$ git for-each-ref refs/remotes/tags | cut -d / -f 4- | grep -v @ | while read tagname; do git tag "$tagname" "tags/$tagname"; git branch -r -d "tags/$tagname"; done
</pre>

<p>
  Das nimmt die Referenzen, die vorher Remote-Zweige waren, die mit <code>tag/</code> begonnen haben und macht aus ihnen echte (leichtgewichtige) Tags.
</p>

<p>
  Als nächstes verschieben wir den Rest der Referenzen aus <code>refs/remotes</code> und machen lokale Zweige daraus:
</p>

<pre>
$ git for-each-ref refs/remotes | cut -d / -f 3- | grep -v @ | while read branchname; do git branch "$branchname" "refs/remotes/$branchname"; git branch -r -d "$branchname"; done
</pre>

<p>
  Jetzt sind alle alten Zweige richtige Git-Zweige geworden und alle alten Tags
  sind echte Git-Tags. Als letztes müssen wir den neuen Git-Server noch als
  entfernten Server einrichten und unsere Änderungen zu ihm pushen. Da wir alle
  Zweige und Tags einbeziehen wollen, kannst Du diesen Befehl verwenden:
</p>

<pre>
$ git remote add origin git@my-git-server:myrepository.git
</pre>

<pre>
$ git push origin --all
$ git push origin --tags
</pre>

<p>
  All Deine Zweige und Tags sollten jetzt in Deinem neuen Git-Server in einem
  schicken, sauberen Import vorhanden sein.
</p>

<h3>Perforce</h3>

<p>
  Das nächste System, dass wir zum Importieren anschauen werden, ist Perforce.
  Ein Import-Werkzeug für Perforce wird ebenfalls mit Git mitgeliefert,
  allerdings nur im <code>contrib</code>-Bereich des Quellcodes — es ist nicht wie <code>git svn</code> standardmäßig verfügbar. Um es auszuführen, musst Du den Git-Quellcode von <code>git.kernel.org</code> herunterladen:
</p>

<pre>
$ git clone git://git.kernel.org/pub/scm/git/git.git
$ cd git/contrib/fast-import
</pre>

<p>
  In diesem <code>fast-import</code> Verzeichnis wirst Du ein ausführbares Python-Skript mit dem Namen <code>git-p4</code> finden. Du musst Python sowie das <code>p4</code>-Werkzeug auf Deiner Maschine installiert haben, damit der Import klappt. Als
  Beispiel werden wir das Jam-Projekt aus dem Perforce Public Depot verwenden.
  Um den Client einzurichten, musst Du die P4PORT-Umgebungsvariable exportieren
  und sie auf das Perforce-Depot einstellen:
</p>

<pre>
$ export P4PORT=public.perforce.com:1666
</pre>

<p>
  Führe den <code>git-p4 clone</code>-Befehl aus, um das Jam-Projekt aus dem Perforce-Server zu importieren. Dazu
  gibst Du den Depot- und Projekt-Pfad sowie den Pfad an, in den Du das Projekt
  importieren willst:
</p>

<pre>
$ git-p4 clone //public/jam/src@all /opt/p4import
Importing from //public/jam/src@all into /opt/p4import
Reinitialized existing Git repository in /opt/p4import/.git/
Import destination: refs/remotes/p4/master
Importing revision 4409 (100%)
</pre>

<p>
  Wenn Du zum <code>/opt/p4import</code>-Verzeichnis wechselst und dann <code>git log</code> ausführst, kannst Du sehen, dass Dein Import funktioniert hat:
</p>

<pre>
$ git log -2
commit 1fd4ec126171790efd2db83548b85b1bbbc07dc2
Author: Perforce staff &lt;support@perforce.com&gt;
Date:   Thu Aug 19 10:18:45 2004 -0800

    Drop 'rc3' moniker of jam-2.5.  Folded rc2 and rc3 RELNOTES into
    the main part of the document.  Built new tar/zip balls.

    Only 16 months later.

    [git-p4: depot-paths = "//public/jam/src/": change = 4409]

commit ca8870db541a23ed867f38847eda65bf4363371d
Author: Richard Geiger &lt;rmg@perforce.com&gt;
Date:   Tue Apr 22 20:51:34 2003 -0800

    Update derived jamgram.c

    [git-p4: depot-paths = "//public/jam/src/": change = 3108]
</pre>

<p>
  Du kannst die <code>git-p4</code>-ID bei jedem Commit sehen. Es ist OK, diese ID hier zu behalten, falls Du
  später noch mal einen Bezug zu der Perforce-Änderung herstellen musst. Falls
  Du die ID entfernen willst, ist jetzt Zeit dazu — bevor Du mit der Arbeit an
  dem neuen Repository beginnst. Du kannst <code>git filter-branch</code> benutzen um all die IDs zu entfernen:
</p>

<pre>
$ git filter-branch --msg-filter '
        sed -e "/^\[git-p4:/d"
'
Rewrite 1fd4ec126171790efd2db83548b85b1bbbc07dc2 (123/123)
Ref 'refs/heads/master' was rewritten
</pre>

<p>
  Wenn Du <code>git log</code> ausführst, kannst Du alle SHA1-Prüfsummen für jene Commits sehen, die sich
  geändert haben, aber die <code>git-p4</code>-Zeichenketten sind nicht mehr in den Commit-Nachrichten vorhanden.
</p>

<pre>
$ git log -2
commit 10a16d60cffca14d454a15c6164378f4082bc5b0
Author: Perforce staff &lt;support@perforce.com&gt;
Date:   Thu Aug 19 10:18:45 2004 -0800

    Drop 'rc3' moniker of jam-2.5.  Folded rc2 and rc3 RELNOTES into
    the main part of the document.  Built new tar/zip balls.

    Only 16 months later.

commit 2b6c6db311dd76c34c66ec1c40a49405e6b527b2
Author: Richard Geiger &lt;rmg@perforce.com&gt;
Date:   Tue Apr 22 20:51:34 2003 -0800

    Update derived jamgram.c
</pre>

<p>
  Dein Import ist jetzt so weit, dass Du ihn auf den neuen Git-Server pushen
  kannst.
</p>

<h3>Ein Import-Tool im Eigenbau</h3>

<p>
  Wenn die Versionsverwaltung, die Du verwendest, nicht Subversion oder
  Perforce ist, solltest Du zunächst einmal online nach einem Import-Tool
  suchen — gute Import-Tools sind für CVS, Clear Case, Visual Source Sage und
  sogar für ein Verzeichnis mit Archiven verfügbar. Wenn für Deinen
  Anwendungsfall keines dieser Werkzeuge passt, Du eine fast schon
  ausgestorbene Versionsverwaltung verwendest oder Du aus irgendeinem anderen
  Grund ein angepassteres Vorgehen brauchst, dann solltest Du <code>git fast-import</code> verwenden. Dieser Befehl nimmt einfache Anweisungen von <code>stdin</code> entgegen um entsprechende Git-Daten zu schreiben. Es ist viel einfacher
  Git-Objekte auf diese Art zu erzeugen als die blanken Git-Kommandos zu
  verwenden oder zu versuchen, die Roh-Objekte zu schreiben (weitere
  Informationen findest Du in Kapitel 9).
</p>

<p>
  Um das kurz zu zeigen, schreiben wir einen einfachen Importer. Nehmen wir an,
  Du arbeitest im Verzeichnis <code>current</code> und führst ab und an ein Backup durch, indem Du dieses Verzeichnis in ein
  Backup-Verzeichnis kopierst und ihm einen anderen Namen mit einem
  Zeitstempel, z. B. <code>back_YYYY_MM_DD</code>, verpasst. Diese Struktur wollen wir jetzt in Git importieren. Dein
  Verzeichnis sieht also so aus:
</p>

<pre>
$ ls /opt/import_from
back_2009_01_02
back_2009_01_04
back_2009_01_14
back_2009_02_03
current
</pre>

<p>
  Damit wir ein Git-Verzeichnis importieren können, müssen wir uns zunächst
  noch einmal anschauen, wie Git seine Daten speichert. Wie Du Dich vielleicht
  erinnerst, ist Git im Grundsatz eine verlinkte Liste von Commit-Objekten die
  auf eine Momentaufnahme (Snapshots) des Inhalts zeigt. Jetzt musst Du nur
  noch <code>fast-import</code> mitteilen, was dieses Snapshots sind, welche Commit-Daten zu ihnen zeigen
  und die Reihenfolge, in die sie gehören. Deine Strategie wir es sein, einen
  nach dem anderen durch die Snapshots zu gehen und Commits mit dem Inhalt
  eines jeden Verzeichnisses zu erzeigen und jeden dieser Commits anschließend
  mit dem vorherigen zu verknüpfen.
</p>

<p>
  Wie wir das schon im Abschnitt „(...)“ in Kapitel 7 getan haben,
  programmieren wir diese Lösung in Ruby, weil es die Sprache ist, mit der ich
  normalerweise arbeite und weil sie recht einfach zu lesen ist. Du kannst das
  Beispiel in so ziemlich jeder Sprache schreiben, mit der Du vertraut bist —
  es muss nur die passenden Informationen nach <code>stdout</code> schreiben.
</p>

<p>
  Zu Beginn musst Du in das Zielverzeichnis wechseln und jedes Unterverzeichnis
  identifizieren, das ein Snapshot ist, den Du als Commit importieren willst.
  Du wirst in jedes dieser Unterverzeichnisse wechseln und den entsprechenden
  Befehl auszugeben um es zu exportieren. Deine Schleife wird etwa so aussehen:
</p>

<pre>
last_mark = nil

# loop through the directories
Dir.chdir(ARGV[0]) do
  Dir.glob("*").each do |dir|
    next if File.file?(dir)

    # move into the target directory
    Dir.chdir(dir) do
      last_mark = print_export(dir, last_mark)
    end
  end
end
</pre>

<p>
  Du führst <code>print_export</code> für jedes Verzeichnis aus. Das nimmt das Manifest und die Markierung des
  letzten Snapshots entgegen und gibt das Manifest und die Markierung des
  aktuellen zurück; auf diese Weise kannst Du sie passend verlinken. „Mark“ ist
  der <code>fast-import</code>-Begriff für eine ID, die Du einem Commit gibst. Während Du Commits anlegst,
  verpasst Du jedem einzelnen eine Markierung, die Du benutzen kannst, um von
  anderen Commits zu ihm zu linken. Daher ist das erste, was Deine <code>print_export</code>-Methode macht, eine Markierung aus dem Verzeichnisnamen zu erstellen:
</p>

<pre>
mark = convert_dir_to_mark(dir)
</pre>

<p>
  Das erreichst Du, indem Du ein Array von Verzeichnissen anlegst und den
  Index-Wert als Markierung verwendest (eine Markierung muss vom Typ <code>integer</code> sein). Deine Methode sieht so aus:
</p>

<pre>
$marks = []
def convert_dir_to_mark(dir)
  if !$marks.include?(dir)
    $marks &lt;&lt; dir
  end
  ($marks.index(dir) + 1).to_s
end
</pre>

<p>
  Jetzt hast Du eine <code>integer</code>-Repräsentation Deines Commits und brauchst nur noch ein Datum für die
  Commit-Metadaten. Da das Datum im Verzeichnisnamen enthalten ist, parsen wir
  es einfach daraus. Die nächste Zeile in Deiner <code>print_export</code>-Datei lautet
</p>

<pre>
date = convert_dir_to_date(dir)
</pre>

<p>wobei <code>convert_dir_to_date</code> so definiert ist:</p>

<pre>
def convert_dir_to_date(dir)
  if dir == 'current'
    return Time.now().to_i
  else
    dir = dir.gsub('back_', '')
    (year, month, day) = dir.split('_')
    return Time.local(year, month, day).to_i
  end
end
</pre>

<p>
  Die Funktion gibt einen Integer-Wert für das Datum eines jeden Verzeichnisses
  zurück. Das letzte Stück Meta-Information, das wir noch für jeden Commit
  brauchen, sind die Commit-Daten des Autors, die wir in eine globale Variable
  packen:
</p>

<pre>
$author = 'Scott Chacon &lt;schacon@example.com&gt;'
</pre>

<p>
  Jetzt können wir damit beginnen, die Commit-Daten für den Importer
  auszugeben. Die ursprüngliche Information gibt an, dass Du ein Commit-Objekt
  definierst und zu welchem Branch es gehört, gefolgt von der Markierung, die
  Du angelegt hast, der Committer-Information und der Commit-Nachricht und
  schließlich der ID des vorhergehenden Commits, falls dieser existiert. Der
  Code sieht wie folgt aus:
</p>

<pre>
# print the import information
puts 'commit refs/heads/master'
puts 'mark :' + mark
puts "committer #{$author} #{date} -0700"
export_data('imported from ' + dir)
puts 'from :' + last_mark if last_mark
</pre>

<p>
  You hardcode the time zone (-0700) because doing so is easy. If you’re
  importing from another system, you must specify the time zone as an offset.
  The commit message must be expressed in a special format:
</p>

<pre>
data (size)\n(contents)
</pre>

<p>
  The format consists of the word data, the size of the data to be read, a
  newline, and finally the data. Because you need to use the same format to
  specify the file contents later, you create a helper method, <code>export_data</code>:
</p>

<pre>
def export_data(string)
  print "data #{string.size}\n#{string}"
end
</pre>

<p>
  Alles, was jetzt noch übrig bleibt, ist das Feststellen des Dateiinhalts
  eines jeden Snapshots. Das ist einfach, weil Du jeden davon in einem
  Verzeichnis hast — Du kannst das <code>deleteall</code>-Kommando ausgeben, gefolgt von den Inhalten einer jeden Datei in dem
  Verzeichnis. Git wird dann jeden Snapshot entsprechend aufzeichnen:
</p>

<pre>
puts 'deleteall'
Dir.glob("**/*").each do |file|
  next if !File.file?(file)
  inline_data(file)
end
</pre>

<p>
  Anmerkung: Da viele Systeme ihre Revisionen als Änderungen von einem Commit
  zu einem anderen betrachten, kann <code>fast-import</code> auch mit jedem Commit Kommandos entgegennehmen, die angeben, welche Dateien
  geändert, entfernt oder verändert wurden und was der neue Inhalt ist. Wir
  könnten die Unterschiede zwischen den Snapshots berechnen und nur diese Daten
  bereitstellen, aber das zu tun ist komplizierter — Du kannst Git auch einfach
  alle Daten füttern und es kümmert sich dann darum. Wenn dieses Vorgehen eher
  zu Deinen Daten passt, schau Dir die <code>fast-import</code> man-Seite an, um Details darüber zu erfahren, wie diese Daten dafür
  bereitgestellt werden müssen.
</p>

<p>
  Das Format, in dem die Inhalte einer neuen Datei oder in eine geänderte Datei
  mit ihrem neuen Inhalt angegeben werden, sieht wie folgt aus:
</p>

<pre>
M 644 inline path/to/file
data (size)
(file contents)
</pre>

<p>
  In diesem Beispiel ist 644 der Datei-Modus (wenn Du ausführbare Dateien hast,
  wirst Du möglicherweise 755 sehen bzw. einstellen), und inline gibt an, dass
  Du die Inhalte direkt im Anschluss an diese Zeile aufführen wirst. Deine <code>inline_data</code>-Methode sieht so aus:
</p>

<pre>
def inline_data(file, code = 'M', mode = '644')
  content = File.read(file)
  puts "#{code} #{mode} inline #{file}"
  export_data(content)
end
</pre>

<p>
  Du kannst die <code>export_data</code>-Methode, die Du vorher definiert hast, wiederverwenden, da wir das auf die
  gleiche Weise lösen, wie wir die Daten für die Commit-Nachrichten aufbereitet
  haben.
</p>

<p>
  Das letzte, das wir jetzt noch machen müssen, ist, die gegenwärtige Marke
  zurückzugeben, damit sie an den nächsten Durchlauf übergeben werden kann.
</p>

<pre>
return mark
</pre>

<pre>
$stdout.binmode
</pre>

<p>
  Das ist alles. Wenn Du diese Skript laufen lässt, wirst Du eine Ausgabe
  erhalten, die etwa wie folgt aussieht:
</p>

<pre>
$ ruby import.rb /opt/import_from
commit refs/heads/master
mark :1
committer Scott Chacon &lt;schacon@geemail.com&gt; 1230883200 -0700
data 29
imported from back_2009_01_02deleteall
M 644 inline file.rb
data 12
version two
commit refs/heads/master
mark :2
committer Scott Chacon &lt;schacon@geemail.com&gt; 1231056000 -0700
data 29
imported from back_2009_01_04from :1
deleteall
M 644 inline file.rb
data 14
version three
M 644 inline new.rb
data 16
new version one
(...)
</pre>

<p>
  Um den Importer zu starten, leite die Ausgabe durch eine Pipe zu <code>git fast-import</code> weiter — während Du im Git Verzeichnis befindest, in das Du importieren
  willst. Zum Anfang kannst Du ein neues Verzeichnis anlegen, darin <code>git init</code> laufen lassen und anschließend das Skript starten:
</p>

<pre>
$ git init
Initialized empty Git repository in /opt/import_to/.git/
$ ruby import.rb /opt/import_from | git fast-import
git-fast-import statistics:
---------------------------------------------------------------------
Alloc'd objects:       5000
Total objects:           18 (         1 duplicates                  )
      blobs  :            7 (         1 duplicates          0 deltas)
      trees  :            6 (         0 duplicates          1 deltas)
      commits:            5 (         0 duplicates          0 deltas)
      tags   :            0 (         0 duplicates          0 deltas)
Total branches:           1 (         1 loads     )
      marks:           1024 (         5 unique    )
      atoms:              3
Memory total:          2255 KiB
       pools:          2098 KiB
     objects:           156 KiB
---------------------------------------------------------------------
pack_report: getpagesize()            =       4096
pack_report: core.packedGitWindowSize =   33554432
pack_report: core.packedGitLimit      =  268435456
pack_report: pack_used_ctr            =          9
pack_report: pack_mmap_calls          =          5
pack_report: pack_open_windows        =          1 /          1
pack_report: pack_mapped              =       1356 /       1356
---------------------------------------------------------------------
</pre>

<p>
  Wie Du sehen kannst, werden Dir eine Menge Statistiken über den erreichten
  Erfolg angezeigt. In diesem Beispiel haben wir insgesamt 18 Objekte für 5
  Commits in einen Zweig importiert. Jetzt kannst Du <code>git log</code> ausführen, um die neue History einzusehen:
</p>

<pre>
$ git log -2
commit 10bfe7d22ce15ee25b60a824c8982157ca593d41
Author: Scott Chacon &lt;schacon@example.com&gt;
Date:   Sun May 3 12:57:39 2009 -0700

    imported from current

commit 7e519590de754d079dd73b44d695a42c9d2df452
Author: Scott Chacon &lt;schacon@example.com&gt;
Date:   Tue Feb 3 01:00:00 2009 -0700

    imported from back_2009_02_03
</pre>

<p>
  Na also — jetzt haben wir ein schickes, sauberes Git-Repository. Dabei ist es
  wichtig, dass noch nichts ausgecheckt ist — zu Beginn hast Du keinerlei
  Dateien in Deinem Arbeitsverzeichnis. Um an sie heran zu kommen, musst Du
  Deinen Zweig dahin zurücksetzen, wo sich <code>master</code> gerade befindet:
</p>

<pre>
$ ls
$ git reset --hard master
HEAD is now at 10bfe7d imported from current
$ ls
file.rb  lib
</pre>

<p>
  Du kannst noch eine ganze Menge mehr mit dem <code>fast-import</code>-Tool anstellen — es kann verschiedene Modi behandeln, Binärdaten, mehrere
  Zweige sowie Merges, Tags, Fortschrittsbalken und mehr. Eine Reihe von
  Beispielen komplexerer Szenarios werden im <code>contrib/fast-import</code>-Verzeichnis des Git-Quellcodes bereitgestellt; eines der besseren ist das <code>git-p4</code> -Skript, das ich gerade behandelt habe.
</p>

<h2>Zusammenfassung</h2>

<p>
  Du solltest Dich jetzt ausreichend sicher fühlen im Umgang mit Git und
  Subversion bzw. mit dem Import von nahezu jedem existierenden Repository in
  ein neues Git-Repository, ohne Daten zu verlieren. Das nächste Kapitel wird
  die Interna von Git behandeln, damit Du jedes einzelne Byte bearbeiten
  kannst, falls es nötig sein sollte.
</p>

<h1>Git Interna</h1>

<p>
  Möglicherweise hast Du dieses Kapitel hier direkt aufgeschlagen, vorherige
  Kapitel ausgelassen, oder bist hierher gelangt, nachdem Du alle vorherigen
  Kapitel gelesen hast. Wie auch immer, in diesem Kapitel werden wir uns mit
  der internen Funktionsweise und der Implementierung von Git befassen. Meine
  eigene Erfahrung ist, dass das Lernen dieser Dinge unerlässlich ist, um zu
  verstehen, wie unheimlich mächtig und flexibel Git ist. Allerdings gibt es
  Leute, die der Ansicht sind, dass sie auch verwirrend und unnötig komplex für
  Anfänger sein können. Deshalb habe ich mich entschieden, dieses Kapitel an
  das Ende des Buches zu verlegen. Du kannst es, ganz wie es Dir beliebt,
  früher oder später einschieben.
</p>

<p>
  Lass uns also loslegen. Zunächst will ich betonen, falls das bisher noch
  nicht klargeworden ist, dass Git im seinen Grundzügen ein Dateisystem ist,
  dessen Inhalte addressierbar sind und auf dem eine VCS-Schnittstelle
  aufgesetzt ist. Wir werden gleich genauer darauf eingehen, was das heißt.
</p>

<p>
  In den frühen Tagen von Git (d.h. vor Version 1.5) war die
  Benutzerschnittstelle sehr viel komplexer, weil es die
  Dateisystem-Eigenschaften stark betonte – im Gegensatz zu einem herkömmlichen
  VCS. In den letzten Jahren wurde die Benutzerschnittstelle dann stückweise
  verbessert und verfeinert, sodass es heute so einfach verständlich und
  einfach zu verwenden ist, wie andere vergleichbare Systeme, die auf dem Markt
  erhältlich sind. Allerdings besteht scheinbar weiterhin das Vorurteil, das
  Git-Interface sei komplex und schwer zu erlernen.
</p>

<p>
  Die inhaltsbasiert adressierbare Dateisystem-Ebene ist erstaunlich cool,
  weshalb ich in diesem Kapitel zuerst darauf eingehen werde. Als Nächstes
  lernst Du etwas über die Transport-Mechanismen und
  Repository-Wartungsaufgaben, mit denen Du möglicherweise irgendwann zu tun
  bekommen wirst.
</p>

<h2>Plumbing und Porcelain</h2>

<p>
  In diesem Buch haben wir Git besprochen, indem wir vielleicht 30 Befehle wie <code>checkout</code>, <code>branch</code>, <code>remote</code> und so weiter verwendet haben. Weil Git aber ursprünglich als ein
  Werkzeugkasten konzipiert war und nicht so sehr als ein komplettes,
  anwenderfreundliches VCS, gibt es auch eine Reihe von Befehlen, die ihre
  Arbeit auf einer sehr viel grundlegenderen Ebene verrichten. Viele davon sind
  ursprünglich entwickelt worden, um als UNIX-Befehle miteinander verkettet zu
  werden oder aus Skripten heraus aufgerufen zu werden. Diese Befehle werden
  oft als „plumbing“-Befehle (Klempner-Befehle) zusammengefasst, während die
  eher anwenderfreundlichen Befehle „porcelain“ (d.h. Porzellan) genannt
  werden.
</p>

<p>
  Die ersten acht Kapitel dieses Buches haben sich fast ausschließlich mit
  „Porcelain“-Befehlen befasst. In diesem Kapitel gehen wir dagegen auf die
  zugrundeliegenden „Plumbing“-Befehle ein, u.a. weil sie Dir den Zugriff auf
  die inneren Abläufe von Git ermöglichen, und weil sie dabei helfen, zu
  verstehen, warum Git tut, was es tut. Diese Befehle sind nicht dazu gedacht,
  manuell in der Eingabeaufforderung ausgeführt zu werden, sondern sind als
  Bausteine für Werkzeuge und Skripts gemeint.
</p>

<p>
  Wenn Du <code>git init</code> in einem neuen oder bereits bestehenden Verzeichnis ausführst, erzeugt Git
  das <code>.git</code>-Verzeichnis, das fast alle Dateien enthält, die Git intern speichert und
  ändert. Wenn Du eine Sicherheitskopie Deines Repositorys anlegen oder es
  duplizieren willst, dann reicht es aus, dieses Verzeichnis zu kopieren.
  Dieses ganze Kapitel handelt praktisch nur von den Inhalten dieses
  Verzeichnisses. Schauen wir es uns einmal an:
</p>

<pre>
$ ls
HEAD
branches/
config
description
hooks/
index
info/
objects/
refs/
</pre>

<p>
  Möglicherweise findest Du darin weitere Dateien. Obiges stammt aus einem mit <code>git init</code> neu angelegten Repository – das sind also die Standardinhalte. Der Ordner <code>branches</code> wird von neueren Git-Versionen nicht mehr verwendet, und die Datei <code>descriptions</code> wird nur vom Programm GitWeb benötigt. Du kannst sie also ignorieren. Die
  Datei <code>config</code> enthält Deine projekt-spezifischen Konfigurationsoptionen, und im Ordner <code>info</code> befindet sich eine Datei, die globale Dateiausschlussmuster enthält, die Du
  nicht in jeder .gitignore-Datei neu spezifizieren willst. Das <code>hooks</code>-Verzeichnis enthält die client- oder serverseitigen Hook-Skripte, die wir in
  Kapitel 7 besprochen haben.
</p>

<p>
  Damit bleiben vier wichtige Einträge übrig: die Dateien <code>HEAD</code> und <code>index</code> und die Verzeichnisse <code>objects</code> und <code>refs</code>. Dies sind die Kernkomponenten eines Git-Repositorys. Im <code>objects</code>-Verzeichnis befinden sich die Inhalte der Datenbank. Das <code>refs</code>-Verzeichnis enthält Referenzen auf Commit-Objekte (Branches) in dieser
  Datenbank. Die Datei <code>HEAD</code> zeigt auf denjeningen Branch, den Du gegenwärtig ausgecheckt hast, und in
  der Datei <code>index</code> verwaltet Git die Informationen der Staging-Area. Wir werden auf diese
  Elemente jetzt im einzelnen darauf eingehen, sodass Du nachvollziehen kannst,
  wie Git intern arbeitet.
</p>

<h2>Git Objekte</h2>

<p>
  Git ist ein Dateisystem, das Inhalte addressieren kann. Prima. Aber was heißt
  das? Es bedeutet, dass Git im Kern nichts anderes ist als ein einfacher
  Key-Value-Store („Schlüssel-Wert-Speicher“). Du kannst darin jede Art von
  Inhalt ablegen und Git wird einen Schlüssel dafür zurückgeben, den Du dann
  verwenden kannst, um diesen Inhalt jederzeit nachzuschlagen. Um das
  auszuprobieren, kannst Du den Plumbing-Befehl <code>hash-object</code> verwenden. Dieser nimmt Daten entgegen, speichert diese in Deinem <code>.git</code>-Verzeichnis und gibt Dir den Schlüssel zurück, unter dem der Inhalt
  gespeichert wurde. Dazu initialisierst Du als erstes ein neues Git-Repository
  und verifizierst, dass das <code>objects</code>-Verzeichnis leer ist:
</p>

<pre>
$ mkdir test
$ cd test
$ git init
Initialized empty Git repository in /tmp/test/.git/
$ find .git/objects
.git/objects
.git/objects/info
.git/objects/pack
$ find .git/objects -type f
$
</pre>

<p>
  Git hat also ein Verzeichnis <code>objects</code> und darin die Unterverzeichnisse <code>pack</code> und <code>info</code> angelegt, bisher aber keine weiteren Dateien. Als nächsten speichern wir
  einen Text in dieser Git-Datenbank:
</p>

<pre>
$ echo 'test content' | git hash-object -w --stdin
d670460b4b4aece5915caf5c68d12f560a9fe3e4
</pre>

<p>
  Die Option <code>-w</code> weist <code>git hash-object</code> an, das Objekt zu speichern. Andernfalls würde Dir der Befehl lediglich den
  Schlüssel mitteilen. <code>--stdin</code> weist den Befehl an, den Inhalt von der Standardeingabe einzulesen. Wenn Du
  diese Option weglässt, erwartet der Befehl zusätzlich einen Dateipfad. Die
  Ausgabe ist ein 40 Zeichen langer SHA-1-Hash, der eine Prüfsumme des
  gespeicherten Inhaltes darstellt (wir gehen auf diese Hashes gleich noch
  genauer ein). Git hat jetzt außerdem eine neue Datei in der Datenbank
  angelegt:
</p>

<pre>
$ find .git/objects -type f
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
</pre>

<p>
  Jetzt liegt im Verzeichnis <code>objects</code> genau eine Datei. Anfangs speichert Git den Inhalt auf diese Art und Weise:
  In jeweils einer einzelnen Datei werden die Daten gespeichert, referenziert
  durch den SHA-1-Hash des Inhaltes und seines Headers. Der Name des
  Unterverzeichnis <code>d6</code> entspricht den ersten zwei Zeichen des SHA-1-Hashes. Die verbleibenden 38
  Zeichen werden als Dateiname verwendet.
</p>

<p>
  Mit dem Befehl <code>git cat-file</code> kannst Du den jeweiligen Inhalt nachschlagen. Dieser Befehl ist so etwas wie
  ein Schweizer Taschenmesser, wenn es um Objekte in der Git-Datenbank geht.
  Wenn Du die Option <code>-p</code> übergibst, versucht <code>git cat-file</code>, die Art des Inhaltes herauszufinden und lesbar darzustellen:
</p>

<pre>
$ git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4
test content
</pre>

<p>
  Auf diese Weise kannst Du also Inhalte zu Git hinzufügen und von dort wieder
  auslesen. Das klappt auch mit Dateiinhalten. Wenn Du beispielsweise eine
  einzelne Datei versionieren willst, legst Du dazu die Datei zunächst an und
  speicherst ihren Inhalt in der Datenbank:
</p>

<pre>
$ echo 'version 1' &gt; test.txt
$ git hash-object -w test.txt
83baae61804e65cc73a7201a7252750c76066a30
</pre>

<p>Dann kannst Du Änderungen vornehmen und die Datei erneut speichern:</p>

<pre>
$ echo 'version 2' &gt; test.txt
$ git hash-object -w test.txt
1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
</pre>

<p>
  Die Datenbank enthält jetzt zwei weitere Versionen der Datei neben dem
  ursprünglich gespeicherten Inhalt:
</p>

<pre>
$ find .git/objects -type f
.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
</pre>

<p>
  Jetzt kannst Du die erste Version der Datei mit folgendem Befehl wieder
  herstellen:
</p>

<pre>
$ git cat-file -p 83baae61804e65cc73a7201a7252750c76066a30 &gt; test.txt
$ cat test.txt
version 1
</pre>

<p>Oder die zweite Version:</p>

<pre>
$ git cat-file -p 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a &gt; test.txt
$ cat test.txt
version 2
</pre>

<p>
  Sich den SHA-1-Hash für jede Version merken zu müssen, ist allerdings nicht
  sonderlich praktisch. Außerdem speicherst Du nicht den Dateinamen in der
  Datenbank, sondern lediglich den Inhalt der Datei. Ein solcher Objekttyp wird
  als „Blob“ bezeichnet. Mit <code>git cat-file -t</code> kannst Du Git nach dem Typ eines Objektes in der Datenbank fragen:
</p>

<pre>
$ git cat-file -t 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
blob
</pre>

<h3>Baum-Objekte</h3>

<p>
  Als Nächstes schauen wir uns den Objekttyp „Tree“ (Baum) an, der es
  ermöglicht, Dateinamen zu speichern und Dateien zu gruppieren. Git speichert
  Inhalte in einer ähnlichen Weise wie das UNIX-Dateisystem, allerdings ein
  bisschen vereinfacht. Sie werden als Tree- und Blob-Objekte abgelegt, wobei
  die Trees mit UNIX-Verzeichnis-Einträgen korrespondieren und Blobs mehr oder
  weniger mit den Inode-Einträgen bzw. Datei-Inhalten. Ein einzelnes
  Baum-Objekt enthält einen oder mehrere Einträge, von denen jeder ein
  SHA-1-Hash ist, der wiederum einen Blob oder einen Untertree referenziert.
  Jeder dieser Einträge verfügt außerdem über einen Modus, Typ und Dateinamen.
  Beispielsweise sieht das aktuelle Tree-Objekt im simplegit-Projekt
  möglicherweise so aus:
</p>

<pre>
$ git cat-file -p master^{tree}
100644 blob a906cb2a4a904a152e80877d4088654daad0c859      README
100644 blob 8f94139338f9404f26296befa88755fc2598c289      Rakefile
040000 tree 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0      lib
</pre>

<p>
  Die <code>master^{tree}</code>-Syntax spezifiziert, dass wir an dem Tree-Objekt interessiert sind, auf das
  der letzte Commit des Branches <code>master</code> zeigt. Beachte, dass das Unterverzeichnis <code>lib</code> nicht auf ein Blob, sondern wiederum auf einen weiteren Baum zeigt.
</p>

<pre>
$ git cat-file -p 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0
100644 blob 47c6340d6459e05787f644c2447d2595f5d3a54b      simplegit.rb
</pre>

<p>
  Git speichert Daten also, konzeptuell gesehen, in etwa wie in Bild 9-1
  dargestellt.
</p>

<p>
  Insert 18333fig0901.png Bild 9-1. Vereinfachte Darstellung des
  Git-Datenmodels.
</p>

<p>
  Du kannst auch Deine eigenen Tree-Objekte anlegen. Git erzeugt Trees
  normalerweise, indem es die Inhalte der Staging-Area nimmt und als ein
  Tree-Objekt speichert. D.h., um ein Tree-Objekt anzulegen, musst Du zunächst
  einen Index (d.h. eine Staging-Area) aufbauen, indem Du einige Dateien
  hinzufügst. Um einen einzelnen Eintrag in den Index zu schreiben – z.B. die
  erste version der Datei test.txt – kannst Du den Plumbing-Befehl <code>git update-index</code> verwenden, der eine frühere Version dieser Datei künstlich zu einer neuen
  Staging-Area hinzufügt. Du musst ihm die Option <code>--add</code> übergeben, weil die Datei bisher noch nicht in der Staging-Area enthalten
  ist (Du hast ja bisher noch überhaupt keine Staging-Area aufgesetzt), und die
  Option <code>--cacheinfo</code>, weil Du eine Datei hinzufügst, die sich nicht in Deinem Verzeichnis
  befindet, sondern in der Datenbank. Du gibst außerdem den Modus, SHA-1-Hash
  und den Dateinamen an:
</p>

<pre>
$ git update-index --add --cacheinfo 100644 \
  83baae61804e65cc73a7201a7252750c76066a30 test.txt
</pre>

<p>
  In diesem Fall gibst Du als Modus <code>100644</code> an, was bedeutet, dass es sich um eine normale Datei handelt. Eine
  ausführbare Datei wäre dagegen <code>100755</code> und ein symbolischer Link <code>120000</code>. Der Modus entspricht normalen UNIX-Datei-Modi, ist aber weniger flexibel.
  Die drei genannten Modi sind die einzigen, die in Git für Dateien (Blobs)
  verwendet werden (es gibt allerdings noch weitere Modi für Verzeichnisse und
  Submodule).
</p>

<p>
  Jetzt kannst Du den Befehl <code>git write-tree</code> verwenden, um die Staging-Area als Tree-Objekt zu schreiben. Dazu brauchst
  Du die <code>-w</code> Option nicht angeben – <code>git write-tree</code> schreibt automatisch ein Tree-Objekt für Einträge der Staging-Area, für die
  es noch keinen Tree gibt:
</p>

<pre>
$ git write-tree
d8329fc1cc938780ffdd9f94e0d364e0ea74f579
$ git cat-file -p d8329fc1cc938780ffdd9f94e0d364e0ea74f579
100644 blob 83baae61804e65cc73a7201a7252750c76066a30      test.txt
</pre>

<p>Um zu überprüfen, ob es sich wirklich um ein Tree-Objekt handelt:</p>

<pre>
$ git cat-file -t d8329fc1cc938780ffdd9f94e0d364e0ea74f579
tree
</pre>

<p>
  Jetzt erzeugen wir einen neuen Tree mit der zweiten Version der Datei
  test.txt sowie einer neuen Datei:
</p>

<pre>
$ echo 'new file' &gt; new.txt
$ git update-index test.txt
$ git update-index --add new.txt
</pre>

<p>
  Die Staging-Area enthält jetzt eine neue Version der Datei test.txt sowie die
  neue Datei new.txt. Speichern wir diesen Tree (d.h. den gegenwärtigen Status
  der Staging-Area bzw. des Index als Tree-Objekt) und schauen ihn uns an:
</p>

<pre>
$ git write-tree
0155eb4229851634a0f03eb265b69f5a2d56f341
$ git cat-file -p 0155eb4229851634a0f03eb265b69f5a2d56f341
100644 blob fa49b077972391ad58037050f2a75f74e3671e92      new.txt
100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a      test.txt
</pre>

<p>
  Beachte, dass das Tree-Objekt beide Datei-Einträge hat und dass der
  SHA-1-Hash der Datei test.txt noch derselbe „Version 2“-Hash ist wie zuvor (<code>1f7a7a</code>). Fügen wir jetzt den ersten Tree als ein Unterverzeichnis in diesem hier
  ein. Du kannst einen Tree mit <code>git read-tree</code> in die Staging-Area einlesen. In diesem Fall können wir einen bereits
  existierenden Tree als einen Untertree zur Staging-Area hinzufügen, indem wir
  die Option <code>--prefix</code> verwenden:
</p>

<pre>
$ git read-tree --prefix=bak d8329fc1cc938780ffdd9f94e0d364e0ea74f579
$ git write-tree
3c4e9cd789d88d8d89c1073707c3585e41b0e614
$ git cat-file -p 3c4e9cd789d88d8d89c1073707c3585e41b0e614
040000 tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579      bak
100644 blob fa49b077972391ad58037050f2a75f74e3671e92      new.txt
100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a      test.txt
</pre>

<p>
  Wenn Du ein Arbeitsverzeichnis aus diesem neuen Tree-Objekt auschecken
  würdest, würdest Du zwei Dateien im Hauptverzeichnis und ein Unterverzeichnis
  mit dem Namen <code>bak</code> erhalten, in dem sich die erste Version der Datei test.txt befindet. Du
  kannst Dir die Daten, die Git für diese Strukturen speichert, in etwa wie in
  Bild 9-2 vorstellen.
</p>

<p>
  Insert 18333fig0902.png Bild 9-2. Die Datenstruktur des gegenwärtigen
  Git-Repositorys.
</p>

<h3>Objekte committen</h3>

<p>
  Du hast jetzt drei Trees, die verschiedene Snapshots Deines Projektes
  spezifizieren, die Du nachverfolgen willst. Das ursprüngliche Problem besteht
  aber weiterhin: Du musst Dir alle drei SHA-1-Hashwerte merken, um wieder an
  die Snapshots zu kommen. Ebenso fehlen Dir die Informationen darüber, wer die
  Snapshots gespeichert hat, wann sie gespeichert wurden und warum. Dies sind
  die drei Hauptinformationen, die ein Commit-Objekt für uns speichert.
</p>

<p>
  Um ein Commit-Objekt anzulegen, verwendest Du den Befehl <code>git commit-tree</code>, spezifizierst den SHA-1-Hash eines einzelnen Trees und welche
  Commit-Objekte (sofern vorhanden) die direkten Vorgänger sind. Fangen wir
  damit an, den ersten Tree, den Du angelegt hast, einzuchecken:
</p>

<pre>
$ echo 'first commit' | git commit-tree d8329f
fdf4fc3344e67ab068f836878b6c4951e3b15f3d
</pre>

<p>
  Du kannst Dir dann dieses neue Commit-Objekt mit <code>cat-file</code> anschauen:
</p>

<pre>
$ git cat-file -p fdf4fc3
tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579
author Scott Chacon &lt;schacon@gmail.com&gt; 1243040974 -0700
committer Scott Chacon &lt;schacon@gmail.com&gt; 1243040974 -0700

first commit
</pre>

<p>
  Das Format für ein Commit-Objekt ist einfach: es besteht aus dem obersten
  Tree für den Snapshot des Projektes zum gegebenen Zeitpunkt, die Autoren- und
  ggf. Committer-Information (jeweils entsprechend Deiner <code>user.name</code>- und <code>user.email</code>-Konfiguration) und dem aktuellen Zeitstempel. Dann folgen eine leere Zeile
  und die Commit-Nachricht.
</p>

<p>
  Als Nächstes speichern wir die beiden anderen Commit-Objekte und
  referenzieren jeweils den vorhergehenden Commit:
</p>

<pre>
$ echo 'second commit' | git commit-tree 0155eb -p fdf4fc3
cac0cab538b970a37ea1e769cbbde608743bc96d
$ echo 'third commit'  | git commit-tree 3c4e9c -p cac0cab
1a410efbd13591db07496601ebc7a059dd55cfe9
</pre>

<p>
  Jedes der drei Commit-Objekte zeigt auf einen der drei Snapshot-Trees, die Du
  zuvor gespeichert hattest. Es mag Dich überraschen, aber Du hast jetzt
  bereits eine vollständige Git-Historie, die Du mit dem Befehl <code>git log</code> inspizieren kannst, indem Du den SHA-1-Hash des letzten Commits angibst:
</p>

<pre>
$ git log --stat 1a410e
commit 1a410efbd13591db07496601ebc7a059dd55cfe9
Author: Scott Chacon &lt;schacon@gmail.com&gt;
Date:   Fri May 22 18:15:24 2009 -0700

    third commit

 bak/test.txt |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

commit cac0cab538b970a37ea1e769cbbde608743bc96d
Author: Scott Chacon &lt;schacon@gmail.com&gt;
Date:   Fri May 22 18:14:29 2009 -0700

    second commit

 new.txt  |    1 +
 test.txt |    2 +-
 2 files changed, 2 insertions(+), 1 deletions(-)

commit fdf4fc3344e67ab068f836878b6c4951e3b15f3d
Author: Scott Chacon &lt;schacon@gmail.com&gt;
Date:   Fri May 22 18:09:34 2009 -0700

    first commit

 test.txt |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)
</pre>

<p>
  Fantastisch, oder? Du hast jetzt sämtliche Low-Level-Operationen
  durchgeführt, die eine vollständige Git-Historie aufbauen, ohne aber
  irgendwelche Frontend-Befehle von Git zu verwenden. Im Wesentlichen ist das
  derselbe Prozess, der im Hintergrund stattfindet, wenn Du die Befehle <code>git add</code> und <code>git commit</code> ausführst. Sie speichern Blobs für die Dateien, die Du hinzugefügt oder
  geändert hast, aktualisieren den Index (d.h. die Staging-Area), speichern
  Trees und legen Commit Objekte an, die die obersten Trees sowie die Commits
  referenzieren, die ihnen unmittelbar vorhergingen. Diese drei Hauptobjekte –
  Blob, Tree und Commit – werden zunächst als separate Dateien im Verzeichnis <code>.git/objects</code> gespeichert. Hier ist eine Liste aller Objekte, die sich in unserem
  Beispiel-Repository jetzt in der Datenbank befinden – jeweils mit einem
  Kommentar darüber, was sie speichern:
</p>

<pre>
$ find .git/objects -type f
.git/objects/01/55eb4229851634a0f03eb265b69f5a2d56f341 # tree 2
.git/objects/1a/410efbd13591db07496601ebc7a059dd55cfe9 # commit 3
.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a # test.txt v2
.git/objects/3c/4e9cd789d88d8d89c1073707c3585e41b0e614 # tree 3
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30 # test.txt v1
.git/objects/ca/c0cab538b970a37ea1e769cbbde608743bc96d # commit 2
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4 # 'test content'
.git/objects/d8/329fc1cc938780ffdd9f94e0d364e0ea74f579 # tree 1
.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 # new.txt
.git/objects/fd/f4fc3344e67ab068f836878b6c4951e3b15f3d # commit 1
</pre>

<p>
  Wenn man all diese internen Zeiger nachverfolgt, erhält man einen
  Objekt-Graphen wie den folgenden (Bild 9-3).
</p>

<p>Insert 18333fig0903.png Bild 9-3. Alle Objekte in Deinem Git-Repository.</p>

<h3>Objekt-Speicher</h3>

<p>
  Ich habe bereits erwähnt, dass zusammen mit dem jeweiligen Inhalt ein Header
  gespeichert wird. Schauen wir uns also genauer an, wie genau Git Objekte
  speichert. Du wirst sehen, wie ein Blob-Objekt – in diesem Fall die
  Zeichenkette „what is up, doc?“ gespeichert wird. Dazu nutzen wir den
  interaktiven Ruby-Modus, den Du mit dem Befehl <code>irb</code> starten kannst:
</p>

<pre>
$ irb
&gt;&gt; content = "what is up, doc?"
=&gt; "what is up, doc?"
</pre>

<p>
  Git erzeugt einen Header, der mit dem Objekttyp beginnt, in diesem Fall ist
  das ein Blob. Dann folgt ein Leerzeichen, die Anzahl der Zeichen des Inhalts
  und schließlich ein Nullbyte.
</p>

<pre>
&gt;&gt; header = "blob #{content.length}\0"
=&gt; "blob 16\000"
</pre>

<p>
  Git fügt diesen Header mit dem ursprünglichen Inhalt zusammen und kalkuliert
  aus dem Ergebnis die SHA-1-Prüfsumme. Du kannst einen SHA-1-Hash in Ruby
  berechnen, indem Du die SHA1-Digest-Bibliothek mit <code>require</code> einbindest und dann <code>Digest::SHA1.hexdigest()</code> mit der Zeichenkette ausführst:
</p>

<pre>
&gt;&gt; store = header + content
=&gt; "blob 16\000what is up, doc?"
&gt;&gt; require 'digest/sha1'
=&gt; true
&gt;&gt; sha1 = Digest::SHA1.hexdigest(store)
=&gt; "bd9dbf5aae1a3862dd1526723246b20206e5fc37"
</pre>

<p>
  Git komprimiert den neuen Inhalt (d.h. inklusive des Headers) mit zlib. In
  Ruby kannst Du dazu die zlib-Bibliothek verwenden, indem Du wiederum zuerst
  die Bibliothek mit <code>require</code> einbindest und dann <code>Zlib::Deflate.deflate()</code> mit dem Inhalt aufrufst:
</p>

<pre>
&gt;&gt; require 'zlib'
=&gt; true
&gt;&gt; zlib_content = Zlib::Deflate.deflate(store)
=&gt; "x\234K\312\311OR04c(\317H,Q\310,V(-\320QH\311O\266\a\000_\034\a\235"
</pre>

<p>
  Schließlich schreibst Du den zlib-komprimierten Inhalt in eine Datei auf der
  Festplatte. Dazu bestimmst Du den Pfad, an den die Datei gespeichert wird
  (die ersten beiden Zeichen für das Unterverzeichnis und die verbleibenden 38
  Zeichen für den Dateinamen). In Ruby kannst Du die Funktion <code>FileUtils.mkdir_p()</code> verwenden, um Unterverzeichnisse anzulegen, die noch nicht existieren. Dann
  öffnest Du die Datei mit <code>File.open()</code> und schreibst den komprimierten Inhalt mit <code>write()</code> in die Datei:
</p>

<pre>
&gt;&gt; path = '.git/objects/' + sha1[0,2] + '/' + sha1[2,38]
=&gt; ".git/objects/bd/9dbf5aae1a3862dd1526723246b20206e5fc37"
&gt;&gt; require 'fileutils'
=&gt; true
&gt;&gt; FileUtils.mkdir_p(File.dirname(path))
=&gt; ".git/objects/bd"
&gt;&gt; File.open(path, 'w') { |f| f.write zlib_content }
=&gt; 32
</pre>

<p>
  Das ist alles – Du hast jetzt ein gültiges Blob-Objekt geschrieben.
  Git-Objekte werden immer in dieser Weise gespeichert, lediglich mit
  verschiedenen Typen, d.h. anstelle der Zeichenkette „blob“ wird der Header
  mit „commit“ oder „tree“ anfangen. Außerdem sind Commit- und Tree-Inhalte auf
  eine sehr spezifische Weise formatiert, während Blobs beliebige Inhalte sein
  können.
</p>

<h2>Git-Referenzen</h2>

<p>
  Du kannst Befehle wie <code>git log 1a410e</code> ausführen, um die Commit-Historie zu inspizieren, aber dazu musst Du Dir
  jeweils merken, dass <code>1a410e</code> der jeweils letzte Commit ist. Um diese SHA-1-Hashes mit einfacheren,
  verständlichen Namen zu referenzieren, verwendet Git weitere Dateien, in
  denen die Namen für Hashes gespeichert sind.
</p>

<p>
  Diese Namen werden in Git intern als „references“ oder „refs“ (also Referenz
  bzw. Verweis) bezeichnet. Du kannst diese Dateien, die SHA-1-Hashes
  enthalten, im Verzeichnis <code>.git/refs</code> finden. In unserem gegenwärtigen Projekt enthält dieses Verzeichnis noch
  keine Dateien, aber eine simple Verzeichnisstruktur:
</p>

<pre>
$ find .git/refs
.git/refs
.git/refs/heads
.git/refs/tags
$ find .git/refs -type f
$
</pre>

<p>
  Um jetzt eine neue Referenz anzulegen, die Dir dabei hilft, Dich zu erinnern,
  wo sich Dein letzter Commit befindet, könntest Du, technisch gesehen,
  Folgendes tun:
</p>

<pre>
$ echo "1a410efbd13591db07496601ebc7a059dd55cfe9" &gt; .git/refs/heads/master
</pre>

<p>
  Jetzt kannst Du diese „head“-Referenz anstelle des SHA-1-Wertes in allen
  möglichen Git-Befehlen verwenden:
</p>

<pre>
$ git log --pretty=oneline  master
1a410efbd13591db07496601ebc7a059dd55cfe9 third commit
cac0cab538b970a37ea1e769cbbde608743bc96d second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit
</pre>

<p>
  Allerdings ist es nicht empfehlenswert, die Referenz-Dateien direkt zu
  bearbeiten. Git stellt einen sichereren Befehl dafür zur Verfügung, den
  Befehl <code>git update-ref</code>:
</p>

<pre>
$ git update-ref refs/heads/master 1a410efbd13591db07496601ebc7a059dd55cfe9
</pre>

<p>
  Im Prinzip ist das alles, was einen Branch in Git ausmacht: ein simpler
  Zeiger oder eine Referenz auf den jeweiligen Head einer Arbeitsreihe. Um
  einen neuen Branch anzulegen, der vom zweiten Commit aus verzweigt, kannst Du
  Folgendes tun:
</p>

<pre>
$ git update-ref refs/heads/test cac0ca
</pre>

<p>Dein Branch beginnt jetzt beim zweiten Commit:</p>

<pre>
$ git log --pretty=oneline test
cac0cab538b970a37ea1e769cbbde608743bc96d second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit
</pre>

<p>
  Die Git-Datenbank unseres Beispiel-Repositorys ist jetzt wie folgt
  strukturiert:
</p>

<p>
  Insert 18333fig0904.png Bild 9-4. Git-Verzeichnis-Objekte mit
  Branch-Head-Referenzen.
</p>

<p>
  Wenn Du Befehle wie <code>git branch (Branch-Name)</code> verwendest, führt Git intern im Wesentlichen den Befehl <code>update-ref</code> aus, um den SHA-1-Hash des letzten Commits des jeweils gegenwärtigen
  Branches mit dem gegebenen Namen zu referenzieren.
</p>

<h3>Der HEAD</h3>

<p>
  Die Frage ist jetzt: Wenn Du <code>git branch (Branch-Name)</code> ausführst, woher weiß Git den SHA-1 des letzten Commits? Die Antwort ist:
  aus der Datei HEAD. Diese Datei ist eine symbolische Referenz auf den
  jeweiligen Branch, auf dem Du Dich gerade befindest. Mit „symbolischer
  Referenz“ meine ich, dass sie (anders als eine „normale“ Referenz) keinen
  SHA-1-Hash enthält, sondern stattdessen auf eine andere Referenz zeigt. Wenn
  Du Dir die Datei ansiehst, findest Du normalerweise etwas wie:
</p>

<pre>
$ cat .git/HEAD
ref: refs/heads/master
</pre>

<p>
  Wenn Du jetzt <code>git checkout test</code> ausführst, wird Git die Datei aktualisieren, sodass sie so aussieht:
</p>

<pre>
$ cat .git/HEAD
ref: refs/heads/test
</pre>

<p>
  Wenn Du <code>git commit</code> ausführst, erzeugt Git das Commit-Objekt und verwendet als Parent des
  Commit-Objektes den jeweiligen Wert der Referenz, auf die HEAD zeigt.
</p>

<p>
  Du kannst diese Datei manuell bearbeiten, aber wiederum verfügt Git über
  einen sichereren Befehl, um das zu tun: <code>git symbolic-ref</code>. Du kannst den Wert des HEAD mit Hilfe des folgenden Befehls lesen:
</p>

<pre>
$ git symbolic-ref HEAD
refs/heads/master
</pre>

<p>Und so kannst Du ihn setzen:</p>

<pre>
$ git symbolic-ref HEAD refs/heads/test
$ cat .git/HEAD
ref: refs/heads/test
</pre>

<p>
  Du kannst den Befehl allerdings nicht verwenden, um eine Referenz außerhalb
  von <code>refs</code> zu setzen:
</p>

<pre>
$ git symbolic-ref HEAD test
fatal: Refusing to point HEAD outside of refs/
</pre>

<h3>Tags</h3>

<p>
  Wir haben jetzt Gits drei Haupt-Objekttypen besprochen, aber es gibt noch
  einen vierten. Das Tag-Objekt ist dem Commit-Objekt sehr ähnlich: es enthält
  den Autor des Tags, ein Datum, eine Meldung und eine Referenz auf ein anderes
  Objekt. Der Hauptunterschied besteht darin, dass ein Tag-Objekt auf einen
  Commit zeigt und nicht auf einen Tree. Ein Tag ist in dieser Hinsicht also
  ähnlich einem Branch, aber er bewegt sich nie, sondern zeigt immer auf
  denselben Commit und gibt ihm damit einen netteren Namen.
</p>

<p>
  Wie wir schon in Kapitel 2 besprochen haben, gibt es zwei Typen von Tags:
  „annotierte“ und „einfache“. Du kannst einen einfachen Tag wie folgt anlegen:
</p>

<pre>
$ git update-ref refs/tags/v1.0 cac0cab538b970a37ea1e769cbbde608743bc96d
</pre>

<p>
  Das ist alles, woraus ein einfacher Tag besteht: einem Branch, der sich nie
  bewegt. Ein annotierter Tag ist komplexer. Wenn Du einen annotierten Tag
  anlegst, erzeugt Git ein Tag-Objekt und speichert eine Referenz, die darauf
  zeigt, statt direkt auf den Commit zu zeigen. Du kannst das sehen, wenn Du
  einen annotierten Tag anlegst (<code>-a</code> bewirkt, dass wir einen annotierten Tag erhalten):
</p>

<pre>
$ git tag -a v1.1 1a410efbd13591db07496601ebc7a059dd55cfe9 -m 'test tag'
</pre>

<p>Das erzeugt den folgenden Objekt SHA-1-Hash:</p>

<pre>
$ cat .git/refs/tags/v1.1
9585191f37f7b0fb9444f35a9bf50de191beadc2
</pre>

<p>
  Jetzt wendest Du den Befehl <code>git cat-file</code> auf diesen SHA-1-Hash an:
</p>

<pre>
$ git cat-file -p 9585191f37f7b0fb9444f35a9bf50de191beadc2
object 1a410efbd13591db07496601ebc7a059dd55cfe9
type commit
tag v1.1
tagger Scott Chacon &lt;schacon@gmail.com&gt; Sat May 23 16:48:58 2009 -0700

test tag
</pre>

<p>
  Beachte, dass der der Wert <code>object</code> auf den SHA-1 des Commits zeigt, den Du getaggt hast. Weiterhin muss der
  Eintrag nicht auf einen Commit zeigen. In Git kann man jedes beliebige Objekt
  taggen. Im Git-Quellcode befindet sich beispielweise der öffentliche
  GPG-Schlüssel des Projektbetreibers als ein Blob-Objekt, sowie ein Tag, der
  darauf zeigt. Du kannst Dir den öffentlichen Schlüssel anzeigen lassen, indem
  du den folgenden Befehl im Git-Quellcode-Repository ausführst:
</p>

<pre>
$ git cat-file blob junio-gpg-pub
</pre>

<p>
  Der Linux-Kernel hat auch ein Tag-Objekt, das nicht auf einen Commit zeigt –
  der erste erstellte Tag zeigt auf den anfänglichen Tree des
  Quelltext-Imports.
</p>

<h3>Externe Referenzen</h3>

<p>
  Der dritte Referenztyp ist die externe Referenz („remote reference“). Wenn Du
  einen externen Server („remote“) definierst und dorthin pushst, merkt sich
  Git den zuletzt gepushten Commit für jeden Branch im <code>refs/remotes</code> Verzeichnis. Bespielsweise fügst Du einen externen Server <code>origin</code> hinzu und pushst Deinen Branch <code>master</code> dorthin:
</p>

<pre>
$ git remote add origin git@github.com:schacon/simplegit-progit.git
$ git push origin master
Counting objects: 11, done.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (7/7), 716 bytes, done.
Total 7 (delta 2), reused 4 (delta 1)
To git@github.com:schacon/simplegit-progit.git
   a11bef0..ca82a6d  master -&gt; master
</pre>

<p>
  Dann kannst Du herausfinden, in welchem Zustand sich der Branch <code>master</code> auf dem Server <code>origin</code> zuletzt befand (d.h. als Du das letzte Mal mit ihm kommuniziert hast), indem
  Du Dir die Datei <code>refs/remotes/origin/master</code> anschaust:
</p>

<pre>
$ cat .git/refs/remotes/origin/master
ca82a6dff817ec66f44342007202690a93763949
</pre>

<p>
  Externe Referenzen unterscheiden sich von Branches (<code>refs/heads</code>) hauptsächlich dadurch, dass man sie nicht auschecken kann. Git verwendet
  sie quasi als Lesezeichen für den zuletzt bekannten Status, in dem sich die
  Branches auf externen Servern jeweils befanden.
</p>

<h2>Pack-Dateien</h2>

<p>
  Kommen wir noch einmal auf die Objekt-Datenbank zurück, die Du für Dein
  Test-Repository angelegt hast. Im Moment müsstest Du 11 Objekte haben: 4
  Blobs, 3 Trees, 3 Commits und 1 Tag:
</p>

<pre>
$ find .git/objects -type f
.git/objects/01/55eb4229851634a0f03eb265b69f5a2d56f341 # tree 2
.git/objects/1a/410efbd13591db07496601ebc7a059dd55cfe9 # commit 3
.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a # test.txt v2
.git/objects/3c/4e9cd789d88d8d89c1073707c3585e41b0e614 # tree 3
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30 # test.txt v1
.git/objects/95/85191f37f7b0fb9444f35a9bf50de191beadc2 # tag
.git/objects/ca/c0cab538b970a37ea1e769cbbde608743bc96d # commit 2
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4 # 'test content'
.git/objects/d8/329fc1cc938780ffdd9f94e0d364e0ea74f579 # tree 1
.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 # new.txt
.git/objects/fd/f4fc3344e67ab068f836878b6c4951e3b15f3d # commit 1
</pre>

<p>
  Git komprimiert die Inhalte dieser Dateien mit zlib und Du hast nicht
  sonderlich viele davon, sodass die Gesamtgröße der Dateien gerade mal 925
  Bytes beträgt. Wir wollen ein anderes interessantes Feature von Git
  demonstrieren, und dazu müssen wir eine größere Datei hinzufügen, z.B. die <code>repo.rb</code> Datei aus der Grit-Bibliothek, die Du schon verwendet hast. Diese Datei ist
  eine etwa 12K große Quelltext-Datei:
</p>

<pre>
$ curl -L https://raw.github.com/mojombo/grit/master/lib/grit/repo.rb &gt; repo.rb
$ git add repo.rb
$ git commit -m 'added repo.rb'
[master 484a592] added repo.rb
 3 files changed, 459 insertions(+), 2 deletions(-)
 delete mode 100644 bak/test.txt
 create mode 100644 repo.rb
 rewrite test.txt (100%)
</pre>

<p>
  Wenn Du Dir den resultierenden Tree anschaust, findest Du den SHA-1-Hash, den
  die Datei <code>repo.rb</code> für das Blob-Objekt erhalten hat:
</p>

<pre>
$ git cat-file -p master^{tree}
100644 blob fa49b077972391ad58037050f2a75f74e3671e92      new.txt
100644 blob 9bc1dc421dcd51b4ac296e3e5b6e2a99cf44391e      repo.rb
100644 blob e3f094f522629ae358806b17daf78246c27c007b      test.txt
</pre>

<p>
  Jetzt kannst Du mit <code>git cat-file</code> sehen, wie groß das Objekt ist:
</p>

<pre>
$ du -b .git/objects/9b/c1dc421dcd51b4ac296e3e5b6e2a99cf44391e
4102	.git/objects/9b/c1dc421dcd51b4ac296e3e5b6e2a99cf44391e
</pre>

<p>
  Als Nächstes ändern wir die Datei ein bisschen, um zu sehen, was passiert:
</p>

<pre>
$ echo '# testing' &gt;&gt; repo.rb
$ git commit -am 'modified repo a bit'
[master ab1afef] modified repo a bit
 1 files changed, 1 insertions(+), 0 deletions(-)
</pre>

<p>
  Wenn Du jetzt den Tree anschaust, der durch den Commit angelegt wurde,
  findest Du etwas Interessantes:
</p>

<pre>
$ git cat-file -p master^{tree}
100644 blob fa49b077972391ad58037050f2a75f74e3671e92      new.txt
100644 blob 05408d195263d853f09dca71d55116663690c27c      repo.rb
100644 blob e3f094f522629ae358806b17daf78246c27c007b      test.txt
</pre>

<p>
  Das Blob ist ein anderes, d.h. obwohl Du lediglich eine einzige Zeile an das
  Ende einer 400 Zeilen langen Datei angehängt hast, speichert Git den Inhalt
  jetzt als ein ganz neues Objekt:
</p>

<pre>
$ du -b .git/objects/05/408d195263d853f09dca71d55116663690c27c
4109	.git/objects/05/408d195263d853f09dca71d55116663690c27c
</pre>

<p>
  Du hast jetzt zwei fast identische 12K große Objekte auf Deiner Festplatte.
  Wäre es nicht besser, wenn Git nur das erste vollständig und das zweite
  lediglich als ein Delta zwischen dem ersten und dem zweiten speichern würde?
</p>

<p>
  Tatsächlich kann Git das. Das ursprüngliche Format, in dem Git Objekte in der
  Datenbank speichert, wird als „freies Objekt-Format“ („loose object format“)
  bezeichnet. Hin und wieder packt Git allerdings eine Reihe solcher Objekte in
  eine einzige binäre Datei zusammen, um Platz zu sparen und effizienter zu
  arbeiten. Eine solche Datei wird als „packfile“ bezeichnet. Git tut das immer
  dann, wenn zu viele freie Objekte vorhanden sind, wenn Du den Befehl <code>git gc</code> manuell ausführst oder wenn Du auf einen externen Server pushst. Schauen wir
  uns also an, was passiert, wenn wir manuell <code>git gc</code> ausführen:
</p>

<pre>
$ git gc
Counting objects: 17, done.
Delta compression using 2 threads.
Compressing objects: 100% (13/13), done.
Writing objects: 100% (17/17), done.
Total 17 (delta 1), reused 10 (delta 0)
</pre>

<p>
  Wenn Du das Objekt-Verzeichnis anschaust, siehst Du, dass die meisten Objekte
  jetzt fehlen und dass stattdessen zwei neue Objekte aufgetaucht sind:
</p>

<pre>
$ find .git/objects -type f
.git/objects/71/08f7ecb345ee9d0084193f147cdad4d2998293
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
.git/objects/info/packs
.git/objects/pack/pack-7a16e4488ae40c7d2bc56ea2bd43e25212a66c45.idx
.git/objects/pack/pack-7a16e4488ae40c7d2bc56ea2bd43e25212a66c45.pack
</pre>

<p>
  Die verbleibenden Objekte sind diejenigen Blobs, die nicht von irgendeinem
  Commit referenziert werden – in diesem Fall sind das die Beispielblobs „what
  is up, doc?“ und „test content“, die wir zuvor gespeichert hatten. Weil wir
  sie nie zu irgendeinem Commit hinzugefügt haben, werden sie als „dangling“
  (wörtlich: herumbaumelnd) betrachtet und nicht im Packfile zusammengepackt.
</p>

<p>
  Die beiden neuen Dateien sind das Packfile und ein Index. Das Packfile ist
  eine einzelne Datei, die die Inhalte all der Dateien umfasst, die jetzt aus
  dem Dateisystem entfernt worden sind. Der Index ist eine Datei, die auf
  Positionen von Objekten im Packfile zeigt, sodass Git schneller nach einem
  bestimmten Objekt suchen kann. Obwohl diese Objekte auf der Festplatte
  ingesamt 12K groß waren, bevor Du <code>git gc</code> ausgeführt hast, ist das Packfile jetzt nur 6K groß. D.h., Du hast den
  Platzverbrauch dadurch um die Hälfte reduziert. Toll, oder?
</p>

<p>
  Wie stellt Git das genau an? Wenn Git Objekte zusammen packt, sucht es nach
  Dateien, die ähnlich benannt und ähnlich groß sind, und speichert dann
  lediglich Deltas von einer Version zur nächsten. Du kannst ein Packfile
  inspizieren, um zu sehen, wie Git die Objekte gepackt hat. Der Plumbing
  Befehl <code>git verify-pack</code> macht das möglich:
</p>

<pre>
$ git verify-pack -v \
  .git/objects/pack/pack-7a16e4488ae40c7d2bc56ea2bd43e25212a66c45.idx
0155eb4229851634a0f03eb265b69f5a2d56f341 tree   71 76 5400
05408d195263d853f09dca71d55116663690c27c blob   12908 3478 874
09f01cea547666f58d6a8d809583841a7c6f0130 tree   106 107 5086
1a410efbd13591db07496601ebc7a059dd55cfe9 commit 225 151 322
1f7a7a472abf3dd9643fd615f6da379c4acb3e3a blob   10 19 5381
3c4e9cd789d88d8d89c1073707c3585e41b0e614 tree   101 105 5211
484a59275031909e19aadb7c92262719cfcdf19a commit 226 153 169
83baae61804e65cc73a7201a7252750c76066a30 blob   10 19 5362
9585191f37f7b0fb9444f35a9bf50de191beadc2 tag    136 127 5476
9bc1dc421dcd51b4ac296e3e5b6e2a99cf44391e blob   7 18 5193 1 \
  05408d195263d853f09dca71d55116663690c27c
ab1afef80fac8e34258ff41fc1b867c702daa24b commit 232 157 12
cac0cab538b970a37ea1e769cbbde608743bc96d commit 226 154 473
d8329fc1cc938780ffdd9f94e0d364e0ea74f579 tree   36 46 5316
e3f094f522629ae358806b17daf78246c27c007b blob   1486 734 4352
f8f51d7d8a1760462eca26eebafde32087499533 tree   106 107 749
fa49b077972391ad58037050f2a75f74e3671e92 blob   9 18 856
fdf4fc3344e67ab068f836878b6c4951e3b15f3d commit 177 122 627
chain length = 1: 1 object
pack-7a16e4488ae40c7d2bc56ea2bd43e25212a66c45.pack: ok
</pre>

<p>
  Du erinnerst dich, dass der Blob <code>9bc1d</code> die erste Version der <code>repo.rb</code>-Datei ist. Dieser Blob referenziert jetzt den&emsp14;&emsp14;Blob<code>05408</code>, der die zweite Version der Datei ist. Die dritte Spalte der Ausgabe ist die
  Größe des Objektes im Packfile. Wir können also sehen, dass <code>05408</code> 12K in Anspruch nimmt, <code>9bc1d</code> aber nur 7 Bytes. Das bedeutet also, dass die zweite Version diejenige ist,
  die vollständig, während die ursprüngliche, erste Version als Delta
  gespeichert wird! Der Grund dafür ist, dass Du höchstwahrscheinlich einen
  schnelleren Zugriff auf die jeweils neuesten Dateien brauchst.
</p>

<p>
  Außerdem ist toll, dass ein Repository jederzeit neu gepackt werden kann. Git
  macht das gelegentlich automatisch, um weniger Platz für die Datenbank zu
  verbrauchen. Du kannst sie aber auch jederzeit manuell mit <code>git gc</code> packen.
</p>

<h2>Die Refspec</h2>

<p>
  In diesem Buch haben wir bisher einfache Zuweisungen von externen Branches
  auf lokale Referenzen verwendet. Sie können aber auch durchaus komplex sein.
  Nehmen wir an, Du hast ein Remote-Repository wie folgt definiert:
</p>

<pre>
$ git remote add origin git@github.com:schacon/simplegit-progit.git
</pre>

<p>
  Das fügt eine Sektion in Deine <code>.git/config</code>-Datei hinzu, die Deinen lokalen Namen des externen Repositorys (<code>origin</code>), dessen URL und die Refspec spezifiziert, mit der neue Daten
  heruntergeladen werden.
</p>

<pre>
[remote "origin"]
       url = git@github.com:schacon/simplegit-progit.git
       fetch = +refs/heads/*:refs/remotes/origin/*
</pre>

<p>
  Das Format der Refspec besteht aus einem optionalen <code>+</code> gefolgt von <code>&lt;Quelle>:&lt;Ziel></code>, wobei <code>&lt;Quelle></code> ein Muster für Referenzen auf der Remote-Seite ist, und <code>&lt;Ziel></code> angibt, wohin diese Referenzen lokal geschrieben werden. Das <code>+</code> weist Git an, die Referenz zu mergen, wenn sie nicht mit einem Fast-forward
  aktualisiert werden kann.
</p>

<p>
  Der Standard, der von <code>git remote add</code> automatisch eingerichtet wird, besteht darin, dass Git automatisch alle
  Referenzen unter <code>refs/heads/</code> vom Server holt und sie lokal nach <code>refs/remotes/origin</code> speichert. D.h., wenn es auf dem Server einen Branch <code>master</code> gibt, kannst Du auf das Log dieses Branches wie folgt zugreifen:
</p>

<pre>
$ git log origin/master
$ git log remotes/origin/master
$ git log refs/remotes/origin/master
</pre>

<p>
  Diese Varianten sind allesamt äquivalent, weil Git sie jeweils zu <code>refs/remotes/origin/master</code> vervollständigt.
</p>

<p>
  Wenn Du stattdessen willst, dass Git jeweils nur den Branch <code>master</code> herunterlädt und andere Branches auf dem Server ignoriert, kannst Du die <code>fetch</code>-Zeile wie folgt ändern:
</p>

<pre>
fetch = +refs/heads/master:refs/remotes/origin/master
</pre>

<p>
  Dies ist allerdings lediglich der Standardwert der Refspec und Du kannst ihn
  auf der Kommandozeile jederzeit überschreiben. Um zum Beispiel nur den Branch
  <code>master</code> vom Server lokal als <code>origin/mymaster</code> zu speichern, kannst Du Folgendes ausführen:
</p>

<pre>
$ git fetch origin master:refs/remotes/origin/mymaster
</pre>

<p>
  Du kannst auch mehrere Refspecs gleichzeitig spezifizieren. Um mehrere
  Branches zu holen kannst du folgenden Befehl in die Kommandozeile eingeben:
</p>

<pre>
$ git fetch origin master:refs/remotes/origin/mymaster \
   topic:refs/remotes/origin/topic
From git@github.com:schacon/simplegit
 ! [rejected]        master     -&gt; origin/mymaster  (non fast forward)
 * [new branch]      topic      -&gt; origin/topic
</pre>

<p>
  In diesem Fall wurde ein Pull zurückgewiesen, weil der Branch nicht mit einem
  simplen Fast-forward aktualisiert werden konnte. Du kannst einen Merge
  erzwingen, indem Du der Refspec ein <code>+</code> voranstellst.
</p>

<p>
  Du kannst außerdem natürlich auch mehrere Refspecs in Deiner Konfiguration
  spezifizieren. Wenn Du z.B. immer die Branches <code>master</code> und <code>experiment</code> holen willst, fügst Du die folgenden Zeilen hinzu:
</p>

<pre>
[remote "origin"]
       url = git@github.com:schacon/simplegit-progit.git
       fetch = +refs/heads/master:refs/remotes/origin/master
       fetch = +refs/heads/experiment:refs/remotes/origin/experiment
</pre>

<p>
  Du kannst keine partiellen Glob-Muster verwenden, d.h. Folgendes wäre
  ungültig:
</p>

<pre>
fetch = +refs/heads/qa*:refs/remotes/origin/qa*
</pre>

<p>
  Allerdings kannst Du Namensräume verwenden, um etwas Ähnliches zu erreichen.
  Nehmen wir an, Du hast ein QA-Team, das regelmäßig verschiedene Branches
  pusht, und Du willst nun den Branch master und sämtliche Branches des
  QA-Teams, aber keine anderen Branches haben. Dann kannst Du eine
  Config-Sektion wie die folgende verwenden:
</p>

<pre>
[remote "origin"]
       url = git@github.com:schacon/simplegit-progit.git
       fetch = +refs/heads/master:refs/remotes/origin/master
       fetch = +refs/heads/qa/*:refs/remotes/origin/qa/*
</pre>

<p>
  In einem großen Team mit einem komplexen Workflow, in dem ein QA-Team,
  Entwickler und ein Integrations-Team jeweils eigene Branches pushen, kann man
  auf diese Weise Branches einfach in Namensräume einteilen.
</p>

<h3>Refspecs pushen</h3>

<p>
  Wie aber legt das QA-Team die Branches im <code>qa/</code> Namensraum ab? Das geht, indem man mit einer Refspec pusht.
</p>

<p>
  Wenn das QA-Team seinen Branch <code>master</code> in einem externen Repository als <code>qa/master</code> speichern will, kann es das wie folgt tun:
</p>

<pre>
$ git push origin master:refs/heads/qa/master
</pre>

<p>
  Um Git so zu konfigurieren, dass diese Refspec jedes Mal automatisch für <code>git push origin</code> verwendet wird, kann man den <code>push</code> Wert in der Config-Datei setzen:
</p>

<pre>
[remote "origin"]
       url = git@github.com:schacon/simplegit-progit.git
       fetch = +refs/heads/*:refs/remotes/origin/*
       push = refs/heads/master:refs/heads/qa/master
</pre>

<p>
  Auf diese Weise wird <code>git push origin</code> den lokalen Branch <code>master</code> als <code>qa/master</code> auf dem Server <code>origin</code> speichern.
</p>

<h3>Referenzen löschen</h3>

<p>
  Man kann Refspecs außerdem verwenden, um Referenzen aus einem externen
  Repository zu löschen:
</p>

<pre>
$ git push origin :topic
</pre>

<p>
  Das Refspec Format ist <code>&lt;Quelle>:&lt;Ziel></code>. Wenn man den Teil <code>&lt;Quelle></code> weglässt, dann heißt das im obigen Beispiel, dass man den Branch <code>topic</code> auf dem Server <code>origin</code> auf „nichts“ setzt, d.h. also löscht.
</p>

<h2>Transfer-Protokolle</h2>

<p>
  Git kann Daten zwischen zwei Repositorys im Wesentlichen auf zwei Arten
  transportieren: über HTTP und über sogenannte smarte Protokolle, die mit <code>file://</code>, <code>ssh://</code> und <code>git://</code> verwendet werden. Die folgende Sektion gibt einen kurzen Überblick über
  diese Protokolle und wie sie funktionieren.
</p>

<h3>Das dumme Protokoll</h3>

<p>
  Das HTTP-Transfer-Protokoll von Git wird oft auch als „dummes“ Protokoll
  bezeichnet, weil es auf der Server-Seite keinen Git-spezifischen Code
  benötigt. Der <code>fetch</code>-Prozess besteht aus einer Reihe von GET-Requests, für die der Client
  Vorannahmen über das Layout des Git-Repositorys auf dem Server machen kann.
  Schauen wir uns den <code>http-fetch</code>-Prozess der Bibliothek <code>simplegit</code> an:
</p>

<pre>
$ git clone http://github.com/schacon/simplegit-progit.git
</pre>

<p>
  Der Befehl lädt zunächst die Datei <code>info/refs</code> herunter. Diese Datei wird vom Befehl <code>update-server-info</code> geschrieben, den man als einen <code>post-receive</code>-Hook einrichten muss, damit das HTTP-Protokoll richtig funktionieren kann.
</p>

<pre>
=&gt; GET info/refs
ca82a6dff817ec66f44342007202690a93763949     refs/heads/master
</pre>

<p>
  Jetzt hat man eine Liste aller Referenzen und SHA-Prüfsummen in diesem
  Repository. Als nächstes schaut man die HEAD-Referenz nach, um zu wissen, was
  ausgecheckt werden muss:
</p>

<pre>
=&gt; GET HEAD
ref: refs/heads/master
</pre>

<p>
  D.h., wenn wir mit dem Prozess fertig sind, wir müssen den Branch <code>master</code> auschecken.
</p>

<p>
  Wir können jetzt loslegen. Weil in der Datei <code>info/refs</code> der Commit <code>ca82a6</code> angegeben ist, fangen wir damit an, dieses Objekt herunterzuladen:
</p>

<pre>
=&gt; GET objects/ca/82a6dff817ec66f44342007202690a93763949
(179 bytes of binary data)
</pre>

<p>
  Wir erhalten also ein Objekt zurück. Dieses Objekt ist im losen Format auf
  dem Server gespeichert, und wir haben es über einen statischen
  HTTP-GET-Request herunter geladen. Jetzt können wir es mit zlib
  dekomprimieren, den Header entfernen und den Inhalt des Commits durchsehen:
</p>

<pre>
$ git cat-file -p ca82a6dff817ec66f44342007202690a93763949
tree cfda3bf379e4f8dba8717dee55aab78aef7f4daf
parent 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
author Scott Chacon &lt;schacon@gmail.com&gt; 1205815931 -0700
committer Scott Chacon &lt;schacon@gmail.com&gt; 1240030591 -0700

changed the version number
</pre>

<p>
  Als Nächstes brauchen wir also zwei weitere Objekte: <code>cfda3b</code>, welches der Tree der Inhalte dieses Commits ist, und <code>085bb3</code>, welches der übergeordnete Commit ist:
</p>

<pre>
=&gt; GET objects/08/5bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
(179 bytes of data)
</pre>

<p>
  Das gibt uns das nächste Commit-Objekt. Versuchen wir, das Tree-Objekt zu
  holen:
</p>

<pre>
=&gt; GET objects/cf/da3bf379e4f8dba8717dee55aab78aef7f4daf
(404 - Not Found)
</pre>

<p>
  Huch. Es sieht so aus, als ob der Tree nicht im losen Format auf dem Server
  gespeichert ist, weshalb wir eine 404-Antwort („Not found“) erhalten. Dafür
  kann es verschiedene Gründe geben. Das Objekt könnte in einem anderen,
  alternativen Repository liegen, oder es könnte sich in einem Packfile
  befinden. Git sucht deshalb zunächst nach alternativen Repositories:
</p>

<pre>
=&gt; GET objects/info/http-alternates
(empty file)

=&gt; GET objects/info/http-alternates
(leere Datei)
</pre>

<p>
  Wenn wir hier eine Liste alternativer URLs erhalten, schaut Git dort nach
  losen Dateien und Packfiles. Auf diese Weise können Repositorys, die Forks
  von anderen Repositorys sind, mit diesen Objekte im Dateisystem teilen. In
  unserem Fall sind allerdings keine Alternativen vorhanden, weshalb sich das
  gesuchte Objekt in einem Packfile befinden muss. Um die vorhandenen Packfiles
  nachzuschlagen, holt Git die <code>objects/info/packs</code> Datei, die eine entsprechende Auflistung enthält (und ebenfalls mit <code>update-server-info</code> erzeugt wird)
</p>

<pre>
=&gt; GET objects/info/packs
P pack-816a9b2334da9953e530f27bcac22082a9f5b835.pack
</pre>

<p>
  Es gibt nur ein einziges Packfile auf dem Server, weshalb sich unser Objekt
  darin befinden muss. Aber wir prüfen die Index-Datei, um sicher zu sein. Gäbe
  es mehrere Packfiles auf dem Server, könnten wir auf diese Weise
  herausfinden, welches Packfile das gesuchte Objekt enthält:
</p>

<pre>
=&gt; GET objects/pack/pack-816a9b2334da9953e530f27bcac22082a9f5b835.idx
(4k of binary data)

=&gt; GET objects/pack/pack-816a9b2334da9953e530f27bcac22082a9f5b835.idx
(4k binäre Daten)
</pre>

<p>
  Nachdem wir jetzt den Packfile-Index haben, können wir prüfen, ob sich unser
  Objekt darin befindet: der Index enthält eine Liste der SHA-Hashes der
  Objekte, die sich im Packfile befinden und die jeweiligen Offsets dieser
  Objekte. Unser Objekt ist vorhanden, also laden wir das Packfile herunter:
</p>

<pre>
=&gt; GET objects/pack/pack-816a9b2334da9953e530f27bcac22082a9f5b835.pack
(13k of binary data)

=&gt; GET objects/pack/pack-816a9b2334da9953e530f27bcac22082a9f5b835.pack
(13k binäre Daten)
</pre>

<p>
  Du hast jetzt das Tree-Objekt, also kannst Du jetzt damit fortfahren, über
  die Commits zu iterieren. Sie sind in unserem Fall allesamt in dem Packfile
  enthalten, das Du gerade heruntergeladen hast.
</p>

<p>Die Ausgabe des ganzen Vorgangs sieht dann in etwa so aus:</p>

<pre>
$ git clone http://github.com/schacon/simplegit-progit.git
Initialized empty Git repository in /private/tmp/simplegit-progit/.git/
got ca82a6dff817ec66f44342007202690a93763949
walk ca82a6dff817ec66f44342007202690a93763949
got 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Getting alternates list for http://github.com/schacon/simplegit-progit.git
Getting pack list for http://github.com/schacon/simplegit-progit.git
Getting index for pack 816a9b2334da9953e530f27bcac22082a9f5b835
Getting pack 816a9b2334da9953e530f27bcac22082a9f5b835
 which contains cfda3bf379e4f8dba8717dee55aab78aef7f4daf
walk 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
walk a11bef06a3f659402fe7563abf99ad00de2209e6
</pre>

<h3>Das schlaue Protokoll</h3>

<p>
  Die HTTP-Methode ist simpel, aber sie ist auch ein bisschen ineffizient.
  Deshalb ist es üblicher, ein schlaues Protokoll für den Datentransfer zu
  verwenden. Diese Protokolle umfassen serverseitige Prozesse, die Wissen über
  Git besitzen. Sie können lokale Daten lesen und herausfinden, was auf dem
  Client schon vorhanden ist oder fehlt und darauf zugeschnittene Daten
  generieren. Es gibt zwei Sets von Prozessen für den Datentransfer: ein Paar
  für den Upload und ein Paar für den Download von Daten.
</p>

<p>Daten hochladen</p>

<p>
  Um Daten an einen serverseitigen Prozess zu schicken, verwendet Git die
  Prozesse <code>send-pack</code> und <code>receive-pack</code>. Der Prozess <code>send-pack</code> läuft auf dem Client und verbindet sich mit einem <code>receive-pack</code>-Prozess auf dem Server.
</p>

<p>
  Nehmen wir z.B. an, Du führst <code>git push origin master</code> in Deinem Projekt aus und <code>origin</code> ist als eine URL mit SSH-Protokoll definiert. Git startet dann einen <code>send-pack</code>-Prozess, der eine SSH-Verbindung zum Server initiiert. Dieser versucht, via
  SSH auf dem Server einen Befehl wie den folgenden auszuführen:
</p>

<pre>
$ ssh -x git@github.com "git-receive-pack 'schacon/simplegit-progit.git'"
005bca82a6dff817ec66f4437202690a93763949 refs/heads/master report-status delete-refs
003e085bb3bcb608e1e84b2432f8ecbe6306e7e7 refs/heads/topic
0000
</pre>

<p>
  Der <code>git-receive-pack</code>-Befehl antwortet dann mit jeweils einer Zeile pro Referenz, die er kennt –
  in diesem Fall sind das lediglich der Branch <code>master</code> und dessen SHA-Prüfsumme. Die erste Zeile listet außerdem Features, die der
  Server beherrscht (in unserem Fall <code>report-status</code> und <code>delete-refs</code>).
</p>

<p>
  Jede Zeile beginnt mit einem 4 Byte Hexadezimalzahl-Wert, der angibt, wie
  lang der Rest der Zeile ist. Die erste Zeile beginnt mit 005b, d.h. dezimal
  91. ALso ist der Rest der Zeile 91 Zeichen lang. Die nächste Zeile fängt mit
  003e an, also dezimal 62. Die letzte Zeile ist 0000, was das Ende der Liste
  anzeigt.
</p>

<p>
  Nachdem Dein <code>send-pack</code>-Prozess jetzt den Zustand des Servers kennt, kann er als nächstes
  evaluieren, welche Commits lokal, aber nicht auf dem Server vorhanden sind.
  der <code>send-pack</code>-Prozess schickt diese Information für jede Referenz, auf die sich der <code>push</code>-Befehl bezieht, an den <code>receive-pack</code> Prozess. Wenn Du beispielsweise den Branch <code>master</code> aktualisierst und einen Branch <code>experiment</code> hinzufügst, dann könnte die Antwort auf <code>send-pack</code> so aussehen:
</p>

<pre>
0085ca82a6dff817ec66f44342007202690a93763949  15027957951b64cf874c3557a0f3547bd83b3ff6 refs/heads/master report-status
00670000000000000000000000000000000000000000 cdfdb42577e2506715f8cfeacdbabc092bf63e8d refs/heads/experiment
0000
</pre>

<p>
  Der SHA-1-Wert, der nur aus Nullen besteht, heißt, dass dort zuvor nichts
  war: Du fügst die <code>experiment</code>-Referenz ja neu hinzu. Würdest Du eine Referenz löschen, würdest Du das
  Gegenteil sehen: nur Nullen auf der rechten Seite.
</p>

<p>
  Pro Referenz, die Du aktualisierst, schickt Git eine Zeile mit dem alten SHA,
  dem neuen SHA und der jeweiligen Referenz, die aktualisiert wird. Die erste
  Zeile listet zudem die Server-Features auf. Als nächstes lädt der Client ein
  Packfile aller Objekte hoch, die der Server noch nicht kennt. Abschließend
  antwortet der Server mit einer Erfolgs- oder Fehlermeldung:
</p>

<pre>
000Aunpack ok
</pre>

<p>Daten herunterladen</p>

<p>
  Wenn Du Daten herunterlädst, sind daran die Prozesse <code>fetch-pack</code> und <code>upload-pack</code> beteiligt. Der Client startet einen <code>fetch-pack</code>-Prozess, der sich mit einem <code>upload-pack</code>-Prozess auf dem Server verbindet, um auszuhandeln, welche Daten
  heruntergeladen werden sollen.
</p>

<p>
  Es gibt verschiedene Möglichkeiten, den <code>upload-pack</code>-Prozess auf dem Server zu starten: einerseits via SSH auf die gleiche Weise
  wie den <code>receive-pack</code>-Prozess. Und andererseits über den Git-Daemon, der standardmäßig auf dem
  Server auf dem Port 9418 läuft. Der <code>fetch-pack</code>-Prozess schickt etwa Folgendes an den Daemon:
</p>

<pre>
003fgit-upload-pack schacon/simplegit-progit.git\0host=myserver.com\0
</pre>

<p>
  Diese Zeile beginnt wiederum mit 4 Bytes, die angeben, wieviel Daten folgen.
  Dann kommt der auszuführende Befehl und ein Null-Byte, und schließlich der
  Hostname des Servers und ein weiteres Null-Byte. Der Git-Daemon prüft, ob der
  Befehl ausgeführt werden kann, das Repository existiert und Schreibzugriff
  erlaubt. Wenn alles stimmt, startet er den <code>upload-pack</code>-Prozess und gibt den Request dorthin weiter.
</p>

<p>
  Wenn Du den <code>fetch</code>-Befehl über SSH verwendest, führt <code>fetch-pack</code> stattdessen etwas aus wie:
</p>

<pre>
$ ssh -x git@github.com "git-upload-pack 'schacon/simplegit-progit.git'"
</pre>

<p>
  In beiden Fällen wird, nachdem <code>fetch-pack</code> verbunden ist, <code>upload-pack</code> eine Antwort wie die folgende zurückschicken:
</p>

<pre>
0088ca82a6dff817ec66f44342007202690a93763949 HEAD\0multi_ack thin-pack \
  side-band side-band-64k ofs-delta shallow no-progress include-tag
003fca82a6dff817ec66f44342007202690a93763949 refs/heads/master
003e085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 refs/heads/topic
0000
</pre>

<p>
  Die Antwort ähnelt der, mit der <code>receive-pack</code> antwortet, aber die aufgelisteten Features sind andere. Zusätzlich wird die
  HEAD-Referenz mitgeschickt, sodass der Client weiß, was er auschecken muss,
  falls es sich um einen Clone handelt.
</p>

<p>
  Der <code>fetch-pack</code>-Prozess inspiziert jetzt die vorhandenen Objekte und antwortet mit einer
  Liste von Objekten, wobei er das Schlüsselwort „want“ für Objekte verwendet,
  die benötigt werden, und „have“ für Objekte, die bereits vorhanden sind. Am
  Ende der Liste folgt das Schlüsselwort „done“. Der <code>upload-pack</code>-Prozess schickt dann ein Packfile mit allen benötigten Objekten:
</p>

<pre>
0054want ca82a6dff817ec66f44342007202690a93763949 ofs-delta
0032have 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
0000
0009done
</pre>

<p>
  Das ist ein sehr einfaches Beispiel. In komplexeren Fällen unterstützt der
  Client die Features <code>multi_ack</code> oder <code>side-band</code>. Aber obiges Beispiel verdeutlicht den grundlegenden Request-Response Zyklus
  der Prozesse bei schlauen Protokollen.
</p>

<h2>Wartung und Datenwiederherstellung</h2>

<p>
  Gelegentlich will man ein bisschen aufräumen – ein Repository komprimieren,
  ein importiertes Repository aufräumen oder verloren gegangene Daten
  wiederherstellen. Dieses Kapitel wird sich mit einigen derartigen Szenarien
  befassen.
</p>

<h3>Wartung</h3>

<p>
  Git führt den Befehl <code>auto gc</code> hin und wieder automatisch aus. In den meisten Fällen tut dieser Befehl
  nichts. Wenn allerdings zu viele lose Objekte (d.h. Objekte, die nicht in
  einem Packfile gepackt sind) oder zu viele einzelne Packfiles vorhanden sind,
  führt Git den Befehl <code>git gc</code> aus. <code>gc</code> steht für „Garbage Collection“. Dieser Befehl führt eine Reihe von Aufgaben
  durch: er sammelt die losen Objekte und packt sie in ein Packfile, er führt
  einzelne Packfiles zu einem einzigen, großen Packfile zusammen, und er
  entfernt Objekte, die mit keinem Commit erreichbar und einige Monate alt
  sind.
</p>

<p>Du kannst <code>auto gc</code> wie folgt manuell ausführen:</p>

<pre>
$ git gc --auto
</pre>

<p>
  Wie schon erwähnt tut dies normalerweise gar nichts. Es müssen sich etwa
  7.000 lose Objekte oder mehr als 50 Packfiles angesammelt haben, bevor Git
  tatsächlich den echten gc-Befehl startet. Du kannst diese Werte mit Hilfe der
  Konfigurationsvariablen <code>gc.auto</code> bzw. <code>gc.autopacklimit</code> manuell setzen.
</p>

<p>
  <code>gc</code> packt außerdem Referenzen in eine einzige Datei zusammen. Nehmen wir an,
  Dein Repository enthält die folgenden Branches und Tags:
</p>

<pre>
$ find .git/refs -type f
.git/refs/heads/experiment
.git/refs/heads/master
.git/refs/tags/v1.0
.git/refs/tags/v1.1
</pre>

<p>
  Nachdem Du <code>git gc</code> ausgeführt hast, werden diese Dateien um der Effizienz willen aus dem
  Verzeichnis <code>refs</code> entfernt und in eine Datei <code>.git/packed-refs</code> verschoben, die dann wie folgt aussieht:
</p>

<pre>
$ cat .git/packed-refs
# pack-refs with: peeled
cac0cab538b970a37ea1e769cbbde608743bc96d refs/heads/experiment
ab1afef80fac8e34258ff41fc1b867c702daa24b refs/heads/master
cac0cab538b970a37ea1e769cbbde608743bc96d refs/tags/v1.0
9585191f37f7b0fb9444f35a9bf50de191beadc2 refs/tags/v1.1
^1a410efbd13591db07496601ebc7a059dd55cfe9
</pre>

<p>
  Wenn Du eine Referenz bearbeitest, lässt Git diese Datei unverändert und
  schreibt stattdessen eine neue Datei nach <code>refs/heads</code>. Um eine SHA-Prüfsumme für eine Referenz nachzuschlagen, schaut Git zunächst
  im Verzeichnis <code>refs</code> und danach erst in der Datei <code>packed-refs</code> nach, falls nötig. Wenn eine Referenz also nicht im Verzeichnis <code>refs</code> liegt, befindet sie sich wahrscheinlich in der Datei <code>packed-refs</code>.
</p>

<p>
  Beachte, dass die letzte Zeile der Datei mit <code>^</code> anfängt. Das bedeutet, dass der Tag darüber ein annotierter Tag ist und
  diese Zeile zeigt den Commit, auf den der annotierte Tag zeigt.
</p>

<h3>Daten-Wiederherstellung</h3>

<p>
  Irgendwann wird es vielleicht mal vorkommen, dass Du während der Arbeit mit
  Git einen Commit verlierst. Normalerweise passiert das, wenn Du versehentlich
  einen Branch löschst, an dem Du gearbeitet hattest und den Du noch brauchst.
  Oder Du führst <code>git reset --hard</code> aus und stellst fest, dass Du einige der Commits noch brauchst. Nehmen wir
  an, Du steckst in einer solchen Situation – wie kannst Du Deine Commits dann
  wiederherstellen?
</p>

<p>
  Das folgende Beispiel setzt zuerst den Branch <code>master</code> auf einen älteren Commit zurück und stellt die verlorenen Commits dann
  wieder her. Zunächst schauen wir uns den gegenwärtigen Zustand des
  Repositorys an:
</p>

<pre>
$ git log --pretty=oneline
ab1afef80fac8e34258ff41fc1b867c702daa24b modified repo a bit
484a59275031909e19aadb7c92262719cfcdf19a added repo.rb
1a410efbd13591db07496601ebc7a059dd55cfe9 third commit
cac0cab538b970a37ea1e769cbbde608743bc96d second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit
</pre>

<p>
  Jetzt setzen wir den Branch <code>master</code> auf den mittleren Commit zurück:
</p>

<pre>
$ git reset --hard 1a410efbd13591db07496601ebc7a059dd55cfe9
HEAD is now at 1a410ef third commit
$ git log --pretty=oneline
1a410efbd13591db07496601ebc7a059dd55cfe9 third commit
cac0cab538b970a37ea1e769cbbde608743bc96d second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit
</pre>

<p>
  Du hast damit die oberen beiden Commits verloren, d.h. es gibt keinen Branch
  mehr, von dem aus diese Commits erreichbar wären. Um sie wiederherzustellen,
  kannst Du einen neuen Branch anlegen, der auf den SHA-Hash des obersten
  (letzten) Commits zeigt. Der Trick besteht darin, diesen letzten Commit-Hash
  herauszufinden. Es ist ja nicht so, dass Du Dir jederzeit all die Hashes
  merken könntest, oder?
</p>

<p>
  In der Regel ist der schnellste Weg, solche Hashes zu finden, der Befehl <code>git reflog</code>. Während Du mit Git arbeitest, macht Git im Stillen fortlaufende Notizen
  darüber, was HEAD ist. Jedes Mal, wenn Du einen Commit anlegst oder den
  Branch wechselst, wird das „Reflog“ aktualisiert. Das Reflog wird außerdem
  vom Befehl <code>git update-ref</code> verwendet – ein weiterer guter Grund, nicht stattdessen einfach den SHA-Wert
  in eine Referenz-Datei zu schreiben (wie wir das in der Sektion
  „Git-Referenzen“ zuvor in diesem Kapitel besprochen haben). Du kannst also
  jederzeit nachschlagen, woran Du jeweils gearbeitet hast, indem Du den Befehl
  <code>git reflog</code> verwendest:
</p>

<pre>
$ git reflog
1a410ef HEAD@{0}: 1a410efbd13591db07496601ebc7a059dd55cfe9: updating HEAD
ab1afef HEAD@{1}: ab1afef80fac8e34258ff41fc1b867c702daa24b: updating HEAD
</pre>

<p>
  Das zeigt also die beiden verloren gegangenen Commits an, die wir zuvor
  ausgecheckt hatten. Allerdings zeigt es auch nicht viel mehr Information. Um
  das Reflog in einer anderen, etwas nützlicheren Weise anzuzeigen, kannst Du <code>git log -g</code> verwenden. Das gibt das Reflog im gewohnten Log-Format aus:
</p>

<pre>
$ git log -g
commit 1a410efbd13591db07496601ebc7a059dd55cfe9
Reflog: HEAD@{0} (Scott Chacon &lt;schacon@gmail.com&gt;)
Reflog message: updating HEAD
Author: Scott Chacon &lt;schacon@gmail.com&gt;
Date:   Fri May 22 18:22:37 2009 -0700

    third commit

commit ab1afef80fac8e34258ff41fc1b867c702daa24b
Reflog: HEAD@{1} (Scott Chacon &lt;schacon@gmail.com&gt;)
Reflog message: updating HEAD
Author: Scott Chacon &lt;schacon@gmail.com&gt;
Date:   Fri May 22 18:15:24 2009 -0700

     modified repo a bit
</pre>

<p>
  Es sieht also so aus, als sei der untere Commit derjenige, den Du verloren
  hast, aber noch brauchst. Du kannst ihn jetzt wiederherstellen, indem Du
  einen neuen Branch erstellst, der auf diesen Commit zeigt. Beispielsweise
  kannst Du einen Branch <code>recover-branch</code> anlegen, der auf den Commit <code>ab1afef</code> zeigt:
</p>

<pre>
$ git branch recover-branch ab1afef
$ git log --pretty=oneline recover-branch
ab1afef80fac8e34258ff41fc1b867c702daa24b modified repo a bit
484a59275031909e19aadb7c92262719cfcdf19a added repo.rb
1a410efbd13591db07496601ebc7a059dd55cfe9 third commit
cac0cab538b970a37ea1e769cbbde608743bc96d second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit
</pre>

<p>
  Sehr gut. Du hast jetzt einen neuen Branch <code>recover-branch</code>, der diejenigen Commits enthält, die sich zuvor in Deinem Branch <code>master</code> befanden. Damit hast Du wieder Zugriff auf die beiden verloren gegangenen
  Commits. Als nächstes nehmen wir aber außerdem an, dass diese verlorenen
  Commits aus irgendeinem Grunde nicht im Reflog enthalten sind – Du kannst das
  z.B. simulieren, indem Du den Branch <code>recover-branch</code> und das Reflog löschst. Damit sind die beiden Commits jetzt von nirgendwo
  her mehr erreichbar:
</p>

<pre>
$ git branch -D recover-branch
$ rm -Rf .git/logs/
</pre>

<p>
  Das Reflog wird im Verzeichnis <code>.git/logs/</code> aufbewahrt, d.h. Du hast nun faktisch kein Reflog mehr. Wie kann man einen
  Commit jetzt noch wiederherstellen? Eine Möglichkeit dazu ist der Befehl <code>git fsck</code>, der die Integrität der Git-Datenbank prüft. Wenn Du den Befehl mit der
  Option <code>--full</code> ausführst, zeigt er alle Objekte an, auf die nicht von einem anderen Objekt
  verwiesen wird:
</p>

<pre>
$ git fsck --full
dangling blob d670460b4b4aece5915caf5c68d12f560a9fe3e4
dangling commit ab1afef80fac8e34258ff41fc1b867c702daa24b
dangling tree aea790b9a58f6cf6f2804eeac9f0abbe9631e4c9
dangling blob 7108f7ecb345ee9d0084193f147cdad4d2998293
</pre>

<p>
  In diesem Fall findest Du den verlorenen Commit nach den Worten „dangling
  commit“. Du kannst ihn dann auf dieselbe Weise wiederherstellen wie zuvor,
  indem Du einen Branch erstellst, der auf diesen Commit-Hash zeigt.
</p>

<h3>Objekte entfernen</h3>

<p>
  Git ist in vielerlei Hinsicht unschlagbar, aber es gibt auch Features, die
  Probleme verursachen können. Ein solches Problem kann darin bestehen, dass <code>git clone</code> die vollständige Historie eines Projektes herunterlädt, d.h. jede einzelne
  Version jeder einzelnen Datei. Das ist eine feine Sache, solange es sich um
  Quellcode handelt, denn Git ist darauf optimiert, diese Art von Daten
  effizient zu komprimieren. Wenn allerdings irgendwann einmal eine einzelne,
  sehr große Datei zur Versionskontrolle hinzugefügt wurde, wird jeder Clone
  dieses Repositorys diese Datei gezwungenermaßen herunterladen müssen – auch
  dann, wenn die Datei inzwischen aus dem Repository entfernt würde. Weil sie
  über die Historie erreichbar ist, muss die Datei vorhanden sein.
</p>

<p>
  Hierin kann ein großes Problem bestehen, wenn Du Subversion- oder
  Perforce-Repositorys nach Git konvertierst. Weil Du in diesen Systemen nicht
  die gesamte Historie herunterlädst, kann diese Art von Hinzufügung einige
  unangenehme Konsequenzen haben. Wenn Du Dein Repository aus einem anderen
  System importiert hast oder aus irgendeinem anderen Grunde findest, dass es
  sehr viel größer ist, als es eigentlich sein sollte, kannst Du große Objekte
  wie folgt suchen und entfernen.
</p>

<p>
  Sei Dir allerdings bewusst, dass diese Technik die Commit-Historie zerstört.
  Sie schreibt angefangen beim ursprünglichen Tree jedes einzelne Commit-Objekt
  neu, um die jeweilige, große Datei zu entfernen. Wenn Du das direkt nach
  einem Import tust, d.h. bevor jemand angefangen hat, auf der Basis eines
  Commits zu arbeiten, ist das in Ordnung – andernfalls müssen alle Mitarbeiter
  ihre Arbeit auf Deinen Commit rebasen.
</p>

<p>
  Um das zu demonstrieren, werden wir eine große Datei zu Deinem
  Test-Repository hinzufügen, sie dann im nächsten Commit löschen, in der
  Datenbank nachschlagen und sie schließlich dauerhaft aus dem Repository
  entfernen. Als erstes checke also eine große Datei in Dein Repository ein:
</p>

<pre>
$ curl http://kernel.org/pub/software/scm/git/git-1.6.3.1.tar.bz2 &gt; git.tbz2
$ git add git.tbz2
$ git commit -am 'added git tarball'
[master 6df7640] added git tarball
 1 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 git.tbz2
</pre>

<p>
  Oha. Du wolltest keinen dermaßen großen Tarball in Deinem Projekt. Am besten
  löschen wir es gleich wieder:
</p>

<pre>
$ git rm git.tbz2
rm 'git.tbz2'
$ git commit -m 'oops - removed large tarball'
[master da3f30d] oops - removed large tarball
 1 files changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 git.tbz2
</pre>

<p>
  Jetzt lassen wir die Garbage-Collection über die Datenbank laufen und sehen,
  wieviel Platz sie braucht:
</p>

<pre>
$ git gc
Counting objects: 21, done.
Delta compression using 2 threads.
Compressing objects: 100% (16/16), done.
Writing objects: 100% (21/21), done.
Total 21 (delta 3), reused 15 (delta 1)
</pre>

<p>
  Du kannst auch den Befehl <code>count-objects</code> laufen lassen, um einen schnellen Überblick darüber zu erhalten, wieviel
  Platz das Repository einnimmt:
</p>

<pre>
$ git count-objects -v
count: 4
size: 16
in-pack: 21
packs: 1
size-pack: 2016
prune-packable: 0
garbage: 0
</pre>

<p>
  Der <code>size-pack</code>-Eintrag zeigt die Größe der Packfiles in Kilobytes an, d.h. Dein Repository
  braucht 2 MB. Vor dem letzten Commit lag dieser Wert eher bei 2 KB. D.h., die
  Datei wurde im letzten Commit eindeutig nicht aus der History entfernt. Jedes
  Mal, wenn jemand künftig dieses sehr kleine Repository klont, wird er die 2
  MB mit herunterladen müssen – nur weil wir versehentlich diese große Datei
  hinzugefügt hatten. Also versuchen wir, sie endgültig loszuwerden.
</p>

<p>
  Zunächst mal müssen wir sie finden. In diesem Fall wissen wir bereits, um
  welche Datei es sich handelt. Aber nehmen wir an, wir wüssten es nicht. Wie
  würdest Du herausfinden, welche Datei (oder welche Dateien) so viel Platz
  verbrauchen? Wenn Du <code>git gc</code> laufen gelassen hast, werden sich alle Objekte in einem Packfile befinden.
  Du kannst dann große Objekte identifizieren, indem Du einen weiteren
  Plumbing-Befehl, nämlich <code>git verify-pack</code> ausführst und nach dem dritten Feld der Ausgabe sortierst, d.h. der
  Dateigröße. Du kannst sie außerdem durch den <code>tail</code> Befehl leiten, denn Du bist ja nur an den wenigen größten Objekten
  interessiert:
</p>

<pre>
$ git verify-pack -v .git/objects/pack/pack-3f8c0...bb.idx | sort -k 3 -n | tail -3
e3f094f522629ae358806b17daf78246c27c007b blob   1486 734 4667
05408d195263d853f09dca71d55116663690c27c blob   12908 3478 1189
7a9eb2fba2b1811321254ac360970fc169ba2330 blob   2056716 2056872 5401
</pre>

<p>
  Das größte Objekt ist ganz klar das letzte: es ist 2 MB groß. Um
  herauszufinden, welche Datei das ist, kannst Du den <code>rev-list</code> Befehl verwenden, den wir in Kapitel 7 schon einmal kurz verwendet haben.
  Wenn Du die Optionen <code>--objects</code> und <code>rev-list</code> verwendest, werden alle Commit- und Blob-SHAs mit den jeweiligen Dateipfaden
  aufgelistet, die mit ihnen assoziiert sind. Auf diese Weise kannst Du den
  Namen des Blobs finden:
</p>

<pre>
$ git rev-list --objects --all | grep 7a9eb2fb
7a9eb2fba2b1811321254ac360970fc169ba2330 git.tbz2
</pre>

<p>
  Du musst diese Datei jetzt aus allen Trees entfernen, in denen sie sich
  befindet. Du kannst leicht herausfinden, welche Commits diese Datei verändert
  haben:
</p>

<pre>
$ git log --pretty=oneline --branches -- git.tbz2
da3f30d019005479c99eb4c3406225613985a1db oops - removed large tarball
6df764092f3e7c8f5f94cbe08ee5cf42e92a0289 added git tarball
</pre>

<p>
  Es geht also darum, alle Commits angefangen bei <code>6df76</code> neu zu schreiben, sodass der Tarball nicht mehr in Deiner Git-Historie
  enthalten ist. Um das zu erreichen, verwendest Du den Befehl <code>git filter-branch</code>, den wir schon mal in Kapitel 6 verwendet haben:
</p>

<pre>
$ git filter-branch --index-filter \
   'git rm --cached --ignore-unmatch git.tbz2' -- 6df7640^..
Rewrite 6df764092f3e7c8f5f94cbe08ee5cf42e92a0289 (1/2)rm 'git.tbz2'
Rewrite da3f30d019005479c99eb4c3406225613985a1db (2/2)
Ref 'refs/heads/master' was rewritten
</pre>

<p>
  Die Option&emsp14;&emsp14;<code>--index-filter</code> ist ähnlich der Option <code>--tree-filter</code> aus Kapitel 6. Allerdings übergibt man in diesem Fall nicht einen Befehl,
  der die Dateien verändert, die sich jeweils ausgecheckt auf der Festplatte
  befinden. Stattdessen verändert man jeweils die Staging-Area bzw. den Index.
  Statt eine bestimmte Datei mit z.B: <code>rm file</code> zu entfernen, müssen wir also <code>git rm --cached</code> verwenden – denn wir wollen sie aus dem Index, nicht von der Festplatte
  löschen. Der Grund dafür ist einfach Geschwindigkeit: Git braucht nicht jede
  einzelne Revision auf die Festplatte auszuchecken, um den Filter anzuwenden.
  Auf diese Weise läuft der ganze Prozess sehr viel schneller. Man kann
  dieselbe Aufgabe aber auch mit <code>--tree-filter</code> erledigen, wenn man will. Die Option <code>--ignore-match</code> weist Git an, nicht mit einer Fehlermeldung abzubrechen, wenn die Datei, die
  wir löschen wollen, nicht vorhanden ist. Außerdem teilen wir <code>filter-branch</code> mit, die Historie nur von dem Commit <code>6df7640</code> an umzuschreiben. Andernfalls würde der Befehl von ganz vorn beginnen und
  unnötig länger brauchen.
</p>

<p>
  Deine Historie enthält jetzt nicht länger eine Referenz auf diese Datei. Dein
  Reflog und ein neues Set an Referenzen, die Git unter <code>.git/refs/original</code> angelegt hat, als Du <code>filter-branch</code> ausgeführt hast, tun das allerdings immer noch – also entfernen wir sie von
  dort und packen das Repository erneut. Bevor Du packst, musst Du zuerst alles
  entfernen, was noch Referenzen auf das alte Objekt enthält:
</p>

<pre>
$ rm -Rf .git/refs/original
$ rm -Rf .git/logs/
$ git gc
Counting objects: 19, done.
Delta compression using 2 threads.
Compressing objects: 100% (14/14), done.
Writing objects: 100% (19/19), done.
Total 19 (delta 3), reused 16 (delta 1)
</pre>

<p>Prüfen wir also, wieviel Platz wir damit eingespart haben:</p>

<pre>
$ git count-objects -v
count: 8
size: 2040
in-pack: 19
packs: 1
size-pack: 7
prune-packable: 0
garbage: 0
</pre>

<p>
  Das gepackte Repository umfasst jetzt nur noch 7K – sehr viel besser als die
  vorherigen 2MB. Du kannst an dem Wert <code>size</code> erkennen, dass sich die große Datei selbst jetzt immer noch in Deinen losen
  Objekten befindet. Aber sie wird bei einem <code>git push</code> oder anschließenden <code>git clone</code> nicht übermittelt werden – und das war unser Ziel. Wenn Du das wirklich
  willst, kannst Du sie jetzt vollständig und endgültig mit <code>git prune --expire</code> aus Deinem Repository löschen.
</p>

<h2>Zusammenfassung</h2>

<p>
  Du solltest jetzt ein gutes Verständnis davon haben, was Git im Hintergrund
  tut, und in einem gewissen Maße auch davon, wie es implementiert ist. In
  diesem Kapitel haben wir eine Reihe von Plumbing-Befehlen besprochen, also
  Befehlen, die grundlegender und einfacher als die Porcelain-Befehle sind, um
  die es im restlichen Buch ging. Dieses Verständnis sollte Dir helfen, zu
  verstehen, warum Git tut, was es tut – und natürlich auch dabei, Deine
  eigenen Werkzeuge und Hilfsskripte zu schreiben, um einen bestimmten
  Arbeitsablauf für Dich anzupassen.
</p>

<p>
  Git als ein Dateisystem, das Inhalte addressieren kann, ist ein äußerst
  mächtiges Werkzeug, das Du leicht für mehr als „nur“ als VCS einsetzen
  kannst. Ich hoffe, Du kannst Dein neugewonnenes Wissen der Git Interna
  nutzen, um Deine eigene tolle Anwendung dieser Technologie zu implementieren
  und Dich wohler damit zu fühlen, Git auch in fortgeschrittener Weise zu
  benutzen.
</p>
