Сравнение изменений с использованием git diff

Сравнение — это функция, анализирующая два входных набора данных и отображающая различия между ними. git diff представляет собой многоцелевую команду Git, которая инициирует функцию сравнения источников данных Git — коммитов, веток, файлов и т. д. В этом документе описываются типичные варианты вызова git diff и сценарии рабочего процесса сравнения. Зачастую вместе с командой git diff используются git status и git log для анализа текущего состояния репозитория Git.
 

Чтение вывода команды diff

Формат вывода необработанных данных

Следующие примеры будут выполнены в простом репозитории. Репозиторий создается с помощью перечисленных ниже команд:

$:> mkdir diff_test_repo
$:> cd diff_test_repo
$:> touch diff_test.txt
$:> echo "this is a git diff test example" > diff_test.txt
$:> git init .
Initialized empty Git repository in /Users/kev/code/test/.git/
$:> git add diff_test.txt
$:> git commit -am"add diff test file"
[master (root-commit) 6f77fc3] add diff test file
1 file changed, 1 insertion(+)
create mode 100644 diff_test.txt

Если выполнить git diff на этом этапе, команда ничего не выведет. Это ожидаемая ситуация, поскольку в репозитории отсутствуют изменения для сравнения. После создания репозитория и добавления файла diff_test.txt можно отредактировать его содержимое, чтобы начать работу с выходными данными сравнения.

$:> echo "this is a diff example" > diff_test.txt

При выполнении этой команды содержимое файла diff_test.txt будет изменено. После этого можно просмотреть изменения и проанализировать выходные данные. Теперь при выполнении git diff мы получим следующие выходные данные:

diff --git a/diff_test.txt b/diff_test.txt
index 6b0c6cf..b37e70a 100644
--- a/diff_test.txt
+++ b/diff_test.txt
@@ -1 +1 @@
-this is a git diff test example
+this is a diff example

А теперь подробно рассмотрим выходные данные сравнения.

1. Входные данные сравнения

diff --git a/diff_test.txt b/diff_test.txt

В этой строке отображаются входные данные сравнения. Как видите, для сравнения переданы файлы a/diff_test.txt и b/diff_test.txt.

2. Метаданные

index 6b0c6cf..b37e70a 100644

В этой строке отображаются внутренние метаданные Git. Скорее всего, они вам не понадобятся. Номера в этих выходных данных соответствуют хеш-идентификаторам версий объектов Git.

3. Маркеры изменений

--- a/diff_test.txt
+++ b/diff_test.txt

Эти строки представляют собой легенду обозначений для каждого источника входных данных сравнения. В данном случае изменения из файла a/diff_test.txt помечаются символом ---, а из файла b/diff_test.txt — символом +++.

4. Сравниваемые фрагменты

Остальные выходные данные сравнения — это список сравниваемых фрагментов. При сравнении отображаются только разделы файла, в которых есть изменения. В данном примере имеется только один такой фрагмент, поскольку обрабатывается простой сценарий. Фрагменты имеют собственную ограниченную семантику вывода.

@@ -1 +1 @@
-this is a git diff test example
+this is a diff example

Первая строка — это заголовок фрагмента. Каждый фрагмент вводится с помощью заголовка, ограниченного символами @@. Заголовок описывает изменения в файле. В нашем простом примере выражение -1 +1 означает, что первая строка содержит изменения. В реальных случаях заголовок может выглядеть так:

@@ -34,6 +34,8 @@

В данном примере заголовка было извлечено 6 строк начиная со строки 34. Кроме того, после строки 34 было добавлено 8 строк.

Остальное содержимое фрагмента сравнения — это недавние изменения. Каждая измененная строка вводится символом + или -, указывающим на исходную версию входных данных сравнения. Как упоминалось, символ - указывает на изменения в файле a/diff_test.txt, а + — на изменения в файле b/diff_test.txt.

Подсветка изменений

1. git diff --color-words

Команда git diff также имеет специальный режим подсветки изменений с повышенной детализацией: ‐‐color-words. В этом режиме добавленные и удаленные строки помечаются пробелами, а затем выполняется их сравнение.

$:> git diff --color-words
diff --git a/diff_test.txt b/diff_test.txt
index 6b0c6cf..b37e70a 100644
--- a/diff_test.txt
+++ b/diff_test.txt
@@ -1 +1 @@
this is agit difftest example

Теперь в выходных данных отображаются только измененные слова с цветовой подсветкой.

2. git diff-highlight

При клонировании источника Git появляется подкаталог с именем contrib. Он содержит набор связанных с Git инструментов и различные данные, которые пока еще не были включены в ядро Git. В их число входит скрипт Perl под названием diff-highlight. Diff-highlight попарно сопоставляет совпадающие строки выходных данных сравнения и подсвечивает измененные фрагменты внутри слов.

$:> git diff | /your/local/path/to/git-core/contrib/diff-highlight/diff-highlight
diff --git a/diff_test.txt b/diff_test.txt
index 6b0c6cf..b37e70a 100644
--- a/diff_test.txt
+++ b/diff_test.txt
@@ -1 +1 @@
-this is a git diff test example
+this is a diff example

Теперь сравниваемые строки объединены в пары для определения наименьших возможных изменений.

Сравнение двоичных файлов

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

$:> git diff
Binary files a/script.pdf and b/script.pdf differ

В Git есть функция, с помощью которой можно указать команду оболочки для преобразования содержимого двоичных файлов в текст до начала сравнения. Для этого требуется выполнить незначительную настройку. Для начала необходимо указать фильтр textconv, описывающий процесс преобразования определенного типа двоичного файла в текст. В данном случае используется простой инструмент pdftohtml (доступный в Homebrew), позволяющий преобразовывать файлы PDF в удобный для восприятия формат HTML. Его можно назначить отдельному репозиторию в файле .git/config или глобально в файле ~ /.gitconfig

[diff "pdfconv"]
textconv=pdftohtml -stdout

Затем необходимо связать один или несколько шаблонов файлов с фильтром pdfconv. Для этого необходимо создать файл .gitattributes в корне репозитория.

*.pdf diff=pdfconv

После настройки команда git diff сначала обрабатывает двоичный файл с помощью настроенного скрипта конвертера, а затем выполняет сравнение выходных данных конвертера. Этот метод можно использовать для эффективного сравнения любых двоичных файлов, например ZIP, JAR и других архивов. Используя инструмент unzip -l (или аналогичный) вместо pdf2html, можно просмотреть, какие пути были добавлены в коммит или удалены из него по сравнению с другим коммитом. Утилиту exiv2 можно использовать для просмотра изменений метаданных, например размеров изображения. Существуют инструменты для преобразования .odf, .doc и других форматов документов в простой текст. В крайнем случае команда может сработать с двоичными файлами, для которых формальных конвертеров не существует.

Сравнение файлов: файл git diff

Команда git diff позволяет передать явный путь к файлу. Когда в git diff передается путь к файлу, выполняется сравнение этого файла. Более подробно этот процесс показан на примерах ниже.

git diff HEAD ./path/to/file

В этом примере выполняется сравнение для файла ./path/to/file. Рабочий каталог сравнивается с разделом проиндексированных файлов и выводятся изменения, которые еще не были проиндексированы. По умолчанию git diff выполняет сравнение с HEAD. Если опустить элемент HEAD в приведенном примере и выполнить команду git diff ./path/to/file, это не повлияет на результат.

git diff --cached ./path/to/file

При вызове git diff с использованием параметра --cached, сравниваются проиндексированные изменения и локальный репозиторий. Параметр --cached синонимичен параметру --staged.

Сравнение всех изменений

При вызове git diff без указания пути к файлу выполняется сравнение всех изменений в репозитории. Команды из приведенных выше примеров можно вызвать без аргумента ./path/to/file и получить аналогичные результаты для всех файлов в локальном репозитории.

Изменения после последнего коммита

По умолчанию команда git diff выводит все неотправленные изменения, внесенные после последнего коммита.

git diff

Сравнение файлов в двух коммитах

Команде git diff можно передать ссылки на коммиты Git для сравнения. Возможные варианты ссылок — HEAD, теги и имена веток. Каждый коммит в Git имеет идентификатор, который можно получить с помощью команды GIT LOG. Этот идентификатор коммита тоже можно передать в git diff.

git log --prety=oneline
957fbc92b123030c389bf8b4b874522bdf2db72c add feature
ce489262a1ee34340440e55a0b99ea6918e19e7a rename some classes
6b539f280d8b0ec4874671bae9c6bed80b788006 refactor some code for feature
646e7863348a427e1ed9163a9a96fa759112f102 add some copy to body
$:> git diff 957fbc92b123030c389bf8b4b874522bdf2db72c ce489262a1ee34340440e55a0b99ea6918e19e7a

Сравнение веток

Сравнение двух веток

Ветки сравниваются с помощью команды git diff точно так же, как любые другие входные ссылки.

git diff branch1..other-feature-branch

В этом примере демонстрируется оператор точка. Две точки показывают, что сравниваются последние коммиты двух веток. Тот же результат можно получить, если опустить точки и поставить пробел между именами веток. Кроме того, можно использовать трехточечный оператор:

git diff branch1...other-feature-branch

Трехточечный оператор инициирует сравнение путем изменения первого параметра ввода branch1. Параметр branch1 преобразуется в ссылку на родительский коммит, общий для двух входных объектов сравнения. Это общий предок ветки branch1 и другой функциональной ветки. Последний параметр ввода остается без изменений — это последний коммит другой функциональной ветки.

Сравнение файлов из двух веток

Чтобы сравнить конкретный файл в разных ветках, передайте команде git diff путь к файлу в качестве третьего аргумента.

git diff master new_branch ./diff_test.txt

Резюме

На этой странице были рассмотрены процесс сравнения в Git и команда git diff. Мы объяснили, как читать выходные данные git diff и различные виды этих данных. В примерах было показано, как изменить выходные данные git diff с помощью подсвечивания и использования цветов. Кроме того, были рассмотрены различные стратегии сравнения, например сравнение файлов в разных ветках и конкретных коммитах. Наряду с git diff также использовались команды git log и git checkout.

Готовы изучить Git?

Попробуйте это интерактивное учебное руководство.

Начните прямо сейчас