W3docs

git diff

Узнайте о команде git diff, её выводе, способах выделения изменений и сравнении файлов и веток в Git.

Что делает git diff

Команда git diff показывает, что именно изменилось — строка за строкой — между двумя источниками в вашем проекте. Она принимает два набора данных и выводит разницу между ними в виде патча.

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

Команда часто используется вместе с git status и git log для проверки состояния репозитория Git: git status показывает список изменённых файлов, а git diff отображает содержимое этих изменений.

gitdiff

Какие два источника сравниваются

Форма команды определяет, что именно сравнивается:

КомандаСравнивает
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, введённый одним коммитом.

Практика

Практика
Каковы функции и параметры команды 'git diff'?
Каковы функции и параметры команды 'git diff'?
Was this page helpful?