Git
Chapters ▾ 2nd Edition

8.2 Прилагођавање програма Гит - Гит атрибути

Гит атрибути

Нека од ових подешавања могу да се наведу и за путању, тако да их програм Гит примењује само за поддиректоријум или за подскуп фајлова. Ова подешавања специфична за путање се зову Гит атрибути и налазе се или у фајлу .gitattributes у неком од директоријума (обично у корену пројекта), или у фајлу .git/info/attributes уколико не желите да фајл са атрибутима комитујете уз пројекат.

Коришћењем атрибута можете урадити ствари као што су навођење посебне стратегије спајања за појединачне фајлове или директоријуме свог пројекта, можете рећи програму Гит како да прикаже разлику фајлова који нису текстуалног типа, или да програм Гит филтрира садржај пре него што га пријавите или одјавите из базе програма Гит. У овом одељку ћете научити неке од атрибута које можете поставити у путање Гит пројекта и видећете неколико примера коришћења ове могућности у пракси.

Бинарни фајлови

Један одличан трик за који можете употребити Гит атрибуте јесте да кажете програму Гит који фајлови су бинарни (у случајевима када то на други начин не може да одреди) и да му задате посебна упутства како да обради те фајлове. На пример, неки текстуални фајлови су можда машински генерисани и немогуће је приказати њихову разлику, а док је могуће приказати разлику неких бинарних фајлова. Видећете како да програму Гит кажете шта је шта.

Идентификовање бинарних фајлова

Неки фајлови изгледају као текстуални, али се за све намере и сврхе третирају као бинарни подаци. На пример, Xcode пројекти на мекОС садрже фајл који се завршава са .pbxproj и који је у суштини скуп података у JSON формату (JavaScript формат података представљен чистим текстом) који IDE записује на диск, у којем се бележе подешавања за изградњу и слично. Технички је ово текстуални фајл (јер је у UTF-8), али не бисте желели да га третирате као такав јер се у суштини ради о простој бази података – садржаје не можете да спојите ако га две особе истовремено промене, а разлике међу њима у општем случају нису од помоћи. Фајл је предвиђен да га чита машина. У суштини, желите да га третирате као бинарни фајл.

Да бисте рекли програму Гит да су сви pbxproj фајлови заправо бинарни подаци, додајте следећи линију у свој .gitattributes фајл:

*.pbxproj binary

Програм Гит сада неће покушавати да среди проблеме са CRLF; нити ће да покушава да израчуна или прикаже разлику за промене у овом фајлу када у свом пројекту извршите git show или git diff .

Приказивање разлике бинарних фајлова

Функционалност Гит атрибута можете да искористите и за поређење бинарних фајлова. Ово можете урадити тако што ћете програму Гит рећи како да конвертује бинарни фајл у текстуални формат који може да се упореди уобичајеним diff алатом.

Прво ћете искористити ову технику да решите један од најнезгоднијих проблема познатих човечанству: контрола верзија докумената писаних у програму Мајкрософт Ворд. Сви знају да је Ворд један од најужаснијих едитора који постоје и чудновато је што га сви и даље користе. Ако желите да контролишете верзије Ворд докумената, можете да их убаците у Гит репозиторијум и комитујете с времена на време; али какве користи имате од тога? Ако извршите git diff на уобичајен начин, видећете само нешто овако:

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

Две верзије не можете директно упоредити осим ако их не одјавите и ручно прегледате, зар не? Испоставља се да ово прилично добро можете урадити помоћу Гит атрибута. Ставите следећу линију у свој .gitattributes фајл:

*.docx diff=word

Ово говори програму Гит да када пробате да погледате разлике између промена за сваки фајл који одговара шаблону (.docx) треба да употреби филтер „word”. Али шта је филтер „word”? Морате да га подесите. Овде ћете програм Гит подесити да употреби програм docx2txt који конвертује Ворд документе у читљиве текстуалне фајлове, за које ћете онда моћи да прегледате разлике како ваља.

Најпре, мораћете да инсталирате docx2txt; можете га преузети са http://docx2txt.sourceforge.net. Пратите упутства из фајла INSTALL како бисте га инсталирали негде где га љуска може пронаћи. Затим ћете написати скрипту-омотач која ће конвертовати излаз у формат који програм Гит очекује. Креирајте фајл под именом docx2txt негде на путањи и уметните у њега следећи садржај:

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

Не заборавите да над фајлом извршите и chmod a+x. И коначно, конфигуришите програм Гит тако да користи ову скрипту:

$ git config diff.word.textconv docx2txt

Програм Гит сада зна да, ако покуша да прикаже разлике између два снимка, а било који од фајлова се завршава са .docx, те фајлове треба пропусти кроз филтер word, који је дефинисан као docx2txt програм. Ово у суштини ствара фине текстуалне верзије Ворд фајлова пре него што се проба њихово поређење.

Ево примера: прво поглавље ове књиге је конвертовано у Ворд формат и комитовано у Гит репозиторијум. Онда је додат нови пасус. Ево шта приказује git diff:

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

Програм Гит нам успешно и недвосмислено каже да смо додали стринг „Testing: 1, 2, 3”, што је тачно. Метода није савршена – овде се не појављују измене у форматирању – али несумњиво ради.

Још један занимљив проблем који можете решити на овај начин јесте преглед разлика између слика. Један од начина да урадите ово јесте да пропустите слике кроз филтер који издваја њихове EXIF податке – метаподатке који се бележе у већини формата за слике. Ако преузмете и инсталирати програм exiftool, можете да га искористите да конвертујете слике у текст о метаподацима, како бисте приликом прегледа разлика барем видели текстуалну презентацију промена које су се можда догодиле. Поставите следећу линију у свој .gitattributes фајл:

*.png diff=exif

Подесите програм Git тако да користи овај алат:

$ git config diff.exif.textconv exiftool

Ако у пројекту промените слику и извршите git diff, видећете нешто овако:

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

Лако можете видети да су се промениле и димензије и величина фајла.

Проширење кључних речи

Програмери који су навикли да користе SVN и CVS често захтевају проширење (експанзију) кључних речи. Главни проблем са овиме у програму Гит је што не можете да измените фајл са информацијом о комиту након што сте комитовали, јер програм Гит прво рачуна контролну суму фајла. Ипак, можете убризгати текст у фајл када се одјави и уклонити га непосредно пре него што се дода у у комит. Гит атрибути вам нуде два начина да урадите ово.

Најпре, можете аутоматски да убризгати SHA-1 контролну суму блоба у $Id$ поље фајла. Овај атрибут можете подесити за фајл или скуп фајлова, па када следећи пут одјавите ту грану, програм Гит ће заменити то поље са SHA-1 контролном сумом блоба. Важно је приметити да ово није SHA-1 комита, већ самог блоба. Поставите следећу линију у свој .gitattributes фајл:

*.txt ident

Додајте $Id$ референцу на тест фајл:

$ echo '$Id$' > test.txt

Следећи пут када одјавите овај фајл, програм Гит убризгава SHA-1 блоба:

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

Међутим, тај резултат има ограничену употребу. Ако сте користили замену кључних речи у програму CVS или Subversion, могли сте да укључите и печат с датумом — SHA-1 није толико користан јер је поприлично насумичан и само на основу посматрања вредности не можете закључити да ли је неки SHA-1 старији или новији од неког другог.

Испоставља се да можете да напишете своје филтере који ће радити замену у фајловима при комитовању/одјављивању. Ови филтери се зову „чисти” (clean) и „замрљани” (smudge). У фајлу .gitattributes можете поставити филтер за одређене путање па онда подесити скрипте које ће обрадити фајлове непосредно пре него што се одјаве („замрљани”, погледајте „Замрљани” филтер се покреће приликом одјављивања) и непосредно пре него што се поставе на стејџ („чисти”, погледајте "Чисти" филтер се покреће када се фајлови постављају на стејџ). Ови филтери се могу подесити тако да раде гомилу занимљивих ствари.

„Замрљани” филтер се покреће приликом одјављивања
Слика 143. „Замрљани” филтер се покреће приликом одјављивања
„Чисти” филтер се покреће када се фајлови постављају на стејџ
Слика 144. "Чисти" филтер се покреће када се фајлови постављају на стејџ

Првобитна комит порука за ову могућност вам даје једноставан пример пропуштања комплетног изворног кода написаног у језику C кроз програм indent пре него што се обави комит. Можете да поставите ово тако што ћете подесити филтер атрибут у фајлу .gitattributes тако да филтрира .*c фајлове кроз filter indent:

*.c filter=indent

Онда реците програму Гит шта филтер indent ради за smudge и clean:

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

У овом случају, када комитујете фајлове које одговарају шаблону *.c, Гит ће их пропустити кроз програм за увлачење редова пре него што их постави на стејџ и онда ће их пропустити кроз програм cat пре него што их одјавите назад на диск. Програм cat у суштини не ради ништа: избацује исте податке које је добио на улазу. Ова комбинација делотворно филтрира сав изворни кôд на језику C кроз филтер indent пре комитовања.

Још један занимљив пример показује проширење кључне речи $Date$, у стилу програма RCS. Да бисте ово урадили како треба, требаће вам мала скрипта која узима име фајла, одређује датум последњег комита за овај пројекат, и убацује тај датум у фајл. Ево мале Руби скрипте која ради управо то:

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

Све што скрипта ради јесте да узме датум последњег комита из излаза команде git log, убаци то у било који стринг $Date$ који види на stdin и штампа резултате – требало би да буде довољно једноставно да направите скрипту у било ком другом језику. Овај фајл можете назвати expand_date и ставити га на путању. Сада треба да подесите филтер у програму Гит (зваћемо га dater) и рећи му да треба да користи ваш филтер expand_date да замрља фајлове приликом одјављивања. Користићемо Перл израз да то почистимо приликом комита:

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

Овај исечак Перл кода склања све што види у стрингу $Date$, како бисте се вратили назад на почетно стање. Сада када је филтер спреман, можете да га тестирате постављањем Гит атрибута за тај фајл који укључује нови филтер и креирањем фајла са $Date$ кључном речи:

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

Ако комитујете те промене па онда опет одјавите фајл, видећете да се кључна реч заменила како треба:

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

Можете видети колико ова техника може бити моћна у посебним применама. Ипак, морате бити обазриви, јер се фајл .gitattributes комитује и прослеђује са пројектом, али не и драјвер (у овом случају dater), што значи да неће радити свуда. Када дизајнирате ове филтере, треба да их направите тако да елегантно прекину извршавање, а да пројекат и даље функционише како ваља.

Извоз репозиторијума

Подаци из Гит атрибута вам омогућавају и да радите неке занимљиве ствари када извозите или архивирате свој пројекат.

export-ignore

Можете рећи програму Гит да не извози одређене фајлове или директоријуме када генерише архиву. Ако постоји поддиректоријум или фајл коју не желите да укључите у архиву али желите да су пријављени у пројекат, можете да их задате помоћу атрибута export-ignore.

На пример, рецимо да имате неке фајлове за тестирање у поддиректоријуму test/ и нема смисла укључивати их у tarball пројекта. Можете додати следећу линију у свој фајл Гит атрибута:

test/ export-ignore

Ако сада извршите git archive да креирате tarball пројекта, тај директоријум неће бити укључен у архиву.

export-subst

Када извозите фајлове за постављање у употребу можете да примените форматирање наредбе git log и обраду проширења кључних речи на одређени скуп фајлова који су обележени атрибутом export-subst.

На пример, ако желите да у свој пројекат укључите фајл под именом LAST_COMMIT и да се метаподаци о последњем комиту аутоматски убризгају када се изврши git archive, можете поставити своје .gitattributes и LAST_COMMIT фајлове рецимо овако:

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'

Када извршите git archive, садржај архивираног фајла ће изгледати овако:

$ 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

Замене могу да укључе, на пример, комит поруке и било какве git notes, а git log може да уради једноставан прелом редова:

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

Архива која се добије као резултат је погодна за постављање за употребу, али као и свака друга извезена архива није погодна за даљи рад на развоју.

Стратегије спајања

Гит атрибуте можете искористите и да кажете програму Гит да користи различите стратегије спајања за одређене фајлове у пројекту. Једна веома корисна опција је да програму Гит кажете да не покушава да споји одређене фајлове када имају конфликте, већ да увек користи вашу страну у спајању уместо туђе.

Ово је корисно ако се грана у вашем пројекту разишла или је специјализована, али желите да будете у могућности да промене у њој спојите назад и желите да игноришете одређене фајлове. Рецимо да имате фајл подешавања базе података database.xml која се разликује у две гране и желите да своју другу грану спојите, а да се фајл базе података не забрља. Атрибут можете да поставите овако:

database.xml merge=ours

Па да затим овако дефинишете лажну ours стратегију:

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

Ако спојите другу грану, уместо да имате конфликте у фајлу database.xml, видећете нешто овако:

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

У овом случају, database.xml остаје у оној верзији коју сте првобитно имали.