Git
Chapters ▾ 2nd Edition

8.2 Налаштування Git - Атрибути Git

Атрибути Git

Деякі з цих налаштувань також можуть бути встановлені для окремих шляхів, щоб Git застосовував їх лише для піддиректорій чи підмножини файлів. Ці налаштування для окремих шляхів називаються в Git атрибутами та встановлюються або у файлі .gitattributes в одній з ваших директорій (зазвичай в корені проекту), або у файлі .git/info/attributes, якщо ви не бажаєте зберігати файл атрибутів у коміті вашого проекту.

За допомогою атрибутів ви можете, серед іншого, встановлювати окремі стратегії злиття для конкретних файлів або директорій вашого проекту, казати Git як отримувати різницю між нетекстовими файлами, або встановити фільтр вмісту перед його отриманням з Git або додаванням до Git. У цій секції, ви дізнаєтесь про деякі атрибути, які ви можете встановити для шляхів вашого проекту Git, та побачите декілька прикладів практичного використання цього функціоналу.

Двійкові файли

Одним файним фокусом, для якого ви можете використати атрибути Git, це для повідомлення Git які файли є двійковими (у випадках, коли іншим чином цього не можна зʼясувати) та надати Git спеціальні інструкції щодо поводження з цими файлами. Наприклад, деякі текстові файли можуть бути згенеровані машиною та бути непридатними для отримання різниці, у той час як різницю між деякими двійковими файлами можна отримати. Ви побачите, як вказати Git які з них є якими.

Визначення двійкових файлів

Деякі файли виглядають як текстові, проте для будь-якого використання та призначення, їх варто вважати двійковими даними. Наприклад, проекти Xcode на Mac містять файли, які закінчуються на .pbxproj, які є набором даних JSON (текстовий формат JavaScript даних) записаний на диск середовищем розробки, в якому записано налаштування збірки тощо. Хоча технічно це текстовий файл (адже містить лише UTF-8), ви не бажаєте щоб його сприймали таким, оскільки це легковага база даних – ви не можете зливати вміст, якщо дві людини змінять її, та дивитись різницю між ними марно. Цей файл призначений для використання машиною. Якщо стисло, ви бажаєте, щоб цей файл сприймався як двійковий.

Щоб сказати Git сприймати всі файли pbxproj як двійкові дані, додайте наступний рядок до файлу .gitattributes:

*.pbxproj binary

Тепер, Git не буде намагатись конвертувати чи виправляти проблеми зі символами нового рядка, ані обчислювати та виводити різницю для змін у цьому файли при виконанні git show чи git diff у вашому проекті.

Порівняння двійкових файлів

Ви також можете використати функціонал атрибутів Git для ефективного порівняння двійкових файлів. Для цього треба сказати Git, як перетворити ваші двійкові дані на текст, який може порівняти звичайний diff.

Спершу, ви використаєте цю техніку для вирішення однієї з найбільш дратівних проблем, що їх знає людство: керування версіями документів Microsoft Word. Всі знають, що Word найжахливіший з існуючих редакторів, проте, на диво, усі досі ним користуються. Якщо ви бажаєте керувати версіями документи Word, ви можете додати їх до сховища Git та подеколи зберігати в комітах; проте яка з того користь? Якщо виконати 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

Ви не можете напряму порівняти дві версії, хіба отримаєте обидві та продивитесь їх вручну, чи не так? Виявляється, ви можете це зробити доволі добре за допомогою атрибутів Git. Додайте наступний рядок до свого файлу .gitattributes:

*.docx diff=word

Це означає, що для будь-якого файлу, що відповідає шаблону (.docx), Git має використати фільтр “word”, коли ви намагаєтесь продивитись різницю, яка містить зміни. Що це за фільтр “word”? Вам доведеться його налаштувати. Тут ви зробите так, щоб Git використовував програму docx2txt для перетворення документів Word на читабельні текстові файли, які він потім відповідно порівняє.

Спершу, треба встановити docx2txt; ви можете завантажити його за адресою http://docx2txt.sourceforge.net. Дотримуйтесь інструкцій з файлу INSTALL, щоб покласти його кудись, де оболонка (shell) зможе його знайти. Далі, ви напишете скрипт-обгортку, яка перетворює вивід на формат, який очікує Git. Створіть файл, що знаходиться десь у вашому path та називається docx2txt та додайте туди такий вміст:

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

Не забудьте зробити chmod a+x з цим файлом. Нарешті, ви можете налаштувати Git, щоб він використовував цей скрипт:

$ git config diff.word.textconv docx2txt

Тепер Git знає, що якщо йому треба порівняти два відбитки, та якийсь з них закінчується на .docx, то Git має передати ці два файли фільтру “word”, який визначено як програму docx2txt. Це призводить до гарних текстових версій ваших файлів Word перед спробами порівняти їх.

Ось приклад: Розділ 1 цієї книжки перетворили на формат Word та зберегли в коміті сховища Git. Потім додали новий параграф. Ось що покаже 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 успішно та стисло доповідає нам, що ми додали рядок “Testing: 1, 2, 3.”, як і було насправді. Це не ідеально – зміни формату тут не буде показано – проте це безперечно працює.

Ще одна цікава проблема, яку ви можете вирішити таким чином, повʼязана з порівнянням зображень. Один із способів це зробити — пропустити зображення через фільтр, який видобуває їхню інформацію EXIF – метадані, що записані у більшості форматів зображень. Якщо ви завантажите та встановите програму exiftool, то зможете використати її для перетворення ваших зображень на текст про метадані, отже принаймні diff покаже вам текстове представлення будь-яких впроваджених змін. Запишіть наступний рядок до файлу .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, часто бажають того ж від Git. Головна проблема з цим у Git полягає в тому, що ви не можете надати інформацію про коміт після збереження коміту, оскільки Git спочатку обчислює контрольну суму файлу. Втім, ви можете додати текст до файлу при отриманні та вилучити його знову перед доданням до коміту. Атрибути Git пропонують два способи зробити це.

Спочатку, ви можете додати контрольну суму SHA-1 блобу до рядка $Id$ у файлі автоматично. Якщо встановити цей атрибут на файлі чи низці файлів, то при наступному переключенні до тієї гілки, Git замінить це поле на SHA-1 блобу. Важливо звернути увагу на те, що це не SHA-1 коміту, а самого блобу. Додайте наступний рядок до файлу .gitattributes:

*.txt ident

Додайте посилання $Id$ до тестового файлу:

$ echo '$Id$' > test.txt

Коли ви наступного разу отримаєте (check out) цей файл, Git додасть SHA-1 блобу:

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

Втім, цей результат має обмежене використання. Якщо ви користувались заміною ключових слів у CVS чи Subversion, то могли додати дату – SHA-1 не таке корисне, оскільки воно є цілком випадкове та за його допомогою не можна сказати відразу, який SHA-1 старший чи новіший.

Виявляється, ви можете написати свої власні фільтри, щоб робити заміни при отриманні/збереженні в коміті файлів. Вони називаються фільтри clean'' та ``smudge''. У файлі `.gitattributes ви можете задати фільтр для окремих шляхів, а потім налаштувати скрипти, які оброблятимуть файли перед отриманням (“smudge”, дивіться Фільтр “smudge” виконується під час отримання.) та перед індексуванням (“clean”, дивіться Фільтр “clean” виконується, коли зміни індексуються.). Ці фільтри можуть робити всілякі втішні речі.

Фільтр ``smudge'' виконується під час отримання.
Figure 143. Фільтр “smudge” виконується під час отримання.
Фільтр ``clean'' виконується
Figure 144. Фільтр “clean” виконується, коли зміни індексуються.

В оригінальному повідомленні коміту з цією функцією міститься простий приклад виконання програми indent на всьому вашому коді C перед збереженням коміту. Щоб це зробити, треба встановити атрибут filter у файлі .gitattributes для файлів *.c у значення “indent”:

*.c filter=indent

Потім, скажіть Git, що фільтр “indent” робить для smudge (забруднити) та clean (очистити):

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

У цьому випадку, коли ви зберігаєте в коміті файлі, які збігаються з *.c, Git виконає на них програму indent перед індексуванням, а потім виконає на них програму cat перед отриманням їх на диск. Програма cat у підсумку нічого не робить: вона видає ті самі дані, що й отримує. Ця комбінація призводить до фільтрації всього вихідного коду C через indent перед збереженням коміту.

Ще один цікавий приклад: розкриття ключового слова $Date$ у стилі RCS. Щоб правильно це зробити, потрібен маленький скрипт, який приймає ім’я файлу, знаходить дату останнього коміту цього проекту, та вставляє дату до файлу. Ось маленький скрипт Ruby, який це робить:

#! /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 та покласти його до вашого path. Тепер, вам треба налаштувати фільтр в Git (назвіть його dater) та сказати йому використовувати фільтр expand_date щоб забруднити ваші файли під час отриманні. Ви використаєте вираз Perl аби очистити їх під час збереження коміту:

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

Цей клаптик Perl прибирає будь-що всередині рядка $Date, щоб повернутись до початкового стану. Тепер, коли ваш фільтр готовий, можна випробувати його. Для цього треба потім налаштувати атрибут для цього файлу, що залучає цей новий фільтр і створити файл з ключовим словом $Date$:

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

Якщо зберегти в коміті ці зміни та отримати файл знову, то ключове слово буде правильно замінено:

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

Ви можете бачити, наскільки могутньою може бути ця техніка для нетипових застосувань. Втім, ви маєте бути обережними, адже файл .gitattributes зберігається в коміті та передається разом з проектом, проте виконавець (у даному випадку, dater) — ні, отже, не буде працювати всюди. Під час розробки цих фільтрів, щоб вони були в змозі зазнавати невдачі достатньо зграбно та щоб з проектом після цього можна було нормально працювати.

Експортування вашого сховища

Дані атрибутів Git також дозволяють вам робити деякі цікаві речі під час експортування архіву вашого проекту.

export-ignore

Ви можете сказати Git не експортувати деякі файли чи директорії, коли він генерує архів. Якщо існує піддиректорія або файл, які ви не бажаєте включати до вашого архіву, проте вони мають бути присутніми в проекті, ви можете зазначити ці файли за допомогою атрибута export-ignore.

Наприклад, припустимо, що у вас є деякі файли для тестів у піддиректорії test/, і включати їх до експортованого архіву вашого проекту не має ніякого сенсу. Ви можете додати наступний рядок до файлу атрибутів Git:

test/ export-ignore

Тепер, коли ви виконуєте git archive для створення архіву вашого проекту, цю директорію не буде включено до архіву.

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, також 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.

Отриманий архів є придатним для розгортання, проте, як і будь-який експортований архів, не є придатним для подальшої розробки.

Стратегії злиття

Атрибути Git також можна використовувати, щоб сказати Git використовувати інші стратегії злиття для окремих файлів проекту. Дуже корисною є опція, що дозволяє сказати Git не намагатись злити окремі файли, коли в них є конфлікти, а натомість використовувати ваш варіант замість інших.

Це корисно, якщо гілка вашого проекту розійшлася або спеціалізована, проте ви бажаєте бути в змозі зливати з неї зміни, та бажаєте ігнорувати деякі файли. Припустимо, у вас є налаштування бази даних у файлі 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 залишається, хай якої версії він був, незмінним.