Chapters ▾ 2nd Edition

8.2 Customizing Git - Git Eienskappe (Git Attributes)

Git Eienskappe (Git Attributes)

Sommige van hierdie instellings kan ook vir 'n pad gespesifiseer word, sodat Git daardie instellings slegs vir 'n subgids of subset van lêers toepas. Hierdie padspesifieke instellings word Git-eienskappe genoem en word óf in 'n .gitattributes lêer in een van jou gidse (gewoonlik die wortel van jou projek) óf in die .git/info/attributes lêer gestel as jy nie wil hê die eienskaplêer moet saam met jou projek vasgelê (committed) word nie.

Deur die gebruik van eienskappe kan jy dinge doen soos om aparte saamsmeltingsstrategieë (merge strategies) vir individuele lêers of gidse in jou projek te spesifiseer, vir Git te vertel hoe om nie-tekslêers te diff, of Git die inhoud te laat filter voordat jy dit in of uit Git in- of uittrek (check in or check out). In hierdie afdeling sal jy leer oor sommige van die eienskappe wat jy op jou paaie in jou Git-projek kan stel en 'n paar voorbeelde sien van die gebruik van hierdie kenmerk in die praktyk.

Binêre Lêers (Binary Files)

Een oulike truuk waarvoor jy Git-eienskappe kan gebruik, is om vir Git te sê watter lêers binêr is (in gevalle waar dit andersins dalk nie sal kan agterkom nie) en vir Git spesiale instruksies te gee oor hoe om daardie lêers te hanteer. Sommige tekslêers kan byvoorbeeld masjiengenererend wees en nie diff-baar wees nie, terwyl sommige binêre lêers wel diff-baar is. Jy sal sien hoe om vir Git te sê watter is watter.

Identifisering van Binêre Lêers (Identifying Binary Files)

Sommige lêers lyk soos tekslêers, maar moet vir alle praktiese doeleindes as binêre data hanteer word. Byvoorbeeld, Xcode-projekte op macOS bevat 'n lêer wat eindig op .pbxproj, wat basies 'n JSON (gewone teks JavaScript-dataformaat) datastel is wat na die skyf geskryf word deur die IDE, wat jou bou-instellings ensovoorts aanteken. Alhoewel dit tegnies 'n tekslêer is (want dit is alles UTF-8), wil jy dit nie as sodanig hanteer nie, want dit is eintlik 'n liggewig databasis – jy kan nie die inhoud saamsmelt as twee mense dit verander nie, en diffs is oor die algemeen nie nuttig nie. Die lêer is bedoel om deur 'n masjien verbruik te word. In wese wil jy dit soos 'n binêre lêer hanteer.

Om vir Git te sê om alle pbxproj lêers as binêre data te hanteer, voeg die volgende reël by jou .gitattributes lêer:

*.pbxproj binary

Nou sal Git nie probeer om CRLF kwessies te omskep of reg te maak nie; en sal ook nie probeer om 'n diff te bereken of af te druk vir veranderings in hierdie lêer wanneer jy git show of git diff op jou projek uitvoer nie.

Diffing van Binêre Lêers (Diffing Binary Files)

Jy kan ook die Git-eienskap-funksionaliteit gebruik om binêre lêers effektief te diff. Jy doen dit deur vir Git te sê hoe om jou binêre data om te skakel na 'n teksformaat wat via die normale diff vergelyk kan word.

Eerstens sal jy hierdie tegniek gebruik om een van die mees irriterende probleme wat aan die mensdom bekend is, op te los: die weergawebeheer (version-controlling) van Microsoft Word dokumente. As jy Word-dokumente onder weergawebeheer wil plaas, kan jy dit in 'n Git-bewaarplek steek en af en toe vaslê; maar wat help dit? As jy git diff normaalweg uitvoer, sien jy net so iets:

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

Jy kan nie twee weergawes direk vergelyk nie tensy jy hulle uittrek (check out) en handmatig deurkyk, reg? Dit blyk dat jy dit redelik goed kan doen deur Git-eienskappe te gebruik. Plaas die volgende reël in jou .gitattributes lêer:

*.docx diff=word

Dit vertel vir Git dat enige lêer wat ooreenstem met hierdie patroon (.docx), die “word” filter moet gebruik wanneer jy 'n diff wat veranderings bevat, probeer besigtig. Wat is die “word” filter? Jy moet dit opstel. Hier sal jy Git konfigureer om die docx2txt program te gebruik om Word-dokumente in leesbare tekslêers om te skakel, wat dit dan behoorlik sal diff.

Eerstens moet jy docx2txt installeer; jy kan dit aflaai by https://sourceforge.net/projects/docx2txt. Volg die instruksies in die INSTALL lêer om dit iewers te plaas waar jou dop (shell) dit kan vind. Volgende skryf jy 'n toedraai-skrip (wrapper script) om die afvoer om te skakel na die formaat wat Git verwag. Skep 'n lêer wat iewers in jou pad is genaamd docx2txt, en voeg hierdie inhoud by:

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

Moenie vergeet om chmod a+x op daardie lêer uit te voer nie. Laastens kan jy Git opstel om hierdie skrip te gebruik:

$ git config diff.word.textconv docx2txt

Nou weet Git dat as dit 'n diff tussen twee momentopnames (snapshots) probeer doen, en enige van die lêers eindig op .docx, moet dit daardie lêers deur die “word” filter hardloop, wat as die docx2txt program gedefinieer is. Dit maak effektief oulike teksgebaseerde weergawes van jou Word-lêers voordat dit poog om hulle te diff.

Hier is 'n voorbeeld: Hoofstuk 1 van hierdie boek is na Word-formaat omgeskakel en in 'n Git-bewaarplek vasgelê. Toe is 'n nuwe paragraaf bygevoeg. Hier is wat git diff wys:

$ 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 vertel ons suksesvol en bondig dat ons die string “Testing: 1, 2, 3.” bygevoeg het, wat korrek is. Dit is nie perfek nie – formateringsveranderings sou nie hier verskyn nie – maar dit werk beslis.

Nog 'n interessante probleem wat jy op hierdie manier kan oplos, behels die diffing van beeldlêers. Een manier om dit te doen is om beeldlêers deur 'n filter te laat loop wat hul EXIF inligting onttrek – metadata wat by die meeste beeldformate aangeteken word. As jy die exiftool program aflaai en installeer, kan jy dit gebruik om jou beelde na teks oor die metadata om te skakel, so ten minste sal die diff vir jou 'n tekstuele verteenwoordiging wys van enige veranderings wat plaasgevind het. Plaas die volgende reël in jou .gitattributes lêer:

*.png diff=exif

Konfigureer Git om hierdie instrument te gebruik:

$ git config diff.exif.textconv exiftool

As jy 'n beeld in jou projek vervang en git diff uitvoer, sien jy iets soos dit:

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

Jy kan maklik sien dat die lêergrootte en beeldafmetings albei verander het.

Sleutelwoorduitbreiding (Keyword Expansion)

SVN- of CVS-styl sleutelwoorduitbreiding word dikwels versoek deur ontwikkelaars wat gewoond is aan daardie stelsels. Die hoofprobleem hiermee in Git is dat jy nie 'n lêer met inligting oor die vaslegging kan wysig nadat jy vasgelê het nie, want Git doen eers 'n kontrolesom (checksums) van die lêer. Jy kan egter teks in 'n lêer inspuit wanneer dit uitgetrek word en dit weer verwyder voordat dit by 'n vaslegging gevoeg word. Git-eienskappe bied vir jou twee maniere om dit te doen.

Eerstens kan jy outomaties die SHA-1 kontrolesom van 'n blob in 'n $Id$ veld in die lêer inspuit. As jy hierdie eienskap op 'n lêer of stel lêers stel, sal Git die volgende keer as jy daardie tak uittrek (check out), daardie veld vervang met die SHA-1 van die blob. Dit is belangrik om op te let dat dit nie die SHA-1 van die vaslegging is nie, maar van die blob self. Plaas die volgende reël in jou .gitattributes lêer:

*.txt ident

Voeg 'n $Id$ verwysing by 'n toetslêer:

$ echo '$Id$' > test.txt

Die volgende keer as jy hierdie lêer uittrek, spuit Git die SHA-1 van die blob in:

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

Die resultaat is egter van beperkte nut. As jy sleutelwoordvervanging (keyword substitution) in CVS of Subversion gebruik het, kan jy 'n datumstempel insluit – die SHA-1 is nie so nuttig nie, want dit is redelik willekeurig en jy kan nie sien of een SHA-1 ouer of nuwer as 'n ander is net deur na hulle te kyk nie.

Dit blyk dat jy jou eie filters kan skryf om vervangings (substitutions) in lêers te doen tydens vaslegging/uittrek (commit/checkout). Hierdie word “clean” en “smudge” filters genoem. In die .gitattributes lêer kan jy 'n filter vir spesifieke paaie stel en dan skripte opstel wat lêers sal verwerk net voordat hulle uitgetrek word (“smudge”, sien Die “smudge” filter word tydens checkout uitgevoer) en net voor dit voorberei (staged) word (“clean”, sien Die “clean” filter word uitgevoer wanneer lêers voorberei (staged) word). Hierdie filters kan ingestel word om allerhande prettige dinge te doen.

The “smudge” filter is run on checkout
Figure 182. Die “smudge” filter word tydens checkout uitgevoer
The “clean” filter is run when files are staged
Figure 183. Die “clean” filter word uitgevoer wanneer lêers voorberei (staged) word

Die oorspronklike vasleggingsboodskap vir hierdie kenmerk gee 'n eenvoudige voorbeeld om al jou C-bronkode deur die indent program te laat loop voordat jy vaslê. Jy kan dit opstel deur die filter-eienskap in jou .gitattributes lêer te stel om \*.c lêers te filter met die “indent” filter:

*.c filter=indent

Sê dan vir Git wat die “indent” filter op smudge en clean doen:

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

In hierdie geval, wanneer jy lêers vaslê wat by *.c pas, sal Git hulle deur die indent program laat loop voordat dit hulle voorberei (stages) en dan deur die cat program laat loop voordat dit hulle weer na die skyf uittrek. Die cat program doen in wese niks nie: dit spoeg dieselfde data uit wat inkom. Hierdie kombinasie filter effektief alle C-bronkodelêers deur indent voor dit vasgelê word.

Nog 'n interessante voorbeeld kry $Date$ sleutelwoorduitbreiding (keyword expansion), RCS styl. Om dit behoorlik te doen, het jy 'n klein skrip nodig wat 'n lêernaam neem, die laaste vasleggingsdatum vir hierdie projek uitwerk, en die datum in die lêer invoeg. Hier is 'n klein Ruby-skrip wat dit doen:

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

Al wat die skrip doen is om die jongste vasleggingsdatum vanaf die git log opdrag te kry, dit in enige $Date$ stringe wat dit in stdin sien in te plak, en die resultate te druk – dit behoort maklik te wees om dit te doen in watter taal jy ook al die gemaklikste mee is. Jy kan hierdie lêer expand_date noem en dit in jou pad plaas. Nou moet jy 'n filter in Git opstel (noem dit dater) en sê dat dit jou expand_date filter moet gebruik om die lêers tydens checkout te smudge. Jy sal 'n Perl uitdrukking (expression) gebruik om dit tydens vaslegging (commit) skoon te maak (clean):

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

Hierdie Perl-brokkie (snippet) stroop enigiets wat dit in 'n $Date$ string sien weg, om terug te kom na waar jy begin het. Noudat jou filter gereed is, kan jy dit toets deur 'n Git-eienskap vir daardie lêer op te stel wat die nuwe filter inwerk, en 'n lêer met jou $Date$ sleutelwoord te skep:

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

As jy daardie veranderings vaslê en die lêer weer uittrek, sien jy die sleutelwoord wat behoorlik vervang is:

$ git add date_test.txt .gitattributes
$ git commit -m "Test 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$

Jy kan sien hoe kragtig hierdie tegniek vir pasgemaakte toepassings kan wees. Jy moet egter versigtig wees, aangesien die .gitattributes lêer vasgelê word en saam met die projek versprei word, maar die drywer (in hierdie geval, dater) word nie, so dit sal nie oral werk nie. Wanneer jy hierdie filters ontwerp, moet hulle in staat wees om op 'n beheerde manier te faal (fail gracefully) en die projek behoort steeds behoorlik te werk.

Uitvoer van Jou Bewaarplek (Exporting Your Repository)

Git-eienskap-data laat jou ook toe om 'n paar interessante dinge te doen wanneer jy 'n argief van jou projek uitvoer (exporting).

export-ignore

Jy kan vir Git sê om nie sekere lêers of gidse uit te voer wanneer 'n argief gegenereer word nie. As daar 'n subgids of lêer is wat jy nie in jou argieflêer wil insluit nie, maar wat jy wel in jou projek ingecheck wil hê, kan jy daardie lêers bepaal via die export-ignore eienskap.

Sê byvoorbeeld jy het 'n paar toetslêers in 'n test/ subgids, en dit maak nie sin om dit by die tarball-uitvoer van jou projek in te sluit nie. Jy kan die volgende reël by jou Git-eienskappe lêer voeg:

test/ export-ignore

Nou, as jy git archive uitvoer om 'n tarball van jou projek te skep, sal daardie gids nie by die argief ingesluit word nie.

export-subst

Wanneer jy lêers vir ontplooiing (deployment) uitvoer, kan jy git log se formatering en sleutelwoorduitbreiding-verwerking (keyword-expansion processing) toepas op geselekteerde gedeeltes van lêers wat met die export-subst eienskap gemerk is.

Byvoorbeeld, as jy 'n lêer genaamd LAST_COMMIT by jou projek wil insluit, en metadata oor die laaste vaslegging outomaties daarin wil hê sodra git archive uitgevoer word, kan jy jou .gitattributes en LAST_COMMIT lêers soos volg opstel:

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'

Wanneer jy git archive uitvoer, sal die inhoud van die geargiveerde lêer soos volg lyk:

$ 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 vervangings kan byvoorbeeld die vasleggingsboodskap en enige git notes insluit, en git log kan eenvoudige woordomvouing (word wrapping) doen:

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

Die resulterende argief is geskik vir ontplooiingswerk, maar soos enige uitgevoerde argief is dit nie geskik vir verdere ontwikkelingswerk nie.

Saamsmeltingsstrategieë (Merge Strategies)

Jy kan ook Git-eienskappe gebruik om vir Git te sê om verskillende saamsmeltingsstrategieë vir spesifieke lêers in jou projek te gebruik. Een baie nuttige opsie is om vir Git te sê om nie te probeer om spesifieke lêers saam te smelt as hulle konflikte het nie, maar eerder om jóú kant van die saamsmelting te gebruik bo iemand anders s’n.

Dit is nuttig as 'n tak in jou projek afgewyk het (diverged) of gespesialiseerd is, maar jy veranderings daaruit wil kan terug insmelt, en jy wil sekere lêers ignoreer. Sê jy het 'n databasisinstellingslêer genaamd database.xml wat in twee takke verskil, en jy wil in jou ander tak insmelt sonder om die databasislêer deurmekaar te krap. Jy kan 'n eienskap soos hierdie opstel:

database.xml merge=ours

En definieer dan 'n fiktiewe (dummy) ours saamsmeltingsstrategie met:

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

As jy die ander tak insmelt, in plaas daarvan om saamsmeltingskonflikte met die database.xml lêer te hê, sien jy so iets:

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

In hierdie geval bly database.xml by watter weergawe jy ook al oorspronklik gehad het.