Git
Chapters ▾ 2nd Edition

8.2 Prilagođavanje Gita - Git atributi

Git atributi

Neka od ovih podešavanja se mogu specificirati i za putanju, tako da ih git primenjuje samo za poddirektorijum ili za podskup datoteka. Ova podešavanja specifična za putanje se zovu i Git atributi i nalaze se ili u datoteci .gitattributes u nekom od direktorijuma (obično u korenu projekta), ili u datoteci .git/info/attributes ukoliko ne želite da datoteku sa atributima komitujete uz projekat.

Korišćenjem atributa možete, na primer, ostvariti to da postoje odvojene strategije za spajanje za različite datoteke ili direktorijume iz projekta; možete reći Gitu kako da predstavi razliku fajlovi koji nisu sačuvani u tekstualnom formatu; ili da isfiltrirate sadržaj pre nego što ga čekinujete ili čekautujete iz Gita. U ovom odeljku, naučićete neke atribute koje možete podesiti u putanju Git prjoekta i videćete nekoliko primera kako ih u praksi možete primeniti.

Binarne datoteke

Jedan super trik za koji vam mogu poslužiti Git atributi jeste da kažete Gitu koji fajlovi su binarni (u slučaju da je nemoguće svhatiti na drugi način), i da date Gitu posebne instrukcije o tome kako da rukovodi tim datotekama. Na primer, neke tekstualne datoteke su automatski generisane i nemoguće je pregledati njihovu razliku, dok je moguće pogledati razliku nekih binarnih datoteka. Videćete kako da Gitu kažete šta je šta.

Identifikovanje binarnih datoteka

Neke fatoteke izgledaju kao tekstualne, ali se uvek koriste i tretiraju kao binarni podaci. Na primer, Xcode projekti na Meku sadrže datoteke koji se završavaju sa .pbxproj, koji su u suštini skup podataka u JSON formatu (JavaScript format podataka predstavljen tekstom) koji IDE zapisuje na disk, što beleži podešavanja za bildovanje, i slično. Tehnički je ovo tekstualna datoteka (jer je u UTF-8), ali ne biste želeli da je tretirate kao takvu jer se u suštini radi o primitivnoj verziji baze podataka — ne možete da spojite njihove sadržaje ako je dve osobe istovremeno promene, a razlike među njima generalno nisu od pomoći. Datoteka je namenjena za čitanje od strane mašine. U suštini, želite da je tretirate kao binarnu datoteku.

Da biste rekli Gitu da su sve pbxproj datoteke zapravo binarni podaci, dodajte sledeći liniju u datoteku .gitattributes.

*.pbxproj binary

Sada Git neće pokušavati da sredi probleme oko CRLF; niti će da pokušava da izračuna ili štampa razliku za promene u ovoj datoteci kada pokrenete git show ili git diff u projektu.

Pregled razlike binarnih fajlova

Možete da iskoristite funkcionalnost Git atributa i za poređenje bianrnih datoteka. Ovo možete uraditi tako što ćete reći Gitu kako da konvertuje binarnu datoteku u tekstualni format koji se može uporediti pomoću uobičejnog alata za pregled razlika.

Prvo, iskoristićemo ovu tehniku da rešimo jedan od najveći problema koje čovečantsvo poznaje: kontrola verzija dokumenata pisanih u Majkrosoft Vordu. Svi znaju da je Vord jedan od najužasnijih editora koji postoje, i čudnovato je što ga svi i dalje koriste. Ako želite da kontrolišete verzije Vord dokumenata, možete da ih ubacite u Git repozitorijum i komitujete s vremena na vreme; ali kakve koristi imate od toga? Ako pokrenete git diff, dobićete nešto ovako:

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

Ne možete neposredno uporediti dve verzije osim ako ih ne čekautujete i ručno ih pregledate, zar ne? Ispostavlja se da ovo možete uraditi i pomoću Git atributa. Stavite sledeću liniju u datoteku .gitattributes.

*.docx diff=word

Ovo govori Gitu da svaki fajl koji odgovara šablonu (.docx) treba da koristi filter word kada probate da pregledate razlike koje sadrže promene. Ali šta je filter word? Morate da ga podesite. Ovde ćete konfigurisati Git da koristi program docx2txt koji konvertuje Vord dokumente u čitljive tekstualne fajlove, za koje ćete onda moći da pregledate razlike kako valja.

Prvo, treba da instalirate docx2txt; možete ga preuzeti sa http://docx2txt.sourceforge.net. Pratite uputstva iz datoteke INSTALL kako biste ga instalirali negde gde ga šel može naći. Zatim ćete napisati skriptu-omotač koja će konvertovati izlaz u format koji Git očekuje. Kreirajte datoteku negde u putanju koju ćete nazvati docx2txt, i dodajte sledeći sadržaj:

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

Ne zaboravite da nad njom pokrenete i chmod a+x. I za kraj, konfigurišite Git da koristi ovu skriptu.

$ git config diff.word.textconv docx2txt

Sada Git zna da, ako pokuša da prikaže razlike između dva snimka, a neki od fajlova se završava sa .docx, treba da sprovede ove fajlove kroz filter word, koji je definisan kao program docx2txt. Ovo će stvoriti tekstualne verzije Vord datoteka pre nego što se proba njihovo poređenje.

Evo primera: prvo poglavlje ove knjige je konvertovano u Vord format i komitovano na Git repozitorijum. Onda je dodat novi paragraf. Evo što prikazuje 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.

Git nam uspešno i nedvosmisleno govori da smo dodali string Testing: 1, 2, 3, što je tačno. Metoda nije savršena — izmene u formatiranju se ne bi pojavile ovde — ali nesumnjivo radi.

Još jedan zanimljiv problem koji možete rešiti na ovaj način jeste pregled razlika između slika. Jedan način da uradite ovo jeste da sprovedete slike kroz filter koji izvlači EXIF podatke — metapodatke koji se beleže u većini formata za slike. Ako preuzmete i instalirati program exiftool, možete da ga iskorisite da konvertujete slike u tekst o metapodacima, kako biste prilikom pregleda razlika barem videli tekstualnu prezentaciju promena:

$ echo '*.png diff=exif' >> .gitattributes
$ git config diff.exif.textconv exiftool

Ako promenite sliku u projektu i pokrenete git diff, videćete nešto ovako:

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

Lako možete da vidite da su se promenile dimenzije i veličina datoteke.

Proširenje ključnih reči

Developeri koji su navikli da koriste SVN i CVS često zahtevaju proširenje (ekspanziju) ključnih reči. Glavni problem sa ovime u Gitu jeste to što ne možete da modifikujete datoteku sa informacijom o komitu nakon što ste komitovali, zato što Git pre svega računa kontrolnu sumu. Ipak, možete ubrizgati tekst u datoteku kada je čekautujete i ponovo ga uklonite kada komitujete. Git atributi vam nude dva načina da uradite ovo.

Kod prvog možete automatski ubrizgati SHA-1 kontrolnu sumu bloba u polje $Id$ datoteke. Ako podesite ovaj atribut nad datotekom ili nad skupom datoteka, kada sledeći put čekautujete tu granu, Git će zameniti to polje sa SHA-1 kontrolnom sumom bloba. Bitno je da primetite da ovo nije SHA-1 komita, već samog bloba:

$ echo '*.txt ident' >> .gitattributes
$ echo '$Id$' > test.txt

Sledeći put kada čekautujete ovaj fajl, Git obrizgava SHA-1 bloba:

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

Ipak, taj rezultat ima ograničenu upotrebu. Ako ste koristili zamenu ključnih reči u CVS-u ili Subversion-u, mogli ste da uključite i pečat s datumom — SHA-1 nije toliko koristan jer je poprilično nasumičan i na osnovu poređenja ne možete da zaključite koji je noviji ili stariji.

Ispostavlja se da možete da napišete svoje filtere koji će raditi zamenu u datotekama pri komitovanju ili čekautovanju. Ovi filteri se zovu "čisti" i "zamrljani". U datoteci .gitattributes, možete podesiti filter za određene putanje i da onda podesite skripte koje će obratiti datoteke neposredno pre nego što se čekautuju ("zamrljani", pogledate "Zamrljani" filter se pokreće prilikom čekauta.) i neposredo pre nego što se dodaju na stejdž ("čisti", pogledajte "Čist" filter se pokreće prilikom stejdžovanja.). Ovi filteri se mogu podesiti tako da radi gomilu zanimljivih stvari.

``Zamrljani'' filter se pokreće prilikom čekauta.
Figure 144. "Zamrljani" filter se pokreće prilikom čekauta.
Čist
Figure 145. "Čist" filter se pokreće prilikom stejdžovanja.

Prvobitna komit poruka za ovo svojstvo daje jednostavan primer sprovođenja celog izvornog koda napisanog u C-u kroz program indent pre nego što se obavi komit. Možete da postavite ovo tako što ćete podesiti atribut-filter u datoteci .gitattributes koji će filtrirati datoteke .*c pomoću filtera indent.

*.c filter=indent

Onda recite Gitu šta filter indent radi za smudge i clean:

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

U ovom slučaju, kada komitujete datoteke koje odgovaraju šablonu *.c, Git će ih sprovesti kroz program za indentaciju pre nego što ih stejdžuje i onda će ih provesti kroz program cat pre nego što ih čekautujete nazad na disk. Program cart u suštini ne radi ništa: izbacuje iste podatke koje je dobio na ulazu. Ova kombinacija delotvorno filtrira sav izvorni kod u C-u kroz filter indent pre komitovanja.

Još jedan zanimljiv primer dobija proširenje ključne reči $Date$, u stilu RCS-a. Da biste ovo uradili kako treba, trebaće vam mala skripta koja uzima ime datoteke, pronalazi datum poslednjem komita za ovaj projekat, i ubacuje datum u datoteku. Evo male Rubi skripte koja radi to.

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

Sve što skripta radi jeste da uzme datum poslednjeg komita na osnovu komande git log, ubaci to u string $Date$ koji vidi iz stdin-a, i štampa rezultate — trebalo bi da bude dovoljno jednostavno da napravite skriptu u bilo kom drugom jeziku. Možete nazvati ovaj fajl expand_date i staviti ga u putanju. Sada treba da podesite filter u Gitu (zvaćemo ga dater) i reći mu da treba da koristi filter expand_date da zamrlja fajlove prilikom čekauta. Koristićemo ekspresiju u Perlu da to počistimo prilikom komita:

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

Ovaj isečak Perl koda sklanja sve što vidi u stringu $Date$, kako biste se vratili nazad na početno stanje. Sada, kada je filter spreman, možete da ga testirate podešavanjem datoteke sa ključnom rečju $Date$ i da onda namesite Git atribut za tu datoteku koji angažuje novi filter:

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

Ako komitujete te promene i onda opet čekautujete fajl, videćete da se ključna reč zamenila kako treba:

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

Sada vidite koliko ova tehnika može da bude moćna za proizvoljne aplikacije. Ipak, morate biti obazrivi, jer se datoteka .gitattributes komituje i deli sa ostatkom projekta, ali ne i drajver (u ovom slučaju dater), što znači da neće raditi svuda. Kada dizajnirate ove filtere, treba da ih napravite tako da elegantno degradiraju i da projekat i dalje funkcioniše kako valja.

Izvoz repozitorijuma

Podaci iz Git atributa vam dozovljavaju i da radite neke zanimljive stvari kada izvozite (eksportujete) ili arhivirate svoj projekat.

export-ignore

Možete reći Gitu da ne izvozi određene datoteke ili direktorijume kada generiše arhivu. Ako postoji poddirektorijum ili datotea koju ne želite da uključite u arhivu ali želite da čekautujete u projekat, možete da specficirate te datoteke pomoću atributa export-ignore.

Na primer, recimo da imate neke datoteke za testiranje u poddirektorijumu test/ — nema smsila uključivati ih u tarball projekta. Možete dodati sledeće linije u datoteci Git atributa:

test/ export-ignore

Sad, kada pokrenete git archive da biste kreirali tarball projekta, taj direktorijum neće biti uključen u arhivu.

export-subst

Kada eksportujete datoteke za razvoj možete da primenite formatiranje naredbe git log i obradu proširenja ključnih reči na određenu skup fajlova koje su obeležene atributom export-subst.

Na primer, ako želite da uključite datoteku sa imenom LAST_COMMIT u projekat, i da se metapodaci o poslednjem komitu automatski ubrizgaju kada pokrenete git archive, možete da datoteku postavite, na primer, ovako:

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

Kada pokrenete git archive, sadržaj arhiviranih datoteka će izgledati ovako:

$ 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

Ramene mogu da uključe, na primer, komit poruke i bilo kakve Git beleške, a git log može da uradi jednostavan prelom:

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

Rezultujuća arhiva je pogodna kao izdanje, ali kao i svaka druga izvezena arhiva nije pogodna za dalji razvitak na njoj.

Strategije spoja

Možete da koristite Git atribute i da kažete Gitu da koristi različite tehnike spajanja za različite datoteke iz projekta. Jedna veoma korisna opcija je da kažete Gitu da ne pokušava da spoji određene datoteke kada se dogode konflikti, već da uvek koristi vašu stranu umesto tuđe.

Ovo je korisno ako je grana u vašem projektu divergirala ili je specijalizovana, ali želite da možete da spojite promene nazad iz nje, i želite da ignorišete određene datoteke. Recimo da imate podešavanja za bazu podataka database.xml koja se razlikuje u dve grane, i želite da spojite u svoju granu, a da druga grana ne remeti datoetku vezanu za bazu podataka. Možete da postavite atribut ovako:

database.xml merge=ours

Zatim definišite lažnu ours startegiju na ovaj način:

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

Ako spojite u drugu granu, umesto da imate konflikte u datoteci database.xml, videćete nešto ovako:

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

U ovom slučaju, database.xml ostaje u onoj verziji koju ste prvobitno imali.