Чтение diff и истории
git diff показывает рабочее дерево против индекса; --staged — индекс против HEAD; HEAD — всё с последнего коммита. Учимся читать кусок (hunk) унифицированного diff: заголовок @@ и строки +/- — и просматривать историю через git log, --oneline, --graph и git show.
Перед коммитом ты должен уверенно отвечать на один вопрос: что именно я сейчас записываю? Слепое индексирование — это то, как секреты, отладочные 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.
git diff без аргументов сравнивает рабочее дерево с областью индекса — изменения, которые ты ещё НЕ проиндексировал. Это самый частый diff и тот, который новички читают неверно. Он не показывает всё, что ты изменил с последнего коммита; он показывает лишь то, что всё ещё не проиндексировано:
git diffКак только ты сделаешь git add, изменение исчезает из обычного git diff (теперь оно проиндексировано), и чтобы его увидеть, нужен другой вариант. Эта неожиданность — «я правил файл, почему diff пустой?» — почти всегда означает, что ты уже его проиндексировал.
Три варианта diff сравнивают три разные пары снимков. Выбирай по тому, что хочешь увидеть:
git diff # рабочее дерево против индекса → непроиндексированные изменения
git diff --staged # индекс против HEAD → что запишет коммит (также --cached)
git diff HEAD # рабочее дерево против HEAD → все изменения с последнего коммита (индекс + рабочее)HEAD — это последний коммит на твоей текущей ветке. git diff --staged стоит запускать прямо перед коммитом — он показывает ровно то, что коммит будет содержать, не больше. git diff HEAD — это общая картина: всё, что отличается от последнего коммита, независимо от индексирования.
Унифицированный 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, контекст — не шум; это якорь, доказывающий, что изменение там, где ты думаешь.
git log выводит историю; флаги превращают стену текста в читаемую карту. Сырой лог многословен, поэтому сеньоры почти всегда его формируют:
git log # полные записи: хеш, автор, дата, сообщение
git log --oneline # одна компактная строка на коммит
git log --oneline --graph --all --decorate # ASCII-граф веток по всем веткам--oneline сворачивает каждый коммит до короткого хеша плюс тема; --graph рисует структуру веток/слияний; --all включает все ветки, не только текущую; --decorate показывает, какие имена веток и тегов куда указывают. Эта комбинация — самый полезный способ увидеть «какая форма у истории этого репозитория?».
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-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.