
# 차이

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

모드가 `100644`인 수정된 텍스트 파일의 경우 `format()`은 `git diff`와 동일한 바이트를 생성합니다. 현재는 다른 경우의 형식이 다릅니다([#3](https://github.com/inline0/pitmaster/issues/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](https://github.com/inline0/pitmaster/issues/5)).

이름이 바뀐 파일의 짝을 찾을 때 마지막 빈 줄이 공유 콘텐츠로 계산되어 서로 관련 없는 한 줄 파일도 현재 일치하는 것으로 처리됩니다([#4](https://github.com/inline0/pitmaster/issues/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](https://github.com/inline0/pitmaster/issues/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'
```
