Git
Chapters ▾ 2nd Edition

7.10 Інструменти Git - Зневадження з Git

Зневадження з Git

Хоча функціонал Git переважно стосується керування версій, він також пропонує декілька команд, що допомагають зневаджувати код вашого проекту. Через те, що Git розроблено для роботи з файлами майже будь-якого вмісту, ці інструменти доволі загальні, проте часто можуть допомогти вам відстежити ваду (bug) чи винуватця, коли щось пішло не так.

Анотація файла

Якщо ви шукаєте ваду у своєму коді та бажаєте знати, коли вона зʼявилася та чому, анотація файлів часто є найкращим інструментом. Він показує вам, який коміт востаннє редагував кожен рядок будь-якого файла. Отже, якщо ви бачите, що метод у вашому коді має помилку, ви можете анотувати файл за допомогою git blame, щоб визначити, у якому коміті цей рядок зʼявився.

У наступному прикладі для визначення того, хто і в якому коміті змінив чи створив рядки файлу Makefile`верхнього рівня ядра Linux, використовується `git blame. Більш того, використовується опція -L щоб обмежити вивід анотації до рядків з 69 до 82:

$ git blame -L 69,82 Makefile
b8b0618cf6fab (Cheng Renquan  2009-05-26 16:03:07 +0800 69) ifeq ("$(origin V)", "command line")
b8b0618cf6fab (Cheng Renquan  2009-05-26 16:03:07 +0800 70)   KBUILD_VERBOSE = $(V)
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 71) endif
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 72) ifndef KBUILD_VERBOSE
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 73)   KBUILD_VERBOSE = 0
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 74) endif
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 75)
066b7ed955808 (Michal Marek   2014-07-04 14:29:30 +0200 76) ifeq ($(KBUILD_VERBOSE),1)
066b7ed955808 (Michal Marek   2014-07-04 14:29:30 +0200 77)   quiet =
066b7ed955808 (Michal Marek   2014-07-04 14:29:30 +0200 78)   Q =
066b7ed955808 (Michal Marek   2014-07-04 14:29:30 +0200 79) else
066b7ed955808 (Michal Marek   2014-07-04 14:29:30 +0200 80)   quiet=quiet_
066b7ed955808 (Michal Marek   2014-07-04 14:29:30 +0200 81)   Q = @
066b7ed955808 (Michal Marek   2014-07-04 14:29:30 +0200 82) endif

Зауважте, що перше поле — це часткова SHA-1 коміту, який востаннє змінював цей рядок. Наступні два поля є отриманими з цього коміту значеннями: імʼя автора та дата створення цього коміту — отже ви легко можете побачити, хто та коли редагував рядки. Після цього йде номер рядка та зміст файла. Також зверніть увагу на рядки комітів ^1da177e4c3f4: вони означають, що ці рядки були в початковому коміті сховища і відтоді не змінювалися. Цей коміт був першим, коли файл було додано до проекту, та ці рядки відтоді не змінювались. Це крапельку дивно, адже тепер ви бачили принаймні три різних значення, в яких Git використовує ^ щоб змінити SHA-1 коміту, проте саме це в даному випадку цей символ і означає.

Інша чудова річ у Git: він не слідкує за перейменуваннями файлів явно. Він записує знімки та потім намагається збагнути, що було перейменовано сам, вже після перейменування. Один з цікавих наслідків цього полягає в тому, що ви також можете попросити Git знайти всілякі переміщення коду. Якщо передати -C до git blame, Git проаналізує файл, що ви його анотуєте, та спробує зрозуміти, звідки зʼявилися частини коду, якщо вони були скопійовані з іншого місця. Наприклад, припустімо ви переробляєте файл GITServerHandler.m на декілька файлів, один з яких має назву GITPackUpload.m. Якщо виконати blame на файлі GITPackUpload.m з опцією -C, ви можете побачити, звідки зʼявились секції коду:

$ git blame -C -L 141,153 GITPackUpload.m
f344f58d GITServerHandler.m (Scott 2009-01-04 141)
f344f58d GITServerHandler.m (Scott 2009-01-04 142) - (void) gatherObjectShasFromC
f344f58d GITServerHandler.m (Scott 2009-01-04 143) {
70befddd GITServerHandler.m (Scott 2009-03-22 144)         //NSLog(@"GATHER COMMI
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 145)
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 146)         NSString *parentSha;
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 147)         GITCommit *commit = [g
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 148)
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 149)         //NSLog(@"GATHER COMMI
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 150)
56ef2caf GITServerHandler.m (Scott 2009-01-05 151)         if(commit) {
56ef2caf GITServerHandler.m (Scott 2009-01-05 152)                 [refDict setOb
56ef2caf GITServerHandler.m (Scott 2009-01-05 153)

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

Щоб анотування файла допомогло, треба щоб ви спочатку знали, де проблема. Якщо ви не знаєте, що зламалося, і було зроблено десятки або сотні комітів відтоді як ви точно знали, що код працював, ви напевно звернетесь до bisect по допомогу. Команда bisect виконує двійковий пошук у вашій історії комітів, щоб допомогти вам визначити якомога швидше, який коміт спричинив проблему.

Скажімо, ви щойно виклали ваш готовий код до виробничого середовища, та отримуєте звіти вад про щось, чого не було у вашому середовищі розробки, і ви гадки не маєте, чому код так себе поводить. Ви повертаєтесь до свого коду, і виявляється, що ви можете відтворити проблему, проте не можете зрозуміти, що працює не так. Ви розділяєте свій код навпіл (bisect), щоб зʼясувати це. Спочатку виконуємо git bisect start, щоб розпочати процес, потім скористаємося git bisect bad, щоб повідомити системі, що поточний коміт зламаний. Потім, ви маєте повідомити bisect, коли востаннє був відомий гарний стан за допомогою git bisect good <гарний коміт>:

$ git bisect start
$ git bisect bad
$ git bisect good v1.0
Bisecting: 6 revisions left to test after this
[ecb6e1bc347ccecc5f9350d878ce677feb13d3b2] error handling on repo

Git зʼясував, що було зроблено приблизно 12 комітів між позначиним як останній гарний коміт (v1.0) та поточною поганою версією, та отримав (checked out) середній для вас. Наразі, ви можете виконати ваші тести, щоб дізнатись, чи існує проблема в цьому коміті. Якщо існує, то вона виникла десь до цього середнього коміту; якщо ж ні, то проблема виникла після середнього коміту. Виявляється, що тут проблеми немає, і ми кажемо про це Git за допомогою git bisect good, та продовжуємо свою подорож:

$ git bisect good
Bisecting: 3 revisions left to test after this
[b047b02ea83310a70fd603dc8cd7a6cd13d15c04] secure this thing

Тепер ви на іншому коміті, посередині між щойно протестованим та вашим поганим комітом. Ви виконуєте тести знов, та зʼясовуєте, що цього разу коміт зламаний, отже ви кажете про це Git за допомогою git bisect bad:

$ git bisect bad
Bisecting: 1 revisions left to test after this
[f71ce38690acf49c1f3c9bea38e09d82a5ce6014] drop exceptions table

Цей коміт у порядку, і тепер Git має всю необхідну інформацію, щоб визначити, де виникла проблема. Він повідомляє вам SHA-1 першого поганого коміту, та показує деяку інформацію про коміт та які файли було змінено в цьому коміті, щоб ви могли дізнатися, що такого сталося, що могло спричинити цю ваду:

$ git bisect good
b047b02ea83310a70fd603dc8cd7a6cd13d15c04 is first bad commit
commit b047b02ea83310a70fd603dc8cd7a6cd13d15c04
Author: PJ Hyett <pjhyett@example.com>
Date:   Tue Jan 27 14:48:32 2009 -0800

    secure this thing

:040000 040000 40ee3e7821b895e52c1695092db9bdc4c61d1730
f24d3c6ebcfc639b1a3814550e62d60b8e68a8e4 M  config

Коли ви завершили, треба виконати git bisect reset, щоб повернути HEAD туди, де ви були спочатку, інакше ви опинитесь у дивному становищі:

$ git bisect reset

Це могутній інструмент, що може допомогти перевірити сотні комітів на наявність вади за хвилини. Насправді, якщо у вас є скрипт, що поверне 0, якщо проект у гарному стані, та не 0, якщо у поганому, то можливо повністю автоматизувати git bisect. Спершу, ви знову повідомляєте проміжок bisect: задаєте відомі гарний та поганий коміти. Це можна зробити, якщо додати їх до команди bisect start, якщо бажаєте: наведіть відомий поганий коміт першим, а відомий гарний коміт другим:

$ git bisect start HEAD v1.0
$ git bisect run test-error.sh

Це автоматично виконає test-error.sh на кожному отриманому коміті, доки Git не знайде перший зламаний коміт. Ви також можете виконати щось на кшталт make або make test чи що завгодно, що виконує автоматичні тести для вас.