Git предоставляет несколько инструментов, которые помогут вам найти и устранить проблемы в ваших проектах. Так как Git рассчитан на работу с проектом почти любого типа, эти инструменты имеют довольно обобщённые возможности, но часто они могут помочь вам отловить ошибку или её виновника.
Если вы обнаружили ошибку в вашем коде и хотите знать, когда она была добавлена и почему, то в большинстве случаев аннотация файла будет лучшим инструментом для этого.
С помощью неё для любого файла можно увидеть, каким коммитом последний раз изменяли каждую из строк.
Поэтому если вы видите, что некоторый метод в вашем коде работает неправильно, вы можете с помощью команды git blame
снабдить файл аннотацией, и таким образом увидеть, когда каждая строка метода была изменена последний раз и кем.
В следующем примере используется git blame
, чтобы определить, какой коммит и коммиттер отвечал за строки в Makefile
ядра Linux верхнего уровня и, кроме того, использует параметр -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 сумма последнего коммита, который изменял соответствующую строку.
Следующими двумя полями являются значения, извлечённые из этого коммита — имя автора и время создания коммита — таким образом, вы можете легко увидеть кто изменял строку и когда.
После этого следуют номер строки и содержимое файла.
Обратите внимание на строки со значением ^4832fe2
в поле коммита, так обозначаются те строки, которые были в первом коммите этого файла.
Этот коммит был сделан, когда данный файл был впервые добавлен в проект и с тех пор эти строки не были изменены.
Немного сбивает с толку то, что вы уже видели в Git, по крайней мере, три различных варианта использования символа ^
для изменения SHA-1 коммита, в данном случае этот символ имеет такое значение.
Другая отличная вещь в Git — это то, что он не отслеживает явно переименования файлов (пользователю не нужно явно указывать какой файл в какой был переименован).
Он сохраняет снимки и уже после выполнения самого переименования неявно попытается выяснить, что было переименовано.
Одна из интересных возможностей, вытекающих из этого — это то, что вы также можете попросить Git выявить перемещения кода всех других видов.
Если передать опцию -C
команде git blame
, Git проанализирует аннотируемый файл и попытается выяснить откуда изначально появились фрагменты кода, если они, конечно же, были откуда-то скопированы.
Например, предположим при реорганизации кода в файле GITServerHandler.m
вы разнесли его по нескольким файлам, один из которых GITPackUpload.m
.
Вызывая git blame
с опцией -C
для файла GITPackUpload.m
, вы можете увидеть откуда изначально появились разные фрагменты этого файла.
$ 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 сообщает вам первый коммит, в котором эти строки были написаны, даже если это было сделано в другом файле.
Аннотирование файла помогает, если вы знаете, где находится проблема и можете начать исследование с этого места.
Если вы не знаете, что сломано, а с тех пор как код работал, были сделаны десятки или сотни коммитов, вы вероятно воспользуетесь командой git bisect
.
Эта команда выполняет бинарный поиск по истории коммитов для того, чтобы помочь вам как можно быстрее определить коммит, который создал проблему.
Допустим, вы только что развернули некоторую версию вашего кода в боевом окружении и теперь получаете отчёты о некоторой ошибке, которая не возникала в вашем разработческом окружении, и вы не можете представить, почему код ведёт себя так.
Вы возвращаетесь к вашему коду и выясняете, что можете воспроизвести проблему, но всё ещё не понимаете, что работает неверно.
Вы можете воспользоваться бинарным поиском, чтобы выяснить это.
Во-первых, выполните команду git bisect start
для запуска процесса поиска, а затем используйте git bisect bad
, чтобы сообщить Git, что текущий коммит сломан.
Затем, используя git bisect good [good_commit]
, вы должны указать, когда было последнее известное рабочее состояние:
$ 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), и текущим плохим коммитом, и выгрузил вам один из середины.
В этот момент вы можете запустить ваши тесты, чтобы проверить присутствует ли проблема в этом коммите.
Если это так, значит она была внесена до выгруженного промежуточного коммита, если нет, значит проблема была внесена после этого коммита.
Пусть в данном коммите проблема не проявляется, вы сообщаете об этом 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 <[email protected]>
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 если проект находится в рабочем состоянии и любое другое число в обратном случае, то вы можете полностью автоматизировать git bisect
.
Сперва, вы снова сообщаете границы бинарного поиска, указывая известные плохие и хорошие коммиты.
Вы можете сделать это, передавая их команде bisect start
— первым аргументом известный плохой коммит, а вторым известный хороший коммит:
$ git bisect start HEAD v1.0
$ git bisect run test-error.sh
Это приведёт к автоматическому выполнению test-error.sh
на каждый выгруженный коммит до тех пор, пока Git не найдёт первый сломанный коммит.
Вы также можете использовать что-то вроде make
или make tests
, или что-то ещё, что у вас есть для запуска автоматизированных тестов.