Різниця
Pitmaster обчислює різницю у форматі unified від git. Механізм обчислення різниці підтримує кілька алгоритмів, форматування unified diff, виявлення двійкових файлів і рекурсивне порівняння на рівні дерев.
Для зміненого текстового файла з режимом 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();
Створює вивід unified diff. Для зміненого текстового файла він збігається з 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);
Різниця Histogram
Різновид різниці Patience, який краще обробляє повторювані рядки. Використовується в JGit і Eclipse.
use Pitmaster\Diff\HistogramDiff;
$hunks = HistogramDiff::diff($oldContent, $newContent);
Мінімальна різниця
Створює найменшу можливу різницю (із найменшою кількістю змінених рядків), але потребує більше обчислень.
use Pitmaster\Diff\MinimalDiff;
$hunks = MinimalDiff::diff($oldContent, $newContent);
Виявлення бінарних файлів
Pitmaster вважає вміст двійковим, якщо в перших 8192 байтах є байт NUL (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
Кольоровий вивід
CLI використовує ColorDiff для виводу в термінал з кольорами ANSI.
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'