Поиск регрессии с bisect
Иногда известно только то, что раньше каталог работал, а теперь ссылка сломана. Последний коммит может касаться README, отступов или другой страницы. Возвращать его наугад бессмысленно: нам нужно найти первый снимок, в котором появилось конкретное нежелательное свойство.
Для этого рассмотрим git bisect. В сценарии lesson-11 архива задана линейная история из шести записей. Результаты получены редакционным разбором подготовленных файлов; поиск и команды не выполнялись. Во время упражнения читатель сам открывает выбранные версии и отмечает наблюдение.
Один вопрос для всех снимков
Сформулируем критерий: «Ссылка JavaScript в content/courses.md содержит постоянный адрес /courses/javascript/». Хороший снимок содержит этот адрес; плохой — /courses/javascript-next/. По условию лаборатории новый путь отсутствует. Мы оцениваем ровно одно свойство, поэтому переименование заголовка, изменение цвета и заметка README не влияют на ответ.
Обозначим записи от B0 до B5. В B0 правильный адрес; B1 уточняет README, B2 добавляет рекомендацию в Markdown, B3 меняет адрес ошибочно, B4 меняет CSS, B5 добавляет заметку о двух курсах. После B3 ошибка больше не исчезает. Такое постоянство свойства делает двоичный поиск уместным.
B0 — B1 — B2 — B3 — B4 — B5 main
good first bad bad
Это схема, не буквальный вывод Git. Реальные идентификаторы создаются только при ручной подготовке лаборатории. Сначала прочитайте лог и запишите хеш начальной и последней записей. Чтобы не перепутать выбранные точки, в сценарии предусмотрены локальные имена lab-good и lab-bad для концов диапазона.
Критерий не должен зависеть от личного впечатления «страница стала хуже». Для такой оценки два человека могут дать разные ответы одной версии. Здесь мы читаем точное значение URL. Для настоящей регрессии также нужно стабилизировать исходные данные, окружение и действие пользователя, иначе поиск будет сужать случайные различия.
Начало и промежуточный снимок
Перед поиском нужен чистый рабочий каталог. В нашей лаборатории все шесть подготовительных изменений уже записаны, а main указывает на B5. Затем запускается ручной поиск:
git bisect start
git bisect bad lab-bad
git bisect good lab-good
Git выберет промежуточный коммит. Мы не задаём в статье обязательный первый выбранный хеш: существенны прочитанное содержимое и последовательное сужение границ. Документация bisect объясняет выбор между известными хорошей и плохой точками. При чтении ориентируйтесь на свою текущую запись и объявленный критерий.
Откройте content/courses.md, найдите строку JavaScript и дайте одну отметку. Если путь правильный, используется git bisect good; если ошибочный — git bisect bad. После каждой отметки Git может переключить рабочие файлы на другой снимок. Снова читайте файл; не переносите ответ от предыдущей версии автоматически.
git show HEAD:content/courses.md
git bisect good
Этот блок показывает хороший вариант ответа. Для снимка с новым адресом последняя команда заменяется на git bisect bad. Выполнение обеих отметок подряд без нового наблюдения испортило бы классификацию: они относятся к текущей выбранной версии, а не перечисляют настройки поиска.
Что означает найденная граница
Для нашей заданной линии ожидаемый результат — B3, первый коммит с неправильным URL. Он отличается от последнего коммита B5, хотя именно последнюю версию вы могли увидеть при обнаружении ошибки. Заметка README лишь находится после причины в истории. Это различие показывает пользу поиска по наблюдаемому свойству.
Когда Git сообщает найденную запись, прочитайте её различие. В нашей модели заменена одна ссылка. Ещё раз сравните смысл изменения с критериями: правильный путь раньше существовал, новая страница не подготовлена, значит переход разорван. Само нахождение границы не является исправлением и не создаёт обратный коммит.
После чтения закончите режим поиска:
git bisect log
git bisect reset
git status --short
reset в составе bisect завершает поиск и возвращает исходное положение. Это другой контекст, чем самостоятельные способы перемещения истории. В лаборатории ожидается снова main на B5, поэтому рабочая ссылка после завершения ещё неправильная. Наш результат — знание причины; исправление оформляется отдельной задачей.
Не начинайте во время поиска несвязанный коммит исправления в случайно выбранном снимке. Промежуточные версии просматриваются в особом состоянии, и их назначение — сравнение. Сначала закончите исследование, затем вернитесь в явную рабочую ветку. Так вы отделите доказательство происхождения ошибки от подготовки решения.
Результат поиска полезно сохранить вместе с исходным вопросом. Запись «найден B3» без критерия теряет смысл: тот же коммит мог добавить правильное пояснение и одновременно изменить ссылку. Для нашего урока важна именно первая версия неправильного адреса. В заметке укажите обе границы, найденную запись и строку, которая нарушила договор. Это позволит отдельно обсудить возврат ссылки и сохранение полезного текста. Если коллега повторит исследование по другому свойству, например цвету карточки, граница будет другой. Разные ответы не обязательно противоречат друг другу: они могут относиться к разным вопросам. Поэтому краткое объяснение критерия является частью результата, а не необязательным приложением к идентификатору коммита.
Непроверяемая версия
Иногда выбранный снимок невозможно оценить: нужный документ временно отсутствует, зависимости не доступны или программа не запускается по другой причине. В таком случае нельзя считать его плохим по нашему критерию лишь из-за невозможности наблюдения. Для пропуска существует git bisect skip. Несколько пропусков около границы могут оставить несколько возможных первых проблемных записей.
В нашем сценарии каждый снимок содержит читаемый Markdown, поэтому пропуск не нужен. Это сознательное упрощение: мы изучаем поиск, а не настройку окружения для старых выпусков. Команды автоматического запуска проверки тоже не предлагаются. Статья содержит ручные наблюдения и не подтверждает выполнение тестов.
Есть и содержательное ограничение. Если ошибка появилась, была исправлена и затем возникла снова, весь диапазон нельзя бездумно трактовать как единичный переход хорошего свойства в плохое. Сначала выберите конкретный интервал и уточните вопрос. В подготовленной истории после B3 адрес остаётся неправильным, поэтому ожидаемая граница однозначна.
Перед поиском полезно записать исходную ветку, обе крайние записи и точную формулировку критерия в заметку. Она позволит другому разработчику повторить исследование и объяснит, что именно означали ваши отметки. Наш сценарий заканчивается найденным изменением адреса и восстановленным исходным рабочим положением, сохраняя всю историю. В следующем уроке рассмотрим временное сохранение незаконченной работы перед переключением задачи.