1 == Гросмейстерство Git ==
3 Тепер ви вже повинні вміти орієнтуватися в сторінках *git help* і розуміти майже все. Однак точний вибір команди, необхідної для вирішення конкретної проблеми, може бути виснажливим. Можливо, я збережу вам трохи часу: нижче наведені рецепти, які знадобилися мені в минулому.
5 === Релізи вихідних кодів ===
7 У моїх проектах Git управляє в точності тими файлами, які я збираюся архівувати і пускати в реліз. Щоб створити тарбол з вихідними кодами, я виконую:
9 $ git archive --format=tar --prefix=proj-1.2.3/ HEAD
13 У деяких проектах може бути трудомістким повідомляти Git про кожне додавання, видаленні та перейменування файлу. Замість цього ви можете виконати команди
18 Git прогляне файли в поточному каталозі і сам подбає про деталі. Замість другої команди add, виконайте `git commit -a`, якщо ви збираєтеся відразу зробити комміт. Дивіться * git help ignore *, щоб дізнатися як вказати файли, які повинні ігноруватися.
20 Ви можете виконати все це одним махом:
22 $ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove
24 Опції *-z* і *-0* запобігають невірну обробку файлових імен, що містять спеціальні символи. Оскільки ця команда додає ігноровані файли, ви можливо захочете використовувати опції `-x` або `-X`.
26 === Мій комміт занадто великий ===
28 Ви нехтували коммітамі занадто довго? Затято писали код і згадали про управління вихідними кодами тільки зараз? Внесли ряд незв'язаних змін, тому що це ваш стиль?
30 Немає причин для занепокоєння. Виконайте
34 Для кожної зробленої вами правки Git покаже змінену ділянку коду і запитає, чи повинна ця зміна потрапити в наступний комміт. Відповідайте "y" (так) або "n" (ні). У вас є й інші варіанти, наприклад відкласти вибір; введіть "?" Щоб дізнатися більше.
36 Коли закінчите, виконайте
40 для внесення саме тих правок, що ви вибрали ('буферизованих' змін). Переконайтеся, що ви не вказали опцію *-a*, інакше Git закоммітить всі правки.
42 Що робити, якщо ви змінили безліч файлів в багатьох місцях? Перевірка кожної окремої зміни стає обтяжливою рутиною. У цьому випадку використовуйте *git add -i*. Її інтерфейс не такий простий, але більш гнучкий. У декілька натискань можна додати або прибрати з буфера кілька файлів одночасно, або переглянути і вибрати зміни лише в окремих файлах. Як варіант, запустіть *git commit \--interactive*, яка автоматично зробить комміт коли ви закінчите.
44 === Індекс — буферна зона Git ===
46 До цих пір ми уникали знаменитого 'індексу' Git, але тепер ми повинні розглянути його, для пояснення вищесказаного. Індекс це тимчасовий буфер. Git рідко переміщує дані безпосередньо між вашим проектом і його історією. Замість цього Git спочатку записує дані в індекс, а вже потім копіює їх з індексу за місцем призначення.
48 Наприклад, *commit -a* насправді двоетапний процес. Спочатку зліпок поточного стану кожного з відстежуваних файлів поміщається в індекс. Потім зліпок, що знаходиться в індексі, записується в історію. Комміт без опції *-a* виконує тільки другий крок, і має сенс тільки після виконання команд, що змінюють індекс, таких як *git add*.
50 Зазвичай ми можемо не звертати уваги на індекс і робити вигляд, що взаємодіємо безпосередньо з історією. Але в даному випадку ми хочемо більш тонкого контролю, тому управляємо індексом. Ми поміщаємо зліпок деяких (але не всіх) наших змін в індекс, після чого остаточно записуємо цей акуратно сформований зліпок.
52 === Не втрачай "голови" ===
54 Тег HEAD подібний курсору, який зазвичай вказує на останній комміт, просуваючись з кожним новим коммітом. Деякі команди Git дозволяють переміщати цей курсор. Наприклад,
58 перемістить HEAD на три комміти назад. Тепер всі команди Git будуть працювати так, ніби ви не робили останніх трьох коммітів, хоча файли залишаться в поточному стані. У довідці описано кілька способів використання цього прийому.
60 Але як повернутися назад у майбутнє? Адже попередні комміти про нього нічого не знають.
62 Якщо у вас є SHA1 вихідної "голови", то:
66 Але припустимо, ви його не записували. Не турбуйтеся: для комнад такого роду Git зберігає оригінальну "голову" як тег під назвою ORIG_HEAD, і ви можете повернутися надійно і безпечно:
70 === Полювання за "головами" ===
72 Припустимо ORIG_HEAD недостатньо. Приміром, ви тільки що усвідомили, що допустили величезну помилку, і вам потрібно повернутися до давнього комміту в давно забутій гілці.
74 За замовчуванням Git зберігає комміти не менше двох тижнів, навіть якщо ви наказали знищити гілку, що їх містить. Проблема в знаходженні відповідного хешу. Ви можете проглянути всі значення хешів в `.git/objects` і методом проб та помилок знайти потрібний. Але є шлях значно легший.
76 Git записує кожен підрахований ним хеш комміта в `.git/logs`. У підкаталозі `refs` міститься повна історія активності на всіх гілках, а файл `HEAD` містить кожне значення хешу, яке коли-небудь приймав HEAD. Останній можна використовувати щоб знайти хеши коммітів на випадково обрубаних гілках.
78 Команда reflog надає зручний інтерфейс роботи з цими журналами. Використовуйте
82 Замість копіювання хешів з reflog, спробуйте
84 $ git checkout "@{10 minutes ago}"
86 Чи зробіть чекаут п'ятого з кінця з відвіданих коммітів за допомогою
90 Дивіться розділ ``Specifying Revisions'' в *git help rev-parse* для додаткової інформації.
92 Ви можете захотіти подовжити відстрочку для коммітів, приречених на видалення. Наприклад,
94 $ git config gc.pruneexpire "30 days"
96 означає, що комміти, які видаляються, будуть остаточно зникати тільки після 30 днів і після запуску *git gc*.
98 Також ви можете захотіти відключити автоматичний виклик *git gc*:
100 $ git config gc.auto 0
102 У цьому випадку комміти будуть видалятися тільки коли ви будете запускати *git gc* вручну.
104 === Git як основа ===
106 Дизайн Git, в істинному дусі UNIX, дозволяє легко використовувати його як низькорівневий компонент інших програм: графічних та веб-інтерфейсів; альтернативних інтерфейсів командного рядка; інструментів управління патчами; засобів імпорту або конвертації, і так далі. Багато команд Git насправді - скрипти, які стоять на плечах гігантів. Невеликим доопрацюванням ви можете переробити Git на свій смак.
108 Найпростіший трюк — використання аліасів Git для скорочення часто використовуваних команд:
110 $ git config --global alias.co checkout
111 $ git config --global --get-regexp alias # відображає поточні аліаси
113 $ git co foo # те ж саме, що і 'git checkout foo'
115 Інший приклад: можна виводити поточну гілку в запрошенні командного рядка або заголовку вікна терміналу. Запуск
117 $ git symbolic-ref HEAD
119 виводить назву поточної гілки. На практиці ви швидше за все захочете прибрати "refs/heads/" і повідомлення про помилки:
121 $ git symbolic-ref HEAD 2> /dev/null | cut -b 12-
123 Підкаталог +contrib+ — це ціла скарбниця інструментів, побудованих на Git. З часом деякі з них можуть ставати офіційними командами. В Debian та Ubuntu цей каталог знаходиться у +/usr/share/doc/git-core/contrib+.
125 Один популярний інструмент з цього каталогу — +workdir/git-new-workdir+. Цей скрипт створює за допомогою символічних посилань новий робочий каталог, який має спільну історію з оригінальним сховищем:
127 $ git-new-workdir існуюче/сховище новий/каталог
129 Новий каталог і файли в ньому можна сприймати як клон, з тією різницею, що два дерева автоматично залишаються синхронізованими зважаючи на спільну історію. Немає необхідності в merge, push і pull.
131 === Ризиковані трюки ===
133 Нинішній Git робить випадкове знищення даних дуже складним.
134 Але якщо ви знаєте, що робите, ви можете обійти захист для розповсюджених команд.
136 *Checkout*: Наявність незакомміченних змін перериває виконання checkout. Щоб перейти до потрібного комміту, навіть знищивши свої зміни, використовуйте прапор змушування (force) *-f*:
138 $ git checkout -f HEAD^
140 З іншої сторони, якщо ви вкажете checkout конкретні шляхи, перевірки на безпеку не буде: вказані файли мовчки перезапишуть. Будьте обережні при такому використанні checkout.
142 *Reset*: скидання також переривається при наявності незакомміченних змін. Щоб змусити його спрацювати, запустіть
144 $ git reset --hard 1b6d
146 *Branch*: Видалення гілки припиниться, якщо воно призвело б до втрати змін. Для примусового видалення введіть
148 $ git branch -D мертва_гілка # замість -d
150 Аналогічно, спроба перезапису гілки шляхом переміщення буде перервана, якщо може призвести до втрати даних. Для примусового переміщення гілки введіть
152 $ git branch -M джерело ціль # замість -m
154 У відмінності від checkout і reset, ці дві команди дають відстрочку у видаленні даних. Зміни залишаються в каталозі.git і можуть бути повернуті відновленням потрібного хешу з `.git/logs` (дивіться вище розділ "Полювання за головами"). За замовчуванням вони будуть зберігатися принаймні два тижні.
156 *Clean*: Деякі команди можуть не спрацювати через побоювання пошкодити невідслідковувані файли. Якщо ви впевнені, що все невідслідковувані файли і каталоги не потрібні, то безжально видаляйте їх командою
160 Наступного разу ця прикра команда спрацює!
162 === Запобігаємо поганим коммітам ===
164 Дурні помилки забруднюють мої сховища. Найжахливіше це проблема відсутніх файлів, викликана забутим *git add*.
166 Приклади менш серйозних проступків: завершальні пропуски і невирішені конфлікти злиття. Незважаючи на нешкідливість, я не хотів би, щоб це з'являлося в публічних записах.
168 Якби я тільки поставив захист від дурня, використовуючи _хук_, який би попереджав мене про ці проблеми:
171 $ cp pre-commit.sample pre-commit # В старих версіях Git: chmod +x pre-commit
173 Тепер Git скасує комміт, якщо виявить зайві пробіли або невирішені конфлікти.
175 Для цього керівництва я в кінці кінців додав наступне в початок хука *pre-commit*, щоб захиститися від своєї неуважності:
177 if git ls-files -o | grep '\.txt$'; then
178 echo ПЕРЕРВАНО! Невідслідковувані .txt файли.
182 Хукі підтримуються кількома різними операціями Git, дивіться *git help hooks*. Ми використовували приклад хука *post-update* раніше, при обговоренні використання Git через http. Він запускався при кожному переміщенні голови. Простий скрипт post-update оновлює файли, які потрібні Git для зв'язку через засоби повідомлення такі як HTTP.