git diff
Узнайте о команде git diff, её выводе, способах выделения изменений и сравнении файлов и веток в Git.
Что делает git diff
Команда git diff показывает, что именно изменилось — строка за строкой — между двумя источниками в вашем проекте. Она принимает два набора данных и выводит разницу между ними в виде патча.
Этими двумя источниками могут быть любая пара из: рабочего дерева (файлы, которые вы редактируете прямо сейчас), области подготовки (индекс, то, что захватила команда git add), и любого зафиксированного снимка. Поскольку все остальные команды Git сообщают лишь что изменилось, git diff — инструмент, который показывает что именно изменилось, прежде чем вы подготовите или зафиксируете изменения.
Команда часто используется вместе с git status и git log для проверки состояния репозитория Git: git status показывает список изменённых файлов, а git diff отображает содержимое этих изменений.

Какие два источника сравниваются
Форма команды определяет, что именно сравнивается:
| Команда | Сравнивает |
|---|---|
git diff | Рабочее дерево с областью подготовки (неподготовленные изменения) |
git diff --staged | Область подготовки с последним коммитом (то, что запишет git commit) |
git diff HEAD | Рабочее дерево с последним коммитом (все незафиксированные изменения) |
git diff <commit> <commit> | Один коммит с другим |
git diff <branch> <branch> | Верхушки двух веток |
Простая команда git diff показывает только то, что вы ещё не подготовили с помощью git add. Это самая распространённая ловушка: если вы уже выполнили git add, git diff выглядит пустым, даже если есть правки — используйте git diff --staged, чтобы увидеть их.
Чтение вывода diff
Вывод diff состоит из нескольких отдельных частей. В разделах ниже они разбираются поэтапно с объяснением каждой строки.
Формат необработанного вывода
Выполните приведённые ниже команды, чтобы создать простой репозиторий:
mkdir test_repo
cd test_repo
touch test.txt
echo "this is a git diff test example" > test.txt
git init .
#Initialized empty Git repository in /Users/kev/code/test/.git/
git add test.txt
git commit -am "add diff test file"
#[master (root-commit) 9e2dcac] add diff test file
#1 file changed, 1 insertion(+)
#create mode 100644 test.txtЧтобы git diff выдал какой-либо вывод, нужно изменить содержимое test.txt после его фиксации. Выполните следующую команду:
echo "this is a diff example" > test.txtТолько теперь можно просмотреть diff и разобрать его вывод. Выполнение git diff даст следующий результат:
diff --git a/test.txt b/test.txt
index 6b0c6cf..b37e70a 100644
--- a/test.txt
+++ b/test.txt
@@ -1 +1 @@
-this is a git diff test example
+this is a diff exampleИсточники ввода diff
Первая строка называет два сравниваемых источника. Здесь a/test.txt (версия «до») и b/test.txt (версия «после») передаются в diff. Префиксы a/ и b/ всегда обозначают две стороны, даже если это один и тот же файл.
diff --git a/test.txt b/test.txtМетаданные
Эта строка содержит внутренние метаданные Git. Два числа — это сокращённые хеши объектов (blob) файла до и после изменения, а 100644 — режим файла (обычный, неисполняемый файл).
index 6b0c6cf..b37e70a 100644Обозначения изменений
Эти строки назначают символ каждому источнику ввода diff. Строки из a/test.txt (оригинал) помечаются ---, а строки из b/test.txt (новая версия) помечаются +++.
--- a/test.txt
+++ b/test.txtФрагменты diff
Diff показывает не весь файл, а только изменённые области. Каждая такая область называется фрагментом (или ханком). Фрагменты включают несколько неизменённых строк окружающего контекста, чтобы было видно, где именно находится изменение.
@@ -1 +1 @@
-this is a git diff test example
+this is a diff exampleПервая строка — заголовок фрагмента, заключённый в символы @@. Он суммирует диапазоны затронутых строк: -1 означает «начиная со строки 1 старого файла», а +1 означает «начиная со строки 1 нового файла». Под заголовком ведущий - обозначает удалённую строку, а ведущий + — добавленную. Таким образом, изменённая строка отображается как удаление с последующим добавлением.
Распространённые флаги
Git предоставляет несколько полезных флагов для различных рабочих процессов:
--staged(или--cached): Сравнивает подготовленные изменения в индексе с коммитомHEAD— то, что запишет git commit.--stat: Показывает сжатую сводку изменённых файлов (количество вставок/удалений для каждого файла) вместо полного diff.--name-only: Выводит только имена изменённых файлов.--name-status: Как--name-only, но добавляет перед каждым файлом букву статуса (M— изменён,A— добавлен,D— удалён).-w(или--ignore-all-space): Игнорирует изменения, состоящие только из пробелов, что удобно, когда переиндентация скрывает реальные правки.
Например, чтобы получить краткий обзор того, насколько изменился каждый файл:
git diff --stat
# test.txt | 2 +-
# 1 file changed, 1 insertion(+), 1 deletion(-)Выделение изменений
Для правок на уровне строк стандартный вывод может быть шумным, поскольку Git показывает полную удалённую строку и полную добавленную строку, даже если отличается всего одно слово. Два инструмента ниже выделяют именно изменённые части.
git diff --color-words
Первый способ — специальный режим, встроенный в git diff: --color-words. Он разбивает добавленные и удалённые строки на токены по пробелам, а затем сравнивает эти токены, поэтому цветом выделяются только изменённые слова, а не целые строки.
git diff --color-words
diff --git a/test.txt b/test.txt
index 6b0c6cf..b37e70a 100644
--- a/test.txt
+++ b/test.txt
@@ -1 +1 @@
-this is a git diff test example
+this is a diff example(Примечание: --color-words выделяет встроенные изменения с помощью цветов терминала. В текстовом виде формат вывода совпадает со стандартным diff.)
git diff-highlight
При клонировании исходного кода Git рядом с ним поставляется подкаталог contrib. Он содержит инструменты, связанные с Git, одним из которых является diff-highlight. Он выделяет изменённые части слов с более высокой детализацией, чем --color-words. Обратите внимание, что этот инструмент фильтрует стандартный ввод (вы передаёте в него diff через pipe) и требует цветовой поддержки терминала для отображения.
git diff | git diff-highlight
diff --git a/test.txt b/test.txt
index 6b0c6cf..b37e70a 100644
--- a/test.txt
+++ b/test.txt
@@ -1 +1 @@
-this is a git diff test example
+this is a diff exampleСравнение бинарных файлов
git diff можно запускать не только для текстовых файлов, но и для бинарных. По умолчанию результат не очень информативен — Git лишь сообщает, что файл изменился, но не как именно:
git diff
# Binary files a/script.pdf and b/script.pdf differВ Git есть возможность указать команду оболочки для преобразования содержимого бинарного файла в текст перед выполнением diff. Для этого требуется небольшая настройка. Сначала определите фильтр textconv, описывающий, как преобразовать определённый бинарный тип в текст. Например, утилита pdftohtml (доступна через Homebrew) может конвертировать PDF в HTML. Настроить это можно в двух местах: для отдельного репозитория — в .git/config, или глобально — в ~/.gitconfig.
[diff "pdfconv"]
textconv=pdftohtml -stdoutЗатем привяжите один или несколько шаблонов файлов к фильтру pdfconv, создав файл .gitattributes в корне репозитория:
*.pdf diff=pdfconvПосле этой настройки git diff сначала пропускает каждый совпадающий бинарный файл через конвертер, а затем сравнивает текстовый вывод конвертера. С помощью той же техники можно получить читаемые diff для многих бинарных форматов (zip, jar и других архивов).
Сравнение одного файла
git diff также принимает явный путь к файлу. Если путь указан, операция ограничивается только этим файлом. В примере ниже аргумент ./path/to/file сравнивает изменения в рабочем каталоге с коммитом HEAD:
git diff HEAD ./path/to/fileСравнение всех изменений
Чтобы сравнить изменения во всём репозитории, запустите git diff без пути к файлу. Любую из приведённых выше форм можно вызвать без аргумента ./path/to/file, чтобы применить то же сравнение ко всем файлам в локальном репозитории.
Изменения с момента последнего коммита
Простая команда git diff сравнивает рабочий каталог с областью подготовки (индексом), поэтому она показывает только неподготовленные правки. Чтобы увидеть все незафиксированные изменения с момента последнего коммита — как подготовленные, так и неподготовленные — сравните с HEAD:
git diff HEADСравнение двух коммитов
git diff принимает ссылки Git, такие как имена веток, теги и хеши коммитов. У каждого коммита есть уникальный идентификатор, который можно найти с помощью git log. Передайте два идентификатора коммита, чтобы сравнить их напрямую:
git diff <commit-hash-1> <commit-hash-2>Вывод показывает, что изменилось от первого коммита ко второму.
Сравнение веток
Сравнение веток работает так же, как и любой другой ссылочный ввод в git diff. Нужно знать два оператора.
Оператор с двумя точками сравнивает верхушки обеих веток:
git diff branch1..branch2Тот же результат получается, если точки опустить и использовать пробел между именами веток. Есть также оператор с тремя точками:
git diff branch1...branch2Оператор с тремя точками базирует сравнение на общей истории: он заменяет branch1 общим предком (базой слияния) двух веток, тогда как второй вход остаётся верхушкой branch2. Иными словами, branch1...branch2 отвечает на вопрос «что произошло на branch2 с момента её ответвления от branch1», что обычно и нужно при проверке ветки с функцией перед git merge.
Сравнение файла между двумя ветками
Чтобы сравнить конкретный файл между ветками, передайте путь к файлу третьим аргументом:
git diff master new_branch ./test.txtСвязанные команды
- git status — посмотрите, какие файлы изменились, прежде чем использовать
git diff, чтобы увидеть, что в них изменилось. - git add — как только diff выглядит правильно, подготовьте его; затем используйте
git diff --staged, чтобы проверить подготовленное. - git commit — зафиксируйте подготовленные изменения.
- git log — найдите хеши коммитов для передачи в
git diff <commit> <commit>. - git show — просмотрите diff, введённый одним коммитом.