본문으로 건너뛰기

차이

Pitmaster은 git의 통합 형식으로 diff를 계산합니다. diff 엔진은 여러 알고리즘, 통합 diff 형식 지정, 바이너리 파일 감지 및 트리 수준 재귀 비교를 지원합니다.

모드가 100644인 수정된 텍스트 파일의 경우 format()은 git diff와 동일한 바이트를 생성합니다. 현재는 다른 경우의 형식이 다릅니다(#3):

  • 새 파일과 삭제된 파일에는 new file mode / deleted file mode 줄, index 줄 및 /dev/null이 없습니다. 양쪽 모두 a/<path> 및 b/<path>로 표시되고, 비어 있는 쪽의 hunk 범위는 git이 -0,0 또는 +0,0로 쓰는 경우 -1,0 또는 +1,0로 기록됩니다.
  • index 줄은 파일 모드와 관계없이 항상 100644로 끝납니다.
  • 모드만 변경된 경우(예: chmod +x) 결과가 생성되지 않습니다.
  • 바이너리 파일은 index 줄 없이 Binary files differ을 출력하며, git은 Binary files a/<path> and b/<path> differ를 출력합니다.
  • 이름이 바뀐 파일은 콘텐츠 diff가 포함된 단일 diff --git a/<old> b/<new> 항목으로 표시되며, similarity index, rename from 또는 rename to 줄은 없습니다.

Diff 작업

스테이징되지 않은 변경 사항(작업 트리와 인덱스 비교)

PHP
$diffs = $repo->diff();

foreach ($diffs as $diff) {
    echo $diff->format();
}

인덱스의 각 파일을 작업 트리의 현재 상태와 비교합니다. 수정, 삭제 및 바이너리 파일을 감지합니다.

스테이징된 변경 사항(인덱스와 HEAD 비교)

PHP
$diffs = $repo->diffStaged();

foreach ($diffs as $diff) {
    echo $diff->format();
}

현재 인덱스를 HEAD 커밋의 트리와 비교합니다. 다음 커밋에 포함될 항목을 표시합니다. 콘텐츠가 비슷한 삭제 및 추가 파일은 이름이 바뀐 단일 항목으로 보고됩니다. 첫 커밋 전에는 HEAD가 없으므로 결과는 비어 있습니다(#5).

이름이 바뀐 파일의 짝을 찾을 때 마지막 빈 줄이 공유 콘텐츠로 계산되어 서로 관련 없는 한 줄 파일도 현재 일치하는 것으로 처리됩니다(#4).

트리 간 diff

PHP
$a = $repo->readObject($repo->resolve('HEAD~1')->hex)->tree;
$b = $repo->head()->tree;
$diffs = $repo->diffTree($a, $b);

두 트리를 재귀적으로 비교합니다. 트리 ID를 전달하세요. 커밋 ID는 해당 트리로 역참조되지 않아 결과가 비어 있습니다. 동일한 TreeDiff 엔진이 diffStaged(), show(), logPath()을 처리합니다.

경로별 필터링

PHP
$diffs = $repo->diff('src/specific-file.php');

경로 지정자는 정확한 파일 경로 하나입니다. diffStaged()은 같은 인수를 받지만 현재는 이를 무시하므로 스테이징된 파일을 모두 반환합니다(#5).

알고리즘 선택

PHP
$diffs = $repo->diff(null, 'patience');
$diffs = $repo->diffStaged(null, 'histogram');
$diffs = $repo->diffTree($a, $b, 'minimal');

유효한 이름은 myers(기본값), patience, histogram 및 minimal입니다. 다른 이름은 InvalidArgumentException을 발생시킵니다.

DiffResult 구조

각 DiffResult은 단일 파일의 diff를 나타냅니다.

PHP
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'
        }
    }
}

형식이 지정된 출력

PHP
echo $diff->format();

통합 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()
     {

변경 사항 확인

PHP
if ($diff->hasChanges()) {
    // File was modified (or is binary with changes)
}

Hunk 구조

각 Hunk은 컨텍스트 줄이 포함된 연속된 변경 영역을 나타냅니다.

PHP
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 @@"

Diff 알고리즘

Pitmaster은 네 가지 diff 알고리즘을 구현합니다. 기본값은 Myers입니다.

Myers 차이

표준 O(ND) 알고리즘으로 git의 기본값과 동일합니다. 최소 편집 스크립트를 생성합니다.

PHP
use Pitmaster\Diff\MyersDiff;

$hunks = MyersDiff::diff($oldContent, $newContent);

diff() 메서드는 문자열 두 개를 받아 Hunk 객체 배열을 반환합니다. 입력을 줄바꿈으로 나누고, 최단 편집 스크립트를 계산한 뒤, 변경 사항을 컨텍스트 3줄이 포함된 hunk로 묶습니다(git의 기본 동작과 일치).

Patience 차이

코드의 구조를 더 잘 보여 주는 diff입니다. 먼저 고유한 줄을 일치시킨 다음 그 사이를 채웁니다.

PHP
use Pitmaster\Diff\PatienceDiff;

$hunks = PatienceDiff::diff($oldContent, $newContent);

Histogram 차이

반복되는 줄을 더 잘 처리하는 Patience diff의 변형입니다. JGit 및 Eclipse에서 사용됩니다.

PHP
use Pitmaster\Diff\HistogramDiff;

$hunks = HistogramDiff::diff($oldContent, $newContent);

최소 차이

계산량이 늘어나는 대신 가능한 한 가장 작은 diff(변경된 줄 수가 가장 적음)를 생성합니다.

PHP
use Pitmaster\Diff\MinimalDiff;

$hunks = MinimalDiff::diff($oldContent, $newContent);

바이너리 감지

Pitmaster은 처음 8192바이트 안에 NUL 바이트가 있으면 콘텐츠를 바이너리로 처리합니다(git은 처음 8000바이트를 확인함).

PHP
if (MyersDiff::isBinary($content)) {
    // Binary file, skip line-level diff
}

바이너리 파일은 $binary = true과 빈 $hunks 배열이 포함된 DiffResult을 생성합니다. 형식이 지정된 출력은 다음과 같습니다:

코드
diff --git a/image.png b/image.png
Binary files differ

색상 출력

CLI은 ANSI 색상을 사용해 터미널에 출력할 때 ColorDiff을 사용합니다.

PHP
use Pitmaster\Diff\ColorDiff;

$colorized = ColorDiff::colorize($diff->format());
echo $colorized;
  • 추가된 줄(+)은 녹색으로 표시됩니다.
  • 제거된 줄(-)은 빨간색으로 표시됩니다.
  • Hunk 헤더(@@)는 청록색으로 표시됩니다.
  • 파일 헤더(diff --git, index, ---, +++)는 굵게 표시됩니다.

pitmaster diff은 출력을 파이프로 전달할 때도 항상 색상을 입힙니다. pitmaster show은 stdout이 터미널일 때만 색상을 입힙니다.

트리 diff(내부)

TreeDiff 클래스는 두 트리 객체를 재귀적으로 비교하고, 변경된 파일마다 DiffResult을 생성합니다.

PHP
use Pitmaster\Diff\TreeDiff;

$treeDiff = new TreeDiff($repo->objectDatabase());
$diffs = $treeDiff->diff($oldTreeId, $newTreeId);

트리 diff는 다음을 처리합니다:

  • 추가된 파일(새 트리에만 있음)
  • 삭제된 파일(이전 트리에만 있음)
  • 수정된 파일(경로는 같고 blob 해시는 다름)
  • 하위 디렉터리의 재귀적 비교
  • 삭제 및 추가된 파일의 이름 바뀜 짝 찾기(해시가 정확히 일치하거나 공유 줄이 50% 이상)

해시가 바뀌지 않은 항목은 건너뛰므로 모드만 변경된 경우 결과가 생성되지 않습니다.

단어 diff

줄 안에서 인라인 단어 수준의 변경 사항을 확인합니다.

PHP
use Pitmaster\Diff\WordDiff;

$result = WordDiff::diff('the quick fox', 'the slow fox');
// 'the [-quick-]{+slow+} fox'