Git
Chapters ▾ 2nd Edition

8.2 Git einrichten - Git-Attribute

Git-Attribute

Einige dieser Einstellungen können auch für einen Pfad angegeben werden, so dass Git diese Einstellungen nur für ein Unterverzeichnis oder eine Teilmenge von Dateien anwendet. Diese pfadspezifischen Einstellungen heißen Git-Attribute und werden entweder in einer .gitattributes Datei in einem Ihrer Verzeichnisse (normalerweise dem Stammverzeichnis Ihres Projekts) oder in der .git/info/attributes Datei festgelegt, falls Sie nicht wollen, dass die Attributdatei mit Ihrem Projekt verknüpft wird.

Mit Hilfe von Attributen können Sie beispielsweise separate Merge-Strategien für einzelne Dateien oder Verzeichnisse eines Projekts festlegen, Git sagen, wie man Nicht-Text-Dateien unterscheidet oder den Inhalt von Git filtern lassen, bevor Sie ihn in Git ein- oder auschecken. In diesem Abschnitt erfahren Sie mehr über einige der Attribute, die Sie in Ihrem Git-Projekt auf Ihre Pfade setzen können, und sehen einige Beispiele für die praktische Anwendung dieser Funktion.

Binäre Dateien

Ein toller Kniff, für den Sie Git-Attribute verwenden können, ist das Erkennen von binären Dateien (falls es sonst nicht möglich ist, es herauszufinden) und Git speziell anzuweisen, wie man mit diesen Dateien umgeht. So können beispielsweise einige Textdateien maschinell erzeugt und nicht mehr unterschieden werden, während andere Binärdateien unterschieden werden können. Sie werden sehen, wie Sie Git mitteilen können, welche die richtige ist.

Binärdateien identifizieren

Einige Dateien sehen aus wie Textdateien, sind aber im Grunde genommen wie Binärdateien zu behandeln. Xcode-Projekte unter macOS enthalten beispielsweise eine Datei, die mit .pbxproj endet, was im Grunde genommen ein JSON-Datensatz (Klartext JavaScript Datenformat) ist, der von der IDE auf die Festplatte geschrieben wird, Ihre Build-Einstellungen aufzeichnet usw. Obwohl es sich technisch gesehen um eine Textdatei handelt (weil sie eigentlich komplett UTF-8 ist), wollen Sie sie nicht als solche behandeln, denn es handelt sich hierbei um eine sehr einfache Datenbank – Sie können den Inhalt nicht mergen, wenn zwei Personen ihn ändern sind Unterschiede im Allgemeinen nicht hilfreich. Die Datei ist für den internen Betrieb auf einem Rechner bestimmt. Im Prinzip sollten Sie sie wie eine Binärdatei behandeln.

Um Git anzuweisen, alle pbxprojj Dateien als Binärdaten zu behandeln, fügen Sie die folgende Zeile zu Ihrer .gitattributes Datei hinzu:

*.pbxproj binary

Jetzt wird Git nicht versuchen, CRLF-Probleme zu konvertieren oder zu beheben; noch wird es versuchen, ein Diff für Änderungen in dieser Datei zu berechnen oder auszugeben, wenn Sie git show oder git diff bei Ihrem Projekt ausführen.

Unterschiede in Binärdateien vergleichen

Sie können auch die Git-Attribut-Funktionalität verwenden, um Binärdateien effektiv zu unterscheiden. Dafür müssen Sie Git mitteilen, wie es Ihre Binärdateien in ein Textformat konvertieren soll, das über das normale diff verglichen werden kann.

Zuerst werden Sie diese Technik nutzen, um eines der lästigsten Probleme zu lösen, die es gibt: die Versionskontrolle von Microsoft Word-Dokumenten. Jeder weiß, dass Word der grauenhafteste Editor überhaupt ist und dennoch wird er von allen benutzt. Wenn Sie Word-Dokumente versionieren wollen, können Sie sie in ein Git-Repository legen und ab und zu committen; aber was nützt das? Wenn Sie git diff wie gewohnt starten, sehen Sie nur so etwas wie das hier:

$ git diff
diff --git a/chapter1.docx b/chapter1.docx
index 88839c4..4afcb7c 100644
Binary files a/chapter1.docx and b/chapter1.docx differ

Sie können zwei Versionen nicht direkt vergleichen, es sei denn, Sie checken sie aus und scannen sie manuell, richtig? Wie sich herausstellt, kann man das ziemlich gut mit Git-Attributen machen. Fügen Sie die folgende Zeile in Ihre .gitattributes Datei ein:

*.docx diff=word

Hiermit erfährt Git, dass jede Datei, die mit diesem Suchmuster übereinstimmt (.docx), den Filter „word“ verwenden sollte, wenn Sie einen Diff anzeigen lassen wollen, der Änderungen enthält. Was ist der Filter „word“? Sie müssen ihn einrichten. Nachfolgend konfigurieren Sie Git so, dass es das Programm docx2txt verwendet, um Word-Dokumente in lesbare Textdateien zu konvertieren, die es dann richtig auswertet.

Zuerst müssen Sie docx2txt installieren. Sie können es von https://sourceforge.net/projects/docx2txt herunterladen. Folgen Sie den Anweisungen in der INSTALL Datei, um es an einen Ort zu platzieren, an dem Ihre Shell es finden kann. Als nächstes schreiben Sie ein Wrapper-Skript, um die Ausgabe in das von Git erwartete Format zu konvertieren. Erstellen Sie eine Datei namens docx2txt, irgendwo innerhalb Ihres Pfads und fügen Sie diesen Inhalt hinzu:

#!/bin/bash
docx2txt.pl "$1" -

Vergessen Sie nicht, die Dateirechte mit chmod a+x zu ändern. Schließlich können Sie Git so einrichten, dass es dieses Skript verwendet:

$ git config diff.word.textconv docx2txt

Jetzt ist Git darüber informiert, dass es bei dem Versuch, einen Diff zwischen zwei Snapshots zu machen, der Dateien enthält, die auf .docx enden, diese Dateien durch den „word“ Filter laufen lassen soll, der als docx2txt Programm definiert ist. Auf diese Weise entstehen im Vorfeld gute Textversionen Ihrer Word-Dateien, die Sie leichter unterscheiden können.

Hier ist ein Beispiel: Kapitel 1 dieses Buches wurde in das Word-Format konvertiert und in ein Git-Repository committet. Dann wurde ein neuer Absatz hinzugefügt. Hier ist das, was git diff zeigt:

$ git diff
diff --git a/chapter1.docx b/chapter1.docx
index 0b013ca..ba25db5 100644
--- a/chapter1.docx
+++ b/chapter1.docx
@@ -2,6 +2,7 @@
 This chapter will be about getting started with Git. We will begin at the beginning by explaining some background on version control tools, then move on to how to get Git running on your system and finally how to get it setup to start working with. At the end of this chapter you should understand why Git is around, why you should use it and you should be all setup to do so.
 1.1. About Version Control
 What is "version control", and why should you care? Version control is a system that records changes to a file or set of files over time so that you can recall specific versions later. For the examples in this book you will use software source code as the files being version controlled, though in reality you can do this with nearly any type of file on a computer.
+Testing: 1, 2, 3.
 If you are a graphic or web designer and want to keep every version of an image or layout (which you would most certainly want to), a Version Control System (VCS) is a very wise thing to use. It allows you to revert files back to a previous state, revert the entire project back to a previous state, compare changes over time, see who last modified something that might be causing a problem, who introduced an issue and when, and more. Using a VCS also generally means that if you screw things up or lose files, you can easily recover. In addition, you get all this for very little overhead.
 1.1.1. Local Version Control Systems
 Many people's version-control method of choice is to copy files into another directory (perhaps a time-stamped directory, if they're clever). This approach is very common because it is so simple, but it is also incredibly error prone. It is easy to forget which directory you're in and accidentally write to the wrong file or copy over files you don't mean to.

Git sagt uns sehr treffend und klar, dass wir die Zeichenkette „Testing: 1, 2, 3.“ hinzugefügt haben, was richtig ist. Was aber nicht perfekt ist: Formatierungsänderungen würden hier nicht angezeigt – aber es offensichtlich, dass es funktioniert.

Ein weiteres spannendes Problem, das Sie auf diese Weise lösen können, ist die Unterscheidung von Bilddateien. Die eine Methode besteht darin, Bilddateien durch einen Filter zu leiten, der ihre EXIF-Informationen extrahiert – Metadaten, die mit den meisten Bildformaten aufgezeichnet werden. Nach dem Herunterladen und Installieren des exiftool Programms können Sie damit die Metadaten Ihrer Bilder in Text umwandeln. So zeigt Ihnen das Diff zumindest eine schriftliche Darstellung der vorgenommenen Änderungen an. Fügen Sie die folgende Zeile in Ihre .gitattributes Datei ein:

*.png diff=exif

So konfigurieren Sie Git, um dieses Tool zu verwenden:

$ git config diff.exif.textconv exiftool

Wenn Sie ein Bild in Ihrem Projekt ersetzen und dann git diff ausführen, sehen Sie etwas ähnliches wie hier:

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:21 07:02:45-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

Sie können leicht erkennen, dass sich sowohl die Dateigröße als auch die Bildgröße geändert haben.

Schlüsselwort-Erweiterung

Die SVN- oder CVS-artige Schlüsselwort-Erweiterung wird oft von Entwicklern gefordert, die mit diesen Systemen arbeiten. Das Hauptproblem in Git ist, dass Sie eine Datei mit Informationen über einen Commit nach dem Committen nicht ändern können, da zuerst die Prüfsumme der Datei von Git ermittelt wird. Wenn die Datei ausgecheckt ist können Sie jedoch Text in sie einfügen und ihn wieder entfernen, bevor er zu einem Commit hinzugefügt wird. Die Git-Attribute stellen Ihnen dafür zwei Möglichkeiten zur Verfügung.

Erstens können Sie die SHA-1-Prüfsumme eines Blobs automatisch in ein $Id$ Feld in der Datei einfügen. Wird dieses Attribut auf eine Datei oder eine Gruppe von Dateien gesetzt, dann wird Git beim nächsten Auschecken dieses Branchs das entsprechende Feld durch den SHA-1 des Blobs ersetzen. Dabei muss man beachten, dass es nicht das SHA-1 des Commits ist, sondern das des Blobs an sich. Fügen Sie die folgende Zeile in Ihre .gitattributes Datei ein:

*.txt ident

Hinzufügen einer $Id$ Referenz zu einer Testdatei:

$ echo '$Id$' > test.txt

Beim nächsten Auschecken dieser Datei fügt Git den SHA-1 des Blobs ein:

$ rm test.txt
$ git checkout -- test.txt
$ cat test.txt
$Id: 42812b7653c7b88933f8a9d6cad0ca16714b9bb3 $

Dieses Ergebnis ist jedoch von begrenztem Nutzen. Wenn Sie die Ersetzung von Schlüsselwörtern in CVS oder Subversion verwendet haben, können Sie einen Datums-Stempel hinzufügen – der SHA-1 ist nicht allzu hilfreich, weil er eher willkürlich ist und Sie nicht erkennen können, ob ein SHA-1 älter oder neuer als ein anderer ist, den Sie sich ansehen.

Es zeigt sich, dass Sie Ihre eigenen Filter schreiben können, um Ersetzungen in Dateien auf einem Commit/Checkout durchzuführen. Diese werden als „saubere“ (engl. clean) und „verschmutzte“ (engl. smudge) Filter bezeichnet. In der Datei .gitattributes können Sie einen Filter für bestimmte Pfade setzen und dann Skripte einrichten, die die Dateien verarbeiten, kurz bevor sie ausgecheckt werden (für „smudge“, siehe Der „smudge“ Filter wird beim Auschecken ausgeführt) und kurz bevor sie zum Commit vorgemerkt werden (für „clean“, siehe Der „clean“ Filter wird ausgeführt, wenn Dateien zum Commit vorgemerkt werden). Diese Filter können so eingestellt werden, um damit alle möglichen interessanten Aufgaben zu erledigen.

Der „smudge“ Filter wird beim Auschecken ausgeführt
Figure 143. Der „smudge“ Filter wird beim Auschecken ausgeführt
Der „clean“ Filter wird ausgeführt
Figure 144. Der „clean“ Filter wird ausgeführt, wenn Dateien zum Commit vorgemerkt werden

Die ursprüngliche Commit-Meldung dieser Funktion zeigt ein einfaches Anwendungsbeispiel, wie Sie Ihren gesamten C-Quellcode vor dem Commit durch das indent Programm laufen lassen können. Sie können es so einrichten, dass das Filterattribut in Ihrer .gitattributes Datei so gesetzt ist, dass *.c Dateien mit dem Filter „indent“ gefiltert werden:

*.c filter=indent

Dann weisen Sie Git an, was der „indent“ Filter bei smudge und clean bewirkt:

$ git config --global filter.indent.clean indent
$ git config --global filter.indent.smudge cat

Wenn Sie in diesem Fall Dateien committen, die mit *.c übereinstimmen, wird Git sie das Indent-Programm durchlaufen lassen, bevor es sie der Staging-Area hinzufügt. Dann werden sie durch das cat Programm laufen, bevor es sie wieder auf die Festplatte auscheckt. Das cat Programm macht im Grunde genommen nichts: Es übergibt die gleichen Daten, die es bekommt. Diese Kombination filtert effektiv alle C-Quellcode-Dateien durch Einrückung (engl. indent), bevor sie committet werden.

Ein weiteres interessantes Beispiel ist die $Date$ Schlüsselwort-Erweiterung im RCS-Stil. Um das richtig anzuwenden, benötigen Sie ein kleines Skript, das einen Dateinamen anlegt, das letzte Commit-Datum für dieses Projekt ermittelt und das Datum in die Datei einfügt. Hier ist ein kleines Ruby-Skript, das das erledigt:

#! /usr/bin/env ruby
data = STDIN.read
last_date = `git log --pretty=format:"%ad" -1`
puts data.gsub('$Date$', '$Date: ' + last_date.to_s + '$')

Alles, was das Skript macht, ist das neueste Commit-Datum aus dem git log Befehl zu ermitteln, es in alle $Date$ Zeichenketten zu übertragen, die es in stdin erkennt und die Ergebnisse auszugeben. Es sollte einfach sein, es in der Programmiersprache zu erstellen mit der Sie am besten zurechtkommen. Sie können diese Datei expand_date nennen und in Ihren Pfad aufnehmen. Jetzt müssen Sie einen Filter in Git einrichten (nennen Sie ihn z.B. dater) und ihm sagen, dass er Ihren expand_date Filter verwenden soll, um die Dateien beim Auschecken zu bearbeiten (smudge). Ein Perl-Ausdruck dient dazu, das beim Commit zu bereinigen:

$ git config filter.dater.smudge expand_date
$ git config filter.dater.clean 'perl -pe "s/\\\$Date[^\\\$]*\\\$/\\\$Date\\\$/"'

Dieses Perl-Snippet entfernt alles, was es in einer $Date$ Zeichenkette sieht, um an den Ausgangspunkt zurückzukehren. Nachdem Ihr Filter jetzt fertig ist, können Sie ihn testen, indem Sie ein Git-Attribut für diese Datei einrichten, das den neuen Filter aktiviert und eine Datei mit Ihrem $Date$ Schlüsselwort erstellen:

date*.txt filter=dater
$ echo '# $Date$' > date_test.txt

Wenn Sie diese Änderungen committen und die Datei erneut auschecken, sehen Sie das Schlüsselwort ordnungsgemäß ersetzt:

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

Sie sehen, wie leistungsfähig diese Technik für maßgeschneiderte Anwendungen sein kann. Sie müssen jedoch vorsichtig sein, da die .gitattributes Datei committed und mit dem Projekt weitergegeben wird, der Treiber (hier: dater) aber nicht. Es wird also nicht überall funktionieren. Wenn Sie diese Filter entwerfen, sollten sie, bei fehlerhaften Ergebnissen, problemlos in der Lage sein das Projekt trotzdem einwandfrei laufen zu lassen.

Exportieren Ihres Repositorys

Mit den Daten von Git-Attribute können Sie interessante Aufgaben beim Export eines Archivs Ihres Projekts erledigen.

export-ignore

Sie können Git anweisen, bestimmte Dateien oder Verzeichnisse nicht zu exportieren, wenn Sie ein Archiv erstellen. Wenn es ein Unterverzeichnis oder eine Datei gibt, die Sie nicht in Ihre Archivdatei aufnehmen möchten, aber in Ihr Projekt einchecken möchten, können Sie diese Dateien über das Attribut export-ignore bestimmen.

Angenommen, Sie haben einige Testdateien in einem test/ Unterverzeichnis und es ist nicht sinnvoll, sie in den Tarball-Export Ihres Projekts aufzunehmen. Man kann die folgende Zeile zu der Datei mit den Git-Attributen hinzufügen:

test/ export-ignore

Wenn Sie nun git archive ausführen, um einen Tarball Ihres Projekts zu erstellen, wird dieses Verzeichnis nicht in das Archiv aufgenommen.

export-subst

Beim Exportieren von Dateien für die Verteilung können Sie die Formatierung und Schlüsselwort-Erweiterung von git log auf ausgewählte Teile von Dateien anwenden, die mit dem Attribut export-subst markiert sind.

Wenn Sie beispielsweise eine Datei mit dem Namen LAST_COMMIT in Ihr Projekt aufnehmen möchten und Metadaten über den letzten Commit automatisch in das Projekt eingespeist werden sollen, sobald git archive läuft, können Sie beispielsweise Ihre .gitattributes und LAST_COMMIT Dateien so einrichten:

LAST_COMMIT export-subst
$ echo 'Last commit date: $Format:%cd by %aN$' > LAST_COMMIT
$ git add LAST_COMMIT .gitattributes
$ git commit -am 'adding LAST_COMMIT file for archives'

Wenn Sie git archive ausführen, sieht der Inhalt der archivierten Datei so aus:

$ git archive HEAD | tar xCf ../deployment-testing -
$ cat ../deployment-testing/LAST_COMMIT
Last commit date: Tue Apr 21 08:38:48 2009 -0700 by Scott Chacon

Die Ersetzungen können z.B. die Commit-Meldung und beliebige git notes enthalten, und git log kann einfache Zeilenumbrüche durchführen:

$ echo '$Format:Last commit: %h by %aN at %cd%n%+w(76,6,9)%B$' > LAST_COMMIT
$ git commit -am 'export-subst uses git log'\''s custom formatter

git archive uses git log'\''s `pretty=format:` processor
directly, and strips the surrounding `$Format:` and `$`
markup from the output.
'
$ git archive @ | tar xfO - LAST_COMMIT
Last commit: 312ccc8 by Jim Hill at Fri May 8 09:14:04 2015 -0700
       export-subst uses git log's custom formatter

         git archive uses git log's `pretty=format:` processor directly, and
         strips the surrounding `$Format:` and `$` markup from the output.

Das so entstandene Archiv ist für die Verwendung geeignet, aber wie jedes andere exportierte Archiv ist es nicht zur weiteren Entwicklungsarbeit verwendbar.

Merge-Strategien

Sie können Git-Attribute auch verwenden, um Git anzuweisen, verschiedene Merge-Strategien für spezifische Dateien in Ihrem Projekt zu verwenden. Eine sehr nützliche Option ist es, Git anzuweisen, dass es nicht versuchen soll, bestimmte Dateien zusammenzuführen, wenn sie Konflikte haben, sondern Ihre Version der Daten zu benutzen, anstelle der von jemand anderem.

Das ist nützlich, wenn ein Branch in Ihrem Projekt auseinander gelaufen ist oder sich spezialisiert hat. Wenn Sie jedoch Änderungen wieder in ihn einfügen möchten und einige Dateien ignorieren möchten. Angenommen, Sie haben eine Datenbank-Einstellungsdatei namens database.xml, die sich in zwei Branches voneinander unterscheidet. Sie wollen in Ihrer zweiten Branch verschmelzen, ohne die Datenbankdatei zu beschädigen. Dann können Sie so ein Attribut einrichten:

database.xml merge=ours

Dann definieren Sie die Dummy-Merge-Strategie ours mit:

$ git config --global merge.ours.driver true

Wenn Sie in den zweiten Branch verschmelzen, ohne in Merge-Konflikte mit der Datei database.xml zu laufen, sehen Sie so etwas wie hier:

$ git merge topic
Auto-merging database.xml
Merge made by recursive.

In diesem Fall bleibt die Datei database.xml auf der Version, die sie ursprünglich gehabt hat.