Различия
Pitmaster вычисляет различия в унифицированном формате git. Механизм вычисления поддерживает несколько алгоритмов, форматирование унифицированных различий, обнаружение двоичных файлов и рекурсивное сравнение на уровне деревьев.
Для изменённого текстового файла в режиме 100644 format() формирует те же байты, что и git diff. Сейчас другие случаи форматируются иначе (#3):
- У новых и удалённых файлов нет строк
new file mode/deleted file mode, строкиindexи/dev/null: обе стороны обозначаются какa/<path>иb/<path>, а диапазон пустого фрагмента записывается как-1,0или+1,0, тогда как git записывает-0,0или+0,0. - Строка
indexвсегда заканчивается на100644независимо от режима файла. - При изменении только режима (например,
chmod +x) результат не формируется. - Для двоичных файлов выводится
Binary files differбез строкиindex, тогда как git выводитBinary files a/<path> and b/<path> differ. - Переименования отображаются одной записью
diff --git a/<old> b/<new>с различиями в содержимом, без строкsimilarity index,rename fromилиrename to.
Операции с различиями
Неиндексированные изменения (рабочее дерево и индекс)
$diffs = $repo->diff();
foreach ($diffs as $diff) {
echo $diff->format();
}
Сравнивает каждый файл в индексе с его текущим состоянием в рабочем дереве. Обнаруживает изменения, удаления и двоичные файлы.
Проиндексированные изменения (индекс и HEAD)
$diffs = $repo->diffStaged();
foreach ($diffs as $diff) {
echo $diff->format();
}
Сравнивает текущий индекс с деревом коммита HEAD. Показывает, что войдёт в следующий коммит. Удалённые и добавленные файлы с похожим содержимым отображаются одной записью о переименовании. До первого коммита HEAD отсутствует, поэтому результат пуст (#5).
Сейчас при сопоставлении переименований также совпадают несвязанные однострочные файлы, поскольку завершающая пустая строка считается общим содержимым (#4).
Сравнение дерева с деревом
$a = $repo->readObject($repo->resolve('HEAD~1')->hex)->tree;
$b = $repo->head()->tree;
$diffs = $repo->diffTree($a, $b);
Рекурсивно сравнивает два дерева. Передавайте идентификаторы деревьев: идентификаторы коммитов не преобразуются в деревья и дают пустой результат. Тот же механизм TreeDiff лежит в основе diffStaged(), show() и logPath().
Фильтрация по пути
$diffs = $repo->diff('src/specific-file.php');
Спецификатор пути — это точный путь к одному файлу. diffStaged() принимает такой же аргумент, но пока игнорирует его, поэтому всегда возвращает все проиндексированные файлы (#5).
Выбор алгоритма
$diffs = $repo->diff(null, 'patience');
$diffs = $repo->diffStaged(null, 'histogram');
$diffs = $repo->diffTree($a, $b, 'minimal');
Допустимые названия: myers (по умолчанию), patience, histogram и minimal. Любое другое название приводит к исключению InvalidArgumentException.
Структура DiffResult
Каждый DiffResult представляет различия для одного файла.
use Pitmaster\Diff\DiffResult;
foreach ($diffs as $diff) {
echo $diff->oldPath; // Path in the old tree
echo $diff->newPath; // Path in the new tree
echo $diff->binary; // true for binary files
echo $diff->oldHash; // Hex hash of the old blob (null for added files)
echo $diff->newHash; // Hex hash of the new blob (null for deleted files)
foreach ($diff->hunks as $hunk) {
echo $hunk->header(); // @@ -1,5 +1,7 @@
foreach ($hunk->lines as $line) {
echo $line . "\n"; // ' context', '+added', '-removed'
}
}
}
Форматированный вывод
echo $diff->format();
Формирует вывод в формате унифицированных различий. Для изменённого текстового файла результат совпадает с git diff:
diff --git a/src/File.php b/src/File.php
index abc1234..def5678 100644
--- a/src/File.php
+++ b/src/File.php
@@ -10,6 +10,8 @@ class File
private string $name;
+ private int $count;
+
public function __construct()
{
Проверка наличия изменений
if ($diff->hasChanges()) {
// File was modified (or is binary with changes)
}
Структура фрагмента
Каждый Hunk представляет непрерывную область изменений со строками контекста.
use Pitmaster\Diff\Hunk;
$hunk->oldStart; // Starting line in old file
$hunk->oldCount; // Number of lines from old file
$hunk->newStart; // Starting line in new file
$hunk->newCount; // Number of lines in new file
$hunk->lines; // Array of diff lines (prefixed with ' ', '+', or '-')
$hunk->header(); // "@@ -10,6 +10,8 @@"
Алгоритмы вычисления различий
Pitmaster реализует четыре алгоритма вычисления различий. По умолчанию используется Myers.
Различия Myers
Стандартный алгоритм O(ND), как и используемый по умолчанию алгоритм в git. Формирует минимальные сценарии редактирования.
use Pitmaster\Diff\MyersDiff;
$hunks = MyersDiff::diff($oldContent, $newContent);
Метод diff() принимает две строки и возвращает массив объектов Hunk. Он разбивает входные данные по символам новой строки, вычисляет кратчайший сценарий редактирования и группирует изменения во фрагменты с 3 строками контекста (как и используемый по умолчанию параметр git).
Различия Patience
Лучше выявляет структуру кода. Сначала сопоставляет уникальные строки, затем заполняет промежутки между ними.
use Pitmaster\Diff\PatienceDiff;
$hunks = PatienceDiff::diff($oldContent, $newContent);
Гистограммные различия
Вариант алгоритма Patience, который лучше обрабатывает повторяющиеся строки. Используется в JGit и Eclipse.
use Pitmaster\Diff\HistogramDiff;
$hunks = HistogramDiff::diff($oldContent, $newContent);
Минимальные различия
Формирует минимально возможный результат (с наименьшим числом изменённых строк) ценой дополнительных вычислений.
use Pitmaster\Diff\MinimalDiff;
$hunks = MinimalDiff::diff($oldContent, $newContent);
Обнаружение бинарных файлов
Pitmaster считает содержимое двоичным, если нулевой байт встречается в первых 8192 байтах (git проверяет первые 8000).
if (MyersDiff::isBinary($content)) {
// Binary file, skip line-level diff
}
Для двоичных файлов создаётся DiffResult с $binary = true и пустым массивом $hunks. Форматированный вывод:
diff --git a/image.png b/image.png
Binary files differ
Цветной вывод
Для вывода в терминал с цветами ANSI CLI использует ColorDiff.
use Pitmaster\Diff\ColorDiff;
$colorized = ColorDiff::colorize($diff->format());
echo $colorized;
- Добавленные строки (
+) отображаются зелёным цветом - Удалённые строки (
-) отображаются красным цветом - Заголовки фрагментов (
@@) отображаются голубым цветом - Заголовки файлов (
diff --git,index,---,+++) отображаются полужирным шрифтом
pitmaster diff всегда добавляет цвет, даже если его вывод перенаправлен. pitmaster show добавляет цвет, только когда stdout — терминал.
Сравнение деревьев (внутренний механизм)
Класс TreeDiff рекурсивно сравнивает два объекта дерева и создает DiffResult для каждого измененного файла.
use Pitmaster\Diff\TreeDiff;
$treeDiff = new TreeDiff($repo->objectDatabase());
$diffs = $treeDiff->diff($oldTreeId, $newTreeId);
Сравнение деревьев обрабатывает:
- Добавленные файлы (есть только в новом дереве)
- Удаленные файлы (есть только в старом дереве)
- Измененные файлы (одинаковый путь, разные хеши blob-объектов)
- Рекурсивное сравнение подкаталогов
- Сопоставление переименований между удаленными и добавленными файлами (точное совпадение хешей или не менее 50% общих строк)
Записи с неизменившимся хешем пропускаются, поэтому одно лишь изменение режима не дает результата.
Сравнение слов
Для встроенного в строки сравнения изменений на уровне слов.
use Pitmaster\Diff\WordDiff;
$result = WordDiff::diff('the quick fox', 'the slow fox');
// 'the [-quick-]{+slow+} fox'