Diff
Pitmaster calcula diffs no formato unificado de git. O mecanismo de diff oferece suporte a vários algoritmos, formatação de diff unificado, detecção de arquivos binários e comparação recursiva no nível da árvore.
Para um arquivo de texto modificado com modo 100644, format() produz os mesmos bytes que git diff. Atualmente, os outros casos são formatados de forma diferente (#3):
- Arquivos novos e excluídos não têm linha
new file mode/deleted file mode, linhaindexnem/dev/null: os dois lados recebem os rótulosa/<path>eb/<path>, e o intervalo do hunk do lado vazio é escrito como-1,0ou+1,0, enquanto git escreve-0,0ou+0,0. - A linha
indexsempre termina em100644, seja qual for o modo do arquivo. - Alterações apenas no modo (por exemplo,
chmod +x) não geram resultado. - Arquivos binários exibem
Binary files differsem uma linhaindex, enquanto git exibeBinary files a/<path> and b/<path> differ. - Renomeações são mostradas como uma única entrada
diff --git a/<old> b/<new>com diff de conteúdo, sem linhassimilarity index,rename fromourename to.
Operações de diff
Alterações não preparadas (árvore de trabalho vs índice)
$diffs = $repo->diff();
foreach ($diffs as $diff) {
echo $diff->format();
}
Compara cada arquivo do índice com seu estado atual na árvore de trabalho. Detecta modificações, exclusões e arquivos binários.
Alterações preparadas (índice vs HEAD)
$diffs = $repo->diffStaged();
foreach ($diffs as $diff) {
echo $diff->format();
}
Compara o índice atual com a árvore do commit HEAD. Mostra o que será incluído no próximo commit. Arquivos excluídos e adicionados com conteúdo semelhante são relatados como uma única entrada renomeada. Antes do primeiro commit, não há HEAD e o resultado é vazio (#5).
Atualmente, o pareamento de renomeações também corresponde a arquivos de uma linha sem relação entre si, porque a linha vazia final conta como conteúdo compartilhado (#4).
Diff entre árvores
$a = $repo->readObject($repo->resolve('HEAD~1')->hex)->tree;
$b = $repo->head()->tree;
$diffs = $repo->diffTree($a, $b);
Compara duas árvores recursivamente. Passe IDs de árvore: IDs de commit não são convertidos em suas árvores e retornam um resultado vazio. O mesmo mecanismo TreeDiff dá suporte a diffStaged(), show() e logPath().
Filtrar por caminho
$diffs = $repo->diff('src/specific-file.php');
A pathspec é um caminho exato de arquivo. diffStaged() aceita o mesmo argumento, mas por enquanto o ignora, então sempre retorna todos os arquivos preparados (#5).
Escolher um algoritmo
$diffs = $repo->diff(null, 'patience');
$diffs = $repo->diffStaged(null, 'histogram');
$diffs = $repo->diffTree($a, $b, 'minimal');
Os nomes válidos são myers (padrão), patience, histogram e minimal. Qualquer outro nome lança InvalidArgumentException.
Estrutura de DiffResult
Cada DiffResult representa o diff de um único arquivo.
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'
}
}
}
Saída formatada
echo $diff->format();
Gera saída de diff unificado. Para um arquivo de texto modificado, corresponde a 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()
{
Verificar alterações
if ($diff->hasChanges()) {
// File was modified (or is binary with changes)
}
Estrutura de hunk
Cada Hunk representa uma região contínua de alterações com linhas de contexto.
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 @@"
Algoritmos de diff
Pitmaster implementa quatro algoritmos de diff. O padrão é Myers.
Diff de Myers
O algoritmo O(ND) padrão, igual ao padrão de git. Produz scripts de edição mínimos.
use Pitmaster\Diff\MyersDiff;
$hunks = MyersDiff::diff($oldContent, $newContent);
O método diff() recebe duas strings e retorna um array de objetos Hunk. Divide a entrada em novas linhas, calcula o script de edição mais curto e agrupa as alterações em hunks com 3 linhas de contexto (igual ao padrão de git).
Diff de Patience
Gera diffs estruturais melhores para código. Primeiro corresponde linhas únicas e, em seguida, preenche os intervalos entre elas.
use Pitmaster\Diff\PatienceDiff;
$hunks = PatienceDiff::diff($oldContent, $newContent);
Diff de histograma
Uma variante do diff de patience que lida melhor com linhas repetidas. Usada por JGit e Eclipse.
use Pitmaster\Diff\HistogramDiff;
$hunks = HistogramDiff::diff($oldContent, $newContent);
Diff mínimo
Gera o menor diff possível (com o menor número de linhas alteradas), ao custo de mais processamento.
use Pitmaster\Diff\MinimalDiff;
$hunks = MinimalDiff::diff($oldContent, $newContent);
Detecção de binários
Pitmaster trata o conteúdo como binário quando ele contém um byte NUL em seus primeiros 8192 bytes (git verifica os primeiros 8000).
if (MyersDiff::isBinary($content)) {
// Binary file, skip line-level diff
}
Arquivos binários produzem um DiffResult com $binary = true e um array $hunks vazio. A saída formatada é:
diff --git a/image.png b/image.png
Binary files differ
Saída colorida
O CLI usa ColorDiff para saída de terminal com cores ANSI.
use Pitmaster\Diff\ColorDiff;
$colorized = ColorDiff::colorize($diff->format());
echo $colorized;
- Linhas adicionadas (
+) aparecem em verde - Linhas removidas (
-) aparecem em vermelho - Cabeçalhos de hunk (
@@) aparecem em ciano - Cabeçalhos de arquivo (
diff --git,index,---,+++) aparecem em negrito
pitmaster diff sempre aplica cores, inclusive quando sua saída é redirecionada por pipe. pitmaster show aplica cores somente quando stdout é um terminal.
Diff de árvore (interno)
A classe TreeDiff compara dois objetos de árvore recursivamente, produzindo um DiffResult para cada arquivo alterado.
use Pitmaster\Diff\TreeDiff;
$treeDiff = new TreeDiff($repo->objectDatabase());
$diffs = $treeDiff->diff($oldTreeId, $newTreeId);
O diff de árvore trata de:
- Arquivos adicionados (presentes apenas na nova árvore)
- Arquivos excluídos (presentes apenas na árvore antiga)
- Arquivos modificados (mesmo caminho, hash de blob diferente)
- Comparação recursiva de subdiretórios
- Associação de renomeações entre arquivos excluídos e adicionados (hash exato ou pelo menos 50% de linhas em comum)
Entradas cujo hash não mudou são ignoradas, portanto uma alteração apenas no modo não gera um resultado.
Diff de palavras
Para alterações em linha no nível das palavras.
use Pitmaster\Diff\WordDiff;
$result = WordDiff::diff('the quick fox', 'the slow fox');
// 'the [-quick-]{+slow+} fox'