Все главы учебника
Содержание учебника
Глава 03 / Git

Как читать diff и находить причину в истории

16 мин чтенияКонтент v0.10.0

Сначала увидеть, потом сохранить

Вы поменяли конфигурацию и описание. В описании случайно исчезла строка запуска. Если сразу сделать commit, ошибка станет частью истории. Git умеет показывать разницу между выбранными состояниями; ваша задача — выбрать правильные два состояния и прочитать содержимое, а не только число строк.

git diff сравнивает рабочее дерево с индексом, git diff --cached — индекс с HEAD, git diff HEAD — рабочие отслеживаемые файлы с последним снимком. Эти ответы могут различаться для одного пути. Файлы untracked обычный diff не показывает, поэтому рядом всегда нужен status. Сравнения diff задают, какая область участвует в проверке.

В текстовом diff строки с - отсутствуют в новом варианте, с + добавлены. Контекст без этих знаков помогает найти место. Заголовок @@ описывает диапазоны строк; он не сообщает качество изменения. Если весь файл выглядит заменённым, проверьте переносы строк и форматирование: логика могла измениться мало, а представление — целиком.

История отвечает на другой вопрос

git log перечисляет коммиты; git show показывает выбранную запись и её изменение. Сообщение — обещание автора, diff — фактическое содержимое. Для причины часто нужны оба. Удаление строки может быть намеренным решением или опечаткой, это нельзя узнать только по символу минус. Просмотр log позволяет ограничивать историю путём и искать сообщения, а show — исследовать конкретную запись.

HEAD~1 означает первого родителя текущего коммита. Пока история линейная, это предыдущая запись. После слияния родителей может быть несколько; номера и ветки станут важнее даты. Хеш можно сократить до однозначного префикса, но в лабораториях проще использовать сохранённую переменную или символическое имя. Нельзя ожидать одинаковые хеши на двух независимо созданных опытах.

Сравните три области

lab=$(mktemp -d "${TMPDIR:-/tmp}/git-03.XXXXXX")
cd "$lab"
git init -b main
git config user.name "Учебный автор"
git config user.email "learner@example.invalid"
git config commit.gpgsign false
printf 'queue=16\nworkers=2\n' > settings.txt
git add settings.txt
git commit -m "Set initial limits"
printf 'queue=32\nworkers=2\n' > settings.txt
git add settings.txt
printf 'queue=32\nworkers=4\n' > settings.txt
git diff -- settings.txt
git diff --cached -- settings.txt
git diff HEAD -- settings.txt
git commit -m "Increase queue capacity"
git log --oneline -- settings.txt
git show HEAD:settings.txt
git diff HEAD~1 HEAD -- settings.txt

Обычный diff меняет workers с двух на четыре. Cached меняет queue с шестнадцати на тридцать два. Diff HEAD видит оба изменения. После commit в снимке workers всё ещё равно двум; неготовая правка остаётся в рабочем дереве. Последнее сравнение коммитов показывает только ёмкость. Название сообщения соответствует факту, если вы не вызвали add повторно.

Затем выполните git status --short и убедитесь, что файл всё ещё изменён. Для длинного вывода Git может открыть pager. Клавиша q возвращает в терминал; команда не зависла. В лабораториях можно использовать git --no-pager log, если нужно обычное завершение вывода. Не путайте просмотр с переходом на старую версию: show ничего не переключает.

Прочитайте вывод как проверяющий

Сначала назовите путь, затем старое и новое поведение, затем оставшийся вопрос. Например: «settings меняет queue с 16 на 32; больше заданий могут ожидать; почему выбран этот предел?» Число добавленных строк не отвечает на последний вопрос. В своём опыте составьте два комментария: один о фактической ошибке, другой о неизвестной причине изменения. Не называйте неизвестную причину доказанной ошибкой.

Теперь представьте, что log нашёл нужное сообщение, но show показывает другую правку. Источник истины о сохранённом содержимом — снимок и diff, а сообщение может быть устаревшим обещанием. Чтение истории требует сопоставлять их. Наконец, проверьте незакоммиченную правку: ни один старый show не обязан её содержать. История и сегодняшнее рабочее дерево отвечают на разные вопросы.

Самостоятельная лаборатория

Практика — примерно 30 минут. Создайте файл инструкции из трёх строк. Сохраните его. Измените цель и подготовьте её; затем допишите команду запуска, не добавляя повторно. Перед каждым из трёх diff предскажите, какие строки увидите. После проверки сохраните только цель, затем отдельным коммитом команду.

Найдите запись, добавившую команду, через log с ограничением пути. Прочитайте её сообщение и diff. Создайте ещё один неотслеживаемый файл и проверьте, почему diff HEAD его не показывает. Запишите это как ограничение своего метода проверки, а не как баг Git. В реальном ревью полезно сверять список путей отдельно от содержимого.

Критерии готовности

Вы объясняете три сравнения, находите нужный коммит по пути и подтверждаете содержимое снимка. Можете заметить, что сообщение «изменить только цель» не соответствует diff, если туда попал запуск. Понимаете, почему чистый diff и непустой status возможны одновременно для untracked файла.

Разбор

В подготовленном изменении должна быть только цель, а в неподготовленном — команда запуска. В первой записи после основы команда ещё отсутствует. Если cached показывает обе правки, индекс обновили позднее ожидаемого. Чтобы восстановить учебную последовательность, создайте новый временный репозиторий; не пытайтесь насильно переписать неизвестные чужие коммиты.

При поиске причины сначала назовите наблюдаемое изменение, затем спросите, какое требование оно обслуживало. «Последний автор строки виноват» — слабое объяснение: строку могли переместить или отформатировать. В главе bisect вы научитесь связывать историю с воспроизводимой поломкой.

Теперь можно разделить независимую работу ветками. Для текущего урока первичные источники — руководства diff, log и show, ссылки на которые приведены у объяснений. Полезная привычка после урока: перед каждым commit прочитать cached diff и назвать одним предложением сохраняемое изменение.