open atlas
↑ К треку
Git: от нуля до сеньора GIT · 02 · 02

Чтение diff и истории

git diff показывает рабочее дерево против индекса; --staged — индекс против HEAD; HEAD — всё с последнего коммита. Учимся читать кусок (hunk) унифицированного diff: заголовок @@ и строки +/- — и просматривать историю через git log, --oneline, --graph и git show.

GIT Junior ◷ 17 min
Уровень
ОсновыJuniorMiddleSenior

Перед коммитом ты должен уверенно отвечать на один вопрос: что именно я сейчас записываю? Слепое индексирование — это то, как секреты, отладочные print и случайные правки утекают в историю. Git точно отвечает на этот вопрос командой git diff, а на вопрос «что изменилось во всём проекте и когда?» отвечает git log.

К концу этого урока ты сможешь читать кусок (hunk) унифицированного diff строка за строкой, выбирать нужный вариант diff под то, что хочешь сравнить, и перемещаться по истории проекта через log и show.

Цель

После этого урока ты сможешь различать git diff, git diff --staged и git diff HEAD, читать кусок унифицированного diff, включая его заголовок @@ и строки +/-/контекста, и просматривать историю через git log --oneline --graph и git show.

1

git diff без аргументов сравнивает рабочее дерево с областью индекса — изменения, которые ты ещё НЕ проиндексировал. Это самый частый diff и тот, который новички читают неверно. Он не показывает всё, что ты изменил с последнего коммита; он показывает лишь то, что всё ещё не проиндексировано:

git diff

Как только ты сделаешь git add, изменение исчезает из обычного git diff (теперь оно проиндексировано), и чтобы его увидеть, нужен другой вариант. Эта неожиданность — «я правил файл, почему diff пустой?» — почти всегда означает, что ты уже его проиндексировал.

2

Три варианта diff сравнивают три разные пары снимков. Выбирай по тому, что хочешь увидеть:

git diff            # рабочее дерево против индекса → непроиндексированные изменения
git diff --staged   # индекс против HEAD            → что запишет коммит (также --cached)
git diff HEAD       # рабочее дерево против HEAD     → все изменения с последнего коммита (индекс + рабочее)

HEAD — это последний коммит на твоей текущей ветке. git diff --staged стоит запускать прямо перед коммитом — он показывает ровно то, что коммит будет содержать, не больше. git diff HEAD — это общая картина: всё, что отличается от последнего коммита, независимо от индексирования.

3

Унифицированный diff читается как набор кусков (hunks), у каждого свой заголовок и три вида строк. Вот один кусок:

@@ -12,6 +12,7 @@ function login(user) {
   const token = sign(user);
-  return token;
+  log.debug("issued token");
+  return token;
 }

Заголовок @@ -12,6 +12,7 @@ — это карта: -12,6 значит «старый файл, начиная со строки 12, протяжённостью 6 строк»; +12,7 значит «новый файл, начиная со строки 12, протяжённостью 7 строк». После него: ведущий пробел — это неизменный контекст, - — строка удалена, + — строка добавлена. Изменённая строка показывается как - (старая) сразу за которой + (новая). Читаешь сверху вниз — и точно восстанавливаешь правку.

Почему это работает

Зачем числа строк в заголовке? Инструменты и люди используют их, чтобы применить или найти кусок, даже если окружающий код сдвинулся. Строки контекста (неизменные вокруг изменения) позволяют git заново найти нужное место в файле, который правили в другом месте после генерации diff, — именно это делает патчи переносимыми. Когда ты ревьюишь diff, контекст — не шум; это якорь, доказывающий, что изменение там, где ты думаешь.

4

git log выводит историю; флаги превращают стену текста в читаемую карту. Сырой лог многословен, поэтому сеньоры почти всегда его формируют:

git log                            # полные записи: хеш, автор, дата, сообщение
git log --oneline                  # одна компактная строка на коммит
git log --oneline --graph --all --decorate   # ASCII-граф веток по всем веткам

--oneline сворачивает каждый коммит до короткого хеша плюс тема; --graph рисует структуру веток/слияний; --all включает все ветки, не только текущую; --decorate показывает, какие имена веток и тегов куда указывают. Эта комбинация — самый полезный способ увидеть «какая форма у истории этого репозитория?».

5

git show исследует один коммит целиком — его метаданные плюс его diff. Когда log подсказал, какой коммит тебе важен, show говорит, что он сделал:

git show <commit>         # сообщение этого коммита и его полный diff
git log -p                # полный diff каждого коммита, новейшие первыми
git log --stat            # сводка по коммиту: затронутые файлы, счёт +/- строк

git show abc1234 печатает автора, дату, сообщение и точные куски, которые этот коммит внёс, — быстрейший способ ответить «что этот коммит на самом деле изменил?».

Разбор примера

Ловим отладочную строку до релиза.

Ты починил баг в auth.js и хочешь закоммитить. Сначала индексируешь и смотришь:

git add auth.js
git diff --staged

Вывод показывает кусок из шага 3 — твоё настоящее исправление, но и случайную + log.debug("issued token");, добавленную при отладке. Поскольку ты прочитал --staged diff, ты ловишь её до того, как она войдёт в историю. Ты убираешь её из индекса и удаляешь строку, затем перепроверяешь:

git diff --staged   # теперь показывает только настоящее исправление
git commit -m "Fix token issuance in login"

Позже коллега спрашивает, что изменил тот коммит. Ты запускаешь git log --oneline, чтобы найти его хеш, затем git show <hash>, чтобы напечатать точный кусок, — без угадывания, без открытия файлов. Чтение diff — это то, что удержало отладочную строку вне истории и что отвечает на вопрос спустя месяцы.

Частая ошибка

Частая путаница: запустить git diff после git add, ничего не увидеть и заключить «моё изменение пропало». Оно не пропало — оно переехало на проиндексированную сторону. Обычный git diff всегда показывает лишь зазор между рабочим деревом и индексом. После индексирования используй git diff --staged (индекс против HEAD) или git diff HEAD (всё с последнего коммита). Знать, какую пару сравнивает каждая команда, — и есть весь навык.

Проверь себя
Викторина

Ты правил файл и сделал git add на нём. Теперь хочешь увидеть точно, что запишет твой следующий коммит. Какая команда это покажет?

Итог

git diff (без аргументов) сравнивает рабочее дерево против индекса — твои непроиндексированные изменения; git diff --staged сравнивает индекс против HEAD — ровно то, что запишет следующий коммит; git diff HEAD сравнивает рабочее дерево против HEAD — всё с последнего коммита. Унифицированный diff читается как куски (hunks): заголовок @@ -a,b +c,d @@ отображает старый и новый диапазоны строк, ведущий пробел — контекст, - — удалено, + — добавлено. Просматривай историю через git log, формируя её флагами --oneline, --graph, --all и --decorate; исследуй отдельный коммит через git show, а -p или --stat к логу дают diff или сводку по каждому коммиту. Дальше ты скажешь git, какие файлы игнорировать полностью, с помощью .gitignore.

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 4 завершено

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.